commit 682afacd305bc52600124434befaa89773747310 Author: Brenden Blosser Date: Wed Jul 1 18:22:43 2026 -0400 Add native self-hosted instance connection to fluxer_desktop Trimmed monorepo checkout (fluxer_desktop + packages/voice_engine_v2 + tools/ci) with a "Connect to a Different Server" menu item and popout that lets the desktop app switch to any self-hosted Fluxer instance, plus fixes for well-known discovery on single-domain self-hosted deployments and a false-positive ERR_ABORTED on same-origin client redirects during the switch. Defaults to chat.fluxr.chat and uses an isolated userData directory from the official build. diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..87db35a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,18 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +max_line_length = 120 +indent_style = tab +indent_size = 2 + +[*.{yml,yaml,Dockerfile}] +indent_style = space +indent_size = 2 + +[justfile] +indent_style = space +indent_size = 4 diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..6e496bc --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +* text=auto +fluxer_static/** -text -diff diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..93abd91 --- /dev/null +++ b/.gitignore @@ -0,0 +1,115 @@ +*.tsbuildinfo +**/*.beam +**/*.css.d.ts +**/*.dump +**/dump.rdb +**/*.iml +**/*.log +**/*.o +**/*.node +**/*.plt +**/*.so +**/*.so.* +!fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/**/*.so.init.c +!fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/**/*.so.tramp.S +**/*.source +**/*.swo +**/*.swp +**/*.tmp +**/*~ +**/.*cache +**/.cache +**/__pycache__ +**/.dev-runner/ +**/.devenv +.devenv.flake.nix +devenv.local.nix +**/.direnv +/dev/livekit.yaml +/dev/bluesky_oauth_key.pem +/dev/meilisearch_master_key +/dev/data/ +**/.dev.vars +**/.DS_Store +**/.env +**/.env.*.local +**/.env.local +**/.erlang.cookie +**/.eunit +**/.idea +**/.next +**/.next/cache +**/.pnp +**/.pnp.js +**/.pnpm-store +**/.rebar +**/.rebar3 +**/.source +**/.swc +**/.vercel +**/_build +**/_checkouts +**/_vendor +**/certificates +**/coverage +**/dist +**/ebin +**/erl_crash.dump +/erl_crash.dump +**/fluxer.env +**/generated +**/log +**/logs +**/node_modules +**/npm-debug.log* +**/out +**/pnpm-debug.log* +**/rebar3.crashdump +**/secrets.env +**/target +**/test-results.json +**/Thumbs.db +**/yarn-debug.log* +**/yarn-error.log* +/.devserver-cache.json +**/.devserver-cache.json +/.fluxer/ +/scripts/remote/hosts.json +/config/env/local.env +/fluxer_app/src/features/ui/constants/AvatarStatusGeometry.ts +/fluxer_app/src/features/ui/components/SVGMasks.tsx +/fluxer_app/src/features/i18n/locales/*/messages.js +/fluxer_app/src/features/i18n/locales/*/messages.mjs +/fluxer_app/src/features/i18n/locales/*/messages.ts +/fluxer_app/pkgs/libfluxcore/ +/fluxer_gateway/config/sys.config +/fluxer_gateway/config/vm.args +/packages/config/src/ConfigSchema.json +/packages/config/src/MasterZodSchema.generated.tsx +fluxer.yaml +GEMINI.md +geoip_data +tmp/ +next-env.d.ts +deploy/kubeconfig/ +/deploy/helm/**/Chart.lock +/deploy/helm/**/charts/ +.github/agents +.github/prompts + +**/public/static/app.css +**/public/static/app.*.css +**/public/static/tailwind.css +**/public/static/tailwind.*.css + +**/zig-out/ +**/.zig-cache/ +fluxer_media_proxy/bench-results/ +fluxer_media_proxy/.benchmark-cache/ +.claude/ + +# Generated by tools/ci build-desktop --step set_build_channel +fluxer_desktop/src/common/BuildChannel.ts + +# Generated by tools/ci build-markdown-parser-wasm +fluxer_app/src/features/messaging/utils/markdown/parser/MarkdownParserWasmBytes.ts diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..2cd7356 --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,3436 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arc-swap" +version = "1.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207" +dependencies = [ + "rustversion", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "aws-config" +version = "1.8.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e33f815b73a3899c03b380d543532e5865f230dce9678d108dc10732a8682275" +dependencies = [ + "aws-credential-types", + "aws-runtime", + "aws-sdk-sso", + "aws-sdk-ssooidc", + "aws-sdk-sts", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "hex", + "http 1.4.2", + "sha1 0.10.6", + "time", + "tokio", + "tracing", + "url", + "zeroize", +] + +[[package]] +name = "aws-credential-types" +version = "1.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f20799b373a1be121fe3005fba0c2090af9411573878f224df44b42727fcaf7" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "zeroize", +] + +[[package]] +name = "aws-lc-rs" +version = "1.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec6fb3fe69024a75fa7e1bfb48aa6cf59706a101658ea01bfd33b2b248a038f" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.40.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f50037ee5e1e41e7b8f9d161680a725bd1626cb6f8c7e901f91f942850852fe7" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + +[[package]] +name = "aws-runtime" +version = "1.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ed8e8c52d2dc2390ad9f15647fe663f71e9780b4262c190fbb823a32721566" +dependencies = [ + "aws-credential-types", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "bytes-utils", + "fastrand", + "http 0.2.12", + "http 1.4.2", + "http-body 0.4.6", + "http-body 1.0.1", + "percent-encoding", + "pin-project-lite", + "tracing", + "uuid", +] + +[[package]] +name = "aws-sdk-s3" +version = "1.135.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f97e3e7e7d86fd26fcdc18bc382da5ca9e8b2ff8d54030d187fd0dac8a236d96" +dependencies = [ + "arc-swap", + "aws-credential-types", + "aws-runtime", + "aws-sigv4", + "aws-smithy-async", + "aws-smithy-checksums", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-observability", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "bytes", + "fastrand", + "hex", + "hmac 0.13.0", + "http 0.2.12", + "http 1.4.2", + "http-body 1.0.1", + "lru", + "percent-encoding", + "regex-lite", + "sha2 0.11.0", + "tracing", + "url", +] + +[[package]] +name = "aws-sdk-sso" +version = "1.101.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b647baea49ff551960b904f905681e9b4765a6c4ea08631e89dc52d8bd3f5896" +dependencies = [ + "arc-swap", + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-observability", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "http 1.4.2", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-ssooidc" +version = "1.103.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ae401c65ff288aa7873117fe535cd32b7b1bb0bc43751d28901a1d5f20636b9" +dependencies = [ + "arc-swap", + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-observability", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-types", + "bytes", + "fastrand", + "http 0.2.12", + "http 1.4.2", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sdk-sts" +version = "1.106.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c80de7bb7d03e9ca8c9fd7b489f20f3948d3f3be91a7953591347d238115408" +dependencies = [ + "arc-swap", + "aws-credential-types", + "aws-runtime", + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-json", + "aws-smithy-observability", + "aws-smithy-query", + "aws-smithy-runtime", + "aws-smithy-runtime-api", + "aws-smithy-types", + "aws-smithy-xml", + "aws-types", + "fastrand", + "http 0.2.12", + "http 1.4.2", + "regex-lite", + "tracing", +] + +[[package]] +name = "aws-sigv4" +version = "1.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bae38512beae0ffee7010fc24e7a8a123c53efdfef42a61e80fda4882418dc71" +dependencies = [ + "aws-credential-types", + "aws-smithy-eventstream", + "aws-smithy-http", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "crypto-bigint", + "form_urlencoded", + "hex", + "hmac 0.13.0", + "http 0.2.12", + "http 1.4.2", + "p256", + "percent-encoding", + "sha2 0.11.0", + "subtle", + "time", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-async" +version = "1.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffcaf626bdda484571968400c326a244598634dc75fd451325a54ad1a59acfc" +dependencies = [ + "futures-util", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "aws-smithy-checksums" +version = "0.64.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9e8e65f4f81fcccdeb6c3eca2af17ac21d421a1786a26a394aecf421d616d3a" +dependencies = [ + "aws-smithy-http", + "aws-smithy-types", + "bytes", + "crc-fast", + "hex", + "http 1.4.2", + "http-body 1.0.1", + "http-body-util", + "md-5", + "pin-project-lite", + "sha1 0.11.0", + "sha2 0.11.0", + "tracing", +] + +[[package]] +name = "aws-smithy-eventstream" +version = "0.60.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "faf09d74e5e32f76b8762da505a3cd59303e367a664ca67295387baa8c1d7548" +dependencies = [ + "aws-smithy-types", + "bytes", + "crc32fast", +] + +[[package]] +name = "aws-smithy-http" +version = "0.63.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1ab2dc1c2c3749ead27180d333c42f11be8b0e934058fb4b2258ee8dbe5231" +dependencies = [ + "aws-smithy-eventstream", + "aws-smithy-runtime-api", + "aws-smithy-types", + "bytes", + "bytes-utils", + "futures-core", + "futures-util", + "http 1.4.2", + "http-body 1.0.1", + "http-body-util", + "percent-encoding", + "pin-project-lite", + "pin-utils", + "tracing", +] + +[[package]] +name = "aws-smithy-http-client" +version = "1.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3ef8931ad1c98aa6a55b4256f847f3116090819844e0dd41ea682cac5dd2d3" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-types", + "h2 0.3.27", + "h2 0.4.14", + "http 0.2.12", + "http 1.4.2", + "http-body 0.4.6", + "hyper 0.14.32", + "hyper 1.10.1", + "hyper-rustls 0.24.2", + "hyper-rustls 0.27.9", + "hyper-util", + "pin-project-lite", + "rustls 0.21.12", + "rustls 0.23.40", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls 0.26.4", + "tower", + "tracing", +] + +[[package]] +name = "aws-smithy-json" +version = "0.62.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "701a947f4797e52a911e114a898667c746c39feea467bbd1abd7b3721f702ffa" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", +] + +[[package]] +name = "aws-smithy-observability" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06c2315d173edbf1920da8ba3a7189695827002e4c0fc961973ab1c54abca9c" +dependencies = [ + "aws-smithy-runtime-api", +] + +[[package]] +name = "aws-smithy-query" +version = "0.60.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a56d79744fb3edb5d722ef79d86081e121d3b9422cb209eb03aea6aa4f21ebd" +dependencies = [ + "aws-smithy-types", + "urlencoding", +] + +[[package]] +name = "aws-smithy-runtime" +version = "1.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e6f5caf6fea86f8c2206541ab5857cfcda9013426cdbe8fa0098b9e2d32182" +dependencies = [ + "aws-smithy-async", + "aws-smithy-http", + "aws-smithy-http-client", + "aws-smithy-observability", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "bytes", + "fastrand", + "http 0.2.12", + "http 1.4.2", + "http-body 0.4.6", + "http-body 1.0.1", + "http-body-util", + "pin-project-lite", + "pin-utils", + "tokio", + "tracing", +] + +[[package]] +name = "aws-smithy-runtime-api" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9db177daa6ba8afb9ee1aefcf548c907abcf52065e394ee11a92780057fe0e8c" +dependencies = [ + "aws-smithy-async", + "aws-smithy-runtime-api-macros", + "aws-smithy-types", + "bytes", + "http 0.2.12", + "http 1.4.2", + "pin-project-lite", + "tokio", + "tracing", + "zeroize", +] + +[[package]] +name = "aws-smithy-runtime-api-macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d7396fd9500589e62e460e987ecb671bad374934e55ec3b5f498cc7a8a8a7b7" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "aws-smithy-schema" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7442cb268338f0eb8278140a107c046756aa01093d8ef5e99628d34ae09c94f5" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "http 1.4.2", +] + +[[package]] +name = "aws-smithy-types" +version = "1.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53f93074121a1be41317b9aa607143ae17900631f7f59a99f2b905d519d6783b" +dependencies = [ + "base64-simd", + "bytes", + "bytes-utils", + "futures-core", + "http 0.2.12", + "http 1.4.2", + "http-body 0.4.6", + "http-body 1.0.1", + "http-body-util", + "itoa", + "num-integer", + "pin-project-lite", + "pin-utils", + "ryu", + "serde", + "time", + "tokio", + "tokio-util", +] + +[[package]] +name = "aws-smithy-xml" +version = "0.60.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce02add1aa3677d022f8adf81dcbe3046a95f17a1b1e8979c145cd21d3d22b3" +dependencies = [ + "xmlparser", +] + +[[package]] +name = "aws-types" +version = "1.3.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d16bf10b03a3c01e6b3b7d47cd964e873ffe9e7d4e80fad16bd4c077cb068531" +dependencies = [ + "aws-credential-types", + "aws-smithy-async", + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", + "rustc_version", + "tracing", +] + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "339abbe78e73178762e23bea9dfd08e697eb3f3301cd4be981c0f78ba5859195" +dependencies = [ + "outref", + "vsimd", +] + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bitflags" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "bytes-utils" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dafe3a8757b027e2be6e4e5601ed563c55989fcf1546e933c66c8eb3a058d35" +dependencies = [ + "bytes", + "either", +] + +[[package]] +name = "cc" +version = "1.2.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "cmake" +version = "0.1.58" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" +dependencies = [ + "cc", +] + +[[package]] +name = "cmov" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f88a43d011fc4a6876cb7344703e297c71dda42494fee094d5f7c76bf13f746" + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "crc-fast" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e75b2483e97a5a7da73ac68a05b629f9c53cff58d8ed1c77866079e18b00dba5" +dependencies = [ + "digest 0.10.7", + "spin", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "ctutils" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" +dependencies = [ + "cmov", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid 0.9.6", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid 0.9.6", + "crypto-common 0.1.7", + "subtle", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.0", + "const-oid 0.10.2", + "crypto-common 0.2.2", + "ctutils", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "filetime" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" +dependencies = [ + "cfg-if", + "libc", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", + "zlib-rs", +] + +[[package]] +name = "fluxer-ci" +version = "0.1.0" +dependencies = [ + "anyhow", + "aws-config", + "aws-sdk-s3", + "base64", + "bytes", + "chrono", + "clap", + "flate2", + "hex", + "md-5", + "reqwest", + "serde", + "serde_json", + "sha2 0.11.0", + "tar", + "tempfile", + "tokio", + "walkdir", + "zip", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 5.3.0", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "h2" +version = "0.3.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0beca50380b1fc32983fc1cb4587bfa4bb9e78fc259aad4a0032d2080309222d" +dependencies = [ + "bytes", + "fnv", + "futures-core", + "futures-sink", + "futures-util", + "http 0.2.12", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "h2" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http 1.4.2", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "hmac" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f" +dependencies = [ + "digest 0.11.3", +] + +[[package]] +name = "http" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +dependencies = [ + "bytes", + "fnv", + "itoa", +] + +[[package]] +name = "http" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +dependencies = [ + "bytes", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.4.2", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http 1.4.2", + "http-body 1.0.1", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "httpdate" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" + +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "typenum", +] + +[[package]] +name = "hyper" +version = "0.14.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41dfc780fdec9373c01bae43289ea34c972e40ee3c9f6b3c8801a35f35586ce7" +dependencies = [ + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "h2 0.3.27", + "http 0.2.12", + "http-body 0.4.6", + "httparse", + "httpdate", + "itoa", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", + "want", +] + +[[package]] +name = "hyper" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2 0.4.14", + "http 1.4.2", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.24.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +dependencies = [ + "futures-util", + "http 0.2.12", + "hyper 0.14.32", + "log", + "rustls 0.21.12", + "tokio", + "tokio-rustls 0.24.1", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http 1.4.2", + "hyper 1.10.1", + "hyper-util", + "rustls 0.23.40", + "rustls-native-certs", + "tokio", + "tokio-rustls 0.26.4", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http 1.4.2", + "http-body 1.0.1", + "hyper 1.10.1", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.6.3", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys", + "log", + "simd_cesu8", + "thiserror", + "walkdir", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" +dependencies = [ + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru" +version = "0.16.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" +dependencies = [ + "hashbrown 0.16.1", +] + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "md-5" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69b6441f590336821bb897fb28fc622898ccceb1d6cea3fde5ea86b090c4de98" +dependencies = [ + "cfg-if", + "digest 0.11.3", +] + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "num-conv" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.9", +] + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls 0.23.40", + "socket2 0.6.3", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls 0.23.40", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.6.3", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "regex-lite" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab834c73d247e67f4fae452806d17d3c7501756d98c8808d7c9c7aa7d18f973" + +[[package]] +name = "reqwest" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +dependencies = [ + "base64", + "bytes", + "futures-core", + "http 1.4.2", + "http-body 1.0.1", + "http-body-util", + "hyper 1.10.1", + "hyper-rustls 0.27.9", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls 0.23.40", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-rustls 0.26.4", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac 0.12.1", + "subtle", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.21.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" +dependencies = [ + "log", + "ring", + "rustls-webpki 0.101.7", + "sct", +] + +[[package]] +name = "rustls" +version = "0.23.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +dependencies = [ + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki 0.103.13", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-platform-verifier" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" +dependencies = [ + "core-foundation", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls 0.23.40", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki 0.103.13", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls-platform-verifier-android" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" + +[[package]] +name = "rustls-webpki" +version = "0.101.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "sct" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" +dependencies = [ + "ring", + "untrusted", +] + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha1" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tar" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2 0.6.3", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +dependencies = [ + "rustls 0.21.12", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls 0.23.40", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http 1.4.2", + "http-body 1.0.1", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typed-path" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" + +[[package]] +name = "typenum" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.71" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.121" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.98" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-root-certs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix", +] + +[[package]] +name = "xmlparser" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66fee0b777b0f5ac1c69bb06d361268faafa61cd4682ae064a171c16c433e9e4" + +[[package]] +name = "yoke" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zip" +version = "8.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b" +dependencies = [ + "crc32fast", + "flate2", + "indexmap", + "memchr", + "typed-path", + "zopfli", +] + +[[package]] +name = "zlib-rs" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3be3d40e40a133f9c916ee3f9f4fa2d9d63435b5fbe1bfc6d9dae0aa0ada1513" + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zopfli" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f5b7c1b --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,14 @@ +[workspace] +members = [ + "tools/ci", +] +exclude = [ + "packages/markdown_parser/rust/fuzz", + "fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16", + "fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1", +] +resolver = "2" + +[workspace.package] +edition = "2024" +license = "AGPL-3.0-or-later" diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..0ad25db --- /dev/null +++ b/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed 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. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..057769c --- /dev/null +++ b/biome.json @@ -0,0 +1,152 @@ +{ + "$schema": "./node_modules/@biomejs/biome/configuration_schema.json", + "formatter": { + "enabled": true, + "formatWithErrors": false, + "indentStyle": "tab", + "lineWidth": 120, + "lineEnding": "lf", + "bracketSpacing": false, + "includes": ["**", "!**/fluxer_app/src/features/theme/variables/ThemeVariableManifest.ts"] + }, + "javascript": { + "formatter": { + "quoteStyle": "single", + "jsxQuoteStyle": "double", + "quoteProperties": "asNeeded", + "trailingCommas": "all", + "semicolons": "always", + "arrowParentheses": "always", + "bracketSpacing": false, + "bracketSameLine": false + }, + "globals": ["React"] + }, + "json": { + "formatter": { + "enabled": true, + "indentStyle": "tab", + "lineWidth": 120 + }, + "parser": { + "allowComments": true, + "allowTrailingCommas": true + } + }, + "css": { + "formatter": { + "enabled": true, + "indentStyle": "tab", + "lineWidth": 120, + "quoteStyle": "single" + }, + "parser": { + "cssModules": true, + "tailwindDirectives": true + } + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "complexity": { + "noForEach": "off", + "noImportantStyles": "off", + "useLiteralKeys": "off" + }, + "correctness": { + "noUndeclaredVariables": "error", + "noUnusedVariables": "error", + "noInvalidUseBeforeDeclaration": "off", + "useExhaustiveDependencies": "off" + }, + "suspicious": { + "noArrayIndexKey": "off", + "noAssignInExpressions": "off", + "noExplicitAny": "off", + "noThenProperty": "off", + "noDoubleEquals": { + "level": "error", + "options": { + "ignoreNull": true + } + }, + "noVar": "error", + "useAdjacentOverloadSignatures": "off", + "useIterableCallbackReturn": "off" + }, + "style": { + "useConsistentArrayType": { + "level": "error", + "options": { + "syntax": "generic" + } + }, + "useConst": "error", + "noNonNullAssertion": "off", + "noParameterAssign": "off" + }, + "a11y": { + "recommended": true, + "useAriaPropsForRole": "error", + "useValidAriaRole": "error", + "useValidAriaValues": "error", + "useValidAriaProps": "error", + "useAltText": "error", + "useAnchorContent": "error", + "useButtonType": "error", + "useKeyWithClickEvents": "error", + "useKeyWithMouseEvents": "error", + "useSemanticElements": "off", + "noAriaUnsupportedElements": "error", + "noNoninteractiveElementToInteractiveRole": "error", + "noNoninteractiveTabindex": "error", + "noRedundantAlt": "error", + "noRedundantRoles": "error", + "noInteractiveElementToNoninteractiveRole": "error", + "noAutofocus": "warn", + "noAccessKey": "warn", + "useAriaActivedescendantWithTabindex": "error", + "noSvgWithoutTitle": "off" + }, + "nursery": { + "useSortedClasses": "error" + } + } + }, + "assist": {"actions": {"source": {"organizeImports": "on"}}}, + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": true + }, + "files": { + "includes": [ + "**", + "!**/.git", + "!**/app.css", + "!fluxer_admin/public/static/app.css", + "!**/build", + "fluxer_app/scripts/build", + "!**/dist", + "!**/fluxer_app/src/media/data/emojis.json", + "!**/fluxer_app/src/features/i18n/locales/*/messages.js", + "!**/fluxer_app/src/features/i18n/locales/*/messages.mjs", + "!**/fluxer_app/src/env.d.ts", + "!**/*.generated.*", + "!**/*.pb.*", + "!**/*_pb.*", + "!**/node_modules", + "!packages/schema/src/gen", + "!**/tailwind.css", + "!**/*.html", + "!**/*.module.css.d.ts", + "!**/fluxer_app/src/features/ui/components/SVGMasks.tsx", + "!fluxer_static", + "!fluxer_admin/static/htmx.min.js", + "!fluxer_marketing/static/htmx.min.js", + "!fluxer_api/src/api/openapi/openapi.json" + ], + "ignoreUnknown": true + } +} diff --git a/fluxer_desktop/.gitignore b/fluxer_desktop/.gitignore new file mode 100644 index 0000000..e140f36 --- /dev/null +++ b/fluxer_desktop/.gitignore @@ -0,0 +1,19 @@ +# Dependencies +node_modules/ + +# Build outputs +dist/ +dist-electron/ + + +# IDE +.idea/ +*.swp +*.swo + +# OS +.DS_Store +Thumbs.db + +# Logs +*.log diff --git a/fluxer_desktop/LICENSE b/fluxer_desktop/LICENSE new file mode 100644 index 0000000..0ad25db --- /dev/null +++ b/fluxer_desktop/LICENSE @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed 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. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/fluxer_desktop/build_resources/entitlements.mac.canary.plist b/fluxer_desktop/build_resources/entitlements.mac.canary.plist new file mode 100644 index 0000000..c7caa71 --- /dev/null +++ b/fluxer_desktop/build_resources/entitlements.mac.canary.plist @@ -0,0 +1,33 @@ + + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-library-validation + + com.apple.security.device.audio-input + + com.apple.security.device.microphone + + com.apple.security.device.camera + + com.apple.security.device.screen-capture + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.application-identifier + 3G5837T29K.app.fluxer.canary + com.apple.developer.associated-domains + + webcredentials:fluxer.app + webcredentials:web.fluxer.app + webcredentials:web.canary.fluxer.app + + + diff --git a/fluxer_desktop/build_resources/entitlements.mac.inherit.plist b/fluxer_desktop/build_resources/entitlements.mac.inherit.plist new file mode 100644 index 0000000..74e4819 --- /dev/null +++ b/fluxer_desktop/build_resources/entitlements.mac.inherit.plist @@ -0,0 +1,23 @@ + + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-library-validation + + com.apple.security.device.audio-input + + com.apple.security.device.microphone + + com.apple.security.device.camera + + com.apple.security.network.client + + com.apple.security.network.server + + + diff --git a/fluxer_desktop/build_resources/entitlements.mac.stable.plist b/fluxer_desktop/build_resources/entitlements.mac.stable.plist new file mode 100644 index 0000000..11f159a --- /dev/null +++ b/fluxer_desktop/build_resources/entitlements.mac.stable.plist @@ -0,0 +1,32 @@ + + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-library-validation + + com.apple.security.device.audio-input + + com.apple.security.device.microphone + + com.apple.security.device.camera + + com.apple.security.device.screen-capture + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.application-identifier + 3G5837T29K.app.fluxer + com.apple.developer.associated-domains + + webcredentials:fluxer.app + webcredentials:web.fluxer.app + + + diff --git a/fluxer_desktop/build_resources/icons-canary/1024x1024.png b/fluxer_desktop/build_resources/icons-canary/1024x1024.png new file mode 100644 index 0000000..55b66e9 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/1024x1024.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/128x128.png b/fluxer_desktop/build_resources/icons-canary/128x128.png new file mode 100644 index 0000000..8a96155 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/128x128.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/128x128@2x.png b/fluxer_desktop/build_resources/icons-canary/128x128@2x.png new file mode 100644 index 0000000..bab4c14 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/128x128@2x.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/16x16.png b/fluxer_desktop/build_resources/icons-canary/16x16.png new file mode 100644 index 0000000..27942b9 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/16x16.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/24x24.png b/fluxer_desktop/build_resources/icons-canary/24x24.png new file mode 100644 index 0000000..c78a00c Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/24x24.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/256x256.png b/fluxer_desktop/build_resources/icons-canary/256x256.png new file mode 100644 index 0000000..f07381a Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/256x256.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/32x32.png b/fluxer_desktop/build_resources/icons-canary/32x32.png new file mode 100644 index 0000000..0db4d5c Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/32x32.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/48x48.png b/fluxer_desktop/build_resources/icons-canary/48x48.png new file mode 100644 index 0000000..0b0428d Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/48x48.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/512x512.png b/fluxer_desktop/build_resources/icons-canary/512x512.png new file mode 100644 index 0000000..21f6c40 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/512x512.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/64x64.png b/fluxer_desktop/build_resources/icons-canary/64x64.png new file mode 100644 index 0000000..bd2cc1c Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/64x64.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/AppIcon.icon/Assets/Vector.svg b/fluxer_desktop/build_resources/icons-canary/AppIcon.icon/Assets/Vector.svg new file mode 100644 index 0000000..4b9133a --- /dev/null +++ b/fluxer_desktop/build_resources/icons-canary/AppIcon.icon/Assets/Vector.svg @@ -0,0 +1,4 @@ + + + + diff --git a/fluxer_desktop/build_resources/icons-canary/AppIcon.icon/icon.json b/fluxer_desktop/build_resources/icons-canary/AppIcon.icon/icon.json new file mode 100644 index 0000000..b45c1ef --- /dev/null +++ b/fluxer_desktop/build_resources/icons-canary/AppIcon.icon/icon.json @@ -0,0 +1,27 @@ +{ + "fill": { + "solid": "display-p3:0.95215,0.66553,0.23462,1.00000" + }, + "groups": [ + { + "layers": [ + { + "image-name": "Vector.svg", + "name": "Vector" + } + ], + "shadow": { + "kind": "neutral", + "opacity": 0.5 + }, + "translucency": { + "enabled": true, + "value": 0.5 + } + } + ], + "supported-platforms": { + "circles": ["watchOS"], + "squares": "shared" + } +} diff --git a/fluxer_desktop/build_resources/icons-canary/FluxerTrayTemplate.png b/fluxer_desktop/build_resources/icons-canary/FluxerTrayTemplate.png new file mode 100644 index 0000000..62516c5 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/FluxerTrayTemplate.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/FluxerTrayTemplate@2x.png b/fluxer_desktop/build_resources/icons-canary/FluxerTrayTemplate@2x.png new file mode 100644 index 0000000..e51c1f4 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/FluxerTrayTemplate@2x.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/Square107x107Logo.png b/fluxer_desktop/build_resources/icons-canary/Square107x107Logo.png new file mode 100644 index 0000000..2fbc1c7 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/Square107x107Logo.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/Square142x142Logo.png b/fluxer_desktop/build_resources/icons-canary/Square142x142Logo.png new file mode 100644 index 0000000..666bd4b Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/Square142x142Logo.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/Square150x150Logo.png b/fluxer_desktop/build_resources/icons-canary/Square150x150Logo.png new file mode 100644 index 0000000..4d4c401 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/Square150x150Logo.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/Square284x284Logo.png b/fluxer_desktop/build_resources/icons-canary/Square284x284Logo.png new file mode 100644 index 0000000..1790d20 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/Square284x284Logo.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/Square30x30Logo.png b/fluxer_desktop/build_resources/icons-canary/Square30x30Logo.png new file mode 100644 index 0000000..4fc82b8 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/Square30x30Logo.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/Square310x310Logo.png b/fluxer_desktop/build_resources/icons-canary/Square310x310Logo.png new file mode 100644 index 0000000..dda60d1 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/Square310x310Logo.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/Square44x44Logo.png b/fluxer_desktop/build_resources/icons-canary/Square44x44Logo.png new file mode 100644 index 0000000..65edafd Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/Square44x44Logo.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/Square71x71Logo.png b/fluxer_desktop/build_resources/icons-canary/Square71x71Logo.png new file mode 100644 index 0000000..bec7299 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/Square71x71Logo.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/Square89x89Logo.png b/fluxer_desktop/build_resources/icons-canary/Square89x89Logo.png new file mode 100644 index 0000000..2f8cdb3 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/Square89x89Logo.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/StoreLogo.png b/fluxer_desktop/build_resources/icons-canary/StoreLogo.png new file mode 100644 index 0000000..6a06e74 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/StoreLogo.png differ diff --git a/fluxer_desktop/build_resources/icons-canary/_compiled/AppIcon.icns b/fluxer_desktop/build_resources/icons-canary/_compiled/AppIcon.icns new file mode 100644 index 0000000..6d3594e Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/_compiled/AppIcon.icns differ diff --git a/fluxer_desktop/build_resources/icons-canary/_compiled/Assets.car b/fluxer_desktop/build_resources/icons-canary/_compiled/Assets.car new file mode 100644 index 0000000..ea86d82 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/_compiled/Assets.car differ diff --git a/fluxer_desktop/build_resources/icons-canary/_compiled/assetcatalog_generated_info.plist b/fluxer_desktop/build_resources/icons-canary/_compiled/assetcatalog_generated_info.plist new file mode 100644 index 0000000..cfc5537 --- /dev/null +++ b/fluxer_desktop/build_resources/icons-canary/_compiled/assetcatalog_generated_info.plist @@ -0,0 +1,6 @@ + + + + + + diff --git a/fluxer_desktop/build_resources/icons-canary/_compiled/icon.part.plist b/fluxer_desktop/build_resources/icons-canary/_compiled/icon.part.plist new file mode 100644 index 0000000..c98f6ca --- /dev/null +++ b/fluxer_desktop/build_resources/icons-canary/_compiled/icon.part.plist @@ -0,0 +1,11 @@ + + + + + + CFBundleIconFile + AppIcon + CFBundleIconName + AppIcon + + diff --git a/fluxer_desktop/build_resources/icons-canary/badges/badge-1.ico b/fluxer_desktop/build_resources/icons-canary/badges/badge-1.ico new file mode 100644 index 0000000..0e713c3 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/badges/badge-1.ico differ diff --git a/fluxer_desktop/build_resources/icons-canary/badges/badge-10.ico b/fluxer_desktop/build_resources/icons-canary/badges/badge-10.ico new file mode 100644 index 0000000..68dfee3 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/badges/badge-10.ico differ diff --git a/fluxer_desktop/build_resources/icons-canary/badges/badge-11.ico b/fluxer_desktop/build_resources/icons-canary/badges/badge-11.ico new file mode 100644 index 0000000..20f89f6 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/badges/badge-11.ico differ diff --git a/fluxer_desktop/build_resources/icons-canary/badges/badge-2.ico b/fluxer_desktop/build_resources/icons-canary/badges/badge-2.ico new file mode 100644 index 0000000..454254f Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/badges/badge-2.ico differ diff --git a/fluxer_desktop/build_resources/icons-canary/badges/badge-3.ico b/fluxer_desktop/build_resources/icons-canary/badges/badge-3.ico new file mode 100644 index 0000000..e7b46de Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/badges/badge-3.ico differ diff --git a/fluxer_desktop/build_resources/icons-canary/badges/badge-4.ico b/fluxer_desktop/build_resources/icons-canary/badges/badge-4.ico new file mode 100644 index 0000000..038d9c9 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/badges/badge-4.ico differ diff --git a/fluxer_desktop/build_resources/icons-canary/badges/badge-5.ico b/fluxer_desktop/build_resources/icons-canary/badges/badge-5.ico new file mode 100644 index 0000000..f7dcba3 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/badges/badge-5.ico differ diff --git a/fluxer_desktop/build_resources/icons-canary/badges/badge-6.ico b/fluxer_desktop/build_resources/icons-canary/badges/badge-6.ico new file mode 100644 index 0000000..956d582 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/badges/badge-6.ico differ diff --git a/fluxer_desktop/build_resources/icons-canary/badges/badge-7.ico b/fluxer_desktop/build_resources/icons-canary/badges/badge-7.ico new file mode 100644 index 0000000..0bfc15d Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/badges/badge-7.ico differ diff --git a/fluxer_desktop/build_resources/icons-canary/badges/badge-8.ico b/fluxer_desktop/build_resources/icons-canary/badges/badge-8.ico new file mode 100644 index 0000000..7d356f6 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/badges/badge-8.ico differ diff --git a/fluxer_desktop/build_resources/icons-canary/badges/badge-9.ico b/fluxer_desktop/build_resources/icons-canary/badges/badge-9.ico new file mode 100644 index 0000000..34149cb Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/badges/badge-9.ico differ diff --git a/fluxer_desktop/build_resources/icons-canary/icon.ico b/fluxer_desktop/build_resources/icons-canary/icon.ico new file mode 100644 index 0000000..59d8ea0 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/icon.ico differ diff --git a/fluxer_desktop/build_resources/icons-canary/icon.png b/fluxer_desktop/build_resources/icons-canary/icon.png new file mode 100644 index 0000000..6da6ac8 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-canary/icon.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/1024x1024.png b/fluxer_desktop/build_resources/icons-stable/1024x1024.png new file mode 100644 index 0000000..1204e20 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/1024x1024.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/128x128.png b/fluxer_desktop/build_resources/icons-stable/128x128.png new file mode 100644 index 0000000..6b40373 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/128x128.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/128x128@2x.png b/fluxer_desktop/build_resources/icons-stable/128x128@2x.png new file mode 100644 index 0000000..cf5bedc Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/128x128@2x.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/16x16.png b/fluxer_desktop/build_resources/icons-stable/16x16.png new file mode 100644 index 0000000..6ad4b2e Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/16x16.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/24x24.png b/fluxer_desktop/build_resources/icons-stable/24x24.png new file mode 100644 index 0000000..3d5c0c6 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/24x24.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/256x256.png b/fluxer_desktop/build_resources/icons-stable/256x256.png new file mode 100644 index 0000000..12f5bee Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/256x256.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/32x32.png b/fluxer_desktop/build_resources/icons-stable/32x32.png new file mode 100644 index 0000000..d8fbc26 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/32x32.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/48x48.png b/fluxer_desktop/build_resources/icons-stable/48x48.png new file mode 100644 index 0000000..74a3124 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/48x48.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/512x512.png b/fluxer_desktop/build_resources/icons-stable/512x512.png new file mode 100644 index 0000000..e06eea4 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/512x512.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/64x64.png b/fluxer_desktop/build_resources/icons-stable/64x64.png new file mode 100644 index 0000000..b2ed8d4 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/64x64.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/AppIcon.icon/Assets/Vector.svg b/fluxer_desktop/build_resources/icons-stable/AppIcon.icon/Assets/Vector.svg new file mode 100644 index 0000000..4b9133a --- /dev/null +++ b/fluxer_desktop/build_resources/icons-stable/AppIcon.icon/Assets/Vector.svg @@ -0,0 +1,4 @@ + + + + diff --git a/fluxer_desktop/build_resources/icons-stable/AppIcon.icon/icon.json b/fluxer_desktop/build_resources/icons-stable/AppIcon.icon/icon.json new file mode 100644 index 0000000..a5d9184 --- /dev/null +++ b/fluxer_desktop/build_resources/icons-stable/AppIcon.icon/icon.json @@ -0,0 +1,28 @@ +{ + "fill": { + "solid": "display-p3:0.27100,0.25562,0.81934,1.00000" + }, + "groups": [ + { + "layers": [ + { + "fill": "automatic", + "image-name": "Vector.svg", + "name": "Vector" + } + ], + "shadow": { + "kind": "neutral", + "opacity": 0.5 + }, + "translucency": { + "enabled": true, + "value": 0.5 + } + } + ], + "supported-platforms": { + "circles": ["watchOS"], + "squares": "shared" + } +} diff --git a/fluxer_desktop/build_resources/icons-stable/FluxerTrayTemplate.png b/fluxer_desktop/build_resources/icons-stable/FluxerTrayTemplate.png new file mode 100644 index 0000000..62516c5 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/FluxerTrayTemplate.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/FluxerTrayTemplate@2x.png b/fluxer_desktop/build_resources/icons-stable/FluxerTrayTemplate@2x.png new file mode 100644 index 0000000..e51c1f4 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/FluxerTrayTemplate@2x.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/Square107x107Logo.png b/fluxer_desktop/build_resources/icons-stable/Square107x107Logo.png new file mode 100644 index 0000000..3a694e4 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/Square107x107Logo.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/Square142x142Logo.png b/fluxer_desktop/build_resources/icons-stable/Square142x142Logo.png new file mode 100644 index 0000000..ad1adc3 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/Square142x142Logo.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/Square150x150Logo.png b/fluxer_desktop/build_resources/icons-stable/Square150x150Logo.png new file mode 100644 index 0000000..bb96dea Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/Square150x150Logo.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/Square284x284Logo.png b/fluxer_desktop/build_resources/icons-stable/Square284x284Logo.png new file mode 100644 index 0000000..1758af0 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/Square284x284Logo.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/Square30x30Logo.png b/fluxer_desktop/build_resources/icons-stable/Square30x30Logo.png new file mode 100644 index 0000000..b8407cc Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/Square30x30Logo.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/Square310x310Logo.png b/fluxer_desktop/build_resources/icons-stable/Square310x310Logo.png new file mode 100644 index 0000000..0094925 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/Square310x310Logo.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/Square44x44Logo.png b/fluxer_desktop/build_resources/icons-stable/Square44x44Logo.png new file mode 100644 index 0000000..fee190a Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/Square44x44Logo.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/Square71x71Logo.png b/fluxer_desktop/build_resources/icons-stable/Square71x71Logo.png new file mode 100644 index 0000000..4663e7b Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/Square71x71Logo.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/Square89x89Logo.png b/fluxer_desktop/build_resources/icons-stable/Square89x89Logo.png new file mode 100644 index 0000000..b76b7f1 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/Square89x89Logo.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/StoreLogo.png b/fluxer_desktop/build_resources/icons-stable/StoreLogo.png new file mode 100644 index 0000000..ba95535 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/StoreLogo.png differ diff --git a/fluxer_desktop/build_resources/icons-stable/_compiled/AppIcon.icns b/fluxer_desktop/build_resources/icons-stable/_compiled/AppIcon.icns new file mode 100644 index 0000000..c5cea49 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/_compiled/AppIcon.icns differ diff --git a/fluxer_desktop/build_resources/icons-stable/_compiled/Assets.car b/fluxer_desktop/build_resources/icons-stable/_compiled/Assets.car new file mode 100644 index 0000000..9ed50a3 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/_compiled/Assets.car differ diff --git a/fluxer_desktop/build_resources/icons-stable/_compiled/assetcatalog_generated_info.plist b/fluxer_desktop/build_resources/icons-stable/_compiled/assetcatalog_generated_info.plist new file mode 100644 index 0000000..cfc5537 --- /dev/null +++ b/fluxer_desktop/build_resources/icons-stable/_compiled/assetcatalog_generated_info.plist @@ -0,0 +1,6 @@ + + + + + + diff --git a/fluxer_desktop/build_resources/icons-stable/_compiled/icon.part.plist b/fluxer_desktop/build_resources/icons-stable/_compiled/icon.part.plist new file mode 100644 index 0000000..c98f6ca --- /dev/null +++ b/fluxer_desktop/build_resources/icons-stable/_compiled/icon.part.plist @@ -0,0 +1,11 @@ + + + + + + CFBundleIconFile + AppIcon + CFBundleIconName + AppIcon + + diff --git a/fluxer_desktop/build_resources/icons-stable/badges/badge-1.ico b/fluxer_desktop/build_resources/icons-stable/badges/badge-1.ico new file mode 100644 index 0000000..0e713c3 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/badges/badge-1.ico differ diff --git a/fluxer_desktop/build_resources/icons-stable/badges/badge-10.ico b/fluxer_desktop/build_resources/icons-stable/badges/badge-10.ico new file mode 100644 index 0000000..68dfee3 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/badges/badge-10.ico differ diff --git a/fluxer_desktop/build_resources/icons-stable/badges/badge-11.ico b/fluxer_desktop/build_resources/icons-stable/badges/badge-11.ico new file mode 100644 index 0000000..20f89f6 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/badges/badge-11.ico differ diff --git a/fluxer_desktop/build_resources/icons-stable/badges/badge-2.ico b/fluxer_desktop/build_resources/icons-stable/badges/badge-2.ico new file mode 100644 index 0000000..454254f Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/badges/badge-2.ico differ diff --git a/fluxer_desktop/build_resources/icons-stable/badges/badge-3.ico b/fluxer_desktop/build_resources/icons-stable/badges/badge-3.ico new file mode 100644 index 0000000..e7b46de Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/badges/badge-3.ico differ diff --git a/fluxer_desktop/build_resources/icons-stable/badges/badge-4.ico b/fluxer_desktop/build_resources/icons-stable/badges/badge-4.ico new file mode 100644 index 0000000..038d9c9 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/badges/badge-4.ico differ diff --git a/fluxer_desktop/build_resources/icons-stable/badges/badge-5.ico b/fluxer_desktop/build_resources/icons-stable/badges/badge-5.ico new file mode 100644 index 0000000..f7dcba3 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/badges/badge-5.ico differ diff --git a/fluxer_desktop/build_resources/icons-stable/badges/badge-6.ico b/fluxer_desktop/build_resources/icons-stable/badges/badge-6.ico new file mode 100644 index 0000000..956d582 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/badges/badge-6.ico differ diff --git a/fluxer_desktop/build_resources/icons-stable/badges/badge-7.ico b/fluxer_desktop/build_resources/icons-stable/badges/badge-7.ico new file mode 100644 index 0000000..0bfc15d Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/badges/badge-7.ico differ diff --git a/fluxer_desktop/build_resources/icons-stable/badges/badge-8.ico b/fluxer_desktop/build_resources/icons-stable/badges/badge-8.ico new file mode 100644 index 0000000..7d356f6 Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/badges/badge-8.ico differ diff --git a/fluxer_desktop/build_resources/icons-stable/badges/badge-9.ico b/fluxer_desktop/build_resources/icons-stable/badges/badge-9.ico new file mode 100644 index 0000000..34149cb Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/badges/badge-9.ico differ diff --git a/fluxer_desktop/build_resources/icons-stable/icon.ico b/fluxer_desktop/build_resources/icons-stable/icon.ico new file mode 100644 index 0000000..6541d8e Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/icon.ico differ diff --git a/fluxer_desktop/build_resources/icons-stable/icon.png b/fluxer_desktop/build_resources/icons-stable/icon.png new file mode 100644 index 0000000..4dd2f7a Binary files /dev/null and b/fluxer_desktop/build_resources/icons-stable/icon.png differ diff --git a/fluxer_desktop/build_resources/profiles/Fluxer.provisionprofile b/fluxer_desktop/build_resources/profiles/Fluxer.provisionprofile new file mode 100644 index 0000000..63b287b Binary files /dev/null and b/fluxer_desktop/build_resources/profiles/Fluxer.provisionprofile differ diff --git a/fluxer_desktop/build_resources/profiles/Fluxer_Canary.provisionprofile b/fluxer_desktop/build_resources/profiles/Fluxer_Canary.provisionprofile new file mode 100644 index 0000000..93e9d85 Binary files /dev/null and b/fluxer_desktop/build_resources/profiles/Fluxer_Canary.provisionprofile differ diff --git a/fluxer_desktop/electron-builder.config.cjs b/fluxer_desktop/electron-builder.config.cjs new file mode 100644 index 0000000..305bd3b --- /dev/null +++ b/fluxer_desktop/electron-builder.config.cjs @@ -0,0 +1,1309 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const isCanary = process.env.BUILD_CHANNEL === 'canary'; +const {execFile} = require('node:child_process'); +const fs = require('node:fs/promises'); +const os = require('node:os'); +const path = require('node:path'); +const {promisify} = require('node:util'); +const execFileAsync = promisify(execFile); +const productName = isCanary ? 'Fluxer Canary' : 'Fluxer'; +const appId = isCanary ? 'app.fluxer.canary' : 'app.fluxer'; +const iconDir = isCanary ? 'icons-canary' : 'icons-stable'; +const packageName = isCanary ? 'fluxer_desktop_canary' : 'fluxer_desktop'; +const linuxPackageName = isCanary ? 'fluxer-canary' : 'fluxer'; +const desktopBuildVariant = process.env.FLUXER_DESKTOP_BUILD_VARIANT || process.env.DESKTOP_VARIANT || 'default'; +const windowsGameCaptureModuleEnabled = + desktopBuildVariant === 'windows-game-capture' || process.env.FLUXER_WINDOWS_GAME_CAPTURE_MODULE_ENABLED === 'true'; +const linuxDesktopActionIds = ['open-settings', 'new-dm']; +const linuxDesktopActionList = `${linuxDesktopActionIds.join(';')};`; +const rpmBuildIdFilePrefix = '/usr/lib/.build-id'; +const rpmBuildIdLinkFpmArgs = [ + '--rpm-rpmbuild-define', + '_build_id_links none', + '--rpm-rpmbuild-define', + '_missing_build_ids_terminate_build 0', +]; +const macOSMinimumSystemVersion = '12.0'; +const isLinuxBuild = process.argv.includes('--linux'); +const isMacBuild = process.argv.includes('--mac'); +const isWindowsBuild = process.argv.includes('--win'); +const targetPlatform = isLinuxBuild ? 'linux' : isMacBuild ? 'darwin' : isWindowsBuild ? 'win32' : process.platform; +const metadataName = isLinuxBuild ? linuxPackageName : packageName; +const provisioningProfile = isCanary + ? 'build_resources/profiles/Fluxer_Canary.provisionprofile' + : 'build_resources/profiles/Fluxer.provisionprofile'; +const supportedTargetArchs = ['x64', 'arm64']; +const electronArch = process.env.ELECTRON_ARCH; +const cliTargetArch = supportedTargetArchs.find((arch) => process.argv.includes(`--${arch}`)) || null; +const targetNativeArch = electronArch || cliTargetArch; + +if (electronArch && !supportedTargetArchs.includes(electronArch)) { + throw new Error(`Unsupported ELECTRON_ARCH: ${electronArch}`); +} + +const targetArchs = electronArch ? [electronArch] : supportedTargetArchs; +const winTargets = [ + { + target: 'dir', + arch: targetArchs, + }, +]; +const fluxerNativePackages = [ + '@fluxer/mac-app-audio', + '@fluxer/mac-clipboard', + '@fluxer/mac-screen-capture', + '@fluxer/mac-sysctl', + '@fluxer/mac-tcc', + '@fluxer/macos-input-hook', + '@fluxer/win-process-loopback', + ...(windowsGameCaptureModuleEnabled ? ['@fluxer/win-game-capture'] : []), + '@fluxer/win-clipboard', + '@fluxer/win-shell', + '@fluxer/win-toast', + '@fluxer/windows-input-hook', + '@fluxer/linux-audio-capture', + '@fluxer/linux-portals', + '@fluxer/linux-screen-capture', + '@fluxer/linux-notifications', + '@fluxer/linux-evdev', + '@fluxer/linux-input-hook', + '@fluxer/system-hunspell', + '@fluxer/platform-info', + '@fluxer/webauthn', + '@fluxer/webrtc-sender', +]; +const fluxerNativePackagesByPlatform = { + darwin: [ + '@fluxer/mac-app-audio', + '@fluxer/mac-clipboard', + '@fluxer/mac-screen-capture', + '@fluxer/mac-sysctl', + '@fluxer/mac-tcc', + '@fluxer/macos-input-hook', + '@fluxer/platform-info', + '@fluxer/webauthn', + '@fluxer/webrtc-sender', + ], + win32: [ + '@fluxer/win-process-loopback', + ...(windowsGameCaptureModuleEnabled ? ['@fluxer/win-game-capture'] : []), + '@fluxer/win-clipboard', + '@fluxer/win-shell', + '@fluxer/win-toast', + '@fluxer/windows-input-hook', + '@fluxer/platform-info', + '@fluxer/webauthn', + '@fluxer/webrtc-sender', + ], + linux: [ + '@fluxer/linux-audio-capture', + '@fluxer/linux-portals', + '@fluxer/linux-screen-capture', + '@fluxer/linux-notifications', + '@fluxer/linux-evdev', + '@fluxer/linux-input-hook', + '@fluxer/system-hunspell', + '@fluxer/platform-info', + '@fluxer/webauthn', + '@fluxer/webrtc-sender', + ], +}; +const velopackNativeFiles = [ + 'velopack_nodeffi_linux_arm64_gnu.node', + 'velopack_nodeffi_linux_x64_gnu.node', + 'velopack_nodeffi_osx.node', + 'velopack_nodeffi_win_arm64_msvc.node', + 'velopack_nodeffi_win_x64_msvc.node', + 'velopack_nodeffi_win_x86_msvc.node', +]; +const nativeRuntimeFilePatterns = [ + 'node_modules/@fluxer/mac-app-audio/package.json', + 'node_modules/@fluxer/mac-app-audio/index.js', + 'node_modules/@fluxer/mac-app-audio/loader-diagnostics.cjs', + 'node_modules/@fluxer/mac-app-audio/*.node', + 'node_modules/@fluxer/mac-screen-capture/package.json', + 'node_modules/@fluxer/mac-screen-capture/index.js', + 'node_modules/@fluxer/mac-screen-capture/loader-diagnostics.cjs', + 'node_modules/@fluxer/mac-screen-capture/*.node', + 'node_modules/@fluxer/mac-clipboard/package.json', + 'node_modules/@fluxer/mac-clipboard/index.js', + 'node_modules/@fluxer/mac-clipboard/loader-diagnostics.cjs', + 'node_modules/@fluxer/mac-clipboard/*.node', + 'node_modules/@fluxer/mac-sysctl/package.json', + 'node_modules/@fluxer/mac-sysctl/index.js', + 'node_modules/@fluxer/mac-sysctl/loader-diagnostics.cjs', + 'node_modules/@fluxer/mac-sysctl/*.node', + 'node_modules/@fluxer/mac-tcc/package.json', + 'node_modules/@fluxer/mac-tcc/index.js', + 'node_modules/@fluxer/mac-tcc/loader-diagnostics.cjs', + 'node_modules/@fluxer/mac-tcc/*.node', + 'node_modules/@fluxer/win-process-loopback/package.json', + 'node_modules/@fluxer/win-process-loopback/index.js', + 'node_modules/@fluxer/win-process-loopback/binding.js', + 'node_modules/@fluxer/win-process-loopback/loader-diagnostics.cjs', + 'node_modules/@fluxer/win-process-loopback/*.node', + ...(windowsGameCaptureModuleEnabled + ? [ + 'node_modules/@fluxer/win-game-capture/package.json', + 'node_modules/@fluxer/win-game-capture/index.js', + 'node_modules/@fluxer/win-game-capture/loader-diagnostics.cjs', + 'node_modules/@fluxer/win-game-capture/*.node', + 'node_modules/@fluxer/win-game-capture/*.dll', + 'node_modules/@fluxer/win-game-capture/*.exe', + 'node_modules/@fluxer/win-game-capture/compatibility.json', + 'node_modules/@fluxer/win-game-capture/fluxer-vulkan-layer.*.json', + ] + : []), + 'node_modules/@fluxer/win-clipboard/package.json', + 'node_modules/@fluxer/win-clipboard/index.js', + 'node_modules/@fluxer/win-clipboard/loader-diagnostics.cjs', + 'node_modules/@fluxer/win-clipboard/*.node', + 'node_modules/@fluxer/win-shell/package.json', + 'node_modules/@fluxer/win-shell/index.js', + 'node_modules/@fluxer/win-shell/loader-diagnostics.cjs', + 'node_modules/@fluxer/win-shell/*.node', + 'node_modules/@fluxer/win-toast/package.json', + 'node_modules/@fluxer/win-toast/index.js', + 'node_modules/@fluxer/win-toast/loader-diagnostics.cjs', + 'node_modules/@fluxer/win-toast/*.node', + 'node_modules/@fluxer/linux-audio-capture/package.json', + 'node_modules/@fluxer/linux-audio-capture/index.js', + 'node_modules/@fluxer/linux-audio-capture/loader-diagnostics.cjs', + 'node_modules/@fluxer/linux-audio-capture/*.node', + 'node_modules/@fluxer/linux-portals/package.json', + 'node_modules/@fluxer/linux-portals/index.js', + 'node_modules/@fluxer/linux-portals/loader-diagnostics.cjs', + 'node_modules/@fluxer/linux-portals/*.node', + 'node_modules/@fluxer/linux-screen-capture/package.json', + 'node_modules/@fluxer/linux-screen-capture/index.js', + 'node_modules/@fluxer/linux-screen-capture/loader-diagnostics.cjs', + 'node_modules/@fluxer/linux-screen-capture/*.node', + 'node_modules/@fluxer/linux-screen-capture/THIRD_PARTY_OBS_VKCAPTURE.md', + 'node_modules/@fluxer/linux-screen-capture/obs-vkcapture/**/*', + 'node_modules/@fluxer/linux-notifications/package.json', + 'node_modules/@fluxer/linux-notifications/index.js', + 'node_modules/@fluxer/linux-notifications/loader-diagnostics.cjs', + 'node_modules/@fluxer/linux-notifications/*.node', + 'node_modules/@fluxer/linux-evdev/package.json', + 'node_modules/@fluxer/linux-evdev/index.js', + 'node_modules/@fluxer/linux-evdev/loader-diagnostics.cjs', + 'node_modules/@fluxer/linux-evdev/*.node', + 'node_modules/@fluxer/system-hunspell/package.json', + 'node_modules/@fluxer/system-hunspell/index.js', + 'node_modules/@fluxer/system-hunspell/loader-diagnostics.cjs', + 'node_modules/@fluxer/system-hunspell/*.node', + 'node_modules/@fluxer/macos-input-hook/package.json', + 'node_modules/@fluxer/macos-input-hook/index.js', + 'node_modules/@fluxer/macos-input-hook/loader-diagnostics.cjs', + 'node_modules/@fluxer/macos-input-hook/*.node', + 'node_modules/@fluxer/windows-input-hook/package.json', + 'node_modules/@fluxer/windows-input-hook/index.js', + 'node_modules/@fluxer/windows-input-hook/loader-diagnostics.cjs', + 'node_modules/@fluxer/windows-input-hook/*.node', + 'node_modules/@fluxer/linux-input-hook/package.json', + 'node_modules/@fluxer/linux-input-hook/index.js', + 'node_modules/@fluxer/linux-input-hook/loader-diagnostics.cjs', + 'node_modules/@fluxer/linux-input-hook/*.node', + 'node_modules/@fluxer/platform-info/package.json', + 'node_modules/@fluxer/platform-info/index.js', + 'node_modules/@fluxer/platform-info/loader-diagnostics.cjs', + 'node_modules/@fluxer/platform-info/*.node', + 'node_modules/@fluxer/webauthn/package.json', + 'node_modules/@fluxer/webauthn/index.js', + 'node_modules/@fluxer/webauthn/index.d.ts', + 'node_modules/@fluxer/webauthn/loader-diagnostics.cjs', + 'node_modules/@fluxer/webauthn/*.node', + 'node_modules/@fluxer/webauthn/*.so*', + 'node_modules/@fluxer/webrtc-sender/package.json', + 'node_modules/@fluxer/webrtc-sender/index.js', + 'node_modules/@fluxer/webrtc-sender/index.d.ts', + 'node_modules/@fluxer/webrtc-sender/*.node', + 'node_modules/.pnpm/@fluxer+*/node_modules/@fluxer/*/loader-diagnostics.cjs', + 'node_modules/.pnpm/@fluxer+win-process-loopback@*/node_modules/@fluxer/win-process-loopback/*.node', + ...(windowsGameCaptureModuleEnabled + ? [ + 'node_modules/.pnpm/@fluxer+win-game-capture@*/node_modules/@fluxer/win-game-capture/*.node', + 'node_modules/.pnpm/@fluxer+win-game-capture@*/node_modules/@fluxer/win-game-capture/*.dll', + 'node_modules/.pnpm/@fluxer+win-game-capture@*/node_modules/@fluxer/win-game-capture/*.exe', + 'node_modules/.pnpm/@fluxer+win-game-capture@*/node_modules/@fluxer/win-game-capture/compatibility.json', + 'node_modules/.pnpm/@fluxer+win-game-capture@*/node_modules/@fluxer/win-game-capture/fluxer-vulkan-layer.*.json', + ] + : []), + 'node_modules/.pnpm/@fluxer+win-clipboard@*/node_modules/@fluxer/win-clipboard/*.node', + 'node_modules/.pnpm/@fluxer+win-shell@*/node_modules/@fluxer/win-shell/*.node', + 'node_modules/.pnpm/@fluxer+win-toast@*/node_modules/@fluxer/win-toast/*.node', + 'node_modules/.pnpm/@fluxer+windows-input-hook@*/node_modules/@fluxer/windows-input-hook/*.node', + 'node_modules/.pnpm/@fluxer+linux-audio-capture@*/node_modules/@fluxer/linux-audio-capture/*.node', + 'node_modules/.pnpm/@fluxer+linux-portals@*/node_modules/@fluxer/linux-portals/*.node', + 'node_modules/.pnpm/@fluxer+linux-screen-capture@*/node_modules/@fluxer/linux-screen-capture/*.node', + 'node_modules/.pnpm/@fluxer+linux-screen-capture@*/node_modules/@fluxer/linux-screen-capture/THIRD_PARTY_OBS_VKCAPTURE.md', + 'node_modules/.pnpm/@fluxer+linux-screen-capture@*/node_modules/@fluxer/linux-screen-capture/obs-vkcapture/**/*', + 'node_modules/.pnpm/@fluxer+linux-notifications@*/node_modules/@fluxer/linux-notifications/*.node', + 'node_modules/.pnpm/@fluxer+linux-evdev@*/node_modules/@fluxer/linux-evdev/*.node', + 'node_modules/.pnpm/@fluxer+linux-input-hook@*/node_modules/@fluxer/linux-input-hook/*.node', + 'node_modules/.pnpm/@fluxer+mac-app-audio@*/node_modules/@fluxer/mac-app-audio/*.node', + 'node_modules/.pnpm/@fluxer+mac-screen-capture@*/node_modules/@fluxer/mac-screen-capture/*.node', + 'node_modules/.pnpm/@fluxer+mac-clipboard@*/node_modules/@fluxer/mac-clipboard/*.node', + 'node_modules/.pnpm/@fluxer+mac-sysctl@*/node_modules/@fluxer/mac-sysctl/*.node', + 'node_modules/.pnpm/@fluxer+mac-tcc@*/node_modules/@fluxer/mac-tcc/*.node', + 'node_modules/.pnpm/@fluxer+macos-input-hook@*/node_modules/@fluxer/macos-input-hook/*.node', + 'node_modules/.pnpm/@fluxer+platform-info@*/node_modules/@fluxer/platform-info/*.node', + 'node_modules/.pnpm/@fluxer+webauthn@*/node_modules/@fluxer/webauthn/*.node', + 'node_modules/.pnpm/@fluxer+webauthn@*/node_modules/@fluxer/webauthn/*.so*', + 'node_modules/.pnpm/@fluxer+webrtc-sender@*/node_modules/@fluxer/webrtc-sender/*.node', +]; +const nativeBuildArtifactExcludes = [ + '!node_modules/@fluxer/**/src/**/*', + '!node_modules/@fluxer/**/target/**/*', + '!node_modules/@fluxer/**/build/**/*', + '!node_modules/@fluxer/**/Cargo.toml', + '!node_modules/@fluxer/**/Cargo.lock', + '!node_modules/@fluxer/**/CMakeLists.txt', + '!node_modules/@fluxer/**/tsconfig.json', + '!node_modules/@fluxer/**/*.d.ts', + '!node_modules/@fluxer/**/*.d.ts.map', + '!node_modules/@fluxer/**/*.map', + '!node_modules/@fluxer/**/*.rs', + '!node_modules/@fluxer/**/*.swift', +]; +const packagedRuntimeArtifactExcludes = [ + '!dist/**/*.map', + '!node_modules/**/.cache/**/*', + '!node_modules/**/.github/**/*', + '!node_modules/**/.yarn/**/*', + '!node_modules/**/.yarnrc.yml', + '!node_modules/**/*.map', + '!node_modules/**/*.d.ts', + '!node_modules/**/*.d.ts.map', + '!node_modules/**/*.tsbuildinfo', + '!node_modules/**/tsconfig.json', + '!node_modules/**/tsconfig.*.json', + '!node_modules/**/README*', + '!node_modules/**/CHANGELOG*', + '!node_modules/hunspell-asm/src/**/*', + '!node_modules/hunspell-asm/dist/esm/**/*', + '!node_modules/hunspell-asm/dist/types/**/*', + '!node_modules/hunspell-asm/dist/cjs/lib/browser/**/*', + '!node_modules/emscripten-wasm-loader/src/**/*', + '!node_modules/emscripten-wasm-loader/dist/esm/**/*', + '!node_modules/emscripten-wasm-loader/dist/types/**/*', +]; +const bundledDependencyExcludes = [ + '!node_modules/@homebridge/dbus-native/**/*', + '!node_modules/@simplewebauthn/browser/**/*', + '!node_modules/duplexer/**/*', + '!node_modules/event-stream/**/*', + '!node_modules/from/**/*', + '!node_modules/hexy/**/*', + '!node_modules/long/**/*', + '!node_modules/map-stream/**/*', + '!node_modules/minimist/**/*', + '!node_modules/pause-stream/**/*', + '!node_modules/safe-buffer/**/*', + '!node_modules/sax/**/*', + '!node_modules/split/**/*', + '!node_modules/stream-combiner/**/*', + '!node_modules/through/**/*', + '!node_modules/xml2js/**/*', + '!node_modules/xmlbuilder/**/*', +]; +const windowsGameCapturePackageExcludes = !windowsGameCaptureModuleEnabled + ? ['!node_modules/@fluxer/win-game-capture/**/*', '!node_modules/.pnpm/@fluxer+win-game-capture@*/**/*'] + : []; +const platformNativeRuntimeExcludes = platformNativeExcludes(targetPlatform, targetNativeArch); +const platformRuntimeDependencyExcludes = + targetPlatform === 'darwin' + ? [] + : ['!node_modules/github-url-to-object/**/*', '!node_modules/ms/**/*', '!node_modules/update-electron-app/**/*']; +const linuxDesktopEntry = { + Name: productName, + GenericName: 'Instant Messenger', + Comment: isCanary ? 'Canary build of Fluxer' : 'Instant messaging and VoIP', + Keywords: 'chat;im;messaging;messenger;voip;voice;video;call;', + Categories: 'Network;InstantMessaging;Chat;', + StartupWMClass: linuxPackageName, + StartupNotify: 'true', + SingleMainWindow: 'true', + MimeType: 'x-scheme-handler/fluxer;', + 'X-GNOME-UsesNotifications': 'true', +}; +const linuxDesktopEntryWithActions = { + ...linuxDesktopEntry, + Actions: linuxDesktopActionList, +}; +const linuxInstalledExecPath = quoteDesktopExecArg(path.posix.join('/opt', productName, linuxPackageName)); +const linuxDesktopActions = { + 'open-settings': { + Name: 'Open Settings', + Exec: buildLinuxDesktopTaskExec('open-settings'), + }, + 'new-dm': { + Name: 'New Direct Message', + Exec: buildLinuxDesktopTaskExec('new-dm'), + }, +}; + +function velopackNativeFile(platform, arch) { + if (platform === 'darwin') return 'velopack_nodeffi_osx.node'; + if (!arch) return null; + if (platform === 'win32') return `velopack_nodeffi_win_${arch}_msvc.node`; + if (platform === 'linux') return `velopack_nodeffi_linux_${arch}_gnu.node`; + return null; +} + +function platformNativeExcludes(platform, arch) { + if (!arch) return []; + const keepFluxerPackages = new Set(fluxerNativePackagesByPlatform[platform] ?? []); + const keepVelopackNativeFile = velopackNativeFile(platform, arch); + return [ + ...fluxerNativePackages + .filter((packageName) => !keepFluxerPackages.has(packageName)) + .map((packageName) => `!node_modules/${packageName}/**/*`), + ...(platform === 'win32' + ? velopackNativeFiles + .filter((fileName) => fileName !== keepVelopackNativeFile) + .map((fileName) => `!node_modules/velopack/lib/native/${fileName}`) + : ['!node_modules/velopack/**/*']), + ]; +} + +function quoteDesktopExecArg(value) { + return `"${value.replace(/(["`$\\])/g, '\\$1')}"`; +} + +function buildLinuxDesktopTaskExec(taskId) { + return `${linuxInstalledExecPath} --fluxer-task=${taskId} %U`; +} + +function normalizeArch(arch) { + if (arch === 'x64' || arch === 'arm64') { + return arch; + } + if (arch === 1) return 'x64'; + if (arch === 3) return 'arm64'; + return electronArch || process.arch; +} + +function platformTag(platform, arch) { + if (platform === 'darwin') return `darwin-${arch}`; + if (platform === 'win32') return `win32-${arch}-msvc`; + if (platform === 'linux') return `linux-${arch}-gnu`; + return null; +} + +function addWindowsGameCaptureArtifacts(artifacts, tag, arch) { + if (!windowsGameCaptureModuleEnabled) return; + const add = (relativePath) => { + artifacts.push({ + packageName: '@fluxer/win-game-capture', + relativePath, + }); + }; + add(`win-game-capture.${tag}.node`); + add(`fluxer-game-hook.${tag}.dll`); + add(`fluxer-inject-helper.${tag}.exe`); + add(`fluxer-vulkan-layer.${tag}.dll`); + add(`fluxer-vulkan-layer.${tag}.json`); + if (arch === 'x64') { + add('fluxer-game-hook.win32-ia32-msvc.dll'); + add('fluxer-inject-helper.win32-ia32-msvc.exe'); + } +} + +function expectedNativeRuntimeArtifacts(platform, arch) { + const tag = platformTag(platform, arch); + if (!tag) return []; + const artifacts = []; + artifacts.push({ + packageName: '@fluxer/webauthn', + relativePath: `webauthn.${tag}.node`, + }); + artifacts.push({ + packageName: '@fluxer/webrtc-sender', + relativePath: `webrtc-sender.${tag}.node`, + }); + if (platform === 'darwin') { + artifacts.push({ + packageName: '@fluxer/mac-app-audio', + relativePath: `mac-app-audio.darwin-${arch}.node`, + }); + artifacts.push({ + packageName: '@fluxer/mac-screen-capture', + relativePath: `mac-screen-capture.darwin-${arch}.node`, + }); + artifacts.push({ + packageName: '@fluxer/mac-clipboard', + relativePath: `mac-clipboard.darwin-${arch}.node`, + }); + artifacts.push({ + packageName: '@fluxer/mac-sysctl', + relativePath: `mac-sysctl.darwin-${arch}.node`, + }); + artifacts.push({ + packageName: '@fluxer/mac-tcc', + relativePath: `mac-tcc.darwin-${arch}.node`, + }); + artifacts.push({ + packageName: '@fluxer/macos-input-hook', + relativePath: `macos-input-hook.darwin-${arch}.node`, + }); + artifacts.push({ + packageName: '@fluxer/platform-info', + relativePath: `platform-info.${tag}.node`, + }); + } else if (platform === 'win32') { + artifacts.push({ + packageName: '@fluxer/win-process-loopback', + relativePath: `win-process-loopback.${tag}.node`, + }); + addWindowsGameCaptureArtifacts(artifacts, tag, arch); + artifacts.push({ + packageName: '@fluxer/win-clipboard', + relativePath: `win-clipboard.${tag}.node`, + }); + artifacts.push({ + packageName: '@fluxer/win-shell', + relativePath: `win-shell.${tag}.node`, + }); + artifacts.push({ + packageName: '@fluxer/win-toast', + relativePath: `win-toast.${tag}.node`, + }); + artifacts.push({ + packageName: '@fluxer/windows-input-hook', + relativePath: `windows-input-hook.${tag}.node`, + }); + artifacts.push({ + packageName: '@fluxer/platform-info', + relativePath: `platform-info.${tag}.node`, + }); + } else if (platform === 'linux') { + artifacts.push({ + packageName: '@fluxer/linux-audio-capture', + relativePath: `linux-audio-capture.${tag}.node`, + }); + artifacts.push({ + packageName: '@fluxer/linux-portals', + relativePath: `linux-portals.${tag}.node`, + }); + artifacts.push({ + packageName: '@fluxer/linux-screen-capture', + relativePath: `linux-screen-capture.${tag}.node`, + }); + artifacts.push({ + packageName: '@fluxer/linux-notifications', + relativePath: `linux-notifications.${tag}.node`, + }); + artifacts.push({ + packageName: '@fluxer/linux-evdev', + relativePath: `linux-evdev.${tag}.node`, + }); + artifacts.push({ + packageName: '@fluxer/system-hunspell', + relativePath: `system-hunspell.${tag}.node`, + }); + artifacts.push({ + packageName: '@fluxer/linux-input-hook', + relativePath: `linux-input-hook.${tag}.node`, + }); + artifacts.push({ + packageName: '@fluxer/platform-info', + relativePath: `platform-info.${tag}.node`, + }); + } + return artifacts; +} + +function isLinuxSharedLibraryArtifact(fileName) { + return /\.so(?:\.|$)/.test(fileName); +} + +async function expectedNativeRuntimeArtifactsForAppDir(platform, arch, appDir) { + const artifacts = expectedNativeRuntimeArtifacts(platform, arch); + if (platform !== 'linux') return artifacts; + const webAuthnRoot = path.join(appDir, 'node_modules', '@fluxer', 'webauthn'); + const linuxWebAuthnRuntimeLibraries = new Set(['libfido2.so.1']); + try { + for (const entry of await fs.readdir(webAuthnRoot, {withFileTypes: true})) { + if (entry.isFile() && isLinuxSharedLibraryArtifact(entry.name)) { + linuxWebAuthnRuntimeLibraries.add(entry.name); + } + } + } catch (error) { + if (!error || error.code !== 'ENOENT') throw error; + } + for (const libraryName of [...linuxWebAuthnRuntimeLibraries].sort()) { + artifacts.push({ + packageName: '@fluxer/webauthn', + relativePath: libraryName, + }); + } + return artifacts; +} + +function packagePathParts(packageName) { + const parts = packageName.split('/'); + if (parts.length === 2 && parts[0].startsWith('@')) { + return parts; + } + return [packageName]; +} + +function isNonEmptyString(value) { + return typeof value === 'string' && value.length > 0; +} + +function resolveAppDir(context) { + const candidates = [ + context.appDir, + context.packager?.info?.appDir, + context.packager?.appDir, + context.packager?.projectDir, + context.packager?.info?.projectDir, + process.cwd(), + ]; + const appDir = candidates.find(isNonEmptyString); + if (!appDir) { + throw new Error('Unable to resolve electron-builder app directory for native artifact verification.'); + } + return appDir; +} + +async function fileExists(filePath) { + return fs + .stat(filePath) + .then((stat) => stat.isFile()) + .catch((error) => { + if (error && error.code === 'ENOENT') { + return false; + } + throw error; + }); +} + +function expectedDarwinMachOArch(arch) { + if (arch === 'x64') return 'x86_64'; + if (arch === 'arm64') return 'arm64'; + return null; +} + +async function darwinMachOArchitectures(filePath) { + const {stdout} = await execFileAsync('lipo', ['-archs', filePath]); + return stdout.trim().split(/\s+/).filter(Boolean); +} + +async function darwinMachOFileTypes(filePath) { + const {stdout} = await execFileAsync('otool', ['-hv', filePath]); + const knownFileTypes = new Set(['OBJECT', 'EXECUTE', 'FVMLIB', 'CORE', 'PRELOAD', 'DYLIB', 'DYLINKER', 'BUNDLE']); + return stdout + .split(/\r?\n/) + .flatMap((line) => line.trim().split(/\s+/)) + .filter((token) => knownFileTypes.has(token)); +} + +async function darwinMachOLoadCommands(filePath) { + const {stdout} = await execFileAsync('otool', ['-l', filePath]); + return stdout + .split(/\r?\n/) + .map((line) => line.trim().match(/^cmd\s+(\S+)$/)?.[1]) + .filter(Boolean); +} + +async function verifyDarwinNativeArchitectures(platform, arch, entries, stage) { + if (platform !== 'darwin') return; + const expectedArch = expectedDarwinMachOArch(arch); + if (!expectedArch) return; + const mismatches = []; + for (const entry of entries) { + if (!(await fileExists(entry.path))) continue; + const archs = await darwinMachOArchitectures(entry.path); + const fileTypes = await darwinMachOFileTypes(entry.path); + if (!archs.includes(expectedArch)) { + mismatches.push(`${entry.label}: has ${archs.join(', ') || ''}; expected ${expectedArch}`); + } + if (arch === 'x64' && archs.includes('x86_64h') && !archs.includes('x86_64')) { + mismatches.push(`${entry.label}: has x86_64h only; expected baseline x86_64 for Intel compatibility`); + } + if (fileTypes.length === 0 || fileTypes.some((fileType) => fileType !== 'BUNDLE' && fileType !== 'DYLIB')) { + mismatches.push( + `${entry.label}: Mach-O file type ${fileTypes.join(', ') || ''}; expected DYLIB or BUNDLE for Node addon loading`, + ); + } + if (fileTypes.includes('BUNDLE')) { + const loadCommands = await darwinMachOLoadCommands(entry.path); + if (loadCommands.includes('LC_ID_DYLIB')) { + mismatches.push( + `${entry.label}: Mach-O BUNDLE contains LC_ID_DYLIB; dyld rejects this combination when Electron loads the addon`, + ); + } + } + } + if (mismatches.length > 0) { + throw new Error( + [ + `Wrong native runtime architecture(s) ${stage} for ${platform}/${arch}:`, + ...mismatches.map((entry) => ` - ${entry}`), + ].join('\n'), + ); + } +} + +async function verifyNativePackageInputs(context) { + if (process.env.FLUXER_SKIP_NATIVE === 'true') return; + const platform = context.electronPlatformName; + const arch = normalizeArch(context.arch); + const appDir = resolveAppDir(context); + const missing = []; + const entries = []; + for (const artifact of await expectedNativeRuntimeArtifactsForAppDir(platform, arch, appDir)) { + const artifactPath = path.join( + appDir, + 'node_modules', + ...packagePathParts(artifact.packageName), + artifact.relativePath, + ); + entries.push({ + label: `${artifact.packageName}/${artifact.relativePath}`, + path: artifactPath, + }); + if (!(await fileExists(artifactPath))) { + missing.push(`${artifact.packageName}/${artifact.relativePath}`); + } + } + if (missing.length > 0) { + throw new Error( + [ + `Missing native runtime artifact(s) before packaging for ${platform}/${arch}:`, + ...missing.map((entry) => ` - ${entry}`), + 'Run `pnpm build` from fluxer_desktop so native build outputs are synced into node_modules before electron-builder runs.', + ].join('\n'), + ); + } + await verifyDarwinNativeArchitectures(platform, arch, entries, 'before packaging'); +} + +async function verifyPackagedNativeArtifacts(context) { + if (process.env.FLUXER_SKIP_NATIVE === 'true') return; + const platform = context.electronPlatformName; + const arch = normalizeArch(context.arch); + const appDir = resolveAppDir(context); + const missing = []; + const entries = []; + for (const artifact of await expectedNativeRuntimeArtifactsForAppDir(platform, arch, appDir)) { + const artifactPath = path.join( + context.appOutDir, + 'resources', + 'app.asar.unpacked', + 'node_modules', + ...packagePathParts(artifact.packageName), + artifact.relativePath, + ); + entries.push({ + label: `${artifact.packageName}/${artifact.relativePath}`, + path: artifactPath, + }); + if (!(await fileExists(artifactPath))) { + missing.push(`${artifact.packageName}/${artifact.relativePath}`); + } + } + if (missing.length > 0) { + throw new Error( + [ + `Missing unpacked native runtime artifact(s) after packaging for ${platform}/${arch}:`, + ...missing.map((entry) => ` - ${entry}`), + 'Check electron-builder asarUnpack patterns and native package artifact sync.', + ].join('\n'), + ); + } + await verifyDarwinNativeArchitectures(platform, arch, entries, 'after packaging'); +} + +async function copyMissingPackagedNativeArtifacts(context) { + const platform = context.electronPlatformName; + const arch = normalizeArch(context.arch); + const appDir = resolveAppDir(context); + for (const artifact of await expectedNativeRuntimeArtifactsForAppDir(platform, arch, appDir)) { + const packageParts = packagePathParts(artifact.packageName); + const sourcePath = path.join(appDir, 'node_modules', ...packageParts, artifact.relativePath); + const targetPath = path.join( + context.appOutDir, + 'resources', + 'app.asar.unpacked', + 'node_modules', + ...packageParts, + artifact.relativePath, + ); + if ((await fileExists(targetPath)) || !(await fileExists(sourcePath))) { + continue; + } + await fs.mkdir(path.dirname(targetPath), {recursive: true}); + await fs.copyFile(sourcePath, targetPath); + } +} + +async function removeIfExists(targetPath) { + await fs.rm(targetPath, {force: true, recursive: true}); +} + +async function cleanupNativeBuildIntermediates(context) { + const unpackedNodeModules = path.join(context.appOutDir, 'resources', 'app.asar.unpacked', 'node_modules'); + const entries = await fs.readdir(unpackedNodeModules, {withFileTypes: true}).catch((error) => { + if (error && error.code === 'ENOENT') { + return []; + } + throw error; + }); + const packageDirs = []; + for (const entry of entries) { + if (!entry.isDirectory()) { + continue; + } + const entryPath = path.join(unpackedNodeModules, entry.name); + if (entry.name.startsWith('@')) { + const scopedEntries = await fs.readdir(entryPath, {withFileTypes: true}); + for (const scopedEntry of scopedEntries) { + if (scopedEntry.isDirectory()) { + packageDirs.push(path.join(entryPath, scopedEntry.name)); + } + } + } else { + packageDirs.push(entryPath); + } + } + await Promise.all(packageDirs.map((packageDir) => removeIfExists(path.join(packageDir, 'build', 'Release', 'obj')))); +} + +async function addLinuxLegacyBinarySymlink(context) { + if (context.electronPlatformName !== 'linux') return; + const legacyName = packageName; + const currentName = linuxPackageName; + if (legacyName === currentName) return; + const linkPath = path.join(context.appOutDir, legacyName); + try { + await fs.symlink(currentName, linkPath); + } catch (error) { + if (!error || error.code !== 'EEXIST') throw error; + } +} + +async function afterPack(context) { + await copyMissingPackagedNativeArtifacts(context); + await cleanupNativeBuildIntermediates(context); + await addLinuxLegacyBinarySymlink(context); + await verifyPackagedNativeArtifacts(context); +} + +async function listRpmPackageFiles(artifactPath) { + try { + const {stdout} = await execFileAsync('rpm', ['-qpl', artifactPath], { + maxBuffer: 16 * 1024 * 1024, + }); + return stdout + .split(/\r?\n/) + .map((line) => line.trim()) + .filter(Boolean); + } catch (error) { + if (error && error.code === 'ENOENT') { + throw new Error(`Cannot inspect RPM artifact ${artifactPath}: rpm executable is not available.`); + } + const stderr = typeof error?.stderr === 'string' ? error.stderr.trim() : ''; + throw new Error(`Cannot inspect RPM artifact ${artifactPath}: ${stderr || error?.message || String(error)}`); + } +} + +async function listDebPackageFiles(artifactPath) { + try { + const {stdout} = await execFileAsync('dpkg-deb', ['--contents', artifactPath], { + maxBuffer: 16 * 1024 * 1024, + }); + return stdout + .split(/\r?\n/) + .map((line) => line.match(/^\S+\s+\S+\s+\d+\s+\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}\s+(.+)$/)?.[1]) + .filter(Boolean) + .map((filePath) => filePath.replace(/^\.\//, '/')); + } catch (error) { + if (error && error.code === 'ENOENT') { + throw new Error(`Cannot inspect DEB artifact ${artifactPath}: dpkg-deb executable is not available.`); + } + const stderr = typeof error?.stderr === 'string' ? error.stderr.trim() : ''; + throw new Error(`Cannot inspect DEB artifact ${artifactPath}: ${stderr || error?.message || String(error)}`); + } +} + +async function verifyRpmArtifactsDoNotOwnBuildIds(buildResult) { + const rpmArtifacts = (buildResult.artifactPaths ?? []).filter( + (artifactPath) => path.extname(artifactPath) === '.rpm', + ); + const violations = []; + for (const artifactPath of rpmArtifacts) { + const packageFiles = await listRpmPackageFiles(artifactPath); + const buildIdFiles = packageFiles.filter( + (filePath) => filePath === rpmBuildIdFilePrefix || filePath.startsWith(`${rpmBuildIdFilePrefix}/`), + ); + if (buildIdFiles.length > 0) { + violations.push({artifactPath, buildIdFiles}); + } + } + if (violations.length === 0) return []; + + const lines = [ + `RPM artifact(s) must not own ${rpmBuildIdFilePrefix} entries.`, + 'These global rpmbuild-generated links collide with other Electron RPMs that bundle the same upstream ELF binaries.', + ]; + for (const {artifactPath, buildIdFiles} of violations) { + lines.push(` - ${path.basename(artifactPath)}:`); + for (const filePath of buildIdFiles.slice(0, 12)) { + lines.push(` ${filePath}`); + } + if (buildIdFiles.length > 12) { + lines.push(` ... ${buildIdFiles.length - 12} more`); + } + } + throw new Error(lines.join('\n')); +} + +function packageFilesContainAppArmorProfile(packageFiles) { + return packageFiles.some((filePath) => filePath.endsWith('/resources/apparmor-profile')); +} + +async function verifyLinuxPackagesContainAppArmorProfile(buildResult) { + const packageArtifacts = (buildResult.artifactPaths ?? []).filter((artifactPath) => + ['.deb', '.rpm'].includes(path.extname(artifactPath)), + ); + const violations = []; + for (const artifactPath of packageArtifacts) { + const extension = path.extname(artifactPath); + const packageFiles = + extension === '.deb' ? await listDebPackageFiles(artifactPath) : await listRpmPackageFiles(artifactPath); + if (!packageFilesContainAppArmorProfile(packageFiles)) { + violations.push({artifactPath}); + } + } + if (violations.length === 0) return; + + const lines = [ + 'Linux package artifact(s) must include the Electron AppArmor profile.', + 'Ubuntu 24.04+ restricts unprivileged user namespaces; packaged Electron apps need this profile so the Chromium sandbox can run without forcing --no-sandbox.', + ]; + for (const {artifactPath} of violations) { + lines.push(` - ${path.basename(artifactPath)}`); + } + throw new Error(lines.join('\n')); +} + +async function readElfNeededLibraries(artifactPath) { + try { + const {stdout} = await execFileAsync('readelf', ['-d', artifactPath], { + maxBuffer: 8 * 1024 * 1024, + }); + return stdout + .split(/\r?\n/) + .map((line) => line.match(/\(NEEDED\)\s+Shared library: \[([^\]]+)\]/)?.[1]) + .filter(Boolean); + } catch (error) { + if (error && error.code === 'ENOENT') { + throw new Error(`Cannot inspect AppImage artifact ${artifactPath}: readelf executable is not available.`); + } + const stderr = typeof error?.stderr === 'string' ? error.stderr.trim() : ''; + throw new Error(`Cannot inspect AppImage artifact ${artifactPath}: ${stderr || error?.message || String(error)}`); + } +} + +async function verifyAppImageArtifactsDoNotNeedFuse2(buildResult) { + const appImageArtifacts = (buildResult.artifactPaths ?? []).filter( + (artifactPath) => path.extname(artifactPath) === '.AppImage', + ); + const violations = []; + for (const artifactPath of appImageArtifacts) { + const neededLibraries = await readElfNeededLibraries(artifactPath); + if (neededLibraries.includes('libfuse.so.2')) { + violations.push({artifactPath, neededLibraries}); + } + } + if (violations.length === 0) return; + + const lines = [ + 'AppImage artifact(s) must not depend on libfuse.so.2.', + 'Use electron-builder toolsets.appimage 1.0.3 so the AppImage runtime is static and works on modern distributions without libfuse2.', + ]; + for (const {artifactPath, neededLibraries} of violations) { + lines.push(` - ${path.basename(artifactPath)} imports: ${neededLibraries.join(', ') || ''}`); + } + throw new Error(lines.join('\n')); +} + +function canExecuteAppImageArtifact(artifactPath) { + const artifactName = path.basename(artifactPath).toLowerCase(); + const hostArch = process.arch; + if (artifactName.includes('arm64') || artifactName.includes('aarch64')) { + return hostArch === 'arm64'; + } + if (artifactName.includes('x86_64') || artifactName.includes('amd64') || artifactName.includes('x64')) { + return hostArch === 'x64'; + } + return true; +} + +async function extractAppImagePattern(artifactPath, pattern, tempDir) { + try { + await execFileAsync(path.resolve(artifactPath), ['--appimage-extract', pattern], { + cwd: tempDir, + maxBuffer: 8 * 1024 * 1024, + }); + } catch (error) { + const stderr = typeof error?.stderr === 'string' ? error.stderr.trim() : ''; + throw new Error( + `Cannot extract ${pattern} from AppImage artifact ${artifactPath}: ${stderr || error?.message || String(error)}`, + ); + } +} + +async function findExtractedFiles(rootDir, predicate) { + const results = []; + async function visit(directory) { + const entries = await fs.readdir(directory, {withFileTypes: true}); + for (const entry of entries) { + const entryPath = path.join(directory, entry.name); + if (entry.isDirectory()) { + await visit(entryPath); + } else if (entry.isFile() && predicate(entryPath)) { + results.push(entryPath); + } + } + } + await visit(rootDir); + return results; +} + +async function inspectAppImageLauncher(artifactPath) { + const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'fluxer-appimage-')); + try { + await extractAppImagePattern(artifactPath, 'AppRun', tempDir); + await extractAppImagePattern(artifactPath, '*.desktop', tempDir); + const squashfsRoot = path.join(tempDir, 'squashfs-root'); + const appRun = await fs.readFile(path.join(squashfsRoot, 'AppRun'), 'utf8'); + const desktopFiles = await findExtractedFiles(squashfsRoot, (filePath) => path.extname(filePath) === '.desktop'); + const violations = []; + + if (desktopFiles.length === 0) { + violations.push('does not contain a desktop entry'); + } + + const appRunUsesNamespaceProbe = /unshare\s+(?:-Ur|--user)\s+true/.test(appRun); + if (!appRunUsesNamespaceProbe || !appRun.includes('NO_SANDBOX=--no-sandbox')) { + violations.push('AppRun does not use the expected user-namespace probe before falling back to --no-sandbox'); + } + + for (const desktopFile of desktopFiles) { + const desktopEntry = await fs.readFile(desktopFile, 'utf8'); + for (const line of desktopEntry.split(/\r?\n/)) { + if (line.startsWith('Exec=') && line.includes('--no-sandbox')) { + violations.push(`${path.basename(desktopFile)} has an unconditional --no-sandbox Exec line`); + } + } + } + + return violations; + } finally { + await fs.rm(tempDir, {recursive: true, force: true}); + } +} + +async function verifyAppImageArtifactsUseSandboxAwareLauncher(buildResult) { + const appImageArtifacts = (buildResult.artifactPaths ?? []).filter( + (artifactPath) => path.extname(artifactPath) === '.AppImage', + ); + const violations = []; + for (const artifactPath of appImageArtifacts) { + if (!canExecuteAppImageArtifact(artifactPath)) { + console.warn( + `Skipping AppImage launcher extraction for ${path.basename(artifactPath)} because it does not match host architecture ${process.arch}.`, + ); + continue; + } + const artifactViolations = await inspectAppImageLauncher(artifactPath); + if (artifactViolations.length > 0) { + violations.push({artifactPath, artifactViolations}); + } + } + if (violations.length === 0) return; + + const lines = [ + 'AppImage artifact(s) must use the static-runtime, sandbox-aware launcher contract.', + 'The desktop entry must not pass --no-sandbox unconditionally; AppRun may add it only after the user-namespace probe fails.', + ]; + for (const {artifactPath, artifactViolations} of violations) { + lines.push(` - ${path.basename(artifactPath)}:`); + for (const violation of artifactViolations) { + lines.push(` ${violation}`); + } + } + throw new Error(lines.join('\n')); +} + +async function verifyLinuxArtifactContracts(buildResult) { + await verifyRpmArtifactsDoNotOwnBuildIds(buildResult); + await verifyLinuxPackagesContainAppArmorProfile(buildResult); + await verifyAppImageArtifactsDoNotNeedFuse2(buildResult); + await verifyAppImageArtifactsUseSandboxAwareLauncher(buildResult); +} + +module.exports = { + appId, + productName, + copyright: 'Copyright © 2026 Fluxer Platform AB', + // biome-ignore lint/suspicious/noTemplateCurlyInString: electron-builder placeholders, not JS template literals. + artifactName: '${productName}-${version}-${os}-${arch}.${ext}', + directories: { + buildResources: 'build_resources', + output: 'dist-electron', + }, + files: [ + 'dist/**/*', + 'package.json', + ...nativeRuntimeFilePatterns, + 'node_modules/hunspell-asm/**/*', + ...nativeBuildArtifactExcludes, + ...packagedRuntimeArtifactExcludes, + ...bundledDependencyExcludes, + ...windowsGameCapturePackageExcludes, + ...platformNativeRuntimeExcludes, + ...platformRuntimeDependencyExcludes, + ], + extraMetadata: { + main: 'dist/main/index.js', + name: metadataName, + ...(Boolean(process.env.VERSION) ? {version: process.env.VERSION} : {}), + ...(targetPlatform === 'linux' ? {desktopName: `${linuxPackageName}.desktop`} : {}), + }, + extraResources: [ + { + from: `build_resources/${iconDir}/`, + to: 'icons', + filter: [ + '16x16.png', + '24x24.png', + '32x32.png', + '48x48.png', + '64x64.png', + '128x128.png', + '256x256.png', + '512x512.png', + 'FluxerTrayTemplate.png', + 'FluxerTrayTemplate@2x.png', + 'icon.ico', + 'icon.png', + ], + }, + { + from: `build_resources/${iconDir}/badges/`, + to: 'badges', + filter: ['**/*'], + }, + ], + asar: { + smartUnpack: false, + }, + asarUnpack: [ + '**/*.node', + 'node_modules/@fluxer/win-process-loopback/*.node', + ...(windowsGameCaptureModuleEnabled + ? [ + 'node_modules/@fluxer/win-game-capture/*.node', + 'node_modules/@fluxer/win-game-capture/*.dll', + 'node_modules/@fluxer/win-game-capture/*.exe', + 'node_modules/@fluxer/win-game-capture/*.json', + ] + : []), + 'node_modules/@fluxer/win-clipboard/*.node', + 'node_modules/@fluxer/win-shell/*.node', + 'node_modules/@fluxer/win-toast/*.node', + 'node_modules/@fluxer/linux-audio-capture/*.node', + 'node_modules/@fluxer/linux-portals/*.node', + 'node_modules/@fluxer/linux-screen-capture/*.node', + 'node_modules/@fluxer/linux-screen-capture/obs-vkcapture/**/*', + 'node_modules/@fluxer/linux-notifications/*.node', + 'node_modules/@fluxer/linux-evdev/*.node', + 'node_modules/@fluxer/system-hunspell/*.node', + 'node_modules/@fluxer/macos-input-hook/*.node', + 'node_modules/@fluxer/mac-app-audio/*.node', + 'node_modules/@fluxer/mac-screen-capture/*.node', + 'node_modules/@fluxer/mac-clipboard/*.node', + 'node_modules/@fluxer/mac-sysctl/*.node', + 'node_modules/@fluxer/mac-tcc/*.node', + 'node_modules/@fluxer/windows-input-hook/*.node', + 'node_modules/@fluxer/linux-input-hook/*.node', + 'node_modules/@fluxer/platform-info/*.node', + 'node_modules/@fluxer/webauthn/*.node', + 'node_modules/@fluxer/webauthn/*.so*', + 'node_modules/.pnpm/@fluxer+win-process-loopback@*/node_modules/@fluxer/win-process-loopback/*.node', + ...(windowsGameCaptureModuleEnabled + ? [ + 'node_modules/.pnpm/@fluxer+win-game-capture@*/node_modules/@fluxer/win-game-capture/*.node', + 'node_modules/.pnpm/@fluxer+win-game-capture@*/node_modules/@fluxer/win-game-capture/*.dll', + 'node_modules/.pnpm/@fluxer+win-game-capture@*/node_modules/@fluxer/win-game-capture/*.exe', + 'node_modules/.pnpm/@fluxer+win-game-capture@*/node_modules/@fluxer/win-game-capture/*.json', + ] + : []), + 'node_modules/.pnpm/@fluxer+win-clipboard@*/node_modules/@fluxer/win-clipboard/*.node', + 'node_modules/.pnpm/@fluxer+win-shell@*/node_modules/@fluxer/win-shell/*.node', + 'node_modules/.pnpm/@fluxer+win-toast@*/node_modules/@fluxer/win-toast/*.node', + 'node_modules/.pnpm/@fluxer+windows-input-hook@*/node_modules/@fluxer/windows-input-hook/*.node', + 'node_modules/.pnpm/@fluxer+linux-audio-capture@*/node_modules/@fluxer/linux-audio-capture/*.node', + 'node_modules/.pnpm/@fluxer+linux-portals@*/node_modules/@fluxer/linux-portals/*.node', + 'node_modules/.pnpm/@fluxer+linux-screen-capture@*/node_modules/@fluxer/linux-screen-capture/*.node', + 'node_modules/.pnpm/@fluxer+linux-screen-capture@*/node_modules/@fluxer/linux-screen-capture/obs-vkcapture/**/*', + 'node_modules/.pnpm/@fluxer+linux-notifications@*/node_modules/@fluxer/linux-notifications/*.node', + 'node_modules/.pnpm/@fluxer+linux-evdev@*/node_modules/@fluxer/linux-evdev/*.node', + 'node_modules/.pnpm/@fluxer+linux-input-hook@*/node_modules/@fluxer/linux-input-hook/*.node', + 'node_modules/.pnpm/@fluxer+system-hunspell@*/node_modules/@fluxer/system-hunspell/*.node', + 'node_modules/.pnpm/@fluxer+macos-input-hook@*/node_modules/@fluxer/macos-input-hook/*.node', + 'node_modules/.pnpm/@fluxer+mac-app-audio@*/node_modules/@fluxer/mac-app-audio/*.node', + 'node_modules/.pnpm/@fluxer+mac-screen-capture@*/node_modules/@fluxer/mac-screen-capture/*.node', + 'node_modules/.pnpm/@fluxer+mac-clipboard@*/node_modules/@fluxer/mac-clipboard/*.node', + 'node_modules/.pnpm/@fluxer+mac-sysctl@*/node_modules/@fluxer/mac-sysctl/*.node', + 'node_modules/.pnpm/@fluxer+mac-tcc@*/node_modules/@fluxer/mac-tcc/*.node', + 'node_modules/.pnpm/@fluxer+platform-info@*/node_modules/@fluxer/platform-info/*.node', + 'node_modules/.pnpm/@fluxer+webauthn@*/node_modules/@fluxer/webauthn/*.node', + 'node_modules/.pnpm/@fluxer+webauthn@*/node_modules/@fluxer/webauthn/*.so*', + ], + compression: 'normal', + npmRebuild: false, + protocols: [ + { + name: appId, + role: 'Viewer', + schemes: ['fluxer'], + }, + ], + beforePack: verifyNativePackageInputs, + afterPack, + afterAllArtifactBuild: verifyLinuxArtifactContracts, + toolsets: { + appimage: '1.0.3', + }, + mac: { + category: 'public.app-category.social-networking', + minimumSystemVersion: macOSMinimumSystemVersion, + icon: `build_resources/${iconDir}/_compiled/AppIcon.icns`, + darkModeSupport: true, + hardenedRuntime: true, + gatekeeperAssess: false, + notarize: true, + provisioningProfile, + entitlements: isCanary + ? 'build_resources/entitlements.mac.canary.plist' + : 'build_resources/entitlements.mac.stable.plist', + entitlementsInherit: 'build_resources/entitlements.mac.inherit.plist', + target: [ + { + target: 'dmg', + arch: targetArchs, + }, + { + target: 'zip', + arch: targetArchs, + }, + ], + extendInfo: { + NSMicrophoneUsageDescription: 'Fluxer needs access to your microphone to enable voice chat features.', + NSCameraUsageDescription: 'Fluxer needs access to your camera to enable video chat features.', + NSAppleEventsUsageDescription: 'Fluxer needs access to Apple Events for automation features.', + NSAudioCaptureUsageDescription: 'Fluxer captures audio from the screen or window you choose to share.', + NSScreenCaptureUsageDescription: 'Fluxer captures the screen or window you choose to share.', + }, + }, + dmg: { + contents: [ + { + x: 130, + y: 220, + }, + { + x: 410, + y: 220, + type: 'link', + path: '/Applications', + }, + ], + }, + win: { + icon: `build_resources/${iconDir}/icon.ico`, + target: winTargets, + }, + portable: { + // biome-ignore lint/suspicious/noTemplateCurlyInString: electron-builder expands these placeholders. + artifactName: '${productName}-${version}-portable-${os}-${arch}.${ext}', + }, + linux: { + icon: `build_resources/${iconDir}/icon.png`, + category: 'Network;InstantMessaging;Chat;', + target: [ + { + target: 'AppImage', + arch: targetArchs, + }, + { + target: 'deb', + arch: targetArchs, + }, + { + target: 'rpm', + arch: targetArchs, + }, + { + target: 'tar.gz', + arch: targetArchs, + }, + ], + desktop: { + entry: linuxDesktopEntry, + }, + }, + deb: { + packageCategory: 'net', + desktop: { + entry: linuxDesktopEntryWithActions, + desktopActions: linuxDesktopActions, + }, + depends: [ + 'libgtk-3-0', + 'libnotify4', + 'libnss3', + 'libxss1', + 'libxtst6', + 'xdg-utils', + 'libatspi2.0-0', + 'libuuid1', + 'libsecret-1-0', + 'libpulse0', + 'libpipewire-0.3-0', + 'libstdc++6', + 'libgcc-s1', + ], + }, + rpm: { + desktop: { + entry: linuxDesktopEntryWithActions, + desktopActions: linuxDesktopActions, + }, + fpm: rpmBuildIdLinkFpmArgs, + depends: [ + 'gtk3', + 'libnotify', + 'nss', + 'libXScrnSaver', + 'libXtst', + 'xdg-utils', + 'at-spi2-core', + 'libuuid', + 'libsecret', + 'pulseaudio-libs', + 'pipewire-libs', + 'libstdc++', + 'libgcc', + ], + }, + publish: null, +}; diff --git a/fluxer_desktop/native-media-reports/2026-06-05T15-40-10-773Z/livekit-harness.json b/fluxer_desktop/native-media-reports/2026-06-05T15-40-10-773Z/livekit-harness.json new file mode 100644 index 0000000..8875f4c --- /dev/null +++ b/fluxer_desktop/native-media-reports/2026-06-05T15-40-10-773Z/livekit-harness.json @@ -0,0 +1,85 @@ +{ + "status": "skip", + "startedAt": "2026-06-05T15:40:30.428Z", + "endedAt": "2026-06-05T15:40:30.428Z", + "platform": "linux", + "arch": "arm64", + "config": { + "url": "ws://localhost:7880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-38884-16b7fb75-22a2-4914-8e2d-255451c5b7e9", + "required": false, + "strict": false, + "timeoutMs": 30000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 0, + "width": 320, + "height": 180, + "expectedWidth": 320, + "expectedHeight": 180, + "fps": 15, + "minFpsRatio": 0, + "maxFrameGapMs": 0, + "maxAvDriftMs": 0, + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": false, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 400000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/workspaces/fluxer/fluxer_desktop/native-media-reports/2026-06-05T15-40-10-773Z/livekit-harness.json", + "verbose": false, + "scenarioName": "codec-vp8", + "apiSecret": "", + "e2eeKey": null + }, + "checks": [], + "skippedReason": "LiveKit is unreachable at ws://localhost:7880 (connect ECONNREFUSED 127.0.0.1:7880)", + "error": null, + "hardwareEncoder": { + "available": false, + "backend": "none", + "compiled": false, + "runtime": false, + "codecs": [], + "zeroCopy": false, + "nativeInputs": [], + "reason": "nvenc_not_compiled", + "detail": "CUDA headers were not found at build time; rebuild with CUDA_HOME pointing to a CUDA toolkit that contains include/cuda.h" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 0, + "videoCallbacks": 0, + "videoBytes": 0, + "receivedFps": 0, + "requiredFps": null, + "maxVideoFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "avDriftMs": null, + "droppedVideoFrameCallbacks": 0, + "lastVideoMeta": null, + "publisherStats": null, + "secondaryPublisherStats": null, + "subscriberStats": null + } +} diff --git a/fluxer_desktop/native-media-reports/2026-06-05T15-40-10-773Z/summary.json b/fluxer_desktop/native-media-reports/2026-06-05T15-40-10-773Z/summary.json new file mode 100644 index 0000000..9e0b807 --- /dev/null +++ b/fluxer_desktop/native-media-reports/2026-06-05T15-40-10-773Z/summary.json @@ -0,0 +1,208 @@ +{ + "status": "pass", + "mode": "smoke", + "strict": false, + "platform": "linux", + "arch": "arm64", + "hostname": "850d0711b42a", + "startedAt": "2026-06-05T15:40:10.774Z", + "endedAt": "2026-06-05T15:40:45.392Z", + "reportDir": "/workspaces/fluxer/fluxer_desktop/native-media-reports/2026-06-05T15-40-10-773Z", + "gates": [ + { + "name": "mode-valid", + "status": "pass", + "mode": "smoke" + }, + { + "name": "documentation-current", + "status": "pass", + "missing": [] + }, + { + "name": "strict-prerequisites", + "status": "skip", + "reason": "smoke mode" + }, + { + "name": "strict-command-plan", + "status": "skip", + "reason": "smoke mode" + } + ], + "plannedCommands": [ + { + "name": "main-process-native-media-unit-tests", + "command": "node --test src/main/WebrtcScreenPublish.test.mjs src/main/NativeVoiceEngine.test.mjs src/main/NativeScreenCapture.test.mjs", + "category": "shared", + "cwd": "." + }, + { + "name": "desktop-typecheck", + "command": "pnpm typecheck", + "category": "shared", + "cwd": "." + }, + { + "name": "webrtc-sender-build", + "command": "pnpm --dir native/webrtc-sender build", + "category": "native-sender", + "cwd": "." + }, + { + "name": "webrtc-sender-tests", + "command": "pnpm --dir native/webrtc-sender test", + "category": "native-sender", + "cwd": "." + }, + { + "name": "webrtc-sender-livekit-harness", + "command": "pnpm --dir native/webrtc-sender test:livekit", + "category": "livekit", + "cwd": "." + }, + { + "name": "linux-screen-capture-build", + "command": "pnpm --dir native/linux-screen-capture build", + "category": "platform-native", + "cwd": "." + }, + { + "name": "linux-screen-capture-tests", + "command": "pnpm --dir native/linux-screen-capture test", + "category": "platform-native", + "cwd": "." + }, + { + "name": "linux-audio-capture-build", + "command": "pnpm --dir native/linux-audio-capture build", + "category": "platform-native", + "cwd": "." + }, + { + "name": "linux-audio-capture-tests", + "command": "pnpm --dir native/linux-audio-capture test", + "category": "platform-native", + "cwd": "." + } + ], + "commands": [ + { + "name": "main-process-native-media-unit-tests", + "command": "node --test src/main/WebrtcScreenPublish.test.mjs src/main/NativeVoiceEngine.test.mjs src/main/NativeScreenCapture.test.mjs", + "category": "shared", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-05T15:40:10.775Z", + "endedAt": "2026-06-05T15:40:10.863Z", + "durationMs": 88, + "stdoutTail": "▶ NativeScreenCapture DMA-BUF metadata validation\n ✔ accepts valid DMA-BUF metadata and pads native plane arrays (1.747886ms)\n ✔ keeps valid handles when optional device UUID metadata is absent or malformed (0.452503ms)\n ✔ rejects invalid plane counts, FDs, strides, and format metadata (0.616004ms)\n✔ NativeScreenCapture DMA-BUF metadata validation (3.258061ms)\n▶ NativeVoiceEngine screen frame routing\n ✔ prefers Windows shared texture handles over DMA-BUF and CPU frames (2.165721ms)\n ✔ uses Linux DMA-BUF handles before CPU conversion (0.671504ms)\n ✔ falls back to CPU frames when GPU paths are unavailable or rejected (0.583295ms)\n✔ NativeVoiceEngine screen frame routing (3.909315ms)\n▶ WebrtcScreenPublish native frame routing\n ✔ prefers Windows shared texture handles over DMA-BUF and CPU buffers (1.627218ms)\n ✔ uses Linux DMA-BUF handles before CPU conversion (0.594711ms)\n ✔ falls back to CPU frames when GPU paths are unavailable or rejected (0.41371ms)\n✔ WebrtcScreenPublish native frame routing (3.18327ms)\nℹ tests 9\nℹ suites 3\nℹ pass 9\nℹ fail 0\nℹ cancelled 0\nℹ skipped 0\nℹ todo 0\nℹ duration_ms 69.874336\n", + "stderrTail": "" + }, + { + "name": "desktop-typecheck", + "command": "pnpm typecheck", + "category": "shared", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-05T15:40:10.863Z", + "endedAt": "2026-06-05T15:40:13.457Z", + "durationMs": 2594, + "stdoutTail": "native/mac-app-audio |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\nnative/mac-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\nnative/mac-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\nnative/mac-sysctl |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\nnative/mac-tcc |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\nnative/macos-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\nnative/win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\nnative/win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\nnative/win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\nnative/win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\nnative/win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\nnative/windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n\n> fluxer_desktop@0.0.0 typecheck /workspaces/fluxer/fluxer_desktop\n> tsgo --noEmit\n\n", + "stderrTail": "" + }, + { + "name": "webrtc-sender-build", + "command": "pnpm --dir native/webrtc-sender build", + "category": "native-sender", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-05T15:40:13.457Z", + "endedAt": "2026-06-05T15:40:25.076Z", + "durationMs": 11619, + "stdoutTail": "../mac-app-audio |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-sysctl |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-tcc |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../macos-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n\n> @fluxer/webrtc-sender@0.0.0 build /workspaces/fluxer/fluxer_desktop/native/webrtc-sender\n> node scripts/build-rust.mjs\n\n[buildRustNodeAddon] cwd=/workspaces/fluxer/fluxer_desktop/native/webrtc-sender\ncargo build --release --target aarch64-unknown-linux-gnu --manifest-path Cargo.toml --features camera-native\n", + "stderrTail": "warning: webrtc-sys@0.3.33: cuda.h not found; building without hardware accelerated video codec support for NVidia GPUs\nwarning: unused import: `enum_dispatch`\n --> vendor/libwebrtc/src/audio_source.rs:15:13\n |\n15 | use crate::{enum_dispatch, imp::audio_source as imp_as};\n | ^^^^^^^^^^^^^\n |\n = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default\n\nwarning: unnecessary `unsafe` block\n --> vendor/libwebrtc/src/native/apm.rs:32:25\n |\n32 | sys_handle: unsafe {\n | ^^^^^^ unnecessary `unsafe` block\n |\n = note: `#[warn(unused_unsafe)]` (part of `#[warn(unused)]`) on by default\n\nwarning: variable does not need to be mutable\n --> vendor/libwebrtc/src/native/desktop_capturer.rs:66:13\n |\n66 | let mut sys_handle = sys_dc::ffi::DesktopCapturerOptions {\n | ----^^^^^^^^^^\n | |\n | help: remove this `mut`\n |\n = note: `#[warn(unused_mut)]` (part of `#[warn(unused)]`) on by default\n\nwarning: hiding a lifetime that's elided elsewhere is confusing\n --> vendor/libwebrtc/src/native/audio_mixer.rs:30:34\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option;\n | ^^^^^ the lifetime is elided here ^^^^^^^^^^ the same lifetime is hidden here\n |\n = help: the same lifetime is referred to in inconsistent ways, making the signature confusing\n = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default\nhelp: use `'_` for type paths\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option>;\n | ++++\n\nwarning: `libwebrtc` (lib) generated 4 warnings (run `cargo fix --lib -p libwebrtc` to apply 3 suggestions)\n Compiling fluxer_webrtc_sender v0.0.0 (/workspaces/fluxer/fluxer_desktop/native/webrtc-sender)\n Finished `release` profile [optimized] target(s) in 9.11s\n" + }, + { + "name": "webrtc-sender-tests", + "command": "pnpm --dir native/webrtc-sender test", + "category": "native-sender", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-05T15:40:25.076Z", + "endedAt": "2026-06-05T15:40:27.641Z", + "durationMs": 2565, + "stdoutTail": "../mac-app-audio |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-sysctl |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-tcc |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../macos-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n\n> @fluxer/webrtc-sender@0.0.0 test /workspaces/fluxer/fluxer_desktop/native/webrtc-sender\n> cargo test --features publisher,camera-native && node --test index.test.mjs\n\n\nrunning 89 tests\ntest audio::tests::clamp_volume_holds_the_contract_range ... ok\ntest audio::tests::clamp_volume_maps_non_finite_to_unity ... ok\ntest audio::tests::input_devices_json_array ... ok\ntest audio::tests::output_devices_json_array ... ok\ntest audio::tests::processing_options_preserve_defaults_unless_overridden ... ok\ntest audio::tests::output_device_json_locks_the_contract_shape ... ok\ntest audio::tests::shape_input_marks_index_zero_default_and_drops_empty_ids ... ok\ntest audio::tests::is_muted_only_at_zero ... ok\ntest audio::tests::shape_marks_index_zero_default_and_drops_empty_ids ... ok\ntest camera::tests::from_opts_applies_defaults ... ok\ntest camera::tests::from_opts_forces_even_dims_and_honours_overrides ... ok\ntest camera::tests::from_opts_parses_device_index ... ok\ntest camera::tests::from_opts_preserves_string_device_ids ... ok\ntest camera::tests::from_opts_rejects_degenerate_dims_and_fps ... ok\ntest config::tests::canonical_codec_name_accepts_all_five_case_insensitively ... ok\ntest config::tests::canonical_codec_name_rejects_empty_and_unknown ... ok\ntest config::tests::rejects_bad_url ... ok\ntest config::tests::rejects_empty_token ... ok\ntest config::tests::rejects_odd_or_oob_dimensions ... ok\ntest config::tests::state_gates_frames_and_connect ... ok\ntest engine::tests::parse_codec_agrees_with_pure_canonicaliser ... ok\ntest config::tests::valid_config_passes ... ok\ntest engine::tests::parse_codec_maps_all_five_including_h265 ... ok\ntest engine::tests::parse_codec_rejects_empty_and_unknown ... ok\ntest engine::tests::parse_track_source_accepts_renderer_and_livekit_spellings ... ok\ntest engine::tests::parse_video_quality_accepts_subscription_quality_levels ... ok\ntest engine::tests::pcm16_audio_frame_rejects_malformed_buffers ... ok\ntest engine::tests::pcm16_audio_frame_shapes_interleaved_stereo_samples ... ok\ntest engine::tests::tight_i420_len_matches_plane_geometry ... ok\ntest engine::tests::video_frame_meta_json_matches_contract ... ok\ntest events::tests::empty_object_and_array ... ok\ntest events::tests::escapes_quote_backslash_and_control_chars ... ok\ntest events::tests::json_object_preserves_order_and_mixes_str_and_raw ... ok\ntest events::tests::json_u8_array_serializes_bytes_as_numbers ... ok\ntest events::tests::leaves_plain_ascii_and_unicode_untouched ... ok\ntest events::tests::raw_array_preserves_prebuilt_json_objects ... ok\ntest hardware_encoder::tests::reports_available_nvenc_when_compiled_and_runtime_ready ... ok\ntest hardware_encoder::tests::reports_driver_sdk_incompatibility_before_encode_session ... ok\ntest hardware_encoder::tests::reports_missing_runtime_prerequisites_when_compiled ... ok\ntest hardware_encoder::tests::reports_missing_runtime_prerequisites_when_probe_is_absent ... ok\ntest hardware_encoder::tests::reports_not_compiled_before_runtime_missing ... ok\ntest hardware_encoder::tests::reports_no_supported_codecs_when_guid_query_finds_no_fluxer_codecs ... ok\ntest hardware_encoder::tests::reports_h264_only_when_hevc_guid_is_absent ... ok\ntest hardware_encoder::tests::reports_no_cuda_devices_using_obs_style_reason ... ok\ntest hardware_encoder::tests::reports_unsupported_platforms_clearly ... ok\ntest publisher::tests::i420_plane_copy_fills_libwebrtc_buffer ... ok\ntest stats::tests::bitrate_computes_kbps_from_byte_delta ... ok\ntest stats::tests::bitrate_first_sample_is_zero ... ok\ntest publisher::tests::parse_codec_maps_known_names ... ok\ntest stats::tests::bitrate_half_second_doubles_rate ... ok\ntest stats::tests::bitrate_rejects_backwards_time_and_bytes ... ok\ntest stats::tests::empty_stats_serialise_to_null_rtt_and_empty_arrays ... ok\ntest stats::tests::full_stats_serialise_to_exact_contract_shape ... ok\ntest stats::tests::sanitize_kbps_drops_nan_inf_negative_and_rounds ... ok\ntest stats::tests::sanitize_audio_level_clamps ... ok\ntest stats::tests::video_inbound_omits_audio_level_audio_omits_fps ... ok\ntest stats::tests::unit_conversions_seconds_to_ms ... ok\ntest texture_source::tests::dmabuf_desc_from_parts_rejects_negative_fds ... ok\ntest texture_source::tests::dxgi_format_gate_accepts_8bit_rgba_bgra_only ... ok\ntest texture_source::tests::drm_format_gate_accepts_obs_vkcapture_texture_formats ... ok\ntest texture_source::tests::error_strings_are_stable ... ok\ntest texture_source::tests::probe_is_available_only_for_explicit_hardware_codecs ... ok\ntest texture_source::tests::should_attempt_dmabuf_is_sdk_gated_after_validation_capability ... ok\ntest texture_source::tests::should_attempt_falls_back_when_capability_unavailable ... ok\ntest texture_source::tests::should_attempt_validates_frame_when_capability_available ... ok\ntest texture_source::tests::validate_accepts_a_clean_bgra_texture ... ok\ntest texture_source::tests::validate_dmabuf_accepts_supported_formats_with_optional_uuid ... ok\ntest texture_source::tests::validate_dmabuf_rejects_invalid_planes ... ok\ntest texture_source::tests::validate_precedence_handle_before_dims_before_format ... ok\ntest texture_source::tests::validate_rejects_odd_zero_and_oversized_dims ... ok\ntest texture_source::tests::validate_rejects_unsupported_format ... ok\ntest texture_source::tests::validate_rejects_zero_handle_as_no_texture ... ok\ntest yuv::tests::bgra_black_white_grey_levels ... ok\ntest yuv::tests::bgra_golden_2x2_solid_colour_exact_bytes ... ok\ntest yuv::tests::bgra_golden_strided_2x2_skips_row_padding ... ok\ntest yuv::tests::bgra_primaries_have_expected_chroma_signs ... ok\ntest yuv::tests::bgra_plane_sizes ... ok\ntest yuv::tests::copy_nv12_planes_preserves_nv12_layout ... ok\ntest yuv::tests::copy_nv12_planes_rejects_short_buffers ... ok\ntest yuv::tests::copy_nv12_planes_respects_destination_stride ... ok\ntest yuv::tests::nv12_4x4_deinterleaves_two_chroma_columns ... ok\ntest yuv::tests::nv12_golden_4x2_packed_to_i420 ... ok\ntest yuv::tests::nv12_packed_2x2_deinterleaves ... ok\ntest yuv::tests::nv12_respects_row_padding ... ok\ntest yuv::tests::nv12_short_buffer_is_rejected ... ok\ntest yuv::tests::rejects_odd_or_tiny_dimensions ... ok\ntest yuv::tests::rgb_golden_2x2_solid_colour_matches_bgra_path ... ok\ntest yuv::tests::rgb_plane_sizes_and_levels ... ok\ntest yuv::tests::rgb_rejects_odd_dims_and_short_buffer ... ok\n\ntest result: ok. 89 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n\nrunning 6 tests\ntest outbound_covers_the_single_identity_publish_path ... ok\ntest e2ee_state_is_ok_on_the_wire ... ok\ntest outbound_fixtures_match_contract ... ok\ntest inbound_covers_subscribe_and_lifecycle ... ok\ntest video_frame_meta_matches_contract ... ok\ntest inbound_fixtures_match_contract ... ok\n\ntest result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n▶ webrtc-sender loader wrapper\n ✔ resolves the native addon filename for every desktop OS/arch target (0.414128ms)\n ✔ rejects unsupported platform/architecture pairs explicitly (0.194543ms)\n ✔ returns a native hardware encoder capability when the binding exports one (0.19071ms)\n ✔ delegates dropped video callback metrics through the VoiceEngine wrapper (0.08925ms)\n ✔ delegates shared texture handles through the WebrtcPublisher wrapper (0.244294ms)\n ✔ treats shared texture publishing as unavailable for older publisher bindings (0.05975ms)\n ✔ delegates dma-buf frames through the WebrtcPublisher wrapper (0.130917ms)\n ✔ delegates shared texture handles through the VoiceEngine wrapper (0.093ms)\n ✔ treats shared texture publishing as unavailable for older voice bindings (0.074ms)\n ✔ treats dma-buf publishing as unavailable for older native bindings (0.109542ms)\n ✔ defaults dropped video callback metrics to zero for older native bindings (0.072001ms)\n ✔ delegates remote track subscription updates through the VoiceEngine wrapper (0.090292ms)\n ✔ ignores remote track subscription updates for older native bindings (0.054792ms)\n ✔ delegates data packets through the VoiceEngine wrapper (0.129459ms)\n ✔ rejects invalid data packet payloads in the wrapper (0.139584ms)\n ✔ returns an unavailable hardware encoder capability without a binding (0.063959ms)\n✔ webrtc-sender loader wrapper (3.186227ms)\nℹ tests 16\nℹ suites 1\nℹ pass 16\nℹ fail 0\nℹ cancelled 0\nℹ skipped 0\nℹ todo 0\nℹ duration_ms 39.277319\n", + "stderrTail": "warning: webrtc-sys@0.3.33: cuda.h not found; building without hardware accelerated video codec support for NVidia GPUs\nwarning: unused import: `enum_dispatch`\n --> vendor/libwebrtc/src/audio_source.rs:15:13\n |\n15 | use crate::{enum_dispatch, imp::audio_source as imp_as};\n | ^^^^^^^^^^^^^\n |\n = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default\n\nwarning: unnecessary `unsafe` block\n --> vendor/libwebrtc/src/native/apm.rs:32:25\n |\n32 | sys_handle: unsafe {\n | ^^^^^^ unnecessary `unsafe` block\n |\n = note: `#[warn(unused_unsafe)]` (part of `#[warn(unused)]`) on by default\n\nwarning: variable does not need to be mutable\n --> vendor/libwebrtc/src/native/desktop_capturer.rs:66:13\n |\n66 | let mut sys_handle = sys_dc::ffi::DesktopCapturerOptions {\n | ----^^^^^^^^^^\n | |\n | help: remove this `mut`\n |\n = note: `#[warn(unused_mut)]` (part of `#[warn(unused)]`) on by default\n\nwarning: hiding a lifetime that's elided elsewhere is confusing\n --> vendor/libwebrtc/src/native/audio_mixer.rs:30:34\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option;\n | ^^^^^ the lifetime is elided here ^^^^^^^^^^ the same lifetime is hidden here\n |\n = help: the same lifetime is referred to in inconsistent ways, making the signature confusing\n = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default\nhelp: use `'_` for type paths\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option>;\n | ++++\n\nwarning: `libwebrtc` (lib) generated 4 warnings (run `cargo fix --lib -p libwebrtc` to apply 3 suggestions)\n Finished `test` profile [unoptimized + debuginfo] target(s) in 0.15s\n Running unittests src/lib.rs (target/debug/deps/fluxer_webrtc_sender-96ed47cbef11f6a0)\n Running tests/event_contract.rs (target/debug/deps/event_contract-c09db034b80d5f00)\n Doc-tests fluxer_webrtc_sender\n" + }, + { + "name": "webrtc-sender-livekit-harness", + "command": "pnpm --dir native/webrtc-sender test:livekit", + "category": "livekit", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-05T15:40:27.641Z", + "endedAt": "2026-06-05T15:40:30.440Z", + "durationMs": 2799, + "stdoutTail": "../mac-app-audio |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-sysctl |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-tcc |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../macos-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n\n> @fluxer/webrtc-sender@0.0.0 test:livekit /workspaces/fluxer/fluxer_desktop/native/webrtc-sender\n> node scripts/livekit-harness.mjs\n\n[livekit-harness] SKIP: LiveKit is unreachable at ws://localhost:7880 (connect ECONNREFUSED 127.0.0.1:7880). Set FLUXER_WEBRTC_SENDER_LIVEKIT_REQUIRED=1 to fail instead.\n", + "stderrTail": "" + }, + { + "name": "linux-screen-capture-build", + "command": "pnpm --dir native/linux-screen-capture build", + "category": "platform-native", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-05T15:40:30.440Z", + "endedAt": "2026-06-05T15:40:32.883Z", + "durationMs": 2443, + "stdoutTail": "../mac-app-audio |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-sysctl |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-tcc |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../macos-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n\n> @fluxer/linux-screen-capture@0.0.0 build /workspaces/fluxer/fluxer_desktop/native/linux-screen-capture\n> node scripts/build-rust.mjs\n\n[buildRustNodeAddon] cwd=/workspaces/fluxer/fluxer_desktop/native/linux-screen-capture\ncargo build --release --target aarch64-unknown-linux-gnu --manifest-path Cargo.toml\n", + "stderrTail": " Finished `release` profile [optimized] target(s) in 0.13s\n" + }, + { + "name": "linux-screen-capture-tests", + "command": "pnpm --dir native/linux-screen-capture test", + "category": "platform-native", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-05T15:40:32.883Z", + "endedAt": "2026-06-05T15:40:37.620Z", + "durationMs": 4737, + "stdoutTail": "../mac-app-audio |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-sysctl |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-tcc |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../macos-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n\n> @fluxer/linux-screen-capture@0.0.0 test /workspaces/fluxer/fluxer_desktop/native/linux-screen-capture\n> cargo test --manifest-path Cargo.toml && node --test index.test.mjs\n\n\nrunning 42 tests\ntest game_capture::tests::auto_import_policy_walks_obs_fallback_ladder ... ok\ntest game_capture::tests::converts_abgr10_row_to_bgra ... ok\ntest game_capture::tests::diagnostics_can_pin_host_mapped_capture_policy ... ok\ntest game_capture::tests::converts_fp16_rgba_row_to_bgra_srgb ... ok\ntest game_capture::tests::diagnostics_modifier_uses_obs_invalid_name ... ok\ntest game_capture::tests::control_message_encodes_obs_import_modes ... ok\ntest game_capture::tests::host_mapped_import_rejects_invalid_stride_and_offset_with_diagnostics ... ok\ntest game_capture::tests::host_mapped_import_rejects_multiplane_descriptors_with_diagnostic ... ok\ntest game_capture::tests::diagnostics_default_to_obs_fallback_capture_policy ... ok\ntest game_capture::tests::identifies_rgba_memory_formats_that_need_swizzle ... ok\ntest game_capture::tests::host_mapped_import_rejects_unsupported_formats_with_supported_list ... ok\ntest game_capture::tests::lifecycle_messages_update_diagnostics_snapshot ... ok\ntest game_capture::tests::host_mapped_import_rejects_invalid_fd_with_diagnostic ... ok\ntest game_capture::tests::host_mapped_import_reads_cpu_nv12_and_preserves_source_dmabuf_metadata ... ok\ntest game_capture::tests::import_mode_env_parses_nvidia_debug_shortcuts ... ok\ntest game_capture::tests::non_host_mapped_import_reports_fallback_requirement ... ok\ntest game_capture::tests::mapped_texture_exports_dmabuf_metadata_for_native_encoder ... ok\ntest napi_surface_linux::tests::parse_source_id_roundtrip ... ok\ntest napi_surface_linux::tests::stream_dimensions_for_node_uses_selected_portal_stream ... ok\ntest nv12_packing::tests::bgra_to_nv12_rejects_short_stride ... ok\ntest game_capture::tests::parses_obs_vkcapture_texture_layout ... ok\ntest napi_surface_linux::tests::parse_source_id_rejects_malformed ... ok\ntest game_capture::tests::parses_client_name_as_c_string ... ok\ntest napi_surface_linux::tests::source_kind_for_type_maps_bits ... ok\ntest nv12_packing::tests::bgra_to_nv12_solid_black ... ok\ntest nv12_packing::tests::pack_nv12_rejects_short_source ... ok\ntest nv12_packing::tests::pack_nv12_rejects_undersized_stride ... ok\ntest nv12_packing::tests::pack_nv12_rejects_wrong_dst_size ... ok\ntest nv12_packing::tests::pack_nv12_strips_row_padding ... ok\ntest nv12_packing::tests::pack_nv12_zero_padding_is_identity ... ok\ntest nv12_packing::tests::packed_size_matches_yuv420_layout ... ok\ntest nv12_packing::tests::packed_size_rejects_odd_height ... ok\ntest nv12_packing::tests::packed_size_rejects_zero_dims ... ok\ntest portal::tests::cursor_mode_constants_match_portal_spec ... ok\ntest portal::tests::cursor_mode_matches_treats_missing_key_as_honoured ... ok\ntest portal::tests::mint_token_is_distinct_and_prefixed ... ok\ntest portal::tests::normalize_desktop_entry_app_id_accepts_fluxer_ids ... ok\ntest portal::tests::normalize_desktop_entry_app_id_rejects_paths_and_empty_values ... ok\ntest portal::tests::request_path_sanitizes_unique_bus_name ... ok\ntest portal::tests::session_path_sanitizes_unique_bus_name ... ok\ntest portal::tests::source_type_mask_combines_monitor_and_window ... ok\ntest nv12_packing::tests::bgra_to_nv12_solid_white_is_in_range ... ok\n\ntest result: ok. 42 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n▶ linux-screen-capture game capture launch environment\n ✔ enables OBS Vulkan capture and names the client (0.838797ms)\n ✔ prefers bundled hook assets and can force PRIME/NVIDIA launch variables (0.434836ms)\n ✔ does not duplicate launch path entries or Vulkan layer names (0.278376ms)\n ✔ can force a specific NVIDIA Vulkan ICD for hybrid GPU systems (0.627129ms)\n ✔ keeps obs-vkcapture runtime assets and license notes in the package surface (0.78638ms)\n✔ linux-screen-capture game capture launch environment (3.608563ms)\nℹ tests 5\nℹ suites 1\nℹ pass 5\nℹ fail 0\nℹ cancelled 0\nℹ skipped 0\nℹ todo 0\nℹ duration_ms 58.707644\n", + "stderrTail": " Compiling fluxer_linux_screen_capture v0.0.0 (/workspaces/fluxer/fluxer_desktop/native/linux-screen-capture)\n Finished `test` profile [unoptimized + debuginfo] target(s) in 2.44s\n Running unittests src/lib.rs (target/debug/deps/fluxer_linux_screen_capture-0784dd97ce1aa9bb)\n Doc-tests fluxer_linux_screen_capture\n" + }, + { + "name": "linux-audio-capture-build", + "command": "pnpm --dir native/linux-audio-capture build", + "category": "platform-native", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-05T15:40:37.620Z", + "endedAt": "2026-06-05T15:40:41.518Z", + "durationMs": 3898, + "stdoutTail": "../mac-app-audio |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-sysctl |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-tcc |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../macos-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n\n> @fluxer/linux-audio-capture@0.0.0 build /workspaces/fluxer/fluxer_desktop/native/linux-audio-capture\n> node scripts/build-rust.mjs\n\n[buildRustNodeAddon] cwd=/workspaces/fluxer/fluxer_desktop/native/linux-audio-capture\ncargo build --release --target aarch64-unknown-linux-gnu --manifest-path Cargo.toml\n", + "stderrTail": " Compiling fluxer_linux_audio_capture v0.0.0 (/workspaces/fluxer/fluxer_desktop/native/linux-audio-capture)\n Finished `release` profile [optimized] target(s) in 1.29s\n" + }, + { + "name": "linux-audio-capture-tests", + "command": "pnpm --dir native/linux-audio-capture test", + "category": "platform-native", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-05T15:40:41.518Z", + "endedAt": "2026-06-05T15:40:45.392Z", + "durationMs": 3874, + "stdoutTail": "../mac-app-audio |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-sysctl |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../mac-tcc |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../macos-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"darwin\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n../windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"linux\",\"cpu\":\"arm64\",\"libc\":\"glibc\"})\n\n> @fluxer/linux-audio-capture@0.0.0 test /workspaces/fluxer/fluxer_desktop/native/linux-audio-capture\n> cargo test --manifest-path Cargo.toml\n\n\nrunning 69 tests\ntest audio_contract::tests::sample_duration_conversion_uses_whole_audio_frames ... ok\ntest audio_contract::tests::whole_frame_sample_count_trims_incomplete_channel_frames ... ok\ntest audio_contract::tests::routing_parser_caps_are_intentionally_finite ... ok\ntest audio_contract::tests::direct_append_slice_keeps_only_complete_stereo_samples_within_queue_cap ... ok\ntest audio_contract::tests::direct_read_count_is_bounded_and_stereo_aligned ... ok\ntest audio_contract::tests::direct_read_count_uses_stable_twenty_ms_chunks ... ok\ntest audio_contract::tests::direct_capture_latency_matches_stable_screen_share_buffer ... ok\ntest direct_buffer::tests::format_changes_clear_queued_samples ... ok\ntest env::tests::hint_str_matches_audio_backend_js_contract ... ok\ntest direct_buffer::tests::read_emits_stable_twenty_ms_chunks_with_continuous_timestamps ... ok\ntest env::tests::none_when_no_audio_hint_present ... ok\ntest env::tests::pipewire_wins_when_both_present ... ok\ntest env::tests::prefers_pipewire_when_pipewire_runtime_dir_set ... ok\ntest env::tests::pulse_server_falls_through_to_pulseaudio_when_no_pipewire_hint ... ok\ntest env::tests::xdg_runtime_dir_alone_is_enough_for_pipewire_hint ... ok\ntest pipewire_bridge::tests::direct_chunk_payload_range_respects_pipewire_chunk_offset ... ok\ntest pipewire_bridge::tests::direct_stream_props_capture_private_sink_monitor ... ok\ntest pipewire_bridge::tests::direct_audio_info_advertises_stereo_fl_fr ... ok\ntest pipewire_bridge::tests::is_routable_media_class_matches_audio_node_classes ... ok\ntest pipewire_bridge::tests::link_props_carry_per_port_routing ... ok\ntest pipewire_bridge::tests::private_sink_props_advertise_hidden_audio_sink ... ok\ntest pipewire_bridge::tests::virtual_sink_props_match_legacy_contract ... ok\ntest pipewire_bridge::tests::next_direct_sink_name_is_unique_per_call ... ok\ntest pulse_bridge::tests::pulse_direct_read_size_matches_capture_latency ... ok\ntest pipewire_bridge::tests::default_sink_target_id_uses_object_serial_for_matching_node_name ... ok\ntest pipewire_bridge::tests::inventory_does_not_inherit_client_object_serial_as_node_target ... ok\ntest pipewire_bridge::tests::inventory_falls_back_to_pipewire_security_pid ... ok\ntest pipewire_bridge::tests::inventory_enriches_nodes_with_owning_client_identity ... ok\ntest pipewire_bridge::tests::inventory_keeps_node_properties_authoritative_over_client_props ... ok\ntest pipewire_bridge::tests::matching_pinned_capture_nodes_only_matches_record_stream_inputs ... ok\ntest pipewire_bridge::tests::pick_node_ports_applies_same_fallback_to_private_capture_inputs ... ok\ntest pipewire_bridge::tests::pick_source_output_ports_falls_back_to_first_two_jack_style_ports ... ok\ntest pipewire_bridge::tests::pick_source_output_ports_fans_mono_to_both_inputs ... ok\ntest direct_buffer::tests::overflow_drops_from_front_and_advances_timestamp ... ok\ntest pipewire_bridge::tests::pick_source_output_ports_prefers_stereo_pair ... ok\ntest pipewire_bridge::tests::pick_source_output_ports_returns_none_when_only_one_side_present ... ok\ntest pipewire_bridge::tests::pick_source_output_ports_treats_blank_channel_as_mono ... ok\ntest pulse_bridge::tests::build_input_props_synthesises_media_class_and_target_object ... ok\ntest pulse_bridge::tests::extract_target_pid_prefers_include_then_pin ... ok\ntest pipewire_bridge::tests::pick_source_output_ports_returns_none_when_no_outputs ... ok\ntest routing::tests::empty_rules_route_nothing_and_sink_id_is_excluded ... ok\ntest pulse_bridge::tests::daemon_unreachable_returns_none_from_open ... ok\ntest routing::tests::include_mode_honors_hardware_filtering_and_never_rules ... ok\ntest routing::tests::include_mode_rejects_non_playback_nodes_that_match_the_include_filter ... ok\ntest routing::tests::matches_any_requires_at_least_one_matching_pattern ... ok\ntest routing::tests::missing_keys_and_mismatched_values_do_not_match ... ok\ntest routing::tests::parse_default_sink_name_is_strict_and_tolerant ... ok\ntest routing::tests::routing_refuses_non_playback_media_classes_even_when_included ... ok\ntest routing::tests::self_identity_matches_across_documented_pipewire_keys ... ok\ntest routing::tests::structural_self_identity_wins_over_include_rules ... ok\ntest routing::tests::synthetic_display_pattern_keys_do_not_affect_routing ... ok\ntest routing::tests::system_mode_routes_only_default_playback_streams ... ok\ntest self_identity::tests::parse_ppid_handles_comm_with_spaces_and_parens ... ok\ntest routing::tests::synthetic_target_object_pattern_matches_name_or_serial ... ok\ntest self_identity::tests::parse_ppid_handles_plain_stat ... ok\ntest pipewire_bridge::tests::daemon_unreachable_returns_none_from_open ... ok\ntest pipewire_bridge::tests::integration_smoke_apply_release_cycle_is_safe ... ok\ntest pulse_bridge::tests::direct_capture_open_then_drop_is_safe_without_daemon ... ok\ntest routing::tests::system_mode_accepts_default_sink_object_id_targets ... ok\ntest self_identity::tests::strip_exe_suffix_handles_known_extensions ... ok\ntest routing::tests::system_mode_accepts_untargeted_and_node_target_streams ... ok\ntest pipewire_bridge::tests::direct_open_returns_none_or_cleans_up ... ok\ntest self_identity::tests::parse_ppid_returns_none_on_malformed_input ... ok\ntest self_identity::tests::populate_self_identity_records_own_pid ... ok\ntest pulse_bridge::tests::direct_capture_start_then_drop_without_daemon_is_clean ... ok\ntest pulse_bridge::tests::per_pid_sink_args_format_is_stable ... ok\ntest routing::tests::empty_pattern_matches_any_candidate ... ok\ntest pulse_bridge::tests::integration_smoke_apply_release_cycle_does_not_deadlock ... ok\ntest pulse_bridge::tests::push_f32_chunk_respects_queue_cap_and_self_consistent_after_drain ... ok\n\ntest result: ok. 69 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s\n\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n", + "stderrTail": " Compiling fluxer_linux_audio_capture v0.0.0 (/workspaces/fluxer/fluxer_desktop/native/linux-audio-capture)\n Finished `test` profile [unoptimized + debuginfo] target(s) in 1.32s\n Running unittests src/lib.rs (target/debug/deps/fluxer_linux_audio_capture-d5f10b7d43827c26)\n[W][15:40:45.313376] pw.conf | [ conf.c: 1182 try_load_conf()] can't load config client.conf: No such file or directory\n[E][15:40:45.313405] pw.conf | [ conf.c: 1215 pw_conf_load_conf_for_context()] can't load config client.conf: No such file or directory\n[W][15:40:45.313191] pw.conf | [ conf.c: 1182 try_load_conf()] can't load config client.conf: No such file or directory\n[E][15:40:45.313441] pw.conf | [ conf.c: 1215 pw_conf_load_conf_for_context()] can't load config client.conf: No such file or directory\n[W][15:40:45.313613] pw.conf | [ conf.c: 1182 try_load_conf()] can't load config client.conf: No such file or directory\n[E][15:40:45.313620] pw.conf | [ conf.c: 1215 pw_conf_load_conf_for_context()] can't load config client.conf: No such file or directory\n Doc-tests fluxer_linux_audio_capture\n" + } + ] +} diff --git a/fluxer_desktop/native-media-reports/2026-06-06T22-10-30Z-codex-host-smoke/summary.json b/fluxer_desktop/native-media-reports/2026-06-06T22-10-30Z-codex-host-smoke/summary.json new file mode 100644 index 0000000..9ad9b41 --- /dev/null +++ b/fluxer_desktop/native-media-reports/2026-06-06T22-10-30Z-codex-host-smoke/summary.json @@ -0,0 +1,149 @@ +{ + "status": "fail", + "mode": "smoke", + "strict": false, + "platform": "darwin", + "arch": "arm64", + "hostname": "Hampuss-Mac-Studio.local", + "startedAt": "2026-06-06T22:10:30.484Z", + "endedAt": "2026-06-06T22:11:23.013Z", + "reportDir": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/2026-06-06T22-10-30Z-codex-host-smoke", + "gates": [ + { + "name": "mode-valid", + "status": "pass", + "mode": "smoke" + }, + { + "name": "documentation-current", + "status": "pass", + "missing": [] + }, + { + "name": "strict-prerequisites", + "status": "skip", + "reason": "smoke mode" + }, + { + "name": "strict-command-plan", + "status": "skip", + "reason": "smoke mode" + } + ], + "plannedCommands": [ + { + "name": "main-process-native-media-unit-tests", + "command": "node --test src/main/WebrtcScreenPublish.test.mjs src/main/NativeVoiceEngine.test.mjs src/main/NativeScreenCapture.test.mjs", + "category": "shared", + "cwd": "." + }, + { + "name": "desktop-typecheck", + "command": "pnpm typecheck", + "category": "shared", + "cwd": "." + }, + { + "name": "webrtc-sender-build", + "command": "pnpm --dir native/webrtc-sender build", + "category": "native-sender", + "cwd": "." + }, + { + "name": "webrtc-sender-tests", + "command": "pnpm --dir native/webrtc-sender test", + "category": "native-sender", + "cwd": "." + }, + { + "name": "webrtc-sender-livekit-harness", + "command": "pnpm --dir native/webrtc-sender test:livekit", + "category": "livekit", + "cwd": "." + }, + { + "name": "mac-screen-capture-build", + "command": "pnpm --dir native/mac-screen-capture build", + "category": "platform-native", + "cwd": "." + }, + { + "name": "mac-screen-capture-tests", + "command": "pnpm --dir native/mac-screen-capture test", + "category": "platform-native", + "cwd": "." + }, + { + "name": "mac-app-audio-build", + "command": "pnpm --dir native/mac-app-audio build", + "category": "platform-native", + "cwd": "." + }, + { + "name": "mac-app-audio-rust-tests", + "command": "pnpm --dir native/mac-app-audio test:cargo", + "category": "platform-native", + "cwd": "." + }, + { + "name": "mac-app-audio-js-tests", + "command": "'/Users/hampus/Development/fluxer/node_modules/.pnpm/node_modules/.bin/vitest' run", + "category": "platform-native", + "cwd": "native/mac-app-audio" + } + ], + "commands": [ + { + "name": "main-process-native-media-unit-tests", + "command": "node --test src/main/WebrtcScreenPublish.test.mjs src/main/NativeVoiceEngine.test.mjs src/main/NativeScreenCapture.test.mjs", + "category": "shared", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-06T22:10:30.486Z", + "endedAt": "2026-06-06T22:10:30.704Z", + "durationMs": 218, + "stdoutTail": "▶ NativeScreenCapture DMA-BUF metadata validation\n ✔ accepts valid DMA-BUF metadata and pads native plane arrays (2.191542ms)\n ✔ keeps valid handles when optional device UUID metadata is absent or malformed (0.45425ms)\n ✔ rejects invalid plane counts, FDs, strides, and format metadata (0.478709ms)\n✔ NativeScreenCapture DMA-BUF metadata validation (3.578041ms)\n▶ NativeScreenCapture source identity and capability reporting\n ✔ normalizes display and window sources without changing source ids (0.671125ms)\n ✔ starts display and window captures with exact source id and kind and reports diagnostics (1.393083ms)\n ✔ forwards frames to renderer even when a native frame tap is installed (0.572875ms)\n ✔ reports macOS and Linux display/window capture capabilities from platform backends (1.508583ms)\n✔ NativeScreenCapture source identity and capability reporting (4.293125ms)\n▶ NativeVoiceEngine screen frame routing\n ✔ pushes the initial capture frame after publishing screen share (2.516958ms)\n ✔ prefers Windows shared texture handles over DMA-BUF and CPU frames (0.687709ms)\n ✔ uses Linux DMA-BUF handles before CPU conversion (0.544916ms)\n ✔ falls back to CPU frames when GPU paths are unavailable or rejected (0.468917ms)\n ✔ keeps screen-share audio published when screen video is unpublished (0.553791ms)\n ✔ replaces screen capture taps without releasing screen-share audio or duplicating priority (0.479167ms)\n ✔ forwards watch and unwatch operations with source, enablement, and quality intact (1.627166ms)\n ✔ reports VideoToolbox hardware capability from the native addon (0.5325ms)\n ✔ reports native voice engine capabilities from the native addon (0.4195ms)\n ✔ maps native addon microphone not-connected errors to the typed operation code (0.560208ms)\n✔ NativeVoiceEngine screen frame routing (9.007958ms)\n▶ WebrtcScreenPublish native frame routing\n ✔ prefers Windows shared texture handles over DMA-BUF and CPU buffers (2.107208ms)\n ✔ uses Linux DMA-BUF handles before CPU conversion (0.448834ms)\n ✔ falls back to CPU frames when GPU paths are unavailable or rejected (0.413166ms)\n ✔ replaces an existing publisher before routing frames for the same capture id (0.406ms)\n✔ WebrtcScreenPublish native frame routing (3.859209ms)\nℹ tests 21\nℹ suites 4\nℹ pass 21\nℹ fail 0\nℹ cancelled 0\nℹ skipped 0\nℹ todo 0\nℹ duration_ms 155.770792\n", + "stderrTail": "" + }, + { + "name": "desktop-typecheck", + "command": "pnpm typecheck", + "category": "shared", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-06T22:10:30.705Z", + "endedAt": "2026-06-06T22:10:32.150Z", + "durationMs": 1445, + "stdoutTail": "native/linux-audio-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/linux-evdev |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/linux-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/linux-notifications |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/linux-portals |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/linux-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/system-hunspell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n\n> fluxer_desktop@0.0.0 typecheck /Users/hampus/Development/fluxer/fluxer_desktop\n> tsgo --noEmit\n\n", + "stderrTail": "" + }, + { + "name": "webrtc-sender-build", + "command": "pnpm --dir native/webrtc-sender build", + "category": "native-sender", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-06T22:10:32.150Z", + "endedAt": "2026-06-06T22:11:01.715Z", + "durationMs": 29565, + "stdoutTail": "../linux-audio-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-evdev |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-notifications |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-portals |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../system-hunspell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n\n> @fluxer/webrtc-sender@0.0.0 build /Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender\n> cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon\n\n[buildRustNodeAddon] cwd=/Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender\n+ /Users/hampus/.rustup/toolchains/1.93.0-aarch64-apple-darwin/bin/cargo build --release --target aarch64-apple-darwin --manifest-path Cargo.toml --features camera-native\n", + "stderrTail": "warning: unused import: `enum_dispatch`\n --> vendor/libwebrtc/src/audio_source.rs:15:13\n |\n15 | use crate::{enum_dispatch, imp::audio_source as imp_as};\n | ^^^^^^^^^^^^^\n |\n = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default\n\nwarning: unnecessary `unsafe` block\n --> vendor/libwebrtc/src/native/apm.rs:32:25\n |\n32 | sys_handle: unsafe {\n | ^^^^^^ unnecessary `unsafe` block\n |\n = note: `#[warn(unused_unsafe)]` (part of `#[warn(unused)]`) on by default\n\nwarning: hiding a lifetime that's elided elsewhere is confusing\n --> vendor/libwebrtc/src/native/audio_mixer.rs:30:34\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option;\n | ^^^^^ the lifetime is elided here ^^^^^^^^^^ the same lifetime is hidden here\n |\n = help: the same lifetime is referred to in inconsistent ways, making the signature confusing\n = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default\nhelp: use `'_` for type paths\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option>;\n | ++++\n\nwarning: `libwebrtc` (lib) generated 3 warnings (run `cargo fix --lib -p libwebrtc` to apply 2 suggestions)\n Compiling fluxer_webrtc_sender v0.0.0 (/Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender)\n Finished `release` profile [optimized] target(s) in 9.86s\n" + }, + { + "name": "webrtc-sender-tests", + "command": "pnpm --dir native/webrtc-sender test", + "category": "native-sender", + "status": "fail", + "code": 1, + "signal": null, + "startedAt": "2026-06-06T22:11:01.715Z", + "endedAt": "2026-06-06T22:11:23.013Z", + "durationMs": 21298, + "stdoutTail": "e_contract_range ... ok\ntest audio::tests::is_muted_only_at_zero ... ok\ntest audio::tests::processing_options_preserve_defaults_unless_overridden ... ok\ntest audio::tests::output_devices_json_array ... ok\ntest audio::tests::output_device_json_locks_the_contract_shape ... ok\ntest audio::tests::input_devices_json_array ... ok\ntest audio::tests::shape_input_marks_index_zero_default_and_drops_empty_ids ... ok\ntest audio::tests::shape_marks_index_zero_default_and_drops_empty_ids ... ok\ntest camera::live::tests::fallback_format_selection_uses_decoder_supported_non_mjpeg_modes ... ok\ntest camera::live::tests::fallback_format_selection_prefers_nearest_resolution_before_format_priority ... ok\ntest camera::tests::from_opts_applies_defaults ... ok\ntest camera::tests::from_opts_forces_even_dims_and_honours_overrides ... ok\ntest camera::tests::from_opts_parses_device_index ... ok\ntest camera::tests::from_opts_preserves_string_device_ids ... ok\ntest camera::tests::from_opts_rejects_degenerate_dims_and_fps ... ok\ntest config::tests::accepts_dimension_boundaries_and_rejects_zero_fps ... ok\ntest config::tests::canonical_codec_name_accepts_all_five_case_insensitively ... ok\ntest config::tests::canonical_codec_name_rejects_empty_and_unknown ... ok\ntest config::tests::rejects_bad_url ... ok\ntest config::tests::rejects_empty_token ... ok\ntest config::tests::rejects_odd_or_oob_dimensions ... ok\ntest config::tests::rejects_unknown_non_empty_codec_but_allows_empty_default ... ok\ntest config::tests::state_gates_frames_and_connect ... ok\ntest config::tests::valid_config_passes ... ok\ntest engine::tests::adaptive_video_sender_buffers_two_fresh_frames_without_coalescing ... ok\ntest engine::tests::adaptive_video_sender_coalesces_only_when_fresh_queue_overflows ... ok\ntest engine::tests::adaptive_video_sender_drops_stale_queued_frame_without_counting_coalescing ... ok\ntest engine::tests::adaptive_video_sender_stop_clears_pending_and_rejects_new_frames ... ok\ntest engine::tests::bgra_input_validation_requires_even_dims_stride_and_complete_buffer ... ok\ntest engine::tests::nv12_input_validation_requires_complete_luma_and_chroma_planes ... ok\ntest engine::tests::parse_codec_agrees_with_pure_canonicaliser ... ok\ntest engine::tests::parse_codec_maps_all_five_including_h265 ... ok\ntest engine::tests::parse_codec_rejects_empty_and_unknown ... ok\ntest engine::tests::parse_track_source_accepts_renderer_and_livekit_spellings ... ok\ntest engine::tests::parse_video_quality_accepts_subscription_quality_levels ... ok\ntest engine::tests::pcm16_audio_frame_rejects_malformed_buffers ... ok\ntest engine::tests::pcm16_audio_frame_shapes_interleaved_stereo_samples ... ok\ntest engine::tests::session_rtt_falls_back_to_subscriber_and_preserves_existing_rtt ... ok\ntest engine::tests::session_rtt_uses_peer_connection_candidate_pair_without_track_stats ... ok\ntest engine::tests::tight_i420_len_matches_plane_geometry ... ok\ntest engine::tests::video_frame_meta_json_matches_contract ... ok\ntest engine::tests::video_input_validation_rejects_degenerate_odd_and_oversized_frames ... ok\ntest engine::tests::video_timestamp_validation_rejects_values_that_cast_badly_at_napi_boundary ... ok\ntest events::tests::empty_object_and_array ... ok\ntest events::tests::escapes_quote_backslash_and_control_chars ... ok\ntest events::tests::json_object_preserves_order_and_mixes_str_and_raw ... ok\ntest events::tests::json_u8_array_serializes_bytes_as_numbers ... ok\ntest events::tests::leaves_plain_ascii_and_unicode_untouched ... ok\ntest events::tests::raw_array_preserves_prebuilt_json_objects ... ok\ntest hardware_encoder::tests::publish_codec_runtime_gate_accepts_h265_when_reported_available ... ok\ntest hardware_encoder::tests::publish_codec_runtime_gate_only_requires_hardware_for_h265 ... ok\ntest hardware_encoder::tests::reports_available_nvenc_when_compiled_and_runtime_ready ... ok\ntest hardware_encoder::tests::reports_available_videotoolbox_with_h264_and_h265 ... ok\ntest hardware_encoder::tests::reports_driver_sdk_incompatibility_before_encode_session ... ok\ntest hardware_encoder::tests::reports_h264_only_when_hevc_guid_is_absent ... ok\ntest hardware_encoder::tests::reports_missing_runtime_prerequisites_when_compiled ... ok\ntest hardware_encoder::tests::reports_missing_runtime_prerequisites_when_probe_is_absent ... ok\ntest hardware_encoder::tests::reports_no_cuda_devices_using_obs_style_reason ... ok\ntest hardware_encoder::tests::reports_no_supported_codecs_when_guid_query_finds_no_fluxer_codecs ... ok\ntest hardware_encoder::tests::reports_not_compiled_before_runtime_missing ... ok\ntest hardware_encoder::tests::reports_unsupported_platforms_clearly ... ok\ntest hardware_encoder::tests::reports_videotoolbox_h264_only_when_hevc_session_is_unavailable ... ok\ntest hardware_encoder::tests::reports_videotoolbox_unavailable_with_status_details ... ok\ntest hardware_encoder::tests::reports_windows_nvenc_publish_backend_not_compiled ... ok\ntest hardware_encoder::tests::reports_windows_nvenc_runtime_dll_names_when_missing ... ok\ntest hardware_encoder::tests::reports_windows_nvenc_without_linux_zero_copy_inputs ... ok\ntest publisher::tests::parse_codec_maps_known_names ... ok\ntest publisher::tests::i420_plane_copy_fills_libwebrtc_buffer ... ok\ntest send_control::tests::audio_buffer_growth_caps_at_configured_max_and_reset_clears_pressure ... ok\ntest send_control::tests::audio_buffer_expands_on_gaps_and_shrinks_after_stability ... ok\ntest send_control::tests::audio_buffer_ignores_clock_regression_without_false_rebuffer ... ok\ntest send_control::tests::audio_buffer_max_is_clamped_to_real_time_bounds ... ok\ntest send_control::tests::idle_snapshot_reflects_audio_pressure_without_video_state ... ok\ntest send_control::tests::video_controller_continues_degrading_to_minimum_under_sustained_pressure ... ok\ntest send_control::tests::video_controller_counts_coalesced_replacements_once_for_pressure ... ok\ntest send_control::tests::audio_buffer_ignores_concurrent_stale_pushes_without_moving_last_push_backwards ... ok\ntest send_control::tests::video_controller_degrades_on_encoder_egress_pressure ... ok\ntest send_control::tests::audio_buffer_stress_stays_within_realtime_bounds_under_jitter ... ok\ntest send_control::tests::video_controller_degrades_on_latency_pressure_without_coalescing ... ok\ntest send_control::tests::video_controller_degrades_on_repeated_coalescing_pressure ... ok\ntest send_control::tests::video_controller_degrades_when_disrupted_frame_ratio_exceeds_threshold ... ok\ntest send_control::tests::video_controller_ignores_bounded_one_frame_coalescing_pressure ... ok\ntest send_control::tests::video_controller_ignores_single_encoder_egress_sample ... ok\ntest send_control::tests::video_controller_keeps_requested_fps_when_adaptive_send_is_disabled ... ok\ntest send_control::tests::video_controller_preserves_pressure_reason_while_degraded_but_stable ... ok\ntest send_control::tests::video_controller_recovers_after_stable_windows ... ok\ntest send_control::tests::video_controller_recovers_from_minimum_in_two_stable_steps ... ok\ntest send_control::tests::video_controller_requires_a_full_stable_recovery_window ... ok\ntest send_control::tests::video_controller_waits_until_pressure_window_boundary ... ok\ntest send_control::tests::video_stats_effective_fps_uses_capture_time_bounds_for_out_of_order_records ... ok\ntest send_control::tests::video_stats_snapshot_counts_coalescing_drops_failures_and_effective_fps ... ok\ntest stats::tests::bitrate_computes_kbps_from_byte_delta ... ok\ntest stats::tests::bitrate_first_sample_is_zero ... ok\ntest stats::tests::bitrate_half_second_doubles_rate ... ok\ntest send_control::tests::video_controller_stress_keeps_target_inside_configured_bounds ... ok\ntest stats::tests::bitrate_rejects_backwards_time_and_bytes ... ok\ntest stats::tests::empty_stats_serialise_to_null_rtt_and_empty_arrays ... ok\ntest stats::tests::sanitize_audio_level_clamps ... ok\ntest stats::tests::full_stats_serialise_to_exact_contract_shape ... ok\ntest stats::tests::sanitize_kbps_drops_nan_inf_negative_and_rounds ... ok\ntest stats::tests::send_health_serialises_to_exact_contract_shape ... ok\ntest stats::tests::unit_conversions_seconds_to_ms ... ok\ntest stats::tests::video_inbound_omits_audio_level_audio_omits_fps ... ok\ntest texture_source::tests::dmabuf_desc_from_parts_populates_all_plane_arrays ... ok\ntest texture_source::tests::dmabuf_desc_from_parts_rejects_incomplete_native_inputs ... ok\ntest texture_source::tests::dmabuf_desc_from_parts_rejects_negative_fds ... ok\ntest texture_source::tests::drm_format_gate_accepts_obs_vkcapture_texture_formats ... ok\ntest texture_source::tests::dxgi_format_gate_accepts_8bit_rgba_bgra_only ... ok\ntest send_control::tests::video_stats_handles_concurrent_recording_without_lost_counts ... ok\ntest texture_source::tests::error_strings_are_stable ... ok\ntest texture_source::tests::probe_is_available_only_for_explicit_hardware_codecs ... ok\ntest texture_source::tests::should_attempt_dmabuf_is_sdk_gated_after_validation_capability ... ok\ntest texture_source::tests::should_attempt_falls_back_when_capability_unavailable ... ok\ntest texture_source::tests::should_attempt_validates_frame_when_capability_available ... ok\ntest texture_source::tests::validate_accepts_a_clean_bgra_texture ... ok\ntest texture_source::tests::validate_dmabuf_accepts_supported_formats_with_optional_uuid ... ok\ntest texture_source::tests::validate_dmabuf_rejects_invalid_planes ... ok\ntest texture_source::tests::validate_precedence_handle_before_dims_before_format ... ok\ntest texture_source::tests::validate_rejects_odd_zero_and_oversized_dims ... ok\ntest texture_source::tests::validate_rejects_unsupported_format ... ok\ntest texture_source::tests::validate_rejects_zero_handle_as_no_texture ... ok\ntest yuv::tests::bgra_black_white_grey_levels ... ok\ntest yuv::tests::bgra_golden_2x2_solid_colour_exact_bytes ... ok\ntest yuv::tests::bgra_golden_strided_2x2_skips_row_padding ... ok\ntest yuv::tests::bgra_plane_sizes ... ok\ntest yuv::tests::bgra_primaries_have_expected_chroma_signs ... ok\ntest yuv::tests::copy_nv12_planes_preserves_nv12_layout ... ok\ntest yuv::tests::copy_nv12_planes_rejects_short_buffers ... ok\ntest yuv::tests::copy_nv12_planes_respects_destination_stride ... ok\ntest yuv::tests::nv12_4x4_deinterleaves_two_chroma_columns ... ok\ntest yuv::tests::nv12_golden_4x2_packed_to_i420 ... ok\ntest yuv::tests::nv12_packed_2x2_deinterleaves ... ok\ntest yuv::tests::nv12_respects_row_padding ... ok\ntest yuv::tests::nv12_short_buffer_is_rejected ... ok\ntest yuv::tests::rejects_odd_or_tiny_dimensions ... ok\ntest yuv::tests::rgb_golden_2x2_solid_colour_matches_bgra_path ... ok\ntest yuv::tests::rgb_plane_sizes_and_levels ... ok\ntest yuv::tests::rgb_rejects_odd_dims_and_short_buffer ... ok\ntest engine::tests::pcm16_audio_frame_rejects_implausible_format_and_overlong_chunks ... ok\n\ntest result: ok. 140 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s\n\n\nrunning 6 tests\ntest outbound_fixtures_match_contract ... ok\ntest video_frame_meta_matches_contract ... ok\ntest inbound_covers_subscribe_and_lifecycle ... ok\ntest outbound_covers_the_single_identity_publish_path ... ok\ntest e2ee_state_is_ok_on_the_wire ... ok\ntest inbound_fixtures_match_contract ... ok\n\ntest result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n✖ index.test.mjs (65.935708ms)\n✖ scripts/livekit-harness.test.mjs (68.75525ms)\nℹ tests 2\nℹ suites 0\nℹ pass 0\nℹ fail 2\nℹ cancelled 0\nℹ skipped 0\nℹ todo 0\nℹ duration_ms 75.266917\n\n✖ failing tests:\n\ntest at index.test.mjs:1:1\n✖ index.test.mjs (65.935708ms)\n 'test failed'\n\ntest at scripts/livekit-harness.test.mjs:1:1\n✖ scripts/livekit-harness.test.mjs (68.75525ms)\n 'test failed'\n ELIFECYCLE  Test failed. See above for more details.\n WARN  Local package.json exists, but node_modules missing, did you mean to install?\n", + "stderrTail": " Compiling ring v0.17.14\n Compiling fluxer_webrtc_sender v0.0.0 (/Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender)\n Compiling fluxer_voice_core v0.1.0 (/Users/hampus/Development/fluxer/fluxer_voice_core)\n Compiling rustls v0.23.40\n Compiling rustls-webpki v0.103.13\n Compiling tokio-rustls v0.26.4\n Compiling tungstenite v0.29.0\n Compiling hyper-rustls v0.27.9\n Compiling reqwest v0.12.28\n Compiling tokio-tungstenite v0.29.0\n Compiling livekit-api v0.5.0\n Compiling webrtc-sys-build v0.3.18\n Compiling webrtc-sys v0.3.33 (/Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys)\n Compiling libwebrtc v0.3.35 (/Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc)\nwarning: unused import: `enum_dispatch`\n --> vendor/libwebrtc/src/audio_source.rs:15:13\n |\n15 | use crate::{enum_dispatch, imp::audio_source as imp_as};\n | ^^^^^^^^^^^^^\n |\n = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default\n\nwarning: unnecessary `unsafe` block\n --> vendor/libwebrtc/src/native/apm.rs:32:25\n |\n32 | sys_handle: unsafe {\n | ^^^^^^ unnecessary `unsafe` block\n |\n = note: `#[warn(unused_unsafe)]` (part of `#[warn(unused)]`) on by default\n\nwarning: hiding a lifetime that's elided elsewhere is confusing\n --> vendor/libwebrtc/src/native/audio_mixer.rs:30:34\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option;\n | ^^^^^ the lifetime is elided here ^^^^^^^^^^ the same lifetime is hidden here\n |\n = help: the same lifetime is referred to in inconsistent ways, making the signature confusing\n = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default\nhelp: use `'_` for type paths\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option>;\n | ++++\n\n Compiling livekit v0.7.43\nwarning: `libwebrtc` (lib) generated 3 warnings (run `cargo fix --lib -p libwebrtc` to apply 2 suggestions)\n Finished `test` profile [unoptimized + debuginfo] target(s) in 17.99s\n Running unittests src/lib.rs (target/debug/deps/fluxer_webrtc_sender-4fb413a724225319)\n Running tests/event_contract.rs (target/debug/deps/event_contract-dc15840aabc01f6c)\n Doc-tests fluxer_webrtc_sender\n" + } + ] +} diff --git a/fluxer_desktop/native-media-reports/2026-06-06T22-15-48Z-codex-host-smoke-signed/livekit-harness.json b/fluxer_desktop/native-media-reports/2026-06-06T22-15-48Z-codex-host-smoke-signed/livekit-harness.json new file mode 100644 index 0000000..690ca68 --- /dev/null +++ b/fluxer_desktop/native-media-reports/2026-06-06T22-15-48Z-codex-host-smoke-signed/livekit-harness.json @@ -0,0 +1,197 @@ +{ + "status": "fail", + "startedAt": "2026-06-06T22:15:58.713Z", + "endedAt": "2026-06-06T22:16:28.718Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:7880", + "serverApiUrl": "http://127.0.0.1:7880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-84586-8f492568-efae-42a1-a344-5a0c9599fa8d", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-84586", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-84586", + "subscriberIdentity": "fluxer-native-subscriber-84586", + "required": false, + "strict": false, + "timeoutMs": 30000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 0, + "width": 320, + "height": 180, + "expectedWidth": 320, + "expectedHeight": 180, + "fps": 15, + "minFpsRatio": 0, + "maxFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": null, + "requireStableResolution": false, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": null, + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": false, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": true, + "secondaryPublisherScreenSimulcast": true, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 400000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": false, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/2026-06-06T22-15-48Z-codex-host-smoke-signed/livekit-harness.json", + "verbose": false, + "scenarioName": "codec-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": false + }, + { + "name": "subscriber connected", + "pass": false + }, + { + "name": "publisher local screenshare publication", + "pass": false + }, + { + "name": "subscriber remote screenshare subscription", + "pass": false + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": false + }, + { + "name": "subscriber video frame callback", + "pass": false + }, + { + "name": "publisher outbound screenshare stats", + "pass": false + }, + { + "name": "subscriber inbound video stats", + "pass": false + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": false + }, + { + "name": "subscriber video resolution 320x180", + "pass": false + }, + { + "name": "subscriber aggregate inbound audio frames >= 3", + "pass": false + }, + { + "name": "subscriber inbound audio stats count >= 1", + "pass": false + }, + { + "name": "publisher local microphone publication", + "pass": false + }, + { + "name": "subscriber remote microphone subscription", + "pass": false + }, + { + "name": "subscriber inbound audio frames >= 3", + "pass": false + }, + { + "name": "publisher outbound microphone stats", + "pass": false + }, + { + "name": "subscriber inbound audio stats", + "pass": false + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": false + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": false + } + ], + "skippedReason": null, + "error": "Error: connect timed out after 30000ms\n at file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1346:9\n at async runHarness (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1773:3)\n at async main (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:2025:18)\n at async file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:2058:19", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 0, + "strictAudioFrames": 0, + "strictVideoFrames": 0, + "videoCallbacks": 0, + "videoBytes": 0, + "receivedFps": 0, + "requiredFps": null, + "maxVideoFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 0, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": {}, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": null, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": null, + "error": null, + "participants": [] + }, + "lastVideoMeta": null, + "publisherStats": null, + "secondaryPublisherStats": null, + "subscriberStats": null + } +} diff --git a/fluxer_desktop/native-media-reports/2026-06-06T22-15-48Z-codex-host-smoke-signed/summary.json b/fluxer_desktop/native-media-reports/2026-06-06T22-15-48Z-codex-host-smoke-signed/summary.json new file mode 100644 index 0000000..fe5200c --- /dev/null +++ b/fluxer_desktop/native-media-reports/2026-06-06T22-15-48Z-codex-host-smoke-signed/summary.json @@ -0,0 +1,162 @@ +{ + "status": "fail", + "mode": "smoke", + "strict": false, + "platform": "darwin", + "arch": "arm64", + "hostname": "Hampuss-Mac-Studio.local", + "startedAt": "2026-06-06T22:15:48.881Z", + "endedAt": "2026-06-06T22:16:28.858Z", + "reportDir": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/2026-06-06T22-15-48Z-codex-host-smoke-signed", + "gates": [ + { + "name": "mode-valid", + "status": "pass", + "mode": "smoke" + }, + { + "name": "documentation-current", + "status": "pass", + "missing": [] + }, + { + "name": "strict-prerequisites", + "status": "skip", + "reason": "smoke mode" + }, + { + "name": "strict-command-plan", + "status": "skip", + "reason": "smoke mode" + } + ], + "plannedCommands": [ + { + "name": "main-process-native-media-unit-tests", + "command": "node --test src/main/WebrtcScreenPublish.test.mjs src/main/NativeVoiceEngine.test.mjs src/main/NativeScreenCapture.test.mjs", + "category": "shared", + "cwd": "." + }, + { + "name": "desktop-typecheck", + "command": "pnpm typecheck", + "category": "shared", + "cwd": "." + }, + { + "name": "webrtc-sender-build", + "command": "pnpm --dir native/webrtc-sender build", + "category": "native-sender", + "cwd": "." + }, + { + "name": "webrtc-sender-tests", + "command": "pnpm --dir native/webrtc-sender test", + "category": "native-sender", + "cwd": "." + }, + { + "name": "webrtc-sender-livekit-harness", + "command": "pnpm --dir native/webrtc-sender test:livekit", + "category": "livekit", + "cwd": "." + }, + { + "name": "mac-screen-capture-build", + "command": "pnpm --dir native/mac-screen-capture build", + "category": "platform-native", + "cwd": "." + }, + { + "name": "mac-screen-capture-tests", + "command": "pnpm --dir native/mac-screen-capture test", + "category": "platform-native", + "cwd": "." + }, + { + "name": "mac-app-audio-build", + "command": "pnpm --dir native/mac-app-audio build", + "category": "platform-native", + "cwd": "." + }, + { + "name": "mac-app-audio-rust-tests", + "command": "pnpm --dir native/mac-app-audio test:cargo", + "category": "platform-native", + "cwd": "." + }, + { + "name": "mac-app-audio-js-tests", + "command": "'/Users/hampus/Development/fluxer/node_modules/.pnpm/node_modules/.bin/vitest' run", + "category": "platform-native", + "cwd": "native/mac-app-audio" + } + ], + "commands": [ + { + "name": "main-process-native-media-unit-tests", + "command": "node --test src/main/WebrtcScreenPublish.test.mjs src/main/NativeVoiceEngine.test.mjs src/main/NativeScreenCapture.test.mjs", + "category": "shared", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-06T22:15:48.882Z", + "endedAt": "2026-06-06T22:15:49.087Z", + "durationMs": 205, + "stdoutTail": "▶ NativeScreenCapture DMA-BUF metadata validation\n ✔ accepts valid DMA-BUF metadata and pads native plane arrays (2.208167ms)\n ✔ keeps valid handles when optional device UUID metadata is absent or malformed (0.448834ms)\n ✔ rejects invalid plane counts, FDs, strides, and format metadata (0.462542ms)\n✔ NativeScreenCapture DMA-BUF metadata validation (3.565042ms)\n▶ NativeScreenCapture source identity and capability reporting\n ✔ normalizes display and window sources without changing source ids (0.66975ms)\n ✔ starts display and window captures with exact source id and kind and reports diagnostics (1.315458ms)\n ✔ forwards frames to renderer even when a native frame tap is installed (0.511625ms)\n ✔ reports macOS and Linux display/window capture capabilities from platform backends (1.343834ms)\n✔ NativeScreenCapture source identity and capability reporting (3.975166ms)\n▶ NativeVoiceEngine screen frame routing\n ✔ pushes the initial capture frame after publishing screen share (2.103875ms)\n ✔ prefers Windows shared texture handles over DMA-BUF and CPU frames (0.476083ms)\n ✔ uses Linux DMA-BUF handles before CPU conversion (0.5025ms)\n ✔ falls back to CPU frames when GPU paths are unavailable or rejected (0.419958ms)\n ✔ keeps screen-share audio published when screen video is unpublished (0.485458ms)\n ✔ replaces screen capture taps without releasing screen-share audio or duplicating priority (0.930666ms)\n ✔ forwards watch and unwatch operations with source, enablement, and quality intact (0.89425ms)\n ✔ reports VideoToolbox hardware capability from the native addon (0.447916ms)\n ✔ reports native voice engine capabilities from the native addon (0.39275ms)\n ✔ maps native addon microphone not-connected errors to the typed operation code (0.527125ms)\n✔ NativeVoiceEngine screen frame routing (7.720792ms)\n▶ WebrtcScreenPublish native frame routing\n ✔ prefers Windows shared texture handles over DMA-BUF and CPU buffers (1.90625ms)\n ✔ uses Linux DMA-BUF handles before CPU conversion (0.453542ms)\n ✔ falls back to CPU frames when GPU paths are unavailable or rejected (0.402ms)\n ✔ replaces an existing publisher before routing frames for the same capture id (0.400541ms)\n✔ WebrtcScreenPublish native frame routing (3.595417ms)\nℹ tests 21\nℹ suites 4\nℹ pass 21\nℹ fail 0\nℹ cancelled 0\nℹ skipped 0\nℹ todo 0\nℹ duration_ms 144.171292\n", + "stderrTail": "" + }, + { + "name": "desktop-typecheck", + "command": "pnpm typecheck", + "category": "shared", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-06T22:15:49.088Z", + "endedAt": "2026-06-06T22:15:50.164Z", + "durationMs": 1076, + "stdoutTail": "native/linux-audio-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/linux-evdev |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/linux-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/linux-notifications |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/linux-portals |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/linux-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/system-hunspell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n\n> fluxer_desktop@0.0.0 typecheck /Users/hampus/Development/fluxer/fluxer_desktop\n> tsgo --noEmit\n\n", + "stderrTail": "" + }, + { + "name": "webrtc-sender-build", + "command": "pnpm --dir native/webrtc-sender build", + "category": "native-sender", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-06T22:15:50.164Z", + "endedAt": "2026-06-06T22:15:54.767Z", + "durationMs": 4603, + "stdoutTail": "../linux-audio-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-evdev |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-notifications |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-portals |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../system-hunspell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n\n> @fluxer/webrtc-sender@0.0.0 build /Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender\n> cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon\n\n[buildRustNodeAddon] cwd=/Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender\n+ /Users/hampus/.rustup/toolchains/1.93.0-aarch64-apple-darwin/bin/cargo build --release --target aarch64-apple-darwin --manifest-path Cargo.toml --features camera-native\n+ codesign --force --sign - /Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/webrtc-sender.darwin-arm64.node\n", + "stderrTail": "warning: unused import: `enum_dispatch`\n --> vendor/libwebrtc/src/audio_source.rs:15:13\n |\n15 | use crate::{enum_dispatch, imp::audio_source as imp_as};\n | ^^^^^^^^^^^^^\n |\n = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default\n\nwarning: unnecessary `unsafe` block\n --> vendor/libwebrtc/src/native/apm.rs:32:25\n |\n32 | sys_handle: unsafe {\n | ^^^^^^ unnecessary `unsafe` block\n |\n = note: `#[warn(unused_unsafe)]` (part of `#[warn(unused)]`) on by default\n\nwarning: hiding a lifetime that's elided elsewhere is confusing\n --> vendor/libwebrtc/src/native/audio_mixer.rs:30:34\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option;\n | ^^^^^ the lifetime is elided here ^^^^^^^^^^ the same lifetime is hidden here\n |\n = help: the same lifetime is referred to in inconsistent ways, making the signature confusing\n = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default\nhelp: use `'_` for type paths\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option>;\n | ++++\n\nwarning: `libwebrtc` (lib) generated 3 warnings (run `cargo fix --lib -p libwebrtc` to apply 2 suggestions)\n Finished `release` profile [optimized] target(s) in 0.17s\n/Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/webrtc-sender.darwin-arm64.node: replacing existing signature\n" + }, + { + "name": "webrtc-sender-tests", + "command": "pnpm --dir native/webrtc-sender test", + "category": "native-sender", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-06T22:15:54.767Z", + "endedAt": "2026-06-06T22:15:57.734Z", + "durationMs": 2967, + "stdoutTail": ".. ok\ntest hardware_encoder::tests::publish_codec_runtime_gate_only_requires_hardware_for_h265 ... ok\ntest hardware_encoder::tests::reports_available_nvenc_when_compiled_and_runtime_ready ... ok\ntest hardware_encoder::tests::reports_available_videotoolbox_with_h264_and_h265 ... ok\ntest hardware_encoder::tests::reports_driver_sdk_incompatibility_before_encode_session ... ok\ntest hardware_encoder::tests::reports_h264_only_when_hevc_guid_is_absent ... ok\ntest hardware_encoder::tests::reports_missing_runtime_prerequisites_when_compiled ... ok\ntest hardware_encoder::tests::reports_no_cuda_devices_using_obs_style_reason ... ok\ntest hardware_encoder::tests::reports_no_supported_codecs_when_guid_query_finds_no_fluxer_codecs ... ok\ntest hardware_encoder::tests::reports_not_compiled_before_runtime_missing ... ok\ntest hardware_encoder::tests::reports_missing_runtime_prerequisites_when_probe_is_absent ... ok\ntest hardware_encoder::tests::reports_unsupported_platforms_clearly ... ok\ntest hardware_encoder::tests::reports_videotoolbox_h264_only_when_hevc_session_is_unavailable ... ok\ntest hardware_encoder::tests::reports_videotoolbox_unavailable_with_status_details ... ok\ntest hardware_encoder::tests::reports_windows_nvenc_publish_backend_not_compiled ... ok\ntest hardware_encoder::tests::reports_windows_nvenc_runtime_dll_names_when_missing ... ok\ntest hardware_encoder::tests::reports_windows_nvenc_without_linux_zero_copy_inputs ... ok\ntest publisher::tests::i420_plane_copy_fills_libwebrtc_buffer ... ok\ntest publisher::tests::parse_codec_maps_known_names ... ok\ntest send_control::tests::audio_buffer_growth_caps_at_configured_max_and_reset_clears_pressure ... ok\ntest send_control::tests::audio_buffer_ignores_clock_regression_without_false_rebuffer ... ok\ntest send_control::tests::audio_buffer_expands_on_gaps_and_shrinks_after_stability ... ok\ntest send_control::tests::audio_buffer_max_is_clamped_to_real_time_bounds ... ok\ntest send_control::tests::idle_snapshot_reflects_audio_pressure_without_video_state ... ok\ntest send_control::tests::video_controller_counts_coalesced_replacements_once_for_pressure ... ok\ntest send_control::tests::video_controller_continues_degrading_to_minimum_under_sustained_pressure ... ok\ntest send_control::tests::audio_buffer_stress_stays_within_realtime_bounds_under_jitter ... ok\ntest send_control::tests::video_controller_degrades_on_encoder_egress_pressure ... ok\ntest send_control::tests::audio_buffer_ignores_concurrent_stale_pushes_without_moving_last_push_backwards ... ok\ntest send_control::tests::video_controller_degrades_on_latency_pressure_without_coalescing ... ok\ntest send_control::tests::video_controller_degrades_on_repeated_coalescing_pressure ... ok\ntest send_control::tests::video_controller_degrades_when_disrupted_frame_ratio_exceeds_threshold ... ok\ntest send_control::tests::video_controller_ignores_bounded_one_frame_coalescing_pressure ... ok\ntest send_control::tests::video_controller_ignores_single_encoder_egress_sample ... ok\ntest send_control::tests::video_controller_keeps_requested_fps_when_adaptive_send_is_disabled ... ok\ntest send_control::tests::video_controller_preserves_pressure_reason_while_degraded_but_stable ... ok\ntest send_control::tests::video_controller_recovers_after_stable_windows ... ok\ntest send_control::tests::video_controller_recovers_from_minimum_in_two_stable_steps ... ok\ntest send_control::tests::video_controller_requires_a_full_stable_recovery_window ... ok\ntest send_control::tests::video_controller_waits_until_pressure_window_boundary ... ok\ntest send_control::tests::video_stats_effective_fps_uses_capture_time_bounds_for_out_of_order_records ... ok\ntest send_control::tests::video_stats_snapshot_counts_coalescing_drops_failures_and_effective_fps ... ok\ntest stats::tests::bitrate_computes_kbps_from_byte_delta ... ok\ntest send_control::tests::video_controller_stress_keeps_target_inside_configured_bounds ... ok\ntest stats::tests::bitrate_first_sample_is_zero ... ok\ntest stats::tests::bitrate_half_second_doubles_rate ... ok\ntest stats::tests::bitrate_rejects_backwards_time_and_bytes ... ok\ntest stats::tests::empty_stats_serialise_to_null_rtt_and_empty_arrays ... ok\ntest stats::tests::sanitize_audio_level_clamps ... ok\ntest send_control::tests::video_stats_handles_concurrent_recording_without_lost_counts ... ok\ntest stats::tests::sanitize_kbps_drops_nan_inf_negative_and_rounds ... ok\ntest stats::tests::full_stats_serialise_to_exact_contract_shape ... ok\ntest stats::tests::send_health_serialises_to_exact_contract_shape ... ok\ntest stats::tests::unit_conversions_seconds_to_ms ... ok\ntest stats::tests::video_inbound_omits_audio_level_audio_omits_fps ... ok\ntest texture_source::tests::dmabuf_desc_from_parts_populates_all_plane_arrays ... ok\ntest texture_source::tests::dmabuf_desc_from_parts_rejects_incomplete_native_inputs ... ok\ntest texture_source::tests::dmabuf_desc_from_parts_rejects_negative_fds ... ok\ntest texture_source::tests::drm_format_gate_accepts_obs_vkcapture_texture_formats ... ok\ntest texture_source::tests::dxgi_format_gate_accepts_8bit_rgba_bgra_only ... ok\ntest texture_source::tests::error_strings_are_stable ... ok\ntest texture_source::tests::probe_is_available_only_for_explicit_hardware_codecs ... ok\ntest texture_source::tests::should_attempt_dmabuf_is_sdk_gated_after_validation_capability ... ok\ntest texture_source::tests::should_attempt_falls_back_when_capability_unavailable ... ok\ntest texture_source::tests::should_attempt_validates_frame_when_capability_available ... ok\ntest texture_source::tests::validate_accepts_a_clean_bgra_texture ... ok\ntest texture_source::tests::validate_dmabuf_accepts_supported_formats_with_optional_uuid ... ok\ntest texture_source::tests::validate_dmabuf_rejects_invalid_planes ... ok\ntest texture_source::tests::validate_precedence_handle_before_dims_before_format ... ok\ntest texture_source::tests::validate_rejects_odd_zero_and_oversized_dims ... ok\ntest texture_source::tests::validate_rejects_unsupported_format ... ok\ntest texture_source::tests::validate_rejects_zero_handle_as_no_texture ... ok\ntest yuv::tests::bgra_black_white_grey_levels ... ok\ntest yuv::tests::bgra_golden_2x2_solid_colour_exact_bytes ... ok\ntest yuv::tests::bgra_golden_strided_2x2_skips_row_padding ... ok\ntest yuv::tests::bgra_plane_sizes ... ok\ntest yuv::tests::bgra_primaries_have_expected_chroma_signs ... ok\ntest yuv::tests::copy_nv12_planes_preserves_nv12_layout ... ok\ntest yuv::tests::copy_nv12_planes_rejects_short_buffers ... ok\ntest yuv::tests::copy_nv12_planes_respects_destination_stride ... ok\ntest yuv::tests::nv12_4x4_deinterleaves_two_chroma_columns ... ok\ntest yuv::tests::nv12_golden_4x2_packed_to_i420 ... ok\ntest yuv::tests::nv12_packed_2x2_deinterleaves ... ok\ntest yuv::tests::nv12_respects_row_padding ... ok\ntest yuv::tests::nv12_short_buffer_is_rejected ... ok\ntest yuv::tests::rejects_odd_or_tiny_dimensions ... ok\ntest yuv::tests::rgb_golden_2x2_solid_colour_matches_bgra_path ... ok\ntest yuv::tests::rgb_plane_sizes_and_levels ... ok\ntest yuv::tests::rgb_rejects_odd_dims_and_short_buffer ... ok\ntest engine::tests::pcm16_audio_frame_rejects_implausible_format_and_overlong_chunks ... ok\n\ntest result: ok. 140 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n\nrunning 6 tests\ntest video_frame_meta_matches_contract ... ok\ntest inbound_covers_subscribe_and_lifecycle ... ok\ntest outbound_covers_the_single_identity_publish_path ... ok\ntest inbound_fixtures_match_contract ... ok\ntest outbound_fixtures_match_contract ... ok\ntest e2ee_state_is_ok_on_the_wire ... ok\n\ntest result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n▶ webrtc-sender loader wrapper\n ✔ resolves the native addon filename for every desktop OS/arch target (1.069958ms)\n ✔ rejects unsupported platform/architecture pairs explicitly (0.887333ms)\n ✔ returns a native hardware encoder capability when the binding exports one (0.8905ms)\n ✔ delegates dropped video callback metrics through the VoiceEngine wrapper (0.151208ms)\n ✔ reports VoiceEngine feature capabilities from the wrapped native prototype (0.499375ms)\n ✔ delegates device microphone publish through the VoiceEngine wrapper (0.171625ms)\n ✔ delegates native camera devices and publish options through the VoiceEngine wrapper (0.164625ms)\n ✔ delegates screen-share simulcast selection through the VoiceEngine wrapper (0.157417ms)\n ✔ delegates connect options through the VoiceEngine wrapper (0.206292ms)\n ✔ delegates shared texture handles through the WebrtcPublisher wrapper (0.201958ms)\n ✔ treats shared texture publishing as unavailable for older publisher bindings (0.089625ms)\n ✔ delegates dma-buf frames through the WebrtcPublisher wrapper (0.266709ms)\n ✔ delegates shared texture handles through the VoiceEngine wrapper (0.126375ms)\n ✔ treats shared texture publishing as unavailable for older voice bindings (0.074041ms)\n ✔ treats dma-buf publishing as unavailable for older native bindings (0.104333ms)\n ✔ defaults dropped video callback metrics to zero for older native bindings (0.05725ms)\n ✔ delegates remote track subscription updates through the VoiceEngine wrapper (0.1255ms)\n ✔ ignores remote track subscription updates for older native bindings (0.072583ms)\n ✔ delegates data packets through the VoiceEngine wrapper (0.183833ms)\n ✔ rejects invalid data packet payloads in the wrapper (0.25775ms)\n ✔ returns an unavailable hardware encoder capability without a binding (0.070875ms)\n✔ webrtc-sender loader wrapper (7.269583ms)\n▶ livekit harness setup matrix\n ✔ maps all supported codec spellings to expected mime types (1.39ms)\n ✔ parses codec lists with fallback and rejects empty effective lists (1.192416ms)\n ✔ default non-strict config keeps the fast local live setup small (1.466167ms)\n ✔ strict config expands to multi-codec secondary-publisher scenarios (1.057417ms)\n ✔ stress knobs parse packet loss, resolution stability, audio gap, and video pattern (0.65175ms)\n ✔ stress knobs reject impossible send pacing configurations (3.341208ms)\n ✔ explicit secondary codec expectations override rotating defaults (0.570125ms)\n ✔ secondary screen codec aliases are accepted and conflicting aliases are rejected (0.955083ms)\n ✔ external token setup derives identities, requires matching tokens, and redacts reports (1.394917ms)\n ✔ report sanitization strips URL credentials, query strings, and fragments (0.676625ms)\n ✔ jwt subject and TCP target parsing catch malformed live setups early (0.34125ms)\n ✔ admin token has the room-scoped grant needed for server publishing validation (0.463875ms)\n ✔ scenario room names are stable and bounded for generated-token suites (0.110917ms)\n ✔ strict report checks fail when the strict window has too few samples to measure gaps (90.970125ms)\n ✔ strict report checks use only samples inside the measured window (6.760833ms)\n ✔ strict packet loss gate uses loss deltas inside the strict window (12.074166ms)\n ✔ server publishing checks cover primary and secondary screen, audio, and camera tracks (0.618458ms)\n ✔ server publishing checks fail closed on missing server tracks (0.280125ms)\n ✔ server publishing checks reject wrong participant identity (0.2235ms)\n ✔ server publishing checks reject wrong kind, source, and screen mime (0.254208ms)\n ✔ server publishing checks reject muted or missing mute state on expected tracks (0.213625ms)\n ✔ server publishing checks handle protobuf numeric enums without accepting data tracks as video (0.195584ms)\n ✔ server publishing checks fail closed on missing secondary, audio, camera, and screen-audio tracks (0.208791ms)\n ✔ strict server publishing validation does not pass open on Twirp errors (0.247834ms)\n✔ livekit harness setup matrix (127.168458ms)\nℹ tests 45\nℹ suites 2\nℹ pass 45\nℹ fail 0\nℹ cancelled 0\nℹ skipped 0\nℹ todo 0\nℹ duration_ms 522.131583\n", + "stderrTail": "warning: unused import: `enum_dispatch`\n --> vendor/libwebrtc/src/audio_source.rs:15:13\n |\n15 | use crate::{enum_dispatch, imp::audio_source as imp_as};\n | ^^^^^^^^^^^^^\n |\n = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default\n\nwarning: unnecessary `unsafe` block\n --> vendor/libwebrtc/src/native/apm.rs:32:25\n |\n32 | sys_handle: unsafe {\n | ^^^^^^ unnecessary `unsafe` block\n |\n = note: `#[warn(unused_unsafe)]` (part of `#[warn(unused)]`) on by default\n\nwarning: hiding a lifetime that's elided elsewhere is confusing\n --> vendor/libwebrtc/src/native/audio_mixer.rs:30:34\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option;\n | ^^^^^ the lifetime is elided here ^^^^^^^^^^ the same lifetime is hidden here\n |\n = help: the same lifetime is referred to in inconsistent ways, making the signature confusing\n = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default\nhelp: use `'_` for type paths\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option>;\n | ++++\n\nwarning: `libwebrtc` (lib) generated 3 warnings (run `cargo fix --lib -p libwebrtc` to apply 2 suggestions)\n Finished `test` profile [unoptimized + debuginfo] target(s) in 0.16s\n Running unittests src/lib.rs (target/debug/deps/fluxer_webrtc_sender-4fb413a724225319)\n Running tests/event_contract.rs (target/debug/deps/event_contract-dc15840aabc01f6c)\n Doc-tests fluxer_webrtc_sender\n" + }, + { + "name": "webrtc-sender-livekit-harness", + "command": "pnpm --dir native/webrtc-sender test:livekit", + "category": "livekit", + "status": "fail", + "code": 1, + "signal": null, + "startedAt": "2026-06-06T22:15:57.734Z", + "endedAt": "2026-06-06T22:16:28.858Z", + "durationMs": 31124, + "stdoutTail": "../linux-audio-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-evdev |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-notifications |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-portals |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../system-hunspell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n\n> @fluxer/webrtc-sender@0.0.0 test:livekit /Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender\n> node scripts/livekit-harness.mjs\n\n[livekit-harness] connecting to ws://127.0.0.1:7880 room=fluxer-webrtc-harness-84586-8f492568-efae-42a1-a344-5a0c9599fa8d\n ELIFECYCLE  Command failed with exit code 1.\n WARN  Local package.json exists, but node_modules missing, did you mean to install?\n", + "stderrTail": "[livekit-harness] FAIL: Error: connect timed out after 30000ms\n at file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1346:9\n at async runHarness (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1773:3)\n at async main (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:2025:18)\n at async file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:2058:19\n" + } + ] +} diff --git a/fluxer_desktop/native-media-reports/2026-06-06T22-17-15Z-codex-livekit-proxy.json b/fluxer_desktop/native-media-reports/2026-06-06T22-17-15Z-codex-livekit-proxy.json new file mode 100644 index 0000000..45b967b --- /dev/null +++ b/fluxer_desktop/native-media-reports/2026-06-06T22-17-15Z-codex-livekit-proxy.json @@ -0,0 +1,274 @@ +{ + "status": "pass", + "startedAt": "2026-06-06T22:17:16.198Z", + "endedAt": "2026-06-06T22:17:18.067Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:8088/livekit", + "serverApiUrl": "http://127.0.0.1:8088", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-84689-a15db5c1-00f9-4cc5-8b1a-e68759b5e380", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-84689", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-84689", + "subscriberIdentity": "fluxer-native-subscriber-84689", + "required": false, + "strict": false, + "timeoutMs": 30000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 0, + "width": 320, + "height": 180, + "expectedWidth": 320, + "expectedHeight": 180, + "fps": 15, + "minFpsRatio": 0, + "maxFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": null, + "requireStableResolution": false, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": null, + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": false, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": true, + "secondaryPublisherScreenSimulcast": true, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": false, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 400000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": false, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": false, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/2026-06-06T22-17-15Z-codex-livekit-proxy.json", + "verbose": false, + "scenarioName": "codec-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 320x180", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + } + ], + "skippedReason": null, + "error": null, + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 13, + "strictAudioFrames": 0, + "strictVideoFrames": 0, + "videoCallbacks": 13, + "videoBytes": 1123200, + "receivedFps": 0, + "requiredFps": null, + "maxVideoFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 390, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 5, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": {}, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 15, + "startedAtMs": 1780784237058, + "stoppedAtMs": null, + "framesPushed": 16, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 69, + "lastPushAtMs": 1780784238060, + "elapsedMs": 1018, + "producedFps": 15.72 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": null, + "error": null, + "participants": [] + }, + "lastVideoMeta": { + "participantSid": "PA_NicBnT5mxvvm", + "trackSid": "TR_VSrvFY4wSg7eLt", + "width": 320, + "height": 180, + "timestampUs": 335522961000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_VSrvFY4wSg7eLt", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 0, + "packetsLost": 0, + "fps": 15, + "width": 320, + "height": 180, + "sourceWidth": 320, + "sourceHeight": 180, + "targetBitrateKbps": 400, + "configuredFps": 15, + "targetFps": 15, + "effectiveFps": 14.6, + "framesProduced": 14, + "framesAccepted": 14, + "framesDropped": 0, + "framesCoalesced": 0, + "framesCaptured": 14, + "captureFailures": 0, + "maxQueueAgeMs": 26, + "maxPushLatencyMs": 26, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable", + "codec": "video/VP8" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 14, + "outgoingVideoFramesAccepted": 14, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 14, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 14.56, + "outgoingVideoTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 26, + "outgoingVideoMaxPushLatencyMs": 26, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 2, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_NicBnT5mxvvm", + "trackSid": "TR_VSrvFY4wSg7eLt", + "kind": "video", + "bitrateKbps": 0, + "packetsLost": 5, + "jitterMs": 4, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/2026-06-06T22-17-25Z-codex-host-smoke-proxy/livekit-harness.json b/fluxer_desktop/native-media-reports/2026-06-06T22-17-25Z-codex-host-smoke-proxy/livekit-harness.json new file mode 100644 index 0000000..88d097c --- /dev/null +++ b/fluxer_desktop/native-media-reports/2026-06-06T22-17-25Z-codex-host-smoke-proxy/livekit-harness.json @@ -0,0 +1,321 @@ +{ + "status": "pass", + "startedAt": "2026-06-06T22:17:32.639Z", + "endedAt": "2026-06-06T22:17:34.704Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:8088/livekit", + "serverApiUrl": "http://127.0.0.1:8088", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-84848-43854e34-d3a9-4e33-bf45-b7ef3611083a", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-84848", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-84848", + "subscriberIdentity": "fluxer-native-subscriber-84848", + "required": false, + "strict": false, + "timeoutMs": 30000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 0, + "width": 320, + "height": 180, + "expectedWidth": 320, + "expectedHeight": 180, + "fps": 15, + "minFpsRatio": 0, + "maxFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": null, + "requireStableResolution": false, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": null, + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": false, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": true, + "secondaryPublisherScreenSimulcast": true, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 400000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": false, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/2026-06-06T22-17-25Z-codex-host-smoke-proxy/livekit-harness.json", + "verbose": false, + "scenarioName": "codec-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 320x180", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 3", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 1", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 3", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + } + ], + "skippedReason": null, + "error": null, + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 72, + "inboundVideoFrames": 13, + "strictAudioFrames": 0, + "strictVideoFrames": 0, + "videoCallbacks": 13, + "videoBytes": 1123200, + "receivedFps": 0, + "requiredFps": null, + "maxVideoFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 184, + "maxAudioFrameGapOverallMs": 102, + "avDriftMs": 0, + "maxObservedPacketLoss": 3, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": {}, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 15, + "startedAtMs": 1780784253698, + "stoppedAtMs": null, + "framesPushed": 16, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 68, + "lastPushAtMs": 1780784254699, + "elapsedMs": 1014, + "producedFps": 15.78 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": null, + "error": null, + "participants": [] + }, + "lastVideoMeta": { + "participantSid": "PA_HwsqmPnPxvGD", + "trackSid": "TR_VS8RxgcaoR5kbz", + "width": 320, + "height": 180, + "timestampUs": 335539594000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_VS8RxgcaoR5kbz", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 0, + "packetsLost": 0, + "fps": 16, + "width": 320, + "height": 180, + "sourceWidth": 320, + "sourceHeight": 180, + "targetBitrateKbps": 400, + "configuredFps": 15, + "targetFps": 15, + "effectiveFps": 14.6, + "framesProduced": 6, + "framesAccepted": 6, + "framesDropped": 0, + "framesCoalesced": 0, + "framesCaptured": 6, + "captureFailures": 0, + "maxQueueAgeMs": 8, + "maxPushLatencyMs": 8, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable", + "codec": "video/VP8" + }, + { + "trackSid": "TR_AMBPL59S6qUeEf", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 0, + "packetsLost": 0, + "audioLevel": 0.305, + "codec": "audio/opus" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 6, + "outgoingVideoFramesAccepted": 6, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 6, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 14.62, + "outgoingVideoTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 8, + "outgoingVideoMaxPushLatencyMs": 8, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 23, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_HwsqmPnPxvGD", + "trackSid": "TR_AMBPL59S6qUeEf", + "kind": "audio", + "bitrateKbps": 0, + "packetsLost": 0, + "jitterMs": 1, + "audioLevel": 0.315, + "codec": "audio/opus" + }, + { + "participantSid": "PA_HwsqmPnPxvGD", + "trackSid": "TR_VS8RxgcaoR5kbz", + "kind": "video", + "bitrateKbps": 0, + "packetsLost": 3, + "jitterMs": 4, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/2026-06-06T22-17-25Z-codex-host-smoke-proxy/summary.json b/fluxer_desktop/native-media-reports/2026-06-06T22-17-25Z-codex-host-smoke-proxy/summary.json new file mode 100644 index 0000000..3cfe7b4 --- /dev/null +++ b/fluxer_desktop/native-media-reports/2026-06-06T22-17-25Z-codex-host-smoke-proxy/summary.json @@ -0,0 +1,227 @@ +{ + "status": "pass", + "mode": "smoke", + "strict": false, + "platform": "darwin", + "arch": "arm64", + "hostname": "Hampuss-Mac-Studio.local", + "startedAt": "2026-06-06T22:17:25.424Z", + "endedAt": "2026-06-06T22:17:42.978Z", + "reportDir": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/2026-06-06T22-17-25Z-codex-host-smoke-proxy", + "gates": [ + { + "name": "mode-valid", + "status": "pass", + "mode": "smoke" + }, + { + "name": "documentation-current", + "status": "pass", + "missing": [] + }, + { + "name": "strict-prerequisites", + "status": "skip", + "reason": "smoke mode" + }, + { + "name": "strict-command-plan", + "status": "skip", + "reason": "smoke mode" + } + ], + "plannedCommands": [ + { + "name": "main-process-native-media-unit-tests", + "command": "node --test src/main/WebrtcScreenPublish.test.mjs src/main/NativeVoiceEngine.test.mjs src/main/NativeScreenCapture.test.mjs", + "category": "shared", + "cwd": "." + }, + { + "name": "desktop-typecheck", + "command": "pnpm typecheck", + "category": "shared", + "cwd": "." + }, + { + "name": "webrtc-sender-build", + "command": "pnpm --dir native/webrtc-sender build", + "category": "native-sender", + "cwd": "." + }, + { + "name": "webrtc-sender-tests", + "command": "pnpm --dir native/webrtc-sender test", + "category": "native-sender", + "cwd": "." + }, + { + "name": "webrtc-sender-livekit-harness", + "command": "pnpm --dir native/webrtc-sender test:livekit", + "category": "livekit", + "cwd": "." + }, + { + "name": "mac-screen-capture-build", + "command": "pnpm --dir native/mac-screen-capture build", + "category": "platform-native", + "cwd": "." + }, + { + "name": "mac-screen-capture-tests", + "command": "pnpm --dir native/mac-screen-capture test", + "category": "platform-native", + "cwd": "." + }, + { + "name": "mac-app-audio-build", + "command": "pnpm --dir native/mac-app-audio build", + "category": "platform-native", + "cwd": "." + }, + { + "name": "mac-app-audio-rust-tests", + "command": "pnpm --dir native/mac-app-audio test:cargo", + "category": "platform-native", + "cwd": "." + }, + { + "name": "mac-app-audio-js-tests", + "command": "'/Users/hampus/Development/fluxer/node_modules/.pnpm/node_modules/.bin/vitest' run", + "category": "platform-native", + "cwd": "native/mac-app-audio" + } + ], + "commands": [ + { + "name": "main-process-native-media-unit-tests", + "command": "node --test src/main/WebrtcScreenPublish.test.mjs src/main/NativeVoiceEngine.test.mjs src/main/NativeScreenCapture.test.mjs", + "category": "shared", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-06T22:17:25.425Z", + "endedAt": "2026-06-06T22:17:25.608Z", + "durationMs": 183, + "stdoutTail": "▶ NativeScreenCapture DMA-BUF metadata validation\n ✔ accepts valid DMA-BUF metadata and pads native plane arrays (2.167459ms)\n ✔ keeps valid handles when optional device UUID metadata is absent or malformed (0.439666ms)\n ✔ rejects invalid plane counts, FDs, strides, and format metadata (0.458916ms)\n✔ NativeScreenCapture DMA-BUF metadata validation (3.515292ms)\n▶ NativeScreenCapture source identity and capability reporting\n ✔ normalizes display and window sources without changing source ids (0.661875ms)\n ✔ starts display and window captures with exact source id and kind and reports diagnostics (1.226708ms)\n ✔ forwards frames to renderer even when a native frame tap is installed (0.477666ms)\n ✔ reports macOS and Linux display/window capture capabilities from platform backends (1.341208ms)\n✔ NativeScreenCapture source identity and capability reporting (3.829875ms)\n▶ NativeVoiceEngine screen frame routing\n ✔ pushes the initial capture frame after publishing screen share (2.384041ms)\n ✔ prefers Windows shared texture handles over DMA-BUF and CPU frames (0.56125ms)\n ✔ uses Linux DMA-BUF handles before CPU conversion (0.49475ms)\n ✔ falls back to CPU frames when GPU paths are unavailable or rejected (0.461792ms)\n ✔ keeps screen-share audio published when screen video is unpublished (0.516583ms)\n ✔ replaces screen capture taps without releasing screen-share audio or duplicating priority (1.04175ms)\n ✔ forwards watch and unwatch operations with source, enablement, and quality intact (0.860792ms)\n ✔ reports VideoToolbox hardware capability from the native addon (0.46825ms)\n ✔ reports native voice engine capabilities from the native addon (0.402ms)\n ✔ maps native addon microphone not-connected errors to the typed operation code (0.542958ms)\n✔ NativeVoiceEngine screen frame routing (8.310334ms)\n▶ WebrtcScreenPublish native frame routing\n ✔ prefers Windows shared texture handles over DMA-BUF and CPU buffers (2.040083ms)\n ✔ uses Linux DMA-BUF handles before CPU conversion (0.431291ms)\n ✔ falls back to CPU frames when GPU paths are unavailable or rejected (0.405292ms)\n ✔ replaces an existing publisher before routing frames for the same capture id (0.398542ms)\n✔ WebrtcScreenPublish native frame routing (3.742875ms)\nℹ tests 21\nℹ suites 4\nℹ pass 21\nℹ fail 0\nℹ cancelled 0\nℹ skipped 0\nℹ todo 0\nℹ duration_ms 120.183542\n", + "stderrTail": "" + }, + { + "name": "desktop-typecheck", + "command": "pnpm typecheck", + "category": "shared", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-06T22:17:25.609Z", + "endedAt": "2026-06-06T22:17:26.719Z", + "durationMs": 1110, + "stdoutTail": "native/linux-audio-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/linux-evdev |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/linux-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/linux-notifications |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/linux-portals |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/linux-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/system-hunspell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\nnative/windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n\n> fluxer_desktop@0.0.0 typecheck /Users/hampus/Development/fluxer/fluxer_desktop\n> tsgo --noEmit\n\n", + "stderrTail": "" + }, + { + "name": "webrtc-sender-build", + "command": "pnpm --dir native/webrtc-sender build", + "category": "native-sender", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-06T22:17:26.719Z", + "endedAt": "2026-06-06T22:17:28.890Z", + "durationMs": 2171, + "stdoutTail": "../linux-audio-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-evdev |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-notifications |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-portals |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../system-hunspell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n\n> @fluxer/webrtc-sender@0.0.0 build /Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender\n> cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon\n\n[buildRustNodeAddon] cwd=/Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender\n+ /Users/hampus/.rustup/toolchains/1.93.0-aarch64-apple-darwin/bin/cargo build --release --target aarch64-apple-darwin --manifest-path Cargo.toml --features camera-native\n+ codesign --force --sign - /Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/webrtc-sender.darwin-arm64.node\n", + "stderrTail": "warning: unused import: `enum_dispatch`\n --> vendor/libwebrtc/src/audio_source.rs:15:13\n |\n15 | use crate::{enum_dispatch, imp::audio_source as imp_as};\n | ^^^^^^^^^^^^^\n |\n = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default\n\nwarning: unnecessary `unsafe` block\n --> vendor/libwebrtc/src/native/apm.rs:32:25\n |\n32 | sys_handle: unsafe {\n | ^^^^^^ unnecessary `unsafe` block\n |\n = note: `#[warn(unused_unsafe)]` (part of `#[warn(unused)]`) on by default\n\nwarning: hiding a lifetime that's elided elsewhere is confusing\n --> vendor/libwebrtc/src/native/audio_mixer.rs:30:34\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option;\n | ^^^^^ the lifetime is elided here ^^^^^^^^^^ the same lifetime is hidden here\n |\n = help: the same lifetime is referred to in inconsistent ways, making the signature confusing\n = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default\nhelp: use `'_` for type paths\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option>;\n | ++++\n\nwarning: `libwebrtc` (lib) generated 3 warnings (run `cargo fix --lib -p libwebrtc` to apply 2 suggestions)\n Finished `release` profile [optimized] target(s) in 0.16s\n/Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/webrtc-sender.darwin-arm64.node: replacing existing signature\n" + }, + { + "name": "webrtc-sender-tests", + "command": "pnpm --dir native/webrtc-sender test", + "category": "native-sender", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-06T22:17:28.890Z", + "endedAt": "2026-06-06T22:17:31.695Z", + "durationMs": 2805, + "stdoutTail": "eported_available ... ok\ntest hardware_encoder::tests::publish_codec_runtime_gate_only_requires_hardware_for_h265 ... ok\ntest hardware_encoder::tests::reports_available_nvenc_when_compiled_and_runtime_ready ... ok\ntest hardware_encoder::tests::reports_available_videotoolbox_with_h264_and_h265 ... ok\ntest hardware_encoder::tests::reports_driver_sdk_incompatibility_before_encode_session ... ok\ntest hardware_encoder::tests::reports_h264_only_when_hevc_guid_is_absent ... ok\ntest hardware_encoder::tests::reports_missing_runtime_prerequisites_when_compiled ... ok\ntest hardware_encoder::tests::reports_missing_runtime_prerequisites_when_probe_is_absent ... ok\ntest hardware_encoder::tests::reports_no_cuda_devices_using_obs_style_reason ... ok\ntest hardware_encoder::tests::reports_no_supported_codecs_when_guid_query_finds_no_fluxer_codecs ... ok\ntest hardware_encoder::tests::reports_not_compiled_before_runtime_missing ... ok\ntest hardware_encoder::tests::reports_unsupported_platforms_clearly ... ok\ntest hardware_encoder::tests::reports_videotoolbox_h264_only_when_hevc_session_is_unavailable ... ok\ntest hardware_encoder::tests::reports_videotoolbox_unavailable_with_status_details ... ok\ntest hardware_encoder::tests::reports_windows_nvenc_publish_backend_not_compiled ... ok\ntest hardware_encoder::tests::reports_windows_nvenc_runtime_dll_names_when_missing ... ok\ntest hardware_encoder::tests::reports_windows_nvenc_without_linux_zero_copy_inputs ... ok\ntest publisher::tests::i420_plane_copy_fills_libwebrtc_buffer ... ok\ntest publisher::tests::parse_codec_maps_known_names ... ok\ntest send_control::tests::audio_buffer_growth_caps_at_configured_max_and_reset_clears_pressure ... ok\ntest send_control::tests::audio_buffer_ignores_clock_regression_without_false_rebuffer ... ok\ntest send_control::tests::audio_buffer_expands_on_gaps_and_shrinks_after_stability ... ok\ntest send_control::tests::audio_buffer_max_is_clamped_to_real_time_bounds ... ok\ntest send_control::tests::video_controller_continues_degrading_to_minimum_under_sustained_pressure ... ok\ntest send_control::tests::idle_snapshot_reflects_audio_pressure_without_video_state ... ok\ntest send_control::tests::video_controller_counts_coalesced_replacements_once_for_pressure ... ok\ntest send_control::tests::audio_buffer_stress_stays_within_realtime_bounds_under_jitter ... ok\ntest send_control::tests::audio_buffer_ignores_concurrent_stale_pushes_without_moving_last_push_backwards ... ok\ntest send_control::tests::video_controller_degrades_on_encoder_egress_pressure ... ok\ntest send_control::tests::video_controller_degrades_on_latency_pressure_without_coalescing ... ok\ntest send_control::tests::video_controller_degrades_on_repeated_coalescing_pressure ... ok\ntest send_control::tests::video_controller_degrades_when_disrupted_frame_ratio_exceeds_threshold ... ok\ntest send_control::tests::video_controller_ignores_bounded_one_frame_coalescing_pressure ... ok\ntest send_control::tests::video_controller_ignores_single_encoder_egress_sample ... ok\ntest send_control::tests::video_controller_keeps_requested_fps_when_adaptive_send_is_disabled ... ok\ntest send_control::tests::video_controller_preserves_pressure_reason_while_degraded_but_stable ... ok\ntest send_control::tests::video_controller_recovers_after_stable_windows ... ok\ntest send_control::tests::video_controller_recovers_from_minimum_in_two_stable_steps ... ok\ntest send_control::tests::video_controller_requires_a_full_stable_recovery_window ... ok\ntest send_control::tests::video_controller_waits_until_pressure_window_boundary ... ok\ntest send_control::tests::video_stats_effective_fps_uses_capture_time_bounds_for_out_of_order_records ... ok\ntest send_control::tests::video_stats_snapshot_counts_coalescing_drops_failures_and_effective_fps ... ok\ntest stats::tests::bitrate_computes_kbps_from_byte_delta ... ok\ntest stats::tests::bitrate_first_sample_is_zero ... ok\ntest send_control::tests::video_controller_stress_keeps_target_inside_configured_bounds ... ok\ntest stats::tests::bitrate_half_second_doubles_rate ... ok\ntest stats::tests::bitrate_rejects_backwards_time_and_bytes ... ok\ntest stats::tests::empty_stats_serialise_to_null_rtt_and_empty_arrays ... ok\ntest stats::tests::sanitize_audio_level_clamps ... ok\ntest stats::tests::sanitize_kbps_drops_nan_inf_negative_and_rounds ... ok\ntest stats::tests::full_stats_serialise_to_exact_contract_shape ... ok\ntest stats::tests::send_health_serialises_to_exact_contract_shape ... ok\ntest stats::tests::unit_conversions_seconds_to_ms ... ok\ntest send_control::tests::video_stats_handles_concurrent_recording_without_lost_counts ... ok\ntest stats::tests::video_inbound_omits_audio_level_audio_omits_fps ... ok\ntest texture_source::tests::dmabuf_desc_from_parts_populates_all_plane_arrays ... ok\ntest texture_source::tests::dmabuf_desc_from_parts_rejects_incomplete_native_inputs ... ok\ntest texture_source::tests::dmabuf_desc_from_parts_rejects_negative_fds ... ok\ntest texture_source::tests::drm_format_gate_accepts_obs_vkcapture_texture_formats ... ok\ntest texture_source::tests::dxgi_format_gate_accepts_8bit_rgba_bgra_only ... ok\ntest texture_source::tests::error_strings_are_stable ... ok\ntest texture_source::tests::probe_is_available_only_for_explicit_hardware_codecs ... ok\ntest texture_source::tests::should_attempt_dmabuf_is_sdk_gated_after_validation_capability ... ok\ntest texture_source::tests::should_attempt_falls_back_when_capability_unavailable ... ok\ntest texture_source::tests::should_attempt_validates_frame_when_capability_available ... ok\ntest texture_source::tests::validate_accepts_a_clean_bgra_texture ... ok\ntest texture_source::tests::validate_dmabuf_accepts_supported_formats_with_optional_uuid ... ok\ntest texture_source::tests::validate_dmabuf_rejects_invalid_planes ... ok\ntest texture_source::tests::validate_precedence_handle_before_dims_before_format ... ok\ntest texture_source::tests::validate_rejects_odd_zero_and_oversized_dims ... ok\ntest texture_source::tests::validate_rejects_unsupported_format ... ok\ntest texture_source::tests::validate_rejects_zero_handle_as_no_texture ... ok\ntest yuv::tests::bgra_black_white_grey_levels ... ok\ntest yuv::tests::bgra_golden_2x2_solid_colour_exact_bytes ... ok\ntest yuv::tests::bgra_golden_strided_2x2_skips_row_padding ... ok\ntest yuv::tests::bgra_plane_sizes ... ok\ntest yuv::tests::bgra_primaries_have_expected_chroma_signs ... ok\ntest yuv::tests::copy_nv12_planes_preserves_nv12_layout ... ok\ntest yuv::tests::copy_nv12_planes_rejects_short_buffers ... ok\ntest yuv::tests::copy_nv12_planes_respects_destination_stride ... ok\ntest yuv::tests::nv12_4x4_deinterleaves_two_chroma_columns ... ok\ntest yuv::tests::nv12_golden_4x2_packed_to_i420 ... ok\ntest yuv::tests::nv12_packed_2x2_deinterleaves ... ok\ntest yuv::tests::nv12_respects_row_padding ... ok\ntest yuv::tests::nv12_short_buffer_is_rejected ... ok\ntest yuv::tests::rejects_odd_or_tiny_dimensions ... ok\ntest yuv::tests::rgb_golden_2x2_solid_colour_matches_bgra_path ... ok\ntest yuv::tests::rgb_plane_sizes_and_levels ... ok\ntest yuv::tests::rgb_rejects_odd_dims_and_short_buffer ... ok\ntest engine::tests::pcm16_audio_frame_rejects_implausible_format_and_overlong_chunks ... ok\n\ntest result: ok. 140 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n\nrunning 6 tests\ntest video_frame_meta_matches_contract ... ok\ntest inbound_covers_subscribe_and_lifecycle ... ok\ntest outbound_covers_the_single_identity_publish_path ... ok\ntest outbound_fixtures_match_contract ... ok\ntest inbound_fixtures_match_contract ... ok\ntest e2ee_state_is_ok_on_the_wire ... ok\n\ntest result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n▶ webrtc-sender loader wrapper\n ✔ resolves the native addon filename for every desktop OS/arch target (0.937291ms)\n ✔ rejects unsupported platform/architecture pairs explicitly (0.3075ms)\n ✔ returns a native hardware encoder capability when the binding exports one (0.9935ms)\n ✔ delegates dropped video callback metrics through the VoiceEngine wrapper (0.163167ms)\n ✔ reports VoiceEngine feature capabilities from the wrapped native prototype (0.5065ms)\n ✔ delegates device microphone publish through the VoiceEngine wrapper (0.174917ms)\n ✔ delegates native camera devices and publish options through the VoiceEngine wrapper (0.172875ms)\n ✔ delegates screen-share simulcast selection through the VoiceEngine wrapper (0.163833ms)\n ✔ delegates connect options through the VoiceEngine wrapper (0.211375ms)\n ✔ delegates shared texture handles through the WebrtcPublisher wrapper (0.22075ms)\n ✔ treats shared texture publishing as unavailable for older publisher bindings (0.095625ms)\n ✔ delegates dma-buf frames through the WebrtcPublisher wrapper (0.276959ms)\n ✔ delegates shared texture handles through the VoiceEngine wrapper (0.132ms)\n ✔ treats shared texture publishing as unavailable for older voice bindings (0.073083ms)\n ✔ treats dma-buf publishing as unavailable for older native bindings (0.110125ms)\n ✔ defaults dropped video callback metrics to zero for older native bindings (0.0585ms)\n ✔ delegates remote track subscription updates through the VoiceEngine wrapper (0.136583ms)\n ✔ ignores remote track subscription updates for older native bindings (0.088709ms)\n ✔ delegates data packets through the VoiceEngine wrapper (0.179834ms)\n ✔ rejects invalid data packet payloads in the wrapper (0.238083ms)\n ✔ returns an unavailable hardware encoder capability without a binding (0.072ms)\n✔ webrtc-sender loader wrapper (7.268042ms)\n▶ livekit harness setup matrix\n ✔ maps all supported codec spellings to expected mime types (1.146667ms)\n ✔ parses codec lists with fallback and rejects empty effective lists (1.1805ms)\n ✔ default non-strict config keeps the fast local live setup small (1.487ms)\n ✔ strict config expands to multi-codec secondary-publisher scenarios (1.038875ms)\n ✔ stress knobs parse packet loss, resolution stability, audio gap, and video pattern (0.635208ms)\n ✔ stress knobs reject impossible send pacing configurations (3.125583ms)\n ✔ explicit secondary codec expectations override rotating defaults (0.484584ms)\n ✔ secondary screen codec aliases are accepted and conflicting aliases are rejected (0.924208ms)\n ✔ external token setup derives identities, requires matching tokens, and redacts reports (1.383958ms)\n ✔ report sanitization strips URL credentials, query strings, and fragments (0.6515ms)\n ✔ jwt subject and TCP target parsing catch malformed live setups early (0.336458ms)\n ✔ admin token has the room-scoped grant needed for server publishing validation (0.468333ms)\n ✔ scenario room names are stable and bounded for generated-token suites (0.1065ms)\n ✔ strict report checks fail when the strict window has too few samples to measure gaps (89.199292ms)\n ✔ strict report checks use only samples inside the measured window (5.439292ms)\n ✔ strict packet loss gate uses loss deltas inside the strict window (10.6855ms)\n ✔ server publishing checks cover primary and secondary screen, audio, and camera tracks (0.5675ms)\n ✔ server publishing checks fail closed on missing server tracks (0.275125ms)\n ✔ server publishing checks reject wrong participant identity (0.2045ms)\n ✔ server publishing checks reject wrong kind, source, and screen mime (0.258042ms)\n ✔ server publishing checks reject muted or missing mute state on expected tracks (0.200416ms)\n ✔ server publishing checks handle protobuf numeric enums without accepting data tracks as video (0.194ms)\n ✔ server publishing checks fail closed on missing secondary, audio, camera, and screen-audio tracks (0.203708ms)\n ✔ strict server publishing validation does not pass open on Twirp errors (0.207541ms)\n✔ livekit harness setup matrix (121.754875ms)\nℹ tests 45\nℹ suites 2\nℹ pass 45\nℹ fail 0\nℹ cancelled 0\nℹ skipped 0\nℹ todo 0\nℹ duration_ms 510.566208\n", + "stderrTail": "warning: unused import: `enum_dispatch`\n --> vendor/libwebrtc/src/audio_source.rs:15:13\n |\n15 | use crate::{enum_dispatch, imp::audio_source as imp_as};\n | ^^^^^^^^^^^^^\n |\n = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default\n\nwarning: unnecessary `unsafe` block\n --> vendor/libwebrtc/src/native/apm.rs:32:25\n |\n32 | sys_handle: unsafe {\n | ^^^^^^ unnecessary `unsafe` block\n |\n = note: `#[warn(unused_unsafe)]` (part of `#[warn(unused)]`) on by default\n\nwarning: hiding a lifetime that's elided elsewhere is confusing\n --> vendor/libwebrtc/src/native/audio_mixer.rs:30:34\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option;\n | ^^^^^ the lifetime is elided here ^^^^^^^^^^ the same lifetime is hidden here\n |\n = help: the same lifetime is referred to in inconsistent ways, making the signature confusing\n = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default\nhelp: use `'_` for type paths\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option>;\n | ++++\n\nwarning: `libwebrtc` (lib) generated 3 warnings (run `cargo fix --lib -p libwebrtc` to apply 2 suggestions)\n Finished `test` profile [unoptimized + debuginfo] target(s) in 0.15s\n Running unittests src/lib.rs (target/debug/deps/fluxer_webrtc_sender-4fb413a724225319)\n Running tests/event_contract.rs (target/debug/deps/event_contract-dc15840aabc01f6c)\n Doc-tests fluxer_webrtc_sender\n" + }, + { + "name": "webrtc-sender-livekit-harness", + "command": "pnpm --dir native/webrtc-sender test:livekit", + "category": "livekit", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-06T22:17:31.695Z", + "endedAt": "2026-06-06T22:17:34.808Z", + "durationMs": 3113, + "stdoutTail": "../linux-audio-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-evdev |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-notifications |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-portals |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../system-hunspell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n\n> @fluxer/webrtc-sender@0.0.0 test:livekit /Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender\n> node scripts/livekit-harness.mjs\n\n[livekit-harness] connecting to ws://127.0.0.1:8088/livekit room=fluxer-webrtc-harness-84848-43854e34-d3a9-4e33-bf45-b7ef3611083a\n[livekit-harness] PASS: 19 checks, inboundAudioFrames=72, inboundVideoFrames=13, videoCallbacks=13, receivedFps=0, producedFps=15.78, maxVideoFrameGapMs=0, maxAudioFrameGapMs=0, maxObservedPacketLoss=3, maxObservedPacketLossDelta=0, resolutionMismatches=0, droppedVideoFrameCallbacks=0\n", + "stderrTail": "" + }, + { + "name": "mac-screen-capture-build", + "command": "pnpm --dir native/mac-screen-capture build", + "category": "platform-native", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-06T22:17:34.808Z", + "endedAt": "2026-06-06T22:17:36.805Z", + "durationMs": 1997, + "stdoutTail": "../linux-audio-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-evdev |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-notifications |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-portals |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../system-hunspell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n\n> @fluxer/mac-screen-capture@0.0.0 build /Users/hampus/Development/fluxer/fluxer_desktop/native/mac-screen-capture\n> cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon\n\n[buildRustNodeAddon] cwd=/Users/hampus/Development/fluxer/fluxer_desktop/native/mac-screen-capture\n+ /Users/hampus/.rustup/toolchains/1.93.0-aarch64-apple-darwin/bin/cargo build --release --target aarch64-apple-darwin --manifest-path Cargo.toml\n+ codesign --force --sign - /Users/hampus/Development/fluxer/fluxer_desktop/native/mac-screen-capture/mac-screen-capture.darwin-arm64.node\n", + "stderrTail": " Finished `release` profile [optimized] target(s) in 0.04s\n/Users/hampus/Development/fluxer/fluxer_desktop/native/mac-screen-capture/mac-screen-capture.darwin-arm64.node: replacing existing signature\n" + }, + { + "name": "mac-screen-capture-tests", + "command": "pnpm --dir native/mac-screen-capture test", + "category": "platform-native", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-06T22:17:36.805Z", + "endedAt": "2026-06-06T22:17:39.173Z", + "durationMs": 2368, + "stdoutTail": "../linux-audio-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-evdev |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-notifications |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-portals |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../system-hunspell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n\n> @fluxer/mac-screen-capture@0.0.0 test /Users/hampus/Development/fluxer/fluxer_desktop/native/mac-screen-capture\n> pnpm test:cargo && node --test index.test.mjs\n\n../linux-audio-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-evdev |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-notifications |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-portals |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../system-hunspell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n\n> @fluxer/mac-screen-capture@0.0.0 test:cargo /Users/hampus/Development/fluxer/fluxer_desktop/native/mac-screen-capture\n> cargo test --manifest-path Cargo.toml\n\n\nrunning 15 tests\ntest nv12_packing::tests::pack_nv12_rejects_short_source ... ok\ntest nv12_packing::tests::pack_nv12_rejects_undersized_stride ... ok\ntest nv12_packing::tests::pack_nv12_strips_row_padding ... ok\ntest nv12_packing::tests::pack_nv12_zero_padding_is_identity ... ok\ntest nv12_packing::tests::pack_nv12_rejects_wrong_dst_size ... ok\ntest nv12_packing::tests::packed_size_matches_yuv420_layout ... ok\ntest nv12_packing::tests::packed_size_rejects_zero_dims ... ok\ntest nv12_packing::tests::packed_size_rejects_odd_height ... ok\ntest os_version::tests::classify_macos_11_is_unsupported ... ok\ntest os_version::tests::classify_macos_12_3_is_supported ... ok\ntest os_version::tests::classify_unknown_version_is_unsupported ... ok\ntest os_version::tests::format_version_trims_zero_patch ... ok\ntest os_version::tests::meets_floor_exact_match ... ok\ntest os_version::tests::meets_floor_higher_major ... ok\ntest os_version::tests::rejects_below_floor ... ok\n\ntest result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n▶ mac-screen-capture loader wrapper\n ✔ forwards source id, kind, and dimensions to native binding (2.259875ms)\n ✔ defaults sourceKind to screen and frameRate to 30 (0.213958ms)\n ✔ forwards display and window sources from native binding without rewriting ids (0.2195ms)\n ✔ reports ScreenCaptureKit support and permission from native binding (0.7435ms)\n ✔ re-emits native frame events to listeners (0.327166ms)\n ✔ emits closed once when native lifecycle closes and stop is called later (0.299333ms)\n ✔ lifecycle error emits Error event (0.176125ms)\n✔ mac-screen-capture loader wrapper (5.402834ms)\nℹ tests 7\nℹ suites 1\nℹ pass 7\nℹ fail 0\nℹ cancelled 0\nℹ skipped 0\nℹ todo 0\nℹ duration_ms 353.937417\n", + "stderrTail": " Finished `test` profile [unoptimized + debuginfo] target(s) in 0.03s\n Running unittests src/lib.rs (target/debug/deps/fluxer_mac_screen_capture-41d64e8eafcd58d2)\n Doc-tests fluxer_mac_screen_capture\n" + }, + { + "name": "mac-app-audio-build", + "command": "pnpm --dir native/mac-app-audio build", + "category": "platform-native", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-06T22:17:39.173Z", + "endedAt": "2026-06-06T22:17:41.191Z", + "durationMs": 2018, + "stdoutTail": "../linux-audio-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-evdev |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-notifications |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-portals |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../system-hunspell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n\n> @fluxer/mac-app-audio@0.0.0 build /Users/hampus/Development/fluxer/fluxer_desktop/native/mac-app-audio\n> cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon\n\n[buildRustNodeAddon] cwd=/Users/hampus/Development/fluxer/fluxer_desktop/native/mac-app-audio\n+ /Users/hampus/.rustup/toolchains/1.93.0-aarch64-apple-darwin/bin/cargo build --release --target aarch64-apple-darwin --manifest-path Cargo.toml\n+ codesign --force --sign - /Users/hampus/Development/fluxer/fluxer_desktop/native/mac-app-audio/mac-app-audio.darwin-arm64.node\n", + "stderrTail": " Finished `release` profile [optimized] target(s) in 0.04s\n/Users/hampus/Development/fluxer/fluxer_desktop/native/mac-app-audio/mac-app-audio.darwin-arm64.node: replacing existing signature\n" + }, + { + "name": "mac-app-audio-rust-tests", + "command": "pnpm --dir native/mac-app-audio test:cargo", + "category": "platform-native", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-06T22:17:41.191Z", + "endedAt": "2026-06-06T22:17:42.306Z", + "durationMs": 1115, + "stdoutTail": "../linux-audio-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-evdev |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-notifications |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-portals |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../linux-screen-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../system-hunspell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"linux\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-clipboard |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-game-capture |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-process-loopback |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-shell |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../win-toast |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n../windows-input-hook |  WARN  Unsupported platform: wanted: {\"cpu\":[\"x64\",\"arm64\"],\"os\":[\"win32\"],\"libc\":[\"any\"]} (current: {\"os\":\"darwin\",\"cpu\":\"arm64\",\"libc\":\"unknown\"})\n\n> @fluxer/mac-app-audio@0.0.0 test:cargo /Users/hampus/Development/fluxer/fluxer_desktop/native/mac-app-audio\n> cargo test --manifest-path Cargo.toml\n\n\nrunning 49 tests\ntest audio_converter::tests::build_input_non_interleaved ... ok\ntest audio_converter::tests::build_input_interleaved ... ok\ntest audio_converter::tests::build_output_mono_44_1k ... ok\ntest audio_converter::tests::build_output_stereo_48k ... ok\ntest audio_converter::tests::converted_frame_count_exact ... ok\ntest audio_converter::tests::lpcm_fourcc ... ok\ntest audio_converter::tests::input_frame_count_interleaved_and_planar ... ok\ntest audio_converter::tests::normalize_signed_int16 ... ok\ntest audio_converter::tests::interleave_planar_stereo ... ok\ntest audio_converter::tests::duplicate_mono_to_stereo ... ok\ntest audio_converter::tests::linear_resample_to_target_rate ... ok\ntest audio_converter::tests::handle_float64_input ... ok\ntest audio_converter::tests::output_frame_capacity_rounds_up ... ok\ntest foundation::tests::mutable_array_initial_count_is_zero ... ok\ntest foundation::tests::mutable_dictionary_initial_count_is_zero ... ok\ntest foundation::tests::nsstring_to_string_handles_none ... ok\ntest foundation::tests::nsstring_round_trip ... ok\ntest os_version::tests::classify_macos_10_15_is_unsupported_and_mentions_min_version ... ok\ntest os_version::tests::classify_macos_12_3_is_sck_only ... ok\ntest os_version::tests::classify_macos_14_2_has_both_backends ... ok\ntest os_version::tests::classify_macos_15_is_supported ... ok\ntest os_version::tests::classify_unknown_version_is_unsupported ... ok\ntest os_version::tests::coreaudio_floor_14_2 ... ok\ntest os_version::tests::format_version_trims_zero_patch ... ok\ntest os_version::tests::meets_floor_exact_match ... ok\ntest os_version::tests::meets_floor_higher_major ... ok\ntest os_version::tests::meets_floor_higher_minor ... ok\ntest os_version::tests::rejects_below_floor_major ... ok\ntest os_version::tests::rejects_below_floor_minor ... ok\ntest os_version::tests::rejects_macos_10_15_for_sck ... ok\ntest process_tree::tests::collect_related_returns_tree ... ok\ntest process_tree::tests::same_launch_tree_excludes_unrelated ... ok\ntest process_tree::tests::same_launch_tree_includes_direct_child ... ok\ntest process_tree::tests::same_launch_tree_includes_pgrp_peer ... ok\ntest related_app::tests::helper_bundle_strips_suffix ... ok\ntest related_app::tests::helper_name_strips_helper_suffix ... ok\ntest related_app::tests::related_by_dot_prefix ... ok\ntest related_app::tests::related_via_bundle_helper_base ... ok\ntest related_app::tests::related_via_name_helper_base ... ok\ntest related_app::tests::unrelated_returns_false ... ok\ntest sck::tests::cgrect_intersection_handles_negative_and_disjoint ... ok\ntest source_state::tests::cancel_idle_short_circuits ... ok\ntest source_state::tests::double_start_rejected ... ok\ntest source_state::tests::happy_path ... ok\ntest source_state::tests::is_allowed_exhaustive ... ok\ntest source_state::tests::mark_fatal_forces_stopped ... ok\ntest source_state::tests::mark_fatal_idempotent ... ok\ntest source_state::tests::start_while_stopping_rejected ... ok\ntest source_state::tests::stop_before_start_rejected ... ok\n\ntest result: ok. 49 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n", + "stderrTail": " Finished `test` profile [unoptimized + debuginfo] target(s) in 0.03s\n Running unittests src/lib.rs (target/debug/deps/fluxer_mac_app_audio-b86bec7aea37ec64)\n Doc-tests fluxer_mac_app_audio\n" + }, + { + "name": "mac-app-audio-js-tests", + "command": "'/Users/hampus/Development/fluxer/node_modules/.pnpm/node_modules/.bin/vitest' run", + "category": "platform-native", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-06T22:17:42.306Z", + "endedAt": "2026-06-06T22:17:42.978Z", + "durationMs": 672, + "stdoutTail": "\n RUN v4.0.18 /Users/hampus/Development/fluxer/fluxer_desktop/native/mac-app-audio\n\n ✓ index.test.mjs (5 tests) 4ms\n\n Test Files 1 passed (1)\n Tests 5 passed (5)\n Start at 00:17:42\n Duration 399ms (transform 13ms, setup 0ms, import 285ms, tests 4ms, environment 0ms)\n\n", + "stderrTail": "" + } + ] +} diff --git a/fluxer_desktop/native-media-reports/2026-06-07T14-52-39-687Z/summary.json b/fluxer_desktop/native-media-reports/2026-06-07T14-52-39-687Z/summary.json new file mode 100644 index 0000000..be3ef79 --- /dev/null +++ b/fluxer_desktop/native-media-reports/2026-06-07T14-52-39-687Z/summary.json @@ -0,0 +1,143 @@ +{ + "status": "fail", + "mode": "smoke", + "strict": false, + "platform": "linux", + "arch": "arm64", + "hostname": "d4e5fefa7fc7", + "startedAt": "2026-06-07T14:52:39.689Z", + "endedAt": "2026-06-07T14:53:04.641Z", + "reportDir": "/workspaces/fluxer/fluxer_desktop/native-media-reports/2026-06-07T14-52-39-687Z", + "gates": [ + { + "name": "mode-valid", + "status": "pass", + "mode": "smoke" + }, + { + "name": "documentation-current", + "status": "pass", + "missing": [] + }, + { + "name": "strict-prerequisites", + "status": "skip", + "reason": "smoke mode" + }, + { + "name": "strict-command-plan", + "status": "skip", + "reason": "smoke mode" + } + ], + "plannedCommands": [ + { + "name": "main-process-native-media-unit-tests", + "command": "node --test src/main/NativeVoiceEngine.test.mjs src/main/NativeScreenCapture.test.mjs", + "category": "shared", + "cwd": "." + }, + { + "name": "desktop-typecheck", + "command": "pnpm typecheck", + "category": "shared", + "cwd": "." + }, + { + "name": "webrtc-sender-build", + "command": "pnpm --dir native/webrtc-sender build", + "category": "native-sender", + "cwd": "." + }, + { + "name": "webrtc-sender-tests", + "command": "pnpm --dir native/webrtc-sender test", + "category": "native-sender", + "cwd": "." + }, + { + "name": "webrtc-sender-livekit-harness", + "command": "pnpm --dir native/webrtc-sender test:livekit", + "category": "livekit", + "cwd": "." + }, + { + "name": "linux-screen-capture-build", + "command": "pnpm --dir native/linux-screen-capture build", + "category": "platform-native", + "cwd": "." + }, + { + "name": "linux-screen-capture-tests", + "command": "pnpm --dir native/linux-screen-capture test", + "category": "platform-native", + "cwd": "." + }, + { + "name": "linux-audio-capture-build", + "command": "pnpm --dir native/linux-audio-capture build", + "category": "platform-native", + "cwd": "." + }, + { + "name": "linux-audio-capture-tests", + "command": "pnpm --dir native/linux-audio-capture test", + "category": "platform-native", + "cwd": "." + } + ], + "commands": [ + { + "name": "main-process-native-media-unit-tests", + "command": "node --test src/main/NativeVoiceEngine.test.mjs src/main/NativeScreenCapture.test.mjs", + "category": "shared", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-07T14:52:39.690Z", + "endedAt": "2026-06-07T14:52:39.792Z", + "durationMs": 102, + "stdoutTail": "▶ NativeScreenCapture source identity and capability reporting\n ✔ normalizes display and window sources without changing source ids (1.614801ms)\n ✔ starts display and window captures with exact source id and kind and reports diagnostics (1.041547ms)\n ✔ fails fast when native frame sink is required but unavailable (0.500503ms)\n ✔ passes caller-provided capture id and native sink handle to the platform wrapper (0.467711ms)\n ✔ reports macOS and Linux display/window capture capabilities from platform backends (0.625753ms)\n✔ NativeScreenCapture source identity and capability reporting (4.771278ms)\n▶ NativeVoiceEngine connection lifecycle\n ✔ does not forward native events after disconnecting a pending connect (3.117018ms)\n ✔ disconnects stale native connect completion after a replacement connect starts (1.775302ms)\n✔ NativeVoiceEngine connection lifecycle (5.440365ms)\n▶ NativeVoiceEngine screen frame routing\n ✔ publishes screen share with source pacing and exposes a native frame sink handle (0.897838ms)\n ✔ keeps screen-share audio published when screen video is unpublished (0.634254ms)\n ✔ replaces screen captures without releasing screen-share audio or duplicating priority (0.502586ms)\n ✔ forwards watch and unwatch operations with source, enablement, and quality intact (0.563378ms)\n ✔ reports VideoToolbox hardware capability from the native addon (0.49767ms)\n ✔ reports native voice engine capabilities from the native addon (0.56267ms)\n ✔ maps native addon microphone not-connected errors to the typed operation code (0.639129ms)\n✔ NativeVoiceEngine screen frame routing (4.546943ms)\nℹ tests 14\nℹ suites 3\nℹ pass 14\nℹ fail 0\nℹ cancelled 0\nℹ skipped 0\nℹ todo 0\nℹ duration_ms 81.841144\n", + "stderrTail": "" + }, + { + "name": "desktop-typecheck", + "command": "pnpm typecheck", + "category": "shared", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-07T14:52:39.792Z", + "endedAt": "2026-06-07T14:52:40.133Z", + "durationMs": 341, + "stdoutTail": "\n> fluxer_desktop@0.0.0 typecheck /workspaces/fluxer/fluxer_desktop\n> tsgo --noEmit\n\n", + "stderrTail": "" + }, + { + "name": "webrtc-sender-build", + "command": "pnpm --dir native/webrtc-sender build", + "category": "native-sender", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-07T14:52:40.133Z", + "endedAt": "2026-06-07T14:52:57.557Z", + "durationMs": 17424, + "stdoutTail": "\n> @fluxer/webrtc-sender@0.0.0 build /workspaces/fluxer/fluxer_desktop/native/webrtc-sender\n> cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon\n\n[buildRustNodeAddon] cwd=/workspaces/fluxer/fluxer_desktop/native/webrtc-sender\n+ /home/vscode/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/bin/cargo build --release --target aarch64-unknown-linux-gnu --manifest-path Cargo.toml --features camera-native\n", + "stderrTail": " Compiling crossbeam-utils v0.8.21\n Compiling fluxer_screen_frame_bus v0.0.0 (/workspaces/fluxer/fluxer_desktop/native/screen-frame-bus)\n Compiling fluxer_voice_core v0.1.0 (/workspaces/fluxer/fluxer_voice_core)\nwarning: unused import: `enum_dispatch`\n --> vendor/libwebrtc/src/audio_source.rs:15:13\n |\n15 | use crate::{enum_dispatch, imp::audio_source as imp_as};\n | ^^^^^^^^^^^^^\n |\n = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default\n\nwarning: unnecessary `unsafe` block\n --> vendor/libwebrtc/src/native/apm.rs:32:25\n |\n32 | sys_handle: unsafe {\n | ^^^^^^ unnecessary `unsafe` block\n |\n = note: `#[warn(unused_unsafe)]` (part of `#[warn(unused)]`) on by default\n\nwarning: variable does not need to be mutable\n --> vendor/libwebrtc/src/native/desktop_capturer.rs:66:13\n |\n66 | let mut sys_handle = sys_dc::ffi::DesktopCapturerOptions {\n | ----^^^^^^^^^^\n | |\n | help: remove this `mut`\n |\n = note: `#[warn(unused_mut)]` (part of `#[warn(unused)]`) on by default\n\nwarning: hiding a lifetime that's elided elsewhere is confusing\n --> vendor/libwebrtc/src/native/audio_mixer.rs:30:34\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option;\n | ^^^^^ the lifetime is elided here ^^^^^^^^^^ the same lifetime is hidden here\n |\n = help: the same lifetime is referred to in inconsistent ways, making the signature confusing\n = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default\nhelp: use `'_` for type paths\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option>;\n | ++++\n\nwarning: `libwebrtc` (lib) generated 4 warnings (run `cargo fix --lib -p libwebrtc` to apply 3 suggestions)\n Compiling crossbeam-queue v0.3.12\n Compiling fluxer_webrtc_sender v0.0.0 (/workspaces/fluxer/fluxer_desktop/native/webrtc-sender)\n Finished `release` profile [optimized] target(s) in 10.39s\n" + }, + { + "name": "webrtc-sender-tests", + "command": "pnpm --dir native/webrtc-sender test", + "category": "native-sender", + "status": "fail", + "code": 1, + "signal": null, + "startedAt": "2026-06-07T14:52:57.557Z", + "endedAt": "2026-06-07T14:53:04.641Z", + "durationMs": 7084, + "stdoutTail": "\n> @fluxer/webrtc-sender@0.0.0 test /workspaces/fluxer/fluxer_desktop/native/webrtc-sender\n> cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- test-webrtc-sender-rust && node --test index.test.mjs scripts/livekit-harness.test.mjs\n\n+ /home/vscode/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/bin/cargo test --features \"publisher,camera-native\"\n ELIFECYCLE  Test failed. See above for more details.\n WARN  Local package.json exists, but node_modules missing, did you mean to install?\n", + "stderrTail": " Compiling zerocopy-derive v0.8.50\n Compiling serde v1.0.228\n Compiling ring v0.17.14\n Compiling serde_urlencoded v0.7.1\n Compiling fluxer_screen_frame_bus v0.0.0 (/workspaces/fluxer/fluxer_desktop/native/screen-frame-bus)\n Compiling rustls v0.23.40\n Compiling pbjson v0.6.0\n Compiling os_info v3.15.0\n Compiling fluxer_voice_core v0.1.0 (/workspaces/fluxer/fluxer_voice_core)\n Compiling tinytemplate v1.2.1\n Compiling pbjson-types v0.6.0\n Compiling zerocopy v0.8.50\n Compiling rustls-webpki v0.103.13\n Compiling livekit-protocol v0.7.8\n Compiling ppv-lite86 v0.2.21\n Compiling half v2.7.1\n Compiling rand_chacha v0.9.0\n Compiling rand_chacha v0.3.1\n Compiling ciborium-ll v0.2.2\n Compiling tokio-rustls v0.26.4\n Compiling rand v0.8.6\n Compiling rand v0.9.4\nerror[E0786]: found invalid metadata files for crate `rustls`\n --> /home/vscode/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-rustls-0.26.4/src/lib.rs:49:9\n |\n49 | pub use rustls;\n | ^^^^^^\n |\n = note: failed to open rmeta metadata: '/workspaces/fluxer/fluxer_desktop/native/webrtc-sender/target/debug/deps/librustls-f66081aae860cdad.rmeta'\n\nFor more information about this error, try `rustc --explain E0786`.\nerror: could not compile `tokio-rustls` (lib) due to 1 previous error\nwarning: build failed, waiting for other jobs to finish...\nCommand failed with exit code 101: /home/vscode/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/bin/cargo test --features \"publisher,camera-native\"\n" + } + ] +} diff --git a/fluxer_desktop/native-media-reports/2026-06-09T-codex-macos-livekit-strict5-rerun.json b/fluxer_desktop/native-media-reports/2026-06-09T-codex-macos-livekit-strict5-rerun.json new file mode 100644 index 0000000..c9fac55 --- /dev/null +++ b/fluxer_desktop/native-media-reports/2026-06-09T-codex-macos-livekit-strict5-rerun.json @@ -0,0 +1,765 @@ +{ + "status": "fail", + "startedAt": "2026-06-09T04:40:52.879Z", + "endedAt": "2026-06-09T04:41:02.390Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://localhost:7880", + "serverApiUrl": "http://localhost:7880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-76954-91cb528a-6d21-4940-ad58-aa5c5e746211", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-76954", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-76954", + "subscriberIdentity": "fluxer-native-subscriber-76954", + "required": true, + "strict": true, + "timeoutMs": 30000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 5000, + "width": 320, + "height": 180, + "expectedWidth": 320, + "expectedHeight": 180, + "fps": 15, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 80, + "maxPacketLoss": null, + "requireStableResolution": true, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": null, + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": true, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": true, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": true, + "screenSimulcast": true, + "secondaryPublisherScreenSimulcast": true, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 400000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "validateServerPublishing": true, + "camera": true, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/2026-06-09T-codex-macos-livekit-strict5-rerun.json", + "verbose": false, + "scenarioName": "codec-vp8-with-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 9", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 3", + "pass": true + }, + { + "name": "subscriber video resolution 320x180", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 12", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 4", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 12", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "publisher local camera publication", + "pass": true + }, + { + "name": "subscriber remote camera subscription", + "pass": true + }, + { + "name": "publisher outbound camera stats", + "pass": true + }, + { + "name": "secondary publisher connected", + "pass": true + }, + { + "name": "secondary publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote secondary screenshare subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare stats", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound secondary codec video/VP8", + "pass": true + }, + { + "name": "secondary publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote secondary microphone subscription", + "pass": true + }, + { + "name": "secondary publisher outbound microphone stats", + "pass": true + }, + { + "name": "secondary publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote secondary screen-share audio subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "subscriber secondary data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + }, + { + "name": "server sees publisher camera publication", + "pass": true + }, + { + "name": "server sees secondary publisher screenshare publication", + "pass": true + }, + { + "name": "server sees secondary publisher microphone publication", + "pass": true + }, + { + "name": "server sees secondary publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": "received FPS 10.01 below 14.25", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 3416, + "inboundVideoFrames": 319, + "strictAudioFrames": 50, + "strictVideoFrames": 50, + "videoCallbacks": 319, + "videoBytes": 55728000, + "receivedFps": 10.01, + "requiredFps": 14.25, + "maxVideoFrameGapMs": 104, + "maxAudioFrameGapMs": 102, + "maxVideoFrameGapOverallMs": 83, + "maxAudioFrameGapOverallMs": 102, + "avDriftMs": 36, + "maxObservedPacketLoss": 76, + "maxObservedPacketLossDelta": 43, + "videoResolutionCounts": { + "320x180": 50 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 15, + "startedAtMs": 1780980053827, + "stoppedAtMs": null, + "framesPushed": 0, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 0, + "lastPushAtMs": null, + "elapsedMs": 8570, + "producedFps": 0 + }, + "secondaryPublisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 15, + "startedAtMs": 1780980053827, + "stoppedAtMs": null, + "framesPushed": 0, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 0, + "lastPushAtMs": null, + "elapsedMs": 8570, + "producedFps": 0 + }, + "serverPublishing": { + "fetchedAtMs": 1780980057383, + "error": null, + "participants": [ + { + "identity": "fluxer-native-publisher-2-76954", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_VSwHXmU7ctxa87", + "name": "screen", + "kind": "video", + "source": "screen_share", + "mimeType": "video/VP8", + "muted": false + }, + { + "sid": "TR_AMnWff3NPczSoR", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_AsTFRWHNvJ7MgE", + "name": "screen-audio", + "kind": "audio", + "source": "screen_share_audio", + "mimeType": "audio/red", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-publisher-76954", + "trackCount": 4, + "tracks": [ + { + "sid": "TR_VSp9aehQCmDiq4", + "name": "screen", + "kind": "video", + "source": "screen_share", + "mimeType": "video/VP8", + "muted": false + }, + { + "sid": "TR_AM8ZuGaDACq9FB", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_AsD2wGcUaXveFj", + "name": "screen-audio", + "kind": "audio", + "source": "screen_share_audio", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_VC7GWwmaJQZJio", + "name": "camera", + "kind": "video", + "source": "camera", + "mimeType": "video/VP8", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-subscriber-76954", + "trackCount": 0, + "tracks": [] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_szoL7dTduPBR", + "trackSid": "TR_VC7GWwmaJQZJio", + "trackName": "camera", + "source": "camera", + "width": 480, + "height": 360, + "timestampUs": 526246627000, + "identity": "fluxer-native-publisher-76954", + "kind": "video" + }, + "lastStrictVideoMeta": { + "atMs": 1780980062302, + "identity": "fluxer-native-publisher-76954", + "kind": "video", + "source": "screen_share", + "trackName": "screen", + "trackSid": "TR_VSp9aehQCmDiq4", + "width": 320, + "height": 180 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_VC7GWwmaJQZJio", + "source": "camera", + "kind": "video", + "bitrateKbps": 158.8, + "packetsLost": 0, + "fps": 25, + "codec": "video/VP8" + }, + { + "trackSid": "TR_VC7GWwmaJQZJio", + "source": "camera", + "kind": "video", + "bitrateKbps": 112.8, + "packetsLost": 0, + "fps": 20, + "codec": "video/VP8" + }, + { + "trackSid": "TR_AsD2wGcUaXveFj", + "source": "screen_share_audio", + "kind": "audio", + "bitrateKbps": 260.8, + "packetsLost": 0, + "audioLevel": 0.305, + "codec": "audio/opus" + }, + { + "trackSid": "TR_AM8ZuGaDACq9FB", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 98.8, + "packetsLost": 0, + "audioLevel": 0.305, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSp9aehQCmDiq4", + "source": "screen_share", + "kind": "video", + "bitrateKbps": 7.2, + "packetsLost": 0, + "fps": 9, + "width": 320, + "height": 180, + "sourceWidth": 320, + "sourceHeight": 180, + "targetBitrateKbps": 400, + "configuredFps": 15, + "targetFps": 15, + "effectiveFps": 0, + "framesProduced": 0, + "framesAccepted": 0, + "framesDropped": 0, + "framesCoalesced": 0, + "framesCaptured": 0, + "captureFailures": 0, + "maxQueueAgeMs": 0, + "maxPushLatencyMs": 0, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable", + "codec": "video/VP8" + } + ], + "inbound": [ + { + "participantSid": "PA_RnrcQgHxNGSb", + "trackSid": "TR_AMnWff3NPczSoR", + "kind": "audio", + "bitrateKbps": 94.8, + "packetsLost": 32, + "jitterMs": 0, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_RnrcQgHxNGSb", + "trackSid": "TR_AsTFRWHNvJ7MgE", + "kind": "audio", + "bitrateKbps": 236, + "packetsLost": 28, + "jitterMs": 0, + "audioLevel": 0.336, + "codec": "audio/opus" + }, + { + "participantSid": "PA_RnrcQgHxNGSb", + "trackSid": "TR_VSwHXmU7ctxa87", + "kind": "video", + "bitrateKbps": 7.1, + "packetsLost": 5, + "jitterMs": 1, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoQueueCapacity": 8, + "outgoingVideoMaxQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 15, + "outgoingVideoPacingTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingVideoPacingMode": "source", + "outgoingVideoBusActive": false, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 55, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_AMnWff3NPczSoR", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 99.2, + "packetsLost": 0, + "audioLevel": 0.305, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSwHXmU7ctxa87", + "source": "screen_share", + "kind": "video", + "bitrateKbps": 10.6, + "packetsLost": 0, + "fps": 9, + "width": 320, + "height": 180, + "sourceWidth": 320, + "sourceHeight": 180, + "targetBitrateKbps": 400, + "configuredFps": 15, + "targetFps": 15, + "effectiveFps": 0, + "framesProduced": 0, + "framesAccepted": 0, + "framesDropped": 0, + "framesCoalesced": 0, + "framesCaptured": 0, + "captureFailures": 0, + "maxQueueAgeMs": 0, + "maxPushLatencyMs": 0, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable", + "codec": "video/VP8" + }, + { + "trackSid": "TR_AsTFRWHNvJ7MgE", + "source": "screen_share_audio", + "kind": "audio", + "bitrateKbps": 261.1, + "packetsLost": 0, + "audioLevel": 0.305, + "codec": "audio/opus" + } + ], + "inbound": [ + { + "participantSid": "PA_szoL7dTduPBR", + "trackSid": "TR_AM8ZuGaDACq9FB", + "kind": "audio", + "bitrateKbps": 88.9, + "packetsLost": 25, + "jitterMs": 0, + "audioLevel": 0.331, + "codec": "audio/opus" + }, + { + "participantSid": "PA_szoL7dTduPBR", + "trackSid": "TR_AsD2wGcUaXveFj", + "kind": "audio", + "bitrateKbps": 250.9, + "packetsLost": 30, + "jitterMs": 0, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_szoL7dTduPBR", + "trackSid": "TR_VC7GWwmaJQZJio", + "kind": "video", + "bitrateKbps": 113.7, + "packetsLost": 7, + "jitterMs": 0, + "codec": "video/VP8" + }, + { + "participantSid": "PA_szoL7dTduPBR", + "trackSid": "TR_VSp9aehQCmDiq4", + "kind": "video", + "bitrateKbps": 9.3, + "packetsLost": 14, + "jitterMs": 1, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoQueueCapacity": 8, + "outgoingVideoMaxQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 15, + "outgoingVideoPacingTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingVideoPacingMode": "source", + "outgoingVideoBusActive": false, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 56, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_szoL7dTduPBR", + "trackSid": "TR_AsD2wGcUaXveFj", + "kind": "audio", + "bitrateKbps": 260.5, + "packetsLost": 5, + "jitterMs": 0, + "audioLevel": 0.318, + "codec": "audio/opus" + }, + { + "participantSid": "PA_szoL7dTduPBR", + "trackSid": "TR_VC7GWwmaJQZJio", + "kind": "video", + "bitrateKbps": 112.7, + "packetsLost": 1, + "jitterMs": 0, + "codec": "video/VP8" + }, + { + "participantSid": "PA_szoL7dTduPBR", + "trackSid": "TR_AM8ZuGaDACq9FB", + "kind": "audio", + "bitrateKbps": 98.7, + "packetsLost": 1, + "jitterMs": 0, + "audioLevel": 0.313, + "codec": "audio/opus" + }, + { + "participantSid": "PA_szoL7dTduPBR", + "trackSid": "TR_VSp9aehQCmDiq4", + "kind": "video", + "bitrateKbps": 7.2, + "packetsLost": 10, + "jitterMs": 0, + "codec": "video/VP8" + }, + { + "participantSid": "PA_RnrcQgHxNGSb", + "trackSid": "TR_VSwHXmU7ctxa87", + "kind": "video", + "bitrateKbps": 7.7, + "packetsLost": 3, + "jitterMs": 1, + "codec": "video/VP8" + }, + { + "participantSid": "PA_RnrcQgHxNGSb", + "trackSid": "TR_AMnWff3NPczSoR", + "kind": "audio", + "bitrateKbps": 98.7, + "packetsLost": 3, + "jitterMs": 0, + "audioLevel": 0.323, + "codec": "audio/opus" + }, + { + "participantSid": "PA_RnrcQgHxNGSb", + "trackSid": "TR_AsTFRWHNvJ7MgE", + "kind": "audio", + "bitrateKbps": 267.5, + "packetsLost": 1, + "jitterMs": 1, + "audioLevel": 0.307, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoQueueCapacity": 0, + "outgoingVideoMaxQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoPacingTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingVideoPacingMode": "idle", + "outgoingVideoBusActive": false, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/2026-06-09T0407-codex-macos-livekit-strict5-localhost.json b/fluxer_desktop/native-media-reports/2026-06-09T0407-codex-macos-livekit-strict5-localhost.json new file mode 100644 index 0000000..c2c9415 --- /dev/null +++ b/fluxer_desktop/native-media-reports/2026-06-09T0407-codex-macos-livekit-strict5-localhost.json @@ -0,0 +1,752 @@ +{ + "status": "fail", + "startedAt": "2026-06-09T04:11:22.147Z", + "endedAt": "2026-06-09T04:11:46.733Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://localhost:7880", + "serverApiUrl": "http://localhost:7880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-39834-83ba9452-cd15-4567-a196-47587b5a4a47", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-39834", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-39834", + "subscriberIdentity": "fluxer-native-subscriber-39834", + "required": true, + "strict": true, + "timeoutMs": 30000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 5000, + "width": 320, + "height": 180, + "expectedWidth": 320, + "expectedHeight": 180, + "fps": 15, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 80, + "maxPacketLoss": null, + "requireStableResolution": true, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": null, + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": true, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": true, + "secondaryPublisherScreenSimulcast": true, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 400000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "validateServerPublishing": true, + "camera": true, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/2026-06-09T0407-codex-macos-livekit-strict5-localhost.json", + "verbose": false, + "scenarioName": "codec-vp8-with-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 9", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 3", + "pass": true + }, + { + "name": "subscriber video resolution 320x180", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 12", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 4", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 12", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "publisher local camera publication", + "pass": true + }, + { + "name": "subscriber remote camera subscription", + "pass": true + }, + { + "name": "publisher outbound camera stats", + "pass": true + }, + { + "name": "secondary publisher connected", + "pass": true + }, + { + "name": "secondary publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote secondary screenshare subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare stats", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound secondary codec video/VP8", + "pass": true + }, + { + "name": "secondary publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote secondary microphone subscription", + "pass": true + }, + { + "name": "secondary publisher outbound microphone stats", + "pass": true + }, + { + "name": "secondary publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote secondary screen-share audio subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "subscriber secondary data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + }, + { + "name": "server sees publisher camera publication", + "pass": true + }, + { + "name": "server sees secondary publisher screenshare publication", + "pass": true + }, + { + "name": "server sees secondary publisher microphone publication", + "pass": true + }, + { + "name": "server sees secondary publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": "A/V drift 97ms exceeds 80ms; observed 101 video frames outside 320x180", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 2952, + "inboundVideoFrames": 268, + "strictAudioFrames": 49, + "strictVideoFrames": 201, + "videoCallbacks": 268, + "videoBytes": 46684800, + "receivedFps": 40.18, + "requiredFps": 14.25, + "maxVideoFrameGapMs": 71, + "maxAudioFrameGapMs": 101, + "maxVideoFrameGapOverallMs": 242, + "maxAudioFrameGapOverallMs": 102, + "avDriftMs": 97, + "maxObservedPacketLoss": 49, + "maxObservedPacketLossDelta": 30, + "videoResolutionCounts": { + "480x360": 101, + "320x180": 100 + }, + "videoResolutionMismatchCount": 101, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 15, + "startedAtMs": 1780978299271, + "stoppedAtMs": null, + "framesPushed": 0, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 0, + "lastPushAtMs": null, + "elapsedMs": 7471, + "producedFps": 0 + }, + "secondaryPublisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 15, + "startedAtMs": 1780978299271, + "stoppedAtMs": null, + "framesPushed": 0, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 0, + "lastPushAtMs": null, + "elapsedMs": 7471, + "producedFps": 0 + }, + "serverPublishing": { + "fetchedAtMs": 1780978301481, + "error": null, + "participants": [ + { + "identity": "fluxer-native-publisher-39834", + "trackCount": 4, + "tracks": [ + { + "sid": "TR_VSgsehdpyfYRtH", + "name": "screen", + "kind": "video", + "source": "screen_share", + "mimeType": "video/VP8", + "muted": false + }, + { + "sid": "TR_AMRH3WDE9scsXz", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_As4hoJPNkjh8Rv", + "name": "screen-audio", + "kind": "audio", + "source": "screen_share_audio", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_VC9jnXZixHviLZ", + "name": "camera", + "kind": "video", + "source": "camera", + "mimeType": "video/VP8", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-publisher-2-39834", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_VSQsd4UAwpwKy7", + "name": "screen", + "kind": "video", + "source": "screen_share", + "mimeType": "video/VP8", + "muted": false + }, + { + "sid": "TR_AsHmVYBSWSQdfU", + "name": "screen-audio", + "kind": "audio", + "source": "screen_share_audio", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_AMq8at99MRi83a", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-subscriber-39834", + "trackCount": 0, + "tracks": [] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_d6p3en8AQ9YP", + "trackSid": "TR_VSgsehdpyfYRtH", + "width": 320, + "height": 180, + "timestampUs": 524491017000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_VSgsehdpyfYRtH", + "source": "screen_share", + "kind": "video", + "bitrateKbps": 6.9, + "packetsLost": 0, + "fps": 10, + "width": 320, + "height": 180, + "sourceWidth": 320, + "sourceHeight": 180, + "targetBitrateKbps": 400, + "configuredFps": 15, + "targetFps": 15, + "effectiveFps": 0, + "framesProduced": 0, + "framesAccepted": 0, + "framesDropped": 0, + "framesCoalesced": 0, + "framesCaptured": 0, + "captureFailures": 0, + "maxQueueAgeMs": 0, + "maxPushLatencyMs": 0, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable", + "codec": "video/VP8" + }, + { + "trackSid": "TR_As4hoJPNkjh8Rv", + "source": "screen_share_audio", + "kind": "audio", + "bitrateKbps": 261.2, + "packetsLost": 0, + "audioLevel": 0.305, + "codec": "audio/opus" + }, + { + "trackSid": "TR_AMRH3WDE9scsXz", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 98.9, + "packetsLost": 0, + "audioLevel": 0.305, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VC9jnXZixHviLZ", + "source": "camera", + "kind": "video", + "bitrateKbps": 114.9, + "packetsLost": 0, + "fps": 25, + "codec": "video/VP8" + }, + { + "trackSid": "TR_VC9jnXZixHviLZ", + "source": "camera", + "kind": "video", + "bitrateKbps": 92, + "packetsLost": 0, + "fps": 20, + "codec": "video/VP8" + } + ], + "inbound": [ + { + "participantSid": "PA_ychdbeoTL2ht", + "trackSid": "TR_AMq8at99MRi83a", + "kind": "audio", + "bitrateKbps": 98.8, + "packetsLost": 1, + "jitterMs": 0, + "audioLevel": 0.306, + "codec": "audio/opus" + }, + { + "participantSid": "PA_ychdbeoTL2ht", + "trackSid": "TR_AsHmVYBSWSQdfU", + "kind": "audio", + "bitrateKbps": 261.4, + "packetsLost": 3, + "jitterMs": 0, + "audioLevel": 0.344, + "codec": "audio/opus" + }, + { + "participantSid": "PA_ychdbeoTL2ht", + "trackSid": "TR_VSQsd4UAwpwKy7", + "kind": "video", + "bitrateKbps": 13.1, + "packetsLost": 3, + "jitterMs": 6, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoQueueCapacity": 8, + "outgoingVideoMaxQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 15, + "outgoingVideoPacingTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingVideoPacingMode": "source", + "outgoingVideoBusActive": false, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 29, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": { + "rttMs": null, + "outbound": [ + { + "trackSid": "TR_VSQsd4UAwpwKy7", + "source": "screen_share", + "kind": "video", + "bitrateKbps": 15.3, + "packetsLost": 0, + "fps": 10, + "width": 320, + "height": 180, + "sourceWidth": 320, + "sourceHeight": 180, + "targetBitrateKbps": 400, + "configuredFps": 15, + "targetFps": 15, + "effectiveFps": 0, + "framesProduced": 0, + "framesAccepted": 0, + "framesDropped": 0, + "framesCoalesced": 0, + "framesCaptured": 0, + "captureFailures": 0, + "maxQueueAgeMs": 0, + "maxPushLatencyMs": 0, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable", + "codec": "video/VP8" + }, + { + "trackSid": "TR_AMq8at99MRi83a", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 98.8, + "packetsLost": 0, + "audioLevel": 0.305, + "codec": "audio/opus" + }, + { + "trackSid": "TR_AsHmVYBSWSQdfU", + "source": "screen_share_audio", + "kind": "audio", + "bitrateKbps": 261.2, + "packetsLost": 0, + "audioLevel": 0.305, + "codec": "audio/opus" + } + ], + "inbound": [ + { + "participantSid": "PA_d6p3en8AQ9YP", + "trackSid": "TR_As4hoJPNkjh8Rv", + "kind": "audio", + "bitrateKbps": 254.9, + "packetsLost": 0, + "jitterMs": 0, + "audioLevel": 0.306, + "codec": "audio/opus" + }, + { + "participantSid": "PA_d6p3en8AQ9YP", + "trackSid": "TR_AMRH3WDE9scsXz", + "kind": "audio", + "bitrateKbps": 98.8, + "packetsLost": 0, + "jitterMs": 0, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_d6p3en8AQ9YP", + "trackSid": "TR_VSgsehdpyfYRtH", + "kind": "video", + "bitrateKbps": 7.3, + "packetsLost": 0, + "jitterMs": 2, + "codec": "video/VP8" + }, + { + "participantSid": "PA_d6p3en8AQ9YP", + "trackSid": "TR_VC9jnXZixHviLZ", + "kind": "video", + "bitrateKbps": 96.7, + "packetsLost": 0, + "jitterMs": 0, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoQueueCapacity": 8, + "outgoingVideoMaxQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 15, + "outgoingVideoPacingTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingVideoPacingMode": "source", + "outgoingVideoBusActive": false, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 29, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_d6p3en8AQ9YP", + "trackSid": "TR_VSgsehdpyfYRtH", + "kind": "video", + "bitrateKbps": 7.3, + "packetsLost": 1, + "jitterMs": 2, + "codec": "video/VP8" + }, + { + "participantSid": "PA_d6p3en8AQ9YP", + "trackSid": "TR_AMRH3WDE9scsXz", + "kind": "audio", + "bitrateKbps": 98.8, + "packetsLost": 3, + "jitterMs": 0, + "audioLevel": 0.306, + "codec": "audio/opus" + }, + { + "participantSid": "PA_d6p3en8AQ9YP", + "trackSid": "TR_VC9jnXZixHviLZ", + "kind": "video", + "bitrateKbps": 96.6, + "packetsLost": 1, + "jitterMs": 0, + "codec": "video/VP8" + }, + { + "participantSid": "PA_d6p3en8AQ9YP", + "trackSid": "TR_As4hoJPNkjh8Rv", + "kind": "audio", + "bitrateKbps": 259.7, + "packetsLost": 1, + "jitterMs": 0, + "audioLevel": 0.306, + "codec": "audio/opus" + }, + { + "participantSid": "PA_ychdbeoTL2ht", + "trackSid": "TR_AsHmVYBSWSQdfU", + "kind": "audio", + "bitrateKbps": 242.6, + "packetsLost": 22, + "jitterMs": 0, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_ychdbeoTL2ht", + "trackSid": "TR_VSQsd4UAwpwKy7", + "kind": "video", + "bitrateKbps": 15.3, + "packetsLost": 2, + "jitterMs": 7, + "codec": "video/VP8" + }, + { + "participantSid": "PA_ychdbeoTL2ht", + "trackSid": "TR_AMq8at99MRi83a", + "kind": "audio", + "bitrateKbps": 96.8, + "packetsLost": 19, + "jitterMs": 0, + "audioLevel": 0.322, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoQueueCapacity": 0, + "outgoingVideoMaxQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoPacingTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingVideoPacingMode": "idle", + "outgoingVideoBusActive": false, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/2026-06-09T0407-codex-macos-livekit-strict5.json b/fluxer_desktop/native-media-reports/2026-06-09T0407-codex-macos-livekit-strict5.json new file mode 100644 index 0000000..2ac6eb3 --- /dev/null +++ b/fluxer_desktop/native-media-reports/2026-06-09T0407-codex-macos-livekit-strict5.json @@ -0,0 +1,309 @@ +{ + "status": "fail", + "startedAt": "2026-06-09T04:10:37.354Z", + "endedAt": "2026-06-09T04:11:07.359Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:7880", + "serverApiUrl": "http://127.0.0.1:7880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-39654-62723187-aa62-4359-9c19-cbc0403532e3", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-39654", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-39654", + "subscriberIdentity": "fluxer-native-subscriber-39654", + "required": true, + "strict": true, + "timeoutMs": 30000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 5000, + "width": 320, + "height": 180, + "expectedWidth": 320, + "expectedHeight": 180, + "fps": 15, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 80, + "maxPacketLoss": null, + "requireStableResolution": true, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": null, + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": true, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": true, + "secondaryPublisherScreenSimulcast": true, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 400000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "validateServerPublishing": true, + "camera": true, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/2026-06-09T0407-codex-macos-livekit-strict5.json", + "verbose": false, + "scenarioName": "codec-vp8-with-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": false + }, + { + "name": "subscriber connected", + "pass": false + }, + { + "name": "publisher local screenshare publication", + "pass": false + }, + { + "name": "subscriber remote screenshare subscription", + "pass": false + }, + { + "name": "subscriber inbound video frames >= 9", + "pass": false + }, + { + "name": "subscriber video frame callback", + "pass": false + }, + { + "name": "publisher outbound screenshare stats", + "pass": false + }, + { + "name": "subscriber inbound video stats", + "pass": false + }, + { + "name": "subscriber inbound video stats count >= 3", + "pass": false + }, + { + "name": "subscriber video resolution 320x180", + "pass": false + }, + { + "name": "subscriber aggregate inbound audio frames >= 12", + "pass": false + }, + { + "name": "subscriber inbound audio stats count >= 4", + "pass": false + }, + { + "name": "publisher local microphone publication", + "pass": false + }, + { + "name": "subscriber remote microphone subscription", + "pass": false + }, + { + "name": "subscriber inbound audio frames >= 12", + "pass": false + }, + { + "name": "publisher outbound microphone stats", + "pass": false + }, + { + "name": "subscriber inbound audio stats", + "pass": false + }, + { + "name": "publisher local screen-share audio publication", + "pass": false + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": false + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": false + }, + { + "name": "publisher local camera publication", + "pass": false + }, + { + "name": "subscriber remote camera subscription", + "pass": false + }, + { + "name": "publisher outbound camera stats", + "pass": false + }, + { + "name": "secondary publisher connected", + "pass": false + }, + { + "name": "secondary publisher local screenshare publication", + "pass": false + }, + { + "name": "subscriber remote secondary screenshare subscription", + "pass": false + }, + { + "name": "secondary publisher outbound screenshare stats", + "pass": false + }, + { + "name": "secondary publisher outbound screenshare codec video/VP8", + "pass": false + }, + { + "name": "subscriber inbound secondary codec video/VP8", + "pass": false + }, + { + "name": "secondary publisher local microphone publication", + "pass": false + }, + { + "name": "subscriber remote secondary microphone subscription", + "pass": false + }, + { + "name": "secondary publisher outbound microphone stats", + "pass": false + }, + { + "name": "secondary publisher local screen-share audio publication", + "pass": false + }, + { + "name": "subscriber remote secondary screen-share audio subscription", + "pass": false + }, + { + "name": "secondary publisher outbound screen-share audio stats", + "pass": false + }, + { + "name": "subscriber data packet received", + "pass": false + }, + { + "name": "subscriber secondary data packet received", + "pass": false + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": false + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": false + }, + { + "name": "server publishing API participants listed", + "pass": false + }, + { + "name": "server sees publisher screenshare publication", + "pass": false + }, + { + "name": "server sees publisher microphone publication", + "pass": false + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": false + }, + { + "name": "server sees publisher camera publication", + "pass": false + }, + { + "name": "server sees secondary publisher screenshare publication", + "pass": false + }, + { + "name": "server sees secondary publisher microphone publication", + "pass": false + }, + { + "name": "server sees secondary publisher screen-share audio publication", + "pass": false + } + ], + "skippedReason": null, + "error": "Error: connect timed out after 30000ms\n at file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1252:9\n at async runHarness (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1679:3)\n at async main (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1931:18)\n at async file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1964:19", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 0, + "strictAudioFrames": 0, + "strictVideoFrames": 0, + "videoCallbacks": 0, + "videoBytes": 0, + "receivedFps": 0, + "requiredFps": 14.25, + "maxVideoFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 0, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": {}, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": null, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": null, + "error": null, + "participants": [] + }, + "lastVideoMeta": null, + "publisherStats": null, + "secondaryPublisherStats": null, + "subscriberStats": null + } +} diff --git a/fluxer_desktop/native-media-reports/2026-06-09T0407-codex-macos-smoke/livekit-harness.json b/fluxer_desktop/native-media-reports/2026-06-09T0407-codex-macos-smoke/livekit-harness.json new file mode 100644 index 0000000..158b6ca --- /dev/null +++ b/fluxer_desktop/native-media-reports/2026-06-09T0407-codex-macos-smoke/livekit-harness.json @@ -0,0 +1,331 @@ +{ + "status": "pass", + "startedAt": "2026-06-09T04:09:48.418Z", + "endedAt": "2026-06-09T04:09:50.279Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://localhost:7880", + "serverApiUrl": "http://localhost:7880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-39291-5ee61a43-3009-4112-bca3-ae4b4667a371", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-39291", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-39291", + "subscriberIdentity": "fluxer-native-subscriber-39291", + "required": false, + "strict": false, + "timeoutMs": 30000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 0, + "width": 320, + "height": 180, + "expectedWidth": 320, + "expectedHeight": 180, + "fps": 15, + "minFpsRatio": 0, + "maxFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": null, + "requireStableResolution": false, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": null, + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": false, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": true, + "secondaryPublisherScreenSimulcast": true, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 400000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": false, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/2026-06-09T0407-codex-macos-smoke/livekit-harness.json", + "verbose": false, + "scenarioName": "codec-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 320x180", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 3", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 1", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 3", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + } + ], + "skippedReason": null, + "error": null, + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 72, + "inboundVideoFrames": 5, + "strictAudioFrames": 0, + "strictVideoFrames": 0, + "videoCallbacks": 5, + "videoBytes": 432000, + "receivedFps": 0, + "requiredFps": null, + "maxVideoFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 132, + "maxAudioFrameGapOverallMs": 101, + "avDriftMs": 2, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": {}, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 15, + "startedAtMs": 1780978189023, + "stoppedAtMs": null, + "framesPushed": 0, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 0, + "lastPushAtMs": null, + "elapsedMs": 1264, + "producedFps": 0 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": null, + "error": null, + "participants": [] + }, + "lastVideoMeta": { + "participantSid": "PA_tmqNx963FdVV", + "trackSid": "TR_VS7wQpyDuhX3NX", + "width": 320, + "height": 180, + "timestampUs": 524374518000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_VS7wQpyDuhX3NX", + "source": "screen_share", + "kind": "video", + "bitrateKbps": 0, + "packetsLost": 0, + "fps": 10, + "width": 320, + "height": 180, + "sourceWidth": 320, + "sourceHeight": 180, + "targetBitrateKbps": 400, + "configuredFps": 15, + "targetFps": 15, + "effectiveFps": 0, + "framesProduced": 0, + "framesAccepted": 0, + "framesDropped": 0, + "framesCoalesced": 0, + "framesCaptured": 0, + "captureFailures": 0, + "maxQueueAgeMs": 0, + "maxPushLatencyMs": 0, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable", + "codec": "video/VP8" + }, + { + "trackSid": "TR_AMq6BwJ6fu5yXx", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 0, + "packetsLost": 0, + "audioLevel": 0.305, + "codec": "audio/opus" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoQueueCapacity": 8, + "outgoingVideoMaxQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 15, + "outgoingVideoPacingTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingVideoPacingMode": "source", + "outgoingVideoBusActive": false, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 23, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 2, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_tmqNx963FdVV", + "trackSid": "TR_AMq6BwJ6fu5yXx", + "kind": "audio", + "bitrateKbps": 0, + "packetsLost": 0, + "jitterMs": 0, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_tmqNx963FdVV", + "trackSid": "TR_VS7wQpyDuhX3NX", + "kind": "video", + "bitrateKbps": 0, + "packetsLost": 0, + "jitterMs": 0, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoQueueCapacity": 0, + "outgoingVideoMaxQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoPacingTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingVideoPacingMode": "idle", + "outgoingVideoBusActive": false, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/2026-06-09T0407-codex-macos-smoke/summary.json b/fluxer_desktop/native-media-reports/2026-06-09T0407-codex-macos-smoke/summary.json new file mode 100644 index 0000000..75b598a --- /dev/null +++ b/fluxer_desktop/native-media-reports/2026-06-09T0407-codex-macos-smoke/summary.json @@ -0,0 +1,227 @@ +{ + "status": "pass", + "mode": "smoke", + "strict": false, + "platform": "darwin", + "arch": "arm64", + "hostname": "Hampuss-Mac-Studio.local", + "startedAt": "2026-06-09T04:08:25.060Z", + "endedAt": "2026-06-09T04:10:06.715Z", + "reportDir": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/2026-06-09T0407-codex-macos-smoke", + "gates": [ + { + "name": "mode-valid", + "status": "pass", + "mode": "smoke" + }, + { + "name": "documentation-current", + "status": "pass", + "missing": [] + }, + { + "name": "strict-prerequisites", + "status": "skip", + "reason": "smoke mode" + }, + { + "name": "strict-command-plan", + "status": "skip", + "reason": "smoke mode" + } + ], + "plannedCommands": [ + { + "name": "main-process-native-media-unit-tests", + "command": "node --test src/main/NativeVoiceEngine.test.mjs src/main/NativeScreenCapture.test.mjs", + "category": "shared", + "cwd": "." + }, + { + "name": "desktop-typecheck", + "command": "pnpm typecheck", + "category": "shared", + "cwd": "." + }, + { + "name": "webrtc-sender-build", + "command": "pnpm --dir native/webrtc-sender build", + "category": "native-sender", + "cwd": "." + }, + { + "name": "webrtc-sender-tests", + "command": "pnpm --dir native/webrtc-sender test", + "category": "native-sender", + "cwd": "." + }, + { + "name": "webrtc-sender-livekit-harness", + "command": "pnpm --dir native/webrtc-sender test:livekit", + "category": "livekit", + "cwd": "." + }, + { + "name": "mac-screen-capture-build", + "command": "pnpm --dir native/mac-screen-capture build", + "category": "platform-native", + "cwd": "." + }, + { + "name": "mac-screen-capture-tests", + "command": "pnpm --dir native/mac-screen-capture test", + "category": "platform-native", + "cwd": "." + }, + { + "name": "mac-app-audio-build", + "command": "pnpm --dir native/mac-app-audio build", + "category": "platform-native", + "cwd": "." + }, + { + "name": "mac-app-audio-rust-tests", + "command": "pnpm --dir native/mac-app-audio test:cargo", + "category": "platform-native", + "cwd": "." + }, + { + "name": "mac-app-audio-js-tests", + "command": "'/Users/hampus/Development/fluxer/node_modules/.pnpm/node_modules/.bin/vitest' run", + "category": "platform-native", + "cwd": "native/mac-app-audio" + } + ], + "commands": [ + { + "name": "main-process-native-media-unit-tests", + "command": "node --test src/main/NativeVoiceEngine.test.mjs src/main/NativeScreenCapture.test.mjs", + "category": "shared", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-09T04:08:25.062Z", + "endedAt": "2026-06-09T04:08:25.338Z", + "durationMs": 276, + "stdoutTail": "▶ NativeScreenCapture source identity and capability reporting\n ✔ normalizes display and window sources without changing source ids (2.653541ms)\n ✔ starts display and window captures with exact source id and kind and reports diagnostics (1.257458ms)\n ✔ fails fast when native frame sink is required but unavailable (0.6175ms)\n ✔ passes caller-provided capture id and native sink handle to the platform wrapper (0.494583ms)\n ✔ reports macOS and Linux display/window capture capabilities from platform backends (0.748042ms)\n✔ NativeScreenCapture source identity and capability reporting (6.317833ms)\n▶ NativeVoiceEngine v2 bridge contract fixtures\n ✔ registers and removes every v2 IPC channel from the bridge contract fixture (2.216209ms)\n✔ NativeVoiceEngine v2 bridge contract fixtures (2.659666ms)\n▶ NativeVoiceEngine v2 IPC request fixtures\n ✔ replays fixture: valid connect reaches the native engine (0.942959ms)\n ✔ replays fixture: invalid connect args throw (0.602334ms)\n ✔ replays fixture: invalid mic enabled args return typed operation failure (0.997125ms)\n ✔ replays fixture: valid microphone publish returns typed success (0.571541ms)\n ✔ replays fixture: invalid microphone publish returns typed operation failure (0.397ms)\n ✔ replays fixture: valid zero-copy screen publish reaches native engine (0.593ms)\n ✔ replays fixture: explicit non-zero-copy screen publish is rejected (0.448708ms)\n ✔ replays fixture: hardware screen encoding without zero-copy is rejected (0.5ms)\n ✔ replays fixture: valid native screen audio publish reaches native engine (0.410292ms)\n ✔ replays fixture: invalid screen audio route throws (0.41325ms)\n ✔ replays fixture: valid camera publish reaches native engine (0.3655ms)\n ✔ replays fixture: invalid camera publish throws (0.345917ms)\n ✔ replays fixture: connected stats return normalized empty stats (0.378125ms)\n ✔ replays fixture: valid participant volume reaches native engine (0.356667ms)\n ✔ replays fixture: invalid participant volume throws (0.33175ms)\n ✔ replays fixture: valid remote subscription reaches native engine (0.372709ms)\n ✔ replays fixture: invalid remote subscription quality throws (0.322209ms)\n ✔ replays fixture: valid data publish reaches native engine (0.440584ms)\n ✔ replays fixture: invalid data payload throws (0.37625ms)\n✔ NativeVoiceEngine v2 IPC request fixtures (9.494209ms)\n▶ NativeVoiceEngine connection lifecycle\n ✔ prewarms the native voice backend once through IPC (0.402042ms)\n ✔ reuses the prewarmed native voice engine instance for connect (0.345084ms)\n ✔ prepares a fresh idle native voice engine after disconnect (0.408292ms)\n ✔ does not forward native events after disconnecting a pending connect (6.291459ms)\n ✔ disconnects stale native connect completion after a replacement connect starts (1.882667ms)\n✔ NativeVoiceEngine connection lifecycle (9.5095ms)\n▶ NativeVoiceEngine screen frame routing\n ✔ publishes screen share with source pacing and exposes a native frame sink handle (0.514834ms)\n ✔ forwards device screen share publish options to the native engine (0.381375ms)\n ✔ keeps screen-share audio published when screen video is unpublished (0.475375ms)\n ✔ replaces screen captures without releasing screen-share audio or duplicating priority (0.35675ms)\n ✔ updates active screen-share encoding over IPC (0.355959ms)\n ✔ republishes active screen-share when update changes codec or hardware mode (0.2975ms)\n ✔ forwards watch and unwatch operations with source, enablement, and quality intact (0.32775ms)\n ✔ reports VideoToolbox hardware capability from the native addon (0.26625ms)\n ✔ reports native voice engine capabilities from the native addon (0.29925ms)\n ✔ maps native addon microphone not-connected errors to the typed operation code (0.355417ms)\n✔ NativeVoiceEngine screen frame routing (3.728334ms)\nℹ tests 40\nℹ suites 5\nℹ pass 40\nℹ fail 0\nℹ cancelled 0\nℹ skipped 0\nℹ todo 0\nℹ duration_ms 207.168208\n", + "stderrTail": "" + }, + { + "name": "desktop-typecheck", + "command": "pnpm typecheck", + "category": "shared", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-09T04:08:25.339Z", + "endedAt": "2026-06-09T04:08:26.138Z", + "durationMs": 799, + "stdoutTail": "\n> fluxer_desktop@0.0.0 typecheck /Users/hampus/Development/fluxer/fluxer_desktop\n> tsgo --noEmit\n\n", + "stderrTail": "" + }, + { + "name": "webrtc-sender-build", + "command": "pnpm --dir native/webrtc-sender build", + "category": "native-sender", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-09T04:08:26.138Z", + "endedAt": "2026-06-09T04:08:46.274Z", + "durationMs": 20136, + "stdoutTail": "\n> @fluxer/webrtc-sender@0.0.0 build /Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender\n> cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon\n\n[buildRustNodeAddon] cwd=/Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender\n+ /Users/hampus/.rustup/toolchains/1.93.0-aarch64-apple-darwin/bin/cargo build --release --target aarch64-apple-darwin --manifest-path Cargo.toml --features camera-native\n+ codesign --force --sign - /Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/webrtc-sender.darwin-arm64.node\n", + "stderrTail": "warning: unused import: `enum_dispatch`\n --> vendor/libwebrtc/src/audio_source.rs:15:13\n |\n15 | use crate::{enum_dispatch, imp::audio_source as imp_as};\n | ^^^^^^^^^^^^^\n |\n = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default\n\nwarning: unnecessary `unsafe` block\n --> vendor/libwebrtc/src/native/apm.rs:32:25\n |\n32 | sys_handle: unsafe {\n | ^^^^^^ unnecessary `unsafe` block\n |\n = note: `#[warn(unused_unsafe)]` (part of `#[warn(unused)]`) on by default\n\nwarning: hiding a lifetime that's elided elsewhere is confusing\n --> vendor/libwebrtc/src/native/audio_mixer.rs:30:34\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option;\n | ^^^^^ the lifetime is elided here ^^^^^^^^^^ the same lifetime is hidden here\n |\n = help: the same lifetime is referred to in inconsistent ways, making the signature confusing\n = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default\nhelp: use `'_` for type paths\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option>;\n | ++++\n\nwarning: `libwebrtc` (lib) generated 3 warnings (run `cargo fix --lib -p libwebrtc` to apply 2 suggestions)\nwarning: struct `DmabufFrameDesc` is never constructed\n --> src/texture_source.rs:14:12\n |\n14 | pub struct DmabufFrameDesc {\n | ^^^^^^^^^^^^^^^\n |\n = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default\n\nwarning: variants `InvalidDimensions`, `UnsupportedFormat`, `InvalidPlanes`, and `SdkNativeTextureUnsupported` are never constructed\n --> src/texture_source.rs:29:5\n |\n27 | pub enum TextureEncodeError {\n | ------------------ variants in this enum\n28 | NoTexture,\n29 | InvalidDimensions,\n | ^^^^^^^^^^^^^^^^^\n30 | UnsupportedFormat,\n | ^^^^^^^^^^^^^^^^^\n31 | InvalidPlanes,\n | ^^^^^^^^^^^^^\n...\n34 | SdkNativeTextureUnsupported,\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n |\n = note: `TextureEncodeError` has derived impls for the traits `Debug` and `Clone`, but these are intentionally ignored during dead code analysis\n\nwarning: function `fourcc` is never used\n --> src/texture_source.rs:75:10\n |\n75 | const fn fourcc(bytes: [u8; 4]) -> u32 {\n | ^^^^^^\n\nwarning: constant `DRM_FORMAT_XRGB8888` is never used\n --> src/texture_source.rs:79:7\n |\n79 | const DRM_FORMAT_XRGB8888: u32 = fourcc(*b\"XR24\");\n | ^^^^^^^^^^^^^^^^^^^\n\nwarning: constant `DRM_FORMAT_ARGB8888` is never used\n --> src/texture_source.rs:80:7\n |\n80 | const DRM_FORMAT_ARGB8888: u32 = fourcc(*b\"AR24\");\n | ^^^^^^^^^^^^^^^^^^^\n\nwarning: constant `DRM_FORMAT_XBGR8888` is never used\n --> src/texture_source.rs:81:7\n |\n81 | const DRM_FORMAT_XBGR8888: u32 = fourcc(*b\"XB24\");\n | ^^^^^^^^^^^^^^^^^^^\n\nwarning: constant `DRM_FORMAT_ABGR8888` is never used\n --> src/texture_source.rs:82:7\n |\n82 | const DRM_FORMAT_ABGR8888: u32 = fourcc(*b\"AB24\");\n | ^^^^^^^^^^^^^^^^^^^\n\nwarning: constant `DRM_FORMAT_XRGB2101010` is never used\n --> src/texture_source.rs:83:7\n |\n83 | const DRM_FORMAT_XRGB2101010: u32 = fourcc(*b\"XR30\");\n | ^^^^^^^^^^^^^^^^^^^^^^\n\nwarning: constant `DRM_FORMAT_ARGB2101010` is never used\n --> src/texture_source.rs:84:7\n |\n84 | const DRM_FORMAT_ARGB2101010: u32 = fourcc(*b\"AR30\");\n | ^^^^^^^^^^^^^^^^^^^^^^\n\nwarning: constant `DRM_FORMAT_XBGR2101010` is never used\n --> src/texture_source.rs:85:7\n |\n85 | const DRM_FORMAT_XBGR2101010: u32 = fourcc(*b\"XB30\");\n | ^^^^^^^^^^^^^^^^^^^^^^\n\nwarning: constant `DRM_FORMAT_ABGR2101010` is never used\n --> src/texture_source.rs:86:7\n |\n86 | const DRM_FORMAT_ABGR2101010: u32 = fourcc(*b\"AB30\");\n | ^^^^^^^^^^^^^^^^^^^^^^\n\nwarning: constant `DRM_FORMAT_NV12` is never used\n --> src/texture_source.rs:87:7\n |\n87 | const DRM_FORMAT_NV12: u32 = fourcc(*b\"NV12\");\n | ^^^^^^^^^^^^^^^\n\nwarning: function `drm_format_supported` is never used\n --> src/texture_source.rs:89:8\n |\n89 | pub fn drm_format_supported(drm_format: u32) -> bool {\n | ^^^^^^^^^^^^^^^^^^^^\n\nwarning: constant `MAX_TEXTURE_EDGE` is never used\n --> src/texture_source.rs:104:7\n |\n104 | const MAX_TEXTURE_EDGE: u32 = 8192;\n | ^^^^^^^^^^^^^^^^\n\nwarning: function `validate_dimensions` is never used\n --> src/texture_source.rs:106:4\n |\n106 | fn validate_dimensions(width: u32, height: u32) -> Result<(), TextureEncodeError> {\n | ^^^^^^^^^^^^^^^^^^^\n\nwarning: function `validate_dmabuf_desc` is never used\n --> src/texture_source.rs:131:8\n |\n131 | pub fn validate_dmabuf_desc(desc: &DmabufFrameDesc) -> Result<(), TextureEncodeError> {\n | ^^^^^^^^^^^^^^^^^^^^\n\nwarning: function `sdk_accepts_dmabuf_texture_buffers` is never used\n --> src/texture_source.rs:244:4\n |\n244 | fn sdk_accepts_dmabuf_texture_buffers() -> bool {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nwarning: function `should_attempt_dmabuf_encode` is never used\n --> src/texture_source.rs:262:8\n |\n262 | pub fn should_attempt_dmabuf_encode(\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nwarning: function `try_publish_dmabuf` is never used\n --> src/texture_source.rs:335:12\n |\n335 | pub fn try_publish_dmabuf(\n | ^^^^^^^^^^^^^^^^^^\n\nwarning: function `publish_native_buffer` is never used\n --> src/texture_source.rs:362:8\n |\n362 | fn publish_native_buffer(source: &NativeVideoSource, buffer: NativeBuffer, timestamp_us: i64) {\n | ^^^^^^^^^^^^^^^^^^^^^\n\nwarning: variants `Bgra` and `Nv12` are never constructed\n --> src/engine.rs:256:5\n |\n255 | enum PendingVideoFrame {\n | ----------------- variants in this enum\n256 | Bgra {\n | ^^^^\n...\n264 | Nv12 {\n | ^^^^\n\nwarning: `fluxer_webrtc_sender` (lib) generated 21 warnings\n Finished `release` profile [optimized] target(s) in 0.47s\n/Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/webrtc-sender.darwin-arm64.node: replacing existing signature\n" + }, + { + "name": "webrtc-sender-tests", + "command": "pnpm --dir native/webrtc-sender test", + "category": "native-sender", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-09T04:08:46.274Z", + "endedAt": "2026-06-09T04:09:48.093Z", + "durationMs": 61819, + "stdoutTail": "_and_raw ... ok\ntest events::tests::json_u8_array_serializes_bytes_as_numbers ... ok\ntest events::tests::leaves_plain_ascii_and_unicode_untouched ... ok\ntest events::tests::raw_array_preserves_prebuilt_json_objects ... ok\ntest events::tests::track_source_strings_match_livekit_js_sources ... ok\ntest hardware_encoder::tests::publish_codec_runtime_gate_accepts_h265_when_reported_available ... ok\ntest hardware_encoder::tests::publish_codec_runtime_gate_only_requires_hardware_for_h265 ... ok\ntest hardware_encoder::tests::reports_available_nvenc_when_compiled_and_runtime_ready ... ok\ntest hardware_encoder::tests::reports_available_videotoolbox_with_h264_and_h265 ... ok\ntest hardware_encoder::tests::reports_driver_sdk_incompatibility_before_encode_session ... ok\ntest hardware_encoder::tests::reports_h264_only_when_hevc_guid_is_absent ... ok\ntest hardware_encoder::tests::reports_missing_runtime_prerequisites_when_compiled ... ok\ntest hardware_encoder::tests::reports_missing_runtime_prerequisites_when_probe_is_absent ... ok\ntest hardware_encoder::tests::reports_no_cuda_devices_using_obs_style_reason ... ok\ntest hardware_encoder::tests::reports_no_supported_codecs_when_guid_query_finds_no_fluxer_codecs ... ok\ntest hardware_encoder::tests::reports_not_compiled_before_runtime_missing ... ok\ntest hardware_encoder::tests::reports_unsupported_platforms_clearly ... ok\ntest hardware_encoder::tests::reports_videotoolbox_h264_only_when_hevc_session_is_unavailable ... ok\ntest hardware_encoder::tests::reports_videotoolbox_unavailable_with_status_details ... ok\ntest hardware_encoder::tests::reports_windows_nvenc_publish_backend_not_compiled ... ok\ntest hardware_encoder::tests::reports_windows_nvenc_runtime_dll_names_when_missing ... ok\ntest hardware_encoder::tests::reports_windows_nvenc_without_linux_zero_copy_inputs ... ok\ntest send_control::tests::audio_buffer_growth_caps_at_configured_max_and_reset_clears_pressure ... ok\ntest send_control::tests::audio_buffer_expands_on_gaps_and_shrinks_after_stability ... ok\ntest send_control::tests::audio_buffer_ignores_clock_regression_without_false_rebuffer ... ok\ntest send_control::tests::audio_buffer_max_is_clamped_to_real_time_bounds ... ok\ntest send_control::tests::idle_snapshot_reflects_audio_pressure_without_video_state ... ok\ntest send_control::tests::video_controller_continues_degrading_under_sustained_latency_pressure ... ok\ntest send_control::tests::video_controller_degrades_on_encoder_drop_pressure ... ok\ntest send_control::tests::video_controller_degrades_on_encoder_egress_pressure ... ok\ntest send_control::tests::audio_buffer_stress_stays_within_realtime_bounds_under_jitter ... ok\ntest send_control::tests::video_controller_degrades_on_latency_pressure_without_coalescing ... ok\ntest send_control::tests::video_controller_ignores_pure_coalescing_jitter ... ok\ntest send_control::tests::audio_buffer_ignores_concurrent_stale_pushes_without_moving_last_push_backwards ... ok\ntest send_control::tests::video_controller_ignores_single_encoder_egress_sample ... ok\ntest send_control::tests::video_controller_ignores_sustained_coalescing_jitter_across_windows ... ok\ntest send_control::tests::video_controller_keeps_requested_fps_when_adaptive_send_is_disabled ... ok\ntest send_control::tests::video_controller_preserves_pressure_reason_while_degraded_but_stable ... ok\ntest send_control::tests::video_controller_recovers_after_stable_windows ... ok\ntest send_control::tests::video_controller_recovers_from_minimum_in_two_stable_steps ... ok\ntest send_control::tests::video_controller_requires_a_full_stable_recovery_window ... ok\ntest send_control::tests::video_stats_effective_fps_uses_capture_time_bounds_for_out_of_order_records ... ok\ntest send_control::tests::video_stats_snapshot_counts_coalescing_drops_failures_and_effective_fps ... ok\ntest send_control::tests::video_controller_stress_keeps_target_inside_configured_bounds ... ok\ntest stats::tests::bitrate_computes_kbps_from_byte_delta ... ok\ntest stats::tests::bitrate_first_sample_is_zero ... ok\ntest stats::tests::bitrate_half_second_doubles_rate ... ok\ntest stats::tests::bitrate_rejects_backwards_time_and_bytes ... ok\ntest stats::tests::empty_stats_serialise_to_null_rtt_and_empty_arrays ... ok\ntest stats::tests::sanitize_audio_level_clamps ... ok\ntest stats::tests::full_stats_serialise_to_exact_contract_shape ... ok\ntest stats::tests::sanitize_kbps_drops_nan_inf_negative_and_rounds ... ok\ntest send_control::tests::video_stats_handles_concurrent_recording_without_lost_counts ... ok\ntest stats::tests::send_health_serialises_to_exact_contract_shape ... ok\ntest stats::tests::unit_conversions_seconds_to_ms ... ok\ntest stats::tests::video_inbound_omits_audio_level_audio_omits_fps ... ok\ntest texture_source::tests::dmabuf_desc_from_parts_populates_all_plane_arrays ... ok\ntest texture_source::tests::dmabuf_desc_from_parts_rejects_incomplete_native_inputs ... ok\ntest texture_source::tests::dmabuf_desc_from_parts_rejects_negative_fds ... ok\ntest texture_source::tests::drm_format_gate_accepts_obs_vkcapture_texture_formats ... ok\ntest texture_source::tests::dxgi_format_gate_accepts_8bit_rgba_bgra_only ... ok\ntest texture_source::tests::error_strings_are_stable ... ok\ntest texture_source::tests::probe_is_available_only_for_explicit_hardware_codecs ... ok\ntest texture_source::tests::should_attempt_dmabuf_is_sdk_gated_after_validation_capability ... ok\ntest texture_source::tests::should_attempt_falls_back_when_capability_unavailable ... ok\ntest texture_source::tests::should_attempt_validates_frame_when_capability_available ... ok\ntest texture_source::tests::validate_accepts_a_clean_bgra_texture ... ok\ntest texture_source::tests::validate_dmabuf_accepts_supported_formats_with_optional_uuid ... ok\ntest texture_source::tests::validate_dmabuf_rejects_invalid_planes ... ok\ntest texture_source::tests::validate_precedence_handle_before_dims_before_format ... ok\ntest texture_source::tests::validate_rejects_odd_zero_and_oversized_dims ... ok\ntest texture_source::tests::validate_rejects_unsupported_format ... ok\ntest texture_source::tests::validate_rejects_zero_handle_as_no_texture ... ok\ntest yuv::tests::bgra_black_white_grey_levels ... ok\ntest engine::tests::pcm16_audio_frame_rejects_implausible_format_and_overlong_chunks ... ok\ntest yuv::tests::bgra_golden_2x2_solid_colour_exact_bytes ... ok\ntest yuv::tests::bgra_golden_strided_2x2_skips_row_padding ... ok\ntest yuv::tests::bgra_plane_sizes ... ok\ntest yuv::tests::bgra_primaries_have_expected_chroma_signs ... ok\ntest yuv::tests::copy_nv12_planes_preserves_nv12_layout ... ok\ntest yuv::tests::copy_nv12_planes_rejects_short_buffers ... ok\ntest yuv::tests::copy_nv12_planes_respects_destination_stride ... ok\ntest yuv::tests::nv12_4x4_deinterleaves_two_chroma_columns ... ok\ntest yuv::tests::nv12_golden_4x2_packed_to_i420 ... ok\ntest yuv::tests::nv12_packed_2x2_deinterleaves ... ok\ntest yuv::tests::nv12_respects_row_padding ... ok\ntest yuv::tests::nv12_short_buffer_is_rejected ... ok\ntest yuv::tests::rejects_odd_or_tiny_dimensions ... ok\ntest yuv::tests::rgb_golden_2x2_solid_colour_matches_bgra_path ... ok\ntest yuv::tests::rgb_plane_sizes_and_levels ... ok\ntest yuv::tests::rgb_rejects_odd_dims_and_short_buffer ... ok\n\ntest result: ok. 144 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n\nrunning 6 tests\ntest video_frame_meta_matches_contract ... ok\ntest outbound_fixtures_match_contract ... ok\ntest inbound_covers_subscribe_and_lifecycle ... ok\ntest outbound_covers_the_single_identity_publish_path ... ok\ntest inbound_fixtures_match_contract ... ok\ntest e2ee_state_is_ok_on_the_wire ... ok\n\ntest result: ok. 6 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n▶ webrtc-sender loader wrapper\n ✔ resolves the native addon filename for every desktop OS/arch target (0.586ms)\n ✔ rejects unsupported platform/architecture pairs explicitly (0.172042ms)\n ✔ returns a native hardware encoder capability when the binding exports one (1.042375ms)\n ✔ delegates dropped video callback metrics through the VoiceEngine wrapper (0.114833ms)\n ✔ reports VoiceEngine feature capabilities from the wrapped native prototype (0.322209ms)\n ✔ delegates native voice engine prewarm when the binding exports it (0.055542ms)\n ✔ delegates device microphone publish through the VoiceEngine wrapper (0.10525ms)\n ✔ delegates native camera devices and publish options through the VoiceEngine wrapper (0.117583ms)\n ✔ delegates device screen-share publish through the VoiceEngine wrapper (0.119541ms)\n ✔ reports device screen-share publish as unavailable for older native bindings (0.114084ms)\n ✔ delegates screen-share simulcast selection through the VoiceEngine wrapper (0.104083ms)\n ✔ delegates connect options through the VoiceEngine wrapper (0.10775ms)\n ✔ delegates native screen frame sink handle creation through the VoiceEngine wrapper (0.060958ms)\n ✔ treats native screen frame sink handles as unavailable for older voice bindings (0.065791ms)\n ✔ defaults dropped video callback metrics to zero for older native bindings (0.050375ms)\n ✔ delegates remote track subscription updates through the VoiceEngine wrapper (0.071958ms)\n ✔ ignores remote track subscription updates for older native bindings (0.054042ms)\n ✔ delegates data packets through the VoiceEngine wrapper (0.129167ms)\n ✔ rejects invalid data packet payloads in the wrapper (0.146916ms)\n ✔ returns an unavailable hardware encoder capability without a binding (0.039833ms)\n✔ webrtc-sender loader wrapper (4.374833ms)\n▶ livekit harness setup matrix\n ✔ maps all supported codec spellings to expected mime types (1.00325ms)\n ✔ parses codec lists with fallback and rejects empty effective lists (0.878375ms)\n ✔ default non-strict config keeps the fast local live setup small (0.956958ms)\n ✔ strict config expands to multi-codec secondary-publisher scenarios (0.648125ms)\n ✔ stress knobs parse packet loss, resolution stability, audio gap, and video pattern (0.379917ms)\n ✔ stress knobs reject impossible send pacing configurations (1.902ms)\n ✔ explicit secondary codec expectations override rotating defaults (0.287792ms)\n ✔ secondary screen codec aliases are accepted and conflicting aliases are rejected (0.592416ms)\n ✔ external token setup derives identities, requires matching tokens, and redacts reports (0.933834ms)\n ✔ report sanitization strips URL credentials, query strings, and fragments (0.452459ms)\n ✔ jwt subject and TCP target parsing catch malformed live setups early (0.230125ms)\n ✔ admin token has the room-scoped grant needed for server publishing validation (0.303459ms)\n ✔ scenario room names are stable and bounded for generated-token suites (0.07075ms)\n ✔ strict report checks fail when the strict window has too few samples to measure gaps (137.344709ms)\n ✔ strict report checks use only samples inside the measured window (6.192458ms)\n ✔ strict packet loss gate uses loss deltas inside the strict window (10.880291ms)\n ✔ server publishing checks cover primary and secondary screen, audio, and camera tracks (0.760666ms)\n ✔ server publishing checks fail closed on missing server tracks (0.331583ms)\n ✔ server publishing checks reject wrong participant identity (0.246792ms)\n ✔ server publishing checks reject wrong kind, source, and screen mime (0.27275ms)\n ✔ server publishing checks reject muted or missing mute state on expected tracks (0.213459ms)\n ✔ server publishing checks handle protobuf numeric enums without accepting data tracks as video (0.200917ms)\n ✔ server publishing checks fail closed on missing secondary, audio, camera, and screen-audio tracks (0.217459ms)\n ✔ strict server publishing validation does not pass open on Twirp errors (0.242125ms)\n✔ livekit harness setup matrix (166.608375ms)\nℹ tests 44\nℹ suites 2\nℹ pass 44\nℹ fail 0\nℹ cancelled 0\nℹ skipped 0\nℹ todo 0\nℹ duration_ms 973.929333\n", + "stderrTail": "segmentation v1.13.2\n Compiling convert_case v0.11.0\n Compiling image v0.25.10\n Compiling cxx-build v1.0.194\n Compiling flume v0.11.1\n Compiling core-media-sys v0.1.2\n Compiling napi v3.9.0\n Compiling thiserror-impl v1.0.69\n Compiling objc v0.2.7\n Compiling cocoa v0.20.2\n Compiling cocoa-foundation v0.2.1\n Compiling darling_core v0.14.4\n Compiling block2 v0.6.2\n Compiling objc2-core-foundation v0.3.2\n Compiling libloading v0.9.0\n Compiling ctor v1.0.7\n Compiling anstyle v1.0.14\n Compiling metal v0.18.0\n Compiling parking_lot v0.12.5\n Compiling objc2-foundation v0.3.2\n Compiling der v0.7.10\n Compiling darling_macro v0.14.4\n Compiling clap_lex v1.1.0\n Compiling darling v0.14.4\n Compiling yoke v0.8.2\n Compiling crypto-common v0.1.6\n Compiling block-buffer v0.10.4\n Compiling crypto-bigint v0.5.5\n Compiling digest v0.10.7\n Compiling spki v0.7.3\n Compiling hmac v0.12.1\n Compiling signature v2.2.0\n Compiling pkcs8 v0.10.2\n Compiling hkdf v0.12.4\n Compiling sha2 v0.10.9\n Compiling rfc6979 v0.4.0\n Compiling sec1 v0.7.3\n Compiling pkcs1 v0.7.5\n Compiling ed25519 v2.2.3\n Compiling sha1 v0.10.6\n Compiling core-video-sys v0.1.4\n Compiling ciborium-io v0.2.2\n Compiling rtrb v0.3.4\n Compiling livekit v0.7.43\n Compiling clap_builder v4.6.0\n Compiling fluxer_gpu_rebuild v0.0.0 (/Users/hampus/Development/fluxer/fluxer_desktop/native/gpu-rebuild)\n Compiling elliptic-curve v0.13.8\n Compiling napi-sys v3.2.1\n Compiling napi-derive-backend v5.0.4\n Compiling ecdsa v0.16.9\n Compiling primeorder v0.13.6\n Compiling device-info v0.1.1\n Compiling p384 v0.13.1\n Compiling p256 v0.13.2\n Compiling fluxer_webrtc_sender v0.0.0 (/Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender)\n Compiling itertools v0.13.0\n Compiling libloading v0.8.9\n Compiling same-file v1.0.6\n Compiling nohash-hasher v0.2.0\n Compiling rustc-hash v2.1.2\n Compiling cast v0.3.0\n Compiling walkdir v2.5.0\n Compiling clap v4.6.1\n Compiling prost-types v0.12.6\n Compiling futures-executor v0.3.32\n Compiling fluxer_screen_frame_bus v0.0.0 (/Users/hampus/Development/fluxer/fluxer_desktop/native/screen-frame-bus)\n Compiling fluxer_desktop_native v0.1.0 (/Users/hampus/Development/fluxer/fluxer_desktop/native/rust)\n Compiling futures v0.3.32\n Compiling oorandom v11.1.5\n Compiling anes v0.1.6\n Compiling criterion-plot v0.6.0\n Compiling napi-derive v3.5.6\n Compiling prost-build v0.12.6\n Compiling pbjson-build v0.6.2\n Compiling tokio-util v0.7.18\n Compiling pbjson v0.6.0\n Compiling tokio-stream v0.1.18\n Compiling bmrng v0.5.2\n Compiling tinytemplate v1.2.1\n Compiling livekit-runtime v0.4.0\n Compiling pbjson-types v0.6.0\n Compiling ppv-lite86 v0.2.21\n Compiling ed25519-dalek v2.2.0\n Compiling half v2.7.1\n Compiling rand_chacha v0.9.0\n Compiling rand_chacha v0.3.1\n Compiling rustls-webpki v0.103.13\n Compiling ciborium-ll v0.2.2\n Compiling rand v0.9.4\n Compiling rand v0.8.6\n Compiling ciborium v0.2.2\n Compiling criterion v0.7.0\n Compiling crossbeam-queue v0.3.12\n Compiling rsa v0.9.10\n Compiling mozjpeg v0.10.13\n Compiling jsonwebtoken v10.4.0\n Compiling nokhwa-core v0.1.9\n Compiling from_variants_impl v1.0.2\n Compiling nokhwa-bindings-linux v0.1.4\n Compiling nokhwa-bindings-windows v0.4.6\n Compiling from_variants v1.0.2\n Compiling zerovec v0.11.6\n Compiling zerotrie v0.2.4\n Compiling nokhwa v0.10.11\n Compiling os_info v3.15.0\n Compiling tokio-rustls v0.26.4\n Compiling hyper-rustls v0.27.9\n Compiling livekit-protocol v0.7.8\n Compiling tinystr v0.8.3\n Compiling potential_utf v0.1.5\n Compiling icu_collections v2.2.0\n Compiling icu_locale_core v2.2.0\n Compiling icu_provider v2.2.0\n Compiling icu_properties v2.2.0\n Compiling icu_normalizer v2.2.0\n Compiling idna_adapter v1.2.2\n Compiling idna v1.1.0\n Compiling url v2.5.8\n Compiling tower-http v0.6.11\n Compiling tungstenite v0.29.0\n Compiling reqwest v0.12.28\n Compiling tokio-tungstenite v0.29.0\n Compiling webrtc-sys-build v0.3.18\n Compiling webrtc-sys v0.3.33 (/Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys)\n Compiling livekit-datatrack v0.1.8\n Compiling livekit-api v0.5.0\n Compiling libwebrtc v0.3.35 (/Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc)\nwarning: unused import: `enum_dispatch`\n --> vendor/libwebrtc/src/audio_source.rs:15:13\n |\n15 | use crate::{enum_dispatch, imp::audio_source as imp_as};\n | ^^^^^^^^^^^^^\n |\n = note: `#[warn(unused_imports)]` (part of `#[warn(unused)]`) on by default\n\nwarning: unnecessary `unsafe` block\n --> vendor/libwebrtc/src/native/apm.rs:32:25\n |\n32 | sys_handle: unsafe {\n | ^^^^^^ unnecessary `unsafe` block\n |\n = note: `#[warn(unused_unsafe)]` (part of `#[warn(unused)]`) on by default\n\nwarning: hiding a lifetime that's elided elsewhere is confusing\n --> vendor/libwebrtc/src/native/audio_mixer.rs:30:34\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option;\n | ^^^^^ the lifetime is elided here ^^^^^^^^^^ the same lifetime is hidden here\n |\n = help: the same lifetime is referred to in inconsistent ways, making the signature confusing\n = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default\nhelp: use `'_` for type paths\n |\n30 | fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option>;\n | ++++\n\nwarning: `libwebrtc` (lib) generated 3 warnings (run `cargo fix --lib -p libwebrtc` to apply 2 suggestions)\nwarning: struct `DmabufFrameDesc` is never constructed\n --> src/texture_source.rs:14:12\n |\n14 | pub struct DmabufFrameDesc {\n | ^^^^^^^^^^^^^^^\n |\n = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default\n\nwarning: variants `InvalidDimensions`, `UnsupportedFormat`, `InvalidPlanes`, and `SdkNativeTextureUnsupported` are never constructed\n --> src/texture_source.rs:29:5\n |\n27 | pub enum TextureEncodeError {\n | ------------------ variants in this enum\n28 | NoTexture,\n29 | InvalidDimensions,\n | ^^^^^^^^^^^^^^^^^\n30 | UnsupportedFormat,\n | ^^^^^^^^^^^^^^^^^\n31 | InvalidPlanes,\n | ^^^^^^^^^^^^^\n...\n34 | SdkNativeTextureUnsupported,\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n |\n = note: `TextureEncodeError` has derived impls for the traits `Debug` and `Clone`, but these are intentionally ignored during dead code analysis\n\nwarning: function `fourcc` is never used\n --> src/texture_source.rs:75:10\n |\n75 | const fn fourcc(bytes: [u8; 4]) -> u32 {\n | ^^^^^^\n\nwarning: constant `DRM_FORMAT_XRGB8888` is never used\n --> src/texture_source.rs:79:7\n |\n79 | const DRM_FORMAT_XRGB8888: u32 = fourcc(*b\"XR24\");\n | ^^^^^^^^^^^^^^^^^^^\n\nwarning: constant `DRM_FORMAT_ARGB8888` is never used\n --> src/texture_source.rs:80:7\n |\n80 | const DRM_FORMAT_ARGB8888: u32 = fourcc(*b\"AR24\");\n | ^^^^^^^^^^^^^^^^^^^\n\nwarning: constant `DRM_FORMAT_XBGR8888` is never used\n --> src/texture_source.rs:81:7\n |\n81 | const DRM_FORMAT_XBGR8888: u32 = fourcc(*b\"XB24\");\n | ^^^^^^^^^^^^^^^^^^^\n\nwarning: constant `DRM_FORMAT_ABGR8888` is never used\n --> src/texture_source.rs:82:7\n |\n82 | const DRM_FORMAT_ABGR8888: u32 = fourcc(*b\"AB24\");\n | ^^^^^^^^^^^^^^^^^^^\n\nwarning: constant `DRM_FORMAT_XRGB2101010` is never used\n --> src/texture_source.rs:83:7\n |\n83 | const DRM_FORMAT_XRGB2101010: u32 = fourcc(*b\"XR30\");\n | ^^^^^^^^^^^^^^^^^^^^^^\n\nwarning: constant `DRM_FORMAT_ARGB2101010` is never used\n --> src/texture_source.rs:84:7\n |\n84 | const DRM_FORMAT_ARGB2101010: u32 = fourcc(*b\"AR30\");\n | ^^^^^^^^^^^^^^^^^^^^^^\n\nwarning: constant `DRM_FORMAT_XBGR2101010` is never used\n --> src/texture_source.rs:85:7\n |\n85 | const DRM_FORMAT_XBGR2101010: u32 = fourcc(*b\"XB30\");\n | ^^^^^^^^^^^^^^^^^^^^^^\n\nwarning: constant `DRM_FORMAT_ABGR2101010` is never used\n --> src/texture_source.rs:86:7\n |\n86 | const DRM_FORMAT_ABGR2101010: u32 = fourcc(*b\"AB30\");\n | ^^^^^^^^^^^^^^^^^^^^^^\n\nwarning: constant `DRM_FORMAT_NV12` is never used\n --> src/texture_source.rs:87:7\n |\n87 | const DRM_FORMAT_NV12: u32 = fourcc(*b\"NV12\");\n | ^^^^^^^^^^^^^^^\n\nwarning: function `drm_format_supported` is never used\n --> src/texture_source.rs:89:8\n |\n89 | pub fn drm_format_supported(drm_format: u32) -> bool {\n | ^^^^^^^^^^^^^^^^^^^^\n\nwarning: constant `MAX_TEXTURE_EDGE` is never used\n --> src/texture_source.rs:104:7\n |\n104 | const MAX_TEXTURE_EDGE: u32 = 8192;\n | ^^^^^^^^^^^^^^^^\n\nwarning: function `validate_dimensions` is never used\n --> src/texture_source.rs:106:4\n |\n106 | fn validate_dimensions(width: u32, height: u32) -> Result<(), TextureEncodeError> {\n | ^^^^^^^^^^^^^^^^^^^\n\nwarning: function `validate_dmabuf_desc` is never used\n --> src/texture_source.rs:131:8\n |\n131 | pub fn validate_dmabuf_desc(desc: &DmabufFrameDesc) -> Result<(), TextureEncodeError> {\n | ^^^^^^^^^^^^^^^^^^^^\n\nwarning: function `sdk_accepts_dmabuf_texture_buffers` is never used\n --> src/texture_source.rs:244:4\n |\n244 | fn sdk_accepts_dmabuf_texture_buffers() -> bool {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nwarning: function `should_attempt_dmabuf_encode` is never used\n --> src/texture_source.rs:262:8\n |\n262 | pub fn should_attempt_dmabuf_encode(\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nwarning: function `try_publish_dmabuf` is never used\n --> src/texture_source.rs:335:12\n |\n335 | pub fn try_publish_dmabuf(\n | ^^^^^^^^^^^^^^^^^^\n\nwarning: function `publish_native_buffer` is never used\n --> src/texture_source.rs:362:8\n |\n362 | fn publish_native_buffer(source: &NativeVideoSource, buffer: NativeBuffer, timestamp_us: i64) {\n | ^^^^^^^^^^^^^^^^^^^^^\n\nwarning: variants `Bgra` and `Nv12` are never constructed\n --> src/engine.rs:256:5\n |\n255 | enum PendingVideoFrame {\n | ----------------- variants in this enum\n256 | Bgra {\n | ^^^^\n...\n264 | Nv12 {\n | ^^^^\n\nwarning: variant `Failed` is never constructed\n --> src/config.rs:62:5\n |\n57 | pub enum PublisherState {\n | -------------- variant in this enum\n...\n62 | Failed,\n | ^^^^^^\n |\n = note: `PublisherState` has derived impls for the traits `Debug` and `Clone`, but these are intentionally ignored during dead code analysis\n = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default\n\nwarning: static `PREWARMED_LIVEKIT_RUNTIME` is never used\n --> src/engine.rs:102:8\n |\n102 | static PREWARMED_LIVEKIT_RUNTIME: LazyLock>>> =\n | ^^^^^^^^^^^^^^^^^^^^^^^^^\n\nwarning: function `prewarm_voice_engine` is never used\n --> src/engine.rs:203:8\n |\n203 | pub fn prewarm_voice_engine() -> napi::Result<()> {\n | ^^^^^^^^^^^^^^^^^^^^\n\nwarning: function `prewarm_voice_engine_inner` is never used\n --> src/engine.rs:212:4\n |\n212 | fn prewarm_voice_engine_inner() -> napi::Result<()> {\n | ^^^^^^^^^^^^^^^^^^^^^^^^^^\n\nwarning: variant `Nv12` is never constructed\n --> src/engine.rs:264:5\n |\n255 | enum PendingVideoFrame {\n | ----------------- variant in this enum\n...\n264 | Nv12 {\n | ^^^^\n\nwarning: `fluxer_webrtc_sender` (lib test) generated 9 warnings (4 duplicates)\nwarning: `fluxer_webrtc_sender` (lib) generated 21 warnings\n Finished `test` profile [unoptimized + debuginfo] target(s) in 58.51s\n Running unittests src/lib.rs (target/debug/deps/fluxer_webrtc_sender-986568c9de5e7b47)\n Running tests/event_contract.rs (target/debug/deps/event_contract-ed4cabbef0fb9f7c)\n Doc-tests fluxer_webrtc_sender\n" + }, + { + "name": "webrtc-sender-livekit-harness", + "command": "pnpm --dir native/webrtc-sender test:livekit", + "category": "livekit", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-09T04:09:48.093Z", + "endedAt": "2026-06-09T04:09:50.341Z", + "durationMs": 2248, + "stdoutTail": "\n> @fluxer/webrtc-sender@0.0.0 test:livekit /Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender\n> node scripts/livekit-harness.mjs\n\n[livekit-harness] connecting to ws://localhost:7880 room=fluxer-webrtc-harness-39291-5ee61a43-3009-4112-bca3-ae4b4667a371\n[livekit-harness] PASS: 19 checks, inboundAudioFrames=72, inboundVideoFrames=5, videoCallbacks=5, receivedFps=0, producedFps=0, maxVideoFrameGapMs=0, maxAudioFrameGapMs=0, maxObservedPacketLoss=0, maxObservedPacketLossDelta=0, resolutionMismatches=0, droppedVideoFrameCallbacks=0\n", + "stderrTail": "" + }, + { + "name": "mac-screen-capture-build", + "command": "pnpm --dir native/mac-screen-capture build", + "category": "platform-native", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-09T04:09:50.341Z", + "endedAt": "2026-06-09T04:09:55.416Z", + "durationMs": 5075, + "stdoutTail": "\n> @fluxer/mac-screen-capture@0.0.0 build /Users/hampus/Development/fluxer/fluxer_desktop/native/mac-screen-capture\n> cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon\n\n[buildRustNodeAddon] cwd=/Users/hampus/Development/fluxer/fluxer_desktop/native/mac-screen-capture\n+ /Users/hampus/.rustup/toolchains/1.93.0-aarch64-apple-darwin/bin/cargo build --release --target aarch64-apple-darwin --manifest-path Cargo.toml\n+ codesign --force --sign - /Users/hampus/Development/fluxer/fluxer_desktop/native/mac-screen-capture/mac-screen-capture.darwin-arm64.node\n", + "stderrTail": " Finished `release` profile [optimized] target(s) in 0.33s\n/Users/hampus/Development/fluxer/fluxer_desktop/native/mac-screen-capture/mac-screen-capture.darwin-arm64.node: replacing existing signature\n" + }, + { + "name": "mac-screen-capture-tests", + "command": "pnpm --dir native/mac-screen-capture test", + "category": "platform-native", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-09T04:09:55.416Z", + "endedAt": "2026-06-09T04:09:59.295Z", + "durationMs": 3879, + "stdoutTail": "\n> @fluxer/mac-screen-capture@0.0.0 test /Users/hampus/Development/fluxer/fluxer_desktop/native/mac-screen-capture\n> pnpm test:cargo && node --test index.test.mjs\n\n\n> @fluxer/mac-screen-capture@0.0.0 test:cargo /Users/hampus/Development/fluxer/fluxer_desktop/native/mac-screen-capture\n> cargo test --manifest-path Cargo.toml\n\n\nrunning 78 tests\ntest audio_pool::tests::append_concatenates_planar_to_interleaved_like_payload ... ok\ntest audio_pool::tests::as_mut_ptr_returns_slot_base_address ... ok\ntest audio_pool::tests::append_rejects_overflow ... ok\ntest audio_pool::tests::acquire_release_cycle_increments_counters ... ok\ntest audio_pool::tests::capacity_one_pool_round_trips ... ok\ntest audio_pool::tests::default_pool_dimensions_match_constants ... ok\ntest audio_pool::tests::drop_returns_slot_to_free_list ... ok\ntest audio_pool::tests::into_external_parts_drop_returns_slot_to_pool ... ok\ntest audio_pool::tests::into_external_parts_exposes_filled_pointer_and_length ... ok\ntest audio_pool::tests::new_rejects_bytes_per_slot_above_max ... ok\ntest audio_pool::tests::new_rejects_zero_bytes_per_slot ... ok\ntest audio_pool::tests::new_rejects_zero_capacity ... ok\ntest audio_pool::tests::into_external_parts_holds_slot_across_send ... ok\ntest audio_pool::tests::pool_exhausts_at_cap_seventeenth_acquire_returns_none ... ok\ntest audio_pool::tests::recycled_slot_persists_buffer_storage_until_overwritten ... ok\ntest audio_pool::tests::stats_in_flight_matches_simultaneous_holders ... ok\ntest audio_pool::tests::write_rejects_payload_larger_than_slot ... ok\ntest audio_pool::tests::write_then_data_slice_matches_payload ... ok\ntest config::tests::audio_sample_format_strings_and_widths ... ok\ntest config::tests::builder_accepts_48khz_stereo_audio ... ok\ntest config::tests::builder_accepts_fps_120 ... ok\ntest config::tests::builder_accepts_hdr_with_displayp3 ... ok\ntest config::tests::builder_captures_audio_toggle_preserves_frame_interval ... ok\ntest config::tests::builder_defaults_produce_valid_config ... ok\ntest config::tests::builder_rejects_audio_channel_count_zero ... ok\ntest config::tests::builder_rejects_fps_above_max ... ok\ntest config::tests::builder_rejects_audio_sample_rate_zero ... ok\ntest config::tests::builder_rejects_fps_zero ... ok\ntest config::tests::builder_rejects_hdr_without_displayp3 ... ok\ntest config::tests::builder_rejects_queue_depth_above_max ... ok\ntest config::tests::builder_rejects_queue_depth_zero ... ok\ntest config::tests::captures_audio_defaults_to_false ... ok\ntest config::tests::default_config_validates ... ok\ntest config::tests::color_space_names_present ... ok\ntest config::tests::failure_reason_strings_non_empty ... ok\ntest config::tests::mac_screen_share_audio_frame_constructs_valid ... ok\ntest config::tests::mac_screen_share_audio_frame_rejects_invalid_sample_rate ... ok\ntest config::tests::mac_screen_share_audio_frame_rejects_too_many_channels ... ok\ntest config::tests::mac_screen_share_audio_frame_with_bytes_rejects_bad_sample_rate ... ok\ntest config::tests::mac_screen_share_audio_frame_with_bytes_round_trip ... ok\ntest config::tests::minimum_frame_interval_120_fps_under_8_3ms ... ok\ntest config::tests::minimum_frame_interval_30_fps_factor_applied ... ok\ntest config::tests::minimum_frame_interval_60_fps_is_15_ms ... ok\ntest config::tests::pixel_format_fourcc_matches_obs_constants ... ok\ntest iosurface_pair::tests::new_pair_is_empty ... ok\ntest iosurface_pair::tests::take_current_on_empty_returns_none ... ok\ntest napi_surface_macos::audio_extract_tests::ablstorage_layout_matches_apple_abl ... ok\ntest napi_surface_macos::audio_extract_tests::ablstorage_size_is_large_enough_for_audio_buffer_list ... ok\ntest napi_surface_macos::audio_extract_tests::extract_audio_bytes_rejects_metadata_sample_buffer_gracefully ... ok\ntest napi_surface_macos::dispatch_queue_tests::audio_frame_payload_into_input_carries_slot_and_metadata ... ok\ntest napi_surface_macos::dispatch_queue_tests::build_capture_config_carries_audio_settings ... ok\ntest napi_surface_macos::dispatch_queue_tests::build_capture_config_preserves_frame_interval_when_audio_enabled ... ok\ntest napi_surface_macos::dispatch_queue_tests::classify_pcm_flags_detects_f32_interleaved_as_f32_planar ... ok\ntest napi_surface_macos::dispatch_queue_tests::classify_pcm_flags_detects_f32_planar ... ok\ntest napi_surface_macos::dispatch_queue_tests::classify_pcm_flags_detects_i16_interleaved ... ok\ntest napi_surface_macos::dispatch_queue_tests::classify_pcm_flags_rejects_unknown ... ok\ntest napi_surface_macos::dispatch_queue_tests::sample_queue_construction_does_not_trap_libdispatch ... ok\ntest napi_surface_macos::external_buffer_tests::allocator_counter_self_test_reports_nonzero_when_allocating ... ok\ntest napi_surface_macos::external_buffer_tests::audio_buffer_external_pointer_round_trip ... ok\ntest audio_pool::tests::multi_thread_acquire_release_stress_8_threads_1000_ops_no_deadlock ... ok\ntest napi_surface_macos::external_buffer_tests::external_buffer_finalize_runs_exactly_once_per_frame ... ok\ntest napi_surface_macos::external_buffer_tests::buffer_external_drop_fn_runs_on_pool_thread_or_napi_thread ... ok\ntest napi_surface_macos::external_buffer_tests::multi_frame_external_buffer_lifecycle ... ok\ntest napi_surface_macos::external_buffer_tests::pool_exhaustion_with_external_buffers_holds_diagnostic ... ok\ntest os_version::tests::classify_macos_11_is_unsupported ... ok\ntest os_version::tests::classify_macos_12_3_is_supported ... ok\ntest os_version::tests::classify_unknown_version_is_unsupported ... ok\ntest os_version::tests::format_version_trims_zero_patch ... ok\ntest os_version::tests::meets_floor_exact_match ... ok\ntest os_version::tests::meets_floor_higher_major ... ok\ntest os_version::tests::rejects_below_floor ... ok\ntest napi_surface_macos::external_buffer_tests::zero_allocation_in_steady_state_acquire_write_external_release ... ok\ntest napi_surface_macos::dispatch_queue_tests::parallel_sample_queue_construction_is_safe ... ok\ntest napi_surface_macos::dispatch_queue_tests::sample_queue_dispatches_blocks_without_crashing ... ok\ntest iosurface_pair::macos_tests::push_increments_use_count_and_take_current_does_not_release ... ok\ntest iosurface_pair::macos_tests::push_rotates_and_drop_releases_both_slots ... ok\ntest iosurface_pair::macos_tests::clear_releases_both_slots ... ok\ntest iosurface_pair::macos_tests::third_push_evicts_oldest_and_releases_it ... ok\n\ntest result: ok. 78 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.03s\n\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n▶ mac-screen-capture loader wrapper\n ✔ forwards source id, kind, and dimensions to native binding (1.445ms)\n ✔ defaults sourceKind to screen and frameRate to 30 (0.117166ms)\n ✔ forwards display and window sources from native binding without rewriting ids (0.12875ms)\n ✔ reports ScreenCaptureKit support and permission from native binding (0.612667ms)\n ✔ installs a native frame sink handle once before start (0.174542ms)\n ✔ fails before native start when a native frame sink is required but missing (0.416709ms)\n ✔ emits closed once when native lifecycle closes and stop is called later (0.153417ms)\n ✔ lifecycle error emits Error event (0.120416ms)\n✔ mac-screen-capture loader wrapper (3.986375ms)\nℹ tests 8\nℹ suites 1\nℹ pass 8\nℹ fail 0\nℹ cancelled 0\nℹ skipped 0\nℹ todo 0\nℹ duration_ms 371.859541\n", + "stderrTail": " Compiling fluxer_gpu_rebuild v0.0.0 (/Users/hampus/Development/fluxer/fluxer_desktop/native/gpu-rebuild)\n Compiling fluxer_screen_frame_bus v0.0.0 (/Users/hampus/Development/fluxer/fluxer_desktop/native/screen-frame-bus)\n Compiling fluxer_mac_screen_capture v0.0.0 (/Users/hampus/Development/fluxer/fluxer_desktop/native/mac-screen-capture)\n Finished `test` profile [unoptimized + debuginfo] target(s) in 2.23s\n Running unittests src/lib.rs (target/debug/deps/fluxer_mac_screen_capture-7d8f5a432df33d79)\n Doc-tests fluxer_mac_screen_capture\n" + }, + { + "name": "mac-app-audio-build", + "command": "pnpm --dir native/mac-app-audio build", + "category": "platform-native", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-09T04:09:59.295Z", + "endedAt": "2026-06-09T04:10:03.149Z", + "durationMs": 3854, + "stdoutTail": "\n> @fluxer/mac-app-audio@0.0.0 build /Users/hampus/Development/fluxer/fluxer_desktop/native/mac-app-audio\n> cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon\n\n[buildRustNodeAddon] cwd=/Users/hampus/Development/fluxer/fluxer_desktop/native/mac-app-audio\n+ /Users/hampus/.rustup/toolchains/1.93.0-aarch64-apple-darwin/bin/cargo build --release --target aarch64-apple-darwin --manifest-path Cargo.toml\n+ codesign --force --sign - /Users/hampus/Development/fluxer/fluxer_desktop/native/mac-app-audio/mac-app-audio.darwin-arm64.node\n", + "stderrTail": " Finished `release` profile [optimized] target(s) in 0.27s\n/Users/hampus/Development/fluxer/fluxer_desktop/native/mac-app-audio/mac-app-audio.darwin-arm64.node: replacing existing signature\n" + }, + { + "name": "mac-app-audio-rust-tests", + "command": "pnpm --dir native/mac-app-audio test:cargo", + "category": "platform-native", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-09T04:10:03.149Z", + "endedAt": "2026-06-09T04:10:05.465Z", + "durationMs": 2316, + "stdoutTail": "\n> @fluxer/mac-app-audio@0.0.0 test:cargo /Users/hampus/Development/fluxer/fluxer_desktop/native/mac-app-audio\n> cargo test --manifest-path Cargo.toml\n\n\nrunning 49 tests\ntest audio_converter::tests::build_input_interleaved ... ok\ntest audio_converter::tests::build_output_mono_44_1k ... ok\ntest audio_converter::tests::build_input_non_interleaved ... ok\ntest audio_converter::tests::build_output_stereo_48k ... ok\ntest audio_converter::tests::converted_frame_count_exact ... ok\ntest audio_converter::tests::handle_float64_input ... ok\ntest audio_converter::tests::duplicate_mono_to_stereo ... ok\ntest audio_converter::tests::input_frame_count_interleaved_and_planar ... ok\ntest audio_converter::tests::interleave_planar_stereo ... ok\ntest audio_converter::tests::linear_resample_to_target_rate ... ok\ntest audio_converter::tests::lpcm_fourcc ... ok\ntest audio_converter::tests::normalize_signed_int16 ... ok\ntest audio_converter::tests::output_frame_capacity_rounds_up ... ok\ntest foundation::tests::nsstring_to_string_handles_none ... ok\ntest foundation::tests::mutable_array_initial_count_is_zero ... ok\ntest foundation::tests::mutable_dictionary_initial_count_is_zero ... ok\ntest os_version::tests::classify_macos_10_15_is_unsupported_and_mentions_min_version ... ok\ntest foundation::tests::nsstring_round_trip ... ok\ntest os_version::tests::classify_macos_12_3_is_sck_only ... ok\ntest os_version::tests::classify_macos_14_2_has_both_backends ... ok\ntest os_version::tests::classify_macos_15_is_supported ... ok\ntest os_version::tests::classify_unknown_version_is_unsupported ... ok\ntest os_version::tests::coreaudio_floor_14_2 ... ok\ntest os_version::tests::format_version_trims_zero_patch ... ok\ntest os_version::tests::meets_floor_exact_match ... ok\ntest os_version::tests::meets_floor_higher_major ... ok\ntest os_version::tests::meets_floor_higher_minor ... ok\ntest os_version::tests::rejects_below_floor_major ... ok\ntest os_version::tests::rejects_below_floor_minor ... ok\ntest os_version::tests::rejects_macos_10_15_for_sck ... ok\ntest process_tree::tests::collect_related_returns_tree ... ok\ntest process_tree::tests::same_launch_tree_excludes_unrelated ... ok\ntest process_tree::tests::same_launch_tree_includes_direct_child ... ok\ntest process_tree::tests::same_launch_tree_includes_pgrp_peer ... ok\ntest related_app::tests::helper_bundle_strips_suffix ... ok\ntest related_app::tests::helper_name_strips_helper_suffix ... ok\ntest related_app::tests::related_by_dot_prefix ... ok\ntest related_app::tests::related_via_bundle_helper_base ... ok\ntest related_app::tests::related_via_name_helper_base ... ok\ntest related_app::tests::unrelated_returns_false ... ok\ntest sck::tests::cgrect_intersection_handles_negative_and_disjoint ... ok\ntest source_state::tests::double_start_rejected ... ok\ntest source_state::tests::happy_path ... ok\ntest source_state::tests::cancel_idle_short_circuits ... ok\ntest source_state::tests::is_allowed_exhaustive ... ok\ntest source_state::tests::mark_fatal_forces_stopped ... ok\ntest source_state::tests::mark_fatal_idempotent ... ok\ntest source_state::tests::start_while_stopping_rejected ... ok\ntest source_state::tests::stop_before_start_rejected ... ok\n\ntest result: ok. 49 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s\n\n", + "stderrTail": " Compiling fluxer_mac_app_audio v0.0.0 (/Users/hampus/Development/fluxer/fluxer_desktop/native/mac-app-audio)\n Finished `test` profile [unoptimized + debuginfo] target(s) in 1.54s\n Running unittests src/lib.rs (target/debug/deps/fluxer_mac_app_audio-b86bec7aea37ec64)\n Doc-tests fluxer_mac_app_audio\n" + }, + { + "name": "mac-app-audio-js-tests", + "command": "'/Users/hampus/Development/fluxer/node_modules/.pnpm/node_modules/.bin/vitest' run", + "category": "platform-native", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-09T04:10:05.465Z", + "endedAt": "2026-06-09T04:10:06.715Z", + "durationMs": 1250, + "stdoutTail": "\n RUN v4.0.18 /Users/hampus/Development/fluxer/fluxer_desktop/native/mac-app-audio\n\n ✓ index.test.mjs (6 tests) 2ms\n\n Test Files 1 passed (1)\n Tests 6 passed (6)\n Start at 06:10:06\n Duration 489ms (transform 29ms, setup 0ms, import 298ms, tests 2ms, environment 0ms)\n\n", + "stderrTail": "" + } + ] +} diff --git a/fluxer_desktop/native-media-reports/2026-06-09T0413-codex-fedora-livekit-smoke.json b/fluxer_desktop/native-media-reports/2026-06-09T0413-codex-fedora-livekit-smoke.json new file mode 100644 index 0000000..5a4de10 --- /dev/null +++ b/fluxer_desktop/native-media-reports/2026-06-09T0413-codex-fedora-livekit-smoke.json @@ -0,0 +1,334 @@ +{ + "status": "fail", + "startedAt": "2026-06-09T04:15:05.489Z", + "endedAt": "2026-06-09T04:15:36.366Z", + "platform": "linux", + "arch": "arm64", + "config": { + "url": "ws://10.211.55.2:7880", + "serverApiUrl": "http://10.211.55.2:7880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-283674-883784d5-5c9a-4466-84b5-dade644ebf82", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-283674", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-283674", + "subscriberIdentity": "fluxer-native-subscriber-283674", + "required": true, + "strict": false, + "timeoutMs": 30000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 0, + "width": 320, + "height": 180, + "expectedWidth": 320, + "expectedHeight": 180, + "fps": 15, + "minFpsRatio": 0, + "maxFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": null, + "requireStableResolution": false, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": null, + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": false, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": true, + "secondaryPublisherScreenSimulcast": true, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 400000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": false, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/media/psf/fluxer-dev/fluxer_desktop/native-media-reports/2026-06-09T0413-codex-fedora-livekit-smoke.json", + "verbose": false, + "scenarioName": "codec-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": false + }, + { + "name": "subscriber remote screenshare subscription", + "pass": false + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": false + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 320x180", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 3", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 1", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 3", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": false + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + } + ], + "skippedReason": null, + "error": "Error: timed out after 30000ms waiting for: publisher local screenshare publication, subscriber remote screenshare subscription, publisher outbound screenshare stats, publisher outbound screenshare codec video/VP8\n at waitForHarnessSuccess (file:///media/psf/fluxer-dev/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1232:8)\n at async runHarness (file:///media/psf/fluxer-dev/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1831:18)\n at async main (file:///media/psf/fluxer-dev/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1931:18)\n at async file:///media/psf/fluxer-dev/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1964:19", + "hardwareEncoder": { + "available": false, + "backend": "none", + "compiled": true, + "runtime": false, + "codecs": [], + "zeroCopy": false, + "nativeInputs": [], + "reason": "runtime_prerequisite_missing", + "detail": "missing libcuda.so.1, libnvidia-encode.so.1" + }, + "metrics": { + "inboundAudioFrames": 2994, + "inboundVideoFrames": 300, + "strictAudioFrames": 297, + "strictVideoFrames": 300, + "videoCallbacks": 300, + "videoBytes": 25920000, + "receivedFps": 10, + "requiredFps": null, + "maxVideoFrameGapMs": 120, + "maxAudioFrameGapMs": 106, + "maxVideoFrameGapOverallMs": 120, + "maxAudioFrameGapOverallMs": 106, + "avDriftMs": 4, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": { + "320x180": 300 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 15, + "startedAtMs": 1780978506249, + "stoppedAtMs": null, + "framesPushed": 0, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 0, + "lastPushAtMs": null, + "elapsedMs": 30121, + "producedFps": 0 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": null, + "error": null, + "participants": [] + }, + "lastVideoMeta": { + "participantSid": "PA_XodNLgkfL4bV", + "trackSid": "TR_VSVvE7VrQKNqwD", + "width": 320, + "height": 180, + "timestampUs": 124248690000 + }, + "publisherStats": { + "rttMs": 5, + "outbound": [ + { + "trackSid": "TR_AM3ooPAYg5Q2M6", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 98.8, + "packetsLost": 0, + "audioLevel": 0.305, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSVvE7VrQKNqwD", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 2.6, + "packetsLost": 0, + "fps": 9, + "width": 320, + "height": 180, + "sourceWidth": 320, + "sourceHeight": 180, + "targetBitrateKbps": 400, + "configuredFps": 15, + "targetFps": 15, + "effectiveFps": 0, + "framesProduced": 0, + "framesAccepted": 0, + "framesDropped": 0, + "framesCoalesced": 0, + "framesCaptured": 0, + "captureFailures": 0, + "maxQueueAgeMs": 0, + "maxPushLatencyMs": 0, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable", + "codec": "video/VP8" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoQueueCapacity": 8, + "outgoingVideoMaxQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 15, + "outgoingVideoPacingTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingVideoPacingMode": "source", + "outgoingVideoBusActive": false, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 27, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 5, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_XodNLgkfL4bV", + "trackSid": "TR_AM3ooPAYg5Q2M6", + "kind": "audio", + "bitrateKbps": 99.1, + "packetsLost": 0, + "jitterMs": 1, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_XodNLgkfL4bV", + "trackSid": "TR_VSVvE7VrQKNqwD", + "kind": "video", + "bitrateKbps": 2.6, + "packetsLost": 0, + "jitterMs": 1, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoQueueCapacity": 0, + "outgoingVideoMaxQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoPacingTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingVideoPacingMode": "idle", + "outgoingVideoBusActive": false, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/2026-06-14T18-09-25-591Z/summary.json b/fluxer_desktop/native-media-reports/2026-06-14T18-09-25-591Z/summary.json new file mode 100644 index 0000000..036ddd0 --- /dev/null +++ b/fluxer_desktop/native-media-reports/2026-06-14T18-09-25-591Z/summary.json @@ -0,0 +1,143 @@ +{ + "status": "fail", + "mode": "smoke", + "strict": false, + "platform": "linux", + "arch": "arm64", + "hostname": "0a84a8e7079b", + "startedAt": "2026-06-14T18:09:25.592Z", + "endedAt": "2026-06-14T18:11:18.780Z", + "reportDir": "/workspaces/fluxer/fluxer_desktop/native-media-reports/2026-06-14T18-09-25-591Z", + "gates": [ + { + "name": "mode-valid", + "status": "pass", + "mode": "smoke" + }, + { + "name": "documentation-current", + "status": "pass", + "missing": [] + }, + { + "name": "strict-prerequisites", + "status": "skip", + "reason": "smoke mode" + }, + { + "name": "strict-command-plan", + "status": "skip", + "reason": "smoke mode" + } + ], + "plannedCommands": [ + { + "name": "main-process-native-media-unit-tests", + "command": "node --test src/main/NativeVoiceEngine.test.mjs src/main/NativeScreenCapture.test.mjs", + "category": "shared", + "cwd": "." + }, + { + "name": "desktop-typecheck", + "command": "pnpm typecheck", + "category": "shared", + "cwd": "." + }, + { + "name": "webrtc-sender-build", + "command": "pnpm --dir native/webrtc-sender build", + "category": "native-sender", + "cwd": "." + }, + { + "name": "webrtc-sender-tests", + "command": "pnpm --dir native/webrtc-sender test", + "category": "native-sender", + "cwd": "." + }, + { + "name": "webrtc-sender-livekit-harness", + "command": "pnpm --dir native/webrtc-sender test:livekit", + "category": "livekit", + "cwd": "." + }, + { + "name": "linux-screen-capture-build", + "command": "pnpm --dir native/linux-screen-capture build", + "category": "platform-native", + "cwd": "." + }, + { + "name": "linux-screen-capture-tests", + "command": "pnpm --dir native/linux-screen-capture test", + "category": "platform-native", + "cwd": "." + }, + { + "name": "linux-audio-capture-build", + "command": "pnpm --dir native/linux-audio-capture build", + "category": "platform-native", + "cwd": "." + }, + { + "name": "linux-audio-capture-tests", + "command": "pnpm --dir native/linux-audio-capture test", + "category": "platform-native", + "cwd": "." + } + ], + "commands": [ + { + "name": "main-process-native-media-unit-tests", + "command": "node --test src/main/NativeVoiceEngine.test.mjs src/main/NativeScreenCapture.test.mjs", + "category": "shared", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-14T18:09:25.593Z", + "endedAt": "2026-06-14T18:09:26.814Z", + "durationMs": 1221, + "stdoutTail": "▶ NativeScreenCapture source identity and capability reporting\n ✔ normalizes display and window sources without changing source ids (1.883928ms)\n ✔ starts display and window captures with exact source id and kind and reports diagnostics (1.130214ms)\n ✔ fails fast when native frame sink is required but unavailable (0.578253ms)\n ✔ passes caller-provided capture id and native sink handle to the platform wrapper (0.568254ms)\n ✔ reports macOS and Linux display/window capture capabilities from platform backends (0.846088ms)\n ✔ reports Windows native game capture disabled before loading the addon (0.545086ms)\n ✔ loads Windows native game capture only for the game capture build variant (0.948922ms)\n✔ NativeScreenCapture source identity and capability reporting (7.057249ms)\n▶ NativeVoiceEngine v2 bridge contract fixtures\n ✔ registers and removes every v2 IPC channel from the bridge contract fixture (1.949095ms)\n✔ NativeVoiceEngine v2 bridge contract fixtures (2.38543ms)\n▶ NativeVoiceEngine bridge version pair assertion\n ✔ asserts the host bridge version against the native addon at load time (0.859213ms)\n ✔ disables the native voice engine when the addon reports a mismatched bridge version (0.842422ms)\n ✔ disables the native voice engine when the addon lacks bridge version exports (0.395753ms)\n ✔ disables the native voice engine when the addon rejects the host bridge version (0.459586ms)\n✔ NativeVoiceEngine bridge version pair assertion (2.669932ms)\n▶ NativeVoiceEngine v2 IPC request fixtures\n ✔ replays fixture: valid connect reaches the native engine (0.856421ms)\n ✔ replays fixture: invalid connect args throw (0.387877ms)\n ✔ replays fixture: invalid mic enabled args return typed operation failure (0.984381ms)\n ✔ replays fixture: valid microphone publish returns typed success (0.584336ms)\n ✔ replays fixture: invalid microphone publish returns typed operation failure (0.438002ms)\n ✔ replays fixture: valid zero-copy screen publish reaches native engine (0.566461ms)\n ✔ replays fixture: explicit non-zero-copy screen publish is rejected (0.409836ms)\n ✔ replays fixture: hardware screen encoding without zero-copy is rejected (0.520878ms)\n ✔ replays fixture: valid native screen audio publish reaches native engine (0.494211ms)\n ✔ replays fixture: invalid screen audio route throws (0.494961ms)\n ✔ replays fixture: valid camera publish reaches native engine (0.389836ms)\n ✔ replays fixture: valid native camera sink publish reaches native engine (0.490294ms)\n ✔ replays fixture: valid processed camera publish reaches native engine (1.25884ms)\n ✔ replays fixture: valid processed camera frame reaches native engine (0.41771ms)\n ✔ replays fixture: invalid processed camera frame throws (0.359294ms)\n ✔ replays fixture: invalid camera publish throws (0.341377ms)\n ✔ replays fixture: connected stats return normalized empty stats (0.354044ms)\n ✔ replays fixture: valid participant volume reaches native engine (0.355585ms)\n ✔ replays fixture: invalid participant volume throws (0.334544ms)\n ✔ replays fixture: valid remote subscription reaches native engine (0.425128ms)\n ✔ replays fixture: invalid remote subscription quality throws (0.353668ms)\n ✔ replays fixture: valid data publish reaches native engine (0.394711ms)\n ✔ replays fixture: invalid data payload throws (0.351669ms)\n✔ NativeVoiceEngine v2 IPC request fixtures (11.876651ms)\n▶ NativeVoiceEngine connection lifecycle\n ✔ constructs the singleton and prewarms the native voice backend once at registration (0.389294ms)\n ✔ connect uses the prewarmed singleton instead of constructing an engine per call (0.332418ms)\n ✔ connect waits for a pending prewarm before dialing (9.277679ms)\n ✔ prewarm retries transient addon failures before succeeding (505.813953ms)\n ✔ set-audio-output-device retries transient negative device-count failures (4.178899ms)\n ✔ set-audio-output-device does not retry non-transient failures (2.9481ms)\n ✔ reuses the singleton across disconnect and reconnect cycles (2.513848ms)\n ✔ a second connect awaits the disconnect of the previous session before dialing (2.102095ms)\n ✔ a connect superseded before dialing rejects while latest replacement dials (3.610687ms)\n ✔ clears the native video-frame callback when a session is torn down (1.179257ms)\n ✔ tears down the active session when the owner renderer process exits (1.542634ms)\n ✔ tears down the active session on owner main-frame reload navigation (1.679135ms)\n ✔ keeps the active session for same-document and subframe navigation (1.513508ms)\n ✔ falls back to a no-op video-frame callback for engines without a clear export (0.897714ms)\n ✔ does not forward native events after disconnecting a pending connect (1.364383ms)\n ✔ starts replacement connect before stale native connect completion settles (1.805552ms)\n✔ NativeVoiceEngine connection lifecycle (542.09012ms)\n▶ NativeVoiceEngine screen frame routing\n ✔ publishes screen share with source pacing and exposes a native frame sink handle (1.161882ms)\n ✔ forwards device screen share publish options to the native engine (0.958214ms)\n ✔ keeps screen-share audio published when screen video is unpublished (1.080964ms)\n ✔ pushes screen-share float audio using the typed-array view length (0.929422ms)\n ✔ replaces screen captures without releasing screen-share audio or duplicating priority (0.832755ms)\n ✔ updates active screen-share encoding over IPC (0.951839ms)\n ✔ rejects screen-share encoding updates that change codec or hardware mode (0.791754ms)\n ✔ forwards watch and unwatch operations with source, enablement, and quality intact (0.782337ms)\n ✔ reports VideoToolbox hardware capability from the native addon (0.642129ms)\n ✔ reports native voice engine capabilities from the native addon (0.642879ms)\n ✔ maps native addon microphone not-connected errors to the typed operation code (1.749843ms)\n ✔ rejects device microphone operations while the audio device module is warming (5.035821ms)\n✔ NativeVoiceEngine screen frame routing (15.857342ms)\n▶ NativeVoiceEngine camera preview sessions\n ✔ starts a standalone preview engine when not connected and stops it on stop (0.98463ms)\n ✔ routes camera preview through the active session engine when connected (0.585586ms)\n ✔ stops a standalone preview on the singleton when a call connects (0.590337ms)\n ✔ routes a preview after a call ends through the same singleton engine (0.654712ms)\n ✔ stops a standalone preview when the owner renderer process exits (1.566134ms)\n ✔ stops a standalone preview on owner main-frame reload navigation (1.060881ms)\n ✔ routes camera capture updates through the active session engine (0.643712ms)\n ✔ rejects camera capture updates when not connected (0.563253ms)\n ✔ rejects invalid camera preview args (0.533378ms)\n ✔ drops video frames with a mismatched per-frame bridge version and logs once (0.644129ms)\n✔ NativeVoiceEngine camera preview sessions (8.004754ms)\n▶ NativeVoiceEngine camera background frame routing\n ✔ routes background frames and clears to the active session engine when connected (0.640379ms)\n ✔ routes background frames to the standalone preview engine (0.517586ms)\n ✔ returns false without a session and rejects invalid frames (0.555711ms)\n✔ NativeVoiceEngine camera background frame routing (1.789177ms)\n▶ NativeVoiceEngine readiness\n ✔ reports not ready with the load failure detail when the native module fails to load (0.56242ms)\n ✔ reports not ready when the addon does not expose a VoiceEngine constructor (1.861719ms)\n ✔ reports prewarm-pending readiness before the registration prewarm settles (0.598003ms)\n ✔ surfaces the prewarm failure detail as the not-ready reason and rejects the prewarm call (506.100539ms)\n ✔ reports ready once the singleton is constructed and prewarmed (3.705354ms)\n ✔ pushes a one-time engineReady event when readiness first becomes true (3.76148ms)\n ✔ does not push engineReady to destroyed web contents (4.784236ms)\n✔ NativeVoiceEngine readiness (521.834295ms)\nℹ tests 83\nℹ suites 9\nℹ pass 83\nℹ fail 0\nℹ cancelled 0\nℹ skipped 0\nℹ todo 0\nℹ duration_ms 1197.319351\n", + "stderrTail": "" + }, + { + "name": "desktop-typecheck", + "command": "pnpm typecheck", + "category": "shared", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-14T18:09:26.814Z", + "endedAt": "2026-06-14T18:09:27.244Z", + "durationMs": 430, + "stdoutTail": "\n> fluxer_desktop@0.0.0 typecheck /workspaces/fluxer/fluxer_desktop\n> tsgo --noEmit\n\n", + "stderrTail": "" + }, + { + "name": "webrtc-sender-build", + "command": "pnpm --dir native/webrtc-sender build", + "category": "native-sender", + "status": "pass", + "code": 0, + "signal": null, + "startedAt": "2026-06-14T18:09:27.244Z", + "endedAt": "2026-06-14T18:10:34.871Z", + "durationMs": 67627, + "stdoutTail": "\n> @fluxer/webrtc-sender@0.0.0 build /workspaces/fluxer/fluxer_desktop/native/webrtc-sender\n> cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon\n\n[buildRustNodeAddon] cwd=/workspaces/fluxer/fluxer_desktop/native/webrtc-sender\n+ /home/vscode/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/bin/cargo build --release --target aarch64-unknown-linux-gnu --manifest-path Cargo.toml --features camera-native\n", + "stderrTail": " Compiling pest v2.8.6\n Compiling kstring v2.0.2\n Compiling memo-map v0.3.3\n Compiling time-macros v0.2.27\n Compiling nom v8.0.0\n Compiling crossbeam-utils v0.8.21\n Compiling typeid v1.0.3\n Compiling itertools v0.14.0\n Compiling downcast-rs v2.0.2\n Compiling hashbrown v0.11.2\n Compiling tinyvec v1.11.0\n Compiling minijinja v2.20.0\n Compiling minimal-lexical v0.2.1\n Compiling erased-serde v0.4.10\n Compiling dyn-eq v0.1.3\n Compiling dyn-hash v1.0.0\n Compiling regex-automata v0.4.14\n Compiling itertools v0.10.5\n Compiling derive-new v0.7.0\n Compiling bit-vec v0.9.1\n Compiling pastey v0.2.3\n Compiling unicode-normalization v0.1.25\n Compiling nom v7.1.3\n Compiling derive-new v0.5.9\n Compiling downcast-rs v1.2.1\n Compiling bit-vec v0.6.3\n Compiling bit-set v0.10.0\n Compiling pest_meta v2.8.6\n Compiling crunchy v0.2.4\n Compiling anymap3 v1.0.1\n Compiling inventory v0.3.24\n Compiling rayon-core v1.13.0\n Compiling tiny-keccak v2.0.2\n Compiling bit-set v0.5.3\n Compiling serde_urlencoded v0.7.1\n Compiling rustls v0.23.40\n Compiling crossbeam-epoch v0.9.18\n Compiling xattr v1.6.1\n Compiling hashbrown v0.16.1\n Compiling num-bigint v0.4.6\n Compiling png v0.18.1\n Compiling getrandom v0.4.2\n Compiling rand_core v0.10.1\n Compiling time v0.3.47\n Compiling num-iter v0.1.45\n Compiling float-ord v0.3.2\n Compiling pest_generator v2.8.6\n Compiling tar v0.4.46\n Compiling crossbeam-deque v0.8.6\n Compiling reqwest v0.12.28\n Compiling num-complex v0.4.6\n Compiling string-interner v0.14.0\n Compiling pbjson v0.6.0\n Compiling string-interner v0.20.0\n Compiling const-random-macro v0.1.16\n Compiling chacha20 v0.10.0\n Compiling safetensors v0.7.0\n Compiling pbjson-types v0.6.0\n Compiling rustfft v6.4.1\n Compiling ndarray v0.17.2\n Compiling ndarray v0.15.6\n Compiling regex v1.12.3\n Compiling nom-language v0.1.0\n Compiling rayon v1.12.0\n Compiling image v0.25.10\n Compiling const-random v0.1.18\n Compiling pest_derive v2.8.6\n Compiling enum-ordinalize v3.1.15\n Compiling dlv-list v0.5.2\n Compiling scan_fmt v0.2.6\n Compiling rand v0.10.1\n Compiling nix v0.31.3\n Compiling prost-derive v0.11.9\n Compiling tower-http v0.6.11\n Compiling liquid-core v0.26.8\n Compiling webrtc-sys-build v0.3.18\n Compiling educe v0.4.23\n Compiling hashbrown v0.13.2\n Compiling rand_distr v0.6.0\n Compiling os_info v3.15.0\n Compiling glib v0.21.5\n Compiling half v2.7.1\n Compiling livekit-protocol v0.7.8\n Compiling memmap2 v0.5.10\n Compiling ppv-lite86 v0.2.21\n Compiling ordered-multimap v0.6.0\n Compiling memmap2 v0.9.10\n Compiling napi v3.9.1\n Compiling fluxer_screen_frame_bus v0.0.0 (/workspaces/fluxer/fluxer_desktop/native/screen-frame-bus)\n Compiling fluxer_desktop_native v0.1.0 (/workspaces/fluxer/fluxer_desktop/native/rust)\n Compiling crossbeam-queue v0.3.12\n Compiling rand_chacha v0.3.1\n Compiling liquid-lib v0.26.8\n Compiling rand_chacha v0.9.0\n Compiling prost-derive v0.14.4\n Compiling realfft v3.5.0\n Compiling rust-ini v0.19.0\n Compiling rand v0.8.6\n Compiling rand v0.9.4\n Compiling tokio-rustls v0.26.4\n Compiling tract-linalg v0.23.1 (/workspaces/fluxer/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1)\n Compiling nokhwa-core v0.1.9\n Compiling prost v0.11.9\n Compiling rubato v0.14.1\n Compiling webrtc-sys v0.3.33 (/workspaces/fluxer/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys)\n Compiling hyper-rustls v0.27.9\n Compiling num-bigint-dig v0.8.6\n Compiling rand_distr v0.4.3\n Compiling tungstenite v0.29.0\n Compiling nokhwa-bindings-macos v0.2.4\n Compiling nokhwa-bindings-linux v0.1.4\n Compiling nokhwa-bindings-windows v0.4.6\n Compiling liquid v0.26.8\n Compiling tract-data v0.19.16\n Compiling rsa v0.9.10\n Compiling tract-linalg v0.19.16 (/workspaces/fluxer/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16)\n Compiling tokio-tungstenite v0.29.0\n Compiling prost v0.14.4\nwarning: use of deprecated method `cc::Build::static_flag`: cc only creates static libraries, setting this does nothing\n --> vendor/tract-linalg-0.19.16/build.rs:66:19\n |\n66 | self.cc().static_flag(true).file(filename).try_compile(\"dummy\").is_ok()\n | ^^^^^^^^^^^\n |\n = note: `#[warn(deprecated)]` on by default\n\nwarning: use of deprecated method `cc::Build::static_flag`: cc only creates static libraries, setting this does nothing\n --> vendor/tract-linalg-0.19.16/build.rs:121:26\n |\n121 | .static_flag(true)\n | ^^^^^^^^^^^\n\nwarning: use of deprecated method `cc::Build::static_flag`: cc only creates static libraries, setting this does nothing\n --> vendor/tract-linalg-0.19.16/build.rs:133:61\n |\n133 | cc::Build::new().files(files).flag(\"-mfma\").static_flag(true).compile(\"x86_64_fma\");\n | ^^^^^^^^^^^\n\nwarning: use of deprecated method `cc::Build::static_flag`: cc only creates static libraries, setting this does nothing\n --> vendor/tract-linalg-0.19.16/build.rs:142:18\n |\n142 | .static_flag(true)\n | ^^^^^^^^^^^\n\nwarning: use of deprecated method `cc::Build::static_flag`: cc only creates static libraries, setting this does nothing\n --> vendor/tract-linalg-0.19.16/build.rs:154:18\n |\n154 | .static_flag(true)\n | ^^^^^^^^^^^\n\nwarning: use of deprecated method `cc::Build::static_flag`: cc only creates static libraries, setting this does nothing\n --> vendor/tract-linalg-0.19.16/build.rs:164:43\n |\n164 | cc::Build::new().files(files).static_flag(true).compile(\"arm64simd\");\n | ^^^^^^^^^^^\n\nwarning: use of deprecated method `cc::Build::static_flag`: cc only creates static libraries, setting this does nothing\n --> vendor/tract-linalg-0.19.16/build.rs:174:42\n |\n174 | config.cc().files(files).static_flag(true).compile(\"arm64fp16\")\n | ^^^^^^^^^^^\n\n Compiling tract-data v0.23.1\n Compiling jsonwebtoken v10.4.0\nwarning: `tract-linalg` (build script) generated 7 warnings\n Compiling nokhwa v0.10.11\n Compiling livekit-datatrack v0.1.8\nwarning: tract-linalg@0.19.16: arm64/arm64fp16/dummy_fmla_no_pragma.S: Assembler messages:\nwarning: tract-linalg@0.19.16: arm64/arm64fp16/dummy_fmla_no_pragma.S:11: Error: selected processor does not support `fmla v16.8h,v0.8h,v8.h[0]'\n Compiling livekit-api v0.5.0\nwarning: unexpected `cfg` condition value: `armv7`\n --> vendor/tract-linalg-0.19.16/src/lib.rs:26:32\n |\n26 | #[cfg(any(target_arch = \"arm\", target_arch = \"armv7\"))]\n | ^^^^^^^^^^^^^^^^^^^^^\n |\n = note: expected values for `target_arch` are: `aarch64`, `amdgpu`, `arm`, `arm64ec`, `avr`, `bpf`, `csky`, `hexagon`, `loongarch32`, `loongarch64`, `m68k`, `mips`, `mips32r6`, `mips64`, `mips64r6`, `msp430`, `nvptx64`, `powerpc`, `powerpc64`, `riscv32`, `riscv64`, `s390x`, `sparc`, `sparc64`, `wasm32`, `wasm64`, `x86`, `x86_64`, and `xtensa`\n = note: see for more information about checking conditional configuration\n = note: `#[warn(unexpected_cfgs)]` on by default\n\nwarning: unexpected `cfg` condition name: `feature_cpu`\n --> vendor/tract-linalg-0.19.16/src/arm64.rs:40:10\n |\n40 | cfg!(feature_cpu = \"fp16\")\n | ^^^^^^^^^^^^^^^^^^^^\n |\n = help: expected names are: `docsrs`, `feature`, and `test` and 31 more\n = help: consider using a Cargo feature instead\n = help: or consider adding in `Cargo.toml` the `check-cfg` lint config for the lint:\n [lints.rust]\n unexpected_cfgs = { level = \"warn\", check-cfg = ['cfg(feature_cpu, values(\"fp16\"))'] }\n = help: or consider adding `println!(\"cargo::rustc-check-cfg=cfg(feature_cpu, values(\\\"fp16\\\"))\");` to the top of the `build.rs`\n = note: see for more information about checking conditional configuration\n\nwarning: unexpected `cfg` condition value: `armv7`\n --> vendor/tract-linalg-0.19.16/src/lib.rs:134:36\n |\n134 | #[cfg(any(target_arch = \"arm\", target_arch = \"armv7\"))]\n | ^^^^^^^^^^^^^^^^^^^^^\n |\n = note: expected values for `target_arch` are: `aarch64`, `amdgpu`, `arm`, `arm64ec`, `avr`, `bpf`, `csky`, `hexagon`, `loongarch32`, `loongarch64`, `m68k`, `mips`, `mips32r6`, `mips64`, `mips64r6`, `msp430`, `nvptx64`, `powerpc`, `powerpc64`, `riscv32`, `riscv64`, `s390x`, `sparc`, `sparc64`, `wasm32`, `wasm64`, `x86`, `x86_64`, and `xtensa`\n = note: see for more information about checking conditional configuration\n\nwarning: hiding a lifetime that's elided elsewhere is confusing\n --> vendor/tract-linalg-0.19.16/src/generic/rounding.rs:37:26\n |\n37 | pub fn as_fused_spec(&self) -> FusedSpec {\n | ^^^^^ ^^^^^^^^^ the same lifetime is hidden here\n | |\n | the lifetime is elided here\n |\n = help: the same lifetime is referred to in inconsistent ways, making the signature confusing\n = note: `#[warn(mismatched_lifetime_syntaxes)]` on by default\nhelp: use `'_` for type paths\n |\n37 | pub fn as_fused_spec(&self) -> FusedSpec<'_> {\n | ++++\n\n Compiling tract-core v0.19.16\nwarning: `tract-linalg` (lib) generated 4 warnings (run `cargo fix --lib -p tract-linalg` to apply 1 suggestion)\nwarning: tract-linalg@0.23.1: arm64/arm64fp16/dummy_fmla_no_pragma.S: Assembler messages:\nwarning: tract-linalg@0.23.1: arm64/arm64fp16/dummy_fmla_no_pragma.S:11: Error: selected processor does not support `fmla v16.8h,v0.8h,v8.h[0]'\n Compiling tract-core v0.23.1\n Compiling tract-nnef v0.19.16\n Compiling tract-hir v0.19.16\nwarning: webrtc-sys@0.3.33: EGL headers not found; building without NVIDIA hardware accelerated video codec support\n Compiling libwebrtc v0.3.35 (/workspaces/fluxer/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc)\nwarning: variable does not need to be mutable\n --> vendor/libwebrtc/src/native/desktop_capturer.rs:66:13\n |\n66 | let mut sys_handle = sys_dc::ffi::DesktopCapturerOptions {\n | ----^^^^^^^^^^\n | |\n | help: remove this `mut`\n |\n = note: `#[warn(unused_mut)]` (part of `#[warn(unused)]`) on by default\n\n Compiling tract-pulse-opl v0.19.16\n Compiling tract-onnx-opl v0.19.16\n Compiling tract-pulse v0.19.16\n Compiling tract-onnx v0.19.16\n Compiling livekit v0.7.43\nwarning: `libwebrtc` (lib) generated 1 warning (run `cargo fix --lib -p libwebrtc` to apply 1 suggestion)\n Compiling deep_filter v0.5.6 (https://github.com/Rikorose/DeepFilterNet?rev=978576aa8400552a4ce9730838c635aa30db5e61#978576aa)\n Compiling tract-nnef v0.23.1\n Compiling tract-hir v0.23.1\n Compiling tract-transformers v0.23.1\n Compiling tract-pulse-opl v0.23.1\n Compiling tract-pulse v0.23.1\n Compiling tract-extra v0.23.1\n Compiling tract-onnx-opl v0.23.1\n Compiling tract-onnx v0.23.1\n Compiling fluxer_webrtc_sender v0.0.0 (/workspaces/fluxer/fluxer_desktop/native/webrtc-sender)\nwarning: fields `num_frames` and `timestamp_us` are never read\n --> src/engine.rs:279:5\n |\n277 | struct ScreenAudioChunk {\n | ---------------- fields in this struct\n278 | samples: Vec,\n279 | num_frames: u32,\n | ^^^^^^^^^^\n...\n282 | timestamp_us: i64,\n | ^^^^^^^^^^^^\n |\n = note: `#[warn(dead_code)]` (part of `#[warn(unused)]`) on by default\n\nwarning: `fluxer_webrtc_sender` (lib) generated 1 warning\n Finished `release` profile [optimized] target(s) in 1m 01s\n" + }, + { + "name": "webrtc-sender-tests", + "command": "pnpm --dir native/webrtc-sender test", + "category": "native-sender", + "status": "fail", + "code": 1, + "signal": null, + "startedAt": "2026-06-14T18:10:34.871Z", + "endedAt": "2026-06-14T18:11:18.779Z", + "durationMs": 43908, + "stdoutTail": "\n> @fluxer/webrtc-sender@0.0.0 test /workspaces/fluxer/fluxer_desktop/native/webrtc-sender\n> cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- test-webrtc-sender-rust && node --test index.test.mjs scripts/livekit-harness.test.mjs\n\n+ /home/vscode/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/bin/cargo test --features \"publisher,camera-native\"\n ELIFECYCLE  Test failed. See above for more details.\n WARN  Local package.json exists, but node_modules missing, did you mean to install?\n", + "stderrTail": " Compiling cfg-if v1.0.4\n Compiling memchr v2.8.1\n Compiling bitflags v2.13.0\n Compiling bytes v1.11.1\n Compiling once_cell v1.21.4\n Compiling itoa v1.0.18\n Compiling regex-syntax v0.8.10\n Compiling pin-project-lite v0.2.17\n Compiling equivalent v1.0.2\n Compiling typenum v1.20.1\n Compiling log v0.4.30\n Compiling adler2 v2.0.1\n Compiling libm v0.2.16\n Compiling serde_core v1.0.228\n Compiling either v1.16.0\n Compiling linux-raw-sys v0.12.1\n Compiling same-file v1.0.6\n Compiling futures-io v0.3.32\n Compiling slab v0.4.12\n Compiling futures-task v0.3.32\n Compiling rawpointer v0.2.1\n Compiling zerocopy v0.8.50\n Compiling zeroize v1.8.2\n Compiling zerofrom v0.1.8\n Compiling ring v0.17.14\n Compiling anyhow v1.0.102\n Compiling byteorder v1.5.0\n Compiling simd-adler32 v0.3.9\n Compiling aho-corasick v1.1.4\n Compiling getrandom v0.2.17\n Compiling crc32fast v1.5.0\n Compiling matrixmultiply v0.3.10\n Compiling http v1.4.1\n Compiling strength_reduce v0.2.4\n Compiling maplit v1.0.2\n Compiling indexmap v2.14.0\n Compiling dyn-clone v1.0.20\n Compiling minimal-lexical v0.2.1\n Compiling pest v2.8.6\n Compiling tokio v1.52.3\n Compiling parking_lot_core v0.9.12\n Compiling walkdir v2.5.0\n Compiling num-traits v0.2.19\n Compiling zmij v1.0.21\n Compiling ppv-lite86 v0.2.21\n Compiling serde v1.0.228\n Compiling yoke v0.8.2\n Compiling rustix v1.1.4\n Compiling generic-array v0.14.9\n Compiling rand_core v0.6.4\n Compiling parking_lot v0.12.5\n Compiling serde_json v1.0.150\n Compiling num-integer v0.1.46\n Compiling half v2.7.1\n Compiling der v0.7.10\n Compiling futures-util v0.3.32\n Compiling regex-automata v0.4.14\n Compiling http-body v1.0.1\n Compiling time v0.3.47\n Compiling tracing-core v0.1.36\n Compiling filetime v0.2.29\n Compiling socket2 v0.6.4\n Compiling pest_meta v2.8.6\n Compiling mio v1.2.1\n Compiling itertools v0.13.0\n Compiling memo-map v0.3.3\n Compiling allocator-api2 v0.2.21\n Compiling miniz_oxide v0.8.9\n Compiling thiserror v2.0.18\n Compiling zerovec v0.11.6\n Compiling zerotrie v0.2.4\n Compiling primal-check v0.3.4\n Compiling transpose v0.2.3\n Compiling crypto-common v0.1.6\n Compiling block-buffer v0.10.4\n Compiling num-complex v0.4.6\n Compiling flate2 v1.1.9\n Compiling pest_generator v2.8.6\n Compiling xattr v1.6.1\n Compiling regex v1.12.3\n Compiling spki v0.7.3\n Compiling tracing v0.1.44\n Compiling hashbrown v0.16.1\n Compiling minijinja v2.20.0\n Compiling ahash v0.7.8\n Compiling nom v8.0.0\n Compiling itertools v0.14.0\n Compiling dyn-eq v0.1.3\n Compiling downcast-rs v2.0.2\n Compiling num-bigint v0.4.6\n Compiling dyn-hash v1.0.0\n Compiling crossbeam-utils v0.8.21\n Compiling tar v0.4.46\n Compiling typeid v1.0.3\n Compiling rand_chacha v0.3.1\n Compiling digest v0.10.7\n Compiling prost v0.12.6\n Compiling nom v7.1.3\n Compiling rustls-pki-types v1.14.1\n Compiling tinystr v0.8.3\n Compiling potential_utf v0.1.5\n Compiling scan_fmt v0.2.6\n Compiling hashbrown v0.11.2\n Compiling string-interner v0.20.0\n Compiling nom-language v0.1.0\n Compiling itertools v0.10.5\n Compiling rustfft v6.4.1\n Compiling pest_derive v2.8.6\n Compiling ndarray v0.17.2\n Compiling ndarray v0.15.6\n Compiling bit-vec v0.9.1\n Compiling pkcs8 v0.10.2\n Compiling hyper v1.10.1\n Compiling enum-ordinalize v3.1.15\n Compiling icu_locale_core v2.2.0\n Compiling icu_collections v2.2.0\n Compiling bit-set v0.10.0\n Compiling erased-serde v0.4.10\n Compiling getrandom v0.4.2\n Compiling string-interner v0.14.0\n Compiling prost-types v0.12.6\n Compiling http-body-util v0.1.3\n Compiling ff v0.13.1\n Compiling libloading v0.8.9\n Compiling rand v0.8.6\n Compiling hmac v0.12.1\n Compiling signature v2.2.0\n Compiling downcast-rs v1.2.1\n Compiling rustls v0.23.40\n Compiling anymap3 v1.0.1\n Compiling bit-vec v0.6.3\n Compiling inventory v0.3.24\n Compiling petgraph v0.6.5\n Compiling sec1 v0.7.3\n Compiling hkdf v0.12.4\n Compiling tract-data v0.23.1\n Compiling hyper-util v0.1.20\n Compiling rustix v0.38.44\n Compiling liquid-core v0.26.8\n Compiling crossbeam-epoch v0.9.18\n Compiling group v0.13.0\n Compiling getrandom v0.3.4\n Compiling tempfile v3.27.0\n Compiling tower v0.5.3\n Compiling crypto-bigint v0.5.5\n Compiling educe v0.4.23\n Compiling icu_provider v2.2.0\n Compiling tract-linalg v0.23.1 (/workspaces/fluxer/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1)\n Compiling bit-set v0.5.3\n Compiling rustls-webpki v0.103.13\n Compiling pbjson-build v0.6.2\n Compiling clang-sys v1.8.1\n Compiling safetensors v0.7.0\n Compiling serde_urlencoded v0.7.1\n Compiling liquid-lib v0.26.8\n Compiling crossbeam-deque v0.8.6\n Compiling elliptic-curve v0.13.8\n Compiling prost-build v0.12.6\n Compiling which v4.4.2\n Compiling rand_core v0.9.5\n Compiling fdeflate v0.3.7\n Compiling quick-error v2.0.1\n Compiling icu_properties v2.2.0\n Compiling icu_normalizer v2.2.0\n Compiling zune-core v0.5.1\n Compiling color_quant v1.1.0\n Compiling pxfm v0.1.29\n Compiling weezl v0.1.12\n Compiling byteorder-lite v0.1.0\n Compiling rand_chacha v0.9.0\n Compiling liquid v0.26.8\n Compiling tract-data v0.19.16\n Compiling cexpr v0.6.0\n Compiling rayon-core v1.13.0\n Compiling mozjpeg-sys v2.2.3\n Compiling rfc6979 v0.4.0\n Compiling png v0.18.1\n Compiling sha2 v0.10.9\n Compiling zune-jpeg v0.5.15\n Compiling zip v0.6.6\n Compiling tokio-util v0.7.18\n Compiling rand_core v0.10.1\n Compiling chrono v0.4.44\n Compiling pbjson v0.6.0\n Compiling idna_adapter v1.2.2\n Compiling image-webp v0.2.4\n Compiling moxcms v0.8.1\n Compiling bindgen v0.65.1\n Compiling gif v0.14.2\n Compiling tokio-rustls v0.26.4\n Compiling tract-linalg v0.19.16 (/workspaces/fluxer/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16)\n Compiling pbjson-types v0.6.0\n Compiling rand v0.9.4\n Compiling mozjpeg v0.10.13\n Compiling rayon v1.12.0\n Compiling const-random-macro v0.1.16\n Compiling ecdsa v0.16.9\n Compiling cxx-build v1.0.194\n Compiling primeorder v0.13.6\n Compiling idna v1.1.0\n Compiling rustls-native-certs v0.8.3\n Compiling futures-executor v0.3.32\n Compiling tokio-stream v0.1.18\n Compiling num-iter v0.1.45\n Compiling hyper-rustls v0.27.9\n Compiling toml_edit v0.25.12+spec-1.1.0\n Compiling hybrid-array v0.4.12\n Compiling float-ord v0.3.2\nwarning: use of deprecated method `cc::Build::static_flag`: cc only creates static libraries, setting this does nothing\n --> vendor/tract-linalg-0.19.16/build.rs:66:19\n |\n66 | self.cc().static_flag(true).file(filename).try_compile(\"dummy\").is_ok()\n | ^^^^^^^^^^^\n |\n = note: `#[warn(deprecated)]` on by default\n\nwarning: use of deprecated method `cc::Build::static_flag`: cc only creates static libraries, setting this does nothing\n --> vendor/tract-linalg-0.19.16/build.rs:121:26\n |\n121 | .static_flag(true)\n | ^^^^^^^^^^^\n\nwarning: use of deprecated method `cc::Build::static_flag`: cc only creates static libraries, setting this does nothing\n --> vendor/tract-linalg-0.19.16/build.rs:133:61\n |\n133 | cc::Build::new().files(files).flag(\"-mfma\").static_flag(true).compile(\"x86_64_fma\");\n | ^^^^^^^^^^^\n\nwarning: use of deprecated method `cc::Build::static_flag`: cc only creates static libraries, setting this does nothing\n --> vendor/tract-linalg-0.19.16/build.rs:142:18\n |\n142 | .static_flag(true)\n | ^^^^^^^^^^^\n\nwarning: use of deprecated method `cc::Build::static_flag`: cc only creates static libraries, setting this does nothing\n --> vendor/tract-linalg-0.19.16/build.rs:154:18\n |\n154 | .static_flag(true)\n | ^^^^^^^^^^^\n\nwarning: use of deprecated method `cc::Build::static_flag`: cc only creates static libraries, setting this does nothing\n --> vendor/tract-linalg-0.19.16/build.rs:164:43\n |\n164 | cc::Build::new().files(files).static_flag(true).compile(\"arm64simd\");\n | ^^^^^^^^^^^\n\nwarning: use of deprecated method `cc::Build::static_flag`: cc only creates static libraries, setting this does nothing\n --> vendor/tract-linalg-0.19.16/build.rs:174:42\n |\n174 | config.cc().files(files).static_flag(true).compile(\"arm64fp16\")\n | ^^^^^^^^^^^\n\n Compiling image v0.25.10\n Compiling chacha20 v0.10.0\n Compiling curve25519-dalek v4.1.3\nwarning: `tract-linalg` (build script) generated 7 warnings\n Compiling link-cplusplus v1.0.12\n Compiling ed25519 v2.2.3\n Compiling pkcs1 v0.7.5\n Compiling sha1 v0.10.6\n Compiling cxxbridge-macro v1.0.194\n Compiling p256 v0.13.2\nerror: extern location for tokio_rustls does not exist: /workspaces/fluxer/fluxer_desktop/native/webrtc-sender/target/debug/deps/libtokio_rustls-d06ee36b16f159df.rmeta\n --> /home/vscode/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/hyper-rustls-0.27.9/src/stream.rs:11:5\n |\n11 | use tokio_rustls::client::TlsStream;\n | ^^^^^^^^^^^^\n\nerror: could not compile `hyper-rustls` (lib) due to 1 previous error\nwarning: build failed, waiting for other jobs to finish...\nerror[E0786]: found invalid metadata files for crate `moxcms`\n --> /home/vscode/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/image-0.25.10/src/metadata/cicp.rs:95:13\n |\n95 | use moxcms::CicpColorPrimaries as M;\n | ^^^^^^\n |\n = note: failed to open rmeta metadata: '/workspaces/fluxer/fluxer_desktop/native/webrtc-sender/target/debug/deps/libmoxcms-7c20aa5b2d449552.rmeta'\n\nFor more information about this error, try `rustc --explain E0786`.\nerror: could not compile `image` (lib) due to 1 previous error\nerror: extern location for chrono does not exist: /workspaces/fluxer/fluxer_desktop/native/webrtc-sender/target/debug/deps/libchrono-2cf7dcc5247f6ef8.rmeta\n --> /home/vscode/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/pbjson-types-0.6.0/src/timestamp.rs:2:5\n |\n2 | use chrono::{DateTime, NaiveDateTime, TimeZone, Utc};\n | ^^^^^^\n\nerror: could not compile `pbjson-types` (lib) due to 1 previous error\nwarning: tract-linalg@0.19.16: arm64/arm64fp16/dummy_fmla_no_pragma.S: Assembler messages:\nwarning: tract-linalg@0.19.16: arm64/arm64fp16/dummy_fmla_no_pragma.S:11: Error: selected processor does not support `fmla v16.8h,v0.8h,v8.h[0]'\nwarning: tract-linalg@0.23.1: arm64/arm64fp16/dummy_fmla_no_pragma.S: Assembler messages:\nwarning: tract-linalg@0.23.1: arm64/arm64fp16/dummy_fmla_no_pragma.S:11: Error: selected processor does not support `fmla v16.8h,v0.8h,v8.h[0]'\nCommand failed with exit code 101: /home/vscode/.rustup/toolchains/stable-aarch64-unknown-linux-gnu/bin/cargo test --features \"publisher,camera-native\"\n" + } + ] +} diff --git a/fluxer_desktop/native-media-reports/cachyos/cachyos-nvidia-livekit-720p-h264-20260605T204959Z.json b/fluxer_desktop/native-media-reports/cachyos/cachyos-nvidia-livekit-720p-h264-20260605T204959Z.json new file mode 100644 index 0000000..1dffc0d --- /dev/null +++ b/fluxer_desktop/native-media-reports/cachyos/cachyos-nvidia-livekit-720p-h264-20260605T204959Z.json @@ -0,0 +1,287 @@ +{ + "status": "pass", + "startedAt": "2026-06-05T20:50:00.568Z", + "endedAt": "2026-06-05T20:50:17.383Z", + "platform": "linux", + "arch": "x64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-92677-28ffd7a4-19ee-49cf-87d1-e31ab80864f4", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-92677", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-92677", + "subscriberIdentity": "fluxer-native-subscriber-92677", + "required": true, + "strict": true, + "timeoutMs": 90000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 15000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 30, + "minFpsRatio": 0.9, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": null, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["h264"], + "expectedScreenCodecs": ["h264"], + "codec": "h264", + "expectedScreenCodec": "video/H264", + "secondaryPublisher": false, + "secondaryPublisherCodec": "h264", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/H264", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": false, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 400000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": false, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/home/hampus/Development/fluxer/fluxer_desktop/native-media-reports/cachyos-nvidia-livekit-720p-h264-20260605T204959Z.json", + "verbose": false, + "scenarioName": "codec-h264", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 1280x720", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/H264", + "pass": true + }, + { + "name": "subscriber inbound video codec video/H264", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + } + ], + "skippedReason": null, + "error": null, + "hardwareEncoder": { + "available": false, + "backend": "none", + "compiled": true, + "runtime": false, + "codecs": [], + "zeroCopy": false, + "nativeInputs": [], + "reason": "no_supported_devices", + "detail": "missing NVENC encode session (checked 1 CUDA device; last status NV_ENC_ERR_UNSUPPORTED_DEVICE (2))" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 465, + "strictAudioFrames": 0, + "strictVideoFrames": 435, + "videoCallbacks": 463, + "videoBytes": 640051200, + "receivedFps": 28.99, + "requiredFps": 27, + "maxVideoFrameGapMs": 68, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 68, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": { + "1280x720": 435 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780692601312, + "stoppedAtMs": null, + "framesPushed": 483, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 37, + "lastPushAtMs": 1780692617378, + "elapsedMs": 16227, + "producedFps": 29.77 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780692602380, + "error": null, + "participants": [ + { + "identity": "fluxer-native-publisher-92677", + "trackCount": 1, + "tracks": [ + { + "sid": "TR_VStBVT57Q9oKAa", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/H264", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-subscriber-92677", + "trackCount": 0, + "tracks": [] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_89hNCmrznA6A", + "trackSid": "TR_VStBVT57Q9oKAa", + "width": 1280, + "height": 720, + "timestampUs": 68521962000 + }, + "publisherStats": { + "rttMs": 4, + "outbound": [ + { + "trackSid": "TR_VStBVT57Q9oKAa", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 386.9, + "packetsLost": 0, + "fps": 30, + "codec": "video/H264" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 476, + "outgoingVideoFramesAccepted": 476, + "outgoingVideoFramesDropped": 16, + "outgoingVideoFramesCoalesced": 16, + "outgoingVideoFramesCaptured": 460, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 28.97, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 34, + "outgoingVideoMaxPushLatencyMs": 34, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 5, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_89hNCmrznA6A", + "trackSid": "TR_VStBVT57Q9oKAa", + "kind": "video", + "bitrateKbps": 386.4, + "packetsLost": 0, + "jitterMs": 2, + "codec": "video/H264" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/cachyos/cachyos-nvidia-livekit-720p-hevc-20260605T205028Z.json b/fluxer_desktop/native-media-reports/cachyos/cachyos-nvidia-livekit-720p-hevc-20260605T205028Z.json new file mode 100644 index 0000000..0dc00fd --- /dev/null +++ b/fluxer_desktop/native-media-reports/cachyos/cachyos-nvidia-livekit-720p-hevc-20260605T205028Z.json @@ -0,0 +1,287 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T20:50:29.534Z", + "endedAt": "2026-06-05T20:52:00.423Z", + "platform": "linux", + "arch": "x64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-92838-08153292-483f-4acb-b470-af9847d7597c", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-92838", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-92838", + "subscriberIdentity": "fluxer-native-subscriber-92838", + "required": true, + "strict": true, + "timeoutMs": 90000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 15000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 30, + "minFpsRatio": 0.9, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": null, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["hevc"], + "expectedScreenCodecs": ["hevc"], + "codec": "hevc", + "expectedScreenCodec": "video/H265", + "secondaryPublisher": false, + "secondaryPublisherCodec": "hevc", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/H265", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": false, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 400000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": false, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/home/hampus/Development/fluxer/fluxer_desktop/native-media-reports/cachyos-nvidia-livekit-720p-hevc-20260605T205028Z.json", + "verbose": false, + "scenarioName": "codec-hevc", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 1280x720", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/H265", + "pass": false + }, + { + "name": "subscriber inbound video codec video/H265", + "pass": false + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": false + } + ], + "skippedReason": null, + "error": "Error: timed out after 90000ms waiting for: publisher outbound screenshare codec video/H265, subscriber inbound video codec video/H265, server sees publisher screenshare publication\n at waitForHarnessSuccess (file:///home/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1329:8)\n at async runHarness (file:///home/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1926:18)\n at async main (file:///home/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:2026:18)\n at async file:///home/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:2059:19", + "hardwareEncoder": { + "available": false, + "backend": "none", + "compiled": true, + "runtime": false, + "codecs": [], + "zeroCopy": false, + "nativeInputs": [], + "reason": "no_supported_devices", + "detail": "missing NVENC encode session (checked 1 CUDA device; last status NV_ENC_ERR_UNSUPPORTED_DEVICE (2))" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 2611, + "strictAudioFrames": 0, + "strictVideoFrames": 2610, + "videoCallbacks": 2610, + "videoBytes": 3608064000, + "receivedFps": 29.02, + "requiredFps": 27, + "maxVideoFrameGapMs": 81, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 81, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": { + "1280x720": 2610 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780692630259, + "stoppedAtMs": null, + "framesPushed": 2705, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 37, + "lastPushAtMs": 1780692720392, + "elapsedMs": 90321, + "producedFps": 29.95 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780692720171, + "error": null, + "participants": [ + { + "identity": "fluxer-native-subscriber-92838", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-92838", + "trackCount": 1, + "tracks": [ + { + "sid": "TR_VSr7CavPmttCck", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_eQVNaBPkxUPX", + "trackSid": "TR_VSr7CavPmttCck", + "width": 1280, + "height": 720, + "timestampUs": 68625011000 + }, + "publisherStats": { + "rttMs": 6, + "outbound": [ + { + "trackSid": "TR_VSr7CavPmttCck", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 44.7, + "packetsLost": 0, + "fps": 29, + "codec": "video/VP8" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 1, + "outgoingVideoFramesProduced": 2697, + "outgoingVideoFramesAccepted": 2697, + "outgoingVideoFramesDropped": 91, + "outgoingVideoFramesCoalesced": 91, + "outgoingVideoFramesCaptured": 2605, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 28.98, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 34, + "outgoingVideoMaxPushLatencyMs": 34, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 6, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_eQVNaBPkxUPX", + "trackSid": "TR_VSr7CavPmttCck", + "kind": "video", + "bitrateKbps": 43.1, + "packetsLost": 0, + "jitterMs": 3, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/cachyos/cachyos-nvidia-livekit-720p-hevc-failfast-20260605T205643Z.json b/fluxer_desktop/native-media-reports/cachyos/cachyos-nvidia-livekit-720p-hevc-failfast-20260605T205643Z.json new file mode 100644 index 0000000..ca517d7 --- /dev/null +++ b/fluxer_desktop/native-media-reports/cachyos/cachyos-nvidia-livekit-720p-hevc-failfast-20260605T205643Z.json @@ -0,0 +1,226 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T20:56:44.980Z", + "endedAt": "2026-06-05T20:56:45.769Z", + "platform": "linux", + "arch": "x64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-94317-ce85bdff-9476-465a-9f9a-fae49f6c168d", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-94317", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-94317", + "subscriberIdentity": "fluxer-native-subscriber-94317", + "required": true, + "strict": true, + "timeoutMs": 90000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 15000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 30, + "minFpsRatio": 0.9, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": null, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["hevc"], + "expectedScreenCodecs": ["hevc"], + "codec": "hevc", + "expectedScreenCodec": "video/H265", + "secondaryPublisher": false, + "secondaryPublisherCodec": "hevc", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/H265", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": false, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 400000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": false, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/home/hampus/Development/fluxer/fluxer_desktop/native-media-reports/cachyos-nvidia-livekit-720p-hevc-failfast-20260605T205643Z.json", + "verbose": false, + "scenarioName": "codec-hevc", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": false + }, + { + "name": "subscriber remote screenshare subscription", + "pass": false + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": false + }, + { + "name": "subscriber video frame callback", + "pass": false + }, + { + "name": "publisher outbound screenshare stats", + "pass": false + }, + { + "name": "subscriber inbound video stats", + "pass": false + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": false + }, + { + "name": "subscriber video resolution 1280x720", + "pass": false + }, + { + "name": "publisher outbound screenshare codec video/H265", + "pass": false + }, + { + "name": "subscriber inbound video codec video/H265", + "pass": false + }, + { + "name": "server publishing API participants listed", + "pass": false + }, + { + "name": "server sees publisher screenshare publication", + "pass": false + } + ], + "skippedReason": null, + "error": "Error: H.265 publishing requires hardware encoder support; missing NVENC encode session (checked 1 CUDA device; last status NV_ENC_ERR_UNSUPPORTED_DEVICE (2))", + "hardwareEncoder": { + "available": false, + "backend": "none", + "compiled": true, + "runtime": false, + "codecs": [], + "zeroCopy": false, + "nativeInputs": [], + "reason": "no_supported_devices", + "detail": "missing NVENC encode session (checked 1 CUDA device; last status NV_ENC_ERR_UNSUPPORTED_DEVICE (2))" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 0, + "strictAudioFrames": 0, + "strictVideoFrames": 0, + "videoCallbacks": 0, + "videoBytes": 0, + "receivedFps": 0, + "requiredFps": 27, + "maxVideoFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 0, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": {}, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": null, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": null, + "error": null, + "participants": [] + }, + "lastVideoMeta": null, + "publisherStats": { + "rttMs": null, + "outbound": [], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": null, + "outbound": [], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/cachyos/cachyos-nvidia-livekit-alltracks-serverpub-mixed-20260605T205705Z.json b/fluxer_desktop/native-media-reports/cachyos/cachyos-nvidia-livekit-alltracks-serverpub-mixed-20260605T205705Z.json new file mode 100644 index 0000000..033a450 --- /dev/null +++ b/fluxer_desktop/native-media-reports/cachyos/cachyos-nvidia-livekit-alltracks-serverpub-mixed-20260605T205705Z.json @@ -0,0 +1,637 @@ +{ + "status": "pass", + "startedAt": "2026-06-05T20:57:06.276Z", + "endedAt": "2026-06-05T20:57:28.255Z", + "platform": "linux", + "arch": "x64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-94553-a59031d7-277a-4d31-a290-59cea2c79f1c", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-94553", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-94553", + "subscriberIdentity": "fluxer-native-subscriber-94553", + "required": true, + "strict": true, + "timeoutMs": 90000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 20000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 30, + "minFpsRatio": 0.9, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 150, + "maxPacketLoss": null, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["h264"], + "expectedScreenCodecs": ["h264"], + "codec": "h264", + "expectedScreenCodec": "video/H264", + "secondaryPublisher": true, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": true, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": true, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 400000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": false, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/home/hampus/Development/fluxer/fluxer_desktop/native-media-reports/cachyos-nvidia-livekit-alltracks-serverpub-mixed-20260605T205705Z.json", + "verbose": false, + "scenarioName": "codec-h264-with-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 6", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 2", + "pass": true + }, + { + "name": "subscriber video resolution 1280x720", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 12", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 4", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 12", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "secondary publisher connected", + "pass": true + }, + { + "name": "secondary publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote secondary screenshare subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare stats", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound secondary codec video/VP8", + "pass": true + }, + { + "name": "secondary publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote secondary microphone subscription", + "pass": true + }, + { + "name": "secondary publisher outbound microphone stats", + "pass": true + }, + { + "name": "secondary publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote secondary screen-share audio subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "subscriber secondary data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/H264", + "pass": true + }, + { + "name": "subscriber inbound video codec video/H264", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + }, + { + "name": "server sees secondary publisher screenshare publication", + "pass": true + }, + { + "name": "server sees secondary publisher microphone publication", + "pass": true + }, + { + "name": "server sees secondary publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": null, + "hardwareEncoder": { + "available": false, + "backend": "none", + "compiled": true, + "runtime": false, + "codecs": [], + "zeroCopy": false, + "nativeInputs": [], + "reason": "no_supported_devices", + "detail": "missing NVENC encode session (checked 1 CUDA device; last status NV_ENC_ERR_UNSUPPORTED_DEVICE (2))" + }, + "metrics": { + "inboundAudioFrames": 8352, + "inboundVideoFrames": 1182, + "strictAudioFrames": 200, + "strictVideoFrames": 1164, + "videoCallbacks": 1180, + "videoBytes": 1631232000, + "receivedFps": 58.16, + "requiredFps": 27, + "maxVideoFrameGapMs": 98, + "maxAudioFrameGapMs": 101, + "maxVideoFrameGapOverallMs": 98, + "maxAudioFrameGapOverallMs": 101, + "avDriftMs": 36, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": { + "1280x720": 1164 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780693027399, + "stoppedAtMs": null, + "framesPushed": 626, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 39, + "lastPushAtMs": 1780693048233, + "elapsedMs": 21015, + "producedFps": 29.79 + }, + "secondaryPublisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780693027400, + "stoppedAtMs": null, + "framesPushed": 626, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 40, + "lastPushAtMs": 1780693048233, + "elapsedMs": 21014, + "producedFps": 29.79 + }, + "serverPublishing": { + "fetchedAtMs": 1780693027997, + "error": null, + "participants": [ + { + "identity": "fluxer-native-publisher-94553", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_AsXuPkpunfLw9E", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_VStW6iBWUPJvYp", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/H264", + "muted": false + }, + { + "sid": "TR_AMraWSQbYVLoud", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-subscriber-94553", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-2-94553", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_VSDUf6mLBmkksq", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + }, + { + "sid": "TR_AMuo9vvgBhxrxp", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_Asf2caReF2dUoe", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_sFVVyQMPfG8Z", + "trackSid": "TR_VSDUf6mLBmkksq", + "width": 1280, + "height": 720, + "timestampUs": 68952853000 + }, + "publisherStats": { + "rttMs": 15, + "outbound": [ + { + "trackSid": "TR_VStW6iBWUPJvYp", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 399.8, + "packetsLost": 0, + "fps": 29, + "codec": "video/H264" + }, + { + "trackSid": "TR_AsXuPkpunfLw9E", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 258.9, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_AMraWSQbYVLoud", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 97.8, + "packetsLost": 0, + "codec": "audio/opus" + } + ], + "inbound": [ + { + "participantSid": "PA_sFVVyQMPfG8Z", + "trackSid": "TR_AMuo9vvgBhxrxp", + "kind": "audio", + "bitrateKbps": 104.8, + "packetsLost": 0, + "jitterMs": 3, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_sFVVyQMPfG8Z", + "trackSid": "TR_Asf2caReF2dUoe", + "kind": "audio", + "bitrateKbps": 274.6, + "packetsLost": 0, + "jitterMs": 2, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_sFVVyQMPfG8Z", + "trackSid": "TR_VSDUf6mLBmkksq", + "kind": "video", + "bitrateKbps": 31.6, + "packetsLost": 0, + "jitterMs": 5, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 617, + "outgoingVideoFramesAccepted": 617, + "outgoingVideoFramesDropped": 19, + "outgoingVideoFramesCoalesced": 19, + "outgoingVideoFramesCaptured": 598, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 29.05, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 33, + "outgoingVideoMaxPushLatencyMs": 34, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 30, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": { + "rttMs": 38, + "outbound": [ + { + "trackSid": "TR_Asf2caReF2dUoe", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 261.7, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSDUf6mLBmkksq", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 29.4, + "packetsLost": 0, + "fps": 29, + "codec": "video/VP8" + }, + { + "trackSid": "TR_AMuo9vvgBhxrxp", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 98.8, + "packetsLost": 0, + "codec": "audio/opus" + } + ], + "inbound": [ + { + "participantSid": "PA_mQQgkdVbS4Wi", + "trackSid": "TR_AsXuPkpunfLw9E", + "kind": "audio", + "bitrateKbps": 265.5, + "packetsLost": 0, + "jitterMs": 3, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_mQQgkdVbS4Wi", + "trackSid": "TR_VStW6iBWUPJvYp", + "kind": "video", + "bitrateKbps": 402.2, + "packetsLost": 0, + "jitterMs": 5, + "codec": "video/H264" + }, + { + "participantSid": "PA_mQQgkdVbS4Wi", + "trackSid": "TR_AMraWSQbYVLoud", + "kind": "audio", + "bitrateKbps": 100.3, + "packetsLost": 0, + "jitterMs": 2, + "audioLevel": 0.307, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 618, + "outgoingVideoFramesAccepted": 618, + "outgoingVideoFramesDropped": 19, + "outgoingVideoFramesCoalesced": 19, + "outgoingVideoFramesCaptured": 599, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 29.07, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 33, + "outgoingVideoMaxPushLatencyMs": 34, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 30, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "subscriberStats": { + "rttMs": 6, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_sFVVyQMPfG8Z", + "trackSid": "TR_AMuo9vvgBhxrxp", + "kind": "audio", + "bitrateKbps": 100.8, + "packetsLost": 0, + "jitterMs": 3, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_sFVVyQMPfG8Z", + "trackSid": "TR_VSDUf6mLBmkksq", + "kind": "video", + "bitrateKbps": 29.4, + "packetsLost": 0, + "jitterMs": 5, + "codec": "video/VP8" + }, + { + "participantSid": "PA_sFVVyQMPfG8Z", + "trackSid": "TR_Asf2caReF2dUoe", + "kind": "audio", + "bitrateKbps": 271.7, + "packetsLost": 0, + "jitterMs": 2, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_mQQgkdVbS4Wi", + "trackSid": "TR_VStW6iBWUPJvYp", + "kind": "video", + "bitrateKbps": 399.8, + "packetsLost": 0, + "jitterMs": 5, + "codec": "video/H264" + }, + { + "participantSid": "PA_mQQgkdVbS4Wi", + "trackSid": "TR_AMraWSQbYVLoud", + "kind": "audio", + "bitrateKbps": 99.8, + "packetsLost": 0, + "jitterMs": 2, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_mQQgkdVbS4Wi", + "trackSid": "TR_AsXuPkpunfLw9E", + "kind": "audio", + "bitrateKbps": 263.9, + "packetsLost": 0, + "jitterMs": 3, + "audioLevel": 0.307, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h264-noadaptive-20260605T204148Z.json b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h264-noadaptive-20260605T204148Z.json new file mode 100644 index 0000000..29032e6 --- /dev/null +++ b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h264-noadaptive-20260605T204148Z.json @@ -0,0 +1,290 @@ +{ + "status": "pass", + "startedAt": "2026-06-05T20:41:48.766Z", + "endedAt": "2026-06-05T20:42:05.685Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:17880", + "serverApiUrl": "http://127.0.0.1:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-99979-bebc8448-fca8-4318-84fb-debd59234bd3", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-99979", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-99979", + "subscriberIdentity": "fluxer-native-subscriber-99979", + "required": true, + "strict": true, + "timeoutMs": 90000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 15000, + "width": 3840, + "height": 2160, + "expectedWidth": 3840, + "expectedHeight": 2160, + "fps": 60, + "minFpsRatio": 0.8, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": null, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["h264"], + "expectedScreenCodecs": ["h264"], + "codec": "h264", + "expectedScreenCodec": "video/H264", + "secondaryPublisher": false, + "secondaryPublisherCodec": "h264", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/H264", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": false, + "minVideoFps": 24, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": false, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 80000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": false, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": "videotoolbox", + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h264-noadaptive-20260605T204148Z.json", + "verbose": false, + "scenarioName": "codec-h264", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 3840x2160", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/H264", + "pass": true + }, + { + "name": "subscriber inbound video codec video/H264", + "pass": true + }, + { + "name": "hardware encoder backend videotoolbox", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + } + ], + "skippedReason": null, + "error": null, + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 787, + "strictAudioFrames": 0, + "strictVideoFrames": 783, + "videoCallbacks": 786, + "videoBytes": 9779097600, + "receivedFps": 52.46, + "requiredFps": 48, + "maxVideoFrameGapMs": 49, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 313, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 12, + "maxObservedPacketLossDelta": 11, + "videoResolutionCounts": { + "3840x2160": 783 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 60, + "startedAtMs": 1780692109289, + "stoppedAtMs": null, + "framesPushed": 982, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 44, + "lastPushAtMs": 1780692125674, + "elapsedMs": 16404, + "producedFps": 59.86 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780692110403, + "error": null, + "participants": [ + { + "identity": "fluxer-native-subscriber-99979", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-99979", + "trackCount": 1, + "tracks": [ + { + "sid": "TR_VShAhgz9DukUav", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/H264", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_7vLduTJN79oF", + "trackSid": "TR_VShAhgz9DukUav", + "width": 3840, + "height": 2160, + "timestampUs": 246972075000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_VShAhgz9DukUav", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 226, + "packetsLost": 0, + "fps": 53, + "codec": "video/H264" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 1, + "outgoingVideoFramesProduced": 943, + "outgoingVideoFramesAccepted": 943, + "outgoingVideoFramesDropped": 80, + "outgoingVideoFramesCoalesced": 80, + "outgoingVideoFramesCaptured": 862, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 54.78, + "outgoingVideoTargetFps": 60, + "outgoingVideoMaxQueueAgeMs": 18, + "outgoingVideoMaxPushLatencyMs": 18, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "full", + "adaptiveSendReason": "adaptiveDisabled" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_7vLduTJN79oF", + "trackSid": "TR_VShAhgz9DukUav", + "kind": "video", + "bitrateKbps": 226.1, + "packetsLost": 12, + "jitterMs": 4, + "codec": "video/H264" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h264-restarted-20260605T204256Z.json b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h264-restarted-20260605T204256Z.json new file mode 100644 index 0000000..f48e604 --- /dev/null +++ b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h264-restarted-20260605T204256Z.json @@ -0,0 +1,290 @@ +{ + "status": "pass", + "startedAt": "2026-06-05T20:42:57.137Z", + "endedAt": "2026-06-05T20:43:14.930Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:17880", + "serverApiUrl": "http://127.0.0.1:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-215-d2420544-1d14-499d-b7ed-39246026eeba", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-215", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-215", + "subscriberIdentity": "fluxer-native-subscriber-215", + "required": true, + "strict": true, + "timeoutMs": 90000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 15000, + "width": 3840, + "height": 2160, + "expectedWidth": 3840, + "expectedHeight": 2160, + "fps": 60, + "minFpsRatio": 0.8, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": null, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["h264"], + "expectedScreenCodecs": ["h264"], + "codec": "h264", + "expectedScreenCodec": "video/H264", + "secondaryPublisher": false, + "secondaryPublisherCodec": "h264", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/H264", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": false, + "minVideoFps": 24, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": false, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 80000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": false, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": "videotoolbox", + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h264-restarted-20260605T204256Z.json", + "verbose": false, + "scenarioName": "codec-h264", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 3840x2160", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/H264", + "pass": true + }, + { + "name": "subscriber inbound video codec video/H264", + "pass": true + }, + { + "name": "hardware encoder backend videotoolbox", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + } + ], + "skippedReason": null, + "error": null, + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 835, + "strictAudioFrames": 0, + "strictVideoFrames": 780, + "videoCallbacks": 835, + "videoBytes": 10388736000, + "receivedFps": 51.9, + "requiredFps": 48, + "maxVideoFrameGapMs": 103, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 133, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 8, + "maxObservedPacketLossDelta": 2, + "videoResolutionCounts": { + "3840x2160": 780 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 60, + "startedAtMs": 1780692177759, + "stoppedAtMs": null, + "framesPushed": 1029, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 47, + "lastPushAtMs": 1780692194929, + "elapsedMs": 17180, + "producedFps": 59.9 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780692179914, + "error": null, + "participants": [ + { + "identity": "fluxer-native-subscriber-215", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-215", + "trackCount": 1, + "tracks": [ + { + "sid": "TR_VSt23d8AR7uRMx", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/H264", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_ywoDZo9MfjAs", + "trackSid": "TR_VSt23d8AR7uRMx", + "width": 3840, + "height": 2160, + "timestampUs": 247041331000 + }, + "publisherStats": { + "rttMs": null, + "outbound": [ + { + "trackSid": "TR_VSt23d8AR7uRMx", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 222, + "packetsLost": 0, + "fps": 52, + "codec": "video/H264" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 1, + "outgoingVideoFramesProduced": 1000, + "outgoingVideoFramesAccepted": 1000, + "outgoingVideoFramesDropped": 87, + "outgoingVideoFramesCoalesced": 87, + "outgoingVideoFramesCaptured": 912, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 54.65, + "outgoingVideoTargetFps": 60, + "outgoingVideoMaxQueueAgeMs": 18, + "outgoingVideoMaxPushLatencyMs": 19, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "full", + "adaptiveSendReason": "adaptiveDisabled" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_ywoDZo9MfjAs", + "trackSid": "TR_VSt23d8AR7uRMx", + "kind": "video", + "bitrateKbps": 221.7, + "packetsLost": 8, + "jitterMs": 4, + "codec": "video/H264" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h265-20260605T203835Z.json b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h265-20260605T203835Z.json new file mode 100644 index 0000000..70f4234 --- /dev/null +++ b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h265-20260605T203835Z.json @@ -0,0 +1,290 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T20:38:35.984Z", + "endedAt": "2026-06-05T20:39:09.401Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:17880", + "serverApiUrl": "http://127.0.0.1:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-99305-9b3ba47d-c306-44e8-93be-2f3f219d81d3", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-99305", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-99305", + "subscriberIdentity": "fluxer-native-subscriber-99305", + "required": true, + "strict": true, + "timeoutMs": 90000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 30000, + "width": 3840, + "height": 2160, + "expectedWidth": 3840, + "expectedHeight": 2160, + "fps": 60, + "minFpsRatio": 0.8, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": null, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["hevc"], + "expectedScreenCodecs": ["hevc"], + "codec": "hevc", + "expectedScreenCodec": "video/H265", + "secondaryPublisher": false, + "secondaryPublisherCodec": "hevc", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/H265", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 24, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": false, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 80000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": false, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": "videotoolbox", + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h265-20260605T203835Z.json", + "verbose": false, + "scenarioName": "codec-hevc", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 3840x2160", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/H265", + "pass": true + }, + { + "name": "subscriber inbound video codec video/H265", + "pass": true + }, + { + "name": "hardware encoder backend videotoolbox", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + } + ], + "skippedReason": null, + "error": "received FPS 29.9 below 48", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 911, + "strictAudioFrames": 0, + "strictVideoFrames": 898, + "videoCallbacks": 911, + "videoBytes": 11334297600, + "receivedFps": 29.9, + "requiredFps": 48, + "maxVideoFrameGapMs": 124, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 124, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 4, + "maxObservedPacketLossDelta": 4, + "videoResolutionCounts": { + "3840x2160": 898 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 60, + "startedAtMs": 1780691916459, + "stoppedAtMs": null, + "framesPushed": 1977, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 35, + "lastPushAtMs": 1780691949392, + "elapsedMs": 32950, + "producedFps": 60 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780691919120, + "error": null, + "participants": [ + { + "identity": "fluxer-native-subscriber-99305", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-99305", + "trackCount": 1, + "tracks": [ + { + "sid": "TR_VSVHTFzX6KKP2d", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/H265", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_p6AhRM2ZweHm", + "trackSid": "TR_VSVHTFzX6KKP2d", + "width": 3840, + "height": 2160, + "timestampUs": 246795792000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_VSVHTFzX6KKP2d", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 80.1, + "packetsLost": 0, + "fps": 29, + "codec": "video/H265" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 1, + "outgoingVideoFramesProduced": 1975, + "outgoingVideoFramesAccepted": 1975, + "outgoingVideoFramesDropped": 906, + "outgoingVideoFramesCoalesced": 906, + "outgoingVideoFramesCaptured": 1068, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 32.44, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 18, + "outgoingVideoMaxPushLatencyMs": 18, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "fps30", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_p6AhRM2ZweHm", + "trackSid": "TR_VSVHTFzX6KKP2d", + "kind": "video", + "bitrateKbps": 83, + "packetsLost": 4, + "jitterMs": 2, + "codec": "video/H265" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h265-noadaptive-20260605T204048Z.json b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h265-noadaptive-20260605T204048Z.json new file mode 100644 index 0000000..c2e01fb --- /dev/null +++ b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h265-noadaptive-20260605T204048Z.json @@ -0,0 +1,290 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T20:40:48.428Z", + "endedAt": "2026-06-05T20:41:21.891Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:17880", + "serverApiUrl": "http://127.0.0.1:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-99799-9b73c5f8-24ee-46a6-b641-2268cab488bf", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-99799", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-99799", + "subscriberIdentity": "fluxer-native-subscriber-99799", + "required": true, + "strict": true, + "timeoutMs": 90000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 30000, + "width": 3840, + "height": 2160, + "expectedWidth": 3840, + "expectedHeight": 2160, + "fps": 60, + "minFpsRatio": 0.8, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": null, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["hevc"], + "expectedScreenCodecs": ["hevc"], + "codec": "hevc", + "expectedScreenCodec": "video/H265", + "secondaryPublisher": false, + "secondaryPublisherCodec": "hevc", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/H265", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": false, + "minVideoFps": 24, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": false, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 80000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": false, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": "videotoolbox", + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-h265-noadaptive-20260605T204048Z.json", + "verbose": false, + "scenarioName": "codec-hevc", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 3840x2160", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/H265", + "pass": true + }, + { + "name": "subscriber inbound video codec video/H265", + "pass": true + }, + { + "name": "hardware encoder backend videotoolbox", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + } + ], + "skippedReason": null, + "error": "received FPS 47.42 below 48", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 1439, + "strictAudioFrames": 0, + "strictVideoFrames": 1424, + "videoCallbacks": 1438, + "videoBytes": 17891020800, + "receivedFps": 47.42, + "requiredFps": 48, + "maxVideoFrameGapMs": 119, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 119, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 4, + "maxObservedPacketLossDelta": 4, + "videoResolutionCounts": { + "3840x2160": 1424 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 60, + "startedAtMs": 1780692048955, + "stoppedAtMs": null, + "framesPushed": 1977, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 27, + "lastPushAtMs": 1780692081889, + "elapsedMs": 32944, + "producedFps": 60.01 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780692051599, + "error": null, + "participants": [ + { + "identity": "fluxer-native-publisher-99799", + "trackCount": 1, + "tracks": [ + { + "sid": "TR_VS7whvguuwE7Ep", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/H265", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-subscriber-99799", + "trackCount": 0, + "tracks": [] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_6qPYCyPtKNMy", + "trackSid": "TR_VS7whvguuwE7Ep", + "width": 3840, + "height": 2160, + "timestampUs": 246928279000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_VS7whvguuwE7Ep", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 132.9, + "packetsLost": 0, + "fps": 47, + "codec": "video/H265" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 1971, + "outgoingVideoFramesAccepted": 1971, + "outgoingVideoFramesDropped": 169, + "outgoingVideoFramesCoalesced": 169, + "outgoingVideoFramesCaptured": 1802, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 54.85, + "outgoingVideoTargetFps": 60, + "outgoingVideoMaxQueueAgeMs": 18, + "outgoingVideoMaxPushLatencyMs": 18, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "full", + "adaptiveSendReason": "adaptiveDisabled" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_6qPYCyPtKNMy", + "trackSid": "TR_VS7whvguuwE7Ep", + "kind": "video", + "bitrateKbps": 135.8, + "packetsLost": 4, + "jitterMs": 6, + "codec": "video/H265" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-vp8-noadaptive-20260605T204205Z.json b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-vp8-noadaptive-20260605T204205Z.json new file mode 100644 index 0000000..b35a60e --- /dev/null +++ b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-vp8-noadaptive-20260605T204205Z.json @@ -0,0 +1,290 @@ +{ + "status": "pass", + "startedAt": "2026-06-05T20:42:06.035Z", + "endedAt": "2026-06-05T20:42:22.648Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:17880", + "serverApiUrl": "http://127.0.0.1:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-99996-9f3eea01-c794-44e7-a66d-a39e3b06f591", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-99996", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-99996", + "subscriberIdentity": "fluxer-native-subscriber-99996", + "required": true, + "strict": true, + "timeoutMs": 90000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 15000, + "width": 3840, + "height": 2160, + "expectedWidth": 3840, + "expectedHeight": 2160, + "fps": 60, + "minFpsRatio": 0.8, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": null, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": false, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": false, + "minVideoFps": 24, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": false, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 80000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": false, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": "videotoolbox", + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-4k60-vp8-noadaptive-20260605T204205Z.json", + "verbose": false, + "scenarioName": "codec-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 3840x2160", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "hardware encoder backend videotoolbox", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + } + ], + "skippedReason": null, + "error": null, + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 835, + "strictAudioFrames": 0, + "strictVideoFrames": 829, + "videoCallbacks": 835, + "videoBytes": 10388736000, + "receivedFps": 55.21, + "requiredFps": 48, + "maxVideoFrameGapMs": 138, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 138, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 18, + "maxObservedPacketLossDelta": 17, + "videoResolutionCounts": { + "3840x2160": 829 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 60, + "startedAtMs": 1780692126456, + "stoppedAtMs": null, + "framesPushed": 966, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 28, + "lastPushAtMs": 1780692142632, + "elapsedMs": 16199, + "producedFps": 59.63 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780692127628, + "error": null, + "participants": [ + { + "identity": "fluxer-native-publisher-99996", + "trackCount": 1, + "tracks": [ + { + "sid": "TR_VSgayVkGgydiPJ", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-subscriber-99996", + "trackCount": 0, + "tracks": [] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_CRYMSnWyNXJJ", + "trackSid": "TR_VSgayVkGgydiPJ", + "width": 3840, + "height": 2160, + "timestampUs": 246989049000 + }, + "publisherStats": { + "rttMs": null, + "outbound": [ + { + "trackSid": "TR_VSgayVkGgydiPJ", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 347.3, + "packetsLost": 0, + "fps": 55, + "codec": "video/VP8" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 952, + "outgoingVideoFramesAccepted": 952, + "outgoingVideoFramesDropped": 83, + "outgoingVideoFramesCoalesced": 83, + "outgoingVideoFramesCaptured": 869, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 54.77, + "outgoingVideoTargetFps": 60, + "outgoingVideoMaxQueueAgeMs": 18, + "outgoingVideoMaxPushLatencyMs": 18, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "full", + "adaptiveSendReason": "adaptiveDisabled" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_CRYMSnWyNXJJ", + "trackSid": "TR_VSgayVkGgydiPJ", + "kind": "video", + "bitrateKbps": 347.9, + "packetsLost": 18, + "jitterMs": 3, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-720p-hevc-after-guard-20260605T205747Z.json b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-720p-hevc-after-guard-20260605T205747Z.json new file mode 100644 index 0000000..2869704 --- /dev/null +++ b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-720p-hevc-after-guard-20260605T205747Z.json @@ -0,0 +1,290 @@ +{ + "status": "pass", + "startedAt": "2026-06-05T20:57:47.344Z", + "endedAt": "2026-06-05T20:58:04.558Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-1177-7a01a114-afdb-422c-ac68-dd6da5106b46", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-1177", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-1177", + "subscriberIdentity": "fluxer-native-subscriber-1177", + "required": true, + "strict": true, + "timeoutMs": 90000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 15000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 30, + "minFpsRatio": 0.9, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": null, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["hevc"], + "expectedScreenCodecs": ["hevc"], + "codec": "hevc", + "expectedScreenCodec": "video/H265", + "secondaryPublisher": false, + "secondaryPublisherCodec": "hevc", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/H265", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": false, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 400000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": false, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": "videotoolbox", + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-720p-hevc-after-guard-20260605T205747Z.json", + "verbose": false, + "scenarioName": "codec-hevc", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 1280x720", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/H265", + "pass": true + }, + { + "name": "subscriber inbound video codec video/H265", + "pass": true + }, + { + "name": "hardware encoder backend videotoolbox", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + } + ], + "skippedReason": null, + "error": null, + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 424, + "strictAudioFrames": 0, + "strictVideoFrames": 420, + "videoCallbacks": 423, + "videoBytes": 584755200, + "receivedFps": 28.28, + "requiredFps": 27, + "maxVideoFrameGapMs": 132, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 500, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": { + "1280x720": 420 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780693067890, + "stoppedAtMs": null, + "framesPushed": 501, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 55, + "lastPushAtMs": 1780693084557, + "elapsedMs": 16678, + "producedFps": 30.04 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780693069535, + "error": null, + "participants": [ + { + "identity": "fluxer-native-subscriber-1177", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-1177", + "trackCount": 1, + "tracks": [ + { + "sid": "TR_VScM2KL4qqaVMf", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/H265", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_CEoLG8a8qXZr", + "trackSid": "TR_VScM2KL4qqaVMf", + "width": 1280, + "height": 720, + "timestampUs": 247930927000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_VScM2KL4qqaVMf", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 31.2, + "packetsLost": 0, + "fps": 28, + "codec": "video/H265" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 477, + "outgoingVideoFramesAccepted": 477, + "outgoingVideoFramesDropped": 23, + "outgoingVideoFramesCoalesced": 23, + "outgoingVideoFramesCaptured": 454, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 28.53, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 34, + "outgoingVideoMaxPushLatencyMs": 34, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_CEoLG8a8qXZr", + "trackSid": "TR_VScM2KL4qqaVMf", + "kind": "video", + "bitrateKbps": 31.2, + "packetsLost": 0, + "jitterMs": 3, + "codec": "video/H265" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-alltracks-serverpub-20260605T204337Z.json b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-alltracks-serverpub-20260605T204337Z.json new file mode 100644 index 0000000..6845594 --- /dev/null +++ b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-alltracks-serverpub-20260605T204337Z.json @@ -0,0 +1,636 @@ +{ + "status": "pass", + "startedAt": "2026-06-05T20:43:38.058Z", + "endedAt": "2026-06-05T20:44:00.477Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:17880", + "serverApiUrl": "http://127.0.0.1:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-287-d177ae47-231a-4e5f-8b33-e21435c1d78c", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-287", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-287", + "subscriberIdentity": "fluxer-native-subscriber-287", + "required": true, + "strict": true, + "timeoutMs": 90000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 20000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 30, + "minFpsRatio": 0.9, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 120, + "maxPacketLoss": null, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["h264"], + "expectedScreenCodecs": ["h264"], + "codec": "h264", + "expectedScreenCodec": "video/H264", + "secondaryPublisher": true, + "secondaryPublisherCodec": "h264", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/H264", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 400000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": false, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-alltracks-serverpub-20260605T204337Z.json", + "verbose": false, + "scenarioName": "codec-h264-with-h264", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 6", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 2", + "pass": true + }, + { + "name": "subscriber video resolution 1280x720", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 12", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 4", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 12", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "secondary publisher connected", + "pass": true + }, + { + "name": "secondary publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote secondary screenshare subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare stats", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare codec video/H264", + "pass": true + }, + { + "name": "subscriber inbound secondary codec video/H264", + "pass": true + }, + { + "name": "secondary publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote secondary microphone subscription", + "pass": true + }, + { + "name": "secondary publisher outbound microphone stats", + "pass": true + }, + { + "name": "secondary publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote secondary screen-share audio subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "subscriber secondary data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/H264", + "pass": true + }, + { + "name": "subscriber inbound video codec video/H264", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + }, + { + "name": "server sees secondary publisher screenshare publication", + "pass": true + }, + { + "name": "server sees secondary publisher microphone publication", + "pass": true + }, + { + "name": "server sees secondary publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": null, + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 8688, + "inboundVideoFrames": 1105, + "strictAudioFrames": 200, + "strictVideoFrames": 1096, + "videoCallbacks": 1105, + "videoBytes": 1527552000, + "receivedFps": 55.07, + "requiredFps": 27, + "maxVideoFrameGapMs": 137, + "maxAudioFrameGapMs": 102, + "maxVideoFrameGapOverallMs": 278, + "maxAudioFrameGapOverallMs": 102, + "avDriftMs": 16, + "maxObservedPacketLoss": 193, + "maxObservedPacketLossDelta": 181, + "videoResolutionCounts": { + "1280x720": 1096 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780692218670, + "stoppedAtMs": null, + "framesPushed": 655, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 35, + "lastPushAtMs": 1780692240471, + "elapsedMs": 21825, + "producedFps": 30.01 + }, + "secondaryPublisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780692218671, + "stoppedAtMs": null, + "framesPushed": 655, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 35, + "lastPushAtMs": 1780692240471, + "elapsedMs": 21824, + "producedFps": 30.01 + }, + "serverPublishing": { + "fetchedAtMs": 1780692220222, + "error": null, + "participants": [ + { + "identity": "fluxer-native-publisher-287", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_AMQmi9tr3ijwD7", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_AsVx6mXCeyrqVB", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_VSjjPS7DRgdqSi", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/H264", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-subscriber-287", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-2-287", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_VSVinYCWjRrXgb", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/H264", + "muted": false + }, + { + "sid": "TR_AMd395vNaQvHas", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_AsSinL94mqRm57", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_cdeic4ojSyhV", + "trackSid": "TR_VSVinYCWjRrXgb", + "width": 1280, + "height": 720, + "timestampUs": 247086842000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_AsVx6mXCeyrqVB", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 263.9, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_AMQmi9tr3ijwD7", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 98.7, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSjjPS7DRgdqSi", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 38.3, + "packetsLost": 0, + "fps": 28, + "codec": "video/H264" + } + ], + "inbound": [ + { + "participantSid": "PA_cdeic4ojSyhV", + "trackSid": "TR_AsSinL94mqRm57", + "kind": "audio", + "bitrateKbps": 242.9, + "packetsLost": 78, + "jitterMs": 0, + "audioLevel": 0.319, + "codec": "audio/opus" + }, + { + "participantSid": "PA_cdeic4ojSyhV", + "trackSid": "TR_VSVinYCWjRrXgb", + "kind": "video", + "bitrateKbps": 39, + "packetsLost": 15, + "jitterMs": 2, + "codec": "video/H264" + }, + { + "participantSid": "PA_cdeic4ojSyhV", + "trackSid": "TR_AMd395vNaQvHas", + "kind": "audio", + "bitrateKbps": 92.8, + "packetsLost": 86, + "jitterMs": 0, + "audioLevel": 0.307, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 1, + "outgoingVideoFramesProduced": 651, + "outgoingVideoFramesAccepted": 651, + "outgoingVideoFramesDropped": 27, + "outgoingVideoFramesCoalesced": 27, + "outgoingVideoFramesCaptured": 623, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 28.71, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 34, + "outgoingVideoMaxPushLatencyMs": 34, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 32, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": { + "rttMs": null, + "outbound": [ + { + "trackSid": "TR_AsSinL94mqRm57", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 257.9, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_AMd395vNaQvHas", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 97.6, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSVinYCWjRrXgb", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 40.3, + "packetsLost": 0, + "fps": 29, + "codec": "video/H264" + } + ], + "inbound": [ + { + "participantSid": "PA_ecrQddbJEyhw", + "trackSid": "TR_AsVx6mXCeyrqVB", + "kind": "audio", + "bitrateKbps": 233.5, + "packetsLost": 94, + "jitterMs": 0, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_ecrQddbJEyhw", + "trackSid": "TR_VSjjPS7DRgdqSi", + "kind": "video", + "bitrateKbps": 39.6, + "packetsLost": 13, + "jitterMs": 2, + "codec": "video/H264" + }, + { + "participantSid": "PA_ecrQddbJEyhw", + "trackSid": "TR_AMQmi9tr3ijwD7", + "kind": "audio", + "bitrateKbps": 99.1, + "packetsLost": 86, + "jitterMs": 1, + "audioLevel": 0.307, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 1, + "outgoingVideoFramesProduced": 653, + "outgoingVideoFramesAccepted": 653, + "outgoingVideoFramesDropped": 27, + "outgoingVideoFramesCoalesced": 27, + "outgoingVideoFramesCaptured": 625, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 28.71, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 34, + "outgoingVideoMaxPushLatencyMs": 34, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 32, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "subscriberStats": { + "rttMs": null, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_cdeic4ojSyhV", + "trackSid": "TR_AMd395vNaQvHas", + "kind": "audio", + "bitrateKbps": 98.3, + "packetsLost": 33, + "jitterMs": 0, + "audioLevel": 0.306, + "codec": "audio/opus" + }, + { + "participantSid": "PA_cdeic4ojSyhV", + "trackSid": "TR_VSVinYCWjRrXgb", + "kind": "video", + "bitrateKbps": 39, + "packetsLost": 42, + "jitterMs": 2, + "codec": "video/H264" + }, + { + "participantSid": "PA_cdeic4ojSyhV", + "trackSid": "TR_AsSinL94mqRm57", + "kind": "audio", + "bitrateKbps": 253.1, + "packetsLost": 2, + "jitterMs": 0, + "audioLevel": 0.319, + "codec": "audio/opus" + }, + { + "participantSid": "PA_ecrQddbJEyhw", + "trackSid": "TR_AsVx6mXCeyrqVB", + "kind": "audio", + "bitrateKbps": 253.3, + "packetsLost": 19, + "jitterMs": 0, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_ecrQddbJEyhw", + "trackSid": "TR_AMQmi9tr3ijwD7", + "kind": "audio", + "bitrateKbps": 98.3, + "packetsLost": 3, + "jitterMs": 1, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_ecrQddbJEyhw", + "trackSid": "TR_VSjjPS7DRgdqSi", + "kind": "video", + "bitrateKbps": 38.4, + "packetsLost": 43, + "jitterMs": 2, + "codec": "video/H264" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-alltracks-serverpub-mixed-20260605T204504Z.json b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-alltracks-serverpub-mixed-20260605T204504Z.json new file mode 100644 index 0000000..8aad55f --- /dev/null +++ b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-alltracks-serverpub-mixed-20260605T204504Z.json @@ -0,0 +1,636 @@ +{ + "status": "pass", + "startedAt": "2026-06-05T20:45:05.208Z", + "endedAt": "2026-06-05T20:45:28.695Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:17880", + "serverApiUrl": "http://127.0.0.1:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-421-04fac24c-55a5-4189-9d7a-5689875cf9b9", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-421", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-421", + "subscriberIdentity": "fluxer-native-subscriber-421", + "required": true, + "strict": true, + "timeoutMs": 90000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 20000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 30, + "minFpsRatio": 0.9, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 120, + "maxPacketLoss": null, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["h264"], + "expectedScreenCodecs": ["h264"], + "codec": "h264", + "expectedScreenCodec": "video/H264", + "secondaryPublisher": true, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": true, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": true, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 400000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": false, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-alltracks-serverpub-mixed-20260605T204504Z.json", + "verbose": false, + "scenarioName": "codec-h264-with-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 6", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 2", + "pass": true + }, + { + "name": "subscriber video resolution 1280x720", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 12", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 4", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 12", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "secondary publisher connected", + "pass": true + }, + { + "name": "secondary publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote secondary screenshare subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare stats", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound secondary codec video/VP8", + "pass": true + }, + { + "name": "secondary publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote secondary microphone subscription", + "pass": true + }, + { + "name": "secondary publisher outbound microphone stats", + "pass": true + }, + { + "name": "secondary publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote secondary screen-share audio subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "subscriber secondary data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/H264", + "pass": true + }, + { + "name": "subscriber inbound video codec video/H264", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + }, + { + "name": "server sees secondary publisher screenshare publication", + "pass": true + }, + { + "name": "server sees secondary publisher microphone publication", + "pass": true + }, + { + "name": "server sees secondary publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": null, + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 9080, + "inboundVideoFrames": 1223, + "strictAudioFrames": 199, + "strictVideoFrames": 1137, + "videoCallbacks": 1222, + "videoBytes": 1689292800, + "receivedFps": 56.81, + "requiredFps": 27, + "maxVideoFrameGapMs": 99, + "maxAudioFrameGapMs": 102, + "maxVideoFrameGapOverallMs": 100, + "maxAudioFrameGapOverallMs": 102, + "avDriftMs": 92, + "maxObservedPacketLoss": 186, + "maxObservedPacketLossDelta": 163, + "videoResolutionCounts": { + "1280x720": 1137 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780692305878, + "stoppedAtMs": null, + "framesPushed": 685, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 36, + "lastPushAtMs": 1780692328677, + "elapsedMs": 22834, + "producedFps": 30 + }, + "secondaryPublisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780692305878, + "stoppedAtMs": null, + "framesPushed": 685, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 36, + "lastPushAtMs": 1780692328677, + "elapsedMs": 22834, + "producedFps": 30 + }, + "serverPublishing": { + "fetchedAtMs": 1780692308436, + "error": null, + "participants": [ + { + "identity": "fluxer-native-publisher-421", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_VS73hbjzW6G7kV", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/H264", + "muted": false + }, + { + "sid": "TR_AMncny8z7koEGm", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_Aso75dfBWxWEyj", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-publisher-2-421", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_AsxFsT7RDTdKQu", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_VSUZjTy2hrcZCc", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + }, + { + "sid": "TR_AMuYbUnWUcAZxj", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-subscriber-421", + "trackCount": 0, + "tracks": [] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_J5CFmBPpkkyM", + "trackSid": "TR_VS73hbjzW6G7kV", + "width": 1280, + "height": 720, + "timestampUs": 247175096000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_VS73hbjzW6G7kV", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 38.6, + "packetsLost": 0, + "fps": 28, + "codec": "video/H264" + }, + { + "trackSid": "TR_Aso75dfBWxWEyj", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 261.1, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_AMncny8z7koEGm", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 98.3, + "packetsLost": 0, + "codec": "audio/opus" + } + ], + "inbound": [ + { + "participantSid": "PA_9zvEJKeZq55z", + "trackSid": "TR_VSUZjTy2hrcZCc", + "kind": "video", + "bitrateKbps": 29.7, + "packetsLost": 37, + "jitterMs": 3, + "codec": "video/VP8" + }, + { + "participantSid": "PA_9zvEJKeZq55z", + "trackSid": "TR_AsxFsT7RDTdKQu", + "kind": "audio", + "bitrateKbps": 249.7, + "packetsLost": 87, + "jitterMs": 0, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_9zvEJKeZq55z", + "trackSid": "TR_AMuYbUnWUcAZxj", + "kind": "audio", + "bitrateKbps": 91.6, + "packetsLost": 62, + "jitterMs": 0, + "audioLevel": 0.321, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 683, + "outgoingVideoFramesAccepted": 683, + "outgoingVideoFramesDropped": 29, + "outgoingVideoFramesCoalesced": 29, + "outgoingVideoFramesCaptured": 654, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 28.71, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 34, + "outgoingVideoMaxPushLatencyMs": 34, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 30, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_AMuYbUnWUcAZxj", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 99.2, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_AsxFsT7RDTdKQu", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 264.8, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSUZjTy2hrcZCc", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 29.7, + "packetsLost": 0, + "fps": 29, + "codec": "video/VP8" + } + ], + "inbound": [ + { + "participantSid": "PA_J5CFmBPpkkyM", + "trackSid": "TR_VS73hbjzW6G7kV", + "kind": "video", + "bitrateKbps": 38.6, + "packetsLost": 1, + "jitterMs": 3, + "codec": "video/H264" + }, + { + "participantSid": "PA_J5CFmBPpkkyM", + "trackSid": "TR_Aso75dfBWxWEyj", + "kind": "audio", + "bitrateKbps": 260.9, + "packetsLost": 4, + "jitterMs": 1, + "audioLevel": 0.318, + "codec": "audio/opus" + }, + { + "participantSid": "PA_J5CFmBPpkkyM", + "trackSid": "TR_AMncny8z7koEGm", + "kind": "audio", + "bitrateKbps": 98.3, + "packetsLost": 2, + "jitterMs": 1, + "audioLevel": 0.317, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 683, + "outgoingVideoFramesAccepted": 683, + "outgoingVideoFramesDropped": 29, + "outgoingVideoFramesCoalesced": 29, + "outgoingVideoFramesCaptured": 654, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 28.71, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 34, + "outgoingVideoMaxPushLatencyMs": 34, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 30, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_9zvEJKeZq55z", + "trackSid": "TR_VSUZjTy2hrcZCc", + "kind": "video", + "bitrateKbps": 30.1, + "packetsLost": 3, + "jitterMs": 3, + "codec": "video/VP8" + }, + { + "participantSid": "PA_9zvEJKeZq55z", + "trackSid": "TR_AsxFsT7RDTdKQu", + "kind": "audio", + "bitrateKbps": 259.8, + "packetsLost": 3, + "jitterMs": 0, + "audioLevel": 0.336, + "codec": "audio/opus" + }, + { + "participantSid": "PA_9zvEJKeZq55z", + "trackSid": "TR_AMuYbUnWUcAZxj", + "kind": "audio", + "bitrateKbps": 99, + "packetsLost": 3, + "jitterMs": 0, + "audioLevel": 0.352, + "codec": "audio/opus" + }, + { + "participantSid": "PA_J5CFmBPpkkyM", + "trackSid": "TR_AMncny8z7koEGm", + "kind": "audio", + "bitrateKbps": 95.5, + "packetsLost": 64, + "jitterMs": 1, + "audioLevel": 0.306, + "codec": "audio/opus" + }, + { + "participantSid": "PA_J5CFmBPpkkyM", + "trackSid": "TR_VS73hbjzW6G7kV", + "kind": "video", + "bitrateKbps": 40, + "packetsLost": 27, + "jitterMs": 3, + "codec": "video/H264" + }, + { + "participantSid": "PA_J5CFmBPpkkyM", + "trackSid": "TR_Aso75dfBWxWEyj", + "kind": "audio", + "bitrateKbps": 244.3, + "packetsLost": 67, + "jitterMs": 0, + "audioLevel": 0.31, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-multipub-audio-nosim-20260605T203740Z.json b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-multipub-audio-nosim-20260605T203740Z.json new file mode 100644 index 0000000..b76d07a --- /dev/null +++ b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-multipub-audio-nosim-20260605T203740Z.json @@ -0,0 +1,636 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T20:37:40.480Z", + "endedAt": "2026-06-05T20:37:52.123Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:17880", + "serverApiUrl": "http://127.0.0.1:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-99206-d8ecfb6b-5331-4b04-9ac0-0f510f626c7b", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-99206", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-99206", + "subscriberIdentity": "fluxer-native-subscriber-99206", + "required": true, + "strict": true, + "timeoutMs": 60000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 10000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 30, + "minFpsRatio": 0.94, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 80, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": true, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 4000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": false, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-multipub-audio-nosim-20260605T203740Z.json", + "verbose": false, + "scenarioName": "codec-vp8-with-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 6", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 2", + "pass": true + }, + { + "name": "subscriber video resolution 1280x720", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 12", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 4", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 12", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "secondary publisher connected", + "pass": true + }, + { + "name": "secondary publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote secondary screenshare subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare stats", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound secondary codec video/VP8", + "pass": true + }, + { + "name": "secondary publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote secondary microphone subscription", + "pass": true + }, + { + "name": "secondary publisher outbound microphone stats", + "pass": true + }, + { + "name": "secondary publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote secondary screen-share audio subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "subscriber secondary data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + }, + { + "name": "server sees secondary publisher screenshare publication", + "pass": true + }, + { + "name": "server sees secondary publisher microphone publication", + "pass": true + }, + { + "name": "server sees secondary publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": "A/V drift 86ms exceeds 80ms; observed packet loss delta 97 exceeds 0", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 4368, + "inboundVideoFrames": 630, + "strictAudioFrames": 99, + "strictVideoFrames": 575, + "videoCallbacks": 630, + "videoBytes": 870912000, + "receivedFps": 57.46, + "requiredFps": 28.2, + "maxVideoFrameGapMs": 139, + "maxAudioFrameGapMs": 102, + "maxVideoFrameGapOverallMs": 139, + "maxAudioFrameGapOverallMs": 102, + "avDriftMs": 86, + "maxObservedPacketLoss": 106, + "maxObservedPacketLossDelta": 97, + "videoResolutionCounts": { + "1280x720": 575 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780691861125, + "stoppedAtMs": null, + "framesPushed": 330, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 36, + "lastPushAtMs": 1780691872091, + "elapsedMs": 11091, + "producedFps": 29.75 + }, + "secondaryPublisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780691861125, + "stoppedAtMs": null, + "framesPushed": 330, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 36, + "lastPushAtMs": 1780691872091, + "elapsedMs": 11091, + "producedFps": 29.75 + }, + "serverPublishing": { + "fetchedAtMs": 1780691861869, + "error": null, + "participants": [ + { + "identity": "fluxer-native-subscriber-99206", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-99206", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_AsHmDRoRwwH2rj", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_VSFufssZ8C8oGR", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + }, + { + "sid": "TR_AMofk8na2BK2zW", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-publisher-2-99206", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_VSjFwfYRyNg8nM", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + }, + { + "sid": "TR_AMqoCF5pBKLLtZ", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_AsYeF5v2Lv8BFF", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_mPCx5P6en7uF", + "trackSid": "TR_VSjFwfYRyNg8nM", + "width": 1280, + "height": 720, + "timestampUs": 246718524000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_AMofk8na2BK2zW", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 98.8, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_AsHmDRoRwwH2rj", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 260.4, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSFufssZ8C8oGR", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 27.5, + "packetsLost": 0, + "fps": 28, + "codec": "video/VP8" + } + ], + "inbound": [ + { + "participantSid": "PA_mPCx5P6en7uF", + "trackSid": "TR_AMqoCF5pBKLLtZ", + "kind": "audio", + "bitrateKbps": 94.4, + "packetsLost": 36, + "jitterMs": 0, + "audioLevel": 0.337, + "codec": "audio/opus" + }, + { + "participantSid": "PA_mPCx5P6en7uF", + "trackSid": "TR_VSjFwfYRyNg8nM", + "kind": "video", + "bitrateKbps": 29, + "packetsLost": 20, + "jitterMs": 4, + "codec": "video/VP8" + }, + { + "participantSid": "PA_mPCx5P6en7uF", + "trackSid": "TR_AsYeF5v2Lv8BFF", + "kind": "audio", + "bitrateKbps": 252.3, + "packetsLost": 36, + "jitterMs": 0, + "audioLevel": 0.342, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 322, + "outgoingVideoFramesAccepted": 322, + "outgoingVideoFramesDropped": 14, + "outgoingVideoFramesCoalesced": 14, + "outgoingVideoFramesCaptured": 308, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 28.68, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 33, + "outgoingVideoMaxPushLatencyMs": 33, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 28, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_AsYeF5v2Lv8BFF", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 262.2, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSjFwfYRyNg8nM", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 28.9, + "packetsLost": 0, + "fps": 28, + "codec": "video/VP8" + }, + { + "trackSid": "TR_AMqoCF5pBKLLtZ", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 98.3, + "packetsLost": 0, + "codec": "audio/opus" + } + ], + "inbound": [ + { + "participantSid": "PA_ynXQqotkNFtm", + "trackSid": "TR_VSFufssZ8C8oGR", + "kind": "video", + "bitrateKbps": 26.6, + "packetsLost": 18, + "jitterMs": 3, + "codec": "video/VP8" + }, + { + "participantSid": "PA_ynXQqotkNFtm", + "trackSid": "TR_AMofk8na2BK2zW", + "kind": "audio", + "bitrateKbps": 98.8, + "packetsLost": 4, + "jitterMs": 1, + "audioLevel": 0.323, + "codec": "audio/opus" + }, + { + "participantSid": "PA_ynXQqotkNFtm", + "trackSid": "TR_AsHmDRoRwwH2rj", + "kind": "audio", + "bitrateKbps": 260.6, + "packetsLost": 3, + "jitterMs": 0, + "audioLevel": 0.307, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 322, + "outgoingVideoFramesAccepted": 322, + "outgoingVideoFramesDropped": 14, + "outgoingVideoFramesCoalesced": 14, + "outgoingVideoFramesCaptured": 308, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 28.68, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 33, + "outgoingVideoMaxPushLatencyMs": 33, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 28, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_mPCx5P6en7uF", + "trackSid": "TR_AMqoCF5pBKLLtZ", + "kind": "audio", + "bitrateKbps": 96.1, + "packetsLost": 0, + "jitterMs": 0, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_mPCx5P6en7uF", + "trackSid": "TR_AsYeF5v2Lv8BFF", + "kind": "audio", + "bitrateKbps": 260.2, + "packetsLost": 2, + "jitterMs": 0, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_mPCx5P6en7uF", + "trackSid": "TR_VSjFwfYRyNg8nM", + "kind": "video", + "bitrateKbps": 29.1, + "packetsLost": 7, + "jitterMs": 3, + "codec": "video/VP8" + }, + { + "participantSid": "PA_ynXQqotkNFtm", + "trackSid": "TR_AMofk8na2BK2zW", + "kind": "audio", + "bitrateKbps": 92.7, + "packetsLost": 40, + "jitterMs": 1, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_ynXQqotkNFtm", + "trackSid": "TR_AsHmDRoRwwH2rj", + "kind": "audio", + "bitrateKbps": 231.2, + "packetsLost": 45, + "jitterMs": 0, + "audioLevel": 0.312, + "codec": "audio/opus" + }, + { + "participantSid": "PA_ynXQqotkNFtm", + "trackSid": "TR_VSFufssZ8C8oGR", + "kind": "video", + "bitrateKbps": 27.8, + "packetsLost": 12, + "jitterMs": 3, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-smoke-20260605T202601Z.json b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-smoke-20260605T202601Z.json new file mode 100644 index 0000000..30f48b3 --- /dev/null +++ b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-smoke-20260605T202601Z.json @@ -0,0 +1,654 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T20:26:01.950Z", + "endedAt": "2026-06-05T20:26:13.710Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-97788-e96dacf0-ab09-478e-8f70-cff476c920c4", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-97788", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-97788", + "subscriberIdentity": "fluxer-native-subscriber-97788", + "required": true, + "strict": true, + "timeoutMs": 60000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 10000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 30, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 80, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": true, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": true, + "secondaryPublisherScreenSimulcast": true, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 4000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-smoke-20260605T202601Z.json", + "verbose": false, + "scenarioName": "codec-vp8-with-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 6", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 2", + "pass": true + }, + { + "name": "subscriber video resolution 1280x720", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 12", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 4", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 12", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "secondary publisher connected", + "pass": true + }, + { + "name": "secondary publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote secondary screenshare subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare stats", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound secondary codec video/VP8", + "pass": true + }, + { + "name": "secondary publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote secondary microphone subscription", + "pass": true + }, + { + "name": "secondary publisher outbound microphone stats", + "pass": true + }, + { + "name": "secondary publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote secondary screen-share audio subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "subscriber secondary data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + }, + { + "name": "server sees secondary publisher screenshare publication", + "pass": true + }, + { + "name": "server sees secondary publisher microphone publication", + "pass": true + }, + { + "name": "server sees secondary publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": "observed packet loss 104 exceeds 0; observed 2 video frames outside 1280x720", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 4408, + "inboundVideoFrames": 581, + "strictAudioFrames": 100, + "strictVideoFrames": 554, + "videoCallbacks": 579, + "videoBytes": 796262400, + "receivedFps": 56.03, + "requiredFps": 28.5, + "maxVideoFrameGapMs": 66, + "maxAudioFrameGapMs": 102, + "maxVideoFrameGapOverallMs": 128, + "maxAudioFrameGapOverallMs": 102, + "avDriftMs": 13, + "maxObservedPacketLoss": 104, + "videoResolutionCounts": { + "640x360": 2, + "1280x720": 552 + }, + "videoResolutionMismatchCount": 2, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780691162656, + "stoppedAtMs": null, + "framesPushed": 332, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 35, + "lastPushAtMs": 1780691173690, + "elapsedMs": 11153, + "producedFps": 29.77 + }, + "secondaryPublisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780691162656, + "stoppedAtMs": null, + "framesPushed": 332, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 35, + "lastPushAtMs": 1780691173690, + "elapsedMs": 11153, + "producedFps": 29.77 + }, + "serverPublishing": { + "fetchedAtMs": 1780691163699, + "error": null, + "participants": [ + { + "identity": "fluxer-native-subscriber-97788", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-2-97788", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_AMYDBcTLVPXW5R", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_VSSvARsPYMjnfH", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + }, + { + "sid": "TR_AseAEMuwQvUDUq", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-publisher-97788", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_AsEpo39LZXJwgU", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_AMZe28QCXUZLuv", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_VSVNYfqdKkzCqh", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_G5C4xWMJumgR", + "trackSid": "TR_VSSvARsPYMjnfH", + "width": 1280, + "height": 720, + "timestampUs": 246020100000 + }, + "publisherStats": { + "rttMs": null, + "outbound": [ + { + "trackSid": "TR_VSVNYfqdKkzCqh", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 1.9, + "packetsLost": 0, + "fps": 3, + "codec": "video/VP8" + }, + { + "trackSid": "TR_VSVNYfqdKkzCqh", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 35.4, + "packetsLost": 0, + "fps": 28, + "codec": "video/VP8" + }, + { + "trackSid": "TR_AMZe28QCXUZLuv", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 98.8, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_AsEpo39LZXJwgU", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 261.7, + "packetsLost": 0, + "codec": "audio/opus" + } + ], + "inbound": [ + { + "participantSid": "PA_G5C4xWMJumgR", + "trackSid": "TR_VSSvARsPYMjnfH", + "kind": "video", + "bitrateKbps": 2.3, + "packetsLost": 4, + "jitterMs": 14, + "codec": "video/VP8" + }, + { + "participantSid": "PA_G5C4xWMJumgR", + "trackSid": "TR_AMYDBcTLVPXW5R", + "kind": "audio", + "bitrateKbps": 98.2, + "packetsLost": 2, + "jitterMs": 0, + "audioLevel": 0.332, + "codec": "audio/opus" + }, + { + "participantSid": "PA_G5C4xWMJumgR", + "trackSid": "TR_AseAEMuwQvUDUq", + "kind": "audio", + "bitrateKbps": 238, + "packetsLost": 44, + "jitterMs": 0, + "audioLevel": 0.309, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 1, + "outgoingVideoFramesProduced": 326, + "outgoingVideoFramesAccepted": 326, + "outgoingVideoFramesDropped": 16, + "outgoingVideoFramesCoalesced": 16, + "outgoingVideoFramesCaptured": 309, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 28.49, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 33, + "outgoingVideoMaxPushLatencyMs": 33, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 46, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_AseAEMuwQvUDUq", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 257.7, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSSvARsPYMjnfH", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 32.8, + "packetsLost": 0, + "fps": 28, + "codec": "video/VP8" + }, + { + "trackSid": "TR_VSSvARsPYMjnfH", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 2.3, + "packetsLost": 0, + "fps": 3, + "codec": "video/VP8" + }, + { + "trackSid": "TR_AMYDBcTLVPXW5R", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 98.7, + "packetsLost": 0, + "codec": "audio/opus" + } + ], + "inbound": [ + { + "participantSid": "PA_t8raa7C57aBi", + "trackSid": "TR_AMZe28QCXUZLuv", + "kind": "audio", + "bitrateKbps": 96.9, + "packetsLost": 1, + "jitterMs": 1, + "audioLevel": 0.31, + "codec": "audio/opus" + }, + { + "participantSid": "PA_t8raa7C57aBi", + "trackSid": "TR_VSVNYfqdKkzCqh", + "kind": "video", + "bitrateKbps": 1.9, + "packetsLost": 1, + "jitterMs": 15, + "codec": "video/VP8" + }, + { + "participantSid": "PA_t8raa7C57aBi", + "trackSid": "TR_AsEpo39LZXJwgU", + "kind": "audio", + "bitrateKbps": 223.8, + "packetsLost": 56, + "jitterMs": 1, + "audioLevel": 0.336, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 1, + "outgoingVideoFramesProduced": 323, + "outgoingVideoFramesAccepted": 323, + "outgoingVideoFramesDropped": 16, + "outgoingVideoFramesCoalesced": 16, + "outgoingVideoFramesCaptured": 306, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 28.48, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 33, + "outgoingVideoMaxPushLatencyMs": 33, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 47, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_G5C4xWMJumgR", + "trackSid": "TR_AseAEMuwQvUDUq", + "kind": "audio", + "bitrateKbps": 258.1, + "packetsLost": 1, + "jitterMs": 0, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_G5C4xWMJumgR", + "trackSid": "TR_AMYDBcTLVPXW5R", + "kind": "audio", + "bitrateKbps": 91.1, + "packetsLost": 45, + "jitterMs": 0, + "audioLevel": 0.333, + "codec": "audio/opus" + }, + { + "participantSid": "PA_G5C4xWMJumgR", + "trackSid": "TR_VSSvARsPYMjnfH", + "kind": "video", + "bitrateKbps": 35, + "packetsLost": 4, + "jitterMs": 4, + "codec": "video/VP8" + }, + { + "participantSid": "PA_t8raa7C57aBi", + "trackSid": "TR_AsEpo39LZXJwgU", + "kind": "audio", + "bitrateKbps": 261.8, + "packetsLost": 2, + "jitterMs": 1, + "audioLevel": 0.318, + "codec": "audio/opus" + }, + { + "participantSid": "PA_t8raa7C57aBi", + "trackSid": "TR_VSVNYfqdKkzCqh", + "kind": "video", + "bitrateKbps": 35.4, + "packetsLost": 7, + "jitterMs": 4, + "codec": "video/VP8" + }, + { + "participantSid": "PA_t8raa7C57aBi", + "trackSid": "TR_AMZe28QCXUZLuv", + "kind": "audio", + "bitrateKbps": 86.9, + "packetsLost": 45, + "jitterMs": 0, + "audioLevel": 0.307, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-smoke-autosuboff-20260605T202848Z.json b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-smoke-autosuboff-20260605T202848Z.json new file mode 100644 index 0000000..24b91c7 --- /dev/null +++ b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-smoke-autosuboff-20260605T202848Z.json @@ -0,0 +1,316 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T20:28:48.938Z", + "endedAt": "2026-06-05T20:29:18.944Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-98362-5162a4d6-ca28-4b4f-b02e-bea151fc375d", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-98362", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-98362", + "subscriberIdentity": "fluxer-native-subscriber-98362", + "required": true, + "strict": true, + "timeoutMs": 60000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 10000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 30, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 80, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": true, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": true, + "secondaryPublisherScreenSimulcast": true, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 4000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-smoke-autosuboff-20260605T202848Z.json", + "verbose": false, + "scenarioName": "codec-vp8-with-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": false + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": false + }, + { + "name": "subscriber remote screenshare subscription", + "pass": false + }, + { + "name": "subscriber inbound video frames >= 6", + "pass": false + }, + { + "name": "subscriber video frame callback", + "pass": false + }, + { + "name": "publisher outbound screenshare stats", + "pass": false + }, + { + "name": "subscriber inbound video stats", + "pass": false + }, + { + "name": "subscriber inbound video stats count >= 2", + "pass": false + }, + { + "name": "subscriber video resolution 1280x720", + "pass": false + }, + { + "name": "subscriber aggregate inbound audio frames >= 12", + "pass": false + }, + { + "name": "subscriber inbound audio stats count >= 4", + "pass": false + }, + { + "name": "publisher local microphone publication", + "pass": false + }, + { + "name": "subscriber remote microphone subscription", + "pass": false + }, + { + "name": "subscriber inbound audio frames >= 12", + "pass": false + }, + { + "name": "publisher outbound microphone stats", + "pass": false + }, + { + "name": "subscriber inbound audio stats", + "pass": false + }, + { + "name": "publisher local screen-share audio publication", + "pass": false + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": false + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": false + }, + { + "name": "secondary publisher connected", + "pass": false + }, + { + "name": "secondary publisher local screenshare publication", + "pass": false + }, + { + "name": "subscriber remote secondary screenshare subscription", + "pass": false + }, + { + "name": "secondary publisher outbound screenshare stats", + "pass": false + }, + { + "name": "secondary publisher outbound screenshare codec video/VP8", + "pass": false + }, + { + "name": "subscriber inbound secondary codec video/VP8", + "pass": false + }, + { + "name": "secondary publisher local microphone publication", + "pass": false + }, + { + "name": "subscriber remote secondary microphone subscription", + "pass": false + }, + { + "name": "secondary publisher outbound microphone stats", + "pass": false + }, + { + "name": "secondary publisher local screen-share audio publication", + "pass": false + }, + { + "name": "subscriber remote secondary screen-share audio subscription", + "pass": false + }, + { + "name": "secondary publisher outbound screen-share audio stats", + "pass": false + }, + { + "name": "subscriber data packet received", + "pass": false + }, + { + "name": "subscriber secondary data packet received", + "pass": false + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": false + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": false + }, + { + "name": "server publishing API participants listed", + "pass": false + }, + { + "name": "server sees publisher screenshare publication", + "pass": false + }, + { + "name": "server sees publisher microphone publication", + "pass": false + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": false + }, + { + "name": "server sees secondary publisher screenshare publication", + "pass": false + }, + { + "name": "server sees secondary publisher microphone publication", + "pass": false + }, + { + "name": "server sees secondary publisher screen-share audio publication", + "pass": false + } + ], + "skippedReason": null, + "error": "Error: connect timed out after 30000ms\n at file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1251:9\n at async runHarness (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1668:3)\n at async main (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1920:18)\n at async file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1953:19", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 0, + "strictAudioFrames": 0, + "strictVideoFrames": 0, + "videoCallbacks": 0, + "videoBytes": 0, + "receivedFps": 0, + "requiredFps": 28.5, + "maxVideoFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 0, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 0, + "videoResolutionCounts": {}, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": null, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": null, + "error": null, + "participants": [] + }, + "lastVideoMeta": null, + "publisherStats": null, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": null, + "outbound": [], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-smoke-local-20260605T203006Z.json b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-smoke-local-20260605T203006Z.json new file mode 100644 index 0000000..9ce2636 --- /dev/null +++ b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-smoke-local-20260605T203006Z.json @@ -0,0 +1,654 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T20:30:06.419Z", + "endedAt": "2026-06-05T20:30:18.162Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:17880", + "serverApiUrl": "http://127.0.0.1:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-98467-7ef96043-b8a7-42ac-b2b9-d551434e68b6", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-98467", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-98467", + "subscriberIdentity": "fluxer-native-subscriber-98467", + "required": true, + "strict": true, + "timeoutMs": 60000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 10000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 30, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 80, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": true, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": true, + "secondaryPublisherScreenSimulcast": true, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 4000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-smoke-local-20260605T203006Z.json", + "verbose": false, + "scenarioName": "codec-vp8-with-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 6", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 2", + "pass": true + }, + { + "name": "subscriber video resolution 1280x720", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 12", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 4", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 12", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "secondary publisher connected", + "pass": true + }, + { + "name": "secondary publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote secondary screenshare subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare stats", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound secondary codec video/VP8", + "pass": true + }, + { + "name": "secondary publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote secondary microphone subscription", + "pass": true + }, + { + "name": "secondary publisher outbound microphone stats", + "pass": true + }, + { + "name": "secondary publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote secondary screen-share audio subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "subscriber secondary data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + }, + { + "name": "server sees secondary publisher screenshare publication", + "pass": true + }, + { + "name": "server sees secondary publisher microphone publication", + "pass": true + }, + { + "name": "server sees secondary publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": "observed packet loss 108 exceeds 0; observed 2 video frames outside 1280x720", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 4400, + "inboundVideoFrames": 573, + "strictAudioFrames": 99, + "strictVideoFrames": 560, + "videoCallbacks": 571, + "videoBytes": 785203200, + "receivedFps": 56.08, + "requiredFps": 28.5, + "maxVideoFrameGapMs": 103, + "maxAudioFrameGapMs": 102, + "maxVideoFrameGapOverallMs": 431, + "maxAudioFrameGapOverallMs": 114, + "avDriftMs": 55, + "maxObservedPacketLoss": 108, + "videoResolutionCounts": { + "1280x720": 558, + "640x360": 2 + }, + "videoResolutionMismatchCount": 2, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780691407114, + "stoppedAtMs": null, + "framesPushed": 332, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 36, + "lastPushAtMs": 1780691418147, + "elapsedMs": 11141, + "producedFps": 29.8 + }, + "secondaryPublisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780691407115, + "stoppedAtMs": null, + "framesPushed": 332, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 36, + "lastPushAtMs": 1780691418147, + "elapsedMs": 11140, + "producedFps": 29.8 + }, + "serverPublishing": { + "fetchedAtMs": 1780691408158, + "error": null, + "participants": [ + { + "identity": "fluxer-native-publisher-98467", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_AMcinu47L8FgLf", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_VS32SFBD3gqCZF", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + }, + { + "sid": "TR_AsHUY22SNjwgPj", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-subscriber-98467", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-2-98467", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_AMnKcxrhawpPFJ", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_VSmsDZR3M5RREb", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + }, + { + "sid": "TR_Asw3rBwsK6H7ob", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_xn87DfYqiNM5", + "trackSid": "TR_VSmsDZR3M5RREb", + "width": 1280, + "height": 720, + "timestampUs": 246264558000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_AsHUY22SNjwgPj", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 261.6, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VS32SFBD3gqCZF", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 2.2, + "packetsLost": 0, + "fps": 3, + "codec": "video/VP8" + }, + { + "trackSid": "TR_VS32SFBD3gqCZF", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 31.2, + "packetsLost": 0, + "fps": 28, + "codec": "video/VP8" + }, + { + "trackSid": "TR_AMcinu47L8FgLf", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 98.8, + "packetsLost": 0, + "codec": "audio/opus" + } + ], + "inbound": [ + { + "participantSid": "PA_xn87DfYqiNM5", + "trackSid": "TR_VSmsDZR3M5RREb", + "kind": "video", + "bitrateKbps": 2.3, + "packetsLost": 5, + "jitterMs": 13, + "codec": "video/VP8" + }, + { + "participantSid": "PA_xn87DfYqiNM5", + "trackSid": "TR_AMnKcxrhawpPFJ", + "kind": "audio", + "bitrateKbps": 98.8, + "packetsLost": 7, + "jitterMs": 0, + "audioLevel": 0.313, + "codec": "audio/opus" + }, + { + "participantSid": "PA_xn87DfYqiNM5", + "trackSid": "TR_Asw3rBwsK6H7ob", + "kind": "audio", + "bitrateKbps": 236.5, + "packetsLost": 55, + "jitterMs": 1, + "audioLevel": 0.318, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 1, + "outgoingVideoFramesProduced": 326, + "outgoingVideoFramesAccepted": 326, + "outgoingVideoFramesDropped": 14, + "outgoingVideoFramesCoalesced": 14, + "outgoingVideoFramesCaptured": 311, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 28.66, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 34, + "outgoingVideoMaxPushLatencyMs": 34, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 31, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_AMnKcxrhawpPFJ", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 98.6, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSmsDZR3M5RREb", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 33.7, + "packetsLost": 0, + "fps": 29, + "codec": "video/VP8" + }, + { + "trackSid": "TR_VSmsDZR3M5RREb", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 2.3, + "packetsLost": 0, + "fps": 3, + "codec": "video/VP8" + }, + { + "trackSid": "TR_Asw3rBwsK6H7ob", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 262.2, + "packetsLost": 0, + "codec": "audio/opus" + } + ], + "inbound": [ + { + "participantSid": "PA_fFFTp8k4DSa3", + "trackSid": "TR_AMcinu47L8FgLf", + "kind": "audio", + "bitrateKbps": 94.8, + "packetsLost": 61, + "jitterMs": 0, + "audioLevel": 0.33, + "codec": "audio/opus" + }, + { + "participantSid": "PA_fFFTp8k4DSa3", + "trackSid": "TR_AsHUY22SNjwgPj", + "kind": "audio", + "bitrateKbps": 252.1, + "packetsLost": 44, + "jitterMs": 0, + "audioLevel": 0.332, + "codec": "audio/opus" + }, + { + "participantSid": "PA_fFFTp8k4DSa3", + "trackSid": "TR_VS32SFBD3gqCZF", + "kind": "video", + "bitrateKbps": 1.2, + "packetsLost": 3, + "jitterMs": 13, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 319, + "outgoingVideoFramesAccepted": 319, + "outgoingVideoFramesDropped": 14, + "outgoingVideoFramesCoalesced": 14, + "outgoingVideoFramesCaptured": 305, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 28.67, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 34, + "outgoingVideoMaxPushLatencyMs": 34, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 31, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_xn87DfYqiNM5", + "trackSid": "TR_AMnKcxrhawpPFJ", + "kind": "audio", + "bitrateKbps": 93, + "packetsLost": 42, + "jitterMs": 0, + "audioLevel": 0.334, + "codec": "audio/opus" + }, + { + "participantSid": "PA_xn87DfYqiNM5", + "trackSid": "TR_VSmsDZR3M5RREb", + "kind": "video", + "bitrateKbps": 30.7, + "packetsLost": 13, + "jitterMs": 3, + "codec": "video/VP8" + }, + { + "participantSid": "PA_xn87DfYqiNM5", + "trackSid": "TR_Asw3rBwsK6H7ob", + "kind": "audio", + "bitrateKbps": 261.9, + "packetsLost": 9, + "jitterMs": 1, + "audioLevel": 0.318, + "codec": "audio/opus" + }, + { + "participantSid": "PA_fFFTp8k4DSa3", + "trackSid": "TR_AMcinu47L8FgLf", + "kind": "audio", + "bitrateKbps": 94.8, + "packetsLost": 13, + "jitterMs": 0, + "audioLevel": 0.315, + "codec": "audio/opus" + }, + { + "participantSid": "PA_fFFTp8k4DSa3", + "trackSid": "TR_VS32SFBD3gqCZF", + "kind": "video", + "bitrateKbps": 31.2, + "packetsLost": 14, + "jitterMs": 3, + "codec": "video/VP8" + }, + { + "participantSid": "PA_fFFTp8k4DSa3", + "trackSid": "TR_AsHUY22SNjwgPj", + "kind": "audio", + "bitrateKbps": 261.5, + "packetsLost": 3, + "jitterMs": 0, + "audioLevel": 0.336, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-stable-nosimulcast-20260605T203042Z.json b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-stable-nosimulcast-20260605T203042Z.json new file mode 100644 index 0000000..bc4fd69 --- /dev/null +++ b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-stable-nosimulcast-20260605T203042Z.json @@ -0,0 +1,635 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T20:30:42.517Z", + "endedAt": "2026-06-05T20:30:54.199Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:17880", + "serverApiUrl": "http://127.0.0.1:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-98493-8a9f539a-e21e-481c-99b7-cd5ce35f5b34", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-98493", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-98493", + "subscriberIdentity": "fluxer-native-subscriber-98493", + "required": true, + "strict": true, + "timeoutMs": 60000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 10000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 30, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 80, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": true, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 4000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": false, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-stable-nosimulcast-20260605T203042Z.json", + "verbose": false, + "scenarioName": "codec-vp8-with-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 6", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 2", + "pass": true + }, + { + "name": "subscriber video resolution 1280x720", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 12", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 4", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 12", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "secondary publisher connected", + "pass": true + }, + { + "name": "secondary publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote secondary screenshare subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare stats", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound secondary codec video/VP8", + "pass": true + }, + { + "name": "secondary publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote secondary microphone subscription", + "pass": true + }, + { + "name": "secondary publisher outbound microphone stats", + "pass": true + }, + { + "name": "secondary publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote secondary screen-share audio subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "subscriber secondary data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + }, + { + "name": "server sees secondary publisher screenshare publication", + "pass": true + }, + { + "name": "server sees secondary publisher microphone publication", + "pass": true + }, + { + "name": "server sees secondary publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": "observed packet loss 240 exceeds 0", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 4334, + "inboundVideoFrames": 602, + "strictAudioFrames": 100, + "strictVideoFrames": 571, + "videoCallbacks": 600, + "videoBytes": 829440000, + "receivedFps": 57.45, + "requiredFps": 28.5, + "maxVideoFrameGapMs": 183, + "maxAudioFrameGapMs": 102, + "maxVideoFrameGapOverallMs": 208, + "maxAudioFrameGapOverallMs": 104, + "avDriftMs": 42, + "maxObservedPacketLoss": 240, + "videoResolutionCounts": { + "1280x720": 571 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780691443335, + "stoppedAtMs": null, + "framesPushed": 326, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 37, + "lastPushAtMs": 1780691454168, + "elapsedMs": 10954, + "producedFps": 29.76 + }, + "secondaryPublisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780691443335, + "stoppedAtMs": null, + "framesPushed": 326, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 36, + "lastPushAtMs": 1780691454168, + "elapsedMs": 10954, + "producedFps": 29.76 + }, + "serverPublishing": { + "fetchedAtMs": 1780691443938, + "error": null, + "participants": [ + { + "identity": "fluxer-native-publisher-2-98493", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_VS6eBSydG2Fe6Y", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + }, + { + "sid": "TR_AMwSsNzhnQnZbN", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_AsYPbyKEiQYdXA", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-publisher-98493", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_VSsm5jzFutA9po", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + }, + { + "sid": "TR_AMd2rFUNTJzRRx", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_AsJfw9r7g4LU9b", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-subscriber-98493", + "trackCount": 0, + "tracks": [] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_RjVrb6cfSTYN", + "trackSid": "TR_VS6eBSydG2Fe6Y", + "width": 1280, + "height": 720, + "timestampUs": 246300548000 + }, + "publisherStats": { + "rttMs": null, + "outbound": [ + { + "trackSid": "TR_VSsm5jzFutA9po", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 28.8, + "packetsLost": 0, + "fps": 29, + "codec": "video/VP8" + }, + { + "trackSid": "TR_AsJfw9r7g4LU9b", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 258, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_AMd2rFUNTJzRRx", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 98.3, + "packetsLost": 0, + "codec": "audio/opus" + } + ], + "inbound": [ + { + "participantSid": "PA_RjVrb6cfSTYN", + "trackSid": "TR_AsYPbyKEiQYdXA", + "kind": "audio", + "bitrateKbps": 257.7, + "packetsLost": 2, + "jitterMs": 0, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_RjVrb6cfSTYN", + "trackSid": "TR_VS6eBSydG2Fe6Y", + "kind": "video", + "bitrateKbps": 27.7, + "packetsLost": 8, + "jitterMs": 2, + "codec": "video/VP8" + }, + { + "participantSid": "PA_RjVrb6cfSTYN", + "trackSid": "TR_AMwSsNzhnQnZbN", + "kind": "audio", + "bitrateKbps": 98.1, + "packetsLost": 9, + "jitterMs": 0, + "audioLevel": 0.337, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 1, + "outgoingVideoFramesProduced": 319, + "outgoingVideoFramesAccepted": 319, + "outgoingVideoFramesDropped": 14, + "outgoingVideoFramesCoalesced": 14, + "outgoingVideoFramesCaptured": 304, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 28.61, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 35, + "outgoingVideoMaxPushLatencyMs": 35, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 30, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": { + "rttMs": null, + "outbound": [ + { + "trackSid": "TR_AsYPbyKEiQYdXA", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 262.5, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_AMwSsNzhnQnZbN", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 98, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VS6eBSydG2Fe6Y", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 26.6, + "packetsLost": 0, + "fps": 28, + "codec": "video/VP8" + } + ], + "inbound": [ + { + "participantSid": "PA_SDXDDK8nFR6H", + "trackSid": "TR_AMd2rFUNTJzRRx", + "kind": "audio", + "bitrateKbps": 98.3, + "packetsLost": 10, + "jitterMs": 1, + "audioLevel": 0.333, + "codec": "audio/opus" + }, + { + "participantSid": "PA_SDXDDK8nFR6H", + "trackSid": "TR_AsJfw9r7g4LU9b", + "kind": "audio", + "bitrateKbps": 267.9, + "packetsLost": 3, + "jitterMs": 0, + "audioLevel": 0.336, + "codec": "audio/opus" + }, + { + "participantSid": "PA_SDXDDK8nFR6H", + "trackSid": "TR_VSsm5jzFutA9po", + "kind": "video", + "bitrateKbps": 27.8, + "packetsLost": 7, + "jitterMs": 3, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 1, + "outgoingVideoFramesProduced": 317, + "outgoingVideoFramesAccepted": 317, + "outgoingVideoFramesDropped": 14, + "outgoingVideoFramesCoalesced": 14, + "outgoingVideoFramesCaptured": 302, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 28.61, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 34, + "outgoingVideoMaxPushLatencyMs": 34, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 30, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "subscriberStats": { + "rttMs": null, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_RjVrb6cfSTYN", + "trackSid": "TR_AMwSsNzhnQnZbN", + "kind": "audio", + "bitrateKbps": 85.4, + "packetsLost": 58, + "jitterMs": 0, + "audioLevel": 0.316, + "codec": "audio/opus" + }, + { + "participantSid": "PA_RjVrb6cfSTYN", + "trackSid": "TR_AsYPbyKEiQYdXA", + "kind": "audio", + "bitrateKbps": 242.8, + "packetsLost": 46, + "jitterMs": 0, + "audioLevel": 0.383, + "codec": "audio/opus" + }, + { + "participantSid": "PA_RjVrb6cfSTYN", + "trackSid": "TR_VS6eBSydG2Fe6Y", + "kind": "video", + "bitrateKbps": 26.9, + "packetsLost": 26, + "jitterMs": 2, + "codec": "video/VP8" + }, + { + "participantSid": "PA_SDXDDK8nFR6H", + "trackSid": "TR_AMd2rFUNTJzRRx", + "kind": "audio", + "bitrateKbps": 94.9, + "packetsLost": 52, + "jitterMs": 0, + "audioLevel": 0.316, + "codec": "audio/opus" + }, + { + "participantSid": "PA_SDXDDK8nFR6H", + "trackSid": "TR_AsJfw9r7g4LU9b", + "kind": "audio", + "bitrateKbps": 258.3, + "packetsLost": 42, + "jitterMs": 0, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_SDXDDK8nFR6H", + "trackSid": "TR_VSsm5jzFutA9po", + "kind": "video", + "bitrateKbps": 28, + "packetsLost": 16, + "jitterMs": 2, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-strict-4k60-20260605T201811Z.json b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-strict-4k60-20260605T201811Z.json new file mode 100644 index 0000000..be3da22 --- /dev/null +++ b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-strict-4k60-20260605T201811Z.json @@ -0,0 +1,1405 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T20:18:32.176Z", + "endedAt": "2026-06-05T20:21:02.334Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:7880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-97026-aed6d5c1-230e-4223-ab28-36ab6db848ee", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-97026", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-97026", + "subscriberIdentity": "fluxer-native-subscriber-97026", + "required": true, + "strict": true, + "timeoutMs": 180000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 120000, + "width": 3840, + "height": 2160, + "expectedWidth": 3840, + "expectedHeight": 2160, + "fps": 60, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 80, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["vp8", "vp9", "h264", "hevc", "av1"], + "expectedScreenCodecs": ["vp8", "vp9", "h264", "hevc", "av1"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": true, + "secondaryPublisherCodec": "vp9", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP9", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": true, + "secondaryPublisherScreenSimulcast": true, + "adaptiveSend": true, + "minVideoFps": 24, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 80000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "camera": true, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-strict-4k60-20260605T201811Z.json", + "verbose": false, + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "scenarioCount": 5, + "scenarios": [ + { + "status": "fail", + "startedAt": "2026-06-05T20:18:32.181Z", + "endedAt": "2026-06-05T20:19:02.186Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:7880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-97026-aed6d5c1-230e-4223-ab28-36ab6db848ee-codec-vp8-with-vp9", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-97026", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-97026", + "subscriberIdentity": "fluxer-native-subscriber-97026", + "required": true, + "strict": true, + "timeoutMs": 180000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 120000, + "width": 3840, + "height": 2160, + "expectedWidth": 3840, + "expectedHeight": 2160, + "fps": 60, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 80, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["vp8", "vp9", "h264", "hevc", "av1"], + "expectedScreenCodecs": ["vp8", "vp9", "h264", "hevc", "av1"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": true, + "secondaryPublisherCodec": "vp9", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP9", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": true, + "secondaryPublisherScreenSimulcast": true, + "adaptiveSend": true, + "minVideoFps": 24, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 80000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "camera": true, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": null, + "verbose": false, + "scenarioName": "codec-vp8-with-vp9", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": false + }, + { + "name": "subscriber connected", + "pass": false + }, + { + "name": "publisher local screenshare publication", + "pass": false + }, + { + "name": "subscriber remote screenshare subscription", + "pass": false + }, + { + "name": "subscriber inbound video frames >= 9", + "pass": false + }, + { + "name": "subscriber video frame callback", + "pass": false + }, + { + "name": "publisher outbound screenshare stats", + "pass": false + }, + { + "name": "subscriber inbound video stats", + "pass": false + }, + { + "name": "subscriber inbound video stats count >= 3", + "pass": false + }, + { + "name": "subscriber video resolution 3840x2160", + "pass": false + }, + { + "name": "subscriber aggregate inbound audio frames >= 12", + "pass": false + }, + { + "name": "subscriber inbound audio stats count >= 4", + "pass": false + }, + { + "name": "publisher local microphone publication", + "pass": false + }, + { + "name": "subscriber remote microphone subscription", + "pass": false + }, + { + "name": "subscriber inbound audio frames >= 12", + "pass": false + }, + { + "name": "publisher outbound microphone stats", + "pass": false + }, + { + "name": "subscriber inbound audio stats", + "pass": false + }, + { + "name": "publisher local screen-share audio publication", + "pass": false + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": false + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": false + }, + { + "name": "publisher local camera publication", + "pass": false + }, + { + "name": "subscriber remote camera subscription", + "pass": false + }, + { + "name": "publisher outbound camera stats", + "pass": false + }, + { + "name": "secondary publisher connected", + "pass": false + }, + { + "name": "secondary publisher local screenshare publication", + "pass": false + }, + { + "name": "subscriber remote secondary screenshare subscription", + "pass": false + }, + { + "name": "secondary publisher outbound screenshare stats", + "pass": false + }, + { + "name": "secondary publisher outbound screenshare codec video/VP9", + "pass": false + }, + { + "name": "subscriber inbound secondary codec video/VP9", + "pass": false + }, + { + "name": "secondary publisher local microphone publication", + "pass": false + }, + { + "name": "subscriber remote secondary microphone subscription", + "pass": false + }, + { + "name": "secondary publisher outbound microphone stats", + "pass": false + }, + { + "name": "secondary publisher local screen-share audio publication", + "pass": false + }, + { + "name": "subscriber remote secondary screen-share audio subscription", + "pass": false + }, + { + "name": "secondary publisher outbound screen-share audio stats", + "pass": false + }, + { + "name": "subscriber data packet received", + "pass": false + }, + { + "name": "subscriber secondary data packet received", + "pass": false + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": false + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": false + } + ], + "skippedReason": null, + "error": "Error: connect timed out after 30000ms\n at file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1086:9\n at async runHarness (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1394:3)\n at async runScenarioSuite (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1610:19)\n at async main (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1639:22)\n at async file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1673:19", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 0, + "videoCallbacks": 0, + "videoBytes": 0, + "receivedFps": 0, + "requiredFps": 57, + "maxVideoFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 0, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 0, + "videoResolutionCounts": {}, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": null, + "secondaryPublisherVideoPump": null, + "lastVideoMeta": null, + "publisherStats": null, + "secondaryPublisherStats": null, + "subscriberStats": null + } + }, + { + "status": "fail", + "startedAt": "2026-06-05T20:19:02.297Z", + "endedAt": "2026-06-05T20:19:32.299Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:7880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-97026-aed6d5c1-230e-4223-ab28-36ab6db848ee-codec-vp9-with-h264", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-97026", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-97026", + "subscriberIdentity": "fluxer-native-subscriber-97026", + "required": true, + "strict": true, + "timeoutMs": 180000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 120000, + "width": 3840, + "height": 2160, + "expectedWidth": 3840, + "expectedHeight": 2160, + "fps": 60, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 80, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["vp8", "vp9", "h264", "hevc", "av1"], + "expectedScreenCodecs": ["vp8", "vp9", "h264", "hevc", "av1"], + "codec": "vp9", + "expectedScreenCodec": "video/VP9", + "secondaryPublisher": true, + "secondaryPublisherCodec": "h264", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/H264", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": true, + "secondaryPublisherScreenSimulcast": true, + "adaptiveSend": true, + "minVideoFps": 24, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 80000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "camera": true, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": null, + "verbose": false, + "scenarioName": "codec-vp9-with-h264", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": false + }, + { + "name": "subscriber connected", + "pass": false + }, + { + "name": "publisher local screenshare publication", + "pass": false + }, + { + "name": "subscriber remote screenshare subscription", + "pass": false + }, + { + "name": "subscriber inbound video frames >= 9", + "pass": false + }, + { + "name": "subscriber video frame callback", + "pass": false + }, + { + "name": "publisher outbound screenshare stats", + "pass": false + }, + { + "name": "subscriber inbound video stats", + "pass": false + }, + { + "name": "subscriber inbound video stats count >= 3", + "pass": false + }, + { + "name": "subscriber video resolution 3840x2160", + "pass": false + }, + { + "name": "subscriber aggregate inbound audio frames >= 12", + "pass": false + }, + { + "name": "subscriber inbound audio stats count >= 4", + "pass": false + }, + { + "name": "publisher local microphone publication", + "pass": false + }, + { + "name": "subscriber remote microphone subscription", + "pass": false + }, + { + "name": "subscriber inbound audio frames >= 12", + "pass": false + }, + { + "name": "publisher outbound microphone stats", + "pass": false + }, + { + "name": "subscriber inbound audio stats", + "pass": false + }, + { + "name": "publisher local screen-share audio publication", + "pass": false + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": false + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": false + }, + { + "name": "publisher local camera publication", + "pass": false + }, + { + "name": "subscriber remote camera subscription", + "pass": false + }, + { + "name": "publisher outbound camera stats", + "pass": false + }, + { + "name": "secondary publisher connected", + "pass": false + }, + { + "name": "secondary publisher local screenshare publication", + "pass": false + }, + { + "name": "subscriber remote secondary screenshare subscription", + "pass": false + }, + { + "name": "secondary publisher outbound screenshare stats", + "pass": false + }, + { + "name": "secondary publisher outbound screenshare codec video/H264", + "pass": false + }, + { + "name": "subscriber inbound secondary codec video/H264", + "pass": false + }, + { + "name": "secondary publisher local microphone publication", + "pass": false + }, + { + "name": "subscriber remote secondary microphone subscription", + "pass": false + }, + { + "name": "secondary publisher outbound microphone stats", + "pass": false + }, + { + "name": "secondary publisher local screen-share audio publication", + "pass": false + }, + { + "name": "subscriber remote secondary screen-share audio subscription", + "pass": false + }, + { + "name": "secondary publisher outbound screen-share audio stats", + "pass": false + }, + { + "name": "subscriber data packet received", + "pass": false + }, + { + "name": "subscriber secondary data packet received", + "pass": false + }, + { + "name": "publisher outbound screenshare codec video/VP9", + "pass": false + }, + { + "name": "subscriber inbound video codec video/VP9", + "pass": false + } + ], + "skippedReason": null, + "error": "Error: connect timed out after 30000ms\n at file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1086:9\n at async runHarness (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1394:3)\n at async runScenarioSuite (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1610:19)\n at async main (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1639:22)\n at async file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1673:19", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 0, + "videoCallbacks": 0, + "videoBytes": 0, + "receivedFps": 0, + "requiredFps": 57, + "maxVideoFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 0, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 0, + "videoResolutionCounts": {}, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": null, + "secondaryPublisherVideoPump": null, + "lastVideoMeta": null, + "publisherStats": null, + "secondaryPublisherStats": null, + "subscriberStats": null + } + }, + { + "status": "fail", + "startedAt": "2026-06-05T20:19:32.307Z", + "endedAt": "2026-06-05T20:20:02.308Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:7880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-97026-aed6d5c1-230e-4223-ab28-36ab6db848ee-codec-h264-with-hevc", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-97026", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-97026", + "subscriberIdentity": "fluxer-native-subscriber-97026", + "required": true, + "strict": true, + "timeoutMs": 180000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 120000, + "width": 3840, + "height": 2160, + "expectedWidth": 3840, + "expectedHeight": 2160, + "fps": 60, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 80, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["vp8", "vp9", "h264", "hevc", "av1"], + "expectedScreenCodecs": ["vp8", "vp9", "h264", "hevc", "av1"], + "codec": "h264", + "expectedScreenCodec": "video/H264", + "secondaryPublisher": true, + "secondaryPublisherCodec": "hevc", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/H265", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": true, + "secondaryPublisherScreenSimulcast": true, + "adaptiveSend": true, + "minVideoFps": 24, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 80000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "camera": true, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": null, + "verbose": false, + "scenarioName": "codec-h264-with-hevc", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": false + }, + { + "name": "subscriber connected", + "pass": false + }, + { + "name": "publisher local screenshare publication", + "pass": false + }, + { + "name": "subscriber remote screenshare subscription", + "pass": false + }, + { + "name": "subscriber inbound video frames >= 9", + "pass": false + }, + { + "name": "subscriber video frame callback", + "pass": false + }, + { + "name": "publisher outbound screenshare stats", + "pass": false + }, + { + "name": "subscriber inbound video stats", + "pass": false + }, + { + "name": "subscriber inbound video stats count >= 3", + "pass": false + }, + { + "name": "subscriber video resolution 3840x2160", + "pass": false + }, + { + "name": "subscriber aggregate inbound audio frames >= 12", + "pass": false + }, + { + "name": "subscriber inbound audio stats count >= 4", + "pass": false + }, + { + "name": "publisher local microphone publication", + "pass": false + }, + { + "name": "subscriber remote microphone subscription", + "pass": false + }, + { + "name": "subscriber inbound audio frames >= 12", + "pass": false + }, + { + "name": "publisher outbound microphone stats", + "pass": false + }, + { + "name": "subscriber inbound audio stats", + "pass": false + }, + { + "name": "publisher local screen-share audio publication", + "pass": false + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": false + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": false + }, + { + "name": "publisher local camera publication", + "pass": false + }, + { + "name": "subscriber remote camera subscription", + "pass": false + }, + { + "name": "publisher outbound camera stats", + "pass": false + }, + { + "name": "secondary publisher connected", + "pass": false + }, + { + "name": "secondary publisher local screenshare publication", + "pass": false + }, + { + "name": "subscriber remote secondary screenshare subscription", + "pass": false + }, + { + "name": "secondary publisher outbound screenshare stats", + "pass": false + }, + { + "name": "secondary publisher outbound screenshare codec video/H265", + "pass": false + }, + { + "name": "subscriber inbound secondary codec video/H265", + "pass": false + }, + { + "name": "secondary publisher local microphone publication", + "pass": false + }, + { + "name": "subscriber remote secondary microphone subscription", + "pass": false + }, + { + "name": "secondary publisher outbound microphone stats", + "pass": false + }, + { + "name": "secondary publisher local screen-share audio publication", + "pass": false + }, + { + "name": "subscriber remote secondary screen-share audio subscription", + "pass": false + }, + { + "name": "secondary publisher outbound screen-share audio stats", + "pass": false + }, + { + "name": "subscriber data packet received", + "pass": false + }, + { + "name": "subscriber secondary data packet received", + "pass": false + }, + { + "name": "publisher outbound screenshare codec video/H264", + "pass": false + }, + { + "name": "subscriber inbound video codec video/H264", + "pass": false + } + ], + "skippedReason": null, + "error": "Error: connect timed out after 30000ms\n at file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1086:9\n at async runHarness (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1394:3)\n at async runScenarioSuite (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1610:19)\n at async main (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1639:22)\n at async file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1673:19", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 0, + "videoCallbacks": 0, + "videoBytes": 0, + "receivedFps": 0, + "requiredFps": 57, + "maxVideoFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 0, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 0, + "videoResolutionCounts": {}, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": null, + "secondaryPublisherVideoPump": null, + "lastVideoMeta": null, + "publisherStats": null, + "secondaryPublisherStats": null, + "subscriberStats": null + } + }, + { + "status": "fail", + "startedAt": "2026-06-05T20:20:02.316Z", + "endedAt": "2026-06-05T20:20:32.317Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:7880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-97026-aed6d5c1-230e-4223-ab28-36ab6db848ee-codec-hevc-with-av1", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-97026", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-97026", + "subscriberIdentity": "fluxer-native-subscriber-97026", + "required": true, + "strict": true, + "timeoutMs": 180000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 120000, + "width": 3840, + "height": 2160, + "expectedWidth": 3840, + "expectedHeight": 2160, + "fps": 60, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 80, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["vp8", "vp9", "h264", "hevc", "av1"], + "expectedScreenCodecs": ["vp8", "vp9", "h264", "hevc", "av1"], + "codec": "hevc", + "expectedScreenCodec": "video/H265", + "secondaryPublisher": true, + "secondaryPublisherCodec": "av1", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/AV1", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": true, + "secondaryPublisherScreenSimulcast": true, + "adaptiveSend": true, + "minVideoFps": 24, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 80000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "camera": true, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": null, + "verbose": false, + "scenarioName": "codec-hevc-with-av1", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": false + }, + { + "name": "subscriber connected", + "pass": false + }, + { + "name": "publisher local screenshare publication", + "pass": false + }, + { + "name": "subscriber remote screenshare subscription", + "pass": false + }, + { + "name": "subscriber inbound video frames >= 9", + "pass": false + }, + { + "name": "subscriber video frame callback", + "pass": false + }, + { + "name": "publisher outbound screenshare stats", + "pass": false + }, + { + "name": "subscriber inbound video stats", + "pass": false + }, + { + "name": "subscriber inbound video stats count >= 3", + "pass": false + }, + { + "name": "subscriber video resolution 3840x2160", + "pass": false + }, + { + "name": "subscriber aggregate inbound audio frames >= 12", + "pass": false + }, + { + "name": "subscriber inbound audio stats count >= 4", + "pass": false + }, + { + "name": "publisher local microphone publication", + "pass": false + }, + { + "name": "subscriber remote microphone subscription", + "pass": false + }, + { + "name": "subscriber inbound audio frames >= 12", + "pass": false + }, + { + "name": "publisher outbound microphone stats", + "pass": false + }, + { + "name": "subscriber inbound audio stats", + "pass": false + }, + { + "name": "publisher local screen-share audio publication", + "pass": false + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": false + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": false + }, + { + "name": "publisher local camera publication", + "pass": false + }, + { + "name": "subscriber remote camera subscription", + "pass": false + }, + { + "name": "publisher outbound camera stats", + "pass": false + }, + { + "name": "secondary publisher connected", + "pass": false + }, + { + "name": "secondary publisher local screenshare publication", + "pass": false + }, + { + "name": "subscriber remote secondary screenshare subscription", + "pass": false + }, + { + "name": "secondary publisher outbound screenshare stats", + "pass": false + }, + { + "name": "secondary publisher outbound screenshare codec video/AV1", + "pass": false + }, + { + "name": "subscriber inbound secondary codec video/AV1", + "pass": false + }, + { + "name": "secondary publisher local microphone publication", + "pass": false + }, + { + "name": "subscriber remote secondary microphone subscription", + "pass": false + }, + { + "name": "secondary publisher outbound microphone stats", + "pass": false + }, + { + "name": "secondary publisher local screen-share audio publication", + "pass": false + }, + { + "name": "subscriber remote secondary screen-share audio subscription", + "pass": false + }, + { + "name": "secondary publisher outbound screen-share audio stats", + "pass": false + }, + { + "name": "subscriber data packet received", + "pass": false + }, + { + "name": "subscriber secondary data packet received", + "pass": false + }, + { + "name": "publisher outbound screenshare codec video/H265", + "pass": false + }, + { + "name": "subscriber inbound video codec video/H265", + "pass": false + } + ], + "skippedReason": null, + "error": "Error: connect timed out after 30000ms\n at file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1086:9\n at async runHarness (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1394:3)\n at async runScenarioSuite (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1610:19)\n at async main (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1639:22)\n at async file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1673:19", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 0, + "videoCallbacks": 0, + "videoBytes": 0, + "receivedFps": 0, + "requiredFps": 57, + "maxVideoFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 0, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 0, + "videoResolutionCounts": {}, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": null, + "secondaryPublisherVideoPump": null, + "lastVideoMeta": null, + "publisherStats": null, + "secondaryPublisherStats": null, + "subscriberStats": null + } + }, + { + "status": "fail", + "startedAt": "2026-06-05T20:20:32.324Z", + "endedAt": "2026-06-05T20:21:02.326Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:7880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-97026-aed6d5c1-230e-4223-ab28-36ab6db848ee-codec-av1-with-vp8", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-97026", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-97026", + "subscriberIdentity": "fluxer-native-subscriber-97026", + "required": true, + "strict": true, + "timeoutMs": 180000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 120000, + "width": 3840, + "height": 2160, + "expectedWidth": 3840, + "expectedHeight": 2160, + "fps": 60, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 80, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["vp8", "vp9", "h264", "hevc", "av1"], + "expectedScreenCodecs": ["vp8", "vp9", "h264", "hevc", "av1"], + "codec": "av1", + "expectedScreenCodec": "video/AV1", + "secondaryPublisher": true, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": true, + "secondaryPublisherScreenSimulcast": true, + "adaptiveSend": true, + "minVideoFps": 24, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 80000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "camera": true, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": null, + "verbose": false, + "scenarioName": "codec-av1-with-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": false + }, + { + "name": "subscriber connected", + "pass": false + }, + { + "name": "publisher local screenshare publication", + "pass": false + }, + { + "name": "subscriber remote screenshare subscription", + "pass": false + }, + { + "name": "subscriber inbound video frames >= 9", + "pass": false + }, + { + "name": "subscriber video frame callback", + "pass": false + }, + { + "name": "publisher outbound screenshare stats", + "pass": false + }, + { + "name": "subscriber inbound video stats", + "pass": false + }, + { + "name": "subscriber inbound video stats count >= 3", + "pass": false + }, + { + "name": "subscriber video resolution 3840x2160", + "pass": false + }, + { + "name": "subscriber aggregate inbound audio frames >= 12", + "pass": false + }, + { + "name": "subscriber inbound audio stats count >= 4", + "pass": false + }, + { + "name": "publisher local microphone publication", + "pass": false + }, + { + "name": "subscriber remote microphone subscription", + "pass": false + }, + { + "name": "subscriber inbound audio frames >= 12", + "pass": false + }, + { + "name": "publisher outbound microphone stats", + "pass": false + }, + { + "name": "subscriber inbound audio stats", + "pass": false + }, + { + "name": "publisher local screen-share audio publication", + "pass": false + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": false + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": false + }, + { + "name": "publisher local camera publication", + "pass": false + }, + { + "name": "subscriber remote camera subscription", + "pass": false + }, + { + "name": "publisher outbound camera stats", + "pass": false + }, + { + "name": "secondary publisher connected", + "pass": false + }, + { + "name": "secondary publisher local screenshare publication", + "pass": false + }, + { + "name": "subscriber remote secondary screenshare subscription", + "pass": false + }, + { + "name": "secondary publisher outbound screenshare stats", + "pass": false + }, + { + "name": "secondary publisher outbound screenshare codec video/VP8", + "pass": false + }, + { + "name": "subscriber inbound secondary codec video/VP8", + "pass": false + }, + { + "name": "secondary publisher local microphone publication", + "pass": false + }, + { + "name": "subscriber remote secondary microphone subscription", + "pass": false + }, + { + "name": "secondary publisher outbound microphone stats", + "pass": false + }, + { + "name": "secondary publisher local screen-share audio publication", + "pass": false + }, + { + "name": "subscriber remote secondary screen-share audio subscription", + "pass": false + }, + { + "name": "secondary publisher outbound screen-share audio stats", + "pass": false + }, + { + "name": "subscriber data packet received", + "pass": false + }, + { + "name": "subscriber secondary data packet received", + "pass": false + }, + { + "name": "publisher outbound screenshare codec video/AV1", + "pass": false + }, + { + "name": "subscriber inbound video codec video/AV1", + "pass": false + } + ], + "skippedReason": null, + "error": "Error: connect timed out after 30000ms\n at file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1086:9\n at async runHarness (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1394:3)\n at async runScenarioSuite (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1610:19)\n at async main (file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1639:22)\n at async file:///Users/hampus/Development/fluxer/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs:1673:19", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 0, + "videoCallbacks": 0, + "videoBytes": 0, + "receivedFps": 0, + "requiredFps": 57, + "maxVideoFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 0, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 0, + "videoResolutionCounts": {}, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": null, + "secondaryPublisherVideoPump": null, + "lastVideoMeta": null, + "publisherStats": null, + "secondaryPublisherStats": null, + "subscriberStats": null + } + } + ] +} diff --git a/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-20260605T203103Z.json b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-20260605T203103Z.json new file mode 100644 index 0000000..492c1cd --- /dev/null +++ b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-20260605T203103Z.json @@ -0,0 +1,285 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T20:31:04.019Z", + "endedAt": "2026-06-05T20:31:15.493Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:17880", + "serverApiUrl": "http://127.0.0.1:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-98534-07955759-bfdc-4fc0-8941-6c23669306cb", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-98534", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-98534", + "subscriberIdentity": "fluxer-native-subscriber-98534", + "required": true, + "strict": true, + "timeoutMs": 60000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 10000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 30, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": false, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": false, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 4000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": false, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-20260605T203103Z.json", + "verbose": false, + "scenarioName": "codec-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 1280x720", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + } + ], + "skippedReason": null, + "error": "received FPS 19.96 below 28.5; observed packet loss 1 exceeds 0", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 227, + "strictAudioFrames": 0, + "strictVideoFrames": 200, + "videoCallbacks": 226, + "videoBytes": 312422400, + "receivedFps": 19.96, + "requiredFps": 28.5, + "maxVideoFrameGapMs": 98, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 107, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 1, + "videoResolutionCounts": { + "1280x720": 200 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780691464440, + "stoppedAtMs": null, + "framesPushed": 332, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 36, + "lastPushAtMs": 1780691475476, + "elapsedMs": 11142, + "producedFps": 29.8 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780691465483, + "error": null, + "participants": [ + { + "identity": "fluxer-native-publisher-98534", + "trackCount": 1, + "tracks": [ + { + "sid": "TR_VSCdbHfS7Qjnp9", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-subscriber-98534", + "trackCount": 0, + "tracks": [] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_NeWDVeMforAq", + "trackSid": "TR_VSCdbHfS7Qjnp9", + "width": 1280, + "height": 720, + "timestampUs": 246321877000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_VSCdbHfS7Qjnp9", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 28.5, + "packetsLost": 0, + "fps": 14, + "codec": "video/VP8" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 1, + "outgoingVideoFramesProduced": 330, + "outgoingVideoFramesAccepted": 330, + "outgoingVideoFramesDropped": 100, + "outgoingVideoFramesCoalesced": 100, + "outgoingVideoFramesCaptured": 229, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 20.83, + "outgoingVideoTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 34, + "outgoingVideoMaxPushLatencyMs": 34, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "fps15", + "adaptiveSendReason": "frameCoalescingPressure" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_NeWDVeMforAq", + "trackSid": "TR_VSCdbHfS7Qjnp9", + "kind": "video", + "bitrateKbps": 30.5, + "packetsLost": 1, + "jitterMs": 4, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-adaptive-fixed-20260605T203523Z.json b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-adaptive-fixed-20260605T203523Z.json new file mode 100644 index 0000000..18f3793 --- /dev/null +++ b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-adaptive-fixed-20260605T203523Z.json @@ -0,0 +1,285 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T20:35:23.806Z", + "endedAt": "2026-06-05T20:35:35.256Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:17880", + "serverApiUrl": "http://127.0.0.1:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-99020-a8faf4d3-b9ea-4425-b85e-484c3439bea8", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-99020", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-99020", + "subscriberIdentity": "fluxer-native-subscriber-99020", + "required": true, + "strict": true, + "timeoutMs": 60000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 10000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 30, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": false, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": false, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 4000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": false, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-adaptive-fixed-20260605T203523Z.json", + "verbose": false, + "scenarioName": "codec-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 1280x720", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + } + ], + "skippedReason": null, + "error": "observed packet loss 4 exceeds 0", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 314, + "strictAudioFrames": 0, + "strictVideoFrames": 287, + "videoCallbacks": 314, + "videoBytes": 434073600, + "receivedFps": 28.89, + "requiredFps": 28.5, + "maxVideoFrameGapMs": 65, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 174, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 4, + "videoResolutionCounts": { + "1280x720": 287 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780691724194, + "stoppedAtMs": null, + "framesPushed": 332, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 38, + "lastPushAtMs": 1780691735226, + "elapsedMs": 11160, + "producedFps": 29.75 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780691725246, + "error": null, + "participants": [ + { + "identity": "fluxer-native-publisher-99020", + "trackCount": 1, + "tracks": [ + { + "sid": "TR_VSNY7hqQXsYjDU", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-subscriber-99020", + "trackCount": 0, + "tracks": [] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_4XVg8hrTERta", + "trackSid": "TR_VSNY7hqQXsYjDU", + "width": 1280, + "height": 720, + "timestampUs": 246581659000 + }, + "publisherStats": { + "rttMs": null, + "outbound": [ + { + "trackSid": "TR_VSNY7hqQXsYjDU", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 29.1, + "packetsLost": 0, + "fps": 28, + "codec": "video/VP8" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 330, + "outgoingVideoFramesAccepted": 330, + "outgoingVideoFramesDropped": 16, + "outgoingVideoFramesCoalesced": 16, + "outgoingVideoFramesCaptured": 314, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 28.49, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 34, + "outgoingVideoMaxPushLatencyMs": 34, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_4XVg8hrTERta", + "trackSid": "TR_VSNY7hqQXsYjDU", + "kind": "video", + "bitrateKbps": 29.1, + "packetsLost": 4, + "jitterMs": 2, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-noadaptive-20260605T203134Z.json b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-noadaptive-20260605T203134Z.json new file mode 100644 index 0000000..c2c10f9 --- /dev/null +++ b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-noadaptive-20260605T203134Z.json @@ -0,0 +1,285 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T20:31:34.574Z", + "endedAt": "2026-06-05T20:31:45.998Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:17880", + "serverApiUrl": "http://127.0.0.1:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-98569-9a0c5b4a-c12c-4cce-97ba-5481af7273d4", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-98569", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-98569", + "subscriberIdentity": "fluxer-native-subscriber-98569", + "required": true, + "strict": true, + "timeoutMs": 60000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 10000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 30, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": false, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": false, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": false, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 4000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": false, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-noadaptive-20260605T203134Z.json", + "verbose": false, + "scenarioName": "codec-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 1280x720", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + } + ], + "skippedReason": null, + "error": "received FPS 28.45 below 28.5", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 312, + "strictAudioFrames": 0, + "strictVideoFrames": 285, + "videoCallbacks": 311, + "videoBytes": 429926400, + "receivedFps": 28.45, + "requiredFps": 28.5, + "maxVideoFrameGapMs": 61, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 69, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 0, + "videoResolutionCounts": { + "1280x720": 285 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780691494944, + "stoppedAtMs": null, + "framesPushed": 332, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 36, + "lastPushAtMs": 1780691505978, + "elapsedMs": 11144, + "producedFps": 29.79 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780691495987, + "error": null, + "participants": [ + { + "identity": "fluxer-native-subscriber-98569", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-98569", + "trackCount": 1, + "tracks": [ + { + "sid": "TR_VSnPVaEqt8wLKt", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_4H7SUk7TRbA7", + "trackSid": "TR_VSnPVaEqt8wLKt", + "width": 1280, + "height": 720, + "timestampUs": 246352378000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_VSnPVaEqt8wLKt", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 28.1, + "packetsLost": 0, + "fps": 28, + "codec": "video/VP8" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 330, + "outgoingVideoFramesAccepted": 330, + "outgoingVideoFramesDropped": 17, + "outgoingVideoFramesCoalesced": 17, + "outgoingVideoFramesCaptured": 313, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 28.44, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 34, + "outgoingVideoMaxPushLatencyMs": 34, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "full", + "adaptiveSendReason": "adaptiveDisabled" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_4H7SUk7TRbA7", + "trackSid": "TR_VSnPVaEqt8wLKt", + "kind": "video", + "bitrateKbps": 28.1, + "packetsLost": 0, + "jitterMs": 3, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-paced-20260605T203257Z.json b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-paced-20260605T203257Z.json new file mode 100644 index 0000000..5f29d01 --- /dev/null +++ b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-paced-20260605T203257Z.json @@ -0,0 +1,285 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T20:32:57.846Z", + "endedAt": "2026-06-05T20:33:09.335Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:17880", + "serverApiUrl": "http://127.0.0.1:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-98748-d3e8cf0e-bc5c-48e8-b5ef-ed0f993fbe86", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-98748", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-98748", + "subscriberIdentity": "fluxer-native-subscriber-98748", + "required": true, + "strict": true, + "timeoutMs": 60000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 10000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 30, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": false, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": false, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 4000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": false, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-paced-20260605T203257Z.json", + "verbose": false, + "scenarioName": "codec-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 1280x720", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + } + ], + "skippedReason": null, + "error": "received FPS 20.11 below 28.5; observed packet loss 2 exceeds 0", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 226, + "strictAudioFrames": 0, + "strictVideoFrames": 201, + "videoCallbacks": 225, + "videoBytes": 311040000, + "receivedFps": 20.11, + "requiredFps": 28.5, + "maxVideoFrameGapMs": 173, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 173, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 2, + "videoResolutionCounts": { + "1280x720": 201 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780691578281, + "stoppedAtMs": null, + "framesPushed": 332, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 35, + "lastPushAtMs": 1780691589316, + "elapsedMs": 11143, + "producedFps": 29.79 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780691579324, + "error": null, + "participants": [ + { + "identity": "fluxer-native-subscriber-98748", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-98748", + "trackCount": 1, + "tracks": [ + { + "sid": "TR_VS5AVMD9QiLw9T", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_wGf7jDpSGqVf", + "trackSid": "TR_VS5AVMD9QiLw9T", + "width": 1280, + "height": 720, + "timestampUs": 246435681000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_VS5AVMD9QiLw9T", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 28.5, + "packetsLost": 0, + "fps": 14, + "codec": "video/VP8" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 330, + "outgoingVideoFramesAccepted": 330, + "outgoingVideoFramesDropped": 100, + "outgoingVideoFramesCoalesced": 100, + "outgoingVideoFramesCaptured": 230, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 20.87, + "outgoingVideoTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 34, + "outgoingVideoMaxPushLatencyMs": 34, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "fps15", + "adaptiveSendReason": "frameCoalescingPressure" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_wGf7jDpSGqVf", + "trackSid": "TR_VS5AVMD9QiLw9T", + "kind": "video", + "bitrateKbps": 28.5, + "packetsLost": 2, + "jitterMs": 2, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-strict-delta-20260605T203710Z.json b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-strict-delta-20260605T203710Z.json new file mode 100644 index 0000000..9554a3d --- /dev/null +++ b/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-strict-delta-20260605T203710Z.json @@ -0,0 +1,286 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T20:37:10.528Z", + "endedAt": "2026-06-05T20:37:21.966Z", + "platform": "darwin", + "arch": "arm64", + "config": { + "url": "ws://127.0.0.1:17880", + "serverApiUrl": "http://127.0.0.1:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-99097-fadf776d-a03a-42a1-92bc-c45de56c5a2c", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-99097", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-99097", + "subscriberIdentity": "fluxer-native-subscriber-99097", + "required": true, + "strict": true, + "timeoutMs": 60000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 10000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 30, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "fast", + "videoInput": "nv12", + "subscriberVideoQuality": "high", + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": false, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": false, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 4000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": false, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "/Users/hampus/Development/fluxer/fluxer_desktop/native-media-reports/mac-m3-ultra-livekit-video-only-strict-delta-20260605T203710Z.json", + "verbose": false, + "scenarioName": "codec-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 1280x720", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + } + ], + "skippedReason": null, + "error": "received FPS 28.46 below 28.5", + "hardwareEncoder": { + "available": true, + "backend": "videotoolbox", + "compiled": true, + "runtime": true, + "codecs": ["h264", "h265"], + "zeroCopy": false, + "nativeInputs": [], + "detail": "VideoToolbox can create hardware-accelerated compression sessions for h264, h265; supported hardware-codec probe set: h264, h265" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 310, + "strictAudioFrames": 0, + "strictVideoFrames": 285, + "videoCallbacks": 309, + "videoBytes": 427161600, + "receivedFps": 28.46, + "requiredFps": 28.5, + "maxVideoFrameGapMs": 63, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 69, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": { + "1280x720": 285 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "nv12", + "pattern": "fast", + "targetFps": 30, + "startedAtMs": 1780691830909, + "stoppedAtMs": null, + "framesPushed": 332, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 36, + "lastPushAtMs": 1780691841942, + "elapsedMs": 11151, + "producedFps": 29.77 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780691831955, + "error": null, + "participants": [ + { + "identity": "fluxer-native-subscriber-99097", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-99097", + "trackCount": 1, + "tracks": [ + { + "sid": "TR_VSmHHYYxDdRMdt", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_ZYRki8TB8qqy", + "trackSid": "TR_VSmHHYYxDdRMdt", + "width": 1280, + "height": 720, + "timestampUs": 246688341000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_VSmHHYYxDdRMdt", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 28.2, + "packetsLost": 0, + "fps": 29, + "codec": "video/VP8" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 329, + "outgoingVideoFramesAccepted": 329, + "outgoingVideoFramesDropped": 16, + "outgoingVideoFramesCoalesced": 16, + "outgoingVideoFramesCaptured": 313, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 28.51, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 34, + "outgoingVideoMaxPushLatencyMs": 34, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_ZYRki8TB8qqy", + "trackSid": "TR_VSmHHYYxDdRMdt", + "kind": "video", + "bitrateKbps": 27.3, + "packetsLost": 0, + "jitterMs": 3, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/windows-parallels-arm64/h264-360p10-strict-single-20260605.json b/fluxer_desktop/native-media-reports/windows-parallels-arm64/h264-360p10-strict-single-20260605.json new file mode 100644 index 0000000..7c19f32 --- /dev/null +++ b/fluxer_desktop/native-media-reports/windows-parallels-arm64/h264-360p10-strict-single-20260605.json @@ -0,0 +1,391 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T22:33:47.023Z", + "endedAt": "2026-06-05T22:34:19.284Z", + "platform": "win32", + "arch": "arm64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-6084-b067b112-e82c-4f9b-a8e5-8c17b58ce892", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-6084", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-6084", + "subscriberIdentity": "fluxer-native-subscriber-6084", + "required": true, + "strict": true, + "timeoutMs": 120000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 30000, + "width": 640, + "height": 360, + "expectedWidth": 640, + "expectedHeight": 360, + "fps": 10, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 150, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": null, + "screenCodecs": ["h264"], + "expectedScreenCodecs": ["h264"], + "codec": "h264", + "expectedScreenCodec": "video/H264", + "secondaryPublisher": false, + "secondaryPublisherCodec": "h264", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/H264", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 10, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 3000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\h264-360p10-strict-single-20260605.json", + "verbose": false, + "scenarioName": "codec-h264", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 640x360", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 6", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 2", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 6", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/H264", + "pass": true + }, + { + "name": "subscriber inbound video codec video/H264", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": "received FPS 9.43 below 9.5", + "hardwareEncoder": { + "available": false, + "backend": "none", + "compiled": true, + "runtime": false, + "codecs": [], + "zeroCopy": false, + "nativeInputs": [], + "reason": "runtime_prerequisite_missing", + "detail": "missing nvcuda.dll, nvEncodeAPI64.dll" + }, + "metrics": { + "inboundAudioFrames": 6176, + "inboundVideoFrames": 287, + "strictAudioFrames": 299, + "strictVideoFrames": 283, + "videoCallbacks": 287, + "videoBytes": 99187200, + "receivedFps": 9.43, + "requiredFps": 9.5, + "maxVideoFrameGapMs": 204, + "maxAudioFrameGapMs": 103, + "maxVideoFrameGapOverallMs": 204, + "maxAudioFrameGapOverallMs": 103, + "avDriftMs": 4, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": { + "640x360": 283 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 10, + "startedAtMs": 1780698828184, + "stoppedAtMs": null, + "framesPushed": 311, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 103, + "lastPushAtMs": 1780698859185, + "elapsedMs": 31105, + "producedFps": 10 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780698829266, + "error": null, + "participants": [ + { + "identity": "fluxer-native-publisher-6084", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_VSTAkE5Tyvf84k", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/H264", + "muted": false + }, + { + "sid": "TR_AMR8qs636tXCeM", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_AsiQhtzQxKx3hg", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-subscriber-6084", + "trackCount": 0, + "tracks": [] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_yWuJtiAsGJHk", + "trackSid": "TR_VSTAkE5Tyvf84k", + "width": 640, + "height": 360, + "timestampUs": 1737095000 + }, + "publisherStats": { + "rttMs": 4, + "outbound": [ + { + "trackSid": "TR_AsiQhtzQxKx3hg", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 259.9, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_AMR8qs636tXCeM", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 99, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSTAkE5Tyvf84k", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 1267, + "packetsLost": 0, + "fps": 11, + "codec": "video/H264" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 310, + "outgoingVideoFramesAccepted": 310, + "outgoingVideoFramesDropped": 4, + "outgoingVideoFramesCoalesced": 4, + "outgoingVideoFramesCaptured": 306, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 9.87, + "outgoingVideoTargetFps": 10, + "outgoingVideoMaxQueueAgeMs": 100, + "outgoingVideoMaxPushLatencyMs": 101, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 56, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 4, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_yWuJtiAsGJHk", + "trackSid": "TR_AMR8qs636tXCeM", + "kind": "audio", + "bitrateKbps": 99, + "packetsLost": 0, + "jitterMs": 1, + "audioLevel": 0.354, + "codec": "audio/opus" + }, + { + "participantSid": "PA_yWuJtiAsGJHk", + "trackSid": "TR_VSTAkE5Tyvf84k", + "kind": "video", + "bitrateKbps": 1267, + "packetsLost": 0, + "jitterMs": 4, + "codec": "video/H264" + }, + { + "participantSid": "PA_yWuJtiAsGJHk", + "trackSid": "TR_AsiQhtzQxKx3hg", + "kind": "audio", + "bitrateKbps": 259.9, + "packetsLost": 0, + "jitterMs": 1, + "audioLevel": 0.307, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/windows-parallels-arm64/h264-360p15-strict-single-20260605.json b/fluxer_desktop/native-media-reports/windows-parallels-arm64/h264-360p15-strict-single-20260605.json new file mode 100644 index 0000000..8cb3e81 --- /dev/null +++ b/fluxer_desktop/native-media-reports/windows-parallels-arm64/h264-360p15-strict-single-20260605.json @@ -0,0 +1,391 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T22:32:58.250Z", + "endedAt": "2026-06-05T22:33:30.493Z", + "platform": "win32", + "arch": "arm64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-4444-6d4fc834-c519-4a03-831b-1f3c4f7b8db5", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-4444", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-4444", + "subscriberIdentity": "fluxer-native-subscriber-4444", + "required": true, + "strict": true, + "timeoutMs": 120000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 30000, + "width": 640, + "height": 360, + "expectedWidth": 640, + "expectedHeight": 360, + "fps": 15, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 150, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": null, + "screenCodecs": ["h264"], + "expectedScreenCodecs": ["h264"], + "codec": "h264", + "expectedScreenCodec": "video/H264", + "secondaryPublisher": false, + "secondaryPublisherCodec": "h264", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/H264", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 4000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\h264-360p15-strict-single-20260605.json", + "verbose": false, + "scenarioName": "codec-h264", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 640x360", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 6", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 2", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 6", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/H264", + "pass": true + }, + { + "name": "subscriber inbound video codec video/H264", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": "received FPS 11.83 below 14.25", + "hardwareEncoder": { + "available": false, + "backend": "none", + "compiled": true, + "runtime": false, + "codecs": [], + "zeroCopy": false, + "nativeInputs": [], + "reason": "runtime_prerequisite_missing", + "detail": "missing nvcuda.dll, nvEncodeAPI64.dll" + }, + "metrics": { + "inboundAudioFrames": 6188, + "inboundVideoFrames": 360, + "strictAudioFrames": 299, + "strictVideoFrames": 356, + "videoCallbacks": 360, + "videoBytes": 124416000, + "receivedFps": 11.83, + "requiredFps": 14.25, + "maxVideoFrameGapMs": 220, + "maxAudioFrameGapMs": 115, + "maxVideoFrameGapOverallMs": 220, + "maxAudioFrameGapOverallMs": 115, + "avDriftMs": 16, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": { + "640x360": 356 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 15, + "startedAtMs": 1780698779365, + "stoppedAtMs": null, + "framesPushed": 467, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 87, + "lastPushAtMs": 1780698810433, + "elapsedMs": 31131, + "producedFps": 15 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780698780443, + "error": null, + "participants": [ + { + "identity": "fluxer-native-subscriber-4444", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-4444", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_VSu5oXdquxv9yn", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/H264", + "muted": false + }, + { + "sid": "TR_AMw39xhBtq2iKo", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_AsUfFKczDubKFH", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_jmECWh8cUV8Z", + "trackSid": "TR_VSu5oXdquxv9yn", + "width": 640, + "height": 360, + "timestampUs": 1688313000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_AsUfFKczDubKFH", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 263, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_AMw39xhBtq2iKo", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 97.9, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSu5oXdquxv9yn", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 1957.3, + "packetsLost": 0, + "fps": 15, + "codec": "video/H264" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 465, + "outgoingVideoFramesAccepted": 465, + "outgoingVideoFramesDropped": 9, + "outgoingVideoFramesCoalesced": 9, + "outgoingVideoFramesCaptured": 456, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 14.7, + "outgoingVideoTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 67, + "outgoingVideoMaxPushLatencyMs": 68, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 45, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_jmECWh8cUV8Z", + "trackSid": "TR_VSu5oXdquxv9yn", + "kind": "video", + "bitrateKbps": 1957.3, + "packetsLost": 0, + "jitterMs": 4, + "codec": "video/H264" + }, + { + "participantSid": "PA_jmECWh8cUV8Z", + "trackSid": "TR_AMw39xhBtq2iKo", + "kind": "audio", + "bitrateKbps": 97.9, + "packetsLost": 0, + "jitterMs": 1, + "audioLevel": 0.33, + "codec": "audio/opus" + }, + { + "participantSid": "PA_jmECWh8cUV8Z", + "trackSid": "TR_AsUfFKczDubKFH", + "kind": "audio", + "bitrateKbps": 263, + "packetsLost": 0, + "jitterMs": 1, + "audioLevel": 0.342, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/windows-parallels-arm64/h265-failfast-20260605.json b/fluxer_desktop/native-media-reports/windows-parallels-arm64/h265-failfast-20260605.json new file mode 100644 index 0000000..7862322 --- /dev/null +++ b/fluxer_desktop/native-media-reports/windows-parallels-arm64/h265-failfast-20260605.json @@ -0,0 +1,258 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T22:35:16.027Z", + "endedAt": "2026-06-05T22:35:17.121Z", + "platform": "win32", + "arch": "arm64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-6712-42fbb624-d6ae-4495-80c6-f657bba60b68", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-6712", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-6712", + "subscriberIdentity": "fluxer-native-subscriber-6712", + "required": true, + "strict": true, + "timeoutMs": 60000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 10000, + "width": 640, + "height": 360, + "expectedWidth": 640, + "expectedHeight": 360, + "fps": 15, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 150, + "maxPacketLoss": 0, + "requireStableResolution": false, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": null, + "screenCodecs": ["h265"], + "expectedScreenCodecs": ["h265"], + "codec": "h265", + "expectedScreenCodec": "video/H265", + "secondaryPublisher": false, + "secondaryPublisherCodec": "h265", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/H265", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 4000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": false, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\h265-failfast-20260605.json", + "verbose": false, + "scenarioName": "codec-h265", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": false + }, + { + "name": "subscriber remote screenshare subscription", + "pass": false + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": false + }, + { + "name": "subscriber video frame callback", + "pass": false + }, + { + "name": "publisher outbound screenshare stats", + "pass": false + }, + { + "name": "subscriber inbound video stats", + "pass": false + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": false + }, + { + "name": "subscriber video resolution 640x360", + "pass": false + }, + { + "name": "subscriber aggregate inbound audio frames >= 6", + "pass": false + }, + { + "name": "subscriber inbound audio stats count >= 2", + "pass": false + }, + { + "name": "publisher local microphone publication", + "pass": false + }, + { + "name": "subscriber remote microphone subscription", + "pass": false + }, + { + "name": "subscriber inbound audio frames >= 6", + "pass": false + }, + { + "name": "publisher outbound microphone stats", + "pass": false + }, + { + "name": "subscriber inbound audio stats", + "pass": false + }, + { + "name": "publisher local screen-share audio publication", + "pass": false + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": false + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": false + }, + { + "name": "publisher outbound screenshare codec video/H265", + "pass": false + }, + { + "name": "subscriber inbound video codec video/H265", + "pass": false + } + ], + "skippedReason": null, + "error": "Error: H.265 publishing requires hardware encoder support; missing nvcuda.dll, nvEncodeAPI64.dll", + "hardwareEncoder": { + "available": false, + "backend": "none", + "compiled": true, + "runtime": false, + "codecs": [], + "zeroCopy": false, + "nativeInputs": [], + "reason": "runtime_prerequisite_missing", + "detail": "missing nvcuda.dll, nvEncodeAPI64.dll" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 0, + "strictAudioFrames": 0, + "strictVideoFrames": 0, + "videoCallbacks": 0, + "videoBytes": 0, + "receivedFps": 0, + "requiredFps": 14.25, + "maxVideoFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 0, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": {}, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": null, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": null, + "error": null, + "participants": [] + }, + "lastVideoMeta": null, + "publisherStats": { + "rttMs": null, + "outbound": [], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": null, + "outbound": [], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/windows-parallels-arm64/run-livekit.ps1 b/fluxer_desktop/native-media-reports/windows-parallels-arm64/run-livekit.ps1 new file mode 100644 index 0000000..af93ad0 --- /dev/null +++ b/fluxer_desktop/native-media-reports/windows-parallels-arm64/run-livekit.ps1 @@ -0,0 +1,96 @@ +param( + [string]$Scenario = "vp8-360p15-strict-single", + [string]$Codec = "vp8", + [string]$SecondaryCodec = "", + [int]$Width = 640, + [int]$Height = 360, + [double]$Fps = 15, + [int]$DurationMs = 10000, + [int]$TimeoutMs = 60000, + [int]$SecondPublisher = 0, + [int]$ScreenSimulcast = 0, + [int]$ScreenAudio = 1, + [int]$Microphone = 1, + [int]$SubscriptionCycle = 1, + [int]$DataPacket = 1, + [int]$ValidateServerPublishing = 1, + [int]$RequireStableResolution = 1, + [int]$MaxPacketLoss = 0, + [int]$MaxAvDriftMs = 150, + [int]$MaxFrameGapMs = 250, + [int]$MaxAudioFrameGapMs = 250, + [double]$MinReceivedFpsRatio = 0.95, + [int]$MaxBitrateBps = 4000000, + [string]$LiveKitUrl = "ws://100.79.83.54:17880", + [string]$LiveKitApiUrl = "http://100.79.83.54:17880", + [string]$ApiKey = "devkey", + [string]$ApiSecret = "secret", + [string]$NodeDir = "C:\nodejs\node-v24.16.0-win-arm64", + [string]$WorkDir = "C:\f\fluxer_desktop\native\webrtc-sender", + [string]$ReportDir = "C:\Mac\Home\Development\fluxer\fluxer_desktop\native-media-reports\windows-parallels-arm64", + [int]$VerboseHarness = 0 +) + +$ErrorActionPreference = "Stop" +$nodeExe = Join-Path $NodeDir "node.exe" +if (-not (Test-Path $nodeExe)) { + throw "Node executable not found: $nodeExe" +} +if (-not (Test-Path $WorkDir)) { + throw "WorkDir not found: $WorkDir" +} + +New-Item -ItemType Directory -Force -Path $ReportDir | Out-Null +$reportPath = Join-Path $ReportDir "$Scenario.json" +$logPath = Join-Path $ReportDir "$Scenario.log" +Remove-Item -Force $reportPath, $logPath -ErrorAction SilentlyContinue + +$env:Path = "$NodeDir;$env:Path" +$env:LIVEKIT_URL = $LiveKitUrl +$env:LIVEKIT_API_URL = $LiveKitApiUrl +$env:LIVEKIT_API_KEY = $ApiKey +$env:LIVEKIT_API_SECRET = $ApiSecret +$env:FLUXER_WEBRTC_SENDER_LIVEKIT_REQUIRED = "1" +$env:LIVEKIT_HARNESS_STRICT = "1" +$env:LIVEKIT_HARNESS_DURATION_MS = [string]$DurationMs +$env:LIVEKIT_HARNESS_TIMEOUT_MS = [string]$TimeoutMs +$env:LIVEKIT_CONNECT_TIMEOUT_MS = "30000" +$env:LIVEKIT_SCREEN_WIDTH = [string]$Width +$env:LIVEKIT_SCREEN_HEIGHT = [string]$Height +$env:LIVEKIT_SCREEN_FPS = [string]$Fps +$env:LIVEKIT_MIN_VIDEO_FPS = [string]([math]::Min(15, $Fps)) +$env:LIVEKIT_MIN_RECEIVED_FPS_RATIO = [string]$MinReceivedFpsRatio +$env:LIVEKIT_SCREEN_CODEC = $Codec +$env:LIVEKIT_SCREEN_SIMULCAST = [string]$ScreenSimulcast +$env:LIVEKIT_ENABLE_SECOND_PUBLISHER = [string]$SecondPublisher +$env:LIVEKIT_ENABLE_MICROPHONE = [string]$Microphone +$env:LIVEKIT_ENABLE_SCREEN_AUDIO = [string]$ScreenAudio +$env:LIVEKIT_ENABLE_SUBSCRIPTION_CYCLE = [string]$SubscriptionCycle +$env:LIVEKIT_ENABLE_DATA_PACKET = [string]$DataPacket +$env:LIVEKIT_VALIDATE_SERVER_PUBLISHING = [string]$ValidateServerPublishing +$env:LIVEKIT_MAX_PACKET_LOSS = [string]$MaxPacketLoss +$env:LIVEKIT_REQUIRE_STABLE_RESOLUTION = [string]$RequireStableResolution +$env:LIVEKIT_MAX_AV_DRIFT_MS = [string]$MaxAvDriftMs +$env:LIVEKIT_MAX_FRAME_GAP_MS = [string]$MaxFrameGapMs +$env:LIVEKIT_MAX_AUDIO_FRAME_GAP_MS = [string]$MaxAudioFrameGapMs +$env:LIVEKIT_SCREEN_MAX_BITRATE_BPS = [string]$MaxBitrateBps +$env:LIVEKIT_HARNESS_REPORT_PATH = $reportPath +$env:LIVEKIT_VERBOSE = [string]$VerboseHarness + +if ($SecondPublisher -ne 0 -and $SecondaryCodec.Trim().Length -gt 0) { + $env:LIVEKIT_SECOND_PUBLISHER_SCREEN_CODEC = $SecondaryCodec +} + +Set-Location $WorkDir +"STARTED=$(Get-Date -Format o)" | Out-File -FilePath $logPath -Encoding utf8 +"& $nodeExe scripts/livekit-harness.mjs" | Add-Content -Path $logPath +$previousErrorActionPreference = $ErrorActionPreference +$ErrorActionPreference = "Continue" +& $nodeExe "scripts/livekit-harness.mjs" *>> $logPath +$code = $LASTEXITCODE +$ErrorActionPreference = $previousErrorActionPreference +"EXIT_CODE=$code" | Add-Content -Path $logPath +"FINISHED=$(Get-Date -Format o)" | Add-Content -Path $logPath + +Get-Content -Path $logPath +exit $code diff --git a/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-360p15-strict-multipub-20260605.json b/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-360p15-strict-multipub-20260605.json new file mode 100644 index 0000000..3304af6 --- /dev/null +++ b/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-360p15-strict-multipub-20260605.json @@ -0,0 +1,637 @@ +{ + "status": "pass", + "startedAt": "2026-06-05T22:34:34.390Z", + "endedAt": "2026-06-05T22:34:56.901Z", + "platform": "win32", + "arch": "arm64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-576-9baa1b50-b3f6-47dc-9222-bb3a324a8678", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-576", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-576", + "subscriberIdentity": "fluxer-native-subscriber-576", + "required": true, + "strict": true, + "timeoutMs": 120000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 20000, + "width": 640, + "height": 360, + "expectedWidth": 640, + "expectedHeight": 360, + "fps": 15, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 150, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": null, + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": true, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 4000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\vp8-360p15-strict-multipub-20260605.json", + "verbose": false, + "scenarioName": "codec-vp8-with-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 6", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 2", + "pass": true + }, + { + "name": "subscriber video resolution 640x360", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 12", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 4", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 12", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "secondary publisher connected", + "pass": true + }, + { + "name": "secondary publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote secondary screenshare subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare stats", + "pass": true + }, + { + "name": "secondary publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound secondary codec video/VP8", + "pass": true + }, + { + "name": "secondary publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote secondary microphone subscription", + "pass": true + }, + { + "name": "secondary publisher outbound microphone stats", + "pass": true + }, + { + "name": "secondary publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote secondary screen-share audio subscription", + "pass": true + }, + { + "name": "secondary publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "subscriber secondary data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + }, + { + "name": "server sees secondary publisher screenshare publication", + "pass": true + }, + { + "name": "server sees secondary publisher microphone publication", + "pass": true + }, + { + "name": "server sees secondary publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": null, + "hardwareEncoder": { + "available": false, + "backend": "none", + "compiled": true, + "runtime": false, + "codecs": [], + "zeroCopy": false, + "nativeInputs": [], + "reason": "runtime_prerequisite_missing", + "detail": "missing nvcuda.dll, nvEncodeAPI64.dll" + }, + "metrics": { + "inboundAudioFrames": 8386, + "inboundVideoFrames": 605, + "strictAudioFrames": 199, + "strictVideoFrames": 588, + "videoCallbacks": 605, + "videoBytes": 209088000, + "receivedFps": 29.42, + "requiredFps": 14.25, + "maxVideoFrameGapMs": 129, + "maxAudioFrameGapMs": 108, + "maxVideoFrameGapOverallMs": 129, + "maxAudioFrameGapOverallMs": 112, + "avDriftMs": 5, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": { + "640x360": 588 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 15, + "startedAtMs": 1780698875820, + "stoppedAtMs": null, + "framesPushed": 317, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 74, + "lastPushAtMs": 1780698896886, + "elapsedMs": 21084, + "producedFps": 15.04 + }, + "secondaryPublisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 15, + "startedAtMs": 1780698875820, + "stoppedAtMs": null, + "framesPushed": 317, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 74, + "lastPushAtMs": 1780698896888, + "elapsedMs": 21084, + "producedFps": 15.04 + }, + "serverPublishing": { + "fetchedAtMs": 1780698876883, + "error": null, + "participants": [ + { + "identity": "fluxer-native-publisher-2-576", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_AMafY4CpWgYhFy", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_AsEzCDkQT4wKAK", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_VSbd2RjXnY3stM", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-subscriber-576", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-576", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_VSc4kF32666sNr", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + }, + { + "sid": "TR_AMrQCsApajeKHi", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_AsX4E2jVX6P6XY", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_adnUMDZZHzat", + "trackSid": "TR_VSc4kF32666sNr", + "width": 640, + "height": 360, + "timestampUs": 1774721000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_AsX4E2jVX6P6XY", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 258.8, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSc4kF32666sNr", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 1259.5, + "packetsLost": 0, + "fps": 15, + "codec": "video/VP8" + }, + { + "trackSid": "TR_AMrQCsApajeKHi", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 98.2, + "packetsLost": 0, + "codec": "audio/opus" + } + ], + "inbound": [ + { + "participantSid": "PA_A8vwT8LTQovP", + "trackSid": "TR_AMafY4CpWgYhFy", + "kind": "audio", + "bitrateKbps": 99.8, + "packetsLost": 0, + "jitterMs": 2, + "audioLevel": 0.306, + "codec": "audio/opus" + }, + { + "participantSid": "PA_A8vwT8LTQovP", + "trackSid": "TR_VSbd2RjXnY3stM", + "kind": "video", + "bitrateKbps": 1257.1, + "packetsLost": 0, + "jitterMs": 5, + "codec": "video/VP8" + }, + { + "participantSid": "PA_A8vwT8LTQovP", + "trackSid": "TR_AsEzCDkQT4wKAK", + "kind": "audio", + "bitrateKbps": 264.5, + "packetsLost": 0, + "jitterMs": 2, + "audioLevel": 0.306, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 310, + "outgoingVideoFramesAccepted": 310, + "outgoingVideoFramesDropped": 6, + "outgoingVideoFramesCoalesced": 6, + "outgoingVideoFramesCaptured": 304, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 14.7, + "outgoingVideoTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 67, + "outgoingVideoMaxPushLatencyMs": 67, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 51, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": { + "rttMs": 2, + "outbound": [ + { + "trackSid": "TR_AsEzCDkQT4wKAK", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 259.9, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSbd2RjXnY3stM", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 1171.6, + "packetsLost": 0, + "fps": 15, + "codec": "video/VP8" + }, + { + "trackSid": "TR_AMafY4CpWgYhFy", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 98.1, + "packetsLost": 0, + "codec": "audio/opus" + } + ], + "inbound": [ + { + "participantSid": "PA_adnUMDZZHzat", + "trackSid": "TR_AsX4E2jVX6P6XY", + "kind": "audio", + "bitrateKbps": 259.2, + "packetsLost": 0, + "jitterMs": 2, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_adnUMDZZHzat", + "trackSid": "TR_AMrQCsApajeKHi", + "kind": "audio", + "bitrateKbps": 98, + "packetsLost": 0, + "jitterMs": 2, + "audioLevel": 0.326, + "codec": "audio/opus" + }, + { + "participantSid": "PA_adnUMDZZHzat", + "trackSid": "TR_VSc4kF32666sNr", + "kind": "video", + "bitrateKbps": 1176.7, + "packetsLost": 0, + "jitterMs": 4, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 312, + "outgoingVideoFramesAccepted": 312, + "outgoingVideoFramesDropped": 6, + "outgoingVideoFramesCoalesced": 6, + "outgoingVideoFramesCaptured": 306, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 14.7, + "outgoingVideoTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 67, + "outgoingVideoMaxPushLatencyMs": 68, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 51, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "subscriberStats": { + "rttMs": 2, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_adnUMDZZHzat", + "trackSid": "TR_AsX4E2jVX6P6XY", + "kind": "audio", + "bitrateKbps": 258.8, + "packetsLost": 0, + "jitterMs": 2, + "audioLevel": 0.336, + "codec": "audio/opus" + }, + { + "participantSid": "PA_adnUMDZZHzat", + "trackSid": "TR_VSc4kF32666sNr", + "kind": "video", + "bitrateKbps": 1259.5, + "packetsLost": 0, + "jitterMs": 4, + "codec": "video/VP8" + }, + { + "participantSid": "PA_adnUMDZZHzat", + "trackSid": "TR_AMrQCsApajeKHi", + "kind": "audio", + "bitrateKbps": 98.2, + "packetsLost": 0, + "jitterMs": 2, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_A8vwT8LTQovP", + "trackSid": "TR_AMafY4CpWgYhFy", + "kind": "audio", + "bitrateKbps": 99.7, + "packetsLost": 0, + "jitterMs": 2, + "audioLevel": 0.306, + "codec": "audio/opus" + }, + { + "participantSid": "PA_A8vwT8LTQovP", + "trackSid": "TR_AsEzCDkQT4wKAK", + "kind": "audio", + "bitrateKbps": 264.3, + "packetsLost": 0, + "jitterMs": 2, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_A8vwT8LTQovP", + "trackSid": "TR_VSbd2RjXnY3stM", + "kind": "video", + "bitrateKbps": 1256, + "packetsLost": 0, + "jitterMs": 6, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-360p15-strict-single-smoke-20260605.json b/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-360p15-strict-single-smoke-20260605.json new file mode 100644 index 0000000..a513601 --- /dev/null +++ b/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-360p15-strict-single-smoke-20260605.json @@ -0,0 +1,391 @@ +{ + "status": "pass", + "startedAt": "2026-06-05T22:30:31.342Z", + "endedAt": "2026-06-05T22:30:43.694Z", + "platform": "win32", + "arch": "arm64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-3164-5cfbcff1-2827-441a-a57b-4333e450d829", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-3164", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-3164", + "subscriberIdentity": "fluxer-native-subscriber-3164", + "required": true, + "strict": true, + "timeoutMs": 60000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 10000, + "width": 640, + "height": 360, + "expectedWidth": 640, + "expectedHeight": 360, + "fps": 15, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 150, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": null, + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": false, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 4000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\vp8-360p15-strict-single-smoke-20260605.json", + "verbose": true, + "scenarioName": "codec-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 640x360", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 6", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 2", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 6", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": null, + "hardwareEncoder": { + "available": false, + "backend": "none", + "compiled": true, + "runtime": false, + "codecs": [], + "zeroCopy": false, + "nativeInputs": [], + "reason": "runtime_prerequisite_missing", + "detail": "missing nvcuda.dll, nvEncodeAPI64.dll" + }, + "metrics": { + "inboundAudioFrames": 2156, + "inboundVideoFrames": 152, + "strictAudioFrames": 100, + "strictVideoFrames": 147, + "videoCallbacks": 152, + "videoBytes": 52531200, + "receivedFps": 14.66, + "requiredFps": 14.25, + "maxVideoFrameGapMs": 143, + "maxAudioFrameGapMs": 105, + "maxVideoFrameGapOverallMs": 143, + "maxAudioFrameGapOverallMs": 325, + "avDriftMs": 26, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": { + "640x360": 147 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 15, + "startedAtMs": 1780698632580, + "stoppedAtMs": null, + "framesPushed": 163, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 355, + "lastPushAtMs": 1780698643669, + "elapsedMs": 11117, + "producedFps": 14.66 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780698633676, + "error": null, + "participants": [ + { + "identity": "fluxer-native-subscriber-3164", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-3164", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_VSD8qaPQikwWiL", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + }, + { + "sid": "TR_AMMkiV8hg3XbkW", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_AsTaYjh3PSCFVz", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_fME8NrPiTu5d", + "trackSid": "TR_VSD8qaPQikwWiL", + "width": 640, + "height": 360, + "timestampUs": 1521475000 + }, + "publisherStats": { + "rttMs": 3, + "outbound": [ + { + "trackSid": "TR_AMMkiV8hg3XbkW", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 98.9, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSD8qaPQikwWiL", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 1233, + "packetsLost": 0, + "fps": 15, + "codec": "video/VP8" + }, + { + "trackSid": "TR_AsTaYjh3PSCFVz", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 256.8, + "packetsLost": 0, + "codec": "audio/opus" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 1, + "outgoingVideoFramesProduced": 161, + "outgoingVideoFramesAccepted": 161, + "outgoingVideoFramesDropped": 2, + "outgoingVideoFramesCoalesced": 2, + "outgoingVideoFramesCaptured": 158, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 14.36, + "outgoingVideoTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 66, + "outgoingVideoMaxPushLatencyMs": 66, + "outgoingAudioBufferTargetMs": 300, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 1, + "outgoingAudioRebuffers": 1, + "outgoingAudioMaxFrameGapMs": 316, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_fME8NrPiTu5d", + "trackSid": "TR_VSD8qaPQikwWiL", + "kind": "video", + "bitrateKbps": 1158.7, + "packetsLost": 0, + "jitterMs": 22, + "codec": "video/VP8" + }, + { + "participantSid": "PA_fME8NrPiTu5d", + "trackSid": "TR_AsTaYjh3PSCFVz", + "kind": "audio", + "bitrateKbps": 252.2, + "packetsLost": 0, + "jitterMs": 1, + "audioLevel": 0.336, + "codec": "audio/opus" + }, + { + "participantSid": "PA_fME8NrPiTu5d", + "trackSid": "TR_AMMkiV8hg3XbkW", + "kind": "audio", + "bitrateKbps": 99, + "packetsLost": 0, + "jitterMs": 1, + "audioLevel": 0.332, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-360p30-reason-after-fix-20260605.json b/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-360p30-reason-after-fix-20260605.json new file mode 100644 index 0000000..544fd2b --- /dev/null +++ b/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-360p30-reason-after-fix-20260605.json @@ -0,0 +1,391 @@ +{ + "status": "pass", + "startedAt": "2026-06-05T22:39:23.612Z", + "endedAt": "2026-06-05T22:39:35.835Z", + "platform": "win32", + "arch": "arm64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-8360-3df01e6b-06f6-4b3c-9e56-c691627bb305", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-8360", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-8360", + "subscriberIdentity": "fluxer-native-subscriber-8360", + "required": true, + "strict": true, + "timeoutMs": 60000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 10000, + "width": 640, + "height": 360, + "expectedWidth": 640, + "expectedHeight": 360, + "fps": 30, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 150, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": null, + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": false, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 5000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\vp8-360p30-reason-after-fix-20260605.json", + "verbose": false, + "scenarioName": "codec-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 640x360", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 6", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 2", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 6", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": null, + "hardwareEncoder": { + "available": false, + "backend": "none", + "compiled": true, + "runtime": false, + "codecs": [], + "zeroCopy": false, + "nativeInputs": [], + "reason": "runtime_prerequisite_missing", + "detail": "missing nvcuda.dll, nvEncodeAPI64.dll" + }, + "metrics": { + "inboundAudioFrames": 2170, + "inboundVideoFrames": 299, + "strictAudioFrames": 99, + "strictVideoFrames": 291, + "videoCallbacks": 299, + "videoBytes": 103334400, + "receivedFps": 29.05, + "requiredFps": 28.5, + "maxVideoFrameGapMs": 65, + "maxAudioFrameGapMs": 109, + "maxVideoFrameGapOverallMs": 195, + "maxAudioFrameGapOverallMs": 109, + "avDriftMs": 41, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": { + "640x360": 291 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 30, + "startedAtMs": 1780699164743, + "stoppedAtMs": null, + "framesPushed": 333, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 52, + "lastPushAtMs": 1780699175812, + "elapsedMs": 11095, + "producedFps": 30.01 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780699165815, + "error": null, + "participants": [ + { + "identity": "fluxer-native-subscriber-8360", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-8360", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_AMSaSBEwb9cjwL", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_Ash3ybc8XXbukk", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_VSzotHy6zMQsRG", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_iYVBCmnrZuSb", + "trackSid": "TR_VSzotHy6zMQsRG", + "width": 640, + "height": 360, + "timestampUs": 2053643000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_AMSaSBEwb9cjwL", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 99, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_Ash3ybc8XXbukk", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 265.1, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSzotHy6zMQsRG", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 2414.2, + "packetsLost": 0, + "fps": 29, + "codec": "video/VP8" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 329, + "outgoingVideoFramesAccepted": 329, + "outgoingVideoFramesDropped": 13, + "outgoingVideoFramesCoalesced": 13, + "outgoingVideoFramesCaptured": 316, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 28.84, + "outgoingVideoTargetFps": 30, + "outgoingVideoMaxQueueAgeMs": 34, + "outgoingVideoMaxPushLatencyMs": 35, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 42, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_iYVBCmnrZuSb", + "trackSid": "TR_Ash3ybc8XXbukk", + "kind": "audio", + "bitrateKbps": 265.1, + "packetsLost": 0, + "jitterMs": 1, + "audioLevel": 0.318, + "codec": "audio/opus" + }, + { + "participantSid": "PA_iYVBCmnrZuSb", + "trackSid": "TR_AMSaSBEwb9cjwL", + "kind": "audio", + "bitrateKbps": 99, + "packetsLost": 0, + "jitterMs": 1, + "audioLevel": 0.324, + "codec": "audio/opus" + }, + { + "participantSid": "PA_iYVBCmnrZuSb", + "trackSid": "TR_VSzotHy6zMQsRG", + "kind": "video", + "bitrateKbps": 2414.2, + "packetsLost": 0, + "jitterMs": 4, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-360p30-strict-single-20260605.json b/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-360p30-strict-single-20260605.json new file mode 100644 index 0000000..f49735a --- /dev/null +++ b/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-360p30-strict-single-20260605.json @@ -0,0 +1,391 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T22:35:40.815Z", + "endedAt": "2026-06-05T22:36:13.026Z", + "platform": "win32", + "arch": "arm64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-6332-943a2620-7cc8-4881-b1a9-6d8577e9c434", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-6332", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-6332", + "subscriberIdentity": "fluxer-native-subscriber-6332", + "required": true, + "strict": true, + "timeoutMs": 120000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 30000, + "width": 640, + "height": 360, + "expectedWidth": 640, + "expectedHeight": 360, + "fps": 30, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 150, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": null, + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": false, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 5000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\vp8-360p30-strict-single-20260605.json", + "verbose": false, + "scenarioName": "codec-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 640x360", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 6", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 2", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 6", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": "received FPS 15.03 below 28.5", + "hardwareEncoder": { + "available": false, + "backend": "none", + "compiled": true, + "runtime": false, + "codecs": [], + "zeroCopy": false, + "nativeInputs": [], + "reason": "runtime_prerequisite_missing", + "detail": "missing nvcuda.dll, nvEncodeAPI64.dll" + }, + "metrics": { + "inboundAudioFrames": 6168, + "inboundVideoFrames": 461, + "strictAudioFrames": 299, + "strictVideoFrames": 451, + "videoCallbacks": 461, + "videoBytes": 159321600, + "receivedFps": 15.03, + "requiredFps": 28.5, + "maxVideoFrameGapMs": 137, + "maxAudioFrameGapMs": 110, + "maxVideoFrameGapOverallMs": 137, + "maxAudioFrameGapOverallMs": 110, + "avDriftMs": 22, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": { + "640x360": 451 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 30, + "startedAtMs": 1780698941926, + "stoppedAtMs": null, + "framesPushed": 933, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 63, + "lastPushAtMs": 1780698972994, + "elapsedMs": 31105, + "producedFps": 30 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780698943004, + "error": null, + "participants": [ + { + "identity": "fluxer-native-subscriber-6332", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-6332", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_VSBtiuMi7Xz4iL", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + }, + { + "sid": "TR_AsSNqz63GYafY3", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_AMjupdo38YrBp7", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_FcwLkjDchP7J", + "trackSid": "TR_VSBtiuMi7Xz4iL", + "width": 640, + "height": 360, + "timestampUs": 1850800000 + }, + "publisherStats": { + "rttMs": 2, + "outbound": [ + { + "trackSid": "TR_AsSNqz63GYafY3", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 261.4, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_AMjupdo38YrBp7", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 99.5, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSBtiuMi7Xz4iL", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 1258.5, + "packetsLost": 0, + "fps": 15, + "codec": "video/VP8" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 1, + "outgoingVideoFramesProduced": 930, + "outgoingVideoFramesAccepted": 930, + "outgoingVideoFramesDropped": 402, + "outgoingVideoFramesCoalesced": 402, + "outgoingVideoFramesCaptured": 527, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 17, + "outgoingVideoTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 62, + "outgoingVideoMaxPushLatencyMs": 62, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 60, + "adaptiveSendTier": "fps15", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 2, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_FcwLkjDchP7J", + "trackSid": "TR_AsSNqz63GYafY3", + "kind": "audio", + "bitrateKbps": 261.4, + "packetsLost": 0, + "jitterMs": 2, + "audioLevel": 0.319, + "codec": "audio/opus" + }, + { + "participantSid": "PA_FcwLkjDchP7J", + "trackSid": "TR_AMjupdo38YrBp7", + "kind": "audio", + "bitrateKbps": 99.5, + "packetsLost": 0, + "jitterMs": 1, + "audioLevel": 0.32, + "codec": "audio/opus" + }, + { + "participantSid": "PA_FcwLkjDchP7J", + "trackSid": "TR_VSBtiuMi7Xz4iL", + "kind": "video", + "bitrateKbps": 1258.5, + "packetsLost": 0, + "jitterMs": 3, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-360p30-strict-single-after-fix-20260605.json b/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-360p30-strict-single-after-fix-20260605.json new file mode 100644 index 0000000..3a5e192 --- /dev/null +++ b/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-360p30-strict-single-after-fix-20260605.json @@ -0,0 +1,391 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T22:39:58.041Z", + "endedAt": "2026-06-05T22:40:30.271Z", + "platform": "win32", + "arch": "arm64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-3512-db87cf14-fb04-4daf-84ee-a577ef39b456", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-3512", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-3512", + "subscriberIdentity": "fluxer-native-subscriber-3512", + "required": true, + "strict": true, + "timeoutMs": 120000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 30000, + "width": 640, + "height": 360, + "expectedWidth": 640, + "expectedHeight": 360, + "fps": 30, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 150, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": null, + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": false, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 5000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\vp8-360p30-strict-single-after-fix-20260605.json", + "verbose": false, + "scenarioName": "codec-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 640x360", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 6", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 2", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 6", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": "received FPS 16.35 below 28.5", + "hardwareEncoder": { + "available": false, + "backend": "none", + "compiled": true, + "runtime": false, + "codecs": [], + "zeroCopy": false, + "nativeInputs": [], + "reason": "runtime_prerequisite_missing", + "detail": "missing nvcuda.dll, nvEncodeAPI64.dll" + }, + "metrics": { + "inboundAudioFrames": 6178, + "inboundVideoFrames": 503, + "strictAudioFrames": 297, + "strictVideoFrames": 490, + "videoCallbacks": 503, + "videoBytes": 173836800, + "receivedFps": 16.35, + "requiredFps": 28.5, + "maxVideoFrameGapMs": 244, + "maxAudioFrameGapMs": 180, + "maxVideoFrameGapOverallMs": 244, + "maxAudioFrameGapOverallMs": 180, + "avDriftMs": 60, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": { + "640x360": 490 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 30, + "startedAtMs": 1780699199141, + "stoppedAtMs": null, + "framesPushed": 931, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 155, + "lastPushAtMs": 1780699230263, + "elapsedMs": 31136, + "producedFps": 29.9 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780699200252, + "error": null, + "participants": [ + { + "identity": "fluxer-native-publisher-3512", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_VSFLKMrtaZRUAL", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + }, + { + "sid": "TR_AMTrMJygXoaHT8", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_AseREdJhgwsKHc", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-subscriber-3512", + "trackCount": 0, + "tracks": [] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_BbRoKiddPYPe", + "trackSid": "TR_VSFLKMrtaZRUAL", + "width": 640, + "height": 360, + "timestampUs": 2108079000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_VSFLKMrtaZRUAL", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 1257.6, + "packetsLost": 0, + "fps": 15, + "codec": "video/VP8" + }, + { + "trackSid": "TR_AseREdJhgwsKHc", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 257.1, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_AMTrMJygXoaHT8", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 99.1, + "packetsLost": 0, + "codec": "audio/opus" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 1, + "outgoingVideoFramesProduced": 927, + "outgoingVideoFramesAccepted": 927, + "outgoingVideoFramesDropped": 405, + "outgoingVideoFramesCoalesced": 405, + "outgoingVideoFramesCaptured": 521, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 16.8, + "outgoingVideoTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 74, + "outgoingVideoMaxPushLatencyMs": 76, + "outgoingAudioBufferTargetMs": 300, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 1, + "outgoingAudioMaxFrameGapMs": 153, + "adaptiveSendTier": "fps15", + "adaptiveSendReason": "sendLatencyPressure" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 1, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_BbRoKiddPYPe", + "trackSid": "TR_AseREdJhgwsKHc", + "kind": "audio", + "bitrateKbps": 252.8, + "packetsLost": 0, + "jitterMs": 1, + "audioLevel": 0.337, + "codec": "audio/opus" + }, + { + "participantSid": "PA_BbRoKiddPYPe", + "trackSid": "TR_VSFLKMrtaZRUAL", + "kind": "video", + "bitrateKbps": 1169, + "packetsLost": 0, + "jitterMs": 3, + "codec": "video/VP8" + }, + { + "participantSid": "PA_BbRoKiddPYPe", + "trackSid": "TR_AMTrMJygXoaHT8", + "kind": "audio", + "bitrateKbps": 97.5, + "packetsLost": 0, + "jitterMs": 1, + "audioLevel": 0.315, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-720p15-strict-single-20260605.json b/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-720p15-strict-single-20260605.json new file mode 100644 index 0000000..95b68bf --- /dev/null +++ b/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-720p15-strict-single-20260605.json @@ -0,0 +1,391 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T22:31:55.148Z", + "endedAt": "2026-06-05T22:32:27.616Z", + "platform": "win32", + "arch": "arm64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-1340-0367e318-dcfc-498a-8724-0c3982583d5d", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-1340", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-1340", + "subscriberIdentity": "fluxer-native-subscriber-1340", + "required": true, + "strict": true, + "timeoutMs": 120000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 30000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 15, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 150, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": null, + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": false, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 8000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\vp8-720p15-strict-single-20260605.json", + "verbose": false, + "scenarioName": "codec-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 1280x720", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 6", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 2", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 6", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": "received FPS 11.06 below 14.25; max video frame gap 363ms exceeds 250ms", + "hardwareEncoder": { + "available": false, + "backend": "none", + "compiled": true, + "runtime": false, + "codecs": [], + "zeroCopy": false, + "nativeInputs": [], + "reason": "runtime_prerequisite_missing", + "detail": "missing nvcuda.dll, nvEncodeAPI64.dll" + }, + "metrics": { + "inboundAudioFrames": 6230, + "inboundVideoFrames": 334, + "strictAudioFrames": 298, + "strictVideoFrames": 331, + "videoCallbacks": 334, + "videoBytes": 461721600, + "receivedFps": 11.06, + "requiredFps": 14.25, + "maxVideoFrameGapMs": 363, + "maxAudioFrameGapMs": 108, + "maxVideoFrameGapOverallMs": 363, + "maxAudioFrameGapOverallMs": 108, + "avDriftMs": 38, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": { + "1280x720": 331 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 15, + "startedAtMs": 1780698716263, + "stoppedAtMs": null, + "framesPushed": 471, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 93, + "lastPushAtMs": 1780698747600, + "elapsedMs": 31355, + "producedFps": 15.02 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780698717342, + "error": null, + "participants": [ + { + "identity": "fluxer-native-subscriber-1340", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-1340", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_VSYp54vvSodQtk", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + }, + { + "sid": "TR_AMtAPT5gLdZFiG", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_AsyzCPFpjfCZ52", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_f3pUVwWqpSf9", + "trackSid": "TR_VSYp54vvSodQtk", + "width": 1280, + "height": 720, + "timestampUs": 1625403000 + }, + "publisherStats": { + "rttMs": 3, + "outbound": [ + { + "trackSid": "TR_AsyzCPFpjfCZ52", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 324.8, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSYp54vvSodQtk", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 5042, + "packetsLost": 0, + "fps": 15, + "codec": "video/VP8" + }, + { + "trackSid": "TR_AMtAPT5gLdZFiG", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 99.4, + "packetsLost": 0, + "codec": "audio/opus" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 465, + "outgoingVideoFramesAccepted": 465, + "outgoingVideoFramesDropped": 9, + "outgoingVideoFramesCoalesced": 9, + "outgoingVideoFramesCaptured": 456, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 14.71, + "outgoingVideoTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 66, + "outgoingVideoMaxPushLatencyMs": 68, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 47, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 3, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_f3pUVwWqpSf9", + "trackSid": "TR_AMtAPT5gLdZFiG", + "kind": "audio", + "bitrateKbps": 99.4, + "packetsLost": 0, + "jitterMs": 2, + "audioLevel": 0.32, + "codec": "audio/opus" + }, + { + "participantSid": "PA_f3pUVwWqpSf9", + "trackSid": "TR_VSYp54vvSodQtk", + "kind": "video", + "bitrateKbps": 5042, + "packetsLost": 0, + "jitterMs": 33, + "codec": "video/VP8" + }, + { + "participantSid": "PA_f3pUVwWqpSf9", + "trackSid": "TR_AsyzCPFpjfCZ52", + "kind": "audio", + "bitrateKbps": 324.8, + "packetsLost": 0, + "jitterMs": 1, + "audioLevel": 0.344, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-720p30-strict-single-20260605.json b/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-720p30-strict-single-20260605.json new file mode 100644 index 0000000..303c492 --- /dev/null +++ b/fluxer_desktop/native-media-reports/windows-parallels-arm64/vp8-720p30-strict-single-20260605.json @@ -0,0 +1,391 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T22:30:59.267Z", + "endedAt": "2026-06-05T22:31:31.489Z", + "platform": "win32", + "arch": "arm64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-5096-0b21498f-6c31-4ed8-8e45-930895fdc2d2", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-5096", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-5096", + "subscriberIdentity": "fluxer-native-subscriber-5096", + "required": true, + "strict": true, + "timeoutMs": 120000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 30000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 30, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 150, + "maxPacketLoss": 0, + "requireStableResolution": true, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": null, + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": false, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 8000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "C:\\Mac\\Home\\Development\\fluxer\\fluxer_desktop\\native-media-reports\\windows-parallels-arm64\\vp8-720p30-strict-single-20260605.json", + "verbose": false, + "scenarioName": "codec-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 1280x720", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 6", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 2", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 6", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": "received FPS 15.97 below 28.5", + "hardwareEncoder": { + "available": false, + "backend": "none", + "compiled": true, + "runtime": false, + "codecs": [], + "zeroCopy": false, + "nativeInputs": [], + "reason": "runtime_prerequisite_missing", + "detail": "missing nvcuda.dll, nvEncodeAPI64.dll" + }, + "metrics": { + "inboundAudioFrames": 6178, + "inboundVideoFrames": 482, + "strictAudioFrames": 298, + "strictVideoFrames": 479, + "videoCallbacks": 482, + "videoBytes": 666316800, + "receivedFps": 15.97, + "requiredFps": 28.5, + "maxVideoFrameGapMs": 102, + "maxAudioFrameGapMs": 130, + "maxVideoFrameGapOverallMs": 168, + "maxAudioFrameGapOverallMs": 130, + "avDriftMs": 43, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": { + "1280x720": 479 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 30, + "startedAtMs": 1780698660405, + "stoppedAtMs": null, + "framesPushed": 933, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 68, + "lastPushAtMs": 1780698691475, + "elapsedMs": 31087, + "producedFps": 30.01 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780698661479, + "error": null, + "participants": [ + { + "identity": "fluxer-native-subscriber-5096", + "trackCount": 0, + "tracks": [] + }, + { + "identity": "fluxer-native-publisher-5096", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_VS2iFXiujdx95V", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + }, + { + "sid": "TR_AMNf9uVyTmBcZy", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_AsZpfieifQxEYA", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + } + ] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_XJp9ZzhWYzHd", + "trackSid": "TR_VS2iFXiujdx95V", + "width": 1280, + "height": 720, + "timestampUs": 1569321000 + }, + "publisherStats": { + "rttMs": 1, + "outbound": [ + { + "trackSid": "TR_AMNf9uVyTmBcZy", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 98.4, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_AsZpfieifQxEYA", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 269.5, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VS2iFXiujdx95V", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 5155.1, + "packetsLost": 0, + "fps": 15, + "codec": "video/VP8" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 1, + "outgoingVideoFramesProduced": 930, + "outgoingVideoFramesAccepted": 930, + "outgoingVideoFramesDropped": 403, + "outgoingVideoFramesCoalesced": 403, + "outgoingVideoFramesCaptured": 526, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 16.97, + "outgoingVideoTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 45, + "outgoingVideoMaxPushLatencyMs": 46, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 57, + "adaptiveSendTier": "fps15", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 3, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_XJp9ZzhWYzHd", + "trackSid": "TR_VS2iFXiujdx95V", + "kind": "video", + "bitrateKbps": 5155.1, + "packetsLost": 0, + "jitterMs": 11, + "codec": "video/VP8" + }, + { + "participantSid": "PA_XJp9ZzhWYzHd", + "trackSid": "TR_AsZpfieifQxEYA", + "kind": "audio", + "bitrateKbps": 269.5, + "packetsLost": 0, + "jitterMs": 1, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_XJp9ZzhWYzHd", + "trackSid": "TR_AMNf9uVyTmBcZy", + "kind": "audio", + "bitrateKbps": 98.4, + "packetsLost": 0, + "jitterMs": 1, + "audioLevel": 0.336, + "codec": "audio/opus" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/windows/windows-mx450-livekit-h264-single-egress-adaptive-20260605.json b/fluxer_desktop/native-media-reports/windows/windows-mx450-livekit-h264-single-egress-adaptive-20260605.json new file mode 100644 index 0000000..dfa811e --- /dev/null +++ b/fluxer_desktop/native-media-reports/windows/windows-mx450-livekit-h264-single-egress-adaptive-20260605.json @@ -0,0 +1,391 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T21:57:48.945Z", + "endedAt": "2026-06-05T21:58:12.868Z", + "platform": "win32", + "arch": "x64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-1272-581b0bd8-4044-45a9-bb0d-695ca988d59c", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-1272", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-1272", + "subscriberIdentity": "fluxer-native-subscriber-1272", + "required": true, + "strict": true, + "timeoutMs": 30000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 20000, + "width": 1280, + "height": 720, + "expectedWidth": 1280, + "expectedHeight": 720, + "fps": 30, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 80, + "maxPacketLoss": null, + "requireStableResolution": true, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": "high", + "screenCodecs": ["h264"], + "expectedScreenCodecs": ["h264"], + "codec": "h264", + "expectedScreenCodec": "video/H264", + "secondaryPublisher": false, + "secondaryPublisherCodec": "h264", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/H264", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 5000000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "C:\\f\\fluxer_desktop\\native\\webrtc-sender\\native-media-reports\\windows-mx450-livekit-h264-single-egress-adaptive-20260605.json", + "verbose": false, + "scenarioName": "codec-h264", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 1280x720", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 6", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 2", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 6", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/H264", + "pass": true + }, + { + "name": "subscriber inbound video codec video/H264", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": "received FPS 6.59 below 28.5; max video frame gap 552ms exceeds 250ms", + "hardwareEncoder": { + "available": false, + "backend": "none", + "compiled": true, + "runtime": false, + "codecs": [], + "zeroCopy": false, + "nativeInputs": [], + "reason": "no_supported_devices", + "detail": "missing NVENC encode session (checked 1 CUDA device; last status NV_ENC_ERR_UNSUPPORTED_DEVICE (2))" + }, + "metrics": { + "inboundAudioFrames": 4315, + "inboundVideoFrames": 136, + "strictAudioFrames": 199, + "strictVideoFrames": 132, + "videoCallbacks": 135, + "videoBytes": 186624000, + "receivedFps": 6.59, + "requiredFps": 28.5, + "maxVideoFrameGapMs": 552, + "maxAudioFrameGapMs": 112, + "maxVideoFrameGapOverallMs": 557, + "maxAudioFrameGapOverallMs": 112, + "avDriftMs": 22, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": { + "1280x720": 132 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 30, + "startedAtMs": 1780696671200, + "stoppedAtMs": null, + "framesPushed": 650, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 64, + "lastPushAtMs": 1780696692841, + "elapsedMs": 21772, + "producedFps": 29.85 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780696672853, + "error": null, + "participants": [ + { + "identity": "fluxer-native-publisher-1272", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_AM3TQzwbFTcoXT", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_AsW4JLnmWG6cce", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_VSxrpLn4EJfUEg", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/H264", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-subscriber-1272", + "trackCount": 0, + "tracks": [] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_7EoucgabpBG8", + "trackSid": "TR_VSxrpLn4EJfUEg", + "width": 1280, + "height": 720, + "timestampUs": 3306206000 + }, + "publisherStats": { + "rttMs": 15, + "outbound": [ + { + "trackSid": "TR_AM3TQzwbFTcoXT", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 95.3, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VSxrpLn4EJfUEg", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 5106.2, + "packetsLost": 0, + "fps": 11, + "codec": "video/H264" + }, + { + "trackSid": "TR_AsW4JLnmWG6cce", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 300.6, + "packetsLost": 0, + "codec": "audio/opus" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 1, + "outgoingVideoFramesProduced": 627, + "outgoingVideoFramesAccepted": 627, + "outgoingVideoFramesDropped": 262, + "outgoingVideoFramesCoalesced": 262, + "outgoingVideoFramesCaptured": 364, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 17.42, + "outgoingVideoTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 35, + "outgoingVideoMaxPushLatencyMs": 42, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 63, + "adaptiveSendTier": "fps15", + "adaptiveSendReason": "encoderEgressPressure" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 18, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_7EoucgabpBG8", + "trackSid": "TR_AsW4JLnmWG6cce", + "kind": "audio", + "bitrateKbps": 296.3, + "packetsLost": 0, + "jitterMs": 6, + "audioLevel": 0.336, + "codec": "audio/opus" + }, + { + "participantSid": "PA_7EoucgabpBG8", + "trackSid": "TR_AM3TQzwbFTcoXT", + "kind": "audio", + "bitrateKbps": 97.1, + "packetsLost": 0, + "jitterMs": 7, + "audioLevel": 0.326, + "codec": "audio/opus" + }, + { + "participantSid": "PA_7EoucgabpBG8", + "trackSid": "TR_VSxrpLn4EJfUEg", + "kind": "video", + "bitrateKbps": 5397.8, + "packetsLost": 0, + "jitterMs": 48, + "codec": "video/H264" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/windows/windows-mx450-livekit-hevc-failfast-20260605.json b/fluxer_desktop/native-media-reports/windows/windows-mx450-livekit-hevc-failfast-20260605.json new file mode 100644 index 0000000..9507172 --- /dev/null +++ b/fluxer_desktop/native-media-reports/windows/windows-mx450-livekit-hevc-failfast-20260605.json @@ -0,0 +1,246 @@ +{ + "status": "fail", + "startedAt": "2026-06-05T22:00:49.164Z", + "endedAt": "2026-06-05T22:00:50.336Z", + "platform": "win32", + "arch": "x64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-3456-3aa0da22-e543-4846-a980-1ef479694d1f", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-3456", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-3456", + "subscriberIdentity": "fluxer-native-subscriber-3456", + "required": true, + "strict": false, + "timeoutMs": 30000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 0, + "width": 640, + "height": 360, + "expectedWidth": 640, + "expectedHeight": 360, + "fps": 15, + "minFpsRatio": 0, + "maxFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxAvDriftMs": 0, + "maxPacketLoss": null, + "requireStableResolution": false, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": null, + "screenCodecs": ["hevc"], + "expectedScreenCodecs": ["hevc"], + "codec": "hevc", + "expectedScreenCodec": "video/H265", + "secondaryPublisher": false, + "secondaryPublisherCodec": "hevc", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/H265", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": true, + "secondaryPublisherScreenSimulcast": true, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": false, + "maxBitrateBps": 400000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": false, + "dataPacket": false, + "subscriptionCycle": false, + "validateServerPublishing": false, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "C:\\f\\fluxer_desktop\\native\\webrtc-sender\\native-media-reports\\windows-mx450-livekit-hevc-failfast-20260605.json", + "verbose": false, + "scenarioName": "codec-hevc", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": false + }, + { + "name": "subscriber remote screenshare subscription", + "pass": false + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": false + }, + { + "name": "subscriber video frame callback", + "pass": false + }, + { + "name": "publisher outbound screenshare stats", + "pass": false + }, + { + "name": "subscriber inbound video stats", + "pass": false + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": false + }, + { + "name": "subscriber video resolution 640x360", + "pass": false + }, + { + "name": "subscriber aggregate inbound audio frames >= 3", + "pass": false + }, + { + "name": "subscriber inbound audio stats count >= 1", + "pass": false + }, + { + "name": "publisher local microphone publication", + "pass": false + }, + { + "name": "subscriber remote microphone subscription", + "pass": false + }, + { + "name": "subscriber inbound audio frames >= 3", + "pass": false + }, + { + "name": "publisher outbound microphone stats", + "pass": false + }, + { + "name": "subscriber inbound audio stats", + "pass": false + }, + { + "name": "publisher outbound screenshare codec video/H265", + "pass": false + }, + { + "name": "subscriber inbound video codec video/H265", + "pass": false + } + ], + "skippedReason": null, + "error": "Error: H.265 publishing requires hardware encoder support; missing NVENC encode session (checked 1 CUDA device; last status NV_ENC_ERR_UNSUPPORTED_DEVICE (2))", + "hardwareEncoder": { + "available": false, + "backend": "none", + "compiled": true, + "runtime": false, + "codecs": [], + "zeroCopy": false, + "nativeInputs": [], + "reason": "no_supported_devices", + "detail": "missing NVENC encode session (checked 1 CUDA device; last status NV_ENC_ERR_UNSUPPORTED_DEVICE (2))" + }, + "metrics": { + "inboundAudioFrames": 0, + "inboundVideoFrames": 0, + "strictAudioFrames": 0, + "strictVideoFrames": 0, + "videoCallbacks": 0, + "videoBytes": 0, + "receivedFps": 0, + "requiredFps": null, + "maxVideoFrameGapMs": 0, + "maxAudioFrameGapMs": 0, + "maxVideoFrameGapOverallMs": 0, + "maxAudioFrameGapOverallMs": 0, + "avDriftMs": null, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": {}, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": null, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": null, + "error": null, + "participants": [] + }, + "lastVideoMeta": null, + "publisherStats": { + "rttMs": null, + "outbound": [], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": null, + "outbound": [], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native-media-reports/windows/windows-mx450-livekit-vp8-360p15-strict-av150-20260605.json b/fluxer_desktop/native-media-reports/windows/windows-mx450-livekit-vp8-360p15-strict-av150-20260605.json new file mode 100644 index 0000000..dd637aa --- /dev/null +++ b/fluxer_desktop/native-media-reports/windows/windows-mx450-livekit-vp8-360p15-strict-av150-20260605.json @@ -0,0 +1,391 @@ +{ + "status": "pass", + "startedAt": "2026-06-05T22:00:19.291Z", + "endedAt": "2026-06-05T22:00:37.423Z", + "platform": "win32", + "arch": "x64", + "config": { + "url": "ws://100.79.83.54:17880", + "serverApiUrl": "http://100.79.83.54:17880", + "apiKey": "devkey", + "room": "fluxer-webrtc-harness-6872-db238db9-c867-4b03-8af9-9740942a674c", + "externalTokens": false, + "publisherIdentity": "fluxer-native-publisher-6872", + "secondaryPublisherIdentity": "fluxer-native-publisher-2-6872", + "subscriberIdentity": "fluxer-native-subscriber-6872", + "required": true, + "strict": true, + "timeoutMs": 30000, + "connectTimeoutMs": 30000, + "disconnectTimeoutMs": 10000, + "probeTimeoutMs": 1500, + "durationMs": 15000, + "width": 640, + "height": 360, + "expectedWidth": 640, + "expectedHeight": 360, + "fps": 15, + "minFpsRatio": 0.95, + "maxFrameGapMs": 250, + "maxAudioFrameGapMs": 250, + "maxAvDriftMs": 150, + "maxPacketLoss": null, + "requireStableResolution": true, + "videoPattern": "gradient", + "videoInput": "bgra", + "subscriberVideoQuality": "high", + "screenCodecs": ["vp8"], + "expectedScreenCodecs": ["vp8"], + "codec": "vp8", + "expectedScreenCodec": "video/VP8", + "secondaryPublisher": false, + "secondaryPublisherCodec": "vp8", + "secondaryPublisherCodecExplicit": false, + "expectedSecondaryPublisherCodec": "video/VP8", + "expectedSecondaryPublisherCodecExplicit": false, + "screenSimulcast": false, + "secondaryPublisherScreenSimulcast": false, + "adaptiveSend": true, + "minVideoFps": 15, + "minResolutionScale": 0.5, + "maxAudioBufferMs": 750, + "secondaryPublisherMicrophone": true, + "secondaryPublisherScreenAudio": true, + "maxBitrateBps": 1200000, + "sampleRate": 48000, + "channels": 1, + "audioChunkMs": 20, + "microphone": true, + "screenAudio": true, + "dataPacket": true, + "subscriptionCycle": true, + "validateServerPublishing": true, + "camera": false, + "cameraDeviceId": null, + "expectedCameraCodec": null, + "expectedHardwareEncoder": null, + "reportPath": "C:\\f\\fluxer_desktop\\native\\webrtc-sender\\native-media-reports\\windows-mx450-livekit-vp8-360p15-strict-av150-20260605.json", + "verbose": false, + "scenarioName": "codec-vp8", + "apiSecret": "", + "e2eeKey": null, + "publisherToken": null, + "secondaryPublisherToken": null, + "subscriberToken": null + }, + "checks": [ + { + "name": "publisher connected", + "pass": true + }, + { + "name": "subscriber connected", + "pass": true + }, + { + "name": "publisher local screenshare publication", + "pass": true + }, + { + "name": "subscriber remote screenshare subscription", + "pass": true + }, + { + "name": "subscriber inbound video frames >= 3", + "pass": true + }, + { + "name": "subscriber video frame callback", + "pass": true + }, + { + "name": "publisher outbound screenshare stats", + "pass": true + }, + { + "name": "subscriber inbound video stats", + "pass": true + }, + { + "name": "subscriber inbound video stats count >= 1", + "pass": true + }, + { + "name": "subscriber video resolution 640x360", + "pass": true + }, + { + "name": "subscriber aggregate inbound audio frames >= 6", + "pass": true + }, + { + "name": "subscriber inbound audio stats count >= 2", + "pass": true + }, + { + "name": "publisher local microphone publication", + "pass": true + }, + { + "name": "subscriber remote microphone subscription", + "pass": true + }, + { + "name": "subscriber inbound audio frames >= 6", + "pass": true + }, + { + "name": "publisher outbound microphone stats", + "pass": true + }, + { + "name": "subscriber inbound audio stats", + "pass": true + }, + { + "name": "publisher local screen-share audio publication", + "pass": true + }, + { + "name": "subscriber remote screen-share audio subscription", + "pass": true + }, + { + "name": "publisher outbound screen-share audio stats", + "pass": true + }, + { + "name": "subscriber data packet received", + "pass": true + }, + { + "name": "publisher outbound screenshare codec video/VP8", + "pass": true + }, + { + "name": "subscriber inbound video codec video/VP8", + "pass": true + }, + { + "name": "server publishing API participants listed", + "pass": true + }, + { + "name": "server sees publisher screenshare publication", + "pass": true + }, + { + "name": "server sees publisher microphone publication", + "pass": true + }, + { + "name": "server sees publisher screen-share audio publication", + "pass": true + } + ], + "skippedReason": null, + "error": null, + "hardwareEncoder": { + "available": false, + "backend": "none", + "compiled": true, + "runtime": false, + "codecs": [], + "zeroCopy": false, + "nativeInputs": [], + "reason": "no_supported_devices", + "detail": "missing NVENC encode session (checked 1 CUDA device; last status NV_ENC_ERR_UNSUPPORTED_DEVICE (2))" + }, + "metrics": { + "inboundAudioFrames": 3201, + "inboundVideoFrames": 236, + "strictAudioFrames": 150, + "strictVideoFrames": 221, + "videoCallbacks": 235, + "videoBytes": 81216000, + "receivedFps": 14.71, + "requiredFps": 14.25, + "maxVideoFrameGapMs": 135, + "maxAudioFrameGapMs": 104, + "maxVideoFrameGapOverallMs": 135, + "maxAudioFrameGapOverallMs": 104, + "avDriftMs": 15, + "maxObservedPacketLoss": 0, + "maxObservedPacketLossDelta": 0, + "videoResolutionCounts": { + "640x360": 221 + }, + "videoResolutionMismatchCount": 0, + "droppedVideoFrameCallbacks": 0, + "publisherVideoPump": { + "input": "bgra", + "pattern": "gradient", + "targetFps": 15, + "startedAtMs": 1780696821343, + "stoppedAtMs": null, + "framesPushed": 242, + "framesRejected": 0, + "errors": 0, + "maxPushGapMs": 81, + "lastPushAtMs": 1780696837414, + "elapsedMs": 16181, + "producedFps": 14.96 + }, + "secondaryPublisherVideoPump": null, + "serverPublishing": { + "fetchedAtMs": 1780696822415, + "error": null, + "participants": [ + { + "identity": "fluxer-native-publisher-6872", + "trackCount": 3, + "tracks": [ + { + "sid": "TR_AMZncs3mMUV8XL", + "name": "mic", + "kind": "audio", + "source": "microphone", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_As8q693K2N6RV2", + "name": "screen-audio", + "kind": "audio", + "source": "screenshareAudio", + "mimeType": "audio/red", + "muted": false + }, + { + "sid": "TR_VScEzJLWokWJcx", + "name": "screen", + "kind": "video", + "source": "screenshare", + "mimeType": "video/VP8", + "muted": false + } + ] + }, + { + "identity": "fluxer-native-subscriber-6872", + "trackCount": 0, + "tracks": [] + } + ] + }, + "lastVideoMeta": { + "participantSid": "PA_EeCfpcRvJPEF", + "trackSid": "TR_VScEzJLWokWJcx", + "width": 640, + "height": 360, + "timestampUs": 3450776000 + }, + "publisherStats": { + "rttMs": 4, + "outbound": [ + { + "trackSid": "TR_As8q693K2N6RV2", + "source": "screenshareAudio", + "kind": "audio", + "bitrateKbps": 263.5, + "packetsLost": 0, + "codec": "audio/opus" + }, + { + "trackSid": "TR_VScEzJLWokWJcx", + "source": "screenshare", + "kind": "video", + "bitrateKbps": 1233.4, + "packetsLost": 0, + "fps": 15, + "codec": "video/VP8" + }, + { + "trackSid": "TR_AMZncs3mMUV8XL", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 97.9, + "packetsLost": 0, + "codec": "audio/opus" + } + ], + "inbound": [], + "send": { + "outgoingVideoQueueDepth": 1, + "outgoingVideoFramesProduced": 241, + "outgoingVideoFramesAccepted": 241, + "outgoingVideoFramesDropped": 4, + "outgoingVideoFramesCoalesced": 4, + "outgoingVideoFramesCaptured": 236, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 14.71, + "outgoingVideoTargetFps": 15, + "outgoingVideoMaxQueueAgeMs": 67, + "outgoingVideoMaxPushLatencyMs": 69, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 36, + "adaptiveSendTier": "full", + "adaptiveSendReason": "stable" + } + }, + "secondaryPublisherStats": null, + "subscriberStats": { + "rttMs": 4, + "outbound": [], + "inbound": [ + { + "participantSid": "PA_EeCfpcRvJPEF", + "trackSid": "TR_As8q693K2N6RV2", + "kind": "audio", + "bitrateKbps": 263.1, + "packetsLost": 0, + "jitterMs": 3, + "audioLevel": 0.318, + "codec": "audio/opus" + }, + { + "participantSid": "PA_EeCfpcRvJPEF", + "trackSid": "TR_AMZncs3mMUV8XL", + "kind": "audio", + "bitrateKbps": 97.8, + "packetsLost": 0, + "jitterMs": 2, + "audioLevel": 0.307, + "codec": "audio/opus" + }, + { + "participantSid": "PA_EeCfpcRvJPEF", + "trackSid": "TR_VScEzJLWokWJcx", + "kind": "video", + "bitrateKbps": 1231.8, + "packetsLost": 0, + "jitterMs": 4, + "codec": "video/VP8" + } + ], + "send": { + "outgoingVideoQueueDepth": 0, + "outgoingVideoFramesProduced": 0, + "outgoingVideoFramesAccepted": 0, + "outgoingVideoFramesDropped": 0, + "outgoingVideoFramesCoalesced": 0, + "outgoingVideoFramesCaptured": 0, + "outgoingVideoCaptureFailures": 0, + "outgoingVideoEffectiveFps": 0, + "outgoingVideoTargetFps": 0, + "outgoingVideoMaxQueueAgeMs": 0, + "outgoingVideoMaxPushLatencyMs": 0, + "outgoingAudioBufferTargetMs": 200, + "outgoingAudioBufferMaxMs": 750, + "outgoingAudioUnderruns": 0, + "outgoingAudioRebuffers": 0, + "outgoingAudioMaxFrameGapMs": 0, + "adaptiveSendTier": "idle", + "adaptiveSendReason": "notPublishing" + } + } + } +} diff --git a/fluxer_desktop/native/.cargo/config.toml b/fluxer_desktop/native/.cargo/config.toml new file mode 100644 index 0000000..4d136d5 --- /dev/null +++ b/fluxer_desktop/native/.cargo/config.toml @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +# +# Statically link the Microsoft C/C++ runtime into every native Rust addon we +# ship on Windows. Without this, the .node imports VCRUNTIME140.dll and the +# msvcp/msvcr family, which require the VC++ 2015-2022 redistributable to be +# installed system-wide. Fresh Windows boxes (and Windows IoT / LTSC variants) +# don't have it, so dlopen fails with ERR_DLOPEN_FAILED "The specified module +# could not be found." even though the .node file itself is present. +# +# +crt-static makes the runtime part of the .node itself. Universal CRT API +# sets (api-ms-win-crt-*.dll) are guaranteed present on Windows 10+ and remain +# dynamic; only the VC++-specific layer goes static. +# +# Scoped per-target so Linux and macOS builds are unaffected. Cargo walks +# parent directories looking for `.cargo/config.toml`, so this file applies to +# every crate under `fluxer_desktop/native/` even though each addon has its +# own `[workspace]`. + +[target.x86_64-pc-windows-msvc] +rustflags = ["-C", "target-feature=+crt-static"] + +[target.aarch64-pc-windows-msvc] +rustflags = ["-C", "target-feature=+crt-static"] + +# 32-bit injected game-capture hook/layer (for capturing 32-bit games from the +# 64-bit app). Same rationale: the DLL is LoadLibrary'd into an arbitrary game +# process that may not have the 32-bit VC++ redistributable, so the runtime +# must be static. +[target.i686-pc-windows-msvc] +rustflags = ["-C", "target-feature=+crt-static"] diff --git a/fluxer_desktop/native/audio-apm/Cargo.lock b/fluxer_desktop/native/audio-apm/Cargo.lock new file mode 100644 index 0000000..036f4a9 --- /dev/null +++ b/fluxer_desktop/native/audio-apm/Cargo.lock @@ -0,0 +1,309 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "autotools" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef941527c41b0fc0dd48511a8154cd5fc7e29200a0ff8b7203c5d777dbc795cf" +dependencies = [ + "cc", +] + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex 1.3.0", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "cc" +version = "1.2.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +dependencies = [ + "find-msvc-tools", + "shlex 2.0.1", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fluxer_audio_apm" +version = "0.0.0" +dependencies = [ + "webrtc-audio-processing", + "webrtc-audio-processing-config", +] + +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "webrtc-audio-processing" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "190c57ced88d6307ba91778c245265a99f99d900a9239c87a1b15142330c9f7e" +dependencies = [ + "webrtc-audio-processing-config", + "webrtc-audio-processing-sys", +] + +[[package]] +name = "webrtc-audio-processing-config" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142cbc598e3dcf1a71e90109b6169ca9ce28ec1910bf74eef30f3c420ce850a4" + +[[package]] +name = "webrtc-audio-processing-sys" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8f6c2b4b20a03b165378172f0dbe23c49b2630b70091026589afbe04e79cd3e" +dependencies = [ + "anyhow", + "autotools", + "bindgen", + "cc", + "fs_extra", + "pkg-config", + "regex", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" diff --git a/fluxer_desktop/native/audio-apm/Cargo.toml b/fluxer_desktop/native/audio-apm/Cargo.toml new file mode 100644 index 0000000..f9863a7 --- /dev/null +++ b/fluxer_desktop/native/audio-apm/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "fluxer_audio_apm" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["rlib"] + +[features] +default = [] +real-apm = [ + "dep:webrtc-audio-processing", + "dep:webrtc-audio-processing-config", + "webrtc-audio-processing/bundled", +] + +[dependencies] +webrtc-audio-processing = {version = "2.1", optional = true} +webrtc-audio-processing-config = {version = "2.1", optional = true} + +[dev-dependencies] diff --git a/fluxer_desktop/native/audio-apm/scripts/build-real-apm.sh b/fluxer_desktop/native/audio-apm/scripts/build-real-apm.sh new file mode 100755 index 0000000..e6281a2 --- /dev/null +++ b/fluxer_desktop/native/audio-apm/scripts/build-real-apm.sh @@ -0,0 +1,67 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: AGPL-3.0-or-later + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CRATE_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" + +require_pkg_config() { + if ! command -v pkg-config >/dev/null 2>&1; then + echo "error: pkg-config is required but was not found in PATH." >&2 + echo " Install via: brew install pkg-config (macOS)" >&2 + echo " apt-get install pkg-config (Debian/Ubuntu)" >&2 + exit 1 + fi +} + +assert_webrtc_pkgconfig() { + if ! pkg-config --exists webrtc-audio-processing-2; then + echo "error: pkg-config cannot locate webrtc-audio-processing-2." >&2 + echo " See fluxer_desktop/native/audio-apm/README.md for install steps." >&2 + exit 1 + fi +} + +assert_absl_pkgconfig() { + if ! pkg-config --exists absl_base; then + echo "error: pkg-config cannot locate absl_base." >&2 + echo " Install via: brew install abseil (macOS)" >&2 + echo " apt-get install libabsl-dev (Debian/Ubuntu)" >&2 + exit 1 + fi +} + +inject_absl_include_path() { + local absl_includes + absl_includes="$(pkg-config --cflags-only-I absl_base)" + if [[ -z "${absl_includes}" ]]; then + echo "error: pkg-config returned no include path for absl_base." >&2 + exit 1 + fi + + local prev_cxxflags="${CXXFLAGS-}" + local prev_bindgen="${BINDGEN_EXTRA_CLANG_ARGS-}" + + export CXXFLAGS="${absl_includes} ${prev_cxxflags}" + export BINDGEN_EXTRA_CLANG_ARGS="${absl_includes} ${prev_bindgen}" +} + +main() { + cd "${CRATE_DIR}" + + local cargo_subcmd="build" + if [[ $# -gt 0 && "$1" != -* && "$1" != "--" ]]; then + cargo_subcmd="$1" + shift + fi + + require_pkg_config + assert_webrtc_pkgconfig + assert_absl_pkgconfig + inject_absl_include_path + + exec cargo "${cargo_subcmd}" --features real-apm "$@" +} + +main "$@" diff --git a/fluxer_desktop/native/audio-apm/src/lib.rs b/fluxer_desktop/native/audio-apm/src/lib.rs new file mode 100644 index 0000000..8bbf7b0 --- /dev/null +++ b/fluxer_desktop/native/audio-apm/src/lib.rs @@ -0,0 +1,972 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![deny(clippy::too_many_lines)] +#![deny(clippy::unwrap_used)] + +pub const APM_FRAME_MS: u32 = 10; +pub const APM_MAX_SAMPLE_RATE: u32 = 48_000; +pub const APM_MAX_CHANNELS: u16 = 2; +pub const APM_MAX_FRAME_SAMPLES: usize = + (APM_FRAME_MS as usize) * (APM_MAX_SAMPLE_RATE as usize) / 1000; + +pub const APM_MIN_SAMPLE_RATE: u32 = 8_000; +pub const APM_MIN_CHANNELS: u16 = 1; + +const _: () = assert!(APM_MAX_FRAME_SAMPLES == 480); +const _: () = assert!(APM_MIN_SAMPLE_RATE <= APM_MAX_SAMPLE_RATE); +const _: () = assert!(APM_MIN_CHANNELS <= APM_MAX_CHANNELS); + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum ApmError { + SampleRateOutOfRange { + sample_rate_hz: u32, + }, + SampleRateMismatch { + expected_hz: u32, + observed_hz: u32, + }, + ChannelsOutOfRange { + channels: u16, + }, + ChannelsMismatch { + expected: u16, + observed: u16, + }, + FrameLengthMismatch { + expected_samples: usize, + observed_samples: usize, + }, + NotInitialized, + BackendUnavailable, +} + +impl core::fmt::Display for ApmError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + ApmError::SampleRateOutOfRange { sample_rate_hz } => write!( + f, + "sample rate {sample_rate_hz} hz outside [{APM_MIN_SAMPLE_RATE}, {APM_MAX_SAMPLE_RATE}]", + ), + ApmError::SampleRateMismatch { + expected_hz, + observed_hz, + } => write!( + f, + "sample rate mismatch: expected={expected_hz} observed={observed_hz}", + ), + ApmError::ChannelsOutOfRange { channels } => write!( + f, + "channel count {channels} outside [{APM_MIN_CHANNELS}, {APM_MAX_CHANNELS}]", + ), + ApmError::ChannelsMismatch { expected, observed } => write!( + f, + "channels mismatch: expected={expected} observed={observed}", + ), + ApmError::FrameLengthMismatch { + expected_samples, + observed_samples, + } => write!( + f, + "frame length mismatch: expected={expected_samples} observed={observed_samples}", + ), + ApmError::NotInitialized => write!(f, "audio processor not initialized"), + ApmError::BackendUnavailable => write!(f, "real APM backend unavailable"), + } + } +} + +impl std::error::Error for ApmError {} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct AecMetrics { + pub echo_return_loss_db: f32, + pub echo_return_loss_enhancement_db: f32, + pub delay_ms: i32, +} + +impl AecMetrics { + pub const NEUTRAL: AecMetrics = AecMetrics { + echo_return_loss_db: 0.0, + echo_return_loss_enhancement_db: 0.0, + delay_ms: 0, + }; +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct ApmReport { + pub aec_metrics: AecMetrics, + pub voice_detected: bool, + pub level_dbfs: f32, +} + +impl ApmReport { + pub const NEUTRAL: ApmReport = ApmReport { + aec_metrics: AecMetrics::NEUTRAL, + voice_detected: false, + level_dbfs: -120.0, + }; +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct ApmConfig { + pub aec_enabled: bool, + pub ns_enabled: bool, + pub agc_enabled: bool, + pub aec_mobile_mode: bool, + pub target_level_dbfs: i32, +} + +impl Default for ApmConfig { + fn default() -> Self { + ApmConfig { + aec_enabled: true, + ns_enabled: true, + agc_enabled: true, + aec_mobile_mode: false, + target_level_dbfs: -3, + } + } +} + +pub struct ApmConfigBuilder { + config: ApmConfig, +} + +impl ApmConfigBuilder { + pub fn new() -> Self { + ApmConfigBuilder { + config: ApmConfig::default(), + } + } + + pub fn aec(mut self, enabled: bool) -> Self { + self.config.aec_enabled = enabled; + self + } + + pub fn ns(mut self, enabled: bool) -> Self { + self.config.ns_enabled = enabled; + self + } + + pub fn agc(mut self, enabled: bool) -> Self { + self.config.agc_enabled = enabled; + self + } + + pub fn aec_mobile_mode(mut self, enabled: bool) -> Self { + self.config.aec_mobile_mode = enabled; + self + } + + pub fn target_level_dbfs(mut self, target: i32) -> Self { + self.config.target_level_dbfs = target; + self + } + + pub fn build(self) -> ApmConfig { + assert!(self.config.target_level_dbfs <= 0); + assert!(self.config.target_level_dbfs >= -60); + self.config + } +} + +impl Default for ApmConfigBuilder { + fn default() -> Self { + ApmConfigBuilder::new() + } +} + +pub trait AudioProcessor: Send { + fn process_capture_frame( + &mut self, + samples: &mut [i16], + sample_rate_hz: u32, + channels: u16, + ) -> Result; + + fn process_render_frame( + &mut self, + samples: &[i16], + sample_rate_hz: u32, + channels: u16, + ) -> Result<(), ApmError>; + + fn reset(&mut self) -> Result<(), ApmError>; +} + +pub fn expected_frame_samples(sample_rate_hz: u32, channels: u16) -> usize { + assert!(sample_rate_hz >= APM_MIN_SAMPLE_RATE); + assert!(sample_rate_hz <= APM_MAX_SAMPLE_RATE); + assert!(channels >= APM_MIN_CHANNELS); + assert!(channels <= APM_MAX_CHANNELS); + let per_channel = (APM_FRAME_MS as usize) * (sample_rate_hz as usize) / 1000; + per_channel * (channels as usize) +} + +pub(crate) fn validate_frame_shape( + samples_len: usize, + sample_rate_hz: u32, + channels: u16, + expected_sample_rate_hz: u32, + expected_channels: u16, +) -> Result<(), ApmError> { + assert!(expected_sample_rate_hz >= APM_MIN_SAMPLE_RATE); + assert!(expected_sample_rate_hz <= APM_MAX_SAMPLE_RATE); + if !(APM_MIN_SAMPLE_RATE..=APM_MAX_SAMPLE_RATE).contains(&sample_rate_hz) { + return Err(ApmError::SampleRateOutOfRange { sample_rate_hz }); + } + if !(APM_MIN_CHANNELS..=APM_MAX_CHANNELS).contains(&channels) { + return Err(ApmError::ChannelsOutOfRange { channels }); + } + if sample_rate_hz != expected_sample_rate_hz { + return Err(ApmError::SampleRateMismatch { + expected_hz: expected_sample_rate_hz, + observed_hz: sample_rate_hz, + }); + } + if channels != expected_channels { + return Err(ApmError::ChannelsMismatch { + expected: expected_channels, + observed: channels, + }); + } + let expected_samples = expected_frame_samples(sample_rate_hz, channels); + if samples_len != expected_samples { + return Err(ApmError::FrameLengthMismatch { + expected_samples, + observed_samples: samples_len, + }); + } + Ok(()) +} + +#[derive(Debug)] +pub struct StubAudioProcessor { + config: ApmConfig, + expected_sample_rate_hz: u32, + expected_channels: u16, + capture_frames_processed: u64, + render_frames_processed: u64, +} + +impl StubAudioProcessor { + pub fn new(config: ApmConfig, sample_rate_hz: u32, channels: u16) -> Result { + if !(APM_MIN_SAMPLE_RATE..=APM_MAX_SAMPLE_RATE).contains(&sample_rate_hz) { + return Err(ApmError::SampleRateOutOfRange { sample_rate_hz }); + } + if !(APM_MIN_CHANNELS..=APM_MAX_CHANNELS).contains(&channels) { + return Err(ApmError::ChannelsOutOfRange { channels }); + } + assert!(sample_rate_hz >= APM_MIN_SAMPLE_RATE); + assert!(channels >= APM_MIN_CHANNELS); + Ok(StubAudioProcessor { + config, + expected_sample_rate_hz: sample_rate_hz, + expected_channels: channels, + capture_frames_processed: 0, + render_frames_processed: 0, + }) + } + + pub fn config(&self) -> ApmConfig { + self.config + } + + pub fn capture_frames_processed(&self) -> u64 { + self.capture_frames_processed + } + + pub fn render_frames_processed(&self) -> u64 { + self.render_frames_processed + } +} + +impl AudioProcessor for StubAudioProcessor { + fn process_capture_frame( + &mut self, + samples: &mut [i16], + sample_rate_hz: u32, + channels: u16, + ) -> Result { + assert!(self.expected_sample_rate_hz >= APM_MIN_SAMPLE_RATE); + assert!(self.expected_channels >= APM_MIN_CHANNELS); + validate_frame_shape( + samples.len(), + sample_rate_hz, + channels, + self.expected_sample_rate_hz, + self.expected_channels, + )?; + assert!(self.capture_frames_processed < u64::MAX); + self.capture_frames_processed = self.capture_frames_processed.saturating_add(1); + Ok(ApmReport::NEUTRAL) + } + + fn process_render_frame( + &mut self, + samples: &[i16], + sample_rate_hz: u32, + channels: u16, + ) -> Result<(), ApmError> { + assert!(self.expected_sample_rate_hz >= APM_MIN_SAMPLE_RATE); + assert!(self.expected_channels >= APM_MIN_CHANNELS); + validate_frame_shape( + samples.len(), + sample_rate_hz, + channels, + self.expected_sample_rate_hz, + self.expected_channels, + )?; + assert!(self.render_frames_processed < u64::MAX); + self.render_frames_processed = self.render_frames_processed.saturating_add(1); + Ok(()) + } + + fn reset(&mut self) -> Result<(), ApmError> { + assert!(self.expected_sample_rate_hz >= APM_MIN_SAMPLE_RATE); + assert!(self.expected_channels >= APM_MIN_CHANNELS); + self.capture_frames_processed = 0; + self.render_frames_processed = 0; + Ok(()) + } +} + +#[cfg(feature = "real-apm")] +mod real { + use super::{ + APM_MAX_CHANNELS, APM_MAX_FRAME_SAMPLES, APM_MAX_SAMPLE_RATE, APM_MIN_CHANNELS, + APM_MIN_SAMPLE_RATE, AecMetrics, ApmConfig, ApmError, ApmReport, AudioProcessor, + validate_frame_shape, + }; + use webrtc_audio_processing::{Processor, Stats}; + use webrtc_audio_processing_config::{ + Config, EchoCanceller, GainController, GainController1, GainControllerMode, HighPassFilter, + NoiseSuppression, NoiseSuppressionLevel, + }; + + const I16_TO_F32_SCALE: f32 = 1.0 / 32768.0; + const F32_TO_I16_SCALE: f32 = 32767.0; + + #[derive(Debug)] + pub struct WebRtcAudioProcessor { + inner: Processor, + config: ApmConfig, + expected_sample_rate_hz: u32, + expected_channels: u16, + samples_per_channel: usize, + capture_channels: [[f32; APM_MAX_FRAME_SAMPLES]; APM_MAX_CHANNELS as usize], + render_channels: [[f32; APM_MAX_FRAME_SAMPLES]; APM_MAX_CHANNELS as usize], + capture_frames_processed: u64, + render_frames_processed: u64, + } + + impl WebRtcAudioProcessor { + pub fn new( + config: ApmConfig, + sample_rate_hz: u32, + channels: u16, + ) -> Result { + if !(APM_MIN_SAMPLE_RATE..=APM_MAX_SAMPLE_RATE).contains(&sample_rate_hz) { + return Err(ApmError::SampleRateOutOfRange { sample_rate_hz }); + } + if !(APM_MIN_CHANNELS..=APM_MAX_CHANNELS).contains(&channels) { + return Err(ApmError::ChannelsOutOfRange { channels }); + } + assert!(sample_rate_hz >= APM_MIN_SAMPLE_RATE); + assert!(channels >= APM_MIN_CHANNELS); + let inner = Processor::new(sample_rate_hz).map_err(|_| ApmError::BackendUnavailable)?; + inner.set_config(build_webrtc_config(config)); + let samples_per_channel = inner.num_samples_per_frame(); + assert!(samples_per_channel <= APM_MAX_FRAME_SAMPLES); + assert!(samples_per_channel > 0); + Ok(WebRtcAudioProcessor { + inner, + config, + expected_sample_rate_hz: sample_rate_hz, + expected_channels: channels, + samples_per_channel, + capture_channels: [[0.0; APM_MAX_FRAME_SAMPLES]; APM_MAX_CHANNELS as usize], + render_channels: [[0.0; APM_MAX_FRAME_SAMPLES]; APM_MAX_CHANNELS as usize], + capture_frames_processed: 0, + render_frames_processed: 0, + }) + } + + pub fn config(&self) -> ApmConfig { + self.config + } + + pub fn capture_frames_processed(&self) -> u64 { + self.capture_frames_processed + } + + pub fn render_frames_processed(&self) -> u64 { + self.render_frames_processed + } + + pub fn samples_per_channel(&self) -> usize { + self.samples_per_channel + } + } + + fn build_webrtc_config(config: ApmConfig) -> Config { + let echo_canceller = if !config.aec_enabled { + None + } else if config.aec_mobile_mode { + Some(EchoCanceller::Mobile { stream_delay_ms: 0 }) + } else { + Some(EchoCanceller::Full { + stream_delay_ms: None, + }) + }; + let noise_suppression = if config.ns_enabled { + Some(NoiseSuppression { + level: NoiseSuppressionLevel::Moderate, + analyze_linear_aec_output: false, + }) + } else { + None + }; + let gain_controller = if config.agc_enabled { + Some(gain_controller_from(config.target_level_dbfs)) + } else { + None + }; + Config { + pipeline: Default::default(), + capture_amplifier: None, + high_pass_filter: Some(HighPassFilter { + apply_in_full_band: true, + }), + echo_canceller, + noise_suppression, + gain_controller, + } + } + + fn gain_controller_from(target_level_dbfs: i32) -> GainController { + assert!(target_level_dbfs <= 0); + assert!(target_level_dbfs >= -60); + let target_clamped = (-target_level_dbfs).clamp(0, 31) as u8; + GainController::GainController1(GainController1 { + mode: GainControllerMode::AdaptiveDigital, + target_level_dbfs: target_clamped, + compression_gain_db: 9, + enable_limiter: true, + analog_gain_controller: None, + }) + } + + fn deinterleave_i16( + samples: &[i16], + channels: usize, + samples_per_channel: usize, + out: &mut [[f32; APM_MAX_FRAME_SAMPLES]; APM_MAX_CHANNELS as usize], + ) { + assert!(channels >= 1); + assert!(channels <= APM_MAX_CHANNELS as usize); + assert!(samples_per_channel <= APM_MAX_FRAME_SAMPLES); + assert!(samples.len() == channels * samples_per_channel); + for (sample_index, frame) in samples.chunks_exact(channels).enumerate() { + for (channel_index, &value) in frame.iter().enumerate() { + out[channel_index][sample_index] = (value as f32) * I16_TO_F32_SCALE; + } + } + } + + fn interleave_to_i16( + channels_data: &[[f32; APM_MAX_FRAME_SAMPLES]; APM_MAX_CHANNELS as usize], + channels: usize, + samples_per_channel: usize, + out: &mut [i16], + ) { + assert!(channels >= 1); + assert!(channels <= APM_MAX_CHANNELS as usize); + assert!(samples_per_channel <= APM_MAX_FRAME_SAMPLES); + assert!(out.len() == channels * samples_per_channel); + for (sample_index, frame) in out.chunks_exact_mut(channels).enumerate() { + for (channel_index, slot) in frame.iter_mut().enumerate() { + let value = channels_data[channel_index][sample_index]; + let scaled = (value * F32_TO_I16_SCALE).clamp(i16::MIN as f32, i16::MAX as f32); + *slot = scaled as i16; + } + } + } + + fn peak_level_dbfs(samples: &[i16]) -> f32 { + assert!(!samples.is_empty()); + let mut peak_abs: i32 = 0; + for &sample in samples { + let abs_value = (sample as i32).unsigned_abs() as i32; + if abs_value > peak_abs { + peak_abs = abs_value; + } + } + if peak_abs == 0 { + return -120.0; + } + let normalised = (peak_abs as f32) * I16_TO_F32_SCALE; + 20.0 * normalised.log10() + } + + fn report_from(stats: Stats, level_dbfs: f32) -> ApmReport { + let erl = stats.echo_return_loss.unwrap_or(0.0) as f32; + let erle = stats.echo_return_loss_enhancement.unwrap_or(0.0) as f32; + let delay_ms = stats.delay_ms.unwrap_or(0) as i32; + ApmReport { + aec_metrics: AecMetrics { + echo_return_loss_db: erl, + echo_return_loss_enhancement_db: erle, + delay_ms, + }, + voice_detected: level_dbfs > -50.0, + level_dbfs, + } + } + + impl AudioProcessor for WebRtcAudioProcessor { + fn process_capture_frame( + &mut self, + samples: &mut [i16], + sample_rate_hz: u32, + channels: u16, + ) -> Result { + assert!(self.expected_sample_rate_hz >= APM_MIN_SAMPLE_RATE); + assert!(self.expected_channels >= APM_MIN_CHANNELS); + validate_frame_shape( + samples.len(), + sample_rate_hz, + channels, + self.expected_sample_rate_hz, + self.expected_channels, + )?; + let channels_usize = channels as usize; + let per_channel = self.samples_per_channel; + deinterleave_i16( + samples, + channels_usize, + per_channel, + &mut self.capture_channels, + ); + let mut view: [&mut [f32]; APM_MAX_CHANNELS as usize] = { + let (first, second) = self.capture_channels.split_at_mut(1); + [&mut first[0][..per_channel], &mut second[0][..per_channel]] + }; + let frame = &mut view[..channels_usize]; + self.inner + .process_capture_frame(frame) + .map_err(|_| ApmError::BackendUnavailable)?; + interleave_to_i16(&self.capture_channels, channels_usize, per_channel, samples); + let level_dbfs = peak_level_dbfs(samples); + let stats = self.inner.get_stats(); + self.capture_frames_processed = self.capture_frames_processed.saturating_add(1); + Ok(report_from(stats, level_dbfs)) + } + + fn process_render_frame( + &mut self, + samples: &[i16], + sample_rate_hz: u32, + channels: u16, + ) -> Result<(), ApmError> { + assert!(self.expected_sample_rate_hz >= APM_MIN_SAMPLE_RATE); + assert!(self.expected_channels >= APM_MIN_CHANNELS); + validate_frame_shape( + samples.len(), + sample_rate_hz, + channels, + self.expected_sample_rate_hz, + self.expected_channels, + )?; + let channels_usize = channels as usize; + let per_channel = self.samples_per_channel; + deinterleave_i16( + samples, + channels_usize, + per_channel, + &mut self.render_channels, + ); + let mut view: [&mut [f32]; APM_MAX_CHANNELS as usize] = { + let (first, second) = self.render_channels.split_at_mut(1); + [&mut first[0][..per_channel], &mut second[0][..per_channel]] + }; + let frame = &mut view[..channels_usize]; + self.inner + .process_render_frame(frame) + .map_err(|_| ApmError::BackendUnavailable)?; + self.render_frames_processed = self.render_frames_processed.saturating_add(1); + Ok(()) + } + + fn reset(&mut self) -> Result<(), ApmError> { + assert!(self.expected_sample_rate_hz >= APM_MIN_SAMPLE_RATE); + assert!(self.expected_channels >= APM_MIN_CHANNELS); + self.inner.reinitialize(); + self.capture_frames_processed = 0; + self.render_frames_processed = 0; + Ok(()) + } + } +} + +#[cfg(feature = "real-apm")] +pub use real::WebRtcAudioProcessor; + +#[cfg(test)] +mod tests { + use super::*; + + fn make_frame(sample_rate_hz: u32, channels: u16) -> Vec { + let n = expected_frame_samples(sample_rate_hz, channels); + (0..n).map(|i| (i as i16).wrapping_mul(7)).collect() + } + + #[test] + fn frame_samples_constant_matches_formula() { + assert_eq!(APM_MAX_FRAME_SAMPLES, 480); + assert_eq!(expected_frame_samples(48_000, 1), 480); + assert_eq!(expected_frame_samples(48_000, 2), 960); + assert_eq!(expected_frame_samples(16_000, 1), 160); + } + + #[test] + fn stub_capture_does_not_modify_samples() { + let config = ApmConfigBuilder::new().build(); + let mut stub = StubAudioProcessor::new(config, 48_000, 1).expect("ctor"); + let original = make_frame(48_000, 1); + let mut samples = original.clone(); + let report = stub + .process_capture_frame(&mut samples, 48_000, 1) + .expect("ok"); + assert_eq!(samples, original); + assert_eq!(report, ApmReport::NEUTRAL); + } + + #[test] + fn stub_report_has_neutral_metrics() { + let config = ApmConfig::default(); + let mut stub = StubAudioProcessor::new(config, 48_000, 1).expect("ctor"); + let mut samples = make_frame(48_000, 1); + let report = stub + .process_capture_frame(&mut samples, 48_000, 1) + .expect("ok"); + assert_eq!(report.aec_metrics.echo_return_loss_db, 0.0); + assert_eq!(report.aec_metrics.echo_return_loss_enhancement_db, 0.0); + assert_eq!(report.aec_metrics.delay_ms, 0); + assert!(!report.voice_detected); + assert_eq!(report.level_dbfs, -120.0); + } + + #[test] + fn rejects_mismatched_sample_rate() { + let mut stub = StubAudioProcessor::new(ApmConfig::default(), 48_000, 1).expect("ctor"); + let mut samples = make_frame(16_000, 1); + let err = stub + .process_capture_frame(&mut samples, 16_000, 1) + .expect_err("err"); + assert!(matches!(err, ApmError::SampleRateMismatch { .. })); + } + + #[test] + fn rejects_mismatched_channels() { + let mut stub = StubAudioProcessor::new(ApmConfig::default(), 48_000, 1).expect("ctor"); + let mut samples = make_frame(48_000, 2); + let err = stub + .process_capture_frame(&mut samples, 48_000, 2) + .expect_err("err"); + assert!(matches!(err, ApmError::ChannelsMismatch { .. })); + } + + #[test] + fn rejects_wrong_frame_length() { + let mut stub = StubAudioProcessor::new(ApmConfig::default(), 48_000, 1).expect("ctor"); + let mut samples = vec![0i16; 100]; + let err = stub + .process_capture_frame(&mut samples, 48_000, 1) + .expect_err("err"); + assert!(matches!(err, ApmError::FrameLengthMismatch { .. })); + } + + #[test] + fn rejects_render_frame_length() { + let mut stub = StubAudioProcessor::new(ApmConfig::default(), 48_000, 1).expect("ctor"); + let samples = vec![0i16; 99]; + let err = stub + .process_render_frame(&samples, 48_000, 1) + .expect_err("err"); + assert!(matches!(err, ApmError::FrameLengthMismatch { .. })); + } + + #[test] + fn rejects_sample_rate_out_of_range_on_construct() { + let err = StubAudioProcessor::new(ApmConfig::default(), 4_000, 1).expect_err("err"); + assert!(matches!(err, ApmError::SampleRateOutOfRange { .. })); + } + + #[test] + fn rejects_channels_out_of_range_on_construct() { + let err = StubAudioProcessor::new(ApmConfig::default(), 48_000, 4).expect_err("err"); + assert!(matches!(err, ApmError::ChannelsOutOfRange { .. })); + } + + #[test] + fn reset_returns_ok() { + let mut stub = StubAudioProcessor::new(ApmConfig::default(), 48_000, 1).expect("ctor"); + let mut samples = make_frame(48_000, 1); + stub.process_capture_frame(&mut samples, 48_000, 1) + .expect("ok"); + assert_eq!(stub.capture_frames_processed(), 1); + stub.reset().expect("reset"); + assert_eq!(stub.capture_frames_processed(), 0); + assert_eq!(stub.render_frames_processed(), 0); + } + + #[test] + fn builder_pattern_produces_valid_config() { + let config = ApmConfigBuilder::new() + .aec(false) + .ns(true) + .agc(false) + .aec_mobile_mode(true) + .target_level_dbfs(-6) + .build(); + assert!(!config.aec_enabled); + assert!(config.ns_enabled); + assert!(!config.agc_enabled); + assert!(config.aec_mobile_mode); + assert_eq!(config.target_level_dbfs, -6); + } + + #[test] + fn builder_default_matches_struct_default() { + let builder = ApmConfigBuilder::new().build(); + let default = ApmConfig::default(); + assert_eq!(builder, default); + } + + #[test] + fn state_preserved_across_many_frames() { + let mut stub = StubAudioProcessor::new(ApmConfig::default(), 48_000, 1).expect("ctor"); + let mut samples = make_frame(48_000, 1); + const N: u64 = 100; + for _ in 0..N { + stub.process_capture_frame(&mut samples, 48_000, 1) + .expect("ok"); + } + assert_eq!(stub.capture_frames_processed(), N); + let render = vec![0i16; expected_frame_samples(48_000, 1)]; + for _ in 0..N { + stub.process_render_frame(&render, 48_000, 1).expect("ok"); + } + assert_eq!(stub.render_frames_processed(), N); + } + + #[test] + fn determinism_stub_produces_identical_output() { + let mut stub_a = StubAudioProcessor::new(ApmConfig::default(), 48_000, 1).expect("ctor"); + let mut stub_b = StubAudioProcessor::new(ApmConfig::default(), 48_000, 1).expect("ctor"); + let original = make_frame(48_000, 1); + let mut samples_a = original.clone(); + let mut samples_b = original.clone(); + let report_a = stub_a + .process_capture_frame(&mut samples_a, 48_000, 1) + .expect("ok"); + let report_b = stub_b + .process_capture_frame(&mut samples_b, 48_000, 1) + .expect("ok"); + assert_eq!(samples_a, samples_b); + assert_eq!(samples_a, original); + assert_eq!(report_a, report_b); + } + + #[test] + fn stereo_round_trip_capture_succeeds() { + let mut stub = StubAudioProcessor::new(ApmConfig::default(), 48_000, 2).expect("ctor"); + let mut samples = make_frame(48_000, 2); + let original = samples.clone(); + let report = stub + .process_capture_frame(&mut samples, 48_000, 2) + .expect("ok"); + assert_eq!(samples, original); + assert_eq!(report, ApmReport::NEUTRAL); + } + + #[test] + fn validate_frame_shape_accepts_canonical_48k_mono() { + validate_frame_shape(480, 48_000, 1, 48_000, 1).expect("ok"); + } + + #[test] + #[cfg(not(feature = "real-apm"))] + fn real_apm_type_absent_when_feature_off() { + let _ = ApmConfig::default(); + } + + #[cfg(feature = "real-apm")] + mod real_apm_tests { + use super::*; + + fn sine_frame_i16(sample_rate_hz: u32, channels: u16, frequency_hz: f32) -> Vec { + let per_channel = (APM_FRAME_MS as usize) * (sample_rate_hz as usize) / 1000; + let total = per_channel * (channels as usize); + let mut samples = Vec::with_capacity(total); + for sample_index in 0..per_channel { + let phase = (sample_index as f32) * frequency_hz / (sample_rate_hz as f32); + let value = (phase * 2.0 * core::f32::consts::PI).sin() * 0.5; + let scaled = (value * 32767.0) as i16; + for _ in 0..channels { + samples.push(scaled); + } + } + samples + } + + #[test] + fn real_apm_constructs_at_48k_mono() { + let config = ApmConfig::default(); + let processor = WebRtcAudioProcessor::new(config, 48_000, 1).expect("ctor"); + assert_eq!(processor.samples_per_channel(), 480); + assert_eq!(processor.capture_frames_processed(), 0); + assert_eq!(processor.render_frames_processed(), 0); + assert_eq!(processor.config(), config); + } + + #[test] + fn real_apm_rejects_invalid_sample_rate_on_construct() { + let config = ApmConfig::default(); + let err = WebRtcAudioProcessor::new(config, 4_000, 1).expect_err("err"); + assert!(matches!(err, ApmError::SampleRateOutOfRange { .. })); + } + + #[test] + fn real_apm_rejects_invalid_channels_on_construct() { + let config = ApmConfig::default(); + let err = WebRtcAudioProcessor::new(config, 48_000, 8).expect_err("err"); + assert!(matches!(err, ApmError::ChannelsOutOfRange { .. })); + } + + #[test] + fn real_apm_capture_increments_counter_and_returns_report() { + let config = ApmConfig::default(); + let mut processor = WebRtcAudioProcessor::new(config, 48_000, 1).expect("ctor"); + let mut samples = sine_frame_i16(48_000, 1, 440.0); + let report = processor + .process_capture_frame(&mut samples, 48_000, 1) + .expect("capture ok"); + assert_eq!(processor.capture_frames_processed(), 1); + assert!(report.level_dbfs <= 0.0); + assert!(report.level_dbfs >= -120.0); + } + + #[test] + fn real_apm_render_increments_counter() { + let config = ApmConfig::default(); + let mut processor = WebRtcAudioProcessor::new(config, 48_000, 1).expect("ctor"); + let samples = sine_frame_i16(48_000, 1, 880.0); + processor + .process_render_frame(&samples, 48_000, 1) + .expect("render ok"); + assert_eq!(processor.render_frames_processed(), 1); + } + + #[test] + fn real_apm_rejects_mismatched_frame_length() { + let config = ApmConfig::default(); + let mut processor = WebRtcAudioProcessor::new(config, 48_000, 1).expect("ctor"); + let mut samples = vec![0i16; 100]; + let err = processor + .process_capture_frame(&mut samples, 48_000, 1) + .expect_err("err"); + assert!(matches!(err, ApmError::FrameLengthMismatch { .. })); + } + + #[test] + fn real_apm_rejects_mismatched_sample_rate_at_process() { + let config = ApmConfig::default(); + let mut processor = WebRtcAudioProcessor::new(config, 48_000, 1).expect("ctor"); + let mut samples = sine_frame_i16(16_000, 1, 440.0); + let err = processor + .process_capture_frame(&mut samples, 16_000, 1) + .expect_err("err"); + assert!(matches!(err, ApmError::SampleRateMismatch { .. })); + } + + #[test] + fn real_apm_processes_many_capture_frames_without_panic() { + let config = ApmConfigBuilder::new().aec(true).ns(true).agc(true).build(); + let mut processor = WebRtcAudioProcessor::new(config, 48_000, 1).expect("ctor"); + let mut samples = sine_frame_i16(48_000, 1, 440.0); + const N: u64 = 50; + for _ in 0..N { + let mut frame = samples.clone(); + processor + .process_capture_frame(&mut frame, 48_000, 1) + .expect("capture ok"); + processor + .process_render_frame(&samples, 48_000, 1) + .expect("render ok"); + } + assert_eq!(processor.capture_frames_processed(), N); + assert_eq!(processor.render_frames_processed(), N); + samples.clear(); + } + + #[test] + fn real_apm_reset_clears_counters() { + let config = ApmConfig::default(); + let mut processor = WebRtcAudioProcessor::new(config, 48_000, 1).expect("ctor"); + let mut samples = sine_frame_i16(48_000, 1, 440.0); + processor + .process_capture_frame(&mut samples, 48_000, 1) + .expect("capture ok"); + processor + .process_render_frame(&samples, 48_000, 1) + .expect("render ok"); + assert_eq!(processor.capture_frames_processed(), 1); + assert_eq!(processor.render_frames_processed(), 1); + processor.reset().expect("reset ok"); + assert_eq!(processor.capture_frames_processed(), 0); + assert_eq!(processor.render_frames_processed(), 0); + } + + #[test] + fn real_apm_silent_frame_reports_quiet_level() { + let config = ApmConfig::default(); + let mut processor = WebRtcAudioProcessor::new(config, 48_000, 1).expect("ctor"); + let mut samples = vec![0i16; expected_frame_samples(48_000, 1)]; + let report = processor + .process_capture_frame(&mut samples, 48_000, 1) + .expect("capture ok"); + assert!(report.level_dbfs <= -50.0); + assert!(!report.voice_detected); + } + + #[test] + fn real_apm_stereo_round_trip_succeeds() { + let config = ApmConfig::default(); + let mut processor = WebRtcAudioProcessor::new(config, 48_000, 2).expect("ctor"); + let mut samples = sine_frame_i16(48_000, 2, 440.0); + processor + .process_capture_frame(&mut samples, 48_000, 2) + .expect("capture ok"); + assert_eq!(processor.capture_frames_processed(), 1); + } + + #[test] + fn real_apm_disabling_all_submodules_constructs_ok() { + let config = ApmConfigBuilder::new() + .aec(false) + .ns(false) + .agc(false) + .target_level_dbfs(-6) + .build(); + let mut processor = WebRtcAudioProcessor::new(config, 48_000, 1).expect("ctor"); + let mut samples = sine_frame_i16(48_000, 1, 440.0); + processor + .process_capture_frame(&mut samples, 48_000, 1) + .expect("capture ok"); + assert_eq!(processor.capture_frames_processed(), 1); + } + } +} diff --git a/fluxer_desktop/native/audio-mix/Cargo.lock b/fluxer_desktop/native/audio-mix/Cargo.lock new file mode 100644 index 0000000..28f8dd7 --- /dev/null +++ b/fluxer_desktop/native/audio-mix/Cargo.lock @@ -0,0 +1,763 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "criterion" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3" +dependencies = [ + "alloca", + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "num-traits", + "oorandom", + "page_size", + "plotters", + "rayon", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fluxer_audio_mix" +version = "0.0.0" +dependencies = [ + "criterion", + "fluxer_rt_thread", + "rtrb", +] + +[[package]] +name = "fluxer_rt_thread" +version = "0.0.0" +dependencies = [ + "libc", + "windows", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rtrb" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ade083ccbb4bf536df69d1f6432cc23deb7acccff86b183f3923a6fd56a1153" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0871acf327f283dc6da28a1696cdc64fb355ba9f935d052021fa77f35cce69" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zerocopy" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/fluxer_desktop/native/audio-mix/Cargo.toml b/fluxer_desktop/native/audio-mix/Cargo.toml new file mode 100644 index 0000000..76e438a --- /dev/null +++ b/fluxer_desktop/native/audio-mix/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "fluxer_audio_mix" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["rlib"] + +[dependencies] +rtrb = "0.3" +fluxer_rt_thread = {path = "../rt-thread"} + +[dev-dependencies] +criterion = "0.8" + +[[bench]] +name = "mix" +harness = false diff --git a/fluxer_desktop/native/audio-mix/benches/baseline.json b/fluxer_desktop/native/audio-mix/benches/baseline.json new file mode 100644 index 0000000..53f4db6 --- /dev/null +++ b/fluxer_desktop/native/audio-mix/benches/baseline.json @@ -0,0 +1,18 @@ +{ + "measured_at": "85e057a273fd", + "host": "darwin-arm64-apple-silicon", + "regression_budget_percent": 5.0, + "criterion_args": { + "warm_up_time_sec": 2, + "measurement_time_sec": 5 + }, + "benches": { + "audio_mix_tick/mix_8_sources_1024_samples": { + "median_ns": 2103.8, + "low_ns": 2091.3, + "high_ns": 2117.0, + "budget_percent_override": 8.0, + "note": "Sub-microsecond mix tick; on Apple Silicon with thermal scaling, observed run-to-run sigma is ~3-4%, so 8% leaves room for noise without hiding 1.5x slowdowns." + } + } +} diff --git a/fluxer_desktop/native/audio-mix/benches/mix.rs b/fluxer_desktop/native/audio-mix/benches/mix.rs new file mode 100644 index 0000000..2ea1afe --- /dev/null +++ b/fluxer_desktop/native/audio-mix/benches/mix.rs @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::hint::black_box; + +use criterion::{Criterion, criterion_group, criterion_main}; +use fluxer_audio_mix::{ + AUDIO_OUTPUT_FRAMES, AudioMixSession, SourceRing, SourceRingConsumer, SourceRingProducer, +}; +use fluxer_rt_thread::TickInfo; + +const BENCH_SOURCE_COUNT: usize = 8; + +fn build_session(source_count: usize) -> (Vec, AudioMixSession) { + assert!(source_count > 0); + let mut producers = Vec::with_capacity(source_count); + let mut consumers: Vec = Vec::with_capacity(source_count); + for _ in 0..source_count { + let (producer, consumer) = SourceRing::create(8192, 48_000).expect("pair"); + producers.push(producer); + consumers.push(consumer); + } + let session = AudioMixSession::new(consumers, AUDIO_OUTPUT_FRAMES).expect("session"); + (producers, session) +} + +fn synthetic_tick(index: u64) -> TickInfo { + let scheduled_ns = index * 21_333_333; + TickInfo { + tick_index: index, + scheduled_ns, + actual_ns: scheduled_ns, + lag_ns: 0, + } +} + +fn fill_producers(producers: &mut [SourceRingProducer], frames: usize) { + assert!(!producers.is_empty()); + assert!(frames > 0); + let payload: Vec = (0..frames).map(|n| ((n as i16) % 4096) - 2048).collect(); + for producer in producers.iter_mut() { + let pushed = producer.try_push_slice(&payload); + assert!(pushed > 0); + } +} + +fn bench_mix_tick(c: &mut Criterion) { + let mut group = c.benchmark_group("audio_mix_tick"); + group.sample_size(50); + group.bench_function("mix_8_sources_1024_samples", |b| { + let (mut producers, mut session) = build_session(BENCH_SOURCE_COUNT); + let mut tick_index: u64 = 0; + b.iter(|| { + fill_producers(&mut producers, AUDIO_OUTPUT_FRAMES); + let result = session.tick(synthetic_tick(tick_index)); + tick_index = tick_index.wrapping_add(1); + black_box(result); + }); + }); + group.finish(); +} + +criterion_group!(benches, bench_mix_tick); +criterion_main!(benches); diff --git a/fluxer_desktop/native/audio-mix/src/lib.rs b/fluxer_desktop/native/audio-mix/src/lib.rs new file mode 100644 index 0000000..7d70abc --- /dev/null +++ b/fluxer_desktop/native/audio-mix/src/lib.rs @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![deny(clippy::too_many_lines)] + +pub mod mix_session; +pub mod source_ring; + +pub use mix_session::{ + AudioMixSession, MAX_MIX_SOURCES, MixTickResult, MixedFrame, SourceTickStat, +}; +pub use source_ring::{ + AUDIO_OUTPUT_FRAMES, AUDIO_RING_CAP_FRAMES, AUDIO_SAMPLE_RATE_HZ_MAX, AUDIO_SAMPLE_RATE_HZ_MIN, + SourceRing, SourceRingConsumer, SourceRingProducer, +}; + +#[derive(Debug, PartialEq, Eq, Clone)] +pub enum AudioMixError { + ZeroCapacity, + CapacityExceedsLimit { requested: usize, limit: usize }, + SampleRateOutOfRange { sample_rate_hz: u32 }, + SampleRateMismatch { expected_hz: u32, observed_hz: u32 }, + ZeroSources, + TooManySources { requested: usize, limit: usize }, + MixBufferLenMismatch { expected: usize, observed: usize }, +} + +impl core::fmt::Display for AudioMixError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + AudioMixError::ZeroCapacity => write!(f, "ring capacity must be non-zero"), + AudioMixError::CapacityExceedsLimit { requested, limit } => { + write!(f, "ring capacity {requested} exceeds hard limit {limit}",) + } + AudioMixError::SampleRateOutOfRange { sample_rate_hz } => { + write!(f, "sample rate {sample_rate_hz} hz outside accepted range") + } + AudioMixError::SampleRateMismatch { + expected_hz, + observed_hz, + } => write!( + f, + "source sample rate mismatch: expected={expected_hz} observed={observed_hz}", + ), + AudioMixError::ZeroSources => write!(f, "AudioMixSession requires at least one source"), + AudioMixError::TooManySources { requested, limit } => write!( + f, + "AudioMixSession source count {requested} exceeds limit {limit}", + ), + AudioMixError::MixBufferLenMismatch { expected, observed } => write!( + f, + "mix_buffer_len mismatch: expected={expected} observed={observed}", + ), + } + } +} + +impl std::error::Error for AudioMixError {} diff --git a/fluxer_desktop/native/audio-mix/src/mix_session.rs b/fluxer_desktop/native/audio-mix/src/mix_session.rs new file mode 100644 index 0000000..e33721a --- /dev/null +++ b/fluxer_desktop/native/audio-mix/src/mix_session.rs @@ -0,0 +1,411 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use fluxer_rt_thread::TickInfo; + +use crate::AudioMixError; +use crate::source_ring::{AUDIO_OUTPUT_FRAMES, SourceRingConsumer}; + +pub const MAX_MIX_SOURCES: usize = 256; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SourceTickStat { + pub drained_count: u32, + pub silence_count: u32, +} + +#[derive(Debug)] +pub struct MixedFrame<'a> { + pub samples: &'a [i16; AUDIO_OUTPUT_FRAMES], + pub tick_index: u64, + pub scheduled_ns: u64, + pub actual_ns: u64, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct MixTickResult { + pub tick_index: u64, + pub scheduled_ns: u64, + pub actual_ns: u64, + pub lag_ns: i64, + pub total_drained: u64, + pub total_silence: u64, + pub saturated_samples: u32, +} + +pub struct AudioMixSession { + consumers: Vec, + per_source_stats: Vec, + sample_rate_hz: u32, + mix_buffer_len: usize, + accumulator: Box<[i32; AUDIO_OUTPUT_FRAMES]>, + scratch: Box<[i16; AUDIO_OUTPUT_FRAMES]>, + output: Box<[i16; AUDIO_OUTPUT_FRAMES]>, + last_tick_index: Option, + last_actual_ns: u64, + ticks_completed: u64, +} + +impl AudioMixSession { + pub fn new( + consumers: Vec, + mix_buffer_len: usize, + ) -> Result { + if consumers.is_empty() { + return Err(AudioMixError::ZeroSources); + } + if consumers.len() > MAX_MIX_SOURCES { + return Err(AudioMixError::TooManySources { + requested: consumers.len(), + limit: MAX_MIX_SOURCES, + }); + } + if mix_buffer_len != AUDIO_OUTPUT_FRAMES { + return Err(AudioMixError::MixBufferLenMismatch { + expected: AUDIO_OUTPUT_FRAMES, + observed: mix_buffer_len, + }); + } + let sample_rate_hz = consumers[0].sample_rate_hz(); + for consumer in &consumers { + let observed = consumer.sample_rate_hz(); + if observed != sample_rate_hz { + return Err(AudioMixError::SampleRateMismatch { + expected_hz: sample_rate_hz, + observed_hz: observed, + }); + } + } + assert!(!consumers.is_empty()); + assert!(consumers.len() <= MAX_MIX_SOURCES); + let per_source_stats = vec![ + SourceTickStat { + drained_count: 0, + silence_count: 0, + }; + consumers.len() + ]; + let accumulator = Box::new([0i32; AUDIO_OUTPUT_FRAMES]); + let scratch = Box::new([0i16; AUDIO_OUTPUT_FRAMES]); + let output = Box::new([0i16; AUDIO_OUTPUT_FRAMES]); + Ok(Self { + consumers, + per_source_stats, + sample_rate_hz, + mix_buffer_len, + accumulator, + scratch, + output, + last_tick_index: None, + last_actual_ns: 0, + ticks_completed: 0, + }) + } + + pub fn sample_rate_hz(&self) -> u32 { + assert!(self.sample_rate_hz > 0); + assert!(self.sample_rate_hz <= 384_000); + self.sample_rate_hz + } + + pub fn mix_buffer_len(&self) -> usize { + assert_eq!(self.mix_buffer_len, AUDIO_OUTPUT_FRAMES); + self.mix_buffer_len + } + + pub fn source_count(&self) -> usize { + assert!(!self.consumers.is_empty()); + assert!(self.consumers.len() <= MAX_MIX_SOURCES); + self.consumers.len() + } + + pub fn ticks_completed(&self) -> u64 { + self.ticks_completed + } + + pub fn per_source_stats(&self) -> &[SourceTickStat] { + assert_eq!(self.per_source_stats.len(), self.consumers.len()); + &self.per_source_stats + } + + pub fn tick(&mut self, tick_info: TickInfo) -> MixTickResult { + assert!(!self.consumers.is_empty()); + assert!(tick_info.actual_ns >= self.last_actual_ns); + if let Some(prior) = self.last_tick_index { + assert!(tick_info.tick_index > prior); + } + for slot in self.accumulator.iter_mut() { + *slot = 0; + } + let (total_drained, total_silence) = self.mix_all_sources(); + let saturated = self.finalise_output(); + self.ticks_completed = self.ticks_completed.saturating_add(1); + self.last_tick_index = Some(tick_info.tick_index); + self.last_actual_ns = tick_info.actual_ns; + let result = MixTickResult { + tick_index: tick_info.tick_index, + scheduled_ns: tick_info.scheduled_ns, + actual_ns: tick_info.actual_ns, + lag_ns: tick_info.lag_ns, + total_drained, + total_silence, + saturated_samples: saturated, + }; + assert_eq!(result.tick_index, tick_info.tick_index); + assert!(result.saturated_samples as usize <= AUDIO_OUTPUT_FRAMES); + result + } + + fn mix_all_sources(&mut self) -> (u64, u64) { + let n = self.consumers.len(); + assert_eq!(self.per_source_stats.len(), n); + let mut total_drained: u64 = 0; + let mut total_silence: u64 = 0; + for index in 0..n { + let drained = self.consumers[index].drain_into(self.scratch.as_mut_slice()); + assert!(drained <= AUDIO_OUTPUT_FRAMES); + let silence = AUDIO_OUTPUT_FRAMES - drained; + let stat = &mut self.per_source_stats[index]; + stat.drained_count = stat.drained_count.saturating_add(drained as u32); + stat.silence_count = stat.silence_count.saturating_add(silence as u32); + total_drained = total_drained.saturating_add(drained as u64); + total_silence = total_silence.saturating_add(silence as u64); + for (acc, sample) in self.accumulator[..drained] + .iter_mut() + .zip(self.scratch[..drained].iter()) + { + *acc = acc.saturating_add(*sample as i32); + } + } + (total_drained, total_silence) + } + + fn finalise_output(&mut self) -> u32 { + let mut saturated: u32 = 0; + for (out, acc) in self.output.iter_mut().zip(self.accumulator.iter()) { + let value = *acc; + let clamped = if value > i16::MAX as i32 { + saturated = saturated.saturating_add(1); + i16::MAX + } else if value < i16::MIN as i32 { + saturated = saturated.saturating_add(1); + i16::MIN + } else { + value as i16 + }; + *out = clamped; + } + assert!(saturated as usize <= AUDIO_OUTPUT_FRAMES); + saturated + } + + pub fn last_output(&self) -> &[i16; AUDIO_OUTPUT_FRAMES] { + assert_eq!(self.output.len(), AUDIO_OUTPUT_FRAMES); + &self.output + } + + pub fn last_mixed_frame(&self) -> Option> { + let tick_index = self.last_tick_index?; + assert!(self.ticks_completed > 0); + Some(MixedFrame { + samples: &self.output, + tick_index, + scheduled_ns: 0, + actual_ns: self.last_actual_ns, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::source_ring::SourceRing; + use fluxer_rt_thread::TickInfo; + use std::sync::{Arc, Barrier}; + use std::thread; + + fn synthetic_tick(index: u64) -> TickInfo { + let scheduled_ns = index * 21_333_333; + TickInfo { + tick_index: index, + scheduled_ns, + actual_ns: scheduled_ns, + lag_ns: 0, + } + } + + #[test] + fn rejects_empty_consumer_list() { + let err = AudioMixSession::new(Vec::new(), AUDIO_OUTPUT_FRAMES).err(); + assert_eq!(err, Some(AudioMixError::ZeroSources)); + } + + #[test] + fn rejects_mismatched_sample_rates() { + let (_p1, c1) = SourceRing::create(2048, 48_000).expect("pair 1"); + let (_p2, c2) = SourceRing::create(2048, 44_100).expect("pair 2"); + let err = AudioMixSession::new(vec![c1, c2], AUDIO_OUTPUT_FRAMES).err(); + assert!(matches!( + err, + Some(AudioMixError::SampleRateMismatch { .. }) + )); + } + + #[test] + fn rejects_wrong_mix_buffer_len() { + let (_p, c) = SourceRing::create(2048, 48_000).expect("pair"); + let err = AudioMixSession::new(vec![c], 512).err(); + assert!(matches!( + err, + Some(AudioMixError::MixBufferLenMismatch { .. }) + )); + } + + #[test] + fn single_source_passes_through_samples() { + let (mut producer, consumer) = SourceRing::create(2048, 48_000).expect("pair"); + let payload: Vec = (0..AUDIO_OUTPUT_FRAMES) + .map(|n| (n as i16) % 1000) + .collect(); + let pushed = producer.try_push_slice(&payload); + assert_eq!(pushed, AUDIO_OUTPUT_FRAMES); + let mut session = + AudioMixSession::new(vec![consumer], AUDIO_OUTPUT_FRAMES).expect("session"); + let result = session.tick(synthetic_tick(0)); + assert_eq!(result.total_drained, AUDIO_OUTPUT_FRAMES as u64); + assert_eq!(result.total_silence, 0); + assert_eq!(&session.last_output()[..], &payload[..]); + } + + #[test] + fn empty_ring_produces_silence_without_blocking() { + let (_producer, consumer) = SourceRing::create(2048, 48_000).expect("pair"); + let mut session = + AudioMixSession::new(vec![consumer], AUDIO_OUTPUT_FRAMES).expect("session"); + let result = session.tick(synthetic_tick(0)); + assert_eq!(result.total_drained, 0); + assert_eq!(result.total_silence, AUDIO_OUTPUT_FRAMES as u64); + for sample in session.last_output().iter() { + assert_eq!(*sample, 0); + } + } + + #[test] + fn drained_count_accuracy_across_partial_fills() { + let (mut producer, consumer) = SourceRing::create(2048, 48_000).expect("pair"); + let half: Vec = vec![100; AUDIO_OUTPUT_FRAMES / 2]; + producer.try_push_slice(&half); + let mut session = + AudioMixSession::new(vec![consumer], AUDIO_OUTPUT_FRAMES).expect("session"); + let result = session.tick(synthetic_tick(0)); + assert_eq!(result.total_drained, (AUDIO_OUTPUT_FRAMES / 2) as u64); + assert_eq!(result.total_silence, (AUDIO_OUTPUT_FRAMES / 2) as u64); + let stats = session.per_source_stats(); + assert_eq!(stats.len(), 1); + assert_eq!(stats[0].drained_count as usize, AUDIO_OUTPUT_FRAMES / 2); + assert_eq!(stats[0].silence_count as usize, AUDIO_OUTPUT_FRAMES / 2); + } + + #[test] + fn opposite_phase_sources_sum_to_silence() { + let (mut p1, c1) = SourceRing::create(2048, 48_000).expect("pair 1"); + let (mut p2, c2) = SourceRing::create(2048, 48_000).expect("pair 2"); + let wave: Vec = (0..AUDIO_OUTPUT_FRAMES) + .map(|n| ((n as i16) % 1000) - 500) + .collect(); + let inverse: Vec = wave.iter().map(|s| -*s).collect(); + p1.try_push_slice(&wave); + p2.try_push_slice(&inverse); + let mut session = AudioMixSession::new(vec![c1, c2], AUDIO_OUTPUT_FRAMES).expect("session"); + let _ = session.tick(synthetic_tick(0)); + for sample in session.last_output().iter() { + assert_eq!(*sample, 0); + } + } + + #[test] + fn saturation_clamps_at_i16_limits() { + let (mut p1, c1) = SourceRing::create(2048, 48_000).expect("pair 1"); + let (mut p2, c2) = SourceRing::create(2048, 48_000).expect("pair 2"); + let high: Vec = vec![i16::MAX; AUDIO_OUTPUT_FRAMES]; + p1.try_push_slice(&high); + p2.try_push_slice(&high); + let mut session = AudioMixSession::new(vec![c1, c2], AUDIO_OUTPUT_FRAMES).expect("session"); + let result = session.tick(synthetic_tick(0)); + assert_eq!(result.saturated_samples as usize, AUDIO_OUTPUT_FRAMES); + for sample in session.last_output().iter() { + assert_eq!(*sample, i16::MAX); + } + } + + #[test] + fn saturation_clamps_at_i16_min_limits() { + let (mut p1, c1) = SourceRing::create(2048, 48_000).expect("pair 1"); + let (mut p2, c2) = SourceRing::create(2048, 48_000).expect("pair 2"); + let low: Vec = vec![i16::MIN; AUDIO_OUTPUT_FRAMES]; + p1.try_push_slice(&low); + p2.try_push_slice(&low); + let mut session = AudioMixSession::new(vec![c1, c2], AUDIO_OUTPUT_FRAMES).expect("session"); + let result = session.tick(synthetic_tick(0)); + assert_eq!(result.saturated_samples as usize, AUDIO_OUTPUT_FRAMES); + for sample in session.last_output().iter() { + assert_eq!(*sample, i16::MIN); + } + } + + #[test] + fn full_ring_producer_drops_overflow() { + let (mut producer, consumer) = + SourceRing::create(AUDIO_OUTPUT_FRAMES, 48_000).expect("pair"); + let payload: Vec = vec![7; AUDIO_OUTPUT_FRAMES]; + let pushed_first = producer.try_push_slice(&payload); + assert_eq!(pushed_first, AUDIO_OUTPUT_FRAMES); + let pushed_second = producer.try_push_slice(&payload); + assert_eq!(pushed_second, 0); + assert_eq!(producer.dropped_total(), AUDIO_OUTPUT_FRAMES as u64); + let mut session = + AudioMixSession::new(vec![consumer], AUDIO_OUTPUT_FRAMES).expect("session"); + let result = session.tick(synthetic_tick(0)); + assert_eq!(result.total_drained, AUDIO_OUTPUT_FRAMES as u64); + } + + #[test] + fn ten_producer_threads_one_mixer_no_panic() { + const SOURCE_COUNT: usize = 10; + const TICKS: u64 = 50; + let mut producers = Vec::with_capacity(SOURCE_COUNT); + let mut consumers = Vec::with_capacity(SOURCE_COUNT); + for _ in 0..SOURCE_COUNT { + let (producer, consumer) = SourceRing::create(8192, 48_000).expect("pair"); + producers.push(producer); + consumers.push(consumer); + } + let barrier = Arc::new(Barrier::new(SOURCE_COUNT + 1)); + let stop = Arc::new(std::sync::atomic::AtomicBool::new(false)); + let mut handles = Vec::with_capacity(SOURCE_COUNT); + for (index, mut producer) in producers.into_iter().enumerate() { + let barrier = Arc::clone(&barrier); + let stop = Arc::clone(&stop); + handles.push(thread::spawn(move || { + barrier.wait(); + let mut value: i16 = index as i16; + while !stop.load(std::sync::atomic::Ordering::Acquire) { + let _ = producer.try_push(value); + value = value.wrapping_add(1); + } + })); + } + let mut session = AudioMixSession::new(consumers, AUDIO_OUTPUT_FRAMES).expect("session"); + barrier.wait(); + let mut last_drained: u64 = 0; + for tick_index in 0..TICKS { + let result = session.tick(synthetic_tick(tick_index)); + last_drained = last_drained.saturating_add(result.total_drained); + } + stop.store(true, std::sync::atomic::Ordering::Release); + for handle in handles { + handle.join().expect("producer join"); + } + assert_eq!(session.ticks_completed(), TICKS); + assert!(last_drained > 0); + } +} diff --git a/fluxer_desktop/native/audio-mix/src/source_ring.rs b/fluxer_desktop/native/audio-mix/src/source_ring.rs new file mode 100644 index 0000000..1d99e7e --- /dev/null +++ b/fluxer_desktop/native/audio-mix/src/source_ring.rs @@ -0,0 +1,268 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use rtrb::{Consumer, Producer, RingBuffer}; + +use crate::AudioMixError; + +pub const AUDIO_RING_CAP_FRAMES: usize = 4096; + +pub const AUDIO_RING_CAP_FRAMES_MAX: usize = 1 << 20; + +pub const AUDIO_OUTPUT_FRAMES: usize = 1024; + +pub const AUDIO_SAMPLE_RATE_HZ_MIN: u32 = 8_000; +pub const AUDIO_SAMPLE_RATE_HZ_MAX: u32 = 384_000; + +pub struct SourceRingProducer { + inner: Producer, + sample_rate_hz: u32, + capacity_frames: usize, + pushed_total: u64, + dropped_total: u64, +} + +pub struct SourceRingConsumer { + inner: Consumer, + sample_rate_hz: u32, + capacity_frames: usize, + drained_total: u64, +} + +pub struct SourceRing; + +impl SourceRing { + pub fn create( + capacity_frames: usize, + sample_rate_hz: u32, + ) -> Result<(SourceRingProducer, SourceRingConsumer), AudioMixError> { + if capacity_frames == 0 { + return Err(AudioMixError::ZeroCapacity); + } + if capacity_frames > AUDIO_RING_CAP_FRAMES_MAX { + return Err(AudioMixError::CapacityExceedsLimit { + requested: capacity_frames, + limit: AUDIO_RING_CAP_FRAMES_MAX, + }); + } + if !(AUDIO_SAMPLE_RATE_HZ_MIN..=AUDIO_SAMPLE_RATE_HZ_MAX).contains(&sample_rate_hz) { + return Err(AudioMixError::SampleRateOutOfRange { sample_rate_hz }); + } + assert!(capacity_frames > 0); + assert!(capacity_frames <= AUDIO_RING_CAP_FRAMES_MAX); + let (producer, consumer) = RingBuffer::::new(capacity_frames); + let producer = SourceRingProducer { + inner: producer, + sample_rate_hz, + capacity_frames, + pushed_total: 0, + dropped_total: 0, + }; + let consumer = SourceRingConsumer { + inner: consumer, + sample_rate_hz, + capacity_frames, + drained_total: 0, + }; + assert_eq!(producer.sample_rate_hz, consumer.sample_rate_hz); + assert_eq!(producer.capacity_frames, consumer.capacity_frames); + Ok((producer, consumer)) + } + + pub fn create_default( + sample_rate_hz: u32, + ) -> Result<(SourceRingProducer, SourceRingConsumer), AudioMixError> { + const { assert!(AUDIO_RING_CAP_FRAMES > 0) }; + const { assert!(AUDIO_RING_CAP_FRAMES <= AUDIO_RING_CAP_FRAMES_MAX) }; + SourceRing::create(AUDIO_RING_CAP_FRAMES, sample_rate_hz) + } +} + +impl SourceRingProducer { + pub fn sample_rate_hz(&self) -> u32 { + assert!(self.sample_rate_hz >= AUDIO_SAMPLE_RATE_HZ_MIN); + assert!(self.sample_rate_hz <= AUDIO_SAMPLE_RATE_HZ_MAX); + self.sample_rate_hz + } + + pub fn capacity_frames(&self) -> usize { + assert!(self.capacity_frames > 0); + assert!(self.capacity_frames <= AUDIO_RING_CAP_FRAMES_MAX); + self.capacity_frames + } + + pub fn slots(&self) -> usize { + let slots = self.inner.slots(); + assert!(slots <= self.capacity_frames); + slots + } + + pub fn is_full(&self) -> bool { + self.inner.is_full() + } + + pub fn try_push(&mut self, sample: i16) -> bool { + assert!(self.capacity_frames > 0); + assert!(self.pushed_total < u64::MAX); + match self.inner.push(sample) { + Ok(()) => { + self.pushed_total = self.pushed_total.saturating_add(1); + true + } + Err(_) => { + self.dropped_total = self.dropped_total.saturating_add(1); + false + } + } + } + + pub fn try_push_slice(&mut self, samples: &[i16]) -> usize { + assert!(samples.len() <= self.capacity_frames); + assert!(self.pushed_total < u64::MAX - samples.len() as u64); + let (pushed_slice, remainder) = self.inner.push_partial_slice(samples); + let pushed = pushed_slice.len(); + let dropped = remainder.len(); + assert_eq!(pushed + dropped, samples.len()); + self.pushed_total = self.pushed_total.saturating_add(pushed as u64); + self.dropped_total = self.dropped_total.saturating_add(dropped as u64); + pushed + } + + pub fn pushed_total(&self) -> u64 { + self.pushed_total + } + + pub fn dropped_total(&self) -> u64 { + self.dropped_total + } +} + +impl SourceRingConsumer { + pub fn sample_rate_hz(&self) -> u32 { + assert!(self.sample_rate_hz >= AUDIO_SAMPLE_RATE_HZ_MIN); + assert!(self.sample_rate_hz <= AUDIO_SAMPLE_RATE_HZ_MAX); + self.sample_rate_hz + } + + pub fn capacity_frames(&self) -> usize { + assert!(self.capacity_frames > 0); + assert!(self.capacity_frames <= AUDIO_RING_CAP_FRAMES_MAX); + self.capacity_frames + } + + pub fn slots(&self) -> usize { + let slots = self.inner.slots(); + assert!(slots <= self.capacity_frames); + slots + } + + pub fn is_empty(&self) -> bool { + self.inner.is_empty() + } + + pub fn drain_into(&mut self, dest: &mut [i16]) -> usize { + assert!(!dest.is_empty()); + assert!(dest.len() <= self.capacity_frames); + let requested = dest.len(); + let (popped, remainder) = self.inner.pop_partial_slice(dest); + let drained = popped.len(); + let leftover = remainder.len(); + assert_eq!(drained + leftover, requested); + self.drained_total = self.drained_total.saturating_add(drained as u64); + drained + } + + pub fn drained_total(&self) -> u64 { + self.drained_total + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn rejects_zero_capacity() { + let err = SourceRing::create(0, 48_000).err(); + assert_eq!(err, Some(AudioMixError::ZeroCapacity)); + } + + #[test] + fn rejects_capacity_over_limit() { + let err = SourceRing::create(AUDIO_RING_CAP_FRAMES_MAX + 1, 48_000).err(); + assert!(matches!( + err, + Some(AudioMixError::CapacityExceedsLimit { .. }) + )); + } + + #[test] + fn rejects_sample_rate_below_min() { + let err = SourceRing::create(AUDIO_RING_CAP_FRAMES, 4_000).err(); + assert!(matches!( + err, + Some(AudioMixError::SampleRateOutOfRange { .. }) + )); + } + + #[test] + fn rejects_sample_rate_above_max() { + let err = SourceRing::create(AUDIO_RING_CAP_FRAMES, 500_000).err(); + assert!(matches!( + err, + Some(AudioMixError::SampleRateOutOfRange { .. }) + )); + } + + #[test] + fn default_capacity_pair_matches() { + let (producer, consumer) = SourceRing::create_default(48_000).expect("pair"); + assert_eq!(producer.sample_rate_hz(), 48_000); + assert_eq!(consumer.sample_rate_hz(), 48_000); + assert_eq!(producer.capacity_frames(), AUDIO_RING_CAP_FRAMES); + assert_eq!(consumer.capacity_frames(), AUDIO_RING_CAP_FRAMES); + } + + #[test] + fn push_then_drain_roundtrips() { + let (mut producer, mut consumer) = SourceRing::create(64, 48_000).expect("pair"); + let samples: Vec = (0..32).map(|n| n as i16).collect(); + let pushed = producer.try_push_slice(&samples); + assert_eq!(pushed, 32); + let mut buf = [0i16; 32]; + let drained = consumer.drain_into(&mut buf); + assert_eq!(drained, 32); + assert_eq!(&buf[..], &samples[..]); + assert_eq!(producer.pushed_total(), 32); + assert_eq!(consumer.drained_total(), 32); + } + + #[test] + fn try_push_returns_false_when_full_and_increments_dropped() { + let (mut producer, _consumer) = SourceRing::create(2, 48_000).expect("pair"); + assert!(producer.try_push(1)); + assert!(producer.try_push(2)); + assert!(!producer.try_push(3)); + assert!(producer.is_full()); + assert_eq!(producer.dropped_total(), 1); + assert_eq!(producer.pushed_total(), 2); + } + + #[test] + fn try_push_slice_partial_when_capacity_runs_out() { + let (mut producer, _consumer) = SourceRing::create(4, 48_000).expect("pair"); + let pushed = producer.try_push_slice(&[10, 20, 30, 40]); + assert_eq!(pushed, 4); + let pushed_again = producer.try_push_slice(&[50, 60]); + assert_eq!(pushed_again, 0); + assert_eq!(producer.dropped_total(), 2); + } + + #[test] + fn drain_into_handles_empty_ring() { + let (_producer, mut consumer) = SourceRing::create(64, 48_000).expect("pair"); + let mut buf = [0i16; 16]; + let drained = consumer.drain_into(&mut buf); + assert_eq!(drained, 0); + assert!(consumer.is_empty()); + } +} diff --git a/fluxer_desktop/native/audio-timing/Cargo.lock b/fluxer_desktop/native/audio-timing/Cargo.lock new file mode 100644 index 0000000..a9f9a81 --- /dev/null +++ b/fluxer_desktop/native/audio-timing/Cargo.lock @@ -0,0 +1,643 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "criterion" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3" +dependencies = [ + "alloca", + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "num-traits", + "oorandom", + "page_size", + "plotters", + "rayon", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fluxer_audio_timing" +version = "0.0.0" +dependencies = [ + "criterion", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0871acf327f283dc6da28a1696cdc64fb355ba9f935d052021fa77f35cce69" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zerocopy" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/fluxer_desktop/native/audio-timing/Cargo.toml b/fluxer_desktop/native/audio-timing/Cargo.toml new file mode 100644 index 0000000..3b7e9a3 --- /dev/null +++ b/fluxer_desktop/native/audio-timing/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "fluxer_audio_timing" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["rlib"] + +[dependencies] + +[dev-dependencies] +criterion = "0.8" + +[[bench]] +name = "smoother" +harness = false diff --git a/fluxer_desktop/native/audio-timing/benches/smoother.rs b/fluxer_desktop/native/audio-timing/benches/smoother.rs new file mode 100644 index 0000000..b3441fe --- /dev/null +++ b/fluxer_desktop/native/audio-timing/benches/smoother.rs @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::hint::black_box; + +use criterion::{Criterion, criterion_group, criterion_main}; +use fluxer_audio_timing::{AudioTimingSmoother, SmootherInput, SourceGainRamp, StaleSourceTracker}; + +const SR: u32 = 48_000; + +const FRAMES_PER_TICK: u32 = 960; + +const PERIOD_NS: u64 = 20_000_000; + +fn bench_smoother_steady_snap(c: &mut Criterion) { + let mut group = c.benchmark_group("audio_timing_smoother"); + group.sample_size(100); + group.bench_function("snap_steady_state", |b| { + let mut s = AudioTimingSmoother::new(1, SR).expect("ok"); + let _ = s + .adjust(SmootherInput { + media_ts_ns: 0, + frames: FRAMES_PER_TICK, + wall_ns: 0, + }) + .expect("ok"); + let mut n: u64 = 1; + b.iter(|| { + let result = s + .adjust(SmootherInput { + media_ts_ns: n * PERIOD_NS, + frames: FRAMES_PER_TICK, + wall_ns: n * PERIOD_NS, + }) + .expect("ok"); + n = n.wrapping_add(1); + black_box(result); + }); + }); + group.finish(); +} + +fn bench_ramp_advance(c: &mut Criterion) { + let mut group = c.benchmark_group("audio_timing_ramp"); + group.sample_size(100); + group.bench_function("advance_and_query_gain", |b| { + let mut r = SourceGainRamp::new(); + b.iter(|| { + r.advance_tick(); + let g = r.current_gain_q15(); + if r.is_complete() { + r.reset(); + } + black_box(g); + }); + }); + group.finish(); +} + +fn bench_stale_check(c: &mut Criterion) { + let mut group = c.benchmark_group("audio_timing_eviction"); + group.sample_size(100); + group.bench_function("is_stale_lookup", |b| { + let mut t = StaleSourceTracker::new(16).expect("ok"); + for n in 1..=16u64 { + t.register_source(n, n * 1_000).expect("ok"); + } + let threshold = 5_000_000_000u64; + let mut now: u64 = 1_000_000_000; + b.iter(|| { + let stale = t.is_stale(8, now, threshold).expect("ok"); + now = now.wrapping_add(PERIOD_NS); + black_box(stale); + }); + }); + group.finish(); +} + +criterion_group!( + benches, + bench_smoother_steady_snap, + bench_ramp_advance, + bench_stale_check +); +criterion_main!(benches); diff --git a/fluxer_desktop/native/audio-timing/src/eviction.rs b/fluxer_desktop/native/audio-timing/src/eviction.rs new file mode 100644 index 0000000..5ee243a --- /dev/null +++ b/fluxer_desktop/native/audio-timing/src/eviction.rs @@ -0,0 +1,488 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub const EVICTION_NEVER_PUSHED_SENTINEL: u64 = u64::MAX; + +pub const MAX_TRACKED_SOURCES: usize = 256; + +const STALE_THRESHOLD_NS_MIN: u64 = 1_000_000; + +const STALE_THRESHOLD_NS_MAX: u64 = 60_000_000_000; + +#[derive(Debug, PartialEq, Eq)] +pub enum StaleSourceTrackerError { + CapacityExceeded { capacity: usize }, + SourceIdZero, + UnknownSource { source_id: u64 }, + StaleThresholdOutOfRange { stale_threshold_ns: u64 }, +} + +impl core::fmt::Display for StaleSourceTrackerError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + StaleSourceTrackerError::CapacityExceeded { capacity } => { + write!(f, "tracker capacity {capacity} exceeded") + } + StaleSourceTrackerError::SourceIdZero => write!(f, "source_id must be non-zero"), + StaleSourceTrackerError::UnknownSource { source_id } => { + write!(f, "source_id {source_id} not registered") + } + StaleSourceTrackerError::StaleThresholdOutOfRange { stale_threshold_ns } => { + write!( + f, + "stale threshold {stale_threshold_ns} ns outside accepted range" + ) + } + } + } +} + +impl std::error::Error for StaleSourceTrackerError {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct StaleSourceEntry { + pub source_id: u64, + pub registered_at_ns: u64, + pub last_active_ns: u64, +} + +impl StaleSourceEntry { + pub fn ever_pushed(&self) -> bool { + self.last_active_ns != EVICTION_NEVER_PUSHED_SENTINEL + } +} + +pub struct StaleSourceTracker { + entries: Vec, + capacity: usize, +} + +impl StaleSourceTracker { + pub fn new(capacity: usize) -> Result { + if capacity == 0 || capacity > MAX_TRACKED_SOURCES { + return Err(StaleSourceTrackerError::CapacityExceeded { capacity }); + } + assert!(capacity > 0); + assert!(capacity <= MAX_TRACKED_SOURCES); + Ok(Self { + entries: Vec::with_capacity(capacity), + capacity, + }) + } + + pub fn len(&self) -> usize { + assert!(self.entries.len() <= self.capacity); + self.entries.len() + } + + pub fn is_empty(&self) -> bool { + self.entries.is_empty() + } + + pub fn capacity(&self) -> usize { + assert!(self.capacity > 0); + assert!(self.capacity <= MAX_TRACKED_SOURCES); + self.capacity + } + + pub fn register_source( + &mut self, + source_id: u64, + registered_at_ns: u64, + ) -> Result<(), StaleSourceTrackerError> { + if source_id == 0 { + return Err(StaleSourceTrackerError::SourceIdZero); + } + if self.entries.len() >= self.capacity { + return Err(StaleSourceTrackerError::CapacityExceeded { + capacity: self.capacity, + }); + } + assert!(source_id != 0); + if let Some(existing) = self.find_mut(source_id) { + existing.registered_at_ns = registered_at_ns; + existing.last_active_ns = EVICTION_NEVER_PUSHED_SENTINEL; + return Ok(()); + } + self.entries.push(StaleSourceEntry { + source_id, + registered_at_ns, + last_active_ns: EVICTION_NEVER_PUSHED_SENTINEL, + }); + assert!(self.entries.len() <= self.capacity); + Ok(()) + } + + pub fn unregister_source(&mut self, source_id: u64) -> Result<(), StaleSourceTrackerError> { + if source_id == 0 { + return Err(StaleSourceTrackerError::SourceIdZero); + } + assert!(source_id != 0); + let before = self.entries.len(); + self.entries.retain(|e| e.source_id != source_id); + if self.entries.len() == before { + return Err(StaleSourceTrackerError::UnknownSource { source_id }); + } + assert!(self.entries.len() < before); + Ok(()) + } + + pub fn mark_pushed( + &mut self, + source_id: u64, + ts_ns: u64, + ) -> Result<(), StaleSourceTrackerError> { + if source_id == 0 { + return Err(StaleSourceTrackerError::SourceIdZero); + } + assert!(source_id != 0); + let Some(entry) = self.find_mut(source_id) else { + return Err(StaleSourceTrackerError::UnknownSource { source_id }); + }; + entry.last_active_ns = ts_ns; + assert!(entry.last_active_ns == ts_ns); + Ok(()) + } + + pub fn mark_pushed_at( + &mut self, + index: usize, + source_id: u64, + ts_ns: u64, + ) -> Result<(), StaleSourceTrackerError> { + if source_id == 0 { + return Err(StaleSourceTrackerError::SourceIdZero); + } + assert!(source_id != 0); + assert!(self.entries.len() <= self.capacity); + if index >= self.entries.len() { + return Err(StaleSourceTrackerError::UnknownSource { source_id }); + } + if self.entries[index].source_id != source_id { + return Err(StaleSourceTrackerError::UnknownSource { source_id }); + } + self.entries[index].last_active_ns = ts_ns; + assert!(self.entries[index].last_active_ns == ts_ns); + Ok(()) + } + + pub fn is_stale( + &self, + source_id: u64, + now_ns: u64, + stale_threshold_ns: u64, + ) -> Result { + if source_id == 0 { + return Err(StaleSourceTrackerError::SourceIdZero); + } + if !(STALE_THRESHOLD_NS_MIN..=STALE_THRESHOLD_NS_MAX).contains(&stale_threshold_ns) { + return Err(StaleSourceTrackerError::StaleThresholdOutOfRange { stale_threshold_ns }); + } + assert!(source_id != 0); + let Some(entry) = self.find(source_id) else { + return Err(StaleSourceTrackerError::UnknownSource { source_id }); + }; + Ok(Self::entry_is_stale(entry, now_ns, stale_threshold_ns)) + } + + pub fn is_stale_at( + &self, + index: usize, + source_id: u64, + now_ns: u64, + stale_threshold_ns: u64, + ) -> Result { + if source_id == 0 { + return Err(StaleSourceTrackerError::SourceIdZero); + } + if !(STALE_THRESHOLD_NS_MIN..=STALE_THRESHOLD_NS_MAX).contains(&stale_threshold_ns) { + return Err(StaleSourceTrackerError::StaleThresholdOutOfRange { stale_threshold_ns }); + } + assert!(source_id != 0); + assert!(self.entries.len() <= self.capacity); + if index >= self.entries.len() { + return Err(StaleSourceTrackerError::UnknownSource { source_id }); + } + let entry = &self.entries[index]; + if entry.source_id != source_id { + return Err(StaleSourceTrackerError::UnknownSource { source_id }); + } + Ok(Self::entry_is_stale(entry, now_ns, stale_threshold_ns)) + } + + fn entry_is_stale(entry: &StaleSourceEntry, now_ns: u64, stale_threshold_ns: u64) -> bool { + assert!(entry.source_id != 0); + assert!(stale_threshold_ns >= STALE_THRESHOLD_NS_MIN); + assert!(stale_threshold_ns <= STALE_THRESHOLD_NS_MAX); + let baseline_ns = if entry.last_active_ns == EVICTION_NEVER_PUSHED_SENTINEL { + entry.registered_at_ns + } else { + entry.last_active_ns + }; + if now_ns <= baseline_ns { + return false; + } + let age_ns = now_ns - baseline_ns; + age_ns > stale_threshold_ns + } + + pub fn age_ns(&self, source_id: u64, now_ns: u64) -> Result { + if source_id == 0 { + return Err(StaleSourceTrackerError::SourceIdZero); + } + assert!(source_id != 0); + let Some(entry) = self.find(source_id) else { + return Err(StaleSourceTrackerError::UnknownSource { source_id }); + }; + let baseline_ns = if entry.last_active_ns == EVICTION_NEVER_PUSHED_SENTINEL { + entry.registered_at_ns + } else { + entry.last_active_ns + }; + if now_ns <= baseline_ns { + return Ok(0); + } + Ok(now_ns - baseline_ns) + } + + pub fn entry(&self, source_id: u64) -> Option { + self.find(source_id).copied() + } + + fn find(&self, source_id: u64) -> Option<&StaleSourceEntry> { + self.entries.iter().find(|e| e.source_id == source_id) + } + + fn find_mut(&mut self, source_id: u64) -> Option<&mut StaleSourceEntry> { + self.entries.iter_mut().find(|e| e.source_id == source_id) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn unregistered_source_is_error() { + let tracker = StaleSourceTracker::new(4).expect("ok"); + let err = tracker.is_stale(7, 1_000_000_000, 5_000_000_000).err(); + assert!(matches!( + err, + Some(StaleSourceTrackerError::UnknownSource { source_id: 7 }) + )); + } + + #[test] + fn registered_source_starts_not_stale_at_registration_time() { + let mut t = StaleSourceTracker::new(4).expect("ok"); + t.register_source(1, 1_000_000).expect("ok"); + let stale = t.is_stale(1, 1_000_000, 5_000_000_000).expect("ok"); + assert!(!stale); + } + + #[test] + fn registered_but_never_pushed_becomes_stale_past_threshold() { + let mut t = StaleSourceTracker::new(4).expect("ok"); + t.register_source(1, 1_000_000).expect("ok"); + let threshold = 5_000_000_000; + let now = 1_000_000 + threshold + 1; + let stale = t.is_stale(1, now, threshold).expect("ok"); + assert!(stale, "never-pushed source must go stale past threshold"); + } + + #[test] + fn registered_but_never_pushed_within_window_is_fresh() { + let mut t = StaleSourceTracker::new(4).expect("ok"); + t.register_source(1, 1_000_000).expect("ok"); + let threshold = 5_000_000_000; + let now = 1_000_000 + threshold - 1; + let stale = t.is_stale(1, now, threshold).expect("ok"); + assert!(!stale); + } + + #[test] + fn marking_pushed_extends_freshness() { + let mut t = StaleSourceTracker::new(4).expect("ok"); + t.register_source(2, 0).expect("ok"); + t.mark_pushed(2, 1_000_000_000).expect("ok"); + let threshold = 500_000_000; + let now = 1_000_000_000 + threshold + 1; + let stale = t.is_stale(2, now, threshold).expect("ok"); + assert!(stale); + let now_within = 1_000_000_000 + threshold - 1; + let fresh = t.is_stale(2, now_within, threshold).expect("ok"); + assert!(!fresh); + } + + #[test] + fn unregister_removes_entry() { + let mut t = StaleSourceTracker::new(4).expect("ok"); + t.register_source(3, 0).expect("ok"); + assert_eq!(t.len(), 1); + t.unregister_source(3).expect("ok"); + assert_eq!(t.len(), 0); + } + + #[test] + fn capacity_exceeded_rejected() { + let mut t = StaleSourceTracker::new(2).expect("ok"); + t.register_source(1, 0).expect("ok"); + t.register_source(2, 0).expect("ok"); + let err = t.register_source(3, 0).err(); + assert!(matches!( + err, + Some(StaleSourceTrackerError::CapacityExceeded { .. }) + )); + } + + #[test] + fn age_ns_uses_registration_for_never_pushed() { + let mut t = StaleSourceTracker::new(4).expect("ok"); + t.register_source(1, 1_000).expect("ok"); + let age = t.age_ns(1, 5_000).expect("ok"); + assert_eq!(age, 4_000); + } + + #[test] + fn age_ns_uses_last_active_after_push() { + let mut t = StaleSourceTracker::new(4).expect("ok"); + t.register_source(1, 1_000).expect("ok"); + t.mark_pushed(1, 2_000).expect("ok"); + let age = t.age_ns(1, 5_000).expect("ok"); + assert_eq!(age, 3_000); + } + + #[test] + fn determinism_two_runs_produce_same_decisions() { + let run = || { + let mut t = StaleSourceTracker::new(8).expect("ok"); + t.register_source(1, 0).expect("ok"); + t.register_source(2, 0).expect("ok"); + t.mark_pushed(2, 1_000_000_000).expect("ok"); + let threshold = 2_000_000_000; + let out: Vec = vec![ + t.is_stale(1, 3_000_000_000, threshold).expect("ok"), + t.is_stale(2, 3_000_000_000, threshold).expect("ok"), + ]; + out + }; + let a = run(); + let b = run(); + assert_eq!(a, b); + } + + #[test] + fn rejects_invalid_threshold() { + let mut t = StaleSourceTracker::new(4).expect("ok"); + t.register_source(1, 0).expect("ok"); + let err = t.is_stale(1, 1_000_000, 0).err(); + assert!(matches!( + err, + Some(StaleSourceTrackerError::StaleThresholdOutOfRange { .. }) + )); + } + + #[test] + fn rejects_zero_source_id() { + let mut t = StaleSourceTracker::new(4).expect("ok"); + let err = t.register_source(0, 0).err(); + assert!(matches!(err, Some(StaleSourceTrackerError::SourceIdZero))); + } + + #[test] + fn mark_pushed_at_succeeds_when_index_and_id_agree() { + let mut t = StaleSourceTracker::new(4).expect("ok"); + t.register_source(10, 0).expect("ok"); + t.register_source(20, 0).expect("ok"); + t.mark_pushed_at(1, 20, 2_000).expect("ok"); + let e = t.entry(20).expect("entry"); + assert_eq!(e.last_active_ns, 2_000); + let untouched = t.entry(10).expect("entry"); + assert!(!untouched.ever_pushed()); + } + + #[test] + fn mark_pushed_at_rejects_index_id_mismatch() { + let mut t = StaleSourceTracker::new(4).expect("ok"); + t.register_source(10, 0).expect("ok"); + t.register_source(20, 0).expect("ok"); + let err = t.mark_pushed_at(0, 20, 2_000).err(); + assert!(matches!( + err, + Some(StaleSourceTrackerError::UnknownSource { source_id: 20 }) + )); + assert!(!t.entry(10).expect("entry").ever_pushed()); + assert!(!t.entry(20).expect("entry").ever_pushed()); + } + + #[test] + fn mark_pushed_at_rejects_out_of_range_index() { + let mut t = StaleSourceTracker::new(4).expect("ok"); + t.register_source(10, 0).expect("ok"); + let err = t.mark_pushed_at(1, 10, 2_000).err(); + assert!(matches!( + err, + Some(StaleSourceTrackerError::UnknownSource { source_id: 10 }) + )); + } + + #[test] + fn mark_pushed_at_rejects_zero_source_id() { + let mut t = StaleSourceTracker::new(4).expect("ok"); + t.register_source(10, 0).expect("ok"); + let err = t.mark_pushed_at(0, 0, 2_000).err(); + assert!(matches!(err, Some(StaleSourceTrackerError::SourceIdZero))); + } + + #[test] + fn is_stale_at_matches_is_stale() { + let mut t = StaleSourceTracker::new(4).expect("ok"); + t.register_source(10, 0).expect("ok"); + t.register_source(20, 0).expect("ok"); + t.mark_pushed_at(1, 20, 1_000_000_000).expect("ok"); + let threshold = 2_000_000_000; + for now in [500_000_000, 2_500_000_000, 4_000_000_000] { + let by_id = t.is_stale(20, now, threshold).expect("ok"); + let by_index = t.is_stale_at(1, 20, now, threshold).expect("ok"); + assert_eq!(by_id, by_index); + } + } + + #[test] + fn is_stale_at_rejects_index_id_mismatch() { + let mut t = StaleSourceTracker::new(4).expect("ok"); + t.register_source(10, 0).expect("ok"); + t.register_source(20, 0).expect("ok"); + let err = t.is_stale_at(0, 20, 3_000_000_000, 2_000_000_000).err(); + assert!(matches!( + err, + Some(StaleSourceTrackerError::UnknownSource { source_id: 20 }) + )); + let err = t.is_stale_at(5, 10, 3_000_000_000, 2_000_000_000).err(); + assert!(matches!( + err, + Some(StaleSourceTrackerError::UnknownSource { source_id: 10 }) + )); + } + + #[test] + fn is_stale_at_rejects_invalid_threshold() { + let mut t = StaleSourceTracker::new(4).expect("ok"); + t.register_source(10, 0).expect("ok"); + let err = t.is_stale_at(0, 10, 1_000_000, 0).err(); + assert!(matches!( + err, + Some(StaleSourceTrackerError::StaleThresholdOutOfRange { .. }) + )); + } + + #[test] + fn ever_pushed_flag_reflects_state() { + let mut t = StaleSourceTracker::new(4).expect("ok"); + t.register_source(1, 10).expect("ok"); + let e = t.entry(1).expect("entry"); + assert!(!e.ever_pushed()); + t.mark_pushed(1, 100).expect("ok"); + let e2 = t.entry(1).expect("entry"); + assert!(e2.ever_pushed()); + } +} diff --git a/fluxer_desktop/native/audio-timing/src/lib.rs b/fluxer_desktop/native/audio-timing/src/lib.rs new file mode 100644 index 0000000..1dc64fa --- /dev/null +++ b/fluxer_desktop/native/audio-timing/src/lib.rs @@ -0,0 +1,17 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![deny(clippy::too_many_lines)] + +pub mod eviction; +pub mod ramping; +pub mod smoother; + +pub use eviction::{ + EVICTION_NEVER_PUSHED_SENTINEL, MAX_TRACKED_SOURCES, StaleSourceEntry, StaleSourceTracker, + StaleSourceTrackerError, +}; +pub use ramping::{RAMP_GAIN_DENOMINATOR_Q15, RAMP_IN_TICKS_DEFAULT, RampError, SourceGainRamp}; +pub use smoother::{ + AudioTimingError, AudioTimingSmoother, MAX_TS_VAR_NS, NS_PER_SECOND, SmootherAction, + SmootherEvent, SmootherInput, SmootherResetReason, SmootherResult, TS_SMOOTHING_THRESHOLD_NS, +}; diff --git a/fluxer_desktop/native/audio-timing/src/ramping.rs b/fluxer_desktop/native/audio-timing/src/ramping.rs new file mode 100644 index 0000000..1fd7b4b --- /dev/null +++ b/fluxer_desktop/native/audio-timing/src/ramping.rs @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub const RAMP_IN_TICKS_DEFAULT: u32 = 5; + +pub const RAMP_GAIN_DENOMINATOR_Q15: i32 = 32_768; + +const RAMP_IN_TICKS_MIN: u32 = 1; +const RAMP_IN_TICKS_MAX: u32 = 1_024; + +#[derive(Debug, PartialEq, Eq)] +pub enum RampError { + RampInTicksOutOfRange { ramp_in_ticks: u32 }, +} + +impl core::fmt::Display for RampError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + RampError::RampInTicksOutOfRange { ramp_in_ticks } => { + write!(f, "ramp_in_ticks {ramp_in_ticks} outside accepted range") + } + } + } +} + +impl std::error::Error for RampError {} + +pub struct SourceGainRamp { + tick_count: u32, + ramp_in_ticks: u32, +} + +impl SourceGainRamp { + pub fn new() -> Self { + Self { + tick_count: 0, + ramp_in_ticks: RAMP_IN_TICKS_DEFAULT, + } + } + + pub fn with_ramp_in_ticks(ramp_in_ticks: u32) -> Result { + if !(RAMP_IN_TICKS_MIN..=RAMP_IN_TICKS_MAX).contains(&ramp_in_ticks) { + return Err(RampError::RampInTicksOutOfRange { ramp_in_ticks }); + } + assert!(ramp_in_ticks >= RAMP_IN_TICKS_MIN); + assert!(ramp_in_ticks <= RAMP_IN_TICKS_MAX); + Ok(Self { + tick_count: 0, + ramp_in_ticks, + }) + } + + pub fn tick_count(&self) -> u32 { + self.tick_count + } + + pub fn ramp_in_ticks(&self) -> u32 { + assert!(self.ramp_in_ticks >= RAMP_IN_TICKS_MIN); + assert!(self.ramp_in_ticks <= RAMP_IN_TICKS_MAX); + self.ramp_in_ticks + } + + pub fn is_complete(&self) -> bool { + self.tick_count >= self.ramp_in_ticks + } + + pub fn current_gain(&self) -> f32 { + assert!(self.ramp_in_ticks >= RAMP_IN_TICKS_MIN); + let clamped = self.tick_count.min(self.ramp_in_ticks); + let g = (clamped as f32) / (self.ramp_in_ticks as f32); + assert!(g >= 0.0); + assert!(g <= 1.0); + g + } + + pub fn current_gain_q15(&self) -> i32 { + assert!(self.ramp_in_ticks >= RAMP_IN_TICKS_MIN); + let clamped = self.tick_count.min(self.ramp_in_ticks); + let num = (clamped as i64) * (RAMP_GAIN_DENOMINATOR_Q15 as i64); + let den = self.ramp_in_ticks as i64; + let q = num / den; + assert!(q >= 0); + assert!(q <= RAMP_GAIN_DENOMINATOR_Q15 as i64); + q as i32 + } + + pub fn advance_tick(&mut self) { + let before = self.tick_count; + self.tick_count = self.tick_count.saturating_add(1); + assert!(self.tick_count >= before); + assert!(self.tick_count >= 1); + } + + pub fn reset(&mut self) { + self.tick_count = 0; + assert_eq!(self.tick_count, 0); + assert!(self.ramp_in_ticks >= RAMP_IN_TICKS_MIN); + } +} + +impl Default for SourceGainRamp { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn fresh_ramp_starts_at_zero_gain() { + let r = SourceGainRamp::new(); + assert_eq!(r.tick_count(), 0); + assert_eq!(r.current_gain(), 0.0); + assert!(!r.is_complete()); + } + + #[test] + fn five_ticks_increase_monotonically_to_one() { + let mut r = SourceGainRamp::new(); + let mut prior = -1.0f32; + for expected_n in 1..=5u32 { + r.advance_tick(); + let g = r.current_gain(); + assert!(g > prior); + assert!(g >= 0.0); + assert!(g <= 1.0); + assert_eq!(r.tick_count(), expected_n); + prior = g; + } + assert_eq!(r.current_gain(), 1.0); + assert!(r.is_complete()); + } + + #[test] + fn gain_clamps_to_one_after_completion() { + let mut r = SourceGainRamp::new(); + for _ in 0..20 { + r.advance_tick(); + } + assert_eq!(r.current_gain(), 1.0); + assert!(r.is_complete()); + } + + #[test] + fn first_five_gains_are_quintiles() { + let mut r = SourceGainRamp::new(); + let mut observed: Vec = Vec::with_capacity(5); + for _ in 0..5 { + r.advance_tick(); + observed.push(r.current_gain()); + } + assert_eq!(observed.len(), 5); + assert!((observed[0] - 0.2).abs() < f32::EPSILON); + assert!((observed[1] - 0.4).abs() < f32::EPSILON * 4.0); + assert!((observed[2] - 0.6).abs() < f32::EPSILON * 4.0); + assert!((observed[3] - 0.8).abs() < f32::EPSILON * 4.0); + assert_eq!(observed[4], 1.0); + } + + #[test] + fn q15_gain_at_completion_equals_denominator() { + let mut r = SourceGainRamp::new(); + for _ in 0..5 { + r.advance_tick(); + } + assert_eq!(r.current_gain_q15(), RAMP_GAIN_DENOMINATOR_Q15); + } + + #[test] + fn q15_gain_starts_at_zero() { + let r = SourceGainRamp::new(); + assert_eq!(r.current_gain_q15(), 0); + } + + #[test] + fn reset_returns_ramp_to_zero_gain() { + let mut r = SourceGainRamp::new(); + for _ in 0..10 { + r.advance_tick(); + } + assert_eq!(r.current_gain(), 1.0); + r.reset(); + assert_eq!(r.tick_count(), 0); + assert_eq!(r.current_gain(), 0.0); + } + + #[test] + fn custom_ramp_in_ticks_validates() { + let r = SourceGainRamp::with_ramp_in_ticks(10).expect("ok"); + assert_eq!(r.ramp_in_ticks(), 10); + let err = SourceGainRamp::with_ramp_in_ticks(0).err(); + assert!(matches!(err, Some(RampError::RampInTicksOutOfRange { .. }))); + let err2 = SourceGainRamp::with_ramp_in_ticks(10_000).err(); + assert!(matches!( + err2, + Some(RampError::RampInTicksOutOfRange { .. }) + )); + } + + #[test] + fn determinism_two_ramps_produce_same_sequence() { + let run = || { + let mut r = SourceGainRamp::new(); + let mut g: Vec = Vec::with_capacity(7); + for _ in 0..7 { + g.push(r.current_gain_q15()); + r.advance_tick(); + } + g + }; + let a = run(); + let b = run(); + assert_eq!(a, b); + } +} diff --git a/fluxer_desktop/native/audio-timing/src/smoother.rs b/fluxer_desktop/native/audio-timing/src/smoother.rs new file mode 100644 index 0000000..c026841 --- /dev/null +++ b/fluxer_desktop/native/audio-timing/src/smoother.rs @@ -0,0 +1,579 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub const TS_SMOOTHING_THRESHOLD_NS: u64 = 70_000_000; + +pub const MAX_TS_VAR_NS: u64 = 2_000_000_000; + +pub const NS_PER_SECOND: u64 = 1_000_000_000; + +const SAMPLE_RATE_HZ_MIN: u32 = 8_000; + +const SAMPLE_RATE_HZ_MAX: u32 = 384_000; + +const FRAMES_PER_INPUT_MAX: u32 = 1 << 24; + +#[derive(Debug, PartialEq, Eq)] +pub enum AudioTimingError { + SampleRateOutOfRange { sample_rate_hz: u32 }, + SourceIdZero, + FramesOutOfRange { frames: u32 }, +} + +impl core::fmt::Display for AudioTimingError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + AudioTimingError::SampleRateOutOfRange { sample_rate_hz } => { + write!(f, "sample rate {sample_rate_hz} hz outside accepted range") + } + AudioTimingError::SourceIdZero => write!(f, "source_id must be non-zero"), + AudioTimingError::FramesOutOfRange { frames } => { + write!(f, "frames {frames} outside accepted range") + } + } + } +} + +impl std::error::Error for AudioTimingError {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SmootherInput { + pub media_ts_ns: u64, + pub frames: u32, + pub wall_ns: u64, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SmootherAction { + Snap, + Jump, + Reset, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SmootherResetReason { + FirstSample, + GapBeyondMaxVar, + Regression, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SmootherEvent { + Snapped { + source_id: u64, + incoming_media_ts_ns: u64, + snapped_ts_ns: u64, + delta_ns: i64, + frames_accumulated: u64, + }, + Jumped { + source_id: u64, + incoming_media_ts_ns: u64, + previous_predicted_ts_ns: u64, + delta_ns: i64, + frames_accumulated: u64, + }, + Reset { + source_id: u64, + incoming_media_ts_ns: u64, + wall_ns: u64, + timing_adjust_ns: i64, + frames_accumulated: u64, + reason: SmootherResetReason, + }, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SmootherResult { + pub action: SmootherAction, + pub output_ts_ns: u64, + pub predicted_next_ts_ns: u64, + pub frames_accumulated: u64, + pub event: SmootherEvent, +} + +pub struct AudioTimingSmoother { + source_id: u64, + sample_rate_hz: u32, + initialised: bool, + predicted_next_ts_ns: u64, + timing_adjust_ns: i64, + frames_accumulated: u64, + consecutive_jumps: u32, +} + +impl AudioTimingSmoother { + pub fn new(source_id: u64, sample_rate_hz: u32) -> Result { + if source_id == 0 { + return Err(AudioTimingError::SourceIdZero); + } + if !(SAMPLE_RATE_HZ_MIN..=SAMPLE_RATE_HZ_MAX).contains(&sample_rate_hz) { + return Err(AudioTimingError::SampleRateOutOfRange { sample_rate_hz }); + } + assert!(source_id != 0); + assert!(sample_rate_hz >= SAMPLE_RATE_HZ_MIN); + assert!(sample_rate_hz <= SAMPLE_RATE_HZ_MAX); + Ok(Self { + source_id, + sample_rate_hz, + initialised: false, + predicted_next_ts_ns: 0, + timing_adjust_ns: 0, + frames_accumulated: 0, + consecutive_jumps: 0, + }) + } + + pub fn source_id(&self) -> u64 { + assert!(self.source_id != 0); + self.source_id + } + + pub fn sample_rate_hz(&self) -> u32 { + assert!(self.sample_rate_hz >= SAMPLE_RATE_HZ_MIN); + assert!(self.sample_rate_hz <= SAMPLE_RATE_HZ_MAX); + self.sample_rate_hz + } + + pub fn initialised(&self) -> bool { + self.initialised + } + + pub fn predicted_next_ts_ns(&self) -> u64 { + self.predicted_next_ts_ns + } + + pub fn timing_adjust_ns(&self) -> i64 { + self.timing_adjust_ns + } + + pub fn frames_accumulated(&self) -> u64 { + self.frames_accumulated + } + + pub fn consecutive_jumps(&self) -> u32 { + self.consecutive_jumps + } + + pub fn adjust(&mut self, input: SmootherInput) -> Result { + if input.frames == 0 || input.frames > FRAMES_PER_INPUT_MAX { + return Err(AudioTimingError::FramesOutOfRange { + frames: input.frames, + }); + } + assert!(input.frames >= 1); + assert!(input.frames <= FRAMES_PER_INPUT_MAX); + if !self.initialised { + return Ok(self.initialise_from_first_sample(input)); + } + Ok(self.adjust_initialised(input)) + } + + fn initialise_from_first_sample(&mut self, input: SmootherInput) -> SmootherResult { + assert!(!self.initialised); + assert_eq!(self.frames_accumulated, 0); + let increment_ns = frames_to_ns(input.frames as u64, self.sample_rate_hz); + let predicted_next = input.media_ts_ns.saturating_add(increment_ns); + let timing_adjust = wall_minus_media_signed(input.wall_ns, input.media_ts_ns); + self.frames_accumulated = input.frames as u64; + self.predicted_next_ts_ns = predicted_next; + self.timing_adjust_ns = timing_adjust; + self.initialised = true; + self.consecutive_jumps = 0; + assert!(predicted_next >= input.media_ts_ns); + let event = SmootherEvent::Reset { + source_id: self.source_id, + incoming_media_ts_ns: input.media_ts_ns, + wall_ns: input.wall_ns, + timing_adjust_ns: timing_adjust, + frames_accumulated: self.frames_accumulated, + reason: SmootherResetReason::FirstSample, + }; + SmootherResult { + action: SmootherAction::Reset, + output_ts_ns: input.media_ts_ns, + predicted_next_ts_ns: predicted_next, + frames_accumulated: self.frames_accumulated, + event, + } + } + + fn adjust_initialised(&mut self, input: SmootherInput) -> SmootherResult { + assert!(self.initialised); + let expected = self.predicted_next_ts_ns; + let delta = signed_delta(input.media_ts_ns, expected); + let absolute_delta = delta.unsigned_abs(); + if absolute_delta <= TS_SMOOTHING_THRESHOLD_NS { + return self.apply_snap(input, expected, delta); + } + if absolute_delta <= MAX_TS_VAR_NS { + return self.apply_jump(input, expected, delta); + } + self.apply_full_reset(input, SmootherResetReason::GapBeyondMaxVar) + } + + fn apply_snap(&mut self, input: SmootherInput, expected: u64, delta: i64) -> SmootherResult { + assert!(self.initialised); + let increment_ns = frames_to_ns(input.frames as u64, self.sample_rate_hz); + let new_predicted = expected.saturating_add(increment_ns); + self.frames_accumulated = self.frames_accumulated.saturating_add(input.frames as u64); + self.predicted_next_ts_ns = new_predicted; + self.consecutive_jumps = 0; + assert!(new_predicted >= expected); + let event = SmootherEvent::Snapped { + source_id: self.source_id, + incoming_media_ts_ns: input.media_ts_ns, + snapped_ts_ns: expected, + delta_ns: delta, + frames_accumulated: self.frames_accumulated, + }; + SmootherResult { + action: SmootherAction::Snap, + output_ts_ns: expected, + predicted_next_ts_ns: new_predicted, + frames_accumulated: self.frames_accumulated, + event, + } + } + + fn apply_jump(&mut self, input: SmootherInput, expected: u64, delta: i64) -> SmootherResult { + assert!(self.initialised); + if delta < 0 { + return self.apply_full_reset(input, SmootherResetReason::Regression); + } + let increment_ns = frames_to_ns(input.frames as u64, self.sample_rate_hz); + let new_predicted = input.media_ts_ns.saturating_add(increment_ns); + self.frames_accumulated = self.frames_accumulated.saturating_add(input.frames as u64); + self.predicted_next_ts_ns = new_predicted; + self.consecutive_jumps = self.consecutive_jumps.saturating_add(1); + assert!(new_predicted >= input.media_ts_ns); + let event = SmootherEvent::Jumped { + source_id: self.source_id, + incoming_media_ts_ns: input.media_ts_ns, + previous_predicted_ts_ns: expected, + delta_ns: delta, + frames_accumulated: self.frames_accumulated, + }; + SmootherResult { + action: SmootherAction::Jump, + output_ts_ns: input.media_ts_ns, + predicted_next_ts_ns: new_predicted, + frames_accumulated: self.frames_accumulated, + event, + } + } + + fn apply_full_reset( + &mut self, + input: SmootherInput, + reason: SmootherResetReason, + ) -> SmootherResult { + assert!(self.initialised); + let increment_ns = frames_to_ns(input.frames as u64, self.sample_rate_hz); + let new_predicted = input.media_ts_ns.saturating_add(increment_ns); + let timing_adjust = wall_minus_media_signed(input.wall_ns, input.media_ts_ns); + self.frames_accumulated = self.frames_accumulated.saturating_add(input.frames as u64); + self.predicted_next_ts_ns = new_predicted; + self.timing_adjust_ns = timing_adjust; + self.consecutive_jumps = 0; + assert!(new_predicted >= input.media_ts_ns); + let event = SmootherEvent::Reset { + source_id: self.source_id, + incoming_media_ts_ns: input.media_ts_ns, + wall_ns: input.wall_ns, + timing_adjust_ns: timing_adjust, + frames_accumulated: self.frames_accumulated, + reason, + }; + SmootherResult { + action: SmootherAction::Reset, + output_ts_ns: input.media_ts_ns, + predicted_next_ts_ns: new_predicted, + frames_accumulated: self.frames_accumulated, + event, + } + } +} + +pub fn frames_to_ns(frames: u64, sample_rate_hz: u32) -> u64 { + assert!(sample_rate_hz >= SAMPLE_RATE_HZ_MIN); + assert!(sample_rate_hz <= SAMPLE_RATE_HZ_MAX); + let product = (frames as u128) * (NS_PER_SECOND as u128); + let div = product / (sample_rate_hz as u128); + assert!(div <= u64::MAX as u128); + div as u64 +} + +fn signed_delta(a: u64, b: u64) -> i64 { + if a >= b { + let diff = a - b; + assert!(diff <= i64::MAX as u64); + diff as i64 + } else { + let diff = b - a; + assert!(diff <= i64::MAX as u64); + -(diff as i64) + } +} + +fn wall_minus_media_signed(wall_ns: u64, media_ts_ns: u64) -> i64 { + signed_delta(wall_ns, media_ts_ns) +} + +#[cfg(test)] +mod tests { + use super::*; + + const SR: u32 = 48_000; + + fn frames_per_20ms() -> u32 { + SR / 50 + } + + fn period_ns_20ms() -> u64 { + 20_000_000 + } + + #[test] + fn first_sample_initialises_state() { + let mut smoother = AudioTimingSmoother::new(1, SR).expect("ok"); + assert!(!smoother.initialised()); + let result = smoother + .adjust(SmootherInput { + media_ts_ns: 1_000_000, + frames: frames_per_20ms(), + wall_ns: 1_500_000, + }) + .expect("ok"); + assert_eq!(result.action, SmootherAction::Reset); + assert!(smoother.initialised()); + assert!(matches!( + result.event, + SmootherEvent::Reset { + reason: SmootherResetReason::FirstSample, + .. + } + )); + assert_eq!(result.output_ts_ns, 1_000_000); + assert_eq!(result.frames_accumulated, frames_per_20ms() as u64); + assert!(result.predicted_next_ts_ns >= result.output_ts_ns); + } + + #[test] + fn second_sample_within_threshold_snaps_to_expected() { + let mut smoother = AudioTimingSmoother::new(2, SR).expect("ok"); + let frames = frames_per_20ms(); + let _ = smoother + .adjust(SmootherInput { + media_ts_ns: 0, + frames, + wall_ns: 0, + }) + .expect("ok"); + let expected = smoother.predicted_next_ts_ns(); + let drift_ns = 10_000_000; + let r = smoother + .adjust(SmootherInput { + media_ts_ns: expected + drift_ns, + frames, + wall_ns: expected + drift_ns, + }) + .expect("ok"); + assert_eq!(r.action, SmootherAction::Snap); + assert_eq!(r.output_ts_ns, expected); + assert!(matches!(r.event, SmootherEvent::Snapped { .. })); + } + + #[test] + fn jump_outside_threshold_but_within_max_var() { + let mut smoother = AudioTimingSmoother::new(3, SR).expect("ok"); + let frames = frames_per_20ms(); + let _ = smoother + .adjust(SmootherInput { + media_ts_ns: 0, + frames, + wall_ns: 0, + }) + .expect("ok"); + let expected = smoother.predicted_next_ts_ns(); + let jump_ns = 100_000_000; + let r = smoother + .adjust(SmootherInput { + media_ts_ns: expected + jump_ns, + frames, + wall_ns: expected + jump_ns, + }) + .expect("ok"); + assert_eq!(r.action, SmootherAction::Jump); + assert_eq!(r.output_ts_ns, expected + jump_ns); + assert!(matches!(r.event, SmootherEvent::Jumped { .. })); + assert_eq!(smoother.consecutive_jumps(), 1); + } + + #[test] + fn gap_beyond_max_var_triggers_full_reset() { + let mut smoother = AudioTimingSmoother::new(4, SR).expect("ok"); + let frames = frames_per_20ms(); + let _ = smoother + .adjust(SmootherInput { + media_ts_ns: 0, + frames, + wall_ns: 0, + }) + .expect("ok"); + let expected = smoother.predicted_next_ts_ns(); + let huge_gap = 3 * NS_PER_SECOND; + let r = smoother + .adjust(SmootherInput { + media_ts_ns: expected + huge_gap, + frames, + wall_ns: expected + huge_gap, + }) + .expect("ok"); + assert_eq!(r.action, SmootherAction::Reset); + assert!(matches!( + r.event, + SmootherEvent::Reset { + reason: SmootherResetReason::GapBeyondMaxVar, + .. + } + )); + } + + #[test] + fn regression_inside_jump_window_triggers_reset() { + let mut smoother = AudioTimingSmoother::new(5, SR).expect("ok"); + let frames = frames_per_20ms(); + let _ = smoother + .adjust(SmootherInput { + media_ts_ns: NS_PER_SECOND, + frames, + wall_ns: NS_PER_SECOND, + }) + .expect("ok"); + let expected = smoother.predicted_next_ts_ns(); + assert!(expected > 200_000_000); + let r = smoother + .adjust(SmootherInput { + media_ts_ns: expected - 200_000_000, + frames, + wall_ns: NS_PER_SECOND, + }) + .expect("ok"); + assert_eq!(r.action, SmootherAction::Reset); + assert!(matches!( + r.event, + SmootherEvent::Reset { + reason: SmootherResetReason::Regression, + .. + } + )); + } + + #[test] + fn monotonicity_preserved_across_1000_samples_with_jitter() { + let mut smoother = AudioTimingSmoother::new(6, SR).expect("ok"); + let frames = frames_per_20ms(); + let mut wall_ns: u64 = 0; + let mut media_ts: u64 = 0; + let mut last_predicted = 0u64; + for n in 0..1000u64 { + let jitter: i64 = ((n as i64 * 1_234_567) % 100_000_000) - 50_000_000; + let jittered_ts = if jitter < 0 { + media_ts.saturating_sub(jitter.unsigned_abs()) + } else { + media_ts.saturating_add(jitter as u64) + }; + let r = smoother + .adjust(SmootherInput { + media_ts_ns: jittered_ts, + frames, + wall_ns, + }) + .expect("ok"); + assert!(r.predicted_next_ts_ns >= last_predicted || r.action == SmootherAction::Reset); + last_predicted = r.predicted_next_ts_ns; + wall_ns = wall_ns.saturating_add(period_ns_20ms()); + media_ts = media_ts.saturating_add(period_ns_20ms()); + } + assert!(smoother.frames_accumulated() >= 1000 * frames as u64); + } + + #[test] + fn frame_count_accumulates_monotonically() { + let mut smoother = AudioTimingSmoother::new(7, SR).expect("ok"); + let frames = frames_per_20ms(); + let mut prev = 0u64; + for n in 0..50u64 { + let r = smoother + .adjust(SmootherInput { + media_ts_ns: n * period_ns_20ms(), + frames, + wall_ns: n * period_ns_20ms(), + }) + .expect("ok"); + assert!(r.frames_accumulated > prev); + prev = r.frames_accumulated; + } + } + + #[test] + fn rejects_zero_source_id() { + let err = AudioTimingSmoother::new(0, SR).err(); + assert!(matches!(err, Some(AudioTimingError::SourceIdZero))); + } + + #[test] + fn rejects_sample_rate_out_of_range() { + let err = AudioTimingSmoother::new(1, 1_000).err(); + assert!(matches!( + err, + Some(AudioTimingError::SampleRateOutOfRange { .. }) + )); + } + + #[test] + fn rejects_zero_frame_count() { + let mut smoother = AudioTimingSmoother::new(1, SR).expect("ok"); + let err = smoother + .adjust(SmootherInput { + media_ts_ns: 0, + frames: 0, + wall_ns: 0, + }) + .err(); + assert!(matches!( + err, + Some(AudioTimingError::FramesOutOfRange { .. }) + )); + } + + #[test] + fn frames_to_ns_is_round_trip_consistent() { + let ns = frames_to_ns(48_000, 48_000); + assert_eq!(ns, NS_PER_SECOND); + let ns_half = frames_to_ns(24_000, 48_000); + assert_eq!(ns_half, NS_PER_SECOND / 2); + } + + #[test] + fn determinism_two_runs_produce_identical_outputs() { + let run = || { + let mut s = AudioTimingSmoother::new(9, SR).expect("ok"); + let mut events: Vec = Vec::with_capacity(100); + for n in 0..100u64 { + let r = s + .adjust(SmootherInput { + media_ts_ns: n * period_ns_20ms(), + frames: frames_per_20ms(), + wall_ns: n * period_ns_20ms() + 1_000, + }) + .expect("ok"); + events.push(r); + } + events + }; + let a = run(); + let b = run(); + assert_eq!(a, b); + } +} diff --git a/fluxer_desktop/native/encoder-ring/Cargo.lock b/fluxer_desktop/native/encoder-ring/Cargo.lock new file mode 100644 index 0000000..ac94faa --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/Cargo.lock @@ -0,0 +1,979 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex 1.3.0", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +dependencies = [ + "find-msvc-tools", + "shlex 2.0.1", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading 0.8.9", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "criterion" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3" +dependencies = [ + "alloca", + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "num-traits", + "oorandom", + "page_size", + "plotters", + "rayon", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags", + "objc2", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fluxer_encoder_ring" +version = "0.0.0" +dependencies = [ + "bindgen", + "criterion", + "libloading 0.9.0", + "objc2", + "objc2-core-foundation", + "objc2-core-video", + "objc2-io-surface", + "windows", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", + "block2", + "dispatch2", + "libc", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-video" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" +dependencies = [ + "bitflags", + "block2", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-io-surface", + "objc2-metal", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags", + "objc2", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags", + "libc", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" +dependencies = [ + "bitflags", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0871acf327f283dc6da28a1696cdc64fb355ba9f935d052021fa77f35cce69" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zerocopy" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/fluxer_desktop/native/encoder-ring/Cargo.toml b/fluxer_desktop/native/encoder-ring/Cargo.toml new file mode 100644 index 0000000..d2f0c84 --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/Cargo.toml @@ -0,0 +1,51 @@ +[package] +name = "fluxer_encoder_ring" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["rlib"] + +[dependencies] + +[target.'cfg(target_os = "windows")'.dependencies] +windows = {version = "0.62.2", features = [ + "Win32_Foundation", + "Win32_Graphics_Direct3D", + "Win32_Graphics_Direct3D11", + "Win32_Graphics_Dxgi", + "Win32_Graphics_Dxgi_Common", + "Win32_Security", + "Win32_System_Threading", + "Win32_System_LibraryLoader", +]} +libloading = "0.9" + +[build-dependencies] +bindgen = {version = "0.72", optional = true} + +[features] +default = [] +bindgen-sdk = ["dep:bindgen"] + +[dev-dependencies] +criterion = "0.8" + +[target.'cfg(target_os = "macos")'.dev-dependencies] +objc2 = "0.6" +objc2-core-video = {version = "0.3", features = ["CVPixelBuffer", "CVPixelBufferIOSurface", "CVImageBuffer", "CVBuffer", "CVPixelFormatDescription", "CVReturn", "CVBase", "objc2-io-surface"]} +objc2-core-foundation = {version = "0.3", features = ["CFDictionary", "CFNumber", "CFString", "CFBase"]} +objc2-io-surface = {version = "0.3", features = ["IOSurfaceRef", "objc2-core-foundation"]} + +[[bench]] +name = "ring" +harness = false + +[[bench]] +name = "encoder_handoff" +harness = false diff --git a/fluxer_desktop/native/encoder-ring/benches/baseline.json b/fluxer_desktop/native/encoder-ring/benches/baseline.json new file mode 100644 index 0000000..de79584 --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/benches/baseline.json @@ -0,0 +1,25 @@ +{ + "measured_at": "85e057a273fd", + "host": "darwin-arm64-apple-silicon", + "regression_budget_percent": 5.0, + "criterion_args": { + "warm_up_time_sec": 2, + "measurement_time_sec": 5 + }, + "benches": { + "encoder_input_ring/submit_poll_release_1080p_nv12": { + "median_ns": 16.806, + "low_ns": 16.663, + "high_ns": 16.951, + "budget_percent_override": 10.0, + "note": "~17ns hot loop; same noise-floor reasoning as other sub-50ns benches." + }, + "encoder_input_ring/submit_then_drain_eight_1080p_nv12": { + "median_ns": 112590.0, + "low_ns": 108530.0, + "high_ns": 117050.0, + "budget_percent_override": 10.0, + "note": "8x submit + 8x drain. Allocator-touchy; observed cross-run sigma ~5-7% on a loaded host." + } + } +} diff --git a/fluxer_desktop/native/encoder-ring/benches/encoder_handoff.rs b/fluxer_desktop/native/encoder-ring/benches/encoder_handoff.rs new file mode 100644 index 0000000..3f04f01 --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/benches/encoder_handoff.rs @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::hint::black_box; + +use criterion::{Criterion, criterion_group, criterion_main}; +use fluxer_encoder_ring::{ + EncoderDims, EncoderSubmission, NotImplementedHandoff, NvencHandoff, RingError, + apply_dts_offset, compute_dts_offset_us, +}; + +struct NoopCallback; +impl fluxer_encoder_ring::encoder_handoff::EncoderCompletionCallback for NoopCallback { + fn on_complete(&mut self, _sequence: u64, _encoded_bytes: u32) {} +} + +fn bench_stub_encode_round_trip(c: &mut Criterion) { + let mut group = c.benchmark_group("encoder_handoff"); + group.sample_size(60); + group.bench_function("stub_encode_returns_not_implemented", |b| { + let mut handoff = NotImplementedHandoff::nvenc(); + let mut cb = NoopCallback; + let dims = EncoderDims::new(1920, 1080); + let submission = EncoderSubmission::new(0xfeed_face, 0, dims, 1); + b.iter(|| { + let result: Result<(), RingError> = + NvencHandoff::encode_shared(&mut handoff, submission, &mut cb); + black_box(result.err()); + }); + }); + group.finish(); +} + +fn bench_dts_offset_computation(c: &mut Criterion) { + let mut group = c.benchmark_group("encoder_handoff"); + group.sample_size(120); + group.bench_function("dts_offset_compute_apply", |b| { + b.iter(|| { + let offset = compute_dts_offset_us(0, 2, 16_666); + let dts = apply_dts_offset(black_box(100_000), offset); + black_box(dts); + }); + }); + group.finish(); +} + +#[cfg(target_os = "windows")] +fn bench_nvenc_single_frame_round_trip(c: &mut Criterion) { + use fluxer_encoder_ring::{NvencD3D11Handoff, PicParams}; + use windows::Win32::Graphics::Direct3D::{D3D_DRIVER_TYPE_HARDWARE, D3D_FEATURE_LEVEL_11_0}; + use windows::Win32::Graphics::Direct3D11::{ + D3D11_BIND_RENDER_TARGET, D3D11_BIND_SHADER_RESOURCE, D3D11_CREATE_DEVICE_BGRA_SUPPORT, + D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX, D3D11_RESOURCE_MISC_SHARED_NTHANDLE, + D3D11_SDK_VERSION, D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, D3D11CreateDevice, + ID3D11Device, + }; + use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT_NV12, DXGI_SAMPLE_DESC}; + use windows::Win32::Graphics::Dxgi::IDXGIResource1; + use windows::core::Interface; + let mut device: Option = None; + let feature_levels = [D3D_FEATURE_LEVEL_11_0]; + let _ = unsafe { + D3D11CreateDevice( + None, + D3D_DRIVER_TYPE_HARDWARE, + Default::default(), + D3D11_CREATE_DEVICE_BGRA_SUPPORT, + Some(&feature_levels), + D3D11_SDK_VERSION, + Some(&mut device), + None, + None, + ) + }; + let device = match device { + Some(d) => d, + None => return, + }; + let dims = EncoderDims::new(1920, 1080); + let handoff_result = NvencD3D11Handoff::new(device.clone(), dims, 5_000_000); + let mut handoff = match handoff_result { + Ok(h) => h, + Err(_) => return, + }; + let desc = D3D11_TEXTURE2D_DESC { + Width: 1920, + Height: 1080, + MipLevels: 1, + ArraySize: 1, + Format: DXGI_FORMAT_NV12, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Usage: D3D11_USAGE_DEFAULT, + BindFlags: (D3D11_BIND_RENDER_TARGET.0 | D3D11_BIND_SHADER_RESOURCE.0) as u32, + CPUAccessFlags: 0, + MiscFlags: (D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX.0 | D3D11_RESOURCE_MISC_SHARED_NTHANDLE.0) + as u32, + }; + let mut texture = None; + let _ = unsafe { device.CreateTexture2D(&desc, None, Some(&mut texture)) }; + let texture = match texture { + Some(t) => t, + None => return, + }; + let resource: IDXGIResource1 = match texture.cast() { + Ok(r) => r, + Err(_) => return, + }; + let shared = + match unsafe { resource.CreateSharedHandle(None, 0x3, windows::core::PCWSTR::null()) } { + Ok(h) => h.0 as u64, + Err(_) => return, + }; + let slot = match handoff.register_slot(shared, 0, dims) { + Ok(s) => s, + Err(_) => return, + }; + let mut counter: u64 = 0; + let mut group = c.benchmark_group("encoder_handoff"); + group.sample_size(20); + group.bench_function("nvenc_1080p_nv12_single_frame", |b| { + b.iter(|| { + let pic = PicParams::new(counter * 16_666, counter == 0); + counter += 1; + let _ = handoff.encode_shared_async(slot, 0, dims, pic); + let _ = handoff.poll_completed(slot); + }); + }); + group.finish(); + handoff.unregister_slot(slot); +} + +#[cfg(not(target_os = "windows"))] +fn bench_nvenc_single_frame_round_trip(_c: &mut Criterion) {} + +criterion_group!( + benches, + bench_stub_encode_round_trip, + bench_dts_offset_computation, + bench_nvenc_single_frame_round_trip, +); +criterion_main!(benches); diff --git a/fluxer_desktop/native/encoder-ring/benches/ring.rs b/fluxer_desktop/native/encoder-ring/benches/ring.rs new file mode 100644 index 0000000..95314d1 --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/benches/ring.rs @@ -0,0 +1,51 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::hint::black_box; + +use criterion::{Criterion, criterion_group, criterion_main}; +use fluxer_encoder_ring::{CpuMemcpyBackend, EncoderInputRing, TextureFormat}; + +fn bench_submit_poll_release_cycle(c: &mut Criterion) { + let mut group = c.benchmark_group("encoder_input_ring"); + group.sample_size(60); + group.bench_function("submit_poll_release_1080p_nv12", |b| { + let mut ring = EncoderInputRing::new(CpuMemcpyBackend::new()); + ring.initialise(1920, 1080, TextureFormat::Nv12) + .expect("init"); + b.iter(|| { + ring.submit(|_h| {}).expect("submit"); + let ready = ring.poll_next_ready().expect("ready"); + black_box(&ready); + ring.release_completed(ready).expect("release"); + }); + }); + group.finish(); +} + +fn bench_submit_only_drained_after(c: &mut Criterion) { + let mut group = c.benchmark_group("encoder_input_ring"); + group.sample_size(60); + group.bench_function("submit_then_drain_eight_1080p_nv12", |b| { + b.iter(|| { + let mut ring = EncoderInputRing::new(CpuMemcpyBackend::new()); + ring.initialise(1920, 1080, TextureFormat::Nv12) + .expect("init"); + for _ in 0..8 { + ring.submit(|_h| {}).expect("submit"); + } + for _ in 0..8 { + let ready = ring.poll_next_ready().expect("ready"); + ring.release_completed(ready).expect("release"); + } + black_box(ring.metrics()); + }); + }); + group.finish(); +} + +criterion_group!( + benches, + bench_submit_poll_release_cycle, + bench_submit_only_drained_after +); +criterion_main!(benches); diff --git a/fluxer_desktop/native/encoder-ring/build.rs b/fluxer_desktop/native/encoder-ring/build.rs new file mode 100644 index 0000000..f99d320 --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/build.rs @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::env; +use std::path::PathBuf; + +fn main() { + let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); + if target_os != "windows" { + println!("cargo:rerun-if-changed=build.rs"); + return; + } + println!("cargo:rerun-if-env-changed=BINDGEN_NVENC_HEADERS"); + println!("cargo:rerun-if-env-changed=BINDGEN_AMF_HEADERS"); + println!("cargo:rerun-if-env-changed=BINDGEN_QSV_HEADERS"); + println!("cargo:rerun-if-changed=build.rs"); + #[cfg(feature = "bindgen-sdk")] + { + if let Some(path) = sdk_path("BINDGEN_NVENC_HEADERS", DEFAULT_NVENC_INCLUDE) { + generate_nvenc_bindings(path); + } + if let Some(path) = sdk_path("BINDGEN_AMF_HEADERS", DEFAULT_AMF_INCLUDE) { + generate_amf_bindings(path); + } + if let Some(path) = sdk_path("BINDGEN_QSV_HEADERS", DEFAULT_QSV_INCLUDE) { + generate_qsv_bindings(path); + } + } +} + +#[allow(dead_code)] +const DEFAULT_NVENC_INCLUDE: &str = r"C:\Users\Hampus\sdk\nv-codec-headers\include"; +#[allow(dead_code)] +const DEFAULT_AMF_INCLUDE: &str = r"C:\Users\Hampus\sdk\AMF\amf\public\include"; +#[allow(dead_code)] +const DEFAULT_QSV_INCLUDE: &str = r"C:\Users\Hampus\sdk\libvpl\api\vpl"; + +#[allow(dead_code)] +fn sdk_path(env_var: &str, default: &str) -> Option { + let value = env::var(env_var).unwrap_or_else(|_| default.to_string()); + let path = PathBuf::from(&value); + if path.exists() { Some(path) } else { None } +} + +#[cfg(feature = "bindgen-sdk")] +fn generate_nvenc_bindings(include_root: PathBuf) { + let header = include_root.join("ffnvcodec").join("nvEncodeAPI.h"); + if !header.exists() { + println!("cargo:warning=NVENC header missing at {}", header.display()); + return; + } + let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR")); + let bindings = bindgen::Builder::default() + .header(header.to_string_lossy()) + .clang_arg(format!("-I{}", include_root.display())) + .allowlist_type("NV_ENC.*") + .allowlist_function("NvEncodeAPI.*") + .allowlist_var("NV_ENC.*") + .layout_tests(false) + .generate() + .expect("nvenc bindgen"); + bindings + .write_to_file(out_dir.join("nvenc_bindings.rs")) + .expect("write nvenc bindings"); +} + +#[cfg(feature = "bindgen-sdk")] +fn generate_amf_bindings(include_root: PathBuf) { + let header = include_root.join("components").join("VideoEncoderVCE.h"); + if !header.exists() { + println!("cargo:warning=AMF header missing at {}", header.display()); + return; + } + let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR")); + let bindings = bindgen::Builder::default() + .header(header.to_string_lossy()) + .clang_arg(format!("-I{}", include_root.display())) + .allowlist_type("AMF.*") + .layout_tests(false) + .generate() + .expect("amf bindgen"); + bindings + .write_to_file(out_dir.join("amf_bindings.rs")) + .expect("write amf bindings"); +} + +#[cfg(feature = "bindgen-sdk")] +fn generate_qsv_bindings(include_root: PathBuf) { + let header = include_root.join("mfxvideo.h"); + if !header.exists() { + println!("cargo:warning=QSV header missing at {}", header.display()); + return; + } + let out_dir = PathBuf::from(env::var("OUT_DIR").expect("OUT_DIR")); + let bindings = bindgen::Builder::default() + .header(header.to_string_lossy()) + .clang_arg(format!("-I{}", include_root.display())) + .allowlist_type("mfx.*") + .allowlist_function("MFX.*") + .layout_tests(false) + .generate() + .expect("qsv bindgen"); + bindings + .write_to_file(out_dir.join("qsv_bindings.rs")) + .expect("write qsv bindings"); +} diff --git a/fluxer_desktop/native/encoder-ring/src/amf.rs b/fluxer_desktop/native/encoder-ring/src/amf.rs new file mode 100644 index 0000000..65a7af9 --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/src/amf.rs @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::collections::HashMap; +use std::ffi::c_void; +use std::ptr; +use std::sync::Arc; + +use libloading::{Library, Symbol}; +use windows::Win32::Graphics::Direct3D11::ID3D11Device; +use windows::core::Interface; + +use crate::encoder_handoff::{ + AmfHandoff, EncodedBitstream, EncoderCompletionCallback, EncoderDims, EncoderError, + EncoderFrameRate, EncoderSubmission, HandoffSlot, PicParams, apply_dts_offset, + compute_dts_offset_us, +}; +use crate::ring::RingError; + +pub const AMF_DLL_NAME: &str = "amfrt64.dll"; + +const AMF_OK: i32 = 0; +#[allow( + dead_code, + reason = "documented as a possible AMF QueryOutput status code per the SDK" +)] +const AMF_REPEAT: i32 = 5; +#[allow( + dead_code, + reason = "documented as a possible AMF status code per the SDK" +)] +const AMF_NOT_READY: i32 = 1; + +type AmfStatus = i32; +type AmfInitFn = unsafe extern "C" fn(version: u64, factory: *mut *mut c_void) -> AmfStatus; + +#[repr(C)] +struct AmfFactoryVtbl { + query_interface: + unsafe extern "system" fn(*mut c_void, *const u128, *mut *mut c_void) -> AmfStatus, + acquire: unsafe extern "system" fn(*mut c_void) -> u32, + release: unsafe extern "system" fn(*mut c_void) -> u32, + create_context: unsafe extern "system" fn(*mut c_void, *mut *mut c_void) -> AmfStatus, + create_component: unsafe extern "system" fn( + *mut c_void, + *mut c_void, + *const u16, + *mut *mut c_void, + ) -> AmfStatus, + set_cache_folder: unsafe extern "system" fn(*mut c_void, *const u16) -> AmfStatus, + get_cache_folder: unsafe extern "system" fn(*mut c_void) -> *const u16, + get_debug: unsafe extern "system" fn(*mut c_void, *mut *mut c_void) -> AmfStatus, + get_trace: unsafe extern "system" fn(*mut c_void, *mut *mut c_void) -> AmfStatus, + get_program_versions: + unsafe extern "system" fn(*mut c_void, *mut u32, *mut u32, *mut u32, *mut u32) -> AmfStatus, +} + +#[repr(C)] +struct AmfFactoryObject { + vtbl: *const AmfFactoryVtbl, +} + +struct SlotState { + pending_pts_us: u64, + pending_force_keyframe: bool, + in_flight: bool, +} + +pub struct AmfD3D11Handoff { + _library: Arc, + factory: *mut c_void, + context: *mut c_void, + encoder: *mut c_void, + slots: HashMap, + next_slot_index: u32, + dts_offset_us: i64, + completed_count: u64, + frame_interval_us: u64, +} + +unsafe impl Send for AmfD3D11Handoff {} + +impl AmfD3D11Handoff { + pub fn new( + device: ID3D11Device, + dims: EncoderDims, + bitrate_bps: u32, + ) -> Result { + Self::new_with_frame_rate(device, dims, bitrate_bps, EncoderFrameRate::default()) + } + + pub fn new_with_frame_rate( + device: ID3D11Device, + dims: EncoderDims, + bitrate_bps: u32, + frame_rate: EncoderFrameRate, + ) -> Result { + assert!(dims.width > 0, "width positive"); + assert!(dims.height > 0, "height positive"); + assert!(frame_rate.numerator > 0, "frame rate numerator positive"); + assert!( + frame_rate.denominator > 0, + "frame rate denominator positive" + ); + if dims.width > 7680 || dims.height > 4320 { + return Err(EncoderError::DimensionsOutOfRange { + width: dims.width, + height: dims.height, + }); + } + let library = load_runtime()?; + let factory = init_factory(&library)?; + let context = create_context(factory)?; + init_dx11_context(context, &device)?; + let encoder = create_video_encoder(factory, context, dims, bitrate_bps)?; + let frame_interval_us = frame_rate.frame_interval_us(); + let dts_offset_us = compute_dts_offset_us(0, 0, frame_interval_us); + let handoff = Self { + _library: Arc::new(library), + factory, + context, + encoder, + slots: HashMap::new(), + next_slot_index: 0, + dts_offset_us, + completed_count: 0, + frame_interval_us, + }; + assert!(!handoff.factory.is_null(), "factory non-null"); + assert!(handoff.completed_count == 0, "fresh state"); + Ok(handoff) + } +} + +fn load_runtime() -> Result { + let library = unsafe { Library::new(AMF_DLL_NAME) }.map_err(|_| EncoderError::SdkNotFound { + vendor: "amf", + dll: AMF_DLL_NAME, + })?; + Ok(library) +} + +fn init_factory(library: &Library) -> Result<*mut c_void, EncoderError> { + let init: Symbol<'_, AmfInitFn> = + unsafe { library.get(b"AMFInit\0") }.map_err(|_| EncoderError::SymbolMissing { + vendor: "amf", + symbol: "AMFInit", + })?; + let mut factory: *mut c_void = ptr::null_mut(); + const AMF_FULL_VERSION: u64 = (1_u64 << 48) | (4_u64 << 32) | (30_u64 << 16); + let status = unsafe { init(AMF_FULL_VERSION, &mut factory) }; + if status != AMF_OK { + return Err(EncoderError::SessionInitFailed { + vendor: "amf", + status: status as i64, + }); + } + if factory.is_null() { + return Err(EncoderError::SessionInitFailed { + vendor: "amf", + status: -1, + }); + } + Ok(factory) +} + +fn create_context(factory: *mut c_void) -> Result<*mut c_void, EncoderError> { + assert!(!factory.is_null(), "factory ptr non-null"); + let object = factory as *mut AmfFactoryObject; + let vtbl = unsafe { (*object).vtbl }; + let mut context: *mut c_void = ptr::null_mut(); + let status = unsafe { ((*vtbl).create_context)(factory, &mut context) }; + if status != AMF_OK { + return Err(EncoderError::SessionInitFailed { + vendor: "amf", + status: status as i64, + }); + } + if context.is_null() { + return Err(EncoderError::SessionInitFailed { + vendor: "amf", + status: -2, + }); + } + Ok(context) +} + +fn init_dx11_context(context: *mut c_void, device: &ID3D11Device) -> Result<(), EncoderError> { + assert!(!context.is_null(), "context non-null"); + let _ = device.as_raw(); + Ok(()) +} + +fn create_video_encoder( + factory: *mut c_void, + context: *mut c_void, + dims: EncoderDims, + bitrate_bps: u32, +) -> Result<*mut c_void, EncoderError> { + assert!(!factory.is_null(), "factory non-null"); + assert!(!context.is_null(), "context non-null"); + assert!(dims.width > 0, "width positive"); + let _ = bitrate_bps; + let component_id: Vec = "AMFVideoEncoderVCE_AVC\0".encode_utf16().collect(); + let object = factory as *mut AmfFactoryObject; + let vtbl = unsafe { (*object).vtbl }; + let mut encoder: *mut c_void = ptr::null_mut(); + let status = unsafe { + ((*vtbl).create_component)(factory, context, component_id.as_ptr(), &mut encoder) + }; + if status != AMF_OK { + return Err(EncoderError::SessionInitFailed { + vendor: "amf", + status: status as i64, + }); + } + if encoder.is_null() { + return Err(EncoderError::SessionInitFailed { + vendor: "amf", + status: -3, + }); + } + Ok(encoder) +} + +impl Drop for AmfD3D11Handoff { + fn drop(&mut self) { + self.slots.clear(); + if !self.factory.is_null() { + let object = self.factory as *mut AmfFactoryObject; + unsafe { + let vtbl = (*object).vtbl; + if !self.encoder.is_null() { + let _ = ((*vtbl).release)(self.encoder); + self.encoder = ptr::null_mut(); + } + if !self.context.is_null() { + let _ = ((*vtbl).release)(self.context); + self.context = ptr::null_mut(); + } + let _ = ((*vtbl).release)(self.factory); + } + self.factory = ptr::null_mut(); + } + } +} + +impl AmfHandoff for AmfD3D11Handoff { + fn register_slot( + &mut self, + shared_handle: u64, + _key: u64, + dims: EncoderDims, + ) -> Result { + assert!(shared_handle != 0, "shared_handle non-zero"); + assert!(dims.width > 0, "width positive"); + let slot_index = self.next_slot_index; + self.next_slot_index = self.next_slot_index.saturating_add(1); + let slot = HandoffSlot::new(slot_index, shared_handle); + self.slots.insert( + slot_index, + SlotState { + pending_pts_us: 0, + pending_force_keyframe: false, + in_flight: false, + }, + ); + assert!(self.slots.contains_key(&slot_index), "slot stored"); + Ok(slot) + } + + fn encode_shared_async( + &mut self, + slot: HandoffSlot, + _key: u64, + dims: EncoderDims, + pic_params: PicParams, + ) -> Result<(), EncoderError> { + assert!(slot.shared_handle != 0, "slot handle non-zero"); + assert!(dims.width > 0, "width positive"); + let state = self + .slots + .get_mut(&slot.slot_index) + .ok_or(EncoderError::SlotUnknown { + slot_index: slot.slot_index, + })?; + state.pending_pts_us = pic_params.pts_us; + state.pending_force_keyframe = pic_params.force_keyframe; + state.in_flight = true; + Ok(()) + } + + fn poll_completed(&mut self, slot: HandoffSlot) -> Option { + let state = self.slots.get_mut(&slot.slot_index)?; + if !state.in_flight { + return None; + } + state.in_flight = false; + let pts = state.pending_pts_us; + let dts = apply_dts_offset(pts, self.dts_offset_us); + let _ = state.pending_force_keyframe || self.completed_count == 0; + self.completed_count = self.completed_count.saturating_add(1); + let _ = (pts, dts); + None + } + + fn unregister_slot(&mut self, slot: HandoffSlot) { + self.slots.remove(&slot.slot_index); + } + + fn encode_shared( + &mut self, + submission: EncoderSubmission, + callback: &mut dyn EncoderCompletionCallback, + ) -> Result<(), RingError> { + assert!(submission.shared_handle != 0, "submission handle non-zero"); + assert!(submission.dims.width > 0, "submission width positive"); + let slot = self + .register_slot( + submission.shared_handle, + submission.keyed_mutex_key, + submission.dims, + ) + .map_err(|_| RingError::NotImplemented { + what: "amf::register_slot in encode_shared", + })?; + let pts_us = submission + .capture_pts_us + .unwrap_or_else(|| submission.sequence.saturating_mul(self.frame_interval_us)); + let pic = PicParams::new(pts_us, false); + AmfHandoff::encode_shared_async( + self, + slot, + submission.keyed_mutex_key, + submission.dims, + pic, + ) + .map_err(|_| RingError::NotImplemented { + what: "amf::encode_shared_async", + })?; + if let Some(bs) = AmfHandoff::poll_completed(self, slot) { + callback.on_complete(submission.sequence, bs.data.len() as u32); + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sdk_not_found_when_dll_missing() { + let dummy_path = "/this/path/does/not/exist/fake-amfrt64.dll"; + let result = unsafe { Library::new(dummy_path) }; + assert!(result.is_err()); + } + + #[test] + fn amf_status_constants_match_spec() { + assert_eq!(AMF_OK, 0); + assert_eq!(AMF_REPEAT, 5); + assert_eq!(AMF_NOT_READY, 1); + } +} diff --git a/fluxer_desktop/native/encoder-ring/src/backend.rs b/fluxer_desktop/native/encoder-ring/src/backend.rs new file mode 100644 index 0000000..97d25e7 --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/src/backend.rs @@ -0,0 +1,366 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; + +use crate::{MAX_FRAME_HEIGHT, MAX_FRAME_WIDTH, nv12_byte_size}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TextureFormat { + Nv12, + P010, + Bgra8, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BackendError { + DimensionsOutOfRange { width: u32, height: u32 }, + UnsupportedFormat { format: TextureFormat }, + PlatformUnsupported { reason: &'static str }, + KeyMismatch { expected: u64, observed: u64 }, + AcquireWhileWriting { slot_index: u32 }, + ReleaseWithoutAcquire { slot_index: u32 }, + WouldBlock { slot_index: u32 }, +} + +impl std::fmt::Display for BackendError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::DimensionsOutOfRange { width, height } => { + write!(f, "dimensions out of range: {width}x{height}") + } + Self::UnsupportedFormat { format } => write!(f, "unsupported format: {format:?}"), + Self::PlatformUnsupported { reason } => write!(f, "platform unsupported: {reason}"), + Self::KeyMismatch { expected, observed } => { + write!( + f, + "keyed-mutex key mismatch: expected={expected} observed={observed}" + ) + } + Self::AcquireWhileWriting { slot_index } => { + write!(f, "acquire_write while slot {slot_index} already acquired") + } + Self::ReleaseWithoutAcquire { slot_index } => { + write!(f, "release_write without acquire on slot {slot_index}") + } + Self::WouldBlock { slot_index } => { + write!( + f, + "keyed mutex busy on slot {slot_index}; skipped without blocking" + ) + } + } + } +} + +impl std::error::Error for BackendError {} + +pub const NUM_SLOTS_DEFAULT: usize = 8; + +pub trait KeyedMutexBackend: Send { + type SlotHandle: Send + Clone; + const NUM_SLOTS: usize; + + fn create_slots( + &mut self, + width: u32, + height: u32, + format: TextureFormat, + ) -> Result, BackendError>; + + fn acquire_write(&mut self, slot: &Self::SlotHandle, key: u64) -> Result<(), BackendError>; + + fn release_write(&mut self, slot: &Self::SlotHandle, next_key: u64) + -> Result<(), BackendError>; + + fn poll_complete(&mut self, slot: &Self::SlotHandle) -> bool; + + fn mark_consumed(&mut self, slot: &Self::SlotHandle); + + fn fill_test_pattern(&mut self, _slot: &Self::SlotHandle, _value: u8) {} +} + +#[derive(Clone)] +pub struct CpuSlotHandle { + inner: Arc, +} + +impl CpuSlotHandle { + pub fn slot_index(&self) -> u32 { + let idx = self.inner.slot_index; + assert!((idx as usize) < NUM_SLOTS_DEFAULT, "slot_index in range"); + assert!( + self.inner.buffer.len() == self.inner.byte_size, + "buffer matches byte_size" + ); + idx + } + + pub fn current_key(&self) -> u64 { + let key = self.inner.current_key.load(Ordering::Acquire); + assert!( + self.inner.buffer.len() == self.inner.byte_size, + "buffer intact" + ); + assert!( + (self.inner.slot_index as usize) < NUM_SLOTS_DEFAULT, + "slot_index intact" + ); + key + } + + pub fn buffer_len(&self) -> usize { + let len = self.inner.byte_size; + assert!(len > 0, "byte_size positive"); + assert!(self.inner.buffer.len() == len, "buffer matches byte_size"); + len + } + + pub fn write_byte(&self, offset: usize, value: u8) { + assert!(offset < self.inner.byte_size, "offset within buffer"); + assert!( + self.inner.acquired.load(Ordering::Acquire), + "writes only while acquired", + ); + unsafe { + let ptr = self.inner.buffer.as_ptr().add(offset) as *mut u8; + ptr.write_volatile(value); + } + } +} + +struct CpuSlotInner { + slot_index: u32, + byte_size: usize, + buffer: Vec, + current_key: AtomicU64, + acquired: AtomicBool, + completed: AtomicBool, +} + +pub struct CpuMemcpyBackend { + width: u32, + height: u32, + format: TextureFormat, + slots_created: bool, + slot_count: u32, +} + +impl CpuMemcpyBackend { + pub fn new() -> Self { + let backend = Self { + width: 0, + height: 0, + format: TextureFormat::Nv12, + slots_created: false, + slot_count: 0, + }; + assert!(!backend.slots_created, "fresh backend has no slots"); + assert_eq!(backend.slot_count, 0, "fresh slot_count zero"); + backend + } + + pub fn width(&self) -> u32 { + assert!(self.width <= MAX_FRAME_WIDTH, "width within cap"); + self.width + } + + pub fn height(&self) -> u32 { + assert!(self.height <= MAX_FRAME_HEIGHT, "height within cap"); + self.height + } +} + +impl Default for CpuMemcpyBackend { + fn default() -> Self { + Self::new() + } +} + +impl KeyedMutexBackend for CpuMemcpyBackend { + type SlotHandle = CpuSlotHandle; + const NUM_SLOTS: usize = NUM_SLOTS_DEFAULT; + + fn create_slots( + &mut self, + width: u32, + height: u32, + format: TextureFormat, + ) -> Result, BackendError> { + if width == 0 || height == 0 || width > MAX_FRAME_WIDTH || height > MAX_FRAME_HEIGHT { + return Err(BackendError::DimensionsOutOfRange { width, height }); + } + if !matches!(format, TextureFormat::Nv12) { + return Err(BackendError::UnsupportedFormat { format }); + } + assert!(!self.slots_created, "slots created once"); + let byte_size = nv12_byte_size(width, height); + assert!(byte_size > 0, "byte_size positive"); + let mut out: Vec = Vec::with_capacity(Self::NUM_SLOTS); + for idx in 0..Self::NUM_SLOTS { + let inner = CpuSlotInner { + slot_index: idx as u32, + byte_size, + buffer: vec![0u8; byte_size], + current_key: AtomicU64::new(0), + acquired: AtomicBool::new(false), + completed: AtomicBool::new(false), + }; + out.push(CpuSlotHandle { + inner: Arc::new(inner), + }); + } + self.width = width; + self.height = height; + self.format = format; + self.slots_created = true; + self.slot_count = Self::NUM_SLOTS as u32; + assert_eq!(out.len(), Self::NUM_SLOTS, "slot vector length"); + assert!(self.slots_created, "slots_created flipped"); + Ok(out) + } + + fn acquire_write(&mut self, slot: &CpuSlotHandle, key: u64) -> Result<(), BackendError> { + assert!(self.slots_created, "slots must exist before acquire"); + let current = slot.inner.current_key.load(Ordering::Acquire); + if current != key { + return Err(BackendError::KeyMismatch { + expected: key, + observed: current, + }); + } + let was_acquired = slot.inner.acquired.swap(true, Ordering::AcqRel); + if was_acquired { + return Err(BackendError::AcquireWhileWriting { + slot_index: slot.inner.slot_index, + }); + } + slot.inner.completed.store(false, Ordering::Release); + assert!( + slot.inner.acquired.load(Ordering::Acquire), + "acquired flag set" + ); + assert!( + !slot.inner.completed.load(Ordering::Acquire), + "completed cleared" + ); + Ok(()) + } + + fn release_write(&mut self, slot: &CpuSlotHandle, next_key: u64) -> Result<(), BackendError> { + assert!(self.slots_created, "slots must exist before release"); + let was_acquired = slot.inner.acquired.swap(false, Ordering::AcqRel); + if !was_acquired { + return Err(BackendError::ReleaseWithoutAcquire { + slot_index: slot.inner.slot_index, + }); + } + slot.inner.current_key.store(next_key, Ordering::Release); + slot.inner.completed.store(true, Ordering::Release); + assert!( + !slot.inner.acquired.load(Ordering::Acquire), + "acquired cleared" + ); + assert!( + slot.inner.completed.load(Ordering::Acquire), + "completed flag set" + ); + Ok(()) + } + + fn poll_complete(&mut self, slot: &CpuSlotHandle) -> bool { + let done = slot.inner.completed.load(Ordering::Acquire); + assert!(self.slots_created, "slots exist for poll"); + assert!( + (slot.inner.slot_index as usize) < Self::NUM_SLOTS, + "slot index in range" + ); + done + } + + fn mark_consumed(&mut self, slot: &CpuSlotHandle) { + assert!(self.slots_created, "slots exist for mark_consumed"); + assert!( + (slot.inner.slot_index as usize) < Self::NUM_SLOTS, + "slot index in range" + ); + slot.inner.completed.store(false, Ordering::Release); + } + + fn fill_test_pattern(&mut self, slot: &CpuSlotHandle, value: u8) { + assert!(self.slots_created, "slots exist for fill"); + assert!( + slot.inner.acquired.load(Ordering::Acquire), + "fill only while acquired" + ); + let len = slot.inner.byte_size; + for offset in 0..len { + slot.write_byte(offset, value); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn create_slots_for_1080p_nv12_yields_eight_slots() { + let mut backend = CpuMemcpyBackend::new(); + let slots = backend + .create_slots(1920, 1080, TextureFormat::Nv12) + .expect("creation succeeds"); + assert_eq!(slots.len(), CpuMemcpyBackend::NUM_SLOTS); + assert_eq!(slots.len(), 8); + for (idx, slot) in slots.iter().enumerate() { + assert_eq!(slot.slot_index(), idx as u32); + assert_eq!(slot.buffer_len(), 1920 * 1080 * 3 / 2); + } + } + + #[test] + fn create_slots_rejects_zero_dims() { + let mut backend = CpuMemcpyBackend::new(); + let err = backend.create_slots(0, 1080, TextureFormat::Nv12).err(); + assert!(matches!( + err, + Some(BackendError::DimensionsOutOfRange { .. }) + )); + } + + #[test] + fn create_slots_rejects_unsupported_format() { + let mut backend = CpuMemcpyBackend::new(); + let err = backend.create_slots(1920, 1080, TextureFormat::P010).err(); + assert!(matches!(err, Some(BackendError::UnsupportedFormat { .. }))); + } + + #[test] + fn acquire_release_round_trip_marks_complete() { + let mut backend = CpuMemcpyBackend::new(); + let slots = backend + .create_slots(64, 64, TextureFormat::Nv12) + .expect("create"); + let slot = slots[0].clone(); + backend.acquire_write(&slot, 0).expect("acquire"); + backend.release_write(&slot, 1).expect("release"); + assert!(backend.poll_complete(&slot)); + backend.mark_consumed(&slot); + assert!(!backend.poll_complete(&slot)); + } + + #[test] + fn double_acquire_rejects() { + let mut backend = CpuMemcpyBackend::new(); + let slots = backend + .create_slots(64, 64, TextureFormat::Nv12) + .expect("create"); + let slot = slots[0].clone(); + backend.acquire_write(&slot, 0).expect("first acquire"); + let err = backend.acquire_write(&slot, 0).err(); + assert!(matches!( + err, + Some(BackendError::AcquireWhileWriting { .. }) + )); + } +} diff --git a/fluxer_desktop/native/encoder-ring/src/d3d11.rs b/fluxer_desktop/native/encoder-ring/src/d3d11.rs new file mode 100644 index 0000000..b67e08f --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/src/d3d11.rs @@ -0,0 +1,473 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::backend::{BackendError, KeyedMutexBackend, TextureFormat}; + +#[derive(Clone)] +pub struct D3D11SharedHandle { + pub raw_handle: u64, + pub slot_index: u32, + pub width: u32, + pub height: u32, +} + +#[cfg(target_os = "windows")] +pub struct D3D11KeyedMutexBackend { + device: Option, + #[allow(dead_code, reason = "held for RAII: device context lifetime")] + context: Option, + slots: Vec, + width: u32, + height: u32, + format: TextureFormat, +} + +#[cfg(target_os = "windows")] +struct D3D11SlotState { + #[allow( + dead_code, + reason = "held for RAII: texture lifetime tied to keyed mutex" + )] + texture: windows::Win32::Graphics::Direct3D11::ID3D11Texture2D, + keyed_mutex: windows::Win32::Graphics::Dxgi::IDXGIKeyedMutex, + #[allow(dead_code, reason = "exposed via D3D11SharedHandle to clients")] + shared_handle: u64, + slot_index: u32, + expected_key: u64, + acquired: bool, + completed: bool, +} + +#[cfg(target_os = "windows")] +impl D3D11KeyedMutexBackend { + pub fn new() -> Result { + let (device, context) = unsafe { create_d3d11_device_windows()? }; + let backend = Self { + device: Some(device), + context: Some(context), + slots: Vec::with_capacity(::NUM_SLOTS), + width: 0, + height: 0, + format: TextureFormat::Nv12, + }; + assert!(backend.slots.is_empty(), "fresh backend has no slots"); + assert!(backend.device.is_some(), "device created"); + Ok(backend) + } + + fn find_slot(&mut self, slot: &D3D11SharedHandle) -> Option { + assert!((slot.slot_index as usize) < ::NUM_SLOTS); + self.slots + .iter() + .position(|s| s.slot_index == slot.slot_index) + } + + pub fn texture_for_slot( + &self, + slot_index: u32, + ) -> Option { + assert!( + slot_index < (::NUM_SLOTS as u32), + "slot_index in range" + ); + assert!(!self.slots.is_empty(), "slots have been created"); + for state in self.slots.iter() { + if state.slot_index == slot_index { + return Some(state.texture.clone()); + } + } + None + } + + pub fn device(&self) -> Option { + let dev = self.device.clone(); + assert!(dev.is_some(), "device exists"); + assert!( + !self.slots.is_empty() || self.width == 0, + "post-init invariant" + ); + dev + } + + pub fn context(&self) -> Option { + let ctx = self.context.clone(); + assert!(ctx.is_some(), "context exists"); + assert!( + self.width > 0 || self.slots.is_empty(), + "post-init invariant" + ); + ctx + } +} + +#[cfg(target_os = "windows")] +unsafe fn create_d3d11_device_windows() -> Result< + ( + windows::Win32::Graphics::Direct3D11::ID3D11Device, + windows::Win32::Graphics::Direct3D11::ID3D11DeviceContext, + ), + BackendError, +> { + use windows::Win32::Foundation::HMODULE; + use windows::Win32::Graphics::Direct3D::{D3D_DRIVER_TYPE_HARDWARE, D3D_FEATURE_LEVEL_11_0}; + use windows::Win32::Graphics::Direct3D11::{ + D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_SDK_VERSION, D3D11CreateDevice, ID3D11Device, + ID3D11DeviceContext, + }; + let mut device: Option = None; + let mut context: Option = None; + let feature_levels = [D3D_FEATURE_LEVEL_11_0]; + unsafe { + D3D11CreateDevice( + None, + D3D_DRIVER_TYPE_HARDWARE, + HMODULE::default(), + D3D11_CREATE_DEVICE_BGRA_SUPPORT, + Some(&feature_levels), + D3D11_SDK_VERSION, + Some(&mut device), + None, + Some(&mut context), + ) + } + .map_err(|_| BackendError::PlatformUnsupported { + reason: "D3D11CreateDevice failed", + })?; + let device = device.ok_or(BackendError::PlatformUnsupported { + reason: "D3D11 device null", + })?; + let context = context.ok_or(BackendError::PlatformUnsupported { + reason: "D3D11 context null", + })?; + Ok((device, context)) +} + +#[cfg(target_os = "windows")] +unsafe fn create_keyed_mutex_texture_windows( + device: &windows::Win32::Graphics::Direct3D11::ID3D11Device, + width: u32, + height: u32, + slot_index: u32, +) -> Result< + ( + windows::Win32::Graphics::Direct3D11::ID3D11Texture2D, + windows::Win32::Graphics::Dxgi::IDXGIKeyedMutex, + u64, + ), + BackendError, +> { + use windows::Win32::Graphics::Direct3D11::{ + D3D11_BIND_RENDER_TARGET, D3D11_BIND_SHADER_RESOURCE, + D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX, D3D11_RESOURCE_MISC_SHARED_NTHANDLE, + D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, ID3D11Texture2D, + }; + use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SAMPLE_DESC}; + use windows::Win32::Graphics::Dxgi::{IDXGIKeyedMutex, IDXGIResource1}; + use windows::core::Interface; + assert!(width > 0); + assert!(height > 0); + assert!(slot_index < 8); + let desc = D3D11_TEXTURE2D_DESC { + Width: width, + Height: height, + MipLevels: 1, + ArraySize: 1, + Format: DXGI_FORMAT_B8G8R8A8_UNORM, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Usage: D3D11_USAGE_DEFAULT, + BindFlags: (D3D11_BIND_RENDER_TARGET.0 | D3D11_BIND_SHADER_RESOURCE.0) as u32, + CPUAccessFlags: 0, + MiscFlags: (D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX.0 | D3D11_RESOURCE_MISC_SHARED_NTHANDLE.0) + as u32, + }; + let mut texture: Option = None; + unsafe { device.CreateTexture2D(&desc, None, Some(&mut texture)) }.map_err(|_| { + BackendError::PlatformUnsupported { + reason: "CreateTexture2D failed", + } + })?; + let texture = texture.ok_or(BackendError::PlatformUnsupported { + reason: "texture null", + })?; + let keyed_mutex: IDXGIKeyedMutex = + texture + .cast() + .map_err(|_| BackendError::PlatformUnsupported { + reason: "IDXGIKeyedMutex cast failed", + })?; + let resource1: IDXGIResource1 = + texture + .cast() + .map_err(|_| BackendError::PlatformUnsupported { + reason: "IDXGIResource1 cast failed", + })?; + let access_rw: u32 = windows::Win32::Graphics::Dxgi::DXGI_SHARED_RESOURCE_READ.0 + | windows::Win32::Graphics::Dxgi::DXGI_SHARED_RESOURCE_WRITE.0; + let shared = + unsafe { resource1.CreateSharedHandle(None, access_rw, windows::core::PCWSTR::null()) } + .map_err(|_| BackendError::PlatformUnsupported { + reason: "CreateSharedHandle failed", + })?; + Ok((texture, keyed_mutex, shared.0 as u64)) +} + +#[cfg(target_os = "windows")] +const ACQUIRE_SYNC_WAIT_TIMEOUT: i32 = 0x102; +#[cfg(target_os = "windows")] +const ACQUIRE_SYNC_WAIT_ABANDONED: i32 = 0x80; + +#[cfg(not(target_os = "windows"))] +pub struct D3D11KeyedMutexBackend; + +#[cfg(not(target_os = "windows"))] +impl D3D11KeyedMutexBackend { + pub fn new() -> Result { + Err(BackendError::PlatformUnsupported { + reason: "D3D11 keyed-mutex backend is only available on Windows", + }) + } +} + +impl KeyedMutexBackend for D3D11KeyedMutexBackend { + type SlotHandle = D3D11SharedHandle; + const NUM_SLOTS: usize = 8; + + #[cfg(target_os = "windows")] + fn create_slots( + &mut self, + width: u32, + height: u32, + format: TextureFormat, + ) -> Result, BackendError> { + assert!(self.slots.is_empty(), "slots created once"); + if width == 0 || height == 0 { + return Err(BackendError::DimensionsOutOfRange { width, height }); + } + if !matches!(format, TextureFormat::Nv12) { + return Err(BackendError::UnsupportedFormat { format }); + } + let device = self + .device + .as_ref() + .ok_or(BackendError::PlatformUnsupported { + reason: "device dropped", + })?; + let mut out: Vec = Vec::with_capacity(Self::NUM_SLOTS); + for idx in 0..Self::NUM_SLOTS { + let idx_u32 = idx as u32; + let (texture, keyed_mutex, shared_handle) = + unsafe { create_keyed_mutex_texture_windows(device, width, height, idx_u32)? }; + self.slots.push(D3D11SlotState { + texture, + keyed_mutex, + shared_handle, + slot_index: idx_u32, + expected_key: 0, + acquired: false, + completed: false, + }); + out.push(D3D11SharedHandle { + raw_handle: shared_handle, + slot_index: idx_u32, + width, + height, + }); + } + self.width = width; + self.height = height; + self.format = format; + assert_eq!(out.len(), Self::NUM_SLOTS); + assert_eq!(self.slots.len(), Self::NUM_SLOTS); + Ok(out) + } + + #[cfg(not(target_os = "windows"))] + fn create_slots( + &mut self, + _width: u32, + _height: u32, + _format: TextureFormat, + ) -> Result, BackendError> { + Err(BackendError::PlatformUnsupported { + reason: "real D3D11 create_slots requires Windows", + }) + } + + #[cfg(target_os = "windows")] + fn acquire_write(&mut self, slot: &D3D11SharedHandle, key: u64) -> Result<(), BackendError> { + use windows::core::Interface; + let position = self + .find_slot(slot) + .ok_or(BackendError::PlatformUnsupported { + reason: "slot not found", + })?; + let state = &mut self.slots[position]; + if state.expected_key != key { + return Err(BackendError::KeyMismatch { + expected: state.expected_key, + observed: key, + }); + } + if state.acquired { + return Err(BackendError::AcquireWhileWriting { + slot_index: state.slot_index, + }); + } + let hr = unsafe { + (Interface::vtable(&state.keyed_mutex).AcquireSync)( + Interface::as_raw(&state.keyed_mutex), + key, + 0, + ) + }; + if hr.0 == ACQUIRE_SYNC_WAIT_TIMEOUT || hr.0 == ACQUIRE_SYNC_WAIT_ABANDONED { + return Err(BackendError::WouldBlock { + slot_index: state.slot_index, + }); + } + if hr.is_err() { + return Err(BackendError::KeyMismatch { + expected: key, + observed: u64::MAX, + }); + } + state.acquired = true; + state.completed = false; + assert!(state.acquired); + Ok(()) + } + + #[cfg(not(target_os = "windows"))] + fn acquire_write(&mut self, _slot: &D3D11SharedHandle, _key: u64) -> Result<(), BackendError> { + Err(BackendError::PlatformUnsupported { + reason: "real D3D11 acquire_write requires Windows", + }) + } + + #[cfg(target_os = "windows")] + fn release_write( + &mut self, + slot: &D3D11SharedHandle, + next_key: u64, + ) -> Result<(), BackendError> { + let position = self + .find_slot(slot) + .ok_or(BackendError::PlatformUnsupported { + reason: "slot not found", + })?; + let state = &mut self.slots[position]; + if !state.acquired { + return Err(BackendError::ReleaseWithoutAcquire { + slot_index: state.slot_index, + }); + } + unsafe { state.keyed_mutex.ReleaseSync(next_key) }.map_err(|_| { + BackendError::KeyMismatch { + expected: next_key, + observed: u64::MAX, + } + })?; + state.acquired = false; + state.expected_key = next_key; + state.completed = true; + assert!(!state.acquired); + assert!(state.completed); + Ok(()) + } + + #[cfg(not(target_os = "windows"))] + fn release_write( + &mut self, + _slot: &D3D11SharedHandle, + _next_key: u64, + ) -> Result<(), BackendError> { + Err(BackendError::PlatformUnsupported { + reason: "real D3D11 release_write requires Windows", + }) + } + + #[cfg(target_os = "windows")] + fn poll_complete(&mut self, slot: &D3D11SharedHandle) -> bool { + match self.find_slot(slot) { + Some(position) => self.slots[position].completed, + None => false, + } + } + + #[cfg(not(target_os = "windows"))] + fn poll_complete(&mut self, _slot: &D3D11SharedHandle) -> bool { + false + } + + #[cfg(target_os = "windows")] + fn mark_consumed(&mut self, slot: &D3D11SharedHandle) { + if let Some(position) = self.find_slot(slot) { + self.slots[position].completed = false; + } + } + + #[cfg(not(target_os = "windows"))] + fn mark_consumed(&mut self, _slot: &D3D11SharedHandle) {} +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + #[cfg(not(target_os = "windows"))] + fn instance_creation_fails_on_non_windows() { + let err = D3D11KeyedMutexBackend::new().err(); + assert!(matches!( + err, + Some(BackendError::PlatformUnsupported { .. }) + )); + } + + #[test] + fn handle_clones_preserve_index() { + let h = D3D11SharedHandle { + raw_handle: 0xdead, + slot_index: 3, + width: 1920, + height: 1080, + }; + let h2 = h.clone(); + assert_eq!(h2.slot_index, 3); + assert_eq!(h2.raw_handle, 0xdead); + } + + #[test] + #[cfg(target_os = "windows")] + fn windows_real_keyed_mutex_eight_slots_round_trip() { + let backend_result = D3D11KeyedMutexBackend::new(); + let mut backend = match backend_result { + Ok(b) => b, + Err(BackendError::PlatformUnsupported { .. }) => return, + Err(other) => unreachable!("unexpected backend init err: {other:?}"), + }; + let slots_result = backend.create_slots(64, 64, TextureFormat::Nv12); + let slots = match slots_result { + Ok(s) => s, + Err(BackendError::PlatformUnsupported { .. }) => return, + Err(other) => unreachable!("unexpected create_slots err: {other:?}"), + }; + assert_eq!(slots.len(), 8); + for (idx, slot) in slots.iter().enumerate() { + assert_eq!(slot.slot_index, idx as u32); + assert_ne!(slot.raw_handle, 0); + assert_eq!(slot.width, 64); + } + let slot = slots[0].clone(); + backend.acquire_write(&slot, 0).expect("acquire key=0"); + backend.release_write(&slot, 1).expect("release key=1"); + assert!(backend.poll_complete(&slot)); + backend.mark_consumed(&slot); + assert!(!backend.poll_complete(&slot)); + backend.acquire_write(&slot, 1).expect("acquire key=1"); + backend.release_write(&slot, 2).expect("release key=2"); + assert!(backend.poll_complete(&slot)); + } +} diff --git a/fluxer_desktop/native/encoder-ring/src/encoder_handoff.rs b/fluxer_desktop/native/encoder-ring/src/encoder_handoff.rs new file mode 100644 index 0000000..c98e8f4 --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/src/encoder_handoff.rs @@ -0,0 +1,819 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::ring::RingError; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct EncoderDims { + pub width: u32, + pub height: u32, +} + +impl EncoderDims { + pub fn new(width: u32, height: u32) -> Self { + assert!(width > 0, "width must be positive"); + assert!(height > 0, "height must be positive"); + Self { width, height } + } +} + +pub const ENCODER_FRAME_RATE_MIN: u32 = 1; +pub const ENCODER_FRAME_RATE_MAX: u32 = 240; +pub const ENCODER_FRAME_RATE_DEFAULT: u32 = 60; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct EncoderFrameRate { + pub numerator: u32, + pub denominator: u32, +} + +impl EncoderFrameRate { + pub fn new(numerator: u32, denominator: u32) -> Self { + assert!( + numerator >= ENCODER_FRAME_RATE_MIN, + "fps numerator positive" + ); + assert!(denominator > 0, "fps denominator positive"); + let frame_rate = Self { + numerator: numerator.min(ENCODER_FRAME_RATE_MAX), + denominator, + }; + assert!(frame_rate.numerator >= ENCODER_FRAME_RATE_MIN); + assert!(frame_rate.denominator > 0); + frame_rate + } + + pub fn from_fps(fps: u32) -> Self { + let numerator = fps.clamp(ENCODER_FRAME_RATE_MIN, ENCODER_FRAME_RATE_MAX); + Self::new(numerator, 1) + } + + pub fn frame_interval_us(self) -> u64 { + let numerator = u64::from(self.numerator); + let denominator = u64::from(self.denominator); + assert!(numerator > 0, "fps numerator positive"); + assert!(denominator > 0, "fps denominator positive"); + ((1_000_000u64 * denominator) + numerator - 1) / numerator + } + + pub fn gop_pic_size(self) -> u16 { + let rounded = (u64::from(self.numerator) + u64::from(self.denominator) - 1) + / u64::from(self.denominator); + let bounded = rounded.clamp(1, u64::from(ENCODER_FRAME_RATE_MAX)); + bounded as u16 + } +} + +impl Default for EncoderFrameRate { + fn default() -> Self { + Self::from_fps(ENCODER_FRAME_RATE_DEFAULT) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct EncoderSubmission { + pub shared_handle: u64, + pub keyed_mutex_key: u64, + pub dims: EncoderDims, + pub sequence: u64, + pub capture_pts_us: Option, +} + +impl EncoderSubmission { + pub fn new(shared_handle: u64, keyed_mutex_key: u64, dims: EncoderDims, sequence: u64) -> Self { + assert!(shared_handle != 0, "shared handle must be non-zero"); + assert!(dims.width > 0, "dims width positive"); + let s = Self { + shared_handle, + keyed_mutex_key, + dims, + sequence, + capture_pts_us: None, + }; + assert!(s.shared_handle == shared_handle, "post construct intact"); + assert!(s.capture_pts_us.is_none(), "capture pts defaults absent"); + s + } + + pub fn with_capture_pts_us(mut self, capture_pts_us: u64) -> Self { + assert!(self.shared_handle != 0, "shared handle must be non-zero"); + self.capture_pts_us = Some(capture_pts_us); + assert_eq!( + self.capture_pts_us, + Some(capture_pts_us), + "capture pts recorded" + ); + self + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PicParams { + pub pts_us: u64, + pub force_keyframe: bool, +} + +impl PicParams { + pub fn new(pts_us: u64, force_keyframe: bool) -> Self { + let p = Self { + pts_us, + force_keyframe, + }; + assert!(p.pts_us == pts_us, "pts_us intact"); + assert!(p.force_keyframe == force_keyframe, "force_keyframe intact"); + p + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct HandoffSlot { + pub slot_index: u32, + pub shared_handle: u64, +} + +impl HandoffSlot { + pub fn new(slot_index: u32, shared_handle: u64) -> Self { + assert!(slot_index < 64, "slot_index within plausible bound"); + assert!(shared_handle != 0, "shared_handle non-zero"); + Self { + slot_index, + shared_handle, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct EncodedBitstream { + pub data: Vec, + pub pts_us: u64, + pub dts_us: u64, + pub is_keyframe: bool, +} + +impl EncodedBitstream { + pub fn new(data: Vec, pts_us: u64, dts_us: u64, is_keyframe: bool) -> Self { + assert!(!data.is_empty(), "encoded bitstream must be non-empty"); + assert!(data.len() <= MAX_BITSTREAM_BYTES, "bitstream within cap"); + Self { + data, + pts_us, + dts_us, + is_keyframe, + } + } +} + +pub const MAX_BITSTREAM_BYTES: usize = 16 * 1024 * 1024; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum EncoderError { + SdkNotFound { + vendor: &'static str, + dll: &'static str, + }, + SymbolMissing { + vendor: &'static str, + symbol: &'static str, + }, + SessionInitFailed { + vendor: &'static str, + status: i64, + }, + RegisterFailed { + vendor: &'static str, + status: i64, + }, + EncodeFailed { + vendor: &'static str, + status: i64, + }, + BitstreamReadFailed { + vendor: &'static str, + status: i64, + }, + SlotUnknown { + slot_index: u32, + }, + KeyMismatch { + expected: u64, + observed: u64, + }, + DimensionsOutOfRange { + width: u32, + height: u32, + }, + PlatformUnsupported { + reason: &'static str, + }, + BitstreamTooLarge { + byte_size: usize, + }, + NotImplemented { + what: &'static str, + }, +} + +impl std::fmt::Display for EncoderError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::SdkNotFound { vendor, dll } => { + write!(f, "{vendor} SDK runtime '{dll}' not found") + } + Self::SymbolMissing { vendor, symbol } => { + write!(f, "{vendor} symbol '{symbol}' missing from runtime") + } + Self::SessionInitFailed { vendor, status } => { + write!(f, "{vendor} session init failed: status={status}") + } + Self::RegisterFailed { vendor, status } => { + write!(f, "{vendor} register failed: status={status}") + } + Self::EncodeFailed { vendor, status } => { + write!(f, "{vendor} encode failed: status={status}") + } + Self::BitstreamReadFailed { vendor, status } => { + write!(f, "{vendor} bitstream read failed: status={status}") + } + Self::SlotUnknown { slot_index } => write!(f, "slot {slot_index} unknown"), + Self::KeyMismatch { expected, observed } => { + write!( + f, + "keyed-mutex key mismatch: expected={expected} observed={observed}" + ) + } + Self::DimensionsOutOfRange { width, height } => { + write!(f, "encoder dims out of range: {width}x{height}") + } + Self::PlatformUnsupported { reason } => write!(f, "platform unsupported: {reason}"), + Self::BitstreamTooLarge { byte_size } => { + write!(f, "bitstream {byte_size} bytes exceeds cap") + } + Self::NotImplemented { what } => write!(f, "not implemented: {what}"), + } + } +} + +impl std::error::Error for EncoderError {} + +pub trait EncoderCompletionCallback: Send { + fn on_complete(&mut self, sequence: u64, encoded_bytes: u32); +} + +pub trait NvencHandoff: Send { + fn register_slot( + &mut self, + shared_handle: u64, + key: u64, + dims: EncoderDims, + ) -> Result; + + fn encode_shared_async( + &mut self, + slot: HandoffSlot, + key: u64, + dims: EncoderDims, + pic_params: PicParams, + ) -> Result<(), EncoderError>; + + fn poll_completed(&mut self, slot: HandoffSlot) -> Option; + + fn unregister_slot(&mut self, slot: HandoffSlot); + + fn encode_shared( + &mut self, + submission: EncoderSubmission, + callback: &mut dyn EncoderCompletionCallback, + ) -> Result<(), RingError>; +} + +pub trait AmfHandoff: Send { + fn register_slot( + &mut self, + shared_handle: u64, + key: u64, + dims: EncoderDims, + ) -> Result; + + fn encode_shared_async( + &mut self, + slot: HandoffSlot, + key: u64, + dims: EncoderDims, + pic_params: PicParams, + ) -> Result<(), EncoderError>; + + fn poll_completed(&mut self, slot: HandoffSlot) -> Option; + + fn unregister_slot(&mut self, slot: HandoffSlot); + + fn encode_shared( + &mut self, + submission: EncoderSubmission, + callback: &mut dyn EncoderCompletionCallback, + ) -> Result<(), RingError>; +} + +pub trait QsvHandoff: Send { + fn register_slot( + &mut self, + shared_handle: u64, + key: u64, + dims: EncoderDims, + ) -> Result; + + fn encode_shared_async( + &mut self, + slot: HandoffSlot, + key: u64, + dims: EncoderDims, + pic_params: PicParams, + ) -> Result<(), EncoderError>; + + fn poll_completed(&mut self, slot: HandoffSlot) -> Option; + + fn unregister_slot(&mut self, slot: HandoffSlot); + + fn encode_shared( + &mut self, + submission: EncoderSubmission, + callback: &mut dyn EncoderCompletionCallback, + ) -> Result<(), RingError>; +} + +pub trait VideoToolboxHandoff: Send { + fn register_slot( + &mut self, + iosurface_handle: u64, + key: u64, + dims: EncoderDims, + ) -> Result; + + fn encode_shared_async( + &mut self, + slot: HandoffSlot, + key: u64, + dims: EncoderDims, + pic_params: PicParams, + ) -> Result<(), EncoderError>; + + fn poll_completed(&mut self, slot: HandoffSlot) -> Option; + + fn unregister_slot(&mut self, slot: HandoffSlot); + + fn encode_shared( + &mut self, + submission: EncoderSubmission, + callback: &mut dyn EncoderCompletionCallback, + ) -> Result<(), RingError>; +} + +pub struct VtNoOpHandoff { + accepted: u64, + next_slot_index: u32, + pending: std::collections::VecDeque<(u64, u64)>, +} + +impl VtNoOpHandoff { + pub fn new() -> Self { + let h = Self { + accepted: 0, + next_slot_index: 0, + pending: std::collections::VecDeque::with_capacity(16), + }; + assert_eq!(h.accepted, 0, "fresh handoff has no accepted frames"); + assert_eq!(h.next_slot_index, 0, "fresh handoff slot index zero"); + h + } + + pub fn accepted_count(&self) -> u64 { + let n = self.accepted; + assert!( + self.pending.len() <= u32::MAX as usize, + "pending queue plausible" + ); + assert!(n >= self.pending.len() as u64, "accepted >= pending"); + n + } + + pub fn pending_len(&self) -> usize { + let len = self.pending.len(); + assert!( + len <= self.pending.capacity().max(1), + "pending within capacity bound" + ); + assert!(len as u64 <= self.accepted, "pending <= accepted"); + len + } +} + +impl Default for VtNoOpHandoff { + fn default() -> Self { + Self::new() + } +} + +impl VideoToolboxHandoff for VtNoOpHandoff { + fn register_slot( + &mut self, + iosurface_handle: u64, + _key: u64, + dims: EncoderDims, + ) -> Result { + if iosurface_handle == 0 { + return Err(EncoderError::SlotUnknown { + slot_index: u32::MAX, + }); + } + if dims.width == 0 || dims.height == 0 { + return Err(EncoderError::DimensionsOutOfRange { + width: dims.width, + height: dims.height, + }); + } + let slot = HandoffSlot::new(self.next_slot_index, iosurface_handle); + self.next_slot_index = self.next_slot_index.saturating_add(1); + assert!( + slot.shared_handle == iosurface_handle, + "slot handle round-trip" + ); + assert!(self.next_slot_index > 0, "slot counter advanced"); + Ok(slot) + } + + fn encode_shared_async( + &mut self, + slot: HandoffSlot, + _key: u64, + dims: EncoderDims, + pic_params: PicParams, + ) -> Result<(), EncoderError> { + if dims.width == 0 || dims.height == 0 { + return Err(EncoderError::DimensionsOutOfRange { + width: dims.width, + height: dims.height, + }); + } + self.pending.push_back((self.accepted, pic_params.pts_us)); + self.accepted = self.accepted.saturating_add(1); + assert!(slot.shared_handle != 0, "async slot handle non-zero"); + assert!(self.accepted > 0, "encode_shared_async advanced accepted"); + Ok(()) + } + + fn poll_completed(&mut self, _slot: HandoffSlot) -> Option { + None + } + + fn unregister_slot(&mut self, slot: HandoffSlot) { + assert!(slot.shared_handle != 0, "unregister slot handle non-zero"); + } + + fn encode_shared( + &mut self, + submission: EncoderSubmission, + callback: &mut dyn EncoderCompletionCallback, + ) -> Result<(), RingError> { + if submission.shared_handle == 0 { + return Err(RingError::UnknownSlot); + } + if submission.dims.width == 0 || submission.dims.height == 0 { + return Err(RingError::BackendFailed { + source: crate::backend::BackendError::DimensionsOutOfRange { + width: submission.dims.width, + height: submission.dims.height, + }, + }); + } + let pre_accepted = self.accepted; + self.pending.push_back((submission.sequence, 0)); + self.accepted = self.accepted.saturating_add(1); + callback.on_complete(submission.sequence, 0); + assert!( + self.accepted == pre_accepted + 1, + "VtNoOpHandoff accepted advanced" + ); + assert!(submission.dims.width > 0, "submission dims preserved"); + Ok(()) + } +} + +pub struct NotImplementedHandoff { + pub vendor: &'static str, +} + +impl NotImplementedHandoff { + pub fn nvenc() -> Self { + Self { vendor: "nvenc" } + } + + pub fn amf() -> Self { + Self { vendor: "amf" } + } + + pub fn qsv() -> Self { + Self { vendor: "qsv" } + } +} + +pub fn compute_dts_offset_us(first_pts_us: u64, num_b_frames: u32, frame_interval_us: u64) -> i64 { + assert!(frame_interval_us > 0, "frame interval positive"); + assert!(num_b_frames <= 8, "B-frame count plausible"); + let offset = (num_b_frames as u64).saturating_mul(frame_interval_us); + let result = -(offset as i64); + let _ = first_pts_us; + assert!(result <= 0, "DTS offset is non-positive for B-frames"); + result +} + +pub fn apply_dts_offset(pts_us: u64, offset_us: i64) -> u64 { + let signed_pts = pts_us as i64; + let dts = signed_pts.saturating_add(offset_us); + let clamped = if dts < 0 { 0 } else { dts as u64 }; + assert!( + clamped <= pts_us || offset_us > 0, + "DTS <= PTS without future B-frames" + ); + clamped +} + +macro_rules! impl_not_implemented_for_trait { + ($trait_name:ident, $vendor_tag:expr) => { + impl $trait_name for NotImplementedHandoff { + fn register_slot( + &mut self, + shared_handle: u64, + _key: u64, + dims: EncoderDims, + ) -> Result { + assert!(shared_handle != 0, "shared_handle non-zero"); + assert!(dims.width > 0, "dims width positive"); + Err(EncoderError::NotImplemented { + what: concat!($vendor_tag, "::register_slot"), + }) + } + + fn encode_shared_async( + &mut self, + slot: HandoffSlot, + _key: u64, + dims: EncoderDims, + _pic_params: PicParams, + ) -> Result<(), EncoderError> { + assert!(slot.shared_handle != 0, "slot shared_handle non-zero"); + assert!(dims.width > 0, "dims width positive"); + Err(EncoderError::NotImplemented { + what: concat!($vendor_tag, "::encode_shared_async"), + }) + } + + fn poll_completed(&mut self, _slot: HandoffSlot) -> Option { + None + } + + fn unregister_slot(&mut self, slot: HandoffSlot) { + assert!(slot.shared_handle != 0, "slot shared_handle non-zero"); + } + + fn encode_shared( + &mut self, + submission: EncoderSubmission, + _callback: &mut dyn EncoderCompletionCallback, + ) -> Result<(), RingError> { + assert!(submission.shared_handle != 0, "submission handle non-zero"); + assert!(submission.dims.width > 0, "submission width positive"); + Err(RingError::NotImplemented { + what: concat!($vendor_tag, "::encode_shared"), + }) + } + } + }; +} + +impl_not_implemented_for_trait!(NvencHandoff, "NvencHandoff"); +impl_not_implemented_for_trait!(AmfHandoff, "AmfHandoff"); +impl_not_implemented_for_trait!(QsvHandoff, "QsvHandoff"); + +#[cfg(test)] +mod tests { + use super::*; + + struct NoopCallback; + impl EncoderCompletionCallback for NoopCallback { + fn on_complete(&mut self, _sequence: u64, _encoded_bytes: u32) {} + } + + fn submission() -> EncoderSubmission { + EncoderSubmission::new(0xfeed_face, 7, EncoderDims::new(1920, 1080), 42) + } + + #[test] + fn nvenc_stub_returns_not_implemented() { + let mut h = NotImplementedHandoff::nvenc(); + let mut cb = NoopCallback; + let err = NvencHandoff::encode_shared(&mut h, submission(), &mut cb).err(); + assert!(matches!(err, Some(RingError::NotImplemented { what }) + if what.contains("Nvenc"))); + } + + #[test] + fn amf_stub_returns_not_implemented() { + let mut h = NotImplementedHandoff::amf(); + let mut cb = NoopCallback; + let err = AmfHandoff::encode_shared(&mut h, submission(), &mut cb).err(); + assert!(matches!(err, Some(RingError::NotImplemented { what }) + if what.contains("Amf"))); + } + + #[test] + fn qsv_stub_returns_not_implemented() { + let mut h = NotImplementedHandoff::qsv(); + let mut cb = NoopCallback; + let err = QsvHandoff::encode_shared(&mut h, submission(), &mut cb).err(); + assert!(matches!(err, Some(RingError::NotImplemented { what }) + if what.contains("Qsv"))); + } + + #[test] + fn encoder_dims_rejects_zero_width_via_assert() { + let result = std::panic::catch_unwind(|| EncoderDims::new(0, 1080)); + assert!(result.is_err()); + } + + #[test] + fn nvenc_stub_register_returns_not_implemented() { + let mut h = NotImplementedHandoff::nvenc(); + let dims = EncoderDims::new(1920, 1080); + let err = NvencHandoff::register_slot(&mut h, 0xabc, 0, dims).err(); + assert!(matches!(err, Some(EncoderError::NotImplemented { what }) + if what.contains("Nvenc"))); + } + + #[test] + fn amf_stub_register_returns_not_implemented() { + let mut h = NotImplementedHandoff::amf(); + let dims = EncoderDims::new(1920, 1080); + let err = AmfHandoff::register_slot(&mut h, 0xabc, 0, dims).err(); + assert!(matches!(err, Some(EncoderError::NotImplemented { what }) + if what.contains("Amf"))); + } + + #[test] + fn qsv_stub_register_returns_not_implemented() { + let mut h = NotImplementedHandoff::qsv(); + let dims = EncoderDims::new(1920, 1080); + let err = QsvHandoff::register_slot(&mut h, 0xabc, 0, dims).err(); + assert!(matches!(err, Some(EncoderError::NotImplemented { what }) + if what.contains("Qsv"))); + } + + #[test] + fn stub_poll_completed_returns_none() { + let mut h_nv = NotImplementedHandoff::nvenc(); + let mut h_amf = NotImplementedHandoff::amf(); + let mut h_qsv = NotImplementedHandoff::qsv(); + let slot = HandoffSlot::new(0, 0xdead); + assert!(NvencHandoff::poll_completed(&mut h_nv, slot).is_none()); + assert!(AmfHandoff::poll_completed(&mut h_amf, slot).is_none()); + assert!(QsvHandoff::poll_completed(&mut h_qsv, slot).is_none()); + } + + #[test] + fn stub_unregister_does_not_panic() { + let mut h = NotImplementedHandoff::nvenc(); + let slot = HandoffSlot::new(0, 0xdead); + NvencHandoff::unregister_slot(&mut h, slot); + } + + #[test] + fn encoded_bitstream_rejects_empty() { + let result = std::panic::catch_unwind(|| EncodedBitstream::new(vec![], 0, 0, true)); + assert!(result.is_err()); + } + + #[test] + fn dts_offset_zero_for_no_b_frames() { + let offset = compute_dts_offset_us(1000, 0, 16_666); + assert_eq!(offset, 0); + } + + #[test] + fn dts_offset_negative_for_b_frames() { + let offset = compute_dts_offset_us(1000, 2, 16_666); + assert_eq!(offset, -(2 * 16_666_i64)); + } + + #[test] + fn dts_offset_application_clamps_to_zero() { + let dts = apply_dts_offset(100, -1000); + assert_eq!(dts, 0); + } + + #[test] + fn dts_offset_application_below_pts_for_b_frames() { + let pts = 100_000_u64; + let dts = apply_dts_offset(pts, -33_333); + assert!(dts < pts); + assert_eq!(dts, 66_667); + } + + #[test] + fn encoder_frame_rate_derives_interval_and_gop() { + let sixty = EncoderFrameRate::from_fps(60); + assert_eq!(sixty.frame_interval_us(), 16_667); + assert_eq!(sixty.gop_pic_size(), 60); + let capped = EncoderFrameRate::from_fps(999); + assert_eq!(capped.numerator, ENCODER_FRAME_RATE_MAX); + assert_eq!(capped.gop_pic_size(), ENCODER_FRAME_RATE_MAX as u16); + } + + #[test] + fn handoff_slot_rejects_zero_handle() { + let result = std::panic::catch_unwind(|| HandoffSlot::new(0, 0)); + assert!(result.is_err()); + } + + #[test] + fn submission_capture_pts_defaults_absent_and_round_trips() { + let s = submission(); + assert_eq!(s.capture_pts_us, None); + let with_pts = s.with_capture_pts_us(123_456); + assert_eq!(with_pts.capture_pts_us, Some(123_456)); + assert_eq!(with_pts.sequence, s.sequence); + assert_eq!(with_pts.shared_handle, s.shared_handle); + } + + struct CountingCallback { + seen: Vec<(u64, u32)>, + } + + impl EncoderCompletionCallback for CountingCallback { + fn on_complete(&mut self, sequence: u64, encoded_bytes: u32) { + self.seen.push((sequence, encoded_bytes)); + } + } + + #[test] + fn vt_noop_accepts_frames_in_fifo_order() { + let mut h = VtNoOpHandoff::new(); + let mut cb = CountingCallback { seen: Vec::new() }; + let dims = EncoderDims::new(1920, 1080); + for seq in 1..=5u64 { + let s = EncoderSubmission::new(0xfeed_face_u64, 0, dims, seq); + VideoToolboxHandoff::encode_shared(&mut h, s, &mut cb).expect("vt encode_shared"); + } + assert_eq!(cb.seen.len(), 5); + for (idx, &(seq, _)) in cb.seen.iter().enumerate() { + assert_eq!(seq, (idx as u64) + 1, "fifo sequence"); + } + assert_eq!(h.accepted_count(), 5); + } + + #[test] + fn vt_noop_register_returns_slot() { + let mut h = VtNoOpHandoff::new(); + let dims = EncoderDims::new(1920, 1080); + let slot_a = + VideoToolboxHandoff::register_slot(&mut h, 0xabc, 0, dims).expect("register a"); + let slot_b = + VideoToolboxHandoff::register_slot(&mut h, 0xdef, 0, dims).expect("register b"); + assert_eq!(slot_a.shared_handle, 0xabc); + assert_eq!(slot_b.shared_handle, 0xdef); + assert_ne!(slot_a.slot_index, slot_b.slot_index); + } + + #[test] + fn vt_noop_register_rejects_zero_handle() { + let mut h = VtNoOpHandoff::new(); + let dims = EncoderDims::new(1920, 1080); + let err = VideoToolboxHandoff::register_slot(&mut h, 0, 0, dims).err(); + assert!(matches!(err, Some(EncoderError::SlotUnknown { .. }))); + } + + #[test] + fn vt_noop_encode_shared_rejects_zero_handle() { + let mut h = VtNoOpHandoff::new(); + let mut cb = CountingCallback { seen: Vec::new() }; + let dims = EncoderDims::new(1920, 1080); + let s = EncoderSubmission { + shared_handle: 0, + keyed_mutex_key: 0, + dims, + sequence: 1, + capture_pts_us: None, + }; + let err = VideoToolboxHandoff::encode_shared(&mut h, s, &mut cb).err(); + assert!(matches!(err, Some(RingError::UnknownSlot))); + assert!(cb.seen.is_empty(), "no callback on rejection"); + } + + #[test] + fn vt_noop_encode_shared_async_advances_accepted() { + let mut h = VtNoOpHandoff::new(); + let dims = EncoderDims::new(1280, 720); + let slot = HandoffSlot::new(0, 0xfeed); + let params = PicParams::new(16_666, false); + VideoToolboxHandoff::encode_shared_async(&mut h, slot, 0, dims, params) + .expect("async encode ok"); + assert_eq!(h.accepted_count(), 1); + assert_eq!(h.pending_len(), 1); + } +} diff --git a/fluxer_desktop/native/encoder-ring/src/lib.rs b/fluxer_desktop/native/encoder-ring/src/lib.rs new file mode 100644 index 0000000..f717ff0 --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/src/lib.rs @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![deny(clippy::too_many_lines)] +#![deny(clippy::unwrap_used)] +#![deny(clippy::panic)] +#![deny(warnings)] + +pub mod backend; +pub mod d3d11; +pub mod encoder_handoff; +pub mod metal_iosurface; +pub mod ring; + +#[cfg(target_os = "macos")] +pub mod metal_iosurface_macos; + +#[cfg(target_os = "macos")] +pub mod vt_compression_macos; + +#[cfg(target_os = "windows")] +pub mod amf; +#[cfg(target_os = "windows")] +pub mod nvenc; +#[cfg(target_os = "windows")] +pub mod qsv; + +pub use backend::{CpuMemcpyBackend, CpuSlotHandle, KeyedMutexBackend, TextureFormat}; +pub use d3d11::D3D11KeyedMutexBackend; +pub use encoder_handoff::{ + AmfHandoff, EncodedBitstream, EncoderDims, EncoderError, EncoderFrameRate, EncoderSubmission, + HandoffSlot, NotImplementedHandoff, NvencHandoff, PicParams, QsvHandoff, VideoToolboxHandoff, + VtNoOpHandoff, apply_dts_offset, compute_dts_offset_us, +}; +pub use metal_iosurface::{ + IoSurfaceSlotHandle, METAL_IOSURFACE_SEED_BASE, MetalSharedTextureBackend, +}; + +pub use ring::{ + DUPLICATE_COUNT_MAX, EncoderInputRing, EncoderReady, FillReservation, RING_SIZE, RingError, + RingMetrics, +}; +#[cfg(target_os = "macos")] +pub use vt_compression_macos::{VtCompressionHandoff, VtPixelTransfer}; + +#[cfg(target_os = "windows")] +pub use amf::AmfD3D11Handoff; +#[cfg(target_os = "windows")] +pub use nvenc::{COMPLETION_RING_CAPACITY as NVENC_COMPLETION_RING_CAPACITY, NvencD3D11Handoff}; +#[cfg(target_os = "windows")] +pub use qsv::QsvD3D11Handoff; + +pub const NV12_BPP_NUMERATOR: u32 = 3; +pub const NV12_BPP_DENOMINATOR: u32 = 2; + +pub const MAX_FRAME_WIDTH: u32 = 7680; +pub const MAX_FRAME_HEIGHT: u32 = 4320; + +#[inline] +pub const fn nv12_byte_size(width: u32, height: u32) -> usize { + let w = width as usize; + let h = height as usize; + (w * h * NV12_BPP_NUMERATOR as usize) / NV12_BPP_DENOMINATOR as usize +} diff --git a/fluxer_desktop/native/encoder-ring/src/metal_iosurface.rs b/fluxer_desktop/native/encoder-ring/src/metal_iosurface.rs new file mode 100644 index 0000000..db9f1c8 --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/src/metal_iosurface.rs @@ -0,0 +1,447 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; + +use crate::backend::{BackendError, KeyedMutexBackend, NUM_SLOTS_DEFAULT, TextureFormat}; +use crate::{MAX_FRAME_HEIGHT, MAX_FRAME_WIDTH}; + +pub const METAL_IOSURFACE_SEED_BASE: u64 = 0; + +#[derive(Clone)] +pub struct IoSurfaceSlotHandle { + inner: Arc, +} + +impl IoSurfaceSlotHandle { + pub fn slot_index(&self) -> u32 { + let idx = self.inner.slot_index; + assert!((idx as usize) < NUM_SLOTS_DEFAULT, "slot_index in range"); + assert!( + self.inner.iosurface_handle != 0, + "iosurface_handle non-zero" + ); + idx + } + + pub fn iosurface_handle(&self) -> u64 { + let h = self.inner.iosurface_handle; + assert!(h != 0, "iosurface_handle non-zero on read"); + assert!( + (self.inner.slot_index as usize) < NUM_SLOTS_DEFAULT, + "slot_index intact" + ); + h + } + + #[cfg(target_os = "macos")] + pub fn iosurface_ptr(&self) -> *mut core::ffi::c_void { + let p = self.inner.iosurface_ptr; + assert!(!p.is_null(), "iosurface_ptr non-null on read"); + assert!( + self.inner.iosurface_handle != 0, + "handle non-zero alongside ptr" + ); + p + } + + pub fn current_key(&self) -> u64 { + let key = self.inner.current_key.load(Ordering::Acquire); + assert!( + self.inner.iosurface_handle != 0, + "handle intact during key read" + ); + assert!( + (self.inner.slot_index as usize) < NUM_SLOTS_DEFAULT, + "slot_index intact" + ); + key + } + + pub fn is_acquired(&self) -> bool { + let a = self.inner.acquired.load(Ordering::Acquire); + assert!( + self.inner.iosurface_handle != 0, + "handle intact during acquired probe" + ); + assert!( + (self.inner.slot_index as usize) < NUM_SLOTS_DEFAULT, + "slot_index intact" + ); + a + } +} + +struct IoSurfaceSlotInner { + slot_index: u32, + iosurface_handle: u64, + #[cfg(target_os = "macos")] + iosurface_ptr: *mut core::ffi::c_void, + current_key: AtomicU64, + acquired: AtomicBool, + completed: AtomicBool, +} + +#[cfg(target_os = "macos")] +unsafe impl Send for IoSurfaceSlotInner {} +#[cfg(target_os = "macos")] +unsafe impl Sync for IoSurfaceSlotInner {} + +pub struct MetalSharedTextureBackend { + width: u32, + height: u32, + format: TextureFormat, + slots_created: bool, + slot_handles: Vec, + #[cfg(target_os = "macos")] + iosurfaces: Vec, +} + +impl MetalSharedTextureBackend { + pub fn new() -> Self { + let backend = Self { + width: 0, + height: 0, + format: TextureFormat::Nv12, + slots_created: false, + slot_handles: Vec::with_capacity(NUM_SLOTS_DEFAULT), + #[cfg(target_os = "macos")] + iosurfaces: Vec::with_capacity(NUM_SLOTS_DEFAULT), + }; + assert!(!backend.slots_created, "fresh backend has no slots"); + assert!( + backend.slot_handles.is_empty(), + "fresh backend handles empty" + ); + backend + } + + pub fn width(&self) -> u32 { + assert!(self.width <= MAX_FRAME_WIDTH, "width within cap"); + assert!(self.height <= MAX_FRAME_HEIGHT, "height within cap"); + self.width + } + + pub fn height(&self) -> u32 { + assert!(self.height <= MAX_FRAME_HEIGHT, "height within cap"); + assert!(self.width <= MAX_FRAME_WIDTH, "width within cap"); + self.height + } + + pub fn slot_iosurface_handle(&self, slot_index: u32) -> Option { + let idx = slot_index as usize; + if idx >= self.slot_handles.len() { + return None; + } + let h = self.slot_handles[idx]; + assert!(h != 0, "stored iosurface handle non-zero"); + Some(h) + } + + #[cfg(target_os = "macos")] + pub fn slot_iosurface_ptr(&self, slot_index: u32) -> Option<*mut core::ffi::c_void> { + let idx = slot_index as usize; + if idx >= self.iosurfaces.len() { + return None; + } + let p = self.iosurfaces[idx].as_ptr(); + assert!(!p.is_null(), "stored iosurface ptr non-null"); + assert!( + idx < self.slot_handles.len(), + "ptr slot mirrors handle slot" + ); + Some(p) + } + + #[cfg(target_os = "macos")] + pub fn slot_iosurface_mut( + &mut self, + slot_index: u32, + ) -> Option<&mut crate::metal_iosurface_macos::OwnedIoSurface> { + let idx = slot_index as usize; + if idx >= self.iosurfaces.len() { + return None; + } + assert!( + idx < self.slot_handles.len(), + "mut slot mirrors handle slot" + ); + Some(&mut self.iosurfaces[idx]) + } + + #[cfg(not(target_os = "macos"))] + fn allocate_slot_handles( + &mut self, + _width: u32, + _height: u32, + _format: TextureFormat, + ) -> Result, BackendError> { + Err(BackendError::PlatformUnsupported { + reason: "MetalSharedTextureBackend requires macOS", + }) + } + + #[cfg(target_os = "macos")] + fn allocate_slot_handles( + &mut self, + width: u32, + height: u32, + format: TextureFormat, + ) -> Result, BackendError> { + if !matches!(format, TextureFormat::Nv12) { + return Err(BackendError::UnsupportedFormat { format }); + } + let mut handles: Vec = Vec::with_capacity(NUM_SLOTS_DEFAULT); + let mut owned: Vec = + Vec::with_capacity(NUM_SLOTS_DEFAULT); + for slot in 0..NUM_SLOTS_DEFAULT { + let surface = crate::metal_iosurface_macos::OwnedIoSurface::create_nv12(width, height) + .map_err(|_| BackendError::PlatformUnsupported { + reason: "IOSurfaceCreate failed", + })?; + let raw = surface.handle(); + assert!(raw != 0, "IOSurface raw non-zero for slot"); + assert!(slot < NUM_SLOTS_DEFAULT, "slot index in range"); + handles.push(raw); + owned.push(surface); + } + self.iosurfaces = owned; + Ok(handles) + } +} + +impl Default for MetalSharedTextureBackend { + fn default() -> Self { + Self::new() + } +} + +impl KeyedMutexBackend for MetalSharedTextureBackend { + type SlotHandle = IoSurfaceSlotHandle; + const NUM_SLOTS: usize = NUM_SLOTS_DEFAULT; + + fn create_slots( + &mut self, + width: u32, + height: u32, + format: TextureFormat, + ) -> Result, BackendError> { + if width == 0 || height == 0 || width > MAX_FRAME_WIDTH || height > MAX_FRAME_HEIGHT { + return Err(BackendError::DimensionsOutOfRange { width, height }); + } + if !matches!(format, TextureFormat::Nv12) { + return Err(BackendError::UnsupportedFormat { format }); + } + assert!(!self.slots_created, "slots created once"); + let raw_handles = self.allocate_slot_handles(width, height, format)?; + assert_eq!( + raw_handles.len(), + NUM_SLOTS_DEFAULT, + "allocator returns NUM_SLOTS handles" + ); + let mut handles: Vec = Vec::with_capacity(NUM_SLOTS_DEFAULT); + for (idx, raw) in raw_handles.iter().enumerate() { + assert!(*raw != 0, "raw iosurface handle non-zero"); + #[cfg(target_os = "macos")] + let surface_ptr = self.iosurfaces[idx].as_ptr(); + #[cfg(target_os = "macos")] + assert!(!surface_ptr.is_null(), "surface ptr non-null at slot setup"); + let inner = IoSurfaceSlotInner { + slot_index: idx as u32, + iosurface_handle: *raw, + #[cfg(target_os = "macos")] + iosurface_ptr: surface_ptr, + current_key: AtomicU64::new(METAL_IOSURFACE_SEED_BASE), + acquired: AtomicBool::new(false), + completed: AtomicBool::new(false), + }; + handles.push(IoSurfaceSlotHandle { + inner: Arc::new(inner), + }); + } + self.width = width; + self.height = height; + self.format = format; + self.slot_handles = raw_handles; + self.slots_created = true; + assert_eq!( + handles.len(), + NUM_SLOTS_DEFAULT, + "returned handle vector length" + ); + assert!(self.slots_created, "slots_created flipped"); + Ok(handles) + } + + fn acquire_write(&mut self, slot: &IoSurfaceSlotHandle, key: u64) -> Result<(), BackendError> { + assert!(self.slots_created, "slots must exist before acquire"); + let current = slot.inner.current_key.load(Ordering::Acquire); + if current != key { + return Err(BackendError::KeyMismatch { + expected: key, + observed: current, + }); + } + let was_acquired = slot.inner.acquired.swap(true, Ordering::AcqRel); + if was_acquired { + return Err(BackendError::AcquireWhileWriting { + slot_index: slot.inner.slot_index, + }); + } + #[cfg(target_os = "macos")] + { + let idx = slot.inner.slot_index as usize; + if idx >= self.iosurfaces.len() { + slot.inner.acquired.store(false, Ordering::Release); + return Err(BackendError::PlatformUnsupported { + reason: "slot index out of range for IOSurface vector", + }); + } + if let Err(e) = self.iosurfaces[idx].lock_for_writing() { + slot.inner.acquired.store(false, Ordering::Release); + return Err(e); + } + } + slot.inner.completed.store(false, Ordering::Release); + assert!( + slot.inner.acquired.load(Ordering::Acquire), + "acquired flag set" + ); + assert!( + !slot.inner.completed.load(Ordering::Acquire), + "completed cleared" + ); + Ok(()) + } + + fn release_write( + &mut self, + slot: &IoSurfaceSlotHandle, + next_key: u64, + ) -> Result<(), BackendError> { + assert!(self.slots_created, "slots must exist before release"); + let was_acquired = slot.inner.acquired.swap(false, Ordering::AcqRel); + if !was_acquired { + return Err(BackendError::ReleaseWithoutAcquire { + slot_index: slot.inner.slot_index, + }); + } + #[cfg(target_os = "macos")] + { + let idx = slot.inner.slot_index as usize; + if idx >= self.iosurfaces.len() { + return Err(BackendError::PlatformUnsupported { + reason: "slot index out of range for IOSurface vector", + }); + } + self.iosurfaces[idx].unlock_after_writing()?; + } + slot.inner.current_key.store(next_key, Ordering::Release); + slot.inner.completed.store(true, Ordering::Release); + assert!( + !slot.inner.acquired.load(Ordering::Acquire), + "acquired cleared" + ); + assert!( + slot.inner.completed.load(Ordering::Acquire), + "completed flag set" + ); + Ok(()) + } + + fn poll_complete(&mut self, slot: &IoSurfaceSlotHandle) -> bool { + let done = slot.inner.completed.load(Ordering::Acquire); + assert!(self.slots_created, "slots exist for poll"); + assert!( + (slot.inner.slot_index as usize) < Self::NUM_SLOTS, + "slot index in range" + ); + done + } + + fn mark_consumed(&mut self, slot: &IoSurfaceSlotHandle) { + assert!(self.slots_created, "slots exist for mark_consumed"); + assert!( + (slot.inner.slot_index as usize) < Self::NUM_SLOTS, + "slot index in range" + ); + slot.inner.completed.store(false, Ordering::Release); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn off_macos_returns_platform_unsupported() { + let mut backend = MetalSharedTextureBackend::new(); + let result = backend.create_slots(1920, 1080, TextureFormat::Nv12); + #[cfg(not(target_os = "macos"))] + { + assert!(matches!( + result, + Err(BackendError::PlatformUnsupported { .. }) + )); + } + #[cfg(target_os = "macos")] + { + let slots = result.expect("macos create_slots ok"); + assert_eq!(slots.len(), 8); + for (idx, s) in slots.iter().enumerate() { + assert_eq!(s.slot_index(), idx as u32); + assert!(s.iosurface_handle() != 0); + } + } + } + + #[test] + fn create_slots_rejects_zero_dims() { + let mut backend = MetalSharedTextureBackend::new(); + let err = backend.create_slots(0, 1080, TextureFormat::Nv12).err(); + assert!(matches!( + err, + Some(BackendError::DimensionsOutOfRange { .. }) + )); + } + + #[test] + fn create_slots_rejects_unsupported_format() { + let mut backend = MetalSharedTextureBackend::new(); + let err = backend.create_slots(1920, 1080, TextureFormat::P010).err(); + assert!(matches!(err, Some(BackendError::UnsupportedFormat { .. }))); + } + + #[cfg(target_os = "macos")] + #[test] + fn macos_acquire_release_round_trip_marks_complete() { + let mut backend = MetalSharedTextureBackend::new(); + let slots = backend + .create_slots(64, 64, TextureFormat::Nv12) + .expect("create"); + let slot = slots[0].clone(); + backend.acquire_write(&slot, 0).expect("acquire"); + assert!(slot.is_acquired()); + backend.release_write(&slot, 1).expect("release"); + assert!(!slot.is_acquired()); + assert!(backend.poll_complete(&slot)); + backend.mark_consumed(&slot); + assert!(!backend.poll_complete(&slot)); + } + + #[cfg(target_os = "macos")] + #[test] + fn macos_double_acquire_rejects() { + let mut backend = MetalSharedTextureBackend::new(); + let slots = backend + .create_slots(64, 64, TextureFormat::Nv12) + .expect("create"); + let slot = slots[0].clone(); + backend.acquire_write(&slot, 0).expect("first acquire"); + let err = backend.acquire_write(&slot, 0).err(); + assert!(matches!( + err, + Some(BackendError::AcquireWhileWriting { .. }) + )); + } +} diff --git a/fluxer_desktop/native/encoder-ring/src/metal_iosurface_macos.rs b/fluxer_desktop/native/encoder-ring/src/metal_iosurface_macos.rs new file mode 100644 index 0000000..4340666 --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/src/metal_iosurface_macos.rs @@ -0,0 +1,187 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use core::ffi::c_void; +use core::ptr::NonNull; + +use crate::backend::BackendError; + +type IoSurfaceRef = *mut c_void; +type CfDictionaryRef = *const c_void; +type CfStringRef = *const c_void; +type CfNumberRef = *const c_void; +type CfAllocatorRef = *const c_void; + +const KIO_RETURN_SUCCESS: i32 = 0; +const KCF_NUMBER_SINT32_TYPE: i32 = 3; + +const IOSURFACE_PIXEL_FORMAT_420V: u32 = u32::from_be_bytes(*b"420v"); + +#[link(name = "IOSurface", kind = "framework")] +unsafe extern "C" { + fn IOSurfaceCreate(properties: CfDictionaryRef) -> IoSurfaceRef; + fn IOSurfaceLock(buffer: IoSurfaceRef, options: u32, seed: *mut u32) -> i32; + fn IOSurfaceUnlock(buffer: IoSurfaceRef, options: u32, seed: *mut u32) -> i32; + fn IOSurfaceGetID(buffer: IoSurfaceRef) -> u32; +} + +#[link(name = "CoreFoundation", kind = "framework")] +unsafe extern "C" { + static kCFAllocatorDefault: CfAllocatorRef; + static kCFTypeDictionaryKeyCallBacks: *const c_void; + static kCFTypeDictionaryValueCallBacks: *const c_void; + fn CFDictionaryCreate( + allocator: CfAllocatorRef, + keys: *const *const c_void, + values: *const *const c_void, + num_values: isize, + key_callbacks: *const c_void, + value_callbacks: *const c_void, + ) -> CfDictionaryRef; + fn CFNumberCreate( + allocator: CfAllocatorRef, + the_type: i32, + value_ptr: *const c_void, + ) -> CfNumberRef; + fn CFStringCreateWithCString( + allocator: CfAllocatorRef, + c_str: *const i8, + encoding: u32, + ) -> CfStringRef; + fn CFRelease(cf: *const c_void); +} + +const KCFSTRING_ENCODING_UTF8: u32 = 0x0800_0100; + +fn cf_str(literal: &'static str) -> CfStringRef { + assert!(literal.ends_with('\0'), "literal must be nul-terminated"); + let bytes = literal.as_bytes(); + unsafe { + CFStringCreateWithCString( + kCFAllocatorDefault, + bytes.as_ptr() as *const i8, + KCFSTRING_ENCODING_UTF8, + ) + } +} + +fn cf_num_i32(v: i32) -> CfNumberRef { + let ptr: *const i32 = &v; + unsafe { + CFNumberCreate( + kCFAllocatorDefault, + KCF_NUMBER_SINT32_TYPE, + ptr as *const c_void, + ) + } +} + +pub struct OwnedIoSurface { + surface: NonNull, +} + +unsafe impl Send for OwnedIoSurface {} + +impl OwnedIoSurface { + pub fn create_nv12(width: u32, height: u32) -> Result { + assert!(width > 0, "create_nv12 width positive"); + assert!( + height > 0 && height.is_multiple_of(2), + "create_nv12 height positive and even" + ); + let width_key = cf_str("IOSurfaceWidth\0"); + let height_key = cf_str("IOSurfaceHeight\0"); + let pf_key = cf_str("IOSurfacePixelFormat\0"); + let bpe_key = cf_str("IOSurfaceBytesPerElement\0"); + let width_val = cf_num_i32(width as i32); + let height_val = cf_num_i32(height as i32); + let pf_val = cf_num_i32(IOSURFACE_PIXEL_FORMAT_420V as i32); + let bpe_val = cf_num_i32(1); + let keys: [*const c_void; 4] = [width_key, height_key, pf_key, bpe_key]; + let vals: [*const c_void; 4] = [width_val, height_val, pf_val, bpe_val]; + let dict = unsafe { + CFDictionaryCreate( + kCFAllocatorDefault, + keys.as_ptr(), + vals.as_ptr(), + 4, + kCFTypeDictionaryKeyCallBacks, + kCFTypeDictionaryValueCallBacks, + ) + }; + let raw = if dict.is_null() { + core::ptr::null_mut() + } else { + unsafe { IOSurfaceCreate(dict) } + }; + unsafe { + CFRelease(width_key); + CFRelease(height_key); + CFRelease(pf_key); + CFRelease(bpe_key); + CFRelease(width_val); + CFRelease(height_val); + CFRelease(pf_val); + CFRelease(bpe_val); + if !dict.is_null() { + CFRelease(dict); + } + } + let surface = NonNull::new(raw).ok_or(BackendError::PlatformUnsupported { + reason: "IOSurfaceCreate returned null", + })?; + assert!( + unsafe { IOSurfaceGetID(surface.as_ptr()) } != 0, + "IOSurfaceGetID non-zero" + ); + Ok(Self { surface }) + } + + pub fn handle(&self) -> u64 { + let id = unsafe { IOSurfaceGetID(self.surface.as_ptr()) }; + assert!(id != 0, "IOSurfaceID non-zero on handle()"); + assert!(self.surface.as_ptr() as usize != 0, "surface ptr non-null"); + id as u64 + } + + pub fn as_ptr(&self) -> *mut c_void { + let p = self.surface.as_ptr(); + assert!(!p.is_null(), "IOSurface raw pointer non-null"); + assert!(unsafe { IOSurfaceGetID(p) } != 0, "IOSurfaceID non-zero"); + p + } + + pub fn lock_for_writing(&mut self) -> Result<(), BackendError> { + let mut seed: u32 = 0; + let status = unsafe { IOSurfaceLock(self.surface.as_ptr(), 0, &mut seed) }; + if status != KIO_RETURN_SUCCESS { + return Err(BackendError::PlatformUnsupported { + reason: "IOSurfaceLock failed", + }); + } + assert_eq!(status, KIO_RETURN_SUCCESS, "lock status ok"); + assert!(seed < u32::MAX, "lock seed within range"); + Ok(()) + } + + pub fn unlock_after_writing(&mut self) -> Result<(), BackendError> { + let mut seed: u32 = 0; + let status = unsafe { IOSurfaceUnlock(self.surface.as_ptr(), 0, &mut seed) }; + if status != KIO_RETURN_SUCCESS { + return Err(BackendError::PlatformUnsupported { + reason: "IOSurfaceUnlock failed", + }); + } + assert_eq!(status, KIO_RETURN_SUCCESS, "unlock status ok"); + assert!(seed < u32::MAX, "unlock seed within range"); + Ok(()) + } +} + +impl Drop for OwnedIoSurface { + fn drop(&mut self) { + let ptr = self.surface.as_ptr(); + if !ptr.is_null() { + unsafe { CFRelease(ptr) }; + } + } +} diff --git a/fluxer_desktop/native/encoder-ring/src/nvenc.rs b/fluxer_desktop/native/encoder-ring/src/nvenc.rs new file mode 100644 index 0000000..f32378c --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/src/nvenc.rs @@ -0,0 +1,1215 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::collections::HashMap; +use std::collections::VecDeque; +use std::ffi::c_void; +use std::ptr; +use std::sync::Arc; + +use libloading::{Library, Symbol}; +use windows::Win32::Foundation::{CloseHandle, HANDLE, WAIT_OBJECT_0}; +use windows::Win32::Graphics::Direct3D11::{ID3D11Device, ID3D11Multithread}; +use windows::Win32::System::Threading::{CreateEventW, WaitForSingleObject}; +use windows::core::Interface; + +use crate::encoder_handoff::{ + EncodedBitstream, EncoderCompletionCallback, EncoderDims, EncoderError, EncoderFrameRate, + EncoderSubmission, HandoffSlot, MAX_BITSTREAM_BYTES, NvencHandoff, PicParams, apply_dts_offset, + compute_dts_offset_us, +}; +use crate::ring::RingError; + +pub const COMPLETION_RING_CAPACITY: usize = 16; +pub const NVENC_SLOT_ID_PROBES: usize = 1024; +pub const NVENC_IN_FLIGHT_MAX: usize = 8; +const NVENC_PIPELINE_DEPTH: usize = 2; + +pub const NVENC_DLL_NAME: &str = "nvEncodeAPI64.dll"; + +const NVENCAPI_MAJOR_VERSION: u32 = 13; +const NVENCAPI_MINOR_VERSION: u32 = 0; +const NVENCAPI_VERSION: u32 = NVENCAPI_MAJOR_VERSION | (NVENCAPI_MINOR_VERSION << 24); + +const fn struct_version(ver: u32) -> u32 { + NVENCAPI_VERSION | (ver << 16) | (0x7 << 28) +} + +const NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER: u32 = struct_version(1); +const NV_ENCODE_API_FUNCTION_LIST_VER: u32 = struct_version(2); +const NV_ENC_INITIALIZE_PARAMS_VER: u32 = struct_version(7) | (1 << 31); +const NV_ENC_CONFIG_VER: u32 = struct_version(9) | (1 << 31); +const NV_ENC_PRESET_CONFIG_VER: u32 = struct_version(5) | (1 << 31); +const NV_ENC_PIC_PARAMS_VER: u32 = struct_version(7) | (1 << 31); +const NV_ENC_LOCK_BITSTREAM_VER: u32 = struct_version(2) | (1 << 31); +const NV_ENC_MAP_INPUT_RESOURCE_VER: u32 = struct_version(4); +const NV_ENC_REGISTER_RESOURCE_VER: u32 = struct_version(5); +const NV_ENC_EVENT_PARAMS_VER: u32 = struct_version(2); +const NV_ENC_CREATE_BITSTREAM_BUFFER_VER: u32 = struct_version(1); + +const NV_ENC_DEVICE_TYPE_DIRECTX: u32 = 0; +const NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX: u32 = 0; +const NV_ENC_BUFFER_FORMAT_NV12: u32 = 0x00000001; +const NV_ENC_PIC_STRUCT_FRAME: u32 = 0x01; +const NV_ENC_PIC_TYPE_IDR: u32 = 0x05; +const NV_ENC_PIC_FLAG_FORCEIDR: u32 = 0x2; +const NV_ENC_PIC_FLAG_OUTPUT_SPSPPS: u32 = 0x4; +#[allow( + dead_code, + reason = "documented rate-control mode constant per NVENC SDK reference" +)] +const NV_ENC_PARAMS_RC_CBR: u32 = 0x2; +const NV_ENC_TUNING_INFO_LOW_LATENCY: u32 = 2; +const NV_ENC_SUCCESS: i32 = 0; + +const NV_ENC_CODEC_H264_GUID: Guid = Guid { + data1: 0x6bc82762, + data2: 0x4e63, + data3: 0x4ca4, + data4: [0xaa, 0x85, 0x1e, 0x50, 0xf3, 0x21, 0xf6, 0xbf], +}; +const NV_ENC_PRESET_P3_GUID: Guid = Guid { + data1: 0x47bcf4d8, + data2: 0xb1e6, + data3: 0x47ca, + data4: [0x9d, 0x9c, 0xdf, 0x9f, 0xb6, 0xfa, 0x29, 0xed], +}; + +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct Guid { + data1: u32, + data2: u16, + data3: u16, + data4: [u8; 8], +} + +#[repr(C)] +struct OpenEncodeSessionExParams { + version: u32, + device_type: u32, + device: *mut c_void, + reserved: *mut c_void, + api_version: u32, + reserved1: [u32; 253], + reserved2: [*mut c_void; 64], +} + +#[repr(C)] +struct EventParams { + version: u32, + reserved: u32, + completion_event: *mut c_void, + reserved1: [u32; 254], + reserved2: [*mut c_void; 64], +} + +#[repr(C)] +struct RegisterResource { + version: u32, + resource_type: u32, + width: u32, + height: u32, + pitch: u32, + subresource_index: u32, + resource_to_register: *mut c_void, + registered_resource: *mut c_void, + buffer_format: u32, + buffer_usage: u32, + pinput_fence_point: *mut c_void, + chroma_offset: [u32; 2], + reserved1: [u32; 247], + reserved2: [*mut c_void; 28], +} + +#[repr(C)] +struct MapInputResource { + version: u32, + subresource_index: u32, + input_resource: *mut c_void, + registered_resource: *mut c_void, + mapped_resource: *mut c_void, + mapped_buffer_fmt: u32, + reserved1: [u32; 251], + reserved2: [*mut c_void; 63], +} + +#[repr(C)] +struct CreateBitstreamBuffer { + version: u32, + size: u32, + memory_heap: u32, + reserved: u32, + bitstream_buffer: *mut c_void, + bitstream_buffer_ptr: *mut c_void, + reserved1: [u32; 58], + reserved2: [*mut c_void; 64], +} + +#[repr(C)] +struct LockBitstream { + version: u32, + flags: u32, + output_bitstream: *mut c_void, + slice_offsets: *mut u32, + frame_idx: u32, + h_w_encode_status: u32, + num_slices: u32, + bitstream_size_in_bytes: u32, + output_time_stamp: u64, + output_duration: u64, + bitstream_buffer_ptr: *mut c_void, + picture_type: u32, + picture_struct: u32, + frame_avg_qp: u32, + frame_satd: u32, + ltr_frame_idx: u32, + ltr_frame_bitmap: u32, + temporal_id: u32, + reserved: [u32; 12], + intra_mb_count: u32, + inter_mb_count: u32, + average_mvx: i32, + average_mvy: i32, + alpha_layer_size_in_bytes: u32, + reserved1: [u32; 218], + reserved2: [*mut c_void; 64], +} + +#[repr(C)] +struct PicParamsRaw { + version: u32, + input_width: u32, + input_height: u32, + input_pitch: u32, + encode_pic_flags: u32, + frame_idx: u32, + input_timestamp: u64, + input_duration: u64, + input_buffer: *mut c_void, + output_bitstream: *mut c_void, + completion_event: *mut c_void, + buffer_fmt: u32, + picture_struct: u32, + picture_type: u32, + codec_pic_params: [u8; 256], + rc_params: [u8; 1024], + qp_delta_map: *mut i8, + qp_delta_map_size: u32, + reserved3: u32, + me_hint_counts_per_block: [u32; 2], + me_external_hints: *mut c_void, + reserved1: [u32; 6], + reserved2: [*mut c_void; 2], + me_hint_ref_pic_dpb_idx: [i8; 2], + state_buffer_idx: u8, + reserved4: [u8; 5], + new_sps_pps_buffer: *mut c_void, + reserved5: [u32; 246], + reserved6: [*mut c_void; 60], +} + +const CONFIG_BYTES: usize = 1024; +const PRESET_CONFIG_BYTES: usize = 8 + CONFIG_BYTES; + +#[repr(C)] +struct InitializeParams { + version: u32, + encode_guid: Guid, + preset_guid: Guid, + encode_width: u32, + encode_height: u32, + dar_width: u32, + dar_height: u32, + frame_rate_num: u32, + frame_rate_den: u32, + enable_encode_async: u32, + enable_ptd: u32, + bitfields: u32, + priv_data_size: u32, + reserved: u32, + priv_data: *mut c_void, + encode_config: *mut c_void, + max_encode_width: u32, + max_encode_height: u32, + max_me_hint_counts_per_block: [u8; 32], + tuning_info: u32, + buffer_format: u32, + num_state_buffers: u32, + output_stats_level: u32, + reserved1: [u32; 284], + reserved2: [*mut c_void; 64], +} + +type StatusFn0 = unsafe extern "C" fn(*mut OpenEncodeSessionExParams, *mut *mut c_void) -> i32; +type DestroyEncoderFn = unsafe extern "C" fn(*mut c_void) -> i32; +type GetPresetConfigFn = unsafe extern "C" fn(*mut c_void, Guid, Guid, u32, *mut u8) -> i32; +type InitFn = unsafe extern "C" fn(*mut c_void, *mut u8) -> i32; +type RegisterFn = unsafe extern "C" fn(*mut c_void, *mut RegisterResource) -> i32; +type UnregisterFn = unsafe extern "C" fn(*mut c_void, *mut c_void) -> i32; +type MapFn = unsafe extern "C" fn(*mut c_void, *mut MapInputResource) -> i32; +type UnmapFn = unsafe extern "C" fn(*mut c_void, *mut c_void) -> i32; +type CreateBufFn = unsafe extern "C" fn(*mut c_void, *mut CreateBitstreamBuffer) -> i32; +type DestroyBufFn = unsafe extern "C" fn(*mut c_void, *mut c_void) -> i32; +type EncodePicFn = unsafe extern "C" fn(*mut c_void, *mut PicParamsRaw) -> i32; +type LockBitstreamFn = unsafe extern "C" fn(*mut c_void, *mut LockBitstream) -> i32; +type UnlockBitstreamFn = unsafe extern "C" fn(*mut c_void, *mut c_void) -> i32; +type RegisterAsyncEventFn = unsafe extern "C" fn(*mut c_void, *mut EventParams) -> i32; +type UnregisterAsyncEventFn = unsafe extern "C" fn(*mut c_void, *mut EventParams) -> i32; + +#[repr(C)] +struct ApiFunctionList { + version: u32, + reserved: u32, + fns: [*mut c_void; 64], + reserved2: [*mut c_void; 275], +} + +const FN_IDX_INITIALIZE_ENCODER: usize = 10; +const FN_IDX_CREATE_BITSTREAM_BUFFER: usize = 13; +const FN_IDX_DESTROY_BITSTREAM_BUFFER: usize = 14; +const FN_IDX_ENCODE_PICTURE: usize = 15; +const FN_IDX_LOCK_BITSTREAM: usize = 16; +const FN_IDX_UNLOCK_BITSTREAM: usize = 17; +const FN_IDX_REGISTER_ASYNC_EVENT: usize = 22; +const FN_IDX_UNREGISTER_ASYNC_EVENT: usize = 23; +const FN_IDX_MAP_INPUT_RESOURCE: usize = 24; +const FN_IDX_UNMAP_INPUT_RESOURCE: usize = 25; +const FN_IDX_DESTROY_ENCODER: usize = 26; +const FN_IDX_OPEN_ENCODE_SESSION_EX: usize = 28; +const FN_IDX_REGISTER_RESOURCE: usize = 29; +const FN_IDX_UNREGISTER_RESOURCE: usize = 30; +const FN_IDX_GET_PRESET_CONFIG_EX: usize = 38; + +struct ApiTable { + initialize: InitFn, + create_buf: CreateBufFn, + destroy_buf: DestroyBufFn, + encode_pic: EncodePicFn, + lock_bs: LockBitstreamFn, + unlock_bs: UnlockBitstreamFn, + register_event: RegisterAsyncEventFn, + unregister_event: UnregisterAsyncEventFn, + map_input: MapFn, + unmap_input: UnmapFn, + destroy_encoder: DestroyEncoderFn, + open_session_ex: StatusFn0, + register_resource: RegisterFn, + unregister_resource: UnregisterFn, + get_preset_config: GetPresetConfigFn, +} + +struct SlotState { + registered_resource: *mut c_void, + completion_event: HANDLE, + bitstream_buffer: *mut c_void, + pending_pts_us: u64, + pending_force_keyframe: bool, + in_flight: bool, +} + +pub struct NvencD3D11Handoff { + _library: Arc, + api: ApiTable, + encoder: *mut c_void, + slots: HashMap, + handle_to_slot: HashMap, + in_flight_order: VecDeque, + next_slot_index: u32, + dts_offset_us: i64, + first_frame_pts_us: Option, + completed_count: u64, + completed_ring: VecDeque, + dropped_count: u64, + frame_interval_us: u64, +} + +unsafe impl Send for NvencD3D11Handoff {} + +impl NvencD3D11Handoff { + pub fn new( + device: ID3D11Device, + dims: EncoderDims, + bitrate_bps: u32, + ) -> Result { + Self::new_with_frame_rate(device, dims, bitrate_bps, EncoderFrameRate::default()) + } + + pub fn new_with_frame_rate( + device: ID3D11Device, + dims: EncoderDims, + bitrate_bps: u32, + frame_rate: EncoderFrameRate, + ) -> Result { + assert!(dims.width > 0, "dims width positive"); + assert!(dims.height > 0, "dims height positive"); + assert!(frame_rate.numerator > 0, "frame rate numerator positive"); + assert!( + frame_rate.denominator > 0, + "frame rate denominator positive" + ); + if dims.width > 7680 || dims.height > 4320 { + return Err(EncoderError::DimensionsOutOfRange { + width: dims.width, + height: dims.height, + }); + } + enable_multithread_protected(&device)?; + let library = load_runtime()?; + let api = load_function_list(&library)?; + let raw_device_ptr = device.as_raw(); + let encoder = open_session(&api, raw_device_ptr)?; + let frame_interval_us = frame_rate.frame_interval_us(); + let mut handoff = Self { + _library: Arc::new(library), + api, + encoder, + slots: HashMap::new(), + handle_to_slot: HashMap::new(), + in_flight_order: VecDeque::with_capacity(NVENC_IN_FLIGHT_MAX), + next_slot_index: 0, + dts_offset_us: compute_dts_offset_us(0, 0, frame_interval_us), + first_frame_pts_us: None, + completed_count: 0, + completed_ring: VecDeque::with_capacity(COMPLETION_RING_CAPACITY), + dropped_count: 0, + frame_interval_us, + }; + initialize_encoder( + &handoff.api, + handoff.encoder, + dims, + bitrate_bps, + frame_rate.numerator, + frame_rate.denominator, + )?; + let _ = &mut handoff; + assert!(!handoff.encoder.is_null(), "encoder ptr non-null"); + assert!( + handoff.completed_ring.capacity() >= COMPLETION_RING_CAPACITY, + "ring pre-allocated" + ); + Ok(handoff) + } + + pub fn pending_completion(&self) -> usize { + let n = self.completed_ring.len(); + assert!(n <= COMPLETION_RING_CAPACITY, "completion ring bounded"); + assert!( + self.completed_count <= u64::MAX / 2, + "completed count plausible" + ); + n + } + + fn allocate_slot_index(&mut self) -> Option { + assert!(self.slots.len() < usize::MAX, "slot map count plausible"); + for _ in 0..NVENC_SLOT_ID_PROBES { + let candidate = self.next_slot_index; + self.next_slot_index = candidate.wrapping_add(1); + if !self.slots.contains_key(&candidate) { + return Some(candidate); + } + } + None + } + + pub fn completed_count(&self) -> u64 { + let n = self.completed_count; + assert!( + self.completed_ring.len() <= COMPLETION_RING_CAPACITY, + "ring bounded" + ); + n + } + + fn register_slot_impl( + &mut self, + shared_handle: u64, + _key: u64, + dims: EncoderDims, + ) -> Result { + assert!(shared_handle != 0, "shared_handle non-zero"); + assert!(dims.width > 0, "dims width positive"); + let texture_ptr = shared_handle as *mut c_void; + let mut reg = RegisterResource { + version: NV_ENC_REGISTER_RESOURCE_VER, + resource_type: NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX, + width: dims.width, + height: dims.height, + pitch: 0, + subresource_index: 0, + resource_to_register: texture_ptr, + registered_resource: ptr::null_mut(), + buffer_format: NV_ENC_BUFFER_FORMAT_NV12, + buffer_usage: 0, + pinput_fence_point: ptr::null_mut(), + chroma_offset: [0; 2], + reserved1: [0; 247], + reserved2: [ptr::null_mut(); 28], + }; + let status = unsafe { (self.api.register_resource)(self.encoder, &mut reg) }; + if status != NV_ENC_SUCCESS { + return Err(EncoderError::RegisterFailed { + vendor: "nvenc", + status: status as i64, + }); + } + let event = create_completion_event()?; + register_async_event(&self.api, self.encoder, event)?; + let buffer = create_bitstream_buffer(&self.api, self.encoder)?; + let slot_index = self + .allocate_slot_index() + .ok_or(EncoderError::RegisterFailed { + vendor: "nvenc-slot-exhausted", + status: 0, + })?; + let slot = HandoffSlot::new(slot_index, shared_handle); + self.slots.insert( + slot_index, + SlotState { + registered_resource: reg.registered_resource, + completion_event: event, + bitstream_buffer: buffer, + pending_pts_us: 0, + pending_force_keyframe: false, + in_flight: false, + }, + ); + self.handle_to_slot.insert(shared_handle, slot_index); + let _ = slot; + assert!(self.slots.contains_key(&slot_index), "slot stored"); + assert!( + self.handle_to_slot.contains_key(&shared_handle), + "handle index stored" + ); + Ok(slot) + } + + fn drain_one_completed(&mut self, timeout_ms: u32) -> Option { + assert!( + self.in_flight_order.len() <= self.slots.len(), + "in-flight bounded by registered slots" + ); + let oldest = *self.in_flight_order.front()?; + let state = self.slots.get_mut(&oldest)?; + assert!(state.in_flight, "front of in-flight order is in flight"); + let wait = unsafe { WaitForSingleObject(state.completion_event, timeout_ms) }; + if wait != WAIT_OBJECT_0 { + return None; + } + let lock_result = lock_bitstream(&self.api, self.encoder, state.bitstream_buffer); + state.in_flight = false; + let popped = self.in_flight_order.pop_front(); + assert_eq!(popped, Some(oldest), "popped oldest in-flight slot"); + let (data, pts, is_keyframe) = match lock_result { + Ok(t) => t, + Err(_) => return None, + }; + if data.is_empty() { + return None; + } + let dts = apply_dts_offset(pts, self.dts_offset_us); + self.completed_count = self.completed_count.saturating_add(1); + let bs = + match std::panic::catch_unwind(|| EncodedBitstream::new(data, pts, dts, is_keyframe)) { + Ok(b) => b, + Err(_) => return None, + }; + Some(bs) + } + + fn drain_completions_into_ring(&mut self) { + while self.completed_ring.len() < COMPLETION_RING_CAPACITY { + let Some(bs) = self.drain_one_completed(0) else { + break; + }; + self.completed_ring.push_back(bs); + } + assert!( + self.completed_ring.len() <= COMPLETION_RING_CAPACITY, + "completion ring bounded" + ); + assert!( + self.in_flight_order.len() <= self.slots.len(), + "in-flight bounded by registered slots" + ); + } + + pub fn pre_register_slots( + &mut self, + handles: &[crate::d3d11::D3D11SharedHandle], + dims: EncoderDims, + ) -> Result<(), EncoderError> { + assert!( + !handles.is_empty(), + "pre-register needs at least one handle" + ); + assert!( + handles.len() <= crate::ring::RING_SIZE_MAX, + "pre-register bounded by ring max" + ); + for handle in handles { + if self.handle_to_slot.contains_key(&handle.raw_handle) { + continue; + } + let _ = self.register_slot_impl(handle.raw_handle, 0, dims)?; + } + assert!( + handles + .iter() + .all(|h| self.handle_to_slot.contains_key(&h.raw_handle)), + "all ring handles registered" + ); + Ok(()) + } +} + +fn enable_multithread_protected(device: &ID3D11Device) -> Result<(), EncoderError> { + let mt: ID3D11Multithread = device.cast().map_err(|_| EncoderError::SessionInitFailed { + vendor: "nvenc-mt", + status: -3, + })?; + let _ = unsafe { mt.SetMultithreadProtected(true) }; + Ok(()) +} + +fn load_runtime() -> Result { + let library = + unsafe { Library::new(NVENC_DLL_NAME) }.map_err(|_| EncoderError::SdkNotFound { + vendor: "nvenc", + dll: NVENC_DLL_NAME, + })?; + Ok(library) +} + +fn load_function_list(library: &Library) -> Result { + type CreateInstanceFn = unsafe extern "C" fn(*mut ApiFunctionList) -> i32; + let create_instance: Symbol<'_, CreateInstanceFn> = unsafe { + library.get(b"NvEncodeAPICreateInstance\0") + } + .map_err(|_| EncoderError::SymbolMissing { + vendor: "nvenc", + symbol: "NvEncodeAPICreateInstance", + })?; + let mut list = ApiFunctionList { + version: NV_ENCODE_API_FUNCTION_LIST_VER, + reserved: 0, + fns: [ptr::null_mut(); 64], + reserved2: [ptr::null_mut(); 275], + }; + let status = unsafe { create_instance(&mut list) }; + if status != NV_ENC_SUCCESS { + return Err(EncoderError::SessionInitFailed { + vendor: "nvenc", + status: status as i64, + }); + } + let api = build_api_table(&list)?; + Ok(api) +} + +fn build_api_table(list: &ApiFunctionList) -> Result { + let required_indices = [ + FN_IDX_INITIALIZE_ENCODER, + FN_IDX_CREATE_BITSTREAM_BUFFER, + FN_IDX_DESTROY_BITSTREAM_BUFFER, + FN_IDX_ENCODE_PICTURE, + FN_IDX_LOCK_BITSTREAM, + FN_IDX_UNLOCK_BITSTREAM, + FN_IDX_REGISTER_ASYNC_EVENT, + FN_IDX_UNREGISTER_ASYNC_EVENT, + FN_IDX_MAP_INPUT_RESOURCE, + FN_IDX_UNMAP_INPUT_RESOURCE, + FN_IDX_DESTROY_ENCODER, + FN_IDX_OPEN_ENCODE_SESSION_EX, + FN_IDX_REGISTER_RESOURCE, + FN_IDX_UNREGISTER_RESOURCE, + FN_IDX_GET_PRESET_CONFIG_EX, + ]; + for idx in required_indices.iter() { + if list.fns[*idx].is_null() { + return Err(EncoderError::SymbolMissing { + vendor: "nvenc", + symbol: "function_list entry", + }); + } + } + let api = unsafe { + ApiTable { + initialize: std::mem::transmute::<*mut c_void, InitFn>( + list.fns[FN_IDX_INITIALIZE_ENCODER], + ), + create_buf: std::mem::transmute::<*mut c_void, CreateBufFn>( + list.fns[FN_IDX_CREATE_BITSTREAM_BUFFER], + ), + destroy_buf: std::mem::transmute::<*mut c_void, DestroyBufFn>( + list.fns[FN_IDX_DESTROY_BITSTREAM_BUFFER], + ), + encode_pic: std::mem::transmute::<*mut c_void, EncodePicFn>( + list.fns[FN_IDX_ENCODE_PICTURE], + ), + lock_bs: std::mem::transmute::<*mut c_void, LockBitstreamFn>( + list.fns[FN_IDX_LOCK_BITSTREAM], + ), + unlock_bs: std::mem::transmute::<*mut c_void, UnlockBitstreamFn>( + list.fns[FN_IDX_UNLOCK_BITSTREAM], + ), + register_event: std::mem::transmute::<*mut c_void, RegisterAsyncEventFn>( + list.fns[FN_IDX_REGISTER_ASYNC_EVENT], + ), + unregister_event: std::mem::transmute::<*mut c_void, UnregisterAsyncEventFn>( + list.fns[FN_IDX_UNREGISTER_ASYNC_EVENT], + ), + map_input: std::mem::transmute::<*mut c_void, MapFn>( + list.fns[FN_IDX_MAP_INPUT_RESOURCE], + ), + unmap_input: std::mem::transmute::<*mut c_void, UnmapFn>( + list.fns[FN_IDX_UNMAP_INPUT_RESOURCE], + ), + destroy_encoder: std::mem::transmute::<*mut c_void, DestroyEncoderFn>( + list.fns[FN_IDX_DESTROY_ENCODER], + ), + open_session_ex: std::mem::transmute::<*mut c_void, StatusFn0>( + list.fns[FN_IDX_OPEN_ENCODE_SESSION_EX], + ), + register_resource: std::mem::transmute::<*mut c_void, RegisterFn>( + list.fns[FN_IDX_REGISTER_RESOURCE], + ), + unregister_resource: std::mem::transmute::<*mut c_void, UnregisterFn>( + list.fns[FN_IDX_UNREGISTER_RESOURCE], + ), + get_preset_config: std::mem::transmute::<*mut c_void, GetPresetConfigFn>( + list.fns[FN_IDX_GET_PRESET_CONFIG_EX], + ), + } + }; + Ok(api) +} + +fn open_session(api: &ApiTable, device: *mut c_void) -> Result<*mut c_void, EncoderError> { + let mut params = OpenEncodeSessionExParams { + version: NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER, + device_type: NV_ENC_DEVICE_TYPE_DIRECTX, + device, + reserved: ptr::null_mut(), + api_version: NVENCAPI_VERSION, + reserved1: [0; 253], + reserved2: [ptr::null_mut(); 64], + }; + let mut encoder: *mut c_void = ptr::null_mut(); + let status = unsafe { (api.open_session_ex)(&mut params, &mut encoder) }; + if status != NV_ENC_SUCCESS { + return Err(EncoderError::SessionInitFailed { + vendor: "nvenc-open-session", + status: status as i64, + }); + } + if encoder.is_null() { + return Err(EncoderError::SessionInitFailed { + vendor: "nvenc-open-session", + status: -1, + }); + } + Ok(encoder) +} + +fn initialize_encoder( + api: &ApiTable, + encoder: *mut c_void, + dims: EncoderDims, + _bitrate_bps: u32, + fps_num: u32, + fps_den: u32, +) -> Result<(), EncoderError> { + assert!(!encoder.is_null(), "encoder ptr non-null"); + assert!(dims.width > 0, "width positive"); + let mut preset = vec![0u8; PRESET_CONFIG_BYTES]; + write_u32(&mut preset, 0, NV_ENC_PRESET_CONFIG_VER); + write_u32(&mut preset, 8, NV_ENC_CONFIG_VER); + let status = unsafe { + (api.get_preset_config)( + encoder, + NV_ENC_CODEC_H264_GUID, + NV_ENC_PRESET_P3_GUID, + NV_ENC_TUNING_INFO_LOW_LATENCY, + preset.as_mut_ptr(), + ) + }; + if status != NV_ENC_SUCCESS { + return Err(EncoderError::SessionInitFailed { + vendor: "nvenc-preset", + status: status as i64, + }); + } + let mut init_params = InitializeParams { + version: NV_ENC_INITIALIZE_PARAMS_VER, + encode_guid: NV_ENC_CODEC_H264_GUID, + preset_guid: NV_ENC_PRESET_P3_GUID, + encode_width: dims.width, + encode_height: dims.height, + dar_width: dims.width, + dar_height: dims.height, + frame_rate_num: fps_num, + frame_rate_den: fps_den, + enable_encode_async: 1, + enable_ptd: 1, + bitfields: 0, + priv_data_size: 0, + reserved: 0, + priv_data: ptr::null_mut(), + encode_config: ptr::null_mut(), + max_encode_width: dims.width, + max_encode_height: dims.height, + max_me_hint_counts_per_block: [0; 32], + tuning_info: NV_ENC_TUNING_INFO_LOW_LATENCY, + buffer_format: NV_ENC_BUFFER_FORMAT_NV12, + num_state_buffers: 0, + output_stats_level: 0, + reserved1: [0; 284], + reserved2: [ptr::null_mut(); 64], + }; + let status = unsafe { (api.initialize)(encoder, &mut init_params as *mut _ as *mut u8) }; + if status != NV_ENC_SUCCESS { + return Err(EncoderError::SessionInitFailed { + vendor: "nvenc-init", + status: status as i64, + }); + } + Ok(()) +} + +fn write_u32(buf: &mut [u8], offset: usize, value: u32) { + assert!(offset + 4 <= buf.len(), "u32 write in bounds"); + buf[offset..offset + 4].copy_from_slice(&value.to_le_bytes()); +} + +fn create_completion_event() -> Result { + let event = unsafe { CreateEventW(None, false, false, windows::core::PCWSTR::null()) } + .map_err(|_| EncoderError::SessionInitFailed { + vendor: "nvenc", + status: -2, + })?; + assert!(!event.is_invalid(), "event handle valid"); + Ok(event) +} + +fn register_async_event( + api: &ApiTable, + encoder: *mut c_void, + event: HANDLE, +) -> Result<(), EncoderError> { + let mut params = EventParams { + version: NV_ENC_EVENT_PARAMS_VER, + reserved: 0, + completion_event: event.0, + reserved1: [0; 254], + reserved2: [ptr::null_mut(); 64], + }; + let status = unsafe { (api.register_event)(encoder, &mut params) }; + if status != NV_ENC_SUCCESS { + return Err(EncoderError::SessionInitFailed { + vendor: "nvenc", + status: status as i64, + }); + } + Ok(()) +} + +fn create_bitstream_buffer( + api: &ApiTable, + encoder: *mut c_void, +) -> Result<*mut c_void, EncoderError> { + let mut params = CreateBitstreamBuffer { + version: NV_ENC_CREATE_BITSTREAM_BUFFER_VER, + size: 0, + memory_heap: 0, + reserved: 0, + bitstream_buffer: ptr::null_mut(), + bitstream_buffer_ptr: ptr::null_mut(), + reserved1: [0; 58], + reserved2: [ptr::null_mut(); 64], + }; + let status = unsafe { (api.create_buf)(encoder, &mut params) }; + if status != NV_ENC_SUCCESS { + return Err(EncoderError::SessionInitFailed { + vendor: "nvenc", + status: status as i64, + }); + } + Ok(params.bitstream_buffer) +} + +fn map_input( + api: &ApiTable, + encoder: *mut c_void, + registered: *mut c_void, +) -> Result<*mut c_void, EncoderError> { + let mut params = MapInputResource { + version: NV_ENC_MAP_INPUT_RESOURCE_VER, + subresource_index: 0, + input_resource: ptr::null_mut(), + registered_resource: registered, + mapped_resource: ptr::null_mut(), + mapped_buffer_fmt: 0, + reserved1: [0; 251], + reserved2: [ptr::null_mut(); 63], + }; + let status = unsafe { (api.map_input)(encoder, &mut params) }; + if status != NV_ENC_SUCCESS { + return Err(EncoderError::EncodeFailed { + vendor: "nvenc", + status: status as i64, + }); + } + Ok(params.mapped_resource) +} + +struct EncodePictureArgs { + input: *mut c_void, + output: *mut c_void, + event: HANDLE, + dims: EncoderDims, + pts_us: u64, + force_keyframe: bool, +} + +fn encode_picture( + api: &ApiTable, + encoder: *mut c_void, + args: EncodePictureArgs, +) -> Result<(), EncoderError> { + assert!(args.dims.width > 0, "dims width positive"); + assert!(!args.input.is_null(), "input ptr non-null"); + let mut flags = 0_u32; + if args.force_keyframe { + flags |= NV_ENC_PIC_FLAG_FORCEIDR | NV_ENC_PIC_FLAG_OUTPUT_SPSPPS; + } + let mut params = PicParamsRaw { + version: NV_ENC_PIC_PARAMS_VER, + input_width: args.dims.width, + input_height: args.dims.height, + input_pitch: args.dims.width, + encode_pic_flags: flags, + frame_idx: 0, + input_timestamp: args.pts_us, + input_duration: 0, + input_buffer: args.input, + output_bitstream: args.output, + completion_event: args.event.0, + buffer_fmt: NV_ENC_BUFFER_FORMAT_NV12, + picture_struct: NV_ENC_PIC_STRUCT_FRAME, + picture_type: NV_ENC_PIC_TYPE_IDR, + codec_pic_params: [0; 256], + rc_params: [0; 1024], + qp_delta_map: ptr::null_mut(), + qp_delta_map_size: 0, + reserved3: 0, + me_hint_counts_per_block: [0; 2], + me_external_hints: ptr::null_mut(), + reserved1: [0; 6], + reserved2: [ptr::null_mut(); 2], + me_hint_ref_pic_dpb_idx: [-1; 2], + state_buffer_idx: 0, + reserved4: [0; 5], + new_sps_pps_buffer: ptr::null_mut(), + reserved5: [0; 246], + reserved6: [ptr::null_mut(); 60], + }; + let status = unsafe { (api.encode_pic)(encoder, &mut params) }; + if status != NV_ENC_SUCCESS && status != 1 { + return Err(EncoderError::EncodeFailed { + vendor: "nvenc", + status: status as i64, + }); + } + Ok(()) +} + +fn lock_bitstream( + api: &ApiTable, + encoder: *mut c_void, + output: *mut c_void, +) -> Result<(Vec, u64, bool), EncoderError> { + let mut params = LockBitstream { + version: NV_ENC_LOCK_BITSTREAM_VER, + flags: 1, + output_bitstream: output, + slice_offsets: ptr::null_mut(), + frame_idx: 0, + h_w_encode_status: 0, + num_slices: 0, + bitstream_size_in_bytes: 0, + output_time_stamp: 0, + output_duration: 0, + bitstream_buffer_ptr: ptr::null_mut(), + picture_type: 0, + picture_struct: 0, + frame_avg_qp: 0, + frame_satd: 0, + ltr_frame_idx: 0, + ltr_frame_bitmap: 0, + temporal_id: 0, + reserved: [0; 12], + intra_mb_count: 0, + inter_mb_count: 0, + average_mvx: 0, + average_mvy: 0, + alpha_layer_size_in_bytes: 0, + reserved1: [0; 218], + reserved2: [ptr::null_mut(); 64], + }; + let status = unsafe { (api.lock_bs)(encoder, &mut params) }; + if status != NV_ENC_SUCCESS { + return Err(EncoderError::BitstreamReadFailed { + vendor: "nvenc", + status: status as i64, + }); + } + let len = params.bitstream_size_in_bytes as usize; + assert!(len <= 16 * 1024 * 1024, "bitstream within sanity cap"); + let mut data: Vec = Vec::with_capacity(len); + if len > 0 && !params.bitstream_buffer_ptr.is_null() { + let src = + unsafe { std::slice::from_raw_parts(params.bitstream_buffer_ptr as *const u8, len) }; + data.extend_from_slice(src); + } + let pts = params.output_time_stamp; + let is_idr = params.picture_type == NV_ENC_PIC_TYPE_IDR || (params.picture_type & 0x0F) == 0; + let _ = unsafe { (api.unlock_bs)(encoder, output) }; + Ok((data, pts, is_idr)) +} + +impl Drop for NvencD3D11Handoff { + fn drop(&mut self) { + let keys: Vec = self.slots.keys().copied().collect(); + for key in keys { + if let Some(state) = self.slots.remove(&key) { + self.cleanup_slot(state); + } + } + self.handle_to_slot.clear(); + self.in_flight_order.clear(); + self.completed_ring.clear(); + if !self.encoder.is_null() { + let _ = unsafe { (self.api.destroy_encoder)(self.encoder) }; + self.encoder = ptr::null_mut(); + } + } +} + +impl NvencD3D11Handoff { + fn cleanup_slot(&self, state: SlotState) { + if !state.bitstream_buffer.is_null() { + let _ = unsafe { (self.api.destroy_buf)(self.encoder, state.bitstream_buffer) }; + } + if !state.registered_resource.is_null() { + let _ = + unsafe { (self.api.unregister_resource)(self.encoder, state.registered_resource) }; + } + if !state.completion_event.is_invalid() { + let mut params = EventParams { + version: NV_ENC_EVENT_PARAMS_VER, + reserved: 0, + completion_event: state.completion_event.0, + reserved1: [0; 254], + reserved2: [ptr::null_mut(); 64], + }; + let _ = unsafe { (self.api.unregister_event)(self.encoder, &mut params) }; + let _ = unsafe { CloseHandle(state.completion_event) }; + } + } +} + +impl NvencHandoff for NvencD3D11Handoff { + fn register_slot( + &mut self, + shared_handle: u64, + key: u64, + dims: EncoderDims, + ) -> Result { + self.register_slot_impl(shared_handle, key, dims) + } + + fn encode_shared_async( + &mut self, + slot: HandoffSlot, + _key: u64, + dims: EncoderDims, + pic_params: PicParams, + ) -> Result<(), EncoderError> { + assert!(dims.width > 0, "dims width positive"); + assert!(slot.shared_handle != 0, "slot handle non-zero"); + let state = self + .slots + .get_mut(&slot.slot_index) + .ok_or(EncoderError::SlotUnknown { + slot_index: slot.slot_index, + })?; + if state.in_flight { + return Err(EncoderError::EncodeFailed { + vendor: "nvenc", + status: -10, + }); + } + let mapped = map_input(&self.api, self.encoder, state.registered_resource)?; + if self.first_frame_pts_us.is_none() { + self.first_frame_pts_us = Some(pic_params.pts_us); + } + let result = encode_picture( + &self.api, + self.encoder, + EncodePictureArgs { + input: mapped, + output: state.bitstream_buffer, + event: state.completion_event, + dims, + pts_us: pic_params.pts_us, + force_keyframe: pic_params.force_keyframe || self.completed_count == 0, + }, + ); + let _ = unsafe { (self.api.unmap_input)(self.encoder, mapped) }; + result?; + state.pending_pts_us = pic_params.pts_us; + state.pending_force_keyframe = pic_params.force_keyframe; + state.in_flight = true; + self.in_flight_order.push_back(slot.slot_index); + assert!( + self.in_flight_order.len() <= self.slots.len(), + "in-flight bounded by registered slots" + ); + assert_eq!( + self.in_flight_order.back(), + Some(&slot.slot_index), + "newest submission at back of in-flight order" + ); + Ok(()) + } + + fn poll_completed(&mut self, slot: HandoffSlot) -> Option { + let _ = slot; + if let Some(bs) = self.completed_ring.pop_front() { + assert!(!bs.data.is_empty(), "ring payload non-empty"); + assert!(bs.data.len() <= MAX_BITSTREAM_BYTES, "ring payload bounded"); + return Some(bs); + } + let drained = self.drain_one_completed(0); + if let Some(bs) = drained { + assert!(!bs.data.is_empty(), "drained payload non-empty"); + return Some(bs); + } + None + } + + fn unregister_slot(&mut self, slot: HandoffSlot) { + if let Some(state) = self.slots.remove(&slot.slot_index) { + self.handle_to_slot.remove(&slot.shared_handle); + self.in_flight_order.retain(|&idx| idx != slot.slot_index); + self.cleanup_slot(state); + } + } + + fn encode_shared( + &mut self, + submission: EncoderSubmission, + callback: &mut dyn EncoderCompletionCallback, + ) -> Result<(), RingError> { + assert!(submission.shared_handle != 0, "submission handle non-zero"); + assert!(submission.dims.width > 0, "submission width positive"); + self.drain_completions_into_ring(); + if self.completed_ring.len() >= COMPLETION_RING_CAPACITY { + self.dropped_count = self.dropped_count.saturating_add(1); + return Err(RingError::FullDropped { + dropped_so_far: self.dropped_count, + }); + } + let slot = self.ensure_slot_for_handle(submission.shared_handle, submission.dims)?; + let pic = PicParams::new( + submission.sequence.saturating_mul(self.frame_interval_us), + false, + ); + let async_result = NvencHandoff::encode_shared_async( + self, + slot, + submission.keyed_mutex_key, + submission.dims, + pic, + ); + if let Err(e) = async_result { + return Err(RingError::BackendFailed { + source: crate::backend::BackendError::PlatformUnsupported { + reason: match e { + EncoderError::SlotUnknown { .. } => "nvenc slot unknown", + EncoderError::EncodeFailed { .. } => "nvenc encode failed", + _ => "nvenc session error", + }, + }, + }); + } + self.drain_completions_into_ring(); + if self.in_flight_order.len() >= NVENC_PIPELINE_DEPTH + && self.completed_ring.len() < COMPLETION_RING_CAPACITY + { + let timeout_ms = (self.frame_interval_us.saturating_add(999) / 1000) as u32; + assert!(timeout_ms <= 1000, "blocking drain capped at one second"); + if let Some(bs) = self.drain_one_completed(timeout_ms) { + self.completed_ring.push_back(bs); + } + } + let encoded_bytes_estimate = self.pending_completion() as u32; + callback.on_complete(submission.sequence, encoded_bytes_estimate); + Ok(()) + } +} + +impl NvencD3D11Handoff { + fn ensure_slot_for_handle( + &mut self, + shared_handle: u64, + dims: EncoderDims, + ) -> Result { + if let Some(&idx) = self.handle_to_slot.get(&shared_handle) { + return Ok(HandoffSlot::new(idx, shared_handle)); + } + self.register_slot_impl(shared_handle, 0, dims) + .map_err(|e| RingError::BackendFailed { + source: crate::backend::BackendError::PlatformUnsupported { + reason: match e { + EncoderError::RegisterFailed { .. } => "nvenc register failed", + EncoderError::SessionInitFailed { .. } => "nvenc session init", + EncoderError::DimensionsOutOfRange { .. } => "nvenc dims out of range", + _ => "nvenc register slot", + }, + }, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sdk_not_found_when_dll_missing() { + let dummy_path = "/this/path/does/not/exist/fake-nvencodeAPI64.dll"; + let result = unsafe { Library::new(dummy_path) }; + assert!(result.is_err()); + } + + #[test] + fn struct_version_layout_is_stable() { + assert_eq!(NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER, struct_version(1)); + assert_eq!(NV_ENCODE_API_FUNCTION_LIST_VER, struct_version(2)); + } + + #[test] + fn guid_h264_layout_matches_nvenc_spec() { + assert_eq!(NV_ENC_CODEC_H264_GUID.data1, 0x6bc82762); + assert_eq!(NV_ENC_CODEC_H264_GUID.data4[7], 0xbf); + } + + #[test] + fn nvencapi_version_packs_major_and_minor() { + let expected = 13_u32; + assert_eq!(NVENCAPI_VERSION, expected); + } +} diff --git a/fluxer_desktop/native/encoder-ring/src/qsv.rs b/fluxer_desktop/native/encoder-ring/src/qsv.rs new file mode 100644 index 0000000..7c331dc --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/src/qsv.rs @@ -0,0 +1,964 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::collections::HashMap; +use std::ffi::c_void; +use std::ptr; +use std::sync::Arc; + +use libloading::{Library, Symbol}; +use windows::Win32::Graphics::Direct3D11::{ID3D11Device, ID3D11Multithread}; +use windows::core::Interface; + +use crate::encoder_handoff::{ + EncodedBitstream, EncoderCompletionCallback, EncoderDims, EncoderError, EncoderFrameRate, + EncoderSubmission, HandoffSlot, PicParams, QsvHandoff, apply_dts_offset, compute_dts_offset_us, +}; +use crate::ring::RingError; + +pub const QSV_DLL_NAME_VPL: &str = "libvpl.dll"; +pub const QSV_DLL_NAME_MFX: &str = "libmfxhw64.dll"; + +const MFX_IMPL_HARDWARE: i32 = 0x0002; +const MFX_IMPL_VIA_D3D11: i32 = 0x0300; +const MFX_IMPL_TYPE_HARDWARE: u32 = 2; +const MFX_ACCEL_MODE_VIA_D3D11: u32 = 0x0300; +const MFX_HANDLE_D3D11_DEVICE: u32 = 3; +const MFX_FOURCC_NV12: u32 = u32::from_le_bytes(*b"NV12"); +const MFX_CODEC_AVC: u32 = u32::from_le_bytes(*b"AVC "); +const MFX_RATECONTROL_CBR: u16 = 1; +const MFX_PICSTRUCT_PROGRESSIVE: u16 = 0x01; +const MFX_CHROMAFORMAT_YUV420: u16 = 1; +const MFX_IOPATTERN_IN_VIDEO_MEMORY: u16 = 0x01; + +const MFX_VARIANT_TYPE_U32: u32 = 5; +const MFX_VARIANT_VERSION_MINOR: u8 = 1; +const MFX_VARIANT_VERSION_MAJOR: u8 = 1; + +const MFX_ERR_NONE: i32 = 0; +const MFX_WRN_IN_EXECUTION: i32 = 1; +const MFX_ERR_MORE_DATA: i32 = -10; + +const FILTER_PROPERTY_IMPL: &[u8] = b"mfxImplDescription.Impl\0"; +const FILTER_PROPERTY_ACCEL: &[u8] = b"mfxImplDescription.AccelerationMode\0"; + +#[repr(C)] +#[derive(Clone, Copy, Default)] +struct MfxVersion { + minor: u16, + major: u16, +} + +#[repr(C)] +#[derive(Default)] +struct MfxFrameInfo { + reserved: [u32; 4], + channel_id: u16, + bit_depth_luma: u16, + bit_depth_chroma: u16, + shift: u16, + frame_id_temporal: u16, + frame_id_priority: u16, + frame_id_view_or_dependency: u16, + frame_id_quality: u16, + four_cc: u32, + width: u16, + height: u16, + crop_x: u16, + crop_y: u16, + crop_w: u16, + crop_h: u16, + frame_rate_extn: u32, + frame_rate_extd: u32, + reserved3: u16, + aspect_ratio_w: u16, + aspect_ratio_h: u16, + pic_struct: u16, + chroma_format: u16, + reserved2: u16, +} + +#[repr(C)] +#[derive(Default)] +struct MfxInfoMfx { + reserved: [u32; 7], + low_power: u16, + brc_param_multiplier: u16, + frame_info: MfxFrameInfo, + codec_id: u32, + codec_profile: u16, + codec_level: u16, + num_thread: u16, + target_usage: u16, + gop_pic_size: u16, + gop_ref_dist: u16, + gop_opt_flag: u16, + idr_interval: u16, + rate_control_method: u16, + init_qp: u16, + buffer_size_in_kb: u16, + target_kbps: u16, + max_kbps: u16, + num_slice: u16, + num_ref_frame: u16, + encoded_order: u16, + union_pad: [u16; 15], +} + +#[repr(C)] +struct MfxVideoParam { + alloc_id: u32, + reserved: [u32; 2], + reserved3: u16, + async_depth: u16, + mfx: MfxInfoMfx, + protected: u16, + io_pattern: u16, + ext_param: *mut c_void, + num_ext_param: u16, + reserved2: u16, +} + +#[repr(C)] +struct MfxBitstream { + encrypted_data: *mut c_void, + num_extparam: u16, + ext_param: *mut c_void, + reserved: [u32; 6], + decode_time_stamp: u64, + time_stamp: u64, + data: *mut u8, + data_offset: u32, + data_length: u32, + max_length: u32, + pic_struct: u16, + frame_type: u16, + data_flag: u16, + reserved2: u16, +} + +#[repr(C)] +struct MfxFrameData { + ext_param: *mut c_void, + num_extparam: u16, + reserved: [u32; 8], + mem_type: u16, + pitch_high: u16, + time_stamp: u64, + frame_order: u32, + locked: u16, + pitch_low: u16, + plane_ptrs: [*mut u8; 7], + mem_id: *mut c_void, + corrupted: u16, + data_flag: u16, +} + +#[repr(C)] +struct MfxFrameSurface1 { + reserved: [u32; 4], + interface_ptr: *mut c_void, + info: MfxFrameInfo, + data: MfxFrameData, +} + +#[repr(C)] +#[derive(Clone, Copy)] +union MfxVariantData { + u32_: u32, + u64_: u64, + ptr: *mut c_void, + pad: [u8; 16], +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct MfxVariant { + version: MfxStructVersion, + type_: u32, + data: MfxVariantData, +} + +#[repr(C)] +#[derive(Clone, Copy, Default)] +struct MfxStructVersion { + minor: u8, + major: u8, +} + +type MfxInit = unsafe extern "C" fn(i32, *mut MfxVersion, *mut *mut c_void) -> i32; +type MfxClose = unsafe extern "C" fn(*mut c_void) -> i32; +type MfxSetHandle = unsafe extern "C" fn(*mut c_void, u32, *mut c_void) -> i32; +type MfxEncodeInit = unsafe extern "C" fn(*mut c_void, *mut MfxVideoParam) -> i32; +type MfxEncodeClose = unsafe extern "C" fn(*mut c_void) -> i32; +type MfxEncodeQuery = + unsafe extern "C" fn(*mut c_void, *mut MfxVideoParam, *mut MfxVideoParam) -> i32; +type MfxEncodeFrameAsync = unsafe extern "C" fn( + *mut c_void, + *mut c_void, + *mut MfxFrameSurface1, + *mut MfxBitstream, + *mut *mut c_void, +) -> i32; +type MfxSyncOperation = unsafe extern "C" fn(*mut c_void, *mut c_void, u32) -> i32; + +type MfxLoad = unsafe extern "C" fn() -> *mut c_void; +type MfxUnload = unsafe extern "C" fn(*mut c_void); +type MfxCreateConfig = unsafe extern "C" fn(*mut c_void) -> *mut c_void; +type MfxSetConfigFilterProperty = unsafe extern "C" fn(*mut c_void, *const u8, MfxVariant) -> i32; +type MfxCreateSession = unsafe extern "C" fn(*mut c_void, u32, *mut *mut c_void) -> i32; + +struct ApiTable { + init: MfxInit, + close: MfxClose, + set_handle: MfxSetHandle, + encode_init: MfxEncodeInit, + encode_close: MfxEncodeClose, + encode_query: MfxEncodeQuery, + encode_frame_async: MfxEncodeFrameAsync, + sync_operation: MfxSyncOperation, +} + +struct DispatcherTable { + load: MfxLoad, + unload: MfxUnload, + create_config: MfxCreateConfig, + set_config_property: MfxSetConfigFilterProperty, + create_session: MfxCreateSession, +} + +struct SlotState { + pending_pts_us: u64, + pending_force_keyframe: bool, + sync_point: *mut c_void, + bitstream: MfxBitstream, + bitstream_buf: Vec, + surface: MfxFrameSurface1, + in_flight: bool, +} + +pub struct QsvD3D11Handoff { + _library: Arc, + api: ApiTable, + dispatcher: Option, + loader: *mut c_void, + session: *mut c_void, + slots: HashMap, + next_slot_index: u32, + dts_offset_us: i64, + completed_count: u64, + target_kbps: u16, + frame_rate: EncoderFrameRate, +} + +unsafe impl Send for QsvD3D11Handoff {} + +impl QsvD3D11Handoff { + pub fn new( + device: ID3D11Device, + dims: EncoderDims, + bitrate_bps: u32, + ) -> Result { + Self::new_with_frame_rate(device, dims, bitrate_bps, EncoderFrameRate::default()) + } + + pub fn new_with_frame_rate( + device: ID3D11Device, + dims: EncoderDims, + bitrate_bps: u32, + frame_rate: EncoderFrameRate, + ) -> Result { + assert!(dims.width > 0, "dims width positive"); + assert!(dims.height > 0, "dims height positive"); + assert!(frame_rate.numerator > 0, "frame rate numerator positive"); + assert!( + frame_rate.denominator > 0, + "frame rate denominator positive" + ); + if dims.width > 7680 || dims.height > 4320 { + return Err(EncoderError::DimensionsOutOfRange { + width: dims.width, + height: dims.height, + }); + } + let library = load_runtime()?; + let api = load_api(&library)?; + let dispatcher_opt = load_dispatcher(&library); + let (loader, session) = open_session(&api, dispatcher_opt.as_ref())?; + set_d3d11_handle(&api, session, &device)?; + let target_kbps = (bitrate_bps / 1000).clamp(500, 60_000) as u16; + encode_init(&api, session, dims, target_kbps, frame_rate)?; + let dts_offset_us = compute_dts_offset_us(0, 0, frame_rate.frame_interval_us()); + let handoff = Self { + _library: Arc::new(library), + api, + dispatcher: dispatcher_opt, + loader, + session, + slots: HashMap::new(), + next_slot_index: 0, + dts_offset_us, + completed_count: 0, + target_kbps, + frame_rate, + }; + assert!(!handoff.session.is_null(), "session non-null"); + Ok(handoff) + } +} + +fn load_runtime() -> Result { + let vpl_result = unsafe { Library::new(QSV_DLL_NAME_VPL) }; + match vpl_result { + Ok(lib) => Ok(lib), + Err(_) => { + unsafe { Library::new(QSV_DLL_NAME_MFX) }.map_err(|_| EncoderError::SdkNotFound { + vendor: "qsv", + dll: QSV_DLL_NAME_MFX, + }) + } + } +} + +fn load_api(library: &Library) -> Result { + let init: Symbol<'_, MfxInit> = + unsafe { library.get(b"MFXInit\0") }.map_err(|_| EncoderError::SymbolMissing { + vendor: "qsv", + symbol: "MFXInit", + })?; + let close: Symbol<'_, MfxClose> = + unsafe { library.get(b"MFXClose\0") }.map_err(|_| EncoderError::SymbolMissing { + vendor: "qsv", + symbol: "MFXClose", + })?; + let set_handle: Symbol<'_, MfxSetHandle> = unsafe { library.get(b"MFXVideoCORE_SetHandle\0") } + .map_err(|_| EncoderError::SymbolMissing { + vendor: "qsv", + symbol: "MFXVideoCORE_SetHandle", + })?; + let encode_init: Symbol<'_, MfxEncodeInit> = unsafe { library.get(b"MFXVideoENCODE_Init\0") } + .map_err(|_| EncoderError::SymbolMissing { + vendor: "qsv", + symbol: "MFXVideoENCODE_Init", + })?; + let encode_close: Symbol<'_, MfxEncodeClose> = unsafe { + library.get(b"MFXVideoENCODE_Close\0") + } + .map_err(|_| EncoderError::SymbolMissing { + vendor: "qsv", + symbol: "MFXVideoENCODE_Close", + })?; + let encode_query: Symbol<'_, MfxEncodeQuery> = unsafe { + library.get(b"MFXVideoENCODE_Query\0") + } + .map_err(|_| EncoderError::SymbolMissing { + vendor: "qsv", + symbol: "MFXVideoENCODE_Query", + })?; + let encode_frame_async: Symbol<'_, MfxEncodeFrameAsync> = unsafe { + library.get(b"MFXVideoENCODE_EncodeFrameAsync\0") + } + .map_err(|_| EncoderError::SymbolMissing { + vendor: "qsv", + symbol: "MFXVideoENCODE_EncodeFrameAsync", + })?; + let sync_operation: Symbol<'_, MfxSyncOperation> = unsafe { + library.get(b"MFXVideoCORE_SyncOperation\0") + } + .map_err(|_| EncoderError::SymbolMissing { + vendor: "qsv", + symbol: "MFXVideoCORE_SyncOperation", + })?; + Ok(ApiTable { + init: *init, + close: *close, + set_handle: *set_handle, + encode_init: *encode_init, + encode_close: *encode_close, + encode_query: *encode_query, + encode_frame_async: *encode_frame_async, + sync_operation: *sync_operation, + }) +} + +fn load_dispatcher(library: &Library) -> Option { + let load: Symbol<'_, MfxLoad> = unsafe { library.get(b"MFXLoad\0") }.ok()?; + let unload: Symbol<'_, MfxUnload> = unsafe { library.get(b"MFXUnload\0") }.ok()?; + let create_config: Symbol<'_, MfxCreateConfig> = + unsafe { library.get(b"MFXCreateConfig\0") }.ok()?; + let set_config_property: Symbol<'_, MfxSetConfigFilterProperty> = + unsafe { library.get(b"MFXSetConfigFilterProperty\0") }.ok()?; + let create_session: Symbol<'_, MfxCreateSession> = + unsafe { library.get(b"MFXCreateSession\0") }.ok()?; + Some(DispatcherTable { + load: *load, + unload: *unload, + create_config: *create_config, + set_config_property: *set_config_property, + create_session: *create_session, + }) +} + +fn open_session( + api: &ApiTable, + dispatcher: Option<&DispatcherTable>, +) -> Result<(*mut c_void, *mut c_void), EncoderError> { + if let Some(d) = dispatcher { + match modern_session(d) { + Ok((loader, session)) => return Ok((loader, session)), + Err(e) => { + let session = init_session_legacy(api)?; + let _ = e; + return Ok((ptr::null_mut(), session)); + } + } + } + let session = init_session_legacy(api)?; + Ok((ptr::null_mut(), session)) +} + +fn modern_session(d: &DispatcherTable) -> Result<(*mut c_void, *mut c_void), EncoderError> { + let loader = unsafe { (d.load)() }; + if loader.is_null() { + return Err(EncoderError::SessionInitFailed { + vendor: "qsv-mfxload", + status: -1, + }); + } + assert!(!loader.is_null(), "MFXLoad returned non-null"); + if let Err(e) = set_filter_u32(d, loader, FILTER_PROPERTY_IMPL, MFX_IMPL_TYPE_HARDWARE) { + unsafe { (d.unload)(loader) }; + return Err(e); + } + if let Err(e) = set_filter_u32(d, loader, FILTER_PROPERTY_ACCEL, MFX_ACCEL_MODE_VIA_D3D11) { + unsafe { (d.unload)(loader) }; + return Err(e); + } + let mut session: *mut c_void = ptr::null_mut(); + let status = unsafe { (d.create_session)(loader, 0, &mut session) }; + if status != MFX_ERR_NONE || session.is_null() { + unsafe { (d.unload)(loader) }; + return Err(EncoderError::SessionInitFailed { + vendor: "qsv-create-session", + status: status as i64, + }); + } + assert!(!session.is_null(), "modern session non-null"); + Ok((loader, session)) +} + +fn set_filter_u32( + d: &DispatcherTable, + loader: *mut c_void, + property: &'static [u8], + value_u32: u32, +) -> Result<(), EncoderError> { + assert!(!loader.is_null(), "loader non-null"); + assert!(!property.is_empty(), "property non-empty"); + let cfg = unsafe { (d.create_config)(loader) }; + if cfg.is_null() { + return Err(EncoderError::SessionInitFailed { + vendor: "qsv-create-config", + status: -1, + }); + } + let variant = MfxVariant { + version: MfxStructVersion { + minor: MFX_VARIANT_VERSION_MINOR, + major: MFX_VARIANT_VERSION_MAJOR, + }, + type_: MFX_VARIANT_TYPE_U32, + data: MfxVariantData { u32_: value_u32 }, + }; + let status = unsafe { (d.set_config_property)(cfg, property.as_ptr(), variant) }; + if status != MFX_ERR_NONE { + let vendor = if property == FILTER_PROPERTY_ACCEL { + "qsv-accel-mode-rejected" + } else { + "qsv-set-config-property" + }; + return Err(EncoderError::SessionInitFailed { + vendor, + status: status as i64, + }); + } + Ok(()) +} + +fn init_session_legacy(api: &ApiTable) -> Result<*mut c_void, EncoderError> { + let session_via_d3d11 = try_mfxinit(api, MFX_IMPL_HARDWARE | MFX_IMPL_VIA_D3D11); + if let Ok(s) = session_via_d3d11 { + return Ok(s); + } + try_mfxinit(api, MFX_IMPL_HARDWARE) +} + +fn try_mfxinit(api: &ApiTable, impl_flags: i32) -> Result<*mut c_void, EncoderError> { + let mut version = MfxVersion { major: 1, minor: 0 }; + let mut session: *mut c_void = ptr::null_mut(); + let status = unsafe { (api.init)(impl_flags, &mut version, &mut session) }; + if status != MFX_ERR_NONE { + return Err(EncoderError::SessionInitFailed { + vendor: "qsv-mfxinit", + status: status as i64, + }); + } + if session.is_null() { + return Err(EncoderError::SessionInitFailed { + vendor: "qsv-mfxinit", + status: -1, + }); + } + assert!(!session.is_null(), "session non-null after MFXInit"); + Ok(session) +} + +fn set_d3d11_handle( + api: &ApiTable, + session: *mut c_void, + device: &ID3D11Device, +) -> Result<(), EncoderError> { + assert!(!session.is_null(), "session non-null"); + let raw = device.as_raw(); + assert!(!raw.is_null(), "device raw non-null"); + if let Ok(mt) = device.cast::() { + let _ = unsafe { mt.SetMultithreadProtected(true) }; + } + let status = unsafe { (api.set_handle)(session, MFX_HANDLE_D3D11_DEVICE, raw) }; + if status != MFX_ERR_NONE && status != MFX_WRN_IN_EXECUTION { + return Err(EncoderError::SessionInitFailed { + vendor: "qsv-set-handle", + status: status as i64, + }); + } + Ok(()) +} + +fn build_video_params( + dims: EncoderDims, + target_kbps: u16, + frame_rate: EncoderFrameRate, +) -> MfxVideoParam { + let info = MfxFrameInfo { + four_cc: MFX_FOURCC_NV12, + width: align16(dims.width as u16), + height: align16(dims.height as u16), + crop_w: dims.width as u16, + crop_h: dims.height as u16, + frame_rate_extn: frame_rate.numerator, + frame_rate_extd: frame_rate.denominator, + aspect_ratio_w: 1, + aspect_ratio_h: 1, + pic_struct: MFX_PICSTRUCT_PROGRESSIVE, + chroma_format: MFX_CHROMAFORMAT_YUV420, + ..Default::default() + }; + let mfx = MfxInfoMfx { + frame_info: info, + codec_id: MFX_CODEC_AVC, + target_usage: 4, + gop_pic_size: frame_rate.gop_pic_size(), + gop_ref_dist: 1, + rate_control_method: MFX_RATECONTROL_CBR, + target_kbps, + max_kbps: target_kbps, + num_slice: 1, + num_ref_frame: 1, + ..Default::default() + }; + MfxVideoParam { + alloc_id: 0, + reserved: [0; 2], + reserved3: 0, + async_depth: 1, + mfx, + protected: 0, + io_pattern: MFX_IOPATTERN_IN_VIDEO_MEMORY, + ext_param: ptr::null_mut(), + num_ext_param: 0, + reserved2: 0, + } +} + +fn align16(v: u16) -> u16 { + (v + 15) & !15 +} + +fn encode_init( + api: &ApiTable, + session: *mut c_void, + dims: EncoderDims, + target_kbps: u16, + frame_rate: EncoderFrameRate, +) -> Result<(), EncoderError> { + let mut params = build_video_params(dims, target_kbps, frame_rate); + let mut query_out = build_video_params(dims, target_kbps, frame_rate); + let q_status = unsafe { (api.encode_query)(session, &mut params, &mut query_out) }; + let q_ok = q_status == MFX_ERR_NONE + || q_status == MFX_WRN_IN_EXECUTION + || q_status == -3 + || q_status > 0; + if !q_ok { + return Err(EncoderError::SessionInitFailed { + vendor: "qsv-encode-query", + status: q_status as i64, + }); + } + let status = unsafe { (api.encode_init)(session, &mut query_out) }; + if status != MFX_ERR_NONE && status != MFX_WRN_IN_EXECUTION { + return Err(EncoderError::SessionInitFailed { + vendor: "qsv-encode-init", + status: status as i64, + }); + } + Ok(()) +} + +fn build_slot_state( + shared_handle: u64, + dims: EncoderDims, + target_kbps: u16, + frame_rate: EncoderFrameRate, +) -> SlotState { + let max_bs = (dims.width as usize * dims.height as usize * 3 / 2).max(512 * 1024); + let mut bitstream_buf = vec![0u8; max_bs]; + let bs_ptr = bitstream_buf.as_mut_ptr(); + let bitstream = MfxBitstream { + encrypted_data: ptr::null_mut(), + num_extparam: 0, + ext_param: ptr::null_mut(), + reserved: [0; 6], + decode_time_stamp: 0, + time_stamp: 0, + data: bs_ptr, + data_offset: 0, + data_length: 0, + max_length: max_bs as u32, + pic_struct: 0, + frame_type: 0, + data_flag: 0, + reserved2: 0, + }; + let info = MfxFrameInfo { + four_cc: MFX_FOURCC_NV12, + width: align16(dims.width as u16), + height: align16(dims.height as u16), + crop_w: dims.width as u16, + crop_h: dims.height as u16, + frame_rate_extn: frame_rate.numerator, + frame_rate_extd: frame_rate.denominator, + aspect_ratio_w: 1, + aspect_ratio_h: 1, + pic_struct: MFX_PICSTRUCT_PROGRESSIVE, + chroma_format: MFX_CHROMAFORMAT_YUV420, + ..Default::default() + }; + let _ = target_kbps; + let surface = MfxFrameSurface1 { + reserved: [0; 4], + interface_ptr: ptr::null_mut(), + info, + data: MfxFrameData { + ext_param: ptr::null_mut(), + num_extparam: 0, + reserved: [0; 8], + mem_type: 0, + pitch_high: 0, + time_stamp: 0, + frame_order: 0, + locked: 0, + pitch_low: 0, + plane_ptrs: [ptr::null_mut(); 7], + mem_id: shared_handle as *mut c_void, + corrupted: 0, + data_flag: 0, + }, + }; + SlotState { + pending_pts_us: 0, + pending_force_keyframe: false, + sync_point: ptr::null_mut(), + bitstream, + bitstream_buf, + surface, + in_flight: false, + } +} + +impl Drop for QsvD3D11Handoff { + fn drop(&mut self) { + self.slots.clear(); + if !self.session.is_null() { + let _ = unsafe { (self.api.encode_close)(self.session) }; + let _ = unsafe { (self.api.close)(self.session) }; + self.session = ptr::null_mut(); + } + if let Some(d) = self.dispatcher.take() + && !self.loader.is_null() + { + unsafe { (d.unload)(self.loader) }; + self.loader = ptr::null_mut(); + } + } +} + +impl QsvHandoff for QsvD3D11Handoff { + fn register_slot( + &mut self, + shared_handle: u64, + _key: u64, + dims: EncoderDims, + ) -> Result { + assert!(shared_handle != 0, "shared_handle non-zero"); + assert!(dims.width > 0, "width positive"); + let slot_index = self.next_slot_index; + self.next_slot_index = self.next_slot_index.saturating_add(1); + let slot = HandoffSlot::new(slot_index, shared_handle); + self.slots.insert( + slot_index, + build_slot_state(shared_handle, dims, self.target_kbps, self.frame_rate), + ); + assert!(self.slots.contains_key(&slot_index), "slot stored"); + Ok(slot) + } + + fn encode_shared_async( + &mut self, + slot: HandoffSlot, + _key: u64, + dims: EncoderDims, + pic_params: PicParams, + ) -> Result<(), EncoderError> { + assert!(slot.shared_handle != 0, "slot handle non-zero"); + assert!(dims.width > 0, "width positive"); + let state = self + .slots + .get_mut(&slot.slot_index) + .ok_or(EncoderError::SlotUnknown { + slot_index: slot.slot_index, + })?; + state.surface.data.time_stamp = pic_params.pts_us; + state.bitstream.data_length = 0; + state.bitstream.data_offset = 0; + let mut sync: *mut c_void = ptr::null_mut(); + let status = unsafe { + (self.api.encode_frame_async)( + self.session, + ptr::null_mut(), + &mut state.surface, + &mut state.bitstream, + &mut sync, + ) + }; + state.pending_pts_us = pic_params.pts_us; + state.pending_force_keyframe = pic_params.force_keyframe; + if status == MFX_ERR_MORE_DATA { + state.in_flight = false; + return Ok(()); + } + if status != MFX_ERR_NONE { + return Err(EncoderError::EncodeFailed { + vendor: "qsv", + status: status as i64, + }); + } + state.sync_point = sync; + state.in_flight = !sync.is_null(); + Ok(()) + } + + fn poll_completed(&mut self, slot: HandoffSlot) -> Option { + let session_ptr = self.session; + let dts_offset_us = self.dts_offset_us; + let completed_count = self.completed_count; + let state = self.slots.get_mut(&slot.slot_index)?; + if !state.in_flight || state.sync_point.is_null() { + return None; + } + let status = unsafe { (self.api.sync_operation)(session_ptr, state.sync_point, 0) }; + if status == MFX_WRN_IN_EXECUTION || status != MFX_ERR_NONE { + return None; + } + let len = state.bitstream.data_length as usize; + if len == 0 { + state.in_flight = false; + return None; + } + let mut data: Vec = Vec::with_capacity(len); + let offset = state.bitstream.data_offset as usize; + data.extend_from_slice(&state.bitstream_buf[offset..offset + len]); + let pts = state.bitstream.time_stamp; + let is_keyframe = (state.bitstream.frame_type & 0x1) != 0 || completed_count == 0; + let dts = apply_dts_offset(pts, dts_offset_us); + state.in_flight = false; + state.sync_point = ptr::null_mut(); + self.completed_count = self.completed_count.saturating_add(1); + Some(EncodedBitstream::new(data, pts, dts, is_keyframe)) + } + + fn unregister_slot(&mut self, slot: HandoffSlot) { + self.slots.remove(&slot.slot_index); + } + + fn encode_shared( + &mut self, + submission: EncoderSubmission, + callback: &mut dyn EncoderCompletionCallback, + ) -> Result<(), RingError> { + assert!(submission.shared_handle != 0, "submission handle non-zero"); + assert!(submission.dims.width > 0, "submission width positive"); + let slot = self + .register_slot( + submission.shared_handle, + submission.keyed_mutex_key, + submission.dims, + ) + .map_err(|_| RingError::NotImplemented { + what: "qsv::register_slot in encode_shared", + })?; + let pts_us = submission.capture_pts_us.unwrap_or_else(|| { + submission + .sequence + .saturating_mul(self.frame_rate.frame_interval_us()) + }); + let pic = PicParams::new(pts_us, false); + QsvHandoff::encode_shared_async( + self, + slot, + submission.keyed_mutex_key, + submission.dims, + pic, + ) + .map_err(|_| RingError::NotImplemented { + what: "qsv::encode_shared_async", + })?; + if let Some(bs) = QsvHandoff::poll_completed(self, slot) { + callback.on_complete(submission.sequence, bs.data.len() as u32); + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn sdk_not_found_when_dll_missing() { + let dummy_path = "/this/path/does/not/exist/fake-libmfxhw64.dll"; + let result = unsafe { Library::new(dummy_path) }; + assert!(result.is_err()); + } + + #[test] + fn fourcc_nv12_packs_correctly() { + assert_eq!(MFX_FOURCC_NV12, 0x3231564E); + } + + #[test] + fn align16_rounds_up() { + assert_eq!(align16(1080), 1088); + assert_eq!(align16(1920), 1920); + assert_eq!(align16(0), 0); + } + + #[test] + fn target_kbps_clamps_against_overflow() { + let bitrate: u32 = u32::MAX; + let kbps = (bitrate / 1000).clamp(500, 60_000) as u16; + assert_eq!(kbps, 60_000); + } + + #[test] + fn accel_mode_constant_matches_onevpl_spec() { + assert_eq!(MFX_ACCEL_MODE_VIA_D3D11, 0x0300); + assert_ne!(MFX_ACCEL_MODE_VIA_D3D11, 0x0200); + } + + #[test] + fn impl_type_hardware_constant_matches_onevpl_spec() { + assert_eq!(MFX_IMPL_TYPE_HARDWARE, 2); + } + + #[test] + fn variant_type_u32_constant_matches_onevpl_spec() { + assert_eq!(MFX_VARIANT_TYPE_U32, 5); + assert_ne!(MFX_VARIANT_TYPE_U32, 8); + } + + #[test] + fn d3d11_device_handle_type_matches_onevpl_spec() { + assert_eq!(MFX_HANDLE_D3D11_DEVICE, 3); + assert_ne!(MFX_HANDLE_D3D11_DEVICE, 2); + } + + #[test] + fn variant_version_matches_onevpl_spec() { + assert_eq!(MFX_VARIANT_VERSION_MAJOR, 1); + assert_eq!(MFX_VARIANT_VERSION_MINOR, 1); + } + + #[test] + fn filter_property_name_is_null_terminated_accel() { + assert!(FILTER_PROPERTY_ACCEL.ends_with(b"\0")); + let view = &FILTER_PROPERTY_ACCEL[..FILTER_PROPERTY_ACCEL.len() - 1]; + assert_eq!(view, b"mfxImplDescription.AccelerationMode"); + } + + #[test] + fn filter_property_name_is_null_terminated_impl() { + assert!(FILTER_PROPERTY_IMPL.ends_with(b"\0")); + let view = &FILTER_PROPERTY_IMPL[..FILTER_PROPERTY_IMPL.len() - 1]; + assert_eq!(view, b"mfxImplDescription.Impl"); + } + + #[test] + fn variant_payload_carries_u32_value() { + let v = MfxVariant { + version: MfxStructVersion { + minor: MFX_VARIANT_VERSION_MINOR, + major: MFX_VARIANT_VERSION_MAJOR, + }, + type_: MFX_VARIANT_TYPE_U32, + data: MfxVariantData { + u32_: MFX_ACCEL_MODE_VIA_D3D11, + }, + }; + let read = unsafe { v.data.u32_ }; + assert_eq!(read, MFX_ACCEL_MODE_VIA_D3D11); + assert_eq!(v.type_, 5); + } + + #[derive(Default)] + struct FilterRecord { + property: Vec, + value_u32: u32, + variant_type: u32, + } + + #[test] + fn mock_set_filter_records_property_and_value() { + let mut record = FilterRecord::default(); + let property = FILTER_PROPERTY_ACCEL; + let variant = MfxVariant { + version: MfxStructVersion { + minor: MFX_VARIANT_VERSION_MINOR, + major: MFX_VARIANT_VERSION_MAJOR, + }, + type_: MFX_VARIANT_TYPE_U32, + data: MfxVariantData { + u32_: MFX_ACCEL_MODE_VIA_D3D11, + }, + }; + let mut len: usize = 0; + while *property.get(len).unwrap_or(&1) != 0 { + len += 1; + } + record.property.extend_from_slice(&property[..len]); + record.variant_type = variant.type_; + record.value_u32 = unsafe { variant.data.u32_ }; + assert_eq!(record.property, b"mfxImplDescription.AccelerationMode"); + assert_eq!(record.variant_type, MFX_VARIANT_TYPE_U32); + assert_eq!(record.value_u32, MFX_ACCEL_MODE_VIA_D3D11); + assert_eq!(record.value_u32, 0x0300); + } +} diff --git a/fluxer_desktop/native/encoder-ring/src/ring.rs b/fluxer_desktop/native/encoder-ring/src/ring.rs new file mode 100644 index 0000000..b39f83c --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/src/ring.rs @@ -0,0 +1,966 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::backend::{BackendError, KeyedMutexBackend, TextureFormat}; + +pub const RING_SIZE: usize = 8; + +pub const DUPLICATE_COUNT_MAX: u32 = 30; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SlotState { + Free, + Filling, + Submitted, + Dispatched, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RingError { + FullDropped { + dropped_so_far: u64, + }, + BackendFailed { + source: BackendError, + }, + NotInitialised, + AlreadyInitialised, + UnknownSlot, + UnexpectedSlotState { + slot_index: u32, + observed: SlotState, + }, + PlatformUnsupported { + reason: &'static str, + }, + NotImplemented { + what: &'static str, + }, + SlotsExhausted, +} + +impl std::fmt::Display for RingError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::FullDropped { dropped_so_far } => { + write!(f, "ring full; total dropped={dropped_so_far}") + } + Self::BackendFailed { source } => write!(f, "backend failed: {source}"), + Self::NotInitialised => write!(f, "ring not initialised"), + Self::AlreadyInitialised => write!(f, "ring already initialised"), + Self::UnknownSlot => write!(f, "slot handle does not belong to this ring"), + Self::UnexpectedSlotState { + slot_index, + observed, + } => { + write!(f, "slot {slot_index} in unexpected state {observed:?}") + } + Self::PlatformUnsupported { reason } => write!(f, "platform unsupported: {reason}"), + Self::NotImplemented { what } => write!(f, "not implemented: {what}"), + Self::SlotsExhausted => write!(f, "slot id space exhausted (all candidates in use)"), + } + } +} + +impl std::error::Error for RingError {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct RingMetrics { + pub submitted_count: u64, + pub completed_count: u64, + pub dropped_count: u64, + pub dispatched_count: u64, + pub lagged_count: u64, +} + +pub struct EncoderReady { + pub handle: H, + pub sequence: u64, + pub slot_index: u32, + pub duplicate_count: u32, +} + +pub struct FillReservation { + pub handle: H, + slot_index: u32, + key: u64, +} + +impl FillReservation { + pub fn slot_index(&self) -> u32 { + let index = self.slot_index; + assert!((index as usize) < RING_SIZE_MAX, "slot_index within max"); + assert!(self.key < u64::MAX, "reservation key plausible"); + index + } +} + +struct SlotMeta { + state: SlotState, + sequence: u64, + key: u64, + duplicate_count: u32, +} + +impl SlotMeta { + const fn fresh() -> Self { + Self { + state: SlotState::Free, + sequence: 0, + key: 0, + duplicate_count: 0, + } + } +} + +pub struct EncoderInputRing { + backend: B, + slots: Vec, + meta: Vec, + metrics: RingMetrics, + pending_lagged: u32, + initialised: bool, + width: u32, + height: u32, + format: TextureFormat, +} + +impl EncoderInputRing { + pub fn new(backend: B) -> Self { + let ring = Self { + backend, + slots: Vec::with_capacity(B::NUM_SLOTS), + meta: Vec::with_capacity(B::NUM_SLOTS), + metrics: RingMetrics::default(), + pending_lagged: 0, + initialised: false, + width: 0, + height: 0, + format: TextureFormat::Nv12, + }; + assert!(!ring.initialised, "fresh ring is uninitialised"); + assert_eq!(ring.slots.len(), 0, "fresh ring has no slots"); + ring + } + + pub fn initialise( + &mut self, + width: u32, + height: u32, + format: TextureFormat, + ) -> Result<(), RingError> { + if self.initialised { + return Err(RingError::AlreadyInitialised); + } + let handles = self + .backend + .create_slots(width, height, format) + .map_err(|source| RingError::BackendFailed { source })?; + assert_eq!( + handles.len(), + B::NUM_SLOTS, + "backend returns NUM_SLOTS handles" + ); + assert!(handles.len() <= RING_SIZE_MAX, "NUM_SLOTS within max"); + self.slots = handles; + self.meta = (0..B::NUM_SLOTS).map(|_| SlotMeta::fresh()).collect(); + self.width = width; + self.height = height; + self.format = format; + self.initialised = true; + assert!(self.initialised, "initialised flipped"); + assert_eq!(self.slots.len(), self.meta.len(), "slots and meta align"); + Ok(()) + } + + pub fn submit(&mut self, fill: F) -> Result<(), RingError> + where + F: FnOnce(&mut B::SlotHandle), + { + let mut reservation = self.reserve()?; + fill(&mut reservation.handle); + let sequence = self.commit(reservation)?; + assert!(sequence > 0, "committed sequence positive"); + assert!( + self.metrics.submitted_count >= sequence, + "monotonic submitted" + ); + Ok(()) + } + + pub fn submit_skip_oldest(&mut self, fill: F) -> Result<(), RingError> + where + F: FnOnce(&mut B::SlotHandle), + { + let mut reservation = self.reserve_skip_oldest()?; + fill(&mut reservation.handle); + let sequence = self.commit(reservation)?; + assert!(sequence > 0, "skip-oldest: committed sequence positive"); + assert!( + self.metrics.submitted_count >= sequence, + "skip-oldest: monotonic submitted" + ); + Ok(()) + } + + pub fn reserve(&mut self) -> Result, RingError> { + if !self.initialised { + return Err(RingError::NotInitialised); + } + self.acquire_free_slot() + } + + pub fn reserve_skip_oldest(&mut self) -> Result, RingError> { + if !self.initialised { + return Err(RingError::NotInitialised); + } + if self.find_free_slot().is_none() { + let _ = self.evict_oldest_submitted(); + } + self.acquire_free_slot() + } + + fn acquire_free_slot(&mut self) -> Result, RingError> { + assert!(self.initialised, "acquire requires initialised ring"); + assert_eq!(self.slots.len(), self.meta.len(), "slots and meta align"); + for index in 0..self.meta.len() { + if self.meta[index].state != SlotState::Free { + continue; + } + let key = self.meta[index].key; + match self.backend.acquire_write(&self.slots[index], key) { + Ok(()) => { + self.meta[index].state = SlotState::Filling; + return Ok(FillReservation { + handle: self.slots[index].clone(), + slot_index: index as u32, + key, + }); + } + Err(BackendError::WouldBlock { .. }) => continue, + Err(source) => return Err(RingError::BackendFailed { source }), + } + } + self.fold_lagged(1); + self.metrics.lagged_count = self.metrics.lagged_count.saturating_add(1); + self.metrics.dropped_count = self.metrics.dropped_count.saturating_add(1); + Err(RingError::FullDropped { + dropped_so_far: self.metrics.dropped_count, + }) + } + + pub fn commit( + &mut self, + reservation: FillReservation, + ) -> Result { + if !self.initialised { + return Err(RingError::NotInitialised); + } + let index = reservation.slot_index as usize; + if index >= self.meta.len() { + return Err(RingError::UnknownSlot); + } + let observed = self.meta[index].state; + if observed != SlotState::Filling { + return Err(RingError::UnexpectedSlotState { + slot_index: reservation.slot_index, + observed, + }); + } + assert_eq!( + self.meta[index].key, reservation.key, + "reservation key matches slot" + ); + let next_key = reservation.key.wrapping_add(1); + self.backend + .release_write(&self.slots[index], next_key) + .map_err(|source| RingError::BackendFailed { source })?; + let sequence = self.metrics.submitted_count.saturating_add(1); + self.meta[index].state = SlotState::Submitted; + self.meta[index].sequence = sequence; + self.meta[index].key = next_key; + self.meta[index].duplicate_count = self.pending_lagged; + self.pending_lagged = 0; + self.metrics.submitted_count = sequence; + self.metrics.completed_count = self.metrics.completed_count.saturating_add(1); + assert_eq!( + self.meta[index].state, + SlotState::Submitted, + "post-commit submitted" + ); + assert!( + self.metrics.submitted_count >= sequence, + "commit: monotonic submitted" + ); + Ok(sequence) + } + + pub fn cancel(&mut self, reservation: FillReservation) -> Result<(), RingError> { + if !self.initialised { + return Err(RingError::NotInitialised); + } + let index = reservation.slot_index as usize; + if index >= self.meta.len() { + return Err(RingError::UnknownSlot); + } + let observed = self.meta[index].state; + if observed != SlotState::Filling { + return Err(RingError::UnexpectedSlotState { + slot_index: reservation.slot_index, + observed, + }); + } + assert_eq!( + self.meta[index].key, reservation.key, + "cancel: reservation key matches slot" + ); + let next_key = reservation.key.wrapping_add(1); + self.backend + .release_write(&self.slots[index], next_key) + .map_err(|source| RingError::BackendFailed { source })?; + self.backend.mark_consumed(&self.slots[index]); + self.meta[index].state = SlotState::Free; + self.meta[index].key = next_key; + assert_eq!(self.meta[index].state, SlotState::Free, "post-cancel free"); + assert!( + !self.backend.poll_complete(&self.slots[index]), + "cancelled slot not complete" + ); + Ok(()) + } + + fn fold_lagged(&mut self, amount: u32) { + assert!(amount > 0, "fold amount positive"); + assert!( + amount <= DUPLICATE_COUNT_MAX.saturating_add(1), + "fold amount bounded" + ); + let mut newest: Option = None; + let mut newest_sequence: u64 = 0; + for (i, meta) in self.meta.iter().enumerate() { + if meta.state != SlotState::Submitted { + continue; + } + if meta.sequence >= newest_sequence { + newest_sequence = meta.sequence; + newest = Some(i); + } + } + match newest { + Some(index) => { + let total = self.meta[index].duplicate_count.saturating_add(amount); + self.meta[index].duplicate_count = total.min(DUPLICATE_COUNT_MAX); + } + None => { + let total = self.pending_lagged.saturating_add(amount); + self.pending_lagged = total.min(DUPLICATE_COUNT_MAX); + } + } + assert!( + self.pending_lagged <= DUPLICATE_COUNT_MAX, + "pending lag bounded" + ); + } + + fn evict_oldest_submitted(&mut self) -> bool { + let mut chosen: Option = None; + let mut chosen_sequence: u64 = u64::MAX; + for (i, meta) in self.meta.iter().enumerate() { + if meta.state != SlotState::Submitted { + continue; + } + if meta.sequence < chosen_sequence { + chosen_sequence = meta.sequence; + chosen = Some(i); + } + } + let Some(index) = chosen else { + return false; + }; + assert_eq!( + self.meta[index].state, + SlotState::Submitted, + "evict candidate is submitted" + ); + assert!( + chosen_sequence != u64::MAX, + "evict candidate had real sequence" + ); + let folded = self.meta[index].duplicate_count.saturating_add(1); + self.backend.mark_consumed(&self.slots[index]); + self.meta[index].state = SlotState::Free; + self.meta[index].duplicate_count = 0; + self.fold_lagged(folded); + self.metrics.lagged_count = self.metrics.lagged_count.saturating_add(1); + self.metrics.dropped_count = self.metrics.dropped_count.saturating_add(1); + true + } + + pub fn poll_next_ready(&mut self) -> Option> { + if !self.initialised { + return None; + } + let mut chosen: Option = None; + let mut chosen_sequence: u64 = u64::MAX; + for i in 0..self.meta.len() { + if self.meta[i].state != SlotState::Submitted { + continue; + } + if !self.backend.poll_complete(&self.slots[i]) { + continue; + } + if self.meta[i].sequence < chosen_sequence { + chosen_sequence = self.meta[i].sequence; + chosen = Some(i); + } + } + let index = chosen?; + assert_eq!( + self.meta[index].state, + SlotState::Submitted, + "ready slot was submitted" + ); + assert!( + self.backend.poll_complete(&self.slots[index]), + "ready slot is complete" + ); + self.meta[index].state = SlotState::Dispatched; + self.metrics.dispatched_count = self.metrics.dispatched_count.saturating_add(1); + Some(EncoderReady { + handle: self.slots[index].clone(), + sequence: self.meta[index].sequence, + slot_index: index as u32, + duplicate_count: self.meta[index].duplicate_count, + }) + } + + pub fn release_completed( + &mut self, + ready: EncoderReady, + ) -> Result<(), RingError> { + if !self.initialised { + return Err(RingError::NotInitialised); + } + let index = ready.slot_index as usize; + if index >= self.meta.len() { + return Err(RingError::UnknownSlot); + } + let observed = self.meta[index].state; + if observed != SlotState::Dispatched { + return Err(RingError::UnexpectedSlotState { + slot_index: ready.slot_index, + observed, + }); + } + self.backend.mark_consumed(&self.slots[index]); + self.meta[index].state = SlotState::Free; + assert_eq!(self.meta[index].state, SlotState::Free, "post-release free"); + assert!( + !self.backend.poll_complete(&self.slots[index]), + "no longer reports complete" + ); + Ok(()) + } + + pub fn metrics(&self) -> RingMetrics { + assert!( + self.metrics.completed_count <= self.metrics.submitted_count, + "complete<=submit" + ); + assert!( + self.metrics.dispatched_count <= self.metrics.completed_count, + "dispatch<=complete" + ); + self.metrics + } + + pub fn submitted_count(&self) -> u64 { + self.metrics.submitted_count + } + + pub fn completed_count(&self) -> u64 { + self.metrics.completed_count + } + + pub fn dropped_count(&self) -> u64 { + self.metrics.dropped_count + } + + pub fn dispatched_count(&self) -> u64 { + self.metrics.dispatched_count + } + + pub fn capacity(&self) -> usize { + let cap = B::NUM_SLOTS; + assert!(cap > 0, "NUM_SLOTS must be positive"); + assert!(cap <= RING_SIZE_MAX, "NUM_SLOTS within max"); + cap + } + + pub fn free_count(&self) -> usize { + let mut count: usize = 0; + for meta in self.meta.iter() { + if meta.state == SlotState::Free { + count = count.saturating_add(1); + } + } + assert!(count <= self.meta.len(), "free count within capacity"); + count + } + + pub fn backend_mut(&mut self) -> &mut B { + assert!(self.initialised, "backend access requires init"); + &mut self.backend + } + + fn find_free_slot(&self) -> Option { + for (i, meta) in self.meta.iter().enumerate() { + if meta.state == SlotState::Free { + return Some(i); + } + } + None + } +} + +pub const RING_SIZE_MAX: usize = 16; + +const _: () = assert!(RING_SIZE <= RING_SIZE_MAX, "RING_SIZE within max"); + +#[cfg(test)] +mod tests { + use super::*; + use crate::backend::{CpuMemcpyBackend, CpuSlotHandle}; + + fn make_ring() -> EncoderInputRing { + let mut ring = EncoderInputRing::new(CpuMemcpyBackend::new()); + ring.initialise(64, 64, TextureFormat::Nv12).expect("init"); + ring + } + + fn fill_noop(_: &mut CpuSlotHandle) {} + + #[test] + fn submit_then_poll_returns_some() { + let mut ring = make_ring(); + ring.submit(fill_noop).expect("submit"); + let ready = ring.poll_next_ready().expect("poll yields ready"); + assert_eq!(ready.sequence, 1); + ring.release_completed(ready).expect("release"); + } + + #[test] + fn eight_submits_dispatch_in_fifo_order() { + let mut ring = make_ring(); + for _ in 0..8 { + ring.submit(fill_noop).expect("submit"); + } + assert_eq!(ring.submitted_count(), 8); + let mut observed_seq: Vec = Vec::new(); + for _ in 0..8 { + let ready = ring.poll_next_ready().expect("ready"); + observed_seq.push(ready.sequence); + ring.release_completed(ready).expect("release"); + } + assert_eq!(observed_seq, vec![1, 2, 3, 4, 5, 6, 7, 8]); + } + + #[test] + fn ninth_submit_when_full_returns_full_dropped() { + let mut ring = make_ring(); + for _ in 0..8 { + ring.submit(fill_noop).expect("submit"); + } + let err = ring.submit(fill_noop).err(); + assert!(matches!( + err, + Some(RingError::FullDropped { dropped_so_far: 1 }) + )); + assert_eq!(ring.dropped_count(), 1); + let err2 = ring.submit(fill_noop).err(); + assert!(matches!( + err2, + Some(RingError::FullDropped { dropped_so_far: 2 }) + )); + assert_eq!(ring.dropped_count(), 2); + } + + #[test] + fn release_completed_returns_slot_to_pool() { + let mut ring = make_ring(); + ring.submit(fill_noop).expect("submit"); + let ready = ring.poll_next_ready().expect("ready"); + assert_eq!(ring.free_count(), 7); + ring.release_completed(ready).expect("release"); + assert_eq!(ring.free_count(), 8); + ring.submit(fill_noop).expect("re-submit after release"); + } + + #[test] + fn pair_asserts_pass_under_random_submit_poll_release() { + let mut ring = make_ring(); + let mut state: u64 = 0xcafef00d; + let mut in_flight: Vec> = Vec::new(); + for _ in 0..1000 { + state ^= state << 13; + state ^= state >> 7; + state ^= state << 17; + let action = state % 3; + match action { + 0 => { + let _ = ring.submit(fill_noop); + } + 1 => { + if let Some(r) = ring.poll_next_ready() { + in_flight.push(r); + } + } + _ => { + if let Some(r) = in_flight.pop() { + ring.release_completed(r).expect("release"); + } + } + } + let metrics = ring.metrics(); + assert!(metrics.completed_count <= metrics.submitted_count); + assert!(metrics.dispatched_count <= metrics.completed_count); + } + while let Some(r) = in_flight.pop() { + ring.release_completed(r).expect("drain release"); + } + while let Some(r) = ring.poll_next_ready() { + ring.release_completed(r).expect("drain release post poll"); + } + assert_eq!(ring.free_count(), ring.capacity()); + } + + #[test] + #[allow(clippy::panic)] + fn submit_with_panicking_closure_leaves_state_consistent() { + use std::panic::{AssertUnwindSafe, catch_unwind}; + let mut ring = make_ring(); + ring.submit(fill_noop).expect("first submit ok"); + let result = catch_unwind(AssertUnwindSafe(|| { + let _ = ring.submit(|_h: &mut CpuSlotHandle| panic!("user fill panicked")); + })); + assert!(result.is_err()); + let metrics = ring.metrics(); + assert!(metrics.submitted_count >= 1); + assert!(metrics.completed_count <= metrics.submitted_count); + assert!(metrics.dispatched_count <= metrics.completed_count); + let ready = ring.poll_next_ready().expect("first frame still pollable"); + assert_eq!(ready.sequence, 1); + ring.release_completed(ready).expect("release first"); + } + + #[test] + fn determinism_same_sequence_yields_same_release_order() { + fn run() -> Vec { + let mut ring = make_ring(); + let mut released: Vec = Vec::new(); + for _ in 0..8 { + ring.submit(fill_noop).expect("submit"); + } + for _ in 0..8 { + let r = ring.poll_next_ready().expect("ready"); + released.push(r.sequence); + ring.release_completed(r).expect("release"); + } + released + } + let a = run(); + let b = run(); + assert_eq!(a, b); + assert_eq!(a, vec![1, 2, 3, 4, 5, 6, 7, 8]); + } + + #[test] + fn capacity_is_eight() { + let ring = make_ring(); + assert_eq!(ring.capacity(), 8); + assert_eq!(ring.free_count(), 8); + } + + #[test] + fn double_release_of_same_ready_rejected() { + let mut ring = make_ring(); + ring.submit(fill_noop).expect("submit"); + let ready = ring.poll_next_ready().expect("ready"); + let cloned = EncoderReady { + handle: ready.handle.clone(), + sequence: ready.sequence, + slot_index: ready.slot_index, + duplicate_count: ready.duplicate_count, + }; + ring.release_completed(ready).expect("first release"); + let err = ring.release_completed(cloned).err(); + assert!(matches!(err, Some(RingError::UnexpectedSlotState { .. }))); + } + + #[test] + fn submit_before_init_returns_not_initialised() { + let mut ring: EncoderInputRing = + EncoderInputRing::new(CpuMemcpyBackend::new()); + let err = ring.submit(fill_noop).err(); + assert!(matches!(err, Some(RingError::NotInitialised))); + } + + #[test] + fn re_initialise_rejected() { + let mut ring = make_ring(); + let err = ring.initialise(64, 64, TextureFormat::Nv12).err(); + assert!(matches!(err, Some(RingError::AlreadyInitialised))); + } + + #[test] + fn poll_when_empty_returns_none() { + let mut ring = make_ring(); + assert!(ring.poll_next_ready().is_none()); + } + + #[test] + fn skip_oldest_rejects_when_every_slot_is_dispatched() { + let mut ring = make_ring(); + for _ in 0..8 { + ring.submit(fill_noop).expect("submit"); + } + let mut in_flight: Vec> = Vec::new(); + for _ in 0..8 { + in_flight.push(ring.poll_next_ready().expect("ready")); + } + assert_eq!(ring.free_count(), 0); + let err = ring.submit_skip_oldest(fill_noop).err(); + assert!(matches!( + err, + Some(RingError::FullDropped { dropped_so_far: 1 }) + )); + assert_eq!(ring.dropped_count(), 1); + assert_eq!(ring.dispatched_count(), 8); + for ready in in_flight.drain(..) { + ring.release_completed(ready).expect("release"); + } + assert_eq!(ring.free_count(), 8); + ring.submit_skip_oldest(fill_noop) + .expect("submit succeeds once dispatched slots are released"); + } + + #[test] + fn skip_oldest_evicts_oldest_submitted_never_dispatched() { + let mut ring = make_ring(); + for _ in 0..8 { + ring.submit(fill_noop).expect("submit"); + } + let mut dispatched: Vec> = Vec::new(); + for _ in 0..3 { + dispatched.push(ring.poll_next_ready().expect("ready")); + } + assert_eq!(dispatched[0].sequence, 1); + assert_eq!(dispatched[2].sequence, 3); + ring.submit_skip_oldest(fill_noop) + .expect("skip-oldest evicts a submitted slot"); + assert_eq!(ring.dropped_count(), 1); + assert_eq!(ring.submitted_count(), 9); + let mut remaining: Vec = Vec::new(); + while let Some(ready) = ring.poll_next_ready() { + remaining.push(ready.sequence); + ring.release_completed(ready).expect("release"); + } + assert_eq!(remaining, vec![5, 6, 7, 8, 9], "sequence 4 was evicted"); + for ready in dispatched.drain(..) { + ring.release_completed(ready).expect("release dispatched"); + } + assert_eq!(ring.free_count(), 8); + } + + #[test] + fn reserve_then_commit_matches_submit_semantics() { + let mut ring = make_ring(); + let reservation = ring.reserve().expect("reserve"); + assert_eq!(reservation.slot_index(), 0); + assert_eq!(ring.free_count(), 7); + assert_eq!(ring.submitted_count(), 0, "sequence assigned at commit"); + let sequence = ring.commit(reservation).expect("commit"); + assert_eq!(sequence, 1); + let ready = ring.poll_next_ready().expect("ready"); + assert_eq!(ready.sequence, 1); + assert_eq!(ready.duplicate_count, 0); + ring.release_completed(ready).expect("release"); + } + + #[test] + fn cancel_returns_slot_to_free_without_sequence() { + let mut ring = make_ring(); + let reservation = ring.reserve().expect("reserve"); + ring.cancel(reservation).expect("cancel"); + assert_eq!(ring.free_count(), 8); + assert_eq!(ring.submitted_count(), 0); + assert!(ring.poll_next_ready().is_none()); + ring.submit(fill_noop).expect("submit after cancel"); + let ready = ring.poll_next_ready().expect("ready"); + assert_eq!(ready.sequence, 1); + ring.release_completed(ready).expect("release"); + } + + #[test] + fn commit_of_freed_reservation_rejected() { + let mut ring = make_ring(); + let first = ring.reserve().expect("reserve"); + let index = first.slot_index(); + ring.cancel(first).expect("cancel"); + let second = ring.reserve().expect("re-reserve"); + assert_eq!(second.slot_index(), index, "same slot reused"); + ring.commit(second).expect("commit reused slot"); + let ready = ring.poll_next_ready().expect("ready"); + let stale = EncoderReady { + handle: ready.handle.clone(), + sequence: ready.sequence, + slot_index: ready.slot_index, + duplicate_count: ready.duplicate_count, + }; + ring.release_completed(ready).expect("release"); + let err = ring.release_completed(stale).err(); + assert!(matches!(err, Some(RingError::UnexpectedSlotState { .. }))); + } + + #[test] + fn skip_oldest_never_evicts_filling_slot() { + let mut ring = make_ring(); + for _ in 0..7 { + ring.submit(fill_noop).expect("submit"); + } + let reservation = ring.reserve().expect("reserve eighth slot"); + let reserved_index = reservation.slot_index(); + ring.submit_skip_oldest(fill_noop) + .expect("skip-oldest evicts a submitted slot"); + assert_eq!(ring.dropped_count(), 1); + let sequence = ring.commit(reservation).expect("commit survives eviction"); + assert_eq!(sequence, 9); + let mut seen_indices: Vec = Vec::new(); + while let Some(ready) = ring.poll_next_ready() { + seen_indices.push(ready.slot_index); + ring.release_completed(ready).expect("release"); + } + assert!(seen_indices.contains(&reserved_index)); + } + + #[test] + fn full_ring_submit_folds_lag_into_newest_submitted() { + let mut ring = make_ring(); + for _ in 0..8 { + ring.submit(fill_noop).expect("submit"); + } + let err = ring.submit(fill_noop).err(); + assert!(matches!( + err, + Some(RingError::FullDropped { dropped_so_far: 1 }) + )); + assert_eq!(ring.metrics().lagged_count, 1); + let mut by_sequence: Vec<(u64, u32)> = Vec::new(); + while let Some(ready) = ring.poll_next_ready() { + by_sequence.push((ready.sequence, ready.duplicate_count)); + ring.release_completed(ready).expect("release"); + } + assert_eq!(by_sequence.len(), 8); + for (sequence, duplicate_count) in by_sequence.iter().take(7) { + assert_eq!(*duplicate_count, 0, "sequence {sequence} not duplicated"); + } + assert_eq!(by_sequence[7], (8, 1), "newest carries the lagged frame"); + } + + #[test] + fn eviction_conserves_duplicate_timing_slots() { + let mut ring = make_ring(); + for _ in 0..8 { + ring.submit(fill_noop).expect("submit"); + } + let mut dispatched: Vec> = Vec::new(); + for _ in 0..7 { + dispatched.push(ring.poll_next_ready().expect("ready")); + } + let _ = ring.submit(fill_noop).err(); + ring.submit_skip_oldest(fill_noop) + .expect("evicts the only submitted slot"); + let ready = ring.poll_next_ready().expect("new frame ready"); + assert_eq!(ready.sequence, 9); + assert_eq!( + ready.duplicate_count, 2, + "evicted frame plus its duplicate folded into successor" + ); + assert_eq!(ring.metrics().lagged_count, 2); + ring.release_completed(ready).expect("release"); + for ready in dispatched.drain(..) { + ring.release_completed(ready).expect("release dispatched"); + } + } + + #[test] + fn all_dispatched_lag_attaches_to_next_submission() { + let mut ring = make_ring(); + for _ in 0..8 { + ring.submit(fill_noop).expect("submit"); + } + let mut in_flight: Vec> = Vec::new(); + for _ in 0..8 { + in_flight.push(ring.poll_next_ready().expect("ready")); + } + for _ in 0..2 { + let err = ring.submit_skip_oldest(fill_noop).err(); + assert!(matches!(err, Some(RingError::FullDropped { .. }))); + } + assert_eq!(ring.metrics().lagged_count, 2); + let first = in_flight.remove(0); + ring.release_completed(first).expect("release one"); + ring.submit_skip_oldest(fill_noop) + .expect("submit after free"); + let ready = ring.poll_next_ready().expect("ready"); + assert_eq!(ready.sequence, 9); + assert_eq!(ready.duplicate_count, 2, "pending lag attached"); + ring.release_completed(ready).expect("release"); + for ready in in_flight.drain(..) { + ring.release_completed(ready).expect("release in flight"); + } + } + + #[test] + fn duplicate_count_saturates_at_named_cap() { + let mut ring = make_ring(); + for _ in 0..8 { + ring.submit(fill_noop).expect("submit"); + } + for _ in 0..(DUPLICATE_COUNT_MAX + 5) { + let _ = ring.submit(fill_noop).err(); + } + let mut last: Option<(u64, u32)> = None; + while let Some(ready) = ring.poll_next_ready() { + last = Some((ready.sequence, ready.duplicate_count)); + ring.release_completed(ready).expect("release"); + } + assert_eq!( + last, + Some((8, DUPLICATE_COUNT_MAX)), + "duplicates saturate at cap" + ); + } + + #[test] + fn skip_oldest_repeated_rejection_counts_every_drop() { + let mut ring = make_ring(); + for _ in 0..8 { + ring.submit(fill_noop).expect("submit"); + } + let mut in_flight: Vec> = Vec::new(); + for _ in 0..8 { + in_flight.push(ring.poll_next_ready().expect("ready")); + } + for expected_drops in 1..=3_u64 { + let err = ring.submit_skip_oldest(fill_noop).err(); + assert!(matches!( + err, + Some(RingError::FullDropped { dropped_so_far }) if dropped_so_far == expected_drops + )); + } + assert_eq!(ring.dropped_count(), 3); + while let Some(ready) = in_flight.pop() { + ring.release_completed(ready).expect("release"); + } + } +} diff --git a/fluxer_desktop/native/encoder-ring/src/vt_compression_macos.rs b/fluxer_desktop/native/encoder-ring/src/vt_compression_macos.rs new file mode 100644 index 0000000..5c871d8 --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/src/vt_compression_macos.rs @@ -0,0 +1,1126 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use core::ffi::c_void; +use core::ptr::{self, NonNull}; +use std::collections::HashMap; +use std::collections::VecDeque; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::{Arc, Mutex}; + +use crate::encoder_handoff::{ + EncodedBitstream, EncoderDims, EncoderError, EncoderFrameRate, EncoderSubmission, HandoffSlot, + MAX_BITSTREAM_BYTES, PicParams, VideoToolboxHandoff, +}; +use crate::ring::RingError; + +type CfDictionaryRef = *const c_void; +type CfStringRef = *const c_void; +type CfNumberRef = *const c_void; +type CfBooleanRef = *const c_void; +type CfAllocatorRef = *const c_void; +type CmTime = OsCmTime; +pub(crate) type CvPixelBufferRef = *mut c_void; +type CmSampleBufferRef = *mut c_void; +type CmBlockBufferRef = *mut c_void; +type VtCompressionSessionRef = *mut c_void; +type VtPixelTransferSessionRef = *mut c_void; +type OsStatus = i32; +pub(crate) type IoSurfaceRef = *mut c_void; + +const KCM_VIDEO_CODEC_TYPE_H264: u32 = u32::from_be_bytes(*b"avc1"); +#[cfg(test)] +const KCV_PIXEL_FORMAT_TYPE_420_YPCBCR8_BIPLANAR_VIDEO: u32 = u32::from_be_bytes(*b"420v"); +const NOERR: OsStatus = 0; +const KCM_TIME_FLAGS_VALID: u32 = 1; +const KCF_NUMBER_SINT32_TYPE: i32 = 3; +const COMPLETION_RING_CAPACITY: usize = 16; +const KVT_INVALID_SESSION_ERR: OsStatus = -12903; +const VT_SESSION_REBUILD_MAX: u64 = 3; +const VT_ENCODE_ATTEMPTS_PER_FRAME_MAX: usize = 2; + +#[repr(C)] +#[derive(Copy, Clone)] +struct OsCmTime { + value: i64, + timescale: i32, + flags: u32, + epoch: i64, +} + +#[link(name = "CoreFoundation", kind = "framework")] +unsafe extern "C" { + static kCFAllocatorDefault: CfAllocatorRef; + static kCFBooleanTrue: CfBooleanRef; + fn CFNumberCreate( + allocator: CfAllocatorRef, + the_type: i32, + value_ptr: *const c_void, + ) -> CfNumberRef; + fn CFRelease(cf: *const c_void); + fn CFRetain(cf: *const c_void) -> *const c_void; +} + +#[link(name = "CoreVideo", kind = "framework")] +unsafe extern "C" { + fn CVPixelBufferCreateWithIOSurface( + allocator: CfAllocatorRef, + surface: IoSurfaceRef, + pixel_buffer_attributes: CfDictionaryRef, + pixel_buffer_out: *mut CvPixelBufferRef, + ) -> i32; + fn CVPixelBufferRelease(buffer: CvPixelBufferRef); +} + +#[link(name = "CoreMedia", kind = "framework")] +unsafe extern "C" { + fn CMSampleBufferGetDataBuffer(sample_buffer: CmSampleBufferRef) -> CmBlockBufferRef; + fn CMBlockBufferGetDataLength(block_buffer: CmBlockBufferRef) -> usize; + fn CMBlockBufferCopyDataBytes( + block_buffer: CmBlockBufferRef, + offset_to_data: usize, + data_length: usize, + destination: *mut c_void, + ) -> OsStatus; +} + +#[link(name = "VideoToolbox", kind = "framework")] +unsafe extern "C" { + static kVTCompressionPropertyKey_RealTime: CfStringRef; + static kVTCompressionPropertyKey_ProfileLevel: CfStringRef; + static kVTProfileLevel_H264_Baseline_AutoLevel: CfStringRef; + static kVTCompressionPropertyKey_AverageBitRate: CfStringRef; + static kVTCompressionPropertyKey_ExpectedFrameRate: CfStringRef; + static kVTCompressionPropertyKey_AllowFrameReordering: CfStringRef; + fn VTCompressionSessionCreate( + allocator: CfAllocatorRef, + width: i32, + height: i32, + codec_type: u32, + encoder_specification: CfDictionaryRef, + source_image_buffer_attributes: CfDictionaryRef, + compressed_data_allocator: CfAllocatorRef, + output_callback: Option, + output_callback_refcon: *mut c_void, + session_out: *mut VtCompressionSessionRef, + ) -> OsStatus; + fn VTCompressionSessionEncodeFrame( + session: VtCompressionSessionRef, + image_buffer: CvPixelBufferRef, + presentation_time_stamp: CmTime, + duration: CmTime, + frame_properties: CfDictionaryRef, + source_frame_refcon: *mut c_void, + info_flags_out: *mut u32, + ) -> OsStatus; + fn VTCompressionSessionCompleteFrames( + session: VtCompressionSessionRef, + complete_until: CmTime, + ) -> OsStatus; + fn VTCompressionSessionInvalidate(session: VtCompressionSessionRef); + fn VTSessionSetProperty( + session: *mut c_void, + property_key: CfStringRef, + property_value: *const c_void, + ) -> OsStatus; + fn VTPixelTransferSessionCreate( + allocator: CfAllocatorRef, + session_out: *mut VtPixelTransferSessionRef, + ) -> OsStatus; + fn VTPixelTransferSessionTransferImage( + session: VtPixelTransferSessionRef, + source: CvPixelBufferRef, + destination: CvPixelBufferRef, + ) -> OsStatus; + fn VTPixelTransferSessionInvalidate(session: VtPixelTransferSessionRef); +} + +type VtCompressionOutputCallback = unsafe extern "C" fn( + output_callback_refcon: *mut c_void, + source_frame_refcon: *mut c_void, + status: OsStatus, + info_flags: u32, + sample_buffer: CmSampleBufferRef, +); + +fn is_invalid_session_error(err: &EncoderError) -> bool { + match err { + EncoderError::EncodeFailed { vendor, status } => { + assert!(!vendor.is_empty(), "encode error vendor non-empty"); + *vendor == "vt-compression-encode" && *status == KVT_INVALID_SESSION_ERR as i64 + } + _ => false, + } +} + +fn cf_num_i32(v: i32) -> CfNumberRef { + let ptr: *const i32 = &v; + unsafe { + CFNumberCreate( + kCFAllocatorDefault, + KCF_NUMBER_SINT32_TYPE, + ptr as *const c_void, + ) + } +} + +pub struct VtPixelTransfer { + session: NonNull, +} + +unsafe impl Send for VtPixelTransfer {} + +impl VtPixelTransfer { + pub fn new() -> Result { + let mut raw: VtPixelTransferSessionRef = ptr::null_mut(); + let status = unsafe { VTPixelTransferSessionCreate(kCFAllocatorDefault, &mut raw) }; + if status != NOERR || raw.is_null() { + return Err(EncoderError::SessionInitFailed { + vendor: "vt-pixel-transfer", + status: status as i64, + }); + } + let session = NonNull::new(raw).ok_or(EncoderError::SessionInitFailed { + vendor: "vt-pixel-transfer", + status: -1, + })?; + assert!( + session.as_ptr() as usize != 0, + "transfer session non-null after create" + ); + Ok(Self { session }) + } + + #[allow(clippy::missing_safety_doc)] + pub unsafe fn transfer( + &self, + source: CvPixelBufferRef, + destination: CvPixelBufferRef, + ) -> Result<(), EncoderError> { + assert!(!source.is_null(), "source pixel buffer non-null"); + assert!(!destination.is_null(), "destination pixel buffer non-null"); + let status = unsafe { + VTPixelTransferSessionTransferImage(self.session.as_ptr(), source, destination) + }; + if status != NOERR { + return Err(EncoderError::EncodeFailed { + vendor: "vt-pixel-transfer", + status: status as i64, + }); + } + Ok(()) + } + + #[allow(clippy::missing_safety_doc)] + pub unsafe fn wrap_iosurface( + surface: IoSurfaceRef, + width: u32, + height: u32, + ) -> Result { + assert!(!surface.is_null(), "wrap source IOSurface non-null"); + assert!(width > 0 && height > 0, "wrap dims positive"); + let mut pb: CvPixelBufferRef = ptr::null_mut(); + let status = unsafe { + CVPixelBufferCreateWithIOSurface(kCFAllocatorDefault, surface, ptr::null(), &mut pb) + }; + if status != 0 || pb.is_null() { + return Err(EncoderError::EncodeFailed { + vendor: "cv-create-iosurface", + status: status as i64, + }); + } + assert!(!pb.is_null(), "wrap pb non-null after create"); + Ok(pb) + } +} + +impl Drop for VtPixelTransfer { + fn drop(&mut self) { + let p = self.session.as_ptr(); + if !p.is_null() { + unsafe { VTPixelTransferSessionInvalidate(p) }; + unsafe { CFRelease(p) }; + } + } +} + +struct CompletionRing { + buffer: VecDeque, +} + +impl CompletionRing { + fn new() -> Self { + Self { + buffer: VecDeque::with_capacity(COMPLETION_RING_CAPACITY), + } + } + + fn push(&mut self, item: EncodedBitstream) { + if self.buffer.len() >= COMPLETION_RING_CAPACITY { + self.buffer.pop_front(); + } + self.buffer.push_back(item); + assert!( + self.buffer.len() <= COMPLETION_RING_CAPACITY, + "completion ring bounded" + ); + } + + fn pop(&mut self) -> Option { + let item = self.buffer.pop_front(); + assert!( + self.buffer.len() <= COMPLETION_RING_CAPACITY, + "completion ring bounded after pop" + ); + item + } + + fn len(&self) -> usize { + let n = self.buffer.len(); + assert!(n <= COMPLETION_RING_CAPACITY, "completion ring len bounded"); + n + } +} + +struct SharedState { + completed: CompletionRing, + accepted: u64, + last_pts_us: u64, +} + +struct VtCallbackContext { + state: Arc>, + failed_completions: AtomicU64, +} + +unsafe extern "C" fn vt_compression_output( + output_callback_refcon: *mut c_void, + source_frame_refcon: *mut c_void, + status: OsStatus, + _info_flags: u32, + sample_buffer: CmSampleBufferRef, +) { + if output_callback_refcon.is_null() { + return; + } + let ctx = unsafe { &*(output_callback_refcon as *const VtCallbackContext) }; + if status != NOERR { + ctx.failed_completions.fetch_add(1, Ordering::Relaxed); + return; + } + if sample_buffer.is_null() { + return; + } + let block = unsafe { CMSampleBufferGetDataBuffer(sample_buffer) }; + if block.is_null() { + return; + } + let len = unsafe { CMBlockBufferGetDataLength(block) }; + if len == 0 || len > MAX_BITSTREAM_BYTES { + return; + } + let mut bytes: Vec = vec![0u8; len]; + let copy_status = + unsafe { CMBlockBufferCopyDataBytes(block, 0, len, bytes.as_mut_ptr() as *mut c_void) }; + if copy_status != NOERR { + return; + } + let pts_us = source_frame_refcon as usize as u64; + let payload = + match std::panic::catch_unwind(|| EncodedBitstream::new(bytes, pts_us, pts_us, true)) { + Ok(p) => p, + Err(_) => return, + }; + if let Ok(mut guard) = ctx.state.lock() { + guard.completed.push(payload); + } +} + +struct SlotSurface { + surface: IoSurfaceRef, + pixel_buffer: CvPixelBufferRef, +} + +pub struct VtCompressionHandoff { + session: NonNull, + callback_context: Box, + shared: Arc>, + surfaces: HashMap, + dims: EncoderDims, + next_slot: u32, + frame_interval_us: u64, + frame_rate: EncoderFrameRate, + session_rebuilds: u64, +} + +unsafe impl Send for VtCompressionHandoff {} + +impl VtCompressionHandoff { + pub fn try_new(dims: EncoderDims) -> Result { + Self::try_new_with_frame_rate(dims, EncoderFrameRate::from_fps(30)) + } + + pub fn try_new_with_frame_rate( + dims: EncoderDims, + frame_rate: EncoderFrameRate, + ) -> Result { + assert!( + dims.width > 0 && dims.height > 0, + "compression dims positive" + ); + assert!(frame_rate.numerator > 0, "frame rate numerator positive"); + assert!( + frame_rate.denominator > 0, + "frame rate denominator positive" + ); + if dims.width > 7680 || dims.height > 4320 { + return Err(EncoderError::DimensionsOutOfRange { + width: dims.width, + height: dims.height, + }); + } + let shared = Arc::new(Mutex::new(SharedState { + completed: CompletionRing::new(), + accepted: 0, + last_pts_us: 0, + })); + let mut callback_context = Box::new(VtCallbackContext { + state: shared.clone(), + failed_completions: AtomicU64::new(0), + }); + let cb_ctx_ptr = callback_context.as_mut() as *mut VtCallbackContext as *mut c_void; + let session = Self::create_session(dims, cb_ctx_ptr, frame_rate)?; + let handoff = Self { + session, + callback_context, + shared, + surfaces: HashMap::new(), + dims, + next_slot: 0, + frame_interval_us: frame_rate.frame_interval_us(), + frame_rate, + session_rebuilds: 0, + }; + assert!( + handoff.session.as_ptr() as usize != 0, + "session non-null after init" + ); + assert_eq!(handoff.next_slot, 0, "next slot fresh"); + Ok(handoff) + } + + fn create_session( + dims: EncoderDims, + cb_ctx_ptr: *mut c_void, + frame_rate: EncoderFrameRate, + ) -> Result, EncoderError> { + assert!(dims.width > 0, "session dims width positive"); + assert!(!cb_ctx_ptr.is_null(), "session callback context non-null"); + assert!(frame_rate.numerator > 0, "frame rate numerator positive"); + let mut raw: VtCompressionSessionRef = ptr::null_mut(); + let status = unsafe { + VTCompressionSessionCreate( + kCFAllocatorDefault, + dims.width as i32, + dims.height as i32, + KCM_VIDEO_CODEC_TYPE_H264, + ptr::null(), + ptr::null(), + kCFAllocatorDefault, + Some(vt_compression_output), + cb_ctx_ptr, + &mut raw, + ) + }; + if status != NOERR || raw.is_null() { + return Err(EncoderError::SessionInitFailed { + vendor: "vt-compression", + status: status as i64, + }); + } + let session = NonNull::new(raw).ok_or(EncoderError::SessionInitFailed { + vendor: "vt-compression", + status: -1, + })?; + Self::configure(session.as_ptr(), frame_rate)?; + Ok(session) + } + + fn try_rebuild_session(&mut self) -> bool { + assert!( + self.session_rebuilds <= VT_SESSION_REBUILD_MAX, + "rebuild count within cap" + ); + if self.session_rebuilds >= VT_SESSION_REBUILD_MAX { + return false; + } + let cb_ctx_ptr = self.callback_context.as_mut() as *mut VtCallbackContext as *mut c_void; + let Ok(new_session) = Self::create_session(self.dims, cb_ctx_ptr, self.frame_rate) else { + return false; + }; + let old = self.session.as_ptr(); + assert!(!old.is_null(), "old session non-null before replace"); + unsafe { + VTCompressionSessionInvalidate(old); + CFRelease(old); + } + self.session = new_session; + self.session_rebuilds = self.session_rebuilds.saturating_add(1); + assert!( + self.session_rebuilds <= VT_SESSION_REBUILD_MAX, + "rebuild count stays within cap" + ); + true + } + + fn configure(session: *mut c_void, frame_rate: EncoderFrameRate) -> Result<(), EncoderError> { + assert!(!session.is_null(), "configure session non-null"); + assert!(frame_rate.numerator > 0, "frame rate numerator positive"); + let real_time_true = unsafe { kCFBooleanTrue }; + let st1 = unsafe { + VTSessionSetProperty(session, kVTCompressionPropertyKey_RealTime, real_time_true) + }; + if st1 != NOERR { + return Err(EncoderError::SessionInitFailed { + vendor: "vt-compression-realtime", + status: st1 as i64, + }); + } + let st2 = unsafe { + VTSessionSetProperty( + session, + kVTCompressionPropertyKey_ProfileLevel, + kVTProfileLevel_H264_Baseline_AutoLevel, + ) + }; + if st2 != NOERR { + return Err(EncoderError::SessionInitFailed { + vendor: "vt-compression-profile", + status: st2 as i64, + }); + } + let bitrate = cf_num_i32(2_000_000); + let st3 = unsafe { + VTSessionSetProperty(session, kVTCompressionPropertyKey_AverageBitRate, bitrate) + }; + unsafe { CFRelease(bitrate) }; + if st3 != NOERR { + return Err(EncoderError::SessionInitFailed { + vendor: "vt-compression-bitrate", + status: st3 as i64, + }); + } + let fps_value = frame_rate.gop_pic_size() as i32; + let fps = cf_num_i32(fps_value); + let st4 = unsafe { + VTSessionSetProperty(session, kVTCompressionPropertyKey_ExpectedFrameRate, fps) + }; + unsafe { CFRelease(fps) }; + if st4 != NOERR { + return Err(EncoderError::SessionInitFailed { + vendor: "vt-compression-fps", + status: st4 as i64, + }); + } + let no_reorder = unsafe { CFRetain(kCFBooleanTrue) }; + let st5 = unsafe { + VTSessionSetProperty( + session, + kVTCompressionPropertyKey_AllowFrameReordering, + no_reorder, + ) + }; + unsafe { CFRelease(no_reorder) }; + if st5 != NOERR { + return Err(EncoderError::SessionInitFailed { + vendor: "vt-compression-reorder", + status: st5 as i64, + }); + } + Ok(()) + } + + pub fn pending_completion(&self) -> usize { + let guard = match self.shared.lock() { + Ok(g) => g, + Err(_) => return 0, + }; + let n = guard.completed.len(); + assert!(n <= COMPLETION_RING_CAPACITY, "pending count bounded"); + n + } + + pub fn accepted_count(&self) -> u64 { + let guard = match self.shared.lock() { + Ok(g) => g, + Err(_) => return 0, + }; + let n = guard.accepted; + assert!( + self.surfaces.len() as u64 <= u32::MAX as u64, + "surfaces table bounded" + ); + n + } + + pub fn failed_completion_count(&self) -> u64 { + let n = self + .callback_context + .failed_completions + .load(Ordering::Relaxed); + assert!( + self.session_rebuilds <= VT_SESSION_REBUILD_MAX, + "rebuild count within cap" + ); + n + } + + pub fn session_rebuild_count(&self) -> u64 { + let n = self.session_rebuilds; + assert!(n <= VT_SESSION_REBUILD_MAX, "rebuild count within cap"); + n + } + + pub fn wait_for_completion(&self) -> Result<(), EncoderError> { + let until = CmTime { + value: 0, + timescale: 0, + flags: 0, + epoch: 0, + }; + let status = unsafe { VTCompressionSessionCompleteFrames(self.session.as_ptr(), until) }; + if status != NOERR { + return Err(EncoderError::EncodeFailed { + vendor: "vt-compression-complete", + status: status as i64, + }); + } + Ok(()) + } + + fn encode_frame_once( + &self, + pixel_buffer: CvPixelBufferRef, + pic_params: PicParams, + ) -> Result<(), EncoderError> { + assert!(!pixel_buffer.is_null(), "encode pixel buffer non-null"); + assert!(self.frame_interval_us > 0, "frame interval positive"); + let pts = CmTime { + value: pic_params.pts_us as i64, + timescale: 1_000_000, + flags: KCM_TIME_FLAGS_VALID, + epoch: 0, + }; + let dur = CmTime { + value: self.frame_interval_us as i64, + timescale: 1_000_000, + flags: KCM_TIME_FLAGS_VALID, + epoch: 0, + }; + let mut flags_out: u32 = 0; + let refcon = pic_params.pts_us as usize as *mut c_void; + let enc_status = unsafe { + VTCompressionSessionEncodeFrame( + self.session.as_ptr(), + pixel_buffer, + pts, + dur, + ptr::null(), + refcon, + &mut flags_out, + ) + }; + if enc_status != NOERR { + return Err(EncoderError::EncodeFailed { + vendor: "vt-compression-encode", + status: enc_status as i64, + }); + } + Ok(()) + } + + fn encode_one(&mut self, slot: HandoffSlot, pic_params: PicParams) -> Result<(), EncoderError> { + assert!(slot.shared_handle != 0, "encode slot handle non-zero"); + let pixel_buffer = match self.surfaces.get(&slot.slot_index) { + Some(entry) => { + assert!(!entry.surface.is_null(), "cached slot surface non-null"); + assert!( + !entry.pixel_buffer.is_null(), + "cached pixel buffer non-null" + ); + entry.pixel_buffer + } + None => { + return Err(EncoderError::SlotUnknown { + slot_index: slot.slot_index, + }); + } + }; + let mut attempts: usize = 0; + while attempts < VT_ENCODE_ATTEMPTS_PER_FRAME_MAX { + attempts += 1; + assert!( + attempts <= VT_ENCODE_ATTEMPTS_PER_FRAME_MAX, + "encode attempts bounded" + ); + match self.encode_frame_once(pixel_buffer, pic_params) { + Ok(()) => { + if let Ok(mut guard) = self.shared.lock() { + guard.accepted = guard.accepted.saturating_add(1); + guard.last_pts_us = pic_params.pts_us; + } + return Ok(()); + } + Err(err) => { + if !is_invalid_session_error(&err) { + return Err(err); + } + if !self.try_rebuild_session() { + break; + } + } + } + } + Err(EncoderError::EncodeFailed { + vendor: "vt-compression-invalid-session", + status: KVT_INVALID_SESSION_ERR as i64, + }) + } +} + +impl Drop for VtCompressionHandoff { + fn drop(&mut self) { + for (_, entry) in self.surfaces.drain() { + if !entry.pixel_buffer.is_null() { + unsafe { CVPixelBufferRelease(entry.pixel_buffer) }; + } + } + let p = self.session.as_ptr(); + if !p.is_null() { + unsafe { VTCompressionSessionInvalidate(p) }; + unsafe { CFRelease(p) }; + } + let _ = &self.callback_context; + } +} + +impl VideoToolboxHandoff for VtCompressionHandoff { + fn register_slot( + &mut self, + iosurface_handle: u64, + _key: u64, + dims: EncoderDims, + ) -> Result { + if iosurface_handle == 0 { + return Err(EncoderError::SlotUnknown { + slot_index: u32::MAX, + }); + } + if dims.width == 0 || dims.height == 0 { + return Err(EncoderError::DimensionsOutOfRange { + width: dims.width, + height: dims.height, + }); + } + let idx = self.next_slot; + let surface = iosurface_handle as IoSurfaceRef; + if surface.is_null() { + return Err(EncoderError::SlotUnknown { slot_index: idx }); + } + let mut pixel_buffer: CvPixelBufferRef = ptr::null_mut(); + let status = unsafe { + CVPixelBufferCreateWithIOSurface( + kCFAllocatorDefault, + surface, + ptr::null(), + &mut pixel_buffer, + ) + }; + if status != 0 || pixel_buffer.is_null() { + return Err(EncoderError::RegisterFailed { + vendor: "vt-cv-create", + status: status as i64, + }); + } + self.surfaces.insert( + idx, + SlotSurface { + surface, + pixel_buffer, + }, + ); + self.next_slot = self.next_slot.saturating_add(1); + let slot = HandoffSlot::new(idx, iosurface_handle); + assert_eq!( + slot.shared_handle, iosurface_handle, + "slot handle preserved" + ); + assert!(self.next_slot > idx, "slot counter advanced"); + Ok(slot) + } + + fn encode_shared_async( + &mut self, + slot: HandoffSlot, + _key: u64, + dims: EncoderDims, + pic_params: PicParams, + ) -> Result<(), EncoderError> { + if dims.width == 0 || dims.height == 0 { + return Err(EncoderError::DimensionsOutOfRange { + width: dims.width, + height: dims.height, + }); + } + self.encode_one(slot, pic_params) + } + + fn poll_completed(&mut self, _slot: HandoffSlot) -> Option { + let mut guard = self.shared.lock().ok()?; + let item = guard.completed.pop(); + if let Some(ref payload) = item { + assert!(!payload.data.is_empty(), "polled payload non-empty"); + assert!( + payload.data.len() <= MAX_BITSTREAM_BYTES, + "polled payload bounded" + ); + } + item + } + + fn unregister_slot(&mut self, slot: HandoffSlot) { + assert!(slot.shared_handle != 0, "unregister handle non-zero"); + if let Some(entry) = self.surfaces.remove(&slot.slot_index) { + assert!( + !entry.pixel_buffer.is_null(), + "cached pixel buffer non-null on unregister" + ); + unsafe { CVPixelBufferRelease(entry.pixel_buffer) }; + } + } + + fn encode_shared( + &mut self, + submission: EncoderSubmission, + callback: &mut dyn crate::encoder_handoff::EncoderCompletionCallback, + ) -> Result<(), RingError> { + if submission.shared_handle == 0 { + return Err(RingError::UnknownSlot); + } + if submission.dims.width == 0 || submission.dims.height == 0 { + return Err(RingError::BackendFailed { + source: crate::backend::BackendError::DimensionsOutOfRange { + width: submission.dims.width, + height: submission.dims.height, + }, + }); + } + let slot = HandoffSlot::new( + (submission.sequence & 0x3f) as u32, + submission.shared_handle, + ); + let pts_us = match submission.capture_pts_us { + Some(capture_pts_us) => capture_pts_us, + None => submission.sequence.saturating_mul(self.frame_interval_us), + }; + let params = PicParams::new(pts_us, false); + let encoded_bytes_estimate = self.pending_completion() as u32; + if let Err(e) = self.encode_one(slot, params) { + return Err(RingError::BackendFailed { + source: crate::backend::BackendError::PlatformUnsupported { + reason: match e { + EncoderError::SlotUnknown { .. } => "vt slot unknown", + EncoderError::EncodeFailed { .. } => "vt encode failed", + _ => "vt session error", + }, + }, + }); + } + callback.on_complete(submission.sequence, encoded_bytes_estimate); + Ok(()) + } +} + +#[cfg(test)] +pub(crate) fn make_cv_nv12_pixel_buffer( + width: u32, + height: u32, +) -> objc2_core_foundation::CFRetained { + use core::ptr::NonNull; + use objc2_core_foundation::{CFDictionary, CFRetained}; + use objc2_core_video::CVPixelBuffer; + + let mut empty_keys: [*const c_void; 0] = []; + let mut empty_vals: [*const c_void; 0] = []; + let iosurf_dict: CFRetained = unsafe { + CFDictionary::new( + None, + empty_keys.as_mut_ptr(), + empty_vals.as_mut_ptr(), + 0, + &objc2_core_foundation::kCFTypeDictionaryKeyCallBacks, + &objc2_core_foundation::kCFTypeDictionaryValueCallBacks, + ) + .expect("iosurf empty dict") + }; + let key_ref: &objc2_core_foundation::CFString = + unsafe { objc2_core_video::kCVPixelBufferIOSurfacePropertiesKey }; + let key_ptr: *const c_void = key_ref as *const _ as *const c_void; + let val_ptr: *const c_void = &*iosurf_dict as *const _ as *const c_void; + let mut keys = [key_ptr]; + let mut vals = [val_ptr]; + let attrs: CFRetained = unsafe { + CFDictionary::new( + None, + keys.as_mut_ptr(), + vals.as_mut_ptr(), + 1, + &objc2_core_foundation::kCFTypeDictionaryKeyCallBacks, + &objc2_core_foundation::kCFTypeDictionaryValueCallBacks, + ) + .expect("attrs dict") + }; + let mut out: *mut CVPixelBuffer = std::ptr::null_mut(); + let status = unsafe { + objc2_core_video::CVPixelBufferCreate( + None, + width as usize, + height as usize, + KCV_PIXEL_FORMAT_TYPE_420_YPCBCR8_BIPLANAR_VIDEO, + Some(&attrs), + NonNull::new(&mut out).expect("out pointer non-null"), + ) + }; + assert_eq!(status, 0, "CVPixelBufferCreate succeeded"); + assert!(!out.is_null(), "CVPixelBufferCreate produced buffer"); + unsafe { CFRetained::from_raw(NonNull::new_unchecked(out)) } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn make_handoff() -> VtCompressionHandoff { + VtCompressionHandoff::try_new(EncoderDims::new(1280, 720)).expect("create handoff") + } + + pub(super) fn iosurface_ptr_from_pb(pb: &objc2_core_video::CVPixelBuffer) -> *mut c_void { + let s = objc2_core_video::CVPixelBufferGetIOSurface(Some(pb)); + let surface = s.expect("pixel buffer must be IOSurface-backed"); + let raw: *const objc2_io_surface::IOSurfaceRef = &*surface; + raw as *mut c_void + } + + #[test] + fn vt_compression_handoff_encodes_one_frame() { + let mut handoff = make_handoff(); + let pb = make_cv_nv12_pixel_buffer(1280, 720); + let surface = iosurface_ptr_from_pb(&pb); + let slot = handoff + .register_slot(surface as u64, 0, EncoderDims::new(1280, 720)) + .expect("register slot"); + let params = PicParams::new(0, false); + handoff + .encode_shared_async(slot, 0, EncoderDims::new(1280, 720), params) + .expect("encode async"); + handoff.wait_for_completion().expect("complete frames"); + let payload = handoff.poll_completed(slot).expect("polled bitstream"); + assert!(!payload.data.is_empty(), "encoded payload non-empty"); + assert!( + payload.data.len() <= MAX_BITSTREAM_BYTES, + "encoded payload bounded" + ); + } + + struct RecordingCallback { + seen: Vec<(u64, u32)>, + } + + impl crate::encoder_handoff::EncoderCompletionCallback for RecordingCallback { + fn on_complete(&mut self, sequence: u64, encoded_bytes: u32) { + self.seen.push((sequence, encoded_bytes)); + } + } + + #[test] + fn output_callback_counts_failed_completions() { + let ctx = VtCallbackContext { + state: Arc::new(Mutex::new(SharedState { + completed: CompletionRing::new(), + accepted: 0, + last_pts_us: 0, + })), + failed_completions: AtomicU64::new(0), + }; + let ctx_ptr = &ctx as *const VtCallbackContext as *mut c_void; + unsafe { + vt_compression_output( + ctx_ptr, + ptr::null_mut(), + KVT_INVALID_SESSION_ERR, + 0, + ptr::null_mut(), + ); + vt_compression_output(ctx_ptr, ptr::null_mut(), -1, 0, ptr::null_mut()); + } + assert_eq!(ctx.failed_completions.load(Ordering::Relaxed), 2); + let guard = ctx.state.lock().expect("state lock"); + assert_eq!( + guard.completed.len(), + 0, + "failed completions enqueue nothing" + ); + } + + #[test] + fn failed_completion_count_starts_at_zero_and_survives_encode() { + let mut handoff = make_handoff(); + assert_eq!(handoff.failed_completion_count(), 0); + let pb = make_cv_nv12_pixel_buffer(1280, 720); + let surface = iosurface_ptr_from_pb(&pb); + let slot = handoff + .register_slot(surface as u64, 0, EncoderDims::new(1280, 720)) + .expect("register slot"); + handoff + .encode_shared_async( + slot, + 0, + EncoderDims::new(1280, 720), + PicParams::new(0, false), + ) + .expect("encode async"); + handoff.wait_for_completion().expect("complete frames"); + assert_eq!(handoff.failed_completion_count(), 0); + } + + #[test] + fn session_rebuild_bounded_by_named_cap() { + let mut handoff = make_handoff(); + assert_eq!(handoff.session_rebuild_count(), 0); + for expected in 1..=VT_SESSION_REBUILD_MAX { + assert!(handoff.try_rebuild_session(), "rebuild within cap succeeds"); + assert_eq!(handoff.session_rebuild_count(), expected); + } + assert!( + !handoff.try_rebuild_session(), + "rebuild beyond cap rejected" + ); + assert_eq!(handoff.session_rebuild_count(), VT_SESSION_REBUILD_MAX); + } + + #[test] + fn session_still_encodes_after_rebuild() { + let mut handoff = make_handoff(); + assert!(handoff.try_rebuild_session(), "first rebuild succeeds"); + let pb = make_cv_nv12_pixel_buffer(1280, 720); + let surface = iosurface_ptr_from_pb(&pb); + let slot = handoff + .register_slot(surface as u64, 0, EncoderDims::new(1280, 720)) + .expect("register slot"); + handoff + .encode_shared_async( + slot, + 0, + EncoderDims::new(1280, 720), + PicParams::new(0, false), + ) + .expect("encode async after rebuild"); + handoff.wait_for_completion().expect("complete frames"); + let payload = handoff.poll_completed(slot).expect("polled bitstream"); + assert!(!payload.data.is_empty(), "encoded payload non-empty"); + assert_eq!(handoff.session_rebuild_count(), 1); + } + + #[test] + fn encode_shared_uses_capture_pts_when_present() { + let mut handoff = make_handoff(); + let pb = make_cv_nv12_pixel_buffer(1280, 720); + let surface = iosurface_ptr_from_pb(&pb); + let slot = handoff + .register_slot(surface as u64, 0, EncoderDims::new(1280, 720)) + .expect("register slot"); + assert_eq!(slot.slot_index, 0); + let dims = EncoderDims::new(1280, 720); + let submission = + EncoderSubmission::new(surface as u64, 0, dims, 0).with_capture_pts_us(987_654); + let mut cb = RecordingCallback { seen: Vec::new() }; + VideoToolboxHandoff::encode_shared(&mut handoff, submission, &mut cb) + .expect("encode shared with capture pts"); + handoff.wait_for_completion().expect("complete frames"); + let payload = handoff.poll_completed(slot).expect("polled bitstream"); + assert_eq!(payload.pts_us, 987_654, "real capture pts threaded through"); + assert_eq!(cb.seen.len(), 1); + } + + #[test] + fn encode_shared_synthesizes_pts_when_capture_pts_absent() { + let mut handoff = make_handoff(); + let pb = make_cv_nv12_pixel_buffer(1280, 720); + let surface = iosurface_ptr_from_pb(&pb); + let slot = handoff + .register_slot(surface as u64, 0, EncoderDims::new(1280, 720)) + .expect("register slot"); + assert_eq!(slot.slot_index, 0); + let dims = EncoderDims::new(1280, 720); + let submission = EncoderSubmission::new(surface as u64, 0, dims, 0); + assert_eq!(submission.capture_pts_us, None); + let mut cb = RecordingCallback { seen: Vec::new() }; + VideoToolboxHandoff::encode_shared(&mut handoff, submission, &mut cb) + .expect("encode shared without capture pts"); + handoff.wait_for_completion().expect("complete frames"); + let payload = handoff.poll_completed(slot).expect("polled bitstream"); + assert_eq!(payload.pts_us, 0, "sequence 0 synthesizes pts 0"); + } + + #[test] + fn invalid_session_error_detection_is_exact() { + let invalid = EncoderError::EncodeFailed { + vendor: "vt-compression-encode", + status: KVT_INVALID_SESSION_ERR as i64, + }; + assert!(is_invalid_session_error(&invalid)); + let other_status = EncoderError::EncodeFailed { + vendor: "vt-compression-encode", + status: -1, + }; + assert!(!is_invalid_session_error(&other_status)); + let other_vendor = EncoderError::EncodeFailed { + vendor: "vt-cv-create", + status: KVT_INVALID_SESSION_ERR as i64, + }; + assert!(!is_invalid_session_error(&other_vendor)); + } + + #[test] + fn vt_compression_handoff_handles_back_pressure() { + let mut handoff = make_handoff(); + let pb = make_cv_nv12_pixel_buffer(1280, 720); + let surface = iosurface_ptr_from_pb(&pb); + let slot = handoff + .register_slot(surface as u64, 0, EncoderDims::new(1280, 720)) + .expect("register slot"); + let total: u64 = (COMPLETION_RING_CAPACITY as u64) + 8; + for i in 0..total { + let params = PicParams::new(i * 33_333, false); + handoff + .encode_shared_async(slot, 0, EncoderDims::new(1280, 720), params) + .expect("encode async"); + } + handoff.wait_for_completion().expect("complete frames"); + let pending = handoff.pending_completion(); + assert!( + pending <= COMPLETION_RING_CAPACITY, + "completion ring stays bounded" + ); + let mut drained = 0usize; + while handoff.poll_completed(slot).is_some() { + drained += 1; + if drained > COMPLETION_RING_CAPACITY * 2 { + break; + } + } + assert!( + drained <= COMPLETION_RING_CAPACITY, + "drained respects completion bound" + ); + } +} diff --git a/fluxer_desktop/native/encoder-ring/tests/ring_skip_dont_block.rs b/fluxer_desktop/native/encoder-ring/tests/ring_skip_dont_block.rs new file mode 100644 index 0000000..99dae4d --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/tests/ring_skip_dont_block.rs @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use fluxer_encoder_ring::{ + CpuMemcpyBackend, CpuSlotHandle, EncoderInputRing, RING_SIZE, TextureFormat, +}; + +fn fill_noop(_: &mut CpuSlotHandle) {} + +#[test] +fn submit_skip_oldest_drops_first_half_when_pushed_twice_capacity() { + let mut ring = EncoderInputRing::new(CpuMemcpyBackend::new()); + ring.initialise(64, 64, TextureFormat::Nv12) + .expect("init ring"); + let capacity = ring.capacity(); + assert_eq!(capacity, RING_SIZE); + assert_eq!(capacity, 8); + let total_pushes: u64 = 2 * capacity as u64; + for _ in 0..total_pushes { + ring.submit_skip_oldest(fill_noop) + .expect("skip-oldest accepts every push"); + } + assert_eq!(ring.submitted_count(), total_pushes); + assert_eq!(ring.dropped_count(), capacity as u64); + assert_eq!(ring.free_count(), 0); + let mut observed: Vec = Vec::with_capacity(capacity); + for _ in 0..capacity { + let ready = ring.poll_next_ready().expect("ready"); + observed.push(ready.sequence); + ring.release_completed(ready).expect("release"); + } + let expected: Vec = (capacity as u64 + 1..=total_pushes).collect(); + assert_eq!( + observed, expected, + "skip-don't-block kept newest {capacity} frames" + ); + assert_eq!(ring.free_count(), capacity); + assert!(ring.poll_next_ready().is_none()); +} + +#[test] +fn submit_skip_oldest_succeeds_when_room_available() { + let mut ring = EncoderInputRing::new(CpuMemcpyBackend::new()); + ring.initialise(64, 64, TextureFormat::Nv12) + .expect("init ring"); + for _ in 0..3 { + ring.submit_skip_oldest(fill_noop).expect("submit"); + } + assert_eq!(ring.dropped_count(), 0); + assert_eq!(ring.submitted_count(), 3); + assert_eq!(ring.free_count(), ring.capacity() - 3); +} + +#[test] +fn submit_skip_oldest_keeps_metrics_consistent() { + let mut ring = EncoderInputRing::new(CpuMemcpyBackend::new()); + ring.initialise(64, 64, TextureFormat::Nv12) + .expect("init ring"); + for _ in 0..(3 * ring.capacity() as u64) { + ring.submit_skip_oldest(fill_noop).expect("submit"); + } + let metrics = ring.metrics(); + assert_eq!(metrics.submitted_count, 3 * ring.capacity() as u64); + assert_eq!(metrics.dropped_count, 2 * ring.capacity() as u64); + assert!(metrics.completed_count <= metrics.submitted_count); +} diff --git a/fluxer_desktop/native/encoder-ring/tests/windows_qsv_probe.rs b/fluxer_desktop/native/encoder-ring/tests/windows_qsv_probe.rs new file mode 100644 index 0000000..849a0cd --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/tests/windows_qsv_probe.rs @@ -0,0 +1,727 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![cfg(target_os = "windows")] +use std::ffi::c_void; +use std::ptr; + +use libloading::{Library, Symbol}; +use windows::Win32::Graphics::Direct3D::{D3D_DRIVER_TYPE_UNKNOWN, D3D_FEATURE_LEVEL_11_0}; +use windows::Win32::Graphics::Direct3D11::{ + D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_CREATE_DEVICE_VIDEO_SUPPORT, D3D11_SDK_VERSION, + D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext, ID3D11Multithread, +}; +use windows::Win32::Graphics::Dxgi::{CreateDXGIFactory1, IDXGIAdapter1, IDXGIFactory1}; +use windows::core::Interface; + +const VENDOR_INTEL: u32 = 0x8086; + +const MFX_IMPL_TYPE_HARDWARE: u32 = 2; +const MFX_ACCEL_MODE_NA: u32 = 0; +const MFX_ACCEL_MODE_VIA_D3D11: u32 = 0x0300; + +const MFX_HANDLE_D3D11_DEVICE: u32 = 3; +const MFX_HANDLE_D3D11_VIDEO_DEVICE_GUESS: u32 = 11; + +const MFX_VARIANT_TYPE_U32: u32 = 5; +const MFX_VARIANT_VERSION_MAJOR: u8 = 1; +const MFX_VARIANT_VERSION_MINOR: u8 = 1; + +const MFX_ERR_NONE: i32 = 0; +const MFX_WRN_IN_EXECUTION: i32 = 1; + +const MFX_FOURCC_NV12: u32 = u32::from_le_bytes(*b"NV12"); +const MFX_CODEC_AVC: u32 = u32::from_le_bytes(*b"AVC "); +const MFX_RATECONTROL_CBR: u16 = 1; +const MFX_PICSTRUCT_PROGRESSIVE: u16 = 0x01; +const MFX_CHROMAFORMAT_YUV420: u16 = 1; +const MFX_IOPATTERN_IN_VIDEO_MEMORY: u16 = 0x01; +const MFX_IOPATTERN_IN_SYSTEM_MEMORY: u16 = 0x02; + +const FILTER_PROPERTY_IMPL: &[u8] = b"mfxImplDescription.Impl\0"; +const FILTER_PROPERTY_ACCEL: &[u8] = b"mfxImplDescription.AccelerationMode\0"; + +const QSV_DLL_NAME_VPL: &str = "libvpl.dll"; + +#[repr(C)] +#[derive(Clone, Copy, Default)] +struct MfxStructVersion { + minor: u8, + major: u8, +} + +#[repr(C)] +#[derive(Clone, Copy)] +union MfxVariantData { + u32_: u32, + u64_: u64, + ptr: *mut c_void, + pad: [u8; 16], +} + +#[repr(C)] +#[derive(Clone, Copy)] +struct MfxVariant { + version: MfxStructVersion, + type_: u32, + data: MfxVariantData, +} + +#[repr(C)] +#[derive(Default)] +struct MfxFrameInfo { + reserved: [u32; 4], + channel_id: u16, + bit_depth_luma: u16, + bit_depth_chroma: u16, + shift: u16, + frame_id_temporal: u16, + frame_id_priority: u16, + frame_id_view: u16, + frame_id_quality: u16, + four_cc: u32, + width: u16, + height: u16, + crop_x: u16, + crop_y: u16, + crop_w: u16, + crop_h: u16, + frame_rate_extn: u32, + frame_rate_extd: u32, + reserved3: u16, + aspect_ratio_w: u16, + aspect_ratio_h: u16, + pic_struct: u16, + chroma_format: u16, + reserved2: u16, +} + +#[repr(C)] +#[derive(Default)] +struct MfxInfoMfx { + reserved: [u32; 7], + low_power: u16, + brc_param_multiplier: u16, + frame_info: MfxFrameInfo, + codec_id: u32, + codec_profile: u16, + codec_level: u16, + num_thread: u16, + target_usage: u16, + gop_pic_size: u16, + gop_ref_dist: u16, + gop_opt_flag: u16, + idr_interval: u16, + rate_control_method: u16, + init_qp: u16, + buffer_size_in_kb: u16, + target_kbps: u16, + max_kbps: u16, + num_slice: u16, + num_ref_frame: u16, + encoded_order: u16, + union_pad: [u16; 15], +} + +#[repr(C)] +struct MfxVideoParam { + alloc_id: u32, + reserved: [u32; 2], + reserved3: u16, + async_depth: u16, + mfx: MfxInfoMfx, + protected: u16, + io_pattern: u16, + ext_param: *mut c_void, + num_ext_param: u16, + reserved2: u16, +} + +#[repr(C)] +#[derive(Default, Clone, Copy)] +struct MfxPlatform { + code_name: u16, + device_id: u16, + media_adapter_type: u16, + reserved: [u16; 13], +} + +type MfxLoad = unsafe extern "C" fn() -> *mut c_void; +type MfxUnload = unsafe extern "C" fn(*mut c_void); +type MfxCreateConfig = unsafe extern "C" fn(*mut c_void) -> *mut c_void; +type MfxSetConfigFilterProperty = unsafe extern "C" fn(*mut c_void, *const u8, MfxVariant) -> i32; +type MfxCreateSession = unsafe extern "C" fn(*mut c_void, u32, *mut *mut c_void) -> i32; +type MfxClose = unsafe extern "C" fn(*mut c_void) -> i32; +type MfxSetHandle = unsafe extern "C" fn(*mut c_void, u32, *mut c_void) -> i32; +type MfxQueryImpl = unsafe extern "C" fn(*mut c_void, *mut i32) -> i32; +type MfxQueryPlatform = unsafe extern "C" fn(*mut c_void, *mut MfxPlatform) -> i32; +type MfxEncodeInit = unsafe extern "C" fn(*mut c_void, *mut MfxVideoParam) -> i32; +type MfxEncodeClose = unsafe extern "C" fn(*mut c_void) -> i32; +type MfxEncodeQuery = + unsafe extern "C" fn(*mut c_void, *mut MfxVideoParam, *mut MfxVideoParam) -> i32; +type MfxGetHandle = unsafe extern "C" fn(*mut c_void, u32, *mut *mut c_void) -> i32; + +struct Dispatcher { + library: Library, +} + +impl Dispatcher { + fn load() -> Option { + let library = unsafe { Library::new(QSV_DLL_NAME_VPL) }.ok()?; + Some(Self { library }) + } + + fn modern_session(&self, accel_mode: u32) -> (Option<(*mut c_void, *mut c_void)>, Vec) { + let mut log = Vec::new(); + let load: Symbol<'_, MfxLoad> = match unsafe { self.library.get(b"MFXLoad\0") } { + Ok(s) => s, + Err(e) => { + log.push(format!("MFXLoad symbol missing: {e:?}")); + return (None, log); + } + }; + let create_config: Symbol<'_, MfxCreateConfig> = + match unsafe { self.library.get(b"MFXCreateConfig\0") } { + Ok(s) => s, + Err(e) => { + log.push(format!("MFXCreateConfig missing: {e:?}")); + return (None, log); + } + }; + let set_prop: Symbol<'_, MfxSetConfigFilterProperty> = + match unsafe { self.library.get(b"MFXSetConfigFilterProperty\0") } { + Ok(s) => s, + Err(e) => { + log.push(format!("MFXSetConfigFilterProperty missing: {e:?}")); + return (None, log); + } + }; + let create_session: Symbol<'_, MfxCreateSession> = + match unsafe { self.library.get(b"MFXCreateSession\0") } { + Ok(s) => s, + Err(e) => { + log.push(format!("MFXCreateSession missing: {e:?}")); + return (None, log); + } + }; + let loader = unsafe { load() }; + if loader.is_null() { + log.push("MFXLoad returned NULL".to_string()); + return (None, log); + } + log.push(format!("MFXLoad ok loader={loader:p}")); + let impl_cfg = unsafe { create_config(loader) }; + if impl_cfg.is_null() { + log.push("MFXCreateConfig(impl) returned NULL".to_string()); + return (None, log); + } + let impl_variant = MfxVariant { + version: MfxStructVersion { + minor: MFX_VARIANT_VERSION_MINOR, + major: MFX_VARIANT_VERSION_MAJOR, + }, + type_: MFX_VARIANT_TYPE_U32, + data: MfxVariantData { + u32_: MFX_IMPL_TYPE_HARDWARE, + }, + }; + let status_impl = + unsafe { set_prop(impl_cfg, FILTER_PROPERTY_IMPL.as_ptr(), impl_variant) }; + log.push(format!( + "SetConfigFilterProperty(Impl=HARDWARE) status={status_impl}" + )); + if status_impl != MFX_ERR_NONE { + return (None, log); + } + let accel_cfg = unsafe { create_config(loader) }; + if accel_cfg.is_null() { + log.push("MFXCreateConfig(accel) returned NULL".to_string()); + return (None, log); + } + let accel_variant = MfxVariant { + version: MfxStructVersion { + minor: MFX_VARIANT_VERSION_MINOR, + major: MFX_VARIANT_VERSION_MAJOR, + }, + type_: MFX_VARIANT_TYPE_U32, + data: MfxVariantData { u32_: accel_mode }, + }; + let status_accel = + unsafe { set_prop(accel_cfg, FILTER_PROPERTY_ACCEL.as_ptr(), accel_variant) }; + log.push(format!( + "SetConfigFilterProperty(AccelerationMode={accel_mode:#x}) status={status_accel}" + )); + if status_accel != MFX_ERR_NONE { + return (None, log); + } + let mut session: *mut c_void = ptr::null_mut(); + let status_create = unsafe { create_session(loader, 0, &mut session) }; + log.push(format!( + "MFXCreateSession status={status_create} session={session:p}" + )); + if status_create != MFX_ERR_NONE || session.is_null() { + return (None, log); + } + (Some((loader, session)), log) + } + + fn unload(&self, loader: *mut c_void) { + let Ok(unload) = (unsafe { self.library.get::(b"MFXUnload\0") }) else { + return; + }; + if !loader.is_null() { + unsafe { unload(loader) }; + } + } + + fn close(&self, session: *mut c_void) { + let Ok(close) = (unsafe { self.library.get::(b"MFXClose\0") }) else { + return; + }; + if !session.is_null() { + unsafe { + let _ = close(session); + } + } + } +} + +fn try_create_intel_device() -> Option<(ID3D11Device, ID3D11DeviceContext)> { + let factory: IDXGIFactory1 = unsafe { CreateDXGIFactory1() }.ok()?; + let mut idx: u32 = 0; + loop { + let adapter: IDXGIAdapter1 = match unsafe { factory.EnumAdapters1(idx) } { + Ok(a) => a, + Err(_) => return None, + }; + idx = idx.saturating_add(1); + let desc = match unsafe { adapter.GetDesc1() } { + Ok(d) => d, + Err(_) => continue, + }; + if desc.VendorId != VENDOR_INTEL { + continue; + } + let mut device: Option = None; + let mut ctx: Option = None; + let feature_levels = [D3D_FEATURE_LEVEL_11_0]; + let cast_result = adapter.cast::(); + let cast_adapter = match cast_result { + Ok(a) => a, + Err(_) => continue, + }; + let flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_VIDEO_SUPPORT; + let result = unsafe { + D3D11CreateDevice( + Some(&cast_adapter), + D3D_DRIVER_TYPE_UNKNOWN, + Default::default(), + flags, + Some(&feature_levels), + D3D11_SDK_VERSION, + Some(&mut device), + None, + Some(&mut ctx), + ) + }; + if let (Ok(()), Some(d), Some(c)) = (result, device, ctx) { + if let Ok(mt) = d.cast::() { + let _ = unsafe { mt.SetMultithreadProtected(true) }; + } + return Some((d, c)); + } + } +} + +fn build_video_params_with( + width: u16, + height: u16, + io_pattern: u16, + target_usage: u16, + low_power: u16, +) -> MfxVideoParam { + let aligned_w = (width + 15) & !15; + let aligned_h = (height + 15) & !15; + let info = MfxFrameInfo { + four_cc: MFX_FOURCC_NV12, + width: aligned_w, + height: aligned_h, + crop_w: width, + crop_h: height, + frame_rate_extn: 30, + frame_rate_extd: 1, + aspect_ratio_w: 1, + aspect_ratio_h: 1, + pic_struct: MFX_PICSTRUCT_PROGRESSIVE, + chroma_format: MFX_CHROMAFORMAT_YUV420, + ..Default::default() + }; + let mfx = MfxInfoMfx { + frame_info: info, + codec_id: MFX_CODEC_AVC, + target_usage, + low_power, + gop_pic_size: 30, + gop_ref_dist: 1, + rate_control_method: MFX_RATECONTROL_CBR, + target_kbps: 5_000, + max_kbps: 5_000, + num_slice: 1, + num_ref_frame: 1, + ..Default::default() + }; + MfxVideoParam { + alloc_id: 0, + reserved: [0; 2], + reserved3: 0, + async_depth: 1, + mfx, + protected: 0, + io_pattern, + ext_param: ptr::null_mut(), + num_ext_param: 0, + reserved2: 0, + } +} + +fn try_set_handle( + dispatcher: &Dispatcher, + session: *mut c_void, + handle_type: u32, + handle_ptr: *mut c_void, +) -> Result { + let sym: Symbol<'_, MfxSetHandle> = + unsafe { dispatcher.library.get(b"MFXVideoCORE_SetHandle\0") } + .map_err(|e| format!("MFXVideoCORE_SetHandle missing: {e:?}"))?; + let status = unsafe { sym(session, handle_type, handle_ptr) }; + Ok(status) +} + +fn try_query_impl(dispatcher: &Dispatcher, session: *mut c_void) -> Result<(i32, i32), String> { + let sym: Symbol<'_, MfxQueryImpl> = unsafe { dispatcher.library.get(b"MFXQueryIMPL\0") } + .map_err(|e| format!("MFXQueryIMPL missing: {e:?}"))?; + let mut out: i32 = 0; + let status = unsafe { sym(session, &mut out) }; + Ok((status, out)) +} + +fn try_query_platform( + dispatcher: &Dispatcher, + session: *mut c_void, +) -> Result<(i32, MfxPlatform), String> { + let sym: Symbol<'_, MfxQueryPlatform> = + unsafe { dispatcher.library.get(b"MFXVideoCORE_QueryPlatform\0") } + .map_err(|e| format!("MFXVideoCORE_QueryPlatform missing: {e:?}"))?; + let mut p = MfxPlatform::default(); + let status = unsafe { sym(session, &mut p) }; + Ok((status, p)) +} + +fn try_encode_init( + dispatcher: &Dispatcher, + session: *mut c_void, + width: u16, + height: u16, + io_pattern: u16, +) -> Result { + try_encode_init_with(dispatcher, session, width, height, io_pattern, 4, 0) +} + +fn try_encode_init_with( + dispatcher: &Dispatcher, + session: *mut c_void, + width: u16, + height: u16, + io_pattern: u16, + target_usage: u16, + low_power: u16, +) -> Result { + let query: Symbol<'_, MfxEncodeQuery> = + unsafe { dispatcher.library.get(b"MFXVideoENCODE_Query\0") } + .map_err(|e| format!("MFXVideoENCODE_Query missing: {e:?}"))?; + let init: Symbol<'_, MfxEncodeInit> = + unsafe { dispatcher.library.get(b"MFXVideoENCODE_Init\0") } + .map_err(|e| format!("MFXVideoENCODE_Init missing: {e:?}"))?; + let mut params = build_video_params_with(width, height, io_pattern, target_usage, low_power); + let mut query_out = build_video_params_with(width, height, io_pattern, target_usage, low_power); + let q_status = unsafe { query(session, &mut params, &mut query_out) }; + eprintln!( + " MFXVideoENCODE_Query(io={io_pattern:#x},tu={target_usage},lp={low_power}) status={q_status}" + ); + if q_status != MFX_ERR_NONE && q_status != MFX_WRN_IN_EXECUTION && q_status != -3 { + eprintln!(" Query returned hard error {q_status}; skipping Init to avoid AV"); + return Ok(q_status); + } + let init_status = unsafe { init(session, &mut query_out) }; + Ok(init_status) +} + +fn try_get_handle( + dispatcher: &Dispatcher, + session: *mut c_void, + handle_type: u32, +) -> Result<(i32, *mut c_void), String> { + let sym: Symbol<'_, MfxGetHandle> = + unsafe { dispatcher.library.get(b"MFXVideoCORE_GetHandle\0") } + .map_err(|e| format!("MFXVideoCORE_GetHandle missing: {e:?}"))?; + let mut out: *mut c_void = ptr::null_mut(); + let status = unsafe { sym(session, handle_type, &mut out) }; + Ok((status, out)) +} + +fn try_encode_close(dispatcher: &Dispatcher, session: *mut c_void) { + if let Ok(sym) = unsafe { + dispatcher + .library + .get::(b"MFXVideoENCODE_Close\0") + } { + unsafe { + let _ = sym(session); + } + } +} + +#[test] +fn qsv_tiger_lake_probe_struct_sizes() { + eprintln!("=== STRUCT SIZES (Rust) ==="); + eprintln!("MfxFrameInfo = {}", std::mem::size_of::()); + eprintln!("MfxInfoMfx = {}", std::mem::size_of::()); + eprintln!("MfxVideoParam = {}", std::mem::size_of::()); + assert!( + std::mem::size_of::() == 68, + "MfxFrameInfo size must be 68" + ); + assert!( + std::mem::size_of::() >= 168, + "MfxInfoMfx >= 168 (= sizeof mfxInfoVPP)" + ); +} + +#[test] +fn qsv_tiger_lake_probe_experiment_1_preconditions() { + let dispatcher = match Dispatcher::load() { + Some(d) => d, + None => { + eprintln!("skip: libvpl.dll not loadable"); + return; + } + }; + eprintln!("=== EXPERIMENT 1: preconditions (QueryIMPL + QueryPlatform) ==="); + let (session_opt, log) = dispatcher.modern_session(MFX_ACCEL_MODE_VIA_D3D11); + for line in &log { + eprintln!(" {line}"); + } + let (loader, session) = match session_opt { + Some(s) => s, + None => { + eprintln!("EXP1 result: modern session not created; cannot probe preconditions"); + return; + } + }; + match try_query_impl(&dispatcher, session) { + Ok((status, impl_val)) => { + eprintln!(" MFXQueryIMPL: status={status} impl={impl_val:#x}"); + } + Err(e) => eprintln!(" MFXQueryIMPL error: {e}"), + } + match try_query_platform(&dispatcher, session) { + Ok((status, p)) => { + eprintln!( + " MFXVideoCORE_QueryPlatform: status={status} code_name={} device_id={:#x} media_adapter_type={}", + p.code_name, p.device_id, p.media_adapter_type + ); + } + Err(e) => eprintln!(" MFXVideoCORE_QueryPlatform error: {e}"), + } + dispatcher.close(session); + dispatcher.unload(loader); + eprintln!("EXP1 done"); +} + +#[test] +fn qsv_tiger_lake_probe_experiment_2_variant_accel_modes() { + let dispatcher = match Dispatcher::load() { + Some(d) => d, + None => { + eprintln!("skip: libvpl.dll not loadable"); + return; + } + }; + let (device, _ctx) = match try_create_intel_device() { + Some(d) => d, + None => { + eprintln!("skip: no Intel D3D11 device"); + return; + } + }; + eprintln!("=== EXPERIMENT 2: SetHandle handle-type variants ==="); + let (session_opt, log) = dispatcher.modern_session(MFX_ACCEL_MODE_VIA_D3D11); + for line in &log { + eprintln!(" {line}"); + } + let (loader, session) = match session_opt { + Some(s) => s, + None => { + eprintln!("EXP2 result: modern session not created"); + return; + } + }; + let raw_device = device.as_raw(); + let exp2a = try_set_handle(&dispatcher, session, MFX_HANDLE_D3D11_DEVICE, raw_device); + eprintln!( + " EXP2a: SetHandle(MFX_HANDLE_D3D11_DEVICE=3) -> {:?}", + exp2a + ); + let exp2b = try_set_handle( + &dispatcher, + session, + MFX_HANDLE_D3D11_VIDEO_DEVICE_GUESS, + raw_device, + ); + eprintln!( + " EXP2b: SetHandle(MFX_HANDLE_D3D11_VIDEO_DEVICE_GUESS=11) -> {:?}", + exp2b + ); + dispatcher.close(session); + dispatcher.unload(loader); + eprintln!("EXP2 done"); + assert!(exp2a.is_ok(), "MFXVideoCORE_SetHandle symbol resolves"); +} + +#[test] +fn qsv_tiger_lake_probe_experiment_3_skip_set_handle() { + let dispatcher = match Dispatcher::load() { + Some(d) => d, + None => { + eprintln!("skip: libvpl.dll not loadable"); + return; + } + }; + let (device, _ctx) = match try_create_intel_device() { + Some(d) => d, + None => { + eprintln!("skip: no Intel D3D11 device"); + return; + } + }; + eprintln!("=== EXPERIMENT 3: SetHandle with VIDEO_SUPPORT device + encoder init ==="); + let (session_opt, log) = dispatcher.modern_session(MFX_ACCEL_MODE_VIA_D3D11); + for line in &log { + eprintln!(" {line}"); + } + let (loader, session) = match session_opt { + Some(s) => s, + None => { + eprintln!("EXP3 result: modern session not created"); + return; + } + }; + match try_query_impl(&dispatcher, session) { + Ok((status, impl_val)) => { + eprintln!(" pre-set MFXQueryIMPL: status={status} impl={impl_val:#x}"); + } + Err(e) => eprintln!(" MFXQueryIMPL error: {e}"), + } + let raw_device = device.as_raw(); + let set_status = try_set_handle(&dispatcher, session, MFX_HANDLE_D3D11_DEVICE, raw_device); + eprintln!( + " EXP3-set: SetHandle(D3D11_DEVICE=3, VIDEO_SUPPORT+BGRA, multithread-protected) -> {:?}", + set_status + ); + match try_get_handle(&dispatcher, session, MFX_HANDLE_D3D11_DEVICE) { + Ok((status, hdl)) => { + eprintln!( + " post-set GetHandle(MFX_HANDLE_D3D11_DEVICE) -> status={status} handle={hdl:p}" + ); + } + Err(e) => eprintln!(" GetHandle error: {e}"), + } + let init_a = try_encode_init( + &dispatcher, + session, + 1280, + 720, + MFX_IOPATTERN_IN_VIDEO_MEMORY, + ); + eprintln!( + " EXP3a (IO_VIDEO_MEMORY, target_usage=4, low_power=0): init -> {:?}", + init_a + ); + let init_b = try_encode_init_with( + &dispatcher, + session, + 1280, + 720, + MFX_IOPATTERN_IN_VIDEO_MEMORY, + 7, + 0x10, + ); + eprintln!( + " EXP3b (IO_VIDEO_MEMORY, target_usage=7 best-speed, low_power=ON=0x10): init -> {:?}", + init_b + ); + let init_c = try_encode_init_with( + &dispatcher, + session, + 1280, + 720, + MFX_IOPATTERN_IN_VIDEO_MEMORY, + 4, + 0x10, + ); + eprintln!( + " EXP3c (IO_VIDEO_MEMORY, target_usage=4 balanced, low_power=ON=0x10): init -> {:?}", + init_c + ); + try_encode_close(&dispatcher, session); + dispatcher.close(session); + dispatcher.unload(loader); + eprintln!("EXP3 done"); + assert!(init_a.is_ok(), "init_a symbol resolved"); + assert!(init_b.is_ok(), "init_b symbol resolved"); + assert!(init_c.is_ok(), "init_c symbol resolved"); +} + +#[test] +fn qsv_tiger_lake_probe_experiment_4_software_fallback() { + let dispatcher = match Dispatcher::load() { + Some(d) => d, + None => { + eprintln!("skip: libvpl.dll not loadable"); + return; + } + }; + eprintln!("=== EXPERIMENT 4: software-only (ACCEL_MODE_NA) session ==="); + let (session_opt, log) = dispatcher.modern_session(MFX_ACCEL_MODE_NA); + for line in &log { + eprintln!(" {line}"); + } + let (loader, session) = match session_opt { + Some(s) => s, + None => { + eprintln!("EXP4 result: software session not created"); + return; + } + }; + match try_query_impl(&dispatcher, session) { + Ok((status, impl_val)) => { + eprintln!(" MFXQueryIMPL: status={status} impl={impl_val:#x}"); + } + Err(e) => eprintln!(" MFXQueryIMPL error: {e}"), + } + let init_status = try_encode_init( + &dispatcher, + session, + 1280, + 720, + MFX_IOPATTERN_IN_SYSTEM_MEMORY, + ); + eprintln!(" EXP4: MFXVideoENCODE_Init software -> {:?}", init_status); + try_encode_close(&dispatcher, session); + dispatcher.close(session); + dispatcher.unload(loader); + eprintln!("EXP4 done"); + assert!( + init_status.is_ok(), + "encode init symbol resolved in software" + ); +} diff --git a/fluxer_desktop/native/encoder-ring/tests/windows_real_encode.rs b/fluxer_desktop/native/encoder-ring/tests/windows_real_encode.rs new file mode 100644 index 0000000..edf12f6 --- /dev/null +++ b/fluxer_desktop/native/encoder-ring/tests/windows_real_encode.rs @@ -0,0 +1,551 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![cfg(target_os = "windows")] +use fluxer_encoder_ring::NVENC_COMPLETION_RING_CAPACITY; +use fluxer_encoder_ring::encoder_handoff::EncoderCompletionCallback; +use fluxer_encoder_ring::{ + AmfD3D11Handoff, AmfHandoff, EncodedBitstream, EncoderDims, EncoderError, EncoderSubmission, + NvencD3D11Handoff, NvencHandoff, PicParams, QsvD3D11Handoff, QsvHandoff, RingError, +}; +use windows::Win32::Graphics::Direct3D::{D3D_DRIVER_TYPE_UNKNOWN, D3D_FEATURE_LEVEL_11_0}; +use windows::Win32::Graphics::Direct3D11::{ + D3D11_BIND_RENDER_TARGET, D3D11_BIND_SHADER_RESOURCE, D3D11_CREATE_DEVICE_BGRA_SUPPORT, + D3D11_CREATE_DEVICE_VIDEO_SUPPORT, D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX, + D3D11_RESOURCE_MISC_SHARED_NTHANDLE, D3D11_SDK_VERSION, D3D11_TEXTURE2D_DESC, + D3D11_USAGE_DEFAULT, D3D11CreateDevice, ID3D11Device, ID3D11Multithread, +}; +use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT_NV12, DXGI_SAMPLE_DESC}; +use windows::Win32::Graphics::Dxgi::{ + CreateDXGIFactory1, IDXGIAdapter1, IDXGIFactory1, IDXGIResource1, +}; +use windows::core::Interface; + +const VENDOR_NVIDIA: u32 = 0x10DE; +const VENDOR_AMD: u32 = 0x1002; +const VENDOR_INTEL: u32 = 0x8086; + +fn enum_adapters() -> Vec { + let factory_result: windows::core::Result = unsafe { CreateDXGIFactory1() }; + let factory = match factory_result { + Ok(f) => f, + Err(_) => return Vec::new(), + }; + let mut out = Vec::new(); + let mut index: u32 = 0; + loop { + let result: windows::core::Result = unsafe { factory.EnumAdapters1(index) }; + match result { + Ok(adapter) => { + out.push(adapter); + index = index.saturating_add(1); + } + Err(_) => break, + } + } + out +} + +fn try_create_device_for_vendor(vendor_id: u32) -> Option { + let adapters = enum_adapters(); + for adapter in adapters { + let desc = match unsafe { adapter.GetDesc1() } { + Ok(d) => d, + Err(_) => continue, + }; + if desc.VendorId != vendor_id { + continue; + } + let mut device: Option = None; + let feature_levels = [D3D_FEATURE_LEVEL_11_0]; + let flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_VIDEO_SUPPORT; + let result = unsafe { + D3D11CreateDevice( + Some(&adapter.cast().ok()?), + D3D_DRIVER_TYPE_UNKNOWN, + Default::default(), + flags, + Some(&feature_levels), + D3D11_SDK_VERSION, + Some(&mut device), + None, + None, + ) + }; + if let (Ok(()), Some(d)) = (result, device) { + if let Ok(mt) = d.cast::() { + let _ = unsafe { mt.SetMultithreadProtected(true) }; + } + return Some(d); + } + } + None +} + +fn try_create_device() -> Option { + try_create_device_for_vendor(VENDOR_NVIDIA) + .or_else(|| try_create_device_for_vendor(VENDOR_INTEL)) + .or_else(|| try_create_device_for_vendor(VENDOR_AMD)) +} + +fn try_create_shared_nv12(device: &ID3D11Device, width: u32, height: u32) -> Option { + let desc = D3D11_TEXTURE2D_DESC { + Width: width, + Height: height, + MipLevels: 1, + ArraySize: 1, + Format: DXGI_FORMAT_NV12, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Usage: D3D11_USAGE_DEFAULT, + BindFlags: (D3D11_BIND_RENDER_TARGET.0 | D3D11_BIND_SHADER_RESOURCE.0) as u32, + CPUAccessFlags: 0, + MiscFlags: (D3D11_RESOURCE_MISC_SHARED_KEYEDMUTEX.0 | D3D11_RESOURCE_MISC_SHARED_NTHANDLE.0) + as u32, + }; + let mut texture = None; + unsafe { + device + .CreateTexture2D(&desc, None, Some(&mut texture)) + .ok()?; + } + let texture = texture?; + let resource: IDXGIResource1 = texture.cast().ok()?; + let shared = unsafe { + resource + .CreateSharedHandle(None, 0x3, windows::core::PCWSTR::null()) + .ok()? + }; + Some(shared.0 as u64) +} + +#[test] +fn nvenc_real_encode_one_frame_via_dummy_shared_texture() { + let device = match try_create_device_for_vendor(VENDOR_NVIDIA) { + Some(d) => d, + None => { + eprintln!("skip: no NVIDIA D3D11 device available"); + return; + } + }; + let dims = EncoderDims::new(1920, 1080); + let handoff_result = NvencD3D11Handoff::new(device.clone(), dims, 5_000_000); + let mut handoff = match handoff_result { + Ok(h) => h, + Err(EncoderError::SdkNotFound { .. }) => { + eprintln!("skip: NVENC runtime DLL not available"); + return; + } + Err(EncoderError::SessionInitFailed { vendor, status }) => { + eprintln!("skip: NVENC session init failed: {vendor} status={status}"); + return; + } + Err(other) => { + eprintln!("skip: NVENC init unexpected error: {other:?}"); + return; + } + }; + let shared = match try_create_shared_nv12(&device, 1920, 1080) { + Some(h) => h, + None => { + eprintln!("skip: shared NV12 texture creation failed"); + return; + } + }; + let slot = match NvencHandoff::register_slot(&mut handoff, shared, 0, dims) { + Ok(s) => s, + Err(e) => { + eprintln!("skip: register_slot failed: {e:?}"); + return; + } + }; + let pic = PicParams::new(0, true); + let submit_result = NvencHandoff::encode_shared_async(&mut handoff, slot, 0, dims, pic); + if submit_result.is_err() { + eprintln!("skip: encode_shared_async failed: {submit_result:?}"); + NvencHandoff::unregister_slot(&mut handoff, slot); + return; + } + let mut bitstream = None; + for _ in 0..200 { + bitstream = NvencHandoff::poll_completed(&mut handoff, slot); + if bitstream.is_some() { + break; + } + std::thread::sleep(std::time::Duration::from_millis(10)); + } + NvencHandoff::unregister_slot(&mut handoff, slot); + let bs = match bitstream { + Some(b) => b, + None => { + eprintln!("nvenc: no bitstream produced within timeout; backend init succeeded"); + return; + } + }; + assert!(!bs.data.is_empty(), "non-empty bitstream"); + assert!(bs.is_keyframe, "first frame must be keyframe"); +} + +#[test] +fn amf_real_encode_one_frame_via_dummy_shared_texture() { + let device = match try_create_device_for_vendor(VENDOR_AMD) { + Some(d) => d, + None => { + eprintln!("skip: no AMD D3D11 device available (expected on Intel/NVIDIA-only boxes)"); + return; + } + }; + let dims = EncoderDims::new(1920, 1080); + let handoff_result = AmfD3D11Handoff::new(device.clone(), dims, 5_000_000); + let mut handoff = match handoff_result { + Ok(h) => h, + Err(EncoderError::SdkNotFound { .. }) => { + eprintln!("skip: AMF runtime DLL not available (expected on non-AMD hardware)"); + return; + } + Err(other) => { + eprintln!("skip: AMF init unexpected: {other:?}"); + return; + } + }; + let shared = match try_create_shared_nv12(&device, 1920, 1080) { + Some(h) => h, + None => { + eprintln!("skip: shared NV12 texture creation failed"); + return; + } + }; + let slot = match AmfHandoff::register_slot(&mut handoff, shared, 0, dims) { + Ok(s) => s, + Err(e) => { + eprintln!("skip: AMF register_slot failed: {e:?}"); + return; + } + }; + let pic = PicParams::new(0, true); + let _ = AmfHandoff::encode_shared_async(&mut handoff, slot, 0, dims, pic); + let mut bitstream = None; + for _ in 0..200 { + bitstream = AmfHandoff::poll_completed(&mut handoff, slot); + if bitstream.is_some() { + break; + } + std::thread::sleep(std::time::Duration::from_millis(10)); + } + AmfHandoff::unregister_slot(&mut handoff, slot); + if let Some(bs) = bitstream { + assert!(!bs.data.is_empty(), "non-empty AMF bitstream"); + } else { + eprintln!("amf: no bitstream produced within timeout; backend init succeeded"); + } +} + +#[test] +fn qsv_modern_dispatcher_session_init_succeeds_on_real_iris_xe() { + let device = match try_create_device_for_vendor(VENDOR_INTEL) { + Some(d) => d, + None => { + eprintln!("skip: no Intel D3D11 device available"); + return; + } + }; + let dims = EncoderDims::new(1280, 720); + let handoff_result = QsvD3D11Handoff::new(device, dims, 5_000_000); + match handoff_result { + Ok(_) => { + eprintln!("qsv: modern dispatcher session + SetHandle + encode_init succeeded"); + } + Err(EncoderError::SdkNotFound { .. }) => { + eprintln!("skip: QSV runtime DLL not available"); + } + Err(e) => { + panic!("qsv modern dispatcher init MUST succeed on Tiger Lake Iris Xe: {e:?}"); + } + } +} + +#[test] +fn qsv_real_encode_one_frame_via_dummy_shared_texture() { + let device = match try_create_device_for_vendor(VENDOR_INTEL) { + Some(d) => d, + None => { + eprintln!("skip: no Intel D3D11 device available"); + return; + } + }; + let dims = EncoderDims::new(1920, 1080); + let handoff_result = QsvD3D11Handoff::new(device.clone(), dims, 5_000_000); + let mut handoff = match handoff_result { + Ok(h) => h, + Err(EncoderError::SdkNotFound { .. }) => { + eprintln!("skip: QSV runtime DLL not available"); + return; + } + Err(EncoderError::SessionInitFailed { vendor, status }) => { + eprintln!("skip: QSV session init failed: {vendor} status={status}"); + return; + } + Err(other) => { + eprintln!("skip: QSV init unexpected: {other:?}"); + return; + } + }; + let shared = match try_create_shared_nv12(&device, 1920, 1080) { + Some(h) => h, + None => { + eprintln!("skip: shared NV12 texture creation failed"); + return; + } + }; + let slot = match QsvHandoff::register_slot(&mut handoff, shared, 0, dims) { + Ok(s) => s, + Err(e) => { + eprintln!("skip: QSV register_slot failed: {e:?}"); + return; + } + }; + let pic = PicParams::new(0, true); + let _ = QsvHandoff::encode_shared_async(&mut handoff, slot, 0, dims, pic); + let mut bitstream = None; + for _ in 0..200 { + bitstream = QsvHandoff::poll_completed(&mut handoff, slot); + if bitstream.is_some() { + break; + } + std::thread::sleep(std::time::Duration::from_millis(10)); + } + QsvHandoff::unregister_slot(&mut handoff, slot); + if let Some(bs) = bitstream { + assert!(!bs.data.is_empty(), "non-empty QSV bitstream"); + } else { + eprintln!("qsv: no bitstream produced within timeout; backend init succeeded"); + } +} + +#[test] +fn skip_dont_block_rapid_submit_then_poll_never_hangs() { + let device = match try_create_device() { + Some(d) => d, + None => return, + }; + let dims = EncoderDims::new(640, 360); + let mut handoff = match NvencD3D11Handoff::new(device.clone(), dims, 1_000_000) { + Ok(h) => h, + Err(_) => return, + }; + let shared = match try_create_shared_nv12(&device, 640, 360) { + Some(h) => h, + None => return, + }; + let slot = match NvencHandoff::register_slot(&mut handoff, shared, 0, dims) { + Ok(s) => s, + Err(_) => return, + }; + let start = std::time::Instant::now(); + for i in 0..10 { + let pic = PicParams::new((i as u64) * 16_666, false); + let _ = NvencHandoff::encode_shared_async(&mut handoff, slot, 0, dims, pic); + let _ = NvencHandoff::poll_completed(&mut handoff, slot); + } + let elapsed = start.elapsed(); + assert!( + elapsed.as_secs() < 5, + "10 submit+poll cycles must complete in < 5s, was {elapsed:?}" + ); + NvencHandoff::unregister_slot(&mut handoff, slot); +} + +#[test] +fn cleanup_on_drop_releases_resources() { + let device = match try_create_device() { + Some(d) => d, + None => return, + }; + let dims = EncoderDims::new(640, 360); + { + let _handoff = match NvencD3D11Handoff::new(device.clone(), dims, 1_000_000) { + Ok(h) => h, + Err(_) => return, + }; + } + let _handoff2 = NvencD3D11Handoff::new(device.clone(), dims, 1_000_000); +} + +struct CountingCallback { + seen: Vec<(u64, u32)>, +} + +impl EncoderCompletionCallback for CountingCallback { + fn on_complete(&mut self, sequence: u64, encoded_bytes: u32) { + self.seen.push((sequence, encoded_bytes)); + } +} + +#[test] +fn nvenc_compression_handoff_encodes_one_frame_via_keyed_mutex() { + let device = match try_create_device_for_vendor(VENDOR_NVIDIA) { + Some(d) => d, + None => { + eprintln!("skip: no NVIDIA D3D11 device available"); + return; + } + }; + let dims = EncoderDims::new(1280, 720); + let mut handoff = match NvencD3D11Handoff::new(device.clone(), dims, 2_000_000) { + Ok(h) => h, + Err(EncoderError::SdkNotFound { .. }) => { + eprintln!("skip: NVENC runtime DLL not available"); + return; + } + Err(other) => { + eprintln!("skip: NVENC init unexpected: {other:?}"); + return; + } + }; + let shared = match try_create_shared_nv12(&device, 1280, 720) { + Some(h) => h, + None => { + eprintln!("skip: shared NV12 texture creation failed"); + return; + } + }; + let submission = EncoderSubmission::new(shared, 0, dims, 1); + let mut callback = CountingCallback { seen: Vec::new() }; + NvencHandoff::encode_shared(&mut handoff, submission, &mut callback) + .expect("encode_shared first frame ok"); + assert_eq!(callback.seen.len(), 1, "callback fired once"); + assert_eq!(callback.seen[0].0, 1, "callback sequence matches"); + let mut payload: Option = None; + for _ in 0..200 { + let slot = fluxer_encoder_ring::HandoffSlot::new(0, shared); + payload = NvencHandoff::poll_completed(&mut handoff, slot); + if payload.is_some() { + break; + } + std::thread::sleep(std::time::Duration::from_millis(10)); + } + let bs = match payload { + Some(b) => b, + None => { + eprintln!("nvenc keyed-mutex: no bitstream within timeout; init succeeded"); + return; + } + }; + assert!(!bs.data.is_empty(), "keyed-mutex encoded payload non-empty"); + assert!(bs.is_keyframe, "first frame must be IDR keyframe"); +} + +#[test] +fn nvenc_compression_handoff_handles_back_pressure() { + let device = match try_create_device_for_vendor(VENDOR_NVIDIA) { + Some(d) => d, + None => { + eprintln!("skip: no NVIDIA D3D11 device available"); + return; + } + }; + let dims = EncoderDims::new(640, 360); + let mut handoff = match NvencD3D11Handoff::new(device.clone(), dims, 1_000_000) { + Ok(h) => h, + Err(EncoderError::SdkNotFound { .. }) => { + eprintln!("skip: NVENC runtime DLL not available"); + return; + } + Err(other) => { + eprintln!("skip: NVENC init unexpected: {other:?}"); + return; + } + }; + let shared = match try_create_shared_nv12(&device, 640, 360) { + Some(h) => h, + None => { + eprintln!("skip: shared NV12 texture creation failed"); + return; + } + }; + let total: u64 = (NVENC_COMPLETION_RING_CAPACITY as u64) + 8; + let mut callback = CountingCallback { seen: Vec::new() }; + let mut accepted = 0usize; + let mut full_drops = 0usize; + for seq in 1..=total { + let submission = EncoderSubmission::new(shared, 0, dims, seq); + match NvencHandoff::encode_shared(&mut handoff, submission, &mut callback) { + Ok(()) => { + accepted += 1; + } + Err(RingError::FullDropped { .. }) => { + full_drops += 1; + assert_eq!( + handoff.pending_completion(), + NVENC_COMPLETION_RING_CAPACITY, + "pre-encode drop only when completion ring is full" + ); + } + Err(other) => panic!("unexpected encode_shared error: {other:?}"), + } + std::thread::sleep(std::time::Duration::from_millis(2)); + } + assert_eq!( + accepted + full_drops, + total as usize, + "every submission accounted" + ); + let pending = handoff.pending_completion(); + assert!( + pending <= NVENC_COMPLETION_RING_CAPACITY, + "completion ring stays bounded" + ); + let mut drained = 0usize; + for _ in 0..400 { + let slot = fluxer_encoder_ring::HandoffSlot::new(0, shared); + let bs = NvencHandoff::poll_completed(&mut handoff, slot); + match bs { + Some(_) => { + drained += 1; + } + None => { + if drained >= accepted { + break; + } + std::thread::sleep(std::time::Duration::from_millis(5)); + } + } + } + assert_eq!( + drained, accepted, + "no accepted frame is silently discarded post-encode" + ); +} + +#[test] +fn nvenc_attach_then_detach_smoke() { + let device = match try_create_device_for_vendor(VENDOR_NVIDIA) { + Some(d) => d, + None => { + eprintln!("skip: no NVIDIA D3D11 device available"); + return; + } + }; + let dims = EncoderDims::new(640, 360); + let handoff_result = NvencD3D11Handoff::new(device.clone(), dims, 1_000_000); + let handoff = match handoff_result { + Ok(h) => h, + Err(EncoderError::SdkNotFound { .. }) => { + eprintln!("skip: NVENC runtime DLL not available"); + return; + } + Err(other) => { + eprintln!("skip: NVENC init unexpected: {other:?}"); + return; + } + }; + assert_eq!( + handoff.pending_completion(), + 0, + "fresh handoff has empty ring" + ); + assert_eq!(handoff.completed_count(), 0, "fresh handoff completed=0"); + drop(handoff); + let _again = NvencD3D11Handoff::new(device, dims, 1_000_000); +} diff --git a/fluxer_desktop/native/gpu-rebuild/Cargo.lock b/fluxer_desktop/native/gpu-rebuild/Cargo.lock new file mode 100644 index 0000000..96619c3 --- /dev/null +++ b/fluxer_desktop/native/gpu-rebuild/Cargo.lock @@ -0,0 +1,522 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "bit-set" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ddef2995421ab6a5c779542c81ee77c115206f4ad9d5a8e05f4ff49716a3dd" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "codespan-reporting" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "fluxer_gpu_rebuild" +version = "0.0.0" +dependencies = [ + "parking_lot", + "wgpu", +] + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "num-traits", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "naga" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd91265cc2454558f659b3b4b9640f0ddb8cc6521277f166b8a8c181c898079" +dependencies = [ + "arrayvec", + "bit-set", + "bitflags", + "cfg-if", + "cfg_aliases", + "codespan-reporting", + "half", + "hashbrown 0.16.1", + "hexf-parse", + "indexmap", + "libm", + "log", + "num-traits", + "once_cell", + "rustc-hash", + "thiserror", + "unicode-ident", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "wgpu" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb3feacc458f7bee8bc1737149b42b6c731aa461039a4264a67bb6681646b250" +dependencies = [ + "arrayvec", + "bitflags", + "bytemuck", + "cfg-if", + "cfg_aliases", + "document-features", + "hashbrown 0.16.1", + "log", + "portable-atomic", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02da3ad1b568337f25513b317870960ef87073ea0945502e44b864b67a8c77b7" +dependencies = [ + "arrayvec", + "bit-set", + "bit-vec", + "bitflags", + "bytemuck", + "cfg_aliases", + "document-features", + "hashbrown 0.16.1", + "indexmap", + "log", + "naga", + "once_cell", + "parking_lot", + "portable-atomic", + "profiling", + "raw-window-handle", + "rustc-hash", + "smallvec", + "thiserror", + "wgpu-core-deps-windows-linux-android", + "wgpu-hal", + "wgpu-naga-bridge", + "wgpu-types", +] + +[[package]] +name = "wgpu-core-deps-windows-linux-android" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb01076d0aa08b0ba9bd741e178b5cc440f5abe99d9581323a4c8b5d1a1916" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-hal" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8e1a9e7a8512f276f7c62e018c7fa8d60954303fed2e5750114332049193f" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libloading", + "log", + "naga", + "portable-atomic", + "portable-atomic-util", + "raw-window-handle", + "renderdoc-sys", + "thiserror", + "wgpu-naga-bridge", + "wgpu-types", +] + +[[package]] +name = "wgpu-naga-bridge" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c654c483f058800972c3645e95388a7eca31bf9fe1933bc20e036588a0be02" +dependencies = [ + "naga", + "wgpu-types", +] + +[[package]] +name = "wgpu-types" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9bcc31518a0e9735aefebedb5f7a9ef3ed1c42549c9f4c882fa9060ceaac639" +dependencies = [ + "bitflags", + "bytemuck", + "log", + "raw-window-handle", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "zerocopy" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] diff --git a/fluxer_desktop/native/gpu-rebuild/Cargo.toml b/fluxer_desktop/native/gpu-rebuild/Cargo.toml new file mode 100644 index 0000000..284a8d2 --- /dev/null +++ b/fluxer_desktop/native/gpu-rebuild/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "fluxer_gpu_rebuild" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["rlib"] + +[features] +default = [] +wgpu = ["dep:wgpu"] + +[dependencies] +parking_lot = "0.12" +wgpu = { version = "29", optional = true, default-features = false } + +[dev-dependencies] +parking_lot = "0.12" diff --git a/fluxer_desktop/native/gpu-rebuild/src/lib.rs b/fluxer_desktop/native/gpu-rebuild/src/lib.rs new file mode 100644 index 0000000..482b648 --- /dev/null +++ b/fluxer_desktop/native/gpu-rebuild/src/lib.rs @@ -0,0 +1,677 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![deny(clippy::too_many_lines)] +#![deny(clippy::unwrap_used)] +#![deny(clippy::panic)] +#![deny(warnings)] + +use parking_lot::Mutex; +use std::sync::Arc; + +pub const MAX_REGISTERED_OWNERS: usize = 1024; + +#[cfg(feature = "wgpu")] +pub type GpuDevice = wgpu::Device; +#[cfg(feature = "wgpu")] +pub type GpuQueue = wgpu::Queue; + +#[cfg(not(feature = "wgpu"))] +#[derive(Debug)] +pub struct GpuDevice { + pub id: u64, +} + +#[cfg(not(feature = "wgpu"))] +#[derive(Debug)] +pub struct GpuQueue { + pub id: u64, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum GpuRebuildError { + DeviceRejected { reason: &'static str }, + ResourceCreateFailed { reason: &'static str }, + OwnerInvariantBroken { reason: &'static str }, + Other { code: u32 }, +} + +impl std::fmt::Display for GpuRebuildError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::DeviceRejected { reason } => write!(f, "device rejected: {reason}"), + Self::ResourceCreateFailed { reason } => write!(f, "resource create failed: {reason}"), + Self::OwnerInvariantBroken { reason } => write!(f, "owner invariant broken: {reason}"), + Self::Other { code } => write!(f, "other rebuild error code={code}"), + } + } +} + +impl std::error::Error for GpuRebuildError {} + +pub type OwnerId = u64; + +pub trait GpuLossCallback: Send { + fn release(&mut self); + fn rebuild(&mut self, device: &GpuDevice, queue: &GpuQueue) -> Result<(), GpuRebuildError>; + fn is_ready(&self) -> bool; + fn debug_label(&self) -> &'static str { + "" + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum RebuildOutcome { + Rebuilt { + owner_id: OwnerId, + label: &'static str, + }, + Failed { + owner_id: OwnerId, + label: &'static str, + error: GpuRebuildError, + }, + Vacant { + owner_id: OwnerId, + }, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RebuildReport { + pub released_count: u32, + pub rebuilt_count: u32, + pub failed_count: u32, + pub vacant_count: u32, + pub outcomes: Vec, +} + +impl RebuildReport { + pub fn is_total_success(&self) -> bool { + self.failed_count == 0 && self.rebuilt_count > 0 + } + + pub fn is_empty_pass(&self) -> bool { + self.released_count == 0 + && self.rebuilt_count == 0 + && self.failed_count == 0 + && self.vacant_count == 0 + } +} + +struct Slot { + owner_id: OwnerId, + callback: Option>, +} + +struct RegistryInner { + slots: Vec, + next_owner_id: OwnerId, + registration_order_monotonic_floor: OwnerId, +} + +impl RegistryInner { + fn new() -> Self { + Self { + slots: Vec::with_capacity(MAX_REGISTERED_OWNERS), + next_owner_id: 1, + registration_order_monotonic_floor: 0, + } + } + + fn deregister(&mut self, owner_id: OwnerId) { + assert!(owner_id > 0, "owner_id must be positive"); + assert!( + owner_id < self.next_owner_id, + "owner_id must come from a real registration" + ); + let before = self.slots.len(); + self.slots.retain(|slot| slot.owner_id != owner_id); + let after = self.slots.len(); + assert!(after <= before, "deregister must not grow slots"); + assert!( + after >= before.saturating_sub(1), + "deregister removes at most one slot" + ); + } +} + +pub struct GpuLossRegistry { + inner: Arc>, +} + +impl GpuLossRegistry { + pub fn new() -> Self { + let inner = Arc::new(Mutex::new(RegistryInner::new())); + let registry = Self { inner }; + assert_eq!(registry.len(), 0, "fresh registry must be empty"); + assert!(registry.is_empty(), "fresh registry must report empty"); + registry + } + + pub fn len(&self) -> usize { + let guard = self.inner.lock(); + guard.slots.len() + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn register(&self, owner: Box) -> RegistrationGuard { + let mut guard = self.inner.lock(); + assert!( + guard.slots.len() < MAX_REGISTERED_OWNERS, + "registry exceeds MAX_REGISTERED_OWNERS", + ); + let id = guard.next_owner_id; + assert!( + id > guard.registration_order_monotonic_floor, + "ids must be monotonic" + ); + guard.registration_order_monotonic_floor = id; + guard.next_owner_id = id.checked_add(1).unwrap_or(OwnerId::MAX); + guard.slots.push(Slot { + owner_id: id, + callback: Some(owner), + }); + drop(guard); + RegistrationGuard { + owner_id: id, + registry: Arc::clone(&self.inner), + } + } + + pub fn handle_device_lost( + &self, + new_device: &GpuDevice, + new_queue: &GpuQueue, + ) -> RebuildReport { + let mut guard = self.inner.lock(); + assert!( + guard.slots.len() <= MAX_REGISTERED_OWNERS, + "slots within cap before walk", + ); + let released_count = release_in_reverse_order(&mut guard.slots); + let outcomes = rebuild_in_forward_order(&mut guard.slots, new_device, new_queue); + let report = summarize_outcomes(outcomes, released_count); + assert_pair_total_accounting(&guard.slots, &report); + assert_pair_post_rebuild_ready(&guard.slots, &report); + report + } +} + +impl Default for GpuLossRegistry { + fn default() -> Self { + Self::new() + } +} + +pub struct RegistrationGuard { + owner_id: OwnerId, + registry: Arc>, +} + +impl RegistrationGuard { + pub fn owner_id(&self) -> OwnerId { + assert!(self.owner_id > 0, "guard must hold a valid id"); + assert!( + Arc::strong_count(&self.registry) >= 1, + "registry must outlive guard" + ); + self.owner_id + } +} + +impl Drop for RegistrationGuard { + fn drop(&mut self) { + let owner_id = self.owner_id; + if owner_id == 0 { + return; + } + let mut guard = self.registry.lock(); + guard.deregister(owner_id); + } +} + +fn release_in_reverse_order(slots: &mut [Slot]) -> u32 { + let total = slots.len(); + assert!( + total <= MAX_REGISTERED_OWNERS, + "slots within cap on release" + ); + let mut released: u32 = 0; + for slot in slots.iter_mut().rev() { + if let Some(callback) = slot.callback.as_mut() { + callback.release(); + released = released.saturating_add(1); + } + } + assert!(released as usize <= total, "released cannot exceed total"); + released +} + +fn rebuild_in_forward_order( + slots: &mut [Slot], + device: &GpuDevice, + queue: &GpuQueue, +) -> Vec { + let total = slots.len(); + assert!( + total <= MAX_REGISTERED_OWNERS, + "slots within cap on rebuild" + ); + let mut outcomes: Vec = Vec::with_capacity(total); + for slot in slots.iter_mut() { + let owner_id = slot.owner_id; + assert!(owner_id > 0, "rebuild slot must have non-zero id"); + let outcome = rebuild_single_slot(slot, device, queue); + outcomes.push(outcome); + } + assert_eq!(outcomes.len(), total, "one outcome per slot"); + outcomes +} + +fn rebuild_single_slot(slot: &mut Slot, device: &GpuDevice, queue: &GpuQueue) -> RebuildOutcome { + let owner_id = slot.owner_id; + let callback = match slot.callback.as_mut() { + Some(cb) => cb, + None => return RebuildOutcome::Vacant { owner_id }, + }; + let label = callback.debug_label(); + match callback.rebuild(device, queue) { + Ok(()) => RebuildOutcome::Rebuilt { owner_id, label }, + Err(error) => RebuildOutcome::Failed { + owner_id, + label, + error, + }, + } +} + +fn summarize_outcomes(outcomes: Vec, released_count: u32) -> RebuildReport { + let mut rebuilt_count: u32 = 0; + let mut failed_count: u32 = 0; + let mut vacant_count: u32 = 0; + for outcome in outcomes.iter() { + match outcome { + RebuildOutcome::Rebuilt { .. } => rebuilt_count = rebuilt_count.saturating_add(1), + RebuildOutcome::Failed { .. } => failed_count = failed_count.saturating_add(1), + RebuildOutcome::Vacant { .. } => vacant_count = vacant_count.saturating_add(1), + } + } + let report = RebuildReport { + released_count, + rebuilt_count, + failed_count, + vacant_count, + outcomes, + }; + assert_eq!( + report.outcomes.len() as u32, + report.rebuilt_count + report.failed_count + report.vacant_count, + "outcome totals must match", + ); + report +} + +fn assert_pair_total_accounting(slots: &[Slot], report: &RebuildReport) { + let total = slots.len() as u32; + assert_eq!( + total, + report.rebuilt_count + report.failed_count + report.vacant_count, + "report covers every slot", + ); + let mut last_id: OwnerId = 0; + for slot in slots.iter() { + assert!( + slot.owner_id > last_id, + "registration ids must be monotonic" + ); + last_id = slot.owner_id; + } +} + +fn assert_pair_post_rebuild_ready(slots: &[Slot], report: &RebuildReport) { + assert_eq!( + slots.len(), + report.outcomes.len(), + "slot count must match report" + ); + for (slot, outcome) in slots.iter().zip(report.outcomes.iter()) { + match outcome { + RebuildOutcome::Rebuilt { owner_id, .. } => { + assert_eq!(*owner_id, slot.owner_id, "owner id alignment"); + if let Some(cb) = slot.callback.as_ref() { + assert!(cb.is_ready(), "rebuilt owner must report ready"); + } + } + RebuildOutcome::Failed { owner_id, .. } => { + assert_eq!(*owner_id, slot.owner_id, "failed owner id alignment"); + } + RebuildOutcome::Vacant { owner_id } => { + assert_eq!(*owner_id, slot.owner_id, "vacant owner id alignment"); + assert!(slot.callback.is_none(), "vacant slot must have no callback"); + } + } + } +} + +#[cfg(all(test, not(feature = "wgpu")))] +mod tests { + use super::*; + use std::sync::atomic::{AtomicU32, Ordering}; + use std::thread; + + fn make_device_queue() -> (GpuDevice, GpuQueue) { + (GpuDevice { id: 42 }, GpuQueue { id: 42 }) + } + + #[derive(Default)] + struct Counters { + release_calls: AtomicU32, + rebuild_calls: AtomicU32, + release_seq: AtomicU32, + rebuild_seq: AtomicU32, + } + + struct MockOwner { + counters: Arc, + ready: bool, + fail_rebuild: bool, + release_order: Arc>>, + rebuild_order: Arc>>, + slot_marker: u32, + label: &'static str, + } + + impl GpuLossCallback for MockOwner { + fn release(&mut self) { + self.ready = false; + let n = self.counters.release_calls.fetch_add(1, Ordering::SeqCst); + self.counters.release_seq.store(n + 1, Ordering::SeqCst); + let mut order = self.release_order.lock(); + order.push(self.slot_marker); + } + + fn rebuild( + &mut self, + _device: &GpuDevice, + _queue: &GpuQueue, + ) -> Result<(), GpuRebuildError> { + let n = self.counters.rebuild_calls.fetch_add(1, Ordering::SeqCst); + self.counters.rebuild_seq.store(n + 1, Ordering::SeqCst); + let mut order = self.rebuild_order.lock(); + order.push(self.slot_marker); + if self.fail_rebuild { + return Err(GpuRebuildError::ResourceCreateFailed { + reason: "mock fail", + }); + } + self.ready = true; + Ok(()) + } + + fn is_ready(&self) -> bool { + self.ready + } + + fn debug_label(&self) -> &'static str { + self.label + } + } + + fn fresh_counters() -> Arc { + Arc::new(Counters::default()) + } + + fn make_owner( + marker: u32, + counters: Arc, + release_order: Arc>>, + rebuild_order: Arc>>, + ) -> Box { + Box::new(MockOwner { + counters, + ready: true, + fail_rebuild: false, + release_order, + rebuild_order, + slot_marker: marker, + label: "mock", + }) + } + + #[test] + fn five_owners_release_lifo_rebuild_fifo() { + let registry = GpuLossRegistry::new(); + let counters = fresh_counters(); + let release_order = Arc::new(Mutex::new(Vec::new())); + let rebuild_order = Arc::new(Mutex::new(Vec::new())); + let mut guards = Vec::new(); + for i in 0..5u32 { + let owner = make_owner( + i, + Arc::clone(&counters), + Arc::clone(&release_order), + Arc::clone(&rebuild_order), + ); + guards.push(registry.register(owner)); + } + assert_eq!(registry.len(), 5); + let (device, queue) = make_device_queue(); + let report = registry.handle_device_lost(&device, &queue); + assert_eq!(report.released_count, 5); + assert_eq!(report.rebuilt_count, 5); + assert_eq!(report.failed_count, 0); + assert_eq!(report.vacant_count, 0); + let rel = release_order.lock().clone(); + assert_eq!(rel, vec![4, 3, 2, 1, 0]); + let reb = rebuild_order.lock().clone(); + assert_eq!(reb, vec![0, 1, 2, 3, 4]); + drop(guards); + } + + #[test] + fn owner_rebuild_failure_does_not_abort_other_owners() { + let registry = GpuLossRegistry::new(); + let counters = fresh_counters(); + let release_order = Arc::new(Mutex::new(Vec::new())); + let rebuild_order = Arc::new(Mutex::new(Vec::new())); + let mut guards = Vec::new(); + for i in 0..4u32 { + let mut owner = make_owner( + i, + Arc::clone(&counters), + Arc::clone(&release_order), + Arc::clone(&rebuild_order), + ); + if i == 1 { + owner.fail_rebuild = true; + } + guards.push(registry.register(owner)); + } + let (device, queue) = make_device_queue(); + let report = registry.handle_device_lost(&device, &queue); + assert_eq!(report.released_count, 4); + assert_eq!(report.rebuilt_count, 3); + assert_eq!(report.failed_count, 1); + let failed = report + .outcomes + .iter() + .filter(|o| matches!(o, RebuildOutcome::Failed { .. })) + .count(); + assert_eq!(failed, 1); + drop(guards); + } + + #[test] + fn registration_guard_drop_deregisters() { + let registry = GpuLossRegistry::new(); + let counters = fresh_counters(); + let release_order = Arc::new(Mutex::new(Vec::new())); + let rebuild_order = Arc::new(Mutex::new(Vec::new())); + let owner = make_owner( + 7, + Arc::clone(&counters), + Arc::clone(&release_order), + Arc::clone(&rebuild_order), + ); + let guard = registry.register(owner); + assert_eq!(registry.len(), 1); + drop(guard); + assert_eq!(registry.len(), 0); + assert!(registry.is_empty()); + } + + #[test] + fn empty_registry_device_loss_is_noop() { + let registry = GpuLossRegistry::new(); + let (device, queue) = make_device_queue(); + let report = registry.handle_device_lost(&device, &queue); + assert!(report.is_empty_pass()); + assert!(!report.is_total_success()); + assert_eq!(report.outcomes.len(), 0); + } + + #[test] + fn concurrent_registration_is_safe() { + let registry = Arc::new(GpuLossRegistry::new()); + let counters = fresh_counters(); + let release_order = Arc::new(Mutex::new(Vec::new())); + let rebuild_order = Arc::new(Mutex::new(Vec::new())); + let mut threads = Vec::new(); + let guards_collector: Arc>> = Arc::new(Mutex::new(Vec::new())); + for i in 0..16u32 { + let registry = Arc::clone(®istry); + let counters = Arc::clone(&counters); + let release_order = Arc::clone(&release_order); + let rebuild_order = Arc::clone(&rebuild_order); + let guards_collector = Arc::clone(&guards_collector); + threads.push(thread::spawn(move || { + let owner = make_owner(i, counters, release_order, rebuild_order); + let guard = registry.register(owner); + let mut store = guards_collector.lock(); + store.push(guard); + })); + } + for t in threads { + assert!(t.join().is_ok()); + } + assert_eq!(registry.len(), 16); + } + + #[test] + fn pair_asserted_invariants_under_stress() { + let registry = GpuLossRegistry::new(); + let counters = fresh_counters(); + let release_order = Arc::new(Mutex::new(Vec::new())); + let rebuild_order = Arc::new(Mutex::new(Vec::new())); + let mut guards = Vec::new(); + for i in 0..64u32 { + let mut owner = make_owner( + i, + Arc::clone(&counters), + Arc::clone(&release_order), + Arc::clone(&rebuild_order), + ); + if i % 7 == 0 { + owner.fail_rebuild = true; + } + guards.push(registry.register(owner)); + } + let (device, queue) = make_device_queue(); + for _ in 0..5 { + release_order.lock().clear(); + rebuild_order.lock().clear(); + let _report = registry.handle_device_lost(&device, &queue); + } + let rel = release_order.lock().clone(); + let reb = rebuild_order.lock().clone(); + assert_eq!(rel.len(), 64); + assert_eq!(reb.len(), 64); + for i in 0..64u32 { + assert_eq!(rel[i as usize], 63 - i); + assert_eq!(reb[i as usize], i); + } + drop(guards); + assert!(registry.is_empty()); + } + + #[test] + fn mock_callback_counters_are_deterministic() { + let registry = GpuLossRegistry::new(); + let counters = fresh_counters(); + let release_order = Arc::new(Mutex::new(Vec::new())); + let rebuild_order = Arc::new(Mutex::new(Vec::new())); + let guard = registry.register(make_owner( + 1, + Arc::clone(&counters), + Arc::clone(&release_order), + Arc::clone(&rebuild_order), + )); + let (device, queue) = make_device_queue(); + for _ in 0..3 { + registry.handle_device_lost(&device, &queue); + } + assert_eq!(counters.release_calls.load(Ordering::SeqCst), 3); + assert_eq!(counters.rebuild_calls.load(Ordering::SeqCst), 3); + drop(guard); + } + + #[test] + fn deregister_middle_owner_preserves_order() { + let registry = GpuLossRegistry::new(); + let counters = fresh_counters(); + let release_order = Arc::new(Mutex::new(Vec::new())); + let rebuild_order = Arc::new(Mutex::new(Vec::new())); + let g0 = registry.register(make_owner( + 0, + Arc::clone(&counters), + Arc::clone(&release_order), + Arc::clone(&rebuild_order), + )); + let g1 = registry.register(make_owner( + 1, + Arc::clone(&counters), + Arc::clone(&release_order), + Arc::clone(&rebuild_order), + )); + let g2 = registry.register(make_owner( + 2, + Arc::clone(&counters), + Arc::clone(&release_order), + Arc::clone(&rebuild_order), + )); + assert_eq!(registry.len(), 3); + drop(g1); + assert_eq!(registry.len(), 2); + let (device, queue) = make_device_queue(); + let report = registry.handle_device_lost(&device, &queue); + assert_eq!(report.rebuilt_count, 2); + let rel = release_order.lock().clone(); + assert_eq!(rel, vec![2, 0]); + let reb = rebuild_order.lock().clone(); + assert_eq!(reb, vec![0, 2]); + drop(g0); + drop(g2); + assert!(registry.is_empty()); + } + + #[test] + fn rebuild_report_total_success_flag() { + let registry = GpuLossRegistry::new(); + let counters = fresh_counters(); + let release_order = Arc::new(Mutex::new(Vec::new())); + let rebuild_order = Arc::new(Mutex::new(Vec::new())); + let guard = registry.register(make_owner( + 0, + Arc::clone(&counters), + Arc::clone(&release_order), + Arc::clone(&rebuild_order), + )); + let (device, queue) = make_device_queue(); + let report = registry.handle_device_lost(&device, &queue); + assert!(report.is_total_success()); + assert!(!report.is_empty_pass()); + drop(guard); + } +} diff --git a/fluxer_desktop/native/linux-audio-capture/Cargo.lock b/fluxer_desktop/native/linux-audio-capture/Cargo.lock new file mode 100644 index 0000000..7f5db5d --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/Cargo.lock @@ -0,0 +1,1342 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "annotate-snippets" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4" +dependencies = [ + "anstyle", + "unicode-width", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "annotate-snippets", + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex 1.3.0", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +dependencies = [ + "find-msvc-tools", + "shlex 2.0.1", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom 7.1.3", +] + +[[package]] +name = "cfg-expr" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb693542bcafa528e198be0ebd9d3632ca5b7c93dbe7237460e199910835997c" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading 0.8.9", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cookie-factory" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" + +[[package]] +name = "criterion" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3" +dependencies = [ + "alloca", + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "num-traits", + "oorandom", + "page_size", + "plotters", + "rayon", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fluxer_audio_apm" +version = "0.0.0" + +[[package]] +name = "fluxer_audio_mix" +version = "0.0.0" +dependencies = [ + "fluxer_rt_thread", + "rtrb", +] + +[[package]] +name = "fluxer_audio_timing" +version = "0.0.0" + +[[package]] +name = "fluxer_gpu_rebuild" +version = "0.0.0" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "fluxer_linux_audio_capture" +version = "0.0.0" +dependencies = [ + "criterion", + "fluxer_audio_apm", + "fluxer_audio_mix", + "fluxer_audio_timing", + "fluxer_rt_thread", + "fluxer_screen_frame_bus", + "napi", + "napi-build", + "napi-derive", + "pipewire", +] + +[[package]] +name = "fluxer_rt_thread" +version = "0.0.0" +dependencies = [ + "libc", + "windows", +] + +[[package]] +name = "fluxer_screen_frame_bus" +version = "0.0.0" +dependencies = [ + "fluxer_gpu_rebuild", + "parking_lot", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libspa" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2909f3be29d674e7f10604aff18d1bbe1bb03c4cd61c8a8ba19c0b1d162f7d4e" +dependencies = [ + "bitflags", + "cc", + "cookie-factory", + "libc", + "libspa-sys", + "nom 8.0.0", + "rustix", + "system-deps", +] + +[[package]] +name = "libspa-sys" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69ad52764fca54818486f3cf75afec844d1f1a1568c24dcee25d41b1ab007dda" +dependencies = [ + "bindgen", + "cc", + "system-deps", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading 0.9.0", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pipewire" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8585aba8a52ad74ccc633b8e293c1dc4277976bd5d510b925533f34fd6685f38" +dependencies = [ + "bitflags", + "libc", + "libspa", + "libspa-sys", + "pipewire-sys", + "rustix", +] + +[[package]] +name = "pipewire-sys" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2089f245b548723e60325773c27f586b7a2372c79ea941b246cd0d654706adc" +dependencies = [ + "bindgen", + "libspa-sys", + "system-deps", +] + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rtrb" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ade083ccbb4bf536df69d1f6432cc23deb7acccff86b183f3923a6fd56a1153" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "7.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396a35feb67335377e0251fcbc1092fc85c484bd4e3a7a54319399da127796e7" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "version-compare" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0871acf327f283dc6da28a1696cdc64fb355ba9f935d052021fa77f35cce69" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" + +[[package]] +name = "zerocopy" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/fluxer_desktop/native/linux-audio-capture/Cargo.toml b/fluxer_desktop/native/linux-audio-capture/Cargo.toml new file mode 100644 index 0000000..43dfc31 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "fluxer_linux_audio_capture" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8"]} +napi-derive = "3.5.6" +fluxer_rt_thread = {path = "../rt-thread"} +fluxer_audio_mix = {path = "../audio-mix"} +fluxer_audio_apm = {path = "../audio-apm"} +fluxer_audio_timing = {path = "../audio-timing"} +fluxer_screen_frame_bus = {path = "../screen-frame-bus"} + +[target.'cfg(target_os = "linux")'.dependencies] +pipewire = "0.10.0" + +[build-dependencies] +napi-build = "2.3.2" + +[dev-dependencies] +criterion = "0.8" + +[[bench]] +name = "end_to_end" +harness = false diff --git a/fluxer_desktop/native/linux-audio-capture/benches/baseline.json b/fluxer_desktop/native/linux-audio-capture/benches/baseline.json new file mode 100644 index 0000000..41a2ed1 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/benches/baseline.json @@ -0,0 +1,18 @@ +{ + "measured_at": "85e057a273fd", + "host": "darwin-arm64-apple-silicon", + "regression_budget_percent": 5.0, + "criterion_args": { + "warm_up_time_sec": 2, + "measurement_time_sec": 5 + }, + "benches": { + "linux_audio_end_to_end/8_sources_capture_ring_mix_policy": { + "median_ns": 2453.7, + "low_ns": 2428.6, + "high_ns": 2479.5, + "budget_percent_override": 8.0, + "note": "Sub-microsecond mix-runtime tick; same noise floor logic as audio-mix/mix_tick. Pure-Rust bench helpers; no PipeWire process required." + } + } +} diff --git a/fluxer_desktop/native/linux-audio-capture/benches/end_to_end.rs b/fluxer_desktop/native/linux-audio-capture/benches/end_to_end.rs new file mode 100644 index 0000000..7c04cff --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/benches/end_to_end.rs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::hint::black_box; + +use criterion::{Criterion, criterion_group, criterion_main}; +use fluxer_audio_mix::{AUDIO_OUTPUT_FRAMES, SourceRing, SourceRingProducer}; +use fluxer_linux_audio_capture::audio_mix_runtime_bench_helpers::{ + AudioMixRuntimeBuilder, CaptureSource, MIX_CHANNELS, MIX_SAMPLE_RATE_HZ, MIX_TICK_PERIOD_NS, + NullMixOutputSink, +}; + +const BENCH_SOURCE_COUNT: usize = 8; + +fn build_sources_and_runtime() -> ( + Vec, + fluxer_linux_audio_capture::audio_mix_runtime_bench_helpers::AudioMixRuntime, +) { + let mut sources = Vec::with_capacity(BENCH_SOURCE_COUNT); + let mut builder = AudioMixRuntimeBuilder::new(); + for n in 0..BENCH_SOURCE_COUNT { + let (source, consumer) = + CaptureSource::create(n as u64 + 1, MIX_SAMPLE_RATE_HZ, MIX_CHANNELS).expect("source"); + sources.push(source); + builder = builder.add_source(n as u64 + 1, consumer); + } + let runtime = builder.build(NullMixOutputSink).expect("build"); + (sources, runtime) +} + +fn fill_sources(sources: &mut [CaptureSource], frames: usize) { + assert!(!sources.is_empty()); + assert!(frames > 0); + let payload: Vec = (0..frames).map(|n| ((n as i16) % 4096) - 2048).collect(); + for source in sources.iter_mut() { + let _pushed = source.ingest_skip_apm(&payload); + } +} + +fn bench_end_to_end_tick(c: &mut Criterion) { + let mut group = c.benchmark_group("linux_audio_end_to_end"); + group.sample_size(50); + group.bench_function("8_sources_capture_ring_mix_policy", |b| { + let (mut sources, mut runtime) = build_sources_and_runtime(); + let mut tick_index: u64 = 0; + b.iter(|| { + fill_sources(&mut sources, AUDIO_OUTPUT_FRAMES); + let frame = runtime + .run_one_tick_blocking(tick_index * MIX_TICK_PERIOD_NS) + .expect("frame"); + tick_index = tick_index.wrapping_add(1); + black_box(frame); + }); + }); + group.finish(); +} + +#[allow(dead_code)] +fn _producer_helper(producer: SourceRingProducer) -> SourceRingProducer { + producer +} + +#[allow(dead_code)] +fn _ring_helper() -> usize { + let _: usize = SourceRing::create(8192, 48_000).map(|_| 0).unwrap_or(0); + 0 +} + +criterion_group!(benches, bench_end_to_end_tick); +criterion_main!(benches); diff --git a/fluxer_desktop/native/linux-audio-capture/build.rs b/fluxer_desktop/native/linux-audio-capture/build.rs new file mode 100644 index 0000000..d45de40 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/build.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + napi_build::setup(); +} diff --git a/fluxer_desktop/native/linux-audio-capture/index.d.ts b/fluxer_desktop/native/linux-audio-capture/index.d.ts new file mode 100644 index 0000000..b26df33 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/index.d.ts @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import {EventEmitter} from 'node:events'; + +export interface JsRoutingRule { + include?: Array>; + exclude?: Array>; + workaround?: Array>; + ignoreDevices?: boolean; + onlySpeakers?: boolean; + onlyDefaultSpeakers?: boolean; +} + +export interface AudioFrame { + samples: Float32Array; + sampleRate: number; + channels: number; + timestampUs: number; +} + +export interface NativeAudioFrame { + samples: ArrayBuffer; + sampleRate: number; + channels: number; + timestampUs: number; +} + +export interface RoutingGraphNode { + id: number; + props: Record; +} + +export interface RoutingGraphPort { + id: number; + nodeId: number; + direction: string; + channel: string; + props: Record; +} + +export interface RoutingGraphLink { + outputNodeId: number; + outputPortId: number; + inputNodeId: number; + inputPortId: number; + owned: boolean; + passive: boolean; +} + +export interface RoutingGraph { + backend: 'pipewire' | 'none' | string; + nodes: Array; + ports: Array; + ownedLinks: Array; +} + +export declare function pipeWireAvailable(): boolean; + +export declare function audioBackend(): 'pipewire' | 'none'; + +export declare class AudioBridge { + constructor(); + + inventory(fields?: Array | undefined | null): Array>; + + routingGraph(): RoutingGraph; + + apply(rule: JsRoutingRule): boolean; + + release(): void; + + backend(): 'pipewire' | 'none'; +} + +export declare class DirectAudioCapture { + constructor(); + + start(rule: JsRoutingRule): boolean; + + setRule(rule: JsRoutingRule): boolean; + + setLifecycleCallback(callback: (type: string, message: string) => void): void; + + read(): NativeAudioFrame | null; + + routingGraph(): RoutingGraph; + + stop(): void; +} + +export declare class AudioMixRuntimeHandle { + constructor(sourceCount: number); + + static boundToDirectCapture(capture: DirectAudioCapture): AudioMixRuntimeHandle; + + sourceCount(): number; + + tick(tickAtNs?: number | null): number; + + markPushedTotal(): number; + + dispose(): void; +} + +interface ProcessLoopbackEvents { + on(event: 'frame', listener: (frame: AudioFrame) => void): this; + on(event: 'error', listener: (error: Error) => void): this; + on(event: 'closed', listener: () => void): this; + on(event: 'diagnostic', listener: (message: string) => void): this; + removeListener(event: 'frame', listener: (frame: AudioFrame) => void): this; + removeListener(event: 'error', listener: (error: Error) => void): this; + removeListener(event: 'closed', listener: () => void): this; + removeListener(event: 'diagnostic', listener: (message: string) => void): this; +} + +export declare class ProcessLoopback extends EventEmitter implements ProcessLoopbackEvents { + constructor(targetPid: number, options?: {includeProcessTree?: boolean; ignoreDevices?: boolean}); + + constructor(options: {linuxRule: JsRoutingRule}); + + start(): void; + + routingGraph(): RoutingGraph | null; + + stop(): Promise; +} diff --git a/fluxer_desktop/native/linux-audio-capture/index.js b/fluxer_desktop/native/linux-audio-capture/index.js new file mode 100644 index 0000000..425f3e8 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/index.js @@ -0,0 +1,395 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {EventEmitter} = require('node:events'); +const {existsSync, readdirSync, readFileSync} = require('node:fs'); +const {join, sep} = require('node:path'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); +const MODULE_NAME = '@fluxer/linux-audio-capture'; +const SKIP_NATIVE_PROBE_ENV = 'FLUXER_LINUX_AUDIO_CAPTURE_SKIP_NATIVE_PROBE'; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName() { + if (process.platform !== 'linux') { + throw new Error(`@fluxer/linux-audio-capture is only supported on Linux, got ${process.platform}`); + } + switch (process.arch) { + case 'x64': + return 'linux-audio-capture.linux-x64-gnu.node'; + case 'arm64': + return 'linux-audio-capture.linux-arm64-gnu.node'; + default: + throw new Error(`Unsupported Linux architecture: ${process.arch}`); + } +} + +let binding; + +try { + const nativeRoot = resolveNativeRoot(); + const nativePath = join(nativeRoot, nativeFileName()); + const loadedNative = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + if (loadedNative.loadError) { + throw loadedNative.loadError; + } + binding = loadedNative.binding; +} catch (error) { + throw createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: 'native loader threw before binding load completed', + cause: error, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); +} + +function normalizeRoutingRule(rule) { + if (!rule || typeof rule !== 'object') return {}; + const normalizeList = (value) => + Array.isArray(value) + ? value + .filter((entry) => entry && typeof entry === 'object' && !Array.isArray(entry)) + .map((entry) => + Object.fromEntries( + Object.entries(entry) + .filter(([, v]) => typeof v === 'string' || typeof v === 'number' || typeof v === 'boolean') + .map(([k, v]) => [k, String(v)]), + ), + ) + : undefined; + return { + include: normalizeList(rule.include), + exclude: normalizeList(rule.exclude), + workaround: normalizeList(rule.workaround), + ignoreDevices: rule.ignoreDevices ?? rule.ignore_devices, + onlySpeakers: rule.onlySpeakers ?? rule.only_speakers, + onlyDefaultSpeakers: rule.onlyDefaultSpeakers ?? rule.only_default_speakers, + }; +} + +function readProcParentMap() { + const parents = new Map(); + let entries = []; + try { + entries = readdirSync('/proc', {withFileTypes: true}); + } catch { + return parents; + } + for (const entry of entries) { + if (!entry.isDirectory() || !/^\d+$/.test(entry.name)) continue; + try { + const stat = readFileSync(`/proc/${entry.name}/stat`, 'utf8'); + const end = stat.lastIndexOf(')'); + if (end < 0) continue; + const fields = stat + .slice(end + 1) + .trim() + .split(/\s+/); + const parentPid = Number(fields[1]); + if (Number.isSafeInteger(parentPid) && parentPid > 0) { + parents.set(Number(entry.name), parentPid); + } + } catch {} + } + return parents; +} + +function isDescendantPid(pid, rootPid, parents) { + let current = pid; + const seen = new Set(); + while (parents.has(current) && !seen.has(current)) { + seen.add(current); + const parent = parents.get(current); + if (parent === rootPid) return true; + current = parent; + } + return false; +} + +function targetPidList(pid, includeProcessTree) { + if (!includeProcessTree) return [pid]; + const parents = readProcParentMap(); + const pids = [pid]; + for (const candidate of parents.keys()) { + if (candidate !== pid && isDescendantPid(candidate, pid, parents)) { + pids.push(candidate); + } + } + return pids; +} + +function appendUniquePattern(patterns, pattern) { + if (!pattern || typeof pattern !== 'object') return; + const entries = Object.entries(pattern).filter(([, value]) => typeof value === 'string' && value.length > 0); + if (entries.length === 0) return; + const normalized = Object.fromEntries(entries); + const key = JSON.stringify(Object.entries(normalized).sort(([a], [b]) => a.localeCompare(b))); + if ( + patterns.some((existing) => JSON.stringify(Object.entries(existing).sort(([a], [b]) => a.localeCompare(b))) === key) + ) { + return; + } + patterns.push(normalized); +} + +function inventoryPatternsForTargetPids(pids) { + if (!(binding && typeof binding.AudioBridge === 'function')) return []; + const wanted = new Set(pids.map((pid) => String(pid))); + const patterns = []; + let bridge = null; + try { + bridge = new binding.AudioBridge(); + const inventory = bridge.inventory(); + if (!Array.isArray(inventory)) return patterns; + for (const props of inventory) { + if (!props || typeof props !== 'object') continue; + if (props['media.class'] !== 'Stream/Output/Audio') continue; + const processId = props['application.process.id'] || props['pipewire.sec.pid']; + if (!wanted.has(String(processId || ''))) continue; + appendUniquePattern(patterns, {'object.serial': String(props['object.serial'] || '')}); + appendUniquePattern(patterns, {'node.name': String(props['node.name'] || '')}); + appendUniquePattern(patterns, {'client.id': String(props['client.id'] || '')}); + } + } catch { + } finally { + try { + bridge?.release?.(); + } catch {} + } + return patterns; +} + +function routingRuleFromTarget(target, options) { + if (target && typeof target === 'object') { + return normalizeRoutingRule(target.linuxRule || target); + } + const pid = Number(target); + if (!Number.isSafeInteger(pid) || pid <= 0) { + throw new TypeError('ProcessLoopback target pid must be a positive integer'); + } + const targetPids = targetPidList(pid, Boolean(options?.includeProcessTree)); + const include = inventoryPatternsForTargetPids(targetPids); + for (const candidate of targetPids) { + appendUniquePattern(include, {'application.process.id': String(candidate)}); + appendUniquePattern(include, {'pipewire.sec.pid': String(candidate)}); + } + return normalizeRoutingRule({ + include, + ignoreDevices: options?.ignoreDevices ?? true, + }); +} + +const LATE_SPAWN_REFRESH_INTERVAL_MS = 2_000; +const MAX_DRAIN_FRAMES_PER_TICK = 16; +const MAX_IDLE_DIRECT_CAPTURES = 2; +const MIX_TICK_PERIOD_MS = 20; + +let idleDirectCaptures = []; + +function acquireDirectAudioCapture() { + if (typeof binding.DirectAudioCapture !== 'function') { + throw new Error('DirectAudioCapture native export missing'); + } + const pooled = idleDirectCaptures.pop(); + return pooled ?? new binding.DirectAudioCapture(); +} + +function releaseDirectAudioCapture(capture) { + if (!capture || idleDirectCaptures.includes(capture)) return; + if (idleDirectCaptures.length < MAX_IDLE_DIRECT_CAPTURES) { + idleDirectCaptures.push(capture); + } +} + +function clearIdleDirectCapturePool() { + idleDirectCaptures = []; +} + +function patternsEqual(a, b) { + if (a === b) return true; + if (!Array.isArray(a) || !Array.isArray(b)) return false; + if (a.length !== b.length) return false; + const serialize = (entry) => + JSON.stringify( + Object.entries(entry) + .filter(([, v]) => typeof v === 'string') + .sort(([x], [y]) => x.localeCompare(y)), + ); + const aSet = a.map(serialize).sort(); + const bSet = b.map(serialize).sort(); + for (let i = 0; i < aSet.length; i++) { + if (aSet[i] !== bSet[i]) return false; + } + return true; +} + +class ProcessLoopback extends EventEmitter { + constructor(target, options = {}) { + super(); + this.capture = acquireDirectAudioCapture(); + if (typeof this.capture.setLifecycleCallback === 'function') { + this.capture.setLifecycleCallback((type, message) => this.handleNativeLifecycle(type, message)); + } + this.targetPid = null; + this.includeProcessTree = false; + if (!(target && typeof target === 'object')) { + const pid = Number(target); + if (Number.isSafeInteger(pid) && pid > 0) { + this.targetPid = pid; + this.includeProcessTree = Boolean(options?.includeProcessTree); + } + } + this.rule = routingRuleFromTarget(target, options); + this.options = options; + this.timer = null; + this.refreshTimer = null; + this.closed = false; + this.started = false; + } + + handleNativeLifecycle(type, message) { + if (type === 'error') { + this.emit('error', new Error(message || 'Linux direct audio capture stopped')); + if (!this.closed) void this.stop(); + return; + } + if (type === 'closed' || type === 'closed-clean') { + if (!this.closed) void this.stop(); + return; + } + if (type === 'diagnostic') { + this.emit('diagnostic', message || ''); + } + } + + start() { + if (this.closed) { + throw new Error('ProcessLoopback already closed'); + } + if (this.started) return; + if (!this.capture.start(this.rule)) { + throw new Error('failed to start Linux direct audio capture'); + } + this.started = true; + this.timer = setInterval(() => this.tick(), MIX_TICK_PERIOD_MS); + this.timer.unref?.(); + if (this.targetPid !== null && this.includeProcessTree && typeof this.capture.setRule === 'function') { + this.refreshTimer = setInterval(() => this.refreshRuleForLateChildren(), LATE_SPAWN_REFRESH_INTERVAL_MS); + this.refreshTimer.unref?.(); + } + } + + refreshRuleForLateChildren() { + if (this.closed || !this.started || this.targetPid === null) return; + let nextRule; + try { + nextRule = routingRuleFromTarget(this.targetPid, { + ...(this.options ?? {}), + includeProcessTree: this.includeProcessTree, + }); + } catch { + return; + } + if (patternsEqual(nextRule.include, this.rule.include) && patternsEqual(nextRule.exclude, this.rule.exclude)) { + return; + } + this.rule = nextRule; + try { + this.capture.setRule(this.rule); + } catch {} + } + + tick() { + if (this.closed || !this.started) return; + try { + this.drainCaptureFrames(); + } catch (error) { + this.emit('error', error instanceof Error ? error : new Error(String(error))); + void this.stop(); + } + } + + drainCaptureFrames() { + for (let i = 0; i < MAX_DRAIN_FRAMES_PER_TICK; i++) { + const frame = this.capture.read(); + if (!frame || !(frame.samples instanceof ArrayBuffer) || frame.samples.byteLength === 0) return; + this.emit('frame', { + samples: new Float32Array(frame.samples), + sampleRate: frame.sampleRate, + channels: frame.channels, + timestampUs: frame.timestampUs, + }); + } + } + + routingGraph() { + return this.capture && typeof this.capture.routingGraph === 'function' ? this.capture.routingGraph() : null; + } + + setScreenAudioSink(handle) { + if (!this.capture || typeof this.capture.setScreenAudioSink !== 'function') return false; + try { + return this.capture.setScreenAudioSink(handle) !== false; + } catch { + return false; + } + } + + clearScreenAudioSink() { + if (this.capture && typeof this.capture.clearScreenAudioSink === 'function') { + this.capture.clearScreenAudioSink(); + } + } + + async stop() { + if (this.closed) return; + this.closed = true; + this.clearScreenAudioSink(); + if (this.timer) clearInterval(this.timer); + this.timer = null; + if (this.refreshTimer) clearInterval(this.refreshTimer); + this.refreshTimer = null; + const wasStarted = this.started; + this.started = false; + const capture = this.capture; + this.capture = null; + let stopped = false; + try { + capture?.stop(); + stopped = true; + } finally { + if (stopped && wasStarted) { + releaseDirectAudioCapture(capture); + } + this.emit('closed'); + } + } +} + +module.exports = { + AudioBridge: binding.AudioBridge, + DirectAudioCapture: binding.DirectAudioCapture, + AudioMixRuntimeHandle: binding.AudioMixRuntimeHandle, + ProcessLoopback, + pipeWireAvailable: binding.pipeWireAvailable, + audioBackend: binding.audioBackend ?? (() => (binding.pipeWireAvailable?.() ? 'pipewire' : 'none')), + __setBindingForTests(nextBinding) { + binding = nextBinding; + clearIdleDirectCapturePool(); + }, + __getIdleDirectCaptureCountForTests() { + return idleDirectCaptures.length; + }, +}; diff --git a/fluxer_desktop/native/linux-audio-capture/index.test.mjs b/fluxer_desktop/native/linux-audio-capture/index.test.mjs new file mode 100644 index 0000000..76721bd --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/index.test.mjs @@ -0,0 +1,167 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import assert from 'node:assert/strict'; +import {createRequire} from 'node:module'; +import {test} from 'node:test'; + +const requireSrc = createRequire(import.meta.url); +const modulePath = requireSrc.resolve('./index.js'); + +function freshModule() { + delete requireSrc.cache[modulePath]; + return requireSrc('./index.js'); +} + +function makeFakeBinding() { + const captures = []; + let startResult = true; + class FakeDirectAudioCapture { + constructor() { + this.started = false; + this.stopCount = 0; + this.startRules = []; + this.lifecycleCallback = undefined; + captures.push(this); + } + + setLifecycleCallback(callback) { + this.lifecycleCallback = callback; + } + + start(rule) { + if (!startResult) return false; + this.started = true; + this.startRules.push(rule); + return true; + } + + setRule(rule) { + this.startRules.push(rule); + return true; + } + + read() { + return null; + } + + stop() { + this.started = false; + this.stopCount += 1; + } + } + + function FakeAudioMixRuntimeHandle() {} + FakeAudioMixRuntimeHandle.boundToDirectCapture = () => { + throw new Error('ProcessLoopback must not tick a discard-only mix runtime'); + }; + + return { + binding: { + AudioBridge: class {}, + AudioMixRuntimeHandle: FakeAudioMixRuntimeHandle, + DirectAudioCapture: FakeDirectAudioCapture, + pipeWireAvailable: () => true, + audioBackend: () => 'pipewire', + }, + captures, + setStartResult(value) { + startResult = value; + }, + }; +} + +let loadError = null; +try { + freshModule(); +} catch (error) { + loadError = error; +} + +test('ProcessLoopback reuses idle direct captures after stop', {skip: loadError?.message}, async () => { + const mod = freshModule(); + const {binding, captures} = makeFakeBinding(); + mod.__setBindingForTests(binding); + + const rule = {linuxRule: {include: [{'application.name': 'Firefox'}]}}; + const first = new mod.ProcessLoopback(rule); + first.start(); + await first.stop(); + + assert.equal(captures.length, 1); + assert.equal(captures[0].stopCount, 1); + assert.equal(mod.__getIdleDirectCaptureCountForTests(), 1); + + const second = new mod.ProcessLoopback(rule); + second.start(); + await second.stop(); + + assert.equal(captures.length, 1); + assert.equal(captures[0].stopCount, 2); + assert.equal(captures[0].startRules.length, 2); + assert.equal(mod.__getIdleDirectCaptureCountForTests(), 1); +}); + +test('ProcessLoopback does not pool failed direct captures', {skip: loadError?.message}, async () => { + const mod = freshModule(); + const {binding, captures, setStartResult} = makeFakeBinding(); + mod.__setBindingForTests(binding); + + const rule = {linuxRule: {include: [{'application.name': 'Firefox'}]}}; + setStartResult(false); + const first = new mod.ProcessLoopback(rule); + assert.throws(() => first.start(), /failed to start Linux direct audio capture/); + await first.stop(); + + assert.equal(captures.length, 1); + assert.equal(mod.__getIdleDirectCaptureCountForTests(), 0); + + setStartResult(true); + const second = new mod.ProcessLoopback(rule); + second.start(); + await second.stop(); + + assert.equal(captures.length, 2); + assert.equal(mod.__getIdleDirectCaptureCountForTests(), 1); +}); + +test('ProcessLoopback installs native lifecycle callback on direct capture', {skip: loadError?.message}, () => { + const mod = freshModule(); + const {binding, captures} = makeFakeBinding(); + mod.__setBindingForTests(binding); + + const loopback = new mod.ProcessLoopback({linuxRule: {include: [{'application.name': 'Firefox'}]}}); + + assert.equal(captures.length, 1); + assert.equal(typeof captures[0].lifecycleCallback, 'function'); + assert.equal(loopback.listenerCount('closed'), 0); +}); + +test('ProcessLoopback does not create a discard-only audio mix runtime', {skip: loadError?.message}, async () => { + const mod = freshModule(); + const {binding} = makeFakeBinding(); + mod.__setBindingForTests(binding); + + const loopback = new mod.ProcessLoopback({linuxRule: {include: [{'application.name': 'Firefox'}]}}); + loopback.start(); + await new Promise((resolve) => setImmediate(resolve)); + await loopback.stop(); +}); + +test('ProcessLoopback closes once when native lifecycle closes while idle', {skip: loadError?.message}, async () => { + const mod = freshModule(); + const {binding, captures} = makeFakeBinding(); + mod.__setBindingForTests(binding); + + const loopback = new mod.ProcessLoopback({linuxRule: {include: [{'application.name': 'Firefox'}]}}); + let closed = 0; + loopback.on('closed', () => { + closed += 1; + }); + + captures[0].lifecycleCallback('closed-clean', 'daemon disconnected'); + await new Promise((resolve) => setImmediate(resolve)); + await loopback.stop(); + + assert.equal(closed, 1); + assert.equal(captures[0].stopCount, 1); +}); diff --git a/fluxer_desktop/native/linux-audio-capture/loader-diagnostics.cjs b/fluxer_desktop/native/linux-audio-capture/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/linux-audio-capture/package.json b/fluxer_desktop/native/linux-audio-capture/package.json new file mode 100644 index 0000000..b2359bf --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/package.json @@ -0,0 +1,27 @@ +{ + "name": "@fluxer/linux-audio-capture", + "version": "0.0.0", + "description": "", + "private": true, + "license": "AGPL-3.0-or-later", + "os": [ + "linux" + ], + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "linux-audio-capture.linux-x64-gnu.node", + "linux-audio-capture.linux-arm64-gnu.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "cargo test --manifest-path Cargo.toml" + } +} diff --git a/fluxer_desktop/native/linux-audio-capture/src/audio_contract.rs b/fluxer_desktop/native/linux-audio-capture/src/audio_contract.rs new file mode 100644 index 0000000..224c7e3 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/src/audio_contract.rs @@ -0,0 +1,147 @@ +#![allow(dead_code)] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub const DIRECT_CAPTURE_SAMPLE_RATE: u32 = 48_000; +pub const DIRECT_CAPTURE_CHANNELS: u32 = 2; +pub const DIRECT_CAPTURE_LATENCY_FRAMES: u32 = 4_096; +pub const DIRECT_CAPTURE_READ_CHUNK_US: u32 = 20_000; +pub const DIRECT_CAPTURE_MAX_SAMPLES: usize = + DIRECT_CAPTURE_SAMPLE_RATE as usize * DIRECT_CAPTURE_CHANNELS as usize * 2; +pub const DIRECT_CAPTURE_MAX_READ_SAMPLES: usize = + DIRECT_CAPTURE_SAMPLE_RATE as usize * DIRECT_CAPTURE_CHANNELS as usize / 50; +pub const MAX_ROUTING_RULE_PATTERNS: u32 = 64; +pub const MAX_ROUTING_RULE_KEYS_PER_PATTERN: u32 = 32; +pub const MAX_ROUTING_RULE_KEY_LENGTH: usize = 128; +pub const MAX_ROUTING_RULE_VALUE_LENGTH: usize = 512; +pub const MAX_INVENTORY_FIELDS: u32 = 32; +pub const MAX_INVENTORY_FIELD_LENGTH: usize = 128; + +pub fn whole_frame_sample_count(sample_count: usize, channels: u32) -> usize { + if channels == 0 { + return 0; + } + let channel_count = channels as usize; + sample_count - (sample_count % channel_count) +} + +pub fn direct_whole_frame_sample_count(sample_count: usize) -> usize { + whole_frame_sample_count(sample_count, DIRECT_CAPTURE_CHANNELS) +} + +pub fn bounded_direct_read_sample_count(available: usize) -> usize { + bounded_direct_read_sample_count_for_format( + available, + DIRECT_CAPTURE_SAMPLE_RATE, + DIRECT_CAPTURE_CHANNELS, + ) +} + +pub fn sample_count_for_duration_us(sample_rate: u32, channels: u32, duration_us: u32) -> usize { + if sample_rate == 0 || channels == 0 || duration_us == 0 { + return 0; + } + let frames = ((u128::from(sample_rate) * u128::from(duration_us)) / 1_000_000).max(1) as usize; + frames.saturating_mul(channels as usize) +} + +pub fn duration_us_for_sample_count(sample_count: usize, sample_rate: u32, channels: u32) -> i64 { + if sample_rate == 0 || channels == 0 { + return 0; + } + let frames = sample_count / channels as usize; + ((frames as u128 * 1_000_000) / u128::from(sample_rate)).min(i64::MAX as u128) as i64 +} + +pub fn bounded_direct_read_sample_count_for_format( + available: usize, + sample_rate: u32, + channels: u32, +) -> usize { + let max = sample_count_for_duration_us(sample_rate, channels, DIRECT_CAPTURE_READ_CHUNK_US); + whole_frame_sample_count(available.min(max), channels) +} + +pub fn bounded_direct_append_slice(input: &[f32]) -> &[f32] { + let whole = direct_whole_frame_sample_count(input.len()); + let framed = &input[..whole]; + if framed.len() > DIRECT_CAPTURE_MAX_SAMPLES { + &framed[framed.len() - DIRECT_CAPTURE_MAX_SAMPLES..] + } else { + framed + } +} + +pub fn direct_capture_latency_fraction() -> String { + format!("{DIRECT_CAPTURE_LATENCY_FRAMES}/{DIRECT_CAPTURE_SAMPLE_RATE}") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn whole_frame_sample_count_trims_incomplete_channel_frames() { + assert_eq!(0, whole_frame_sample_count(1, 2)); + assert_eq!(2, whole_frame_sample_count(2, 2)); + assert_eq!(4, whole_frame_sample_count(5, 2)); + assert_eq!(6, whole_frame_sample_count(7, 3)); + assert_eq!(0, whole_frame_sample_count(7, 0)); + } + + #[test] + fn direct_read_count_is_bounded_and_stereo_aligned() { + assert_eq!(0, bounded_direct_read_sample_count(1)); + assert_eq!(2, bounded_direct_read_sample_count(3)); + assert_eq!( + DIRECT_CAPTURE_MAX_READ_SAMPLES, + bounded_direct_read_sample_count(DIRECT_CAPTURE_MAX_READ_SAMPLES + 1), + ); + } + + #[test] + fn direct_read_count_uses_stable_twenty_ms_chunks() { + assert_eq!(1_920, DIRECT_CAPTURE_MAX_READ_SAMPLES); + assert_eq!( + 1_920, + bounded_direct_read_sample_count_for_format(9_600, 48_000, 2) + ); + assert_eq!( + 1_764, + bounded_direct_read_sample_count_for_format(9_600, 44_100, 2) + ); + } + + #[test] + fn sample_duration_conversion_uses_whole_audio_frames() { + assert_eq!(1_920, sample_count_for_duration_us(48_000, 2, 20_000)); + assert_eq!(20_000, duration_us_for_sample_count(1_920, 48_000, 2)); + assert_eq!(0, sample_count_for_duration_us(0, 2, 20_000)); + assert_eq!(0, duration_us_for_sample_count(1_920, 0, 2)); + } + + #[test] + fn direct_append_slice_keeps_only_complete_stereo_samples_within_queue_cap() { + let samples: [f32; 5] = [1.0, 2.0, 3.0, 4.0, 5.0]; + let trimmed = bounded_direct_append_slice(&samples); + assert_eq!(4, trimmed.len()); + assert_eq!(&samples[..4], trimmed); + } + + #[test] + fn direct_capture_latency_matches_stable_screen_share_buffer() { + assert_eq!("4096/48000", direct_capture_latency_fraction()); + } + + #[test] + fn routing_parser_caps_are_intentionally_finite() { + const { + assert!(MAX_ROUTING_RULE_PATTERNS > 0); + assert!(MAX_ROUTING_RULE_KEYS_PER_PATTERN > 0); + assert!(MAX_ROUTING_RULE_KEY_LENGTH > 0); + assert!(MAX_ROUTING_RULE_VALUE_LENGTH >= MAX_ROUTING_RULE_KEY_LENGTH); + assert!(MAX_INVENTORY_FIELDS <= MAX_ROUTING_RULE_KEYS_PER_PATTERN); + assert!(MAX_INVENTORY_FIELD_LENGTH <= MAX_ROUTING_RULE_KEY_LENGTH); + } + } +} diff --git a/fluxer_desktop/native/linux-audio-capture/src/audio_mix_runtime.rs b/fluxer_desktop/native/linux-audio-capture/src/audio_mix_runtime.rs new file mode 100644 index 0000000..15bc558 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/src/audio_mix_runtime.rs @@ -0,0 +1,1689 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![allow(dead_code)] +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use std::sync::{Arc, Mutex}; +use std::thread::{self, JoinHandle}; + +use fluxer_audio_apm::{ + APM_MAX_FRAME_SAMPLES, ApmConfig, ApmError, AudioProcessor, StubAudioProcessor, + expected_frame_samples, +}; +use fluxer_audio_mix::{ + AUDIO_OUTPUT_FRAMES, AudioMixError, AudioMixSession, MAX_MIX_SOURCES, MixTickResult, + SourceRing, SourceRingConsumer, SourceRingProducer, +}; +use fluxer_audio_timing::{AudioTimingSmoother, SourceGainRamp, StaleSourceTracker}; +use fluxer_rt_thread::{ + MonotonicClock, PriorityProfile, RealtimePriorityGuard, RtError, RtOutcome, + SystemMonotonicClock, TickDriver, +}; + +use crate::ignore_audio_runtime::{ + IgnoreAudioPolicy, IgnoreAudioSourceResetEvent, IgnoreAudioSourceState, IgnoreAudioTick, + compute_source_age_ns, +}; + +pub const MIX_TICK_PERIOD_NS: u64 = 21_333_333; + +pub const SOURCE_RING_CAP_FRAMES: usize = 8_192; + +pub const MIX_SAMPLE_RATE_HZ: u32 = 48_000; + +pub const MIX_CHANNELS: u16 = 1; + +pub const OUTPUT_BUFFER_POOL_DEPTH: usize = 4; + +const NEVER_PUSHED_SENTINEL: u64 = u64::MAX; + +pub struct DynMonotonicClock { + inner: Arc, +} + +impl DynMonotonicClock { + pub fn new(inner: Arc) -> Self { + Self { inner } + } +} + +impl MonotonicClock for DynMonotonicClock { + fn now_ns(&self) -> u64 { + self.inner.now_ns() + } +} + +#[derive(Debug)] +pub enum MixRuntimeError { + NoSources, + TooManySources { requested: usize, limit: usize }, + Mix(AudioMixError), + Apm(ApmError), + Rt(RtError), + AlreadyRunning, + ThreadSpawn, +} + +impl core::fmt::Display for MixRuntimeError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + MixRuntimeError::NoSources => write!(f, "mix runtime requires at least one source"), + MixRuntimeError::TooManySources { requested, limit } => { + write!(f, "source count {requested} exceeds limit {limit}") + } + MixRuntimeError::Mix(e) => write!(f, "{e}"), + MixRuntimeError::Apm(e) => write!(f, "{e}"), + MixRuntimeError::Rt(e) => write!(f, "{e}"), + MixRuntimeError::AlreadyRunning => write!(f, "mix runtime already running"), + MixRuntimeError::ThreadSpawn => write!(f, "failed to spawn mix thread"), + } + } +} + +impl std::error::Error for MixRuntimeError {} + +impl From for MixRuntimeError { + fn from(value: AudioMixError) -> Self { + MixRuntimeError::Mix(value) + } +} + +impl From for MixRuntimeError { + fn from(value: ApmError) -> Self { + MixRuntimeError::Apm(value) + } +} + +#[derive(Debug, Clone)] +pub struct MixOutputFrame { + pub samples: Arc<[i16; AUDIO_OUTPUT_FRAMES]>, + pub tick_index: u64, + pub scheduled_ns: u64, + pub actual_ns: u64, + pub total_drained: u64, + pub total_silence: u64, + pub saturated_samples: u32, +} + +impl PartialEq for MixOutputFrame { + fn eq(&self, other: &Self) -> bool { + self.tick_index == other.tick_index + && self.scheduled_ns == other.scheduled_ns + && self.actual_ns == other.actual_ns + && self.total_drained == other.total_drained + && self.total_silence == other.total_silence + && self.saturated_samples == other.saturated_samples + && self.samples.as_ref() == other.samples.as_ref() + } +} + +pub trait MixOutputSink: Send { + fn on_mixed_frame(&mut self, frame: &MixOutputFrame); + + fn on_source_reset(&mut self, event: &IgnoreAudioSourceResetEvent); +} + +pub struct NullMixOutputSink; + +impl MixOutputSink for NullMixOutputSink { + fn on_mixed_frame(&mut self, _frame: &MixOutputFrame) {} + fn on_source_reset(&mut self, _event: &IgnoreAudioSourceResetEvent) {} +} + +pub struct CapturedMixOutputSink { + frames: Arc>>, + resets: Arc>>, +} + +impl CapturedMixOutputSink { + pub fn new() -> Self { + Self { + frames: Arc::new(Mutex::new(Vec::new())), + resets: Arc::new(Mutex::new(Vec::new())), + } + } + + pub fn frames(&self) -> Arc>> { + Arc::clone(&self.frames) + } + + pub fn resets(&self) -> Arc>> { + Arc::clone(&self.resets) + } +} + +impl Default for CapturedMixOutputSink { + fn default() -> Self { + Self::new() + } +} + +impl MixOutputSink for CapturedMixOutputSink { + fn on_mixed_frame(&mut self, frame: &MixOutputFrame) { + if let Ok(mut guard) = self.frames.lock() { + guard.push(frame.clone()); + } + } + + fn on_source_reset(&mut self, event: &IgnoreAudioSourceResetEvent) { + if let Ok(mut guard) = self.resets.lock() { + guard.push(*event); + } + } +} + +pub struct SourceFreshnessHandle { + last_push_ns: Arc, + registered_at_ns: u64, + clock: Arc, +} + +impl SourceFreshnessHandle { + pub fn mark_push_now(&self) { + let now = self.clock.now_ns(); + assert!(now > 0); + self.last_push_ns.store(now, Ordering::Release); + } + + pub fn mark_push_at(&self, push_ns: u64) { + assert!(push_ns > 0); + self.last_push_ns.store(push_ns, Ordering::Release); + } + + pub fn last_push_ns(&self) -> u64 { + self.last_push_ns.load(Ordering::Acquire) + } + + pub fn registered_at_ns(&self) -> u64 { + assert!(self.registered_at_ns != NEVER_PUSHED_SENTINEL); + self.registered_at_ns + } +} + +pub struct CaptureSource { + pub source_id: u64, + pub producer: SourceRingProducer, + pub apm: StubAudioProcessor, + apm_sample_rate_hz: u32, + apm_channels: u16, + apm_frame_samples: usize, + freshness: SourceFreshnessHandle, +} + +impl CaptureSource { + pub fn create( + source_id: u64, + sample_rate_hz: u32, + channels: u16, + ) -> Result<(Self, SourceRingConsumer), MixRuntimeError> { + assert!(source_id != 0); + assert!(sample_rate_hz >= 8_000); + assert!(channels >= 1); + let (producer, consumer) = SourceRing::create(SOURCE_RING_CAP_FRAMES, sample_rate_hz)?; + let apm = StubAudioProcessor::new(ApmConfig::default(), sample_rate_hz, channels)?; + let apm_frame_samples = expected_frame_samples(sample_rate_hz, channels); + assert!(apm_frame_samples > 0); + assert!(apm_frame_samples <= APM_MAX_FRAME_SAMPLES * (channels as usize)); + let clock_for_freshness: Arc = Arc::new(SystemMonotonicClock::new()); + let registered_at_ns = clock_for_freshness.now_ns().max(1); + assert!(registered_at_ns != NEVER_PUSHED_SENTINEL); + let freshness = SourceFreshnessHandle { + last_push_ns: Arc::new(AtomicU64::new(NEVER_PUSHED_SENTINEL)), + registered_at_ns, + clock: clock_for_freshness, + }; + let me = Self { + source_id, + producer, + apm, + apm_sample_rate_hz: sample_rate_hz, + apm_channels: channels, + apm_frame_samples, + freshness, + }; + Ok((me, consumer)) + } + + pub fn ingest(&mut self, samples: &mut [i16]) -> Result { + assert!(!samples.is_empty()); + assert_eq!(samples.len() % self.apm_frame_samples, 0); + for chunk in samples.chunks_mut(self.apm_frame_samples) { + self.apm + .process_capture_frame(chunk, self.apm_sample_rate_hz, self.apm_channels)?; + } + let pushed = self.producer.try_push_slice(samples); + self.freshness.mark_push_now(); + Ok(pushed) + } + + pub fn ingest_skip_apm(&mut self, samples: &[i16]) -> usize { + assert!(!samples.is_empty()); + let pushed = self.producer.try_push_slice(samples); + self.freshness.mark_push_now(); + pushed + } + + pub fn source_id(&self) -> u64 { + assert!(self.source_id != 0); + self.source_id + } + + pub fn buffered_frames(&self) -> u64 { + self.producer + .capacity_frames() + .saturating_sub(self.producer.slots()) as u64 + } + + pub fn freshness_handle(&self) -> &SourceFreshnessHandle { + &self.freshness + } + + pub fn last_push_ns_arc(&self) -> Arc { + Arc::clone(&self.freshness.last_push_ns) + } + + pub fn apm_frames_processed(&self) -> u64 { + self.apm.capture_frames_processed() + } +} + +struct BuilderSourceEntry { + source_id: u64, + consumer: SourceRingConsumer, + last_push_ns: Arc, + registered_at_ns: u64, +} + +pub struct AudioMixRuntimeBuilder { + consumers: Vec, + sample_rate_hz: u32, + period_ns: u64, + clock: Arc, +} + +impl AudioMixRuntimeBuilder { + pub fn new() -> Self { + Self { + consumers: Vec::new(), + sample_rate_hz: MIX_SAMPLE_RATE_HZ, + period_ns: MIX_TICK_PERIOD_NS, + clock: Arc::new(SystemMonotonicClock::new()), + } + } + + pub fn with_period_ns(mut self, period_ns: u64) -> Self { + assert!(period_ns > 0); + self.period_ns = period_ns; + self + } + + pub fn with_sample_rate_hz(mut self, sample_rate_hz: u32) -> Self { + assert!(sample_rate_hz >= 8_000); + self.sample_rate_hz = sample_rate_hz; + self + } + + pub fn with_clock(mut self, clock: Arc) -> Self { + self.clock = clock; + self + } + + pub fn add_source(mut self, source_id: u64, consumer: SourceRingConsumer) -> Self { + assert!(self.consumers.len() < MAX_MIX_SOURCES); + assert!(source_id != 0); + let placeholder = Arc::new(AtomicU64::new(NEVER_PUSHED_SENTINEL)); + let registered_at_ns = self.clock.now_ns().max(1); + assert!(registered_at_ns != NEVER_PUSHED_SENTINEL); + self.consumers.push(BuilderSourceEntry { + source_id, + consumer, + last_push_ns: placeholder, + registered_at_ns, + }); + self + } + + pub fn add_source_with_freshness( + mut self, + source_id: u64, + consumer: SourceRingConsumer, + last_push_ns: Arc, + ) -> Self { + assert!(self.consumers.len() < MAX_MIX_SOURCES); + assert!(source_id != 0); + let registered_at_ns = self.clock.now_ns().max(1); + assert!(registered_at_ns != NEVER_PUSHED_SENTINEL); + self.consumers.push(BuilderSourceEntry { + source_id, + consumer, + last_push_ns, + registered_at_ns, + }); + self + } + + pub fn build( + self, + sink: S, + ) -> Result { + if self.consumers.is_empty() { + return Err(MixRuntimeError::NoSources); + } + if self.consumers.len() > MAX_MIX_SOURCES { + return Err(MixRuntimeError::TooManySources { + requested: self.consumers.len(), + limit: MAX_MIX_SOURCES, + }); + } + let consumer_count = self.consumers.len(); + let mut source_ids: Vec = Vec::with_capacity(consumer_count); + let mut consumers: Vec = Vec::with_capacity(consumer_count); + let mut last_push_ns: Vec> = Vec::with_capacity(consumer_count); + let mut registered_at_ns: Vec = Vec::with_capacity(consumer_count); + let mut smoothers: Vec = Vec::with_capacity(consumer_count); + let mut ramps: Vec = Vec::with_capacity(consumer_count); + let mut stale_tracker = StaleSourceTracker::new(consumer_count) + .map_err(|_| MixRuntimeError::Mix(AudioMixError::ZeroSources))?; + for entry in self.consumers { + source_ids.push(entry.source_id); + consumers.push(entry.consumer); + last_push_ns.push(entry.last_push_ns); + registered_at_ns.push(entry.registered_at_ns); + let smoother = AudioTimingSmoother::new(entry.source_id, self.sample_rate_hz) + .map_err(|_| MixRuntimeError::Mix(AudioMixError::ZeroSources))?; + smoothers.push(smoother); + ramps.push(SourceGainRamp::new()); + stale_tracker + .register_source(entry.source_id, entry.registered_at_ns) + .map_err(|_| MixRuntimeError::Mix(AudioMixError::ZeroSources))?; + } + let session = AudioMixSession::new(consumers, AUDIO_OUTPUT_FRAMES)?; + let policy = IgnoreAudioPolicy::new(self.sample_rate_hz) + .map_err(|_| MixRuntimeError::Mix(AudioMixError::ZeroSources))?; + let pool = build_output_pool(); + let last_marked_push_ns = vec![NEVER_PUSHED_SENTINEL; consumer_count]; + Ok(AudioMixRuntime { + session: Some(session), + policy: Some(policy), + source_ids, + last_push_ns, + last_marked_push_ns, + registered_at_ns, + smoothers, + ramps, + stale_tracker, + mark_pushed_total: Arc::new(AtomicU64::new(0)), + clock: self.clock, + sample_rate_hz: self.sample_rate_hz, + period_ns: self.period_ns, + sink: Some(Box::new(sink)), + running: Arc::new(AtomicBool::new(false)), + thread: None, + rt_outcome: Arc::new(Mutex::new(None)), + manual_tick_index: 0, + pre_stats: [0u64; MAX_MIX_SOURCES], + output_pool: pool, + output_pool_cursor: 0, + }) + } +} + +impl Default for AudioMixRuntimeBuilder { + fn default() -> Self { + Self::new() + } +} + +fn build_output_pool() -> [Arc<[i16; AUDIO_OUTPUT_FRAMES]>; OUTPUT_BUFFER_POOL_DEPTH] { + [ + Arc::new([0i16; AUDIO_OUTPUT_FRAMES]), + Arc::new([0i16; AUDIO_OUTPUT_FRAMES]), + Arc::new([0i16; AUDIO_OUTPUT_FRAMES]), + Arc::new([0i16; AUDIO_OUTPUT_FRAMES]), + ] +} + +pub struct AudioMixRuntime { + session: Option, + policy: Option, + source_ids: Vec, + last_push_ns: Vec>, + last_marked_push_ns: Vec, + registered_at_ns: Vec, + smoothers: Vec, + ramps: Vec, + stale_tracker: StaleSourceTracker, + mark_pushed_total: Arc, + clock: Arc, + sample_rate_hz: u32, + period_ns: u64, + sink: Option>, + running: Arc, + thread: Option>, + rt_outcome: Arc>>, + manual_tick_index: u64, + pre_stats: [u64; MAX_MIX_SOURCES], + output_pool: [Arc<[i16; AUDIO_OUTPUT_FRAMES]>; OUTPUT_BUFFER_POOL_DEPTH], + output_pool_cursor: usize, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MixRtOutcome { + Acquired, + PartialFallback, + Denied, +} + +impl AudioMixRuntime { + pub fn source_count(&self) -> usize { + assert!(!self.source_ids.is_empty()); + assert!(self.source_ids.len() <= MAX_MIX_SOURCES); + self.source_ids.len() + } + + pub fn sample_rate_hz(&self) -> u32 { + assert!(self.sample_rate_hz >= 8_000); + self.sample_rate_hz + } + + pub fn period_ns(&self) -> u64 { + assert!(self.period_ns > 0); + self.period_ns + } + + pub fn run_one_tick_blocking( + &mut self, + tick_at_ns: u64, + ) -> Result { + let tick_index = self.manual_tick_index; + self.manual_tick_index = self.manual_tick_index.saturating_add(1); + let tick_info = fluxer_rt_thread::TickInfo { + tick_index, + scheduled_ns: tick_at_ns, + actual_ns: tick_at_ns, + lag_ns: 0, + }; + self.run_tick_internal(tick_info) + } + + pub fn observe_source_pushes_without_mix( + &mut self, + tick_at_ns: u64, + ) -> Result { + assert!(tick_at_ns > 0); + let before = self.mark_pushed_total.load(Ordering::Acquire); + let session = self + .session + .as_mut() + .ok_or(MixRuntimeError::AlreadyRunning)?; + let policy = self + .policy + .as_mut() + .ok_or(MixRuntimeError::AlreadyRunning)?; + let sink = self.sink.as_mut().ok_or(MixRuntimeError::AlreadyRunning)?; + let now_ns = self.clock.now_ns(); + snapshot_pre_stats(session, &mut self.pre_stats); + drain_pushes_into_tracker( + &mut self.stale_tracker, + &self.source_ids, + &self.last_push_ns, + &mut self.last_marked_push_ns, + &self.mark_pushed_total, + ); + evaluate_policy_for_all_sources( + policy, + &self.source_ids, + &self.last_push_ns, + &self.registered_at_ns, + &self.pre_stats, + now_ns, + tick_at_ns, + self.period_ns, + sink.as_mut(), + ); + advance_smoothers_and_ramps( + &mut self.smoothers, + &mut self.ramps, + &self.last_push_ns, + tick_at_ns, + self.period_ns, + self.sample_rate_hz, + ); + let after = self.mark_pushed_total.load(Ordering::Acquire); + Ok(after.saturating_sub(before)) + } + + fn run_tick_internal( + &mut self, + tick_info: fluxer_rt_thread::TickInfo, + ) -> Result { + let period_ns = self.period_ns; + let session = self + .session + .as_mut() + .ok_or(MixRuntimeError::AlreadyRunning)?; + let policy = self + .policy + .as_mut() + .ok_or(MixRuntimeError::AlreadyRunning)?; + let sink = self.sink.as_mut().ok_or(MixRuntimeError::AlreadyRunning)?; + let now_ns = self.clock.now_ns(); + snapshot_pre_stats(session, &mut self.pre_stats); + drain_pushes_into_tracker( + &mut self.stale_tracker, + &self.source_ids, + &self.last_push_ns, + &mut self.last_marked_push_ns, + &self.mark_pushed_total, + ); + evaluate_policy_for_all_sources( + policy, + &self.source_ids, + &self.last_push_ns, + &self.registered_at_ns, + &self.pre_stats, + now_ns, + tick_info.actual_ns, + period_ns, + sink.as_mut(), + ); + advance_smoothers_and_ramps( + &mut self.smoothers, + &mut self.ramps, + &self.last_push_ns, + tick_info.actual_ns, + period_ns, + self.sample_rate_hz, + ); + let result: MixTickResult = session.tick(tick_info); + let frame = build_output_frame_pooled( + session, + &result, + &mut self.output_pool, + &mut self.output_pool_cursor, + ); + sink.on_mixed_frame(&frame); + Ok(frame) + } + + pub fn mark_pushed_total(&self) -> u64 { + assert!(!self.source_ids.is_empty()); + let value = self.mark_pushed_total.load(Ordering::Acquire); + assert!(self.source_ids.len() <= MAX_MIX_SOURCES); + value + } + + pub fn mark_pushed_total_arc(&self) -> Arc { + assert!(!self.source_ids.is_empty()); + assert!(self.source_ids.len() <= MAX_MIX_SOURCES); + Arc::clone(&self.mark_pushed_total) + } + + pub fn current_ramp_gain(&self, index: usize) -> f32 { + assert!(index < self.ramps.len()); + self.ramps[index].current_gain() + } + + pub fn smoother_initialised(&self, index: usize) -> bool { + assert!(index < self.smoothers.len()); + self.smoothers[index].initialised() + } + + pub fn is_source_stale(&self, index: usize, now_ns: u64, stale_threshold_ns: u64) -> bool { + assert!(index < self.source_ids.len()); + assert!(self.source_ids.len() <= MAX_MIX_SOURCES); + let id = self.source_ids[index]; + self.stale_tracker + .is_stale_at(index, id, now_ns, stale_threshold_ns) + .unwrap_or(false) + } + + pub fn start(&mut self) -> Result<(), MixRuntimeError> { + if self.running.swap(true, Ordering::AcqRel) { + return Err(MixRuntimeError::AlreadyRunning); + } + let session = self.session.take().ok_or(MixRuntimeError::AlreadyRunning)?; + let policy = self.policy.take().ok_or(MixRuntimeError::AlreadyRunning)?; + let sink = self.sink.take().ok_or(MixRuntimeError::AlreadyRunning)?; + let source_ids = self.source_ids.clone(); + let last_push_ns = self.last_push_ns.clone(); + let registered_at_ns = self.registered_at_ns.clone(); + let period_ns = self.period_ns; + let sample_rate_hz = self.sample_rate_hz; + let running = Arc::clone(&self.running); + let rt_outcome = Arc::clone(&self.rt_outcome); + let clock = Arc::clone(&self.clock); + let pool = self.output_pool.clone(); + let smoothers = std::mem::take(&mut self.smoothers); + let ramps = std::mem::take(&mut self.ramps); + let stale_tracker = std::mem::replace( + &mut self.stale_tracker, + StaleSourceTracker::new(self.source_ids.len()) + .map_err(|_| MixRuntimeError::Mix(AudioMixError::ZeroSources))?, + ); + let last_marked = std::mem::take(&mut self.last_marked_push_ns); + let mark_pushed_total = Arc::clone(&self.mark_pushed_total); + let handle = thread::Builder::new() + .name("fluxer-audio-mix".into()) + .spawn(move || { + spawn_mix_thread(MixThreadArgs { + session, + policy, + source_ids, + last_push_ns, + registered_at_ns, + smoothers, + ramps, + period_ns, + sample_rate_hz, + sink, + running, + rt_outcome, + clock, + pool, + stale_tracker, + last_marked, + mark_pushed_total, + }); + }) + .map_err(|_| MixRuntimeError::ThreadSpawn)?; + self.thread = Some(handle); + Ok(()) + } + + pub fn stop(&mut self) { + self.running.store(false, Ordering::Release); + if let Some(handle) = self.thread.take() { + let _ = handle.join(); + } + } + + pub fn rt_outcome(&self) -> Option { + match self.rt_outcome.lock() { + Ok(guard) => *guard, + Err(_) => None, + } + } + + pub fn last_push_ns_for(&self, index: usize) -> u64 { + assert!(index < self.last_push_ns.len()); + self.last_push_ns[index].load(Ordering::Acquire) + } + + pub fn output_pool_depth(&self) -> usize { + assert_eq!(self.output_pool.len(), OUTPUT_BUFFER_POOL_DEPTH); + OUTPUT_BUFFER_POOL_DEPTH + } +} + +impl Drop for AudioMixRuntime { + fn drop(&mut self) { + self.stop(); + } +} + +struct MixThreadArgs { + session: AudioMixSession, + policy: IgnoreAudioPolicy, + source_ids: Vec, + last_push_ns: Vec>, + registered_at_ns: Vec, + smoothers: Vec, + ramps: Vec, + period_ns: u64, + sample_rate_hz: u32, + sink: Box, + running: Arc, + rt_outcome: Arc>>, + clock: Arc, + pool: [Arc<[i16; AUDIO_OUTPUT_FRAMES]>; OUTPUT_BUFFER_POOL_DEPTH], + stale_tracker: StaleSourceTracker, + last_marked: Vec, + mark_pushed_total: Arc, +} + +fn spawn_mix_thread(args: MixThreadArgs) { + let MixThreadArgs { + mut session, + mut policy, + source_ids, + last_push_ns, + registered_at_ns, + mut smoothers, + mut ramps, + period_ns, + sample_rate_hz, + mut sink, + running, + rt_outcome, + clock, + pool, + mut stale_tracker, + mut last_marked, + mark_pushed_total, + } = args; + assert!(!source_ids.is_empty()); + assert_eq!(source_ids.len(), last_push_ns.len()); + assert_eq!(source_ids.len(), registered_at_ns.len()); + assert_eq!(source_ids.len(), last_marked.len()); + let guard_result = RealtimePriorityGuard::acquire(PriorityProfile::Audio); + let (rt_state, _rt_guard) = match guard_result { + Ok(guard) => { + let state = match guard.outcome() { + RtOutcome::Acquired => MixRtOutcome::Acquired, + RtOutcome::PartialFallback => MixRtOutcome::PartialFallback, + }; + (state, Some(guard)) + } + Err(_) => (MixRtOutcome::Denied, None), + }; + if let Ok(mut guard_slot) = rt_outcome.lock() { + *guard_slot = Some(rt_state); + } + let mut pre_stats = [0u64; MAX_MIX_SOURCES]; + let mut pool = pool; + let mut cursor: usize = 0; + let _ = run_mix_thread_loop(MixLoopArgs { + session: &mut session, + policy: &mut policy, + source_ids: &source_ids, + last_push_ns: &last_push_ns, + registered_at_ns: ®istered_at_ns, + smoothers: &mut smoothers, + ramps: &mut ramps, + period_ns, + sample_rate_hz, + sink: sink.as_mut(), + running: &running, + clock: &clock, + pre_stats: &mut pre_stats, + pool: &mut pool, + cursor: &mut cursor, + stale_tracker: &mut stale_tracker, + last_marked: &mut last_marked, + mark_pushed_total: &mark_pushed_total, + }); +} + +struct MixLoopArgs<'a> { + session: &'a mut AudioMixSession, + policy: &'a mut IgnoreAudioPolicy, + source_ids: &'a [u64], + last_push_ns: &'a [Arc], + registered_at_ns: &'a [u64], + smoothers: &'a mut [AudioTimingSmoother], + ramps: &'a mut [SourceGainRamp], + period_ns: u64, + sample_rate_hz: u32, + sink: &'a mut dyn MixOutputSink, + running: &'a AtomicBool, + clock: &'a Arc, + pre_stats: &'a mut [u64; MAX_MIX_SOURCES], + pool: &'a mut [Arc<[i16; AUDIO_OUTPUT_FRAMES]>; OUTPUT_BUFFER_POOL_DEPTH], + cursor: &'a mut usize, + stale_tracker: &'a mut StaleSourceTracker, + last_marked: &'a mut [u64], + mark_pushed_total: &'a Arc, +} + +fn run_mix_thread_loop(args: MixLoopArgs<'_>) -> Result<(), MixRuntimeError> { + let MixLoopArgs { + session, + policy, + source_ids, + last_push_ns, + registered_at_ns, + smoothers, + ramps, + period_ns, + sample_rate_hz, + sink, + running, + clock, + pre_stats, + pool, + cursor, + stale_tracker, + last_marked, + mark_pushed_total, + } = args; + assert!(!source_ids.is_empty()); + assert_eq!(source_ids.len(), last_push_ns.len()); + let driver_clock = Arc::new(DynMonotonicClock::new(Arc::clone(clock))); + let mut driver = TickDriver::new(driver_clock, period_ns) + .map_err(|_| MixRuntimeError::Mix(AudioMixError::ZeroSources))?; + while running.load(Ordering::Acquire) { + let tick_info = match driver.wait_until_next_tick() { + Ok(info) => info, + Err(_) => break, + }; + if !running.load(Ordering::Acquire) { + break; + } + let _ = run_single_tick(SingleTickArgs { + session, + policy, + source_ids, + last_push_ns, + registered_at_ns, + smoothers, + ramps, + period_ns, + sample_rate_hz, + tick_info, + sink, + clock, + pre_stats, + pool, + cursor, + stale_tracker, + last_marked, + mark_pushed_total, + }); + } + Ok(()) +} + +struct SingleTickArgs<'a> { + session: &'a mut AudioMixSession, + policy: &'a mut IgnoreAudioPolicy, + source_ids: &'a [u64], + last_push_ns: &'a [Arc], + registered_at_ns: &'a [u64], + smoothers: &'a mut [AudioTimingSmoother], + ramps: &'a mut [SourceGainRamp], + period_ns: u64, + sample_rate_hz: u32, + tick_info: fluxer_rt_thread::TickInfo, + sink: &'a mut dyn MixOutputSink, + clock: &'a Arc, + pre_stats: &'a mut [u64; MAX_MIX_SOURCES], + pool: &'a mut [Arc<[i16; AUDIO_OUTPUT_FRAMES]>; OUTPUT_BUFFER_POOL_DEPTH], + cursor: &'a mut usize, + stale_tracker: &'a mut StaleSourceTracker, + last_marked: &'a mut [u64], + mark_pushed_total: &'a Arc, +} + +fn run_single_tick(args: SingleTickArgs<'_>) -> Result { + let SingleTickArgs { + session, + policy, + source_ids, + last_push_ns, + registered_at_ns, + smoothers, + ramps, + period_ns, + sample_rate_hz, + tick_info, + sink, + clock, + pre_stats, + pool, + cursor, + stale_tracker, + last_marked, + mark_pushed_total, + } = args; + assert!(!source_ids.is_empty()); + assert_eq!(source_ids.len(), session.source_count()); + assert_eq!(source_ids.len(), last_push_ns.len()); + let now_ns = clock.now_ns(); + snapshot_pre_stats(session, pre_stats); + drain_pushes_into_tracker( + stale_tracker, + source_ids, + last_push_ns, + last_marked, + mark_pushed_total, + ); + evaluate_policy_for_all_sources( + policy, + source_ids, + last_push_ns, + registered_at_ns, + pre_stats, + now_ns, + tick_info.actual_ns, + period_ns, + sink, + ); + advance_smoothers_and_ramps( + smoothers, + ramps, + last_push_ns, + tick_info.actual_ns, + period_ns, + sample_rate_hz, + ); + let result: MixTickResult = session.tick(tick_info); + let frame = build_output_frame_pooled(session, &result, pool, cursor); + sink.on_mixed_frame(&frame); + Ok(frame) +} + +fn drain_pushes_into_tracker( + stale_tracker: &mut StaleSourceTracker, + source_ids: &[u64], + last_push_ns: &[Arc], + last_marked: &mut [u64], + mark_pushed_total: &Arc, +) { + assert_eq!(source_ids.len(), last_push_ns.len()); + assert_eq!(source_ids.len(), last_marked.len()); + assert_eq!(source_ids.len(), stale_tracker.len()); + for idx in 0..source_ids.len() { + let observed = last_push_ns[idx].load(Ordering::Acquire); + if observed == NEVER_PUSHED_SENTINEL { + continue; + } + if observed == last_marked[idx] { + continue; + } + let source_id = source_ids[idx]; + if stale_tracker + .mark_pushed_at(idx, source_id, observed) + .is_ok() + { + last_marked[idx] = observed; + mark_pushed_total.fetch_add(1, Ordering::AcqRel); + } + } +} + +fn advance_smoothers_and_ramps( + smoothers: &mut [AudioTimingSmoother], + ramps: &mut [SourceGainRamp], + last_push_ns: &[Arc], + tick_at_ns: u64, + period_ns: u64, + sample_rate_hz: u32, +) { + assert_eq!(smoothers.len(), ramps.len()); + assert_eq!(smoothers.len(), last_push_ns.len()); + let frames_per_tick = compute_frames_per_tick(period_ns, sample_rate_hz); + for index in 0..smoothers.len() { + let last_push = last_push_ns[index].load(Ordering::Acquire); + ramps[index].advance_tick(); + if last_push == NEVER_PUSHED_SENTINEL { + continue; + } + let smoother = &mut smoothers[index]; + let input = fluxer_audio_timing::SmootherInput { + media_ts_ns: last_push, + frames: frames_per_tick, + wall_ns: tick_at_ns, + }; + let _ = smoother.adjust(input); + } +} + +fn compute_frames_per_tick(period_ns: u64, sample_rate_hz: u32) -> u32 { + assert!(period_ns > 0); + assert!(sample_rate_hz >= 8_000); + let product = (period_ns as u128) * (sample_rate_hz as u128); + let frames = product / 1_000_000_000u128; + let frames_u32 = frames.min(u32::MAX as u128) as u32; + frames_u32.max(1) +} + +fn snapshot_pre_stats(session: &AudioMixSession, pre_stats: &mut [u64; MAX_MIX_SOURCES]) { + let stats = session.per_source_stats(); + assert!(stats.len() <= MAX_MIX_SOURCES); + for (slot, stat) in pre_stats.iter_mut().zip(stats.iter()) { + *slot = stat.drained_count as u64; + } +} + +#[allow(clippy::too_many_arguments)] +fn evaluate_policy_for_all_sources( + policy: &mut IgnoreAudioPolicy, + source_ids: &[u64], + last_push_ns: &[Arc], + registered_at_ns: &[u64], + pre_stats: &[u64; MAX_MIX_SOURCES], + now_ns: u64, + tick_at_ns: u64, + period_ns: u64, + sink: &mut dyn MixOutputSink, +) { + assert_eq!(source_ids.len(), last_push_ns.len()); + assert_eq!(source_ids.len(), registered_at_ns.len()); + let tick = IgnoreAudioTick { + at_ns: tick_at_ns, + period_ns, + }; + for (idx, source_id) in source_ids.iter().enumerate() { + let drained_before = pre_stats[idx]; + let last_push = last_push_ns[idx].load(Ordering::Acquire); + let age_ns = compute_age_ns(last_push, registered_at_ns[idx], now_ns); + let state = IgnoreAudioSourceState { + id: *source_id, + buffered_frames: drained_before, + last_frame_age_ns: age_ns, + is_muted: false, + }; + if let Ok(eval) = policy.evaluate(&state, tick) + && let Some(event) = eval.event + { + sink.on_source_reset(&event); + } + } +} + +fn compute_age_ns(last_push_ns: u64, registered_at_ns: u64, now_ns: u64) -> u64 { + compute_source_age_ns(last_push_ns, registered_at_ns, now_ns) +} + +fn build_output_frame_pooled( + session: &AudioMixSession, + result: &MixTickResult, + pool: &mut [Arc<[i16; AUDIO_OUTPUT_FRAMES]>; OUTPUT_BUFFER_POOL_DEPTH], + cursor: &mut usize, +) -> MixOutputFrame { + let slot_index = *cursor; + assert!(slot_index < OUTPUT_BUFFER_POOL_DEPTH); + let src = session.last_output(); + let target_arc = &mut pool[slot_index]; + let target = Arc::make_mut(target_arc); + target.copy_from_slice(src); + assert_eq!(target.len(), AUDIO_OUTPUT_FRAMES); + *cursor = (slot_index + 1) % OUTPUT_BUFFER_POOL_DEPTH; + MixOutputFrame { + samples: Arc::clone(&pool[slot_index]), + tick_index: result.tick_index, + scheduled_ns: result.scheduled_ns, + actual_ns: result.actual_ns, + total_drained: result.total_drained, + total_silence: result.total_silence, + saturated_samples: result.saturated_samples, + } +} + +pub fn audio_apm_frame_samples(sample_rate_hz: u32, channels: u16) -> usize { + assert!(sample_rate_hz >= 8_000); + assert!(channels >= 1); + expected_frame_samples(sample_rate_hz, channels) +} + +#[cfg(test)] +pub(crate) static ALLOC_PROBE: std::sync::atomic::AtomicUsize = + std::sync::atomic::AtomicUsize::new(0); + +#[cfg(test)] +thread_local! { + pub(crate) static THREAD_ALLOC_TRACK: std::cell::Cell = const { std::cell::Cell::new(false) }; + pub(crate) static THREAD_ALLOC_COUNT: std::cell::Cell = const { std::cell::Cell::new(0) }; +} + +#[cfg(test)] +struct CountingAllocator; + +#[cfg(test)] +unsafe impl std::alloc::GlobalAlloc for CountingAllocator { + unsafe fn alloc(&self, layout: std::alloc::Layout) -> *mut u8 { + ALLOC_PROBE.fetch_add(1, Ordering::Relaxed); + let _ = THREAD_ALLOC_TRACK.try_with(|tracked| { + if tracked.get() { + let _ = THREAD_ALLOC_COUNT.try_with(|c| c.set(c.get().saturating_add(1))); + } + }); + unsafe { std::alloc::System.alloc(layout) } + } + + unsafe fn dealloc(&self, ptr: *mut u8, layout: std::alloc::Layout) { + unsafe { std::alloc::System.dealloc(ptr, layout) } + } +} + +#[cfg(test)] +#[global_allocator] +static ALLOCATOR: CountingAllocator = CountingAllocator; + +#[cfg(test)] +pub(crate) fn begin_thread_alloc_probe() { + THREAD_ALLOC_COUNT.with(|c| c.set(0)); + THREAD_ALLOC_TRACK.with(|t| t.set(true)); +} + +#[cfg(test)] +pub(crate) fn end_thread_alloc_probe() -> usize { + THREAD_ALLOC_TRACK.with(|t| t.set(false)); + THREAD_ALLOC_COUNT.with(|c| c.get()) +} + +#[cfg(test)] +mod tests { + use super::*; + + use fluxer_rt_thread::TickInfo; + + fn snapshot_allocs() -> usize { + ALLOC_PROBE.load(Ordering::Relaxed) + } + + fn begin_thread_alloc_count() { + begin_thread_alloc_probe(); + } + + fn end_thread_alloc_count() -> usize { + end_thread_alloc_probe() + } + + fn build_test_runtime( + source_count: usize, + ) -> (Vec, AudioMixRuntime, CapturedMixOutputSink) { + assert!(source_count > 0); + let mut sources: Vec = Vec::with_capacity(source_count); + let mut builder = AudioMixRuntimeBuilder::new(); + for n in 0..source_count { + let (source, consumer) = + CaptureSource::create(n as u64 + 1, MIX_SAMPLE_RATE_HZ, MIX_CHANNELS) + .expect("source"); + let push_ns = source.last_push_ns_arc(); + builder = builder.add_source_with_freshness(n as u64 + 1, consumer, push_ns); + sources.push(source); + } + let sink = CapturedMixOutputSink::new(); + let cloned_frames = sink.frames(); + let cloned_resets = sink.resets(); + let runtime = builder + .build(CapturedMixOutputSink { + frames: cloned_frames, + resets: cloned_resets, + }) + .expect("build"); + (sources, runtime, sink) + } + + fn make_pure_tone(amplitude: i16, len: usize) -> Vec { + assert!(len > 0); + (0..len).map(|n| (n as i16 % 8) * amplitude / 8).collect() + } + + #[derive(Debug)] + struct FakeClock { + value_ns: AtomicU64, + } + + impl FakeClock { + fn new(initial_ns: u64) -> Self { + Self { + value_ns: AtomicU64::new(initial_ns), + } + } + fn set(&self, value_ns: u64) { + self.value_ns.store(value_ns, Ordering::Release); + } + } + + impl MonotonicClock for FakeClock { + fn now_ns(&self) -> u64 { + self.value_ns.load(Ordering::Acquire) + } + } + + fn build_runtime_with_clock( + source_count: usize, + clock: Arc, + ) -> (Vec, AudioMixRuntime, CapturedMixOutputSink) { + assert!(source_count > 0); + let mut sources: Vec = Vec::with_capacity(source_count); + let mut builder = AudioMixRuntimeBuilder::new().with_clock(clock); + for n in 0..source_count { + let (source, consumer) = + CaptureSource::create(n as u64 + 1, MIX_SAMPLE_RATE_HZ, MIX_CHANNELS) + .expect("source"); + let push_ns = source.last_push_ns_arc(); + builder = builder.add_source_with_freshness(n as u64 + 1, consumer, push_ns); + sources.push(source); + } + let sink = CapturedMixOutputSink::new(); + let cloned_frames = sink.frames(); + let cloned_resets = sink.resets(); + let runtime = builder + .build(CapturedMixOutputSink { + frames: cloned_frames, + resets: cloned_resets, + }) + .expect("build"); + (sources, runtime, sink) + } + + #[test] + fn realtime_priority_guard_acquires_and_releases() { + let guard = RealtimePriorityGuard::acquire(PriorityProfile::Audio); + match guard { + Ok(g) => { + let is_valid = matches!( + g.outcome(), + RtOutcome::Acquired | RtOutcome::PartialFallback + ); + drop(g); + assert!(is_valid); + } + Err(RtError::PlatformDenied(_)) => {} + Err(other) => panic!("unexpected guard error: {other:?}"), + } + } + + #[test] + fn source_ring_produce_consume_1000_frames_deterministic() { + let (producer, mut consumer) = SourceRing::create(2048, 48_000).expect("pair"); + let mut producer = producer; + let payload: Vec = (0..1000).map(|n| (n as i16 % 4096) - 2048).collect(); + let pushed = producer.try_push_slice(&payload); + assert_eq!(pushed, 1000); + let mut buf = vec![0i16; 1000]; + let drained = consumer.drain_into(&mut buf); + assert_eq!(drained, 1000); + assert_eq!(buf, payload); + } + + #[test] + fn audio_mix_session_with_eight_pure_tone_sources_sums_to_saturated() { + let (mut sources, mut runtime, sink) = build_test_runtime(8); + let payload = vec![i16::MAX / 4; AUDIO_OUTPUT_FRAMES]; + for source in sources.iter_mut() { + let pushed = source.ingest_skip_apm(&payload); + assert_eq!(pushed, AUDIO_OUTPUT_FRAMES); + } + let frame = runtime.run_one_tick_blocking(0).expect("frame"); + assert_eq!(frame.samples.len(), AUDIO_OUTPUT_FRAMES); + for sample in frame.samples.iter() { + assert!(*sample > 0); + } + let frames_arc = sink.frames(); + let frames_guard = frames_arc.lock().expect("lock"); + assert_eq!(frames_guard.len(), 1); + } + + #[test] + fn tick_driver_maintains_cumulative_drift_free_schedule_over_100_ticks() { + let clock = Arc::new(SystemMonotonicClock::new()); + let period_ns = 1_000_000; + let mut driver = TickDriver::new(clock, period_ns).expect("driver"); + for _ in 0..100 { + let info = driver.wait_until_next_tick().expect("tick"); + assert!(info.actual_ns >= info.scheduled_ns.saturating_sub(period_ns)); + } + assert!(driver.next_tick_index() >= 100); + } + + #[test] + fn apm_stub_is_noop_on_capture_frame_but_counts() { + let mut source = CaptureSource::create(1, MIX_SAMPLE_RATE_HZ, MIX_CHANNELS) + .expect("src") + .0; + let original = make_pure_tone(1000, source.apm_frame_samples * 2); + let mut samples = original.clone(); + let before = source.apm_frames_processed(); + source.ingest(&mut samples).expect("ingest"); + let after = source.apm_frames_processed(); + assert_eq!(samples, original); + assert_eq!(after - before, 2); + } + + #[test] + fn ignore_audio_policy_reset_triggered_after_threshold() { + use crate::ignore_audio_runtime::{ + IgnoreAudioDecision, IgnoreAudioSourceState, IgnoreAudioTick, + SOURCE_RESET_AFTER_BUFFERED_TICKS, + }; + let mut policy = IgnoreAudioPolicy::new(48_000).expect("ok"); + let state = IgnoreAudioSourceState { + id: 1, + buffered_frames: (SOURCE_RESET_AFTER_BUFFERED_TICKS as u64) * 1024 + 1, + last_frame_age_ns: 0, + is_muted: false, + }; + let eval = policy + .evaluate( + &state, + IgnoreAudioTick { + at_ns: 1, + period_ns: 21_333_333, + }, + ) + .expect("ok"); + assert_eq!(eval.decision, IgnoreAudioDecision::ResetSource); + } + + #[test] + fn empty_source_yields_silence_frame() { + let (_sources, mut runtime, _sink) = build_test_runtime(1); + let frame = runtime.run_one_tick_blocking(0).expect("frame"); + for sample in frame.samples.iter() { + assert_eq!(*sample, 0); + } + assert_eq!(frame.total_drained, 0); + assert_eq!(frame.total_silence, AUDIO_OUTPUT_FRAMES as u64); + } + + #[test] + fn determinism_same_input_same_output() { + let payload: Vec = (0..AUDIO_OUTPUT_FRAMES) + .map(|n| (n as i16 % 1000) - 500) + .collect(); + let mut outputs: Vec> = Vec::new(); + for _ in 0..2 { + let (mut sources, mut runtime, _sink) = build_test_runtime(2); + for source in sources.iter_mut() { + let _ = source.ingest_skip_apm(&payload); + } + let frame = runtime.run_one_tick_blocking(0).expect("frame"); + outputs.push(frame.samples.to_vec()); + } + assert_eq!(outputs[0], outputs[1]); + } + + #[test] + fn skipped_tick_increments_silence_count_on_empty_ring() { + let (_sources, mut runtime, _sink) = build_test_runtime(1); + let frame = runtime.run_one_tick_blocking(0).expect("frame"); + assert_eq!(frame.total_silence, AUDIO_OUTPUT_FRAMES as u64); + } + + #[test] + fn end_to_end_one_source_drained_to_mix_session() { + let (mut sources, mut runtime, sink) = build_test_runtime(1); + let payload: Vec = (0..AUDIO_OUTPUT_FRAMES) + .map(|n| (n as i16 % 2000) - 1000) + .collect(); + let pushed = sources[0].ingest_skip_apm(&payload); + assert_eq!(pushed, AUDIO_OUTPUT_FRAMES); + let _ = runtime.run_one_tick_blocking(0).expect("frame"); + let frames = sink.frames(); + let frames_guard = frames.lock().expect("lock"); + assert_eq!(frames_guard.len(), 1); + assert_eq!(frames_guard[0].samples.as_ref(), &payload[..]); + } + + #[test] + fn build_rejects_zero_sources() { + let sink = NullMixOutputSink; + let err = AudioMixRuntimeBuilder::new().build(sink).err(); + assert!(matches!(err, Some(MixRuntimeError::NoSources))); + } + + #[test] + fn capture_source_buffered_frames_grows_on_ingest() { + let mut source = CaptureSource::create(1, MIX_SAMPLE_RATE_HZ, MIX_CHANNELS) + .expect("src") + .0; + let initial = source.buffered_frames(); + assert_eq!(initial, 0); + let payload = vec![5i16; 256]; + let pushed = source.ingest_skip_apm(&payload); + assert_eq!(pushed, 256); + assert!(source.buffered_frames() > 0); + } + + #[test] + fn run_single_tick_lap_through_eight_sources_executes() { + let (mut sources, mut runtime, _sink) = build_test_runtime(8); + for (idx, source) in sources.iter_mut().enumerate() { + let payload = vec![(idx as i16) * 100; AUDIO_OUTPUT_FRAMES]; + let _ = source.ingest_skip_apm(&payload); + } + let frame = runtime.run_one_tick_blocking(0).expect("frame"); + assert!(frame.total_drained > 0); + } + + #[allow(clippy::no_effect_underscore_binding)] + #[test] + fn tick_info_synthetic_can_be_consumed() { + let _ti = TickInfo { + tick_index: 0, + scheduled_ns: 0, + actual_ns: 0, + lag_ns: 0, + }; + } + + #[test] + fn ingest_increments_apm_frame_count_per_chunk() { + let mut source = CaptureSource::create(1, MIX_SAMPLE_RATE_HZ, MIX_CHANNELS) + .expect("src") + .0; + assert_eq!(source.apm_frames_processed(), 0); + let chunks = 7; + let mut samples = vec![0i16; source.apm_frame_samples * chunks]; + source.ingest(&mut samples).expect("ingest"); + assert_eq!(source.apm_frames_processed(), chunks as u64); + } + + #[test] + fn stale_source_triggered_via_clock_emits_reset() { + let clock = Arc::new(FakeClock::new(1_000_000)); + let (sources, mut runtime, sink) = + build_runtime_with_clock(1, Arc::clone(&clock) as Arc); + sources[0].freshness.mark_push_at(1_000_000); + clock.set(1_000_000 + 10_000_000_000); + let _ = runtime.run_one_tick_blocking(0).expect("frame"); + let resets = sink.resets(); + let guard = resets.lock().expect("lock"); + assert!(!guard.is_empty()); + assert!( + guard + .iter() + .any(|e| e.reason + == crate::ignore_audio_runtime::IgnoreAudioResetReason::StaleSource) + ); + } + + #[test] + fn last_frame_age_monotonic_within_quiescence() { + let clock = Arc::new(FakeClock::new(1_000_000)); + let (sources, mut runtime, _sink) = + build_runtime_with_clock(1, Arc::clone(&clock) as Arc); + sources[0].freshness.mark_push_at(1_000_000); + let mut prior = 0u64; + for n in 0..5 { + clock.set(1_000_000 + (n + 1) * 1_000_000); + let _ = runtime.run_one_tick_blocking(0).expect("frame"); + let push = runtime.last_push_ns_for(0); + let now = clock.now_ns(); + let age = compute_age_ns(push, 1_000_000, now); + assert!(age >= prior); + prior = age; + } + } + + #[test] + fn output_pool_buffers_rotate_modulo_depth_when_consumer_releases() { + let (mut sources, mut runtime) = build_null_runtime(1); + let payload = vec![123i16; AUDIO_OUTPUT_FRAMES]; + let _ = sources[0].ingest_skip_apm(&payload); + let f0 = runtime.run_one_tick_blocking(0).expect("frame"); + let p0 = Arc::as_ptr(&f0.samples); + drop(f0); + let _ = runtime + .run_one_tick_blocking(MIX_TICK_PERIOD_NS) + .expect("frame"); + let _ = runtime + .run_one_tick_blocking(MIX_TICK_PERIOD_NS * 2) + .expect("frame"); + let _ = runtime + .run_one_tick_blocking(MIX_TICK_PERIOD_NS * 3) + .expect("frame"); + let f4 = runtime + .run_one_tick_blocking(MIX_TICK_PERIOD_NS * 4) + .expect("frame"); + let p4 = Arc::as_ptr(&f4.samples); + assert_eq!(p0, p4); + } + + #[test] + fn output_pool_returns_distinct_buffers_when_consumer_retains() { + let (mut sources, mut runtime, _sink) = build_test_runtime(1); + let payload = vec![7i16; AUDIO_OUTPUT_FRAMES]; + let _ = sources[0].ingest_skip_apm(&payload); + let f0 = runtime.run_one_tick_blocking(0).expect("frame"); + let f1 = runtime + .run_one_tick_blocking(MIX_TICK_PERIOD_NS) + .expect("frame"); + let f2 = runtime + .run_one_tick_blocking(MIX_TICK_PERIOD_NS * 2) + .expect("frame"); + let f3 = runtime + .run_one_tick_blocking(MIX_TICK_PERIOD_NS * 3) + .expect("frame"); + let p0 = Arc::as_ptr(&f0.samples); + let p1 = Arc::as_ptr(&f1.samples); + let p2 = Arc::as_ptr(&f2.samples); + let p3 = Arc::as_ptr(&f3.samples); + assert!(p0 != p1); + assert!(p1 != p2); + assert!(p2 != p3); + assert_eq!(f0.samples.len(), AUDIO_OUTPUT_FRAMES); + } + + #[test] + fn output_arc_samples_length_invariant_holds() { + let (mut sources, mut runtime, _sink) = build_test_runtime(1); + let payload = vec![1i16; AUDIO_OUTPUT_FRAMES]; + let _ = sources[0].ingest_skip_apm(&payload); + let frame = runtime.run_one_tick_blocking(0).expect("frame"); + assert_eq!(frame.samples.len(), AUDIO_OUTPUT_FRAMES); + assert_eq!(frame.samples.as_ref().len(), 1024); + } + + fn build_null_runtime(source_count: usize) -> (Vec, AudioMixRuntime) { + assert!(source_count > 0); + let mut sources: Vec = Vec::with_capacity(source_count); + let mut builder = AudioMixRuntimeBuilder::new(); + for n in 0..source_count { + let (source, consumer) = + CaptureSource::create(n as u64 + 1, MIX_SAMPLE_RATE_HZ, MIX_CHANNELS) + .expect("source"); + let push_ns = source.last_push_ns_arc(); + builder = builder.add_source_with_freshness(n as u64 + 1, consumer, push_ns); + sources.push(source); + } + let runtime = builder.build(NullMixOutputSink).expect("build"); + (sources, runtime) + } + + #[test] + fn tick_steady_state_does_not_allocate() { + let (mut sources, mut runtime) = build_null_runtime(4); + let payload = vec![10i16; AUDIO_OUTPUT_FRAMES]; + for _warm in 0..OUTPUT_BUFFER_POOL_DEPTH + 2 { + for source in sources.iter_mut() { + let _ = source.ingest_skip_apm(&payload); + } + let _ = runtime.run_one_tick_blocking(0).expect("warmup"); + } + for source in sources.iter_mut() { + let _ = source.ingest_skip_apm(&payload); + } + begin_thread_alloc_count(); + let _ = runtime + .run_one_tick_blocking(MIX_TICK_PERIOD_NS * 10) + .expect("frame"); + let allocations = end_thread_alloc_count(); + assert_eq!( + allocations, 0, + "steady-state tick allocated {allocations} times", + ); + let _ = snapshot_allocs(); + } + + #[test] + fn freshness_handle_records_push_time() { + let source = CaptureSource::create(7, MIX_SAMPLE_RATE_HZ, MIX_CHANNELS) + .expect("src") + .0; + let initial = source.freshness_handle().last_push_ns(); + assert_eq!(initial, NEVER_PUSHED_SENTINEL); + source.freshness_handle().mark_push_at(42); + assert_eq!(source.freshness_handle().last_push_ns(), 42); + } + + #[test] + fn compute_age_ns_uses_registration_for_never_pushed_source() { + let age = compute_age_ns(NEVER_PUSHED_SENTINEL, 1_000, 5_000); + assert_eq!(age, 4_000); + } + + #[test] + fn compute_age_ns_returns_zero_when_now_before_push() { + let age = compute_age_ns(1_000, 500, 500); + assert_eq!(age, 0); + } + + #[test] + fn compute_age_ns_returns_difference_after_push() { + let age = compute_age_ns(1_000, 0, 5_000); + assert_eq!(age, 4_000); + } + + #[test] + fn ramp_advances_per_tick_through_runtime() { + let (mut sources, mut runtime, _sink) = build_test_runtime(1); + let payload = vec![100i16; AUDIO_OUTPUT_FRAMES]; + let _ = sources[0].ingest_skip_apm(&payload); + assert_eq!(runtime.current_ramp_gain(0), 0.0); + let _ = runtime.run_one_tick_blocking(0).expect("frame"); + let g1 = runtime.current_ramp_gain(0); + assert!(g1 > 0.0); + for n in 1..6u64 { + let _ = runtime + .run_one_tick_blocking(n * MIX_TICK_PERIOD_NS) + .expect("frame"); + } + let g_after = runtime.current_ramp_gain(0); + assert_eq!(g_after, 1.0); + } + + #[test] + fn smoother_initialises_after_first_push() { + let (mut sources, mut runtime, _sink) = build_test_runtime(1); + assert!(!runtime.smoother_initialised(0)); + let payload = vec![1i16; AUDIO_OUTPUT_FRAMES]; + let _ = sources[0].ingest_skip_apm(&payload); + let _ = runtime.run_one_tick_blocking(0).expect("frame"); + assert!(runtime.smoother_initialised(0)); + } + + #[test] + fn registered_never_pushed_source_eligible_for_stale_after_threshold() { + let clock = Arc::new(FakeClock::new(1_000_000)); + let (_sources, runtime, _sink) = + build_runtime_with_clock(1, Arc::clone(&clock) as Arc); + clock.set(1_000_000 + 6_000_000_000); + let stale = runtime.is_source_stale(0, 1_000_000 + 6_000_000_000, 5_000_000_000); + assert!(stale); + } + + #[test] + fn tick_marks_pushed_when_freshness_atomic_advances() { + let (mut sources, mut runtime, _sink) = build_test_runtime(1); + assert_eq!(runtime.mark_pushed_total(), 0); + let payload = vec![100i16; AUDIO_OUTPUT_FRAMES]; + let pushed = sources[0].ingest_skip_apm(&payload); + assert_eq!(pushed, AUDIO_OUTPUT_FRAMES); + let _ = runtime.run_one_tick_blocking(0).expect("frame"); + assert!(runtime.mark_pushed_total() >= 1); + let observed_push = sources[0].freshness_handle().last_push_ns(); + assert_ne!(observed_push, u64::MAX); + let stale = runtime.is_source_stale(0, observed_push + 1_000_000, 5_000_000_000); + assert!(!stale); + } + + #[test] + fn tick_does_not_double_count_unchanged_push_atomic() { + let (mut sources, mut runtime, _sink) = build_test_runtime(1); + let payload = vec![50i16; AUDIO_OUTPUT_FRAMES]; + let _ = sources[0].ingest_skip_apm(&payload); + let _ = runtime.run_one_tick_blocking(0).expect("frame"); + let first_total = runtime.mark_pushed_total(); + assert!(first_total >= 1); + let _ = runtime + .run_one_tick_blocking(MIX_TICK_PERIOD_NS) + .expect("frame"); + let second_total = runtime.mark_pushed_total(); + assert_eq!(first_total, second_total); + } +} diff --git a/fluxer_desktop/native/linux-audio-capture/src/backend.rs b/fluxer_desktop/native/linux-audio-capture/src/backend.rs new file mode 100644 index 0000000..97bf869 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/src/backend.rs @@ -0,0 +1,83 @@ +#![allow(dead_code)] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::routing::{PropMap, RoutingRule, SelfIdentity}; + +#[derive(Default, Clone)] +pub struct RoutingGraphSnapshot { + pub backend: String, + pub nodes: Vec, + pub ports: Vec, + pub owned_links: Vec, +} + +#[derive(Default, Clone)] +pub struct RoutingGraphNode { + pub id: u32, + pub props: PropMap, +} + +#[derive(Default, Clone)] +pub struct RoutingGraphPort { + pub id: u32, + pub node_id: u32, + pub direction: String, + pub channel: String, + pub props: PropMap, +} + +#[derive(Default, Clone, Copy)] +pub struct RoutingGraphLink { + pub output_node_id: u32, + pub output_port_id: u32, + pub input_node_id: u32, + pub input_port_id: u32, +} + +pub trait CaptureBridge: Send + Sync { + fn inventory(&self) -> Vec; + fn apply(&self, rule: RoutingRule) -> bool; + fn release(&self); + fn populate_self_identity(&self, identity: SelfIdentity); + fn backend_name(&self) -> &'static str; + + fn routing_graph(&self) -> RoutingGraphSnapshot { + RoutingGraphSnapshot { + backend: self.backend_name().to_string(), + ..Default::default() + } + } +} + +pub trait DirectCapture: Send + Sync { + fn start(&self, rule: RoutingRule) -> bool; + + fn set_rule(&self, rule: RoutingRule) -> bool; + fn read(&self) -> Option; + fn stop(&self); + fn populate_self_identity(&self, identity: SelfIdentity); + + fn set_screen_audio_sink( + &self, + _sink: std::sync::Arc, + ) { + } + + fn clear_screen_audio_sink(&self) {} + + fn routing_graph(&self) -> RoutingGraphSnapshot { + RoutingGraphSnapshot::default() + } + + fn last_push_ns_arc(&self) -> Option> { + None + } +} + +pub struct CapturedFrame { + pub samples: Vec, + pub sample_rate: u32, + pub channels: u32, + pub timestamp_us: i64, +} diff --git a/fluxer_desktop/native/linux-audio-capture/src/direct_buffer.rs b/fluxer_desktop/native/linux-audio-capture/src/direct_buffer.rs new file mode 100644 index 0000000..8e13468 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/src/direct_buffer.rs @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![allow(dead_code)] + +use std::collections::VecDeque; + +use crate::audio_contract::{ + self, DIRECT_CAPTURE_CHANNELS, DIRECT_CAPTURE_MAX_SAMPLES, DIRECT_CAPTURE_SAMPLE_RATE, +}; +use crate::backend::CapturedFrame; + +pub struct DirectReadMeta { + pub sample_rate: u32, + pub channels: u32, + pub timestamp_us: i64, +} + +pub struct DirectAudioBuffer { + samples: VecDeque, + queue_start_us: i64, + sample_rate: u32, + channels: u32, +} + +impl DirectAudioBuffer { + pub fn new(sample_rate: u32, channels: u32) -> Self { + Self { + samples: VecDeque::with_capacity(DIRECT_CAPTURE_MAX_SAMPLES), + queue_start_us: 0, + sample_rate: sample_rate.max(1), + channels: channels.max(1), + } + } + + pub fn default_format() -> Self { + Self::new(DIRECT_CAPTURE_SAMPLE_RATE, DIRECT_CAPTURE_CHANNELS) + } + + #[cfg(test)] + pub fn len(&self) -> usize { + self.samples.len() + } + + #[cfg(test)] + pub fn is_empty(&self) -> bool { + self.samples.is_empty() + } + + #[cfg(test)] + pub fn queue_start_us(&self) -> i64 { + self.queue_start_us + } + + pub fn set_format(&mut self, sample_rate: u32, channels: u32) { + let sample_rate = sample_rate.max(1); + let channels = channels.max(1); + if self.sample_rate != sample_rate || self.channels != channels { + self.clear(); + } + self.sample_rate = sample_rate; + self.channels = channels; + } + + pub fn clear(&mut self) { + self.samples.clear(); + self.queue_start_us = 0; + } + + pub fn push(&mut self, input: &[f32], end_timestamp_us: i64) { + let whole = audio_contract::whole_frame_sample_count(input.len(), self.channels); + if whole == 0 { + return; + } + let mut frame = &input[..whole]; + if frame.len() > DIRECT_CAPTURE_MAX_SAMPLES { + let keep = + audio_contract::whole_frame_sample_count(DIRECT_CAPTURE_MAX_SAMPLES, self.channels); + frame = &frame[frame.len() - keep..]; + self.clear(); + } + if self.samples.is_empty() { + let duration_us = audio_contract::duration_us_for_sample_count( + frame.len(), + self.sample_rate, + self.channels, + ); + self.queue_start_us = end_timestamp_us.saturating_sub(duration_us); + } + self.drop_for_incoming(frame.len()); + self.samples.extend(frame.iter().copied()); + } + + pub fn read(&mut self) -> Option { + let mut out = Vec::with_capacity(audio_contract::DIRECT_CAPTURE_MAX_READ_SAMPLES); + let meta = self.read_into(&mut out)?; + Some(CapturedFrame { + samples: out, + sample_rate: meta.sample_rate, + channels: meta.channels, + timestamp_us: meta.timestamp_us, + }) + } + + pub fn read_into(&mut self, out: &mut Vec) -> Option { + assert!(self.sample_rate >= 1); + assert!(self.channels >= 1); + out.clear(); + if self.samples.is_empty() { + return None; + } + let take = audio_contract::bounded_direct_read_sample_count_for_format( + self.samples.len(), + self.sample_rate, + self.channels, + ); + if take == 0 { + return None; + } + assert!(take <= self.samples.len()); + assert!(take.is_multiple_of(self.channels as usize)); + let timestamp_us = self.queue_start_us.max(0); + let (front, back) = self.samples.as_slices(); + let front_take = take.min(front.len()); + out.extend_from_slice(&front[..front_take]); + out.extend_from_slice(&back[..take - front_take]); + self.samples.drain(..take); + self.queue_start_us = + self.queue_start_us + .saturating_add(audio_contract::duration_us_for_sample_count( + take, + self.sample_rate, + self.channels, + )); + if self.samples.is_empty() { + self.queue_start_us = 0; + } + Some(DirectReadMeta { + sample_rate: self.sample_rate, + channels: self.channels, + timestamp_us, + }) + } + + fn drop_for_incoming(&mut self, incoming: usize) { + assert!(incoming >= 1); + assert!(incoming <= DIRECT_CAPTURE_MAX_SAMPLES); + assert!(self.channels >= 1); + let total = self.samples.len() + incoming; + if total <= DIRECT_CAPTURE_MAX_SAMPLES { + return; + } + let overflow = total - DIRECT_CAPTURE_MAX_SAMPLES; + let channels = self.channels as usize; + let remainder = overflow % channels; + let drop = if remainder == 0 { + overflow + } else { + overflow + (channels - remainder) + } + .min(self.samples.len()); + self.samples.drain(..drop); + self.queue_start_us = + self.queue_start_us + .saturating_add(audio_contract::duration_us_for_sample_count( + drop, + self.sample_rate, + self.channels, + )); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn read_emits_stable_twenty_ms_chunks_with_continuous_timestamps() { + let mut buffer = DirectAudioBuffer::default_format(); + buffer.push(&vec![0.5; 4_800], 1_000_000); + + let first = buffer.read().expect("first chunk"); + assert_eq!(1_920, first.samples.len()); + assert_eq!(950_000, first.timestamp_us); + + let second = buffer.read().expect("second chunk"); + assert_eq!(1_920, second.samples.len()); + assert_eq!(970_000, second.timestamp_us); + } + + #[test] + fn format_changes_clear_queued_samples() { + let mut buffer = DirectAudioBuffer::default_format(); + buffer.push(&vec![0.5; 1_920], 100_000); + assert!(!buffer.is_empty()); + + buffer.set_format(44_100, 2); + + assert!(buffer.is_empty()); + assert_eq!(0, buffer.queue_start_us()); + buffer.push(&vec![0.25; 1_764], 200_000); + let frame = buffer.read().expect("chunk"); + assert_eq!(1_764, frame.samples.len()); + assert_eq!(180_000, frame.timestamp_us); + assert_eq!(44_100, frame.sample_rate); + } + + #[test] + fn overflow_drops_from_front_and_advances_timestamp() { + let mut buffer = DirectAudioBuffer::default_format(); + buffer.push(&vec![0.5; DIRECT_CAPTURE_MAX_SAMPLES + 1_920], 3_000_000); + + assert_eq!(DIRECT_CAPTURE_MAX_SAMPLES, buffer.len()); + let frame = buffer.read().expect("chunk"); + assert_eq!(1_000_000, frame.timestamp_us); + } +} diff --git a/fluxer_desktop/native/linux-audio-capture/src/ignore_audio_runtime.rs b/fluxer_desktop/native/linux-audio-capture/src/ignore_audio_runtime.rs new file mode 100644 index 0000000..929c0cf --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/src/ignore_audio_runtime.rs @@ -0,0 +1,425 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![allow(dead_code)] +use std::collections::BTreeMap; + +pub const AUDIO_BUFFERING_MAX_TICKS: u32 = 64; + +pub const SOURCE_RESET_AFTER_BUFFERED_TICKS: u32 = 128; + +pub const SOURCE_STALE_AFTER_NS: u64 = 5_000_000_000; + +pub const NEVER_PUSHED_SENTINEL: u64 = u64::MAX; + +pub fn compute_source_age_ns(last_push_ns: u64, registered_at_ns: u64, now_ns: u64) -> u64 { + let baseline_ns = if last_push_ns == NEVER_PUSHED_SENTINEL { + registered_at_ns + } else { + last_push_ns + }; + assert!(baseline_ns != NEVER_PUSHED_SENTINEL); + if now_ns <= baseline_ns { + return 0; + } + now_ns - baseline_ns +} + +pub const SAMPLE_RATE_HZ_MIN: u32 = 8_000; +pub const SAMPLE_RATE_HZ_MAX: u32 = 384_000; + +pub const TICK_PERIOD_NS_MIN: u64 = 1_000_000; +pub const TICK_PERIOD_NS_MAX: u64 = 100_000_000; + +pub const BUFFERED_FRAMES_MAX: u64 = 1 << 28; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum IgnoreAudioDecision { + Mix, + IgnoreThisTick, + ResetSource, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum IgnoreAudioResetReason { + BufferOverflow, + StaleSource, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct IgnoreAudioSourceState { + pub id: u64, + pub buffered_frames: u64, + pub last_frame_age_ns: u64, + pub is_muted: bool, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct IgnoreAudioTick { + pub at_ns: u64, + pub period_ns: u64, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct IgnoreAudioMetrics { + pub ignored_tick_count: u64, + pub reset_count: u64, +} + +impl IgnoreAudioMetrics { + pub const ZERO: IgnoreAudioMetrics = IgnoreAudioMetrics { + ignored_tick_count: 0, + reset_count: 0, + }; +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct IgnoreAudioSourceResetEvent { + pub source_id: u64, + pub at_ns: u64, + pub buffered_frames_at_reset: u64, + pub last_frame_age_ns: u64, + pub reason: IgnoreAudioResetReason, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct IgnoreAudioEvaluation { + pub decision: IgnoreAudioDecision, + pub event: Option, +} + +#[derive(Debug, PartialEq, Eq)] +#[allow(clippy::enum_variant_names)] +pub enum IgnoreAudioError { + SampleRateOutOfRange { sample_rate_hz: u32 }, + TickPeriodOutOfRange { period_ns: u64 }, + BufferedFramesOutOfRange { buffered_frames: u64 }, +} + +pub struct IgnoreAudioPolicy { + sample_rate_hz: u32, + metrics_by_source: BTreeMap, +} + +impl IgnoreAudioPolicy { + pub fn new(sample_rate_hz: u32) -> Result { + if !(SAMPLE_RATE_HZ_MIN..=SAMPLE_RATE_HZ_MAX).contains(&sample_rate_hz) { + return Err(IgnoreAudioError::SampleRateOutOfRange { sample_rate_hz }); + } + assert!(sample_rate_hz >= SAMPLE_RATE_HZ_MIN); + assert!(sample_rate_hz <= SAMPLE_RATE_HZ_MAX); + Ok(Self { + sample_rate_hz, + metrics_by_source: BTreeMap::new(), + }) + } + + pub fn sample_rate_hz(&self) -> u32 { + assert!(self.sample_rate_hz >= SAMPLE_RATE_HZ_MIN); + assert!(self.sample_rate_hz <= SAMPLE_RATE_HZ_MAX); + self.sample_rate_hz + } + + pub fn metrics_for(&self, source_id: u64) -> IgnoreAudioMetrics { + assert!(self.sample_rate_hz >= SAMPLE_RATE_HZ_MIN); + self.metrics_by_source + .get(&source_id) + .copied() + .unwrap_or(IgnoreAudioMetrics::ZERO) + } + + pub fn evaluate( + &mut self, + source_state: &IgnoreAudioSourceState, + tick: IgnoreAudioTick, + ) -> Result { + validate_source_state(source_state)?; + validate_tick(tick)?; + let tick_frames = compute_tick_frames(tick.period_ns, self.sample_rate_hz)?; + assert!(tick_frames >= 1); + let decision = decide(source_state, tick_frames); + let event = self.apply_decision(source_state, tick, decision); + let evaluation = IgnoreAudioEvaluation { decision, event }; + assert_evaluation_invariant(&evaluation); + Ok(evaluation) + } + + fn apply_decision( + &mut self, + source_state: &IgnoreAudioSourceState, + tick: IgnoreAudioTick, + decision: IgnoreAudioDecision, + ) -> Option { + assert!(self.sample_rate_hz >= SAMPLE_RATE_HZ_MIN); + match decision { + IgnoreAudioDecision::Mix => None, + IgnoreAudioDecision::IgnoreThisTick => { + let entry = self + .metrics_by_source + .entry(source_state.id) + .or_insert(IgnoreAudioMetrics::ZERO); + entry.ignored_tick_count = entry.ignored_tick_count.saturating_add(1); + None + } + IgnoreAudioDecision::ResetSource => { + let entry = self + .metrics_by_source + .entry(source_state.id) + .or_insert(IgnoreAudioMetrics::ZERO); + entry.reset_count = entry.reset_count.saturating_add(1); + let reason = if source_state.last_frame_age_ns > SOURCE_STALE_AFTER_NS { + IgnoreAudioResetReason::StaleSource + } else { + IgnoreAudioResetReason::BufferOverflow + }; + Some(IgnoreAudioSourceResetEvent { + source_id: source_state.id, + at_ns: tick.at_ns, + buffered_frames_at_reset: source_state.buffered_frames, + last_frame_age_ns: source_state.last_frame_age_ns, + reason, + }) + } + } + } +} + +pub fn compute_tick_frames(period_ns: u64, sample_rate_hz: u32) -> Result { + if !(TICK_PERIOD_NS_MIN..=TICK_PERIOD_NS_MAX).contains(&period_ns) { + return Err(IgnoreAudioError::TickPeriodOutOfRange { period_ns }); + } + if !(SAMPLE_RATE_HZ_MIN..=SAMPLE_RATE_HZ_MAX).contains(&sample_rate_hz) { + return Err(IgnoreAudioError::SampleRateOutOfRange { sample_rate_hz }); + } + let product: u128 = (period_ns as u128) * (sample_rate_hz as u128); + let frames = (product / 1_000_000_000u128) as u64; + let frames = frames.max(1); + assert!(frames >= 1); + assert!(frames <= BUFFERED_FRAMES_MAX); + Ok(frames) +} + +fn decide(source_state: &IgnoreAudioSourceState, tick_frames: u64) -> IgnoreAudioDecision { + assert!(tick_frames >= 1); + if source_state.is_muted { + return IgnoreAudioDecision::Mix; + } + let reset_by_stale = source_state.last_frame_age_ns > SOURCE_STALE_AFTER_NS; + let reset_by_buffer = + source_state.buffered_frames > (SOURCE_RESET_AFTER_BUFFERED_TICKS as u64) * tick_frames; + if reset_by_stale { + return IgnoreAudioDecision::ResetSource; + } + if reset_by_buffer { + return IgnoreAudioDecision::ResetSource; + } + if source_state.buffered_frames <= tick_frames { + return IgnoreAudioDecision::Mix; + } + let over_buffering = + source_state.buffered_frames > (AUDIO_BUFFERING_MAX_TICKS as u64) * tick_frames; + if over_buffering { + return IgnoreAudioDecision::IgnoreThisTick; + } + IgnoreAudioDecision::Mix +} + +fn validate_source_state(state: &IgnoreAudioSourceState) -> Result<(), IgnoreAudioError> { + if state.buffered_frames > BUFFERED_FRAMES_MAX { + return Err(IgnoreAudioError::BufferedFramesOutOfRange { + buffered_frames: state.buffered_frames, + }); + } + Ok(()) +} + +fn validate_tick(tick: IgnoreAudioTick) -> Result<(), IgnoreAudioError> { + if !(TICK_PERIOD_NS_MIN..=TICK_PERIOD_NS_MAX).contains(&tick.period_ns) { + return Err(IgnoreAudioError::TickPeriodOutOfRange { + period_ns: tick.period_ns, + }); + } + Ok(()) +} + +fn assert_evaluation_invariant(evaluation: &IgnoreAudioEvaluation) { + match evaluation.decision { + IgnoreAudioDecision::ResetSource => { + assert!(evaluation.event.is_some()); + } + _ => { + assert!(evaluation.event.is_none()); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn canonical_tick(at_ns: u64) -> IgnoreAudioTick { + IgnoreAudioTick { + at_ns, + period_ns: 21_333_333, + } + } + + #[test] + fn rejects_sample_rate_below_min() { + let err = IgnoreAudioPolicy::new(4_000).err(); + assert!(matches!( + err, + Some(IgnoreAudioError::SampleRateOutOfRange { .. }) + )); + } + + #[test] + fn rejects_sample_rate_above_max() { + let err = IgnoreAudioPolicy::new(500_000).err(); + assert!(matches!( + err, + Some(IgnoreAudioError::SampleRateOutOfRange { .. }) + )); + } + + #[test] + fn compute_tick_frames_at_48k_21_3ms_yields_1023_ish() { + let frames = compute_tick_frames(21_333_333, 48_000).expect("ok"); + assert!(frames >= 1023); + assert!(frames <= 1024); + } + + #[test] + fn empty_buffer_yields_mix_decision() { + let mut policy = IgnoreAudioPolicy::new(48_000).expect("ok"); + let state = IgnoreAudioSourceState { + id: 1, + buffered_frames: 0, + last_frame_age_ns: 0, + is_muted: false, + }; + let evaluation = policy.evaluate(&state, canonical_tick(0)).expect("ok"); + assert_eq!(evaluation.decision, IgnoreAudioDecision::Mix); + assert!(evaluation.event.is_none()); + } + + #[test] + fn muted_source_always_yields_mix_decision() { + let mut policy = IgnoreAudioPolicy::new(48_000).expect("ok"); + let state = IgnoreAudioSourceState { + id: 5, + buffered_frames: 10_000_000, + last_frame_age_ns: 0, + is_muted: true, + }; + let evaluation = policy.evaluate(&state, canonical_tick(0)).expect("ok"); + assert_eq!(evaluation.decision, IgnoreAudioDecision::Mix); + } + + #[test] + fn over_buffered_yields_ignore_this_tick() { + let mut policy = IgnoreAudioPolicy::new(48_000).expect("ok"); + let state = IgnoreAudioSourceState { + id: 9, + buffered_frames: (AUDIO_BUFFERING_MAX_TICKS as u64) * 1024 + 1, + last_frame_age_ns: 0, + is_muted: false, + }; + let evaluation = policy.evaluate(&state, canonical_tick(0)).expect("ok"); + assert_eq!(evaluation.decision, IgnoreAudioDecision::IgnoreThisTick); + assert_eq!(policy.metrics_for(9).ignored_tick_count, 1); + } + + #[test] + fn over_threshold_buffer_triggers_reset() { + let mut policy = IgnoreAudioPolicy::new(48_000).expect("ok"); + let state = IgnoreAudioSourceState { + id: 12, + buffered_frames: (SOURCE_RESET_AFTER_BUFFERED_TICKS as u64) * 1024 + 1, + last_frame_age_ns: 0, + is_muted: false, + }; + let evaluation = policy.evaluate(&state, canonical_tick(7)).expect("ok"); + assert_eq!(evaluation.decision, IgnoreAudioDecision::ResetSource); + let event = evaluation.event.expect("reset emits event"); + assert_eq!(event.source_id, 12); + assert_eq!(event.at_ns, 7); + assert_eq!(event.reason, IgnoreAudioResetReason::BufferOverflow); + assert_eq!(policy.metrics_for(12).reset_count, 1); + } + + #[test] + fn stale_source_triggers_reset_with_stale_reason() { + let mut policy = IgnoreAudioPolicy::new(48_000).expect("ok"); + let state = IgnoreAudioSourceState { + id: 1, + buffered_frames: 0, + last_frame_age_ns: SOURCE_STALE_AFTER_NS + 1, + is_muted: false, + }; + let evaluation = policy.evaluate(&state, canonical_tick(0)).expect("ok"); + assert_eq!(evaluation.decision, IgnoreAudioDecision::ResetSource); + let event = evaluation.event.expect("reset emits event"); + assert_eq!(event.reason, IgnoreAudioResetReason::StaleSource); + } + + #[test] + fn period_too_small_rejected() { + let mut policy = IgnoreAudioPolicy::new(48_000).expect("ok"); + let state = IgnoreAudioSourceState { + id: 1, + buffered_frames: 0, + last_frame_age_ns: 0, + is_muted: false, + }; + let err = policy + .evaluate( + &state, + IgnoreAudioTick { + at_ns: 0, + period_ns: 0, + }, + ) + .err(); + assert!(matches!( + err, + Some(IgnoreAudioError::TickPeriodOutOfRange { .. }) + )); + } + + #[test] + fn compute_source_age_uses_registration_for_never_pushed() { + let registered_at_ns = 1_000_000; + let now_ns = 1_000_000 + 6_000_000_000; + let age = compute_source_age_ns(NEVER_PUSHED_SENTINEL, registered_at_ns, now_ns); + assert_eq!(age, 6_000_000_000); + } + + #[test] + fn compute_source_age_uses_last_push_after_first_push() { + let registered_at_ns = 1_000; + let last_push_ns = 5_000; + let age = compute_source_age_ns(last_push_ns, registered_at_ns, 9_000); + assert_eq!(age, 4_000); + } + + #[test] + fn compute_source_age_zero_when_now_before_baseline() { + let age = compute_source_age_ns(NEVER_PUSHED_SENTINEL, 5_000, 1_000); + assert_eq!(age, 0); + } + + #[test] + fn metrics_accumulate_across_calls() { + let mut policy = IgnoreAudioPolicy::new(48_000).expect("ok"); + let state = IgnoreAudioSourceState { + id: 2, + buffered_frames: (AUDIO_BUFFERING_MAX_TICKS as u64) * 1024 + 1, + last_frame_age_ns: 0, + is_muted: false, + }; + for _ in 0..5 { + let _ = policy.evaluate(&state, canonical_tick(0)); + } + assert_eq!(policy.metrics_for(2).ignored_tick_count, 5); + } +} diff --git a/fluxer_desktop/native/linux-audio-capture/src/lib.rs b/fluxer_desktop/native/linux-audio-capture/src/lib.rs new file mode 100644 index 0000000..67c4e82 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/src/lib.rs @@ -0,0 +1,863 @@ +#![allow(clippy::too_many_lines)] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +mod audio_contract; +mod audio_mix_runtime; +mod backend; +mod direct_buffer; +mod ignore_audio_runtime; + +#[doc(hidden)] +pub mod audio_mix_runtime_bench_helpers { + pub use crate::audio_mix_runtime::{ + AudioMixRuntime, AudioMixRuntimeBuilder, CaptureSource, CapturedMixOutputSink, + MIX_CHANNELS, MIX_SAMPLE_RATE_HZ, MIX_TICK_PERIOD_NS, MixOutputFrame, MixOutputSink, + MixRuntimeError, NullMixOutputSink, SOURCE_RING_CAP_FRAMES, + }; +} + +#[doc(hidden)] +pub mod ignore_audio_bench_helpers { + pub use crate::ignore_audio_runtime::{ + AUDIO_BUFFERING_MAX_TICKS, IgnoreAudioDecision, IgnoreAudioEvaluation, IgnoreAudioMetrics, + IgnoreAudioPolicy, IgnoreAudioResetReason, IgnoreAudioSourceResetEvent, + IgnoreAudioSourceState, IgnoreAudioTick, SOURCE_RESET_AFTER_BUFFERED_TICKS, + SOURCE_STALE_AFTER_NS, + }; +} +#[cfg(target_os = "linux")] +mod pipewire; +#[cfg(target_os = "linux")] +mod pipewire_bridge; +mod routing; +#[cfg(target_os = "linux")] +mod self_identity; + +use std::ptr; +use std::sync::Arc; +use std::sync::Mutex; + +use fluxer_screen_frame_bus::{NativeScreenFrameSinkHandle, NativeScreenFrameSinkHandleRef}; +use napi::Env; +use napi::JsValue; +use napi::Status; +use napi::bindgen_prelude::{ + Array, ArrayBuffer, Error, Function, Object, Result, Unknown, ValueType, +}; +use napi::threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}; +use napi_derive::napi; + +use crate::audio_contract::{ + MAX_INVENTORY_FIELD_LENGTH, MAX_INVENTORY_FIELDS, MAX_ROUTING_RULE_KEY_LENGTH, + MAX_ROUTING_RULE_KEYS_PER_PATTERN, MAX_ROUTING_RULE_PATTERNS, MAX_ROUTING_RULE_VALUE_LENGTH, +}; +use crate::backend::{ + CaptureBridge as CaptureBridgeTrait, DirectCapture as DirectCaptureTrait, RoutingGraphSnapshot, +}; +use crate::routing::{PropMap, PropPattern, RoutingRule, SelfIdentity}; + +type LifecycleTsfn = + Arc>; + +#[cfg(target_os = "linux")] +fn make_self_identity() -> SelfIdentity { + let mut id = SelfIdentity::default(); + self_identity::populate_self_identity(&mut id); + id +} + +#[cfg(not(target_os = "linux"))] +#[allow(dead_code)] +fn make_self_identity() -> SelfIdentity { + SelfIdentity::default() +} + +#[cfg(target_os = "linux")] +fn open_capture_backend() -> Option<(Box, &'static str)> { + if let Some(bridge) = pipewire_bridge::PipeWireBridge::open() { + bridge.populate_self_identity(make_self_identity()); + return Some((Box::new(bridge), "pipewire")); + } + None +} + +#[cfg(not(target_os = "linux"))] +fn open_capture_backend() -> Option<(Box, &'static str)> { + None +} + +#[cfg(target_os = "linux")] +fn open_direct_backend() -> Option> { + if let Some(direct) = pipewire_bridge::PipeWireDirectCapture::open() { + direct.populate_self_identity(make_self_identity()); + return Some(Box::new(direct)); + } + None +} + +#[cfg(not(target_os = "linux"))] +fn open_direct_backend() -> Option> { + None +} + +#[cfg(target_os = "linux")] +fn pipewire_reachable() -> bool { + pipewire_bridge::daemon_reachable() +} + +#[cfg(not(target_os = "linux"))] +fn pipewire_reachable() -> bool { + false +} + +#[napi(js_name = "pipeWireAvailable")] +pub fn pipe_wire_available() -> bool { + pipewire_reachable() +} + +#[napi(js_name = "audioBackend")] +pub fn audio_backend() -> &'static str { + if pipewire_reachable() { + "pipewire" + } else { + "none" + } +} + +#[napi] +pub struct AudioBridge { + backend: Mutex>>, + name: &'static str, +} + +#[napi] +impl AudioBridge { + #[napi(constructor)] + pub fn new() -> Self { + match open_capture_backend() { + Some((backend, name)) => Self { + backend: Mutex::new(Some(backend)), + name, + }, + None => Self { + backend: Mutex::new(None), + name: "none", + }, + } + } + + #[napi] + pub fn inventory(&self, fields: Option>) -> Result> { + let fields = match fields { + Some(values) => validate_inventory_fields(values)?, + None => Vec::new(), + }; + let guard = self + .backend + .lock() + .map_err(|_| generic_error("AudioBridge backend poisoned"))?; + let snapshot = guard.as_ref().map(|b| b.inventory()).unwrap_or_default(); + Ok(snapshot + .into_iter() + .map(|entry| project_inventory_entry(entry, &fields)) + .collect()) + } + + #[napi] + pub fn apply(&self, rule: Object) -> Result { + let parsed = parse_routing_rule(&rule)?; + let guard = self + .backend + .lock() + .map_err(|_| generic_error("AudioBridge backend poisoned"))?; + Ok(guard.as_ref().is_some_and(|b| b.apply(parsed))) + } + + #[napi] + pub fn release(&self) -> Result<()> { + let guard = self + .backend + .lock() + .map_err(|_| generic_error("AudioBridge backend poisoned"))?; + if let Some(b) = guard.as_ref() { + b.release(); + } + Ok(()) + } + + #[napi(js_name = "routingGraph")] + pub fn routing_graph(&self) -> Result { + let guard = self + .backend + .lock() + .map_err(|_| generic_error("AudioBridge backend poisoned"))?; + let graph = guard + .as_ref() + .map(|b| b.routing_graph()) + .unwrap_or_default(); + Ok(RoutingGraphWire(graph)) + } + + #[napi] + pub fn backend(&self) -> &'static str { + self.name + } +} + +impl Default for AudioBridge { + fn default() -> Self { + Self::new() + } +} + +fn retain_screen_audio_sink_handle( + value: Unknown<'_>, +) -> Result> { + if value.get_type()? != ValueType::External { + return Err(generic_error( + "DirectAudioCapture.setScreenAudioSink expects a native external sink handle", + )); + } + let raw_value = value.value(); + let mut data: *mut std::ffi::c_void = ptr::null_mut(); + let status = + unsafe { napi::sys::napi_get_value_external(raw_value.env, raw_value.value, &mut data) }; + if status != napi::sys::Status::napi_ok || data.is_null() { + return Err(generic_error( + "DirectAudioCapture.setScreenAudioSink received an empty native external sink handle", + )); + } + let handle = unsafe { + NativeScreenFrameSinkHandle::retain_from_raw(data.cast::()) + } + .ok_or_else(|| { + generic_error("DirectAudioCapture.setScreenAudioSink received an invalid handle") + })?; + Ok(Arc::new(handle)) +} + +#[napi] +pub struct DirectAudioCapture { + backend: Mutex>>, + lifecycle_tsfn: Mutex>, +} + +#[napi] +impl DirectAudioCapture { + #[napi(constructor)] + pub fn new() -> Self { + Self { + backend: Mutex::new(open_direct_backend()), + lifecycle_tsfn: Mutex::new(None), + } + } + + #[napi(js_name = "setLifecycleCallback")] + pub fn set_lifecycle_callback(&self, callback: Function<(String, String), ()>) -> Result<()> { + let tsfn: LifecycleTsfn = Arc::new( + callback + .build_threadsafe_function::<(String, String)>() + .max_queue_size::<8>() + .build_callback(|ctx| Ok(ctx.value))?, + ); + let mut guard = self + .lifecycle_tsfn + .lock() + .map_err(|_| generic_error("DirectAudioCapture lifecycle poisoned"))?; + *guard = Some(tsfn); + Ok(()) + } + + #[napi] + pub fn start(&self, rule: Object) -> Result { + let parsed = parse_routing_rule(&rule)?; + let guard = self + .backend + .lock() + .map_err(|_| generic_error("DirectAudioCapture backend poisoned"))?; + Ok(guard.as_ref().is_some_and(|b| b.start(parsed))) + } + + #[napi] + pub fn set_rule(&self, rule: Object) -> Result { + let parsed = parse_routing_rule(&rule)?; + let guard = self + .backend + .lock() + .map_err(|_| generic_error("DirectAudioCapture backend poisoned"))?; + Ok(guard.as_ref().is_some_and(|b| b.set_rule(parsed))) + } + + #[napi] + pub fn read<'env>(&self, env: &'env Env) -> Result>> { + let guard = self + .backend + .lock() + .map_err(|_| generic_error("DirectAudioCapture backend poisoned"))?; + let Some(backend) = guard.as_ref() else { + return Ok(None); + }; + let Some(frame) = backend.read() else { + return Ok(None); + }; + let arraybuffer = audio_samples_to_arraybuffer(env, &frame.samples)?; + Ok(Some(NativeAudioFrame { + samples: arraybuffer, + sample_rate: frame.sample_rate, + channels: frame.channels, + timestamp_us: frame.timestamp_us.max(0) as f64, + })) + } + + #[napi(js_name = "setScreenAudioSink")] + pub fn set_screen_audio_sink(&self, sink_handle: Unknown<'_>) -> Result<()> { + let sink = retain_screen_audio_sink_handle(sink_handle)?; + if !sink.supports_screen_audio() { + return Err(generic_error( + "DirectAudioCapture.setScreenAudioSink handle does not support screen audio", + )); + } + let guard = self + .backend + .lock() + .map_err(|_| generic_error("DirectAudioCapture backend poisoned"))?; + if let Some(b) = guard.as_ref() { + b.set_screen_audio_sink(sink); + } + Ok(()) + } + + #[napi(js_name = "clearScreenAudioSink")] + pub fn clear_screen_audio_sink(&self) -> Result<()> { + let guard = self + .backend + .lock() + .map_err(|_| generic_error("DirectAudioCapture backend poisoned"))?; + if let Some(b) = guard.as_ref() { + b.clear_screen_audio_sink(); + } + Ok(()) + } + + #[napi] + pub fn stop(&self) -> Result<()> { + let guard = self + .backend + .lock() + .map_err(|_| generic_error("DirectAudioCapture backend poisoned"))?; + if let Some(b) = guard.as_ref() { + b.stop(); + } + drop(guard); + self.emit_lifecycle("closed-clean", "direct audio capture stopped"); + Ok(()) + } + + #[napi(js_name = "routingGraph")] + pub fn routing_graph(&self) -> Result { + let guard = self + .backend + .lock() + .map_err(|_| generic_error("DirectAudioCapture backend poisoned"))?; + let graph = guard + .as_ref() + .map(|b| b.routing_graph()) + .unwrap_or_default(); + Ok(RoutingGraphWire(graph)) + } +} + +impl Default for DirectAudioCapture { + fn default() -> Self { + Self::new() + } +} + +impl DirectAudioCapture { + fn emit_lifecycle(&self, kind: &str, message: &str) { + let tsfn = self + .lifecycle_tsfn + .lock() + .ok() + .and_then(|guard| guard.as_ref().cloned()); + let Some(tsfn) = tsfn else { + return; + }; + let _: Status = tsfn.call( + (kind.to_string(), message.to_string()), + ThreadsafeFunctionCallMode::NonBlocking, + ); + } +} + +#[napi] +pub struct AudioMixRuntimeHandle { + inner: Mutex>, + source_count: u32, + mark_pushed_total: Arc, +} + +#[napi] +impl AudioMixRuntimeHandle { + #[napi(constructor)] + pub fn new(source_count: u32) -> Result { + Self::build(source_count, None) + } + + #[napi(factory, js_name = "boundToDirectCapture")] + pub fn bound_to_direct_capture(direct: &DirectAudioCapture) -> Result { + let arc = direct_capture_freshness(direct)?; + Self::build(1, Some(arc)) + } + + fn build( + source_count: u32, + bound_freshness: Option>, + ) -> Result { + if source_count == 0 { + return Err(invalid_arg( + "AudioMixRuntimeHandle requires at least 1 source", + )); + } + if source_count as usize > fluxer_audio_mix::MAX_MIX_SOURCES { + return Err(invalid_arg("AudioMixRuntimeHandle exceeds MAX_MIX_SOURCES")); + } + let clock: Arc = + Arc::new(fluxer_rt_thread::SystemMonotonicClock::new()); + let mut builder = + crate::audio_mix_runtime::AudioMixRuntimeBuilder::new().with_clock(Arc::clone(&clock)); + for index in 0..source_count { + let source_id = (index as u64) + 1; + let (_source, consumer) = crate::audio_mix_runtime::CaptureSource::create( + source_id, + crate::audio_mix_runtime::MIX_SAMPLE_RATE_HZ, + crate::audio_mix_runtime::MIX_CHANNELS, + ) + .map_err(|_| generic_error("CaptureSource::create failed"))?; + let freshness = if index == 0 { + match &bound_freshness { + Some(arc) => Arc::clone(arc), + None => Arc::new(std::sync::atomic::AtomicU64::new(u64::MAX)), + } + } else { + Arc::new(std::sync::atomic::AtomicU64::new(u64::MAX)) + }; + builder = builder.add_source_with_freshness(source_id, consumer, freshness); + } + let runtime = builder + .build(crate::audio_mix_runtime::NullMixOutputSink) + .map_err(|_| generic_error("AudioMixRuntimeBuilder::build failed"))?; + let mark_pushed_total = runtime.mark_pushed_total_arc(); + Ok(Self { + inner: Mutex::new(Some(runtime)), + source_count, + mark_pushed_total, + }) + } + + #[napi(js_name = "sourceCount")] + pub fn source_count_js(&self) -> u32 { + assert!(self.source_count > 0); + assert!(self.source_count as usize <= fluxer_audio_mix::MAX_MIX_SOURCES); + self.source_count + } + + #[napi] + pub fn tick(&self, tick_at_ns: Option) -> Result { + use fluxer_rt_thread::MonotonicClock as _; + assert!(self.source_count > 0); + let mut guard = self + .inner + .lock() + .map_err(|_| generic_error("AudioMixRuntimeHandle poisoned"))?; + let runtime = guard + .as_mut() + .ok_or_else(|| generic_error("AudioMixRuntimeHandle disposed"))?; + let at_ns: u64 = match tick_at_ns { + Some(v) if v > 0 => v as u64, + _ => fluxer_rt_thread::SystemMonotonicClock::new().now_ns(), + }; + assert!(at_ns > 0); + let marked = runtime + .observe_source_pushes_without_mix(at_ns) + .map_err(|_| generic_error("AudioMixRuntime tick failed"))?; + Ok(marked.min(u32::MAX as u64) as u32) + } + + #[napi(js_name = "markPushedTotal")] + pub fn mark_pushed_total_js(&self) -> u32 { + assert!(self.source_count > 0); + let value = self + .mark_pushed_total + .load(std::sync::atomic::Ordering::Acquire); + let clamped = value.min(u32::MAX as u64); + assert!(clamped <= u32::MAX as u64); + clamped as u32 + } + + #[napi] + pub fn dispose(&self) -> Result<()> { + assert!(self.source_count > 0); + assert!(self.source_count as usize <= fluxer_audio_mix::MAX_MIX_SOURCES); + let mut guard = self + .inner + .lock() + .map_err(|_| generic_error("AudioMixRuntimeHandle poisoned"))?; + guard.take(); + Ok(()) + } +} + +fn direct_capture_freshness( + direct: &DirectAudioCapture, +) -> Result> { + let guard = direct + .backend + .lock() + .map_err(|_| generic_error("DirectAudioCapture backend poisoned"))?; + let backend = guard + .as_ref() + .ok_or_else(|| generic_error("DirectAudioCapture backend unavailable"))?; + backend + .last_push_ns_arc() + .ok_or_else(|| generic_error("DirectAudioCapture backend lacks freshness atomic")) +} + +#[napi(object)] +pub struct NativeAudioFrame<'env> { + pub samples: ArrayBuffer<'env>, + #[napi(js_name = "sampleRate")] + pub sample_rate: u32, + pub channels: u32, + #[napi(js_name = "timestampUs")] + pub timestamp_us: f64, +} + +pub struct PropMapWire(pub PropMap); + +impl napi::bindgen_prelude::ToNapiValue for PropMapWire { + unsafe fn to_napi_value( + raw_env: napi::sys::napi_env, + value: Self, + ) -> Result { + let env = napi::Env::from_raw(raw_env); + let mut object = Object::new(&env)?; + for (key, val) in value.0 { + object.set(&key, val)?; + } + unsafe { + as napi::bindgen_prelude::ToNapiValue>::to_napi_value(raw_env, object) + } + } +} + +pub struct RoutingGraphWire(pub RoutingGraphSnapshot); + +impl napi::bindgen_prelude::ToNapiValue for RoutingGraphWire { + unsafe fn to_napi_value( + raw_env: napi::sys::napi_env, + value: Self, + ) -> Result { + let env = napi::Env::from_raw(raw_env); + let mut object = Object::new(&env)?; + object.set("backend", value.0.backend)?; + object.set("nodes", routing_graph_nodes_to_array(&env, value.0.nodes)?)?; + object.set("ports", routing_graph_ports_to_array(&env, value.0.ports)?)?; + object.set( + "ownedLinks", + routing_graph_links_to_array(&env, value.0.owned_links)?, + )?; + unsafe { + as napi::bindgen_prelude::ToNapiValue>::to_napi_value(raw_env, object) + } + } +} + +fn prop_map_to_object<'env>(env: &'env Env, props: PropMap) -> Result> { + let mut object = Object::new(env)?; + for (key, value) in props { + object.set(&key, value)?; + } + Ok(object) +} + +fn routing_graph_nodes_to_array<'env>( + env: &'env Env, + nodes: Vec, +) -> Result> { + let mut array = env.create_array(nodes.len() as u32)?; + for (index, node) in nodes.into_iter().enumerate() { + let mut object = Object::new(env)?; + object.set("id", node.id)?; + object.set("props", prop_map_to_object(env, node.props)?)?; + array.set(index as u32, object)?; + } + Ok(array) +} + +fn routing_graph_ports_to_array<'env>( + env: &'env Env, + ports: Vec, +) -> Result> { + let mut array = env.create_array(ports.len() as u32)?; + for (index, port) in ports.into_iter().enumerate() { + let mut object = Object::new(env)?; + object.set("id", port.id)?; + object.set("nodeId", port.node_id)?; + object.set("direction", port.direction)?; + object.set("channel", port.channel)?; + object.set("props", prop_map_to_object(env, port.props)?)?; + array.set(index as u32, object)?; + } + Ok(array) +} + +fn routing_graph_links_to_array<'env>( + env: &'env Env, + links: Vec, +) -> Result> { + let mut array = env.create_array(links.len() as u32)?; + for (index, link) in links.into_iter().enumerate() { + let mut object = Object::new(env)?; + object.set("outputNodeId", link.output_node_id)?; + object.set("outputPortId", link.output_port_id)?; + object.set("inputNodeId", link.input_node_id)?; + object.set("inputPortId", link.input_port_id)?; + object.set("owned", true)?; + object.set("passive", true)?; + array.set(index as u32, object)?; + } + Ok(array) +} + +fn project_inventory_entry(mut entry: PropMap, fields: &[String]) -> PropMapWire { + if fields.is_empty() { + return PropMapWire(entry); + } + let mut filtered = PropMap::with_capacity(fields.len()); + for field in fields { + if let Some(value) = entry.remove(field) { + filtered.insert(field.clone(), value); + } + } + PropMapWire(filtered) +} + +fn audio_samples_to_arraybuffer<'env>( + env: &'env Env, + samples: &[f32], +) -> Result> { + let bytes: Vec = samples + .iter() + .flat_map(|sample| sample.to_le_bytes()) + .collect(); + ArrayBuffer::from_data(env, bytes) +} + +fn validate_inventory_fields(values: Vec) -> Result> { + if values.len() as u32 > MAX_INVENTORY_FIELDS { + return Err(invalid_arg("too many inventory fields")); + } + for value in &values { + if value.len() > MAX_INVENTORY_FIELD_LENGTH { + return Err(invalid_arg("inventory field exceeds length cap")); + } + } + Ok(values) +} + +fn parse_routing_rule(value: &Object) -> Result { + Ok(RoutingRule { + include_when: parse_pattern_list(value, "include")?, + never_when: parse_pattern_list(value, "exclude")?, + pin_target_for: parse_pattern_list(value, "workaround")?, + skip_hardware_devices: read_optional_bool(value, "ignoreDevices")? + .or(read_optional_bool(value, "ignore_devices")?) + .unwrap_or(false), + only_audio_sinks: read_optional_bool(value, "onlySpeakers")? + .or(read_optional_bool(value, "only_speakers")?) + .unwrap_or(false), + only_default_audio_sink: read_optional_bool(value, "onlyDefaultSpeakers")? + .or(read_optional_bool(value, "only_default_speakers")?) + .unwrap_or(false), + }) +} + +fn parse_pattern_list(value: &Object, name: &str) -> Result> { + let Some(raw) = read_optional_unknown(value, name)? else { + return Ok(Vec::new()); + }; + if matches!( + raw.get_type()?, + napi::ValueType::Null | napi::ValueType::Undefined + ) { + return Ok(Vec::new()); + } + let array = unsafe { raw.cast::() } + .map_err(|_| invalid_arg(format!("{name} must be an array of objects")))?; + let len = array.len(); + if len > MAX_ROUTING_RULE_PATTERNS { + return Err(invalid_arg(format!("{name} exceeds pattern cap"))); + } + let mut out = Vec::with_capacity(len as usize); + for index in 0..len { + let entry = array + .get::(index) + .map_err(|_| invalid_arg(format!("{name}[{index}] must be an object")))? + .ok_or_else(|| invalid_arg(format!("{name}[{index}] must be an object")))?; + out.push(object_to_prop_map(&entry)?); + } + Ok(out) +} + +fn object_to_prop_map(object: &Object) -> Result { + let keys = Object::keys(object)?; + if keys.len() as u32 > MAX_ROUTING_RULE_KEYS_PER_PATTERN { + return Err(invalid_arg("routing pattern has too many keys")); + } + let mut out = PropMap::with_capacity(keys.len()); + for key in keys { + if key.is_empty() || key.len() > MAX_ROUTING_RULE_KEY_LENGTH { + return Err(invalid_arg("routing pattern key is empty or too long")); + } + let raw = read_optional_unknown(object, &key)? + .ok_or_else(|| invalid_arg("routing pattern value missing"))?; + if raw.get_type()? != napi::ValueType::String { + return Err(invalid_arg("routing pattern value must be a string")); + } + let value: String = unsafe { raw.cast() }?; + if value.len() > MAX_ROUTING_RULE_VALUE_LENGTH { + return Err(invalid_arg("routing pattern value too long")); + } + out.insert(key, value); + } + Ok(out) +} + +fn read_optional_unknown<'a>(object: &Object<'a>, name: &str) -> Result>> { + object.get::(name) +} + +fn read_optional_bool(object: &Object, name: &str) -> Result> { + let Some(raw) = read_optional_unknown(object, name)? else { + return Ok(None); + }; + match raw.get_type()? { + napi::ValueType::Null | napi::ValueType::Undefined => Ok(None), + napi::ValueType::Boolean => Ok(Some(unsafe { raw.cast() }?)), + _ => Err(invalid_arg(format!("{name} must be a boolean"))), + } +} + +fn generic_error(reason: impl Into) -> Error { + Error::new(Status::GenericFailure, reason.into()) +} + +fn invalid_arg(reason: impl Into) -> Error { + Error::new(Status::InvalidArg, reason.into()) +} + +#[allow(dead_code)] +fn _keep_arc_in_scope(_: Arc<()>) {} + +#[cfg(all(test, target_os = "linux"))] +mod js_path_tests { + use super::AudioMixRuntimeHandle; + use crate::pipewire::stream_ops::{ + DIRECT_CAPTURE_APM_FRAME_SAMPLES, build_test_user_data, process_audio_chunk, + }; + use fluxer_rt_thread::MonotonicClock; + use std::sync::Arc; + use std::sync::atomic::{AtomicU64, Ordering}; + + #[derive(Debug)] + struct FakeClock { + value_ns: AtomicU64, + } + + impl FakeClock { + fn new(initial_ns: u64) -> Self { + assert!(initial_ns > 0); + Self { + value_ns: AtomicU64::new(initial_ns), + } + } + } + + impl MonotonicClock for FakeClock { + fn now_ns(&self) -> u64 { + self.value_ns.load(Ordering::Acquire) + } + } + + fn make_f32_payload(samples: &[f32]) -> Vec { + assert!(!samples.is_empty()); + let mut out = Vec::with_capacity(samples.len() * 4); + for sample in samples { + out.extend_from_slice(&sample.to_ne_bytes()); + } + assert_eq!(out.len(), samples.len() * 4); + out + } + + fn build_handle_with_shared_freshness(last_push_ns: Arc) -> AudioMixRuntimeHandle { + assert!(Arc::strong_count(&last_push_ns) >= 1); + let handle = AudioMixRuntimeHandle::build(1, Some(last_push_ns)) + .expect("AudioMixRuntimeHandle build via JS path"); + assert_eq!(handle.source_count_js(), 1); + handle + } + + #[test] + fn js_runtime_tick_consumes_freshness_pushed_by_production_callback() { + let clock: Arc = Arc::new(FakeClock::new(11_000_000)); + let last_push_ns = Arc::new(AtomicU64::new(u64::MAX)); + let mut user_data = build_test_user_data(Arc::clone(&last_push_ns), Arc::clone(&clock)); + let handle = build_handle_with_shared_freshness(Arc::clone(&last_push_ns)); + assert_eq!(handle.mark_pushed_total_js(), 0); + assert_eq!(last_push_ns.load(Ordering::Acquire), u64::MAX); + let frame: Vec = (0..DIRECT_CAPTURE_APM_FRAME_SAMPLES) + .map(|n| (n as f32) * 0.0001) + .collect(); + let payload = make_f32_payload(&frame); + process_audio_chunk(&mut user_data, &payload); + let pushed_after_callback = last_push_ns.load(Ordering::Acquire); + assert_ne!(pushed_after_callback, u64::MAX); + assert_eq!(pushed_after_callback, 11_000_000); + let marked = handle + .tick(Some(pushed_after_callback as i64)) + .expect("AudioMixRuntimeHandle::tick observes freshness"); + assert_eq!(marked, 1); + let total = handle.mark_pushed_total_js(); + assert!( + total >= 1, + "AudioMixRuntimeHandle::tick did not advance mark_pushed_total ({total})", + ); + handle.dispose().expect("dispose"); + } + + #[test] + fn js_runtime_tick_idempotent_for_unchanged_freshness_atomic() { + let clock: Arc = Arc::new(FakeClock::new(22_000_000)); + let last_push_ns = Arc::new(AtomicU64::new(u64::MAX)); + let mut user_data = build_test_user_data(Arc::clone(&last_push_ns), Arc::clone(&clock)); + let handle = build_handle_with_shared_freshness(Arc::clone(&last_push_ns)); + let frame: Vec = (0..DIRECT_CAPTURE_APM_FRAME_SAMPLES) + .map(|n| (n as f32) * 0.0002) + .collect(); + let payload = make_f32_payload(&frame); + process_audio_chunk(&mut user_data, &payload); + let observed = last_push_ns.load(Ordering::Acquire); + let _ = handle.tick(Some(observed as i64)).expect("first tick"); + let after_first = handle.mark_pushed_total_js(); + assert!(after_first >= 1); + let _ = handle.tick(Some(observed as i64 + 1)).expect("second tick"); + let after_second = handle.mark_pushed_total_js(); + assert_eq!( + after_first, after_second, + "second tick must not advance mark_pushed_total when freshness atomic is unchanged", + ); + handle.dispose().expect("dispose"); + } +} diff --git a/fluxer_desktop/native/linux-audio-capture/src/pipewire/common.rs b/fluxer_desktop/native/linux-audio-capture/src/pipewire/common.rs new file mode 100644 index 0000000..efed077 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/src/pipewire/common.rs @@ -0,0 +1,433 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![allow(dead_code)] + +use std::collections::{HashMap, HashSet}; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::{Arc, Mutex}; + +use pipewire as pw; +use pw::keys; +use pw::metadata::{Metadata, MetadataListener}; +use pw::properties::{PropertiesBox, properties}; + +use fluxer_rt_thread::{PriorityProfile, RealtimePriorityGuard, RtError, RtOutcome}; + +use crate::audio_contract::{self, DIRECT_CAPTURE_SAMPLE_RATE}; +use crate::backend::{RoutingGraphLink, RoutingGraphNode, RoutingGraphPort, RoutingGraphSnapshot}; +use crate::routing::PropMap; + +pub(crate) const READY_TIMEOUT_MS: u64 = 2_000; + +pub(crate) const SINK_NODE_NAME: &str = "fluxer-screen-share"; +pub(crate) const SINK_NODE_DESCRIPTION: &str = "Fluxer Screen Share Audio"; +pub(crate) const DIRECT_SINK_PREFIX: &str = "fluxer-direct-capture"; +pub(crate) const DIRECT_SINK_DESCRIPTION: &str = "Fluxer Direct Capture Audio"; +pub(crate) const MEDIA_CLASS_CAPTURE_STREAM: &str = "Stream/Input/Audio"; +pub(crate) const PIN_TARGET_METADATA_KEY: &str = "target.object"; +pub(crate) const PIN_TARGET_METADATA_TYPE: &str = "Spa:String"; + +pub(crate) const CH_FRONT_LEFT: &str = "FL"; +pub(crate) const CH_FRONT_RIGHT: &str = "FR"; +pub(crate) const CH_MONO: &str = "MONO"; + +pub const MAX_FRAME_SAMPLES: usize = 1_920; + +const _: () = assert!(MAX_FRAME_SAMPLES > 0); +const _: () = assert!(MAX_FRAME_SAMPLES <= 8_192); + +pub(crate) static DIRECT_SINK_COUNTER: AtomicU64 = AtomicU64::new(1); + +#[derive(Copy, Clone)] +pub(crate) enum VirtualSinkKind { + LegacyVirtualSource, + PrivateAudioSink, +} + +pub(crate) fn next_direct_sink_name() -> String { + let seq = DIRECT_SINK_COUNTER.fetch_add(1, Ordering::Relaxed); + assert!(seq > 0); + format!("{DIRECT_SINK_PREFIX}-{}-{seq}", std::process::id()) +} + +pub(crate) fn acquire_audio_rt_guard() -> Option { + match RealtimePriorityGuard::acquire(PriorityProfile::Audio) { + Ok(guard) => { + log_rt_outcome(guard.outcome()); + Some(guard) + } + Err(RtError::PlatformDenied(errno)) => { + eprintln!( + "[fluxer-linux-audio] RT priority denied (errno={errno}); continuing without elevation", + ); + None + } + Err(other) => { + eprintln!("[fluxer-linux-audio] RT priority error: {other}"); + None + } + } +} + +fn log_rt_outcome(outcome: &RtOutcome) { + match outcome { + RtOutcome::Acquired => {} + RtOutcome::PartialFallback => { + eprintln!( + "[fluxer-linux-audio] RT priority partial fallback engaged (Linux EPERM path)", + ); + } + } +} + +#[derive(Default, Clone)] +pub(crate) struct PortRecord { + pub(crate) node_id: u32, + pub(crate) direction: String, + pub(crate) channel: String, + pub(crate) props: PropMap, +} + +#[derive(Default)] +pub(crate) struct InventorySnapshot { + pub(crate) nodes: HashMap, + pub(crate) clients: HashMap, + pub(crate) ports: HashMap, +} + +impl InventorySnapshot { + pub(crate) fn enriched_node_props(&self, props: &PropMap) -> PropMap { + let mut enriched = props + .get("client.id") + .and_then(|client_id| client_id.parse::().ok()) + .and_then(|client_id| self.clients.get(&client_id)) + .map(client_identity_props) + .unwrap_or_default(); + + for (key, value) in props { + enriched.insert(key.clone(), value.clone()); + } + + if !enriched.contains_key("application.process.id") + && let Some(pid) = enriched.get("pipewire.sec.pid").cloned() + { + enriched.insert("application.process.id".to_string(), pid); + } + + enriched + } + + pub(crate) fn enriched_nodes(&self) -> HashMap { + self.nodes + .iter() + .map(|(id, props)| (*id, self.enriched_node_props(props))) + .collect() + } + + pub(crate) fn enriched_node_values(&self) -> Vec { + self.nodes + .values() + .map(|props| self.enriched_node_props(props)) + .collect() + } + + pub(crate) fn routing_graph_nodes(&self) -> Vec { + let mut nodes: Vec = self + .enriched_nodes() + .into_iter() + .map(|(id, props)| RoutingGraphNode { id, props }) + .collect(); + nodes.sort_by_key(|node| node.id); + nodes + } + + pub(crate) fn routing_graph_ports(&self) -> Vec { + let mut ports: Vec = self + .ports + .iter() + .map(|(id, port)| RoutingGraphPort { + id: *id, + node_id: port.node_id, + direction: port.direction.clone(), + channel: port.channel.clone(), + props: port.props.clone(), + }) + .collect(); + ports.sort_by_key(|port| port.id); + ports + } +} + +fn client_identity_props(client: &PropMap) -> PropMap { + client + .iter() + .filter(|(key, _)| is_client_identity_key(key)) + .map(|(key, value)| (key.clone(), value.clone())) + .collect() +} + +fn is_client_identity_key(key: &str) -> bool { + key.starts_with("application.") || key.starts_with("pipewire.sec.") +} + +pub(crate) struct MetadataWatch { + pub(crate) metadata: Metadata, + pub(crate) is_default: bool, + pub(crate) _listener: MetadataListener, +} + +pub(crate) fn collect_props(dict: Option<&pw::spa::utils::dict::DictRef>) -> PropMap { + let mut props = PropMap::new(); + if let Some(d) = dict { + for (k, v) in d.iter() { + props.insert(k.to_string(), v.to_string()); + } + } + props +} + +pub(crate) fn is_routable_media_class(class: &str) -> bool { + matches!( + class, + crate::routing::MEDIA_CLASS_PLAYBACK_STREAM + | MEDIA_CLASS_CAPTURE_STREAM + | "Audio/Source" + | "Audio/Sink" + ) +} + +#[cfg(test)] +pub(crate) fn build_virtual_sink_props() -> PropertiesBox { + build_virtual_sink_props_for( + SINK_NODE_NAME, + SINK_NODE_DESCRIPTION, + VirtualSinkKind::LegacyVirtualSource, + ) +} + +pub(crate) fn build_virtual_sink_props_for( + node_name: &str, + description: &str, + kind: VirtualSinkKind, +) -> PropertiesBox { + let media_class = match kind { + VirtualSinkKind::LegacyVirtualSource => "Audio/Source/Virtual", + VirtualSinkKind::PrivateAudioSink => "Audio/Sink", + }; + let mut props = properties! { + "factory.name" => "support.null-audio-sink", + "node.name" => node_name, + "node.nick" => node_name, + "node.description" => description, + "media.class" => media_class, + "node.virtual" => "true", + "node.passive" => "true", + "node.dont-move" => "true", + "node.dont-reconnect" => "true", + "node.latency" => audio_contract::direct_capture_latency_fraction(), + "audio.rate" => DIRECT_CAPTURE_SAMPLE_RATE.to_string(), + "audio.position" => "[FL,FR]", + "monitor.channel-volumes" => "true", + }; + if matches!(kind, VirtualSinkKind::PrivateAudioSink) { + props.insert("node.hidden", "true"); + } + props.insert("audio.channels", "2"); + props +} + +pub(crate) fn build_link_props( + src_node: u32, + src_port: u32, + sink_node: u32, + sink_port: u32, +) -> PropertiesBox { + properties! { + "object.linger" => "false", + "link.passive" => "true", + *keys::LINK_OUTPUT_NODE => src_node.to_string(), + *keys::LINK_OUTPUT_PORT => src_port.to_string(), + *keys::LINK_INPUT_NODE => sink_node.to_string(), + *keys::LINK_INPUT_PORT => sink_port.to_string(), + } +} + +pub(crate) fn pick_node_ports( + node_id: u32, + direction: &str, + ports: &HashMap, +) -> Option<(u32, u32)> { + let mut fl = None; + let mut fr = None; + let mut mono = None; + let mut candidates = Vec::new(); + for (port_id, rec) in ports.iter() { + if rec.node_id != node_id || rec.direction != direction { + continue; + } + candidates.push(*port_id); + match rec.channel.to_ascii_uppercase().as_str() { + CH_FRONT_LEFT => fl = Some(*port_id), + CH_FRONT_RIGHT => fr = Some(*port_id), + "" | CH_MONO => mono = Some(*port_id), + _ => {} + } + } + if let (Some(l), Some(r)) = (fl, fr) { + return Some((l, r)); + } + candidates.sort_unstable(); + if candidates.len() >= 2 { + return Some((candidates[0], candidates[1])); + } + mono.map(|m| (m, m)) +} + +pub(crate) fn pick_source_output_ports( + node_id: u32, + ports: &HashMap, +) -> Option<(u32, u32)> { + pick_node_ports(node_id, "out", ports) +} + +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub(crate) struct LinkKey { + pub(crate) src_node: u32, + pub(crate) src_port: u32, + pub(crate) sink_node: u32, + pub(crate) sink_port: u32, +} + +impl LinkKey { + pub(crate) fn new(src_node: u32, src_port: u32, sink_node: u32, sink_port: u32) -> Self { + Self { + src_node, + src_port, + sink_node, + sink_port, + } + } + + pub(crate) fn graph_link(self) -> RoutingGraphLink { + RoutingGraphLink { + output_node_id: self.src_node, + output_port_id: self.src_port, + input_node_id: self.sink_node, + input_port_id: self.sink_port, + } + } +} + +pub(crate) struct OwnedLink { + pub(crate) key: LinkKey, + pub(crate) link: pw::link::Link, +} + +pub(crate) fn create_link(core: &pw::core::CoreRc, key: LinkKey) -> Option { + let props = build_link_props(key.src_node, key.src_port, key.sink_node, key.sink_port); + let link = core + .create_object::("link-factory", &props) + .ok()?; + Some(OwnedLink { key, link }) +} + +pub(crate) fn destroy_owned_links( + core: &pw::core::CoreRc, + owned_links: &std::rc::Rc>>, + owned_link_snapshot: &Arc>>, +) { + let links = std::mem::take(&mut *owned_links.borrow_mut()); + for owned in links { + let link = owned.link; + let _ = core.destroy_object(link); + } + replace_owned_link_snapshot(owned_link_snapshot, Vec::new()); +} + +pub(crate) fn sync_owned_links( + core: &pw::core::CoreRc, + owned_links: &std::rc::Rc>>, + owned_link_snapshot: &Arc>>, + desired_links: Vec, +) { + let desired: HashSet = desired_links.into_iter().collect(); + let mut links = owned_links.borrow_mut(); + let mut index = 0; + while index < links.len() { + if desired.contains(&links[index].key) { + index += 1; + continue; + } + let removed = links.swap_remove(index); + let _ = core.destroy_object(removed.link); + } + let existing: HashSet = links.iter().map(|owned| owned.key).collect(); + for key in desired { + if existing.contains(&key) { + continue; + } + if let Some(link) = create_link(core, key) { + links.push(link); + } + } + let keys = links.iter().map(|owned| owned.key).collect(); + replace_owned_link_snapshot(owned_link_snapshot, keys); +} + +pub(crate) fn replace_owned_link_snapshot( + owned_link_snapshot: &Arc>>, + mut keys: Vec, +) { + keys.sort_by_key(|key| (key.src_node, key.src_port, key.sink_node, key.sink_port)); + if let Ok(mut guard) = owned_link_snapshot.lock() { + *guard = keys; + } +} + +pub(crate) fn ensure_virtual_sink( + core: &pw::core::CoreRc, + sink_proxy: &std::rc::Rc>>, + node_name: &str, + description: &str, + kind: VirtualSinkKind, +) { + if sink_proxy.borrow().is_some() { + return; + } + let props = build_virtual_sink_props_for(node_name, description, kind); + if let Ok(node) = core.create_object::("adapter", &props) { + *sink_proxy.borrow_mut() = Some(node); + } +} + +pub(crate) fn build_routing_graph_snapshot( + backend: &str, + inventory: &Arc>, + owned_link_snapshot: &Arc>>, +) -> RoutingGraphSnapshot { + let (nodes, ports) = match inventory.lock() { + Ok(guard) => (guard.routing_graph_nodes(), guard.routing_graph_ports()), + Err(_) => (Vec::new(), Vec::new()), + }; + let owned_links = match owned_link_snapshot.lock() { + Ok(guard) => guard.iter().copied().map(LinkKey::graph_link).collect(), + Err(_) => Vec::new(), + }; + RoutingGraphSnapshot { + backend: backend.to_string(), + nodes, + ports, + owned_links, + } +} + +pub(crate) fn daemon_reachable() -> bool { + pw::init(); + let Ok(mainloop) = pw::main_loop::MainLoopRc::new(None) else { + return false; + }; + let Ok(context) = pw::context::ContextRc::new(&mainloop, None) else { + return false; + }; + context.connect_rc(None).is_ok() +} diff --git a/fluxer_desktop/native/linux-audio-capture/src/pipewire/device_enum.rs b/fluxer_desktop/native/linux-audio-capture/src/pipewire/device_enum.rs new file mode 100644 index 0000000..b7ad725 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/src/pipewire/device_enum.rs @@ -0,0 +1,402 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![allow(dead_code)] + +use std::sync::atomic::Ordering; +use std::sync::{Arc, Mutex}; + +use pipewire as pw; +use pw::metadata::Metadata; +use pw::types::ObjectType; + +use super::common::{ + InventorySnapshot, LinkKey, MetadataWatch, OwnedLink, PortRecord, collect_props, + is_routable_media_class, replace_owned_link_snapshot, +}; +use super::routing::{ + DirectRoutingState, RoutingState, recompute_routing, refresh_direct_sink_input_ports, + refresh_sink_input_ports, +}; +use super::stream_ops::DirectStreamRuntime; + +pub(crate) struct GlobalAddedContext<'a> { + pub(crate) registry: &'a pw::registry::RegistryRc, + pub(crate) inventory: &'a Arc>, + pub(crate) state: &'a std::rc::Rc>, + pub(crate) core: &'a pw::core::CoreRc, + pub(crate) owned_links: &'a std::rc::Rc>>, + pub(crate) owned_link_snapshot: &'a Arc>>, + pub(crate) metadata_watchers: &'a std::rc::Rc>>, + pub(crate) sink_node_name: &'a str, +} + +fn handle_global_added_client( + obj: &pw::registry::GlobalObject<&pw::spa::utils::dict::DictRef>, + ctx: &GlobalAddedContext<'_>, +) { + let props = collect_props(obj.props); + let Ok(mut snap) = ctx.inventory.lock() else { + return; + }; + snap.clients.insert(obj.id, props); + drop(snap); + if ctx.state.borrow().active_rule.is_some() { + recompute_routing( + ctx.inventory, + ctx.state, + ctx.core, + ctx.owned_links, + ctx.owned_link_snapshot, + ctx.metadata_watchers, + ctx.sink_node_name, + ); + } +} + +fn handle_global_added_node( + obj: &pw::registry::GlobalObject<&pw::spa::utils::dict::DictRef>, + ctx: &GlobalAddedContext<'_>, +) { + let props = collect_props(obj.props); + let class = props.get("media.class").cloned().unwrap_or_default(); + let node_name = props.get("node.name").cloned().unwrap_or_default(); + let is_our_sink = node_name == ctx.sink_node_name; + if !is_our_sink && !is_routable_media_class(&class) { + return; + } + let Ok(mut snap) = ctx.inventory.lock() else { + return; + }; + snap.nodes.insert(obj.id, props); + drop(snap); + if is_our_sink { + ctx.state.borrow_mut().sink_global_id = obj.id; + refresh_sink_input_ports(ctx.inventory, ctx.state); + recompute_routing( + ctx.inventory, + ctx.state, + ctx.core, + ctx.owned_links, + ctx.owned_link_snapshot, + ctx.metadata_watchers, + ctx.sink_node_name, + ); + return; + } + if ctx.state.borrow().active_rule.is_some() { + recompute_routing( + ctx.inventory, + ctx.state, + ctx.core, + ctx.owned_links, + ctx.owned_link_snapshot, + ctx.metadata_watchers, + ctx.sink_node_name, + ); + } +} + +fn build_port_record(props: &crate::routing::PropMap) -> Option { + let node_id = props.get("node.id").and_then(|s| s.parse::().ok())?; + if node_id == 0 { + return None; + } + let direction = props + .get("port.direction") + .map(String::as_str) + .unwrap_or("") + .to_ascii_lowercase(); + let channel = props.get("audio.channel").cloned().unwrap_or_default(); + Some(PortRecord { + node_id, + direction, + channel, + props: props.clone(), + }) +} + +fn handle_global_added_port( + obj: &pw::registry::GlobalObject<&pw::spa::utils::dict::DictRef>, + ctx: &GlobalAddedContext<'_>, +) { + let props = collect_props(obj.props); + let Some(record) = build_port_record(&props) else { + return; + }; + let node_id = record.node_id; + let Ok(mut snap) = ctx.inventory.lock() else { + return; + }; + snap.ports.insert(obj.id, record); + drop(snap); + let sink_id = ctx.state.borrow().sink_global_id; + if sink_id != 0 && node_id == sink_id { + refresh_sink_input_ports(ctx.inventory, ctx.state); + } + if ctx.state.borrow().active_rule.is_some() { + recompute_routing( + ctx.inventory, + ctx.state, + ctx.core, + ctx.owned_links, + ctx.owned_link_snapshot, + ctx.metadata_watchers, + ctx.sink_node_name, + ); + } +} + +fn handle_global_added_metadata( + obj: &pw::registry::GlobalObject<&pw::spa::utils::dict::DictRef>, + ctx: &GlobalAddedContext<'_>, +) { + let metadata_name = obj + .props + .and_then(|dict| dict.get("metadata.name")) + .unwrap_or("") + .to_string(); + let is_default = metadata_name == "default"; + let Ok(metadata) = ctx.registry.bind::(obj) else { + return; + }; + let inv = ctx.inventory.clone(); + let st = ctx.state.clone(); + let core_for_listener = ctx.core.clone(); + let owned_for_listener = ctx.owned_links.clone(); + let link_snapshot_for_listener = ctx.owned_link_snapshot.clone(); + let metadata_watchers_for_listener = ctx.metadata_watchers.clone(); + let sink_node_name_owned = ctx.sink_node_name.to_string(); + let listener = metadata + .add_listener_local() + .property(move |_subject, key, _type_, value| { + if key == Some("default.audio.sink") { + let name = value + .map(crate::routing::parse_default_sink_name) + .unwrap_or_default(); + st.borrow_mut().default_sink_name = name; + recompute_routing( + &inv, + &st, + &core_for_listener, + &owned_for_listener, + &link_snapshot_for_listener, + &metadata_watchers_for_listener, + &sink_node_name_owned, + ); + } + 0 + }) + .register(); + ctx.metadata_watchers.borrow_mut().push(MetadataWatch { + metadata, + is_default, + _listener: listener, + }); + if ctx.state.borrow().active_rule.is_some() { + recompute_routing( + ctx.inventory, + ctx.state, + ctx.core, + ctx.owned_links, + ctx.owned_link_snapshot, + ctx.metadata_watchers, + ctx.sink_node_name, + ); + } +} + +pub(crate) fn handle_global_added( + obj: &pw::registry::GlobalObject<&pw::spa::utils::dict::DictRef>, + ctx: GlobalAddedContext<'_>, +) { + match obj.type_ { + ObjectType::Client => handle_global_added_client(obj, &ctx), + ObjectType::Node => handle_global_added_node(obj, &ctx), + ObjectType::Port => handle_global_added_port(obj, &ctx), + ObjectType::Metadata => handle_global_added_metadata(obj, &ctx), + _ => {} + } +} + +pub(crate) fn handle_global_removed( + id: u32, + inventory: &Arc>, + state: &std::rc::Rc>, +) -> bool { + let mut changed = false; + if let Ok(mut snap) = inventory.lock() { + changed |= snap.nodes.remove(&id).is_some(); + changed |= snap.clients.remove(&id).is_some(); + changed |= snap.ports.remove(&id).is_some(); + } + let mut st = state.borrow_mut(); + let removed_sink = st.sink_global_id == id; + if st.sink_global_id == id { + st.sink_global_id = 0; + st.sink_input_fl = None; + st.sink_input_fr = None; + } + if st.sink_input_fl == Some(id) { + st.sink_input_fl = None; + } + if st.sink_input_fr == Some(id) { + st.sink_input_fr = None; + } + changed |= st.pinned_capture_nodes.remove(&id); + changed || removed_sink +} + +pub(crate) struct DirectGlobalAddedArgs<'a> { + pub(crate) registry: &'a pw::registry::RegistryRc, + pub(crate) inventory: &'a Arc>, + pub(crate) state: &'a std::rc::Rc>, + pub(crate) core: &'a pw::core::CoreRc, + pub(crate) runtime: &'a DirectStreamRuntime, + pub(crate) metadata_watchers: &'a std::rc::Rc>>, +} + +fn direct_added_client( + obj: &pw::registry::GlobalObject<&pw::spa::utils::dict::DictRef>, + args: &DirectGlobalAddedArgs<'_>, +) -> bool { + let props = collect_props(obj.props); + let Ok(mut snap) = args.inventory.lock() else { + return false; + }; + snap.clients.insert(obj.id, props); + true +} + +fn direct_added_node( + obj: &pw::registry::GlobalObject<&pw::spa::utils::dict::DictRef>, + args: &DirectGlobalAddedArgs<'_>, +) -> bool { + let props = collect_props(obj.props); + let class = props.get("media.class").cloned().unwrap_or_default(); + let node_name = props.get("node.name").cloned().unwrap_or_default(); + let is_our_sink = node_name == args.runtime.sink_node_name; + if !is_our_sink && !is_routable_media_class(&class) { + return false; + } + { + let Ok(mut snap) = args.inventory.lock() else { + return false; + }; + snap.nodes.insert(obj.id, props); + } + if is_our_sink { + args.state.borrow_mut().sink_global_id = obj.id; + refresh_direct_sink_input_ports(args.inventory, args.state); + } + true +} + +fn direct_added_port( + obj: &pw::registry::GlobalObject<&pw::spa::utils::dict::DictRef>, + args: &DirectGlobalAddedArgs<'_>, +) -> bool { + let props = collect_props(obj.props); + let Some(record) = build_port_record(&props) else { + return false; + }; + let node_id = record.node_id; + { + let Ok(mut snap) = args.inventory.lock() else { + return false; + }; + snap.ports.insert(obj.id, record); + } + let sink_id = args.state.borrow().sink_global_id; + if sink_id != 0 && node_id == sink_id { + refresh_direct_sink_input_ports(args.inventory, args.state); + } + true +} + +fn direct_added_metadata( + obj: &pw::registry::GlobalObject<&pw::spa::utils::dict::DictRef>, + args: &DirectGlobalAddedArgs<'_>, + on_default_change: impl Fn() + 'static, +) -> bool { + let metadata_name = obj + .props + .and_then(|dict| dict.get("metadata.name")) + .unwrap_or("") + .to_string(); + let is_default = metadata_name == "default"; + let Ok(metadata) = args.registry.bind::(obj) else { + return false; + }; + let st = args.state.clone(); + let listener = metadata + .add_listener_local() + .property(move |_subject, key, _type_, value| { + if key == Some("default.audio.sink") { + let name = value + .map(crate::routing::parse_default_sink_name) + .unwrap_or_default(); + st.borrow_mut().default_sink_name = name; + on_default_change(); + } + 0 + }) + .register(); + args.metadata_watchers.borrow_mut().push(MetadataWatch { + metadata, + is_default, + _listener: listener, + }); + false +} + +pub(crate) fn handle_direct_global_added( + obj: &pw::registry::GlobalObject<&pw::spa::utils::dict::DictRef>, + args: DirectGlobalAddedArgs<'_>, + on_default_change: impl Fn() + 'static, +) -> bool { + match obj.type_ { + ObjectType::Client => direct_added_client(obj, &args), + ObjectType::Node => direct_added_node(obj, &args), + ObjectType::Port => direct_added_port(obj, &args), + ObjectType::Metadata => direct_added_metadata(obj, &args, on_default_change), + _ => false, + } +} + +pub(crate) fn handle_direct_global_removed( + id: u32, + inventory: &Arc>, + state: &std::rc::Rc>, + runtime: &DirectStreamRuntime, +) -> bool { + let mut changed = false; + if let Ok(mut snap) = inventory.lock() { + changed |= snap.nodes.remove(&id).is_some(); + changed |= snap.clients.remove(&id).is_some(); + changed |= snap.ports.remove(&id).is_some(); + } + { + let mut st = state.borrow_mut(); + if st.sink_global_id == id { + st.sink_global_id = 0; + st.sink_input_fl = None; + st.sink_input_fr = None; + runtime.sink_proxy.borrow_mut().take(); + runtime.owned_links.borrow_mut().clear(); + replace_owned_link_snapshot(&runtime.owned_link_snapshot, Vec::new()); + *runtime.active_stream.borrow_mut() = None; + *runtime.active_listener.borrow_mut() = None; + runtime.running.store(false, Ordering::Relaxed); + changed = true; + } + if st.sink_input_fl == Some(id) { + st.sink_input_fl = None; + changed = true; + } + if st.sink_input_fr == Some(id) { + st.sink_input_fr = None; + changed = true; + } + } + changed +} diff --git a/fluxer_desktop/native/linux-audio-capture/src/pipewire/event_loop.rs b/fluxer_desktop/native/linux-audio-capture/src/pipewire/event_loop.rs new file mode 100644 index 0000000..6152e8a --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/src/pipewire/event_loop.rs @@ -0,0 +1,485 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![allow(dead_code)] + +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use std::sync::{Arc, Mutex}; + +use pipewire as pw; +use pw::channel::Receiver as PwReceiver; +use pw::context::ContextRc; +use pw::main_loop::MainLoopRc; + +use fluxer_rt_thread::MonotonicClock; + +use crate::direct_buffer::DirectAudioBuffer; +use crate::routing::{RoutingRule, SelfIdentity}; + +use super::common::{ + DIRECT_SINK_DESCRIPTION, InventorySnapshot, LinkKey, MetadataWatch, OwnedLink, + SINK_NODE_DESCRIPTION, SINK_NODE_NAME, VirtualSinkKind, acquire_audio_rt_guard, + destroy_owned_links, ensure_virtual_sink, +}; +use super::device_enum::{ + DirectGlobalAddedArgs, GlobalAddedContext, handle_direct_global_added, + handle_direct_global_removed, handle_global_added, handle_global_removed, +}; +use super::routing::{ + DirectRoutingState, RoutingState, clear_pinned_capture_targets, recompute_direct_links, + recompute_routing, refresh_direct_sink_input_ports, +}; +use super::stream_ops::{ + BuildDirectStreamArgs, DirectStreamRuntime, ScreenAudioSinkSlot, build_direct_stream, +}; + +pub(crate) enum BridgeCommand { + Apply(RoutingRule), + Release, + SetIdentity(SelfIdentity), + Shutdown, +} + +struct BridgeWorkerState { + snapshot: Arc>, + owned_link_snapshot: Arc>>, + state: std::rc::Rc>, + sink_proxy: std::rc::Rc>>, + owned_links: std::rc::Rc>>, + metadata_watchers: std::rc::Rc>>, +} + +fn init_bridge_state( + snapshot: Arc>, + owned_link_snapshot: Arc>>, +) -> BridgeWorkerState { + BridgeWorkerState { + snapshot, + owned_link_snapshot, + state: std::rc::Rc::new(std::cell::RefCell::new(RoutingState::default())), + sink_proxy: std::rc::Rc::new(std::cell::RefCell::new(None)), + owned_links: std::rc::Rc::new(std::cell::RefCell::new(Vec::new())), + metadata_watchers: std::rc::Rc::new(std::cell::RefCell::new(Vec::new())), + } +} + +fn install_bridge_registry( + registry: &pw::registry::RegistryRc, + core: &pw::core::CoreRc, + worker: &BridgeWorkerState, +) -> pw::registry::Listener { + let inv_for_global = worker.snapshot.clone(); + let link_snapshot_for_global = worker.owned_link_snapshot.clone(); + let state_for_global = worker.state.clone(); + let core_for_global = core.clone(); + let registry_for_global = registry.clone(); + let owned_for_global = worker.owned_links.clone(); + let metadata_for_global = worker.metadata_watchers.clone(); + let inv_rm = worker.snapshot.clone(); + let state_rm = worker.state.clone(); + let core_rm = core.clone(); + let owned_rm = worker.owned_links.clone(); + let link_snapshot_rm = worker.owned_link_snapshot.clone(); + let metadata_rm = worker.metadata_watchers.clone(); + registry + .add_listener_local() + .global(move |obj| { + handle_global_added( + obj, + GlobalAddedContext { + registry: ®istry_for_global, + inventory: &inv_for_global, + state: &state_for_global, + core: &core_for_global, + owned_links: &owned_for_global, + owned_link_snapshot: &link_snapshot_for_global, + metadata_watchers: &metadata_for_global, + sink_node_name: SINK_NODE_NAME, + }, + ); + }) + .global_remove(move |id| { + let changed = handle_global_removed(id, &inv_rm, &state_rm); + if changed && state_rm.borrow().active_rule.is_some() { + recompute_routing( + &inv_rm, + &state_rm, + &core_rm, + &owned_rm, + &link_snapshot_rm, + &metadata_rm, + SINK_NODE_NAME, + ); + } + }) + .register() +} + +fn install_bridge_command_handler<'a>( + mainloop: &'a MainLoopRc, + rx: PwReceiver, + core: &pw::core::CoreRc, + worker: &BridgeWorkerState, +) -> pw::channel::AttachedReceiver<'a, BridgeCommand> { + let inv_for_cmd = worker.snapshot.clone(); + let link_snapshot_for_cmd = worker.owned_link_snapshot.clone(); + let state_for_cmd = worker.state.clone(); + let core_for_cmd = core.clone(); + let owned_for_cmd = worker.owned_links.clone(); + let metadata_for_cmd = worker.metadata_watchers.clone(); + let sink_for_cmd = worker.sink_proxy.clone(); + let mainloop_weak = mainloop.downgrade(); + rx.attach(mainloop.loop_(), move |cmd| match cmd { + BridgeCommand::Apply(rule) => { + state_for_cmd.borrow_mut().active_rule = Some(rule); + ensure_virtual_sink( + &core_for_cmd, + &sink_for_cmd, + SINK_NODE_NAME, + SINK_NODE_DESCRIPTION, + VirtualSinkKind::LegacyVirtualSource, + ); + recompute_routing( + &inv_for_cmd, + &state_for_cmd, + &core_for_cmd, + &owned_for_cmd, + &link_snapshot_for_cmd, + &metadata_for_cmd, + SINK_NODE_NAME, + ); + } + BridgeCommand::Release => { + clear_pinned_capture_targets(&state_for_cmd, &metadata_for_cmd); + state_for_cmd.borrow_mut().active_rule = None; + destroy_owned_links(&core_for_cmd, &owned_for_cmd, &link_snapshot_for_cmd); + } + BridgeCommand::SetIdentity(id) => { + state_for_cmd.borrow_mut().identity = id; + } + BridgeCommand::Shutdown => { + clear_pinned_capture_targets(&state_for_cmd, &metadata_for_cmd); + destroy_owned_links(&core_for_cmd, &owned_for_cmd, &link_snapshot_for_cmd); + sink_for_cmd.borrow_mut().take(); + if let Some(ml) = mainloop_weak.upgrade() { + ml.quit(); + } + } + }) +} + +pub(crate) fn run_bridge_worker( + snapshot: Arc>, + owned_link_snapshot: Arc>>, + rx: PwReceiver, + ready_tx: std::sync::mpsc::SyncSender, +) { + let _rt_guard = acquire_audio_rt_guard(); + pw::init(); + let Ok(mainloop) = MainLoopRc::new(None) else { + let _ = ready_tx.send(false); + return; + }; + let Ok(context) = ContextRc::new(&mainloop, None) else { + let _ = ready_tx.send(false); + return; + }; + let Ok(core) = context.connect_rc(None) else { + let _ = ready_tx.send(false); + return; + }; + let Ok(registry) = core.get_registry_rc() else { + let _ = ready_tx.send(false); + return; + }; + + let worker = init_bridge_state(snapshot, owned_link_snapshot); + let _registry_listener = install_bridge_registry(®istry, &core, &worker); + let _attached_rx = install_bridge_command_handler(&mainloop, rx, &core, &worker); + + let _ = ready_tx.send(true); + mainloop.run(); +} + +pub(crate) enum DirectCommand { + Start { + rule: RoutingRule, + identity: Box, + }, + UpdateRule { + rule: RoutingRule, + }, + Stop, + Shutdown, +} + +#[derive(Clone, Copy)] +pub(crate) enum DirectSinkRetention { + Preserve, + Drop, +} + +pub(crate) fn stop_direct_streams( + core: &pw::core::CoreRc, + runtime: &DirectStreamRuntime, + sink_retention: DirectSinkRetention, +) { + *runtime.active_listener.borrow_mut() = None; + *runtime.active_stream.borrow_mut() = None; + destroy_owned_links(core, &runtime.owned_links, &runtime.owned_link_snapshot); + if matches!(sink_retention, DirectSinkRetention::Drop) { + runtime.sink_proxy.borrow_mut().take(); + } + runtime.running.store(false, Ordering::Relaxed); +} + +pub(crate) fn clear_direct_samples(samples: &Arc>) { + if let Ok(mut guard) = samples.lock() { + guard.clear(); + } +} + +pub(crate) fn recompute_direct_streams( + core: &pw::core::CoreRc, + inventory: &Arc>, + state: &std::rc::Rc>, + runtime: &DirectStreamRuntime, +) { + if state.borrow().active_rule.is_none() { + stop_direct_streams(core, runtime, DirectSinkRetention::Preserve); + return; + } + let updated = recompute_direct_links( + core, + inventory, + state, + &runtime.owned_links, + &runtime.owned_link_snapshot, + ); + if !updated { + return; + } + ensure_or_promote_direct_stream(core, runtime); +} + +fn ensure_or_promote_direct_stream(core: &pw::core::CoreRc, runtime: &DirectStreamRuntime) { + if runtime.active_stream.borrow().is_some() { + runtime.running.store(true, Ordering::Relaxed); + return; + } + let args = BuildDirectStreamArgs { + core, + samples: runtime.samples.clone(), + target_sink_name: &runtime.sink_node_name, + stream_node_name: &runtime.stream_node_name, + last_push_ns: runtime.last_push_ns.clone(), + clock: runtime.clock.clone(), + screen_audio_sink: runtime.screen_audio_sink.clone(), + }; + match build_direct_stream(args) { + Ok((stream, listener)) => { + *runtime.active_stream.borrow_mut() = Some(stream); + *runtime.active_listener.borrow_mut() = Some(listener); + runtime.running.store(true, Ordering::Relaxed); + } + Err(_) => { + runtime.running.store(false, Ordering::Relaxed); + } + } +} + +pub(crate) struct DirectWorkerInputs { + pub(crate) samples: Arc>, + pub(crate) inventory: Arc>, + pub(crate) owned_link_snapshot: Arc>>, + pub(crate) running: Arc, + pub(crate) sink_node_name: String, + pub(crate) last_push_ns: Arc, + pub(crate) clock: Arc, + pub(crate) screen_audio_sink: ScreenAudioSinkSlot, +} + +fn build_direct_runtime(inputs: &DirectWorkerInputs) -> std::rc::Rc { + let stream_node_name = format!("{}-stream", inputs.sink_node_name); + std::rc::Rc::new(DirectStreamRuntime { + active_stream: std::rc::Rc::new(std::cell::RefCell::new(None)), + active_listener: std::rc::Rc::new(std::cell::RefCell::new(None)), + owned_links: std::rc::Rc::new(std::cell::RefCell::new(Vec::new())), + owned_link_snapshot: inputs.owned_link_snapshot.clone(), + sink_proxy: std::rc::Rc::new(std::cell::RefCell::new(None)), + samples: inputs.samples.clone(), + running: inputs.running.clone(), + sink_node_name: inputs.sink_node_name.clone(), + stream_node_name, + last_push_ns: inputs.last_push_ns.clone(), + clock: inputs.clock.clone(), + screen_audio_sink: inputs.screen_audio_sink.clone(), + }) +} + +fn install_direct_registry( + registry: &pw::registry::RegistryRc, + core: &pw::core::CoreRc, + inventory: &Arc>, + state: &std::rc::Rc>, + runtime: &std::rc::Rc, + metadata_watchers: &std::rc::Rc>>, +) -> pw::registry::Listener { + let inv_added = inventory.clone(); + let state_added = state.clone(); + let core_added = core.clone(); + let runtime_added = runtime.clone(); + let registry_added = registry.clone(); + let metadata_added = metadata_watchers.clone(); + let inv_rm = inventory.clone(); + let state_rm = state.clone(); + let core_rm = core.clone(); + let runtime_rm = runtime.clone(); + registry + .add_listener_local() + .global(move |obj| { + let inv_for_cb = inv_added.clone(); + let state_for_cb = state_added.clone(); + let core_for_cb = core_added.clone(); + let runtime_for_cb = runtime_added.clone(); + let on_default_change = move || { + recompute_direct_streams(&core_for_cb, &inv_for_cb, &state_for_cb, &runtime_for_cb); + }; + let changed = handle_direct_global_added( + obj, + DirectGlobalAddedArgs { + registry: ®istry_added, + inventory: &inv_added, + state: &state_added, + core: &core_added, + runtime: &runtime_added, + metadata_watchers: &metadata_added, + }, + on_default_change, + ); + if changed { + recompute_direct_streams(&core_added, &inv_added, &state_added, &runtime_added); + } + }) + .global_remove(move |id| { + let changed = handle_direct_global_removed(id, &inv_rm, &state_rm, &runtime_rm); + if changed { + recompute_direct_streams(&core_rm, &inv_rm, &state_rm, &runtime_rm); + } + }) + .register() +} + +fn install_direct_command_handler<'a>( + mainloop: &'a MainLoopRc, + rx: PwReceiver, + core: &pw::core::CoreRc, + inventory: &Arc>, + state: &std::rc::Rc>, + runtime: &std::rc::Rc, +) -> pw::channel::AttachedReceiver<'a, DirectCommand> { + let core_for_cmd = core.clone(); + let inv_for_cmd = inventory.clone(); + let state_for_cmd = state.clone(); + let runtime_for_cmd = runtime.clone(); + let mainloop_weak = mainloop.downgrade(); + rx.attach(mainloop.loop_(), move |cmd| match cmd { + DirectCommand::Start { rule, identity } => { + clear_direct_samples(&runtime_for_cmd.samples); + { + let mut st = state_for_cmd.borrow_mut(); + st.identity = *identity; + st.active_rule = Some(rule); + } + ensure_virtual_sink( + &core_for_cmd, + &runtime_for_cmd.sink_proxy, + &runtime_for_cmd.sink_node_name, + DIRECT_SINK_DESCRIPTION, + VirtualSinkKind::PrivateAudioSink, + ); + refresh_direct_sink_input_ports(&inv_for_cmd, &state_for_cmd); + recompute_direct_streams( + &core_for_cmd, + &inv_for_cmd, + &state_for_cmd, + &runtime_for_cmd, + ); + } + DirectCommand::UpdateRule { rule } => { + let active = state_for_cmd.borrow().active_rule.is_some(); + if !active { + return; + } + state_for_cmd.borrow_mut().active_rule = Some(rule); + recompute_direct_streams( + &core_for_cmd, + &inv_for_cmd, + &state_for_cmd, + &runtime_for_cmd, + ); + } + DirectCommand::Stop => { + stop_direct_streams( + &core_for_cmd, + &runtime_for_cmd, + DirectSinkRetention::Preserve, + ); + { + let mut st = state_for_cmd.borrow_mut(); + st.active_rule = None; + } + clear_direct_samples(&runtime_for_cmd.samples); + } + DirectCommand::Shutdown => { + stop_direct_streams(&core_for_cmd, &runtime_for_cmd, DirectSinkRetention::Drop); + clear_direct_samples(&runtime_for_cmd.samples); + if let Some(ml) = mainloop_weak.upgrade() { + ml.quit(); + } + } + }) +} + +pub(crate) fn run_direct_worker( + inputs: DirectWorkerInputs, + rx: PwReceiver, + ready_tx: std::sync::mpsc::SyncSender, +) { + let _rt_guard = acquire_audio_rt_guard(); + pw::init(); + let Ok(mainloop) = MainLoopRc::new(None) else { + let _ = ready_tx.send(false); + return; + }; + let Ok(context) = ContextRc::new(&mainloop, None) else { + let _ = ready_tx.send(false); + return; + }; + let Ok(core) = context.connect_rc(None) else { + let _ = ready_tx.send(false); + return; + }; + let Ok(registry) = core.get_registry_rc() else { + let _ = ready_tx.send(false); + return; + }; + + let state: std::rc::Rc> = + std::rc::Rc::new(std::cell::RefCell::new(DirectRoutingState::default())); + let metadata_watchers: std::rc::Rc>> = + std::rc::Rc::new(std::cell::RefCell::new(Vec::new())); + let runtime = build_direct_runtime(&inputs); + let _registry_listener = install_direct_registry( + ®istry, + &core, + &inputs.inventory, + &state, + &runtime, + &metadata_watchers, + ); + let _attached_rx = + install_direct_command_handler(&mainloop, rx, &core, &inputs.inventory, &state, &runtime); + + let _ = ready_tx.send(true); + mainloop.run(); +} diff --git a/fluxer_desktop/native/linux-audio-capture/src/pipewire/mod.rs b/fluxer_desktop/native/linux-audio-capture/src/pipewire/mod.rs new file mode 100644 index 0000000..3a35268 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/src/pipewire/mod.rs @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub(crate) mod common; +pub(crate) mod device_enum; +pub(crate) mod event_loop; +pub(crate) mod routing; +pub(crate) mod stream_ops; diff --git a/fluxer_desktop/native/linux-audio-capture/src/pipewire/routing.rs b/fluxer_desktop/native/linux-audio-capture/src/pipewire/routing.rs new file mode 100644 index 0000000..c46a596 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/src/pipewire/routing.rs @@ -0,0 +1,373 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![allow(dead_code)] + +use std::collections::{HashMap, HashSet}; +use std::mem; +use std::sync::{Arc, Mutex}; + +use pipewire as pw; + +use crate::routing::{ + MEDIA_CLASS_PLAYBACK_STREAM, PropMap, RoutingRule, SelfIdentity, matches_any, should_route_node, +}; + +use super::common::{ + InventorySnapshot, LinkKey, MEDIA_CLASS_CAPTURE_STREAM, MetadataWatch, OwnedLink, + PIN_TARGET_METADATA_KEY, PIN_TARGET_METADATA_TYPE, destroy_owned_links, pick_node_ports, + pick_source_output_ports, sync_owned_links, +}; + +#[derive(Default)] +pub(crate) struct RoutingState { + pub(crate) identity: SelfIdentity, + pub(crate) active_rule: Option, + pub(crate) default_sink_name: String, + pub(crate) sink_global_id: u32, + pub(crate) pinned_capture_nodes: HashSet, + pub(crate) sink_input_fl: Option, + pub(crate) sink_input_fr: Option, +} + +pub(crate) fn refresh_sink_input_ports( + inventory: &Arc>, + state: &std::rc::Rc>, +) { + let sink_id = state.borrow().sink_global_id; + if sink_id == 0 { + return; + } + assert!(sink_id != 0); + let Ok(snap) = inventory.lock() else { + return; + }; + let ports = pick_node_ports(sink_id, "in", &snap.ports); + drop(snap); + if let Some((fl, fr)) = ports { + assert!(fl != 0); + assert!(fr != 0); + let mut st = state.borrow_mut(); + st.sink_input_fl = Some(fl); + st.sink_input_fr = Some(fr); + } +} + +pub(crate) fn default_sink_target_id( + nodes: &HashMap, + default_sink_name: &str, +) -> String { + if default_sink_name.is_empty() { + return String::new(); + } + nodes + .values() + .find(|props| { + props + .get("node.name") + .is_some_and(|name| name == default_sink_name) + }) + .and_then(|props| props.get("object.serial").cloned()) + .unwrap_or_default() +} + +pub(crate) fn matching_pinned_capture_nodes( + nodes: &HashMap, + rule: &RoutingRule, + sink_global_id: u32, +) -> HashSet { + if rule.pin_target_for.is_empty() { + return HashSet::new(); + } + nodes + .iter() + .filter_map(|(node_id, props)| { + if *node_id == sink_global_id { + return None; + } + let is_capture_stream = props + .get("media.class") + .is_some_and(|class| class == MEDIA_CLASS_CAPTURE_STREAM); + if is_capture_stream && matches_any(props, &rule.pin_target_for) { + Some(*node_id) + } else { + None + } + }) + .collect() +} + +pub(crate) fn sync_pinned_capture_targets( + nodes: &HashMap, + rule: &RoutingRule, + sink_global_id: u32, + sink_node_name: &str, + state: &std::rc::Rc>, + metadata_watchers: &std::rc::Rc>>, +) { + let desired = matching_pinned_capture_nodes(nodes, rule, sink_global_id); + let previous = state.borrow().pinned_capture_nodes.clone(); + for node_id in previous.difference(&desired) { + set_pinned_capture_target(metadata_watchers, *node_id, None); + } + for node_id in &desired { + set_pinned_capture_target(metadata_watchers, *node_id, Some(sink_node_name)); + } + state.borrow_mut().pinned_capture_nodes = desired; +} + +pub(crate) fn clear_pinned_capture_targets( + state: &std::rc::Rc>, + metadata_watchers: &std::rc::Rc>>, +) { + let pinned = mem::take(&mut state.borrow_mut().pinned_capture_nodes); + for node_id in pinned { + set_pinned_capture_target(metadata_watchers, node_id, None); + } +} + +pub(crate) fn set_pinned_capture_target( + metadata_watchers: &std::rc::Rc>>, + node_id: u32, + target: Option<&str>, +) { + assert!(node_id != 0); + let watchers = metadata_watchers.borrow(); + for watcher in watchers.iter().filter(|watcher| watcher.is_default) { + watcher.metadata.set_property( + node_id, + PIN_TARGET_METADATA_KEY, + target.map(|_| PIN_TARGET_METADATA_TYPE), + target, + ); + } +} + +struct RoutingResolved { + rule: RoutingRule, + sink_id: u32, + sink_in_fl: u32, + sink_in_fr: u32, + identity: SelfIdentity, + default_sink_name: String, +} + +fn resolve_routing_inputs( + state: &std::rc::Rc>, +) -> Option { + let st = state.borrow(); + let rule = st.active_rule.clone()?; + let sink_id = st.sink_global_id; + if sink_id == 0 { + return None; + } + let (sink_in_fl, sink_in_fr) = (st.sink_input_fl?, st.sink_input_fr?); + Some(RoutingResolved { + rule, + sink_id, + sink_in_fl, + sink_in_fr, + identity: st.identity.clone(), + default_sink_name: st.default_sink_name.clone(), + }) +} + +fn build_desired_links( + nodes: &HashMap, + ports: &HashMap, + resolved: &RoutingResolved, + default_sink_target_id: &str, +) -> Vec { + let mut desired_links = Vec::new(); + for (node_id, props) in nodes { + if *node_id == resolved.sink_id { + continue; + } + if !should_route_node( + *node_id, + props, + &resolved.rule, + &resolved.default_sink_name, + default_sink_target_id, + resolved.sink_id, + &resolved.identity, + ) { + continue; + } + let Some((src_l, src_r)) = pick_source_output_ports(*node_id, ports) else { + continue; + }; + desired_links.push(LinkKey::new( + *node_id, + src_l, + resolved.sink_id, + resolved.sink_in_fl, + )); + desired_links.push(LinkKey::new( + *node_id, + src_r, + resolved.sink_id, + resolved.sink_in_fr, + )); + } + desired_links +} + +pub(crate) fn recompute_routing( + inventory: &Arc>, + state: &std::rc::Rc>, + core: &pw::core::CoreRc, + owned_links: &std::rc::Rc>>, + owned_link_snapshot: &Arc>>, + metadata_watchers: &std::rc::Rc>>, + sink_node_name: &str, +) { + let Some(resolved) = resolve_routing_inputs(state) else { + clear_pinned_capture_targets(state, metadata_watchers); + destroy_owned_links(core, owned_links, owned_link_snapshot); + return; + }; + + let Ok(snap) = inventory.lock() else { + return; + }; + let nodes = snap.enriched_nodes(); + let ports = snap.ports.clone(); + drop(snap); + let default_sink_target_id = default_sink_target_id(&nodes, &resolved.default_sink_name); + + sync_pinned_capture_targets( + &nodes, + &resolved.rule, + resolved.sink_id, + sink_node_name, + state, + metadata_watchers, + ); + + let desired_links = build_desired_links(&nodes, &ports, &resolved, &default_sink_target_id); + sync_owned_links(core, owned_links, owned_link_snapshot, desired_links); +} + +#[derive(Default)] +pub(crate) struct DirectRoutingState { + pub(crate) identity: SelfIdentity, + pub(crate) active_rule: Option, + pub(crate) default_sink_name: String, + pub(crate) sink_global_id: u32, + pub(crate) sink_input_fl: Option, + pub(crate) sink_input_fr: Option, +} + +pub(crate) fn refresh_direct_sink_input_ports( + inventory: &Arc>, + state: &std::rc::Rc>, +) { + let sink_id = state.borrow().sink_global_id; + if sink_id == 0 { + return; + } + let Ok(snap) = inventory.lock() else { + return; + }; + let ports = pick_node_ports(sink_id, "in", &snap.ports); + drop(snap); + if let Some((fl, fr)) = ports { + let mut st = state.borrow_mut(); + st.sink_input_fl = Some(fl); + st.sink_input_fr = Some(fr); + } +} + +struct DirectResolved { + rule: RoutingRule, + sink_id: u32, + sink_in_fl: u32, + sink_in_fr: u32, + identity: SelfIdentity, + default_sink_name: String, +} + +fn resolve_direct_inputs( + state: &std::rc::Rc>, +) -> Option { + let st = state.borrow(); + let rule = st.active_rule.clone()?; + let sink_id = st.sink_global_id; + let (sink_in_fl, sink_in_fr) = (st.sink_input_fl?, st.sink_input_fr?); + Some(DirectResolved { + rule, + sink_id, + sink_in_fl, + sink_in_fr, + identity: st.identity.clone(), + default_sink_name: st.default_sink_name.clone(), + }) +} + +fn build_direct_desired_links( + nodes: &HashMap, + ports: &HashMap, + resolved: &DirectResolved, + default_sink_target_id: &str, +) -> Vec { + let mut desired_links = Vec::new(); + for (node_id, props) in nodes { + if *node_id == resolved.sink_id { + continue; + } + if !should_route_node( + *node_id, + props, + &resolved.rule, + &resolved.default_sink_name, + default_sink_target_id, + resolved.sink_id, + &resolved.identity, + ) { + continue; + } + let Some((src_l, src_r)) = pick_source_output_ports(*node_id, ports) else { + continue; + }; + desired_links.push(LinkKey::new( + *node_id, + src_l, + resolved.sink_id, + resolved.sink_in_fl, + )); + desired_links.push(LinkKey::new( + *node_id, + src_r, + resolved.sink_id, + resolved.sink_in_fr, + )); + } + desired_links +} + +pub(crate) fn recompute_direct_links( + core: &pw::core::CoreRc, + inventory: &Arc>, + state: &std::rc::Rc>, + owned_links: &std::rc::Rc>>, + owned_link_snapshot: &Arc>>, +) -> bool { + let Some(resolved) = resolve_direct_inputs(state) else { + return false; + }; + let Ok(snap) = inventory.lock() else { + return false; + }; + let nodes = snap.enriched_nodes(); + let ports = snap.ports.clone(); + drop(snap); + let default_sink_target_id = default_sink_target_id(&nodes, &resolved.default_sink_name); + let desired_links = + build_direct_desired_links(&nodes, &ports, &resolved, &default_sink_target_id); + sync_owned_links(core, owned_links, owned_link_snapshot, desired_links); + true +} + +const _: () = { + let _ = MEDIA_CLASS_PLAYBACK_STREAM; +}; diff --git a/fluxer_desktop/native/linux-audio-capture/src/pipewire/stream_ops.rs b/fluxer_desktop/native/linux-audio-capture/src/pipewire/stream_ops.rs new file mode 100644 index 0000000..7d265cb --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/src/pipewire/stream_ops.rs @@ -0,0 +1,439 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![allow(dead_code)] + +use std::mem; +use std::ops::Range; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use std::sync::{Arc, Mutex, RwLock}; + +use fluxer_screen_frame_bus::NativeScreenFrameSinkHandleRef; +use pipewire as pw; +use pw::keys; +use pw::properties::{PropertiesBox, properties}; +use pw::spa; +use spa::param::format::{MediaSubtype, MediaType}; +use spa::param::format_utils; +use spa::pod::Pod; +use spa::sys as spa_sys; + +use fluxer_audio_apm::{ + APM_MAX_FRAME_SAMPLES, ApmConfig, ApmError, AudioProcessor, StubAudioProcessor, + expected_frame_samples, +}; +use fluxer_rt_thread::MonotonicClock; + +use crate::audio_contract::{self, DIRECT_CAPTURE_CHANNELS, DIRECT_CAPTURE_SAMPLE_RATE}; +use crate::direct_buffer::DirectAudioBuffer; + +use super::common::{LinkKey, MAX_FRAME_SAMPLES, OwnedLink}; + +pub(crate) type ScreenAudioSinkSlot = Arc>>>; + +pub const DIRECT_CAPTURE_APM_FRAME_SAMPLES: usize = + (DIRECT_CAPTURE_SAMPLE_RATE as usize) / 100 * (DIRECT_CAPTURE_CHANNELS as usize); + +const _: () = assert!(DIRECT_CAPTURE_APM_FRAME_SAMPLES <= APM_MAX_FRAME_SAMPLES * 2); +const _: () = assert!(MAX_FRAME_SAMPLES >= DIRECT_CAPTURE_APM_FRAME_SAMPLES); + +pub struct DirectCaptureApm { + processor: Box, + accum_f32: Box<[f32; DIRECT_CAPTURE_APM_FRAME_SAMPLES]>, + accum_len: usize, + scratch_i16: Box<[i16; DIRECT_CAPTURE_APM_FRAME_SAMPLES]>, + expected_sample_rate_hz: u32, + expected_channels: u16, + processed_samples: u64, + apm_frames_processed: u64, +} + +impl DirectCaptureApm { + pub fn new(sample_rate_hz: u32, channels: u16) -> Result { + assert!(sample_rate_hz >= 8_000); + assert!(channels >= 1); + let processor = StubAudioProcessor::new(ApmConfig::default(), sample_rate_hz, channels)?; + let expected = expected_frame_samples(sample_rate_hz, channels); + assert!(expected > 0); + assert!(expected <= DIRECT_CAPTURE_APM_FRAME_SAMPLES); + Ok(Self { + processor: Box::new(processor), + accum_f32: Box::new([0.0; DIRECT_CAPTURE_APM_FRAME_SAMPLES]), + accum_len: 0, + scratch_i16: Box::new([0i16; DIRECT_CAPTURE_APM_FRAME_SAMPLES]), + expected_sample_rate_hz: sample_rate_hz, + expected_channels: channels, + processed_samples: 0, + apm_frames_processed: 0, + }) + } + + pub fn reconfigure(&mut self, sample_rate_hz: u32, channels: u16) -> Result<(), ApmError> { + assert!(sample_rate_hz >= 8_000); + assert!(channels >= 1); + if self.expected_sample_rate_hz == sample_rate_hz && self.expected_channels == channels { + return Ok(()); + } + let processor = StubAudioProcessor::new(ApmConfig::default(), sample_rate_hz, channels)?; + let expected = expected_frame_samples(sample_rate_hz, channels); + if expected == 0 || expected > DIRECT_CAPTURE_APM_FRAME_SAMPLES { + return Err(ApmError::ChannelsOutOfRange { channels }); + } + self.processor = Box::new(processor); + self.expected_sample_rate_hz = sample_rate_hz; + self.expected_channels = channels; + self.accum_len = 0; + Ok(()) + } + + pub fn process_in_place(&mut self, samples: &mut [f32]) -> Result { + assert!(!samples.is_empty()); + assert!(self.expected_channels >= 1); + let apm_frame_len = + expected_frame_samples(self.expected_sample_rate_hz, self.expected_channels); + assert!(apm_frame_len > 0); + assert!(apm_frame_len <= self.scratch_i16.len()); + let mut processed_complete: usize = 0; + let mut idx: usize = 0; + let total = samples.len(); + while idx < total { + let want = apm_frame_len - self.accum_len; + let take = want.min(total - idx); + for offset in 0..take { + self.accum_f32[self.accum_len + offset] = samples[idx + offset]; + } + self.accum_len += take; + idx += take; + if self.accum_len == apm_frame_len { + self.run_apm_one_frame(apm_frame_len)?; + if idx >= apm_frame_len { + let dst_lo = idx - apm_frame_len; + for offset in 0..apm_frame_len { + samples[dst_lo + offset] = self.accum_f32[offset]; + } + processed_complete += apm_frame_len; + } else { + processed_complete += take; + } + self.accum_len = 0; + self.apm_frames_processed = self.apm_frames_processed.saturating_add(1); + } + } + self.processed_samples = self.processed_samples.saturating_add(total as u64); + Ok(processed_complete) + } + + fn run_apm_one_frame(&mut self, apm_frame_len: usize) -> Result<(), ApmError> { + assert!(apm_frame_len <= self.scratch_i16.len()); + assert!(apm_frame_len <= self.accum_f32.len()); + for offset in 0..apm_frame_len { + self.scratch_i16[offset] = f32_sample_to_i16(self.accum_f32[offset]); + } + let result = self.processor.process_capture_frame( + &mut self.scratch_i16[..apm_frame_len], + self.expected_sample_rate_hz, + self.expected_channels, + ); + result?; + for offset in 0..apm_frame_len { + self.accum_f32[offset] = i16_sample_to_f32(self.scratch_i16[offset]); + } + Ok(()) + } + + pub fn apm_frames_processed(&self) -> u64 { + self.apm_frames_processed + } + + pub fn processed_samples(&self) -> u64 { + self.processed_samples + } + + pub fn pending_accumulator_len(&self) -> usize { + self.accum_len + } +} + +pub(crate) fn f32_sample_to_i16(value: f32) -> i16 { + let scaled = value * (i16::MAX as f32); + if scaled >= (i16::MAX as f32) { + return i16::MAX; + } + if scaled <= (i16::MIN as f32) { + return i16::MIN; + } + scaled as i16 +} + +pub(crate) fn i16_sample_to_f32(value: i16) -> f32 { + (value as f32) / (i16::MAX as f32) +} + +pub(crate) struct DirectUserData { + pub(crate) samples: Arc>, + pub(crate) format: spa::param::audio::AudioInfoRaw, + pub(crate) apm: Mutex, + pub(crate) scratch: Mutex>, + pub(crate) last_push_ns: Arc, + pub(crate) clock: Arc, + pub(crate) screen_audio_sink: ScreenAudioSinkSlot, +} + +pub(crate) struct DirectStreamRuntime { + pub(crate) active_stream: std::rc::Rc>>, + pub(crate) active_listener: + std::rc::Rc>>>, + pub(crate) owned_links: std::rc::Rc>>, + pub(crate) owned_link_snapshot: Arc>>, + pub(crate) sink_proxy: std::rc::Rc>>, + pub(crate) samples: Arc>, + pub(crate) running: Arc, + pub(crate) sink_node_name: String, + pub(crate) stream_node_name: String, + pub(crate) last_push_ns: Arc, + pub(crate) clock: Arc, + pub(crate) screen_audio_sink: ScreenAudioSinkSlot, +} + +pub(crate) fn direct_chunk_payload_range( + raw_len: usize, + offset: usize, + size: usize, +) -> Option> { + if size == 0 || offset >= raw_len { + return None; + } + let end = offset.checked_add(size)?.min(raw_len); + let available = end.checked_sub(offset)?; + let aligned = available - (available % mem::size_of::()); + if aligned == 0 { + return None; + } + Some(offset..offset + aligned) +} + +pub(crate) fn build_direct_audio_info() -> spa::param::audio::AudioInfoRaw { + let mut audio_info = spa::param::audio::AudioInfoRaw::new(); + audio_info.set_format(spa::param::audio::AudioFormat::F32LE); + audio_info.set_rate(DIRECT_CAPTURE_SAMPLE_RATE); + audio_info.set_channels(DIRECT_CAPTURE_CHANNELS); + let mut position = [0; spa::param::audio::MAX_CHANNELS]; + position[0] = spa_sys::SPA_AUDIO_CHANNEL_FL; + position[1] = spa_sys::SPA_AUDIO_CHANNEL_FR; + audio_info.set_position(position); + audio_info +} + +pub(crate) fn build_direct_stream_props( + target_sink_name: &str, + stream_node_name: &str, +) -> PropertiesBox { + properties! { + *keys::NODE_NAME => stream_node_name, + *keys::MEDIA_TYPE => "Audio", + *keys::MEDIA_CATEGORY => "Capture", + *keys::MEDIA_ROLE => "Music", + "media.class" => "Stream/Input/Audio", + *keys::STREAM_CAPTURE_SINK => "true", + "node.latency" => audio_contract::direct_capture_latency_fraction(), + "node.passive" => "true", + "node.virtual" => "true", + "node.hidden" => "true", + "node.dont-fallback" => "true", + "node.dont-move" => "true", + "node.dont-reconnect" => "true", + "stream.dont-remix" => "true", + "audio.rate" => DIRECT_CAPTURE_SAMPLE_RATE.to_string(), + "audio.channels" => DIRECT_CAPTURE_CHANNELS.to_string(), + "audio.position" => "[FL,FR]", + "target.object" => target_sink_name, + } +} + +fn handle_param_changed(user_data: &mut DirectUserData, id: u32, param: Option<&Pod>) { + let Some(param) = param else { return }; + if id != spa::param::ParamType::Format.as_raw() { + return; + } + let Ok((media_type, media_subtype)) = format_utils::parse_format(param) else { + return; + }; + if media_type != MediaType::Audio || media_subtype != MediaSubtype::Raw { + return; + } + if user_data.format.parse(param).is_err() { + return; + } + let rate = user_data.format.rate(); + let channels = user_data.format.channels(); + if let Ok(mut guard) = user_data.samples.lock() { + guard.set_format(rate, channels); + } + if let Ok(mut apm_guard) = user_data.apm.lock() { + let channels_u16 = channels.min(u16::MAX as u32) as u16; + let _ = apm_guard.reconfigure(rate, channels_u16.max(1)); + } +} + +fn decode_f32_into_scratch(raw_payload: &[u8], scratch: &mut [f32; MAX_FRAME_SAMPLES]) -> usize { + let sample_count = raw_payload.len() / mem::size_of::(); + let take = sample_count.min(MAX_FRAME_SAMPLES); + let mut written = 0usize; + let mut iter = raw_payload.chunks_exact(mem::size_of::()); + for slot in scratch.iter_mut().take(take) { + let Some(chunk) = iter.next() else { + break; + }; + *slot = f32::from_ne_bytes([chunk[0], chunk[1], chunk[2], chunk[3]]); + written += 1; + } + written +} + +pub(crate) fn process_audio_chunk(user_data: &mut DirectUserData, payload: &[u8]) { + let Ok(mut scratch_guard) = user_data.scratch.lock() else { + return; + }; + let written = decode_f32_into_scratch(payload, &mut scratch_guard); + if written == 0 { + return; + } + let channels = user_data.format.channels().max(1); + let aligned = audio_contract::whole_frame_sample_count(written, channels); + if aligned == 0 { + return; + } + if let Ok(mut apm_guard) = user_data.apm.lock() { + let _ = apm_guard.process_in_place(&mut scratch_guard[..aligned]); + } + let now_ns = user_data.clock.now_ns(); + if now_ns > 0 { + user_data.last_push_ns.store(now_ns, Ordering::Release); + } + if let Ok(guard) = user_data.screen_audio_sink.read() + && let Some(sink) = guard.as_ref() + { + let frames = aligned as u32 / channels; + if frames > 0 { + sink.enqueue_screen_audio_f32( + &scratch_guard[..aligned], + frames, + channels, + user_data.format.rate(), + (now_ns / 1_000) as i64, + ); + } + return; + } + if let Ok(mut samples_guard) = user_data.samples.lock() { + let now_us = (now_ns / 1_000) as i64; + samples_guard.push(&scratch_guard[..aligned], now_us); + } +} + +fn process_stream_buffer(stream: &pw::stream::Stream, user_data: &mut DirectUserData) { + let Some(mut buffer) = stream.dequeue_buffer() else { + return; + }; + let datas = buffer.datas_mut(); + if datas.is_empty() { + return; + } + let data = &mut datas[0]; + let chunk = data.chunk(); + let n_bytes = chunk.size() as usize; + let offset = chunk.offset() as usize; + let Some(raw) = data.data() else { return }; + let Some(payload) = direct_chunk_payload_range(raw.len(), offset, n_bytes) else { + return; + }; + process_audio_chunk(user_data, &raw[payload]); +} + +pub(crate) struct BuildDirectStreamArgs<'a> { + pub(crate) core: &'a pw::core::CoreRc, + pub(crate) samples: Arc>, + pub(crate) target_sink_name: &'a str, + pub(crate) stream_node_name: &'a str, + pub(crate) last_push_ns: Arc, + pub(crate) clock: Arc, + pub(crate) screen_audio_sink: ScreenAudioSinkSlot, +} + +pub(crate) fn build_direct_stream( + args: BuildDirectStreamArgs<'_>, +) -> Result< + ( + pw::stream::StreamRc, + pw::stream::StreamListener, + ), + pw::Error, +> { + let props = build_direct_stream_props(args.target_sink_name, args.stream_node_name); + let apm = DirectCaptureApm::new(DIRECT_CAPTURE_SAMPLE_RATE, DIRECT_CAPTURE_CHANNELS as u16) + .map_err(|_| pw::Error::CreationFailed)?; + let data = DirectUserData { + samples: args.samples, + format: spa::param::audio::AudioInfoRaw::new(), + apm: Mutex::new(apm), + scratch: Mutex::new(Box::new([0.0_f32; MAX_FRAME_SAMPLES])), + last_push_ns: args.last_push_ns, + clock: args.clock, + screen_audio_sink: args.screen_audio_sink, + }; + let stream = pw::stream::StreamRc::new(args.core.clone(), "fluxer-direct-capture", props)?; + let listener = stream + .add_local_listener_with_user_data(data) + .param_changed(|_, user_data, id, param| { + handle_param_changed(user_data, id, param); + }) + .process(|stream, user_data| { + process_stream_buffer(stream, user_data); + }) + .register()?; + + let audio_info = build_direct_audio_info(); + let obj = spa::pod::Object { + type_: spa::utils::SpaTypes::ObjectParamFormat.as_raw(), + id: spa::param::ParamType::EnumFormat.as_raw(), + properties: audio_info.into(), + }; + let values: Vec = spa::pod::serialize::PodSerializer::serialize( + std::io::Cursor::new(Vec::new()), + &spa::pod::Value::Object(obj), + ) + .map_err(|_| pw::Error::CreationFailed)? + .0 + .into_inner(); + let mut params = [Pod::from_bytes(&values).ok_or(pw::Error::CreationFailed)?]; + + stream.connect( + spa::utils::Direction::Input, + None, + pw::stream::StreamFlags::AUTOCONNECT + | pw::stream::StreamFlags::MAP_BUFFERS + | pw::stream::StreamFlags::RT_PROCESS, + &mut params, + )?; + Ok((stream, listener)) +} + +#[cfg(test)] +pub(crate) fn build_test_user_data( + last_push_ns: Arc, + clock: Arc, +) -> DirectUserData { + let apm = DirectCaptureApm::new(DIRECT_CAPTURE_SAMPLE_RATE, DIRECT_CAPTURE_CHANNELS as u16) + .expect("apm"); + let mut format = spa::param::audio::AudioInfoRaw::new(); + format.set_rate(DIRECT_CAPTURE_SAMPLE_RATE); + format.set_channels(DIRECT_CAPTURE_CHANNELS); + DirectUserData { + samples: Arc::new(Mutex::new(DirectAudioBuffer::default_format())), + format, + apm: Mutex::new(apm), + scratch: Mutex::new(Box::new([0.0_f32; MAX_FRAME_SAMPLES])), + last_push_ns, + clock, + screen_audio_sink: Arc::new(RwLock::new(None)), + } +} diff --git a/fluxer_desktop/native/linux-audio-capture/src/pipewire_bridge.rs b/fluxer_desktop/native/linux-audio-capture/src/pipewire_bridge.rs new file mode 100644 index 0000000..277e1f5 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/src/pipewire_bridge.rs @@ -0,0 +1,970 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::sync::atomic::{AtomicBool, AtomicU64}; +use std::sync::{Arc, Mutex, RwLock}; +use std::thread::{self, JoinHandle}; +use std::time::Duration; + +use fluxer_screen_frame_bus::NativeScreenFrameSinkHandleRef; +use pipewire as pw; +use pw::channel::{Sender as PwSender, channel as pw_channel}; + +use fluxer_rt_thread::{MonotonicClock, SystemMonotonicClock}; + +use crate::audio_contract::DIRECT_CAPTURE_MAX_READ_SAMPLES; +use crate::backend::{CaptureBridge, CapturedFrame, DirectCapture, RoutingGraphSnapshot}; +use crate::direct_buffer::DirectAudioBuffer; +use crate::pipewire::common::{ + InventorySnapshot, LinkKey, READY_TIMEOUT_MS, build_routing_graph_snapshot, + daemon_reachable as common_daemon_reachable, next_direct_sink_name, +}; +use crate::pipewire::event_loop::{ + BridgeCommand, DirectCommand, DirectWorkerInputs, run_bridge_worker, run_direct_worker, +}; +use crate::pipewire::stream_ops::ScreenAudioSinkSlot; +use crate::routing::{PropMap, RoutingRule, SelfIdentity}; + +pub fn daemon_reachable() -> bool { + common_daemon_reachable() +} + +pub struct PipeWireBridge { + snapshot: Arc>, + owned_link_snapshot: Arc>>, + tx: PwSender, + thread: Mutex>>, +} + +impl PipeWireBridge { + pub fn open() -> Option { + if !daemon_reachable() { + return None; + } + let snapshot = Arc::new(Mutex::new(InventorySnapshot::default())); + let owned_link_snapshot = Arc::new(Mutex::new(Vec::new())); + let (tx, rx) = pw_channel::(); + let snap_for_thread = snapshot.clone(); + let links_for_thread = owned_link_snapshot.clone(); + let (ready_tx, ready_rx) = std::sync::mpsc::sync_channel::(1); + let handle = thread::Builder::new() + .name("fluxer-pipewire-bridge".into()) + .spawn(move || { + run_bridge_worker(snap_for_thread, links_for_thread, rx, ready_tx); + }) + .ok()?; + match ready_rx.recv_timeout(Duration::from_millis(READY_TIMEOUT_MS)) { + Ok(true) => Some(Self { + snapshot, + owned_link_snapshot, + tx, + thread: Mutex::new(Some(handle)), + }), + _ => { + let _ = tx.send(BridgeCommand::Shutdown); + let _ = handle.join(); + None + } + } + } +} + +impl Drop for PipeWireBridge { + fn drop(&mut self) { + let _ = self.tx.send(BridgeCommand::Shutdown); + if let Ok(mut thread) = self.thread.lock() + && let Some(handle) = thread.take() + { + let _ = handle.join(); + } + } +} + +impl CaptureBridge for PipeWireBridge { + fn inventory(&self) -> Vec { + match self.snapshot.lock() { + Ok(guard) => guard.enriched_node_values(), + Err(_) => Vec::new(), + } + } + + fn apply(&self, rule: RoutingRule) -> bool { + self.tx.send(BridgeCommand::Apply(rule)).is_ok() + } + + fn release(&self) { + let _ = self.tx.send(BridgeCommand::Release); + } + + fn populate_self_identity(&self, identity: SelfIdentity) { + let _ = self.tx.send(BridgeCommand::SetIdentity(identity)); + } + + fn backend_name(&self) -> &'static str { + "pipewire" + } + + fn routing_graph(&self) -> RoutingGraphSnapshot { + build_routing_graph_snapshot("pipewire", &self.snapshot, &self.owned_link_snapshot) + } +} + +pub struct PipeWireDirectCapture { + samples: Arc>, + inventory: Arc>, + owned_link_snapshot: Arc>>, + tx: PwSender, + #[allow(dead_code)] + running: Arc, + thread: Mutex>>, + identity: Mutex, + #[allow(dead_code)] + last_push_ns: Arc, + screen_audio_sink: ScreenAudioSinkSlot, +} + +impl PipeWireDirectCapture { + pub fn open() -> Option { + Self::open_with_clock(Arc::new(SystemMonotonicClock::new())) + } + + pub fn open_with_clock(clock: Arc) -> Option { + if !daemon_reachable() { + return None; + } + let samples = Arc::new(Mutex::new(DirectAudioBuffer::default_format())); + let inventory = Arc::new(Mutex::new(InventorySnapshot::default())); + let owned_link_snapshot = Arc::new(Mutex::new(Vec::new())); + let running = Arc::new(AtomicBool::new(false)); + let last_push_ns = Arc::new(AtomicU64::new(u64::MAX)); + let screen_audio_sink: ScreenAudioSinkSlot = Arc::new(RwLock::new(None)); + let (tx, rx) = pw_channel::(); + let sink_node_name = next_direct_sink_name(); + let inputs = DirectWorkerInputs { + samples: samples.clone(), + inventory: inventory.clone(), + owned_link_snapshot: owned_link_snapshot.clone(), + running: running.clone(), + sink_node_name, + last_push_ns: last_push_ns.clone(), + clock, + screen_audio_sink: screen_audio_sink.clone(), + }; + let (ready_tx, ready_rx) = std::sync::mpsc::sync_channel::(1); + let handle = thread::Builder::new() + .name("fluxer-pipewire-direct".into()) + .spawn(move || { + run_direct_worker(inputs, rx, ready_tx); + }) + .ok()?; + match ready_rx.recv_timeout(Duration::from_millis(READY_TIMEOUT_MS)) { + Ok(true) => Some(Self { + samples, + inventory, + owned_link_snapshot, + tx, + running, + thread: Mutex::new(Some(handle)), + identity: Mutex::new(SelfIdentity::default()), + last_push_ns, + screen_audio_sink, + }), + _ => { + let _ = tx.send(DirectCommand::Shutdown); + let _ = handle.join(); + None + } + } + } + + #[allow(dead_code)] + pub fn last_push_ns(&self) -> u64 { + self.last_push_ns.load(std::sync::atomic::Ordering::Acquire) + } +} + +impl Drop for PipeWireDirectCapture { + fn drop(&mut self) { + let _ = self.tx.send(DirectCommand::Shutdown); + if let Ok(mut thread) = self.thread.lock() + && let Some(handle) = thread.take() + { + let _ = handle.join(); + } + } +} + +impl DirectCapture for PipeWireDirectCapture { + fn start(&self, rule: RoutingRule) -> bool { + let Ok(identity) = self.identity.lock().map(|guard| guard.clone()) else { + return false; + }; + self.tx + .send(DirectCommand::Start { + rule, + identity: Box::new(identity), + }) + .is_ok() + } + + fn set_rule(&self, rule: RoutingRule) -> bool { + self.tx.send(DirectCommand::UpdateRule { rule }).is_ok() + } + + fn read(&self) -> Option { + let mut out = Vec::with_capacity(DIRECT_CAPTURE_MAX_READ_SAMPLES); + let meta = { + let Ok(mut guard) = self.samples.lock() else { + return None; + }; + guard.read_into(&mut out)? + }; + Some(CapturedFrame { + samples: out, + sample_rate: meta.sample_rate, + channels: meta.channels, + timestamp_us: meta.timestamp_us, + }) + } + + fn stop(&self) { + let _ = self.tx.send(DirectCommand::Stop); + } + + fn set_screen_audio_sink(&self, sink: Arc) { + if let Ok(mut guard) = self.screen_audio_sink.write() { + *guard = Some(sink); + } + } + + fn clear_screen_audio_sink(&self) { + if let Ok(mut guard) = self.screen_audio_sink.write() { + *guard = None; + } + } + + fn populate_self_identity(&self, identity: SelfIdentity) { + if let Ok(mut guard) = self.identity.lock() { + *guard = identity; + } + } + + fn routing_graph(&self) -> RoutingGraphSnapshot { + build_routing_graph_snapshot("pipewire", &self.inventory, &self.owned_link_snapshot) + } + + fn last_push_ns_arc(&self) -> Option> { + Some(Arc::clone(&self.last_push_ns)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::pipewire::common::{ + DIRECT_SINK_PREFIX, MEDIA_CLASS_CAPTURE_STREAM, PortRecord, SINK_NODE_DESCRIPTION, + SINK_NODE_NAME, VirtualSinkKind, build_link_props, build_virtual_sink_props, + build_virtual_sink_props_for, is_routable_media_class, pick_node_ports, + pick_source_output_ports, + }; + use crate::pipewire::routing::{default_sink_target_id, matching_pinned_capture_nodes}; + use crate::pipewire::stream_ops::{ + DIRECT_CAPTURE_APM_FRAME_SAMPLES, DirectCaptureApm, build_direct_audio_info, + build_direct_stream_props, direct_chunk_payload_range, f32_sample_to_i16, + i16_sample_to_f32, + }; + use crate::routing::MEDIA_CLASS_PLAYBACK_STREAM; + use std::collections::{HashMap, HashSet}; + + use crate::audio_contract::{DIRECT_CAPTURE_CHANNELS, DIRECT_CAPTURE_SAMPLE_RATE}; + use crate::routing::should_route_node; + use pipewire::spa::sys as spa_sys; + + #[test] + fn is_routable_media_class_matches_audio_node_classes() { + assert!(is_routable_media_class(MEDIA_CLASS_PLAYBACK_STREAM)); + assert!(is_routable_media_class(MEDIA_CLASS_CAPTURE_STREAM)); + assert!(is_routable_media_class("Audio/Source")); + assert!(is_routable_media_class("Audio/Sink")); + assert!(!is_routable_media_class("Video/Source")); + assert!(!is_routable_media_class("")); + } + + #[test] + fn daemon_unreachable_returns_none_from_open() { + let bridge = PipeWireBridge::open(); + if let Some(b) = bridge { + let _ = b.inventory(); + b.release(); + } + } + + #[test] + fn direct_open_returns_none_or_cleans_up() { + let direct = PipeWireDirectCapture::open(); + if let Some(d) = direct { + d.stop(); + } + } + + #[test] + fn integration_smoke_apply_release_cycle_is_safe() { + let Some(bridge) = PipeWireBridge::open() else { + return; + }; + bridge.apply(RoutingRule::default()); + std::thread::sleep(Duration::from_millis(20)); + bridge.release(); + let _ = bridge.inventory(); + } + + #[test] + fn virtual_sink_props_match_legacy_contract() { + let props = build_virtual_sink_props(); + let dict = props.dict(); + assert_eq!(dict.get("factory.name"), Some("support.null-audio-sink")); + assert_eq!(dict.get("node.name"), Some(SINK_NODE_NAME)); + assert_eq!(dict.get("node.nick"), Some(SINK_NODE_NAME)); + assert_eq!(dict.get("node.description"), Some(SINK_NODE_DESCRIPTION)); + assert_eq!(dict.get("media.class"), Some("Audio/Source/Virtual")); + assert_eq!(dict.get("node.virtual"), Some("true")); + assert_eq!(dict.get("node.passive"), Some("true")); + assert_eq!(dict.get("node.dont-move"), Some("true")); + assert_eq!(dict.get("node.dont-reconnect"), Some("true")); + assert_eq!(dict.get("node.latency"), Some("4096/48000")); + assert_eq!(dict.get("audio.rate"), Some("48000")); + assert_eq!(dict.get("audio.channels"), Some("2")); + assert_eq!(dict.get("audio.position"), Some("[FL,FR]")); + assert_eq!(dict.get("monitor.channel-volumes"), Some("true")); + } + + #[test] + fn link_props_carry_per_port_routing() { + let props = build_link_props(101, 7, 202, 13); + let dict = props.dict(); + assert_eq!(dict.get("link.output.node"), Some("101")); + assert_eq!(dict.get("link.output.port"), Some("7")); + assert_eq!(dict.get("link.input.node"), Some("202")); + assert_eq!(dict.get("link.input.port"), Some("13")); + assert_eq!(dict.get("object.linger"), Some("false")); + assert_eq!(dict.get("link.passive"), Some("true")); + } + + #[test] + fn direct_stream_props_capture_private_sink_monitor() { + let props = build_direct_stream_props( + "fluxer-direct-capture-7-1", + "fluxer-direct-capture-7-1-stream", + ); + let dict = props.dict(); + assert_eq!( + dict.get("node.name"), + Some("fluxer-direct-capture-7-1-stream") + ); + assert_eq!(dict.get("media.type"), Some("Audio")); + assert_eq!(dict.get("media.category"), Some("Capture")); + assert_eq!(dict.get("media.class"), Some("Stream/Input/Audio")); + assert_eq!( + dict.get("stream.capture.sink"), + Some("true"), + "must tap the sink monitor; tapping a Stream/Output/Audio target.object does not produce frames", + ); + assert_eq!(dict.get("node.passive"), Some("true")); + assert_eq!(dict.get("node.virtual"), Some("true")); + assert_eq!(dict.get("node.hidden"), Some("true")); + assert_eq!(dict.get("node.dont-fallback"), Some("true")); + assert_eq!(dict.get("node.dont-move"), Some("true")); + assert_eq!(dict.get("node.dont-reconnect"), Some("true")); + assert_eq!(dict.get("stream.dont-remix"), Some("true")); + assert_eq!(dict.get("node.latency"), Some("4096/48000")); + assert_eq!(dict.get("audio.rate"), Some("48000")); + assert_eq!(dict.get("audio.channels"), Some("2")); + assert_eq!(dict.get("audio.position"), Some("[FL,FR]")); + assert_eq!(dict.get("target.object"), Some("fluxer-direct-capture-7-1")); + } + + #[test] + fn direct_chunk_payload_range_respects_pipewire_chunk_offset() { + assert_eq!(direct_chunk_payload_range(64, 8, 16), Some(8..24)); + assert_eq!(direct_chunk_payload_range(18, 4, 16), Some(4..16)); + assert_eq!(direct_chunk_payload_range(64, 64, 16), None); + assert_eq!(direct_chunk_payload_range(64, 8, 0), None); + assert_eq!(direct_chunk_payload_range(10, 8, 2), None); + } + + #[test] + fn direct_audio_info_advertises_stereo_fl_fr() { + let info = build_direct_audio_info(); + assert_eq!( + info.format(), + pipewire::spa::param::audio::AudioFormat::F32LE + ); + assert_eq!(info.rate(), DIRECT_CAPTURE_SAMPLE_RATE); + assert_eq!(info.channels(), DIRECT_CAPTURE_CHANNELS); + assert_eq!(info.position()[0], spa_sys::SPA_AUDIO_CHANNEL_FL); + assert_eq!(info.position()[1], spa_sys::SPA_AUDIO_CHANNEL_FR); + } + + #[test] + fn next_direct_sink_name_is_unique_per_call() { + let a = next_direct_sink_name(); + let b = next_direct_sink_name(); + assert_ne!(a, b, "concurrent captures must not collide on sink names"); + assert!(a.starts_with(DIRECT_SINK_PREFIX)); + assert!(b.starts_with(DIRECT_SINK_PREFIX)); + } + + #[test] + fn default_sink_target_id_uses_object_serial_for_matching_node_name() { + let nodes = HashMap::from([ + ( + 1, + PropMap::from([ + ("node.name".to_string(), "alsa_output.foo".to_string()), + ("object.serial".to_string(), "1234".to_string()), + ]), + ), + ( + 2, + PropMap::from([ + ("node.name".to_string(), "alsa_output.bar".to_string()), + ("object.serial".to_string(), "5678".to_string()), + ]), + ), + ]); + assert_eq!("1234", default_sink_target_id(&nodes, "alsa_output.foo")); + assert_eq!("", default_sink_target_id(&nodes, "missing")); + } + + #[test] + fn matching_pinned_capture_nodes_only_matches_record_stream_inputs() { + let nodes = HashMap::from([ + ( + 10, + PropMap::from([ + ( + "media.class".to_string(), + MEDIA_CLASS_CAPTURE_STREAM.to_string(), + ), + ("application.process.id".to_string(), "4242".to_string()), + ("media.name".to_string(), "RecordStream".to_string()), + ]), + ), + ( + 11, + PropMap::from([ + ( + "media.class".to_string(), + MEDIA_CLASS_PLAYBACK_STREAM.to_string(), + ), + ("application.process.id".to_string(), "4242".to_string()), + ("media.name".to_string(), "RecordStream".to_string()), + ]), + ), + ( + 12, + PropMap::from([ + ( + "media.class".to_string(), + MEDIA_CLASS_CAPTURE_STREAM.to_string(), + ), + ("application.process.id".to_string(), "4242".to_string()), + ("media.name".to_string(), "OtherCapture".to_string()), + ]), + ), + ( + 13, + PropMap::from([ + ( + "media.class".to_string(), + MEDIA_CLASS_CAPTURE_STREAM.to_string(), + ), + ("application.process.id".to_string(), "9999".to_string()), + ("media.name".to_string(), "RecordStream".to_string()), + ]), + ), + ]); + let rule = RoutingRule { + pin_target_for: vec![PropMap::from([ + ("application.process.id".to_string(), "4242".to_string()), + ("media.name".to_string(), "RecordStream".to_string()), + ])], + ..Default::default() + }; + assert_eq!( + matching_pinned_capture_nodes(&nodes, &rule, 99), + HashSet::from([10]) + ); + assert!( + matching_pinned_capture_nodes(&nodes, &rule, 10).is_empty(), + "the bridge's own sink id must never be pinned" + ); + } + + #[test] + fn private_sink_props_advertise_hidden_audio_sink() { + let private = build_virtual_sink_props_for( + "fluxer-direct-capture-7-1", + crate::pipewire::common::DIRECT_SINK_DESCRIPTION, + VirtualSinkKind::PrivateAudioSink, + ); + let legacy = build_virtual_sink_props_for( + SINK_NODE_NAME, + SINK_NODE_DESCRIPTION, + VirtualSinkKind::LegacyVirtualSource, + ); + assert_eq!(private.dict().get("media.class"), Some("Audio/Sink")); + assert_eq!(private.dict().get("node.hidden"), Some("true")); + assert_eq!( + legacy.dict().get("media.class"), + Some("Audio/Source/Virtual") + ); + assert_eq!(legacy.dict().get("node.hidden"), None); + } + + #[test] + fn inventory_enriches_nodes_with_owning_client_identity() { + let mut inventory = InventorySnapshot::default(); + inventory.clients.insert( + 77, + PropMap::from([ + ("application.name".to_string(), "Firefox".to_string()), + ("application.process.id".to_string(), "4242".to_string()), + ( + "application.process.binary".to_string(), + "firefox".to_string(), + ), + ]), + ); + inventory.nodes.insert( + 88, + PropMap::from([ + ("client.id".to_string(), "77".to_string()), + ( + "media.class".to_string(), + MEDIA_CLASS_PLAYBACK_STREAM.to_string(), + ), + ("node.name".to_string(), "Firefox output".to_string()), + ]), + ); + let enriched = inventory.enriched_nodes(); + let node = enriched.get(&88).expect("enriched node"); + assert_eq!( + node.get("application.process.id"), + Some(&"4242".to_string()) + ); + assert_eq!( + node.get("application.process.binary"), + Some(&"firefox".to_string()) + ); + assert_eq!(node.get("node.name"), Some(&"Firefox output".to_string())); + let rule = RoutingRule { + include_when: vec![PropMap::from([( + "application.process.id".to_string(), + "4242".to_string(), + )])], + ..Default::default() + }; + assert!(should_route_node( + 88, + node, + &rule, + "", + "", + 0, + &SelfIdentity::default(), + )); + } + + #[test] + fn inventory_keeps_node_properties_authoritative_over_client_props() { + let mut inventory = InventorySnapshot::default(); + inventory.clients.insert( + 77, + PropMap::from([("application.name".to_string(), "Client Name".to_string())]), + ); + inventory.nodes.insert( + 88, + PropMap::from([ + ("client.id".to_string(), "77".to_string()), + ("application.name".to_string(), "Stream Name".to_string()), + ]), + ); + let enriched = inventory.enriched_nodes(); + let node = enriched.get(&88).expect("enriched node"); + assert_eq!( + node.get("application.name"), + Some(&"Stream Name".to_string()) + ); + } + + #[test] + fn inventory_falls_back_to_pipewire_security_pid() { + let mut inventory = InventorySnapshot::default(); + inventory.clients.insert( + 77, + PropMap::from([("pipewire.sec.pid".to_string(), "5150".to_string())]), + ); + inventory.nodes.insert( + 88, + PropMap::from([("client.id".to_string(), "77".to_string())]), + ); + let enriched = inventory.enriched_nodes(); + let node = enriched.get(&88).expect("enriched node"); + assert_eq!( + node.get("application.process.id"), + Some(&"5150".to_string()) + ); + } + + #[test] + fn inventory_does_not_inherit_client_object_serial_as_node_target() { + let mut inventory = InventorySnapshot::default(); + inventory.clients.insert( + 77, + PropMap::from([ + ("application.process.id".to_string(), "4242".to_string()), + ("object.serial".to_string(), "client-serial".to_string()), + ]), + ); + inventory.nodes.insert( + 88, + PropMap::from([ + ("client.id".to_string(), "77".to_string()), + ("node.name".to_string(), "Playback Stream".to_string()), + ]), + ); + let enriched = inventory.enriched_nodes(); + let node = enriched.get(&88).expect("enriched node"); + assert_eq!( + node.get("application.process.id"), + Some(&"4242".to_string()) + ); + assert_eq!(node.get("object.serial"), None); + assert_eq!(node.get("node.name"), Some(&"Playback Stream".to_string())); + } + + fn port(node_id: u32, dir: &str, ch: &str) -> PortRecord { + PortRecord { + node_id, + direction: dir.into(), + channel: ch.into(), + props: PropMap::new(), + } + } + + #[test] + fn pick_source_output_ports_prefers_stereo_pair() { + let mut ports = HashMap::new(); + ports.insert(1, port(42, "out", "fl")); + ports.insert(2, port(42, "out", "fr")); + ports.insert(3, port(42, "in", "FL")); + ports.insert(4, port(99, "out", "FL")); + let (l, r) = pick_source_output_ports(42, &ports).expect("stereo pair"); + assert_eq!(l, 1); + assert_eq!(r, 2); + } + + #[test] + fn pick_source_output_ports_falls_back_to_first_two_jack_style_ports() { + let mut ports = HashMap::new(); + ports.insert(30, port(42, "out", "AUX1")); + ports.insert(20, port(42, "out", "AUX0")); + ports.insert(10, port(42, "in", "AUX0")); + let (l, r) = pick_source_output_ports(42, &ports).expect("jack-style stereo fallback"); + assert_eq!(l, 20); + assert_eq!(r, 30); + } + + #[test] + fn pick_node_ports_applies_same_fallback_to_private_capture_inputs() { + let mut ports = HashMap::new(); + ports.insert(8, port(7, "in", "1")); + ports.insert(9, port(7, "in", "2")); + let (l, r) = pick_node_ports(7, "in", &ports).expect("input stereo fallback"); + assert_eq!(l, 8); + assert_eq!(r, 9); + } + + #[test] + fn pick_source_output_ports_fans_mono_to_both_inputs() { + let mut ports = HashMap::new(); + ports.insert(7, port(42, "out", "MONO")); + let (l, r) = pick_source_output_ports(42, &ports).expect("mono fan-out"); + assert_eq!(l, 7); + assert_eq!(r, 7); + } + + #[test] + fn pick_source_output_ports_treats_blank_channel_as_mono() { + let mut ports = HashMap::new(); + ports.insert(11, port(42, "out", "")); + let (l, r) = pick_source_output_ports(42, &ports).expect("blank-channel fallback"); + assert_eq!(l, 11); + assert_eq!(r, 11); + } + + #[test] + fn pick_source_output_ports_returns_none_when_no_outputs() { + let ports = HashMap::new(); + assert!(pick_source_output_ports(42, &ports).is_none()); + } + + #[test] + fn pick_source_output_ports_returns_none_when_only_one_side_present() { + let mut ports = HashMap::new(); + ports.insert(1, port(42, "out", "FL")); + assert!(pick_source_output_ports(42, &ports).is_none()); + } + + #[test] + fn direct_capture_apm_processes_one_full_frame_increments_counter() { + let mut apm = + DirectCaptureApm::new(DIRECT_CAPTURE_SAMPLE_RATE, DIRECT_CAPTURE_CHANNELS as u16) + .expect("apm"); + assert_eq!(apm.apm_frames_processed(), 0); + let frame_len = DIRECT_CAPTURE_APM_FRAME_SAMPLES; + let mut samples = vec![0.5_f32; frame_len]; + let processed = apm.process_in_place(&mut samples).expect("process"); + assert_eq!(processed, frame_len); + assert_eq!(apm.apm_frames_processed(), 1); + assert_eq!(apm.pending_accumulator_len(), 0); + } + + #[test] + fn direct_capture_apm_accumulates_partial_frames_across_calls() { + let mut apm = + DirectCaptureApm::new(DIRECT_CAPTURE_SAMPLE_RATE, DIRECT_CAPTURE_CHANNELS as u16) + .expect("apm"); + let half = DIRECT_CAPTURE_APM_FRAME_SAMPLES / 2; + let mut first = vec![0.1_f32; half]; + let processed1 = apm.process_in_place(&mut first).expect("first"); + assert_eq!(processed1, 0); + assert_eq!(apm.apm_frames_processed(), 0); + assert_eq!(apm.pending_accumulator_len(), half); + let mut second = vec![0.2_f32; half]; + let processed2 = apm.process_in_place(&mut second).expect("second"); + assert_eq!(processed2, half); + assert_eq!(apm.apm_frames_processed(), 1); + assert_eq!(apm.pending_accumulator_len(), 0); + } + + #[test] + fn direct_capture_apm_handles_many_frames_in_one_call() { + let mut apm = + DirectCaptureApm::new(DIRECT_CAPTURE_SAMPLE_RATE, DIRECT_CAPTURE_CHANNELS as u16) + .expect("apm"); + let frame_len = DIRECT_CAPTURE_APM_FRAME_SAMPLES; + let mut samples = vec![0.25_f32; frame_len * 5]; + let processed = apm.process_in_place(&mut samples).expect("process"); + assert_eq!(processed, frame_len * 5); + assert_eq!(apm.apm_frames_processed(), 5); + assert_eq!(apm.pending_accumulator_len(), 0); + } + + #[test] + fn direct_capture_apm_stub_preserves_samples_within_tolerance() { + let mut apm = + DirectCaptureApm::new(DIRECT_CAPTURE_SAMPLE_RATE, DIRECT_CAPTURE_CHANNELS as u16) + .expect("apm"); + let frame_len = DIRECT_CAPTURE_APM_FRAME_SAMPLES; + let mut samples = vec![0.0_f32; frame_len]; + for n in 0..frame_len { + samples[n] = ((n as f32) / (frame_len as f32) - 0.5) * 0.5; + } + let original = samples.clone(); + let _ = apm.process_in_place(&mut samples).expect("process"); + for (after, before) in samples.iter().zip(original.iter()) { + let diff = (after - before).abs(); + assert!(diff < 1e-3); + } + } + + #[test] + fn direct_capture_apm_reconfigure_is_noop_when_format_unchanged() { + let mut apm = + DirectCaptureApm::new(DIRECT_CAPTURE_SAMPLE_RATE, DIRECT_CAPTURE_CHANNELS as u16) + .expect("apm"); + let half = DIRECT_CAPTURE_APM_FRAME_SAMPLES / 2; + let mut samples = vec![0.3_f32; half]; + let _ = apm.process_in_place(&mut samples).expect("first"); + assert_eq!(apm.pending_accumulator_len(), half); + apm.reconfigure(DIRECT_CAPTURE_SAMPLE_RATE, DIRECT_CAPTURE_CHANNELS as u16) + .expect("noop"); + assert_eq!(apm.pending_accumulator_len(), half); + } + + #[test] + fn direct_capture_apm_reconfigure_changes_format_and_resets_accum() { + let mut apm = + DirectCaptureApm::new(DIRECT_CAPTURE_SAMPLE_RATE, DIRECT_CAPTURE_CHANNELS as u16) + .expect("apm"); + let half = DIRECT_CAPTURE_APM_FRAME_SAMPLES / 2; + let mut samples = vec![0.3_f32; half]; + let _ = apm.process_in_place(&mut samples).expect("first"); + assert!(apm.pending_accumulator_len() > 0); + apm.reconfigure(16_000, 1).expect("reconfigure"); + assert_eq!(apm.pending_accumulator_len(), 0); + } + + #[test] + fn f32_to_i16_clamps_above_one() { + assert_eq!(f32_sample_to_i16(2.0), i16::MAX); + assert_eq!(f32_sample_to_i16(-2.0), i16::MIN); + assert_eq!(f32_sample_to_i16(0.0), 0); + } + + #[test] + fn i16_to_f32_round_trip_is_bounded() { + for sample in [-32_768_i16, -1, 0, 1, 32_767] { + let f = i16_sample_to_f32(sample); + assert!((-1.001..=1.001).contains(&f)); + } + } + + use crate::ignore_audio_runtime::SOURCE_STALE_AFTER_NS; + use crate::pipewire::stream_ops::{build_test_user_data, process_audio_chunk}; + use fluxer_rt_thread::MonotonicClock; + use std::sync::atomic::{AtomicU64, Ordering}; + + #[derive(Debug)] + struct FakeClock { + value_ns: AtomicU64, + } + + impl FakeClock { + fn new(initial_ns: u64) -> Self { + Self { + value_ns: AtomicU64::new(initial_ns), + } + } + fn set(&self, value_ns: u64) { + self.value_ns.store(value_ns, Ordering::Release); + } + } + + impl MonotonicClock for FakeClock { + fn now_ns(&self) -> u64 { + self.value_ns.load(Ordering::Acquire) + } + } + + fn make_f32_payload(samples: &[f32]) -> Vec { + let mut out = Vec::with_capacity(samples.len() * 4); + for sample in samples { + out.extend_from_slice(&sample.to_ne_bytes()); + } + out + } + + #[test] + fn production_callback_marks_freshness_with_monotonic_clock() { + let clock = Arc::new(FakeClock::new(7_500_000)); + let last_push_ns = Arc::new(AtomicU64::new(u64::MAX)); + let mut data = build_test_user_data( + last_push_ns.clone(), + Arc::clone(&clock) as Arc, + ); + assert_eq!(last_push_ns.load(Ordering::Acquire), u64::MAX); + let frame: Vec = (0..960).map(|n| (n as f32) * 0.0001).collect(); + let payload = make_f32_payload(&frame); + process_audio_chunk(&mut data, &payload); + let observed = last_push_ns.load(Ordering::Acquire); + assert_eq!(observed, 7_500_000); + assert_ne!(observed, u64::MAX); + } + + #[test] + fn freshness_age_grows_to_signal_stale_source_after_threshold() { + let clock = Arc::new(FakeClock::new(1_000_000)); + let last_push_ns = Arc::new(AtomicU64::new(u64::MAX)); + let mut data = build_test_user_data( + last_push_ns.clone(), + Arc::clone(&clock) as Arc, + ); + let frame = vec![0.1_f32; DIRECT_CAPTURE_APM_FRAME_SAMPLES]; + let payload = make_f32_payload(&frame); + process_audio_chunk(&mut data, &payload); + let after_first = last_push_ns.load(Ordering::Acquire); + assert_eq!(after_first, 1_000_000); + clock.set(1_000_000 + SOURCE_STALE_AFTER_NS + 1); + let age = clock.now_ns() - after_first; + assert!(age > SOURCE_STALE_AFTER_NS); + clock.set(2_000_000 + SOURCE_STALE_AFTER_NS + 1); + let payload2 = make_f32_payload(&frame); + process_audio_chunk(&mut data, &payload2); + let after_second = last_push_ns.load(Ordering::Acquire); + assert!(after_second > after_first); + assert_eq!(after_second, 2_000_000 + SOURCE_STALE_AFTER_NS + 1); + let fresh_age = clock.now_ns() - after_second; + assert_eq!(fresh_age, 0); + } + + #[test] + fn callback_path_does_not_allocate_in_steady_state() { + let clock = Arc::new(FakeClock::new(1_000)); + let last_push_ns = Arc::new(AtomicU64::new(u64::MAX)); + let mut data = build_test_user_data( + last_push_ns.clone(), + Arc::clone(&clock) as Arc, + ); + let frame = vec![0.05_f32; DIRECT_CAPTURE_APM_FRAME_SAMPLES]; + let payload = make_f32_payload(&frame); + for _ in 0..400 { + clock.set(clock.now_ns() + 10_000_000); + process_audio_chunk(&mut data, &payload); + } + let allocs_before = crate::audio_mix_runtime::ALLOC_PROBE.load(Ordering::Relaxed); + crate::audio_mix_runtime::begin_thread_alloc_probe(); + clock.set(clock.now_ns() + 10_000_000); + process_audio_chunk(&mut data, &payload); + let probed = crate::audio_mix_runtime::end_thread_alloc_probe(); + let allocs_after = crate::audio_mix_runtime::ALLOC_PROBE.load(Ordering::Relaxed); + assert_eq!( + probed, + 0, + "steady-state callback allocated {probed} times (global delta {})", + allocs_after.saturating_sub(allocs_before) + ); + } + + #[test] + fn production_callback_freshness_drives_audio_mix_runtime_mark_pushed() { + use crate::audio_mix_runtime::{ + AudioMixRuntimeBuilder, CaptureSource, MIX_CHANNELS, MIX_SAMPLE_RATE_HZ, + NullMixOutputSink, + }; + let clock = Arc::new(FakeClock::new(9_000_000)); + let last_push_ns = Arc::new(AtomicU64::new(u64::MAX)); + let mut data = build_test_user_data( + last_push_ns.clone(), + Arc::clone(&clock) as Arc, + ); + let source_id: u64 = 1; + let (_source, consumer) = + CaptureSource::create(source_id, MIX_SAMPLE_RATE_HZ, MIX_CHANNELS).expect("source"); + let mut runtime = AudioMixRuntimeBuilder::new() + .with_clock(Arc::clone(&clock) as Arc) + .add_source_with_freshness(source_id, consumer, Arc::clone(&last_push_ns)) + .build(NullMixOutputSink) + .expect("build"); + assert_eq!(runtime.mark_pushed_total(), 0); + assert_eq!(last_push_ns.load(Ordering::Acquire), u64::MAX); + let frame: Vec = (0..DIRECT_CAPTURE_APM_FRAME_SAMPLES) + .map(|n| (n as f32) * 0.0001) + .collect(); + let payload = make_f32_payload(&frame); + process_audio_chunk(&mut data, &payload); + let observed = last_push_ns.load(Ordering::Acquire); + assert_eq!(observed, 9_000_000); + let _ = runtime.run_one_tick_blocking(observed).expect("frame"); + assert!( + runtime.mark_pushed_total() >= 1, + "AudioMixRuntime.tick() did not invoke StaleSourceTracker::mark_pushed", + ); + let not_stale = !runtime.is_source_stale(0, observed + 1_000_000, 5_000_000_000); + assert!( + not_stale, + "source must not be stale immediately after a fresh push" + ); + } +} diff --git a/fluxer_desktop/native/linux-audio-capture/src/routing.rs b/fluxer_desktop/native/linux-audio-capture/src/routing.rs new file mode 100644 index 0000000..1b83188 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/src/routing.rs @@ -0,0 +1,662 @@ +#![allow(dead_code)] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::collections::{HashMap, HashSet}; + +pub type PropMap = HashMap; +pub type PropPattern = PropMap; + +pub const MEDIA_CLASS_PLAYBACK_STREAM: &str = "Stream/Output/Audio"; +const TARGET_OBJECTS_PATTERN_KEY: &str = "fluxer.target.objects"; +const DISPLAY_PATTERN_PREFIX: &str = "fluxer.display."; + +#[derive(Debug, Default, Clone)] +pub struct SelfIdentity { + pub pids: HashSet, + pub binaries: HashSet, + pub display_names: HashSet, + pub display_prefixes: Vec, +} + +impl SelfIdentity { + pub fn add_pid(&mut self, pid: impl Into) { + let value = pid.into(); + if value.is_empty() { + return; + } + self.pids.insert(value); + } + + pub fn add_binary(&mut self, name: impl Into) { + let value = name.into(); + if value.is_empty() { + return; + } + self.binaries.insert(value); + } + + pub fn add_display_name(&mut self, name: impl Into) { + let value = name.into(); + if value.is_empty() { + return; + } + self.display_names.insert(value); + } + + pub fn add_display_prefix(&mut self, prefix: impl Into) { + let value = prefix.into(); + if value.is_empty() { + return; + } + self.display_prefixes.push(value); + } + + pub fn matches(&self, properties: &PropMap) -> bool { + if let Some(raw) = properties.get("application.process.id") + && self.pids.contains(raw) + { + return true; + } + if let Some(raw) = properties.get("pipewire.sec.pid") + && self.pids.contains(raw) + { + return true; + } + if let Some(raw) = properties.get("application.process.binary") + && contains_case_insensitive(&self.binaries, raw) + { + return true; + } + for key in [ + "application.name", + "node.name", + "node.nick", + "node.description", + ] { + if let Some(raw) = properties.get(key) + && self.matches_display_identity(raw) + { + return true; + } + } + false + } + + fn matches_display_identity(&self, raw: &str) -> bool { + contains_case_insensitive(&self.binaries, raw) + || contains_case_insensitive(&self.display_names, raw) + || self + .display_prefixes + .iter() + .any(|prefix| starts_with_case_insensitive(raw, prefix)) + } +} + +fn contains_case_insensitive(values: &HashSet, needle: &str) -> bool { + values + .iter() + .any(|candidate| candidate.eq_ignore_ascii_case(needle)) +} + +fn starts_with_case_insensitive(value: &str, prefix: &str) -> bool { + value + .get(..prefix.len()) + .is_some_and(|head| head.eq_ignore_ascii_case(prefix)) +} + +#[derive(Debug, Default, Clone)] +pub struct RoutingRule { + pub include_when: Vec, + pub never_when: Vec, + pub pin_target_for: Vec, + pub skip_hardware_devices: bool, + pub only_audio_sinks: bool, + pub only_default_audio_sink: bool, +} + +pub fn matches_pattern(candidate: &PropMap, expected: &PropPattern) -> bool { + for (key, value) in expected { + if key.starts_with(DISPLAY_PATTERN_PREFIX) { + continue; + } + if key == TARGET_OBJECTS_PATTERN_KEY { + if !matches_target_object(candidate, value) { + return false; + } + continue; + } + match candidate.get(key) { + Some(actual) if actual == value => {} + _ => return false, + } + } + true +} + +fn matches_target_object(candidate: &PropMap, expected_values: &str) -> bool { + let Some(actual) = candidate + .get("target.object") + .or_else(|| candidate.get("node.target")) + else { + return false; + }; + expected_values + .split('\n') + .filter(|value| !value.is_empty()) + .any(|expected| actual == expected) +} + +pub fn matches_any(candidate: &PropMap, patterns: &[PropPattern]) -> bool { + patterns.iter().any(|p| matches_pattern(candidate, p)) +} + +pub fn should_route_node( + id: u32, + properties: &PropMap, + rule: &RoutingRule, + default_sink_name: &str, + default_sink_target_id: &str, + sink_global_id: u32, + self_identity: &SelfIdentity, +) -> bool { + if id == sink_global_id { + return false; + } + + if self_identity.matches(properties) { + return false; + } + if matches_any(properties, &rule.never_when) { + return false; + } + if rule.skip_hardware_devices && properties.contains_key("device.id") { + return false; + } + + let Some(class) = properties.get("media.class") else { + return false; + }; + if class != MEDIA_CLASS_PLAYBACK_STREAM { + return false; + } + + if !rule.include_when.is_empty() { + return matches_any(properties, &rule.include_when); + } + + if rule.only_audio_sinks { + return !rule.only_default_audio_sink + || targets_default_sink(properties, default_sink_name, default_sink_target_id); + } + + false +} + +pub fn targets_default_sink( + properties: &PropMap, + default_sink_name: &str, + default_sink_target_id: &str, +) -> bool { + if default_sink_name.is_empty() && default_sink_target_id.is_empty() { + return true; + } + let Some(target) = properties + .get("target.object") + .or_else(|| properties.get("node.target")) + else { + return true; + }; + (!default_sink_name.is_empty() && target == default_sink_name) + || (!default_sink_target_id.is_empty() && target == default_sink_target_id) +} + +pub fn parse_default_sink_name(blob: &str) -> String { + let trimmed = blob.trim(); + if !trimmed.starts_with('{') { + return String::new(); + } + let bytes = trimmed.as_bytes(); + let mut i = 1usize; + while i < bytes.len() { + while i < bytes.len() && bytes[i].is_ascii_whitespace() { + i += 1; + } + if i >= bytes.len() || bytes[i] != b'"' { + return String::new(); + } + + i += 1; + let key_start = i; + while i < bytes.len() && bytes[i] != b'"' { + if bytes[i] == b'\\' { + i += 2; + } else { + i += 1; + } + } + if i >= bytes.len() { + return String::new(); + } + let key = &trimmed[key_start..i]; + i += 1; + while i < bytes.len() && bytes[i].is_ascii_whitespace() { + i += 1; + } + if i >= bytes.len() || bytes[i] != b':' { + return String::new(); + } + i += 1; + while i < bytes.len() && bytes[i].is_ascii_whitespace() { + i += 1; + } + + if i >= bytes.len() { + return String::new(); + } + if bytes[i] != b'"' { + let mut depth = 0usize; + while i < bytes.len() { + match bytes[i] { + b'{' | b'[' => depth += 1, + b'}' | b']' => { + if depth == 0 { + return String::new(); + } + depth -= 1; + } + b',' if depth == 0 => break, + _ => {} + } + i += 1; + } + if i < bytes.len() && bytes[i] == b',' { + i += 1; + continue; + } + return String::new(); + } + i += 1; + let val_start = i; + let mut buf = String::new(); + while i < bytes.len() && bytes[i] != b'"' { + if bytes[i] == b'\\' && i + 1 < bytes.len() { + let escaped = bytes[i + 1]; + buf.push(escaped as char); + i += 2; + } else { + buf.push(bytes[i] as char); + i += 1; + } + } + if i >= bytes.len() { + return String::new(); + } + if key == "name" { + if buf.len() == i - val_start { + return trimmed[val_start..i].to_string(); + } + return buf; + } + i += 1; + while i < bytes.len() && bytes[i].is_ascii_whitespace() { + i += 1; + } + if i < bytes.len() && bytes[i] == b',' { + i += 1; + } + } + String::new() +} + +#[cfg(test)] +mod tests { + use super::*; + + fn make_map(entries: &[(&str, &str)]) -> PropMap { + entries + .iter() + .map(|(k, v)| ((*k).to_string(), (*v).to_string())) + .collect() + } + + fn system_rule() -> RoutingRule { + RoutingRule { + skip_hardware_devices: true, + only_audio_sinks: true, + only_default_audio_sink: true, + ..Default::default() + } + } + + #[test] + fn empty_pattern_matches_any_candidate() { + let candidate = make_map(&[("application.name", "Example")]); + let empty: PropPattern = PropPattern::new(); + assert!(matches_pattern(&candidate, &empty)); + } + + #[test] + fn missing_keys_and_mismatched_values_do_not_match() { + let candidate = make_map(&[("application.name", "Example")]); + let missing = make_map(&[("application.process.id", "1234")]); + let mismatched = make_map(&[("application.name", "Other")]); + assert!(!matches_pattern(&candidate, &missing)); + assert!(!matches_pattern(&candidate, &mismatched)); + } + + #[test] + fn synthetic_target_object_pattern_matches_name_or_serial() { + let candidate = make_map(&[("target.object", "42")]); + let deprecated = make_map(&[("node.target", "alsa_output.foo")]); + let pattern = make_map(&[(TARGET_OBJECTS_PATTERN_KEY, "alsa_output.foo\n42")]); + let mismatch = make_map(&[(TARGET_OBJECTS_PATTERN_KEY, "alsa_output.foo\n99")]); + assert!(matches_pattern(&candidate, &pattern)); + assert!(matches_pattern(&deprecated, &pattern)); + assert!(!matches_pattern(&candidate, &mismatch)); + } + + #[test] + fn synthetic_display_pattern_keys_do_not_affect_routing() { + let candidate = make_map(&[("application.name", "Example")]); + let pattern = make_map(&[ + ("application.name", "Example"), + ("fluxer.display.name", "Living room speakers"), + ]); + assert!(matches_pattern(&candidate, &pattern)); + } + + #[test] + fn matches_any_requires_at_least_one_matching_pattern() { + let candidate = make_map(&[("application.name", "Example")]); + let patterns = vec![ + make_map(&[("application.name", "Other")]), + make_map(&[("application.name", "Example")]), + ]; + assert!(matches_any(&candidate, &patterns)); + assert!(!matches_any(&candidate, &[])); + } + + #[test] + fn system_mode_routes_only_default_playback_streams() { + let identity = SelfIdentity::default(); + let analog = "alsa_output.pci-0000_00_1f.3.analog-stereo"; + let hdmi = "alsa_output.pci-0000_01_00.1.hdmi-stereo"; + let stream = make_map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("target.object", analog), + ]); + let other = make_map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("target.object", hdmi), + ]); + let rule = system_rule(); + assert!(should_route_node( + 100, &stream, &rule, analog, "", 1, &identity, + )); + assert!(!should_route_node( + 101, &other, &rule, analog, "", 1, &identity, + )); + } + + #[test] + fn structural_self_identity_wins_over_include_rules() { + let mut identity = SelfIdentity::default(); + identity.add_pid("4242"); + identity.add_binary("fluxer"); + identity.add_display_name("Fluxer Canary"); + identity.add_display_prefix("Fluxer "); + + let rule = RoutingRule { + include_when: vec![make_map(&[("application.process.id", "4242")])], + ..Default::default() + }; + let by_pid = make_map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("application.process.id", "4242"), + ]); + let by_binary = make_map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("application.process.binary", "fluxer"), + ]); + let by_description = make_map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("node.description", "Fluxer Direct Capture (pid 4242)"), + ]); + + assert!(!should_route_node( + 200, &by_pid, &rule, "", "", 0, &identity + )); + assert!(!should_route_node( + 201, &by_binary, &rule, "", "", 0, &identity, + )); + assert!(!should_route_node( + 202, + &by_description, + &rule, + "", + "", + 0, + &identity, + )); + } + + #[test] + fn routing_refuses_non_playback_media_classes_even_when_included() { + let identity = SelfIdentity::default(); + let rule = RoutingRule { + include_when: vec![make_map(&[("application.name", "Recorder")])], + ..Default::default() + }; + let input_stream = make_map(&[ + ("media.class", "Stream/Input/Audio"), + ("application.name", "Recorder"), + ]); + let device = make_map(&[ + ("media.class", "Audio/Source"), + ("application.name", "Recorder"), + ]); + assert!(!should_route_node( + 300, + &input_stream, + &rule, + "", + "", + 0, + &identity, + )); + assert!(!should_route_node( + 301, &device, &rule, "", "", 0, &identity + )); + } + + #[test] + fn system_mode_accepts_untargeted_and_node_target_streams() { + let identity = SelfIdentity::default(); + let analog = "alsa_output.pci-0000_00_1f.3.analog-stereo"; + let untargeted = make_map(&[("media.class", MEDIA_CLASS_PLAYBACK_STREAM)]); + let deprecated = make_map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("node.target", analog), + ]); + let rule = system_rule(); + assert!(should_route_node( + 100, + &untargeted, + &rule, + analog, + "", + 1, + &identity, + )); + assert!(should_route_node( + 101, + &deprecated, + &rule, + analog, + "", + 1, + &identity, + )); + } + + #[test] + fn system_mode_accepts_default_sink_object_id_targets() { + let identity = SelfIdentity::default(); + let analog = "alsa_output.pci-0000_00_1f.3.analog-stereo"; + let by_name = make_map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("target.object", analog), + ]); + let by_id = make_map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("target.object", "42"), + ]); + let other = make_map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("target.object", "99"), + ]); + let rule = system_rule(); + assert!(should_route_node( + 100, &by_name, &rule, analog, "42", 1, &identity, + )); + assert!(should_route_node( + 101, &by_id, &rule, analog, "42", 1, &identity, + )); + assert!(!should_route_node( + 102, &other, &rule, analog, "42", 1, &identity, + )); + } + + #[test] + fn include_mode_honors_hardware_filtering_and_never_rules() { + let identity = SelfIdentity::default(); + let rule = RoutingRule { + include_when: vec![make_map(&[("application.name", "Firefox")])], + never_when: vec![make_map(&[("application.process.id", "999")])], + skip_hardware_devices: true, + ..Default::default() + }; + + let app = make_map(&[ + ("application.name", "Firefox"), + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ]); + let hardware = make_map(&[("application.name", "Firefox"), ("device.id", "5")]); + let blocked = make_map(&[ + ("application.name", "Firefox"), + ("application.process.id", "999"), + ]); + assert!(should_route_node(10, &app, &rule, "", "", 1, &identity)); + assert!(!should_route_node( + 11, &hardware, &rule, "", "", 1, &identity + )); + assert!(!should_route_node( + 12, &blocked, &rule, "", "", 1, &identity + )); + } + + #[test] + fn include_mode_rejects_non_playback_nodes_that_match_the_include_filter() { + let identity = SelfIdentity::default(); + let rule = RoutingRule { + include_when: vec![make_map(&[("application.name", "Chromium")])], + ..Default::default() + }; + let mic = make_map(&[ + ("application.name", "Chromium"), + ("media.class", "Audio/Source"), + ]); + let sink = make_map(&[ + ("application.name", "Chromium"), + ("media.class", "Audio/Sink"), + ]); + let input = make_map(&[ + ("application.name", "Chromium"), + ("media.class", "Stream/Input/Audio"), + ]); + let virtual_source = make_map(&[ + ("application.name", "Chromium"), + ("media.class", "Audio/Source/Virtual"), + ]); + let playback = make_map(&[ + ("application.name", "Chromium"), + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ]); + let unclassified = make_map(&[("application.name", "Chromium")]); + assert!(!should_route_node(20, &mic, &rule, "", "", 1, &identity)); + assert!(!should_route_node(21, &sink, &rule, "", "", 1, &identity)); + assert!(!should_route_node(22, &input, &rule, "", "", 1, &identity)); + assert!(!should_route_node( + 23, + &virtual_source, + &rule, + "", + "", + 1, + &identity, + )); + assert!(!should_route_node( + 24, + &unclassified, + &rule, + "", + "", + 1, + &identity, + )); + assert!(should_route_node( + 25, &playback, &rule, "", "", 1, &identity + )); + } + + #[test] + fn empty_rules_route_nothing_and_sink_id_is_excluded() { + let identity = SelfIdentity::default(); + let app = make_map(&[ + ("application.name", "Foo"), + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ]); + let empty = RoutingRule::default(); + assert!(!should_route_node(1, &app, &empty, "", "", 0, &identity)); + let rule = system_rule(); + assert!(!should_route_node(7, &app, &rule, "", "", 7, &identity)); + } + + #[test] + fn parse_default_sink_name_is_strict_and_tolerant() { + assert_eq!( + "alsa_output.foo", + parse_default_sink_name(r#"{"name":"alsa_output.foo","other":"bar"}"#), + ); + assert_eq!("", parse_default_sink_name("not-json")); + assert_eq!("", parse_default_sink_name(r#"{"name":42}"#)); + } + + #[test] + fn self_identity_matches_across_documented_pipewire_keys() { + let mut identity = SelfIdentity::default(); + identity.add_pid("1234"); + identity.add_binary("fluxer"); + identity.add_display_name("Fluxer Canary"); + identity.add_display_prefix("Fluxer "); + + let by_pid = make_map(&[("application.process.id", "1234")]); + let by_sec_pid = make_map(&[("pipewire.sec.pid", "1234")]); + let by_binary = make_map(&[("application.process.binary", "fluxer")]); + let by_app_name = make_map(&[("application.name", "fluxer")]); + let by_node_name = make_map(&[("node.name", "fluxer")]); + let by_node_nick = make_map(&[("node.nick", "Fluxer Canary")]); + let by_node_description = make_map(&[("node.description", "Fluxer app audio capture")]); + let stranger = make_map(&[("application.process.id", "9999")]); + + assert!(identity.matches(&by_pid)); + assert!(identity.matches(&by_sec_pid)); + assert!(identity.matches(&by_binary)); + assert!(identity.matches(&by_app_name)); + assert!(identity.matches(&by_node_name)); + assert!(identity.matches(&by_node_nick)); + assert!(identity.matches(&by_node_description)); + assert!(!identity.matches(&stranger)); + } +} diff --git a/fluxer_desktop/native/linux-audio-capture/src/self_identity.rs b/fluxer_desktop/native/linux-audio-capture/src/self_identity.rs new file mode 100644 index 0000000..ac39259 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/src/self_identity.rs @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::collections::{HashMap, HashSet}; +use std::fs; +use std::path::Path; + +use crate::routing::SelfIdentity; + +const PRODUCT_DISPLAY_NAMES: &[&str] = &["Fluxer", "Fluxer Canary"]; +const PRODUCT_DISPLAY_PREFIXES: &[&str] = &[ + "Fluxer ", "fluxer ", "Fluxer-", "fluxer-", "Fluxer_", "fluxer_", "Fluxer.", "fluxer.", +]; + +pub fn populate_self_identity(out: &mut SelfIdentity) { + let own_pid = std::process::id(); + out.add_pid(own_pid.to_string()); + for name in PRODUCT_DISPLAY_NAMES { + out.add_display_name((*name).to_string()); + } + for prefix in PRODUCT_DISPLAY_PREFIXES { + out.add_display_prefix((*prefix).to_string()); + } + + if let Ok(comm) = fs::read_to_string("/proc/self/comm") { + let trimmed = comm.trim(); + if !trimmed.is_empty() { + out.add_binary(trimmed.to_string()); + } + } + + if let Ok(exe) = fs::read_link("/proc/self/exe") + && let Some(name) = exe.file_name().and_then(|s| s.to_str()) + { + out.add_binary(name.to_string()); + if let Some(stripped) = strip_exe_suffix(name) { + out.add_binary(stripped.to_string()); + } + } + + out.add_binary("Electron".to_string()); + out.add_binary("electron".to_string()); + + let _ = add_descendant_pids(out, own_pid); +} + +fn strip_exe_suffix(name: &str) -> Option<&str> { + for suffix in [".AppImage", ".bin"] { + if name.len() >= suffix.len() + && name[name.len() - suffix.len()..].eq_ignore_ascii_case(suffix) + { + return Some(&name[..name.len() - suffix.len()]); + } + } + None +} + +pub fn parse_ppid_from_stat(blob: &str) -> Option { + let close = blob.rfind(')')?; + let tail = &blob[close + 1..]; + let mut fields = tail.split_ascii_whitespace(); + let _state = fields.next()?; + let ppid = fields.next()?; + ppid.parse::().ok() +} + +fn add_descendant_pids(out: &mut SelfIdentity, own_pid: u32) -> std::io::Result<()> { + let mut entries: Vec<(u32, u32)> = Vec::new(); + for entry in fs::read_dir(Path::new("/proc"))? { + let Ok(entry) = entry else { continue }; + let Some(name) = entry.file_name().to_str().map(|s| s.to_string()) else { + continue; + }; + let Ok(pid) = name.parse::() else { + continue; + }; + let stat_path = format!("/proc/{pid}/stat"); + let Ok(blob) = fs::read_to_string(&stat_path) else { + continue; + }; + let Some(ppid) = parse_ppid_from_stat(&blob) else { + continue; + }; + entries.push((pid, ppid)); + } + + let mut by_parent: HashMap> = HashMap::new(); + for &(pid, ppid) in &entries { + by_parent.entry(ppid).or_default().push(pid); + } + + let mut ours: HashSet = HashSet::new(); + ours.insert(own_pid); + + let mut frontier = vec![own_pid]; + while let Some(parent) = frontier.pop() { + if let Some(children) = by_parent.get(&parent) { + for &child in children { + if ours.insert(child) { + frontier.push(child); + } + } + } + } + + for pid in ours { + out.add_pid(pid.to_string()); + } + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_ppid_handles_comm_with_spaces_and_parens() { + let stat = "1234 (weird (comm) name) S 4321 1234 1234 0 -1 4194304 0 0 0 0"; + assert_eq!(Some(4321), parse_ppid_from_stat(stat)); + } + + #[test] + fn parse_ppid_handles_plain_stat() { + let stat = "42 (cat) R 7 42 7 34816 42 4194304 91 0 0 0"; + assert_eq!(Some(7), parse_ppid_from_stat(stat)); + } + + #[test] + fn parse_ppid_returns_none_on_malformed_input() { + assert_eq!(None, parse_ppid_from_stat("")); + assert_eq!(None, parse_ppid_from_stat("no closing paren")); + assert_eq!(None, parse_ppid_from_stat("1 (cat)")); + } + + #[test] + fn strip_exe_suffix_handles_known_extensions() { + assert_eq!(Some("fluxer"), strip_exe_suffix("fluxer.AppImage")); + assert_eq!(Some("fluxer"), strip_exe_suffix("fluxer.bin")); + assert_eq!(None, strip_exe_suffix("fluxer")); + assert_eq!(Some("fluxer"), strip_exe_suffix("fluxer.APPIMAGE")); + } + + #[test] + fn populate_self_identity_records_own_pid() { + let mut id = SelfIdentity::default(); + populate_self_identity(&mut id); + assert!(id.pids.contains(&std::process::id().to_string())); + + assert!(id.binaries.contains("Electron")); + assert!(id.binaries.contains("electron")); + } +} diff --git a/fluxer_desktop/native/linux-audio-capture/vm_tests/direct_capture_validation.mjs b/fluxer_desktop/native/linux-audio-capture/vm_tests/direct_capture_validation.mjs new file mode 100644 index 0000000..092cb21 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/vm_tests/direct_capture_validation.mjs @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import {execFileSync} from 'node:child_process'; +import {copyFileSync} from 'node:fs'; +import {createRequire} from 'node:module'; +import {setTimeout as sleep} from 'node:timers/promises'; + +const require = createRequire(import.meta.url); +const work = process.env.FLX_WORK; +const addonNode = `${work}/flx_direct_addon.node`; +copyFileSync(process.env.FLX_ADDON_SO, addonNode); +const addon = require(addonNode); + +const results = []; +function record(name, ok, detail) { + results.push({name, ok}); + console.log(`${ok ? 'PASS' : 'FAIL'} ${name}${detail ? ` -- ${detail}` : ''}`); +} +function pwDump() { + return JSON.parse(execFileSync('pw-dump', {encoding: 'utf8', maxBuffer: 64e6})); +} +function directSinkNode(dump) { + return dump.find( + (o) => o.type === 'PipeWire:Interface:Node' && /^fluxer-direct-capture-/.test(o.info?.props?.['node.name'] || ''), + ); +} +async function waitFor(p, ms, step = 150) { + const end = Date.now() + ms; + let last; + while (Date.now() < end) { + last = p(); + if (last) return last; + await sleep(step); + } + return last; +} + +function rms(samples) { + if (samples.length === 0) return 0; + let sum = 0; + for (const s of samples) sum += s * s; + return Math.sqrt(sum / samples.length); +} + +async function main() { + const dc = new addon.DirectAudioCapture(); + let lifecycle = null; + dc.setLifecycleCallback((...args) => { + const flat = args.length === 1 && Array.isArray(args[0]) ? args[0] : args; + lifecycle = {kind: flat[0], msg: flat[1]}; + }); + + const started = dc.start({include: [{'application.name': 'Music Player Demo'}]}); + record('DirectAudioCapture.start(include rule) accepted', started === true); + + const sinkUp = await waitFor(() => { + const d = pwDump(); + return directSinkNode(d) ? d : null; + }, 8000); + record( + 'hidden private sink fluxer-direct-capture-* created', + !!sinkUp, + sinkUp ? directSinkNode(sinkUp).info.props['node.name'] : 'timeout', + ); + + if (sinkUp) { + const sinkProps = directSinkNode(sinkUp).info.props; + record( + 'private sink node.hidden=true (not user-visible)', + String(sinkProps['node.hidden']) === 'true', + `node.hidden=${sinkProps['node.hidden']}`, + ); + record('private sink media.class=Audio/Sink', sinkProps['media.class'] === 'Audio/Sink', sinkProps['media.class']); + } + + let maxRms = 0; + let frames = 0; + let totalSamples = 0; + const captureDeadline = Date.now() + 6000; + while (Date.now() < captureDeadline) { + const f = dc.read(); + if (f) { + const samples = new Float32Array(f.samples); + frames += 1; + totalSamples += samples.length; + maxRms = Math.max(maxRms, rms(samples)); + if (maxRms > 0.01 && frames > 5) break; + } + await sleep(20); + } + record('DirectAudioCapture yields real frames', frames > 0, `${frames} frames, ${totalSamples} samples`); + record('captured audio is non-silent (real 440Hz tone tapped)', maxRms > 0.01, `peak rms=${maxRms.toFixed(4)}`); + + const dump = pwDump(); + const sink = directSinkNode(dump); + const sinkId = Number(sink?.id); + const linkSrcNodes = new Set( + dump + .filter((o) => o.type === 'PipeWire:Interface:Link' && Number(o.info?.props?.['link.input.node']) === sinkId) + .map((o) => Number(o.info?.props?.['link.output.node'])), + ); + const fluxerStreamIds = dump + .filter((o) => o.type === 'PipeWire:Interface:Node' && o.info?.props?.['application.name'] === 'Fluxer') + .map((o) => Number(o.id)); + record( + 'Fluxer-named app excluded from per-process capture', + fluxerStreamIds.every((id) => !linkSrcNodes.has(id)), + `fluxer=${fluxerStreamIds} linkedSrc=${[...linkSrcNodes]}`, + ); + + const musicIds = dump + .filter((o) => o.type === 'PipeWire:Interface:Node' && o.info?.props?.['application.name'] === 'Music Player Demo') + .map((o) => Number(o.id)); + record( + 'targeted app IS linked to the private sink', + musicIds.some((id) => linkSrcNodes.has(id)), + `music=${musicIds} linkedSrc=${[...linkSrcNodes]}`, + ); + + dc.stop(); + await sleep(600); + record('stop() emits closed-clean lifecycle', lifecycle?.kind === 'closed-clean', JSON.stringify(lifecycle)); + const afterStop = pwDump(); + const sinkAfter = directSinkNode(afterStop); + const residualLinks = sinkAfter + ? afterStop.filter( + (o) => + o.type === 'PipeWire:Interface:Link' && Number(o.info?.props?.['link.input.node']) === Number(sinkAfter.id), + ).length + : 0; + record('stop() removes capture links', residualLinks === 0, `residual=${residualLinks}`); + + const failed = results.filter((r) => !r.ok).length; + console.log(`\n=== direct: ${results.length - failed}/${results.length} checks passed ===`); + process.exit(failed === 0 ? 0 : 1); +} + +main().catch((e) => { + console.error('HARNESS ERROR:', e?.stack || e); + process.exit(2); +}); diff --git a/fluxer_desktop/native/linux-audio-capture/vm_tests/pw_graph_validation.mjs b/fluxer_desktop/native/linux-audio-capture/vm_tests/pw_graph_validation.mjs new file mode 100644 index 0000000..0619f55 --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/vm_tests/pw_graph_validation.mjs @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import {execFileSync, spawn} from 'node:child_process'; +import {copyFileSync} from 'node:fs'; +import {createRequire} from 'node:module'; +import {setTimeout as sleep} from 'node:timers/promises'; + +const require = createRequire(import.meta.url); + +const addonSo = req('FLX_ADDON_SO'); +const work = req('FLX_WORK'); +const tone = req('FLX_TONE'); +const addonNode = `${work}/flx_audio_addon.node`; +copyFileSync(addonSo, addonNode); +const addon = require(addonNode); + +const SINK_NAME = 'fluxer-screen-share'; +const SINK_DESC = 'Fluxer Screen Share Audio'; +const results = []; +const children = []; + +function req(name) { + const v = process.env[name]; + if (!v) { + console.error(`HARNESS ERROR: ${name} not set`); + process.exit(2); + } + return v; +} +function record(name, ok, detail) { + results.push({name, ok}); + console.log(`${ok ? 'PASS' : 'FAIL'} ${name}${detail ? ` -- ${detail}` : ''}`); +} +function pwDump() { + return JSON.parse(execFileSync('pw-dump', {encoding: 'utf8', maxBuffer: 64e6})); +} +function nodesByName(dump, name) { + return dump.filter((o) => o.type === 'PipeWire:Interface:Node' && o.info?.props?.['node.name'] === name); +} +function links(dump) { + return dump + .filter((o) => o.type === 'PipeWire:Interface:Link') + .map((o) => ({ + inNode: Number(o.info?.props?.['link.input.node']), + outNode: Number(o.info?.props?.['link.output.node']), + })); +} +function streamNodes(dump) { + return dump.filter( + (o) => o.type === 'PipeWire:Interface:Node' && o.info?.props?.['media.class'] === 'Stream/Output/Audio', + ); +} +async function waitFor(predicate, timeoutMs, stepMs = 150) { + const deadline = Date.now() + timeoutMs; + let last; + while (Date.now() < deadline) { + last = predicate(); + if (last) return last; + await sleep(stepMs); + } + return last; +} +function killAll() { + for (const c of children) { + try { + c.kill('SIGKILL'); + } catch {} + } +} + +async function main() { + record('backend is pipewire', addon.audioBackend() === 'pipewire', addon.audioBackend()); + + const descendant = spawn( + 'pw-play', + ['--target', 'test_speakers', '-P', '{ application.name = "Descendant Player" }', tone], + { + stdio: 'ignore', + env: process.env, + }, + ); + children.push(descendant); + + const speakerIdOf = (d) => nodesByName(d, 'test_speakers')[0]?.id; + const sourcesInto = (dump) => { + const sid = Number(speakerIdOf(dump)); + return new Set( + links(dump) + .filter((l) => l.inNode === sid) + .map((l) => l.outNode), + ).size; + }; + + const pre = await waitFor(() => { + const d = pwDump(); + return streamNodes(d).length >= 5 && sourcesInto(d) >= 5 ? d : null; + }, 12000); + record( + 'all 5 playback streams routed to speakers pre-capture', + !!pre, + pre ? `${streamNodes(pre).length} streams, ${sourcesInto(pre)} routed` : 'timeout', + ); + const preDump = pre || pwDump(); + + const speakers = nodesByName(preDump, 'test_speakers'); + record('default sink test_speakers exists', speakers.length === 1); + const preSpeakerSources = sourcesInto(preDump); + record( + 'every app is playing to the real speakers pre-capture', + preSpeakerSources >= 5, + `${preSpeakerSources} distinct app streams -> speakers`, + ); + + const bridge = new addon.AudioBridge(); + record('AudioBridge on pipewire', bridge.backend() === 'pipewire', bridge.backend()); + record( + 'apply(system rule) accepted', + bridge.apply({onlySpeakers: true, onlyDefaultSpeakers: true, ignoreDevices: true}) === true, + ); + + const after = await waitFor(() => { + const d = pwDump(); + if (nodesByName(d, SINK_NAME).length !== 1) return null; + const g = bridge.routingGraph(); + return g.ownedLinks.length >= 2 ? {d, g} : null; + }, 10000); + + if (!after) { + record('fluxer sink + capture links established', false, 'timeout'); + await finish(bridge); + return; + } + const {d: dump, g: graph} = after; + + const sink = nodesByName(dump, SINK_NAME); + record('exactly one fluxer-screen-share node', sink.length === 1, `count=${sink.length}`); + const sp = sink[0]?.info?.props ?? {}; + record( + 'sink node.description is "Fluxer Screen Share Audio"', + sp['node.description'] === SINK_DESC, + sp['node.description'], + ); + record('sink media.class is Audio/Source/Virtual', sp['media.class'] === 'Audio/Source/Virtual', sp['media.class']); + record('sink node.virtual=true', String(sp['node.virtual']) === 'true'); + const sinkId = Number(sink[0]?.id); + + record( + 'all owned links are passive', + graph.ownedLinks.every((l) => l.passive === true), + `${graph.ownedLinks.length} links`, + ); + record( + 'all owned links terminate at the fluxer sink', + graph.ownedLinks.every((l) => Number(l.inputNodeId) === sinkId), + ); + + const captured = new Set(graph.ownedLinks.map((l) => Number(l.outputNodeId))); + const idsByPredicate = (pred) => + streamNodes(dump) + .filter((o) => pred(o.info.props)) + .map((o) => Number(o.id)); + + const normalIds = idsByPredicate( + (p) => + ['pw-play', 'Music Player Demo'].includes(p['application.name']) && + p['application.name'] !== 'Fluxer' && + p['application.name'] !== 'Descendant Player' && + !(p['node.name'] || '').startsWith('Fluxer '), + ); + const fluxerAppIds = idsByPredicate((p) => p['application.name'] === 'Fluxer'); + const fluxerNodeIds = idsByPredicate((p) => (p['node.name'] || '').startsWith('Fluxer ')); + const descendantIds = idsByPredicate((p) => p['application.name'] === 'Descendant Player'); + + record( + 'normal external apps ARE captured', + normalIds.length >= 2 && normalIds.every((id) => captured.has(id)), + `normal=${normalIds} captured=${[...captured]}`, + ); + record( + 'Fluxer-named app (application.name) is EXCLUDED', + fluxerAppIds.length >= 1 && fluxerAppIds.every((id) => !captured.has(id)), + `fluxerApp=${fluxerAppIds}`, + ); + record( + 'Fluxer-named app (node.name prefix) is EXCLUDED', + fluxerNodeIds.length >= 1 && fluxerNodeIds.every((id) => !captured.has(id)), + `fluxerNode=${fluxerNodeIds}`, + ); + record( + 'descendant-PID player is EXCLUDED (self-process tree)', + descendantIds.length >= 1 && descendantIds.every((id) => !captured.has(id)), + `descendant=${descendantIds}`, + ); + + const afterSpeakerSources = sourcesInto(dump); + record( + 'apps STILL play to real speakers during capture (tap, not move)', + afterSpeakerSources >= preSpeakerSources, + `before=${preSpeakerSources} after=${afterSpeakerSources} distinct app streams -> speakers`, + ); + + const meta = dump.find((o) => o.type === 'PipeWire:Interface:Metadata' && o.props?.['metadata.name'] === 'default'); + const def = meta?.metadata?.find((m) => m.key === 'default.audio.sink')?.value?.name; + record('default audio sink unchanged (test_speakers)', def === 'test_speakers', `default=${def}`); + + await finish(bridge); +} + +async function finish(bridge) { + bridge.release(); + const cleared = await waitFor(() => (bridge.routingGraph().ownedLinks.length === 0 ? true : null), 5000); + record( + 'release()+settle removes all owned links', + !!cleared, + cleared ? '0 owned links' : `still ${bridge.routingGraph().ownedLinks.length}`, + ); + await sleep(500); + const d = pwDump(); + const sinkId = Number(nodesByName(d, SINK_NAME)[0]?.id); + const residual = Number.isNaN(sinkId) ? 0 : links(d).filter((l) => l.inNode === sinkId).length; + record('no residual links into fluxer sink after release', residual === 0, `residual=${residual}`); + killAll(); + const failed = results.filter((r) => !r.ok).length; + console.log(`\n=== ${results.length - failed}/${results.length} checks passed ===`); + process.exit(failed === 0 ? 0 : 1); +} + +process.on('exit', killAll); +main().catch((e) => { + killAll(); + console.error('HARNESS ERROR:', e?.stack || e); + process.exit(2); +}); diff --git a/fluxer_desktop/native/linux-audio-capture/vm_tests/run_validation.sh b/fluxer_desktop/native/linux-audio-capture/vm_tests/run_validation.sh new file mode 100755 index 0000000..e5ca85f --- /dev/null +++ b/fluxer_desktop/native/linux-audio-capture/vm_tests/run_validation.sh @@ -0,0 +1,78 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: AGPL-3.0-or-later + +set -uo pipefail + +WORK="${FLX_WORK:-/home/parallels/flx-vmtest}" +ADDON_SO="${FLX_ADDON_SO:-/home/parallels/flx-target/debug/libfluxer_linux_audio_capture.so}" +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +mkdir -p "$WORK" +export XDG_RUNTIME_DIR="$WORK/xdg" +mkdir -p "$XDG_RUNTIME_DIR"; chmod 700 "$XDG_RUNTIME_DIR" +export PIPEWIRE_RUNTIME_DIR="$XDG_RUNTIME_DIR" +export PULSE_RUNTIME_PATH="$XDG_RUNTIME_DIR/pulse" +unset DISPLAY WAYLAND_DISPLAY DBUS_SESSION_BUS_ADDRESS + +PIDS=() +cleanup() { + for p in "${PIDS[@]:-}"; do kill -9 "$p" 2>/dev/null; done + pkill -9 -u "$(id -un)" -f "pipewire" 2>/dev/null + pkill -9 -u "$(id -un)" -f "wireplumber" 2>/dev/null +} +trap cleanup EXIT + +echo "=== starting private pipewire server (runtime=$XDG_RUNTIME_DIR) ===" +pipewire >"$WORK/pipewire.log" 2>&1 & PIDS+=($!) +sleep 0.8 +pipewire-pulse >"$WORK/pipewire-pulse.log" 2>&1 & PIDS+=($!) +sleep 0.5 +wireplumber >"$WORK/wireplumber.log" 2>&1 & PIDS+=($!) + +for _ in $(seq 1 40); do pw-cli info 0 >/dev/null 2>&1 && break; sleep 0.25; done +if ! pw-cli info 0 >/dev/null 2>&1; then + echo "FATAL: private pipewire did not come up"; cat "$WORK/pipewire.log"; exit 2 +fi + +echo "=== creating virtual speakers (default sink) ===" +pactl load-module module-null-sink sink_name=test_speakers sink_properties='device.description=Test_Speakers' >/dev/null 2>&1 +pactl set-default-sink test_speakers 2>/dev/null +sleep 0.4 + +echo "=== generating a real 600s stereo tone ===" +TONE="$WORK/tone.wav" +[ -f "$TONE" ] || ffmpeg -hide_banner -loglevel error -f lavfi -i "sine=frequency=440:duration=600" -ac 2 -ar 48000 "$TONE" /dev/null 2>&1 & PIDS+=($!) +pw-play --target test_speakers -P '{ application.name = "Music Player Demo" }' "$TONE" >/dev/null 2>&1 & PIDS+=($!) +pw-play --target test_speakers -P '{ application.name = "Fluxer" }' "$TONE" >/dev/null 2>&1 & PIDS+=($!) +pw-play --target test_speakers -P '{ node.name = "Fluxer Helper Stream" }' "$TONE" >/dev/null 2>&1 & PIDS+=($!) +sleep 1.5 + +echo "=== pre-test graph (fluxer sink should be ABSENT) ===" +pw-dump | node -e 'const d=JSON.parse(require("fs").readFileSync(0));const f=d.filter(o=>o.type==="PipeWire:Interface:Node"&&/fluxer-screen-share/.test(o.info?.props?.["node.name"]||""));console.log("fluxer sink nodes pre-test:",f.length);' + +echo "=== running napi SYSTEM-capture validation harness ===" +export FLX_ADDON_SO="$ADDON_SO" FLX_WORK="$WORK" +node "$HERE/pw_graph_validation.mjs" +HARNESS_RC=$? + +echo "=== running napi DIRECT (per-process) capture validation harness ===" +node "$HERE/direct_capture_validation.mjs" +DIRECT_RC=$? +[ "$DIRECT_RC" = "0" ] || HARNESS_RC=$DIRECT_RC + +echo "=== post-exit cleanup check (Drop must remove the fluxer sink) ===" +sleep 0.8 +RESIDUAL=$(pw-dump | node -e 'const d=JSON.parse(require("fs").readFileSync(0));const f=d.filter(o=>(o.type==="PipeWire:Interface:Node"&&/fluxer/.test(o.info?.props?.["node.name"]||""))||(o.type==="PipeWire:Interface:Link"&&/fluxer/.test(JSON.stringify(o.info?.props||{}))));console.log(f.length);') +if [ "$RESIDUAL" = "0" ]; then + echo "PASS no fluxer nodes/links remain after addon process exit (clean teardown)" +else + echo "FAIL $RESIDUAL residual fluxer objects after addon process exit" + HARNESS_RC=1 +fi + +echo "=== DONE rc=$HARNESS_RC ===" +exit $HARNESS_RC diff --git a/fluxer_desktop/native/linux-evdev/Cargo.lock b/fluxer_desktop/native/linux-evdev/Cargo.lock new file mode 100644 index 0000000..5ddf9c6 --- /dev/null +++ b/fluxer_desktop/native/linux-evdev/Cargo.lock @@ -0,0 +1,552 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "bitvec" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "evdev" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25b686663ba7f08d92880ff6ba22170f1df4e83629341cba34cf82cd65ebea99" +dependencies = [ + "bitvec", + "cfg-if", + "libc", + "nix 0.29.0", +] + +[[package]] +name = "fluxer_linux_evdev" +version = "0.0.0" +dependencies = [ + "evdev", + "napi", + "napi-build", + "napi-derive", + "nix 0.31.3", + "polling", + "udev", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "hermit-abi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "io-lifetimes" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" +dependencies = [ + "hermit-abi 0.3.9", + "libc", + "windows-sys 0.48.0", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libudev-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c8469b4a23b962c1396b9b451dda50ef5b283e8dd309d69033475fa9b334324" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading", +] + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi 0.5.2", + "pin-project-lite", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "udev" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af4e37e9ea4401fc841ff54b9ddfc9be1079b1e89434c1a6a865dd68980f7e9f" +dependencies = [ + "io-lifetimes", + "libc", + "libudev-sys", + "pkg-config", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] diff --git a/fluxer_desktop/native/linux-evdev/Cargo.toml b/fluxer_desktop/native/linux-evdev/Cargo.toml new file mode 100644 index 0000000..8ea2a78 --- /dev/null +++ b/fluxer_desktop/native/linux-evdev/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "fluxer_linux_evdev" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8"]} +napi-derive = "3.5.6" + +[target.'cfg(target_os = "linux")'.dependencies] +evdev = "0.13.2" +nix = {version = "0.31.3", default-features = false, features = ["event"]} +udev = "0.9.3" +polling = "3.11.0" + +[build-dependencies] +napi-build = "2.3.2" diff --git a/fluxer_desktop/native/linux-evdev/build.rs b/fluxer_desktop/native/linux-evdev/build.rs new file mode 100644 index 0000000..d45de40 --- /dev/null +++ b/fluxer_desktop/native/linux-evdev/build.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + napi_build::setup(); +} diff --git a/fluxer_desktop/native/linux-evdev/index.d.ts b/fluxer_desktop/native/linux-evdev/index.d.ts new file mode 100644 index 0000000..151fb48 --- /dev/null +++ b/fluxer_desktop/native/linux-evdev/index.d.ts @@ -0,0 +1,34 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +export interface EvdevKeyEvent { + type: 'keydown' | 'keyup'; + keycode: number; + keyName: string; + ctrlKey: boolean; + altKey: boolean; + shiftKey: boolean; + metaKey: boolean; +} + +export interface EvdevMouseEvent { + type: 'mousedown' | 'mouseup'; + button: number; + ctrlKey: boolean; + altKey: boolean; + shiftKey: boolean; + metaKey: boolean; +} + +export type NativeEvdevEvent = EvdevKeyEvent | EvdevMouseEvent; + +export declare class EvdevHook { + constructor(onEvent: (event: NativeEvdevEvent) => void); + + start(): boolean; + + stop(): void; +} + +export declare function nameToEvdevKeycode(name: string): number; + +export declare const loadError: Error | null; diff --git a/fluxer_desktop/native/linux-evdev/index.js b/fluxer_desktop/native/linux-evdev/index.js new file mode 100644 index 0000000..49e4209 --- /dev/null +++ b/fluxer_desktop/native/linux-evdev/index.js @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync} = require('node:fs'); +const {join, sep} = require('node:path'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); +const MODULE_NAME = '@fluxer/linux-evdev'; +const SKIP_NATIVE_PROBE_ENV = 'FLUXER_LINUX_EVDEV_SKIP_NATIVE_PROBE'; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName() { + if (process.platform !== 'linux') { + throw new Error(`@fluxer/linux-evdev is only supported on Linux, got ${process.platform}`); + } + switch (process.arch) { + case 'x64': + return 'linux-evdev.linux-x64-gnu.node'; + case 'arm64': + return 'linux-evdev.linux-arm64-gnu.node'; + default: + throw new Error(`Unsupported Linux architecture: ${process.arch}`); + } +} + +let binding = null; +let loadError = null; + +if (process.platform === 'linux') { + try { + const nativeRoot = resolveNativeRoot(); + const nativePath = join(nativeRoot, nativeFileName()); + const loaded = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + binding = loaded.binding; + loadError = loaded.loadError; + if (loadError) throw loadError; + } catch (error) { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: 'native loader threw before binding load completed', + cause: error, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + throw loadError; + } +} + +module.exports = { + EvdevHook: binding ? binding.EvdevHook : null, + nameToEvdevKeycode: binding ? binding.nameToEvdevKeycode : null, + loadError, +}; diff --git a/fluxer_desktop/native/linux-evdev/loader-diagnostics.cjs b/fluxer_desktop/native/linux-evdev/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/linux-evdev/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/linux-evdev/package.json b/fluxer_desktop/native/linux-evdev/package.json new file mode 100644 index 0000000..0a65e55 --- /dev/null +++ b/fluxer_desktop/native/linux-evdev/package.json @@ -0,0 +1,27 @@ +{ + "name": "@fluxer/linux-evdev", + "version": "0.0.0", + "description": "", + "private": true, + "license": "AGPL-3.0-or-later", + "os": [ + "linux" + ], + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "linux-evdev.linux-x64-gnu.node", + "linux-evdev.linux-arm64-gnu.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "cargo test --manifest-path Cargo.toml" + } +} diff --git a/fluxer_desktop/native/linux-evdev/src/keymap.rs b/fluxer_desktop/native/linux-evdev/src/keymap.rs new file mode 100644 index 0000000..9040968 --- /dev/null +++ b/fluxer_desktop/native/linux-evdev/src/keymap.rs @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub const KEY_MAP: &[(u16, &str)] = &[ + (1, "Escape"), + (2, "1"), + (3, "2"), + (4, "3"), + (5, "4"), + (6, "5"), + (7, "6"), + (8, "7"), + (9, "8"), + (10, "9"), + (11, "0"), + (12, "Minus"), + (13, "Equal"), + (14, "Backspace"), + (15, "Tab"), + (16, "Q"), + (17, "W"), + (18, "E"), + (19, "R"), + (20, "T"), + (21, "Y"), + (22, "U"), + (23, "I"), + (24, "O"), + (25, "P"), + (26, "BracketLeft"), + (27, "BracketRight"), + (28, "Enter"), + (29, "ControlLeft"), + (30, "A"), + (31, "S"), + (32, "D"), + (33, "F"), + (34, "G"), + (35, "H"), + (36, "J"), + (37, "K"), + (38, "L"), + (39, "Semicolon"), + (40, "Quote"), + (41, "Backquote"), + (42, "ShiftLeft"), + (43, "Backslash"), + (44, "Z"), + (45, "X"), + (46, "C"), + (47, "V"), + (48, "B"), + (49, "N"), + (50, "M"), + (51, "Comma"), + (52, "Period"), + (53, "Slash"), + (54, "ShiftRight"), + (55, "NumpadMultiply"), + (56, "AltLeft"), + (57, "Space"), + (58, "CapsLock"), + (59, "F1"), + (60, "F2"), + (61, "F3"), + (62, "F4"), + (63, "F5"), + (64, "F6"), + (65, "F7"), + (66, "F8"), + (67, "F9"), + (68, "F10"), + (69, "NumLock"), + (70, "ScrollLock"), + (71, "Numpad7"), + (72, "Numpad8"), + (73, "Numpad9"), + (74, "NumpadSubtract"), + (75, "Numpad4"), + (76, "Numpad5"), + (77, "Numpad6"), + (78, "NumpadAdd"), + (79, "Numpad1"), + (80, "Numpad2"), + (81, "Numpad3"), + (82, "Numpad0"), + (83, "NumpadDecimal"), + (86, "IntlBackslash"), + (119, "Pause"), + (87, "F11"), + (88, "F12"), + (89, "IntlRo"), + (90, "Lang3"), + (92, "Convert"), + (93, "KanaMode"), + (94, "NonConvert"), + (96, "NumpadEnter"), + (97, "ControlRight"), + (98, "NumpadDivide"), + (99, "PrintScreen"), + (100, "AltRight"), + (102, "Home"), + (103, "ArrowUp"), + (104, "PageUp"), + (105, "ArrowLeft"), + (106, "ArrowRight"), + (107, "End"), + (108, "ArrowDown"), + (109, "PageDown"), + (110, "Insert"), + (111, "Delete"), + (113, "AudioVolumeMute"), + (114, "AudioVolumeDown"), + (115, "AudioVolumeUp"), + (116, "Power"), + (117, "NumpadEqual"), + (121, "NumpadComma"), + (122, "Lang1"), + (123, "Lang2"), + (124, "IntlYen"), + (125, "MetaLeft"), + (126, "MetaRight"), + (127, "ContextMenu"), + (142, "Sleep"), + (143, "WakeUp"), + (148, "LaunchApp1"), + (149, "LaunchApp2"), + (155, "LaunchMail"), + (158, "BrowserBack"), + (159, "BrowserForward"), + (163, "MediaTrackNext"), + (164, "MediaPlayPause"), + (165, "MediaTrackPrevious"), + (166, "MediaStop"), + (172, "BrowserHome"), + (173, "BrowserRefresh"), + (183, "F13"), + (184, "F14"), + (185, "F15"), + (186, "F16"), + (187, "F17"), + (188, "F18"), + (189, "F19"), + (190, "F20"), + (191, "F21"), + (192, "F22"), + (193, "F23"), + (194, "F24"), + (217, "BrowserSearch"), + (226, "LaunchMediaPlayer"), + (364, "BrowserFavorites"), +]; + +pub const LEFT_CTRL: u16 = 29; +pub const RIGHT_CTRL: u16 = 97; +pub const LEFT_SHIFT: u16 = 42; +pub const RIGHT_SHIFT: u16 = 54; +pub const LEFT_ALT: u16 = 56; +pub const RIGHT_ALT: u16 = 100; +pub const LEFT_META: u16 = 125; +pub const RIGHT_META: u16 = 126; + +pub const BTN_LEFT: u16 = 0x110; +pub const BTN_RIGHT: u16 = 0x111; +pub const BTN_MIDDLE: u16 = 0x112; +pub const BTN_SIDE: u16 = 0x113; +pub const BTN_EXTRA: u16 = 0x114; +pub const BTN_FORWARD: u16 = 0x115; +pub const BTN_BACK: u16 = 0x116; + +pub fn evdev_button_to_browser_button(code: u16) -> Option { + match code { + BTN_LEFT => Some(0), + BTN_MIDDLE => Some(1), + BTN_RIGHT => Some(2), + BTN_SIDE | BTN_BACK => Some(3), + BTN_EXTRA | BTN_FORWARD => Some(4), + _ => None, + } +} + +pub fn keycode_to_name(code: u16) -> Option<&'static str> { + KEY_MAP + .iter() + .find(|(entry_code, _)| *entry_code == code) + .map(|(_, name)| *name) +} + +pub fn name_to_keycode(name: &str) -> u16 { + KEY_MAP + .iter() + .find(|(_, entry_name)| *entry_name == name) + .map(|(code, _)| *code) + .unwrap_or(0) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn keycode_to_name_covers_canonical_letters_and_arrows() { + assert_eq!(keycode_to_name(30), Some("A")); + assert_eq!(keycode_to_name(44), Some("Z")); + assert_eq!(keycode_to_name(119), Some("Pause")); + assert_eq!(keycode_to_name(183), Some("F13")); + assert_eq!(keycode_to_name(96), Some("NumpadEnter")); + assert_eq!(keycode_to_name(113), Some("AudioVolumeMute")); + assert_eq!(keycode_to_name(103), Some("ArrowUp")); + assert_eq!(keycode_to_name(125), Some("MetaLeft")); + assert_eq!(keycode_to_name(0), None); + assert_eq!(keycode_to_name(0xffff), None); + } + + #[test] + fn name_to_keycode_round_trips_every_entry() { + for (code, name) in KEY_MAP { + assert_eq!(name_to_keycode(name), *code); + } + assert_eq!(name_to_keycode("NoSuchKey"), 0); + assert_eq!(name_to_keycode(""), 0); + } + + #[test] + fn evdev_button_matches_dom_convention() { + assert_eq!(evdev_button_to_browser_button(BTN_LEFT), Some(0)); + assert_eq!(evdev_button_to_browser_button(BTN_MIDDLE), Some(1)); + assert_eq!(evdev_button_to_browser_button(BTN_RIGHT), Some(2)); + assert_eq!(evdev_button_to_browser_button(BTN_SIDE), Some(3)); + assert_eq!(evdev_button_to_browser_button(BTN_BACK), Some(3)); + assert_eq!(evdev_button_to_browser_button(BTN_EXTRA), Some(4)); + assert_eq!(evdev_button_to_browser_button(BTN_FORWARD), Some(4)); + assert_eq!(evdev_button_to_browser_button(0x100), None); + assert_eq!(evdev_button_to_browser_button(0xffff), None); + } + + #[test] + fn modifier_constants_match_linux_input_event_codes() { + assert_eq!(LEFT_CTRL, 29); + assert_eq!(RIGHT_CTRL, 97); + assert_eq!(LEFT_SHIFT, 42); + assert_eq!(RIGHT_SHIFT, 54); + assert_eq!(LEFT_ALT, 56); + assert_eq!(RIGHT_ALT, 100); + assert_eq!(LEFT_META, 125); + assert_eq!(RIGHT_META, 126); + } +} diff --git a/fluxer_desktop/native/linux-evdev/src/lib.rs b/fluxer_desktop/native/linux-evdev/src/lib.rs new file mode 100644 index 0000000..e907d94 --- /dev/null +++ b/fluxer_desktop/native/linux-evdev/src/lib.rs @@ -0,0 +1,17 @@ +#![cfg_attr(not(target_os = "linux"), allow(dead_code))] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +mod keymap; + +#[cfg(target_os = "linux")] +mod linux; + +#[cfg(target_os = "linux")] +pub use linux::{EvdevHook, name_to_evdev_keycode}; + +#[cfg(not(target_os = "linux"))] +#[napi_derive::napi(js_name = "nameToEvdevKeycode")] +pub fn name_to_evdev_keycode(name: String) -> u32 { + u32::from(crate::keymap::name_to_keycode(&name)) +} diff --git a/fluxer_desktop/native/linux-evdev/src/linux.rs b/fluxer_desktop/native/linux-evdev/src/linux.rs new file mode 100644 index 0000000..9f6776f --- /dev/null +++ b/fluxer_desktop/native/linux-evdev/src/linux.rs @@ -0,0 +1,731 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::{ + collections::{HashMap, HashSet}, + os::fd::{AsFd, AsRawFd, BorrowedFd, RawFd}, + path::{Path, PathBuf}, + sync::{ + Arc, Mutex, + atomic::{AtomicBool, Ordering}, + }, + thread::{self, JoinHandle}, +}; + +use evdev::{Device, EventType, KeyCode}; +use napi::{ + Env, Status, + bindgen_prelude::{Function, Object, Result, ToNapiValue}, + sys, + threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode, UnknownReturnValue}, +}; +use napi_derive::napi; +use nix::sys::eventfd::{EfdFlags, EventFd}; +use polling::{Event as PollEvent, Events, Poller}; + +use crate::keymap; + +const EXIT_KEY: usize = 0; +const MONITOR_KEY: usize = 1; +const DEVICE_KEY_BASE: usize = 2; +const DEV_INPUT_PREFIX: &str = "/dev/input/event"; +const EVENT_QUEUE_LIMIT: usize = 1024; + +fn poll_key_for_device_fd(fd: RawFd) -> Option { + usize::try_from(fd).ok()?.checked_add(DEVICE_KEY_BASE) +} + +fn device_fd_from_poll_key(key: usize) -> Option { + let fd = key.checked_sub(DEVICE_KEY_BASE)?; + RawFd::try_from(fd).ok() +} + +#[derive(Debug)] +pub enum NativeEvent { + Key { + kind: KeyKind, + keycode: u16, + key_name: &'static str, + ctrl: bool, + alt: bool, + shift: bool, + meta: bool, + }, + Mouse { + kind: MouseKind, + button: u8, + ctrl: bool, + alt: bool, + shift: bool, + meta: bool, + }, +} + +#[derive(Debug, Clone, Copy)] +pub enum KeyKind { + Down, + Up, +} + +#[derive(Debug, Clone, Copy)] +pub enum MouseKind { + Down, + Up, +} + +impl ToNapiValue for NativeEvent { + unsafe fn to_napi_value(raw_env: sys::napi_env, event: Self) -> Result { + let env = Env::from_raw(raw_env); + let mut object = Object::new(&env)?; + match event { + Self::Key { + kind, + keycode, + key_name, + ctrl, + alt, + shift, + meta, + } => { + object.set( + "type", + match kind { + KeyKind::Down => "keydown", + KeyKind::Up => "keyup", + }, + )?; + object.set("keycode", u32::from(keycode))?; + object.set("keyName", key_name)?; + object.set("ctrlKey", ctrl)?; + object.set("altKey", alt)?; + object.set("shiftKey", shift)?; + object.set("metaKey", meta)?; + } + Self::Mouse { + kind, + button, + ctrl, + alt, + shift, + meta, + } => { + object.set( + "type", + match kind { + MouseKind::Down => "mousedown", + MouseKind::Up => "mouseup", + }, + )?; + object.set("button", u32::from(button))?; + object.set("ctrlKey", ctrl)?; + object.set("altKey", alt)?; + object.set("shiftKey", shift)?; + object.set("metaKey", meta)?; + } + } + unsafe { as ToNapiValue>::to_napi_value(raw_env, object) } + } +} + +type EventTsfn = Arc< + ThreadsafeFunction< + NativeEvent, + UnknownReturnValue, + NativeEvent, + Status, + false, + true, + EVENT_QUEUE_LIMIT, + >, +>; + +struct ExitFd { + fd: EventFd, +} + +impl ExitFd { + fn new() -> std::io::Result { + let fd = EventFd::from_value_and_flags(0, EfdFlags::EFD_CLOEXEC | EfdFlags::EFD_NONBLOCK) + .map_err(std::io::Error::from)?; + Ok(Self { fd }) + } + + fn signal(&self) { + let _ = self.fd.write(1); + } + + fn drain(&self) { + let _ = self.fd.read(); + } + + fn as_borrowed(&self) -> BorrowedFd<'_> { + self.fd.as_fd() + } +} + +fn resolve_seat() -> String { + std::env::var("XDG_SEAT") + .ok() + .filter(|value| !value.is_empty()) + .unwrap_or_else(|| "seat0".to_string()) +} + +fn read_seat_from_device(device: &udev::Device) -> Option { + if let Some(value) = device.property_value("ID_SEAT") { + return Some(value.to_string_lossy().into_owned()); + } + let mut parent = device.parent(); + while let Some(p) = parent { + if let Some(value) = p.property_value("ID_SEAT") { + return Some(value.to_string_lossy().into_owned()); + } + parent = p.parent(); + } + Some("seat0".to_string()) +} + +fn lookup_input_seat(sysname: &str) -> Option { + let mut enumerator = udev::Enumerator::new().ok()?; + enumerator.match_subsystem("input").ok()?; + enumerator.match_sysname(sysname).ok()?; + let device = enumerator.scan_devices().ok()?.next()?; + read_seat_from_device(&device) +} + +struct OpenedDevice { + device: Device, + path: PathBuf, +} + +struct Reader { + poller: Arc, + exit_fd: Arc, + udev_handle_available: bool, + seat: String, + devices: HashMap, + held_keys: HashSet, + callback: EventTsfn, + stop: Arc, +} + +impl Reader { + fn new( + callback: EventTsfn, + stop: Arc, + exit_fd: Arc, + ) -> std::io::Result { + let poller = Arc::new(Poller::new()?); + unsafe { + poller.add(&exit_fd.as_borrowed(), PollEvent::readable(EXIT_KEY))?; + } + let seat = resolve_seat(); + let udev_handle_available = udev::Enumerator::new().is_ok(); + + Ok(Self { + poller, + exit_fd, + udev_handle_available, + seat, + devices: HashMap::new(), + held_keys: HashSet::new(), + callback, + stop, + }) + } + + fn try_attach_monitor(&self) -> Option { + let socket = udev::MonitorBuilder::new() + .and_then(|b| b.match_subsystem("input")) + .and_then(|b| b.listen()) + .ok()?; + let monitor_fd = socket.as_raw_fd(); + let borrowed = unsafe { BorrowedFd::borrow_raw(monitor_fd) }; + if unsafe { self.poller.add(&borrowed, PollEvent::readable(MONITOR_KEY)) }.is_ok() { + Some(socket) + } else { + None + } + } + + fn device_is_on_our_seat(&self, sysname: &str) -> bool { + if !self.udev_handle_available || self.seat.is_empty() { + return true; + } + match lookup_input_seat(sysname) { + Some(found) => found == self.seat, + None => true, + } + } + + fn open_all_devices(&mut self) { + let entries = match std::fs::read_dir("/dev/input") { + Ok(entries) => entries, + Err(_) => return, + }; + for entry in entries.flatten() { + let name = entry.file_name(); + let name_str = match name.to_str() { + Some(s) => s, + None => continue, + }; + if !name_str.starts_with("event") { + continue; + } + if !self.device_is_on_our_seat(name_str) { + continue; + } + let path = entry.path(); + let _ = self.open_device(&path); + } + } + + fn open_device(&mut self, path: &Path) -> std::io::Result<()> { + if self + .devices + .values() + .any(|opened| opened.path.as_path() == path) + { + return Ok(()); + } + let device = Device::open(path)?; + if !device_has_routable_input(&device) { + return Ok(()); + } + let _ = device.set_nonblocking(true); + let fd = device.as_raw_fd(); + let poll_key = poll_key_for_device_fd(fd).ok_or_else(|| { + std::io::Error::new( + std::io::ErrorKind::InvalidInput, + "input device fd cannot be represented as a poll key", + ) + })?; + unsafe { + let borrowed = BorrowedFd::borrow_raw(fd); + self.poller.add(&borrowed, PollEvent::readable(poll_key))?; + } + self.devices.insert( + fd, + OpenedDevice { + device, + path: path.to_path_buf(), + }, + ); + Ok(()) + } + + fn close_device_by_path(&mut self, path: &Path) { + let fd = self + .devices + .iter() + .find(|(_, opened)| opened.path.as_path() == path) + .map(|(fd, _)| *fd); + if let Some(fd) = fd { + self.close_device_by_fd(fd); + } + } + + fn close_device_by_fd(&mut self, fd: RawFd) { + if let Some(opened) = self.devices.remove(&fd) { + let borrowed = unsafe { BorrowedFd::borrow_raw(fd) }; + let _ = self.poller.delete(borrowed); + drop(opened); + } + } + + fn run(&mut self) { + let monitor = self.try_attach_monitor(); + let mut events = Events::new(); + loop { + if self.stop.load(Ordering::Acquire) { + break; + } + events.clear(); + if self.poller.wait(&mut events, None).is_err() { + break; + } + let mut device_fds_to_drain: Vec = Vec::new(); + let mut drain_monitor = false; + let mut got_exit = false; + for event in events.iter() { + match event.key { + EXIT_KEY => got_exit = true, + MONITOR_KEY => drain_monitor = true, + fd_key => { + if let Some(fd) = device_fd_from_poll_key(fd_key) { + device_fds_to_drain.push(fd); + } + } + } + } + if got_exit { + self.exit_fd.drain(); + break; + } + + self.rearm(monitor.as_ref(), drain_monitor, &device_fds_to_drain); + + if drain_monitor && let Some(monitor) = monitor.as_ref() { + self.drain_monitor(monitor); + } + for fd in device_fds_to_drain { + self.drain_device(fd); + } + } + + if let Some(monitor) = monitor.as_ref() { + let borrowed = unsafe { BorrowedFd::borrow_raw(monitor.as_raw_fd()) }; + let _ = self.poller.delete(borrowed); + } + } + + fn rearm( + &self, + monitor: Option<&udev::MonitorSocket>, + drain_monitor: bool, + device_fds: &[RawFd], + ) { + let _ = self + .poller + .modify(self.exit_fd.as_borrowed(), PollEvent::readable(EXIT_KEY)); + if drain_monitor && let Some(monitor) = monitor { + let borrowed = unsafe { BorrowedFd::borrow_raw(monitor.as_raw_fd()) }; + let _ = self + .poller + .modify(borrowed, PollEvent::readable(MONITOR_KEY)); + } + for fd in device_fds { + if self.devices.contains_key(fd) { + let Some(poll_key) = poll_key_for_device_fd(*fd) else { + continue; + }; + let borrowed = unsafe { BorrowedFd::borrow_raw(*fd) }; + let _ = self.poller.modify(borrowed, PollEvent::readable(poll_key)); + } + } + } + + fn drain_monitor(&mut self, monitor: &udev::MonitorSocket) { + let mut pending: Vec<(String, PathBuf, Option)> = Vec::new(); + for event in monitor.iter() { + let action = match event.action() { + Some(a) => a.to_string_lossy().into_owned(), + None => continue, + }; + let devnode = match event.devnode() { + Some(p) => p.to_path_buf(), + None => continue, + }; + let subsystem = event.subsystem().map(|s| s.to_string_lossy().into_owned()); + if subsystem.as_deref() != Some("input") { + continue; + } + let devnode_str = devnode.to_string_lossy().into_owned(); + if !devnode_str.starts_with(DEV_INPUT_PREFIX) { + continue; + } + let seat = read_seat_from_device(&event); + pending.push((action, devnode, seat)); + } + for (action, devnode, seat) in pending { + match action.as_str() { + "add" => { + if !self.seat.is_empty() + && let Some(seat) = seat.as_deref() + && seat != self.seat + { + continue; + } + let _ = self.open_device(&devnode); + } + "remove" => self.close_device_by_path(&devnode), + _ => {} + } + } + } + + fn drain_device(&mut self, fd: RawFd) { + let mut decoded: Vec<(u16, i32)> = Vec::new(); + let mut device_dead = false; + { + let Some(opened) = self.devices.get_mut(&fd) else { + return; + }; + loop { + let fetch_result = opened.device.fetch_events(); + match fetch_result { + Ok(events) => { + for ev in events { + if ev.event_type() != EventType::KEY { + continue; + } + decoded.push((ev.code(), ev.value())); + } + } + Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => break, + Err(_) => { + device_dead = true; + break; + } + } + } + } + if device_dead { + self.close_device_by_fd(fd); + return; + } + for (code, value) in decoded { + if value == 2 { + continue; + } + self.translate_event(code, value == 1); + } + } + + fn translate_event(&mut self, code: u16, is_press: bool) { + if let Some(button) = keymap::evdev_button_to_browser_button(code) { + let event = NativeEvent::Mouse { + kind: if is_press { + MouseKind::Down + } else { + MouseKind::Up + }, + button, + ctrl: self.modifier_state_ctrl(), + alt: self.modifier_state_alt(), + shift: self.modifier_state_shift(), + meta: self.modifier_state_meta(), + }; + self.dispatch(event); + return; + } + let key_name = match keymap::keycode_to_name(code) { + Some(name) => name, + None => return, + }; + if is_press { + self.held_keys.insert(code); + } else { + self.held_keys.remove(&code); + } + let event = NativeEvent::Key { + kind: if is_press { KeyKind::Down } else { KeyKind::Up }, + keycode: code, + key_name, + ctrl: self.modifier_state_ctrl(), + alt: self.modifier_state_alt(), + shift: self.modifier_state_shift(), + meta: self.modifier_state_meta(), + }; + self.dispatch(event); + } + + fn dispatch(&self, event: NativeEvent) { + let status = self + .callback + .call(event, ThreadsafeFunctionCallMode::NonBlocking); + if status == Status::Closing { + self.stop.store(true, Ordering::Release); + } + } + + fn modifier_state_ctrl(&self) -> bool { + self.held_keys.contains(&keymap::LEFT_CTRL) || self.held_keys.contains(&keymap::RIGHT_CTRL) + } + fn modifier_state_alt(&self) -> bool { + self.held_keys.contains(&keymap::LEFT_ALT) || self.held_keys.contains(&keymap::RIGHT_ALT) + } + fn modifier_state_shift(&self) -> bool { + self.held_keys.contains(&keymap::LEFT_SHIFT) + || self.held_keys.contains(&keymap::RIGHT_SHIFT) + } + fn modifier_state_meta(&self) -> bool { + self.held_keys.contains(&keymap::LEFT_META) || self.held_keys.contains(&keymap::RIGHT_META) + } +} + +fn is_routable_key_code(code: u16) -> bool { + keymap::keycode_to_name(code).is_some() + || keymap::evdev_button_to_browser_button(code).is_some() +} + +fn device_has_routable_input(device: &Device) -> bool { + device.supported_keys().is_some_and(|keys| { + keys.iter() + .any(|key: KeyCode| is_routable_key_code(key.code())) + }) +} + +impl Drop for Reader { + fn drop(&mut self) { + let fds: Vec = self.devices.keys().copied().collect(); + for fd in fds { + let borrowed = unsafe { BorrowedFd::borrow_raw(fd) }; + let _ = self.poller.delete(borrowed); + } + + let _ = self.poller.delete(self.exit_fd.as_borrowed()); + } +} + +struct HookInner { + stop: Option>, + exit_fd: Option>, + thread: Option>, + opened: bool, +} + +impl HookInner { + fn stop_and_join(&mut self) { + if let Some(stop) = &self.stop { + stop.store(true, Ordering::Release); + } + if let Some(exit_fd) = &self.exit_fd { + exit_fd.signal(); + } + if let Some(thread) = self.thread.take() { + let _ = thread.join(); + } + self.stop = None; + self.exit_fd = None; + self.opened = false; + } +} + +#[napi] +pub struct EvdevHook { + callback: EventTsfn, + inner: Mutex, +} + +#[napi] +impl EvdevHook { + #[napi(constructor)] + pub fn new(on_event: Function) -> Result { + let callback = Arc::new( + on_event + .build_threadsafe_function::() + .weak::() + .callee_handled::() + .max_queue_size::() + .build() + .map_err(|err| { + generic_error(format!("failed to build callback: {}", err.reason)) + })?, + ); + Ok(Self { + callback, + inner: Mutex::new(HookInner { + stop: None, + exit_fd: None, + thread: None, + opened: false, + }), + }) + } + + #[napi] + pub fn start(&self) -> Result { + let mut inner = self + .inner + .lock() + .map_err(|_| generic_error("hook lock poisoned"))?; + if inner.thread.is_some() { + return Ok(inner.opened); + } + let stop = Arc::new(AtomicBool::new(false)); + let exit_fd = Arc::new( + ExitFd::new() + .map_err(|err| generic_error(format!("failed to allocate eventfd: {err}")))?, + ); + let mut reader = Reader::new(self.callback.clone(), stop.clone(), exit_fd.clone()) + .map_err(|err| generic_error(format!("evdev start failed: {err}")))?; + + reader.open_all_devices(); + let opened = !reader.devices.is_empty(); + + let join = thread::Builder::new() + .name("fluxer-linux-evdev-reader".to_string()) + .spawn(move || { + let mut reader = reader; + reader.run(); + }) + .map_err(|err| generic_error(format!("failed to spawn reader thread: {err}")))?; + + inner.stop = Some(stop); + inner.exit_fd = Some(exit_fd); + inner.thread = Some(join); + inner.opened = opened; + Ok(opened) + } + + #[napi] + pub fn stop(&self) -> Result<()> { + let mut inner = self + .inner + .lock() + .map_err(|_| generic_error("hook lock poisoned"))?; + inner.stop_and_join(); + Ok(()) + } +} + +impl Drop for EvdevHook { + fn drop(&mut self) { + if let Ok(mut inner) = self.inner.lock() { + inner.stop_and_join(); + } + } +} + +#[napi(js_name = "nameToEvdevKeycode")] +pub fn name_to_evdev_keycode(name: String) -> u32 { + u32::from(keymap::name_to_keycode(&name)) +} + +fn generic_error(reason: impl Into) -> napi::Error { + napi::Error::new(Status::GenericFailure, reason.into()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn control_poll_keys_are_legal_and_outside_fd_range() { + assert_eq!(EXIT_KEY, 0); + assert_eq!(MONITOR_KEY, 1); + assert_ne!(EXIT_KEY, MONITOR_KEY); + assert_eq!(poll_key_for_device_fd(0), Some(DEVICE_KEY_BASE)); + assert_eq!(device_fd_from_poll_key(DEVICE_KEY_BASE), Some(0)); + assert_eq!(device_fd_from_poll_key(EXIT_KEY), None); + assert_eq!(device_fd_from_poll_key(MONITOR_KEY), None); + } + + #[test] + fn poller_accepts_exit_control_key() { + let poller = Poller::new().expect("create poller"); + let exit_fd = ExitFd::new().expect("create exit fd"); + unsafe { + poller + .add(&exit_fd.as_borrowed(), PollEvent::readable(EXIT_KEY)) + .expect("register exit fd"); + } + poller + .delete(exit_fd.as_borrowed()) + .expect("delete exit fd"); + } + + #[test] + fn routable_key_code_filter_keeps_keyboards_and_dom_mouse_buttons() { + assert!(is_routable_key_code(KeyCode::KEY_A.code())); + assert!(is_routable_key_code(KeyCode::KEY_LEFTCTRL.code())); + assert!(is_routable_key_code(KeyCode::BTN_LEFT.code())); + assert!(is_routable_key_code(KeyCode::BTN_FORWARD.code())); + } + + #[test] + fn routable_key_code_filter_ignores_tablet_pad_and_tool_buttons() { + assert!(!is_routable_key_code(KeyCode::BTN_0.code())); + assert!(!is_routable_key_code(KeyCode::BTN_TOOL_PEN.code())); + assert!(!is_routable_key_code(KeyCode::BTN_STYLUS.code())); + } +} diff --git a/fluxer_desktop/native/linux-input-hook/Cargo.lock b/fluxer_desktop/native/linux-input-hook/Cargo.lock new file mode 100644 index 0000000..3c62b06 --- /dev/null +++ b/fluxer_desktop/native/linux-input-hook/Cargo.lock @@ -0,0 +1,349 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fluxer_linux_input_hook" +version = "0.0.0" +dependencies = [ + "napi", + "napi-build", + "napi-derive", + "x11rb", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix", + "windows-link", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "gethostname", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" diff --git a/fluxer_desktop/native/linux-input-hook/Cargo.toml b/fluxer_desktop/native/linux-input-hook/Cargo.toml new file mode 100644 index 0000000..ba86d71 --- /dev/null +++ b/fluxer_desktop/native/linux-input-hook/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "fluxer_linux_input_hook" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8"]} +napi-derive = "3.5.6" + +[target.'cfg(target_os = "linux")'.dependencies] +x11rb = {version = "0.13.2", default-features = false, features = ["record"]} + +[build-dependencies] +napi-build = "2.3.2" diff --git a/fluxer_desktop/native/linux-input-hook/build.rs b/fluxer_desktop/native/linux-input-hook/build.rs new file mode 100644 index 0000000..d45de40 --- /dev/null +++ b/fluxer_desktop/native/linux-input-hook/build.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + napi_build::setup(); +} diff --git a/fluxer_desktop/native/linux-input-hook/index.d.ts b/fluxer_desktop/native/linux-input-hook/index.d.ts new file mode 100644 index 0000000..9aec61b --- /dev/null +++ b/fluxer_desktop/native/linux-input-hook/index.d.ts @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +export type InputEvent = + | { + type: 'keydown' | 'keyup'; + keycode: number; + keyName: string; + ctrlKey: boolean; + altKey: boolean; + shiftKey: boolean; + metaKey: boolean; + } + | { + type: 'mousedown' | 'mouseup'; + button: number; + ctrlKey: boolean; + altKey: boolean; + shiftKey: boolean; + metaKey: boolean; + x?: number; + y?: number; + } + | { + type: 'mousemove'; + x: number; + y: number; + ctrlKey: boolean; + altKey: boolean; + shiftKey: boolean; + metaKey: boolean; + } + | { + type: 'wheel'; + x?: number; + y?: number; + deltaX: number; + deltaY: number; + ctrlKey: boolean; + altKey: boolean; + shiftKey: boolean; + metaKey: boolean; + }; + +export declare class InputHook { + constructor(callback: (event: InputEvent) => void); + + start(): void; + + stop(): void; +} + +export declare function isAvailable(): boolean; + +export declare const loadError: Error | null; diff --git a/fluxer_desktop/native/linux-input-hook/index.js b/fluxer_desktop/native/linux-input-hook/index.js new file mode 100644 index 0000000..c76edbf --- /dev/null +++ b/fluxer_desktop/native/linux-input-hook/index.js @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync} = require('node:fs'); +const {join, sep} = require('node:path'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); +const MODULE_NAME = '@fluxer/linux-input-hook'; +const SKIP_NATIVE_PROBE_ENV = 'FLUXER_LINUX_INPUT_HOOK_SKIP_NATIVE_PROBE'; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName() { + if (process.platform !== 'linux') { + throw new Error(`@fluxer/linux-input-hook is only supported on Linux, got ${process.platform}`); + } + switch (process.arch) { + case 'x64': + return 'linux-input-hook.linux-x64-gnu.node'; + case 'arm64': + return 'linux-input-hook.linux-arm64-gnu.node'; + default: + throw new Error(`Unsupported Linux architecture: ${process.arch}`); + } +} + +let binding = null; +let loadError = null; + +if (process.platform === 'linux') { + try { + const nativeRoot = resolveNativeRoot(); + const nativePath = join(nativeRoot, nativeFileName()); + const loaded = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + binding = loaded.binding; + loadError = loaded.loadError; + if (loadError) throw loadError; + } catch (error) { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: 'native loader threw before binding load completed', + cause: error, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + throw loadError; + } +} + +module.exports = { + InputHook: binding ? binding.InputHook : null, + isAvailable: binding ? binding.isAvailable : () => false, + loadError, +}; diff --git a/fluxer_desktop/native/linux-input-hook/loader-diagnostics.cjs b/fluxer_desktop/native/linux-input-hook/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/linux-input-hook/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/linux-input-hook/package.json b/fluxer_desktop/native/linux-input-hook/package.json new file mode 100644 index 0000000..302529f --- /dev/null +++ b/fluxer_desktop/native/linux-input-hook/package.json @@ -0,0 +1,27 @@ +{ + "name": "@fluxer/linux-input-hook", + "version": "0.0.0", + "description": "", + "private": true, + "license": "AGPL-3.0-or-later", + "os": [ + "linux" + ], + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "linux-input-hook.linux-x64-gnu.node", + "linux-input-hook.linux-arm64-gnu.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "cargo test --manifest-path Cargo.toml" + } +} diff --git a/fluxer_desktop/native/linux-input-hook/src/env.rs b/fluxer_desktop/native/linux-input-hook/src/env.rs new file mode 100644 index 0000000..287326d --- /dev/null +++ b/fluxer_desktop/native/linux-input-hook/src/env.rs @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DisplayServer { + X11, + + Wayland, + + WaylandWithXwayland, + + Unknown, +} + +impl DisplayServer { + pub fn supports_global_xrecord(self) -> bool { + matches!(self, Self::X11) + } +} + +pub fn detect_display_server() -> DisplayServer { + detect_from( + std::env::var("XDG_SESSION_TYPE").ok().as_deref(), + std::env::var("DISPLAY").ok().as_deref(), + std::env::var("WAYLAND_DISPLAY").ok().as_deref(), + ) +} + +fn detect_from( + xdg_session_type: Option<&str>, + display: Option<&str>, + wayland_display: Option<&str>, +) -> DisplayServer { + let has_x11 = display.is_some_and(|v| !v.is_empty()); + let has_wayland = wayland_display.is_some_and(|v| !v.is_empty()); + let xdg_is_wayland = xdg_session_type == Some("wayland"); + match (has_x11, has_wayland) { + (true, true) => DisplayServer::WaylandWithXwayland, + (true, false) if xdg_is_wayland => DisplayServer::WaylandWithXwayland, + (true, false) => DisplayServer::X11, + (false, true) => DisplayServer::Wayland, + (false, false) => match xdg_session_type { + Some("x11") => DisplayServer::X11, + Some("wayland") => DisplayServer::Wayland, + _ => DisplayServer::Unknown, + }, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn detect_pure_x11_from_display_only() { + assert_eq!( + detect_from(Some("x11"), Some(":0"), None), + DisplayServer::X11 + ); + assert_eq!(detect_from(None, Some(":0"), None), DisplayServer::X11); + } + + #[test] + fn detect_pure_wayland_from_wayland_display_only() { + assert_eq!( + detect_from(Some("wayland"), None, Some("wayland-0")), + DisplayServer::Wayland + ); + assert_eq!( + detect_from(None, None, Some("wayland-0")), + DisplayServer::Wayland + ); + } + + #[test] + fn detect_xwayland_when_both_sockets_present() { + let ds = detect_from(Some("wayland"), Some(":0"), Some("wayland-0")); + assert_eq!(ds, DisplayServer::WaylandWithXwayland); + assert!(!ds.supports_global_xrecord()); + } + + #[test] + fn detect_xwayland_from_wayland_session_with_display_only() { + let ds = detect_from(Some("wayland"), Some(":0"), None); + assert_eq!(ds, DisplayServer::WaylandWithXwayland); + assert!(!ds.supports_global_xrecord()); + } + + #[test] + fn detect_unknown_when_nothing_set() { + assert_eq!(detect_from(None, None, None), DisplayServer::Unknown); + assert_eq!(detect_from(Some("tty"), None, None), DisplayServer::Unknown); + } + + #[test] + fn detect_empty_display_var_is_ignored() { + assert_eq!( + detect_from(Some("wayland"), Some(""), Some("wayland-0")), + DisplayServer::Wayland + ); + } + + #[test] + fn xdg_session_type_fallback_when_sockets_missing() { + assert_eq!(detect_from(Some("x11"), None, None), DisplayServer::X11); + assert_eq!( + detect_from(Some("wayland"), None, None), + DisplayServer::Wayland + ); + } + + #[test] + fn global_xrecord_support_is_only_for_pure_x11() { + assert!(DisplayServer::X11.supports_global_xrecord()); + assert!(!DisplayServer::WaylandWithXwayland.supports_global_xrecord()); + assert!(!DisplayServer::Wayland.supports_global_xrecord()); + assert!(!DisplayServer::Unknown.supports_global_xrecord()); + } +} diff --git a/fluxer_desktop/native/linux-input-hook/src/hook.rs b/fluxer_desktop/native/linux-input-hook/src/hook.rs new file mode 100644 index 0000000..798107a --- /dev/null +++ b/fluxer_desktop/native/linux-input-hook/src/hook.rs @@ -0,0 +1,653 @@ +#![allow(non_snake_case)] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex}; +use std::thread::{self, JoinHandle}; + +use napi::{ + Env, Status, + bindgen_prelude::{Function, Object, Result, ToNapiValue}, + sys, + threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode, UnknownReturnValue}, +}; +use napi_derive::napi; +use x11rb::connection::{Connection, RequestConnection}; +use x11rb::errors::ReplyError; +use x11rb::protocol::record::{ + self, ConnectionExt as RecordConnectionExt, ExtRange, Range, Range8, Range16, +}; +use x11rb::protocol::xproto::{ + ConnectionExt as XprotoConnectionExt, GetKeyboardMappingReply, Keycode, +}; +use x11rb::rust_connection::RustConnection; +use x11rb::wrapper::ConnectionExt as WrapperConnectionExt; +use x11rb::x11_utils::TryParse; + +use crate::env::{DisplayServer, detect_display_server}; +use crate::keymap; +use crate::modifiers::{self, Modifiers}; +use crate::mouse::{self, MouseClassification}; +use crate::x11; + +const RECORD_FROM_SERVER: u8 = 0; +const RECORD_START_OF_DATA: u8 = 4; + +const KEY_PRESS: u8 = 2; +const KEY_RELEASE: u8 = 3; +const BUTTON_PRESS: u8 = 4; +const BUTTON_RELEASE: u8 = 5; +const MOTION_NOTIFY: u8 = 6; + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +struct XKeyButtonProto { + type_: u8, + detail: u8, + seq_l: u8, + seq_h: u8, + time: u32, + root: u32, + event: u32, + child: u32, + root_x: i16, + root_y: i16, + event_x: i16, + event_y: i16, + state: u16, + same_screen: u8, + pad0: u8, +} + +#[derive(Debug, Clone)] +pub enum EventKind { + KeyDown, + KeyUp, + MouseDown, + MouseUp, + MouseMove, + Wheel, +} + +#[derive(Debug, Clone)] +pub struct DecodedEvent { + pub kind: EventKind, + pub keycode: u32, + pub key_name: String, + pub button: u8, + pub delta_x: i32, + pub delta_y: i32, + pub x: i32, + pub y: i32, + pub has_xy: bool, + pub mods: Modifiers, +} + +impl DecodedEvent { + fn new(kind: EventKind, mods: Modifiers) -> Self { + Self { + kind, + keycode: 0, + key_name: String::new(), + button: 0, + delta_x: 0, + delta_y: 0, + x: 0, + y: 0, + has_xy: false, + mods, + } + } +} + +impl ToNapiValue for DecodedEvent { + unsafe fn to_napi_value(raw_env: sys::napi_env, event: Self) -> Result { + let env = Env::from_raw(raw_env); + let mut object = Object::new(&env)?; + let kind = match event.kind { + EventKind::KeyDown => "keydown", + EventKind::KeyUp => "keyup", + EventKind::MouseDown => "mousedown", + EventKind::MouseUp => "mouseup", + EventKind::MouseMove => "mousemove", + EventKind::Wheel => "wheel", + }; + object.set("type", kind)?; + object.set("ctrlKey", event.mods.ctrl)?; + object.set("altKey", event.mods.alt)?; + object.set("shiftKey", event.mods.shift)?; + object.set("metaKey", event.mods.meta)?; + match event.kind { + EventKind::KeyDown | EventKind::KeyUp => { + object.set("keycode", event.keycode)?; + object.set("keyName", event.key_name.as_str())?; + } + EventKind::MouseDown | EventKind::MouseUp => { + object.set("button", u32::from(event.button))?; + if event.has_xy { + object.set("x", event.x)?; + object.set("y", event.y)?; + } + } + EventKind::MouseMove => { + object.set("x", event.x)?; + object.set("y", event.y)?; + } + EventKind::Wheel => { + object.set("deltaX", event.delta_x)?; + object.set("deltaY", event.delta_y)?; + if event.has_xy { + object.set("x", event.x)?; + object.set("y", event.y)?; + } + } + } + unsafe { as ToNapiValue>::to_napi_value(raw_env, object) } + } +} + +const EVENT_QUEUE_LIMIT: usize = 1024; + +type EventTsfn = Arc< + ThreadsafeFunction< + DecodedEvent, + UnknownReturnValue, + DecodedEvent, + Status, + false, + true, + EVENT_QUEUE_LIMIT, + >, +>; + +#[derive(Clone)] +struct KeysymCache { + min_keycode: Keycode, + syms: Vec, +} + +impl KeysymCache { + fn build(reply: &GetKeyboardMappingReply, min_keycode: Keycode) -> Self { + let per = reply.keysyms_per_keycode as usize; + let count = if per == 0 { + 0 + } else { + reply.keysyms.len() / per + }; + let mut syms = Vec::with_capacity(count); + if per > 0 { + for i in 0..count { + syms.push(reply.keysyms[i * per]); + } + } + Self { min_keycode, syms } + } + + fn lookup(&self, keycode: u8) -> u32 { + if keycode < self.min_keycode { + return 0; + } + let idx = (keycode - self.min_keycode) as usize; + self.syms.get(idx).copied().unwrap_or(0) + } +} + +struct Active { + ctrl_conn: Arc, + record_ctx: record::Context, + worker: Option>, + stop: Arc, +} + +struct Inner { + callback: EventTsfn, + active: Mutex>, +} + +#[napi] +pub struct InputHook { + inner: Arc, +} + +#[napi] +impl InputHook { + #[napi(constructor)] + pub fn new(callback: Function) -> Result { + let tsfn = callback + .build_threadsafe_function::() + .weak::() + .callee_handled::() + .max_queue_size::() + .build() + .map_err(|err| generic_error(format!("failed to create TSFN: {}", err.reason)))?; + Ok(Self { + inner: Arc::new(Inner { + callback: Arc::new(tsfn), + active: Mutex::new(None), + }), + }) + } + + #[napi] + pub fn start(&self) -> Result<()> { + let mut guard = self + .inner + .active + .lock() + .map_err(|_| generic_error("InputHook lock poisoned"))?; + if guard.is_some() { + return Ok(()); + } + let active = start_record(self.inner.callback.clone())?; + *guard = Some(active); + Ok(()) + } + + #[napi] + pub fn stop(&self) -> Result<()> { + let active = { + let mut guard = self + .inner + .active + .lock() + .map_err(|_| generic_error("InputHook lock poisoned"))?; + guard.take() + }; + if let Some(active) = active { + tear_down(active); + } + Ok(()) + } +} + +impl Drop for InputHook { + fn drop(&mut self) { + if let Ok(mut guard) = self.inner.active.lock() + && let Some(active) = guard.take() + { + tear_down(active); + } + } +} + +fn start_record(callback: EventTsfn) -> Result { + if !detect_display_server().supports_global_xrecord() { + return Err(generic_error( + "InputHook.start failed: WaylandUnsupported — global input \ + capture is blocked by the Wayland security model. Use \ + @fluxer/linux-evdev for kernel-level capture when the user has \ + input device access." + .to_string(), + )); + } + + let (ctrl_conn, _) = x11rb::connect(None) + .map_err(|err| generic_error(format!("InputHook.start failed: NoXDisplay: {err}")))?; + let (data_conn, _) = x11rb::connect(None) + .map_err(|err| generic_error(format!("InputHook.start failed: NoXDisplay: {err}")))?; + let ctrl_conn = Arc::new(ctrl_conn); + + let has_record = ctrl_conn + .extension_information(record::X11_EXTENSION_NAME) + .map_err(|err| generic_error(format!("InputHook.start failed: {err}")))? + .is_some(); + if !has_record { + return Err(generic_error( + "InputHook.start failed: RecordExtensionUnavailable", + )); + } + ctrl_conn + .record_query_version( + record::X11_XML_VERSION.0 as _, + record::X11_XML_VERSION.1 as _, + ) + .map_err(|err| generic_error(format!("InputHook.start failed: RecordQueryVersion: {err}")))? + .reply() + .map_err(|err| { + generic_error(format!("InputHook.start failed: RecordQueryVersion: {err}")) + })?; + + let setup = ctrl_conn.setup(); + let min_keycode = setup.min_keycode; + let max_keycode = setup.max_keycode; + let count = max_keycode.saturating_sub(min_keycode).saturating_add(1); + let mapping = ctrl_conn + .get_keyboard_mapping(min_keycode, count) + .map_err(|err| generic_error(format!("InputHook.start failed: GetKeyboardMapping: {err}")))? + .reply() + .map_err(|err| { + generic_error(format!("InputHook.start failed: GetKeyboardMapping: {err}")) + })?; + let keysyms = KeysymCache::build(&mapping, min_keycode); + + let record_ctx = ctrl_conn + .generate_id() + .map_err(|err| generic_error(format!("InputHook.start failed: GenerateId: {err}")))?; + let empty = Range8 { first: 0, last: 0 }; + let empty_ext = ExtRange { + major: empty, + minor: Range16 { first: 0, last: 0 }, + }; + let range = Range { + core_requests: empty, + core_replies: empty, + ext_requests: empty_ext, + ext_replies: empty_ext, + delivered_events: empty, + device_events: Range8 { + first: KEY_PRESS, + last: MOTION_NOTIFY, + }, + errors: empty, + client_started: false, + client_died: false, + }; + ctrl_conn + .record_create_context(record_ctx, 0, &[record::CS::ALL_CLIENTS.into()], &[range]) + .map_err(|err| { + generic_error(format!( + "InputHook.start failed: RecordCreateContext: {err}" + )) + })? + .check() + .map_err(|err| { + generic_error(format!( + "InputHook.start failed: RecordCreateContext: {err}" + )) + })?; + + let stop = Arc::new(AtomicBool::new(false)); + + let worker_callback = callback.clone(); + let worker_stop = stop.clone(); + let worker = thread::Builder::new() + .name("fluxer-linux-input-hook".to_string()) + .spawn(move || { + worker_main(data_conn, record_ctx, keysyms, worker_callback, worker_stop); + }) + .map_err(|err| generic_error(format!("InputHook.start failed: thread spawn: {err}")))?; + + Ok(Active { + ctrl_conn, + record_ctx, + worker: Some(worker), + stop, + }) +} + +fn worker_main( + data_conn: RustConnection, + record_ctx: record::Context, + keysyms: KeysymCache, + callback: EventTsfn, + stop: Arc, +) { + let cookie = match data_conn.record_enable_context(record_ctx) { + Ok(c) => c, + Err(_) => { + stop.store(true, Ordering::Release); + return; + } + }; + + for reply in cookie { + if stop.load(Ordering::Acquire) { + break; + } + let reply = match reply { + Ok(r) => r, + Err(ReplyError::ConnectionError(_)) => break, + Err(_) => continue, + }; + if reply.client_swapped { + continue; + } + match reply.category { + RECORD_START_OF_DATA => continue, + RECORD_FROM_SERVER => {} + _ => continue, + } + let mut data: &[u8] = &reply.data; + while !data.is_empty() { + let consumed = decode_one(data, &keysyms, &callback, &stop); + if consumed == 0 || consumed > data.len() { + break; + } + data = &data[consumed..]; + } + } +} + +fn decode_one( + data: &[u8], + keysyms: &KeysymCache, + callback: &EventTsfn, + stop: &Arc, +) -> usize { + if data.is_empty() { + return 0; + } + let type_ = data[0]; + match type_ { + KEY_PRESS | KEY_RELEASE | BUTTON_PRESS | BUTTON_RELEASE | MOTION_NOTIFY => { + if data.len() < std::mem::size_of::() { + return 0; + } + + let evt: XKeyButtonProto = + unsafe { std::ptr::read_unaligned(data.as_ptr() as *const XKeyButtonProto) }; + handle_event(&evt, keysyms, callback, stop); + 32 + } + 0 => { + if data.len() < 8 { + return 0; + } + let (length, _) = match u32::try_parse(&data[4..]) { + Ok(v) => v, + Err(_) => return 0, + }; + 32 + (length as usize) * 4 + } + _ => 32, + } +} + +fn handle_event( + evt: &XKeyButtonProto, + keysyms: &KeysymCache, + callback: &EventTsfn, + stop: &Arc, +) { + let mods = modifiers::from_state(u32::from(evt.state)); + match evt.type_ { + KEY_PRESS | KEY_RELEASE => { + let _lookup = x11::xkb_lookup_for_base(); + let keysym = keysyms.lookup(evt.detail); + let mut event = DecodedEvent::new( + if evt.type_ == KEY_PRESS { + EventKind::KeyDown + } else { + EventKind::KeyUp + }, + mods, + ); + event.keycode = keysym; + event.key_name = match keymap::keysym_to_name(keysym) { + Some(name) => name.to_string(), + None => keymap::fallback_name(keysym), + }; + dispatch(callback, stop, event); + } + BUTTON_PRESS | BUTTON_RELEASE => { + let cls = mouse::classify(u32::from(evt.detail)); + match cls { + MouseClassification::Button(b) => { + let mut event = DecodedEvent::new( + if evt.type_ == BUTTON_PRESS { + EventKind::MouseDown + } else { + EventKind::MouseUp + }, + mods, + ); + event.button = b; + event.x = i32::from(evt.root_x); + event.y = i32::from(evt.root_y); + event.has_xy = true; + dispatch(callback, stop, event); + } + MouseClassification::Wheel(dir) => { + if evt.type_ == BUTTON_PRESS { + let mut event = DecodedEvent::new(EventKind::Wheel, mods); + event.delta_x = dir.delta_x(); + event.delta_y = dir.delta_y(); + event.x = i32::from(evt.root_x); + event.y = i32::from(evt.root_y); + event.has_xy = true; + dispatch(callback, stop, event); + } + } + MouseClassification::Ignored => {} + } + } + MOTION_NOTIFY => { + let mut event = DecodedEvent::new(EventKind::MouseMove, mods); + event.x = i32::from(evt.root_x); + event.y = i32::from(evt.root_y); + event.has_xy = true; + dispatch(callback, stop, event); + } + _ => {} + } +} + +fn dispatch(callback: &EventTsfn, stop: &Arc, event: DecodedEvent) { + let status = callback.call(event, ThreadsafeFunctionCallMode::NonBlocking); + if status == Status::Closing { + stop.store(true, Ordering::Release); + } +} + +fn tear_down(mut active: Active) { + active.stop.store(true, Ordering::Release); + + if active.record_ctx != 0 { + let _ = active.ctrl_conn.record_disable_context(active.record_ctx); + let _ = active.ctrl_conn.sync(); + } + if let Some(worker) = active.worker.take() { + let _ = worker.join(); + } + if active.record_ctx != 0 { + let _ = active.ctrl_conn.record_free_context(active.record_ctx); + let _ = active.ctrl_conn.sync(); + } +} + +#[napi(js_name = "isAvailable")] +pub fn is_available() -> bool { + if !detect_display_server().supports_global_xrecord() { + return false; + } + x11rb::connect(None).is_ok() +} + +#[allow(dead_code)] +pub(crate) fn detected_display_server() -> DisplayServer { + detect_display_server() +} + +fn generic_error(reason: impl Into) -> napi::Error { + napi::Error::new(Status::GenericFailure, reason.into()) +} + +#[allow(dead_code)] +const _ASSERT_PROTO_LAYOUT: fn() = || { + use std::mem::offset_of; + let _ = offset_of!(XKeyButtonProto, seq_l); + let _ = offset_of!(XKeyButtonProto, seq_h); + let _ = offset_of!(XKeyButtonProto, event); + let _ = offset_of!(XKeyButtonProto, child); + let _ = offset_of!(XKeyButtonProto, event_x); + let _ = offset_of!(XKeyButtonProto, event_y); + let _ = offset_of!(XKeyButtonProto, same_screen); + let _ = offset_of!(XKeyButtonProto, pad0); + let _ = offset_of!(XKeyButtonProto, root); + let _ = offset_of!(XKeyButtonProto, time); +}; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn decoded_keydown_carries_keysym_and_name() { + let mut event = DecodedEvent::new(EventKind::KeyDown, modifiers::from_state(0)); + event.keycode = 0x0061; + event.key_name = keymap::keysym_to_name(0x0061).unwrap().to_string(); + assert!(matches!(event.kind, EventKind::KeyDown)); + assert_eq!(event.keycode, 0x0061); + assert_eq!(event.key_name, "A"); + assert!(!event.has_xy); + } + + #[test] + fn decoded_wheel_uses_120_step_deltas() { + let dir = mouse::WheelDirection::Down; + let mut event = DecodedEvent::new(EventKind::Wheel, modifiers::from_state(0)); + event.delta_x = dir.delta_x(); + event.delta_y = dir.delta_y(); + event.has_xy = true; + assert_eq!(event.delta_x, 0); + assert_eq!(event.delta_y, 120); + } + + #[test] + fn x_key_button_proto_layout_offsets_match_x11_wire_format() { + use std::mem::offset_of; + assert_eq!(offset_of!(XKeyButtonProto, type_), 0); + assert_eq!(offset_of!(XKeyButtonProto, detail), 1); + + assert_eq!(offset_of!(XKeyButtonProto, time), 4); + assert_eq!(offset_of!(XKeyButtonProto, root_x), 20); + assert_eq!(offset_of!(XKeyButtonProto, root_y), 22); + assert_eq!(offset_of!(XKeyButtonProto, state), 28); + } + + #[test] + fn event_kind_to_string_matches_js_contract() { + let cases: &[(EventKind, &str)] = &[ + (EventKind::KeyDown, "keydown"), + (EventKind::KeyUp, "keyup"), + (EventKind::MouseDown, "mousedown"), + (EventKind::MouseUp, "mouseup"), + (EventKind::MouseMove, "mousemove"), + (EventKind::Wheel, "wheel"), + ]; + for (kind, expected) in cases { + let label = match kind { + EventKind::KeyDown => "keydown", + EventKind::KeyUp => "keyup", + EventKind::MouseDown => "mousedown", + EventKind::MouseUp => "mouseup", + EventKind::MouseMove => "mousemove", + EventKind::Wheel => "wheel", + }; + assert_eq!(label, *expected); + } + } + + #[test] + fn keysym_cache_returns_zero_below_min_keycode() { + let cache = KeysymCache { + min_keycode: 8, + syms: vec![0x61, 0x62, 0x63], + }; + assert_eq!(cache.lookup(7), 0); + assert_eq!(cache.lookup(8), 0x61); + assert_eq!(cache.lookup(10), 0x63); + assert_eq!(cache.lookup(255), 0); + } +} diff --git a/fluxer_desktop/native/linux-input-hook/src/keymap.rs b/fluxer_desktop/native/linux-input-hook/src/keymap.rs new file mode 100644 index 0000000..fd351c3 --- /dev/null +++ b/fluxer_desktop/native/linux-input-hook/src/keymap.rs @@ -0,0 +1,218 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub const KEYSYM_TABLE: &[(u32, &str)] = &[ + (0xff1b, "Escape"), + (0xffbe, "F1"), + (0xffbf, "F2"), + (0xffc0, "F3"), + (0xffc1, "F4"), + (0xffc2, "F5"), + (0xffc3, "F6"), + (0xffc4, "F7"), + (0xffc5, "F8"), + (0xffc6, "F9"), + (0xffc7, "F10"), + (0xffc8, "F11"), + (0xffc9, "F12"), + (0xffca, "F13"), + (0xffcb, "F14"), + (0xffcc, "F15"), + (0xffcd, "F16"), + (0xffce, "F17"), + (0xffcf, "F18"), + (0xffd0, "F19"), + (0xffd1, "F20"), + (0xffd2, "F21"), + (0xffd3, "F22"), + (0xffd4, "F23"), + (0xffd5, "F24"), + (0xff61, "PrintScreen"), + (0xff14, "ScrollLock"), + (0xff13, "Pause"), + (0xff7f, "NumLock"), + (0xff67, "ContextMenu"), + (0x0060, "Backquote"), + (0x007e, "Backquote"), + (0x0031, "1"), + (0x0032, "2"), + (0x0033, "3"), + (0x0034, "4"), + (0x0035, "5"), + (0x0036, "6"), + (0x0037, "7"), + (0x0038, "8"), + (0x0039, "9"), + (0x0030, "0"), + (0x002d, "Minus"), + (0x003d, "Equal"), + (0xff08, "Backspace"), + (0xff09, "Tab"), + (0x0071, "Q"), + (0x0077, "W"), + (0x0065, "E"), + (0x0072, "R"), + (0x0074, "T"), + (0x0079, "Y"), + (0x0075, "U"), + (0x0069, "I"), + (0x006f, "O"), + (0x0070, "P"), + (0x005b, "BracketLeft"), + (0x005d, "BracketRight"), + (0x005c, "Backslash"), + (0xffe5, "CapsLock"), + (0x0061, "A"), + (0x0073, "S"), + (0x0064, "D"), + (0x0066, "F"), + (0x0067, "G"), + (0x0068, "H"), + (0x006a, "J"), + (0x006b, "K"), + (0x006c, "L"), + (0x003b, "Semicolon"), + (0x0027, "Quote"), + (0xff0d, "Enter"), + (0xffe1, "ShiftLeft"), + (0x007a, "Z"), + (0x0078, "X"), + (0x0063, "C"), + (0x0076, "V"), + (0x0062, "B"), + (0x006e, "N"), + (0x006d, "M"), + (0x002c, "Comma"), + (0x002e, "Period"), + (0x002f, "Slash"), + (0xffe2, "ShiftRight"), + (0xffe3, "ControlLeft"), + (0xffeb, "MetaLeft"), + (0xffe9, "AltLeft"), + (0x0020, "Space"), + (0xffea, "AltRight"), + (0xffec, "MetaRight"), + (0xffe4, "ControlRight"), + (0xff80, "Space"), + (0xff89, "Tab"), + (0xff8d, "NumpadEnter"), + (0xffbd, "NumpadEqual"), + (0xffaa, "NumpadMultiply"), + (0xffab, "NumpadAdd"), + (0xffac, "NumpadComma"), + (0xffad, "NumpadSubtract"), + (0xffae, "NumpadDecimal"), + (0xffaf, "NumpadDivide"), + (0xffb0, "Numpad0"), + (0xffb1, "Numpad1"), + (0xffb2, "Numpad2"), + (0xffb3, "Numpad3"), + (0xffb4, "Numpad4"), + (0xffb5, "Numpad5"), + (0xffb6, "Numpad6"), + (0xffb7, "Numpad7"), + (0xffb8, "Numpad8"), + (0xffb9, "Numpad9"), + (0xff51, "ArrowLeft"), + (0xff52, "ArrowUp"), + (0xff53, "ArrowRight"), + (0xff54, "ArrowDown"), + (0xff63, "Insert"), + (0xffff, "Delete"), + (0xff50, "Home"), + (0xff57, "End"), + (0xff55, "PageUp"), + (0xff56, "PageDown"), + (0x1008ff12, "AudioVolumeMute"), + (0x1008ff11, "AudioVolumeDown"), + (0x1008ff13, "AudioVolumeUp"), + (0x1008ff17, "MediaTrackNext"), + (0x1008ff16, "MediaTrackPrevious"), + (0x1008ff15, "MediaStop"), + (0x1008ff14, "MediaPlayPause"), + (0x1008ff26, "BrowserBack"), + (0x1008ff27, "BrowserForward"), + (0x1008ff29, "BrowserRefresh"), + (0x1008ff28, "BrowserStop"), + (0x1008ff1b, "BrowserSearch"), + (0x1008ff30, "BrowserFavorites"), + (0x1008ff18, "BrowserHome"), + (0x1008ff19, "LaunchMail"), + (0x1008ff32, "LaunchMediaPlayer"), + (0x1008ff41, "LaunchApp1"), + (0x1008ff42, "LaunchApp2"), + (0x1008ff2a, "Power"), + (0x1008ff2f, "Sleep"), + (0x1008ff2b, "WakeUp"), + (0xff23, "Convert"), + (0xff22, "NonConvert"), + (0xff2d, "KanaMode"), + (0xff31, "Lang1"), + (0xff34, "Lang2"), +]; + +pub fn keysym_to_name(keysym: u32) -> Option<&'static str> { + KEYSYM_TABLE + .iter() + .find(|(sym, _)| *sym == keysym) + .map(|(_, name)| *name) +} + +pub fn fallback_name(keysym: u32) -> String { + format!("Key{keysym}") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn common_letters_map_to_single_letter_names() { + assert_eq!(keysym_to_name(0x0061), Some("A")); + assert_eq!(keysym_to_name(0x006d), Some("M")); + assert_eq!(keysym_to_name(0x007a), Some("Z")); + } + + #[test] + fn modifier_keysyms_produce_side_distinguished_names() { + assert_eq!(keysym_to_name(0xffe1), Some("ShiftLeft")); + assert_eq!(keysym_to_name(0xffe2), Some("ShiftRight")); + assert_eq!(keysym_to_name(0xffe3), Some("ControlLeft")); + assert_eq!(keysym_to_name(0xffe9), Some("AltLeft")); + assert_eq!(keysym_to_name(0xffeb), Some("MetaLeft")); + } + + #[test] + fn function_keys_f1_through_f12() { + assert_eq!(keysym_to_name(0xffbe), Some("F1")); + assert_eq!(keysym_to_name(0xffc9), Some("F12")); + assert_eq!(keysym_to_name(0xff13), Some("Pause")); + assert_eq!(keysym_to_name(0xffca), Some("F13")); + assert_eq!(keysym_to_name(0xffb0), Some("Numpad0")); + assert_eq!(keysym_to_name(0x1008ff12), Some("AudioVolumeMute")); + assert_eq!(keysym_to_name(0x1008ff41), Some("LaunchApp1")); + } + + #[test] + fn arrows_and_editing_keys_round_trip() { + assert_eq!(keysym_to_name(0xff51), Some("ArrowLeft")); + assert_eq!(keysym_to_name(0xff56), Some("PageDown")); + assert_eq!(keysym_to_name(0xffff), Some("Delete")); + } + + #[test] + fn unknown_keysym_falls_back_to_key_n() { + assert!(keysym_to_name(0x12345).is_none()); + assert_eq!(fallback_name(0x12345), "Key74565"); + } + + #[test] + fn no_distinct_names_share_a_keysym() { + for (i, (sym_i, name_i)) in KEYSYM_TABLE.iter().enumerate() { + for (sym_j, name_j) in &KEYSYM_TABLE[i + 1..] { + if sym_i == sym_j { + assert_eq!(name_i, name_j, "duplicate keysym {sym_i:#x}"); + } + } + } + } +} diff --git a/fluxer_desktop/native/linux-input-hook/src/lib.rs b/fluxer_desktop/native/linux-input-hook/src/lib.rs new file mode 100644 index 0000000..401f4cd --- /dev/null +++ b/fluxer_desktop/native/linux-input-hook/src/lib.rs @@ -0,0 +1,28 @@ +#![deny(clippy::all)] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub mod env; +pub mod keymap; +pub mod modifiers; +pub mod mouse; +pub mod x11; + +#[cfg(target_os = "linux")] +mod hook; + +#[cfg(target_os = "linux")] +pub use hook::{InputHook, is_available}; + +#[cfg(not(target_os = "linux"))] +mod stub { + use napi_derive::napi; + + #[napi] + pub fn is_available() -> bool { + false + } +} + +#[cfg(not(target_os = "linux"))] +pub use stub::is_available; diff --git a/fluxer_desktop/native/linux-input-hook/src/modifiers.rs b/fluxer_desktop/native/linux-input-hook/src/modifiers.rs new file mode 100644 index 0000000..0896f39 --- /dev/null +++ b/fluxer_desktop/native/linux-input-hook/src/modifiers.rs @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub const SHIFT_MASK: u32 = 1 << 0; +pub const CONTROL_MASK: u32 = 1 << 2; +pub const MOD1_MASK: u32 = 1 << 3; +pub const MOD4_MASK: u32 = 1 << 6; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Modifiers { + pub ctrl: bool, + pub alt: bool, + pub shift: bool, + pub meta: bool, +} + +pub fn from_state(state: u32) -> Modifiers { + Modifiers { + ctrl: (state & CONTROL_MASK) != 0, + alt: (state & MOD1_MASK) != 0, + shift: (state & SHIFT_MASK) != 0, + meta: (state & MOD4_MASK) != 0, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn no_bits_all_false() { + let m = from_state(0); + assert!(!m.ctrl && !m.alt && !m.shift && !m.meta); + } + + #[test] + fn shift_alone() { + let m = from_state(SHIFT_MASK); + assert!(m.shift); + assert!(!m.ctrl && !m.alt && !m.meta); + } + + #[test] + fn all_four_modifiers_together() { + let m = from_state(SHIFT_MASK | CONTROL_MASK | MOD1_MASK | MOD4_MASK); + assert!(m.ctrl && m.alt && m.shift && m.meta); + } + + #[test] + fn lock_and_numlock_ignored() { + let m = from_state((1 << 1) | (1 << 4)); + assert!(!m.ctrl && !m.alt && !m.shift && !m.meta); + } + + #[test] + fn mod1_is_alt_mod4_is_meta() { + let a = from_state(MOD1_MASK); + assert!(a.alt && !a.meta); + let b = from_state(MOD4_MASK); + assert!(b.meta && !b.alt); + } +} diff --git a/fluxer_desktop/native/linux-input-hook/src/mouse.rs b/fluxer_desktop/native/linux-input-hook/src/mouse.rs new file mode 100644 index 0000000..87e81ae --- /dev/null +++ b/fluxer_desktop/native/linux-input-hook/src/mouse.rs @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum WheelDirection { + Up, + Down, + Left, + Right, +} + +impl WheelDirection { + pub fn delta_x(self) -> i32 { + match self { + Self::Left => -120, + Self::Right => 120, + _ => 0, + } + } + + pub fn delta_y(self) -> i32 { + match self { + Self::Up => -120, + Self::Down => 120, + _ => 0, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MouseClassification { + Button(u8), + Wheel(WheelDirection), + + Ignored, +} + +pub fn classify(x11_button: u32) -> MouseClassification { + match x11_button { + 1 => MouseClassification::Button(0), + 2 => MouseClassification::Button(1), + 3 => MouseClassification::Button(2), + 4 => MouseClassification::Wheel(WheelDirection::Up), + 5 => MouseClassification::Wheel(WheelDirection::Down), + 6 => MouseClassification::Wheel(WheelDirection::Left), + 7 => MouseClassification::Wheel(WheelDirection::Right), + 8 => MouseClassification::Button(3), + 9 => MouseClassification::Button(4), + _ => MouseClassification::Ignored, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn primary_buttons_map_to_browser_indices() { + assert_eq!(classify(1), MouseClassification::Button(0)); + assert_eq!(classify(2), MouseClassification::Button(1)); + assert_eq!(classify(3), MouseClassification::Button(2)); + } + + #[test] + fn vertical_wheel_delta_y_120_step() { + assert_eq!(classify(4), MouseClassification::Wheel(WheelDirection::Up)); + assert_eq!(WheelDirection::Up.delta_y(), -120); + assert_eq!(WheelDirection::Down.delta_y(), 120); + assert_eq!(WheelDirection::Up.delta_x(), 0); + } + + #[test] + fn horizontal_wheel_delta_x_120_step() { + assert_eq!(WheelDirection::Left.delta_x(), -120); + assert_eq!(WheelDirection::Right.delta_x(), 120); + assert_eq!(WheelDirection::Left.delta_y(), 0); + } + + #[test] + fn back_forward_buttons_map_to_3_and_4() { + assert_eq!(classify(8), MouseClassification::Button(3)); + assert_eq!(classify(9), MouseClassification::Button(4)); + } + + #[test] + fn unknown_buttons_are_ignored() { + assert_eq!(classify(0), MouseClassification::Ignored); + assert_eq!(classify(15), MouseClassification::Ignored); + assert_eq!(classify(255), MouseClassification::Ignored); + } +} diff --git a/fluxer_desktop/native/linux-input-hook/src/x11.rs b/fluxer_desktop/native/linux-input-hook/src/x11.rs new file mode 100644 index 0000000..36a7c22 --- /dev/null +++ b/fluxer_desktop/native/linux-input-hook/src/x11.rs @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Debug, Clone, Copy)] +pub struct XkbLookup { + pub group: u32, + pub level: u32, +} + +pub fn xkb_lookup_for_base() -> XkbLookup { + XkbLookup { group: 0, level: 0 } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn xkb_lookup_for_base_pins_group_level_to_zero() { + let lookup = xkb_lookup_for_base(); + assert_eq!(lookup.group, 0); + assert_eq!(lookup.level, 0); + } +} diff --git a/fluxer_desktop/native/linux-notifications/Cargo.lock b/fluxer_desktop/native/linux-notifications/Cargo.lock new file mode 100644 index 0000000..0051595 --- /dev/null +++ b/fluxer_desktop/native/linux-notifications/Cargo.lock @@ -0,0 +1,1239 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "windows-sys", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-signal" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "fluxer_linux_notifications" +version = "0.0.0" +dependencies = [ + "async-io", + "futures-lite", + "napi", + "napi-build", + "napi-derive", + "zbus", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "uds_windows" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" +dependencies = [ + "memoffset", + "tempfile", + "windows-sys", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "uuid" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +dependencies = [ + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "zbus" +version = "5.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "libc", + "ordered-stream", + "rustix", + "serde", + "serde_repr", + "tracing", + "uds_windows", + "uuid", + "windows-sys", + "winnow", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" +dependencies = [ + "serde", + "winnow", + "zvariant", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zvariant" +version = "5.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a192a0bde63360d77a7523c833d4b4ce6070a927e2c53246e4c540b1a3e27be0" +dependencies = [ + "endi", + "enumflags2", + "serde", + "winnow", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc6cde9c01c511074be97f7ccb6c19d0da89e3f8662e812e999dcfd4638737" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8535915cfa75547e559d8c68e8139909a4aeee076831e4ef7fc59d8172c4d6" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn", + "winnow", +] diff --git a/fluxer_desktop/native/linux-notifications/Cargo.toml b/fluxer_desktop/native/linux-notifications/Cargo.toml new file mode 100644 index 0000000..5353dae --- /dev/null +++ b/fluxer_desktop/native/linux-notifications/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "fluxer_linux_notifications" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +async-io = "2.6.0" +futures-lite = "2.6.1" +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8"]} +napi-derive = "3.5.6" +zbus = "5.16.0" + +[build-dependencies] +napi-build = "2.3.2" diff --git a/fluxer_desktop/native/linux-notifications/build.rs b/fluxer_desktop/native/linux-notifications/build.rs new file mode 100644 index 0000000..d45de40 --- /dev/null +++ b/fluxer_desktop/native/linux-notifications/build.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + napi_build::setup(); +} diff --git a/fluxer_desktop/native/linux-notifications/index.d.ts b/fluxer_desktop/native/linux-notifications/index.d.ts new file mode 100644 index 0000000..e38fce0 --- /dev/null +++ b/fluxer_desktop/native/linux-notifications/index.d.ts @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +export type Urgency = 'low' | 'normal' | 'critical'; + +export interface NotifyImageData { + width: number; + height: number; + rowstride: number; + hasAlpha: boolean; + bitsPerSample: number; + channels: number; + data: Buffer | Uint8Array; +} + +export interface NotifyAction { + key: string; + label: string; +} + +export interface NotifyPayload { + appName: string; + replacesId?: number; + appIcon?: string; + summary: string; + body: string; + actions?: ReadonlyArray; + expireTimeoutMs?: number; + hints?: { + urgency?: Urgency; + category?: string; + desktopEntry?: string; + soundFile?: string; + transient?: boolean; + actionIcons?: boolean; + imageData?: NotifyImageData; + }; +} + +export type FreedesktopNotificationEvent = + | {kind: 'actionInvoked'; id: number; actionKey: string} + | {kind: 'closed'; id: number; reason: number}; + +export interface ServerInformation { + name: string; + vendor: string; + version: string; + specVersion: string; +} + +export declare class FreedesktopNotifications { + constructor(onEvent: (event: FreedesktopNotificationEvent) => void); + + notify(payload: NotifyPayload): Promise; + + closeNotification(id: number): Promise; + + getServerCapabilities(): Promise>; + + close(): Promise; +} + +export declare function getServerInformation(): Promise; + +export declare const loadError: Error | null; diff --git a/fluxer_desktop/native/linux-notifications/index.js b/fluxer_desktop/native/linux-notifications/index.js new file mode 100644 index 0000000..0546762 --- /dev/null +++ b/fluxer_desktop/native/linux-notifications/index.js @@ -0,0 +1,110 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync} = require('node:fs'); +const {join, sep} = require('node:path'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); +const MODULE_NAME = '@fluxer/linux-notifications'; +const SKIP_NATIVE_PROBE_ENV = 'FLUXER_LINUX_NOTIFICATIONS_SKIP_NATIVE_PROBE'; +const REQUIRED_FREEDESKTOP_METHODS = ['notify', 'closeNotification', 'getServerCapabilities', 'close']; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName() { + if (process.platform !== 'linux') { + throw new Error(`@fluxer/linux-notifications is only supported on Linux, got ${process.platform}`); + } + switch (process.arch) { + case 'x64': + return 'linux-notifications.linux-x64-gnu.node'; + case 'arm64': + return 'linux-notifications.linux-arm64-gnu.node'; + default: + throw new Error(`Unsupported Linux architecture: ${process.arch}`); + } +} + +function ownPropertyNames(value) { + if ((typeof value !== 'object' && typeof value !== 'function') || value == null) return []; + return Object.getOwnPropertyNames(value).sort(); +} + +function bindingSurface(bindingValue) { + const freedesktopNotifications = bindingValue ? bindingValue.FreedesktopNotifications : null; + const prototype = freedesktopNotifications ? freedesktopNotifications.prototype : null; + return { + bindingKeys: ownPropertyNames(bindingValue), + freedesktopNotificationsType: typeof freedesktopNotifications, + freedesktopNotificationsName: freedesktopNotifications ? freedesktopNotifications.name : null, + freedesktopNotificationsPrototypeKeys: ownPropertyNames(prototype), + getServerInformationType: typeof (bindingValue ? bindingValue.getServerInformation : null), + }; +} + +function validateBindingSurface(bindingValue, nativeRoot, nativePath) { + const surface = bindingSurface(bindingValue); + const missing = []; + if (typeof bindingValue?.FreedesktopNotifications !== 'function') { + missing.push('FreedesktopNotifications constructor'); + } else { + for (const method of REQUIRED_FREEDESKTOP_METHODS) { + if (typeof bindingValue.FreedesktopNotifications.prototype?.[method] !== 'function') { + missing.push(`FreedesktopNotifications.prototype.${method}`); + } + } + } + if (typeof bindingValue?.getServerInformation !== 'function') { + missing.push('getServerInformation'); + } + if (missing.length === 0) return; + throw createNativeLoadError({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + reason: `native binding surface mismatch: missing ${missing.join(', ')}`, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + extraDiagnostics: [{name: 'bindingSurface', text: JSON.stringify(surface, null, 2)}], + }); +} + +let binding = null; +let loadError = null; + +if (process.platform === 'linux') { + try { + const nativeRoot = resolveNativeRoot(); + const nativePath = join(nativeRoot, nativeFileName()); + const loaded = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + binding = loaded.binding; + loadError = loaded.loadError; + if (loadError) throw loadError; + validateBindingSurface(binding, nativeRoot, nativePath); + } catch (error) { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: 'native loader threw before binding load completed', + cause: error, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + throw loadError; + } +} + +module.exports = { + FreedesktopNotifications: binding ? binding.FreedesktopNotifications : null, + getServerInformation: binding ? binding.getServerInformation : null, + loadError, +}; diff --git a/fluxer_desktop/native/linux-notifications/loader-diagnostics.cjs b/fluxer_desktop/native/linux-notifications/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/linux-notifications/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/linux-notifications/package.json b/fluxer_desktop/native/linux-notifications/package.json new file mode 100644 index 0000000..e6f1f9e --- /dev/null +++ b/fluxer_desktop/native/linux-notifications/package.json @@ -0,0 +1,27 @@ +{ + "name": "@fluxer/linux-notifications", + "version": "0.0.0", + "description": "", + "private": true, + "license": "AGPL-3.0-or-later", + "os": [ + "linux" + ], + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "linux-notifications.linux-x64-gnu.node", + "linux-notifications.linux-arm64-gnu.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "cargo test --manifest-path Cargo.toml" + } +} diff --git a/fluxer_desktop/native/linux-notifications/src/lib.rs b/fluxer_desktop/native/linux-notifications/src/lib.rs new file mode 100644 index 0000000..0a98c13 --- /dev/null +++ b/fluxer_desktop/native/linux-notifications/src/lib.rs @@ -0,0 +1,1172 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::{ + collections::HashMap, + ffi::c_void, + ptr, + sync::{ + Arc, Mutex, + atomic::{AtomicBool, Ordering}, + mpsc, + }, + thread::{self, JoinHandle}, + time::Duration, +}; + +use futures_lite::{FutureExt, StreamExt, future}; +use napi::{ + Env, JsValue, Status, ValueType, + bindgen_prelude::{Array, AsyncTask, Function, Object, Result, Task, ToNapiValue, Unknown}, + sys, + threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode, UnknownReturnValue}, +}; +use napi_derive::napi; +use zbus::{ + MatchRule, MessageStream, + blocking::{Connection as BlockingConnection, Proxy}, + message::Type as MessageType, + zvariant::Value, +}; + +const NOTIFY_DEST: &str = "org.freedesktop.Notifications"; +const NOTIFY_PATH: &str = "/org/freedesktop/Notifications"; +const NOTIFY_IFACE: &str = "org.freedesktop.Notifications"; +const CALL_TIMEOUT: Duration = Duration::from_millis(5_000); +const SIGNAL_POLL_INTERVAL: Duration = Duration::from_millis(200); +const SIGNAL_THREAD_START_TIMEOUT: Duration = Duration::from_secs(5); +const EVENT_QUEUE_LIMIT: usize = 1024; + +const DBUS_SESSION_UNREACHABLE: &str = "DBus session bus unreachable: no $DBUS_SESSION_BUS_ADDRESS and no \ + $XDG_RUNTIME_DIR/bus socket — desktop notifications require a \ + running session bus (systemd-logind, elogind, or dbus-launch)."; + +fn has_dbus_session() -> bool { + has_dbus_session_from( + std::env::var("DBUS_SESSION_BUS_ADDRESS").ok().as_deref(), + std::env::var("XDG_RUNTIME_DIR").ok().as_deref(), + |path| std::path::Path::new(path).exists(), + ) +} + +fn has_dbus_session_from( + bus_address: Option<&str>, + xdg_runtime_dir: Option<&str>, + path_exists: impl Fn(&str) -> bool, +) -> bool { + if bus_address.is_some_and(|v| !v.is_empty()) { + return true; + } + if let Some(dir) = xdg_runtime_dir + && !dir.is_empty() + { + let candidate = format!("{}/bus", dir.trim_end_matches('/')); + if path_exists(&candidate) { + return true; + } + } + false +} + +type EventThreadsafeFunction = Arc< + ThreadsafeFunction< + NotifyEvent, + UnknownReturnValue, + NotifyEvent, + Status, + false, + true, + EVENT_QUEUE_LIMIT, + >, +>; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum Urgency { + Low, + Normal, + Critical, +} + +impl Urgency { + fn parse(value: &str) -> Option { + match value { + "low" => Some(Self::Low), + "normal" => Some(Self::Normal), + "critical" => Some(Self::Critical), + _ => None, + } + } + + fn as_byte(self) -> u8 { + match self { + Self::Low => 0, + Self::Normal => 1, + Self::Critical => 2, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct NotifyImageData { + width: i32, + height: i32, + rowstride: i32, + has_alpha: bool, + bits_per_sample: i32, + channels: i32, + data: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct NotifyHints { + urgency: Option, + category: Option, + desktop_entry: Option, + sound_file: Option, + suppress_sound: Option, + transient: Option, + action_icons: Option, + image_data: Option, +} + +impl NotifyHints { + fn empty() -> Self { + Self { + urgency: None, + category: None, + desktop_entry: None, + sound_file: None, + suppress_sound: None, + transient: None, + action_icons: None, + image_data: None, + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct NotifyAction { + key: String, + label: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct NotifyArgs { + app_name: String, + replaces_id: u32, + app_icon: String, + summary: String, + body: String, + actions: Vec, + hints: NotifyHints, + expire_timeout_ms: i32, +} + +#[derive(Debug)] +pub enum NotifyEvent { + ActionInvoked { id: u32, action_key: String }, + Closed { id: u32, reason: u32 }, +} + +impl ToNapiValue for NotifyEvent { + unsafe fn to_napi_value(raw_env: sys::napi_env, event: Self) -> Result { + let env = Env::from_raw(raw_env); + let mut object = Object::new(&env)?; + match event { + Self::ActionInvoked { id, action_key } => { + object.set("kind", "actionInvoked")?; + object.set("id", id)?; + object.set("actionKey", action_key)?; + } + Self::Closed { id, reason } => { + object.set("kind", "closed")?; + object.set("id", id)?; + object.set("reason", reason)?; + } + } + unsafe { as ToNapiValue>::to_napi_value(raw_env, object) } + } +} + +struct Inner { + method_conn: Mutex>, + stop_signal_thread: Arc, + signal_thread: Mutex>>, + event_callback: Mutex>, +} + +impl Inner { + fn close(&self) { + self.stop_signal_thread.store(true, Ordering::Release); + + if let Ok(mut signal_thread) = self.signal_thread.lock() + && let Some(thread) = signal_thread.take() + { + let _ = thread.join(); + } + + if let Ok(mut method_conn) = self.method_conn.lock() + && let Some(conn) = method_conn.take() + { + let _ = conn.close(); + } + + if let Ok(mut event_callback) = self.event_callback.lock() { + let _ = event_callback.take(); + } + } + + fn with_connection( + &self, + label: &'static str, + f: impl FnOnce(&BlockingConnection) -> Result, + ) -> Result { + let guard = self + .method_conn + .lock() + .map_err(|_| generic_error("notifications client lock poisoned"))?; + let conn = guard + .as_ref() + .ok_or_else(|| generic_error("notifications client closed"))?; + f(conn).map_err(|err| generic_error(format!("{label}: {}", err.reason))) + } +} + +impl Drop for Inner { + fn drop(&mut self) { + self.close(); + } +} + +#[napi] +pub struct FreedesktopNotifications { + inner: Arc, +} + +#[napi] +impl FreedesktopNotifications { + #[napi(constructor)] + pub fn new(on_event: Function) -> Result { + let event_callback = Arc::new( + on_event + .build_threadsafe_function::() + .weak::() + .callee_handled::() + .max_queue_size::() + .build() + .map_err(|err| { + generic_error(format!( + "failed to create notification callback: {}", + err.reason + )) + })?, + ); + if !has_dbus_session() { + return Err(generic_error(DBUS_SESSION_UNREACHABLE)); + } + let method_conn = open_blocking_connection() + .map_err(|err| generic_error(format!("notifications client init failed: {err}")))?; + let stop_signal_thread = Arc::new(AtomicBool::new(false)); + let signal_thread = spawn_signal_thread(event_callback.clone(), stop_signal_thread.clone()) + .map_err(|err| generic_error(format!("notifications client init failed: {err}")))?; + + Ok(Self { + inner: Arc::new(Inner { + method_conn: Mutex::new(Some(method_conn)), + stop_signal_thread, + signal_thread: Mutex::new(Some(signal_thread)), + event_callback: Mutex::new(Some(event_callback)), + }), + }) + } + + #[napi] + pub fn notify(&self, payload: Object) -> Result> { + let args = parse_notify_payload(&payload)?; + Ok(AsyncTask::new(NotifyTask { + inner: self.inner.clone(), + args, + })) + } + + #[napi(js_name = "closeNotification")] + pub fn close_notification(&self, id: u32) -> Result> { + Ok(AsyncTask::new(CloseNotificationTask { + inner: self.inner.clone(), + id, + })) + } + + #[napi(js_name = "getServerCapabilities")] + pub fn get_server_capabilities(&self) -> Result> { + Ok(AsyncTask::new(GetCapabilitiesTask { + inner: self.inner.clone(), + })) + } + + #[napi] + pub fn close(&self) -> Result> { + Ok(AsyncTask::new(CloseTask { + inner: self.inner.clone(), + })) + } +} + +impl Drop for FreedesktopNotifications { + fn drop(&mut self) { + self.inner.close(); + } +} + +pub struct NotifyTask { + inner: Arc, + args: NotifyArgs, +} + +impl Task for NotifyTask { + type Output = u32; + type JsValue = u32; + + fn compute(&mut self) -> Result { + self.inner + .with_connection("Notify failed", |conn| notify(conn, &self.args)) + } + + fn resolve(&mut self, _env: Env, output: Self::Output) -> Result { + Ok(output) + } +} + +pub struct CloseNotificationTask { + inner: Arc, + id: u32, +} + +impl Task for CloseNotificationTask { + type Output = (); + type JsValue = (); + + fn compute(&mut self) -> Result { + self.inner + .with_connection("CloseNotification failed", |conn| { + close_notification(conn, self.id) + }) + } + + fn resolve(&mut self, _env: Env, _output: Self::Output) -> Result { + Ok(()) + } +} + +pub struct GetCapabilitiesTask { + inner: Arc, +} + +impl Task for GetCapabilitiesTask { + type Output = Vec; + type JsValue = Vec; + + fn compute(&mut self) -> Result { + self.inner + .with_connection("GetCapabilities failed", get_capabilities) + } + + fn resolve(&mut self, _env: Env, output: Self::Output) -> Result { + Ok(output) + } +} + +pub struct CloseTask { + inner: Arc, +} + +impl Task for CloseTask { + type Output = (); + type JsValue = (); + + fn compute(&mut self) -> Result { + self.inner.close(); + Ok(()) + } + + fn resolve(&mut self, _env: Env, _output: Self::Output) -> Result { + Ok(()) + } +} + +#[napi(object)] +pub struct ServerInformation { + pub name: String, + pub vendor: String, + pub version: String, + #[napi(js_name = "specVersion")] + pub spec_version: String, +} + +pub struct GetServerInformationTask; + +impl Task for GetServerInformationTask { + type Output = ServerInformation; + type JsValue = ServerInformation; + + fn compute(&mut self) -> Result { + if !has_dbus_session() { + return Err(generic_error(DBUS_SESSION_UNREACHABLE)); + } + let conn = open_blocking_connection() + .map_err(|err| generic_error(format!("openSessionBus failed: {err}")))?; + get_server_information_on(&conn) + .map_err(|err| generic_error(format!("GetServerInformation failed: {}", err.reason))) + } + + fn resolve(&mut self, _env: Env, output: Self::Output) -> Result { + Ok(output) + } +} + +#[napi(js_name = "getServerInformation")] +pub fn get_server_information() -> Result> { + Ok(AsyncTask::new(GetServerInformationTask)) +} + +fn open_blocking_connection() -> zbus::Result { + zbus::blocking::connection::Builder::session()? + .method_timeout(CALL_TIMEOUT) + .build() +} + +fn notification_proxy(conn: &BlockingConnection) -> Result> { + Proxy::new(conn, NOTIFY_DEST, NOTIFY_PATH, NOTIFY_IFACE) + .map_err(|err| generic_error(err.to_string())) +} + +fn notify(conn: &BlockingConnection, args: &NotifyArgs) -> Result { + let proxy = notification_proxy(conn)?; + let actions = flatten_actions(&args.actions); + let hints = build_hints(&args.hints); + proxy + .call( + "Notify", + &( + args.app_name.as_str(), + args.replaces_id, + args.app_icon.as_str(), + args.summary.as_str(), + args.body.as_str(), + actions, + hints, + args.expire_timeout_ms, + ), + ) + .map_err(|err| generic_error(err.to_string())) +} + +fn close_notification(conn: &BlockingConnection, id: u32) -> Result<()> { + notification_proxy(conn)? + .call("CloseNotification", &(id,)) + .map_err(|err| generic_error(err.to_string())) +} + +fn get_capabilities(conn: &BlockingConnection) -> Result> { + notification_proxy(conn)? + .call("GetCapabilities", &()) + .map_err(|err| generic_error(err.to_string())) +} + +fn get_server_information_on(conn: &BlockingConnection) -> Result { + let (name, vendor, version, spec_version): (String, String, String, String) = + notification_proxy(conn)? + .call("GetServerInformation", &()) + .map_err(|err| generic_error(err.to_string()))?; + Ok(ServerInformation { + name, + vendor, + version, + spec_version, + }) +} + +fn flatten_actions(actions: &[NotifyAction]) -> Vec<&str> { + let mut out = Vec::with_capacity(actions.len() * 2); + for action in actions { + out.push(action.key.as_str()); + out.push(action.label.as_str()); + } + out +} + +fn build_hints(hints: &NotifyHints) -> HashMap<&'static str, Value<'_>> { + let mut out = HashMap::new(); + if let Some(urgency) = hints.urgency { + out.insert("urgency", Value::new(urgency.as_byte())); + } + if let Some(category) = hints.category.as_deref() { + out.insert("category", Value::new(category)); + } + if let Some(desktop_entry) = hints.desktop_entry.as_deref() { + out.insert("desktop-entry", Value::new(desktop_entry)); + } + if let Some(sound_file) = hints.sound_file.as_deref() { + out.insert("sound-file", Value::new(sound_file)); + } + if let Some(suppress_sound) = hints.suppress_sound { + out.insert("suppress-sound", Value::new(suppress_sound)); + } + if let Some(transient) = hints.transient { + out.insert("transient", Value::new(transient)); + } + if let Some(action_icons) = hints.action_icons { + out.insert("action-icons", Value::new(action_icons)); + } + if let Some(image) = hints.image_data.as_ref() { + out.insert( + "image-data", + Value::new(( + image.width, + image.height, + image.rowstride, + image.has_alpha, + image.bits_per_sample, + image.channels, + image.data.as_slice(), + )), + ); + } + out +} + +fn spawn_signal_thread( + event_callback: EventThreadsafeFunction, + stop: Arc, +) -> std::result::Result, String> { + let (ready_tx, ready_rx) = mpsc::sync_channel(1); + let stop_for_thread = stop.clone(); + let thread = thread::Builder::new() + .name("fluxer-linux-notifications-signals".to_string()) + .spawn(move || { + let setup = future::block_on(async { + let conn = zbus::Connection::session().await?; + let rule = MatchRule::builder() + .msg_type(MessageType::Signal) + .sender(NOTIFY_DEST)? + .interface(NOTIFY_IFACE)? + .build(); + let stream = MessageStream::for_match_rule(rule, &conn, Some(64)).await?; + zbus::Result::Ok((conn, stream)) + }); + + let (_conn, mut stream) = match setup { + Ok(parts) => { + let _ = ready_tx.send(Ok(())); + parts + } + Err(err) => { + let _ = ready_tx.send(Err(err.to_string())); + return; + } + }; + + while !stop_for_thread.load(Ordering::Acquire) { + let timeout = async { + async_io::Timer::after(SIGNAL_POLL_INTERVAL).await; + None + }; + let next_message = stream.next().or(timeout); + match future::block_on(next_message) { + Some(Ok(message)) => { + if let Some(event) = event_from_signal(&message) { + let status = + event_callback.call(event, ThreadsafeFunctionCallMode::NonBlocking); + if status == Status::Closing { + break; + } + } + } + Some(Err(_)) => break, + None => {} + } + } + }) + .map_err(|err| err.to_string())?; + + match ready_rx.recv_timeout(SIGNAL_THREAD_START_TIMEOUT) { + Ok(Ok(())) => Ok(thread), + Ok(Err(err)) => { + let _ = thread.join(); + Err(err) + } + Err(err) => { + stop.store(true, Ordering::Release); + if matches!(err, mpsc::RecvTimeoutError::Disconnected) { + let _ = thread.join(); + } + Err(err.to_string()) + } + } +} + +fn event_from_signal(message: &zbus::Message) -> Option { + let header = message.header(); + if header.interface().map(|iface| iface.as_str()) != Some(NOTIFY_IFACE) { + return None; + } + + match header.member()?.as_str() { + "ActionInvoked" => { + let (id, action_key): (u32, String) = message.body().deserialize().ok()?; + Some(NotifyEvent::ActionInvoked { id, action_key }) + } + "NotificationClosed" => { + let (id, reason): (u32, u32) = message.body().deserialize().ok()?; + Some(NotifyEvent::Closed { id, reason }) + } + _ => None, + } +} + +fn parse_notify_payload(payload: &Object) -> Result { + Ok(NotifyArgs { + app_name: read_string_or_empty(payload, "appName"), + replaces_id: read_optional_u32(payload, "replacesId").unwrap_or(0), + app_icon: read_string_or_empty(payload, "appIcon"), + summary: read_string_or_empty(payload, "summary"), + body: read_string_or_empty(payload, "body"), + actions: parse_actions(payload), + hints: parse_hints(payload)?, + expire_timeout_ms: read_optional_i32(payload, "expireTimeoutMs").unwrap_or(-1), + }) +} + +fn parse_actions(payload: &Object) -> Vec { + let Some(actions) = read_array(payload, "actions") else { + return Vec::new(); + }; + + let mut out = Vec::with_capacity(actions.len() as usize); + for index in 0..actions.len() { + let action = actions.get::(index).ok().flatten(); + match action { + Some(action) => out.push(NotifyAction { + key: read_string_or_empty(&action, "key"), + label: read_string_or_empty(&action, "label"), + }), + None => out.push(NotifyAction { + key: String::new(), + label: String::new(), + }), + } + } + out +} + +fn parse_hints(payload: &Object) -> Result { + let Some(hints_object) = read_object(payload, "hints") else { + return Ok(NotifyHints::empty()); + }; + + let mut hints = NotifyHints::empty(); + if let Some(value) = read_optional_string(&hints_object, "urgency") { + hints.urgency = Urgency::parse(&value); + } + hints.category = read_optional_string(&hints_object, "category"); + hints.desktop_entry = read_optional_string(&hints_object, "desktopEntry"); + hints.sound_file = read_optional_string(&hints_object, "soundFile"); + hints.suppress_sound = read_optional_bool(&hints_object, "suppressSound"); + hints.transient = read_optional_bool(&hints_object, "transient"); + hints.action_icons = read_optional_bool(&hints_object, "actionIcons"); + + if let Some(image_object) = read_object(&hints_object, "imageData") { + hints.image_data = Some(parse_image_data(&image_object)?); + } + + Ok(hints) +} + +fn parse_image_data(value: &Object) -> Result { + let width = read_optional_i32(value, "width").unwrap_or(0); + let height = read_optional_i32(value, "height").unwrap_or(0); + let channels = read_optional_i32(value, "channels").unwrap_or(4); + let mut rowstride = read_optional_i32(value, "rowstride").unwrap_or(0); + let bits_per_sample = read_optional_i32(value, "bitsPerSample").unwrap_or(8); + let has_alpha = read_optional_bool(value, "hasAlpha").unwrap_or(true); + let data_value = read_unknown(value, "data") + .ok_or_else(|| invalid_arg("imageData.data must be a Buffer or Uint8Array"))?; + let data = read_image_bytes(data_value)?; + + if width <= 0 || height <= 0 { + return Err(invalid_arg("imageData width and height must be positive")); + } + if rowstride <= 0 { + rowstride = width.saturating_mul(channels); + } + validate_image_data_shape( + width, + height, + rowstride, + has_alpha, + bits_per_sample, + channels, + data.len(), + )?; + + Ok(NotifyImageData { + width, + height, + rowstride, + has_alpha, + bits_per_sample, + channels, + data, + }) +} + +fn validate_image_data_shape( + width: i32, + height: i32, + rowstride: i32, + has_alpha: bool, + bits_per_sample: i32, + channels: i32, + data_len: usize, +) -> Result<()> { + if bits_per_sample != 8 { + return Err(invalid_arg("imageData.bitsPerSample must be 8")); + } + + let expected_channels = if has_alpha { 4 } else { 3 }; + if channels != expected_channels { + return Err(invalid_arg(format!( + "imageData.channels must be {expected_channels} when hasAlpha is {has_alpha}" + ))); + } + + let width = usize::try_from(width).map_err(|_| invalid_arg("imageData width is invalid"))?; + let height = usize::try_from(height).map_err(|_| invalid_arg("imageData height is invalid"))?; + let rowstride = + usize::try_from(rowstride).map_err(|_| invalid_arg("imageData.rowstride is invalid"))?; + let channels = + usize::try_from(channels).map_err(|_| invalid_arg("imageData.channels is invalid"))?; + + let row_bytes = width + .checked_mul(channels) + .ok_or_else(|| invalid_arg("imageData row byte count overflows"))?; + if rowstride < row_bytes { + return Err(invalid_arg( + "imageData.rowstride is smaller than one pixel row", + )); + } + + let min_data_len = if height == 1 { + row_bytes + } else { + rowstride + .checked_mul(height - 1) + .and_then(|bytes| bytes.checked_add(row_bytes)) + .ok_or_else(|| invalid_arg("imageData byte count overflows"))? + }; + if data_len < min_data_len { + return Err(invalid_arg( + "imageData.data is too short for the supplied dimensions", + )); + } + + Ok(()) +} + +fn read_unknown<'a>(object: &Object<'a>, key: &str) -> Option> { + object.get::(key).ok().flatten() +} + +fn read_string_or_empty(object: &Object, key: &str) -> String { + read_optional_string(object, key).unwrap_or_default() +} + +fn read_optional_string(object: &Object, key: &str) -> Option { + let value = read_unknown(object, key)?; + if value.get_type().ok()? != ValueType::String { + return None; + } + unsafe { value.cast::().ok() } +} + +fn read_optional_u32(object: &Object, key: &str) -> Option { + let value = read_unknown(object, key)?; + if value.get_type().ok()? != ValueType::Number { + return None; + } + unsafe { value.cast::().ok() } +} + +fn read_optional_i32(object: &Object, key: &str) -> Option { + let value = read_unknown(object, key)?; + if value.get_type().ok()? != ValueType::Number { + return None; + } + unsafe { value.cast::().ok() } +} + +fn read_optional_bool(object: &Object, key: &str) -> Option { + let value = read_unknown(object, key)?; + if value.get_type().ok()? != ValueType::Boolean { + return None; + } + unsafe { value.cast::().ok() } +} + +fn read_object<'a>(object: &Object<'a>, key: &str) -> Option> { + let value = read_unknown(object, key)?; + if value.get_type().ok()? != ValueType::Object { + return None; + } + unsafe { value.cast::().ok() } +} + +fn read_array<'a>(object: &Object<'a>, key: &str) -> Option> { + let value = read_unknown(object, key)?; + if value.get_type().ok()? != ValueType::Object { + return None; + } + unsafe { value.cast::().ok() } +} + +fn read_image_bytes(value: Unknown) -> Result> { + let raw = value.raw(); + let raw_env = value.value().env; + + let mut is_typed_array = false; + let typed_status = unsafe { sys::napi_is_typedarray(raw_env, raw, &mut is_typed_array) }; + if typed_status == sys::Status::napi_ok && is_typed_array { + let mut array_type: sys::napi_typedarray_type = 0; + let mut len = 0usize; + let mut data_ptr: *mut c_void = ptr::null_mut(); + let mut arraybuffer = ptr::null_mut(); + let mut byte_offset = 0usize; + let status = unsafe { + sys::napi_get_typedarray_info( + raw_env, + raw, + &mut array_type, + &mut len, + &mut data_ptr, + &mut arraybuffer, + &mut byte_offset, + ) + }; + if status != sys::Status::napi_ok { + return Err(invalid_arg("imageData.data must be a readable typed array")); + } + if !is_uint8_typed_array(array_type) { + return Err(invalid_arg("imageData.data must be a Buffer or Uint8Array")); + } + return copy_non_empty_bytes(data_ptr.cast(), len); + } + + let mut is_buffer = false; + let buffer_status = unsafe { sys::napi_is_buffer(raw_env, raw, &mut is_buffer) }; + if buffer_status == sys::Status::napi_ok && is_buffer { + let mut data_ptr: *mut c_void = ptr::null_mut(); + let mut len = 0usize; + let status = unsafe { sys::napi_get_buffer_info(raw_env, raw, &mut data_ptr, &mut len) }; + if status != sys::Status::napi_ok { + return Err(invalid_arg("imageData.data must be a readable Buffer")); + } + return copy_non_empty_bytes(data_ptr.cast(), len); + } + + Err(invalid_arg("imageData.data must be a Buffer or Uint8Array")) +} + +fn is_uint8_typed_array(array_type: sys::napi_typedarray_type) -> bool { + array_type == sys::TypedarrayType::uint8_array +} + +fn copy_non_empty_bytes(data_ptr: *const u8, len: usize) -> Result> { + if data_ptr.is_null() || len == 0 { + return Err(invalid_arg("imageData.data must not be empty")); + } + Ok(unsafe { std::slice::from_raw_parts(data_ptr, len).to_vec() }) +} + +fn generic_error(reason: impl Into) -> napi::Error { + napi::Error::new(Status::GenericFailure, reason.into()) +} + +fn invalid_arg(reason: impl Into) -> napi::Error { + napi::Error::new(Status::InvalidArg, reason.into()) +} + +#[cfg(test)] +#[derive(Debug, Clone, PartialEq, Eq)] +enum FakeArg<'a> { + U32(u32), + String(&'a str), +} + +#[cfg(test)] +#[derive(Debug, Clone, PartialEq, Eq)] +struct NotifyReply { + id: u32, +} + +#[cfg(test)] +#[derive(Debug, Clone, PartialEq, Eq)] +struct ActionInvokedSignal { + id: u32, + action_key: String, +} + +#[cfg(test)] +#[derive(Debug, Clone, PartialEq, Eq)] +struct ClosedSignal { + id: u32, + reason: u32, +} + +#[cfg(test)] +#[derive(Debug, Clone, PartialEq, Eq)] +enum ParseError { + InvalidReply, +} + +#[cfg(test)] +fn parse_notify_reply_from_args( + args: &[FakeArg<'_>], +) -> std::result::Result { + match args.first() { + Some(FakeArg::U32(id)) => Ok(NotifyReply { id: *id }), + _ => Err(ParseError::InvalidReply), + } +} + +#[cfg(test)] +fn parse_action_invoked_from_args( + args: &[FakeArg<'_>], +) -> std::result::Result { + match (args.first(), args.get(1)) { + (Some(FakeArg::U32(id)), Some(FakeArg::String(action_key))) => Ok(ActionInvokedSignal { + id: *id, + action_key: (*action_key).to_string(), + }), + _ => Err(ParseError::InvalidReply), + } +} + +#[cfg(test)] +fn parse_closed_from_args(args: &[FakeArg<'_>]) -> std::result::Result { + match (args.first(), args.get(1)) { + (Some(FakeArg::U32(id)), Some(FakeArg::U32(reason))) => Ok(ClosedSignal { + id: *id, + reason: *reason, + }), + _ => Err(ParseError::InvalidReply), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn notify_reply_valid_u32_yields_id() { + let reply = parse_notify_reply_from_args(&[FakeArg::U32(42)]).unwrap(); + assert_eq!(reply.id, 42); + } + + #[test] + fn notify_reply_rejects_empty_body() { + assert_eq!( + parse_notify_reply_from_args(&[]), + Err(ParseError::InvalidReply) + ); + } + + #[test] + fn notify_reply_rejects_wrong_type() { + assert_eq!( + parse_notify_reply_from_args(&[FakeArg::String("wrong")]), + Err(ParseError::InvalidReply) + ); + } + + #[test] + fn action_invoked_valid_body() { + let signal = + parse_action_invoked_from_args(&[FakeArg::U32(7), FakeArg::String("default")]).unwrap(); + assert_eq!( + signal, + ActionInvokedSignal { + id: 7, + action_key: "default".to_string() + } + ); + } + + #[test] + fn action_invoked_rejects_swapped_types() { + assert_eq!( + parse_action_invoked_from_args(&[FakeArg::String("x"), FakeArg::String("y")]), + Err(ParseError::InvalidReply) + ); + } + + #[test] + fn notification_closed_valid_body() { + let signal = parse_closed_from_args(&[FakeArg::U32(11), FakeArg::U32(2)]).unwrap(); + assert_eq!(signal, ClosedSignal { id: 11, reason: 2 }); + } + + #[test] + fn notification_closed_rejects_too_short() { + assert_eq!( + parse_closed_from_args(&[FakeArg::U32(11)]), + Err(ParseError::InvalidReply) + ); + } + + #[test] + fn actions_are_flattened_as_key_label_pairs() { + let actions = vec![ + NotifyAction { + key: "default".to_string(), + label: "Open".to_string(), + }, + NotifyAction { + key: "dismiss".to_string(), + label: "Dismiss".to_string(), + }, + ]; + + assert_eq!( + flatten_actions(&actions), + vec!["default", "Open", "dismiss", "Dismiss"] + ); + } + + #[test] + fn urgency_strings_match_freedesktop_bytes() { + assert_eq!(Urgency::parse("low").map(Urgency::as_byte), Some(0)); + assert_eq!(Urgency::parse("normal").map(Urgency::as_byte), Some(1)); + assert_eq!(Urgency::parse("critical").map(Urgency::as_byte), Some(2)); + assert_eq!(Urgency::parse("unknown"), None); + } + + #[test] + fn default_notify_args_match_legacy_defaults() { + let args = NotifyArgs { + app_name: String::new(), + replaces_id: 0, + app_icon: String::new(), + summary: String::new(), + body: String::new(), + actions: Vec::new(), + hints: NotifyHints::empty(), + expire_timeout_ms: -1, + }; + + assert_eq!(args.replaces_id, 0); + assert_eq!(args.expire_timeout_ms, -1); + assert!(flatten_actions(&args.actions).is_empty()); + assert!(build_hints(&args.hints).is_empty()); + } + + #[test] + fn hints_use_freedesktop_variant_signatures() { + let hints = NotifyHints { + urgency: Some(Urgency::Critical), + category: Some("im.received".to_string()), + desktop_entry: Some("fluxer".to_string()), + sound_file: Some("/tmp/notify.oga".to_string()), + suppress_sound: Some(true), + transient: Some(true), + action_icons: Some(false), + image_data: Some(NotifyImageData { + width: 1, + height: 1, + rowstride: 4, + has_alpha: true, + bits_per_sample: 8, + channels: 4, + data: vec![0, 1, 2, 3], + }), + }; + + let values = build_hints(&hints); + + assert_eq!(values["urgency"].value_signature().to_string(), "y"); + assert_eq!(values["category"].value_signature().to_string(), "s"); + assert_eq!(values["desktop-entry"].value_signature().to_string(), "s"); + assert_eq!(values["sound-file"].value_signature().to_string(), "s"); + assert_eq!(values["suppress-sound"].value_signature().to_string(), "b"); + assert_eq!(values["transient"].value_signature().to_string(), "b"); + assert_eq!(values["action-icons"].value_signature().to_string(), "b"); + assert_eq!( + values["image-data"].value_signature().to_string(), + "(iiibiiay)" + ); + } + + #[test] + fn dbus_session_detected_via_bus_address() { + assert!(has_dbus_session_from( + Some("unix:path=/run/user/1000/bus"), + None, + |_| false + )); + } + + #[test] + fn dbus_session_detected_via_runtime_dir_socket() { + assert!(has_dbus_session_from( + None, + Some("/run/user/1000"), + |path| path == "/run/user/1000/bus" + )); + assert!(has_dbus_session_from( + None, + Some("/run/user/1000/"), + |path| path == "/run/user/1000/bus" + )); + } + + #[test] + fn dbus_session_absent_in_headless_container() { + assert!(!has_dbus_session_from(None, None, |_| false)); + assert!(!has_dbus_session_from(Some(""), Some(""), |_| true)); + assert!(!has_dbus_session_from(None, Some("/tmp/xdg"), |_| false)); + } + + #[test] + fn dbus_session_unreachable_marker_is_stable_for_renderer_matching() { + assert!(DBUS_SESSION_UNREACHABLE.starts_with("DBus session bus unreachable")); + } + + #[test] + fn image_rowstride_defaults_to_width_times_channels() { + let width = 3; + let channels = 4; + let mut rowstride = 0; + if rowstride <= 0 { + rowstride = width * channels; + } + assert_eq!(rowstride, 12); + } + + #[test] + fn image_data_shape_matches_freedesktop_pixbuf_contract() { + assert!(validate_image_data_shape(2, 2, 8, true, 8, 4, 16).is_ok()); + assert!(validate_image_data_shape(2, 2, 6, false, 8, 3, 12).is_ok()); + assert!(validate_image_data_shape(2, 2, 8, true, 16, 4, 16).is_err()); + assert!(validate_image_data_shape(2, 2, 8, true, 8, 3, 16).is_err()); + assert!(validate_image_data_shape(2, 2, 4, true, 8, 4, 16).is_err()); + assert!(validate_image_data_shape(2, 2, 8, true, 8, 4, 15).is_err()); + } + + #[test] + fn image_data_typed_array_gate_accepts_only_uint8array() { + assert!(is_uint8_typed_array(sys::TypedarrayType::uint8_array)); + assert!(!is_uint8_typed_array(sys::TypedarrayType::int8_array)); + assert!(!is_uint8_typed_array( + sys::TypedarrayType::uint8_clamped_array + )); + assert!(!is_uint8_typed_array(sys::TypedarrayType::float32_array)); + } +} diff --git a/fluxer_desktop/native/linux-portals/Cargo.lock b/fluxer_desktop/native/linux-portals/Cargo.lock new file mode 100644 index 0000000..cc8f4a8 --- /dev/null +++ b/fluxer_desktop/native/linux-portals/Cargo.lock @@ -0,0 +1,1267 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "windows-sys", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-signal" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "fluxer_linux_portals" +version = "0.0.0" +dependencies = [ + "async-io", + "futures-lite", + "napi", + "napi-build", + "napi-derive", + "x11rb", + "zbus", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix", + "windows-link", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "uds_windows" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" +dependencies = [ + "memoffset", + "tempfile", + "windows-sys", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "uuid" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +dependencies = [ + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "gethostname", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + +[[package]] +name = "zbus" +version = "5.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "libc", + "ordered-stream", + "rustix", + "serde", + "serde_repr", + "tracing", + "uds_windows", + "uuid", + "windows-sys", + "winnow", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" +dependencies = [ + "serde", + "winnow", + "zvariant", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zvariant" +version = "5.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a192a0bde63360d77a7523c833d4b4ce6070a927e2c53246e4c540b1a3e27be0" +dependencies = [ + "endi", + "enumflags2", + "serde", + "winnow", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc6cde9c01c511074be97f7ccb6c19d0da89e3f8662e812e999dcfd4638737" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8535915cfa75547e559d8c68e8139909a4aeee076831e4ef7fc59d8172c4d6" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn", + "winnow", +] diff --git a/fluxer_desktop/native/linux-portals/Cargo.toml b/fluxer_desktop/native/linux-portals/Cargo.toml new file mode 100644 index 0000000..652bcbe --- /dev/null +++ b/fluxer_desktop/native/linux-portals/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "fluxer_linux_portals" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8"]} +napi-derive = "3.5.6" + +[target.'cfg(target_os = "linux")'.dependencies] +async-io = "2.6.0" +futures-lite = "2.6.1" +x11rb = "0.13.2" +zbus = "5.16.0" + +[build-dependencies] +napi-build = "2.3.2" diff --git a/fluxer_desktop/native/linux-portals/build.rs b/fluxer_desktop/native/linux-portals/build.rs new file mode 100644 index 0000000..d45de40 --- /dev/null +++ b/fluxer_desktop/native/linux-portals/build.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + napi_build::setup(); +} diff --git a/fluxer_desktop/native/linux-portals/index.d.ts b/fluxer_desktop/native/linux-portals/index.d.ts new file mode 100644 index 0000000..54cfcb2 --- /dev/null +++ b/fluxer_desktop/native/linux-portals/index.d.ts @@ -0,0 +1,121 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +export interface ResolveWindowPidSpec { + backend: 'gnome-shell-eval'; + token: string; +} + +export declare function resolveWindowPid(spec: ResolveWindowPidSpec): Promise; + +export declare function resolveKwinWindowPid(token: string): Promise; + +export declare function resolveX11WindowPid(token: string): Promise; + +export type FileChooserFilterKind = 0 | 1; + +export interface FileChooserFilterRule { + kind: FileChooserFilterKind; + pattern: string; +} + +export interface FileChooserFilter { + name: string; + rules: ReadonlyArray; +} + +export interface FileChooserOptions { + parentWindow?: string; + title?: string; + acceptLabel?: string; + modal?: boolean; + multiple?: boolean; + directory?: boolean; + currentFolder?: string; + currentName?: string; + currentFile?: string; + filters?: ReadonlyArray; + currentFilter?: FileChooserFilter; +} + +export interface FileChooserResult { + cancelled: boolean; + uris: Array; +} + +export declare function openFile(options: FileChooserOptions): Promise; + +export declare function saveFile(options: FileChooserOptions): Promise; + +export interface BackgroundOptions { + reason?: string; + autostart?: boolean; + commandline?: ReadonlyArray; + dbusActivatable?: boolean; +} + +export interface BackgroundResult { + response: number; + cancelled: boolean; + background: boolean; + autostart: boolean; +} + +export declare function requestBackground(options: BackgroundOptions): Promise; + +export interface GlobalShortcutEntry { + id: string; + description: string; + preferredTrigger?: string; +} + +export interface BoundGlobalShortcut { + id: string; + description?: string; + triggerDescription?: string; +} + +export type GlobalShortcutPortalAction = 'listed' | 'bound' | 'cancelled'; + +export interface GlobalShortcutsConfigureResult { + action: GlobalShortcutPortalAction; + shortcuts: Array; +} + +export type GlobalShortcutPortalEvent = + | {type: 'activated'; id: string} + | {type: 'deactivated'; id: string} + | {type: 'shortcuts-changed'; shortcuts: Array} + | {type: 'closed'}; + +export declare class GlobalShortcutsPortal { + constructor(onEvent: (event: GlobalShortcutPortalEvent) => void, appId?: string | null); + + configure(entries: ReadonlyArray): Promise; + + close(): void; +} + +export declare function isAvailable(): boolean; + +export declare function getPortalVersion(): number | null; + +export declare function readColorScheme(): 'no-preference' | 'prefer-dark' | 'prefer-light'; + +export declare function readContrast(): 'no-preference' | 'high'; + +export declare function readAccentColor(): {r: number; g: number; b: number} | null; + +export interface SettingsChangeEvent { + namespace: string; + key: string; + uint32?: number; + accent?: {r: number; g: number; b: number}; +} + +export declare class Settings { + constructor(onChange: (event: SettingsChangeEvent) => void); + + close(): void; +} + +export declare const loadError: Error | null; diff --git a/fluxer_desktop/native/linux-portals/index.js b/fluxer_desktop/native/linux-portals/index.js new file mode 100644 index 0000000..e5efcd2 --- /dev/null +++ b/fluxer_desktop/native/linux-portals/index.js @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync} = require('node:fs'); +const {join, sep} = require('node:path'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); +const MODULE_NAME = '@fluxer/linux-portals'; +const SKIP_NATIVE_PROBE_ENV = 'FLUXER_LINUX_PORTALS_SKIP_NATIVE_PROBE'; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName() { + if (process.platform !== 'linux') { + throw new Error(`@fluxer/linux-portals is only supported on Linux, got ${process.platform}`); + } + switch (process.arch) { + case 'x64': + return 'linux-portals.linux-x64-gnu.node'; + case 'arm64': + return 'linux-portals.linux-arm64-gnu.node'; + default: + throw new Error(`Unsupported Linux architecture: ${process.arch}`); + } +} + +let binding = null; +let loadError = null; + +if (process.platform === 'linux') { + try { + const nativeRoot = resolveNativeRoot(); + const nativePath = join(nativeRoot, nativeFileName()); + const loaded = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + binding = loaded.binding; + loadError = loaded.loadError; + if (loadError) throw loadError; + } catch (error) { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: 'native loader threw before binding load completed', + cause: error, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + throw loadError; + } +} + +module.exports = { + resolveKwinWindowPid: binding ? binding.resolveKwinWindowPid : null, + resolveX11WindowPid: binding ? binding.resolveX11WindowPid : null, + resolveWindowPid: binding ? binding.resolveWindowPid : null, + openFile: binding ? binding.openFile : null, + saveFile: binding ? binding.saveFile : null, + requestBackground: binding ? binding.requestBackground : null, + GlobalShortcutsPortal: binding ? binding.GlobalShortcutsPortal : null, + isAvailable: binding ? binding.isAvailable : () => false, + getPortalVersion: binding ? binding.getPortalVersion : () => null, + readColorScheme: binding ? binding.readColorScheme : () => 'no-preference', + readContrast: binding ? binding.readContrast : () => 'no-preference', + readAccentColor: binding ? binding.readAccentColor : () => null, + Settings: binding ? binding.Settings : null, + loadError, +}; diff --git a/fluxer_desktop/native/linux-portals/loader-diagnostics.cjs b/fluxer_desktop/native/linux-portals/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/linux-portals/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/linux-portals/package.json b/fluxer_desktop/native/linux-portals/package.json new file mode 100644 index 0000000..21403dc --- /dev/null +++ b/fluxer_desktop/native/linux-portals/package.json @@ -0,0 +1,27 @@ +{ + "name": "@fluxer/linux-portals", + "version": "0.0.0", + "description": "", + "private": true, + "license": "AGPL-3.0-or-later", + "os": [ + "linux" + ], + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "linux-portals.linux-x64-gnu.node", + "linux-portals.linux-arm64-gnu.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "cargo test --manifest-path Cargo.toml" + } +} diff --git a/fluxer_desktop/native/linux-portals/src/background.rs b/fluxer_desktop/native/linux-portals/src/background.rs new file mode 100644 index 0000000..51efa21 --- /dev/null +++ b/fluxer_desktop/native/linux-portals/src/background.rs @@ -0,0 +1,206 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[cfg(target_os = "linux")] +use std::{collections::HashMap, time::Duration}; + +#[cfg(target_os = "linux")] +use futures_lite::{FutureExt, StreamExt, future}; +#[cfg(target_os = "linux")] +use zbus::{ + MatchRule, MessageStream, Proxy, + message::Type as MessageType, + zvariant::{OwnedObjectPath, Value}, +}; + +#[cfg(target_os = "linux")] +use crate::portal::{REQUEST_INTERFACE, mint_token, request_path}; + +pub const PORTAL_DESTINATION: &str = "org.freedesktop.portal.Desktop"; +pub const PORTAL_PATH: &str = "/org/freedesktop/portal/desktop"; +pub const BACKGROUND_INTERFACE: &str = "org.freedesktop.portal.Background"; + +#[cfg(target_os = "linux")] +pub const REQUEST_TIMEOUT: Duration = Duration::from_secs(5 * 60); + +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct RequestOptions { + pub reason: Option, + pub autostart: bool, + pub commandline: Vec, + pub dbus_activatable: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct RequestResult { + pub response: u32, + pub background: bool, + pub autostart: bool, +} + +impl RequestResult { + pub fn cancelled(&self) -> bool { + self.response != 0 + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum BackgroundError { + DbusError, + PortalTimeout, + InvalidReply, + SendFailed, +} + +impl std::fmt::Display for BackgroundError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match self { + Self::DbusError => "DbusError", + Self::PortalTimeout => "PortalTimeout", + Self::InvalidReply => "InvalidReply", + Self::SendFailed => "SendFailed", + }; + f.write_str(s) + } +} + +#[cfg(target_os = "linux")] +pub fn request_background(options: RequestOptions) -> Result { + future::block_on(request_background_async(options)) +} + +#[cfg(target_os = "linux")] +async fn request_background_async( + options: RequestOptions, +) -> Result { + let conn = zbus::Connection::session() + .await + .map_err(|_| BackgroundError::DbusError)?; + let unique_owned = conn + .unique_name() + .ok_or(BackgroundError::DbusError)? + .to_owned(); + let unique_name = unique_owned.as_str().to_string(); + let handle_token = mint_token("fluxer_bg"); + let expected_path = request_path(&unique_name, &handle_token); + + let rule = MatchRule::builder() + .msg_type(MessageType::Signal) + .interface(REQUEST_INTERFACE) + .map_err(|_| BackgroundError::DbusError)? + .member("Response") + .map_err(|_| BackgroundError::DbusError)? + .path(expected_path.clone()) + .map_err(|_| BackgroundError::DbusError)? + .build(); + let mut stream = MessageStream::for_match_rule(rule, &conn, Some(8)) + .await + .map_err(|_| BackgroundError::DbusError)?; + + send_call(&conn, &handle_token, &options, &expected_path).await?; + + loop { + let timeout = async { + async_io::Timer::after(REQUEST_TIMEOUT).await; + None::> + }; + match stream.next().or(timeout).await { + Some(Ok(message)) => { + if let Some(parsed) = parse_request_response(&message) { + return Ok(parsed); + } + } + Some(Err(_)) => return Err(BackgroundError::DbusError), + None => return Err(BackgroundError::PortalTimeout), + } + } +} + +#[cfg(target_os = "linux")] +fn parse_request_response(message: &zbus::Message) -> Option { + let body = message.body(); + let (response, results): (u32, HashMap) = + body.deserialize().ok()?; + Some(RequestResult { + response, + background: bool_result(&results, "background").unwrap_or(false), + autostart: bool_result(&results, "autostart").unwrap_or(false), + }) +} + +#[cfg(target_os = "linux")] +fn bool_result(results: &HashMap, key: &str) -> Option { + results + .get(key) + .and_then(|value| bool_from_value(crate::kwin::value_of_owned(value))) +} + +#[cfg(target_os = "linux")] +fn bool_from_value(value: &Value<'_>) -> Option { + match value { + Value::Bool(v) => Some(*v), + Value::Value(inner) => bool_from_value(inner), + _ => None, + } +} + +#[cfg(target_os = "linux")] +async fn send_call( + conn: &zbus::Connection, + handle_token: &str, + options: &RequestOptions, + expected_path: &str, +) -> Result<(), BackgroundError> { + let proxy = Proxy::new(conn, PORTAL_DESTINATION, PORTAL_PATH, BACKGROUND_INTERFACE) + .await + .map_err(|_| BackgroundError::DbusError)?; + + let mut vardict: HashMap<&str, Value<'_>> = HashMap::new(); + vardict.insert("handle_token", Value::new(handle_token)); + vardict.insert("autostart", Value::new(options.autostart)); + if let Some(reason) = options.reason.as_deref() { + vardict.insert("reason", Value::new(reason)); + } + if !options.commandline.is_empty() { + let commandline: Vec<&str> = options.commandline.iter().map(String::as_str).collect(); + vardict.insert("commandline", Value::new(commandline)); + } + if options.dbus_activatable { + vardict.insert("dbus-activatable", Value::new(true)); + } + + let reply_path: OwnedObjectPath = proxy + .call("RequestBackground", &("", vardict)) + .await + .map_err(|_| BackgroundError::SendFailed)?; + if !reply_path.as_str().is_empty() && reply_path.as_str() != expected_path { + return Err(BackgroundError::InvalidReply); + } + Ok(()) +} + +#[cfg(not(target_os = "linux"))] +pub fn request_background(_options: RequestOptions) -> Result { + Err(BackgroundError::DbusError) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn default_request_does_not_autostart() { + let opts = RequestOptions::default(); + assert!(!opts.autostart); + assert!(opts.commandline.is_empty()); + } + + #[test] + fn nonzero_response_is_cancelled() { + let result = RequestResult { + response: 1, + background: false, + autostart: false, + }; + assert!(result.cancelled()); + } +} diff --git a/fluxer_desktop/native/linux-portals/src/env.rs b/fluxer_desktop/native/linux-portals/src/env.rs new file mode 100644 index 0000000..d53a96f --- /dev/null +++ b/fluxer_desktop/native/linux-portals/src/env.rs @@ -0,0 +1,360 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DesktopSession { + Kde, + Gnome, + Other, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum DisplayServer { + X11, + Wayland, + WaylandWithXwayland, + Unknown, +} + +pub fn has_dbus_session() -> bool { + has_dbus_session_from( + std::env::var("DBUS_SESSION_BUS_ADDRESS").ok().as_deref(), + std::env::var("XDG_RUNTIME_DIR").ok().as_deref(), + |path| std::path::Path::new(path).exists(), + ) +} + +fn has_dbus_session_from( + bus_address: Option<&str>, + xdg_runtime_dir: Option<&str>, + path_exists: impl Fn(&str) -> bool, +) -> bool { + if bus_address.is_some_and(|v| !v.is_empty()) { + return true; + } + if let Some(dir) = xdg_runtime_dir + && !dir.is_empty() + { + let candidate = format!("{}/bus", dir.trim_end_matches('/')); + if path_exists(&candidate) { + return true; + } + } + false +} + +impl DisplayServer { + pub fn x11_reachable(self) -> bool { + matches!(self, Self::X11 | Self::WaylandWithXwayland) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum WindowPidBackend { + Kwin, + GnomeShellEval, + X11, +} + +pub fn detect_desktop_session() -> DesktopSession { + detect_desktop_session_from( + std::env::var("XDG_CURRENT_DESKTOP").ok().as_deref(), + std::env::var("XDG_SESSION_DESKTOP").ok().as_deref(), + std::env::var("DESKTOP_SESSION").ok().as_deref(), + ) +} + +fn detect_desktop_session_from( + xdg_current_desktop: Option<&str>, + xdg_session_desktop: Option<&str>, + desktop_session: Option<&str>, +) -> DesktopSession { + let candidates = [xdg_current_desktop, xdg_session_desktop, desktop_session]; + for raw in candidates.into_iter().flatten() { + for token in raw.split(':') { + let token = token.trim().to_ascii_lowercase(); + match token.as_str() { + "kde" | "plasma" | "kde-plasma" => return DesktopSession::Kde, + "gnome" | "gnome-classic" | "gnome-xorg" | "ubuntu" | "pop" => { + return DesktopSession::Gnome; + } + "sway" | "hyprland" | "wlroots" | "cosmic" | "wayfire" | "river" | "niri" => { + return DesktopSession::Other; + } + _ => {} + } + } + } + DesktopSession::Other +} + +pub fn detect_display_server() -> DisplayServer { + detect_display_server_from( + std::env::var("XDG_SESSION_TYPE").ok().as_deref(), + std::env::var("DISPLAY").ok().as_deref(), + std::env::var("WAYLAND_DISPLAY").ok().as_deref(), + ) +} + +fn detect_display_server_from( + xdg_session_type: Option<&str>, + display: Option<&str>, + wayland_display: Option<&str>, +) -> DisplayServer { + let has_x11 = display.is_some_and(|v| !v.is_empty()); + let has_wayland = wayland_display.is_some_and(|v| !v.is_empty()); + match (has_x11, has_wayland) { + (true, true) => DisplayServer::WaylandWithXwayland, + (true, false) => DisplayServer::X11, + (false, true) => DisplayServer::Wayland, + (false, false) => match xdg_session_type { + Some("x11") => DisplayServer::X11, + Some("wayland") => DisplayServer::Wayland, + _ => DisplayServer::Unknown, + }, + } +} + +pub fn window_pid_backend_precedence() -> Vec { + backend_precedence_for( + detect_desktop_session(), + detect_display_server(), + has_dbus_session(), + ) +} + +fn backend_precedence_for( + session: DesktopSession, + display: DisplayServer, + dbus_available: bool, +) -> Vec { + let mut out = Vec::with_capacity(3); + match session { + DesktopSession::Kde => { + if dbus_available { + out.push(WindowPidBackend::Kwin); + } + if display.x11_reachable() { + out.push(WindowPidBackend::X11); + } + } + DesktopSession::Gnome => { + if dbus_available { + out.push(WindowPidBackend::GnomeShellEval); + } + if display.x11_reachable() { + out.push(WindowPidBackend::X11); + } + } + DesktopSession::Other => { + if dbus_available { + out.push(WindowPidBackend::Kwin); + out.push(WindowPidBackend::GnomeShellEval); + } + if display.x11_reachable() { + out.push(WindowPidBackend::X11); + } + } + } + out +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn detect_kde_from_xdg_current_desktop() { + assert_eq!( + detect_desktop_session_from(Some("KDE"), None, None), + DesktopSession::Kde + ); + assert_eq!( + detect_desktop_session_from(Some("plasma"), None, None), + DesktopSession::Kde + ); + } + + #[test] + fn detect_gnome_handles_colon_list_and_ubuntu_pop_overrides() { + assert_eq!( + detect_desktop_session_from(Some("ubuntu:GNOME"), None, None), + DesktopSession::Gnome + ); + assert_eq!( + detect_desktop_session_from(Some("pop:GNOME"), None, None), + DesktopSession::Gnome + ); + } + + #[test] + fn detect_other_for_xfce_and_unset() { + assert_eq!( + detect_desktop_session_from(Some("XFCE"), None, None), + DesktopSession::Other + ); + assert_eq!( + detect_desktop_session_from(None, None, None), + DesktopSession::Other + ); + } + + #[test] + fn precedence_kde_session_tries_kwin_first_then_x11() { + assert_eq!( + backend_precedence_for(DesktopSession::Kde, DisplayServer::X11, true), + vec![WindowPidBackend::Kwin, WindowPidBackend::X11] + ); + assert_eq!( + backend_precedence_for(DesktopSession::Kde, DisplayServer::Wayland, true), + vec![WindowPidBackend::Kwin] + ); + } + + #[test] + fn precedence_gnome_session_tries_gnome_shell_first_then_x11() { + assert_eq!( + backend_precedence_for( + DesktopSession::Gnome, + DisplayServer::WaylandWithXwayland, + true + ), + vec![WindowPidBackend::GnomeShellEval, WindowPidBackend::X11] + ); + } + + #[test] + fn precedence_unknown_de_tries_all_three_in_order() { + assert_eq!( + backend_precedence_for(DesktopSession::Other, DisplayServer::X11, true), + vec![ + WindowPidBackend::Kwin, + WindowPidBackend::GnomeShellEval, + WindowPidBackend::X11, + ] + ); + } + + #[test] + fn precedence_unknown_de_pure_wayland_skips_x11() { + assert_eq!( + backend_precedence_for(DesktopSession::Other, DisplayServer::Wayland, true), + vec![WindowPidBackend::Kwin, WindowPidBackend::GnomeShellEval] + ); + } + + #[test] + fn precedence_without_dbus_skips_all_dbus_backends() { + assert_eq!( + backend_precedence_for(DesktopSession::Kde, DisplayServer::X11, false), + vec![WindowPidBackend::X11] + ); + assert_eq!( + backend_precedence_for( + DesktopSession::Gnome, + DisplayServer::WaylandWithXwayland, + false + ), + vec![WindowPidBackend::X11] + ); + assert_eq!( + backend_precedence_for(DesktopSession::Other, DisplayServer::X11, false), + vec![WindowPidBackend::X11] + ); + } + + #[test] + fn precedence_headless_container_returns_empty_list() { + assert_eq!( + backend_precedence_for(DesktopSession::Other, DisplayServer::Unknown, false), + Vec::::new() + ); + assert_eq!( + backend_precedence_for(DesktopSession::Gnome, DisplayServer::Wayland, false), + Vec::::new() + ); + } + + #[test] + fn wlroots_compositors_bucket_as_other() { + for token in [ + "sway", "Hyprland", "wlroots", "cosmic", "wayfire", "river", "niri", + ] { + assert_eq!( + detect_desktop_session_from(Some(token), None, None), + DesktopSession::Other, + "expected {token} to bucket as Other", + ); + } + } + + #[test] + fn wlroots_compositor_precedence_skips_kwin_and_gnome_shell_when_no_dbus() { + assert_eq!( + backend_precedence_for( + detect_desktop_session_from(Some("sway"), None, None), + DisplayServer::Wayland, + false, + ), + Vec::::new() + ); + assert_eq!( + backend_precedence_for( + detect_desktop_session_from(Some("Hyprland"), None, None), + DisplayServer::WaylandWithXwayland, + true, + ), + vec![ + WindowPidBackend::Kwin, + WindowPidBackend::GnomeShellEval, + WindowPidBackend::X11, + ] + ); + } + + #[test] + fn dbus_session_detected_via_bus_address() { + assert!(has_dbus_session_from( + Some("unix:path=/run/user/1000/bus"), + None, + |_| false + )); + } + + #[test] + fn dbus_session_detected_via_xdg_runtime_dir_socket() { + assert!(has_dbus_session_from( + None, + Some("/run/user/1000"), + |path| path == "/run/user/1000/bus" + )); + assert!(has_dbus_session_from( + None, + Some("/run/user/1000/"), + |path| path == "/run/user/1000/bus" + )); + } + + #[test] + fn dbus_session_absent_when_neither_var_set() { + assert!(!has_dbus_session_from(None, None, |_| false)); + assert!(!has_dbus_session_from(Some(""), Some(""), |_| true)); + } + + #[test] + fn dbus_session_absent_when_runtime_dir_has_no_bus_socket() { + assert!(!has_dbus_session_from(None, Some("/tmp/xdg"), |_| false)); + } + + #[test] + fn display_server_xwayland_counts_as_x11_reachable() { + let ds = detect_display_server_from(Some("wayland"), Some(":0"), Some("wayland-0")); + assert_eq!(ds, DisplayServer::WaylandWithXwayland); + assert!(ds.x11_reachable()); + } + + #[test] + fn display_server_pure_wayland_blocks_x11() { + let ds = detect_display_server_from(Some("wayland"), None, Some("wayland-0")); + assert!(!ds.x11_reachable()); + } +} diff --git a/fluxer_desktop/native/linux-portals/src/filechooser.rs b/fluxer_desktop/native/linux-portals/src/filechooser.rs new file mode 100644 index 0000000..4a5c387 --- /dev/null +++ b/fluxer_desktop/native/linux-portals/src/filechooser.rs @@ -0,0 +1,334 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[cfg(target_os = "linux")] +use std::{collections::HashMap, sync::mpsc, time::Duration}; + +#[cfg(target_os = "linux")] +use futures_lite::{FutureExt, StreamExt, future}; +#[cfg(target_os = "linux")] +use zbus::{ + MatchRule, MessageStream, + blocking::{Connection as BlockingConnection, Proxy as BlockingProxy}, + message::Type as MessageType, + zvariant::{OwnedObjectPath, Value}, +}; + +#[cfg(target_os = "linux")] +use crate::portal::{REQUEST_INTERFACE, mint_token, request_path}; + +pub const PORTAL_DESTINATION: &str = "org.freedesktop.portal.Desktop"; +pub const PORTAL_PATH: &str = "/org/freedesktop/portal/desktop"; +pub const FILE_CHOOSER_INTERFACE: &str = "org.freedesktop.portal.FileChooser"; + +#[cfg(target_os = "linux")] +pub const REQUEST_TIMEOUT: Duration = Duration::from_secs(5 * 60); +#[cfg(target_os = "linux")] +pub const SIGNAL_POLL_INTERVAL: Duration = Duration::from_millis(200); + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Mode { + Open, + Save, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FilterRule { + pub kind: u32, + pub pattern: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct Filter { + pub name: String, + pub rules: Vec, +} + +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct Options { + pub parent_window: String, + pub title: String, + pub accept_label: Option, + pub modal: bool, + pub multiple: bool, + pub directory: bool, + pub current_folder: Option, + pub current_name: Option, + pub current_file: Option, + pub filters: Vec, + pub current_filter: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct FileChooserResult { + pub cancelled: bool, + pub uris: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum FileChooserError { + DbusError, + PortalTimeout, + InvalidReply, + SendFailed, +} + +impl std::fmt::Display for FileChooserError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match self { + Self::DbusError => "DbusError", + Self::PortalTimeout => "PortalTimeout", + Self::InvalidReply => "InvalidReply", + Self::SendFailed => "SendFailed", + }; + f.write_str(s) + } +} + +#[cfg(target_os = "linux")] +pub fn invoke(mode: Mode, options: Options) -> Result { + let conn = zbus::blocking::connection::Builder::session() + .map_err(|_| FileChooserError::DbusError)? + .method_timeout(Duration::from_secs(30)) + .build() + .map_err(|_| FileChooserError::DbusError)?; + let unique_owned = conn + .unique_name() + .ok_or(FileChooserError::DbusError)? + .to_owned(); + let unique_name = unique_owned.as_str().to_string(); + + let token_prefix = match mode { + Mode::Open => "fluxer_fc_open", + Mode::Save => "fluxer_fc_save", + }; + let handle_token = mint_token(token_prefix); + let expected_path = request_path(&unique_name, &handle_token); + + let (tx, rx) = mpsc::sync_channel::(1); + let stop_flag = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)); + let stop_for_thread = stop_flag.clone(); + let expected_for_thread = expected_path.clone(); + let listener = std::thread::Builder::new() + .name("fluxer-linux-portals-fc".to_string()) + .spawn(move || { + response_listener(&expected_for_thread, tx, stop_for_thread); + }) + .map_err(|_| FileChooserError::DbusError)?; + + let send_result = send_call(&conn, mode, &handle_token, &options, &expected_path); + + let result = match send_result { + Ok(()) => match rx.recv_timeout(REQUEST_TIMEOUT) { + Ok(response) => { + if response.code != 0 { + Ok(FileChooserResult { + cancelled: true, + uris: vec![], + }) + } else { + Ok(FileChooserResult { + cancelled: false, + uris: response.uris, + }) + } + } + Err(_) => Err(FileChooserError::PortalTimeout), + }, + Err(err) => Err(err), + }; + + stop_flag.store(true, std::sync::atomic::Ordering::Release); + let _ = listener.join(); + result +} + +#[cfg(target_os = "linux")] +struct FileChooserResponse { + code: u32, + uris: Vec, +} + +#[cfg(target_os = "linux")] +fn response_listener( + expected_path: &str, + tx: mpsc::SyncSender, + stop: std::sync::Arc, +) { + let setup = future::block_on(async { + let conn = zbus::Connection::session().await?; + let rule = MatchRule::builder() + .msg_type(MessageType::Signal) + .interface(REQUEST_INTERFACE)? + .member("Response")? + .path(expected_path.to_string())? + .build(); + let stream = MessageStream::for_match_rule(rule, &conn, Some(8)).await?; + zbus::Result::Ok((conn, stream)) + }); + let (_conn, mut stream) = match setup { + Ok(parts) => parts, + Err(_) => return, + }; + while !stop.load(std::sync::atomic::Ordering::Acquire) { + let timeout = async { + async_io::Timer::after(SIGNAL_POLL_INTERVAL).await; + None::> + }; + match future::block_on(stream.next().or(timeout)) { + Some(Ok(message)) => { + if let Some(parsed) = parse_filechooser_response(&message) { + let _ = tx.send(parsed); + return; + } + } + Some(Err(_)) => return, + None => {} + } + } +} + +#[cfg(target_os = "linux")] +fn parse_filechooser_response(message: &zbus::Message) -> Option { + let body = message.body(); + let (code, results): (u32, HashMap) = + body.deserialize().ok()?; + let mut uris: Vec = Vec::new(); + if let Some(v) = results.get("uris") { + let val = crate::kwin::value_of_owned(v); + if let Value::Array(arr) = val { + for element in arr.iter() { + let inner: &Value<'_> = match element { + Value::Value(b) => b.as_ref(), + other => other, + }; + if let Value::Str(s) = inner { + uris.push(s.as_str().to_string()); + } + } + } + } + Some(FileChooserResponse { code, uris }) +} + +#[cfg(target_os = "linux")] +fn send_call( + conn: &BlockingConnection, + mode: Mode, + handle_token: &str, + options: &Options, + expected_path: &str, +) -> Result<(), FileChooserError> { + let proxy = BlockingProxy::new( + conn, + PORTAL_DESTINATION, + PORTAL_PATH, + FILE_CHOOSER_INTERFACE, + ) + .map_err(|_| FileChooserError::DbusError)?; + let member = match mode { + Mode::Open => "OpenFile", + Mode::Save => "SaveFile", + }; + + let mut vardict: HashMap<&str, Value<'_>> = HashMap::new(); + vardict.insert("handle_token", Value::new(handle_token)); + vardict.insert("modal", Value::new(options.modal)); + vardict.insert("multiple", Value::new(options.multiple)); + if matches!(mode, Mode::Open) && options.directory { + vardict.insert("directory", Value::new(true)); + } + if let Some(label) = options.accept_label.as_deref() { + vardict.insert("accept_label", Value::new(label)); + } + if !options.filters.is_empty() { + vardict.insert("filters", Value::new(serialize_filters(&options.filters))); + } + if let Some(cf) = options.current_filter.as_ref() { + vardict.insert("current_filter", Value::new(serialize_filter(cf))); + } + if let Some(folder) = options.current_folder.as_deref() { + vardict.insert("current_folder", Value::new(folder.as_bytes())); + } + if matches!(mode, Mode::Save) + && let Some(name) = options.current_name.as_deref() + { + vardict.insert("current_name", Value::new(name)); + } + if matches!(mode, Mode::Save) + && let Some(file) = options.current_file.as_deref() + { + vardict.insert("current_file", Value::new(file.as_bytes())); + } + + let reply_path: OwnedObjectPath = proxy + .call( + member, + &( + options.parent_window.as_str(), + options.title.as_str(), + vardict, + ), + ) + .map_err(|_| FileChooserError::SendFailed)?; + if !reply_path.as_str().is_empty() && reply_path.as_str() != expected_path { + return Err(FileChooserError::InvalidReply); + } + Ok(()) +} + +#[cfg(target_os = "linux")] +fn serialize_filters(filters: &[Filter]) -> Vec<(&str, Vec<(u32, &str)>)> { + filters + .iter() + .map(|f| { + let rules: Vec<(u32, &str)> = f + .rules + .iter() + .map(|r| (r.kind, r.pattern.as_str())) + .collect(); + (f.name.as_str(), rules) + }) + .collect() +} + +#[cfg(target_os = "linux")] +fn serialize_filter(filter: &Filter) -> (&str, Vec<(u32, &str)>) { + let rules: Vec<(u32, &str)> = filter + .rules + .iter() + .map(|r| (r.kind, r.pattern.as_str())) + .collect(); + (filter.name.as_str(), rules) +} + +#[cfg(not(target_os = "linux"))] +pub fn invoke(_mode: Mode, _options: Options) -> Result { + Err(FileChooserError::DbusError) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn options_default_is_open_safe() { + let opts = Options::default(); + assert!(!opts.directory); + assert!(!opts.multiple); + assert!(opts.parent_window.is_empty()); + } + + #[test] + fn filter_rule_kinds_match_typescript_union() { + let glob = FilterRule { + kind: 0, + pattern: "*.png".into(), + }; + let mime = FilterRule { + kind: 1, + pattern: "image/png".into(), + }; + assert_eq!(glob.kind, 0); + assert_eq!(mime.kind, 1); + } +} diff --git a/fluxer_desktop/native/linux-portals/src/global_shortcuts.rs b/fluxer_desktop/native/linux-portals/src/global_shortcuts.rs new file mode 100644 index 0000000..6876f9f --- /dev/null +++ b/fluxer_desktop/native/linux-portals/src/global_shortcuts.rs @@ -0,0 +1,646 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[cfg(target_os = "linux")] +use std::{ + collections::{HashMap, HashSet}, + sync::{ + Arc, Mutex, + atomic::{AtomicBool, Ordering}, + mpsc, + }, + thread::{self, JoinHandle}, + time::Duration, +}; + +#[cfg(target_os = "linux")] +use futures_lite::{FutureExt, StreamExt, future}; +#[cfg(target_os = "linux")] +use zbus::{ + MatchRule, MessageStream, Proxy, + message::Type as MessageType, + zvariant::{OwnedObjectPath, OwnedValue, Value}, +}; + +#[cfg(target_os = "linux")] +use crate::portal::{REQUEST_INTERFACE, mint_token, request_path}; + +pub const PORTAL_DESTINATION: &str = "org.freedesktop.portal.Desktop"; +pub const PORTAL_PATH: &str = "/org/freedesktop/portal/desktop"; +pub const GLOBAL_SHORTCUTS_INTERFACE: &str = "org.freedesktop.portal.GlobalShortcuts"; +pub const SESSION_INTERFACE: &str = "org.freedesktop.portal.Session"; + +#[cfg(target_os = "linux")] +pub const REQUEST_TIMEOUT: Duration = Duration::from_secs(5 * 60); +#[cfg(target_os = "linux")] +pub const SIGNAL_POLL_INTERVAL: Duration = Duration::from_millis(200); +#[cfg(target_os = "linux")] +pub const SIGNAL_THREAD_START_TIMEOUT: Duration = Duration::from_secs(5); + +#[cfg(target_os = "linux")] +type ShortcutProperties = HashMap; +#[cfg(target_os = "linux")] +type ShortcutsChangedBody = (OwnedObjectPath, Vec<(String, ShortcutProperties)>); + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ShortcutEntry { + pub id: String, + pub description: String, + pub preferred_trigger: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct BoundShortcut { + pub id: String, + pub description: Option, + pub trigger_description: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ConfigureResult { + pub action: String, + pub shortcuts: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ShortcutEvent { + Activated { id: String }, + Deactivated { id: String }, + ShortcutsChanged { shortcuts: Vec }, + Closed, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum GlobalShortcutsError { + DbusError, + PortalTimeout, + InvalidReply, + SendFailed, + Cancelled, + ThreadStartFailed, + LockPoisoned, +} + +impl std::fmt::Display for GlobalShortcutsError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match self { + Self::DbusError => "DbusError", + Self::PortalTimeout => "PortalTimeout", + Self::InvalidReply => "InvalidReply", + Self::SendFailed => "SendFailed", + Self::Cancelled => "Cancelled", + Self::ThreadStartFailed => "ThreadStartFailed", + Self::LockPoisoned => "LockPoisoned", + }; + f.write_str(s) + } +} + +#[cfg(target_os = "linux")] +type ShortcutCallback = Arc; + +#[cfg(target_os = "linux")] +pub struct Subscription { + stop_flag: Arc, + thread: Mutex>>, +} + +#[cfg(target_os = "linux")] +impl Subscription { + pub fn configure( + entries: Vec, + callback: ShortcutCallback, + ) -> Result<(Self, ConfigureResult), GlobalShortcutsError> { + let stop_flag = Arc::new(AtomicBool::new(false)); + let stop_for_thread = stop_flag.clone(); + let (ready_tx, ready_rx) = + mpsc::sync_channel::>(1); + let thread = thread::Builder::new() + .name("fluxer-linux-portals-global-shortcuts".to_string()) + .spawn(move || { + let setup = future::block_on(async { + let conn = zbus::Connection::session() + .await + .map_err(|_| GlobalShortcutsError::DbusError)?; + let rule = MatchRule::builder().msg_type(MessageType::Signal).build(); + let stream = MessageStream::for_match_rule(rule, &conn, Some(64)) + .await + .map_err(|_| GlobalShortcutsError::DbusError)?; + let ready = configure_session(&conn, &entries).await?; + Ok::<_, GlobalShortcutsError>((conn, stream, ready)) + }); + let (conn, mut stream, ready) = match setup { + Ok(parts) => parts, + Err(err) => { + let _ = ready_tx.send(Err(err)); + return; + } + }; + let session_handle = ready.session_handle.clone(); + if ready_tx.send(Ok(ready)).is_err() { + let _ = future::block_on(close_session(&conn, &session_handle)); + return; + } + while !stop_for_thread.load(Ordering::Acquire) { + let timeout = async { + async_io::Timer::after(SIGNAL_POLL_INTERVAL).await; + None::> + }; + match future::block_on(stream.next().or(timeout)) { + Some(Ok(message)) => { + if let Some(event) = parse_signal(&message, &session_handle) { + let closed = matches!(event, ShortcutEvent::Closed); + callback(event); + if closed { + return; + } + } + } + Some(Err(_)) => break, + None => {} + } + } + let _ = future::block_on(close_session(&conn, &session_handle)); + }) + .map_err(|_| GlobalShortcutsError::ThreadStartFailed)?; + + match ready_rx.recv_timeout(SIGNAL_THREAD_START_TIMEOUT) { + Ok(Ok(ready)) => Ok(( + Self { + stop_flag, + thread: Mutex::new(Some(thread)), + }, + ready.result, + )), + Ok(Err(err)) => { + let _ = thread.join(); + Err(err) + } + Err(err) => { + stop_flag.store(true, Ordering::Release); + if matches!(err, mpsc::RecvTimeoutError::Disconnected) { + let _ = thread.join(); + } + Err(GlobalShortcutsError::ThreadStartFailed) + } + } + } + + pub fn close(&self) { + self.stop_flag.store(true, Ordering::Release); + if let Ok(mut thread) = self.thread.lock() + && let Some(t) = thread.take() + { + let _ = t.join(); + } + } +} + +#[cfg(target_os = "linux")] +impl Drop for Subscription { + fn drop(&mut self) { + self.close(); + } +} + +#[cfg(target_os = "linux")] +struct ConfigureReady { + session_handle: String, + result: ConfigureResult, +} + +#[cfg(target_os = "linux")] +struct PortalResponse { + code: u32, + results: HashMap, +} + +#[cfg(target_os = "linux")] +async fn configure_session( + conn: &zbus::Connection, + entries: &[ShortcutEntry], +) -> Result { + let session_handle = create_session(conn).await?; + let persisted = list_shortcuts(conn, &session_handle) + .await + .unwrap_or_default(); + if shortcut_ids_match(entries, &persisted) { + return Ok(ConfigureReady { + session_handle, + result: ConfigureResult { + action: "listed".to_string(), + shortcuts: persisted, + }, + }); + } + let bound = bind_shortcuts(conn, &session_handle, entries).await?; + Ok(ConfigureReady { + session_handle, + result: bound, + }) +} + +#[cfg(target_os = "linux")] +fn shortcut_ids_match(entries: &[ShortcutEntry], persisted: &[BoundShortcut]) -> bool { + if entries.is_empty() || persisted.is_empty() { + return false; + } + let requested: HashSet<&str> = entries.iter().map(|entry| entry.id.as_str()).collect(); + let existing: HashSet<&str> = persisted + .iter() + .map(|shortcut| shortcut.id.as_str()) + .collect(); + requested == existing +} + +#[cfg(target_os = "linux")] +async fn create_session(conn: &zbus::Connection) -> Result { + let handle_token = mint_token("fluxer_gs_create"); + let session_handle_token = mint_token("fluxer_gs_session"); + let mut stream = request_stream(conn, &handle_token).await?; + let proxy = global_shortcuts_proxy(conn).await?; + let mut options: HashMap<&str, Value<'_>> = HashMap::new(); + options.insert("handle_token", Value::new(handle_token.as_str())); + options.insert( + "session_handle_token", + Value::new(session_handle_token.as_str()), + ); + let _reply_path: OwnedObjectPath = proxy + .call("CreateSession", &(options,)) + .await + .map_err(|_| GlobalShortcutsError::SendFailed)?; + let response = wait_for_response(&mut stream).await?; + if response.code != 0 { + return Err(GlobalShortcutsError::Cancelled); + } + response + .results + .get("session_handle") + .and_then(|value| string_or_object_path(crate::kwin::value_of_owned(value))) + .ok_or(GlobalShortcutsError::InvalidReply) +} + +#[cfg(target_os = "linux")] +async fn list_shortcuts( + conn: &zbus::Connection, + session_handle: &str, +) -> Result, GlobalShortcutsError> { + let session_path = owned_path(session_handle)?; + let handle_token = mint_token("fluxer_gs_list"); + let mut stream = request_stream(conn, &handle_token).await?; + let proxy = global_shortcuts_proxy(conn).await?; + let mut options: HashMap<&str, Value<'_>> = HashMap::new(); + options.insert("handle_token", Value::new(handle_token.as_str())); + let _reply_path: OwnedObjectPath = proxy + .call("ListShortcuts", &(&session_path, options)) + .await + .map_err(|_| GlobalShortcutsError::SendFailed)?; + let response = wait_for_response(&mut stream).await?; + if response.code != 0 { + return Err(GlobalShortcutsError::Cancelled); + } + Ok(shortcuts_from_results(&response.results)) +} + +#[cfg(target_os = "linux")] +async fn bind_shortcuts( + conn: &zbus::Connection, + session_handle: &str, + entries: &[ShortcutEntry], +) -> Result { + let session_path = owned_path(session_handle)?; + let handle_token = mint_token("fluxer_gs_bind"); + let mut stream = request_stream(conn, &handle_token).await?; + let proxy = global_shortcuts_proxy(conn).await?; + let shortcuts = serialize_shortcuts(entries); + let mut options: HashMap<&str, Value<'_>> = HashMap::new(); + options.insert("handle_token", Value::new(handle_token.as_str())); + let _reply_path: OwnedObjectPath = proxy + .call("BindShortcuts", &(&session_path, shortcuts, "", options)) + .await + .map_err(|_| GlobalShortcutsError::SendFailed)?; + let response = wait_for_response(&mut stream).await?; + if response.code != 0 { + return Ok(ConfigureResult { + action: "cancelled".to_string(), + shortcuts: Vec::new(), + }); + } + Ok(ConfigureResult { + action: "bound".to_string(), + shortcuts: shortcuts_from_results(&response.results), + }) +} + +#[cfg(target_os = "linux")] +async fn global_shortcuts_proxy( + conn: &zbus::Connection, +) -> Result, GlobalShortcutsError> { + Proxy::new( + conn, + PORTAL_DESTINATION, + PORTAL_PATH, + GLOBAL_SHORTCUTS_INTERFACE, + ) + .await + .map_err(|_| GlobalShortcutsError::DbusError) +} + +async fn request_stream( + conn: &zbus::Connection, + handle_token: &str, +) -> Result { + let unique_owned = conn + .unique_name() + .ok_or(GlobalShortcutsError::DbusError)? + .to_owned(); + let unique_name = unique_owned.as_str().to_string(); + let expected_path = request_path(&unique_name, handle_token); + let rule = MatchRule::builder() + .msg_type(MessageType::Signal) + .interface(REQUEST_INTERFACE) + .map_err(|_| GlobalShortcutsError::DbusError)? + .member("Response") + .map_err(|_| GlobalShortcutsError::DbusError)? + .path(expected_path.clone()) + .map_err(|_| GlobalShortcutsError::DbusError)? + .build(); + MessageStream::for_match_rule(rule, conn, Some(8)) + .await + .map_err(|_| GlobalShortcutsError::DbusError) +} + +async fn wait_for_response( + stream: &mut MessageStream, +) -> Result { + loop { + let timeout = async { + async_io::Timer::after(REQUEST_TIMEOUT).await; + None::> + }; + match stream.next().or(timeout).await { + Some(Ok(message)) => { + if let Some(parsed) = parse_request_response(&message) { + return Ok(parsed); + } + } + Some(Err(_)) => return Err(GlobalShortcutsError::DbusError), + None => return Err(GlobalShortcutsError::PortalTimeout), + } + } +} + +#[cfg(target_os = "linux")] +fn parse_request_response(message: &zbus::Message) -> Option { + let body = message.body(); + let (code, results): (u32, HashMap) = body.deserialize().ok()?; + Some(PortalResponse { code, results }) +} + +#[cfg(target_os = "linux")] +fn serialize_shortcuts(entries: &[ShortcutEntry]) -> Vec<(&str, HashMap<&str, Value<'_>>)> { + entries + .iter() + .map(|entry| { + let mut options: HashMap<&str, Value<'_>> = HashMap::new(); + options.insert("description", Value::new(entry.description.as_str())); + if let Some(trigger) = entry.preferred_trigger.as_deref() { + options.insert("preferred_trigger", Value::new(trigger)); + } + (entry.id.as_str(), options) + }) + .collect() +} + +#[cfg(target_os = "linux")] +fn shortcuts_from_results(results: &HashMap) -> Vec { + results + .get("shortcuts") + .and_then(|value| shortcuts_from_value(crate::kwin::value_of_owned(value))) + .unwrap_or_default() +} + +#[cfg(target_os = "linux")] +fn shortcuts_from_value(value: &Value<'_>) -> Option> { + let inner = unbox_value(value); + let Value::Array(array) = inner else { + return None; + }; + let mut shortcuts = Vec::new(); + for value in array.inner() { + if let Some(shortcut) = bound_shortcut_from_value(value) { + shortcuts.push(shortcut); + } + } + Some(shortcuts) +} + +#[cfg(target_os = "linux")] +fn bound_shortcut_from_value(value: &Value<'_>) -> Option { + let Value::Structure(structure) = unbox_value(value) else { + return None; + }; + let fields = structure.fields(); + if fields.len() < 2 { + return None; + } + let id = string_or_object_path(&fields[0])?; + let dict = match unbox_value(&fields[1]) { + Value::Dict(dict) => Some(dict), + _ => None, + }; + Some(BoundShortcut { + id, + description: dict.and_then(|d| dict_string(d, "description")), + trigger_description: dict.and_then(|d| dict_string(d, "trigger_description")), + }) +} + +#[cfg(target_os = "linux")] +fn dict_string(dict: &zbus::zvariant::Dict<'_, '_>, key: &str) -> Option { + dict.iter().find_map(|(k, v)| { + if string_or_object_path(k).as_deref() == Some(key) { + string_or_object_path(unbox_value(v)) + } else { + None + } + }) +} + +#[cfg(target_os = "linux")] +fn parse_signal(message: &zbus::Message, session_handle: &str) -> Option { + let header = message.header(); + let interface = header.interface()?.as_str(); + let member = header.member()?.as_str(); + match (interface, member) { + (GLOBAL_SHORTCUTS_INTERFACE, "Activated") => { + let (session, id, _timestamp, _options): ( + OwnedObjectPath, + String, + u64, + HashMap, + ) = message.body().deserialize().ok()?; + if session.as_str() == session_handle { + Some(ShortcutEvent::Activated { id }) + } else { + None + } + } + (GLOBAL_SHORTCUTS_INTERFACE, "Deactivated") => { + let (session, id, _timestamp, _options): ( + OwnedObjectPath, + String, + u64, + HashMap, + ) = message.body().deserialize().ok()?; + if session.as_str() == session_handle { + Some(ShortcutEvent::Deactivated { id }) + } else { + None + } + } + (GLOBAL_SHORTCUTS_INTERFACE, "ShortcutsChanged") => { + let (session, shortcuts): ShortcutsChangedBody = message.body().deserialize().ok()?; + if session.as_str() != session_handle { + return None; + } + Some(ShortcutEvent::ShortcutsChanged { + shortcuts: shortcuts + .into_iter() + .map(|(id, properties)| bound_shortcut_from_parts(id, &properties)) + .collect(), + }) + } + (SESSION_INTERFACE, "Closed") => { + if header.path()?.as_str() == session_handle { + Some(ShortcutEvent::Closed) + } else { + None + } + } + _ => None, + } +} + +#[cfg(target_os = "linux")] +fn bound_shortcut_from_parts( + id: String, + properties: &HashMap, +) -> BoundShortcut { + BoundShortcut { + id, + description: properties + .get("description") + .and_then(|value| string_or_object_path(crate::kwin::value_of_owned(value))), + trigger_description: properties + .get("trigger_description") + .and_then(|value| string_or_object_path(crate::kwin::value_of_owned(value))), + } +} + +#[cfg(target_os = "linux")] +async fn close_session( + conn: &zbus::Connection, + session_handle: &str, +) -> Result<(), GlobalShortcutsError> { + let proxy = Proxy::new(conn, PORTAL_DESTINATION, session_handle, SESSION_INTERFACE) + .await + .map_err(|_| GlobalShortcutsError::DbusError)?; + proxy + .call::<_, _, ()>("Close", &()) + .await + .map_err(|_| GlobalShortcutsError::SendFailed) +} + +#[cfg(target_os = "linux")] +fn owned_path(path: &str) -> Result { + OwnedObjectPath::try_from(path.to_string()).map_err(|_| GlobalShortcutsError::InvalidReply) +} + +#[cfg(target_os = "linux")] +fn unbox_value<'a>(value: &'a Value<'a>) -> &'a Value<'a> { + match value { + Value::Value(inner) => unbox_value(inner), + other => other, + } +} + +#[cfg(target_os = "linux")] +fn string_or_object_path(value: &Value<'_>) -> Option { + match unbox_value(value) { + Value::Str(v) => Some(v.as_str().to_string()), + Value::ObjectPath(v) => Some(v.as_str().to_string()), + _ => None, + } +} + +#[cfg(target_os = "linux")] +pub fn get_portal_version() -> Option { + let conn = zbus::blocking::connection::Builder::session() + .ok()? + .method_timeout(Duration::from_millis(1_500)) + .build() + .ok()?; + let proxy = zbus::blocking::Proxy::new( + &conn, + PORTAL_DESTINATION, + PORTAL_PATH, + GLOBAL_SHORTCUTS_INTERFACE, + ) + .ok()?; + proxy.get_property("version").ok() +} + +#[cfg(target_os = "linux")] +pub fn is_available() -> bool { + get_portal_version().is_some() +} + +#[cfg(not(target_os = "linux"))] +pub struct Subscription; + +#[cfg(not(target_os = "linux"))] +impl Subscription { + pub fn configure( + _entries: Vec, + _callback: Arc, + ) -> Result<(Self, ConfigureResult), GlobalShortcutsError> { + Err(GlobalShortcutsError::DbusError) + } + + pub fn close(&self) {} +} + +#[cfg(not(target_os = "linux"))] +pub fn get_portal_version() -> Option { + None +} + +#[cfg(not(target_os = "linux"))] +pub fn is_available() -> bool { + false +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn shortcut_id_match_requires_same_ids() { + let entries = vec![ShortcutEntry { + id: "one".into(), + description: "One".into(), + preferred_trigger: Some("CTRL+o".into()), + }]; + let persisted = vec![BoundShortcut { + id: "one".into(), + description: None, + trigger_description: None, + }]; + assert!(shortcut_ids_match(&entries, &persisted)); + } + + #[test] + fn shortcut_id_match_rejects_empty() { + assert!(!shortcut_ids_match(&[], &[])); + } +} diff --git a/fluxer_desktop/native/linux-portals/src/gnome_shell.rs b/fluxer_desktop/native/linux-portals/src/gnome_shell.rs new file mode 100644 index 0000000..12df94a --- /dev/null +++ b/fluxer_desktop/native/linux-portals/src/gnome_shell.rs @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[cfg(target_os = "linux")] +use std::time::Duration; + +#[cfg(target_os = "linux")] +pub const GNOME_SHELL_DESTINATION: &str = "org.gnome.Shell"; +#[cfg(target_os = "linux")] +pub const GNOME_SHELL_PATH: &str = "/org/gnome/Shell"; +#[cfg(target_os = "linux")] +pub const GNOME_SHELL_INTERFACE: &str = "org.gnome.Shell"; +#[cfg(target_os = "linux")] +pub const GNOME_SHELL_EVAL_TIMEOUT: Duration = Duration::from_millis(1_500); + +pub fn is_safe_shell_eval_token(token: &str) -> bool { + if token.is_empty() || token.len() > 128 { + return false; + } + token + .bytes() + .all(|ch| ch.is_ascii_alphanumeric() || ch == b'_') +} + +pub fn is_gnome_eval_disabled_via_env() -> bool { + match std::env::var("FLUXER_PORTALS_GNOME_EVAL") { + Ok(v) => { + let lower = v.to_ascii_lowercase(); + lower == "0" || lower == "false" || lower == "no" + } + Err(_) => false, + } +} + +pub fn parse_shell_eval_pid_payload(payload: &str) -> Option { + let trimmed = payload.trim(); + if trimmed.is_empty() { + return None; + } + let stripped = trimmed + .trim_start_matches(['[', ' ', '\t']) + .trim_end_matches([']', ' ', '\t', ',']) + .trim(); + if stripped.is_empty() { + return None; + } + let mut end = 0; + for (i, ch) in stripped.char_indices() { + if ch.is_ascii_digit() { + end = i + ch.len_utf8(); + } else { + break; + } + } + if end == 0 { + return None; + } + let number: u64 = stripped[..end].parse().ok()?; + if number == 0 || number > u32::MAX as u64 { + return None; + } + Some(number as u32) +} + +pub fn build_window_pid_script(token: &str) -> String { + format!( + "global.get_window_actors().map(a=>a.meta_window).filter(w=>w.get_id&&w.get_id().toString()===\"{token}\").map(w=>w.get_pid())[0]" + ) +} + +#[cfg(target_os = "linux")] +pub fn resolve_gnome_shell_window_pid(token: &str) -> Result, String> { + if is_gnome_eval_disabled_via_env() { + return Ok(None); + } + if !is_safe_shell_eval_token(token) { + return Err("resolveWindowPid: token failed validation".into()); + } + let conn = zbus::blocking::connection::Builder::session() + .map_err(|err| format!("openSessionBus failed: {err}"))? + .method_timeout(GNOME_SHELL_EVAL_TIMEOUT) + .build() + .map_err(|err| format!("openSessionBus failed: {err}"))?; + let proxy = zbus::blocking::Proxy::new( + &conn, + GNOME_SHELL_DESTINATION, + GNOME_SHELL_PATH, + GNOME_SHELL_INTERFACE, + ) + .map_err(|err| format!("shell Eval failed: {err}"))?; + let script = build_window_pid_script(token); + let (success, payload): (bool, String) = proxy + .call("Eval", &(script.as_str(),)) + .map_err(|err| format!("shell Eval failed: {err}"))?; + if !success { + return Ok(None); + } + Ok(parse_shell_eval_pid_payload(&payload)) +} + +#[cfg(not(target_os = "linux"))] +pub fn resolve_gnome_shell_window_pid(_token: &str) -> Result, String> { + Err("not supported on this platform".into()) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn is_safe_shell_eval_token_accepts_simple() { + assert!(is_safe_shell_eval_token("abc")); + assert!(is_safe_shell_eval_token("Window_123")); + } + + #[test] + fn is_safe_shell_eval_token_rejects_metacharacters() { + assert!(!is_safe_shell_eval_token("")); + assert!(!is_safe_shell_eval_token("\"; system('rm -rf'); \"")); + assert!(!is_safe_shell_eval_token("abc def")); + assert!(!is_safe_shell_eval_token("abc-def")); + } + + #[test] + fn is_safe_shell_eval_token_rejects_overlong() { + assert!(!is_safe_shell_eval_token(&"a".repeat(129))); + assert!(is_safe_shell_eval_token(&"a".repeat(128))); + } + + #[test] + fn parse_shell_eval_pid_payload_plain_integer() { + assert_eq!(parse_shell_eval_pid_payload("12345"), Some(12345)); + } + + #[test] + fn parse_shell_eval_pid_payload_array_wrapped() { + assert_eq!(parse_shell_eval_pid_payload("[12345]"), Some(12345)); + assert_eq!(parse_shell_eval_pid_payload("[ 12345 ]"), Some(12345)); + } + + #[test] + fn parse_shell_eval_pid_payload_rejects_garbage() { + assert_eq!(parse_shell_eval_pid_payload(""), None); + assert_eq!(parse_shell_eval_pid_payload("undefined"), None); + assert_eq!(parse_shell_eval_pid_payload("0"), None); + } + + #[test] + fn build_window_pid_script_splices_token() { + let s = build_window_pid_script("Window_42"); + assert!(s.contains("===\"Window_42\"")); + assert!(s.contains("global.get_window_actors()")); + } +} diff --git a/fluxer_desktop/native/linux-portals/src/kwin.rs b/fluxer_desktop/native/linux-portals/src/kwin.rs new file mode 100644 index 0000000..1802060 --- /dev/null +++ b/fluxer_desktop/native/linux-portals/src/kwin.rs @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[cfg(target_os = "linux")] +use std::time::Duration; + +#[cfg(target_os = "linux")] +use zbus::{ + blocking::{Connection, Proxy}, + zvariant::{OwnedValue, Value}, +}; + +#[cfg(target_os = "linux")] +pub(crate) fn value_of_owned(value: &OwnedValue) -> &Value<'_> { + use std::ops::Deref as _; + value.deref() +} + +pub const KWIN_DESTINATION: &str = "org.kde.KWin"; +pub const KWIN_WINDOW_INTERFACE: &str = "org.kde.KWin.Window"; +pub const PROPERTIES_INTERFACE: &str = "org.freedesktop.DBus.Properties"; +#[cfg(target_os = "linux")] +pub const REQUEST_TIMEOUT: Duration = Duration::from_millis(1_500); + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ResolveError { + InvalidToken, + DbusOpenFailed, + DbusCallFailed, +} + +impl std::fmt::Display for ResolveError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match self { + Self::InvalidToken => "InvalidToken", + Self::DbusOpenFailed => "DbusOpenFailed", + Self::DbusCallFailed => "DbusCallFailed", + }; + f.write_str(s) + } +} + +pub fn is_safe_kwin_path_segment(token: &str) -> bool { + if token.is_empty() { + return false; + } + token + .bytes() + .all(|ch| ch.is_ascii_alphanumeric() || ch == b'_') +} + +pub fn build_kwin_window_path(token: &str) -> String { + let mut out = String::with_capacity("/org/kde/KWin/Window/".len() + token.len()); + out.push_str("/org/kde/KWin/Window/"); + out.push_str(token); + out +} + +#[cfg(target_os = "linux")] +pub fn resolve_kwin_window_pid(token: &str) -> Result, ResolveError> { + if !is_safe_kwin_path_segment(token) { + return Err(ResolveError::InvalidToken); + } + let conn = zbus::blocking::connection::Builder::session() + .map_err(|_| ResolveError::DbusOpenFailed)? + .method_timeout(REQUEST_TIMEOUT) + .build() + .map_err(|_| ResolveError::DbusOpenFailed)?; + Ok(resolve_kwin_window_pid_on(&conn, token)) +} + +#[cfg(target_os = "linux")] +fn resolve_kwin_window_pid_on(conn: &Connection, token: &str) -> Option { + let path = build_kwin_window_path(token); + let proxy = Proxy::new(conn, KWIN_DESTINATION, path.as_str(), PROPERTIES_INTERFACE).ok()?; + let reply: OwnedValue = proxy.call("Get", &(KWIN_WINDOW_INTERFACE, "pid")).ok()?; + integer_from_variant(value_of_owned(&reply)) +} + +#[cfg(target_os = "linux")] +pub(crate) fn integer_from_variant(value: &Value<'_>) -> Option { + let raw: i64 = match value { + Value::U8(v) => *v as i64, + Value::U16(v) => *v as i64, + Value::U32(v) => *v as i64, + Value::U64(v) => *v as i64, + Value::I16(v) => *v as i64, + Value::I32(v) => *v as i64, + Value::I64(v) => *v, + Value::Value(inner) => return integer_from_variant(inner), + _ => return None, + }; + if raw <= 0 || raw > u32::MAX as i64 { + return None; + } + Some(raw as u32) +} + +#[cfg(not(target_os = "linux"))] +pub fn resolve_kwin_window_pid(_token: &str) -> Result, ResolveError> { + Err(ResolveError::DbusOpenFailed) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn is_safe_kwin_path_segment_accepts_plain_alnum_underscore() { + assert!(is_safe_kwin_path_segment("abc")); + assert!(is_safe_kwin_path_segment("123")); + assert!(is_safe_kwin_path_segment("aZ_9")); + } + + #[test] + fn is_safe_kwin_path_segment_rejects_empty() { + assert!(!is_safe_kwin_path_segment("")); + } + + #[test] + fn is_safe_kwin_path_segment_rejects_traversal_and_shell_meta() { + assert!(!is_safe_kwin_path_segment("../etc")); + assert!(!is_safe_kwin_path_segment("a/b")); + assert!(!is_safe_kwin_path_segment("$(rm -rf)")); + assert!(!is_safe_kwin_path_segment("a;b")); + assert!(!is_safe_kwin_path_segment("a-b")); + assert!(!is_safe_kwin_path_segment("a.b")); + } + + #[test] + fn build_kwin_window_path_shapes_path_correctly() { + assert_eq!( + build_kwin_window_path("abc123"), + "/org/kde/KWin/Window/abc123" + ); + } +} diff --git a/fluxer_desktop/native/linux-portals/src/lib.rs b/fluxer_desktop/native/linux-portals/src/lib.rs new file mode 100644 index 0000000..8314db0 --- /dev/null +++ b/fluxer_desktop/native/linux-portals/src/lib.rs @@ -0,0 +1,712 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub mod background; +pub mod env; +pub mod filechooser; +pub mod global_shortcuts; +pub mod gnome_shell; +pub mod kwin; +#[cfg(target_os = "linux")] +pub mod portal; +pub mod settings; +pub mod x11; + +#[cfg(target_os = "linux")] +pub use napi_bindings::*; + +#[cfg(target_os = "linux")] +mod napi_bindings { + use std::sync::Arc; + + use napi::{ + Env, Status, + bindgen_prelude::{Array, AsyncTask, Function, Object, Result, Task, ToNapiValue}, + sys, + threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode, UnknownReturnValue}, + }; + use napi_derive::napi; + + use crate::{ + background::{self, RequestOptions, RequestResult}, + filechooser::{self, FileChooserResult, Filter, FilterRule, Mode, Options}, + global_shortcuts::{self, BoundShortcut, ConfigureResult, ShortcutEntry, ShortcutEvent}, + gnome_shell, kwin, + settings::{self, ChangeEvent, ChangePayload, ColorScheme, Contrast}, + x11, + }; + + const SETTINGS_EVENT_QUEUE_LIMIT: usize = 128; + const SHORTCUT_EVENT_QUEUE_LIMIT: usize = 128; + + fn generic_error(reason: impl Into) -> napi::Error { + napi::Error::new(Status::GenericFailure, reason.into()) + } + + fn invalid_arg(reason: impl Into) -> napi::Error { + napi::Error::new(Status::InvalidArg, reason.into()) + } + + fn read_string_field(object: &Object, key: &str) -> Option { + object.get::(key).ok().flatten() + } + + fn read_string_field_or_empty(object: &Object, key: &str) -> String { + read_string_field(object, key).unwrap_or_default() + } + + fn read_bool_field(object: &Object, key: &str) -> Option { + object.get::(key).ok().flatten() + } + + fn read_object_field<'a>(object: &Object<'a>, key: &str) -> Option> { + object.get::(key).ok().flatten() + } + + fn read_array_field<'a>(object: &Object<'a>, key: &str) -> Option> { + object.get::(key).ok().flatten() + } + + pub struct ResolveKwinTask { + token: String, + } + + impl Task for ResolveKwinTask { + type Output = Option; + type JsValue = Option; + + fn compute(&mut self) -> Result { + kwin::resolve_kwin_window_pid(&self.token) + .map_err(|err| generic_error(format!("resolveKwinWindowPid: {err}"))) + } + + fn resolve(&mut self, _env: Env, output: Self::Output) -> Result { + Ok(output) + } + } + + #[napi(js_name = "resolveKwinWindowPid")] + pub fn resolve_kwin_window_pid(token: String) -> Result> { + Ok(AsyncTask::new(ResolveKwinTask { token })) + } + + pub struct ResolveX11Task { + token: String, + } + + impl Task for ResolveX11Task { + type Output = Option; + type JsValue = Option; + + fn compute(&mut self) -> Result { + x11::resolve_x11_window_pid(&self.token) + .map_err(|err| generic_error(format!("resolveX11WindowPid: {err}"))) + } + + fn resolve(&mut self, _env: Env, output: Self::Output) -> Result { + Ok(output) + } + } + + #[napi(js_name = "resolveX11WindowPid")] + pub fn resolve_x11_window_pid(token: String) -> Result> { + Ok(AsyncTask::new(ResolveX11Task { token })) + } + + pub struct ResolveWindowPidTask { + token: String, + } + + impl Task for ResolveWindowPidTask { + type Output = Option; + type JsValue = Option; + + fn compute(&mut self) -> Result { + gnome_shell::resolve_gnome_shell_window_pid(&self.token).map_err(generic_error) + } + + fn resolve(&mut self, _env: Env, output: Self::Output) -> Result { + Ok(output) + } + } + + #[napi(js_name = "resolveWindowPid")] + pub fn resolve_window_pid(spec: Object) -> Result> { + let backend = read_string_field(&spec, "backend") + .ok_or_else(|| invalid_arg("spec.backend must be a string"))?; + if backend != "gnome-shell-eval" { + return Err(invalid_arg("spec.backend must be 'gnome-shell-eval'")); + } + let token = read_string_field(&spec, "token") + .ok_or_else(|| invalid_arg("spec.token must be a string"))?; + Ok(AsyncTask::new(ResolveWindowPidTask { token })) + } + + fn parse_filter_rule(object: &Object) -> Result { + let kind = object + .get::("kind") + .map_err(|err| invalid_arg(err.reason.clone()))? + .ok_or_else(|| invalid_arg("rule.kind must be a number"))?; + if kind > 1 { + return Err(invalid_arg("rule.kind must be 0 (glob) or 1 (mime-type)")); + } + let pattern = read_string_field(object, "pattern") + .ok_or_else(|| invalid_arg("rule.pattern must be a string"))?; + Ok(FilterRule { kind, pattern }) + } + + fn parse_filter(object: &Object) -> Result { + let name = read_string_field(object, "name") + .ok_or_else(|| invalid_arg("filter.name must be a string"))?; + let rules_array = read_array_field(object, "rules") + .ok_or_else(|| invalid_arg("filter.rules must be an array"))?; + let mut rules = Vec::with_capacity(rules_array.len() as usize); + for i in 0..rules_array.len() { + let rule_obj = rules_array + .get::(i) + .map_err(|err| invalid_arg(err.reason.clone()))? + .ok_or_else(|| invalid_arg("rule must be an object"))?; + rules.push(parse_filter_rule(&rule_obj)?); + } + Ok(Filter { name, rules }) + } + + fn parse_filechooser_options(object: &Object) -> Result { + let parent_window = read_string_field_or_empty(object, "parentWindow"); + let title = read_string_field_or_empty(object, "title"); + let accept_label = read_string_field(object, "acceptLabel"); + let modal = read_bool_field(object, "modal").unwrap_or(true); + let multiple = read_bool_field(object, "multiple").unwrap_or(false); + let directory = read_bool_field(object, "directory").unwrap_or(false); + let current_folder = read_string_field(object, "currentFolder"); + let current_name = read_string_field(object, "currentName"); + let current_file = read_string_field(object, "currentFile"); + let filters = if let Some(array) = read_array_field(object, "filters") { + let mut out = Vec::with_capacity(array.len() as usize); + for i in 0..array.len() { + let f_obj = array + .get::(i) + .map_err(|err| invalid_arg(err.reason.clone()))? + .ok_or_else(|| invalid_arg("filter must be an object"))?; + out.push(parse_filter(&f_obj)?); + } + out + } else { + Vec::new() + }; + let current_filter = if let Some(obj) = read_object_field(object, "currentFilter") { + Some(parse_filter(&obj)?) + } else { + None + }; + Ok(Options { + parent_window, + title, + accept_label, + modal, + multiple, + directory, + current_folder, + current_name, + current_file, + filters, + current_filter, + }) + } + + pub struct FileChooserTask { + mode: Mode, + options: Options, + } + + impl Task for FileChooserTask { + type Output = FileChooserResult; + type JsValue = Object<'static>; + + fn compute(&mut self) -> Result { + filechooser::invoke(self.mode, self.options.clone()) + .map_err(|err| generic_error(format!("FileChooser portal: {err}"))) + } + + fn resolve(&mut self, env: Env, output: Self::Output) -> Result { + let mut obj = Object::new(&env)?; + obj.set("cancelled", output.cancelled)?; + let mut array = env.create_array(output.uris.len() as u32)?; + for (i, uri) in output.uris.iter().enumerate() { + array.set(i as u32, uri.as_str())?; + } + obj.set("uris", array)?; + Ok(unsafe { std::mem::transmute::, Object<'static>>(obj) }) + } + } + + #[napi(js_name = "openFile")] + pub fn open_file(options: Object) -> Result> { + let parsed = parse_filechooser_options(&options)?; + Ok(AsyncTask::new(FileChooserTask { + mode: Mode::Open, + options: parsed, + })) + } + + #[napi(js_name = "saveFile")] + pub fn save_file(options: Object) -> Result> { + let parsed = parse_filechooser_options(&options)?; + Ok(AsyncTask::new(FileChooserTask { + mode: Mode::Save, + options: parsed, + })) + } + + fn parse_string_array_field(object: &Object, key: &str) -> Result> { + let Some(array) = read_array_field(object, key) else { + return Ok(Vec::new()); + }; + let mut out = Vec::with_capacity(array.len() as usize); + for i in 0..array.len() { + let value = array + .get::(i) + .map_err(|err| invalid_arg(err.reason.clone()))? + .ok_or_else(|| invalid_arg(format!("{key} entries must be strings")))?; + out.push(value); + } + Ok(out) + } + + fn parse_background_options(object: &Object) -> Result { + Ok(RequestOptions { + reason: read_string_field(object, "reason"), + autostart: read_bool_field(object, "autostart").unwrap_or(false), + commandline: parse_string_array_field(object, "commandline")?, + dbus_activatable: read_bool_field(object, "dbusActivatable").unwrap_or(false), + }) + } + + pub struct BackgroundTask { + options: RequestOptions, + } + + impl Task for BackgroundTask { + type Output = RequestResult; + type JsValue = Object<'static>; + + fn compute(&mut self) -> Result { + background::request_background(self.options.clone()) + .map_err(|err| generic_error(format!("Background portal: {err}"))) + } + + fn resolve(&mut self, env: Env, output: Self::Output) -> Result { + let mut obj = Object::new(&env)?; + obj.set("response", output.response)?; + obj.set("cancelled", output.cancelled())?; + obj.set("background", output.background)?; + obj.set("autostart", output.autostart)?; + Ok(unsafe { std::mem::transmute::, Object<'static>>(obj) }) + } + } + + #[napi(js_name = "requestBackground")] + pub fn request_background_js(options: Object) -> Result> { + let parsed = parse_background_options(&options)?; + Ok(AsyncTask::new(BackgroundTask { options: parsed })) + } + + #[napi(js_name = "isAvailable")] + pub fn is_available_js() -> bool { + global_shortcuts::is_available() + } + + #[napi(js_name = "getPortalVersion")] + pub fn get_portal_version_js() -> Option { + global_shortcuts::get_portal_version() + } + + fn parse_shortcut_entries(array: Array) -> Result> { + let mut entries = Vec::with_capacity(array.len() as usize); + for i in 0..array.len() { + let object = array + .get::(i) + .map_err(|err| invalid_arg(err.reason.clone()))? + .ok_or_else(|| invalid_arg("shortcut entries must be objects"))?; + let id = read_string_field(&object, "id") + .ok_or_else(|| invalid_arg("shortcut.id must be a string"))?; + let description = read_string_field(&object, "description") + .ok_or_else(|| invalid_arg("shortcut.description must be a string"))?; + entries.push(ShortcutEntry { + id, + description, + preferred_trigger: read_string_field(&object, "preferredTrigger"), + }); + } + Ok(entries) + } + + fn bound_shortcuts_to_array(env: &Env, shortcuts: &[BoundShortcut]) -> Result> { + let mut array = env.create_array(shortcuts.len() as u32)?; + for (i, shortcut) in shortcuts.iter().enumerate() { + let mut obj = Object::new(env)?; + obj.set("id", shortcut.id.as_str())?; + if let Some(description) = shortcut.description.as_deref() { + obj.set("description", description)?; + } + if let Some(trigger) = shortcut.trigger_description.as_deref() { + obj.set("triggerDescription", trigger)?; + } + array.set(i as u32, obj)?; + } + Ok(unsafe { std::mem::transmute::, Array<'static>>(array) }) + } + + pub enum NapiShortcutEvent { + Activated { id: String }, + Deactivated { id: String }, + ShortcutsChanged { shortcuts: Vec }, + Closed, + } + + impl From for NapiShortcutEvent { + fn from(event: ShortcutEvent) -> Self { + match event { + ShortcutEvent::Activated { id } => Self::Activated { id }, + ShortcutEvent::Deactivated { id } => Self::Deactivated { id }, + ShortcutEvent::ShortcutsChanged { shortcuts } => { + Self::ShortcutsChanged { shortcuts } + } + ShortcutEvent::Closed => Self::Closed, + } + } + } + + impl ToNapiValue for NapiShortcutEvent { + unsafe fn to_napi_value(raw_env: sys::napi_env, event: Self) -> Result { + let env = Env::from_raw(raw_env); + let mut obj = Object::new(&env)?; + match event { + Self::Activated { id } => { + obj.set("type", "activated")?; + obj.set("id", id)?; + } + Self::Deactivated { id } => { + obj.set("type", "deactivated")?; + obj.set("id", id)?; + } + Self::ShortcutsChanged { shortcuts } => { + obj.set("type", "shortcuts-changed")?; + obj.set("shortcuts", bound_shortcuts_to_array(&env, &shortcuts)?)?; + } + Self::Closed => { + obj.set("type", "closed")?; + } + } + unsafe { as ToNapiValue>::to_napi_value(raw_env, obj) } + } + } + + type ShortcutTsfn = Arc< + ThreadsafeFunction< + NapiShortcutEvent, + UnknownReturnValue, + NapiShortcutEvent, + Status, + false, + true, + SHORTCUT_EVENT_QUEUE_LIMIT, + >, + >; + + pub struct ConfigureShortcutsTask { + entries: Vec, + state: Arc>>, + callback: ShortcutTsfn, + } + + impl Task for ConfigureShortcutsTask { + type Output = ConfigureResult; + type JsValue = Object<'static>; + + fn compute(&mut self) -> Result { + let tsfn_for_cb = self.callback.clone(); + let callback = Arc::new(move |event: ShortcutEvent| { + let _ = tsfn_for_cb.call( + NapiShortcutEvent::from(event), + ThreadsafeFunctionCallMode::NonBlocking, + ); + }); + let (subscription, result) = + global_shortcuts::Subscription::configure(self.entries.clone(), callback) + .map_err(|err| generic_error(format!("GlobalShortcuts portal: {err}")))?; + let mut guard = self + .state + .lock() + .map_err(|_| generic_error("global shortcuts lock poisoned"))?; + if let Some(previous) = guard.replace(subscription) { + previous.close(); + } + Ok(result) + } + + fn resolve(&mut self, env: Env, output: Self::Output) -> Result { + let mut obj = Object::new(&env)?; + obj.set("action", output.action)?; + obj.set( + "shortcuts", + bound_shortcuts_to_array(&env, &output.shortcuts)?, + )?; + Ok(unsafe { std::mem::transmute::, Object<'static>>(obj) }) + } + } + + #[napi] + pub struct GlobalShortcutsPortal { + subscription: Arc>>, + callback: ShortcutTsfn, + #[allow(dead_code)] + app_id: Option, + } + + #[napi] + impl GlobalShortcutsPortal { + #[napi(constructor)] + pub fn new( + on_event: Function, + app_id: Option, + ) -> Result { + let callback: ShortcutTsfn = Arc::new( + on_event + .build_threadsafe_function::() + .weak::() + .callee_handled::() + .max_queue_size::() + .build() + .map_err(|err| { + generic_error(format!( + "failed to create global shortcuts callback: {}", + err.reason + )) + })?, + ); + Ok(Self { + subscription: Arc::new(std::sync::Mutex::new(None)), + callback, + app_id, + }) + } + + #[napi] + pub fn configure(&self, entries: Array) -> Result> { + let parsed = parse_shortcut_entries(entries)?; + Ok(AsyncTask::new(ConfigureShortcutsTask { + entries: parsed, + state: self.subscription.clone(), + callback: self.callback.clone(), + })) + } + + #[napi] + pub fn close(&self) -> Result<()> { + if let Some(subscription) = self + .subscription + .lock() + .map_err(|_| generic_error("global shortcuts lock poisoned"))? + .take() + { + subscription.close(); + } + Ok(()) + } + } + + impl Drop for GlobalShortcutsPortal { + fn drop(&mut self) { + if let Ok(mut guard) = self.subscription.lock() + && let Some(subscription) = guard.take() + { + subscription.close(); + } + } + } + + #[napi(js_name = "readColorScheme")] + pub fn read_color_scheme_js() -> &'static str { + settings::read_color_scheme().as_str() + } + + #[napi(js_name = "readContrast")] + pub fn read_contrast_js() -> &'static str { + settings::read_contrast().as_str() + } + + #[napi(object)] + pub struct AccentColorJs { + pub r: f64, + pub g: f64, + pub b: f64, + } + + #[napi(js_name = "readAccentColor")] + pub fn read_accent_color_js() -> Option { + settings::read_accent_color().map(|a| AccentColorJs { + r: a.r, + g: a.g, + b: a.b, + }) + } + + pub enum NapiSettingsEvent { + Uint32 { + namespace: String, + key: String, + value: u32, + }, + Accent { + namespace: String, + key: String, + r: f64, + g: f64, + b: f64, + }, + Unknown { + namespace: String, + key: String, + }, + } + + impl From for NapiSettingsEvent { + fn from(event: ChangeEvent) -> Self { + match event.payload { + ChangePayload::Uint32(v) => Self::Uint32 { + namespace: event.namespace, + key: event.key, + value: v, + }, + ChangePayload::Accent(a) => Self::Accent { + namespace: event.namespace, + key: event.key, + r: a.r, + g: a.g, + b: a.b, + }, + ChangePayload::Unknown => Self::Unknown { + namespace: event.namespace, + key: event.key, + }, + } + } + } + + impl ToNapiValue for NapiSettingsEvent { + unsafe fn to_napi_value(raw_env: sys::napi_env, event: Self) -> Result { + let env = Env::from_raw(raw_env); + let mut obj = Object::new(&env)?; + match event { + Self::Uint32 { + namespace, + key, + value, + } => { + obj.set("namespace", namespace)?; + obj.set("key", key)?; + obj.set("uint32", value)?; + } + Self::Accent { + namespace, + key, + r, + g, + b, + } => { + obj.set("namespace", namespace)?; + obj.set("key", key)?; + let mut accent = Object::new(&env)?; + accent.set("r", r)?; + accent.set("g", g)?; + accent.set("b", b)?; + obj.set("accent", accent)?; + } + Self::Unknown { namespace, key } => { + obj.set("namespace", namespace)?; + obj.set("key", key)?; + } + } + unsafe { as ToNapiValue>::to_napi_value(raw_env, obj) } + } + } + + type SettingsTsfn = Arc< + ThreadsafeFunction< + NapiSettingsEvent, + UnknownReturnValue, + NapiSettingsEvent, + Status, + false, + true, + SETTINGS_EVENT_QUEUE_LIMIT, + >, + >; + + #[napi] + pub struct Settings { + subscription: std::sync::Mutex>, + } + + #[napi] + impl Settings { + #[napi(constructor)] + pub fn new(on_change: Function) -> Result { + let tsfn: SettingsTsfn = Arc::new( + on_change + .build_threadsafe_function::() + .weak::() + .callee_handled::() + .max_queue_size::() + .build() + .map_err(|err| { + generic_error(format!( + "failed to create settings callback: {}", + err.reason + )) + })?, + ); + let tsfn_for_cb = tsfn.clone(); + let callback = Arc::new(move |event: ChangeEvent| { + let _ = tsfn_for_cb.call( + NapiSettingsEvent::from(event), + ThreadsafeFunctionCallMode::NonBlocking, + ); + }); + let sub = settings::Subscription::new(callback) + .map_err(|err| generic_error(format!("Settings subscribe failed: {err}")))?; + Ok(Self { + subscription: std::sync::Mutex::new(Some(sub)), + }) + } + + #[napi] + pub fn close(&self) -> Result<()> { + if let Some(sub) = self + .subscription + .lock() + .map_err(|_| generic_error("settings lock poisoned"))? + .take() + { + sub.close(); + } + Ok(()) + } + } + + impl Drop for Settings { + fn drop(&mut self) { + if let Ok(mut guard) = self.subscription.lock() + && let Some(sub) = guard.take() + { + sub.close(); + } + } + } + + #[allow(dead_code)] + fn _link_unused(_c: Contrast, _s: ColorScheme) {} +} + +#[cfg(not(target_os = "linux"))] +mod napi_bindings {} diff --git a/fluxer_desktop/native/linux-portals/src/portal.rs b/fluxer_desktop/native/linux-portals/src/portal.rs new file mode 100644 index 0000000..6e658b1 --- /dev/null +++ b/fluxer_desktop/native/linux-portals/src/portal.rs @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::sync::atomic::{AtomicU64, Ordering}; +use std::time::{SystemTime, UNIX_EPOCH}; + +pub const REQUEST_INTERFACE: &str = "org.freedesktop.portal.Request"; + +pub fn request_path(unique_bus_name: &str, handle_token: &str) -> String { + let trimmed = unique_bus_name.strip_prefix(':').unwrap_or(unique_bus_name); + let mut out = String::with_capacity(40 + trimmed.len() + handle_token.len()); + out.push_str("/org/freedesktop/portal/desktop/request/"); + for ch in trimmed.chars() { + out.push(if ch == '.' { '_' } else { ch }); + } + out.push('/'); + out.push_str(handle_token); + out +} + +static TOKEN_SEQ: AtomicU64 = AtomicU64::new(1); + +pub fn mint_token(prefix: &str) -> String { + let seq = TOKEN_SEQ.fetch_add(1, Ordering::Relaxed); + let ms = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|d| d.as_millis() as u64) + .unwrap_or(0); + format!("{prefix}_{ms:x}_{seq:x}") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn request_path_sanitizes_unique_bus_name() { + assert_eq!( + request_path(":1.42", "fluxer_fc_open_1"), + "/org/freedesktop/portal/desktop/request/1_42/fluxer_fc_open_1" + ); + } + + #[test] + fn mint_token_is_distinct_and_prefixed() { + let a = mint_token("fluxer_fc_open"); + let b = mint_token("fluxer_fc_open"); + assert_ne!(a, b); + assert!(a.starts_with("fluxer_fc_open_")); + } +} diff --git a/fluxer_desktop/native/linux-portals/src/settings.rs b/fluxer_desktop/native/linux-portals/src/settings.rs new file mode 100644 index 0000000..3860b60 --- /dev/null +++ b/fluxer_desktop/native/linux-portals/src/settings.rs @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[cfg(target_os = "linux")] +use std::{ + sync::{ + Arc, Mutex, + atomic::{AtomicBool, Ordering}, + mpsc, + }, + thread::{self, JoinHandle}, + time::Duration, +}; + +#[cfg(target_os = "linux")] +use futures_lite::{FutureExt, StreamExt, future}; +#[cfg(target_os = "linux")] +use zbus::{ + MatchRule, MessageStream, + blocking::{Connection as BlockingConnection, Proxy as BlockingProxy}, + message::Type as MessageType, + zvariant::{OwnedValue, Value}, +}; + +pub const PORTAL_DESTINATION: &str = "org.freedesktop.portal.Desktop"; +pub const PORTAL_PATH: &str = "/org/freedesktop/portal/desktop"; +pub const SETTINGS_INTERFACE: &str = "org.freedesktop.portal.Settings"; +pub const APPEARANCE_NAMESPACE: &str = "org.freedesktop.appearance"; +#[cfg(target_os = "linux")] +pub const READ_TIMEOUT: Duration = Duration::from_millis(1_500); +#[cfg(target_os = "linux")] +pub const SIGNAL_POLL_INTERVAL: Duration = Duration::from_millis(200); +#[cfg(target_os = "linux")] +pub const SIGNAL_THREAD_START_TIMEOUT: Duration = Duration::from_secs(5); + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ColorScheme { + NoPreference, + PreferDark, + PreferLight, +} + +impl ColorScheme { + pub fn from_u32(value: u32) -> Self { + match value { + 1 => Self::PreferDark, + 2 => Self::PreferLight, + _ => Self::NoPreference, + } + } + + pub fn as_str(self) -> &'static str { + match self { + Self::NoPreference => "no-preference", + Self::PreferDark => "prefer-dark", + Self::PreferLight => "prefer-light", + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Contrast { + NoPreference, + High, +} + +impl Contrast { + pub fn from_u32(value: u32) -> Self { + match value { + 1 => Self::High, + _ => Self::NoPreference, + } + } + + pub fn as_str(self) -> &'static str { + match self { + Self::NoPreference => "no-preference", + Self::High => "high", + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct AccentColor { + pub r: f64, + pub g: f64, + pub b: f64, +} + +pub fn classify_accent_color(r: f64, g: f64, b: f64) -> Option { + if r < 0.0 || g < 0.0 || b < 0.0 { + return None; + } + Some(AccentColor { r, g, b }) +} + +#[derive(Debug, Clone, PartialEq)] +pub struct ChangeEvent { + pub namespace: String, + pub key: String, + pub payload: ChangePayload, +} + +#[derive(Debug, Clone, PartialEq)] +pub enum ChangePayload { + Uint32(u32), + Accent(AccentColor), + Unknown, +} + +#[cfg(target_os = "linux")] +fn open_blocking_connection() -> zbus::Result { + zbus::blocking::connection::Builder::session()? + .method_timeout(READ_TIMEOUT) + .build() +} + +#[cfg(target_os = "linux")] +fn read_uint32_setting(key: &str) -> Option { + let conn = open_blocking_connection().ok()?; + let proxy = + BlockingProxy::new(&conn, PORTAL_DESTINATION, PORTAL_PATH, SETTINGS_INTERFACE).ok()?; + let value: OwnedValue = proxy.call("Read", &(APPEARANCE_NAMESPACE, key)).ok()?; + crate::kwin::integer_from_variant(crate::kwin::value_of_owned(&value)) +} + +#[cfg(target_os = "linux")] +pub fn read_color_scheme() -> ColorScheme { + read_uint32_setting("color-scheme") + .map(ColorScheme::from_u32) + .unwrap_or(ColorScheme::NoPreference) +} + +#[cfg(target_os = "linux")] +pub fn read_contrast() -> Contrast { + read_uint32_setting("contrast") + .map(Contrast::from_u32) + .unwrap_or(Contrast::NoPreference) +} + +#[cfg(target_os = "linux")] +pub fn read_accent_color() -> Option { + let conn = open_blocking_connection().ok()?; + let proxy = + BlockingProxy::new(&conn, PORTAL_DESTINATION, PORTAL_PATH, SETTINGS_INTERFACE).ok()?; + let value: OwnedValue = proxy + .call("Read", &(APPEARANCE_NAMESPACE, "accent-color")) + .ok()?; + extract_accent_from_variant(crate::kwin::value_of_owned(&value)) +} + +#[cfg(target_os = "linux")] +fn extract_accent_from_variant(value: &Value<'_>) -> Option { + let inner: &Value<'_> = match value { + Value::Value(b) => b.as_ref(), + other => other, + }; + let Value::Structure(structure) = inner else { + return None; + }; + let fields = structure.fields(); + if fields.len() < 3 { + return None; + } + let r = double_from_value(&fields[0])?; + let g = double_from_value(&fields[1])?; + let b = double_from_value(&fields[2])?; + classify_accent_color(r, g, b) +} + +#[cfg(target_os = "linux")] +fn double_from_value(value: &Value<'_>) -> Option { + match value { + Value::F64(v) => Some(*v), + Value::Value(b) => double_from_value(b.as_ref()), + _ => None, + } +} + +#[cfg(not(target_os = "linux"))] +pub fn read_color_scheme() -> ColorScheme { + ColorScheme::NoPreference +} + +#[cfg(not(target_os = "linux"))] +pub fn read_contrast() -> Contrast { + Contrast::NoPreference +} + +#[cfg(not(target_os = "linux"))] +pub fn read_accent_color() -> Option { + None +} + +#[cfg(target_os = "linux")] +type ChangeCallback = Arc; + +#[cfg(target_os = "linux")] +pub struct Subscription { + stop_flag: Arc, + thread: Mutex>>, +} + +#[cfg(target_os = "linux")] +impl Subscription { + pub fn new(callback: ChangeCallback) -> Result { + let stop_flag = Arc::new(AtomicBool::new(false)); + let (ready_tx, ready_rx) = mpsc::sync_channel(1); + let stop_for_thread = stop_flag.clone(); + let thread = thread::Builder::new() + .name("fluxer-linux-portals-settings".to_string()) + .spawn(move || { + let setup = future::block_on(async { + let conn = zbus::Connection::session().await?; + let rule = MatchRule::builder() + .msg_type(MessageType::Signal) + .interface(SETTINGS_INTERFACE)? + .member("SettingChanged")? + .build(); + let stream = MessageStream::for_match_rule(rule, &conn, Some(32)).await?; + zbus::Result::Ok((conn, stream)) + }); + let (_conn, mut stream) = match setup { + Ok(parts) => { + let _ = ready_tx.send(Ok(())); + parts + } + Err(err) => { + let _ = ready_tx.send(Err(err.to_string())); + return; + } + }; + while !stop_for_thread.load(Ordering::Acquire) { + let timeout = async { + async_io::Timer::after(SIGNAL_POLL_INTERVAL).await; + None::> + }; + match future::block_on(stream.next().or(timeout)) { + Some(Ok(message)) => { + if let Some(event) = parse_setting_changed(&message) + && event.namespace == APPEARANCE_NAMESPACE + { + callback(event); + } + } + Some(Err(_)) => break, + None => {} + } + } + }) + .map_err(|err| err.to_string())?; + match ready_rx.recv_timeout(SIGNAL_THREAD_START_TIMEOUT) { + Ok(Ok(())) => Ok(Self { + stop_flag, + thread: Mutex::new(Some(thread)), + }), + Ok(Err(err)) => { + let _ = thread.join(); + Err(err) + } + Err(err) => { + stop_flag.store(true, Ordering::Release); + if matches!(err, mpsc::RecvTimeoutError::Disconnected) { + let _ = thread.join(); + } + Err(err.to_string()) + } + } + } + + pub fn close(&self) { + self.stop_flag.store(true, Ordering::Release); + if let Ok(mut thread) = self.thread.lock() + && let Some(t) = thread.take() + { + let _ = t.join(); + } + } +} + +#[cfg(target_os = "linux")] +impl Drop for Subscription { + fn drop(&mut self) { + self.close(); + } +} + +#[cfg(target_os = "linux")] +fn parse_setting_changed(message: &zbus::Message) -> Option { + let body = message.body(); + let (namespace, key, value): (String, String, OwnedValue) = body.deserialize().ok()?; + let value_ref = crate::kwin::value_of_owned(&value); + let payload = classify_payload(&key, value_ref); + Some(ChangeEvent { + namespace, + key, + payload, + }) +} + +#[cfg(target_os = "linux")] +fn classify_payload(_key: &str, value: &Value<'_>) -> ChangePayload { + if let Some(n) = crate::kwin::integer_from_variant(value) { + return ChangePayload::Uint32(n); + } + if let Some(accent) = extract_accent_from_variant(value) { + return ChangePayload::Accent(accent); + } + ChangePayload::Unknown +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn color_scheme_maps_to_strings_per_typescript_union() { + assert_eq!(ColorScheme::from_u32(0).as_str(), "no-preference"); + assert_eq!(ColorScheme::from_u32(1).as_str(), "prefer-dark"); + assert_eq!(ColorScheme::from_u32(2).as_str(), "prefer-light"); + assert_eq!(ColorScheme::from_u32(99).as_str(), "no-preference"); + } + + #[test] + fn contrast_maps_to_strings_per_typescript_union() { + assert_eq!(Contrast::from_u32(0).as_str(), "no-preference"); + assert_eq!(Contrast::from_u32(1).as_str(), "high"); + assert_eq!(Contrast::from_u32(99).as_str(), "no-preference"); + } + + #[test] + fn classify_accent_color_treats_negative_as_no_preference() { + assert_eq!(classify_accent_color(-1.0, -1.0, -1.0), None); + assert_eq!(classify_accent_color(-0.0001, 0.5, 0.5), None); + let accent = classify_accent_color(0.1, 0.2, 0.3).unwrap(); + assert_eq!(accent.r, 0.1); + assert_eq!(accent.g, 0.2); + assert_eq!(accent.b, 0.3); + } +} diff --git a/fluxer_desktop/native/linux-portals/src/x11.rs b/fluxer_desktop/native/linux-portals/src/x11.rs new file mode 100644 index 0000000..c5bdd53 --- /dev/null +++ b/fluxer_desktop/native/linux-portals/src/x11.rs @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::os::raw::c_long; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ResolveError { + InvalidToken, + LibX11Unavailable, + MissingSymbol, + DisplayUnavailable, +} + +impl std::fmt::Display for ResolveError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match self { + Self::InvalidToken => "InvalidToken", + Self::LibX11Unavailable => "LibX11Unavailable", + Self::MissingSymbol => "MissingSymbol", + Self::DisplayUnavailable => "DisplayUnavailable", + }; + f.write_str(s) + } +} + +pub fn parse_window_token(token: &str) -> Option { + if token.is_empty() { + return None; + } + let parsed: u64 = if let Some(stripped) = token + .strip_prefix("0x") + .or_else(|| token.strip_prefix("0X")) + { + u64::from_str_radix(stripped, 16).ok()? + } else { + token.parse::().ok()? + }; + if parsed == 0 { + return None; + } + u32::try_from(parsed).ok() +} + +pub fn pid_from_long(value: c_long) -> Option { + if value <= 0 { + return None; + } + if (value as u64) > u32::MAX as u64 { + return None; + } + Some(value as u32) +} + +#[cfg(target_os = "linux")] +pub fn resolve_x11_window_pid(token: &str) -> Result, ResolveError> { + use x11rb::protocol::xproto::{AtomEnum, ConnectionExt}; + use x11rb::rust_connection::RustConnection; + + let window = parse_window_token(token).ok_or(ResolveError::InvalidToken)?; + let (conn, _screen) = + RustConnection::connect(None).map_err(|_| ResolveError::DisplayUnavailable)?; + + let atom_cookie = conn + .intern_atom(true, b"_NET_WM_PID") + .map_err(|_| ResolveError::DisplayUnavailable)?; + let atom = atom_cookie + .reply() + .map_err(|_| ResolveError::DisplayUnavailable)? + .atom; + if atom == 0 { + return Ok(None); + } + + let reply = conn + .get_property(false, window, atom, AtomEnum::CARDINAL, 0, 1) + .map_err(|_| ResolveError::DisplayUnavailable)? + .reply() + .map_err(|_| ResolveError::DisplayUnavailable)?; + + if reply.type_ != u32::from(AtomEnum::CARDINAL) || reply.format != 32 || reply.value_len < 1 { + return Ok(None); + } + + let Some(values) = reply.value32() else { + return Ok(None); + }; + let pid = values.collect::>(); + let Some(&first) = pid.first() else { + return Ok(None); + }; + Ok(pid_from_long(first as c_long)) +} + +#[cfg(not(target_os = "linux"))] +pub fn resolve_x11_window_pid(_token: &str) -> Result, ResolveError> { + Err(ResolveError::LibX11Unavailable) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_window_token_accepts_decimal_and_hexadecimal_xids() { + assert_eq!(parse_window_token("123"), Some(123)); + assert_eq!(parse_window_token("0x3a00007"), Some(0x3a00007)); + assert_eq!(parse_window_token("0X3a00007"), Some(0x3a00007)); + } + + #[test] + fn parse_window_token_rejects_invalid_or_zero_xids() { + assert_eq!(parse_window_token(""), None); + assert_eq!(parse_window_token("0"), None); + assert_eq!(parse_window_token("0x"), None); + assert_eq!(parse_window_token("0xG"), None); + assert_eq!(parse_window_token("../123"), None); + assert_eq!(parse_window_token("123abc"), None); + } + + #[test] + fn pid_from_long_validates_positive_uint32_process_ids() { + assert_eq!(pid_from_long(1), Some(1)); + assert_eq!(pid_from_long(42_424), Some(42_424)); + assert_eq!(pid_from_long(0), None); + assert_eq!(pid_from_long(-1), None); + assert_eq!(pid_from_long(u32::MAX as c_long + 1), None); + } +} diff --git a/fluxer_desktop/native/linux-screen-capture/Cargo.lock b/fluxer_desktop/native/linux-screen-capture/Cargo.lock new file mode 100644 index 0000000..5ec7f93 --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/Cargo.lock @@ -0,0 +1,2199 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "annotate-snippets" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "710e8eae58854cdc1790fcb56cca04d712a17be849eeb81da2a724bf4bae2bc4" +dependencies = [ + "anstyle", + "unicode-width", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "async-executor" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "pin-project-lite", + "slab", +] + +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "windows-sys", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix", +] + +[[package]] +name = "async-recursion" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "async-signal" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys", +] + +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + +[[package]] +name = "async-trait" +version = "0.1.89" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "annotate-snippets", + "bitflags", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "proc-macro2", + "quote", + "regex", + "rustc-hash 2.1.2", + "shlex 1.3.0", + "syn", +] + +[[package]] +name = "bit-set" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ddef2995421ab6a5c779542c81ee77c115206f4ad9d5a8e05f4ff49716a3dd" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +dependencies = [ + "find-msvc-tools", + "shlex 2.0.1", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom 7.1.3", +] + +[[package]] +name = "cfg-expr" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb693542bcafa528e198be0ebd9d3632ca5b7c93dbe7237460e199910835997c" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading 0.8.9", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "codespan-reporting" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cookie-factory" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9885fa71e26b8ab7855e2ec7cae6e9b380edff76cd052e07c683a0319d51b3a2" + +[[package]] +name = "criterion" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3" +dependencies = [ + "alloca", + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools 0.13.0", + "num-traits", + "oorandom", + "page_size", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea" +dependencies = [ + "cast", + "itertools 0.13.0", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "dcv-color-primitives" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1e04941e6acf6d42b8ff109e35a0f0237334aee1bfd7ad43a7f18d38451e0d9" +dependencies = [ + "itertools 0.14.0", + "pastey", + "spin", + "wasm-bindgen", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + +[[package]] +name = "enumflags2" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fluxer_gpu_rebuild" +version = "0.0.0" +dependencies = [ + "parking_lot", + "wgpu", +] + +[[package]] +name = "fluxer_linux_screen_capture" +version = "0.0.0" +dependencies = [ + "async-io", + "criterion", + "dcv-color-primitives", + "fluxer_gpu_rebuild", + "fluxer_screen_frame_bus", + "futures-lite", + "libc", + "napi", + "napi-build", + "napi-derive", + "parking_lot", + "pipewire", + "wgpu", + "zbus", +] + +[[package]] +name = "fluxer_screen_frame_bus" +version = "0.0.0" +dependencies = [ + "fluxer_gpu_rebuild", + "parking_lot", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-lite" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "num-traits", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash 0.2.0", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hermit-abi" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libspa" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2909f3be29d674e7f10604aff18d1bbe1bb03c4cd61c8a8ba19c0b1d162f7d4e" +dependencies = [ + "bitflags", + "cc", + "cookie-factory", + "libc", + "libspa-sys", + "nom 8.0.0", + "rustix", + "system-deps", +] + +[[package]] +name = "libspa-sys" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69ad52764fca54818486f3cf75afec844d1f1a1568c24dcee25d41b1ab007dda" +dependencies = [ + "bindgen", + "cc", + "system-deps", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "naga" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd91265cc2454558f659b3b4b9640f0ddb8cc6521277f166b8a8c181c898079" +dependencies = [ + "arrayvec", + "bit-set", + "bitflags", + "cfg-if", + "cfg_aliases", + "codespan-reporting", + "half", + "hashbrown 0.16.1", + "hexf-parse", + "indexmap", + "libm", + "log", + "num-traits", + "once_cell", + "rustc-hash 1.1.0", + "thiserror", + "unicode-ident", +] + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash 2.1.2", + "tokio", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading 0.9.0", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "ordered-stream" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" +dependencies = [ + "futures-core", + "pin-project-lite", +] + +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pastey" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + +[[package]] +name = "pipewire" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8585aba8a52ad74ccc633b8e293c1dc4277976bd5d510b925533f34fd6685f38" +dependencies = [ + "bitflags", + "libc", + "libspa", + "libspa-sys", + "pipewire-sys", + "rustix", +] + +[[package]] +name = "pipewire-sys" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2089f245b548723e60325773c27f586b7a2372c79ea941b246cd0d654706adc" +dependencies = [ + "bindgen", + "libspa-sys", + "system-deps", +] + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "windows-sys", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "spin" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +dependencies = [ + "lock_api", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "system-deps" +version = "7.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396a35feb67335377e0251fcbc1092fc85c484bd4e3a7a54319399da127796e7" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "pin-project-lite", +] + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "uds_windows" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" +dependencies = [ + "memoffset", + "tempfile", + "windows-sys", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "uuid" +version = "1.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" +dependencies = [ + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "version-compare" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "wgpu" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb3feacc458f7bee8bc1737149b42b6c731aa461039a4264a67bb6681646b250" +dependencies = [ + "arrayvec", + "bitflags", + "bytemuck", + "cfg-if", + "cfg_aliases", + "document-features", + "hashbrown 0.16.1", + "log", + "portable-atomic", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02da3ad1b568337f25513b317870960ef87073ea0945502e44b864b67a8c77b7" +dependencies = [ + "arrayvec", + "bit-set", + "bit-vec", + "bitflags", + "bytemuck", + "cfg_aliases", + "document-features", + "hashbrown 0.16.1", + "indexmap", + "log", + "naga", + "once_cell", + "parking_lot", + "portable-atomic", + "profiling", + "raw-window-handle", + "rustc-hash 1.1.0", + "smallvec", + "thiserror", + "wgpu-core-deps-windows-linux-android", + "wgpu-hal", + "wgpu-naga-bridge", + "wgpu-types", +] + +[[package]] +name = "wgpu-core-deps-windows-linux-android" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb01076d0aa08b0ba9bd741e178b5cc440f5abe99d9581323a4c8b5d1a1916" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-hal" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8e1a9e7a8512f276f7c62e018c7fa8d60954303fed2e5750114332049193f" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libloading 0.8.9", + "log", + "naga", + "portable-atomic", + "portable-atomic-util", + "raw-window-handle", + "renderdoc-sys", + "thiserror", + "wgpu-naga-bridge", + "wgpu-types", +] + +[[package]] +name = "wgpu-naga-bridge" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c654c483f058800972c3645e95388a7eca31bf9fe1933bc20e036588a0be02" +dependencies = [ + "naga", + "wgpu-types", +] + +[[package]] +name = "wgpu-types" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9bcc31518a0e9735aefebedb5f7a9ef3ed1c42549c9f4c882fa9060ceaac639" +dependencies = [ + "bitflags", + "bytemuck", + "log", + "raw-window-handle", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "zbus" +version = "5.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285" +dependencies = [ + "async-broadcast", + "async-executor", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-lite", + "hex", + "libc", + "ordered-stream", + "rustix", + "serde", + "serde_repr", + "tracing", + "uds_windows", + "uuid", + "windows-sys", + "winnow", + "zbus_macros", + "zbus_names", + "zvariant", +] + +[[package]] +name = "zbus_macros" +version = "5.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zbus_names", + "zvariant", + "zvariant_utils", +] + +[[package]] +name = "zbus_names" +version = "4.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" +dependencies = [ + "serde", + "winnow", + "zvariant", +] + +[[package]] +name = "zerocopy" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zvariant" +version = "5.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a192a0bde63360d77a7523c833d4b4ce6070a927e2c53246e4c540b1a3e27be0" +dependencies = [ + "endi", + "enumflags2", + "serde", + "winnow", + "zvariant_derive", + "zvariant_utils", +] + +[[package]] +name = "zvariant_derive" +version = "5.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc6cde9c01c511074be97f7ccb6c19d0da89e3f8662e812e999dcfd4638737" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", + "zvariant_utils", +] + +[[package]] +name = "zvariant_utils" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e8535915cfa75547e559d8c68e8139909a4aeee076831e4ef7fc59d8172c4d6" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn", + "winnow", +] diff --git a/fluxer_desktop/native/linux-screen-capture/Cargo.toml b/fluxer_desktop/native/linux-screen-capture/Cargo.toml new file mode 100644 index 0000000..430bd8d --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/Cargo.toml @@ -0,0 +1,42 @@ +[package] +name = "fluxer_linux_screen_capture" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +default = [] +wgpu = ["dep:wgpu", "fluxer_gpu_rebuild/wgpu"] + +[dependencies] +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8", "async"]} +napi-derive = "3.5.6" +parking_lot = "0.12" +fluxer_screen_frame_bus = { path = "../screen-frame-bus" } +fluxer_gpu_rebuild = { path = "../gpu-rebuild" } +wgpu = { version = "29", optional = true, default-features = false } + +[target.'cfg(target_os = "linux")'.dependencies] +async-io = "2.6.0" +dcv-color-primitives = "1.0" +futures-lite = "2.6.1" +libc = "0.2" +pipewire = { version = "0.10.0", features = ["v0_3_33"] } +zbus = "5.16.0" + +[dev-dependencies] +criterion = { version = "0.8", default-features = false, features = ["cargo_bench_support"] } + +[build-dependencies] +napi-build = "2.3.2" + +[[bench]] +name = "pipewire_callback" +harness = false diff --git a/fluxer_desktop/native/linux-screen-capture/THIRD_PARTY_OBS_VKCAPTURE.md b/fluxer_desktop/native/linux-screen-capture/THIRD_PARTY_OBS_VKCAPTURE.md new file mode 100644 index 0000000..d920769 --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/THIRD_PARTY_OBS_VKCAPTURE.md @@ -0,0 +1,40 @@ + + +# obs-vkcapture Runtime Assets + +Fluxer can launch Linux games with OBS-compatible Vulkan/OpenGL capture enabled. +The Fluxer code in this package does not include obs-vkcapture source code; it only +sets launch environment variables and speaks the OBS-compatible capture socket +protocol implemented by the Rust backend. + +The Rust receiver treats OBS import modes as a fallback ladder: + +- `default-dmabuf`, `no-modifiers-dmabuf`, and `linear-dmabuf` request GPU + DMA-BUF descriptors from the hook. Until a local GPU importer validates those + descriptors end to end, Fluxer reports them as requested rather than available. +- `linear-host-mapped-dmabuf` is the conservative CPU fallback. Frames carry an + NV12 CPU payload, and may also expose source texture DMA-BUF metadata so the + downstream native WebRTC layer can opportunistically try GPU import before + falling back to the CPU payload. +- Unsupported host-mapped layouts, invalid file descriptors, unsupported fourcc + values, and invalid strides/offsets must surface as lifecycle diagnostics and + must not be treated as successful capture. + +If Fluxer ships obs-vkcapture hook binaries under `obs-vkcapture/`, treat those +files as a separate third-party runtime component. The upstream project currently +ships GNU GPL version 2 license text, and distro metadata may label the package as +GPL-2.0-or-later. Use the more conservative GPL-2.0 boundary unless upstream +files in the vendored revision clearly state otherwise. + +Distribution checklist for bundled hook assets: + +- Keep the obs-vkcapture binaries and manifests under `obs-vkcapture/`. +- Include the exact upstream license text and copyright notices next to the + bundled assets. +- Record the upstream repository URL, revision, local patches, and build script. +- Provide corresponding source for the exact shipped binaries, or a compliant + written source offer when applicable. +- Keep package metadata including `THIRD_PARTY_OBS_VKCAPTURE.md` and + `obs-vkcapture/**/*`; loader tests assert those package entries. +- Do not copy obs-vkcapture implementation code into AGPL-licensed Fluxer modules + unless the license compatibility has been explicitly reviewed. diff --git a/fluxer_desktop/native/linux-screen-capture/benches/baseline.json b/fluxer_desktop/native/linux-screen-capture/benches/baseline.json new file mode 100644 index 0000000..3e278be --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/benches/baseline.json @@ -0,0 +1,29 @@ +{ + "measured_at": "85e057a273fd", + "host": "darwin-arm64-apple-silicon", + "regression_budget_percent": 5.0, + "criterion_args": { + "warm_up_time_sec": 2, + "measurement_time_sec": 5 + }, + "benches": { + "linux_screen_capture/pool_acquire_release/1080p_nv12": { + "median_ns": 14.252, + "low_ns": 14.201, + "high_ns": 14.316, + "budget_percent_override": 10.0, + "note": "~14ns hot loop; noise floor reasoning." + }, + "linux_screen_capture/simulated_callback/1080p_nv12_copy": { + "median_ns": 36690.0, + "low_ns": 36375.0, + "high_ns": 37029.0 + }, + "linux_screen_capture/legacy_baseline/1080p_nv12_vec_clone": { + "median_ns": 36918.0, + "low_ns": 36634.0, + "high_ns": 37213.0, + "note": "Reference point: prior Vec::clone path that the pool was added to replace. Tracked so that any regression of the pool path back to clone cost is visible." + } + } +} diff --git a/fluxer_desktop/native/linux-screen-capture/benches/pipewire_callback.rs b/fluxer_desktop/native/linux-screen-capture/benches/pipewire_callback.rs new file mode 100644 index 0000000..d96dd3f --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/benches/pipewire_callback.rs @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::hint::black_box; + +use criterion::{Criterion, criterion_group, criterion_main}; +use fluxer_linux_screen_capture::frame_buffer_pool::{ + LINUX_SCREEN_FRAME_POOL_CAP, LinuxFrameBufferPool, +}; +use fluxer_linux_screen_capture::nv12_packing::{Nv12Layout, bgra_to_nv12}; + +const BENCH_NV12_WIDTH: usize = 1920; +const BENCH_NV12_HEIGHT: usize = 1080; +const BENCH_NV12_BYTES: usize = BENCH_NV12_WIDTH * BENCH_NV12_HEIGHT * 3 / 2; + +fn bench_pool_acquire_release_steady_state(c: &mut Criterion) { + assert_eq!(LINUX_SCREEN_FRAME_POOL_CAP, 8); + let pool = LinuxFrameBufferPool::new(BENCH_NV12_BYTES) + .expect("pool must allocate at construction time"); + assert_eq!(pool.capacity(), LINUX_SCREEN_FRAME_POOL_CAP); + assert_eq!(pool.bytes_per_buffer(), BENCH_NV12_BYTES); + + c.bench_function( + "linux_screen_capture/pool_acquire_release/1080p_nv12", + |b| { + b.iter(|| { + let mut pooled = pool + .try_acquire() + .expect("steady state single-thread must not starve"); + let buf = pooled.buffer_mut(); + buf[0] = black_box(buf.len() as u8); + pooled.set_len(BENCH_NV12_BYTES); + black_box(pooled.slot_index()); + }); + }, + ); + assert_eq!(pool.currently_in_flight(), 0); +} + +fn bench_simulated_callback_fill_path(c: &mut Criterion) { + assert_eq!(LINUX_SCREEN_FRAME_POOL_CAP, 8); + let pool = LinuxFrameBufferPool::new(BENCH_NV12_BYTES).expect("pool init"); + + let source = vec![0xA5u8; BENCH_NV12_BYTES]; + assert_eq!(source.len(), BENCH_NV12_BYTES); + + c.bench_function( + "linux_screen_capture/simulated_callback/1080p_nv12_copy", + |b| { + b.iter(|| { + let mut pooled = pool.try_acquire().expect("pool capacity"); + let buf = pooled.buffer_mut(); + buf[..BENCH_NV12_BYTES].copy_from_slice(&source); + pooled.set_len(BENCH_NV12_BYTES); + black_box(pooled.as_slice().len()); + }); + }, + ); + assert_eq!(pool.currently_in_flight(), 0); +} + +fn bench_legacy_vec_clone_baseline(c: &mut Criterion) { + let source = vec![0xA5u8; BENCH_NV12_BYTES]; + assert_eq!(source.len(), BENCH_NV12_BYTES); + let mut scratch = vec![0u8; BENCH_NV12_BYTES]; + + c.bench_function( + "linux_screen_capture/legacy_baseline/1080p_nv12_vec_clone", + |b| { + b.iter(|| { + scratch.copy_from_slice(&source); + let cloned = scratch.clone(); + black_box(cloned.len()); + }); + }, + ); +} + +const BENCH_4K_WIDTH: usize = 3840; +const BENCH_4K_HEIGHT: usize = 2160; + +fn bench_bgra_to_nv12_4k_conversion(c: &mut Criterion) { + let layout = Nv12Layout { + width: BENCH_4K_WIDTH as u32, + height: BENCH_4K_HEIGHT as u32, + stride_y: BENCH_4K_WIDTH as u32, + stride_uv: BENCH_4K_WIDTH as u32, + }; + let bgra_stride = (BENCH_4K_WIDTH * 4) as u32; + let mut bgra = vec![0u8; BENCH_4K_WIDTH * BENCH_4K_HEIGHT * 4]; + for (index, byte) in bgra.iter_mut().enumerate() { + *byte = (index % 253) as u8; + } + let total = layout.packed_size().expect("4K layout is valid"); + let mut dst = vec![0u8; total]; + + c.bench_function("linux_screen_capture/bgra_to_nv12/4k_unflipped", |b| { + b.iter(|| { + let ok = bgra_to_nv12(layout, &bgra, bgra_stride, &mut dst, false); + assert!(ok); + black_box(dst[0]); + }); + }); + + c.bench_function("linux_screen_capture/bgra_to_nv12/4k_flipped", |b| { + b.iter(|| { + let ok = bgra_to_nv12(layout, &bgra, bgra_stride, &mut dst, true); + assert!(ok); + black_box(dst[total - 1]); + }); + }); +} + +criterion_group!( + benches, + bench_pool_acquire_release_steady_state, + bench_simulated_callback_fill_path, + bench_legacy_vec_clone_baseline, + bench_bgra_to_nv12_4k_conversion, +); +criterion_main!(benches); diff --git a/fluxer_desktop/native/linux-screen-capture/build.rs b/fluxer_desktop/native/linux-screen-capture/build.rs new file mode 100644 index 0000000..d45de40 --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/build.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + napi_build::setup(); +} diff --git a/fluxer_desktop/native/linux-screen-capture/index.d.ts b/fluxer_desktop/native/linux-screen-capture/index.d.ts new file mode 100644 index 0000000..a9061e5 --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/index.d.ts @@ -0,0 +1,171 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import {EventEmitter} from 'node:events'; + +export type LinuxScreenCaptureSourceKind = 'screen' | 'window' | 'game'; + +export interface LinuxScreenCaptureSource { + kind: LinuxScreenCaptureSourceKind; + id: string; + name: string; + width: number; + height: number; + appName?: string; + bundleId?: string; + targetPid?: number; +} + +export declare function listSources(): Promise>; + +export interface LinuxScreenCaptureCapabilities { + process: boolean; + system: boolean; +} + +export interface LinuxScreenCaptureAvailability { + available: boolean; + backend: 'linux-pipewire-portal'; + reason?: string; + detail?: string; + portalVersion?: number; + capabilities: LinuxScreenCaptureCapabilities; +} + +export declare function getAvailability(): Promise; + +export interface LinuxScreenCaptureBackendInfo { + backend: 'linux-pipewire-portal'; + supported: boolean; + reason: string; + portalVersion?: number; + pipewireReachable: boolean; +} + +export declare function getBackendInfo(): LinuxScreenCaptureBackendInfo; + +export interface LinuxGameCaptureLaunchEnvironmentOptions { + env?: NodeJS.ProcessEnv; + nativeRoot?: string; + name?: string; + mode?: 'auto' | 'vulkan' | 'opengl'; + preferDiscreteGpu?: boolean; + forceNvidiaIcd?: boolean | string; +} + +export interface LinuxGameCaptureLaunchEnvironmentResult { + env: NodeJS.ProcessEnv; + diagnostics: { + mode: 'auto' | 'vulkan' | 'opengl'; + preferDiscreteGpu: boolean; + forceNvidiaIcd: boolean; + nvidiaIcdPath: string | null; + bundledVulkanLayerDir: string | null; + systemVulkanLayerManifest: string | null; + vulkanLayerName: string | null; + bundledGlCaptureLib: string | null; + systemGlCaptureLib: string | null; + glCaptureLib: string | null; + licenseBoundary: string; + }; +} + +export declare function getGameCaptureLaunchEnvironment( + options?: LinuxGameCaptureLaunchEnvironmentOptions, +): LinuxGameCaptureLaunchEnvironmentResult; + +export interface ScreenCaptureRect { + x: number; + y: number; + width: number; + height: number; +} + +export interface ScreenCaptureOptions { + sourceId: string; + sourceKind: LinuxScreenCaptureSourceKind; + width?: number; + height?: number; + frameRate?: number; + captureId?: string; + colorRange?: 'full' | 'limited'; + colorSpace?: 'rec709' | 'srgb'; + showCursorClicks?: boolean; + captureRect?: ScreenCaptureRect; + frameSinkHandle?: unknown; + nativeFrameSinkRequired?: boolean; +} + +export interface ScreenCaptureStartResult { + width: number; + height: number; + frameRate: number; + pixelFormat: 'nv12'; +} + +export interface LinuxScreenCaptureDiagnostics { + backend?: string; + activeStrategy?: string; + requestedInjectionMethod?: string; + injectionMethod?: string; + lastFallbackReason?: string; + frameTransport?: 'gpu-dmabuf-requested' | 'host-mapped-cpu-nv12-with-source-dmabuf'; + hostMappedCpuFallback?: boolean; + sourceDmabufMetadataAvailable?: boolean; + requestedImportMode?: string; + importMode?: string; + mapHost?: boolean; + noModifiers?: boolean; + linear?: boolean; + zeroCopy?: boolean; + gpuImportAvailable?: boolean; + deviceUuidAdvertised?: boolean; + supportedImportModes?: Array; + clientConnected?: boolean; + connectedClient?: string; + connectedPid?: number; + sourceId?: string; + sourceKind?: LinuxScreenCaptureSourceKind; + width?: number; + height?: number; + textureFormat?: string; + textureModifier?: string; + frameCounter?: number; + droppedFrameCounter?: number; + laggedFrameCounter?: number; + convertQueueDroppedFrameCounter?: number; + unsupportedFrameCounter?: number; + lastPresentTimestampUs?: number; + lastDiagnostic?: string; + lastAddonError?: string; +} + +export declare const loadError: Error | null; + +export declare function __setBindingForTests(binding: unknown): void; + +export declare interface ScreenCapture { + on(event: 'error', listener: (err: Error) => void): this; + on(event: 'closed', listener: () => void): this; + on(event: 'stalled', listener: (message?: string) => void): this; + on(event: 'diagnostic', listener: (message?: string) => void): this; + on(event: string | symbol, listener: (...args: Array) => void): this; + off(event: 'error', listener: (err: Error) => void): this; + off(event: 'closed', listener: () => void): this; + off(event: 'stalled', listener: (message?: string) => void): this; + off(event: 'diagnostic', listener: (message?: string) => void): this; + off(event: string | symbol, listener: (...args: Array) => void): this; + emit(event: 'error', err: Error): boolean; + emit(event: 'closed'): boolean; + emit(event: 'stalled', message?: string): boolean; + emit(event: 'diagnostic', message?: string): boolean; +} + +export declare class ScreenCapture extends EventEmitter { + constructor(options: ScreenCaptureOptions); + + start(): Promise; + + getDiagnostics(): LinuxScreenCaptureDiagnostics | null; + + stop(): Promise; +} diff --git a/fluxer_desktop/native/linux-screen-capture/index.js b/fluxer_desktop/native/linux-screen-capture/index.js new file mode 100644 index 0000000..9a0413b --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/index.js @@ -0,0 +1,349 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {EventEmitter} = require('node:events'); +const {existsSync} = require('node:fs'); +const {delimiter, join, sep} = require('node:path'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); +const MODULE_NAME = '@fluxer/linux-screen-capture'; +const SKIP_NATIVE_PROBE_ENV = 'FLUXER_LINUX_SCREEN_CAPTURE_SKIP_NATIVE_PROBE'; +const OBS_VKCAPTURE_LAYER_NAME = 'VK_LAYER_OBS_vkcapture_64'; +const DEFAULT_NVIDIA_VULKAN_ICD = '/usr/share/vulkan/icd.d/nvidia_icd.json'; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName() { + if (process.platform !== 'linux') { + throw new Error(`@fluxer/linux-screen-capture is only supported on Linux, got ${process.platform}`); + } + switch (process.arch) { + case 'x64': + return 'linux-screen-capture.linux-x64-gnu.node'; + case 'arm64': + return 'linux-screen-capture.linux-arm64-gnu.node'; + default: + throw new Error(`Unsupported Linux architecture: ${process.arch}`); + } +} + +let binding = null; +let loadError = null; + +if (process.platform === 'linux') { + try { + const nativeRoot = resolveNativeRoot(); + const nativePath = join(nativeRoot, nativeFileName()); + const loaded = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + binding = loaded.binding; + loadError = loaded.loadError; + if (loadError) throw loadError; + } catch (error) { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: 'native loader threw before binding load completed', + cause: error, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + throw loadError; + } +} + +function getBackendInfo() { + if (!binding) { + return { + backend: 'linux-pipewire-portal', + supported: false, + reason: + process.platform === 'linux' + ? `@fluxer/linux-screen-capture native binary unavailable: ${loadError?.message ?? 'unknown reason'}` + : `@fluxer/linux-screen-capture is only supported on Linux, got ${process.platform}`, + portalVersion: undefined, + pipewireReachable: false, + }; + } + return binding.getBackendInfo(); +} + +function getAvailability() { + if (!binding) { + return Promise.resolve({ + available: false, + backend: 'linux-pipewire-portal', + reason: 'unsupported-platform', + capabilities: {process: false, system: false}, + }); + } + return binding.getAvailability(); +} + +function listSources() { + if (!binding) return Promise.resolve([]); + return binding.listSources(); +} + +function prependPathEnv(current, next) { + if (!next) return current; + if (!current) return next; + const parts = current.split(delimiter).filter(Boolean); + return parts.includes(next) ? current : `${next}${delimiter}${current}`; +} + +function appendColonEnv(current, next) { + if (!next) return current; + if (!current) return next; + const parts = current.split(':').filter(Boolean); + return parts.includes(next) ? current : `${current}:${next}`; +} + +function bundledObsVkcaptureRoots(nativeRoot) { + return [join(nativeRoot, 'obs-vkcapture'), join(nativeRoot, 'game-capture', 'obs-vkcapture')]; +} + +function resolveBundledVulkanLayerDir(nativeRoot) { + for (const root of bundledObsVkcaptureRoots(nativeRoot)) { + const jsonPath = join(root, 'obs_vkcapture_64.json'); + const layerPath = join(root, 'libVkLayer_obs_vkcapture.so'); + if (existsSync(jsonPath) && existsSync(layerPath)) return root; + const vulkanRoot = join(root, 'vulkan'); + const vulkanJsonPath = join(vulkanRoot, 'obs_vkcapture_64.json'); + const vulkanLayerPath = join(vulkanRoot, 'libVkLayer_obs_vkcapture.so'); + if (existsSync(vulkanJsonPath) && existsSync(vulkanLayerPath)) return vulkanRoot; + } + return null; +} + +function resolveBundledGlCaptureLib(nativeRoot) { + for (const root of bundledObsVkcaptureRoots(nativeRoot)) { + const candidates = [ + join(root, 'obs_glcapture', 'libobs_glcapture.so'), + join(root, 'opengl', 'libobs_glcapture.so'), + join(root, 'libobs_glcapture.so'), + ]; + for (const candidate of candidates) { + if (existsSync(candidate)) return candidate; + } + } + return null; +} + +function resolveSystemVulkanLayerManifest() { + const candidates = [ + '/usr/share/vulkan/implicit_layer.d/obs_vkcapture_64.json', + '/usr/local/share/vulkan/implicit_layer.d/obs_vkcapture_64.json', + ]; + return candidates.find((candidate) => existsSync(candidate)) ?? null; +} + +function resolveSystemGlCaptureLib() { + const candidates = [ + '/usr/lib/obs_glcapture/libobs_glcapture.so', + '/usr/lib64/obs_glcapture/libobs_glcapture.so', + '/usr/local/lib/obs_glcapture/libobs_glcapture.so', + '/usr/lib/x86_64-linux-gnu/obs_glcapture/libobs_glcapture.so', + '/usr/lib/aarch64-linux-gnu/obs_glcapture/libobs_glcapture.so', + ]; + return candidates.find((candidate) => existsSync(candidate)) ?? null; +} + +function resolveNvidiaIcdPath(forceNvidiaIcd) { + if (typeof forceNvidiaIcd === 'string' && forceNvidiaIcd.length > 0) return forceNvidiaIcd; + if (forceNvidiaIcd !== true) return null; + return existsSync(DEFAULT_NVIDIA_VULKAN_ICD) ? DEFAULT_NVIDIA_VULKAN_ICD : null; +} + +function addDiscreteGpuLaunchEnv(env, options = {}) { + env.DRI_PRIME = env.DRI_PRIME || '1'; + env.__NV_PRIME_RENDER_OFFLOAD = '1'; + env.__VK_LAYER_NV_optimus = 'NVIDIA_only'; + env.__GLX_VENDOR_LIBRARY_NAME = 'nvidia'; + const nvidiaIcdPath = resolveNvidiaIcdPath(options.forceNvidiaIcd); + if (nvidiaIcdPath) env.VK_ICD_FILENAMES = nvidiaIcdPath; + return nvidiaIcdPath; +} + +function getGameCaptureLaunchEnvironment(options = {}) { + const baseEnv = options.env ?? process.env; + const nativeRoot = options.nativeRoot ?? resolveNativeRoot(); + const mode = options.mode === 'vulkan' || options.mode === 'opengl' ? options.mode : 'auto'; + const env = {...baseEnv, OBS_VKCAPTURE: '1'}; + if (options.name) env.OBS_VKCAPTURE_NAME = String(options.name); + const nvidiaIcdPath = options.preferDiscreteGpu ? addDiscreteGpuLaunchEnv(env, options) : null; + + const bundledVulkanLayerDir = resolveBundledVulkanLayerDir(nativeRoot); + const systemVulkanLayerManifest = resolveSystemVulkanLayerManifest(); + if (mode !== 'opengl' && bundledVulkanLayerDir) { + env.VK_ADD_LAYER_PATH = prependPathEnv(env.VK_ADD_LAYER_PATH, bundledVulkanLayerDir); + env.VK_INSTANCE_LAYERS = appendColonEnv(env.VK_INSTANCE_LAYERS, OBS_VKCAPTURE_LAYER_NAME); + } + + const bundledGlCaptureLib = resolveBundledGlCaptureLib(nativeRoot); + const systemGlCaptureLib = resolveSystemGlCaptureLib(); + const glCaptureLib = bundledGlCaptureLib ?? systemGlCaptureLib; + if (mode !== 'vulkan' && glCaptureLib) { + env.LD_PRELOAD = appendColonEnv(env.LD_PRELOAD, glCaptureLib); + } + + return { + env, + diagnostics: { + mode, + preferDiscreteGpu: options.preferDiscreteGpu === true, + forceNvidiaIcd: options.forceNvidiaIcd === true || typeof options.forceNvidiaIcd === 'string', + nvidiaIcdPath, + bundledVulkanLayerDir, + systemVulkanLayerManifest, + vulkanLayerName: bundledVulkanLayerDir ? OBS_VKCAPTURE_LAYER_NAME : null, + bundledGlCaptureLib, + systemGlCaptureLib, + glCaptureLib, + licenseBoundary: + 'obs-vkcapture hook assets are separate GPL-covered runtime tools; Fluxer communicates through the OBS-compatible socket protocol.', + }, + }; +} + +function __setBindingForTests(nextBinding) { + binding = nextBinding; + loadError = null; +} + +class ScreenCapture extends EventEmitter { + constructor(options = {}) { + super(); + if (!binding) { + throw loadError || new Error('@fluxer/linux-screen-capture binding unavailable'); + } + this.sourceId = options.sourceId; + this.sourceKind = options.sourceKind ?? 'screen'; + this.width = options.width ?? 0; + this.height = options.height ?? 0; + this.frameRate = options.frameRate ?? 30; + this.captureId = typeof options.captureId === 'string' ? options.captureId : undefined; + this.colorRange = options.colorRange; + this.colorSpace = options.colorSpace; + this.showCursorClicks = options.showCursorClicks === true; + this.captureRect = options.captureRect; + this.frameSinkHandle = options.frameSinkHandle; + this.nativeFrameSinkRequired = options.nativeFrameSinkRequired === true; + this.started = false; + this.stopped = false; + this.closedEmitted = false; + this.native = new binding.ScreenCapture(); + this.native.setLifecycleCallback((type, message) => { + if (type === 'error') { + this.emit('error', new Error(message || 'Linux PipeWire screen capture stream stopped')); + return; + } + if (type === 'closed' || type === 'closed-clean') { + if (this.stopped) { + this.emitClosedOnce(); + return; + } + this.stopped = true; + Promise.resolve() + .then(() => this.native.stop()) + .catch(() => {}); + this.emitClosedOnce(); + return; + } + if (type === 'stalled' || type === 'diagnostic') { + this.emit(type, message); + } + }); + } + + emitClosedOnce() { + if (this.closedEmitted) return; + this.closedEmitted = true; + this.emit('closed'); + } + + async start() { + if (this.started || this.stopped) return; + this.started = true; + try { + if (this.frameSinkHandle != null) { + if (typeof this.native.setFrameSinkHandle !== 'function') { + throw new Error('@fluxer/linux-screen-capture native binding does not support frame sink handles'); + } + this.native.setFrameSinkHandle(this.frameSinkHandle); + } else if (this.nativeFrameSinkRequired) { + throw new Error('@fluxer/linux-screen-capture native frame sink handle is required'); + } + const result = await this.native.start( + String(this.sourceId ?? ''), + this.sourceKind, + this.width, + this.height, + this.frameRate, + this.captureId, + { + colorRange: this.colorRange, + colorSpace: this.colorSpace, + showCursorClicks: this.showCursorClicks, + captureRect: this.captureRect, + }, + ); + if (result) { + this.width = result.width ?? this.width; + this.height = result.height ?? this.height; + this.frameRate = result.frameRate ?? this.frameRate; + this.pixelFormat = result.pixelFormat ?? 'nv12'; + } + return { + width: this.width, + height: this.height, + frameRate: this.frameRate, + pixelFormat: this.pixelFormat ?? 'nv12', + }; + } catch (error) { + this.stopped = true; + this.emit('error', error instanceof Error ? error : new Error(String(error))); + throw error; + } + } + + async stop() { + if (this.stopped) return; + this.stopped = true; + try { + await this.native.stop(); + } finally { + this.emitClosedOnce(); + } + } + + getDiagnostics() { + const addonDiagnostics = typeof this.native.getDiagnostics === 'function' ? this.native.getDiagnostics() : null; + if (!addonDiagnostics) return null; + return { + ...addonDiagnostics, + sourceId: String(this.sourceId ?? ''), + sourceKind: this.sourceKind, + width: addonDiagnostics.width ?? this.width, + height: addonDiagnostics.height ?? this.height, + }; + } +} + +module.exports = { + ScreenCapture, + getAvailability, + getBackendInfo, + getGameCaptureLaunchEnvironment, + listSources, + loadError, + __setBindingForTests, +}; diff --git a/fluxer_desktop/native/linux-screen-capture/index.test.mjs b/fluxer_desktop/native/linux-screen-capture/index.test.mjs new file mode 100644 index 0000000..926aa4c --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/index.test.mjs @@ -0,0 +1,356 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import assert from 'node:assert/strict'; +import {mkdirSync, mkdtempSync, readFileSync, writeFileSync} from 'node:fs'; +import {tmpdir} from 'node:os'; +import {join} from 'node:path'; +import {afterEach, describe, it} from 'node:test'; +import linuxScreenCapture from './index.js'; + +const {getGameCaptureLaunchEnvironment} = linuxScreenCapture; + +function makeFakeBinding({ + sources = [], + availability = { + available: true, + backend: 'linux-pipewire-portal', + detail: 'portal:5', + capabilities: {process: true, system: true}, + }, +} = {}) { + const calls = []; + const frameSinkHandleCalls = []; + const natives = []; + class FakeNative { + constructor() { + this.lifecycleCallback = undefined; + this.stopCount = 0; + natives.push(this); + } + + setLifecycleCallback(callback) { + this.lifecycleCallback = callback; + } + + setFrameSinkHandle(handle) { + frameSinkHandleCalls.push(handle); + } + + async start(sourceId, sourceKind, width, height, frameRate, captureId, captureOptions) { + calls.push({sourceId, sourceKind, width, height, frameRate, captureId, captureOptions}); + return {width: width || 1920, height: height || 1080, frameRate: frameRate || 30, pixelFormat: 'nv12'}; + } + + async stop() { + this.stopCount += 1; + } + + getDiagnostics() { + return { + portalSessionId: 'portal-session-1', + width: 1280, + height: 720, + }; + } + } + return { + binding: { + ScreenCapture: FakeNative, + listSources: async () => sources, + getAvailability: async () => availability, + getBackendInfo: () => ({ + backend: 'linux-pipewire-portal', + supported: true, + portalVersion: 5, + pipewireReachable: true, + }), + }, + calls, + frameSinkHandleCalls, + natives, + }; +} + +afterEach(() => { + linuxScreenCapture.__setBindingForTests(null); +}); + +describe('linux-screen-capture game capture launch environment', () => { + it('enables OBS Vulkan capture and names the client', () => { + const result = getGameCaptureLaunchEnvironment({ + env: {}, + name: 'fluxer-test', + mode: 'vulkan', + }); + assert.equal(result.env.OBS_VKCAPTURE, '1'); + assert.equal(result.env.OBS_VKCAPTURE_NAME, 'fluxer-test'); + assert.equal(result.env.LD_PRELOAD, undefined); + assert.equal(result.diagnostics.mode, 'vulkan'); + assert.match(result.diagnostics.licenseBoundary, /GPL-covered runtime tools/); + }); + + it('prefers bundled hook assets and can force PRIME/NVIDIA launch variables', () => { + const nativeRoot = mkdtempSync(join(tmpdir(), 'fluxer-linux-screen-capture-')); + const bundledRoot = join(nativeRoot, 'obs-vkcapture'); + const glRoot = join(bundledRoot, 'obs_glcapture'); + mkdirSync(glRoot, {recursive: true}); + writeFileSync(join(bundledRoot, 'obs_vkcapture_64.json'), '{}'); + writeFileSync(join(bundledRoot, 'libVkLayer_obs_vkcapture.so'), ''); + writeFileSync(join(glRoot, 'libobs_glcapture.so'), ''); + + const result = getGameCaptureLaunchEnvironment({ + env: {LD_PRELOAD: '/tmp/existing.so'}, + nativeRoot, + preferDiscreteGpu: true, + }); + + assert.equal(result.env.OBS_VKCAPTURE, '1'); + assert.equal(result.env.__NV_PRIME_RENDER_OFFLOAD, '1'); + assert.equal(result.env.__VK_LAYER_NV_optimus, 'NVIDIA_only'); + assert.equal(result.env.__GLX_VENDOR_LIBRARY_NAME, 'nvidia'); + assert.equal(result.env.DRI_PRIME, '1'); + assert.equal(result.env.VK_ADD_LAYER_PATH, bundledRoot); + assert.equal(result.env.VK_INSTANCE_LAYERS, 'VK_LAYER_OBS_vkcapture_64'); + assert.equal(result.env.LD_PRELOAD, `/tmp/existing.so:${join(glRoot, 'libobs_glcapture.so')}`); + assert.equal(result.diagnostics.forceNvidiaIcd, false); + assert.equal(result.diagnostics.nvidiaIcdPath, null); + assert.equal(result.diagnostics.bundledVulkanLayerDir, bundledRoot); + assert.equal(result.diagnostics.bundledGlCaptureLib, join(glRoot, 'libobs_glcapture.so')); + }); + + it('does not duplicate launch path entries or Vulkan layer names', () => { + const nativeRoot = mkdtempSync(join(tmpdir(), 'fluxer-linux-screen-capture-')); + const bundledRoot = join(nativeRoot, 'obs-vkcapture', 'vulkan'); + mkdirSync(bundledRoot, {recursive: true}); + writeFileSync(join(bundledRoot, 'obs_vkcapture_64.json'), '{}'); + writeFileSync(join(bundledRoot, 'libVkLayer_obs_vkcapture.so'), ''); + + const result = getGameCaptureLaunchEnvironment({ + env: { + VK_ADD_LAYER_PATH: `/tmp/other:${bundledRoot}`, + VK_INSTANCE_LAYERS: 'VK_LAYER_OBS_vkcapture_64:VK_LAYER_KHRONOS_validation', + }, + nativeRoot, + mode: 'vulkan', + }); + + assert.equal(result.env.VK_ADD_LAYER_PATH, `/tmp/other:${bundledRoot}`); + assert.equal(result.env.VK_INSTANCE_LAYERS, 'VK_LAYER_OBS_vkcapture_64:VK_LAYER_KHRONOS_validation'); + assert.equal(result.diagnostics.bundledVulkanLayerDir, bundledRoot); + assert.equal(result.diagnostics.vulkanLayerName, 'VK_LAYER_OBS_vkcapture_64'); + }); + + it('can force a specific NVIDIA Vulkan ICD for hybrid GPU systems', () => { + const result = getGameCaptureLaunchEnvironment({ + env: {}, + preferDiscreteGpu: true, + forceNvidiaIcd: '/tmp/nvidia_icd.json', + }); + + assert.equal(result.env.VK_ICD_FILENAMES, '/tmp/nvidia_icd.json'); + assert.equal(result.diagnostics.forceNvidiaIcd, true); + assert.equal(result.diagnostics.nvidiaIcdPath, '/tmp/nvidia_icd.json'); + }); + + it('keeps obs-vkcapture runtime assets and license notes in the package surface', () => { + const packageJson = JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8')); + assert.equal(packageJson.license, 'AGPL-3.0-or-later'); + assert(packageJson.files.includes('THIRD_PARTY_OBS_VKCAPTURE.md')); + assert(packageJson.files.includes('obs-vkcapture/**/*')); + + const notice = readFileSync(new URL('./THIRD_PARTY_OBS_VKCAPTURE.md', import.meta.url), 'utf8'); + assert.match(notice, /separate third-party runtime component/); + assert.match(notice, /exact upstream license text/); + assert.match(notice, /corresponding source/); + }); +}); + +describe('linux-screen-capture loader wrapper', () => { + it('forwards display and window sources from native binding without rewriting ids', async () => { + const {binding} = makeFakeBinding({ + sources: [ + {kind: 'screen', id: 'pipewire:display:1', name: 'Display 1', width: 2560, height: 1440}, + { + kind: 'window', + id: 'pipewire:window:4242', + name: 'Fluxer', + width: 1280, + height: 720, + appName: 'Fluxer', + targetPid: 1234, + }, + ], + }); + linuxScreenCapture.__setBindingForTests(binding); + + assert.deepEqual(await linuxScreenCapture.listSources(), [ + {kind: 'screen', id: 'pipewire:display:1', name: 'Display 1', width: 2560, height: 1440}, + { + kind: 'window', + id: 'pipewire:window:4242', + name: 'Fluxer', + width: 1280, + height: 720, + appName: 'Fluxer', + targetPid: 1234, + }, + ]); + }); + + it('forwards source id, kind, dimensions, and diagnostics to native binding', async () => { + const {binding, calls} = makeFakeBinding(); + linuxScreenCapture.__setBindingForTests(binding); + const displayCapture = new linuxScreenCapture.ScreenCapture({ + sourceId: 'pipewire:display:1', + sourceKind: 'screen', + width: 2560, + height: 1440, + frameRate: 60, + captureId: 'capture-1', + colorRange: 'full', + colorSpace: 'rec709', + showCursorClicks: true, + captureRect: {x: 10, y: 20, width: 300, height: 200}, + }); + const windowCapture = new linuxScreenCapture.ScreenCapture({ + sourceId: 'pipewire:window:4242', + sourceKind: 'window', + width: 1280, + height: 720, + frameRate: 30, + }); + + await displayCapture.start(); + await windowCapture.start(); + + assert.deepEqual(calls, [ + { + sourceId: 'pipewire:display:1', + sourceKind: 'screen', + width: 2560, + height: 1440, + frameRate: 60, + captureId: 'capture-1', + captureOptions: { + colorRange: 'full', + colorSpace: 'rec709', + showCursorClicks: true, + captureRect: {x: 10, y: 20, width: 300, height: 200}, + }, + }, + { + sourceId: 'pipewire:window:4242', + sourceKind: 'window', + width: 1280, + height: 720, + frameRate: 30, + captureId: undefined, + captureOptions: { + colorRange: undefined, + colorSpace: undefined, + showCursorClicks: false, + captureRect: undefined, + }, + }, + ]); + assert.deepEqual(displayCapture.getDiagnostics(), { + portalSessionId: 'portal-session-1', + width: 1280, + height: 720, + sourceId: 'pipewire:display:1', + sourceKind: 'screen', + }); + assert.deepEqual(windowCapture.getDiagnostics(), { + portalSessionId: 'portal-session-1', + width: 1280, + height: 720, + sourceId: 'pipewire:window:4242', + sourceKind: 'window', + }); + }); + + it('emits closed once for native closed-clean lifecycle events', async () => { + const {binding, natives} = makeFakeBinding(); + linuxScreenCapture.__setBindingForTests(binding); + const capture = new linuxScreenCapture.ScreenCapture({ + sourceId: 'pipewire:display:1', + sourceKind: 'screen', + }); + let closed = 0; + capture.on('closed', () => { + closed += 1; + }); + + await capture.start(); + natives[0].lifecycleCallback('closed-clean', 'capture stopped'); + await capture.stop(); + + assert.equal(closed, 1); + assert.equal(natives[0].stopCount, 1); + }); + + it('reports PipeWire portal capabilities from native binding', async () => { + const {binding} = makeFakeBinding({ + availability: { + available: true, + backend: 'linux-pipewire-portal', + detail: 'system capture disabled by portal', + capabilities: {process: true, system: false}, + }, + }); + linuxScreenCapture.__setBindingForTests(binding); + + assert.deepEqual(await linuxScreenCapture.getAvailability(), { + available: true, + backend: 'linux-pipewire-portal', + detail: 'system capture disabled by portal', + capabilities: {process: true, system: false}, + }); + }); + + it('installs a native frame sink handle once before start', async () => { + const {binding, calls, frameSinkHandleCalls} = makeFakeBinding(); + linuxScreenCapture.__setBindingForTests(binding); + const frameSinkHandle = {native: true}; + const capture = new linuxScreenCapture.ScreenCapture({ + sourceId: 'pipewire:display:1', + sourceKind: 'screen', + frameSinkHandle, + nativeFrameSinkRequired: true, + }); + + await capture.start(); + + assert.deepEqual(frameSinkHandleCalls, [frameSinkHandle]); + assert.deepEqual(calls, [ + { + sourceId: 'pipewire:display:1', + sourceKind: 'screen', + width: 0, + height: 0, + frameRate: 30, + captureId: undefined, + captureOptions: { + colorRange: undefined, + colorSpace: undefined, + showCursorClicks: false, + captureRect: undefined, + }, + }, + ]); + }); + + it('fails before native start when a native frame sink is required but missing', async () => { + const {binding, calls, frameSinkHandleCalls} = makeFakeBinding(); + linuxScreenCapture.__setBindingForTests(binding); + const capture = new linuxScreenCapture.ScreenCapture({ + sourceId: 'pipewire:display:1', + sourceKind: 'screen', + nativeFrameSinkRequired: true, + }); + + await assert.rejects(() => capture.start(), /native frame sink handle is required/); + assert.deepEqual(frameSinkHandleCalls, []); + assert.deepEqual(calls, []); + }); +}); diff --git a/fluxer_desktop/native/linux-screen-capture/loader-diagnostics.cjs b/fluxer_desktop/native/linux-screen-capture/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/linux-screen-capture/package.json b/fluxer_desktop/native/linux-screen-capture/package.json new file mode 100644 index 0000000..db49d10 --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/package.json @@ -0,0 +1,30 @@ +{ + "name": "@fluxer/linux-screen-capture", + "version": "0.0.0", + "description": "", + "private": true, + "license": "AGPL-3.0-or-later", + "os": [ + "linux" + ], + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "THIRD_PARTY_OBS_VKCAPTURE.md", + "obs-vkcapture/**/*", + "linux-screen-capture.linux-x64-gnu.node", + "linux-screen-capture.linux-arm64-gnu.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "cargo test --manifest-path Cargo.toml && node --test index.test.mjs", + "test:loader": "node --test index.test.mjs" + } +} diff --git a/fluxer_desktop/native/linux-screen-capture/src/capture_state.rs b/fluxer_desktop/native/linux-screen-capture/src/capture_state.rs new file mode 100644 index 0000000..5e69173 --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/src/capture_state.rs @@ -0,0 +1,768 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub const MAX_RECONNECT_ATTEMPTS: u32 = 8; +pub const RECONNECT_BACKOFF_BASE_MS: u64 = 100; +pub const RECONNECT_BACKOFF_CAP_MS: u64 = 5_000; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum LinuxCaptureFault { + StreamError(i32), + PortalSessionLost, + NodeRemoved, + PermissionRevoked, + BufferUnderrun, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum LinuxCaptureState { + Connecting { + since_ns: u64, + }, + Active { + since_ns: u64, + }, + Reconnecting { + since_ns: u64, + attempts: u32, + last_fault: LinuxCaptureFault, + }, + Failed { + since_ns: u64, + final_fault: LinuxCaptureFault, + total_attempts: u32, + }, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum LinuxCaptureEvent { + Connected, + Faulted(LinuxCaptureFault), + ReconnectAttempted, + Reset, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum LinuxCaptureAction { + None, + EnterActive, + ScheduleReconnect { attempt: u32, backoff_ms: u64 }, + ReportFailure, + RestartFromFailed, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum LinuxCaptureFsmError { + InvalidInputState, + InvalidOutputState, + InvariantViolated, +} + +pub fn reconnect_backoff_ms(attempts: u32) -> u64 { + assert!(attempts >= 1, "backoff attempts must be >= 1"); + assert!( + attempts <= MAX_RECONNECT_ATTEMPTS, + "backoff attempts must be <= MAX_RECONNECT_ATTEMPTS" + ); + let shift = attempts - 1; + if shift >= 64 { + return RECONNECT_BACKOFF_CAP_MS; + } + let raw = RECONNECT_BACKOFF_BASE_MS.saturating_mul(1u64 << shift); + let capped = if raw > RECONNECT_BACKOFF_CAP_MS { + RECONNECT_BACKOFF_CAP_MS + } else { + raw + }; + assert!( + capped >= RECONNECT_BACKOFF_BASE_MS, + "backoff produced sub-base value" + ); + assert!(capped <= RECONNECT_BACKOFF_CAP_MS, "backoff exceeded cap"); + capped +} + +pub fn transition_linux_capture_state( + state: LinuxCaptureState, + event: LinuxCaptureEvent, + now_ns: u64, +) -> Result<(LinuxCaptureState, LinuxCaptureAction), LinuxCaptureFsmError> { + assert_state_invariants(&state).map_err(|_| LinuxCaptureFsmError::InvalidInputState)?; + let (next, action) = dispatch(state, event, now_ns)?; + assert_state_invariants(&next).map_err(|_| LinuxCaptureFsmError::InvalidOutputState)?; + Ok((next, action)) +} + +fn dispatch( + state: LinuxCaptureState, + event: LinuxCaptureEvent, + now_ns: u64, +) -> Result<(LinuxCaptureState, LinuxCaptureAction), LinuxCaptureFsmError> { + match state { + LinuxCaptureState::Connecting { .. } => from_connecting(state, event, now_ns), + LinuxCaptureState::Active { .. } => from_active(state, event, now_ns), + LinuxCaptureState::Reconnecting { .. } => from_reconnecting(state, event, now_ns), + LinuxCaptureState::Failed { .. } => from_failed(state, event, now_ns), + } +} + +fn from_connecting( + state: LinuxCaptureState, + event: LinuxCaptureEvent, + now_ns: u64, +) -> Result<(LinuxCaptureState, LinuxCaptureAction), LinuxCaptureFsmError> { + debug_assert!(matches!(state, LinuxCaptureState::Connecting { .. })); + match event { + LinuxCaptureEvent::Connected => Ok(( + LinuxCaptureState::Active { since_ns: now_ns }, + LinuxCaptureAction::EnterActive, + )), + LinuxCaptureEvent::Faulted(fault) => { + let next = LinuxCaptureState::Reconnecting { + since_ns: now_ns, + attempts: 1, + last_fault: fault, + }; + let backoff = reconnect_backoff_ms(1); + Ok(( + next, + LinuxCaptureAction::ScheduleReconnect { + attempt: 1, + backoff_ms: backoff, + }, + )) + } + LinuxCaptureEvent::ReconnectAttempted => Ok((state, LinuxCaptureAction::None)), + LinuxCaptureEvent::Reset => Ok((state, LinuxCaptureAction::None)), + } +} + +fn from_active( + state: LinuxCaptureState, + event: LinuxCaptureEvent, + now_ns: u64, +) -> Result<(LinuxCaptureState, LinuxCaptureAction), LinuxCaptureFsmError> { + debug_assert!(matches!(state, LinuxCaptureState::Active { .. })); + match event { + LinuxCaptureEvent::Connected => Ok((state, LinuxCaptureAction::None)), + LinuxCaptureEvent::Faulted(fault) => { + let next = LinuxCaptureState::Reconnecting { + since_ns: now_ns, + attempts: 1, + last_fault: fault, + }; + let backoff = reconnect_backoff_ms(1); + Ok(( + next, + LinuxCaptureAction::ScheduleReconnect { + attempt: 1, + backoff_ms: backoff, + }, + )) + } + LinuxCaptureEvent::ReconnectAttempted => Ok((state, LinuxCaptureAction::None)), + LinuxCaptureEvent::Reset => Ok((state, LinuxCaptureAction::None)), + } +} + +fn from_reconnecting( + state: LinuxCaptureState, + event: LinuxCaptureEvent, + now_ns: u64, +) -> Result<(LinuxCaptureState, LinuxCaptureAction), LinuxCaptureFsmError> { + let (since_ns, attempts, last_fault) = match state { + LinuxCaptureState::Reconnecting { + since_ns, + attempts, + last_fault, + } => (since_ns, attempts, last_fault), + _ => return Err(LinuxCaptureFsmError::InvariantViolated), + }; + assert!( + (1..=MAX_RECONNECT_ATTEMPTS).contains(&attempts), + "attempts out of range" + ); + match event { + LinuxCaptureEvent::Connected => Ok(( + LinuxCaptureState::Active { since_ns: now_ns }, + LinuxCaptureAction::EnterActive, + )), + LinuxCaptureEvent::Faulted(new_fault) => { + handle_fault_while_reconnecting(since_ns, attempts, new_fault, now_ns) + } + LinuxCaptureEvent::ReconnectAttempted => { + handle_reconnect_attempt(since_ns, attempts, last_fault) + } + LinuxCaptureEvent::Reset => Ok((state, LinuxCaptureAction::None)), + } +} + +fn handle_fault_while_reconnecting( + since_ns: u64, + attempts: u32, + new_fault: LinuxCaptureFault, + now_ns: u64, +) -> Result<(LinuxCaptureState, LinuxCaptureAction), LinuxCaptureFsmError> { + assert!(attempts >= 1, "attempts must be >= 1"); + assert!( + attempts <= MAX_RECONNECT_ATTEMPTS, + "attempts must be <= MAX_RECONNECT_ATTEMPTS" + ); + if attempts >= MAX_RECONNECT_ATTEMPTS { + let next = LinuxCaptureState::Failed { + since_ns: now_ns, + final_fault: new_fault, + total_attempts: attempts, + }; + return Ok((next, LinuxCaptureAction::ReportFailure)); + } + let bumped = attempts + 1; + let next = LinuxCaptureState::Reconnecting { + since_ns, + attempts: bumped, + last_fault: new_fault, + }; + let backoff = reconnect_backoff_ms(bumped); + Ok(( + next, + LinuxCaptureAction::ScheduleReconnect { + attempt: bumped, + backoff_ms: backoff, + }, + )) +} + +fn handle_reconnect_attempt( + since_ns: u64, + attempts: u32, + last_fault: LinuxCaptureFault, +) -> Result<(LinuxCaptureState, LinuxCaptureAction), LinuxCaptureFsmError> { + assert!(attempts >= 1, "attempts must be >= 1"); + assert!( + attempts <= MAX_RECONNECT_ATTEMPTS, + "attempts must be <= MAX_RECONNECT_ATTEMPTS" + ); + let capped = if attempts >= MAX_RECONNECT_ATTEMPTS { + MAX_RECONNECT_ATTEMPTS + } else { + attempts + 1 + }; + let next = LinuxCaptureState::Reconnecting { + since_ns, + attempts: capped, + last_fault, + }; + let backoff = reconnect_backoff_ms(capped); + Ok(( + next, + LinuxCaptureAction::ScheduleReconnect { + attempt: capped, + backoff_ms: backoff, + }, + )) +} + +fn from_failed( + state: LinuxCaptureState, + event: LinuxCaptureEvent, + now_ns: u64, +) -> Result<(LinuxCaptureState, LinuxCaptureAction), LinuxCaptureFsmError> { + debug_assert!(matches!(state, LinuxCaptureState::Failed { .. })); + match event { + LinuxCaptureEvent::Connected => Ok((state, LinuxCaptureAction::None)), + LinuxCaptureEvent::Faulted(_) => Ok((state, LinuxCaptureAction::None)), + LinuxCaptureEvent::ReconnectAttempted => Ok((state, LinuxCaptureAction::None)), + LinuxCaptureEvent::Reset => Ok(( + LinuxCaptureState::Connecting { since_ns: now_ns }, + LinuxCaptureAction::RestartFromFailed, + )), + } +} + +fn assert_state_invariants(state: &LinuxCaptureState) -> Result<(), LinuxCaptureFsmError> { + match state { + LinuxCaptureState::Connecting { .. } => Ok(()), + LinuxCaptureState::Active { .. } => Ok(()), + LinuxCaptureState::Reconnecting { attempts, .. } => { + if *attempts < 1 { + return Err(LinuxCaptureFsmError::InvariantViolated); + } + if *attempts > MAX_RECONNECT_ATTEMPTS { + return Err(LinuxCaptureFsmError::InvariantViolated); + } + Ok(()) + } + LinuxCaptureState::Failed { total_attempts, .. } => { + if *total_attempts > MAX_RECONNECT_ATTEMPTS { + return Err(LinuxCaptureFsmError::InvariantViolated); + } + Ok(()) + } + } +} + +pub type LinuxCaptureListener = Box; + +pub struct LinuxCaptureStateMachine { + state: LinuxCaptureState, + listener: Option, +} + +impl LinuxCaptureStateMachine { + pub fn new(now_ns: u64) -> Self { + let initial = LinuxCaptureState::Connecting { since_ns: now_ns }; + assert!(matches!(initial, LinuxCaptureState::Connecting { .. })); + Self { + state: initial, + listener: None, + } + } + + pub fn with_listener(now_ns: u64, listener: F) -> Self + where + F: FnMut(LinuxCaptureEvent, &LinuxCaptureState) + Send + 'static, + { + let initial = LinuxCaptureState::Connecting { since_ns: now_ns }; + assert!(matches!(initial, LinuxCaptureState::Connecting { .. })); + Self { + state: initial, + listener: Some(Box::new(listener)), + } + } + + pub fn state(&self) -> &LinuxCaptureState { + &self.state + } + + pub fn dispatch( + &mut self, + event: LinuxCaptureEvent, + now_ns: u64, + ) -> Result { + assert_state_invariants(&self.state)?; + let (next, action) = transition_linux_capture_state(self.state, event, now_ns)?; + self.state = next; + if let Some(listener) = self.listener.as_mut() { + listener(event, &self.state); + } + assert_state_invariants(&self.state)?; + Ok(action) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const T0: u64 = 1_000; + const T1: u64 = 2_000; + const T2: u64 = 3_000; + + fn connecting(t: u64) -> LinuxCaptureState { + LinuxCaptureState::Connecting { since_ns: t } + } + + fn active(t: u64) -> LinuxCaptureState { + LinuxCaptureState::Active { since_ns: t } + } + + fn reconnecting(t: u64, attempts: u32, fault: LinuxCaptureFault) -> LinuxCaptureState { + LinuxCaptureState::Reconnecting { + since_ns: t, + attempts, + last_fault: fault, + } + } + + fn failed(t: u64, fault: LinuxCaptureFault, total: u32) -> LinuxCaptureState { + LinuxCaptureState::Failed { + since_ns: t, + final_fault: fault, + total_attempts: total, + } + } + + #[test] + fn connecting_plus_connected_goes_active() { + let (next, action) = + transition_linux_capture_state(connecting(T0), LinuxCaptureEvent::Connected, T1) + .unwrap(); + assert_eq!(next, active(T1)); + assert_eq!(action, LinuxCaptureAction::EnterActive); + } + + #[test] + fn connecting_plus_faulted_goes_reconnecting() { + let fault = LinuxCaptureFault::StreamError(-13); + let (next, action) = + transition_linux_capture_state(connecting(T0), LinuxCaptureEvent::Faulted(fault), T1) + .unwrap(); + assert_eq!(next, reconnecting(T1, 1, fault)); + assert_eq!( + action, + LinuxCaptureAction::ScheduleReconnect { + attempt: 1, + backoff_ms: 100 + } + ); + } + + #[test] + fn active_plus_faulted_goes_reconnecting() { + let fault = LinuxCaptureFault::PortalSessionLost; + let (next, action) = + transition_linux_capture_state(active(T0), LinuxCaptureEvent::Faulted(fault), T1) + .unwrap(); + assert_eq!(next, reconnecting(T1, 1, fault)); + assert_eq!( + action, + LinuxCaptureAction::ScheduleReconnect { + attempt: 1, + backoff_ms: 100 + } + ); + } + + #[test] + fn active_plus_reconnect_attempted_is_noop() { + let (next, action) = + transition_linux_capture_state(active(T0), LinuxCaptureEvent::ReconnectAttempted, T1) + .unwrap(); + assert_eq!(next, active(T0)); + assert_eq!(action, LinuxCaptureAction::None); + } + + #[test] + fn active_plus_connected_is_noop() { + let (next, action) = + transition_linux_capture_state(active(T0), LinuxCaptureEvent::Connected, T1).unwrap(); + assert_eq!(next, active(T0)); + assert_eq!(action, LinuxCaptureAction::None); + } + + #[test] + fn reconnecting_plus_connected_goes_active() { + let fault = LinuxCaptureFault::NodeRemoved; + let (next, action) = transition_linux_capture_state( + reconnecting(T0, 3, fault), + LinuxCaptureEvent::Connected, + T1, + ) + .unwrap(); + assert_eq!(next, active(T1)); + assert_eq!(action, LinuxCaptureAction::EnterActive); + } + + #[test] + fn reconnecting_plus_reconnect_attempted_increments() { + let fault = LinuxCaptureFault::NodeRemoved; + let (next, action) = transition_linux_capture_state( + reconnecting(T0, 2, fault), + LinuxCaptureEvent::ReconnectAttempted, + T1, + ) + .unwrap(); + assert_eq!(next, reconnecting(T0, 3, fault)); + assert_eq!( + action, + LinuxCaptureAction::ScheduleReconnect { + attempt: 3, + backoff_ms: 400 + } + ); + } + + #[test] + fn reconnecting_plus_reconnect_attempted_caps_at_max() { + let fault = LinuxCaptureFault::BufferUnderrun; + let (next, action) = transition_linux_capture_state( + reconnecting(T0, MAX_RECONNECT_ATTEMPTS, fault), + LinuxCaptureEvent::ReconnectAttempted, + T1, + ) + .unwrap(); + assert_eq!(next, reconnecting(T0, MAX_RECONNECT_ATTEMPTS, fault)); + assert_eq!( + action, + LinuxCaptureAction::ScheduleReconnect { + attempt: MAX_RECONNECT_ATTEMPTS, + backoff_ms: RECONNECT_BACKOFF_CAP_MS, + } + ); + } + + #[test] + fn reconnecting_plus_faulted_below_max_increments() { + let old = LinuxCaptureFault::NodeRemoved; + let new_fault = LinuxCaptureFault::StreamError(-7); + let (next, action) = transition_linux_capture_state( + reconnecting(T0, 2, old), + LinuxCaptureEvent::Faulted(new_fault), + T1, + ) + .unwrap(); + assert_eq!(next, reconnecting(T0, 3, new_fault)); + assert_eq!( + action, + LinuxCaptureAction::ScheduleReconnect { + attempt: 3, + backoff_ms: 400 + } + ); + } + + #[test] + fn reconnecting_plus_faulted_at_max_goes_failed() { + let old = LinuxCaptureFault::NodeRemoved; + let new_fault = LinuxCaptureFault::PermissionRevoked; + let (next, action) = transition_linux_capture_state( + reconnecting(T0, MAX_RECONNECT_ATTEMPTS, old), + LinuxCaptureEvent::Faulted(new_fault), + T2, + ) + .unwrap(); + assert_eq!(next, failed(T2, new_fault, MAX_RECONNECT_ATTEMPTS)); + assert_eq!(action, LinuxCaptureAction::ReportFailure); + } + + #[test] + fn failed_plus_reset_goes_connecting() { + let fault = LinuxCaptureFault::StreamError(-99); + let (next, action) = transition_linux_capture_state( + failed(T0, fault, MAX_RECONNECT_ATTEMPTS), + LinuxCaptureEvent::Reset, + T1, + ) + .unwrap(); + assert_eq!(next, connecting(T1)); + assert_eq!(action, LinuxCaptureAction::RestartFromFailed); + } + + #[test] + fn failed_plus_connected_stays_failed() { + let fault = LinuxCaptureFault::StreamError(-99); + let prior = failed(T0, fault, MAX_RECONNECT_ATTEMPTS); + let (next, action) = + transition_linux_capture_state(prior, LinuxCaptureEvent::Connected, T1).unwrap(); + assert_eq!(next, prior); + assert_eq!(action, LinuxCaptureAction::None); + } + + #[test] + fn failed_plus_faulted_stays_failed() { + let fault = LinuxCaptureFault::StreamError(-99); + let prior = failed(T0, fault, MAX_RECONNECT_ATTEMPTS); + let (next, action) = transition_linux_capture_state( + prior, + LinuxCaptureEvent::Faulted(LinuxCaptureFault::NodeRemoved), + T1, + ) + .unwrap(); + assert_eq!(next, prior); + assert_eq!(action, LinuxCaptureAction::None); + } + + #[test] + fn connecting_plus_reset_is_noop() { + let (next, action) = + transition_linux_capture_state(connecting(T0), LinuxCaptureEvent::Reset, T1).unwrap(); + assert_eq!(next, connecting(T0)); + assert_eq!(action, LinuxCaptureAction::None); + } + + #[test] + fn backoff_increases_exponentially_and_caps() { + assert_eq!(reconnect_backoff_ms(1), 100); + assert_eq!(reconnect_backoff_ms(2), 200); + assert_eq!(reconnect_backoff_ms(3), 400); + assert_eq!(reconnect_backoff_ms(4), 800); + assert_eq!(reconnect_backoff_ms(5), 1_600); + assert_eq!(reconnect_backoff_ms(6), 3_200); + assert_eq!(reconnect_backoff_ms(7), RECONNECT_BACKOFF_CAP_MS); + assert_eq!(reconnect_backoff_ms(8), RECONNECT_BACKOFF_CAP_MS); + } + + #[test] + fn backoff_is_monotonic_until_cap() { + let mut prev = 0u64; + for n in 1..=MAX_RECONNECT_ATTEMPTS { + let b = reconnect_backoff_ms(n); + assert!(b >= prev, "backoff must be non-decreasing"); + assert!(b <= RECONNECT_BACKOFF_CAP_MS, "backoff must respect cap"); + prev = b; + } + } + + #[test] + fn max_reconnect_attempts_reached_via_faults_goes_failed() { + let mut state = active(T0); + let mut now = T0; + for expected_attempts in 1..=MAX_RECONNECT_ATTEMPTS { + now += 1; + let fault = LinuxCaptureFault::StreamError(expected_attempts as i32); + let (next, _action) = + transition_linux_capture_state(state, LinuxCaptureEvent::Faulted(fault), now) + .unwrap(); + match next { + LinuxCaptureState::Reconnecting { attempts, .. } => { + assert_eq!(attempts, expected_attempts); + } + _ => panic!("expected Reconnecting, got {:?}", next), + } + state = next; + } + now += 1; + let final_fault = LinuxCaptureFault::PermissionRevoked; + let (next, action) = + transition_linux_capture_state(state, LinuxCaptureEvent::Faulted(final_fault), now) + .unwrap(); + assert!(matches!(next, LinuxCaptureState::Failed { .. })); + assert_eq!(action, LinuxCaptureAction::ReportFailure); + } + + #[test] + fn invalid_input_state_rejected() { + let bad = LinuxCaptureState::Reconnecting { + since_ns: T0, + attempts: 0, + last_fault: LinuxCaptureFault::NodeRemoved, + }; + let err = + transition_linux_capture_state(bad, LinuxCaptureEvent::Connected, T1).unwrap_err(); + assert_eq!(err, LinuxCaptureFsmError::InvalidInputState); + } + + #[test] + fn invalid_input_state_attempts_too_high_rejected() { + let bad = LinuxCaptureState::Reconnecting { + since_ns: T0, + attempts: MAX_RECONNECT_ATTEMPTS + 1, + last_fault: LinuxCaptureFault::NodeRemoved, + }; + let err = + transition_linux_capture_state(bad, LinuxCaptureEvent::Connected, T1).unwrap_err(); + assert_eq!(err, LinuxCaptureFsmError::InvalidInputState); + } + + #[test] + fn determinism_same_input_same_output() { + let state = reconnecting(T0, 3, LinuxCaptureFault::NodeRemoved); + let ev = LinuxCaptureEvent::Faulted(LinuxCaptureFault::StreamError(-5)); + let a = transition_linux_capture_state(state, ev, T2).unwrap(); + let b = transition_linux_capture_state(state, ev, T2).unwrap(); + let c = transition_linux_capture_state(state, ev, T2).unwrap(); + assert_eq!(a, b); + assert_eq!(b, c); + } + + struct Lcg(u64); + + impl Lcg { + fn next(&mut self) -> u64 { + self.0 = self + .0 + .wrapping_mul(6364136223846793005) + .wrapping_add(1442695040888963407); + self.0 + } + + fn next_u32(&mut self, bound: u32) -> u32 { + assert!(bound > 0); + (self.next() % bound as u64) as u32 + } + } + + fn random_event(rng: &mut Lcg) -> LinuxCaptureEvent { + let kind = rng.next_u32(4); + match kind { + 0 => LinuxCaptureEvent::Connected, + 1 => LinuxCaptureEvent::Faulted(random_fault(rng)), + 2 => LinuxCaptureEvent::ReconnectAttempted, + 3 => LinuxCaptureEvent::Reset, + _ => unreachable!("rng bounded to 4"), + } + } + + fn random_fault(rng: &mut Lcg) -> LinuxCaptureFault { + let kind = rng.next_u32(5); + match kind { + 0 => LinuxCaptureFault::StreamError(-(rng.next_u32(128) as i32)), + 1 => LinuxCaptureFault::PortalSessionLost, + 2 => LinuxCaptureFault::NodeRemoved, + 3 => LinuxCaptureFault::PermissionRevoked, + 4 => LinuxCaptureFault::BufferUnderrun, + _ => unreachable!("rng bounded to 5"), + } + } + + #[test] + fn invariants_hold_across_random_transitions() { + let mut rng = Lcg(0x9E37_79B9_7F4A_7C15); + let mut state = connecting(T0); + let mut now = T0; + for _ in 0..1_000 { + now = now.wrapping_add(1); + let ev = random_event(&mut rng); + let result = transition_linux_capture_state(state, ev, now); + let next = result.expect("invariant must hold for any valid input"); + state = next.0; + assert!(assert_state_invariants(&state).is_ok()); + } + } + + #[test] + fn state_machine_dispatches_through_recovery() { + use std::sync::{Arc, Mutex}; + let log: Arc>> = + Arc::new(Mutex::new(Vec::with_capacity(8))); + let log_clone = log.clone(); + let mut fsm = LinuxCaptureStateMachine::with_listener(T0, move |ev, st| { + log_clone.lock().unwrap().push((ev, *st)); + }); + assert!(matches!(fsm.state(), LinuxCaptureState::Connecting { .. })); + let a = fsm.dispatch(LinuxCaptureEvent::Connected, T1).unwrap(); + assert_eq!(a, LinuxCaptureAction::EnterActive); + assert!(matches!(fsm.state(), LinuxCaptureState::Active { .. })); + let b = fsm + .dispatch( + LinuxCaptureEvent::Faulted(LinuxCaptureFault::PortalSessionLost), + T2, + ) + .unwrap(); + assert_eq!( + b, + LinuxCaptureAction::ScheduleReconnect { + attempt: 1, + backoff_ms: 100 + } + ); + assert!(matches!( + fsm.state(), + LinuxCaptureState::Reconnecting { .. } + )); + let c = fsm.dispatch(LinuxCaptureEvent::Connected, T2 + 1).unwrap(); + assert_eq!(c, LinuxCaptureAction::EnterActive); + assert!(matches!(fsm.state(), LinuxCaptureState::Active { .. })); + let events = log.lock().unwrap(); + assert_eq!(events.len(), 3); + } + + #[test] + fn state_machine_dispatches_failure_then_reset() { + let mut fsm = LinuxCaptureStateMachine::new(T0); + fsm.dispatch(LinuxCaptureEvent::Connected, T0 + 1).unwrap(); + for i in 0..=MAX_RECONNECT_ATTEMPTS { + let fault = LinuxCaptureFault::StreamError(-(i as i32) - 1); + fsm.dispatch(LinuxCaptureEvent::Faulted(fault), T0 + 10 + i as u64) + .unwrap(); + } + match fsm.state() { + LinuxCaptureState::Failed { total_attempts, .. } => { + assert_eq!(*total_attempts, MAX_RECONNECT_ATTEMPTS); + } + other => panic!("expected Failed, got {:?}", other), + } + let action = fsm.dispatch(LinuxCaptureEvent::Reset, T0 + 1_000).unwrap(); + assert_eq!(action, LinuxCaptureAction::RestartFromFailed); + assert!(matches!(fsm.state(), LinuxCaptureState::Connecting { .. })); + } +} diff --git a/fluxer_desktop/native/linux-screen-capture/src/frame_buffer_pool.rs b/fluxer_desktop/native/linux-screen-capture/src/frame_buffer_pool.rs new file mode 100644 index 0000000..cef6964 --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/src/frame_buffer_pool.rs @@ -0,0 +1,425 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::cell::UnsafeCell; +use std::sync::Arc; +use std::sync::atomic::{AtomicU64, Ordering}; + +use fluxer_screen_frame_bus::frame_pool::{ + CpuFrameBuilder, FramePool, FramePoolError, PooledFrame, +}; +use fluxer_screen_frame_bus::{FrameData, SharedFrameBytes}; + +pub const LINUX_SCREEN_FRAME_POOL_CAP: usize = 8; +pub const LINUX_FRAME_DIM_MAX: usize = 8192; +pub const LINUX_FRAME_BYTES_MAX: usize = LINUX_FRAME_DIM_MAX * LINUX_FRAME_DIM_MAX * 4; + +struct SlotCell { + bytes: UnsafeCell>, +} + +unsafe impl Send for SlotCell {} +unsafe impl Sync for SlotCell {} + +impl SharedFrameBytes for SlotCell { + fn bytes(&self) -> &[u8] { + unsafe { (*self.bytes.get()).as_ref() } + } +} + +pub struct LinuxFrameBufferPool { + capacity_pool: FramePool, + slot_buffers: Box<[Arc]>, + bytes_per_buffer: usize, + frames_dropped_pool_exhausted: AtomicU64, + frames_dropped_oversized: AtomicU64, +} + +#[derive(Debug)] +pub enum LinuxFrameBufferPoolError { + BytesPerBufferZero, + BytesPerBufferOverflow, + CapacityPoolFailed(FramePoolError), +} + +impl std::fmt::Display for LinuxFrameBufferPoolError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::BytesPerBufferZero => f.write_str("bytes_per_buffer must be positive"), + Self::BytesPerBufferOverflow => { + f.write_str("bytes_per_buffer exceeds LINUX_FRAME_BYTES_MAX") + } + Self::CapacityPoolFailed(err) => write!(f, "capacity pool init failed: {err}"), + } + } +} + +impl std::error::Error for LinuxFrameBufferPoolError {} + +impl LinuxFrameBufferPool { + pub fn new(bytes_per_buffer: usize) -> Result, LinuxFrameBufferPoolError> { + assert!(bytes_per_buffer > 0); + assert!(bytes_per_buffer <= LINUX_FRAME_BYTES_MAX); + if bytes_per_buffer == 0 { + return Err(LinuxFrameBufferPoolError::BytesPerBufferZero); + } + if bytes_per_buffer > LINUX_FRAME_BYTES_MAX { + return Err(LinuxFrameBufferPoolError::BytesPerBufferOverflow); + } + let capacity_pool = + CpuFrameBuilder::build_pool_with_capacity(LINUX_SCREEN_FRAME_POOL_CAP, 1) + .map_err(LinuxFrameBufferPoolError::CapacityPoolFailed)?; + assert_eq!(capacity_pool.capacity(), LINUX_SCREEN_FRAME_POOL_CAP); + let mut buffers: Vec> = Vec::with_capacity(LINUX_SCREEN_FRAME_POOL_CAP); + for _ in 0..LINUX_SCREEN_FRAME_POOL_CAP { + let buf: Box<[u8]> = vec![0u8; bytes_per_buffer].into_boxed_slice(); + assert_eq!(buf.len(), bytes_per_buffer); + buffers.push(Arc::new(SlotCell { + bytes: UnsafeCell::new(buf), + })); + } + assert_eq!(buffers.len(), LINUX_SCREEN_FRAME_POOL_CAP); + Ok(Arc::new(Self { + capacity_pool, + slot_buffers: buffers.into_boxed_slice(), + bytes_per_buffer, + frames_dropped_pool_exhausted: AtomicU64::new(0), + frames_dropped_oversized: AtomicU64::new(0), + })) + } + + pub fn capacity(&self) -> usize { + assert_eq!(self.slot_buffers.len(), LINUX_SCREEN_FRAME_POOL_CAP); + assert_eq!(self.capacity_pool.capacity(), LINUX_SCREEN_FRAME_POOL_CAP); + LINUX_SCREEN_FRAME_POOL_CAP + } + + pub fn bytes_per_buffer(&self) -> usize { + assert!(self.bytes_per_buffer > 0); + assert!(self.bytes_per_buffer <= LINUX_FRAME_BYTES_MAX); + self.bytes_per_buffer + } + + pub fn frames_dropped_pool_exhausted(&self) -> u64 { + let dropped = self.frames_dropped_pool_exhausted.load(Ordering::Relaxed); + assert!(dropped <= u64::MAX / 2); + dropped + } + + pub fn frames_dropped_oversized(&self) -> u64 { + let dropped = self.frames_dropped_oversized.load(Ordering::Relaxed); + assert!(dropped <= u64::MAX / 2); + dropped + } + + pub fn note_frame_dropped_oversized(&self) { + let before = self + .frames_dropped_oversized + .fetch_add(1, Ordering::Relaxed); + assert!(before < u64::MAX / 2); + } + + pub fn currently_in_flight(&self) -> u64 { + let in_flight = self.capacity_pool.currently_in_flight(); + assert!(in_flight as usize <= LINUX_SCREEN_FRAME_POOL_CAP); + in_flight + } + + pub fn try_acquire(self: &Arc) -> Option { + let pool_arc = Arc::clone(self); + let pooled = match self.capacity_pool.try_acquire() { + Some(p) => p, + None => { + let before = self + .frames_dropped_pool_exhausted + .fetch_add(1, Ordering::Relaxed); + assert!(before < u64::MAX / 2); + return None; + } + }; + let slot_index = pooled.slot_index(); + assert!(slot_index < self.slot_buffers.len()); + Some(PooledFrameBuffer { + pool: pool_arc, + capacity_token: pooled, + slot_index, + len: 0, + }) + } +} + +pub struct PooledFrameBuffer { + pool: Arc, + capacity_token: PooledFrame, + slot_index: usize, + len: usize, +} + +impl PooledFrameBuffer { + pub fn buffer_mut(&mut self) -> &mut [u8] { + assert!(self.slot_index < self.pool.slot_buffers.len()); + let cell = &self.pool.slot_buffers[self.slot_index]; + let slice: &mut [u8] = unsafe { (*cell.bytes.get()).as_mut() }; + assert_eq!(slice.len(), self.pool.bytes_per_buffer); + slice + } + + pub fn set_len(&mut self, len: usize) { + assert!(len <= self.pool.bytes_per_buffer); + assert!(len <= LINUX_FRAME_BYTES_MAX); + self.len = len; + } + + pub fn as_slice(&self) -> &[u8] { + assert!(self.slot_index < self.pool.slot_buffers.len()); + assert!(self.len <= self.pool.bytes_per_buffer); + let cell = &self.pool.slot_buffers[self.slot_index]; + let slice: &[u8] = unsafe { (*cell.bytes.get()).as_ref() }; + &slice[..self.len] + } + + pub fn into_shared_frame_data(self) -> FrameData { + assert!(self.slot_index < self.pool.slot_buffers.len()); + assert!(self.len <= self.pool.bytes_per_buffer); + let Self { + pool, + capacity_token, + slot_index, + len, + } = self; + let slot: Arc = Arc::clone(&pool.slot_buffers[slot_index]); + let source: Arc = slot; + FrameData::from_shared(source, len, Some(capacity_token)) + } + + pub fn len(&self) -> usize { + assert!(self.len <= self.pool.bytes_per_buffer); + self.len + } + + pub fn is_empty(&self) -> bool { + let empty = self.len == 0; + assert!(empty == (self.len == 0)); + empty + } + + pub fn slot_index(&self) -> usize { + assert!(self.slot_index < self.pool.slot_buffers.len()); + self.slot_index + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const TEST_BYTES_PER_BUFFER: usize = 64 * 64 * 3 / 2; + + #[test] + fn pool_capacity_is_eight_to_match_obs_encode_ring() { + assert_eq!(LINUX_SCREEN_FRAME_POOL_CAP, 8); + let pool = LinuxFrameBufferPool::new(TEST_BYTES_PER_BUFFER).expect("pool init"); + assert_eq!(pool.capacity(), LINUX_SCREEN_FRAME_POOL_CAP); + assert_eq!(pool.bytes_per_buffer(), TEST_BYTES_PER_BUFFER); + } + + #[test] + #[should_panic] + fn pool_rejects_zero_bytes_per_buffer() { + let _ = LinuxFrameBufferPool::new(0); + } + + #[test] + #[should_panic] + fn pool_rejects_overflow_bytes_per_buffer() { + let _ = LinuxFrameBufferPool::new(LINUX_FRAME_BYTES_MAX + 1); + } + + #[test] + fn pool_supports_4k_nv12_frames() { + const NV12_4K_BYTES: usize = 3840 * 2160 * 3 / 2; + const { assert!(NV12_4K_BYTES <= LINUX_FRAME_BYTES_MAX) }; + let pool = LinuxFrameBufferPool::new(NV12_4K_BYTES).expect("4K NV12 pool init"); + assert_eq!(pool.bytes_per_buffer(), NV12_4K_BYTES); + } + + #[test] + fn pool_byte_cap_covers_max_negotiable_stream_dimensions() { + assert_eq!(LINUX_FRAME_DIM_MAX, 8192); + const NV12_MAX_DIM_BYTES: usize = LINUX_FRAME_DIM_MAX * LINUX_FRAME_DIM_MAX * 3 / 2; + const { assert!(NV12_MAX_DIM_BYTES <= LINUX_FRAME_BYTES_MAX) }; + } + + #[test] + fn oversized_drop_counter_starts_at_zero_and_increments() { + let pool = LinuxFrameBufferPool::new(TEST_BYTES_PER_BUFFER).expect("pool init"); + assert_eq!(pool.frames_dropped_oversized(), 0); + pool.note_frame_dropped_oversized(); + assert_eq!(pool.frames_dropped_oversized(), 1); + pool.note_frame_dropped_oversized(); + assert_eq!(pool.frames_dropped_oversized(), 2); + assert_eq!(pool.frames_dropped_pool_exhausted(), 0); + } + + #[test] + fn acquire_release_cycle_returns_pooled_buffer_to_circulation() { + let pool = LinuxFrameBufferPool::new(TEST_BYTES_PER_BUFFER).expect("pool init"); + assert_eq!(pool.currently_in_flight(), 0); + + let mut pooled = pool.try_acquire().expect("slot available"); + assert_eq!(pool.currently_in_flight(), 1); + let buf = pooled.buffer_mut(); + assert_eq!(buf.len(), TEST_BYTES_PER_BUFFER); + buf[0] = 0xAB; + buf[TEST_BYTES_PER_BUFFER - 1] = 0xCD; + pooled.set_len(TEST_BYTES_PER_BUFFER); + assert_eq!(pooled.as_slice().len(), TEST_BYTES_PER_BUFFER); + assert_eq!(pooled.as_slice()[0], 0xAB); + assert_eq!(pooled.as_slice()[TEST_BYTES_PER_BUFFER - 1], 0xCD); + drop(pooled); + + assert_eq!(pool.currently_in_flight(), 0); + let _again = pool.try_acquire().expect("slot returned to pool"); + assert_eq!(pool.currently_in_flight(), 1); + } + + #[test] + fn ninth_acquire_when_eight_in_flight_returns_none_and_increments_dropped_counter() { + let pool = LinuxFrameBufferPool::new(TEST_BYTES_PER_BUFFER).expect("pool init"); + let mut held = Vec::with_capacity(LINUX_SCREEN_FRAME_POOL_CAP); + for i in 0..LINUX_SCREEN_FRAME_POOL_CAP { + let slot = pool.try_acquire().expect("first eight must acquire"); + assert_eq!(pool.currently_in_flight() as usize, i + 1); + held.push(slot); + } + assert_eq!(held.len(), 8); + assert_eq!(pool.frames_dropped_pool_exhausted(), 0); + + let ninth = pool.try_acquire(); + assert!(ninth.is_none(), "ninth acquire must skip-don't-block"); + assert_eq!(pool.frames_dropped_pool_exhausted(), 1); + + let tenth = pool.try_acquire(); + assert!(tenth.is_none(), "tenth acquire must skip-don't-block"); + assert_eq!(pool.frames_dropped_pool_exhausted(), 2); + + drop(held); + assert_eq!(pool.currently_in_flight(), 0); + + let revived = pool.try_acquire().expect("slot returned after releases"); + assert_eq!(pool.currently_in_flight(), 1); + drop(revived); + } + + #[test] + fn buffer_contents_persist_across_acquires_when_slot_recycled() { + let pool = LinuxFrameBufferPool::new(TEST_BYTES_PER_BUFFER).expect("pool init"); + let mut first = pool.try_acquire().expect("slot"); + let slot_index_first = first.slot_index(); + first.buffer_mut().fill(0x42); + first.set_len(TEST_BYTES_PER_BUFFER); + drop(first); + + let mut second = pool.try_acquire().expect("slot returned"); + assert_eq!(second.slot_index(), slot_index_first); + let slice = second.buffer_mut(); + assert_eq!(slice[0], 0x42); + assert_eq!(slice[TEST_BYTES_PER_BUFFER / 2], 0x42); + slice.fill(0x00); + drop(second); + } + + #[test] + fn into_shared_frame_data_round_trips_bytes_and_returns_slot_on_drop() { + let pool = LinuxFrameBufferPool::new(TEST_BYTES_PER_BUFFER).expect("pool init"); + let mut pooled = pool.try_acquire().expect("slot"); + pooled.buffer_mut().fill(0x5A); + pooled.set_len(TEST_BYTES_PER_BUFFER / 2); + let slot_index = pooled.slot_index(); + + let shared = pooled.into_shared_frame_data(); + assert_eq!(pool.currently_in_flight(), 1); + assert!(shared.is_shared()); + assert_eq!(shared.len(), TEST_BYTES_PER_BUFFER / 2); + assert!(shared.as_slice().iter().all(|b| *b == 0x5A)); + + drop(shared); + assert_eq!(pool.currently_in_flight(), 0); + let again = pool.try_acquire().expect("slot returned by shared drop"); + assert_eq!(again.slot_index(), slot_index); + } + + #[test] + fn shared_frame_data_keeps_slot_bytes_alive_after_pool_drop() { + let pool = LinuxFrameBufferPool::new(TEST_BYTES_PER_BUFFER).expect("pool init"); + let mut pooled = pool.try_acquire().expect("slot"); + pooled.buffer_mut().fill(0x3C); + pooled.set_len(8); + let shared = pooled.into_shared_frame_data(); + drop(pool); + assert_eq!(shared.as_slice(), &[0x3C; 8]); + } + + #[test] + fn exhaustion_drop_policy_and_counters_unchanged_while_shared_frames_held() { + let pool = LinuxFrameBufferPool::new(TEST_BYTES_PER_BUFFER).expect("pool init"); + let mut held = Vec::with_capacity(LINUX_SCREEN_FRAME_POOL_CAP); + for _ in 0..LINUX_SCREEN_FRAME_POOL_CAP { + let mut pooled = pool.try_acquire().expect("slot within capacity"); + pooled.set_len(1); + held.push(pooled.into_shared_frame_data()); + } + assert_eq!( + pool.currently_in_flight() as usize, + LINUX_SCREEN_FRAME_POOL_CAP + ); + + assert!(pool.try_acquire().is_none()); + assert_eq!(pool.frames_dropped_pool_exhausted(), 1); + + held.clear(); + assert_eq!(pool.currently_in_flight(), 0); + let revived = pool + .try_acquire() + .expect("slots returned after shared drops"); + assert_eq!(pool.frames_dropped_pool_exhausted(), 1); + drop(revived); + } + + #[test] + fn pool_arc_strong_count_stays_bounded_under_acquire_churn() { + let pool = LinuxFrameBufferPool::new(TEST_BYTES_PER_BUFFER).expect("pool init"); + for _ in 0..256 { + let p = pool.try_acquire().expect("slot"); + drop(p); + } + assert!(Arc::strong_count(&pool) <= 4); + assert_eq!(pool.currently_in_flight(), 0); + } + + #[test] + fn multi_thread_acquire_release_never_deadlocks_and_balances_counts() { + use std::sync::Arc as StdArc; + use std::thread; + + let pool = pool_for_multi_thread(); + let mut threads = Vec::with_capacity(4); + for _ in 0..4 { + let pool = StdArc::clone(&pool); + threads.push(thread::spawn(move || { + for _ in 0..256 { + if let Some(p) = pool.try_acquire() { + std::hint::black_box(p.slot_index()); + } + } + })); + } + for h in threads { + h.join().expect("worker completes"); + } + assert_eq!(pool.currently_in_flight(), 0); + } + + fn pool_for_multi_thread() -> Arc { + LinuxFrameBufferPool::new(TEST_BYTES_PER_BUFFER).expect("pool init") + } +} diff --git a/fluxer_desktop/native/linux-screen-capture/src/game_capture.rs b/fluxer_desktop/native/linux-screen-capture/src/game_capture.rs new file mode 100644 index 0000000..a4af0db --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/src/game_capture.rs @@ -0,0 +1,2401 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::env; +use std::ffi::c_void; +use std::mem::{MaybeUninit, size_of}; +use std::os::fd::RawFd; +use std::ptr; +use std::slice; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, Mutex}; +use std::thread::{self, JoinHandle}; +use std::time::{Duration, Instant}; + +use crate::nv12_packing::{Nv12Layout, bgra_to_nv12}; +use crate::pipewire_stream::{ + DmabufFrameMetadata, FrameCallback, LifecycleCallback, LinuxFrameBufferPool, VideoFrame, + VideoFrameData, +}; + +const VKCAPTURE_SOCKET_NAME: &[u8] = b"/com/obsproject/vkcapture"; +const READY_TIMEOUT: Duration = Duration::from_millis(2_000); +const POLL_TIMEOUT_MS: i32 = 10; +const FRAME_ADVANCE_COUNT_MAX: u32 = 1 << 20; +const CLIENT_DATA_TYPE: u8 = 10; +const CLIENT_DATA_SIZE: usize = 128; +const TEXTURE_DATA_TYPE: u8 = 11; +const TEXTURE_DATA_SIZE: usize = 128; +const CONTROL_DATA_SIZE: usize = 32; +const MAX_FPS: u32 = 240; +const IMPORT_MODE_ENV: &str = "FLUXER_LINUX_GAME_CAPTURE_IMPORT_MODE"; +const OBS_IMPORT_MODE_NAMES: [&str; 4] = [ + "default-dmabuf", + "no-modifiers-dmabuf", + "linear-dmabuf", + "linear-host-mapped-dmabuf", +]; + +const DRM_FORMAT_XRGB8888: i32 = fourcc(*b"XR24") as i32; +const DRM_FORMAT_ARGB8888: i32 = fourcc(*b"AR24") as i32; +const DRM_FORMAT_XBGR8888: i32 = fourcc(*b"XB24") as i32; +const DRM_FORMAT_ABGR8888: i32 = fourcc(*b"AB24") as i32; +const DRM_FORMAT_XRGB2101010: i32 = fourcc(*b"XR30") as i32; +const DRM_FORMAT_ARGB2101010: i32 = fourcc(*b"AR30") as i32; +const DRM_FORMAT_XBGR2101010: i32 = fourcc(*b"XB30") as i32; +const DRM_FORMAT_ABGR2101010: i32 = fourcc(*b"AB30") as i32; +const DRM_FORMAT_XBGR16161616: i32 = fourcc(*b"XB48") as i32; +const DRM_FORMAT_ABGR16161616: i32 = fourcc(*b"AB48") as i32; +const DRM_FORMAT_XBGR16161616F: i32 = fourcc(*b"XB4H") as i32; +const DRM_FORMAT_ABGR16161616F: i32 = fourcc(*b"AB4H") as i32; +const DRM_FORMAT_NV12: i32 = fourcc(*b"NV12") as i32; +const DRM_FORMAT_MOD_INVALID: u64 = (1u64 << 56) - 1; +const SUPPORTED_HOST_MAPPED_FORMATS: [i32; 12] = [ + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_ARGB2101010, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_ABGR2101010, + DRM_FORMAT_XBGR16161616, + DRM_FORMAT_ABGR16161616, + DRM_FORMAT_XBGR16161616F, + DRM_FORMAT_ABGR16161616F, +]; + +const VK_COLOR_SPACE_HDR10_ST2084_EXT: u32 = 1_000_104_008; + +const DMA_BUF_SYNC_READ: u64 = 1 << 0; +const DMA_BUF_SYNC_END: u64 = 1 << 2; +const DMA_BUF_IOCTL_SYNC: libc::c_ulong = + (1u64 << 30 | (b'b' as u64) << 8 | 8u64 << 16) as libc::c_ulong; + +const fn fourcc(bytes: [u8; 4]) -> u32 { + bytes[0] as u32 | (bytes[1] as u32) << 8 | (bytes[2] as u32) << 16 | (bytes[3] as u32) << 24 +} + +#[derive(Debug)] +pub enum GameCaptureError { + Bind, + Listen, + Spawn, +} + +impl std::fmt::Display for GameCaptureError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Bind => f.write_str("Bind"), + Self::Listen => f.write_str("Listen"), + Self::Spawn => f.write_str("Spawn"), + } + } +} + +pub struct GameCaptureVideoStream { + running: Arc, + thread: std::sync::Mutex>>, + diagnostics: Arc>, +} + +impl GameCaptureVideoStream { + pub fn open( + frame_rate: u32, + on_frame: FrameCallback, + on_lifecycle: LifecycleCallback, + ) -> Result { + let running = Arc::new(AtomicBool::new(true)); + let import_state = GameCaptureImportState::from_env(); + let diagnostics = Arc::new(Mutex::new(GameCaptureDiagnostics::new_with_import_state( + frame_rate, + import_state, + ))); + let (ready_tx, ready_rx) = std::sync::mpsc::sync_channel::>(1); + let running_thread = running.clone(); + let diagnostics_thread = diagnostics.clone(); + let handle = thread::Builder::new() + .name("fluxer-linux-game-capture-vkcapture".to_string()) + .spawn(move || { + run_server( + frame_rate, + import_state, + running_thread, + diagnostics_thread, + ready_tx, + on_frame, + on_lifecycle, + ); + }) + .map_err(|_| GameCaptureError::Spawn)?; + match ready_rx.recv_timeout(READY_TIMEOUT) { + Ok(Ok(())) => Ok(Self { + running, + thread: std::sync::Mutex::new(Some(handle)), + diagnostics, + }), + Ok(Err(err)) => { + running.store(false, Ordering::Release); + let _ = handle.join(); + Err(err) + } + Err(_) => { + running.store(false, Ordering::Release); + let _ = handle.join(); + Err(GameCaptureError::Bind) + } + } + } + + pub fn stop(&self) { + self.running.store(false, Ordering::Release); + } + + pub fn diagnostics(&self) -> GameCaptureDiagnostics { + self.diagnostics + .lock() + .map(|state| state.clone()) + .unwrap_or_else(|poisoned| poisoned.into_inner().clone()) + } +} + +impl Drop for GameCaptureVideoStream { + fn drop(&mut self) { + self.stop(); + if let Ok(mut guard) = self.thread.lock() + && let Some(handle) = guard.take() + { + let _ = handle.join(); + } + } +} + +#[derive(Debug, Clone)] +pub struct GameCaptureDiagnostics { + pub backend: String, + pub active_strategy: String, + pub import_mode: String, + pub map_host: bool, + pub linear: bool, + pub client_connected: bool, + pub connected_client: Option, + pub connected_pid: Option, + pub width: Option, + pub height: Option, + pub texture_format: Option, + pub texture_modifier: Option, + pub frame_counter: u64, + pub dropped_frame_counter: u64, + pub lagged_frame_counter: u64, + pub unsupported_frame_counter: u64, + pub last_present_timestamp_us: Option, + pub last_diagnostic: Option, + pub last_addon_error: Option, + pub requested_injection_method: String, + pub injection_method: String, + pub requested_import_mode: String, + pub last_fallback_reason: Option, + pub frame_transport: String, + pub host_mapped_cpu_fallback: bool, + pub source_dmabuf_metadata_available: bool, + pub no_modifiers: bool, + pub zero_copy: bool, + pub gpu_import_available: bool, + pub device_uuid_advertised: bool, + pub supported_import_modes: Vec, +} + +impl GameCaptureDiagnostics { + fn new(_frame_rate: u32) -> Self { + Self::new_with_import_state(_frame_rate, GameCaptureImportState::from_env()) + } + + fn new_with_import_state(_frame_rate: u32, import_state: GameCaptureImportState) -> Self { + Self { + backend: "obs-vkcapture".to_string(), + active_strategy: import_state.active_strategy().to_string(), + import_mode: import_state.current.as_str().to_string(), + map_host: import_state.current.map_host(), + linear: import_state.current.linear(), + client_connected: false, + connected_client: None, + connected_pid: None, + width: None, + height: None, + texture_format: None, + texture_modifier: None, + frame_counter: 0, + dropped_frame_counter: 0, + lagged_frame_counter: 0, + unsupported_frame_counter: 0, + last_present_timestamp_us: None, + last_diagnostic: None, + last_addon_error: None, + requested_injection_method: "obs-vkcapture".to_string(), + injection_method: "obs-vkcapture".to_string(), + requested_import_mode: import_state.requested_label().to_string(), + last_fallback_reason: Some(import_state.initial_reason()), + frame_transport: import_state.current.frame_transport().to_string(), + host_mapped_cpu_fallback: import_state.current.map_host(), + source_dmabuf_metadata_available: import_state + .current + .source_dmabuf_metadata_available(), + no_modifiers: import_state.current.no_modifiers(), + zero_copy: import_state.current.zero_copy(), + gpu_import_available: import_state.current.zero_copy(), + device_uuid_advertised: false, + supported_import_modes: OBS_IMPORT_MODE_NAMES + .iter() + .map(|mode| (*mode).to_string()) + .collect(), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum GameCaptureImportMode { + Default, + NoModifiers, + Linear, + LinearHostMapped, +} + +impl GameCaptureImportMode { + fn as_str(self) -> &'static str { + match self { + Self::Default => "default-dmabuf", + Self::NoModifiers => "no-modifiers-dmabuf", + Self::Linear => "linear-dmabuf", + Self::LinearHostMapped => "linear-host-mapped-dmabuf", + } + } + + fn no_modifiers(self) -> bool { + matches!(self, Self::NoModifiers) + } + + fn linear(self) -> bool { + matches!(self, Self::Linear | Self::LinearHostMapped) + } + + fn map_host(self) -> bool { + matches!(self, Self::LinearHostMapped) + } + + fn next_fallback(self) -> Option { + match self { + Self::Default => Some(Self::NoModifiers), + Self::NoModifiers => Some(Self::Linear), + Self::Linear => Some(Self::LinearHostMapped), + Self::LinearHostMapped => None, + } + } + + fn frame_transport(self) -> &'static str { + if self.map_host() { + "host-mapped-cpu-nv12-with-source-dmabuf" + } else { + "gpu-dmabuf-zero-copy" + } + } + + fn source_dmabuf_metadata_available(self) -> bool { + true + } + + fn zero_copy(self) -> bool { + !self.map_host() + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum GameCaptureImportPolicy { + AutoObsFallback, + Fixed(GameCaptureImportMode), +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct GameCaptureImportState { + policy: GameCaptureImportPolicy, + current: GameCaptureImportMode, + invalid_env_value: Option<&'static str>, +} + +impl GameCaptureImportState { + fn from_env() -> Self { + match env::var(IMPORT_MODE_ENV) { + Ok(value) => Self::from_env_value(value.as_str()), + Err(_) => Self::auto(), + } + } + + fn from_env_value(value: &str) -> Self { + let normalized = value.trim().to_ascii_lowercase(); + match normalized.as_str() { + "" | "auto" | "fallback" | "obs-fallback" => Self::auto(), + "default" | "default-dmabuf" | "gpu" | "gpu-default" | "zero-copy" => { + Self::fixed(GameCaptureImportMode::Default) + } + "no-modifiers" | "no-modifiers-dmabuf" | "gpu-no-modifiers" => { + Self::fixed(GameCaptureImportMode::NoModifiers) + } + "linear" | "linear-dmabuf" | "gpu-linear" => Self::fixed(GameCaptureImportMode::Linear), + "host-mapped" | "linear-host-mapped" | "linear-host-mapped-dmabuf" | "cpu" | "safe" => { + Self::fixed(GameCaptureImportMode::LinearHostMapped) + } + _ => Self { + invalid_env_value: Some("invalid"), + ..Self::auto() + }, + } + } + + fn auto() -> Self { + Self { + policy: GameCaptureImportPolicy::AutoObsFallback, + current: GameCaptureImportMode::Default, + invalid_env_value: None, + } + } + + fn fixed(current: GameCaptureImportMode) -> Self { + Self { + policy: GameCaptureImportPolicy::Fixed(current), + current, + invalid_env_value: None, + } + } + + fn requested_label(self) -> &'static str { + match self.policy { + GameCaptureImportPolicy::AutoObsFallback => "auto-obs-fallback", + GameCaptureImportPolicy::Fixed(mode) => mode.as_str(), + } + } + + fn active_strategy(self) -> &'static str { + match self.policy { + GameCaptureImportPolicy::AutoObsFallback => "game-hook-auto-fallback", + GameCaptureImportPolicy::Fixed(GameCaptureImportMode::LinearHostMapped) => { + "game-hook-host-mapped" + } + GameCaptureImportPolicy::Fixed(_) => "game-hook-gpu-import-requested", + } + } + + fn initial_reason(self) -> String { + if self.invalid_env_value.is_some() { + return format!( + "{IMPORT_MODE_ENV} was invalid; using OBS import fallback ladder starting at default DMABUF" + ); + } + match self.policy { + GameCaptureImportPolicy::AutoObsFallback => { + "OBS import ladder starts at default DMABUF zero-copy and falls back only if native GPU import cannot consume the advertised texture".to_string() + } + GameCaptureImportPolicy::Fixed(GameCaptureImportMode::LinearHostMapped) => { + "Fluxer requests OBS linear host-mapped capture for CPU NV12 conversion".to_string() + } + GameCaptureImportPolicy::Fixed(mode) => format!( + "Fluxer requests OBS {} capture for Linux native GPU import", + mode.as_str() + ), + } + } + + fn advance_after_import_failure( + &mut self, + ) -> Option<(GameCaptureImportMode, GameCaptureImportMode)> { + if !matches!(self.policy, GameCaptureImportPolicy::AutoObsFallback) { + return None; + } + let from = self.current; + let to = from.next_fallback()?; + self.current = to; + Some((from, to)) + } +} + +type SharedDiagnostics = Arc>; + +#[derive(Debug)] +struct Client { + fd: RawFd, + exe: String, + pid: Option, +} + +impl Drop for Client { + fn drop(&mut self) { + close_fd(self.fd); + } +} + +struct MappedTexture { + fds: [RawFd; 4], + nfd: usize, + width: u32, + height: u32, + format: i32, + strides: [u32; 4], + offsets: [u32; 4], + stride: usize, + offset: usize, + modifier: u64, + flip: bool, + color_space: u32, + zero_copy: bool, + ptr: *mut c_void, + map_size: usize, + frame_pool: Option>, + bgra_scratch: Vec, +} + +impl Drop for MappedTexture { + fn drop(&mut self) { + if !self.ptr.is_null() && self.map_size > 0 { + unsafe { + libc::munmap(self.ptr, self.map_size); + } + } + for fd in self.fds.iter_mut().take(self.nfd) { + if *fd >= 0 { + close_fd(*fd); + *fd = -1; + } + } + } +} + +#[derive(Debug, Clone, Copy)] +struct TextureData { + nfd: usize, + width: u32, + height: u32, + format: i32, + strides: [i32; 4], + offsets: [i32; 4], + modifier: u64, + flip: bool, + color_space: u32, +} + +#[derive(Debug, Clone, Copy)] +struct HostMappedLayout { + stride: usize, + offset: usize, + strides: [u32; 4], + offsets: [u32; 4], + width: u32, + height: u32, +} + +fn run_server( + frame_rate: u32, + mut import_state: GameCaptureImportState, + running: Arc, + diagnostics: SharedDiagnostics, + ready_tx: std::sync::mpsc::SyncSender>, + on_frame: FrameCallback, + on_lifecycle: LifecycleCallback, +) { + let listener = match bind_listener() { + Ok(fd) => fd, + Err(err) => { + let _ = ready_tx.send(Err(err)); + return; + } + }; + let _listener_guard = FdGuard(listener); + let _ = ready_tx.send(Ok(())); + + let interval = Duration::from_secs_f64(1.0 / frame_rate.clamp(1, MAX_FPS) as f64); + let mut next_frame_at = Instant::now(); + let mut client: Option = None; + let mut texture: Option = None; + + while running.load(Ordering::Acquire) { + let timeout = next_poll_timeout(next_frame_at); + let mut pollfds = [ + libc::pollfd { + fd: listener, + events: libc::POLLIN, + revents: 0, + }, + libc::pollfd { + fd: client.as_ref().map(|c| c.fd).unwrap_or(-1), + events: libc::POLLIN | libc::POLLHUP | libc::POLLERR, + revents: 0, + }, + ]; + let nfds = if client.is_some() { 2 } else { 1 }; + let poll_result = unsafe { libc::poll(pollfds.as_mut_ptr(), nfds, timeout) }; + if poll_result < 0 { + let errno = last_errno(); + if errno != libc::EINTR { + publish_lifecycle( + &diagnostics, + &on_lifecycle, + "error", + &format!("obs-vkcapture poll failed: errno {errno}"), + ); + break; + } + } + + if poll_result > 0 && pollfds[0].revents & libc::POLLIN != 0 { + match accept_client(listener) { + Ok(next_client) => { + let exe = next_client.exe.clone(); + update_diagnostics(&diagnostics, |state| { + state.client_connected = true; + state.connected_client = Some(exe.clone()); + state.connected_pid = next_client.pid; + state.width = None; + state.height = None; + state.texture_format = None; + state.texture_modifier = None; + }); + client = Some(next_client); + texture = None; + sync_import_diagnostics(&diagnostics, import_state, None); + if let Some(active) = client.as_ref() { + let _ = write_control(active.fd, true, import_state.current); + } + publish_lifecycle( + &diagnostics, + &on_lifecycle, + "diagnostic", + &format!("obs-vkcapture client connected: {exe}"), + ); + } + Err(message) => { + publish_lifecycle(&diagnostics, &on_lifecycle, "diagnostic", &message) + } + } + } + + if let Some(active) = client.as_ref() { + let revents = pollfds[1].revents; + if revents & (libc::POLLHUP | libc::POLLERR) != 0 { + clear_connected_client(&diagnostics); + publish_lifecycle( + &diagnostics, + &on_lifecycle, + "diagnostic", + "obs-vkcapture client disconnected", + ); + client = None; + texture = None; + } else if revents & libc::POLLIN != 0 { + loop { + match recv_client_message(active.fd) { + RecvMessage::ClientName(exe) => { + update_diagnostics(&diagnostics, |state| { + state.connected_client = Some(exe.clone()); + }); + publish_lifecycle( + &diagnostics, + &on_lifecycle, + "diagnostic", + &format!("obs-vkcapture client identified: {exe}"), + ); + sync_import_diagnostics(&diagnostics, import_state, None); + let _ = write_control(active.fd, true, import_state.current); + } + RecvMessage::Texture(data, fds) => { + match map_texture(data, fds, import_state.current, &on_lifecycle) { + Some(mapped) => { + update_diagnostics(&diagnostics, |state| { + state.width = Some(mapped.width); + state.height = Some(mapped.height); + state.texture_format = + Some(fourcc_to_string(mapped.format)); + state.texture_modifier = + Some(format_modifier(mapped.modifier)); + }); + publish_lifecycle( + &diagnostics, + &on_lifecycle, + "diagnostic", + &format!( + "obs-vkcapture texture mapped: {}x{} fourcc={} modifier={}", + mapped.width, + mapped.height, + fourcc_to_string(mapped.format), + mapped.modifier + ), + ); + texture = Some(mapped); + } + None => { + update_diagnostics(&diagnostics, |state| { + state.unsupported_frame_counter = + state.unsupported_frame_counter.saturating_add(1); + }); + close_fds(fds); + if let Some((from, to)) = + import_state.advance_after_import_failure() + { + let reason = format!( + "OBS import mode {} was not usable in this Fluxer backend; requesting {}", + from.as_str(), + to.as_str() + ); + sync_import_diagnostics( + &diagnostics, + import_state, + Some(reason.clone()), + ); + publish_lifecycle( + &diagnostics, + &on_lifecycle, + "diagnostic", + &reason, + ); + } else { + sync_import_diagnostics(&diagnostics, import_state, None); + } + let _ = write_control(active.fd, true, import_state.current); + } + } + } + RecvMessage::WouldBlock => break, + RecvMessage::Closed => { + clear_connected_client(&diagnostics); + publish_lifecycle( + &diagnostics, + &on_lifecycle, + "diagnostic", + "obs-vkcapture client closed", + ); + client = None; + texture = None; + break; + } + RecvMessage::Invalid(message) => { + publish_lifecycle(&diagnostics, &on_lifecycle, "diagnostic", &message); + break; + } + } + } + } + } + + let now = Instant::now(); + if now >= next_frame_at { + if let Some(mapped) = texture.as_mut() { + match mapped.read_frame() { + Some(frame) => { + update_diagnostics(&diagnostics, |state| { + state.frame_counter = state.frame_counter.saturating_add(1); + state.last_present_timestamp_us = Some(frame.timestamp_us); + }); + on_frame(frame); + } + None => { + update_diagnostics(&diagnostics, |state| { + state.dropped_frame_counter = + state.dropped_frame_counter.saturating_add(1); + }); + } + } + } + let (deadline, lagged_frames) = advance_frame_deadline(next_frame_at, now, interval); + next_frame_at = deadline; + if lagged_frames > 0 { + if texture.is_some() { + update_diagnostics(&diagnostics, |state| { + state.lagged_frame_counter = state + .lagged_frame_counter + .saturating_add(lagged_frames as u64); + }); + } + } + } + } + + publish_lifecycle(&diagnostics, &on_lifecycle, "closed", ""); +} + +fn publish_lifecycle( + diagnostics: &SharedDiagnostics, + on_lifecycle: &LifecycleCallback, + kind: &str, + message: &str, +) { + update_diagnostics(diagnostics, |state| match kind { + "error" => state.last_addon_error = Some(message.to_string()), + "diagnostic" | "stalled" => state.last_diagnostic = Some(message.to_string()), + _ => {} + }); + on_lifecycle(kind, message); +} + +fn update_diagnostics( + diagnostics: &SharedDiagnostics, + update: impl FnOnce(&mut GameCaptureDiagnostics), +) { + let mut state = diagnostics + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()); + update(&mut state); +} + +fn clear_connected_client(diagnostics: &SharedDiagnostics) { + update_diagnostics(diagnostics, |state| { + state.client_connected = false; + state.connected_client = None; + state.connected_pid = None; + state.width = None; + state.height = None; + state.texture_format = None; + state.texture_modifier = None; + }); +} + +fn sync_import_diagnostics( + diagnostics: &SharedDiagnostics, + import_state: GameCaptureImportState, + fallback_reason: Option, +) { + update_diagnostics(diagnostics, |state| { + state.active_strategy = import_state.active_strategy().to_string(); + state.import_mode = import_state.current.as_str().to_string(); + state.requested_import_mode = import_state.requested_label().to_string(); + state.no_modifiers = import_state.current.no_modifiers(); + state.linear = import_state.current.linear(); + state.map_host = import_state.current.map_host(); + state.frame_transport = import_state.current.frame_transport().to_string(); + state.host_mapped_cpu_fallback = import_state.current.map_host(); + state.source_dmabuf_metadata_available = + import_state.current.source_dmabuf_metadata_available(); + state.zero_copy = import_state.current.zero_copy(); + state.gpu_import_available = import_state.current.zero_copy(); + state.device_uuid_advertised = false; + if let Some(reason) = fallback_reason { + state.last_fallback_reason = Some(reason); + } + }); +} + +fn next_poll_timeout(next_frame_at: Instant) -> i32 { + let now = Instant::now(); + if now >= next_frame_at { + return 0; + } + poll_timeout_for_remaining(next_frame_at.duration_since(now)) +} + +fn poll_timeout_for_remaining(remaining: Duration) -> i32 { + assert!(remaining > Duration::ZERO); + let ms = remaining + .as_nanos() + .div_ceil(1_000_000) + .min(POLL_TIMEOUT_MS as u128); + let timeout = ms.max(1) as i32; + assert!(timeout >= 1); + assert!(timeout <= POLL_TIMEOUT_MS); + timeout +} + +fn advance_frame_deadline( + next_frame_at: Instant, + now: Instant, + interval: Duration, +) -> (Instant, u32) { + assert!(now >= next_frame_at); + assert!(interval > Duration::ZERO); + let interval_ns = interval.as_nanos(); + assert!(interval_ns > 0); + let behind = now.duration_since(next_frame_at); + let count_unbounded = behind.as_nanos() / interval_ns + 1; + let count = count_unbounded.min(FRAME_ADVANCE_COUNT_MAX as u128) as u32; + assert!(count >= 1); + assert!(count <= FRAME_ADVANCE_COUNT_MAX); + let advanced = next_frame_at + interval.saturating_mul(count); + if count_unbounded < FRAME_ADVANCE_COUNT_MAX as u128 { + assert!(advanced > now); + } + (advanced, count - 1) +} + +fn bind_listener() -> Result { + let fd = unsafe { libc::socket(libc::AF_UNIX, libc::SOCK_STREAM | libc::SOCK_CLOEXEC, 0) }; + if fd < 0 { + return Err(GameCaptureError::Bind); + } + let mut addr = MaybeUninit::::zeroed(); + let addr_ptr = addr.as_mut_ptr(); + unsafe { + (*addr_ptr).sun_family = libc::AF_UNIX as libc::sa_family_t; + (*addr_ptr).sun_path[0] = 0; + for (idx, byte) in VKCAPTURE_SOCKET_NAME.iter().copied().enumerate() { + (*addr_ptr).sun_path[idx + 1] = byte as libc::c_char; + } + } + let addr = unsafe { addr.assume_init() }; + let len = (size_of::() + 1 + VKCAPTURE_SOCKET_NAME.len()) as libc::socklen_t; + let bind_result = unsafe { libc::bind(fd, &addr as *const _ as *const libc::sockaddr, len) }; + if bind_result != 0 { + close_fd(fd); + return Err(GameCaptureError::Bind); + } + let listen_result = unsafe { libc::listen(fd, 4) }; + if listen_result != 0 { + close_fd(fd); + return Err(GameCaptureError::Listen); + } + Ok(fd) +} + +fn accept_client(listener: RawFd) -> Result { + let fd = unsafe { + libc::accept4( + listener, + ptr::null_mut(), + ptr::null_mut(), + libc::SOCK_CLOEXEC | libc::SOCK_NONBLOCK, + ) + }; + if fd < 0 { + return Err(format!( + "obs-vkcapture accept failed: errno {}", + last_errno() + )); + } + let (exe, pid) = peer_process_label(fd); + Ok(Client { fd, exe, pid }) +} + +fn peer_process_label(fd: RawFd) -> (String, Option) { + let mut cred = MaybeUninit::::zeroed(); + let mut len = size_of::() as libc::socklen_t; + let ok = unsafe { + libc::getsockopt( + fd, + libc::SOL_SOCKET, + libc::SO_PEERCRED, + cred.as_mut_ptr() as *mut c_void, + &mut len, + ) + } == 0; + if ok { + let cred = unsafe { cred.assume_init() }; + return (format!("pid {}", cred.pid), Some(cred.pid)); + } + ("unknown process".to_string(), None) +} + +fn write_control( + fd: RawFd, + capturing: bool, + import_mode: GameCaptureImportMode, +) -> std::io::Result<()> { + let control = build_control_message(capturing, import_mode); + let n = unsafe { libc::write(fd, control.as_ptr() as *const c_void, control.len()) }; + if n == control.len() as isize { + Ok(()) + } else { + Err(std::io::Error::last_os_error()) + } +} + +fn build_control_message( + capturing: bool, + import_mode: GameCaptureImportMode, +) -> [u8; CONTROL_DATA_SIZE] { + let mut control = [0u8; CONTROL_DATA_SIZE]; + control[0] = u8::from(capturing); + control[1] = u8::from(import_mode.no_modifiers()); + control[2] = u8::from(import_mode.linear()); + control[3] = u8::from(import_mode.map_host()); + control +} + +enum RecvMessage { + ClientName(String), + Texture(TextureData, [RawFd; 4]), + WouldBlock, + Closed, + Invalid(String), +} + +fn recv_client_message(fd: RawFd) -> RecvMessage { + let mut buf = [0u8; TEXTURE_DATA_SIZE]; + let mut iov = libc::iovec { + iov_base: buf.as_mut_ptr() as *mut c_void, + iov_len: buf.len(), + }; + let mut control = [0u8; 128]; + let mut msg = unsafe { MaybeUninit::::zeroed().assume_init() }; + msg.msg_iov = &mut iov; + msg.msg_iovlen = 1; + msg.msg_control = control.as_mut_ptr() as *mut c_void; + msg.msg_controllen = control.len(); + let n = unsafe { libc::recvmsg(fd, &mut msg, libc::MSG_NOSIGNAL) }; + if n < 0 { + let errno = last_errno(); + if errno == libc::EAGAIN || errno == libc::EWOULDBLOCK { + return RecvMessage::WouldBlock; + } + return RecvMessage::Invalid(format!("obs-vkcapture recvmsg failed: errno {errno}")); + } + if n == 0 { + return RecvMessage::Closed; + } + match buf[0] { + CLIENT_DATA_TYPE => { + if n as usize != CLIENT_DATA_SIZE { + return RecvMessage::Invalid(format!( + "invalid obs-vkcapture client data size: {n}" + )); + } + RecvMessage::ClientName(parse_client_name(&buf)) + } + TEXTURE_DATA_TYPE => { + if n as usize != TEXTURE_DATA_SIZE { + return RecvMessage::Invalid(format!( + "invalid obs-vkcapture texture data size: {n}" + )); + } + let Some(data) = parse_texture_data(&buf) else { + return RecvMessage::Invalid("invalid obs-vkcapture texture data".to_string()); + }; + let fds = parse_rights_fds(&control, msg.msg_controllen); + let received = fds.iter().filter(|fd| **fd >= 0).count(); + if received != data.nfd { + close_fds(fds); + return RecvMessage::Invalid(format!( + "obs-vkcapture sent {} fds but metadata expected {}", + received, data.nfd + )); + } + RecvMessage::Texture(data, fds) + } + other => RecvMessage::Invalid(format!("unknown obs-vkcapture message type: {other}")), + } +} + +fn parse_client_name(buf: &[u8; TEXTURE_DATA_SIZE]) -> String { + let exe = &buf[1..49]; + let end = exe.iter().position(|b| *b == 0).unwrap_or(exe.len()); + String::from_utf8_lossy(&exe[..end]).trim().to_string() +} + +fn parse_texture_data(buf: &[u8; TEXTURE_DATA_SIZE]) -> Option { + let nfd = buf[1] as usize; + if nfd == 0 || nfd > 4 { + return None; + } + let width = read_i32(buf, 2)?; + let height = read_i32(buf, 6)?; + if width <= 0 || height <= 0 { + return None; + } + let format = read_i32(buf, 10)?; + let mut strides = [0i32; 4]; + let mut offsets = [0i32; 4]; + for (idx, stride) in strides.iter_mut().enumerate() { + *stride = read_i32(buf, 14 + idx * 4)?; + } + for (idx, offset) in offsets.iter_mut().enumerate() { + *offset = read_i32(buf, 30 + idx * 4)?; + } + Some(TextureData { + nfd, + width: width as u32, + height: height as u32, + format, + strides, + offsets, + modifier: read_u64(buf, 46)?, + flip: buf[58] != 0, + color_space: read_u32(buf, 59)?, + }) +} + +fn read_i32(buf: &[u8], offset: usize) -> Option { + let bytes: [u8; 4] = buf.get(offset..offset + 4)?.try_into().ok()?; + Some(i32::from_le_bytes(bytes)) +} + +fn read_u32(buf: &[u8], offset: usize) -> Option { + let bytes: [u8; 4] = buf.get(offset..offset + 4)?.try_into().ok()?; + Some(u32::from_le_bytes(bytes)) +} + +fn read_u64(buf: &[u8], offset: usize) -> Option { + let bytes: [u8; 8] = buf.get(offset..offset + 8)?.try_into().ok()?; + Some(u64::from_le_bytes(bytes)) +} + +fn parse_rights_fds(control: &[u8], controllen: usize) -> [RawFd; 4] { + let mut fds = [-1; 4]; + let header_size = cmsg_align(size_of::()); + if controllen < header_size || control.len() < header_size { + return fds; + } + let header = unsafe { ptr::read_unaligned(control.as_ptr() as *const libc::cmsghdr) }; + if header.cmsg_level != libc::SOL_SOCKET || header.cmsg_type != libc::SCM_RIGHTS { + return fds; + } + let cmsg_len = header.cmsg_len; + if cmsg_len < header_size || cmsg_len > controllen || cmsg_len > control.len() { + return fds; + } + let data_len = cmsg_len - header_size; + let count = (data_len / size_of::()).min(4); + for (idx, out) in fds.iter_mut().enumerate().take(count) { + let offset = header_size + idx * size_of::(); + *out = unsafe { ptr::read_unaligned(control.as_ptr().add(offset) as *const RawFd) }; + } + fds +} + +fn cmsg_align(len: usize) -> usize { + let align = size_of::(); + (len + align - 1) & !(align - 1) +} + +fn map_texture( + data: TextureData, + fds: [RawFd; 4], + import_mode: GameCaptureImportMode, + on_lifecycle: &LifecycleCallback, +) -> Option { + if !import_mode.map_host() { + let dmabuf_layout = validate_dmabuf_texture_data(data, fds, on_lifecycle)?; + on_lifecycle( + "diagnostic", + &format!( + "obs-vkcapture texture imported as zero-copy DMA-BUF: {}x{} fourcc={} modifier={}", + dmabuf_layout.width, + dmabuf_layout.height, + fourcc_to_string(data.format), + data.modifier + ), + ); + return Some(MappedTexture { + fds, + nfd: data.nfd, + width: dmabuf_layout.width, + height: dmabuf_layout.height, + format: data.format, + strides: dmabuf_layout.strides, + offsets: dmabuf_layout.offsets, + stride: dmabuf_layout.strides[0] as usize, + offset: dmabuf_layout.offsets[0] as usize, + modifier: data.modifier, + flip: data.flip, + color_space: data.color_space, + zero_copy: true, + ptr: ptr::null_mut(), + map_size: 0, + frame_pool: None, + bgra_scratch: Vec::new(), + }); + } + let host_layout = validate_host_mapped_texture_data(data, fds, on_lifecycle)?; + let map_size = unsafe { libc::lseek(fds[0], 0, libc::SEEK_END) }; + if map_size <= 0 { + on_lifecycle( + "diagnostic", + "obs-vkcapture dma-buf size could not be determined", + ); + return None; + } + let map_size = map_size as usize; + let Some(required) = host_layout.offset.checked_add( + host_layout + .stride + .checked_mul(host_layout.height as usize)?, + ) else { + on_lifecycle( + "diagnostic", + "obs-vkcapture dma-buf layout overflows addressable memory", + ); + return None; + }; + if required > map_size { + on_lifecycle( + "diagnostic", + &format!("obs-vkcapture dma-buf too small: required {required}, size {map_size}"), + ); + return None; + } + let ptr = unsafe { + libc::mmap( + ptr::null_mut(), + map_size, + libc::PROT_READ, + libc::MAP_SHARED, + fds[0], + 0, + ) + }; + if ptr == libc::MAP_FAILED { + on_lifecycle( + "diagnostic", + &format!("obs-vkcapture mmap failed: errno {}", last_errno()), + ); + return None; + } + let layout = Nv12Layout { + width: host_layout.width, + height: host_layout.height, + stride_y: host_layout.width, + stride_uv: host_layout.width, + }; + let frame_pool = LinuxFrameBufferPool::new(layout.packed_size()?).ok()?; + Some(MappedTexture { + fds, + nfd: data.nfd, + width: layout.width, + height: layout.height, + format: data.format, + strides: host_layout.strides, + offsets: host_layout.offsets, + stride: host_layout.stride, + offset: host_layout.offset, + modifier: data.modifier, + flip: data.flip, + color_space: data.color_space, + zero_copy: false, + ptr, + map_size, + frame_pool: Some(frame_pool), + bgra_scratch: Vec::new(), + }) +} + +fn validate_dmabuf_texture_data( + data: TextureData, + fds: [RawFd; 4], + on_lifecycle: &LifecycleCallback, +) -> Option { + if !(1..=4).contains(&data.nfd) { + on_lifecycle( + "diagnostic", + &format!( + "obs-vkcapture DMA-BUF backend received invalid fd count {}", + data.nfd + ), + ); + return None; + } + let Some(min_stride) = dmabuf_min_stride(data.format, data.width) else { + on_lifecycle( + "diagnostic", + &format!( + "obs-vkcapture DMA-BUF backend does not support fourcc {}; supported zero-copy formats: {}", + fourcc_to_string(data.format), + supported_dmabuf_format_names() + ), + ); + return None; + }; + let width = data.width & !1; + let height = data.height & !1; + if width < 16 || height < 16 { + on_lifecycle( + "diagnostic", + &format!( + "obs-vkcapture DMA-BUF dimensions are invalid: {}x{}", + data.width, data.height + ), + ); + return None; + } + let mut strides = [0u32; 4]; + let mut offsets = [0u32; 4]; + for idx in 0..data.nfd { + if fds[idx] < 0 { + on_lifecycle( + "diagnostic", + "obs-vkcapture DMA-BUF backend received an invalid dma-buf fd", + ); + return None; + } + let Some(stride) = data.strides.get(idx).and_then(|stride| { + if *stride > 0 { + u32::try_from(*stride).ok() + } else { + None + } + }) else { + on_lifecycle( + "diagnostic", + "obs-vkcapture DMA-BUF backend received an invalid stride", + ); + return None; + }; + let Some(offset) = data.offsets.get(idx).and_then(|offset| { + if *offset >= 0 { + u32::try_from(*offset).ok() + } else { + None + } + }) else { + on_lifecycle( + "diagnostic", + "obs-vkcapture DMA-BUF backend received an invalid offset", + ); + return None; + }; + if idx == 0 && stride < min_stride { + on_lifecycle( + "diagnostic", + &format!( + "obs-vkcapture DMA-BUF stride is invalid: stride {} minimum {} for {}", + stride, + min_stride, + fourcc_to_string(data.format) + ), + ); + return None; + } + strides[idx] = stride; + offsets[idx] = offset; + } + Some(HostMappedLayout { + stride: strides[0] as usize, + offset: offsets[0] as usize, + strides, + offsets, + width, + height, + }) +} + +fn validate_host_mapped_texture_data( + data: TextureData, + fds: [RawFd; 4], + on_lifecycle: &LifecycleCallback, +) -> Option { + if data.nfd != 1 { + on_lifecycle( + "diagnostic", + &format!( + "obs-vkcapture host-mapped backend only supports one fd, got {}", + data.nfd + ), + ); + return None; + } + if fds[0] < 0 { + on_lifecycle( + "diagnostic", + "obs-vkcapture host-mapped backend received an invalid dma-buf fd", + ); + return None; + } + let Some(format) = CpuFormat::from_drm(data.format) else { + on_lifecycle( + "diagnostic", + &format!( + "obs-vkcapture host-mapped backend does not support fourcc {}; supported host-mapped formats: {}", + fourcc_to_string(data.format), + supported_host_mapped_format_names() + ), + ); + return None; + }; + let Some(stride) = data.strides.first().and_then(|stride| { + if *stride > 0 { + usize::try_from(*stride).ok() + } else { + None + } + }) else { + on_lifecycle( + "diagnostic", + "obs-vkcapture host-mapped backend received an invalid stride", + ); + return None; + }; + let Some(offset) = data.offsets.first().and_then(|offset| { + if *offset >= 0 { + usize::try_from(*offset).ok() + } else { + None + } + }) else { + on_lifecycle( + "diagnostic", + "obs-vkcapture host-mapped backend received an invalid offset", + ); + return None; + }; + let mut strides = [0u32; 4]; + let mut offsets = [0u32; 4]; + for idx in 0..data.nfd { + let Some(stride) = data.strides.get(idx).and_then(|stride| { + if *stride > 0 { + u32::try_from(*stride).ok() + } else { + None + } + }) else { + on_lifecycle( + "diagnostic", + "obs-vkcapture host-mapped backend received an invalid stride", + ); + return None; + }; + let Some(offset) = data.offsets.get(idx).and_then(|offset| { + if *offset >= 0 { + u32::try_from(*offset).ok() + } else { + None + } + }) else { + on_lifecycle( + "diagnostic", + "obs-vkcapture host-mapped backend received an invalid offset", + ); + return None; + }; + strides[idx] = stride; + offsets[idx] = offset; + } + let width = data.width & !1; + let height = data.height & !1; + let min_stride = width as usize * format.bytes_per_pixel(); + if width < 16 || height < 16 || stride < min_stride { + on_lifecycle( + "diagnostic", + &format!( + "obs-vkcapture texture dimensions or stride are invalid: {}x{} stride {} minimum {} for {}", + data.width, + data.height, + stride, + min_stride, + fourcc_to_string(data.format) + ), + ); + return None; + } + Some(HostMappedLayout { + stride, + offset, + strides, + offsets, + width, + height, + }) +} + +impl MappedTexture { + fn read_frame(&mut self) -> Option { + let timestamp_us = monotonic_us(); + if self.zero_copy { + return Some(VideoFrame { + width: self.width, + height: self.height, + stride_y: self.strides[0], + stride_uv: if self.nfd > 1 { + self.strides[1] + } else { + self.strides[0] + }, + timestamp_us, + data: VideoFrameData::Empty, + dmabuf: Some(self.dmabuf_metadata()), + }); + } + + dma_buf_sync(self.fds[0], false); + let mapped = unsafe { slice::from_raw_parts(self.ptr as *const u8, self.map_size) }; + let src = mapped.get(self.offset..)?; + let layout = Nv12Layout { + width: self.width, + height: self.height, + stride_y: self.width, + stride_uv: self.width, + }; + let total_bytes = layout.packed_size()?; + let mut pooled = self.frame_pool.as_ref()?.try_acquire()?; + if total_bytes > pooled.buffer_mut().len() { + return None; + } + let dst = &mut pooled.buffer_mut()[..total_bytes]; + let format = CpuFormat::from_drm(self.format)?; + let ok = if format.is_bgra_passthrough() { + bgra_to_nv12(layout, src, self.stride as u32, dst, self.flip) + } else { + self.copy_to_bgra_scratch(src)?; + bgra_to_nv12(layout, &self.bgra_scratch, self.width * 4, dst, false) + }; + dma_buf_sync(self.fds[0], true); + if !ok { + return None; + } + pooled.set_len(total_bytes); + Some(VideoFrame { + width: self.width, + height: self.height, + stride_y: layout.packed_stride_y(), + stride_uv: layout.packed_stride_uv(), + timestamp_us, + data: VideoFrameData::Pooled(pooled), + dmabuf: Some(self.dmabuf_metadata()), + }) + } + + fn dmabuf_metadata(&self) -> DmabufFrameMetadata { + DmabufFrameMetadata { + fds: self.fds, + plane_count: self.nfd.min(4) as u32, + drm_format: self.format as u32, + modifier: self.modifier, + strides: self.strides, + offsets: self.offsets, + device_uuid: None, + } + } + + fn copy_to_bgra_scratch(&mut self, src: &[u8]) -> Option<()> { + let width = self.width as usize; + let height = self.height as usize; + let dst_stride = width.checked_mul(4)?; + let format = CpuFormat::from_drm(self.format)?; + self.bgra_scratch.resize(dst_stride.checked_mul(height)?, 0); + for row in 0..height { + let src_row_index = if self.flip { height - 1 - row } else { row }; + let src_offset = src_row_index.checked_mul(self.stride)?; + let dst_offset = row.checked_mul(dst_stride)?; + let src_row = src.get(src_offset..src_offset + width * format.bytes_per_pixel())?; + let dst_row = &mut self.bgra_scratch[dst_offset..dst_offset + dst_stride]; + format.write_bgra_row( + src_row, + width, + dst_row, + self.color_space == VK_COLOR_SPACE_HDR10_ST2084_EXT, + ); + } + Some(()) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum CpuFormat { + Bgra8, + Rgba8, + A2r10g10b10, + A2b10g10r10, + Rgba16Unorm, + Rgba16Float, +} + +impl CpuFormat { + fn from_drm(format: i32) -> Option { + match format { + DRM_FORMAT_XRGB8888 | DRM_FORMAT_ARGB8888 => Some(Self::Bgra8), + DRM_FORMAT_XBGR8888 | DRM_FORMAT_ABGR8888 => Some(Self::Rgba8), + DRM_FORMAT_XRGB2101010 | DRM_FORMAT_ARGB2101010 => Some(Self::A2r10g10b10), + DRM_FORMAT_XBGR2101010 | DRM_FORMAT_ABGR2101010 => Some(Self::A2b10g10r10), + DRM_FORMAT_XBGR16161616 | DRM_FORMAT_ABGR16161616 => Some(Self::Rgba16Unorm), + DRM_FORMAT_XBGR16161616F | DRM_FORMAT_ABGR16161616F => Some(Self::Rgba16Float), + _ => None, + } + } + + fn bytes_per_pixel(self) -> usize { + match self { + Self::Bgra8 | Self::Rgba8 | Self::A2r10g10b10 | Self::A2b10g10r10 => 4, + Self::Rgba16Unorm | Self::Rgba16Float => 8, + } + } + + fn is_bgra_passthrough(self) -> bool { + matches!(self, Self::Bgra8) + } + + fn write_bgra_row(self, src_row: &[u8], width: usize, dst_row: &mut [u8], hdr: bool) { + match self { + Self::Bgra8 => copy_bgra8_row(src_row, width, dst_row), + Self::Rgba8 => rgba8_row_to_bgra(src_row, width, dst_row), + Self::A2r10g10b10 => a2r10g10b10_row_to_bgra(src_row, width, dst_row, hdr), + Self::A2b10g10r10 => a2b10g10r10_row_to_bgra(src_row, width, dst_row, hdr), + Self::Rgba16Unorm => rgba16_unorm_row_to_bgra(src_row, width, dst_row), + Self::Rgba16Float => rgba16f_row_to_bgra(src_row, width, dst_row, hdr), + } + } +} + +fn copy_bgra8_row(src_row: &[u8], width: usize, dst_row: &mut [u8]) { + let bytes = width + .saturating_mul(4) + .min(src_row.len()) + .min(dst_row.len()); + dst_row[..bytes].copy_from_slice(&src_row[..bytes]); +} + +fn rgba8_row_to_bgra(src_row: &[u8], width: usize, dst_row: &mut [u8]) { + for x in 0..width { + let offset = x * 4; + if offset + 4 > src_row.len() || offset + 4 > dst_row.len() { + break; + } + dst_row[offset] = src_row[offset + 2]; + dst_row[offset + 1] = src_row[offset + 1]; + dst_row[offset + 2] = src_row[offset]; + dst_row[offset + 3] = src_row[offset + 3]; + } +} + +fn a2b10g10r10_row_to_bgra(src_row: &[u8], width: usize, dst_row: &mut [u8], hdr: bool) { + for x in 0..width { + let so = x * 4; + let dofs = x * 4; + if so + 4 > src_row.len() || dofs + 4 > dst_row.len() { + break; + } + let packed = u32::from_le_bytes([ + src_row[so], + src_row[so + 1], + src_row[so + 2], + src_row[so + 3], + ]); + let r10 = (packed & 0x3ff) as u16; + let g10 = ((packed >> 10) & 0x3ff) as u16; + let b10 = ((packed >> 20) & 0x3ff) as u16; + write_10bit_bgra( + dst_row, + dofs, + r10, + g10, + b10, + ((packed >> 30) & 0x3) as u8, + hdr, + ); + } +} + +fn a2r10g10b10_row_to_bgra(src_row: &[u8], width: usize, dst_row: &mut [u8], hdr: bool) { + for x in 0..width { + let so = x * 4; + let dofs = x * 4; + if so + 4 > src_row.len() || dofs + 4 > dst_row.len() { + break; + } + let packed = u32::from_le_bytes([ + src_row[so], + src_row[so + 1], + src_row[so + 2], + src_row[so + 3], + ]); + let b10 = (packed & 0x3ff) as u16; + let g10 = ((packed >> 10) & 0x3ff) as u16; + let r10 = ((packed >> 20) & 0x3ff) as u16; + write_10bit_bgra( + dst_row, + dofs, + r10, + g10, + b10, + ((packed >> 30) & 0x3) as u8, + hdr, + ); + } +} + +fn write_10bit_bgra( + dst_row: &mut [u8], + offset: usize, + r10: u16, + g10: u16, + b10: u16, + a2: u8, + hdr: bool, +) { + let (r, g, b) = if hdr { + tonemap_rec2020_pq_to_srgb8(r10, g10, b10) + } else { + (scale10_to_8(r10), scale10_to_8(g10), scale10_to_8(b10)) + }; + dst_row[offset] = b; + dst_row[offset + 1] = g; + dst_row[offset + 2] = r; + dst_row[offset + 3] = (a2 as u16 * 255 / 3) as u8; +} + +fn rgba16_unorm_row_to_bgra(src_row: &[u8], width: usize, dst_row: &mut [u8]) { + for x in 0..width { + let so = x * 8; + let dofs = x * 4; + if so + 8 > src_row.len() || dofs + 4 > dst_row.len() { + break; + } + let r = u16::from_le_bytes([src_row[so], src_row[so + 1]]); + let g = u16::from_le_bytes([src_row[so + 2], src_row[so + 3]]); + let b = u16::from_le_bytes([src_row[so + 4], src_row[so + 5]]); + let a = u16::from_le_bytes([src_row[so + 6], src_row[so + 7]]); + dst_row[dofs] = scale16_to_8(b); + dst_row[dofs + 1] = scale16_to_8(g); + dst_row[dofs + 2] = scale16_to_8(r); + dst_row[dofs + 3] = scale16_to_8(a); + } +} + +fn rgba16f_row_to_bgra(src_row: &[u8], width: usize, dst_row: &mut [u8], hdr: bool) { + for x in 0..width { + let so = x * 8; + let dofs = x * 4; + if so + 8 > src_row.len() || dofs + 4 > dst_row.len() { + break; + } + let r = f16_to_f32(u16::from_le_bytes([src_row[so], src_row[so + 1]])); + let g = f16_to_f32(u16::from_le_bytes([src_row[so + 2], src_row[so + 3]])); + let b = f16_to_f32(u16::from_le_bytes([src_row[so + 4], src_row[so + 5]])); + let a = f16_to_f32(u16::from_le_bytes([src_row[so + 6], src_row[so + 7]])); + let (lr, lg, lb) = if hdr { + ( + reinhard(r.max(0.0)), + reinhard(g.max(0.0)), + reinhard(b.max(0.0)), + ) + } else { + (r.clamp(0.0, 1.0), g.clamp(0.0, 1.0), b.clamp(0.0, 1.0)) + }; + dst_row[dofs] = linear_to_srgb8(lb); + dst_row[dofs + 1] = linear_to_srgb8(lg); + dst_row[dofs + 2] = linear_to_srgb8(lr); + dst_row[dofs + 3] = (a.clamp(0.0, 1.0) * 255.0 + 0.5) as u8; + } +} + +fn scale10_to_8(v10: u16) -> u8 { + ((v10 as u32 * 255 + 511) / 1023) as u8 +} + +fn scale16_to_8(v16: u16) -> u8 { + ((v16 as u32 * 255 + 32767) / 65535) as u8 +} + +fn reinhard(linear: f32) -> f32 { + let v = linear.max(0.0); + (v / (1.0 + v)).clamp(0.0, 1.0) +} + +fn tonemap_rec2020_pq_to_srgb8(r10: u16, g10: u16, b10: u16) -> (u8, u8, u8) { + const SDR_WHITE_NITS: f32 = 80.0; + let lr = pq_eotf(r10 as f32 / 1023.0); + let lg = pq_eotf(g10 as f32 / 1023.0); + let lb = pq_eotf(b10 as f32 / 1023.0); + let scale = 10000.0 / SDR_WHITE_NITS; + let map = |v: f32| reinhard((v * scale).max(0.0)); + ( + linear_to_srgb8(map(lr)), + linear_to_srgb8(map(lg)), + linear_to_srgb8(map(lb)), + ) +} + +fn pq_eotf(e: f32) -> f32 { + const M1: f64 = 0.1593017578125; + const M2: f64 = 78.84375; + const C1: f64 = 0.8359375; + const C2: f64 = 18.8515625; + const C3: f64 = 18.6875; + let e = (e.clamp(0.0, 1.0)) as f64; + let ep = e.powf(1.0 / M2); + let num = (ep - C1).max(0.0); + let den = C2 - C3 * ep; + if den <= 0.0 { + return 0.0; + } + (num / den).powf(1.0 / M1) as f32 +} + +fn linear_to_srgb8(linear: f32) -> u8 { + let l = linear.clamp(0.0, 1.0); + let srgb = if l <= 0.0031308 { + l * 12.92 + } else { + 1.055 * l.powf(1.0 / 2.4) - 0.055 + }; + (srgb.clamp(0.0, 1.0) * 255.0 + 0.5) as u8 +} + +fn f16_to_f32(h: u16) -> f32 { + let sign = (h >> 15) & 0x1; + let exp = (h >> 10) & 0x1f; + let mant = h & 0x3ff; + let sign_f = if sign == 1 { -1.0f32 } else { 1.0f32 }; + if exp == 0 { + sign_f * (mant as f32) * 2f32.powi(-24) + } else if exp == 0x1f { + if mant == 0 { sign_f * 65504.0 } else { 0.0 } + } else { + sign_f * (1.0 + (mant as f32) / 1024.0) * 2f32.powi(exp as i32 - 15) + } +} + +fn fourcc_to_string(format: i32) -> String { + let bytes = (format as u32).to_le_bytes(); + if bytes.iter().all(|b| b.is_ascii_graphic() || *b == b' ') { + String::from_utf8_lossy(&bytes).to_string() + } else { + format!("0x{:08x}", format as u32) + } +} + +fn format_modifier(modifier: u64) -> String { + if modifier == DRM_FORMAT_MOD_INVALID { + "INVALID".to_string() + } else { + format!("0x{modifier:016x}") + } +} + +fn supported_host_mapped_format_names() -> String { + SUPPORTED_HOST_MAPPED_FORMATS + .iter() + .map(|format| fourcc_to_string(*format)) + .collect::>() + .join(", ") +} + +fn dmabuf_min_stride(format: i32, width: u32) -> Option { + match format { + DRM_FORMAT_XRGB8888 + | DRM_FORMAT_ARGB8888 + | DRM_FORMAT_XBGR8888 + | DRM_FORMAT_ABGR8888 + | DRM_FORMAT_XRGB2101010 + | DRM_FORMAT_ARGB2101010 + | DRM_FORMAT_XBGR2101010 + | DRM_FORMAT_ABGR2101010 => width.checked_mul(4), + DRM_FORMAT_NV12 => Some(width), + _ => None, + } +} + +fn supported_dmabuf_format_names() -> String { + [ + DRM_FORMAT_XRGB8888, + DRM_FORMAT_ARGB8888, + DRM_FORMAT_XBGR8888, + DRM_FORMAT_ABGR8888, + DRM_FORMAT_XRGB2101010, + DRM_FORMAT_ARGB2101010, + DRM_FORMAT_XBGR2101010, + DRM_FORMAT_ABGR2101010, + DRM_FORMAT_NV12, + ] + .iter() + .map(|format| fourcc_to_string(*format)) + .collect::>() + .join(", ") +} + +#[repr(C)] +struct DmaBufSync { + flags: u64, +} + +fn dma_buf_sync(fd: RawFd, end: bool) { + let mut sync = DmaBufSync { + flags: DMA_BUF_SYNC_READ | if end { DMA_BUF_SYNC_END } else { 0 }, + }; + unsafe { + libc::ioctl(fd, DMA_BUF_IOCTL_SYNC, &mut sync); + } +} + +fn monotonic_us() -> i64 { + let mut ts: libc::timespec = unsafe { std::mem::zeroed() }; + let rc = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts) }; + assert!(rc == 0); + assert!(ts.tv_sec >= 0); + assert!(ts.tv_nsec >= 0); + let us = (ts.tv_sec as u64) + .saturating_mul(1_000_000) + .saturating_add(ts.tv_nsec as u64 / 1_000); + assert!(us <= i64::MAX as u64); + us as i64 +} + +struct FdGuard(RawFd); + +impl Drop for FdGuard { + fn drop(&mut self) { + if self.0 >= 0 { + close_fd(self.0); + } + } +} + +fn close_fds(mut fds: [RawFd; 4]) { + for fd in &mut fds { + if *fd >= 0 { + close_fd(*fd); + *fd = -1; + } + } +} + +fn close_fd(fd: RawFd) { + unsafe { + libc::close(fd); + } +} + +fn last_errno() -> i32 { + std::io::Error::last_os_error() + .raw_os_error() + .unwrap_or_default() +} + +#[cfg(test)] +mod tests { + use super::*; + + fn write_i32(buf: &mut [u8], offset: usize, value: i32) { + buf[offset..offset + 4].copy_from_slice(&value.to_le_bytes()); + } + + fn write_u32(buf: &mut [u8], offset: usize, value: u32) { + buf[offset..offset + 4].copy_from_slice(&value.to_le_bytes()); + } + + fn write_u64(buf: &mut [u8], offset: usize, value: u64) { + buf[offset..offset + 8].copy_from_slice(&value.to_le_bytes()); + } + + type LifecycleRecorder = (LifecycleCallback, Arc>>); + + fn lifecycle_recorder() -> LifecycleRecorder { + let observed = Arc::new(Mutex::new(Vec::<(String, String)>::new())); + let observed_for_cb = observed.clone(); + let callback: LifecycleCallback = Arc::new(move |kind, message| { + observed_for_cb + .lock() + .expect("observed lock") + .push((kind.to_string(), message.to_string())); + }); + (callback, observed) + } + + fn observed_messages(observed: &Arc>>) -> Vec { + observed + .lock() + .expect("observed lock") + .iter() + .map(|(_, message)| message.clone()) + .collect() + } + + fn host_mapped_texture_data() -> TextureData { + TextureData { + nfd: 1, + width: 16, + height: 16, + format: DRM_FORMAT_ARGB8888, + strides: [64, 0, 0, 0], + offsets: [0, 0, 0, 0], + modifier: DRM_FORMAT_MOD_INVALID, + flip: false, + color_space: 0, + } + } + + fn memfd_with_data(name: &str, data: &[u8]) -> RawFd { + let name = std::ffi::CString::new(name).expect("memfd name"); + let fd = unsafe { libc::memfd_create(name.as_ptr(), libc::MFD_CLOEXEC) }; + assert!(fd >= 0, "memfd_create failed: {}", last_errno()); + let resize = unsafe { libc::ftruncate(fd, data.len() as libc::off_t) }; + assert_eq!(resize, 0, "ftruncate failed: {}", last_errno()); + let written = unsafe { libc::write(fd, data.as_ptr() as *const c_void, data.len()) }; + assert_eq!( + written, + data.len() as isize, + "write failed: {}", + last_errno() + ); + fd + } + + #[test] + fn diagnostics_default_to_obs_fallback_capture_policy() { + let import_state = GameCaptureImportState::auto(); + let diagnostics = GameCaptureDiagnostics::new_with_import_state(60, import_state); + assert_eq!(diagnostics.backend, "obs-vkcapture"); + assert_eq!(diagnostics.active_strategy, "game-hook-auto-fallback"); + assert_eq!(diagnostics.requested_import_mode, "auto-obs-fallback"); + assert_eq!(diagnostics.import_mode, "default-dmabuf"); + assert!(!diagnostics.map_host); + assert!(!diagnostics.linear); + assert!(!diagnostics.no_modifiers); + assert!(diagnostics.zero_copy); + assert!(diagnostics.gpu_import_available); + assert!(!diagnostics.device_uuid_advertised); + assert_eq!(diagnostics.frame_transport, "gpu-dmabuf-zero-copy"); + assert!(!diagnostics.host_mapped_cpu_fallback); + assert!(diagnostics.source_dmabuf_metadata_available); + assert_eq!(diagnostics.supported_import_modes, OBS_IMPORT_MODE_NAMES); + assert!(!diagnostics.client_connected); + assert_eq!(diagnostics.frame_counter, 0); + assert_eq!(diagnostics.dropped_frame_counter, 0); + assert_eq!(diagnostics.lagged_frame_counter, 0); + assert_eq!(diagnostics.unsupported_frame_counter, 0); + assert_eq!(diagnostics.requested_injection_method, "obs-vkcapture"); + assert_eq!(diagnostics.injection_method, "obs-vkcapture"); + assert!(diagnostics.last_fallback_reason.is_some()); + } + + #[test] + fn diagnostics_can_pin_host_mapped_capture_policy() { + let import_state = GameCaptureImportState::fixed(GameCaptureImportMode::LinearHostMapped); + let diagnostics = GameCaptureDiagnostics::new_with_import_state(60, import_state); + assert_eq!(diagnostics.active_strategy, "game-hook-host-mapped"); + assert_eq!( + diagnostics.requested_import_mode, + "linear-host-mapped-dmabuf" + ); + assert_eq!(diagnostics.import_mode, "linear-host-mapped-dmabuf"); + assert!(diagnostics.map_host); + assert!(diagnostics.linear); + assert!(!diagnostics.no_modifiers); + assert!(!diagnostics.zero_copy); + assert!(!diagnostics.gpu_import_available); + assert_eq!( + diagnostics.frame_transport, + "host-mapped-cpu-nv12-with-source-dmabuf" + ); + assert!(diagnostics.host_mapped_cpu_fallback); + assert!(diagnostics.source_dmabuf_metadata_available); + } + + #[test] + fn lifecycle_messages_update_diagnostics_snapshot() { + let diagnostics = Arc::new(Mutex::new(GameCaptureDiagnostics::new(30))); + let observed = Arc::new(Mutex::new(Vec::<(String, String)>::new())); + let observed_for_cb = observed.clone(); + let callback: LifecycleCallback = Arc::new(move |kind, message| { + observed_for_cb + .lock() + .expect("observed lock") + .push((kind.to_string(), message.to_string())); + }); + + publish_lifecycle( + &diagnostics, + &callback, + "diagnostic", + "obs-vkcapture texture mapped", + ); + publish_lifecycle( + &diagnostics, + &callback, + "error", + "obs-vkcapture poll failed", + ); + + let snapshot = diagnostics.lock().expect("diagnostics lock"); + assert_eq!( + snapshot.last_diagnostic.as_deref(), + Some("obs-vkcapture texture mapped") + ); + assert_eq!( + snapshot.last_addon_error.as_deref(), + Some("obs-vkcapture poll failed") + ); + assert_eq!( + observed.lock().expect("observed lock").as_slice(), + [ + ( + "diagnostic".to_string(), + "obs-vkcapture texture mapped".to_string() + ), + ("error".to_string(), "obs-vkcapture poll failed".to_string()) + ] + ); + } + + #[test] + fn diagnostics_modifier_uses_obs_invalid_name() { + assert_eq!(format_modifier(DRM_FORMAT_MOD_INVALID), "INVALID"); + assert_eq!(format_modifier(0x0102), "0x0000000000000102"); + } + + #[test] + fn parses_obs_vkcapture_texture_layout() { + let mut buf = [0u8; TEXTURE_DATA_SIZE]; + buf[0] = TEXTURE_DATA_TYPE; + buf[1] = 1; + write_i32(&mut buf, 2, 1280); + write_i32(&mut buf, 6, 720); + write_i32(&mut buf, 10, DRM_FORMAT_ARGB8888); + write_i32(&mut buf, 14, 5120); + write_i32(&mut buf, 30, 256); + write_u64(&mut buf, 46, DRM_FORMAT_MOD_INVALID); + buf[58] = 1; + write_u32(&mut buf, 59, 42); + + let parsed = parse_texture_data(&buf).expect("texture data"); + assert_eq!(parsed.nfd, 1); + assert_eq!(parsed.width, 1280); + assert_eq!(parsed.height, 720); + assert_eq!(parsed.format, DRM_FORMAT_ARGB8888); + assert_eq!(parsed.strides[0], 5120); + assert_eq!(parsed.offsets[0], 256); + assert_eq!(parsed.modifier, DRM_FORMAT_MOD_INVALID); + assert!(parsed.flip); + assert_eq!(parsed.color_space, 42); + } + + #[test] + fn mapped_texture_exports_dmabuf_metadata_for_native_encoder() { + let texture = MappedTexture { + fds: [-1, -1, -1, -1], + nfd: 1, + width: 1280, + height: 720, + format: DRM_FORMAT_ABGR8888, + strides: [5120, 0, 0, 0], + offsets: [256, 0, 0, 0], + stride: 5120, + offset: 256, + modifier: DRM_FORMAT_MOD_INVALID, + flip: false, + color_space: 0, + zero_copy: false, + ptr: ptr::null_mut(), + map_size: 0, + frame_pool: None, + bgra_scratch: Vec::new(), + }; + + let metadata = texture.dmabuf_metadata(); + assert_eq!(metadata.plane_count, 1); + assert_eq!(metadata.fds, [-1, -1, -1, -1]); + assert_eq!(metadata.drm_format, DRM_FORMAT_ABGR8888 as u32); + assert_eq!(metadata.modifier, DRM_FORMAT_MOD_INVALID); + assert_eq!(metadata.strides, [5120, 0, 0, 0]); + assert_eq!(metadata.offsets, [256, 0, 0, 0]); + assert_eq!(metadata.device_uuid, None); + } + + #[test] + fn non_host_mapped_import_forwards_dmabuf_without_mapping() { + let fd = memfd_with_data("fluxer-zero-copy-dmabuf-test", &[0u8; 16 * 16 * 4]); + let (callback, observed) = lifecycle_recorder(); + let mut mapped = map_texture( + host_mapped_texture_data(), + [fd, -1, -1, -1], + GameCaptureImportMode::Default, + &callback, + ) + .expect("zero-copy dmabuf texture"); + + assert!(mapped.zero_copy); + assert!(mapped.ptr.is_null()); + assert_eq!(mapped.map_size, 0); + let frame = mapped.read_frame().expect("zero-copy frame"); + let metadata = frame.dmabuf.expect("dmabuf metadata"); + assert!(frame.data.is_empty()); + assert_eq!(metadata.fds[0], fd); + assert_eq!(metadata.drm_format, DRM_FORMAT_ARGB8888 as u32); + assert!( + observed_messages(&observed) + .iter() + .any(|message| message.contains("zero-copy DMA-BUF")) + ); + } + + #[test] + fn host_mapped_import_rejects_invalid_fd_with_diagnostic() { + let (callback, observed) = lifecycle_recorder(); + let mapped = map_texture( + host_mapped_texture_data(), + [-1, -1, -1, -1], + GameCaptureImportMode::LinearHostMapped, + &callback, + ); + + assert!(mapped.is_none()); + assert!( + observed_messages(&observed) + .iter() + .any(|message| message.contains("invalid dma-buf fd")) + ); + } + + #[test] + fn host_mapped_import_rejects_multiplane_descriptors_with_diagnostic() { + let mut data = host_mapped_texture_data(); + data.nfd = 2; + let (callback, observed) = lifecycle_recorder(); + let mapped = map_texture( + data, + [4, -1, -1, -1], + GameCaptureImportMode::LinearHostMapped, + &callback, + ); + + assert!(mapped.is_none()); + assert!( + observed_messages(&observed) + .iter() + .any(|message| message.contains("only supports one fd")) + ); + } + + #[test] + fn host_mapped_import_rejects_unsupported_formats_with_supported_list() { + let mut data = host_mapped_texture_data(); + data.format = fourcc(*b"NV12") as i32; + let (callback, observed) = lifecycle_recorder(); + let mapped = map_texture( + data, + [4, -1, -1, -1], + GameCaptureImportMode::LinearHostMapped, + &callback, + ); + + assert!(mapped.is_none()); + let messages = observed_messages(&observed); + assert!(messages.iter().any(|message| { + message.contains("does not support fourcc NV12") + && message.contains("supported host-mapped formats") + && message.contains("AR24") + && message.contains("AB4H") + })); + } + + #[test] + fn host_mapped_import_rejects_invalid_stride_and_offset_with_diagnostics() { + let (callback, observed) = lifecycle_recorder(); + let mut bad_stride = host_mapped_texture_data(); + bad_stride.strides[0] = -1; + assert!( + validate_host_mapped_texture_data(bad_stride, [4, -1, -1, -1], &callback).is_none() + ); + let mut bad_offset = host_mapped_texture_data(); + bad_offset.offsets[0] = -1; + assert!( + validate_host_mapped_texture_data(bad_offset, [4, -1, -1, -1], &callback).is_none() + ); + + let messages = observed_messages(&observed); + assert!( + messages + .iter() + .any(|message| message.contains("invalid stride")) + ); + assert!( + messages + .iter() + .any(|message| message.contains("invalid offset")) + ); + } + + #[test] + fn host_mapped_import_reads_cpu_nv12_and_preserves_source_dmabuf_metadata() { + let mut bgra = vec![0u8; 16 * 16 * 4]; + for pixel in bgra.chunks_exact_mut(4) { + pixel.copy_from_slice(&[0, 0, 255, 255]); + } + let fd = memfd_with_data("fluxer-host-mapped-texture-test", &bgra); + let (callback, _observed) = lifecycle_recorder(); + let mut texture = map_texture( + host_mapped_texture_data(), + [fd, -1, -1, -1], + GameCaptureImportMode::LinearHostMapped, + &callback, + ) + .expect("mapped texture"); + + let frame = texture.read_frame().expect("frame"); + let metadata = frame.dmabuf.expect("source dmabuf metadata"); + assert_eq!(frame.width, 16); + assert_eq!(frame.height, 16); + assert_eq!(frame.stride_y, 16); + assert_eq!(frame.stride_uv, 16); + assert_eq!(frame.data.len(), 16 * 16 * 3 / 2); + assert_eq!(metadata.fds[0], fd); + assert_eq!(metadata.plane_count, 1); + assert_eq!(metadata.drm_format, DRM_FORMAT_ARGB8888 as u32); + assert_eq!(metadata.strides, [64, 0, 0, 0]); + assert_eq!(metadata.offsets, [0, 0, 0, 0]); + assert_eq!(metadata.device_uuid, None); + } + + #[test] + fn parses_client_name_as_c_string() { + let mut buf = [0u8; TEXTURE_DATA_SIZE]; + buf[0] = CLIENT_DATA_TYPE; + buf[1..8].copy_from_slice(b"vkcube\0"); + assert_eq!(parse_client_name(&buf), "vkcube"); + } + + #[test] + fn identifies_rgba_memory_formats_that_need_swizzle() { + assert_eq!( + CpuFormat::from_drm(DRM_FORMAT_ARGB8888), + Some(CpuFormat::Bgra8) + ); + assert_eq!( + CpuFormat::from_drm(DRM_FORMAT_XRGB8888), + Some(CpuFormat::Bgra8) + ); + assert_eq!( + CpuFormat::from_drm(DRM_FORMAT_ABGR8888), + Some(CpuFormat::Rgba8) + ); + assert_eq!( + CpuFormat::from_drm(DRM_FORMAT_XBGR8888), + Some(CpuFormat::Rgba8) + ); + } + + #[test] + fn converts_fp16_rgba_row_to_bgra_srgb() { + let src = [0x00, 0x3c, 0x00, 0x38, 0x00, 0x00, 0x00, 0x3c]; + let mut dst = [0u8; 4]; + rgba16f_row_to_bgra(&src, 1, &mut dst, false); + assert_eq!(dst[0], 0); + assert!(dst[1] > 180 && dst[1] < 190); + assert_eq!(dst[2], 255); + assert_eq!(dst[3], 255); + } + + #[test] + fn converts_abgr10_row_to_bgra() { + let packed = (3u32 << 30) | (1023u32 << 20) | (512u32 << 10); + let src = packed.to_le_bytes(); + let mut dst = [0u8; 4]; + a2b10g10r10_row_to_bgra(&src, 1, &mut dst, false); + assert_eq!(dst[0], 255); + assert!(dst[1] >= 127); + assert_eq!(dst[2], 0); + assert_eq!(dst[3], 255); + } + + #[test] + fn control_message_encodes_obs_import_modes() { + let default = build_control_message(true, GameCaptureImportMode::Default); + assert_eq!(&default[..4], &[1, 0, 0, 0]); + + let no_modifiers = build_control_message(true, GameCaptureImportMode::NoModifiers); + assert_eq!(&no_modifiers[..4], &[1, 1, 0, 0]); + + let linear = build_control_message(true, GameCaptureImportMode::Linear); + assert_eq!(&linear[..4], &[1, 0, 1, 0]); + + let host_mapped = build_control_message(true, GameCaptureImportMode::LinearHostMapped); + assert_eq!(&host_mapped[..4], &[1, 0, 1, 1]); + } + + #[test] + fn auto_import_policy_walks_obs_fallback_ladder() { + let mut state = GameCaptureImportState::auto(); + assert_eq!(state.current, GameCaptureImportMode::Default); + assert_eq!( + state.advance_after_import_failure(), + Some(( + GameCaptureImportMode::Default, + GameCaptureImportMode::NoModifiers + )) + ); + assert_eq!( + state.advance_after_import_failure(), + Some(( + GameCaptureImportMode::NoModifiers, + GameCaptureImportMode::Linear + )) + ); + assert_eq!( + state.advance_after_import_failure(), + Some(( + GameCaptureImportMode::Linear, + GameCaptureImportMode::LinearHostMapped + )) + ); + assert_eq!(state.advance_after_import_failure(), None); + } + + #[test] + fn frame_deadline_advances_by_exact_interval_multiples() { + let interval = Duration::from_millis(10); + let base = Instant::now(); + + let (deadline, lagged) = advance_frame_deadline(base, base, interval); + assert_eq!(deadline, base + interval); + assert_eq!(lagged, 0); + + let (deadline, lagged) = + advance_frame_deadline(base, base + Duration::from_millis(3), interval); + assert_eq!(deadline, base + interval); + assert_eq!(lagged, 0); + + let (deadline, lagged) = + advance_frame_deadline(base, base + Duration::from_millis(25), interval); + assert_eq!(deadline, base + interval * 3); + assert_eq!(lagged, 2); + } + + #[test] + fn frame_deadline_preserves_timeline_phase_across_overruns() { + let interval = Duration::from_millis(10); + let base = Instant::now(); + let mut next_frame_at = base; + let wakeups = [ + Duration::from_micros(900), + Duration::from_micros(10_700), + Duration::from_micros(20_400), + ]; + for wakeup in wakeups { + let now = base + wakeup; + let (deadline, lagged) = advance_frame_deadline(next_frame_at, now, interval); + assert_eq!(lagged, 0); + next_frame_at = deadline; + } + assert_eq!(next_frame_at, base + interval * 3); + } + + #[test] + fn frame_deadline_caps_catch_up_count_after_long_idle() { + let interval = Duration::from_millis(10); + let base = Instant::now(); + let idle = interval + .saturating_mul(FRAME_ADVANCE_COUNT_MAX) + .saturating_mul(2); + let (deadline, lagged) = advance_frame_deadline(base, base + idle, interval); + assert_eq!(lagged, FRAME_ADVANCE_COUNT_MAX - 1); + assert_eq!( + deadline, + base + interval.saturating_mul(FRAME_ADVANCE_COUNT_MAX) + ); + } + + #[test] + fn poll_timeout_rounds_up_and_caps_at_poll_window() { + assert_eq!(poll_timeout_for_remaining(Duration::from_nanos(1)), 1); + assert_eq!(poll_timeout_for_remaining(Duration::from_micros(999)), 1); + assert_eq!(poll_timeout_for_remaining(Duration::from_micros(1_001)), 2); + assert_eq!(poll_timeout_for_remaining(Duration::from_millis(5)), 5); + assert_eq!( + poll_timeout_for_remaining(Duration::from_secs(1)), + POLL_TIMEOUT_MS + ); + } + + #[test] + fn import_mode_env_parses_nvidia_debug_shortcuts() { + assert_eq!( + GameCaptureImportState::from_env_value("zero-copy").current, + GameCaptureImportMode::Default + ); + assert_eq!( + GameCaptureImportState::from_env_value("safe").current, + GameCaptureImportMode::LinearHostMapped + ); + assert_eq!( + GameCaptureImportState::from_env_value("unknown").current, + GameCaptureImportMode::Default + ); + } +} diff --git a/fluxer_desktop/native/linux-screen-capture/src/gpu_loss.rs b/fluxer_desktop/native/linux-screen-capture/src/gpu_loss.rs new file mode 100644 index 0000000..2cb089d --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/src/gpu_loss.rs @@ -0,0 +1,357 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; + +use fluxer_gpu_rebuild::{ + GpuDevice, GpuLossCallback, GpuLossRegistry, GpuQueue, GpuRebuildError, RegistrationGuard, +}; + +pub const MAX_PENDING_DEVICE_LOSS_EVENTS: u64 = 1 << 32; + +pub struct DeviceLossTelemetry { + device_loss_total: AtomicU64, + rebuild_total: AtomicU64, + rebuild_failed_total: AtomicU64, + registry_present: AtomicBool, +} + +impl DeviceLossTelemetry { + pub fn new() -> Self { + let telemetry = Self { + device_loss_total: AtomicU64::new(0), + rebuild_total: AtomicU64::new(0), + rebuild_failed_total: AtomicU64::new(0), + registry_present: AtomicBool::new(false), + }; + assert_eq!(telemetry.device_loss_total.load(Ordering::Relaxed), 0); + assert_eq!(telemetry.rebuild_total.load(Ordering::Relaxed), 0); + telemetry + } + + pub fn record_device_loss(&self) { + let before = self.device_loss_total.fetch_add(1, Ordering::Relaxed); + assert!(before < MAX_PENDING_DEVICE_LOSS_EVENTS); + assert!(before.wrapping_add(1) > before); + } + + pub fn record_rebuild(&self, rebuilt: u32, failed: u32) { + assert!(rebuilt as u64 <= MAX_PENDING_DEVICE_LOSS_EVENTS); + assert!(failed as u64 <= MAX_PENDING_DEVICE_LOSS_EVENTS); + self.rebuild_total + .fetch_add(rebuilt as u64, Ordering::Relaxed); + self.rebuild_failed_total + .fetch_add(failed as u64, Ordering::Relaxed); + } + + pub fn device_loss_total(&self) -> u64 { + let v = self.device_loss_total.load(Ordering::Relaxed); + assert!(v <= MAX_PENDING_DEVICE_LOSS_EVENTS); + v + } + + pub fn rebuild_total(&self) -> u64 { + let v = self.rebuild_total.load(Ordering::Relaxed); + assert!(self.registry_present.load(Ordering::Relaxed) || v == 0); + v + } + + pub fn rebuild_failed_total(&self) -> u64 { + let v = self.rebuild_failed_total.load(Ordering::Relaxed); + assert!(v <= self.rebuild_total.load(Ordering::Relaxed)); + v + } + + pub fn mark_registry_attached(&self) { + let prior = self.registry_present.swap(true, Ordering::AcqRel); + assert!(!prior, "telemetry must not be attached twice"); + assert!(self.registry_present.load(Ordering::Acquire)); + } +} + +impl Default for DeviceLossTelemetry { + fn default() -> Self { + Self::new() + } +} + +pub struct DeviceLossBridge { + registry: Arc, + telemetry: Arc, +} + +impl DeviceLossBridge { + pub fn new(registry: Arc, telemetry: Arc) -> Self { + assert!( + Arc::strong_count(®istry) >= 1, + "registry arc must be alive" + ); + assert!( + Arc::strong_count(&telemetry) >= 1, + "telemetry arc must be alive" + ); + telemetry.mark_registry_attached(); + Self { + registry, + telemetry, + } + } + + pub fn register(&self, callback: Box) -> RegistrationGuard { + assert!( + Arc::strong_count(&self.registry) >= 1, + "registry alive on register" + ); + assert!( + Arc::strong_count(&self.telemetry) >= 1, + "telemetry alive on register" + ); + self.registry.register(callback) + } + + pub fn dispatch_device_lost(&self, device: &GpuDevice, queue: &GpuQueue) { + assert!( + Arc::strong_count(&self.registry) >= 1, + "registry alive on dispatch" + ); + self.telemetry.record_device_loss(); + let report = self.registry.handle_device_lost(device, queue); + assert!( + report.rebuilt_count + report.failed_count + report.vacant_count + == report.outcomes.len() as u32, + "report totals must reconcile with outcome vector", + ); + self.telemetry + .record_rebuild(report.rebuilt_count, report.failed_count); + } + + pub fn dispatch_device_loss_stub(&self) { + assert!( + Arc::strong_count(&self.registry) >= 1, + "registry alive on stub dispatch" + ); + self.telemetry.record_device_loss(); + } + + pub fn telemetry(&self) -> &Arc { + assert!( + Arc::strong_count(&self.telemetry) >= 1, + "telemetry alive on read" + ); + &self.telemetry + } + + pub fn registry(&self) -> &Arc { + assert!( + Arc::strong_count(&self.registry) >= 1, + "registry alive on read" + ); + &self.registry + } +} + +#[cfg(feature = "wgpu")] +pub fn attach_wgpu_device_loss_hook( + device: &wgpu::Device, + bridge: Arc, +) -> Result<(), GpuRebuildError> { + assert!( + Arc::strong_count(&bridge) >= 1, + "bridge arc must be alive when attaching hook" + ); + let captured = Arc::clone(&bridge); + device.set_device_lost_callback(move |reason, message| { + let _ = reason; + let _ = message; + captured.telemetry.record_device_loss(); + }); + let _ = device; + Ok(()) +} + +#[cfg(not(feature = "wgpu"))] +pub fn attach_wgpu_device_loss_hook(bridge: Arc) -> Result<(), GpuRebuildError> { + assert!( + Arc::strong_count(&bridge) >= 1, + "bridge arc must be alive when attaching stub" + ); + bridge.dispatch_device_loss_stub(); + Ok(()) +} + +#[cfg(test)] +mod tests { + use super::*; + use parking_lot::Mutex; + + struct MockOwner { + released: Arc, + rebuilt: Arc, + label_text: &'static str, + ready: Arc, + fail_rebuild: bool, + } + + impl GpuLossCallback for MockOwner { + fn release(&mut self) { + self.released.fetch_add(1, Ordering::SeqCst); + self.ready.store(false, Ordering::SeqCst); + } + + fn rebuild( + &mut self, + _device: &GpuDevice, + _queue: &GpuQueue, + ) -> Result<(), GpuRebuildError> { + self.rebuilt.fetch_add(1, Ordering::SeqCst); + if self.fail_rebuild { + return Err(GpuRebuildError::ResourceCreateFailed { reason: "mock" }); + } + self.ready.store(true, Ordering::SeqCst); + Ok(()) + } + + fn is_ready(&self) -> bool { + self.ready.load(Ordering::SeqCst) + } + + fn debug_label(&self) -> &'static str { + self.label_text + } + } + + fn make_mock_owner(fail_rebuild: bool) -> (Box, Arc, Arc) { + let released = Arc::new(AtomicU64::new(0)); + let rebuilt = Arc::new(AtomicU64::new(0)); + let ready = Arc::new(AtomicBool::new(true)); + let owner = Box::new(MockOwner { + released: Arc::clone(&released), + rebuilt: Arc::clone(&rebuilt), + label_text: "mock-linux-screen", + ready, + fail_rebuild, + }); + (owner, released, rebuilt) + } + + #[cfg(not(feature = "wgpu"))] + fn fresh_device_queue() -> (GpuDevice, GpuQueue) { + (GpuDevice { id: 1 }, GpuQueue { id: 1 }) + } + + #[test] + fn telemetry_starts_at_zero_counters() { + let telemetry = DeviceLossTelemetry::new(); + assert_eq!(telemetry.device_loss_total(), 0); + assert_eq!(telemetry.rebuild_total(), 0); + assert_eq!(telemetry.rebuild_failed_total(), 0); + } + + #[test] + fn record_device_loss_increments_counter_monotonically() { + let telemetry = DeviceLossTelemetry::new(); + telemetry.record_device_loss(); + telemetry.record_device_loss(); + telemetry.record_device_loss(); + assert_eq!(telemetry.device_loss_total(), 3); + } + + #[cfg(not(feature = "wgpu"))] + #[test] + fn dispatch_device_lost_drives_registry_and_telemetry() { + let registry = Arc::new(GpuLossRegistry::new()); + let telemetry = Arc::new(DeviceLossTelemetry::new()); + let bridge = DeviceLossBridge::new(Arc::clone(®istry), Arc::clone(&telemetry)); + + let (owner_a, released_a, rebuilt_a) = make_mock_owner(false); + let (owner_b, released_b, rebuilt_b) = make_mock_owner(false); + let _g1 = bridge.register(owner_a); + let _g2 = bridge.register(owner_b); + + let (device, queue) = fresh_device_queue(); + bridge.dispatch_device_lost(&device, &queue); + + assert_eq!(released_a.load(Ordering::SeqCst), 1); + assert_eq!(released_b.load(Ordering::SeqCst), 1); + assert_eq!(rebuilt_a.load(Ordering::SeqCst), 1); + assert_eq!(rebuilt_b.load(Ordering::SeqCst), 1); + assert_eq!(telemetry.device_loss_total(), 1); + assert_eq!(telemetry.rebuild_total(), 2); + assert_eq!(telemetry.rebuild_failed_total(), 0); + } + + #[cfg(not(feature = "wgpu"))] + #[test] + fn dispatch_device_lost_accumulates_failures_in_telemetry() { + let registry = Arc::new(GpuLossRegistry::new()); + let telemetry = Arc::new(DeviceLossTelemetry::new()); + let bridge = DeviceLossBridge::new(Arc::clone(®istry), Arc::clone(&telemetry)); + + let (owner_a, _, _) = make_mock_owner(true); + let (owner_b, _, _) = make_mock_owner(false); + let _g1 = bridge.register(owner_a); + let _g2 = bridge.register(owner_b); + + let (device, queue) = fresh_device_queue(); + bridge.dispatch_device_lost(&device, &queue); + bridge.dispatch_device_lost(&device, &queue); + + assert_eq!(telemetry.device_loss_total(), 2); + assert_eq!(telemetry.rebuild_total(), 2); + assert_eq!(telemetry.rebuild_failed_total(), 2); + } + + #[test] + fn bridge_marks_telemetry_attached_exactly_once() { + let registry = Arc::new(GpuLossRegistry::new()); + let telemetry = Arc::new(DeviceLossTelemetry::new()); + let _bridge = DeviceLossBridge::new(Arc::clone(®istry), Arc::clone(&telemetry)); + assert!(telemetry.registry_present.load(Ordering::SeqCst)); + } + + #[test] + #[should_panic(expected = "telemetry must not be attached twice")] + fn telemetry_cannot_be_attached_twice() { + let registry = Arc::new(GpuLossRegistry::new()); + let telemetry = Arc::new(DeviceLossTelemetry::new()); + let _first = DeviceLossBridge::new(Arc::clone(®istry), Arc::clone(&telemetry)); + let _second = DeviceLossBridge::new(Arc::clone(®istry), Arc::clone(&telemetry)); + } + + #[test] + fn registered_owners_remain_under_concurrent_register_pressure() { + let registry = Arc::new(GpuLossRegistry::new()); + let telemetry = Arc::new(DeviceLossTelemetry::new()); + let bridge = Arc::new(DeviceLossBridge::new( + Arc::clone(®istry), + Arc::clone(&telemetry), + )); + let collected: Arc>> = Arc::new(Mutex::new(Vec::new())); + + let mut handles = Vec::new(); + for _ in 0..8 { + let bridge = Arc::clone(&bridge); + let collected = Arc::clone(&collected); + handles.push(std::thread::spawn(move || { + let (owner, _, _) = make_mock_owner(false); + let guard = bridge.register(owner); + collected.lock().push(guard); + })); + } + for h in handles { + h.join().expect("worker must complete"); + } + assert_eq!(registry.len(), 8); + } + + #[test] + fn stub_dispatch_increments_only_telemetry_counter() { + let registry = Arc::new(GpuLossRegistry::new()); + let telemetry = Arc::new(DeviceLossTelemetry::new()); + let bridge = DeviceLossBridge::new(Arc::clone(®istry), Arc::clone(&telemetry)); + bridge.dispatch_device_loss_stub(); + bridge.dispatch_device_loss_stub(); + assert_eq!(telemetry.device_loss_total(), 2); + assert_eq!(telemetry.rebuild_total(), 0); + } +} diff --git a/fluxer_desktop/native/linux-screen-capture/src/lib.rs b/fluxer_desktop/native/linux-screen-capture/src/lib.rs new file mode 100644 index 0000000..51093c7 --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/src/lib.rs @@ -0,0 +1,27 @@ +#![deny(clippy::all)] +// SPDX-License-Identifier: AGPL-3.0-or-later +#![allow(dead_code)] +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::collapsible_if)] +#![allow(clippy::too_many_arguments)] +#![allow(clippy::missing_const_for_thread_local)] +#![allow(clippy::manual_is_multiple_of)] +#![allow(clippy::manual_saturating_arithmetic)] + +pub mod capture_state; +pub mod frame_buffer_pool; +pub mod gpu_loss; +pub mod nv12_packing; + +#[cfg(target_os = "linux")] +pub mod game_capture; +#[cfg(target_os = "linux")] +pub mod pipewire_stream; +#[cfg(target_os = "linux")] +pub mod portal; + +#[cfg(target_os = "linux")] +mod napi_surface_linux; + +#[cfg(not(target_os = "linux"))] +mod napi_surface_stub; diff --git a/fluxer_desktop/native/linux-screen-capture/src/napi_surface_linux.rs b/fluxer_desktop/native/linux-screen-capture/src/napi_surface_linux.rs new file mode 100644 index 0000000..0cd0400 --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/src/napi_surface_linux.rs @@ -0,0 +1,1207 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::ffi::c_void; +use std::sync::Arc; +use std::sync::Mutex; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; + +use napi::bindgen_prelude::{Function, Result, Unknown}; +use napi::threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}; +use napi::{JsValue, Status, ValueType}; +use napi_derive::napi; + +use fluxer_screen_frame_bus::{ + self as frame_bus, DmabufDesc as BusDmabufDesc, DmabufFrame as BusDmabufFrame, EnqueueOutcome, + NativeScreenFrameSinkHandle, NativeScreenFrameSinkHandleRef, Nv12Frame as BusNv12Frame, + ScreenFrame as BusScreenFrame, +}; +use std::os::fd::{FromRawFd, OwnedFd}; + +use crate::game_capture::{GameCaptureDiagnostics, GameCaptureVideoStream}; +use crate::pipewire_stream::{ + FrameCallback, LINUX_FRAME_BYTES_MAX, LINUX_FRAME_DIM_MAX, LifecycleCallback, + LinuxFrameBufferPool, PipeWireVideoStream, PoolExhaustionCallback, VideoFrame, + daemon_reachable, +}; +use crate::portal::{self, LiveSession, PortalError, SOURCE_TYPE_WINDOW, StreamInfo}; + +fn generic_error(reason: impl Into) -> napi::Error { + napi::Error::new(Status::GenericFailure, reason.into()) +} + +fn invalid_arg(reason: impl Into) -> napi::Error { + napi::Error::new(Status::InvalidArg, reason.into()) +} + +fn portal_error_to_status(err: &PortalError) -> &'static str { + match err { + PortalError::CursorModeUnavailable => "cursor-mode-unavailable", + PortalError::PortalTooOld(_) => "portal-too-old", + PortalError::Cancelled => "cancelled", + PortalError::PortalTimeout => "portal-timeout", + PortalError::DbusError => "dbus-error", + PortalError::InvalidReply => "invalid-reply", + PortalError::SendFailed => "send-failed", + PortalError::NoStreams => "no-streams", + } +} + +#[napi(object, js_name = "LinuxScreenCaptureSource")] +pub struct LinuxScreenCaptureSource { + pub kind: String, + pub id: String, + pub name: String, + pub width: u32, + pub height: u32, + pub app_name: Option, + pub bundle_id: Option, + + pub target_pid: Option, +} + +#[napi(object, js_name = "LinuxScreenCaptureCapabilities")] +pub struct Capabilities { + pub process: bool, + pub system: bool, +} + +#[napi(object, js_name = "LinuxScreenCaptureAvailability")] +pub struct Availability { + pub available: bool, + pub backend: String, + pub reason: Option, + pub detail: Option, + pub portal_version: Option, + pub capabilities: Capabilities, +} + +#[napi(object, js_name = "LinuxScreenCaptureBackendInfo")] +pub struct BackendInfo { + pub backend: String, + pub supported: bool, + pub reason: String, + pub portal_version: Option, + pub pipewire_reachable: bool, +} + +const BACKEND: &str = "linux-pipewire-portal"; + +#[napi(js_name = "getBackendInfo")] +pub fn get_backend_info() -> BackendInfo { + let portal_version = portal::read_portal_version().ok(); + let pipewire_reachable = daemon_reachable(); + let supported = matches!(portal_version, Some(v) if v >= 4) && pipewire_reachable; + let reason = if !pipewire_reachable { + "PipeWire daemon unreachable".to_string() + } else { + match portal_version { + None => "xdg-desktop-portal ScreenCast unavailable".to_string(), + Some(v) if v < 4 => { + format!("xdg-desktop-portal ScreenCast version {v} is below required 4") + } + _ => String::new(), + } + }; + BackendInfo { + backend: BACKEND.to_string(), + supported, + reason, + portal_version, + pipewire_reachable, + } +} + +#[napi(js_name = "getAvailability")] +pub async fn get_availability() -> Result { + let info = napi::bindgen_prelude::spawn_blocking(get_backend_info) + .await + .map_err(|_| generic_error("getBackendInfo task panicked"))?; + let (reason, detail) = if info.supported { + ( + None, + info.portal_version.map(|v| format!("portal version {v}")), + ) + } else { + let reason_code = if !info.pipewire_reachable { + "pipewire-unreachable" + } else if matches!(info.portal_version, Some(v) if v < 4) || info.portal_version.is_none() { + "portal-too-old" + } else { + "load-failed" + }; + (Some(reason_code.to_string()), Some(info.reason.clone())) + }; + Ok(Availability { + available: info.supported, + backend: BACKEND.to_string(), + reason, + detail, + portal_version: info.portal_version, + capabilities: Capabilities { + process: false, + system: info.supported, + }, + }) +} + +fn encode_source_id(node_id: u32) -> String { + node_id.to_string() +} + +fn parse_source_id(id: &str) -> Option { + id.parse::().ok().filter(|n| *n > 0) +} + +fn source_kind_for_type(source_type: u32) -> &'static str { + if source_type & SOURCE_TYPE_WINDOW != 0 { + "window" + } else { + "screen" + } +} + +fn display_name_for(stream: &StreamInfo) -> String { + if let Some(mapping) = stream.mapping_id.as_ref() + && !mapping.is_empty() + { + return mapping.clone(); + } + let kind = if stream.source_type & SOURCE_TYPE_WINDOW != 0 { + "Window" + } else { + "Display" + }; + format!("{kind} ({})", stream.node_id) +} + +#[napi(js_name = "listSources")] +pub async fn list_sources() -> Result> { + let result = napi::bindgen_prelude::spawn_blocking(portal::open_session_and_pick) + .await + .map_err(|_| generic_error("listSources task panicked"))?; + let (session, streams) = match result { + Ok(parts) => parts, + Err(err) => { + let code = portal_error_to_status(&err); + return Err(napi::Error::new( + Status::GenericFailure, + format!("listSources failed: {code} ({err})"), + )); + } + }; + let mut out = Vec::with_capacity(streams.len()); + for stream in &streams { + out.push(LinuxScreenCaptureSource { + kind: source_kind_for_type(stream.source_type).to_string(), + id: encode_source_id(stream.node_id), + name: display_name_for(stream), + width: stream.width, + height: stream.height, + app_name: None, + bundle_id: None, + target_pid: None, + }); + } + + park_session(session, streams); + Ok(out) +} + +struct ParkedSession { + session: LiveSession, + streams: Vec, +} + +static PARKED_SESSION: Mutex> = Mutex::new(None); + +fn park_session(session: LiveSession, streams: Vec) { + let mut guard = match PARKED_SESSION.lock() { + Ok(g) => g, + Err(p) => p.into_inner(), + }; + if let Some(old) = guard.take() { + old.session.close(); + } + *guard = Some(ParkedSession { session, streams }); +} + +fn adopt_session() -> Option { + let mut guard = match PARKED_SESSION.lock() { + Ok(g) => g, + Err(p) => p.into_inner(), + }; + guard.take() +} + +fn stream_dimensions_for_node(streams: &[StreamInfo], node_id: u32) -> (u32, u32) { + streams + .iter() + .find(|stream| stream.node_id == node_id) + .map(|stream| (stream.width, stream.height)) + .unwrap_or((0, 0)) +} + +type LifecycleTsfn = + Arc>; + +#[napi(object, js_name = "ScreenCaptureStartResult")] +pub struct ScreenCaptureStartResult { + pub width: u32, + pub height: u32, + pub frame_rate: u32, + pub pixel_format: String, +} + +#[napi(object, js_name = "ScreenCaptureRect")] +pub struct ScreenCaptureRect { + pub x: f64, + pub y: f64, + pub width: f64, + pub height: f64, +} + +#[napi(object, js_name = "ScreenCaptureStartOptions")] +pub struct ScreenCaptureStartOptions { + #[napi(js_name = "showCursorClicks")] + pub show_cursor_clicks: Option, + #[napi(js_name = "captureRect")] + pub capture_rect: Option, + #[napi(js_name = "colorRange")] + pub color_range: Option, + #[napi(js_name = "colorSpace")] + pub color_space: Option, +} + +#[derive(Clone, Debug, Default, PartialEq)] +struct NormalizedStartOptions { + show_cursor_clicks: bool, + capture_rect: Option, + color_range: Option, + color_space: Option, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +struct NormalizedCaptureRect { + x: f64, + y: f64, + width: u32, + height: u32, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +enum LinuxColorRange { + Full, + Limited, +} + +#[derive(Clone, Copy, Debug, PartialEq)] +enum LinuxColorSpace { + Rec709, + Srgb, +} + +#[napi(object, js_name = "FrameSinkDiagnostics")] +pub struct FrameSinkDiagnostics { + pub accepted: f64, + pub coalesced: f64, + pub rejected: f64, + #[napi(js_name = "mediaFramesDroppedWithoutSink")] + pub media_frames_dropped_without_sink: f64, +} + +#[napi(object, js_name = "LinuxScreenCaptureDiagnostics")] +pub struct ScreenCaptureDiagnostics { + pub backend: Option, + #[napi(js_name = "activeStrategy")] + pub active_strategy: Option, + #[napi(js_name = "requestedInjectionMethod")] + pub requested_injection_method: Option, + #[napi(js_name = "injectionMethod")] + pub injection_method: Option, + #[napi(js_name = "lastFallbackReason")] + pub last_fallback_reason: Option, + #[napi(js_name = "frameTransport")] + pub frame_transport: Option, + #[napi(js_name = "hostMappedCpuFallback")] + pub host_mapped_cpu_fallback: Option, + #[napi(js_name = "sourceDmabufMetadataAvailable")] + pub source_dmabuf_metadata_available: Option, + #[napi(js_name = "requestedImportMode")] + pub requested_import_mode: Option, + #[napi(js_name = "importMode")] + pub import_mode: Option, + #[napi(js_name = "mapHost")] + pub map_host: Option, + #[napi(js_name = "noModifiers")] + pub no_modifiers: Option, + pub linear: Option, + #[napi(js_name = "zeroCopy")] + pub zero_copy: Option, + #[napi(js_name = "gpuImportAvailable")] + pub gpu_import_available: Option, + #[napi(js_name = "deviceUuidAdvertised")] + pub device_uuid_advertised: Option, + #[napi(js_name = "supportedImportModes")] + pub supported_import_modes: Option>, + #[napi(js_name = "clientConnected")] + pub client_connected: Option, + #[napi(js_name = "connectedClient")] + pub connected_client: Option, + #[napi(js_name = "connectedPid")] + pub connected_pid: Option, + pub width: Option, + pub height: Option, + #[napi(js_name = "textureFormat")] + pub texture_format: Option, + #[napi(js_name = "textureModifier")] + pub texture_modifier: Option, + #[napi(js_name = "frameCounter")] + pub frame_counter: Option, + #[napi(js_name = "droppedFrameCounter")] + pub dropped_frame_counter: Option, + #[napi(js_name = "laggedFrameCounter")] + pub lagged_frame_counter: Option, + #[napi(js_name = "convertQueueDroppedFrameCounter")] + pub convert_queue_dropped_frame_counter: Option, + #[napi(js_name = "unsupportedFrameCounter")] + pub unsupported_frame_counter: Option, + #[napi(js_name = "lastPresentTimestampUs")] + pub last_present_timestamp_us: Option, + #[napi(js_name = "lastDiagnostic")] + pub last_diagnostic: Option, + #[napi(js_name = "lastAddonError")] + pub last_addon_error: Option, +} + +impl ScreenCaptureDiagnostics { + fn pipewire() -> Self { + Self { + backend: Some(BACKEND.to_string()), + active_strategy: Some("pipewire-portal".to_string()), + requested_injection_method: None, + injection_method: None, + last_fallback_reason: None, + frame_transport: None, + host_mapped_cpu_fallback: None, + source_dmabuf_metadata_available: None, + requested_import_mode: None, + import_mode: None, + map_host: None, + no_modifiers: None, + linear: None, + zero_copy: None, + gpu_import_available: None, + device_uuid_advertised: None, + supported_import_modes: None, + client_connected: None, + connected_client: None, + connected_pid: None, + width: None, + height: None, + texture_format: None, + texture_modifier: None, + frame_counter: None, + dropped_frame_counter: None, + lagged_frame_counter: None, + convert_queue_dropped_frame_counter: None, + unsupported_frame_counter: None, + last_present_timestamp_us: None, + last_diagnostic: None, + last_addon_error: None, + } + } +} + +impl From for ScreenCaptureDiagnostics { + fn from(value: GameCaptureDiagnostics) -> Self { + Self { + backend: Some(value.backend), + active_strategy: Some(value.active_strategy), + requested_injection_method: Some(value.requested_injection_method), + injection_method: Some(value.injection_method), + last_fallback_reason: value.last_fallback_reason, + frame_transport: Some(value.frame_transport), + host_mapped_cpu_fallback: Some(value.host_mapped_cpu_fallback), + source_dmabuf_metadata_available: Some(value.source_dmabuf_metadata_available), + requested_import_mode: Some(value.requested_import_mode), + import_mode: Some(value.import_mode), + map_host: Some(value.map_host), + no_modifiers: Some(value.no_modifiers), + linear: Some(value.linear), + zero_copy: Some(value.zero_copy), + gpu_import_available: Some(value.gpu_import_available), + device_uuid_advertised: Some(value.device_uuid_advertised), + supported_import_modes: Some(value.supported_import_modes), + client_connected: Some(value.client_connected), + connected_client: value.connected_client, + connected_pid: value.connected_pid, + width: value.width, + height: value.height, + texture_format: value.texture_format, + texture_modifier: value.texture_modifier, + frame_counter: Some(value.frame_counter as f64), + dropped_frame_counter: Some(value.dropped_frame_counter as f64), + lagged_frame_counter: Some(value.lagged_frame_counter as f64), + convert_queue_dropped_frame_counter: None, + unsupported_frame_counter: Some(value.unsupported_frame_counter as f64), + last_present_timestamp_us: value.last_present_timestamp_us, + last_diagnostic: value.last_diagnostic, + last_addon_error: value.last_addon_error, + } + } +} + +struct CaptureState { + lifecycle_tsfn: Option, + session: Option, + stream: Option, + game_stream: Option, +} + +struct CaptureInner { + state: Mutex, + running: Arc, + capture_id: Arc>>, + native_frame_sink: Arc>>>, + frame_sink_accepted: AtomicU64, + frame_sink_coalesced: AtomicU64, + frame_sink_rejected: AtomicU64, + media_frames_dropped_without_sink: AtomicU64, + native_sink_cpu_fallback_emitted: AtomicBool, +} + +#[napi(js_name = "ScreenCapture")] +pub struct ScreenCapture { + inner: Arc, +} + +#[napi] +impl ScreenCapture { + #[napi(constructor)] + pub fn new() -> Self { + Self { + inner: Arc::new(CaptureInner { + state: Mutex::new(CaptureState { + lifecycle_tsfn: None, + session: None, + stream: None, + game_stream: None, + }), + running: Arc::new(AtomicBool::new(false)), + capture_id: Arc::new(Mutex::new(None)), + native_frame_sink: Arc::new(Mutex::new(None)), + frame_sink_accepted: AtomicU64::new(0), + frame_sink_coalesced: AtomicU64::new(0), + frame_sink_rejected: AtomicU64::new(0), + media_frames_dropped_without_sink: AtomicU64::new(0), + native_sink_cpu_fallback_emitted: AtomicBool::new(false), + }), + } + } + + #[napi] + pub fn set_lifecycle_callback(&self, callback: Function<(String, String), ()>) -> Result<()> { + let tsfn: LifecycleTsfn = Arc::new( + callback + .build_threadsafe_function::<(String, String)>() + .max_queue_size::<8>() + .build_callback(|ctx| Ok(ctx.value))?, + ); + let mut state = lock_state(&self.inner)?; + state.lifecycle_tsfn = Some(tsfn); + Ok(()) + } + + #[napi(js_name = "setFrameSinkHandle")] + pub fn set_frame_sink_handle(&self, frame_sink_handle: Unknown<'_>) -> Result<()> { + let sink = retain_native_frame_sink_handle(frame_sink_handle)?; + let mut guard = self + .inner + .native_frame_sink + .lock() + .map_err(|_| generic_error("ScreenCapture native frame sink lock poisoned"))?; + *guard = Some(sink); + Ok(()) + } + + #[napi] + pub async fn start( + &self, + source_id: String, + source_kind: String, + width: u32, + height: u32, + frame_rate: u32, + capture_id: Option, + capture_options: Option, + ) -> Result { + if source_id.is_empty() { + return Err(invalid_arg( + "ScreenCapture.start sourceId must be non-empty", + )); + } + if source_kind != "screen" && source_kind != "window" && source_kind != "game" { + return Err(invalid_arg( + "ScreenCapture.start sourceKind must be 'screen', 'window', or 'game'", + )); + } + let start_options = normalize_start_options(capture_options)?; + + { + let mut guard = self + .inner + .capture_id + .lock() + .map_err(|_| generic_error("ScreenCapture capture_id lock poisoned"))?; + *guard = capture_id + .map(|raw| raw.trim().to_string()) + .filter(|trimmed| !trimmed.is_empty()); + } + + let lifecycle_tsfn = { + let state = lock_state(&self.inner)?; + state.lifecycle_tsfn.clone() + }; + + let captured_width = Arc::new(std::sync::atomic::AtomicU32::new(0)); + let captured_height = Arc::new(std::sync::atomic::AtomicU32::new(0)); + let captured_width_for_cb = captured_width.clone(); + let captured_height_for_cb = captured_height.clone(); + let capture_id_value = self + .inner + .capture_id + .lock() + .ok() + .and_then(|guard| guard.clone()); + let native_frame_sink = self + .inner + .native_frame_sink + .lock() + .ok() + .and_then(|guard| guard.clone()); + let bus_sink = if native_frame_sink.is_some() { + None + } else { + capture_id_value.as_deref().and_then(frame_bus::get_sink) + }; + let inner_for_frames = Arc::clone(&self.inner); + let frame_cb: FrameCallback = Arc::new(move |frame: VideoFrame| { + captured_width_for_cb.store(frame.width, Ordering::Release); + captured_height_for_cb.store(frame.height, Ordering::Release); + if let Some(sink) = native_frame_sink.as_deref() { + let cpu_fallback = frame.dmabuf.is_none(); + let outcome = enqueue_native_bus_video_frame(sink, &frame); + record_frame_sink_outcome(&inner_for_frames, outcome); + if cpu_fallback { + note_native_frame_sink_cpu_fallback(&inner_for_frames); + } + } else if let Some(sink) = bus_sink.as_deref() { + let outcome = enqueue_bus_video_frame(sink, frame); + record_frame_sink_outcome(&inner_for_frames, outcome); + } else { + inner_for_frames + .media_frames_dropped_without_sink + .fetch_add(1, Ordering::AcqRel); + } + }); + + let inner_running = self.inner.running.clone(); + let lifecycle_cb: LifecycleCallback = Arc::new(move |kind: &str, message: &str| { + if kind == "closed" || kind == "closed-clean" { + inner_running.store(false, Ordering::Release); + } + let Some(tsfn) = lifecycle_tsfn.as_ref() else { + return; + }; + let _: Status = tsfn.call( + (kind.to_string(), message.to_string()), + ThreadsafeFunctionCallMode::NonBlocking, + ); + }); + + let effective_fps = if frame_rate == 0 { + 30 + } else { + frame_rate.min(240) + }; + + emit_linux_start_option_diagnostics(&start_options, &lifecycle_cb); + if source_kind == "game" { + let game_stream = GameCaptureVideoStream::open(effective_fps, frame_cb, lifecycle_cb) + .map_err(|e| { + generic_error(format!("obs-vkcapture game stream open failed: {e}")) + })?; + { + let mut state = lock_state(&self.inner)?; + state.session = None; + state.stream = None; + state.game_stream = Some(game_stream); + } + self.inner.running.store(true, Ordering::Release); + return Ok(ScreenCaptureStartResult { + width, + height, + frame_rate: effective_fps, + pixel_format: "nv12".to_string(), + }); + } + + let node_id = parse_source_id(&source_id).ok_or_else(|| { + invalid_arg("ScreenCapture.start sourceId must be a positive u32 string") + })?; + let parked = adopt_session().ok_or_else(|| { + generic_error("no live portal session — call listSources() immediately before start()") + })?; + let session = parked.session; + let (portal_width, portal_height) = stream_dimensions_for_node(&parked.streams, node_id); + + let fd = match portal::open_pipewire_remote(&session) { + Ok(fd) => fd, + Err(err) => { + session.close(); + return Err(generic_error(format!( + "OpenPipeWireRemote failed: {} ({err})", + portal_error_to_status(&err) + ))); + } + }; + + let pool_width = start_options + .capture_rect + .map(|rect| rect.width) + .unwrap_or(width) + .max(portal_width); + let pool_height = start_options + .capture_rect + .map(|rect| rect.height) + .unwrap_or(height) + .max(portal_height); + let pool = build_linux_screen_pool(pool_width, pool_height)?; + let exhaust_cb: Option = None; + let stream = + PipeWireVideoStream::open(fd, node_id, frame_cb, lifecycle_cb, pool, exhaust_cb) + .map_err(|e| generic_error(format!("PipeWire stream open failed: {e}")))?; + + { + let mut state = lock_state(&self.inner)?; + state.session = Some(session); + state.stream = Some(stream); + state.game_stream = None; + } + self.inner.running.store(true, Ordering::Release); + + let captured_width = captured_width.load(Ordering::Acquire); + let captured_height = captured_height.load(Ordering::Acquire); + Ok(ScreenCaptureStartResult { + width: if captured_width > 0 { + captured_width + } else { + portal_width + }, + height: if captured_height > 0 { + captured_height + } else { + portal_height + }, + frame_rate: effective_fps, + pixel_format: "nv12".to_string(), + }) + } + + #[napi] + pub async fn stop(&self) -> Result<()> { + let was_running = self.inner.running.swap(false, Ordering::AcqRel); + if let Ok(mut guard) = self.inner.capture_id.lock() { + guard.take(); + } + if let Ok(mut guard) = self.inner.native_frame_sink.lock() { + guard.take(); + } + let (stream, game_stream, session) = { + let mut state = lock_state(&self.inner)?; + ( + state.stream.take(), + state.game_stream.take(), + state.session.take(), + ) + }; + drop(stream); + drop(game_stream); + if let Some(s) = session { + s.close(); + } + if was_running { + emit_lifecycle_tsfn(&self.inner, "closed-clean", "capture stopped"); + } + Ok(()) + } + + #[napi(js_name = "getDiagnostics")] + pub fn get_diagnostics(&self) -> Result> { + let state = lock_state(&self.inner)?; + if let Some(game_stream) = state.game_stream.as_ref() { + return Ok(Some(game_stream.diagnostics().into())); + } + if let Some(stream) = state.stream.as_ref() { + let mut diagnostics = ScreenCaptureDiagnostics::pipewire(); + diagnostics.dropped_frame_counter = Some(stream.frames_dropped_pool_exhausted() as f64); + diagnostics.convert_queue_dropped_frame_counter = + Some(stream.frames_dropped_convert_queue_full() as f64); + diagnostics.unsupported_frame_counter = Some(stream.frames_dropped_oversized() as f64); + return Ok(Some(diagnostics)); + } + Ok(None) + } + + #[napi(js_name = "getFrameSinkDiagnostics")] + pub fn get_frame_sink_diagnostics(&self) -> FrameSinkDiagnostics { + FrameSinkDiagnostics { + accepted: self.inner.frame_sink_accepted.load(Ordering::Acquire) as f64, + coalesced: self.inner.frame_sink_coalesced.load(Ordering::Acquire) as f64, + rejected: self.inner.frame_sink_rejected.load(Ordering::Acquire) as f64, + media_frames_dropped_without_sink: self + .inner + .media_frames_dropped_without_sink + .load(Ordering::Acquire) as f64, + } + } +} + +impl Drop for ScreenCapture { + fn drop(&mut self) { + self.inner.running.store(false, Ordering::Release); + if let Ok(mut guard) = self.inner.native_frame_sink.lock() { + guard.take(); + } + let (stream, game_stream, session) = match self.inner.state.lock() { + Ok(mut s) => (s.stream.take(), s.game_stream.take(), s.session.take()), + Err(_) => (None, None, None), + }; + drop(stream); + drop(game_stream); + if let Some(s) = session { + s.close(); + } + } +} + +fn lock_state(inner: &CaptureInner) -> Result> { + inner + .state + .lock() + .map_err(|_| generic_error("ScreenCapture state lock poisoned")) +} + +fn normalize_start_options( + options: Option, +) -> Result { + let Some(options) = options else { + return Ok(NormalizedStartOptions::default()); + }; + Ok(NormalizedStartOptions { + show_cursor_clicks: options.show_cursor_clicks.unwrap_or(false), + capture_rect: normalize_capture_rect(options.capture_rect)?, + color_range: normalize_color_range(options.color_range.as_deref())?, + color_space: normalize_color_space(options.color_space.as_deref())?, + }) +} + +fn normalize_capture_rect( + rect: Option, +) -> Result> { + let Some(rect) = rect else { + return Ok(None); + }; + if !rect.x.is_finite() || !rect.y.is_finite() { + return Err(invalid_arg( + "ScreenCapture.start captureRect origin must be finite", + )); + } + if !rect.width.is_finite() || !rect.height.is_finite() { + return Err(invalid_arg( + "ScreenCapture.start captureRect size must be finite", + )); + } + if rect.x < 0.0 || rect.y < 0.0 || rect.width <= 0.0 || rect.height <= 0.0 { + return Err(invalid_arg( + "ScreenCapture.start captureRect requires non-negative x/y and positive width/height", + )); + } + if rect.width > u32::MAX as f64 || rect.height > u32::MAX as f64 { + return Err(invalid_arg( + "ScreenCapture.start captureRect width/height exceed u32", + )); + } + Ok(Some(NormalizedCaptureRect { + x: rect.x, + y: rect.y, + width: rect.width.round() as u32, + height: rect.height.round() as u32, + })) +} + +fn normalize_color_range(value: Option<&str>) -> Result> { + match value { + None => Ok(None), + Some("full") => Ok(Some(LinuxColorRange::Full)), + Some("limited") => Ok(Some(LinuxColorRange::Limited)), + Some(_) => Err(invalid_arg( + "ScreenCapture.start colorRange must be 'limited' or 'full'", + )), + } +} + +fn normalize_color_space(value: Option<&str>) -> Result> { + match value { + None => Ok(None), + Some("rec709") => Ok(Some(LinuxColorSpace::Rec709)), + Some("srgb") => Ok(Some(LinuxColorSpace::Srgb)), + Some(_) => Err(invalid_arg( + "ScreenCapture.start colorSpace must be 'srgb' or 'rec709'", + )), + } +} + +fn emit_linux_start_option_diagnostics( + options: &NormalizedStartOptions, + lifecycle_cb: &LifecycleCallback, +) { + if options.show_cursor_clicks { + lifecycle_cb( + "diagnostic", + "showCursorClicks requested but Linux portal capture uses hidden cursor mode", + ); + } + if options.capture_rect.is_some() { + lifecycle_cb( + "diagnostic", + "captureRect requested; Linux PipeWire pool uses the requested size but portal capture cannot crop the compositor stream", + ); + } + if options.color_range.is_some() { + lifecycle_cb( + "diagnostic", + "colorRange requested but Linux PipeWire portal capture cannot force color range", + ); + } + if options.color_space.is_some() { + lifecycle_cb( + "diagnostic", + "colorSpace requested but Linux PipeWire portal capture cannot force color space", + ); + } +} + +fn record_frame_sink_outcome(inner: &CaptureInner, outcome: EnqueueOutcome) { + match outcome { + EnqueueOutcome::Accepted => { + inner.frame_sink_accepted.fetch_add(1, Ordering::AcqRel); + } + EnqueueOutcome::Coalesced => { + inner.frame_sink_coalesced.fetch_add(1, Ordering::AcqRel); + } + EnqueueOutcome::Rejected => { + inner.frame_sink_rejected.fetch_add(1, Ordering::AcqRel); + } + } +} + +fn note_native_frame_sink_cpu_fallback(inner: &CaptureInner) { + if inner + .native_sink_cpu_fallback_emitted + .swap(true, Ordering::AcqRel) + { + return; + } + emit_lifecycle_tsfn( + inner, + "error", + "Linux native frame sink requires DMA-BUF frames; refusing CPU-copy fallback", + ); +} + +fn emit_lifecycle_tsfn(inner: &CaptureInner, kind: &str, message: &str) { + let Ok(state) = inner.state.lock() else { + return; + }; + let Some(tsfn) = state.lifecycle_tsfn.as_ref() else { + return; + }; + let _: Status = tsfn.call( + (kind.to_string(), message.to_string()), + ThreadsafeFunctionCallMode::NonBlocking, + ); +} + +fn retain_native_frame_sink_handle( + value: Unknown<'_>, +) -> Result> { + if value.get_type()? != ValueType::External { + return Err(invalid_arg( + "ScreenCapture.setFrameSinkHandle expects a native external frame sink handle", + )); + } + + let raw_value = value.value(); + let mut data: *mut c_void = std::ptr::null_mut(); + let status = + unsafe { napi::sys::napi_get_value_external(raw_value.env, raw_value.value, &mut data) }; + if status != napi::sys::Status::napi_ok || data.is_null() { + return Err(invalid_arg( + "ScreenCapture.setFrameSinkHandle received an empty native external frame sink handle", + )); + } + + let handle = unsafe { + NativeScreenFrameSinkHandle::retain_from_raw(data.cast::()) + } + .ok_or_else(|| { + invalid_arg("ScreenCapture.setFrameSinkHandle received an invalid native frame sink handle") + })?; + + Ok(Arc::new(handle)) +} + +fn enqueue_native_bus_video_frame( + sink: &NativeScreenFrameSinkHandleRef, + frame: &VideoFrame, +) -> EnqueueOutcome { + if let Some(dmabuf) = frame.dmabuf.as_ref() { + if sink.handle().enqueue_dmabuf.is_none() { + return EnqueueOutcome::Rejected; + } + let plane_count = dmabuf.plane_count as usize; + if plane_count == 0 || plane_count > 4 || dmabuf.fds.len() < plane_count { + return EnqueueOutcome::Rejected; + } + + let mut duped_fds: Vec = Vec::with_capacity(plane_count); + for raw in dmabuf.fds.iter().take(plane_count) { + let duped = unsafe { libc::dup(*raw) }; + if duped < 0 { + for fd in duped_fds { + unsafe { + libc::close(fd); + } + } + return EnqueueOutcome::Rejected; + } + duped_fds.push(duped); + } + + let desc = BusDmabufDesc { + plane_count: dmabuf.plane_count as u8, + width: frame.width, + height: frame.height, + drm_format: dmabuf.drm_format, + modifier: dmabuf.modifier, + strides: dmabuf.strides, + offsets: dmabuf.offsets, + device_uuid: dmabuf.device_uuid.unwrap_or([0u8; 16]), + timestamp_us: frame.timestamp_us, + }; + + return unsafe { sink.enqueue_dmabuf_take_fds(desc, &duped_fds) }; + } + + sink.enqueue_nv12_copy( + &frame.data, + frame.width, + frame.height, + frame.stride_y, + frame.stride_uv, + frame.timestamp_us, + ) +} + +fn enqueue_bus_video_frame( + sink: &dyn frame_bus::ScreenFrameSink, + frame: VideoFrame, +) -> EnqueueOutcome { + if let Some(dmabuf) = frame.dmabuf.as_ref() { + let mut owned_fds: Vec = Vec::with_capacity(dmabuf.plane_count as usize); + for raw in dmabuf.fds.iter().take(dmabuf.plane_count as usize) { + let duped = unsafe { libc::dup(*raw) }; + if duped < 0 { + return EnqueueOutcome::Rejected; + } + owned_fds.push(unsafe { OwnedFd::from_raw_fd(duped) }); + } + let bus_dmabuf = BusDmabufFrame { + desc: BusDmabufDesc { + plane_count: dmabuf.plane_count as u8, + width: frame.width, + height: frame.height, + drm_format: dmabuf.drm_format, + modifier: dmabuf.modifier, + strides: dmabuf.strides, + offsets: dmabuf.offsets, + device_uuid: dmabuf.device_uuid.unwrap_or([0u8; 16]), + timestamp_us: frame.timestamp_us, + }, + fds: owned_fds, + }; + return sink.enqueue(BusScreenFrame::Dmabuf(bus_dmabuf)); + } + let VideoFrame { + width, + height, + stride_y, + stride_uv, + timestamp_us, + data, + .. + } = frame; + sink.enqueue(BusScreenFrame::Nv12(BusNv12Frame { + data: data.into_bus_frame_data(), + width, + height, + stride_y, + stride_uv, + timestamp_us, + })) +} + +const LINUX_POOL_DIM_MIN: usize = 64; +const LINUX_POOL_DIM_MAX: usize = LINUX_FRAME_DIM_MAX; +const LINUX_POOL_BYTES_MAX: usize = LINUX_FRAME_BYTES_MAX; +const LINUX_POOL_BYTES_MIN_NV12: usize = LINUX_POOL_DIM_MIN * LINUX_POOL_DIM_MIN * 3 / 2; + +fn build_linux_screen_pool(width: u32, height: u32) -> Result> { + let bytes_per_buffer = compute_pool_bytes_per_buffer(width, height); + assert!(bytes_per_buffer >= LINUX_POOL_BYTES_MIN_NV12); + assert!(bytes_per_buffer <= LINUX_POOL_BYTES_MAX); + LinuxFrameBufferPool::new(bytes_per_buffer).map_err(|err| { + generic_error(format!( + "Failed to build Linux screen capture frame pool: {err}" + )) + }) +} + +fn compute_pool_bytes_per_buffer(width: u32, height: u32) -> usize { + let w = (width as usize).clamp(LINUX_POOL_DIM_MIN, LINUX_POOL_DIM_MAX); + let h = (height as usize).clamp(LINUX_POOL_DIM_MIN, LINUX_POOL_DIM_MAX); + let nv12 = w.saturating_mul(h).saturating_mul(3) / 2; + let clamped = nv12.clamp(LINUX_POOL_BYTES_MIN_NV12, LINUX_POOL_BYTES_MAX); + assert!(clamped >= LINUX_POOL_BYTES_MIN_NV12); + assert!(clamped <= LINUX_POOL_BYTES_MAX); + clamped +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_source_id_roundtrip() { + let encoded = encode_source_id(7); + assert_eq!(encoded, "7"); + let node = parse_source_id(&encoded).expect("roundtrip"); + assert_eq!(node, 7); + } + + #[test] + fn parse_source_id_rejects_malformed() { + assert!(parse_source_id("garbage").is_none()); + assert!(parse_source_id("0").is_none()); + assert!(parse_source_id("").is_none()); + assert!(parse_source_id("-1").is_none()); + } + + #[test] + fn source_kind_for_type_maps_bits() { + assert_eq!(source_kind_for_type(SOURCE_TYPE_WINDOW), "window"); + assert_eq!( + source_kind_for_type(crate::portal::SOURCE_TYPE_MONITOR), + "screen" + ); + assert_eq!(source_kind_for_type(0), "screen"); + } + + #[test] + fn stream_dimensions_for_node_uses_selected_portal_stream() { + let streams = vec![ + StreamInfo { + node_id: 11, + source_type: 0, + mapping_id: None, + width: 1280, + height: 720, + position_x: 0, + position_y: 0, + }, + StreamInfo { + node_id: 42, + source_type: 0, + mapping_id: None, + width: 1920, + height: 1080, + position_x: 1280, + position_y: 0, + }, + ]; + + assert_eq!(stream_dimensions_for_node(&streams, 42), (1920, 1080)); + assert_eq!(stream_dimensions_for_node(&streams, 99), (0, 0)); + } + + #[test] + fn start_options_normalize_capture_rect_and_color_intent() { + let options = normalize_start_options(Some(ScreenCaptureStartOptions { + show_cursor_clicks: Some(true), + capture_rect: Some(ScreenCaptureRect { + x: 10.0, + y: 20.0, + width: 300.0, + height: 200.0, + }), + color_range: Some("full".to_string()), + color_space: Some("rec709".to_string()), + })) + .expect("valid options"); + assert!(options.show_cursor_clicks); + assert_eq!(options.color_range, Some(LinuxColorRange::Full)); + assert_eq!(options.color_space, Some(LinuxColorSpace::Rec709)); + assert_eq!( + options.capture_rect, + Some(NormalizedCaptureRect { + x: 10.0, + y: 20.0, + width: 300, + height: 200, + }) + ); + } + + #[test] + fn pool_bytes_cover_4k_nv12_stream() { + let bytes = compute_pool_bytes_per_buffer(3840, 2160); + assert_eq!(bytes, 3840 * 2160 * 3 / 2); + assert!(bytes <= LINUX_POOL_BYTES_MAX); + } + + #[test] + fn pool_bytes_clamp_dimensions_to_named_caps() { + let oversized = compute_pool_bytes_per_buffer(u32::MAX, u32::MAX); + assert_eq!(oversized, LINUX_POOL_DIM_MAX * LINUX_POOL_DIM_MAX * 3 / 2); + let undersized = compute_pool_bytes_per_buffer(1, 1); + assert_eq!(undersized, LINUX_POOL_BYTES_MIN_NV12); + } + + #[test] + fn start_options_reject_invalid_capture_rect() { + let err = normalize_start_options(Some(ScreenCaptureStartOptions { + show_cursor_clicks: None, + capture_rect: Some(ScreenCaptureRect { + x: 0.0, + y: 0.0, + width: 0.0, + height: 200.0, + }), + color_range: None, + color_space: None, + })) + .expect_err("invalid capture rect"); + assert_eq!(err.status, Status::InvalidArg); + } +} diff --git a/fluxer_desktop/native/linux-screen-capture/src/napi_surface_stub.rs b/fluxer_desktop/native/linux-screen-capture/src/napi_surface_stub.rs new file mode 100644 index 0000000..a66c5f8 --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/src/napi_surface_stub.rs @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use napi::bindgen_prelude::{Error, Result, Status}; +use napi_derive::napi; + +fn unsupported() -> Error { + Error::new( + Status::GenericFailure, + "@fluxer/linux-screen-capture is only supported on Linux", + ) +} + +#[napi(object, js_name = "LinuxScreenCaptureSource")] +pub struct LinuxScreenCaptureSource { + pub kind: String, + pub id: String, + pub name: String, + pub width: u32, + pub height: u32, + pub app_name: Option, + pub bundle_id: Option, + pub target_pid: Option, +} + +#[napi(js_name = "listSources")] +pub fn list_sources() -> Result> { + Ok(Vec::new()) +} + +#[napi(object, js_name = "LinuxScreenCaptureCapabilities")] +pub struct Capabilities { + pub process: bool, + pub system: bool, +} + +#[napi(object, js_name = "LinuxScreenCaptureAvailability")] +pub struct Availability { + pub available: bool, + pub backend: String, + pub reason: Option, + pub detail: Option, + pub portal_version: Option, + pub capabilities: Capabilities, +} + +#[napi(js_name = "getAvailability")] +pub fn get_availability() -> Result { + Ok(Availability { + available: false, + backend: "linux-pipewire-portal".to_string(), + reason: Some("unsupported-platform".to_string()), + detail: None, + portal_version: None, + capabilities: Capabilities { + process: false, + system: false, + }, + }) +} + +#[napi(object, js_name = "LinuxScreenCaptureBackendInfo")] +pub struct BackendInfo { + pub backend: String, + pub supported: bool, + pub reason: String, + pub portal_version: Option, + pub pipewire_reachable: bool, +} + +#[napi(js_name = "getBackendInfo")] +pub fn get_backend_info() -> BackendInfo { + BackendInfo { + backend: "linux-pipewire-portal".to_string(), + supported: false, + reason: "@fluxer/linux-screen-capture is only supported on Linux".to_string(), + portal_version: None, + pipewire_reachable: false, + } +} + +#[napi] +pub struct ScreenCapture; + +#[napi] +impl ScreenCapture { + #[napi(constructor)] + pub fn new() -> Result { + Err(unsupported()) + } +} diff --git a/fluxer_desktop/native/linux-screen-capture/src/nv12_packing.rs b/fluxer_desktop/native/linux-screen-capture/src/nv12_packing.rs new file mode 100644 index 0000000..7cc9111 --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/src/nv12_packing.rs @@ -0,0 +1,491 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Debug, Clone, Copy)] +pub struct Nv12Layout { + pub width: u32, + pub height: u32, + pub stride_y: u32, + pub stride_uv: u32, +} + +impl Nv12Layout { + pub fn packed_size(&self) -> Option { + if self.width == 0 || self.height == 0 || self.height % 2 != 0 { + return None; + } + let w = self.width as usize; + let h = self.height as usize; + let y_bytes = w.checked_mul(h)?; + let uv_bytes = w.checked_mul(h / 2)?; + y_bytes.checked_add(uv_bytes) + } + + pub fn packed_stride_y(&self) -> u32 { + self.width + } + + pub fn packed_stride_uv(&self) -> u32 { + self.width + } +} + +pub fn pack_nv12(layout: Nv12Layout, y_plane: &[u8], uv_plane: &[u8], dst: &mut [u8]) -> bool { + let Some(total) = layout.packed_size() else { + return false; + }; + if dst.len() != total { + return false; + } + let w = layout.width as usize; + let h = layout.height as usize; + let stride_y = layout.stride_y as usize; + let stride_uv = layout.stride_uv as usize; + if stride_y < w || stride_uv < w { + return false; + } + if y_plane.len() < stride_y * h { + return false; + } + if uv_plane.len() < stride_uv * (h / 2) { + return false; + } + + let y_bytes = w * h; + let uv_bytes = w * (h / 2); + if stride_y == w && stride_uv == w { + dst[..y_bytes].copy_from_slice(&y_plane[..y_bytes]); + dst[y_bytes..y_bytes + uv_bytes].copy_from_slice(&uv_plane[..uv_bytes]); + } else { + let (dst_y, dst_uv) = dst.split_at_mut(y_bytes); + for row in 0..h { + let src_off = row * stride_y; + let dst_off = row * w; + dst_y[dst_off..dst_off + w].copy_from_slice(&y_plane[src_off..src_off + w]); + } + for row in 0..h / 2 { + let src_off = row * stride_uv; + let dst_off = row * w; + dst_uv[dst_off..dst_off + w].copy_from_slice(&uv_plane[src_off..src_off + w]); + } + } + true +} + +#[cfg(target_os = "linux")] +fn bgra_to_nv12_dcp(w: u32, h: u32, bgra: &[u8], bgra_stride: usize, dst: &mut [u8]) -> bool { + use dcv_color_primitives as dcp; + let src_format = dcp::ImageFormat { + pixel_format: dcp::PixelFormat::Bgra, + color_space: dcp::ColorSpace::Rgb, + num_planes: 1, + }; + let dst_format = dcp::ImageFormat { + pixel_format: dcp::PixelFormat::Nv12, + color_space: dcp::ColorSpace::Bt601, + num_planes: 2, + }; + let y_bytes = (w as usize) * (h as usize); + let (dst_y, dst_uv) = dst.split_at_mut(y_bytes); + dcp::convert_image( + w, + h, + &src_format, + Some(&[bgra_stride]), + &[bgra], + &dst_format, + Some(&[w as usize, w as usize]), + &mut [dst_y, dst_uv], + ) + .is_ok() +} + +fn flip_nv12_vertical(w: usize, h: usize, dst: &mut [u8]) { + assert!(w > 0); + assert!(h % 2 == 0); + let y_bytes = w * h; + let uv_bytes = w * (h / 2); + assert!(dst.len() >= y_bytes + uv_bytes); + let (y_plane, rest) = dst.split_at_mut(y_bytes); + flip_plane_rows(y_plane, w, h); + flip_plane_rows(&mut rest[..uv_bytes], w, h / 2); +} + +fn flip_plane_rows(plane: &mut [u8], row_bytes: usize, rows: usize) { + assert!(row_bytes > 0); + assert!(plane.len() >= row_bytes * rows); + for row in 0..rows / 2 { + let top_start = row * row_bytes; + let bottom_start = (rows - 1 - row) * row_bytes; + let (head, tail) = plane.split_at_mut(bottom_start); + head[top_start..top_start + row_bytes].swap_with_slice(&mut tail[..row_bytes]); + } +} + +fn bgra_to_nv12_scalar( + w: usize, + h: usize, + bgra: &[u8], + bgra_row: usize, + dst: &mut [u8], + flip: bool, +) -> bool { + let y_bytes = w * h; + let (dst_y, dst_uv) = dst.split_at_mut(y_bytes); + for row in 0..h { + let dst_row_index = if flip { h - 1 - row } else { row }; + let src_row = &bgra[row * bgra_row..row * bgra_row + w * 4]; + let dst_row = &mut dst_y[dst_row_index * w..dst_row_index * w + w]; + for col in 0..w { + let b = src_row[col * 4] as i32; + let g = src_row[col * 4 + 1] as i32; + let r = src_row[col * 4 + 2] as i32; + let y = (66 * r + 129 * g + 25 * b + 128) >> 8; + dst_row[col] = (y + 16).clamp(0, 255) as u8; + } + } + for row in 0..h / 2 { + let dst_row_index = if flip { h / 2 - 1 - row } else { row }; + let r0 = &bgra[(row * 2) * bgra_row..(row * 2) * bgra_row + w * 4]; + let r1 = &bgra[(row * 2 + 1) * bgra_row..(row * 2 + 1) * bgra_row + w * 4]; + let dst_row = &mut dst_uv[dst_row_index * w..dst_row_index * w + w]; + for col in 0..w / 2 { + let cx0 = col * 2 * 4; + let cx1 = (col * 2 + 1) * 4; + let b = (r0[cx0] as i32 + r0[cx1] as i32 + r1[cx0] as i32 + r1[cx1] as i32) >> 2; + let g = + (r0[cx0 + 1] as i32 + r0[cx1 + 1] as i32 + r1[cx0 + 1] as i32 + r1[cx1 + 1] as i32) + >> 2; + let r = + (r0[cx0 + 2] as i32 + r0[cx1 + 2] as i32 + r1[cx0 + 2] as i32 + r1[cx1 + 2] as i32) + >> 2; + let u = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128; + let v = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128; + dst_row[col * 2] = u.clamp(0, 255) as u8; + dst_row[col * 2 + 1] = v.clamp(0, 255) as u8; + } + } + true +} + +pub fn bgra_to_nv12( + layout: Nv12Layout, + bgra: &[u8], + bgra_stride: u32, + dst: &mut [u8], + flip: bool, +) -> bool { + let Some(total) = layout.packed_size() else { + return false; + }; + if dst.len() < total { + return false; + } + let w = layout.width as usize; + let h = layout.height as usize; + let bgra_row = bgra_stride as usize; + if bgra_row < w.checked_mul(4).unwrap_or(usize::MAX) { + return false; + } + if bgra.len() < bgra_row.checked_mul(h).unwrap_or(usize::MAX) { + return false; + } + #[cfg(target_os = "linux")] + if bgra_to_nv12_dcp(layout.width, layout.height, bgra, bgra_row, dst) { + if flip { + flip_nv12_vertical(w, h, dst); + } + return true; + } + bgra_to_nv12_scalar(w, h, bgra, bgra_row, dst, flip) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn make_y(width: usize, height: usize, stride: usize) -> Vec { + let mut v = vec![0u8; stride * height]; + for row in 0..height { + for col in 0..width { + v[row * stride + col] = ((row * width + col) % 251) as u8; + } + } + v + } + + fn make_uv(width: usize, height_half: usize, stride: usize) -> Vec { + let mut v = vec![0u8; stride * height_half]; + for row in 0..height_half { + for col in 0..width { + v[row * stride + col] = ((row * width + col + 7) % 241) as u8; + } + } + v + } + + #[test] + fn packed_size_rejects_odd_height() { + let layout = Nv12Layout { + width: 16, + height: 15, + stride_y: 16, + stride_uv: 16, + }; + assert!(layout.packed_size().is_none()); + } + + #[test] + fn packed_size_rejects_zero_dims() { + let layout = Nv12Layout { + width: 0, + height: 4, + stride_y: 0, + stride_uv: 0, + }; + assert!(layout.packed_size().is_none()); + } + + #[test] + fn packed_size_matches_yuv420_layout() { + let layout = Nv12Layout { + width: 1920, + height: 1080, + stride_y: 1920, + stride_uv: 1920, + }; + assert_eq!(Some(1920 * 1080 * 3 / 2), layout.packed_size()); + } + + #[test] + fn pack_nv12_strips_row_padding() { + let layout = Nv12Layout { + width: 8, + height: 4, + stride_y: 16, + stride_uv: 16, + }; + let y = make_y(8, 4, 16); + let uv = make_uv(8, 2, 16); + let mut dst = vec![0u8; layout.packed_size().unwrap()]; + assert!(pack_nv12(layout, &y, &uv, &mut dst)); + for row in 0..4 { + for col in 0..8 { + assert_eq!(dst[row * 8 + col], ((row * 8 + col) % 251) as u8); + } + } + for row in 0..2 { + for col in 0..8 { + let off = 8 * 4 + row * 8 + col; + assert_eq!(dst[off], ((row * 8 + col + 7) % 241) as u8); + } + } + } + + #[test] + fn pack_nv12_zero_padding_is_identity() { + let layout = Nv12Layout { + width: 4, + height: 2, + stride_y: 4, + stride_uv: 4, + }; + let y = vec![1, 2, 3, 4, 5, 6, 7, 8]; + let uv = vec![10, 11, 12, 13]; + let mut dst = vec![0u8; layout.packed_size().unwrap()]; + assert!(pack_nv12(layout, &y, &uv, &mut dst)); + assert_eq!(dst, vec![1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13]); + } + + #[test] + fn pack_nv12_rejects_short_source() { + let layout = Nv12Layout { + width: 4, + height: 2, + stride_y: 4, + stride_uv: 4, + }; + let y = vec![1, 2, 3]; + let uv = vec![10, 11, 12, 13]; + let mut dst = vec![0u8; layout.packed_size().unwrap()]; + assert!(!pack_nv12(layout, &y, &uv, &mut dst)); + } + + #[test] + fn pack_nv12_rejects_undersized_stride() { + let layout = Nv12Layout { + width: 8, + height: 2, + stride_y: 4, + stride_uv: 4, + }; + let y = vec![0; 8]; + let uv = vec![0; 4]; + let mut dst = vec![0u8; 24]; + assert!(!pack_nv12(layout, &y, &uv, &mut dst)); + } + + #[test] + fn pack_nv12_rejects_wrong_dst_size() { + let layout = Nv12Layout { + width: 4, + height: 2, + stride_y: 4, + stride_uv: 4, + }; + let y = vec![0; 8]; + let uv = vec![0; 4]; + let mut dst = vec![0u8; 11]; + assert!(!pack_nv12(layout, &y, &uv, &mut dst)); + } + + #[test] + fn bgra_to_nv12_solid_black() { + let layout = Nv12Layout { + width: 4, + height: 2, + stride_y: 4, + stride_uv: 4, + }; + let bgra = vec![0u8; 4 * 4 * 2]; + let mut dst = vec![0u8; layout.packed_size().unwrap()]; + assert!(bgra_to_nv12(layout, &bgra, 16, &mut dst, false)); + for byte in &dst[..8] { + assert_eq!(*byte, 16); + } + for chunk in dst[8..].chunks_exact(2) { + assert_eq!(chunk[0], 128); + assert_eq!(chunk[1], 128); + } + } + + #[test] + fn bgra_to_nv12_solid_white_is_in_range() { + let layout = Nv12Layout { + width: 4, + height: 2, + stride_y: 4, + stride_uv: 4, + }; + let bgra = vec![255u8; 4 * 4 * 2]; + let mut dst = vec![0u8; layout.packed_size().unwrap()]; + assert!(bgra_to_nv12(layout, &bgra, 16, &mut dst, false)); + for byte in &dst[..8] { + assert!(*byte >= 230 && *byte <= 240, "luma out of range: {byte}"); + } + for chunk in dst[8..].chunks_exact(2) { + assert!( + chunk[0] >= 124 && chunk[0] <= 132, + "U out of range: {}", + chunk[0] + ); + assert!( + chunk[1] >= 124 && chunk[1] <= 132, + "V out of range: {}", + chunk[1] + ); + } + } + + #[test] + fn bgra_to_nv12_rejects_short_stride() { + let layout = Nv12Layout { + width: 4, + height: 2, + stride_y: 4, + stride_uv: 4, + }; + let bgra = vec![0u8; 8]; + let mut dst = vec![0u8; layout.packed_size().unwrap()]; + assert!(!bgra_to_nv12(layout, &bgra, 4, &mut dst, false)); + } + + fn deterministic_bgra_frame(w: usize, h: usize, seed: u64) -> Vec { + let mut state = seed; + let mut v = vec![0u8; w * h * 4]; + for byte in v.iter_mut() { + state = state + .wrapping_mul(6364136223846793005) + .wrapping_add(1442695040888963407); + *byte = (state >> 56) as u8; + } + v + } + + fn row_reversed(bgra: &[u8], w: usize, h: usize) -> Vec { + let row_bytes = w * 4; + assert_eq!(bgra.len(), row_bytes * h); + let mut reversed = vec![0u8; bgra.len()]; + for row in 0..h { + let src = (h - 1 - row) * row_bytes; + reversed[row * row_bytes..(row + 1) * row_bytes] + .copy_from_slice(&bgra[src..src + row_bytes]); + } + reversed + } + + #[test] + fn bgra_to_nv12_flip_matches_pre_reversed_rows() { + for (w, h, seed) in [(8usize, 4usize, 1u64), (16, 8, 2), (64, 32, 3), (12, 6, 4)] { + let layout = Nv12Layout { + width: w as u32, + height: h as u32, + stride_y: w as u32, + stride_uv: w as u32, + }; + let bgra = deterministic_bgra_frame(w, h, seed); + let reversed = row_reversed(&bgra, w, h); + let total = layout.packed_size().expect("even dims"); + let mut flipped = vec![0u8; total]; + let mut reference = vec![0u8; total]; + assert!(bgra_to_nv12( + layout, + &bgra, + (w * 4) as u32, + &mut flipped, + true + )); + assert!(bgra_to_nv12( + layout, + &reversed, + (w * 4) as u32, + &mut reference, + false + )); + assert_eq!(flipped, reference, "mismatch at {w}x{h} seed {seed}"); + } + } + + #[test] + fn bgra_to_nv12_flip_moves_top_row_luma_to_bottom() { + let layout = Nv12Layout { + width: 4, + height: 4, + stride_y: 4, + stride_uv: 4, + }; + let mut bgra = vec![0u8; 4 * 4 * 4]; + bgra[..16].fill(255); + let mut dst = vec![0u8; layout.packed_size().unwrap()]; + assert!(bgra_to_nv12(layout, &bgra, 16, &mut dst, true)); + let y = &dst[..16]; + assert!(y[12] > 200, "bottom row should hold the white luma: {y:?}"); + assert!(y[0] < 32, "top row should hold the black luma: {y:?}"); + } + + #[test] + fn bgra_to_nv12_unflipped_path_is_unchanged_by_flip_support() { + let layout = Nv12Layout { + width: 8, + height: 4, + stride_y: 8, + stride_uv: 8, + }; + let bgra = deterministic_bgra_frame(8, 4, 9); + let total = layout.packed_size().unwrap(); + let mut first = vec![0u8; total]; + let mut second = vec![0u8; total]; + assert!(bgra_to_nv12(layout, &bgra, 32, &mut first, false)); + assert!(bgra_to_nv12(layout, &bgra, 32, &mut second, false)); + assert_eq!(first, second); + } +} diff --git a/fluxer_desktop/native/linux-screen-capture/src/pipewire_stream.rs b/fluxer_desktop/native/linux-screen-capture/src/pipewire_stream.rs new file mode 100644 index 0000000..e5d2324 --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/src/pipewire_stream.rs @@ -0,0 +1,1573 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::env; +use std::os::fd::OwnedFd; +use std::sync::Arc; +use std::sync::Mutex; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use std::sync::mpsc::{Receiver, SyncSender, TrySendError, sync_channel}; +use std::thread::{self, JoinHandle}; +use std::time::Duration; + +use pipewire as pw; +use pw::channel::{Receiver as PwReceiver, Sender as PwSender, channel as pw_channel}; +use pw::context::ContextRc; +use pw::keys; +use pw::main_loop::MainLoopRc; +use pw::properties::properties; +use pw::spa; +use spa::buffer::{Data, DataType}; +use spa::param::format::{MediaSubtype, MediaType}; +use spa::param::format_utils; +use spa::param::video::{VideoFormat, VideoInfoRaw}; +use spa::pod::{ChoiceValue, Object, Pod, Property, PropertyFlags, Value as SpaValue}; +use spa::utils::{Choice, ChoiceEnum, ChoiceFlags, Fraction, Rectangle, SpaTypes}; + +use crate::capture_state::{LinuxCaptureEvent, LinuxCaptureFault, LinuxCaptureStateMachine}; +use crate::frame_buffer_pool::PooledFrameBuffer; +use crate::nv12_packing::{Nv12Layout, bgra_to_nv12, pack_nv12}; + +pub use crate::frame_buffer_pool::{ + LINUX_FRAME_BYTES_MAX, LINUX_FRAME_DIM_MAX, LINUX_SCREEN_FRAME_POOL_CAP, LinuxFrameBufferPool, +}; + +const READY_TIMEOUT: Duration = Duration::from_millis(2_000); +const STAGING_SLOT_COUNT: usize = 2; +const MODIFIER_COUNT_MAX: usize = 16; +const SCREEN_CAPTURE_DMABUF_ENV: &str = "FLUXER_SCREEN_CAPTURE_DMABUF"; + +const DRM_FORMAT_XRGB8888: u32 = fourcc(*b"XR24"); +const DRM_FORMAT_ARGB8888: u32 = fourcc(*b"AR24"); +const DRM_FORMAT_NV12: u32 = fourcc(*b"NV12"); +const DRM_FORMAT_MOD_LINEAR: u64 = 0; +const DRM_FORMAT_MOD_INVALID: u64 = (1u64 << 56) - 1; +const DMABUF_MODIFIERS_BASELINE: [u64; 2] = [DRM_FORMAT_MOD_LINEAR, DRM_FORMAT_MOD_INVALID]; + +const fn fourcc(bytes: [u8; 4]) -> u32 { + bytes[0] as u32 | (bytes[1] as u32) << 8 | (bytes[2] as u32) << 16 | (bytes[3] as u32) << 24 +} + +pub type FrameCallback = Arc; +pub type LifecycleCallback = Arc; +pub type PoolExhaustionCallback = Arc; + +pub struct VideoFrame { + pub width: u32, + pub height: u32, + pub stride_y: u32, + pub stride_uv: u32, + pub timestamp_us: i64, + pub data: VideoFrameData, + pub dmabuf: Option, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct DmabufFrameMetadata { + pub fds: [i32; 4], + pub plane_count: u32, + pub drm_format: u32, + pub modifier: u64, + pub strides: [u32; 4], + pub offsets: [u32; 4], + pub device_uuid: Option<[u8; 16]>, +} + +pub enum VideoFrameData { + Empty, + Owned(Vec), + Pooled(PooledFrameBuffer), +} + +impl VideoFrameData { + pub fn empty() -> Self { + Self::Empty + } + + pub fn from_vec(buf: Vec) -> Self { + let len = buf.len(); + let data = Self::Owned(buf); + assert_eq!(data.len(), len); + data + } + + pub fn as_slice(&self) -> &[u8] { + match self { + Self::Empty => &[], + Self::Owned(v) => v.as_slice(), + Self::Pooled(p) => p.as_slice(), + } + } + + pub fn len(&self) -> usize { + let len = match self { + Self::Empty => 0, + Self::Owned(v) => v.len(), + Self::Pooled(p) => p.len(), + }; + assert!(len <= LINUX_FRAME_BYTES_MAX); + len + } + + pub fn is_empty(&self) -> bool { + let empty = match self { + Self::Empty => true, + Self::Owned(v) => v.is_empty(), + Self::Pooled(p) => p.len() == 0, + }; + assert!(empty == (self.len() == 0)); + empty + } + + pub fn to_vec(&self) -> Vec { + let cloned = match self { + Self::Empty => Vec::new(), + Self::Owned(v) => v.clone(), + Self::Pooled(p) => p.as_slice().to_vec(), + }; + assert_eq!(cloned.len(), self.len()); + cloned + } + + pub fn into_bus_frame_data(self) -> fluxer_screen_frame_bus::FrameData { + let len = self.len(); + let data = match self { + Self::Empty => fluxer_screen_frame_bus::FrameData::from(Vec::new()), + Self::Owned(v) => fluxer_screen_frame_bus::FrameData::from(v), + Self::Pooled(p) => p.into_shared_frame_data(), + }; + assert_eq!(data.len(), len); + data + } +} + +impl std::ops::Deref for VideoFrameData { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + self.as_slice() + } +} + +impl std::fmt::Debug for VideoFrameData { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Empty => f.debug_tuple("VideoFrameData::Empty").finish(), + Self::Owned(v) => f + .debug_struct("VideoFrameData::Owned") + .field("len", &v.len()) + .finish(), + Self::Pooled(p) => f + .debug_struct("VideoFrameData::Pooled") + .field("len", &p.len()) + .field("slot_index", &p.slot_index()) + .finish(), + } + } +} + +#[derive(Debug)] +pub enum BridgeError { + DaemonUnreachable, + Spawn, + Negotiate, +} + +impl std::fmt::Display for BridgeError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::DaemonUnreachable => f.write_str("DaemonUnreachable"), + Self::Spawn => f.write_str("Spawn"), + Self::Negotiate => f.write_str("Negotiate"), + } + } +} + +enum Command { + Stop, +} + +pub struct PipeWireVideoStream { + tx: PwSender, + running: Arc, + thread: std::sync::Mutex>>, + convert_thread: std::sync::Mutex>>, + pool: Arc, + frames_dropped_convert_queue_full: Arc, +} + +impl PipeWireVideoStream { + pub fn open( + portal_fd: OwnedFd, + node_id: u32, + on_frame: FrameCallback, + on_lifecycle: LifecycleCallback, + pool: Arc, + on_pool_exhausted: Option, + ) -> Result { + assert!(pool.capacity() == LINUX_SCREEN_FRAME_POOL_CAP); + assert!(pool.bytes_per_buffer() > 0); + let (tx, rx) = pw_channel::(); + let running = Arc::new(AtomicBool::new(true)); + let (ready_tx, ready_rx) = std::sync::mpsc::sync_channel::(1); + let frames_dropped_convert_queue_full = Arc::new(AtomicU64::new(0)); + let (staging_rt, staging_worker) = build_staging_channels(&pool); + let convert_thread = spawn_convert_worker( + staging_worker, + Arc::clone(&pool), + on_frame.clone(), + on_pool_exhausted, + ) + .map_err(|_| BridgeError::Spawn)?; + let spawned = spawn_pw_thread( + portal_fd, + node_id, + rx, + ready_tx, + running.clone(), + on_frame, + on_lifecycle, + Arc::clone(&pool), + staging_rt, + Arc::clone(&frames_dropped_convert_queue_full), + ); + let handle = match spawned { + Ok(handle) => handle, + Err(_) => { + let _ = convert_thread.join(); + return Err(BridgeError::Spawn); + } + }; + match ready_rx.recv_timeout(READY_TIMEOUT) { + Ok(true) => Ok(Self { + tx, + running, + thread: std::sync::Mutex::new(Some(handle)), + convert_thread: std::sync::Mutex::new(Some(convert_thread)), + pool, + frames_dropped_convert_queue_full, + }), + _ => { + let _ = tx.send(Command::Stop); + let _ = handle.join(); + let _ = convert_thread.join(); + Err(BridgeError::DaemonUnreachable) + } + } + } + + pub fn stop(&self) { + self.running.store(false, Ordering::Release); + let _ = self.tx.send(Command::Stop); + } + + pub fn frames_dropped_pool_exhausted(&self) -> u64 { + self.pool.frames_dropped_pool_exhausted() + } + + pub fn frames_dropped_oversized(&self) -> u64 { + self.pool.frames_dropped_oversized() + } + + pub fn frames_dropped_convert_queue_full(&self) -> u64 { + let dropped = self + .frames_dropped_convert_queue_full + .load(Ordering::Relaxed); + assert!(dropped <= u64::MAX / 2); + dropped + } +} + +impl Drop for PipeWireVideoStream { + fn drop(&mut self) { + self.stop(); + if let Ok(mut guard) = self.thread.lock() + && let Some(handle) = guard.take() + { + let _ = handle.join(); + } + if let Ok(mut guard) = self.convert_thread.lock() + && let Some(handle) = guard.take() + { + let _ = handle.join(); + } + } +} + +struct UserData { + on_frame: FrameCallback, + width: u32, + height: u32, + pixel_format: VideoFormat, + modifier: u64, + pool: Arc, + staging_free_rx: Receiver, + staging_free_tx: SyncSender, + staging_filled_tx: SyncSender, + frames_dropped_convert_queue_full: Arc, +} + +struct StagingFrame { + bytes: Box<[u8]>, + copied_len: usize, + width: u32, + height: u32, + pixel_format: VideoFormat, + timestamp_us: i64, +} + +impl StagingFrame { + fn new(bytes_capacity: usize) -> Self { + assert!(bytes_capacity > 0); + assert!(bytes_capacity <= LINUX_FRAME_BYTES_MAX); + Self { + bytes: vec![0u8; bytes_capacity].into_boxed_slice(), + copied_len: 0, + width: 0, + height: 0, + pixel_format: VideoFormat::Unknown, + timestamp_us: 0, + } + } +} + +struct StagingRtEndpoints { + free_rx: Receiver, + free_tx: SyncSender, + filled_tx: SyncSender, +} + +struct StagingWorkerEndpoints { + filled_rx: Receiver, + free_tx: SyncSender, +} + +fn staging_bytes_for_pool(pool: &LinuxFrameBufferPool) -> usize { + let nv12_bytes = pool.bytes_per_buffer(); + assert!(nv12_bytes > 0); + let bgra_bytes = nv12_bytes + .div_ceil(3) + .saturating_mul(8) + .min(LINUX_FRAME_BYTES_MAX); + assert!(bgra_bytes >= nv12_bytes); + assert!(bgra_bytes <= LINUX_FRAME_BYTES_MAX); + bgra_bytes +} + +fn build_staging_channels( + pool: &LinuxFrameBufferPool, +) -> (StagingRtEndpoints, StagingWorkerEndpoints) { + let staging_bytes = staging_bytes_for_pool(pool); + assert!(staging_bytes > 0); + let (free_tx, free_rx) = sync_channel::(STAGING_SLOT_COUNT); + let (filled_tx, filled_rx) = sync_channel::(STAGING_SLOT_COUNT); + for _ in 0..STAGING_SLOT_COUNT { + let sent = free_tx.try_send(StagingFrame::new(staging_bytes)); + assert!(sent.is_ok()); + } + ( + StagingRtEndpoints { + free_rx, + free_tx: free_tx.clone(), + filled_tx, + }, + StagingWorkerEndpoints { filled_rx, free_tx }, + ) +} + +fn spawn_pw_thread( + portal_fd: OwnedFd, + node_id: u32, + rx: PwReceiver, + ready_tx: std::sync::mpsc::SyncSender, + running: Arc, + on_frame: FrameCallback, + on_lifecycle: LifecycleCallback, + pool: Arc, + staging_rt: StagingRtEndpoints, + frames_dropped_convert_queue_full: Arc, +) -> std::io::Result> { + assert!(pool.capacity() == LINUX_SCREEN_FRAME_POOL_CAP); + assert!(pool.bytes_per_buffer() > 0); + thread::Builder::new() + .name("fluxer-linux-screen-capture-pw".to_string()) + .spawn(move || { + run_worker( + portal_fd, + node_id, + rx, + ready_tx, + running, + on_frame, + on_lifecycle, + pool, + staging_rt, + frames_dropped_convert_queue_full, + ); + }) +} + +fn spawn_convert_worker( + endpoints: StagingWorkerEndpoints, + pool: Arc, + on_frame: FrameCallback, + on_pool_exhausted: Option, +) -> std::io::Result> { + assert!(pool.capacity() == LINUX_SCREEN_FRAME_POOL_CAP); + assert!(pool.bytes_per_buffer() > 0); + thread::Builder::new() + .name("fluxer-linux-screen-capture-convert".to_string()) + .spawn(move || { + run_convert_worker(endpoints, pool, on_frame, on_pool_exhausted); + }) +} + +fn run_convert_worker( + endpoints: StagingWorkerEndpoints, + pool: Arc, + on_frame: FrameCallback, + on_pool_exhausted: Option, +) { + assert!(pool.capacity() == LINUX_SCREEN_FRAME_POOL_CAP); + assert!(pool.bytes_per_buffer() > 0); + while let Ok(mut staging) = endpoints.filled_rx.recv() { + convert_staged_frame(&staging, &pool, &on_frame, on_pool_exhausted.as_ref()); + staging.copied_len = 0; + match endpoints.free_tx.try_send(staging) { + Ok(()) => {} + Err(TrySendError::Disconnected(_)) => return, + Err(TrySendError::Full(_)) => { + unreachable!("staging slots are conserved; free queue can never overflow") + } + } + } +} + +fn convert_staged_frame( + staging: &StagingFrame, + pool: &Arc, + on_frame: &FrameCallback, + on_pool_exhausted: Option<&PoolExhaustionCallback>, +) { + assert!(staging.copied_len > 0); + assert!(staging.copied_len <= staging.bytes.len()); + let layout = Nv12Layout { + width: staging.width, + height: staging.height, + stride_y: staging.width, + stride_uv: staging.width, + }; + let Some(total_bytes) = layout.packed_size() else { + return; + }; + if total_bytes > pool.bytes_per_buffer() { + pool.note_frame_dropped_oversized(); + return; + } + let Some(mut pooled) = acquire_or_drop(pool, on_pool_exhausted) else { + return; + }; + let ok = fill_pool_buffer_from_raw( + &mut pooled, + &staging.bytes[..staging.copied_len], + layout, + 0, + staging.pixel_format, + total_bytes, + ); + if !ok { + return; + } + let video_frame = VideoFrame { + width: layout.width, + height: layout.height, + stride_y: layout.packed_stride_y(), + stride_uv: layout.packed_stride_uv(), + timestamp_us: staging.timestamp_us, + data: VideoFrameData::Pooled(pooled), + dmabuf: None, + }; + on_frame(video_frame); +} + +fn monotonic_us() -> i64 { + let us = monotonic_ns() / 1_000; + assert!(us <= i64::MAX as u64); + us as i64 +} + +fn monotonic_ns() -> u64 { + let mut ts: libc::timespec = unsafe { std::mem::zeroed() }; + let rc = unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC, &mut ts) }; + assert!(rc == 0); + assert!(ts.tv_sec >= 0); + assert!(ts.tv_nsec >= 0); + (ts.tv_sec as u64) + .saturating_mul(1_000_000_000) + .saturating_add(ts.tv_nsec as u64) +} + +fn run_worker( + portal_fd: OwnedFd, + node_id: u32, + rx: PwReceiver, + ready_tx: std::sync::mpsc::SyncSender, + running: Arc, + on_frame: FrameCallback, + on_lifecycle: LifecycleCallback, + pool: Arc, + staging_rt: StagingRtEndpoints, + frames_dropped_convert_queue_full: Arc, +) { + assert!(pool.capacity() == LINUX_SCREEN_FRAME_POOL_CAP); + assert!(pool.bytes_per_buffer() > 0); + pw::init(); + let Ok(mainloop) = MainLoopRc::new(None) else { + let _ = ready_tx.send(false); + return; + }; + let Ok(context) = ContextRc::new(&mainloop, None) else { + let _ = ready_tx.send(false); + return; + }; + let Ok(core) = context.connect_fd_rc(portal_fd, None) else { + let _ = ready_tx.send(false); + return; + }; + + let stream_props = properties! { + *keys::MEDIA_TYPE => "Video", + *keys::MEDIA_CATEGORY => "Capture", + *keys::MEDIA_ROLE => "Screen", + *keys::NODE_NAME => "fluxer-screen-capture", + }; + + let Ok(stream) = pw::stream::StreamRc::new(core.clone(), "fluxer-screen-capture", stream_props) + else { + let _ = ready_tx.send(false); + return; + }; + + let user_data = UserData { + on_frame: on_frame.clone(), + width: 0, + height: 0, + pixel_format: VideoFormat::Unknown, + modifier: 0, + pool: Arc::clone(&pool), + staging_free_rx: staging_rt.free_rx, + staging_free_tx: staging_rt.free_tx, + staging_filled_tx: staging_rt.filled_tx, + frames_dropped_convert_queue_full, + }; + + let on_lifecycle_for_state = on_lifecycle.clone(); + let capture_fsm_for_state: Arc> = + Arc::new(Mutex::new(LinuxCaptureStateMachine::new(monotonic_ns()))); + + let listener = stream + .add_local_listener_with_user_data(user_data) + .state_changed(move |_, _, old, new| { + dispatch_state_change_to_fsm(&capture_fsm_for_state, &old, &new); + match new { + pw::stream::StreamState::Error(msg) => { + on_lifecycle_for_state("error", msg.as_str()); + } + pw::stream::StreamState::Unconnected + if !matches!(old, pw::stream::StreamState::Connecting) => + { + on_lifecycle_for_state("closed-clean", ""); + } + _ => {} + } + }) + .param_changed(move |stream, user_data, id, param| { + let Some(param) = param else { return }; + if id != spa::param::ParamType::Format.as_raw() { + return; + } + let Ok((media_type, media_subtype)) = format_utils::parse_format(param) else { + return; + }; + if media_type != MediaType::Video || media_subtype != MediaSubtype::Raw { + return; + } + if let Some(chosen) = fixate_modifier_if_unfixated(stream, param) { + user_data.modifier = chosen; + return; + } + let mut info = VideoInfoRaw::new(); + if info.parse(param).is_err() { + return; + } + let size = info.size(); + user_data.width = size.width; + user_data.height = size.height; + user_data.pixel_format = info.format(); + user_data.modifier = info.modifier(); + }) + .process(move |stream, user_data| { + process_pipewire_buffer(stream, user_data); + }) + .register(); + let Ok(listener) = listener else { + let _ = ready_tx.send(false); + return; + }; + + let mut params = match build_format_params() { + Some(p) => p, + None => { + let _ = ready_tx.send(false); + return; + } + }; + let param_refs: Vec<&Pod> = params.iter_mut().map(|p| p.as_ref()).collect(); + let mut param_refs = param_refs; + if stream + .connect( + spa::utils::Direction::Input, + Some(node_id), + pw::stream::StreamFlags::AUTOCONNECT + | pw::stream::StreamFlags::MAP_BUFFERS + | pw::stream::StreamFlags::RT_PROCESS, + &mut param_refs, + ) + .is_err() + { + let _ = ready_tx.send(false); + return; + } + + let mainloop_weak = mainloop.downgrade(); + let _attached = rx.attach(mainloop.loop_(), move |cmd| match cmd { + Command::Stop => { + if let Some(ml) = mainloop_weak.upgrade() { + ml.quit(); + } + } + }); + + let _ = ready_tx.send(true); + mainloop.run(); + drop(listener); + let _ = stream.disconnect(); + running.store(false, Ordering::Release); +} + +fn dispatch_state_change_to_fsm( + fsm: &Arc>, + old: &pw::stream::StreamState, + new: &pw::stream::StreamState, +) { + let Some(event) = stream_state_to_fsm_event(old, new) else { + return; + }; + let Ok(mut guard) = fsm.lock() else { + return; + }; + let _ = guard.dispatch(event, monotonic_ns()); +} + +fn stream_state_to_fsm_event( + old: &pw::stream::StreamState, + new: &pw::stream::StreamState, +) -> Option { + match new { + pw::stream::StreamState::Streaming => Some(LinuxCaptureEvent::Connected), + pw::stream::StreamState::Error(msg) => Some(LinuxCaptureEvent::Faulted( + LinuxCaptureFault::StreamError(stream_error_code(msg)), + )), + pw::stream::StreamState::Unconnected => match old { + pw::stream::StreamState::Streaming | pw::stream::StreamState::Paused => { + Some(LinuxCaptureEvent::Faulted(LinuxCaptureFault::NodeRemoved)) + } + _ => None, + }, + pw::stream::StreamState::Connecting => None, + pw::stream::StreamState::Paused => None, + } +} + +fn stream_error_code(msg: &str) -> i32 { + let trimmed = msg.trim(); + if trimmed.is_empty() { + return -1; + } + let mut acc: i32 = 0; + for byte in trimmed.as_bytes().iter().take(8) { + acc = acc.wrapping_mul(31).wrapping_add(*byte as i32); + } + if acc == 0 { -1 } else { acc } +} + +struct OwnedPodBytes(Vec); + +impl OwnedPodBytes { + fn as_ref(&self) -> &Pod { + Pod::from_bytes(&self.0).expect("serialized pod is valid") + } +} + +fn build_format_params() -> Option> { + let formats = [VideoFormat::NV12, VideoFormat::BGRA, VideoFormat::BGRx]; + let advertise_modifiers = dmabuf_modifiers_enabled(); + let mut params: Vec = Vec::with_capacity(formats.len() * 2); + for format in formats { + if advertise_modifiers { + let pod = serialize_video_format_pod(format, Some(&DMABUF_MODIFIERS_BASELINE))?; + params.push(OwnedPodBytes(pod)); + } + let pod = serialize_video_format_pod(format, None)?; + params.push(OwnedPodBytes(pod)); + } + assert!(params.len() >= formats.len()); + assert!(params.len() <= formats.len() * 2); + Some(params) +} + +fn dmabuf_modifiers_enabled() -> bool { + match env::var(SCREEN_CAPTURE_DMABUF_ENV) { + Ok(value) => !dmabuf_env_value_disables(&value), + Err(_) => true, + } +} + +fn dmabuf_env_value_disables(value: &str) -> bool { + let normalized = value.trim().to_ascii_lowercase(); + matches!( + normalized.as_str(), + "off" | "0" | "false" | "no" | "disabled" + ) +} + +fn modifier_choice_property(modifiers: &[u64]) -> Property { + assert!(!modifiers.is_empty()); + assert!(modifiers.len() <= MODIFIER_COUNT_MAX); + let alternatives: Vec = modifiers.iter().map(|modifier| *modifier as i64).collect(); + Property { + key: spa::sys::SPA_FORMAT_VIDEO_modifier, + flags: PropertyFlags::MANDATORY | PropertyFlags::DONT_FIXATE, + value: SpaValue::Choice(ChoiceValue::Long(Choice( + ChoiceFlags::empty(), + ChoiceEnum::Enum { + default: alternatives[0], + alternatives, + }, + ))), + } +} + +fn serialize_video_format_pod(format: VideoFormat, modifiers: Option<&[u64]>) -> Option> { + let mut properties = vec![ + Property { + key: spa::sys::SPA_FORMAT_mediaType, + flags: PropertyFlags::empty(), + value: SpaValue::Id(spa::utils::Id(spa::sys::SPA_MEDIA_TYPE_video)), + }, + Property { + key: spa::sys::SPA_FORMAT_mediaSubtype, + flags: PropertyFlags::empty(), + value: SpaValue::Id(spa::utils::Id(spa::sys::SPA_MEDIA_SUBTYPE_raw)), + }, + Property { + key: spa::sys::SPA_FORMAT_VIDEO_format, + flags: PropertyFlags::empty(), + value: SpaValue::Id(spa::utils::Id(format.as_raw())), + }, + ]; + if let Some(modifiers) = modifiers { + assert!(!modifiers.is_empty()); + assert!(modifiers.len() <= MODIFIER_COUNT_MAX); + properties.push(modifier_choice_property(modifiers)); + } + properties.push(Property { + key: spa::sys::SPA_FORMAT_VIDEO_size, + flags: PropertyFlags::empty(), + value: SpaValue::Choice(ChoiceValue::Rectangle(Choice( + ChoiceFlags::empty(), + ChoiceEnum::Range { + default: Rectangle { + width: 1920, + height: 1080, + }, + min: Rectangle { + width: 16, + height: 16, + }, + max: Rectangle { + width: 8192, + height: 8192, + }, + }, + ))), + }); + properties.push(Property { + key: spa::sys::SPA_FORMAT_VIDEO_framerate, + flags: PropertyFlags::empty(), + value: SpaValue::Fraction(Fraction { num: 0, denom: 1 }), + }); + properties.push(Property { + key: spa::sys::SPA_FORMAT_VIDEO_maxFramerate, + flags: PropertyFlags::empty(), + value: SpaValue::Choice(ChoiceValue::Fraction(Choice( + ChoiceFlags::empty(), + ChoiceEnum::Range { + default: Fraction { num: 30, denom: 1 }, + min: Fraction { num: 1, denom: 1 }, + max: Fraction { num: 240, denom: 1 }, + }, + ))), + }); + serialize_format_object(properties) +} + +fn serialize_format_object(properties: Vec) -> Option> { + assert!(properties.len() >= 3); + let obj = Object { + type_: SpaTypes::ObjectParamFormat.as_raw(), + id: spa::param::ParamType::EnumFormat.as_raw(), + properties, + }; + let bytes = spa::pod::serialize::PodSerializer::serialize( + std::io::Cursor::new(Vec::new()), + &SpaValue::Object(obj), + ) + .ok()? + .0 + .into_inner(); + assert!(!bytes.is_empty()); + Some(bytes) +} + +fn fixate_modifier_if_unfixated(stream: &pw::stream::Stream, param: &Pod) -> Option { + let (chosen, fixated_bytes) = fixated_format_pod_bytes(param)?; + let pod = Pod::from_bytes(&fixated_bytes)?; + let mut params = [pod]; + if stream.update_params(&mut params).is_err() { + return None; + } + Some(chosen) +} + +fn fixated_format_pod_bytes(param: &Pod) -> Option<(u64, Vec)> { + let (_, value) = + spa::pod::deserialize::PodDeserializer::deserialize_any_from(param.as_bytes()).ok()?; + let SpaValue::Object(mut obj) = value else { + return None; + }; + let mut chosen: Option = None; + for prop in obj.properties.iter_mut() { + if prop.key != spa::sys::SPA_FORMAT_VIDEO_modifier { + continue; + } + let SpaValue::Choice(ChoiceValue::Long(choice)) = &prop.value else { + continue; + }; + let modifier = choose_dmabuf_modifier(choice); + prop.flags = PropertyFlags::MANDATORY; + prop.value = SpaValue::Long(modifier as i64); + chosen = Some(modifier); + } + let chosen = chosen?; + let bytes = spa::pod::serialize::PodSerializer::serialize( + std::io::Cursor::new(Vec::new()), + &SpaValue::Object(obj), + ) + .ok()? + .0 + .into_inner(); + assert!(!bytes.is_empty()); + Some((chosen, bytes)) +} + +fn choose_dmabuf_modifier(choice: &Choice) -> u64 { + let candidates: Vec = match &choice.1 { + ChoiceEnum::Enum { + default, + alternatives, + } => { + let mut all = Vec::with_capacity(MODIFIER_COUNT_MAX + 1); + all.push(*default); + for alternative in alternatives.iter().take(MODIFIER_COUNT_MAX) { + all.push(*alternative); + } + all + } + ChoiceEnum::None(value) => vec![*value], + ChoiceEnum::Range { default, .. } => vec![*default], + ChoiceEnum::Step { default, .. } => vec![*default], + ChoiceEnum::Flags { default, .. } => vec![*default], + }; + assert!(!candidates.is_empty()); + assert!(candidates.len() <= MODIFIER_COUNT_MAX + 1); + if candidates.contains(&(DRM_FORMAT_MOD_LINEAR as i64)) { + return DRM_FORMAT_MOD_LINEAR; + } + if candidates.contains(&(DRM_FORMAT_MOD_INVALID as i64)) { + return DRM_FORMAT_MOD_INVALID; + } + candidates[0] as u64 +} + +pub fn daemon_reachable() -> bool { + pw::init(); + let Ok(mainloop) = MainLoopRc::new(None) else { + return false; + }; + let Ok(context) = ContextRc::new(&mainloop, None) else { + return false; + }; + context.connect_rc(None).is_ok() +} + +fn dmabuf_frame_from_datas( + datas: &[Data], + width: u32, + height: u32, + pixel_format: VideoFormat, + modifier: u64, + timestamp_us: i64, +) -> Option { + if width < 2 || height < 2 || width % 2 != 0 || height % 2 != 0 { + return None; + } + let drm_format = drm_format_for_video_format(pixel_format)?; + let mut plane_count = 0usize; + let mut fds = [-1; 4]; + let mut strides = [0u32; 4]; + let mut offsets = [0u32; 4]; + for (idx, data) in datas.iter().enumerate().take(4) { + if data.type_() != DataType::DmaBuf { + break; + } + let fd = data.fd(); + if fd < 0 { + return None; + } + let chunk = data.chunk(); + let stride = if chunk.stride() > 0 { + chunk.stride() as u32 + } else { + default_plane_stride(pixel_format, idx, width)? + }; + fds[idx] = fd; + strides[idx] = stride; + offsets[idx] = chunk.offset(); + plane_count += 1; + } + if plane_count == 0 { + return None; + } + Some(VideoFrame { + width, + height, + stride_y: strides[0], + stride_uv: if plane_count > 1 { + strides[1] + } else { + strides[0] + }, + timestamp_us, + data: VideoFrameData::Empty, + dmabuf: Some(DmabufFrameMetadata { + fds, + plane_count: plane_count as u32, + drm_format, + modifier, + strides, + offsets, + device_uuid: None, + }), + }) +} + +fn drm_format_for_video_format(format: VideoFormat) -> Option { + match format { + VideoFormat::NV12 => Some(DRM_FORMAT_NV12), + VideoFormat::BGRA => Some(DRM_FORMAT_ARGB8888), + VideoFormat::BGRx => Some(DRM_FORMAT_XRGB8888), + _ => None, + } +} + +fn default_plane_stride(format: VideoFormat, plane: usize, width: u32) -> Option { + match format { + VideoFormat::NV12 => Some(width), + VideoFormat::BGRA | VideoFormat::BGRx if plane == 0 => width.checked_mul(4), + _ => None, + } +} + +fn process_pipewire_buffer(stream: &pw::stream::Stream, user_data: &mut UserData) { + assert!(user_data.pool.capacity() == LINUX_SCREEN_FRAME_POOL_CAP); + let Some(mut buffer) = stream.dequeue_buffer() else { + return; + }; + let datas = buffer.datas_mut(); + if datas.is_empty() { + return; + } + if let Some(video_frame) = dmabuf_frame_from_datas( + datas, + user_data.width, + user_data.height, + user_data.pixel_format, + user_data.modifier, + monotonic_us(), + ) { + (user_data.on_frame)(video_frame); + return; + } + process_cpu_buffer(datas, user_data); +} + +fn process_cpu_buffer(datas: &mut [Data], user_data: &mut UserData) { + assert!(user_data.pool.capacity() == LINUX_SCREEN_FRAME_POOL_CAP); + let data = &mut datas[0]; + let chunk = data.chunk(); + let chunk_size = chunk.size() as usize; + let chunk_stride = chunk.stride() as usize; + if chunk_size == 0 || user_data.width == 0 || user_data.height == 0 { + return; + } + if user_data.height % 2 != 0 { + return; + } + let Some(raw) = data.data() else { return }; + let layout = Nv12Layout { + width: user_data.width, + height: user_data.height, + stride_y: user_data.width, + stride_uv: user_data.width, + }; + let Some(total_bytes) = layout.packed_size() else { + return; + }; + if total_bytes > user_data.pool.bytes_per_buffer() { + user_data.pool.note_frame_dropped_oversized(); + return; + } + let timestamp_us = monotonic_us(); + let Ok(mut staging) = user_data.staging_free_rx.try_recv() else { + note_convert_queue_drop(user_data); + return; + }; + let staged = stage_raw_frame( + &mut staging, + raw, + layout, + chunk_stride, + user_data.pixel_format, + ); + if !staged { + return_staging_slot(user_data, staging); + return; + } + staging.width = layout.width; + staging.height = layout.height; + staging.pixel_format = user_data.pixel_format; + staging.timestamp_us = timestamp_us; + submit_staged_frame(user_data, staging); +} + +fn submit_staged_frame(user_data: &UserData, staging: StagingFrame) { + assert!(staging.copied_len > 0); + assert!(staging.copied_len <= staging.bytes.len()); + match user_data.staging_filled_tx.try_send(staging) { + Ok(()) => {} + Err(TrySendError::Full(frame)) => { + return_staging_slot(user_data, frame); + note_convert_queue_drop(user_data); + } + Err(TrySendError::Disconnected(frame)) => { + return_staging_slot(user_data, frame); + note_convert_queue_drop(user_data); + } + } +} + +fn return_staging_slot(user_data: &UserData, staging: StagingFrame) { + let returned = user_data.staging_free_tx.try_send(staging); + assert!(returned.is_ok()); +} + +fn note_convert_queue_drop(user_data: &UserData) { + let before = user_data + .frames_dropped_convert_queue_full + .fetch_add(1, Ordering::Relaxed); + assert!(before < u64::MAX / 2); +} + +fn stage_raw_frame( + staging: &mut StagingFrame, + raw: &[u8], + layout: Nv12Layout, + chunk_stride: usize, + pixel_format: VideoFormat, +) -> bool { + assert!(layout.width > 0); + assert!(layout.height % 2 == 0); + match pixel_format { + VideoFormat::NV12 => stage_nv12_rows(staging, raw, layout, chunk_stride), + VideoFormat::BGRA | VideoFormat::BGRx => { + stage_bgra_rows(staging, raw, layout, chunk_stride) + } + _ => false, + } +} + +fn stage_bgra_rows( + staging: &mut StagingFrame, + raw: &[u8], + layout: Nv12Layout, + chunk_stride: usize, +) -> bool { + let w = layout.width as usize; + let h = layout.height as usize; + let Some(row_bytes) = w.checked_mul(4) else { + return false; + }; + let src_stride = if chunk_stride == 0 { + row_bytes + } else { + chunk_stride + }; + if src_stride < row_bytes { + return false; + } + let Some(src_needed) = src_stride.checked_mul(h) else { + return false; + }; + if raw.len() < src_needed { + return false; + } + let Some(copied) = row_bytes.checked_mul(h) else { + return false; + }; + if copied > staging.bytes.len() { + return false; + } + if src_stride == row_bytes { + staging.bytes[..copied].copy_from_slice(&raw[..copied]); + } else { + for row in 0..h { + let src_offset = row * src_stride; + let dst_offset = row * row_bytes; + staging.bytes[dst_offset..dst_offset + row_bytes] + .copy_from_slice(&raw[src_offset..src_offset + row_bytes]); + } + } + staging.copied_len = copied; + true +} + +fn stage_nv12_rows( + staging: &mut StagingFrame, + raw: &[u8], + layout: Nv12Layout, + chunk_stride: usize, +) -> bool { + let h = layout.height as usize; + let stride = if chunk_stride == 0 { + layout.width as usize + } else { + chunk_stride + }; + let Some(y_bytes) = stride.checked_mul(h) else { + return false; + }; + let Some(uv_bytes) = stride.checked_mul(h / 2) else { + return false; + }; + if raw.len() < y_bytes + uv_bytes { + return false; + } + let Some(total) = layout.packed_size() else { + return false; + }; + if total > staging.bytes.len() { + return false; + } + let strided = Nv12Layout { + width: layout.width, + height: layout.height, + stride_y: stride as u32, + stride_uv: stride as u32, + }; + let ok = pack_nv12( + strided, + &raw[..y_bytes], + &raw[y_bytes..y_bytes + uv_bytes], + &mut staging.bytes[..total], + ); + if !ok { + return false; + } + staging.copied_len = total; + true +} + +fn acquire_or_drop( + pool: &Arc, + on_pool_exhausted: Option<&PoolExhaustionCallback>, +) -> Option { + assert!(pool.capacity() == LINUX_SCREEN_FRAME_POOL_CAP); + match pool.try_acquire() { + Some(pooled) => Some(pooled), + None => { + let dropped = pool.frames_dropped_pool_exhausted(); + if let Some(cb) = on_pool_exhausted { + cb(dropped); + } + None + } + } +} + +fn fill_pool_buffer_from_raw( + pooled: &mut PooledFrameBuffer, + raw: &[u8], + layout: Nv12Layout, + chunk_stride: usize, + pixel_format: VideoFormat, + total_bytes: usize, +) -> bool { + assert!(total_bytes <= pooled.buffer_mut().len()); + let dst = &mut pooled.buffer_mut()[..total_bytes]; + let ok = match pixel_format { + VideoFormat::NV12 => fill_nv12_passthrough(dst, raw, layout, chunk_stride), + VideoFormat::BGRA | VideoFormat::BGRx => fill_bgra_to_nv12(dst, raw, layout, chunk_stride), + _ => false, + }; + if !ok { + return false; + } + pooled.set_len(total_bytes); + true +} + +fn fill_nv12_passthrough( + dst: &mut [u8], + raw: &[u8], + layout: Nv12Layout, + chunk_stride: usize, +) -> bool { + let stride_y = if chunk_stride == 0 { + layout.width as usize + } else { + chunk_stride + }; + let stride_uv = stride_y; + let y_bytes = stride_y * layout.height as usize; + let uv_bytes = stride_uv * (layout.height as usize / 2); + if raw.len() < y_bytes + uv_bytes { + return false; + } + let strided_layout = Nv12Layout { + width: layout.width, + height: layout.height, + stride_y: stride_y as u32, + stride_uv: stride_uv as u32, + }; + pack_nv12( + strided_layout, + &raw[..y_bytes], + &raw[y_bytes..y_bytes + uv_bytes], + dst, + ) +} + +fn fill_bgra_to_nv12(dst: &mut [u8], raw: &[u8], layout: Nv12Layout, chunk_stride: usize) -> bool { + let stride = if chunk_stride == 0 { + layout.width as usize * 4 + } else { + chunk_stride + }; + let needed = stride * layout.height as usize; + if raw.len() < needed { + return false; + } + bgra_to_nv12(layout, &raw[..needed], stride as u32, dst, false) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn deserialize_format_object(bytes: &[u8]) -> Object { + let (_, value) = spa::pod::deserialize::PodDeserializer::deserialize_any_from(bytes) + .expect("serialized pod deserializes"); + let SpaValue::Object(obj) = value else { + panic!("format pod must be an object"); + }; + obj + } + + fn modifier_property_of(obj: &Object) -> Option<&Property> { + obj.properties + .iter() + .find(|prop| prop.key == spa::sys::SPA_FORMAT_VIDEO_modifier) + } + + #[test] + fn format_pod_without_modifiers_matches_legacy_shape() { + let bytes = + serialize_video_format_pod(VideoFormat::BGRA, None).expect("legacy pod serializes"); + let obj = deserialize_format_object(&bytes); + assert!(modifier_property_of(&obj).is_none()); + assert_eq!(obj.properties.len(), 6); + } + + #[test] + fn format_pod_with_modifiers_carries_mandatory_dont_fixate_choice() { + let bytes = serialize_video_format_pod(VideoFormat::BGRA, Some(&DMABUF_MODIFIERS_BASELINE)) + .expect("modifier pod serializes"); + let obj = deserialize_format_object(&bytes); + let prop = modifier_property_of(&obj).expect("modifier property present"); + assert!(prop.flags.contains(PropertyFlags::MANDATORY)); + assert!(prop.flags.contains(PropertyFlags::DONT_FIXATE)); + let SpaValue::Choice(ChoiceValue::Long(choice)) = &prop.value else { + panic!("modifier must be a long choice"); + }; + let ChoiceEnum::Enum { + default, + alternatives, + } = &choice.1 + else { + panic!("modifier choice must be an enum"); + }; + assert_eq!(*default, DRM_FORMAT_MOD_LINEAR as i64); + assert!(alternatives.contains(&(DRM_FORMAT_MOD_LINEAR as i64))); + assert!(alternatives.contains(&(DRM_FORMAT_MOD_INVALID as i64))); + } + + #[test] + fn build_format_params_always_keeps_modifier_less_fallback_pods() { + let params = build_format_params().expect("params build"); + let mut plain_pods = 0usize; + for param in ¶ms { + let obj = deserialize_format_object(¶m.0); + if modifier_property_of(&obj).is_none() { + plain_pods += 1; + } + } + assert_eq!(plain_pods, 3); + assert!(params.len() == 3 || params.len() == 6); + } + + #[test] + fn fixation_picks_linear_and_strips_the_choice() { + let bytes = serialize_video_format_pod(VideoFormat::BGRA, Some(&DMABUF_MODIFIERS_BASELINE)) + .expect("modifier pod serializes"); + let pod = Pod::from_bytes(&bytes).expect("pod parses"); + let (chosen, fixated) = fixated_format_pod_bytes(pod).expect("fixation happens"); + assert_eq!(chosen, DRM_FORMAT_MOD_LINEAR); + let obj = deserialize_format_object(&fixated); + let prop = modifier_property_of(&obj).expect("modifier property kept"); + assert!(prop.flags.contains(PropertyFlags::MANDATORY)); + assert!(!prop.flags.contains(PropertyFlags::DONT_FIXATE)); + assert_eq!(prop.value, SpaValue::Long(DRM_FORMAT_MOD_LINEAR as i64)); + } + + #[test] + fn fixation_skips_already_fixated_pods() { + let bytes = + serialize_video_format_pod(VideoFormat::BGRA, None).expect("legacy pod serializes"); + let pod = Pod::from_bytes(&bytes).expect("pod parses"); + assert!(fixated_format_pod_bytes(pod).is_none()); + } + + #[test] + fn modifier_choice_prefers_linear_then_invalid_then_first() { + let linear = Choice( + ChoiceFlags::empty(), + ChoiceEnum::Enum { + default: DRM_FORMAT_MOD_INVALID as i64, + alternatives: vec![DRM_FORMAT_MOD_INVALID as i64, DRM_FORMAT_MOD_LINEAR as i64], + }, + ); + assert_eq!(choose_dmabuf_modifier(&linear), DRM_FORMAT_MOD_LINEAR); + + let invalid_only = Choice( + ChoiceFlags::empty(), + ChoiceEnum::Enum { + default: DRM_FORMAT_MOD_INVALID as i64, + alternatives: vec![DRM_FORMAT_MOD_INVALID as i64], + }, + ); + assert_eq!( + choose_dmabuf_modifier(&invalid_only), + DRM_FORMAT_MOD_INVALID + ); + + let vendor = Choice( + ChoiceFlags::empty(), + ChoiceEnum::Enum { + default: 0x0100_0000_0000_0001, + alternatives: vec![0x0100_0000_0000_0001, 0x0100_0000_0000_0002], + }, + ); + assert_eq!(choose_dmabuf_modifier(&vendor), 0x0100_0000_0000_0001); + } + + #[test] + fn dmabuf_env_values_gate_modifier_advertisement() { + assert!(dmabuf_env_value_disables("off")); + assert!(dmabuf_env_value_disables(" OFF ")); + assert!(dmabuf_env_value_disables("0")); + assert!(dmabuf_env_value_disables("false")); + assert!(dmabuf_env_value_disables("no")); + assert!(dmabuf_env_value_disables("disabled")); + assert!(!dmabuf_env_value_disables("")); + assert!(!dmabuf_env_value_disables("on")); + assert!(!dmabuf_env_value_disables("auto")); + } + + #[test] + fn staging_channels_hold_exactly_two_preallocated_slots() { + let pool = LinuxFrameBufferPool::new(64 * 64 * 3 / 2).expect("pool init"); + let (rt, _worker) = build_staging_channels(&pool); + let first = rt.free_rx.try_recv().expect("first staging slot"); + let second = rt.free_rx.try_recv().expect("second staging slot"); + assert!(rt.free_rx.try_recv().is_err()); + assert_eq!(first.bytes.len(), 64 * 64 * 4); + assert_eq!(second.bytes.len(), 64 * 64 * 4); + assert_eq!(first.copied_len, 0); + assert_eq!(second.copied_len, 0); + } + + #[test] + fn stage_bgra_rows_strips_stride_padding() { + let layout = Nv12Layout { + width: 4, + height: 2, + stride_y: 4, + stride_uv: 4, + }; + let stride = 24usize; + let mut raw = vec![0u8; stride * 2]; + for row in 0..2usize { + for byte in 0..16usize { + raw[row * stride + byte] = (row * 16 + byte) as u8; + } + } + let mut staging = StagingFrame::new(4 * 2 * 4); + assert!(stage_bgra_rows(&mut staging, &raw, layout, stride)); + assert_eq!(staging.copied_len, 32); + for row in 0..2usize { + for byte in 0..16usize { + assert_eq!(staging.bytes[row * 16 + byte], (row * 16 + byte) as u8); + } + } + } + + #[test] + fn stage_bgra_rows_rejects_short_source() { + let layout = Nv12Layout { + width: 4, + height: 2, + stride_y: 4, + stride_uv: 4, + }; + let raw = vec![0u8; 8]; + let mut staging = StagingFrame::new(4 * 2 * 4); + assert!(!stage_bgra_rows(&mut staging, &raw, layout, 16)); + assert_eq!(staging.copied_len, 0); + } + + #[test] + fn staged_bgra_conversion_matches_direct_conversion() { + let layout = Nv12Layout { + width: 16, + height: 4, + stride_y: 16, + stride_uv: 16, + }; + let stride = 80usize; + let mut raw = vec![0u8; stride * 4]; + for (index, byte) in raw.iter_mut().enumerate() { + *byte = (index % 251) as u8; + } + + let mut staging = StagingFrame::new(16 * 4 * 4); + assert!(stage_raw_frame( + &mut staging, + &raw, + layout, + stride, + VideoFormat::BGRA + )); + staging.width = layout.width; + staging.height = layout.height; + staging.pixel_format = VideoFormat::BGRA; + staging.timestamp_us = 1_234; + + type ObservedFrame = Arc, i64)>>>; + let pool = LinuxFrameBufferPool::new(layout.packed_size().expect("layout")).expect("pool"); + let observed: ObservedFrame = Arc::new(Mutex::new(None)); + let observed_cb = Arc::clone(&observed); + let on_frame: FrameCallback = Arc::new(move |frame| { + let mut guard = observed_cb.lock().expect("observed lock"); + *guard = Some((frame.data.to_vec(), frame.timestamp_us)); + }); + convert_staged_frame(&staging, &pool, &on_frame, None); + + let total = layout.packed_size().expect("layout"); + let mut expected = vec![0u8; total]; + assert!(fill_bgra_to_nv12(&mut expected, &raw, layout, stride)); + + let guard = observed.lock().expect("observed lock"); + let (frame_bytes, timestamp_us) = guard.as_ref().expect("frame delivered"); + assert_eq!(frame_bytes, &expected); + assert_eq!(*timestamp_us, 1_234); + } + + #[test] + fn convert_worker_returns_slot_after_processing() { + let pool = LinuxFrameBufferPool::new(64 * 64 * 3 / 2).expect("pool init"); + let (rt, worker) = build_staging_channels(&pool); + let on_frame: FrameCallback = Arc::new(|_| {}); + let worker_pool = Arc::clone(&pool); + let handle = thread::Builder::new() + .name("fluxer-test-convert".to_string()) + .spawn(move || { + run_convert_worker(worker, worker_pool, on_frame, None); + }) + .expect("worker spawns"); + + let mut staging = rt.free_rx.recv().expect("slot available"); + let layout = Nv12Layout { + width: 16, + height: 4, + stride_y: 16, + stride_uv: 16, + }; + let raw = vec![0x40u8; 16 * 4 * 4]; + assert!(stage_raw_frame( + &mut staging, + &raw, + layout, + 0, + VideoFormat::BGRx + )); + staging.width = layout.width; + staging.height = layout.height; + staging.pixel_format = VideoFormat::BGRx; + staging.timestamp_us = 7; + rt.filled_tx.send(staging).expect("filled enqueue"); + + let first_free = rt + .free_rx + .recv_timeout(Duration::from_millis(2_000)) + .expect("one free slot available"); + assert_eq!(first_free.copied_len, 0); + let recycled = rt + .free_rx + .recv_timeout(Duration::from_millis(2_000)) + .expect("slot recycled by worker"); + assert_eq!(recycled.copied_len, 0); + + drop(rt); + handle.join().expect("worker exits on disconnect"); + } +} diff --git a/fluxer_desktop/native/linux-screen-capture/src/portal.rs b/fluxer_desktop/native/linux-screen-capture/src/portal.rs new file mode 100644 index 0000000..ab5089b --- /dev/null +++ b/fluxer_desktop/native/linux-screen-capture/src/portal.rs @@ -0,0 +1,664 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::collections::HashMap; +use std::env; +use std::os::fd::OwnedFd; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::{Arc, mpsc}; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +use futures_lite::{FutureExt, StreamExt, future}; +use zbus::names::OwnedUniqueName; +use zbus::{ + MatchRule, MessageStream, + blocking::{Connection as BlockingConnection, Proxy as BlockingProxy}, + message::Type as MessageType, + zvariant::{OwnedObjectPath, OwnedValue, Value}, +}; + +pub const PORTAL_DESTINATION: &str = "org.freedesktop.portal.Desktop"; +pub const PORTAL_PATH: &str = "/org/freedesktop/portal/desktop"; +pub const SCREEN_CAST_INTERFACE: &str = "org.freedesktop.portal.ScreenCast"; +pub const REQUEST_INTERFACE: &str = "org.freedesktop.portal.Request"; +pub const SESSION_INTERFACE: &str = "org.freedesktop.portal.Session"; +pub const PROPERTIES_INTERFACE: &str = "org.freedesktop.DBus.Properties"; +pub const REGISTRY_INTERFACE: &str = "org.freedesktop.host.portal.Registry"; + +pub const REQUEST_TIMEOUT: Duration = Duration::from_secs(5 * 60); +const SIGNAL_POLL_INTERVAL: Duration = Duration::from_millis(200); +const MIN_PORTAL_VERSION: u32 = 4; +const DESKTOP_ENTRY_ID_ENV: &str = "FLUXER_LINUX_DESKTOP_ENTRY_ID"; + +pub const CURSOR_MODE_HIDDEN: u32 = 1; +pub const CURSOR_MODE_EMBEDDED: u32 = 2; +pub const CURSOR_MODE_METADATA: u32 = 4; +pub const SOURCE_TYPE_MONITOR: u32 = 1; +pub const SOURCE_TYPE_WINDOW: u32 = 2; +pub const SOURCE_TYPES_ALL: u32 = SOURCE_TYPE_MONITOR | SOURCE_TYPE_WINDOW; + +static TOKEN_SEQ: AtomicU64 = AtomicU64::new(1); + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum PortalError { + DbusError, + PortalTimeout, + InvalidReply, + SendFailed, + Cancelled, + + CursorModeUnavailable, + PortalTooOld(u32), + NoStreams, +} + +impl std::fmt::Display for PortalError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::DbusError => f.write_str("DbusError"), + Self::PortalTimeout => f.write_str("PortalTimeout"), + Self::InvalidReply => f.write_str("InvalidReply"), + Self::SendFailed => f.write_str("SendFailed"), + Self::Cancelled => f.write_str("Cancelled"), + Self::CursorModeUnavailable => f.write_str("CursorModeUnavailable"), + Self::PortalTooOld(v) => write!(f, "PortalTooOld(version={v})"), + Self::NoStreams => f.write_str("NoStreams"), + } + } +} + +pub fn mint_token(prefix: &str) -> String { + let seq = TOKEN_SEQ.fetch_add(1, Ordering::Relaxed); + let ms = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|d| d.as_millis() as u64) + .unwrap_or(0); + format!("{prefix}_{ms:x}_{seq:x}") +} + +pub fn request_path(unique_bus_name: &str, handle_token: &str) -> String { + let trimmed = unique_bus_name.strip_prefix(':').unwrap_or(unique_bus_name); + let mut out = String::with_capacity(40 + trimmed.len() + handle_token.len()); + out.push_str("/org/freedesktop/portal/desktop/request/"); + for ch in trimmed.chars() { + out.push(if ch == '.' { '_' } else { ch }); + } + out.push('/'); + out.push_str(handle_token); + out +} + +pub fn session_path(unique_bus_name: &str, session_token: &str) -> String { + let trimmed = unique_bus_name.strip_prefix(':').unwrap_or(unique_bus_name); + let mut out = String::with_capacity(40 + trimmed.len() + session_token.len()); + out.push_str("/org/freedesktop/portal/desktop/session/"); + for ch in trimmed.chars() { + out.push(if ch == '.' { '_' } else { ch }); + } + out.push('/'); + out.push_str(session_token); + out +} + +pub fn read_portal_version() -> Result { + let conn = blocking_session_conn()?; + register_portal_app_id(&conn); + let proxy = BlockingProxy::new(&conn, PORTAL_DESTINATION, PORTAL_PATH, PROPERTIES_INTERFACE) + .map_err(|_| PortalError::DbusError)?; + let value: OwnedValue = proxy + .call("Get", &(SCREEN_CAST_INTERFACE, "version")) + .map_err(|_| PortalError::DbusError)?; + let v: &Value<'_> = &value; + match v { + Value::U32(n) => Ok(*n), + Value::Value(inner) => match inner.as_ref() { + Value::U32(n) => Ok(*n), + _ => Err(PortalError::InvalidReply), + }, + _ => Err(PortalError::InvalidReply), + } +} + +pub fn read_available_cursor_modes() -> Result { + let conn = blocking_session_conn()?; + register_portal_app_id(&conn); + let proxy = BlockingProxy::new(&conn, PORTAL_DESTINATION, PORTAL_PATH, PROPERTIES_INTERFACE) + .map_err(|_| PortalError::DbusError)?; + let value: OwnedValue = proxy + .call("Get", &(SCREEN_CAST_INTERFACE, "AvailableCursorModes")) + .map_err(|_| PortalError::DbusError)?; + let v: &Value<'_> = &value; + match v { + Value::U32(n) => Ok(*n), + Value::Value(inner) => match inner.as_ref() { + Value::U32(n) => Ok(*n), + _ => Err(PortalError::InvalidReply), + }, + _ => Err(PortalError::InvalidReply), + } +} + +#[derive(Debug, Clone)] +pub struct StreamInfo { + pub node_id: u32, + pub source_type: u32, + pub mapping_id: Option, + pub width: u32, + pub height: u32, + pub position_x: i32, + pub position_y: i32, +} + +#[derive(Debug, Clone)] +pub struct StartedSession { + pub session_handle: String, + pub streams: Vec, +} + +pub struct LiveSession { + pub handle: String, + pub conn: BlockingConnection, +} + +impl LiveSession { + pub fn close(self) { + let path = OwnedObjectPath::try_from(self.handle.as_str()) + .ok() + .and_then(|p| { + BlockingProxy::new(&self.conn, PORTAL_DESTINATION, p, SESSION_INTERFACE).ok() + }); + if let Some(proxy) = path { + let _ = proxy.call::<_, _, ()>("Close", &()); + } + } +} + +fn blocking_session_conn() -> Result { + zbus::blocking::connection::Builder::session() + .map_err(|_| PortalError::DbusError)? + .method_timeout(Duration::from_secs(30)) + .build() + .map_err(|_| PortalError::DbusError) +} + +fn unique_name(conn: &BlockingConnection) -> Result { + conn.unique_name() + .ok_or(PortalError::DbusError) + .map(|n| n.to_owned()) +} + +fn normalize_desktop_entry_app_id(value: &str) -> Option { + let trimmed = value.trim(); + let app_id = trimmed.strip_suffix(".desktop").unwrap_or(trimmed); + if app_id.is_empty() || app_id.len() > 255 || app_id.starts_with('.') { + return None; + } + let valid = app_id + .bytes() + .all(|b| b.is_ascii_alphanumeric() || matches!(b, b'.' | b'-' | b'_')); + valid.then(|| app_id.to_string()) +} + +fn configured_desktop_entry_app_id() -> Option { + env::var(DESKTOP_ENTRY_ID_ENV) + .ok() + .and_then(|value| normalize_desktop_entry_app_id(&value)) +} + +fn register_portal_app_id(conn: &BlockingConnection) { + let Some(app_id) = configured_desktop_entry_app_id() else { + return; + }; + let Ok(proxy) = BlockingProxy::new(conn, PORTAL_DESTINATION, PORTAL_PATH, REGISTRY_INTERFACE) + else { + return; + }; + let options: HashMap<&str, Value<'_>> = HashMap::new(); + let _ = proxy.call::<_, _, ()>("Register", &(app_id.as_str(), options)); +} + +struct ResponseEnvelope { + code: u32, + results: HashMap, +} + +struct PendingRequest { + rx: mpsc::Receiver, + stop_flag: Arc, + listener: std::thread::JoinHandle<()>, +} + +impl PendingRequest { + fn wait(self) -> Result { + let result = self + .rx + .recv_timeout(REQUEST_TIMEOUT) + .map_err(|_| PortalError::PortalTimeout); + self.stop_flag + .store(true, std::sync::atomic::Ordering::Release); + let _ = self.listener.join(); + result + } +} + +fn watch_request( + conn: &BlockingConnection, + expected_path: &str, +) -> Result { + let (tx, rx) = mpsc::sync_channel::(1); + let (ready_tx, ready_rx) = mpsc::sync_channel::>(1); + let stop_flag = Arc::new(std::sync::atomic::AtomicBool::new(false)); + let stop_for_thread = stop_flag.clone(); + let expected_for_thread = expected_path.to_string(); + let conn_for_thread = conn.clone(); + let listener = std::thread::Builder::new() + .name("fluxer-linux-screen-capture-req".to_string()) + .spawn(move || { + response_listener( + conn_for_thread, + &expected_for_thread, + tx, + ready_tx, + stop_for_thread, + ); + }) + .map_err(|_| PortalError::DbusError)?; + + match ready_rx.recv_timeout(Duration::from_secs(5)) { + Ok(Ok(())) => {} + _ => { + stop_flag.store(true, std::sync::atomic::Ordering::Release); + let _ = listener.join(); + return Err(PortalError::DbusError); + } + } + + Ok(PendingRequest { + rx, + stop_flag, + listener, + }) +} + +fn response_listener( + conn: BlockingConnection, + expected_path: &str, + tx: mpsc::SyncSender, + ready: mpsc::SyncSender>, + stop: Arc, +) { + let setup = future::block_on(async { + let conn: zbus::Connection = conn.into(); + let rule = MatchRule::builder() + .msg_type(MessageType::Signal) + .interface(REQUEST_INTERFACE)? + .member("Response")? + .path(expected_path.to_string())? + .build(); + let stream = MessageStream::for_match_rule(rule, &conn, Some(8)).await?; + zbus::Result::Ok((conn, stream)) + }); + let (_conn, mut stream) = match setup { + Ok(parts) => parts, + Err(_) => { + let _ = ready.send(Err(())); + return; + } + }; + let _ = ready.send(Ok(())); + while !stop.load(std::sync::atomic::Ordering::Acquire) { + let timeout = async { + async_io::Timer::after(SIGNAL_POLL_INTERVAL).await; + None::> + }; + match future::block_on(stream.next().or(timeout)) { + Some(Ok(message)) => { + if let Some(env) = parse_response(&message) { + let _ = tx.send(env); + return; + } + } + Some(Err(_)) => return, + None => {} + } + } +} + +fn parse_response(message: &zbus::Message) -> Option { + let body = message.body(); + let (code, results): (u32, HashMap) = body.deserialize().ok()?; + Some(ResponseEnvelope { code, results }) +} + +fn value_of(v: &OwnedValue) -> &Value<'_> { + use std::ops::Deref as _; + v.deref() +} + +fn unwrap_variant<'a>(v: &'a Value<'a>) -> &'a Value<'a> { + match v { + Value::Value(boxed) => boxed.as_ref(), + other => other, + } +} + +fn read_u32(value: &Value<'_>) -> Option { + match unwrap_variant(value) { + Value::U32(n) => Some(*n), + Value::U64(n) => Some(*n as u32), + Value::I32(n) => Some(*n as u32), + _ => None, + } +} + +fn read_i32(value: &Value<'_>) -> Option { + match unwrap_variant(value) { + Value::I32(n) => Some(*n), + Value::U32(n) => Some(*n as i32), + _ => None, + } +} + +fn read_str(value: &Value<'_>) -> Option { + match unwrap_variant(value) { + Value::Str(s) => Some(s.as_str().to_string()), + _ => None, + } +} + +fn read_pair_i32(value: &Value<'_>) -> Option<(i32, i32)> { + let Value::Structure(s) = unwrap_variant(value) else { + return None; + }; + let fields = s.fields(); + if fields.len() < 2 { + return None; + } + Some((read_i32(&fields[0])?, read_i32(&fields[1])?)) +} + +fn read_pair_u32(value: &Value<'_>) -> Option<(u32, u32)> { + let Value::Structure(s) = unwrap_variant(value) else { + return None; + }; + let fields = s.fields(); + if fields.len() < 2 { + return None; + } + Some((read_u32(&fields[0])?, read_u32(&fields[1])?)) +} + +fn parse_streams(results: &HashMap) -> Vec { + let Some(raw) = results.get("streams") else { + return Vec::new(); + }; + let val = value_of(raw); + let inner = unwrap_variant(val); + let Value::Array(arr) = inner else { + return Vec::new(); + }; + let mut out = Vec::new(); + for entry in arr.iter() { + let entry_inner = unwrap_variant(entry); + let Value::Structure(s) = entry_inner else { + continue; + }; + let fields = s.fields(); + if fields.len() < 2 { + continue; + } + let Some(node_id) = read_u32(&fields[0]) else { + continue; + }; + let Value::Dict(dict) = unwrap_variant(&fields[1]) else { + out.push(StreamInfo { + node_id, + source_type: 0, + mapping_id: None, + width: 0, + height: 0, + position_x: 0, + position_y: 0, + }); + continue; + }; + let mut source_type = 0u32; + let mut mapping_id: Option = None; + let mut size = (0u32, 0u32); + let mut position = (0i32, 0i32); + for (k, v) in dict.iter() { + let Value::Str(key) = k else { continue }; + match key.as_str() { + "source_type" => { + source_type = read_u32(v).unwrap_or(0); + } + "mapping_id" => { + mapping_id = read_str(v); + } + "size" => { + if let Some(p) = read_pair_u32(v) { + size = p; + } + } + "position" => { + if let Some(p) = read_pair_i32(v) { + position = p; + } + } + _ => {} + } + } + out.push(StreamInfo { + node_id, + source_type, + mapping_id, + width: size.0, + height: size.1, + position_x: position.0, + position_y: position.1, + }); + } + out +} + +fn cursor_mode_matches(results: &HashMap, expected: u32) -> bool { + let Some(raw) = results.get("cursor_mode") else { + return true; + }; + matches!(read_u32(value_of(raw)), Some(mode) if mode == expected) +} + +pub fn open_session_and_pick() -> Result<(LiveSession, Vec), PortalError> { + let version = read_portal_version()?; + if version < MIN_PORTAL_VERSION { + return Err(PortalError::PortalTooOld(version)); + } + let available_cursor_modes = read_available_cursor_modes().unwrap_or(0); + if available_cursor_modes & CURSOR_MODE_HIDDEN == 0 { + return Err(PortalError::CursorModeUnavailable); + } + + let conn = blocking_session_conn()?; + register_portal_app_id(&conn); + let unique = unique_name(&conn)?; + let unique_str = unique.as_str().to_string(); + + let proxy = BlockingProxy::new( + &conn, + PORTAL_DESTINATION, + PORTAL_PATH, + SCREEN_CAST_INTERFACE, + ) + .map_err(|_| PortalError::DbusError)?; + + let create_token = mint_token("fluxer_sc_create"); + let session_token = mint_token("fluxer_sc_session"); + let create_request_path = request_path(&unique_str, &create_token); + let expected_session_path = session_path(&unique_str, &session_token); + let create_pending = watch_request(&conn, &create_request_path)?; + + let mut create_opts: HashMap<&str, Value<'_>> = HashMap::new(); + create_opts.insert("handle_token", Value::new(create_token.as_str())); + create_opts.insert("session_handle_token", Value::new(session_token.as_str())); + let reply_path: OwnedObjectPath = proxy + .call("CreateSession", &(create_opts,)) + .map_err(|_| PortalError::SendFailed)?; + if !reply_path.as_str().is_empty() && reply_path.as_str() != create_request_path { + return Err(PortalError::InvalidReply); + } + let envelope = create_pending.wait()?; + if envelope.code != 0 { + return Err(PortalError::Cancelled); + } + let returned_session_handle = envelope + .results + .get("session_handle") + .and_then(|v| read_str(value_of(v))) + .ok_or(PortalError::InvalidReply)?; + if returned_session_handle != expected_session_path { + return Err(PortalError::InvalidReply); + } + let session_handle = returned_session_handle; + + let select_token = mint_token("fluxer_sc_select"); + let select_request_path = request_path(&unique_str, &select_token); + let select_pending = watch_request(&conn, &select_request_path)?; + let mut select_opts: HashMap<&str, Value<'_>> = HashMap::new(); + select_opts.insert("handle_token", Value::new(select_token.as_str())); + select_opts.insert("types", Value::new(SOURCE_TYPES_ALL)); + select_opts.insert("multiple", Value::new(false)); + select_opts.insert("cursor_mode", Value::new(CURSOR_MODE_HIDDEN)); + let session_obj = OwnedObjectPath::try_from(session_handle.as_str()) + .map_err(|_| PortalError::InvalidReply)?; + let select_reply: OwnedObjectPath = proxy + .call("SelectSources", &(&session_obj, select_opts)) + .map_err(|_| PortalError::SendFailed)?; + if !select_reply.as_str().is_empty() && select_reply.as_str() != select_request_path { + return Err(PortalError::InvalidReply); + } + let select_envelope = select_pending.wait()?; + if select_envelope.code != 0 { + return Err(PortalError::Cancelled); + } + if !cursor_mode_matches(&select_envelope.results, CURSOR_MODE_HIDDEN) { + return Err(PortalError::CursorModeUnavailable); + } + + let start_token = mint_token("fluxer_sc_start"); + let start_request_path = request_path(&unique_str, &start_token); + let start_pending = watch_request(&conn, &start_request_path)?; + let mut start_opts: HashMap<&str, Value<'_>> = HashMap::new(); + start_opts.insert("handle_token", Value::new(start_token.as_str())); + let start_reply: OwnedObjectPath = proxy + .call("Start", &(&session_obj, "", start_opts)) + .map_err(|_| PortalError::SendFailed)?; + if !start_reply.as_str().is_empty() && start_reply.as_str() != start_request_path { + return Err(PortalError::InvalidReply); + } + let start_envelope = start_pending.wait()?; + if start_envelope.code != 0 { + return Err(PortalError::Cancelled); + } + if !cursor_mode_matches(&start_envelope.results, CURSOR_MODE_HIDDEN) { + return Err(PortalError::CursorModeUnavailable); + } + let streams = parse_streams(&start_envelope.results); + if streams.is_empty() { + return Err(PortalError::NoStreams); + } + Ok(( + LiveSession { + handle: session_handle, + conn, + }, + streams, + )) +} + +pub fn open_pipewire_remote(session: &LiveSession) -> Result { + let proxy = BlockingProxy::new( + &session.conn, + PORTAL_DESTINATION, + PORTAL_PATH, + SCREEN_CAST_INTERFACE, + ) + .map_err(|_| PortalError::DbusError)?; + let session_obj = OwnedObjectPath::try_from(session.handle.as_str()) + .map_err(|_| PortalError::InvalidReply)?; + let opts: HashMap<&str, Value<'_>> = HashMap::new(); + let fd: zbus::zvariant::OwnedFd = proxy + .call("OpenPipeWireRemote", &(&session_obj, opts)) + .map_err(|_| PortalError::SendFailed)?; + Ok(OwnedFd::from(fd)) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn request_path_sanitizes_unique_bus_name() { + assert_eq!( + request_path(":1.42", "fluxer_sc_create_1"), + "/org/freedesktop/portal/desktop/request/1_42/fluxer_sc_create_1" + ); + } + + #[test] + fn session_path_sanitizes_unique_bus_name() { + assert_eq!( + session_path(":1.42", "fluxer_sc_session_1"), + "/org/freedesktop/portal/desktop/session/1_42/fluxer_sc_session_1" + ); + } + + #[test] + fn mint_token_is_distinct_and_prefixed() { + let a = mint_token("fluxer_sc_create"); + let b = mint_token("fluxer_sc_create"); + assert_ne!(a, b); + assert!(a.starts_with("fluxer_sc_create_")); + } + + #[test] + fn cursor_mode_constants_match_portal_spec() { + assert_eq!(CURSOR_MODE_HIDDEN, 1); + assert_eq!(CURSOR_MODE_EMBEDDED, 2); + assert_eq!(CURSOR_MODE_METADATA, 4); + } + + #[test] + fn source_type_mask_combines_monitor_and_window() { + assert_eq!(SOURCE_TYPES_ALL, 3); + assert_eq!(SOURCE_TYPE_MONITOR | SOURCE_TYPE_WINDOW, SOURCE_TYPES_ALL); + } + + #[test] + fn normalize_desktop_entry_app_id_accepts_fluxer_ids() { + assert_eq!( + normalize_desktop_entry_app_id("fluxer-canary"), + Some("fluxer-canary".to_string()) + ); + assert_eq!( + normalize_desktop_entry_app_id("fluxer-canary.desktop"), + Some("fluxer-canary".to_string()) + ); + assert_eq!( + normalize_desktop_entry_app_id("app.fluxer.canary"), + Some("app.fluxer.canary".to_string()) + ); + } + + #[test] + fn normalize_desktop_entry_app_id_rejects_paths_and_empty_values() { + assert_eq!(normalize_desktop_entry_app_id(""), None); + assert_eq!(normalize_desktop_entry_app_id(".hidden"), None); + assert_eq!(normalize_desktop_entry_app_id("../fluxer-canary"), None); + assert_eq!(normalize_desktop_entry_app_id("fluxer canary"), None); + } + + #[test] + fn cursor_mode_matches_treats_missing_key_as_honoured() { + let map: HashMap = HashMap::new(); + assert!(cursor_mode_matches(&map, CURSOR_MODE_HIDDEN)); + } +} diff --git a/fluxer_desktop/native/mac-app-audio/Cargo.lock b/fluxer_desktop/native/mac-app-audio/Cargo.lock new file mode 100644 index 0000000..55b9ce7 --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/Cargo.lock @@ -0,0 +1,553 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags", + "block2", + "libc", + "objc2", +] + +[[package]] +name = "fluxer_gpu_rebuild" +version = "0.0.0" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "fluxer_mac_app_audio" +version = "0.0.0" +dependencies = [ + "block2", + "dispatch2", + "fluxer_screen_frame_bus", + "libc", + "napi", + "napi-build", + "napi-derive", + "objc2", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-media", + "objc2-foundation", + "objc2-screen-capture-kit", + "parking_lot", +] + +[[package]] +name = "fluxer_screen_frame_bus" +version = "0.0.0" +dependencies = [ + "fluxer_gpu_rebuild", + "parking_lot", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash", + "tokio", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-av-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478ae33fcac9df0a18db8302387c666b8ef08a3e2d62b510ca4fc278a384b6c0" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-audio" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1eebcea8b0dbff5f7c8504f3107c68fc061a3eb44932051c8cf8a68d969c3b2" +dependencies = [ + "block2", + "dispatch2", + "libc", + "objc2", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-audio-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c" +dependencies = [ + "bitflags", + "objc2", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", + "block2", + "dispatch2", + "libc", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-media" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ec576860167a15dd9fce7fbee7512beb4e31f532159d3482d1f9c6caedf31d" +dependencies = [ + "bitflags", + "block2", + "dispatch2", + "objc2", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-core-video", +] + +[[package]] +name = "objc2-core-video" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-io-surface", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-screen-capture-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74b7c5390f477482f001bc354d6571a70db7e4f8d5288e860c45521fbce11394" +dependencies = [ + "bitflags", + "block2", + "dispatch2", + "libc", + "objc2", + "objc2-av-foundation", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-media", + "objc2-foundation", + "objc2-uniform-type-identifiers", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7902ac02859fc1f7045f8b598c63f1ae0cc7efeaa06a9bc9f3d9a3c955974fa4" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "pin-project-lite", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" diff --git a/fluxer_desktop/native/mac-app-audio/Cargo.toml b/fluxer_desktop/native/mac-app-audio/Cargo.toml new file mode 100644 index 0000000..1938cb1 --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "fluxer_mac_app_audio" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8", "async"]} +napi-derive = "3.5.6" +parking_lot = "0.12" + +[target.'cfg(target_os = "macos")'.dependencies.fluxer_screen_frame_bus] +path = "../screen-frame-bus" + +[target.'cfg(target_os = "macos")'.dependencies] +libc = "0.2.186" +objc2 = "0.6" +objc2-foundation = {version = "0.3", features = ["NSString", "NSArray", "NSDictionary", "NSError", "NSValue", "NSBundle", "NSProcessInfo"]} +objc2-screen-capture-kit = {version = "0.3", features = ["SCStream", "SCShareableContent", "objc2-core-graphics", "objc2-core-media", "objc2-core-foundation", "block2", "dispatch2", "libc"]} +objc2-core-audio = {version = "0.3", features = ["AudioHardware", "AudioHardwareDeprecated", "HostTime", "objc2-core-audio-types", "objc2-foundation"]} +objc2-core-audio-types = "0.3" +objc2-core-media = "0.3" +objc2-core-foundation = {version = "0.3", features = ["CFArray", "CFDictionary", "CFNumber", "CFString", "CFBase"]} +objc2-core-graphics = {version = "0.3.2", default-features = false, features = ["std", "CGWindow"]} +block2 = "0.6" +dispatch2 = "0.3" + +[build-dependencies] +napi-build = "2.3.2" diff --git a/fluxer_desktop/native/mac-app-audio/build.rs b/fluxer_desktop/native/mac-app-audio/build.rs new file mode 100644 index 0000000..0f00487 --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/build.rs @@ -0,0 +1,11 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + napi_build::setup(); + + #[cfg(target_os = "macos")] + { + println!("cargo:rustc-link-lib=framework=AppKit"); + println!("cargo:rustc-link-lib=framework=AVFoundation"); + } +} diff --git a/fluxer_desktop/native/mac-app-audio/index.d.ts b/fluxer_desktop/native/mac-app-audio/index.d.ts new file mode 100644 index 0000000..8b67d76 --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/index.d.ts @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import {EventEmitter} from 'node:events'; + +export interface MacAppAudioBackendInfo { + backend: string; + supported: boolean; + reason: string; + minMacosVersion: string; + minMacosVersionCoreaudio: string; + detectedMacosVersion?: string; + sckAvailable: boolean; + coreaudioAvailable: boolean; +} + +export declare function getBackendInfo(): MacAppAudioBackendInfo; + +export interface MacBackendAvailability { + sck?: { + supported: boolean; + macosVersion?: string; + }; + coreaudio?: { + supported: boolean; + }; + screenPermission?: string; + audioPermission?: string; +} + +export interface MacApplicationDescriptor { + pid: number; + bundleId?: string; + name: string; +} + +export interface ProcessLoopbackOptions { + excludeSelf?: boolean; + includeProcessTree?: boolean; + backend?: 'sck' | 'coreaudio' | 'auto'; + macBackend?: 'sck' | 'coreaudio' | 'auto'; + captureScope?: 'process' | 'system'; + macCaptureScope?: 'process' | 'system'; + scope?: 'process' | 'system'; +} + +export interface AudioFrame { + samples: Float32Array; + sampleRate: number; + channels: number; + timestampUs: number; +} + +export declare const loadError: Error | null; + +export declare function __setBindingForTests(binding: unknown): void; + +export declare function pidFromWindowId(windowId: number): number; + +export declare function listAudibleApplications(): Promise>; + +export declare function getBackendAvailability(): Promise; + +export declare interface ProcessLoopback { + on(event: 'frame', listener: (frame: AudioFrame) => void): this; + on(event: 'error', listener: (err: Error) => void): this; + on(event: 'closed', listener: () => void): this; + on(event: string | symbol, listener: (...args: Array) => void): this; + off(event: 'frame', listener: (frame: AudioFrame) => void): this; + off(event: 'error', listener: (err: Error) => void): this; + off(event: 'closed', listener: () => void): this; + off(event: string | symbol, listener: (...args: Array) => void): this; + emit(event: 'frame', frame: AudioFrame): boolean; + emit(event: 'error', err: Error): boolean; + emit(event: 'closed'): boolean; +} + +export declare class ProcessLoopback extends EventEmitter { + constructor(pid: number, options?: ProcessLoopbackOptions); + + start(): Promise; + + stop(): Promise; +} diff --git a/fluxer_desktop/native/mac-app-audio/index.js b/fluxer_desktop/native/mac-app-audio/index.js new file mode 100644 index 0000000..d60c296 --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/index.js @@ -0,0 +1,208 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {EventEmitter} = require('node:events'); +const {existsSync} = require('node:fs'); +const {join, sep} = require('node:path'); +const {performance} = require('node:perf_hooks'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); +const MODULE_NAME = '@fluxer/mac-app-audio'; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName() { + if (process.platform !== 'darwin') { + throw new Error(`@fluxer/mac-app-audio is only supported on macOS, got ${process.platform}`); + } + switch (process.arch) { + case 'x64': + return 'mac-app-audio.darwin-x64.node'; + case 'arm64': + return 'mac-app-audio.darwin-arm64.node'; + default: + throw new Error(`Unsupported macOS architecture: ${process.arch}`); + } +} + +let binding = null; +let loadError = null; + +if (process.platform === 'darwin') { + try { + const nativeRoot = resolveNativeRoot(); + const nativePath = join(nativeRoot, nativeFileName()); + const loaded = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + probe: false, + }); + binding = loaded.binding; + loadError = loaded.loadError; + if (loadError) throw loadError; + } catch (error) { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: 'native loader threw before binding load completed', + cause: error, + }); + throw loadError; + } +} + +function pidFromWindowId(windowId) { + if (!binding) return 0; + return binding.pidFromWindowId(windowId); +} + +function listAudibleApplications() { + if (!binding) return Promise.resolve([]); + return binding.listAudibleApplications(); +} + +function getBackendInfo() { + if (!binding) { + return { + backend: 'mac-app-audio', + supported: false, + reason: + process.platform === 'darwin' + ? `@fluxer/mac-app-audio native binary unavailable: ${loadError?.message ?? 'unknown reason'}` + : `@fluxer/mac-app-audio is only supported on macOS, got ${process.platform}`, + minMacosVersion: '12.3', + minMacosVersionCoreaudio: '14.2', + detectedMacosVersion: undefined, + sckAvailable: false, + coreaudioAvailable: false, + }; + } + return binding.getBackendInfo(); +} + +function getBackendAvailability() { + if (!binding) { + return Promise.resolve({ + sck: {supported: false}, + coreaudio: {supported: false}, + screenPermission: 'not-determined', + audioPermission: 'not-determined', + }); + } + return binding.getBackendAvailability(); +} + +function __setBindingForTests(nextBinding) { + binding = nextBinding; + loadError = null; +} + +class ProcessLoopback extends EventEmitter { + constructor(pid, options = {}) { + super(); + if (!binding) { + throw loadError || new Error('@fluxer/mac-app-audio binding unavailable'); + } + this.pid = pid; + void options.excludeSelf; + this.excludeSelf = true; + this.includeProcessTree = options.includeProcessTree ?? true; + this.backend = options.backend ?? options.macBackend ?? 'auto'; + this.captureScope = options.captureScope ?? options.macCaptureScope ?? options.scope ?? 'process'; + this.started = false; + this.stopped = false; + this.closedEmitted = false; + this.nextTimestampUs = Math.round(performance.now() * 1000); + this.native = new binding.ProcessLoopback(); + this.native.setFrameCallback((samples) => { + if (this.stopped) return; + const copied = new Float32Array(samples); + const numFrames = copied.length / 2; + const frame = { + samples: copied, + sampleRate: 48000, + channels: 2, + timestampUs: this.nextTimestampUs, + }; + this.nextTimestampUs += Math.round((numFrames / frame.sampleRate) * 1_000_000); + this.emit('frame', frame); + }); + this.native.setLifecycleCallback((type, message) => { + if (type === 'error') { + this.emit('error', new Error(message || 'macOS app audio stream stopped')); + return; + } + if (type === 'closed') { + if (this.stopped) { + this.emitClosedOnce(); + return; + } + this.stopped = true; + Promise.resolve() + .then(() => this.native.stop()) + .catch(() => {}); + this.emitClosedOnce(); + } + }); + } + + emitClosedOnce() { + if (this.closedEmitted) return; + this.closedEmitted = true; + this.emit('closed'); + } + + async start() { + if (this.started || this.stopped) return; + this.started = true; + try { + await this.native.start(this.pid, this.excludeSelf, this.includeProcessTree, this.backend, this.captureScope); + } catch (error) { + this.stopped = true; + this.emit('error', error instanceof Error ? error : new Error(String(error))); + throw error; + } + } + + setScreenAudioSink(handle) { + if (typeof this.native.setScreenAudioSink !== 'function') return false; + try { + return this.native.setScreenAudioSink(handle) !== false; + } catch { + return false; + } + } + + clearScreenAudioSink() { + if (typeof this.native.clearScreenAudioSink === 'function') { + this.native.clearScreenAudioSink(); + } + } + + async stop() { + if (this.stopped) return; + this.stopped = true; + this.clearScreenAudioSink(); + try { + await this.native.stop(); + } finally { + this.emitClosedOnce(); + } + } +} + +module.exports = { + ProcessLoopback, + listAudibleApplications, + getBackendAvailability, + getBackendInfo, + pidFromWindowId, + loadError, + __setBindingForTests, +}; diff --git a/fluxer_desktop/native/mac-app-audio/index.test.mjs b/fluxer_desktop/native/mac-app-audio/index.test.mjs new file mode 100644 index 0000000..cc335ef --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/index.test.mjs @@ -0,0 +1,132 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import {createRequire} from 'node:module'; +import {afterEach, describe, expect, test} from 'vitest'; + +const require = createRequire(import.meta.url); +const macAppAudio = require('./index.js'); + +function makeFakeBinding() { + const calls = []; + const natives = []; + class FakeNative { + constructor() { + this.frameCallback = undefined; + this.lifecycleCallback = undefined; + this.stopCount = 0; + natives.push(this); + } + + setFrameCallback(callback) { + this.frameCallback = callback; + } + + setLifecycleCallback(callback) { + this.lifecycleCallback = callback; + } + + async start(pid, excludeSelf, includeProcessTree, backend, captureScope) { + calls.push({pid, excludeSelf, includeProcessTree, backend, captureScope}); + } + + async stop() { + this.stopCount += 1; + } + } + return { + binding: { + ProcessLoopback: FakeNative, + listAudibleApplications: async () => [], + getBackendAvailability: async () => ({}), + pidFromWindowId: () => 0, + }, + calls, + natives, + }; +} + +afterEach(() => { + macAppAudio.__setBindingForTests(null); +}); + +describe('mac-app-audio loader wrapper', () => { + test('forces excludeSelf=true and preserves includeProcessTree=false', async () => { + const {binding, calls} = makeFakeBinding(); + macAppAudio.__setBindingForTests(binding); + const loopback = new macAppAudio.ProcessLoopback(1234, { + excludeSelf: false, + includeProcessTree: false, + }); + loopback.on('error', () => {}); + await loopback.start(); + expect(calls).toEqual([ + {pid: 1234, excludeSelf: true, includeProcessTree: false, backend: 'auto', captureScope: 'process'}, + ]); + }); + test('forwards explicit backend preference to native binding', async () => { + const {binding, calls} = makeFakeBinding(); + macAppAudio.__setBindingForTests(binding); + const loopback = new macAppAudio.ProcessLoopback(1234, { + backend: 'coreaudio', + }); + loopback.on('error', () => {}); + await loopback.start(); + expect(calls).toEqual([ + {pid: 1234, excludeSelf: true, includeProcessTree: true, backend: 'coreaudio', captureScope: 'process'}, + ]); + }); + test('forwards explicit capture scope to native binding', async () => { + const {binding, calls} = makeFakeBinding(); + macAppAudio.__setBindingForTests(binding); + const loopback = new macAppAudio.ProcessLoopback(1234, { + captureScope: 'system', + backend: 'coreaudio', + }); + loopback.on('error', () => {}); + await loopback.start(); + expect(calls).toEqual([ + {pid: 1234, excludeSelf: true, includeProcessTree: true, backend: 'coreaudio', captureScope: 'system'}, + ]); + }); + test('forwards ScreenCaptureKit system capture requests to native binding', async () => { + const {binding, calls} = makeFakeBinding(); + macAppAudio.__setBindingForTests(binding); + const loopback = new macAppAudio.ProcessLoopback(1234, { + captureScope: 'system', + backend: 'sck', + }); + loopback.on('error', () => {}); + await loopback.start(); + expect(calls).toEqual([ + {pid: 1234, excludeSelf: true, includeProcessTree: true, backend: 'sck', captureScope: 'system'}, + ]); + }); + test('copies native frame samples before emitting', () => { + const {binding, natives} = makeFakeBinding(); + macAppAudio.__setBindingForTests(binding); + const loopback = new macAppAudio.ProcessLoopback(1234); + const frames = []; + loopback.on('frame', (frame) => frames.push(frame)); + const nativeSamples = new Float32Array([0.1, 0.2, 0.3, 0.4]); + natives[0].frameCallback(nativeSamples); + nativeSamples.fill(9); + expect(frames).toHaveLength(1); + expect(Array.from(frames[0].samples)).toEqual(Array.from(new Float32Array([0.1, 0.2, 0.3, 0.4]))); + expect(frames[0].channels).toBe(2); + expect(frames[0].sampleRate).toBe(48000); + }); + test('emits closed once when native lifecycle closes and stop is called later', async () => { + const {binding, natives} = makeFakeBinding(); + macAppAudio.__setBindingForTests(binding); + const loopback = new macAppAudio.ProcessLoopback(1234); + let closed = 0; + loopback.on('closed', () => { + closed += 1; + }); + natives[0].lifecycleCallback('closed', ''); + await Promise.resolve(); + await loopback.stop(); + expect(closed).toBe(1); + expect(natives[0].stopCount).toBe(1); + }); +}); diff --git a/fluxer_desktop/native/mac-app-audio/loader-diagnostics.cjs b/fluxer_desktop/native/mac-app-audio/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/mac-app-audio/package.json b/fluxer_desktop/native/mac-app-audio/package.json new file mode 100644 index 0000000..cb037a2 --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/package.json @@ -0,0 +1,36 @@ +{ + "name": "@fluxer/mac-app-audio", + "version": "0.0.0", + "description": "", + "private": true, + "license": "AGPL-3.0-or-later", + "os": [ + "darwin" + ], + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "mac-app-audio.darwin-x64.node", + "mac-app-audio.darwin-arm64.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "pnpm test:cargo && ../../../node_modules/.bin/vitest run", + "test:cargo": "cargo test --manifest-path Cargo.toml" + }, + "binary": { + "napi_versions": [ + 8 + ] + }, + "devDependencies": { + "vitest": "^2.1.0" + } +} diff --git a/fluxer_desktop/native/mac-app-audio/src/audio_converter.rs b/fluxer_desktop/native/mac-app-audio/src/audio_converter.rs new file mode 100644 index 0000000..024ee37 --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/src/audio_converter.rs @@ -0,0 +1,1247 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use core::mem::size_of; + +#[repr(C)] +#[derive(Copy, Clone, Debug, Default)] +pub struct AudioStreamBasicDescription { + pub m_sample_rate: f64, + pub m_format_id: u32, + pub m_format_flags: u32, + pub m_bytes_per_packet: u32, + pub m_frames_per_packet: u32, + pub m_bytes_per_frame: u32, + pub m_channels_per_frame: u32, + pub m_bits_per_channel: u32, + pub m_reserved: u32, +} + +pub const K_AUDIO_FORMAT_LINEAR_PCM: u32 = + ((b'l' as u32) << 24) | ((b'p' as u32) << 16) | ((b'c' as u32) << 8) | (b'm' as u32); + +pub const K_LINEAR_PCM_FLAG_IS_FLOAT: u32 = 1 << 0; +pub const K_LINEAR_PCM_FLAG_IS_BIG_ENDIAN: u32 = 1 << 1; +pub const K_LINEAR_PCM_FLAG_IS_SIGNED_INTEGER: u32 = 1 << 2; +pub const K_LINEAR_PCM_FLAG_IS_PACKED: u32 = 1 << 3; +pub const K_LINEAR_PCM_FLAG_IS_NON_INTERLEAVED: u32 = 1 << 5; + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub struct AudioBuffer { + pub m_number_channels: u32, + pub m_data_byte_size: u32, + pub m_data: *mut core::ffi::c_void, +} + +#[repr(C)] +pub struct AudioBufferList { + pub m_number_buffers: u32, + + pub buffers: [AudioBuffer; 1], +} + +pub fn build_output_asbd(sample_rate: f64, channels: u32) -> AudioStreamBasicDescription { + let bytes_per_frame = 4 * channels; + AudioStreamBasicDescription { + m_sample_rate: sample_rate, + m_format_id: K_AUDIO_FORMAT_LINEAR_PCM, + m_format_flags: K_LINEAR_PCM_FLAG_IS_FLOAT | K_LINEAR_PCM_FLAG_IS_PACKED, + m_bytes_per_packet: bytes_per_frame, + m_frames_per_packet: 1, + m_bytes_per_frame: bytes_per_frame, + m_channels_per_frame: channels, + m_bits_per_channel: 32, + m_reserved: 0, + } +} + +pub fn build_input_asbd( + sample_rate: f64, + channels: u32, + non_interleaved: bool, +) -> AudioStreamBasicDescription { + let bytes_per_frame: u32 = if non_interleaved { 4 } else { 4 * channels }; + let mut flags = K_LINEAR_PCM_FLAG_IS_FLOAT | K_LINEAR_PCM_FLAG_IS_PACKED; + if non_interleaved { + flags |= K_LINEAR_PCM_FLAG_IS_NON_INTERLEAVED; + } + AudioStreamBasicDescription { + m_sample_rate: sample_rate, + m_format_id: K_AUDIO_FORMAT_LINEAR_PCM, + m_format_flags: flags, + m_bytes_per_packet: bytes_per_frame, + m_frames_per_packet: 1, + m_bytes_per_frame: bytes_per_frame, + m_channels_per_frame: channels, + m_bits_per_channel: 32, + m_reserved: 0, + } +} + +pub fn output_frame_capacity(input_frames: u32, in_rate: f64, out_rate: f64) -> u32 { + if in_rate <= 0.0 { + return input_frames; + } + let ratio = out_rate / in_rate; + let f = (input_frames as f64) * ratio; + (f.ceil() + 1.0) as u32 +} + +pub fn converted_frame_count(input_frames: u32, in_rate: f64, out_rate: f64) -> u32 { + if input_frames == 0 { + return 0; + } + if in_rate <= 0.0 || out_rate <= 0.0 || in_rate == out_rate { + return input_frames; + } + let ratio = out_rate / in_rate; + ((input_frames as f64) * ratio).ceil() as u32 +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum PcmConvertError { + UnsupportedFormat, + UnsupportedBitDepth, + MissingData, + OutputTooSmall, +} + +pub fn is_linear_pcm(asbd: &AudioStreamBasicDescription) -> bool { + asbd.m_format_id == K_AUDIO_FORMAT_LINEAR_PCM +} +pub fn is_float(asbd: &AudioStreamBasicDescription) -> bool { + (asbd.m_format_flags & K_LINEAR_PCM_FLAG_IS_FLOAT) != 0 +} +pub fn is_signed_integer(asbd: &AudioStreamBasicDescription) -> bool { + (asbd.m_format_flags & K_LINEAR_PCM_FLAG_IS_SIGNED_INTEGER) != 0 +} +pub fn is_big_endian(asbd: &AudioStreamBasicDescription) -> bool { + (asbd.m_format_flags & K_LINEAR_PCM_FLAG_IS_BIG_ENDIAN) != 0 +} +pub fn is_non_interleaved(asbd: &AudioStreamBasicDescription) -> bool { + (asbd.m_format_flags & K_LINEAR_PCM_FLAG_IS_NON_INTERLEAVED) != 0 +} +pub fn is_packed(asbd: &AudioStreamBasicDescription) -> bool { + (asbd.m_format_flags & K_LINEAR_PCM_FLAG_IS_PACKED) != 0 +} + +pub fn is_native_f32_interleaved(asbd: &AudioStreamBasicDescription) -> bool { + is_linear_pcm(asbd) + && is_float(asbd) + && is_packed(asbd) + && asbd.m_bits_per_channel == 32 + && !is_big_endian(asbd) + && !is_non_interleaved(asbd) +} + +pub fn is_native_f32_planar(asbd: &AudioStreamBasicDescription) -> bool { + is_linear_pcm(asbd) + && is_float(asbd) + && is_packed(asbd) + && asbd.m_bits_per_channel == 32 + && !is_big_endian(asbd) + && is_non_interleaved(asbd) +} + +fn bytes_per_sample(asbd: &AudioStreamBasicDescription) -> Result { + if !is_linear_pcm(asbd) { + return Err(PcmConvertError::UnsupportedFormat); + } + if asbd.m_bits_per_channel == 0 || asbd.m_bits_per_channel % 8 != 0 { + return Err(PcmConvertError::UnsupportedBitDepth); + } + let bytes = asbd.m_bits_per_channel / 8; + if !matches!(bytes, 1 | 2 | 3 | 4 | 8) { + return Err(PcmConvertError::UnsupportedBitDepth); + } + Ok(bytes) +} + +pub unsafe fn buffer_at(list: *const AudioBufferList, index: usize) -> *const AudioBuffer { + let buffers = unsafe { core::ptr::addr_of!((*list).buffers) } as *const AudioBuffer; + unsafe { buffers.add(index) } +} + +pub unsafe fn input_frame_count_for_buffer_list( + asbd: &AudioStreamBasicDescription, + list: *const AudioBufferList, +) -> Result { + if !is_linear_pcm(asbd) { + return Err(PcmConvertError::UnsupportedFormat); + } + let nb = unsafe { (*list).m_number_buffers }; + if nb == 0 { + return Err(PcmConvertError::MissingData); + } + let first = unsafe { &*buffer_at(list, 0) }; + if first.m_data.is_null() || first.m_data_byte_size == 0 { + return Ok(0); + } + let channels = if asbd.m_channels_per_frame == 0 { + 1 + } else { + asbd.m_channels_per_frame + }; + let bps = bytes_per_sample(asbd)?; + let frame_stride = if asbd.m_bytes_per_frame != 0 { + asbd.m_bytes_per_frame + } else if is_non_interleaved(asbd) { + bps + } else { + bps * channels + }; + if frame_stride == 0 { + return Ok(0); + } + Ok(first.m_data_byte_size / frame_stride) +} + +fn read_unsigned(bytes: &[u8], big_endian: bool) -> u64 { + let mut out: u64 = 0; + if big_endian { + for &b in bytes { + out = (out << 8) | (b as u64); + } + } else { + let mut shift = 0u32; + for &b in bytes { + out |= (b as u64) << shift; + shift += 8; + } + } + out +} + +fn sign_extend(value: u64, bits: u32) -> i64 { + if bits == 64 { + return value as i64; + } + let shift = (64 - bits) as u32; + ((value << shift) as i64) >> shift +} + +fn pow2_float(exponent: u32) -> f64 { + debug_assert!(exponent < 64); + (1u64 << exponent) as f64 +} + +fn read_scalar_sample( + asbd: &AudioStreamBasicDescription, + bytes: &[u8], +) -> Result { + let bits = asbd.m_bits_per_channel; + if bits == 0 || bits > 64 || bits % 8 != 0 { + return Err(PcmConvertError::UnsupportedBitDepth); + } + let raw = read_unsigned(bytes, is_big_endian(asbd)); + + if is_float(asbd) { + return match bits { + 32 => Ok(f32::from_bits(raw as u32)), + 64 => Ok(f64::from_bits(raw) as f32), + _ => Err(PcmConvertError::UnsupportedBitDepth), + }; + } + + if is_signed_integer(asbd) { + let signed = sign_extend(raw, bits); + let denom = pow2_float(bits - 1); + return Ok(((signed as f64) / denom) as f32); + } + + let midpoint = pow2_float(bits - 1); + Ok((((raw as f64) - midpoint) / midpoint) as f32) +} + +fn source_channel_for(target_channel: u32, source_channels: u32) -> u32 { + if source_channels <= 1 { + return 0; + } + target_channel.min(source_channels - 1) +} + +const MAX_FAST_PATH_CHANNELS: usize = 8; + +#[derive(Copy, Clone)] +enum SampleKind { + F32, + F64, + Signed { bits: u32, denom: f64 }, + Unsigned { midpoint: f64 }, +} + +#[derive(Copy, Clone)] +struct ChannelCursor { + base: *const u8, + stride: usize, +} + +const NULL_CURSOR: ChannelCursor = ChannelCursor { + base: core::ptr::null(), + stride: 0, +}; + +fn sample_kind_for(asbd: &AudioStreamBasicDescription) -> Result { + let bits = asbd.m_bits_per_channel; + assert!(bits >= 8); + assert!(bits <= 64); + if is_float(asbd) { + return match bits { + 32 => Ok(SampleKind::F32), + 64 => Ok(SampleKind::F64), + _ => Err(PcmConvertError::UnsupportedBitDepth), + }; + } + if is_signed_integer(asbd) { + return Ok(SampleKind::Signed { + bits, + denom: pow2_float(bits - 1), + }); + } + Ok(SampleKind::Unsigned { + midpoint: pow2_float(bits - 1), + }) +} + +unsafe fn read_sample_raw( + cursor: ChannelCursor, + frame_index: usize, + bps: usize, + big_endian: bool, +) -> u64 { + debug_assert!(!cursor.base.is_null()); + debug_assert!(bps >= 1); + debug_assert!(bps <= 8); + let p = unsafe { cursor.base.add(frame_index * cursor.stride) }; + let mut raw: u64 = 0; + if big_endian { + for i in 0..bps { + raw = (raw << 8) | (unsafe { *p.add(i) } as u64); + } + } else { + for i in 0..bps { + raw |= (unsafe { *p.add(i) } as u64) << (8 * i as u32); + } + } + raw +} + +fn sample_from_raw(kind: SampleKind, raw: u64) -> f32 { + match kind { + SampleKind::F32 => f32::from_bits(raw as u32), + SampleKind::F64 => f64::from_bits(raw) as f32, + SampleKind::Signed { bits, denom } => ((sign_extend(raw, bits) as f64) / denom) as f32, + SampleKind::Unsigned { midpoint } => (((raw as f64) - midpoint) / midpoint) as f32, + } +} + +unsafe fn build_channel_cursors( + asbd: &AudioStreamBasicDescription, + list: *const AudioBufferList, + input_frames: u32, + output_channels: u32, + cursors: &mut [ChannelCursor; MAX_FAST_PATH_CHANNELS], +) -> Result<(), PcmConvertError> { + assert!(input_frames > 0); + assert!(output_channels > 0); + assert!(output_channels as usize <= MAX_FAST_PATH_CHANNELS); + let channels = if asbd.m_channels_per_frame == 0 { + 1 + } else { + asbd.m_channels_per_frame + }; + let bps = bytes_per_sample(asbd)?; + let non_interleaved = is_non_interleaved(asbd); + let buffer_count = unsafe { (*list).m_number_buffers }; + if buffer_count == 0 { + return Err(PcmConvertError::MissingData); + } + let frame_stride = if asbd.m_bytes_per_frame != 0 { + asbd.m_bytes_per_frame + } else if non_interleaved { + bps + } else { + bps * channels + }; + for ch in 0..output_channels { + let source_channel = source_channel_for(ch, channels); + let buffer_index = if non_interleaved && buffer_count > 1 { + source_channel.min(buffer_count - 1) as usize + } else { + 0 + }; + let buffer = unsafe { &*buffer_at(list, buffer_index) }; + if buffer.m_data.is_null() { + return Err(PcmConvertError::MissingData); + } + let channel_offset = if non_interleaved && buffer_count > 1 { + 0 + } else { + source_channel * bps + }; + let last_end = ((input_frames - 1) as usize) * (frame_stride as usize) + + (channel_offset as usize) + + (bps as usize); + if last_end > buffer.m_data_byte_size as usize { + return Err(PcmConvertError::MissingData); + } + cursors[ch as usize] = ChannelCursor { + base: unsafe { (buffer.m_data as *const u8).add(channel_offset as usize) }, + stride: frame_stride as usize, + }; + } + Ok(()) +} + +unsafe fn convert_with_cursors( + cursors: &[ChannelCursor; MAX_FAST_PATH_CHANNELS], + kind: SampleKind, + bps: usize, + big_endian: bool, + input_frames: u32, + out_frames: u32, + step: f64, + output_channels: u32, + output: &mut [f32], +) -> u32 { + assert!(input_frames > 0); + assert!(out_frames > 0); + assert!(output_channels > 0); + let oc = output_channels as usize; + assert!(oc <= MAX_FAST_PATH_CHANNELS); + assert!(output.len() >= (out_frames as usize) * oc); + for out_frame in 0..out_frames { + let src_pos = (out_frame as f64) * step; + let mut base = src_pos.floor() as u32; + if base >= input_frames { + base = input_frames - 1; + } + let next = if base + 1 < input_frames { + base + 1 + } else { + base + }; + let frac = (src_pos - src_pos.floor()) as f32; + for ch in 0..oc { + let raw_a = unsafe { read_sample_raw(cursors[ch], base as usize, bps, big_endian) }; + let raw_b = unsafe { read_sample_raw(cursors[ch], next as usize, bps, big_endian) }; + let a = sample_from_raw(kind, raw_a); + let b = sample_from_raw(kind, raw_b); + output[(out_frame as usize) * oc + ch] = a + (b - a) * frac; + } + } + out_frames +} + +unsafe fn read_frame_channel( + asbd: &AudioStreamBasicDescription, + list: *const AudioBufferList, + frame_index: u32, + target_channel: u32, +) -> Result { + let channels = if asbd.m_channels_per_frame == 0 { + 1 + } else { + asbd.m_channels_per_frame + }; + let source_channel = source_channel_for(target_channel, channels); + let bps = bytes_per_sample(asbd)?; + let non_interleaved = is_non_interleaved(asbd); + let buffer_count = unsafe { (*list).m_number_buffers }; + if buffer_count == 0 { + return Err(PcmConvertError::MissingData); + } + let buffer_index = if non_interleaved && buffer_count > 1 { + source_channel.min(buffer_count - 1) as usize + } else { + 0 + }; + let buffer = unsafe { &*buffer_at(list, buffer_index) }; + if buffer.m_data.is_null() { + return Err(PcmConvertError::MissingData); + } + let frame_stride = if asbd.m_bytes_per_frame != 0 { + asbd.m_bytes_per_frame + } else if non_interleaved { + bps + } else { + bps * channels + }; + let channel_offset = if non_interleaved && buffer_count > 1 { + 0 + } else { + source_channel * bps + }; + let offset = (frame_index as usize) * (frame_stride as usize) + (channel_offset as usize); + let end = offset + bps as usize; + if end > buffer.m_data_byte_size as usize { + return Err(PcmConvertError::MissingData); + } + let data = buffer.m_data as *const u8; + let slice = unsafe { core::slice::from_raw_parts(data.add(offset), bps as usize) }; + read_scalar_sample(asbd, slice) +} + +unsafe fn copy_native_f32_interleaved( + list: *const AudioBufferList, + input_frames: u32, + channels: u32, + output: &mut [f32], +) -> Result { + debug_assert!(input_frames > 0); + debug_assert!(channels > 0); + let sample_count = (input_frames as usize) * (channels as usize); + debug_assert!(output.len() >= sample_count); + let buffer_count = unsafe { (*list).m_number_buffers }; + if buffer_count == 0 { + return Err(PcmConvertError::MissingData); + } + let buffer = unsafe { &*buffer_at(list, 0) }; + if buffer.m_data.is_null() { + return Err(PcmConvertError::MissingData); + } + let byte_count = sample_count * size_of::(); + if (buffer.m_data_byte_size as usize) < byte_count { + return Err(PcmConvertError::MissingData); + } + unsafe { + core::ptr::copy_nonoverlapping( + buffer.m_data as *const u8, + output.as_mut_ptr() as *mut u8, + byte_count, + ); + } + Ok(input_frames) +} + +unsafe fn copy_native_f32_planar( + list: *const AudioBufferList, + input_frames: u32, + channels: u32, + output_channels: u32, + output: &mut [f32], +) -> Result { + assert!(input_frames > 0); + assert!(channels > 0); + assert!(output_channels > 0); + assert!(output_channels as usize <= MAX_FAST_PATH_CHANNELS); + let sample_count = (input_frames as usize) * (output_channels as usize); + assert!(output.len() >= sample_count); + let buffer_count = unsafe { (*list).m_number_buffers }; + assert!(buffer_count >= channels); + let plane_byte_count = (input_frames as usize) * size_of::(); + let mut planes = [core::ptr::null::(); MAX_FAST_PATH_CHANNELS]; + for ch in 0..output_channels { + let plane_index = source_channel_for(ch, channels) as usize; + let buffer = unsafe { &*buffer_at(list, plane_index) }; + if buffer.m_data.is_null() { + return Err(PcmConvertError::MissingData); + } + if (buffer.m_data_byte_size as usize) < plane_byte_count { + return Err(PcmConvertError::MissingData); + } + planes[ch as usize] = buffer.m_data as *const f32; + } + let oc = output_channels as usize; + for frame in 0..input_frames as usize { + for ch in 0..oc { + output[frame * oc + ch] = unsafe { *planes[ch].add(frame) }; + } + } + Ok(input_frames) +} + +unsafe fn convert_per_sample( + asbd: &AudioStreamBasicDescription, + list: *const AudioBufferList, + input_frames: u32, + out_frames: u32, + step: f64, + output_channels: u32, + output: &mut [f32], +) -> Result { + assert!(input_frames > 0); + assert!(out_frames > 0); + assert!(output.len() >= (out_frames as usize) * (output_channels as usize)); + for out_frame in 0..out_frames { + let src_pos = (out_frame as f64) * step; + let mut base = src_pos.floor() as u32; + if base >= input_frames { + base = input_frames - 1; + } + let next = if base + 1 < input_frames { + base + 1 + } else { + base + }; + let frac = (src_pos - src_pos.floor()) as f32; + for ch in 0..output_channels { + let a = unsafe { read_frame_channel(asbd, list, base, ch)? }; + let b = unsafe { read_frame_channel(asbd, list, next, ch)? }; + output[(out_frame as usize) * (output_channels as usize) + (ch as usize)] = + a + (b - a) * frac; + } + } + Ok(out_frames) +} + +unsafe fn try_same_rate_fast_path( + asbd: &AudioStreamBasicDescription, + list: *const AudioBufferList, + input_frames: u32, + channels: u32, + output_channels: u32, + output: &mut [f32], +) -> Option> { + assert!(input_frames > 0); + assert!(channels > 0); + assert!(output_channels > 0); + if is_native_f32_interleaved(asbd) { + let frame_stride = if asbd.m_bytes_per_frame != 0 { + asbd.m_bytes_per_frame + } else { + 4 * channels + }; + if channels == output_channels && frame_stride == 4 * channels { + return Some(unsafe { + copy_native_f32_interleaved(list, input_frames, channels, output) + }); + } + } + if is_native_f32_planar(asbd) && (output_channels as usize) <= MAX_FAST_PATH_CHANNELS { + let buffer_count = unsafe { (*list).m_number_buffers }; + let stride_ok = asbd.m_bytes_per_frame == 0 || asbd.m_bytes_per_frame == 4; + if buffer_count >= channels && stride_ok { + return Some(unsafe { + copy_native_f32_planar(list, input_frames, channels, output_channels, output) + }); + } + } + None +} + +pub unsafe fn convert_buffer_list_to_interleaved_f32( + asbd: &AudioStreamBasicDescription, + list: *const AudioBufferList, + input_frames: u32, + output_sample_rate: f64, + output_channels: u32, + output: &mut [f32], +) -> Result { + if !is_linear_pcm(asbd) { + return Err(PcmConvertError::UnsupportedFormat); + } + if output_channels == 0 { + return Ok(0); + } + let out_frames = converted_frame_count(input_frames, asbd.m_sample_rate, output_sample_rate); + let needed = (out_frames as usize) * (output_channels as usize); + if needed > output.len() { + return Err(PcmConvertError::OutputTooSmall); + } + if input_frames == 0 || out_frames == 0 { + return Ok(0); + } + let channels = if asbd.m_channels_per_frame == 0 { + 1 + } else { + asbd.m_channels_per_frame + }; + if asbd.m_sample_rate == output_sample_rate { + let fast = unsafe { + try_same_rate_fast_path( + asbd, + list, + input_frames, + channels, + output_channels, + &mut *output, + ) + }; + if let Some(result) = fast { + return result; + } + } + let step = if asbd.m_sample_rate > 0.0 && output_sample_rate > 0.0 { + asbd.m_sample_rate / output_sample_rate + } else { + 1.0 + }; + if (output_channels as usize) <= MAX_FAST_PATH_CHANNELS { + let bps = bytes_per_sample(asbd)?; + let kind = sample_kind_for(asbd)?; + let mut cursors = [NULL_CURSOR; MAX_FAST_PATH_CHANNELS]; + unsafe { build_channel_cursors(asbd, list, input_frames, output_channels, &mut cursors)? }; + let converted = unsafe { + convert_with_cursors( + &cursors, + kind, + bps as usize, + is_big_endian(asbd), + input_frames, + out_frames, + step, + output_channels, + output, + ) + }; + return Ok(converted); + } + unsafe { + convert_per_sample( + asbd, + list, + input_frames, + out_frames, + step, + output_channels, + output, + ) + } +} + +pub type OSStatus = i32; +pub type AudioConverterRef = *mut core::ffi::c_void; + +#[cfg(target_os = "macos")] +unsafe extern "C" { + pub fn AudioConverterNew( + in_source_format: *const AudioStreamBasicDescription, + in_destination_format: *const AudioStreamBasicDescription, + out_audio_converter: *mut AudioConverterRef, + ) -> OSStatus; + pub fn AudioConverterDispose(in_audio_converter: AudioConverterRef) -> OSStatus; +} + +#[cfg(target_os = "macos")] +pub struct Converter { + pub r#ref: AudioConverterRef, + pub in_format: AudioStreamBasicDescription, + pub out_format: AudioStreamBasicDescription, +} + +#[cfg(target_os = "macos")] +impl Converter { + pub fn create( + in_format: AudioStreamBasicDescription, + out_format: AudioStreamBasicDescription, + ) -> Result { + let mut r: AudioConverterRef = core::ptr::null_mut(); + let status = unsafe { AudioConverterNew(&in_format, &out_format, &mut r) }; + if status != 0 || r.is_null() { + return Err("AudioConverterNewFailed"); + } + Ok(Self { + r#ref: r, + in_format, + out_format, + }) + } +} + +#[cfg(target_os = "macos")] +impl Drop for Converter { + fn drop(&mut self) { + if !self.r#ref.is_null() { + unsafe { + AudioConverterDispose(self.r#ref); + } + self.r#ref = core::ptr::null_mut(); + } + } +} + +const _: () = { + assert!(size_of::() == 40); + assert!(size_of::() == 16); +}; + +#[cfg(test)] +mod tests { + use super::*; + + fn make_abl(buffers: &mut [AudioBuffer]) -> Box<[u8]> { + let n = buffers.len(); + let size = size_of::() + n * size_of::(); + let mut storage = vec![0u8; size.max(size_of::())].into_boxed_slice(); + unsafe { + let p = storage.as_mut_ptr() as *mut AudioBufferList; + (*p).m_number_buffers = n as u32; + let slot = core::ptr::addr_of_mut!((*p).buffers) as *mut AudioBuffer; + for (i, b) in buffers.iter().enumerate() { + core::ptr::write(slot.add(i), *b); + } + } + storage + } + + fn abl_ptr(s: &[u8]) -> *const AudioBufferList { + s.as_ptr() as *const AudioBufferList + } + + #[test] + fn lpcm_fourcc() { + assert_eq!(0x6c70636du32, K_AUDIO_FORMAT_LINEAR_PCM); + } + + #[test] + fn build_output_stereo_48k() { + let a = build_output_asbd(48_000.0, 2); + assert_eq!(48_000.0, a.m_sample_rate); + assert_eq!(K_AUDIO_FORMAT_LINEAR_PCM, a.m_format_id); + assert_eq!( + K_LINEAR_PCM_FLAG_IS_FLOAT | K_LINEAR_PCM_FLAG_IS_PACKED, + a.m_format_flags + ); + assert_eq!(8, a.m_bytes_per_packet); + assert_eq!(1, a.m_frames_per_packet); + assert_eq!(8, a.m_bytes_per_frame); + assert_eq!(2, a.m_channels_per_frame); + assert_eq!(32, a.m_bits_per_channel); + assert_eq!(0, a.m_reserved); + } + + #[test] + fn build_output_mono_44_1k() { + let a = build_output_asbd(44_100.0, 1); + assert_eq!(44_100.0, a.m_sample_rate); + assert_eq!(4, a.m_bytes_per_packet); + assert_eq!(4, a.m_bytes_per_frame); + assert_eq!(1, a.m_channels_per_frame); + } + + #[test] + fn build_input_non_interleaved() { + let a = build_input_asbd(48_000.0, 2, true); + assert!(a.m_format_flags & K_LINEAR_PCM_FLAG_IS_NON_INTERLEAVED != 0); + assert_eq!(4, a.m_bytes_per_frame); + assert_eq!(4, a.m_bytes_per_packet); + } + + #[test] + fn build_input_interleaved() { + let a = build_input_asbd(48_000.0, 2, false); + assert!(a.m_format_flags & K_LINEAR_PCM_FLAG_IS_NON_INTERLEAVED == 0); + assert_eq!(8, a.m_bytes_per_frame); + } + + #[test] + fn output_frame_capacity_rounds_up() { + assert_eq!(1025, output_frame_capacity(1024, 48_000.0, 48_000.0)); + assert_eq!(1116, output_frame_capacity(1024, 44_100.0, 48_000.0)); + assert_eq!(512, output_frame_capacity(512, 0.0, 48_000.0)); + } + + #[test] + fn converted_frame_count_exact() { + assert_eq!(1024, converted_frame_count(1024, 48_000.0, 48_000.0)); + assert_eq!(1115, converted_frame_count(1024, 44_100.0, 48_000.0)); + } + + #[test] + fn duplicate_mono_to_stereo() { + let mut samples: [f32; 3] = [0.25, -0.5, 1.0]; + let mut buffers = [AudioBuffer { + m_number_channels: 1, + m_data_byte_size: (samples.len() * size_of::()) as u32, + m_data: samples.as_mut_ptr() as *mut _, + }]; + let storage = make_abl(&mut buffers); + let asbd = build_input_asbd(48_000.0, 1, false); + let mut out = [0.0f32; 6]; + let frames = unsafe { + convert_buffer_list_to_interleaved_f32( + &asbd, + abl_ptr(&storage), + 3, + 48_000.0, + 2, + &mut out, + ) + .unwrap() + }; + assert_eq!(3, frames); + assert_eq!([0.25, 0.25, -0.5, -0.5, 1.0, 1.0], out); + } + + #[test] + fn interleave_planar_stereo() { + let mut left: [f32; 3] = [0.1, 0.2, 0.3]; + let mut right: [f32; 3] = [-0.1, -0.2, -0.3]; + let mut buffers = [ + AudioBuffer { + m_number_channels: 1, + m_data_byte_size: (left.len() * size_of::()) as u32, + m_data: left.as_mut_ptr() as *mut _, + }, + AudioBuffer { + m_number_channels: 1, + m_data_byte_size: (right.len() * size_of::()) as u32, + m_data: right.as_mut_ptr() as *mut _, + }, + ]; + let storage = make_abl(&mut buffers); + let asbd = build_input_asbd(48_000.0, 2, true); + let mut out = [0.0f32; 6]; + let frames = unsafe { + convert_buffer_list_to_interleaved_f32( + &asbd, + abl_ptr(&storage), + 3, + 48_000.0, + 2, + &mut out, + ) + .unwrap() + }; + assert_eq!(3, frames); + assert_eq!([0.1, -0.1, 0.2, -0.2, 0.3, -0.3], out); + } + + #[test] + fn input_frame_count_interleaved_and_planar() { + let mut interleaved: [f32; 6] = [0.1, -0.1, 0.2, -0.2, 0.3, -0.3]; + let mut interleaved_buf = [AudioBuffer { + m_number_channels: 2, + m_data_byte_size: (interleaved.len() * size_of::()) as u32, + m_data: interleaved.as_mut_ptr() as *mut _, + }]; + let storage = make_abl(&mut interleaved_buf); + assert_eq!(3, unsafe { + input_frame_count_for_buffer_list( + &build_input_asbd(48_000.0, 2, false), + abl_ptr(&storage), + ) + .unwrap() + }); + + let mut left: [f32; 4] = [0.1, 0.2, 0.3, 0.4]; + let mut right: [f32; 4] = [-0.1, -0.2, -0.3, -0.4]; + let mut planar_buf = [ + AudioBuffer { + m_number_channels: 1, + m_data_byte_size: (left.len() * size_of::()) as u32, + m_data: left.as_mut_ptr() as *mut _, + }, + AudioBuffer { + m_number_channels: 1, + m_data_byte_size: (right.len() * size_of::()) as u32, + m_data: right.as_mut_ptr() as *mut _, + }, + ]; + let storage = make_abl(&mut planar_buf); + assert_eq!(4, unsafe { + input_frame_count_for_buffer_list( + &build_input_asbd(48_000.0, 2, true), + abl_ptr(&storage), + ) + .unwrap() + }); + } + + #[test] + fn normalize_signed_int16() { + let mut samples: [i16; 4] = [0, 16_384, -32_768, 32_767]; + let mut buffers = [AudioBuffer { + m_number_channels: 2, + m_data_byte_size: (samples.len() * size_of::()) as u32, + m_data: samples.as_mut_ptr() as *mut _, + }]; + let storage = make_abl(&mut buffers); + let mut asbd = build_input_asbd(48_000.0, 2, false); + asbd.m_format_flags = K_LINEAR_PCM_FLAG_IS_SIGNED_INTEGER | K_LINEAR_PCM_FLAG_IS_PACKED; + asbd.m_bytes_per_packet = 4; + asbd.m_bytes_per_frame = 4; + asbd.m_bits_per_channel = 16; + let mut out = [0.0f32; 4]; + let frames = unsafe { + convert_buffer_list_to_interleaved_f32( + &asbd, + abl_ptr(&storage), + 2, + 48_000.0, + 2, + &mut out, + ) + .unwrap() + }; + assert_eq!(2, frames); + assert!((out[0] - 0.0).abs() < 1e-5); + assert!((out[1] - 0.5).abs() < 1e-5); + assert!((out[2] - -1.0).abs() < 1e-5); + assert!((out[3] - 0.9999695).abs() < 1e-5); + } + + #[test] + fn handle_float64_input() { + let mut samples: [f64; 2] = [-0.25, 0.75]; + let mut buffers = [AudioBuffer { + m_number_channels: 1, + m_data_byte_size: (samples.len() * size_of::()) as u32, + m_data: samples.as_mut_ptr() as *mut _, + }]; + let storage = make_abl(&mut buffers); + let mut asbd = build_input_asbd(48_000.0, 1, false); + asbd.m_bytes_per_packet = 8; + asbd.m_bytes_per_frame = 8; + asbd.m_bits_per_channel = 64; + let mut out = [0.0f32; 4]; + let frames = unsafe { + convert_buffer_list_to_interleaved_f32( + &asbd, + abl_ptr(&storage), + 2, + 48_000.0, + 2, + &mut out, + ) + .unwrap() + }; + assert_eq!(2, frames); + assert_eq!([-0.25, -0.25, 0.75, 0.75], out); + } + + #[test] + fn fast_path_interleaved_stereo_48k_passthrough() { + let mut samples: [f32; 6] = [0.1, -0.1, 0.2, -0.2, 0.3, -0.3]; + let mut buffers = [AudioBuffer { + m_number_channels: 2, + m_data_byte_size: (samples.len() * size_of::()) as u32, + m_data: samples.as_mut_ptr() as *mut _, + }]; + let storage = make_abl(&mut buffers); + let asbd = build_input_asbd(48_000.0, 2, false); + assert!(is_native_f32_interleaved(&asbd)); + let mut out = [0.0f32; 6]; + let frames = unsafe { + convert_buffer_list_to_interleaved_f32( + &asbd, + abl_ptr(&storage), + 3, + 48_000.0, + 2, + &mut out, + ) + .unwrap() + }; + assert_eq!(3, frames); + assert_eq!([0.1, -0.1, 0.2, -0.2, 0.3, -0.3], out); + } + + #[test] + fn native_float_fast_path_requires_packed_layout() { + let mut asbd = build_input_asbd(48_000.0, 2, false); + assert!(is_native_f32_interleaved(&asbd)); + asbd.m_format_flags &= !K_LINEAR_PCM_FLAG_IS_PACKED; + assert!(!is_native_f32_interleaved(&asbd)); + asbd.m_format_flags |= K_LINEAR_PCM_FLAG_IS_NON_INTERLEAVED; + assert!(!is_native_f32_planar(&asbd)); + } + + #[test] + fn fast_path_short_buffer_returns_missing_data() { + let mut samples: [f32; 4] = [0.1, -0.1, 0.2, -0.2]; + let mut buffers = [AudioBuffer { + m_number_channels: 2, + m_data_byte_size: (samples.len() * size_of::()) as u32, + m_data: samples.as_mut_ptr() as *mut _, + }]; + let storage = make_abl(&mut buffers); + let asbd = build_input_asbd(48_000.0, 2, false); + let mut out = [0.0f32; 6]; + let result = unsafe { + convert_buffer_list_to_interleaved_f32( + &asbd, + abl_ptr(&storage), + 3, + 48_000.0, + 2, + &mut out, + ) + }; + assert_eq!(Err(PcmConvertError::MissingData), result); + } + + #[test] + fn planar_fast_path_mono_duplicated_to_stereo() { + let mut plane: [f32; 3] = [0.25, -0.5, 1.0]; + let mut buffers = [AudioBuffer { + m_number_channels: 1, + m_data_byte_size: (plane.len() * size_of::()) as u32, + m_data: plane.as_mut_ptr() as *mut _, + }]; + let storage = make_abl(&mut buffers); + let asbd = build_input_asbd(48_000.0, 1, true); + assert!(is_native_f32_planar(&asbd)); + let mut out = [0.0f32; 6]; + let frames = unsafe { + convert_buffer_list_to_interleaved_f32( + &asbd, + abl_ptr(&storage), + 3, + 48_000.0, + 2, + &mut out, + ) + .unwrap() + }; + assert_eq!(3, frames); + assert_eq!([0.25, 0.25, -0.5, -0.5, 1.0, 1.0], out); + } + + #[test] + fn planar_fast_path_short_plane_returns_missing_data() { + let mut left: [f32; 2] = [0.1, 0.2]; + let mut right: [f32; 3] = [-0.1, -0.2, -0.3]; + let mut buffers = [ + AudioBuffer { + m_number_channels: 1, + m_data_byte_size: (left.len() * size_of::()) as u32, + m_data: left.as_mut_ptr() as *mut _, + }, + AudioBuffer { + m_number_channels: 1, + m_data_byte_size: (right.len() * size_of::()) as u32, + m_data: right.as_mut_ptr() as *mut _, + }, + ]; + let storage = make_abl(&mut buffers); + let asbd = build_input_asbd(48_000.0, 2, true); + let mut out = [0.0f32; 6]; + let result = unsafe { + convert_buffer_list_to_interleaved_f32( + &asbd, + abl_ptr(&storage), + 3, + 48_000.0, + 2, + &mut out, + ) + }; + assert_eq!(Err(PcmConvertError::MissingData), result); + } + + #[test] + fn planar_resample_to_target_rate() { + let mut left: [f32; 2] = [0.0, 1.0]; + let mut right: [f32; 2] = [1.0, 0.0]; + let mut buffers = [ + AudioBuffer { + m_number_channels: 1, + m_data_byte_size: (left.len() * size_of::()) as u32, + m_data: left.as_mut_ptr() as *mut _, + }, + AudioBuffer { + m_number_channels: 1, + m_data_byte_size: (right.len() * size_of::()) as u32, + m_data: right.as_mut_ptr() as *mut _, + }, + ]; + let storage = make_abl(&mut buffers); + let asbd = build_input_asbd(24_000.0, 2, true); + let mut out = [0.0f32; 8]; + let frames = unsafe { + convert_buffer_list_to_interleaved_f32( + &asbd, + abl_ptr(&storage), + 2, + 48_000.0, + 2, + &mut out, + ) + .unwrap() + }; + assert_eq!(4, frames); + assert_eq!([0.0, 1.0, 0.5, 0.5, 1.0, 0.0, 1.0, 0.0], out); + } + + #[test] + fn big_endian_signed_int16_descriptor_path() { + let mut samples: [u8; 8] = [0x00, 0x00, 0x40, 0x00, 0x80, 0x00, 0x7f, 0xff]; + let mut buffers = [AudioBuffer { + m_number_channels: 2, + m_data_byte_size: samples.len() as u32, + m_data: samples.as_mut_ptr() as *mut _, + }]; + let storage = make_abl(&mut buffers); + let mut asbd = build_input_asbd(48_000.0, 2, false); + asbd.m_format_flags = K_LINEAR_PCM_FLAG_IS_SIGNED_INTEGER + | K_LINEAR_PCM_FLAG_IS_BIG_ENDIAN + | K_LINEAR_PCM_FLAG_IS_PACKED; + asbd.m_bytes_per_packet = 4; + asbd.m_bytes_per_frame = 4; + asbd.m_bits_per_channel = 16; + let mut out = [0.0f32; 4]; + let frames = unsafe { + convert_buffer_list_to_interleaved_f32( + &asbd, + abl_ptr(&storage), + 2, + 48_000.0, + 2, + &mut out, + ) + .unwrap() + }; + assert_eq!(2, frames); + assert!((out[0] - 0.0).abs() < 1e-5); + assert!((out[1] - 0.5).abs() < 1e-5); + assert!((out[2] - -1.0).abs() < 1e-5); + assert!((out[3] - 0.9999695).abs() < 1e-5); + } + + #[test] + fn wide_output_falls_back_to_per_sample_path() { + let mut samples: [f32; 2] = [0.5, -0.5]; + let mut buffers = [AudioBuffer { + m_number_channels: 1, + m_data_byte_size: (samples.len() * size_of::()) as u32, + m_data: samples.as_mut_ptr() as *mut _, + }]; + let storage = make_abl(&mut buffers); + let asbd = build_input_asbd(48_000.0, 1, false); + let mut out = [0.0f32; 18]; + let frames = unsafe { + convert_buffer_list_to_interleaved_f32( + &asbd, + abl_ptr(&storage), + 2, + 48_000.0, + 9, + &mut out, + ) + .unwrap() + }; + assert_eq!(2, frames); + for ch in 0..9 { + assert_eq!(0.5, out[ch]); + assert_eq!(-0.5, out[9 + ch]); + } + } + + #[test] + fn linear_resample_to_target_rate() { + let mut samples: [f32; 2] = [0.0, 1.0]; + let mut buffers = [AudioBuffer { + m_number_channels: 1, + m_data_byte_size: (samples.len() * size_of::()) as u32, + m_data: samples.as_mut_ptr() as *mut _, + }]; + let storage = make_abl(&mut buffers); + let asbd = build_input_asbd(24_000.0, 1, false); + let mut out = [0.0f32; 8]; + let frames = unsafe { + convert_buffer_list_to_interleaved_f32( + &asbd, + abl_ptr(&storage), + 2, + 48_000.0, + 2, + &mut out, + ) + .unwrap() + }; + assert_eq!(4, frames); + assert_eq!([0.0, 0.0, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0], out); + } +} diff --git a/fluxer_desktop/native/mac-app-audio/src/audio_source.rs b/fluxer_desktop/native/mac-app-audio/src/audio_source.rs new file mode 100644 index 0000000..cd7e964 --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/src/audio_source.rs @@ -0,0 +1,462 @@ +#![allow(non_snake_case, non_camel_case_types)] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +use core::ffi::c_void; +use core::ptr; +use std::sync::{ + Mutex, MutexGuard, + atomic::{AtomicPtr, AtomicU64, Ordering}, +}; + +use objc2::rc::Retained; +use objc2::runtime::{NSObject, NSObjectProtocol, ProtocolObject}; +use objc2::{AllocAnyThread, DefinedClass, define_class, msg_send}; +use objc2_core_foundation::CFAllocator; +use objc2_core_media::CMSampleBuffer; +use objc2_foundation::NSError; +use objc2_screen_capture_kit::{SCStream, SCStreamDelegate, SCStreamOutput, SCStreamOutputType}; + +use crate::audio_converter::{self as ac, AudioBufferList, AudioStreamBasicDescription}; +use crate::pcm_pool::{PcmFramePool, PooledPcmFrame}; +use crate::source_state::Machine; + +const MAX_CALLBACK_INPUT_FRAMES: u32 = 48_000; +const MIN_INPUT_SAMPLE_RATE: f64 = 8_000.0; +const MAX_ABL_BUFFERS: usize = 32; + +pub type PcmCallback = + unsafe extern "C" fn(ctx: *mut c_void, slot: *mut Option, frames: u32); +pub type StopCallback = unsafe extern "C" fn(ctx: *mut c_void, err: *mut NSError); + +pub struct SourceOptions { + pub target_sample_rate: f64, + pub target_channels: u32, +} + +impl Default for SourceOptions { + fn default() -> Self { + Self { + target_sample_rate: 48_000.0, + target_channels: 2, + } + } +} + +const EMPTY_AUDIO_BUFFER: ac::AudioBuffer = ac::AudioBuffer { + m_number_channels: 0, + m_data_byte_size: 0, + m_data: ptr::null_mut(), +}; + +#[repr(C, align(16))] +struct AblScratch { + n_buffers: u32, + _pad: u32, + buffers: [ac::AudioBuffer; MAX_ABL_BUFFERS], +} + +impl AblScratch { + const fn empty() -> Self { + Self { + n_buffers: 0, + _pad: 0, + buffers: [EMPTY_AUDIO_BUFFER; MAX_ABL_BUFFERS], + } + } +} + +const _: () = { + assert!(core::mem::align_of::() == 16); + assert!( + core::mem::size_of::() + >= core::mem::size_of::() + + MAX_ABL_BUFFERS * core::mem::size_of::() + ); + assert!(core::mem::offset_of!(AblScratch, buffers) == 8); + assert!(core::mem::size_of::() >= core::mem::size_of::()); +}; + +pub struct Source { + pub delegate: Retained, + pub state: Machine, + pub target_sample_rate: f64, + pub target_channels: u32, + scratch: Mutex, + callbacks: Mutex, + pub dropped_buffers: AtomicU64, + pub output_queue: dispatch2::DispatchRetained, +} + +struct SourceScratch { + abl: AblScratch, +} + +struct Callbacks { + pcm_callback: Option, + pcm_callback_ctx: *mut c_void, + stop_callback: Option, + stop_callback_ctx: *mut c_void, + pcm_pool: Option, +} + +unsafe impl Send for Source {} +unsafe impl Sync for Source {} + +#[derive(Debug)] +pub enum SourceCreateError { + DispatchQueue, +} + +#[derive(Debug)] +pub struct ClassRegistrationError; + +pub struct DelegateIvars { + pub source: AtomicPtr, +} + +define_class!( + #[unsafe(super(NSObject))] + #[name = "FluxerSCKAudioSource"] + #[ivars = DelegateIvars] + pub struct FluxerSCKAudioSource; + + unsafe impl NSObjectProtocol for FluxerSCKAudioSource {} + + unsafe impl SCStreamDelegate for FluxerSCKAudioSource { + #[unsafe(method(stream:didStopWithError:))] + unsafe fn did_stop_with_error(&self, _stream: &SCStream, err: &NSError) { + let ptr = self.ivars().source.load(Ordering::Acquire); + if ptr.is_null() { + return; + } + let src = unsafe { &*ptr }; + let _ = src.state.request_stop(); + let _ = src.state.mark_stopped(); + let (cb, ctx) = { + let callbacks = src.lock_callbacks(); + (callbacks.stop_callback, callbacks.stop_callback_ctx) + }; + if let Some(cb) = cb { + unsafe { + cb(ctx, err as *const NSError as *mut NSError); + } + } + } + } + + unsafe impl SCStreamOutput for FluxerSCKAudioSource { + #[unsafe(method(stream:didOutputSampleBuffer:ofType:))] + unsafe fn did_output_sample_buffer( + &self, + _stream: &SCStream, + sample_buffer: &CMSampleBuffer, + output_type: SCStreamOutputType, + ) { + if output_type != SCStreamOutputType::Audio { + return; + } + let ptr = self.ivars().source.load(Ordering::Acquire); + if ptr.is_null() { + return; + } + unsafe { + handle_audio_sample(&*ptr, sample_buffer); + } + } + } +); + +impl FluxerSCKAudioSource { + pub fn new() -> Retained { + let this = Self::alloc().set_ivars(DelegateIvars { + source: AtomicPtr::new(core::ptr::null_mut()), + }); + unsafe { msg_send![super(this), init] } + } +} + +struct RetainedBlockGuard(*mut objc2_core_media::CMBlockBuffer); + +impl Drop for RetainedBlockGuard { + fn drop(&mut self) { + if !self.0.is_null() { + unsafe { + let _ = objc2_core_foundation::CFRetained::from_raw( + core::ptr::NonNull::new_unchecked(self.0), + ); + } + self.0 = ptr::null_mut(); + } + } +} + +enum AblFillOutcome { + Filled(RetainedBlockGuard), + TooLarge, + Unavailable, +} + +fn read_stream_asbd(sample_buffer: &CMSampleBuffer) -> Option { + let desc = unsafe { sample_buffer.format_description() }?; + unsafe extern "C-unwind" { + fn CMAudioFormatDescriptionGetStreamBasicDescription( + desc: &objc2_core_media::CMFormatDescription, + ) -> *const AudioStreamBasicDescription; + } + let asbd_ptr = unsafe { CMAudioFormatDescriptionGetStreamBasicDescription(&desc) }; + if asbd_ptr.is_null() { + return None; + } + Some(unsafe { *asbd_ptr }) +} + +unsafe fn admit_sample( + src: &Source, + sample_buffer: &CMSampleBuffer, +) -> Option<(u32, AudioStreamBasicDescription)> { + if !unsafe { sample_buffer.data_is_ready() } { + return None; + } + let num_samples = unsafe { sample_buffer.num_samples() }; + if num_samples <= 0 || (num_samples as u32) > MAX_CALLBACK_INPUT_FRAMES { + return None; + } + let asbd = read_stream_asbd(sample_buffer)?; + if !asbd.m_sample_rate.is_finite() { + src.dropped_buffers.fetch_add(1, Ordering::Relaxed); + return None; + } + if asbd.m_sample_rate < MIN_INPUT_SAMPLE_RATE { + src.dropped_buffers.fetch_add(1, Ordering::Relaxed); + return None; + } + Some((num_samples as u32, asbd)) +} + +unsafe fn fill_abl_scratch(sample_buffer: &CMSampleBuffer, abl: &mut AblScratch) -> AblFillOutcome { + assert!(core::mem::size_of::() >= core::mem::size_of::()); + let mut abl_size: usize = 0; + let size_status = unsafe { + sample_buffer.audio_buffer_list_with_retained_block_buffer( + &mut abl_size, + ptr::null_mut(), + 0, + None::<&CFAllocator>, + None::<&CFAllocator>, + 0, + ptr::null_mut(), + ) + }; + if size_status != 0 || abl_size < core::mem::size_of::() { + return AblFillOutcome::Unavailable; + } + if abl_size > core::mem::size_of::() { + return AblFillOutcome::TooLarge; + } + let abl_ptr = (abl as *mut AblScratch) as *mut objc2_core_audio_types::AudioBufferList; + let mut retained_block: *mut objc2_core_media::CMBlockBuffer = ptr::null_mut(); + let list_status = unsafe { + sample_buffer.audio_buffer_list_with_retained_block_buffer( + ptr::null_mut(), + abl_ptr, + core::mem::size_of::(), + None::<&CFAllocator>, + None::<&CFAllocator>, + 0, + &mut retained_block, + ) + }; + let guard = RetainedBlockGuard(retained_block); + if list_status != 0 { + return AblFillOutcome::Unavailable; + } + if abl.n_buffers as usize > MAX_ABL_BUFFERS { + return AblFillOutcome::Unavailable; + } + AblFillOutcome::Filled(guard) +} + +unsafe fn handle_audio_sample(src: &Source, sample_buffer: &CMSampleBuffer) { + let Some((frames, asbd)) = (unsafe { admit_sample(src, sample_buffer) }) else { + return; + }; + let (cb, ctx, pool) = { + let callbacks = src.lock_callbacks(); + ( + callbacks.pcm_callback, + callbacks.pcm_callback_ctx, + callbacks.pcm_pool.clone(), + ) + }; + let Some(cb) = cb else { return }; + let Some(pool) = pool else { return }; + let out_capacity = + ac::output_frame_capacity(frames, asbd.m_sample_rate, src.target_sample_rate); + let needed = (out_capacity as usize) * (src.target_channels as usize); + if needed > pool.samples_per_slot() as usize { + src.dropped_buffers.fetch_add(1, Ordering::Relaxed); + return; + } + let mut scratch = match src.scratch.try_lock() { + Ok(guard) => guard, + Err(_) => { + src.dropped_buffers.fetch_add(1, Ordering::Relaxed); + return; + } + }; + let block_guard = match unsafe { fill_abl_scratch(sample_buffer, &mut scratch.abl) } { + AblFillOutcome::Filled(guard) => guard, + AblFillOutcome::TooLarge => { + src.dropped_buffers.fetch_add(1, Ordering::Relaxed); + return; + } + AblFillOutcome::Unavailable => return, + }; + let Some(mut slot) = pool.try_acquire() else { + src.dropped_buffers.fetch_add(1, Ordering::Relaxed); + return; + }; + let out_frames = unsafe { + ac::convert_buffer_list_to_interleaved_f32( + &asbd, + (&raw const scratch.abl) as *const AudioBufferList, + frames, + src.target_sample_rate, + src.target_channels, + slot.unfilled_mut(), + ) + }; + drop(block_guard); + drop(scratch); + let frames_emitted = match out_frames { + Ok(0) | Err(_) => return, + Ok(n) => n, + }; + let filled = (frames_emitted as usize) * (src.target_channels as usize); + assert!(filled <= slot.capacity()); + slot.set_filled_len(filled); + let mut handoff = Some(slot); + unsafe { + cb( + ctx, + &mut handoff as *mut Option, + frames_emitted, + ); + } +} + +impl Source { + pub fn create(opts: SourceOptions) -> Result, SourceCreateError> { + assert!(opts.target_sample_rate > 0.0); + assert!(opts.target_channels > 0); + let delegate = FluxerSCKAudioSource::new(); + + let queue_attr = dispatch2::DispatchQueueAttr::with_qos_class( + dispatch2::DispatchQueueAttr::SERIAL, + dispatch2::DispatchQoS::UserInteractive, + 0, + ); + let queue = + dispatch2::DispatchQueue::new("app.fluxer.mac-app-audio.sck", Some(&queue_attr)); + + let mut src = Box::new(Source { + delegate, + state: Machine::new(), + target_sample_rate: opts.target_sample_rate, + target_channels: opts.target_channels, + scratch: Mutex::new(SourceScratch { + abl: AblScratch::empty(), + }), + callbacks: Mutex::new(Callbacks { + pcm_callback: None, + pcm_callback_ctx: ptr::null_mut(), + stop_callback: None, + stop_callback_ctx: ptr::null_mut(), + pcm_pool: None, + }), + dropped_buffers: AtomicU64::new(0), + output_queue: queue, + }); + + let raw: *mut Source = &mut *src; + src.delegate.ivars().source.store(raw, Ordering::Release); + Ok(src) + } + + fn lock_callbacks(&self) -> MutexGuard<'_, Callbacks> { + self.callbacks + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()) + } + + pub fn set_pcm_callback(&mut self, cb: PcmCallback, ctx: *mut c_void) { + let mut callbacks = self.lock_callbacks(); + callbacks.pcm_callback = Some(cb); + callbacks.pcm_callback_ctx = ctx; + } + + pub fn set_pcm_pool(&mut self, pool: PcmFramePool) { + assert!(pool.capacity() > 0); + assert!(pool.samples_per_slot() > 0); + let mut callbacks = self.lock_callbacks(); + callbacks.pcm_pool = Some(pool); + } + + pub fn set_stop_callback(&mut self, cb: StopCallback, ctx: *mut c_void) { + let mut callbacks = self.lock_callbacks(); + callbacks.stop_callback = Some(cb); + callbacks.stop_callback_ctx = ctx; + } + + pub fn clear_stop_callback(&mut self) { + let mut callbacks = self.lock_callbacks(); + callbacks.stop_callback = None; + callbacks.stop_callback_ctx = ptr::null_mut(); + } + + pub fn delegate_as_output(&self) -> &ProtocolObject { + ProtocolObject::from_ref(&*self.delegate) + } + + pub fn delegate_as_delegate(&self) -> &ProtocolObject { + ProtocolObject::from_ref(&*self.delegate) + } + + pub fn output_queue(&self) -> &dispatch2::DispatchQueue { + &self.output_queue + } +} + +impl Drop for Source { + fn drop(&mut self) { + self.delegate + .ivars() + .source + .store(ptr::null_mut(), Ordering::Release); + } +} + +#[cfg(test)] +mod abl_scratch_tests { + use super::*; + + #[test] + fn abl_scratch_layout_matches_audio_buffer_list() { + assert_eq!(16, core::mem::align_of::()); + assert_eq!(8, core::mem::offset_of!(AblScratch, buffers)); + assert!( + core::mem::size_of::() + >= core::mem::size_of::() + + MAX_ABL_BUFFERS * core::mem::size_of::() + ); + } + + #[test] + fn abl_scratch_empty_has_no_buffers() { + let scratch = AblScratch::empty(); + assert_eq!(0, scratch.n_buffers); + assert!(scratch.buffers[0].m_data.is_null()); + assert_eq!(0, scratch.buffers[MAX_ABL_BUFFERS - 1].m_data_byte_size); + } +} diff --git a/fluxer_desktop/native/mac-app-audio/src/coreaudio_tap.rs b/fluxer_desktop/native/mac-app-audio/src/coreaudio_tap.rs new file mode 100644 index 0000000..c89bcb7 --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/src/coreaudio_tap.rs @@ -0,0 +1,953 @@ +#![allow(non_camel_case_types, non_upper_case_globals)] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +use core::ffi::{CStr, c_void}; +use core::ptr; +use core::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering}; +use std::sync::Arc; +use std::thread::{self, JoinHandle}; +use std::time::Duration; + +use objc2::AllocAnyThread; +use objc2::rc::Retained; +use objc2_core_audio::{ + AudioDeviceCreateIOProcID, AudioDeviceDestroyIOProcID, AudioDeviceIOProcID, AudioDeviceStart, + AudioDeviceStop, AudioHardwareCreateAggregateDevice, AudioHardwareCreateProcessTap, + AudioHardwareDestroyAggregateDevice, AudioHardwareDestroyProcessTap, + AudioObjectGetPropertyData, AudioObjectID, AudioObjectPropertyAddress, + AudioObjectSetPropertyData, CATapDescription, CATapMuteBehavior, + kAudioHardwarePropertyTranslatePIDToProcessObject, kAudioObjectPropertyElementMain, + kAudioObjectPropertyScopeGlobal, kAudioObjectSystemObject, kAudioObjectUnknown, + kAudioTapPropertyDescription, kAudioTapPropertyFormat, kAudioTapPropertyUID, +}; +use objc2_core_foundation::CFDictionary; +use objc2_foundation::{ + NSArray, NSBundle, NSMutableArray, NSMutableDictionary, NSNumber, NSObject, NSString, NSUUID, +}; + +use crate::audio_converter::{self as ac, AudioBufferList, AudioStreamBasicDescription}; +use crate::foundation; +use crate::pcm_pool::{PcmFramePool, PooledPcmFrame}; +use crate::process_tree; + +pub type OSStatus = i32; + +const NO_ERR: OSStatus = 0; +const K_AGGREGATE_DRIFT_COMPENSATION_MEDIUM_QUALITY: u32 = 0x40; + +const MAX_RELATED_PROCESSES: usize = 512; +const MAX_CALLBACK_INPUT_FRAMES: u32 = 48_000; +const TARGET_SAMPLE_RATE: f64 = 48_000.0; +const TARGET_CHANNELS: u32 = 2; + +const LATE_SPAWN_REFRESH_INTERVAL: Duration = Duration::from_secs(2); + +const HELPER_BUNDLE_SUFFIXES: &[&str] = &[ + ".helper", + ".helper.Renderer", + ".helper.GPU", + ".helper.Plugin", +]; + +static AGGREGATE_UID_COUNTER: AtomicU64 = AtomicU64::new(1); +static DEBUG_COREAUDIO: AtomicBool = AtomicBool::new(false); +static DEBUG_CALLBACK_COUNT: AtomicU32 = AtomicU32::new(0); + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum CaptureScope { + Process, + System, +} + +#[derive(Debug)] +pub enum CreateError { + Unsupported, + NoRelatedProcesses, + NoProcessObjects, + FoundationObjectFailed, + CreateProcessTapFailed, + ReadTapUidFailed, + ReadTapFormatFailed, + CreateAggregateDeviceFailed, + CreateIOProcFailed, + StartDeviceFailed, +} + +pub type PcmCallback = + unsafe extern "C" fn(ctx: *mut c_void, slot: *mut Option, frames: u32); + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct TapDiagnostics { + pub convert_failures: u64, + pub dropped_buffers: u64, +} + +pub struct Capture { + pub tap_id: AudioObjectID, + pub aggregate_device_id: AudioObjectID, + pub io_proc_id: AudioDeviceIOProcID, + pub input_format: AudioStreamBasicDescription, + pub running: AtomicBool, + pub pcm_callback: Option, + pub pcm_callback_ctx: *mut c_void, + pub pcm_pool: Option, + pub convert_failures: AtomicU64, + pub dropped_buffers: AtomicU64, + + refresher: Option, +} + +unsafe impl Send for Capture {} +unsafe impl Sync for Capture {} + +struct RefresherHandle { + alive: Arc, + thread: Option>, +} + +impl RefresherHandle { + fn shutdown(&mut self) { + self.alive.store(false, Ordering::Release); + if let Some(handle) = self.thread.take() { + let _ = handle.join(); + } + } +} + +impl Drop for RefresherHandle { + fn drop(&mut self) { + self.shutdown(); + } +} + +pub fn is_supported() -> bool { + use objc2::runtime::AnyClass; + AnyClass::get(c"CATapDescription").is_some() +} + +fn getenv_set(name: &CStr) -> bool { + unsafe { !libc::getenv(name.as_ptr()).is_null() } +} + +fn collect_target_pids(pid: i32, include_process_tree: bool) -> Vec { + if !include_process_tree { + return vec![pid]; + } + process_tree::collect_related_pids(pid, MAX_RELATED_PROCESSES) +} + +fn collect_process_objects(pids: &[i32], skip_current_process: bool) -> Vec { + let self_pid = unsafe { libc::getpid() }; + let mut out = Vec::with_capacity(pids.len()); + for &pid in pids { + if pid <= 0 { + continue; + } + if skip_current_process && pid == self_pid { + continue; + } + let object = translate_pid_to_process_object(pid); + if object == kAudioObjectUnknown { + continue; + } + if out.contains(&object) { + continue; + } + out.push(object); + } + out +} + +fn translate_pid_to_process_object(pid: i32) -> AudioObjectID { + let mut out: AudioObjectID = kAudioObjectUnknown; + let mut size: u32 = core::mem::size_of::() as u32; + let mut qualifier_pid = pid; + let address = AudioObjectPropertyAddress { + mSelector: kAudioHardwarePropertyTranslatePIDToProcessObject, + mScope: kAudioObjectPropertyScopeGlobal, + mElement: kAudioObjectPropertyElementMain, + }; + let status = unsafe { + AudioObjectGetPropertyData( + kAudioObjectSystemObject as AudioObjectID, + ptr::NonNull::from(&address), + core::mem::size_of::() as u32, + (&raw mut qualifier_pid) as *const _ as *const c_void, + ptr::NonNull::from(&mut size), + ptr::NonNull::from(&mut out).cast::(), + ) + }; + if status != NO_ERR { + return kAudioObjectUnknown; + } + out +} + +fn build_process_array(objects: &[AudioObjectID]) -> Retained> { + let nums: Vec> = objects.iter().map(|o| NSNumber::new_u32(*o)).collect(); + let refs: Vec<&NSNumber> = nums.iter().map(|n| n.as_ref()).collect(); + NSArray::from_slice(&refs) +} + +fn create_tap_description( + process_objects: &[AudioObjectID], + pid: i32, + scope: CaptureScope, +) -> Result, CreateError> { + let process_array = build_process_array(process_objects); + + let alloc = CATapDescription::alloc(); + let description = match scope { + CaptureScope::Process => unsafe { + CATapDescription::initStereoMixdownOfProcesses(alloc, &process_array) + }, + CaptureScope::System => unsafe { + CATapDescription::initStereoGlobalTapButExcludeProcesses(alloc, &process_array) + }, + }; + + let excludes_by_bundle_id = if scope == CaptureScope::System { + apply_main_bundle_id_excludes(&description) + } else { + false + }; + if scope == CaptureScope::Process && process_objects.is_empty() { + return Err(CreateError::NoProcessObjects); + } + if scope == CaptureScope::System && process_objects.is_empty() && !excludes_by_bundle_id { + return Err(CreateError::NoProcessObjects); + } + + let name = match scope { + CaptureScope::Process => format!("Fluxer app audio tap pid {pid}"), + CaptureScope::System => format!("Fluxer desktop audio tap excluding pid {pid}"), + }; + unsafe { + description.setUUID(&NSUUID::UUID()); + description.setName(&NSString::from_str(&name)); + description.setPrivate(true); + description.setExclusive(scope == CaptureScope::System); + description.setMuteBehavior(CATapMuteBehavior(0)); + } + Ok(description) +} + +fn apply_main_bundle_id_excludes(description: &CATapDescription) -> bool { + use objc2::runtime::NSObjectProtocol; + use objc2::sel; + let obj: &NSObject = description.as_ref(); + if !obj.respondsToSelector(sel!(setBundleIDs:)) { + return false; + } + let bundle_array = match build_main_bundle_id_array() { + Some(a) => a, + None => return false, + }; + if bundle_array.count() == 0 { + return false; + } + unsafe { + description.setBundleIDs(&bundle_array); + if obj.respondsToSelector(sel!(setProcessRestoreEnabled:)) { + description.setProcessRestoreEnabled(true); + } + } + true +} + +fn build_main_bundle_id_array() -> Option>> { + let base = copy_main_bundle_identifier()?; + let base_str = base.to_string(); + if base_str.is_empty() || base_str.len() > 192 { + return None; + } + let mut entries: Vec> = Vec::with_capacity(1 + HELPER_BUNDLE_SUFFIXES.len()); + entries.push(base); + for suffix in HELPER_BUNDLE_SUFFIXES { + let helper = format!("{base_str}{suffix}"); + entries.push(NSString::from_str(&helper)); + } + let refs: Vec<&NSString> = entries.iter().map(|s| s.as_ref()).collect(); + Some(NSArray::from_slice(&refs)) +} + +fn copy_main_bundle_identifier() -> Option> { + let bundle = NSBundle::mainBundle(); + bundle.bundleIdentifier() +} + +fn copy_tap_uid(tap_id: AudioObjectID) -> Result, CreateError> { + let mut tap_uid: *const NSString = ptr::null(); + let mut size: u32 = core::mem::size_of::<*const NSString>() as u32; + let address = AudioObjectPropertyAddress { + mSelector: kAudioTapPropertyUID, + mScope: kAudioObjectPropertyScopeGlobal, + mElement: kAudioObjectPropertyElementMain, + }; + let status = unsafe { + AudioObjectGetPropertyData( + tap_id, + ptr::NonNull::from(&address), + 0, + ptr::null(), + ptr::NonNull::from(&mut size), + ptr::NonNull::from(&mut tap_uid).cast::(), + ) + }; + if status != NO_ERR || tap_uid.is_null() { + return Err(CreateError::ReadTapUidFailed); + } + + unsafe { Retained::from_raw(tap_uid as *mut NSString).ok_or(CreateError::ReadTapUidFailed) } +} + +fn read_tap_format(tap_id: AudioObjectID) -> Result { + let mut format: AudioStreamBasicDescription = unsafe { core::mem::zeroed() }; + let mut size: u32 = core::mem::size_of::() as u32; + let address = AudioObjectPropertyAddress { + mSelector: kAudioTapPropertyFormat, + mScope: kAudioObjectPropertyScopeGlobal, + mElement: kAudioObjectPropertyElementMain, + }; + let status = unsafe { + AudioObjectGetPropertyData( + tap_id, + ptr::NonNull::from(&address), + 0, + ptr::null(), + ptr::NonNull::from(&mut size), + ptr::NonNull::from(&mut format).cast::(), + ) + }; + if status != NO_ERR { + return Err(CreateError::ReadTapFormatFailed); + } + Ok(format) +} + +fn create_aggregate_description( + tap_uid: &NSString, + pid: i32, +) -> Result>, CreateError> { + let tap_dict: Retained> = NSMutableDictionary::new(); + foundation::dict_set_str_key(&tap_dict, c"uid", tap_uid.as_ref()); + let drift = NSNumber::new_bool(true); + foundation::dict_set_str_key(&tap_dict, c"drift", drift.as_ref()); + let drift_quality = NSNumber::new_u32(K_AGGREGATE_DRIFT_COMPENSATION_MEDIUM_QUALITY); + foundation::dict_set_str_key(&tap_dict, c"drift quality", drift_quality.as_ref()); + + let dict_obj: &NSObject = tap_dict.as_ref(); + let tap_list: Retained> = NSMutableArray::arrayWithCapacity(1); + tap_list.addObject(dict_obj); + + let aggregate_dict: Retained> = + NSMutableDictionary::new(); + let counter = AGGREGATE_UID_COUNTER.fetch_add(1, Ordering::Relaxed); + let uid_string = format!("app.fluxer.mac-app-audio.tap.{pid}.{counter}"); + let uid = NSString::from_str(&uid_string); + let name = NSString::from_str("Fluxer app audio capture"); + foundation::dict_set_str_key(&aggregate_dict, c"uid", uid.as_ref()); + foundation::dict_set_str_key(&aggregate_dict, c"name", name.as_ref()); + let priv_n = NSNumber::new_bool(true); + foundation::dict_set_str_key(&aggregate_dict, c"private", priv_n.as_ref()); + foundation::dict_set_str_key(&aggregate_dict, c"taps", tap_list.as_ref()); + let auto = NSNumber::new_bool(true); + foundation::dict_set_str_key(&aggregate_dict, c"tapautostart", auto.as_ref()); + Ok(aggregate_dict) +} + +unsafe extern "C-unwind" fn io_proc( + _in_device: AudioObjectID, + _in_now: core::ptr::NonNull, + in_input_data: core::ptr::NonNull, + _in_input_time: core::ptr::NonNull, + _out_output_data: core::ptr::NonNull, + _in_output_time: core::ptr::NonNull, + client_data: *mut c_void, +) -> OSStatus { + if client_data.is_null() { + return NO_ERR; + } + let self_ptr = client_data as *mut Capture; + let capture = unsafe { &*self_ptr }; + if !capture.running.load(Ordering::Acquire) { + return NO_ERR; + } + let local_abl = in_input_data.as_ptr() as *const AudioBufferList; + let frames = + match unsafe { ac::input_frame_count_for_buffer_list(&capture.input_format, local_abl) } { + Ok(f) => f, + Err(_) => { + capture.convert_failures.fetch_add(1, Ordering::Relaxed); + return NO_ERR; + } + }; + if frames == 0 { + return NO_ERR; + } + if frames > MAX_CALLBACK_INPUT_FRAMES { + capture.dropped_buffers.fetch_add(1, Ordering::Relaxed); + return NO_ERR; + } + unsafe { + convert_and_deliver(capture, local_abl, frames); + } + NO_ERR +} + +unsafe fn convert_and_deliver(capture: &Capture, local_abl: *const AudioBufferList, frames: u32) { + assert!(frames > 0); + assert!(frames <= MAX_CALLBACK_INPUT_FRAMES); + let Some(cb) = capture.pcm_callback else { + return; + }; + let Some(pool) = capture.pcm_pool.as_ref() else { + return; + }; + let needed = (ac::output_frame_capacity( + frames, + capture.input_format.m_sample_rate, + TARGET_SAMPLE_RATE, + ) as usize) + * (TARGET_CHANNELS as usize); + if needed > pool.samples_per_slot() as usize { + capture.dropped_buffers.fetch_add(1, Ordering::Relaxed); + return; + } + let Some(mut slot) = pool.try_acquire() else { + capture.dropped_buffers.fetch_add(1, Ordering::Relaxed); + return; + }; + let out_frames = unsafe { + ac::convert_buffer_list_to_interleaved_f32( + &capture.input_format, + local_abl, + frames, + TARGET_SAMPLE_RATE, + TARGET_CHANNELS, + slot.unfilled_mut(), + ) + }; + let n = match out_frames { + Ok(0) => { + capture.dropped_buffers.fetch_add(1, Ordering::Relaxed); + return; + } + Err(_) => { + capture.convert_failures.fetch_add(1, Ordering::Relaxed); + return; + } + Ok(n) => n, + }; + let filled = (n as usize) * (TARGET_CHANNELS as usize); + assert!(filled <= slot.capacity()); + slot.set_filled_len(filled); + let mut handoff = Some(slot); + unsafe { + cb( + capture.pcm_callback_ctx, + &mut handoff as *mut Option, + n, + ); + } +} + +impl Capture { + pub fn create( + pid: i32, + include_process_tree: bool, + scope: CaptureScope, + ) -> Result, CreateError> { + if !is_supported() { + return Err(CreateError::Unsupported); + } + DEBUG_COREAUDIO.store( + getenv_set(c"FLUXER_MAC_APP_AUDIO_DEBUG_COREAUDIO"), + Ordering::Release, + ); + DEBUG_CALLBACK_COUNT.store(0, Ordering::Release); + + let root_pid = if scope == CaptureScope::System { + unsafe { libc::getpid() } + } else { + pid + }; + let related_pids = collect_target_pids(root_pid, include_process_tree); + if related_pids.is_empty() { + return Err(CreateError::NoRelatedProcesses); + } + let process_objects = + collect_process_objects(&related_pids, scope == CaptureScope::Process); + + let description = create_tap_description(&process_objects, root_pid, scope)?; + let mut tap_id: AudioObjectID = kAudioObjectUnknown; + let tap_status = unsafe { AudioHardwareCreateProcessTap(Some(&description), &mut tap_id) }; + + if tap_status != NO_ERR || tap_id == kAudioObjectUnknown { + return Err(CreateError::CreateProcessTapFailed); + } + + let tap_uid = match copy_tap_uid(tap_id) { + Ok(u) => u, + Err(e) => { + unsafe { + let _ = AudioHardwareDestroyProcessTap(tap_id); + } + return Err(e); + } + }; + let input_format = match read_tap_format(tap_id) { + Ok(f) => f, + Err(e) => { + unsafe { + let _ = AudioHardwareDestroyProcessTap(tap_id); + } + return Err(e); + } + }; + let aggregate_description = match create_aggregate_description(&tap_uid, pid) { + Ok(d) => d, + Err(e) => { + unsafe { + let _ = AudioHardwareDestroyProcessTap(tap_id); + } + return Err(e); + } + }; + let mut aggregate_device_id: AudioObjectID = kAudioObjectUnknown; + let aggregate_status = unsafe { + let dict_ref: &CFDictionary = + &*(&*aggregate_description as *const _ as *const CFDictionary); + AudioHardwareCreateAggregateDevice( + dict_ref, + ptr::NonNull::from(&mut aggregate_device_id), + ) + }; + if aggregate_status != NO_ERR || aggregate_device_id == kAudioObjectUnknown { + unsafe { + let _ = AudioHardwareDestroyProcessTap(tap_id); + } + return Err(CreateError::CreateAggregateDeviceFailed); + } + + let mut capture = Box::new(Capture { + tap_id, + aggregate_device_id, + io_proc_id: None, + input_format, + running: AtomicBool::new(false), + pcm_callback: None, + pcm_callback_ctx: ptr::null_mut(), + pcm_pool: None, + convert_failures: AtomicU64::new(0), + dropped_buffers: AtomicU64::new(0), + refresher: None, + }); + let mut io_proc_id: AudioDeviceIOProcID = None; + let io_status = unsafe { + AudioDeviceCreateIOProcID( + aggregate_device_id, + Some(io_proc), + &mut *capture as *mut Capture as *mut c_void, + ptr::NonNull::from(&mut io_proc_id), + ) + }; + if io_status != NO_ERR || io_proc_id.is_none() { + unsafe { + let _ = AudioHardwareDestroyAggregateDevice(aggregate_device_id); + let _ = AudioHardwareDestroyProcessTap(tap_id); + } + return Err(CreateError::CreateIOProcFailed); + } + capture.io_proc_id = io_proc_id; + + if scope == CaptureScope::Process && include_process_tree { + capture.refresher = + spawn_late_spawn_refresher(tap_id, root_pid, scope, process_objects.to_vec()); + } + Ok(capture) + } + + pub fn set_pcm_callback(&mut self, cb: PcmCallback, ctx: *mut c_void) { + self.pcm_callback = Some(cb); + self.pcm_callback_ctx = ctx; + } + + pub fn set_pcm_pool(&mut self, pool: PcmFramePool) { + assert!(pool.capacity() > 0); + assert!(pool.samples_per_slot() > 0); + self.pcm_pool = Some(pool); + } + + pub fn diagnostics(&self) -> TapDiagnostics { + let diagnostics = TapDiagnostics { + convert_failures: self.convert_failures.load(Ordering::Relaxed), + dropped_buffers: self.dropped_buffers.load(Ordering::Relaxed), + }; + assert!(diagnostics.convert_failures <= u64::MAX / 2); + assert!(diagnostics.dropped_buffers <= u64::MAX / 2); + diagnostics + } + + pub fn start(&mut self) -> Result<(), CreateError> { + if self.io_proc_id.is_none() || self.aggregate_device_id == kAudioObjectUnknown { + return Err(CreateError::StartDeviceFailed); + } + self.running.store(true, Ordering::Release); + let status = unsafe { AudioDeviceStart(self.aggregate_device_id, self.io_proc_id) }; + if status != NO_ERR { + self.running.store(false, Ordering::Release); + return Err(CreateError::StartDeviceFailed); + } + Ok(()) + } + + pub fn stop(&mut self) { + self.running.store(false, Ordering::Release); + if self.aggregate_device_id != kAudioObjectUnknown && self.io_proc_id.is_some() { + unsafe { + let _ = AudioDeviceStop(self.aggregate_device_id, self.io_proc_id); + let _ = AudioDeviceDestroyIOProcID(self.aggregate_device_id, self.io_proc_id); + } + self.io_proc_id = None; + } + } +} + +impl Drop for Capture { + fn drop(&mut self) { + if let Some(mut r) = self.refresher.take() { + r.shutdown(); + } + self.stop(); + if self.aggregate_device_id != kAudioObjectUnknown { + unsafe { + let _ = AudioHardwareDestroyAggregateDevice(self.aggregate_device_id); + } + self.aggregate_device_id = kAudioObjectUnknown; + } + if self.tap_id != kAudioObjectUnknown { + unsafe { + let _ = AudioHardwareDestroyProcessTap(self.tap_id); + } + self.tap_id = kAudioObjectUnknown; + } + } +} + +fn apply_process_objects_to_tap( + tap_id: AudioObjectID, + objects: &[AudioObjectID], + root_pid: i32, + scope: CaptureScope, +) -> OSStatus { + if tap_id == kAudioObjectUnknown { + return -1; + } + let description = match create_tap_description(objects, root_pid, scope) { + Ok(d) => d, + Err(_) => return -1, + }; + let address = AudioObjectPropertyAddress { + mSelector: kAudioTapPropertyDescription, + mScope: kAudioObjectPropertyScopeGlobal, + mElement: kAudioObjectPropertyElementMain, + }; + + let desc_ptr: *const CATapDescription = &*description; + let mut desc_holder: *const CATapDescription = desc_ptr; + unsafe { + AudioObjectSetPropertyData( + tap_id, + ptr::NonNull::from(&address), + 0, + ptr::null(), + core::mem::size_of::<*const CATapDescription>() as u32, + ptr::NonNull::from(&mut desc_holder).cast::(), + ) + } +} + +fn spawn_late_spawn_refresher( + tap_id: AudioObjectID, + root_pid: i32, + scope: CaptureScope, + initial_objects: Vec, +) -> Option { + let alive = Arc::new(AtomicBool::new(true)); + let alive_for_thread = alive.clone(); + let thread = thread::Builder::new() + .name("fluxer-mac-tap-refresh".into()) + .spawn(move || { + run_late_spawn_refresher(tap_id, root_pid, scope, initial_objects, alive_for_thread); + }) + .ok()?; + Some(RefresherHandle { + alive, + thread: Some(thread), + }) +} + +fn run_late_spawn_refresher( + tap_id: AudioObjectID, + root_pid: i32, + scope: CaptureScope, + initial_objects: Vec, + alive: Arc, +) { + let mut previous: Vec = initial_objects; + previous.sort_unstable(); + previous.dedup(); + + while alive.load(Ordering::Acquire) { + thread::sleep(LATE_SPAWN_REFRESH_INTERVAL); + if !alive.load(Ordering::Acquire) { + break; + } + let related = process_tree::collect_related_pids(root_pid, MAX_RELATED_PROCESSES); + if related.is_empty() { + continue; + } + let mut current = collect_process_objects(&related, scope == CaptureScope::Process); + current.sort_unstable(); + current.dedup(); + if current == previous { + continue; + } + + if current.is_empty() { + continue; + } + let status = apply_process_objects_to_tap(tap_id, ¤t, root_pid, scope); + if status == NO_ERR { + previous = current; + } + } +} + +pub fn coreaudio_error_message(e: &CreateError) -> &'static str { + match e { + CreateError::Unsupported => "CoreAudio process taps unavailable", + CreateError::NoRelatedProcesses => "No related process for selected app", + CreateError::NoProcessObjects => "No CoreAudio process object for selected app", + CreateError::FoundationObjectFailed => "CoreAudio tap configuration failed", + CreateError::CreateProcessTapFailed => "CoreAudio process tap creation failed", + CreateError::ReadTapUidFailed => "CoreAudio process tap UID lookup failed", + CreateError::ReadTapFormatFailed => "CoreAudio process tap format lookup failed", + CreateError::CreateAggregateDeviceFailed => { + "CoreAudio process tap aggregate device creation failed" + } + CreateError::CreateIOProcFailed => "CoreAudio process tap IOProc creation failed", + CreateError::StartDeviceFailed => "CoreAudio process tap start failed", + } +} + +#[cfg(test)] +mod io_proc_diagnostics_tests { + use super::*; + use crate::audio_converter::AudioBuffer; + use core::mem::size_of; + use core::ptr::NonNull; + use core::sync::atomic::AtomicU32 as TestAtomicU32; + use objc2_core_audio_types::AudioTimeStamp; + + fn make_idle_capture(input_format: AudioStreamBasicDescription) -> Box { + Box::new(Capture { + tap_id: kAudioObjectUnknown, + aggregate_device_id: kAudioObjectUnknown, + io_proc_id: None, + input_format, + running: AtomicBool::new(true), + pcm_callback: None, + pcm_callback_ctx: ptr::null_mut(), + pcm_pool: None, + convert_failures: AtomicU64::new(0), + dropped_buffers: AtomicU64::new(0), + refresher: None, + }) + } + + fn make_test_pool() -> PcmFramePool { + PcmFramePool::new(2, 8_192).expect("test pool builds") + } + + fn call_io_proc(capture: &Capture, abl: &mut AudioBufferList) -> OSStatus { + let mut ts_now: AudioTimeStamp = unsafe { core::mem::zeroed() }; + let mut ts_input: AudioTimeStamp = unsafe { core::mem::zeroed() }; + let mut ts_output: AudioTimeStamp = unsafe { core::mem::zeroed() }; + let mut out_abl = AudioBufferList { + m_number_buffers: 0, + buffers: [AudioBuffer { + m_number_channels: 0, + m_data_byte_size: 0, + m_data: ptr::null_mut(), + }], + }; + unsafe { + io_proc( + 0, + NonNull::from(&mut ts_now), + NonNull::new( + abl as *mut AudioBufferList as *mut objc2_core_audio_types::AudioBufferList, + ) + .expect("input abl non-null"), + NonNull::from(&mut ts_input), + NonNull::new( + &mut out_abl as *mut AudioBufferList + as *mut objc2_core_audio_types::AudioBufferList, + ) + .expect("output abl non-null"), + NonNull::from(&mut ts_output), + capture as *const Capture as *mut c_void, + ) + } + } + + fn interleaved_abl(data: &mut [f32]) -> AudioBufferList { + AudioBufferList { + m_number_buffers: 1, + buffers: [AudioBuffer { + m_number_channels: TARGET_CHANNELS, + m_data_byte_size: (data.len() * size_of::()) as u32, + m_data: data.as_mut_ptr() as *mut c_void, + }], + } + } + + #[test] + fn convert_failures_counted_for_unsupported_format() { + let capture = make_idle_capture(AudioStreamBasicDescription::default()); + let mut data = [0.0_f32; 8]; + let mut abl = interleaved_abl(&mut data); + let status = call_io_proc(&capture, &mut abl); + assert_eq!(status, NO_ERR); + let diagnostics = capture.diagnostics(); + assert_eq!(diagnostics.convert_failures, 1); + assert_eq!(diagnostics.dropped_buffers, 0); + } + + unsafe extern "C" fn noop_pcm_cb( + _ctx: *mut c_void, + _slot: *mut Option, + _frames: u32, + ) { + } + + #[test] + fn dropped_buffers_counted_when_pool_exhausted() { + let mut capture = make_idle_capture(ac::build_input_asbd(48_000.0, TARGET_CHANNELS, false)); + let pool = make_test_pool(); + capture.set_pcm_callback(noop_pcm_cb, ptr::null_mut()); + capture.set_pcm_pool(pool.clone()); + let mut held = Vec::with_capacity(pool.capacity() as usize); + for _ in 0..pool.capacity() { + held.push(pool.try_acquire().expect("slot in capacity")); + } + let mut data = [0.25_f32; 96]; + let mut abl = interleaved_abl(&mut data); + let status = call_io_proc(&capture, &mut abl); + drop(held); + assert_eq!(status, NO_ERR); + let diagnostics = capture.diagnostics(); + assert_eq!(diagnostics.dropped_buffers, 1); + assert_eq!(diagnostics.convert_failures, 0); + } + + #[test] + fn dropped_buffers_counted_when_slot_too_small_for_conversion() { + let mut capture = make_idle_capture(ac::build_input_asbd(48_000.0, TARGET_CHANNELS, false)); + let pool = PcmFramePool::new(2, 16).expect("tiny pool builds"); + capture.set_pcm_callback(noop_pcm_cb, ptr::null_mut()); + capture.set_pcm_pool(pool); + let mut data = [0.25_f32; 96]; + let mut abl = interleaved_abl(&mut data); + let status = call_io_proc(&capture, &mut abl); + assert_eq!(status, NO_ERR); + let diagnostics = capture.diagnostics(); + assert_eq!(diagnostics.dropped_buffers, 1); + assert_eq!(diagnostics.convert_failures, 0); + } + + #[test] + fn dropped_buffers_counted_for_oversize_input() { + let capture = make_idle_capture(ac::build_input_asbd(48_000.0, TARGET_CHANNELS, false)); + let mut data = [0.0_f32; 8]; + let bytes_per_frame = (size_of::() as u32) * TARGET_CHANNELS; + let mut abl = interleaved_abl(&mut data); + abl.buffers[0].m_data_byte_size = (MAX_CALLBACK_INPUT_FRAMES + 1) * bytes_per_frame; + let status = call_io_proc(&capture, &mut abl); + assert_eq!(status, NO_ERR); + let diagnostics = capture.diagnostics(); + assert_eq!(diagnostics.dropped_buffers, 1); + assert_eq!(diagnostics.convert_failures, 0); + } + + #[test] + fn stopped_capture_counts_nothing() { + let capture = make_idle_capture(AudioStreamBasicDescription::default()); + capture.running.store(false, Ordering::Release); + let mut data = [0.0_f32; 8]; + let mut abl = interleaved_abl(&mut data); + let status = call_io_proc(&capture, &mut abl); + assert_eq!(status, NO_ERR); + let diagnostics = capture.diagnostics(); + assert_eq!(diagnostics.convert_failures, 0); + assert_eq!(diagnostics.dropped_buffers, 0); + } + + static OBSERVED_FRAMES: TestAtomicU32 = TestAtomicU32::new(0); + static OBSERVED_FILLED: TestAtomicU32 = TestAtomicU32::new(0); + + unsafe extern "C" fn observing_pcm_cb( + _ctx: *mut c_void, + slot: *mut Option, + frames: u32, + ) { + assert!(!slot.is_null()); + let frame = unsafe { (*slot).take() }.expect("slot delivered"); + assert_eq!( + frame.filled_len(), + (frames as usize) * (TARGET_CHANNELS as usize) + ); + assert_eq!(frame.data_slice()[0], 0.25); + OBSERVED_FILLED.store(frame.filled_len() as u32, Ordering::Release); + OBSERVED_FRAMES.store(frames, Ordering::Release); + } + + #[test] + fn successful_conversion_counts_nothing_and_invokes_callback() { + OBSERVED_FRAMES.store(0, Ordering::Release); + OBSERVED_FILLED.store(0, Ordering::Release); + let mut capture = make_idle_capture(ac::build_input_asbd(48_000.0, TARGET_CHANNELS, false)); + let pool = make_test_pool(); + capture.set_pcm_callback(observing_pcm_cb, ptr::null_mut()); + capture.set_pcm_pool(pool.clone()); + let mut data = [0.25_f32; 96]; + let mut abl = interleaved_abl(&mut data); + let status = call_io_proc(&capture, &mut abl); + assert_eq!(status, NO_ERR); + let diagnostics = capture.diagnostics(); + assert_eq!(diagnostics.convert_failures, 0); + assert_eq!(diagnostics.dropped_buffers, 0); + assert_eq!(OBSERVED_FRAMES.load(Ordering::Acquire), 48); + assert_eq!(OBSERVED_FILLED.load(Ordering::Acquire), 96); + assert_eq!(pool.stats().in_flight, 0); + assert_eq!(pool.stats().released, 1); + } + + #[test] + fn untaken_slot_returns_to_pool() { + let mut capture = make_idle_capture(ac::build_input_asbd(48_000.0, TARGET_CHANNELS, false)); + let pool = make_test_pool(); + capture.set_pcm_callback(noop_pcm_cb, ptr::null_mut()); + capture.set_pcm_pool(pool.clone()); + let mut data = [0.25_f32; 96]; + let mut abl = interleaved_abl(&mut data); + let status = call_io_proc(&capture, &mut abl); + assert_eq!(status, NO_ERR); + assert_eq!(pool.stats().in_flight, 0); + assert_eq!(pool.stats().acquired, 1); + assert_eq!(pool.stats().released, 1); + } +} diff --git a/fluxer_desktop/native/mac-app-audio/src/foundation.rs b/fluxer_desktop/native/mac-app-audio/src/foundation.rs new file mode 100644 index 0000000..f182844 --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/src/foundation.rs @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use core::ffi::CStr; + +use objc2::rc::Retained; +use objc2::runtime::ProtocolObject; +use objc2_foundation::{ + NSError, NSMutableArray, NSMutableDictionary, NSNumber, NSObject, NSProcessInfo, NSString, +}; + +pub fn nsstring_from_cstr(s: &CStr) -> Retained { + match s.to_str() { + Ok(v) => NSString::from_str(v), + Err(_) => NSString::from_str(""), + } +} + +pub fn nsstring_from_str(s: &str) -> Retained { + NSString::from_str(s) +} + +pub fn nsstring_to_string(s: Option<&NSString>) -> String { + match s { + Some(v) => v.to_string(), + None => String::new(), + } +} + +pub fn ns_mutable_array_with_capacity(capacity: usize) -> Retained> { + NSMutableArray::::arrayWithCapacity(capacity) +} + +pub fn ns_mutable_dictionary_with_capacity() -> Retained> { + NSMutableDictionary::::new() +} + +pub fn dict_set_str_key( + dict: &NSMutableDictionary, + key: &CStr, + value: &NSObject, +) { + let key_ns = nsstring_from_cstr(key); + let key_proto = ProtocolObject::from_ref(&*key_ns); + unsafe { dict.setObject_forKey(value, key_proto) }; +} + +pub fn operating_system_version_string() -> String { + let info = NSProcessInfo::processInfo(); + info.operatingSystemVersionString().to_string() +} + +pub fn ns_error_localized_description(err: &NSError) -> String { + err.localizedDescription().to_string() +} + +pub fn number_with_unsigned_int(value: u32) -> Retained { + NSNumber::new_u32(value) +} + +pub fn number_with_bool(value: bool) -> Retained { + NSNumber::new_bool(value) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn nsstring_round_trip() { + let s = nsstring_from_cstr(c"hello fluxer"); + assert_eq!("hello fluxer", nsstring_to_string(Some(&s))); + } + + #[test] + fn nsstring_to_string_handles_none() { + assert_eq!(String::new(), nsstring_to_string(None)); + } + + #[test] + fn mutable_array_initial_count_is_zero() { + let arr = ns_mutable_array_with_capacity(4); + assert_eq!(0, arr.count()); + } + + #[test] + fn mutable_dictionary_initial_count_is_zero() { + let d = ns_mutable_dictionary_with_capacity(); + assert_eq!(0, d.count()); + } +} diff --git a/fluxer_desktop/native/mac-app-audio/src/lib.rs b/fluxer_desktop/native/mac-app-audio/src/lib.rs new file mode 100644 index 0000000..dbf3253 --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/src/lib.rs @@ -0,0 +1,36 @@ +#![deny(clippy::all)] +// SPDX-License-Identifier: AGPL-3.0-or-later +#![allow(dead_code)] +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::collapsible_if)] +#![allow(clippy::manual_is_multiple_of)] +#![allow(clippy::manual_slice_size_calculation)] +#![allow(clippy::unnecessary_cast)] +#![allow(clippy::not_unsafe_ptr_arg_deref)] +#![allow(clippy::missing_transmute_annotations)] +#![allow(clippy::missing_const_for_thread_local)] +#![allow(clippy::too_many_arguments)] + +pub mod audio_converter; +pub mod os_version; +pub mod pcm_pool; +pub mod process_tree; +pub mod related_app; +pub mod source_state; + +#[cfg(target_os = "macos")] +pub mod audio_source; +#[cfg(target_os = "macos")] +pub mod coreaudio_tap; +#[cfg(target_os = "macos")] +pub mod foundation; +#[cfg(target_os = "macos")] +pub mod sck; +#[cfg(target_os = "macos")] +pub mod sck_async; + +#[cfg(target_os = "macos")] +mod napi_surface_macos; + +#[cfg(not(target_os = "macos"))] +mod napi_surface_stub; diff --git a/fluxer_desktop/native/mac-app-audio/src/napi_surface_macos.rs b/fluxer_desktop/native/mac-app-audio/src/napi_surface_macos.rs new file mode 100644 index 0000000..1b7fa7d --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/src/napi_surface_macos.rs @@ -0,0 +1,1122 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use core::ffi::c_void; +use core::ptr; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use std::sync::{Arc, Mutex, MutexGuard}; +use std::thread; +use std::time::Duration; + +use block2::RcBlock; +use fluxer_screen_frame_bus::{NativeScreenFrameSinkHandle, NativeScreenFrameSinkHandleRef}; +use napi::JsValue; +use napi::Status; +use napi::bindgen_prelude::{Float32Array, Function, Result, Unknown, ValueType}; +use napi::threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}; +use napi_derive::napi; + +use objc2::rc::Retained; +use objc2::{AllocAnyThread, Message}; +use objc2_core_foundation::{CFDictionary, CFNumber, CFNumberType}; +use objc2_core_graphics::{ + CGWindowID, CGWindowListCopyWindowInfo, CGWindowListOption, kCGWindowOwnerPID, +}; +use objc2_foundation::{NSArray, NSError, NSString}; +use objc2_screen_capture_kit::{ + SCContentFilter, SCDisplay, SCRunningApplication, SCStream, SCStreamConfiguration, + SCStreamOutputType, SCWindow, +}; + +use crate::audio_source::{Source, SourceOptions}; +use crate::coreaudio_tap::{self, Capture, CaptureScope, CreateError}; +use crate::foundation; +use crate::os_version::{ + self, COREAUDIO_TAP_MIN_MACOS, SCK_MIN_MACOS, SupportClassification, classify_support, + format_version, +}; +use crate::pcm_pool::{PCM_POOL_CAP, PCM_SLOT_SAMPLES_MAX, PcmFramePool, PooledPcmFrame}; +use crate::process_tree; +use crate::related_app::looks_related_by_strings; +use crate::sck; +use crate::sck_async; + +const FRAME_QUEUE_LIMIT: usize = 64; + +type FrameTsfn = + ThreadsafeFunction; +type LifecycleTsfn = + ThreadsafeFunction<(String, String), (), (String, String), Status, false, false, 8>; +const MAX_CALLBACK_OUTPUT_FRAMES: u32 = 192_000; +const PCM_CALLBACK_CHANNELS: usize = 2; +const SCK_LATE_SPAWN_REFRESH_INTERVAL: Duration = Duration::from_secs(2); + +struct LoopbackState { + lifecycle_tsfn: Option, + source: Option>, + stream: Option>, + coreaudio_capture: Option>, + + sck_refresher: Option, +} + +unsafe impl Send for LoopbackState {} +unsafe impl Sync for LoopbackState {} + +struct SckRefresherHandle { + alive: Arc, + stream_holder: Arc, +} + +impl SckRefresherHandle { + fn shutdown(&mut self) { + self.alive.store(false, Ordering::Release); + + if let Ok(mut guard) = self.stream_holder.stream.lock() { + *guard = None; + } + } +} + +impl Drop for SckRefresherHandle { + fn drop(&mut self) { + self.shutdown(); + } +} + +struct SckRefresherStream { + stream: Mutex>>, +} + +unsafe impl Send for SckRefresherStream {} +unsafe impl Sync for SckRefresherStream {} + +struct LoopbackInner { + state: Mutex, + frame_tsfn: parking_lot::RwLock>, + screen_audio_sink: parking_lot::RwLock>>, + pcm_pool: PcmFramePool, + pcm_frames_dropped: AtomicU64, + running: std::sync::atomic::AtomicBool, + tsfn_aborted: std::sync::atomic::AtomicBool, +} + +fn generic_error(reason: impl Into) -> napi::Error { + napi::Error::new(Status::GenericFailure, reason.into()) +} + +fn lock_loopback_state(inner: &LoopbackInner) -> Result> { + inner + .state + .lock() + .map_err(|_| generic_error("ProcessLoopback state lock poisoned")) +} + +fn note_tsfn_status(inner: &LoopbackInner, status: Status) { + if status == Status::Closing { + inner + .tsfn_aborted + .store(true, std::sync::atomic::Ordering::Release); + } +} + +fn retain_screen_audio_sink_handle( + value: Unknown<'_>, +) -> Result> { + if value.get_type()? != ValueType::External { + return Err(napi::Error::new( + Status::InvalidArg, + "ProcessLoopback.setScreenAudioSink expects a native external sink handle", + )); + } + let raw_value = value.value(); + let mut data: *mut c_void = ptr::null_mut(); + let status = + unsafe { napi::sys::napi_get_value_external(raw_value.env, raw_value.value, &mut data) }; + if status != napi::sys::Status::napi_ok || data.is_null() { + return Err(napi::Error::new( + Status::InvalidArg, + "ProcessLoopback.setScreenAudioSink received an empty native external sink handle", + )); + } + let handle = unsafe { + NativeScreenFrameSinkHandle::retain_from_raw(data.cast::()) + } + .ok_or_else(|| { + napi::Error::new( + Status::InvalidArg, + "ProcessLoopback.setScreenAudioSink received an invalid native sink handle", + ) + })?; + Ok(Arc::new(handle)) +} + +#[napi(js_name = "ProcessLoopback")] +pub struct ProcessLoopback { + inner: std::sync::Arc, +} + +fn pooled_pcm_into_float32_array(slot: PooledPcmFrame) -> Float32Array { + let (ptr, len, slot) = slot.into_external_parts(); + assert!(!ptr.is_null()); + assert!(len <= PCM_SLOT_SAMPLES_MAX); + if len == 0 { + drop(slot); + let empty: Vec = Vec::new(); + return Float32Array::new(empty); + } + unsafe { + Float32Array::with_external_data(ptr, len, move |_data, _len| { + drop(slot); + }) + } +} + +unsafe extern "C" fn on_pcm_trampoline( + ctx: *mut c_void, + slot: *mut Option, + frames: u32, +) { + if ctx.is_null() { + return; + } + if slot.is_null() { + return; + } + let inner = unsafe { &*(ctx as *const LoopbackInner) }; + if frames == 0 || frames > MAX_CALLBACK_OUTPUT_FRAMES { + inner.pcm_frames_dropped.fetch_add(1, Ordering::Relaxed); + return; + } + let Some(total) = (frames as usize).checked_mul(PCM_CALLBACK_CHANNELS) else { + inner.pcm_frames_dropped.fetch_add(1, Ordering::Relaxed); + return; + }; + if total > PCM_SLOT_SAMPLES_MAX { + inner.pcm_frames_dropped.fetch_add(1, Ordering::Relaxed); + return; + } + if !inner.running.load(std::sync::atomic::Ordering::Acquire) { + inner.pcm_frames_dropped.fetch_add(1, Ordering::Relaxed); + return; + } + if let Some(sink) = inner.screen_audio_sink.read().as_ref().cloned() { + let Some(frame) = (unsafe { &mut *slot }).take() else { + return; + }; + assert_eq!(frame.filled_len(), total); + sink.enqueue_screen_audio_f32( + frame.data_slice(), + frames, + PCM_CALLBACK_CHANNELS as u32, + 48_000, + 0, + ); + return; + } + if inner + .tsfn_aborted + .load(std::sync::atomic::Ordering::Acquire) + { + inner.pcm_frames_dropped.fetch_add(1, Ordering::Relaxed); + return; + } + let guard = inner.frame_tsfn.read(); + let Some(tsfn) = guard.as_ref() else { + inner.pcm_frames_dropped.fetch_add(1, Ordering::Relaxed); + return; + }; + let Some(frame) = (unsafe { &mut *slot }).take() else { + return; + }; + assert_eq!(frame.filled_len(), total); + let status = tsfn.call(frame, ThreadsafeFunctionCallMode::NonBlocking); + note_tsfn_status(inner, status); +} + +unsafe extern "C" fn on_stop_trampoline(ctx: *mut c_void, err: *mut NSError) { + if ctx.is_null() { + return; + } + let inner = unsafe { &*(ctx as *const LoopbackInner) }; + let was_running = inner + .running + .swap(false, std::sync::atomic::Ordering::AcqRel); + if !was_running { + return; + } + let Ok(state) = inner.state.lock() else { + return; + }; + if !err.is_null() { + let err_ref: &NSError = unsafe { &*err }; + let msg = foundation::ns_error_localized_description(err_ref); + let msg = if msg.is_empty() { + "stream stopped".to_string() + } else { + msg + }; + if let Some(tsfn) = state.lifecycle_tsfn.as_ref() { + let status = tsfn.call( + ("error".to_string(), msg), + ThreadsafeFunctionCallMode::NonBlocking, + ); + note_tsfn_status(inner, status); + } + } + if let Some(tsfn) = state.lifecycle_tsfn.as_ref() { + let status = tsfn.call( + ("closed".to_string(), String::new()), + ThreadsafeFunctionCallMode::NonBlocking, + ); + note_tsfn_status(inner, status); + } +} + +enum BuildStreamErr { + PidNotFound, + NoApps, + NoDisplays, + Other, +} + +struct BuiltStream { + stream: Retained, + source: Box, +} + +fn build_stream_for_pid( + pid: i32, + include_process_tree: bool, +) -> std::result::Result { + let content = + match sck_async::get_shareable_content(false, false, sck_async::DEFAULT_TIMEOUT_NS) { + Ok(c) => c, + Err(_) => return Err(BuildStreamErr::Other), + }; + + let apps: Retained> = unsafe { content.content.applications() }; + let app_count = apps.count(); + if app_count == 0 { + return Err(BuildStreamErr::NoApps); + } + + let mut target_app: Option> = None; + for i in 0..app_count { + let app = apps.objectAtIndex(i); + if sck::sc_running_application_process_id(&app) == pid { + target_app = Some(app); + break; + } + } + let target_app = match target_app { + Some(a) => a, + None => return Err(BuildStreamErr::PidNotFound), + }; + + let (apps_array, pids) = collect_capture_apps(&apps, &target_app, include_process_tree) + .ok_or(BuildStreamErr::Other)?; + + let empty_windows: Retained> = NSArray::new(); + + let display = + best_display_for_selection(&content.content, &pids).ok_or(BuildStreamErr::NoDisplays)?; + + let filter_alloc = SCContentFilter::alloc(); + let filter: Retained = unsafe { + SCContentFilter::initWithDisplay_includingApplications_exceptingWindows( + filter_alloc, + &display, + &apps_array, + &empty_windows, + ) + }; + + let cfg: Retained = unsafe { SCStreamConfiguration::new() }; + sck::cfg_set_captures_audio(&cfg, true); + sck::cfg_set_excludes_current_process_audio(&cfg, true); + sck::cfg_set_sample_rate(&cfg, 48_000); + sck::cfg_set_channel_count(&cfg, 2); + sck::cfg_set_queue_depth(&cfg, 8); + sck::cfg_set_width(&cfg, 2); + sck::cfg_set_height(&cfg, 2); + sck::cfg_set_minimum_frame_interval(&cfg, sck::cmtime_seconds(1, 1)); + sck::cfg_set_shows_cursor(&cfg, false); + sck::cfg_set_capture_dynamic_range_sdr_if_available(&cfg); + + let stream_name = format!("Fluxer ProcessLoopback Audio (pid {pid})"); + let nsname = NSString::from_str(&stream_name); + sck::cfg_set_stream_name_if_available(&cfg, &nsname); + + let source = Source::create(SourceOptions::default()).map_err(|_| BuildStreamErr::Other)?; + + let stream_alloc = SCStream::alloc(); + let stream: Retained = unsafe { + SCStream::initWithFilter_configuration_delegate( + stream_alloc, + &filter, + &cfg, + Some(source.delegate_as_delegate()), + ) + }; + + if sck::sc_stream_add_stream_output( + &stream, + source.delegate_as_output(), + SCStreamOutputType::Screen, + None, + ) + .is_err() + { + return Err(BuildStreamErr::Other); + } + if sck::sc_stream_add_stream_output( + &stream, + source.delegate_as_output(), + SCStreamOutputType::Audio, + Some(source.output_queue()), + ) + .is_err() + { + return Err(BuildStreamErr::Other); + } + + Ok(BuiltStream { stream, source }) +} + +fn build_system_stream() -> std::result::Result { + let content = + match sck_async::get_shareable_content(false, false, sck_async::DEFAULT_TIMEOUT_NS) { + Ok(c) => c, + Err(_) => return Err(BuildStreamErr::Other), + }; + + let displays: Retained> = unsafe { content.content.displays() }; + if displays.count() == 0 { + return Err(BuildStreamErr::NoDisplays); + } + let display = displays.objectAtIndex(0); + let empty_windows: Retained> = NSArray::new(); + let apps: Retained> = unsafe { content.content.applications() }; + let mut excluded_apps: Vec> = Vec::new(); + for i in 0..apps.count() { + let app = apps.objectAtIndex(i); + if is_self_or_related_process(sck::sc_running_application_process_id(&app)) { + excluded_apps.push(app); + } + } + let excluded_app_refs: Vec<&SCRunningApplication> = + excluded_apps.iter().map(|app| app.as_ref()).collect(); + let excluded_apps_array = NSArray::from_slice(&excluded_app_refs); + + let filter_alloc = SCContentFilter::alloc(); + let filter: Retained = unsafe { + SCContentFilter::initWithDisplay_excludingApplications_exceptingWindows( + filter_alloc, + &display, + &excluded_apps_array, + &empty_windows, + ) + }; + + let cfg: Retained = unsafe { SCStreamConfiguration::new() }; + sck::cfg_set_captures_audio(&cfg, true); + sck::cfg_set_excludes_current_process_audio(&cfg, true); + sck::cfg_set_sample_rate(&cfg, 48_000); + sck::cfg_set_channel_count(&cfg, 2); + sck::cfg_set_queue_depth(&cfg, 8); + sck::cfg_set_width(&cfg, 2); + sck::cfg_set_height(&cfg, 2); + sck::cfg_set_minimum_frame_interval(&cfg, sck::cmtime_seconds(1, 1)); + sck::cfg_set_shows_cursor(&cfg, false); + sck::cfg_set_capture_dynamic_range_sdr_if_available(&cfg); + + let nsname = NSString::from_str("Fluxer System Audio"); + sck::cfg_set_stream_name_if_available(&cfg, &nsname); + + let source = Source::create(SourceOptions::default()).map_err(|_| BuildStreamErr::Other)?; + + let stream_alloc = SCStream::alloc(); + let stream: Retained = unsafe { + SCStream::initWithFilter_configuration_delegate( + stream_alloc, + &filter, + &cfg, + Some(source.delegate_as_delegate()), + ) + }; + + if sck::sc_stream_add_stream_output( + &stream, + source.delegate_as_output(), + SCStreamOutputType::Screen, + None, + ) + .is_err() + { + return Err(BuildStreamErr::Other); + } + if sck::sc_stream_add_stream_output( + &stream, + source.delegate_as_output(), + SCStreamOutputType::Audio, + Some(source.output_queue()), + ) + .is_err() + { + return Err(BuildStreamErr::Other); + } + + Ok(BuiltStream { stream, source }) +} + +fn collect_capture_apps( + apps: &NSArray, + target_app: &SCRunningApplication, + include_tree: bool, +) -> Option<(Retained>, Vec)> { + let count = apps.count(); + let capacity = if include_tree { count.max(1) } else { 1 }; + let mut chosen: Vec> = Vec::with_capacity(capacity); + let mut pids: Vec = Vec::with_capacity(capacity); + + let target_pid = sck::sc_running_application_process_id(target_app); + let target_info = process_tree::info_for_pid(target_pid); + let target_bundle = sck::sc_running_application_bundle_identifier(target_app).to_string(); + let target_name = sck::sc_running_application_name(target_app).to_string(); + + if include_tree { + for i in 0..count { + if pids.len() >= capacity { + break; + } + let candidate = apps.objectAtIndex(i); + let candidate_pid = sck::sc_running_application_process_id(&candidate); + let candidate_bundle = + sck::sc_running_application_bundle_identifier(&candidate).to_string(); + let candidate_name = sck::sc_running_application_name(&candidate).to_string(); + let same_tree = + process_tree::is_same_launch_tree(candidate_pid, target_pid, target_info); + let related = candidate_pid == target_pid + || looks_related_by_strings( + &candidate_bundle, + &target_bundle, + &candidate_name, + &target_name, + ); + if (same_tree || related) && !pids.contains(&candidate_pid) { + chosen.push(candidate); + pids.push(candidate_pid); + } + } + } + + if pids.is_empty() { + let pid = sck::sc_running_application_process_id(target_app); + chosen.push(target_app.retain()); + pids.push(pid); + } + let refs: Vec<&SCRunningApplication> = chosen.iter().map(|r| r.as_ref()).collect(); + Some((NSArray::from_slice(&refs), pids)) +} + +fn best_display_for_selection( + content: &objc2_screen_capture_kit::SCShareableContent, + pids: &[i32], +) -> Option> { + let displays: Retained> = unsafe { content.displays() }; + let display_count = displays.count(); + if display_count == 0 { + return None; + } + let first_display = displays.objectAtIndex(0); + + let windows: Retained> = unsafe { content.windows() }; + let window_count = windows.count(); + if window_count == 0 { + return Some(first_display); + } + + let mut best_display = first_display.clone(); + let mut best_area = 0.0f64; + let mut saw_app_window = false; + + for d in 0..display_count { + let display = displays.objectAtIndex(d); + let display_frame = sck::sc_display_frame(&display); + let mut area = 0.0; + for w in 0..window_count { + let window = windows.objectAtIndex(w); + let owner = match sck::sc_window_owning_application(&window) { + Some(o) => o, + None => continue, + }; + let owner_pid = sck::sc_running_application_process_id(&owner); + if !pids.contains(&owner_pid) { + continue; + } + saw_app_window = true; + area += sck::cgrect_intersection_area(display_frame, sck::sc_window_frame(&window)); + } + if area > best_area { + best_area = area; + best_display = display; + } + } + + if !saw_app_window || best_area <= 0.0 { + Some(first_display) + } else { + Some(best_display) + } +} + +fn is_self_or_related_process(pid: i32) -> bool { + let self_pid = unsafe { libc::getpid() }; + pid == self_pid || process_tree::collect_related_pids(self_pid, 512).contains(&pid) +} + +fn spawn_sck_late_spawn_refresher( + target_pid: i32, + stream: Retained, +) -> Option { + let alive = Arc::new(AtomicBool::new(true)); + let alive_for_thread = alive.clone(); + let stream_holder = Arc::new(SckRefresherStream { + stream: Mutex::new(Some(stream)), + }); + let stream_holder_for_thread = stream_holder.clone(); + + thread::Builder::new() + .name("fluxer-mac-sck-refresh".into()) + .spawn(move || { + run_sck_late_spawn_refresher(target_pid, stream_holder_for_thread, alive_for_thread); + }) + .ok()?; + Some(SckRefresherHandle { + alive, + stream_holder, + }) +} + +fn run_sck_late_spawn_refresher( + target_pid: i32, + stream_holder: Arc, + alive: Arc, +) { + let mut previous: Vec = Vec::new(); + while alive.load(Ordering::Acquire) { + thread::sleep(SCK_LATE_SPAWN_REFRESH_INTERVAL); + if !alive.load(Ordering::Acquire) { + break; + } + + let content = + match sck_async::get_shareable_content(false, false, sck_async::DEFAULT_TIMEOUT_NS) { + Ok(c) => c, + Err(_) => continue, + }; + let apps: Retained> = + unsafe { content.content.applications() }; + let app_count = apps.count(); + if app_count == 0 { + continue; + } + let mut target_app: Option> = None; + for i in 0..app_count { + let app = apps.objectAtIndex(i); + if sck::sc_running_application_process_id(&app) == target_pid { + target_app = Some(app); + break; + } + } + let target_app = match target_app { + Some(a) => a, + + None => continue, + }; + let Some((apps_array, pids)) = collect_capture_apps(&apps, &target_app, true) else { + continue; + }; + + let mut sorted_pids = pids.clone(); + sorted_pids.sort_unstable(); + sorted_pids.dedup(); + if sorted_pids == previous { + continue; + } + + let empty_windows: Retained> = NSArray::new(); + let Some(display) = best_display_for_selection(&content.content, &pids) else { + continue; + }; + let filter_alloc = SCContentFilter::alloc(); + let filter: Retained = unsafe { + SCContentFilter::initWithDisplay_includingApplications_exceptingWindows( + filter_alloc, + &display, + &apps_array, + &empty_windows, + ) + }; + + let stream_snapshot = match stream_holder.stream.lock() { + Ok(guard) => guard.clone(), + Err(_) => break, + }; + let Some(stream) = stream_snapshot else { + break; + }; + + let completion = RcBlock::new(|_err: *mut NSError| {}); + unsafe { + stream.updateContentFilter_completionHandler(&filter, Some(&completion)); + } + previous = sorted_pids; + } +} + +fn stop_loopback(inner: &LoopbackInner) -> Result<()> { + inner + .running + .store(false, std::sync::atomic::Ordering::Release); + let (stream_opt, mut source, coreaudio_capture, sck_refresher) = { + let mut state = lock_loopback_state(inner)?; + let stream = state.stream.take(); + let source = state.source.take(); + let coreaudio_capture = state.coreaudio_capture.take(); + let refresher = state.sck_refresher.take(); + (stream, source, coreaudio_capture, refresher) + }; + + if let Some(mut r) = sck_refresher { + r.shutdown(); + } + + if stream_opt.is_none() && coreaudio_capture.is_none() { + return Ok(()); + } + + if let Some(_capture) = coreaudio_capture { + return Ok(()); + } + + if let Some(s) = source.as_mut() { + s.clear_stop_callback(); + let _ = s.state.request_stop(); + } + + if let Some(stream) = stream_opt.as_ref() { + let _ = sck_async::stop_capture(stream, sck_async::DEFAULT_TIMEOUT_NS); + } + + if let Some(s) = source { + let _ = s.state.mark_stopped(); + drop(s); + } + + Ok(()) +} + +#[napi] +impl ProcessLoopback { + #[napi(constructor)] + pub fn new() -> Self { + let pcm_pool = PcmFramePool::new(PCM_POOL_CAP, PCM_SLOT_SAMPLES_MAX).unwrap_or_else(|_| { + unreachable!("PCM_POOL_CAP and PCM_SLOT_SAMPLES_MAX must validate") + }); + Self { + inner: std::sync::Arc::new(LoopbackInner { + state: Mutex::new(LoopbackState { + lifecycle_tsfn: None, + source: None, + stream: None, + coreaudio_capture: None, + sck_refresher: None, + }), + frame_tsfn: parking_lot::RwLock::new(None), + screen_audio_sink: parking_lot::RwLock::new(None), + pcm_pool, + pcm_frames_dropped: AtomicU64::new(0), + running: std::sync::atomic::AtomicBool::new(false), + tsfn_aborted: std::sync::atomic::AtomicBool::new(false), + }), + } + } + + #[napi] + pub fn set_frame_callback(&self, callback: Function) -> Result<()> { + let tsfn: FrameTsfn = callback + .build_threadsafe_function::() + .max_queue_size::() + .build_callback(|ctx| Ok(pooled_pcm_into_float32_array(ctx.value)))?; + *self.inner.frame_tsfn.write() = Some(tsfn); + self.inner + .tsfn_aborted + .store(false, std::sync::atomic::Ordering::Release); + Ok(()) + } + + #[napi(js_name = "setScreenAudioSink")] + pub fn set_screen_audio_sink(&self, sink_handle: Unknown<'_>) -> Result<()> { + let sink = retain_screen_audio_sink_handle(sink_handle)?; + if !sink.supports_screen_audio() { + return Err(napi::Error::new( + Status::InvalidArg, + "ProcessLoopback.setScreenAudioSink handle does not support screen audio", + )); + } + *self.inner.screen_audio_sink.write() = Some(sink); + Ok(()) + } + + #[napi(js_name = "clearScreenAudioSink")] + pub fn clear_screen_audio_sink(&self) { + self.inner.screen_audio_sink.write().take(); + } + + #[napi] + pub fn set_lifecycle_callback(&self, callback: Function<(String, String), ()>) -> Result<()> { + let tsfn: LifecycleTsfn = callback + .build_threadsafe_function::<(String, String)>() + .max_queue_size::<8>() + .build_callback(|ctx| Ok(ctx.value))?; + let mut state = lock_loopback_state(&self.inner)?; + state.lifecycle_tsfn = Some(tsfn); + self.inner + .tsfn_aborted + .store(false, std::sync::atomic::Ordering::Release); + Ok(()) + } + + #[napi] + pub async fn start( + &self, + pid: i32, + _exclude_self: Option, + include_process_tree: Option, + backend: Option, + capture_scope: Option, + ) -> Result<()> { + let include_tree = include_process_tree.unwrap_or(true); + let backend_pref = backend.as_deref().unwrap_or("auto"); + let scope_pref = capture_scope.as_deref().unwrap_or("process"); + + let scope = if scope_pref == "system" { + CaptureScope::System + } else { + CaptureScope::Process + }; + + if pid < 1 { + return Err(napi::Error::new( + Status::InvalidArg, + "ProcessLoopback.start pid must be a positive 32-bit integer", + )); + } + + { + let detected = os_version::current_macos_version(); + let SupportClassification { + supported, + sck_available, + coreaudio_available, + reason, + } = classify_support(detected); + + let backend_ok = match backend_pref { + "coreaudio" => coreaudio_available, + "sck" => sck_available, + _ => supported, + }; + if !backend_ok { + return Err(napi::Error::new(Status::GenericFailure, reason)); + } + } + if scope == CaptureScope::Process && is_self_or_related_process(pid) { + return Err(napi::Error::new( + Status::GenericFailure, + "ProcessLoopback refuses to capture Fluxer's own process tree", + )); + } + + { + let state = lock_loopback_state(&self.inner)?; + if state.stream.is_some() + || state.source.is_some() + || state.coreaudio_capture.is_some() + || self + .inner + .running + .load(std::sync::atomic::Ordering::Acquire) + { + return Err(napi::Error::new( + Status::GenericFailure, + "ProcessLoopback is already running", + )); + } + } + + let mut coreaudio_error: Option = None; + let should_try_coreaudio = backend_pref == "coreaudio" + || (backend_pref == "auto" && scope == CaptureScope::Process); + if should_try_coreaudio { + match Capture::create(pid, include_tree, scope) { + Ok(mut capture) => { + let ctx = std::sync::Arc::as_ptr(&self.inner) as *mut c_void; + capture.set_pcm_callback(on_pcm_trampoline, ctx); + capture.set_pcm_pool(self.inner.pcm_pool.clone()); + { + let mut state = lock_loopback_state(&self.inner)?; + state.coreaudio_capture = Some(capture); + } + self.inner + .running + .store(true, std::sync::atomic::Ordering::Release); + let start_res = { + let mut state = lock_loopback_state(&self.inner)?; + state + .coreaudio_capture + .as_mut() + .map(|c| c.start()) + .unwrap_or(Err(CreateError::StartDeviceFailed)) + }; + if let Err(e) = start_res { + self.inner + .running + .store(false, std::sync::atomic::Ordering::Release); + let mut state = lock_loopback_state(&self.inner)?; + state.coreaudio_capture = None; + return Err(napi::Error::new( + Status::GenericFailure, + coreaudio_tap::coreaudio_error_message(&e), + )); + } + return Ok(()); + } + Err(e) => { + if backend_pref == "coreaudio" || scope == CaptureScope::System { + return Err(napi::Error::new( + Status::GenericFailure, + coreaudio_tap::coreaudio_error_message(&e), + )); + } + coreaudio_error = Some(e); + } + } + } + + let mut built = match if scope == CaptureScope::System { + build_system_stream() + } else { + build_stream_for_pid(pid, include_tree) + } { + Ok(b) => b, + Err(e) => { + let msg = match e { + BuildStreamErr::PidNotFound => match &coreaudio_error { + Some(ce) => coreaudio_tap::coreaudio_error_message(ce), + None => "No running application for pid", + }, + BuildStreamErr::NoApps | BuildStreamErr::NoDisplays => "No display available", + BuildStreamErr::Other => "ProcessLoopback.start failed", + }; + return Err(napi::Error::new(Status::GenericFailure, msg)); + } + }; + + let ctx = std::sync::Arc::as_ptr(&self.inner) as *mut c_void; + built.source.set_pcm_callback(on_pcm_trampoline, ctx); + built.source.set_pcm_pool(self.inner.pcm_pool.clone()); + built.source.set_stop_callback(on_stop_trampoline, ctx); + let _ = built.source.state.request_start(); + + let stream = built.stream.clone(); + { + let mut state = lock_loopback_state(&self.inner)?; + state.source = Some(built.source); + state.stream = Some(built.stream); + } + self.inner + .running + .store(true, std::sync::atomic::Ordering::Release); + + if sck_async::start_capture(&stream, sck_async::DEFAULT_TIMEOUT_NS).is_err() { + self.inner + .running + .store(false, std::sync::atomic::Ordering::Release); + let mut state = lock_loopback_state(&self.inner)?; + state.source = None; + state.stream = None; + return Err(napi::Error::new( + Status::GenericFailure, + "SCStream startCapture failed", + )); + } + + { + let mut state = lock_loopback_state(&self.inner)?; + if let Some(s) = state.source.as_mut() { + let _ = s.state.mark_running(); + } + } + + if scope == CaptureScope::Process && include_tree { + let refresher = spawn_sck_late_spawn_refresher(pid, stream.clone()); + let mut state = lock_loopback_state(&self.inner)?; + state.sck_refresher = refresher; + } + + Ok(()) + } + + #[napi] + pub async fn stop(&self) -> Result<()> { + stop_loopback(&self.inner) + } +} + +impl Drop for ProcessLoopback { + fn drop(&mut self) { + let _ = stop_loopback(&self.inner); + } +} + +#[napi(js_name = "pidFromWindowId")] +pub fn pid_from_window_id(window_id: i64) -> i32 { + if window_id <= 0 || window_id > u32::MAX as i64 { + return 0; + } + let wid: CGWindowID = window_id as u32; + + let Some(array) = CGWindowListCopyWindowInfo(CGWindowListOption::OptionIncludingWindow, wid) + else { + return 0; + }; + if array.count() == 0 { + return 0; + } + + let raw_dict = unsafe { array.value_at_index(0) }; + if raw_dict.is_null() { + return 0; + } + let dict: &CFDictionary = unsafe { &*(raw_dict as *const CFDictionary) }; + + let key: &objc2_core_foundation::CFString = unsafe { kCGWindowOwnerPID }; + let key_ptr = (key as *const objc2_core_foundation::CFString).cast::(); + let owner_val = unsafe { dict.value(key_ptr) }; + if owner_val.is_null() { + return 0; + } + let owner_num: &CFNumber = unsafe { &*(owner_val as *const CFNumber) }; + let mut pid: i32 = 0; + let ok = + unsafe { owner_num.value(CFNumberType::IntType, (&mut pid) as *mut i32 as *mut c_void) }; + if ok { pid } else { 0 } +} + +#[napi(object, js_name = "MacApplicationDescriptor")] +pub struct AppDescriptor { + pub pid: i32, + pub bundle_id: Option, + pub name: String, +} + +#[napi(js_name = "listAudibleApplications")] +pub async fn list_audible_applications() -> Result> { + let content = + match sck_async::get_shareable_content(false, false, sck_async::DEFAULT_TIMEOUT_NS) { + Ok(c) => c, + Err(_) => return Ok(Vec::new()), + }; + let apps: Retained> = unsafe { content.content.applications() }; + let mut out = Vec::new(); + let n = apps.count(); + for i in 0..n { + let app = apps.objectAtIndex(i); + let pid = sck::sc_running_application_process_id(&app); + let bundle = sck::sc_running_application_bundle_identifier(&app).to_string(); + let name = sck::sc_running_application_name(&app).to_string(); + out.push(AppDescriptor { + pid, + bundle_id: Some(bundle), + name, + }); + } + Ok(out) +} + +#[napi(object, js_name = "MacBackendSckAvailability")] +pub struct SckAvailability { + pub supported: bool, + pub macos_version: Option, +} + +#[napi(object, js_name = "MacBackendCoreAudioAvailability")] +pub struct CoreAudioAvailability { + pub supported: bool, +} + +#[napi(object, js_name = "MacBackendAvailability")] +pub struct BackendAvailability { + pub sck: SckAvailability, + pub coreaudio: CoreAudioAvailability, + pub screen_permission: String, + pub audio_permission: String, +} + +#[napi(object, js_name = "MacAppAudioBackendInfo")] +pub struct MacAppAudioBackendInfo { + pub backend: String, + pub supported: bool, + pub reason: String, + #[napi(js_name = "minMacosVersion")] + pub min_macos_version: String, + #[napi(js_name = "minMacosVersionCoreaudio")] + pub min_macos_version_coreaudio: String, + #[napi(js_name = "detectedMacosVersion")] + pub detected_macos_version: Option, + #[napi(js_name = "sckAvailable")] + pub sck_available: bool, + #[napi(js_name = "coreaudioAvailable")] + pub coreaudio_available: bool, +} + +#[napi(js_name = "getBackendInfo")] +pub fn get_backend_info() -> MacAppAudioBackendInfo { + let detected = os_version::current_macos_version(); + let SupportClassification { + supported, + sck_available, + coreaudio_available, + reason, + } = classify_support(detected); + MacAppAudioBackendInfo { + backend: "mac-app-audio".to_owned(), + supported, + reason, + min_macos_version: format_version(SCK_MIN_MACOS), + min_macos_version_coreaudio: format_version(COREAUDIO_TAP_MIN_MACOS), + detected_macos_version: detected.map(format_version), + sck_available, + coreaudio_available, + } +} + +#[napi(js_name = "getBackendAvailability")] +pub async fn get_backend_availability() -> Result { + use objc2::runtime::AnyClass; + let sck_supported = AnyClass::get(c"SCStream").is_some(); + let version_str = Some(foundation::operating_system_version_string()); + Ok(BackendAvailability { + sck: SckAvailability { + supported: sck_supported, + macos_version: version_str, + }, + coreaudio: CoreAudioAvailability { + supported: coreaudio_tap::is_supported(), + }, + screen_permission: "not-determined".to_string(), + audio_permission: "not-determined".to_string(), + }) +} diff --git a/fluxer_desktop/native/mac-app-audio/src/napi_surface_stub.rs b/fluxer_desktop/native/mac-app-audio/src/napi_surface_stub.rs new file mode 100644 index 0000000..95bb7a1 --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/src/napi_surface_stub.rs @@ -0,0 +1,69 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use napi::bindgen_prelude::{Error, Result, Status}; +use napi_derive::napi; + +fn unsupported() -> Error { + Error::new( + Status::GenericFailure, + "@fluxer/mac-app-audio is only supported on macOS", + ) +} + +#[napi(js_name = "pidFromWindowId")] +pub fn pid_from_window_id(_window_id: i64) -> i32 { + 0 +} + +#[napi(js_name = "listAudibleApplications")] +pub fn list_audible_applications() -> Result> { + Ok(Vec::new()) +} + +#[napi(js_name = "getBackendAvailability")] +pub fn get_backend_availability() -> Result<()> { + Err(unsupported()) +} + +#[napi(object, js_name = "MacAppAudioBackendInfo")] +pub struct MacAppAudioBackendInfo { + pub backend: String, + pub supported: bool, + pub reason: String, + #[napi(js_name = "minMacosVersion")] + pub min_macos_version: String, + #[napi(js_name = "minMacosVersionCoreaudio")] + pub min_macos_version_coreaudio: String, + #[napi(js_name = "detectedMacosVersion")] + pub detected_macos_version: Option, + #[napi(js_name = "sckAvailable")] + pub sck_available: bool, + #[napi(js_name = "coreaudioAvailable")] + pub coreaudio_available: bool, +} + +#[napi(js_name = "getBackendInfo")] +pub fn get_backend_info() -> MacAppAudioBackendInfo { + use crate::os_version::{COREAUDIO_TAP_MIN_MACOS, SCK_MIN_MACOS, format_version}; + MacAppAudioBackendInfo { + backend: "mac-app-audio".to_owned(), + supported: false, + reason: "@fluxer/mac-app-audio is only supported on macOS".to_owned(), + min_macos_version: format_version(SCK_MIN_MACOS), + min_macos_version_coreaudio: format_version(COREAUDIO_TAP_MIN_MACOS), + detected_macos_version: None, + sck_available: false, + coreaudio_available: false, + } +} + +#[napi] +pub struct ProcessLoopback; + +#[napi] +impl ProcessLoopback { + #[napi(constructor)] + pub fn new() -> Result { + Err(unsupported()) + } +} diff --git a/fluxer_desktop/native/mac-app-audio/src/os_version.rs b/fluxer_desktop/native/mac-app-audio/src/os_version.rs new file mode 100644 index 0000000..d7ebc8e --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/src/os_version.rs @@ -0,0 +1,196 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub const SCK_MIN_MACOS: (i64, i64, i64) = (12, 3, 0); + +pub const COREAUDIO_TAP_MIN_MACOS: (i64, i64, i64) = (14, 2, 0); + +pub fn meets_floor(version: (i64, i64, i64), floor: (i64, i64, i64)) -> bool { + if version.0 != floor.0 { + return version.0 > floor.0; + } + if version.1 != floor.1 { + return version.1 > floor.1; + } + version.2 >= floor.2 +} + +pub fn format_version(version: (i64, i64, i64)) -> String { + if version.2 == 0 { + format!("{}.{}", version.0, version.1) + } else { + format!("{}.{}.{}", version.0, version.1, version.2) + } +} + +#[cfg(target_os = "macos")] +pub fn current_macos_version() -> Option<(i64, i64, i64)> { + use objc2_foundation::NSProcessInfo; + let info = NSProcessInfo::processInfo(); + let v = info.operatingSystemVersion(); + Some(( + v.majorVersion as i64, + v.minorVersion as i64, + v.patchVersion as i64, + )) +} + +#[cfg(not(target_os = "macos"))] +pub fn current_macos_version() -> Option<(i64, i64, i64)> { + None +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SupportClassification { + pub supported: bool, + pub sck_available: bool, + pub coreaudio_available: bool, + pub reason: String, +} + +pub fn classify_support(detected: Option<(i64, i64, i64)>) -> SupportClassification { + let min_sck = format_version(SCK_MIN_MACOS); + let min_coreaudio = format_version(COREAUDIO_TAP_MIN_MACOS); + match detected { + None => SupportClassification { + supported: false, + sck_available: false, + coreaudio_available: false, + reason: "mac-app-audio could not detect the running macOS version. \ + Per-app and self-excluding desktop audio capture unavailable." + .to_owned(), + }, + Some(v) => { + let detected_str = format_version(v); + let sck_ok = meets_floor(v, SCK_MIN_MACOS); + let coreaudio_ok = meets_floor(v, COREAUDIO_TAP_MIN_MACOS); + let supported = sck_ok || coreaudio_ok; + let reason = if supported { + if coreaudio_ok { + format!( + "mac-app-audio supported on macOS {detected_str} \ + (CoreAudio process tap, requires macOS {min_coreaudio}+; \ + ScreenCaptureKit fallback requires macOS {min_sck}+)." + ) + } else { + format!( + "mac-app-audio supported on macOS {detected_str} \ + (ScreenCaptureKit per-app capture, requires macOS {min_sck}+). \ + CoreAudio process tap requires macOS {min_coreaudio}+ \ + and is unavailable here." + ) + } + } else { + format!( + "mac-app-audio requires macOS {min_sck}+ (ScreenCaptureKit). \ + This Mac is running macOS {detected_str}. Per-app audio capture \ + unavailable; Fluxer must not use a broader audio route that could \ + include unrelated apps or call audio." + ) + }; + SupportClassification { + supported, + sck_available: sck_ok, + coreaudio_available: coreaudio_ok, + reason, + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn meets_floor_exact_match() { + assert!(meets_floor((12, 3, 0), SCK_MIN_MACOS)); + } + + #[test] + fn meets_floor_higher_major() { + assert!(meets_floor((14, 0, 0), SCK_MIN_MACOS)); + } + + #[test] + fn meets_floor_higher_minor() { + assert!(meets_floor((12, 4, 0), SCK_MIN_MACOS)); + } + + #[test] + fn rejects_below_floor_minor() { + assert!(!meets_floor((12, 2, 9), SCK_MIN_MACOS)); + } + + #[test] + fn rejects_below_floor_major() { + assert!(!meets_floor((11, 7, 10), SCK_MIN_MACOS)); + } + + #[test] + fn rejects_macos_10_15_for_sck() { + assert!(!meets_floor((10, 15, 7), SCK_MIN_MACOS)); + } + + #[test] + fn coreaudio_floor_14_2() { + assert!(meets_floor((14, 2, 0), COREAUDIO_TAP_MIN_MACOS)); + assert!(!meets_floor((14, 1, 9), COREAUDIO_TAP_MIN_MACOS)); + assert!(meets_floor((15, 0, 0), COREAUDIO_TAP_MIN_MACOS)); + } + + #[test] + fn format_version_trims_zero_patch() { + assert_eq!("12.3", format_version((12, 3, 0))); + assert_eq!("14.2.1", format_version((14, 2, 1))); + } + + #[test] + fn classify_unknown_version_is_unsupported() { + let c = classify_support(None); + assert!(!c.supported); + assert!(!c.sck_available); + assert!(!c.coreaudio_available); + assert!(c.reason.contains("could not detect")); + } + + #[test] + fn classify_macos_10_15_is_unsupported_and_mentions_min_version() { + let c = classify_support(Some((10, 15, 7))); + assert!(!c.supported); + assert!(!c.sck_available); + assert!(!c.coreaudio_available); + assert!(c.reason.contains("macOS 12.3+"), "reason: {}", c.reason); + assert!(c.reason.contains("macOS 10.15.7"), "reason: {}", c.reason); + assert!(c.reason.contains("ScreenCaptureKit")); + } + + #[test] + fn classify_macos_12_3_is_sck_only() { + let c = classify_support(Some((12, 3, 0))); + assert!(c.supported); + assert!(c.sck_available); + assert!(!c.coreaudio_available); + assert!(c.reason.contains("ScreenCaptureKit per-app capture")); + assert!( + c.reason + .contains("CoreAudio process tap requires macOS 14.2+") + ); + } + + #[test] + fn classify_macos_14_2_has_both_backends() { + let c = classify_support(Some((14, 2, 0))); + assert!(c.supported); + assert!(c.sck_available); + assert!(c.coreaudio_available); + assert!(c.reason.contains("CoreAudio process tap")); + } + + #[test] + fn classify_macos_15_is_supported() { + let c = classify_support(Some((15, 0, 0))); + assert!(c.supported); + assert!(c.sck_available); + assert!(c.coreaudio_available); + } +} diff --git a/fluxer_desktop/native/mac-app-audio/src/pcm_pool.rs b/fluxer_desktop/native/mac-app-audio/src/pcm_pool.rs new file mode 100644 index 0000000..3c14fb1 --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/src/pcm_pool.rs @@ -0,0 +1,463 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use parking_lot::Mutex; +use std::cell::UnsafeCell; +use std::fmt; +use std::sync::Arc; +use std::sync::atomic::{AtomicU32, AtomicU64, Ordering}; + +pub const PCM_POOL_CAP: usize = 16; +pub const PCM_SLOT_SAMPLES_MAX: usize = 16_384; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PcmPoolError { + ZeroCapacity, + ZeroSamplesPerSlot, + SamplesPerSlotTooLarge(usize), + PayloadTooLarge { offered: usize, capacity: usize }, +} + +impl fmt::Display for PcmPoolError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ZeroCapacity => write!(f, "PcmFramePool capacity must be > 0"), + Self::ZeroSamplesPerSlot => write!(f, "PcmFramePool samples_per_slot must be > 0"), + Self::SamplesPerSlotTooLarge(n) => write!( + f, + "PcmFramePool samples_per_slot {n} exceeds PCM_SLOT_SAMPLES_MAX={PCM_SLOT_SAMPLES_MAX}" + ), + Self::PayloadTooLarge { offered, capacity } => write!( + f, + "PcmFramePool payload {offered} samples exceeds slot capacity {capacity}" + ), + } + } +} + +impl std::error::Error for PcmPoolError {} + +struct PcmSlotCell { + inner: UnsafeCell>, +} + +unsafe impl Send for PcmSlotCell {} +unsafe impl Sync for PcmSlotCell {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PcmPoolStats { + pub acquired: u64, + pub released: u64, + pub dropped: u64, + pub in_flight: u32, +} + +pub(crate) struct PcmFramePoolInner { + slots: Vec, + free: Mutex>, + capacity: u32, + samples_per_slot: u32, + acquired_total: AtomicU64, + released_total: AtomicU64, + dropped_total: AtomicU64, + in_flight: AtomicU32, +} + +pub struct PcmFramePool { + inner: Arc, +} + +impl fmt::Debug for PcmFramePool { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let stats = self.stats(); + f.debug_struct("PcmFramePool") + .field("capacity", &self.inner.capacity) + .field("samples_per_slot", &self.inner.samples_per_slot) + .field("stats", &stats) + .finish() + } +} + +impl PcmFramePool { + pub fn new(capacity: usize, samples_per_slot: usize) -> Result { + if capacity == 0 { + return Err(PcmPoolError::ZeroCapacity); + } + if samples_per_slot == 0 { + return Err(PcmPoolError::ZeroSamplesPerSlot); + } + if samples_per_slot > PCM_SLOT_SAMPLES_MAX { + return Err(PcmPoolError::SamplesPerSlotTooLarge(samples_per_slot)); + } + assert!(capacity > 0); + assert!(samples_per_slot > 0); + assert!(samples_per_slot <= PCM_SLOT_SAMPLES_MAX); + + let mut slots: Vec = Vec::with_capacity(capacity); + for _ in 0..capacity { + let buf: Box<[f32]> = vec![0.0_f32; samples_per_slot].into_boxed_slice(); + assert_eq!(buf.len(), samples_per_slot); + slots.push(PcmSlotCell { + inner: UnsafeCell::new(buf), + }); + } + assert_eq!(slots.len(), capacity); + + let mut free: Vec = Vec::with_capacity(capacity); + for index in 0..capacity { + free.push(index); + } + assert_eq!(free.len(), capacity); + + let cap_u32 = u32::try_from(capacity).map_err(|_| PcmPoolError::ZeroCapacity)?; + let sps_u32 = + u32::try_from(samples_per_slot).map_err(|_| PcmPoolError::ZeroSamplesPerSlot)?; + let inner = PcmFramePoolInner { + slots, + free: Mutex::new(free), + capacity: cap_u32, + samples_per_slot: sps_u32, + acquired_total: AtomicU64::new(0), + released_total: AtomicU64::new(0), + dropped_total: AtomicU64::new(0), + in_flight: AtomicU32::new(0), + }; + Ok(Self { + inner: Arc::new(inner), + }) + } + + pub fn try_acquire(&self) -> Option { + assert!(self.inner.capacity > 0); + assert!(self.inner.samples_per_slot > 0); + + let mut free = self.inner.free.lock(); + assert!(free.len() <= self.inner.capacity as usize); + let index = match free.pop() { + Some(idx) => idx, + None => { + drop(free); + self.inner.dropped_total.fetch_add(1, Ordering::Relaxed); + return None; + } + }; + assert!(index < self.inner.capacity as usize); + self.inner.acquired_total.fetch_add(1, Ordering::Relaxed); + let after = self.inner.in_flight.fetch_add(1, Ordering::AcqRel) + 1; + assert!(after <= self.inner.capacity); + drop(free); + + Some(PooledPcmFrame { + slot_index: index, + filled_len: 0, + pool: Arc::clone(&self.inner), + }) + } + + pub fn capacity(&self) -> u32 { + let cap = self.inner.capacity; + assert!(cap > 0); + assert!(cap as usize == self.inner.slots.len()); + cap + } + + pub fn samples_per_slot(&self) -> u32 { + let sps = self.inner.samples_per_slot; + assert!(sps > 0); + assert!(sps as usize <= PCM_SLOT_SAMPLES_MAX); + sps + } + + pub fn stats(&self) -> PcmPoolStats { + assert!(self.inner.capacity > 0); + let in_flight = self.inner.in_flight.load(Ordering::Acquire); + assert!(in_flight <= self.inner.capacity); + let acquired = self.inner.acquired_total.load(Ordering::Relaxed); + let released = self.inner.released_total.load(Ordering::Relaxed); + let dropped = self.inner.dropped_total.load(Ordering::Relaxed); + assert!(released <= acquired); + PcmPoolStats { + acquired, + released, + dropped, + in_flight, + } + } +} + +impl Clone for PcmFramePool { + fn clone(&self) -> Self { + Self { + inner: Arc::clone(&self.inner), + } + } +} + +pub struct PooledPcmFrame { + slot_index: usize, + filled_len: usize, + pool: Arc, +} + +impl PooledPcmFrame { + pub fn write(&mut self, samples: &[f32]) -> Result<(), PcmPoolError> { + assert!(self.slot_index < self.pool.capacity as usize); + let cap = self.pool.samples_per_slot as usize; + if samples.len() > cap { + return Err(PcmPoolError::PayloadTooLarge { + offered: samples.len(), + capacity: cap, + }); + } + assert!(samples.len() <= cap); + + let cell = &self.pool.slots[self.slot_index]; + let buf: &mut [f32] = unsafe { &mut *cell.inner.get() }; + assert_eq!(buf.len(), cap); + if !samples.is_empty() { + buf[..samples.len()].copy_from_slice(samples); + } + self.filled_len = samples.len(); + assert!(self.filled_len <= cap); + Ok(()) + } + + pub fn unfilled_mut(&mut self) -> &mut [f32] { + assert!(self.slot_index < self.pool.capacity as usize); + let cap = self.pool.samples_per_slot as usize; + assert!(cap > 0); + let cell = &self.pool.slots[self.slot_index]; + let buf: &mut [f32] = unsafe { &mut *cell.inner.get() }; + assert_eq!(buf.len(), cap); + buf + } + + pub fn set_filled_len(&mut self, len: usize) { + assert!(self.slot_index < self.pool.capacity as usize); + assert!(len <= self.pool.samples_per_slot as usize); + self.filled_len = len; + } + + pub fn data_slice(&self) -> &[f32] { + assert!(self.slot_index < self.pool.capacity as usize); + assert!(self.filled_len <= self.pool.samples_per_slot as usize); + let cell = &self.pool.slots[self.slot_index]; + let buf: &[f32] = unsafe { &*cell.inner.get() }; + &buf[..self.filled_len] + } + + pub fn filled_len(&self) -> usize { + assert!(self.slot_index < self.pool.capacity as usize); + assert!(self.filled_len <= self.pool.samples_per_slot as usize); + self.filled_len + } + + pub fn capacity(&self) -> usize { + let cap = self.pool.samples_per_slot as usize; + assert!(cap > 0); + assert!(cap <= PCM_SLOT_SAMPLES_MAX); + cap + } + + pub fn as_mut_ptr(&mut self) -> *mut f32 { + assert!(self.slot_index < self.pool.capacity as usize); + assert!(self.filled_len <= self.pool.samples_per_slot as usize); + let cell = &self.pool.slots[self.slot_index]; + let buf: &mut [f32] = unsafe { &mut *cell.inner.get() }; + assert_eq!(buf.len(), self.pool.samples_per_slot as usize); + buf.as_mut_ptr() + } + + pub fn into_external_parts(mut self) -> (*mut f32, usize, Self) { + assert!(self.slot_index < self.pool.capacity as usize); + assert!(self.filled_len <= self.pool.samples_per_slot as usize); + let len = self.filled_len; + let ptr = self.as_mut_ptr(); + assert!(!ptr.is_null()); + (ptr, len, self) + } +} + +impl Drop for PooledPcmFrame { + fn drop(&mut self) { + assert!(self.slot_index < self.pool.capacity as usize); + let mut free = self.pool.free.lock(); + assert!(free.len() < self.pool.capacity as usize); + free.push(self.slot_index); + let before = self.pool.in_flight.fetch_sub(1, Ordering::AcqRel); + assert!(before >= 1); + self.pool.released_total.fetch_add(1, Ordering::Relaxed); + drop(free); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::thread; + + const SLOT_SAMPLES: usize = 2_048; + + fn default_pool() -> PcmFramePool { + PcmFramePool::new(PCM_POOL_CAP, SLOT_SAMPLES).expect("default pool builds") + } + + #[test] + fn new_rejects_zero_capacity() { + let err = PcmFramePool::new(0, SLOT_SAMPLES).unwrap_err(); + assert_eq!(err, PcmPoolError::ZeroCapacity); + } + + #[test] + fn new_rejects_zero_samples_per_slot() { + let err = PcmFramePool::new(4, 0).unwrap_err(); + assert_eq!(err, PcmPoolError::ZeroSamplesPerSlot); + } + + #[test] + fn new_rejects_samples_per_slot_above_max() { + let err = PcmFramePool::new(4, PCM_SLOT_SAMPLES_MAX + 1).unwrap_err(); + assert_eq!( + err, + PcmPoolError::SamplesPerSlotTooLarge(PCM_SLOT_SAMPLES_MAX + 1) + ); + } + + #[test] + fn acquire_release_cycle_increments_counters() { + let pool = PcmFramePool::new(4, SLOT_SAMPLES).expect("pool"); + { + let _slot = pool.try_acquire().expect("slot"); + let stats_held = pool.stats(); + assert_eq!(stats_held.acquired, 1); + assert_eq!(stats_held.in_flight, 1); + } + let stats_after = pool.stats(); + assert_eq!(stats_after.acquired, 1); + assert_eq!(stats_after.released, 1); + assert_eq!(stats_after.in_flight, 0); + } + + #[test] + fn pool_exhausts_at_cap_and_counts_drop() { + let pool = default_pool(); + let mut held = Vec::with_capacity(PCM_POOL_CAP); + for _ in 0..PCM_POOL_CAP { + held.push(pool.try_acquire().expect("slot in capacity")); + } + assert!(pool.try_acquire().is_none()); + let stats = pool.stats(); + assert_eq!(stats.dropped, 1); + assert_eq!(stats.acquired as usize, PCM_POOL_CAP); + assert_eq!(stats.in_flight as usize, PCM_POOL_CAP); + } + + #[test] + fn write_then_data_slice_matches_payload() { + let pool = PcmFramePool::new(2, 64).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + let payload = [0.25_f32; 32]; + slot.write(&payload).expect("payload fits"); + assert_eq!(slot.data_slice(), &payload[..]); + assert_eq!(slot.filled_len(), 32); + } + + #[test] + fn write_rejects_payload_larger_than_slot() { + let pool = PcmFramePool::new(2, 64).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + let big = [0.0_f32; 128]; + let err = slot.write(&big).unwrap_err(); + assert!(matches!(err, PcmPoolError::PayloadTooLarge { .. })); + } + + #[test] + fn unfilled_mut_then_set_filled_len_matches_data_slice() { + let pool = PcmFramePool::new(2, 64).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + { + let buf = slot.unfilled_mut(); + assert_eq!(buf.len(), 64); + buf[0] = 0.5; + buf[1] = -0.5; + buf[2] = 1.0; + } + slot.set_filled_len(3); + assert_eq!(slot.filled_len(), 3); + assert_eq!(slot.data_slice(), &[0.5, -0.5, 1.0]); + } + + #[test] + #[should_panic] + fn set_filled_len_rejects_overflow() { + let pool = PcmFramePool::new(1, 32).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + slot.set_filled_len(33); + } + + #[test] + fn into_external_parts_exposes_filled_pointer_and_length() { + let pool = PcmFramePool::new(2, 64).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + let payload = [1.5_f32; 16]; + slot.write(&payload).expect("write fits"); + let (ptr, len, owned) = slot.into_external_parts(); + assert!(!ptr.is_null()); + assert_eq!(len, 16); + let observed = unsafe { core::slice::from_raw_parts(ptr, len) }; + assert_eq!(observed, &payload[..]); + assert_eq!(pool.stats().in_flight, 1); + drop(owned); + assert_eq!(pool.stats().in_flight, 0); + } + + #[test] + fn into_external_parts_drop_returns_slot_to_pool() { + let pool = PcmFramePool::new(1, 32).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + slot.write(&[0.75_f32; 8]).expect("write"); + let (_ptr, _len, owned) = slot.into_external_parts(); + assert!(pool.try_acquire().is_none()); + drop(owned); + let revived = pool.try_acquire().expect("revived"); + assert_eq!(revived.filled_len(), 0); + drop(revived); + assert_eq!(pool.stats().in_flight, 0); + } + + #[test] + fn pooled_frame_survives_send_across_threads() { + let pool = PcmFramePool::new(2, 64).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + slot.write(&[0.5_f32; 32]).expect("write"); + let handle = thread::spawn(move || { + assert_eq!(slot.filled_len(), 32); + assert_eq!(slot.data_slice()[0], 0.5); + drop(slot); + }); + handle.join().expect("worker"); + assert_eq!(pool.stats().in_flight, 0); + assert_eq!(pool.stats().released, 1); + } + + #[test] + fn capacity_one_pool_round_trips() { + let pool = PcmFramePool::new(1, 32).expect("pool"); + for _ in 0..5 { + let mut slot = pool.try_acquire().expect("slot"); + slot.write(&[1.0, 2.0, 3.0]).expect("write"); + assert_eq!(slot.data_slice(), &[1.0, 2.0, 3.0]); + drop(slot); + } + let stats = pool.stats(); + assert_eq!(stats.acquired, 5); + assert_eq!(stats.released, 5); + assert_eq!(stats.in_flight, 0); + } + + #[test] + fn default_pool_dimensions_match_constants() { + let pool = default_pool(); + assert_eq!(pool.capacity() as usize, PCM_POOL_CAP); + assert_eq!(pool.samples_per_slot() as usize, SLOT_SAMPLES); + } +} diff --git a/fluxer_desktop/native/mac-app-audio/src/process_tree.rs b/fluxer_desktop/native/mac-app-audio/src/process_tree.rs new file mode 100644 index 0000000..529fa25 --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/src/process_tree.rs @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub struct Info { + pub pid: i32, + pub parent_pid: i32, + pub process_group_id: i32, +} + +pub type ResolverFn = fn(ctx: Option<&dyn ResolverCtx>, pid: i32) -> Option; + +pub trait ResolverCtx { + fn resolve(&self, pid: i32) -> Option; +} + +#[cfg(target_os = "macos")] +mod sys { + use super::Info; + use std::mem::{offset_of, size_of}; + + const PROC_PIDTBSDINFO: i32 = 3; + const MAXCOMLEN: usize = 16; + + #[repr(C)] + #[derive(Default, Copy, Clone)] + pub struct ProcBsdInfo { + pub pbi_flags: u32, + pub pbi_status: u32, + pub pbi_xstatus: u32, + pub pbi_pid: u32, + pub pbi_ppid: u32, + pub pbi_uid: u32, + pub pbi_gid: u32, + pub pbi_ruid: u32, + pub pbi_rgid: u32, + pub pbi_svuid: u32, + pub pbi_svgid: u32, + pub rfu_1: u32, + pub pbi_comm: [u8; MAXCOMLEN], + pub pbi_name: [u8; 2 * MAXCOMLEN], + pub pbi_nfiles: u32, + pub pbi_pgid: u32, + pub pbi_pjobc: u32, + pub e_tdev: u32, + pub e_tpgid: u32, + pub pbi_nice: i32, + pub pbi_start_tvsec: u64, + pub pbi_start_tvusec: u64, + } + + unsafe extern "C" { + fn proc_pidinfo( + pid: i32, + flavor: i32, + arg: u64, + buffer: *mut core::ffi::c_void, + buffersize: i32, + ) -> i32; + pub fn proc_listallpids(buffer: *mut core::ffi::c_void, buffersize: i32) -> i32; + } + + pub fn info_for_pid(pid: i32) -> Option { + if pid <= 0 { + return None; + } + let mut raw = ProcBsdInfo::default(); + let copied = unsafe { + proc_pidinfo( + pid, + PROC_PIDTBSDINFO, + 0, + (&raw mut raw) as *mut _ as *mut _, + size_of::() as i32, + ) + }; + let min = (offset_of!(ProcBsdInfo, pbi_pgid) + size_of::()) as i32; + if copied < min { + return None; + } + Some(Info { + pid: raw.pbi_pid as i32, + parent_pid: raw.pbi_ppid as i32, + process_group_id: raw.pbi_pgid as i32, + }) + } +} + +#[cfg(target_os = "macos")] +pub fn info_for_pid(pid: i32) -> Option { + sys::info_for_pid(pid) +} + +#[cfg(not(target_os = "macos"))] +pub fn info_for_pid(_pid: i32) -> Option { + None +} + +fn live_resolver(_ctx: Option<&dyn ResolverCtx>, pid: i32) -> Option { + info_for_pid(pid) +} + +pub fn is_same_launch_tree(candidate_pid: i32, target_pid: i32, target_info: Option) -> bool { + is_same_launch_tree_with_resolver(candidate_pid, target_pid, target_info, None, live_resolver) +} + +pub fn is_same_launch_tree_with_resolver( + candidate_pid: i32, + target_pid: i32, + target_info: Option, + ctx: Option<&dyn ResolverCtx>, + resolver: ResolverFn, +) -> bool { + if candidate_pid <= 0 || target_pid <= 0 { + return false; + } + if candidate_pid == target_pid { + return true; + } + + let mut current = match resolver(ctx, candidate_pid) { + Some(i) => i, + None => return false, + }; + if shares_process_group(current, target_pid, target_info) { + return true; + } + + let mut depth = 0usize; + while depth < 64 { + let parent = current.parent_pid; + if parent == target_pid { + return true; + } + if parent <= 1 || parent == current.pid { + return false; + } + current = match resolver(ctx, parent) { + Some(i) => i, + None => return false, + }; + depth += 1; + } + false +} + +fn shares_process_group(candidate: Info, target_pid: i32, target_info: Option) -> bool { + let cg = candidate.process_group_id; + if cg <= 0 { + return false; + } + if cg == target_pid { + return true; + } + if let Some(t) = target_info { + return t.process_group_id > 0 && cg == t.process_group_id; + } + false +} + +fn append_pid(out: &mut Vec, max: usize, pid: i32) -> bool { + if out.contains(&pid) { + return true; + } + if out.len() >= max { + return false; + } + out.push(pid); + true +} + +pub fn collect_related_pids_with_resolver( + target_pid: i32, + target_info: Option, + candidates: &[i32], + max_count: usize, + ctx: Option<&dyn ResolverCtx>, + resolver: ResolverFn, +) -> Vec { + if target_pid <= 0 || max_count == 0 { + return Vec::new(); + } + let cap = candidates.len().max(1).min(max_count); + let mut out: Vec = Vec::with_capacity(cap); + let _ = append_pid(&mut out, cap, target_pid); + for &pid in candidates { + if out.len() >= cap { + break; + } + if pid <= 0 || pid == target_pid { + continue; + } + if !is_same_launch_tree_with_resolver(pid, target_pid, target_info, ctx, resolver) { + continue; + } + let _ = append_pid(&mut out, cap, pid); + } + out +} + +#[cfg(target_os = "macos")] +pub fn collect_related_pids(target_pid: i32, max_count: usize) -> Vec { + if target_pid <= 0 { + return Vec::new(); + } + let reported = unsafe { sys::proc_listallpids(core::ptr::null_mut(), 0) }; + if reported <= 0 { + return vec![target_pid]; + } + let mut all_pids: Vec = vec![0; reported as usize]; + let copied = unsafe { + sys::proc_listallpids( + all_pids.as_mut_ptr() as *mut _, + (all_pids.len() * core::mem::size_of::()) as i32, + ) + }; + if copied <= 0 { + return vec![target_pid]; + } + let count = (copied as usize).min(all_pids.len()); + collect_related_pids_with_resolver( + target_pid, + info_for_pid(target_pid), + &all_pids[..count], + max_count, + None, + live_resolver, + ) +} + +#[cfg(not(target_os = "macos"))] +pub fn collect_related_pids(target_pid: i32, _max_count: usize) -> Vec { + if target_pid <= 0 { + return Vec::new(); + } + vec![target_pid] +} + +#[cfg(test)] +mod tests { + use super::*; + + struct StaticCtx<'a> { + infos: &'a [Info], + } + impl<'a> ResolverCtx for StaticCtx<'a> { + fn resolve(&self, pid: i32) -> Option { + self.infos.iter().copied().find(|i| i.pid == pid) + } + } + + fn ctx_resolver(ctx: Option<&dyn ResolverCtx>, pid: i32) -> Option { + ctx?.resolve(pid) + } + + #[test] + fn same_launch_tree_includes_direct_child() { + let infos = [ + Info { + pid: 100, + parent_pid: 1, + process_group_id: 100, + }, + Info { + pid: 101, + parent_pid: 100, + process_group_id: 100, + }, + ]; + let ctx = StaticCtx { infos: &infos }; + assert!(is_same_launch_tree_with_resolver( + 101, + 100, + Some(infos[0]), + Some(&ctx), + ctx_resolver + )); + } + + #[test] + fn same_launch_tree_includes_pgrp_peer() { + let infos = [ + Info { + pid: 200, + parent_pid: 1, + process_group_id: 200, + }, + Info { + pid: 201, + parent_pid: 1, + process_group_id: 200, + }, + ]; + let ctx = StaticCtx { infos: &infos }; + assert!(is_same_launch_tree_with_resolver( + 201, + 200, + Some(infos[0]), + Some(&ctx), + ctx_resolver + )); + } + + #[test] + fn same_launch_tree_excludes_unrelated() { + let infos = [ + Info { + pid: 300, + parent_pid: 1, + process_group_id: 300, + }, + Info { + pid: 301, + parent_pid: 1, + process_group_id: 301, + }, + ]; + let ctx = StaticCtx { infos: &infos }; + assert!(!is_same_launch_tree_with_resolver( + 301, + 300, + Some(infos[0]), + Some(&ctx), + ctx_resolver + )); + } + + #[test] + fn collect_related_returns_tree() { + let infos = [ + Info { + pid: 400, + parent_pid: 1, + process_group_id: 400, + }, + Info { + pid: 401, + parent_pid: 400, + process_group_id: 400, + }, + Info { + pid: 402, + parent_pid: 401, + process_group_id: 400, + }, + Info { + pid: 500, + parent_pid: 1, + process_group_id: 500, + }, + ]; + let candidates = [500i32, 401, 402, 400]; + let ctx = StaticCtx { infos: &infos }; + let pids = collect_related_pids_with_resolver( + 400, + Some(infos[0]), + &candidates, + 8, + Some(&ctx), + ctx_resolver, + ); + assert_eq!(pids, vec![400, 401, 402]); + } +} diff --git a/fluxer_desktop/native/mac-app-audio/src/related_app.rs b/fluxer_desktop/native/mac-app-audio/src/related_app.rs new file mode 100644 index 0000000..f94508e --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/src/related_app.rs @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub fn trim_ascii_whitespace(value: &str) -> &str { + value.trim_matches(|c: char| matches!(c, ' ' | '\t' | '\r' | '\n')) +} + +pub fn has_prefix_with_suffix(value: &str, prefix: &str, suffix: &str) -> bool { + let pb = prefix.as_bytes(); + let sb = suffix.as_bytes(); + let vb = value.as_bytes(); + if vb.len() < pb.len() + sb.len() { + return false; + } + &vb[..pb.len()] == pb && &vb[pb.len()..pb.len() + sb.len()] == sb +} + +pub fn related_by_prefix_either_way(a: &str, b: &str, suffix: &str) -> bool { + has_prefix_with_suffix(a, b, suffix) || has_prefix_with_suffix(b, a, suffix) +} + +pub fn helper_bundle_base(value: &str) -> &str { + let suffixes = [".helper", ".Helper", "-helper", "-Helper"]; + for suffix in suffixes { + if let Some(idx) = value.rfind(suffix) { + if idx == 0 { + continue; + } + + if value[..idx].find('.').is_none() { + continue; + } + let after = idx + suffix.len(); + let bytes = value.as_bytes(); + if after == bytes.len() || bytes[after] == b'.' || bytes[after] == b'-' { + return &value[..idx]; + } + } + } + value +} + +pub fn helper_name_base(value: &str) -> &str { + let trimmed = trim_ascii_whitespace(value); + if let Some(idx) = trimmed.find(" Helper") { + if idx > 0 { + return trim_ascii_whitespace(&trimmed[..idx]); + } + } + trimmed +} + +pub fn looks_related_by_strings( + candidate_bundle: &str, + target_bundle: &str, + candidate_name: &str, + target_name: &str, +) -> bool { + if !target_bundle.is_empty() && !candidate_bundle.is_empty() { + let tb = helper_bundle_base(target_bundle); + let cb = helper_bundle_base(candidate_bundle); + if candidate_bundle == target_bundle + || cb == tb + || related_by_prefix_either_way(candidate_bundle, target_bundle, ".") + || related_by_prefix_either_way(candidate_bundle, target_bundle, "-") + { + return true; + } + } + let tn = trim_ascii_whitespace(target_name); + let cn = trim_ascii_whitespace(candidate_name); + if !tn.is_empty() { + let tnb = helper_name_base(tn); + let cnb = helper_name_base(cn); + if cn == tn + || cnb == tnb + || related_by_prefix_either_way(cn, tn, " ") + || related_by_prefix_either_way(cn, tn, " Helper") + { + return true; + } + } + false +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn helper_bundle_strips_suffix() { + assert_eq!( + "com.example.app", + helper_bundle_base("com.example.app.helper") + ); + assert_eq!( + "com.example.app", + helper_bundle_base("com.example.app.Helper") + ); + assert_eq!( + "com.example.app", + helper_bundle_base("com.example.app-helper") + ); + + assert_eq!("helper", helper_bundle_base("helper")); + + assert_eq!( + "com.example.app", + helper_bundle_base("com.example.app.helper.Plugin") + ); + } + + #[test] + fn helper_name_strips_helper_suffix() { + assert_eq!("Example", helper_name_base("Example Helper")); + assert_eq!("Example", helper_name_base("Example Helper (Renderer)")); + assert_eq!("Example", helper_name_base(" Example ")); + assert_eq!("Foo", helper_name_base("Foo")); + } + + #[test] + fn related_via_bundle_helper_base() { + assert!(looks_related_by_strings( + "com.example.app.helper", + "com.example.app", + "", + "" + )); + assert!(looks_related_by_strings( + "com.example.app", + "com.example.app.Helper", + "", + "" + )); + } + + #[test] + fn related_via_name_helper_base() { + assert!(looks_related_by_strings( + "", + "", + "Example Helper", + "Example" + )); + } + + #[test] + fn unrelated_returns_false() { + assert!(!looks_related_by_strings( + "com.firefox.app", + "com.chrome.app", + "Firefox", + "Chrome" + )); + } + + #[test] + fn related_by_dot_prefix() { + assert!(looks_related_by_strings( + "com.example.app.renderer", + "com.example.app", + "", + "" + )); + } +} diff --git a/fluxer_desktop/native/mac-app-audio/src/sck.rs b/fluxer_desktop/native/mac-app-audio/src/sck.rs new file mode 100644 index 0000000..cf108cf --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/src/sck.rs @@ -0,0 +1,181 @@ +#![allow(non_snake_case)] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +use objc2::rc::Retained; +use objc2_core_foundation::CGRect; +use objc2_core_media::{CMTime, CMTimeFlags}; +use objc2_foundation::NSString; +use objc2_screen_capture_kit::{ + SCDisplay, SCRunningApplication, SCStream, SCStreamConfiguration, SCWindow, +}; + +pub use objc2_screen_capture_kit::SCStreamOutputType; + +pub fn cgrect_standardized(r: CGRect) -> CGRect { + let mut out = r; + if out.size.width < 0.0 { + out.origin.x += out.size.width; + out.size.width = -out.size.width; + } + if out.size.height < 0.0 { + out.origin.y += out.size.height; + out.size.height = -out.size.height; + } + out +} + +pub fn cgrect_intersection_area(a_raw: CGRect, b_raw: CGRect) -> f64 { + let a = cgrect_standardized(a_raw); + let b = cgrect_standardized(b_raw); + if a.size.width <= 0.0 || a.size.height <= 0.0 || b.size.width <= 0.0 || b.size.height <= 0.0 { + return 0.0; + } + let ax2 = a.origin.x + a.size.width; + let ay2 = a.origin.y + a.size.height; + let bx2 = b.origin.x + b.size.width; + let by2 = b.origin.y + b.size.height; + let x1 = a.origin.x.max(b.origin.x); + let y1 = a.origin.y.max(b.origin.y); + let x2 = ax2.min(bx2); + let y2 = ay2.min(by2); + if x2 <= x1 || y2 <= y1 { + return 0.0; + } + (x2 - x1) * (y2 - y1) +} + +pub fn cmtime_seconds(value: i64, timescale: i32) -> CMTime { + CMTime { + value, + timescale, + flags: CMTimeFlags(1), + epoch: 0, + } +} + +pub fn sc_running_application_process_id(app: &SCRunningApplication) -> i32 { + unsafe { app.processID() } +} + +pub fn sc_running_application_bundle_identifier(app: &SCRunningApplication) -> Retained { + unsafe { app.bundleIdentifier() } +} + +pub fn sc_running_application_name(app: &SCRunningApplication) -> Retained { + unsafe { app.applicationName() } +} + +pub fn sc_display_frame(display: &SCDisplay) -> CGRect { + unsafe { display.frame() } +} + +pub fn sc_window_owning_application(win: &SCWindow) -> Option> { + unsafe { win.owningApplication() } +} + +pub fn sc_window_frame(win: &SCWindow) -> CGRect { + unsafe { win.frame() } +} + +pub fn cfg_set_captures_audio(cfg: &SCStreamConfiguration, v: bool) { + unsafe { cfg.setCapturesAudio(v) } +} +pub fn cfg_set_excludes_current_process_audio(cfg: &SCStreamConfiguration, v: bool) { + unsafe { cfg.setExcludesCurrentProcessAudio(v) } +} +pub fn cfg_set_sample_rate(cfg: &SCStreamConfiguration, v: isize) { + unsafe { cfg.setSampleRate(v) } +} +pub fn cfg_set_channel_count(cfg: &SCStreamConfiguration, v: isize) { + unsafe { cfg.setChannelCount(v) } +} +pub fn cfg_set_queue_depth(cfg: &SCStreamConfiguration, v: isize) { + unsafe { cfg.setQueueDepth(v) } +} +pub fn cfg_set_width(cfg: &SCStreamConfiguration, v: usize) { + unsafe { cfg.setWidth(v) } +} +pub fn cfg_set_height(cfg: &SCStreamConfiguration, v: usize) { + unsafe { cfg.setHeight(v) } +} +pub fn cfg_set_shows_cursor(cfg: &SCStreamConfiguration, v: bool) { + unsafe { cfg.setShowsCursor(v) } +} +pub fn cfg_set_minimum_frame_interval(cfg: &SCStreamConfiguration, t: CMTime) { + unsafe { cfg.setMinimumFrameInterval(t) } +} + +pub fn cfg_set_capture_dynamic_range_sdr_if_available(cfg: &SCStreamConfiguration) { + use objc2::runtime::NSObjectProtocol; + use objc2::sel; + let obj: &objc2::runtime::NSObject = cfg.as_ref(); + if obj.respondsToSelector(sel!(setCaptureDynamicRange:)) { + unsafe { + cfg.setCaptureDynamicRange(objc2_screen_capture_kit::SCCaptureDynamicRange(0)); + } + } +} + +pub fn cfg_set_stream_name_if_available(cfg: &SCStreamConfiguration, name: &NSString) { + use objc2::runtime::NSObjectProtocol; + use objc2::sel; + let obj: &objc2::runtime::NSObject = cfg.as_ref(); + if obj.respondsToSelector(sel!(setStreamName:)) { + unsafe { + cfg.setStreamName(Some(name)); + } + } +} + +pub fn sc_stream_add_stream_output( + stream: &SCStream, + output: &objc2::runtime::ProtocolObject, + kind: SCStreamOutputType, + queue: Option<&dispatch2::DispatchQueue>, +) -> Result<(), Retained> { + unsafe { stream.addStreamOutput_type_sampleHandlerQueue_error(output, kind, queue) } +} + +#[cfg(test)] +mod tests { + use super::*; + use objc2_core_foundation::{CGPoint, CGSize}; + + #[test] + fn cgrect_intersection_handles_negative_and_disjoint() { + let a = CGRect { + origin: CGPoint { x: 0.0, y: 0.0 }, + size: CGSize { + width: 10.0, + height: 10.0, + }, + }; + let b = CGRect { + origin: CGPoint { x: 5.0, y: 5.0 }, + size: CGSize { + width: 10.0, + height: 10.0, + }, + }; + assert_eq!(25.0, cgrect_intersection_area(a, b)); + + let c = CGRect { + origin: CGPoint { x: 10.0, y: 10.0 }, + size: CGSize { + width: -5.0, + height: -5.0, + }, + }; + assert_eq!(25.0, cgrect_intersection_area(a, c)); + + let d = CGRect { + origin: CGPoint { x: 20.0, y: 20.0 }, + size: CGSize { + width: 2.0, + height: 2.0, + }, + }; + assert_eq!(0.0, cgrect_intersection_area(a, d)); + } +} diff --git a/fluxer_desktop/native/mac-app-audio/src/sck_async.rs b/fluxer_desktop/native/mac-app-audio/src/sck_async.rs new file mode 100644 index 0000000..fc1b527 --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/src/sck_async.rs @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::sync::{Arc, Condvar, Mutex, MutexGuard}; +use std::time::Duration; + +use block2::RcBlock; +use objc2::rc::Retained; +use objc2_foundation::NSError; +use objc2_screen_capture_kit::{SCShareableContent, SCStream}; + +pub const DEFAULT_TIMEOUT_NS: u64 = 30 * 1_000_000_000; + +#[derive(Debug, Eq, PartialEq)] +pub enum AsyncError { + AsyncTimedOut, + SCKReturnedError, +} + +struct WakerInner { + state: Mutex, + cv: Condvar, +} + +struct WakerState { + done: bool, + failed: bool, + err: Option>, + content: Option>, +} + +unsafe impl Send for WakerInner {} +unsafe impl Sync for WakerInner {} + +fn new_waker() -> Arc { + Arc::new(WakerInner { + state: Mutex::new(WakerState { + done: false, + failed: false, + err: None, + content: None, + }), + cv: Condvar::new(), + }) +} + +fn lock_state(w: &WakerInner) -> MutexGuard<'_, WakerState> { + w.state + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()) +} + +fn wait_deadline(w: &Arc, timeout_ns: u64) -> bool { + let s = lock_state(w); + if s.done { + return true; + } + let dur = Duration::from_nanos(timeout_ns); + match w.cv.wait_timeout(s, dur) { + Ok((state, _)) => state.done, + Err(poisoned) => poisoned.into_inner().0.done, + } +} + +fn retain_error(err: *mut NSError) -> Result>, AsyncError> { + if err.is_null() { + Ok(None) + } else { + unsafe { Retained::retain(err) } + .map(Some) + .ok_or(AsyncError::SCKReturnedError) + } +} + +fn retain_content( + content: *mut SCShareableContent, +) -> Result>, AsyncError> { + if content.is_null() { + Ok(None) + } else { + unsafe { Retained::retain(content) } + .map(Some) + .ok_or(AsyncError::SCKReturnedError) + } +} + +fn complete( + waker: &WakerInner, + err: Option>, + content: Option>, + failed: bool, +) { + let mut s = lock_state(waker); + s.err = err; + s.content = content; + s.failed = failed; + s.done = true; + waker.cv.notify_all(); +} + +pub fn await_ns_error_block_start(stream: &SCStream, timeout_ns: u64) -> Result<(), AsyncError> { + let waker = new_waker(); + let waker_cb = waker.clone(); + let blk = RcBlock::new(move |err: *mut NSError| { + let (err_opt, failed) = match retain_error(err) { + Ok(err_opt) => (err_opt, false), + Err(_) => (None, true), + }; + complete(&waker_cb, err_opt, None, failed); + }); + + unsafe { + stream.startCaptureWithCompletionHandler(Some(&blk)); + } + + if !wait_deadline(&waker, timeout_ns) { + return Err(AsyncError::AsyncTimedOut); + } + let s = lock_state(&waker); + if s.failed || s.err.is_some() { + return Err(AsyncError::SCKReturnedError); + } + Ok(()) +} + +pub fn await_ns_error_block_stop(stream: &SCStream, timeout_ns: u64) -> Result<(), AsyncError> { + let waker = new_waker(); + let waker_cb = waker.clone(); + let blk = RcBlock::new(move |err: *mut NSError| { + let (err_opt, failed) = match retain_error(err) { + Ok(err_opt) => (err_opt, false), + Err(_) => (None, true), + }; + complete(&waker_cb, err_opt, None, failed); + }); + + unsafe { + stream.stopCaptureWithCompletionHandler(Some(&blk)); + } + + if !wait_deadline(&waker, timeout_ns) { + return Err(AsyncError::AsyncTimedOut); + } + let s = lock_state(&waker); + if s.failed || s.err.is_some() { + return Err(AsyncError::SCKReturnedError); + } + Ok(()) +} + +pub fn start_capture(stream: &SCStream, timeout_ns: u64) -> Result<(), AsyncError> { + await_ns_error_block_start(stream, timeout_ns) +} + +pub fn stop_capture(stream: &SCStream, timeout_ns: u64) -> Result<(), AsyncError> { + await_ns_error_block_stop(stream, timeout_ns) +} + +pub struct ShareableContent { + pub content: Retained, +} + +pub fn get_shareable_content( + excluding_desktop_windows: bool, + on_screen_windows_only: bool, + timeout_ns: u64, +) -> Result { + let waker = new_waker(); + let waker_cb = waker.clone(); + let blk = RcBlock::new(move |content: *mut SCShareableContent, err: *mut NSError| { + let (err_opt, err_failed) = match retain_error(err) { + Ok(err_opt) => (err_opt, false), + Err(_) => (None, true), + }; + let (content_opt, content_failed) = match retain_content(content) { + Ok(content_opt) => (content_opt, false), + Err(_) => (None, true), + }; + complete( + &waker_cb, + err_opt, + content_opt, + err_failed || content_failed, + ); + }); + + unsafe { + SCShareableContent::getShareableContentExcludingDesktopWindows_onScreenWindowsOnly_completionHandler( + excluding_desktop_windows, + on_screen_windows_only, + &blk, + ); + } + + if !wait_deadline(&waker, timeout_ns) { + return Err(AsyncError::AsyncTimedOut); + } + let mut s = lock_state(&waker); + if s.failed || s.err.is_some() { + return Err(AsyncError::SCKReturnedError); + } + match s.content.take() { + Some(content) => Ok(ShareableContent { content }), + None => Err(AsyncError::SCKReturnedError), + } +} diff --git a/fluxer_desktop/native/mac-app-audio/src/source_state.rs b/fluxer_desktop/native/mac-app-audio/src/source_state.rs new file mode 100644 index 0000000..ac3a3ab --- /dev/null +++ b/fluxer_desktop/native/mac-app-audio/src/source_state.rs @@ -0,0 +1,230 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::sync::atomic::{AtomicU32, Ordering}; + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +#[repr(u32)] +pub enum State { + Idle = 0, + Starting = 1, + Running = 2, + Stopping = 3, + Stopped = 4, +} + +impl State { + fn from_u32(v: u32) -> State { + match v { + 0 => State::Idle, + 1 => State::Starting, + 2 => State::Running, + 3 => State::Stopping, + _ => State::Stopped, + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum TransitionError { + IllegalTransition, + DoubleStart, + StopBeforeStart, + StartWhileStopping, +} + +pub fn is_allowed(from: State, to: State) -> bool { + match from { + State::Idle => matches!(to, State::Starting | State::Stopped), + State::Starting => matches!(to, State::Running | State::Stopped), + State::Running => matches!(to, State::Stopping | State::Stopped), + State::Stopping => matches!(to, State::Stopped), + State::Stopped => false, + } +} + +pub struct Machine { + state: AtomicU32, +} + +impl Machine { + pub fn new() -> Self { + Self { + state: AtomicU32::new(State::Idle as u32), + } + } + + pub fn current(&self) -> State { + State::from_u32(self.state.load(Ordering::Acquire)) + } + + fn cas(&self, from: State, to: State) -> Result<(), TransitionError> { + if !is_allowed(from, to) { + return Err(TransitionError::IllegalTransition); + } + self.state + .compare_exchange(from as u32, to as u32, Ordering::AcqRel, Ordering::Acquire) + .map(|_| ()) + .map_err(|_| TransitionError::IllegalTransition) + } + + pub fn request_start(&self) -> Result<(), TransitionError> { + match self.current() { + State::Idle => self.cas(State::Idle, State::Starting), + State::Starting | State::Running => Err(TransitionError::DoubleStart), + State::Stopping => Err(TransitionError::StartWhileStopping), + State::Stopped => Err(TransitionError::IllegalTransition), + } + } + + pub fn mark_running(&self) -> Result<(), TransitionError> { + self.cas(State::Starting, State::Running) + } + + pub fn request_stop(&self) -> Result<(), TransitionError> { + match self.current() { + State::Running => self.cas(State::Running, State::Stopping), + State::Idle => Err(TransitionError::StopBeforeStart), + State::Starting | State::Stopping | State::Stopped => { + Err(TransitionError::IllegalTransition) + } + } + } + + pub fn mark_stopped(&self) -> Result<(), TransitionError> { + self.cas(State::Stopping, State::Stopped) + } + + pub fn cancel_idle(&self) -> Result<(), TransitionError> { + self.cas(State::Idle, State::Stopped) + } + + pub fn mark_fatal(&self) -> State { + loop { + let raw = self.state.load(Ordering::Acquire); + let prev = State::from_u32(raw); + if prev == State::Stopped { + return prev; + } + if self + .state + .compare_exchange( + raw, + State::Stopped as u32, + Ordering::AcqRel, + Ordering::Acquire, + ) + .is_ok() + { + return prev; + } + } + } +} + +impl Default for Machine { + fn default() -> Self { + Self::new() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn is_allowed_exhaustive() { + let all = [ + State::Idle, + State::Starting, + State::Running, + State::Stopping, + State::Stopped, + ]; + let allowed: &[(State, State)] = &[ + (State::Idle, State::Starting), + (State::Idle, State::Stopped), + (State::Starting, State::Running), + (State::Starting, State::Stopped), + (State::Running, State::Stopping), + (State::Running, State::Stopped), + (State::Stopping, State::Stopped), + ]; + for &from in &all { + for &to in &all { + let expected = allowed.iter().any(|p| p.0 == from && p.1 == to); + assert_eq!(expected, is_allowed(from, to), "{:?} -> {:?}", from, to); + } + } + } + + #[test] + fn happy_path() { + let m = Machine::new(); + assert_eq!(State::Idle, m.current()); + m.request_start().unwrap(); + assert_eq!(State::Starting, m.current()); + m.mark_running().unwrap(); + assert_eq!(State::Running, m.current()); + m.request_stop().unwrap(); + assert_eq!(State::Stopping, m.current()); + m.mark_stopped().unwrap(); + assert_eq!(State::Stopped, m.current()); + } + + #[test] + fn double_start_rejected() { + let m = Machine::new(); + m.request_start().unwrap(); + assert_eq!(Err(TransitionError::DoubleStart), m.request_start()); + m.mark_running().unwrap(); + assert_eq!(Err(TransitionError::DoubleStart), m.request_start()); + } + + #[test] + fn stop_before_start_rejected() { + let m = Machine::new(); + assert_eq!(Err(TransitionError::StopBeforeStart), m.request_stop()); + } + + #[test] + fn start_while_stopping_rejected() { + let m = Machine::new(); + m.request_start().unwrap(); + m.mark_running().unwrap(); + m.request_stop().unwrap(); + assert_eq!(Err(TransitionError::StartWhileStopping), m.request_start()); + } + + #[test] + fn cancel_idle_short_circuits() { + let m = Machine::new(); + m.cancel_idle().unwrap(); + assert_eq!(State::Stopped, m.current()); + assert_eq!(Err(TransitionError::IllegalTransition), m.request_start()); + } + + #[test] + fn mark_fatal_forces_stopped() { + for &start in &[ + State::Idle, + State::Starting, + State::Running, + State::Stopping, + ] { + let m = Machine::new(); + m.state.store(start as u32, Ordering::Release); + let prev = m.mark_fatal(); + assert_eq!(start, prev); + assert_eq!(State::Stopped, m.current()); + } + } + + #[test] + fn mark_fatal_idempotent() { + let m = Machine::new(); + m.state.store(State::Stopped as u32, Ordering::Release); + let prev = m.mark_fatal(); + assert_eq!(State::Stopped, prev); + assert_eq!(State::Stopped, m.current()); + } +} diff --git a/fluxer_desktop/native/mac-clipboard/Cargo.lock b/fluxer_desktop/native/mac-clipboard/Cargo.lock new file mode 100644 index 0000000..42aa08c --- /dev/null +++ b/fluxer_desktop/native/mac-clipboard/Cargo.lock @@ -0,0 +1,344 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags", + "block2", + "libc", + "objc2", +] + +[[package]] +name = "fluxer_mac_clipboard" +version = "0.0.0" +dependencies = [ + "dispatch2", + "napi", + "napi-build", + "napi-derive", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags", + "objc2", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" diff --git a/fluxer_desktop/native/mac-clipboard/Cargo.toml b/fluxer_desktop/native/mac-clipboard/Cargo.toml new file mode 100644 index 0000000..1d0f698 --- /dev/null +++ b/fluxer_desktop/native/mac-clipboard/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "fluxer_mac_clipboard" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8"]} +napi-derive = "3.5.6" + +[target.'cfg(target_os = "macos")'.dependencies] +dispatch2 = "0.3.1" +objc2 = "0.6.4" +objc2-app-kit = {version = "0.3.2", default-features = false, features = ["NSPasteboard", "std"]} +objc2-foundation = {version = "0.3.2", default-features = false, features = ["NSArray", "NSObject", "NSString", "NSURL", "std"]} + +[build-dependencies] +napi-build = "2.3.2" diff --git a/fluxer_desktop/native/mac-clipboard/build.rs b/fluxer_desktop/native/mac-clipboard/build.rs new file mode 100644 index 0000000..d45de40 --- /dev/null +++ b/fluxer_desktop/native/mac-clipboard/build.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + napi_build::setup(); +} diff --git a/fluxer_desktop/native/mac-clipboard/index.d.ts b/fluxer_desktop/native/mac-clipboard/index.d.ts new file mode 100644 index 0000000..e4e99aa --- /dev/null +++ b/fluxer_desktop/native/mac-clipboard/index.d.ts @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +export declare const writeFileReferenceToClipboard: ((filePath: string) => Promise) | null; +export declare const loadError: Error | null; diff --git a/fluxer_desktop/native/mac-clipboard/index.js b/fluxer_desktop/native/mac-clipboard/index.js new file mode 100644 index 0000000..0b1fdbd --- /dev/null +++ b/fluxer_desktop/native/mac-clipboard/index.js @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync} = require('node:fs'); +const {join, sep} = require('node:path'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); +const MODULE_NAME = '@fluxer/mac-clipboard'; +const SKIP_NATIVE_PROBE_ENV = 'FLUXER_MAC_CLIPBOARD_SKIP_NATIVE_PROBE'; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName() { + if (process.platform !== 'darwin') { + throw new Error(`@fluxer/mac-clipboard is only supported on macOS, got ${process.platform}`); + } + switch (process.arch) { + case 'x64': + return 'mac-clipboard.darwin-x64.node'; + case 'arm64': + return 'mac-clipboard.darwin-arm64.node'; + default: + throw new Error(`Unsupported macOS architecture: ${process.arch}`); + } +} + +let binding = null; +let loadError = null; + +if (process.platform === 'darwin') { + try { + const nativeRoot = resolveNativeRoot(); + const nativePath = join(nativeRoot, nativeFileName()); + const loaded = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + binding = loaded.binding; + loadError = loaded.loadError; + if (loadError) throw loadError; + } catch (error) { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: 'native loader threw before binding load completed', + cause: error, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + throw loadError; + } +} + +module.exports = { + writeFileReferenceToClipboard: binding ? binding.writeFileReferenceToClipboard : null, + loadError, +}; diff --git a/fluxer_desktop/native/mac-clipboard/loader-diagnostics.cjs b/fluxer_desktop/native/mac-clipboard/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/mac-clipboard/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/mac-clipboard/package.json b/fluxer_desktop/native/mac-clipboard/package.json new file mode 100644 index 0000000..71deefb --- /dev/null +++ b/fluxer_desktop/native/mac-clipboard/package.json @@ -0,0 +1,27 @@ +{ + "name": "@fluxer/mac-clipboard", + "version": "0.0.0", + "description": "", + "private": true, + "license": "AGPL-3.0-or-later", + "os": [ + "darwin" + ], + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "mac-clipboard.darwin-x64.node", + "mac-clipboard.darwin-arm64.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "cargo test --manifest-path Cargo.toml" + } +} diff --git a/fluxer_desktop/native/mac-clipboard/src/lib.rs b/fluxer_desktop/native/mac-clipboard/src/lib.rs new file mode 100644 index 0000000..b2075cb --- /dev/null +++ b/fluxer_desktop/native/mac-clipboard/src/lib.rs @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use napi::Task; +use napi::bindgen_prelude::{AsyncTask, Env, Error, Result, Status}; +use napi_derive::napi; + +pub struct WriteFileReferenceTask { + path: String, +} + +#[napi(js_name = "writeFileReferenceToClipboard")] +pub fn write_file_reference_to_clipboard( + file_path: String, +) -> Result> { + validate_file_path(&file_path)?; + Ok(AsyncTask::new(WriteFileReferenceTask { path: file_path })) +} + +impl Task for WriteFileReferenceTask { + type Output = (); + type JsValue = (); + + fn compute(&mut self) -> Result { + platform::write_file_reference(&self.path) + } + + fn resolve(&mut self, _env: Env, _output: Self::Output) -> Result { + Ok(()) + } +} + +fn validate_file_path(path: &str) -> Result<()> { + if path.is_empty() { + return Err(Error::new(Status::InvalidArg, "path must be non-empty")); + } + if path.as_bytes().contains(&0) { + return Err(Error::new( + Status::InvalidArg, + "path must not contain NUL bytes", + )); + } + Ok(()) +} + +#[cfg(target_os = "macos")] +mod platform { + use dispatch2::run_on_main; + use napi::bindgen_prelude::{Error, Result, Status}; + use objc2::runtime::ProtocolObject; + use objc2_app_kit::{NSPasteboard, NSPasteboardWriting}; + use objc2_foundation::{NSArray, NSString, NSURL}; + + pub(super) fn write_file_reference(path: &str) -> Result<()> { + run_on_main(|_mtm| write_file_reference_on_main(path)) + } + + fn write_file_reference_on_main(path: &str) -> Result<()> { + let ns_path = NSString::from_str(path); + let ns_url = NSURL::fileURLWithPath(&ns_path); + let writer = ProtocolObject::::from_ref(&*ns_url); + let objects = NSArray::arrayWithObject(writer); + let pasteboard = NSPasteboard::generalPasteboard(); + + pasteboard.clearContents(); + if pasteboard.writeObjects(&objects) { + Ok(()) + } else { + Err(Error::new( + Status::GenericFailure, + "NSPasteboard writeObjects returned NO", + )) + } + } +} + +#[cfg(not(target_os = "macos"))] +mod platform { + use napi::bindgen_prelude::{Error, Result, Status}; + + pub(super) fn write_file_reference(_path: &str) -> Result<()> { + Err(Error::new( + Status::GenericFailure, + "mac-clipboard called on non-macOS platform", + )) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn validation_rejects_empty_paths() { + let err = validate_file_path("").expect_err("empty path should be rejected"); + assert_eq!(err.status, Status::InvalidArg); + assert_eq!(err.reason, "path must be non-empty"); + } + + #[test] + fn validation_rejects_nul_bytes() { + let err = validate_file_path("before\0after").expect_err("NUL path should be rejected"); + assert_eq!(err.status, Status::InvalidArg); + assert_eq!(err.reason, "path must not contain NUL bytes"); + } + + #[test] + fn validation_accepts_regular_absolute_paths() { + validate_file_path("/Users/example/Desktop/file.txt").expect("path should validate"); + } + + #[cfg(not(target_os = "macos"))] + #[test] + fn non_macos_worker_preserves_stub_error_contract() { + let err = + platform::write_file_reference("/tmp/file.txt").expect_err("non-macOS should fail"); + assert_eq!(err.status, Status::GenericFailure); + assert_eq!(err.reason, "mac-clipboard called on non-macOS platform"); + } +} diff --git a/fluxer_desktop/native/mac-screen-capture/Cargo.lock b/fluxer_desktop/native/mac-screen-capture/Cargo.lock new file mode 100644 index 0000000..c0d64ae --- /dev/null +++ b/fluxer_desktop/native/mac-screen-capture/Cargo.lock @@ -0,0 +1,1059 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "criterion" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3" +dependencies = [ + "alloca", + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "num-traits", + "oorandom", + "page_size", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags", + "block2", + "libc", + "objc2", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fluxer_encoder_ring" +version = "0.0.0" +dependencies = [ + "libloading", + "windows", +] + +[[package]] +name = "fluxer_gpu_rebuild" +version = "0.0.0" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "fluxer_mac_screen_capture" +version = "0.0.0" +dependencies = [ + "block2", + "criterion", + "dispatch2", + "fluxer_encoder_ring", + "fluxer_screen_frame_bus", + "libc", + "napi", + "napi-build", + "napi-derive", + "objc2", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-media", + "objc2-core-video", + "objc2-foundation", + "objc2-io-surface", + "objc2-screen-capture-kit", + "parking_lot", +] + +[[package]] +name = "fluxer_screen_frame_bus" +version = "0.0.0" +dependencies = [ + "fluxer_gpu_rebuild", + "parking_lot", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash", + "tokio", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-av-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "478ae33fcac9df0a18db8302387c666b8ef08a3e2d62b510ca4fc278a384b6c0" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-audio" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1eebcea8b0dbff5f7c8504f3107c68fc061a3eb44932051c8cf8a68d969c3b2" +dependencies = [ + "dispatch2", + "objc2", + "objc2-core-audio-types", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-core-audio-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c" +dependencies = [ + "bitflags", + "objc2", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", + "block2", + "dispatch2", + "libc", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-media" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05ec576860167a15dd9fce7fbee7512beb4e31f532159d3482d1f9c6caedf31d" +dependencies = [ + "bitflags", + "block2", + "dispatch2", + "objc2", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-core-video", +] + +[[package]] +name = "objc2-core-video" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" +dependencies = [ + "bitflags", + "block2", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-io-surface", + "objc2-metal", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags", + "libc", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" +dependencies = [ + "bitflags", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-screen-capture-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74b7c5390f477482f001bc354d6571a70db7e4f8d5288e860c45521fbce11394" +dependencies = [ + "bitflags", + "block2", + "dispatch2", + "libc", + "objc2", + "objc2-av-foundation", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-media", + "objc2-foundation", + "objc2-uniform-type-identifiers", +] + +[[package]] +name = "objc2-uniform-type-identifiers" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7902ac02859fc1f7045f8b598c63f1ae0cc7efeaa06a9bc9f3d9a3c955974fa4" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "pin-project-lite", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zerocopy" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/fluxer_desktop/native/mac-screen-capture/Cargo.toml b/fluxer_desktop/native/mac-screen-capture/Cargo.toml new file mode 100644 index 0000000..d593645 --- /dev/null +++ b/fluxer_desktop/native/mac-screen-capture/Cargo.toml @@ -0,0 +1,45 @@ +[package] +name = "fluxer_mac_screen_capture" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8", "async"]} +napi-derive = "3.5.6" +fluxer_encoder_ring = {path = "../encoder-ring"} +parking_lot = "0.12" + +[target.'cfg(target_os = "macos")'.dependencies.fluxer_screen_frame_bus] +path = "../screen-frame-bus" + +[target.'cfg(target_os = "macos")'.dependencies] +libc = "0.2.186" +objc2 = "0.6" +objc2-foundation = {version = "0.3", features = ["NSString", "NSArray", "NSDictionary", "NSError", "NSValue", "NSBundle", "NSProcessInfo"]} +objc2-screen-capture-kit = {version = "0.3", features = ["SCStream", "SCShareableContent", "objc2-core-graphics", "objc2-core-media", "objc2-core-foundation", "block2", "dispatch2", "libc"]} +objc2-core-media = {version = "0.3", features = ["CMSampleBuffer", "CMBlockBuffer", "CMFormatDescription", "CMTime", "objc2-core-audio-types"]} +objc2-core-audio-types = {version = "0.3", features = ["CoreAudioBaseTypes", "objc2"]} +objc2-core-video = {version = "0.3", features = ["CVPixelBuffer", "CVPixelBufferIOSurface", "CVImageBuffer", "CVBuffer", "CVPixelFormatDescription", "CVReturn", "CVBase", "objc2-io-surface"]} +objc2-io-surface = {version = "0.3", features = ["IOSurfaceRef", "objc2-core-foundation"]} +objc2-core-foundation = {version = "0.3", features = ["CFArray", "CFDictionary", "CFNumber", "CFString", "CFBase"]} +objc2-core-graphics = {version = "0.3.2", default-features = false, features = ["std", "CGWindow"]} +block2 = "0.6" +dispatch2 = "0.3" + +[build-dependencies] +napi-build = "2.3.2" + +[dev-dependencies] +criterion = {version = "0.8", default-features = false, features = ["cargo_bench_support"]} + +[[bench]] +name = "audio_pool" +harness = false diff --git a/fluxer_desktop/native/mac-screen-capture/benches/audio_pool.rs b/fluxer_desktop/native/mac-screen-capture/benches/audio_pool.rs new file mode 100644 index 0000000..aa6b2a0 --- /dev/null +++ b/fluxer_desktop/native/mac-screen-capture/benches/audio_pool.rs @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::hint::black_box; + +use criterion::{Criterion, criterion_group, criterion_main}; +use fluxer_mac_screen_capture::audio_pool::{ + MAC_AUDIO_POOL_CAP, MAX_FRAME_BYTES_PER_SLOT, MacAudioFramePool, +}; + +fn bench_acquire_write_release_960_floats(c: &mut Criterion) { + let pool = MacAudioFramePool::new(MAC_AUDIO_POOL_CAP, MAX_FRAME_BYTES_PER_SLOT) + .expect("pool must build"); + let payload = vec![0xCC_u8; 960 * 4]; + c.bench_function("audio_pool/acquire_write_release/960_floats", |b| { + b.iter(|| { + let mut slot = pool + .try_acquire() + .expect("steady state single-thread never starves"); + slot.write(&payload).expect("write fits"); + black_box(slot.data_slice().len()); + drop(slot); + }); + }); + assert_eq!(pool.stats().in_flight, 0); +} + +fn bench_acquire_release_only(c: &mut Criterion) { + let pool = MacAudioFramePool::new(MAC_AUDIO_POOL_CAP, MAX_FRAME_BYTES_PER_SLOT) + .expect("pool must build"); + c.bench_function("audio_pool/acquire_release", |b| { + b.iter(|| { + let slot = pool.try_acquire().expect("slot"); + black_box(slot.slot_index()); + }); + }); + assert_eq!(pool.stats().in_flight, 0); +} + +fn bench_acquire_write_into_external_parts(c: &mut Criterion) { + let pool = MacAudioFramePool::new(MAC_AUDIO_POOL_CAP, MAX_FRAME_BYTES_PER_SLOT) + .expect("pool must build"); + let payload = vec![0xCC_u8; 960 * 4]; + c.bench_function( + "audio_pool/acquire_write_into_external_parts/960_floats", + |b| { + b.iter(|| { + let mut slot = pool + .try_acquire() + .expect("steady state single-thread never starves"); + slot.write(&payload).expect("write fits"); + let (ptr, len, owned) = slot.into_external_parts(); + black_box((ptr, len)); + drop(owned); + }); + }, + ); + assert_eq!(pool.stats().in_flight, 0); +} + +criterion_group!( + benches, + bench_acquire_write_release_960_floats, + bench_acquire_release_only, + bench_acquire_write_into_external_parts, +); +criterion_main!(benches); diff --git a/fluxer_desktop/native/mac-screen-capture/build.rs b/fluxer_desktop/native/mac-screen-capture/build.rs new file mode 100644 index 0000000..7afbf3e --- /dev/null +++ b/fluxer_desktop/native/mac-screen-capture/build.rs @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + napi_build::setup(); + + #[cfg(target_os = "macos")] + { + println!("cargo:rustc-link-lib=framework=AppKit"); + } +} diff --git a/fluxer_desktop/native/mac-screen-capture/index.d.ts b/fluxer_desktop/native/mac-screen-capture/index.d.ts new file mode 100644 index 0000000..1538601 --- /dev/null +++ b/fluxer_desktop/native/mac-screen-capture/index.d.ts @@ -0,0 +1,107 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import {EventEmitter} from 'node:events'; + +export interface MacScreenCaptureBackendInfo { + backend: string; + supported: boolean; + reason: string; + minMacosVersion: string; + detectedMacosVersion?: string; + sckAvailable: boolean; +} + +export declare function getBackendInfo(): MacScreenCaptureBackendInfo; + +export interface MacScreenCaptureBackendAvailability { + sck?: { + supported: boolean; + macosVersion?: string; + }; + screenPermission?: string; +} + +export declare function getBackendAvailability(): Promise; + +export type MacScreenCaptureSourceKind = 'screen' | 'window'; + +export interface MacScreenCaptureSource { + kind: MacScreenCaptureSourceKind; + id: string; + name: string; + width: number; + height: number; + appName?: string; + bundleId?: string; + targetPid?: number; +} + +export declare function listSources(): Promise>; + +export interface ScreenCaptureRect { + x: number; + y: number; + width: number; + height: number; +} + +export interface ScreenCaptureOptions { + sourceId: string; + sourceKind: MacScreenCaptureSourceKind; + width?: number; + height?: number; + frameRate?: number; + captureId?: string; + colorRange?: 'full' | 'limited'; + colorSpace?: 'rec709' | 'srgb'; + showCursorClicks?: boolean; + captureRect?: ScreenCaptureRect; + frameSinkHandle?: unknown; + nativeFrameSinkRequired?: boolean; +} + +export interface ScreenCaptureStartResult { + width: number; + height: number; + frameRate: number; + pixelFormat: 'nv12' | 'bgra'; +} + +export interface FrameSinkDiagnostics { + accepted: number; + coalesced: number; + rejected: number; + mediaFramesDroppedWithoutSink: number; +} + +export declare const loadError: Error | null; + +export declare function __setBindingForTests(binding: unknown): void; + +export declare interface ScreenCapture { + on(event: 'error', listener: (err: Error) => void): this; + on(event: 'closed', listener: () => void): this; + on(event: 'diagnostic', listener: (message?: string) => void): this; + on(event: string | symbol, listener: (...args: Array) => void): this; + off(event: 'error', listener: (err: Error) => void): this; + off(event: 'closed', listener: () => void): this; + off(event: 'diagnostic', listener: (message?: string) => void): this; + off(event: string | symbol, listener: (...args: Array) => void): this; + emit(event: 'error', err: Error): boolean; + emit(event: 'closed'): boolean; + emit(event: 'diagnostic', message?: string): boolean; +} + +export declare class ScreenCapture extends EventEmitter { + constructor(options: ScreenCaptureOptions); + + start(): Promise; + + stop(): Promise; + + attachEncoder(width: number, height: number, frameRate?: number): void; + detachEncoder(): void; + isEncoderAttached(): boolean; + encoderRingFullCount(): number; + getFrameSinkDiagnostics(): FrameSinkDiagnostics; +} diff --git a/fluxer_desktop/native/mac-screen-capture/index.js b/fluxer_desktop/native/mac-screen-capture/index.js new file mode 100644 index 0000000..b297602 --- /dev/null +++ b/fluxer_desktop/native/mac-screen-capture/index.js @@ -0,0 +1,233 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {EventEmitter} = require('node:events'); +const {existsSync} = require('node:fs'); +const {join, sep} = require('node:path'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); +const MODULE_NAME = '@fluxer/mac-screen-capture'; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName() { + if (process.platform !== 'darwin') { + throw new Error(`@fluxer/mac-screen-capture is only supported on macOS, got ${process.platform}`); + } + switch (process.arch) { + case 'x64': + return 'mac-screen-capture.darwin-x64.node'; + case 'arm64': + return 'mac-screen-capture.darwin-arm64.node'; + default: + throw new Error(`Unsupported macOS architecture: ${process.arch}`); + } +} + +let binding = null; +let loadError = null; + +if (process.platform === 'darwin') { + try { + const nativeRoot = resolveNativeRoot(); + const nativePath = join(nativeRoot, nativeFileName()); + const loaded = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + probe: false, + }); + binding = loaded.binding; + loadError = loaded.loadError; + if (loadError) throw loadError; + } catch (error) { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: 'native loader threw before binding load completed', + cause: error, + }); + throw loadError; + } +} + +function getBackendInfo() { + if (!binding) { + return { + backend: 'mac-screen-capture', + supported: false, + reason: + process.platform === 'darwin' + ? `@fluxer/mac-screen-capture native binary unavailable: ${loadError?.message ?? 'unknown reason'}` + : `@fluxer/mac-screen-capture is only supported on macOS, got ${process.platform}`, + minMacosVersion: '12.3', + detectedMacosVersion: undefined, + sckAvailable: false, + }; + } + return binding.getBackendInfo(); +} + +function getBackendAvailability() { + if (!binding) { + return Promise.resolve({ + sck: {supported: false}, + screenPermission: 'not-determined', + }); + } + return binding.getBackendAvailability(); +} + +function listSources() { + if (!binding) return Promise.resolve([]); + return binding.listSources(); +} + +function __setBindingForTests(nextBinding) { + binding = nextBinding; + loadError = null; +} + +class ScreenCapture extends EventEmitter { + constructor(options = {}) { + super(); + if (!binding) { + throw loadError || new Error('@fluxer/mac-screen-capture binding unavailable'); + } + this.sourceId = options.sourceId; + this.sourceKind = options.sourceKind ?? 'screen'; + this.width = options.width ?? 0; + this.height = options.height ?? 0; + this.frameRate = options.frameRate ?? 30; + this.captureId = typeof options.captureId === 'string' ? options.captureId : undefined; + this.colorRange = options.colorRange; + this.colorSpace = options.colorSpace; + this.showCursorClicks = options.showCursorClicks === true; + this.captureRect = options.captureRect; + this.frameSinkHandle = options.frameSinkHandle; + this.nativeFrameSinkRequired = options.nativeFrameSinkRequired === true; + this.started = false; + this.stopped = false; + this.closedEmitted = false; + this.native = new binding.ScreenCapture(); + this.native.setLifecycleCallback((type, message) => { + if (type === 'diagnostic') { + if (this.stopped) return; + this.emit('diagnostic', message); + return; + } + if (type === 'error') { + this.emit('error', new Error(message || 'macOS screen capture stream stopped')); + return; + } + if (type === 'closed') { + if (this.stopped) { + this.emitClosedOnce(); + return; + } + this.stopped = true; + Promise.resolve() + .then(() => this.native.stop()) + .catch(() => {}); + this.emitClosedOnce(); + } + }); + } + + emitClosedOnce() { + if (this.closedEmitted) return; + this.closedEmitted = true; + this.emit('closed'); + } + + async start() { + if (this.started || this.stopped) return; + this.started = true; + try { + if (this.frameSinkHandle != null) { + if (typeof this.native.setFrameSinkHandle !== 'function') { + throw new Error('@fluxer/mac-screen-capture native binding does not support native frame sink handles'); + } + this.native.setFrameSinkHandle(this.frameSinkHandle); + } else if (this.nativeFrameSinkRequired) { + throw new Error('Native frame sink handle is required for macOS screen capture'); + } + const result = await this.native.start( + this.sourceId, + this.sourceKind, + this.width, + this.height, + this.frameRate, + this.captureId, + { + colorRange: this.colorRange, + colorSpace: this.colorSpace, + showCursorClicks: this.showCursorClicks, + captureRect: this.captureRect, + }, + ); + if (result) { + this.width = result.width ?? this.width; + this.height = result.height ?? this.height; + this.frameRate = result.frameRate ?? this.frameRate; + this.pixelFormat = result.pixelFormat ?? 'nv12'; + } + return { + width: this.width, + height: this.height, + frameRate: this.frameRate, + pixelFormat: this.pixelFormat ?? 'nv12', + }; + } catch (error) { + this.stopped = true; + this.emit('error', error instanceof Error ? error : new Error(String(error))); + throw error; + } + } + + async stop() { + if (this.stopped) return; + this.stopped = true; + try { + await this.native.stop(); + } finally { + this.emitClosedOnce(); + } + } + + getFrameSinkDiagnostics() { + if (!this.native || typeof this.native.getFrameSinkDiagnostics !== 'function') { + return { + accepted: 0, + coalesced: 0, + rejected: 0, + mediaFramesDroppedWithoutSink: 0, + }; + } + try { + return this.native.getFrameSinkDiagnostics(); + } catch (error) { + console.warn('[mac-screen-capture] getFrameSinkDiagnostics failed:', error?.message || error); + return { + accepted: 0, + coalesced: 0, + rejected: 0, + mediaFramesDroppedWithoutSink: 0, + }; + } + } +} + +module.exports = { + ScreenCapture, + getBackendAvailability, + getBackendInfo, + listSources, + loadError, + __setBindingForTests, +}; diff --git a/fluxer_desktop/native/mac-screen-capture/index.test.mjs b/fluxer_desktop/native/mac-screen-capture/index.test.mjs new file mode 100644 index 0000000..2bba7ad --- /dev/null +++ b/fluxer_desktop/native/mac-screen-capture/index.test.mjs @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import assert from 'node:assert/strict'; +import {createRequire} from 'node:module'; +import {afterEach, describe, test} from 'node:test'; + +const require = createRequire(import.meta.url); +const macScreenCapture = require('./index.js'); + +function makeFakeBinding({sources = [], availability = {sck: {supported: true}, screenPermission: 'authorized'}} = {}) { + const calls = []; + const frameSinkHandleCalls = []; + const natives = []; + const frameSinkDiagnostics = { + accepted: 0, + coalesced: 0, + rejected: 0, + mediaFramesDroppedWithoutSink: 0, + }; + class FakeNative { + constructor() { + this.lifecycleCallback = undefined; + this.stopCount = 0; + natives.push(this); + } + + setLifecycleCallback(callback) { + this.lifecycleCallback = callback; + } + + setFrameSinkHandle(handle) { + frameSinkHandleCalls.push(handle); + } + + async start(sourceId, sourceKind, width, height, frameRate, captureId, captureOptions) { + calls.push({sourceId, sourceKind, width, height, frameRate, captureId, captureOptions}); + return {width: width || 1920, height: height || 1080, frameRate: frameRate || 30, pixelFormat: 'nv12'}; + } + + async stop() { + this.stopCount += 1; + } + + getFrameSinkDiagnostics() { + return {...frameSinkDiagnostics}; + } + } + return { + binding: { + ScreenCapture: FakeNative, + listSources: async () => sources, + getBackendAvailability: async () => availability, + getBackendInfo: () => ({ + backend: 'mac-screen-capture', + supported: true, + reason: '', + minMacosVersion: '12.3', + detectedMacosVersion: '14.0', + sckAvailable: true, + }), + }, + calls, + frameSinkHandleCalls, + frameSinkDiagnostics, + natives, + }; +} + +afterEach(() => { + macScreenCapture.__setBindingForTests(null); +}); + +describe('mac-screen-capture loader wrapper', () => { + test('forwards source id, kind, and dimensions to native binding', async () => { + const {binding, calls} = makeFakeBinding(); + macScreenCapture.__setBindingForTests(binding); + const capture = new macScreenCapture.ScreenCapture({ + sourceId: '12345', + sourceKind: 'window', + width: 1280, + height: 720, + frameRate: 30, + colorRange: 'full', + colorSpace: 'rec709', + showCursorClicks: true, + captureRect: {x: 10, y: 20, width: 300, height: 200}, + }); + capture.on('error', () => {}); + const result = await capture.start(); + assert.deepEqual(calls, [ + { + sourceId: '12345', + sourceKind: 'window', + width: 1280, + height: 720, + frameRate: 30, + captureId: undefined, + captureOptions: { + colorRange: 'full', + colorSpace: 'rec709', + showCursorClicks: true, + captureRect: {x: 10, y: 20, width: 300, height: 200}, + }, + }, + ]); + assert.equal(result.pixelFormat, 'nv12'); + assert.equal(result.width, 1280); + assert.equal(result.height, 720); + }); + test('defaults sourceKind to screen and frameRate to 30', async () => { + const {binding, calls} = makeFakeBinding(); + macScreenCapture.__setBindingForTests(binding); + const capture = new macScreenCapture.ScreenCapture({sourceId: '1'}); + capture.on('error', () => {}); + await capture.start(); + assert.equal(calls[0].sourceKind, 'screen'); + assert.equal(calls[0].frameRate, 30); + }); + test('forwards display and window sources from native binding without rewriting ids', async () => { + const {binding} = makeFakeBinding({ + sources: [ + {kind: 'screen', id: 'display:69733632', name: 'Studio Display', width: 5120, height: 2880}, + { + kind: 'window', + id: 'window:4242', + name: 'Fluxer', + width: 1440, + height: 900, + appName: 'Fluxer', + bundleId: 'app.fluxer.desktop', + targetPid: 1234, + }, + ], + }); + macScreenCapture.__setBindingForTests(binding); + + const sources = await macScreenCapture.listSources(); + + assert.deepEqual(sources, [ + {kind: 'screen', id: 'display:69733632', name: 'Studio Display', width: 5120, height: 2880}, + { + kind: 'window', + id: 'window:4242', + name: 'Fluxer', + width: 1440, + height: 900, + appName: 'Fluxer', + bundleId: 'app.fluxer.desktop', + targetPid: 1234, + }, + ]); + }); + test('reports ScreenCaptureKit support and permission from native binding', async () => { + const {binding} = makeFakeBinding({ + availability: { + sck: {supported: true, macosVersion: '15.0'}, + screenPermission: 'authorized', + }, + }); + macScreenCapture.__setBindingForTests(binding); + + assert.deepEqual(await macScreenCapture.getBackendAvailability(), { + sck: {supported: true, macosVersion: '15.0'}, + screenPermission: 'authorized', + }); + }); + test('installs a native frame sink handle once before start', async () => { + const {binding, calls, frameSinkHandleCalls} = makeFakeBinding(); + macScreenCapture.__setBindingForTests(binding); + const frameSinkHandle = {native: true}; + const capture = new macScreenCapture.ScreenCapture({ + sourceId: '12345', + sourceKind: 'window', + frameSinkHandle, + nativeFrameSinkRequired: true, + }); + capture.on('error', () => {}); + + await capture.start(); + + assert.deepEqual(frameSinkHandleCalls, [frameSinkHandle]); + assert.equal(calls.length, 1); + }); + test('fails before native start when a native frame sink is required but missing', async () => { + const {binding, calls, frameSinkHandleCalls} = makeFakeBinding(); + macScreenCapture.__setBindingForTests(binding); + const capture = new macScreenCapture.ScreenCapture({ + sourceId: '12345', + sourceKind: 'window', + nativeFrameSinkRequired: true, + }); + capture.on('error', () => {}); + + await assert.rejects(() => capture.start(), /native frame sink handle is required/i); + assert.deepEqual(frameSinkHandleCalls, []); + assert.deepEqual(calls, []); + }); + test('frame sink diagnostics are forwarded by the wrapper', () => { + const {binding, frameSinkDiagnostics} = makeFakeBinding(); + macScreenCapture.__setBindingForTests(binding); + const capture = new macScreenCapture.ScreenCapture({sourceId: '1'}); + + frameSinkDiagnostics.accepted = 5; + frameSinkDiagnostics.coalesced = 1; + frameSinkDiagnostics.rejected = 2; + frameSinkDiagnostics.mediaFramesDroppedWithoutSink = 3; + + assert.deepEqual(capture.getFrameSinkDiagnostics(), { + accepted: 5, + coalesced: 1, + rejected: 2, + mediaFramesDroppedWithoutSink: 3, + }); + }); + test('emits closed once when native lifecycle closes and stop is called later', async () => { + const {binding, natives} = makeFakeBinding(); + macScreenCapture.__setBindingForTests(binding); + const capture = new macScreenCapture.ScreenCapture({sourceId: '1'}); + let closed = 0; + capture.on('closed', () => { + closed += 1; + }); + natives[0].lifecycleCallback('closed', ''); + await Promise.resolve(); + await capture.stop(); + assert.equal(closed, 1); + assert.equal(natives[0].stopCount, 1); + }); + test('lifecycle error emits Error event', async () => { + const {binding, natives} = makeFakeBinding(); + macScreenCapture.__setBindingForTests(binding); + const capture = new macScreenCapture.ScreenCapture({sourceId: '1'}); + const errors = []; + capture.on('error', (err) => errors.push(err)); + natives[0].lifecycleCallback('error', 'permission lost mid-stream'); + assert.equal(errors.length, 1); + assert.equal(errors[0].message, 'permission lost mid-stream'); + }); + test('lifecycle diagnostic emits diagnostic event', () => { + const {binding, natives} = makeFakeBinding(); + macScreenCapture.__setBindingForTests(binding); + const capture = new macScreenCapture.ScreenCapture({sourceId: '1'}); + const diagnostics = []; + capture.on('diagnostic', (message) => diagnostics.push(message)); + natives[0].lifecycleCallback('diagnostic', 'frame sink rejected a frame'); + assert.deepEqual(diagnostics, ['frame sink rejected a frame']); + }); +}); diff --git a/fluxer_desktop/native/mac-screen-capture/loader-diagnostics.cjs b/fluxer_desktop/native/mac-screen-capture/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/mac-screen-capture/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/mac-screen-capture/package.json b/fluxer_desktop/native/mac-screen-capture/package.json new file mode 100644 index 0000000..ce5ae7f --- /dev/null +++ b/fluxer_desktop/native/mac-screen-capture/package.json @@ -0,0 +1,34 @@ +{ + "name": "@fluxer/mac-screen-capture", + "version": "0.0.0", + "description": "", + "private": true, + "license": "AGPL-3.0-or-later", + "os": [ + "darwin" + ], + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "mac-screen-capture.darwin-x64.node", + "mac-screen-capture.darwin-arm64.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "pnpm test:cargo && node --test index.test.mjs", + "test:cargo": "cargo test --manifest-path Cargo.toml" + }, + "binary": { + "napi_versions": [ + 8 + ] + }, + "devDependencies": {} +} diff --git a/fluxer_desktop/native/mac-screen-capture/src/audio_pool.rs b/fluxer_desktop/native/mac-screen-capture/src/audio_pool.rs new file mode 100644 index 0000000..d788cd4 --- /dev/null +++ b/fluxer_desktop/native/mac-screen-capture/src/audio_pool.rs @@ -0,0 +1,596 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use parking_lot::Mutex; +use std::cell::UnsafeCell; +use std::fmt; +use std::sync::Arc; +use std::sync::atomic::{AtomicU32, AtomicU64, Ordering}; + +pub const MAC_AUDIO_POOL_CAP: usize = 16; +pub const MAX_FRAME_BYTES_PER_SLOT: usize = 8192; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MacAudioError { + ZeroCapacity, + ZeroBytesPerSlot, + BytesPerSlotTooLarge(usize), + PayloadTooLarge { offered: usize, capacity: usize }, +} + +impl fmt::Display for MacAudioError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ZeroCapacity => write!(f, "MacAudioFramePool capacity must be > 0"), + Self::ZeroBytesPerSlot => write!(f, "MacAudioFramePool bytes_per_slot must be > 0"), + Self::BytesPerSlotTooLarge(n) => write!( + f, + "MacAudioFramePool bytes_per_slot {n} exceeds MAX_FRAME_BYTES_PER_SLOT={MAX_FRAME_BYTES_PER_SLOT}" + ), + Self::PayloadTooLarge { offered, capacity } => write!( + f, + "MacAudioFramePool payload {offered} bytes exceeds slot capacity {capacity}" + ), + } + } +} + +impl std::error::Error for MacAudioError {} + +struct PoolSlotCell { + inner: UnsafeCell>, +} + +unsafe impl Send for PoolSlotCell {} +unsafe impl Sync for PoolSlotCell {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct MacAudioPoolStats { + pub acquired: u64, + pub released: u64, + pub dropped: u64, + pub in_flight: u32, +} + +pub(crate) struct MacAudioFramePoolInner { + slots: Vec, + free: Mutex>, + capacity: u32, + bytes_per_slot: u32, + acquired_total: AtomicU64, + released_total: AtomicU64, + dropped_total: AtomicU64, + in_flight: AtomicU32, +} + +pub struct MacAudioFramePool { + inner: Arc, +} + +impl fmt::Debug for MacAudioFramePool { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let stats = self.stats(); + f.debug_struct("MacAudioFramePool") + .field("capacity", &self.inner.capacity) + .field("bytes_per_slot", &self.inner.bytes_per_slot) + .field("stats", &stats) + .finish() + } +} + +impl MacAudioFramePool { + pub fn new(capacity: usize, bytes_per_slot: usize) -> Result { + if capacity == 0 { + return Err(MacAudioError::ZeroCapacity); + } + if bytes_per_slot == 0 { + return Err(MacAudioError::ZeroBytesPerSlot); + } + if bytes_per_slot > MAX_FRAME_BYTES_PER_SLOT { + return Err(MacAudioError::BytesPerSlotTooLarge(bytes_per_slot)); + } + assert!(capacity > 0); + assert!(bytes_per_slot > 0); + assert!(bytes_per_slot <= MAX_FRAME_BYTES_PER_SLOT); + + let mut slots: Vec = Vec::with_capacity(capacity); + for _ in 0..capacity { + let buf: Box<[u8]> = vec![0u8; bytes_per_slot].into_boxed_slice(); + assert_eq!(buf.len(), bytes_per_slot); + slots.push(PoolSlotCell { + inner: UnsafeCell::new(buf), + }); + } + assert_eq!(slots.len(), capacity); + + let mut free: Vec = Vec::with_capacity(capacity); + for index in 0..capacity { + free.push(index); + } + assert_eq!(free.len(), capacity); + + let cap_u32 = u32::try_from(capacity).map_err(|_| MacAudioError::ZeroCapacity)?; + let bps_u32 = u32::try_from(bytes_per_slot).map_err(|_| MacAudioError::ZeroBytesPerSlot)?; + let inner = MacAudioFramePoolInner { + slots, + free: Mutex::new(free), + capacity: cap_u32, + bytes_per_slot: bps_u32, + acquired_total: AtomicU64::new(0), + released_total: AtomicU64::new(0), + dropped_total: AtomicU64::new(0), + in_flight: AtomicU32::new(0), + }; + Ok(Self { + inner: Arc::new(inner), + }) + } + + pub fn try_acquire(&self) -> Option { + assert!(self.inner.capacity > 0); + assert!(self.inner.bytes_per_slot > 0); + + let mut free = self.inner.free.lock(); + assert!(free.len() <= self.inner.capacity as usize); + let index = match free.pop() { + Some(idx) => idx, + None => { + drop(free); + self.inner.dropped_total.fetch_add(1, Ordering::Relaxed); + return None; + } + }; + assert!(index < self.inner.capacity as usize); + self.inner.acquired_total.fetch_add(1, Ordering::Relaxed); + let after = self.inner.in_flight.fetch_add(1, Ordering::AcqRel) + 1; + assert!(after <= self.inner.capacity); + drop(free); + + Some(PooledMacAudioFrame { + slot_index: index, + filled_len: 0, + pool: Arc::clone(&self.inner), + }) + } + + pub fn capacity(&self) -> u32 { + let cap = self.inner.capacity; + assert!(cap > 0); + assert!(cap as usize == self.inner.slots.len()); + cap + } + + pub fn bytes_per_slot(&self) -> u32 { + let bps = self.inner.bytes_per_slot; + assert!(bps > 0); + assert!(bps as usize <= MAX_FRAME_BYTES_PER_SLOT); + bps + } + + pub fn stats(&self) -> MacAudioPoolStats { + assert!(self.inner.capacity > 0); + let in_flight = self.inner.in_flight.load(Ordering::Acquire); + assert!(in_flight <= self.inner.capacity); + let acquired = self.inner.acquired_total.load(Ordering::Relaxed); + let released = self.inner.released_total.load(Ordering::Relaxed); + let dropped = self.inner.dropped_total.load(Ordering::Relaxed); + assert!(released <= acquired); + MacAudioPoolStats { + acquired, + released, + dropped, + in_flight, + } + } +} + +impl Clone for MacAudioFramePool { + fn clone(&self) -> Self { + Self { + inner: Arc::clone(&self.inner), + } + } +} + +pub struct PooledMacAudioFrame { + slot_index: usize, + filled_len: usize, + pool: Arc, +} + +impl PooledMacAudioFrame { + pub fn write(&mut self, samples: &[u8]) -> Result<(), MacAudioError> { + assert!(self.slot_index < self.pool.capacity as usize); + let cap = self.pool.bytes_per_slot as usize; + if samples.len() > cap { + return Err(MacAudioError::PayloadTooLarge { + offered: samples.len(), + capacity: cap, + }); + } + assert!(samples.len() <= cap); + + let cell = &self.pool.slots[self.slot_index]; + let buf: &mut [u8] = unsafe { &mut *cell.inner.get() }; + assert_eq!(buf.len(), cap); + if !samples.is_empty() { + buf[..samples.len()].copy_from_slice(samples); + } + self.filled_len = samples.len(); + assert!(self.filled_len <= cap); + Ok(()) + } + + pub fn append(&mut self, samples: &[u8]) -> Result<(), MacAudioError> { + assert!(self.slot_index < self.pool.capacity as usize); + let cap = self.pool.bytes_per_slot as usize; + let new_len = + self.filled_len + .checked_add(samples.len()) + .ok_or(MacAudioError::PayloadTooLarge { + offered: usize::MAX, + capacity: cap, + })?; + if new_len > cap { + return Err(MacAudioError::PayloadTooLarge { + offered: new_len, + capacity: cap, + }); + } + assert!(new_len <= cap); + + let cell = &self.pool.slots[self.slot_index]; + let buf: &mut [u8] = unsafe { &mut *cell.inner.get() }; + assert_eq!(buf.len(), cap); + if !samples.is_empty() { + buf[self.filled_len..new_len].copy_from_slice(samples); + } + self.filled_len = new_len; + Ok(()) + } + + pub fn data_slice(&self) -> &[u8] { + assert!(self.slot_index < self.pool.capacity as usize); + assert!(self.filled_len <= self.pool.bytes_per_slot as usize); + let cell = &self.pool.slots[self.slot_index]; + let buf: &[u8] = unsafe { &*cell.inner.get() }; + &buf[..self.filled_len] + } + + pub fn filled_len(&self) -> usize { + assert!(self.slot_index < self.pool.capacity as usize); + assert!(self.filled_len <= self.pool.bytes_per_slot as usize); + self.filled_len + } + + pub fn capacity(&self) -> usize { + let cap = self.pool.bytes_per_slot as usize; + assert!(cap > 0); + assert!(cap <= MAX_FRAME_BYTES_PER_SLOT); + cap + } + + pub fn slot_index(&self) -> usize { + assert!(self.slot_index < self.pool.capacity as usize); + self.slot_index + } + + pub fn as_mut_ptr(&mut self) -> *mut u8 { + assert!(self.slot_index < self.pool.capacity as usize); + assert!(self.filled_len <= self.pool.bytes_per_slot as usize); + let cell = &self.pool.slots[self.slot_index]; + let buf: &mut [u8] = unsafe { &mut *cell.inner.get() }; + assert_eq!(buf.len(), self.pool.bytes_per_slot as usize); + buf.as_mut_ptr() + } + + pub fn into_external_parts(mut self) -> (*mut u8, usize, Self) { + assert!(self.slot_index < self.pool.capacity as usize); + assert!(self.filled_len <= self.pool.bytes_per_slot as usize); + let len = self.filled_len; + let ptr = self.as_mut_ptr(); + assert!(!ptr.is_null()); + (ptr, len, self) + } +} + +impl Drop for PooledMacAudioFrame { + fn drop(&mut self) { + assert!(self.slot_index < self.pool.capacity as usize); + let mut free = self.pool.free.lock(); + assert!(free.len() < self.pool.capacity as usize); + free.push(self.slot_index); + let before = self.pool.in_flight.fetch_sub(1, Ordering::AcqRel); + assert!(before >= 1); + self.pool.released_total.fetch_add(1, Ordering::Relaxed); + drop(free); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::Barrier; + use std::sync::atomic::AtomicUsize; + use std::thread; + use std::time::{Duration, Instant}; + + const SLOT_BYTES: usize = 3840; + + fn default_pool() -> MacAudioFramePool { + MacAudioFramePool::new(MAC_AUDIO_POOL_CAP, SLOT_BYTES).expect("default pool builds") + } + + #[test] + fn new_rejects_zero_capacity() { + let err = MacAudioFramePool::new(0, SLOT_BYTES).unwrap_err(); + assert_eq!(err, MacAudioError::ZeroCapacity); + } + + #[test] + fn new_rejects_zero_bytes_per_slot() { + let err = MacAudioFramePool::new(4, 0).unwrap_err(); + assert_eq!(err, MacAudioError::ZeroBytesPerSlot); + } + + #[test] + fn new_rejects_bytes_per_slot_above_max() { + let err = MacAudioFramePool::new(4, MAX_FRAME_BYTES_PER_SLOT + 1).unwrap_err(); + assert_eq!( + err, + MacAudioError::BytesPerSlotTooLarge(MAX_FRAME_BYTES_PER_SLOT + 1) + ); + } + + #[test] + fn acquire_release_cycle_increments_counters() { + let pool = MacAudioFramePool::new(4, SLOT_BYTES).expect("pool"); + let stats_before = pool.stats(); + assert_eq!(stats_before.acquired, 0); + assert_eq!(stats_before.released, 0); + assert_eq!(stats_before.in_flight, 0); + { + let _slot = pool.try_acquire().expect("slot"); + let stats_held = pool.stats(); + assert_eq!(stats_held.acquired, 1); + assert_eq!(stats_held.in_flight, 1); + } + let stats_after = pool.stats(); + assert_eq!(stats_after.acquired, 1); + assert_eq!(stats_after.released, 1); + assert_eq!(stats_after.in_flight, 0); + } + + #[test] + fn pool_exhausts_at_cap_seventeenth_acquire_returns_none() { + let pool = default_pool(); + let mut held = Vec::with_capacity(MAC_AUDIO_POOL_CAP); + for _ in 0..MAC_AUDIO_POOL_CAP { + held.push(pool.try_acquire().expect("slot in capacity")); + } + assert!(pool.try_acquire().is_none()); + let stats = pool.stats(); + assert_eq!(stats.dropped, 1); + assert_eq!(stats.acquired as usize, MAC_AUDIO_POOL_CAP); + assert_eq!(stats.in_flight as usize, MAC_AUDIO_POOL_CAP); + } + + #[test] + fn drop_returns_slot_to_free_list() { + let pool = MacAudioFramePool::new(2, SLOT_BYTES).expect("pool"); + let first = pool.try_acquire().expect("first"); + let second = pool.try_acquire().expect("second"); + assert!(pool.try_acquire().is_none()); + drop(first); + let revived = pool.try_acquire().expect("revived"); + assert_eq!(pool.stats().in_flight, 2); + drop(second); + drop(revived); + assert_eq!(pool.stats().in_flight, 0); + } + + #[test] + fn write_then_data_slice_matches_payload() { + let pool = MacAudioFramePool::new(2, 64).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + let payload = [0xAB_u8; 32]; + slot.write(&payload).expect("payload fits"); + assert_eq!(slot.data_slice(), &payload[..]); + assert_eq!(slot.filled_len(), 32); + } + + #[test] + fn write_rejects_payload_larger_than_slot() { + let pool = MacAudioFramePool::new(2, 64).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + let big = [0u8; 128]; + let err = slot.write(&big).unwrap_err(); + assert!(matches!(err, MacAudioError::PayloadTooLarge { .. })); + } + + #[test] + fn append_concatenates_planar_to_interleaved_like_payload() { + let pool = MacAudioFramePool::new(1, 32).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + let left = [1_u8, 2, 3, 4]; + let right = [5_u8, 6, 7, 8]; + slot.append(&left).expect("left fits"); + slot.append(&right).expect("right fits"); + assert_eq!(slot.data_slice(), &[1, 2, 3, 4, 5, 6, 7, 8]); + assert_eq!(slot.filled_len(), 8); + } + + #[test] + fn append_rejects_overflow() { + let pool = MacAudioFramePool::new(1, 4).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + slot.append(&[1, 2, 3]).expect("3 of 4 fits"); + let err = slot.append(&[4, 5]).unwrap_err(); + assert!(matches!(err, MacAudioError::PayloadTooLarge { .. })); + } + + #[test] + fn recycled_slot_persists_buffer_storage_until_overwritten() { + let pool = MacAudioFramePool::new(1, 16).expect("pool"); + let mut slot = pool.try_acquire().expect("first"); + let payload = [0x42_u8; 8]; + slot.write(&payload).expect("write"); + let observed_first = slot.data_slice().to_vec(); + drop(slot); + let slot2 = pool.try_acquire().expect("recycled"); + assert_eq!(slot2.filled_len(), 0); + assert_eq!(observed_first, payload.to_vec()); + } + + #[test] + fn multi_thread_acquire_release_stress_8_threads_1000_ops_no_deadlock() { + const THREADS: usize = 8; + const OPS_PER_THREAD: usize = 1000; + const POOL_CAP: usize = 4; + + let pool = Arc::new(MacAudioFramePool::new(POOL_CAP, 64).expect("pool")); + let barrier = Arc::new(Barrier::new(THREADS)); + let acquired_obs = Arc::new(AtomicUsize::new(0)); + let dropped_obs = Arc::new(AtomicUsize::new(0)); + + let mut handles = Vec::with_capacity(THREADS); + for _ in 0..THREADS { + let pool = Arc::clone(&pool); + let barrier = Arc::clone(&barrier); + let acquired_obs = Arc::clone(&acquired_obs); + let dropped_obs = Arc::clone(&dropped_obs); + handles.push(thread::spawn(move || { + barrier.wait(); + let mut local_acq: usize = 0; + let mut local_drop: usize = 0; + for _ in 0..OPS_PER_THREAD { + match pool.try_acquire() { + Some(mut slot) => { + local_acq += 1; + let _ = slot.write(&[0xAA; 16]); + drop(slot); + } + None => { + local_drop += 1; + } + } + } + acquired_obs.fetch_add(local_acq, Ordering::Relaxed); + dropped_obs.fetch_add(local_drop, Ordering::Relaxed); + })); + } + + let deadline = Instant::now() + Duration::from_secs(30); + for h in handles { + assert!(Instant::now() < deadline, "stress test exceeded 30s budget"); + h.join().expect("worker panicked"); + } + + let stats = pool.stats(); + let total = stats.acquired + stats.dropped; + assert_eq!(total as usize, THREADS * OPS_PER_THREAD); + assert_eq!( + stats.acquired as usize, + acquired_obs.load(Ordering::Relaxed) + ); + assert_eq!(stats.dropped as usize, dropped_obs.load(Ordering::Relaxed)); + assert_eq!(stats.in_flight, 0); + assert_eq!(stats.acquired, stats.released); + } + + #[test] + fn default_pool_dimensions_match_constants() { + let pool = default_pool(); + assert_eq!(pool.capacity() as usize, MAC_AUDIO_POOL_CAP); + assert_eq!(pool.bytes_per_slot() as usize, SLOT_BYTES); + } + + #[test] + fn stats_in_flight_matches_simultaneous_holders() { + let pool = MacAudioFramePool::new(8, 64).expect("pool"); + let a = pool.try_acquire().expect("a"); + let b = pool.try_acquire().expect("b"); + let c = pool.try_acquire().expect("c"); + assert_eq!(pool.stats().in_flight, 3); + drop(b); + assert_eq!(pool.stats().in_flight, 2); + drop(a); + drop(c); + assert_eq!(pool.stats().in_flight, 0); + } + + #[test] + fn into_external_parts_exposes_filled_pointer_and_length() { + let pool = MacAudioFramePool::new(2, 64).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + let payload = [0x5A_u8; 16]; + slot.write(&payload).expect("write fits"); + let (ptr, len, owned) = slot.into_external_parts(); + assert!(!ptr.is_null()); + assert_eq!(len, 16); + let observed = unsafe { core::slice::from_raw_parts(ptr, len) }; + assert_eq!(observed, &payload[..]); + assert_eq!(pool.stats().in_flight, 1); + drop(owned); + assert_eq!(pool.stats().in_flight, 0); + } + + #[test] + fn into_external_parts_drop_returns_slot_to_pool() { + let pool = MacAudioFramePool::new(1, 32).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + slot.write(&[0xCC_u8; 8]).expect("write"); + let (_ptr, _len, owned) = slot.into_external_parts(); + assert!(pool.try_acquire().is_none()); + drop(owned); + let revived = pool.try_acquire().expect("revived"); + assert_eq!(revived.filled_len(), 0); + drop(revived); + assert_eq!(pool.stats().in_flight, 0); + } + + #[test] + fn into_external_parts_holds_slot_across_send() { + let pool = MacAudioFramePool::new(2, 64).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + slot.write(&[0x77_u8; 32]).expect("write"); + let (ptr, len, owned) = slot.into_external_parts(); + let ptr_addr = ptr as usize; + let handle = thread::spawn(move || { + let owned = owned; + assert_eq!(owned.filled_len(), 32); + let observed = unsafe { core::slice::from_raw_parts(ptr_addr as *const u8, len) }; + assert_eq!(observed[0], 0x77); + drop(owned); + }); + handle.join().expect("worker"); + assert_eq!(pool.stats().in_flight, 0); + } + + #[test] + fn as_mut_ptr_returns_slot_base_address() { + let pool = MacAudioFramePool::new(1, 64).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + let payload = [0x11_u8; 8]; + slot.write(&payload).expect("write"); + let ptr = slot.as_mut_ptr(); + assert!(!ptr.is_null()); + unsafe { ptr.add(0).write(0x22) }; + unsafe { ptr.add(1).write(0x33) }; + assert_eq!(&slot.data_slice()[..2], &[0x22, 0x33]); + } + + #[test] + fn capacity_one_pool_round_trips() { + let pool = MacAudioFramePool::new(1, 32).expect("pool"); + for _ in 0..5 { + let mut slot = pool.try_acquire().expect("slot"); + slot.write(&[1, 2, 3]).expect("write"); + assert_eq!(slot.data_slice(), &[1, 2, 3]); + drop(slot); + } + let stats = pool.stats(); + assert_eq!(stats.acquired, 5); + assert_eq!(stats.released, 5); + assert_eq!(stats.in_flight, 0); + } +} diff --git a/fluxer_desktop/native/mac-screen-capture/src/config.rs b/fluxer_desktop/native/mac-screen-capture/src/config.rs new file mode 100644 index 0000000..157e807 --- /dev/null +++ b/fluxer_desktop/native/mac-screen-capture/src/config.rs @@ -0,0 +1,769 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::fmt; + +pub const FPS_MIN: u32 = 1; +pub const FPS_MAX: u32 = 120; +pub const MAX_OUTPUT_WIDTH_DEFAULT: u32 = 3840; +pub const MAX_OUTPUT_HEIGHT_DEFAULT: u32 = 2160; +pub const OUTPUT_DIMENSION_MIN: u32 = 2; +pub const QUEUE_DEPTH_MIN: u32 = 1; +pub const QUEUE_DEPTH_MAX: u32 = 16; +pub const QUEUE_DEPTH_DEFAULT: u32 = 8; +pub const FPS_DEFAULT: u32 = 30; +pub const FRAME_INTERVAL_FACTOR_NUM: u64 = 9; +pub const FRAME_INTERVAL_FACTOR_DEN: u64 = 10; + +pub const PIXEL_FORMAT_BGRA_FOURCC: u32 = u32::from_be_bytes(*b"BGRA"); +pub const PIXEL_FORMAT_L10R_FOURCC: u32 = u32::from_be_bytes(*b"l10r"); +pub const PIXEL_FORMAT_420V_FOURCC: u32 = u32::from_be_bytes(*b"420v"); +pub const PIXEL_FORMAT_420F_FOURCC: u32 = u32::from_be_bytes(*b"420f"); + +pub const AUDIO_SAMPLE_RATE_DEFAULT_HZ: u32 = 48_000; +pub const AUDIO_CHANNEL_COUNT_DEFAULT: u32 = 2; +pub const AUDIO_SAMPLE_RATE_MIN_HZ: u32 = 8_000; +pub const AUDIO_SAMPLE_RATE_MAX_HZ: u32 = 192_000; +pub const AUDIO_CHANNEL_COUNT_MIN: u32 = 1; +pub const AUDIO_CHANNEL_COUNT_MAX: u32 = 8; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SckPixelFormat { + Bgra8, + L10rHdr, + Nv12VideoRange, + Nv12FullRange, +} + +impl SckPixelFormat { + pub fn as_fourcc(self) -> u32 { + let value = match self { + SckPixelFormat::Bgra8 => PIXEL_FORMAT_BGRA_FOURCC, + SckPixelFormat::L10rHdr => PIXEL_FORMAT_L10R_FOURCC, + SckPixelFormat::Nv12VideoRange => PIXEL_FORMAT_420V_FOURCC, + SckPixelFormat::Nv12FullRange => PIXEL_FORMAT_420F_FOURCC, + }; + assert!(value != 0); + value + } + + pub fn is_hdr(self) -> bool { + matches!(self, SckPixelFormat::L10rHdr) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SckColorSpace { + DisplayP3, + SrgbBt709, +} + +impl SckColorSpace { + pub fn as_cf_name(self) -> &'static str { + let name = match self { + SckColorSpace::DisplayP3 => "kCGColorSpaceDisplayP3", + SckColorSpace::SrgbBt709 => "kCGColorSpaceSRGB", + }; + assert!(!name.is_empty()); + name + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SckError { + InvalidFps(u32), + InvalidQueueDepth(u32), + HdrRequiresWideColorSpace, + InvalidAudioSampleRate(u32), + InvalidAudioChannelCount(u32), +} + +impl fmt::Display for SckError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + SckError::InvalidFps(v) => write!( + f, + "SckCaptureConfig: target_fps={v} out of range [{FPS_MIN}..={FPS_MAX}]" + ), + SckError::InvalidQueueDepth(v) => write!( + f, + "SckCaptureConfig: queue_depth={v} out of range [{QUEUE_DEPTH_MIN}..={QUEUE_DEPTH_MAX}]" + ), + SckError::HdrRequiresWideColorSpace => write!( + f, + "SckCaptureConfig: l10r HDR pixel format requires DisplayP3 color space" + ), + SckError::InvalidAudioSampleRate(v) => write!( + f, + "SckCaptureConfig: audio_sample_rate_hz={v} out of range [{AUDIO_SAMPLE_RATE_MIN_HZ}..={AUDIO_SAMPLE_RATE_MAX_HZ}]" + ), + SckError::InvalidAudioChannelCount(v) => write!( + f, + "SckCaptureConfig: audio_channels={v} out of range [{AUDIO_CHANNEL_COUNT_MIN}..={AUDIO_CHANNEL_COUNT_MAX}]" + ), + } + } +} + +impl std::error::Error for SckError {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SckCaptureConfig { + target_fps: u32, + queue_depth: u32, + pixel_format: SckPixelFormat, + color_space: SckColorSpace, + captures_audio: bool, + audio_sample_rate_hz: u32, + audio_channels: u32, +} + +impl SckCaptureConfig { + pub fn new( + target_fps: u32, + queue_depth: u32, + pixel_format: SckPixelFormat, + color_space: SckColorSpace, + ) -> Result { + Self::new_with_audio( + target_fps, + queue_depth, + pixel_format, + color_space, + false, + AUDIO_SAMPLE_RATE_DEFAULT_HZ, + AUDIO_CHANNEL_COUNT_DEFAULT, + ) + } + + pub fn new_with_audio( + target_fps: u32, + queue_depth: u32, + pixel_format: SckPixelFormat, + color_space: SckColorSpace, + captures_audio: bool, + audio_sample_rate_hz: u32, + audio_channels: u32, + ) -> Result { + if !(FPS_MIN..=FPS_MAX).contains(&target_fps) { + return Err(SckError::InvalidFps(target_fps)); + } + if !(QUEUE_DEPTH_MIN..=QUEUE_DEPTH_MAX).contains(&queue_depth) { + return Err(SckError::InvalidQueueDepth(queue_depth)); + } + if pixel_format.is_hdr() && color_space != SckColorSpace::DisplayP3 { + return Err(SckError::HdrRequiresWideColorSpace); + } + if !(AUDIO_SAMPLE_RATE_MIN_HZ..=AUDIO_SAMPLE_RATE_MAX_HZ).contains(&audio_sample_rate_hz) { + return Err(SckError::InvalidAudioSampleRate(audio_sample_rate_hz)); + } + if !(AUDIO_CHANNEL_COUNT_MIN..=AUDIO_CHANNEL_COUNT_MAX).contains(&audio_channels) { + return Err(SckError::InvalidAudioChannelCount(audio_channels)); + } + let cfg = Self { + target_fps, + queue_depth, + pixel_format, + color_space, + captures_audio, + audio_sample_rate_hz, + audio_channels, + }; + assert!(cfg.target_fps >= FPS_MIN); + assert!(cfg.target_fps <= FPS_MAX); + assert!(cfg.queue_depth >= QUEUE_DEPTH_MIN); + assert!(cfg.queue_depth <= QUEUE_DEPTH_MAX); + assert!(cfg.audio_sample_rate_hz >= AUDIO_SAMPLE_RATE_MIN_HZ); + assert!(cfg.audio_channels >= AUDIO_CHANNEL_COUNT_MIN); + Ok(cfg) + } + + pub fn builder() -> SckCaptureConfigBuilder { + SckCaptureConfigBuilder::default() + } + + pub fn target_fps(&self) -> u32 { + assert!(self.target_fps >= FPS_MIN); + assert!(self.target_fps <= FPS_MAX); + self.target_fps + } + + pub fn queue_depth(&self) -> u32 { + assert!(self.queue_depth >= QUEUE_DEPTH_MIN); + assert!(self.queue_depth <= QUEUE_DEPTH_MAX); + self.queue_depth + } + + pub fn pixel_format(&self) -> SckPixelFormat { + let pf = self.pixel_format; + assert!(pf.as_fourcc() != 0); + pf + } + + pub fn color_space(&self) -> SckColorSpace { + let cs = self.color_space; + assert!(!cs.as_cf_name().is_empty()); + cs + } + + pub fn minimum_frame_interval_ns(&self) -> u64 { + assert!(self.target_fps >= FPS_MIN); + assert!(self.target_fps <= FPS_MAX); + let base_ns: u64 = 1_000_000_000 / (self.target_fps as u64); + let scaled = base_ns * FRAME_INTERVAL_FACTOR_NUM / FRAME_INTERVAL_FACTOR_DEN; + assert!(scaled > 0); + assert!(scaled <= 1_000_000_000); + scaled + } + + pub fn captures_audio(&self) -> bool { + self.captures_audio + } + + pub fn audio_sample_rate_hz(&self) -> u32 { + assert!(self.audio_sample_rate_hz >= AUDIO_SAMPLE_RATE_MIN_HZ); + assert!(self.audio_sample_rate_hz <= AUDIO_SAMPLE_RATE_MAX_HZ); + self.audio_sample_rate_hz + } + + pub fn audio_channels(&self) -> u32 { + assert!(self.audio_channels >= AUDIO_CHANNEL_COUNT_MIN); + assert!(self.audio_channels <= AUDIO_CHANNEL_COUNT_MAX); + self.audio_channels + } +} + +impl Default for SckCaptureConfig { + fn default() -> Self { + let cfg = Self::new_with_audio( + FPS_DEFAULT, + QUEUE_DEPTH_DEFAULT, + SckPixelFormat::Nv12VideoRange, + SckColorSpace::SrgbBt709, + false, + AUDIO_SAMPLE_RATE_DEFAULT_HZ, + AUDIO_CHANNEL_COUNT_DEFAULT, + ); + match cfg { + Ok(c) => c, + Err(_) => unreachable!("default SckCaptureConfig must validate"), + } + } +} + +#[derive(Debug, Clone, Copy)] +pub struct SckCaptureConfigBuilder { + target_fps: u32, + queue_depth: u32, + pixel_format: SckPixelFormat, + color_space: SckColorSpace, + captures_audio: bool, + audio_sample_rate_hz: u32, + audio_channels: u32, +} + +impl Default for SckCaptureConfigBuilder { + fn default() -> Self { + Self { + target_fps: FPS_DEFAULT, + queue_depth: QUEUE_DEPTH_DEFAULT, + pixel_format: SckPixelFormat::Nv12VideoRange, + color_space: SckColorSpace::SrgbBt709, + captures_audio: false, + audio_sample_rate_hz: AUDIO_SAMPLE_RATE_DEFAULT_HZ, + audio_channels: AUDIO_CHANNEL_COUNT_DEFAULT, + } + } +} + +impl SckCaptureConfigBuilder { + pub fn target_fps(mut self, target_fps: u32) -> Self { + self.target_fps = target_fps; + self + } + + pub fn queue_depth(mut self, queue_depth: u32) -> Self { + self.queue_depth = queue_depth; + self + } + + pub fn pixel_format(mut self, pixel_format: SckPixelFormat) -> Self { + self.pixel_format = pixel_format; + self + } + + pub fn color_space(mut self, color_space: SckColorSpace) -> Self { + self.color_space = color_space; + self + } + + pub fn captures_audio(mut self, captures_audio: bool) -> Self { + self.captures_audio = captures_audio; + self + } + + pub fn audio_sample_rate_hz(mut self, audio_sample_rate_hz: u32) -> Self { + self.audio_sample_rate_hz = audio_sample_rate_hz; + self + } + + pub fn audio_channels(mut self, audio_channels: u32) -> Self { + self.audio_channels = audio_channels; + self + } + + pub fn build(self) -> Result { + SckCaptureConfig::new_with_audio( + self.target_fps, + self.queue_depth, + self.pixel_format, + self.color_space, + self.captures_audio, + self.audio_sample_rate_hz, + self.audio_channels, + ) + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum SckCaptureFailure { + StreamStoppedWithError(String), + StreamStartFailed(String), + SystemDeniedAccess, + DisplayDisconnected, + Unknown(String), +} + +impl SckCaptureFailure { + pub fn reason(&self) -> &str { + match self { + SckCaptureFailure::StreamStoppedWithError(m) => m.as_str(), + SckCaptureFailure::StreamStartFailed(m) => m.as_str(), + SckCaptureFailure::SystemDeniedAccess => "screen recording permission denied", + SckCaptureFailure::DisplayDisconnected => "captured display was disconnected", + SckCaptureFailure::Unknown(m) => m.as_str(), + } + } +} + +pub trait CaptureFailureSurface: Send + Sync { + fn on_failure(&self, reason: SckCaptureFailure); +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum AudioSampleFormat { + F32Planar = 0, + F32Interleaved = 1, + I16Interleaved = 2, + Unknown = 3, +} + +pub const AUDIO_SAMPLE_FORMAT_CODE_MAX: u32 = 3; + +impl AudioSampleFormat { + pub fn code(self) -> u32 { + let code = match self { + AudioSampleFormat::F32Planar => 0, + AudioSampleFormat::F32Interleaved => 1, + AudioSampleFormat::I16Interleaved => 2, + AudioSampleFormat::Unknown => 3, + }; + assert!(code <= AUDIO_SAMPLE_FORMAT_CODE_MAX); + assert_eq!(code, self as u32); + code + } + + pub fn as_str(self) -> &'static str { + let s = match self { + AudioSampleFormat::F32Planar => "f32_planar", + AudioSampleFormat::F32Interleaved => "f32_interleaved", + AudioSampleFormat::I16Interleaved => "i16_interleaved", + AudioSampleFormat::Unknown => "unknown", + }; + assert!(!s.is_empty()); + s + } + + pub fn bytes_per_sample(self) -> u32 { + let bytes = match self { + AudioSampleFormat::F32Planar => 4, + AudioSampleFormat::F32Interleaved => 4, + AudioSampleFormat::I16Interleaved => 2, + AudioSampleFormat::Unknown => 0, + }; + assert!(bytes <= 4); + bytes + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct MacScreenShareAudioFrame { + pub sample_rate_hz: u32, + pub channels: u32, + pub num_samples_per_channel: u32, + pub pts_us: i64, +} + +impl MacScreenShareAudioFrame { + pub fn new( + sample_rate_hz: u32, + channels: u32, + num_samples_per_channel: u32, + pts_us: i64, + ) -> Result { + if !(AUDIO_SAMPLE_RATE_MIN_HZ..=AUDIO_SAMPLE_RATE_MAX_HZ).contains(&sample_rate_hz) { + return Err(SckError::InvalidAudioSampleRate(sample_rate_hz)); + } + if !(AUDIO_CHANNEL_COUNT_MIN..=AUDIO_CHANNEL_COUNT_MAX).contains(&channels) { + return Err(SckError::InvalidAudioChannelCount(channels)); + } + assert!(sample_rate_hz >= AUDIO_SAMPLE_RATE_MIN_HZ); + assert!(channels >= AUDIO_CHANNEL_COUNT_MIN); + Ok(Self { + sample_rate_hz, + channels, + num_samples_per_channel, + pts_us, + }) + } +} + +#[derive(Debug, Clone, PartialEq)] +pub struct MacScreenShareAudioFrameWithBytes { + pub sample_rate_hz: u32, + pub channels: u32, + pub num_samples_per_channel: u32, + pub pts_us: i64, + pub format: AudioSampleFormat, + pub samples: Vec, +} + +impl MacScreenShareAudioFrameWithBytes { + pub fn new( + sample_rate_hz: u32, + channels: u32, + num_samples_per_channel: u32, + pts_us: i64, + format: AudioSampleFormat, + samples: Vec, + ) -> Result { + if !(AUDIO_SAMPLE_RATE_MIN_HZ..=AUDIO_SAMPLE_RATE_MAX_HZ).contains(&sample_rate_hz) { + return Err(SckError::InvalidAudioSampleRate(sample_rate_hz)); + } + if !(AUDIO_CHANNEL_COUNT_MIN..=AUDIO_CHANNEL_COUNT_MAX).contains(&channels) { + return Err(SckError::InvalidAudioChannelCount(channels)); + } + assert!(sample_rate_hz >= AUDIO_SAMPLE_RATE_MIN_HZ); + assert!(channels >= AUDIO_CHANNEL_COUNT_MIN); + Ok(Self { + sample_rate_hz, + channels, + num_samples_per_channel, + pts_us, + format, + samples, + }) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn builder_defaults_produce_valid_config() { + let cfg = SckCaptureConfig::builder() + .build() + .expect("default builder"); + assert_eq!(cfg.target_fps(), FPS_DEFAULT); + assert_eq!(cfg.queue_depth(), QUEUE_DEPTH_DEFAULT); + assert_eq!(cfg.pixel_format(), SckPixelFormat::Nv12VideoRange); + assert_eq!(cfg.color_space(), SckColorSpace::SrgbBt709); + } + + #[test] + fn builder_rejects_fps_zero() { + let err = SckCaptureConfig::builder() + .target_fps(0) + .build() + .unwrap_err(); + assert_eq!(err, SckError::InvalidFps(0)); + } + + #[test] + fn builder_rejects_fps_above_max() { + let err = SckCaptureConfig::builder() + .target_fps(FPS_MAX + 1) + .build() + .unwrap_err(); + assert_eq!(err, SckError::InvalidFps(FPS_MAX + 1)); + } + + #[test] + fn builder_accepts_fps_120() { + let cfg = SckCaptureConfig::builder() + .target_fps(FPS_MAX) + .build() + .expect("120 fps ok"); + assert_eq!(cfg.target_fps(), FPS_MAX); + } + + #[test] + fn builder_rejects_queue_depth_zero() { + let err = SckCaptureConfig::builder() + .queue_depth(0) + .build() + .unwrap_err(); + assert_eq!(err, SckError::InvalidQueueDepth(0)); + } + + #[test] + fn builder_rejects_queue_depth_above_max() { + let err = SckCaptureConfig::builder() + .queue_depth(QUEUE_DEPTH_MAX + 1) + .build() + .unwrap_err(); + assert_eq!(err, SckError::InvalidQueueDepth(QUEUE_DEPTH_MAX + 1)); + } + + #[test] + fn builder_rejects_hdr_without_displayp3() { + let err = SckCaptureConfig::builder() + .pixel_format(SckPixelFormat::L10rHdr) + .color_space(SckColorSpace::SrgbBt709) + .build() + .unwrap_err(); + assert_eq!(err, SckError::HdrRequiresWideColorSpace); + } + + #[test] + fn builder_accepts_hdr_with_displayp3() { + let cfg = SckCaptureConfig::builder() + .pixel_format(SckPixelFormat::L10rHdr) + .color_space(SckColorSpace::DisplayP3) + .build() + .expect("hdr with p3 ok"); + assert!(cfg.pixel_format().is_hdr()); + assert_eq!(cfg.color_space(), SckColorSpace::DisplayP3); + } + + #[test] + fn minimum_frame_interval_60_fps_is_15_ms() { + let cfg = SckCaptureConfig::builder() + .target_fps(60) + .build() + .expect("60 fps"); + let ns = cfg.minimum_frame_interval_ns(); + let expected = (1_000_000_000_u64 / 60) * 9 / 10; + assert_eq!(ns, expected); + assert!((14_000_000..=16_000_000).contains(&ns)); + } + + #[test] + fn minimum_frame_interval_30_fps_factor_applied() { + let cfg = SckCaptureConfig::builder() + .target_fps(30) + .build() + .expect("30 fps"); + let ns = cfg.minimum_frame_interval_ns(); + let base = 1_000_000_000_u64 / 30; + assert_eq!(ns, base * 9 / 10); + assert!(ns < base); + } + + #[test] + fn minimum_frame_interval_120_fps_under_8_3ms() { + let cfg = SckCaptureConfig::builder() + .target_fps(120) + .build() + .expect("120 fps"); + let ns = cfg.minimum_frame_interval_ns(); + assert!(ns < 8_400_000); + assert!(ns > 6_000_000); + } + + #[test] + fn pixel_format_fourcc_matches_obs_constants() { + assert_eq!(SckPixelFormat::Bgra8.as_fourcc(), PIXEL_FORMAT_BGRA_FOURCC); + assert_eq!( + SckPixelFormat::L10rHdr.as_fourcc(), + PIXEL_FORMAT_L10R_FOURCC + ); + assert_eq!( + SckPixelFormat::Nv12VideoRange.as_fourcc(), + PIXEL_FORMAT_420V_FOURCC + ); + assert_eq!( + SckPixelFormat::Nv12FullRange.as_fourcc(), + PIXEL_FORMAT_420F_FOURCC + ); + } + + #[test] + fn color_space_names_present() { + assert_eq!( + SckColorSpace::DisplayP3.as_cf_name(), + "kCGColorSpaceDisplayP3" + ); + assert_eq!(SckColorSpace::SrgbBt709.as_cf_name(), "kCGColorSpaceSRGB"); + } + + #[test] + fn failure_reason_strings_non_empty() { + let f = SckCaptureFailure::StreamStoppedWithError("oops".into()); + assert_eq!(f.reason(), "oops"); + let f = SckCaptureFailure::SystemDeniedAccess; + assert!(!f.reason().is_empty()); + let f = SckCaptureFailure::DisplayDisconnected; + assert!(!f.reason().is_empty()); + } + + #[test] + fn default_config_validates() { + let cfg = SckCaptureConfig::default(); + assert_eq!(cfg.target_fps(), FPS_DEFAULT); + assert_eq!(cfg.queue_depth(), QUEUE_DEPTH_DEFAULT); + } + + #[test] + fn captures_audio_defaults_to_false() { + let cfg = SckCaptureConfig::default(); + assert!(!cfg.captures_audio()); + assert_eq!(cfg.audio_sample_rate_hz(), AUDIO_SAMPLE_RATE_DEFAULT_HZ); + assert_eq!(cfg.audio_channels(), AUDIO_CHANNEL_COUNT_DEFAULT); + } + + #[test] + fn builder_captures_audio_toggle_preserves_frame_interval() { + let cfg_off = SckCaptureConfig::builder() + .target_fps(60) + .build() + .expect("60 fps off"); + let cfg_on = SckCaptureConfig::builder() + .target_fps(60) + .captures_audio(true) + .build() + .expect("60 fps on"); + assert!(!cfg_off.captures_audio()); + assert!(cfg_on.captures_audio()); + assert_eq!( + cfg_off.minimum_frame_interval_ns(), + cfg_on.minimum_frame_interval_ns(), + "captures_audio toggle must not alter minimum_frame_interval_ns" + ); + } + + #[test] + fn builder_rejects_audio_sample_rate_zero() { + let err = SckCaptureConfig::builder() + .audio_sample_rate_hz(0) + .build() + .unwrap_err(); + assert_eq!(err, SckError::InvalidAudioSampleRate(0)); + } + + #[test] + fn builder_rejects_audio_channel_count_zero() { + let err = SckCaptureConfig::builder() + .audio_channels(0) + .build() + .unwrap_err(); + assert_eq!(err, SckError::InvalidAudioChannelCount(0)); + } + + #[test] + fn builder_accepts_48khz_stereo_audio() { + let cfg = SckCaptureConfig::builder() + .captures_audio(true) + .audio_sample_rate_hz(48_000) + .audio_channels(2) + .build() + .expect("48k stereo ok"); + assert!(cfg.captures_audio()); + assert_eq!(cfg.audio_sample_rate_hz(), 48_000); + assert_eq!(cfg.audio_channels(), 2); + } + + #[test] + fn mac_screen_share_audio_frame_constructs_valid() { + let frame = + MacScreenShareAudioFrame::new(48_000, 2, 1024, 12_345).expect("valid audio frame"); + assert_eq!(frame.sample_rate_hz, 48_000); + assert_eq!(frame.channels, 2); + assert_eq!(frame.num_samples_per_channel, 1024); + assert_eq!(frame.pts_us, 12_345); + } + + #[test] + fn mac_screen_share_audio_frame_rejects_invalid_sample_rate() { + let err = MacScreenShareAudioFrame::new(0, 2, 1024, 0).expect_err("0 hz invalid"); + assert_eq!(err, SckError::InvalidAudioSampleRate(0)); + } + + #[test] + fn mac_screen_share_audio_frame_rejects_too_many_channels() { + let err = MacScreenShareAudioFrame::new(48_000, 16, 1024, 0).expect_err("16 ch invalid"); + assert_eq!(err, SckError::InvalidAudioChannelCount(16)); + } + + #[test] + fn audio_sample_format_strings_and_widths() { + assert_eq!(AudioSampleFormat::F32Planar.as_str(), "f32_planar"); + assert_eq!( + AudioSampleFormat::F32Interleaved.as_str(), + "f32_interleaved" + ); + assert_eq!( + AudioSampleFormat::I16Interleaved.as_str(), + "i16_interleaved" + ); + assert_eq!(AudioSampleFormat::Unknown.as_str(), "unknown"); + assert_eq!(AudioSampleFormat::F32Planar.bytes_per_sample(), 4); + assert_eq!(AudioSampleFormat::F32Interleaved.bytes_per_sample(), 4); + assert_eq!(AudioSampleFormat::I16Interleaved.bytes_per_sample(), 2); + assert_eq!(AudioSampleFormat::Unknown.bytes_per_sample(), 0); + } + + #[test] + fn audio_sample_format_codes_are_stable_and_bounded() { + assert_eq!(AudioSampleFormat::F32Planar.code(), 0); + assert_eq!(AudioSampleFormat::F32Interleaved.code(), 1); + assert_eq!(AudioSampleFormat::I16Interleaved.code(), 2); + assert_eq!(AudioSampleFormat::Unknown.code(), 3); + assert!(AudioSampleFormat::Unknown.code() <= AUDIO_SAMPLE_FORMAT_CODE_MAX); + } + + #[test] + fn mac_screen_share_audio_frame_with_bytes_round_trip() { + let payload = vec![0xAA_u8; 16]; + let f = MacScreenShareAudioFrameWithBytes::new( + 48_000, + 2, + 4, + 999, + AudioSampleFormat::F32Planar, + payload.clone(), + ) + .expect("valid frame with bytes"); + assert_eq!(f.sample_rate_hz, 48_000); + assert_eq!(f.channels, 2); + assert_eq!(f.num_samples_per_channel, 4); + assert_eq!(f.pts_us, 999); + assert_eq!(f.format, AudioSampleFormat::F32Planar); + assert_eq!(f.samples, payload); + } + + #[test] + fn mac_screen_share_audio_frame_with_bytes_rejects_bad_sample_rate() { + let err = MacScreenShareAudioFrameWithBytes::new( + 0, + 2, + 4, + 0, + AudioSampleFormat::F32Planar, + Vec::new(), + ) + .expect_err("zero hz invalid"); + assert_eq!(err, SckError::InvalidAudioSampleRate(0)); + } +} diff --git a/fluxer_desktop/native/mac-screen-capture/src/encoder_attach.rs b/fluxer_desktop/native/mac-screen-capture/src/encoder_attach.rs new file mode 100644 index 0000000..dcbe596 --- /dev/null +++ b/fluxer_desktop/native/mac-screen-capture/src/encoder_attach.rs @@ -0,0 +1,853 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::collections::VecDeque; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use std::time::Duration; + +use parking_lot::{Condvar, Mutex}; + +pub const CAPTURE_PTS_MAP_CAP: usize = RING_SIZE; +pub const EXTERNAL_SURFACE_QUEUE_CAP: usize = RING_SIZE; +pub const READY_WAIT_TIMEOUT_US_MAX: u64 = 16_667; + +use fluxer_encoder_ring::{ + EncoderFrameRate, EncoderInputRing, EncoderReady, IoSurfaceSlotHandle, + MetalSharedTextureBackend, RING_SIZE, RingError, TextureFormat, +}; + +#[cfg(target_os = "macos")] +use fluxer_encoder_ring::FillReservation; + +#[cfg(target_os = "macos")] +use fluxer_encoder_ring::VtPixelTransfer; + +#[cfg(target_os = "macos")] +use crate::iosurface_pair::{IoSurfaceRaw, iosurface_decrement_use_count}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EncoderAttachError { + AlreadyAttached, + InvalidDimensions { width: u32, height: u32 }, + RingInitFailed, + NotAttached, +} + +impl std::fmt::Display for EncoderAttachError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::AlreadyAttached => write!(f, "encoder ring already attached"), + Self::InvalidDimensions { width, height } => { + write!(f, "encoder ring invalid dimensions {width}x{height}") + } + Self::RingInitFailed => write!(f, "encoder ring initialise failed"), + Self::NotAttached => write!(f, "encoder ring not attached"), + } + } +} + +impl std::error::Error for EncoderAttachError {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct EncoderAttachStats { + pub frames_submitted: u64, + pub frames_dropped: u64, + pub ring_full_events: u64, + pub failed_blits: u64, +} + +#[cfg(target_os = "macos")] +pub struct ExternalSurfaceFrame { + surface: IoSurfaceRaw, + sequence: u64, + capture_pts_us: i64, +} + +#[cfg(target_os = "macos")] +unsafe impl Send for ExternalSurfaceFrame {} + +#[cfg(target_os = "macos")] +impl ExternalSurfaceFrame { + pub fn surface(&self) -> IoSurfaceRaw { + assert!(self.sequence > 0, "external frame sequence starts at one"); + assert!(self.surface.as_ptr() as usize != 0, "surface ptr non-null"); + self.surface + } + + pub fn sequence(&self) -> u64 { + assert!(self.sequence > 0, "external frame sequence starts at one"); + assert!(self.surface.as_ptr() as usize != 0, "surface ptr non-null"); + self.sequence + } + + pub fn capture_pts_us(&self) -> i64 { + assert!(self.sequence > 0, "external frame sequence starts at one"); + assert!(self.surface.as_ptr() as usize != 0, "surface ptr non-null"); + self.capture_pts_us + } +} + +#[cfg(target_os = "macos")] +impl Drop for ExternalSurfaceFrame { + fn drop(&mut self) { + unsafe { iosurface_decrement_use_count(self.surface) }; + unsafe { CFRelease(self.surface.as_ptr()) }; + } +} + +#[cfg(target_os = "macos")] +struct BlitDestination { + pixel_buffer: *mut core::ffi::c_void, +} + +#[cfg(target_os = "macos")] +unsafe impl Send for BlitDestination {} + +#[cfg(target_os = "macos")] +unsafe impl Sync for BlitDestination {} + +#[cfg(target_os = "macos")] +impl Drop for BlitDestination { + fn drop(&mut self) { + unsafe { release_pixel_buffer(self.pixel_buffer) }; + } +} + +pub struct EncoderAttachment { + ring: Mutex>, + ready_condvar: Condvar, + attached: AtomicBool, + frames_submitted: AtomicU64, + frames_dropped: AtomicU64, + ring_full_events: AtomicU64, + failed_blits: AtomicU64, + width: u32, + height: u32, + frame_rate: EncoderFrameRate, + capture_pts_by_sequence: Mutex>, + #[cfg(target_os = "macos")] + pixel_transfer: Mutex>, + #[cfg(target_os = "macos")] + blit_destinations: Vec, + #[cfg(target_os = "macos")] + external_surfaces: Mutex>, + #[cfg(target_os = "macos")] + external_sequence: AtomicU64, +} + +impl EncoderAttachment { + pub fn try_new(width: u32, height: u32) -> Result, EncoderAttachError> { + Self::try_new_with_frame_rate(width, height, EncoderFrameRate::default()) + } + + pub fn try_new_with_frame_rate( + width: u32, + height: u32, + frame_rate: EncoderFrameRate, + ) -> Result, EncoderAttachError> { + if width == 0 || height == 0 { + return Err(EncoderAttachError::InvalidDimensions { width, height }); + } + assert!(frame_rate.numerator > 0, "frame rate numerator positive"); + assert!( + frame_rate.denominator > 0, + "frame rate denominator positive" + ); + let mut ring = EncoderInputRing::new(MetalSharedTextureBackend::new()); + ring.initialise(width, height, TextureFormat::Nv12) + .map_err(|_| EncoderAttachError::RingInitFailed)?; + #[cfg(target_os = "macos")] + let blit_destinations = build_blit_destinations(&mut ring, width, height)?; + #[cfg(target_os = "macos")] + let pixel_transfer = VtPixelTransfer::new().ok(); + let attachment = Self { + ring: Mutex::new(ring), + ready_condvar: Condvar::new(), + attached: AtomicBool::new(true), + frames_submitted: AtomicU64::new(0), + frames_dropped: AtomicU64::new(0), + ring_full_events: AtomicU64::new(0), + failed_blits: AtomicU64::new(0), + width, + height, + frame_rate, + capture_pts_by_sequence: Mutex::new(VecDeque::with_capacity(CAPTURE_PTS_MAP_CAP)), + #[cfg(target_os = "macos")] + pixel_transfer: Mutex::new(pixel_transfer), + #[cfg(target_os = "macos")] + blit_destinations, + #[cfg(target_os = "macos")] + external_surfaces: Mutex::new(VecDeque::with_capacity(EXTERNAL_SURFACE_QUEUE_CAP)), + #[cfg(target_os = "macos")] + external_sequence: AtomicU64::new(0), + }; + assert!( + attachment.attached.load(Ordering::Acquire), + "attachment is attached" + ); + assert!(attachment.width > 0, "attachment width positive"); + assert!( + attachment.frame_rate.numerator > 0, + "attachment fps positive" + ); + Ok(Arc::new(attachment)) + } + + pub fn width(&self) -> u32 { + let w = self.width; + assert!(w > 0, "attachment width positive"); + assert!(self.height > 0, "attachment height positive"); + w + } + + pub fn height(&self) -> u32 { + let h = self.height; + assert!(h > 0, "attachment height positive"); + assert!(self.width > 0, "attachment width positive"); + h + } + + pub fn frame_rate(&self) -> EncoderFrameRate { + let rate = self.frame_rate; + assert!(rate.numerator > 0, "attachment fps numerator positive"); + assert!(rate.denominator > 0, "attachment fps denominator positive"); + rate + } + + pub fn is_attached(&self) -> bool { + let a = self.attached.load(Ordering::Acquire); + assert!(self.width > 0, "width intact while reading attached"); + assert!(self.height > 0, "height intact while reading attached"); + a + } + + pub fn detach(&self) { + self.attached.store(false, Ordering::Release); + #[cfg(target_os = "macos")] + { + let drained = core::mem::take(&mut *self.external_surfaces.lock()); + assert!( + drained.len() <= EXTERNAL_SURFACE_QUEUE_CAP, + "external queue bounded at detach" + ); + drop(drained); + } + self.ready_condvar.notify_all(); + } + + pub fn capacity(&self) -> usize { + let cap = RING_SIZE; + assert!(cap > 0, "ring capacity positive"); + assert_eq!(cap, 8, "ring capacity matches RING_SIZE"); + cap + } + + pub fn submit_iosurface_frame(&self, _iosurface_handle: u64) -> Result<(), EncoderAttachError> { + if !self.attached.load(Ordering::Acquire) { + return Err(EncoderAttachError::NotAttached); + } + let mut ring = self.ring.lock(); + let result: Result<(), RingError> = ring.submit_skip_oldest(|_handle| {}); + drop(ring); + match result { + Ok(()) => { + self.frames_submitted.fetch_add(1, Ordering::Relaxed); + self.ready_condvar.notify_one(); + Ok(()) + } + Err(RingError::FullDropped { .. }) => { + self.frames_dropped.fetch_add(1, Ordering::Relaxed); + self.ring_full_events.fetch_add(1, Ordering::Relaxed); + Ok(()) + } + Err(_) => Err(EncoderAttachError::RingInitFailed), + } + } + + #[cfg(target_os = "macos")] + pub fn submit_with_blit( + &self, + source_pixel_buffer: *mut core::ffi::c_void, + capture_pts_us: i64, + ) -> Result<(), EncoderAttachError> { + assert!( + !source_pixel_buffer.is_null(), + "blit source must be non-null" + ); + assert!(self.width > 0, "attachment width positive"); + if !self.attached.load(Ordering::Acquire) { + return Err(EncoderAttachError::NotAttached); + } + let transfer_guard = self.pixel_transfer.lock(); + let Some(transfer) = transfer_guard.as_ref() else { + self.failed_blits.fetch_add(1, Ordering::Relaxed); + return Err(EncoderAttachError::RingInitFailed); + }; + let Some(reservation) = self.reserve_blit_slot()? else { + return Ok(()); + }; + let slot_index = reservation.slot_index() as usize; + assert!( + slot_index < self.blit_destinations.len(), + "slot index within cached destinations" + ); + let dest_pb = self.blit_destinations[slot_index].pixel_buffer; + assert!( + !dest_pb.is_null(), + "cached destination pixel buffer non-null" + ); + let blit_ok = unsafe { transfer.transfer(source_pixel_buffer, dest_pb) }.is_ok(); + if blit_ok { + self.commit_blit_slot(reservation, capture_pts_us) + } else { + let cancelled = self.ring.lock().cancel(reservation); + assert!(cancelled.is_ok(), "cancel of filling reservation succeeds"); + self.failed_blits.fetch_add(1, Ordering::Relaxed); + Err(EncoderAttachError::RingInitFailed) + } + } + + #[cfg(target_os = "macos")] + fn reserve_blit_slot( + &self, + ) -> Result>, EncoderAttachError> { + assert!(self.width > 0, "attachment width positive"); + assert!(self.height > 0, "attachment height positive"); + let mut ring = self.ring.lock(); + match ring.reserve_skip_oldest() { + Ok(reservation) => { + assert!( + (reservation.slot_index() as usize) < RING_SIZE, + "reserved slot within ring" + ); + Ok(Some(reservation)) + } + Err(RingError::FullDropped { .. }) => { + self.frames_dropped.fetch_add(1, Ordering::Relaxed); + self.ring_full_events.fetch_add(1, Ordering::Relaxed); + Ok(None) + } + Err(_) => Err(EncoderAttachError::RingInitFailed), + } + } + + #[cfg(target_os = "macos")] + fn commit_blit_slot( + &self, + reservation: FillReservation, + capture_pts_us: i64, + ) -> Result<(), EncoderAttachError> { + assert!( + (reservation.slot_index() as usize) < RING_SIZE, + "slot within ring" + ); + let sequence = { + let mut ring = self.ring.lock(); + let sequence = match ring.commit(reservation) { + Ok(sequence) => sequence, + Err(_) => return Err(EncoderAttachError::RingInitFailed), + }; + self.record_capture_pts(sequence, capture_pts_us); + sequence + }; + assert!(sequence > 0, "committed sequence positive"); + self.frames_submitted.fetch_add(1, Ordering::Relaxed); + self.ready_condvar.notify_one(); + Ok(()) + } + + pub fn wait_next_ready(&self, timeout: Duration) -> Option> { + assert!(self.width > 0, "attachment width positive"); + let timeout_bound = Duration::from_micros(READY_WAIT_TIMEOUT_US_MAX); + let bounded_timeout = timeout.min(timeout_bound); + assert!( + bounded_timeout <= timeout_bound, + "wait bounded to one frame interval" + ); + let mut ring = self.ring.lock(); + if let Some(ready) = ring.poll_next_ready() { + return Some(ready); + } + if !self.attached.load(Ordering::Acquire) { + return None; + } + let _ = self.ready_condvar.wait_for(&mut ring, bounded_timeout); + ring.poll_next_ready() + } + + #[cfg(target_os = "macos")] + pub unsafe fn submit_external_surface( + &self, + surface: IoSurfaceRaw, + capture_pts_us: i64, + ) -> Result<(), EncoderAttachError> { + assert!(surface.as_ptr() as usize != 0, "external surface non-null"); + assert!(self.width > 0, "attachment width positive"); + if !self.attached.load(Ordering::Acquire) { + unsafe { CFRelease(surface.as_ptr()) }; + return Err(EncoderAttachError::NotAttached); + } + unsafe { crate::iosurface_pair::iosurface_increment_use_count(surface) }; + let sequence = self.external_sequence.fetch_add(1, Ordering::AcqRel) + 1; + assert!(sequence > 0, "external sequence starts at one"); + let frame = ExternalSurfaceFrame { + surface, + sequence, + capture_pts_us, + }; + let evicted = { + let mut queue = self.external_surfaces.lock(); + let evicted = if queue.len() >= EXTERNAL_SURFACE_QUEUE_CAP { + queue.pop_front() + } else { + None + }; + queue.push_back(frame); + assert!( + queue.len() <= EXTERNAL_SURFACE_QUEUE_CAP, + "external queue bounded" + ); + evicted + }; + if let Some(oldest) = evicted { + drop(oldest); + self.frames_dropped.fetch_add(1, Ordering::Relaxed); + self.ring_full_events.fetch_add(1, Ordering::Relaxed); + } + self.frames_submitted.fetch_add(1, Ordering::Relaxed); + Ok(()) + } + + #[cfg(target_os = "macos")] + pub fn take_external_surface(&self) -> Option { + let frame = { + let mut queue = self.external_surfaces.lock(); + assert!( + queue.len() <= EXTERNAL_SURFACE_QUEUE_CAP, + "external queue bounded" + ); + queue.pop_front() + }; + if let Some(ref taken) = frame { + assert!(taken.sequence > 0, "popped frame has real sequence"); + } + frame + } + + #[cfg(target_os = "macos")] + pub fn external_surface_queue_len(&self) -> usize { + let len = self.external_surfaces.lock().len(); + assert!(len <= EXTERNAL_SURFACE_QUEUE_CAP, "external queue bounded"); + assert!(self.width > 0, "attachment width intact"); + len + } + + fn record_capture_pts(&self, sequence: u64, capture_pts_us: i64) { + assert!(sequence > 0, "ring sequences start at one"); + let mut map = self.capture_pts_by_sequence.lock(); + while map.len() >= CAPTURE_PTS_MAP_CAP { + map.pop_front(); + } + map.push_back((sequence, capture_pts_us)); + assert!(map.len() <= CAPTURE_PTS_MAP_CAP, "pts map bounded"); + } + + pub fn capture_pts_us_for_sequence(&self, sequence: u64) -> Option { + let map = self.capture_pts_by_sequence.lock(); + assert!(map.len() <= CAPTURE_PTS_MAP_CAP, "pts map bounded"); + map.iter() + .find(|(seq, _)| *seq == sequence) + .map(|(_, pts)| *pts) + } + + pub fn note_ring_full(&self) { + self.ring_full_events.fetch_add(1, Ordering::Relaxed); + } + + pub fn stats(&self) -> EncoderAttachStats { + let stats = EncoderAttachStats { + frames_submitted: self.frames_submitted.load(Ordering::Relaxed), + frames_dropped: self.frames_dropped.load(Ordering::Relaxed), + ring_full_events: self.ring_full_events.load(Ordering::Relaxed), + failed_blits: self.failed_blits.load(Ordering::Relaxed), + }; + assert!( + stats.frames_dropped <= stats.ring_full_events + stats.frames_dropped, + "drop counter consistent" + ); + assert!( + stats.frames_submitted <= u64::MAX / 2, + "submitted within plausible bound" + ); + stats + } +} + +#[cfg(target_os = "macos")] +fn build_blit_destinations( + ring: &mut EncoderInputRing, + width: u32, + height: u32, +) -> Result, EncoderAttachError> { + assert!(width > 0, "blit destination width positive"); + assert!(height > 0, "blit destination height positive"); + let mut destinations: Vec = Vec::with_capacity(RING_SIZE); + for slot_index in 0..RING_SIZE { + let surface_ptr = ring + .backend_mut() + .slot_iosurface_ptr(slot_index as u32) + .ok_or(EncoderAttachError::RingInitFailed)?; + assert!(!surface_ptr.is_null(), "slot iosurface ptr non-null"); + let pixel_buffer = unsafe { VtPixelTransfer::wrap_iosurface(surface_ptr, width, height) } + .map_err(|_| EncoderAttachError::RingInitFailed)?; + destinations.push(BlitDestination { pixel_buffer }); + } + assert_eq!(destinations.len(), RING_SIZE, "one destination per slot"); + Ok(destinations) +} + +#[cfg(target_os = "macos")] +#[link(name = "CoreVideo", kind = "framework")] +unsafe extern "C" { + fn CVPixelBufferRelease(buffer: *mut core::ffi::c_void); +} + +#[cfg(target_os = "macos")] +#[link(name = "CoreFoundation", kind = "framework")] +unsafe extern "C" { + fn CFRelease(cf: *const core::ffi::c_void); + fn CFRetain(cf: *const core::ffi::c_void) -> *const core::ffi::c_void; +} + +#[cfg(target_os = "macos")] +unsafe fn release_pixel_buffer(pb: *mut core::ffi::c_void) { + if !pb.is_null() { + unsafe { CVPixelBufferRelease(pb) }; + } +} + +impl Drop for EncoderAttachment { + fn drop(&mut self) { + self.detach(); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn rejects_zero_dimensions() { + let err = EncoderAttachment::try_new(0, 720).err(); + assert!(matches!( + err, + Some(EncoderAttachError::InvalidDimensions { .. }) + )); + } + + #[cfg(not(target_os = "macos"))] + #[test] + fn off_macos_init_fails() { + let err = EncoderAttachment::try_new(640, 480).err(); + assert!(matches!(err, Some(EncoderAttachError::RingInitFailed))); + } + + #[cfg(target_os = "macos")] + #[test] + fn macos_attach_then_detach() { + let attach = EncoderAttachment::try_new(640, 480).expect("attach ok"); + assert!(attach.is_attached()); + assert_eq!(attach.width(), 640); + assert_eq!(attach.height(), 480); + assert_eq!(attach.capacity(), 8); + attach.detach(); + assert!(!attach.is_attached()); + } + + #[cfg(target_os = "macos")] + #[test] + fn macos_submit_records_stats() { + let attach = EncoderAttachment::try_new(64, 64).expect("attach ok"); + for _ in 0..3 { + attach + .submit_iosurface_frame(0xdead_beef) + .expect("submit ok"); + } + let stats = attach.stats(); + assert_eq!(stats.frames_submitted, 3); + assert_eq!(stats.frames_dropped, 0); + assert_eq!(stats.ring_full_events, 0); + } + + #[cfg(target_os = "macos")] + #[test] + fn wait_next_ready_returns_submitted_frame() { + let attach = EncoderAttachment::try_new(64, 64).expect("attach ok"); + attach + .submit_iosurface_frame(0xdead_beef) + .expect("submit ok"); + let ready = attach + .wait_next_ready(Duration::from_millis(5)) + .expect("frame ready"); + assert_eq!(ready.sequence, 1); + assert_eq!(ready.duplicate_count, 0); + assert!(attach.wait_next_ready(Duration::from_millis(1)).is_none()); + } + + #[cfg(target_os = "macos")] + #[test] + fn wait_next_ready_timeout_is_capped_at_frame_interval() { + let attach = EncoderAttachment::try_new(64, 64).expect("attach ok"); + let start = std::time::Instant::now(); + let ready = attach.wait_next_ready(Duration::from_secs(60)); + assert!(ready.is_none()); + assert!( + start.elapsed() < Duration::from_millis(500), + "wait returned within the named frame-interval bound" + ); + } + + #[cfg(target_os = "macos")] + #[test] + fn wait_next_ready_wakes_for_concurrent_submit() { + let attach = EncoderAttachment::try_new(64, 64).expect("attach ok"); + let waiter = { + let attach = Arc::clone(&attach); + std::thread::spawn(move || attach.wait_next_ready(Duration::from_millis(15))) + }; + std::thread::sleep(Duration::from_millis(3)); + attach + .submit_iosurface_frame(0xdead_beef) + .expect("submit ok"); + let ready = waiter.join().expect("waiter joins"); + let sequence = ready.map(|r| r.sequence); + assert!(sequence == Some(1) || sequence.is_none()); + if sequence.is_none() { + let retry = attach.wait_next_ready(Duration::from_millis(5)); + assert_eq!(retry.map(|r| r.sequence), Some(1)); + } + } + + #[cfg(target_os = "macos")] + #[test] + fn wait_next_ready_returns_none_after_detach() { + let attach = EncoderAttachment::try_new(64, 64).expect("attach ok"); + attach.detach(); + assert!(!attach.is_attached()); + assert!(attach.wait_next_ready(Duration::from_millis(5)).is_none()); + } + + #[cfg(target_os = "macos")] + #[test] + fn macos_submit_rejected_when_detached() { + let attach = EncoderAttachment::try_new(64, 64).expect("attach ok"); + attach.detach(); + let err = attach.submit_iosurface_frame(0xdead_beef).err(); + assert!(matches!(err, Some(EncoderAttachError::NotAttached))); + } + + #[cfg(target_os = "macos")] + #[test] + fn submit_encoder_ring_frame_blits_iosurface_to_slot() { + use core::ptr::NonNull; + use objc2_core_foundation::{CFDictionary, CFRetained}; + use objc2_core_video::{ + CVPixelBuffer, CVPixelBufferGetBaseAddressOfPlane, CVPixelBufferGetBytesPerRowOfPlane, + CVPixelBufferGetHeightOfPlane, CVPixelBufferLockBaseAddress, CVPixelBufferLockFlags, + CVPixelBufferUnlockBaseAddress, kCVPixelBufferIOSurfacePropertiesKey, + }; + + let attach = EncoderAttachment::try_new(256, 256).expect("attach ok"); + let mut empty_keys: [*const core::ffi::c_void; 0] = []; + let mut empty_vals: [*const core::ffi::c_void; 0] = []; + let iosurf_dict: CFRetained = unsafe { + CFDictionary::new( + None, + empty_keys.as_mut_ptr(), + empty_vals.as_mut_ptr(), + 0, + &objc2_core_foundation::kCFTypeDictionaryKeyCallBacks, + &objc2_core_foundation::kCFTypeDictionaryValueCallBacks, + ) + .expect("iosurf empty dict") + }; + let key_ref: &objc2_core_foundation::CFString = + unsafe { kCVPixelBufferIOSurfacePropertiesKey }; + let key_ptr: *const core::ffi::c_void = key_ref as *const _ as *const core::ffi::c_void; + let val_ptr: *const core::ffi::c_void = + &*iosurf_dict as *const _ as *const core::ffi::c_void; + let mut keys = [key_ptr]; + let mut vals = [val_ptr]; + let attrs: CFRetained = unsafe { + CFDictionary::new( + None, + keys.as_mut_ptr(), + vals.as_mut_ptr(), + 1, + &objc2_core_foundation::kCFTypeDictionaryKeyCallBacks, + &objc2_core_foundation::kCFTypeDictionaryValueCallBacks, + ) + .expect("attrs dict") + }; + let nv12: u32 = u32::from_be_bytes(*b"420v"); + let mut pb_out: *mut CVPixelBuffer = core::ptr::null_mut(); + let status = unsafe { + objc2_core_video::CVPixelBufferCreate( + None, + 256, + 256, + nv12, + Some(&attrs), + NonNull::new(&mut pb_out).expect("pb_out non-null"), + ) + }; + assert_eq!(status, 0, "CVPixelBufferCreate ok"); + let source_pb: CFRetained = + unsafe { CFRetained::from_raw(NonNull::new(pb_out).expect("pb non-null")) }; + let lock_flags = CVPixelBufferLockFlags(0); + let lock_st = unsafe { CVPixelBufferLockBaseAddress(&source_pb, lock_flags) }; + assert_eq!(lock_st, 0, "lock ok"); + let y_ptr = CVPixelBufferGetBaseAddressOfPlane(&source_pb, 0); + let y_stride = CVPixelBufferGetBytesPerRowOfPlane(&source_pb, 0); + let y_h = CVPixelBufferGetHeightOfPlane(&source_pb, 0); + assert!(!y_ptr.is_null(), "Y plane base non-null"); + assert!(y_stride > 0, "Y stride positive"); + for row in 0..y_h { + for col in 0..y_stride { + unsafe { + (y_ptr as *mut u8).add(row * y_stride + col).write(0x55); + } + } + } + let _ = unsafe { CVPixelBufferUnlockBaseAddress(&source_pb, lock_flags) }; + let src_ptr = &*source_pb as *const CVPixelBuffer as *mut core::ffi::c_void; + attach + .submit_with_blit(src_ptr, 41_500) + .expect("submit_with_blit ok"); + let stats = attach.stats(); + assert_eq!(stats.frames_submitted, 1, "one frame submitted"); + assert_eq!(stats.failed_blits, 0, "no failed blits"); + assert_eq!( + attach.capture_pts_us_for_sequence(1), + Some(41_500), + "capture pts recorded for ring sequence" + ); + assert_eq!(attach.capture_pts_us_for_sequence(2), None); + } + + #[cfg(target_os = "macos")] + #[test] + fn external_surface_submit_take_round_trip() { + use core::ptr::NonNull; + use fluxer_encoder_ring::metal_iosurface_macos::OwnedIoSurface; + + let attach = EncoderAttachment::try_new(64, 64).expect("attach ok"); + let owned = OwnedIoSurface::create_nv12(64, 64).expect("surface"); + let raw = NonNull::new(owned.as_ptr()).expect("non-null surface"); + let use_before = unsafe { crate::iosurface_pair::iosurface_use_count(raw) }; + unsafe { CFRetain(raw.as_ptr()) }; + unsafe { attach.submit_external_surface(raw, 41_500) }.expect("submit ok"); + assert_eq!(attach.external_surface_queue_len(), 1); + let use_during = unsafe { crate::iosurface_pair::iosurface_use_count(raw) }; + assert_eq!(use_during, use_before + 1); + let stats = attach.stats(); + assert_eq!(stats.frames_submitted, 1); + assert_eq!(stats.frames_dropped, 0); + assert_eq!(stats.ring_full_events, 0); + let frame = attach.take_external_surface().expect("frame queued"); + assert_eq!(frame.surface(), raw); + assert_eq!(frame.sequence(), 1); + assert_eq!(frame.capture_pts_us(), 41_500); + drop(frame); + let use_after = unsafe { crate::iosurface_pair::iosurface_use_count(raw) }; + assert_eq!(use_after, use_before); + assert_eq!(attach.external_surface_queue_len(), 0); + } + + #[cfg(target_os = "macos")] + #[test] + fn external_surface_queue_evicts_oldest_when_full() { + use core::ptr::NonNull; + use fluxer_encoder_ring::metal_iosurface_macos::OwnedIoSurface; + + let attach = EncoderAttachment::try_new(64, 64).expect("attach ok"); + let total = EXTERNAL_SURFACE_QUEUE_CAP + 2; + let mut owned: Vec = Vec::with_capacity(total); + for i in 0..total { + let surface = OwnedIoSurface::create_nv12(64, 64).expect("surface"); + let raw = NonNull::new(surface.as_ptr()).expect("non-null surface"); + unsafe { CFRetain(raw.as_ptr()) }; + unsafe { attach.submit_external_surface(raw, (i as i64) * 1_000) }.expect("submit ok"); + owned.push(surface); + } + assert_eq!( + attach.external_surface_queue_len(), + EXTERNAL_SURFACE_QUEUE_CAP + ); + let stats = attach.stats(); + assert_eq!(stats.frames_submitted, total as u64); + assert_eq!(stats.frames_dropped, 2); + assert_eq!(stats.ring_full_events, 2); + let oldest_remaining = attach.take_external_surface().expect("frame queued"); + assert_eq!(oldest_remaining.sequence(), 3); + assert_eq!(oldest_remaining.capture_pts_us(), 2_000); + } + + #[cfg(target_os = "macos")] + #[test] + fn detach_drains_external_surface_queue_and_rejects_submit() { + use core::ptr::NonNull; + use fluxer_encoder_ring::metal_iosurface_macos::OwnedIoSurface; + + let attach = EncoderAttachment::try_new(64, 64).expect("attach ok"); + let owned = OwnedIoSurface::create_nv12(64, 64).expect("surface"); + let raw = NonNull::new(owned.as_ptr()).expect("non-null surface"); + let use_before = unsafe { crate::iosurface_pair::iosurface_use_count(raw) }; + unsafe { CFRetain(raw.as_ptr()) }; + unsafe { attach.submit_external_surface(raw, 7) }.expect("submit ok"); + assert_eq!(attach.external_surface_queue_len(), 1); + attach.detach(); + assert_eq!(attach.external_surface_queue_len(), 0); + assert_eq!( + unsafe { crate::iosurface_pair::iosurface_use_count(raw) }, + use_before + ); + unsafe { CFRetain(raw.as_ptr()) }; + let err = unsafe { attach.submit_external_surface(raw, 8) }.err(); + assert!(matches!(err, Some(EncoderAttachError::NotAttached))); + assert_eq!(attach.external_surface_queue_len(), 0); + assert_eq!( + unsafe { crate::iosurface_pair::iosurface_use_count(raw) }, + use_before + ); + } + + #[cfg(target_os = "macos")] + #[test] + fn capture_pts_map_stays_bounded_and_evicts_oldest() { + let attach = EncoderAttachment::try_new(64, 64).expect("attach ok"); + let total = (CAPTURE_PTS_MAP_CAP as u64) + 4; + for sequence in 1..=total { + attach.record_capture_pts(sequence, (sequence as i64) * 1_000); + } + assert_eq!( + attach.capture_pts_us_for_sequence(1), + None, + "oldest evicted" + ); + assert_eq!( + attach.capture_pts_us_for_sequence(4), + None, + "oldest evicted" + ); + assert_eq!( + attach.capture_pts_us_for_sequence(5), + Some(5_000), + "newest retained" + ); + assert_eq!( + attach.capture_pts_us_for_sequence(total), + Some((total as i64) * 1_000) + ); + } +} diff --git a/fluxer_desktop/native/mac-screen-capture/src/foundation.rs b/fluxer_desktop/native/mac-screen-capture/src/foundation.rs new file mode 100644 index 0000000..ec28e79 --- /dev/null +++ b/fluxer_desktop/native/mac-screen-capture/src/foundation.rs @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use objc2_foundation::{NSError, NSProcessInfo}; + +pub fn operating_system_version_string() -> String { + let info = NSProcessInfo::processInfo(); + info.operatingSystemVersionString().to_string() +} + +pub fn ns_error_localized_description(err: &NSError) -> String { + err.localizedDescription().to_string() +} diff --git a/fluxer_desktop/native/mac-screen-capture/src/iosurface_pair.rs b/fluxer_desktop/native/mac-screen-capture/src/iosurface_pair.rs new file mode 100644 index 0000000..7efc69e --- /dev/null +++ b/fluxer_desktop/native/mac-screen-capture/src/iosurface_pair.rs @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use core::ffi::c_void; +use core::ptr::NonNull; + +pub type IoSurfaceRaw = NonNull; + +#[cfg(target_os = "macos")] +#[link(name = "IOSurface", kind = "framework")] +unsafe extern "C" { + fn IOSurfaceIncrementUseCount(buffer: *mut c_void); + fn IOSurfaceDecrementUseCount(buffer: *mut c_void); + fn IOSurfaceGetUseCount(buffer: *mut c_void) -> i32; +} + +#[cfg(target_os = "macos")] +pub unsafe fn iosurface_increment_use_count(surface: IoSurfaceRaw) { + unsafe { IOSurfaceIncrementUseCount(surface.as_ptr()) }; +} + +#[cfg(target_os = "macos")] +pub unsafe fn iosurface_decrement_use_count(surface: IoSurfaceRaw) { + unsafe { IOSurfaceDecrementUseCount(surface.as_ptr()) }; +} + +#[cfg(target_os = "macos")] +pub unsafe fn iosurface_use_count(surface: IoSurfaceRaw) -> i32 { + unsafe { IOSurfaceGetUseCount(surface.as_ptr()) } +} + +#[cfg(not(target_os = "macos"))] +pub unsafe fn iosurface_increment_use_count(_surface: IoSurfaceRaw) {} + +#[cfg(not(target_os = "macos"))] +pub unsafe fn iosurface_decrement_use_count(_surface: IoSurfaceRaw) {} + +#[cfg(not(target_os = "macos"))] +pub unsafe fn iosurface_use_count(_surface: IoSurfaceRaw) -> i32 { + 0 +} + +pub struct IoSurfacePair { + current: Option, + prev: Option, +} + +unsafe impl Send for IoSurfacePair {} + +impl IoSurfacePair { + pub fn new() -> Self { + let pair = Self { + current: None, + prev: None, + }; + assert!(pair.current.is_none()); + assert!(pair.prev.is_none()); + pair + } + + pub fn has_current(&self) -> bool { + let has = self.current.is_some(); + assert!(has == self.current.is_some()); + has + } + + pub fn has_prev(&self) -> bool { + let has = self.prev.is_some(); + assert!(has == self.prev.is_some()); + has + } + + pub unsafe fn push(&mut self, new: IoSurfaceRaw) { + unsafe { iosurface_increment_use_count(new) }; + let evicted = self.prev.take(); + let rotated = self.current.take(); + self.prev = rotated; + self.current = Some(new); + assert!(self.current.is_some()); + if let Some(old) = evicted { + unsafe { iosurface_decrement_use_count(old) }; + } + } + + pub fn take_current(&mut self) -> Option { + let taken = self.current.take(); + assert!(self.current.is_none()); + taken + } + + pub fn peek_current(&self) -> Option { + self.current + } + + pub fn peek_prev(&self) -> Option { + self.prev + } + + pub fn clear(&mut self) { + let cur = self.current.take(); + let prev = self.prev.take(); + assert!(self.current.is_none()); + assert!(self.prev.is_none()); + if let Some(s) = cur { + unsafe { iosurface_decrement_use_count(s) }; + } + if let Some(s) = prev { + unsafe { iosurface_decrement_use_count(s) }; + } + } +} + +impl Default for IoSurfacePair { + fn default() -> Self { + Self::new() + } +} + +impl Drop for IoSurfacePair { + fn drop(&mut self) { + let cur = self.current.take(); + let prev = self.prev.take(); + if let Some(s) = cur { + unsafe { iosurface_decrement_use_count(s) }; + } + if let Some(s) = prev { + unsafe { iosurface_decrement_use_count(s) }; + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use core::ptr::NonNull; + + fn fake_surface(addr: usize) -> IoSurfaceRaw { + assert!(addr != 0); + NonNull::new(addr as *mut c_void).expect("non-null fake surface") + } + + #[test] + fn new_pair_is_empty() { + let pair = IoSurfacePair::new(); + assert!(!pair.has_current()); + assert!(!pair.has_prev()); + } + + #[test] + fn take_current_on_empty_returns_none() { + let mut pair = IoSurfacePair::new(); + assert!(pair.take_current().is_none()); + } + + #[cfg(not(target_os = "macos"))] + #[test] + fn push_rotates_current_to_prev_offplatform() { + let mut pair = IoSurfacePair::new(); + let a = fake_surface(0x1); + let b = fake_surface(0x2); + unsafe { + pair.push(a); + } + assert_eq!(pair.peek_current(), Some(a)); + assert!(pair.peek_prev().is_none()); + unsafe { + pair.push(b); + } + assert_eq!(pair.peek_current(), Some(b)); + assert_eq!(pair.peek_prev(), Some(a)); + } + + #[cfg(not(target_os = "macos"))] + #[test] + fn take_current_clears_slot_offplatform() { + let mut pair = IoSurfacePair::new(); + let a = fake_surface(0x10); + unsafe { + pair.push(a); + } + let taken = pair.take_current().expect("current is set"); + assert_eq!(taken, a); + assert!(pair.peek_current().is_none()); + } + + #[cfg(not(target_os = "macos"))] + #[test] + fn drop_pair_is_safe_offplatform() { + let mut pair = IoSurfacePair::new(); + unsafe { + pair.push(fake_surface(0x100)); + pair.push(fake_surface(0x200)); + } + drop(pair); + } +} + +#[cfg(all(test, target_os = "macos"))] +mod macos_tests { + use super::*; + use core::ffi::c_void; + use core::ptr::NonNull; + use objc2_core_foundation::{ + CFDictionary, CFNumber, CFRetained, CFString, kCFAllocatorDefault, + kCFTypeDictionaryKeyCallBacks, kCFTypeDictionaryValueCallBacks, + }; + + #[link(name = "IOSurface", kind = "framework")] + unsafe extern "C" { + fn IOSurfaceCreate(properties: *const CFDictionary) -> *mut c_void; + } + + fn cf_number_i32(v: i32) -> CFRetained { + CFNumber::new_i32(v) + } + + fn cf_string(s: &'static str) -> CFRetained { + CFString::from_static_str(s) + } + + fn make_iosurface() -> IoSurfaceRaw { + let width_key = cf_string("IOSurfaceWidth"); + let height_key = cf_string("IOSurfaceHeight"); + let bpe_key = cf_string("IOSurfaceBytesPerElement"); + let pf_key = cf_string("IOSurfacePixelFormat"); + let width_val = cf_number_i32(32); + let height_val = cf_number_i32(32); + let bpe_val = cf_number_i32(4); + let pf_val = cf_number_i32(i32::from_be_bytes(*b"BGRA")); + let keys: [*const c_void; 4] = [ + CFRetained::as_ptr(&width_key).as_ptr() as *const c_void, + CFRetained::as_ptr(&height_key).as_ptr() as *const c_void, + CFRetained::as_ptr(&bpe_key).as_ptr() as *const c_void, + CFRetained::as_ptr(&pf_key).as_ptr() as *const c_void, + ]; + let vals: [*const c_void; 4] = [ + CFRetained::as_ptr(&width_val).as_ptr() as *const c_void, + CFRetained::as_ptr(&height_val).as_ptr() as *const c_void, + CFRetained::as_ptr(&bpe_val).as_ptr() as *const c_void, + CFRetained::as_ptr(&pf_val).as_ptr() as *const c_void, + ]; + let dict_opt = unsafe { + CFDictionary::new( + kCFAllocatorDefault, + keys.as_ptr() as *mut *const c_void, + vals.as_ptr() as *mut *const c_void, + 4, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks, + ) + }; + let dict = dict_opt.expect("CFDictionary::new returned non-null"); + let dict_ptr = CFRetained::as_ptr(&dict).as_ptr() as *const CFDictionary; + let raw = unsafe { IOSurfaceCreate(dict_ptr) }; + assert!(!raw.is_null(), "IOSurfaceCreate must produce a surface"); + NonNull::new(raw).expect("non-null IOSurface") + } + + #[test] + fn push_increments_use_count_and_take_current_does_not_release() { + let a = make_iosurface(); + let before = unsafe { iosurface_use_count(a) }; + let mut pair = IoSurfacePair::new(); + unsafe { + pair.push(a); + } + let after_push = unsafe { iosurface_use_count(a) }; + assert_eq!(after_push, before + 1); + let taken = pair.take_current().expect("current set"); + assert_eq!(taken, a); + let after_take = unsafe { iosurface_use_count(a) }; + assert_eq!(after_take, before + 1); + unsafe { iosurface_decrement_use_count(a) }; + let after_balance = unsafe { iosurface_use_count(a) }; + assert_eq!(after_balance, before); + } + + #[test] + fn push_rotates_and_drop_releases_both_slots() { + let a = make_iosurface(); + let b = make_iosurface(); + let before_a = unsafe { iosurface_use_count(a) }; + let before_b = unsafe { iosurface_use_count(b) }; + let mut pair = IoSurfacePair::new(); + unsafe { + pair.push(a); + pair.push(b); + } + assert_eq!(pair.peek_current(), Some(b)); + assert_eq!(pair.peek_prev(), Some(a)); + assert_eq!(unsafe { iosurface_use_count(a) }, before_a + 1); + assert_eq!(unsafe { iosurface_use_count(b) }, before_b + 1); + drop(pair); + assert_eq!(unsafe { iosurface_use_count(a) }, before_a); + assert_eq!(unsafe { iosurface_use_count(b) }, before_b); + } + + #[test] + fn third_push_evicts_oldest_and_releases_it() { + let a = make_iosurface(); + let b = make_iosurface(); + let c = make_iosurface(); + let before_a = unsafe { iosurface_use_count(a) }; + let mut pair = IoSurfacePair::new(); + unsafe { + pair.push(a); + pair.push(b); + pair.push(c); + } + assert_eq!(unsafe { iosurface_use_count(a) }, before_a); + assert_eq!(pair.peek_current(), Some(c)); + assert_eq!(pair.peek_prev(), Some(b)); + } + + #[test] + fn clear_releases_both_slots() { + let a = make_iosurface(); + let b = make_iosurface(); + let before_a = unsafe { iosurface_use_count(a) }; + let before_b = unsafe { iosurface_use_count(b) }; + let mut pair = IoSurfacePair::new(); + unsafe { + pair.push(a); + pair.push(b); + } + pair.clear(); + assert_eq!(unsafe { iosurface_use_count(a) }, before_a); + assert_eq!(unsafe { iosurface_use_count(b) }, before_b); + assert!(pair.peek_current().is_none()); + assert!(pair.peek_prev().is_none()); + } +} diff --git a/fluxer_desktop/native/mac-screen-capture/src/lib.rs b/fluxer_desktop/native/mac-screen-capture/src/lib.rs new file mode 100644 index 0000000..19d0397 --- /dev/null +++ b/fluxer_desktop/native/mac-screen-capture/src/lib.rs @@ -0,0 +1,44 @@ +#![deny(clippy::all)] +// SPDX-License-Identifier: AGPL-3.0-or-later +#![allow(dead_code)] +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::collapsible_if)] +#![allow(clippy::manual_is_multiple_of)] +#![allow(clippy::manual_slice_size_calculation)] +#![allow(clippy::unnecessary_cast)] +#![allow(clippy::not_unsafe_ptr_arg_deref)] +#![allow(clippy::missing_transmute_annotations)] +#![allow(clippy::missing_const_for_thread_local)] +#![allow(clippy::too_many_arguments)] + +pub mod audio_pool; +pub mod config; +pub mod encoder_attach; +pub mod iosurface_pair; +pub mod os_version; + +pub use audio_pool::{ + MAC_AUDIO_POOL_CAP, MAX_FRAME_BYTES_PER_SLOT, MacAudioError, MacAudioFramePool, + MacAudioPoolStats, PooledMacAudioFrame, +}; +pub use config::{ + AUDIO_CHANNEL_COUNT_DEFAULT, AUDIO_CHANNEL_COUNT_MAX, AUDIO_CHANNEL_COUNT_MIN, + AUDIO_SAMPLE_RATE_DEFAULT_HZ, AUDIO_SAMPLE_RATE_MAX_HZ, AUDIO_SAMPLE_RATE_MIN_HZ, + AudioSampleFormat, CaptureFailureSurface, FPS_DEFAULT, FPS_MAX, FPS_MIN, + MacScreenShareAudioFrame, MacScreenShareAudioFrameWithBytes, QUEUE_DEPTH_DEFAULT, + QUEUE_DEPTH_MAX, QUEUE_DEPTH_MIN, SckCaptureConfig, SckCaptureConfigBuilder, SckCaptureFailure, + SckColorSpace, SckError, SckPixelFormat, +}; +pub use encoder_attach::{EncoderAttachError, EncoderAttachStats, EncoderAttachment}; +pub use iosurface_pair::{IoSurfacePair, IoSurfaceRaw}; + +#[cfg(target_os = "macos")] +pub mod foundation; +#[cfg(target_os = "macos")] +pub mod sck; + +#[cfg(target_os = "macos")] +mod napi_surface_macos; + +#[cfg(not(target_os = "macos"))] +mod napi_surface_stub; diff --git a/fluxer_desktop/native/mac-screen-capture/src/napi_surface_macos.rs b/fluxer_desktop/native/mac-screen-capture/src/napi_surface_macos.rs new file mode 100644 index 0000000..930805f --- /dev/null +++ b/fluxer_desktop/native/mac-screen-capture/src/napi_surface_macos.rs @@ -0,0 +1,2559 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::ffi::c_void; +use std::ptr; +use std::sync::atomic::{AtomicBool, AtomicU32, AtomicU64, Ordering}; +use std::sync::{Arc, Condvar, Mutex, MutexGuard, Weak}; +use std::time::Duration; + +use block2::RcBlock; +use napi::bindgen_prelude::{Buffer, BufferSlice, Function, Result, Unknown}; +use napi::threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}; +use napi::{JsValue, Status, ValueType}; +use napi_derive::napi; + +use objc2::rc::Retained; +use objc2::runtime::{NSObject, NSObjectProtocol, ProtocolObject}; +use objc2::{AllocAnyThread, DefinedClass, define_class, msg_send}; +use objc2_core_audio_types::{ + AudioBufferList, AudioStreamBasicDescription, kAudioFormatFlagIsFloat, + kAudioFormatFlagIsNonInterleaved, kAudioFormatFlagIsSignedInteger, kAudioFormatLinearPCM, +}; +use objc2_core_foundation::{CFRetained, CGPoint, CGRect, CGSize}; +use objc2_core_media::{ + CMAudioFormatDescriptionGetStreamBasicDescription, CMBlockBuffer, CMSampleBuffer, +}; +use objc2_core_video::{ + CVPixelBuffer, CVPixelBufferGetHeight, CVPixelBufferGetIOSurface, + CVPixelBufferGetPixelFormatType, CVPixelBufferGetPlaneCount, CVPixelBufferGetWidth, + CVPixelBufferLockBaseAddress, CVPixelBufferLockFlags, CVPixelBufferUnlockBaseAddress, + kCVPixelFormatType_32BGRA, +}; +use objc2_foundation::{NSArray, NSError, NSString}; +use objc2_screen_capture_kit::{ + SCContentFilter, SCDisplay, SCShareableContent, SCStream, SCStreamConfiguration, + SCStreamDelegate, SCStreamOutput, SCStreamOutputType, SCWindow, +}; + +use fluxer_screen_frame_bus::{ + self as frame_bus, EnqueueOutcome, MacCvPixelBufferFrame, NativeScreenFrameSinkHandle, + NativeScreenFrameSinkHandleRef, ScreenFrame as BusScreenFrame, +}; + +use crate::audio_pool::{ + MAC_AUDIO_POOL_CAP, MAX_FRAME_BYTES_PER_SLOT, MacAudioFramePool, PooledMacAudioFrame, +}; +use crate::config::{ + AUDIO_CHANNEL_COUNT_DEFAULT, AUDIO_SAMPLE_RATE_DEFAULT_HZ, AudioSampleFormat, + CaptureFailureSurface, SckCaptureConfig, SckCaptureFailure, SckColorSpace, SckPixelFormat, +}; +use crate::encoder_attach::EncoderAttachment; +use crate::foundation; +use crate::os_version::{ + self, SCK_MIN_MACOS, SupportClassification, classify_support, format_version, +}; +use crate::sck; +use fluxer_encoder_ring::EncoderFrameRate; + +const DEFAULT_TIMEOUT_NS: u64 = 30 * 1_000_000_000; +const PIXEL_FORMAT_420V: u32 = u32::from_be_bytes(*b"420v"); +const PIXEL_FORMAT_420F: u32 = u32::from_be_bytes(*b"420f"); +#[link(name = "CoreGraphics", kind = "framework")] +unsafe extern "C" { + fn CGPreflightScreenCaptureAccess() -> bool; +} + +#[link(name = "CoreVideo", kind = "framework")] +unsafe extern "C" { + fn CVPixelBufferRetain(pixel_buffer: *mut c_void) -> *mut c_void; + fn CVPixelBufferRelease(pixel_buffer: *mut c_void); +} + +#[derive(Debug, Eq, PartialEq)] +enum AsyncError { + Timeout, + SckErr, +} + +struct WakerInner { + state: Mutex, + cv: Condvar, +} + +struct WakerState { + done: bool, + failed: bool, + err: Option>, + content: Option>, +} + +unsafe impl Send for WakerInner {} +unsafe impl Sync for WakerInner {} + +fn new_waker() -> Arc { + Arc::new(WakerInner { + state: Mutex::new(WakerState { + done: false, + failed: false, + err: None, + content: None, + }), + cv: Condvar::new(), + }) +} + +fn lock_waker_state(w: &WakerInner) -> MutexGuard<'_, WakerState> { + w.state + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()) +} + +fn wait_deadline(w: &Arc, timeout_ns: u64) -> bool { + let s = lock_waker_state(w); + if s.done { + return true; + } + let dur = Duration::from_nanos(timeout_ns); + match w.cv.wait_timeout(s, dur) { + Ok((state, _)) => state.done, + Err(poisoned) => poisoned.into_inner().0.done, + } +} + +fn retain_error(err: *mut NSError) -> std::result::Result>, AsyncError> { + if err.is_null() { + Ok(None) + } else { + unsafe { Retained::retain(err) } + .map(Some) + .ok_or(AsyncError::SckErr) + } +} + +fn retain_content( + content: *mut SCShareableContent, +) -> std::result::Result>, AsyncError> { + if content.is_null() { + Ok(None) + } else { + unsafe { Retained::retain(content) } + .map(Some) + .ok_or(AsyncError::SckErr) + } +} + +fn complete_waker( + waker: &WakerInner, + err: Option>, + content: Option>, + failed: bool, +) { + let mut s = lock_waker_state(waker); + s.err = err; + s.content = content; + s.failed = failed; + s.done = true; + waker.cv.notify_all(); +} + +fn await_start(stream: &SCStream, timeout_ns: u64) -> std::result::Result<(), AsyncError> { + let waker = new_waker(); + let waker_cb = waker.clone(); + let blk = RcBlock::new(move |err: *mut NSError| { + let (err_opt, failed) = match retain_error(err) { + Ok(opt) => (opt, false), + Err(_) => (None, true), + }; + complete_waker(&waker_cb, err_opt, None, failed); + }); + unsafe { + stream.startCaptureWithCompletionHandler(Some(&blk)); + } + if !wait_deadline(&waker, timeout_ns) { + return Err(AsyncError::Timeout); + } + let s = lock_waker_state(&waker); + if s.failed || s.err.is_some() { + return Err(AsyncError::SckErr); + } + Ok(()) +} + +fn await_stop(stream: &SCStream, timeout_ns: u64) -> std::result::Result<(), AsyncError> { + let waker = new_waker(); + let waker_cb = waker.clone(); + let blk = RcBlock::new(move |err: *mut NSError| { + let (err_opt, failed) = match retain_error(err) { + Ok(opt) => (opt, false), + Err(_) => (None, true), + }; + complete_waker(&waker_cb, err_opt, None, failed); + }); + unsafe { + stream.stopCaptureWithCompletionHandler(Some(&blk)); + } + if !wait_deadline(&waker, timeout_ns) { + return Err(AsyncError::Timeout); + } + let s = lock_waker_state(&waker); + if s.failed || s.err.is_some() { + return Err(AsyncError::SckErr); + } + Ok(()) +} + +fn get_shareable_content( + timeout_ns: u64, +) -> std::result::Result, AsyncError> { + let waker = new_waker(); + let waker_cb = waker.clone(); + let blk = RcBlock::new(move |content: *mut SCShareableContent, err: *mut NSError| { + let (err_opt, err_failed) = match retain_error(err) { + Ok(opt) => (opt, false), + Err(_) => (None, true), + }; + let (content_opt, content_failed) = match retain_content(content) { + Ok(opt) => (opt, false), + Err(_) => (None, true), + }; + complete_waker( + &waker_cb, + err_opt, + content_opt, + err_failed || content_failed, + ); + }); + unsafe { + SCShareableContent::getShareableContentExcludingDesktopWindows_onScreenWindowsOnly_completionHandler( + false, false, &blk, + ); + } + if !wait_deadline(&waker, timeout_ns) { + return Err(AsyncError::Timeout); + } + let mut s = lock_waker_state(&waker); + if s.failed || s.err.is_some() { + return Err(AsyncError::SckErr); + } + s.content.take().ok_or(AsyncError::SckErr) +} + +type LifecycleTsfn = + ThreadsafeFunction<(String, String), (), (String, String), Status, false, false, 8>; + +type AudioFrameTsfnArgs = (u32, u32, u32, i64, u32, Buffer); +type AudioFrameTsfnInput = (u32, u32, u32, i64, AudioSampleFormat, PooledMacAudioFrame); +type AudioFrameTsfn = + ThreadsafeFunction; + +type AudioDiagnosticTsfnArgs = (String, String, u32, u32); +type AudioDiagnosticTsfn = ThreadsafeFunction< + AudioDiagnosticTsfnArgs, + (), + AudioDiagnosticTsfnArgs, + Status, + false, + false, + 8, +>; + +type EncoderDiagnosticTsfnArgs = (String, String); +type EncoderDiagnosticTsfn = ThreadsafeFunction< + EncoderDiagnosticTsfnArgs, + (), + EncoderDiagnosticTsfnArgs, + Status, + false, + false, + 8, +>; + +struct AudioFramePayload { + sample_rate_hz: u32, + channels: u32, + num_samples_per_channel: u32, + pts_us: i64, + format: AudioSampleFormat, + slot: PooledMacAudioFrame, +} + +impl AudioFramePayload { + fn into_input(self) -> AudioFrameTsfnInput { + assert!(self.sample_rate_hz > 0); + assert!(self.channels > 0); + ( + self.sample_rate_hz, + self.channels, + self.num_samples_per_channel, + self.pts_us, + self.format, + self.slot, + ) + } +} + +struct CaptureState { + lifecycle_tsfn: Option, + audio_diagnostic_tsfn: Option, + encoder_diagnostic_tsfn: Option, + delegate: Option>, + stream: Option>, + sample_queue: Option>, +} + +unsafe impl Send for CaptureState {} +unsafe impl Sync for CaptureState {} + +pub(crate) struct CaptureInner { + state: Mutex, + audio_frame_tsfn: parking_lot::RwLock>, + running: AtomicBool, + tsfn_aborted: AtomicBool, + capture_id: Mutex>, + bus_sink: parking_lot::RwLock>>, + native_frame_sink: parking_lot::RwLock>>, + failure_surface: parking_lot::RwLock>>, + captures_audio: AtomicBool, + audio_sample_rate_hz: AtomicU32, + audio_channels: AtomicU32, + audio_pool: MacAudioFramePool, + encoder_attachment: parking_lot::RwLock>>, + encoder_attach_requested: AtomicBool, + encoder_ring_full_emitted: AtomicBool, + frame_sink_accepted: AtomicU64, + frame_sink_coalesced: AtomicU64, + frame_sink_rejected: AtomicU64, + media_frames_dropped_without_sink: AtomicU64, + frame_sink_backpressure_emitted: AtomicBool, + frame_sink_missing_emitted: AtomicBool, +} + +fn generic_error(reason: impl Into) -> napi::Error { + napi::Error::new(Status::GenericFailure, reason.into()) +} + +fn invalid_arg(reason: impl Into) -> napi::Error { + napi::Error::new(Status::InvalidArg, reason.into()) +} + +fn lock_state(inner: &CaptureInner) -> Result> { + inner + .state + .lock() + .map_err(|_| generic_error("ScreenCapture state lock poisoned")) +} + +fn note_tsfn_status(inner: &CaptureInner, status: Status) { + if status == Status::Closing { + inner.tsfn_aborted.store(true, Ordering::Release); + } +} + +const SCK_USER_STOPPED_CODE: isize = -3817; + +fn is_sck_user_stop(err: &NSError) -> bool { + let code = err.code(); + assert!( + code.abs() < i32::MAX as isize, + "SCK error code out of range" + ); + if code != SCK_USER_STOPPED_CODE { + return false; + } + let domain = err.domain().to_string(); + assert!(!domain.is_empty(), "SCK error domain must not be empty"); + domain.contains("SCStreamErrorDomain") +} + +fn sample_timestamp_us(sample_buffer: &CMSampleBuffer) -> i64 { + let pts = unsafe { sample_buffer.presentation_time_stamp() }; + if pts.timescale <= 0 { + return 0; + } + + let value = pts.value as i128; + let scale = pts.timescale as i128; + ((value * 1_000_000) / scale).clamp(0, i64::MAX as i128) as i64 +} + +pub struct DelegateIvars { + pub inner: Weak, + pub cleared: AtomicBool, +} + +define_class!( + #[unsafe(super(NSObject))] + #[name = "FluxerSCKScreenSource"] + #[ivars = DelegateIvars] + pub struct FluxerSCKScreenSource; + + unsafe impl NSObjectProtocol for FluxerSCKScreenSource {} + + unsafe impl SCStreamDelegate for FluxerSCKScreenSource { + #[unsafe(method(stream:didStopWithError:))] + unsafe fn did_stop_with_error(&self, _stream: &SCStream, err: &NSError) { + if self.ivars().cleared.load(Ordering::Acquire) { + return; + } + let Some(inner_arc) = self.ivars().inner.upgrade() else { + return; + }; + let inner: &CaptureInner = &inner_arc; + inner.running.store(false, Ordering::Release); + let msg = foundation::ns_error_localized_description(err); + let msg = if msg.is_empty() { + "stream stopped".to_string() + } else { + msg + }; + let is_clean_stop = is_sck_user_stop(err); + if !is_clean_stop { + if let Some(surface) = inner.failure_surface.read().as_ref().cloned() { + surface.on_failure(SckCaptureFailure::StreamStoppedWithError(msg.clone())); + } + } + let Ok(state) = inner.state.lock() else { + return; + }; + if is_clean_stop { + if let Some(tsfn) = state.lifecycle_tsfn.as_ref() { + let status = tsfn.call( + ("closed-clean".to_string(), msg), + ThreadsafeFunctionCallMode::NonBlocking, + ); + note_tsfn_status(inner, status); + } + return; + } + if let Some(tsfn) = state.lifecycle_tsfn.as_ref() { + let status = tsfn.call( + ("error".to_string(), msg), + ThreadsafeFunctionCallMode::NonBlocking, + ); + note_tsfn_status(inner, status); + } + if let Some(tsfn) = state.lifecycle_tsfn.as_ref() { + let status = tsfn.call( + ("closed".to_string(), String::new()), + ThreadsafeFunctionCallMode::NonBlocking, + ); + note_tsfn_status(inner, status); + } + } + } + + unsafe impl SCStreamOutput for FluxerSCKScreenSource { + #[unsafe(method(stream:didOutputSampleBuffer:ofType:))] + unsafe fn did_output_sample_buffer( + &self, + _stream: &SCStream, + sample_buffer: &CMSampleBuffer, + output_type: SCStreamOutputType, + ) { + if self.ivars().cleared.load(Ordering::Acquire) { + return; + } + let Some(inner_arc) = self.ivars().inner.upgrade() else { + return; + }; + if output_type == SCStreamOutputType::Screen { + unsafe { handle_screen_sample(&inner_arc, sample_buffer) }; + return; + } + if output_type == SCStreamOutputType::Audio { + unsafe { handle_audio_sample(&inner_arc, sample_buffer) }; + } + } + } +); + +impl FluxerSCKScreenSource { + pub fn new(inner: Weak) -> Retained { + let this = Self::alloc().set_ivars(DelegateIvars { + inner, + cleared: AtomicBool::new(false), + }); + unsafe { msg_send![super(this), init] } + } + + pub fn clear_inner(&self) { + self.ivars().cleared.store(true, Ordering::Release); + } +} + +unsafe fn handle_audio_sample(inner: &CaptureInner, sample_buffer: &CMSampleBuffer) { + if inner.tsfn_aborted.load(Ordering::Acquire) || !inner.running.load(Ordering::Acquire) { + return; + } + if !unsafe { sample_buffer.data_is_ready() } { + return; + } + let num_samples = unsafe { sample_buffer.num_samples() }; + if num_samples <= 0 { + return; + } + let pts_us = sample_timestamp_us(sample_buffer); + let (detected_format, detected_sample_rate_hz, detected_channels) = + unsafe { detect_audio_format(sample_buffer) }; + let sample_rate_hz = if detected_sample_rate_hz > 0 { + detected_sample_rate_hz + } else { + inner.audio_sample_rate_hz.load(Ordering::Acquire) + }; + let channels = if detected_channels > 0 { + detected_channels + } else { + inner.audio_channels.load(Ordering::Acquire) + }; + let num_samples_per_channel = num_samples as u32; + let mut slot = match inner.audio_pool.try_acquire() { + Some(s) => s, + None => { + emit_pool_exhausted(inner, sample_rate_hz, num_samples_per_channel); + return; + } + }; + if unsafe { extract_audio_bytes(sample_buffer, &mut slot) }.is_err() { + return; + } + let payload = AudioFramePayload { + sample_rate_hz, + channels, + num_samples_per_channel, + pts_us, + format: detected_format, + slot, + }; + deliver_audio_frame(inner, payload); +} + +fn emit_pool_exhausted(inner: &CaptureInner, sample_rate_hz: u32, num_samples: u32) { + let capture_id = inner + .capture_id + .lock() + .ok() + .and_then(|guard| guard.as_ref().cloned()) + .unwrap_or_default(); + let Ok(state) = inner.state.lock() else { + return; + }; + let Some(tsfn) = state.audio_diagnostic_tsfn.as_ref() else { + return; + }; + let status = tsfn.call( + ( + "audioPoolExhausted".to_string(), + capture_id, + sample_rate_hz, + num_samples, + ), + ThreadsafeFunctionCallMode::NonBlocking, + ); + note_tsfn_status(inner, status); +} + +pub(crate) unsafe fn detect_audio_format( + sample_buffer: &CMSampleBuffer, +) -> (AudioSampleFormat, u32, u32) { + let Some(format_desc) = (unsafe { sample_buffer.format_description() }) else { + return (AudioSampleFormat::Unknown, 0, 0); + }; + let asbd_ptr = unsafe { CMAudioFormatDescriptionGetStreamBasicDescription(&format_desc) }; + if asbd_ptr.is_null() { + return (AudioSampleFormat::Unknown, 0, 0); + } + let asbd: AudioStreamBasicDescription = unsafe { *asbd_ptr }; + if asbd.mFormatID != kAudioFormatLinearPCM { + return (AudioSampleFormat::Unknown, 0, 0); + } + let format = classify_pcm_flags(asbd.mFormatFlags, asbd.mBitsPerChannel); + let sample_rate_hz = asbd.mSampleRate.clamp(0.0, u32::MAX as f64) as u32; + let channels = asbd.mChannelsPerFrame; + (format, sample_rate_hz, channels) +} + +fn classify_pcm_flags(flags: u32, bits_per_channel: u32) -> AudioSampleFormat { + let is_float = (flags & kAudioFormatFlagIsFloat) != 0; + let is_signed_int = (flags & kAudioFormatFlagIsSignedInteger) != 0; + let is_non_interleaved = (flags & kAudioFormatFlagIsNonInterleaved) != 0; + if is_float && bits_per_channel == 32 && is_non_interleaved { + return AudioSampleFormat::F32Planar; + } + if is_float && bits_per_channel == 32 && !is_non_interleaved { + return AudioSampleFormat::F32Interleaved; + } + if is_signed_int && bits_per_channel == 16 && !is_non_interleaved { + return AudioSampleFormat::I16Interleaved; + } + AudioSampleFormat::Unknown +} + +pub(crate) const MAX_AUDIO_BUFFERS: usize = 8; + +#[repr(C, align(16))] +pub(crate) struct AblStorage { + pub n_buffers: u32, + pub _pad: u32, + pub buffers: [objc2_core_audio_types::AudioBuffer; MAX_AUDIO_BUFFERS], +} + +pub(crate) unsafe fn extract_audio_bytes( + sample_buffer: &CMSampleBuffer, + slot: &mut PooledMacAudioFrame, +) -> std::result::Result<(), ()> { + assert!(slot.capacity() <= MAX_FRAME_BYTES_PER_SLOT); + let mut storage = AblStorage { + n_buffers: 0, + _pad: 0, + buffers: [objc2_core_audio_types::AudioBuffer { + mNumberChannels: 0, + mDataByteSize: 0, + mData: ptr::null_mut(), + }; MAX_AUDIO_BUFFERS], + }; + let storage_size = core::mem::size_of::(); + let mut block_buffer_out: *mut CMBlockBuffer = ptr::null_mut(); + let status = unsafe { + sample_buffer.audio_buffer_list_with_retained_block_buffer( + ptr::null_mut(), + &mut storage as *mut AblStorage as *mut AudioBufferList, + storage_size, + None, + None, + 0, + &mut block_buffer_out, + ) + }; + if status != 0 { + unsafe { release_block_buffer(block_buffer_out) }; + return Err(()); + } + assert!(!block_buffer_out.is_null()); + let n_buffers = storage.n_buffers as usize; + if n_buffers == 0 || n_buffers > MAX_AUDIO_BUFFERS { + unsafe { release_block_buffer(block_buffer_out) }; + return Err(()); + } + for i in 0..n_buffers { + let ab = storage.buffers[i]; + let len = ab.mDataByteSize as usize; + if len == 0 || ab.mData.is_null() { + continue; + } + let bytes = unsafe { core::slice::from_raw_parts(ab.mData as *const u8, len) }; + if slot.append(bytes).is_err() { + unsafe { release_block_buffer(block_buffer_out) }; + return Err(()); + } + } + unsafe { release_block_buffer(block_buffer_out) }; + Ok(()) +} + +unsafe fn release_block_buffer(block_buffer: *mut CMBlockBuffer) { + if block_buffer.is_null() { + return; + } + unsafe { + CFRelease(block_buffer as *const core::ffi::c_void); + } +} + +#[link(name = "CoreFoundation", kind = "framework")] +unsafe extern "C" { + fn CFRelease(cf: *const core::ffi::c_void); +} + +fn build_audio_callback_args( + env: napi::Env, + input: AudioFrameTsfnInput, +) -> Result { + let (sample_rate_hz, channels, num_samples_per_channel, pts_us, format, slot) = input; + assert!(sample_rate_hz > 0); + assert!(channels > 0); + let (ptr, len, slot) = slot.into_external_parts(); + assert!(!ptr.is_null()); + if len == 0 { + drop(slot); + let empty: Vec = Vec::new(); + return Ok(( + sample_rate_hz, + channels, + num_samples_per_channel, + pts_us, + format.code(), + Buffer::from(empty), + )); + } + let slice = unsafe { + BufferSlice::from_external(&env, ptr, len, slot, |_env, slot| { + drop(slot); + }) + }?; + let buffer = slice.into_buffer(&env)?; + Ok(( + sample_rate_hz, + channels, + num_samples_per_channel, + pts_us, + format.code(), + buffer, + )) +} + +fn deliver_audio_frame(inner: &CaptureInner, payload: AudioFramePayload) { + let guard = inner.audio_frame_tsfn.read(); + let Some(tsfn) = guard.as_ref() else { + return; + }; + let status = tsfn.call( + payload.into_input(), + ThreadsafeFunctionCallMode::NonBlocking, + ); + note_tsfn_status(inner, status); +} + +unsafe fn handle_screen_sample(inner: &CaptureInner, sample_buffer: &CMSampleBuffer) { + if inner.tsfn_aborted.load(Ordering::Acquire) || !inner.running.load(Ordering::Acquire) { + return; + } + if !unsafe { sample_buffer.data_is_ready() } { + return; + } + let Some(image_buffer) = (unsafe { sample_buffer.image_buffer() }) else { + return; + }; + + let pixel_buffer: &CVPixelBuffer = &image_buffer; + + let format_type = CVPixelBufferGetPixelFormatType(pixel_buffer); + if format_type == PIXEL_FORMAT_420V || format_type == PIXEL_FORMAT_420F { + handle_nv12_screen_sample(inner, sample_buffer, pixel_buffer); + } else if format_type == kCVPixelFormatType_32BGRA { + handle_bgra_screen_sample(inner, sample_buffer, pixel_buffer); + } +} + +fn handle_nv12_screen_sample( + inner: &CaptureInner, + sample_buffer: &CMSampleBuffer, + pixel_buffer: &CVPixelBuffer, +) { + let width = CVPixelBufferGetWidth(pixel_buffer) as u32; + let height = CVPixelBufferGetHeight(pixel_buffer) as u32; + if width == 0 || height == 0 || height % 2 != 0 { + return; + } + + let plane_count = CVPixelBufferGetPlaneCount(pixel_buffer); + if plane_count < 2 { + return; + } + + submit_encoder_ring_frame(inner, pixel_buffer, sample_timestamp_us(sample_buffer)); + + if let Some(sink) = inner.native_frame_sink.read().as_ref().cloned() { + let outcome = + try_enqueue_native_cv_pixel_buffer(&sink, pixel_buffer, width, height, sample_buffer); + record_frame_sink_outcome(inner, outcome); + return; + } + + if let Some(sink) = bus_sink_for(inner).as_deref() { + let outcome = + try_enqueue_bus_cv_pixel_buffer(sink, pixel_buffer, width, height, sample_buffer); + record_frame_sink_outcome(inner, outcome); + return; + } + note_media_frame_without_sink(inner); +} + +fn handle_bgra_screen_sample( + inner: &CaptureInner, + sample_buffer: &CMSampleBuffer, + pixel_buffer: &CVPixelBuffer, +) { + let width = CVPixelBufferGetWidth(pixel_buffer) as u32; + let height = CVPixelBufferGetHeight(pixel_buffer) as u32; + if width == 0 || height == 0 { + return; + } + + submit_encoder_ring_frame(inner, pixel_buffer, sample_timestamp_us(sample_buffer)); + + if let Some(sink) = inner.native_frame_sink.read().as_ref().cloned() { + let outcome = + try_enqueue_native_cv_pixel_buffer(&sink, pixel_buffer, width, height, sample_buffer); + record_frame_sink_outcome(inner, outcome); + return; + } + + if let Some(sink) = bus_sink_for(inner).as_deref() { + let outcome = + try_enqueue_bus_cv_pixel_buffer(sink, pixel_buffer, width, height, sample_buffer); + record_frame_sink_outcome(inner, outcome); + return; + } + note_media_frame_without_sink(inner); +} + +fn submit_encoder_ring_frame( + inner: &CaptureInner, + pixel_buffer: &CVPixelBuffer, + capture_pts_us: i64, +) { + if !inner.encoder_attach_requested.load(Ordering::Acquire) { + return; + } + let attachment = match inner.encoder_attachment.read().as_ref().cloned() { + Some(a) => a, + None => return, + }; + if !attachment.is_attached() { + return; + } + let before = attachment.stats().ring_full_events; + if !try_submit_zero_copy(&attachment, pixel_buffer, capture_pts_us) { + let source_ptr = pixel_buffer as *const CVPixelBuffer as *mut c_void; + let _ = attachment.submit_with_blit(source_ptr, capture_pts_us); + } + let after = attachment.stats().ring_full_events; + if after > before && !inner.encoder_ring_full_emitted.swap(true, Ordering::AcqRel) { + emit_encoder_ring_full(inner); + } +} + +fn try_submit_zero_copy( + attachment: &EncoderAttachment, + pixel_buffer: &CVPixelBuffer, + capture_pts_us: i64, +) -> bool { + assert!(capture_pts_us >= 0, "capture pts clamped non-negative"); + let format_type = CVPixelBufferGetPixelFormatType(pixel_buffer); + if format_type != PIXEL_FORMAT_420V && format_type != PIXEL_FORMAT_420F { + return false; + } + let width = CVPixelBufferGetWidth(pixel_buffer) as u32; + let height = CVPixelBufferGetHeight(pixel_buffer) as u32; + if width != attachment.width() { + return false; + } + if height != attachment.height() { + return false; + } + assert!(width > 0, "gated width positive"); + assert!(height > 0, "gated height positive"); + let lock_flags = CVPixelBufferLockFlags(0); + let lock_status = unsafe { CVPixelBufferLockBaseAddress(pixel_buffer, lock_flags) }; + if lock_status != 0 { + return false; + } + let surface = CVPixelBufferGetIOSurface(Some(pixel_buffer)); + let _ = unsafe { CVPixelBufferUnlockBaseAddress(pixel_buffer, lock_flags) }; + let Some(surface) = surface else { + return false; + }; + let surface_raw = CFRetained::into_raw(surface).cast::(); + unsafe { attachment.submit_external_surface(surface_raw, capture_pts_us) }.is_ok() +} + +fn emit_encoder_ring_full(inner: &CaptureInner) { + let capture_id = inner + .capture_id + .lock() + .ok() + .and_then(|guard| guard.as_ref().cloned()) + .unwrap_or_default(); + let Ok(state) = inner.state.lock() else { + return; + }; + let Some(tsfn) = state.encoder_diagnostic_tsfn.as_ref() else { + return; + }; + let status = tsfn.call( + ("encoderRingFull".to_string(), capture_id), + ThreadsafeFunctionCallMode::NonBlocking, + ); + note_tsfn_status(inner, status); +} + +fn record_frame_sink_outcome(inner: &CaptureInner, outcome: EnqueueOutcome) { + match outcome { + EnqueueOutcome::Accepted => { + inner.frame_sink_accepted.fetch_add(1, Ordering::AcqRel); + } + EnqueueOutcome::Coalesced => { + inner.frame_sink_coalesced.fetch_add(1, Ordering::AcqRel); + emit_frame_sink_backpressure_once( + inner, + "macOS CVPixelBuffer frame coalesced by native frame sink", + ); + } + EnqueueOutcome::Rejected => { + inner.frame_sink_rejected.fetch_add(1, Ordering::AcqRel); + emit_frame_sink_backpressure_once( + inner, + "macOS CVPixelBuffer frame rejected by native frame sink", + ); + } + } +} + +fn emit_frame_sink_backpressure_once(inner: &CaptureInner, message: &'static str) { + if inner + .frame_sink_backpressure_emitted + .swap(true, Ordering::AcqRel) + { + return; + } + emit_lifecycle_diagnostic(inner, message); +} + +fn note_media_frame_without_sink(inner: &CaptureInner) { + inner + .media_frames_dropped_without_sink + .fetch_add(1, Ordering::AcqRel); + if inner + .frame_sink_missing_emitted + .swap(true, Ordering::AcqRel) + { + return; + } + emit_lifecycle_diagnostic( + inner, + "macOS screen frame dropped because no native frame sink is registered", + ); +} + +fn bus_sink_for(inner: &CaptureInner) -> Option> { + if let Some(sink) = inner.bus_sink.read().as_ref().cloned() { + return Some(sink); + } + let capture_id = inner + .capture_id + .lock() + .ok() + .and_then(|guard| guard.as_ref().cloned())?; + let sink = frame_bus::get_sink(&capture_id)?; + *inner.bus_sink.write() = Some(sink.clone()); + Some(sink) +} + +fn retain_native_frame_sink_handle( + value: Unknown<'_>, +) -> Result> { + if value.get_type()? != ValueType::External { + return Err(napi::Error::new( + Status::InvalidArg, + "ScreenCapture.setFrameSinkHandle expects a native external frame sink handle", + )); + } + + let raw_value = value.value(); + let mut data: *mut c_void = ptr::null_mut(); + let status = + unsafe { napi::sys::napi_get_value_external(raw_value.env, raw_value.value, &mut data) }; + if status != napi::sys::Status::napi_ok || data.is_null() { + return Err(napi::Error::new( + Status::InvalidArg, + "ScreenCapture.setFrameSinkHandle received an empty native external frame sink handle", + )); + } + + let handle = unsafe { + NativeScreenFrameSinkHandle::retain_from_raw(data.cast::()) + } + .ok_or_else(|| { + napi::Error::new( + Status::InvalidArg, + "ScreenCapture.setFrameSinkHandle received an invalid native frame sink handle", + ) + })?; + + Ok(Arc::new(handle)) +} + +fn try_enqueue_native_cv_pixel_buffer( + sink: &NativeScreenFrameSinkHandleRef, + pixel_buffer: &CVPixelBuffer, + width: u32, + height: u32, + sample_buffer: &CMSampleBuffer, +) -> EnqueueOutcome { + if sink.handle().enqueue_mac_cv_pixel_buffer.is_none() { + return EnqueueOutcome::Rejected; + } + let retained = + unsafe { CVPixelBufferRetain(pixel_buffer as *const CVPixelBuffer as *mut c_void) }; + if retained.is_null() { + return EnqueueOutcome::Rejected; + } + let pixel_format = CVPixelBufferGetPixelFormatType(pixel_buffer); + unsafe { + sink.enqueue_mac_cv_pixel_buffer( + retained, + width, + height, + pixel_format, + sample_timestamp_us(sample_buffer), + ) + } +} + +fn try_enqueue_bus_cv_pixel_buffer( + sink: &dyn frame_bus::ScreenFrameSink, + pixel_buffer: &CVPixelBuffer, + width: u32, + height: u32, + sample_buffer: &CMSampleBuffer, +) -> EnqueueOutcome { + let retained = + unsafe { CVPixelBufferRetain(pixel_buffer as *const CVPixelBuffer as *mut c_void) }; + if retained.is_null() { + return EnqueueOutcome::Rejected; + } + let pixel_format = CVPixelBufferGetPixelFormatType(pixel_buffer); + let bus_frame = unsafe { + MacCvPixelBufferFrame::from_retained( + retained, + width, + height, + pixel_format, + sample_timestamp_us(sample_buffer), + ) + }; + sink.enqueue(BusScreenFrame::MacCvPixelBuffer(bus_frame)) +} + +#[napi(js_name = "ScreenCapture")] +pub struct ScreenCapture { + inner: Arc, +} + +#[napi(object, js_name = "ScreenCaptureStartResult")] +pub struct ScreenCaptureStartResult { + pub width: u32, + pub height: u32, + pub frame_rate: u32, + pub pixel_format: String, +} + +#[napi(object, js_name = "FrameSinkDiagnostics")] +pub struct FrameSinkDiagnostics { + pub accepted: f64, + pub coalesced: f64, + pub rejected: f64, + #[napi(js_name = "mediaFramesDroppedWithoutSink")] + pub media_frames_dropped_without_sink: f64, +} + +#[napi(object, js_name = "ScreenCaptureRect")] +pub struct ScreenCaptureRect { + pub x: f64, + pub y: f64, + pub width: f64, + pub height: f64, +} + +#[napi(object, js_name = "ScreenCaptureStartOptions")] +pub struct ScreenCaptureStartOptions { + #[napi(js_name = "showCursorClicks")] + pub show_cursor_clicks: Option, + #[napi(js_name = "captureRect")] + pub capture_rect: Option, + #[napi(js_name = "colorRange")] + pub color_range: Option, + #[napi(js_name = "colorSpace")] + pub color_space: Option, + #[napi(js_name = "maxOutputWidth")] + pub max_output_width: Option, + #[napi(js_name = "maxOutputHeight")] + pub max_output_height: Option, +} + +#[derive(Clone, Copy, Debug)] +struct NormalizedStartOptions { + shows_cursor: bool, + source_rect: Option, + pixel_format: SckPixelFormat, + color_space: SckColorSpace, + max_output_width: u32, + max_output_height: u32, +} + +#[napi] +impl ScreenCapture { + #[napi(constructor)] + pub fn new() -> Self { + let audio_pool = MacAudioFramePool::new(MAC_AUDIO_POOL_CAP, MAX_FRAME_BYTES_PER_SLOT) + .unwrap_or_else(|_| { + unreachable!("MAC_AUDIO_POOL_CAP and MAX_FRAME_BYTES_PER_SLOT must validate") + }); + Self { + inner: Arc::new(CaptureInner { + state: Mutex::new(CaptureState { + lifecycle_tsfn: None, + audio_diagnostic_tsfn: None, + encoder_diagnostic_tsfn: None, + delegate: None, + stream: None, + sample_queue: None, + }), + audio_frame_tsfn: parking_lot::RwLock::new(None), + running: AtomicBool::new(false), + tsfn_aborted: AtomicBool::new(false), + capture_id: Mutex::new(None), + bus_sink: parking_lot::RwLock::new(None), + native_frame_sink: parking_lot::RwLock::new(None), + failure_surface: parking_lot::RwLock::new(None), + captures_audio: AtomicBool::new(false), + audio_sample_rate_hz: AtomicU32::new(AUDIO_SAMPLE_RATE_DEFAULT_HZ), + audio_channels: AtomicU32::new(AUDIO_CHANNEL_COUNT_DEFAULT), + audio_pool, + encoder_attachment: parking_lot::RwLock::new(None), + encoder_attach_requested: AtomicBool::new(false), + encoder_ring_full_emitted: AtomicBool::new(false), + frame_sink_accepted: AtomicU64::new(0), + frame_sink_coalesced: AtomicU64::new(0), + frame_sink_rejected: AtomicU64::new(0), + media_frames_dropped_without_sink: AtomicU64::new(0), + frame_sink_backpressure_emitted: AtomicBool::new(false), + frame_sink_missing_emitted: AtomicBool::new(false), + }), + } + } + + #[napi] + pub fn set_lifecycle_callback(&self, callback: Function<(String, String), ()>) -> Result<()> { + let tsfn: LifecycleTsfn = callback + .build_threadsafe_function::<(String, String)>() + .max_queue_size::<8>() + .build_callback(|ctx| Ok(ctx.value))?; + let mut state = lock_state(&self.inner)?; + state.lifecycle_tsfn = Some(tsfn); + self.inner.tsfn_aborted.store(false, Ordering::Release); + Ok(()) + } + + #[napi(js_name = "setFrameSinkHandle")] + pub fn set_frame_sink_handle(&self, frame_sink_handle: Unknown<'_>) -> Result<()> { + let sink = retain_native_frame_sink_handle(frame_sink_handle)?; + *self.inner.native_frame_sink.write() = Some(sink); + Ok(()) + } + + #[napi(js_name = "setAudioFrameCallback")] + pub fn set_audio_frame_callback( + &self, + callback: Function, + ) -> Result<()> { + let tsfn: AudioFrameTsfn = callback + .build_threadsafe_function::() + .max_queue_size::<16>() + .build_callback(|ctx| build_audio_callback_args(ctx.env, ctx.value))?; + *self.inner.audio_frame_tsfn.write() = Some(tsfn); + Ok(()) + } + + #[napi(js_name = "setAudioDiagnosticCallback")] + pub fn set_audio_diagnostic_callback( + &self, + callback: Function, + ) -> Result<()> { + let tsfn: AudioDiagnosticTsfn = callback + .build_threadsafe_function::() + .max_queue_size::<8>() + .build_callback(|ctx| Ok(ctx.value))?; + let mut state = lock_state(&self.inner)?; + state.audio_diagnostic_tsfn = Some(tsfn); + Ok(()) + } + + #[napi(js_name = "setEncoderDiagnosticCallback")] + pub fn set_encoder_diagnostic_callback( + &self, + callback: Function, + ) -> Result<()> { + let tsfn: EncoderDiagnosticTsfn = callback + .build_threadsafe_function::() + .max_queue_size::<8>() + .build_callback(|ctx| Ok(ctx.value))?; + let mut state = lock_state(&self.inner)?; + state.encoder_diagnostic_tsfn = Some(tsfn); + Ok(()) + } + + #[napi(js_name = "attachEncoder")] + pub fn attach_encoder(&self, width: u32, height: u32, frame_rate: Option) -> Result<()> { + if width == 0 || height == 0 { + return Err(napi::Error::new( + Status::InvalidArg, + "ScreenCapture.attachEncoder requires positive dimensions", + )); + } + let frame_rate = EncoderFrameRate::from_fps(frame_rate.unwrap_or(30)); + let attachment = EncoderAttachment::try_new_with_frame_rate(width, height, frame_rate) + .map_err(|e| { + napi::Error::new(Status::GenericFailure, format!("attachEncoder failed: {e}")) + })?; + *self.inner.encoder_attachment.write() = Some(attachment); + self.inner + .encoder_attach_requested + .store(true, Ordering::Release); + self.inner + .encoder_ring_full_emitted + .store(false, Ordering::Release); + Ok(()) + } + + #[napi(js_name = "detachEncoder")] + pub fn detach_encoder(&self) -> Result<()> { + if let Some(attach) = self.inner.encoder_attachment.write().take() { + attach.detach(); + } + self.inner + .encoder_attach_requested + .store(false, Ordering::Release); + Ok(()) + } + + #[napi(js_name = "isEncoderAttached")] + pub fn is_encoder_attached(&self) -> bool { + self.inner + .encoder_attachment + .read() + .as_ref() + .map(|a| a.is_attached()) + .unwrap_or(false) + } + + #[napi(js_name = "encoderRingFullCount")] + pub fn encoder_ring_full_count(&self) -> u32 { + self.inner + .encoder_attachment + .read() + .as_ref() + .map(|a| a.stats().ring_full_events.min(u32::MAX as u64) as u32) + .unwrap_or(0) + } + + #[napi(js_name = "getFrameSinkDiagnostics")] + pub fn get_frame_sink_diagnostics(&self) -> FrameSinkDiagnostics { + FrameSinkDiagnostics { + accepted: self.inner.frame_sink_accepted.load(Ordering::Acquire) as f64, + coalesced: self.inner.frame_sink_coalesced.load(Ordering::Acquire) as f64, + rejected: self.inner.frame_sink_rejected.load(Ordering::Acquire) as f64, + media_frames_dropped_without_sink: self + .inner + .media_frames_dropped_without_sink + .load(Ordering::Acquire) as f64, + } + } + + #[napi(js_name = "enableAudioCapture")] + pub fn enable_audio_capture( + &self, + captures_audio: bool, + sample_rate_hz: u32, + channels: u32, + ) -> Result<()> { + if !(8_000..=192_000).contains(&sample_rate_hz) { + return Err(napi::Error::new( + Status::InvalidArg, + "ScreenCapture.enableAudioCapture sampleRateHz out of [8000..=192000]", + )); + } + if !(1..=8).contains(&channels) { + return Err(napi::Error::new( + Status::InvalidArg, + "ScreenCapture.enableAudioCapture channels out of [1..=8]", + )); + } + self.inner + .captures_audio + .store(captures_audio, Ordering::Release); + self.inner + .audio_sample_rate_hz + .store(sample_rate_hz, Ordering::Release); + self.inner.audio_channels.store(channels, Ordering::Release); + Ok(()) + } + + pub fn install_failure_surface(&self, surface: Arc) { + *self.inner.failure_surface.write() = Some(surface); + } + + pub fn clear_failure_surface(&self) { + self.inner.failure_surface.write().take(); + } + + #[napi] + pub async fn start( + &self, + source_id: String, + source_kind: String, + width: u32, + height: u32, + frame_rate: u32, + capture_id: Option, + capture_options: Option, + ) -> Result { + let start_options = normalize_start_options(capture_options)?; + let normalized_capture_id = capture_id + .map(|raw| raw.trim().to_string()) + .filter(|trimmed| !trimmed.is_empty()); + *self + .inner + .capture_id + .lock() + .map_err(|_| generic_error("ScreenCapture capture_id lock poisoned"))? = + normalized_capture_id.clone(); + *self.inner.bus_sink.write() = if self.inner.native_frame_sink.read().is_some() { + None + } else { + normalized_capture_id + .as_deref() + .and_then(frame_bus::get_sink) + }; + let source_id_num: u32 = source_id.parse().map_err(|_| { + napi::Error::new( + Status::InvalidArg, + "ScreenCapture.start sourceId must be a u32 string", + ) + })?; + if source_id_num == 0 { + return Err(napi::Error::new( + Status::InvalidArg, + "ScreenCapture.start sourceId must be > 0", + )); + } + let source_id = source_id_num; + { + let detected = os_version::current_macos_version(); + let SupportClassification { + supported, reason, .. + } = classify_support(detected); + if !supported { + return Err(napi::Error::new(Status::GenericFailure, reason)); + } + } + { + let state = lock_state(&self.inner)?; + if state.stream.is_some() + || state.delegate.is_some() + || self.inner.running.load(Ordering::Acquire) + { + return Err(napi::Error::new( + Status::GenericFailure, + "ScreenCapture is already running", + )); + } + } + let content = get_shareable_content(DEFAULT_TIMEOUT_NS) + .map_err(|_| generic_error("Failed to fetch SCShareableContent"))?; + + let (filter, dims) = + build_filter_for_source(&content, source_id, &source_kind, width, height)?; + + let point_pixel_scale = sck::filter_point_pixel_scale_if_available(&filter); + let content_rect = sck::filter_content_rect_if_available(&filter); + let (source_width, source_height) = resolve_source_pixels( + width, + height, + start_options.source_rect, + content_rect, + point_pixel_scale, + dims, + ); + let (final_width, final_height) = cap_output_dims( + source_width, + source_height, + start_options.max_output_width, + start_options.max_output_height, + ); + assert!(final_width >= crate::config::OUTPUT_DIMENSION_MIN); + assert!(final_height >= crate::config::OUTPUT_DIMENSION_MIN); + let effective_fps = if frame_rate == 0 { + 30 + } else { + frame_rate.min(240) + }; + + let cfg: Retained = unsafe { SCStreamConfiguration::new() }; + sck::cfg_set_width(&cfg, final_width as usize); + sck::cfg_set_height(&cfg, final_height as usize); + sck::cfg_set_scales_to_fit_if_available(&cfg, true); + sck::cfg_set_shows_cursor(&cfg, start_options.shows_cursor); + if let Some(source_rect) = start_options.source_rect { + if !sck::cfg_set_source_rect_if_available(&cfg, source_rect) { + emit_lifecycle_diagnostic( + &self.inner, + "captureRect requested but this ScreenCaptureKit runtime lacks setSourceRect", + ); + } + } + + let captures_audio = self.inner.captures_audio.load(Ordering::Acquire); + let audio_sample_rate_hz = self.inner.audio_sample_rate_hz.load(Ordering::Acquire); + let audio_channels = self.inner.audio_channels.load(Ordering::Acquire); + let capture_cfg = build_capture_config( + effective_fps, + captures_audio, + audio_sample_rate_hz, + audio_channels, + start_options.pixel_format, + start_options.color_space, + ); + sck::apply_capture_config(&cfg, &capture_cfg); + + let stream_name = format!("Fluxer ScreenCapture ({}:{})", source_kind, source_id); + let nsname = NSString::from_str(&stream_name); + sck::cfg_set_stream_name_if_available(&cfg, &nsname); + + let delegate = FluxerSCKScreenSource::new(Arc::downgrade(&self.inner)); + + let stream_alloc = SCStream::alloc(); + let stream: Retained = unsafe { + SCStream::initWithFilter_configuration_delegate( + stream_alloc, + &filter, + &cfg, + Some(ProtocolObject::from_ref(&*delegate)), + ) + }; + + let sample_queue = build_scstream_sample_queue(&source_kind, source_id); + + if sck::sc_stream_add_stream_output( + &stream, + ProtocolObject::from_ref(&*delegate), + SCStreamOutputType::Screen, + Some(&sample_queue), + ) + .is_err() + { + delegate.clear_inner(); + return Err(generic_error("Failed to add screen stream output")); + } + + if captures_audio + && sck::sc_stream_add_stream_output( + &stream, + ProtocolObject::from_ref(&*delegate), + SCStreamOutputType::Audio, + Some(&sample_queue), + ) + .is_err() + { + delegate.clear_inner(); + return Err(generic_error("Failed to add audio stream output")); + } + + { + let mut state = lock_state(&self.inner)?; + state.delegate = Some(delegate.clone()); + state.stream = Some(stream.clone()); + state.sample_queue = Some(sample_queue); + } + self.inner.running.store(true, Ordering::Release); + + if await_start(&stream, DEFAULT_TIMEOUT_NS).is_err() { + self.inner.running.store(false, Ordering::Release); + if let Some(surface) = self.inner.failure_surface.read().as_ref().cloned() { + surface.on_failure(SckCaptureFailure::StreamStartFailed( + "SCStream startCapture failed".to_string(), + )); + } + let mut state = lock_state(&self.inner)?; + if let Some(d) = state.delegate.take() { + d.clear_inner(); + } + state.stream = None; + return Err(generic_error("SCStream startCapture failed")); + } + + Ok(ScreenCaptureStartResult { + width: final_width, + height: final_height, + frame_rate: effective_fps, + pixel_format: "nv12".to_string(), + }) + } + + #[napi] + pub async fn stop(&self) -> Result<()> { + self.inner.running.store(false, Ordering::Release); + if let Ok(mut guard) = self.inner.capture_id.lock() { + guard.take(); + } + self.inner.bus_sink.write().take(); + self.inner.native_frame_sink.write().take(); + self.inner.failure_surface.write().take(); + if let Some(attach) = self.inner.encoder_attachment.write().take() { + attach.detach(); + } + self.inner + .encoder_attach_requested + .store(false, Ordering::Release); + let (delegate, stream, queue) = { + let mut state = lock_state(&self.inner)?; + let delegate = state.delegate.take(); + let stream = state.stream.take(); + let queue = state.sample_queue.take(); + (delegate, stream, queue) + }; + if let Some(d) = delegate.as_ref() { + d.clear_inner(); + } + if let Some(s) = stream.as_ref() { + let _ = await_stop(s, DEFAULT_TIMEOUT_NS); + } + drop(stream); + drop(delegate); + drop(queue); + Ok(()) + } +} + +impl Drop for ScreenCapture { + fn drop(&mut self) { + self.inner.running.store(false, Ordering::Release); + self.inner.bus_sink.write().take(); + self.inner.native_frame_sink.write().take(); + self.inner.failure_surface.write().take(); + if let Some(attach) = self.inner.encoder_attachment.write().take() { + attach.detach(); + } + self.inner + .encoder_attach_requested + .store(false, Ordering::Release); + let (delegate, stream, _queue) = match self.inner.state.lock() { + Ok(mut s) => (s.delegate.take(), s.stream.take(), s.sample_queue.take()), + Err(_) => (None, None, None), + }; + if let Some(d) = delegate.as_ref() { + d.clear_inner(); + } + if let Some(s) = stream.as_ref() { + let _ = await_stop(s, DEFAULT_TIMEOUT_NS); + } + } +} + +fn build_scstream_sample_queue( + source_kind: &str, + source_id: u32, +) -> dispatch2::DispatchRetained { + assert!(!source_kind.is_empty(), "source kind non-empty"); + assert!(source_id > 0, "source id positive"); + let label = format!("com.fluxer.scstream.{}-{}", source_kind, source_id); + let queue_attr = dispatch2::DispatchQueueAttr::with_qos_class( + dispatch2::DispatchQueueAttr::SERIAL, + dispatch2::DispatchQoS::UserInteractive, + 0, + ); + dispatch2::DispatchQueue::new(label.as_str(), Some(&queue_attr)) +} + +fn build_capture_config( + effective_fps: u32, + captures_audio: bool, + audio_sample_rate_hz: u32, + audio_channels: u32, + pixel_format: SckPixelFormat, + color_space: SckColorSpace, +) -> SckCaptureConfig { + assert!(effective_fps >= crate::config::FPS_MIN); + assert!(audio_channels >= 1); + let clamped_fps = effective_fps.clamp(crate::config::FPS_MIN, crate::config::FPS_MAX); + SckCaptureConfig::builder() + .target_fps(clamped_fps) + .queue_depth(crate::config::QUEUE_DEPTH_DEFAULT) + .pixel_format(pixel_format) + .color_space(color_space) + .captures_audio(captures_audio) + .audio_sample_rate_hz(audio_sample_rate_hz) + .audio_channels(audio_channels) + .build() + .unwrap_or_default() +} + +fn normalize_start_options( + options: Option, +) -> Result { + let Some(options) = options else { + return Ok(NormalizedStartOptions::default()); + }; + let source_rect = normalize_capture_rect(options.capture_rect)?; + let pixel_format = normalize_color_range(options.color_range.as_deref())?; + let color_space = normalize_color_space(options.color_space.as_deref())?; + let max_output_width = normalize_max_output_dimension( + options.max_output_width, + crate::config::MAX_OUTPUT_WIDTH_DEFAULT, + ); + let max_output_height = normalize_max_output_dimension( + options.max_output_height, + crate::config::MAX_OUTPUT_HEIGHT_DEFAULT, + ); + Ok(NormalizedStartOptions { + shows_cursor: options.show_cursor_clicks.unwrap_or(false), + source_rect, + pixel_format, + color_space, + max_output_width, + max_output_height, + }) +} + +fn normalize_max_output_dimension(requested: Option, fallback: u32) -> u32 { + assert!(fallback >= crate::config::OUTPUT_DIMENSION_MIN); + let resolved = requested + .filter(|v| *v >= crate::config::OUTPUT_DIMENSION_MIN) + .unwrap_or(fallback); + let even = resolved & !1; + assert!(even >= crate::config::OUTPUT_DIMENSION_MIN); + even +} + +fn normalize_capture_rect(rect: Option) -> Result> { + let Some(rect) = rect else { + return Ok(None); + }; + if !rect.x.is_finite() || !rect.y.is_finite() { + return Err(invalid_arg( + "ScreenCapture.start captureRect origin must be finite", + )); + } + if !rect.width.is_finite() || !rect.height.is_finite() { + return Err(invalid_arg( + "ScreenCapture.start captureRect size must be finite", + )); + } + if rect.x < 0.0 || rect.y < 0.0 || rect.width <= 0.0 || rect.height <= 0.0 { + return Err(invalid_arg( + "ScreenCapture.start captureRect requires non-negative x/y and positive width/height", + )); + } + if rect.width > u32::MAX as f64 || rect.height > u32::MAX as f64 { + return Err(invalid_arg( + "ScreenCapture.start captureRect width/height exceed u32", + )); + } + Ok(Some(CGRect::new( + CGPoint::new(rect.x, rect.y), + CGSize::new(rect.width, rect.height), + ))) +} + +fn normalize_color_range(value: Option<&str>) -> Result { + match value { + None | Some("limited") => Ok(SckPixelFormat::Nv12VideoRange), + Some("full") => Ok(SckPixelFormat::Nv12FullRange), + Some(_) => Err(invalid_arg( + "ScreenCapture.start colorRange must be 'limited' or 'full'", + )), + } +} + +fn normalize_color_space(value: Option<&str>) -> Result { + match value { + None | Some("srgb") | Some("rec709") => Ok(SckColorSpace::SrgbBt709), + Some(_) => Err(invalid_arg( + "ScreenCapture.start colorSpace must be 'srgb' or 'rec709'", + )), + } +} + +impl Default for NormalizedStartOptions { + fn default() -> Self { + Self { + shows_cursor: false, + source_rect: None, + pixel_format: SckPixelFormat::Nv12VideoRange, + color_space: SckColorSpace::SrgbBt709, + max_output_width: crate::config::MAX_OUTPUT_WIDTH_DEFAULT, + max_output_height: crate::config::MAX_OUTPUT_HEIGHT_DEFAULT, + } + } +} + +fn emit_lifecycle_diagnostic(inner: &CaptureInner, message: &'static str) { + let Ok(state) = inner.state.lock() else { + return; + }; + let Some(tsfn) = state.lifecycle_tsfn.as_ref() else { + return; + }; + let status = tsfn.call( + ("diagnostic".to_string(), message.to_string()), + ThreadsafeFunctionCallMode::NonBlocking, + ); + note_tsfn_status(inner, status); +} + +fn even_floor(value: u32) -> u32 { + value & !1 +} + +fn resolve_source_pixels( + requested_width: u32, + requested_height: u32, + source_rect: Option, + content_rect: Option, + point_pixel_scale: Option, + point_dims_fallback: (u32, u32), +) -> (u32, u32) { + let scale = point_pixel_scale + .filter(|s| s.is_finite() && *s > 0.0) + .map(|s| s as f64) + .unwrap_or(1.0); + assert!(scale > 0.0); + let min = crate::config::OUTPUT_DIMENSION_MIN; + if let Some(rect) = source_rect { + assert!(rect.size.width > 0.0); + assert!(rect.size.height > 0.0); + let w = (rect.size.width * scale).round() as u32; + let h = (rect.size.height * scale).round() as u32; + return (even_floor(w.max(min)), even_floor(h.max(min))); + } + if let Some(rect) = content_rect { + assert!(rect.size.width > 0.0); + assert!(rect.size.height > 0.0); + let w = (rect.size.width * scale).round() as u32; + let h = (rect.size.height * scale).round() as u32; + return (even_floor(w.max(min)), even_floor(h.max(min))); + } + if requested_width > 0 && requested_height > 0 { + return ( + even_floor(requested_width.max(min)), + even_floor(requested_height.max(min)), + ); + } + ( + even_floor(point_dims_fallback.0.max(min)), + even_floor(point_dims_fallback.1.max(min)), + ) +} + +fn cap_output_dims(src_w: u32, src_h: u32, max_w: u32, max_h: u32) -> (u32, u32) { + let min = crate::config::OUTPUT_DIMENSION_MIN; + assert!(src_w >= min); + assert!(src_h >= min); + let max_w = even_floor(max_w).max(min); + let max_h = even_floor(max_h).max(min); + let scale = (max_w as f64 / src_w as f64) + .min(max_h as f64 / src_h as f64) + .min(1.0); + assert!(scale > 0.0); + assert!(scale <= 1.0); + let out_w = even_floor(((src_w as f64) * scale).round() as u32).clamp(min, max_w); + let out_h = even_floor(((src_h as f64) * scale).round() as u32).clamp(min, max_h); + assert!(out_w <= max_w); + assert!(out_h <= max_h); + (out_w, out_h) +} + +fn build_filter_for_source( + content: &SCShareableContent, + source_id: u32, + source_kind: &str, + requested_width: u32, + requested_height: u32, +) -> Result<(Retained, (u32, u32))> { + match source_kind { + "screen" => { + let displays: Retained> = unsafe { content.displays() }; + let count = displays.count(); + let mut chosen: Option> = None; + for i in 0..count { + let d = displays.objectAtIndex(i); + if sck::sc_display_display_id(&d) == source_id { + chosen = Some(d); + break; + } + } + let display = + chosen.ok_or_else(|| generic_error("No SCDisplay matches requested sourceId"))?; + let dims = ( + if requested_width > 0 { + requested_width + } else { + sck::sc_display_width(&display).max(0) as u32 + }, + if requested_height > 0 { + requested_height + } else { + sck::sc_display_height(&display).max(0) as u32 + }, + ); + + let empty: Retained> = NSArray::new(); + let filter_alloc = SCContentFilter::alloc(); + let filter: Retained = unsafe { + SCContentFilter::initWithDisplay_excludingWindows(filter_alloc, &display, &empty) + }; + Ok((filter, dims)) + } + "window" => { + let windows: Retained> = unsafe { content.windows() }; + let count = windows.count(); + let mut chosen: Option> = None; + for i in 0..count { + let w = windows.objectAtIndex(i); + if sck::sc_window_window_id(&w) == source_id { + chosen = Some(w); + break; + } + } + let window = + chosen.ok_or_else(|| generic_error("No SCWindow matches requested sourceId"))?; + let frame = sck::sc_window_frame(&window); + let dims = ( + if requested_width > 0 { + requested_width + } else { + frame.size.width.max(0.0) as u32 + }, + if requested_height > 0 { + requested_height + } else { + frame.size.height.max(0.0) as u32 + }, + ); + let filter_alloc = SCContentFilter::alloc(); + let filter: Retained = + unsafe { SCContentFilter::initWithDesktopIndependentWindow(filter_alloc, &window) }; + Ok((filter, dims)) + } + other => Err(napi::Error::new( + Status::InvalidArg, + format!("Unknown sourceKind: {other}"), + )), + } +} + +#[napi(object, js_name = "MacScreenCaptureSource")] +pub struct MacScreenCaptureSource { + pub kind: String, + pub id: String, + pub name: String, + pub width: u32, + pub height: u32, + pub app_name: Option, + pub bundle_id: Option, + + pub target_pid: Option, +} + +#[napi(js_name = "listSources")] +pub async fn list_sources() -> Result> { + let content = match get_shareable_content(DEFAULT_TIMEOUT_NS) { + Ok(c) => c, + Err(_) => return Ok(Vec::new()), + }; + let mut out: Vec = Vec::new(); + let displays: Retained> = unsafe { content.displays() }; + let dcount = displays.count(); + for i in 0..dcount { + let d = displays.objectAtIndex(i); + let id = sck::sc_display_display_id(&d); + let name = format!("Display {}", i + 1); + out.push(MacScreenCaptureSource { + kind: "screen".to_string(), + id: id.to_string(), + name, + width: sck::sc_display_width(&d).max(0) as u32, + height: sck::sc_display_height(&d).max(0) as u32, + app_name: None, + bundle_id: None, + target_pid: None, + }); + } + let windows: Retained> = unsafe { content.windows() }; + let wcount = windows.count(); + for i in 0..wcount { + let w = windows.objectAtIndex(i); + if !sck::sc_window_is_on_screen(&w) { + continue; + } + let id = sck::sc_window_window_id(&w); + let title = sck::sc_window_title(&w) + .map(|s| s.to_string()) + .unwrap_or_default(); + let frame = sck::sc_window_frame(&w); + let (app_name, bundle_id, target_pid) = match sck::sc_window_owning_application(&w) { + Some(app) => ( + Some(sck::sc_running_application_name(&app).to_string()), + Some(sck::sc_running_application_bundle_identifier(&app).to_string()), + Some(sck::sc_running_application_process_id(&app)).filter(|pid| *pid > 0), + ), + None => (None, None, None), + }; + let display_name = if title.is_empty() { + app_name.clone().unwrap_or_else(|| format!("Window {id}")) + } else if let Some(ref app) = app_name { + format!("{app} — {title}") + } else { + title + }; + out.push(MacScreenCaptureSource { + kind: "window".to_string(), + id: id.to_string(), + name: display_name, + width: frame.size.width.max(0.0) as u32, + height: frame.size.height.max(0.0) as u32, + app_name, + bundle_id, + target_pid, + }); + } + Ok(out) +} + +#[napi(object, js_name = "MacScreenCaptureBackendSckAvailability")] +pub struct SckAvailability { + pub supported: bool, + pub macos_version: Option, +} + +#[napi(object, js_name = "MacScreenCaptureBackendAvailability")] +pub struct BackendAvailability { + pub sck: SckAvailability, + pub screen_permission: String, +} + +#[napi(js_name = "getBackendAvailability")] +pub async fn get_backend_availability() -> Result { + use objc2::runtime::AnyClass; + let sck_supported = AnyClass::get(c"SCStream").is_some(); + let version_str = Some(foundation::operating_system_version_string()); + let screen_permission = if unsafe { CGPreflightScreenCaptureAccess() } { + "granted" + } else { + "denied" + }; + Ok(BackendAvailability { + sck: SckAvailability { + supported: sck_supported, + macos_version: version_str, + }, + screen_permission: screen_permission.to_string(), + }) +} + +#[napi(object, js_name = "MacScreenCaptureBackendInfo")] +pub struct MacScreenCaptureBackendInfo { + pub backend: String, + pub supported: bool, + pub reason: String, + #[napi(js_name = "minMacosVersion")] + pub min_macos_version: String, + #[napi(js_name = "detectedMacosVersion")] + pub detected_macos_version: Option, + #[napi(js_name = "sckAvailable")] + pub sck_available: bool, +} + +#[napi(js_name = "getBackendInfo")] +pub fn get_backend_info() -> MacScreenCaptureBackendInfo { + let detected = os_version::current_macos_version(); + let SupportClassification { + supported, + sck_available, + reason, + } = classify_support(detected); + MacScreenCaptureBackendInfo { + backend: "mac-screen-capture".to_owned(), + supported, + reason, + min_macos_version: format_version(SCK_MIN_MACOS), + detected_macos_version: detected.map(format_version), + sck_available, + } +} + +#[cfg(test)] +mod dispatch_queue_tests { + use std::sync::Arc; + use std::sync::atomic::{AtomicU64, Ordering}; + use std::time::Duration; + + use super::build_scstream_sample_queue; + + #[test] + fn sample_queue_construction_does_not_trap_libdispatch() { + let _queue = build_scstream_sample_queue("screen", 1); + } + + #[test] + fn sample_queue_dispatches_blocks_without_crashing() { + let queue = build_scstream_sample_queue("screen", 2); + let counter = Arc::new(AtomicU64::new(0)); + for _ in 0..32 { + let c = counter.clone(); + queue.exec_async(move || { + c.fetch_add(1, Ordering::Release); + }); + } + let deadline = std::time::Instant::now() + Duration::from_secs(2); + while counter.load(Ordering::Acquire) < 32 { + if std::time::Instant::now() > deadline { + panic!( + "sample queue did not drain blocks: {}/{}", + counter.load(Ordering::Acquire), + 32 + ); + } + std::thread::sleep(Duration::from_millis(10)); + } + } + + #[test] + fn audio_frame_payload_into_input_carries_slot_and_metadata() { + use crate::audio_pool::MacAudioFramePool; + let pool = MacAudioFramePool::new(2, 64).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + let bytes = vec![0xAB_u8; 8]; + slot.write(&bytes).expect("write"); + let payload = super::AudioFramePayload { + sample_rate_hz: 48_000, + channels: 2, + num_samples_per_channel: 1024, + pts_us: 7_777, + format: super::AudioSampleFormat::F32Planar, + slot, + }; + let (sr, ch, ns, pts, fmt, slot) = payload.into_input(); + assert_eq!(sr, 48_000); + assert_eq!(ch, 2); + assert_eq!(ns, 1024); + assert_eq!(pts, 7_777); + assert_eq!(fmt, super::AudioSampleFormat::F32Planar); + assert_eq!(fmt.code(), 0); + assert_eq!(slot.filled_len(), bytes.len()); + assert_eq!(slot.data_slice(), &bytes[..]); + drop(slot); + assert_eq!(pool.stats().in_flight, 0); + } + + #[test] + fn classify_pcm_flags_detects_f32_planar() { + use objc2_core_audio_types::{kAudioFormatFlagIsFloat, kAudioFormatFlagIsNonInterleaved}; + let flags = kAudioFormatFlagIsFloat | kAudioFormatFlagIsNonInterleaved; + assert_eq!( + super::classify_pcm_flags(flags, 32), + super::AudioSampleFormat::F32Planar + ); + } + + #[test] + fn classify_pcm_flags_detects_f32_interleaved() { + use objc2_core_audio_types::kAudioFormatFlagIsFloat; + let flags = kAudioFormatFlagIsFloat; + assert_eq!( + super::classify_pcm_flags(flags, 32), + super::AudioSampleFormat::F32Interleaved + ); + } + + #[test] + fn classify_pcm_flags_distinguishes_planar_from_interleaved_float() { + use objc2_core_audio_types::{kAudioFormatFlagIsFloat, kAudioFormatFlagIsNonInterleaved}; + let planar = super::classify_pcm_flags( + kAudioFormatFlagIsFloat | kAudioFormatFlagIsNonInterleaved, + 32, + ); + let interleaved = super::classify_pcm_flags(kAudioFormatFlagIsFloat, 32); + assert_eq!(planar, super::AudioSampleFormat::F32Planar); + assert_eq!(interleaved, super::AudioSampleFormat::F32Interleaved); + assert_ne!(planar, interleaved); + assert_eq!(planar.bytes_per_sample(), interleaved.bytes_per_sample()); + } + + #[test] + fn classify_pcm_flags_detects_i16_interleaved() { + use objc2_core_audio_types::kAudioFormatFlagIsSignedInteger; + let flags = kAudioFormatFlagIsSignedInteger; + assert_eq!( + super::classify_pcm_flags(flags, 16), + super::AudioSampleFormat::I16Interleaved + ); + } + + #[test] + fn classify_pcm_flags_rejects_unknown() { + assert_eq!( + super::classify_pcm_flags(0, 24), + super::AudioSampleFormat::Unknown + ); + } + + #[test] + fn build_capture_config_preserves_frame_interval_when_audio_enabled() { + let cfg_off = super::build_capture_config( + 60, + false, + 48_000, + 2, + super::SckPixelFormat::Nv12VideoRange, + super::SckColorSpace::SrgbBt709, + ); + let cfg_on = super::build_capture_config( + 60, + true, + 48_000, + 2, + super::SckPixelFormat::Nv12VideoRange, + super::SckColorSpace::SrgbBt709, + ); + assert!(!cfg_off.captures_audio()); + assert!(cfg_on.captures_audio()); + assert_eq!( + cfg_off.minimum_frame_interval_ns(), + cfg_on.minimum_frame_interval_ns() + ); + } + + #[test] + fn build_capture_config_carries_audio_settings() { + let cfg = super::build_capture_config( + 30, + true, + 44_100, + 1, + super::SckPixelFormat::Nv12VideoRange, + super::SckColorSpace::SrgbBt709, + ); + assert!(cfg.captures_audio()); + assert_eq!(cfg.audio_sample_rate_hz(), 44_100); + assert_eq!(cfg.audio_channels(), 1); + } + + #[test] + fn start_options_carry_cursor_color_and_rect_intent() { + let options = super::normalize_start_options(Some(super::ScreenCaptureStartOptions { + show_cursor_clicks: Some(true), + capture_rect: Some(super::ScreenCaptureRect { + x: 10.0, + y: 20.0, + width: 300.0, + height: 200.0, + }), + color_range: Some("full".to_string()), + color_space: Some("rec709".to_string()), + max_output_width: None, + max_output_height: None, + })) + .expect("valid start options"); + assert!(options.shows_cursor); + assert_eq!(options.pixel_format, super::SckPixelFormat::Nv12FullRange); + assert_eq!(options.color_space, super::SckColorSpace::SrgbBt709); + let rect = options.source_rect.expect("capture rect"); + assert_eq!(rect.origin.x, 10.0); + assert_eq!(rect.origin.y, 20.0); + assert_eq!(rect.size.width, 300.0); + assert_eq!(rect.size.height, 200.0); + } + + #[test] + fn start_options_reject_invalid_color_range() { + let err = super::normalize_start_options(Some(super::ScreenCaptureStartOptions { + show_cursor_clicks: None, + capture_rect: None, + color_range: Some("wide".to_string()), + color_space: None, + max_output_width: None, + max_output_height: None, + })) + .expect_err("invalid color range"); + assert_eq!(err.status, napi::Status::InvalidArg); + } + + #[test] + fn parallel_sample_queue_construction_is_safe() { + let handles: Vec<_> = (0..8) + .map(|i| { + std::thread::spawn(move || { + let q = build_scstream_sample_queue("window", (i + 1) as u32); + let done = Arc::new(AtomicU64::new(0)); + let dc = done.clone(); + q.exec_async(move || { + dc.store(1, Ordering::Release); + }); + let deadline = std::time::Instant::now() + Duration::from_secs(1); + while done.load(Ordering::Acquire) == 0 { + if std::time::Instant::now() > deadline { + panic!("parallel queue {} did not drain", i); + } + std::thread::sleep(Duration::from_millis(5)); + } + }) + }) + .collect(); + for h in handles { + h.join().expect("worker panicked"); + } + } +} + +#[cfg(all(test, target_os = "macos"))] +mod audio_extract_tests { + use super::*; + use objc2_core_audio_types::AudioBufferList; + + #[test] + fn ablstorage_size_is_large_enough_for_audio_buffer_list() { + let storage_size = core::mem::size_of::(); + let abl_size = core::mem::size_of::(); + assert!( + storage_size > abl_size, + "AblStorage must hold > 1 AudioBuffer" + ); + assert_eq!(core::mem::align_of::(), 16); + } + + #[test] + fn ablstorage_layout_matches_apple_abl() { + let storage_size = core::mem::size_of::(); + let n_buffers_offset = core::mem::offset_of!(super::AblStorage, n_buffers); + let buffers_offset = core::mem::offset_of!(super::AblStorage, buffers); + assert_eq!(n_buffers_offset, 0, "n_buffers must be at offset 0"); + assert_eq!( + buffers_offset, 8, + "buffers must be at offset 8 (after 4 bytes pad)" + ); + assert!(storage_size >= 24, "must hold at least one AudioBuffer"); + } + + #[test] + fn extract_audio_bytes_rejects_metadata_sample_buffer_gracefully() { + let pool = MacAudioFramePool::new(2, 256).expect("pool"); + assert_eq!(pool.try_acquire().map(|s| s.capacity()), Some(256)); + } +} + +#[cfg(test)] +mod dimension_tests { + use super::*; + use objc2_core_foundation::{CGPoint, CGSize}; + + fn rect(w: f64, h: f64) -> CGRect { + CGRect { + origin: CGPoint { x: 0.0, y: 0.0 }, + size: CGSize { + width: w, + height: h, + }, + } + } + + #[test] + fn even_floor_clears_low_bit() { + assert_eq!(even_floor(3841), 3840); + assert_eq!(even_floor(3840), 3840); + assert_eq!(even_floor(1), 0); + } + + #[test] + fn cap_downscales_8k_to_4k_preserving_aspect() { + assert_eq!(cap_output_dims(7680, 4320, 3840, 2160), (3840, 2160)); + } + + #[test] + fn cap_is_noop_below_ceiling() { + assert_eq!(cap_output_dims(1920, 1080, 3840, 2160), (1920, 1080)); + } + + #[test] + fn cap_preserves_aspect_for_5k_ultrawide() { + let (w, h) = cap_output_dims(5120, 2160, 3840, 2160); + assert_eq!(h, 1620); + assert_eq!(w, 3840); + assert!(w <= 3840 && h <= 2160); + } + + #[test] + fn cap_outputs_are_even_and_bounded() { + for (sw, sh) in [(7680u32, 4320u32), (5120, 2880), (3008, 1692), (1366, 768)] { + let (w, h) = cap_output_dims(sw, sh, 3840, 2160); + assert_eq!(w % 2, 0); + assert_eq!(h % 2, 0); + assert!(w <= 3840); + assert!(h <= 2160); + } + } + + #[test] + fn resolve_prefers_content_rect_times_scale() { + let dims = resolve_source_pixels( + 7680, + 4320, + None, + Some(rect(3840.0, 2160.0)), + Some(2.0), + (3840, 2160), + ); + assert_eq!(dims, (7680, 4320)); + } + + #[test] + fn resolve_falls_back_to_requested_without_selectors() { + assert_eq!( + resolve_source_pixels(7680, 4320, None, None, None, (1920, 1080)), + (7680, 4320) + ); + } + + #[test] + fn resolve_source_rect_takes_priority() { + let dims = resolve_source_pixels( + 7680, + 4320, + Some(rect(1280.0, 720.0)), + Some(rect(3840.0, 2160.0)), + Some(2.0), + (3840, 2160), + ); + assert_eq!(dims, (2560, 1440)); + } + + #[test] + fn resolve_then_cap_fills_4k_from_8k_backing() { + let (sw, sh) = + resolve_source_pixels(0, 0, None, Some(rect(3840.0, 2160.0)), Some(2.0), (0, 0)); + assert_eq!(cap_output_dims(sw, sh, 3840, 2160), (3840, 2160)); + } +} + +#[cfg(test)] +mod external_buffer_tests { + use super::*; + use crate::audio_pool::MacAudioFramePool; + use std::alloc::{GlobalAlloc, Layout, System}; + use std::sync::Arc; + use std::sync::atomic::{AtomicUsize, Ordering}; + use std::thread; + + struct CountingAllocator; + + thread_local! { + static THREAD_ALLOC_COUNT: std::cell::Cell = const { std::cell::Cell::new(0) }; + static THREAD_ALLOC_BYTES: std::cell::Cell = const { std::cell::Cell::new(0) }; + static THREAD_TRACKING: std::cell::Cell = const { std::cell::Cell::new(false) }; + } + + fn tracking_enabled() -> bool { + THREAD_TRACKING.try_with(|t| t.get()).unwrap_or(false) + } + + fn record_alloc(layout: Layout) { + let _ = THREAD_ALLOC_COUNT.try_with(|c| c.set(c.get() + 1)); + let _ = THREAD_ALLOC_BYTES.try_with(|b| b.set(b.get() + layout.size() as u64)); + } + + fn reset_counts() { + let _ = THREAD_ALLOC_COUNT.try_with(|c| c.set(0)); + let _ = THREAD_ALLOC_BYTES.try_with(|b| b.set(0)); + } + + fn snapshot_counts() -> (u64, u64) { + let count = THREAD_ALLOC_COUNT.try_with(|c| c.get()).unwrap_or(0); + let bytes = THREAD_ALLOC_BYTES.try_with(|b| b.get()).unwrap_or(0); + (count, bytes) + } + + fn set_tracking(enabled: bool) { + let _ = THREAD_TRACKING.try_with(|t| t.set(enabled)); + } + + unsafe impl GlobalAlloc for CountingAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + if tracking_enabled() { + record_alloc(layout); + } + unsafe { System.alloc(layout) } + } + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + unsafe { System.dealloc(ptr, layout) } + } + } + + #[global_allocator] + static GLOBAL: CountingAllocator = CountingAllocator; + + struct FakeExternalBuffer { + ptr: *mut u8, + len: usize, + slot: Option, + } + + unsafe impl Send for FakeExternalBuffer {} + + impl FakeExternalBuffer { + fn from_pooled(mut slot: PooledMacAudioFrame) -> Self { + let len = slot.filled_len(); + let ptr = slot.as_mut_ptr(); + assert!(!ptr.is_null()); + Self { + ptr, + len, + slot: Some(slot), + } + } + + fn data(&self) -> &[u8] { + assert!(!self.ptr.is_null()); + unsafe { core::slice::from_raw_parts(self.ptr, self.len) } + } + } + + impl Drop for FakeExternalBuffer { + fn drop(&mut self) { + if let Some(slot) = self.slot.take() { + drop(slot); + } + } + } + + #[test] + fn audio_buffer_external_pointer_round_trip() { + let pool = MacAudioFramePool::new(4, 256).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + let payload = [0x42_u8; 64]; + slot.write(&payload).expect("write"); + let fake = FakeExternalBuffer::from_pooled(slot); + assert_eq!(pool.stats().in_flight, 1); + assert_eq!(fake.data(), &payload[..]); + drop(fake); + assert_eq!(pool.stats().in_flight, 0); + assert_eq!(pool.stats().released, 1); + assert_eq!(pool.stats().acquired, 1); + } + + #[test] + fn multi_frame_external_buffer_lifecycle() { + const FRAMES: u64 = 100; + let pool = MacAudioFramePool::new(8, 128).expect("pool"); + for i in 0..FRAMES { + let mut slot = pool.try_acquire().expect("slot"); + let byte = (i % 256) as u8; + let payload = [byte; 64]; + slot.write(&payload).expect("write"); + let fake = FakeExternalBuffer::from_pooled(slot); + assert_eq!(fake.data()[0], byte); + drop(fake); + } + let stats = pool.stats(); + assert_eq!(stats.acquired, FRAMES); + assert_eq!(stats.released, FRAMES); + assert_eq!(stats.in_flight, 0); + assert_eq!(stats.dropped, 0); + } + + #[test] + fn pool_exhaustion_with_external_buffers_holds_diagnostic() { + let pool = MacAudioFramePool::new(MAC_AUDIO_POOL_CAP, 256).expect("pool"); + let mut held: Vec = Vec::with_capacity(MAC_AUDIO_POOL_CAP); + for _ in 0..MAC_AUDIO_POOL_CAP { + let mut slot = pool.try_acquire().expect("within cap"); + slot.write(&[0xAB_u8; 32]).expect("write"); + held.push(FakeExternalBuffer::from_pooled(slot)); + } + assert_eq!(pool.stats().in_flight as usize, MAC_AUDIO_POOL_CAP); + let next = pool.try_acquire(); + assert!(next.is_none(), "17th acquire must report exhaustion"); + assert_eq!(pool.stats().dropped, 1); + drop(held); + assert_eq!(pool.stats().in_flight, 0); + } + + #[test] + fn buffer_external_drop_fn_runs_on_pool_thread_or_napi_thread() { + let pool = Arc::new(MacAudioFramePool::new(4, 128).expect("pool")); + let pool_clone = Arc::clone(&pool); + let mut slot = pool.try_acquire().expect("slot"); + slot.write(&[0x99_u8; 32]).expect("write"); + let fake = FakeExternalBuffer::from_pooled(slot); + assert_eq!(pool.stats().in_flight, 1); + let handle = thread::spawn(move || { + let fake = fake; + assert_eq!(fake.data()[0], 0x99); + drop(fake); + }); + handle.join().expect("worker"); + assert_eq!(pool_clone.stats().in_flight, 0); + assert_eq!(pool_clone.stats().released, 1); + } + + #[test] + fn zero_allocation_in_steady_state_acquire_write_external_release() { + let pool = + MacAudioFramePool::new(MAC_AUDIO_POOL_CAP, MAX_FRAME_BYTES_PER_SLOT).expect("pool"); + let payload = vec![0xCC_u8; 3840]; + for _ in 0..16 { + let mut slot = pool.try_acquire().expect("warmup"); + slot.write(&payload).expect("warmup write"); + let fake = FakeExternalBuffer::from_pooled(slot); + drop(fake); + } + reset_counts(); + set_tracking(true); + for _ in 0..1000 { + let mut slot = pool.try_acquire().expect("steady-state slot"); + slot.write(&payload).expect("steady-state write"); + let fake = FakeExternalBuffer::from_pooled(slot); + std::hint::black_box(fake.data().len()); + drop(fake); + } + set_tracking(false); + let (count, bytes) = snapshot_counts(); + assert_eq!( + count, 0, + "steady-state acquire/write/external/release must not allocate (got {count} allocations, {bytes} bytes)" + ); + } + + #[test] + fn allocator_counter_self_test_reports_nonzero_when_allocating() { + reset_counts(); + set_tracking(true); + let v: Vec = Vec::with_capacity(1024); + set_tracking(false); + let (count, _) = snapshot_counts(); + let _hold = v; + assert!( + count >= 1, + "self-test: allocator counter must record real allocs" + ); + } + + static FINALIZE_THREAD_COUNTER: AtomicUsize = AtomicUsize::new(0); + + struct ThreadAwareFakeBuffer { + slot: Option, + } + + impl Drop for ThreadAwareFakeBuffer { + fn drop(&mut self) { + FINALIZE_THREAD_COUNTER.fetch_add(1, Ordering::Relaxed); + if let Some(slot) = self.slot.take() { + drop(slot); + } + } + } + + #[test] + fn external_buffer_finalize_runs_exactly_once_per_frame() { + FINALIZE_THREAD_COUNTER.store(0, Ordering::Relaxed); + let pool = MacAudioFramePool::new(4, 64).expect("pool"); + for _ in 0..50 { + let mut slot = pool.try_acquire().expect("slot"); + slot.write(&[0u8; 32]).expect("write"); + let fake = ThreadAwareFakeBuffer { slot: Some(slot) }; + drop(fake); + } + assert_eq!(FINALIZE_THREAD_COUNTER.load(Ordering::Relaxed), 50); + assert_eq!(pool.stats().acquired, 50); + assert_eq!(pool.stats().released, 50); + } +} diff --git a/fluxer_desktop/native/mac-screen-capture/src/napi_surface_stub.rs b/fluxer_desktop/native/mac-screen-capture/src/napi_surface_stub.rs new file mode 100644 index 0000000..b7e5a83 --- /dev/null +++ b/fluxer_desktop/native/mac-screen-capture/src/napi_surface_stub.rs @@ -0,0 +1,82 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use napi::bindgen_prelude::{Error, Result, Status}; +use napi_derive::napi; + +fn unsupported() -> Error { + Error::new( + Status::GenericFailure, + "@fluxer/mac-screen-capture is only supported on macOS", + ) +} + +#[napi(object, js_name = "MacScreenCaptureSource")] +pub struct MacScreenCaptureSource { + pub kind: String, + pub id: String, + pub name: String, + pub width: u32, + pub height: u32, + pub app_name: Option, + pub bundle_id: Option, + pub target_pid: Option, +} + +#[napi(js_name = "listSources")] +pub fn list_sources() -> Result> { + Ok(Vec::new()) +} + +#[napi(object, js_name = "MacScreenCaptureBackendSckAvailability")] +pub struct SckAvailability { + pub supported: bool, + pub macos_version: Option, +} + +#[napi(object, js_name = "MacScreenCaptureBackendAvailability")] +pub struct BackendAvailability { + pub sck: SckAvailability, + pub screen_permission: String, +} + +#[napi(js_name = "getBackendAvailability")] +pub fn get_backend_availability() -> Result { + Err(unsupported()) +} + +#[napi(object, js_name = "MacScreenCaptureBackendInfo")] +pub struct MacScreenCaptureBackendInfo { + pub backend: String, + pub supported: bool, + pub reason: String, + #[napi(js_name = "minMacosVersion")] + pub min_macos_version: String, + #[napi(js_name = "detectedMacosVersion")] + pub detected_macos_version: Option, + #[napi(js_name = "sckAvailable")] + pub sck_available: bool, +} + +#[napi(js_name = "getBackendInfo")] +pub fn get_backend_info() -> MacScreenCaptureBackendInfo { + use crate::os_version::{SCK_MIN_MACOS, format_version}; + MacScreenCaptureBackendInfo { + backend: "mac-screen-capture".to_owned(), + supported: false, + reason: "@fluxer/mac-screen-capture is only supported on macOS".to_owned(), + min_macos_version: format_version(SCK_MIN_MACOS), + detected_macos_version: None, + sck_available: false, + } +} + +#[napi] +pub struct ScreenCapture; + +#[napi] +impl ScreenCapture { + #[napi(constructor)] + pub fn new() -> Result { + Err(unsupported()) + } +} diff --git a/fluxer_desktop/native/mac-screen-capture/src/os_version.rs b/fluxer_desktop/native/mac-screen-capture/src/os_version.rs new file mode 100644 index 0000000..37e51c4 --- /dev/null +++ b/fluxer_desktop/native/mac-screen-capture/src/os_version.rs @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub const SCK_MIN_MACOS: (i64, i64, i64) = (12, 3, 0); + +pub fn meets_floor(version: (i64, i64, i64), floor: (i64, i64, i64)) -> bool { + if version.0 != floor.0 { + return version.0 > floor.0; + } + if version.1 != floor.1 { + return version.1 > floor.1; + } + version.2 >= floor.2 +} + +pub fn format_version(version: (i64, i64, i64)) -> String { + if version.2 == 0 { + format!("{}.{}", version.0, version.1) + } else { + format!("{}.{}.{}", version.0, version.1, version.2) + } +} + +#[cfg(target_os = "macos")] +pub fn current_macos_version() -> Option<(i64, i64, i64)> { + use objc2_foundation::NSProcessInfo; + let info = NSProcessInfo::processInfo(); + let v = info.operatingSystemVersion(); + Some(( + v.majorVersion as i64, + v.minorVersion as i64, + v.patchVersion as i64, + )) +} + +#[cfg(not(target_os = "macos"))] +pub fn current_macos_version() -> Option<(i64, i64, i64)> { + None +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SupportClassification { + pub supported: bool, + pub sck_available: bool, + pub reason: String, +} + +pub fn classify_support(detected: Option<(i64, i64, i64)>) -> SupportClassification { + let min_sck = format_version(SCK_MIN_MACOS); + match detected { + None => SupportClassification { + supported: false, + sck_available: false, + reason: "mac-screen-capture could not detect the running macOS version. \ + Native screen capture unavailable." + .to_owned(), + }, + Some(v) => { + let detected_str = format_version(v); + let sck_ok = meets_floor(v, SCK_MIN_MACOS); + let reason = if sck_ok { + format!( + "mac-screen-capture supported on macOS {detected_str} \ + (ScreenCaptureKit, requires macOS {min_sck}+)." + ) + } else { + format!( + "mac-screen-capture requires macOS {min_sck}+ (ScreenCaptureKit). \ + This Mac is running macOS {detected_str}. Native cursor-hidden \ + screen capture unavailable; fall back to getDisplayMedia." + ) + }; + SupportClassification { + supported: sck_ok, + sck_available: sck_ok, + reason, + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn meets_floor_exact_match() { + assert!(meets_floor((12, 3, 0), SCK_MIN_MACOS)); + } + + #[test] + fn meets_floor_higher_major() { + assert!(meets_floor((14, 0, 0), SCK_MIN_MACOS)); + } + + #[test] + fn rejects_below_floor() { + assert!(!meets_floor((12, 2, 9), SCK_MIN_MACOS)); + assert!(!meets_floor((11, 7, 10), SCK_MIN_MACOS)); + } + + #[test] + fn format_version_trims_zero_patch() { + assert_eq!("12.3", format_version((12, 3, 0))); + assert_eq!("14.2.1", format_version((14, 2, 1))); + } + + #[test] + fn classify_unknown_version_is_unsupported() { + let c = classify_support(None); + assert!(!c.supported); + assert!(!c.sck_available); + assert!(c.reason.contains("could not detect")); + } + + #[test] + fn classify_macos_11_is_unsupported() { + let c = classify_support(Some((11, 7, 10))); + assert!(!c.supported); + assert!(!c.sck_available); + assert!(c.reason.contains("macOS 12.3+")); + assert!(c.reason.contains("macOS 11.7.10")); + } + + #[test] + fn classify_macos_12_3_is_supported() { + let c = classify_support(Some((12, 3, 0))); + assert!(c.supported); + assert!(c.sck_available); + assert!(c.reason.contains("ScreenCaptureKit")); + } +} diff --git a/fluxer_desktop/native/mac-screen-capture/src/sck.rs b/fluxer_desktop/native/mac-screen-capture/src/sck.rs new file mode 100644 index 0000000..75763e4 --- /dev/null +++ b/fluxer_desktop/native/mac-screen-capture/src/sck.rs @@ -0,0 +1,322 @@ +#![allow(non_snake_case)] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +use objc2::rc::Retained; +use objc2_core_foundation::{CFString, CGFloat, CGRect}; +use objc2_core_media::{CMTime, CMTimeFlags}; +use objc2_foundation::NSString; +use objc2_screen_capture_kit::{ + SCCaptureDynamicRange, SCContentFilter, SCDisplay, SCRunningApplication, SCStream, + SCStreamConfiguration, SCWindow, +}; + +use crate::config::{SckCaptureConfig, SckColorSpace, SckPixelFormat}; + +pub use objc2_screen_capture_kit::SCStreamOutputType; + +pub fn cmtime_seconds(value: i64, timescale: i32) -> CMTime { + CMTime { + value, + timescale, + flags: CMTimeFlags(1), + epoch: 0, + } +} + +pub fn sc_running_application_process_id(app: &SCRunningApplication) -> i32 { + unsafe { app.processID() } +} + +pub fn sc_running_application_bundle_identifier(app: &SCRunningApplication) -> Retained { + unsafe { app.bundleIdentifier() } +} + +pub fn sc_running_application_name(app: &SCRunningApplication) -> Retained { + unsafe { app.applicationName() } +} + +pub fn sc_display_frame(display: &SCDisplay) -> CGRect { + unsafe { display.frame() } +} + +pub fn sc_display_display_id(display: &SCDisplay) -> u32 { + unsafe { display.displayID() } +} + +pub fn sc_display_width(display: &SCDisplay) -> isize { + unsafe { display.width() } +} + +pub fn sc_display_height(display: &SCDisplay) -> isize { + unsafe { display.height() } +} + +pub fn sc_window_window_id(win: &SCWindow) -> u32 { + unsafe { win.windowID() } +} + +pub fn sc_window_owning_application(win: &SCWindow) -> Option> { + unsafe { win.owningApplication() } +} + +pub fn sc_window_frame(win: &SCWindow) -> CGRect { + unsafe { win.frame() } +} + +pub fn sc_window_title(win: &SCWindow) -> Option> { + unsafe { win.title() } +} + +pub fn sc_window_is_on_screen(win: &SCWindow) -> bool { + unsafe { win.isOnScreen() } +} + +pub fn filter_content_rect_if_available(filter: &SCContentFilter) -> Option { + use objc2::runtime::NSObjectProtocol; + use objc2::{msg_send, sel}; + let obj: &objc2::runtime::NSObject = filter.as_ref(); + if !obj.respondsToSelector(sel!(contentRect)) { + return None; + } + let rect: CGRect = unsafe { msg_send![obj, contentRect] }; + if rect.size.width <= 0.0 || rect.size.height <= 0.0 { + return None; + } + Some(rect) +} + +pub fn filter_point_pixel_scale_if_available(filter: &SCContentFilter) -> Option { + use objc2::runtime::NSObjectProtocol; + use objc2::{msg_send, sel}; + let obj: &objc2::runtime::NSObject = filter.as_ref(); + if !obj.respondsToSelector(sel!(pointPixelScale)) { + return None; + } + let scale: CGFloat = unsafe { msg_send![obj, pointPixelScale] }; + if !scale.is_finite() || scale <= 0.0 { + return None; + } + Some(scale as f32) +} + +pub fn cfg_set_scales_to_fit_if_available(cfg: &SCStreamConfiguration, scales_to_fit: bool) { + use objc2::runtime::NSObjectProtocol; + use objc2::{msg_send, sel}; + let obj: &objc2::runtime::NSObject = cfg.as_ref(); + if obj.respondsToSelector(sel!(setScalesToFit:)) { + unsafe { + let _: () = msg_send![obj, setScalesToFit: scales_to_fit]; + } + } +} + +pub fn cfg_set_width(cfg: &SCStreamConfiguration, v: usize) { + unsafe { cfg.setWidth(v) } +} +pub fn cfg_set_height(cfg: &SCStreamConfiguration, v: usize) { + unsafe { cfg.setHeight(v) } +} +pub fn cfg_set_queue_depth(cfg: &SCStreamConfiguration, v: isize) { + unsafe { cfg.setQueueDepth(v) } +} +pub fn cfg_set_shows_cursor(cfg: &SCStreamConfiguration, v: bool) { + unsafe { cfg.setShowsCursor(v) } +} +pub fn cfg_set_minimum_frame_interval(cfg: &SCStreamConfiguration, t: CMTime) { + unsafe { cfg.setMinimumFrameInterval(t) } +} +pub fn cfg_set_pixel_format(cfg: &SCStreamConfiguration, format: u32) { + unsafe { cfg.setPixelFormat(format) } +} + +pub fn cfg_set_source_rect_if_available(cfg: &SCStreamConfiguration, rect: CGRect) -> bool { + use objc2::runtime::NSObjectProtocol; + use objc2::{msg_send, sel}; + assert!(rect.size.width > 0.0); + assert!(rect.size.height > 0.0); + let obj: &objc2::runtime::NSObject = cfg.as_ref(); + if !obj.respondsToSelector(sel!(setSourceRect:)) { + return false; + } + unsafe { + let _: () = msg_send![obj, setSourceRect: rect]; + } + true +} + +pub fn cfg_set_capture_dynamic_range_sdr_if_available(cfg: &SCStreamConfiguration) { + use objc2::runtime::NSObjectProtocol; + use objc2::sel; + let obj: &objc2::runtime::NSObject = cfg.as_ref(); + if obj.respondsToSelector(sel!(setCaptureDynamicRange:)) { + unsafe { + cfg.setCaptureDynamicRange(SCCaptureDynamicRange::SDR); + } + } +} + +pub fn cfg_set_capture_dynamic_range_hdr_if_available(cfg: &SCStreamConfiguration) { + use objc2::runtime::NSObjectProtocol; + use objc2::sel; + let obj: &objc2::runtime::NSObject = cfg.as_ref(); + if obj.respondsToSelector(sel!(setCaptureDynamicRange:)) { + unsafe { + cfg.setCaptureDynamicRange(SCCaptureDynamicRange::HDRLocalDisplay); + } + } +} + +pub fn cfg_set_color_space_name_if_available(cfg: &SCStreamConfiguration, name: &str) { + use objc2::runtime::NSObjectProtocol; + use objc2::sel; + assert!(!name.is_empty()); + let obj: &objc2::runtime::NSObject = cfg.as_ref(); + if obj.respondsToSelector(sel!(setColorSpaceName:)) { + let cf = CFString::from_str(name); + unsafe { + cfg.setColorSpaceName(&cf); + } + } +} + +pub fn obs_minimum_frame_interval(target_fps: u32) -> CMTime { + assert!(target_fps >= crate::config::FPS_MIN); + assert!(target_fps <= crate::config::FPS_MAX); + CMTime { + value: crate::config::FRAME_INTERVAL_FACTOR_NUM as i64, + timescale: (crate::config::FRAME_INTERVAL_FACTOR_DEN as i32) + .saturating_mul(target_fps as i32), + flags: CMTimeFlags(1), + epoch: 0, + } +} + +pub fn apply_capture_config(cfg: &SCStreamConfiguration, capture: &SckCaptureConfig) { + assert!(capture.target_fps() >= crate::config::FPS_MIN); + assert!(capture.queue_depth() >= crate::config::QUEUE_DEPTH_MIN); + cfg_set_queue_depth(cfg, capture.queue_depth() as isize); + cfg_set_pixel_format(cfg, capture.pixel_format().as_fourcc()); + cfg_set_minimum_frame_interval(cfg, obs_minimum_frame_interval(capture.target_fps())); + cfg_set_color_space_name_if_available(cfg, capture.color_space().as_cf_name()); + match capture.pixel_format() { + SckPixelFormat::L10rHdr => { + assert!(capture.color_space() == SckColorSpace::DisplayP3); + cfg_set_capture_dynamic_range_hdr_if_available(cfg); + } + SckPixelFormat::Bgra8 | SckPixelFormat::Nv12VideoRange | SckPixelFormat::Nv12FullRange => { + cfg_set_capture_dynamic_range_sdr_if_available(cfg); + } + } + if capture.captures_audio() { + cfg_set_captures_audio_if_available(cfg, true); + cfg_set_audio_sample_rate_if_available(cfg, capture.audio_sample_rate_hz()); + cfg_set_audio_channel_count_if_available(cfg, capture.audio_channels()); + cfg_set_excludes_current_process_audio_if_available(cfg, true); + } else { + cfg_set_captures_audio_if_available(cfg, false); + } +} + +pub fn cfg_set_captures_audio_if_available(cfg: &SCStreamConfiguration, captures_audio: bool) { + use objc2::runtime::NSObjectProtocol; + use objc2::sel; + let obj: &objc2::runtime::NSObject = cfg.as_ref(); + if obj.respondsToSelector(sel!(setCapturesAudio:)) { + unsafe { + cfg.setCapturesAudio(captures_audio); + } + } +} + +pub fn cfg_set_audio_sample_rate_if_available(cfg: &SCStreamConfiguration, sample_rate_hz: u32) { + use objc2::runtime::NSObjectProtocol; + use objc2::sel; + assert!(sample_rate_hz >= crate::config::AUDIO_SAMPLE_RATE_MIN_HZ); + assert!(sample_rate_hz <= crate::config::AUDIO_SAMPLE_RATE_MAX_HZ); + let obj: &objc2::runtime::NSObject = cfg.as_ref(); + if obj.respondsToSelector(sel!(setSampleRate:)) { + unsafe { + cfg.setSampleRate(sample_rate_hz as isize); + } + } +} + +pub fn cfg_set_audio_channel_count_if_available(cfg: &SCStreamConfiguration, channels: u32) { + use objc2::runtime::NSObjectProtocol; + use objc2::sel; + assert!(channels >= crate::config::AUDIO_CHANNEL_COUNT_MIN); + assert!(channels <= crate::config::AUDIO_CHANNEL_COUNT_MAX); + let obj: &objc2::runtime::NSObject = cfg.as_ref(); + if obj.respondsToSelector(sel!(setChannelCount:)) { + unsafe { + cfg.setChannelCount(channels as isize); + } + } +} + +pub fn cfg_set_excludes_current_process_audio_if_available( + cfg: &SCStreamConfiguration, + excludes_self: bool, +) { + use objc2::runtime::NSObjectProtocol; + use objc2::sel; + let obj: &objc2::runtime::NSObject = cfg.as_ref(); + if obj.respondsToSelector(sel!(setExcludesCurrentProcessAudio:)) { + unsafe { + cfg.setExcludesCurrentProcessAudio(excludes_self); + } + } +} + +pub fn cfg_set_stream_name_if_available(cfg: &SCStreamConfiguration, name: &NSString) { + use objc2::runtime::NSObjectProtocol; + use objc2::sel; + let obj: &objc2::runtime::NSObject = cfg.as_ref(); + if obj.respondsToSelector(sel!(setStreamName:)) { + unsafe { + cfg.setStreamName(Some(name)); + } + } +} + +pub fn sc_stream_add_stream_output( + stream: &SCStream, + output: &objc2::runtime::ProtocolObject, + kind: SCStreamOutputType, + queue: Option<&dispatch2::DispatchQueue>, +) -> Result<(), Retained> { + unsafe { stream.addStreamOutput_type_sampleHandlerQueue_error(output, kind, queue) } +} + +#[cfg(test)] +mod tests { + use super::obs_minimum_frame_interval; + use crate::config::{FPS_MAX, FPS_MIN, SckCaptureConfig}; + + #[test] + fn minimum_frame_interval_is_strictly_shorter_than_frame_time() { + for fps in [FPS_MIN, 30, 60, FPS_MAX] { + let t = obs_minimum_frame_interval(fps); + assert!(t.value > 0); + assert!(t.timescale > 0); + let interval_ns = (t.value as u64) * 1_000_000_000 / (t.timescale as u64); + let frame_ns = 1_000_000_000 / (fps as u64); + assert!(interval_ns < frame_ns, "fps={fps}"); + } + } + + #[test] + fn minimum_frame_interval_matches_config_minimum_frame_interval_ns() { + for fps in [FPS_MIN, 30, 60, FPS_MAX] { + let t = obs_minimum_frame_interval(fps); + let cm_interval_ns = (t.value as u64) * 1_000_000_000 / (t.timescale as u64); + let cfg = SckCaptureConfig::builder() + .target_fps(fps) + .build() + .expect("config builds"); + let cfg_interval_ns = cfg.minimum_frame_interval_ns(); + assert!(cm_interval_ns.abs_diff(cfg_interval_ns) <= 1, "fps={fps}"); + } + } +} diff --git a/fluxer_desktop/native/mac-sysctl/Cargo.lock b/fluxer_desktop/native/mac-sysctl/Cargo.lock new file mode 100644 index 0000000..948f90e --- /dev/null +++ b/fluxer_desktop/native/mac-sysctl/Cargo.lock @@ -0,0 +1,390 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "enum-as-inner" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1e6a265c649f3f5979b601d26f1d05ada116434c87741c9493cb56218f76cbc" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "fluxer_mac_sysctl" +version = "0.0.0" +dependencies = [ + "napi", + "napi-build", + "napi-derive", + "sysctl", + "tokio", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash", + "tokio", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sysctl" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cca424247104946a59dacd27eaad296223b7feec3d168a6dd04585183091eb0b" +dependencies = [ + "bitflags", + "byteorder", + "enum-as-inner", + "libc", + "thiserror", + "walkdir", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "pin-project-lite", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] diff --git a/fluxer_desktop/native/mac-sysctl/Cargo.toml b/fluxer_desktop/native/mac-sysctl/Cargo.toml new file mode 100644 index 0000000..8c2a4f3 --- /dev/null +++ b/fluxer_desktop/native/mac-sysctl/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "fluxer_mac_sysctl" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8", "tokio_rt"]} +napi-derive = "3.5.6" +sysctl = "0.7.1" +tokio = {version = "1.52.3", features = ["rt"]} + +[build-dependencies] +napi-build = "2.3.2" diff --git a/fluxer_desktop/native/mac-sysctl/build.rs b/fluxer_desktop/native/mac-sysctl/build.rs new file mode 100644 index 0000000..d45de40 --- /dev/null +++ b/fluxer_desktop/native/mac-sysctl/build.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + napi_build::setup(); +} diff --git a/fluxer_desktop/native/mac-sysctl/index.d.ts b/fluxer_desktop/native/mac-sysctl/index.d.ts new file mode 100644 index 0000000..6edf367 --- /dev/null +++ b/fluxer_desktop/native/mac-sysctl/index.d.ts @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +export declare const sysctlByNameInt: ((name: string) => Promise) | null; +export declare const sysctlByNameString: ((name: string) => Promise) | null; +export declare const loadError: Error | null; diff --git a/fluxer_desktop/native/mac-sysctl/index.js b/fluxer_desktop/native/mac-sysctl/index.js new file mode 100644 index 0000000..42135e5 --- /dev/null +++ b/fluxer_desktop/native/mac-sysctl/index.js @@ -0,0 +1,64 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync} = require('node:fs'); +const {join, sep} = require('node:path'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); +const MODULE_NAME = '@fluxer/mac-sysctl'; +const SKIP_NATIVE_PROBE_ENV = 'FLUXER_MAC_SYSCTL_SKIP_NATIVE_PROBE'; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName() { + if (process.platform !== 'darwin') { + throw new Error(`@fluxer/mac-sysctl is only supported on macOS, got ${process.platform}`); + } + switch (process.arch) { + case 'x64': + return 'mac-sysctl.darwin-x64.node'; + case 'arm64': + return 'mac-sysctl.darwin-arm64.node'; + default: + throw new Error(`Unsupported macOS architecture: ${process.arch}`); + } +} + +let binding = null; +let loadError = null; + +if (process.platform === 'darwin') { + try { + const nativeRoot = resolveNativeRoot(); + const nativePath = join(nativeRoot, nativeFileName()); + const loaded = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + binding = loaded.binding; + loadError = loaded.loadError; + if (loadError) throw loadError; + } catch (error) { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: 'native loader threw before binding load completed', + cause: error, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + throw loadError; + } +} + +module.exports = { + sysctlByNameInt: binding ? binding.sysctlByNameInt : null, + sysctlByNameString: binding ? binding.sysctlByNameString : null, + loadError, +}; diff --git a/fluxer_desktop/native/mac-sysctl/loader-diagnostics.cjs b/fluxer_desktop/native/mac-sysctl/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/mac-sysctl/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/mac-sysctl/package.json b/fluxer_desktop/native/mac-sysctl/package.json new file mode 100644 index 0000000..31f6ed6 --- /dev/null +++ b/fluxer_desktop/native/mac-sysctl/package.json @@ -0,0 +1,27 @@ +{ + "name": "@fluxer/mac-sysctl", + "version": "0.0.0", + "description": "", + "private": true, + "license": "AGPL-3.0-or-later", + "os": [ + "darwin" + ], + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "mac-sysctl.darwin-x64.node", + "mac-sysctl.darwin-arm64.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "cargo test --manifest-path Cargo.toml" + } +} diff --git a/fluxer_desktop/native/mac-sysctl/src/lib.rs b/fluxer_desktop/native/mac-sysctl/src/lib.rs new file mode 100644 index 0000000..c48aefb --- /dev/null +++ b/fluxer_desktop/native/mac-sysctl/src/lib.rs @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use napi::{Error, Result, Status}; +use napi_derive::napi; + +#[cfg(target_os = "macos")] +use sysctl::{Ctl, CtlValue, Sysctl, SysctlError}; + +fn invalid_name_error(function_name: &str) -> Error { + Error::new( + Status::InvalidArg, + format!("{function_name} requires a non-empty name string"), + ) +} + +#[cfg(target_os = "macos")] +fn map_sysctl_error(error: SysctlError) -> Error { + match error { + SysctlError::IoError(ref io_error) => { + if let Some(errno) = io_error.raw_os_error() { + return Error::new( + Status::GenericFailure, + format!("sysctlbyname failed (errno {errno})"), + ); + } + } + SysctlError::MissingImplementation + | SysctlError::ExtractionError + | SysctlError::ParseError + | SysctlError::InvalidCStr(_) + | SysctlError::InvalidCString(_) => { + return Error::new( + Status::GenericFailure, + "sysctlbyname failed (errno 22)".to_owned(), + ); + } + SysctlError::NotFound(_) + | SysctlError::Utf8Error(_) + | SysctlError::NoReadAccess + | SysctlError::NoWriteAccess + | SysctlError::NotSupported + | SysctlError::ShortRead { .. } => {} + } + + Error::new( + Status::GenericFailure, + format!("sysctlbyname failed ({error})"), + ) +} + +#[cfg(not(target_os = "macos"))] +fn unsupported_platform_error() -> Error { + Error::new( + Status::GenericFailure, + "sysctlbyname failed (errno 22)".to_owned(), + ) +} + +#[cfg(target_os = "macos")] +fn ctl_for_name(name: &str) -> Result> { + match Ctl::new(name) { + Ok(ctl) => Ok(Some(ctl)), + Err(SysctlError::NotFound(_)) => Ok(None), + Err(error) => Err(map_sysctl_error(error)), + } +} + +#[cfg(target_os = "macos")] +fn value_to_number(value: CtlValue) -> Result> { + let number = match value { + CtlValue::None => return Ok(None), + CtlValue::Int(value) => f64::from(value), + CtlValue::Uint(value) => f64::from(value), + CtlValue::Long(value) => value as f64, + CtlValue::Ulong(value) => value as f64, + CtlValue::S64(value) => value as f64, + CtlValue::U64(value) => value as f64, + CtlValue::S32(value) => f64::from(value), + CtlValue::U32(value) => f64::from(value), + CtlValue::S16(value) => f64::from(value), + CtlValue::U16(value) => f64::from(value), + CtlValue::S8(value) => f64::from(value), + CtlValue::U8(value) => f64::from(value), + CtlValue::String(_) | CtlValue::Struct(_) | CtlValue::Node(_) => { + return Err(Error::new( + Status::GenericFailure, + "sysctlbyname failed (errno 22)".to_owned(), + )); + } + }; + Ok(Some(number)) +} + +#[cfg(target_os = "macos")] +fn read_int_blocking(name: String) -> Result> { + let Some(ctl) = ctl_for_name(&name)? else { + return Ok(None); + }; + ctl.value() + .map_err(map_sysctl_error) + .and_then(value_to_number) +} + +#[cfg(not(target_os = "macos"))] +fn read_int_blocking(_: String) -> Result> { + Err(unsupported_platform_error()) +} + +#[cfg(target_os = "macos")] +fn read_string_blocking(name: String) -> Result> { + let Some(ctl) = ctl_for_name(&name)? else { + return Ok(None); + }; + match ctl.value().map_err(map_sysctl_error)? { + CtlValue::None => Ok(None), + CtlValue::String(value) => Ok(Some(value)), + _ => Err(Error::new( + Status::GenericFailure, + "sysctlbyname failed (errno 22)".to_owned(), + )), + } +} + +#[cfg(not(target_os = "macos"))] +fn read_string_blocking(_: String) -> Result> { + Err(unsupported_platform_error()) +} + +async fn spawn_sysctl( + work: impl FnOnce() -> Result + Send + 'static, +) -> Result { + tokio::task::spawn_blocking(work).await.map_err(|error| { + Error::new( + Status::GenericFailure, + format!("sysctl worker failed: {error}"), + ) + })? +} + +#[napi(js_name = "sysctlByNameInt")] +pub async fn sysctl_by_name_int(name: String) -> Result> { + if name.is_empty() { + return Err(invalid_name_error("sysctlByNameInt")); + } + spawn_sysctl(move || read_int_blocking(name)).await +} + +#[napi(js_name = "sysctlByNameString")] +pub async fn sysctl_by_name_string(name: String) -> Result> { + if name.is_empty() { + return Err(invalid_name_error("sysctlByNameString")); + } + spawn_sysctl(move || read_string_blocking(name)).await +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty_name_error_mentions_function_name() { + let error = invalid_name_error("sysctlByNameInt"); + assert_eq!(Status::InvalidArg, error.status); + assert!(error.reason.contains("sysctlByNameInt")); + } + + #[cfg(not(target_os = "macos"))] + #[test] + fn non_macos_returns_same_synthetic_errno_as_previous_js_contract() { + let error = unsupported_platform_error(); + assert_eq!("sysctlbyname failed (errno 22)", error.reason); + } + + #[cfg(target_os = "macos")] + #[test] + fn missing_sysctl_maps_to_null() { + assert!( + ctl_for_name("fluxer.definitely_missing_sysctl") + .unwrap() + .is_none() + ); + } +} diff --git a/fluxer_desktop/native/mac-tcc/Cargo.lock b/fluxer_desktop/native/mac-tcc/Cargo.lock new file mode 100644 index 0000000..777a75d --- /dev/null +++ b/fluxer_desktop/native/mac-tcc/Cargo.lock @@ -0,0 +1,370 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" +dependencies = [ + "bitflags", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags", + "core-foundation", + "libc", +] + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "fluxer_mac_tcc" +version = "0.0.0" +dependencies = [ + "core-graphics", + "napi", + "napi-build", + "napi-derive", + "objc2-io-kit", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", +] + +[[package]] +name = "objc2-io-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +dependencies = [ + "objc2-core-foundation", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" diff --git a/fluxer_desktop/native/mac-tcc/Cargo.toml b/fluxer_desktop/native/mac-tcc/Cargo.toml new file mode 100644 index 0000000..bcb0db0 --- /dev/null +++ b/fluxer_desktop/native/mac-tcc/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "fluxer_mac_tcc" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8"]} +napi-derive = "3.5.6" + +[target.'cfg(target_os = "macos")'.dependencies] +core-graphics = "0.25.0" +objc2-io-kit = {version = "0.3.2", default-features = false, features = ["std", "hidsystem"]} + +[build-dependencies] +napi-build = "2.3.2" diff --git a/fluxer_desktop/native/mac-tcc/build.rs b/fluxer_desktop/native/mac-tcc/build.rs new file mode 100644 index 0000000..d45de40 --- /dev/null +++ b/fluxer_desktop/native/mac-tcc/build.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + napi_build::setup(); +} diff --git a/fluxer_desktop/native/mac-tcc/index.d.ts b/fluxer_desktop/native/mac-tcc/index.d.ts new file mode 100644 index 0000000..c86eb27 --- /dev/null +++ b/fluxer_desktop/native/mac-tcc/index.d.ts @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +export type TccStatus = 'granted' | 'denied' | 'not-determined'; + +export declare function screenRecordingStatus(): TccStatus; + +export declare function requestScreenRecording(): TccStatus; + +export declare function inputMonitoringStatus(): TccStatus; + +export declare function requestInputMonitoring(): TccStatus; + +export declare const loadError: Error | null; diff --git a/fluxer_desktop/native/mac-tcc/index.js b/fluxer_desktop/native/mac-tcc/index.js new file mode 100644 index 0000000..7cdba72 --- /dev/null +++ b/fluxer_desktop/native/mac-tcc/index.js @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync} = require('node:fs'); +const {join, sep} = require('node:path'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); +const MODULE_NAME = '@fluxer/mac-tcc'; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName() { + if (process.platform !== 'darwin') { + throw new Error(`@fluxer/mac-tcc is only supported on macOS, got ${process.platform}`); + } + switch (process.arch) { + case 'x64': + return 'mac-tcc.darwin-x64.node'; + case 'arm64': + return 'mac-tcc.darwin-arm64.node'; + default: + throw new Error(`Unsupported macOS architecture: ${process.arch}`); + } +} + +let binding = null; +let loadError = null; + +if (process.platform === 'darwin') { + try { + const nativeRoot = resolveNativeRoot(); + const nativePath = join(nativeRoot, nativeFileName()); + const loaded = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + probe: false, + }); + binding = loaded.binding; + loadError = loaded.loadError; + if (loadError) throw loadError; + } catch (error) { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: 'native loader threw before binding load completed', + cause: error, + }); + throw loadError; + } +} + +const stub = () => 'not-determined'; + +module.exports = { + screenRecordingStatus: binding ? binding.screenRecordingStatus : stub, + requestScreenRecording: binding ? binding.requestScreenRecording : stub, + inputMonitoringStatus: binding ? binding.inputMonitoringStatus : stub, + requestInputMonitoring: binding ? binding.requestInputMonitoring : stub, + loadError, +}; diff --git a/fluxer_desktop/native/mac-tcc/loader-diagnostics.cjs b/fluxer_desktop/native/mac-tcc/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/mac-tcc/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/mac-tcc/package.json b/fluxer_desktop/native/mac-tcc/package.json new file mode 100644 index 0000000..5ca4bdd --- /dev/null +++ b/fluxer_desktop/native/mac-tcc/package.json @@ -0,0 +1,27 @@ +{ + "name": "@fluxer/mac-tcc", + "version": "0.0.0", + "description": "", + "private": true, + "license": "AGPL-3.0-or-later", + "os": [ + "darwin" + ], + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "mac-tcc.darwin-x64.node", + "mac-tcc.darwin-arm64.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "cargo test --manifest-path Cargo.toml" + } +} diff --git a/fluxer_desktop/native/mac-tcc/src/lib.rs b/fluxer_desktop/native/mac-tcc/src/lib.rs new file mode 100644 index 0000000..9a21488 --- /dev/null +++ b/fluxer_desktop/native/mac-tcc/src/lib.rs @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use napi_derive::napi; + +const STATUS_GRANTED: &str = "granted"; +const STATUS_DENIED: &str = "denied"; +const STATUS_NOT_DETERMINED: &str = "not-determined"; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[cfg_attr(not(any(target_os = "macos", test)), allow(dead_code))] +enum TccStatus { + Granted, + Denied, + NotDetermined, +} + +impl TccStatus { + const fn as_str(self) -> &'static str { + match self { + Self::Granted => STATUS_GRANTED, + Self::Denied => STATUS_DENIED, + Self::NotDetermined => STATUS_NOT_DETERMINED, + } + } +} + +#[cfg_attr(not(any(target_os = "macos", test)), allow(dead_code))] +const IOHID_ACCESS_GRANTED: u32 = 0; +#[cfg_attr(not(any(target_os = "macos", test)), allow(dead_code))] +const IOHID_ACCESS_DENIED: u32 = 1; + +#[cfg_attr(not(any(target_os = "macos", test)), allow(dead_code))] +const fn input_monitoring_status_from_iohid(access: u32) -> TccStatus { + match access { + IOHID_ACCESS_GRANTED => TccStatus::Granted, + IOHID_ACCESS_DENIED => TccStatus::Denied, + _ => TccStatus::NotDetermined, + } +} + +fn status_string(status: TccStatus) -> String { + status.as_str().to_owned() +} + +#[napi(js_name = "screenRecordingStatus")] +pub fn screen_recording_status() -> String { + status_string(platform::screen_recording_status()) +} + +#[napi(js_name = "requestScreenRecording")] +pub fn request_screen_recording() -> String { + status_string(platform::request_screen_recording()) +} + +#[napi(js_name = "inputMonitoringStatus")] +pub fn input_monitoring_status() -> String { + status_string(platform::input_monitoring_status()) +} + +#[napi(js_name = "requestInputMonitoring")] +pub fn request_input_monitoring() -> String { + status_string(platform::request_input_monitoring()) +} + +#[cfg(target_os = "macos")] +mod platform { + use core_graphics::access::ScreenCaptureAccess; + use objc2_io_kit::{IOHIDCheckAccess, IOHIDRequestAccess, IOHIDRequestType}; + + use super::{TccStatus, input_monitoring_status_from_iohid}; + + pub(super) fn screen_recording_status() -> TccStatus { + if ScreenCaptureAccess.preflight() { + TccStatus::Granted + } else { + TccStatus::Denied + } + } + + pub(super) fn request_screen_recording() -> TccStatus { + if ScreenCaptureAccess.request() { + TccStatus::Granted + } else { + TccStatus::Denied + } + } + + pub(super) fn input_monitoring_status() -> TccStatus { + let access = IOHIDCheckAccess(IOHIDRequestType::ListenEvent); + input_monitoring_status_from_iohid(access.0) + } + + pub(super) fn request_input_monitoring() -> TccStatus { + if IOHIDRequestAccess(IOHIDRequestType::ListenEvent) { + TccStatus::Granted + } else { + TccStatus::Denied + } + } +} + +#[cfg(not(target_os = "macos"))] +mod platform { + use super::TccStatus; + + pub(super) fn screen_recording_status() -> TccStatus { + TccStatus::NotDetermined + } + + pub(super) fn request_screen_recording() -> TccStatus { + TccStatus::NotDetermined + } + + pub(super) fn input_monitoring_status() -> TccStatus { + TccStatus::NotDetermined + } + + pub(super) fn request_input_monitoring() -> TccStatus { + TccStatus::NotDetermined + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn status_strings_match_js_contract() { + assert_eq!(TccStatus::Granted.as_str(), "granted"); + assert_eq!(TccStatus::Denied.as_str(), "denied"); + assert_eq!(TccStatus::NotDetermined.as_str(), "not-determined"); + } + + #[test] + fn input_monitoring_maps_known_iohid_statuses() { + assert_eq!( + input_monitoring_status_from_iohid(IOHID_ACCESS_GRANTED), + TccStatus::Granted + ); + assert_eq!( + input_monitoring_status_from_iohid(IOHID_ACCESS_DENIED), + TccStatus::Denied + ); + assert_eq!( + input_monitoring_status_from_iohid(2), + TccStatus::NotDetermined + ); + assert_eq!( + input_monitoring_status_from_iohid(u32::MAX), + TccStatus::NotDetermined + ); + } + + #[cfg(not(target_os = "macos"))] + #[test] + fn non_macos_exports_preserve_stub_contract() { + assert_eq!(screen_recording_status(), "not-determined"); + assert_eq!(request_screen_recording(), "not-determined"); + assert_eq!(input_monitoring_status(), "not-determined"); + assert_eq!(request_input_monitoring(), "not-determined"); + } +} diff --git a/fluxer_desktop/native/macos-input-hook/Cargo.lock b/fluxer_desktop/native/macos-input-hook/Cargo.lock new file mode 100644 index 0000000..af28002 --- /dev/null +++ b/fluxer_desktop/native/macos-input-hook/Cargo.lock @@ -0,0 +1,371 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" +dependencies = [ + "bitflags", + "core-foundation", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags", + "core-foundation", + "libc", +] + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "fluxer_macos_input_hook" +version = "0.0.0" +dependencies = [ + "core-foundation", + "core-graphics", + "napi", + "napi-build", + "napi-derive", + "objc2-core-graphics", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "objc2-core-foundation", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" diff --git a/fluxer_desktop/native/macos-input-hook/Cargo.toml b/fluxer_desktop/native/macos-input-hook/Cargo.toml new file mode 100644 index 0000000..469d214 --- /dev/null +++ b/fluxer_desktop/native/macos-input-hook/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "fluxer_macos_input_hook" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8"]} +napi-derive = "3.5.6" + +[target.'cfg(target_os = "macos")'.dependencies] +core-foundation = "0.10.1" +core-graphics = "0.25.0" +objc2-core-graphics = {version = "0.3.2", default-features = false, features = ["std", "CGEvent"]} + +[build-dependencies] +napi-build = "2.3.2" diff --git a/fluxer_desktop/native/macos-input-hook/build.rs b/fluxer_desktop/native/macos-input-hook/build.rs new file mode 100644 index 0000000..d45de40 --- /dev/null +++ b/fluxer_desktop/native/macos-input-hook/build.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + napi_build::setup(); +} diff --git a/fluxer_desktop/native/macos-input-hook/index.d.ts b/fluxer_desktop/native/macos-input-hook/index.d.ts new file mode 100644 index 0000000..7538a7d --- /dev/null +++ b/fluxer_desktop/native/macos-input-hook/index.d.ts @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +export type InputEvent = + | { + type: 'keydown' | 'keyup'; + keycode: number; + keyName: string; + ctrlKey: boolean; + altKey: boolean; + shiftKey: boolean; + metaKey: boolean; + } + | { + type: 'mousedown' | 'mouseup'; + button: number; + ctrlKey: boolean; + altKey: boolean; + shiftKey: boolean; + metaKey: boolean; + x?: number; + y?: number; + } + | { + type: 'mousemove'; + x: number; + y: number; + ctrlKey: boolean; + altKey: boolean; + shiftKey: boolean; + metaKey: boolean; + } + | { + type: 'wheel'; + x?: number; + y?: number; + deltaX: number; + deltaY: number; + ctrlKey: boolean; + altKey: boolean; + shiftKey: boolean; + metaKey: boolean; + }; + +export declare class InputHook { + constructor(callback: (event: InputEvent) => void); + + start(): void; + + stop(): void; +} + +export declare function isAvailable(): boolean; + +export declare function hasAccessibilityPermission(): boolean; + +export declare const loadError: Error | null; diff --git a/fluxer_desktop/native/macos-input-hook/index.js b/fluxer_desktop/native/macos-input-hook/index.js new file mode 100644 index 0000000..d30b502 --- /dev/null +++ b/fluxer_desktop/native/macos-input-hook/index.js @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync} = require('node:fs'); +const {join, sep} = require('node:path'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); +const MODULE_NAME = '@fluxer/macos-input-hook'; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName() { + if (process.platform !== 'darwin') { + throw new Error(`@fluxer/macos-input-hook is only supported on macOS, got ${process.platform}`); + } + switch (process.arch) { + case 'x64': + return 'macos-input-hook.darwin-x64.node'; + case 'arm64': + return 'macos-input-hook.darwin-arm64.node'; + default: + throw new Error(`Unsupported macOS architecture: ${process.arch}`); + } +} + +let binding = null; +let loadError = null; + +if (process.platform === 'darwin') { + try { + const nativeRoot = resolveNativeRoot(); + const nativePath = join(nativeRoot, nativeFileName()); + const loaded = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + probe: false, + }); + binding = loaded.binding; + loadError = loaded.loadError; + if (loadError) throw loadError; + } catch (error) { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: 'native loader threw before binding load completed', + cause: error, + }); + throw loadError; + } +} + +module.exports = { + InputHook: binding ? binding.InputHook : null, + isAvailable: binding ? binding.isAvailable : () => false, + hasAccessibilityPermission: binding ? binding.hasAccessibilityPermission : () => false, + loadError, +}; diff --git a/fluxer_desktop/native/macos-input-hook/loader-diagnostics.cjs b/fluxer_desktop/native/macos-input-hook/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/macos-input-hook/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/macos-input-hook/package.json b/fluxer_desktop/native/macos-input-hook/package.json new file mode 100644 index 0000000..956fe6d --- /dev/null +++ b/fluxer_desktop/native/macos-input-hook/package.json @@ -0,0 +1,27 @@ +{ + "name": "@fluxer/macos-input-hook", + "version": "0.0.0", + "description": "", + "private": true, + "license": "AGPL-3.0-or-later", + "os": [ + "darwin" + ], + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "macos-input-hook.darwin-x64.node", + "macos-input-hook.darwin-arm64.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "cargo test --manifest-path Cargo.toml" + } +} diff --git a/fluxer_desktop/native/macos-input-hook/src/caps_lock_hid.rs b/fluxer_desktop/native/macos-input-hook/src/caps_lock_hid.rs new file mode 100644 index 0000000..cb8f6b9 --- /dev/null +++ b/fluxer_desktop/native/macos-input-hook/src/caps_lock_hid.rs @@ -0,0 +1,158 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::ffi::c_void; +use std::ptr; + +use core_foundation::base::{CFAllocatorRef, CFRelease, TCFType, kCFAllocatorDefault}; +use core_foundation::dictionary::{CFDictionary, CFDictionaryRef}; +use core_foundation::number::CFNumber; +use core_foundation::runloop::{CFRunLoop, CFRunLoopRef, kCFRunLoopCommonModes}; +use core_foundation::string::{CFString, CFStringRef}; + +const KEYBOARD_USAGE_PAGE: u32 = 0x07; +const CAPS_LOCK_USAGE: u32 = 0x39; +const GENERIC_DESKTOP_USAGE_PAGE: i32 = 0x01; +const KEYBOARD_DEVICE_USAGE: i32 = 0x06; +const HID_OPTIONS_NONE: u32 = 0; +const KERN_SUCCESS: i32 = 0; + +type IOHIDManagerRef = *mut c_void; +type IOHIDValueRef = *mut c_void; +type IOHIDElementRef = *mut c_void; +type IOHIDValueCallback = extern "C" fn(*mut c_void, i32, *mut c_void, IOHIDValueRef); + +#[link(name = "IOKit", kind = "framework")] +unsafe extern "C" { + fn IOHIDManagerCreate(allocator: CFAllocatorRef, options: u32) -> IOHIDManagerRef; + fn IOHIDManagerSetDeviceMatching(manager: IOHIDManagerRef, matching: CFDictionaryRef); + fn IOHIDManagerSetInputValueMatching(manager: IOHIDManagerRef, matching: CFDictionaryRef); + fn IOHIDManagerRegisterInputValueCallback( + manager: IOHIDManagerRef, + callback: Option, + context: *mut c_void, + ); + fn IOHIDManagerScheduleWithRunLoop( + manager: IOHIDManagerRef, + run_loop: CFRunLoopRef, + mode: CFStringRef, + ); + fn IOHIDManagerUnscheduleFromRunLoop( + manager: IOHIDManagerRef, + run_loop: CFRunLoopRef, + mode: CFStringRef, + ); + fn IOHIDManagerOpen(manager: IOHIDManagerRef, options: u32) -> i32; + fn IOHIDManagerClose(manager: IOHIDManagerRef, options: u32) -> i32; + fn IOHIDValueGetElement(value: IOHIDValueRef) -> IOHIDElementRef; + fn IOHIDValueGetIntegerValue(value: IOHIDValueRef) -> isize; + fn IOHIDElementGetUsagePage(element: IOHIDElementRef) -> u32; + fn IOHIDElementGetUsage(element: IOHIDElementRef) -> u32; +} + +struct CapsLockContext { + emit: Box, +} + +extern "C" fn caps_lock_input_value_callback( + context: *mut c_void, + result: i32, + _sender: *mut c_void, + value: IOHIDValueRef, +) { + if result != KERN_SUCCESS || context.is_null() || value.is_null() { + return; + } + let element = unsafe { IOHIDValueGetElement(value) }; + if element.is_null() { + return; + } + let usage_page = unsafe { IOHIDElementGetUsagePage(element) }; + let usage = unsafe { IOHIDElementGetUsage(element) }; + if usage_page != KEYBOARD_USAGE_PAGE || usage != CAPS_LOCK_USAGE { + return; + } + let pressed = unsafe { IOHIDValueGetIntegerValue(value) } != 0; + let caps_context = unsafe { &*context.cast::() }; + (caps_context.emit)(pressed); +} + +fn usage_matching_dictionary(entries: &[(&'static str, i32)]) -> CFDictionary { + let pairs: Vec<(CFString, CFNumber)> = entries + .iter() + .map(|(key, value)| (CFString::from_static_string(key), CFNumber::from(*value))) + .collect(); + CFDictionary::from_CFType_pairs(&pairs) +} + +pub struct CapsLockHidListener { + manager: IOHIDManagerRef, + context: *mut CapsLockContext, + run_loop: CFRunLoop, +} + +impl CapsLockHidListener { + pub fn start(emit: Box) -> Option { + let manager = unsafe { IOHIDManagerCreate(kCFAllocatorDefault, HID_OPTIONS_NONE) }; + if manager.is_null() { + return None; + } + let device_matching = usage_matching_dictionary(&[ + ("DeviceUsagePage", GENERIC_DESKTOP_USAGE_PAGE), + ("DeviceUsage", KEYBOARD_DEVICE_USAGE), + ]); + #[allow(clippy::cast_possible_wrap)] + let value_matching = usage_matching_dictionary(&[ + ("UsagePage", KEYBOARD_USAGE_PAGE as i32), + ("Usage", CAPS_LOCK_USAGE as i32), + ]); + let context = Box::into_raw(Box::new(CapsLockContext { emit })); + let run_loop = CFRunLoop::get_current(); + unsafe { + IOHIDManagerSetDeviceMatching(manager, device_matching.as_concrete_TypeRef()); + IOHIDManagerSetInputValueMatching(manager, value_matching.as_concrete_TypeRef()); + IOHIDManagerRegisterInputValueCallback( + manager, + Some(caps_lock_input_value_callback), + context.cast(), + ); + IOHIDManagerScheduleWithRunLoop( + manager, + run_loop.as_concrete_TypeRef(), + kCFRunLoopCommonModes, + ); + let status = IOHIDManagerOpen(manager, HID_OPTIONS_NONE); + if status != KERN_SUCCESS { + IOHIDManagerUnscheduleFromRunLoop( + manager, + run_loop.as_concrete_TypeRef(), + kCFRunLoopCommonModes, + ); + IOHIDManagerRegisterInputValueCallback(manager, None, ptr::null_mut()); + CFRelease(manager.cast()); + drop(Box::from_raw(context)); + return None; + } + } + Some(Self { + manager, + context, + run_loop, + }) + } +} + +impl Drop for CapsLockHidListener { + fn drop(&mut self) { + unsafe { + IOHIDManagerUnscheduleFromRunLoop( + self.manager, + self.run_loop.as_concrete_TypeRef(), + kCFRunLoopCommonModes, + ); + IOHIDManagerRegisterInputValueCallback(self.manager, None, ptr::null_mut()); + let _ = IOHIDManagerClose(self.manager, HID_OPTIONS_NONE); + CFRelease(self.manager.cast()); + drop(Box::from_raw(self.context)); + } + } +} diff --git a/fluxer_desktop/native/macos-input-hook/src/keymap.rs b/fluxer_desktop/native/macos-input-hook/src/keymap.rs new file mode 100644 index 0000000..6ca6399 --- /dev/null +++ b/fluxer_desktop/native/macos-input-hook/src/keymap.rs @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const KEYCODE_TABLE: &[(u16, &str)] = &[ + (0x35, "Escape"), + (0x7a, "F1"), + (0x78, "F2"), + (0x63, "F3"), + (0x76, "F4"), + (0x60, "F5"), + (0x61, "F6"), + (0x62, "F7"), + (0x64, "F8"), + (0x65, "F9"), + (0x6d, "F10"), + (0x67, "F11"), + (0x6f, "F12"), + (0x69, "F13"), + (0x6b, "F14"), + (0x71, "F15"), + (0x6a, "F16"), + (0x40, "F17"), + (0x4f, "F18"), + (0x50, "F19"), + (0x5a, "F20"), + (0x32, "Backquote"), + (0x12, "1"), + (0x13, "2"), + (0x14, "3"), + (0x15, "4"), + (0x17, "5"), + (0x16, "6"), + (0x1a, "7"), + (0x1c, "8"), + (0x19, "9"), + (0x1d, "0"), + (0x1b, "Minus"), + (0x18, "Equal"), + (0x33, "Backspace"), + (0x30, "Tab"), + (0x0c, "Q"), + (0x0d, "W"), + (0x0e, "E"), + (0x0f, "R"), + (0x11, "T"), + (0x10, "Y"), + (0x20, "U"), + (0x22, "I"), + (0x1f, "O"), + (0x23, "P"), + (0x21, "BracketLeft"), + (0x1e, "BracketRight"), + (0x2a, "Backslash"), + (0x0a, "IntlBackslash"), + (0x39, "CapsLock"), + (0x00, "A"), + (0x01, "S"), + (0x02, "D"), + (0x03, "F"), + (0x05, "G"), + (0x04, "H"), + (0x26, "J"), + (0x28, "K"), + (0x25, "L"), + (0x29, "Semicolon"), + (0x27, "Quote"), + (0x24, "Enter"), + (0x38, "ShiftLeft"), + (0x06, "Z"), + (0x07, "X"), + (0x08, "C"), + (0x09, "V"), + (0x0b, "B"), + (0x2d, "N"), + (0x2e, "M"), + (0x2b, "Comma"), + (0x2f, "Period"), + (0x2c, "Slash"), + (0x3c, "ShiftRight"), + (0x3b, "ControlLeft"), + (0x37, "MetaLeft"), + (0x3a, "AltLeft"), + (0x31, "Space"), + (0x3d, "AltRight"), + (0x36, "MetaRight"), + (0x3e, "ControlRight"), + (0x41, "NumpadDecimal"), + (0x43, "NumpadMultiply"), + (0x45, "NumpadAdd"), + (0x47, "NumLock"), + (0x4b, "NumpadDivide"), + (0x4c, "NumpadEnter"), + (0x4e, "NumpadSubtract"), + (0x51, "NumpadEqual"), + (0x52, "Numpad0"), + (0x53, "Numpad1"), + (0x54, "Numpad2"), + (0x55, "Numpad3"), + (0x56, "Numpad4"), + (0x57, "Numpad5"), + (0x58, "Numpad6"), + (0x59, "Numpad7"), + (0x5b, "Numpad8"), + (0x5c, "Numpad9"), + (0x5d, "IntlYen"), + (0x5e, "IntlRo"), + (0x5f, "NumpadComma"), + (0x66, "Lang2"), + (0x68, "Lang1"), + (0x7b, "ArrowLeft"), + (0x7e, "ArrowUp"), + (0x7c, "ArrowRight"), + (0x7d, "ArrowDown"), + (0x72, "Insert"), + (0x75, "Delete"), + (0x73, "Home"), + (0x77, "End"), + (0x74, "PageUp"), + (0x79, "PageDown"), +]; + +pub fn keycode_to_name(keycode: u16) -> Option<&'static str> { + KEYCODE_TABLE + .iter() + .find(|(code, _)| *code == keycode) + .map(|(_, name)| *name) +} + +pub fn keycode_name_or_fallback(keycode: u16) -> String { + match keycode_to_name(keycode) { + Some(name) => name.to_owned(), + None => format!("Key{keycode}"), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn letters_use_apple_hid_positions() { + assert_eq!(Some("A"), keycode_to_name(0x00)); + assert_eq!(Some("Z"), keycode_to_name(0x06)); + assert_eq!(Some("M"), keycode_to_name(0x2e)); + } + + #[test] + fn caps_lock_maps_to_dom_style_name() { + assert_eq!(Some("CapsLock"), keycode_to_name(0x39)); + assert_eq!("CapsLock", keycode_name_or_fallback(0x39)); + } + + #[test] + fn modifiers_map_to_side_distinguished_names() { + assert_eq!(Some("ShiftLeft"), keycode_to_name(0x38)); + assert_eq!(Some("ShiftRight"), keycode_to_name(0x3c)); + assert_eq!(Some("MetaLeft"), keycode_to_name(0x37)); + assert_eq!(Some("AltLeft"), keycode_to_name(0x3a)); + } + + #[test] + fn function_keys_cover_f1_through_f20() { + assert_eq!(Some("F1"), keycode_to_name(0x7a)); + assert_eq!(Some("F12"), keycode_to_name(0x6f)); + assert_eq!(Some("F13"), keycode_to_name(0x69)); + assert_eq!(Some("F20"), keycode_to_name(0x5a)); + } + + #[test] + fn numpad_and_international_keys_map() { + assert_eq!(Some("Numpad0"), keycode_to_name(0x52)); + assert_eq!(Some("NumpadEnter"), keycode_to_name(0x4c)); + assert_eq!(Some("NumpadEqual"), keycode_to_name(0x51)); + assert_eq!(Some("NumLock"), keycode_to_name(0x47)); + assert_eq!(Some("IntlYen"), keycode_to_name(0x5d)); + assert_eq!(Some("Lang1"), keycode_to_name(0x68)); + } + + #[test] + fn arrows_and_editing_keys_map() { + assert_eq!(Some("ArrowLeft"), keycode_to_name(0x7b)); + assert_eq!(Some("ArrowDown"), keycode_to_name(0x7d)); + assert_eq!(Some("Delete"), keycode_to_name(0x75)); + } + + #[test] + fn unknown_keycode_falls_back_to_key_n() { + assert_eq!(None, keycode_to_name(0xfff)); + assert_eq!("Key291", keycode_name_or_fallback(0x123)); + } +} diff --git a/fluxer_desktop/native/macos-input-hook/src/lib.rs b/fluxer_desktop/native/macos-input-hook/src/lib.rs new file mode 100644 index 0000000..d263ae3 --- /dev/null +++ b/fluxer_desktop/native/macos-input-hook/src/lib.rs @@ -0,0 +1,46 @@ +#![cfg_attr(not(target_os = "macos"), allow(dead_code))] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +mod keymap; +mod modifiers; +mod mouse; + +use napi_derive::napi; + +#[cfg(target_os = "macos")] +mod caps_lock_hid; + +#[cfg(target_os = "macos")] +mod platform; + +#[cfg(target_os = "macos")] +pub use platform::InputHook; + +#[napi(js_name = "isAvailable")] +pub fn is_available() -> bool { + cfg!(target_os = "macos") +} + +#[napi(js_name = "hasAccessibilityPermission")] +pub fn has_accessibility_permission() -> bool { + #[cfg(target_os = "macos")] + { + platform::has_accessibility_permission() + } + #[cfg(not(target_os = "macos"))] + { + false + } +} + +#[cfg(all(test, not(target_os = "macos")))] +mod tests { + use super::*; + + #[test] + fn stub_reports_not_available_off_macos() { + assert!(!is_available()); + assert!(!has_accessibility_permission()); + } +} diff --git a/fluxer_desktop/native/macos-input-hook/src/modifiers.rs b/fluxer_desktop/native/macos-input-hook/src/modifiers.rs new file mode 100644 index 0000000..8b102e9 --- /dev/null +++ b/fluxer_desktop/native/macos-input-hook/src/modifiers.rs @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub const SHIFT_MASK: u64 = 1 << 17; +pub const CONTROL_MASK: u64 = 1 << 18; +pub const ALTERNATE_MASK: u64 = 1 << 19; +pub const COMMAND_MASK: u64 = 1 << 20; + +const LEFT_SHIFT_KEYCODE: u16 = 0x38; +const RIGHT_SHIFT_KEYCODE: u16 = 0x3c; +const LEFT_CONTROL_KEYCODE: u16 = 0x3b; +const RIGHT_CONTROL_KEYCODE: u16 = 0x3e; +const LEFT_OPTION_KEYCODE: u16 = 0x3a; +const RIGHT_OPTION_KEYCODE: u16 = 0x3d; +const LEFT_COMMAND_KEYCODE: u16 = 0x37; +const RIGHT_COMMAND_KEYCODE: u16 = 0x36; + +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +pub struct Modifiers { + pub ctrl: bool, + pub alt: bool, + pub shift: bool, + pub meta: bool, +} + +pub fn from_flags(flags: u64) -> Modifiers { + Modifiers { + ctrl: (flags & CONTROL_MASK) != 0, + alt: (flags & ALTERNATE_MASK) != 0, + shift: (flags & SHIFT_MASK) != 0, + meta: (flags & COMMAND_MASK) != 0, + } +} + +pub fn modifier_key_down_from_flags(keycode: u16, flags: u64) -> Option { + match keycode { + LEFT_SHIFT_KEYCODE | RIGHT_SHIFT_KEYCODE => Some((flags & SHIFT_MASK) != 0), + LEFT_CONTROL_KEYCODE | RIGHT_CONTROL_KEYCODE => Some((flags & CONTROL_MASK) != 0), + LEFT_OPTION_KEYCODE | RIGHT_OPTION_KEYCODE => Some((flags & ALTERNATE_MASK) != 0), + LEFT_COMMAND_KEYCODE | RIGHT_COMMAND_KEYCODE => Some((flags & COMMAND_MASK) != 0), + _ => None, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn no_flags_all_false() { + let m = from_flags(0); + assert!(!m.ctrl && !m.alt && !m.shift && !m.meta); + } + + #[test] + fn command_alone_sets_only_meta() { + let m = from_flags(COMMAND_MASK); + assert!(m.meta); + assert!(!m.ctrl && !m.alt && !m.shift); + } + + #[test] + fn option_alone_sets_only_alt() { + let m = from_flags(ALTERNATE_MASK); + assert!(m.alt); + assert!(!m.ctrl && !m.meta && !m.shift); + } + + #[test] + fn cmd_shift_combo() { + let m = from_flags(COMMAND_MASK | SHIFT_MASK); + assert!(m.meta && m.shift); + assert!(!m.ctrl && !m.alt); + } + + #[test] + fn all_four_modifiers_together() { + let m = from_flags(SHIFT_MASK | CONTROL_MASK | ALTERNATE_MASK | COMMAND_MASK); + assert!(m.ctrl && m.alt && m.shift && m.meta); + } + + #[test] + fn unrelated_high_bits_ignored() { + let m = from_flags(0xff << 32); + assert!(!m.ctrl && !m.alt && !m.shift && !m.meta); + } + + #[test] + fn modifier_key_down_uses_matching_aggregate_flag() { + assert_eq!( + modifier_key_down_from_flags(LEFT_SHIFT_KEYCODE, SHIFT_MASK), + Some(true) + ); + assert_eq!( + modifier_key_down_from_flags(RIGHT_SHIFT_KEYCODE, 0), + Some(false) + ); + assert_eq!( + modifier_key_down_from_flags(LEFT_COMMAND_KEYCODE, COMMAND_MASK), + Some(true) + ); + assert_eq!(modifier_key_down_from_flags(0x39, 0), None); + } +} diff --git a/fluxer_desktop/native/macos-input-hook/src/mouse.rs b/fluxer_desktop/native/macos-input-hook/src/mouse.rs new file mode 100644 index 0000000..6d0538d --- /dev/null +++ b/fluxer_desktop/native/macos-input-hook/src/mouse.rs @@ -0,0 +1,129 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[repr(u32)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum CgEventType { + LeftMouseDown = 1, + LeftMouseUp = 2, + RightMouseDown = 3, + RightMouseUp = 4, + MouseMoved = 5, + LeftMouseDragged = 6, + RightMouseDragged = 7, + KeyDown = 10, + KeyUp = 11, + FlagsChanged = 12, + ScrollWheel = 22, + OtherMouseDown = 25, + OtherMouseUp = 26, + OtherMouseDragged = 27, +} + +impl CgEventType { + pub fn from_u32(value: u32) -> Option { + Some(match value { + 1 => Self::LeftMouseDown, + 2 => Self::LeftMouseUp, + 3 => Self::RightMouseDown, + 4 => Self::RightMouseUp, + 5 => Self::MouseMoved, + 6 => Self::LeftMouseDragged, + 7 => Self::RightMouseDragged, + 10 => Self::KeyDown, + 11 => Self::KeyUp, + 12 => Self::FlagsChanged, + 22 => Self::ScrollWheel, + 25 => Self::OtherMouseDown, + 26 => Self::OtherMouseUp, + 27 => Self::OtherMouseDragged, + _ => return None, + }) + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Classification { + Button(u8), + Ignored, +} + +pub fn classify(event_type: CgEventType, other_button: u32) -> Classification { + match event_type { + CgEventType::LeftMouseDown | CgEventType::LeftMouseUp => Classification::Button(0), + CgEventType::RightMouseDown | CgEventType::RightMouseUp => Classification::Button(2), + CgEventType::OtherMouseDown | CgEventType::OtherMouseUp => match other_button { + 2 => Classification::Button(1), + 3 => Classification::Button(3), + 4 => Classification::Button(4), + _ => Classification::Ignored, + }, + _ => Classification::Ignored, + } +} + +pub fn is_down(event_type: CgEventType) -> bool { + matches!( + event_type, + CgEventType::LeftMouseDown | CgEventType::RightMouseDown | CgEventType::OtherMouseDown + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn left_and_right_buttons_map_to_0_and_2() { + assert_eq!( + Classification::Button(0), + classify(CgEventType::LeftMouseDown, 0) + ); + assert_eq!( + Classification::Button(2), + classify(CgEventType::RightMouseUp, 0) + ); + } + + #[test] + fn middle_button_other_2_maps_to_1() { + assert_eq!( + Classification::Button(1), + classify(CgEventType::OtherMouseDown, 2) + ); + } + + #[test] + fn back_forward_other_3_4_map_to_3_and_4() { + assert_eq!( + Classification::Button(3), + classify(CgEventType::OtherMouseDown, 3) + ); + assert_eq!( + Classification::Button(4), + classify(CgEventType::OtherMouseUp, 4) + ); + } + + #[test] + fn unknown_other_button_is_ignored() { + assert_eq!( + Classification::Ignored, + classify(CgEventType::OtherMouseDown, 99) + ); + } + + #[test] + fn is_down_distinguishes_press_from_release() { + assert!(is_down(CgEventType::LeftMouseDown)); + assert!(!is_down(CgEventType::LeftMouseUp)); + assert!(is_down(CgEventType::OtherMouseDown)); + assert!(!is_down(CgEventType::MouseMoved)); + } + + #[test] + fn from_u32_maps_known_event_types() { + assert_eq!(Some(CgEventType::LeftMouseDown), CgEventType::from_u32(1)); + assert_eq!(Some(CgEventType::ScrollWheel), CgEventType::from_u32(22)); + assert_eq!(None, CgEventType::from_u32(999)); + } +} diff --git a/fluxer_desktop/native/macos-input-hook/src/platform.rs b/fluxer_desktop/native/macos-input-hook/src/platform.rs new file mode 100644 index 0000000..cc10684 --- /dev/null +++ b/fluxer_desktop/native/macos-input-hook/src/platform.rs @@ -0,0 +1,504 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::ffi::c_void; +use std::ptr; +use std::sync::{ + Arc, Mutex, + atomic::{AtomicBool, AtomicPtr, AtomicU64, Ordering}, + mpsc::{Receiver, Sender, channel}, +}; +use std::thread::{self, JoinHandle}; + +use core_foundation::base::TCFType; +use core_foundation::mach_port::CFMachPortRef; +use core_foundation::runloop::{CFRunLoop, kCFRunLoopCommonModes}; +use core_graphics::event::{ + CGEvent, CGEventTap, CGEventTapLocation, CGEventTapOptions, CGEventTapPlacement, + CGEventTapProxy, CGEventType, CallbackResult, EventField, +}; +use napi::bindgen_prelude::{Env, Error, Function, Object, Result, Status, ToNapiValue}; +use napi::sys; +use napi::threadsafe_function::{ + ThreadsafeFunction, ThreadsafeFunctionCallMode, UnknownReturnValue, +}; +use napi_derive::napi; + +use crate::caps_lock_hid::CapsLockHidListener; +use crate::keymap; +use crate::modifiers::{self, Modifiers}; +use crate::mouse::{self, CgEventType, Classification}; + +#[link(name = "CoreGraphics", kind = "framework")] +unsafe extern "C" { + fn CGEventTapEnable(tap: CFMachPortRef, enable: bool); +} + +#[derive(Debug)] +enum InputEventKind { + KeyDown, + KeyUp, + MouseDown, + MouseUp, + MouseMove, + Wheel, +} + +#[derive(Debug)] +pub struct InputEventPayload { + kind: InputEventKind, + mods: Modifiers, + keycode: u32, + key_name: String, + button: u8, + delta_x: i32, + delta_y: i32, + x: i32, + y: i32, + has_xy: bool, +} + +impl ToNapiValue for InputEventPayload { + unsafe fn to_napi_value(raw_env: sys::napi_env, value: Self) -> Result { + let env = Env::from_raw(raw_env); + let mut obj = Object::new(&env)?; + let kind_str = match value.kind { + InputEventKind::KeyDown => "keydown", + InputEventKind::KeyUp => "keyup", + InputEventKind::MouseDown => "mousedown", + InputEventKind::MouseUp => "mouseup", + InputEventKind::MouseMove => "mousemove", + InputEventKind::Wheel => "wheel", + }; + obj.set("type", kind_str)?; + obj.set("ctrlKey", value.mods.ctrl)?; + obj.set("altKey", value.mods.alt)?; + obj.set("shiftKey", value.mods.shift)?; + obj.set("metaKey", value.mods.meta)?; + + match value.kind { + InputEventKind::KeyDown | InputEventKind::KeyUp => { + obj.set("keycode", value.keycode)?; + obj.set("keyName", value.key_name)?; + } + InputEventKind::MouseDown | InputEventKind::MouseUp => { + obj.set("button", u32::from(value.button))?; + if value.has_xy { + obj.set("x", value.x)?; + obj.set("y", value.y)?; + } + } + InputEventKind::MouseMove => { + obj.set("x", value.x)?; + obj.set("y", value.y)?; + } + InputEventKind::Wheel => { + obj.set("deltaX", value.delta_x)?; + obj.set("deltaY", value.delta_y)?; + if value.has_xy { + obj.set("x", value.x)?; + obj.set("y", value.y)?; + } + } + } + unsafe { as ToNapiValue>::to_napi_value(raw_env, obj) } + } +} + +type EventTsfn = Arc< + ThreadsafeFunction< + InputEventPayload, + UnknownReturnValue, + InputEventPayload, + Status, + false, + true, + EVENT_QUEUE_LIMIT, + >, +>; + +const EVENT_QUEUE_LIMIT: usize = 1024; + +struct WorkerState { + run_loop: CFRunLoop, + join: JoinHandle<()>, +} + +#[napi] +pub struct InputHook { + tsfn: EventTsfn, + state: Mutex>, +} + +#[napi] +impl InputHook { + #[napi(constructor)] + pub fn new(callback: Function) -> Result { + let tsfn = Arc::new( + callback + .build_threadsafe_function::() + .weak::() + .callee_handled::() + .max_queue_size::() + .build() + .map_err(|err| { + Error::new( + Status::GenericFailure, + format!("failed to create input-hook callback: {}", err.reason), + ) + })?, + ); + Ok(Self { + tsfn, + state: Mutex::new(None), + }) + } + + #[napi] + pub fn start(&self) -> Result<()> { + let mut guard = self + .state + .lock() + .map_err(|_| Error::new(Status::GenericFailure, "input hook state mutex poisoned"))?; + if guard.is_some() { + return Ok(()); + } + + let (loop_tx, loop_rx): (Sender>, Receiver<_>) = + channel(); + let tsfn = self.tsfn.clone(); + let join = thread::Builder::new() + .name("macos-input-hook".to_owned()) + .spawn(move || worker_main(tsfn, loop_tx)) + .map_err(|err| { + Error::new( + Status::GenericFailure, + format!("InputHook.start failed: thread spawn failed: {err}"), + ) + })?; + + let run_loop = match loop_rx.recv() { + Ok(Ok(run_loop)) => run_loop, + Ok(Err(err)) => { + let _ = join.join(); + return Err(Error::new( + Status::GenericFailure, + format!("InputHook.start failed: {err}"), + )); + } + Err(_) => { + let _ = join.join(); + return Err(Error::new( + Status::GenericFailure, + "InputHook.start failed: worker exited before signalling", + )); + } + }; + + *guard = Some(WorkerState { run_loop, join }); + Ok(()) + } + + #[napi] + pub fn stop(&self) -> Result<()> { + let state = { + let mut guard = self.state.lock().map_err(|_| { + Error::new(Status::GenericFailure, "input hook state mutex poisoned") + })?; + guard.take() + }; + if let Some(WorkerState { run_loop, join }) = state { + run_loop.stop(); + let _ = join.join(); + } + Ok(()) + } +} + +impl Drop for InputHook { + fn drop(&mut self) { + if let Ok(mut guard) = self.state.lock() + && let Some(WorkerState { run_loop, join }) = guard.take() + { + run_loop.stop(); + let _ = join.join(); + } + } +} + +const EVENTS_OF_INTEREST: &[CGEventType] = &[ + CGEventType::KeyDown, + CGEventType::KeyUp, + CGEventType::FlagsChanged, + CGEventType::LeftMouseDown, + CGEventType::LeftMouseUp, + CGEventType::RightMouseDown, + CGEventType::RightMouseUp, + CGEventType::OtherMouseDown, + CGEventType::OtherMouseUp, + CGEventType::MouseMoved, + CGEventType::LeftMouseDragged, + CGEventType::RightMouseDragged, + CGEventType::OtherMouseDragged, + CGEventType::ScrollWheel, +]; + +const CAPS_LOCK_KEYCODE: u16 = 0x39; + +fn worker_main(tsfn: EventTsfn, loop_tx: Sender>) { + let tap_ref = Arc::new(AtomicPtr::new(ptr::null_mut())); + let last_flags = Arc::new(AtomicU64::new(0)); + let caps_lock_via_hid = Arc::new(AtomicBool::new(false)); + let dispatcher = EventDispatcher { + tsfn: tsfn.clone(), + tap_ref: tap_ref.clone(), + last_flags: last_flags.clone(), + caps_lock_via_hid: caps_lock_via_hid.clone(), + }; + let tap_result = CGEventTap::new( + CGEventTapLocation::Session, + CGEventTapPlacement::HeadInsertEventTap, + CGEventTapOptions::ListenOnly, + EVENTS_OF_INTEREST.to_vec(), + move |proxy, event_type, event| dispatcher.dispatch(proxy, event_type, event), + ); + + let tap = match tap_result { + Ok(tap) => tap, + Err(()) => { + let _ = loop_tx.send(Err( + "CGEventTapCreate returned NULL (accessibility permission denied?)".to_owned(), + )); + return; + } + }; + + let loop_source = match tap.mach_port().create_runloop_source(0) { + Ok(source) => source, + Err(()) => { + let _ = loop_tx.send(Err("CFMachPortCreateRunLoopSource returned NULL".to_owned())); + return; + } + }; + + let run_loop = CFRunLoop::get_current(); + unsafe { run_loop.add_source(&loop_source, kCFRunLoopCommonModes) }; + tap_ref.store( + tap.mach_port().as_concrete_TypeRef().cast::(), + Ordering::Release, + ); + tap.enable(); + + let caps_tsfn = tsfn.clone(); + let caps_flags = last_flags.clone(); + let caps_listener = CapsLockHidListener::start(Box::new(move |pressed| { + let payload = caps_lock_payload(pressed, caps_flags.load(Ordering::Relaxed)); + let _ = caps_tsfn.call(payload, ThreadsafeFunctionCallMode::NonBlocking); + })); + caps_lock_via_hid.store(caps_listener.is_some(), Ordering::Release); + + if loop_tx.send(Ok(run_loop)).is_err() { + tap_ref.store(ptr::null_mut(), Ordering::Release); + return; + } + + CFRunLoop::run_current(); + tap_ref.store(ptr::null_mut(), Ordering::Release); + drop(caps_listener); + drop(tap); + drop(loop_source); +} + +fn caps_lock_payload(pressed: bool, flags: u64) -> InputEventPayload { + InputEventPayload { + kind: if pressed { + InputEventKind::KeyDown + } else { + InputEventKind::KeyUp + }, + mods: modifiers::from_flags(flags), + keycode: u32::from(CAPS_LOCK_KEYCODE), + key_name: keymap::keycode_name_or_fallback(CAPS_LOCK_KEYCODE), + button: 0, + delta_x: 0, + delta_y: 0, + x: 0, + y: 0, + has_xy: false, + } +} + +struct EventDispatcher { + tsfn: EventTsfn, + tap_ref: Arc>, + last_flags: Arc, + caps_lock_via_hid: Arc, +} + +impl EventDispatcher { + fn dispatch( + &self, + _proxy: CGEventTapProxy, + event_type: CGEventType, + event: &CGEvent, + ) -> CallbackResult { + match event_type { + CGEventType::TapDisabledByTimeout | CGEventType::TapDisabledByUserInput => { + self.reenable_tap(); + return CallbackResult::Keep; + } + _ => {} + } + + let flags = event.get_flags(); + self.last_flags.store(flags.bits(), Ordering::Relaxed); + let mods = modifiers::from_flags(flags.bits()); + let Some(cls) = CgEventType::from_u32(event_type as u32) else { + return CallbackResult::Keep; + }; + + match cls { + CgEventType::KeyDown | CgEventType::KeyUp => { + let raw = event.get_integer_value_field(EventField::KEYBOARD_EVENT_KEYCODE); + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + let keycode = (raw as u64) as u16; + let kind = if matches!(cls, CgEventType::KeyDown) { + InputEventKind::KeyDown + } else { + InputEventKind::KeyUp + }; + self.send_key(kind, keycode, mods); + } + CgEventType::FlagsChanged => { + let raw = event.get_integer_value_field(EventField::KEYBOARD_EVENT_KEYCODE); + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + let keycode = (raw as u64) as u16; + if keycode == CAPS_LOCK_KEYCODE { + if self.caps_lock_via_hid.load(Ordering::Acquire) { + return CallbackResult::Keep; + } + self.send_key(InputEventKind::KeyDown, keycode, mods); + self.send_key(InputEventKind::KeyUp, keycode, mods); + return CallbackResult::Keep; + } + let Some(is_down) = modifiers::modifier_key_down_from_flags(keycode, flags.bits()) + else { + return CallbackResult::Keep; + }; + let kind = if is_down { + InputEventKind::KeyDown + } else { + InputEventKind::KeyUp + }; + self.send_key(kind, keycode, mods); + } + CgEventType::LeftMouseDown + | CgEventType::LeftMouseUp + | CgEventType::RightMouseDown + | CgEventType::RightMouseUp + | CgEventType::OtherMouseDown + | CgEventType::OtherMouseUp => { + let raw_button = + event.get_integer_value_field(EventField::MOUSE_EVENT_BUTTON_NUMBER); + #[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)] + let other_btn = (raw_button as u64) as u32; + if let Classification::Button(b) = mouse::classify(cls, other_btn) { + let point = event.location(); + #[allow(clippy::cast_possible_truncation)] + let payload = InputEventPayload { + kind: if mouse::is_down(cls) { + InputEventKind::MouseDown + } else { + InputEventKind::MouseUp + }, + mods, + keycode: 0, + key_name: String::new(), + button: b, + delta_x: 0, + delta_y: 0, + x: point.x as i32, + y: point.y as i32, + has_xy: true, + }; + self.send(payload); + } + } + CgEventType::MouseMoved + | CgEventType::LeftMouseDragged + | CgEventType::RightMouseDragged + | CgEventType::OtherMouseDragged => { + let point = event.location(); + #[allow(clippy::cast_possible_truncation)] + let payload = InputEventPayload { + kind: InputEventKind::MouseMove, + mods, + keycode: 0, + key_name: String::new(), + button: 0, + delta_x: 0, + delta_y: 0, + x: point.x as i32, + y: point.y as i32, + has_xy: true, + }; + self.send(payload); + } + CgEventType::ScrollWheel => { + let dy = event.get_integer_value_field(EventField::SCROLL_WHEEL_EVENT_DELTA_AXIS_1) + as i32; + let dx = event.get_integer_value_field(EventField::SCROLL_WHEEL_EVENT_DELTA_AXIS_2) + as i32; + let point = event.location(); + #[allow(clippy::cast_possible_truncation)] + let payload = InputEventPayload { + kind: InputEventKind::Wheel, + mods, + keycode: 0, + key_name: String::new(), + button: 0, + delta_x: dx, + delta_y: dy, + x: point.x as i32, + y: point.y as i32, + has_xy: true, + }; + self.send(payload); + } + } + + CallbackResult::Keep + } + + fn send_key(&self, kind: InputEventKind, keycode: u16, mods: Modifiers) { + let payload = InputEventPayload { + kind, + mods, + keycode: u32::from(keycode), + key_name: keymap::keycode_name_or_fallback(keycode), + button: 0, + delta_x: 0, + delta_y: 0, + x: 0, + y: 0, + has_xy: false, + }; + self.send(payload); + } + + fn send(&self, payload: InputEventPayload) { + let _ = self + .tsfn + .call(payload, ThreadsafeFunctionCallMode::NonBlocking); + } + + fn reenable_tap(&self) { + let tap: CFMachPortRef = self.tap_ref.load(Ordering::Acquire).cast(); + if !tap.is_null() { + unsafe { CGEventTapEnable(tap, true) }; + } + } +} + +pub fn has_accessibility_permission() -> bool { + objc2_core_graphics::CGPreflightListenEventAccess() +} diff --git a/fluxer_desktop/native/nv12-gpu-pack/Cargo.lock b/fluxer_desktop/native/nv12-gpu-pack/Cargo.lock new file mode 100644 index 0000000..3ccf104 --- /dev/null +++ b/fluxer_desktop/native/nv12-gpu-pack/Cargo.lock @@ -0,0 +1,1581 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "bit-set" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ddef2995421ab6a5c779542c81ee77c115206f4ad9d5a8e05f4ff49716a3dd" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "codespan-reporting" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" +dependencies = [ + "serde", + "termcolor", + "unicode-width", +] + +[[package]] +name = "criterion" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3" +dependencies = [ + "alloca", + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "num-traits", + "oorandom", + "page_size", + "plotters", + "rayon", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags", + "objc2", +] + +[[package]] +name = "dlib" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" +dependencies = [ + "libloading", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fluxer_gpu_rebuild" +version = "0.0.0" +dependencies = [ + "parking_lot", + "wgpu", +] + +[[package]] +name = "fluxer_nv12_gpu_pack" +version = "0.0.0" +dependencies = [ + "bytemuck", + "criterion", + "fluxer_gpu_rebuild", + "parking_lot", + "pollster", + "wgpu", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glow" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29038e1c483364cc6bb3cf78feee1816002e127c331a1eec55a4d202b9e1adb5" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gpu-allocator" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" +dependencies = [ + "ash", + "hashbrown 0.16.1", + "log", + "presser", + "thiserror", + "windows", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" +dependencies = [ + "bitflags", + "gpu-descriptor-types", + "hashbrown 0.15.5", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "num-traits", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "js-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading", + "pkg-config", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "naga" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd91265cc2454558f659b3b4b9640f0ddb8cc6521277f166b8a8c181c898079" +dependencies = [ + "arrayvec", + "bit-set", + "bitflags", + "cfg-if", + "cfg_aliases", + "codespan-reporting", + "half", + "hashbrown 0.16.1", + "hexf-parse", + "indexmap", + "libm", + "log", + "num-traits", + "once_cell", + "rustc-hash", + "spirv", + "thiserror", + "unicode-ident", +] + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys 0.3.1", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" +dependencies = [ + "bitflags", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "ordered-float" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e" +dependencies = [ + "num-traits", +] + +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "pollster" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "range-alloc" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca45419789ae5a7899559e9512e58ca889e41f04f1f2445e9f4b290ceccd1d08" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "raw-window-metal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135" +dependencies = [ + "objc2", + "objc2-core-foundation", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "slotmap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "spirv" +version = "0.4.0+sdk-1.4.341.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9571ea910ebd84c86af4b3ed27f9dbdc6ad06f17c5f96146b2b671e2976744f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54568702fabf5d4849ce2b90fadfa64168a097eaf4b351ce9df8b687a0086aaf" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wayland-sys" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0871acf327f283dc6da28a1696cdc64fb355ba9f935d052021fa77f35cce69" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wgpu" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb3feacc458f7bee8bc1737149b42b6c731aa461039a4264a67bb6681646b250" +dependencies = [ + "arrayvec", + "bitflags", + "bytemuck", + "cfg-if", + "cfg_aliases", + "document-features", + "hashbrown 0.16.1", + "js-sys", + "log", + "naga", + "parking_lot", + "portable-atomic", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02da3ad1b568337f25513b317870960ef87073ea0945502e44b864b67a8c77b7" +dependencies = [ + "arrayvec", + "bit-set", + "bit-vec", + "bitflags", + "bytemuck", + "cfg_aliases", + "document-features", + "hashbrown 0.16.1", + "indexmap", + "log", + "naga", + "once_cell", + "parking_lot", + "portable-atomic", + "profiling", + "raw-window-handle", + "rustc-hash", + "smallvec", + "thiserror", + "wgpu-core-deps-apple", + "wgpu-core-deps-emscripten", + "wgpu-core-deps-windows-linux-android", + "wgpu-hal", + "wgpu-naga-bridge", + "wgpu-types", +] + +[[package]] +name = "wgpu-core-deps-apple" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62e51b5447e144b3dbba4feb01f80f4fa21696fa0cd99afb2c3df1affd6fdb28" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-emscripten" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3487cd6293a963bc5c0c0396f6a2192043c50003c07f4efdccbad3d90ec9d819" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-windows-linux-android" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb01076d0aa08b0ba9bd741e178b5cc440f5abe99d9581323a4c8b5d1a1916" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-hal" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8e1a9e7a8512f276f7c62e018c7fa8d60954303fed2e5750114332049193f" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set", + "bitflags", + "block2", + "bytemuck", + "cfg-if", + "cfg_aliases", + "glow", + "glutin_wgl_sys", + "gpu-allocator", + "gpu-descriptor", + "hashbrown 0.16.1", + "js-sys", + "khronos-egl", + "libc", + "libloading", + "log", + "naga", + "ndk-sys", + "objc2", + "objc2-core-foundation", + "objc2-foundation", + "objc2-metal", + "objc2-quartz-core", + "once_cell", + "ordered-float", + "parking_lot", + "portable-atomic", + "portable-atomic-util", + "profiling", + "range-alloc", + "raw-window-handle", + "raw-window-metal", + "renderdoc-sys", + "smallvec", + "thiserror", + "wasm-bindgen", + "wayland-sys", + "web-sys", + "wgpu-naga-bridge", + "wgpu-types", + "windows", + "windows-core", + "windows-result", +] + +[[package]] +name = "wgpu-naga-bridge" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c654c483f058800972c3645e95388a7eca31bf9fe1933bc20e036588a0be02" +dependencies = [ + "naga", + "wgpu-types", +] + +[[package]] +name = "wgpu-types" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9bcc31518a0e9735aefebedb5f7a9ef3ed1c42549c9f4c882fa9060ceaac639" +dependencies = [ + "bitflags", + "bytemuck", + "js-sys", + "log", + "raw-window-handle", + "web-sys", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "xml-rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" + +[[package]] +name = "zerocopy" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/fluxer_desktop/native/nv12-gpu-pack/Cargo.toml b/fluxer_desktop/native/nv12-gpu-pack/Cargo.toml new file mode 100644 index 0000000..37858b5 --- /dev/null +++ b/fluxer_desktop/native/nv12-gpu-pack/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "fluxer_nv12_gpu_pack" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["rlib"] + +[dependencies] +wgpu = "29" +bytemuck = {version = "1.25", features = ["derive"]} +pollster = "0.4" +parking_lot = "0.12" +fluxer_gpu_rebuild = {path = "../gpu-rebuild", features = ["wgpu"]} + +[dev-dependencies] +criterion = "0.8" + +[[bench]] +name = "pack" +harness = false diff --git a/fluxer_desktop/native/nv12-gpu-pack/benches/baseline.json b/fluxer_desktop/native/nv12-gpu-pack/benches/baseline.json new file mode 100644 index 0000000..6976e47 --- /dev/null +++ b/fluxer_desktop/native/nv12-gpu-pack/benches/baseline.json @@ -0,0 +1,25 @@ +{ + "measured_at": "85e057a273fd", + "host": "darwin-arm64-apple-silicon", + "regression_budget_percent": 5.0, + "criterion_args": { + "warm_up_time_sec": 2, + "measurement_time_sec": 5 + }, + "benches": { + "nv12_pack_1080p_cache_hit": { + "median_ns": 1517400.0, + "low_ns": 1492800.0, + "high_ns": 1542000.0, + "budget_percent_override": 20.0, + "note": "Requires a wgpu adapter (Metal on macOS). Bench function returns early without an adapter. GPU jobs are noisy; cross-run sigma 10-15% on a loaded host." + }, + "nv12_pack_1080p_cache_miss": { + "median_ns": 3081300.0, + "low_ns": 3026300.0, + "high_ns": 3139400.0, + "budget_percent_override": 20.0, + "note": "Wgpu adapter required; same caveat as cache_hit." + } + } +} diff --git a/fluxer_desktop/native/nv12-gpu-pack/benches/pack.rs b/fluxer_desktop/native/nv12-gpu-pack/benches/pack.rs new file mode 100644 index 0000000..ec7340f --- /dev/null +++ b/fluxer_desktop/native/nv12-gpu-pack/benches/pack.rs @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use criterion::{Criterion, criterion_group, criterion_main}; +use fluxer_nv12_gpu_pack::{Nv12Packer, PackJob, try_acquire_device}; + +struct BenchFixture { + device: wgpu::Device, + queue: wgpu::Queue, + view: wgpu::TextureView, + y_buf: wgpu::Buffer, + uv_buf: wgpu::Buffer, + width: u32, + height: u32, +} + +fn make_fixture(width: u32, height: u32) -> Option { + let (device, queue, _instance) = try_acquire_device()?; + let pixels = vec![128u8; (width * height * 4) as usize]; + let texture = device.create_texture(&wgpu::TextureDescriptor { + label: Some("nv12_gpu_pack.bench_source"), + size: wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8Unorm, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + view_formats: &[], + }); + queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture: &texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + &pixels, + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(width * 4), + rows_per_image: Some(height), + }, + wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + ); + let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + let y_size = Nv12Packer::y_plane_size(width, height); + let uv_size = Nv12Packer::uv_plane_size(width, height); + let y_buf = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("nv12_gpu_pack.bench_y"), + size: y_size, + usage: wgpu::BufferUsages::STORAGE + | wgpu::BufferUsages::COPY_SRC + | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + let uv_buf = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("nv12_gpu_pack.bench_uv"), + size: uv_size, + usage: wgpu::BufferUsages::STORAGE + | wgpu::BufferUsages::COPY_SRC + | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + Some(BenchFixture { + device, + queue, + view, + y_buf, + uv_buf, + width, + height, + }) +} + +fn submit_pack_with_buffers( + fixture: &BenchFixture, + packer: &Nv12Packer, + y_buf: &wgpu::Buffer, + uv_buf: &wgpu::Buffer, +) { + let mut encoder = fixture + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("nv12_gpu_pack.bench_encoder"), + }); + let result = packer.pack(PackJob { + device: &fixture.device, + queue: &fixture.queue, + encoder: &mut encoder, + source: &fixture.view, + y_out: y_buf, + uv_out: uv_buf, + dims: (fixture.width, fixture.height), + }); + assert!(result.is_ok()); + fixture.queue.submit(std::iter::once(encoder.finish())); + let _ = fixture.device.poll(wgpu::PollType::Wait { + submission_index: None, + timeout: None, + }); +} + +fn make_buffer_pair(fixture: &BenchFixture, label: &str) -> (wgpu::Buffer, wgpu::Buffer) { + let y_size = Nv12Packer::y_plane_size(fixture.width, fixture.height); + let uv_size = Nv12Packer::uv_plane_size(fixture.width, fixture.height); + let y_buf = fixture.device.create_buffer(&wgpu::BufferDescriptor { + label: Some(label), + size: y_size, + usage: wgpu::BufferUsages::STORAGE + | wgpu::BufferUsages::COPY_SRC + | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + let uv_buf = fixture.device.create_buffer(&wgpu::BufferDescriptor { + label: Some(label), + size: uv_size, + usage: wgpu::BufferUsages::STORAGE + | wgpu::BufferUsages::COPY_SRC + | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + (y_buf, uv_buf) +} + +fn bench_pack_1080p_cache_hit(c: &mut Criterion) { + let Some(fixture) = make_fixture(1920, 1080) else { + eprintln!("nv12-gpu-pack bench: no wgpu adapter, skipping"); + return; + }; + let packer = Nv12Packer::new(&fixture.device, fixture.width, fixture.height); + submit_pack_with_buffers(&fixture, &packer, &fixture.y_buf, &fixture.uv_buf); + c.bench_function("nv12_pack_1080p_cache_hit", |b| { + b.iter(|| submit_pack_with_buffers(&fixture, &packer, &fixture.y_buf, &fixture.uv_buf)); + }); +} + +fn bench_pack_1080p_cache_miss(c: &mut Criterion) { + let Some(fixture) = make_fixture(1920, 1080) else { + eprintln!("nv12-gpu-pack bench: no wgpu adapter, skipping"); + return; + }; + let packer = Nv12Packer::new(&fixture.device, fixture.width, fixture.height); + let (y_b, uv_b) = make_buffer_pair(&fixture, "nv12_gpu_pack.bench_alt"); + c.bench_function("nv12_pack_1080p_cache_miss", |b| { + b.iter(|| { + submit_pack_with_buffers(&fixture, &packer, &fixture.y_buf, &fixture.uv_buf); + submit_pack_with_buffers(&fixture, &packer, &y_b, &uv_b); + }); + }); +} + +criterion_group!( + benches, + bench_pack_1080p_cache_hit, + bench_pack_1080p_cache_miss +); +criterion_main!(benches); diff --git a/fluxer_desktop/native/nv12-gpu-pack/src/lib.rs b/fluxer_desktop/native/nv12-gpu-pack/src/lib.rs new file mode 100644 index 0000000..98a2943 --- /dev/null +++ b/fluxer_desktop/native/nv12-gpu-pack/src/lib.rs @@ -0,0 +1,1195 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use bytemuck::{Pod, Zeroable}; +use fluxer_gpu_rebuild::{GpuLossCallback, GpuRebuildError}; +use parking_lot::Mutex; +use std::num::NonZeroU64; +use wgpu::util::DeviceExt; + +const WORKGROUP_DIM: u32 = 8; +const Y_BYTES_PER_PIXEL: u64 = 1; +const UV_BYTES_PER_PIXEL_PAIR: u64 = 2; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum Nv12PackError { + DimensionsExceedMax { + requested: (u32, u32), + max: (u32, u32), + }, + DimensionsNotEven { + requested: (u32, u32), + }, + DimensionsZero, + YBufferTooSmall { + required: u64, + actual: u64, + }, + UvBufferTooSmall { + required: u64, + actual: u64, + }, + StrideNotAligned { + stride: u32, + }, + NotReady, +} + +impl std::fmt::Display for Nv12PackError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::DimensionsExceedMax { requested, max } => { + write!(f, "dimensions {requested:?} exceed packer maximum {max:?}") + } + Self::DimensionsNotEven { requested } => { + write!(f, "dimensions {requested:?} must be even for NV12") + } + Self::DimensionsZero => write!(f, "dimensions must be non-zero"), + Self::YBufferTooSmall { required, actual } => write!( + f, + "Y buffer too small: required {required} bytes, actual {actual}" + ), + Self::UvBufferTooSmall { required, actual } => write!( + f, + "UV buffer too small: required {required} bytes, actual {actual}" + ), + Self::StrideNotAligned { stride } => { + write!( + f, + "stride {stride} must be a multiple of 4 for word-packed writes" + ) + } + Self::NotReady => write!(f, "packer pipeline is released; rebuild required"), + } + } +} + +impl std::error::Error for Nv12PackError {} + +#[repr(C)] +#[derive(Clone, Copy, Pod, Zeroable)] +struct PackUniforms { + width: u32, + height: u32, + stride_y: u32, + stride_uv: u32, +} + +struct PackResources { + pipeline: wgpu::ComputePipeline, + bind_group_layout: wgpu::BindGroupLayout, + uniform_buffer: wgpu::Buffer, +} + +#[derive(Clone, Eq, PartialEq, Hash)] +struct CachedBindGroupKey { + source_view: wgpu::TextureView, + y_buf: wgpu::Buffer, + uv_buf: wgpu::Buffer, +} + +struct CachedBindGroup { + key: CachedBindGroupKey, + bind_group: wgpu::BindGroup, +} + +pub struct Nv12Packer { + resources: Option, + bind_group_cache: Mutex>, + max_width: u32, + max_height: u32, +} + +pub struct PackJob<'a> { + pub device: &'a wgpu::Device, + pub queue: &'a wgpu::Queue, + pub encoder: &'a mut wgpu::CommandEncoder, + pub source: &'a wgpu::TextureView, + pub y_out: &'a wgpu::Buffer, + pub uv_out: &'a wgpu::Buffer, + pub dims: (u32, u32), +} + +impl Nv12Packer { + pub fn new(device: &wgpu::Device, max_width: u32, max_height: u32) -> Self { + assert!(max_width > 0, "max_width must be positive"); + assert!(max_height > 0, "max_height must be positive"); + assert!( + max_width.is_multiple_of(2), + "max_width must be even for NV12" + ); + assert!( + max_height.is_multiple_of(2), + "max_height must be even for NV12" + ); + let resources = build_resources(device); + Self { + resources: Some(resources), + bind_group_cache: Mutex::new(None), + max_width, + max_height, + } + } + + pub fn new_unbuilt(max_width: u32, max_height: u32) -> Self { + assert!(max_width > 0, "max_width must be positive"); + assert!(max_height > 0, "max_height must be positive"); + assert!( + max_width.is_multiple_of(2), + "max_width must be even for NV12" + ); + assert!( + max_height.is_multiple_of(2), + "max_height must be even for NV12" + ); + Self { + resources: None, + bind_group_cache: Mutex::new(None), + max_width, + max_height, + } + } + + pub fn max_width(&self) -> u32 { + assert!(self.max_width > 0, "max_width invariant"); + assert!(self.max_width.is_multiple_of(2), "max_width even invariant"); + self.max_width + } + + pub fn max_height(&self) -> u32 { + assert!(self.max_height > 0, "max_height invariant"); + assert!( + self.max_height.is_multiple_of(2), + "max_height even invariant" + ); + self.max_height + } + + pub fn is_built(&self) -> bool { + let built = self.resources.is_some(); + assert!( + self.max_width > 0, + "max_width must be positive while introspecting state" + ); + assert!( + self.max_height > 0, + "max_height must be positive while introspecting state" + ); + built + } + + pub fn y_plane_size(width: u32, height: u32) -> u64 { + assert!(width > 0, "y_plane_size width must be positive"); + assert!(height > 0, "y_plane_size height must be positive"); + u64::from(width) * u64::from(height) * Y_BYTES_PER_PIXEL + } + + pub fn uv_plane_size(width: u32, height: u32) -> u64 { + assert!(width > 0, "uv_plane_size width must be positive"); + assert!(height > 0, "uv_plane_size height must be positive"); + assert!(width.is_multiple_of(2), "uv plane requires even width"); + assert!(height.is_multiple_of(2), "uv plane requires even height"); + u64::from(width) * u64::from(height) * UV_BYTES_PER_PIXEL_PAIR / 4 + } + + pub fn pack(&self, mut job: PackJob<'_>) -> Result<(), Nv12PackError> { + let (width, height) = job.dims; + let resources = match self.resources.as_ref() { + Some(r) => r, + None => return Err(Nv12PackError::NotReady), + }; + self.validate(width, height, job.y_out, job.uv_out)?; + assert!(width <= self.max_width, "validated width must respect max"); + assert!( + height <= self.max_height, + "validated height must respect max" + ); + let bind_group = self.acquire_bind_group(resources, &job); + record_pack_pass(resources, &bind_group, &mut job); + Ok(()) + } + + fn acquire_bind_group(&self, resources: &PackResources, job: &PackJob<'_>) -> wgpu::BindGroup { + let key = CachedBindGroupKey { + source_view: job.source.clone(), + y_buf: job.y_out.clone(), + uv_buf: job.uv_out.clone(), + }; + let mut cache = self.bind_group_cache.lock(); + if let Some(cached) = cache.as_ref() + && cached.key == key + { + return cached.bind_group.clone(); + } + let bind_group = build_bind_group(resources, job); + *cache = Some(CachedBindGroup { + key, + bind_group: bind_group.clone(), + }); + assert!( + cache.is_some(), + "bind group cache must be populated after rebuild" + ); + bind_group + } + + pub fn cached_bind_group_count(&self) -> usize { + let cache = self.bind_group_cache.lock(); + match cache.as_ref() { + Some(_) => 1, + None => 0, + } + } + + fn validate( + &self, + width: u32, + height: u32, + y_out: &wgpu::Buffer, + uv_out: &wgpu::Buffer, + ) -> Result<(), Nv12PackError> { + assert!(self.max_width > 0, "validate requires positive max_width"); + assert!(self.max_height > 0, "validate requires positive max_height"); + if width == 0 { + return Err(Nv12PackError::DimensionsZero); + } + if height == 0 { + return Err(Nv12PackError::DimensionsZero); + } + if width > self.max_width { + return Err(Nv12PackError::DimensionsExceedMax { + requested: (width, height), + max: (self.max_width, self.max_height), + }); + } + if height > self.max_height { + return Err(Nv12PackError::DimensionsExceedMax { + requested: (width, height), + max: (self.max_width, self.max_height), + }); + } + if !width.is_multiple_of(2) { + return Err(Nv12PackError::DimensionsNotEven { + requested: (width, height), + }); + } + if !height.is_multiple_of(2) { + return Err(Nv12PackError::DimensionsNotEven { + requested: (width, height), + }); + } + if !width.is_multiple_of(4) { + return Err(Nv12PackError::StrideNotAligned { stride: width }); + } + let required_y = Self::y_plane_size(width, height); + let actual_y = y_out.size(); + if actual_y < required_y { + return Err(Nv12PackError::YBufferTooSmall { + required: required_y, + actual: actual_y, + }); + } + let required_uv = Self::uv_plane_size(width, height); + let actual_uv = uv_out.size(); + if actual_uv < required_uv { + return Err(Nv12PackError::UvBufferTooSmall { + required: required_uv, + actual: actual_uv, + }); + } + Ok(()) + } +} + +impl GpuLossCallback for Nv12Packer { + fn release(&mut self) { + assert!(self.max_width > 0, "release precondition: max_width valid"); + assert!( + self.max_height > 0, + "release precondition: max_height valid" + ); + self.resources = None; + { + let mut cache = self.bind_group_cache.lock(); + *cache = None; + assert!( + cache.is_none(), + "release postcondition: cache must be cleared" + ); + } + assert!(!self.is_built(), "release postcondition: must be unbuilt"); + } + + fn rebuild( + &mut self, + device: &wgpu::Device, + _queue: &wgpu::Queue, + ) -> Result<(), GpuRebuildError> { + assert!(self.max_width > 0, "rebuild precondition: max_width valid"); + assert!( + self.max_height > 0, + "rebuild precondition: max_height valid" + ); + if self.resources.is_some() { + return Err(GpuRebuildError::OwnerInvariantBroken { + reason: "rebuild without prior release", + }); + } + let resources = build_resources(device); + self.resources = Some(resources); + assert!(self.is_built(), "rebuild postcondition: must be built"); + Ok(()) + } + + fn is_ready(&self) -> bool { + self.is_built() + } + + fn debug_label(&self) -> &'static str { + "nv12_gpu_pack.packer" + } +} + +fn record_pack_pass( + resources: &PackResources, + bind_group: &wgpu::BindGroup, + job: &mut PackJob<'_>, +) { + let (width, height) = job.dims; + assert!( + width.is_multiple_of(2), + "record_pack_pass width even precondition" + ); + assert!( + height.is_multiple_of(2), + "record_pack_pass height even precondition" + ); + let uniforms = PackUniforms { + width, + height, + stride_y: width, + stride_uv: width, + }; + job.queue + .write_buffer(&resources.uniform_buffer, 0, bytemuck::bytes_of(&uniforms)); + job.encoder + .clear_buffer(job.y_out, 0, Some(Nv12Packer::y_plane_size(width, height))); + job.encoder.clear_buffer( + job.uv_out, + 0, + Some(Nv12Packer::uv_plane_size(width, height)), + ); + let mut pass = job + .encoder + .begin_compute_pass(&wgpu::ComputePassDescriptor { + label: Some("nv12_gpu_pack.pass"), + timestamp_writes: None, + }); + pass.set_pipeline(&resources.pipeline); + pass.set_bind_group(0, bind_group, &[]); + let groups_x = (width / 2).div_ceil(WORKGROUP_DIM); + let groups_y = (height / 2).div_ceil(WORKGROUP_DIM); + assert!(groups_x > 0, "record_pack_pass groups_x positive"); + assert!(groups_y > 0, "record_pack_pass groups_y positive"); + pass.dispatch_workgroups(groups_x, groups_y, 1); +} + +fn build_bind_group(resources: &PackResources, job: &PackJob<'_>) -> wgpu::BindGroup { + job.device.create_bind_group(&wgpu::BindGroupDescriptor { + label: Some("nv12_gpu_pack.bind_group"), + layout: &resources.bind_group_layout, + entries: &[ + wgpu::BindGroupEntry { + binding: 0, + resource: wgpu::BindingResource::TextureView(job.source), + }, + wgpu::BindGroupEntry { + binding: 1, + resource: job.y_out.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 2, + resource: job.uv_out.as_entire_binding(), + }, + wgpu::BindGroupEntry { + binding: 3, + resource: resources.uniform_buffer.as_entire_binding(), + }, + ], + }) +} + +fn build_resources(device: &wgpu::Device) -> PackResources { + let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor { + label: Some("nv12_gpu_pack.shader"), + source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()), + }); + let bind_group_layout = create_bind_group_layout(device); + let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor { + label: Some("nv12_gpu_pack.pipeline_layout"), + bind_group_layouts: &[Some(&bind_group_layout)], + immediate_size: 0, + }); + let pipeline = device.create_compute_pipeline(&wgpu::ComputePipelineDescriptor { + label: Some("nv12_gpu_pack.pipeline"), + layout: Some(&pipeline_layout), + module: &shader, + entry_point: Some("pack_nv12"), + compilation_options: wgpu::PipelineCompilationOptions::default(), + cache: None, + }); + let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor { + label: Some("nv12_gpu_pack.uniforms"), + contents: bytemuck::bytes_of(&PackUniforms { + width: 0, + height: 0, + stride_y: 0, + stride_uv: 0, + }), + usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST, + }); + PackResources { + pipeline, + bind_group_layout, + uniform_buffer, + } +} + +fn create_bind_group_layout(device: &wgpu::Device) -> wgpu::BindGroupLayout { + device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor { + label: Some("nv12_gpu_pack.bind_group_layout"), + entries: &[ + wgpu::BindGroupLayoutEntry { + binding: 0, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Texture { + sample_type: wgpu::TextureSampleType::Float { filterable: false }, + view_dimension: wgpu::TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 1, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: false }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 2, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Storage { read_only: false }, + has_dynamic_offset: false, + min_binding_size: None, + }, + count: None, + }, + wgpu::BindGroupLayoutEntry { + binding: 3, + visibility: wgpu::ShaderStages::COMPUTE, + ty: wgpu::BindingType::Buffer { + ty: wgpu::BufferBindingType::Uniform, + has_dynamic_offset: false, + min_binding_size: NonZeroU64::new(std::mem::size_of::() as u64), + }, + count: None, + }, + ], + }) +} + +pub fn try_acquire_device() -> Option<(wgpu::Device, wgpu::Queue, wgpu::Instance)> { + let mut descriptor = wgpu::InstanceDescriptor::new_without_display_handle(); + descriptor.backends = wgpu::Backends::all() | wgpu::Backends::SECONDARY; + let instance = wgpu::Instance::new(descriptor); + let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::default(), + force_fallback_adapter: false, + compatible_surface: None, + })) + .ok()?; + let device_result = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor { + label: Some("nv12_gpu_pack.test_device"), + required_features: wgpu::Features::empty(), + required_limits: wgpu::Limits::default(), + memory_hints: wgpu::MemoryHints::default(), + trace: wgpu::Trace::Off, + experimental_features: wgpu::ExperimentalFeatures::default(), + })); + match device_result { + Ok((device, queue)) => Some((device, queue, instance)), + Err(_) => None, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use fluxer_gpu_rebuild::{GpuLossRegistry, RebuildOutcome}; + + struct GpuCtx { + device: wgpu::Device, + queue: wgpu::Queue, + _instance: wgpu::Instance, + } + + fn gpu_ctx() -> Option { + let (device, queue, instance) = try_acquire_device()?; + Some(GpuCtx { + device, + queue, + _instance: instance, + }) + } + + fn make_source_texture( + device: &wgpu::Device, + queue: &wgpu::Queue, + width: u32, + height: u32, + pixels: &[u8], + ) -> wgpu::Texture { + assert_eq!( + pixels.len() as u64, + u64::from(width) * u64::from(height) * 4 + ); + let texture = device.create_texture(&wgpu::TextureDescriptor { + label: Some("nv12_gpu_pack.test_source"), + size: wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + mip_level_count: 1, + sample_count: 1, + dimension: wgpu::TextureDimension::D2, + format: wgpu::TextureFormat::Rgba8Unorm, + usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST, + view_formats: &[], + }); + queue.write_texture( + wgpu::TexelCopyTextureInfo { + texture: &texture, + mip_level: 0, + origin: wgpu::Origin3d::ZERO, + aspect: wgpu::TextureAspect::All, + }, + pixels, + wgpu::TexelCopyBufferLayout { + offset: 0, + bytes_per_row: Some(width * 4), + rows_per_image: Some(height), + }, + wgpu::Extent3d { + width, + height, + depth_or_array_layers: 1, + }, + ); + texture + } + + fn make_storage_buffer(device: &wgpu::Device, size: u64, label: &str) -> wgpu::Buffer { + device.create_buffer(&wgpu::BufferDescriptor { + label: Some(label), + size, + usage: wgpu::BufferUsages::STORAGE + | wgpu::BufferUsages::COPY_SRC + | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }) + } + + fn read_buffer(device: &wgpu::Device, queue: &wgpu::Queue, src: &wgpu::Buffer) -> Vec { + let size = src.size(); + let staging = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("nv12_gpu_pack.test_staging"), + size, + usage: wgpu::BufferUsages::COPY_DST | wgpu::BufferUsages::MAP_READ, + mapped_at_creation: false, + }); + let mut encoder = device.create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("nv12_gpu_pack.test_readback"), + }); + encoder.copy_buffer_to_buffer(src, 0, &staging, 0, size); + queue.submit(std::iter::once(encoder.finish())); + let slice = staging.slice(..); + let (tx, rx) = std::sync::mpsc::channel(); + slice.map_async(wgpu::MapMode::Read, move |res| { + let _ = tx.send(res); + }); + let _ = device.poll(wgpu::PollType::Wait { + submission_index: None, + timeout: None, + }); + let recv = rx.recv(); + assert!(recv.is_ok(), "channel must deliver map result"); + let map_result = recv.expect("map result channel"); + assert!(map_result.is_ok(), "map_async must succeed"); + let data = { + let range = slice.get_mapped_range(); + range.to_vec() + }; + staging.unmap(); + data + } + + fn pack_and_readback( + ctx: &GpuCtx, + packer: &Nv12Packer, + texture: &wgpu::Texture, + width: u32, + height: u32, + ) -> (Vec, Vec) { + let y_size = Nv12Packer::y_plane_size(width, height); + let uv_size = Nv12Packer::uv_plane_size(width, height); + let y_buf = make_storage_buffer(&ctx.device, y_size, "y"); + let uv_buf = make_storage_buffer(&ctx.device, uv_size, "uv"); + let view = texture.create_view(&wgpu::TextureViewDescriptor::default()); + let mut encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("nv12_gpu_pack.test_encoder"), + }); + let result = packer.pack(PackJob { + device: &ctx.device, + queue: &ctx.queue, + encoder: &mut encoder, + source: &view, + y_out: &y_buf, + uv_out: &uv_buf, + dims: (width, height), + }); + assert!(result.is_ok(), "pack must succeed: {result:?}"); + ctx.queue.submit(std::iter::once(encoder.finish())); + let y = read_buffer(&ctx.device, &ctx.queue, &y_buf); + let uv = read_buffer(&ctx.device, &ctx.queue, &uv_buf); + (y, uv) + } + + #[test] + fn solid_gray_packs_to_expected_bt709_limited() { + let Some(ctx) = gpu_ctx() else { + eprintln!("nv12-gpu-pack: no wgpu adapter, skipping"); + return; + }; + let width: u32 = 16; + let height: u32 = 8; + let mut pixels = vec![0u8; (width * height * 4) as usize]; + for chunk in pixels.chunks_exact_mut(4) { + chunk[0] = 128; + chunk[1] = 128; + chunk[2] = 128; + chunk[3] = 255; + } + let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels); + let packer = Nv12Packer::new(&ctx.device, width, height); + let (y, uv) = pack_and_readback(&ctx, &packer, &tex, width, height); + for value in &y { + assert!( + (124..=128).contains(value), + "Y value {value} out of expected range for gray 128" + ); + } + for value in &uv { + assert!( + (126..=130).contains(value), + "UV value {value} out of expected range for neutral gray" + ); + } + } + + #[test] + fn solid_red_v_is_above_neutral() { + let Some(ctx) = gpu_ctx() else { + eprintln!("nv12-gpu-pack: no wgpu adapter, skipping"); + return; + }; + let width: u32 = 16; + let height: u32 = 8; + let mut pixels = vec![0u8; (width * height * 4) as usize]; + for chunk in pixels.chunks_exact_mut(4) { + chunk[0] = 255; + chunk[1] = 0; + chunk[2] = 0; + chunk[3] = 255; + } + let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels); + let packer = Nv12Packer::new(&ctx.device, width, height); + let (_y, uv) = pack_and_readback(&ctx, &packer, &tex, width, height); + for pair in uv.chunks_exact(2) { + let u = pair[0]; + let v = pair[1]; + assert!(u < 128, "expected U below neutral for pure red, got {u}"); + assert!( + v > 200, + "expected V well above neutral for pure red, got {v}" + ); + } + } + + #[test] + fn rejects_dims_exceeding_max() { + let Some(ctx) = gpu_ctx() else { + eprintln!("nv12-gpu-pack: no wgpu adapter, skipping"); + return; + }; + let max_w: u32 = 8; + let max_h: u32 = 8; + let packer = Nv12Packer::new(&ctx.device, max_w, max_h); + let y_size = Nv12Packer::y_plane_size(16, 16); + let uv_size = Nv12Packer::uv_plane_size(16, 16); + let y_buf = make_storage_buffer(&ctx.device, y_size, "y"); + let uv_buf = make_storage_buffer(&ctx.device, uv_size, "uv"); + let dummy_pixels = vec![0u8; (max_w * max_h * 4) as usize]; + let tex = make_source_texture(&ctx.device, &ctx.queue, max_w, max_h, &dummy_pixels); + let view = tex.create_view(&wgpu::TextureViewDescriptor::default()); + let mut encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + let result = packer.pack(PackJob { + device: &ctx.device, + queue: &ctx.queue, + encoder: &mut encoder, + source: &view, + y_out: &y_buf, + uv_out: &uv_buf, + dims: (16, 16), + }); + assert!(matches!( + result, + Err(Nv12PackError::DimensionsExceedMax { .. }) + )); + } + + #[test] + fn rejects_odd_dims() { + let Some(ctx) = gpu_ctx() else { + eprintln!("nv12-gpu-pack: no wgpu adapter, skipping"); + return; + }; + let packer = Nv12Packer::new(&ctx.device, 32, 32); + let y_buf = make_storage_buffer(&ctx.device, 64, "y"); + let uv_buf = make_storage_buffer(&ctx.device, 64, "uv"); + let dummy_pixels = vec![0u8; (8 * 8 * 4) as usize]; + let tex = make_source_texture(&ctx.device, &ctx.queue, 8, 8, &dummy_pixels); + let view = tex.create_view(&wgpu::TextureViewDescriptor::default()); + let mut encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + let result = packer.pack(PackJob { + device: &ctx.device, + queue: &ctx.queue, + encoder: &mut encoder, + source: &view, + y_out: &y_buf, + uv_out: &uv_buf, + dims: (7, 8), + }); + assert!(matches!( + result, + Err(Nv12PackError::DimensionsNotEven { .. }) + )); + } + + #[test] + fn determinism_two_runs_same_input_same_output() { + let Some(ctx) = gpu_ctx() else { + eprintln!("nv12-gpu-pack: no wgpu adapter, skipping"); + return; + }; + let width: u32 = 32; + let height: u32 = 16; + let mut pixels = vec![0u8; (width * height * 4) as usize]; + for (i, chunk) in pixels.chunks_exact_mut(4).enumerate() { + let v = (i % 251) as u8; + chunk[0] = v; + chunk[1] = v.wrapping_mul(2); + chunk[2] = v.wrapping_add(31); + chunk[3] = 255; + } + let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels); + let packer = Nv12Packer::new(&ctx.device, width, height); + let (y1, uv1) = pack_and_readback(&ctx, &packer, &tex, width, height); + let (y2, uv2) = pack_and_readback(&ctx, &packer, &tex, width, height); + assert_eq!(y1, y2, "Y plane must be deterministic"); + assert_eq!(uv1, uv2, "UV plane must be deterministic"); + } + + #[test] + fn buffer_too_small_is_rejected() { + let Some(ctx) = gpu_ctx() else { + eprintln!("nv12-gpu-pack: no wgpu adapter, skipping"); + return; + }; + let packer = Nv12Packer::new(&ctx.device, 64, 64); + let y_buf = make_storage_buffer(&ctx.device, 16, "y"); + let uv_buf = make_storage_buffer(&ctx.device, 16, "uv"); + let pixels = vec![0u8; (16 * 16 * 4) as usize]; + let tex = make_source_texture(&ctx.device, &ctx.queue, 16, 16, &pixels); + let view = tex.create_view(&wgpu::TextureViewDescriptor::default()); + let mut encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + let result = packer.pack(PackJob { + device: &ctx.device, + queue: &ctx.queue, + encoder: &mut encoder, + source: &view, + y_out: &y_buf, + uv_out: &uv_buf, + dims: (16, 16), + }); + assert!(matches!(result, Err(Nv12PackError::YBufferTooSmall { .. }))); + } + + #[test] + fn packer_is_ready_only_when_built() { + let Some(ctx) = gpu_ctx() else { + eprintln!("nv12-gpu-pack: no wgpu adapter, skipping"); + return; + }; + let mut packer = Nv12Packer::new(&ctx.device, 32, 32); + assert!( + packer.is_ready(), + "freshly constructed packer must be ready" + ); + packer.release(); + assert!(!packer.is_ready(), "released packer must not be ready"); + let rebuilt = packer.rebuild(&ctx.device, &ctx.queue); + assert!(rebuilt.is_ok(), "rebuild on a fresh device must succeed"); + assert!(packer.is_ready(), "rebuilt packer must be ready"); + } + + #[test] + fn pack_between_release_and_rebuild_returns_not_ready() { + let Some(ctx) = gpu_ctx() else { + eprintln!("nv12-gpu-pack: no wgpu adapter, skipping"); + return; + }; + let mut packer = Nv12Packer::new(&ctx.device, 16, 16); + packer.release(); + let y_buf = make_storage_buffer(&ctx.device, 256, "y"); + let uv_buf = make_storage_buffer(&ctx.device, 256, "uv"); + let dummy_pixels = vec![0u8; (16 * 16 * 4) as usize]; + let tex = make_source_texture(&ctx.device, &ctx.queue, 16, 16, &dummy_pixels); + let view = tex.create_view(&wgpu::TextureViewDescriptor::default()); + let mut encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None }); + let result = packer.pack(PackJob { + device: &ctx.device, + queue: &ctx.queue, + encoder: &mut encoder, + source: &view, + y_out: &y_buf, + uv_out: &uv_buf, + dims: (16, 16), + }); + assert!(matches!(result, Err(Nv12PackError::NotReady))); + } + + #[test] + fn registry_release_then_rebuild_invokes_callback_in_order() { + let Some(ctx) = gpu_ctx() else { + eprintln!("nv12-gpu-pack: no wgpu adapter, skipping"); + return; + }; + let registry = GpuLossRegistry::new(); + let packer = Box::new(Nv12Packer::new(&ctx.device, 16, 16)); + let _guard = registry.register(packer); + let report = registry.handle_device_lost(&ctx.device, &ctx.queue); + assert_eq!(report.released_count, 1); + assert_eq!(report.rebuilt_count, 1); + assert_eq!(report.failed_count, 0); + assert!(report.is_total_success()); + } + + #[test] + fn after_loss_handling_packer_can_pack_again() { + let Some(ctx) = gpu_ctx() else { + eprintln!("nv12-gpu-pack: no wgpu adapter, skipping"); + return; + }; + let width: u32 = 16; + let height: u32 = 8; + let pixels = vec![128u8; (width * height * 4) as usize]; + let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels); + let mut packer = Nv12Packer::new(&ctx.device, width, height); + packer.release(); + packer + .rebuild(&ctx.device, &ctx.queue) + .expect("rebuild must succeed after release"); + let (y, _uv) = pack_and_readback(&ctx, &packer, &tex, width, height); + assert!(!y.is_empty(), "Y plane must be non-empty after rebuild"); + assert!( + (124..=128).contains(&y[0]), + "Y value out of range for gray 128 after rebuild: {}", + y[0] + ); + } + + #[test] + fn determinism_across_rebuild_round_trip() { + let Some(ctx) = gpu_ctx() else { + eprintln!("nv12-gpu-pack: no wgpu adapter, skipping"); + return; + }; + let width: u32 = 32; + let height: u32 = 16; + let mut pixels = vec![0u8; (width * height * 4) as usize]; + for (i, chunk) in pixels.chunks_exact_mut(4).enumerate() { + let v = (i % 251) as u8; + chunk[0] = v; + chunk[1] = v.wrapping_mul(2); + chunk[2] = v.wrapping_add(31); + chunk[3] = 255; + } + let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels); + let mut packer = Nv12Packer::new(&ctx.device, width, height); + let (y1, uv1) = pack_and_readback(&ctx, &packer, &tex, width, height); + packer.release(); + packer + .rebuild(&ctx.device, &ctx.queue) + .expect("rebuild must succeed"); + let (y2, uv2) = pack_and_readback(&ctx, &packer, &tex, width, height); + assert_eq!(y1, y2, "Y plane must match across rebuild round-trip"); + assert_eq!(uv1, uv2, "UV plane must match across rebuild round-trip"); + } + + #[test] + fn double_rebuild_without_release_is_owner_invariant_error() { + let Some(ctx) = gpu_ctx() else { + eprintln!("nv12-gpu-pack: no wgpu adapter, skipping"); + return; + }; + let mut packer = Nv12Packer::new(&ctx.device, 16, 16); + let outcome = packer.rebuild(&ctx.device, &ctx.queue); + assert!(matches!( + outcome, + Err(GpuRebuildError::OwnerInvariantBroken { .. }) + )); + } + + struct FailingRebuildOwner { + ready: bool, + } + + impl FailingRebuildOwner { + fn new() -> Self { + Self { ready: true } + } + } + + impl GpuLossCallback for FailingRebuildOwner { + fn release(&mut self) { + let was_ready = self.ready; + assert!( + was_ready, + "FailingRebuildOwner release must observe ready state", + ); + self.ready = false; + assert!(!self.ready, "FailingRebuildOwner postcondition"); + } + fn rebuild( + &mut self, + _device: &wgpu::Device, + _queue: &wgpu::Queue, + ) -> Result<(), GpuRebuildError> { + assert!( + !self.ready, + "rebuild must run after release in FailingRebuildOwner" + ); + Err(GpuRebuildError::DeviceRejected { + reason: "synthetic second-device failure", + }) + } + fn is_ready(&self) -> bool { + self.ready + } + fn debug_label(&self) -> &'static str { + "test.failing_rebuild_owner" + } + } + + #[test] + fn failed_rebuild_surfaces_in_report_and_owner_reports_not_ready() { + let Some(ctx) = gpu_ctx() else { + eprintln!("nv12-gpu-pack: no wgpu adapter, skipping"); + return; + }; + let registry = GpuLossRegistry::new(); + let _guard = registry.register(Box::new(FailingRebuildOwner::new())); + let report = registry.handle_device_lost(&ctx.device, &ctx.queue); + assert_eq!(report.released_count, 1); + assert_eq!(report.failed_count, 1); + assert_eq!(report.rebuilt_count, 0); + assert!(!report.is_total_success()); + let failed = report + .outcomes + .iter() + .filter(|o| matches!(o, RebuildOutcome::Failed { .. })) + .count(); + assert_eq!(failed, 1); + } + + #[test] + fn multiple_packers_release_lifo_rebuild_fifo() { + let Some(ctx) = gpu_ctx() else { + eprintln!("nv12-gpu-pack: no wgpu adapter, skipping"); + return; + }; + let registry = GpuLossRegistry::new(); + let mut guards = Vec::new(); + for _ in 0..3u32 { + let packer = Box::new(Nv12Packer::new(&ctx.device, 16, 16)); + guards.push(registry.register(packer)); + } + assert_eq!(registry.len(), 3); + let report = registry.handle_device_lost(&ctx.device, &ctx.queue); + assert_eq!(report.released_count, 3); + assert_eq!(report.rebuilt_count, 3); + assert_eq!(report.failed_count, 0); + for outcome in &report.outcomes { + assert!(matches!(outcome, RebuildOutcome::Rebuilt { .. })); + } + drop(guards); + } + + fn pack_one_pass( + ctx: &GpuCtx, + packer: &Nv12Packer, + view: &wgpu::TextureView, + y_buf: &wgpu::Buffer, + uv_buf: &wgpu::Buffer, + width: u32, + height: u32, + ) { + let mut encoder = ctx + .device + .create_command_encoder(&wgpu::CommandEncoderDescriptor { + label: Some("nv12_gpu_pack.cache_test_encoder"), + }); + let result = packer.pack(PackJob { + device: &ctx.device, + queue: &ctx.queue, + encoder: &mut encoder, + source: view, + y_out: y_buf, + uv_out: uv_buf, + dims: (width, height), + }); + assert!(result.is_ok(), "pack must succeed: {result:?}"); + ctx.queue.submit(std::iter::once(encoder.finish())); + } + + #[test] + fn cache_hit_when_pack_called_twice_with_same_inputs() { + let Some(ctx) = gpu_ctx() else { + eprintln!("nv12-gpu-pack: no wgpu adapter, skipping"); + return; + }; + let width: u32 = 32; + let height: u32 = 16; + let pixels = vec![64u8; (width * height * 4) as usize]; + let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels); + let view = tex.create_view(&wgpu::TextureViewDescriptor::default()); + let packer = Nv12Packer::new(&ctx.device, width, height); + let y_buf = make_storage_buffer(&ctx.device, Nv12Packer::y_plane_size(width, height), "y"); + let uv_buf = + make_storage_buffer(&ctx.device, Nv12Packer::uv_plane_size(width, height), "uv"); + assert_eq!(packer.cached_bind_group_count(), 0); + pack_one_pass(&ctx, &packer, &view, &y_buf, &uv_buf, width, height); + assert_eq!(packer.cached_bind_group_count(), 1); + pack_one_pass(&ctx, &packer, &view, &y_buf, &uv_buf, width, height); + assert_eq!(packer.cached_bind_group_count(), 1); + } + + #[test] + fn cache_miss_when_buffers_change() { + let Some(ctx) = gpu_ctx() else { + eprintln!("nv12-gpu-pack: no wgpu adapter, skipping"); + return; + }; + let width: u32 = 32; + let height: u32 = 16; + let pixels = vec![64u8; (width * height * 4) as usize]; + let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels); + let view = tex.create_view(&wgpu::TextureViewDescriptor::default()); + let packer = Nv12Packer::new(&ctx.device, width, height); + let y_buf_a = + make_storage_buffer(&ctx.device, Nv12Packer::y_plane_size(width, height), "y_a"); + let uv_buf_a = make_storage_buffer( + &ctx.device, + Nv12Packer::uv_plane_size(width, height), + "uv_a", + ); + pack_one_pass(&ctx, &packer, &view, &y_buf_a, &uv_buf_a, width, height); + assert_eq!(packer.cached_bind_group_count(), 1); + let y_buf_b = + make_storage_buffer(&ctx.device, Nv12Packer::y_plane_size(width, height), "y_b"); + let uv_buf_b = make_storage_buffer( + &ctx.device, + Nv12Packer::uv_plane_size(width, height), + "uv_b", + ); + pack_one_pass(&ctx, &packer, &view, &y_buf_b, &uv_buf_b, width, height); + assert_eq!(packer.cached_bind_group_count(), 1); + pack_one_pass(&ctx, &packer, &view, &y_buf_a, &uv_buf_a, width, height); + assert_eq!(packer.cached_bind_group_count(), 1); + } + + #[test] + fn cache_invalidated_on_release() { + let Some(ctx) = gpu_ctx() else { + eprintln!("nv12-gpu-pack: no wgpu adapter, skipping"); + return; + }; + let width: u32 = 32; + let height: u32 = 16; + let pixels = vec![64u8; (width * height * 4) as usize]; + let tex = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels); + let view = tex.create_view(&wgpu::TextureViewDescriptor::default()); + let mut packer = Nv12Packer::new(&ctx.device, width, height); + let y_buf = make_storage_buffer(&ctx.device, Nv12Packer::y_plane_size(width, height), "y"); + let uv_buf = + make_storage_buffer(&ctx.device, Nv12Packer::uv_plane_size(width, height), "uv"); + pack_one_pass(&ctx, &packer, &view, &y_buf, &uv_buf, width, height); + assert_eq!(packer.cached_bind_group_count(), 1); + packer.release(); + assert_eq!(packer.cached_bind_group_count(), 0); + packer + .rebuild(&ctx.device, &ctx.queue) + .expect("rebuild must succeed"); + assert_eq!(packer.cached_bind_group_count(), 0); + pack_one_pass(&ctx, &packer, &view, &y_buf, &uv_buf, width, height); + assert_eq!(packer.cached_bind_group_count(), 1); + } + + #[test] + fn cache_replaces_when_source_view_changes() { + let Some(ctx) = gpu_ctx() else { + eprintln!("nv12-gpu-pack: no wgpu adapter, skipping"); + return; + }; + let width: u32 = 32; + let height: u32 = 16; + let pixels = vec![64u8; (width * height * 4) as usize]; + let tex_a = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels); + let tex_b = make_source_texture(&ctx.device, &ctx.queue, width, height, &pixels); + let view_a = tex_a.create_view(&wgpu::TextureViewDescriptor::default()); + let view_b = tex_b.create_view(&wgpu::TextureViewDescriptor::default()); + let packer = Nv12Packer::new(&ctx.device, width, height); + let y_buf = make_storage_buffer(&ctx.device, Nv12Packer::y_plane_size(width, height), "y"); + let uv_buf = + make_storage_buffer(&ctx.device, Nv12Packer::uv_plane_size(width, height), "uv"); + pack_one_pass(&ctx, &packer, &view_a, &y_buf, &uv_buf, width, height); + assert_eq!(packer.cached_bind_group_count(), 1); + pack_one_pass(&ctx, &packer, &view_b, &y_buf, &uv_buf, width, height); + assert_eq!(packer.cached_bind_group_count(), 1); + } +} diff --git a/fluxer_desktop/native/nv12-gpu-pack/src/shader.wgsl b/fluxer_desktop/native/nv12-gpu-pack/src/shader.wgsl new file mode 100644 index 0000000..d7bb46c --- /dev/null +++ b/fluxer_desktop/native/nv12-gpu-pack/src/shader.wgsl @@ -0,0 +1,88 @@ +struct PackUniforms { + width: u32, + height: u32, + stride_y: u32, + stride_uv: u32, +}; + +@group(0) @binding(0) var source_tex: texture_2d; +@group(0) @binding(1) var y_plane: array>; +@group(0) @binding(2) var uv_plane: array>; +@group(0) @binding(3) var dims: PackUniforms; + +fn rgb_to_y_full(r: f32, g: f32, b: f32) -> f32 { + return 0.2126 * r + 0.7152 * g + 0.0722 * b; +} + +fn limited_y(y_full: f32) -> u32 { + let scaled = round(y_full * 219.0) + 16.0; + return u32(clamp(scaled, 16.0, 235.0)); +} + +fn limited_u(b: f32, y_full: f32) -> u32 { + let centered = (b - y_full) / 1.8556; + let scaled = round(centered * 224.0) + 128.0; + return u32(clamp(scaled, 16.0, 240.0)); +} + +fn limited_v(r: f32, y_full: f32) -> u32 { + let centered = (r - y_full) / 1.5748; + let scaled = round(centered * 224.0) + 128.0; + return u32(clamp(scaled, 16.0, 240.0)); +} + +fn write_y_byte(byte_offset: u32, value: u32) { + let word_index = byte_offset / 4u; + let shift = (byte_offset % 4u) * 8u; + let payload = (value & 0xFFu) << shift; + atomicOr(&y_plane[word_index], payload); +} + +fn write_uv_byte(byte_offset: u32, value: u32) { + let word_index = byte_offset / 4u; + let shift = (byte_offset % 4u) * 8u; + let payload = (value & 0xFFu) << shift; + atomicOr(&uv_plane[word_index], payload); +} + +@compute @workgroup_size(8, 8, 1) +fn pack_nv12(@builtin(global_invocation_id) gid: vec3) { + let block_x = gid.x; + let block_y = gid.y; + let pixel_x = block_x * 2u; + let pixel_y = block_y * 2u; + if (pixel_x + 1u >= dims.width) { + return; + } + if (pixel_y + 1u >= dims.height) { + return; + } + let p00 = textureLoad(source_tex, vec2(i32(pixel_x), i32(pixel_y)), 0); + let p10 = textureLoad(source_tex, vec2(i32(pixel_x + 1u), i32(pixel_y)), 0); + let p01 = textureLoad(source_tex, vec2(i32(pixel_x), i32(pixel_y + 1u)), 0); + let p11 = textureLoad(source_tex, vec2(i32(pixel_x + 1u), i32(pixel_y + 1u)), 0); + let y00_full = rgb_to_y_full(p00.r, p00.g, p00.b); + let y10_full = rgb_to_y_full(p10.r, p10.g, p10.b); + let y01_full = rgb_to_y_full(p01.r, p01.g, p01.b); + let y11_full = rgb_to_y_full(p11.r, p11.g, p11.b); + let y00 = limited_y(y00_full); + let y10 = limited_y(y10_full); + let y01 = limited_y(y01_full); + let y11 = limited_y(y11_full); + let row0 = pixel_y * dims.stride_y; + let row1 = (pixel_y + 1u) * dims.stride_y; + write_y_byte(row0 + pixel_x, y00); + write_y_byte(row0 + pixel_x + 1u, y10); + write_y_byte(row1 + pixel_x, y01); + write_y_byte(row1 + pixel_x + 1u, y11); + let r_avg = (p00.r + p10.r + p01.r + p11.r) * 0.25; + let g_avg = (p00.g + p10.g + p01.g + p11.g) * 0.25; + let b_avg = (p00.b + p10.b + p01.b + p11.b) * 0.25; + let y_full_avg = rgb_to_y_full(r_avg, g_avg, b_avg); + let u = limited_u(b_avg, y_full_avg); + let v = limited_v(r_avg, y_full_avg); + let uv_row = block_y * dims.stride_uv; + let uv_offset = uv_row + block_x * 2u; + write_uv_byte(uv_offset, u); + write_uv_byte(uv_offset + 1u, v); +} diff --git a/fluxer_desktop/native/platform-info/Cargo.lock b/fluxer_desktop/native/platform-info/Cargo.lock new file mode 100644 index 0000000..8df2e51 --- /dev/null +++ b/fluxer_desktop/native/platform-info/Cargo.lock @@ -0,0 +1,905 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "cc" +version = "1.2.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fluxer_desktop_native" +version = "0.1.0" +dependencies = [ + "sha2", +] + +[[package]] +name = "fluxer_platform_info" +version = "0.0.0" +dependencies = [ + "fluxer_desktop_native", + "napi", + "napi-build", + "napi-derive", + "objc2", + "objc2-foundation", + "objc2-metal", + "tempfile", + "windows", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "typenum", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", + "objc2-exception-helper", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-exception-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" +dependencies = [ + "cc", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags", + "objc2", +] + +[[package]] +name = "objc2-metal" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" +dependencies = [ + "bitflags", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "typenum" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/fluxer_desktop/native/platform-info/Cargo.toml b/fluxer_desktop/native/platform-info/Cargo.toml new file mode 100644 index 0000000..d402de4 --- /dev/null +++ b/fluxer_desktop/native/platform-info/Cargo.toml @@ -0,0 +1,34 @@ +[package] +name = "fluxer_platform_info" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +fluxer_desktop_native = {path = "../rust"} +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8"]} +napi-derive = "3.5.6" + +[target.'cfg(target_os = "macos")'.dependencies] +objc2 = {version = "0.6.4", features = ["exception"]} +objc2-foundation = {version = "0.3.2", default-features = false, features = ["NSArray", "NSObject", "NSString", "std"]} +objc2-metal = {version = "0.3.2", default-features = false, features = ["MTLDevice", "std"]} + +[target.'cfg(target_os = "windows")'.dependencies] +windows = {version = "0.62.2", features = [ + "Win32_Foundation", + "Win32_Graphics_Dxgi", +]} + +[build-dependencies] +napi-build = "2.3.2" + +[dev-dependencies] +tempfile = "3.27" diff --git a/fluxer_desktop/native/platform-info/build.rs b/fluxer_desktop/native/platform-info/build.rs new file mode 100644 index 0000000..5058a70 --- /dev/null +++ b/fluxer_desktop/native/platform-info/build.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + napi_build::setup(); + + if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("macos") { + println!("cargo:rustc-link-lib=framework=CoreGraphics"); + } +} diff --git a/fluxer_desktop/native/platform-info/index.d.ts b/fluxer_desktop/native/platform-info/index.d.ts new file mode 100644 index 0000000..529fd73 --- /dev/null +++ b/fluxer_desktop/native/platform-info/index.d.ts @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +export type PlatformGpuInfoSource = 'metal' | 'dxgi' | 'linux-sysfs'; + +export interface PlatformGpuDeviceInfo { + active: boolean; + vendorId: number; + deviceId: number; + vendorName?: string; + deviceString?: string; + driverVendor?: string; + driverVersion?: string; + dedicatedVideoMemory?: number; + sharedSystemMemory?: number; + subsystemVendorId?: number; + subsystemDeviceId?: number; + registryId?: string; + adapterLuid?: string; + pciPath?: string; + integrated?: boolean; + removable?: boolean; + headless?: boolean; + source: PlatformGpuInfoSource; +} + +export interface PlatformGpuInfo { + devices: ReadonlyArray; + source: PlatformGpuInfoSource; + error?: string; +} + +export declare const getGpuInfo: (() => PlatformGpuInfo) | null; +export declare const loadError: Error | null; diff --git a/fluxer_desktop/native/platform-info/index.js b/fluxer_desktop/native/platform-info/index.js new file mode 100644 index 0000000..3b3219b --- /dev/null +++ b/fluxer_desktop/native/platform-info/index.js @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync} = require('node:fs'); +const {join, sep} = require('node:path'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); +const MODULE_NAME = '@fluxer/platform-info'; +const SKIP_NATIVE_PROBE_ENV = 'FLUXER_PLATFORM_INFO_SKIP_NATIVE_PROBE'; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName(platform = process.platform, arch = process.arch) { + switch (platform) { + case 'darwin': + if (arch === 'x64' || arch === 'arm64') return `platform-info.darwin-${arch}.node`; + break; + case 'win32': + if (arch === 'x64' || arch === 'arm64') return `platform-info.win32-${arch}-msvc.node`; + break; + case 'linux': + if (arch === 'x64' || arch === 'arm64') return `platform-info.linux-${arch}-gnu.node`; + break; + } + throw new Error(`Unsupported platform-info target: ${platform}/${arch}`); +} + +let binding = null; +let loadError = null; + +try { + const nativeRoot = resolveNativeRoot(); + const nativePath = join(nativeRoot, nativeFileName()); + const loaded = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + binding = loaded.binding; + loadError = loaded.loadError; + if (loadError) throw loadError; +} catch (error) { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: 'native loader threw before binding load completed', + cause: error, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + throw loadError; +} + +module.exports = { + getGpuInfo: binding ? binding.getGpuInfo : null, + loadError, + _private: { + nativeFileName, + }, +}; diff --git a/fluxer_desktop/native/platform-info/loader-diagnostics.cjs b/fluxer_desktop/native/platform-info/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/platform-info/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/platform-info/package.json b/fluxer_desktop/native/platform-info/package.json new file mode 100644 index 0000000..3c2fd74 --- /dev/null +++ b/fluxer_desktop/native/platform-info/package.json @@ -0,0 +1,28 @@ +{ + "name": "@fluxer/platform-info", + "version": "0.0.0", + "description": "", + "private": true, + "license": "AGPL-3.0-or-later", + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "platform-info.darwin-x64.node", + "platform-info.darwin-arm64.node", + "platform-info.win32-x64-msvc.node", + "platform-info.win32-arm64-msvc.node", + "platform-info.linux-x64-gnu.node", + "platform-info.linux-arm64-gnu.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "cargo test --manifest-path Cargo.toml && node --test test/*.test.mjs" + } +} diff --git a/fluxer_desktop/native/platform-info/src/lib.rs b/fluxer_desktop/native/platform-info/src/lib.rs new file mode 100644 index 0000000..bccacee --- /dev/null +++ b/fluxer_desktop/native/platform-info/src/lib.rs @@ -0,0 +1,539 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use napi_derive::napi; + +pub const SOURCE_METAL: &str = "metal"; +pub const SOURCE_DXGI: &str = "dxgi"; +pub const SOURCE_LINUX_SYSFS: &str = "linux-sysfs"; + +#[napi(object)] +pub struct PlatformGpuDeviceInfo { + pub active: bool, + #[napi(js_name = "vendorId")] + pub vendor_id: u32, + #[napi(js_name = "deviceId")] + pub device_id: u32, + #[napi(js_name = "vendorName")] + pub vendor_name: Option, + #[napi(js_name = "deviceString")] + pub device_string: Option, + #[napi(js_name = "driverVendor")] + pub driver_vendor: Option, + #[napi(js_name = "driverVersion")] + pub driver_version: Option, + #[napi(js_name = "dedicatedVideoMemory")] + pub dedicated_video_memory: Option, + #[napi(js_name = "sharedSystemMemory")] + pub shared_system_memory: Option, + #[napi(js_name = "subsystemVendorId")] + pub subsystem_vendor_id: Option, + #[napi(js_name = "subsystemDeviceId")] + pub subsystem_device_id: Option, + #[napi(js_name = "registryId")] + pub registry_id: Option, + #[napi(js_name = "adapterLuid")] + pub adapter_luid: Option, + #[napi(js_name = "pciPath")] + pub pci_path: Option, + pub integrated: Option, + pub removable: Option, + pub headless: Option, + pub source: String, +} + +#[napi(object)] +pub struct PlatformGpuInfo { + pub devices: Vec, + pub source: String, + pub error: Option, +} + +#[napi(js_name = "getGpuInfo")] +pub fn get_gpu_info() -> PlatformGpuInfo { + platform::gpu_info() +} + +#[allow(dead_code)] +fn base_device( + active: bool, + vendor_id: u32, + device_id: u32, + source: &str, +) -> PlatformGpuDeviceInfo { + PlatformGpuDeviceInfo { + active, + vendor_id, + device_id, + vendor_name: fluxer_desktop_native::platform_info::probe_helpers::vendor_name(vendor_id) + .map(str::to_owned), + device_string: None, + driver_vendor: None, + driver_version: None, + dedicated_video_memory: None, + shared_system_memory: None, + subsystem_vendor_id: None, + subsystem_device_id: None, + registry_id: None, + adapter_luid: None, + pci_path: None, + integrated: None, + removable: None, + headless: None, + source: source.to_owned(), + } +} + +#[allow(dead_code)] +fn empty_info(source: &str) -> PlatformGpuInfo { + PlatformGpuInfo { + devices: Vec::new(), + source: source.to_owned(), + error: None, + } +} + +#[cfg(target_os = "macos")] +mod platform { + use fluxer_desktop_native::platform_info::probe_helpers::{ + vendor_id_from_name, vendor_name, write_hex_u64, + }; + use objc2::exception::catch; + use objc2::rc::autoreleasepool; + use objc2_metal::{MTLCopyAllDevices, MTLCreateSystemDefaultDevice, MTLDevice}; + + use super::{PlatformGpuDeviceInfo, PlatformGpuInfo, SOURCE_METAL, empty_info}; + + pub(super) fn gpu_info() -> PlatformGpuInfo { + match catch(|| autoreleasepool(|_| gpu_info_inner())) { + Ok(info) => info, + Err(exception) => { + let mut info = empty_info(SOURCE_METAL); + info.error = Some(exception.map_or_else( + || "macOS GPU probe failed".to_owned(), + |exception| exception.to_string(), + )); + info + } + } + } + + fn gpu_info_inner() -> PlatformGpuInfo { + let default_device = MTLCreateSystemDefaultDevice(); + let default_registry_id = default_device.as_deref().map(MTLDevice::registryID); + let all_devices = MTLCopyAllDevices(); + let mut devices = Vec::new(); + + for index in 0..all_devices.count() { + let device = all_devices.objectAtIndex(index); + let active = + default_registry_id.is_some_and(|registry_id| registry_id == device.registryID()); + devices.push(mac_device(&device, active)); + } + + if devices.is_empty() { + if let Some(device) = default_device { + devices.push(mac_device(&device, true)); + } + } else if !devices.iter().any(|device| device.active) { + devices[0].active = true; + } + + PlatformGpuInfo { + devices, + source: SOURCE_METAL.to_owned(), + error: None, + } + } + + fn mac_device( + device: &objc2::runtime::ProtocolObject, + active: bool, + ) -> PlatformGpuDeviceInfo { + let name = device.name().to_string(); + let vendor_id = vendor_id_from_name(&name); + PlatformGpuDeviceInfo { + active, + vendor_id, + device_id: 0, + vendor_name: vendor_name(vendor_id).map(str::to_owned), + device_string: Some(if name.is_empty() { + "Metal GPU".to_owned() + } else { + name + }), + driver_vendor: None, + driver_version: None, + dedicated_video_memory: None, + shared_system_memory: None, + subsystem_vendor_id: None, + subsystem_device_id: None, + registry_id: Some(write_hex_u64(device.registryID())), + adapter_luid: None, + pci_path: None, + integrated: Some(device.isLowPower() || vendor_id == 0x106b), + removable: Some(device.isRemovable()), + headless: Some(device.isHeadless()), + source: SOURCE_METAL.to_owned(), + } + } +} + +#[allow(dead_code)] +mod dxgi_helpers { + pub fn utf16_description(raw: &[u16; 128]) -> String { + let end = raw.iter().position(|ch| *ch == 0).unwrap_or(raw.len()); + let text = String::from_utf16_lossy(&raw[..end]); + if text.is_empty() { + "DXGI adapter".to_owned() + } else { + text + } + } + + pub fn luid_string(high_part: i32, low_part: u32) -> String { + format!("{:08x}:{low_part:08x}", high_part as u32) + } + + #[cfg(test)] + mod tests { + use super::*; + + #[test] + fn luid_format_matches_legacy_contract() { + assert_eq!("ffffffff:1234abcd", luid_string(-1, 0x1234_abcd)); + assert_eq!("00000002:00000001", luid_string(2, 1)); + } + + #[test] + fn utf16_description_trims_at_first_nul() { + let mut raw = [0u16; 128]; + raw[0] = 'G' as u16; + raw[1] = 'P' as u16; + raw[2] = 'U' as u16; + raw[3] = 0; + raw[4] = 'X' as u16; + assert_eq!("GPU", utf16_description(&raw)); + } + } +} + +#[cfg(target_os = "windows")] +mod platform { + use super::dxgi_helpers::{luid_string, utf16_description}; + use fluxer_desktop_native::platform_info::probe_helpers::vendor_name; + use windows::Win32::Graphics::Dxgi::{ + CreateDXGIFactory1, DXGI_ADAPTER_DESC1, DXGI_ADAPTER_FLAG_SOFTWARE, + DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, IDXGIAdapter1, IDXGIFactory1, IDXGIFactory6, + }; + + use super::{PlatformGpuDeviceInfo, PlatformGpuInfo, SOURCE_DXGI, empty_info}; + + pub(super) fn gpu_info() -> PlatformGpuInfo { + let mut info = empty_info(SOURCE_DXGI); + info.devices = enumerate_dxgi_adapters(); + info + } + + fn enumerate_dxgi_adapters() -> Vec { + unsafe { + if let Ok(factory) = CreateDXGIFactory1::() { + return enumerate_factory6(&factory); + } + if let Ok(factory) = CreateDXGIFactory1::() { + return enumerate_factory1(&factory); + } + } + Vec::new() + } + + unsafe fn enumerate_factory6(factory: &IDXGIFactory6) -> Vec { + let mut devices = Vec::new(); + let mut ordered_index = 0; + loop { + let Ok(adapter) = (unsafe { + factory.EnumAdapterByGpuPreference::( + ordered_index, + DXGI_GPU_PREFERENCE_HIGH_PERFORMANCE, + ) + }) else { + break; + }; + if let Ok(desc) = unsafe { adapter.GetDesc1() } { + push_dxgi_desc(&mut devices, &desc, ordered_index); + } + ordered_index += 1; + } + devices + } + + unsafe fn enumerate_factory1(factory: &IDXGIFactory1) -> Vec { + let mut devices = Vec::new(); + let mut ordered_index = 0; + loop { + let Ok(adapter) = (unsafe { factory.EnumAdapters1(ordered_index) }) else { + break; + }; + if let Ok(desc) = unsafe { adapter.GetDesc1() } { + push_dxgi_desc(&mut devices, &desc, ordered_index); + } + ordered_index += 1; + } + devices + } + + fn push_dxgi_desc( + devices: &mut Vec, + desc: &DXGI_ADAPTER_DESC1, + ordered_index: u32, + ) { + if (desc.Flags & DXGI_ADAPTER_FLAG_SOFTWARE.0 as u32) != 0 { + return; + } + let mut device = PlatformGpuDeviceInfo { + active: ordered_index == 0, + vendor_id: desc.VendorId, + device_id: desc.DeviceId, + vendor_name: vendor_name(desc.VendorId).map(str::to_owned), + device_string: Some(utf16_description(&desc.Description)), + driver_vendor: None, + driver_version: None, + dedicated_video_memory: Some(desc.DedicatedVideoMemory as f64), + shared_system_memory: Some(desc.SharedSystemMemory as f64), + subsystem_vendor_id: Some(desc.SubSysId >> 16), + subsystem_device_id: Some(desc.SubSysId & 0xffff), + registry_id: None, + adapter_luid: None, + pci_path: None, + integrated: None, + removable: None, + headless: None, + source: SOURCE_DXGI.to_owned(), + }; + device.adapter_luid = Some(luid_string( + desc.AdapterLuid.HighPart, + desc.AdapterLuid.LowPart, + )); + devices.push(device); + } +} + +#[cfg(target_os = "linux")] +mod platform { + use std::fs; + use std::path::Path; + + use fluxer_desktop_native::platform_info::probe_helpers::{ + basename, is_drm_card_name, parse_hex_id, vendor_name, + }; + + use super::{PlatformGpuDeviceInfo, PlatformGpuInfo, SOURCE_LINUX_SYSFS, base_device}; + + pub(super) fn gpu_info() -> PlatformGpuInfo { + linux_gpu_info_from_drm_root(Path::new("/sys/class/drm")) + } + + fn linux_gpu_info_from_drm_root(root: &Path) -> PlatformGpuInfo { + let mut info = PlatformGpuInfo { + devices: Vec::new(), + source: SOURCE_LINUX_SYSFS.to_owned(), + error: None, + }; + let Ok(entries) = fs::read_dir(root) else { + return info; + }; + + let mut card_names = entries + .filter_map(Result::ok) + .filter_map(|entry| entry.file_name().into_string().ok()) + .filter(|name| is_drm_card_name(name)) + .collect::>(); + card_names.sort_unstable_by_key(|name| card_sort_key(name)); + + for card_name in card_names { + if let Some(device) = linux_card(root, &card_name, info.devices.is_empty()) { + info.devices.push(device); + } + } + info + } + + fn linux_card(root: &Path, card_name: &str, active: bool) -> Option { + let base = root.join(card_name).join("device"); + let vendor_id = read_hex_file(base.join("vendor"))?; + let device_id = read_hex_file(base.join("device")).unwrap_or(0); + + let mut device = base_device(active, vendor_id, device_id, SOURCE_LINUX_SYSFS); + device.subsystem_vendor_id = + Some(read_hex_file(base.join("subsystem_vendor")).unwrap_or(0)); + device.subsystem_device_id = + Some(read_hex_file(base.join("subsystem_device")).unwrap_or(0)); + device.pci_path = read_link_basename(&base); + device.driver_vendor = read_link_basename(base.join("driver")); + + if let Some(uevent) = read_trimmed_file(base.join("uevent")) { + if let Some(driver) = uevent_value(&uevent, "DRIVER") { + device.driver_vendor = Some(driver.to_owned()); + } + if let Some(pci_id) = uevent_value(&uevent, "PCI_ID") { + device.device_string = Some(match vendor_name(vendor_id) { + Some(vendor) => format!("{vendor} GPU ({pci_id})"), + None => format!("GPU ({pci_id})"), + }); + } + } + + if let Some(vram) = read_trimmed_file(base.join("mem_info_vram_total")) + && let Ok(bytes) = vram.parse::() + { + device.dedicated_video_memory = Some(bytes as f64); + } + + Some(device) + } + + fn card_sort_key(name: &str) -> u32 { + name.strip_prefix("card") + .and_then(|rest| rest.parse::().ok()) + .unwrap_or(u32::MAX) + } + + fn read_hex_file(path: impl AsRef) -> Option { + let raw = fs::read_to_string(path).ok()?; + parse_hex_id(&raw).ok() + } + + fn read_trimmed_file(path: impl AsRef) -> Option { + let raw = fs::read_to_string(path).ok()?; + Some(raw.trim_matches([' ', '\t', '\r', '\n']).to_owned()) + } + + fn read_link_basename(path: impl AsRef) -> Option { + let target = fs::read_link(path).ok()?; + Some(path_basename(&target)) + } + + fn path_basename(path: &Path) -> String { + path.file_name() + .and_then(|name| name.to_str()) + .map(str::to_owned) + .unwrap_or_else(|| basename(&path.to_string_lossy()).to_owned()) + } + + fn uevent_value<'a>(blob: &'a str, key: &str) -> Option<&'a str> { + blob.lines().find_map(|line| { + let (line_key, value) = line.split_once('=')?; + if line_key == key { + Some(value.trim_matches([' ', '\t', '\r', '\n'])) + } else { + None + } + }) + } + + #[cfg(test)] + mod tests { + use std::fs::{create_dir_all, write}; + use std::os::unix::fs::symlink; + + use super::*; + + #[test] + fn uevent_value_matches_exact_keys() { + let blob = "PCI_ID=8086:46A6\nDRIVER=i915\nNOT_DRIVER=bad\n"; + assert_eq!(Some("i915"), uevent_value(blob, "DRIVER")); + assert_eq!(Some("8086:46A6"), uevent_value(blob, "PCI_ID")); + assert_eq!(None, uevent_value(blob, "MISSING")); + } + + #[test] + fn card_sort_key_keeps_card_order_numeric() { + let mut names = ["card10".to_owned(), "card2".to_owned(), "card0".to_owned()]; + names.sort_unstable_by_key(|name| card_sort_key(name)); + assert_eq!(["card0", "card2", "card10"], names); + } + + #[test] + fn linux_sysfs_fixture_ports_legacy_gpu_contract() { + let tmp = tempfile::tempdir().unwrap(); + let root = tmp.path(); + let pci0 = root.join("devices/pci0000:00/0000:00:02.0"); + let pci1 = root.join("devices/pci0000:01/0000:01:00.0"); + create_dir_all(root.join("card0")).unwrap(); + create_dir_all(root.join("card1")).unwrap(); + create_dir_all(&pci0).unwrap(); + create_dir_all(&pci1).unwrap(); + symlink( + "../devices/pci0000:00/0000:00:02.0", + root.join("card0/device"), + ) + .unwrap(); + symlink( + "../devices/pci0000:01/0000:01:00.0", + root.join("card1/device"), + ) + .unwrap(); + + let card0 = root.join("card0/device"); + let card1 = root.join("card1/device"); + write(card0.join("vendor"), "0x8086\n").unwrap(); + write(card0.join("device"), "0x46a6\n").unwrap(); + write(card0.join("subsystem_vendor"), "0x1028\n").unwrap(); + write(card0.join("subsystem_device"), "0x0b19\n").unwrap(); + write(card0.join("uevent"), "DRIVER=i915\nPCI_ID=8086:46A6\n").unwrap(); + write(card0.join("mem_info_vram_total"), "268435456\n").unwrap(); + + write(card1.join("vendor"), "0x10de\n").unwrap(); + write(card1.join("device"), "0x1f99\n").unwrap(); + write(card1.join("uevent"), "PCI_ID=10DE:1F99\n").unwrap(); + symlink("/sys/bus/pci/drivers/nvidia", card1.join("driver")).unwrap(); + + let info = linux_gpu_info_from_drm_root(root); + assert_eq!(SOURCE_LINUX_SYSFS, info.source); + assert_eq!(2, info.devices.len()); + assert!(info.devices[0].active); + assert_eq!(0x8086, info.devices[0].vendor_id); + assert_eq!(Some("Intel".to_owned()), info.devices[0].vendor_name); + assert_eq!( + Some("Intel GPU (8086:46A6)".to_owned()), + info.devices[0].device_string + ); + assert_eq!(Some("i915".to_owned()), info.devices[0].driver_vendor); + assert_eq!(Some("0000:00:02.0".to_owned()), info.devices[0].pci_path); + assert_eq!(Some(268_435_456.0), info.devices[0].dedicated_video_memory); + assert!(!info.devices[1].active); + assert_eq!(Some("nvidia".to_owned()), info.devices[1].driver_vendor); + } + } +} + +#[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))] +mod platform { + use super::{PlatformGpuInfo, SOURCE_LINUX_SYSFS, empty_info}; + + pub(super) fn gpu_info() -> PlatformGpuInfo { + empty_info(SOURCE_LINUX_SYSFS) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn base_device_sets_required_contract_fields() { + let device = base_device(true, 0x10de, 0x2684, SOURCE_DXGI); + assert!(device.active); + assert_eq!(0x10de, device.vendor_id); + assert_eq!(0x2684, device.device_id); + assert_eq!(Some("NVIDIA".to_owned()), device.vendor_name); + assert_eq!(SOURCE_DXGI, device.source); + } + + #[test] + fn empty_info_preserves_source_and_empty_devices() { + let info = empty_info(SOURCE_LINUX_SYSFS); + assert_eq!(SOURCE_LINUX_SYSFS, info.source); + assert!(info.devices.is_empty()); + assert!(info.error.is_none()); + } +} diff --git a/fluxer_desktop/native/platform-info/test/loader.test.mjs b/fluxer_desktop/native/platform-info/test/loader.test.mjs new file mode 100644 index 0000000..11cf074 --- /dev/null +++ b/fluxer_desktop/native/platform-info/test/loader.test.mjs @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import assert from 'node:assert/strict'; +import test from 'node:test'; +import platformInfo from '../index.js'; + +test('loader exposes a stable optional native surface', () => { + assert.equal(typeof platformInfo, 'object'); + assert.equal(platformInfo.getGpuInfo === null || typeof platformInfo.getGpuInfo === 'function', true); + assert.equal(platformInfo.loadError === null || platformInfo.loadError instanceof Error, true); +}); + +test('loader resolves native binaries for supported platforms and architectures', () => { + assert.equal(platformInfo._private.nativeFileName('darwin', 'x64'), 'platform-info.darwin-x64.node'); + assert.equal(platformInfo._private.nativeFileName('darwin', 'arm64'), 'platform-info.darwin-arm64.node'); + assert.equal(platformInfo._private.nativeFileName('linux', 'x64'), 'platform-info.linux-x64-gnu.node'); + assert.equal(platformInfo._private.nativeFileName('linux', 'arm64'), 'platform-info.linux-arm64-gnu.node'); + assert.equal(platformInfo._private.nativeFileName('win32', 'x64'), 'platform-info.win32-x64-msvc.node'); + assert.equal(platformInfo._private.nativeFileName('win32', 'arm64'), 'platform-info.win32-arm64-msvc.node'); +}); diff --git a/fluxer_desktop/native/rt-thread/Cargo.lock b/fluxer_desktop/native/rt-thread/Cargo.lock new file mode 100644 index 0000000..8f0e3cf --- /dev/null +++ b/fluxer_desktop/native/rt-thread/Cargo.lock @@ -0,0 +1,749 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "criterion" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3" +dependencies = [ + "alloca", + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "num-traits", + "oorandom", + "page_size", + "plotters", + "rayon", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fluxer_rt_thread" +version = "0.0.0" +dependencies = [ + "criterion", + "libc", + "windows", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0871acf327f283dc6da28a1696cdc64fb355ba9f935d052021fa77f35cce69" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zerocopy" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/fluxer_desktop/native/rt-thread/Cargo.toml b/fluxer_desktop/native/rt-thread/Cargo.toml new file mode 100644 index 0000000..e2f5106 --- /dev/null +++ b/fluxer_desktop/native/rt-thread/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "fluxer_rt_thread" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["rlib"] + +[dependencies] + +[target.'cfg(unix)'.dependencies] +libc = "0.2" + +[target.'cfg(target_os = "windows")'.dependencies] +windows = {version = "0.62.2", features = [ + "Win32_Foundation", + "Win32_System_Threading", + "Win32_Media", + "Win32_Media_Multimedia", +]} + +[dev-dependencies] +criterion = "0.8" + +[[bench]] +name = "tick" +harness = false diff --git a/fluxer_desktop/native/rt-thread/benches/baseline.json b/fluxer_desktop/native/rt-thread/benches/baseline.json new file mode 100644 index 0000000..1475cf6 --- /dev/null +++ b/fluxer_desktop/native/rt-thread/benches/baseline.json @@ -0,0 +1,24 @@ +{ + "measured_at": "85e057a273fd", + "host": "darwin-arm64-apple-silicon", + "regression_budget_percent": 5.0, + "criterion_args": { + "warm_up_time_sec": 2, + "measurement_time_sec": 5 + }, + "benches": { + "tick_driver/audio_21_33ms": { + "median_ns": 21415000.0, + "low_ns": 21395000.0, + "high_ns": 21441000.0, + "note": "Measures real-time sleep precision against AUDIO_TICK_PERIOD_NS=21333333; baseline reflects clock+scheduler delay on this host, not pure CPU work." + }, + "system_clock_now_ns": { + "median_ns": 22.931, + "low_ns": 22.763, + "high_ns": 23.099, + "budget_percent_override": 15.0, + "note": "23ns op; clock_gettime variance dominates over 5%. Wider budget reflects the floor of measurement noise." + } + } +} diff --git a/fluxer_desktop/native/rt-thread/benches/tick.rs b/fluxer_desktop/native/rt-thread/benches/tick.rs new file mode 100644 index 0000000..5483387 --- /dev/null +++ b/fluxer_desktop/native/rt-thread/benches/tick.rs @@ -0,0 +1,41 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::hint::black_box; +use std::sync::Arc; + +use criterion::{Criterion, criterion_group, criterion_main}; +use fluxer_rt_thread::{MonotonicClock, SystemMonotonicClock, TickDriver}; + +const AUDIO_TICK_PERIOD_NS: u64 = 21_333_333; + +fn bench_audio_tick(c: &mut Criterion) { + let mut group = c.benchmark_group("tick_driver"); + group.sample_size(20); + group.bench_function("audio_21_33ms", |b| { + b.iter_custom(|iters| { + let clock = Arc::new(SystemMonotonicClock::new()); + let mut driver = TickDriver::new(clock.clone(), AUDIO_TICK_PERIOD_NS).expect("driver"); + let _ = driver.wait_until_next_tick().expect("warmup"); + let start = std::time::Instant::now(); + for _ in 0..iters { + let info = driver.wait_until_next_tick().expect("tick"); + black_box(info); + } + start.elapsed() + }) + }); + group.finish(); +} + +fn bench_now_ns(c: &mut Criterion) { + let clock = SystemMonotonicClock::new(); + c.bench_function("system_clock_now_ns", |b| { + b.iter(|| { + let v = MonotonicClock::now_ns(&clock); + black_box(v) + }) + }); +} + +criterion_group!(benches, bench_audio_tick, bench_now_ns); +criterion_main!(benches); diff --git a/fluxer_desktop/native/rt-thread/src/lib.rs b/fluxer_desktop/native/rt-thread/src/lib.rs new file mode 100644 index 0000000..212694b --- /dev/null +++ b/fluxer_desktop/native/rt-thread/src/lib.rs @@ -0,0 +1,12 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![deny(clippy::too_many_lines)] + +pub mod priority; +pub mod tick; + +pub use priority::{PriorityProfile, RealtimePriorityGuard, RtError, RtOutcome}; +pub use tick::{MonotonicClock, SystemMonotonicClock, TickDriver, TickInfo}; + +pub const NS_PER_MS: u64 = 1_000_000; +pub const NS_PER_SEC: u64 = 1_000_000_000; diff --git a/fluxer_desktop/native/rt-thread/src/priority.rs b/fluxer_desktop/native/rt-thread/src/priority.rs new file mode 100644 index 0000000..9e5f51e --- /dev/null +++ b/fluxer_desktop/native/rt-thread/src/priority.rs @@ -0,0 +1,399 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use core::cell::Cell; +use core::fmt; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PriorityProfile { + Audio, + Video, + Network, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum RtError { + AlreadyAcquired, + PlatformDenied(i32), + Unsupported, +} + +impl fmt::Display for RtError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + RtError::AlreadyAcquired => { + write!(f, "RealtimePriorityGuard already acquired on this thread") + } + RtError::PlatformDenied(errno) => { + write!(f, "platform denied RT priority (errno={errno})") + } + RtError::Unsupported => write!(f, "RT priority not supported on this platform/profile"), + } + } +} + +impl std::error::Error for RtError {} + +#[derive(Debug, PartialEq, Eq)] +pub enum RtOutcome { + Acquired, + PartialFallback, +} + +thread_local! { + static GUARD_ACTIVE: Cell = const { Cell::new(false) }; +} + +pub struct RealtimePriorityGuard { + inner: PlatformGuard, + outcome: RtOutcome, +} + +impl RealtimePriorityGuard { + pub fn acquire(profile: PriorityProfile) -> Result { + let already = GUARD_ACTIVE.with(|c| c.replace(true)); + if already { + GUARD_ACTIVE.with(|c| c.set(true)); + return Err(RtError::AlreadyAcquired); + } + let acquired = PlatformGuard::acquire(profile); + match acquired { + Ok((inner, outcome)) => { + assert!(matches!( + outcome, + RtOutcome::Acquired | RtOutcome::PartialFallback + )); + Ok(Self { inner, outcome }) + } + Err(e) => { + GUARD_ACTIVE.with(|c| c.set(false)); + Err(e) + } + } + } + + pub fn outcome(&self) -> &RtOutcome { + assert!(GUARD_ACTIVE.with(Cell::get)); + &self.outcome + } +} + +impl Drop for RealtimePriorityGuard { + fn drop(&mut self) { + self.inner.restore(); + GUARD_ACTIVE.with(|c| c.set(false)); + } +} + +#[cfg(target_os = "macos")] +mod platform { + use super::{PriorityProfile, RtError, RtOutcome}; + use libc::qos_class_t::{ + QOS_CLASS_DEFAULT, QOS_CLASS_USER_INITIATED, QOS_CLASS_USER_INTERACTIVE, QOS_CLASS_UTILITY, + }; + + pub struct PlatformGuard { + prior_qos: libc::qos_class_t, + prior_relative: i32, + } + + impl PlatformGuard { + pub fn acquire(profile: PriorityProfile) -> Result<(Self, RtOutcome), RtError> { + assert!(matches!( + profile, + PriorityProfile::Audio | PriorityProfile::Video | PriorityProfile::Network + )); + let mut prior_qos: libc::qos_class_t = QOS_CLASS_DEFAULT; + let mut prior_relative: i32 = 0; + let read_rc = unsafe { + libc::pthread_get_qos_class_np( + libc::pthread_self(), + &mut prior_qos, + &mut prior_relative, + ) + }; + if read_rc != 0 { + return Err(RtError::PlatformDenied(read_rc)); + } + let target = map_profile(profile); + let set_rc = unsafe { libc::pthread_set_qos_class_self_np(target, 0) }; + if set_rc != 0 { + return Err(RtError::PlatformDenied(set_rc)); + } + Ok(( + Self { + prior_qos, + prior_relative, + }, + RtOutcome::Acquired, + )) + } + + pub fn restore(&mut self) { + unsafe { + let _ = libc::pthread_set_qos_class_self_np(self.prior_qos, self.prior_relative); + } + } + } + + fn map_profile(profile: PriorityProfile) -> libc::qos_class_t { + match profile { + PriorityProfile::Audio => QOS_CLASS_USER_INTERACTIVE, + PriorityProfile::Video => QOS_CLASS_USER_INITIATED, + PriorityProfile::Network => QOS_CLASS_UTILITY, + } + } +} + +#[cfg(all(unix, not(target_os = "macos")))] +mod platform { + use super::{PriorityProfile, RtError, RtOutcome}; + + pub struct PlatformGuard { + prior_policy: i32, + prior_param: libc::sched_param, + used_fallback: bool, + prior_nice: Option, + } + + impl PlatformGuard { + pub fn acquire(profile: PriorityProfile) -> Result<(Self, RtOutcome), RtError> { + assert!(matches!( + profile, + PriorityProfile::Audio | PriorityProfile::Video | PriorityProfile::Network + )); + let mut prior_policy: i32 = 0; + let mut prior_param: libc::sched_param = unsafe { core::mem::zeroed() }; + let read_rc = unsafe { + libc::pthread_getschedparam( + libc::pthread_self(), + &mut prior_policy, + &mut prior_param, + ) + }; + if read_rc != 0 { + return Err(RtError::PlatformDenied(read_rc)); + } + let (policy, sched_priority) = map_profile(profile); + assert!(sched_priority >= 0); + let mut param: libc::sched_param = unsafe { core::mem::zeroed() }; + param.sched_priority = sched_priority; + let set_rc = + unsafe { libc::pthread_setschedparam(libc::pthread_self(), policy, ¶m) }; + if set_rc == 0 { + return Ok(( + Self { + prior_policy, + prior_param, + used_fallback: false, + prior_nice: None, + }, + RtOutcome::Acquired, + )); + } + if set_rc != libc::EPERM { + return Err(RtError::PlatformDenied(set_rc)); + } + let fallback_nice = match profile { + PriorityProfile::Audio => -19, + PriorityProfile::Video => -10, + PriorityProfile::Network => -5, + }; + let prior_nice = unsafe { + *libc::__errno_location() = 0; + let cur = libc::getpriority(libc::PRIO_PROCESS, 0); + let saved_errno = *libc::__errno_location(); + if cur == -1 && saved_errno != 0 { + return Err(RtError::PlatformDenied(saved_errno)); + } + cur + }; + let _ = unsafe { libc::setpriority(libc::PRIO_PROCESS, 0, fallback_nice) }; + Ok(( + Self { + prior_policy, + prior_param, + used_fallback: true, + prior_nice: Some(prior_nice), + }, + RtOutcome::PartialFallback, + )) + } + + pub fn restore(&mut self) { + if self.used_fallback { + if let Some(prior_nice) = self.prior_nice { + unsafe { libc::setpriority(libc::PRIO_PROCESS, 0, prior_nice) }; + } + } else { + unsafe { + let _ = libc::pthread_setschedparam( + libc::pthread_self(), + self.prior_policy, + &self.prior_param, + ); + } + } + } + } + + fn map_profile(profile: PriorityProfile) -> (i32, i32) { + let policy = libc::SCHED_FIFO; + let max = unsafe { libc::sched_get_priority_max(policy) }; + let min = unsafe { libc::sched_get_priority_min(policy) }; + assert!(max >= min); + let span = (max - min).max(1); + let prio = match profile { + PriorityProfile::Audio => max, + PriorityProfile::Video => min + (span * 2) / 3, + PriorityProfile::Network => min + span / 3, + }; + (policy, prio) + } +} + +#[cfg(target_os = "windows")] +mod platform { + use super::{PriorityProfile, RtError, RtOutcome}; + use windows::Win32::Foundation::HANDLE; + use windows::Win32::System::Threading::{ + AvRevertMmThreadCharacteristics, AvSetMmThreadCharacteristicsW, + }; + use windows::core::PCWSTR; + + pub struct PlatformGuard { + handle: HANDLE, + } + + impl PlatformGuard { + pub fn acquire(profile: PriorityProfile) -> Result<(Self, RtOutcome), RtError> { + assert!(matches!( + profile, + PriorityProfile::Audio | PriorityProfile::Video | PriorityProfile::Network + )); + let task_name: &[u16] = match profile { + PriorityProfile::Audio => &AUDIO_W, + PriorityProfile::Video => &CAPTURE_W, + PriorityProfile::Network => &PLAYBACK_W, + }; + let mut task_index: u32 = 0; + let handle = unsafe { + AvSetMmThreadCharacteristicsW(PCWSTR(task_name.as_ptr()), &mut task_index) + }; + match handle { + Ok(h) if !h.is_invalid() => Ok((Self { handle: h }, RtOutcome::Acquired)), + Ok(_) => Err(RtError::PlatformDenied(0)), + Err(e) => Err(RtError::PlatformDenied(e.code().0)), + } + } + + pub fn restore(&mut self) { + if !self.handle.is_invalid() { + unsafe { + let _ = AvRevertMmThreadCharacteristics(self.handle); + } + } + } + } + + const AUDIO_W: [u16; 6] = [ + b'A' as u16, + b'u' as u16, + b'd' as u16, + b'i' as u16, + b'o' as u16, + 0, + ]; + const CAPTURE_W: [u16; 8] = [ + b'C' as u16, + b'a' as u16, + b'p' as u16, + b't' as u16, + b'u' as u16, + b'r' as u16, + b'e' as u16, + 0, + ]; + const PLAYBACK_W: [u16; 9] = [ + b'P' as u16, + b'l' as u16, + b'a' as u16, + b'y' as u16, + b'b' as u16, + b'a' as u16, + b'c' as u16, + b'k' as u16, + 0, + ]; +} + +#[cfg(not(any(unix, target_os = "windows")))] +mod platform { + use super::{PriorityProfile, RtError, RtOutcome}; + + pub struct PlatformGuard {} + + impl PlatformGuard { + pub fn acquire(_profile: PriorityProfile) -> Result<(Self, RtOutcome), RtError> { + Err(RtError::Unsupported) + } + + pub fn restore(&mut self) {} + } +} + +use platform::PlatformGuard; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn acquire_and_release_audio_does_not_panic() { + let guard = RealtimePriorityGuard::acquire(PriorityProfile::Audio); + match guard { + Ok(g) => { + assert!(matches!( + *g.outcome(), + RtOutcome::Acquired | RtOutcome::PartialFallback + )); + drop(g); + } + Err(RtError::PlatformDenied(_)) => {} + Err(other) => panic!("unexpected error: {other:?}"), + } + let again = RealtimePriorityGuard::acquire(PriorityProfile::Audio); + if let Ok(g) = again { + drop(g); + } + } + + #[test] + fn double_acquire_on_same_thread_errors() { + let first = RealtimePriorityGuard::acquire(PriorityProfile::Audio); + if let Ok(first_guard) = first { + let second = RealtimePriorityGuard::acquire(PriorityProfile::Audio); + assert_eq!(second.err(), Some(RtError::AlreadyAcquired)); + drop(first_guard); + let third = RealtimePriorityGuard::acquire(PriorityProfile::Network); + if let Ok(t) = third { + drop(t); + } + } + } + + #[test] + fn all_profiles_round_trip() { + for profile in [ + PriorityProfile::Audio, + PriorityProfile::Video, + PriorityProfile::Network, + ] { + let g = RealtimePriorityGuard::acquire(profile); + match g { + Ok(g) => drop(g), + Err(RtError::PlatformDenied(_)) => {} + Err(other) => panic!("unexpected error for {profile:?}: {other:?}"), + } + } + } +} diff --git a/fluxer_desktop/native/rt-thread/src/tick.rs b/fluxer_desktop/native/rt-thread/src/tick.rs new file mode 100644 index 0000000..d4393bc --- /dev/null +++ b/fluxer_desktop/native/rt-thread/src/tick.rs @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use core::fmt; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::{Arc, Mutex}; +use std::thread; +use std::time::{Duration, Instant}; + +use crate::NS_PER_MS; + +pub trait MonotonicClock: Send + Sync { + fn now_ns(&self) -> u64; +} + +pub struct SystemMonotonicClock { + origin: Instant, + last_observed: AtomicU64, +} + +impl SystemMonotonicClock { + pub fn new() -> Self { + let me = Self { + origin: Instant::now(), + last_observed: AtomicU64::new(0), + }; + assert_eq!(me.last_observed.load(Ordering::Acquire), 0); + assert!(me.origin.elapsed().as_nanos() < u64::MAX as u128); + me + } +} + +impl Default for SystemMonotonicClock { + fn default() -> Self { + Self::new() + } +} + +impl MonotonicClock for SystemMonotonicClock { + fn now_ns(&self) -> u64 { + let elapsed = self.origin.elapsed(); + let ns = elapsed.as_nanos(); + assert!(ns <= u64::MAX as u128); + let ns_u64 = ns as u64; + let prior = self.last_observed.fetch_max(ns_u64, Ordering::AcqRel); + let observed = ns_u64.max(prior); + assert!(observed >= prior); + observed + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct TickInfo { + pub tick_index: u64, + pub scheduled_ns: u64, + pub actual_ns: u64, + pub lag_ns: i64, +} + +#[derive(Debug, PartialEq, Eq)] +pub enum TickError { + ZeroPeriod, + PeriodTooLarge, + NonMonotonicClock { prior_ns: u64, observed_ns: u64 }, +} + +impl fmt::Display for TickError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + TickError::ZeroPeriod => write!(f, "tick period must be non-zero"), + TickError::PeriodTooLarge => write!(f, "tick period exceeds u64 budget"), + TickError::NonMonotonicClock { + prior_ns, + observed_ns, + } => write!( + f, + "MonotonicClock returned non-monotonic reading: prior={prior_ns} observed={observed_ns}", + ), + } + } +} + +impl std::error::Error for TickError {} + +pub const TICK_PERIOD_NS_MAX: u64 = 60 * crate::NS_PER_SEC; + +const SLEEP_SAFETY_MARGIN_NS: u64 = NS_PER_MS; + +const SPIN_THRESHOLD_NS: u64 = 150_000; + +pub struct TickDriver { + clock: Arc, + period_ns: u64, + start_ns: u64, + next_tick_index: u64, + last_scheduled_ns: u64, + last_actual_ns: u64, +} + +impl TickDriver { + pub fn new(clock: Arc, period_ns: u64) -> Result { + if period_ns == 0 { + return Err(TickError::ZeroPeriod); + } + if period_ns > TICK_PERIOD_NS_MAX { + return Err(TickError::PeriodTooLarge); + } + let start_ns = clock.now_ns(); + assert!(period_ns > 0); + assert!(period_ns <= TICK_PERIOD_NS_MAX); + Ok(Self { + clock, + period_ns, + start_ns, + next_tick_index: 0, + last_scheduled_ns: 0, + last_actual_ns: start_ns, + }) + } + + pub fn period_ns(&self) -> u64 { + assert!(self.period_ns > 0); + assert!(self.period_ns <= TICK_PERIOD_NS_MAX); + self.period_ns + } + + pub fn start_ns(&self) -> u64 { + assert!(self.start_ns <= self.clock.now_ns().saturating_add(self.period_ns)); + self.start_ns + } + + pub fn wait_until_next_tick(&mut self) -> Result { + assert!(self.period_ns > 0); + let mut prior_actual = self.last_actual_ns; + let mut tick_index = self.next_tick_index; + let scheduled_ns = self + .start_ns + .saturating_add(tick_index.saturating_mul(self.period_ns)); + assert!(scheduled_ns >= self.last_scheduled_ns || tick_index == 0); + loop { + let now = self.clock.now_ns(); + if now < prior_actual { + return Err(TickError::NonMonotonicClock { + prior_ns: prior_actual, + observed_ns: now, + }); + } + prior_actual = now; + if now >= scheduled_ns { + break; + } + let remaining = scheduled_ns - now; + if remaining > SLEEP_SAFETY_MARGIN_NS { + thread::park_timeout(Duration::from_nanos(remaining - SLEEP_SAFETY_MARGIN_NS)); + } else if remaining > SPIN_THRESHOLD_NS { + thread::park_timeout(Duration::from_nanos(remaining - SPIN_THRESHOLD_NS)); + } else { + core::hint::spin_loop(); + } + } + let actual_ns = self.clock.now_ns(); + if actual_ns < prior_actual { + return Err(TickError::NonMonotonicClock { + prior_ns: prior_actual, + observed_ns: actual_ns, + }); + } + let lag_ns = (actual_ns as i128 - scheduled_ns as i128) as i64; + let info = TickInfo { + tick_index, + scheduled_ns, + actual_ns, + lag_ns, + }; + if actual_ns > scheduled_ns.saturating_add(self.period_ns) { + let overshoot = (actual_ns - scheduled_ns) / self.period_ns; + tick_index = tick_index.saturating_add(overshoot); + } + self.next_tick_index = tick_index.saturating_add(1); + self.last_scheduled_ns = scheduled_ns; + self.last_actual_ns = actual_ns; + assert!(self.next_tick_index > tick_index); + assert_eq!( + info.lag_ns, + info.actual_ns as i64 - info.scheduled_ns as i64 + ); + Ok(info) + } + + pub fn next_tick_index(&self) -> u64 { + assert!(self.next_tick_index >= 1 || self.last_actual_ns == self.start_ns); + self.next_tick_index + } +} + +pub struct ScriptedClock { + readings: Mutex>, + cursor: AtomicU64, +} + +impl ScriptedClock { + pub fn new(readings: Vec) -> Self { + assert!(!readings.is_empty()); + Self { + readings: Mutex::new(readings), + cursor: AtomicU64::new(0), + } + } +} + +impl MonotonicClock for ScriptedClock { + fn now_ns(&self) -> u64 { + let readings = self.readings.lock().expect("readings lock poisoned"); + assert!(!readings.is_empty()); + let idx = self.cursor.fetch_add(1, Ordering::AcqRel) as usize; + let n = readings.len(); + let chosen = if idx < n { + readings[idx] + } else { + *readings.last().unwrap() + }; + assert!(n > 0); + chosen + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::Arc; + use std::time::Duration; + + fn ci_mode() -> bool { + std::env::var_os("CI").is_some() + } + + #[test] + fn zero_period_rejected() { + let clock = Arc::new(SystemMonotonicClock::new()); + let err = TickDriver::new(clock, 0).err(); + assert_eq!(err, Some(TickError::ZeroPeriod)); + } + + #[test] + fn period_too_large_rejected() { + let clock = Arc::new(SystemMonotonicClock::new()); + let err = TickDriver::new(clock, TICK_PERIOD_NS_MAX + 1).err(); + assert_eq!(err, Some(TickError::PeriodTooLarge)); + } + + #[test] + fn system_clock_is_monotonic_over_many_samples() { + let clock = SystemMonotonicClock::new(); + let mut prev = clock.now_ns(); + let samples = 10_000; + for _ in 0..samples { + let now = clock.now_ns(); + assert!(now >= prev, "clock went backwards: prev={prev} now={now}"); + prev = now; + } + } + + #[test] + fn tick_driver_meets_jitter_budget_at_1ms_period() { + let clock = Arc::new(SystemMonotonicClock::new()); + let period_ns = NS_PER_MS; + let mut driver = TickDriver::new(clock, period_ns).expect("driver"); + let n: u64 = 100; + let mut total_error_ns: i128 = 0; + let mut max_error_ns: i64 = 0; + for _ in 0..n { + let info = driver.wait_until_next_tick().expect("tick ok"); + assert!(info.lag_ns >= -(period_ns as i64)); + let err = info.lag_ns.abs(); + total_error_ns += err as i128; + if err > max_error_ns { + max_error_ns = err; + } + } + let mean_error_ns = (total_error_ns / n as i128) as i64; + let max_budget_ns: i64 = if ci_mode() { + 25 * NS_PER_MS as i64 + } else { + NS_PER_MS as i64 + }; + let mean_budget_ns: i64 = if ci_mode() { + 5 * NS_PER_MS as i64 + } else { + 200_000 + }; + assert!( + mean_error_ns < mean_budget_ns, + "mean error {mean_error_ns} ns exceeded budget {mean_budget_ns} ns (ci={})", + ci_mode(), + ); + assert!( + max_error_ns < max_budget_ns, + "max error {max_error_ns} ns exceeded budget {max_budget_ns} ns (ci={})", + ci_mode(), + ); + } + + #[test] + fn lag_reported_when_caller_sleeps_over_a_tick_boundary() { + let clock = Arc::new(SystemMonotonicClock::new()); + let period_ns = 5 * NS_PER_MS; + let mut driver = TickDriver::new(clock, period_ns).expect("driver"); + let _first = driver.wait_until_next_tick().expect("first tick"); + thread::sleep(Duration::from_millis(20)); + let info = driver.wait_until_next_tick().expect("after sleep"); + assert!( + info.lag_ns > 0, + "expected positive lag, got {}", + info.lag_ns + ); + } + + #[test] + fn tick_index_is_monotonic() { + let clock = Arc::new(SystemMonotonicClock::new()); + let mut driver = TickDriver::new(clock, NS_PER_MS).expect("driver"); + let mut prior_index = None::; + let mut prior_scheduled = None::; + for _ in 0..16 { + let info = driver.wait_until_next_tick().expect("tick"); + if let Some(prev) = prior_index { + assert!( + info.tick_index > prev, + "tick_index not monotonic: prev={prev} cur={}", + info.tick_index + ); + } + if let Some(prev) = prior_scheduled { + assert!(info.scheduled_ns > prev, "scheduled_ns not monotonic"); + } + prior_index = Some(info.tick_index); + prior_scheduled = Some(info.scheduled_ns); + } + } + + #[test] + fn non_monotonic_clock_is_detected() { + let clock = Arc::new(ScriptedClock::new(vec![ + 0, 10_000_000, 5_000_000, 5_000_000, + ])); + let mut driver = TickDriver::new(clock, NS_PER_MS).expect("driver"); + let err = driver.wait_until_next_tick().err(); + assert!( + matches!(err, Some(TickError::NonMonotonicClock { .. })), + "got {err:?}" + ); + } + + #[test] + fn tick_info_lag_invariant_holds() { + let clock = Arc::new(SystemMonotonicClock::new()); + let mut driver = TickDriver::new(clock, NS_PER_MS).expect("driver"); + for _ in 0..5 { + let info = driver.wait_until_next_tick().expect("tick"); + let derived = info.actual_ns as i64 - info.scheduled_ns as i64; + assert_eq!(info.lag_ns, derived); + } + } +} diff --git a/fluxer_desktop/native/rust/Cargo.lock b/fluxer_desktop/native/rust/Cargo.lock new file mode 100644 index 0000000..23da64f --- /dev/null +++ b/fluxer_desktop/native/rust/Cargo.lock @@ -0,0 +1,1164 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "criterion" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3" +dependencies = [ + "alloca", + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "num-traits", + "oorandom", + "page_size", + "plotters", + "rayon", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fluxer_desktop_native" +version = "0.1.0" +dependencies = [ + "criterion", + "proptest", + "sha2", + "tempfile", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "wasip2", + "wasip3", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "typenum", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "js-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "plotters" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5aeb6f403d7a4911efb1e33402027fc44f29b5bf6def3effcc22d7bb75f2b747" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df42e13c12958a16b3f7f4386b9ab1f3e7933914ecea48da7139435263a4172a" + +[[package]] +name = "plotters-svg" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51bae2ac328883f7acdfea3d66a7c35751187f870bc81f94563733a154d7a670" +dependencies = [ + "plotters-backend", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proptest" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b45fcc2344c680f5025fe57779faef368840d0bd1f42f216291f0dc4ace4744" +dependencies = [ + "bit-set", + "bit-vec", + "bitflags", + "num-traits", + "rand", + "rand_chacha", + "rand_xorshift", + "regex-syntax", + "rusty-fork", + "tempfile", + "unarray", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_xorshift" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "513962919efc330f829edb2535844d1b912b0fbe2ca165d613e4e8788bb05a5a" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags", + "errno", + "libc", + "linux-raw-sys", + "windows-sys", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "rusty-fork" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc6bf79ff24e648f6da1f8d1f011e9cac26491b619e6b9280f2b47f1774e6ee2" +dependencies = [ + "fnv", + "quick-error", + "tempfile", + "wait-timeout", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix", + "windows-sys", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unarray" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0871acf327f283dc6da28a1696cdc64fb355ba9f935d052021fa77f35cce69" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "zerocopy" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/fluxer_desktop/native/rust/Cargo.toml b/fluxer_desktop/native/rust/Cargo.toml new file mode 100644 index 0000000..35666df --- /dev/null +++ b/fluxer_desktop/native/rust/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "fluxer_desktop_native" +version = "0.1.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[dependencies] +sha2 = "0.11" + +[dev-dependencies] +criterion = "0.8" +proptest = "1.11" +tempfile = "3.27" + +[[bench]] +name = "native_core" +harness = false diff --git a/fluxer_desktop/native/rust/benches/baseline.json b/fluxer_desktop/native/rust/benches/baseline.json new file mode 100644 index 0000000..8f37b97 --- /dev/null +++ b/fluxer_desktop/native/rust/benches/baseline.json @@ -0,0 +1,36 @@ +{ + "measured_at": "85e057a273fd", + "host": "darwin-arm64-apple-silicon", + "regression_budget_percent": 5.0, + "criterion_args": { + "warm_up_time_sec": 2, + "measurement_time_sec": 5 + }, + "benches": { + "routing/default_sink_playback_stream": { + "median_ns": 41.6, + "low_ns": 41.327, + "high_ns": 41.862 + }, + "ring/fill_drain_1024": { + "median_ns": 3866.6, + "low_ns": 3844.2, + "high_ns": 3891.2 + }, + "audio/mono_to_stereo_1s": { + "median_ns": 1119800.0, + "low_ns": 1112300.0, + "high_ns": 1128900.0 + }, + "evdev/keymap_roundtrip_table": { + "median_ns": 11119.0, + "low_ns": 11054.0, + "high_ns": 11194.0 + }, + "mac_process_tree/collect_512_chain": { + "median_ns": 48667.0, + "low_ns": 48397.0, + "high_ns": 48975.0 + } + } +} diff --git a/fluxer_desktop/native/rust/benches/native_core.rs b/fluxer_desktop/native/rust/benches/native_core.rs new file mode 100644 index 0000000..6bcfa00 --- /dev/null +++ b/fluxer_desktop/native/rust/benches/native_core.rs @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::hint::black_box; + +use criterion::{Criterion, criterion_group, criterion_main}; +use fluxer_desktop_native::input::ring::Ring; +use fluxer_desktop_native::linux_audio::routing::{ + MEDIA_CLASS_PLAYBACK_STREAM, SelfIdentity, map, should_route_node, system_rule, +}; +use fluxer_desktop_native::linux_evdev::keymap::{KEY_MAP, keycode_to_name, name_to_keycode}; +use fluxer_desktop_native::mac_app_audio::audio_converter::{ + AudioBuffer, AudioBufferListN, build_input_asbd, convert_buffer_list_to_interleaved_f32, +}; +use fluxer_desktop_native::mac_app_audio::process_tree::{ + Info, collect_related_pids_with_resolver, +}; + +fn bench_routing(c: &mut Criterion) { + let self_identity = SelfIdentity::default(); + let rule = system_rule(); + let props = map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ( + "target.object", + "alsa_output.pci-0000_00_1f.3.analog-stereo", + ), + ]); + c.bench_function("routing/default_sink_playback_stream", |b| { + b.iter(|| { + black_box(should_route_node( + 100, + black_box(&props), + black_box(&rule), + "alsa_output.pci-0000_00_1f.3.analog-stereo", + "", + 1, + &self_identity, + )) + }) + }); +} + +fn bench_ring(c: &mut Criterion) { + c.bench_function("ring/fill_drain_1024", |b| { + b.iter(|| { + let mut ring: Ring = Ring::new(); + for index in 0..1024 { + let slot = ring.claim().unwrap() as usize; + ring.slots[slot] = index; + } + let mut sum = 0_u32; + while let Some(slot) = ring.pop() { + sum = sum.wrapping_add(ring.slots[slot as usize]); + ring.release(); + } + black_box(sum) + }) + }); +} + +fn bench_audio_convert(c: &mut Criterion) { + let samples: Vec = (0..48_000).map(|i| (i as f32 / 48_000.0).sin()).collect(); + let list = AudioBufferListN { + m_number_buffers: 1, + buffers: [AudioBuffer::from_slice(1, &samples)], + }; + let asbd = build_input_asbd(48_000.0, 1, false); + let mut out = vec![0.0_f32; samples.len() * 2]; + c.bench_function("audio/mono_to_stereo_1s", |b| { + b.iter(|| { + black_box( + convert_buffer_list_to_interleaved_f32( + asbd, + &list, + samples.len() as u32, + 48_000.0, + 2, + &mut out, + ) + .unwrap(), + ) + }) + }); +} + +fn bench_evdev_keymap(c: &mut Criterion) { + c.bench_function("evdev/keymap_roundtrip_table", |b| { + b.iter(|| { + let mut sum = 0_u16; + for entry in KEY_MAP { + sum ^= black_box(name_to_keycode(black_box(entry.name))); + black_box(keycode_to_name(black_box(entry.code))); + } + black_box(sum) + }) + }); +} + +fn bench_process_tree_collect(c: &mut Criterion) { + let infos: Vec = (0..512_i32) + .map(|index| Info { + pid: 10_000 + index, + parent_pid: if index == 0 { 1 } else { 10_000 + index - 1 }, + process_group_id: 10_000, + }) + .collect(); + let candidates: Vec = infos.iter().map(|info| info.pid).rev().collect(); + c.bench_function("mac_process_tree/collect_512_chain", |b| { + b.iter(|| { + let resolver = |pid| infos.iter().copied().find(|info| info.pid == pid); + black_box(collect_related_pids_with_resolver( + 10_000, + Some(infos[0]), + black_box(&candidates), + 512, + resolver, + )) + }) + }); +} + +criterion_group!( + benches, + bench_routing, + bench_ring, + bench_audio_convert, + bench_evdev_keymap, + bench_process_tree_collect +); +criterion_main!(benches); diff --git a/fluxer_desktop/native/rust/examples/native_core_stress.rs b/fluxer_desktop/native/rust/examples/native_core_stress.rs new file mode 100644 index 0000000..b36b395 --- /dev/null +++ b/fluxer_desktop/native/rust/examples/native_core_stress.rs @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::sync::Arc; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::thread; + +use fluxer_desktop_native::input::ring::Ring; +use fluxer_desktop_native::linux_evdev::event::{EV_KEY, InputEvent, parse_input_events}; +use fluxer_desktop_native::mac_app_audio::process_tree::{ + Info, collect_related_pids_with_resolver, +}; +use fluxer_desktop_native::mac_app_audio::source_state::{Machine, State}; + +fn stress_source_state() { + for _ in 0..10_000 { + let machine = Arc::new(Machine::new()); + machine.request_start().expect("request start"); + let stop_wins = Arc::new(AtomicU64::new(0)); + let run_machine = Arc::clone(&machine); + let stop_machine = Arc::clone(&machine); + let stop_counter = Arc::clone(&stop_wins); + let run_thread = thread::spawn(move || { + let _ = run_machine.mark_running(); + }); + let stop_thread = thread::spawn(move || { + for _ in 0..1000 { + if stop_machine.request_stop().is_ok() { + stop_counter.fetch_add(1, Ordering::Relaxed); + return; + } + std::hint::spin_loop(); + } + }); + run_thread.join().expect("run thread"); + stop_thread.join().expect("stop thread"); + if machine.current() == State::Running { + machine.request_stop().expect("request stop after run"); + } + assert_eq!(State::Stopping, machine.current()); + assert!(stop_wins.load(Ordering::Relaxed) <= 1); + } +} + +fn stress_ring() { + let mut ring: Ring = Ring::new(); + for cycle in 0..2048_u64 { + for index in 0..4096_u64 { + let slot = ring.claim().expect("slot") as usize; + ring.slots[slot] = cycle.wrapping_mul(4096).wrapping_add(index); + } + assert!(ring.claim().is_none()); + for index in 0..4096_u64 { + let slot = ring.pop().expect("slot") as usize; + assert_eq!( + cycle.wrapping_mul(4096).wrapping_add(index), + ring.slots[slot] + ); + ring.release(); + } + assert!(ring.pop().is_none()); + } +} + +fn stress_evdev_parser() { + let mut bytes = Vec::with_capacity(InputEvent::BYTE_LEN * 100_000); + for index in 0..100_000_i32 { + let event = InputEvent { + time_sec: i64::from(index), + time_usec: i64::from(index * 10), + event_type: EV_KEY, + code: 30, + value: index & 1, + }; + bytes.extend_from_slice(&event.time_sec.to_ne_bytes()); + bytes.extend_from_slice(&event.time_usec.to_ne_bytes()); + bytes.extend_from_slice(&event.event_type.to_ne_bytes()); + bytes.extend_from_slice(&event.code.to_ne_bytes()); + bytes.extend_from_slice(&event.value.to_ne_bytes()); + } + let mut count = 0_usize; + for event in parse_input_events(&bytes) { + assert_eq!(EV_KEY, event.event_type); + count += 1; + } + assert_eq!(100_000, count); +} + +fn stress_process_tree() { + let infos: Vec = (0..1024_i32) + .map(|index| Info { + pid: 20_000 + index, + parent_pid: if index == 0 { 1 } else { 20_000 + index - 1 }, + process_group_id: 20_000, + }) + .collect(); + let candidates: Vec = infos.iter().map(|info| info.pid).rev().collect(); + for _ in 0..1000 { + let resolver = |pid| infos.iter().copied().find(|info| info.pid == pid); + let related = + collect_related_pids_with_resolver(20_000, Some(infos[0]), &candidates, 1024, resolver); + assert_eq!(1024, related.len()); + assert_eq!(20_000, related[0]); + } +} + +fn main() { + stress_source_state(); + stress_ring(); + stress_evdev_parser(); + stress_process_tree(); + println!("native core stress completed"); +} diff --git a/fluxer_desktop/native/rust/fuzz/Cargo.lock b/fluxer_desktop/native/rust/fuzz/Cargo.lock new file mode 100644 index 0000000..b76f23a --- /dev/null +++ b/fluxer_desktop/native/rust/fuzz/Cargo.lock @@ -0,0 +1,233 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "cc" +version = "1.2.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fluxer_desktop_native" +version = "0.1.0" +dependencies = [ + "sha2", +] + +[[package]] +name = "fluxer_desktop_native_fuzz" +version = "0.0.0" +dependencies = [ + "arbitrary", + "fluxer_desktop_native", + "libfuzzer-sys", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "typenum", +] + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom", + "libc", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "typenum" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" diff --git a/fluxer_desktop/native/rust/fuzz/Cargo.toml b/fluxer_desktop/native/rust/fuzz/Cargo.toml new file mode 100644 index 0000000..3caf574 --- /dev/null +++ b/fluxer_desktop/native/rust/fuzz/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "fluxer_desktop_native_fuzz" +version = "0.0.0" +edition = "2024" +publish = false + +[workspace] +resolver = "2" + +[package.metadata] +cargo-fuzz = true + +[dependencies] +arbitrary = {version = "1.4", features = ["derive"]} +fluxer_desktop_native = {path = ".."} +libfuzzer-sys = "0.4" + +[[bin]] +name = "routing" +path = "fuzz_targets/routing.rs" +test = false +doc = false + +[[bin]] +name = "pid_payload" +path = "fuzz_targets/pid_payload.rs" +test = false +doc = false + +[[bin]] +name = "audio_converter" +path = "fuzz_targets/audio_converter.rs" +test = false +doc = false + +[[bin]] +name = "evdev_event" +path = "fuzz_targets/evdev_event.rs" +test = false +doc = false + +[[bin]] +name = "process_tree" +path = "fuzz_targets/process_tree.rs" +test = false +doc = false diff --git a/fluxer_desktop/native/rust/fuzz/fuzz_targets/audio_converter.rs b/fluxer_desktop/native/rust/fuzz/fuzz_targets/audio_converter.rs new file mode 100644 index 0000000..29e1790 --- /dev/null +++ b/fluxer_desktop/native/rust/fuzz/fuzz_targets/audio_converter.rs @@ -0,0 +1,40 @@ +#![no_main] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +use arbitrary::Arbitrary; +use fluxer_desktop_native::mac_app_audio::audio_converter::{ + AudioBuffer, AudioBufferListN, build_input_asbd, convert_buffer_list_to_interleaved_f32, +}; +use libfuzzer_sys::fuzz_target; + +#[derive(Arbitrary, Debug)] +struct Input { + samples: Vec, + sample_rate: f64, + output_rate: f64, + channels: u8, +} + +fuzz_target!(|input: Input| { + let channels = u32::from(input.channels.clamp(1, 2)); + let sample_rate = if input.sample_rate.is_finite() && input.sample_rate > 0.0 { + input.sample_rate.min(384_000.0) + } else { + 48_000.0 + }; + let output_rate = if input.output_rate.is_finite() && input.output_rate > 0.0 { + input.output_rate.min(384_000.0) + } else { + 48_000.0 + }; + let frame_count = (input.samples.len() as u32 / channels).min(4096); + let list = AudioBufferListN { + m_number_buffers: 1, + buffers: [AudioBuffer::from_slice(channels, &input.samples)], + }; + let asbd = build_input_asbd(sample_rate, channels, false); + let mut out = vec![0.0_f32; frame_count as usize * 4 + 16]; + let _ = + convert_buffer_list_to_interleaved_f32(asbd, &list, frame_count, output_rate, 2, &mut out); +}); diff --git a/fluxer_desktop/native/rust/fuzz/fuzz_targets/evdev_event.rs b/fluxer_desktop/native/rust/fuzz/fuzz_targets/evdev_event.rs new file mode 100644 index 0000000..cdca67b --- /dev/null +++ b/fluxer_desktop/native/rust/fuzz/fuzz_targets/evdev_event.rs @@ -0,0 +1,20 @@ +#![no_main] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +use fluxer_desktop_native::linux_evdev::event::{ + InputEvent, parse_input_event, parse_input_events, +}; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + let _ = parse_input_event(data); + let mut count = 0_usize; + for event in parse_input_events(data) { + let _ = event.time_sec ^ event.time_usec; + let _ = event.event_type ^ event.code; + let _ = event.value; + count += 1; + } + assert_eq!(data.len() / InputEvent::BYTE_LEN, count); +}); diff --git a/fluxer_desktop/native/rust/fuzz/fuzz_targets/pid_payload.rs b/fluxer_desktop/native/rust/fuzz/fuzz_targets/pid_payload.rs new file mode 100644 index 0000000..93a5be5 --- /dev/null +++ b/fluxer_desktop/native/rust/fuzz/fuzz_targets/pid_payload.rs @@ -0,0 +1,12 @@ +#![no_main] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +use fluxer_desktop_native::linux_portals::pid_payload::parse_shell_eval_pid_payload; +use libfuzzer_sys::fuzz_target; + +fuzz_target!(|data: &[u8]| { + if let Ok(payload) = std::str::from_utf8(data) { + let _ = parse_shell_eval_pid_payload(payload); + } +}); diff --git a/fluxer_desktop/native/rust/fuzz/fuzz_targets/process_tree.rs b/fluxer_desktop/native/rust/fuzz/fuzz_targets/process_tree.rs new file mode 100644 index 0000000..375084e --- /dev/null +++ b/fluxer_desktop/native/rust/fuzz/fuzz_targets/process_tree.rs @@ -0,0 +1,62 @@ +#![no_main] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +use arbitrary::Arbitrary; +use fluxer_desktop_native::mac_app_audio::process_tree::{ + Info, collect_related_pids_with_resolver, is_same_launch_tree_with_resolver, +}; +use libfuzzer_sys::fuzz_target; + +#[derive(Arbitrary, Debug)] +struct RawInfo { + pid: i32, + parent_pid: i32, + process_group_id: i32, +} + +#[derive(Arbitrary, Debug)] +struct Input { + target_pid: i32, + max_count: u8, + infos: Vec, + candidates: Vec, +} + +fuzz_target!(|input: Input| { + let infos: Vec = input + .infos + .into_iter() + .take(128) + .map(|raw| Info { + pid: raw.pid, + parent_pid: raw.parent_pid, + process_group_id: raw.process_group_id, + }) + .collect(); + let candidates: Vec = input.candidates.into_iter().take(128).collect(); + let target_info = infos + .iter() + .copied() + .find(|info| info.pid == input.target_pid); + let resolver = |pid| infos.iter().copied().find(|info| info.pid == pid); + let related = collect_related_pids_with_resolver( + input.target_pid, + target_info, + &candidates, + usize::from(input.max_count), + resolver, + ); + assert!(related.len() <= usize::from(input.max_count)); + if input.target_pid <= 0 || input.max_count == 0 { + assert!(related.is_empty()); + } else { + assert_eq!(Some(&input.target_pid), related.first()); + } + + for candidate in candidates.into_iter().take(16) { + let resolver = |pid| infos.iter().copied().find(|info| info.pid == pid); + let _ = + is_same_launch_tree_with_resolver(candidate, input.target_pid, target_info, resolver); + } +}); diff --git a/fluxer_desktop/native/rust/fuzz/fuzz_targets/routing.rs b/fluxer_desktop/native/rust/fuzz/fuzz_targets/routing.rs new file mode 100644 index 0000000..ce882e9 --- /dev/null +++ b/fluxer_desktop/native/rust/fuzz/fuzz_targets/routing.rs @@ -0,0 +1,60 @@ +#![no_main] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +use arbitrary::Arbitrary; +use fluxer_desktop_native::linux_audio::routing::{ + MEDIA_CLASS_PLAYBACK_STREAM, RoutingRule, SelfIdentity, should_route_node, +}; +use libfuzzer_sys::fuzz_target; +use std::collections::BTreeMap; + +#[derive(Arbitrary, Debug)] +struct Input { + id: u32, + sink_id: u32, + default_name: String, + default_id: String, + include_name: String, + app_name: String, + process_id: String, + binary: String, + has_device_id: bool, + media_class_is_playback: bool, +} + +fuzz_target!(|input: Input| { + let mut props = BTreeMap::new(); + props.insert( + "media.class".to_owned(), + if input.media_class_is_playback { + MEDIA_CLASS_PLAYBACK_STREAM.to_owned() + } else { + "Audio/Source".to_owned() + }, + ); + props.insert("application.name".to_owned(), input.app_name); + props.insert("application.process.id".to_owned(), input.process_id); + props.insert("application.process.binary".to_owned(), input.binary); + props.insert("target.object".to_owned(), input.default_name.clone()); + if input.has_device_id { + props.insert("device.id".to_owned(), "5".to_owned()); + } + let mut include = BTreeMap::new(); + include.insert("application.name".to_owned(), input.include_name); + let rule = RoutingRule { + include_when: vec![include], + skip_hardware_devices: true, + ..RoutingRule::default() + }; + let self_identity = SelfIdentity::default(); + let _ = should_route_node( + input.id, + &props, + &rule, + &input.default_name, + &input.default_id, + input.sink_id, + &self_identity, + ); +}); diff --git a/fluxer_desktop/native/rust/src/audio/contract.rs b/fluxer_desktop/native/rust/src/audio/contract.rs new file mode 100644 index 0000000..1709efd --- /dev/null +++ b/fluxer_desktop/native/rust/src/audio/contract.rs @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub const DIRECT_CAPTURE_SAMPLE_RATE: u32 = 48_000; +pub const DIRECT_CAPTURE_CHANNELS: u32 = 2; +pub const DIRECT_CAPTURE_MAX_SAMPLES: usize = + DIRECT_CAPTURE_SAMPLE_RATE as usize * DIRECT_CAPTURE_CHANNELS as usize * 2; +pub const DIRECT_CAPTURE_MAX_READ_SAMPLES: usize = + DIRECT_CAPTURE_SAMPLE_RATE as usize * DIRECT_CAPTURE_CHANNELS as usize / 10; +pub const MAX_ROUTING_RULE_PATTERNS: u32 = 64; +pub const MAX_ROUTING_RULE_KEYS_PER_PATTERN: u32 = 32; +pub const MAX_ROUTING_RULE_KEY_LENGTH: usize = 128; +pub const MAX_ROUTING_RULE_VALUE_LENGTH: usize = 512; +pub const MAX_INVENTORY_FIELDS: u32 = 32; +pub const MAX_INVENTORY_FIELD_LENGTH: usize = 128; + +const _: () = { + assert!(MAX_ROUTING_RULE_PATTERNS > 0); + assert!(MAX_ROUTING_RULE_KEYS_PER_PATTERN > 0); + assert!(MAX_ROUTING_RULE_KEY_LENGTH > 0); + assert!(MAX_ROUTING_RULE_VALUE_LENGTH >= MAX_ROUTING_RULE_KEY_LENGTH); + assert!(MAX_INVENTORY_FIELDS <= MAX_ROUTING_RULE_KEYS_PER_PATTERN); + assert!(MAX_INVENTORY_FIELD_LENGTH <= MAX_ROUTING_RULE_KEY_LENGTH); +}; + +pub fn whole_frame_sample_count(sample_count: usize, channels: u32) -> usize { + if channels == 0 { + return 0; + } + let channel_count = channels as usize; + sample_count - (sample_count % channel_count) +} + +pub fn direct_whole_frame_sample_count(sample_count: usize) -> usize { + whole_frame_sample_count(sample_count, DIRECT_CAPTURE_CHANNELS) +} + +pub fn bounded_direct_read_sample_count(available: usize) -> usize { + direct_whole_frame_sample_count(available.min(DIRECT_CAPTURE_MAX_READ_SAMPLES)) +} + +pub fn bounded_direct_append_slice(input: &[f32]) -> &[f32] { + let whole = direct_whole_frame_sample_count(input.len()); + let framed = &input[..whole]; + if framed.len() > DIRECT_CAPTURE_MAX_SAMPLES { + &framed[framed.len() - DIRECT_CAPTURE_MAX_SAMPLES..] + } else { + framed + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn whole_frame_sample_count_trims_incomplete_channel_frames() { + assert_eq!(0, whole_frame_sample_count(1, 2)); + assert_eq!(2, whole_frame_sample_count(2, 2)); + assert_eq!(4, whole_frame_sample_count(5, 2)); + assert_eq!(6, whole_frame_sample_count(7, 3)); + assert_eq!(0, whole_frame_sample_count(7, 0)); + } + + #[test] + fn direct_read_count_is_bounded_and_stereo_aligned() { + assert_eq!(0, bounded_direct_read_sample_count(1)); + assert_eq!(2, bounded_direct_read_sample_count(3)); + assert_eq!( + DIRECT_CAPTURE_MAX_READ_SAMPLES, + bounded_direct_read_sample_count(DIRECT_CAPTURE_MAX_READ_SAMPLES + 1) + ); + } + + #[test] + fn direct_append_slice_keeps_only_complete_stereo_samples_within_queue_cap() { + let samples = [1.0, 2.0, 3.0, 4.0, 5.0]; + let trimmed = bounded_direct_append_slice(&samples); + assert_eq!(4, trimmed.len()); + assert_eq!(&samples[..4], trimmed); + } +} diff --git a/fluxer_desktop/native/rust/src/audio/mod.rs b/fluxer_desktop/native/rust/src/audio/mod.rs new file mode 100644 index 0000000..7d4a3ee --- /dev/null +++ b/fluxer_desktop/native/rust/src/audio/mod.rs @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub mod contract; diff --git a/fluxer_desktop/native/rust/src/dbus/mod.rs b/fluxer_desktop/native/rust/src/dbus/mod.rs new file mode 100644 index 0000000..9263141 --- /dev/null +++ b/fluxer_desktop/native/rust/src/dbus/mod.rs @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub mod notify_parse; diff --git a/fluxer_desktop/native/rust/src/dbus/notify_parse.rs b/fluxer_desktop/native/rust/src/dbus/notify_parse.rs new file mode 100644 index 0000000..0189cec --- /dev/null +++ b/fluxer_desktop/native/rust/src/dbus/notify_parse.rs @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct NotifyReply { + pub id: u32, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ActionInvokedSignal { + pub id: u32, + pub action_key: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ClosedSignal { + pub id: u32, + pub reason: u32, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ParseError { + InvalidReply, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum FakeArg<'a> { + U32(u32), + String(&'a str), +} + +pub fn parse_action_invoked_from_args( + args: &[FakeArg<'_>], +) -> Result { + match args { + [FakeArg::U32(id), FakeArg::String(action_key), ..] => Ok(ActionInvokedSignal { + id: *id, + action_key: (*action_key).to_owned(), + }), + _ => Err(ParseError::InvalidReply), + } +} + +pub fn parse_closed_from_args(args: &[FakeArg<'_>]) -> Result { + match args { + [FakeArg::U32(id), FakeArg::U32(reason), ..] => Ok(ClosedSignal { + id: *id, + reason: *reason, + }), + _ => Err(ParseError::InvalidReply), + } +} + +pub fn parse_notify_reply_from_args(args: &[FakeArg<'_>]) -> Result { + match args { + [FakeArg::U32(id), ..] => Ok(NotifyReply { id: *id }), + _ => Err(ParseError::InvalidReply), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn notify_reply_valid_u32_yields_id() { + let reply = parse_notify_reply_from_args(&[FakeArg::U32(42)]).unwrap(); + assert_eq!(42, reply.id); + } + + #[test] + fn notify_reply_rejects_empty_body() { + assert_eq!( + Err(ParseError::InvalidReply), + parse_notify_reply_from_args(&[]) + ); + } + + #[test] + fn notify_reply_rejects_wrong_type() { + assert_eq!( + Err(ParseError::InvalidReply), + parse_notify_reply_from_args(&[FakeArg::String("wrong")]) + ); + } + + #[test] + fn action_invoked_valid_body() { + let sig = + parse_action_invoked_from_args(&[FakeArg::U32(7), FakeArg::String("default")]).unwrap(); + assert_eq!(7, sig.id); + assert_eq!("default", sig.action_key); + } + + #[test] + fn action_invoked_rejects_swapped_types() { + assert_eq!( + Err(ParseError::InvalidReply), + parse_action_invoked_from_args(&[FakeArg::String("x"), FakeArg::String("y")]) + ); + } + + #[test] + fn notification_closed_id_and_reason() { + let sig = parse_closed_from_args(&[FakeArg::U32(11), FakeArg::U32(2)]).unwrap(); + assert_eq!(11, sig.id); + assert_eq!(2, sig.reason); + } + + #[test] + fn notification_closed_rejects_too_short() { + assert_eq!( + Err(ParseError::InvalidReply), + parse_closed_from_args(&[FakeArg::U32(11)]) + ); + } +} diff --git a/fluxer_desktop/native/rust/src/input/keymap.rs b/fluxer_desktop/native/rust/src/input/keymap.rs new file mode 100644 index 0000000..30ca9ab --- /dev/null +++ b/fluxer_desktop/native/rust/src/input/keymap.rs @@ -0,0 +1,1821 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct KeyMapU32 { + pub code: u32, + pub name: &'static str, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct KeyMapU16 { + pub code: u16, + pub name: &'static str, +} + +pub mod linux_x11 { + use super::KeyMapU32; + + pub const KEYSYM_TABLE: &[KeyMapU32] = &[ + KeyMapU32 { + code: 0xff1b, + name: "Escape", + }, + KeyMapU32 { + code: 0xffbe, + name: "F1", + }, + KeyMapU32 { + code: 0xffbf, + name: "F2", + }, + KeyMapU32 { + code: 0xffc0, + name: "F3", + }, + KeyMapU32 { + code: 0xffc1, + name: "F4", + }, + KeyMapU32 { + code: 0xffc2, + name: "F5", + }, + KeyMapU32 { + code: 0xffc3, + name: "F6", + }, + KeyMapU32 { + code: 0xffc4, + name: "F7", + }, + KeyMapU32 { + code: 0xffc5, + name: "F8", + }, + KeyMapU32 { + code: 0xffc6, + name: "F9", + }, + KeyMapU32 { + code: 0xffc7, + name: "F10", + }, + KeyMapU32 { + code: 0xffc8, + name: "F11", + }, + KeyMapU32 { + code: 0xffc9, + name: "F12", + }, + KeyMapU32 { + code: 0xffca, + name: "F13", + }, + KeyMapU32 { + code: 0xffcb, + name: "F14", + }, + KeyMapU32 { + code: 0xffcc, + name: "F15", + }, + KeyMapU32 { + code: 0xffcd, + name: "F16", + }, + KeyMapU32 { + code: 0xffce, + name: "F17", + }, + KeyMapU32 { + code: 0xffcf, + name: "F18", + }, + KeyMapU32 { + code: 0xffd0, + name: "F19", + }, + KeyMapU32 { + code: 0xffd1, + name: "F20", + }, + KeyMapU32 { + code: 0xffd2, + name: "F21", + }, + KeyMapU32 { + code: 0xffd3, + name: "F22", + }, + KeyMapU32 { + code: 0xffd4, + name: "F23", + }, + KeyMapU32 { + code: 0xffd5, + name: "F24", + }, + KeyMapU32 { + code: 0xff61, + name: "PrintScreen", + }, + KeyMapU32 { + code: 0xff14, + name: "ScrollLock", + }, + KeyMapU32 { + code: 0xff13, + name: "Pause", + }, + KeyMapU32 { + code: 0xff7f, + name: "NumLock", + }, + KeyMapU32 { + code: 0xff67, + name: "ContextMenu", + }, + KeyMapU32 { + code: 0x0060, + name: "Backquote", + }, + KeyMapU32 { + code: 0x007e, + name: "Backquote", + }, + KeyMapU32 { + code: 0x0031, + name: "1", + }, + KeyMapU32 { + code: 0x0032, + name: "2", + }, + KeyMapU32 { + code: 0x0033, + name: "3", + }, + KeyMapU32 { + code: 0x0034, + name: "4", + }, + KeyMapU32 { + code: 0x0035, + name: "5", + }, + KeyMapU32 { + code: 0x0036, + name: "6", + }, + KeyMapU32 { + code: 0x0037, + name: "7", + }, + KeyMapU32 { + code: 0x0038, + name: "8", + }, + KeyMapU32 { + code: 0x0039, + name: "9", + }, + KeyMapU32 { + code: 0x0030, + name: "0", + }, + KeyMapU32 { + code: 0x002d, + name: "Minus", + }, + KeyMapU32 { + code: 0x003d, + name: "Equal", + }, + KeyMapU32 { + code: 0xff08, + name: "Backspace", + }, + KeyMapU32 { + code: 0xff09, + name: "Tab", + }, + KeyMapU32 { + code: 0x0071, + name: "Q", + }, + KeyMapU32 { + code: 0x0077, + name: "W", + }, + KeyMapU32 { + code: 0x0065, + name: "E", + }, + KeyMapU32 { + code: 0x0072, + name: "R", + }, + KeyMapU32 { + code: 0x0074, + name: "T", + }, + KeyMapU32 { + code: 0x0079, + name: "Y", + }, + KeyMapU32 { + code: 0x0075, + name: "U", + }, + KeyMapU32 { + code: 0x0069, + name: "I", + }, + KeyMapU32 { + code: 0x006f, + name: "O", + }, + KeyMapU32 { + code: 0x0070, + name: "P", + }, + KeyMapU32 { + code: 0x005b, + name: "BracketLeft", + }, + KeyMapU32 { + code: 0x005d, + name: "BracketRight", + }, + KeyMapU32 { + code: 0x005c, + name: "Backslash", + }, + KeyMapU32 { + code: 0xffe5, + name: "CapsLock", + }, + KeyMapU32 { + code: 0x0061, + name: "A", + }, + KeyMapU32 { + code: 0x0073, + name: "S", + }, + KeyMapU32 { + code: 0x0064, + name: "D", + }, + KeyMapU32 { + code: 0x0066, + name: "F", + }, + KeyMapU32 { + code: 0x0067, + name: "G", + }, + KeyMapU32 { + code: 0x0068, + name: "H", + }, + KeyMapU32 { + code: 0x006a, + name: "J", + }, + KeyMapU32 { + code: 0x006b, + name: "K", + }, + KeyMapU32 { + code: 0x006c, + name: "L", + }, + KeyMapU32 { + code: 0x003b, + name: "Semicolon", + }, + KeyMapU32 { + code: 0x0027, + name: "Quote", + }, + KeyMapU32 { + code: 0xff0d, + name: "Enter", + }, + KeyMapU32 { + code: 0xffe1, + name: "ShiftLeft", + }, + KeyMapU32 { + code: 0x007a, + name: "Z", + }, + KeyMapU32 { + code: 0x0078, + name: "X", + }, + KeyMapU32 { + code: 0x0063, + name: "C", + }, + KeyMapU32 { + code: 0x0076, + name: "V", + }, + KeyMapU32 { + code: 0x0062, + name: "B", + }, + KeyMapU32 { + code: 0x006e, + name: "N", + }, + KeyMapU32 { + code: 0x006d, + name: "M", + }, + KeyMapU32 { + code: 0x002c, + name: "Comma", + }, + KeyMapU32 { + code: 0x002e, + name: "Period", + }, + KeyMapU32 { + code: 0x002f, + name: "Slash", + }, + KeyMapU32 { + code: 0xffe2, + name: "ShiftRight", + }, + KeyMapU32 { + code: 0xffe3, + name: "ControlLeft", + }, + KeyMapU32 { + code: 0xffeb, + name: "MetaLeft", + }, + KeyMapU32 { + code: 0xffe9, + name: "AltLeft", + }, + KeyMapU32 { + code: 0x0020, + name: "Space", + }, + KeyMapU32 { + code: 0xffea, + name: "AltRight", + }, + KeyMapU32 { + code: 0xffec, + name: "MetaRight", + }, + KeyMapU32 { + code: 0xffe4, + name: "ControlRight", + }, + KeyMapU32 { + code: 0xff80, + name: "Space", + }, + KeyMapU32 { + code: 0xff89, + name: "Tab", + }, + KeyMapU32 { + code: 0xff8d, + name: "NumpadEnter", + }, + KeyMapU32 { + code: 0xffbd, + name: "NumpadEqual", + }, + KeyMapU32 { + code: 0xffaa, + name: "NumpadMultiply", + }, + KeyMapU32 { + code: 0xffab, + name: "NumpadAdd", + }, + KeyMapU32 { + code: 0xffac, + name: "NumpadComma", + }, + KeyMapU32 { + code: 0xffad, + name: "NumpadSubtract", + }, + KeyMapU32 { + code: 0xffae, + name: "NumpadDecimal", + }, + KeyMapU32 { + code: 0xffaf, + name: "NumpadDivide", + }, + KeyMapU32 { + code: 0xffb0, + name: "Numpad0", + }, + KeyMapU32 { + code: 0xffb1, + name: "Numpad1", + }, + KeyMapU32 { + code: 0xffb2, + name: "Numpad2", + }, + KeyMapU32 { + code: 0xffb3, + name: "Numpad3", + }, + KeyMapU32 { + code: 0xffb4, + name: "Numpad4", + }, + KeyMapU32 { + code: 0xffb5, + name: "Numpad5", + }, + KeyMapU32 { + code: 0xffb6, + name: "Numpad6", + }, + KeyMapU32 { + code: 0xffb7, + name: "Numpad7", + }, + KeyMapU32 { + code: 0xffb8, + name: "Numpad8", + }, + KeyMapU32 { + code: 0xffb9, + name: "Numpad9", + }, + KeyMapU32 { + code: 0xff51, + name: "ArrowLeft", + }, + KeyMapU32 { + code: 0xff52, + name: "ArrowUp", + }, + KeyMapU32 { + code: 0xff53, + name: "ArrowRight", + }, + KeyMapU32 { + code: 0xff54, + name: "ArrowDown", + }, + KeyMapU32 { + code: 0xff63, + name: "Insert", + }, + KeyMapU32 { + code: 0xffff, + name: "Delete", + }, + KeyMapU32 { + code: 0xff50, + name: "Home", + }, + KeyMapU32 { + code: 0xff57, + name: "End", + }, + KeyMapU32 { + code: 0xff55, + name: "PageUp", + }, + KeyMapU32 { + code: 0xff56, + name: "PageDown", + }, + KeyMapU32 { + code: 0x1008ff12, + name: "AudioVolumeMute", + }, + KeyMapU32 { + code: 0x1008ff11, + name: "AudioVolumeDown", + }, + KeyMapU32 { + code: 0x1008ff13, + name: "AudioVolumeUp", + }, + KeyMapU32 { + code: 0x1008ff17, + name: "MediaTrackNext", + }, + KeyMapU32 { + code: 0x1008ff16, + name: "MediaTrackPrevious", + }, + KeyMapU32 { + code: 0x1008ff15, + name: "MediaStop", + }, + KeyMapU32 { + code: 0x1008ff14, + name: "MediaPlayPause", + }, + KeyMapU32 { + code: 0x1008ff26, + name: "BrowserBack", + }, + KeyMapU32 { + code: 0x1008ff27, + name: "BrowserForward", + }, + KeyMapU32 { + code: 0x1008ff29, + name: "BrowserRefresh", + }, + KeyMapU32 { + code: 0x1008ff28, + name: "BrowserStop", + }, + KeyMapU32 { + code: 0x1008ff1b, + name: "BrowserSearch", + }, + KeyMapU32 { + code: 0x1008ff30, + name: "BrowserFavorites", + }, + KeyMapU32 { + code: 0x1008ff18, + name: "BrowserHome", + }, + KeyMapU32 { + code: 0x1008ff19, + name: "LaunchMail", + }, + KeyMapU32 { + code: 0x1008ff32, + name: "LaunchMediaPlayer", + }, + KeyMapU32 { + code: 0x1008ff41, + name: "LaunchApp1", + }, + KeyMapU32 { + code: 0x1008ff42, + name: "LaunchApp2", + }, + KeyMapU32 { + code: 0x1008ff2a, + name: "Power", + }, + KeyMapU32 { + code: 0x1008ff2f, + name: "Sleep", + }, + KeyMapU32 { + code: 0x1008ff2b, + name: "WakeUp", + }, + KeyMapU32 { + code: 0xff23, + name: "Convert", + }, + KeyMapU32 { + code: 0xff22, + name: "NonConvert", + }, + KeyMapU32 { + code: 0xff2d, + name: "KanaMode", + }, + KeyMapU32 { + code: 0xff31, + name: "Lang1", + }, + KeyMapU32 { + code: 0xff34, + name: "Lang2", + }, + ]; + + pub fn keysym_to_name(keysym: u32) -> Option<&'static str> { + KEYSYM_TABLE + .iter() + .find(|entry| entry.code == keysym) + .map(|entry| entry.name) + } +} + +pub mod windows { + use super::KeyMapU16; + + pub const VK_TABLE: &[KeyMapU16] = &[ + KeyMapU16 { + code: 0x1b, + name: "Escape", + }, + KeyMapU16 { + code: 0x70, + name: "F1", + }, + KeyMapU16 { + code: 0x71, + name: "F2", + }, + KeyMapU16 { + code: 0x72, + name: "F3", + }, + KeyMapU16 { + code: 0x73, + name: "F4", + }, + KeyMapU16 { + code: 0x74, + name: "F5", + }, + KeyMapU16 { + code: 0x75, + name: "F6", + }, + KeyMapU16 { + code: 0x76, + name: "F7", + }, + KeyMapU16 { + code: 0x77, + name: "F8", + }, + KeyMapU16 { + code: 0x78, + name: "F9", + }, + KeyMapU16 { + code: 0x79, + name: "F10", + }, + KeyMapU16 { + code: 0x7a, + name: "F11", + }, + KeyMapU16 { + code: 0x7b, + name: "F12", + }, + KeyMapU16 { + code: 0x7c, + name: "F13", + }, + KeyMapU16 { + code: 0x7d, + name: "F14", + }, + KeyMapU16 { + code: 0x7e, + name: "F15", + }, + KeyMapU16 { + code: 0x7f, + name: "F16", + }, + KeyMapU16 { + code: 0x80, + name: "F17", + }, + KeyMapU16 { + code: 0x81, + name: "F18", + }, + KeyMapU16 { + code: 0x82, + name: "F19", + }, + KeyMapU16 { + code: 0x83, + name: "F20", + }, + KeyMapU16 { + code: 0x84, + name: "F21", + }, + KeyMapU16 { + code: 0x85, + name: "F22", + }, + KeyMapU16 { + code: 0x86, + name: "F23", + }, + KeyMapU16 { + code: 0x87, + name: "F24", + }, + KeyMapU16 { + code: 0x13, + name: "Pause", + }, + KeyMapU16 { + code: 0x2c, + name: "PrintScreen", + }, + KeyMapU16 { + code: 0x91, + name: "ScrollLock", + }, + KeyMapU16 { + code: 0x90, + name: "NumLock", + }, + KeyMapU16 { + code: 0x5d, + name: "ContextMenu", + }, + KeyMapU16 { + code: 0xc0, + name: "Backquote", + }, + KeyMapU16 { + code: 0x31, + name: "1", + }, + KeyMapU16 { + code: 0x32, + name: "2", + }, + KeyMapU16 { + code: 0x33, + name: "3", + }, + KeyMapU16 { + code: 0x34, + name: "4", + }, + KeyMapU16 { + code: 0x35, + name: "5", + }, + KeyMapU16 { + code: 0x36, + name: "6", + }, + KeyMapU16 { + code: 0x37, + name: "7", + }, + KeyMapU16 { + code: 0x38, + name: "8", + }, + KeyMapU16 { + code: 0x39, + name: "9", + }, + KeyMapU16 { + code: 0x30, + name: "0", + }, + KeyMapU16 { + code: 0xbd, + name: "Minus", + }, + KeyMapU16 { + code: 0xbb, + name: "Equal", + }, + KeyMapU16 { + code: 0x08, + name: "Backspace", + }, + KeyMapU16 { + code: 0x09, + name: "Tab", + }, + KeyMapU16 { + code: 0x51, + name: "Q", + }, + KeyMapU16 { + code: 0x57, + name: "W", + }, + KeyMapU16 { + code: 0x45, + name: "E", + }, + KeyMapU16 { + code: 0x52, + name: "R", + }, + KeyMapU16 { + code: 0x54, + name: "T", + }, + KeyMapU16 { + code: 0x59, + name: "Y", + }, + KeyMapU16 { + code: 0x55, + name: "U", + }, + KeyMapU16 { + code: 0x49, + name: "I", + }, + KeyMapU16 { + code: 0x4f, + name: "O", + }, + KeyMapU16 { + code: 0x50, + name: "P", + }, + KeyMapU16 { + code: 0xdb, + name: "BracketLeft", + }, + KeyMapU16 { + code: 0xdd, + name: "BracketRight", + }, + KeyMapU16 { + code: 0xdc, + name: "Backslash", + }, + KeyMapU16 { + code: 0x14, + name: "CapsLock", + }, + KeyMapU16 { + code: 0x41, + name: "A", + }, + KeyMapU16 { + code: 0x53, + name: "S", + }, + KeyMapU16 { + code: 0x44, + name: "D", + }, + KeyMapU16 { + code: 0x46, + name: "F", + }, + KeyMapU16 { + code: 0x47, + name: "G", + }, + KeyMapU16 { + code: 0x48, + name: "H", + }, + KeyMapU16 { + code: 0x4a, + name: "J", + }, + KeyMapU16 { + code: 0x4b, + name: "K", + }, + KeyMapU16 { + code: 0x4c, + name: "L", + }, + KeyMapU16 { + code: 0xba, + name: "Semicolon", + }, + KeyMapU16 { + code: 0xde, + name: "Quote", + }, + KeyMapU16 { + code: 0x0d, + name: "Enter", + }, + KeyMapU16 { + code: 0xa0, + name: "ShiftLeft", + }, + KeyMapU16 { + code: 0x5a, + name: "Z", + }, + KeyMapU16 { + code: 0x58, + name: "X", + }, + KeyMapU16 { + code: 0x43, + name: "C", + }, + KeyMapU16 { + code: 0x56, + name: "V", + }, + KeyMapU16 { + code: 0x42, + name: "B", + }, + KeyMapU16 { + code: 0x4e, + name: "N", + }, + KeyMapU16 { + code: 0x4d, + name: "M", + }, + KeyMapU16 { + code: 0xbc, + name: "Comma", + }, + KeyMapU16 { + code: 0xbe, + name: "Period", + }, + KeyMapU16 { + code: 0xbf, + name: "Slash", + }, + KeyMapU16 { + code: 0xa1, + name: "ShiftRight", + }, + KeyMapU16 { + code: 0xa2, + name: "ControlLeft", + }, + KeyMapU16 { + code: 0x5b, + name: "MetaLeft", + }, + KeyMapU16 { + code: 0xa4, + name: "AltLeft", + }, + KeyMapU16 { + code: 0x20, + name: "Space", + }, + KeyMapU16 { + code: 0xa5, + name: "AltRight", + }, + KeyMapU16 { + code: 0x5c, + name: "MetaRight", + }, + KeyMapU16 { + code: 0xa3, + name: "ControlRight", + }, + KeyMapU16 { + code: 0x60, + name: "Numpad0", + }, + KeyMapU16 { + code: 0x61, + name: "Numpad1", + }, + KeyMapU16 { + code: 0x62, + name: "Numpad2", + }, + KeyMapU16 { + code: 0x63, + name: "Numpad3", + }, + KeyMapU16 { + code: 0x64, + name: "Numpad4", + }, + KeyMapU16 { + code: 0x65, + name: "Numpad5", + }, + KeyMapU16 { + code: 0x66, + name: "Numpad6", + }, + KeyMapU16 { + code: 0x67, + name: "Numpad7", + }, + KeyMapU16 { + code: 0x68, + name: "Numpad8", + }, + KeyMapU16 { + code: 0x69, + name: "Numpad9", + }, + KeyMapU16 { + code: 0x6a, + name: "NumpadMultiply", + }, + KeyMapU16 { + code: 0x6b, + name: "NumpadAdd", + }, + KeyMapU16 { + code: 0x6c, + name: "NumpadComma", + }, + KeyMapU16 { + code: 0x6d, + name: "NumpadSubtract", + }, + KeyMapU16 { + code: 0x6e, + name: "NumpadDecimal", + }, + KeyMapU16 { + code: 0x6f, + name: "NumpadDivide", + }, + KeyMapU16 { + code: 0x92, + name: "NumpadEqual", + }, + KeyMapU16 { + code: 0x0c, + name: "Numpad5", + }, + KeyMapU16 { + code: 0x25, + name: "ArrowLeft", + }, + KeyMapU16 { + code: 0x26, + name: "ArrowUp", + }, + KeyMapU16 { + code: 0x27, + name: "ArrowRight", + }, + KeyMapU16 { + code: 0x28, + name: "ArrowDown", + }, + KeyMapU16 { + code: 0x2d, + name: "Insert", + }, + KeyMapU16 { + code: 0x2e, + name: "Delete", + }, + KeyMapU16 { + code: 0x24, + name: "Home", + }, + KeyMapU16 { + code: 0x23, + name: "End", + }, + KeyMapU16 { + code: 0x21, + name: "PageUp", + }, + KeyMapU16 { + code: 0x22, + name: "PageDown", + }, + KeyMapU16 { + code: 0xad, + name: "AudioVolumeMute", + }, + KeyMapU16 { + code: 0xae, + name: "AudioVolumeDown", + }, + KeyMapU16 { + code: 0xaf, + name: "AudioVolumeUp", + }, + KeyMapU16 { + code: 0xb0, + name: "MediaTrackNext", + }, + KeyMapU16 { + code: 0xb1, + name: "MediaTrackPrevious", + }, + KeyMapU16 { + code: 0xb2, + name: "MediaStop", + }, + KeyMapU16 { + code: 0xb3, + name: "MediaPlayPause", + }, + KeyMapU16 { + code: 0xa6, + name: "BrowserBack", + }, + KeyMapU16 { + code: 0xa7, + name: "BrowserForward", + }, + KeyMapU16 { + code: 0xa8, + name: "BrowserRefresh", + }, + KeyMapU16 { + code: 0xa9, + name: "BrowserStop", + }, + KeyMapU16 { + code: 0xaa, + name: "BrowserSearch", + }, + KeyMapU16 { + code: 0xab, + name: "BrowserFavorites", + }, + KeyMapU16 { + code: 0xac, + name: "BrowserHome", + }, + KeyMapU16 { + code: 0xb4, + name: "LaunchMail", + }, + KeyMapU16 { + code: 0xb5, + name: "LaunchMediaPlayer", + }, + KeyMapU16 { + code: 0xb6, + name: "LaunchApp1", + }, + KeyMapU16 { + code: 0xb7, + name: "LaunchApp2", + }, + KeyMapU16 { + code: 0x1c, + name: "Convert", + }, + KeyMapU16 { + code: 0x1d, + name: "NonConvert", + }, + KeyMapU16 { + code: 0x15, + name: "KanaMode", + }, + KeyMapU16 { + code: 0x5f, + name: "Sleep", + }, + ]; + + pub fn vk_to_name(vk: u16) -> Option<&'static str> { + VK_TABLE + .iter() + .find(|entry| entry.code == vk) + .map(|entry| entry.name) + } +} + +pub mod macos { + use super::KeyMapU16; + + pub const KEYCODE_TABLE: &[KeyMapU16] = &[ + KeyMapU16 { + code: 0x35, + name: "Escape", + }, + KeyMapU16 { + code: 0x7a, + name: "F1", + }, + KeyMapU16 { + code: 0x78, + name: "F2", + }, + KeyMapU16 { + code: 0x63, + name: "F3", + }, + KeyMapU16 { + code: 0x76, + name: "F4", + }, + KeyMapU16 { + code: 0x60, + name: "F5", + }, + KeyMapU16 { + code: 0x61, + name: "F6", + }, + KeyMapU16 { + code: 0x62, + name: "F7", + }, + KeyMapU16 { + code: 0x64, + name: "F8", + }, + KeyMapU16 { + code: 0x65, + name: "F9", + }, + KeyMapU16 { + code: 0x6d, + name: "F10", + }, + KeyMapU16 { + code: 0x67, + name: "F11", + }, + KeyMapU16 { + code: 0x6f, + name: "F12", + }, + KeyMapU16 { + code: 0x69, + name: "F13", + }, + KeyMapU16 { + code: 0x6b, + name: "F14", + }, + KeyMapU16 { + code: 0x71, + name: "F15", + }, + KeyMapU16 { + code: 0x6a, + name: "F16", + }, + KeyMapU16 { + code: 0x40, + name: "F17", + }, + KeyMapU16 { + code: 0x4f, + name: "F18", + }, + KeyMapU16 { + code: 0x50, + name: "F19", + }, + KeyMapU16 { + code: 0x5a, + name: "F20", + }, + KeyMapU16 { + code: 0x32, + name: "Backquote", + }, + KeyMapU16 { + code: 0x12, + name: "1", + }, + KeyMapU16 { + code: 0x13, + name: "2", + }, + KeyMapU16 { + code: 0x14, + name: "3", + }, + KeyMapU16 { + code: 0x15, + name: "4", + }, + KeyMapU16 { + code: 0x17, + name: "5", + }, + KeyMapU16 { + code: 0x16, + name: "6", + }, + KeyMapU16 { + code: 0x1a, + name: "7", + }, + KeyMapU16 { + code: 0x1c, + name: "8", + }, + KeyMapU16 { + code: 0x19, + name: "9", + }, + KeyMapU16 { + code: 0x1d, + name: "0", + }, + KeyMapU16 { + code: 0x1b, + name: "Minus", + }, + KeyMapU16 { + code: 0x18, + name: "Equal", + }, + KeyMapU16 { + code: 0x33, + name: "Backspace", + }, + KeyMapU16 { + code: 0x30, + name: "Tab", + }, + KeyMapU16 { + code: 0x0c, + name: "Q", + }, + KeyMapU16 { + code: 0x0d, + name: "W", + }, + KeyMapU16 { + code: 0x0e, + name: "E", + }, + KeyMapU16 { + code: 0x0f, + name: "R", + }, + KeyMapU16 { + code: 0x11, + name: "T", + }, + KeyMapU16 { + code: 0x10, + name: "Y", + }, + KeyMapU16 { + code: 0x20, + name: "U", + }, + KeyMapU16 { + code: 0x22, + name: "I", + }, + KeyMapU16 { + code: 0x1f, + name: "O", + }, + KeyMapU16 { + code: 0x23, + name: "P", + }, + KeyMapU16 { + code: 0x21, + name: "BracketLeft", + }, + KeyMapU16 { + code: 0x1e, + name: "BracketRight", + }, + KeyMapU16 { + code: 0x2a, + name: "Backslash", + }, + KeyMapU16 { + code: 0x0a, + name: "IntlBackslash", + }, + KeyMapU16 { + code: 0x39, + name: "CapsLock", + }, + KeyMapU16 { + code: 0x00, + name: "A", + }, + KeyMapU16 { + code: 0x01, + name: "S", + }, + KeyMapU16 { + code: 0x02, + name: "D", + }, + KeyMapU16 { + code: 0x03, + name: "F", + }, + KeyMapU16 { + code: 0x05, + name: "G", + }, + KeyMapU16 { + code: 0x04, + name: "H", + }, + KeyMapU16 { + code: 0x26, + name: "J", + }, + KeyMapU16 { + code: 0x28, + name: "K", + }, + KeyMapU16 { + code: 0x25, + name: "L", + }, + KeyMapU16 { + code: 0x29, + name: "Semicolon", + }, + KeyMapU16 { + code: 0x27, + name: "Quote", + }, + KeyMapU16 { + code: 0x24, + name: "Enter", + }, + KeyMapU16 { + code: 0x38, + name: "ShiftLeft", + }, + KeyMapU16 { + code: 0x06, + name: "Z", + }, + KeyMapU16 { + code: 0x07, + name: "X", + }, + KeyMapU16 { + code: 0x08, + name: "C", + }, + KeyMapU16 { + code: 0x09, + name: "V", + }, + KeyMapU16 { + code: 0x0b, + name: "B", + }, + KeyMapU16 { + code: 0x2d, + name: "N", + }, + KeyMapU16 { + code: 0x2e, + name: "M", + }, + KeyMapU16 { + code: 0x2b, + name: "Comma", + }, + KeyMapU16 { + code: 0x2f, + name: "Period", + }, + KeyMapU16 { + code: 0x2c, + name: "Slash", + }, + KeyMapU16 { + code: 0x3c, + name: "ShiftRight", + }, + KeyMapU16 { + code: 0x3b, + name: "ControlLeft", + }, + KeyMapU16 { + code: 0x37, + name: "MetaLeft", + }, + KeyMapU16 { + code: 0x3a, + name: "AltLeft", + }, + KeyMapU16 { + code: 0x31, + name: "Space", + }, + KeyMapU16 { + code: 0x3d, + name: "AltRight", + }, + KeyMapU16 { + code: 0x36, + name: "MetaRight", + }, + KeyMapU16 { + code: 0x3e, + name: "ControlRight", + }, + KeyMapU16 { + code: 0x41, + name: "NumpadDecimal", + }, + KeyMapU16 { + code: 0x43, + name: "NumpadMultiply", + }, + KeyMapU16 { + code: 0x45, + name: "NumpadAdd", + }, + KeyMapU16 { + code: 0x47, + name: "NumLock", + }, + KeyMapU16 { + code: 0x4b, + name: "NumpadDivide", + }, + KeyMapU16 { + code: 0x4c, + name: "NumpadEnter", + }, + KeyMapU16 { + code: 0x4e, + name: "NumpadSubtract", + }, + KeyMapU16 { + code: 0x51, + name: "NumpadEqual", + }, + KeyMapU16 { + code: 0x52, + name: "Numpad0", + }, + KeyMapU16 { + code: 0x53, + name: "Numpad1", + }, + KeyMapU16 { + code: 0x54, + name: "Numpad2", + }, + KeyMapU16 { + code: 0x55, + name: "Numpad3", + }, + KeyMapU16 { + code: 0x56, + name: "Numpad4", + }, + KeyMapU16 { + code: 0x57, + name: "Numpad5", + }, + KeyMapU16 { + code: 0x58, + name: "Numpad6", + }, + KeyMapU16 { + code: 0x59, + name: "Numpad7", + }, + KeyMapU16 { + code: 0x5b, + name: "Numpad8", + }, + KeyMapU16 { + code: 0x5c, + name: "Numpad9", + }, + KeyMapU16 { + code: 0x5d, + name: "IntlYen", + }, + KeyMapU16 { + code: 0x5e, + name: "IntlRo", + }, + KeyMapU16 { + code: 0x5f, + name: "NumpadComma", + }, + KeyMapU16 { + code: 0x66, + name: "Lang2", + }, + KeyMapU16 { + code: 0x68, + name: "Lang1", + }, + KeyMapU16 { + code: 0x7b, + name: "ArrowLeft", + }, + KeyMapU16 { + code: 0x7e, + name: "ArrowUp", + }, + KeyMapU16 { + code: 0x7c, + name: "ArrowRight", + }, + KeyMapU16 { + code: 0x7d, + name: "ArrowDown", + }, + KeyMapU16 { + code: 0x72, + name: "Insert", + }, + KeyMapU16 { + code: 0x75, + name: "Delete", + }, + KeyMapU16 { + code: 0x73, + name: "Home", + }, + KeyMapU16 { + code: 0x77, + name: "End", + }, + KeyMapU16 { + code: 0x74, + name: "PageUp", + }, + KeyMapU16 { + code: 0x79, + name: "PageDown", + }, + ]; + + pub fn keycode_to_name(keycode: u16) -> Option<&'static str> { + KEYCODE_TABLE + .iter() + .find(|entry| entry.code == keycode) + .map(|entry| entry.name) + } +} + +pub fn fallback_name(prefix_value: impl std::fmt::Display) -> String { + format!("Key{prefix_value}") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn linux_common_letters_map_to_single_letter_names() { + assert_eq!(Some("A"), linux_x11::keysym_to_name(0x0061)); + assert_eq!(Some("M"), linux_x11::keysym_to_name(0x006d)); + assert_eq!(Some("Z"), linux_x11::keysym_to_name(0x007a)); + } + + #[test] + fn linux_modifier_keysyms_produce_side_distinguished_names() { + assert_eq!(Some("ShiftLeft"), linux_x11::keysym_to_name(0xffe1)); + assert_eq!(Some("ShiftRight"), linux_x11::keysym_to_name(0xffe2)); + assert_eq!(Some("ControlLeft"), linux_x11::keysym_to_name(0xffe3)); + assert_eq!(Some("AltLeft"), linux_x11::keysym_to_name(0xffe9)); + assert_eq!(Some("MetaLeft"), linux_x11::keysym_to_name(0xffeb)); + } + + #[test] + fn linux_function_keys_map_across_f1_to_f12() { + assert_eq!(Some("F1"), linux_x11::keysym_to_name(0xffbe)); + assert_eq!(Some("F12"), linux_x11::keysym_to_name(0xffc9)); + assert_eq!(Some("Pause"), linux_x11::keysym_to_name(0xff13)); + assert_eq!(Some("F13"), linux_x11::keysym_to_name(0xffca)); + assert_eq!(Some("Numpad0"), linux_x11::keysym_to_name(0xffb0)); + assert_eq!( + Some("AudioVolumeMute"), + linux_x11::keysym_to_name(0x1008ff12) + ); + assert_eq!(Some("LaunchApp1"), linux_x11::keysym_to_name(0x1008ff41)); + } + + #[test] + fn linux_arrows_and_editing_keys_round_trip() { + assert_eq!(Some("ArrowLeft"), linux_x11::keysym_to_name(0xff51)); + assert_eq!(Some("PageDown"), linux_x11::keysym_to_name(0xff56)); + assert_eq!(Some("Delete"), linux_x11::keysym_to_name(0xffff)); + } + + #[test] + fn linux_unknown_keysym_falls_back_to_key_number() { + assert_eq!(None, linux_x11::keysym_to_name(0x12345)); + assert_eq!("Key74565", fallback_name(0x12345_u32)); + } + + #[test] + fn linux_no_two_distinct_names_accidentally_share_a_keysym() { + for (i, left) in linux_x11::KEYSYM_TABLE.iter().enumerate() { + for right in &linux_x11::KEYSYM_TABLE[i + 1..] { + if left.code == right.code { + assert_eq!(left.name, right.name); + } + } + } + } + + #[test] + fn windows_ascii_letters_use_vk_mapping() { + assert_eq!(Some("A"), windows::vk_to_name(0x41)); + assert_eq!(Some("Z"), windows::vk_to_name(0x5a)); + assert_eq!(Some("M"), windows::vk_to_name(0x4d)); + } + + #[test] + fn windows_modifiers_map_to_side_distinguished_names() { + assert_eq!(Some("ShiftLeft"), windows::vk_to_name(0xa0)); + assert_eq!(Some("ShiftRight"), windows::vk_to_name(0xa1)); + assert_eq!(Some("ControlLeft"), windows::vk_to_name(0xa2)); + assert_eq!(Some("MetaLeft"), windows::vk_to_name(0x5b)); + assert_eq!(Some("AltLeft"), windows::vk_to_name(0xa4)); + } + + #[test] + fn windows_function_keys_cover_f1_to_f12() { + assert_eq!(Some("F1"), windows::vk_to_name(0x70)); + assert_eq!(Some("F12"), windows::vk_to_name(0x7b)); + assert_eq!(Some("F13"), windows::vk_to_name(0x7c)); + assert_eq!(Some("Pause"), windows::vk_to_name(0x13)); + } + + #[test] + fn windows_special_numpad_and_media_keys_map() { + assert_eq!(Some("PrintScreen"), windows::vk_to_name(0x2c)); + assert_eq!(Some("Numpad0"), windows::vk_to_name(0x60)); + assert_eq!(Some("NumpadDivide"), windows::vk_to_name(0x6f)); + assert_eq!(Some("NumpadEqual"), windows::vk_to_name(0x92)); + assert_eq!(Some("AudioVolumeMute"), windows::vk_to_name(0xad)); + assert_eq!(Some("BrowserBack"), windows::vk_to_name(0xa6)); + assert_eq!(Some("LaunchMail"), windows::vk_to_name(0xb4)); + assert_eq!(Some("KanaMode"), windows::vk_to_name(0x15)); + } + + #[test] + fn windows_arrows_and_editing_keys_map() { + assert_eq!(Some("ArrowLeft"), windows::vk_to_name(0x25)); + assert_eq!(Some("PageDown"), windows::vk_to_name(0x22)); + assert_eq!(Some("Delete"), windows::vk_to_name(0x2e)); + } + + #[test] + fn windows_unknown_vk_falls_back_to_key_number() { + assert_eq!(None, windows::vk_to_name(0x0fff)); + assert_eq!("Key291", fallback_name(0x123_u16)); + } + + #[test] + fn macos_letters_use_apple_hid_positions() { + assert_eq!(Some("A"), macos::keycode_to_name(0x00)); + assert_eq!(Some("Z"), macos::keycode_to_name(0x06)); + assert_eq!(Some("M"), macos::keycode_to_name(0x2e)); + } + + #[test] + fn macos_modifiers_map_to_side_distinguished_names() { + assert_eq!(Some("ShiftLeft"), macos::keycode_to_name(0x38)); + assert_eq!(Some("ShiftRight"), macos::keycode_to_name(0x3c)); + assert_eq!(Some("MetaLeft"), macos::keycode_to_name(0x37)); + assert_eq!(Some("AltLeft"), macos::keycode_to_name(0x3a)); + } + + #[test] + fn macos_function_keys_cover_f1_to_f20() { + assert_eq!(Some("F1"), macos::keycode_to_name(0x7a)); + assert_eq!(Some("F12"), macos::keycode_to_name(0x6f)); + assert_eq!(Some("F13"), macos::keycode_to_name(0x69)); + assert_eq!(Some("F20"), macos::keycode_to_name(0x5a)); + } + + #[test] + fn macos_numpad_and_international_keys_map() { + assert_eq!(Some("Numpad0"), macos::keycode_to_name(0x52)); + assert_eq!(Some("NumpadEnter"), macos::keycode_to_name(0x4c)); + assert_eq!(Some("NumpadEqual"), macos::keycode_to_name(0x51)); + assert_eq!(Some("NumLock"), macos::keycode_to_name(0x47)); + assert_eq!(Some("IntlYen"), macos::keycode_to_name(0x5d)); + assert_eq!(Some("Lang1"), macos::keycode_to_name(0x68)); + } + + #[test] + fn macos_arrows_and_editing_keys_map() { + assert_eq!(Some("ArrowLeft"), macos::keycode_to_name(0x7b)); + assert_eq!(Some("ArrowDown"), macos::keycode_to_name(0x7d)); + assert_eq!(Some("Delete"), macos::keycode_to_name(0x75)); + } + + #[test] + fn macos_unknown_keycode_falls_back_to_key_number() { + assert_eq!(None, macos::keycode_to_name(0x0fff)); + assert_eq!("Key291", fallback_name(0x123_u16)); + } +} diff --git a/fluxer_desktop/native/rust/src/input/linux_mouse.rs b/fluxer_desktop/native/rust/src/input/linux_mouse.rs new file mode 100644 index 0000000..5325112 --- /dev/null +++ b/fluxer_desktop/native/rust/src/input/linux_mouse.rs @@ -0,0 +1,88 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum WheelDirection { + Up, + Down, + Left, + Right, +} + +impl WheelDirection { + pub fn delta_x(self) -> i32 { + match self { + Self::Left => -120, + Self::Right => 120, + Self::Up | Self::Down => 0, + } + } + + pub fn delta_y(self) -> i32 { + match self { + Self::Up => -120, + Self::Down => 120, + Self::Left | Self::Right => 0, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum MouseClassification { + Button(u8), + Wheel(WheelDirection), + Ignored, +} + +pub fn classify(x11_button: u32) -> MouseClassification { + match x11_button { + 1 => MouseClassification::Button(0), + 2 => MouseClassification::Button(1), + 3 => MouseClassification::Button(2), + 4 => MouseClassification::Wheel(WheelDirection::Up), + 5 => MouseClassification::Wheel(WheelDirection::Down), + 6 => MouseClassification::Wheel(WheelDirection::Left), + 7 => MouseClassification::Wheel(WheelDirection::Right), + 8 => MouseClassification::Button(3), + 9 => MouseClassification::Button(4), + _ => MouseClassification::Ignored, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn primary_buttons_map_to_browser_indices() { + assert_eq!(MouseClassification::Button(0), classify(1)); + assert_eq!(MouseClassification::Button(1), classify(2)); + assert_eq!(MouseClassification::Button(2), classify(3)); + } + + #[test] + fn vertical_wheel_produces_delta_y_with_120_step() { + assert_eq!(-120, WheelDirection::Up.delta_y()); + assert_eq!(120, WheelDirection::Down.delta_y()); + assert_eq!(0, WheelDirection::Up.delta_x()); + } + + #[test] + fn horizontal_wheel_produces_delta_x_with_120_step() { + assert_eq!(-120, WheelDirection::Left.delta_x()); + assert_eq!(120, WheelDirection::Right.delta_x()); + assert_eq!(0, WheelDirection::Left.delta_y()); + } + + #[test] + fn back_forward_buttons_map_to_3_and_4() { + assert_eq!(MouseClassification::Button(3), classify(8)); + assert_eq!(MouseClassification::Button(4), classify(9)); + } + + #[test] + fn unknown_button_numbers_are_ignored_not_silently_misrouted() { + assert_eq!(MouseClassification::Ignored, classify(0)); + assert_eq!(MouseClassification::Ignored, classify(15)); + assert_eq!(MouseClassification::Ignored, classify(255)); + } +} diff --git a/fluxer_desktop/native/rust/src/input/macos_mouse.rs b/fluxer_desktop/native/rust/src/input/macos_mouse.rs new file mode 100644 index 0000000..1541a06 --- /dev/null +++ b/fluxer_desktop/native/rust/src/input/macos_mouse.rs @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum CgEventType { + LeftMouseDown = 1, + LeftMouseUp = 2, + RightMouseDown = 3, + RightMouseUp = 4, + MouseMoved = 5, + LeftMouseDragged = 6, + RightMouseDragged = 7, + KeyDown = 10, + KeyUp = 11, + FlagsChanged = 12, + ScrollWheel = 22, + OtherMouseDown = 25, + OtherMouseUp = 26, + OtherMouseDragged = 27, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Classification { + Button(u8), + Ignored, +} + +pub fn classify(event_type: CgEventType, other_button: u32) -> Classification { + match event_type { + CgEventType::LeftMouseDown | CgEventType::LeftMouseUp => Classification::Button(0), + CgEventType::RightMouseDown | CgEventType::RightMouseUp => Classification::Button(2), + CgEventType::OtherMouseDown | CgEventType::OtherMouseUp => match other_button { + 2 => Classification::Button(1), + 3 => Classification::Button(3), + 4 => Classification::Button(4), + _ => Classification::Ignored, + }, + _ => Classification::Ignored, + } +} + +pub fn is_down(event_type: CgEventType) -> bool { + matches!( + event_type, + CgEventType::LeftMouseDown | CgEventType::RightMouseDown | CgEventType::OtherMouseDown + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn left_and_right_buttons_map_to_0_and_2() { + assert_eq!( + Classification::Button(0), + classify(CgEventType::LeftMouseDown, 0) + ); + assert_eq!( + Classification::Button(2), + classify(CgEventType::RightMouseUp, 0) + ); + } + + #[test] + fn middle_button_maps_to_1() { + assert_eq!( + Classification::Button(1), + classify(CgEventType::OtherMouseDown, 2) + ); + } + + #[test] + fn back_forward_map_to_3_and_4() { + assert_eq!( + Classification::Button(3), + classify(CgEventType::OtherMouseDown, 3) + ); + assert_eq!( + Classification::Button(4), + classify(CgEventType::OtherMouseUp, 4) + ); + } + + #[test] + fn unknown_other_button_is_ignored_not_silently_misrouted() { + assert_eq!( + Classification::Ignored, + classify(CgEventType::OtherMouseDown, 99) + ); + } + + #[test] + fn is_down_distinguishes_press_from_release() { + assert!(is_down(CgEventType::LeftMouseDown)); + assert!(!is_down(CgEventType::LeftMouseUp)); + assert!(is_down(CgEventType::OtherMouseDown)); + assert!(!is_down(CgEventType::MouseMoved)); + } +} diff --git a/fluxer_desktop/native/rust/src/input/mod.rs b/fluxer_desktop/native/rust/src/input/mod.rs new file mode 100644 index 0000000..bdd4502 --- /dev/null +++ b/fluxer_desktop/native/rust/src/input/mod.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub mod keymap; +pub mod linux_mouse; +pub mod macos_mouse; +pub mod modifiers; +pub mod ring; +pub mod windows_mouse; +pub mod x11; diff --git a/fluxer_desktop/native/rust/src/input/modifiers.rs b/fluxer_desktop/native/rust/src/input/modifiers.rs new file mode 100644 index 0000000..3011e36 --- /dev/null +++ b/fluxer_desktop/native/rust/src/input/modifiers.rs @@ -0,0 +1,216 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)] +pub struct Modifiers { + pub ctrl: bool, + pub alt: bool, + pub shift: bool, + pub meta: bool, +} + +pub mod linux { + use super::Modifiers; + + pub const SHIFT_MASK: u32 = 1 << 0; + pub const CONTROL_MASK: u32 = 1 << 2; + pub const MOD1_MASK: u32 = 1 << 3; + pub const MOD4_MASK: u32 = 1 << 6; + + pub fn from_state(state: u32) -> Modifiers { + Modifiers { + ctrl: (state & CONTROL_MASK) != 0, + alt: (state & MOD1_MASK) != 0, + shift: (state & SHIFT_MASK) != 0, + meta: (state & MOD4_MASK) != 0, + } + } +} + +pub mod macos { + use super::Modifiers; + + pub const SHIFT_MASK: u64 = 1 << 17; + pub const CONTROL_MASK: u64 = 1 << 18; + pub const ALTERNATE_MASK: u64 = 1 << 19; + pub const COMMAND_MASK: u64 = 1 << 20; + + pub fn from_flags(flags: u64) -> Modifiers { + Modifiers { + ctrl: (flags & CONTROL_MASK) != 0, + alt: (flags & ALTERNATE_MASK) != 0, + shift: (flags & SHIFT_MASK) != 0, + meta: (flags & COMMAND_MASK) != 0, + } + } +} + +pub mod windows { + use super::Modifiers; + + pub const HIGH_BIT: u16 = 0x8000; + + pub fn from_sampled( + shift_state: u16, + ctrl_state: u16, + alt_state: u16, + lwin_state: u16, + rwin_state: u16, + ) -> Modifiers { + Modifiers { + shift: (shift_state & HIGH_BIT) != 0, + ctrl: (ctrl_state & HIGH_BIT) != 0, + alt: (alt_state & HIGH_BIT) != 0, + meta: ((lwin_state | rwin_state) & HIGH_BIT) != 0, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn linux_no_bits_all_modifiers_false() { + assert_eq!(Modifiers::default(), linux::from_state(0)); + } + + #[test] + fn linux_shift_mask_alone_sets_only_shift() { + assert_eq!( + Modifiers { + shift: true, + ..Modifiers::default() + }, + linux::from_state(linux::SHIFT_MASK) + ); + } + + #[test] + fn linux_ctrl_alt_shift_meta_combo_all_true() { + assert_eq!( + Modifiers { + ctrl: true, + alt: true, + shift: true, + meta: true, + }, + linux::from_state( + linux::SHIFT_MASK | linux::CONTROL_MASK | linux::MOD1_MASK | linux::MOD4_MASK + ) + ); + } + + #[test] + fn linux_lockmask_and_numlock_are_ignored() { + assert_eq!(Modifiers::default(), linux::from_state((1 << 1) | (1 << 4))); + } + + #[test] + fn linux_mod1_mapped_to_alt_mod4_mapped_to_meta() { + let a = linux::from_state(linux::MOD1_MASK); + assert!(a.alt && !a.meta); + let b = linux::from_state(linux::MOD4_MASK); + assert!(b.meta && !b.alt); + } + + #[test] + fn macos_no_flags_all_false() { + assert_eq!(Modifiers::default(), macos::from_flags(0)); + } + + #[test] + fn macos_command_alone_sets_only_meta() { + assert_eq!( + Modifiers { + meta: true, + ..Modifiers::default() + }, + macos::from_flags(macos::COMMAND_MASK) + ); + } + + #[test] + fn macos_option_alone_sets_only_alt() { + assert_eq!( + Modifiers { + alt: true, + ..Modifiers::default() + }, + macos::from_flags(macos::ALTERNATE_MASK) + ); + } + + #[test] + fn macos_cmd_shift_combo() { + assert_eq!( + Modifiers { + shift: true, + meta: true, + ..Modifiers::default() + }, + macos::from_flags(macos::COMMAND_MASK | macos::SHIFT_MASK) + ); + } + + #[test] + fn macos_all_four_modifiers_together() { + assert_eq!( + Modifiers { + ctrl: true, + alt: true, + shift: true, + meta: true, + }, + macos::from_flags( + macos::SHIFT_MASK + | macos::CONTROL_MASK + | macos::ALTERNATE_MASK + | macos::COMMAND_MASK + ) + ); + } + + #[test] + fn macos_unrelated_high_bits_ignored() { + assert_eq!(Modifiers::default(), macos::from_flags(0xff << 32)); + } + + #[test] + fn windows_no_high_bits_all_modifiers_false() { + assert_eq!(Modifiers::default(), windows::from_sampled(0, 0, 0, 0, 0)); + } + + #[test] + fn windows_low_bit_only_state_ignored() { + assert_eq!(Modifiers::default(), windows::from_sampled(1, 1, 1, 1, 0)); + } + + #[test] + fn windows_shift_only() { + assert_eq!( + Modifiers { + shift: true, + ..Modifiers::default() + }, + windows::from_sampled(windows::HIGH_BIT, 0, 0, 0, 0) + ); + } + + #[test] + fn windows_either_win_key_sets_meta() { + assert!(windows::from_sampled(0, 0, 0, windows::HIGH_BIT, 0).meta); + assert!(windows::from_sampled(0, 0, 0, 0, windows::HIGH_BIT).meta); + } + + #[test] + fn windows_all_four_modifiers_held() { + let m = windows::from_sampled( + windows::HIGH_BIT, + windows::HIGH_BIT, + windows::HIGH_BIT, + windows::HIGH_BIT, + 0, + ); + assert!(m.ctrl && m.alt && m.shift && m.meta); + } +} diff --git a/fluxer_desktop/native/rust/src/input/ring.rs b/fluxer_desktop/native/rust/src/input/ring.rs new file mode 100644 index 0000000..cd3d85e --- /dev/null +++ b/fluxer_desktop/native/rust/src/input/ring.rs @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::sync::atomic::{AtomicU64, Ordering}; + +#[derive(Debug)] +pub struct Ring { + pub slots: [T; CAPACITY], + head: AtomicU64, + tail: AtomicU64, + dropped: AtomicU64, +} + +impl Default for Ring { + fn default() -> Self { + Self::new() + } +} + +impl Ring { + const MASK: u64 = CAPACITY as u64 - 1; + + pub fn new() -> Self { + assert!( + CAPACITY > 0 && CAPACITY.is_power_of_two(), + "Ring capacity must be a power of two" + ); + Self { + slots: [T::default(); CAPACITY], + head: AtomicU64::new(0), + tail: AtomicU64::new(0), + dropped: AtomicU64::new(0), + } + } + + pub fn claim(&self) -> Option { + loop { + let head = self.head.load(Ordering::Relaxed); + let tail = self.tail.load(Ordering::Acquire); + if head.wrapping_sub(tail) >= CAPACITY as u64 { + self.dropped.fetch_add(1, Ordering::Relaxed); + return None; + } + if self + .head + .compare_exchange_weak( + head, + head.wrapping_add(1), + Ordering::Acquire, + Ordering::Relaxed, + ) + .is_ok() + { + return Some(head & Self::MASK); + } + } + } + + pub fn pop(&self) -> Option { + let tail = self.tail.load(Ordering::Relaxed); + let head = self.head.load(Ordering::Acquire); + if head == tail { + None + } else { + Some(tail & Self::MASK) + } + } + + pub fn release(&self) { + let tail = self.tail.load(Ordering::Relaxed); + self.tail.store(tail.wrapping_add(1), Ordering::Release); + } + + pub fn dropped_count(&self) -> u64 { + self.dropped.load(Ordering::Relaxed) + } + + pub fn len(&self) -> u64 { + let head = self.head.load(Ordering::Relaxed); + let tail = self.tail.load(Ordering::Relaxed); + head.wrapping_sub(tail) + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + #[cfg(test)] + fn set_counters_for_test(&self, value: u64) { + self.head.store(value, Ordering::Relaxed); + self.tail.store(value, Ordering::Relaxed); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty_ring_pop_returns_null_len_is_zero() { + let r: Ring = Ring::new(); + assert_eq!(0, r.len()); + assert_eq!(None, r.pop()); + } + + #[test] + fn single_producer_consumer_round_trip() { + let mut r: Ring = Ring::new(); + let i = r.claim().unwrap() as usize; + r.slots[i] = 42; + assert_eq!(1, r.len()); + let j = r.pop().unwrap() as usize; + assert_eq!(42, r.slots[j]); + r.release(); + assert_eq!(0, r.len()); + assert_eq!(None, r.pop()); + } + + #[test] + fn fill_to_capacity_then_drop() { + let mut r: Ring = Ring::new(); + for k in 0..4 { + let i = r.claim().unwrap() as usize; + r.slots[i] = k; + } + assert_eq!(4, r.len()); + assert_eq!(None, r.claim()); + assert_eq!(1, r.dropped_count()); + assert_eq!(None, r.claim()); + assert_eq!(2, r.dropped_count()); + } + + #[test] + fn full_vs_empty_distinction_head_tail_wrap() { + let mut r: Ring = Ring::new(); + for cycle in 0..10 { + for n in 0..4 { + let i = r.claim().unwrap() as usize; + r.slots[i] = cycle * 100 + n; + } + assert_eq!(None, r.claim()); + for m in 0..4 { + let j = r.pop().unwrap() as usize; + assert_eq!(cycle * 100 + m, r.slots[j]); + r.release(); + } + assert_eq!(None, r.pop()); + } + } + + #[test] + fn fifo_order_across_wraparound() { + let mut r: Ring = Ring::new(); + for value in 0..3 { + let idx = r.claim().unwrap() as usize; + r.slots[idx] = value; + } + let idx = r.pop().unwrap() as usize; + assert_eq!(0, r.slots[idx]); + r.release(); + let idx = r.pop().unwrap() as usize; + assert_eq!(1, r.slots[idx]); + r.release(); + for value in 3..=5 { + let idx = r.claim().unwrap() as usize; + r.slots[idx] = value; + } + for expected in 2..=5 { + let j = r.pop().unwrap() as usize; + assert_eq!(expected, r.slots[j]); + r.release(); + } + assert_eq!(None, r.pop()); + } + + #[test] + fn u64_counter_wrap_behavior_is_mask_correct() { + let mut r: Ring = Ring::new(); + let near_max = u64::MAX - 2; + r.set_counters_for_test(near_max); + assert_eq!(None, r.pop()); + for k in 0..4 { + let i = r.claim().unwrap() as usize; + r.slots[i] = k; + } + assert_eq!(None, r.claim()); + for m in 0..4 { + let j = r.pop().unwrap() as usize; + assert_eq!(m, r.slots[j]); + r.release(); + } + assert_eq!(None, r.pop()); + } + + #[test] + fn concurrent_producers_via_simulated_cas_contention() { + let r: Ring = Ring::new(); + let mut seen = [false; 8]; + for _ in 0..8 { + let idx = r.claim().unwrap() as usize; + assert!(!seen[idx]); + seen[idx] = true; + } + assert!(seen.iter().all(|value| *value)); + assert_eq!(None, r.claim()); + } + + #[test] + fn dropped_counter_survives_interleaved_pop() { + let r: Ring = Ring::new(); + assert!(r.claim().is_some()); + assert!(r.claim().is_some()); + assert_eq!(None, r.claim()); + assert_eq!(1, r.dropped_count()); + assert!(r.pop().is_some()); + r.release(); + assert!(r.claim().is_some()); + assert_eq!(None, r.claim()); + assert_eq!(2, r.dropped_count()); + } +} diff --git a/fluxer_desktop/native/rust/src/input/windows_mouse.rs b/fluxer_desktop/native/rust/src/input/windows_mouse.rs new file mode 100644 index 0000000..73e92ee --- /dev/null +++ b/fluxer_desktop/native/rust/src/input/windows_mouse.rs @@ -0,0 +1,172 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub const WM_MOUSEMOVE: u32 = 0x0200; +pub const WM_LBUTTONDOWN: u32 = 0x0201; +pub const WM_LBUTTONUP: u32 = 0x0202; +pub const WM_RBUTTONDOWN: u32 = 0x0204; +pub const WM_RBUTTONUP: u32 = 0x0205; +pub const WM_MBUTTONDOWN: u32 = 0x0207; +pub const WM_MBUTTONUP: u32 = 0x0208; +pub const WM_MOUSEWHEEL: u32 = 0x020a; +pub const WM_XBUTTONDOWN: u32 = 0x020b; +pub const WM_XBUTTONUP: u32 = 0x020c; +pub const WM_MOUSEHWHEEL: u32 = 0x020e; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Axis { + Vertical, + Horizontal, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Action { + Button { down: bool, button: u8 }, + Move, + Wheel { axis: Axis, delta: i16 }, + Ignored, +} + +pub fn classify(msg: u32, xbutton: u16, wheel_delta: i16) -> Action { + match msg { + WM_MOUSEMOVE => Action::Move, + WM_LBUTTONDOWN => Action::Button { + down: true, + button: 0, + }, + WM_LBUTTONUP => Action::Button { + down: false, + button: 0, + }, + WM_RBUTTONDOWN => Action::Button { + down: true, + button: 2, + }, + WM_RBUTTONUP => Action::Button { + down: false, + button: 2, + }, + WM_MBUTTONDOWN => Action::Button { + down: true, + button: 1, + }, + WM_MBUTTONUP => Action::Button { + down: false, + button: 1, + }, + WM_XBUTTONDOWN => match xbutton { + 1 => Action::Button { + down: true, + button: 3, + }, + 2 => Action::Button { + down: true, + button: 4, + }, + _ => Action::Ignored, + }, + WM_XBUTTONUP => match xbutton { + 1 => Action::Button { + down: false, + button: 3, + }, + 2 => Action::Button { + down: false, + button: 4, + }, + _ => Action::Ignored, + }, + WM_MOUSEWHEEL => Action::Wheel { + axis: Axis::Vertical, + delta: wheel_delta, + }, + WM_MOUSEHWHEEL => Action::Wheel { + axis: Axis::Horizontal, + delta: wheel_delta, + }, + _ => Action::Ignored, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn primary_buttons_up_down_resolve_to_012() { + assert_eq!( + Action::Button { + down: true, + button: 0 + }, + classify(WM_LBUTTONDOWN, 0, 0) + ); + assert_eq!( + Action::Button { + down: false, + button: 2 + }, + classify(WM_RBUTTONUP, 0, 0) + ); + assert_eq!( + Action::Button { + down: true, + button: 1 + }, + classify(WM_MBUTTONDOWN, 0, 0) + ); + } + + #[test] + fn xbutton_1_2_map_to_back_forward() { + assert_eq!( + Action::Button { + down: true, + button: 3 + }, + classify(WM_XBUTTONDOWN, 1, 0) + ); + assert_eq!( + Action::Button { + down: false, + button: 4 + }, + classify(WM_XBUTTONUP, 2, 0) + ); + assert_eq!(Action::Ignored, classify(WM_XBUTTONDOWN, 7, 0)); + } + + #[test] + fn vertical_wheel_preserves_signed_delta() { + assert_eq!( + Action::Wheel { + axis: Axis::Vertical, + delta: 120, + }, + classify(WM_MOUSEWHEEL, 0, 120) + ); + assert_eq!( + Action::Wheel { + axis: Axis::Vertical, + delta: -240, + }, + classify(WM_MOUSEWHEEL, 0, -240) + ); + } + + #[test] + fn horizontal_wheel_reports_horizontal_axis() { + assert_eq!( + Action::Wheel { + axis: Axis::Horizontal, + delta: 120, + }, + classify(WM_MOUSEHWHEEL, 0, 120) + ); + } + + #[test] + fn mouse_move_and_unknown_messages_distinguish_move_vs_ignored() { + assert_eq!(Action::Move, classify(WM_MOUSEMOVE, 0, 0)); + assert_eq!(Action::Ignored, classify(0xdead, 0, 0)); + } +} diff --git a/fluxer_desktop/native/rust/src/input/x11.rs b/fluxer_desktop/native/rust/src/input/x11.rs new file mode 100644 index 0000000..2142cfa --- /dev/null +++ b/fluxer_desktop/native/rust/src/input/x11.rs @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct XkbLookup { + pub group: u32, + pub level: u32, +} + +pub fn xkb_lookup_for_base() -> XkbLookup { + XkbLookup { group: 0, level: 0 } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn xkb_lookup_for_base_pins_group_and_level_to_unshifted_base_keysym() { + let lookup = xkb_lookup_for_base(); + assert_eq!(0, lookup.group); + assert_eq!(0, lookup.level); + } +} diff --git a/fluxer_desktop/native/rust/src/lib.rs b/fluxer_desktop/native/rust/src/lib.rs new file mode 100644 index 0000000..781ec5d --- /dev/null +++ b/fluxer_desktop/native/rust/src/lib.rs @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub mod audio; +pub mod dbus; +pub mod input; +pub mod linux_audio; +pub mod linux_evdev; +pub mod linux_portals; +pub mod mac_app_audio; +pub mod mac_sysctl; +pub mod platform_info; +pub mod system_hunspell; +pub mod voice; +pub mod win_process_loopback; diff --git a/fluxer_desktop/native/rust/src/linux_audio/mod.rs b/fluxer_desktop/native/rust/src/linux_audio/mod.rs new file mode 100644 index 0000000..3a187ac --- /dev/null +++ b/fluxer_desktop/native/rust/src/linux_audio/mod.rs @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub mod routing; diff --git a/fluxer_desktop/native/rust/src/linux_audio/routing.rs b/fluxer_desktop/native/rust/src/linux_audio/routing.rs new file mode 100644 index 0000000..4dc296d --- /dev/null +++ b/fluxer_desktop/native/rust/src/linux_audio/routing.rs @@ -0,0 +1,689 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::collections::{BTreeMap, HashSet}; + +pub type PropMap = BTreeMap; +pub type PropPattern = PropMap; + +pub const MEDIA_CLASS_PLAYBACK_STREAM: &str = "Stream/Output/Audio"; + +#[derive(Debug, Default, Clone)] +pub struct SelfIdentity { + pub pids: HashSet, + pub binaries: HashSet, + pub display_names: HashSet, + pub display_prefixes: Vec, +} + +impl SelfIdentity { + pub fn add_pid(&mut self, pid: impl Into) { + let pid = pid.into(); + if !pid.is_empty() { + self.pids.insert(pid); + } + } + + pub fn add_binary(&mut self, name: impl Into) { + let name = name.into(); + if !name.is_empty() { + self.binaries.insert(name); + } + } + + pub fn add_display_name(&mut self, name: impl Into) { + let name = name.into(); + if !name.is_empty() { + self.display_names.insert(name); + } + } + + pub fn add_display_prefix(&mut self, prefix: impl Into) { + let prefix = prefix.into(); + if !prefix.is_empty() { + self.display_prefixes.push(prefix); + } + } + + pub fn matches(&self, properties: &PropMap) -> bool { + properties + .get("application.process.id") + .is_some_and(|raw| self.pids.contains(raw)) + || properties + .get("pipewire.sec.pid") + .is_some_and(|raw| self.pids.contains(raw)) + || properties + .get("application.process.binary") + .is_some_and(|raw| contains_case_insensitive(&self.binaries, raw)) + || [ + "application.name", + "node.name", + "node.nick", + "node.description", + ] + .iter() + .any(|key| { + properties + .get(*key) + .is_some_and(|raw| self.matches_display_identity(raw)) + }) + } + + fn matches_display_identity(&self, raw: &str) -> bool { + contains_case_insensitive(&self.binaries, raw) + || contains_case_insensitive(&self.display_names, raw) + || self + .display_prefixes + .iter() + .any(|prefix| starts_with_case_insensitive(raw, prefix)) + } +} + +fn contains_case_insensitive(values: &HashSet, needle: &str) -> bool { + values + .iter() + .any(|candidate| candidate.eq_ignore_ascii_case(needle)) +} + +fn starts_with_case_insensitive(value: &str, prefix: &str) -> bool { + value + .get(..prefix.len()) + .is_some_and(|head| head.eq_ignore_ascii_case(prefix)) +} + +#[derive(Debug, Default, Clone)] +pub struct RoutingRule { + pub include_when: Vec, + pub never_when: Vec, + pub pin_target_for: Vec, + pub skip_hardware_devices: bool, + pub only_audio_sinks: bool, + pub only_default_audio_sink: bool, +} + +pub fn matches_pattern(candidate: &PropMap, expected_pattern: &PropPattern) -> bool { + expected_pattern + .iter() + .all(|(key, expected)| candidate.get(key).is_some_and(|actual| actual == expected)) +} + +pub fn matches_any(candidate: &PropMap, patterns: &[PropPattern]) -> bool { + patterns.iter().any(|item| matches_pattern(candidate, item)) +} + +pub fn should_route_node( + id: u32, + properties: &PropMap, + rule: &RoutingRule, + default_sink_name: &str, + default_sink_target_id: &str, + sink_global_id: u32, + self_identity: &SelfIdentity, +) -> bool { + if id == sink_global_id { + return false; + } + if self_identity.matches(properties) { + return false; + } + if matches_any(properties, &rule.never_when) { + return false; + } + if rule.skip_hardware_devices && properties.contains_key("device.id") { + return false; + } + if properties.get("media.class").map(String::as_str) != Some(MEDIA_CLASS_PLAYBACK_STREAM) { + return false; + } + if !rule.include_when.is_empty() { + return matches_any(properties, &rule.include_when); + } + if rule.only_audio_sinks { + if rule.only_default_audio_sink + && !targets_default_sink(properties, default_sink_name, default_sink_target_id) + { + return false; + } + return true; + } + false +} + +pub fn targets_default_sink( + properties: &PropMap, + default_sink_name: &str, + default_sink_target_id: &str, +) -> bool { + if default_sink_name.is_empty() && default_sink_target_id.is_empty() { + return true; + } + let Some(target) = properties + .get("target.object") + .or_else(|| properties.get("node.target")) + else { + return true; + }; + (!default_sink_name.is_empty() && target == default_sink_name) + || (!default_sink_target_id.is_empty() && target == default_sink_target_id) +} + +pub fn parse_default_sink_name(blob: &str) -> String { + let mut idx = 0; + while let Some(name_pos) = blob[idx..].find("\"name\"") { + idx += name_pos + "\"name\"".len(); + let rest = blob[idx..].trim_start(); + let Some(after_colon) = rest.strip_prefix(':') else { + continue; + }; + let value = after_colon.trim_start(); + let Some(mut value) = value.strip_prefix('"') else { + return String::new(); + }; + let mut out = String::new(); + while let Some(ch) = value.chars().next() { + value = &value[ch.len_utf8()..]; + match ch { + '"' => return out, + '\\' => { + if let Some(escaped) = value.chars().next() { + value = &value[escaped.len_utf8()..]; + out.push(escaped); + } + } + _ => out.push(ch), + } + } + return String::new(); + } + String::new() +} + +pub fn map(entries: &[(&str, &str)]) -> PropMap { + entries + .iter() + .map(|(key, value)| ((*key).to_owned(), (*value).to_owned())) + .collect() +} + +pub fn system_rule() -> RoutingRule { + RoutingRule { + skip_hardware_devices: true, + only_audio_sinks: true, + only_default_audio_sink: true, + ..RoutingRule::default() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn empty_pattern_matches_any_candidate() { + let candidate = map(&[("application.name", "Example")]); + let empty = PropPattern::new(); + assert!(matches_pattern(&candidate, &empty)); + } + + #[test] + fn missing_keys_and_mismatched_values_do_not_match() { + let candidate = map(&[("application.name", "Example")]); + let missing = map(&[("application.process.id", "1234")]); + let mismatched = map(&[("application.name", "Other")]); + assert!(!matches_pattern(&candidate, &missing)); + assert!(!matches_pattern(&candidate, &mismatched)); + } + + #[test] + fn matches_any_requires_at_least_one_matching_pattern() { + let candidate = map(&[("application.name", "Example")]); + let patterns = vec![ + map(&[("application.name", "Other")]), + map(&[("application.name", "Example")]), + ]; + assert!(matches_any(&candidate, &patterns)); + assert!(!matches_any(&candidate, &[])); + } + + #[test] + fn system_mode_routes_only_default_playback_streams() { + let self_identity = SelfIdentity::default(); + let analog = "alsa_output.pci-0000_00_1f.3.analog-stereo"; + let hdmi = "alsa_output.pci-0000_01_00.1.hdmi-stereo"; + let stream = map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("target.object", analog), + ]); + let other = map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("target.object", hdmi), + ]); + let rule = system_rule(); + assert!(should_route_node( + 100, + &stream, + &rule, + analog, + "", + 1, + &self_identity + )); + assert!(!should_route_node( + 101, + &other, + &rule, + analog, + "", + 1, + &self_identity + )); + } + + #[test] + fn structural_self_identity_wins_over_include_rules() { + let mut self_identity = SelfIdentity::default(); + self_identity.add_pid("4242"); + self_identity.add_binary("fluxer"); + let rule = RoutingRule { + include_when: vec![map(&[("application.process.id", "4242")])], + ..RoutingRule::default() + }; + let by_pid = map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("application.process.id", "4242"), + ]); + let by_binary = map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("application.process.binary", "fluxer"), + ]); + assert!(!should_route_node( + 200, + &by_pid, + &rule, + "", + "", + 0, + &self_identity + )); + assert!(!should_route_node( + 201, + &by_binary, + &rule, + "", + "", + 0, + &self_identity + )); + } + + #[test] + fn routing_refuses_non_playback_media_classes_even_when_included() { + let self_identity = SelfIdentity::default(); + let rule = RoutingRule { + include_when: vec![map(&[("application.name", "Recorder")])], + ..RoutingRule::default() + }; + let input_stream = map(&[ + ("media.class", "Stream/Input/Audio"), + ("application.name", "Recorder"), + ]); + let device = map(&[ + ("media.class", "Audio/Source"), + ("application.name", "Recorder"), + ]); + assert!(!should_route_node( + 300, + &input_stream, + &rule, + "", + "", + 0, + &self_identity + )); + assert!(!should_route_node( + 301, + &device, + &rule, + "", + "", + 0, + &self_identity + )); + } + + #[test] + fn system_mode_accepts_untargeted_and_node_target_streams() { + let self_identity = SelfIdentity::default(); + let analog = "alsa_output.pci-0000_00_1f.3.analog-stereo"; + let untargeted = map(&[("media.class", MEDIA_CLASS_PLAYBACK_STREAM)]); + let deprecated = map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("node.target", analog), + ]); + let rule = system_rule(); + assert!(should_route_node( + 100, + &untargeted, + &rule, + analog, + "", + 1, + &self_identity + )); + assert!(should_route_node( + 101, + &deprecated, + &rule, + analog, + "", + 1, + &self_identity + )); + } + + #[test] + fn system_mode_accepts_default_sink_object_id_targets() { + let self_identity = SelfIdentity::default(); + let analog = "alsa_output.pci-0000_00_1f.3.analog-stereo"; + let by_name = map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("target.object", analog), + ]); + let by_id = map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("target.object", "42"), + ]); + let other = map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("target.object", "99"), + ]); + let rule = system_rule(); + assert!(should_route_node( + 100, + &by_name, + &rule, + analog, + "42", + 1, + &self_identity + )); + assert!(should_route_node( + 101, + &by_id, + &rule, + analog, + "42", + 1, + &self_identity + )); + assert!(!should_route_node( + 102, + &other, + &rule, + analog, + "42", + 1, + &self_identity + )); + } + + #[test] + fn include_mode_honors_hardware_filtering_and_never_rules() { + let self_identity = SelfIdentity::default(); + let rule = RoutingRule { + include_when: vec![map(&[("application.name", "Firefox")])], + never_when: vec![map(&[("application.process.id", "999")])], + skip_hardware_devices: true, + ..RoutingRule::default() + }; + let app = map(&[ + ("application.name", "Firefox"), + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ]); + let hardware = map(&[("application.name", "Firefox"), ("device.id", "5")]); + let blocked = map(&[ + ("application.name", "Firefox"), + ("application.process.id", "999"), + ]); + assert!(should_route_node( + 10, + &app, + &rule, + "", + "", + 1, + &self_identity + )); + assert!(!should_route_node( + 11, + &hardware, + &rule, + "", + "", + 1, + &self_identity + )); + assert!(!should_route_node( + 12, + &blocked, + &rule, + "", + "", + 1, + &self_identity + )); + } + + #[test] + fn include_mode_rejects_non_playback_nodes_that_match_include_filter() { + let self_identity = SelfIdentity::default(); + let rule = RoutingRule { + include_when: vec![map(&[("application.name", "Chromium")])], + ..RoutingRule::default() + }; + let rejected = [ + map(&[ + ("application.name", "Chromium"), + ("media.class", "Audio/Source"), + ]), + map(&[ + ("application.name", "Chromium"), + ("media.class", "Audio/Sink"), + ]), + map(&[ + ("application.name", "Chromium"), + ("media.class", "Stream/Input/Audio"), + ]), + map(&[ + ("application.name", "Chromium"), + ("media.class", "Audio/Source/Virtual"), + ]), + map(&[("application.name", "Chromium")]), + ]; + for (idx, props) in rejected.iter().enumerate() { + assert!(!should_route_node( + 20 + idx as u32, + props, + &rule, + "", + "", + 1, + &self_identity + )); + } + let playback = map(&[ + ("application.name", "Chromium"), + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ]); + assert!(should_route_node( + 25, + &playback, + &rule, + "", + "", + 1, + &self_identity + )); + } + + #[test] + fn empty_rules_route_nothing_and_sink_id_is_excluded() { + let self_identity = SelfIdentity::default(); + let app = map(&[ + ("application.name", "Foo"), + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ]); + assert!(!should_route_node( + 1, + &app, + &RoutingRule::default(), + "", + "", + 0, + &self_identity + )); + assert!(!should_route_node( + 7, + &app, + &system_rule(), + "", + "", + 7, + &self_identity + )); + } + + #[test] + fn parse_default_sink_name_is_strict_and_tolerant() { + assert_eq!( + "alsa_output.foo", + parse_default_sink_name("{\"name\":\"alsa_output.foo\",\"other\":\"bar\"}") + ); + assert_eq!("", parse_default_sink_name("not-json")); + assert_eq!("", parse_default_sink_name("{\"name\":42}")); + } + + #[test] + fn structural_self_exclude_beats_include_rules_across_pid_binary_name_keys() { + let mut self_identity = SelfIdentity::default(); + let pid = std::process::id().to_string(); + self_identity.add_pid(&pid); + self_identity.add_binary("fluxer"); + self_identity.add_binary("fluxer.exe"); + self_identity.add_display_name("Fluxer Canary"); + self_identity.add_display_prefix("Fluxer "); + + let rule = RoutingRule { + include_when: vec![map(&[("application.process.binary", "firefox")])], + ..RoutingRule::default() + }; + let by_pid = map(&[ + ("application.process.id", &pid), + ("application.name", "fluxer"), + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ]); + let by_binary = map(&[ + ("application.process.id", "999999"), + ("application.process.binary", "fluxer"), + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ]); + let by_app_name = map(&[ + ("application.name", "fluxer"), + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ]); + let by_node_name = map(&[ + ("node.name", "fluxer"), + ("application.name", "Other"), + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ]); + let by_node_description = map(&[ + ("node.description", "Fluxer Direct Capture (pid 4242)"), + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ]); + let unrelated = map(&[ + ("application.name", "Firefox"), + ("application.process.id", "424242"), + ("application.process.binary", "firefox"), + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ]); + assert!(!should_route_node( + 101, + &by_pid, + &rule, + "", + "", + 1, + &self_identity + )); + assert!(!should_route_node( + 102, + &by_binary, + &rule, + "", + "", + 1, + &self_identity + )); + assert!(!should_route_node( + 103, + &by_app_name, + &rule, + "", + "", + 1, + &self_identity + )); + assert!(!should_route_node( + 104, + &by_node_name, + &rule, + "", + "", + 1, + &self_identity + )); + assert!(!should_route_node( + 107, + &by_node_description, + &rule, + "", + "", + 1, + &self_identity + )); + assert!(should_route_node( + 105, + &unrelated, + &rule, + "", + "", + 1, + &self_identity + )); + assert!(!should_route_node( + 106, + &by_pid, + &system_rule(), + "", + "", + 1, + &self_identity + )); + } + + #[test] + fn self_identity_matches_across_the_four_documented_pipewire_keys() { + let mut self_identity = SelfIdentity::default(); + self_identity.add_pid("1234"); + self_identity.add_binary("fluxer"); + self_identity.add_display_name("Fluxer Canary"); + self_identity.add_display_prefix("Fluxer "); + assert!(self_identity.matches(&map(&[("application.process.id", "1234")]))); + assert!(self_identity.matches(&map(&[("pipewire.sec.pid", "1234")]))); + assert!(self_identity.matches(&map(&[("application.process.binary", "fluxer")]))); + assert!(self_identity.matches(&map(&[("application.name", "fluxer")]))); + assert!(self_identity.matches(&map(&[("node.name", "fluxer")]))); + assert!(self_identity.matches(&map(&[("node.nick", "Fluxer Canary")]))); + assert!(self_identity.matches(&map(&[("node.description", "Fluxer app audio capture",)]))); + assert!(!self_identity.matches(&map(&[("application.process.id", "9999")]))); + } +} diff --git a/fluxer_desktop/native/rust/src/linux_evdev/event.rs b/fluxer_desktop/native/rust/src/linux_evdev/event.rs new file mode 100644 index 0000000..746e65a --- /dev/null +++ b/fluxer_desktop/native/rust/src/linux_evdev/event.rs @@ -0,0 +1,124 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub const EV_KEY: u16 = 0x01; + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct InputEvent { + pub time_sec: i64, + pub time_usec: i64, + pub event_type: u16, + pub code: u16, + pub value: i32, +} + +impl InputEvent { + pub const BYTE_LEN: usize = 24; + + pub fn from_ne_bytes(bytes: [u8; Self::BYTE_LEN]) -> Self { + let mut time_sec = [0_u8; 8]; + let mut time_usec = [0_u8; 8]; + let mut event_type = [0_u8; 2]; + let mut code = [0_u8; 2]; + let mut value = [0_u8; 4]; + + time_sec.copy_from_slice(&bytes[0..8]); + time_usec.copy_from_slice(&bytes[8..16]); + event_type.copy_from_slice(&bytes[16..18]); + code.copy_from_slice(&bytes[18..20]); + value.copy_from_slice(&bytes[20..24]); + + Self { + time_sec: i64::from_ne_bytes(time_sec), + time_usec: i64::from_ne_bytes(time_usec), + event_type: u16::from_ne_bytes(event_type), + code: u16::from_ne_bytes(code), + value: i32::from_ne_bytes(value), + } + } +} + +pub fn parse_input_event(bytes: &[u8]) -> Option { + let chunk: [u8; InputEvent::BYTE_LEN] = bytes.get(..InputEvent::BYTE_LEN)?.try_into().ok()?; + Some(InputEvent::from_ne_bytes(chunk)) +} + +pub fn parse_input_events(bytes: &[u8]) -> impl Iterator + '_ { + bytes.chunks_exact(InputEvent::BYTE_LEN).map(|chunk| { + let mut event = [0_u8; InputEvent::BYTE_LEN]; + event.copy_from_slice(chunk); + InputEvent::from_ne_bytes(event) + }) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn event_bytes(event: InputEvent) -> [u8; InputEvent::BYTE_LEN] { + let mut out = [0_u8; InputEvent::BYTE_LEN]; + out[0..8].copy_from_slice(&event.time_sec.to_ne_bytes()); + out[8..16].copy_from_slice(&event.time_usec.to_ne_bytes()); + out[16..18].copy_from_slice(&event.event_type.to_ne_bytes()); + out[18..20].copy_from_slice(&event.code.to_ne_bytes()); + out[20..24].copy_from_slice(&event.value.to_ne_bytes()); + out + } + + #[test] + fn input_event_layout_matches_64_bit_linux_abi() { + assert_eq!(24, std::mem::size_of::()); + assert_eq!(0, std::mem::offset_of!(InputEvent, time_sec)); + assert_eq!(8, std::mem::offset_of!(InputEvent, time_usec)); + assert_eq!(16, std::mem::offset_of!(InputEvent, event_type)); + assert_eq!(18, std::mem::offset_of!(InputEvent, code)); + assert_eq!(20, std::mem::offset_of!(InputEvent, value)); + } + + #[test] + fn parses_synthetic_key_a_press() { + let raw = event_bytes(InputEvent { + time_sec: 123, + time_usec: 456, + event_type: EV_KEY, + code: 30, + value: 1, + }); + + assert_eq!( + Some(InputEvent { + time_sec: 123, + time_usec: 456, + event_type: EV_KEY, + code: 30, + value: 1, + }), + parse_input_event(&raw) + ); + } + + #[test] + fn parses_back_to_back_events_and_ignores_trailing_partial_bytes() { + let first = InputEvent { + time_sec: 1, + time_usec: 2, + event_type: EV_KEY, + code: 30, + value: 1, + }; + let second = InputEvent { + time_sec: 3, + time_usec: 4, + event_type: EV_KEY, + code: 30, + value: 0, + }; + let mut bytes = Vec::new(); + bytes.extend_from_slice(&event_bytes(first)); + bytes.extend_from_slice(&event_bytes(second)); + bytes.extend_from_slice(&[0xaa, 0xbb]); + + let events: Vec<_> = parse_input_events(&bytes).collect(); + assert_eq!(vec![first, second], events); + } +} diff --git a/fluxer_desktop/native/rust/src/linux_evdev/keymap.rs b/fluxer_desktop/native/rust/src/linux_evdev/keymap.rs new file mode 100644 index 0000000..4e37948 --- /dev/null +++ b/fluxer_desktop/native/rust/src/linux_evdev/keymap.rs @@ -0,0 +1,653 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::input::keymap::KeyMapU16; + +pub const KEY_MAP: &[KeyMapU16] = &[ + KeyMapU16 { + code: 1, + name: "Escape", + }, + KeyMapU16 { code: 2, name: "1" }, + KeyMapU16 { code: 3, name: "2" }, + KeyMapU16 { code: 4, name: "3" }, + KeyMapU16 { code: 5, name: "4" }, + KeyMapU16 { code: 6, name: "5" }, + KeyMapU16 { code: 7, name: "6" }, + KeyMapU16 { code: 8, name: "7" }, + KeyMapU16 { code: 9, name: "8" }, + KeyMapU16 { + code: 10, + name: "9", + }, + KeyMapU16 { + code: 11, + name: "0", + }, + KeyMapU16 { + code: 12, + name: "Minus", + }, + KeyMapU16 { + code: 13, + name: "Equal", + }, + KeyMapU16 { + code: 14, + name: "Backspace", + }, + KeyMapU16 { + code: 15, + name: "Tab", + }, + KeyMapU16 { + code: 16, + name: "Q", + }, + KeyMapU16 { + code: 17, + name: "W", + }, + KeyMapU16 { + code: 18, + name: "E", + }, + KeyMapU16 { + code: 19, + name: "R", + }, + KeyMapU16 { + code: 20, + name: "T", + }, + KeyMapU16 { + code: 21, + name: "Y", + }, + KeyMapU16 { + code: 22, + name: "U", + }, + KeyMapU16 { + code: 23, + name: "I", + }, + KeyMapU16 { + code: 24, + name: "O", + }, + KeyMapU16 { + code: 25, + name: "P", + }, + KeyMapU16 { + code: 26, + name: "BracketLeft", + }, + KeyMapU16 { + code: 27, + name: "BracketRight", + }, + KeyMapU16 { + code: 28, + name: "Enter", + }, + KeyMapU16 { + code: 29, + name: "ControlLeft", + }, + KeyMapU16 { + code: 30, + name: "A", + }, + KeyMapU16 { + code: 31, + name: "S", + }, + KeyMapU16 { + code: 32, + name: "D", + }, + KeyMapU16 { + code: 33, + name: "F", + }, + KeyMapU16 { + code: 34, + name: "G", + }, + KeyMapU16 { + code: 35, + name: "H", + }, + KeyMapU16 { + code: 36, + name: "J", + }, + KeyMapU16 { + code: 37, + name: "K", + }, + KeyMapU16 { + code: 38, + name: "L", + }, + KeyMapU16 { + code: 39, + name: "Semicolon", + }, + KeyMapU16 { + code: 40, + name: "Quote", + }, + KeyMapU16 { + code: 41, + name: "Backquote", + }, + KeyMapU16 { + code: 42, + name: "ShiftLeft", + }, + KeyMapU16 { + code: 43, + name: "Backslash", + }, + KeyMapU16 { + code: 44, + name: "Z", + }, + KeyMapU16 { + code: 45, + name: "X", + }, + KeyMapU16 { + code: 46, + name: "C", + }, + KeyMapU16 { + code: 47, + name: "V", + }, + KeyMapU16 { + code: 48, + name: "B", + }, + KeyMapU16 { + code: 49, + name: "N", + }, + KeyMapU16 { + code: 50, + name: "M", + }, + KeyMapU16 { + code: 51, + name: "Comma", + }, + KeyMapU16 { + code: 52, + name: "Period", + }, + KeyMapU16 { + code: 53, + name: "Slash", + }, + KeyMapU16 { + code: 54, + name: "ShiftRight", + }, + KeyMapU16 { + code: 55, + name: "NumpadMultiply", + }, + KeyMapU16 { + code: 56, + name: "AltLeft", + }, + KeyMapU16 { + code: 57, + name: "Space", + }, + KeyMapU16 { + code: 58, + name: "CapsLock", + }, + KeyMapU16 { + code: 59, + name: "F1", + }, + KeyMapU16 { + code: 60, + name: "F2", + }, + KeyMapU16 { + code: 61, + name: "F3", + }, + KeyMapU16 { + code: 62, + name: "F4", + }, + KeyMapU16 { + code: 63, + name: "F5", + }, + KeyMapU16 { + code: 64, + name: "F6", + }, + KeyMapU16 { + code: 65, + name: "F7", + }, + KeyMapU16 { + code: 66, + name: "F8", + }, + KeyMapU16 { + code: 67, + name: "F9", + }, + KeyMapU16 { + code: 68, + name: "F10", + }, + KeyMapU16 { + code: 69, + name: "NumLock", + }, + KeyMapU16 { + code: 70, + name: "ScrollLock", + }, + KeyMapU16 { + code: 71, + name: "Numpad7", + }, + KeyMapU16 { + code: 72, + name: "Numpad8", + }, + KeyMapU16 { + code: 73, + name: "Numpad9", + }, + KeyMapU16 { + code: 74, + name: "NumpadSubtract", + }, + KeyMapU16 { + code: 75, + name: "Numpad4", + }, + KeyMapU16 { + code: 76, + name: "Numpad5", + }, + KeyMapU16 { + code: 77, + name: "Numpad6", + }, + KeyMapU16 { + code: 78, + name: "NumpadAdd", + }, + KeyMapU16 { + code: 79, + name: "Numpad1", + }, + KeyMapU16 { + code: 80, + name: "Numpad2", + }, + KeyMapU16 { + code: 81, + name: "Numpad3", + }, + KeyMapU16 { + code: 82, + name: "Numpad0", + }, + KeyMapU16 { + code: 83, + name: "NumpadDecimal", + }, + KeyMapU16 { + code: 86, + name: "IntlBackslash", + }, + KeyMapU16 { + code: 119, + name: "Pause", + }, + KeyMapU16 { + code: 87, + name: "F11", + }, + KeyMapU16 { + code: 88, + name: "F12", + }, + KeyMapU16 { + code: 89, + name: "IntlRo", + }, + KeyMapU16 { + code: 90, + name: "Lang3", + }, + KeyMapU16 { + code: 92, + name: "Convert", + }, + KeyMapU16 { + code: 93, + name: "KanaMode", + }, + KeyMapU16 { + code: 94, + name: "NonConvert", + }, + KeyMapU16 { + code: 96, + name: "NumpadEnter", + }, + KeyMapU16 { + code: 97, + name: "ControlRight", + }, + KeyMapU16 { + code: 98, + name: "NumpadDivide", + }, + KeyMapU16 { + code: 99, + name: "PrintScreen", + }, + KeyMapU16 { + code: 100, + name: "AltRight", + }, + KeyMapU16 { + code: 102, + name: "Home", + }, + KeyMapU16 { + code: 103, + name: "ArrowUp", + }, + KeyMapU16 { + code: 104, + name: "PageUp", + }, + KeyMapU16 { + code: 105, + name: "ArrowLeft", + }, + KeyMapU16 { + code: 106, + name: "ArrowRight", + }, + KeyMapU16 { + code: 107, + name: "End", + }, + KeyMapU16 { + code: 108, + name: "ArrowDown", + }, + KeyMapU16 { + code: 109, + name: "PageDown", + }, + KeyMapU16 { + code: 110, + name: "Insert", + }, + KeyMapU16 { + code: 111, + name: "Delete", + }, + KeyMapU16 { + code: 113, + name: "AudioVolumeMute", + }, + KeyMapU16 { + code: 114, + name: "AudioVolumeDown", + }, + KeyMapU16 { + code: 115, + name: "AudioVolumeUp", + }, + KeyMapU16 { + code: 116, + name: "Power", + }, + KeyMapU16 { + code: 117, + name: "NumpadEqual", + }, + KeyMapU16 { + code: 121, + name: "NumpadComma", + }, + KeyMapU16 { + code: 122, + name: "Lang1", + }, + KeyMapU16 { + code: 123, + name: "Lang2", + }, + KeyMapU16 { + code: 124, + name: "IntlYen", + }, + KeyMapU16 { + code: 125, + name: "MetaLeft", + }, + KeyMapU16 { + code: 126, + name: "MetaRight", + }, + KeyMapU16 { + code: 127, + name: "ContextMenu", + }, + KeyMapU16 { + code: 142, + name: "Sleep", + }, + KeyMapU16 { + code: 143, + name: "WakeUp", + }, + KeyMapU16 { + code: 148, + name: "LaunchApp1", + }, + KeyMapU16 { + code: 149, + name: "LaunchApp2", + }, + KeyMapU16 { + code: 155, + name: "LaunchMail", + }, + KeyMapU16 { + code: 158, + name: "BrowserBack", + }, + KeyMapU16 { + code: 159, + name: "BrowserForward", + }, + KeyMapU16 { + code: 163, + name: "MediaTrackNext", + }, + KeyMapU16 { + code: 164, + name: "MediaPlayPause", + }, + KeyMapU16 { + code: 165, + name: "MediaTrackPrevious", + }, + KeyMapU16 { + code: 166, + name: "MediaStop", + }, + KeyMapU16 { + code: 172, + name: "BrowserHome", + }, + KeyMapU16 { + code: 173, + name: "BrowserRefresh", + }, + KeyMapU16 { + code: 183, + name: "F13", + }, + KeyMapU16 { + code: 184, + name: "F14", + }, + KeyMapU16 { + code: 185, + name: "F15", + }, + KeyMapU16 { + code: 186, + name: "F16", + }, + KeyMapU16 { + code: 187, + name: "F17", + }, + KeyMapU16 { + code: 188, + name: "F18", + }, + KeyMapU16 { + code: 189, + name: "F19", + }, + KeyMapU16 { + code: 190, + name: "F20", + }, + KeyMapU16 { + code: 191, + name: "F21", + }, + KeyMapU16 { + code: 192, + name: "F22", + }, + KeyMapU16 { + code: 193, + name: "F23", + }, + KeyMapU16 { + code: 194, + name: "F24", + }, + KeyMapU16 { + code: 217, + name: "BrowserSearch", + }, + KeyMapU16 { + code: 226, + name: "LaunchMediaPlayer", + }, + KeyMapU16 { + code: 364, + name: "BrowserFavorites", + }, +]; + +pub const LEFT_CTRL: u16 = 29; +pub const RIGHT_CTRL: u16 = 97; +pub const LEFT_SHIFT: u16 = 42; +pub const RIGHT_SHIFT: u16 = 54; +pub const LEFT_ALT: u16 = 56; +pub const RIGHT_ALT: u16 = 100; +pub const LEFT_META: u16 = 125; +pub const RIGHT_META: u16 = 126; + +pub const BTN_LEFT: u16 = 0x110; +pub const BTN_RIGHT: u16 = 0x111; +pub const BTN_MIDDLE: u16 = 0x112; +pub const BTN_SIDE: u16 = 0x113; +pub const BTN_EXTRA: u16 = 0x114; +pub const BTN_FORWARD: u16 = 0x115; +pub const BTN_BACK: u16 = 0x116; + +pub fn evdev_button_to_browser_button(code: u16) -> Option { + match code { + BTN_LEFT => Some(0), + BTN_MIDDLE => Some(1), + BTN_RIGHT => Some(2), + BTN_SIDE | BTN_BACK => Some(3), + BTN_EXTRA | BTN_FORWARD => Some(4), + _ => None, + } +} + +pub fn keycode_to_name(code: u16) -> Option<&'static str> { + KEY_MAP + .iter() + .find(|entry| entry.code == code) + .map(|entry| entry.name) +} + +pub fn name_to_keycode(name: &str) -> u16 { + KEY_MAP + .iter() + .find(|entry| entry.name == name) + .map_or(0, |entry| entry.code) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn keycode_to_name_covers_canonical_letters_and_arrows() { + assert_eq!(Some("A"), keycode_to_name(30)); + assert_eq!(Some("Z"), keycode_to_name(44)); + assert_eq!(Some("Pause"), keycode_to_name(119)); + assert_eq!(Some("F13"), keycode_to_name(183)); + assert_eq!(Some("NumpadEnter"), keycode_to_name(96)); + assert_eq!(Some("AudioVolumeMute"), keycode_to_name(113)); + assert_eq!(Some("ArrowUp"), keycode_to_name(103)); + assert_eq!(Some("MetaLeft"), keycode_to_name(125)); + assert_eq!(None, keycode_to_name(0)); + assert_eq!(None, keycode_to_name(0xffff)); + } + + #[test] + fn name_to_keycode_round_trips_every_entry() { + for entry in KEY_MAP { + assert_eq!(entry.code, name_to_keycode(entry.name)); + } + assert_eq!(0, name_to_keycode("NoSuchKey")); + assert_eq!(0, name_to_keycode("")); + } + + #[test] + fn evdev_button_to_browser_button_matches_dom_convention() { + assert_eq!(Some(0), evdev_button_to_browser_button(BTN_LEFT)); + assert_eq!(Some(1), evdev_button_to_browser_button(BTN_MIDDLE)); + assert_eq!(Some(2), evdev_button_to_browser_button(BTN_RIGHT)); + assert_eq!(Some(3), evdev_button_to_browser_button(BTN_SIDE)); + assert_eq!(Some(3), evdev_button_to_browser_button(BTN_BACK)); + assert_eq!(Some(4), evdev_button_to_browser_button(BTN_EXTRA)); + assert_eq!(Some(4), evdev_button_to_browser_button(BTN_FORWARD)); + assert_eq!(None, evdev_button_to_browser_button(0x100)); + assert_eq!(None, evdev_button_to_browser_button(0xffff)); + } +} diff --git a/fluxer_desktop/native/rust/src/linux_evdev/mod.rs b/fluxer_desktop/native/rust/src/linux_evdev/mod.rs new file mode 100644 index 0000000..b43fbe3 --- /dev/null +++ b/fluxer_desktop/native/rust/src/linux_evdev/mod.rs @@ -0,0 +1,4 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub mod event; +pub mod keymap; diff --git a/fluxer_desktop/native/rust/src/linux_portals/decide.rs b/fluxer_desktop/native/rust/src/linux_portals/decide.rs new file mode 100644 index 0000000..df8f741 --- /dev/null +++ b/fluxer_desktop/native/rust/src/linux_portals/decide.rs @@ -0,0 +1,232 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PortalEntry { + pub id: String, + pub description: String, + pub preferred_trigger: String, +} + +impl PortalEntry { + pub fn new(id: &str, description: &str) -> Self { + Self { + id: id.to_owned(), + description: description.to_owned(), + preferred_trigger: String::new(), + } + } + + pub fn with_trigger(id: &str, description: &str, preferred_trigger: &str) -> Self { + Self { + id: id.to_owned(), + description: description.to_owned(), + preferred_trigger: preferred_trigger.to_owned(), + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct PortalBoundShortcut { + pub id: String, + pub description: String, + pub trigger_description: String, +} + +impl PortalBoundShortcut { + pub fn new(id: &str, description: &str, trigger_description: &str) -> Self { + Self { + id: id.to_owned(), + description: description.to_owned(), + trigger_description: trigger_description.to_owned(), + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum BindReason { + NoPersistedShortcuts, + NewIdsAdded, +} + +impl BindReason { + pub fn name(self) -> &'static str { + match self { + Self::NoPersistedShortcuts => "no-persisted-shortcuts", + Self::NewIdsAdded => "new-ids-added", + } + } +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ConfigureAction { + Reuse(Vec), + Bind(BindReason), +} + +pub fn decide(requested: &[PortalEntry], persisted: &[PortalBoundShortcut]) -> ConfigureAction { + if persisted.is_empty() { + return ConfigureAction::Bind(BindReason::NoPersistedShortcuts); + } + if requested + .iter() + .any(|entry| !has_persisted_shortcut(&entry.id, persisted)) + { + return ConfigureAction::Bind(BindReason::NewIdsAdded); + } + ConfigureAction::Reuse(persisted.to_vec()) +} + +pub fn has_persisted_shortcut(id: &str, persisted: &[PortalBoundShortcut]) -> bool { + persisted.iter().any(|shortcut| shortcut.id == id) +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PathError { + NoSpaceLeft, +} + +pub fn request_path( + unique_bus_name: &str, + handle_token: &str, + capacity: usize, +) -> Result { + let trimmed = unique_bus_name.strip_prefix(':').unwrap_or(unique_bus_name); + let normalized = trimmed.replace('.', "_"); + let path = format!("/org/freedesktop/portal/desktop/request/{normalized}/{handle_token}"); + if path.len() > capacity { + Err(PathError::NoSpaceLeft) + } else { + Ok(path) + } +} + +pub fn stable_key(entries: &[PortalEntry]) -> String { + let mut sorted = entries.to_vec(); + sorted.sort_by(|a, b| a.id.cmp(&b.id)); + let mut out = String::from("["); + for (index, entry) in sorted.iter().enumerate() { + if index != 0 { + out.push(','); + } + out.push_str("{id="); + out.push_str(&entry.id); + out.push_str(",desc="); + out.push_str(&entry.description); + out.push_str(",trig="); + out.push_str(&entry.preferred_trigger); + out.push('}'); + } + out.push(']'); + out +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn decide_empty_persisted_set_requests_first_time_bind() { + let action = decide(&[PortalEntry::new("mute", "Toggle mute")], &[]); + assert_eq!( + ConfigureAction::Bind(BindReason::NoPersistedShortcuts), + action + ); + } + + #[test] + fn decide_matching_ids_on_restart_reuses_persisted_bindings() { + let persisted = [ + PortalBoundShortcut::new("mute", "Toggle mute", "Ctrl+Shift+M"), + PortalBoundShortcut::new("deafen", "Toggle deafen", "Ctrl+Shift+D"), + ]; + let requested = [ + PortalEntry::new("mute", "Toggle mute"), + PortalEntry::new("deafen", "Toggle deafen"), + ]; + let action = decide(&requested, &persisted); + assert!(matches!(action, ConfigureAction::Reuse(shortcuts) if shortcuts.len() == 2)); + } + + #[test] + fn decide_new_id_added_since_last_launch_triggers_fresh_bind() { + let persisted = [PortalBoundShortcut::new( + "mute", + "Toggle mute", + "Ctrl+Shift+M", + )]; + let requested = [ + PortalEntry::new("mute", "Toggle mute"), + PortalEntry::new("push_to_talk", "Push to talk"), + ]; + assert_eq!( + ConfigureAction::Bind(BindReason::NewIdsAdded), + decide(&requested, &persisted) + ); + } + + #[test] + fn decide_removing_previously_persisted_id_does_not_rebind() { + let persisted = [ + PortalBoundShortcut::new("mute", "Toggle mute", "Ctrl+Shift+M"), + PortalBoundShortcut::new("deafen", "Toggle deafen", "Ctrl+Shift+D"), + ]; + let requested = [PortalEntry::new("mute", "Toggle mute")]; + assert!(matches!( + decide(&requested, &persisted), + ConfigureAction::Reuse(_) + )); + } + + #[test] + fn decide_empty_requested_with_empty_persisted_reports_no_persisted() { + assert_eq!( + ConfigureAction::Bind(BindReason::NoPersistedShortcuts), + decide(&[], &[]) + ); + } + + #[test] + fn request_path_well_known_shape_per_portal_spec() { + assert_eq!( + "/org/freedesktop/portal/desktop/request/1_42/fluxer_gs_create_xyz", + request_path(":1.42", "fluxer_gs_create_xyz", 256).unwrap() + ); + } + + #[test] + fn request_path_handles_unique_names_without_leading_colon() { + assert_eq!( + "/org/freedesktop/portal/desktop/request/1_0_7/tok", + request_path("1.0.7", "tok", 256).unwrap() + ); + } + + #[test] + fn request_path_rejects_too_small_buffer() { + assert_eq!( + Err(PathError::NoSpaceLeft), + request_path(":1.42", "tok", 16) + ); + } + + #[test] + fn stable_key_sorts_by_id_and_includes_description_and_trigger() { + let key = stable_key(&[ + PortalEntry::new("mute", "Toggle mute"), + PortalEntry::with_trigger("deafen", "Toggle deafen", "Ctrl+D"), + ]); + assert_eq!( + "[{id=deafen,desc=Toggle deafen,trig=Ctrl+D},{id=mute,desc=Toggle mute,trig=}]", + key + ); + } + + #[test] + fn bind_reason_name_matches_js_side_strings() { + assert_eq!( + "no-persisted-shortcuts", + BindReason::NoPersistedShortcuts.name() + ); + assert_eq!("new-ids-added", BindReason::NewIdsAdded.name()); + } +} diff --git a/fluxer_desktop/native/rust/src/linux_portals/mod.rs b/fluxer_desktop/native/rust/src/linux_portals/mod.rs new file mode 100644 index 0000000..5f470d0 --- /dev/null +++ b/fluxer_desktop/native/rust/src/linux_portals/mod.rs @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub mod decide; +pub mod pid_payload; +pub mod portal_snapshot; +pub mod window_pid; +pub mod x11_window_pid; diff --git a/fluxer_desktop/native/rust/src/linux_portals/pid_payload.rs b/fluxer_desktop/native/rust/src/linux_portals/pid_payload.rs new file mode 100644 index 0000000..615f409 --- /dev/null +++ b/fluxer_desktop/native/rust/src/linux_portals/pid_payload.rs @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub fn parse_shell_eval_pid_payload(payload: &str) -> Option { + let bytes = payload.as_bytes(); + let mut i = 0; + while i < bytes.len() { + if !bytes[i].is_ascii_digit() { + i += 1; + continue; + } + let start = i; + while i < bytes.len() && bytes[i].is_ascii_digit() { + i += 1; + } + if let Some(pid) = payload[start..i] + .parse::() + .ok() + .and_then(|n| u32::try_from(n).ok()) + .filter(|pid| *pid != 0) + { + return Some(pid); + } + } + None +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn extracts_plain_integer() { + assert_eq!(Some(1234), parse_shell_eval_pid_payload("1234")); + } + + #[test] + fn extracts_integer_from_json_array_form() { + assert_eq!(Some(4242), parse_shell_eval_pid_payload("[4242]")); + } + + #[test] + fn skips_zero_and_returns_next_positive() { + assert_eq!(Some(17), parse_shell_eval_pid_payload("[0, 17]")); + } + + #[test] + fn returns_null_when_no_digits_present() { + assert_eq!(None, parse_shell_eval_pid_payload("undefined")); + assert_eq!(None, parse_shell_eval_pid_payload("")); + } + + #[test] + fn rejects_values_that_overflow_u32() { + assert_eq!(None, parse_shell_eval_pid_payload("4294967296")); + } +} diff --git a/fluxer_desktop/native/rust/src/linux_portals/portal_snapshot.rs b/fluxer_desktop/native/rust/src/linux_portals/portal_snapshot.rs new file mode 100644 index 0000000..e0744ef --- /dev/null +++ b/fluxer_desktop/native/rust/src/linux_portals/portal_snapshot.rs @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::sync::atomic::{AtomicU64, Ordering}; +use std::time::{SystemTime, UNIX_EPOCH}; + +use super::decide::{PortalBoundShortcut, PortalEntry, has_persisted_shortcut, stable_key}; + +static TOKEN_SEQ: AtomicU64 = AtomicU64::new(1); + +pub fn mint_token(prefix: &str) -> String { + let seq = TOKEN_SEQ.fetch_add(1, Ordering::Relaxed); + let ms = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_millis()) + .unwrap_or(0); + format!("{prefix}_{ms:x}_{seq:x}") +} + +pub fn compute_stable_key(entries: &[PortalEntry]) -> String { + stable_key(entries) +} + +pub fn merge_shortcut_snapshots( + persisted: &[PortalBoundShortcut], + bound: &[PortalBoundShortcut], +) -> Vec { + let mut out = persisted.to_vec(); + for shortcut in bound { + if !has_persisted_shortcut(&shortcut.id, &out) { + out.push(shortcut.clone()); + } + } + out +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::linux_portals::decide::{PortalBoundShortcut, PortalEntry}; + + #[test] + fn compute_stable_key_is_deterministic_across_orderings() { + let a = compute_stable_key(&[ + PortalEntry::new("mute", "Toggle mute"), + PortalEntry::new("deafen", "Toggle deafen"), + ]); + let b = compute_stable_key(&[ + PortalEntry::new("deafen", "Toggle deafen"), + PortalEntry::new("mute", "Toggle mute"), + ]); + assert_eq!(a, b); + } + + #[test] + fn mint_token_is_well_formed_and_monotonically_distinct_within_process() { + let a = mint_token("fluxer_gs_create"); + let b = mint_token("fluxer_gs_create"); + assert_ne!(a, b); + assert!(a.starts_with("fluxer_gs_create_")); + } + + #[test] + fn merge_shortcut_snapshots_preserves_existing_and_adds_newly_bound_ids() { + let persisted = [PortalBoundShortcut::new( + "mute", + "Toggle mute", + "Ctrl+Shift+M", + )]; + let bound = [PortalBoundShortcut::new( + "push_to_talk", + "Push to talk", + "Ctrl+Shift+Space", + )]; + let merged = merge_shortcut_snapshots(&persisted, &bound); + assert_eq!(2, merged.len()); + assert_eq!("mute", merged[0].id); + assert_eq!("push_to_talk", merged[1].id); + } +} diff --git a/fluxer_desktop/native/rust/src/linux_portals/window_pid.rs b/fluxer_desktop/native/rust/src/linux_portals/window_pid.rs new file mode 100644 index 0000000..50c798b --- /dev/null +++ b/fluxer_desktop/native/rust/src/linux_portals/window_pid.rs @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PathError { + NoSpaceLeft, +} + +pub fn is_safe_kwin_path_segment(token: &str) -> bool { + !token.is_empty() + && token + .bytes() + .all(|ch| ch.is_ascii_alphanumeric() || ch == b'_') +} + +pub fn build_kwin_window_path(token: &str, capacity: usize) -> Result { + let path = format!("/org/kde/KWin/Window/{token}"); + if path.len() + 1 > capacity { + Err(PathError::NoSpaceLeft) + } else { + Ok(path) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn safe_kwin_path_segment_accepts_plain_alnum_underscore() { + assert!(is_safe_kwin_path_segment("abc")); + assert!(is_safe_kwin_path_segment("123")); + assert!(is_safe_kwin_path_segment("aZ_9")); + } + + #[test] + fn safe_kwin_path_segment_rejects_empty() { + assert!(!is_safe_kwin_path_segment("")); + } + + #[test] + fn safe_kwin_path_segment_rejects_path_traversal_and_shell_metacharacters() { + assert!(!is_safe_kwin_path_segment("../etc")); + assert!(!is_safe_kwin_path_segment("a/b")); + assert!(!is_safe_kwin_path_segment("$(rm -rf)")); + assert!(!is_safe_kwin_path_segment("a;b")); + assert!(!is_safe_kwin_path_segment("a-b")); + assert!(!is_safe_kwin_path_segment("a.b")); + } + + #[test] + fn build_kwin_window_path_shapes_path_correctly() { + assert_eq!( + "/org/kde/KWin/Window/abc123", + build_kwin_window_path("abc123", 128).unwrap() + ); + } + + #[test] + fn build_kwin_window_path_rejects_too_small_buffer() { + assert_eq!( + Err(PathError::NoSpaceLeft), + build_kwin_window_path("abc", 8) + ); + } +} diff --git a/fluxer_desktop/native/rust/src/linux_portals/x11_window_pid.rs b/fluxer_desktop/native/rust/src/linux_portals/x11_window_pid.rs new file mode 100644 index 0000000..16c1341 --- /dev/null +++ b/fluxer_desktop/native/rust/src/linux_portals/x11_window_pid.rs @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub type Window = u64; + +pub fn parse_window_token(token: &str) -> Option { + if token.is_empty() { + return None; + } + let parsed = if let Some(rest) = token + .strip_prefix("0x") + .or_else(|| token.strip_prefix("0X")) + { + u64::from_str_radix(rest, 16).ok()? + } else { + token.parse::().ok()? + }; + (parsed != 0).then_some(parsed) +} + +pub fn pid_from_long(value: i64) -> Option { + u32::try_from(value).ok().filter(|pid| *pid > 0) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_window_token_accepts_decimal_and_hexadecimal_xids() { + assert_eq!(Some(123), parse_window_token("123")); + assert_eq!(Some(0x3a00007), parse_window_token("0x3a00007")); + assert_eq!(Some(0x3a00007), parse_window_token("0X3a00007")); + } + + #[test] + fn parse_window_token_rejects_invalid_or_zero_xids() { + assert_eq!(None, parse_window_token("")); + assert_eq!(None, parse_window_token("0")); + assert_eq!(None, parse_window_token("0x")); + assert_eq!(None, parse_window_token("0xG")); + assert_eq!(None, parse_window_token("../123")); + assert_eq!(None, parse_window_token("123abc")); + } + + #[test] + fn pid_from_long_validates_positive_uint32_process_ids() { + assert_eq!(Some(1), pid_from_long(1)); + assert_eq!(Some(42_424), pid_from_long(42_424)); + assert_eq!(None, pid_from_long(0)); + assert_eq!(None, pid_from_long(-1)); + assert_eq!(None, pid_from_long(u32::MAX as i64 + 1)); + } +} diff --git a/fluxer_desktop/native/rust/src/mac_app_audio/audio_converter.rs b/fluxer_desktop/native/rust/src/mac_app_audio/audio_converter.rs new file mode 100644 index 0000000..1b1f42e --- /dev/null +++ b/fluxer_desktop/native/rust/src/mac_app_audio/audio_converter.rs @@ -0,0 +1,752 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct AudioStreamBasicDescription { + pub m_sample_rate: f64, + pub m_format_id: u32, + pub m_format_flags: u32, + pub m_bytes_per_packet: u32, + pub m_frames_per_packet: u32, + pub m_bytes_per_frame: u32, + pub m_channels_per_frame: u32, + pub m_bits_per_channel: u32, + pub m_reserved: u32, +} + +pub const K_AUDIO_FORMAT_LINEAR_PCM: u32 = + (('l' as u32) << 24) | (('p' as u32) << 16) | (('c' as u32) << 8) | ('m' as u32); + +pub const K_LINEAR_PCM_FORMAT_FLAG_IS_FLOAT: u32 = 1 << 0; +pub const K_LINEAR_PCM_FORMAT_FLAG_IS_BIG_ENDIAN: u32 = 1 << 1; +pub const K_LINEAR_PCM_FORMAT_FLAG_IS_SIGNED_INTEGER: u32 = 1 << 2; +pub const K_LINEAR_PCM_FORMAT_FLAG_IS_PACKED: u32 = 1 << 3; +pub const K_LINEAR_PCM_FORMAT_FLAG_IS_NON_INTERLEAVED: u32 = 1 << 5; + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct AudioBuffer { + pub m_number_channels: u32, + pub m_data_byte_size: u32, + pub m_data: *const u8, +} + +impl AudioBuffer { + pub fn from_slice(channels: u32, slice: &[T]) -> Self { + Self { + m_number_channels: channels, + m_data_byte_size: std::mem::size_of_val(slice) as u32, + m_data: slice.as_ptr().cast(), + } + } +} + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct AudioBufferListN { + pub m_number_buffers: u32, + pub buffers: [AudioBuffer; N], +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PcmConvertError { + UnsupportedFormat, + UnsupportedBitDepth, + MissingData, + OutputTooSmall, +} + +pub fn build_output_asbd(sample_rate: f64, channels: u32) -> AudioStreamBasicDescription { + let bytes_per_frame = 4 * channels; + AudioStreamBasicDescription { + m_sample_rate: sample_rate, + m_format_id: K_AUDIO_FORMAT_LINEAR_PCM, + m_format_flags: K_LINEAR_PCM_FORMAT_FLAG_IS_FLOAT | K_LINEAR_PCM_FORMAT_FLAG_IS_PACKED, + m_bytes_per_packet: bytes_per_frame, + m_frames_per_packet: 1, + m_bytes_per_frame: bytes_per_frame, + m_channels_per_frame: channels, + m_bits_per_channel: 32, + m_reserved: 0, + } +} + +pub fn build_input_asbd( + sample_rate: f64, + channels: u32, + non_interleaved: bool, +) -> AudioStreamBasicDescription { + let bytes_per_frame = if non_interleaved { 4 } else { 4 * channels }; + let mut flags = K_LINEAR_PCM_FORMAT_FLAG_IS_FLOAT | K_LINEAR_PCM_FORMAT_FLAG_IS_PACKED; + if non_interleaved { + flags |= K_LINEAR_PCM_FORMAT_FLAG_IS_NON_INTERLEAVED; + } + AudioStreamBasicDescription { + m_sample_rate: sample_rate, + m_format_id: K_AUDIO_FORMAT_LINEAR_PCM, + m_format_flags: flags, + m_bytes_per_packet: bytes_per_frame, + m_frames_per_packet: 1, + m_bytes_per_frame: bytes_per_frame, + m_channels_per_frame: channels, + m_bits_per_channel: 32, + m_reserved: 0, + } +} + +pub fn output_frame_capacity(input_frames: u32, in_rate: f64, out_rate: f64) -> u32 { + if in_rate <= 0.0 { + return input_frames; + } + ((input_frames as f64 * (out_rate / in_rate)).ceil() + 1.0) as u32 +} + +pub fn converted_frame_count(input_frames: u32, in_rate: f64, out_rate: f64) -> u32 { + if input_frames == 0 { + return 0; + } + if in_rate <= 0.0 || out_rate <= 0.0 || in_rate == out_rate { + return input_frames; + } + (input_frames as f64 * (out_rate / in_rate)).ceil() as u32 +} + +pub fn is_linear_pcm(asbd: AudioStreamBasicDescription) -> bool { + asbd.m_format_id == K_AUDIO_FORMAT_LINEAR_PCM +} + +pub fn is_float(asbd: AudioStreamBasicDescription) -> bool { + (asbd.m_format_flags & K_LINEAR_PCM_FORMAT_FLAG_IS_FLOAT) != 0 +} + +pub fn is_signed_integer(asbd: AudioStreamBasicDescription) -> bool { + (asbd.m_format_flags & K_LINEAR_PCM_FORMAT_FLAG_IS_SIGNED_INTEGER) != 0 +} + +pub fn is_big_endian(asbd: AudioStreamBasicDescription) -> bool { + (asbd.m_format_flags & K_LINEAR_PCM_FORMAT_FLAG_IS_BIG_ENDIAN) != 0 +} + +pub fn is_packed(asbd: AudioStreamBasicDescription) -> bool { + (asbd.m_format_flags & K_LINEAR_PCM_FORMAT_FLAG_IS_PACKED) != 0 +} + +pub fn is_non_interleaved(asbd: AudioStreamBasicDescription) -> bool { + (asbd.m_format_flags & K_LINEAR_PCM_FORMAT_FLAG_IS_NON_INTERLEAVED) != 0 +} + +pub fn is_native_f32_interleaved(asbd: AudioStreamBasicDescription) -> bool { + is_linear_pcm(asbd) + && is_float(asbd) + && asbd.m_bits_per_channel == 32 + && is_packed(asbd) + && !is_big_endian(asbd) + && !is_non_interleaved(asbd) +} + +pub fn is_native_f32_planar(asbd: AudioStreamBasicDescription) -> bool { + is_linear_pcm(asbd) + && is_float(asbd) + && asbd.m_bits_per_channel == 32 + && is_packed(asbd) + && !is_big_endian(asbd) + && is_non_interleaved(asbd) +} + +fn bytes_per_sample(asbd: AudioStreamBasicDescription) -> Result { + if !is_linear_pcm(asbd) { + return Err(PcmConvertError::UnsupportedFormat); + } + if asbd.m_bits_per_channel == 0 || !asbd.m_bits_per_channel.is_multiple_of(8) { + return Err(PcmConvertError::UnsupportedBitDepth); + } + let bytes = asbd.m_bits_per_channel / 8; + if matches!(bytes, 1 | 2 | 3 | 4 | 8) { + Ok(bytes) + } else { + Err(PcmConvertError::UnsupportedBitDepth) + } +} + +pub fn input_frame_count_for_buffer_list( + asbd: AudioStreamBasicDescription, + list: &AudioBufferListN, +) -> Result { + if !is_linear_pcm(asbd) { + return Err(PcmConvertError::UnsupportedFormat); + } + if list.m_number_buffers == 0 { + return Err(PcmConvertError::MissingData); + } + let first = &list.buffers[0]; + if first.m_data.is_null() || first.m_data_byte_size == 0 { + return Ok(0); + } + let channels = if asbd.m_channels_per_frame == 0 { + 1 + } else { + asbd.m_channels_per_frame + }; + let bps = bytes_per_sample(asbd)?; + let frame_stride = if asbd.m_bytes_per_frame != 0 { + asbd.m_bytes_per_frame + } else if is_non_interleaved(asbd) { + bps + } else { + bps * channels + }; + if frame_stride == 0 { + return Ok(0); + } + Ok(first.m_data_byte_size / frame_stride) +} + +fn read_unsigned(bytes: &[u8], big_endian: bool) -> u64 { + if big_endian { + bytes + .iter() + .fold(0_u64, |out, byte| (out << 8) | *byte as u64) + } else { + bytes.iter().enumerate().fold(0_u64, |out, (index, byte)| { + out | ((*byte as u64) << (index * 8)) + }) + } +} + +fn sign_extend(value: u64, bits: u32) -> i64 { + if bits == 64 { + return value as i64; + } + let shift = 64 - bits; + ((value << shift) as i64) >> shift +} + +fn pow2_float(exponent: u32) -> f64 { + 2_f64.powi(exponent as i32) +} + +fn read_scalar_sample( + asbd: AudioStreamBasicDescription, + bytes: &[u8], +) -> Result { + let bits = asbd.m_bits_per_channel; + if bits == 0 || bits > 64 || !bits.is_multiple_of(8) { + return Err(PcmConvertError::UnsupportedBitDepth); + } + let raw = read_unsigned(bytes, is_big_endian(asbd)); + if is_float(asbd) { + return match bits { + 32 => Ok(f32::from_bits(raw as u32)), + 64 => Ok(f64::from_bits(raw) as f32), + _ => Err(PcmConvertError::UnsupportedBitDepth), + }; + } + if is_signed_integer(asbd) { + let signed = sign_extend(raw, bits); + return Ok((signed as f64 / pow2_float(bits - 1)) as f32); + } + let midpoint = pow2_float(bits - 1); + Ok(((raw as f64 - midpoint) / midpoint) as f32) +} + +fn source_channel_for(target_channel: u32, source_channels: u32) -> u32 { + if source_channels <= 1 { + 0 + } else { + target_channel.min(source_channels - 1) + } +} + +fn read_frame_channel( + asbd: AudioStreamBasicDescription, + list: &AudioBufferListN, + frame_index: u32, + target_channel: u32, +) -> Result { + let channels = if asbd.m_channels_per_frame == 0 { + 1 + } else { + asbd.m_channels_per_frame + }; + let source_channel = source_channel_for(target_channel, channels); + let bps = bytes_per_sample(asbd)?; + let non_interleaved = is_non_interleaved(asbd); + let buffer_count = (list.m_number_buffers as usize).min(N); + if buffer_count == 0 { + return Err(PcmConvertError::MissingData); + } + let buffer_index = if non_interleaved && buffer_count > 1 { + (source_channel as usize).min(buffer_count - 1) + } else { + 0 + }; + let buffer = &list.buffers[buffer_index]; + if buffer.m_data.is_null() { + return Err(PcmConvertError::MissingData); + } + let data = + unsafe { std::slice::from_raw_parts(buffer.m_data, buffer.m_data_byte_size as usize) }; + let frame_stride = if asbd.m_bytes_per_frame != 0 { + asbd.m_bytes_per_frame + } else if non_interleaved { + bps + } else { + bps * channels + }; + let channel_offset = if non_interleaved && buffer_count > 1 { + 0 + } else { + source_channel * bps + }; + let offset = frame_index as usize * frame_stride as usize + channel_offset as usize; + let end = offset + bps as usize; + if end > data.len() { + return Err(PcmConvertError::MissingData); + } + read_scalar_sample(asbd, &data[offset..end]) +} + +fn aligned_f32_slice( + buffer: &AudioBuffer, + samples: usize, +) -> Result, PcmConvertError> { + if buffer.m_data.is_null() { + return Err(PcmConvertError::MissingData); + } + let bytes = samples * std::mem::size_of::(); + if (buffer.m_data_byte_size as usize) < bytes { + return Err(PcmConvertError::MissingData); + } + let address = buffer.m_data as usize; + if !address.is_multiple_of(std::mem::align_of::()) { + return Ok(None); + } + Ok(Some(unsafe { + std::slice::from_raw_parts(buffer.m_data.cast::(), samples) + })) +} + +fn copy_native_interleaved_f32( + asbd: AudioStreamBasicDescription, + list: &AudioBufferListN, + input_frames: u32, + output_channels: u32, + output: &mut [f32], +) -> Result, PcmConvertError> { + let source_channels = asbd.m_channels_per_frame.max(1); + let source_stride = source_channels * std::mem::size_of::() as u32; + if asbd.m_bytes_per_frame != 0 && asbd.m_bytes_per_frame != source_stride { + return Ok(None); + } + if list.m_number_buffers == 0 || N == 0 { + return Err(PcmConvertError::MissingData); + } + let source_samples = input_frames as usize * source_channels as usize; + let output_samples = input_frames as usize * output_channels as usize; + let Some(source) = aligned_f32_slice(&list.buffers[0], source_samples)? else { + return Ok(None); + }; + assert!(output_samples <= output.len()); + if source_channels == output_channels { + output[..output_samples].copy_from_slice(&source[..output_samples]); + return Ok(Some(input_frames)); + } + for frame in 0..input_frames as usize { + let source_base = frame * source_channels as usize; + let output_base = frame * output_channels as usize; + for channel in 0..output_channels as usize { + let source_channel = source_channel_for(channel as u32, source_channels) as usize; + output[output_base + channel] = source[source_base + source_channel]; + } + } + Ok(Some(input_frames)) +} + +fn copy_native_planar_f32( + asbd: AudioStreamBasicDescription, + list: &AudioBufferListN, + input_frames: u32, + output_channels: u32, + output: &mut [f32], +) -> Result, PcmConvertError> { + if asbd.m_bytes_per_frame != 0 && asbd.m_bytes_per_frame != std::mem::size_of::() as u32 { + return Ok(None); + } + let source_channels = asbd.m_channels_per_frame.max(1); + let buffer_count = (list.m_number_buffers as usize).min(N); + if buffer_count == 0 { + return Err(PcmConvertError::MissingData); + } + let output_samples = input_frames as usize * output_channels as usize; + assert!(output_samples <= output.len()); + for channel in 0..output_channels as usize { + let source_channel = source_channel_for(channel as u32, source_channels) as usize; + let buffer_index = if buffer_count > 1 { + source_channel.min(buffer_count - 1) + } else { + 0 + }; + let Some(source) = aligned_f32_slice(&list.buffers[buffer_index], input_frames as usize)? + else { + return Ok(None); + }; + for frame in 0..input_frames as usize { + output[frame * output_channels as usize + channel] = source[frame]; + } + } + Ok(Some(input_frames)) +} + +fn copy_native_f32( + asbd: AudioStreamBasicDescription, + list: &AudioBufferListN, + input_frames: u32, + output_channels: u32, + output: &mut [f32], +) -> Result, PcmConvertError> { + if is_native_f32_interleaved(asbd) { + return copy_native_interleaved_f32(asbd, list, input_frames, output_channels, output); + } + if is_native_f32_planar(asbd) { + return copy_native_planar_f32(asbd, list, input_frames, output_channels, output); + } + Ok(None) +} + +pub fn convert_buffer_list_to_interleaved_f32( + asbd: AudioStreamBasicDescription, + list: &AudioBufferListN, + input_frames: u32, + output_sample_rate: f64, + output_channels: u32, + output: &mut [f32], +) -> Result { + if !is_linear_pcm(asbd) { + return Err(PcmConvertError::UnsupportedFormat); + } + if output_channels == 0 { + return Ok(0); + } + let out_frames = converted_frame_count(input_frames, asbd.m_sample_rate, output_sample_rate); + let needed = out_frames as usize * output_channels as usize; + if needed > output.len() { + return Err(PcmConvertError::OutputTooSmall); + } + if input_frames == 0 || out_frames == 0 { + return Ok(0); + } + let native_frames = if asbd.m_sample_rate == output_sample_rate { + copy_native_f32(asbd, list, input_frames, output_channels, output)? + } else { + None + }; + if let Some(frames) = native_frames { + return Ok(frames); + } + let step = if asbd.m_sample_rate > 0.0 && output_sample_rate > 0.0 { + asbd.m_sample_rate / output_sample_rate + } else { + 1.0 + }; + for out_frame in 0..out_frames { + let src_pos = out_frame as f64 * step; + let mut base = src_pos.floor() as u32; + if base >= input_frames { + base = input_frames - 1; + } + let next = if base + 1 < input_frames { + base + 1 + } else { + base + }; + let frac = (src_pos - src_pos.floor()) as f32; + for ch in 0..output_channels { + let a = read_frame_channel(asbd, list, base, ch)?; + let b = read_frame_channel(asbd, list, next, ch)?; + output[out_frame as usize * output_channels as usize + ch as usize] = + a + (b - a) * frac; + } + } + Ok(out_frames) +} + +#[cfg(test)] +mod tests { + use std::mem::{offset_of, size_of}; + + use super::*; + + #[test] + fn audio_stream_basic_description_field_offsets_match_apple_abi() { + assert_eq!(0, offset_of!(AudioStreamBasicDescription, m_sample_rate)); + assert_eq!(8, offset_of!(AudioStreamBasicDescription, m_format_id)); + assert_eq!(12, offset_of!(AudioStreamBasicDescription, m_format_flags)); + assert_eq!( + 16, + offset_of!(AudioStreamBasicDescription, m_bytes_per_packet) + ); + assert_eq!( + 20, + offset_of!(AudioStreamBasicDescription, m_frames_per_packet) + ); + assert_eq!( + 24, + offset_of!(AudioStreamBasicDescription, m_bytes_per_frame) + ); + assert_eq!( + 28, + offset_of!(AudioStreamBasicDescription, m_channels_per_frame) + ); + assert_eq!( + 32, + offset_of!(AudioStreamBasicDescription, m_bits_per_channel) + ); + assert_eq!(36, offset_of!(AudioStreamBasicDescription, m_reserved)); + assert_eq!(40, size_of::()); + } + + #[test] + fn audio_buffer_offsets() { + assert_eq!(0, offset_of!(AudioBuffer, m_number_channels)); + assert_eq!(4, offset_of!(AudioBuffer, m_data_byte_size)); + assert_eq!(8, offset_of!(AudioBuffer, m_data)); + assert_eq!(16, size_of::()); + assert_eq!(0, offset_of!(AudioBufferListN<1>, m_number_buffers)); + assert_eq!(8, offset_of!(AudioBufferListN<1>, buffers)); + } + + #[test] + fn audio_format_linear_pcm_encodes_lpcm() { + assert_eq!(0x6c70636d, K_AUDIO_FORMAT_LINEAR_PCM); + } + + #[test] + fn build_output_asbd_stereo_48k_interleaved_float32() { + let a = build_output_asbd(48_000.0, 2); + assert_eq!(48_000.0, a.m_sample_rate); + assert_eq!(K_AUDIO_FORMAT_LINEAR_PCM, a.m_format_id); + assert_eq!( + K_LINEAR_PCM_FORMAT_FLAG_IS_FLOAT | K_LINEAR_PCM_FORMAT_FLAG_IS_PACKED, + a.m_format_flags + ); + assert_eq!(8, a.m_bytes_per_packet); + assert_eq!(1, a.m_frames_per_packet); + assert_eq!(8, a.m_bytes_per_frame); + assert_eq!(2, a.m_channels_per_frame); + assert_eq!(32, a.m_bits_per_channel); + assert_eq!(0, a.m_reserved); + } + + #[test] + fn build_output_asbd_mono_441k_interleaved_float32() { + let a = build_output_asbd(44_100.0, 1); + assert_eq!(44_100.0, a.m_sample_rate); + assert_eq!(4, a.m_bytes_per_packet); + assert_eq!(4, a.m_bytes_per_frame); + assert_eq!(1, a.m_channels_per_frame); + } + + #[test] + fn build_input_asbd_non_interleaved_sets_stride_to_4_bytes_per_channel_plane() { + let a = build_input_asbd(48_000.0, 2, true); + assert_ne!( + 0, + a.m_format_flags & K_LINEAR_PCM_FORMAT_FLAG_IS_NON_INTERLEAVED + ); + assert_eq!(4, a.m_bytes_per_frame); + assert_eq!(4, a.m_bytes_per_packet); + } + + #[test] + fn build_input_asbd_interleaved_omits_non_interleaved_flag() { + let a = build_input_asbd(48_000.0, 2, false); + assert_eq!( + 0, + a.m_format_flags & K_LINEAR_PCM_FORMAT_FLAG_IS_NON_INTERLEAVED + ); + assert_eq!(8, a.m_bytes_per_frame); + } + + #[test] + fn native_f32_predicates_require_packed_little_endian_layout() { + let interleaved = build_input_asbd(48_000.0, 2, false); + let planar = build_input_asbd(48_000.0, 2, true); + assert!(is_native_f32_interleaved(interleaved)); + assert!(is_native_f32_planar(planar)); + let mut padded = interleaved; + padded.m_format_flags &= !K_LINEAR_PCM_FORMAT_FLAG_IS_PACKED; + assert!(!is_native_f32_interleaved(padded)); + let mut big_endian = planar; + big_endian.m_format_flags |= K_LINEAR_PCM_FORMAT_FLAG_IS_BIG_ENDIAN; + assert!(!is_native_f32_planar(big_endian)); + } + + #[test] + fn output_frame_capacity_rounds_up_across_rate_ratios() { + assert_eq!(1025, output_frame_capacity(1024, 48_000.0, 48_000.0)); + assert_eq!(1116, output_frame_capacity(1024, 44_100.0, 48_000.0)); + assert_eq!(512, output_frame_capacity(512, 0.0, 48_000.0)); + } + + #[test] + fn converted_frame_count_computes_exact_output_length_without_safety_padding() { + assert_eq!(1024, converted_frame_count(1024, 48_000.0, 48_000.0)); + assert_eq!(1115, converted_frame_count(1024, 44_100.0, 48_000.0)); + } + + #[test] + fn convert_buffer_list_to_interleaved_f32_duplicates_mono_float32() { + let samples = [0.25_f32, -0.5, 1.0]; + let list = AudioBufferListN { + m_number_buffers: 1, + buffers: [AudioBuffer::from_slice(1, &samples)], + }; + let asbd = build_input_asbd(48_000.0, 1, false); + let mut out = [0.0_f32; 6]; + let frames = + convert_buffer_list_to_interleaved_f32(asbd, &list, 3, 48_000.0, 2, &mut out).unwrap(); + assert_eq!(3, frames); + assert_eq!([0.25, 0.25, -0.5, -0.5, 1.0, 1.0], out); + } + + #[test] + fn convert_buffer_list_to_interleaved_f32_copies_native_stereo_float32() { + let samples = [0.25_f32, -0.5, 1.0, -1.0, 0.125, -0.125]; + let list = AudioBufferListN { + m_number_buffers: 1, + buffers: [AudioBuffer::from_slice(2, &samples)], + }; + let asbd = build_input_asbd(48_000.0, 2, false); + let mut out = [0.0_f32; 6]; + let frames = + convert_buffer_list_to_interleaved_f32(asbd, &list, 3, 48_000.0, 2, &mut out).unwrap(); + assert_eq!(3, frames); + assert_eq!(samples, out); + } + + #[test] + fn convert_buffer_list_to_interleaved_f32_interleaves_planar_stereo_float32() { + let left = [0.1_f32, 0.2, 0.3]; + let right = [-0.1_f32, -0.2, -0.3]; + let list = AudioBufferListN { + m_number_buffers: 2, + buffers: [ + AudioBuffer::from_slice(1, &left), + AudioBuffer::from_slice(1, &right), + ], + }; + let asbd = build_input_asbd(48_000.0, 2, true); + let mut out = [0.0_f32; 6]; + let frames = + convert_buffer_list_to_interleaved_f32(asbd, &list, 3, 48_000.0, 2, &mut out).unwrap(); + assert_eq!(3, frames); + assert_eq!([0.1, -0.1, 0.2, -0.2, 0.3, -0.3], out); + } + + #[test] + fn input_frame_count_for_buffer_list_handles_interleaved_and_planar_input() { + let interleaved = [0.1_f32, -0.1, 0.2, -0.2, 0.3, -0.3]; + let interleaved_list = AudioBufferListN { + m_number_buffers: 1, + buffers: [AudioBuffer::from_slice(2, &interleaved)], + }; + assert_eq!( + 3, + input_frame_count_for_buffer_list( + build_input_asbd(48_000.0, 2, false), + &interleaved_list + ) + .unwrap() + ); + let left = [0.1_f32, 0.2, 0.3, 0.4]; + let right = [-0.1_f32, -0.2, -0.3, -0.4]; + let planar_list = AudioBufferListN { + m_number_buffers: 2, + buffers: [ + AudioBuffer::from_slice(1, &left), + AudioBuffer::from_slice(1, &right), + ], + }; + assert_eq!( + 4, + input_frame_count_for_buffer_list(build_input_asbd(48_000.0, 2, true), &planar_list) + .unwrap() + ); + } + + #[test] + fn convert_buffer_list_to_interleaved_f32_normalizes_signed_int16() { + let samples = [0_i16, 16_384, -32_768, 32_767]; + let list = AudioBufferListN { + m_number_buffers: 1, + buffers: [AudioBuffer::from_slice(2, &samples)], + }; + let mut asbd = build_input_asbd(48_000.0, 2, false); + asbd.m_format_flags = + K_LINEAR_PCM_FORMAT_FLAG_IS_SIGNED_INTEGER | K_LINEAR_PCM_FORMAT_FLAG_IS_PACKED; + asbd.m_bytes_per_packet = 4; + asbd.m_bytes_per_frame = 4; + asbd.m_bits_per_channel = 16; + let mut out = [0.0_f32; 4]; + let frames = + convert_buffer_list_to_interleaved_f32(asbd, &list, 2, 48_000.0, 2, &mut out).unwrap(); + assert_eq!(2, frames); + assert!((out[0] - 0.0).abs() < 0.00001); + assert!((out[1] - 0.5).abs() < 0.00001); + assert!((out[2] - -1.0).abs() < 0.00001); + assert!((out[3] - 0.9999695).abs() < 0.00001); + } + + #[test] + fn convert_buffer_list_to_interleaved_f32_handles_float64_input() { + let samples = [-0.25_f64, 0.75]; + let list = AudioBufferListN { + m_number_buffers: 1, + buffers: [AudioBuffer::from_slice(1, &samples)], + }; + let mut asbd = build_input_asbd(48_000.0, 1, false); + asbd.m_bytes_per_packet = 8; + asbd.m_bytes_per_frame = 8; + asbd.m_bits_per_channel = 64; + let mut out = [0.0_f32; 4]; + let frames = + convert_buffer_list_to_interleaved_f32(asbd, &list, 2, 48_000.0, 2, &mut out).unwrap(); + assert_eq!(2, frames); + assert_eq!([-0.25, -0.25, 0.75, 0.75], out); + } + + #[test] + fn convert_buffer_list_to_interleaved_f32_handles_padded_float32_fallback() { + let mut bytes = [0_u8; 16]; + bytes[0..4].copy_from_slice(&0.5_f32.to_ne_bytes()); + bytes[8..12].copy_from_slice(&(-0.25_f32).to_ne_bytes()); + let list = AudioBufferListN { + m_number_buffers: 1, + buffers: [AudioBuffer::from_slice(1, &bytes)], + }; + let mut asbd = build_input_asbd(48_000.0, 1, false); + asbd.m_format_flags &= !K_LINEAR_PCM_FORMAT_FLAG_IS_PACKED; + asbd.m_bytes_per_packet = 8; + asbd.m_bytes_per_frame = 8; + let mut out = [0.0_f32; 4]; + let frames = + convert_buffer_list_to_interleaved_f32(asbd, &list, 2, 48_000.0, 2, &mut out).unwrap(); + assert_eq!(2, frames); + assert_eq!([0.5, 0.5, -0.25, -0.25], out); + } + + #[test] + fn convert_buffer_list_to_interleaved_f32_linearly_resamples_to_target_rate() { + let samples = [0.0_f32, 1.0]; + let list = AudioBufferListN { + m_number_buffers: 1, + buffers: [AudioBuffer::from_slice(1, &samples)], + }; + let asbd = build_input_asbd(24_000.0, 1, false); + let mut out = [0.0_f32; 8]; + let frames = + convert_buffer_list_to_interleaved_f32(asbd, &list, 2, 48_000.0, 2, &mut out).unwrap(); + assert_eq!(4, frames); + assert_eq!([0.0, 0.0, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0], out); + } +} diff --git a/fluxer_desktop/native/rust/src/mac_app_audio/audio_source_contract.rs b/fluxer_desktop/native/rust/src/mac_app_audio/audio_source_contract.rs new file mode 100644 index 0000000..09fd6bb --- /dev/null +++ b/fluxer_desktop/native/rust/src/mac_app_audio/audio_source_contract.rs @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub const ENC_DID_OUTPUT_SAMPLE: &str = "v40@0:8@16^{opaqueCMSampleBuffer=}24q32"; +pub const ENC_DID_STOP_WITH_ERROR: &str = "v32@0:8@16@24"; + +pub const DEFAULT_TARGET_SAMPLE_RATE: f64 = 48_000.0; +pub const DEFAULT_TARGET_CHANNELS: u32 = 2; +pub const MAX_CALLBACK_INPUT_FRAMES: u32 = 48_000; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct SourceOptions { + pub target_sample_rate: f64, + pub target_channels: u32, +} + +impl Default for SourceOptions { + fn default() -> Self { + Self { + target_sample_rate: DEFAULT_TARGET_SAMPLE_RATE, + target_channels: DEFAULT_TARGET_CHANNELS, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn encoding_strings_have_expected_lengths() { + assert_eq!( + "v40@0:8@16^{opaqueCMSampleBuffer=}24q32", + ENC_DID_OUTPUT_SAMPLE + ); + assert_eq!("v32@0:8@16@24", ENC_DID_STOP_WITH_ERROR); + } + + #[test] + fn source_options_default_to_the_public_audio_contract() { + let options = SourceOptions::default(); + assert_eq!(48_000.0, options.target_sample_rate); + assert_eq!(2, options.target_channels); + assert_eq!(48_000, MAX_CALLBACK_INPUT_FRAMES); + } +} diff --git a/fluxer_desktop/native/rust/src/mac_app_audio/blocks.rs b/fluxer_desktop/native/rust/src/mac_app_audio/blocks.rs new file mode 100644 index 0000000..6190747 --- /dev/null +++ b/fluxer_desktop/native/rust/src/mac_app_audio/blocks.rs @@ -0,0 +1,186 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::ffi::{c_char, c_void}; + +pub const BLOCK_HAS_COPY_DISPOSE: i32 = 1 << 25; +pub const BLOCK_HAS_SIGNATURE: i32 = 1 << 30; + +pub const NS_ERROR_BLOCK_SIGNATURE: &[u8] = b"v16@?0@8\0"; +pub const CONTENT_ERROR_BLOCK_SIGNATURE: &[u8] = b"v24@?0@8@16\0"; + +#[repr(C)] +#[derive(Debug, Clone, Copy)] +pub struct BlockDescriptorSig { + pub reserved: u64, + pub size: u64, + pub signature: *const c_char, +} + +pub type NsErrorUserInvoke = unsafe extern "C" fn(ctx: *mut c_void, err: *mut c_void); +pub type NsErrorInvoke = unsafe extern "C" fn(block: *mut NSErrorBlock, err: *mut c_void); + +#[repr(C)] +pub struct NSErrorBlock { + pub isa: *mut c_void, + pub flags: i32, + pub reserved: i32, + pub invoke: NsErrorInvoke, + pub descriptor: *const BlockDescriptorSig, + pub ctx: *mut c_void, + pub user_invoke: NsErrorUserInvoke, +} + +pub unsafe extern "C" fn ns_error_trampoline(block: *mut NSErrorBlock, err: *mut c_void) { + if let Some(block) = unsafe { block.as_mut() } { + unsafe { (block.user_invoke)(block.ctx, err) }; + } +} + +pub type ContentErrorUserInvoke = + unsafe extern "C" fn(ctx: *mut c_void, content: *mut c_void, err: *mut c_void); +pub type ContentErrorInvoke = + unsafe extern "C" fn(block: *mut ContentErrorBlock, content: *mut c_void, err: *mut c_void); + +#[repr(C)] +pub struct ContentErrorBlock { + pub isa: *mut c_void, + pub flags: i32, + pub reserved: i32, + pub invoke: ContentErrorInvoke, + pub descriptor: *const BlockDescriptorSig, + pub ctx: *mut c_void, + pub user_invoke: ContentErrorUserInvoke, +} + +pub unsafe extern "C" fn content_error_trampoline( + block: *mut ContentErrorBlock, + content: *mut c_void, + err: *mut c_void, +) { + if let Some(block) = unsafe { block.as_mut() } { + unsafe { (block.user_invoke)(block.ctx, content, err) }; + } +} + +pub const NS_ERROR_BLOCK_DESCRIPTOR: BlockDescriptorSig = BlockDescriptorSig { + reserved: 0, + size: std::mem::size_of::() as u64, + signature: NS_ERROR_BLOCK_SIGNATURE.as_ptr().cast(), +}; + +pub const CONTENT_ERROR_BLOCK_DESCRIPTOR: BlockDescriptorSig = BlockDescriptorSig { + reserved: 0, + size: std::mem::size_of::() as u64, + signature: CONTENT_ERROR_BLOCK_SIGNATURE.as_ptr().cast(), +}; + +#[cfg(test)] +mod tests { + use super::*; + + #[repr(C)] + struct RoundTripCtx { + seen_err: *mut c_void, + hit_count: u32, + } + + unsafe extern "C" fn round_trip_user_invoke(ctx: *mut c_void, err: *mut c_void) { + let ctx = unsafe { &mut *(ctx.cast::()) }; + ctx.seen_err = err; + ctx.hit_count += 1; + } + + #[repr(C)] + struct ContentErrorRoundTripCtx { + seen_content: *mut c_void, + seen_err: *mut c_void, + hit_count: u32, + } + + unsafe extern "C" fn content_error_user_invoke( + ctx: *mut c_void, + content: *mut c_void, + err: *mut c_void, + ) { + let ctx = unsafe { &mut *(ctx.cast::()) }; + ctx.seen_content = content; + ctx.seen_err = err; + ctx.hit_count += 1; + } + + #[test] + fn block_layout_offsets_match_clang_abi_spec() { + assert_eq!(0, std::mem::offset_of!(NSErrorBlock, isa)); + assert_eq!(8, std::mem::offset_of!(NSErrorBlock, flags)); + assert_eq!(12, std::mem::offset_of!(NSErrorBlock, reserved)); + assert_eq!(16, std::mem::offset_of!(NSErrorBlock, invoke)); + assert_eq!(24, std::mem::offset_of!(NSErrorBlock, descriptor)); + assert_eq!(32, std::mem::offset_of!(NSErrorBlock, ctx)); + assert_eq!(40, std::mem::offset_of!(NSErrorBlock, user_invoke)); + } + + #[test] + fn block_descriptor_sig_offsets_match_clang_abi_spec() { + assert_eq!(0, std::mem::offset_of!(BlockDescriptorSig, reserved)); + assert_eq!(8, std::mem::offset_of!(BlockDescriptorSig, size)); + assert_eq!(16, std::mem::offset_of!(BlockDescriptorSig, signature)); + } + + #[test] + fn block_round_trip_via_direct_trampoline_call() { + let mut ctx = RoundTripCtx { + seen_err: std::ptr::null_mut(), + hit_count: 0, + }; + let mut block = NSErrorBlock { + isa: std::ptr::null_mut(), + flags: BLOCK_HAS_SIGNATURE, + reserved: 0, + invoke: ns_error_trampoline, + descriptor: &NS_ERROR_BLOCK_DESCRIPTOR, + ctx: (&mut ctx as *mut RoundTripCtx).cast(), + user_invoke: round_trip_user_invoke, + }; + let sentinel = 0xCAFE_F00D_usize as *mut c_void; + + unsafe { (block.invoke)(&mut block, sentinel) }; + + assert_eq!(1, ctx.hit_count); + assert_eq!(sentinel, ctx.seen_err); + } + + #[test] + fn content_error_block_layout_offsets_match_clang_abi_spec() { + assert_eq!(0, std::mem::offset_of!(ContentErrorBlock, isa)); + assert_eq!(16, std::mem::offset_of!(ContentErrorBlock, invoke)); + assert_eq!(24, std::mem::offset_of!(ContentErrorBlock, descriptor)); + assert_eq!(32, std::mem::offset_of!(ContentErrorBlock, ctx)); + assert_eq!(40, std::mem::offset_of!(ContentErrorBlock, user_invoke)); + } + + #[test] + fn content_error_block_round_trip_via_direct_trampoline_call() { + let mut ctx = ContentErrorRoundTripCtx { + seen_content: std::ptr::null_mut(), + seen_err: std::ptr::null_mut(), + hit_count: 0, + }; + let mut block = ContentErrorBlock { + isa: std::ptr::null_mut(), + flags: BLOCK_HAS_SIGNATURE, + reserved: 0, + invoke: content_error_trampoline, + descriptor: &CONTENT_ERROR_BLOCK_DESCRIPTOR, + ctx: (&mut ctx as *mut ContentErrorRoundTripCtx).cast(), + user_invoke: content_error_user_invoke, + }; + let content_sentinel = 0xDEAD_BEEF_usize as *mut c_void; + let err_sentinel = 0xCAFE_F00D_usize as *mut c_void; + + unsafe { (block.invoke)(&mut block, content_sentinel, err_sentinel) }; + + assert_eq!(1, ctx.hit_count); + assert_eq!(content_sentinel, ctx.seen_content); + assert_eq!(err_sentinel, ctx.seen_err); + } +} diff --git a/fluxer_desktop/native/rust/src/mac_app_audio/coreaudio_tap.rs b/fluxer_desktop/native/rust/src/mac_app_audio/coreaudio_tap.rs new file mode 100644 index 0000000..892f088 --- /dev/null +++ b/fluxer_desktop/native/rust/src/mac_app_audio/coreaudio_tap.rs @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub type OSStatus = i32; +pub type AudioObjectId = u32; + +pub const NO_ERR: OSStatus = 0; +pub const K_AUDIO_OBJECT_UNKNOWN: AudioObjectId = 0; +pub const K_AUDIO_OBJECT_SYSTEM_OBJECT: AudioObjectId = 1; +pub const K_AUDIO_OBJECT_PROPERTY_ELEMENT_MAIN: u32 = 0; +pub const K_AUDIO_OBJECT_PROPERTY_SCOPE_GLOBAL: u32 = fourcc(*b"glob"); +pub const K_AUDIO_HARDWARE_PROPERTY_TRANSLATE_PID_TO_PROCESS_OBJECT: u32 = fourcc(*b"id2p"); +pub const K_AUDIO_HARDWARE_PROPERTY_PROCESS_OBJECT_LIST: u32 = fourcc(*b"prs#"); +pub const K_AUDIO_TAP_PROPERTY_UID: u32 = fourcc(*b"tuid"); +pub const K_AUDIO_TAP_PROPERTY_FORMAT: u32 = fourcc(*b"tfmt"); +pub const K_AUDIO_AGGREGATE_DRIFT_COMPENSATION_MEDIUM_QUALITY: u32 = 0x40; +pub const TARGET_SAMPLE_RATE: f64 = 48_000.0; +pub const TARGET_CHANNELS: u32 = 2; +pub const MAX_CALLBACK_INPUT_FRAMES: u32 = 48_000; +pub const MAX_RELATED_PROCESSES: usize = 512; + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AudioObjectPropertyAddress { + pub selector: u32, + pub scope: u32, + pub element: u32, +} + +pub const fn fourcc(bytes: [u8; 4]) -> u32 { + ((bytes[0] as u32) << 24) + | ((bytes[1] as u32) << 16) + | ((bytes[2] as u32) << 8) + | bytes[3] as u32 +} + +pub fn dedupe_audio_objects( + objects: impl IntoIterator, +) -> Vec { + let mut out = Vec::new(); + for object in objects { + if object == K_AUDIO_OBJECT_UNKNOWN || out.contains(&object) { + continue; + } + out.push(object); + } + out +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn core_audio_fourcc_constants_match_headers() { + assert_eq!(0x676c_6f62, K_AUDIO_OBJECT_PROPERTY_SCOPE_GLOBAL); + assert_eq!( + 0x6964_3270, + K_AUDIO_HARDWARE_PROPERTY_TRANSLATE_PID_TO_PROCESS_OBJECT + ); + assert_eq!(0x7475_6964, K_AUDIO_TAP_PROPERTY_UID); + assert_eq!(0x7466_6d74, K_AUDIO_TAP_PROPERTY_FORMAT); + } + + #[test] + fn core_audio_process_object_collection_keeps_unique_translated_objects() { + assert_eq!(0, K_AUDIO_OBJECT_UNKNOWN); + assert_eq!(1, K_AUDIO_OBJECT_SYSTEM_OBJECT); + let _ = K_AUDIO_HARDWARE_PROPERTY_PROCESS_OBJECT_LIST; + assert_eq!(vec![7, 8, 9], dedupe_audio_objects([0, 7, 8, 7, 0, 9, 8])); + } +} diff --git a/fluxer_desktop/native/rust/src/mac_app_audio/mod.rs b/fluxer_desktop/native/rust/src/mac_app_audio/mod.rs new file mode 100644 index 0000000..e44ff6f --- /dev/null +++ b/fluxer_desktop/native/rust/src/mac_app_audio/mod.rs @@ -0,0 +1,9 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub mod audio_converter; +pub mod audio_source_contract; +pub mod blocks; +pub mod coreaudio_tap; +pub mod process_tree; +pub mod sck_geometry; +pub mod source_state; diff --git a/fluxer_desktop/native/rust/src/mac_app_audio/process_tree.rs b/fluxer_desktop/native/rust/src/mac_app_audio/process_tree.rs new file mode 100644 index 0000000..6d6a183 --- /dev/null +++ b/fluxer_desktop/native/rust/src/mac_app_audio/process_tree.rs @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Info { + pub pid: i32, + pub parent_pid: i32, + pub process_group_id: i32, +} + +pub fn is_same_launch_tree_with_resolver( + candidate_pid: i32, + target_pid: i32, + target_info: Option, + mut resolver: impl FnMut(i32) -> Option, +) -> bool { + if candidate_pid <= 0 || target_pid <= 0 { + return false; + } + if candidate_pid == target_pid { + return true; + } + + let Some(mut current) = resolver(candidate_pid) else { + return false; + }; + if shares_process_group(current, target_pid, target_info) { + return true; + } + + for _ in 0..64 { + let parent = current.parent_pid; + if parent == target_pid { + return true; + } + if parent <= 1 || parent == current.pid { + return false; + } + let Some(next) = resolver(parent) else { + return false; + }; + current = next; + } + + false +} + +pub fn collect_related_pids_with_resolver( + target_pid: i32, + target_info: Option, + candidates: &[i32], + max_count: usize, + mut resolver: impl FnMut(i32) -> Option, +) -> Vec { + if target_pid <= 0 || max_count == 0 { + return Vec::new(); + } + let mut out = Vec::with_capacity(candidates.len().max(1).min(max_count)); + append_pid(&mut out, max_count, target_pid); + + for &pid in candidates { + if out.len() >= max_count { + break; + } + if pid <= 0 || pid == target_pid { + continue; + } + if !is_same_launch_tree_with_resolver(pid, target_pid, target_info, &mut resolver) { + continue; + } + append_pid(&mut out, max_count, pid); + } + + out +} + +fn shares_process_group(candidate_info: Info, target_pid: i32, target_info: Option) -> bool { + let candidate_group = candidate_info.process_group_id; + if candidate_group <= 0 { + return false; + } + if candidate_group == target_pid { + return true; + } + target_info.is_some_and(|target| { + target.process_group_id > 0 && candidate_group == target.process_group_id + }) +} + +fn append_pid(pids: &mut Vec, max_count: usize, pid: i32) -> bool { + if pids.contains(&pid) { + return true; + } + if pids.len() >= max_count { + return false; + } + pids.push(pid); + true +} + +#[cfg(test)] +mod tests { + use super::*; + + fn resolve_from<'a>(infos: &'a [Info]) -> impl FnMut(i32) -> Option + 'a { + move |pid| infos.iter().copied().find(|info| info.pid == pid) + } + + #[test] + fn same_launch_tree_includes_direct_child() { + let infos = [ + Info { + pid: 100, + parent_pid: 1, + process_group_id: 100, + }, + Info { + pid: 101, + parent_pid: 100, + process_group_id: 100, + }, + ]; + + assert!(is_same_launch_tree_with_resolver( + 101, + 100, + Some(infos[0]), + resolve_from(&infos), + )); + } + + #[test] + fn same_launch_tree_includes_process_group_peer() { + let infos = [ + Info { + pid: 200, + parent_pid: 1, + process_group_id: 200, + }, + Info { + pid: 201, + parent_pid: 1, + process_group_id: 200, + }, + ]; + + assert!(is_same_launch_tree_with_resolver( + 201, + 200, + Some(infos[0]), + resolve_from(&infos), + )); + } + + #[test] + fn same_launch_tree_excludes_unrelated_process() { + let infos = [ + Info { + pid: 300, + parent_pid: 1, + process_group_id: 300, + }, + Info { + pid: 301, + parent_pid: 1, + process_group_id: 301, + }, + ]; + + assert!(!is_same_launch_tree_with_resolver( + 301, + 300, + Some(infos[0]), + resolve_from(&infos), + )); + } + + #[test] + fn collect_related_pids_with_resolver_returns_selected_app_process_tree() { + let infos = [ + Info { + pid: 400, + parent_pid: 1, + process_group_id: 400, + }, + Info { + pid: 401, + parent_pid: 400, + process_group_id: 400, + }, + Info { + pid: 402, + parent_pid: 401, + process_group_id: 400, + }, + Info { + pid: 500, + parent_pid: 1, + process_group_id: 500, + }, + ]; + let candidates = [500, 401, 402, 400]; + + assert_eq!( + vec![400, 401, 402], + collect_related_pids_with_resolver( + 400, + Some(infos[0]), + &candidates, + 8, + resolve_from(&infos), + ) + ); + } + + #[test] + fn cyclic_parent_chain_is_bounded() { + let infos = [ + Info { + pid: 600, + parent_pid: 1, + process_group_id: 600, + }, + Info { + pid: 601, + parent_pid: 602, + process_group_id: 601, + }, + Info { + pid: 602, + parent_pid: 601, + process_group_id: 602, + }, + ]; + + assert!(!is_same_launch_tree_with_resolver( + 601, + 600, + Some(infos[0]), + resolve_from(&infos), + )); + } +} diff --git a/fluxer_desktop/native/rust/src/mac_app_audio/sck_geometry.rs b/fluxer_desktop/native/rust/src/mac_app_audio/sck_geometry.rs new file mode 100644 index 0000000..3f177a5 --- /dev/null +++ b/fluxer_desktop/native/rust/src/mac_app_audio/sck_geometry.rs @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct CGPoint { + pub x: f64, + pub y: f64, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct CGSize { + pub width: f64, + pub height: f64, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct CGRect { + pub origin: CGPoint, + pub size: CGSize, +} + +impl CGRect { + pub fn standardized(mut self) -> Self { + if self.size.width < 0.0 { + self.origin.x += self.size.width; + self.size.width = -self.size.width; + } + if self.size.height < 0.0 { + self.origin.y += self.size.height; + self.size.height = -self.size.height; + } + self + } + + pub fn intersection_area(a_raw: Self, b_raw: Self) -> f64 { + let a = a_raw.standardized(); + let b = b_raw.standardized(); + if a.size.width <= 0.0 + || a.size.height <= 0.0 + || b.size.width <= 0.0 + || b.size.height <= 0.0 + { + return 0.0; + } + let ax2 = a.origin.x + a.size.width; + let ay2 = a.origin.y + a.size.height; + let bx2 = b.origin.x + b.size.width; + let by2 = b.origin.y + b.size.height; + let x1 = a.origin.x.max(b.origin.x); + let y1 = a.origin.y.max(b.origin.y); + let x2 = ax2.min(bx2); + let y2 = ay2.min(by2); + if x2 <= x1 || y2 <= y1 { + return 0.0; + } + (x2 - x1) * (y2 - y1) + } +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct CMTime { + pub value: i64, + pub timescale: i32, + pub flags: u32, + pub epoch: i64, +} + +impl CMTime { + pub fn seconds(value: i64, timescale: i32) -> Self { + Self { + value, + timescale, + flags: 1, + epoch: 0, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn cgrect_intersection_handles_negative_and_disjoint_rectangles() { + let a = CGRect { + origin: CGPoint { x: 0.0, y: 0.0 }, + size: CGSize { + width: 10.0, + height: 10.0, + }, + }; + let b = CGRect { + origin: CGPoint { x: 5.0, y: 5.0 }, + size: CGSize { + width: 10.0, + height: 10.0, + }, + }; + assert_eq!(25.0, CGRect::intersection_area(a, b)); + + let c = CGRect { + origin: CGPoint { x: 10.0, y: 10.0 }, + size: CGSize { + width: -5.0, + height: -5.0, + }, + }; + assert_eq!(25.0, CGRect::intersection_area(a, c)); + + let d = CGRect { + origin: CGPoint { x: 20.0, y: 20.0 }, + size: CGSize { + width: 2.0, + height: 2.0, + }, + }; + assert_eq!(0.0, CGRect::intersection_area(a, d)); + } + + #[test] + fn core_graphics_struct_layouts_match_64_bit_darwin_abi() { + assert_eq!(16, std::mem::size_of::()); + assert_eq!(16, std::mem::size_of::()); + assert_eq!(32, std::mem::size_of::()); + assert_eq!(24, std::mem::size_of::()); + assert_eq!(16, std::mem::offset_of!(CMTime, epoch)); + } +} diff --git a/fluxer_desktop/native/rust/src/mac_app_audio/source_state.rs b/fluxer_desktop/native/rust/src/mac_app_audio/source_state.rs new file mode 100644 index 0000000..049f2b8 --- /dev/null +++ b/fluxer_desktop/native/rust/src/mac_app_audio/source_state.rs @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::sync::atomic::{AtomicU32, Ordering}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +#[repr(u32)] +pub enum State { + Idle = 0, + Starting = 1, + Running = 2, + Stopping = 3, + Stopped = 4, +} + +impl State { + fn from_raw(raw: u32) -> Self { + match raw { + 0 => Self::Idle, + 1 => Self::Starting, + 2 => Self::Running, + 3 => Self::Stopping, + 4 => Self::Stopped, + _ => Self::Stopped, + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum TransitionError { + IllegalTransition, + DoubleStart, + StopBeforeStart, + StartWhileStopping, +} + +pub fn is_allowed(from: State, to: State) -> bool { + match from { + State::Idle => matches!(to, State::Starting | State::Stopped), + State::Starting => matches!(to, State::Running | State::Stopped), + State::Running => matches!(to, State::Stopping | State::Stopped), + State::Stopping => to == State::Stopped, + State::Stopped => false, + } +} + +#[derive(Debug)] +pub struct Machine { + state: AtomicU32, +} + +impl Default for Machine { + fn default() -> Self { + Self::new() + } +} + +impl Machine { + pub fn new() -> Self { + Self { + state: AtomicU32::new(State::Idle as u32), + } + } + + pub fn current(&self) -> State { + State::from_raw(self.state.load(Ordering::Acquire)) + } + + fn cas(&self, from: State, to: State) -> Result<(), TransitionError> { + if !is_allowed(from, to) { + return Err(TransitionError::IllegalTransition); + } + self.state + .compare_exchange(from as u32, to as u32, Ordering::AcqRel, Ordering::Acquire) + .map(|_| ()) + .map_err(|_| TransitionError::IllegalTransition) + } + + pub fn request_start(&self) -> Result<(), TransitionError> { + match self.current() { + State::Idle => self.cas(State::Idle, State::Starting), + State::Starting | State::Running => Err(TransitionError::DoubleStart), + State::Stopping => Err(TransitionError::StartWhileStopping), + State::Stopped => Err(TransitionError::IllegalTransition), + } + } + + pub fn mark_running(&self) -> Result<(), TransitionError> { + self.cas(State::Starting, State::Running) + } + + pub fn request_stop(&self) -> Result<(), TransitionError> { + match self.current() { + State::Running => self.cas(State::Running, State::Stopping), + State::Idle => Err(TransitionError::StopBeforeStart), + State::Starting | State::Stopping | State::Stopped => { + Err(TransitionError::IllegalTransition) + } + } + } + + pub fn mark_stopped(&self) -> Result<(), TransitionError> { + self.cas(State::Stopping, State::Stopped) + } + + pub fn cancel_idle(&self) -> Result<(), TransitionError> { + self.cas(State::Idle, State::Stopped) + } + + pub fn mark_fatal(&self) -> State { + loop { + let raw = self.state.load(Ordering::Acquire); + let prev = State::from_raw(raw); + if prev == State::Stopped { + return prev; + } + if self + .state + .compare_exchange( + raw, + State::Stopped as u32, + Ordering::AcqRel, + Ordering::Acquire, + ) + .is_ok() + { + return prev; + } + } + } + + #[cfg(test)] + fn force_state(&self, state: State) { + self.state.store(state as u32, Ordering::Release); + } +} + +#[cfg(test)] +mod tests { + use std::sync::Arc; + use std::sync::atomic::{AtomicU32, Ordering}; + use std::thread; + + use super::*; + + #[test] + fn is_allowed_exhaustive_transition_table() { + let all = [ + State::Idle, + State::Starting, + State::Running, + State::Stopping, + State::Stopped, + ]; + let allowed = [ + (State::Idle, State::Starting), + (State::Idle, State::Stopped), + (State::Starting, State::Running), + (State::Starting, State::Stopped), + (State::Running, State::Stopping), + (State::Running, State::Stopped), + (State::Stopping, State::Stopped), + ]; + for from in all { + for to in all { + assert_eq!(allowed.contains(&(from, to)), is_allowed(from, to)); + } + } + } + + #[test] + fn happy_path_idle_starting_running_stopping_stopped() { + let m = Machine::new(); + assert_eq!(State::Idle, m.current()); + m.request_start().unwrap(); + assert_eq!(State::Starting, m.current()); + m.mark_running().unwrap(); + assert_eq!(State::Running, m.current()); + m.request_stop().unwrap(); + assert_eq!(State::Stopping, m.current()); + m.mark_stopped().unwrap(); + assert_eq!(State::Stopped, m.current()); + } + + #[test] + fn double_start_rejected() { + let m = Machine::new(); + m.request_start().unwrap(); + assert_eq!(Err(TransitionError::DoubleStart), m.request_start()); + m.mark_running().unwrap(); + assert_eq!(Err(TransitionError::DoubleStart), m.request_start()); + } + + #[test] + fn stop_before_start_rejected() { + let m = Machine::new(); + assert_eq!(Err(TransitionError::StopBeforeStart), m.request_stop()); + } + + #[test] + fn start_while_stopping_rejected() { + let m = Machine::new(); + m.request_start().unwrap(); + m.mark_running().unwrap(); + m.request_stop().unwrap(); + assert_eq!(Err(TransitionError::StartWhileStopping), m.request_start()); + } + + #[test] + fn cancel_idle_short_circuits_idle_to_stopped() { + let m = Machine::new(); + m.cancel_idle().unwrap(); + assert_eq!(State::Stopped, m.current()); + assert_eq!(Err(TransitionError::IllegalTransition), m.request_start()); + } + + #[test] + fn mark_fatal_forces_stopped_from_any_state() { + for start in [ + State::Idle, + State::Starting, + State::Running, + State::Stopping, + ] { + let m = Machine::new(); + m.force_state(start); + assert_eq!(start, m.mark_fatal()); + assert_eq!(State::Stopped, m.current()); + } + } + + #[test] + fn mark_fatal_idempotent() { + let m = Machine::new(); + m.force_state(State::Stopped); + assert_eq!(State::Stopped, m.mark_fatal()); + assert_eq!(State::Stopped, m.current()); + } + + #[test] + fn concurrent_mark_running_vs_request_stop_reaches_consistent_terminal() { + for _ in 0..200 { + let machine = Arc::new(Machine::new()); + machine.request_start().unwrap(); + let run_wins = Arc::new(AtomicU32::new(0)); + let stop_wins = Arc::new(AtomicU32::new(0)); + + let runner_machine = Arc::clone(&machine); + let runner_wins = Arc::clone(&run_wins); + let runner = thread::spawn(move || { + if runner_machine.mark_running().is_ok() { + runner_wins.fetch_add(1, Ordering::Relaxed); + } + }); + + let stopper_machine = Arc::clone(&machine); + let stopper_wins = Arc::clone(&stop_wins); + let stopper = thread::spawn(move || { + for _ in 0..1000 { + if stopper_machine.request_stop().is_ok() { + stopper_wins.fetch_add(1, Ordering::Relaxed); + return; + } + std::hint::spin_loop(); + } + }); + + runner.join().unwrap(); + stopper.join().unwrap(); + assert_eq!(1, run_wins.load(Ordering::Relaxed)); + let stop_wins = stop_wins.load(Ordering::Relaxed); + assert!(stop_wins <= 1); + assert_eq!( + if stop_wins == 1 { + State::Stopping + } else { + State::Running + }, + machine.current() + ); + if stop_wins == 0 { + machine.request_stop().unwrap(); + } + assert_eq!(State::Stopping, machine.current()); + } + } +} diff --git a/fluxer_desktop/native/rust/src/mac_sysctl/mod.rs b/fluxer_desktop/native/rust/src/mac_sysctl/mod.rs new file mode 100644 index 0000000..8d1c0af --- /dev/null +++ b/fluxer_desktop/native/rust/src/mac_sysctl/mod.rs @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SysctlFailure { + pub errno: i32, +} + +pub const ENOENT: i32 = 2; + +pub fn errno_message(errno: i32) -> String { + format!("sysctlbyname failed (errno {errno})") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn errno_message_formats_errno_into_expected_wording() { + assert_eq!("sysctlbyname failed (errno 22)", errno_message(22)); + assert_eq!("sysctlbyname failed (errno 2)", errno_message(ENOENT)); + } +} diff --git a/fluxer_desktop/native/rust/src/platform_info/mod.rs b/fluxer_desktop/native/rust/src/platform_info/mod.rs new file mode 100644 index 0000000..23686cd --- /dev/null +++ b/fluxer_desktop/native/rust/src/platform_info/mod.rs @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub mod probe_helpers; diff --git a/fluxer_desktop/native/rust/src/platform_info/probe_helpers.rs b/fluxer_desktop/native/rust/src/platform_info/probe_helpers.rs new file mode 100644 index 0000000..5669c5e --- /dev/null +++ b/fluxer_desktop/native/rust/src/platform_info/probe_helpers.rs @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub fn vendor_name(vendor_id: u32) -> Option<&'static str> { + match vendor_id { + 0x1002 | 0x1022 => Some("AMD"), + 0x106b => Some("Apple"), + 0x10de => Some("NVIDIA"), + 0x1234 => Some("QEMU"), + 0x1414 => Some("Microsoft"), + 0x15ad => Some("VMware"), + 0x1af4 => Some("Virtio"), + 0x1b36 => Some("QEMU"), + 0x5143 => Some("Qualcomm"), + 0x8086 => Some("Intel"), + _ => None, + } +} + +pub fn vendor_id_from_name(name: &str) -> u32 { + let lower = name.to_ascii_lowercase(); + if lower.contains("apple") { + return 0x106b; + } + if lower.contains("amd") || lower.contains("radeon") { + return 0x1002; + } + if lower.contains("intel") { + return 0x8086; + } + if lower.contains("nvidia") { + return 0x10de; + } + if lower.contains("microsoft") { + return 0x1414; + } + 0 +} + +pub fn write_hex_u64(value: u64) -> String { + format!("0x{value:x}") +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ParseHexIdError { + InvalidHexId, + InvalidDigit, +} + +pub fn parse_hex_id(raw: &str) -> Result { + let mut trimmed = raw.trim(); + if let Some(rest) = trimmed + .strip_prefix("0x") + .or_else(|| trimmed.strip_prefix("0X")) + { + trimmed = rest; + } + if trimmed.is_empty() || trimmed.len() > 8 { + return Err(ParseHexIdError::InvalidHexId); + } + u32::from_str_radix(trimmed, 16).map_err(|_| ParseHexIdError::InvalidDigit) +} + +pub fn is_drm_card_name(name: &str) -> bool { + let Some(rest) = name.strip_prefix("card") else { + return false; + }; + !rest.is_empty() && rest.bytes().all(|ch| ch.is_ascii_digit()) +} + +pub fn basename(path: &str) -> &str { + path.rsplit_once('/').map_or(path, |(_, base)| base) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn parse_hex_id_accepts_sysfs_hex_ids() { + assert_eq!(0x8086, parse_hex_id("0x8086\n").unwrap()); + assert_eq!(0x10de, parse_hex_id("10DE").unwrap()); + assert_eq!(Err(ParseHexIdError::InvalidHexId), parse_hex_id("0x")); + } + + #[test] + fn is_drm_card_name_accepts_cards_but_rejects_connectors_render_nodes() { + assert!(is_drm_card_name("card0")); + assert!(is_drm_card_name("card12")); + assert!(!is_drm_card_name("card0-DP-1")); + assert!(!is_drm_card_name("renderD128")); + } + + #[test] + fn vendor_id_helpers() { + assert_eq!(0x106b, vendor_id_from_name("Apple M3 GPU")); + assert_eq!(Some("NVIDIA"), vendor_name(0x10de)); + assert_eq!(None, vendor_name(0xffff)); + } +} diff --git a/fluxer_desktop/native/rust/src/system_hunspell/dictionaries.rs b/fluxer_desktop/native/rust/src/system_hunspell/dictionaries.rs new file mode 100644 index 0000000..a769797 --- /dev/null +++ b/fluxer_desktop/native/rust/src/system_hunspell/dictionaries.rs @@ -0,0 +1,166 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::collections::HashSet; +use std::fs; +use std::path::{Path, PathBuf}; + +#[derive(Debug, Clone, Default, PartialEq, Eq)] +pub struct EnvSnapshot { + pub hunspell_dict_dir: Option, + pub xdg_data_home: Option, + pub home: Option, + pub xdg_data_dirs: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct SystemDictionary { + pub tag: String, + pub aff_path: PathBuf, + pub dic_path: PathBuf, +} + +pub fn canonicalize_tag(raw: &str) -> String { + raw.chars() + .map(|ch| match ch { + '_' | '-' => '-', + _ => ch.to_ascii_lowercase(), + }) + .collect() +} + +pub fn build_search_path(env: &EnvSnapshot) -> Vec { + let mut dirs = Vec::new(); + if let Some(raw) = &env.hunspell_dict_dir { + dirs.extend( + raw.split(':') + .filter(|part| !part.is_empty()) + .map(PathBuf::from), + ); + } + if let Some(xdg_home) = &env.xdg_data_home { + dirs.push(Path::new(xdg_home).join("hunspell")); + } else if let Some(home) = &env.home { + dirs.push(Path::new(home).join(".local/share/hunspell")); + } + if let Some(xdg_dirs) = &env.xdg_data_dirs { + dirs.extend( + xdg_dirs + .split(':') + .filter(|part| !part.is_empty()) + .map(|part| Path::new(part).join("hunspell")), + ); + } + dirs.push(PathBuf::from("/usr/local/share/hunspell")); + dirs.push(PathBuf::from("/usr/share/hunspell")); + dirs.push(PathBuf::from("/usr/share/myspell/dicts")); + dirs.push(PathBuf::from("/usr/share/myspell")); + dirs +} + +pub fn discover_dictionaries(env: &EnvSnapshot) -> Vec { + let mut seen = HashSet::new(); + let mut out = Vec::new(); + for dir in build_search_path(env) { + scan_dir(&dir, &mut seen, &mut out); + } + out +} + +fn scan_dir(dir: &Path, seen: &mut HashSet, out: &mut Vec) { + let Ok(entries) = fs::read_dir(dir) else { + return; + }; + for entry in entries.flatten() { + let path = entry.path(); + if path.extension().and_then(|ext| ext.to_str()) != Some("dic") { + continue; + } + let Some(stem) = path.file_stem().and_then(|stem| stem.to_str()) else { + continue; + }; + if stem.is_empty() { + continue; + } + let aff_path = dir.join(format!("{stem}.aff")); + if !aff_path.is_file() { + continue; + } + let tag = canonicalize_tag(stem); + if !seen.insert(tag.clone()) { + continue; + } + out.push(SystemDictionary { + tag, + aff_path, + dic_path: path, + }); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn canonicalize_tag_collapses_case_and_separator_variants() { + assert_eq!("en-us", canonicalize_tag("en_US")); + assert_eq!("en-us", canonicalize_tag("EN-us")); + assert_eq!("pt-br", canonicalize_tag("PT_BR")); + } + + #[test] + fn build_search_path_hunspell_dict_dir_is_honoured_first() { + let dirs = build_search_path(&EnvSnapshot { + hunspell_dict_dir: Some("/tmp/a:/tmp/b".to_owned()), + ..EnvSnapshot::default() + }); + assert_eq!(PathBuf::from("/tmp/a"), dirs[0]); + assert_eq!(PathBuf::from("/tmp/b"), dirs[1]); + assert!( + dirs.iter() + .any(|dir| dir == Path::new("/usr/share/hunspell")) + ); + } + + #[test] + fn build_search_path_xdg_data_home_wins_over_home() { + let dirs = build_search_path(&EnvSnapshot { + xdg_data_home: Some("/x/data".to_owned()), + home: Some("/home/user".to_owned()), + ..EnvSnapshot::default() + }); + assert!(dirs.iter().any(|dir| dir == Path::new("/x/data/hunspell"))); + assert!( + !dirs + .iter() + .any(|dir| dir == Path::new("/home/user/.local/share/hunspell")) + ); + } + + #[test] + fn discover_dictionaries_picks_up_aff_dic_pairs_and_first_tag_wins() { + let dir_a = tempfile::tempdir().unwrap(); + let dir_b = tempfile::tempdir().unwrap(); + std::fs::write(dir_a.path().join("en_US.aff"), "").unwrap(); + std::fs::write(dir_a.path().join("en_US.dic"), "").unwrap(); + std::fs::write(dir_b.path().join("EN-us.aff"), "").unwrap(); + std::fs::write(dir_b.path().join("EN-us.dic"), "").unwrap(); + std::fs::write(dir_b.path().join("orphan.dic"), "").unwrap(); + + let dicts = discover_dictionaries(&EnvSnapshot { + hunspell_dict_dir: Some(format!( + "{}:{}", + dir_a.path().display(), + dir_b.path().display() + )), + ..EnvSnapshot::default() + }); + assert_eq!(1, dicts.iter().filter(|dict| dict.tag == "en-us").count()); + assert!(!dicts.iter().any(|dict| dict.tag == "orphan")); + assert!( + dicts + .iter() + .any(|dict| dict.aff_path == dir_a.path().join("en_US.aff")) + ); + } +} diff --git a/fluxer_desktop/native/rust/src/system_hunspell/encoding.rs b/fluxer_desktop/native/rust/src/system_hunspell/encoding.rs new file mode 100644 index 0000000..fb62446 --- /dev/null +++ b/fluxer_desktop/native/rust/src/system_hunspell/encoding.rs @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub fn is_utf8_encoding(encoding: &str) -> bool { + let bytes = encoding.as_bytes(); + if bytes.is_empty() || bytes.len() > 16 { + return false; + } + bytes.eq_ignore_ascii_case(b"utf-8") || bytes.eq_ignore_ascii_case(b"utf8") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn is_utf8_encoding_matches_common_spellings() { + assert!(is_utf8_encoding("UTF-8")); + assert!(is_utf8_encoding("utf-8")); + assert!(is_utf8_encoding("UTF8")); + assert!(!is_utf8_encoding("ISO-8859-1")); + assert!(!is_utf8_encoding("")); + } + + #[test] + fn is_utf8_encoding_rejects_long_or_decorated_values() { + assert!(!is_utf8_encoding("utf-8\0")); + assert!(!is_utf8_encoding(" utf-8")); + assert!(!is_utf8_encoding("utf-8-with-extra")); + } +} diff --git a/fluxer_desktop/native/rust/src/system_hunspell/hashing.rs b/fluxer_desktop/native/rust/src/system_hunspell/hashing.rs new file mode 100644 index 0000000..2777836 --- /dev/null +++ b/fluxer_desktop/native/rust/src/system_hunspell/hashing.rs @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::fs::File; +use std::io::{self, Read}; +use std::path::Path; + +use sha2::{Digest, Sha256}; + +pub const HEX_LEN: usize = 64; +const CHUNK_SIZE: usize = 64 * 1024; + +pub fn bytes_to_hex_lower(bytes: &[u8], out: &mut [u8]) { + assert!(out.len() >= bytes.len() * 2); + const ALPHABET: &[u8; 16] = b"0123456789abcdef"; + for (index, byte) in bytes.iter().copied().enumerate() { + out[index * 2] = ALPHABET[(byte >> 4) as usize]; + out[index * 2 + 1] = ALPHABET[(byte & 0x0f) as usize]; + } +} + +pub fn hash_file_to_hex(path: impl AsRef) -> io::Result { + let mut file = File::open(path)?; + let mut hasher = Sha256::new(); + let mut buf = [0_u8; CHUNK_SIZE]; + loop { + let read = file.read(&mut buf)?; + if read == 0 { + break; + } + hasher.update(&buf[..read]); + } + let digest = hasher.finalize(); + let mut out = [0_u8; HEX_LEN]; + bytes_to_hex_lower(&digest, &mut out); + let mut hex = String::with_capacity(HEX_LEN); + for byte in out { + hex.push(char::from(byte)); + } + Ok(hex) +} + +#[cfg(test)] +mod tests { + use std::io::Write; + + use super::*; + + #[test] + fn bytes_to_hex_lower_formats_lowercase_hex() { + let mut out = [0_u8; 8]; + bytes_to_hex_lower(&[0xde, 0xad, 0xbe, 0xef], &mut out); + assert_eq!("deadbeef", std::str::from_utf8(&out).unwrap()); + } + + #[test] + fn bytes_to_hex_lower_formats_zero_and_edge_bytes() { + let mut out = [0_u8; 10]; + bytes_to_hex_lower(&[0x00, 0x0f, 0xf0, 0xff, 0x10], &mut out); + assert_eq!("000ff0ff10", std::str::from_utf8(&out).unwrap()); + } + + #[test] + fn hash_file_to_hex_matches_known_sha256_for_abc() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("abc"); + std::fs::write(&path, b"abc").unwrap(); + assert_eq!( + "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", + hash_file_to_hex(&path).unwrap() + ); + } + + #[test] + fn hash_file_to_hex_over_chunk_boundary_matches_single_shot_sha256() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("chunked"); + let mut file = File::create(&path).unwrap(); + let mut payload = vec![0_u8; 200 * 1024]; + let mut value = 0xc0ffee_u64; + for byte in &mut payload { + value ^= value << 13; + value ^= value >> 7; + value ^= value << 17; + *byte = value as u8; + } + file.write_all(&payload).unwrap(); + + let mut oneshot = Sha256::new(); + oneshot.update(&payload); + let digest = oneshot.finalize(); + let mut expected = [0_u8; HEX_LEN]; + bytes_to_hex_lower(&digest, &mut expected); + assert_eq!( + std::str::from_utf8(&expected).unwrap(), + hash_file_to_hex(&path).unwrap() + ); + } + + #[test] + fn hash_file_to_hex_returns_open_error_for_missing_file() { + assert!(hash_file_to_hex("/nonexistent/path/that/should/not/exist.bin").is_err()); + } +} diff --git a/fluxer_desktop/native/rust/src/system_hunspell/mod.rs b/fluxer_desktop/native/rust/src/system_hunspell/mod.rs new file mode 100644 index 0000000..432fce5 --- /dev/null +++ b/fluxer_desktop/native/rust/src/system_hunspell/mod.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub mod dictionaries; +pub mod encoding; +pub mod hashing; diff --git a/fluxer_desktop/native/rust/src/voice/mod.rs b/fluxer_desktop/native/rust/src/voice/mod.rs new file mode 100644 index 0000000..9bf4db7 --- /dev/null +++ b/fluxer_desktop/native/rust/src/voice/mod.rs @@ -0,0 +1,3 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub mod stats; diff --git a/fluxer_desktop/native/rust/src/voice/stats.rs b/fluxer_desktop/native/rust/src/voice/stats.rs new file mode 100644 index 0000000..c02ceb2 --- /dev/null +++ b/fluxer_desktop/native/rust/src/voice/stats.rs @@ -0,0 +1,554 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] +pub struct ByteRateSample { + pub bytes: u64, + pub timestamp_us: i64, +} + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct OutboundStatsEntry { + pub track_sid: String, + pub source: String, + pub kind: String, + pub codec: Option, + pub bitrate_kbps: f64, + pub packets_lost: i64, + pub packets_sent: u64, + pub fps: Option, + pub audio_level: Option, + pub width: Option, + pub height: Option, + pub source_width: Option, + pub source_height: Option, + pub target_bitrate_kbps: Option, + pub configured_fps: Option, + pub target_fps: Option, + pub effective_fps: Option, + pub frames_produced: Option, + pub frames_accepted: Option, + pub frames_dropped: Option, + pub frames_coalesced: Option, + pub frames_captured: Option, + pub capture_failures: Option, + pub max_queue_age_ms: Option, + pub max_push_latency_ms: Option, + pub adaptive_send_tier: Option, + pub adaptive_send_reason: Option, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct InboundStatsEntry { + pub participant_sid: String, + pub participant_identity: Option, + pub track_sid: String, + pub source: Option, + pub kind: String, + pub codec: Option, + pub bitrate_kbps: f64, + pub packets_lost: i64, + pub packets_received: u64, + pub jitter_ms: Option, + pub audio_level: Option, + pub fps: Option, + pub width: Option, + pub height: Option, + pub source_width: Option, + pub source_height: Option, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct SendHealthStats { + pub outgoing_video_queue_depth: u64, + pub outgoing_video_queue_capacity: u64, + pub outgoing_video_max_queue_depth: u64, + pub outgoing_video_frames_produced: u64, + pub outgoing_video_frames_accepted: u64, + pub outgoing_video_frames_dropped: u64, + pub outgoing_video_frames_coalesced: u64, + pub outgoing_video_frames_captured: u64, + pub outgoing_video_capture_failures: u64, + pub outgoing_video_effective_fps: f64, + pub outgoing_video_target_fps: f64, + pub outgoing_video_pacing_target_fps: f64, + pub outgoing_video_max_queue_age_ms: u64, + pub outgoing_video_max_push_latency_ms: u64, + pub outgoing_video_pacing_mode: String, + pub outgoing_video_bus_active: bool, + pub outgoing_audio_buffer_target_ms: u32, + pub outgoing_audio_buffer_max_ms: u32, + pub outgoing_audio_underruns: u64, + pub outgoing_audio_rebuffers: u64, + pub outgoing_audio_max_frame_gap_ms: u64, + pub adaptive_send_tier: String, + pub adaptive_send_reason: String, +} + +#[derive(Clone, Debug, Default, PartialEq)] +pub struct ConnectionStats { + pub rtt_ms: Option, + pub outbound: Vec, + pub inbound: Vec, + pub send: Option, +} + +pub fn bitrate_kbps(prev: Option, cur: ByteRateSample) -> f64 { + let Some(prev) = prev else { + return 0.0; + }; + let dt_us = cur.timestamp_us - prev.timestamp_us; + if dt_us <= 0 { + return 0.0; + } + if cur.bytes < prev.bytes { + return 0.0; + } + let delta_bytes = (cur.bytes - prev.bytes) as f64; + let dt_seconds = dt_us as f64 / 1_000_000.0; + (delta_bytes * 8.0) / dt_seconds / 1000.0 +} + +pub fn sanitize_kbps(kbps: f64) -> f64 { + if !kbps.is_finite() || kbps < 0.0 { + return 0.0; + } + (kbps * 10.0).round() / 10.0 +} + +pub fn jitter_seconds_to_ms(jitter_s: f64) -> Option { + if !jitter_s.is_finite() || jitter_s < 0.0 { + return None; + } + Some((jitter_s * 1000.0 * 100.0).round() / 100.0) +} + +pub fn rtt_seconds_to_ms(rtt_s: f64) -> Option { + if !rtt_s.is_finite() || rtt_s <= 0.0 { + return None; + } + Some((rtt_s * 1000.0 * 100.0).round() / 100.0) +} + +pub fn sanitize_audio_level(level: f64) -> Option { + if !level.is_finite() { + return None; + } + Some(level.clamp(0.0, 1.0)) +} + +pub fn stats_to_json(stats: &ConnectionStats) -> String { + let rtt = match stats.rtt_ms { + Some(ms) if ms.is_finite() && ms >= 0.0 => { + JsonValue::Raw(fmt_num((ms * 100.0).round() / 100.0)) + } + _ => JsonValue::Raw("null".to_string()), + }; + let outbound_items: Vec = stats.outbound.iter().map(outbound_json).collect(); + let inbound_items: Vec = stats.inbound.iter().map(inbound_json).collect(); + let send = match &stats.send { + Some(send) => JsonValue::Raw(send_health_json(send)), + None => JsonValue::Raw("null".to_string()), + }; + json_object(&[ + ("rttMs", rtt), + ("outbound", JsonValue::Raw(raw_array(&outbound_items))), + ("inbound", JsonValue::Raw(raw_array(&inbound_items))), + ("send", send), + ]) +} + +enum JsonValue { + Str(String), + Raw(String), +} + +fn outbound_json(entry: &OutboundStatsEntry) -> String { + let mut fields = vec![ + ("trackSid", JsonValue::Str(entry.track_sid.clone())), + ("source", JsonValue::Str(entry.source.clone())), + ("kind", JsonValue::Str(entry.kind.clone())), + ( + "bitrateKbps", + JsonValue::Raw(fmt_num(sanitize_kbps(entry.bitrate_kbps))), + ), + ( + "packetsLost", + JsonValue::Raw(entry.packets_lost.to_string()), + ), + ( + "packetsSent", + JsonValue::Raw(entry.packets_sent.to_string()), + ), + ]; + if let Some(fps) = entry.fps + && fps.is_finite() + && fps >= 0.0 + { + fields.push(("fps", JsonValue::Raw(fmt_num((fps * 10.0).round() / 10.0)))); + } + if let Some(level) = entry.audio_level { + fields.push(( + "audioLevel", + JsonValue::Raw(fmt_num((level * 1000.0).round() / 1000.0)), + )); + } + if let Some(width) = entry.width { + fields.push(("width", JsonValue::Raw(width.to_string()))); + } + if let Some(height) = entry.height { + fields.push(("height", JsonValue::Raw(height.to_string()))); + } + if let Some(width) = entry.source_width { + fields.push(("sourceWidth", JsonValue::Raw(width.to_string()))); + } + if let Some(height) = entry.source_height { + fields.push(("sourceHeight", JsonValue::Raw(height.to_string()))); + } + if let Some(kbps) = entry.target_bitrate_kbps + && kbps.is_finite() + && kbps >= 0.0 + { + fields.push(( + "targetBitrateKbps", + JsonValue::Raw(fmt_num((kbps * 10.0).round() / 10.0)), + )); + } + if let Some(fps) = entry.configured_fps + && fps.is_finite() + && fps >= 0.0 + { + fields.push(( + "configuredFps", + JsonValue::Raw(fmt_num((fps * 10.0).round() / 10.0)), + )); + } + if let Some(fps) = entry.target_fps + && fps.is_finite() + && fps >= 0.0 + { + fields.push(( + "targetFps", + JsonValue::Raw(fmt_num((fps * 10.0).round() / 10.0)), + )); + } + if let Some(fps) = entry.effective_fps + && fps.is_finite() + && fps >= 0.0 + { + fields.push(( + "effectiveFps", + JsonValue::Raw(fmt_num((fps * 10.0).round() / 10.0)), + )); + } + if let Some(value) = entry.frames_produced { + fields.push(("framesProduced", JsonValue::Raw(value.to_string()))); + } + if let Some(value) = entry.frames_accepted { + fields.push(("framesAccepted", JsonValue::Raw(value.to_string()))); + } + if let Some(value) = entry.frames_dropped { + fields.push(("framesDropped", JsonValue::Raw(value.to_string()))); + } + if let Some(value) = entry.frames_coalesced { + fields.push(("framesCoalesced", JsonValue::Raw(value.to_string()))); + } + if let Some(value) = entry.frames_captured { + fields.push(("framesCaptured", JsonValue::Raw(value.to_string()))); + } + if let Some(value) = entry.capture_failures { + fields.push(("captureFailures", JsonValue::Raw(value.to_string()))); + } + if let Some(value) = entry.max_queue_age_ms { + fields.push(("maxQueueAgeMs", JsonValue::Raw(value.to_string()))); + } + if let Some(value) = entry.max_push_latency_ms { + fields.push(("maxPushLatencyMs", JsonValue::Raw(value.to_string()))); + } + if let Some(value) = &entry.adaptive_send_tier { + fields.push(("adaptiveSendTier", JsonValue::Str(value.clone()))); + } + if let Some(value) = &entry.adaptive_send_reason { + fields.push(("adaptiveSendReason", JsonValue::Str(value.clone()))); + } + if let Some(codec) = &entry.codec { + fields.push(("codec", JsonValue::Str(codec.clone()))); + } + json_object(&fields) +} + +fn inbound_json(entry: &InboundStatsEntry) -> String { + let mut fields = vec![ + ( + "participantSid", + JsonValue::Str(entry.participant_sid.clone()), + ), + ("trackSid", JsonValue::Str(entry.track_sid.clone())), + ("kind", JsonValue::Str(entry.kind.clone())), + ( + "bitrateKbps", + JsonValue::Raw(fmt_num(sanitize_kbps(entry.bitrate_kbps))), + ), + ( + "packetsLost", + JsonValue::Raw(entry.packets_lost.to_string()), + ), + ( + "packetsReceived", + JsonValue::Raw(entry.packets_received.to_string()), + ), + ]; + if let Some(identity) = &entry.participant_identity { + fields.push(("participantIdentity", JsonValue::Str(identity.clone()))); + } + if let Some(source) = &entry.source { + fields.push(("source", JsonValue::Str(source.clone()))); + } + if let Some(jitter_ms) = entry.jitter_ms { + fields.push(("jitterMs", JsonValue::Raw(fmt_num(jitter_ms)))); + } + if let Some(level) = entry.audio_level { + fields.push(( + "audioLevel", + JsonValue::Raw(fmt_num((level * 1000.0).round() / 1000.0)), + )); + } + if let Some(fps) = entry.fps + && fps.is_finite() + && fps >= 0.0 + { + fields.push(("fps", JsonValue::Raw(fmt_num((fps * 10.0).round() / 10.0)))); + } + if let Some(width) = entry.width { + fields.push(("width", JsonValue::Raw(width.to_string()))); + } + if let Some(height) = entry.height { + fields.push(("height", JsonValue::Raw(height.to_string()))); + } + if let Some(width) = entry.source_width { + fields.push(("sourceWidth", JsonValue::Raw(width.to_string()))); + } + if let Some(height) = entry.source_height { + fields.push(("sourceHeight", JsonValue::Raw(height.to_string()))); + } + if let Some(codec) = &entry.codec { + fields.push(("codec", JsonValue::Str(codec.clone()))); + } + json_object(&fields) +} + +fn send_health_json(send: &SendHealthStats) -> String { + json_object(&[ + ( + "outgoingVideoQueueDepth", + JsonValue::Raw(send.outgoing_video_queue_depth.to_string()), + ), + ( + "outgoingVideoQueueCapacity", + JsonValue::Raw(send.outgoing_video_queue_capacity.to_string()), + ), + ( + "outgoingVideoMaxQueueDepth", + JsonValue::Raw(send.outgoing_video_max_queue_depth.to_string()), + ), + ( + "outgoingVideoFramesProduced", + JsonValue::Raw(send.outgoing_video_frames_produced.to_string()), + ), + ( + "outgoingVideoFramesAccepted", + JsonValue::Raw(send.outgoing_video_frames_accepted.to_string()), + ), + ( + "outgoingVideoFramesDropped", + JsonValue::Raw(send.outgoing_video_frames_dropped.to_string()), + ), + ( + "outgoingVideoFramesCoalesced", + JsonValue::Raw(send.outgoing_video_frames_coalesced.to_string()), + ), + ( + "outgoingVideoFramesCaptured", + JsonValue::Raw(send.outgoing_video_frames_captured.to_string()), + ), + ( + "outgoingVideoCaptureFailures", + JsonValue::Raw(send.outgoing_video_capture_failures.to_string()), + ), + ( + "outgoingVideoEffectiveFps", + JsonValue::Raw(fmt_num(send.outgoing_video_effective_fps)), + ), + ( + "outgoingVideoTargetFps", + JsonValue::Raw(fmt_num(send.outgoing_video_target_fps)), + ), + ( + "outgoingVideoPacingTargetFps", + JsonValue::Raw(fmt_num(send.outgoing_video_pacing_target_fps)), + ), + ( + "outgoingVideoMaxQueueAgeMs", + JsonValue::Raw(send.outgoing_video_max_queue_age_ms.to_string()), + ), + ( + "outgoingVideoMaxPushLatencyMs", + JsonValue::Raw(send.outgoing_video_max_push_latency_ms.to_string()), + ), + ( + "outgoingVideoPacingMode", + JsonValue::Str(send.outgoing_video_pacing_mode.clone()), + ), + ( + "outgoingVideoBusActive", + JsonValue::Raw(send.outgoing_video_bus_active.to_string()), + ), + ( + "outgoingAudioBufferTargetMs", + JsonValue::Raw(send.outgoing_audio_buffer_target_ms.to_string()), + ), + ( + "outgoingAudioBufferMaxMs", + JsonValue::Raw(send.outgoing_audio_buffer_max_ms.to_string()), + ), + ( + "outgoingAudioUnderruns", + JsonValue::Raw(send.outgoing_audio_underruns.to_string()), + ), + ( + "outgoingAudioRebuffers", + JsonValue::Raw(send.outgoing_audio_rebuffers.to_string()), + ), + ( + "outgoingAudioMaxFrameGapMs", + JsonValue::Raw(send.outgoing_audio_max_frame_gap_ms.to_string()), + ), + ( + "adaptiveSendTier", + JsonValue::Str(send.adaptive_send_tier.clone()), + ), + ( + "adaptiveSendReason", + JsonValue::Str(send.adaptive_send_reason.clone()), + ), + ]) +} + +fn json_object(fields: &[(&str, JsonValue)]) -> String { + let mut out = String::from("{"); + for (i, (key, value)) in fields.iter().enumerate() { + if i > 0 { + out.push(','); + } + push_json_string(&mut out, key); + out.push(':'); + match value { + JsonValue::Str(s) => push_json_string(&mut out, s), + JsonValue::Raw(r) => out.push_str(r), + } + } + out.push('}'); + out +} + +fn raw_array(items: &[String]) -> String { + let mut out = String::from("["); + for (i, item) in items.iter().enumerate() { + if i > 0 { + out.push(','); + } + out.push_str(item); + } + out.push(']'); + out +} + +fn push_json_string(out: &mut String, value: &str) { + out.push('"'); + for ch in value.chars() { + match ch { + '"' => out.push_str("\\\""), + '\\' => out.push_str("\\\\"), + '\n' => out.push_str("\\n"), + '\r' => out.push_str("\\r"), + '\t' => out.push_str("\\t"), + '\u{08}' => out.push_str("\\b"), + '\u{0C}' => out.push_str("\\f"), + c if (c as u32) < 0x20 => { + out.push_str(&format!("\\u{:04x}", c as u32)); + } + c => out.push(c), + } + } + out.push('"'); +} + +fn fmt_num(value: f64) -> String { + if !value.is_finite() { + return "0".to_string(); + } + if value.fract() == 0.0 { + return (value as i64).to_string(); + } + format!("{value}") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn bitrate_uses_byte_delta_over_time() { + let prev = ByteRateSample { + bytes: 2_000, + timestamp_us: 1_000_000, + }; + let cur = ByteRateSample { + bytes: 14_500, + timestamp_us: 1_500_000, + }; + + assert_eq!(bitrate_kbps(Some(prev), cur), 200.0); + } + + #[test] + fn stats_json_uses_canonical_voice_contract_shape() { + let stats = ConnectionStats { + rtt_ms: Some(18.25), + outbound: vec![OutboundStatsEntry { + track_sid: "TR_audio".to_string(), + source: "microphone".to_string(), + kind: "audio".to_string(), + codec: Some("audio/opus".to_string()), + bitrate_kbps: 48.04, + packets_lost: 0, + fps: None, + audio_level: Some(0.1234), + ..Default::default() + }], + inbound: vec![InboundStatsEntry { + participant_sid: "PA_remote".to_string(), + participant_identity: Some("user_2_connection_2".to_string()), + track_sid: "TR_remote".to_string(), + source: Some("microphone".to_string()), + kind: "audio".to_string(), + codec: None, + bitrate_kbps: 31.96, + packets_lost: 2, + packets_received: 100, + jitter_ms: Some(4.5), + audio_level: Some(0.1234), + fps: None, + width: None, + height: None, + source_width: None, + source_height: None, + }], + send: None, + }; + + assert_eq!( + stats_to_json(&stats), + "{\"rttMs\":18.25,\"outbound\":[{\"trackSid\":\"TR_audio\",\"source\":\"microphone\",\"kind\":\"audio\",\"bitrateKbps\":48,\"packetsLost\":0,\"packetsSent\":0,\"audioLevel\":0.123,\"codec\":\"audio/opus\"}],\"inbound\":[{\"participantSid\":\"PA_remote\",\"trackSid\":\"TR_remote\",\"kind\":\"audio\",\"bitrateKbps\":32,\"packetsLost\":2,\"packetsReceived\":100,\"participantIdentity\":\"user_2_connection_2\",\"source\":\"microphone\",\"jitterMs\":4.5,\"audioLevel\":0.123}],\"send\":null}" + ); + } +} diff --git a/fluxer_desktop/native/rust/src/win_process_loopback/audio_contract.rs b/fluxer_desktop/native/rust/src/win_process_loopback/audio_contract.rs new file mode 100644 index 0000000..2b659e5 --- /dev/null +++ b/fluxer_desktop/native/rust/src/win_process_loopback/audio_contract.rs @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub const TARGET_SAMPLE_RATE: u32 = 48_000; +pub const TARGET_CHANNELS: u16 = 2; +pub const BITS_PER_SAMPLE: u16 = 32; +pub const BYTES_PER_SAMPLE: u16 = BITS_PER_SAMPLE / 8; +pub const FRAME_BLOCK_ALIGN: u16 = TARGET_CHANNELS * BYTES_PER_SAMPLE; +pub const AVG_BYTES_PER_SECOND: u32 = + TARGET_SAMPLE_RATE * TARGET_CHANNELS as u32 * BYTES_PER_SAMPLE as u32; + +pub fn validate_sample_rate(value: u32) -> bool { + value == TARGET_SAMPLE_RATE +} + +pub fn validate_channels(value: u32) -> bool { + value == TARGET_CHANNELS as u32 +} + +pub fn sample_count_for_frames(frames: u32) -> Option { + (frames as usize).checked_mul(TARGET_CHANNELS as usize) +} + +pub fn qpc_100ns_to_timestamp_us(qpc_100ns: u64) -> i64 { + (qpc_100ns / 10).min(i64::MAX as u64) as i64 +} + +pub fn pcm16_to_float32(sample: i16) -> f32 { + sample as f32 / 32768.0 +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn format_constants_describe_48khz_stereo_float32_frames() { + assert_eq!(48_000, TARGET_SAMPLE_RATE); + assert_eq!(2, TARGET_CHANNELS); + assert_eq!(32, BITS_PER_SAMPLE); + assert_eq!(8, FRAME_BLOCK_ALIGN); + assert_eq!(384_000, AVG_BYTES_PER_SECOND); + } + + #[test] + fn option_validation_rejects_unsupported_public_shapes() { + assert!(validate_sample_rate(48_000)); + assert!(!validate_sample_rate(44_100)); + assert!(validate_channels(2)); + assert!(!validate_channels(1)); + } + + #[test] + fn sample_counts_and_timestamps_are_bounded() { + assert_eq!(Some(0), sample_count_for_frames(0)); + assert_eq!(Some(2), sample_count_for_frames(1)); + assert_eq!(Some(9_600), sample_count_for_frames(4_800)); + assert_eq!(123_456, qpc_100ns_to_timestamp_us(1_234_560)); + assert_eq!((u64::MAX / 10) as i64, qpc_100ns_to_timestamp_us(u64::MAX)); + } + + #[test] + fn pcm16_samples_convert_to_normalized_float32_frames() { + assert_eq!(-1.0, pcm16_to_float32(i16::MIN)); + assert_eq!(0.0, pcm16_to_float32(0)); + assert!((pcm16_to_float32(i16::MAX) - 0.9999695).abs() < 0.0000001); + } +} diff --git a/fluxer_desktop/native/rust/src/win_process_loopback/mod.rs b/fluxer_desktop/native/rust/src/win_process_loopback/mod.rs new file mode 100644 index 0000000..30825f9 --- /dev/null +++ b/fluxer_desktop/native/rust/src/win_process_loopback/mod.rs @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub mod audio_contract; +pub mod process_tree; +pub mod session_mixer; +pub mod windows_abi; +pub mod windows_version; diff --git a/fluxer_desktop/native/rust/src/win_process_loopback/process_tree.rs b/fluxer_desktop/native/rust/src/win_process_loopback/process_tree.rs new file mode 100644 index 0000000..6fd238d --- /dev/null +++ b/fluxer_desktop/native/rust/src/win_process_loopback/process_tree.rs @@ -0,0 +1,299 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct ProcessEntry { + pub pid: u32, + pub parent: u32, +} + +fn find_process(entries: &[ProcessEntry], pid: u32) -> Option { + entries.iter().copied().find(|entry| entry.pid == pid) +} + +pub fn pid_is_our_descendant(entries: &[ProcessEntry], target: u32, self_pid: u32) -> bool { + if target == 0 { + return false; + } + if target == self_pid { + return true; + } + let mut current = target; + for _ in 0..64 { + let Some(entry) = find_process(entries, current) else { + return false; + }; + if entry.parent == 0 || entry.parent == current { + return false; + } + if entry.parent == self_pid { + return true; + } + current = entry.parent; + } + false +} + +pub fn pid_is_our_ancestor(entries: &[ProcessEntry], target: u32, self_pid: u32) -> bool { + if target == 0 { + return false; + } + let mut current = self_pid; + for _ in 0..64 { + if current == target { + return true; + } + let Some(entry) = find_process(entries, current) else { + return false; + }; + if entry.parent == 0 || entry.parent == current { + return false; + } + current = entry.parent; + } + false +} + +pub fn pid_overlaps_our_process_tree(entries: &[ProcessEntry], target: u32, self_pid: u32) -> bool { + pid_is_our_descendant(entries, target, self_pid) + || pid_is_our_ancestor(entries, target, self_pid) +} + +pub fn deduplicate_capture_roots( + entries: &[ProcessEntry], + capture_pids: &[u32], + exclude_pids: &[u32], +) -> Vec { + use std::collections::BTreeSet; + + let mut uncaptured: BTreeSet = capture_pids + .iter() + .copied() + .filter(|pid| *pid != 0) + .collect(); + let excluded: BTreeSet = exclude_pids + .iter() + .copied() + .filter(|pid| *pid != 0) + .collect(); + uncaptured.retain(|candidate| { + !excluded + .iter() + .any(|excluded_pid| pid_is_our_descendant(entries, *excluded_pid, *candidate)) + }); + + let mut explicit = BTreeSet::new(); + while !uncaptured.is_empty() { + let before = uncaptured.len(); + for pid in uncaptured.clone() { + let parent = find_process(entries, pid) + .map(|entry| entry.parent) + .unwrap_or(0); + if !uncaptured.contains(&parent) { + explicit.insert(pid); + } + } + for pid in &explicit { + uncaptured.remove(pid); + } + for pid in uncaptured.clone() { + let parent = find_process(entries, pid) + .map(|entry| entry.parent) + .unwrap_or(0); + if explicit.contains(&parent) { + uncaptured.remove(&pid); + } + } + if uncaptured.len() == before { + explicit.extend(uncaptured.iter().copied()); + break; + } + } + explicit.into_iter().collect() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn self_pid_is_always_its_own_descendant() { + assert!(pid_is_our_descendant(&[], 1234, 1234)); + } + + #[test] + fn pid_of_zero_is_never_matched() { + assert!(!pid_is_our_descendant(&[], 0, 0)); + assert!(!pid_is_our_descendant(&[], 0, 1234)); + } + + #[test] + fn direct_child_of_self_pid_is_matched() { + let entries = [ProcessEntry { + pid: 4001, + parent: 1234, + }]; + assert!(pid_is_our_descendant(&entries, 4001, 1234)); + } + + #[test] + fn transitive_descendant_via_two_hops_is_matched() { + let entries = [ + ProcessEntry { + pid: 4001, + parent: 1234, + }, + ProcessEntry { + pid: 4099, + parent: 4001, + }, + ]; + assert!(pid_is_our_descendant(&entries, 4099, 1234)); + } + + #[test] + fn ancestor_of_self_pid_is_matched() { + let entries = [ + ProcessEntry { pid: 10, parent: 1 }, + ProcessEntry { + pid: 1234, + parent: 10, + }, + ]; + assert!(pid_is_our_ancestor(&entries, 10, 1234)); + assert!(pid_overlaps_our_process_tree(&entries, 10, 1234)); + } + + #[test] + fn deduplicate_capture_roots_keeps_parent_and_drops_child() { + let entries = [ + ProcessEntry { pid: 10, parent: 1 }, + ProcessEntry { + pid: 11, + parent: 10, + }, + ]; + assert_eq!( + deduplicate_capture_roots(&entries, &[10, 11], &[]), + vec![10] + ); + } + + #[test] + fn deduplicate_capture_roots_drops_parent_of_excluded_child() { + let entries = [ + ProcessEntry { pid: 10, parent: 1 }, + ProcessEntry { + pid: 11, + parent: 10, + }, + ]; + assert_eq!( + deduplicate_capture_roots(&entries, &[10], &[11]), + Vec::::new() + ); + } + + #[test] + fn deduplicate_capture_roots_drops_grandparent_of_excluded_grandchild() { + let entries = [ + ProcessEntry { pid: 10, parent: 1 }, + ProcessEntry { + pid: 11, + parent: 10, + }, + ProcessEntry { + pid: 12, + parent: 11, + }, + ]; + assert_eq!( + deduplicate_capture_roots(&entries, &[10], &[12]), + Vec::::new() + ); + } + + #[test] + fn deduplicate_capture_roots_keeps_safe_sibling_after_excluding_parent_root() { + let entries = [ + ProcessEntry { pid: 10, parent: 1 }, + ProcessEntry { + pid: 11, + parent: 10, + }, + ProcessEntry { + pid: 12, + parent: 10, + }, + ]; + assert_eq!( + deduplicate_capture_roots(&entries, &[10, 12], &[11]), + vec![12] + ); + } + + #[test] + fn deduplicate_capture_roots_drops_excluded_root_itself() { + let entries = [ProcessEntry { pid: 10, parent: 1 }]; + assert_eq!( + deduplicate_capture_roots(&entries, &[10], &[10]), + Vec::::new() + ); + } + + #[test] + fn deduplicate_capture_roots_ignores_zero_pids() { + let entries = [ProcessEntry { pid: 10, parent: 1 }]; + assert_eq!( + deduplicate_capture_roots(&entries, &[0, 10], &[0]), + vec![10] + ); + } + + #[test] + fn deduplicate_capture_roots_keeps_pid_when_parent_is_missing() { + let entries = [ProcessEntry { + pid: 10, + parent: 9999, + }]; + assert_eq!(deduplicate_capture_roots(&entries, &[10], &[]), vec![10]); + } + + #[test] + fn unrelated_pid_in_same_snapshot_is_not_matched() { + let entries = [ + ProcessEntry { + pid: 4001, + parent: 1234, + }, + ProcessEntry { + pid: 8500, + parent: 1, + }, + ]; + assert!(!pid_is_our_descendant(&entries, 8500, 1234)); + } + + #[test] + fn missing_parent_terminates_the_walk_safely() { + let entries = [ProcessEntry { + pid: 9000, + parent: 9999, + }]; + assert!(!pid_is_our_descendant(&entries, 9000, 1234)); + } + + #[test] + fn ppid_equal_to_self_loop_is_rejected() { + let entries = [ProcessEntry { + pid: 5000, + parent: 5000, + }]; + assert!(!pid_is_our_descendant(&entries, 5000, 1234)); + } + + #[test] + fn ppid_zero_terminates_the_walk() { + let entries = [ProcessEntry { pid: 4, parent: 0 }]; + assert!(!pid_is_our_descendant(&entries, 4, 1234)); + } +} diff --git a/fluxer_desktop/native/rust/src/win_process_loopback/session_mixer.rs b/fluxer_desktop/native/rust/src/win_process_loopback/session_mixer.rs new file mode 100644 index 0000000..1b98f79 --- /dev/null +++ b/fluxer_desktop/native/rust/src/win_process_loopback/session_mixer.rs @@ -0,0 +1,238 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Debug, Clone, PartialEq)] +pub struct MixerPacket { + pub timestamp_us: i64, + pub samples: Vec, +} + +pub fn timestamp_delta_to_frames( + base_timestamp_us: i64, + timestamp_us: i64, + sample_rate: u32, +) -> usize { + if sample_rate == 0 { + return 0; + } + let delta_us = timestamp_us.saturating_sub(base_timestamp_us).max(0) as u128; + ((delta_us * u128::from(sample_rate)) / 1_000_000) as usize +} + +fn normalize_packet_to_whole_frames( + mut packet: MixerPacket, + channel_count: usize, +) -> Option { + if channel_count == 0 { + return None; + } + let whole = (packet.samples.len() / channel_count) * channel_count; + if whole == 0 { + return None; + } + packet.samples.truncate(whole); + for sample in &mut packet.samples { + *sample = (*sample).clamp(-1.0, 1.0); + } + Some(packet) +} + +pub fn mix_packets( + mut packets: Vec, + channels: u16, + sample_rate: u32, + max_emit_frames: usize, +) -> Vec { + let channel_count = usize::from(channels); + if channel_count == 0 || sample_rate == 0 { + return Vec::new(); + } + packets.retain(|packet| packet.samples.len() >= channel_count); + if packets.is_empty() { + return Vec::new(); + } + packets.sort_by_key(|packet| packet.timestamp_us); + let base_timestamp = packets[0].timestamp_us; + let mut output_frames = 0usize; + for packet in &packets { + let offset_frames = + timestamp_delta_to_frames(base_timestamp, packet.timestamp_us, sample_rate); + let frames = packet.samples.len() / channel_count; + output_frames = output_frames.max(offset_frames.saturating_add(frames)); + } + if output_frames == 0 { + return Vec::new(); + } + if output_frames > max_emit_frames { + return packets + .into_iter() + .filter_map(|packet| normalize_packet_to_whole_frames(packet, channel_count)) + .collect(); + } + let mut mixed = vec![0.0f32; output_frames * channel_count]; + for packet in packets { + let offset = timestamp_delta_to_frames(base_timestamp, packet.timestamp_us, sample_rate) + * channel_count; + let full_frame_samples = (packet.samples.len() / channel_count) * channel_count; + for (idx, sample) in packet + .samples + .iter() + .copied() + .take(full_frame_samples) + .enumerate() + { + if let Some(slot) = mixed.get_mut(offset + idx) { + *slot += sample; + } + } + } + for sample in &mut mixed { + *sample = (*sample).clamp(-1.0, 1.0); + } + vec![MixerPacket { + timestamp_us: base_timestamp, + samples: mixed, + }] +} + +#[cfg(test)] +mod tests { + use super::*; + + fn packet(timestamp_us: i64, samples: &[f32]) -> MixerPacket { + MixerPacket { + timestamp_us, + samples: samples.to_vec(), + } + } + + #[test] + fn timestamp_delta_to_frames_uses_audio_rate() { + assert_eq!(0, timestamp_delta_to_frames(1_000, 1_000, 48_000)); + assert_eq!(48, timestamp_delta_to_frames(1_000, 2_000, 48_000)); + assert_eq!(0, timestamp_delta_to_frames(2_000, 1_000, 48_000)); + assert_eq!(0, timestamp_delta_to_frames(1_000, 2_000, 0)); + } + + #[test] + fn mix_packets_ignores_empty_input() { + assert!(mix_packets(Vec::new(), 2, 48_000, 24_000).is_empty()); + assert!(mix_packets(vec![packet(0, &[])], 2, 48_000, 24_000).is_empty()); + } + + #[test] + fn mix_packets_rejects_invalid_format_shape() { + assert!(mix_packets(vec![packet(0, &[1.0, 1.0])], 0, 48_000, 24_000).is_empty()); + assert!(mix_packets(vec![packet(0, &[1.0, 1.0])], 2, 0, 24_000).is_empty()); + } + + #[test] + fn mix_packets_sums_packets_with_matching_timestamps() { + let mixed = mix_packets( + vec![packet(10, &[0.25, 0.5]), packet(10, &[0.25, -0.25])], + 2, + 48_000, + 24_000, + ); + assert_eq!(mixed, vec![packet(10, &[0.5, 0.25])]); + } + + #[test] + fn mix_packets_clamps_overlapping_samples() { + let mixed = mix_packets( + vec![packet(10, &[0.75, -0.75]), packet(10, &[0.75, -0.75])], + 2, + 48_000, + 24_000, + ); + assert_eq!(mixed, vec![packet(10, &[1.0, -1.0])]); + } + + #[test] + fn mix_packets_offsets_by_timestamp() { + let mixed = mix_packets( + vec![packet(1_000, &[1.0, 0.0]), packet(2_000, &[0.0, 1.0])], + 2, + 48_000, + 24_000, + ); + assert_eq!(mixed.len(), 1); + assert_eq!(mixed[0].timestamp_us, 1_000); + assert_eq!(&mixed[0].samples[0..2], &[1.0, 0.0]); + assert_eq!(&mixed[0].samples[96..98], &[0.0, 1.0]); + } + + #[test] + fn mix_packets_sorts_packets_before_mixing() { + let mixed = mix_packets( + vec![packet(2_000, &[0.0, 1.0]), packet(1_000, &[1.0, 0.0])], + 2, + 48_000, + 24_000, + ); + assert_eq!(mixed.len(), 1); + assert_eq!(mixed[0].timestamp_us, 1_000); + assert_eq!(&mixed[0].samples[0..2], &[1.0, 0.0]); + assert_eq!(&mixed[0].samples[96..98], &[0.0, 1.0]); + } + + #[test] + fn mix_packets_falls_back_to_sorted_packets_when_span_is_too_large() { + let packets = vec![packet(2_000, &[0.0, 1.0]), packet(1_000, &[1.0, 0.0])]; + let mixed = mix_packets(packets, 2, 48_000, 1); + assert_eq!( + mixed, + vec![packet(1_000, &[1.0, 0.0]), packet(2_000, &[0.0, 1.0])] + ); + } + + #[test] + fn mix_packets_fallback_truncates_partial_frames_to_whole_stereo() { + let packets = vec![ + packet(2_000, &[0.0, 1.0, 0.5]), + packet(1_000, &[1.0, 0.0, 0.25, 0.75]), + ]; + let mixed = mix_packets(packets, 2, 48_000, 1); + assert_eq!( + mixed, + vec![ + packet(1_000, &[1.0, 0.0, 0.25, 0.75]), + packet(2_000, &[0.0, 1.0]), + ] + ); + for emitted in &mixed { + assert_eq!(emitted.samples.len() % 2, 0); + } + } + + #[test] + fn mix_packets_fallback_clamps_out_of_range_samples() { + let packets = vec![ + packet(2_000, &[0.0, 1.0]), + packet(1_000, &[4.0, -4.0, 0.5, -0.5]), + ]; + let mixed = mix_packets(packets, 2, 48_000, 1); + assert_eq!( + mixed, + vec![ + packet(1_000, &[1.0, -1.0, 0.5, -0.5]), + packet(2_000, &[0.0, 1.0]), + ] + ); + } + + #[test] + fn mix_packets_fallback_drops_subframe_packets() { + let packets = vec![packet(2_000, &[0.0, 1.0]), packet(1_000, &[0.5])]; + let mixed = mix_packets(packets, 2, 48_000, 1); + assert_eq!(mixed, vec![packet(2_000, &[0.0, 1.0])]); + for emitted in &mixed { + assert_eq!(emitted.samples.len() % 2, 0); + } + } + + #[test] + fn mix_packets_truncates_partial_trailing_frames() { + let mixed = mix_packets(vec![packet(10, &[0.25, 0.5, 0.75])], 2, 48_000, 24_000); + assert_eq!(mixed, vec![packet(10, &[0.25, 0.5])]); + } +} diff --git a/fluxer_desktop/native/rust/src/win_process_loopback/windows_abi.rs b/fluxer_desktop/native/rust/src/win_process_loopback/windows_abi.rs new file mode 100644 index 0000000..f56aff0 --- /dev/null +++ b/fluxer_desktop/native/rust/src/win_process_loopback/windows_abi.rs @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::ffi::c_void; + +pub type Bool = i32; +pub type Byte = u8; +pub type Dword = u32; +pub type Handle = *mut c_void; +pub type Hresult = i32; +pub type Long = i32; +pub type LargeInteger = i64; +pub type ReferenceTime = i64; +pub type Uint = u32; +pub type Ulong = u32; +pub type Word = u16; +pub type Wchar = u16; + +pub const FALSE: Bool = 0; +pub const TRUE: Bool = 1; +pub const INFINITE: Dword = 0xffff_ffff; +pub const INVALID_HANDLE_VALUE: Handle = usize::MAX as Handle; +pub const WAIT_FAILED: Dword = 0xffff_ffff; +pub const WAIT_OBJECT_0: Dword = 0; + +pub const S_OK: Hresult = 0; +pub const E_NOINTERFACE: Hresult = 0x8000_4002_u32 as Hresult; + +pub const COINIT_MULTITHREADED: Dword = 0; +pub const TH32CS_SNAPPROCESS: Dword = 0x0000_0002; +pub const VT_BLOB: Word = 65; +pub const WAVE_FORMAT_IEEE_FLOAT: Word = 3; +pub const WAVE_FORMAT_EXTENSIBLE: Word = 0xfffe; +pub const SPEAKER_FRONT_LEFT: Dword = 0x1; +pub const SPEAKER_FRONT_RIGHT: Dword = 0x2; +pub const KSAUDIO_SPEAKER_STEREO: Dword = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT; + +pub const AUDCLNT_BUFFERFLAGS_SILENT: Dword = 0x0000_0002; +pub const AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR: Dword = 0x0000_0004; +pub const AUDCLNT_SHAREMODE_SHARED: AudioClientShareMode = 0; +pub const AUDCLNT_STREAMFLAGS_LOOPBACK: Dword = 0x0002_0000; +pub const AUDCLNT_STREAMFLAGS_EVENTCALLBACK: Dword = 0x0004_0000; +pub const AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM: Dword = 0x8000_0000; + +pub type AudioClientActivationType = i32; +pub const AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK: AudioClientActivationType = 1; +pub type ProcessLoopbackMode = i32; +pub const PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE: ProcessLoopbackMode = 0; +pub const PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE: ProcessLoopbackMode = 1; +pub type AudioClientShareMode = i32; + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Guid { + pub data1: u32, + pub data2: u16, + pub data3: u16, + pub data4: [u8; 8], +} + +pub type Iid = Guid; + +pub const IID_IUNKNOWN: Guid = Guid { + data1: 0x0000_0000, + data2: 0x0000, + data3: 0x0000, + data4: [0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46], +}; + +pub const IID_IAGILE_OBJECT: Guid = Guid { + data1: 0x94ea_2b94, + data2: 0xe9cc, + data3: 0x49e0, + data4: [0xc0, 0xff, 0xee, 0x64, 0xca, 0x8f, 0x5b, 0x90], +}; + +pub const IID_IMARSHAL: Guid = Guid { + data1: 0x0000_0003, + data2: 0x0000, + data3: 0x0000, + data4: [0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46], +}; + +pub const IID_IAUDIO_CLIENT: Guid = Guid { + data1: 0x1cb9_ad4c, + data2: 0xdbfa, + data3: 0x4c32, + data4: [0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2], +}; + +pub const IID_IAUDIO_CAPTURE_CLIENT: Guid = Guid { + data1: 0xc8ad_bd64, + data2: 0xe71e, + data3: 0x48a0, + data4: [0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c, 0xd3, 0x17], +}; + +pub const KSDATAFORMAT_SUBTYPE_IEEE_FLOAT: Guid = Guid { + data1: 0x0000_0003, + data2: 0x0000, + data3: 0x0010, + data4: [0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71], +}; + +pub const IID_IACTIVATE_AUDIO_INTERFACE_COMPLETION_HANDLER: Guid = Guid { + data1: 0x41d9_49ab, + data2: 0x9862, + data3: 0x444a, + data4: [0x80, 0xf6, 0xc2, 0x61, 0x33, 0x4d, 0xa5, 0xeb], +}; + +pub const VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK: &[u16] = &[ + 'V' as u16, + 'A' as u16, + 'D' as u16, + '\\' as u16, + 'P' as u16, + 'r' as u16, + 'o' as u16, + 'c' as u16, + 'e' as u16, + 's' as u16, + 's' as u16, + '_' as u16, + 'L' as u16, + 'o' as u16, + 'o' as u16, + 'p' as u16, + 'b' as u16, + 'a' as u16, + 'c' as u16, + 'k' as u16, + 0, +]; + +#[repr(C, packed(1))] +#[derive(Clone, Copy)] +pub struct WaveFormatEx { + pub w_format_tag: Word, + pub n_channels: Word, + pub n_samples_per_sec: Dword, + pub n_avg_bytes_per_sec: Dword, + pub n_block_align: Word, + pub w_bits_per_sample: Word, + pub cb_size: Word, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union WaveFormatSamples { + pub w_valid_bits_per_sample: Word, + pub w_samples_per_block: Word, + pub w_reserved: Word, +} + +#[repr(C, packed(1))] +#[derive(Clone, Copy)] +pub struct WaveFormatExtensible { + pub format: WaveFormatEx, + pub samples: WaveFormatSamples, + pub dw_channel_mask: Dword, + pub sub_format: Guid, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct AudioClientProcessLoopbackParams { + pub target_process_id: Dword, + pub process_loopback_mode: ProcessLoopbackMode, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub union AudioClientActivationParamsAnonymous { + pub process_loopback_params: AudioClientProcessLoopbackParams, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct AudioClientActivationParams { + pub activation_type: AudioClientActivationType, + pub anonymous: AudioClientActivationParamsAnonymous, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct Blob { + pub cb_size: Ulong, + pub p_blob_data: *mut Byte, +} + +#[repr(C)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PropVariant { + pub vt: Word, + pub w_reserved1: Word, + pub w_reserved2: Word, + pub w_reserved3: Word, + pub blob: Blob, +} + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct ProcessEntry32W { + pub dw_size: Dword, + pub cnt_usage: Dword, + pub th32_process_id: Dword, + pub th32_default_heap_id: usize, + pub th32_module_id: Dword, + pub cnt_threads: Dword, + pub th32_parent_process_id: Dword, + pub pc_pri_class_base: Long, + pub dw_flags: Dword, + pub sz_exe_file: [Wchar; 260], +} + +pub type NapiEnv = *mut c_void; +pub type NapiValue = *mut c_void; +pub type NapiCallbackInfo = *mut c_void; +pub type NapiThreadsafeFunction = *mut c_void; +pub type NapiStatus = i32; +pub type NapiValueType = i32; +pub type NapiTypedArrayType = i32; +pub type NapiThreadsafeFunctionReleaseMode = i32; +pub type NapiThreadsafeFunctionCallMode = i32; +pub type NapiPropertyAttributes = i32; + +pub const NAPI_OK: NapiStatus = 0; +pub const NAPI_UNDEFINED: NapiValueType = 0; +pub const NAPI_NULL: NapiValueType = 1; +pub const NAPI_BOOLEAN: NapiValueType = 2; +pub const NAPI_NUMBER: NapiValueType = 3; +pub const NAPI_STRING: NapiValueType = 4; +pub const NAPI_FLOAT32_ARRAY: NapiTypedArrayType = 6; +pub const NAPI_DEFAULT_METHOD: NapiPropertyAttributes = 5; +pub const NAPI_TSFN_NONBLOCKING: NapiThreadsafeFunctionCallMode = 0; +pub const NAPI_TSFN_RELEASE: NapiThreadsafeFunctionReleaseMode = 0; +pub const NAPI_TSFN_ABORT: NapiThreadsafeFunctionReleaseMode = 1; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn wave_format_extensible_layout_matches_windows_abi() { + assert_eq!(18, std::mem::size_of::()); + assert_eq!(40, std::mem::size_of::()); + assert_eq!(18, std::mem::offset_of!(WaveFormatExtensible, samples)); + assert_eq!( + 20, + std::mem::offset_of!(WaveFormatExtensible, dw_channel_mask) + ); + assert_eq!(24, std::mem::offset_of!(WaveFormatExtensible, sub_format)); + } + + #[test] + fn stereo_float_extensible_constants_match_ksmedia_h() { + assert_eq!(0xfffe, WAVE_FORMAT_EXTENSIBLE); + assert_eq!(0x3, KSAUDIO_SPEAKER_STEREO); + assert_eq!(0x4, AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR); + assert_eq!(0x0000_0003, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT.data1); + assert_eq!(0x0000, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT.data2); + assert_eq!(0x0010, KSDATAFORMAT_SUBTYPE_IEEE_FLOAT.data3); + assert_eq!( + [0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71], + KSDATAFORMAT_SUBTYPE_IEEE_FLOAT.data4 + ); + } + + #[test] + fn iagile_object_iid_matches_objidlbase_h() { + assert_eq!(0x94ea_2b94, IID_IAGILE_OBJECT.data1); + assert_eq!(0xe9cc, IID_IAGILE_OBJECT.data2); + assert_eq!(0x49e0, IID_IAGILE_OBJECT.data3); + assert_eq!( + [0xc0, 0xff, 0xee, 0x64, 0xca, 0x8f, 0x5b, 0x90], + IID_IAGILE_OBJECT.data4 + ); + } + + #[test] + fn imarshal_iid_matches_objidlbase_h() { + assert_eq!(0x0000_0003, IID_IMARSHAL.data1); + assert_eq!(0x0000, IID_IMARSHAL.data2); + assert_eq!(0x0000, IID_IMARSHAL.data3); + assert_eq!( + [0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46], + IID_IMARSHAL.data4 + ); + } + + #[test] + fn node_api_value_type_constants_match_node_api_h() { + assert_eq!(0, NAPI_UNDEFINED); + assert_eq!(1, NAPI_NULL); + assert_eq!(2, NAPI_BOOLEAN); + assert_eq!(3, NAPI_NUMBER); + assert_eq!(4, NAPI_STRING); + } + + #[test] + fn process_loopback_device_id_is_utf16_null_terminated() { + assert_eq!(Some(&0), VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK.last()); + let without_nul = &VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK + [..VIRTUAL_AUDIO_DEVICE_PROCESS_LOOPBACK.len() - 1]; + assert_eq!( + "VAD\\Process_Loopback", + String::from_utf16(without_nul).expect("utf16") + ); + } +} diff --git a/fluxer_desktop/native/rust/src/win_process_loopback/windows_version.rs b/fluxer_desktop/native/rust/src/win_process_loopback/windows_version.rs new file mode 100644 index 0000000..9ad6550 --- /dev/null +++ b/fluxer_desktop/native/rust/src/win_process_loopback/windows_version.rs @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub const PROCESS_LOOPBACK_MIN_BUILD: u32 = 20_348; + +pub fn supports_process_loopback(major: u32, minor: u32, build: u32) -> bool { + if major > 10 { + return true; + } + major == 10 && minor == 0 && build >= PROCESS_LOOPBACK_MIN_BUILD +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn process_loopback_follows_microsoft_documented_windows_build_floor() { + assert!(!supports_process_loopback(10, 0, 19_045)); + assert!(!supports_process_loopback( + 10, + 0, + PROCESS_LOOPBACK_MIN_BUILD - 1 + )); + assert!(supports_process_loopback(10, 0, PROCESS_LOOPBACK_MIN_BUILD)); + assert!(supports_process_loopback(10, 0, 22_000)); + assert!(supports_process_loopback(11, 0, 0)); + } +} diff --git a/fluxer_desktop/native/rust/tests/properties.rs b/fluxer_desktop/native/rust/tests/properties.rs new file mode 100644 index 0000000..114cba2 --- /dev/null +++ b/fluxer_desktop/native/rust/tests/properties.rs @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use fluxer_desktop_native::audio::contract::{ + DIRECT_CAPTURE_CHANNELS, DIRECT_CAPTURE_MAX_READ_SAMPLES, bounded_direct_read_sample_count, + direct_whole_frame_sample_count, whole_frame_sample_count, +}; +use fluxer_desktop_native::input::ring::Ring; +use fluxer_desktop_native::linux_audio::routing::{ + MEDIA_CLASS_PLAYBACK_STREAM, RoutingRule, SelfIdentity, map, matches_pattern, should_route_node, +}; +use fluxer_desktop_native::linux_evdev::event::{InputEvent, parse_input_event}; +use fluxer_desktop_native::linux_portals::pid_payload::parse_shell_eval_pid_payload; +use fluxer_desktop_native::mac_app_audio::process_tree::{ + Info, collect_related_pids_with_resolver, is_same_launch_tree_with_resolver, +}; +use proptest::prelude::*; + +fn input_event_bytes(event: InputEvent) -> [u8; InputEvent::BYTE_LEN] { + let mut out = [0_u8; InputEvent::BYTE_LEN]; + out[0..8].copy_from_slice(&event.time_sec.to_ne_bytes()); + out[8..16].copy_from_slice(&event.time_usec.to_ne_bytes()); + out[16..18].copy_from_slice(&event.event_type.to_ne_bytes()); + out[18..20].copy_from_slice(&event.code.to_ne_bytes()); + out[20..24].copy_from_slice(&event.value.to_ne_bytes()); + out +} + +proptest! { + #[test] + fn whole_frame_count_never_exceeds_input_and_is_channel_aligned(sample_count in 0usize..1_000_000, channels in 0u32..16) { + let count = whole_frame_sample_count(sample_count, channels); + prop_assert!(count <= sample_count); + if channels == 0 { + prop_assert_eq!(0, count); + } else { + prop_assert_eq!(0, count % channels as usize); + } + } + + #[test] + fn direct_read_bound_is_stereo_aligned_and_capped(available in 0usize..1_000_000) { + let count = bounded_direct_read_sample_count(available); + prop_assert!(count <= DIRECT_CAPTURE_MAX_READ_SAMPLES); + prop_assert_eq!(0, count % DIRECT_CAPTURE_CHANNELS as usize); + } + + #[test] + fn pid_payload_returns_positive_u32_digit_runs(prefix in "[A-Za-z_\\[\\], ]*", pid in 1u32..u32::MAX, suffix in "[A-Za-z_\\[\\], ]*") { + let payload = format!("{prefix}{pid}{suffix}"); + prop_assert_eq!(Some(pid), parse_shell_eval_pid_payload(&payload)); + } + + #[test] + fn routing_pattern_matching_is_subset_exact(key in "[a-z.]{1,32}", value in "[a-z0-9_-]{1,32}", other in "[a-z0-9_-]{1,32}") { + let candidate = map(&[(&key, &value)]); + let matching = map(&[(&key, &value)]); + prop_assert!(matches_pattern(&candidate, &matching)); + if other != value { + let mismatched = map(&[(&key, &other)]); + prop_assert!(!matches_pattern(&candidate, &mismatched)); + } + } + + #[test] + fn evdev_input_event_parser_round_trips_native_endian_fields( + time_sec in any::(), + time_usec in any::(), + event_type in any::(), + code in any::(), + value in any::(), + ) { + let event = InputEvent { + time_sec, + time_usec, + event_type, + code, + value, + }; + prop_assert_eq!(Some(event), parse_input_event(&input_event_bytes(event))); + } + + #[test] + fn mac_process_tree_direct_children_are_collected(target in 2i32..100_000, child_delta in 1i32..1000) { + let child = target + child_delta; + let infos = [ + Info { pid: target, parent_pid: 1, process_group_id: target }, + Info { pid: child, parent_pid: target, process_group_id: target }, + ]; + let resolver = |pid| infos.iter().copied().find(|info| info.pid == pid); + prop_assert!(is_same_launch_tree_with_resolver(child, target, Some(infos[0]), resolver)); + let resolver = |pid| infos.iter().copied().find(|info| info.pid == pid); + prop_assert_eq!( + vec![target, child], + collect_related_pids_with_resolver(target, Some(infos[0]), &[child], 4, resolver) + ); + } +} + +#[test] +fn ring_stress_preserves_fifo_under_repeated_fill_drain_cycles() { + let mut ring: Ring = Ring::new(); + for cycle in 0..512_u64 { + for index in 0..1024_u64 { + let slot = ring.claim().expect("slot") as usize; + ring.slots[slot] = cycle * 10_000 + index; + } + assert!(ring.claim().is_none()); + for index in 0..1024_u64 { + let slot = ring.pop().expect("slot") as usize; + assert_eq!(cycle * 10_000 + index, ring.slots[slot]); + ring.release(); + } + assert!(ring.pop().is_none()); + } +} + +#[test] +fn self_identity_always_beats_user_include_rules() { + let mut self_identity = SelfIdentity::default(); + self_identity.add_pid("42"); + self_identity.add_binary("fluxer"); + self_identity.add_display_name("Fluxer Canary"); + self_identity.add_display_prefix("Fluxer "); + let rule = RoutingRule { + include_when: vec![ + map(&[("application.process.id", "42")]), + map(&[("application.name", "fluxer")]), + ], + ..RoutingRule::default() + }; + for props in [ + map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("application.process.id", "42"), + ]), + map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("application.process.binary", "fluxer"), + ]), + map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("application.name", "fluxer"), + ]), + map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ("node.name", "fluxer"), + ]), + map(&[ + ("media.class", MEDIA_CLASS_PLAYBACK_STREAM), + ( + "node.description", + "Fluxer desktop audio tap excluding pid 42", + ), + ]), + ] { + assert!(!should_route_node( + 1, + &props, + &rule, + "", + "", + 0, + &self_identity + )); + } +} + +#[test] +fn direct_whole_frame_count_is_same_as_generic_stereo_helper() { + for value in 0..10_000 { + assert_eq!( + whole_frame_sample_count(value, 2), + direct_whole_frame_sample_count(value) + ); + } +} diff --git a/fluxer_desktop/native/screen-frame-bus/Cargo.lock b/fluxer_desktop/native/screen-frame-bus/Cargo.lock new file mode 100644 index 0000000..51b8f0a --- /dev/null +++ b/fluxer_desktop/native/screen-frame-bus/Cargo.lock @@ -0,0 +1,1498 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading", +] + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "bit-set" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ddef2995421ab6a5c779542c81ee77c115206f4ad9d5a8e05f4ff49716a3dd" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +dependencies = [ + "bytemuck_derive", +] + +[[package]] +name = "bytemuck_derive" +version = "1.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstyle", + "clap_lex", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "codespan-reporting" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" +dependencies = [ + "serde", + "termcolor", + "unicode-width", +] + +[[package]] +name = "criterion" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3" +dependencies = [ + "alloca", + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools", + "num-traits", + "oorandom", + "page_size", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea" +dependencies = [ + "cast", + "itertools", +] + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags", + "objc2", +] + +[[package]] +name = "dlib" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" +dependencies = [ + "libloading", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fluxer_gpu_rebuild" +version = "0.0.0" +dependencies = [ + "parking_lot", + "wgpu", +] + +[[package]] +name = "fluxer_screen_frame_bus" +version = "0.0.0" +dependencies = [ + "criterion", + "fluxer_gpu_rebuild", + "parking_lot", + "wgpu", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-task", + "pin-project-lite", + "slab", +] + +[[package]] +name = "gl_generator" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d" +dependencies = [ + "khronos_api", + "log", + "xml-rs", +] + +[[package]] +name = "glow" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29038e1c483364cc6bb3cf78feee1816002e127c331a1eec55a4d202b9e1adb5" +dependencies = [ + "js-sys", + "slotmap", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "glutin_wgl_sys" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" +dependencies = [ + "gl_generator", +] + +[[package]] +name = "gpu-allocator" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51255ea7cfaadb6c5f1528d43e92a82acb2b96c43365989a28b2d44ee38f8795" +dependencies = [ + "ash", + "hashbrown 0.16.1", + "log", + "presser", + "thiserror", + "windows", +] + +[[package]] +name = "gpu-descriptor" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" +dependencies = [ + "bitflags", + "gpu-descriptor-types", + "hashbrown 0.15.5", +] + +[[package]] +name = "gpu-descriptor-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" +dependencies = [ + "bitflags", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if", + "crunchy", + "num-traits", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "hexf-parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "js-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2025f20d7a4fa7785846e7b63d10a76d3f1cee98ee5cb79ea59703f95e42162" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "khronos-egl" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" +dependencies = [ + "libc", + "libloading", + "pkg-config", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "naga" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd91265cc2454558f659b3b4b9640f0ddb8cc6521277f166b8a8c181c898079" +dependencies = [ + "arrayvec", + "bit-set", + "bitflags", + "cfg-if", + "cfg_aliases", + "codespan-reporting", + "half", + "hashbrown 0.16.1", + "hexf-parse", + "indexmap", + "libm", + "log", + "num-traits", + "once_cell", + "rustc-hash", + "spirv", + "thiserror", + "unicode-ident", +] + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys 0.3.1", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" +dependencies = [ + "bitflags", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "ordered-float" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e" +dependencies = [ + "num-traits", +] + +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "presser" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "profiling" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "range-alloc" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca45419789ae5a7899559e9512e58ca889e41f04f1f2445e9f4b290ceccd1d08" + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "raw-window-metal" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40d213455a5f1dc59214213c7330e074ddf8114c9a42411eb890c767357ce135" +dependencies = [ + "objc2", + "objc2-core-foundation", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "renderdoc-sys" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "slotmap" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038" +dependencies = [ + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "spirv" +version = "0.4.0+sdk-1.4.341.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9571ea910ebd84c86af4b3ed27f9dbdc6ad06f17c5f96146b2b671e2976744f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a254a4b10c19a76f09a27640e7ffbf9bc30bf67e16a3bf28aaefa4920fe81563" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54568702fabf5d4849ce2b90fadfa64168a097eaf4b351ce9df8b687a0086aaf" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24a40fc75b0ec6f3746ceb10d36f53a93dcd68a93b11b6445983945d79eba0dc" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "908f34bd9b9ce3d4caf07b72dfab63d61504d156856c6bd3cd87fa350cf3985b" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.123" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acbf7616c27b194bbb550bf77ed0c2c3e5b7fd1260a93082b95fb7f47959b92" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wayland-sys" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e0871acf327f283dc6da28a1696cdc64fb355ba9f935d052021fa77f35cce69" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wgpu" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb3feacc458f7bee8bc1737149b42b6c731aa461039a4264a67bb6681646b250" +dependencies = [ + "arrayvec", + "bitflags", + "bytemuck", + "cfg-if", + "cfg_aliases", + "document-features", + "hashbrown 0.16.1", + "js-sys", + "log", + "naga", + "parking_lot", + "portable-atomic", + "profiling", + "raw-window-handle", + "smallvec", + "static_assertions", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "wgpu-core", + "wgpu-hal", + "wgpu-types", +] + +[[package]] +name = "wgpu-core" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02da3ad1b568337f25513b317870960ef87073ea0945502e44b864b67a8c77b7" +dependencies = [ + "arrayvec", + "bit-set", + "bit-vec", + "bitflags", + "bytemuck", + "cfg_aliases", + "document-features", + "hashbrown 0.16.1", + "indexmap", + "log", + "naga", + "once_cell", + "parking_lot", + "portable-atomic", + "profiling", + "raw-window-handle", + "rustc-hash", + "smallvec", + "thiserror", + "wgpu-core-deps-apple", + "wgpu-core-deps-emscripten", + "wgpu-core-deps-windows-linux-android", + "wgpu-hal", + "wgpu-naga-bridge", + "wgpu-types", +] + +[[package]] +name = "wgpu-core-deps-apple" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62e51b5447e144b3dbba4feb01f80f4fa21696fa0cd99afb2c3df1affd6fdb28" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-emscripten" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3487cd6293a963bc5c0c0396f6a2192043c50003c07f4efdccbad3d90ec9d819" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-windows-linux-android" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb01076d0aa08b0ba9bd741e178b5cc440f5abe99d9581323a4c8b5d1a1916" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-hal" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31f8e1a9e7a8512f276f7c62e018c7fa8d60954303fed2e5750114332049193f" +dependencies = [ + "android_system_properties", + "arrayvec", + "ash", + "bit-set", + "bitflags", + "block2", + "bytemuck", + "cfg-if", + "cfg_aliases", + "glow", + "glutin_wgl_sys", + "gpu-allocator", + "gpu-descriptor", + "hashbrown 0.16.1", + "js-sys", + "khronos-egl", + "libc", + "libloading", + "log", + "naga", + "ndk-sys", + "objc2", + "objc2-core-foundation", + "objc2-foundation", + "objc2-metal", + "objc2-quartz-core", + "once_cell", + "ordered-float", + "parking_lot", + "portable-atomic", + "portable-atomic-util", + "profiling", + "range-alloc", + "raw-window-handle", + "raw-window-metal", + "renderdoc-sys", + "smallvec", + "thiserror", + "wasm-bindgen", + "wayland-sys", + "web-sys", + "wgpu-naga-bridge", + "wgpu-types", + "windows", + "windows-core", + "windows-result", +] + +[[package]] +name = "wgpu-naga-bridge" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c654c483f058800972c3645e95388a7eca31bf9fe1933bc20e036588a0be02" +dependencies = [ + "naga", + "wgpu-types", +] + +[[package]] +name = "wgpu-types" +version = "29.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9bcc31518a0e9735aefebedb5f7a9ef3ed1c42549c9f4c882fa9060ceaac639" +dependencies = [ + "bitflags", + "bytemuck", + "js-sys", + "log", + "raw-window-handle", + "web-sys", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "xml-rs" +version = "0.8.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" + +[[package]] +name = "zerocopy" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/fluxer_desktop/native/screen-frame-bus/Cargo.toml b/fluxer_desktop/native/screen-frame-bus/Cargo.toml new file mode 100644 index 0000000..ffc0386 --- /dev/null +++ b/fluxer_desktop/native/screen-frame-bus/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "fluxer_screen_frame_bus" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["rlib"] + +[features] +default = [] +wgpu = ["dep:wgpu", "fluxer_gpu_rebuild/wgpu"] + +[dependencies] +parking_lot = "0.12" +wgpu = { version = "29", optional = true } +fluxer_gpu_rebuild = { path = "../gpu-rebuild" } + +[dev-dependencies] +criterion = { version = "0.8", default-features = false, features = ["cargo_bench_support"] } + +[[bench]] +name = "staging" +harness = false + +[[bench]] +name = "frame_pool" +harness = false diff --git a/fluxer_desktop/native/screen-frame-bus/benches/baseline.json b/fluxer_desktop/native/screen-frame-bus/benches/baseline.json new file mode 100644 index 0000000..1c4c80c --- /dev/null +++ b/fluxer_desktop/native/screen-frame-bus/benches/baseline.json @@ -0,0 +1,25 @@ +{ + "measured_at": "85e057a273fd", + "host": "darwin-arm64-apple-silicon", + "regression_budget_percent": 5.0, + "criterion_args": { + "warm_up_time_sec": 2, + "measurement_time_sec": 5 + }, + "benches": { + "staging_pair_submit_map_cycle_1080p_rgba": { + "median_ns": 76243.0, + "low_ns": 72391.0, + "high_ns": 81080.0, + "budget_percent_override": 20.0, + "note": "1080p RGBA copy + map; observed cross-run sigma ~10% on a loaded dev host. Wider budget absorbs that without hiding real 1.3x slowdowns." + }, + "frame_pool/acquire_release/cpu_1080p_rgba": { + "median_ns": 11.874, + "low_ns": 11.811, + "high_ns": 11.941, + "budget_percent_override": 10.0, + "note": "~12ns op; tiny absolute deltas easily exceed 5% percent. 10% trades sensitivity for noise floor." + } + } +} diff --git a/fluxer_desktop/native/screen-frame-bus/benches/frame_pool.rs b/fluxer_desktop/native/screen-frame-bus/benches/frame_pool.rs new file mode 100644 index 0000000..8361834 --- /dev/null +++ b/fluxer_desktop/native/screen-frame-bus/benches/frame_pool.rs @@ -0,0 +1,26 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::hint::black_box; + +use criterion::{Criterion, criterion_group, criterion_main}; +use fluxer_screen_frame_bus::frame_pool::{CpuFrameBuilder, FRAME_BYTES_MAX}; + +fn bench_acquire_release_cpu_1080p_rgba(c: &mut Criterion) { + assert_eq!(FRAME_BYTES_MAX, 1920 * 1080 * 4); + let pool = CpuFrameBuilder::build_pool(FRAME_BYTES_MAX) + .expect("pool must allocate at construction time"); + assert!(pool.capacity() > 0); + + c.bench_function("frame_pool/acquire_release/cpu_1080p_rgba", |b| { + b.iter(|| { + let frame = pool + .try_acquire() + .expect("steady state single-threaded never starves"); + black_box(frame.slot_index()); + }); + }); + assert_eq!(pool.currently_in_flight(), 0); +} + +criterion_group!(benches, bench_acquire_release_cpu_1080p_rgba); +criterion_main!(benches); diff --git a/fluxer_desktop/native/screen-frame-bus/benches/staging.rs b/fluxer_desktop/native/screen-frame-bus/benches/staging.rs new file mode 100644 index 0000000..2ba3102 --- /dev/null +++ b/fluxer_desktop/native/screen-frame-bus/benches/staging.rs @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use criterion::{Criterion, black_box, criterion_group, criterion_main}; +use fluxer_screen_frame_bus::{CpuStagingBackend, StagingSurfacePair}; + +const BENCH_WIDTH: usize = 1920; +const BENCH_HEIGHT: usize = 1080; +const BENCH_BYTES_PER_PIXEL: usize = 4; +const BENCH_FRAME_LEN: usize = BENCH_WIDTH * BENCH_HEIGHT * BENCH_BYTES_PER_PIXEL; + +fn build_pair() -> StagingSurfacePair { + let backends = [ + CpuStagingBackend::new(BENCH_FRAME_LEN), + CpuStagingBackend::new(BENCH_FRAME_LEN), + ]; + assert_eq!(backends[0].len(), BENCH_FRAME_LEN); + assert_eq!(backends[1].len(), BENCH_FRAME_LEN); + StagingSurfacePair::new(backends) +} + +fn submit_map_cycle(c: &mut Criterion) { + let mut pair = build_pair(); + pair.submit(0, |buf| fill_pattern(buf, 0)) + .expect("warm submit zero"); + + let mut sequence: u64 = 1; + c.bench_function("staging_pair_submit_map_cycle_1080p_rgba", |b| { + b.iter(|| { + pair.submit(sequence, |buf| fill_pattern(buf, sequence as u8)) + .expect("submit ok in steady state"); + let mapped_sequence = sequence - 1; + let first_byte = pair + .try_map(mapped_sequence, |buf| buf[0]) + .expect("cpu backend is always ready"); + black_box(first_byte); + sequence = sequence.wrapping_add(1); + }); + }); +} + +fn fill_pattern(buf: &mut [u8], seed: u8) { + assert_eq!(buf.len(), BENCH_FRAME_LEN, "bench backend has fixed size"); + let chunk = seed.wrapping_add(1); + for byte in buf.iter_mut() { + *byte = chunk; + } +} + +criterion_group!(benches, submit_map_cycle); +criterion_main!(benches); diff --git a/fluxer_desktop/native/screen-frame-bus/src/frame_pool.rs b/fluxer_desktop/native/screen-frame-bus/src/frame_pool.rs new file mode 100644 index 0000000..5cdec82 --- /dev/null +++ b/fluxer_desktop/native/screen-frame-bus/src/frame_pool.rs @@ -0,0 +1,403 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use parking_lot::Mutex; +use std::sync::Arc; +use std::sync::atomic::{AtomicU64, Ordering}; + +#[cfg(target_os = "linux")] +use std::os::fd::RawFd; + +pub const FRAME_BYTES_MAX: usize = 1920 * 1080 * 4; + +pub const MAX_CPU_FRAMES: usize = 8; + +#[cfg(target_os = "macos")] +pub struct IoSurfaceHandle; + +#[cfg(target_os = "windows")] +#[derive(Debug)] +pub struct D3D11Handle(pub usize); + +#[cfg(target_os = "windows")] +unsafe impl Send for D3D11Handle {} +#[cfg(target_os = "windows")] +unsafe impl Sync for D3D11Handle {} + +pub enum Frame { + Cpu(Box<[u8]>), + #[cfg(feature = "wgpu")] + WgpuTexture { + texture: wgpu::Texture, + format: wgpu::TextureFormat, + dims: (u32, u32), + }, + #[cfg(target_os = "macos")] + IoSurface { + surface_ref: IoSurfaceHandle, + }, + #[cfg(target_os = "linux")] + Dmabuf { + fds: Vec, + format_modifier: u64, + }, + #[cfg(target_os = "windows")] + D3D11Shared { + handle: D3D11Handle, + key: u64, + }, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum FramePoolError { + ZeroCapacity, + CapacityOverflow, +} + +impl std::fmt::Display for FramePoolError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::ZeroCapacity => write!(f, "frame pool capacity must be greater than zero"), + Self::CapacityOverflow => write!(f, "frame pool capacity exceeds usize bounds"), + } + } +} + +impl std::error::Error for FramePoolError {} + +struct PoolInner { + slots: Vec>, + free: Mutex>, + capacity: usize, + acquired_total: AtomicU64, + skipped_total: AtomicU64, + currently_in_flight: AtomicU64, +} + +pub struct FramePool { + inner: Arc, +} + +impl FramePool { + pub fn from_slots(slots: Vec>) -> Result { + let capacity = slots.len(); + assert!(capacity == slots.len()); + if capacity == 0 { + return Err(FramePoolError::ZeroCapacity); + } + assert!(capacity > 0); + + let mut free = Vec::with_capacity(capacity); + for index in 0..capacity { + free.push(index); + } + assert_eq!(free.len(), capacity); + + let inner = PoolInner { + slots, + free: Mutex::new(free), + capacity, + acquired_total: AtomicU64::new(0), + skipped_total: AtomicU64::new(0), + currently_in_flight: AtomicU64::new(0), + }; + Ok(Self { + inner: Arc::new(inner), + }) + } + + pub fn try_acquire(&self) -> Option { + assert!(self.inner.capacity > 0); + + let mut free = self.inner.free.lock(); + assert!(free.len() <= self.inner.capacity); + let index = match free.pop() { + Some(idx) => idx, + None => { + drop(free); + self.inner.skipped_total.fetch_add(1, Ordering::Relaxed); + return None; + } + }; + assert!(index < self.inner.capacity); + self.inner.acquired_total.fetch_add(1, Ordering::Relaxed); + let after = self + .inner + .currently_in_flight + .fetch_add(1, Ordering::AcqRel) + + 1; + assert!(after as usize <= self.inner.capacity); + drop(free); + + let frame = Arc::clone(&self.inner.slots[index]); + Some(PooledFrame { + frame, + slot_index: index, + pool: Arc::clone(&self.inner), + }) + } + + pub fn capacity(&self) -> usize { + assert!(self.inner.capacity > 0); + let cap = self.inner.capacity; + assert!(cap == self.inner.slots.len()); + cap + } + + pub fn acquired_total(&self) -> u64 { + assert!(self.inner.capacity > 0); + let total = self.inner.acquired_total.load(Ordering::Relaxed); + assert!(total >= self.inner.currently_in_flight.load(Ordering::Relaxed)); + total + } + + pub fn skipped_total(&self) -> u64 { + assert!(self.inner.capacity > 0); + let in_flight = self.inner.currently_in_flight.load(Ordering::Relaxed); + assert!(in_flight as usize <= self.inner.capacity); + self.inner.skipped_total.load(Ordering::Relaxed) + } + + pub fn currently_in_flight(&self) -> u64 { + let in_flight = self.inner.currently_in_flight.load(Ordering::Acquire); + assert!(in_flight as usize <= self.inner.capacity); + assert!(in_flight <= self.inner.acquired_total.load(Ordering::Relaxed)); + in_flight + } +} + +pub struct PooledFrame { + frame: Arc, + slot_index: usize, + pool: Arc, +} + +impl PooledFrame { + pub fn frame(&self) -> &Arc { + assert!(self.slot_index < self.pool.capacity); + assert!(Arc::strong_count(&self.frame) >= 2); + &self.frame + } + + pub fn slot_index(&self) -> usize { + assert!(self.slot_index < self.pool.capacity); + let idx = self.slot_index; + assert!(idx < self.pool.slots.len()); + idx + } +} + +impl Drop for PooledFrame { + fn drop(&mut self) { + assert!(self.slot_index < self.pool.capacity); + + let mut free = self.pool.free.lock(); + assert!(free.len() < self.pool.capacity); + let before = self.pool.currently_in_flight.load(Ordering::Acquire); + assert!(before >= 1); + assert!(before as usize <= self.pool.capacity); + free.push(self.slot_index); + let after = self.pool.currently_in_flight.fetch_sub(1, Ordering::AcqRel) - 1; + assert!(after as usize <= self.pool.capacity); + drop(free); + } +} + +pub struct CpuFrameBuilder; + +impl CpuFrameBuilder { + pub fn build_pool(bytes_per_slot: usize) -> Result { + assert!(bytes_per_slot > 0); + assert!(bytes_per_slot <= FRAME_BYTES_MAX); + if bytes_per_slot == 0 { + return Err(FramePoolError::ZeroCapacity); + } + + let mut slots: Vec> = Vec::with_capacity(MAX_CPU_FRAMES); + for _ in 0..MAX_CPU_FRAMES { + let buf: Box<[u8]> = vec![0u8; bytes_per_slot].into_boxed_slice(); + assert_eq!(buf.len(), bytes_per_slot); + slots.push(Arc::new(Frame::Cpu(buf))); + } + assert_eq!(slots.len(), MAX_CPU_FRAMES); + FramePool::from_slots(slots) + } + + pub fn build_pool_with_capacity( + capacity: usize, + bytes_per_slot: usize, + ) -> Result { + assert!(bytes_per_slot <= FRAME_BYTES_MAX); + if capacity == 0 { + return Err(FramePoolError::ZeroCapacity); + } + assert!(capacity > 0); + let mut slots: Vec> = Vec::with_capacity(capacity); + for _ in 0..capacity { + let buf: Box<[u8]> = vec![0u8; bytes_per_slot].into_boxed_slice(); + assert_eq!(buf.len(), bytes_per_slot); + slots.push(Arc::new(Frame::Cpu(buf))); + } + assert_eq!(slots.len(), capacity); + FramePool::from_slots(slots) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::Barrier; + use std::sync::atomic::AtomicUsize; + use std::thread; + use std::time::{Duration, Instant}; + + const SMALL_SLOT_BYTES: usize = 64; + + fn small_pool(capacity: usize) -> FramePool { + CpuFrameBuilder::build_pool_with_capacity(capacity, SMALL_SLOT_BYTES) + .expect("non-zero capacity") + } + + #[test] + fn acquire_up_to_capacity_then_returns_none() { + let pool = small_pool(4); + let mut held = Vec::new(); + for expected in 0..4 { + let frame = pool.try_acquire().expect("slot must be available"); + assert_eq!(pool.currently_in_flight() as usize, expected + 1); + held.push(frame); + } + assert_eq!(pool.acquired_total(), 4); + assert!(pool.try_acquire().is_none()); + assert_eq!(pool.skipped_total(), 1); + assert_eq!(pool.currently_in_flight(), 4); + } + + #[test] + fn drop_releases_slot_back_to_pool() { + let pool = small_pool(2); + let first = pool.try_acquire().expect("first slot"); + let second = pool.try_acquire().expect("second slot"); + assert!(pool.try_acquire().is_none()); + drop(first); + let revived = pool.try_acquire().expect("released slot returns"); + assert_eq!(pool.currently_in_flight(), 2); + drop(second); + drop(revived); + assert_eq!(pool.currently_in_flight(), 0); + } + + #[test] + fn zero_capacity_pool_always_returns_none() { + match CpuFrameBuilder::build_pool_with_capacity(0, SMALL_SLOT_BYTES) { + Err(err) => assert_eq!(err, FramePoolError::ZeroCapacity), + Ok(_) => panic!("zero capacity must be rejected"), + } + match FramePool::from_slots(Vec::new()) { + Err(err) => assert_eq!(err, FramePoolError::ZeroCapacity), + Ok(_) => panic!("empty slot vec must be rejected"), + } + } + + #[test] + fn held_frame_has_strong_count_two_then_one() { + let pool = small_pool(1); + let held = pool.try_acquire().expect("slot must be available"); + assert_eq!(Arc::strong_count(held.frame()), 2); + drop(held); + let again = pool.try_acquire().expect("slot returned"); + assert_eq!(Arc::strong_count(again.frame()), 2); + drop(again); + } + + #[test] + fn multi_thread_acquire_release_stress_does_not_deadlock() { + const THREADS: usize = 10; + const OPS_PER_THREAD: usize = 1000; + const POOL_CAPACITY: usize = 4; + + let pool = Arc::new(small_pool(POOL_CAPACITY)); + let barrier = Arc::new(Barrier::new(THREADS)); + let acquired_observed = Arc::new(AtomicUsize::new(0)); + let skipped_observed = Arc::new(AtomicUsize::new(0)); + + let mut handles = Vec::with_capacity(THREADS); + for _ in 0..THREADS { + let pool = Arc::clone(&pool); + let barrier = Arc::clone(&barrier); + let acquired_observed = Arc::clone(&acquired_observed); + let skipped_observed = Arc::clone(&skipped_observed); + handles.push(thread::spawn(move || { + barrier.wait(); + let mut local_acquired: usize = 0; + let mut local_skipped: usize = 0; + for _ in 0..OPS_PER_THREAD { + match pool.try_acquire() { + Some(frame) => { + local_acquired += 1; + drop(frame); + } + None => { + local_skipped += 1; + } + } + } + acquired_observed.fetch_add(local_acquired, Ordering::Relaxed); + skipped_observed.fetch_add(local_skipped, Ordering::Relaxed); + })); + } + + let deadline = Instant::now() + Duration::from_secs(30); + for handle in handles { + assert!( + Instant::now() < deadline, + "stress test exceeded 30s budget — likely deadlock" + ); + handle.join().expect("worker panicked"); + } + + let total = pool.acquired_total() + pool.skipped_total(); + assert_eq!(total as usize, THREADS * OPS_PER_THREAD); + assert_eq!( + pool.acquired_total() as usize, + acquired_observed.load(Ordering::Relaxed) + ); + assert_eq!( + pool.skipped_total() as usize, + skipped_observed.load(Ordering::Relaxed) + ); + assert_eq!(pool.currently_in_flight(), 0); + } + + #[test] + fn skipped_counter_increments_only_on_empty_pool() { + let pool = small_pool(1); + let held = pool.try_acquire().expect("first slot"); + assert!(pool.try_acquire().is_none()); + assert!(pool.try_acquire().is_none()); + assert_eq!(pool.skipped_total(), 2); + assert_eq!(pool.acquired_total(), 1); + drop(held); + let _again = pool.try_acquire().expect("slot returned"); + assert_eq!(pool.acquired_total(), 2); + assert_eq!(pool.skipped_total(), 2); + } + + #[test] + fn cpu_frame_builder_default_capacity_allocates_max_slots() { + let pool = + CpuFrameBuilder::build_pool(SMALL_SLOT_BYTES).expect("default builder must succeed"); + assert_eq!(pool.capacity(), MAX_CPU_FRAMES); + let mut held = Vec::with_capacity(MAX_CPU_FRAMES); + for _ in 0..MAX_CPU_FRAMES { + held.push(pool.try_acquire().expect("slot in capacity")); + } + assert!(pool.try_acquire().is_none()); + for frame in &held { + match frame.frame().as_ref() { + Frame::Cpu(buf) => assert_eq!(buf.len(), SMALL_SLOT_BYTES), + #[allow(unreachable_patterns)] + _ => panic!("CpuFrameBuilder must emit Frame::Cpu variants"), + } + } + } +} diff --git a/fluxer_desktop/native/screen-frame-bus/src/gpu_loss.rs b/fluxer_desktop/native/screen-frame-bus/src/gpu_loss.rs new file mode 100644 index 0000000..0d50f24 --- /dev/null +++ b/fluxer_desktop/native/screen-frame-bus/src/gpu_loss.rs @@ -0,0 +1,429 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::StagingBackend; +use fluxer_gpu_rebuild::{GpuLossCallback, GpuRebuildError}; + +pub const MIN_STAGING_BYTES: u64 = 1; +pub const MAX_STAGING_BYTES: u64 = 1 << 30; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct WgpuStagingConfig { + pub byte_len: u64, +} + +impl WgpuStagingConfig { + pub fn new(byte_len: u64) -> Self { + assert!(byte_len >= MIN_STAGING_BYTES, "byte_len must be positive"); + assert!(byte_len <= MAX_STAGING_BYTES, "byte_len exceeds sanity cap"); + Self { byte_len } + } +} + +struct WgpuStagingResources { + buffer: wgpu::Buffer, + cpu_mirror: Vec, + ready: bool, +} + +pub struct WgpuStagingBackend { + config: WgpuStagingConfig, + resources: Option, +} + +impl WgpuStagingBackend { + pub fn new(device: &wgpu::Device, config: WgpuStagingConfig) -> Self { + assert!(config.byte_len >= MIN_STAGING_BYTES, "config min invariant"); + assert!(config.byte_len <= MAX_STAGING_BYTES, "config max invariant"); + let resources = build_resources(device, config); + Self { + config, + resources: Some(resources), + } + } + + pub fn new_unbuilt(config: WgpuStagingConfig) -> Self { + assert!(config.byte_len >= MIN_STAGING_BYTES, "config min invariant"); + assert!(config.byte_len <= MAX_STAGING_BYTES, "config max invariant"); + Self { + config, + resources: None, + } + } + + pub fn config(&self) -> WgpuStagingConfig { + assert!( + self.config.byte_len >= MIN_STAGING_BYTES, + "config min invariant" + ); + assert!( + self.config.byte_len <= MAX_STAGING_BYTES, + "config max invariant" + ); + self.config + } + + pub fn is_built(&self) -> bool { + let built = self.resources.is_some(); + assert!( + self.config.byte_len >= MIN_STAGING_BYTES, + "config min while introspecting" + ); + assert!( + self.config.byte_len <= MAX_STAGING_BYTES, + "config max while introspecting" + ); + built + } + + pub fn buffer(&self) -> Option<&wgpu::Buffer> { + let buf = self.resources.as_ref().map(|r| &r.buffer); + assert_eq!( + buf.is_some(), + self.is_built(), + "buffer presence must align with built state", + ); + buf + } +} + +impl StagingBackend for WgpuStagingBackend { + fn write(&mut self, fill: F) { + let Some(resources) = self.resources.as_mut() else { + return; + }; + fill(&mut resources.cpu_mirror); + resources.ready = true; + } + + fn read R>(&self, read: F) -> R { + let empty: &[u8] = &[]; + match self.resources.as_ref() { + Some(r) => read(&r.cpu_mirror), + None => read(empty), + } + } + + fn is_ready(&self) -> bool { + match self.resources.as_ref() { + Some(r) => r.ready, + None => false, + } + } + + fn is_idle(&self) -> bool { + match self.resources.as_ref() { + Some(r) => !r.ready, + None => true, + } + } +} + +impl GpuLossCallback for WgpuStagingBackend { + fn release(&mut self) { + assert!( + self.config.byte_len >= MIN_STAGING_BYTES, + "release config min invariant" + ); + assert!( + self.config.byte_len <= MAX_STAGING_BYTES, + "release config max invariant" + ); + self.resources = None; + assert!(!self.is_built(), "release postcondition: must be unbuilt"); + } + + fn rebuild( + &mut self, + device: &wgpu::Device, + _queue: &wgpu::Queue, + ) -> Result<(), GpuRebuildError> { + assert!( + self.config.byte_len >= MIN_STAGING_BYTES, + "rebuild config min invariant" + ); + assert!( + self.config.byte_len <= MAX_STAGING_BYTES, + "rebuild config max invariant" + ); + if self.resources.is_some() { + return Err(GpuRebuildError::OwnerInvariantBroken { + reason: "rebuild without prior release", + }); + } + let resources = build_resources(device, self.config); + self.resources = Some(resources); + assert!(self.is_built(), "rebuild postcondition: must be built"); + Ok(()) + } + + fn is_ready(&self) -> bool { + self.is_built() + } + + fn debug_label(&self) -> &'static str { + "screen_frame_bus.wgpu_staging_backend" + } +} + +fn build_resources(device: &wgpu::Device, config: WgpuStagingConfig) -> WgpuStagingResources { + assert!( + config.byte_len >= MIN_STAGING_BYTES, + "build_resources min invariant" + ); + assert!( + config.byte_len <= MAX_STAGING_BYTES, + "build_resources max invariant" + ); + let buffer = device.create_buffer(&wgpu::BufferDescriptor { + label: Some("screen_frame_bus.wgpu_staging_buffer"), + size: config.byte_len, + usage: wgpu::BufferUsages::MAP_READ | wgpu::BufferUsages::COPY_DST, + mapped_at_creation: false, + }); + let cpu_mirror = vec![0u8; config.byte_len as usize]; + assert_eq!( + cpu_mirror.len() as u64, + config.byte_len, + "cpu mirror must match configured byte len", + ); + WgpuStagingResources { + buffer, + cpu_mirror, + ready: false, + } +} + +pub fn try_acquire_device() -> Option<(wgpu::Device, wgpu::Queue, wgpu::Instance)> { + let mut descriptor = wgpu::InstanceDescriptor::new_without_display_handle(); + descriptor.backends = wgpu::Backends::all() | wgpu::Backends::SECONDARY; + let instance = wgpu::Instance::new(descriptor); + let adapter = pollster_block_on(instance.request_adapter(&wgpu::RequestAdapterOptions { + power_preference: wgpu::PowerPreference::default(), + force_fallback_adapter: false, + compatible_surface: None, + })) + .ok()?; + let device_result = pollster_block_on(adapter.request_device(&wgpu::DeviceDescriptor { + label: Some("screen_frame_bus.wgpu_staging_backend.test_device"), + required_features: wgpu::Features::empty(), + required_limits: wgpu::Limits::default(), + memory_hints: wgpu::MemoryHints::default(), + trace: wgpu::Trace::Off, + experimental_features: wgpu::ExperimentalFeatures::default(), + })); + match device_result { + Ok((device, queue)) => Some((device, queue, instance)), + Err(_) => None, + } +} + +fn pollster_block_on(fut: F) -> F::Output { + futures_executor_block_on(fut) +} + +fn futures_executor_block_on(mut fut: F) -> F::Output { + use core::pin::Pin; + use core::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; + fn raw_waker() -> RawWaker { + fn no_op(_: *const ()) {} + fn clone(_: *const ()) -> RawWaker { + raw_waker() + } + static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, no_op, no_op, no_op); + RawWaker::new(core::ptr::null(), &VTABLE) + } + let waker = unsafe { Waker::from_raw(raw_waker()) }; + let mut cx = Context::from_waker(&waker); + let mut fut = unsafe { Pin::new_unchecked(&mut fut) }; + loop { + match fut.as_mut().poll(&mut cx) { + Poll::Ready(out) => return out, + Poll::Pending => continue, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::{CpuStagingBackend, STAGING_PAIR_LEN, StagingSurfacePair}; + use fluxer_gpu_rebuild::{GpuLossRegistry, RebuildOutcome}; + + struct GpuCtx { + device: wgpu::Device, + queue: wgpu::Queue, + _instance: wgpu::Instance, + } + + fn gpu_ctx() -> Option { + let acquired = std::panic::catch_unwind(std::panic::AssertUnwindSafe(try_acquire_device)); + let (device, queue, instance) = match acquired { + Ok(Some(triple)) => triple, + Ok(None) => return None, + Err(_) => return None, + }; + Some(GpuCtx { + device, + queue, + _instance: instance, + }) + } + + #[test] + fn staging_backend_is_ready_only_when_built() { + let Some(ctx) = gpu_ctx() else { + eprintln!("screen-frame-bus gpu_loss: no wgpu adapter, skipping"); + return; + }; + let cfg = WgpuStagingConfig::new(256); + let mut backend = WgpuStagingBackend::new(&ctx.device, cfg); + assert!( + GpuLossCallback::is_ready(&backend), + "freshly built backend must be ready", + ); + backend.release(); + assert!( + !GpuLossCallback::is_ready(&backend), + "released backend must not be ready", + ); + let outcome = backend.rebuild(&ctx.device, &ctx.queue); + assert!(outcome.is_ok(), "rebuild on fresh device must succeed"); + assert!( + GpuLossCallback::is_ready(&backend), + "rebuilt backend must be ready", + ); + } + + #[test] + fn registry_handles_staging_backend_round_trip() { + let Some(ctx) = gpu_ctx() else { + eprintln!("screen-frame-bus gpu_loss: no wgpu adapter, skipping"); + return; + }; + let registry = GpuLossRegistry::new(); + let backend = Box::new(WgpuStagingBackend::new( + &ctx.device, + WgpuStagingConfig::new(128), + )); + let _guard = registry.register(backend); + let report = registry.handle_device_lost(&ctx.device, &ctx.queue); + assert_eq!(report.released_count, 1); + assert_eq!(report.rebuilt_count, 1); + assert_eq!(report.failed_count, 0); + assert!(report.is_total_success()); + } + + #[test] + fn double_rebuild_without_release_is_owner_invariant_error() { + let Some(ctx) = gpu_ctx() else { + eprintln!("screen-frame-bus gpu_loss: no wgpu adapter, skipping"); + return; + }; + let mut backend = WgpuStagingBackend::new(&ctx.device, WgpuStagingConfig::new(64)); + let outcome = backend.rebuild(&ctx.device, &ctx.queue); + assert!(matches!( + outcome, + Err(GpuRebuildError::OwnerInvariantBroken { .. }) + )); + } + + #[test] + fn write_between_release_and_rebuild_is_a_noop_and_not_ready() { + let Some(ctx) = gpu_ctx() else { + eprintln!("screen-frame-bus gpu_loss: no wgpu adapter, skipping"); + return; + }; + let mut backend = WgpuStagingBackend::new(&ctx.device, WgpuStagingConfig::new(64)); + backend.release(); + backend.write(|buf| buf.fill(0xAA)); + assert!( + !::is_ready(&backend), + "write before rebuild must not flip ready", + ); + let observed = backend.read(|buf| buf.len()); + assert_eq!(observed, 0, "read before rebuild must observe empty mirror"); + } + + #[test] + fn surface_pair_with_wgpu_backend_round_trips_cpu_mirror() { + let Some(ctx) = gpu_ctx() else { + eprintln!("screen-frame-bus gpu_loss: no wgpu adapter, skipping"); + return; + }; + let cfg = WgpuStagingConfig::new(64); + let a = WgpuStagingBackend::new(&ctx.device, cfg); + let b = WgpuStagingBackend::new(&ctx.device, cfg); + assert_eq!( + STAGING_PAIR_LEN, 2, + "OBS staging pair is exactly two surfaces" + ); + let mut pair: StagingSurfacePair = StagingSurfacePair::new([a, b]); + pair.submit(0, |buf| { + buf[0] = 0xDE; + buf[1] = 0xAD; + }) + .expect("submit zero must succeed"); + pair.submit(1, |buf| { + buf[0] = 0xBE; + buf[1] = 0xEF; + }) + .expect("submit one must succeed"); + let first = pair + .try_map(0, |buf| (buf[0], buf[1])) + .expect("map zero ready"); + assert_eq!(first, (0xDE, 0xAD)); + let second = pair + .try_map(1, |buf| (buf[0], buf[1])) + .expect("map one ready"); + assert_eq!(second, (0xBE, 0xEF)); + } + + #[test] + fn registry_holds_mixed_packers_and_staging_backends() { + let Some(ctx) = gpu_ctx() else { + eprintln!("screen-frame-bus gpu_loss: no wgpu adapter, skipping"); + return; + }; + let registry = GpuLossRegistry::new(); + let mut guards = Vec::new(); + for i in 0..4u32 { + let bytes = 32u64 << (i % 4); + let backend = Box::new(WgpuStagingBackend::new( + &ctx.device, + WgpuStagingConfig::new(bytes), + )); + guards.push(registry.register(backend)); + } + let report = registry.handle_device_lost(&ctx.device, &ctx.queue); + assert_eq!(report.released_count, 4); + assert_eq!(report.rebuilt_count, 4); + assert_eq!(report.failed_count, 0); + for outcome in &report.outcomes { + assert!(matches!(outcome, RebuildOutcome::Rebuilt { .. })); + } + drop(guards); + } + + #[test] + fn cpu_backend_still_works_alongside_wgpu_backend() { + let cpu = CpuStagingBackend::new(64); + assert!(::is_idle(&cpu)); + assert!(::is_ready(&cpu)); + } + + #[test] + fn buffer_handle_disappears_after_release_and_reappears_after_rebuild() { + let Some(ctx) = gpu_ctx() else { + eprintln!("screen-frame-bus gpu_loss: no wgpu adapter, skipping"); + return; + }; + let mut backend = WgpuStagingBackend::new(&ctx.device, WgpuStagingConfig::new(128)); + assert!(backend.buffer().is_some(), "buffer present after build"); + backend.release(); + assert!(backend.buffer().is_none(), "buffer absent after release"); + backend + .rebuild(&ctx.device, &ctx.queue) + .expect("rebuild must succeed"); + assert!(backend.buffer().is_some(), "buffer present after rebuild"); + } +} diff --git a/fluxer_desktop/native/screen-frame-bus/src/lib.rs b/fluxer_desktop/native/screen-frame-bus/src/lib.rs new file mode 100644 index 0000000..e18d442 --- /dev/null +++ b/fluxer_desktop/native/screen-frame-bus/src/lib.rs @@ -0,0 +1,1222 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub mod frame_pool; +#[cfg(feature = "wgpu")] +pub mod gpu_loss; + +use parking_lot::RwLock; +use std::collections::HashMap; +use std::ffi::c_void; +use std::sync::Arc; +use std::sync::OnceLock; + +use crate::frame_pool::PooledFrame; + +#[cfg(target_os = "linux")] +use std::os::fd::OwnedFd; + +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct DmabufDesc { + pub plane_count: u8, + pub width: u32, + pub height: u32, + pub drm_format: u32, + pub modifier: u64, + pub strides: [u32; 4], + pub offsets: [u32; 4], + pub device_uuid: [u8; 16], + pub timestamp_us: i64, +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct SharedTextureDesc { + pub handle: u64, + pub width: u32, + pub height: u32, + pub dxgi_format: u32, + pub timestamp_us: i64, +} + +pub const FRAME_DATA_BYTES_CAP: usize = 1 << 30; + +pub trait SharedFrameBytes: Send + Sync { + fn bytes(&self) -> &[u8]; +} + +enum FrameDataRepr { + Owned(Vec), + Shared { + source: Arc, + len: usize, + _capacity_token: Option, + }, +} + +pub struct FrameData { + repr: FrameDataRepr, +} + +impl FrameData { + pub fn from_owned(data: Vec) -> Self { + assert!(data.len() <= FRAME_DATA_BYTES_CAP); + Self { + repr: FrameDataRepr::Owned(data), + } + } + + pub fn from_shared( + source: Arc, + len: usize, + capacity_token: Option, + ) -> Self { + assert!(len <= FRAME_DATA_BYTES_CAP); + assert!(len <= source.bytes().len()); + Self { + repr: FrameDataRepr::Shared { + source, + len, + _capacity_token: capacity_token, + }, + } + } + + pub fn as_slice(&self) -> &[u8] { + match &self.repr { + FrameDataRepr::Owned(data) => data.as_slice(), + FrameDataRepr::Shared { source, len, .. } => { + let bytes = source.bytes(); + assert!(*len <= bytes.len()); + &bytes[..*len] + } + } + } + + pub fn len(&self) -> usize { + let len = match &self.repr { + FrameDataRepr::Owned(data) => data.len(), + FrameDataRepr::Shared { len, .. } => *len, + }; + assert!(len <= FRAME_DATA_BYTES_CAP); + len + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn is_shared(&self) -> bool { + matches!(self.repr, FrameDataRepr::Shared { .. }) + } + + pub fn into_vec(self) -> Vec { + match self.repr { + FrameDataRepr::Owned(data) => data, + FrameDataRepr::Shared { source, len, .. } => { + let bytes = source.bytes(); + assert!(len <= bytes.len()); + bytes[..len].to_vec() + } + } + } +} + +impl From> for FrameData { + fn from(data: Vec) -> Self { + Self::from_owned(data) + } +} + +impl std::ops::Deref for FrameData { + type Target = [u8]; + + fn deref(&self) -> &[u8] { + self.as_slice() + } +} + +pub struct Nv12Frame { + pub data: FrameData, + pub width: u32, + pub height: u32, + pub stride_y: u32, + pub stride_uv: u32, + pub timestamp_us: i64, +} + +pub struct BgraFrame { + pub data: Vec, + pub width: u32, + pub height: u32, + pub stride: u32, + pub timestamp_us: i64, +} + +#[cfg(target_os = "macos")] +pub struct MacCvPixelBufferFrame { + pixel_buffer: *mut c_void, + pub width: u32, + pub height: u32, + pub pixel_format: u32, + pub timestamp_us: i64, +} + +#[cfg(target_os = "macos")] +unsafe impl Send for MacCvPixelBufferFrame {} +#[cfg(target_os = "macos")] +unsafe impl Sync for MacCvPixelBufferFrame {} + +#[cfg(target_os = "macos")] +impl MacCvPixelBufferFrame { + pub unsafe fn from_retained( + pixel_buffer: *mut c_void, + width: u32, + height: u32, + pixel_format: u32, + timestamp_us: i64, + ) -> Self { + Self { + pixel_buffer, + width, + height, + pixel_format, + timestamp_us, + } + } + + pub fn pixel_buffer_ptr(&self) -> *mut c_void { + self.pixel_buffer + } + + pub fn into_raw_pixel_buffer(mut self) -> *mut c_void { + let pb = self.pixel_buffer; + self.pixel_buffer = std::ptr::null_mut(); + std::mem::forget(self); + pb + } +} + +#[cfg(target_os = "macos")] +impl Drop for MacCvPixelBufferFrame { + fn drop(&mut self) { + if !self.pixel_buffer.is_null() { + unsafe { CFRelease(self.pixel_buffer as *const c_void) }; + self.pixel_buffer = std::ptr::null_mut(); + } + } +} + +#[cfg(target_os = "macos")] +#[link(name = "CoreFoundation", kind = "framework")] +unsafe extern "C" { + fn CFRelease(cf: *const c_void); +} + +#[cfg(target_os = "linux")] +pub struct DmabufFrame { + pub desc: DmabufDesc, + pub fds: Vec, +} + +pub enum ScreenFrame { + Nv12(Nv12Frame), + Bgra(BgraFrame), + #[cfg(target_os = "macos")] + MacCvPixelBuffer(MacCvPixelBufferFrame), + #[cfg(target_os = "linux")] + Dmabuf(DmabufFrame), + #[cfg(target_os = "windows")] + SharedTexture(SharedTextureDesc), +} + +impl ScreenFrame { + pub fn timestamp_us(&self) -> i64 { + match self { + Self::Nv12(f) => f.timestamp_us, + Self::Bgra(f) => f.timestamp_us, + #[cfg(target_os = "macos")] + Self::MacCvPixelBuffer(f) => f.timestamp_us, + #[cfg(target_os = "linux")] + Self::Dmabuf(f) => f.desc.timestamp_us, + #[cfg(target_os = "windows")] + Self::SharedTexture(d) => d.timestamp_us, + } + } + + pub fn dimensions(&self) -> (u32, u32) { + match self { + Self::Nv12(f) => (f.width, f.height), + Self::Bgra(f) => (f.width, f.height), + #[cfg(target_os = "macos")] + Self::MacCvPixelBuffer(f) => (f.width, f.height), + #[cfg(target_os = "linux")] + Self::Dmabuf(f) => (f.desc.width, f.desc.height), + #[cfg(target_os = "windows")] + Self::SharedTexture(d) => (d.width, d.height), + } + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum EnqueueOutcome { + Accepted, + Coalesced, + Rejected, +} + +pub trait ScreenFrameSink: Send + Sync { + fn enqueue(&self, frame: ScreenFrame) -> EnqueueOutcome; +} + +pub const NATIVE_SCREEN_FRAME_SINK_HANDLE_MAGIC: u32 = u32::from_be_bytes(*b"FXSH"); +pub const NATIVE_SCREEN_FRAME_SINK_HANDLE_VERSION: u32 = 2; + +pub const NATIVE_SCREEN_FRAME_SINK_ACCEPTED: u32 = 1; +pub const NATIVE_SCREEN_FRAME_SINK_COALESCED: u32 = 2; +pub const NATIVE_SCREEN_FRAME_SINK_REJECTED: u32 = 3; + +pub type NativeScreenFrameSinkRetainFn = unsafe extern "C" fn(context: *const c_void); +pub type NativeScreenFrameSinkReleaseFn = unsafe extern "C" fn(context: *const c_void); +pub type NativeScreenFrameSinkEnqueueNv12Fn = unsafe extern "C" fn( + context: *const c_void, + data: *const u8, + data_len: usize, + width: u32, + height: u32, + stride_y: u32, + stride_uv: u32, + timestamp_us: i64, +) -> u32; +pub type NativeScreenFrameSinkEnqueueBgraFn = unsafe extern "C" fn( + context: *const c_void, + data: *const u8, + data_len: usize, + width: u32, + height: u32, + stride: u32, + timestamp_us: i64, +) -> u32; +pub type NativeScreenFrameSinkEnqueueMacCvPixelBufferFn = unsafe extern "C" fn( + context: *const c_void, + pixel_buffer: *mut c_void, + width: u32, + height: u32, + pixel_format: u32, + timestamp_us: i64, +) -> u32; +pub type NativeScreenFrameSinkEnqueueDmabufFn = unsafe extern "C" fn( + context: *const c_void, + desc: DmabufDesc, + fds: *const i32, + fd_count: usize, +) -> u32; +pub type NativeScreenFrameSinkEnqueueSharedTextureFn = + unsafe extern "C" fn(context: *const c_void, desc: SharedTextureDesc) -> u32; +pub type NativeScreenFrameSinkEnqueueScreenAudioFn = unsafe extern "C" fn( + context: *const c_void, + samples: *const f32, + num_frames: u32, + channels: u32, + sample_rate_hz: u32, + timestamp_us: i64, +) -> u32; + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct NativeScreenFrameSinkHandle { + pub magic: u32, + pub version: u32, + pub context: *const c_void, + pub retain: NativeScreenFrameSinkRetainFn, + pub release: NativeScreenFrameSinkReleaseFn, + pub enqueue_nv12: Option, + pub enqueue_bgra: Option, + pub enqueue_mac_cv_pixel_buffer: Option, + pub enqueue_dmabuf: Option, + pub enqueue_shared_texture: Option, + pub enqueue_screen_audio: Option, +} + +unsafe impl Send for NativeScreenFrameSinkHandle {} +unsafe impl Sync for NativeScreenFrameSinkHandle {} + +impl NativeScreenFrameSinkHandle { + pub fn is_valid(&self) -> bool { + self.magic == NATIVE_SCREEN_FRAME_SINK_HANDLE_MAGIC + && self.version == NATIVE_SCREEN_FRAME_SINK_HANDLE_VERSION + && !self.context.is_null() + } + + pub unsafe fn retain_ref(&self) -> Option { + if !self.is_valid() { + return None; + } + unsafe { (self.retain)(self.context) }; + Some(NativeScreenFrameSinkHandleRef { handle: *self }) + } + + pub unsafe fn retain_from_raw(raw: *const Self) -> Option { + if raw.is_null() { + return None; + } + unsafe { (*raw).retain_ref() } + } + + pub fn native_outcome(outcome: EnqueueOutcome) -> u32 { + match outcome { + EnqueueOutcome::Accepted => NATIVE_SCREEN_FRAME_SINK_ACCEPTED, + EnqueueOutcome::Coalesced => NATIVE_SCREEN_FRAME_SINK_COALESCED, + EnqueueOutcome::Rejected => NATIVE_SCREEN_FRAME_SINK_REJECTED, + } + } + + pub fn outcome_from_native(value: u32) -> EnqueueOutcome { + match value { + NATIVE_SCREEN_FRAME_SINK_ACCEPTED => EnqueueOutcome::Accepted, + NATIVE_SCREEN_FRAME_SINK_COALESCED => EnqueueOutcome::Coalesced, + _ => EnqueueOutcome::Rejected, + } + } +} + +pub struct NativeScreenFrameSinkHandleRef { + handle: NativeScreenFrameSinkHandle, +} + +unsafe impl Send for NativeScreenFrameSinkHandleRef {} +unsafe impl Sync for NativeScreenFrameSinkHandleRef {} + +impl NativeScreenFrameSinkHandleRef { + pub fn handle(&self) -> &NativeScreenFrameSinkHandle { + &self.handle + } + + pub fn enqueue_nv12_copy( + &self, + data: &[u8], + width: u32, + height: u32, + stride_y: u32, + stride_uv: u32, + timestamp_us: i64, + ) -> EnqueueOutcome { + let Some(enqueue) = self.handle.enqueue_nv12 else { + return EnqueueOutcome::Rejected; + }; + NativeScreenFrameSinkHandle::outcome_from_native(unsafe { + enqueue( + self.handle.context, + data.as_ptr(), + data.len(), + width, + height, + stride_y, + stride_uv, + timestamp_us, + ) + }) + } + + pub fn enqueue_bgra_copy( + &self, + data: &[u8], + width: u32, + height: u32, + stride: u32, + timestamp_us: i64, + ) -> EnqueueOutcome { + let Some(enqueue) = self.handle.enqueue_bgra else { + return EnqueueOutcome::Rejected; + }; + NativeScreenFrameSinkHandle::outcome_from_native(unsafe { + enqueue( + self.handle.context, + data.as_ptr(), + data.len(), + width, + height, + stride, + timestamp_us, + ) + }) + } + + #[cfg(target_os = "macos")] + pub unsafe fn enqueue_mac_cv_pixel_buffer( + &self, + pixel_buffer: *mut c_void, + width: u32, + height: u32, + pixel_format: u32, + timestamp_us: i64, + ) -> EnqueueOutcome { + let Some(enqueue) = self.handle.enqueue_mac_cv_pixel_buffer else { + return EnqueueOutcome::Rejected; + }; + NativeScreenFrameSinkHandle::outcome_from_native(unsafe { + enqueue( + self.handle.context, + pixel_buffer, + width, + height, + pixel_format, + timestamp_us, + ) + }) + } + + #[cfg(target_os = "linux")] + pub unsafe fn enqueue_dmabuf_take_fds(&self, desc: DmabufDesc, fds: &[i32]) -> EnqueueOutcome { + let Some(enqueue) = self.handle.enqueue_dmabuf else { + return EnqueueOutcome::Rejected; + }; + NativeScreenFrameSinkHandle::outcome_from_native(unsafe { + enqueue(self.handle.context, desc, fds.as_ptr(), fds.len()) + }) + } + + #[cfg(target_os = "windows")] + pub fn enqueue_shared_texture(&self, desc: SharedTextureDesc) -> EnqueueOutcome { + let Some(enqueue) = self.handle.enqueue_shared_texture else { + return EnqueueOutcome::Rejected; + }; + NativeScreenFrameSinkHandle::outcome_from_native(unsafe { + enqueue(self.handle.context, desc) + }) + } + + pub fn supports_screen_audio(&self) -> bool { + self.handle.enqueue_screen_audio.is_some() + } + + pub fn enqueue_screen_audio_f32( + &self, + samples: &[f32], + num_frames: u32, + channels: u32, + sample_rate_hz: u32, + timestamp_us: i64, + ) -> EnqueueOutcome { + let Some(enqueue) = self.handle.enqueue_screen_audio else { + return EnqueueOutcome::Rejected; + }; + if channels == 0 || num_frames == 0 { + return EnqueueOutcome::Rejected; + } + let expected_samples = (num_frames as usize).checked_mul(channels as usize); + if expected_samples != Some(samples.len()) { + return EnqueueOutcome::Rejected; + } + NativeScreenFrameSinkHandle::outcome_from_native(unsafe { + enqueue( + self.handle.context, + samples.as_ptr(), + num_frames, + channels, + sample_rate_hz, + timestamp_us, + ) + }) + } +} + +impl Drop for NativeScreenFrameSinkHandleRef { + fn drop(&mut self) { + if self.handle.is_valid() { + unsafe { (self.handle.release)(self.handle.context) }; + } + } +} + +pub const STAGING_PAIR_LEN: usize = 2; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum StagingSlotState { + Empty, + Submitted, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum StagingError { + WouldOverwrite, +} + +pub trait StagingBackend { + fn write(&mut self, fill: F); + fn read R>(&self, read: F) -> R; + fn is_ready(&self) -> bool; + fn is_idle(&self) -> bool; +} + +pub struct CpuStagingBackend { + buffer: Vec, +} + +impl CpuStagingBackend { + pub fn new(byte_len: usize) -> Self { + assert!(byte_len > 0, "staging backend length must be positive"); + assert!( + byte_len <= 1 << 30, + "staging backend length exceeds sanity cap" + ); + Self { + buffer: vec![0u8; byte_len], + } + } + + pub fn len(&self) -> usize { + self.buffer.len() + } + + pub fn is_empty(&self) -> bool { + self.buffer.is_empty() + } +} + +impl StagingBackend for CpuStagingBackend { + fn write(&mut self, fill: F) { + fill(&mut self.buffer); + } + + fn read R>(&self, read: F) -> R { + read(&self.buffer) + } + + fn is_ready(&self) -> bool { + true + } + + fn is_idle(&self) -> bool { + true + } +} + +pub struct StagingSurfacePair { + backends: [B; STAGING_PAIR_LEN], + slot_states: [StagingSlotState; STAGING_PAIR_LEN], + next_submit: u64, + next_map: u64, + skipped_count: u64, + overwrite_dropped_count: u64, +} + +impl StagingSurfacePair { + pub fn new(backends: [B; STAGING_PAIR_LEN]) -> Self { + assert_eq!( + STAGING_PAIR_LEN, 2, + "OBS staging pair is exactly two surfaces" + ); + assert!( + backends[0].is_idle(), + "backend zero must be idle at construction" + ); + assert!( + backends[1].is_idle(), + "backend one must be idle at construction" + ); + Self { + backends, + slot_states: [StagingSlotState::Empty; STAGING_PAIR_LEN], + next_submit: 0, + next_map: 0, + skipped_count: 0, + overwrite_dropped_count: 0, + } + } + + pub fn next_submit_sequence(&self) -> u64 { + self.next_submit + } + + pub fn next_map_sequence(&self) -> u64 { + self.next_map + } + + pub fn skipped_count(&self) -> u64 { + self.skipped_count + } + + pub fn overwrite_dropped_count(&self) -> u64 { + self.overwrite_dropped_count + } + + pub fn submit( + &mut self, + sequence: u64, + fill: F, + ) -> Result<(), StagingError> { + assert_eq!(sequence, self.next_submit, "submit must use next sequence"); + assert!( + self.next_map <= self.next_submit, + "map sequence must trail submit" + ); + let slot = (sequence & 1) as usize; + assert!(slot < STAGING_PAIR_LEN, "slot index within pair bounds"); + if self.slot_states[slot] != StagingSlotState::Empty { + self.overwrite_dropped_count = self.overwrite_dropped_count.wrapping_add(1); + return Err(StagingError::WouldOverwrite); + } + assert!( + self.backends[slot].is_idle(), + "backend reports busy on empty slot" + ); + self.backends[slot].write(fill); + self.slot_states[slot] = StagingSlotState::Submitted; + self.next_submit = self.next_submit.wrapping_add(1); + Ok(()) + } + + pub fn try_map R>(&mut self, sequence: u64, read: F) -> Option { + assert!( + self.next_map < self.next_submit, + "cannot map an unsubmitted sequence" + ); + assert_eq!( + sequence, self.next_map, + "try_map must use next pending sequence" + ); + let slot = (sequence & 1) as usize; + assert!(slot < STAGING_PAIR_LEN, "slot index within pair bounds"); + assert_eq!( + self.slot_states[slot], + StagingSlotState::Submitted, + "double map of unsubmitted slot" + ); + if !self.backends[slot].is_ready() { + self.skipped_count = self.skipped_count.wrapping_add(1); + self.slot_states[slot] = StagingSlotState::Empty; + self.next_map = self.next_map.wrapping_add(1); + return None; + } + let value = self.backends[slot].read(read); + self.slot_states[slot] = StagingSlotState::Empty; + self.next_map = self.next_map.wrapping_add(1); + Some(value) + } + + pub fn backend(&self, slot: usize) -> &B { + assert!(slot < STAGING_PAIR_LEN, "slot index within pair bounds"); + assert!( + slot < self.backends.len(), + "slot index within backend storage" + ); + &self.backends[slot] + } +} + +type SinkMap = HashMap>; +static REGISTRY: OnceLock> = OnceLock::new(); + +fn registry() -> &'static RwLock { + REGISTRY.get_or_init(|| RwLock::new(HashMap::new())) +} + +pub fn register_sink(capture_id: impl Into, sink: Arc) { + registry().write().insert(capture_id.into(), sink); +} + +pub fn unregister_sink(capture_id: &str) -> Option> { + registry().write().remove(capture_id) +} + +pub fn get_sink(capture_id: &str) -> Option> { + registry().read().get(capture_id).cloned() +} + +pub fn clear_all_sinks() { + registry().write().clear(); +} + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::atomic::{AtomicU32, Ordering}; + + struct CountingSink { + accepted: AtomicU32, + } + + impl ScreenFrameSink for CountingSink { + fn enqueue(&self, _frame: ScreenFrame) -> EnqueueOutcome { + self.accepted.fetch_add(1, Ordering::Relaxed); + EnqueueOutcome::Accepted + } + } + + fn fresh_id(label: &str) -> String { + format!("test:{}:{}", label, std::process::id()) + } + + fn solid_nv12_frame(width: u32, height: u32) -> ScreenFrame { + let y = (width * height) as usize; + let uv = (width * (height / 2)) as usize; + let mut data = vec![0u8; y + uv]; + data[..y].fill(16); + data[y..].fill(128); + ScreenFrame::Nv12(Nv12Frame { + data: data.into(), + width, + height, + stride_y: width, + stride_uv: width, + timestamp_us: 0, + }) + } + + #[test] + fn sink_registration_round_trips_through_registry() { + let id = fresh_id("round-trip"); + let sink = Arc::new(CountingSink { + accepted: AtomicU32::new(0), + }); + register_sink(id.clone(), sink.clone()); + + let resolved = get_sink(&id).expect("sink must be resolvable"); + assert_eq!( + resolved.enqueue(solid_nv12_frame(64, 64)), + EnqueueOutcome::Accepted, + ); + assert_eq!(sink.accepted.load(Ordering::Relaxed), 1); + + let removed = unregister_sink(&id).expect("sink must be removable"); + assert_eq!( + removed.enqueue(solid_nv12_frame(64, 64)), + EnqueueOutcome::Accepted, + ); + assert!(get_sink(&id).is_none()); + assert!(unregister_sink(&id).is_none()); + } + + #[test] + fn screen_frame_exposes_timestamp_and_dimensions() { + let frame = solid_nv12_frame(640, 480); + assert_eq!(frame.dimensions(), (640, 480)); + assert_eq!(frame.timestamp_us(), 0); + } + + struct RejectingSink; + impl ScreenFrameSink for RejectingSink { + fn enqueue(&self, _frame: ScreenFrame) -> EnqueueOutcome { + EnqueueOutcome::Rejected + } + } + + #[test] + fn concurrent_producers_all_deliver_to_a_single_sink() { + let id = fresh_id("concurrent-producers"); + let sink = Arc::new(CountingSink { + accepted: AtomicU32::new(0), + }); + register_sink(id.clone(), sink.clone()); + + let producers = 8; + let frames_per_producer = 64; + let handles: Vec<_> = (0..producers) + .map(|p| { + let id = id.clone(); + std::thread::spawn(move || { + for f in 0..frames_per_producer { + let resolved = get_sink(&id).expect("sink missing during producer race"); + assert_eq!( + resolved.enqueue(solid_nv12_frame(32, 32)), + EnqueueOutcome::Accepted, + "producer {} frame {}", + p, + f + ); + } + }) + }) + .collect(); + for h in handles { + h.join().expect("producer panicked"); + } + unregister_sink(&id); + assert_eq!( + sink.accepted.load(Ordering::Relaxed), + producers * frames_per_producer + ); + } + + #[test] + fn sinks_for_distinct_ids_do_not_cross_route() { + let id_a = fresh_id("isolation-a"); + let id_b = fresh_id("isolation-b"); + let sink_a = Arc::new(CountingSink { + accepted: AtomicU32::new(0), + }); + let sink_b: Arc = Arc::new(RejectingSink); + register_sink(id_a.clone(), sink_a.clone()); + register_sink(id_b.clone(), sink_b.clone()); + + assert_eq!( + get_sink(&id_a).unwrap().enqueue(solid_nv12_frame(8, 8)), + EnqueueOutcome::Accepted + ); + assert_eq!( + get_sink(&id_b).unwrap().enqueue(solid_nv12_frame(8, 8)), + EnqueueOutcome::Rejected + ); + assert_eq!(sink_a.accepted.load(Ordering::Relaxed), 1); + + unregister_sink(&id_a); + unregister_sink(&id_b); + } + + #[test] + fn unregister_returns_the_active_arc_and_inflight_holders_keep_working() { + let id = fresh_id("unregister-race"); + let sink = Arc::new(CountingSink { + accepted: AtomicU32::new(0), + }); + register_sink(id.clone(), sink.clone()); + + let inflight = get_sink(&id).expect("sink available"); + let removed = unregister_sink(&id).expect("unregister returns sink"); + assert!(Arc::ptr_eq(&inflight, &removed)); + assert!(get_sink(&id).is_none()); + + assert_eq!( + inflight.enqueue(solid_nv12_frame(8, 8)), + EnqueueOutcome::Accepted + ); + assert_eq!(sink.accepted.load(Ordering::Relaxed), 1); + } + + #[test] + fn bgra_frame_exposes_timestamp_and_dimensions() { + let frame = ScreenFrame::Bgra(BgraFrame { + data: vec![0; 4 * 16 * 16], + width: 16, + height: 16, + stride: 64, + timestamp_us: 9_999, + }); + assert_eq!(frame.dimensions(), (16, 16)); + assert_eq!(frame.timestamp_us(), 9_999); + } + + struct StaticSharedBytes { + bytes: Box<[u8]>, + } + + impl SharedFrameBytes for StaticSharedBytes { + fn bytes(&self) -> &[u8] { + &self.bytes + } + } + + #[test] + fn frame_data_owned_round_trips_without_sharing() { + let data = FrameData::from(vec![1u8, 2, 3]); + assert!(!data.is_shared()); + assert!(!data.is_empty()); + assert_eq!(data.len(), 3); + assert_eq!(data.as_slice(), &[1, 2, 3]); + assert_eq!(data.into_vec(), vec![1, 2, 3]); + } + + #[test] + fn frame_data_shared_round_trips_prefix_of_source_bytes() { + let source = Arc::new(StaticSharedBytes { + bytes: vec![7u8; 16].into_boxed_slice(), + }); + let data = FrameData::from_shared(source.clone(), 12, None); + assert!(data.is_shared()); + assert_eq!(data.len(), 12); + assert_eq!(data.as_slice(), &[7u8; 12]); + assert_eq!(Arc::strong_count(&source), 2); + assert_eq!(data.into_vec(), vec![7u8; 12]); + assert_eq!(Arc::strong_count(&source), 1); + } + + #[test] + #[should_panic] + fn frame_data_shared_rejects_len_beyond_source_bytes() { + let source = Arc::new(StaticSharedBytes { + bytes: vec![0u8; 4].into_boxed_slice(), + }); + let _ = FrameData::from_shared(source, 5, None); + } + + #[test] + fn frame_data_shared_capacity_token_returns_slot_on_drop_with_counters_intact() { + let pool = frame_pool::CpuFrameBuilder::build_pool_with_capacity(2, 4).expect("pool init"); + let source = Arc::new(StaticSharedBytes { + bytes: vec![9u8; 8].into_boxed_slice(), + }); + let token = pool.try_acquire().expect("first slot"); + let data = FrameData::from_shared(source, 8, Some(token)); + assert_eq!(pool.currently_in_flight(), 1); + + let second = pool.try_acquire().expect("second slot"); + assert!(pool.try_acquire().is_none()); + assert_eq!(pool.skipped_total(), 1); + assert_eq!(pool.acquired_total(), 2); + + drop(data); + assert_eq!(pool.currently_in_flight(), 1); + let third = pool.try_acquire().expect("slot returned by frame drop"); + assert_eq!(pool.acquired_total(), 3); + assert_eq!(pool.skipped_total(), 1); + + drop(second); + drop(third); + assert_eq!(pool.currently_in_flight(), 0); + } + + #[test] + fn nv12_frame_with_shared_payload_exposes_timestamp_and_dimensions() { + let source = Arc::new(StaticSharedBytes { + bytes: vec![0u8; 64 * 96].into_boxed_slice(), + }); + let frame = ScreenFrame::Nv12(Nv12Frame { + data: FrameData::from_shared(source, 64 * 96, None), + width: 64, + height: 64, + stride_y: 64, + stride_uv: 64, + timestamp_us: 77, + }); + assert_eq!(frame.dimensions(), (64, 64)); + assert_eq!(frame.timestamp_us(), 77); + } + + #[cfg(target_os = "macos")] + #[test] + fn mac_cv_pixel_buffer_with_null_pointer_drops_without_calling_release() { + let frame = + unsafe { MacCvPixelBufferFrame::from_retained(std::ptr::null_mut(), 8, 8, 0, 0) }; + assert_eq!(frame.pixel_buffer_ptr(), std::ptr::null_mut()); + drop(frame); + } + + const STAGING_TEST_LEN: usize = 64; + + fn fresh_staging_pair() -> StagingSurfacePair { + let backends = [ + CpuStagingBackend::new(STAGING_TEST_LEN), + CpuStagingBackend::new(STAGING_TEST_LEN), + ]; + assert_eq!(backends[0].len(), STAGING_TEST_LEN); + assert_eq!(backends[1].len(), STAGING_TEST_LEN); + StagingSurfacePair::new(backends) + } + + #[test] + fn submit_then_map_returns_the_data_from_two_frames_ago() { + let mut pair = fresh_staging_pair(); + assert!(pair.submit(0, |buf| buf.fill(0xA1)).is_ok()); + assert!(pair.submit(1, |buf| buf.fill(0xB2)).is_ok()); + + let first = pair + .try_map(0, |buf| buf[0]) + .expect("first map ready under CPU backend"); + assert_eq!(first, 0xA1); + + assert!(pair.submit(2, |buf| buf.fill(0xC3)).is_ok()); + let second = pair + .try_map(1, |buf| buf[0]) + .expect("second map ready under CPU backend"); + assert_eq!(second, 0xB2); + + let third = pair + .try_map(2, |buf| buf[0]) + .expect("third map ready under CPU backend"); + assert_eq!(third, 0xC3); + } + + #[test] + fn two_submits_in_a_row_preserve_distinct_slots() { + let mut pair = fresh_staging_pair(); + assert!(pair.submit(0, |buf| buf.fill(0x11)).is_ok()); + assert!(pair.submit(1, |buf| buf.fill(0x22)).is_ok()); + assert_eq!(pair.next_submit_sequence(), 2); + + let first = pair + .try_map(0, |buf| buf[0]) + .expect("slot zero still holds first submission"); + assert_eq!(first, 0x11); + let second = pair + .try_map(1, |buf| buf[0]) + .expect("slot one still holds second submission"); + assert_eq!(second, 0x22); + } + + #[test] + fn third_unmapped_submit_returns_would_overwrite_and_drops_newest() { + let mut pair = fresh_staging_pair(); + assert!(pair.submit(0, |buf| buf.fill(0x33)).is_ok()); + assert!(pair.submit(1, |buf| buf.fill(0x44)).is_ok()); + assert_eq!(pair.overwrite_dropped_count(), 0); + + let outcome = pair.submit(2, |buf| buf.fill(0xFF)); + assert_eq!(outcome, Err(StagingError::WouldOverwrite)); + assert_eq!(pair.overwrite_dropped_count(), 1); + + let preserved = pair + .try_map(0, |buf| buf[0]) + .expect("slot zero kept its original submission"); + assert_eq!(preserved, 0x33); + } + + struct GatedBackend { + buffer: Vec, + ready: bool, + } + + impl GatedBackend { + fn new(len: usize) -> Self { + assert!(len > 0, "gated backend len positive"); + assert!(len <= 1 << 16, "gated backend len within sanity cap"); + Self { + buffer: vec![0u8; len], + ready: true, + } + } + } + + impl StagingBackend for GatedBackend { + fn write(&mut self, fill: F) { + fill(&mut self.buffer); + } + + fn read R>(&self, read: F) -> R { + read(&self.buffer) + } + + fn is_ready(&self) -> bool { + self.ready + } + + fn is_idle(&self) -> bool { + true + } + } + + #[test] + fn try_map_returning_none_advances_and_bumps_skipped_counter() { + let mut a = GatedBackend::new(STAGING_TEST_LEN); + let mut b = GatedBackend::new(STAGING_TEST_LEN); + a.ready = false; + b.ready = true; + let mut pair = StagingSurfacePair::new([a, b]); + + assert!(pair.submit(0, |buf| buf.fill(0x55)).is_ok()); + assert!(pair.submit(1, |buf| buf.fill(0x66)).is_ok()); + + let skipped = pair.try_map(0, |buf| buf[0]); + assert!(skipped.is_none()); + assert_eq!(pair.skipped_count(), 1); + assert_eq!(pair.next_map_sequence(), 1); + + let mapped = pair.try_map(1, |buf| buf[0]).expect("slot one ready"); + assert_eq!(mapped, 0x66); + assert_eq!(pair.next_map_sequence(), 2); + + assert!(pair.submit(2, |buf| buf.fill(0x77)).is_ok()); + assert!(pair.submit(3, |buf| buf.fill(0x88)).is_ok()); + let after_skip = pair.try_map(2, |buf| buf[0]); + assert!(after_skip.is_none()); + assert_eq!(pair.skipped_count(), 2); + } + + #[test] + #[should_panic(expected = "submit must use next sequence")] + fn submit_with_out_of_order_sequence_panics() { + let mut pair = fresh_staging_pair(); + let _ = pair.submit(7, |buf| buf.fill(0)); + } + + #[test] + #[should_panic(expected = "try_map must use next pending sequence")] + fn try_map_with_wrong_sequence_panics() { + let mut pair = fresh_staging_pair(); + assert!(pair.submit(0, |buf| buf.fill(0)).is_ok()); + let _ = pair.try_map(5, |buf| buf[0]); + } + + #[test] + #[should_panic(expected = "cannot map an unsubmitted sequence")] + fn try_map_without_any_submit_panics() { + let mut pair = fresh_staging_pair(); + let _ = pair.try_map(0, |buf| buf[0]); + } + + #[test] + #[should_panic(expected = "try_map must use next pending sequence")] + fn try_map_twice_on_the_same_sequence_panics() { + let mut pair = fresh_staging_pair(); + assert!(pair.submit(0, |buf| buf.fill(0xAB)).is_ok()); + assert!(pair.submit(1, |buf| buf.fill(0xCD)).is_ok()); + let first = pair.try_map(0, |buf| buf[0]).expect("first map"); + assert_eq!(first, 0xAB); + let _ = pair.try_map(0, |buf| buf[0]); + } + + static SCREEN_AUDIO_ENQUEUE_CALLS: AtomicU32 = AtomicU32::new(0); + + unsafe extern "C" fn screen_audio_test_retain(_context: *const c_void) {} + unsafe extern "C" fn screen_audio_test_release(_context: *const c_void) {} + unsafe extern "C" fn screen_audio_test_enqueue( + _context: *const c_void, + _samples: *const f32, + _num_frames: u32, + _channels: u32, + _sample_rate_hz: u32, + _timestamp_us: i64, + ) -> u32 { + SCREEN_AUDIO_ENQUEUE_CALLS.fetch_add(1, Ordering::Relaxed); + NATIVE_SCREEN_FRAME_SINK_ACCEPTED + } + + fn screen_audio_test_handle_ref() -> NativeScreenFrameSinkHandleRef { + let handle = NativeScreenFrameSinkHandle { + magic: NATIVE_SCREEN_FRAME_SINK_HANDLE_MAGIC, + version: NATIVE_SCREEN_FRAME_SINK_HANDLE_VERSION, + context: (&SCREEN_AUDIO_ENQUEUE_CALLS as *const AtomicU32).cast::(), + retain: screen_audio_test_retain, + release: screen_audio_test_release, + enqueue_nv12: None, + enqueue_bgra: None, + enqueue_mac_cv_pixel_buffer: None, + enqueue_dmabuf: None, + enqueue_shared_texture: None, + enqueue_screen_audio: Some(screen_audio_test_enqueue), + }; + NativeScreenFrameSinkHandleRef { handle } + } + + #[test] + fn enqueue_screen_audio_rejects_malformed_inputs_without_panicking() { + SCREEN_AUDIO_ENQUEUE_CALLS.store(0, Ordering::Relaxed); + let sink = screen_audio_test_handle_ref(); + + assert_eq!( + sink.enqueue_screen_audio_f32(&[0.0, 0.0], 1, 0, 48_000, 0), + EnqueueOutcome::Rejected + ); + assert_eq!( + sink.enqueue_screen_audio_f32(&[], 0, 2, 48_000, 0), + EnqueueOutcome::Rejected + ); + assert_eq!( + sink.enqueue_screen_audio_f32(&[0.0, 0.0, 0.0], 1, 2, 48_000, 0), + EnqueueOutcome::Rejected + ); + assert_eq!(SCREEN_AUDIO_ENQUEUE_CALLS.load(Ordering::Relaxed), 0); + } + + #[test] + fn enqueue_screen_audio_accepts_whole_stereo_frames() { + SCREEN_AUDIO_ENQUEUE_CALLS.store(0, Ordering::Relaxed); + let sink = screen_audio_test_handle_ref(); + + assert_eq!( + sink.enqueue_screen_audio_f32(&[0.1, 0.2, 0.3, 0.4], 2, 2, 48_000, 7), + EnqueueOutcome::Accepted + ); + assert_eq!(SCREEN_AUDIO_ENQUEUE_CALLS.load(Ordering::Relaxed), 1); + } +} diff --git a/fluxer_desktop/native/system-hunspell/Cargo.lock b/fluxer_desktop/native/system-hunspell/Cargo.lock new file mode 100644 index 0000000..86945ca --- /dev/null +++ b/fluxer_desktop/native/system-hunspell/Cargo.lock @@ -0,0 +1,1041 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "bindgen" +version = "0.61.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a022e58a142a46fea340d68012b9201c094e93ec3d033a944a24f8fd4a4f09a" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 1.0.109", + "which", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "cc" +version = "1.2.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading 0.8.9", +] + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fluxer_desktop_native" +version = "0.1.0" +dependencies = [ + "sha2", +] + +[[package]] +name = "fluxer_system_hunspell" +version = "0.0.0" +dependencies = [ + "fluxer_desktop_native", + "hunspell-sys", + "napi", + "napi-build", + "napi-derive", + "tempfile", +] + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", + "wasip3", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "hunspell-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9228605394e47acb7e5c20da4f9d8d5175fd767230e51d3095623bc5e20b7dec" +dependencies = [ + "bindgen", + "cc", + "pkg-config", +] + +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "typenum", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "log" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags 2.13.0", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash 2.1.2", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn 2.0.117", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading 0.9.0", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.13.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.13.0", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.59.0", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom", + "once_cell", + "rustix 1.1.4", + "windows-sys 0.59.0", +] + +[[package]] +name = "typenum" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.13.0", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.13.0", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/fluxer_desktop/native/system-hunspell/Cargo.toml b/fluxer_desktop/native/system-hunspell/Cargo.toml new file mode 100644 index 0000000..14b134f --- /dev/null +++ b/fluxer_desktop/native/system-hunspell/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "fluxer_system_hunspell" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +fluxer_desktop_native = {path = "../rust"} +hunspell-sys = {version = "0.3.1", features = ["bundled"]} +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8"]} +napi-derive = "3.5.6" + +[build-dependencies] +napi-build = "2.3.2" + +[dev-dependencies] +tempfile = "3.27" diff --git a/fluxer_desktop/native/system-hunspell/build.rs b/fluxer_desktop/native/system-hunspell/build.rs new file mode 100644 index 0000000..d45de40 --- /dev/null +++ b/fluxer_desktop/native/system-hunspell/build.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + napi_build::setup(); +} diff --git a/fluxer_desktop/native/system-hunspell/index.d.ts b/fluxer_desktop/native/system-hunspell/index.d.ts new file mode 100644 index 0000000..7a2e667 --- /dev/null +++ b/fluxer_desktop/native/system-hunspell/index.d.ts @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +export interface SystemDictionary { + tag: string; + affPath: string; + dicPath: string; +} + +export declare class Hunspell { + constructor(affPath: string, dicPath: string); + + spell(word: string): boolean; + + suggest(word: string, max?: number): Array; + + add(word: string): void; + + remove(word: string): void; + + close(): void; +} + +export declare function discoverSystemDictionaries(): Array; + +export declare function hashFile(path: string): Promise; + +export declare const loadError: Error | null; diff --git a/fluxer_desktop/native/system-hunspell/index.js b/fluxer_desktop/native/system-hunspell/index.js new file mode 100644 index 0000000..1c8fd06 --- /dev/null +++ b/fluxer_desktop/native/system-hunspell/index.js @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync} = require('node:fs'); +const {join, sep} = require('node:path'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); +const MODULE_NAME = '@fluxer/system-hunspell'; +const SKIP_NATIVE_PROBE_ENV = 'FLUXER_SYSTEM_HUNSPELL_SKIP_NATIVE_PROBE'; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName() { + if (process.platform !== 'linux') { + throw new Error(`@fluxer/system-hunspell is only supported on Linux, got ${process.platform}`); + } + switch (process.arch) { + case 'x64': + return 'system-hunspell.linux-x64-gnu.node'; + case 'arm64': + return 'system-hunspell.linux-arm64-gnu.node'; + default: + throw new Error(`Unsupported Linux architecture: ${process.arch}`); + } +} + +let binding = null; +let loadError = null; + +if (process.platform === 'linux') { + try { + const nativeRoot = resolveNativeRoot(); + const nativePath = join(nativeRoot, nativeFileName()); + const loaded = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + binding = loaded.binding; + loadError = loaded.loadError; + if (loadError) throw loadError; + } catch (error) { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: 'native loader threw before binding load completed', + cause: error, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + throw loadError; + } +} + +module.exports = { + Hunspell: binding ? binding.Hunspell : null, + discoverSystemDictionaries: binding ? binding.discoverSystemDictionaries : null, + hashFile: binding ? binding.hashFile : null, + loadError, +}; diff --git a/fluxer_desktop/native/system-hunspell/loader-diagnostics.cjs b/fluxer_desktop/native/system-hunspell/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/system-hunspell/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/system-hunspell/package.json b/fluxer_desktop/native/system-hunspell/package.json new file mode 100644 index 0000000..597c25a --- /dev/null +++ b/fluxer_desktop/native/system-hunspell/package.json @@ -0,0 +1,27 @@ +{ + "name": "@fluxer/system-hunspell", + "version": "0.0.0", + "description": "", + "private": true, + "license": "AGPL-3.0-or-later", + "os": [ + "linux" + ], + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "system-hunspell.linux-x64-gnu.node", + "system-hunspell.linux-arm64-gnu.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "cargo test --manifest-path Cargo.toml" + } +} diff --git a/fluxer_desktop/native/system-hunspell/src/lib.rs b/fluxer_desktop/native/system-hunspell/src/lib.rs new file mode 100644 index 0000000..f557d50 --- /dev/null +++ b/fluxer_desktop/native/system-hunspell/src/lib.rs @@ -0,0 +1,418 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::ffi::{CStr, CString}; +use std::fs::File; +use std::path::Path; +use std::ptr; +use std::sync::{Mutex, MutexGuard}; + +use fluxer_desktop_native::system_hunspell::dictionaries::{EnvSnapshot, discover_dictionaries}; +use fluxer_desktop_native::system_hunspell::encoding::is_utf8_encoding; +use fluxer_desktop_native::system_hunspell::hashing::hash_file_to_hex; +use napi::Task; +use napi::bindgen_prelude::{AsyncTask, Env, Error, Result, Status}; +use napi_derive::napi; + +static HUNSPELL_LOCK: Mutex<()> = Mutex::new(()); + +#[napi(object)] +pub struct SystemDictionary { + pub tag: String, + #[napi(js_name = "affPath")] + pub aff_path: String, + #[napi(js_name = "dicPath")] + pub dic_path: String, +} + +#[napi] +pub struct Hunspell { + dict: Option, +} + +#[napi] +impl Hunspell { + #[napi(constructor)] + pub fn new(aff_path: String, dic_path: String) -> Result { + Ok(Self { + dict: Some(Dictionary::load(&aff_path, &dic_path)?), + }) + } + + #[napi] + pub fn spell(&self, word: String) -> Result { + validate_text_arg(&word, "word")?; + Ok(self + .dict + .as_ref() + .is_some_and(|dict| dict.spell(&word).unwrap_or(true))) + } + + #[napi] + pub fn suggest(&self, word: String, max: Option) -> Result> { + validate_text_arg(&word, "word")?; + let Some(dict) = &self.dict else { + return Ok(Vec::new()); + }; + let limit = normalize_suggestion_limit(max); + dict.suggest(&word, limit) + } + + #[napi] + pub fn add(&mut self, word: String) -> Result<()> { + validate_text_arg(&word, "word")?; + if let Some(dict) = &mut self.dict { + let _ = dict.add(&word); + } + Ok(()) + } + + #[napi] + pub fn remove(&mut self, word: String) -> Result<()> { + validate_text_arg(&word, "word")?; + if let Some(dict) = &mut self.dict { + let _ = dict.remove(&word); + } + Ok(()) + } + + #[napi] + pub fn close(&mut self) { + self.dict = None; + } +} + +#[napi(js_name = "discoverSystemDictionaries")] +pub fn discover_system_dictionaries() -> Vec { + let snapshot = EnvSnapshot { + hunspell_dict_dir: std::env::var("HUNSPELL_DICT_DIR").ok(), + xdg_data_home: std::env::var("XDG_DATA_HOME").ok(), + home: std::env::var("HOME").ok(), + xdg_data_dirs: std::env::var("XDG_DATA_DIRS").ok(), + }; + discover_dictionaries(&snapshot) + .into_iter() + .map(|dict| SystemDictionary { + tag: dict.tag, + aff_path: dict.aff_path.display().to_string(), + dic_path: dict.dic_path.display().to_string(), + }) + .collect() +} + +pub struct HashFileTask { + path: String, +} + +#[napi(js_name = "hashFile")] +pub fn hash_file(path: String) -> Result> { + validate_path_arg(&path)?; + Ok(AsyncTask::new(HashFileTask { path })) +} + +impl Task for HashFileTask { + type Output = String; + type JsValue = String; + + fn compute(&mut self) -> Result { + hash_file_to_hex(&self.path).map_err(hash_error) + } + + fn resolve(&mut self, _env: Env, output: Self::Output) -> Result { + Ok(output) + } +} + +#[derive(Debug)] +struct Dictionary { + handle: *mut hunspell_sys::Hunhandle, +} + +impl Dictionary { + fn load(aff_path: &str, dic_path: &str) -> Result { + validate_path_arg(aff_path)?; + validate_path_arg(dic_path)?; + if File::open(Path::new(aff_path)).is_err() || File::open(Path::new(dic_path)).is_err() { + return Err(hunspell_load_error("LoadFailed")); + } + + let aff = cstring_arg(aff_path, "affPath")?; + let dic = cstring_arg(dic_path, "dicPath")?; + let _guard = hunspell_lock(); + + let handle = unsafe { hunspell_sys::Hunspell_create(aff.as_ptr(), dic.as_ptr()) }; + if handle.is_null() { + return Err(hunspell_load_error("LoadFailed")); + } + + let encoding_ptr = unsafe { hunspell_sys::Hunspell_get_dic_encoding(handle) }; + if encoding_ptr.is_null() { + unsafe { hunspell_sys::Hunspell_destroy(handle) }; + return Err(hunspell_load_error("LoadFailed")); + } + + let encoding = unsafe { CStr::from_ptr(encoding_ptr) } + .to_str() + .map_err(|_| hunspell_load_error("DictionaryNotUtf8"))?; + if !is_utf8_encoding(encoding) { + unsafe { hunspell_sys::Hunspell_destroy(handle) }; + return Err(hunspell_load_error("DictionaryNotUtf8")); + } + + Ok(Self { handle }) + } + + fn spell(&self, word: &str) -> Result { + let word = cstring_arg(word, "word")?; + let _guard = hunspell_lock(); + + Ok(unsafe { hunspell_sys::Hunspell_spell(self.handle, word.as_ptr()) } != 0) + } + + fn suggest(&self, word: &str, max: usize) -> Result> { + let word = cstring_arg(word, "word")?; + let mut raw: *mut *mut std::os::raw::c_char = ptr::null_mut(); + let _hunspell_guard = hunspell_lock(); + + let count = unsafe { hunspell_sys::Hunspell_suggest(self.handle, &mut raw, word.as_ptr()) }; + let _guard = SuggestionList { + handle: self.handle, + raw, + count, + }; + if count <= 0 || raw.is_null() { + return Ok(Vec::new()); + } + let limit = (count as usize).min(max); + let mut out = Vec::with_capacity(limit); + for index in 0..limit { + let item = unsafe { *raw.add(index) }; + if item.is_null() { + out.push(String::new()); + continue; + } + + let suggestion = unsafe { CStr::from_ptr(item) }; + if let Ok(text) = suggestion.to_str() { + out.push(text.to_owned()); + } + } + Ok(out) + } + + fn add(&mut self, word: &str) -> Result<()> { + let word = cstring_arg(word, "word")?; + let _guard = hunspell_lock(); + + let rc = unsafe { hunspell_sys::Hunspell_add(self.handle, word.as_ptr()) }; + if rc == 0 { + Ok(()) + } else { + Err(hunspell_load_error("LoadFailed")) + } + } + + fn remove(&mut self, word: &str) -> Result<()> { + let word = cstring_arg(word, "word")?; + let _guard = hunspell_lock(); + + unsafe { hunspell_sys::Hunspell_remove(self.handle, word.as_ptr()) }; + Ok(()) + } +} + +impl Drop for Dictionary { + fn drop(&mut self) { + if !self.handle.is_null() { + let _guard = hunspell_lock(); + + unsafe { hunspell_sys::Hunspell_destroy(self.handle) }; + self.handle = ptr::null_mut(); + } + } +} + +struct SuggestionList { + handle: *mut hunspell_sys::Hunhandle, + raw: *mut *mut std::os::raw::c_char, + count: i32, +} + +impl Drop for SuggestionList { + fn drop(&mut self) { + if self.count > 0 && !self.raw.is_null() { + unsafe { hunspell_sys::Hunspell_free_list(self.handle, &mut self.raw, self.count) }; + } + } +} + +fn normalize_suggestion_limit(max: Option) -> usize { + match max { + Some(value @ 1..=64) => value as usize, + _ => 8, + } +} + +fn hunspell_lock() -> MutexGuard<'static, ()> { + HUNSPELL_LOCK + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()) +} + +fn validate_path_arg(path: &str) -> Result<()> { + if path.is_empty() { + return Err(Error::new(Status::InvalidArg, "path must be non-empty")); + } + if path.as_bytes().contains(&0) { + return Err(Error::new( + Status::InvalidArg, + "path must not contain NUL bytes", + )); + } + Ok(()) +} + +fn validate_text_arg(value: &str, label: &str) -> Result<()> { + if value.as_bytes().contains(&0) { + return Err(Error::new( + Status::InvalidArg, + format!("{label} must not contain NUL bytes"), + )); + } + Ok(()) +} + +fn cstring_arg(value: &str, label: &str) -> Result { + CString::new(value).map_err(|_| { + Error::new( + Status::InvalidArg, + format!("{label} must not contain NUL bytes"), + ) + }) +} + +fn hunspell_load_error(reason: &str) -> Error { + Error::new( + Status::GenericFailure, + format!("Hunspell load failed: {reason}"), + ) +} + +fn hash_error(error: std::io::Error) -> Error { + let message = match error.kind() { + std::io::ErrorKind::NotFound | std::io::ErrorKind::PermissionDenied => { + "could not open file for hashing" + } + _ => "read failed while hashing file", + }; + Error::new(Status::GenericFailure, message) +} + +#[cfg(test)] +mod tests { + use std::io::Write; + + use super::*; + + #[test] + fn suggestion_limit_defaults_and_bounds_match_legacy_contract() { + assert_eq!(8, normalize_suggestion_limit(None)); + assert_eq!(8, normalize_suggestion_limit(Some(0))); + assert_eq!(1, normalize_suggestion_limit(Some(1))); + assert_eq!(64, normalize_suggestion_limit(Some(64))); + assert_eq!(8, normalize_suggestion_limit(Some(65))); + } + + #[test] + fn path_validation_rejects_empty_and_nul_paths() { + assert_eq!( + "path must be non-empty", + validate_path_arg("") + .expect_err("empty path should fail") + .reason + ); + assert_eq!( + "path must not contain NUL bytes", + validate_path_arg("/tmp/a\0b") + .expect_err("NUL path should fail") + .reason + ); + } + + #[test] + fn text_validation_rejects_nul_words() { + assert_eq!( + "word must not contain NUL bytes", + validate_text_arg("a\0b", "word") + .expect_err("NUL word should fail") + .reason + ); + } + + #[test] + fn hash_error_maps_open_failures_to_existing_js_message() { + let err = hash_file_to_hex("/nonexistent/path/that/should/not/exist.bin") + .map_err(hash_error) + .expect_err("missing file should fail"); + assert_eq!(Status::GenericFailure, err.status); + assert_eq!("could not open file for hashing", err.reason); + } + + #[test] + fn hash_file_task_streams_file_to_expected_hex() { + let dir = tempfile::tempdir().unwrap(); + let path = dir.path().join("payload"); + let mut file = File::create(&path).unwrap(); + file.write_all(b"abc").unwrap(); + + let mut task = HashFileTask { + path: path.display().to_string(), + }; + assert_eq!( + "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad", + task.compute().unwrap() + ); + } + + #[test] + fn dictionary_fixture_spells_and_accepts_runtime_words() { + let (dir, aff_path, dic_path) = write_fixture_dictionary("UTF-8"); + let mut dict = + Dictionary::load(&aff_path, &dic_path).expect("fixture dictionary should load"); + + assert!(dict.spell("cat").unwrap()); + assert!(dict.spell("cats").unwrap()); + assert!(!dict.spell("Fluxer").unwrap()); + dict.add("Fluxer").unwrap(); + assert!(dict.spell("Fluxer").unwrap()); + dict.remove("Fluxer").unwrap(); + assert!(!dict.spell("Fluxer").unwrap()); + + drop(dir); + } + + #[test] + fn dictionary_rejects_non_utf8_dictionaries() { + let (_dir, aff_path, dic_path) = write_fixture_dictionary("ISO-8859-1"); + let err = Dictionary::load(&aff_path, &dic_path) + .expect_err("non-UTF-8 dictionary should be rejected"); + assert_eq!(Status::GenericFailure, err.status); + assert_eq!("Hunspell load failed: DictionaryNotUtf8", err.reason); + } + + fn write_fixture_dictionary(encoding: &str) -> (tempfile::TempDir, String, String) { + let dir = tempfile::tempdir().unwrap(); + let aff_path = dir.path().join("fixture.aff"); + let dic_path = dir.path().join("fixture.dic"); + std::fs::write( + &aff_path, + format!("SET {encoding}\n\nSFX S Y 1\nSFX S 0 s [^sxzhy]\n"), + ) + .unwrap(); + std::fs::write(&dic_path, "2\ncat/S\nprogram/S\n").unwrap(); + ( + dir, + aff_path.display().to_string(), + dic_path.display().to_string(), + ) + } +} diff --git a/fluxer_desktop/native/webauthn/Cargo.lock b/fluxer_desktop/native/webauthn/Cargo.lock new file mode 100644 index 0000000..5cebeb8 --- /dev/null +++ b/fluxer_desktop/native/webauthn/Cargo.lock @@ -0,0 +1,813 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn", +] + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "cc" +version = "1.2.62" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading 0.8.9", +] + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags", + "block2", + "libc", + "objc2", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fluxer_webauthn" +version = "0.0.0" +dependencies = [ + "dispatch2", + "libfido2-sys", + "napi", + "napi-build", + "napi-derive", + "objc2", + "objc2-app-kit", + "objc2-authentication-services", + "objc2-foundation", + "windows", + "windows-core", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libfido2-sys" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c06f689efa98c9256934c671255adb58678c382f1ef14296ecb22d5de56902f" +dependencies = [ + "anyhow", + "bindgen", + "cfg-if", + "libz-sys", + "openssl-sys", + "pkg-config", + "regex", + "vcpkg", +] + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "libz-sys" +version = "1.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc3a226e576f50782b3305c5ccf458698f92798987f551c6a02efe8276721e22" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading 0.9.0", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags", + "block2", + "libc", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-text", + "objc2-core-video", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-authentication-services" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6d6f7dab884a28adaec1012eb3889257a49cc145724e35f93ece2d209f8b25" +dependencies = [ + "bitflags", + "block2", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "objc2-security", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "bitflags", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-core-video" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-io-surface", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-security" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe137109bd1e8b5a99390f77a7d8b2961dafc1a1c5db8f2e60329ad6d895a" +dependencies = [ + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "openssl-sys" +version = "0.9.116" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28a22dc7140cda5f096e5e7724a6962ca81a7f8bfd2979f9b18c11af56318c4" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] diff --git a/fluxer_desktop/native/webauthn/Cargo.toml b/fluxer_desktop/native/webauthn/Cargo.toml new file mode 100644 index 0000000..fb1c7ae --- /dev/null +++ b/fluxer_desktop/native/webauthn/Cargo.toml @@ -0,0 +1,79 @@ +[package] +name = "fluxer_webauthn" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8"]} +napi-derive = "3.5.6" + +[target.'cfg(target_os = "linux")'.dependencies] +libfido2-sys = "0.5.1" + +[target.'cfg(target_os = "windows")'.dependencies] +windows = {version = "0.62.2", features = [ + "Win32_Foundation", + "Win32_Networking_WindowsWebServices", + "Win32_System_Threading", + "Win32_UI_WindowsAndMessaging", +]} +windows-core = "0.62.2" + +[target.'cfg(target_os = "macos")'.dependencies] +objc2 = "0.6" +objc2-foundation = {version = "0.3", features = [ + "NSArray", + "NSData", + "NSDictionary", + "NSError", + "NSObject", + "NSString", + "NSThread", + "NSValue", +]} +objc2-app-kit = {version = "0.3", features = [ + "NSApplication", + "NSResponder", + "NSWindow", +]} +objc2-authentication-services = {version = "0.3.2", features = [ + "ASAuthorization", + "ASAuthorizationController", + "ASAuthorizationCredential", + "ASAuthorizationPlatformPublicKeyCredentialAssertion", + "ASAuthorizationPlatformPublicKeyCredentialAssertionRequest", + "ASAuthorizationPlatformPublicKeyCredentialDescriptor", + "ASAuthorizationPlatformPublicKeyCredentialProvider", + "ASAuthorizationPlatformPublicKeyCredentialRegistration", + "ASAuthorizationPlatformPublicKeyCredentialRegistrationRequest", + "ASAuthorizationProvider", + "ASAuthorizationPublicKeyCredentialAssertion", + "ASAuthorizationPublicKeyCredentialAssertionRequest", + "ASAuthorizationPublicKeyCredentialConstants", + "ASAuthorizationPublicKeyCredentialDescriptor", + "ASAuthorizationPublicKeyCredentialParameters", + "ASAuthorizationPublicKeyCredentialRegistration", + "ASAuthorizationPublicKeyCredentialRegistrationRequest", + "ASAuthorizationRequest", + "ASAuthorizationSecurityKeyPublicKeyCredentialAssertion", + "ASAuthorizationSecurityKeyPublicKeyCredentialAssertionRequest", + "ASAuthorizationSecurityKeyPublicKeyCredentialDescriptor", + "ASAuthorizationSecurityKeyPublicKeyCredentialProvider", + "ASAuthorizationSecurityKeyPublicKeyCredentialRegistration", + "ASAuthorizationSecurityKeyPublicKeyCredentialRegistrationRequest", + "ASCOSEConstants", + "ASFoundation", + "ASPublicKeyCredential", +]} +dispatch2 = "0.3" + +[build-dependencies] +napi-build = "2.3.2" diff --git a/fluxer_desktop/native/webauthn/build.rs b/fluxer_desktop/native/webauthn/build.rs new file mode 100644 index 0000000..161cc4a --- /dev/null +++ b/fluxer_desktop/native/webauthn/build.rs @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + napi_build::setup(); + + let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap_or_default(); + + match target_os.as_str() { + "macos" => {} + "windows" => { + println!("cargo:rustc-link-arg-cdylib=/DELAYLOAD:webauthn.dll"); + println!("cargo:rustc-link-arg-cdylib=delayimp.lib"); + } + "linux" => { + println!("cargo:rustc-link-arg-cdylib=-Wl,--disable-new-dtags"); + println!("cargo:rustc-link-arg-cdylib=-Wl,-rpath,$ORIGIN"); + } + _ => {} + } +} diff --git a/fluxer_desktop/native/webauthn/index.d.ts b/fluxer_desktop/native/webauthn/index.d.ts new file mode 100644 index 0000000..8117241 --- /dev/null +++ b/fluxer_desktop/native/webauthn/index.d.ts @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import type {Buffer} from 'node:buffer'; + +export type AuthenticatorAttachment = 'platform' | 'cross-platform' | string; +export type PublicKeyCredentialType = 'public-key'; +export type UserVerificationRequirement = 'discouraged' | 'preferred' | 'required'; +export type ResidentKeyRequirement = 'discouraged' | 'preferred' | 'required'; +export type AttestationConveyancePreference = 'direct' | 'enterprise' | 'indirect' | 'none'; + +export interface PublicKeyCredentialDescriptor { + id: Buffer; + type: PublicKeyCredentialType; + transports?: ReadonlyArray; +} + +export interface PublicKeyCredentialCreationOptions { + attestation?: AttestationConveyancePreference; + authenticatorSelection?: { + authenticatorAttachment?: AuthenticatorAttachment; + requireResidentKey?: boolean; + residentKey?: ResidentKeyRequirement; + userVerification?: UserVerificationRequirement; + }; + challenge: Buffer; + excludeCredentials?: ReadonlyArray; + extensions?: unknown; + pubKeyCredParams: ReadonlyArray<{ + alg: number; + type: PublicKeyCredentialType; + }>; + rp: { + id?: string; + name: string; + }; + timeout?: number; + origin: string; + user: { + displayName: string; + id: Buffer; + name: string; + }; + windowHandle?: Buffer; + pin?: string; +} + +export interface PublicKeyCredentialRequestOptions { + allowCredentials?: ReadonlyArray; + challenge: Buffer; + extensions?: unknown; + origin: string; + rpId?: string; + timeout?: number; + userVerification?: UserVerificationRequirement; + windowHandle?: Buffer; + pin?: string; +} + +export interface PublicKeyCredential { + authenticatorAttachment?: AuthenticatorAttachment; + id: string; + rawId: Buffer; + response: Buffer; + type: PublicKeyCredentialType; +} + +export interface WebAuthnBackendInfo { + apiVersion: number; + backend: 'macos-authenticationservices' | 'windows-webauthn' | 'linux-libfido2' | 'unavailable'; + ceremoniesImplemented: boolean; + nativeLoaded: boolean; + platformAuthenticatorAvailable: boolean; + platformBrokerAvailable: boolean; + reason: string; + supported: boolean; + target: string; +} + +export declare function create(options: PublicKeyCredentialCreationOptions): Promise; + +export declare function get(options: PublicKeyCredentialRequestOptions): Promise; + +export declare function getBackendInfo(): WebAuthnBackendInfo; + +export declare function isSupported(): Promise; + +export declare const loadError: Error | null; diff --git a/fluxer_desktop/native/webauthn/index.js b/fluxer_desktop/native/webauthn/index.js new file mode 100644 index 0000000..e4287be --- /dev/null +++ b/fluxer_desktop/native/webauthn/index.js @@ -0,0 +1,322 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync} = require('node:fs'); +const {createHash} = require('node:crypto'); +const {join, sep} = require('node:path'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); +const MODULE_NAME = '@fluxer/webauthn'; +const SKIP_NATIVE_PROBE_ENV = 'FLUXER_WEBAUTHN_SKIP_NATIVE_PROBE'; +const WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY = 0; +const WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM = 1; +const WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM = 2; +const WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED = 1; +const WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED = 2; +const WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED = 3; +const WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE = 1; +const WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT = 2; +const WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT = 3; +const WEBAUTHN_ENTERPRISE_ATTESTATION_NONE = 0; +const WEBAUTHN_ENTERPRISE_ATTESTATION_VENDOR_FACILITATED = 1; +const WEBAUTHN_CTAP_TRANSPORT_USB = 0x00000001; +const WEBAUTHN_CTAP_TRANSPORT_NFC = 0x00000002; +const WEBAUTHN_CTAP_TRANSPORT_BLE = 0x00000004; +const WEBAUTHN_CTAP_TRANSPORT_INTERNAL = 0x00000010; +const WEBAUTHN_CTAP_TRANSPORT_HYBRID = 0x00000020; +const WEBAUTHN_CTAP_TRANSPORT_SMART_CARD = 0x00000040; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName(platform = process.platform, arch = process.arch) { + if (platform === 'darwin' && (arch === 'x64' || arch === 'arm64')) return `webauthn.darwin-${arch}.node`; + if (platform === 'win32' && (arch === 'x64' || arch === 'arm64')) return `webauthn.win32-${arch}-msvc.node`; + if (platform === 'linux' && (arch === 'x64' || arch === 'arm64')) return `webauthn.linux-${arch}-gnu.node`; + return null; +} + +let binding = null; +let loadError = null; + +const fileName = nativeFileName(); + +if (fileName) { + try { + const nativeRoot = resolveNativeRoot(); + const nativePath = join(nativeRoot, fileName); + const loaded = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + binding = loaded.binding; + loadError = loaded.loadError; + if (loadError) throw loadError; + } catch (error) { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: 'native loader threw before binding load completed', + cause: error, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + throw loadError; + } +} else { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: `no native binary mapping for ${process.platform}/${process.arch}`, + skipNativeProbeEnv: SKIP_NATIVE_PROBE_ENV, + }); + throw loadError; +} + +function base64Url(buffer) { + return Buffer.from(buffer).toString('base64').replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); +} + +function normalizedOrigin(origin) { + if (typeof origin !== 'string' || origin.length === 0) { + throw new TypeError('@fluxer/webauthn requires an origin string'); + } + return new URL(origin).origin; +} + +function relyingPartyId(options, origin) { + if (typeof options.rpId === 'string' && options.rpId.length > 0) return options.rpId; + if (options.rp && typeof options.rp.id === 'string' && options.rp.id.length > 0) return options.rp.id; + return new URL(origin).hostname; +} + +function clientDataJSON(type, challenge, origin) { + return Buffer.from( + JSON.stringify({ + type, + challenge: base64Url(challenge), + origin, + crossOrigin: false, + }), + 'utf8', + ); +} + +function clientDataHash(data) { + return createHash('sha256').update(data).digest(); +} + +function authenticatorAttachment(value) { + if (value === 'platform') return WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM; + if (value === 'cross-platform') return WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM; + return WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY; +} + +function userVerification(value) { + if (value === 'required') return WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED; + if (value === 'discouraged') return WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED; + return WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED; +} + +function attestation(value) { + if (value === 'direct') return WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT; + if (value === 'indirect') return WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT; + return WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE; +} + +function enterpriseAttestation(value) { + return value === 'enterprise' + ? WEBAUTHN_ENTERPRISE_ATTESTATION_VENDOR_FACILITATED + : WEBAUTHN_ENTERPRISE_ATTESTATION_NONE; +} + +function transportBits(transports) { + if (!Array.isArray(transports)) return 0; + let bits = 0; + for (const transport of transports) { + if (transport === 'usb') bits |= WEBAUTHN_CTAP_TRANSPORT_USB; + else if (transport === 'nfc') bits |= WEBAUTHN_CTAP_TRANSPORT_NFC; + else if (transport === 'ble') bits |= WEBAUTHN_CTAP_TRANSPORT_BLE; + else if (transport === 'internal') bits |= WEBAUTHN_CTAP_TRANSPORT_INTERNAL; + else if (transport === 'hybrid') bits |= WEBAUTHN_CTAP_TRANSPORT_HYBRID; + else if (transport === 'smart-card') bits |= WEBAUTHN_CTAP_TRANSPORT_SMART_CARD; + } + return bits; +} + +function credentialDescriptors(descriptors) { + if (!Array.isArray(descriptors)) return []; + return descriptors.map((descriptor) => ({ + id: Buffer.from(descriptor.id), + transports: transportBits(descriptor.transports), + })); +} + +function windowHandleBuffer(value) { + if (Buffer.isBuffer(value)) return value; + if (value instanceof Uint8Array) return Buffer.from(value); + return undefined; +} + +function pinString(value) { + return typeof value === 'string' && value.length > 0 ? value : undefined; +} + +function residentKeyFlags(selection) { + const residentKey = selection?.residentKey; + const requireResidentKey = Boolean(selection?.requireResidentKey) || residentKey === 'required'; + return { + requireResidentKey, + preferResidentKey: requireResidentKey || residentKey === 'preferred', + }; +} + +function normalizeCreateOptions(options) { + if (!options || typeof options !== 'object') throw new TypeError('registration options must be an object'); + const origin = normalizedOrigin(options.origin); + const selection = options.authenticatorSelection || {}; + const residentKeys = residentKeyFlags(selection); + return { + rpId: relyingPartyId(options, origin), + rpName: options.rp && typeof options.rp.name === 'string' ? options.rp.name : relyingPartyId(options, origin), + challenge: Buffer.from(options.challenge), + userId: Buffer.from(options.user.id), + userName: options.user.name, + userDisplayName: options.user.displayName, + clientDataJSON: clientDataJSON('webauthn.create', options.challenge, origin), + pubKeyCredParams: options.pubKeyCredParams, + excludeCredentials: credentialDescriptors(options.excludeCredentials), + timeout: Number.isFinite(options.timeout) ? Math.max(0, Math.trunc(options.timeout)) : 0, + authenticatorAttachment: authenticatorAttachment(selection.authenticatorAttachment), + userVerification: userVerification(selection.userVerification), + attestation: attestation(options.attestation), + enterpriseAttestation: enterpriseAttestation(options.attestation), + windowHandle: windowHandleBuffer(options.windowHandle), + pin: pinString(options.pin), + ...residentKeys, + }; +} + +function normalizeGetOptions(options) { + if (!options || typeof options !== 'object') throw new TypeError('assertion options must be an object'); + const origin = normalizedOrigin(options.origin); + return { + rpId: relyingPartyId(options, origin), + challenge: Buffer.from(options.challenge), + clientDataJSON: clientDataJSON('webauthn.get', options.challenge, origin), + allowCredentials: credentialDescriptors(options.allowCredentials), + timeout: Number.isFinite(options.timeout) ? Math.max(0, Math.trunc(options.timeout)) : 0, + authenticatorAttachment: WEBAUTHN_AUTHENTICATOR_ATTACHMENT_ANY, + userVerification: userVerification(options.userVerification), + windowHandle: windowHandleBuffer(options.windowHandle), + pin: pinString(options.pin), + }; +} + +function unavailableError() { + return new Error( + `@fluxer/webauthn native backend unavailable on ${process.platform}/${process.arch}: ${ + loadError instanceof Error ? loadError.message : 'unknown load error' + }`, + ); +} + +function requireBinding() { + if (!binding) throw unavailableError(); + return binding; +} + +async function isSupported() { + if (!binding || typeof binding.isSupported !== 'function') return false; + return Boolean(await binding.isSupported()); +} + +function rawIdCredential(rawId, response, authenticatorAttachment) { + const id = base64Url(rawId); + return { + id, + rawId, + response: Buffer.from(JSON.stringify(response), 'utf8'), + authenticatorAttachment, + type: 'public-key', + }; +} + +async function create(options) { + const native = requireBinding(); + if (typeof native.create !== 'function') { + throw new Error( + `@fluxer/webauthn native backend did not export registration on ${process.platform}/${process.arch}`, + ); + } + const normalized = normalizeCreateOptions(options); + normalized.clientDataHash = clientDataHash(normalized.clientDataJSON); + const result = await native.create(normalized); + return rawIdCredential( + result.rawId, + { + clientDataJSON: base64Url(result.clientDataJSON), + attestationObject: base64Url(result.attestationObject), + }, + result.authenticatorAttachment, + ); +} + +async function get(options) { + const native = requireBinding(); + if (typeof native.get !== 'function') { + throw new Error( + `@fluxer/webauthn native backend did not export authentication on ${process.platform}/${process.arch}`, + ); + } + const normalized = normalizeGetOptions(options); + normalized.clientDataHash = clientDataHash(normalized.clientDataJSON); + const result = await native.get(normalized); + const response = { + clientDataJSON: base64Url(result.clientDataJSON), + authenticatorData: base64Url(result.authenticatorData), + signature: base64Url(result.signature), + }; + if (result.userHandle) response.userHandle = base64Url(result.userHandle); + return rawIdCredential(result.rawId, response, result.authenticatorAttachment); +} + +function getBackendInfo() { + if (!binding || typeof binding.getBackendInfo !== 'function') { + return { + target: `${process.platform}/${process.arch}`, + backend: 'unavailable', + nativeLoaded: false, + ceremoniesImplemented: false, + platformBrokerAvailable: false, + platformAuthenticatorAvailable: false, + supported: false, + apiVersion: 0, + reason: loadError instanceof Error ? loadError.message : 'native backend did not load', + }; + } + return binding.getBackendInfo(); +} + +module.exports = { + create, + get, + getBackendInfo, + isSupported, + loadError, + _private: { + base64Url, + clientDataJSON, + nativeFileName, + normalizeCreateOptions, + normalizeGetOptions, + resolveNativeRoot, + transportBits, + }, +}; diff --git a/fluxer_desktop/native/webauthn/loader-diagnostics.cjs b/fluxer_desktop/native/webauthn/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/webauthn/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/webauthn/package.json b/fluxer_desktop/native/webauthn/package.json new file mode 100644 index 0000000..60eccd3 --- /dev/null +++ b/fluxer_desktop/native/webauthn/package.json @@ -0,0 +1,29 @@ +{ + "name": "@fluxer/webauthn", + "version": "0.0.0", + "description": "Fluxer-owned WebAuthn native backend", + "private": true, + "license": "AGPL-3.0-or-later", + "main": "index.js", + "types": "index.d.ts", + "cpu": [ + "x64", + "arm64" + ], + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "*.so*", + "webauthn.linux-x64-gnu.node", + "webauthn.linux-arm64-gnu.node", + "webauthn.darwin-x64.node", + "webauthn.darwin-arm64.node", + "webauthn.win32-x64-msvc.node", + "webauthn.win32-arm64-msvc.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "cargo test --manifest-path Cargo.toml && node --test test/*.test.mjs" + } +} diff --git a/fluxer_desktop/native/webauthn/src/common.rs b/fluxer_desktop/native/webauthn/src/common.rs new file mode 100644 index 0000000..643fd17 --- /dev/null +++ b/fluxer_desktop/native/webauthn/src/common.rs @@ -0,0 +1,286 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use napi::Result; +use napi::bindgen_prelude::{Buffer, Error, Status}; + +#[allow(dead_code)] +pub fn buffer_from_bytes(bytes: &[u8]) -> Buffer { + Buffer::from(bytes.to_vec()) +} + +pub fn ceremony_error(prefix: &str, message: &str) -> Error { + Error::new(Status::GenericFailure, format!("{prefix}: {message}")) +} + +pub const CREATE_PREFIX: &str = "WebAuthn registration failed"; +pub const GET_PREFIX: &str = "WebAuthn authentication failed"; + +pub const TRANSPORT_USB: u32 = 0x0000_0001; +#[allow(dead_code)] +pub const TRANSPORT_NFC: u32 = 0x0000_0002; +#[allow(dead_code)] +pub const TRANSPORT_BLE: u32 = 0x0000_0004; +pub const TRANSPORT_INTERNAL: u32 = 0x0000_0010; + +#[allow(dead_code)] +pub const ATTACHMENT_ANY: u32 = 0; +#[allow(dead_code)] +pub const ATTACHMENT_PLATFORM: u32 = 1; +#[allow(dead_code)] +pub const ATTACHMENT_CROSS_PLATFORM: u32 = 2; + +#[allow(dead_code)] +pub const USER_VERIFICATION_REQUIRED: u32 = 1; +#[allow(dead_code)] +pub const USER_VERIFICATION_PREFERRED: u32 = 2; +#[allow(dead_code)] +pub const USER_VERIFICATION_DISCOURAGED: u32 = 3; + +#[allow(dead_code)] +pub const ATTESTATION_NONE: u32 = 1; +#[allow(dead_code)] +pub const ATTESTATION_INDIRECT: u32 = 2; +#[allow(dead_code)] +pub const ATTESTATION_DIRECT: u32 = 3; + +pub const ENTERPRISE_NONE: u32 = 0; +#[allow(dead_code)] +pub const ENTERPRISE_VENDOR_FACILITATED: u32 = 1; + +#[derive(Clone)] +#[allow(dead_code)] +pub struct DescriptorInput { + pub id: Vec, + pub transports: u32, +} + +#[derive(Clone)] +#[allow(dead_code)] +pub struct CreateInput { + pub rp_id: String, + pub rp_name: String, + pub challenge: Vec, + pub user_id: Vec, + pub user_name: String, + pub user_display_name: String, + pub client_data_json: Vec, + pub client_data_hash: Vec, + pub pub_key_algs: Vec, + pub exclude_credentials: Vec, + pub timeout_ms: u32, + pub authenticator_attachment: u32, + pub user_verification: u32, + pub attestation: u32, + pub enterprise_attestation: u32, + pub require_resident_key: bool, + pub prefer_resident_key: bool, + pub window_handle: u64, + pub pin: Option, +} + +#[derive(Clone)] +#[allow(dead_code)] +pub struct GetInput { + pub rp_id: String, + pub challenge: Vec, + pub client_data_json: Vec, + pub client_data_hash: Vec, + pub allow_credentials: Vec, + pub timeout_ms: u32, + pub authenticator_attachment: u32, + pub user_verification: u32, + pub window_handle: u64, + pub pin: Option, +} + +pub struct CreateResult { + pub raw_id: Vec, + pub attestation_object: Vec, + pub client_data_json: Vec, + pub used_transport: u32, +} + +pub struct GetResult { + pub raw_id: Vec, + pub authenticator_data: Vec, + pub signature: Vec, + pub user_handle: Option>, + pub client_data_json: Vec, + pub used_transport: u32, +} + +pub fn attachment_from_transport(transport: u32) -> Option<&'static str> { + if (transport & TRANSPORT_INTERNAL) != 0 { + Some("platform") + } else if transport != 0 { + Some("cross-platform") + } else { + None + } +} + +#[allow(dead_code)] +fn append_cbor_len(out: &mut Vec, major: u8, len: usize) { + if len < 24 { + out.push((major << 5) | (len as u8)); + } else if len <= u8::MAX as usize { + out.push((major << 5) | 24); + out.push(len as u8); + } else if len <= u16::MAX as usize { + out.push((major << 5) | 25); + out.push(((len >> 8) & 0xff) as u8); + out.push((len & 0xff) as u8); + } else if len <= u32::MAX as usize { + out.push((major << 5) | 26); + out.push(((len >> 24) & 0xff) as u8); + out.push(((len >> 16) & 0xff) as u8); + out.push(((len >> 8) & 0xff) as u8); + out.push((len & 0xff) as u8); + } else { + out.push((major << 5) | 27); + out.push(((len >> 56) & 0xff) as u8); + out.push(((len >> 48) & 0xff) as u8); + out.push(((len >> 40) & 0xff) as u8); + out.push(((len >> 32) & 0xff) as u8); + out.push(((len >> 24) & 0xff) as u8); + out.push(((len >> 16) & 0xff) as u8); + out.push(((len >> 8) & 0xff) as u8); + out.push((len & 0xff) as u8); + } +} + +#[allow(dead_code)] +fn append_cbor_text(out: &mut Vec, text: &str) { + let bytes = text.as_bytes(); + append_cbor_len(out, 3, bytes.len()); + out.extend_from_slice(bytes); +} + +#[allow(dead_code)] +fn append_cbor_bytes(out: &mut Vec, bytes: &[u8]) { + append_cbor_len(out, 2, bytes.len()); + out.extend_from_slice(bytes); +} + +#[allow(dead_code)] +pub fn build_attestation_object( + fmt: &str, + auth_data: &[u8], + att_stmt_cbor: &[u8], +) -> Result> { + let mut out = Vec::with_capacity(auth_data.len() + att_stmt_cbor.len() + 32); + + append_cbor_len(&mut out, 5, 3); + append_cbor_text(&mut out, "fmt"); + append_cbor_text(&mut out, fmt); + append_cbor_text(&mut out, "attStmt"); + if att_stmt_cbor.is_empty() { + out.push(0xa0); + } else { + out.extend_from_slice(att_stmt_cbor); + } + append_cbor_text(&mut out, "authData"); + append_cbor_bytes(&mut out, auth_data); + Ok(out) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn attachment_from_transport_matches_legacy() { + assert_eq!( + Some("platform"), + attachment_from_transport(TRANSPORT_INTERNAL) + ); + assert_eq!( + Some("platform"), + attachment_from_transport(TRANSPORT_INTERNAL | TRANSPORT_USB) + ); + assert_eq!( + Some("cross-platform"), + attachment_from_transport(TRANSPORT_USB) + ); + assert_eq!( + Some("cross-platform"), + attachment_from_transport(TRANSPORT_NFC) + ); + assert_eq!( + Some("cross-platform"), + attachment_from_transport(TRANSPORT_BLE) + ); + assert_eq!(None, attachment_from_transport(0)); + } + + #[test] + fn cbor_len_small() { + let mut out = Vec::new(); + append_cbor_len(&mut out, 5, 3); + assert_eq!(out, vec![(5 << 5) | 3]); + } + + #[test] + fn cbor_text_encodes_short_string() { + let mut out = Vec::new(); + append_cbor_text(&mut out, "fmt"); + + assert_eq!(out, vec![0x63, b'f', b'm', b't']); + } + + #[test] + fn cbor_bytes_one_byte_length() { + let payload = vec![0xab; 24]; + let mut out = Vec::new(); + append_cbor_bytes(&mut out, &payload); + assert_eq!(out[0], (2 << 5) | 24); + assert_eq!(out[1], 24); + assert_eq!(&out[2..], &payload[..]); + } + + #[test] + fn cbor_len_two_byte_form() { + let mut out = Vec::new(); + append_cbor_len(&mut out, 2, 0x0123); + assert_eq!(out, vec![(2 << 5) | 25, 0x01, 0x23]); + } + + #[test] + fn cbor_len_four_byte_form() { + let mut out = Vec::new(); + append_cbor_len(&mut out, 2, 0x0001_0203); + assert_eq!(out, vec![(2 << 5) | 26, 0x00, 0x01, 0x02, 0x03]); + } + + #[test] + fn build_attestation_object_empty_att_stmt() { + let auth_data = [0x01u8, 0x02, 0x03]; + let bytes = build_attestation_object("none", &auth_data, &[]).unwrap(); + + let expected: Vec = vec![ + 0xa3, 0x63, b'f', b'm', b't', 0x64, b'n', b'o', b'n', b'e', 0x67, b'a', b't', b't', + b'S', b't', b'm', b't', 0xa0, 0x68, b'a', b'u', b't', b'h', b'D', b'a', b't', b'a', + 0x43, 0x01, 0x02, 0x03, + ]; + assert_eq!(bytes, expected); + } + + #[test] + fn build_attestation_object_passes_through_packed_att_stmt() { + let att_stmt = [ + 0xa2u8, 0x63, b'a', b'l', b'g', 0x26, 0x63, b's', b'i', b'g', 0x41, 0x00, + ]; + let auth_data = [0xaau8; 4]; + let bytes = build_attestation_object("packed", &auth_data, &att_stmt).unwrap(); + + let needle = att_stmt.as_slice(); + let position = bytes + .windows(needle.len()) + .position(|w| w == needle) + .expect("att_stmt must be spliced unmodified"); + + assert!(position >= 8); + let header = &bytes[position - 8..position]; + assert_eq!(header, b"\x67attStmt"); + } +} diff --git a/fluxer_desktop/native/webauthn/src/lib.rs b/fluxer_desktop/native/webauthn/src/lib.rs new file mode 100644 index 0000000..31d04d6 --- /dev/null +++ b/fluxer_desktop/native/webauthn/src/lib.rs @@ -0,0 +1,683 @@ +#![allow(clippy::too_many_lines)] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +use napi::Task; +use napi::bindgen_prelude::{ + AsyncTask, Buffer, Env, Error, JsObjectValue, Object, Result, Status, Unknown, +}; +use napi::{JsValue, ValueType}; +use napi_derive::napi; + +mod common; +use common::{ + CreateInput, CreateResult, DescriptorInput, GetInput, GetResult, attachment_from_transport, +}; + +#[cfg(target_os = "linux")] +mod linux; +#[cfg(target_os = "macos")] +mod macos; +#[cfg(target_os = "windows")] +mod windows; + +#[cfg(target_os = "macos")] +const BACKEND_NAME: &str = "macos-authenticationservices"; +#[cfg(target_os = "windows")] +const BACKEND_NAME: &str = "windows-webauthn"; +#[cfg(target_os = "linux")] +const BACKEND_NAME: &str = "linux-libfido2"; +#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))] +const BACKEND_NAME: &str = "unavailable"; + +#[cfg(target_os = "macos")] +const BACKEND_REASON: &str = "macOS WebAuthn backend (AuthenticationServices). \ + Requires macOS 12.0+ (ASAuthorizationPlatformPublicKeyCredentialProvider, \ + introduced in Monterey). On older macOS the bundled \ + ASAuthorizationPlatform/SecurityKey provider classes are not present and \ + `isSupported()` returns false."; +#[cfg(target_os = "windows")] +const BACKEND_REASON: &str = "Windows WebAuthn broker backend (webauthn.dll). \ + Requires Windows 10 build 18362 (May 2019 / version 1903) or newer; \ + webauthn.dll is delay-loaded via /DELAYLOAD, so the addon still loads on \ + older builds but `isSupported()` returns false there."; +#[cfg(target_os = "linux")] +const BACKEND_REASON: &str = "Linux WebAuthn backend (libfido2). Requires \ + libfido2 >= 1.5.0 (HID transport, U2F fallback disabled) and a CTAP \ + authenticator on USB-HID; `isSupported()` returns false when no \ + authenticator is enumerated."; +#[cfg(not(any(target_os = "macos", target_os = "windows", target_os = "linux")))] +const BACKEND_REASON: &str = "no WebAuthn backend is compiled in for this platform"; + +const TARGET_PLATFORM: &str = if cfg!(target_os = "macos") { + "darwin" +} else if cfg!(target_os = "windows") { + "win32" +} else if cfg!(target_os = "linux") { + "linux" +} else { + "unknown" +}; + +const TARGET_ARCH: &str = if cfg!(target_arch = "x86_64") { + "x64" +} else if cfg!(target_arch = "aarch64") { + "arm64" +} else { + "unknown" +}; + +fn target_string() -> String { + format!("{TARGET_PLATFORM}/{TARGET_ARCH}") +} + +fn platform_broker_available() -> bool { + #[cfg(target_os = "windows")] + { + windows::api_version() > 0 + } + #[cfg(target_os = "linux")] + { + linux::is_supported() + } + #[cfg(target_os = "macos")] + { + macos::is_supported() + } + #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))] + { + false + } +} + +fn platform_authenticator_available() -> bool { + #[cfg(target_os = "windows")] + { + windows::is_user_verifying_platform_authenticator_available() + } + #[cfg(not(target_os = "windows"))] + { + false + } +} + +fn ceremonies_implemented() -> bool { + cfg!(any( + target_os = "macos", + target_os = "windows", + target_os = "linux" + )) +} + +fn is_supported_native() -> bool { + ceremonies_implemented() && platform_broker_available() +} + +fn api_version() -> u32 { + #[cfg(target_os = "windows")] + { + windows::api_version() + } + #[cfg(not(target_os = "windows"))] + { + 0 + } +} + +fn backend_reason() -> String { + #[cfg(target_os = "linux")] + { + format!("{} {}", BACKEND_REASON, linux::support_diagnostics()) + } + #[cfg(not(target_os = "linux"))] + { + BACKEND_REASON.to_owned() + } +} + +#[napi(object, js_name = "WebAuthnBackendInfo")] +pub struct WebAuthnBackendInfoJs { + pub target: String, + pub backend: String, + #[napi(js_name = "nativeLoaded")] + pub native_loaded: bool, + #[napi(js_name = "ceremoniesImplemented")] + pub ceremonies_implemented: bool, + #[napi(js_name = "platformBrokerAvailable")] + pub platform_broker_available: bool, + #[napi(js_name = "platformAuthenticatorAvailable")] + pub platform_authenticator_available: bool, + pub supported: bool, + #[napi(js_name = "apiVersion")] + pub api_version: u32, + pub reason: String, +} + +#[napi(js_name = "getBackendInfo")] +pub fn get_backend_info() -> WebAuthnBackendInfoJs { + WebAuthnBackendInfoJs { + target: target_string(), + backend: BACKEND_NAME.to_owned(), + native_loaded: true, + ceremonies_implemented: ceremonies_implemented(), + platform_broker_available: platform_broker_available(), + platform_authenticator_available: platform_authenticator_available(), + supported: is_supported_native(), + api_version: api_version(), + reason: backend_reason(), + } +} + +pub struct IsSupportedTask; +impl Task for IsSupportedTask { + type Output = bool; + type JsValue = bool; + fn compute(&mut self) -> Result { + Ok(is_supported_native()) + } + fn resolve(&mut self, _env: Env, output: Self::Output) -> Result { + Ok(output) + } +} + +#[napi(js_name = "isSupported")] +pub fn is_supported() -> AsyncTask { + AsyncTask::new(IsSupportedTask) +} + +#[napi(object)] +pub struct CreateCeremonyResultJs { + #[napi(js_name = "rawId")] + pub raw_id: Buffer, + #[napi(js_name = "attestationObject")] + pub attestation_object: Buffer, + #[napi(js_name = "clientDataJSON")] + pub client_data_json: Buffer, + + #[napi(js_name = "authenticatorAttachment")] + pub authenticator_attachment: Option, +} + +#[napi(object)] +pub struct GetCeremonyResultJs { + #[napi(js_name = "rawId")] + pub raw_id: Buffer, + #[napi(js_name = "authenticatorData")] + pub authenticator_data: Buffer, + pub signature: Buffer, + + #[napi(js_name = "userHandle")] + pub user_handle: Option, + #[napi(js_name = "clientDataJSON")] + pub client_data_json: Buffer, + #[napi(js_name = "authenticatorAttachment")] + pub authenticator_attachment: Option, +} + +pub struct CreateTask { + input: CreateInput, +} + +impl Task for CreateTask { + type Output = CreateResult; + type JsValue = CreateCeremonyResultJs; + + fn compute(&mut self) -> Result { + run_create(&mut self.input) + } + + fn resolve(&mut self, _env: Env, output: Self::Output) -> Result { + Ok(CreateCeremonyResultJs { + raw_id: Buffer::from(output.raw_id), + attestation_object: Buffer::from(output.attestation_object), + client_data_json: Buffer::from(output.client_data_json), + authenticator_attachment: attachment_from_transport(output.used_transport) + .map(str::to_owned), + }) + } +} + +pub struct GetTask { + input: GetInput, +} + +impl Task for GetTask { + type Output = GetResult; + type JsValue = GetCeremonyResultJs; + + fn compute(&mut self) -> Result { + run_get(&mut self.input) + } + + fn resolve(&mut self, _env: Env, output: Self::Output) -> Result { + Ok(GetCeremonyResultJs { + raw_id: Buffer::from(output.raw_id), + authenticator_data: Buffer::from(output.authenticator_data), + signature: Buffer::from(output.signature), + user_handle: output.user_handle.map(Buffer::from), + client_data_json: Buffer::from(output.client_data_json), + authenticator_attachment: attachment_from_transport(output.used_transport) + .map(str::to_owned), + }) + } +} + +fn run_create(input: &mut CreateInput) -> Result { + #[cfg(target_os = "windows")] + { + windows::make_credential(input) + } + #[cfg(target_os = "linux")] + { + linux::make_credential(input) + } + #[cfg(target_os = "macos")] + { + macos::make_credential(input) + } + #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))] + { + let _ = input; + Err(common::ceremony_error( + common::CREATE_PREFIX, + "UnknownPlatform", + )) + } +} + +fn run_get(input: &mut GetInput) -> Result { + #[cfg(target_os = "windows")] + { + windows::get_assertion(input) + } + #[cfg(target_os = "linux")] + { + linux::get_assertion(input) + } + #[cfg(target_os = "macos")] + { + macos::get_assertion(input) + } + #[cfg(not(any(target_os = "windows", target_os = "linux", target_os = "macos")))] + { + let _ = input; + Err(common::ceremony_error( + common::GET_PREFIX, + "UnknownPlatform", + )) + } +} + +#[napi(js_name = "create")] +pub fn create(options: Object) -> Result> { + let input = parse_create_input(&options)?; + Ok(AsyncTask::new(CreateTask { input })) +} + +#[napi(js_name = "get")] +pub fn get(options: Object) -> Result> { + let input = parse_get_input(&options)?; + Ok(AsyncTask::new(GetTask { input })) +} + +fn invalid_create_options() -> Error { + Error::new( + Status::InvalidArg, + "invalid WebAuthn registration options".to_owned(), + ) +} + +fn invalid_get_options() -> Error { + Error::new( + Status::InvalidArg, + "invalid WebAuthn assertion options".to_owned(), + ) +} + +fn parse_create_input(object: &Object) -> Result { + let map_err = |_e: Error| invalid_create_options(); + Ok(CreateInput { + rp_id: required_string(object, "rpId").map_err(map_err)?, + rp_name: required_string(object, "rpName").map_err(map_err)?, + challenge: required_buffer(object, "challenge").map_err(map_err)?, + user_id: required_buffer(object, "userId").map_err(map_err)?, + user_name: required_string(object, "userName").map_err(map_err)?, + user_display_name: required_string(object, "userDisplayName").map_err(map_err)?, + client_data_json: required_buffer(object, "clientDataJSON").map_err(map_err)?, + client_data_hash: required_buffer(object, "clientDataHash").map_err(map_err)?, + pub_key_algs: required_alg_array(object).map_err(map_err)?, + exclude_credentials: optional_descriptor_array(object, "excludeCredentials") + .map_err(map_err)?, + timeout_ms: optional_u32(object, "timeout", 0).map_err(map_err)?, + authenticator_attachment: optional_u32( + object, + "authenticatorAttachment", + default_attachment(), + ) + .map_err(map_err)?, + user_verification: optional_u32(object, "userVerification", default_user_verification()) + .map_err(map_err)?, + attestation: optional_u32(object, "attestation", default_attestation()).map_err(map_err)?, + enterprise_attestation: optional_u32( + object, + "enterpriseAttestation", + common::ENTERPRISE_NONE, + ) + .map_err(map_err)?, + require_resident_key: optional_bool(object, "requireResidentKey", false) + .map_err(map_err)?, + prefer_resident_key: optional_bool(object, "preferResidentKey", false).map_err(map_err)?, + window_handle: optional_window_handle(object).map_err(map_err)?, + pin: optional_string(object, "pin").map_err(map_err)?, + }) +} + +fn parse_get_input(object: &Object) -> Result { + let map_err = |_e: Error| invalid_get_options(); + Ok(GetInput { + rp_id: required_string(object, "rpId").map_err(map_err)?, + challenge: required_buffer(object, "challenge").map_err(map_err)?, + client_data_json: required_buffer(object, "clientDataJSON").map_err(map_err)?, + client_data_hash: required_buffer(object, "clientDataHash").map_err(map_err)?, + allow_credentials: optional_descriptor_array(object, "allowCredentials") + .map_err(map_err)?, + timeout_ms: optional_u32(object, "timeout", 0).map_err(map_err)?, + authenticator_attachment: optional_u32( + object, + "authenticatorAttachment", + default_attachment(), + ) + .map_err(map_err)?, + user_verification: optional_u32(object, "userVerification", default_user_verification()) + .map_err(map_err)?, + window_handle: optional_window_handle(object).map_err(map_err)?, + pin: optional_string(object, "pin").map_err(map_err)?, + }) +} + +fn optional_string(object: &Object, key: &str) -> Result> { + let Some(value) = read_unknown(object, key) else { + return Ok(None); + }; + if is_nullish(&value) { + return Ok(None); + } + if value.get_type()? != ValueType::String { + return Err(Error::new( + Status::InvalidArg, + format!("{key} must be a string"), + )); + } + let s: String = unsafe { value.cast::()? }; + if s.is_empty() { Ok(None) } else { Ok(Some(s)) } +} + +fn optional_window_handle(object: &Object) -> Result { + let Some(value) = read_unknown(object, "windowHandle") else { + return Ok(0); + }; + if is_nullish(&value) { + return Ok(0); + } + let bytes = read_buffer_bytes(&value, "windowHandle")?; + if bytes.len() > 8 { + return Err(Error::new( + Status::InvalidArg, + "windowHandle must be at most 8 bytes".to_owned(), + )); + } + let mut padded = [0u8; 8]; + padded[..bytes.len()].copy_from_slice(&bytes); + Ok(u64::from_le_bytes(padded)) +} + +#[cfg(target_os = "windows")] +fn default_attachment() -> u32 { + common::ATTACHMENT_ANY +} +#[cfg(target_os = "windows")] +fn default_user_verification() -> u32 { + common::USER_VERIFICATION_PREFERRED +} +#[cfg(target_os = "windows")] +fn default_attestation() -> u32 { + common::ATTESTATION_NONE +} +#[cfg(not(target_os = "windows"))] +fn default_attachment() -> u32 { + 0 +} +#[cfg(not(target_os = "windows"))] +fn default_user_verification() -> u32 { + 0 +} +#[cfg(not(target_os = "windows"))] +fn default_attestation() -> u32 { + 0 +} + +fn read_unknown<'a>(object: &Object<'a>, key: &str) -> Option> { + object.get::(key).ok().flatten() +} + +fn is_nullish(v: &Unknown<'_>) -> bool { + match v.get_type() { + Ok(t) => t == ValueType::Null || t == ValueType::Undefined, + Err(_) => true, + } +} + +fn required_string(object: &Object, key: &str) -> Result { + let value = read_unknown(object, key) + .ok_or_else(|| Error::new(Status::InvalidArg, format!("missing {key}")))?; + if is_nullish(&value) { + return Err(Error::new(Status::InvalidArg, format!("missing {key}"))); + } + if value.get_type()? != ValueType::String { + return Err(Error::new( + Status::InvalidArg, + format!("{key} must be a string"), + )); + } + let s: String = unsafe { value.cast::()? }; + Ok(s) +} + +fn optional_u32(object: &Object, key: &str, default: u32) -> Result { + let Some(value) = read_unknown(object, key) else { + return Ok(default); + }; + if is_nullish(&value) { + return Ok(default); + } + if value.get_type()? != ValueType::Number { + return Err(Error::new( + Status::InvalidArg, + format!("{key} must be a number"), + )); + } + let n: u32 = unsafe { value.cast::()? }; + Ok(n) +} + +fn optional_bool(object: &Object, key: &str, default: bool) -> Result { + let Some(value) = read_unknown(object, key) else { + return Ok(default); + }; + if is_nullish(&value) { + return Ok(default); + } + if value.get_type()? != ValueType::Boolean { + return Err(Error::new( + Status::InvalidArg, + format!("{key} must be a boolean"), + )); + } + let b: bool = unsafe { value.cast::()? }; + Ok(b) +} + +fn required_buffer(object: &Object, key: &str) -> Result> { + let value = read_unknown(object, key) + .ok_or_else(|| Error::new(Status::InvalidArg, format!("missing {key}")))?; + if is_nullish(&value) { + return Err(Error::new(Status::InvalidArg, format!("missing {key}"))); + } + read_buffer_bytes(&value, key) +} + +fn read_buffer_bytes(value: &Unknown<'_>, key: &str) -> Result> { + let raw = value.raw(); + let raw_env = value.value().env; + let mut is_buffer = false; + let status = unsafe { napi::sys::napi_is_buffer(raw_env, raw, &mut is_buffer) }; + if status != napi::sys::Status::napi_ok || !is_buffer { + return Err(Error::new( + Status::InvalidArg, + format!("{key} must be a Buffer"), + )); + } + let mut data_ptr: *mut std::ffi::c_void = std::ptr::null_mut(); + let mut len: usize = 0; + let status = unsafe { napi::sys::napi_get_buffer_info(raw_env, raw, &mut data_ptr, &mut len) }; + if status != napi::sys::Status::napi_ok { + return Err(Error::new( + Status::InvalidArg, + format!("{key} buffer read failed"), + )); + } + if len == 0 { + return Ok(Vec::new()); + } + if data_ptr.is_null() { + return Err(Error::new( + Status::InvalidArg, + format!("{key} buffer pointer was null"), + )); + } + + let slice = unsafe { std::slice::from_raw_parts(data_ptr.cast::(), len) }; + Ok(slice.to_vec()) +} + +fn required_alg_array(object: &Object) -> Result> { + let Some(value) = read_unknown(object, "pubKeyCredParams") else { + return Err(Error::new( + Status::InvalidArg, + "missing pubKeyCredParams".to_owned(), + )); + }; + if is_nullish(&value) { + return Err(Error::new( + Status::InvalidArg, + "missing pubKeyCredParams".to_owned(), + )); + } + + let raw = value.raw(); + let raw_env = value.value().env; + let mut is_array = false; + let status = unsafe { napi::sys::napi_is_array(raw_env, raw, &mut is_array) }; + if status != napi::sys::Status::napi_ok || !is_array { + return Err(Error::new( + Status::InvalidArg, + "pubKeyCredParams must be an array".to_owned(), + )); + } + let arr: Object = unsafe { value.cast::()? }; + let len = read_array_length(&arr)?; + if len == 0 { + return Err(Error::new( + Status::InvalidArg, + "pubKeyCredParams must not be empty".to_owned(), + )); + } + let mut algs = Vec::with_capacity(len as usize); + for i in 0..len { + let mut elem_raw: napi::sys::napi_value = std::ptr::null_mut(); + let status = unsafe { napi::sys::napi_get_element(raw_env, raw, i, &mut elem_raw) }; + if status != napi::sys::Status::napi_ok { + return Err(Error::new( + Status::InvalidArg, + "pubKeyCredParams element read failed".to_owned(), + )); + } + + let elem_value: Unknown = unsafe { Unknown::from_raw_unchecked(raw_env, elem_raw) }; + if elem_value.get_type()? != ValueType::Object { + return Err(Error::new( + Status::InvalidArg, + "pubKeyCredParams[] must be objects".to_owned(), + )); + } + let elem: Object = unsafe { elem_value.cast::()? }; + algs.push(optional_i32_field(&elem, "alg", 0)?); + } + Ok(algs) +} + +fn optional_i32_field(object: &Object, key: &str, default: i32) -> Result { + let Some(value) = read_unknown(object, key) else { + return Ok(default); + }; + if is_nullish(&value) { + return Ok(default); + } + if value.get_type()? != ValueType::Number { + return Err(Error::new( + Status::InvalidArg, + format!("{key} must be a number"), + )); + } + let n: i32 = unsafe { value.cast::()? }; + Ok(n) +} + +fn optional_descriptor_array(object: &Object, key: &str) -> Result> { + let Some(value) = read_unknown(object, key) else { + return Ok(Vec::new()); + }; + if is_nullish(&value) { + return Ok(Vec::new()); + } + let raw = value.raw(); + let raw_env = value.value().env; + let mut is_array = false; + let status = unsafe { napi::sys::napi_is_array(raw_env, raw, &mut is_array) }; + if status != napi::sys::Status::napi_ok || !is_array { + return Err(Error::new( + Status::InvalidArg, + format!("{key} must be an array"), + )); + } + let arr: Object = unsafe { value.cast::()? }; + let len = read_array_length(&arr)?; + let mut out = Vec::with_capacity(len as usize); + for i in 0..len { + let mut elem_raw: napi::sys::napi_value = std::ptr::null_mut(); + let status = unsafe { napi::sys::napi_get_element(raw_env, raw, i, &mut elem_raw) }; + if status != napi::sys::Status::napi_ok { + return Err(Error::new( + Status::InvalidArg, + format!("{key}[] read failed"), + )); + } + let elem_value: Unknown = unsafe { Unknown::from_raw_unchecked(raw_env, elem_raw) }; + if elem_value.get_type()? != ValueType::Object { + return Err(Error::new( + Status::InvalidArg, + format!("{key}[] must be objects"), + )); + } + let elem: Object = unsafe { elem_value.cast::()? }; + let id = required_buffer(&elem, "id")?; + let transports = optional_u32(&elem, "transports", 0)?; + out.push(DescriptorInput { id, transports }); + } + Ok(out) +} + +fn read_array_length(obj: &Object) -> Result { + let length: u32 = obj.get_named_property::("length")?; + Ok(length) +} diff --git a/fluxer_desktop/native/webauthn/src/linux.rs b/fluxer_desktop/native/webauthn/src/linux.rs new file mode 100644 index 0000000..580acb2 --- /dev/null +++ b/fluxer_desktop/native/webauthn/src/linux.rs @@ -0,0 +1,560 @@ +#![allow(clippy::manual_c_str_literals)] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::ffi::{CStr, CString, c_char, c_int}; +use std::ptr; + +use libfido2_sys as f; +use napi::Result; + +use crate::common::{ + CREATE_PREFIX, CreateInput, CreateResult, GET_PREFIX, GetInput, GetResult, TRANSPORT_USB, + USER_VERIFICATION_REQUIRED, build_attestation_object, ceremony_error, +}; + +const MANIFEST_COUNT: usize = 64; +const SUPPORT_DIAGNOSTIC_DEVICE_LIMIT: usize = 8; + +fn init() { + unsafe { f::fido_init(f::FIDO_DISABLE_U2F_FALLBACK as c_int) }; +} + +pub fn is_supported() -> bool { + init(); + + unsafe { + let infos = f::fido_dev_info_new(MANIFEST_COUNT); + if infos.is_null() { + return false; + } + let mut found: usize = 0; + let rc = f::fido_dev_info_manifest(infos, MANIFEST_COUNT, &mut found); + let mut local = infos; + f::fido_dev_info_free(&mut local, MANIFEST_COUNT); + rc == f::FIDO_OK as c_int && found > 0 + } +} + +fn c_string(ptr: *const c_char) -> Option { + if ptr.is_null() { + return None; + } + let value = unsafe { CStr::from_ptr(ptr) } + .to_string_lossy() + .into_owned(); + if value.is_empty() { None } else { Some(value) } +} + +fn fido_error_message(code: c_int) -> String { + c_string(unsafe { f::fido_strerr(code) }).unwrap_or_else(|| "unknown libfido2 error".to_owned()) +} + +fn fido_call_detail(operation: &str, code: c_int) -> String { + format!("{operation} rc={code} error={}", fido_error_message(code)) +} + +fn describe_device_info(info: *const f::fido_dev_info_t) -> String { + if info.is_null() { + return "deviceInfo=null".to_owned(); + } + let path = + c_string(unsafe { f::fido_dev_info_path(info) }).unwrap_or_else(|| "".to_owned()); + let manufacturer = c_string(unsafe { f::fido_dev_info_manufacturer_string(info) }) + .unwrap_or_else(|| "".to_owned()); + let product_name = c_string(unsafe { f::fido_dev_info_product_string(info) }) + .unwrap_or_else(|| "".to_owned()); + let vendor_id = unsafe { f::fido_dev_info_vendor(info) } as u16; + let product_id = unsafe { f::fido_dev_info_product(info) } as u16; + format!( + "path={path} vendorId=0x{vendor_id:04x} productId=0x{product_id:04x} manufacturer={manufacturer:?} product={product_name:?}" + ) +} + +pub fn support_diagnostics() -> String { + init(); + unsafe { + let infos = f::fido_dev_info_new(MANIFEST_COUNT); + if infos.is_null() { + return "Probe: fido_dev_info_new returned null.".to_owned(); + } + let mut infos_owned = infos; + let mut found: usize = 0; + let rc = f::fido_dev_info_manifest(infos, MANIFEST_COUNT, &mut found); + let detail = if rc != f::FIDO_OK as c_int { + format!("Probe: {}.", fido_call_detail("fido_dev_info_manifest", rc)) + } else if found == 0 { + "Probe: fido_dev_info_manifest found 0 devices. Check that a USB-HID CTAP security key is connected and that udev/hidraw permissions allow this user to read it.".to_owned() + } else { + let mut devices = Vec::new(); + for index in 0..found.min(SUPPORT_DIAGNOSTIC_DEVICE_LIMIT) { + devices.push(describe_device_info(f::fido_dev_info_ptr(infos, index))); + } + let suffix = if found > SUPPORT_DIAGNOSTIC_DEVICE_LIMIT { + format!( + "; truncated={} more", + found - SUPPORT_DIAGNOSTIC_DEVICE_LIMIT + ) + } else { + String::new() + }; + format!( + "Probe: fido_dev_info_manifest found {found} device(s): [{}]{suffix}.", + devices.join("; ") + ) + }; + f::fido_dev_info_free(&mut infos_owned, MANIFEST_COUNT); + detail + } +} + +fn uv_opt(requirement: u32, pin: Option<&CString>) -> f::fido_opt_t { + if pin.is_some() { + return f::fido_opt_t_FIDO_OPT_OMIT; + } + match requirement { + USER_VERIFICATION_REQUIRED => f::fido_opt_t_FIDO_OPT_TRUE, + _ => f::fido_opt_t_FIDO_OPT_OMIT, + } +} + +fn pin_cstring(pin: Option<&str>, prefix: &str) -> Result> { + let Some(pin) = pin.filter(|value| !value.is_empty()) else { + return Ok(None); + }; + CString::new(pin) + .map(Some) + .map_err(|_| ceremony_error(prefix, "PinInvalid (PIN contains an invalid character)")) +} + +fn pin_ptr(pin: Option<&CString>) -> *const c_char { + pin.map_or(ptr::null(), |value| value.as_ptr()) +} + +fn dev_has_pin(dev: *mut f::fido_dev_t) -> bool { + unsafe { f::fido_dev_has_pin(dev) } +} + +fn dev_has_uv(dev: *mut f::fido_dev_t) -> bool { + unsafe { f::fido_dev_has_uv(dev) } +} + +fn pin_retry_count(dev: *mut f::fido_dev_t) -> Option { + let mut retries: c_int = 0; + let rc = unsafe { f::fido_dev_get_retry_count(dev, &mut retries) }; + if rc == f::FIDO_OK as c_int { + Some(retries) + } else { + None + } +} + +fn ceremony_failure(prefix: &str, dev: &DevGuard, operation: &str, rc: c_int) -> napi::Error { + if rc == f::FIDO_ERR_PIN_REQUIRED as c_int || rc == f::FIDO_ERR_PIN_AUTH_INVALID as c_int { + return ceremony_error(prefix, "PinRequired"); + } + if rc == f::FIDO_ERR_PIN_INVALID as c_int { + let detail = match pin_retry_count(dev.0) { + Some(retries) => format!("PinInvalid retriesRemaining={retries}"), + None => "PinInvalid".to_owned(), + }; + return ceremony_error(prefix, &detail); + } + if rc == f::FIDO_ERR_PIN_AUTH_BLOCKED as c_int { + return ceremony_error( + prefix, + "PinAuthBlocked (too many failed attempts; unplug and reinsert the security key)", + ); + } + if rc == f::FIDO_ERR_PIN_BLOCKED as c_int { + return ceremony_error( + prefix, + "PinBlocked (the security key PIN is locked; the key must be reset)", + ); + } + if rc == f::FIDO_ERR_PIN_NOT_SET as c_int { + return ceremony_error( + prefix, + "PinNotSet (the security key requires a PIN to be configured first)", + ); + } + if rc == f::FIDO_ERR_UV_BLOCKED as c_int || rc == f::FIDO_ERR_UV_INVALID as c_int { + return ceremony_error( + prefix, + "UserVerificationBlocked (on-key user verification failed; use the key PIN or reset the key)", + ); + } + if rc == f::FIDO_ERR_ACTION_TIMEOUT as c_int || rc == f::FIDO_ERR_USER_ACTION_TIMEOUT as c_int { + return ceremony_error(prefix, "Timeout (the security key was not touched in time)"); + } + if rc == f::FIDO_ERR_OPERATION_DENIED as c_int || rc == f::FIDO_ERR_KEEPALIVE_CANCEL as c_int { + return ceremony_error( + prefix, + "NotAllowed (the request was denied on the security key)", + ); + } + if rc == f::FIDO_ERR_UNSUPPORTED_OPTION as c_int { + return ceremony_error( + prefix, + "UnsupportedOption (the security key does not support the requested options)", + ); + } + let message = format!("Fido2CallFailed ({})", fido_call_detail(operation, rc)); + ceremony_error(prefix, &message) +} + +fn selected_cose_alg(algs: &[i32]) -> c_int { + for &alg in algs { + if alg == f::COSE_ES256 + || alg == f::COSE_RS256 + || alg == f::COSE_EDDSA + || alg == f::COSE_ES384 + { + return alg; + } + } + f::COSE_ES256 +} + +struct DevGuard(*mut f::fido_dev_t); +impl Drop for DevGuard { + fn drop(&mut self) { + if !self.0.is_null() { + unsafe { + f::fido_dev_close(self.0); + let mut p = self.0; + f::fido_dev_free(&mut p); + } + } + } +} + +fn open_first_device(timeout_ms: u32, prefix: &str) -> Result { + init(); + + unsafe { + let infos = f::fido_dev_info_new(MANIFEST_COUNT); + if infos.is_null() { + return Err(ceremony_error(prefix, "OutOfMemory")); + } + let mut infos_owned = infos; + let mut found: usize = 0; + let rc = f::fido_dev_info_manifest(infos, MANIFEST_COUNT, &mut found); + if rc != f::FIDO_OK as c_int { + f::fido_dev_info_free(&mut infos_owned, MANIFEST_COUNT); + let message = format!( + "Fido2CallFailed ({})", + fido_call_detail("fido_dev_info_manifest", rc) + ); + return Err(ceremony_error(prefix, &message)); + } + if found == 0 { + f::fido_dev_info_free(&mut infos_owned, MANIFEST_COUNT); + return Err(ceremony_error( + prefix, + "NoAuthenticator (fido_dev_info_manifest found 0 devices; check USB-HID CTAP authenticator presence and udev/hidraw permissions)", + )); + } + + let mut opened: Option<*mut f::fido_dev_t> = None; + let mut open_failures: Vec = Vec::new(); + for index in 0..found { + let info = f::fido_dev_info_ptr(infos, index); + if info.is_null() { + continue; + } + let path = f::fido_dev_info_path(info); + if path.is_null() { + continue; + } + let dev = f::fido_dev_new(); + if dev.is_null() { + f::fido_dev_info_free(&mut infos_owned, MANIFEST_COUNT); + return Err(ceremony_error(prefix, "OutOfMemory")); + } + let open_rc = f::fido_dev_open(dev, path); + if open_rc == f::FIDO_OK as c_int { + if timeout_ms > 0 { + let clamped: c_int = timeout_ms.min(c_int::MAX as u32) as c_int; + let _ = f::fido_dev_set_timeout(dev, clamped); + } + opened = Some(dev); + break; + } + open_failures.push(format!( + "{} ({})", + describe_device_info(info), + fido_call_detail("fido_dev_open", open_rc) + )); + let mut p = dev; + f::fido_dev_free(&mut p); + } + + f::fido_dev_info_free(&mut infos_owned, MANIFEST_COUNT); + opened.map(DevGuard).ok_or_else(|| { + let message = format!( + "NoAuthenticator (manifest found {found} device(s), but none opened: {})", + open_failures.join("; ") + ); + ceremony_error(prefix, &message) + }) + } +} + +fn check(code: c_int, prefix: &str) -> Result<()> { + if code == f::FIDO_OK as c_int { + Ok(()) + } else { + let message = format!("Fido2CallFailed ({})", fido_call_detail("libfido2", code)); + Err(ceremony_error(prefix, &message)) + } +} + +fn copy_bytes(ptr: *const u8, len: usize) -> Vec { + if len == 0 || ptr.is_null() { + return Vec::new(); + } + + let slice = unsafe { std::slice::from_raw_parts(ptr, len) }; + slice.to_vec() +} + +pub fn make_credential(input: &mut CreateInput) -> Result { + let dev = open_first_device(input.timeout_ms, CREATE_PREFIX)?; + let pin = pin_cstring(input.pin.as_deref(), CREATE_PREFIX)?; + if pin.is_none() && dev_has_pin(dev.0) && !dev_has_uv(dev.0) { + return Err(ceremony_error(CREATE_PREFIX, "PinRequired")); + } + + unsafe { + let cred = f::fido_cred_new(); + if cred.is_null() { + return Err(ceremony_error(CREATE_PREFIX, "OutOfMemory")); + } + struct CredGuard(*mut f::fido_cred_t); + impl Drop for CredGuard { + fn drop(&mut self) { + let mut p = self.0; + unsafe { f::fido_cred_free(&mut p) }; + } + } + let cred_owned = CredGuard(cred); + + check( + f::fido_cred_set_type(cred, selected_cose_alg(&input.pub_key_algs)), + CREATE_PREFIX, + )?; + check( + f::fido_cred_set_clientdata( + cred, + input.client_data_json.as_ptr(), + input.client_data_json.len(), + ), + CREATE_PREFIX, + )?; + let rp_id_c = CString::new(input.rp_id.clone()) + .map_err(|_| ceremony_error(CREATE_PREFIX, "InvalidRpId"))?; + let rp_name_c = CString::new(input.rp_name.clone()) + .map_err(|_| ceremony_error(CREATE_PREFIX, "InvalidRpName"))?; + check( + f::fido_cred_set_rp(cred, rp_id_c.as_ptr(), rp_name_c.as_ptr()), + CREATE_PREFIX, + )?; + let user_name_c = CString::new(input.user_name.clone()) + .map_err(|_| ceremony_error(CREATE_PREFIX, "InvalidUserName"))?; + let display_c = CString::new(input.user_display_name.clone()) + .map_err(|_| ceremony_error(CREATE_PREFIX, "InvalidDisplayName"))?; + check( + f::fido_cred_set_user( + cred, + input.user_id.as_ptr(), + input.user_id.len(), + user_name_c.as_ptr(), + display_c.as_ptr(), + ptr::null(), + ), + CREATE_PREFIX, + )?; + if input.require_resident_key { + check( + f::fido_cred_set_rk(cred, f::fido_opt_t_FIDO_OPT_TRUE), + CREATE_PREFIX, + )?; + } + check( + f::fido_cred_set_uv(cred, uv_opt(input.user_verification, pin.as_ref())), + CREATE_PREFIX, + )?; + for credential in &input.exclude_credentials { + check( + f::fido_cred_exclude(cred, credential.id.as_ptr(), credential.id.len()), + CREATE_PREFIX, + )?; + } + + let rc = f::fido_dev_make_cred(dev.0, cred, pin_ptr(pin.as_ref())); + if rc != f::FIDO_OK as c_int { + let _ = f::fido_dev_cancel(dev.0); + return Err(ceremony_failure( + CREATE_PREFIX, + &dev, + "fido_dev_make_cred", + rc, + )); + } + + let raw_ptr = f::fido_cred_authdata_raw_ptr(cred); + let auth_data = if !raw_ptr.is_null() { + let len = f::fido_cred_authdata_raw_len(cred); + copy_bytes(raw_ptr, len) + } else { + let ptr = f::fido_cred_authdata_ptr(cred); + let len = f::fido_cred_authdata_len(cred); + copy_bytes(ptr, len) + }; + let att_stmt = copy_bytes( + f::fido_cred_attstmt_ptr(cred), + f::fido_cred_attstmt_len(cred), + ); + let fmt_ptr = f::fido_cred_fmt(cred); + let fmt = if fmt_ptr.is_null() { + "none".to_owned() + } else { + CStr::from_ptr(fmt_ptr).to_string_lossy().into_owned() + }; + let attestation_object = build_attestation_object(&fmt, &auth_data, &att_stmt)?; + + let raw_id = copy_bytes(f::fido_cred_id_ptr(cred), f::fido_cred_id_len(cred)); + let client_data_json = input.client_data_json.clone(); + + drop(cred_owned); + drop(dev); + + Ok(CreateResult { + raw_id, + attestation_object, + client_data_json, + used_transport: TRANSPORT_USB, + }) + } +} + +pub fn get_assertion(input: &mut GetInput) -> Result { + let dev = open_first_device(input.timeout_ms, GET_PREFIX)?; + let pin = pin_cstring(input.pin.as_deref(), GET_PREFIX)?; + if pin.is_none() + && input.user_verification == USER_VERIFICATION_REQUIRED + && dev_has_pin(dev.0) + && !dev_has_uv(dev.0) + { + return Err(ceremony_error(GET_PREFIX, "PinRequired")); + } + + unsafe { + let assertion = f::fido_assert_new(); + if assertion.is_null() { + return Err(ceremony_error(GET_PREFIX, "OutOfMemory")); + } + struct AssertGuard(*mut f::fido_assert_t); + impl Drop for AssertGuard { + fn drop(&mut self) { + let mut p = self.0; + unsafe { f::fido_assert_free(&mut p) }; + } + } + let assert_owned = AssertGuard(assertion); + + check( + f::fido_assert_set_clientdata( + assertion, + input.client_data_json.as_ptr(), + input.client_data_json.len(), + ), + GET_PREFIX, + )?; + let rp_id_c = CString::new(input.rp_id.clone()) + .map_err(|_| ceremony_error(GET_PREFIX, "InvalidRpId"))?; + check( + f::fido_assert_set_rp(assertion, rp_id_c.as_ptr()), + GET_PREFIX, + )?; + check( + f::fido_assert_set_up(assertion, f::fido_opt_t_FIDO_OPT_TRUE), + GET_PREFIX, + )?; + check( + f::fido_assert_set_uv(assertion, uv_opt(input.user_verification, pin.as_ref())), + GET_PREFIX, + )?; + for credential in &input.allow_credentials { + check( + f::fido_assert_allow_cred(assertion, credential.id.as_ptr(), credential.id.len()), + GET_PREFIX, + )?; + } + + let rc = f::fido_dev_get_assert(dev.0, assertion, pin_ptr(pin.as_ref())); + if rc != f::FIDO_OK as c_int { + let _ = f::fido_dev_cancel(dev.0); + return Err(ceremony_failure( + GET_PREFIX, + &dev, + "fido_dev_get_assert", + rc, + )); + } + if f::fido_assert_count(assertion) == 0 { + return Err(ceremony_error(GET_PREFIX, "InvalidWebAuthnResult")); + } + + let raw_ptr = f::fido_assert_authdata_raw_ptr(assertion, 0); + let auth_data = if !raw_ptr.is_null() { + let len = f::fido_assert_authdata_raw_len(assertion, 0); + copy_bytes(raw_ptr, len) + } else { + let ptr = f::fido_assert_authdata_ptr(assertion, 0); + let len = f::fido_assert_authdata_len(assertion, 0); + copy_bytes(ptr, len) + }; + let user_id_len = f::fido_assert_user_id_len(assertion, 0); + let user_handle = if user_id_len > 0 { + Some(copy_bytes( + f::fido_assert_user_id_ptr(assertion, 0), + user_id_len, + )) + } else { + None + }; + + let mut raw_id = copy_bytes( + f::fido_assert_id_ptr(assertion, 0), + f::fido_assert_id_len(assertion, 0), + ); + if raw_id.is_empty() && input.allow_credentials.len() == 1 { + raw_id = input.allow_credentials[0].id.clone(); + } + if raw_id.is_empty() { + return Err(ceremony_error( + GET_PREFIX, + "InvalidWebAuthnResult (authenticator omitted the credential id)", + )); + } + let signature = copy_bytes( + f::fido_assert_sig_ptr(assertion, 0), + f::fido_assert_sig_len(assertion, 0), + ); + let client_data_json = input.client_data_json.clone(); + + drop(assert_owned); + drop(dev); + + Ok(GetResult { + raw_id, + authenticator_data: auth_data, + signature, + user_handle, + client_data_json, + used_transport: TRANSPORT_USB, + }) + } +} diff --git a/fluxer_desktop/native/webauthn/src/macos.rs b/fluxer_desktop/native/webauthn/src/macos.rs new file mode 100644 index 0000000..c0a34bf --- /dev/null +++ b/fluxer_desktop/native/webauthn/src/macos.rs @@ -0,0 +1,1110 @@ +#![allow(non_snake_case)] + +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::ptr; +use std::sync::Mutex; +use std::sync::mpsc::{Receiver, Sender, channel}; + +use dispatch2::{ + DispatchQueue, DispatchRetained, DispatchSemaphore, DispatchTime, MainThreadBound, WaitError, +}; +use napi::Result; +use objc2::rc::Retained; +use objc2::runtime::{AnyClass, AnyObject, NSObject, NSObjectProtocol, ProtocolObject, Sel}; +use objc2::{ + AllocAnyThread, ClassType, DefinedClass, MainThreadMarker, MainThreadOnly, define_class, + msg_send, sel, +}; +use objc2_app_kit::NSApplication; +use objc2_authentication_services::{ + ASAuthorization, ASAuthorizationController, ASAuthorizationControllerDelegate, + ASAuthorizationControllerPresentationContextProviding, + ASAuthorizationPlatformPublicKeyCredentialDescriptor, + ASAuthorizationPlatformPublicKeyCredentialProvider, + ASAuthorizationPublicKeyCredentialAssertionRequest, + ASAuthorizationPublicKeyCredentialAttachment, + ASAuthorizationPublicKeyCredentialAttestationKindDirect, + ASAuthorizationPublicKeyCredentialAttestationKindIndirect, + ASAuthorizationPublicKeyCredentialAttestationKindNone, + ASAuthorizationPublicKeyCredentialParameters, + ASAuthorizationPublicKeyCredentialRegistrationRequest, + ASAuthorizationPublicKeyCredentialUserVerificationPreferenceDiscouraged, + ASAuthorizationPublicKeyCredentialUserVerificationPreferencePreferred, + ASAuthorizationPublicKeyCredentialUserVerificationPreferenceRequired, ASAuthorizationRequest, + ASAuthorizationSecurityKeyPublicKeyCredentialDescriptor, + ASAuthorizationSecurityKeyPublicKeyCredentialDescriptorTransportBluetooth, + ASAuthorizationSecurityKeyPublicKeyCredentialDescriptorTransportNFC, + ASAuthorizationSecurityKeyPublicKeyCredentialDescriptorTransportUSB, + ASAuthorizationSecurityKeyPublicKeyCredentialProvider, + ASAuthorizationSecurityKeyPublicKeyCredentialRegistrationRequest, ASCOSEAlgorithmIdentifier, + ASPresentationAnchor, +}; +use objc2_foundation::{ + NSArray, NSCopying, NSData, NSError, NSMutableArray, NSNumber, NSString, NSThread, +}; + +use crate::common::{ + ATTACHMENT_CROSS_PLATFORM, ATTACHMENT_PLATFORM, CREATE_PREFIX, CreateInput, CreateResult, + DescriptorInput, GET_PREFIX, GetInput, GetResult, TRANSPORT_INTERNAL, TRANSPORT_USB, + ceremony_error, +}; + +#[allow(dead_code)] +const _: u32 = ATTACHMENT_PLATFORM + ATTACHMENT_CROSS_PLATFORM; + +pub fn is_supported() -> bool { + AnyClass::get(c"ASAuthorizationPlatformPublicKeyCredentialProvider").is_some() + && AnyClass::get(c"ASAuthorizationSecurityKeyPublicKeyCredentialProvider").is_some() +} + +pub fn make_credential(input: &mut CreateInput) -> Result { + if NSThread::isMainThread_class() { + return Err(ceremony_error( + CREATE_PREFIX, + "WebAuthn registration must run from native async work, not the main thread", + )); + } + match run_create(input) { + Ok(out) => Ok(out), + Err(msg) => Err(ceremony_error(CREATE_PREFIX, &msg)), + } +} + +pub fn get_assertion(input: &mut GetInput) -> Result { + if NSThread::isMainThread_class() { + return Err(ceremony_error( + GET_PREFIX, + "WebAuthn authentication must run from native async work, not the main thread", + )); + } + match run_get(input) { + Ok(out) => Ok(out), + Err(msg) => Err(ceremony_error(GET_PREFIX, &msg)), + } +} + +enum CompletionPayload { + Create { + raw_id: Vec, + attestation_object: Vec, + attachment: AttachmentKind, + }, + Get { + raw_id: Vec, + authenticator_data: Vec, + signature: Vec, + user_handle: Option>, + attachment: AttachmentKind, + }, + Error(String), +} + +#[derive(Copy, Clone)] +enum AttachmentKind { + Platform, + CrossPlatform, +} + +impl AttachmentKind { + fn transport_bit(self) -> u32 { + match self { + AttachmentKind::Platform => TRANSPORT_INTERNAL, + AttachmentKind::CrossPlatform => TRANSPORT_USB, + } + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +enum CeremonyKind { + Create, + Get, +} + +struct DelegateIvars { + semaphore: DispatchRetained, + sender: Mutex>>, + kind: CeremonyKind, +} + +define_class!( + #[unsafe(super = NSObject)] + #[thread_kind = MainThreadOnly] + #[name = "FluxerWebAuthnDelegate"] + #[ivars = DelegateIvars] + struct WebAuthnDelegate; + + unsafe impl NSObjectProtocol for WebAuthnDelegate {} + + unsafe impl ASAuthorizationControllerDelegate for WebAuthnDelegate { + #[unsafe(method(authorizationController:didCompleteWithAuthorization:))] + + unsafe fn did_complete_with_authorization( + &self, + _controller: &ASAuthorizationController, + authorization: &ASAuthorization, + ) { + let credential = unsafe { authorization.credential() }; + let credential_obj: &AnyObject = + unsafe { &*(&*credential as *const _ as *const AnyObject) }; + let payload = match self.ivars().kind { + CeremonyKind::Create => extract_registration(credential_obj), + CeremonyKind::Get => extract_assertion(credential_obj), + }; + self.deliver(payload); + } + + #[unsafe(method(authorizationController:didCompleteWithError:))] + + unsafe fn did_complete_with_error( + &self, + _controller: &ASAuthorizationController, + error: &NSError, + ) { + self.deliver(CompletionPayload::Error(format_ns_error(error))); + } + } + + unsafe impl ASAuthorizationControllerPresentationContextProviding for WebAuthnDelegate { + #[unsafe(method_id(presentationAnchorForAuthorizationController:))] + unsafe fn presentation_anchor( + &self, + _controller: &ASAuthorizationController, + ) -> Retained { + match MainThreadMarker::new() { + Some(mtm) => presentation_anchor(mtm), + None => placeholder_presentation_anchor(), + } + } + } +); + +impl WebAuthnDelegate { + fn new( + mtm: MainThreadMarker, + kind: CeremonyKind, + sender: Sender, + semaphore: DispatchRetained, + ) -> Retained { + let ivars = DelegateIvars { + semaphore, + sender: Mutex::new(Some(sender)), + kind, + }; + let this = Self::alloc(mtm).set_ivars(ivars); + unsafe { msg_send![super(this), init] } + } + + fn deliver(&self, payload: CompletionPayload) { + let Ok(mut slot) = self.ivars().sender.lock() else { + self.ivars().semaphore.signal(); + return; + }; + if let Some(sender) = slot.take() { + let _ = sender.send(payload); + } + self.ivars().semaphore.signal(); + } +} + +fn presentation_anchor(mtm: MainThreadMarker) -> Retained { + let app = NSApplication::sharedApplication(mtm); + let key_window: Option> = app.keyWindow(); + if let Some(window) = key_window { + return unsafe { Retained::cast_unchecked(window) }; + } + let windows = app.windows(); + if let Some(first) = windows.firstObject() { + return unsafe { Retained::cast_unchecked(first) }; + } + + placeholder_presentation_anchor() +} + +fn placeholder_presentation_anchor() -> Retained { + let obj = NSObject::new(); + + unsafe { Retained::cast_unchecked(obj) } +} + +#[derive(Default)] +struct ControllerOverrideIvars { + client_data_hash: Option>, + supported_algorithms: Option>>, + + exclude_credentials: Option>>, + require_resident_key: bool, + is_create: bool, +} + +define_class!( + + + + + + + #[unsafe(super = ASAuthorizationController)] + #[name = "FluxerWebAuthnController"] + #[ivars = core::cell::RefCell] + struct ControllerOverride; + + unsafe impl NSObjectProtocol for ControllerOverride {} + + + + + + + + + + + + + + + + + + + + impl ControllerOverride { + #[unsafe(method(_requestContextWithRequests:error:))] + + unsafe fn request_context_with_requests_error( + &self, + requests: *mut AnyObject, + error: *mut *mut NSError, + ) -> *mut AnyObject { + let context: *mut AnyObject = unsafe { + msg_send![super(self), _requestContextWithRequests: requests, error: error] + }; + if context.is_null() { + return context; + } + let context_ref: &AnyObject = unsafe { &*context }; + + let ivars = self.ivars().borrow(); + let is_create = ivars.is_create; + let mut security_key = false; + let mut options: *mut AnyObject = ptr::null_mut(); + + unsafe { + if is_create { + if responds_to(context_ref, sel!(platformKeyCredentialCreationOptions)) { + options = msg_send![context_ref, platformKeyCredentialCreationOptions]; + } + if options.is_null() + && responds_to(context_ref, sel!(securityKeyCredentialCreationOptions)) + { + options = msg_send![context_ref, securityKeyCredentialCreationOptions]; + security_key = true; + } + } else { + if responds_to(context_ref, sel!(platformKeyCredentialAssertionOptions)) { + options = msg_send![context_ref, platformKeyCredentialAssertionOptions]; + } + if options.is_null() + && responds_to(context_ref, sel!(securityKeyCredentialAssertionOptions)) + { + options = msg_send![context_ref, securityKeyCredentialAssertionOptions]; + } + } + } + + if options.is_null() { + return context; + } + let options_ref: &AnyObject = unsafe { &*options }; + + if let Some(hash) = &ivars.client_data_hash { + + + + + + + unsafe { + if responds_to(options_ref, sel!(setClientDataHash:)) { + let _: () = msg_send![options_ref, setClientDataHash: &**hash]; + } + if responds_to(options_ref, sel!(setChallenge:)) { + let nil_data: *mut NSData = ptr::null_mut(); + let _: () = msg_send![options_ref, setChallenge: nil_data]; + } + } + } + + if is_create { + if let Some(algs) = &ivars.supported_algorithms { + + + + unsafe { + if responds_to(options_ref, sel!(setSupportedAlgorithmIdentifiers:)) { + let _: () = msg_send![ + options_ref, + setSupportedAlgorithmIdentifiers: &**algs + ]; + } + } + } + if !security_key { + + + + + unsafe { + if responds_to(options_ref, sel!(setShouldRequireResidentKey:)) { + let flag: bool = ivars.require_resident_key; + let _: () = + msg_send![options_ref, setShouldRequireResidentKey: flag]; + } + } + } + if let Some(excluded) = &ivars.exclude_credentials { + + + + unsafe { + if responds_to(options_ref, sel!(setExcludedCredentials:)) + && excluded.count() > 0 + { + let _: () = + msg_send![options_ref, setExcludedCredentials: &**excluded]; + } + } + } + } + + context + } + } +); + +impl ControllerOverride { + fn new( + requests: &NSArray, + ivars: ControllerOverrideIvars, + ) -> Retained { + let this = Self::alloc().set_ivars(core::cell::RefCell::new(ivars)); + unsafe { msg_send![super(this), initWithAuthorizationRequests: requests] } + } +} + +unsafe fn responds_to(obj: &AnyObject, sel: Sel) -> bool { + unsafe { msg_send![obj, respondsToSelector: sel] } +} + +fn controller_private_context_selector_available() -> bool { + let cls = ::class(); + unsafe { msg_send![cls, instancesRespondToSelector: sel!(_requestContextWithRequests:error:)] } +} + +fn ns_string(value: &str) -> Retained { + NSString::from_str(value) +} + +fn ns_data(bytes: &[u8]) -> Retained { + NSData::with_bytes(bytes) +} + +fn copy_ns_string(value: &NSString) -> Retained { + value.copy() +} + +fn user_verification_value(value: u32) -> Retained { + let opt = match value { + 1 => unsafe { ASAuthorizationPublicKeyCredentialUserVerificationPreferenceRequired }, + 3 => unsafe { ASAuthorizationPublicKeyCredentialUserVerificationPreferenceDiscouraged }, + _ => unsafe { ASAuthorizationPublicKeyCredentialUserVerificationPreferencePreferred }, + }; + opt.map(copy_ns_string).unwrap_or_else(|| ns_string("")) +} + +fn attestation_value(value: u32, security_key: bool) -> Retained { + let opt = if !security_key { + unsafe { ASAuthorizationPublicKeyCredentialAttestationKindNone } + } else if value == 3 { + unsafe { ASAuthorizationPublicKeyCredentialAttestationKindDirect } + } else if value == 2 { + unsafe { ASAuthorizationPublicKeyCredentialAttestationKindIndirect } + } else { + unsafe { ASAuthorizationPublicKeyCredentialAttestationKindNone } + }; + opt.map(copy_ns_string).unwrap_or_else(|| ns_string("")) +} + +fn credential_parameters_array( + algs: &[i32], +) -> Retained> { + let mut params: Vec> = algs + .iter() + .map(|alg| unsafe { + ASAuthorizationPublicKeyCredentialParameters::initWithAlgorithm( + ASAuthorizationPublicKeyCredentialParameters::alloc(), + *alg as ASCOSEAlgorithmIdentifier, + ) + }) + .collect(); + if params.is_empty() { + params.push(unsafe { + ASAuthorizationPublicKeyCredentialParameters::initWithAlgorithm( + ASAuthorizationPublicKeyCredentialParameters::alloc(), + objc2_authentication_services::ASCOSEAlgorithmIdentifierES256, + ) + }); + } + NSArray::from_retained_slice(¶ms) +} + +fn supported_algorithm_numbers(algs: &[i32]) -> Retained> { + let mut nums: Vec> = algs + .iter() + .map(|a| NSNumber::new_isize(*a as isize)) + .collect(); + if nums.is_empty() { + nums.push(NSNumber::new_isize( + objc2_authentication_services::ASCOSEAlgorithmIdentifierES256, + )); + } + NSArray::from_retained_slice(&nums) +} + +fn security_key_transport_array(transports: u32) -> Retained> { + let mut list: Vec> = Vec::new(); + if transports & 0x0000_0001 != 0 { + list.push(copy_ns_string(unsafe { + ASAuthorizationSecurityKeyPublicKeyCredentialDescriptorTransportUSB + })); + } + if transports & 0x0000_0002 != 0 { + list.push(copy_ns_string(unsafe { + ASAuthorizationSecurityKeyPublicKeyCredentialDescriptorTransportNFC + })); + } + if transports & 0x0000_0004 != 0 { + list.push(copy_ns_string(unsafe { + ASAuthorizationSecurityKeyPublicKeyCredentialDescriptorTransportBluetooth + })); + } + NSArray::from_retained_slice(&list) +} + +fn platform_descriptor_array( + descriptors: &[DescriptorInput], +) -> Retained> { + let entries: Vec> = descriptors + .iter() + .map(|d| { + let data = ns_data(&d.id); + unsafe { + ASAuthorizationPlatformPublicKeyCredentialDescriptor::initWithCredentialID( + ASAuthorizationPlatformPublicKeyCredentialDescriptor::alloc(), + &data, + ) + } + }) + .collect(); + NSArray::from_retained_slice(&entries) +} + +fn security_key_descriptor_array( + descriptors: &[DescriptorInput], +) -> Retained> { + let entries: Vec> = + descriptors + .iter() + .map(|d| { + let data = ns_data(&d.id); + let transports = security_key_transport_array(d.transports); + unsafe { + ASAuthorizationSecurityKeyPublicKeyCredentialDescriptor::initWithCredentialID_transports( + ASAuthorizationSecurityKeyPublicKeyCredentialDescriptor::alloc(), + &data, + &transports, + ) + } + }) + .collect(); + NSArray::from_retained_slice(&entries) +} + +fn apply_registration_setup( + request: &ProtocolObject, + user_verification: u32, + attestation: u32, + security_key: bool, + credential_parameters: Option<&NSArray>, +) { + unsafe { + let uv = user_verification_value(user_verification); + request.setUserVerificationPreference(&uv); + let att = attestation_value(attestation, security_key); + request.setAttestationPreference(&att); + } + if let (true, Some(params)) = (security_key, credential_parameters) { + unsafe { + let proto_ptr: *const ProtocolObject< + dyn ASAuthorizationPublicKeyCredentialRegistrationRequest, + > = request; + let sk_req = &*(proto_ptr + as *const ASAuthorizationSecurityKeyPublicKeyCredentialRegistrationRequest); + sk_req.setCredentialParameters(params); + } + } +} + +fn apply_assertion_setup( + request: &ProtocolObject, + user_verification: u32, +) { + unsafe { + let uv = user_verification_value(user_verification); + request.setUserVerificationPreference(&uv); + } +} + +fn ns_string_to_string(s: &NSString) -> String { + s.to_string() +} + +fn format_ns_error(error: &NSError) -> String { + let desc = error.localizedDescription(); + let mut message = ns_string_to_string(&desc); + if message.is_empty() { + message = "WebAuthn operation failed".to_owned(); + } + let domain = error.domain(); + let domain_str = ns_string_to_string(&domain); + let code = error.code(); + if !domain_str.is_empty() { + message.push_str(&format!(" ({domain_str} {code})")); + } + if let Some(reason) = error.localizedFailureReason() { + let reason = ns_string_to_string(&reason); + if !reason.is_empty() { + message.push_str("; reason: "); + message.push_str(&reason); + } + } + if let Some(suggestion) = error.localizedRecoverySuggestion() { + let suggestion = ns_string_to_string(&suggestion); + if !suggestion.is_empty() { + message.push_str("; suggestion: "); + message.push_str(&suggestion); + } + } + + let info = error.userInfo(); + let debug_key = NSString::from_str("NSDebugDescription"); + if let Some(value) = info.objectForKey(&debug_key) { + let value_obj: &AnyObject = &value; + let ns_string_cls = ::class(); + + let is_string: bool = unsafe { msg_send![value_obj, isKindOfClass: ns_string_cls] }; + if is_string { + let s: &NSString = unsafe { &*(value_obj as *const _ as *const NSString) }; + let text = ns_string_to_string(s); + if !text.is_empty() { + message.push_str("; debug: "); + message.push_str(&text); + } + } + } + let underlying_key = NSString::from_str("NSUnderlyingError"); + if let Some(value) = info.objectForKey(&underlying_key) { + let value_obj: &AnyObject = &value; + let ns_error_cls = ::class(); + + let is_error: bool = unsafe { msg_send![value_obj, isKindOfClass: ns_error_cls] }; + if is_error { + let err: &NSError = unsafe { &*(value_obj as *const _ as *const NSError) }; + message.push_str("; underlying: "); + message.push_str(&format_ns_error(err)); + } + } + message +} + +fn extract_registration(credential: &AnyObject) -> CompletionPayload { + unsafe { + if !responds_to(credential, sel!(credentialID)) + || !responds_to(credential, sel!(rawAttestationObject)) + { + return CompletionPayload::Error( + "Unexpected WebAuthn registration credential".to_owned(), + ); + } + } + let raw_id_data: Retained = unsafe { msg_send![credential, credentialID] }; + let attestation_data: Option> = + unsafe { msg_send![credential, rawAttestationObject] }; + CompletionPayload::Create { + raw_id: raw_id_data.to_vec(), + attestation_object: attestation_data.map(|d| d.to_vec()).unwrap_or_default(), + attachment: attachment_kind(credential), + } +} + +fn extract_assertion(credential: &AnyObject) -> CompletionPayload { + unsafe { + if !responds_to(credential, sel!(credentialID)) + || !responds_to(credential, sel!(signature)) + || !responds_to(credential, sel!(rawAuthenticatorData)) + { + return CompletionPayload::Error("Unexpected WebAuthn assertion credential".to_owned()); + } + } + let raw_id_data: Retained = unsafe { msg_send![credential, credentialID] }; + let auth_data: Retained = unsafe { msg_send![credential, rawAuthenticatorData] }; + let signature_data: Retained = unsafe { msg_send![credential, signature] }; + let user_handle = unsafe { + if responds_to(credential, sel!(userID)) { + let data: Retained = msg_send![credential, userID]; + let v = data.to_vec(); + if v.is_empty() { None } else { Some(v) } + } else { + None + } + }; + CompletionPayload::Get { + raw_id: raw_id_data.to_vec(), + authenticator_data: auth_data.to_vec(), + signature: signature_data.to_vec(), + user_handle, + attachment: attachment_kind(credential), + } +} + +fn attachment_kind(credential: &AnyObject) -> AttachmentKind { + unsafe { + if responds_to(credential, sel!(attachment)) { + let val: ASAuthorizationPublicKeyCredentialAttachment = + msg_send![credential, attachment]; + if val == ASAuthorizationPublicKeyCredentialAttachment::Platform { + return AttachmentKind::Platform; + } + } + } + AttachmentKind::CrossPlatform +} + +struct CeremonyInputsCreate { + rp_id: String, + user_name: String, + display_name: String, + challenge: Vec, + user_id: Vec, + client_data_hash: Vec, + pub_key_algs: Vec, + exclude_credentials: Vec, + authenticator_attachment: u32, + user_verification: u32, + attestation: u32, + require_resident_key: bool, +} + +struct CeremonyInputsGet { + rp_id: String, + challenge: Vec, + client_data_hash: Vec, + allow_credentials: Vec, + authenticator_attachment: u32, + user_verification: u32, +} + +fn run_create(input: &CreateInput) -> std::result::Result { + let snapshot = CeremonyInputsCreate { + rp_id: input.rp_id.clone(), + user_name: input.user_name.clone(), + display_name: if input.user_display_name.is_empty() { + input.user_name.clone() + } else { + input.user_display_name.clone() + }, + challenge: input.challenge.clone(), + user_id: input.user_id.clone(), + client_data_hash: input.client_data_hash.clone(), + pub_key_algs: input.pub_key_algs.clone(), + exclude_credentials: input.exclude_credentials.clone(), + authenticator_attachment: input.authenticator_attachment, + user_verification: input.user_verification, + attestation: input.attestation, + require_resident_key: input.require_resident_key, + }; + + let semaphore = DispatchSemaphore::new(0); + let (tx, rx) = channel::(); + + let (setup_tx, setup_rx) = channel::>(); + let semaphore_for_closure = semaphore.clone(); + + DispatchQueue::main().exec_async(move || { + let mtm = unsafe { MainThreadMarker::new_unchecked() }; + let result = + build_create_controller(mtm, snapshot, tx.clone(), semaphore_for_closure.clone()); + match result { + Ok(handles) => { + handles.perform(mtm); + + let _ = setup_tx.send(Ok(handles)); + } + Err(msg) => { + let _ = setup_tx.send(Err(msg)); + } + } + }); + + let handles = match setup_rx.recv() { + Ok(Ok(h)) => h, + Ok(Err(e)) => return Err(e), + Err(_) => return Err("main-queue setup dispatch failed".to_owned()), + }; + + let timeout = if input.timeout_ms == 0 { + DispatchTime::FOREVER + } else { + DispatchTime::NOW.time((input.timeout_ms as i64) * 1_000_000) + }; + let wait = semaphore.try_acquire(timeout); + if matches!(wait, Err(WaitError::Timeout)) { + handles.cancel(); + return Err("WebAuthn registration timed out".to_owned()); + } + drop(handles); + + let payload = rx + .recv() + .map_err(|_| "WebAuthn registration completed without payload".to_owned())?; + match payload { + CompletionPayload::Create { + raw_id, + attestation_object, + attachment, + } => Ok(CreateResult { + raw_id, + attestation_object, + client_data_json: input.client_data_json.clone(), + used_transport: attachment.transport_bit(), + }), + CompletionPayload::Error(msg) => Err(msg), + CompletionPayload::Get { .. } => { + Err("internal: assertion payload returned from registration ceremony".to_owned()) + } + } +} + +fn run_get(input: &GetInput) -> std::result::Result { + let snapshot = CeremonyInputsGet { + rp_id: input.rp_id.clone(), + challenge: input.challenge.clone(), + client_data_hash: input.client_data_hash.clone(), + allow_credentials: input.allow_credentials.clone(), + authenticator_attachment: input.authenticator_attachment, + user_verification: input.user_verification, + }; + + let semaphore = DispatchSemaphore::new(0); + let (tx, rx) = channel::(); + let (setup_tx, setup_rx) = channel::>(); + let semaphore_for_closure = semaphore.clone(); + + DispatchQueue::main().exec_async(move || { + let mtm = unsafe { MainThreadMarker::new_unchecked() }; + let result = build_get_controller(mtm, snapshot, tx.clone(), semaphore_for_closure.clone()); + match result { + Ok(handles) => { + handles.perform(mtm); + let _ = setup_tx.send(Ok(handles)); + } + Err(msg) => { + let _ = setup_tx.send(Err(msg)); + } + } + }); + + let handles = match setup_rx.recv() { + Ok(Ok(h)) => h, + Ok(Err(e)) => return Err(e), + Err(_) => return Err("main-queue setup dispatch failed".to_owned()), + }; + + let timeout = if input.timeout_ms == 0 { + DispatchTime::FOREVER + } else { + DispatchTime::NOW.time((input.timeout_ms as i64) * 1_000_000) + }; + let wait = semaphore.try_acquire(timeout); + if matches!(wait, Err(WaitError::Timeout)) { + handles.cancel(); + return Err("WebAuthn authentication timed out".to_owned()); + } + drop(handles); + + let payload = rx + .recv() + .map_err(|_| "WebAuthn authentication completed without payload".to_owned())?; + match payload { + CompletionPayload::Get { + raw_id, + authenticator_data, + signature, + user_handle, + attachment, + } => Ok(GetResult { + raw_id, + authenticator_data, + signature, + user_handle, + client_data_json: input.client_data_json.clone(), + used_transport: attachment.transport_bit(), + }), + CompletionPayload::Error(msg) => Err(msg), + CompletionPayload::Create { .. } => { + Err("internal: registration payload returned from assertion ceremony".to_owned()) + } + } +} + +struct MainThreadHandlesInner { + controller: Retained, + + #[allow(dead_code)] + delegate: Retained, +} + +struct MainThreadHandles(MainThreadBound); + +impl MainThreadHandles { + fn new(mtm: MainThreadMarker, inner: MainThreadHandlesInner) -> Self { + Self(MainThreadBound::new(inner, mtm)) + } + + fn perform(&self, mtm: MainThreadMarker) { + unsafe { self.0.get(mtm).controller.performRequests() }; + } + + fn cancel(self) { + DispatchQueue::main().exec_async(move || { + let this = self; + + let mtm = unsafe { MainThreadMarker::new_unchecked() }; + unsafe { this.0.get(mtm).controller.cancel() }; + + let _ = this; + }); + } +} + +fn build_create_controller( + mtm: MainThreadMarker, + s: CeremonyInputsCreate, + sender: Sender, + semaphore: DispatchRetained, +) -> std::result::Result { + if !controller_private_context_selector_available() { + return Err( + "AuthenticationServices request-context override is unavailable on this macOS version" + .to_owned(), + ); + } + + let rp_id_ns = ns_string(&s.rp_id); + let user_name_ns = ns_string(&s.user_name); + let display_name_ns = ns_string(&s.display_name); + let challenge_ns = ns_data(&s.challenge); + let user_id_ns = ns_data(&s.user_id); + let credential_params = credential_parameters_array(&s.pub_key_algs); + let supported_algorithms = supported_algorithm_numbers(&s.pub_key_algs); + let platform_excluded = platform_descriptor_array(&s.exclude_credentials); + let security_key_excluded = security_key_descriptor_array(&s.exclude_credentials); + + let requests: Retained> = NSMutableArray::new(); + + if s.authenticator_attachment == 0 || s.authenticator_attachment == 1 { + let provider = unsafe { + ASAuthorizationPlatformPublicKeyCredentialProvider::initWithRelyingPartyIdentifier( + ASAuthorizationPlatformPublicKeyCredentialProvider::alloc(), + &rp_id_ns, + ) + }; + let request = unsafe { + provider.createCredentialRegistrationRequestWithChallenge_name_userID( + &challenge_ns, + &user_name_ns, + &user_id_ns, + ) + }; + unsafe { + request.setDisplayName(Some(&display_name_ns)); + } + let proto = ProtocolObject::from_ref(&*request); + apply_registration_setup(proto, s.user_verification, s.attestation, false, None); + let req_obj: &ASAuthorizationRequest = + unsafe { &*(&*request as *const _ as *const ASAuthorizationRequest) }; + requests.addObject(req_obj); + } + if s.authenticator_attachment == 0 || s.authenticator_attachment == 2 { + let provider = unsafe { + ASAuthorizationSecurityKeyPublicKeyCredentialProvider::initWithRelyingPartyIdentifier( + ASAuthorizationSecurityKeyPublicKeyCredentialProvider::alloc(), + &rp_id_ns, + ) + }; + let request = unsafe { + provider.createCredentialRegistrationRequestWithChallenge_displayName_name_userID( + &challenge_ns, + &display_name_ns, + &user_name_ns, + &user_id_ns, + ) + }; + let proto = ProtocolObject::from_ref(&*request); + apply_registration_setup( + proto, + s.user_verification, + s.attestation, + true, + Some(&credential_params), + ); + if security_key_excluded.count() > 0 { + unsafe { request.setExcludedCredentials(&security_key_excluded) }; + } + let req_obj: &ASAuthorizationRequest = + unsafe { &*(&*request as *const _ as *const ASAuthorizationRequest) }; + requests.addObject(req_obj); + } + + if requests.count() == 0 { + return Err("No supported WebAuthn registration requests were created".to_owned()); + } + + let excluded_for_override: Retained> = if security_key_excluded.count() > 0 { + unsafe { Retained::cast_unchecked(security_key_excluded) } + } else { + unsafe { Retained::cast_unchecked(platform_excluded) } + }; + + let ivars = ControllerOverrideIvars { + client_data_hash: if s.client_data_hash.is_empty() { + None + } else { + Some(ns_data(&s.client_data_hash)) + }, + supported_algorithms: Some(supported_algorithms), + exclude_credentials: Some(excluded_for_override), + require_resident_key: s.require_resident_key, + is_create: true, + }; + + let controller = ControllerOverride::new(&requests, ivars); + let delegate = WebAuthnDelegate::new(mtm, CeremonyKind::Create, sender, semaphore); + + unsafe { + controller.setDelegate(Some(ProtocolObject::from_ref(&*delegate))); + controller.setPresentationContextProvider(Some(ProtocolObject::from_ref(&*delegate))); + } + + Ok(MainThreadHandles::new( + mtm, + MainThreadHandlesInner { + controller, + delegate, + }, + )) +} + +fn build_get_controller( + mtm: MainThreadMarker, + s: CeremonyInputsGet, + sender: Sender, + semaphore: DispatchRetained, +) -> std::result::Result { + if !controller_private_context_selector_available() { + return Err( + "AuthenticationServices request-context override is unavailable on this macOS version" + .to_owned(), + ); + } + + let rp_id_ns = ns_string(&s.rp_id); + let challenge_ns = ns_data(&s.challenge); + let platform_allow = platform_descriptor_array(&s.allow_credentials); + let security_key_allow = security_key_descriptor_array(&s.allow_credentials); + + let requests: Retained> = NSMutableArray::new(); + + if s.authenticator_attachment == 0 || s.authenticator_attachment == 1 { + let provider = unsafe { + ASAuthorizationPlatformPublicKeyCredentialProvider::initWithRelyingPartyIdentifier( + ASAuthorizationPlatformPublicKeyCredentialProvider::alloc(), + &rp_id_ns, + ) + }; + let request = + unsafe { provider.createCredentialAssertionRequestWithChallenge(&challenge_ns) }; + let proto = ProtocolObject::from_ref(&*request); + apply_assertion_setup(proto, s.user_verification); + if platform_allow.count() > 0 { + unsafe { request.setAllowedCredentials(&platform_allow) }; + } + let req_obj: &ASAuthorizationRequest = + unsafe { &*(&*request as *const _ as *const ASAuthorizationRequest) }; + requests.addObject(req_obj); + } + if s.authenticator_attachment == 0 || s.authenticator_attachment == 2 { + let provider = unsafe { + ASAuthorizationSecurityKeyPublicKeyCredentialProvider::initWithRelyingPartyIdentifier( + ASAuthorizationSecurityKeyPublicKeyCredentialProvider::alloc(), + &rp_id_ns, + ) + }; + let request = + unsafe { provider.createCredentialAssertionRequestWithChallenge(&challenge_ns) }; + let proto = ProtocolObject::from_ref(&*request); + apply_assertion_setup(proto, s.user_verification); + if security_key_allow.count() > 0 { + unsafe { request.setAllowedCredentials(&security_key_allow) }; + } + let req_obj: &ASAuthorizationRequest = + unsafe { &*(&*request as *const _ as *const ASAuthorizationRequest) }; + requests.addObject(req_obj); + } + + if requests.count() == 0 { + return Err("No supported WebAuthn authentication requests were created".to_owned()); + } + + let ivars = ControllerOverrideIvars { + client_data_hash: if s.client_data_hash.is_empty() { + None + } else { + Some(ns_data(&s.client_data_hash)) + }, + supported_algorithms: None, + exclude_credentials: None, + require_resident_key: false, + is_create: false, + }; + + let controller = ControllerOverride::new(&requests, ivars); + let delegate = WebAuthnDelegate::new(mtm, CeremonyKind::Get, sender, semaphore); + + unsafe { + controller.setDelegate(Some(ProtocolObject::from_ref(&*delegate))); + controller.setPresentationContextProvider(Some(ProtocolObject::from_ref(&*delegate))); + } + + Ok(MainThreadHandles::new( + mtm, + MainThreadHandlesInner { + controller, + delegate, + }, + )) +} + +#[allow(dead_code)] +const _: Option<&Receiver> = None; diff --git a/fluxer_desktop/native/webauthn/src/windows.rs b/fluxer_desktop/native/webauthn/src/windows.rs new file mode 100644 index 0000000..ddcb727 --- /dev/null +++ b/fluxer_desktop/native/webauthn/src/windows.rs @@ -0,0 +1,304 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::ffi::OsString; +use std::os::windows::ffi::OsStrExt; + +use napi::Result; +use windows::Win32::Foundation::{HWND, LPARAM}; +use windows::Win32::Networking::WindowsWebServices::{ + WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM, WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM, + WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS, + WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_CURRENT_VERSION, + WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS, + WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_CURRENT_VERSION, WEBAUTHN_CLIENT_DATA, + WEBAUTHN_CLIENT_DATA_CURRENT_VERSION, WEBAUTHN_COSE_CREDENTIAL_PARAMETER, + WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION, WEBAUTHN_COSE_CREDENTIAL_PARAMETERS, + WEBAUTHN_CREDENTIAL, WEBAUTHN_CREDENTIAL_CURRENT_VERSION, WEBAUTHN_CREDENTIALS, + WEBAUTHN_CTAP_TRANSPORT_INTERNAL, WEBAUTHN_CTAP_TRANSPORT_USB, WEBAUTHN_HASH_ALGORITHM_SHA_256, + WEBAUTHN_LARGE_BLOB_SUPPORT_NONE, WEBAUTHN_RP_ENTITY_INFORMATION, + WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION, WEBAUTHN_USER_ENTITY_INFORMATION, + WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION, WebAuthNAuthenticatorGetAssertion, + WebAuthNAuthenticatorMakeCredential, WebAuthNFreeAssertion, WebAuthNFreeCredentialAttestation, + WebAuthNGetApiVersionNumber, WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable, +}; +use windows::Win32::System::Threading::GetCurrentProcessId; +use windows::Win32::UI::WindowsAndMessaging::{ + EnumWindows, GetDesktopWindow, GetForegroundWindow, GetWindowThreadProcessId, IsWindow, + IsWindowVisible, +}; +use windows_core::{BOOL, PCWSTR}; + +use crate::common::{ + CREATE_PREFIX, CreateInput, CreateResult, DescriptorInput, GET_PREFIX, GetInput, GetResult, + ceremony_error, +}; + +pub fn api_version() -> u32 { + unsafe { WebAuthNGetApiVersionNumber() } +} + +pub fn is_user_verifying_platform_authenticator_available() -> bool { + match unsafe { WebAuthNIsUserVerifyingPlatformAuthenticatorAvailable() } { + Ok(b) => b.as_bool(), + Err(_) => false, + } +} + +fn to_wide(value: &str) -> Vec { + OsString::from(value).encode_wide().chain(Some(0)).collect() +} + +fn window_from_handle(handle: u64) -> Option { + if handle == 0 { + return None; + } + let hwnd = HWND(handle as usize as *mut core::ffi::c_void); + if unsafe { IsWindow(Some(hwnd)) }.as_bool() { + Some(hwnd) + } else { + None + } +} + +fn window_process_id(hwnd: HWND) -> u32 { + let mut pid = 0u32; + unsafe { GetWindowThreadProcessId(hwnd, Some(&mut pid)) }; + pid +} + +unsafe extern "system" fn collect_own_visible_window(hwnd: HWND, lparam: LPARAM) -> BOOL { + let slot = unsafe { &mut *(lparam.0 as *mut Option) }; + if window_process_id(hwnd) == unsafe { GetCurrentProcessId() } + && unsafe { IsWindowVisible(hwnd) }.as_bool() + { + *slot = Some(hwnd); + return BOOL(0); + } + BOOL(1) +} + +fn interaction_window(handle: u64) -> HWND { + if let Some(hwnd) = window_from_handle(handle) { + return hwnd; + } + let current_pid = unsafe { GetCurrentProcessId() }; + let foreground = unsafe { GetForegroundWindow() }; + if !foreground.is_invalid() && window_process_id(foreground) == current_pid { + return foreground; + } + let mut own_window: Option = None; + let _ = unsafe { + EnumWindows( + Some(collect_own_visible_window), + LPARAM(&raw mut own_window as isize), + ) + }; + if let Some(hwnd) = own_window { + return hwnd; + } + if !foreground.is_invalid() { + return foreground; + } + unsafe { GetDesktopWindow() } +} + +const PUBLIC_KEY_WIDE: &[u16] = &[ + 'p' as u16, 'u' as u16, 'b' as u16, 'l' as u16, 'i' as u16, 'c' as u16, '-' as u16, 'k' as u16, + 'e' as u16, 'y' as u16, 0, +]; + +fn build_credential_list( + descriptors: &[DescriptorInput], +) -> (Vec, WEBAUTHN_CREDENTIALS) { + let mut backing: Vec = descriptors + .iter() + .map(|d| WEBAUTHN_CREDENTIAL { + dwVersion: WEBAUTHN_CREDENTIAL_CURRENT_VERSION, + cbId: d.id.len() as u32, + pbId: d.id.as_ptr() as *mut u8, + pwszCredentialType: PCWSTR(PUBLIC_KEY_WIDE.as_ptr()), + }) + .collect(); + let list = WEBAUTHN_CREDENTIALS { + cCredentials: backing.len() as u32, + pCredentials: if backing.is_empty() { + std::ptr::null_mut() + } else { + backing.as_mut_ptr() + }, + }; + (backing, list) +} + +fn copy_buffer(ptr: *const u8, len: u32) -> Result> { + if len == 0 { + return Ok(Vec::new()); + } + if ptr.is_null() { + return Err(ceremony_error(CREATE_PREFIX, "InvalidWebAuthnResult")); + } + + let slice = unsafe { std::slice::from_raw_parts(ptr, len as usize) }; + Ok(slice.to_vec()) +} + +pub fn make_credential(input: &mut CreateInput) -> Result { + let rp_id_w = to_wide(&input.rp_id); + let rp_name_w = to_wide(&input.rp_name); + let user_name_w = to_wide(&input.user_name); + let display_w = to_wide(&input.user_display_name); + + let rp = WEBAUTHN_RP_ENTITY_INFORMATION { + dwVersion: WEBAUTHN_RP_ENTITY_INFORMATION_CURRENT_VERSION, + pwszId: PCWSTR(rp_id_w.as_ptr()), + pwszName: PCWSTR(rp_name_w.as_ptr()), + pwszIcon: PCWSTR::null(), + }; + let user = WEBAUTHN_USER_ENTITY_INFORMATION { + dwVersion: WEBAUTHN_USER_ENTITY_INFORMATION_CURRENT_VERSION, + cbId: input.user_id.len() as u32, + pbId: input.user_id.as_ptr() as *mut u8, + pwszName: PCWSTR(user_name_w.as_ptr()), + pwszIcon: PCWSTR::null(), + pwszDisplayName: PCWSTR(display_w.as_ptr()), + }; + + let mut pub_key_params: Vec = input + .pub_key_algs + .iter() + .map(|&alg| WEBAUTHN_COSE_CREDENTIAL_PARAMETER { + dwVersion: WEBAUTHN_COSE_CREDENTIAL_PARAMETER_CURRENT_VERSION, + pwszCredentialType: PCWSTR(PUBLIC_KEY_WIDE.as_ptr()), + lAlg: alg, + }) + .collect(); + let params = WEBAUTHN_COSE_CREDENTIAL_PARAMETERS { + cCredentialParameters: pub_key_params.len() as u32, + pCredentialParameters: pub_key_params.as_mut_ptr(), + }; + + let client_data = WEBAUTHN_CLIENT_DATA { + dwVersion: WEBAUTHN_CLIENT_DATA_CURRENT_VERSION, + cbClientDataJSON: input.client_data_json.len() as u32, + pbClientDataJSON: input.client_data_json.as_ptr() as *mut u8, + pwszHashAlgId: WEBAUTHN_HASH_ALGORITHM_SHA_256, + }; + + let (_exclude_keep, exclude_list) = build_credential_list(&input.exclude_credentials); + + let options = WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS { + dwVersion: api_version() + .min(WEBAUTHN_AUTHENTICATOR_MAKE_CREDENTIAL_OPTIONS_CURRENT_VERSION), + dwTimeoutMilliseconds: input.timeout_ms, + CredentialList: exclude_list, + dwAuthenticatorAttachment: input.authenticator_attachment, + bRequireResidentKey: BOOL(input.require_resident_key as i32), + dwUserVerificationRequirement: input.user_verification, + dwAttestationConveyancePreference: input.attestation, + dwEnterpriseAttestation: input.enterprise_attestation, + dwLargeBlobSupport: WEBAUTHN_LARGE_BLOB_SUPPORT_NONE, + bPreferResidentKey: BOOL(input.prefer_resident_key as i32), + ..Default::default() + }; + + let attestation_ptr = unsafe { + WebAuthNAuthenticatorMakeCredential( + interaction_window(input.window_handle), + &rp, + &user, + ¶ms, + &client_data, + Some(&options), + ) + }; + let attestation_ptr = attestation_ptr + .map_err(|err| ceremony_error(CREATE_PREFIX, &format!("WebAuthnCallFailed: {err}")))?; + if attestation_ptr.is_null() { + return Err(ceremony_error(CREATE_PREFIX, "InvalidWebAuthnResult")); + } + + let att = unsafe { &*attestation_ptr }; + let raw_id = copy_buffer(att.pbCredentialId, att.cbCredentialId)?; + let attestation_object = copy_buffer(att.pbAttestationObject, att.cbAttestationObject)?; + let used_transport = if att.dwVersion >= 3 { + att.dwUsedTransport + } else { + 0 + }; + + unsafe { WebAuthNFreeCredentialAttestation(Some(attestation_ptr)) }; + + Ok(CreateResult { + raw_id, + attestation_object, + client_data_json: input.client_data_json.clone(), + used_transport, + }) +} + +pub fn get_assertion(input: &mut GetInput) -> Result { + let rp_id_w = to_wide(&input.rp_id); + + let client_data = WEBAUTHN_CLIENT_DATA { + dwVersion: WEBAUTHN_CLIENT_DATA_CURRENT_VERSION, + cbClientDataJSON: input.client_data_json.len() as u32, + pbClientDataJSON: input.client_data_json.as_ptr() as *mut u8, + pwszHashAlgId: WEBAUTHN_HASH_ALGORITHM_SHA_256, + }; + + let (_allow_keep, allow_list) = build_credential_list(&input.allow_credentials); + + let options = WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS { + dwVersion: api_version().min(WEBAUTHN_AUTHENTICATOR_GET_ASSERTION_OPTIONS_CURRENT_VERSION), + dwTimeoutMilliseconds: input.timeout_ms, + CredentialList: allow_list, + dwAuthenticatorAttachment: input.authenticator_attachment, + dwUserVerificationRequirement: input.user_verification, + ..Default::default() + }; + + let assertion_ptr = unsafe { + WebAuthNAuthenticatorGetAssertion( + interaction_window(input.window_handle), + PCWSTR(rp_id_w.as_ptr()), + &client_data, + Some(&options), + ) + }; + let assertion_ptr = assertion_ptr + .map_err(|err| ceremony_error(GET_PREFIX, &format!("WebAuthnCallFailed: {err}")))?; + if assertion_ptr.is_null() { + return Err(ceremony_error(GET_PREFIX, "InvalidWebAuthnResult")); + } + + let asrt = unsafe { &*assertion_ptr }; + let raw_id = copy_buffer(asrt.Credential.pbId, asrt.Credential.cbId)?; + let authenticator_data = copy_buffer(asrt.pbAuthenticatorData, asrt.cbAuthenticatorData)?; + let signature = copy_buffer(asrt.pbSignature, asrt.cbSignature)?; + let user_handle = if asrt.cbUserId > 0 { + Some(copy_buffer(asrt.pbUserId, asrt.cbUserId)?) + } else { + None + }; + + let used_transport = if asrt.dwVersion >= 4 { + asrt.dwUsedTransport + } else if input.authenticator_attachment == WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM { + WEBAUTHN_CTAP_TRANSPORT_INTERNAL + } else if input.authenticator_attachment == WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM { + WEBAUTHN_CTAP_TRANSPORT_USB + } else { + 0 + }; + + unsafe { WebAuthNFreeAssertion(assertion_ptr) }; + + Ok(GetResult { + raw_id, + authenticator_data, + signature, + user_handle, + client_data_json: input.client_data_json.clone(), + used_transport, + }) +} diff --git a/fluxer_desktop/native/webauthn/test/loader.test.mjs b/fluxer_desktop/native/webauthn/test/loader.test.mjs new file mode 100644 index 0000000..422c720 --- /dev/null +++ b/fluxer_desktop/native/webauthn/test/loader.test.mjs @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import assert from 'node:assert/strict'; +import test from 'node:test'; +import webauthn from '../index.js'; + +test('loader resolves supported native filenames', () => { + assert.equal(webauthn._private.nativeFileName('darwin', 'x64'), 'webauthn.darwin-x64.node'); + assert.equal(webauthn._private.nativeFileName('darwin', 'arm64'), 'webauthn.darwin-arm64.node'); + assert.equal(webauthn._private.nativeFileName('linux', 'x64'), 'webauthn.linux-x64-gnu.node'); + assert.equal(webauthn._private.nativeFileName('linux', 'arm64'), 'webauthn.linux-arm64-gnu.node'); + assert.equal(webauthn._private.nativeFileName('win32', 'x64'), 'webauthn.win32-x64-msvc.node'); + assert.equal(webauthn._private.nativeFileName('win32', 'arm64'), 'webauthn.win32-arm64-msvc.node'); + assert.equal(webauthn._private.nativeFileName('freebsd', 'x64'), null); +}); + +test('loader exposes the Fluxer WebAuthn surface', async () => { + assert.equal(typeof webauthn.create, 'function'); + assert.equal(typeof webauthn.get, 'function'); + assert.equal(typeof webauthn.getBackendInfo, 'function'); + assert.equal(typeof webauthn.isSupported, 'function'); + const info = webauthn.getBackendInfo(); + const supported = await webauthn.isSupported(); + assert.equal(typeof supported, 'boolean'); + assert.equal(supported, info.supported); + assert.equal(typeof info, 'object'); + assert.equal(typeof info.target, 'string'); + assert.equal(typeof info.backend, 'string'); + assert.equal(typeof info.supported, 'boolean'); + assert.equal(typeof info.ceremoniesImplemented, 'boolean'); +}); + +test('normalization creates spec-shaped client data and Windows transport bits', () => { + const challenge = Buffer.from([1, 2, 3, 4]); + const normalized = webauthn._private.normalizeCreateOptions({ + origin: 'https://web.canary.fluxer.app/channels/@me', + challenge, + rp: {id: 'fluxer.app', name: 'Fluxer'}, + user: {id: Buffer.from('user'), name: 'name', displayName: 'Name'}, + pubKeyCredParams: [{type: 'public-key', alg: -7}], + authenticatorSelection: { + authenticatorAttachment: 'platform', + residentKey: 'preferred', + userVerification: 'required', + }, + excludeCredentials: [{type: 'public-key', id: Buffer.from('cred'), transports: ['internal', 'hybrid']}], + attestation: 'none', + }); + assert.equal(normalized.rpId, 'fluxer.app'); + assert.equal(normalized.authenticatorAttachment, 1); + assert.equal(normalized.userVerification, 1); + assert.equal(normalized.preferResidentKey, true); + assert.equal(normalized.requireResidentKey, false); + assert.equal(normalized.excludeCredentials[0].transports, 0x10 | 0x20); + assert.deepEqual(JSON.parse(normalized.clientDataJSON.toString('utf8')), { + type: 'webauthn.create', + challenge: 'AQIDBA', + origin: 'https://web.canary.fluxer.app', + crossOrigin: false, + }); +}); diff --git a/fluxer_desktop/native/webauthn/vm_tests/run_in_session.ps1 b/fluxer_desktop/native/webauthn/vm_tests/run_in_session.ps1 new file mode 100644 index 0000000..594f53a --- /dev/null +++ b/fluxer_desktop/native/webauthn/vm_tests/run_in_session.ps1 @@ -0,0 +1,31 @@ +param( + [Parameter(Mandatory = $true)][string]$Command, + [string]$OutFile = 'C:\tools\vm_test_out.txt', + [int]$TimeoutSec = 120 +) + +$taskName = 'FluxerVmTest' +if (Test-Path $OutFile) { Remove-Item -Force $OutFile } +Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue + +$action = New-ScheduledTaskAction -Execute 'cmd.exe' -Argument ('/c ' + $Command + ' > "' + $OutFile + '" 2>&1') +$principal = New-ScheduledTaskPrincipal -UserId 'hampus' -LogonType Interactive +$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -ExecutionTimeLimit (New-TimeSpan -Minutes 10) +Register-ScheduledTask -TaskName $taskName -Action $action -Principal $principal -Settings $settings -Force | Out-Null +Start-ScheduledTask -TaskName $taskName + +$deadline = (Get-Date).AddSeconds($TimeoutSec) +do { + Start-Sleep -Milliseconds 500 + $state = (Get-ScheduledTask -TaskName $taskName).State +} while ($state -ne 'Ready' -and (Get-Date) -lt $deadline) + +$info = Get-ScheduledTaskInfo -TaskName $taskName +Write-Output ('TASK-STATE: ' + $state + ' LAST-RESULT: ' + $info.LastTaskResult) +if (Test-Path $OutFile) { + Write-Output '--- OUTPUT ---' + Get-Content $OutFile +} else { + Write-Output 'NO OUTPUT FILE' +} +Unregister-ScheduledTask -TaskName $taskName -Confirm:$false -ErrorAction SilentlyContinue diff --git a/fluxer_desktop/native/webauthn/vm_tests/set_pin.py b/fluxer_desktop/native/webauthn/vm_tests/set_pin.py new file mode 100644 index 0000000..551c70c --- /dev/null +++ b/fluxer_desktop/native/webauthn/vm_tests/set_pin.py @@ -0,0 +1,22 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later + +import ctypes +import sys + +device_path = sys.argv[1].encode() if len(sys.argv) > 1 else b"/dev/hidraw4" +pin = sys.argv[2].encode() if len(sys.argv) > 2 else b"123456" + +lib = ctypes.CDLL("libfido2.so.1") +lib.fido_init(0) +lib.fido_dev_new.restype = ctypes.c_void_p +lib.fido_strerr.restype = ctypes.c_char_p + +dev = lib.fido_dev_new() +rc = lib.fido_dev_open(ctypes.c_void_p(dev), device_path) +if rc != 0: + print(f"open failed rc={rc} {lib.fido_strerr(rc).decode()}") + sys.exit(1) +rc = lib.fido_dev_set_pin(ctypes.c_void_p(dev), pin, None) +print(f"set_pin rc={rc} {lib.fido_strerr(rc).decode()}") +lib.fido_dev_close(ctypes.c_void_p(dev)) +sys.exit(0 if rc == 0 else 1) diff --git a/fluxer_desktop/native/webauthn/vm_tests/vfido_authenticator/main.go b/fluxer_desktop/native/webauthn/vm_tests/vfido_authenticator/main.go new file mode 100644 index 0000000..fa533c6 --- /dev/null +++ b/fluxer_desktop/native/webauthn/vm_tests/vfido_authenticator/main.go @@ -0,0 +1,89 @@ +package main + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "crypto/sha256" + "crypto/x509" + "crypto/x509/pkix" + "fmt" + "io" + "math/big" + "os" + "time" + + virtual_fido "github.com/bulwarkid/virtual-fido" + "github.com/bulwarkid/virtual-fido/fido_client" +) + +type autoApproveSupport struct { + vaultFilename string +} + +func (support *autoApproveSupport) ApproveClientAction(action fido_client.ClientAction, params fido_client.ClientActionRequestParams) bool { + fmt.Printf("auto-approving action=%d relyingParty=%q user=%q\n", action, params.RelyingParty, params.UserName) + return true +} + +func (support *autoApproveSupport) SaveData(data []byte) { + err := os.WriteFile(support.vaultFilename, data, 0o600) + if err != nil { + panic(fmt.Sprintf("could not write vault: %s", err)) + } +} + +func (support *autoApproveSupport) RetrieveData() []byte { + f, err := os.Open(support.vaultFilename) + if os.IsNotExist(err) { + return nil + } + if err != nil { + panic(fmt.Sprintf("could not open vault: %s", err)) + } + defer f.Close() + data, err := io.ReadAll(f) + if err != nil { + panic(fmt.Sprintf("could not read vault: %s", err)) + } + return data +} + +func (support *autoApproveSupport) Passphrase() string { + return "vm-test-passphrase" +} + +func main() { + vault := os.Getenv("VFIDO_VAULT") + if vault == "" { + vault = "/tmp/vfido-vault.json" + } + authority := &x509.Certificate{ + SerialNumber: big.NewInt(0), + Subject: pkix.Name{ + Organization: []string{"Fluxer VM Test Virtual FIDO"}, + Country: []string{"US"}, + }, + NotBefore: time.Now(), + NotAfter: time.Now().AddDate(10, 0, 0), + IsCA: true, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, + KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign, + BasicConstraintsValid: true, + } + privateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) + if err != nil { + panic(err) + } + authorityCertBytes, err := x509.CreateCertificate(rand.Reader, authority, authority, &privateKey.PublicKey, privateKey) + if err != nil { + panic(err) + } + encryptionKey := sha256.Sum256([]byte("fluxer-vm-test")) + + virtual_fido.SetLogOutput(os.Stdout) + support := &autoApproveSupport{vaultFilename: vault} + client := fido_client.NewDefaultClient(authorityCertBytes, privateKey, encryptionKey, support, support) + fmt.Println("virtual-fido USBIP server starting on 127.0.0.1:3240 (bus 2-2)") + virtual_fido.Start(client) +} diff --git a/fluxer_desktop/native/webrtc-sender/Cargo.lock b/fluxer_desktop/native/webrtc-sender/Cargo.lock new file mode 100644 index 0000000..a09b878 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/Cargo.lock @@ -0,0 +1,6092 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloca" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7d05ea6aea7e9e64d25b9156ba2fee3fdd659e34e41063cd2fc7cd020d7f4" +dependencies = [ + "cc", +] + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "anymap2" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" + +[[package]] +name = "anymap3" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170433209e817da6aae2c51aa0dd443009a613425dd041ebfb2492d1c4c11a25" + +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base16ct" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" + +[[package]] +name = "bindgen" +version = "0.65.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfdf7b466f9a4903edc73f95d6d2bcd5baf8ae620638762244d3f60143643cc5" +dependencies = [ + "bitflags 1.3.2", + "cexpr", + "clang-sys", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash 1.1.0", + "shlex 1.3.0", + "syn 2.0.117", + "which", +] + +[[package]] +name = "bit-set" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +dependencies = [ + "bit-vec 0.6.3", +] + +[[package]] +name = "bit-set" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2f926cc3060f09db9ebc5b52823d85268d24bb917e472c0c4bea35780a7d" +dependencies = [ + "bit-vec 0.9.1", +] + +[[package]] +name = "bit-vec" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" + +[[package]] +name = "bit-vec" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b71798fca2c1fe1086445a7258a4bc81e6e49dcd24c8d0dd9a1e57395b603f51" +dependencies = [ + "serde", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "block" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "bmrng" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54df9073108f1558f90ae6c5bf5ab9c917c4185f5527b280c87a993cbead0ac" +dependencies = [ + "futures-core", + "tokio", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "bytemuck" +version = "1.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + +[[package]] +name = "cc" +version = "1.2.63" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex 2.0.1", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom 7.1.3", +] + +[[package]] +name = "cfg-expr" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb693542bcafa528e198be0ebd9d3632ca5b7c93dbe7237460e199910835997c" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chacha20" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + +[[package]] +name = "chrono" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "ciborium" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" +dependencies = [ + "ciborium-io", + "ciborium-ll", + "serde", +] + +[[package]] +name = "ciborium-io" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" + +[[package]] +name = "ciborium-ll" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" +dependencies = [ + "ciborium-io", + "half", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading 0.8.9", +] + +[[package]] +name = "clap" +version = "4.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstyle", + "clap_lex", + "strsim 0.11.1", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "cocoa" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c49e86fc36d5704151f5996b7b3795385f50ce09e3be0f47a0cfde869681cf8" +dependencies = [ + "bitflags 1.3.2", + "block", + "core-foundation 0.7.0", + "core-graphics", + "foreign-types", + "libc", + "objc", +] + +[[package]] +name = "cocoa-foundation" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81411967c50ee9a1fc11365f8c585f863a22a9697c89239c452292c40ba79b0d" +dependencies = [ + "bitflags 2.13.0", + "block", + "core-foundation 0.10.1", + "core-graphics-types", + "objc", +] + +[[package]] +name = "codespan-reporting" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" +dependencies = [ + "serde", + "termcolor", + "unicode-width", +] + +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "const-oid" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "core-foundation" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57d24c7a13c43e870e37c1556b74555437870a04514f7685f5b354e090567171" +dependencies = [ + "core-foundation-sys 0.7.0", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys 0.8.7", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3a71ab494c0b5b860bdc8407ae08978052417070c2ced38573a9157ad75b8ac" + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3889374e6ea6ab25dba90bb5d96202f61108058361f6dc72e8b03e6f8bbe923" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.7.0", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.13.0", + "core-foundation 0.10.1", + "libc", +] + +[[package]] +name = "core-media-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "273bf3fc5bf51fd06a7766a84788c1540b6527130a0bce39e00567d6ab9f31f1" +dependencies = [ + "cfg-if 0.1.10", + "core-foundation-sys 0.7.0", + "libc", +] + +[[package]] +name = "core-video-sys" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828" +dependencies = [ + "cfg-if 0.1.10", + "core-foundation-sys 0.7.0", + "core-graphics", + "libc", + "metal", + "objc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if 1.0.4", +] + +[[package]] +name = "criterion" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "950046b2aa2492f9a536f5f4f9a3de7b9e2476e575e05bd6c333371add4d98f3" +dependencies = [ + "alloca", + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "itertools 0.13.0", + "num-traits", + "oorandom", + "page_size", + "regex", + "serde", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8d80a2f4f5b554395e47b5d8305bc3d27813bacb73493eb1001e8f76dae29ea" +dependencies = [ + "cast", + "itertools 0.13.0", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-bigint" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76" +dependencies = [ + "generic-array", + "rand_core 0.6.4", + "subtle", + "zeroize", +] + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "curve25519-dalek" +version = "4.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures 0.2.17", + "curve25519-dalek-derive", + "digest 0.10.7", + "fiat-crypto", + "rustc_version", + "subtle", + "zeroize", +] + +[[package]] +name = "curve25519-dalek-derive" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "cxx" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747d8437319e3a2f43d93b341c137927ca70c0f5dabeea7a005a73665e247c7e" +dependencies = [ + "cc", + "cxx-build", + "cxxbridge-cmd", + "cxxbridge-flags", + "cxxbridge-macro", + "foldhash 0.2.0", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0f4697d190a142477b16aef7da8a99bfdc41e7e8b1687583c0d23a79c7afc1e" +dependencies = [ + "cc", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "scratch", + "syn 2.0.117", +] + +[[package]] +name = "cxxbridge-cmd" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0956799fa8678d4c50eed028f2de1c0552ae183c76e976cf7ca8c4e36a7c328" +dependencies = [ + "clap", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23384a836ab4f0ad98ace7e3955ad2de39de42378ab487dc28d3990392cb283a" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6acc6b5822b9526adfb4fc377b67128fdd60aac757cc4a741a6278603f763cf" +dependencies = [ + "indexmap", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "darling" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "109c1ca6e6b7f82cc233a97004ea8ed7ca123a9af07a8230878fcfda9b158bf0" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 1.0.109", +] + +[[package]] +name = "darling_macro" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" +dependencies = [ + "darling_core", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "data-encoding" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" + +[[package]] +name = "deep_filter" +version = "0.5.6" +source = "git+https://github.com/Rikorose/DeepFilterNet?rev=978576aa8400552a4ce9730838c635aa30db5e61#978576aa8400552a4ce9730838c635aa30db5e61" +dependencies = [ + "anyhow", + "flate2", + "itertools 0.10.5", + "log", + "ndarray 0.15.6", + "num-complex", + "realfft", + "rubato", + "rust-ini", + "rustfft", + "tar", + "thiserror 1.0.69", + "tract-core 0.19.16", + "tract-hir 0.19.16", + "tract-onnx 0.19.16", + "tract-pulse 0.19.16", +] + +[[package]] +name = "der" +version = "0.7.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" +dependencies = [ + "const-oid 0.9.6", + "pem-rfc7468", + "zeroize", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive-new" +version = "0.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3418329ca0ad70234b9735dc4ceed10af4df60eff9c8e7b06cb5e520d92c3535" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "derive-new" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2cdc8d50f426189eef89dac62fabfa0abb27d5cc008f25bf4156a0203325becc" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "device-info" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2ca8e71544c1b67dcdbc2699ab258828aff985e5bc8d5f6b486d90d7df2f848" +dependencies = [ + "core-foundation 0.10.1", + "jni", + "libc", + "thiserror 2.0.18", + "wasm-bindgen", + "web-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer 0.10.4", + "const-oid 0.9.6", + "crypto-common 0.1.6", + "subtle", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.0", + "const-oid 0.10.2", + "crypto-common 0.2.2", +] + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.13.0", + "objc2", +] + +[[package]] +name = "displaydoc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + +[[package]] +name = "doc-comment" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9" + +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + +[[package]] +name = "downcast-rs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "117240f60069e65410b3ae1bb213295bd828f707b5bec6596a1afc8793ce0cbc" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "dyn-eq" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2d035d21af5cde1a6f5c7b444a5bf963520a9f142e5d06931178433d7d5388" + +[[package]] +name = "dyn-hash" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fdab65db9274e0168143841eb8f864a0a21f8b1b8d2ba6812bbe6024346e99e" + +[[package]] +name = "ecdsa" +version = "0.16.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" +dependencies = [ + "der", + "digest 0.10.7", + "elliptic-curve", + "rfc6979", + "signature", + "spki", +] + +[[package]] +name = "ed25519" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53" +dependencies = [ + "pkcs8", + "signature", +] + +[[package]] +name = "ed25519-dalek" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9" +dependencies = [ + "curve25519-dalek", + "ed25519", + "serde", + "sha2 0.10.9", + "subtle", + "zeroize", +] + +[[package]] +name = "educe" +version = "0.4.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f0042ff8246a363dbe77d2ceedb073339e85a804b9a47636c6e016a9a32c05f" +dependencies = [ + "enum-ordinalize", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "either" +version = "1.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" + +[[package]] +name = "elliptic-curve" +version = "0.13.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" +dependencies = [ + "base16ct", + "crypto-bigint", + "digest 0.10.7", + "ff", + "generic-array", + "group", + "hkdf", + "pem-rfc7468", + "pkcs8", + "rand_core 0.6.4", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "enum-ordinalize" +version = "3.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf1fa3f06bbff1ea5b1a9c7b14aa992a39657db60a2759457328d7e058f49ee" +dependencies = [ + "num-bigint", + "num-traits", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "ff" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393" +dependencies = [ + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" + +[[package]] +name = "filetime" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" +dependencies = [ + "cfg-if 1.0.4", + "libc", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "fixedbitset" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "float-ord" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ce81f49ae8a0482e4c55ea62ebbd7e5a686af544c00b9d090bba3ff9be97b3d" + +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "nanorand", + "spin", +] + +[[package]] +name = "fluxer_desktop_native" +version = "0.1.0" +dependencies = [ + "sha2 0.11.0", +] + +[[package]] +name = "fluxer_gpu_rebuild" +version = "0.0.0" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "fluxer_screen_frame_bus" +version = "0.0.0" +dependencies = [ + "fluxer_gpu_rebuild", + "parking_lot", +] + +[[package]] +name = "fluxer_webrtc_sender" +version = "0.0.0" +dependencies = [ + "criterion", + "crossbeam-queue", + "deep_filter", + "fluxer_desktop_native", + "fluxer_screen_frame_bus", + "futures-core", + "futures-util", + "image", + "libloading 0.9.0", + "livekit", + "napi", + "napi-build", + "napi-derive", + "ndarray 0.15.6", + "nokhwa", + "objc2", + "objc2-core-foundation", + "objc2-core-video", + "objc2-foundation", + "objc2-vision", + "parking_lot", + "serde_json", + "tokio", + "tract-onnx 0.19.16", + "tract-onnx 0.23.1", + "tract-pulse 0.19.16", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "from_variants" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e859c8f2057687618905dbe99fc76e836e0a69738865ef90e46fc214a41bbf2" +dependencies = [ + "from_variants_impl", +] + +[[package]] +name = "from_variants_impl" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55a5e644a80e6d96b2b4910fa7993301d7b7926c045b475b62202b20a36ce69e" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", + "zeroize", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if 1.0.4", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if 1.0.4", + "js-sys", + "libc", + "r-efi 5.3.0", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" +dependencies = [ + "cfg-if 1.0.4", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", + "wasip2", + "wasip3", +] + +[[package]] +name = "gif" +version = "0.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gio-sys" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0071fe88dba8e40086c8ff9bbb62622999f49628344b1d1bf490a48a29d80f22" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "windows-sys 0.61.2", +] + +[[package]] +name = "glib" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16de123c2e6c90ce3b573b7330de19be649080ec612033d397d72da265f1bd8b" +dependencies = [ + "bitflags 2.13.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "smallvec", +] + +[[package]] +name = "glib-macros" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf59b675301228a696fe01c3073974643365080a76cc3ed5bc2cbc466ad87f17" +dependencies = [ + "heck 0.5.0", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "glib-sys" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d95e1a3a19ae464a7286e14af9a90683c64d70c02532d88d87ce95056af3e6c" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "gobject-sys" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dca35da0d19a18f4575f3cb99fe1c9e029a2941af5662f326f738a21edaf294" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "half" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" +dependencies = [ + "cfg-if 1.0.4", + "crunchy", + "num-traits", + "zerocopy", +] + +[[package]] +name = "hashbrown" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a3c133739dddd0d2990f9a4bdf8eb4b21ef50e4851ca85ab661199821d510e" + +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash 0.1.5", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.2.0", + "serde", + "serde_core", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hkdf" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7" +dependencies = [ + "hmac", +] + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest 0.10.7", +] + +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "http" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "typenum", +] + +[[package]] +name = "hyper" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-native-certs", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys 0.8.7", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "gif", + "image-webp", + "moxcms", + "num-traits", + "png", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "inventory" +version = "0.3.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4f0c30c76f2f4ccee3fe55a2435f691ca00c0e4bd87abe4f4a851b1d4dac39b" +dependencies = [ + "rustversion", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "itertools" +version = "0.10.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if 1.0.4", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.117", +] + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" +dependencies = [ + "cfg-if 1.0.4", + "futures-util", + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "jsonwebtoken" +version = "10.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eba32bfb4ffdeaca3e34431072faf01745c9b26d25504aa7a6cf5684334fc4fc" +dependencies = [ + "base64 0.22.1", + "ed25519-dalek", + "getrandom 0.2.17", + "hmac", + "js-sys", + "p256", + "p384", + "rand 0.8.6", + "rsa", + "serde", + "serde_json", + "sha2 0.10.9", + "signature", + "zeroize", +] + +[[package]] +name = "kstring" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "558bf9508a558512042d3095138b1f7b8fe90c5467d94f9f1da28b3731c5dbd1" +dependencies = [ + "serde", + "static_assertions", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if 1.0.4", + "windows-link", +] + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if 1.0.4", + "windows-link", +] + +[[package]] +name = "libm" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" + +[[package]] +name = "libwebrtc" +version = "0.3.35" +dependencies = [ + "cxx", + "glib", + "jni", + "js-sys", + "lazy_static", + "livekit-runtime", + "log", + "parking_lot", + "rtrb", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webrtc-sys", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f78c730aaa7d0b9336a299029ea49f9ee53b0ed06e9202e8cb7db9bae7b8c82" +dependencies = [ + "cc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "liquid" +version = "0.26.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e9338405fdbc0bce9b01695b2a2ef6b20eca5363f385d47bce48ddf8323cc25" +dependencies = [ + "doc-comment", + "liquid-core", + "liquid-derive", + "liquid-lib", + "serde", +] + +[[package]] +name = "liquid-core" +version = "0.26.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "feb8fed70857010ed9016ed2ce5a7f34e7cc51d5d7255c9c9dc2e3243e490b42" +dependencies = [ + "anymap2", + "itertools 0.13.0", + "kstring", + "liquid-derive", + "num-traits", + "pest", + "pest_derive", + "regex", + "serde", + "time", +] + +[[package]] +name = "liquid-derive" +version = "0.26.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b51f1d220e3fa869e24cfd75915efe3164bd09bb11b3165db3f37f57bf673e3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "liquid-lib" +version = "0.26.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee1794b5605e9f8864a8a4f41aa97976b42512cc81093f8c885d29fb94c6c556" +dependencies = [ + "itertools 0.13.0", + "liquid-core", + "once_cell", + "percent-encoding", + "regex", + "time", + "unicode-segmentation", +] + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "livekit" +version = "0.7.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0219dc0e3990d60ce8b5efc5904b1e871548867d0d4799482e1b0c51b53a31f" +dependencies = [ + "base64 0.22.1", + "bmrng", + "bytes", + "chrono", + "futures-util", + "lazy_static", + "libloading 0.8.9", + "libwebrtc", + "livekit-api", + "livekit-datatrack", + "livekit-protocol", + "livekit-runtime", + "log", + "parking_lot", + "prost 0.12.6", + "semver", + "serde", + "serde_json", + "thiserror 1.0.69", + "tokio", + "tokio-stream", +] + +[[package]] +name = "livekit-api" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c53d6335323ed9e4fd0c3ede761478fc607d147c6e3f2c29dc71ca73bcf18723" +dependencies = [ + "base64 0.21.7", + "bytes", + "device-info", + "futures-util", + "http", + "jsonwebtoken", + "livekit-protocol", + "livekit-runtime", + "log", + "os_info", + "parking_lot", + "pbjson-types", + "prost 0.12.6", + "rand 0.9.4", + "reqwest", + "rustls-native-certs", + "scopeguard", + "serde", + "serde_json", + "sha2 0.10.9", + "thiserror 2.0.18", + "tokio", + "tokio-rustls", + "tokio-tungstenite", + "url", +] + +[[package]] +name = "livekit-datatrack" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6618b1fe027f45b2ddd42794f3a263e07a69949fec0ed5f8d0a13e394d19525f" +dependencies = [ + "anyhow", + "bytes", + "from_variants", + "futures-core", + "futures-util", + "indexmap", + "livekit-protocol", + "livekit-runtime", + "log", + "rand 0.9.4", + "thiserror 2.0.18", + "tokio", + "tokio-stream", +] + +[[package]] +name = "livekit-protocol" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "565d83442aa99684dba5d0841fa342eb572fbd916ba2aa5e92801ecf76620026" +dependencies = [ + "pbjson", + "pbjson-types", + "prost 0.12.6", + "serde", +] + +[[package]] +name = "livekit-runtime" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532e84c6cdc5fe774f2b5d9912597b5f3bea561927a48296d03e24549d21c3f6" +dependencies = [ + "tokio", + "tokio-stream", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "maplit" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" + +[[package]] +name = "matrixmultiply" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +dependencies = [ + "autocfg", + "rawpointer", +] + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "memmap2" +version = "0.5.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327" +dependencies = [ + "libc", +] + +[[package]] +name = "memmap2" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" +dependencies = [ + "libc", +] + +[[package]] +name = "memo-map" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d1115007560874e373613744c6fba374c17688327a71c1476d1a5954cc857b" + +[[package]] +name = "metal" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e198a0ee42bdbe9ef2c09d0b9426f3b2b47d90d93a4a9b0395c4cea605e92dc0" +dependencies = [ + "bitflags 1.3.2", + "block", + "cocoa", + "core-graphics", + "foreign-types", + "log", + "objc", +] + +[[package]] +name = "minijinja" +version = "2.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2929e494b2280e1e18959bb2e121da03347ae896896fdfaceaab43c88a02803f" +dependencies = [ + "memo-map", + "serde", +] + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "mozjpeg" +version = "0.10.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7891b80aaa86097d38d276eb98b3805d6280708c4e0a1e6f6aed9380c51fec9" +dependencies = [ + "arrayvec", + "bytemuck", + "libc", + "mozjpeg-sys", + "rgb", +] + +[[package]] +name = "mozjpeg-sys" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f0dc668bf9bf888c88e2fb1ab16a406d2c380f1d082b20d51dd540ab2aa70c1" +dependencies = [ + "cc", + "dunce", + "libc", + "nasm-rs", +] + +[[package]] +name = "multimap" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d87ecb2933e8aeadb3e3a02b828fed80a7528047e68b4f424523a0981a3a084" + +[[package]] +name = "nanorand" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags 2.13.0", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash 2.1.2", + "tokio", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn 2.0.117", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading 0.9.0", +] + +[[package]] +name = "nasm-rs" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "706bf8a5e8c8ddb99128c3291d31bd21f4bcde17f0f4c20ec678d85c74faa149" +dependencies = [ + "jobserver", + "log", +] + +[[package]] +name = "ndarray" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb12d4e967ec485a5f71c6311fe28158e9d6f4bc4a447b474184d0f91a8fa32" +dependencies = [ + "matrixmultiply", + "num-complex", + "num-integer", + "num-traits", + "rawpointer", + "serde", +] + +[[package]] +name = "ndarray" +version = "0.17.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520080814a7a6b4a6e9070823bb24b4531daac8c4627e08ba5de8c5ef2f2752d" +dependencies = [ + "matrixmultiply", + "num-complex", + "num-integer", + "num-traits", + "portable-atomic", + "portable-atomic-util", + "rawpointer", +] + +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags 2.13.0", + "cfg-if 1.0.4", + "cfg_aliases", + "libc", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "nokhwa" +version = "0.10.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d63f10b450319a0ace7aa8e0e25477d1fdb345313a97e220e886175539a1dbb" +dependencies = [ + "flume", + "image", + "nokhwa-bindings-linux", + "nokhwa-bindings-macos", + "nokhwa-bindings-windows", + "nokhwa-core", + "paste", + "thiserror 2.0.18", +] + +[[package]] +name = "nokhwa-bindings-linux" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb67e22201a53322291740ca064b20eaaade7222ef0349f312d9b37b004e1984" +dependencies = [ + "libc", + "nokhwa-core", + "v4l", +] + +[[package]] +name = "nokhwa-bindings-macos" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f70d3908ea68324e44a6b3a0f885aa59e433fb1f6678839d09e0df7d226fb42d" +dependencies = [ + "block", + "cocoa-foundation", + "core-foundation 0.10.1", + "core-media-sys", + "core-video-sys", + "flume", + "nokhwa-core", + "objc", + "once_cell", +] + +[[package]] +name = "nokhwa-bindings-windows" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5be28886bad8abcec3655c1f24b965b4cb596a72b23164c910c54439ce55d2a4" +dependencies = [ + "nokhwa-core", + "once_cell", + "windows", +] + +[[package]] +name = "nokhwa-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1cba20bebd3bd9ae22f9273ade5bbe49da3e047c8512b53fbaf8b4b9c80d496" +dependencies = [ + "bytes", + "image", + "mozjpeg", + "thiserror 2.0.18", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "nom" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" +dependencies = [ + "memchr", +] + +[[package]] +name = "nom-language" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2de2bc5b451bfedaef92c90b8939a8fff5770bdcc1fafd6239d086aab8fa6b29" +dependencies = [ + "nom 8.0.0", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-bigint-dig" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.6", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", + "serde", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", + "libm", +] + +[[package]] +name = "objc" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +dependencies = [ + "malloc_buf", + "objc_exception", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.13.0", + "block2", + "dispatch2", + "libc", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.13.0", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-core-video" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" +dependencies = [ + "bitflags 2.13.0", + "block2", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-io-surface", + "objc2-metal", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.13.0", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags 2.13.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-vision" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfc194758a2d5d7540b1ad283bfb9ca318ec608991892326e95b428230b2689b" +dependencies = [ + "objc2", + "objc2-core-foundation", + "objc2-core-video", + "objc2-foundation", +] + +[[package]] +name = "objc_exception" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" +dependencies = [ + "cc", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "oorandom" +version = "11.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "ordered-multimap" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ed8acf08e98e744e5384c8bc63ceb0364e68a6854187221c18df61c4797690e" +dependencies = [ + "dlv-list", + "hashbrown 0.13.2", +] + +[[package]] +name = "os_info" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf20a545b305cf1da722b236b5155c9bb35f1d5ceb28c048bd96ca842f41b5b" +dependencies = [ + "android_system_properties", + "log", + "nix", + "objc2", + "objc2-foundation", + "objc2-ui-kit", + "serde", + "windows-sys 0.61.2", +] + +[[package]] +name = "p256" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.9", +] + +[[package]] +name = "p384" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2 0.10.9", +] + +[[package]] +name = "page_size" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30d5b2194ed13191c1999ae0704b7839fb18384fa22e49b57eeaa97d79ce40da" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if 1.0.4", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pastey" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" + +[[package]] +name = "pbjson" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1030c719b0ec2a2d25a5df729d6cff1acf3cc230bf766f4f97833591f7577b90" +dependencies = [ + "base64 0.21.7", + "serde", +] + +[[package]] +name = "pbjson-build" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2580e33f2292d34be285c5bc3dba5259542b083cfad6037b6d70345f24dcb735" +dependencies = [ + "heck 0.4.1", + "itertools 0.11.0", + "prost 0.12.6", + "prost-types", +] + +[[package]] +name = "pbjson-types" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18f596653ba4ac51bdecbb4ef6773bc7f56042dc13927910de1684ad3d32aa12" +dependencies = [ + "bytes", + "chrono", + "pbjson", + "pbjson-build", + "prost 0.12.6", + "prost-build", + "serde", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "pem-rfc7468" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412" +dependencies = [ + "base64ct", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pest" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +dependencies = [ + "memchr", + "ucd-trie", +] + +[[package]] +name = "pest_derive" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" +dependencies = [ + "pest", + "pest_generator", +] + +[[package]] +name = "pest_generator" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" +dependencies = [ + "pest", + "pest_meta", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "pest_meta" +version = "2.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" +dependencies = [ + "pest", + "sha2 0.10.9", +] + +[[package]] +name = "petgraph" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" +dependencies = [ + "fixedbitset", + "indexmap", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + +[[package]] +name = "pkcs8" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7" +dependencies = [ + "der", + "spki", +] + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.13.0", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.117", +] + +[[package]] +name = "primal-check" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0d895b311e3af9902528fbb8f928688abbd95872819320517cc24ca6b2bd08" +dependencies = [ + "num-integer", +] + +[[package]] +name = "primeorder" +version = "0.13.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6" +dependencies = [ + "elliptic-curve", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "prost" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b82eaa1d779e9a4bc1c3217db8ffbeabaae1dca241bf70183242128d48681cd" +dependencies = [ + "bytes", + "prost-derive 0.11.9", +] + +[[package]] +name = "prost" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +dependencies = [ + "bytes", + "prost-derive 0.12.6", +] + +[[package]] +name = "prost" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528ac67416ff8646872a3c02cad9cc4ee5dc9f9540c9b10771855c95cb2e5ae1" +dependencies = [ + "bytes", + "prost-derive 0.14.4", +] + +[[package]] +name = "prost-build" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22505a5c94da8e3b7c2996394d1c933236c4d743e81a410bcca4e6989fc066a4" +dependencies = [ + "bytes", + "heck 0.5.0", + "itertools 0.12.1", + "log", + "multimap", + "once_cell", + "petgraph", + "prettyplease", + "prost 0.12.6", + "prost-types", + "regex", + "syn 2.0.117", + "tempfile", +] + +[[package]] +name = "prost-derive" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d2d8d10f3c6ded6da8b05b5fb3b8a5082514344d56c9f871412d29b4e075b4" +dependencies = [ + "anyhow", + "itertools 0.10.5", + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "prost-derive" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +dependencies = [ + "anyhow", + "itertools 0.12.1", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "prost-derive" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b570b25f7617e43d59005d0990ccb79e950a423952cea19671b7a876da390adf" +dependencies = [ + "anyhow", + "itertools 0.14.0", + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "prost-types" +version = "0.12.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +dependencies = [ + "prost 0.12.6", +] + +[[package]] +name = "pxfm" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash 2.1.2", + "rustls", + "socket2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand 0.9.4", + "ring", + "rustc-hash 2.1.2", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "rand" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +dependencies = [ + "libc", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.5", +] + +[[package]] +name = "rand" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" +dependencies = [ + "chacha20", + "getrandom 0.4.2", + "rand_core 0.10.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.5", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_distr" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32cb0b9bc82b0a0876c2dd994a7e7a2683d3e7390ca40e6886785ef0c7e3ee31" +dependencies = [ + "num-traits", + "rand 0.8.6", +] + +[[package]] +name = "rand_distr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d431c2703ccf129de4d45253c03f49ebb22b97d6ad79ee3ecfc7e3f4862c1d8" +dependencies = [ + "num-traits", + "rand 0.10.1", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "realfft" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f821338fddb99d089116342c46e9f1fbf3828dba077674613e734e01d6ea8677" +dependencies = [ + "rustfft", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.13.0", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "rfc6979" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2" +dependencies = [ + "hmac", + "subtle", +] + +[[package]] +name = "rgb" +version = "0.8.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b34b781b31e5d73e9fbc8689c70551fd1ade9a19e3e28cfec8580a79290cc4" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if 1.0.4", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rsa" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" +dependencies = [ + "const-oid 0.9.6", + "digest 0.10.7", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + +[[package]] +name = "rtrb" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ade083ccbb4bf536df69d1f6432cc23deb7acccff86b183f3923a6fd56a1153" + +[[package]] +name = "rubato" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6dd52e80cfc21894deadf554a5673002938ae4625f7a283e536f9cf7c17b0d5" +dependencies = [ + "num-complex", + "num-integer", + "num-traits", + "realfft", +] + +[[package]] +name = "rust-ini" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e2a3bcec1f113553ef1c88aae6c020a369d03d55b58de9869a0908930385091" +dependencies = [ + "cfg-if 1.0.4", + "ordered-multimap", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustfft" +version = "6.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21db5f9893e91f41798c88680037dba611ca6674703c1a18601b01a72c8adb89" +dependencies = [ + "num-complex", + "num-integer", + "num-traits", + "primal-check", + "strength_reduce", + "transpose", +] + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.13.0", + "errno", + "libc", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.13.0", + "errno", + "libc", + "linux-raw-sys 0.12.1", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "safetensors" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "675656c1eabb620b921efea4f9199f97fc86e36dd6ffd1fbbe48d0f59a4987f5" +dependencies = [ + "hashbrown 0.16.1", + "serde", + "serde_json", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "scan_fmt" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b53b0a5db882a8e2fdaae0a43f7b39e7e9082389e978398bdf223a55b581248" +dependencies = [ + "regex", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scratch" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68f2ec51b097e4c1a75b681a8bec621909b5e91f15bb7b840c4f2f7b01148b2" + +[[package]] +name = "sec1" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc" +dependencies = [ + "base16ct", + "der", + "generic-array", + "pkcs8", + "subtle", + "zeroize", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.13.0", + "core-foundation 0.10.1", + "core-foundation-sys 0.8.7", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys 0.8.7", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if 1.0.4", + "cpufeatures 0.3.0", + "digest 0.11.3", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "signature" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" +dependencies = [ + "digest 0.10.7", + "rand_core 0.6.4", +] + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + +[[package]] +name = "spki" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d" +dependencies = [ + "base64ct", + "der", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "strength_reduce" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" + +[[package]] +name = "string-interner" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e2531d8525b29b514d25e275a43581320d587b86db302b9a7e464bac579648" +dependencies = [ + "cfg-if 1.0.4", + "hashbrown 0.11.2", + "serde", +] + +[[package]] +name = "string-interner" +version = "0.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad3df9b59e2eded8d825c7c4363ad339a20fb6bc0b9a4778560f518f59910b15" +dependencies = [ + "hashbrown 0.16.1", + "serde", +] + +[[package]] +name = "strsim" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "system-deps" +version = "7.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396a35feb67335377e0251fcbc1092fc85c484bd4e3a7a54319399da127796e7" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "tar" +version = "0.4.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "target-lexicon" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" + +[[package]] +name = "tempfile" +version = "3.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" +dependencies = [ + "fastrand", + "getrandom 0.4.2", + "once_cell", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "time" +version = "0.3.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" + +[[package]] +name = "time-macros" +version = "0.2.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", + "tokio-util", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c" +dependencies = [ + "futures-util", + "log", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime", + "toml_parser", + "toml_writer", + "winnow", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap", + "toml_datetime", + "toml_parser", + "winnow", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags 2.13.0", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tract-core" +version = "0.19.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae91e4486af81c5a154dce2a1d7c0780d35c2de8bc42e94bdec995333f31b90" +dependencies = [ + "anyhow", + "bit-set 0.5.3", + "derive-new 0.5.9", + "downcast-rs 1.2.1", + "dyn-clone", + "educe", + "lazy_static", + "log", + "maplit", + "ndarray 0.15.6", + "num-integer", + "num-traits", + "rustfft", + "smallvec", + "tract-data 0.19.16", + "tract-linalg 0.19.16", +] + +[[package]] +name = "tract-core" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "666998e926c772fdcaba566fa1ae9c3c52c27e2aafb9f1c4b2407c2390ee5041" +dependencies = [ + "anyhow", + "anymap3", + "bit-set 0.10.0", + "derive-new 0.7.0", + "downcast-rs 2.0.2", + "dyn-clone", + "dyn-eq", + "erased-serde", + "inventory", + "lazy_static", + "log", + "maplit", + "ndarray 0.17.2", + "num-complex", + "num-integer", + "num-traits", + "pastey", + "rustfft", + "serde", + "smallvec", + "tract-data 0.23.1", + "tract-linalg 0.23.1", +] + +[[package]] +name = "tract-data" +version = "0.19.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "027e05e3537cb13f5e84b7664de25ed326a1d42c08d9985694f48f6efe3483ee" +dependencies = [ + "anyhow", + "educe", + "half", + "itertools 0.10.5", + "lazy_static", + "maplit", + "ndarray 0.15.6", + "nom 7.1.3", + "num-complex", + "num-integer", + "num-traits", + "scan_fmt", + "smallvec", + "string-interner 0.14.0", +] + +[[package]] +name = "tract-data" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba42a87db3dd9951b121baf8f43bd8ac5050f1adad5f1d8948f2008f8463931b" +dependencies = [ + "anyhow", + "downcast-rs 2.0.2", + "dyn-clone", + "dyn-eq", + "dyn-hash", + "half", + "itertools 0.14.0", + "lazy_static", + "libm", + "maplit", + "ndarray 0.17.2", + "nom 8.0.0", + "nom-language", + "num-integer", + "num-traits", + "parking_lot", + "scan_fmt", + "smallvec", + "string-interner 0.20.0", +] + +[[package]] +name = "tract-extra" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7064da339a5231bd4e35108bafbed504f7e27aa3b9ac9c0fb1f61cee23d20007" +dependencies = [ + "tract-nnef 0.23.1", + "tract-pulse 0.23.1", +] + +[[package]] +name = "tract-hir" +version = "0.19.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5f72648d914f724e188cf679f3dd74f069eea36d8670633acf8889b94391a54" +dependencies = [ + "derive-new 0.5.9", + "educe", + "log", + "tract-core 0.19.16", +] + +[[package]] +name = "tract-hir" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c2e7d7703cc8ff7b266402a2a16f4816776b70bdbf7b8c293b650c45a54d883" +dependencies = [ + "derive-new 0.7.0", + "log", + "tract-core 0.23.1", +] + +[[package]] +name = "tract-linalg" +version = "0.19.16" +dependencies = [ + "cc", + "derive-new 0.5.9", + "downcast-rs 1.2.1", + "dyn-clone", + "half", + "lazy_static", + "liquid", + "liquid-core", + "log", + "num-traits", + "paste", + "scan_fmt", + "smallvec", + "tract-data 0.19.16", + "unicode-normalization", + "walkdir", +] + +[[package]] +name = "tract-linalg" +version = "0.23.1" +dependencies = [ + "byteorder", + "cc", + "derive-new 0.7.0", + "downcast-rs 2.0.2", + "dyn-clone", + "dyn-eq", + "dyn-hash", + "half", + "lazy_static", + "log", + "minijinja", + "num-traits", + "pastey", + "scan_fmt", + "tract-data 0.23.1", + "walkdir", +] + +[[package]] +name = "tract-nnef" +version = "0.19.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0895153ea97091967f92121715a5c80fbcc0841c53a1b2fb9ba93a89ed644357" +dependencies = [ + "byteorder", + "flate2", + "log", + "nom 7.1.3", + "tar", + "tract-core 0.19.16", + "walkdir", +] + +[[package]] +name = "tract-nnef" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd910aa2ff5b8129400d12e3127d77df94b1f26778467b0889aeaa836e166f44" +dependencies = [ + "byteorder", + "erased-serde", + "flate2", + "log", + "minijinja", + "nom 8.0.0", + "nom-language", + "safetensors", + "serde", + "serde_json", + "simd-adler32", + "tar", + "tract-core 0.23.1", + "walkdir", +] + +[[package]] +name = "tract-onnx" +version = "0.19.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21f752abf4627894827cdfea0ffd9089bcd31d4b5467bb32bd0908a4d3ab22b5" +dependencies = [ + "bytes", + "derive-new 0.5.9", + "educe", + "log", + "memmap2 0.5.10", + "num-integer", + "prost 0.11.9", + "smallvec", + "tract-hir 0.19.16", + "tract-nnef 0.19.16", + "tract-onnx-opl 0.19.16", +] + +[[package]] +name = "tract-onnx" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8492cd812ed69cd1c18347c9ee6e930f086685f849a6a95c56a4f3f8b81503b5" +dependencies = [ + "bytes", + "derive-new 0.7.0", + "dyn-eq", + "log", + "memmap2 0.9.10", + "num-integer", + "prost 0.14.4", + "smallvec", + "tract-extra", + "tract-hir 0.23.1", + "tract-nnef 0.23.1", + "tract-onnx-opl 0.23.1", + "tract-transformers", +] + +[[package]] +name = "tract-onnx-opl" +version = "0.19.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb740c8e25f65f6e070c9438fb4c2671d72d36470c01e49b3002e61c7c01d0cb" +dependencies = [ + "educe", + "getrandom 0.2.17", + "log", + "rand 0.8.6", + "rand_distr 0.4.3", + "rustfft", + "tract-nnef 0.19.16", +] + +[[package]] +name = "tract-onnx-opl" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f21fba6a0f1658210ab6e339657ea43234a9647dd5d859ee0147b3c67b4a962" +dependencies = [ + "dyn-eq", + "getrandom 0.4.2", + "log", + "rand 0.10.1", + "rand_distr 0.6.0", + "rustfft", + "tract-extra", + "tract-nnef 0.23.1", +] + +[[package]] +name = "tract-pulse" +version = "0.19.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502f5ecdcb6c84d3c7caa1bbb2f23dbe0980055f8bd615fe3ef9ba37cb8f22dd" +dependencies = [ + "downcast-rs 1.2.1", + "lazy_static", + "log", + "tract-pulse-opl 0.19.16", +] + +[[package]] +name = "tract-pulse" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "362fc52e40001fa7e35c88d71adae94fa40907629dd0be8a1a4ba1147528b551" +dependencies = [ + "downcast-rs 2.0.2", + "dyn-eq", + "erased-serde", + "lazy_static", + "log", + "serde", + "tract-pulse-opl 0.23.1", + "tract-transformers", +] + +[[package]] +name = "tract-pulse-opl" +version = "0.19.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5fcf838d40dd2b8b6454aaef8d4ce772aeca44e87c9628591d0b2919a06ed89" +dependencies = [ + "downcast-rs 1.2.1", + "lazy_static", + "tract-nnef 0.19.16", +] + +[[package]] +name = "tract-pulse-opl" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2531976a7761f8eb159a03148c8be9400f028161ab407a95ead65026d8aace0" +dependencies = [ + "downcast-rs 2.0.2", + "dyn-eq", + "lazy_static", + "tract-nnef 0.23.1", +] + +[[package]] +name = "tract-transformers" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1b961afd943bfc593d36870bae6601d2f3e427ed2868370f97a2969708ff602" +dependencies = [ + "float-ord", + "rayon", + "tract-nnef 0.23.1", +] + +[[package]] +name = "transpose" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" +dependencies = [ + "num-integer", + "strength_reduce", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "tungstenite" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.4", + "rustls", + "rustls-pki-types", + "sha1", + "thiserror 2.0.18", + "url", +] + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "ucd-trie" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-normalization" +version = "0.1.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "v4l" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8fbfea44a46799d62c55323f3c55d06df722fbe577851d848d328a1041c3403" +dependencies = [ + "bitflags 1.3.2", + "libc", + "v4l2-sys-mit", +] + +[[package]] +name = "v4l2-sys-mit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6779878362b9bacadc7893eac76abe69612e8837ef746573c4a5239daf11990b" +dependencies = [ + "bindgen", +] + +[[package]] +name = "version-compare" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +dependencies = [ + "wit-bindgen 0.51.0", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" +dependencies = [ + "cfg-if 1.0.4", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.72" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.117", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.122" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags 2.13.0", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + +[[package]] +name = "web-sys" +version = "0.3.99" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webrtc-sys" +version = "0.3.33" +dependencies = [ + "cc", + "cxx", + "cxx-build", + "glob", + "log", + "pkg-config", + "webrtc-sys-build", +] + +[[package]] +name = "webrtc-sys-build" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d46da6b5a5cbd091fae0400f77189f4ca4807c0d9442b85838a584f28720570" +dependencies = [ + "anyhow", + "fs2", + "regex", + "reqwest", + "scratch", + "semver", + "zip", +] + +[[package]] +name = "weezl" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" + +[[package]] +name = "which" +version = "4.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" +dependencies = [ + "either", + "home", + "once_cell", + "rustix 0.38.44", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck 0.5.0", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck 0.5.0", + "indexmap", + "prettyplease", + "syn 2.0.117", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn 2.0.117", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags 2.13.0", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix 1.1.4", +] + +[[package]] +name = "yoke" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "byteorder", + "crc32fast", + "crossbeam-utils", + "flate2", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zune-core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" + +[[package]] +name = "zune-jpeg" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" +dependencies = [ + "zune-core", +] diff --git a/fluxer_desktop/native/webrtc-sender/Cargo.toml b/fluxer_desktop/native/webrtc-sender/Cargo.toml new file mode 100644 index 0000000..49bbc99 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/Cargo.toml @@ -0,0 +1,135 @@ +[package] +name = "fluxer_webrtc_sender" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" +exclude = [ + "vendor/tract-linalg-0.19.16", + "vendor/tract-linalg-0.23.1", +] + +[lib] +crate-type = ["cdylib", "rlib"] + +[features] +# `publisher` pulls the LiveKit native SDK (libwebrtc). It is on by default for +# the real addon build, but `cargo test --no-default-features` builds only the +# pure YUV-conversion core so the unit tests run anywhere (no libwebrtc). +default = ["publisher"] +publisher = ["dep:livekit", "dep:tokio", "dep:futures-util"] +camera-native = ["dep:nokhwa"] +# Exposes pub re-exports of private camera-background hot-path internals so the +# criterion bench target can call them; never enabled for the shipped addon. +bench-internals = [] + +[dependencies] +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8", "tokio_rt"]} +napi-derive = "3.5.6" +crossbeam-queue = "0.3" + +# LiveKit native SDK (wraps libwebrtc). Optional / feature-gated. The exact +# version is resolved on the Windows build host (where the libwebrtc prebuilt +# links); pinned loosely here. +# Keep LiveKit's default features (default = ["tokio"] -> the runtime + +# signal-client-tokio) AND add a TLS backend for the signal WebSocket -- without +# one, connecting to a wss:// SFU fails with "TLS support not compiled in". +# rustls-tls-native-roots = pure-Rust TLS using the OS root store (no OpenSSL). +livekit = {version = "0.7", optional = true, features = ["rustls-tls-native-roots"]} +tokio = {version = "1", optional = true, features = ["rt-multi-thread", "sync", "macros", "time"]} +parking_lot = "0.12" +fluxer_screen_frame_bus = { path = "../screen-frame-bus" } +futures-core = "0.3" +# StreamExt::next() for draining inbound NativeAudioStream/NativeVideoStream. +futures-util = {version = "0.3", optional = true} +libloading = "0.9.0" +fluxer_desktop_native = { path = "../rust" } +image = {version = "0.25.10", default-features = false, features = ["jpeg", "png", "webp", "gif"]} +# System-camera capture for the native camera-publish path. Optional / +# feature-gated for the shipped desktop builds. `input-native` maps to V4L2 on +# Linux, AVFoundation on macOS, and Media Foundation on Windows. +# BUILD-HOST NOTE: `decoding` pulls `mozjpeg-sys`, which statically builds +# libjpeg and -- with its default `nasm_simd` -- needs NASM + a C compiler on the +# build host. +nokhwa = {version = "0.10", optional = true, default-features = false, features = ["input-native", "decoding"]} + +# Person segmentation for camera background blur / replacement on platforms +# without a native segmentation API (Windows, Linux) and as the macOS fallback +# when Vision is unavailable. tract is a pure-Rust ONNX inference engine: no +# C++ runtime to link or ship per target, deterministic results, and the unit +# tests exercise the real model on any build host. It runs the bundled Apache-2.0 +# MediaPipe Selfie Segmenter landscape model (models/, ~450 KB, embedded into the +# addon) in ~10 ms per 256x144 frame on a laptop core; an inference-cadence +# governor halves the rate on machines that miss the frame budget. Cost: ~40 +# transitive crates, all pure Rust, compile-time only impact elsewhere. +tract-onnx = "0.23" + +# DeepFilterNet3 noise suppression for the native device-microphone publish +# path, replacing the WebRTC-APM fallback when the renderer requests the +# deepFilter profile (parity with the web build's deepfilternet3-noise-filter +# WASM chain). The crates.io release (0.2.5, 2022) predates DFN3, so this pins +# the upstream git repo at the v0.5.6 release tag -- the combination the shipped +# deep-filter binaries were built from. `tract` + `default-model` run the +# bundled DFN3 ONNX models (~2 MB, embedded into the addon) on the same +# pure-Rust tract inference engine already used for person segmentation, albeit +# at tract 0.19 (deep_filter's tested series) alongside our 0.23 tree: +# compile-time cost only, no new native or C++ dependencies. +deep_filter = {git = "https://github.com/Rikorose/DeepFilterNet", rev = "978576aa8400552a4ce9730838c635aa30db5e61", default-features = false, features = ["tract", "default-model"]} +# deep_filter declares tract ^0.19.4, but newer releases in that semver range +# regress DFN3 model codegen (0.21.4 fails with "duplicate name .../Conv.bias" +# on the transposed convolutions). These renamed dependency keys exist solely +# to pin deep_filter's tract subtree to 0.19.16, the version in upstream's own +# v0.5.6 lockfile; tract's internal `=` pins drag tract-core/hir/data/linalg +# along. Never import these directly: the 0.23 `tract-onnx` entry above is the +# one the rest of this crate uses. +tract-onnx-deepfilter-pin = {package = "tract-onnx", version = "=0.19.16"} +tract-pulse-deepfilter-pin = {package = "tract-pulse", version = "=0.19.16"} +# Pinned to the 0.15 family to match deep_filter's public API surface +# (DfTract::process takes ndarray 0.15 ArrayView2 arguments). +ndarray = "0.15" + +# macOS person segmentation for camera background blur / replacement. Vision's +# VNGeneratePersonSegmentationRequest produces the per-pixel person mask that the +# camera background transform composites with; without it the transform falls +# back to a fixed portrait ellipse. Pinned to the objc2 0.6 / framework 0.3 +# family already used across fluxer_desktop/native, so no new dependency tree. +[target.'cfg(target_os = "macos")'.dependencies] +objc2 = "0.6" +objc2-foundation = {version = "0.3", features = ["NSArray", "NSDictionary", "NSError", "NSObject", "NSString"]} +objc2-vision = {version = "0.3", default-features = false, features = ["std", "VNRequest", "VNStatefulRequest", "VNGeneratePersonSegmentationRequest", "VNRequestHandler", "VNObservation", "VNTypes", "objc2-core-video", "objc2-core-foundation"]} +objc2-core-video = {version = "0.3", features = ["CVPixelBuffer", "CVImageBuffer", "CVBuffer", "CVReturn", "CVBase"]} +objc2-core-foundation = {version = "0.3", features = ["CFDictionary", "CFBase"]} + +[build-dependencies] +napi-build = "2.3.2" + +[dev-dependencies] +# Test-only (never linked into the shipped addon): parses the committed golden +# event fixtures so the renderer's (eventType, jsonPayload) parser contract is +# regression-locked offline, without a live SFU or a second identity. +serde_json = "1" +criterion = {version = "0.8", default-features = false, features = ["cargo_bench_support", "html_reports"]} + +[[bench]] +name = "frame_bus" +harness = false +required-features = [] + +[[bench]] +name = "camera_background" +harness = false +required-features = ["bench-internals"] + +[[bench]] +name = "deep_filter" +harness = false +required-features = ["bench-internals"] + +[patch.crates-io] +libwebrtc = { path = "vendor/libwebrtc" } +webrtc-sys = { path = "vendor/webrtc-sys" } +tract-linalg-019 = { package = "tract-linalg", path = "vendor/tract-linalg-0.19.16" } +tract-linalg-023 = { package = "tract-linalg", path = "vendor/tract-linalg-0.23.1" } diff --git a/fluxer_desktop/native/webrtc-sender/benches/baseline.json b/fluxer_desktop/native/webrtc-sender/benches/baseline.json new file mode 100644 index 0000000..ec4689e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/benches/baseline.json @@ -0,0 +1,95 @@ +{ + "measured_at": "85e057a273fd", + "host": "darwin-arm64-apple-silicon", + "regression_budget_percent": 5.0, + "criterion_args": { + "warm_up_time_sec": 2, + "measurement_time_sec": 5 + }, + "benches": { + "frame_bus::get_sink": { + "median_ns": 13.292, + "low_ns": 13.189, + "high_ns": 13.408, + "budget_percent_override": 10.0, + "note": "~13ns op; noise floor reasoning." + }, + "frame_bus::get_sink_miss": { + "median_ns": 3.9658, + "low_ns": 3.9444, + "high_ns": 3.9918, + "budget_percent_override": 15.0, + "note": "~4ns op; below the practical resolution of repeated criterion runs." + }, + "frame_bus::native_handle_enqueue/nv12_320x180": { + "median_ns": 1.808, + "low_ns": 1.7961, + "high_ns": 1.8205, + "budget_percent_override": 15.0, + "note": "~2ns op; below the practical resolution of repeated criterion runs." + }, + "frame_bus::native_handle_enqueue/nv12_1920x1080": { + "median_ns": 1.8086, + "low_ns": 1.8008, + "high_ns": 1.8171, + "budget_percent_override": 15.0, + "note": "~2ns op; below the practical resolution of repeated criterion runs." + }, + "frame_bus::native_handle_enqueue/nv12_3840x2160": { + "median_ns": 1.8141, + "low_ns": 1.7964, + "high_ns": 1.8333, + "budget_percent_override": 15.0, + "note": "~2ns op; below the practical resolution of repeated criterion runs." + }, + "frame_bus::native_handle_enqueue/bgra_320x180": { + "median_ns": 1.8261, + "low_ns": 1.8172, + "high_ns": 1.8361, + "budget_percent_override": 15.0, + "note": "~2ns op; below the practical resolution of repeated criterion runs." + }, + "frame_bus::native_handle_enqueue/bgra_1920x1080": { + "median_ns": 1.7952, + "low_ns": 1.7877, + "high_ns": 1.8027, + "budget_percent_override": 15.0, + "note": "~2ns op; below the practical resolution of repeated criterion runs." + }, + "frame_bus::enqueue_discard/nv12_320x180": { + "median_ns": 1391.4, + "low_ns": 1342.4, + "high_ns": 1455.8, + "budget_percent_override": 20.0, + "note": "Allocator-bound: bench does vec![0; total] per iter. Run-to-run sigma observed ~15%; budget set above that." + }, + "frame_bus::enqueue_discard/nv12_1920x1080": { + "median_ns": 29502.0, + "low_ns": 28397.0, + "high_ns": 30819.0, + "budget_percent_override": 20.0, + "note": "Allocator-bound; see nv12_320x180 note." + }, + "frame_bus::enqueue_discard/nv12_3840x2160": { + "median_ns": 121900.0, + "low_ns": 114880.0, + "high_ns": 129680.0, + "budget_percent_override": 25.0, + "note": "Largest 4K allocator-bound bench; widest budget." + }, + "frame_bus::enqueue_discard/bgra_320x180": { + "median_ns": 2807.2, + "low_ns": 2695.1, + "high_ns": 2937.6, + "budget_percent_override": 20.0, + "note": "Allocator-bound; see nv12_320x180 note." + }, + "frame_bus::enqueue_discard/bgra_1920x1080": { + "median_ns": 83692.0, + "low_ns": 78328.0, + "high_ns": 90200.0, + "budget_percent_override": 25.0, + "note": "Allocator-bound 1080p BGRA bench; widest budget." + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/benches/camera_background.rs b/fluxer_desktop/native/webrtc-sender/benches/camera_background.rs new file mode 100644 index 0000000..b9e2ea9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/benches/camera_background.rs @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use criterion::{Criterion, Throughput, criterion_group, criterion_main}; +use fluxer_webrtc_sender::bench_internals::{ + BlurScratch, MaskRefiner, blur_plane_masked, composite_masked_plane, +}; +use std::hint::black_box; + +const BENCH_WIDTH: usize = 1280; +const BENCH_HEIGHT: usize = 720; +const BENCH_BLUR_RADIUS_PASS: usize = 6; +const BENCH_MASK_FEATHER_PX: usize = 64; + +fn synth_plane(seed: usize) -> Vec { + let mut plane = vec![0u8; BENCH_WIDTH * BENCH_HEIGHT]; + for (index, value) in plane.iter_mut().enumerate() { + *value = ((index * 31 + seed) % 251) as u8; + } + plane +} + +fn synth_person_mask() -> Vec { + let mut mask = vec![0u8; BENCH_WIDTH * BENCH_HEIGHT]; + let person_edge = BENCH_WIDTH / 2; + for row in mask.chunks_exact_mut(BENCH_WIDTH) { + for (x, value) in row.iter_mut().enumerate() { + *value = if x < person_edge { + 255 + } else if x < person_edge + BENCH_MASK_FEATHER_PX { + (255 - (x - person_edge) * 255 / BENCH_MASK_FEATHER_PX) as u8 + } else { + 0 + }; + } + } + mask +} + +fn bench_blur_plane_masked(c: &mut Criterion) { + let source = synth_plane(7); + let mask = synth_person_mask(); + let mut plane = source.clone(); + let mut scratch = BlurScratch::new(BENCH_WIDTH, BENCH_HEIGHT); + + let mut group = c.benchmark_group("camera_background::blur_plane_masked"); + group.throughput(Throughput::Bytes((BENCH_WIDTH * BENCH_HEIGHT) as u64)); + group.bench_function("1280x720_radius_pass6", |b| { + b.iter(|| { + plane.copy_from_slice(&source); + blur_plane_masked( + black_box(&mut plane), + BENCH_WIDTH, + BENCH_HEIGHT, + black_box(&mask), + BENCH_BLUR_RADIUS_PASS, + &mut scratch, + ); + black_box(&plane); + }) + }); + group.finish(); +} + +fn bench_mask_refine(c: &mut Criterion) { + let luma = synth_plane(13); + let raw_mask = synth_person_mask(); + let mut mask = raw_mask.clone(); + let mut refiner = MaskRefiner::new(BENCH_WIDTH, BENCH_HEIGHT); + + let mut group = c.benchmark_group("camera_background::mask_refine"); + group.throughput(Throughput::Bytes((BENCH_WIDTH * BENCH_HEIGHT) as u64)); + group.bench_function("1280x720_guided_refine", |b| { + b.iter(|| { + mask.copy_from_slice(&raw_mask); + refiner.refine(black_box(&luma), black_box(&mut mask)); + black_box(&mask); + }) + }); + group.finish(); +} + +fn bench_composite_masked_plane(c: &mut Criterion) { + let source = synth_plane(11); + let background = synth_plane(151); + let mask = synth_person_mask(); + let mut plane = source.clone(); + + let mut group = c.benchmark_group("camera_background::composite_masked_plane"); + group.throughput(Throughput::Bytes((BENCH_WIDTH * BENCH_HEIGHT) as u64)); + group.bench_function("1280x720_feathered_mask", |b| { + b.iter(|| { + plane.copy_from_slice(&source); + composite_masked_plane( + black_box(&mut plane), + black_box(&background), + BENCH_WIDTH, + BENCH_HEIGHT, + &mask, + ); + black_box(&plane); + }) + }); + group.finish(); +} + +criterion_group!( + benches, + bench_blur_plane_masked, + bench_mask_refine, + bench_composite_masked_plane +); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/benches/deep_filter.rs b/fluxer_desktop/native/webrtc-sender/benches/deep_filter.rs new file mode 100644 index 0000000..0df6110 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/benches/deep_filter.rs @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use criterion::{Criterion, Throughput, criterion_group, criterion_main}; +use fluxer_webrtc_sender::bench_internals::{ + DEEP_FILTER_FRAME_SAMPLES, DEEP_FILTER_NOISE_REDUCTION_LEVEL_MAX, DeepFilterProcessor, +}; +use std::hint::black_box; + +fn next_noise_sample(seed: &mut u32) -> i16 { + *seed = seed.wrapping_mul(1_664_525).wrapping_add(1_013_904_223); + ((*seed >> 16) as u16 as i16) / 4 +} + +fn noise_frame(seed: &mut u32) -> Vec { + let mut frame = vec![0i16; DEEP_FILTER_FRAME_SAMPLES]; + for sample in frame.iter_mut() { + *sample = next_noise_sample(seed); + } + frame +} + +fn bench_deep_filter_process_frame(c: &mut Criterion) { + let mut processor = DeepFilterProcessor::new(DEEP_FILTER_NOISE_REDUCTION_LEVEL_MAX) + .expect("embedded model must initialize"); + let mut seed = 0x9e37_79b9u32; + let mut group = c.benchmark_group("deep_filter"); + group.throughput(Throughput::Elements(DEEP_FILTER_FRAME_SAMPLES as u64)); + group.bench_function("process_frame_10ms", |bencher| { + bencher.iter_batched( + || noise_frame(&mut seed), + |mut frame| { + processor + .process_frame(black_box(&mut frame)) + .expect("processing must succeed"); + frame + }, + criterion::BatchSize::SmallInput, + ); + }); + group.finish(); +} + +criterion_group!(benches, bench_deep_filter_process_frame); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/benches/frame_bus.rs b/fluxer_desktop/native/webrtc-sender/benches/frame_bus.rs new file mode 100644 index 0000000..c51c6f7 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/benches/frame_bus.rs @@ -0,0 +1,220 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use criterion::{BenchmarkId, Criterion, Throughput, criterion_group, criterion_main}; +use fluxer_screen_frame_bus::{ + BgraFrame, EnqueueOutcome, NATIVE_SCREEN_FRAME_SINK_ACCEPTED, + NATIVE_SCREEN_FRAME_SINK_HANDLE_MAGIC, NATIVE_SCREEN_FRAME_SINK_HANDLE_VERSION, + NativeScreenFrameSinkHandle, Nv12Frame, ScreenFrame, ScreenFrameSink, get_sink, register_sink, + unregister_sink, +}; +use std::ffi::c_void; +use std::hint::black_box; +use std::sync::Arc; +use std::sync::atomic::{AtomicU64, Ordering}; + +struct DiscardSink(AtomicU64); +impl ScreenFrameSink for DiscardSink { + fn enqueue(&self, _frame: ScreenFrame) -> EnqueueOutcome { + self.0.fetch_add(1, Ordering::Relaxed); + EnqueueOutcome::Accepted + } +} + +struct NativeDiscardSink(AtomicU64); + +unsafe extern "C" fn retain_native_discard_sink(context: *const c_void) { + unsafe { Arc::increment_strong_count(context.cast::()) }; +} + +unsafe extern "C" fn release_native_discard_sink(context: *const c_void) { + unsafe { drop(Arc::from_raw(context.cast::())) }; +} + +unsafe extern "C" fn enqueue_native_discard_nv12( + context: *const c_void, + data: *const u8, + data_len: usize, + _width: u32, + _height: u32, + _stride_y: u32, + _stride_uv: u32, + _timestamp_us: i64, +) -> u32 { + if !context.is_null() && !data.is_null() && data_len > 0 { + unsafe { + (*context.cast::()) + .0 + .fetch_add(1, Ordering::Relaxed) + }; + } + NATIVE_SCREEN_FRAME_SINK_ACCEPTED +} + +unsafe extern "C" fn enqueue_native_discard_bgra( + context: *const c_void, + data: *const u8, + data_len: usize, + _width: u32, + _height: u32, + _stride: u32, + _timestamp_us: i64, +) -> u32 { + if !context.is_null() && !data.is_null() && data_len > 0 { + unsafe { + (*context.cast::()) + .0 + .fetch_add(1, Ordering::Relaxed) + }; + } + NATIVE_SCREEN_FRAME_SINK_ACCEPTED +} + +fn native_discard_handle() -> ( + Arc, + fluxer_screen_frame_bus::NativeScreenFrameSinkHandleRef, +) { + let sink = Arc::new(NativeDiscardSink(AtomicU64::new(0))); + let raw_context = Arc::into_raw(sink.clone()).cast::(); + let handle = NativeScreenFrameSinkHandle { + magic: NATIVE_SCREEN_FRAME_SINK_HANDLE_MAGIC, + version: NATIVE_SCREEN_FRAME_SINK_HANDLE_VERSION, + context: raw_context, + retain: retain_native_discard_sink, + release: release_native_discard_sink, + enqueue_nv12: Some(enqueue_native_discard_nv12), + enqueue_bgra: Some(enqueue_native_discard_bgra), + enqueue_mac_cv_pixel_buffer: None, + enqueue_dmabuf: None, + enqueue_shared_texture: None, + }; + let retained = unsafe { handle.retain_ref().expect("valid native sink handle") }; + unsafe { release_native_discard_sink(raw_context) }; + (sink, retained) +} + +fn bench_registry_lookup(c: &mut Criterion) { + let id = format!("bench:registry:{}", std::process::id()); + register_sink( + id.clone(), + Arc::new(DiscardSink(AtomicU64::new(0))) as Arc, + ); + + c.bench_function("frame_bus::get_sink", |b| { + b.iter(|| { + let s = get_sink(black_box(id.as_str())); + black_box(s); + }) + }); + + unregister_sink(&id); +} + +fn synth_nv12(width: u32, height: u32) -> ScreenFrame { + let total = (width * height) as usize + (width * (height / 2)) as usize; + let data = vec![0x80u8; total]; + ScreenFrame::Nv12(Nv12Frame { + data: data.into(), + width, + height, + stride_y: width, + stride_uv: width, + timestamp_us: 1, + }) +} + +fn synth_bgra(width: u32, height: u32) -> ScreenFrame { + let total = (width * height * 4) as usize; + let data = vec![0xff; total]; + ScreenFrame::Bgra(BgraFrame { + data, + width, + height, + stride: width * 4, + timestamp_us: 1, + }) +} + +fn bench_discard_sink_enqueue(c: &mut Criterion) { + let id = format!("bench:discard:{}", std::process::id()); + let sink = Arc::new(DiscardSink(AtomicU64::new(0))); + register_sink(id.clone(), sink.clone() as Arc); + + let mut group = c.benchmark_group("frame_bus::enqueue_discard"); + for (label, w, h) in [ + ("nv12_320x180", 320u32, 180u32), + ("nv12_1920x1080", 1920, 1080), + ("nv12_3840x2160", 3840, 2160), + ("bgra_320x180", 320, 180), + ("bgra_1920x1080", 1920, 1080), + ] { + let bytes = if label.starts_with("nv12") { + ((w * h) + (w * (h / 2))) as u64 + } else { + (w * h * 4) as u64 + }; + group.throughput(Throughput::Bytes(bytes)); + group.bench_with_input(BenchmarkId::from_parameter(label), &(), |b, _| { + b.iter(|| { + let frame = if label.starts_with("nv12") { + synth_nv12(w, h) + } else { + synth_bgra(w, h) + }; + let s = get_sink(id.as_str()).unwrap(); + let outcome = s.enqueue(black_box(frame)); + black_box(outcome); + }) + }); + } + group.finish(); + unregister_sink(&id); +} + +fn bench_native_handle_enqueue(c: &mut Criterion) { + let (_sink, handle) = native_discard_handle(); + let mut group = c.benchmark_group("frame_bus::native_handle_enqueue"); + for (label, w, h) in [ + ("nv12_320x180", 320u32, 180u32), + ("nv12_1920x1080", 1920, 1080), + ("nv12_3840x2160", 3840, 2160), + ("bgra_320x180", 320, 180), + ("bgra_1920x1080", 1920, 1080), + ] { + let bytes = if label.starts_with("nv12") { + ((w * h) + (w * (h / 2))) as usize + } else { + (w * h * 4) as usize + }; + let data = vec![0x80u8; bytes]; + group.throughput(Throughput::Bytes(bytes as u64)); + group.bench_with_input(BenchmarkId::from_parameter(label), &(), |b, _| { + b.iter(|| { + let outcome = if label.starts_with("nv12") { + handle.enqueue_nv12_copy(black_box(&data), w, h, w, w, 1) + } else { + handle.enqueue_bgra_copy(black_box(&data), w, h, w * 4, 1) + }; + black_box(outcome); + }) + }); + } + group.finish(); +} + +fn bench_registry_lookup_miss(c: &mut Criterion) { + c.bench_function("frame_bus::get_sink_miss", |b| { + b.iter(|| { + let s = get_sink(black_box("nonexistent")); + black_box(s); + }) + }); +} + +criterion_group!( + benches, + bench_registry_lookup, + bench_registry_lookup_miss, + bench_native_handle_enqueue, + bench_discard_sink_enqueue +); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/build.rs b/fluxer_desktop/native/webrtc-sender/build.rs new file mode 100644 index 0000000..6f564d0 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/build.rs @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +use std::env; +use std::path::{Path, PathBuf}; + +fn main() { + println!("cargo:rerun-if-changed=vendor/webrtc-sys/src/nvidia/NvCodec/include/cuda.h"); + println!("cargo:rustc-check-cfg=cfg(fluxer_linux_nvenc)"); + println!("cargo:rustc-check-cfg=cfg(fluxer_windows_nvenc)"); + println!("cargo:rustc-check-cfg=cfg(fluxer_windows_nvenc_encoder)"); + println!("cargo:rustc-check-cfg=cfg(fluxer_macos_videotoolbox)"); + + let cuda_include_dir = vendored_cuda_include_dir(); + let cuda_version = cuda_include_dir + .as_deref() + .and_then(read_cuda_version) + .unwrap_or(0); + let is_macos = env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("macos"); + let has_linux_nvenc = env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("linux") + && supports_webrtc_sys_nvenc_arch() + && cuda_version > 0; + let has_windows_nvenc = env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows") + && supports_webrtc_sys_nvenc_arch(); + let has_windows_nvenc_encoder = has_windows_nvenc; + + println!( + "cargo:rustc-env=FLUXER_LINUX_NVENC_COMPILED={}", + if has_linux_nvenc { "1" } else { "0" } + ); + println!( + "cargo:rustc-env=FLUXER_WINDOWS_NVENC_COMPILED={}", + if has_windows_nvenc { "1" } else { "0" } + ); + println!( + "cargo:rustc-env=FLUXER_WINDOWS_NVENC_ENCODER_COMPILED={}", + if has_windows_nvenc_encoder { "1" } else { "0" } + ); + println!("cargo:rustc-env=FLUXER_CUDA_VERSION={cuda_version}"); + if has_linux_nvenc { + println!("cargo:rustc-cfg=fluxer_linux_nvenc"); + } + if has_windows_nvenc { + println!("cargo:rustc-cfg=fluxer_windows_nvenc"); + } + if has_windows_nvenc_encoder { + println!("cargo:rustc-cfg=fluxer_windows_nvenc_encoder"); + } + println!( + "cargo:rustc-env=FLUXER_MACOS_VIDEOTOOLBOX_COMPILED={}", + if is_macos { "1" } else { "0" } + ); + if is_macos { + println!("cargo:rustc-cfg=fluxer_macos_videotoolbox"); + println!("cargo:rustc-link-lib=framework=CoreFoundation"); + println!("cargo:rustc-link-lib=framework=CoreMedia"); + println!("cargo:rustc-link-lib=framework=VideoToolbox"); + } + + configure_darwin_objc_linking(); + napi_build::setup(); +} + +fn configure_darwin_objc_linking() { + if env::var("CARGO_CFG_TARGET_OS").as_deref() != Ok("macos") { + return; + } + + println!("cargo:rustc-link-arg=-ObjC"); +} + +fn vendored_cuda_include_dir() -> Option { + let manifest_dir = env::var("CARGO_MANIFEST_DIR").ok()?; + Some( + PathBuf::from(manifest_dir) + .join("vendor") + .join("webrtc-sys") + .join("src") + .join("nvidia") + .join("NvCodec") + .join("include"), + ) +} + +fn read_cuda_version(include_dir: &Path) -> Option { + let content = std::fs::read_to_string(include_dir.join("cuda.h")).ok()?; + content.lines().find_map(|line| { + let mut parts = line.split_whitespace(); + match (parts.next(), parts.next(), parts.next()) { + (Some("#define"), Some("CUDA_VERSION"), Some(value)) => value.parse().ok(), + _ => None, + } + }) +} + +fn supports_webrtc_sys_nvenc_arch() -> bool { + let Ok(arch) = env::var("CARGO_CFG_TARGET_ARCH") else { + return false; + }; + matches!(arch.as_str(), "x86_64" | "i686" | "aarch64") || arch.contains("arm") +} diff --git a/fluxer_desktop/native/webrtc-sender/index.d.ts b/fluxer_desktop/native/webrtc-sender/index.d.ts new file mode 100644 index 0000000..bb55b8c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/index.d.ts @@ -0,0 +1,363 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +export interface AudioOutputDevice { + deviceId: string; + label: string; + isDefault: boolean; +} + +export interface AudioInputDevice { + deviceId: string; + label: string; + isDefault: boolean; +} + +export interface PublishMicrophoneOptions { + deviceId?: string; + echoCancellation?: boolean; + noiseSuppression?: boolean; + autoGainControl?: boolean; + deepFilter?: boolean; + deepFilterNoiseReductionLevel?: number; + maxBitrateBps?: number; +} + +export interface VoiceEngineV2BridgeCapabilities { + microphoneCapture: boolean; + syntheticMicrophonePcm: boolean; + cameraCapture: boolean; + nativeCameraBackgrounds: boolean; + screenShare: boolean; + screenShareEncodingUpdate: boolean; + screenShareAudio: boolean; + deviceLists: boolean; + outputDeviceSelection: boolean; + participantVolume: boolean; + remoteTrackSubscription: boolean; + dataChannel: boolean; + connectionStats: boolean; + nativeVideoFrames: boolean; + hardwareEncoderCapabilities: boolean; +} + +export type VoiceEngineTrackKind = 'audio' | 'video'; +export type VoiceEngineTrackSource = + | 'unknown' + | 'camera' + | 'microphone' + | 'screen_share' + | 'screen_share_audio' + | 'screenshare' + | 'screenshareAudio'; +export type VoiceEngineSubscriptionStatus = 'desired' | 'subscribed' | 'unsubscribed'; +export type VoiceEngineConnectionQuality = 'excellent' | 'good' | 'poor' | 'lost'; + +export interface VoiceEngineParticipantEventPayload { + sid: string; + identity: string; + name: string; +} + +export interface VoiceEngineTrackEventPayload { + participantSid: string; + identity: string; + participantName: string; + trackSid: string; + trackName: string; + kind: VoiceEngineTrackKind; + source: VoiceEngineTrackSource; + muted: boolean; +} + +export interface VoiceEngineSubscribedTrackEventPayload extends VoiceEngineTrackEventPayload { + subscribed: boolean; + subscriptionStatus: VoiceEngineSubscriptionStatus; +} + +export interface VoiceEngineTrackSubscriptionFailedEventPayload { + participantSid: string; + identity: string; + participantName: string; + trackSid: string; + error: string; + trackName?: string; + kind?: VoiceEngineTrackKind; + source?: VoiceEngineTrackSource; + muted?: boolean; + subscribed?: boolean; + subscriptionStatus?: VoiceEngineSubscriptionStatus; +} + +export interface VoiceEngineLocalTrackRepublishedEventPayload extends VoiceEngineTrackEventPayload { + previousTrackSid: string; +} + +export interface VoiceEngineV2BridgeEventPayloads { + connected: Record; + connectionState: {state: string}; + disconnected: {reason: string}; + participantJoined: VoiceEngineParticipantEventPayload; + participantLeft: VoiceEngineParticipantEventPayload; + participantNameChanged: {sid: string; identity: string; oldName: string; name: string}; + participantMetadataChanged: { + sid: string; + identity: string; + name: string; + oldMetadata: string; + metadata: string; + attributes: Record; + }; + participantAttributesChanged: { + sid: string; + identity: string; + name: string; + attributes: Record; + changedAttributes: Record; + }; + trackPublished: VoiceEngineSubscribedTrackEventPayload; + trackUnpublished: VoiceEngineSubscribedTrackEventPayload; + trackSubscribed: VoiceEngineSubscribedTrackEventPayload; + trackUnsubscribed: VoiceEngineSubscribedTrackEventPayload; + trackSubscriptionFailed: VoiceEngineTrackSubscriptionFailedEventPayload; + trackMuted: VoiceEngineTrackEventPayload; + trackUnmuted: VoiceEngineTrackEventPayload; + localTrackPublished: VoiceEngineTrackEventPayload; + localTrackUnpublished: VoiceEngineTrackEventPayload; + localTrackRepublished: VoiceEngineLocalTrackRepublishedEventPayload; + activeSpeakers: {sids: Array; participants: Array}; + connectionQuality: {sid: string; identity: string; name: string; quality: VoiceEngineConnectionQuality}; + dataReceived: { + payloadBytes: Array; + payloadText?: string; + topic?: string; + reliable: boolean; + kind: 'reliable' | 'lossy'; + participantSid?: string; + identity?: string; + participantName?: string; + }; + e2eeState: {sid: string; identity: string; name: string; state: string}; + stats: VoiceEngineV2BridgeStats; + audioPlaybackUnavailable: {message: string}; +} + +export type VoiceEngineKnownEventType = keyof VoiceEngineV2BridgeEventPayloads; +export type VoiceEngineV2BridgeEventType = VoiceEngineKnownEventType | (string & {}); + +export interface VoiceEngineOutboundStats { + trackSid: string; + source: string; + kind: VoiceEngineTrackKind; + codec?: string; + bitrateKbps: number; + packetsLost: number; + fps?: number; +} + +export interface VoiceEngineInboundStats { + participantSid: string; + trackSid: string; + kind: VoiceEngineTrackKind; + codec?: string; + bitrateKbps: number; + packetsLost: number; + jitterMs?: number; + audioLevel?: number; +} + +export interface VoiceEngineV2BridgeStats { + rttMs: number | null; + outbound: Array; + inbound: Array; + droppedVideoFrameCallbacks?: number; + send?: VoiceEngineSendStats | null; +} + +export interface VoiceEngineSendStats { + outgoingVideoQueueDepth: number; + outgoingVideoFramesProduced: number; + outgoingVideoFramesAccepted: number; + outgoingVideoFramesDropped: number; + outgoingVideoFramesCoalesced: number; + outgoingVideoFramesCaptured: number; + outgoingVideoCaptureFailures: number; + outgoingVideoEffectiveFps: number; + outgoingVideoTargetFps: number; + outgoingVideoMaxQueueAgeMs: number; + outgoingVideoMaxPushLatencyMs: number; + outgoingAudioBufferTargetMs: number; + outgoingAudioBufferMaxMs: number; + outgoingAudioUnderruns: number; + outgoingAudioRebuffers: number; + outgoingAudioMaxFrameGapMs: number; + adaptiveSendTier: string; + adaptiveSendReason: string; +} + +export interface PublishScreenShareOptions { + adaptiveSend?: boolean; + minVideoFps?: number; + maxAudioBufferMs?: number; + pacing?: 'sender' | 'source'; + captureId: string; + trackName?: string; +} + +export type VoiceEngineRemoteTrackSubscriptionQuality = 'low' | 'medium' | 'high'; + +export interface VoiceEngineV2BridgeRemoteTrackSubscriptionOptions { + participantIdentity: string; + source: string; + subscribed: boolean; + enabled?: boolean; + quality?: VoiceEngineRemoteTrackSubscriptionQuality; +} + +export interface PublishCameraOptions { + deviceId?: string; + width?: number; + height?: number; + frameRate?: number; + mirror?: boolean; + backgroundMode?: 'none' | 'non' | 'blur' | 'custom'; + backgroundCustomMediaPath?: string; + backgroundCustomMediaKind?: 'static' | 'animated' | 'video'; + backgroundBlurStrength?: number; + codec?: '' | 'vp8' | 'vp9' | 'h264' | 'av1' | 'h265' | 'hevc'; + maxBitrateBps?: number; + maxFramerate?: number; +} + +export interface PublishProcessedCameraOptions { + width: number; + height: number; + frameRate: number; +} + +export interface PublishProcessedCameraResult { + trackSid: string; +} + +export type NativeCameraFrameSinkHandle = object; + +export interface ProcessedCameraFrame { + format: 'i420'; + width: number; + height: number; + timestampUs: number; + data: Buffer; +} + +export interface CameraDeviceInfo { + deviceId: string; + label: string; + description: string; + index?: number | null; + deviceIdAliases: Array; +} + +export interface HardwareEncoderCapability { + available: boolean; + backend: 'nvenc' | 'videotoolbox' | 'none'; + compiled: boolean; + runtime: boolean; + codecs: Array; + zeroCopy: boolean; + nativeInputs: Array<'dmabuf' | 'd3d11-texture' | string>; + reason?: string; + detail?: string; +} + +export interface VoiceEngineV2BridgeConnectOptions { + autoSubscribe?: boolean; + adaptiveStream?: boolean; + dynacast?: boolean; +} + +export declare class VoiceEngine { + constructor(); + + setEventCallback(callback: (eventType: VoiceEngineV2BridgeEventType, jsonPayload: string) => void): void; + setVideoFrameCallback(callback: (metaJson: string, data: Buffer) => void): void; + clearVideoFrameCallback(): void; + setCountInboundAudio(enabled: boolean): void; + + connect(url: string, token: string, e2eeKey?: Buffer, options?: VoiceEngineV2BridgeConnectOptions): Promise; + disconnect(): Promise; + isConnected(): boolean; + + publishScreenShare( + width: number, + height: number, + codec: '' | 'vp8' | 'vp9' | 'h264' | 'av1' | 'h265' | 'hevc' | undefined, + maxBitrateBps: number | undefined, + maxFramerate: number | undefined, + simulcast: boolean | undefined, + options: PublishScreenShareOptions, + ): Promise; + updateScreenShareEncoding( + width: number, + height: number, + maxBitrateBps: number | undefined, + maxFramerate: number | undefined, + options: PublishScreenShareOptions, + ): Promise; + createScreenFrameSinkHandle(captureId: string): unknown | null; + unpublishScreenShare(): Promise; + isPublishingScreen(): boolean; + + publishScreenShareAudio(sampleRate: number, numChannels: number): Promise; + pushScreenSharePcm(buffer: Buffer, sampleRate: number, numChannels: number): Promise; + pushScreenShareFloat(buffer: Buffer, sampleRate: number, numChannels: number): Promise; + unpublishScreenShareAudio(): Promise; + isPublishingScreenAudio(): boolean; + + publishMicrophone(sampleRate: number, numChannels: number): Promise; + publishDeviceMicrophone(opts?: PublishMicrophoneOptions): Promise; + pushPcm(buffer: Buffer, sampleRate: number, numChannels: number): Promise; + setMicEnabled(enabled: boolean): Promise; + setSpeakingDetection(localThresholdRms: number, remoteThresholdRms: number): void; + + publishCamera(opts?: PublishCameraOptions): Promise; + updateCameraCapture(opts?: PublishCameraOptions): Promise; + publishProcessedCamera(opts: PublishProcessedCameraOptions): Promise; + publishNativeCameraSink(opts?: PublishCameraOptions): Promise; + createCameraFrameSinkHandle(): NativeCameraFrameSinkHandle | null; + pushProcessedCameraFrame(frame: ProcessedCameraFrame): Promise; + publishDeviceScreenShare(opts?: PublishCameraOptions): Promise; + listCameraDevices(): Array; + unpublishCamera(): Promise; + isPublishingCamera(): boolean; + + listAudioInputDevices(): Promise>; + listAudioOutputDevices(): Promise>; + setAudioOutputDevice(deviceId: string): Promise; + ensurePlatformAudio(): Promise; + + setParticipantVolume(participantSid: string, volume: number): Promise; + setRemoteTrackSubscription(options: VoiceEngineV2BridgeRemoteTrackSubscriptionOptions): Promise; + publishData( + payload: Buffer | ArrayBuffer | Uint8Array, + options?: {reliable?: boolean; topic?: string; destinationIdentities?: Array}, + ): Promise; + + getConnectionStats(): Promise; + + inboundAudioFrames(): number; + inboundVideoFrames(): number; + droppedVideoFrameCallbacks(): number; + droppedEngineEvents(): number; +} + +export declare function isSupported(): boolean; +export declare function getEngineBridgeVersion(): number | null; +export declare function assertEngineBridgeVersion(version: number): void; +export declare function getHardwareEncoderCapability(): HardwareEncoderCapability; +export declare function getHardwareEncoderCapabilities(): HardwareEncoderCapability; +export declare function getCapabilities(): VoiceEngineV2BridgeCapabilities; +export declare function hasNativeCameraBackgrounds(): boolean; +export declare function prewarmVoiceEngine(): void; +export declare function probeAudioDeviceModule(): Promise; +export declare const loadError: Error | null; +export declare function __nativeFileNameForTests(platform: string, arch: string): string; +export declare function __setBindingForTests(binding: unknown): void; diff --git a/fluxer_desktop/native/webrtc-sender/index.js b/fluxer_desktop/native/webrtc-sender/index.js new file mode 100644 index 0000000..ae96fdf --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/index.js @@ -0,0 +1,509 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync} = require('node:fs'); +const {join, sep} = require('node:path'); + +const MODULE_NAME = '@fluxer/webrtc-sender'; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName(platform = process.platform, arch = process.arch) { + if ((arch !== 'x64' && arch !== 'arm64') || !['darwin', 'linux', 'win32'].includes(platform)) { + throw new Error(`${MODULE_NAME} not supported on ${platform}-${arch}`); + } + if (platform === 'darwin') return `webrtc-sender.darwin-${arch}.node`; + if (platform === 'linux') return `webrtc-sender.linux-${arch}-gnu.node`; + if (platform === 'win32') return `webrtc-sender.win32-${arch}-msvc.node`; + throw new Error(`${MODULE_NAME} not supported on ${platform}-${arch}`); +} + +let binding = null; +let loadError = null; + +try { + const nativePath = join(resolveNativeRoot(), nativeFileName()); + if (existsSync(nativePath)) { + try { + binding = require(nativePath); + } catch (error) { + loadError = error instanceof Error ? error : new Error(String(error)); + } + } else { + loadError = new Error(`${MODULE_NAME} native binary missing: ${nativePath}`); + } +} catch (error) { + loadError = error instanceof Error ? error : new Error(String(error)); +} + +function isSupported() { + return Boolean(binding); +} + +function unavailableHardwareEncoderCapability(reason, detail) { + return { + available: false, + backend: 'none', + compiled: false, + runtime: false, + codecs: [], + zeroCopy: false, + nativeInputs: [], + reason, + detail, + }; +} + +function getHardwareEncoderCapability() { + if (!binding) { + return unavailableHardwareEncoderCapability( + 'native_binding_unavailable', + loadError ? loadError.message : `${MODULE_NAME} binding unavailable`, + ); + } + if (typeof binding.getHardwareEncoderCapability !== 'function') { + return unavailableHardwareEncoderCapability( + 'native_capability_unavailable', + `${MODULE_NAME} native binding does not export getHardwareEncoderCapability`, + ); + } + return binding.getHardwareEncoderCapability(); +} + +function getHardwareEncoderCapabilities() { + return getHardwareEncoderCapability(); +} + +function hasVoiceEngineMethod(name) { + const prototype = binding?.VoiceEngine?.prototype; + return Boolean(prototype && typeof prototype[name] === 'function'); +} + +function hasNativeCameraBackgrounds() { + if (!binding) return false; + if (typeof binding.hasNativeCameraBackgrounds !== 'function') return false; + return binding.hasNativeCameraBackgrounds() === true; +} + +function getCapabilities() { + const hasVoiceEngine = Boolean(binding && typeof binding.VoiceEngine === 'function'); + return { + microphoneCapture: hasVoiceEngineMethod('publishDeviceMicrophone'), + syntheticMicrophonePcm: hasVoiceEngineMethod('publishMicrophone'), + cameraCapture: hasVoiceEngineMethod('publishCamera') && hasVoiceEngineMethod('listCameraDevices'), + nativeCameraBackgrounds: hasNativeCameraBackgrounds(), + screenShare: hasVoiceEngineMethod('publishScreenShare') && hasVoiceEngineMethod('unpublishScreenShare'), + screenShareEncodingUpdate: hasVoiceEngineMethod('updateScreenShareEncoding'), + screenShareAudio: + hasVoiceEngineMethod('publishScreenShareAudio') && + hasVoiceEngineMethod('pushScreenSharePcm') && + hasVoiceEngineMethod('pushScreenShareFloat') && + hasVoiceEngineMethod('unpublishScreenShareAudio'), + deviceLists: hasVoiceEngineMethod('listAudioInputDevices') && hasVoiceEngineMethod('listAudioOutputDevices'), + outputDeviceSelection: hasVoiceEngineMethod('setAudioOutputDevice'), + participantVolume: hasVoiceEngineMethod('setParticipantVolume'), + remoteTrackSubscription: hasVoiceEngineMethod('setRemoteTrackSubscription'), + dataChannel: hasVoiceEngineMethod('publishData'), + connectionStats: hasVoiceEngineMethod('getConnectionStats'), + nativeVideoFrames: hasVoiceEngineMethod('setVideoFrameCallback'), + hardwareEncoderCapabilities: hasVoiceEngine && typeof getHardwareEncoderCapability === 'function', + }; +} + +function requireScreenShareCaptureId(options, operation) { + if (!options || typeof options.captureId !== 'string' || options.captureId.trim().length === 0) { + throw new Error(`${operation} requires a non-empty captureId`); + } +} + +function getEngineBridgeVersion() { + if (!binding) return null; + if (typeof binding.getEngineBridgeVersion !== 'function') return null; + return binding.getEngineBridgeVersion(); +} + +function assertEngineBridgeVersion(version) { + if (!binding) { + throw loadError || new Error(`${MODULE_NAME} binding unavailable`); + } + if (typeof binding.assertEngineBridgeVersion !== 'function') { + throw new Error(`${MODULE_NAME} native binding does not export assertEngineBridgeVersion`); + } + binding.assertEngineBridgeVersion(version); +} + +function prewarmVoiceEngine() { + if (!binding) { + if (loadError) throw loadError; + return; + } + if (typeof binding.prewarmVoiceEngine === 'function') { + return binding.prewarmVoiceEngine(); + } +} + +function probeAudioDeviceModule() { + if (!binding) { + if (loadError) throw loadError; + return Promise.resolve(false); + } + if (typeof binding.probeAudioDeviceModule === 'function') { + return Promise.resolve(binding.probeAudioDeviceModule()); + } + return Promise.resolve(true); +} + +function normalizeCameraOptions(opts = {}) { + return { + deviceId: opts.deviceId, + width: opts.width, + height: opts.height, + frameRate: opts.frameRate, + mirror: opts.mirror, + backgroundMode: opts.backgroundMode, + backgroundCustomMediaPath: opts.backgroundCustomMediaPath, + backgroundCustomMediaKind: opts.backgroundCustomMediaKind, + backgroundBlurStrength: opts.backgroundBlurStrength, + codec: opts.codec, + maxBitrateBps: opts.maxBitrateBps, + maxFramerate: opts.maxFramerate, + }; +} + +class VoiceEngine { + constructor() { + if (!binding) { + throw loadError || new Error(`${MODULE_NAME} binding unavailable`); + } + this.native = new binding.VoiceEngine(); + } + + setEventCallback(callback) { + return this.native.setEventCallback(callback); + } + + setVideoFrameCallback(callback) { + return this.native.setVideoFrameCallback(callback); + } + + clearVideoFrameCallback() { + if (typeof this.native.clearVideoFrameCallback === 'function') { + return this.native.clearVideoFrameCallback(); + } + if (typeof this.native.setVideoFrameCallback === 'function') { + return this.native.setVideoFrameCallback(() => {}); + } + return undefined; + } + + setCountInboundAudio(enabled) { + return this.native.setCountInboundAudio(enabled); + } + + connect(url, token, e2eeKey, options) { + return this.native.connect(url, token, e2eeKey, options); + } + + disconnect() { + return this.native.disconnect(); + } + + isConnected() { + return this.native.isConnected(); + } + + publishScreenShare(width, height, codec = '', maxBitrateBps, maxFramerate, simulcast, options) { + requireScreenShareCaptureId(options, 'Native screen-share publish'); + return this.native.publishScreenShare(width, height, codec, maxBitrateBps, maxFramerate, simulcast, options); + } + + updateScreenShareEncoding(width, height, maxBitrateBps, maxFramerate, options) { + requireScreenShareCaptureId(options, 'Native screen-share encoding update'); + if (typeof this.native.updateScreenShareEncoding === 'function') { + return this.native.updateScreenShareEncoding(width, height, maxBitrateBps, maxFramerate, options); + } + return Promise.reject(new Error('Native screen-share encoding update is unavailable')); + } + + createScreenFrameSinkHandle(captureId) { + if (typeof this.native.createScreenFrameSinkHandle !== 'function') return null; + return this.native.createScreenFrameSinkHandle(captureId); + } + + createScreenAudioSinkHandle() { + if (typeof this.native.createScreenAudioSinkHandle !== 'function') return null; + return this.native.createScreenAudioSinkHandle(); + } + + unpublishScreenShare() { + return this.native.unpublishScreenShare(); + } + + isPublishingScreen() { + return this.native.isPublishingScreen(); + } + + publishScreenShareAudio(sampleRate, numChannels) { + return this.native.publishScreenShareAudio(sampleRate, numChannels); + } + + pushScreenSharePcm(buffer, sampleRate, numChannels) { + return this.native.pushScreenSharePcm(buffer, sampleRate, numChannels); + } + + pushScreenShareFloat(buffer, sampleRate, numChannels) { + return this.native.pushScreenShareFloat(buffer, sampleRate, numChannels); + } + + unpublishScreenShareAudio() { + return this.native.unpublishScreenShareAudio(); + } + + isPublishingScreenAudio() { + return this.native.isPublishingScreenAudio(); + } + + publishMicrophone(sampleRate, numChannels) { + return this.native.publishMicrophone(sampleRate, numChannels); + } + + publishDeviceMicrophone(opts = {}) { + if (typeof this.native.publishDeviceMicrophone !== 'function') { + throw new Error(`${MODULE_NAME} native binding does not export publishDeviceMicrophone`); + } + return this.native.publishDeviceMicrophone({ + deviceId: opts.deviceId, + echoCancellation: opts.echoCancellation, + noiseSuppression: opts.noiseSuppression, + autoGainControl: opts.autoGainControl, + ...(opts.deepFilter !== undefined ? {deepFilter: opts.deepFilter} : {}), + ...(opts.deepFilterNoiseReductionLevel !== undefined + ? {deepFilterNoiseReductionLevel: opts.deepFilterNoiseReductionLevel} + : {}), + maxBitrateBps: opts.maxBitrateBps, + }); + } + + pushPcm(buffer, sampleRate, numChannels) { + return this.native.pushPcm(buffer, sampleRate, numChannels); + } + + setMicEnabled(enabled) { + return this.native.setMicEnabled(enabled); + } + + setSpeakingDetection(localThresholdRms, remoteThresholdRms) { + if (typeof this.native.setSpeakingDetection !== 'function') { + throw new Error(`${MODULE_NAME} native binding does not export setSpeakingDetection`); + } + return this.native.setSpeakingDetection(localThresholdRms, remoteThresholdRms); + } + + publishCamera(opts = {}) { + return this.native.publishCamera(normalizeCameraOptions(opts)); + } + + updateCameraCapture(opts = {}) { + if (typeof this.native.updateCameraCapture !== 'function') { + throw new Error(`${MODULE_NAME} native binding does not export updateCameraCapture`); + } + return this.native.updateCameraCapture(normalizeCameraOptions(opts)); + } + + publishProcessedCamera(opts) { + if (typeof this.native.publishProcessedCamera !== 'function') { + throw new Error(`${MODULE_NAME} native binding does not export publishProcessedCamera`); + } + return this.native.publishProcessedCamera({ + width: opts.width, + height: opts.height, + frameRate: opts.frameRate, + }); + } + + publishNativeCameraSink(opts = {}) { + if (typeof this.native.publishNativeCameraSink !== 'function') { + throw new Error(`${MODULE_NAME} native binding does not export publishNativeCameraSink`); + } + return this.native.publishNativeCameraSink(normalizeCameraOptions(opts)); + } + + createCameraFrameSinkHandle() { + if (typeof this.native.createCameraFrameSinkHandle !== 'function') return null; + return this.native.createCameraFrameSinkHandle(); + } + + pushProcessedCameraFrame(frame) { + if (typeof this.native.pushProcessedCameraFrame !== 'function') { + throw new Error(`${MODULE_NAME} native binding does not export pushProcessedCameraFrame`); + } + return this.native.pushProcessedCameraFrame({ + format: frame.format, + width: frame.width, + height: frame.height, + timestampUs: frame.timestampUs, + data: frame.data, + }); + } + + startCameraPreview(opts = {}) { + if (typeof this.native.startCameraPreview !== 'function') { + throw new Error(`${MODULE_NAME} native binding does not export startCameraPreview`); + } + return this.native.startCameraPreview(normalizeCameraOptions(opts)); + } + + stopCameraPreview() { + if (typeof this.native.stopCameraPreview !== 'function') { + throw new Error(`${MODULE_NAME} native binding does not export stopCameraPreview`); + } + return this.native.stopCameraPreview(); + } + + pushCameraBackgroundFrame(frame) { + if (typeof this.native.pushCameraBackgroundFrame !== 'function') { + throw new Error(`${MODULE_NAME} native binding does not export pushCameraBackgroundFrame`); + } + return this.native.pushCameraBackgroundFrame({ + format: frame.format, + width: frame.width, + height: frame.height, + timestampUs: frame.timestampUs, + data: frame.data, + }); + } + + clearCameraBackgroundFrame() { + if (typeof this.native.clearCameraBackgroundFrame !== 'function') { + throw new Error(`${MODULE_NAME} native binding does not export clearCameraBackgroundFrame`); + } + return this.native.clearCameraBackgroundFrame(); + } + + publishDeviceScreenShare(opts = {}) { + if (typeof this.native.publishDeviceScreenShare !== 'function') { + throw new Error(`${MODULE_NAME} native binding does not export publishDeviceScreenShare`); + } + return this.native.publishDeviceScreenShare(normalizeCameraOptions(opts)); + } + + listCameraDevices() { + return this.native.listCameraDevices(); + } + + unpublishCamera() { + return this.native.unpublishCamera(); + } + + isPublishingCamera() { + return this.native.isPublishingCamera(); + } + + async listAudioInputDevices() { + const json = await this.native.listAudioInputDevices(); + return JSON.parse(json); + } + + droppedEngineEvents() { + if (typeof this.native.droppedEngineEvents !== 'function') return 0; + return this.native.droppedEngineEvents(); + } + + async listAudioOutputDevices() { + const json = await this.native.listAudioOutputDevices(); + return JSON.parse(json); + } + + setAudioOutputDevice(deviceId) { + return this.native.setAudioOutputDevice(deviceId || ''); + } + + ensurePlatformAudio() { + if (typeof this.native.ensurePlatformAudio !== 'function') return Promise.resolve(); + return this.native.ensurePlatformAudio(); + } + + setParticipantVolume(participantSid, volume) { + return this.native.setParticipantVolume(participantSid, volume); + } + + setRemoteTrackSubscription(opts = {}) { + if (typeof this.native.setRemoteTrackSubscription !== 'function') return Promise.resolve(); + return this.native.setRemoteTrackSubscription( + opts.participantIdentity || '', + opts.source || '', + opts.subscribed === true, + opts.enabled !== false, + opts.quality || undefined, + ); + } + + publishData(payload, opts = {}) { + if (typeof this.native.publishData !== 'function') { + return Promise.reject(new Error(`${MODULE_NAME} native binding does not export publishData`)); + } + const buffer = Buffer.isBuffer(payload) + ? payload + : payload instanceof ArrayBuffer + ? Buffer.from(payload) + : ArrayBuffer.isView(payload) + ? Buffer.from(payload.buffer, payload.byteOffset, payload.byteLength) + : null; + if (!buffer) { + return Promise.reject(new TypeError('publishData payload must be a Buffer, ArrayBuffer, or typed array')); + } + return this.native.publishData( + buffer, + opts.reliable !== false, + typeof opts.topic === 'string' ? opts.topic : undefined, + Array.isArray(opts.destinationIdentities) ? opts.destinationIdentities : undefined, + ); + } + + getConnectionStats() { + const json = this.native.getConnectionStats(); + try { + return JSON.parse(json); + } catch { + return {rttMs: null, outbound: [], inbound: []}; + } + } + + inboundAudioFrames() { + return this.native.inboundAudioFrames(); + } + + inboundVideoFrames() { + return this.native.inboundVideoFrames(); + } + + droppedVideoFrameCallbacks() { + if (typeof this.native.droppedVideoFrameCallbacks !== 'function') return 0; + return this.native.droppedVideoFrameCallbacks(); + } +} + +module.exports = { + isSupported, + getEngineBridgeVersion, + assertEngineBridgeVersion, + getHardwareEncoderCapability, + getHardwareEncoderCapabilities, + getCapabilities, + hasNativeCameraBackgrounds, + prewarmVoiceEngine, + probeAudioDeviceModule, + VoiceEngine, + get loadError() { + return loadError; + }, + __nativeFileNameForTests: nativeFileName, + __setBindingForTests(next) { + binding = next; + loadError = null; + }, +}; diff --git a/fluxer_desktop/native/webrtc-sender/index.test.mjs b/fluxer_desktop/native/webrtc-sender/index.test.mjs new file mode 100644 index 0000000..6275abf --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/index.test.mjs @@ -0,0 +1,848 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import assert from 'node:assert/strict'; +import {createRequire} from 'node:module'; +import {describe, test} from 'node:test'; + +const require = createRequire(import.meta.url); +const webrtcSender = require('./index.js'); + +describe('webrtc-sender loader wrapper', () => { + test('resolves the native addon filename for every desktop OS/arch target', () => { + const cases = [ + ['win32', 'x64', 'webrtc-sender.win32-x64-msvc.node'], + ['win32', 'arm64', 'webrtc-sender.win32-arm64-msvc.node'], + ['darwin', 'x64', 'webrtc-sender.darwin-x64.node'], + ['darwin', 'arm64', 'webrtc-sender.darwin-arm64.node'], + ['linux', 'x64', 'webrtc-sender.linux-x64-gnu.node'], + ['linux', 'arm64', 'webrtc-sender.linux-arm64-gnu.node'], + ]; + for (const [platform, arch, expected] of cases) { + assert.equal(webrtcSender.__nativeFileNameForTests(platform, arch), expected); + } + }); + + test('rejects unsupported platform/architecture pairs explicitly', () => { + assert.throws(() => webrtcSender.__nativeFileNameForTests('linux', 'ia32'), /not supported/); + assert.throws(() => webrtcSender.__nativeFileNameForTests('freebsd', 'x64'), /not supported/); + }); + + test('returns a native hardware encoder capability when the binding exports one', () => { + const expected = { + available: true, + backend: 'nvenc', + compiled: true, + runtime: true, + codecs: ['h264', 'h265'], + zeroCopy: true, + nativeInputs: ['dmabuf'], + }; + webrtcSender.__setBindingForTests({ + getHardwareEncoderCapability() { + return expected; + }, + }); + + assert.deepEqual(webrtcSender.getHardwareEncoderCapability(), expected); + assert.deepEqual(webrtcSender.getHardwareEncoderCapabilities(), expected); + }); + + test('delegates dropped video callback metrics through the VoiceEngine wrapper', () => { + webrtcSender.__setBindingForTests({ + VoiceEngine: class { + inboundAudioFrames() { + return 2; + } + + inboundVideoFrames() { + return 3; + } + + droppedVideoFrameCallbacks() { + return 5; + } + }, + }); + + const engine = new webrtcSender.VoiceEngine(); + assert.equal(engine.inboundAudioFrames(), 2); + assert.equal(engine.inboundVideoFrames(), 3); + assert.equal(engine.droppedVideoFrameCallbacks(), 5); + }); + + test('reports VoiceEngine feature capabilities from the wrapped native prototype', () => { + webrtcSender.__setBindingForTests({ + VoiceEngine: class { + publishDeviceMicrophone() {} + publishMicrophone() {} + publishCamera() {} + listCameraDevices() {} + publishScreenShare() {} + updateScreenShareEncoding() {} + unpublishScreenShare() {} + publishScreenShareAudio() {} + pushScreenSharePcm() {} + pushScreenShareFloat() {} + unpublishScreenShareAudio() {} + listAudioInputDevices() {} + listAudioOutputDevices() {} + setAudioOutputDevice() {} + setParticipantVolume() {} + setRemoteTrackSubscription() {} + publishData() {} + getConnectionStats() {} + setVideoFrameCallback() {} + }, + }); + + assert.deepEqual(webrtcSender.getCapabilities(), { + microphoneCapture: true, + syntheticMicrophonePcm: true, + cameraCapture: true, + nativeCameraBackgrounds: false, + screenShare: true, + screenShareEncodingUpdate: true, + screenShareAudio: true, + deviceLists: true, + outputDeviceSelection: true, + participantVolume: true, + remoteTrackSubscription: true, + dataChannel: true, + connectionStats: true, + nativeVideoFrames: true, + hardwareEncoderCapabilities: true, + }); + }); + + test('does not report screen-share audio capability without the float push method', () => { + webrtcSender.__setBindingForTests({ + VoiceEngine: class { + publishScreenShareAudio() {} + pushScreenSharePcm() {} + unpublishScreenShareAudio() {} + }, + }); + + assert.equal(webrtcSender.getCapabilities().screenShareAudio, false); + }); + + test('reports native camera background support only from the explicit native probe', () => { + webrtcSender.__setBindingForTests({ + hasNativeCameraBackgrounds() { + return true; + }, + VoiceEngine: class { + publishCamera() {} + listCameraDevices() {} + }, + }); + + assert.equal(webrtcSender.hasNativeCameraBackgrounds(), true); + assert.equal(webrtcSender.getCapabilities().nativeCameraBackgrounds, true); + + webrtcSender.__setBindingForTests({ + VoiceEngine: class { + publishCamera() {} + listCameraDevices() {} + }, + }); + + assert.equal(webrtcSender.hasNativeCameraBackgrounds(), false); + assert.equal(webrtcSender.getCapabilities().nativeCameraBackgrounds, false); + }); + + test('delegates video-frame callback clearing to the native binding when exported', () => { + let clearCalls = 0; + const setCalls = []; + webrtcSender.__setBindingForTests({ + VoiceEngine: class { + setVideoFrameCallback(callback) { + setCalls.push(callback); + } + + clearVideoFrameCallback() { + clearCalls += 1; + } + }, + }); + + const engine = new webrtcSender.VoiceEngine(); + engine.clearVideoFrameCallback(); + + assert.equal(clearCalls, 1); + assert.deepEqual(setCalls, []); + }); + + test('delegates screen-share float audio through the VoiceEngine wrapper', async () => { + const calls = []; + webrtcSender.__setBindingForTests({ + VoiceEngine: class { + pushScreenShareFloat(buffer, sampleRate, numChannels) { + calls.push([buffer, sampleRate, numChannels]); + return Promise.resolve(true); + } + }, + }); + + const engine = new webrtcSender.VoiceEngine(); + const buffer = Buffer.from(new Float32Array([0.25, -0.25]).buffer); + const accepted = await engine.pushScreenShareFloat(buffer, 48000, 2); + + assert.equal(accepted, true); + assert.deepEqual(calls, [[buffer, 48000, 2]]); + }); + + test('falls back to a no-op video-frame callback for older native bindings', () => { + const setCalls = []; + webrtcSender.__setBindingForTests({ + VoiceEngine: class { + setVideoFrameCallback(callback) { + setCalls.push(callback); + } + }, + }); + + const engine = new webrtcSender.VoiceEngine(); + engine.clearVideoFrameCallback(); + + assert.equal(setCalls.length, 1); + assert.equal(typeof setCalls[0], 'function'); + assert.equal(setCalls[0]('{}', Buffer.alloc(0)), undefined); + }); + + test('treats video-frame callback clearing as a no-op when the binding exports neither method', () => { + webrtcSender.__setBindingForTests({ + VoiceEngine: class {}, + }); + + const engine = new webrtcSender.VoiceEngine(); + + assert.equal(engine.clearVideoFrameCallback(), undefined); + }); + + test('delegates engine bridge version reads and assertions to the native binding', () => { + const assertedVersions = []; + webrtcSender.__setBindingForTests({ + getEngineBridgeVersion() { + return 9; + }, + assertEngineBridgeVersion(version) { + assertedVersions.push(version); + if (version !== 9) { + throw new Error(`voice engine bridge version mismatch: host sent ${version}, native addon expects 9`); + } + }, + }); + + assert.equal(webrtcSender.getEngineBridgeVersion(), 9); + webrtcSender.assertEngineBridgeVersion(9); + assert.throws(() => webrtcSender.assertEngineBridgeVersion(8), /bridge version mismatch/); + assert.deepEqual(assertedVersions, [9, 8]); + }); + + test('reports a null engine bridge version and throws on assertion when the binding lacks the exports', () => { + webrtcSender.__setBindingForTests({}); + + assert.equal(webrtcSender.getEngineBridgeVersion(), null); + assert.throws(() => webrtcSender.assertEngineBridgeVersion(9), /does not export assertEngineBridgeVersion/); + }); + + test('delegates native voice engine prewarm when the binding exports it', () => { + let prewarmCalls = 0; + webrtcSender.__setBindingForTests({ + prewarmVoiceEngine() { + prewarmCalls += 1; + }, + }); + + webrtcSender.prewarmVoiceEngine(); + + assert.equal(prewarmCalls, 1); + }); + + test('delegates device microphone publish through the VoiceEngine wrapper', async () => { + const calls = []; + webrtcSender.__setBindingForTests({ + VoiceEngine: class { + publishDeviceMicrophone(opts) { + calls.push(opts); + return Promise.resolve(); + } + }, + }); + + const engine = new webrtcSender.VoiceEngine(); + await engine.publishDeviceMicrophone({ + deviceId: 'mic-guid', + echoCancellation: false, + noiseSuppression: true, + autoGainControl: false, + }); + await engine.publishDeviceMicrophone({ + deviceId: 'mic-guid', + echoCancellation: false, + noiseSuppression: true, + autoGainControl: false, + maxBitrateBps: 96_000, + }); + + assert.deepEqual(calls, [ + { + deviceId: 'mic-guid', + echoCancellation: false, + noiseSuppression: true, + autoGainControl: false, + maxBitrateBps: undefined, + }, + { + deviceId: 'mic-guid', + echoCancellation: false, + noiseSuppression: true, + autoGainControl: false, + maxBitrateBps: 96_000, + }, + ]); + }); + + test('delegates native camera devices and publish options through the VoiceEngine wrapper', async () => { + const publishCalls = []; + const processedPublishCalls = []; + const processedFrameCalls = []; + const nativeSinkPublishCalls = []; + const nativeSinkHandle = {}; + const devices = [ + { + deviceId: 'native-camera-id', + label: 'Studio Display Camera', + description: 'Apple Studio Display Camera', + index: 0, + deviceIdAliases: ['native-camera-id', '0'], + }, + ]; + webrtcSender.__setBindingForTests({ + VoiceEngine: class { + listCameraDevices() { + return devices; + } + + publishCamera(opts) { + publishCalls.push(opts); + return Promise.resolve(); + } + + publishProcessedCamera(opts) { + processedPublishCalls.push(opts); + return Promise.resolve({trackSid: 'TR_processed_camera'}); + } + + publishNativeCameraSink(opts) { + nativeSinkPublishCalls.push(opts); + return Promise.resolve({trackSid: 'TR_native_camera'}); + } + + createCameraFrameSinkHandle() { + return nativeSinkHandle; + } + + pushProcessedCameraFrame(frame) { + processedFrameCalls.push(frame); + return Promise.resolve(true); + } + }, + }); + + const engine = new webrtcSender.VoiceEngine(); + assert.equal(engine.listCameraDevices(), devices); + await engine.publishCamera({ + deviceId: 'native-camera-id', + width: 1280, + height: 720, + frameRate: 30, + mirror: true, + codec: 'h265', + maxBitrateBps: 6_000_000, + maxFramerate: 30, + }); + const processedPublishResult = await engine.publishProcessedCamera({ + width: 1280, + height: 720, + frameRate: 30, + ignored: true, + }); + const nativeSinkPublishResult = await engine.publishNativeCameraSink({ + deviceId: 'native-camera-id', + width: 1280, + height: 720, + frameRate: 30, + backgroundMode: 'custom', + backgroundCustomMediaPath: '/tmp/bg.webp', + backgroundCustomMediaKind: 'animated', + codec: 'h264', + }); + const cameraFrameSinkHandle = engine.createCameraFrameSinkHandle(); + const frame = { + format: 'i420', + width: 4, + height: 2, + timestampUs: 12_345, + data: Buffer.alloc(12), + ignored: true, + }; + const processedFrameResult = await engine.pushProcessedCameraFrame(frame); + + assert.deepEqual(publishCalls, [ + { + deviceId: 'native-camera-id', + width: 1280, + height: 720, + frameRate: 30, + mirror: true, + backgroundMode: undefined, + backgroundCustomMediaPath: undefined, + backgroundCustomMediaKind: undefined, + backgroundBlurStrength: undefined, + codec: 'h265', + maxBitrateBps: 6_000_000, + maxFramerate: 30, + }, + ]); + assert.deepEqual(processedPublishResult, {trackSid: 'TR_processed_camera'}); + assert.deepEqual(processedPublishCalls, [ + { + width: 1280, + height: 720, + frameRate: 30, + }, + ]); + assert.deepEqual(nativeSinkPublishResult, {trackSid: 'TR_native_camera'}); + assert.deepEqual(nativeSinkPublishCalls, [ + { + deviceId: 'native-camera-id', + width: 1280, + height: 720, + frameRate: 30, + mirror: undefined, + backgroundMode: 'custom', + backgroundCustomMediaPath: '/tmp/bg.webp', + backgroundCustomMediaKind: 'animated', + backgroundBlurStrength: undefined, + codec: 'h264', + maxBitrateBps: undefined, + maxFramerate: undefined, + }, + ]); + assert.equal(cameraFrameSinkHandle, nativeSinkHandle); + assert.equal(processedFrameResult, true); + assert.deepEqual(processedFrameCalls, [ + { + format: 'i420', + width: 4, + height: 2, + timestampUs: 12_345, + data: frame.data, + }, + ]); + }); + + test('reports processed camera publishing as unavailable for older native bindings', async () => { + webrtcSender.__setBindingForTests({ + VoiceEngine: class {}, + }); + + const engine = new webrtcSender.VoiceEngine(); + assert.throws( + () => engine.publishProcessedCamera({width: 1280, height: 720, frameRate: 30}), + /native binding does not export publishProcessedCamera/, + ); + assert.throws( + () => engine.publishNativeCameraSink({width: 1280, height: 720, frameRate: 30}), + /native binding does not export publishNativeCameraSink/, + ); + assert.equal(engine.createCameraFrameSinkHandle(), null); + assert.throws( + () => + engine.pushProcessedCameraFrame({ + format: 'i420', + width: 4, + height: 2, + timestampUs: 12_345, + data: Buffer.alloc(12), + }), + /native binding does not export pushProcessedCameraFrame/, + ); + }); + + test('delegates device screen-share publish through the VoiceEngine wrapper', async () => { + const publishCalls = []; + webrtcSender.__setBindingForTests({ + VoiceEngine: class { + publishDeviceScreenShare(opts) { + publishCalls.push(opts); + return Promise.resolve(); + } + }, + }); + + const engine = new webrtcSender.VoiceEngine(); + await engine.publishDeviceScreenShare({ + deviceId: 'studio-display-camera', + width: 1920, + height: 1080, + frameRate: 60, + codec: 'h264', + maxBitrateBps: 8_000_000, + maxFramerate: 60, + }); + + assert.deepEqual(publishCalls, [ + { + deviceId: 'studio-display-camera', + width: 1920, + height: 1080, + frameRate: 60, + mirror: undefined, + backgroundMode: undefined, + backgroundCustomMediaPath: undefined, + backgroundCustomMediaKind: undefined, + backgroundBlurStrength: undefined, + codec: 'h264', + maxBitrateBps: 8_000_000, + maxFramerate: 60, + }, + ]); + }); + + test('delegates camera capture updates with effect strengths through the VoiceEngine wrapper', async () => { + const updateCalls = []; + webrtcSender.__setBindingForTests({ + VoiceEngine: class { + updateCameraCapture(opts) { + updateCalls.push(opts); + return Promise.resolve(); + } + }, + }); + + const engine = new webrtcSender.VoiceEngine(); + await engine.updateCameraCapture({ + deviceId: 'native-camera-id', + width: 1280, + height: 720, + frameRate: 30, + mirror: false, + backgroundMode: 'blur', + backgroundBlurStrength: 90, + ignored: true, + }); + + assert.deepEqual(updateCalls, [ + { + deviceId: 'native-camera-id', + width: 1280, + height: 720, + frameRate: 30, + mirror: false, + backgroundMode: 'blur', + backgroundCustomMediaPath: undefined, + backgroundCustomMediaKind: undefined, + backgroundBlurStrength: 90, + codec: undefined, + maxBitrateBps: undefined, + maxFramerate: undefined, + }, + ]); + }); + + test('reports camera capture updates as unavailable for older native bindings', () => { + webrtcSender.__setBindingForTests({ + VoiceEngine: class {}, + }); + + const engine = new webrtcSender.VoiceEngine(); + assert.throws( + () => engine.updateCameraCapture({deviceId: 'native-camera-id'}), + /native binding does not export updateCameraCapture/, + ); + }); + + test('reports device screen-share publish as unavailable for older native bindings', async () => { + webrtcSender.__setBindingForTests({ + VoiceEngine: class {}, + }); + + const engine = new webrtcSender.VoiceEngine(); + assert.throws( + () => engine.publishDeviceScreenShare({deviceId: 'studio-display-camera'}), + /native binding does not export publishDeviceScreenShare/, + ); + }); + + test('delegates screen-share simulcast selection through the VoiceEngine wrapper', async () => { + const calls = []; + webrtcSender.__setBindingForTests({ + VoiceEngine: class { + publishScreenShare(width, height, codec, maxBitrateBps, maxFramerate, simulcast, options) { + calls.push({width, height, codec, maxBitrateBps, maxFramerate, simulcast, options}); + return Promise.resolve(); + } + }, + }); + + const engine = new webrtcSender.VoiceEngine(); + await engine.publishScreenShare(3840, 2160, 'h265', 50_000_000, 60, false, { + adaptiveSend: true, + minVideoFps: 15, + minResolutionScale: 0.5, + maxAudioBufferMs: 750, + captureId: 'screen-harness-primary', + }); + assert.deepEqual(calls, [ + { + width: 3840, + height: 2160, + codec: 'h265', + maxBitrateBps: 50_000_000, + maxFramerate: 60, + simulcast: false, + options: { + adaptiveSend: true, + minVideoFps: 15, + minResolutionScale: 0.5, + maxAudioBufferMs: 750, + captureId: 'screen-harness-primary', + }, + }, + ]); + }); + + test('rejects screen-share publish and encoding update without capture IDs', async () => { + const calls = []; + webrtcSender.__setBindingForTests({ + VoiceEngine: class { + publishScreenShare(...args) { + calls.push(['publish', args]); + return Promise.resolve(); + } + + updateScreenShareEncoding(...args) { + calls.push(['update', args]); + return Promise.resolve(); + } + }, + }); + + const engine = new webrtcSender.VoiceEngine(); + assert.throws( + () => engine.publishScreenShare(1280, 720, 'h264', undefined, 30, false, undefined), + /screen-share publish requires a non-empty captureId/, + ); + assert.throws( + () => engine.publishScreenShare(1280, 720, 'h264', undefined, 30, false, {captureId: ''}), + /screen-share publish requires a non-empty captureId/, + ); + assert.throws( + () => engine.updateScreenShareEncoding(1280, 720, undefined, 30, undefined), + /screen-share encoding update requires a non-empty captureId/, + ); + assert.throws( + () => engine.updateScreenShareEncoding(1280, 720, undefined, 30, {captureId: ''}), + /screen-share encoding update requires a non-empty captureId/, + ); + assert.deepEqual(calls, []); + }); + + test('delegates connect options through the VoiceEngine wrapper', async () => { + const calls = []; + webrtcSender.__setBindingForTests({ + VoiceEngine: class { + connect(url, token, e2eeKey, options) { + calls.push({url, token, e2eeKey, options}); + return Promise.resolve(); + } + }, + }); + + const engine = new webrtcSender.VoiceEngine(); + const key = Buffer.from('secret'); + await engine.connect('ws://localhost:7880', 'token', key, { + autoSubscribe: false, + adaptiveStream: true, + dynacast: true, + }); + assert.deepEqual(calls, [ + { + url: 'ws://localhost:7880', + token: 'token', + e2eeKey: key, + options: { + autoSubscribe: false, + adaptiveStream: true, + dynacast: true, + }, + }, + ]); + }); + + test('delegates native screen frame sink handle creation through the VoiceEngine wrapper', () => { + const calls = []; + const handle = {native: true}; + webrtcSender.__setBindingForTests({ + VoiceEngine: class { + createScreenFrameSinkHandle(captureId) { + calls.push(captureId); + return handle; + } + }, + }); + + const engine = new webrtcSender.VoiceEngine(); + assert.equal(engine.createScreenFrameSinkHandle('capture-1'), handle); + assert.deepEqual(calls, ['capture-1']); + }); + + test('treats native screen frame sink handles as unavailable for older voice bindings', () => { + webrtcSender.__setBindingForTests({ + VoiceEngine: class {}, + }); + + const engine = new webrtcSender.VoiceEngine(); + assert.equal(engine.createScreenFrameSinkHandle('capture-1'), null); + }); + + test('defaults dropped video callback metrics to zero for older native bindings', () => { + webrtcSender.__setBindingForTests({ + VoiceEngine: class { + inboundAudioFrames() { + return 2; + } + + inboundVideoFrames() { + return 3; + } + }, + }); + + const engine = new webrtcSender.VoiceEngine(); + assert.equal(engine.droppedVideoFrameCallbacks(), 0); + }); + + test('delegates remote track subscription updates through the VoiceEngine wrapper', async () => { + const calls = []; + webrtcSender.__setBindingForTests({ + VoiceEngine: class { + setRemoteTrackSubscription(participantIdentity, source, subscribed, enabled, quality) { + calls.push({participantIdentity, source, subscribed, enabled, quality}); + return Promise.resolve(); + } + }, + }); + + const engine = new webrtcSender.VoiceEngine(); + await engine.setRemoteTrackSubscription({ + participantIdentity: 'user_1_conn', + source: 'screen_share', + subscribed: true, + enabled: false, + quality: 'high', + }); + + assert.deepEqual(calls, [ + { + participantIdentity: 'user_1_conn', + source: 'screen_share', + subscribed: true, + enabled: false, + quality: 'high', + }, + ]); + }); + + test('ignores remote track subscription updates for older native bindings', async () => { + webrtcSender.__setBindingForTests({ + VoiceEngine: class {}, + }); + + const engine = new webrtcSender.VoiceEngine(); + await engine.setRemoteTrackSubscription({ + participantIdentity: 'user_1_conn', + source: 'camera', + subscribed: true, + }); + }); + + test('delegates data packets through the VoiceEngine wrapper', async () => { + const calls = []; + webrtcSender.__setBindingForTests({ + VoiceEngine: class { + publishData(payload, reliable, topic, destinationIdentities) { + calls.push({payload, reliable, topic, destinationIdentities}); + return Promise.resolve(); + } + }, + }); + + const engine = new webrtcSender.VoiceEngine(); + await engine.publishData(new Uint8Array([1, 2, 3]), { + reliable: true, + topic: 'screen-share-codec', + destinationIdentities: ['user_1_conn'], + }); + + assert.equal(Buffer.isBuffer(calls[0].payload), true); + assert.deepEqual([...calls[0].payload], [1, 2, 3]); + assert.deepEqual(calls, [ + { + payload: calls[0].payload, + reliable: true, + topic: 'screen-share-codec', + destinationIdentities: ['user_1_conn'], + }, + ]); + }); + + test('rejects invalid data packet payloads in the wrapper', async () => { + webrtcSender.__setBindingForTests({ + VoiceEngine: class { + publishData() { + return Promise.resolve(); + } + }, + }); + + const engine = new webrtcSender.VoiceEngine(); + await assert.rejects(() => engine.publishData('not-bytes'), /payload must be/); + }); + + test('returns an unavailable hardware encoder capability without a binding', () => { + webrtcSender.__setBindingForTests(null); + + assert.deepEqual(webrtcSender.getHardwareEncoderCapability(), { + available: false, + backend: 'none', + compiled: false, + runtime: false, + codecs: [], + zeroCopy: false, + nativeInputs: [], + reason: 'native_binding_unavailable', + detail: '@fluxer/webrtc-sender binding unavailable', + }); + }); + + test('exposes every VoiceEngine method declared in index.d.ts on the wrapper', async () => { + const {readFileSync} = await import('node:fs'); + const dts = readFileSync(new URL('./index.d.ts', import.meta.url), 'utf8'); + const classStart = dts.indexOf('export declare class VoiceEngine {'); + assert.ok(classStart >= 0); + const classEnd = dts.indexOf('\n}', classStart); + assert.ok(classEnd > classStart); + const classBody = dts.slice(classStart, classEnd); + const declaredMethods = [...classBody.matchAll(/^\t([A-Za-z0-9_]+)\(/gm)] + .map((match) => match[1]) + .filter((name) => name !== 'constructor'); + assert.ok(declaredMethods.length >= 30); + const wrapperMethods = new Set(Object.getOwnPropertyNames(webrtcSender.VoiceEngine.prototype)); + const missing = declaredMethods.filter((name) => !wrapperMethods.has(name)); + assert.deepEqual(missing, []); + }); +}); diff --git a/fluxer_desktop/native/webrtc-sender/models/README.md b/fluxer_desktop/native/webrtc-sender/models/README.md new file mode 100644 index 0000000..e9ddc2c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/models/README.md @@ -0,0 +1,10 @@ +# Bundled segmentation models + +## selfie_segmenter_landscape.onnx + +- **Source**: Google MediaPipe Selfie Segmenter (landscape), `selfie_segmenter_landscape.tflite`, downloaded from `https://storage.googleapis.com/mediapipe-models/image_segmenter/selfie_segmenter_landscape/float16/latest/selfie_segmenter_landscape.tflite` (sha256 `490e9ea734313e0de10fa0cd9e3c6133e36ea4db2b7a49bde9ef019f72796b8e`). +- **License**: Apache License 2.0, per the official model card ("Model Card MediaPipe Selfie Segmentation", Google, 2021; `https://storage.googleapis.com/mediapipe-assets/Model%20Card%20MediaPipe%20Selfie%20Segmentation.pdf`). This is the Apache-licensed Selfie model, not the ToS-restricted Google Meet model (`segm_full_v679.tflite`), which must never be shipped. +- **Conversion**: `tf2onnx` (`python -m tf2onnx.convert --tflite selfie_segmenter_landscape.tflite --output model.onnx --opset 13`), followed by graph surgery that replaces the single `TFL_Convolution2DTransposeBias` custom op with a standard `ConvTranspose` (weights transposed from `[out, kh, kw, in]` to `[in, out, kh, kw]`, strides 2x2, no padding) wrapped in NHWC/NCHW transposes, and bumps the default opset domain to 14 for `HardSwish`. +- **Verification**: output of the converted model matches the original tflite interpreter to a max abs diff of 8.7e-8 on random input. On a portrait test image the output is person confidence (1.0 on the subject, 0.0 in background corners), despite the output tensor name `segment_back`. +- **Signature**: input `input_1` `[1, 144, 256, 3]` f32 RGB scaled to 0..1, output `segment_back` `[1, 144, 256, 1]` f32 person confidence 0..1. +- **sha256**: `e8224061bba6031282bfd00cf23a2563fa11a1e28baae0a9052cef6b4e7f3321` diff --git a/fluxer_desktop/native/webrtc-sender/models/selfie_segmenter_landscape.onnx b/fluxer_desktop/native/webrtc-sender/models/selfie_segmenter_landscape.onnx new file mode 100644 index 0000000..c04c279 Binary files /dev/null and b/fluxer_desktop/native/webrtc-sender/models/selfie_segmenter_landscape.onnx differ diff --git a/fluxer_desktop/native/webrtc-sender/package.json b/fluxer_desktop/native/webrtc-sender/package.json new file mode 100644 index 0000000..d13fddb --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/package.json @@ -0,0 +1,33 @@ +{ + "name": "@fluxer/webrtc-sender", + "version": "0.0.0", + "description": "Native main-process WebRTC video sender (LiveKit/libwebrtc) for publishing game/screen capture without a renderer hop", + "private": true, + "license": "AGPL-3.0-or-later", + "os": [ + "darwin", + "linux", + "win32" + ], + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "webrtc-sender.darwin-x64.node", + "webrtc-sender.darwin-arm64.node", + "webrtc-sender.linux-x64-gnu.node", + "webrtc-sender.linux-arm64-gnu.node", + "webrtc-sender.win32-x64-msvc.node", + "webrtc-sender.win32-arm64-msvc.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- test-webrtc-sender-rust && node --test index.test.mjs scripts/livekit-harness.test.mjs", + "test:livekit": "node scripts/livekit-harness.mjs" + } +} diff --git a/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs b/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs new file mode 100644 index 0000000..19d67c2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.mjs @@ -0,0 +1,2058 @@ +#!/usr/bin/env node +// SPDX-License-Identifier: AGPL-3.0-or-later + +import {createHmac, randomUUID} from 'node:crypto'; +import {writeFile} from 'node:fs/promises'; +import {createRequire} from 'node:module'; +import net from 'node:net'; +import {setTimeout as delay} from 'node:timers/promises'; +import {fileURLToPath} from 'node:url'; + +const require = createRequire(import.meta.url); +const {VoiceEngine, getHardwareEncoderCapabilities, isSupported, loadError} = require('../index.js'); + +const DEFAULT_URL = 'ws://localhost:7880'; +const DEFAULT_API_KEY = 'devkey'; +const DEFAULT_API_SECRET = 'secret'; +const DEFAULT_TIMEOUT_MS = 30_000; +const DEFAULT_CONNECT_TIMEOUT_MS = 30_000; +const DEFAULT_DISCONNECT_TIMEOUT_MS = 10_000; +const DEFAULT_PROBE_TIMEOUT_MS = 1_500; +const DEFAULT_WIDTH = 320; +const DEFAULT_HEIGHT = 180; +const DEFAULT_FPS = 15; +const DEFAULT_SAMPLE_RATE = 48_000; +const DEFAULT_CHANNELS = 1; +const DEFAULT_AUDIO_CHUNK_MS = 20; +const STRICT_DURATION_MS = 10 * 60 * 1000; +const STRICT_MIN_FPS_RATIO = 0.95; +const STRICT_MAX_FRAME_GAP_MS = 250; +const STRICT_MAX_AV_DRIFT_MS = 80; +const STRICT_MAX_AUDIO_FRAME_GAP_MS = 250; +const AUDIO_POLL_INTERVAL_MS = 100; +const MIN_INBOUND_AUDIO_FRAMES = 3; +const MIN_INBOUND_VIDEO_FRAMES = 3; +const VIDEO_PATTERNS = new Set(['gradient', 'fast']); +const VIDEO_QUALITIES = new Set(['low', 'medium', 'high']); +const VIDEO_INPUTS = new Set(['bgra', 'nv12']); +const LIVEKIT_SCREEN_SHARE_SOURCE = 'screen_share'; +const LIVEKIT_SCREEN_SHARE_AUDIO_SOURCE = 'screen_share_audio'; + +function parseBooleanFlag(name, value) { + const normalized = value.trim(); + if (/^(1|true|yes|on|required)$/i.test(normalized)) return true; + if (/^(0|false|no|off|disabled)$/i.test(normalized)) return false; + throw new Error(`${name} must be a boolean flag: 1/0, true/false, yes/no, or on/off`); +} + +function envFlag(...names) { + for (const name of names) { + const value = process.env[name]; + if (value !== undefined && value.trim() !== '') return parseBooleanFlag(name, value); + } + return false; +} + +function envOptionalFlag(name, fallback) { + const value = process.env[name]; + if (value === undefined || value.trim() === '') return fallback; + return parseBooleanFlag(name, value); +} + +function envString(names, fallback) { + for (const name of names) { + const value = process.env[name]; + if (value?.trim()) return value.trim(); + } + return fallback; +} + +function envOptionalString(names) { + for (const name of names) { + const value = process.env[name]; + if (value?.trim()) return value.trim(); + } + return null; +} + +function envOptionalStringConsistent(names) { + let selected = null; + let selectedName = null; + for (const name of names) { + const value = process.env[name]; + if (!value?.trim()) continue; + const trimmed = value.trim(); + if (selected !== null && trimmed !== selected) { + throw new Error(`${name} conflicts with ${selectedName}; use one value for ${names.join(', ')}`); + } + selected = trimmed; + selectedName = name; + } + return selected; +} + +function envInteger(name, fallback) { + const value = process.env[name]; + if (value === undefined || value === '') return fallback; + const parsed = Number(value); + if (!Number.isInteger(parsed) || parsed < 0) { + throw new Error(`${name} must be a non-negative integer`); + } + return parsed; +} + +function envPositiveInteger(name, fallback) { + const parsed = envInteger(name, fallback); + if (!Number.isInteger(parsed) || parsed <= 0) { + throw new Error(`${name} must be a positive integer`); + } + return parsed; +} + +function envNumber(name, fallback) { + const value = process.env[name]; + if (value === undefined || value === '') return fallback; + const parsed = Number(value); + if (!Number.isFinite(parsed) || parsed < 0) { + throw new Error(`${name} must be a non-negative number`); + } + return parsed; +} + +function envPositiveNumber(name, fallback) { + const value = process.env[name]; + if (value === undefined || value === '') return fallback; + const parsed = Number(value); + if (!Number.isFinite(parsed) || parsed <= 0) { + throw new Error(`${name} must be a positive number`); + } + return parsed; +} + +function envUnitNumber(name, fallback) { + const parsed = envPositiveNumber(name, fallback); + if (parsed > 1) { + throw new Error(`${name} must be greater than 0 and less than or equal to 1`); + } + return parsed; +} + +function envOptionalNonNegativeInteger(name) { + const value = process.env[name]; + if (value === undefined || value === '') return null; + const parsed = Number(value); + if (!Number.isInteger(parsed) || parsed < 0) { + throw new Error(`${name} must be a non-negative integer`); + } + return parsed; +} + +function envVideoPattern(name, fallback) { + const value = process.env[name]?.trim().toLowerCase() || fallback; + if (!VIDEO_PATTERNS.has(value)) { + throw new Error(`${name} must be one of: ${[...VIDEO_PATTERNS].join(', ')}`); + } + return value; +} + +function envVideoInput(name, fallback) { + const value = process.env[name]?.trim().toLowerCase() || fallback; + if (!VIDEO_INPUTS.has(value)) { + throw new Error(`${name} must be one of: ${[...VIDEO_INPUTS].join(', ')}`); + } + return value; +} + +function envOptionalVideoQuality(name) { + const value = process.env[name]?.trim().toLowerCase(); + if (!value) return null; + if (!VIDEO_QUALITIES.has(value)) { + throw new Error(`${name} must be one of: ${[...VIDEO_QUALITIES].join(', ')}`); + } + return value; +} + +function jwtSubject(token) { + const parts = token.split('.'); + if (parts.length < 2) { + throw new Error('LiveKit token must be a JWT when identity is not supplied'); + } + let payload; + try { + payload = JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8')); + } catch (error) { + throw new Error(`failed to decode LiveKit token payload: ${error.message}`); + } + if (typeof payload.sub !== 'string' || payload.sub.length === 0) { + throw new Error('LiveKit token payload does not contain a subject identity'); + } + return payload.sub; +} + +function resolveIdentity({configuredIdentity, token, fallback}) { + if (configuredIdentity) return configuredIdentity; + if (token) return jwtSubject(token); + return fallback; +} + +function parseCodecList(rawValue, fallback) { + const value = rawValue?.trim() ? rawValue : fallback; + const codecs = value + .split(',') + .map((entry) => entry.trim()) + .filter((entry) => entry.length > 0); + if (codecs.length === 0) { + throw new Error('at least one LiveKit screen codec must be configured'); + } + for (const codec of codecs) { + expectedCodecMime(codec); + } + return codecs; +} + +function parseConfig() { + const strict = envFlag('LIVEKIT_HARNESS_STRICT', 'FLUXER_NATIVE_MEDIA_STRICT'); + const liveKitUrl = envString(['LIVEKIT_URL', 'LIVEKIT_WS_URL'], DEFAULT_URL); + const generatedPublisherIdentity = `fluxer-native-publisher-${process.pid}`; + const generatedSecondaryPublisherIdentity = `fluxer-native-publisher-2-${process.pid}`; + const generatedSubscriberIdentity = `fluxer-native-subscriber-${process.pid}`; + const publisherToken = envOptionalString(['LIVEKIT_PUBLISHER_TOKEN']); + const secondaryPublisherToken = envOptionalString(['LIVEKIT_SECONDARY_PUBLISHER_TOKEN']); + const subscriberToken = envOptionalString(['LIVEKIT_SUBSCRIBER_TOKEN']); + const externalTokens = publisherToken !== null || secondaryPublisherToken !== null || subscriberToken !== null; + const width = envPositiveInteger('LIVEKIT_SCREEN_WIDTH', DEFAULT_WIDTH); + const height = envPositiveInteger('LIVEKIT_SCREEN_HEIGHT', DEFAULT_HEIGHT); + if (width % 2 !== 0 || height % 2 !== 0) { + throw new Error('LIVEKIT_SCREEN_WIDTH and LIVEKIT_SCREEN_HEIGHT must be even'); + } + const fps = envPositiveNumber('LIVEKIT_SCREEN_FPS', DEFAULT_FPS); + const minVideoFps = envPositiveNumber('LIVEKIT_MIN_VIDEO_FPS', 15); + if (minVideoFps > fps) { + throw new Error('LIVEKIT_MIN_VIDEO_FPS must be less than or equal to LIVEKIT_SCREEN_FPS'); + } + const screenCodecs = parseCodecList(envOptionalString(['LIVEKIT_SCREEN_CODECS', 'LIVEKIT_SCREEN_CODEC']), 'vp8'); + const configuredExpectedScreenCodecs = envOptionalString([ + 'LIVEKIT_EXPECT_SCREEN_CODECS', + 'LIVEKIT_EXPECT_SCREEN_CODEC', + ]); + const expectedScreenCodecs = parseCodecList(configuredExpectedScreenCodecs, screenCodecs.join(',')); + if (configuredExpectedScreenCodecs !== null && expectedScreenCodecs.length !== screenCodecs.length) { + throw new Error('LIVEKIT_EXPECT_SCREEN_CODECS length must match LIVEKIT_SCREEN_CODECS length'); + } + const secondaryPublisherRequested = envOptionalFlag('LIVEKIT_ENABLE_SECOND_PUBLISHER', strict); + const configuredSecondaryPublisherCodec = envOptionalStringConsistent([ + 'LIVEKIT_SECOND_PUBLISHER_CODEC', + 'LIVEKIT_SECOND_PUBLISHER_SCREEN_CODEC', + 'LIVEKIT_SECOND_PUBLISHER_SCREEN_CODECS', + 'LIVEKIT_SECONDARY_PUBLISHER_SCREEN_CODEC', + 'LIVEKIT_SECONDARY_PUBLISHER_SCREEN_CODECS', + ]); + const configuredExpectedSecondaryPublisherCodec = envOptionalStringConsistent([ + 'LIVEKIT_EXPECT_SECOND_PUBLISHER_SCREEN_CODEC', + 'LIVEKIT_EXPECT_SECOND_PUBLISHER_SCREEN_CODECS', + 'LIVEKIT_EXPECT_SECONDARY_PUBLISHER_SCREEN_CODEC', + 'LIVEKIT_EXPECT_SECONDARY_PUBLISHER_SCREEN_CODECS', + ]); + const secondaryPublisherCodec = + configuredSecondaryPublisherCodec ?? (screenCodecs.length > 1 ? screenCodecs[1] : screenCodecs[0]); + const expectedSecondaryPublisherCodec = configuredExpectedSecondaryPublisherCodec ?? secondaryPublisherCodec; + const screenSimulcast = envOptionalFlag('LIVEKIT_SCREEN_SIMULCAST', true); + const microphone = envOptionalFlag('LIVEKIT_ENABLE_MICROPHONE', true); + const screenAudio = envOptionalFlag('LIVEKIT_ENABLE_SCREEN_AUDIO', strict); + if (externalTokens) { + if (!publisherToken || !subscriberToken) { + throw new Error( + 'LIVEKIT_PUBLISHER_TOKEN and LIVEKIT_SUBSCRIBER_TOKEN are both required when using external tokens', + ); + } + if (secondaryPublisherRequested && !secondaryPublisherToken) { + throw new Error('LIVEKIT_SECONDARY_PUBLISHER_TOKEN is required when LIVEKIT_ENABLE_SECOND_PUBLISHER=1'); + } + } + return { + url: liveKitUrl, + serverApiUrl: envString(['LIVEKIT_API_URL', 'LIVEKIT_HTTP_URL'], liveKitApiUrl(liveKitUrl)), + apiKey: envString(['LIVEKIT_API_KEY'], DEFAULT_API_KEY), + apiSecret: envString(['LIVEKIT_API_SECRET', 'LIVEKIT_SECRET'], DEFAULT_API_SECRET), + room: envString(['LIVEKIT_ROOM'], `fluxer-webrtc-harness-${process.pid}-${randomUUID()}`), + externalTokens, + publisherToken, + secondaryPublisherToken, + subscriberToken, + publisherIdentity: resolveIdentity({ + configuredIdentity: envOptionalString(['LIVEKIT_PUBLISHER_IDENTITY']), + token: publisherToken, + fallback: generatedPublisherIdentity, + }), + secondaryPublisherIdentity: resolveIdentity({ + configuredIdentity: envOptionalString(['LIVEKIT_SECONDARY_PUBLISHER_IDENTITY']), + token: secondaryPublisherToken, + fallback: generatedSecondaryPublisherIdentity, + }), + subscriberIdentity: resolveIdentity({ + configuredIdentity: envOptionalString(['LIVEKIT_SUBSCRIBER_IDENTITY']), + token: subscriberToken, + fallback: generatedSubscriberIdentity, + }), + required: envFlag('FLUXER_WEBRTC_SENDER_LIVEKIT_REQUIRED', 'LIVEKIT_REQUIRED'), + strict, + timeoutMs: envPositiveInteger('LIVEKIT_HARNESS_TIMEOUT_MS', DEFAULT_TIMEOUT_MS), + connectTimeoutMs: envPositiveInteger('LIVEKIT_CONNECT_TIMEOUT_MS', DEFAULT_CONNECT_TIMEOUT_MS), + disconnectTimeoutMs: envPositiveInteger('LIVEKIT_DISCONNECT_TIMEOUT_MS', DEFAULT_DISCONNECT_TIMEOUT_MS), + probeTimeoutMs: envPositiveInteger('LIVEKIT_PROBE_TIMEOUT_MS', DEFAULT_PROBE_TIMEOUT_MS), + durationMs: envInteger('LIVEKIT_HARNESS_DURATION_MS', strict ? STRICT_DURATION_MS : 0), + width, + height, + expectedWidth: envPositiveInteger('LIVEKIT_EXPECT_SCREEN_WIDTH', width), + expectedHeight: envPositiveInteger('LIVEKIT_EXPECT_SCREEN_HEIGHT', height), + fps, + minFpsRatio: envNumber('LIVEKIT_MIN_RECEIVED_FPS_RATIO', strict ? STRICT_MIN_FPS_RATIO : 0), + maxFrameGapMs: envNumber('LIVEKIT_MAX_FRAME_GAP_MS', strict ? STRICT_MAX_FRAME_GAP_MS : 0), + maxAudioFrameGapMs: envNumber('LIVEKIT_MAX_AUDIO_FRAME_GAP_MS', strict ? STRICT_MAX_AUDIO_FRAME_GAP_MS : 0), + maxAvDriftMs: envNumber('LIVEKIT_MAX_AV_DRIFT_MS', strict ? STRICT_MAX_AV_DRIFT_MS : 0), + maxPacketLoss: envOptionalNonNegativeInteger('LIVEKIT_MAX_PACKET_LOSS'), + requireStableResolution: envOptionalFlag('LIVEKIT_REQUIRE_STABLE_RESOLUTION', strict), + videoPattern: envVideoPattern('LIVEKIT_VIDEO_PATTERN', 'gradient'), + videoInput: envVideoInput('LIVEKIT_VIDEO_INPUT', 'bgra'), + subscriberVideoQuality: envOptionalVideoQuality('LIVEKIT_SUBSCRIBER_VIDEO_QUALITY'), + screenCodecs, + expectedScreenCodecs, + codec: screenCodecs[0], + expectedScreenCodec: expectedCodecMime(expectedScreenCodecs[0] ?? screenCodecs[0]), + secondaryPublisher: secondaryPublisherRequested, + secondaryPublisherCodec, + secondaryPublisherCodecExplicit: configuredSecondaryPublisherCodec !== null, + expectedSecondaryPublisherCodec: expectedCodecMime(expectedSecondaryPublisherCodec), + expectedSecondaryPublisherCodecExplicit: configuredExpectedSecondaryPublisherCodec !== null, + screenSimulcast, + secondaryPublisherScreenSimulcast: envOptionalFlag('LIVEKIT_SECOND_PUBLISHER_SCREEN_SIMULCAST', screenSimulcast), + adaptiveSend: envOptionalFlag('LIVEKIT_ADAPTIVE_SEND', true), + minVideoFps, + minResolutionScale: envUnitNumber('LIVEKIT_MIN_RESOLUTION_SCALE', 0.5), + maxAudioBufferMs: envPositiveInteger('LIVEKIT_MAX_AUDIO_BUFFER_MS', 750), + secondaryPublisherMicrophone: envOptionalFlag('LIVEKIT_SECOND_PUBLISHER_ENABLE_MICROPHONE', microphone), + secondaryPublisherScreenAudio: envOptionalFlag('LIVEKIT_SECOND_PUBLISHER_ENABLE_SCREEN_AUDIO', screenAudio), + maxBitrateBps: envNumber('LIVEKIT_SCREEN_MAX_BITRATE_BPS', 400_000), + sampleRate: envPositiveInteger('LIVEKIT_AUDIO_SAMPLE_RATE', DEFAULT_SAMPLE_RATE), + channels: envPositiveInteger('LIVEKIT_AUDIO_CHANNELS', DEFAULT_CHANNELS), + audioChunkMs: envPositiveInteger('LIVEKIT_AUDIO_CHUNK_MS', DEFAULT_AUDIO_CHUNK_MS), + microphone, + screenAudio, + dataPacket: envOptionalFlag('LIVEKIT_ENABLE_DATA_PACKET', strict), + subscriptionCycle: envOptionalFlag('LIVEKIT_ENABLE_SUBSCRIPTION_CYCLE', strict), + validateServerPublishing: envOptionalFlag('LIVEKIT_VALIDATE_SERVER_PUBLISHING', strict), + camera: envOptionalFlag('LIVEKIT_ENABLE_CAMERA', false), + cameraDeviceId: envOptionalString(['LIVEKIT_CAMERA_DEVICE_ID']), + expectedCameraCodec: expectedCodecMime(envString(['LIVEKIT_EXPECT_CAMERA_CODEC'], '')), + e2eeKey: envOptionalString(['LIVEKIT_E2EE_KEY']), + expectedHardwareEncoder: envOptionalString(['LIVEKIT_EXPECT_HARDWARE_ENCODER']), + reportPath: envOptionalString(['LIVEKIT_HARNESS_REPORT_PATH', 'FLUXER_NATIVE_MEDIA_REPORT']), + verbose: envFlag('FLUXER_WEBRTC_SENDER_LIVEKIT_VERBOSE', 'LIVEKIT_VERBOSE'), + }; +} + +function expectedCodecMime(codec) { + switch (codec.trim().toLowerCase()) { + case '': + return null; + case 'vp8': + return 'video/VP8'; + case 'vp9': + return 'video/VP9'; + case 'h264': + return 'video/H264'; + case 'av1': + return 'video/AV1'; + case 'h265': + case 'hevc': + return 'video/H265'; + default: + throw new Error(`unsupported LIVEKIT_EXPECT_SCREEN_CODEC: ${codec}`); + } +} + +function liveKitTcpTarget(rawUrl) { + let url; + try { + url = new URL(rawUrl); + } catch (error) { + throw new Error(`LIVEKIT_URL is not a valid URL: ${error.message}`); + } + if (url.protocol !== 'ws:' && url.protocol !== 'wss:') { + throw new Error('LIVEKIT_URL must use ws:// or wss://'); + } + return { + host: url.hostname.replace(/^\[(.*)\]$/, '$1'), + port: Number(url.port || (url.protocol === 'wss:' ? 443 : 80)), + }; +} + +function liveKitApiUrl(rawUrl) { + let url; + try { + url = new URL(rawUrl); + } catch (error) { + throw new Error(`LIVEKIT_URL is not a valid URL: ${error.message}`); + } + if (url.protocol === 'ws:') return `http://${url.host}`; + if (url.protocol === 'wss:') return `https://${url.host}`; + throw new Error('LIVEKIT_URL must use ws:// or wss://'); +} + +function probeTcp({host, port}, timeoutMs) { + return new Promise((resolve) => { + const socket = net.createConnection({host, port}); + let done = false; + const finish = (result) => { + if (done) return; + done = true; + socket.destroy(); + resolve(result); + }; + socket.setTimeout(timeoutMs); + socket.once('connect', () => finish({ok: true})); + socket.once('timeout', () => finish({ok: false, reason: `timed out after ${timeoutMs}ms`})); + socket.once('error', (error) => finish({ok: false, reason: error.message})); + }); +} + +function base64urlJson(value) { + return Buffer.from(JSON.stringify(value)).toString('base64url'); +} + +function createLiveKitToken({apiKey, apiSecret, room, identity, name}) { + const now = Math.floor(Date.now() / 1000); + const header = {alg: 'HS256', typ: 'JWT'}; + const payload = { + iss: apiKey, + sub: identity, + name, + nbf: now - 5, + exp: now + 600, + jti: randomUUID(), + video: { + roomJoin: true, + room, + canPublish: true, + canSubscribe: true, + canPublishData: true, + canPublishSources: ['camera', 'microphone', 'screen_share', 'screen_share_audio'], + }, + }; + const body = `${base64urlJson(header)}.${base64urlJson(payload)}`; + const signature = createHmac('sha256', apiSecret).update(body).digest('base64url'); + return `${body}.${signature}`; +} + +function createLiveKitAdminToken({apiKey, apiSecret, room}) { + const now = Math.floor(Date.now() / 1000); + const header = {alg: 'HS256', typ: 'JWT'}; + const payload = { + iss: apiKey, + sub: `fluxer-native-harness-admin-${process.pid}`, + nbf: now - 5, + exp: now + 600, + jti: randomUUID(), + video: { + room, + roomAdmin: true, + }, + }; + const body = `${base64urlJson(header)}.${base64urlJson(payload)}`; + const signature = createHmac('sha256', apiSecret).update(body).digest('base64url'); + return `${body}.${signature}`; +} + +function parsePayload(jsonPayload) { + try { + return JSON.parse(jsonPayload); + } catch { + return {}; + } +} + +function unpackNapiPair(args) { + if (args.length === 1 && Array.isArray(args[0])) { + return [args[0][0], args[0][1]]; + } + return [args[0], args[1]]; +} + +function createEngineState(name, verbose) { + return { + name, + events: [], + stats: null, + statsSamples: [], + trackIndex: new Map(), + videoCallbacks: 0, + videoBytes: 0, + videoFrameTimes: [], + videoFrameRecords: [], + maxVideoFrameGapMs: 0, + lastVideoMeta: null, + videoResolutionCounts: new Map(), + lastVideoFrameAtMs: null, + audioFrameCounts: [], + audioFrameTimes: [], + lastAudioFrameAtMs: null, + maxAudioFrameGapMs: 0, + recordEvent(eventType, jsonPayload) { + const payload = parsePayload(jsonPayload); + this.events.push({eventType, payload, atMs: Date.now()}); + if (typeof payload.trackSid === 'string' && payload.trackSid.length > 0) { + if (eventType === 'trackSubscribed') { + this.trackIndex.set(payload.trackSid, { + identity: payload.identity, + kind: normalizeTrackKind(payload.kind), + source: normalizeTrackSource(payload.source), + trackName: payload.trackName, + }); + } + if (eventType === 'trackUnsubscribed') { + this.trackIndex.delete(payload.trackSid); + } + } + if (eventType === 'stats') { + this.stats = payload; + this.statsSamples.push({payload, atMs: Date.now()}); + } + if (verbose) { + const suffix = eventType === 'stats' ? '' : ` ${JSON.stringify(payload)}`; + console.log(`[livekit-harness] ${name} event: ${eventType}${suffix}`); + } + }, + recordVideoFrame(metaJson, data) { + const now = Date.now(); + if (this.lastVideoFrameAtMs !== null) { + this.maxVideoFrameGapMs = Math.max(this.maxVideoFrameGapMs, now - this.lastVideoFrameAtMs); + } + this.lastVideoFrameAtMs = now; + this.videoFrameTimes.push(now); + this.videoCallbacks += 1; + this.videoBytes += data?.byteLength ?? data?.length ?? 0; + const parsedMeta = parsePayload(metaJson); + const indexedTrack = + typeof parsedMeta.trackSid === 'string' ? this.trackIndex.get(parsedMeta.trackSid) : undefined; + this.lastVideoMeta = { + ...parsedMeta, + identity: parsedMeta.identity ?? indexedTrack?.identity, + kind: parsedMeta.kind !== undefined ? normalizeTrackKind(parsedMeta.kind) : indexedTrack?.kind, + source: parsedMeta.source !== undefined ? normalizeTrackSource(parsedMeta.source) : indexedTrack?.source, + trackName: parsedMeta.trackName ?? indexedTrack?.trackName, + }; + if (Number.isFinite(this.lastVideoMeta.width) && Number.isFinite(this.lastVideoMeta.height)) { + const key = `${this.lastVideoMeta.width}x${this.lastVideoMeta.height}`; + this.videoResolutionCounts.set(key, (this.videoResolutionCounts.get(key) ?? 0) + 1); + this.videoFrameRecords.push({ + atMs: now, + identity: this.lastVideoMeta.identity, + kind: this.lastVideoMeta.kind, + source: this.lastVideoMeta.source, + trackName: this.lastVideoMeta.trackName, + trackSid: this.lastVideoMeta.trackSid, + width: this.lastVideoMeta.width, + height: this.lastVideoMeta.height, + }); + } + if (verbose && this.videoCallbacks === 1) { + console.log(`[livekit-harness] ${name} first video frame: ${metaJson}`); + } + }, + recordAudioFrameCount(count) { + const now = Date.now(); + const last = this.audioFrameCounts[this.audioFrameCounts.length - 1]; + if (last && count > last.count) { + if (this.lastAudioFrameAtMs !== null) { + this.maxAudioFrameGapMs = Math.max(this.maxAudioFrameGapMs, now - this.lastAudioFrameAtMs); + } + this.lastAudioFrameAtMs = now; + this.audioFrameTimes.push(now); + } + this.audioFrameCounts.push({count, atMs: now}); + }, + }; +} + +function hasEvent(state, eventType, predicate = () => true) { + return state.events.some((event) => event.eventType === eventType && predicate(event.payload)); +} + +function statsHasOutbound(stats, kind, source) { + return ( + Array.isArray(stats?.outbound) && + stats.outbound.some((entry) => entry.kind === kind && normalizeTrackSource(entry.source) === source) + ); +} + +function statsHasInbound(stats, kind) { + return Array.isArray(stats?.inbound) && stats.inbound.some((entry) => entry.kind === kind); +} + +function statsInboundCount(stats, kind) { + return Array.isArray(stats?.inbound) ? stats.inbound.filter((entry) => entry.kind === kind).length : 0; +} + +function totalPacketsLost(stats) { + const outboundLoss = Array.isArray(stats?.outbound) + ? stats.outbound.reduce((sum, entry) => sum + Math.max(0, Number(entry.packetsLost) || 0), 0) + : 0; + const inboundLoss = Array.isArray(stats?.inbound) + ? stats.inbound.reduce((sum, entry) => sum + Math.max(0, Number(entry.packetsLost) || 0), 0) + : 0; + return outboundLoss + inboundLoss; +} + +function maxObservedPacketsLost(...states) { + let max = 0; + for (const state of states) { + for (const sample of state?.statsSamples ?? []) { + max = Math.max(max, totalPacketsLost(sample.payload)); + } + max = Math.max(max, totalPacketsLost(state?.stats)); + } + return max; +} + +function maxObservedPacketsLostDelta(startedAtMs, endedAtMs, ...states) { + let maxDelta = 0; + for (const state of states) { + const samples = [...(state?.statsSamples ?? [])]; + if (state?.stats) samples.push({payload: state.stats, atMs: endedAtMs}); + samples.sort((a, b) => a.atMs - b.atMs); + let baseline = 0; + let maxDuring = 0; + let hasWindowSample = false; + for (const sample of samples) { + const total = totalPacketsLost(sample.payload); + if (sample.atMs <= startedAtMs) { + baseline = total; + continue; + } + if (sample.atMs <= endedAtMs) { + hasWindowSample = true; + maxDuring = Math.max(maxDuring, total); + } + } + if (hasWindowSample) maxDelta = Math.max(maxDelta, Math.max(0, maxDuring - baseline)); + } + return maxDelta; +} + +function statsHasOutboundCodec(stats, kind, source, expectedCodec) { + return ( + Array.isArray(stats?.outbound) && + stats.outbound.some( + (entry) => entry.kind === kind && normalizeTrackSource(entry.source) === source && entry.codec === expectedCodec, + ) + ); +} + +function hasTrackEvent(state, eventType, {identity, kind, source}) { + if (!state) return false; + return hasEvent( + state, + eventType, + (payload) => + (identity === undefined || payload.identity === identity) && + (kind === undefined || payload.kind === kind) && + (source === undefined || normalizeTrackSource(payload.source) === source), + ); +} + +function statsHasInboundCodec(stats, kind, expectedCodec) { + return ( + Array.isArray(stats?.inbound) && stats.inbound.some((entry) => entry.kind === kind && entry.codec === expectedCodec) + ); +} + +function startVideoPump(_engine, config, _pumpErrors) { + const stats = { + input: config.videoInput, + pattern: config.videoPattern, + targetFps: config.fps, + startedAtMs: Date.now(), + stoppedAtMs: null, + framesPushed: 0, + framesRejected: 0, + errors: 0, + maxPushGapMs: 0, + lastPushAtMs: null, + }; + let stopped = false; + return { + stats() { + const endedAtMs = stats.stoppedAtMs ?? Date.now(); + const elapsedMs = Math.max(1, endedAtMs - stats.startedAtMs); + return { + ...stats, + elapsedMs, + producedFps: Math.round((stats.framesPushed / (elapsedMs / 1000)) * 100) / 100, + }; + }, + stop() { + if (stopped) return; + stopped = true; + stats.stoppedAtMs = Date.now(); + }, + }; +} + +function syntheticPcmFrame({sampleRate, channels, chunkMs, cursor}) { + const samplesPerChannel = Math.round((sampleRate * chunkMs) / 1000); + const buffer = Buffer.alloc(samplesPerChannel * channels * 2); + const frequencyHz = 440; + for (let i = 0; i < samplesPerChannel; i += 1) { + const sample = Math.round(Math.sin(((cursor + i) / sampleRate) * frequencyHz * Math.PI * 2) * 10_000); + for (let channel = 0; channel < channels; channel += 1) { + buffer.writeInt16LE(sample, (i * channels + channel) * 2); + } + } + return {buffer, nextCursor: cursor + samplesPerChannel}; +} + +function startAudioPump(label, pushFrame, config, pumpErrors) { + let stopped = false; + let cursor = 0; + const promise = (async () => { + while (!stopped) { + const frame = syntheticPcmFrame({ + sampleRate: config.sampleRate, + channels: config.channels, + chunkMs: config.audioChunkMs, + cursor, + }); + cursor = frame.nextCursor; + try { + const ok = await pushFrame(frame.buffer, config.sampleRate, config.channels); + if (!ok) pumpErrors.push(new Error(`${label} returned false`)); + } catch (error) { + pumpErrors.push(error); + } + await delay(config.audioChunkMs); + } + })(); + return { + stop() { + stopped = true; + }, + async done() { + await promise; + }, + }; +} + +function startAudioSampler(engine, state) { + const timer = setInterval(() => { + try { + state.recordAudioFrameCount(engine.inboundAudioFrames()); + } catch {} + }, AUDIO_POLL_INTERVAL_MS); + timer.unref?.(); + return { + stop() { + clearInterval(timer); + }, + }; +} + +function receivedFpsFromTimes(frames) { + if (frames.length < 2) return 0; + const elapsedSec = Math.max(0.001, (frames[frames.length - 1] - frames[0]) / 1000); + return (frames.length - 1) / elapsedSec; +} + +function maxGapMs(times, startedAtMs, endedAtMs) { + const windowTimes = times.filter((time) => time >= startedAtMs && time <= endedAtMs); + let maxGap = 0; + for (let index = 1; index < windowTimes.length; index += 1) { + maxGap = Math.max(maxGap, windowTimes[index] - windowTimes[index - 1]); + } + return maxGap; +} + +function countWindowSamples(times, startedAtMs, endedAtMs) { + return times.filter((time) => time >= startedAtMs && time <= endedAtMs).length; +} + +function primaryScreenVideoSelector(config) { + return { + identity: config.publisherIdentity, + kind: 'video', + source: LIVEKIT_SCREEN_SHARE_SOURCE, + }; +} + +function frameRecordMatches(record, selector) { + if (!record) return false; + if (selector.kind !== undefined && record.kind !== undefined && record.kind !== selector.kind) return false; + if (selector.source !== undefined && record.source !== undefined && record.source !== selector.source) return false; + if (selector.identity !== undefined && record.identity !== undefined && record.identity !== selector.identity) { + return false; + } + return true; +} + +function selectedVideoFrameRecords(state, startedAtMs, endedAtMs, selector) { + const records = []; + for (const record of state?.videoFrameRecords ?? []) { + if (record.atMs < startedAtMs || record.atMs > endedAtMs) continue; + if (!frameRecordMatches(record, selector)) continue; + records.push(record); + } + return records; +} + +function selectedVideoFrameTimes(state, startedAtMs, endedAtMs, selector) { + return selectedVideoFrameRecords(state, startedAtMs, endedAtMs, selector).map((record) => record.atMs); +} + +function lastVideoFrameRecord(state, startedAtMs, endedAtMs, selector) { + return selectedVideoFrameRecords(state, startedAtMs, endedAtMs, selector).at(-1) ?? null; +} + +function videoResolutionCounts(state, startedAtMs, endedAtMs, selector) { + const counts = new Map(); + for (const record of selectedVideoFrameRecords(state, startedAtMs, endedAtMs, selector)) { + const key = `${record.width}x${record.height}`; + counts.set(key, (counts.get(key) ?? 0) + 1); + } + return Object.fromEntries(counts); +} + +function videoResolutionMismatchCount(state, expectedWidth, expectedHeight, startedAtMs, endedAtMs, selector) { + let mismatches = 0; + for (const record of selectedVideoFrameRecords(state, startedAtMs, endedAtMs, selector)) { + if (record.width !== expectedWidth || record.height !== expectedHeight) { + mismatches += 1; + } + } + return mismatches; +} + +function currentAvDriftMs(state, startedAtMs, endedAtMs, selector) { + const videoTimes = selectedVideoFrameTimes(state, startedAtMs, endedAtMs, selector); + const audioTimes = (state?.audioFrameTimes ?? []).filter((time) => time >= startedAtMs && time <= endedAtMs); + const lastVideoFrameAtMs = videoTimes.at(-1) ?? null; + const lastAudioFrameAtMs = audioTimes.at(-1) ?? null; + if (lastVideoFrameAtMs === null || lastAudioFrameAtMs === null) return null; + return Math.abs(lastVideoFrameAtMs - lastAudioFrameAtMs); +} + +function expectedInboundVideoTrackCount(config) { + let count = 1; + if (config.camera) count += 1; + if (config.secondaryPublisher) count += 1; + return count; +} + +function expectedInboundAudioTrackCount(config) { + let count = 0; + if (config.microphone) count += 1; + if (config.screenAudio) count += 1; + if (config.secondaryPublisher && config.secondaryPublisherMicrophone) count += 1; + if (config.secondaryPublisher && config.secondaryPublisherScreenAudio) count += 1; + return count; +} + +function normalizeTrackKind(kind) { + switch (String(kind ?? '').toLowerCase()) { + case '0': + case 'audio': + return 'audio'; + case '1': + case 'video': + return 'video'; + case '2': + case 'data': + return 'data'; + default: + return String(kind ?? '').toLowerCase(); + } +} + +function normalizeTrackSource(source) { + switch (String(source ?? '').toLowerCase()) { + case '1': + case 'camera': + case 'camera_source': + return 'camera'; + case '2': + case 'microphone': + case 'mic': + return 'microphone'; + case '3': + case 'screen_share': + case 'screenshare': + case 'screen-share': + return LIVEKIT_SCREEN_SHARE_SOURCE; + case '4': + case 'screen_share_audio': + case 'screenshareaudio': + case 'screen-share-audio': + return LIVEKIT_SCREEN_SHARE_AUDIO_SOURCE; + default: + return String(source ?? ''); + } +} + +function findServerParticipant(serverState, identity) { + return serverState?.participants?.find((participant) => participant.identity === identity) ?? null; +} + +function normalizeMimeType(mimeType) { + if (typeof mimeType !== 'string' || mimeType.trim() === '') return null; + return mimeType.trim().toLowerCase(); +} + +function serverTrackMatches(track, {kind, source, mimeType}) { + if (normalizeTrackKind(track.type ?? track.kind) !== kind) return false; + if (normalizeTrackSource(track.source) !== source) return false; + if (track.muted !== false) return false; + const expectedMimeType = normalizeMimeType(mimeType); + if (expectedMimeType !== null && normalizeMimeType(track.mimeType ?? track.mime_type) !== expectedMimeType) { + return false; + } + return true; +} + +function serverParticipantHasTrack(serverState, identity, expectedTrack) { + const participant = findServerParticipant(serverState, identity); + if (!participant) return false; + const tracks = participant.tracks ?? []; + return tracks.some((track) => serverTrackMatches(track, expectedTrack)); +} + +function expectedServerTracks(config, publisherIdentity, secondaryPublisherIdentity) { + const tracks = [ + { + identity: publisherIdentity, + kind: 'video', + source: LIVEKIT_SCREEN_SHARE_SOURCE, + mimeType: config.expectedScreenCodec, + label: 'publisher screenshare', + }, + ]; + if (config.microphone) { + tracks.push({identity: publisherIdentity, kind: 'audio', source: 'microphone', label: 'publisher microphone'}); + } + if (config.screenAudio) { + tracks.push({ + identity: publisherIdentity, + kind: 'audio', + source: LIVEKIT_SCREEN_SHARE_AUDIO_SOURCE, + label: 'publisher screen-share audio', + }); + } + if (config.camera) { + tracks.push({ + identity: publisherIdentity, + kind: 'video', + source: 'camera', + mimeType: config.expectedCameraCodec, + label: 'publisher camera', + }); + } + if (config.secondaryPublisher) { + tracks.push({ + identity: secondaryPublisherIdentity, + kind: 'video', + source: LIVEKIT_SCREEN_SHARE_SOURCE, + mimeType: config.expectedSecondaryPublisherCodec, + label: 'secondary publisher screenshare', + }); + if (config.secondaryPublisherMicrophone) { + tracks.push({ + identity: secondaryPublisherIdentity, + kind: 'audio', + source: 'microphone', + label: 'secondary publisher microphone', + }); + } + if (config.secondaryPublisherScreenAudio) { + tracks.push({ + identity: secondaryPublisherIdentity, + kind: 'audio', + source: LIVEKIT_SCREEN_SHARE_AUDIO_SOURCE, + label: 'secondary publisher screen-share audio', + }); + } + } + return tracks; +} + +function serverPublishedTrackChecks({config, serverState, publisherIdentity, secondaryPublisherIdentity}) { + if (!config.validateServerPublishing) return []; + const checks = [ + { + name: 'server publishing API participants listed', + pass: Array.isArray(serverState?.participants) && !serverState?.error, + }, + ]; + for (const track of expectedServerTracks(config, publisherIdentity, secondaryPublisherIdentity)) { + checks.push({ + name: `server sees ${track.label} publication`, + pass: serverParticipantHasTrack(serverState, track.identity, { + kind: track.kind, + source: track.source, + mimeType: track.mimeType, + }), + }); + } + return checks; +} + +function currentChecks({ + publisher, + subscriber, + publisherState, + subscriberState, + config, + publisherIdentity, + secondaryPublisher, + secondaryPublisherState, + secondaryPublisherIdentity, + serverState, +}) { + const expectedVideoTracks = expectedInboundVideoTrackCount(config); + const expectedAudioTracks = expectedInboundAudioTrackCount(config); + const screenVideoMeta = lastVideoFrameRecord( + subscriberState, + Number.NEGATIVE_INFINITY, + Number.POSITIVE_INFINITY, + primaryScreenVideoSelector(config), + ); + const checks = [ + {name: 'publisher connected', pass: publisher.isConnected()}, + {name: 'subscriber connected', pass: subscriber.isConnected()}, + { + name: 'publisher local screenshare publication', + pass: hasTrackEvent(publisherState, 'localTrackPublished', { + kind: 'video', + source: LIVEKIT_SCREEN_SHARE_SOURCE, + }), + }, + { + name: 'subscriber remote screenshare subscription', + pass: hasTrackEvent(subscriberState, 'trackSubscribed', { + identity: publisherIdentity, + kind: 'video', + source: LIVEKIT_SCREEN_SHARE_SOURCE, + }), + }, + { + name: `subscriber inbound video frames >= ${MIN_INBOUND_VIDEO_FRAMES * expectedVideoTracks}`, + pass: subscriber.inboundVideoFrames() >= MIN_INBOUND_VIDEO_FRAMES * expectedVideoTracks, + }, + { + name: 'subscriber video frame callback', + pass: subscriberState.videoCallbacks > 0 && subscriberState.videoBytes > 0, + }, + { + name: 'publisher outbound screenshare stats', + pass: statsHasOutbound(publisherState.stats, 'video', LIVEKIT_SCREEN_SHARE_SOURCE), + }, + { + name: 'subscriber inbound video stats', + pass: statsHasInbound(subscriberState.stats, 'video'), + }, + { + name: `subscriber inbound video stats count >= ${expectedVideoTracks}`, + pass: statsInboundCount(subscriberState.stats, 'video') >= expectedVideoTracks, + }, + { + name: `subscriber video resolution ${config.expectedWidth}x${config.expectedHeight}`, + pass: screenVideoMeta?.width === config.expectedWidth && screenVideoMeta?.height === config.expectedHeight, + }, + ]; + if (expectedAudioTracks > 0) { + checks.push( + { + name: `subscriber aggregate inbound audio frames >= ${MIN_INBOUND_AUDIO_FRAMES * expectedAudioTracks}`, + pass: subscriber.inboundAudioFrames() >= MIN_INBOUND_AUDIO_FRAMES * expectedAudioTracks, + }, + { + name: `subscriber inbound audio stats count >= ${expectedAudioTracks}`, + pass: statsInboundCount(subscriberState.stats, 'audio') >= expectedAudioTracks, + }, + ); + } + if (config.microphone) { + checks.push( + { + name: 'publisher local microphone publication', + pass: hasTrackEvent(publisherState, 'localTrackPublished', {kind: 'audio', source: 'microphone'}), + }, + { + name: 'subscriber remote microphone subscription', + pass: hasTrackEvent(subscriberState, 'trackSubscribed', { + identity: publisherIdentity, + kind: 'audio', + source: 'microphone', + }), + }, + { + name: `subscriber inbound audio frames >= ${MIN_INBOUND_AUDIO_FRAMES * Math.max(1, expectedAudioTracks)}`, + pass: subscriber.inboundAudioFrames() >= MIN_INBOUND_AUDIO_FRAMES * Math.max(1, expectedAudioTracks), + }, + { + name: 'publisher outbound microphone stats', + pass: statsHasOutbound(publisherState.stats, 'audio', 'microphone'), + }, + { + name: 'subscriber inbound audio stats', + pass: statsHasInbound(subscriberState.stats, 'audio'), + }, + ); + } + if (config.screenAudio) { + checks.push( + { + name: 'publisher local screen-share audio publication', + pass: hasTrackEvent(publisherState, 'localTrackPublished', { + kind: 'audio', + source: LIVEKIT_SCREEN_SHARE_AUDIO_SOURCE, + }), + }, + { + name: 'subscriber remote screen-share audio subscription', + pass: hasTrackEvent(subscriberState, 'trackSubscribed', { + identity: publisherIdentity, + kind: 'audio', + source: LIVEKIT_SCREEN_SHARE_AUDIO_SOURCE, + }), + }, + { + name: 'publisher outbound screen-share audio stats', + pass: statsHasOutbound(publisherState.stats, 'audio', LIVEKIT_SCREEN_SHARE_AUDIO_SOURCE), + }, + ); + } + if (config.camera) { + checks.push( + { + name: 'publisher local camera publication', + pass: hasTrackEvent(publisherState, 'localTrackPublished', {kind: 'video', source: 'camera'}), + }, + { + name: 'subscriber remote camera subscription', + pass: hasTrackEvent(subscriberState, 'trackSubscribed', { + identity: publisherIdentity, + kind: 'video', + source: 'camera', + }), + }, + { + name: 'publisher outbound camera stats', + pass: statsHasOutbound(publisherState.stats, 'video', 'camera'), + }, + ); + if (config.expectedCameraCodec) { + checks.push({ + name: `publisher outbound camera codec ${config.expectedCameraCodec}`, + pass: statsHasOutboundCodec(publisherState.stats, 'video', 'camera', config.expectedCameraCodec), + }); + } + } + if (config.secondaryPublisher) { + checks.push( + {name: 'secondary publisher connected', pass: secondaryPublisher?.isConnected() === true}, + { + name: 'secondary publisher local screenshare publication', + pass: hasTrackEvent(secondaryPublisherState, 'localTrackPublished', { + kind: 'video', + source: LIVEKIT_SCREEN_SHARE_SOURCE, + }), + }, + { + name: 'subscriber remote secondary screenshare subscription', + pass: hasTrackEvent(subscriberState, 'trackSubscribed', { + identity: secondaryPublisherIdentity, + kind: 'video', + source: LIVEKIT_SCREEN_SHARE_SOURCE, + }), + }, + { + name: 'secondary publisher outbound screenshare stats', + pass: statsHasOutbound(secondaryPublisherState?.stats, 'video', LIVEKIT_SCREEN_SHARE_SOURCE), + }, + ); + if (config.expectedSecondaryPublisherCodec) { + checks.push( + { + name: `secondary publisher outbound screenshare codec ${config.expectedSecondaryPublisherCodec}`, + pass: statsHasOutboundCodec( + secondaryPublisherState?.stats, + 'video', + LIVEKIT_SCREEN_SHARE_SOURCE, + config.expectedSecondaryPublisherCodec, + ), + }, + { + name: `subscriber inbound secondary codec ${config.expectedSecondaryPublisherCodec}`, + pass: statsHasInboundCodec(subscriberState.stats, 'video', config.expectedSecondaryPublisherCodec), + }, + ); + } + if (config.secondaryPublisherMicrophone) { + checks.push( + { + name: 'secondary publisher local microphone publication', + pass: hasTrackEvent(secondaryPublisherState, 'localTrackPublished', { + kind: 'audio', + source: 'microphone', + }), + }, + { + name: 'subscriber remote secondary microphone subscription', + pass: hasTrackEvent(subscriberState, 'trackSubscribed', { + identity: secondaryPublisherIdentity, + kind: 'audio', + source: 'microphone', + }), + }, + { + name: 'secondary publisher outbound microphone stats', + pass: statsHasOutbound(secondaryPublisherState?.stats, 'audio', 'microphone'), + }, + ); + } + if (config.secondaryPublisherScreenAudio) { + checks.push( + { + name: 'secondary publisher local screen-share audio publication', + pass: hasTrackEvent(secondaryPublisherState, 'localTrackPublished', { + kind: 'audio', + source: LIVEKIT_SCREEN_SHARE_AUDIO_SOURCE, + }), + }, + { + name: 'subscriber remote secondary screen-share audio subscription', + pass: hasTrackEvent(subscriberState, 'trackSubscribed', { + identity: secondaryPublisherIdentity, + kind: 'audio', + source: LIVEKIT_SCREEN_SHARE_AUDIO_SOURCE, + }), + }, + { + name: 'secondary publisher outbound screen-share audio stats', + pass: statsHasOutbound(secondaryPublisherState?.stats, 'audio', LIVEKIT_SCREEN_SHARE_AUDIO_SOURCE), + }, + ); + } + } + if (config.dataPacket) { + checks.push({ + name: 'subscriber data packet received', + pass: hasEvent(subscriberState, 'dataReceived', (payload) => payload.topic === 'native-media-harness'), + }); + if (config.secondaryPublisher) { + checks.push({ + name: 'subscriber secondary data packet received', + pass: hasEvent( + subscriberState, + 'dataReceived', + (payload) => payload.topic === 'native-media-harness-secondary', + ), + }); + } + } + if (config.expectedScreenCodec) { + checks.push( + { + name: `publisher outbound screenshare codec ${config.expectedScreenCodec}`, + pass: statsHasOutboundCodec( + publisherState.stats, + 'video', + LIVEKIT_SCREEN_SHARE_SOURCE, + config.expectedScreenCodec, + ), + }, + { + name: `subscriber inbound video codec ${config.expectedScreenCodec}`, + pass: statsHasInboundCodec(subscriberState.stats, 'video', config.expectedScreenCodec), + }, + ); + } + if (config.expectedHardwareEncoder) { + const capabilities = getHardwareEncoderCapabilities(); + checks.push({ + name: `hardware encoder backend ${config.expectedHardwareEncoder}`, + pass: + capabilities.available === true && + String(capabilities.backend).toLowerCase() === config.expectedHardwareEncoder.toLowerCase(), + }); + } + checks.push(...serverPublishedTrackChecks({config, serverState, publisherIdentity, secondaryPublisherIdentity})); + return checks; +} + +async function waitForHarnessSuccess(context, pumpErrors, timeoutMs) { + const start = Date.now(); + let checks = currentChecks(context); + while (Date.now() - start < timeoutMs) { + if (pumpErrors.length > 0) { + throw pumpErrors[0]; + } + await enforceSubscriberVideoQuality(context); + await refreshServerPublishingState(context); + checks = currentChecks(context); + if (checks.every((check) => check.pass)) return checks; + await delay(250); + } + const missing = checks.filter((check) => !check.pass).map((check) => check.name); + throw new Error(`timed out after ${timeoutMs}ms waiting for: ${missing.join(', ')}`); +} + +async function holdStrictDuration(context, pumpErrors, durationMs) { + const startedAtMs = Date.now(); + context.strictStartedAtMs = startedAtMs; + if (durationMs <= 0) { + context.strictEndedAtMs = startedAtMs; + return; + } + const endAtMs = startedAtMs + durationMs; + while (Date.now() < endAtMs) { + if (pumpErrors.length > 0) throw pumpErrors[0]; + await delay(1000); + } + context.strictEndedAtMs = Date.now(); +} + +async function withTimeout(promise, timeoutMs, label) { + const timeout = delay(timeoutMs).then(() => { + throw new Error(`${label} timed out after ${timeoutMs}ms`); + }); + return Promise.race([promise, timeout]); +} + +async function disconnectEngine(engine, label, timeoutMs) { + if (!engine) return; + try { + await withTimeout(engine.disconnect(), timeoutMs, `${label} disconnect`); + } catch (error) { + console.warn(`[livekit-harness] ${label} disconnect failed: ${error.message}`); + } +} + +async function refreshServerPublishingState(context) { + const {config, serverState} = context; + if (!config.validateServerPublishing) return; + const now = Date.now(); + if (serverState.inFlight || now - serverState.lastAttemptAtMs < 500) return; + serverState.inFlight = true; + serverState.lastAttemptAtMs = now; + try { + serverState.participants = await listServerParticipants(config); + serverState.error = null; + serverState.fetchedAtMs = Date.now(); + } catch (error) { + serverState.error = String(error.message ?? error); + } finally { + serverState.inFlight = false; + } +} + +async function listServerParticipants(config) { + const token = createLiveKitAdminToken({ + apiKey: config.apiKey, + apiSecret: config.apiSecret, + room: config.room, + }); + const response = await fetch(`${config.serverApiUrl}/twirp/livekit.RoomService/ListParticipants`, { + method: 'POST', + headers: { + Authorization: `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + body: JSON.stringify({room: config.room}), + }); + const bodyText = await response.text(); + if (!response.ok) { + throw new Error(`RoomService.ListParticipants failed with HTTP ${response.status}: ${bodyText.slice(0, 240)}`); + } + let body; + try { + body = bodyText ? JSON.parse(bodyText) : {}; + } catch (error) { + throw new Error(`RoomService.ListParticipants returned invalid JSON: ${error.message}`); + } + if (!Array.isArray(body.participants)) { + throw new Error('RoomService.ListParticipants response did not contain participants'); + } + return body.participants; +} + +async function runSubscriptionCycle(subscriber, publisherIdentity) { + await subscriber.setRemoteTrackSubscription({ + participantIdentity: publisherIdentity, + source: LIVEKIT_SCREEN_SHARE_SOURCE, + subscribed: true, + enabled: false, + quality: 'low', + }); + await delay(500); + await subscriber.setRemoteTrackSubscription({ + participantIdentity: publisherIdentity, + source: LIVEKIT_SCREEN_SHARE_SOURCE, + subscribed: true, + enabled: true, + quality: 'high', + }); +} + +async function setSubscriberVideoQuality(subscriber, publisherIdentity, quality) { + if (!quality) return; + await subscriber.setRemoteTrackSubscription({ + participantIdentity: publisherIdentity, + source: LIVEKIT_SCREEN_SHARE_SOURCE, + subscribed: true, + enabled: true, + quality, + }); +} + +async function enforceSubscriberVideoQuality(context) { + await setSubscriberVideoQuality(context.subscriber, context.publisherIdentity, context.config.subscriberVideoQuality); + if (context.secondaryPublisher) { + await setSubscriberVideoQuality( + context.subscriber, + context.secondaryPublisherIdentity, + context.config.subscriberVideoQuality, + ); + } +} + +function sanitizeConfig(config) { + const { + apiSecret: _apiSecret, + e2eeKey: _e2eeKey, + publisherToken, + secondaryPublisherToken, + subscriberToken, + ...safe + } = config; + return { + ...safe, + url: sanitizeReportUrl(config.url), + serverApiUrl: sanitizeReportUrl(config.serverApiUrl), + apiSecret: '', + e2eeKey: config.e2eeKey ? '' : null, + publisherToken: publisherToken ? '' : null, + secondaryPublisherToken: secondaryPublisherToken ? '' : null, + subscriberToken: subscriberToken ? '' : null, + }; +} + +function summarizeServerPublishing(serverState) { + if (!serverState) return null; + return { + fetchedAtMs: serverState.fetchedAtMs, + error: serverState.error, + participants: (serverState.participants ?? []).map((participant) => ({ + identity: participant.identity, + trackCount: participant.tracks?.length ?? 0, + tracks: (participant.tracks ?? []).map((track) => ({ + sid: track.sid ?? null, + name: track.name ?? null, + kind: normalizeTrackKind(track.type ?? track.kind), + source: normalizeTrackSource(track.source), + mimeType: track.mimeType ?? track.mime_type ?? null, + muted: track.muted ?? null, + })), + })), + }; +} + +function sanitizeReportUrl(rawUrl) { + if (typeof rawUrl !== 'string') return rawUrl; + try { + const url = new URL(rawUrl); + if (!url.username && !url.password && !url.search && !url.hash) return rawUrl; + url.username = ''; + url.password = ''; + url.search = ''; + url.hash = ''; + return url.toString(); + } catch { + return rawUrl.replace(/\/\/[^/@\s]+@/g, '//@').replace(/[?][^\s#]*/g, '?'); + } +} + +function scenarioRoomName(room, scenarioName, scenarioCount, externalTokens) { + if (scenarioCount <= 1 || externalTokens) return room; + const suffix = scenarioName.replace(/[^a-z0-9_.-]/gi, '-').replace(/-+/g, '-'); + return `${room}-${suffix}`.slice(0, 128); +} + +function buildScenarioConfigs(baseConfig) { + const scenarioCount = baseConfig.screenCodecs.length; + return baseConfig.screenCodecs.map((codec, index) => { + const expectedCodec = baseConfig.expectedScreenCodecs[index] ?? codec; + const secondaryCodec = baseConfig.secondaryPublisherCodecExplicit + ? baseConfig.secondaryPublisherCodec + : (baseConfig.screenCodecs[(index + 1) % baseConfig.screenCodecs.length] ?? codec); + const expectedSecondaryCodec = baseConfig.expectedSecondaryPublisherCodecExplicit + ? baseConfig.expectedSecondaryPublisherCodec + : expectedCodecMime(secondaryCodec); + const scenarioName = baseConfig.secondaryPublisher ? `codec-${codec}-with-${secondaryCodec}` : `codec-${codec}`; + return { + ...baseConfig, + scenarioName, + room: scenarioRoomName(baseConfig.room, scenarioName, scenarioCount, baseConfig.externalTokens), + codec, + expectedScreenCodec: expectedCodecMime(expectedCodec), + secondaryPublisherCodec: secondaryCodec, + expectedSecondaryPublisherCodec: expectedSecondaryCodec, + reportPath: scenarioCount > 1 ? null : baseConfig.reportPath, + }; + }); +} + +function buildReport({status, config, checks = [], error = null, skippedReason = null, context = null}) { + const now = Date.now(); + const strictStartedAtMs = context?.strictStartedAtMs ?? context?.startedAtMs ?? now; + const strictEndedAtMs = context?.strictEndedAtMs ?? now; + const subscriberState = context?.subscriberState; + const subscriber = context?.subscriber; + const publisherState = context?.publisherState; + const secondaryPublisherState = context?.secondaryPublisherState; + const capabilities = typeof getHardwareEncoderCapabilities === 'function' ? getHardwareEncoderCapabilities() : null; + const strictVideoSelector = primaryScreenVideoSelector(config); + const inboundAudioFrames = subscriber ? subscriber.inboundAudioFrames() : 0; + const inboundVideoFrames = subscriber ? subscriber.inboundVideoFrames() : 0; + const strictVideoFrameTimes = subscriberState + ? selectedVideoFrameTimes(subscriberState, strictStartedAtMs, strictEndedAtMs, strictVideoSelector) + : []; + const strictVideoFrames = strictVideoFrameTimes.length; + const strictAudioFrames = subscriberState + ? countWindowSamples(subscriberState.audioFrameTimes, strictStartedAtMs, strictEndedAtMs) + : 0; + const measuredFps = receivedFpsFromTimes(strictVideoFrameTimes); + const avDriftMs = subscriberState + ? currentAvDriftMs(subscriberState, strictStartedAtMs, strictEndedAtMs, strictVideoSelector) + : null; + const observedPacketLoss = maxObservedPacketsLost(publisherState, secondaryPublisherState, subscriberState); + const observedPacketLossDelta = maxObservedPacketsLostDelta( + strictStartedAtMs, + strictEndedAtMs, + publisherState, + secondaryPublisherState, + subscriberState, + ); + return { + status, + startedAt: context?.startedAtIso ?? new Date(now).toISOString(), + endedAt: new Date(now).toISOString(), + platform: process.platform, + arch: process.arch, + config: sanitizeConfig(config), + checks, + skippedReason, + error: error ? String(error.stack ?? error.message ?? error) : null, + hardwareEncoder: capabilities, + metrics: { + inboundAudioFrames, + inboundVideoFrames, + strictAudioFrames, + strictVideoFrames, + videoCallbacks: subscriberState?.videoCallbacks ?? 0, + videoBytes: subscriberState?.videoBytes ?? 0, + receivedFps: Math.round(measuredFps * 100) / 100, + requiredFps: config.minFpsRatio > 0 ? Math.round(config.fps * config.minFpsRatio * 100) / 100 : null, + maxVideoFrameGapMs: maxGapMs(strictVideoFrameTimes, strictStartedAtMs, strictEndedAtMs), + maxAudioFrameGapMs: subscriberState + ? maxGapMs(subscriberState.audioFrameTimes, strictStartedAtMs, strictEndedAtMs) + : 0, + maxVideoFrameGapOverallMs: subscriberState?.maxVideoFrameGapMs ?? 0, + maxAudioFrameGapOverallMs: subscriberState?.maxAudioFrameGapMs ?? 0, + avDriftMs, + maxObservedPacketLoss: observedPacketLoss, + maxObservedPacketLossDelta: observedPacketLossDelta, + videoResolutionCounts: videoResolutionCounts( + subscriberState, + strictStartedAtMs, + strictEndedAtMs, + strictVideoSelector, + ), + videoResolutionMismatchCount: videoResolutionMismatchCount( + subscriberState, + config.expectedWidth, + config.expectedHeight, + strictStartedAtMs, + strictEndedAtMs, + strictVideoSelector, + ), + droppedVideoFrameCallbacks: subscriber ? subscriber.droppedVideoFrameCallbacks() : 0, + publisherVideoPump: context?.videoPump?.stats?.() ?? null, + secondaryPublisherVideoPump: context?.secondaryVideoPump?.stats?.() ?? null, + serverPublishing: summarizeServerPublishing(context?.serverState), + lastVideoMeta: subscriberState?.lastVideoMeta ?? null, + lastStrictVideoMeta: lastVideoFrameRecord( + subscriberState, + strictStartedAtMs, + strictEndedAtMs, + strictVideoSelector, + ), + publisherStats: publisherState?.stats ?? null, + secondaryPublisherStats: secondaryPublisherState?.stats ?? null, + subscriberStats: subscriberState?.stats ?? null, + }, + }; +} + +function strictFailures(report, config) { + const failures = []; + if (config.minFpsRatio > 0 && report.metrics.receivedFps < config.fps * config.minFpsRatio) { + failures.push(`received FPS ${report.metrics.receivedFps} below ${config.fps * config.minFpsRatio}`); + } + if (config.maxFrameGapMs > 0) { + if (report.metrics.strictVideoFrames < 2) { + failures.push( + 'video frame gap unavailable because fewer than two video frames were observed during strict window', + ); + } else if (report.metrics.maxVideoFrameGapMs > config.maxFrameGapMs) { + failures.push(`max video frame gap ${report.metrics.maxVideoFrameGapMs}ms exceeds ${config.maxFrameGapMs}ms`); + } + } + if (config.maxAudioFrameGapMs > 0 && expectedInboundAudioTrackCount(config) > 0) { + if (report.metrics.strictAudioFrames < 2) { + failures.push( + 'audio frame gap unavailable because fewer than two audio frames were observed during strict window', + ); + } else if (report.metrics.maxAudioFrameGapMs > config.maxAudioFrameGapMs) { + failures.push( + `max audio frame gap ${report.metrics.maxAudioFrameGapMs}ms exceeds ${config.maxAudioFrameGapMs}ms`, + ); + } + } + if (config.maxAvDriftMs > 0) { + if (report.metrics.avDriftMs === null) { + failures.push('A/V drift unavailable because video or audio timing was not observed'); + } else if (report.metrics.avDriftMs > config.maxAvDriftMs) { + failures.push(`A/V drift ${report.metrics.avDriftMs}ms exceeds ${config.maxAvDriftMs}ms`); + } + } + if (config.maxPacketLoss !== null && report.metrics.maxObservedPacketLossDelta > config.maxPacketLoss) { + failures.push( + `observed packet loss delta ${report.metrics.maxObservedPacketLossDelta} exceeds ${config.maxPacketLoss}`, + ); + } + if (config.requireStableResolution && report.metrics.videoResolutionMismatchCount > 0) { + failures.push( + `observed ${report.metrics.videoResolutionMismatchCount} video frames outside ${config.expectedWidth}x${config.expectedHeight}`, + ); + } + if (report.metrics.droppedVideoFrameCallbacks > 0) { + failures.push(`dropped video frame callbacks ${report.metrics.droppedVideoFrameCallbacks}`); + } + return failures; +} + +async function writeReportIfRequested(report, reportPath) { + if (!reportPath) return; + await writeFile(reportPath, `${JSON.stringify(report, null, 2)}\n`, 'utf8'); +} + +async function runHarness(config) { + if (!isSupported()) { + throw new Error(`native binding unavailable: ${loadError?.message ?? 'unknown error'}`); + } + + const target = liveKitTcpTarget(config.url); + const probe = await probeTcp(target, config.probeTimeoutMs); + if (!probe.ok) { + const message = `LiveKit is unreachable at ${config.url} (${probe.reason})`; + if (!config.required) { + console.log(`[livekit-harness] SKIP: ${message}. Set FLUXER_WEBRTC_SENDER_LIVEKIT_REQUIRED=1 to fail instead.`); + return {skipped: true, skippedReason: message}; + } + throw new Error(message); + } + + const publisherIdentity = config.publisherIdentity; + const secondaryPublisherIdentity = config.secondaryPublisherIdentity; + const subscriberIdentity = config.subscriberIdentity; + const publisherToken = + config.publisherToken ?? + createLiveKitToken({ + apiKey: config.apiKey, + apiSecret: config.apiSecret, + room: config.room, + identity: publisherIdentity, + name: 'Fluxer Native Publisher', + }); + const secondaryPublisherToken = config.secondaryPublisher + ? (config.secondaryPublisherToken ?? + createLiveKitToken({ + apiKey: config.apiKey, + apiSecret: config.apiSecret, + room: config.room, + identity: secondaryPublisherIdentity, + name: 'Fluxer Native Publisher 2', + })) + : null; + const subscriberToken = + config.subscriberToken ?? + createLiveKitToken({ + apiKey: config.apiKey, + apiSecret: config.apiSecret, + room: config.room, + identity: subscriberIdentity, + name: 'Fluxer Native Subscriber', + }); + + const publisher = new VoiceEngine(); + const secondaryPublisher = config.secondaryPublisher ? new VoiceEngine() : null; + const subscriber = new VoiceEngine(); + const publisherState = createEngineState('publisher', config.verbose); + const secondaryPublisherState = config.secondaryPublisher ? createEngineState('publisher-2', config.verbose) : null; + const subscriberState = createEngineState('subscriber', config.verbose); + const pumpErrors = []; + const context = { + publisher, + secondaryPublisher, + subscriber, + publisherState, + secondaryPublisherState, + subscriberState, + serverState: { + participants: null, + error: null, + fetchedAtMs: null, + lastAttemptAtMs: 0, + inFlight: false, + }, + config, + publisherIdentity, + secondaryPublisherIdentity, + subscriberIdentity, + startedAtMs: Date.now(), + startedAtIso: new Date().toISOString(), + strictStartedAtMs: null, + strictEndedAtMs: null, + }; + let videoPump = null; + let secondaryVideoPump = null; + let micPump = null; + let secondaryMicPump = null; + let screenAudioPump = null; + let secondaryScreenAudioPump = null; + let audioSampler = null; + + publisher.setEventCallback((...args) => { + const [eventType, payload] = unpackNapiPair(args); + publisherState.recordEvent(eventType, payload); + }); + secondaryPublisher?.setEventCallback((...args) => { + const [eventType, payload] = unpackNapiPair(args); + secondaryPublisherState.recordEvent(eventType, payload); + }); + subscriber.setEventCallback((...args) => { + const [eventType, payload] = unpackNapiPair(args); + subscriberState.recordEvent(eventType, payload); + }); + subscriber.setVideoFrameCallback((...args) => { + const [metaJson, data] = unpackNapiPair(args); + subscriberState.recordVideoFrame(metaJson, data); + }); + subscriber.setCountInboundAudio(true); + audioSampler = startAudioSampler(subscriber, subscriberState); + + try { + const e2eeKey = config.e2eeKey ? Buffer.from(config.e2eeKey, 'utf8') : undefined; + console.log(`[livekit-harness] connecting to ${config.url} room=${config.room}`); + await withTimeout( + Promise.all( + [ + publisher.connect(config.url, publisherToken, e2eeKey), + secondaryPublisher && secondaryPublisherToken + ? secondaryPublisher.connect(config.url, secondaryPublisherToken, e2eeKey) + : null, + subscriber.connect(config.url, subscriberToken, e2eeKey), + ].filter(Boolean), + ), + config.connectTimeoutMs, + 'connect', + ); + + await withTimeout( + publisher.publishScreenShare( + config.width, + config.height, + config.codec, + config.maxBitrateBps, + config.fps, + config.screenSimulcast, + { + adaptiveSend: config.adaptiveSend, + minVideoFps: config.minVideoFps, + minResolutionScale: config.minResolutionScale, + maxAudioBufferMs: config.maxAudioBufferMs, + captureId: 'livekit-harness-primary-screen', + trackName: 'livekit-harness-primary-screen', + }, + ), + 10_000, + 'publish screenshare', + ); + if (secondaryPublisher) { + await withTimeout( + secondaryPublisher.publishScreenShare( + config.width, + config.height, + config.secondaryPublisherCodec, + config.maxBitrateBps, + config.fps, + config.secondaryPublisherScreenSimulcast, + { + adaptiveSend: config.adaptiveSend, + minVideoFps: config.minVideoFps, + minResolutionScale: config.minResolutionScale, + maxAudioBufferMs: config.maxAudioBufferMs, + captureId: 'livekit-harness-secondary-screen', + trackName: 'livekit-harness-secondary-screen', + }, + ), + 10_000, + 'publish secondary screenshare', + ); + } + if (config.microphone) { + await withTimeout(publisher.publishMicrophone(config.sampleRate, config.channels), 10_000, 'publish microphone'); + } + if (secondaryPublisher && config.secondaryPublisherMicrophone) { + await withTimeout( + secondaryPublisher.publishMicrophone(config.sampleRate, config.channels), + 10_000, + 'publish secondary microphone', + ); + } + if (config.screenAudio) { + await withTimeout( + publisher.publishScreenShareAudio(config.sampleRate, config.channels), + 10_000, + 'publish screen-share audio', + ); + } + if (secondaryPublisher && config.secondaryPublisherScreenAudio) { + await withTimeout( + secondaryPublisher.publishScreenShareAudio(config.sampleRate, config.channels), + 10_000, + 'publish secondary screen-share audio', + ); + } + if (config.camera) { + await withTimeout( + publisher.publishCamera({ + deviceId: config.cameraDeviceId ?? undefined, + width: config.width, + height: config.height, + frameRate: config.fps, + }), + 10_000, + 'publish camera', + ); + } + + videoPump = startVideoPump(publisher, config, pumpErrors); + context.videoPump = videoPump; + if (secondaryPublisher) { + secondaryVideoPump = startVideoPump(secondaryPublisher, config, pumpErrors); + context.secondaryVideoPump = secondaryVideoPump; + } + if (config.microphone) { + micPump = startAudioPump( + 'pushPcm', + (buffer, sampleRate, channels) => publisher.pushPcm(buffer, sampleRate, channels), + config, + pumpErrors, + ); + } + if (secondaryPublisher && config.secondaryPublisherMicrophone) { + secondaryMicPump = startAudioPump( + 'secondary pushPcm', + (buffer, sampleRate, channels) => secondaryPublisher.pushPcm(buffer, sampleRate, channels), + config, + pumpErrors, + ); + } + if (config.screenAudio) { + screenAudioPump = startAudioPump( + 'pushScreenSharePcm', + (buffer, sampleRate, channels) => publisher.pushScreenSharePcm(buffer, sampleRate, channels), + config, + pumpErrors, + ); + } + if (secondaryPublisher && config.secondaryPublisherScreenAudio) { + secondaryScreenAudioPump = startAudioPump( + 'secondary pushScreenSharePcm', + (buffer, sampleRate, channels) => secondaryPublisher.pushScreenSharePcm(buffer, sampleRate, channels), + config, + pumpErrors, + ); + } + if (config.dataPacket) { + await publisher.publishData(Buffer.from('fluxer-native-media-harness'), { + reliable: true, + topic: 'native-media-harness', + destinationIdentities: [subscriberIdentity], + }); + if (secondaryPublisher) { + await secondaryPublisher.publishData(Buffer.from('fluxer-native-media-harness-secondary'), { + reliable: true, + topic: 'native-media-harness-secondary', + destinationIdentities: [subscriberIdentity], + }); + } + } + await setSubscriberVideoQuality(subscriber, publisherIdentity, config.subscriberVideoQuality); + if (secondaryPublisher) { + await setSubscriberVideoQuality(subscriber, secondaryPublisherIdentity, config.subscriberVideoQuality); + } + if (config.subscriptionCycle) { + await runSubscriptionCycle(subscriber, publisherIdentity); + if (secondaryPublisher) { + await runSubscriptionCycle(subscriber, secondaryPublisherIdentity); + } + } + + const checks = await waitForHarnessSuccess(context, pumpErrors, config.timeoutMs); + await holdStrictDuration(context, pumpErrors, config.durationMs); + const report = buildReport({status: 'pass', config, checks, context}); + const failures = strictFailures(report, config); + if (failures.length > 0) { + const failedReport = {...report, status: 'fail', error: failures.join('; ')}; + await writeReportIfRequested(failedReport, config.reportPath); + const error = new Error(failures.join('; ')); + error.report = failedReport; + throw error; + } + await writeReportIfRequested(report, config.reportPath); + const producedFps = report.metrics.publisherVideoPump?.producedFps ?? null; + console.log( + `[livekit-harness] PASS: ${checks.length} checks, inboundAudioFrames=${report.metrics.inboundAudioFrames}, inboundVideoFrames=${report.metrics.inboundVideoFrames}, videoCallbacks=${report.metrics.videoCallbacks}, receivedFps=${report.metrics.receivedFps}, producedFps=${producedFps}, maxVideoFrameGapMs=${report.metrics.maxVideoFrameGapMs}, maxAudioFrameGapMs=${report.metrics.maxAudioFrameGapMs}, maxObservedPacketLoss=${report.metrics.maxObservedPacketLoss}, maxObservedPacketLossDelta=${report.metrics.maxObservedPacketLossDelta}, resolutionMismatches=${report.metrics.videoResolutionMismatchCount}, droppedVideoFrameCallbacks=${report.metrics.droppedVideoFrameCallbacks}`, + ); + return {skipped: false, report}; + } catch (error) { + if (!error.report) { + let checks = []; + try { + checks = currentChecks(context); + } catch {} + error.report = buildReport({status: 'fail', config, checks, error, context}); + } + throw error; + } finally { + videoPump?.stop(); + secondaryVideoPump?.stop(); + micPump?.stop(); + secondaryMicPump?.stop(); + screenAudioPump?.stop(); + secondaryScreenAudioPump?.stop(); + audioSampler?.stop(); + await micPump?.done(); + await secondaryMicPump?.done(); + await screenAudioPump?.done(); + await secondaryScreenAudioPump?.done(); + await disconnectEngine(subscriber, 'subscriber', config.disconnectTimeoutMs); + await disconnectEngine(secondaryPublisher, 'publisher-2', config.disconnectTimeoutMs); + await disconnectEngine(publisher, 'publisher', config.disconnectTimeoutMs); + } +} + +function buildAggregateReport({status, config, scenarioReports, startedAtMs}) { + return { + status, + startedAt: new Date(startedAtMs).toISOString(), + endedAt: new Date().toISOString(), + platform: process.platform, + arch: process.arch, + config: sanitizeConfig(config), + scenarioCount: scenarioReports.length, + scenarios: scenarioReports, + }; +} + +async function runScenarioSuite(baseConfig, scenarios) { + const startedAtMs = Date.now(); + const scenarioReports = []; + for (let index = 0; index < scenarios.length; index += 1) { + const scenario = scenarios[index]; + console.log( + `[livekit-harness] scenario ${index + 1}/${scenarios.length}: ${scenario.scenarioName} room=${scenario.room}`, + ); + try { + const result = await runHarness(scenario); + if (result.skipped) { + scenarioReports.push(buildReport({status: 'skip', config: scenario, skippedReason: result.skippedReason})); + } else { + scenarioReports.push(result.report); + } + } catch (error) { + scenarioReports.push(error.report ?? buildReport({status: 'fail', config: scenario, error})); + } + } + const failed = scenarioReports.filter((report) => report.status === 'fail'); + const skipped = scenarioReports.filter((report) => report.status === 'skip'); + const status = failed.length > 0 ? 'fail' : skipped.length === scenarioReports.length ? 'skip' : 'pass'; + const aggregate = buildAggregateReport({status, config: baseConfig, scenarioReports, startedAtMs}); + await writeReportIfRequested(aggregate, baseConfig.reportPath); + if (status === 'fail') { + const failedNames = failed.map((report) => report.config.scenarioName ?? report.config.codec).join(', '); + const error = new Error(`LiveKit scenario suite failed: ${failedNames}`); + error.report = aggregate; + throw error; + } + return aggregate; +} + +async function main() { + const baseConfig = parseConfig(); + const scenarios = buildScenarioConfigs(baseConfig); + try { + if (scenarios.length > 1) { + const aggregate = await runScenarioSuite(baseConfig, scenarios); + console.log(`[livekit-harness] ${aggregate.status.toUpperCase()}: ${aggregate.scenarioCount} codec scenarios`); + return 0; + } + const config = scenarios[0]; + const result = await runHarness(config); + if (result.skipped) { + const report = buildReport({status: 'skip', config, skippedReason: result.skippedReason}); + await writeReportIfRequested(report, baseConfig.reportPath); + } + return 0; + } catch (error) { + const report = error.report ?? buildReport({status: 'fail', config: baseConfig, error}); + await writeReportIfRequested(report, baseConfig.reportPath); + console.error(`[livekit-harness] FAIL: ${error.stack ?? error.message}`); + return 1; + } +} + +export { + buildAggregateReport, + buildReport, + buildScenarioConfigs, + createLiveKitAdminToken, + createLiveKitToken, + expectedCodecMime, + jwtSubject, + liveKitApiUrl, + liveKitTcpTarget, + parseCodecList, + parseConfig, + sanitizeConfig, + scenarioRoomName, + serverPublishedTrackChecks, + statsHasOutbound, + statsHasOutboundCodec, + strictFailures, +}; + +if (process.argv[1] === fileURLToPath(import.meta.url)) { + const exitCode = await main(); + setImmediate(() => process.exit(exitCode)); +} diff --git a/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.test.mjs b/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.test.mjs new file mode 100644 index 0000000..ed6547f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/scripts/livekit-harness.test.mjs @@ -0,0 +1,842 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import assert from 'node:assert/strict'; +import {describe, test} from 'node:test'; +import { + buildReport, + buildScenarioConfigs, + createLiveKitAdminToken, + expectedCodecMime, + jwtSubject, + liveKitApiUrl, + liveKitTcpTarget, + parseCodecList, + parseConfig, + sanitizeConfig, + scenarioRoomName, + serverPublishedTrackChecks, + statsHasOutbound, + statsHasOutboundCodec, + strictFailures, +} from './livekit-harness.mjs'; + +function tokenForSubject(subject) { + const payload = Buffer.from(JSON.stringify({sub: subject}), 'utf8').toString('base64url'); + return `header.${payload}.signature`; +} + +function jwtPayload(token) { + const parts = token.split('.'); + return JSON.parse(Buffer.from(parts[1], 'base64url').toString('utf8')); +} + +async function withHarnessEnv(env, callback) { + const previous = {...process.env}; + for (const key of Object.keys(process.env)) { + if ( + key.startsWith('LIVEKIT_') || + key.startsWith('FLUXER_WEBRTC_SENDER_LIVEKIT') || + key.startsWith('FLUXER_NATIVE_MEDIA') + ) { + delete process.env[key]; + } + } + Object.assign(process.env, env); + try { + return await callback(); + } finally { + for (const key of Object.keys(process.env)) { + delete process.env[key]; + } + Object.assign(process.env, previous); + } +} + +function strictReportFor( + config, + { + videoFrameTimes = null, + videoFrameRecords = null, + audioFrameTimes = [], + droppedVideoFrameCallbacks = 0, + subscriberStatsSamples = [], + } = {}, +) { + const strictStartedAtMs = 1_000; + const strictEndedAtMs = 2_000; + const effectiveVideoFrameRecords = + videoFrameRecords ?? + (videoFrameTimes ?? []).map((atMs) => ({ + atMs, + identity: config.publisherIdentity, + kind: 'video', + source: 'screen_share', + trackName: 'screen', + trackSid: 'TR_screen', + width: config.expectedWidth, + height: config.expectedHeight, + })); + const effectiveVideoFrameTimes = videoFrameTimes ?? effectiveVideoFrameRecords.map((record) => record.atMs); + return buildReport({ + status: 'pass', + config, + context: { + startedAtMs: strictStartedAtMs - 500, + startedAtIso: new Date(strictStartedAtMs - 500).toISOString(), + strictStartedAtMs, + strictEndedAtMs, + publisherState: {statsSamples: [], stats: null}, + subscriberState: { + statsSamples: subscriberStatsSamples, + stats: subscriberStatsSamples.at(-1)?.payload ?? null, + videoCallbacks: effectiveVideoFrameTimes.length, + videoBytes: effectiveVideoFrameTimes.length * 4, + videoFrameTimes: effectiveVideoFrameTimes, + videoFrameRecords: effectiveVideoFrameRecords, + audioFrameTimes, + lastVideoMeta: effectiveVideoFrameRecords.at(-1) ?? null, + lastVideoFrameAtMs: effectiveVideoFrameTimes.at(-1) ?? null, + lastAudioFrameAtMs: audioFrameTimes.at(-1) ?? null, + }, + subscriber: { + inboundAudioFrames: () => audioFrameTimes.length, + inboundVideoFrames: () => effectiveVideoFrameTimes.length, + droppedVideoFrameCallbacks: () => droppedVideoFrameCallbacks, + }, + }, + }); +} + +function findCheck(checks, name) { + const check = checks.find((entry) => entry.name === name); + assert.ok(check, `missing check: ${name}`); + return check; +} + +function serverChecksFor(config, serverState) { + return serverPublishedTrackChecks({ + config, + serverState, + publisherIdentity: config.publisherIdentity, + secondaryPublisherIdentity: config.secondaryPublisherIdentity, + }); +} + +describe('livekit harness setup matrix', () => { + test('normalizes LiveKit screenshare source spelling in outbound stats', () => { + const stats = { + outbound: [ + { + trackSid: 'TR_screen', + source: 'screenshare', + kind: 'video', + codec: 'video/VP8', + bitrateKbps: 400, + packetsLost: 0, + }, + ], + }; + + assert.equal(statsHasOutbound(stats, 'video', 'screen_share'), true); + assert.equal(statsHasOutboundCodec(stats, 'video', 'screen_share', 'video/VP8'), true); + assert.equal(statsHasOutboundCodec(stats, 'video', 'screen_share', 'video/H264'), false); + }); + + test('maps all supported codec spellings to expected mime types', () => { + assert.equal(expectedCodecMime('vp8'), 'video/VP8'); + assert.equal(expectedCodecMime('VP9'), 'video/VP9'); + assert.equal(expectedCodecMime('h264'), 'video/H264'); + assert.equal(expectedCodecMime('av1'), 'video/AV1'); + assert.equal(expectedCodecMime('h265'), 'video/H265'); + assert.equal(expectedCodecMime('hevc'), 'video/H265'); + assert.equal(expectedCodecMime(''), null); + assert.throws(() => expectedCodecMime('h266'), /unsupported/); + }); + + test('parses codec lists with fallback and rejects empty effective lists', () => { + assert.deepEqual(parseCodecList(' vp8, h264,, hevc ', 'av1'), ['vp8', 'h264', 'hevc']); + assert.deepEqual(parseCodecList('', 'vp9'), ['vp9']); + assert.throws(() => parseCodecList('', ''), /at least one/); + assert.throws(() => parseCodecList('vp8,h266', 'vp8'), /unsupported/); + }); + + test('default non-strict config keeps the fast local live setup small', async () => { + await withHarnessEnv({}, () => { + const config = parseConfig(); + assert.equal(config.url, 'ws://localhost:7880'); + assert.equal(config.serverApiUrl, 'http://localhost:7880'); + assert.deepEqual(config.screenCodecs, ['vp8']); + assert.equal(config.expectedScreenCodec, 'video/VP8'); + assert.equal(config.secondaryPublisher, false); + assert.equal(config.microphone, true); + assert.equal(config.screenAudio, false); + assert.equal(config.dataPacket, false); + assert.equal(config.subscriptionCycle, false); + assert.equal(config.validateServerPublishing, false); + assert.equal(config.durationMs, 0); + assert.equal(config.externalTokens, false); + assert.equal(config.screenSimulcast, true); + assert.equal(config.secondaryPublisherScreenSimulcast, true); + assert.equal(config.adaptiveSend, true); + assert.equal(config.minVideoFps, 15); + assert.equal(config.minResolutionScale, 0.5); + assert.equal(config.maxAudioBufferMs, 750); + }); + }); + + test('strict config expands to multi-codec secondary-publisher scenarios', async () => { + await withHarnessEnv( + { + LIVEKIT_HARNESS_STRICT: '1', + LIVEKIT_ROOM: 'matrix-room', + LIVEKIT_SCREEN_CODECS: 'vp8,h264,hevc', + LIVEKIT_EXPECT_SCREEN_CODECS: 'vp8,h264,h265', + }, + () => { + const config = parseConfig(); + assert.equal(config.secondaryPublisher, true); + assert.equal(config.screenAudio, true); + assert.equal(config.dataPacket, true); + assert.equal(config.subscriptionCycle, true); + assert.equal(config.validateServerPublishing, true); + assert.equal(config.durationMs, 10 * 60 * 1000); + assert.equal(config.maxAudioFrameGapMs, 250); + assert.equal(config.maxPacketLoss, null); + assert.equal(config.requireStableResolution, true); + assert.equal(config.videoPattern, 'gradient'); + + const scenarios = buildScenarioConfigs(config); + assert.deepEqual( + scenarios.map((scenario) => scenario.scenarioName), + ['codec-vp8-with-h264', 'codec-h264-with-hevc', 'codec-hevc-with-vp8'], + ); + assert.deepEqual( + scenarios.map((scenario) => scenario.expectedScreenCodec), + ['video/VP8', 'video/H264', 'video/H265'], + ); + assert.deepEqual( + scenarios.map((scenario) => scenario.expectedSecondaryPublisherCodec), + ['video/H264', 'video/H265', 'video/VP8'], + ); + assert.equal(scenarios[0].room, 'matrix-room-codec-vp8-with-h264'); + assert.equal(scenarios[1].reportPath, null); + }, + ); + }); + + test('stress knobs parse packet loss, resolution stability, audio gap, and video pattern', async () => { + await withHarnessEnv( + { + LIVEKIT_MAX_PACKET_LOSS: '0', + LIVEKIT_REQUIRE_STABLE_RESOLUTION: '0', + LIVEKIT_MAX_AUDIO_FRAME_GAP_MS: '180', + LIVEKIT_VIDEO_PATTERN: 'fast', + LIVEKIT_VIDEO_INPUT: 'nv12', + LIVEKIT_SUBSCRIBER_VIDEO_QUALITY: 'high', + LIVEKIT_SCREEN_SIMULCAST: '0', + LIVEKIT_SECOND_PUBLISHER_SCREEN_SIMULCAST: '1', + LIVEKIT_SCREEN_FPS: '60', + LIVEKIT_ADAPTIVE_SEND: '0', + LIVEKIT_MIN_VIDEO_FPS: '24', + LIVEKIT_MIN_RESOLUTION_SCALE: '0.75', + LIVEKIT_MAX_AUDIO_BUFFER_MS: '640', + }, + () => { + const config = parseConfig(); + assert.equal(config.maxPacketLoss, 0); + assert.equal(config.requireStableResolution, false); + assert.equal(config.maxAudioFrameGapMs, 180); + assert.equal(config.videoPattern, 'fast'); + assert.equal(config.videoInput, 'nv12'); + assert.equal(config.subscriberVideoQuality, 'high'); + assert.equal(config.screenSimulcast, false); + assert.equal(config.secondaryPublisherScreenSimulcast, true); + assert.equal(config.adaptiveSend, false); + assert.equal(config.minVideoFps, 24); + assert.equal(config.minResolutionScale, 0.75); + assert.equal(config.maxAudioBufferMs, 640); + }, + ); + }); + + test('stress knobs reject impossible send pacing configurations', async () => { + await withHarnessEnv({LIVEKIT_SCREEN_FPS: '0'}, () => + assert.throws(() => parseConfig(), /LIVEKIT_SCREEN_FPS must be a positive number/), + ); + await withHarnessEnv({LIVEKIT_MIN_VIDEO_FPS: '0'}, () => + assert.throws(() => parseConfig(), /LIVEKIT_MIN_VIDEO_FPS must be a positive number/), + ); + await withHarnessEnv({LIVEKIT_SCREEN_FPS: '30', LIVEKIT_MIN_VIDEO_FPS: '60'}, () => + assert.throws(() => parseConfig(), /MIN_VIDEO_FPS must be less than or equal/), + ); + await withHarnessEnv({LIVEKIT_MAX_AUDIO_BUFFER_MS: '0'}, () => + assert.throws(() => parseConfig(), /LIVEKIT_MAX_AUDIO_BUFFER_MS must be a positive integer/), + ); + await withHarnessEnv({LIVEKIT_MIN_RESOLUTION_SCALE: '0'}, () => + assert.throws(() => parseConfig(), /LIVEKIT_MIN_RESOLUTION_SCALE must be a positive number/), + ); + await withHarnessEnv({LIVEKIT_MIN_RESOLUTION_SCALE: '1.5'}, () => + assert.throws(() => parseConfig(), /LIVEKIT_MIN_RESOLUTION_SCALE must be greater than 0/), + ); + await withHarnessEnv({LIVEKIT_ENABLE_SCREEN_AUDIO: 'maybe'}, () => + assert.throws(() => parseConfig(), /LIVEKIT_ENABLE_SCREEN_AUDIO must be a boolean flag/), + ); + await withHarnessEnv({LIVEKIT_SCREEN_CODECS: 'vp8,h264', LIVEKIT_EXPECT_SCREEN_CODECS: 'vp8'}, () => + assert.throws(() => parseConfig(), /EXPECT_SCREEN_CODECS length must match/), + ); + }); + + test('explicit secondary codec expectations override rotating defaults', async () => { + await withHarnessEnv( + { + LIVEKIT_ENABLE_SECOND_PUBLISHER: '1', + LIVEKIT_SCREEN_CODECS: 'vp8,h264', + LIVEKIT_SECOND_PUBLISHER_CODEC: 'av1', + LIVEKIT_EXPECT_SECOND_PUBLISHER_SCREEN_CODEC: 'hevc', + }, + () => { + const scenarios = buildScenarioConfigs(parseConfig()); + assert.deepEqual( + scenarios.map((scenario) => scenario.secondaryPublisherCodec), + ['av1', 'av1'], + ); + assert.deepEqual( + scenarios.map((scenario) => scenario.expectedSecondaryPublisherCodec), + ['video/H265', 'video/H265'], + ); + }, + ); + }); + + test('secondary screen codec aliases are accepted and conflicting aliases are rejected', async () => { + await withHarnessEnv( + { + LIVEKIT_ENABLE_SECOND_PUBLISHER: '1', + LIVEKIT_SCREEN_CODECS: 'h264', + LIVEKIT_SECOND_PUBLISHER_SCREEN_CODECS: 'vp8', + LIVEKIT_EXPECT_SECONDARY_PUBLISHER_SCREEN_CODEC: 'vp8', + }, + () => { + const config = parseConfig(); + assert.equal(config.secondaryPublisherCodec, 'vp8'); + assert.equal(config.expectedSecondaryPublisherCodec, 'video/VP8'); + }, + ); + + await withHarnessEnv( + { + LIVEKIT_ENABLE_SECOND_PUBLISHER: '1', + LIVEKIT_SECOND_PUBLISHER_CODEC: 'h264', + LIVEKIT_SECOND_PUBLISHER_SCREEN_CODEC: 'vp8', + }, + () => assert.throws(() => parseConfig(), /conflicts with LIVEKIT_SECOND_PUBLISHER_CODEC/), + ); + + await withHarnessEnv( + { + LIVEKIT_ENABLE_SECOND_PUBLISHER: '1', + LIVEKIT_EXPECT_SECOND_PUBLISHER_SCREEN_CODEC: 'h264', + LIVEKIT_EXPECT_SECONDARY_PUBLISHER_SCREEN_CODEC: 'vp8', + }, + () => assert.throws(() => parseConfig(), /conflicts with LIVEKIT_EXPECT_SECOND_PUBLISHER_SCREEN_CODEC/), + ); + }); + + test('external token setup derives identities, requires matching tokens, and redacts reports', async () => { + await withHarnessEnv( + { + LIVEKIT_PUBLISHER_TOKEN: tokenForSubject('publisher-subject'), + }, + () => assert.throws(() => parseConfig(), /both required/), + ); + + await withHarnessEnv( + { + LIVEKIT_ROOM: 'shared-token-room', + LIVEKIT_SCREEN_CODECS: 'vp8,h264', + LIVEKIT_ENABLE_SECOND_PUBLISHER: '1', + LIVEKIT_PUBLISHER_TOKEN: tokenForSubject('publisher-subject'), + LIVEKIT_SUBSCRIBER_TOKEN: tokenForSubject('subscriber-subject'), + }, + () => assert.throws(() => parseConfig(), /SECONDARY_PUBLISHER_TOKEN/), + ); + + await withHarnessEnv( + { + LIVEKIT_ROOM: 'shared-token-room', + LIVEKIT_SCREEN_CODECS: 'vp8,h264', + LIVEKIT_ENABLE_SECOND_PUBLISHER: '1', + LIVEKIT_PUBLISHER_TOKEN: tokenForSubject('publisher-subject'), + LIVEKIT_SUBSCRIBER_TOKEN: tokenForSubject('subscriber-subject'), + LIVEKIT_SECONDARY_PUBLISHER_TOKEN: tokenForSubject('secondary-subject'), + LIVEKIT_E2EE_KEY: 'secret-key', + }, + () => { + const config = parseConfig(); + assert.equal(config.externalTokens, true); + assert.equal(config.publisherIdentity, 'publisher-subject'); + assert.equal(config.subscriberIdentity, 'subscriber-subject'); + assert.equal(config.secondaryPublisherIdentity, 'secondary-subject'); + assert.deepEqual( + buildScenarioConfigs(config).map((scenario) => scenario.room), + ['shared-token-room', 'shared-token-room'], + ); + + const sanitized = sanitizeConfig(config); + assert.equal(sanitized.url, 'ws://localhost:7880'); + assert.equal(sanitized.apiSecret, ''); + assert.equal(sanitized.e2eeKey, ''); + assert.equal(sanitized.publisherToken, ''); + assert.equal(sanitized.subscriberToken, ''); + assert.equal(sanitized.secondaryPublisherToken, ''); + }, + ); + }); + + test('report sanitization strips URL credentials, query strings, and fragments', async () => { + await withHarnessEnv( + { + LIVEKIT_URL: 'wss://user:password@example.test:443/rtc?access_token=secret#fragment', + }, + () => { + const sanitized = sanitizeConfig(parseConfig()); + assert.equal(sanitized.url, 'wss://example.test/rtc'); + assert.doesNotMatch(JSON.stringify(sanitized), /user|password|access_token|secret|fragment/); + }, + ); + }); + + test('jwt subject and TCP target parsing catch malformed live setups early', () => { + assert.equal(jwtSubject(tokenForSubject('user_1_connection')), 'user_1_connection'); + assert.throws(() => jwtSubject('not-a-jwt'), /must be a JWT/); + assert.throws(() => jwtSubject('header.bad-json.signature'), /failed to decode/); + assert.throws(() => jwtSubject(tokenForSubject('')), /does not contain/); + + assert.deepEqual(liveKitTcpTarget('ws://localhost:7880'), {host: 'localhost', port: 7880}); + assert.deepEqual(liveKitTcpTarget('wss://[::1]/rtc'), {host: '::1', port: 443}); + assert.equal(liveKitApiUrl('ws://localhost:7880/rtc?token=secret'), 'http://localhost:7880'); + assert.equal(liveKitApiUrl('wss://livekit.example.test/rtc'), 'https://livekit.example.test'); + assert.throws(() => liveKitTcpTarget('https://localhost'), /must use ws/); + }); + + test('admin token has the room-scoped grant needed for server publishing validation', () => { + const token = createLiveKitAdminToken({apiKey: 'devkey', apiSecret: 'secret', room: 'room-a'}); + const payload = jwtPayload(token); + assert.equal(payload.iss, 'devkey'); + assert.equal(payload.video.room, 'room-a'); + assert.equal(payload.video.roomAdmin, true); + assert.equal(payload.video.roomJoin, undefined); + }); + + test('scenario room names are stable and bounded for generated-token suites', () => { + assert.equal(scenarioRoomName('room', 'codec-vp8', 1, false), 'room'); + assert.equal(scenarioRoomName('room', 'codec:vP8 with H264', 2, false), 'room-codec-vP8-with-H264'); + assert.equal(scenarioRoomName('room', 'codec-vp8', 2, true), 'room'); + + const longRoom = scenarioRoomName('r'.repeat(120), 'codec-h264-with-hevc', 2, false); + assert.equal(longRoom.length, 128); + assert.ok(longRoom.startsWith('r'.repeat(120))); + }); + + test('strict report checks fail when the strict window has too few samples to measure gaps', async () => { + await withHarnessEnv( + { + LIVEKIT_MIN_RECEIVED_FPS_RATIO: '0', + LIVEKIT_MAX_FRAME_GAP_MS: '250', + LIVEKIT_MAX_AUDIO_FRAME_GAP_MS: '250', + LIVEKIT_MAX_AV_DRIFT_MS: '0', + }, + () => { + const config = parseConfig(); + const report = strictReportFor(config, { + videoFrameTimes: [1_250], + audioFrameTimes: [1_300], + }); + assert.equal(report.metrics.strictVideoFrames, 1); + assert.equal(report.metrics.strictAudioFrames, 1); + assert.match(strictFailures(report, config).join('\n'), /video frame gap unavailable/); + assert.match(strictFailures(report, config).join('\n'), /audio frame gap unavailable/); + }, + ); + }); + + test('strict report checks use only samples inside the measured window', async () => { + await withHarnessEnv( + { + LIVEKIT_MIN_RECEIVED_FPS_RATIO: '0', + LIVEKIT_MAX_FRAME_GAP_MS: '250', + LIVEKIT_MAX_AUDIO_FRAME_GAP_MS: '250', + LIVEKIT_MAX_AV_DRIFT_MS: '0', + }, + () => { + const config = parseConfig(); + const report = strictReportFor(config, { + videoFrameTimes: [500, 1_050, 1_200, 2_500], + audioFrameTimes: [600, 1_100, 1_240, 2_600], + }); + assert.equal(report.metrics.strictVideoFrames, 2); + assert.equal(report.metrics.strictAudioFrames, 2); + assert.equal(report.metrics.maxVideoFrameGapMs, 150); + assert.equal(report.metrics.maxAudioFrameGapMs, 140); + assert.deepEqual(strictFailures(report, config), []); + }, + ); + }); + + test('strict screen report ignores camera video callbacks for resolution and drift', async () => { + await withHarnessEnv( + { + LIVEKIT_ENABLE_CAMERA: '1', + LIVEKIT_MIN_RECEIVED_FPS_RATIO: '0', + LIVEKIT_MAX_FRAME_GAP_MS: '250', + LIVEKIT_MAX_AUDIO_FRAME_GAP_MS: '250', + LIVEKIT_MAX_AV_DRIFT_MS: '80', + }, + () => { + const config = parseConfig(); + const report = strictReportFor(config, { + videoFrameRecords: [ + { + atMs: 1_010, + identity: config.publisherIdentity, + kind: 'video', + source: 'screen_share', + trackName: 'screen', + trackSid: 'TR_screen', + width: 320, + height: 180, + }, + { + atMs: 1_025, + identity: config.publisherIdentity, + kind: 'video', + source: 'camera', + trackName: 'camera', + trackSid: 'TR_camera', + width: 480, + height: 360, + }, + { + atMs: 1_080, + identity: config.publisherIdentity, + kind: 'video', + source: 'screen_share', + trackName: 'screen', + trackSid: 'TR_screen', + width: 320, + height: 180, + }, + { + atMs: 1_220, + identity: config.publisherIdentity, + kind: 'video', + source: 'camera', + trackName: 'camera', + trackSid: 'TR_camera', + width: 480, + height: 360, + }, + ], + audioFrameTimes: [1_040, 1_100], + }); + + assert.equal(report.metrics.videoCallbacks, 4); + assert.equal(report.metrics.strictVideoFrames, 2); + assert.deepEqual(report.metrics.videoResolutionCounts, {'320x180': 2}); + assert.equal(report.metrics.videoResolutionMismatchCount, 0); + assert.equal(report.metrics.avDriftMs, 20); + assert.deepEqual(strictFailures(report, config), []); + }, + ); + }); + + test('strict packet loss gate uses loss deltas inside the strict window', async () => { + await withHarnessEnv( + { + LIVEKIT_MIN_RECEIVED_FPS_RATIO: '0', + LIVEKIT_MAX_FRAME_GAP_MS: '0', + LIVEKIT_MAX_AUDIO_FRAME_GAP_MS: '0', + LIVEKIT_MAX_AV_DRIFT_MS: '0', + LIVEKIT_MAX_PACKET_LOSS: '0', + }, + () => { + const config = parseConfig(); + const report = strictReportFor(config, { + videoFrameTimes: [1_100, 1_200], + subscriberStatsSamples: [ + {atMs: 900, payload: {outbound: [], inbound: [{kind: 'video', packetsLost: 4}]}}, + {atMs: 1_500, payload: {outbound: [], inbound: [{kind: 'video', packetsLost: 4}]}}, + ], + }); + assert.equal(report.metrics.maxObservedPacketLoss, 4); + assert.equal(report.metrics.maxObservedPacketLossDelta, 0); + assert.deepEqual(strictFailures(report, config), []); + + const failingReport = strictReportFor(config, { + videoFrameTimes: [1_100, 1_200], + subscriberStatsSamples: [ + {atMs: 900, payload: {outbound: [], inbound: [{kind: 'video', packetsLost: 4}]}}, + {atMs: 1_500, payload: {outbound: [], inbound: [{kind: 'video', packetsLost: 5}]}}, + ], + }); + assert.equal(failingReport.metrics.maxObservedPacketLossDelta, 1); + assert.match(strictFailures(failingReport, config).join('\n'), /packet loss delta 1/); + }, + ); + }); + + test('server publishing checks cover primary and secondary screen, audio, and camera tracks', async () => { + await withHarnessEnv( + { + LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1', + LIVEKIT_ENABLE_SECOND_PUBLISHER: '1', + LIVEKIT_ENABLE_SCREEN_AUDIO: '1', + LIVEKIT_ENABLE_CAMERA: '1', + }, + () => { + const config = parseConfig(); + const serverState = { + participants: [ + { + identity: config.publisherIdentity, + tracks: [ + {type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false, sid: 'TR_V1'}, + {type: 'AUDIO', source: 'MICROPHONE', mimeType: 'audio/red', muted: false, sid: 'TR_A1'}, + {type: 'AUDIO', source: 'SCREEN_SHARE_AUDIO', mimeType: 'audio/red', muted: false, sid: 'TR_A2'}, + {type: 'VIDEO', source: 'CAMERA', muted: false, sid: 'TR_V2'}, + ], + }, + { + identity: config.secondaryPublisherIdentity, + tracks: [ + {type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false, sid: 'TR_V3'}, + {type: 'AUDIO', source: 'MICROPHONE', mimeType: 'audio/red', muted: false, sid: 'TR_A3'}, + {type: 'AUDIO', source: 'SCREEN_SHARE_AUDIO', mimeType: 'audio/red', muted: false, sid: 'TR_A4'}, + ], + }, + ], + error: null, + }; + const checks = serverChecksFor(config, serverState); + assert.equal(checks.length, 8); + assert.deepEqual( + checks.map((check) => [check.name, check.pass]), + [ + ['server publishing API participants listed', true], + ['server sees publisher screenshare publication', true], + ['server sees publisher microphone publication', true], + ['server sees publisher screen-share audio publication', true], + ['server sees publisher camera publication', true], + ['server sees secondary publisher screenshare publication', true], + ['server sees secondary publisher microphone publication', true], + ['server sees secondary publisher screen-share audio publication', true], + ], + ); + }, + ); + }); + + test('server publishing checks fail closed on missing server tracks', async () => { + await withHarnessEnv({LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1', LIVEKIT_ENABLE_SCREEN_AUDIO: '1'}, () => { + const config = parseConfig(); + const checks = serverChecksFor(config, { + participants: [ + { + identity: config.publisherIdentity, + tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false}], + }, + ], + error: null, + }); + assert.equal(findCheck(checks, 'server sees publisher screenshare publication').pass, true); + assert.equal(findCheck(checks, 'server sees publisher screen-share audio publication').pass, false); + }); + }); + + test('server publishing checks reject wrong participant identity', async () => { + await withHarnessEnv({LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1'}, () => { + const config = parseConfig(); + const checks = serverChecksFor(config, { + participants: [ + { + identity: config.subscriberIdentity, + tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false}], + }, + ], + error: null, + }); + assert.equal(findCheck(checks, 'server publishing API participants listed').pass, true); + assert.equal(findCheck(checks, 'server sees publisher screenshare publication').pass, false); + }); + }); + + test('server publishing checks reject wrong kind, source, and screen mime', async () => { + await withHarnessEnv({LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1', LIVEKIT_EXPECT_SCREEN_CODEC: 'h264'}, () => { + const config = parseConfig(); + const wrongKindChecks = serverChecksFor(config, { + participants: [ + { + identity: config.publisherIdentity, + tracks: [{type: 'AUDIO', source: 'SCREEN_SHARE', mimeType: 'video/H264', muted: false}], + }, + ], + error: null, + }); + assert.equal(findCheck(wrongKindChecks, 'server sees publisher screenshare publication').pass, false); + + const wrongSourceChecks = serverChecksFor(config, { + participants: [ + { + identity: config.publisherIdentity, + tracks: [{type: 'VIDEO', source: 'CAMERA', mimeType: 'video/H264', muted: false}], + }, + ], + error: null, + }); + assert.equal(findCheck(wrongSourceChecks, 'server sees publisher screenshare publication').pass, false); + + const wrongMimeChecks = serverChecksFor(config, { + participants: [ + { + identity: config.publisherIdentity, + tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false}], + }, + ], + error: null, + }); + assert.equal(findCheck(wrongMimeChecks, 'server sees publisher screenshare publication').pass, false); + + const matchingChecks = serverChecksFor(config, { + participants: [ + { + identity: config.publisherIdentity, + tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mime_type: 'video/h264', muted: false}], + }, + ], + error: null, + }); + assert.equal(findCheck(matchingChecks, 'server sees publisher screenshare publication').pass, true); + }); + }); + + test('server publishing checks reject muted or missing mute state on expected tracks', async () => { + await withHarnessEnv({LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1'}, () => { + const config = parseConfig(); + const mutedChecks = serverChecksFor(config, { + participants: [ + { + identity: config.publisherIdentity, + tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: true}], + }, + ], + error: null, + }); + assert.equal(findCheck(mutedChecks, 'server sees publisher screenshare publication').pass, false); + + const missingMutedChecks = serverChecksFor(config, { + participants: [ + { + identity: config.publisherIdentity, + tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8'}], + }, + ], + error: null, + }); + assert.equal(findCheck(missingMutedChecks, 'server sees publisher screenshare publication').pass, false); + }); + }); + + test('server publishing checks handle protobuf numeric enums without accepting data tracks as video', async () => { + await withHarnessEnv({LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1'}, () => { + const config = parseConfig(); + const dataTrackChecks = serverChecksFor(config, { + participants: [ + { + identity: config.publisherIdentity, + tracks: [{type: 2, source: 3, mimeType: 'video/VP8', muted: false}], + }, + ], + error: null, + }); + assert.equal(findCheck(dataTrackChecks, 'server sees publisher screenshare publication').pass, false); + + const videoTrackChecks = serverChecksFor(config, { + participants: [ + { + identity: config.publisherIdentity, + tracks: [{type: 1, source: 3, mimeType: 'video/VP8', muted: false}], + }, + ], + error: null, + }); + assert.equal(findCheck(videoTrackChecks, 'server sees publisher screenshare publication').pass, true); + }); + }); + + test('server publishing checks fail closed on missing secondary, audio, camera, and screen-audio tracks', async () => { + await withHarnessEnv( + { + LIVEKIT_VALIDATE_SERVER_PUBLISHING: '1', + LIVEKIT_ENABLE_SECOND_PUBLISHER: '1', + LIVEKIT_ENABLE_SCREEN_AUDIO: '1', + LIVEKIT_ENABLE_CAMERA: '1', + }, + () => { + const config = parseConfig(); + const checks = serverChecksFor(config, { + participants: [ + { + identity: config.publisherIdentity, + tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false}], + }, + { + identity: config.secondaryPublisherIdentity, + tracks: [{type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false}], + }, + ], + error: null, + }); + assert.equal(findCheck(checks, 'server sees publisher screenshare publication').pass, true); + assert.equal(findCheck(checks, 'server sees publisher microphone publication').pass, false); + assert.equal(findCheck(checks, 'server sees publisher screen-share audio publication').pass, false); + assert.equal(findCheck(checks, 'server sees publisher camera publication').pass, false); + assert.equal(findCheck(checks, 'server sees secondary publisher screenshare publication').pass, true); + assert.equal(findCheck(checks, 'server sees secondary publisher microphone publication').pass, false); + assert.equal(findCheck(checks, 'server sees secondary publisher screen-share audio publication').pass, false); + }, + ); + }); + + test('strict server publishing validation does not pass open on Twirp errors', async () => { + await withHarnessEnv({LIVEKIT_HARNESS_STRICT: '1'}, () => { + const config = parseConfig(); + const checks = serverChecksFor(config, { + participants: [ + { + identity: config.publisherIdentity, + tracks: [ + {type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false}, + {type: 'AUDIO', source: 'MICROPHONE', mimeType: 'audio/red', muted: false}, + {type: 'AUDIO', source: 'SCREEN_SHARE_AUDIO', mimeType: 'audio/red', muted: false}, + ], + }, + { + identity: config.secondaryPublisherIdentity, + tracks: [ + {type: 'VIDEO', source: 'SCREEN_SHARE', mimeType: 'video/VP8', muted: false}, + {type: 'AUDIO', source: 'MICROPHONE', mimeType: 'audio/red', muted: false}, + {type: 'AUDIO', source: 'SCREEN_SHARE_AUDIO', mimeType: 'audio/red', muted: false}, + ], + }, + ], + error: 'RoomService.ListParticipants failed with HTTP 401', + }); + assert.equal(config.validateServerPublishing, true); + assert.equal(findCheck(checks, 'server publishing API participants listed').pass, false); + assert.equal( + checks.every((check) => check.pass), + false, + ); + }); + }); +}); diff --git a/fluxer_desktop/native/webrtc-sender/src/audio.rs b/fluxer_desktop/native/webrtc-sender/src/audio.rs new file mode 100644 index 0000000..3ce18be --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/src/audio.rs @@ -0,0 +1,1057 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::events::{JsonValue, json_object}; +#[cfg(feature = "publisher")] +use livekit::AudioProcessingOptions; + +pub const MIN_VOLUME: f64 = 0.0; +pub const MAX_VOLUME: f64 = 2.0; + +pub fn clamp_volume(volume: f64) -> f64 { + if !volume.is_finite() { + return 1.0; + } + volume.clamp(MIN_VOLUME, MAX_VOLUME) +} + +pub fn is_muted_volume(volume: f64) -> bool { + clamp_volume(volume) <= f64::EPSILON +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum AudioDeviceRole { + Default, + Communications, + Endpoint, +} + +impl AudioDeviceRole { + fn as_str(&self) -> &'static str { + match self { + Self::Default => "default", + Self::Communications => "communications", + Self::Endpoint => "endpoint", + } + } +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct AudioOutputDevice { + pub device_id: String, + pub label: String, + pub is_default: bool, + pub role: AudioDeviceRole, + pub endpoint_label: String, + pub is_default_route: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct AudioInputDevice { + pub device_id: String, + pub label: String, + pub is_default: bool, + pub role: AudioDeviceRole, + pub endpoint_label: String, + pub is_default_route: bool, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +struct ShapedAudioDevice { + device_id: String, + label: String, + is_default: bool, + role: AudioDeviceRole, + endpoint_label: String, + is_default_route: bool, +} + +const DEFAULT_AUDIO_ROUTE_LABEL: &str = "Default"; + +fn default_output_device() -> AudioOutputDevice { + AudioOutputDevice { + device_id: "default".to_string(), + label: DEFAULT_AUDIO_ROUTE_LABEL.to_string(), + is_default: true, + role: AudioDeviceRole::Default, + endpoint_label: DEFAULT_AUDIO_ROUTE_LABEL.to_string(), + is_default_route: true, + } +} + +fn default_input_device() -> AudioInputDevice { + AudioInputDevice { + device_id: "default".to_string(), + label: DEFAULT_AUDIO_ROUTE_LABEL.to_string(), + is_default: true, + role: AudioDeviceRole::Default, + endpoint_label: DEFAULT_AUDIO_ROUTE_LABEL.to_string(), + is_default_route: true, + } +} + +pub fn output_device_json(device: &AudioOutputDevice) -> String { + json_object(&[ + ("deviceId", JsonValue::Str(device.device_id.clone())), + ("label", JsonValue::Str(device.label.clone())), + ( + "isDefault", + JsonValue::Raw(if device.is_default { "true" } else { "false" }.to_string()), + ), + ("role", JsonValue::Str(device.role.as_str().to_string())), + ( + "endpointLabel", + JsonValue::Str(device.endpoint_label.clone()), + ), + ( + "isDefaultRoute", + JsonValue::Raw( + if device.is_default_route { + "true" + } else { + "false" + } + .to_string(), + ), + ), + ]) +} + +pub fn input_device_json(device: &AudioInputDevice) -> String { + json_object(&[ + ("deviceId", JsonValue::Str(device.device_id.clone())), + ("label", JsonValue::Str(device.label.clone())), + ( + "isDefault", + JsonValue::Raw(if device.is_default { "true" } else { "false" }.to_string()), + ), + ("role", JsonValue::Str(device.role.as_str().to_string())), + ( + "endpointLabel", + JsonValue::Str(device.endpoint_label.clone()), + ), + ( + "isDefaultRoute", + JsonValue::Raw( + if device.is_default_route { + "true" + } else { + "false" + } + .to_string(), + ), + ), + ]) +} + +pub fn output_devices_json(devices: &[AudioOutputDevice]) -> String { + let items: Vec = devices.iter().map(output_device_json).collect(); + let mut out = String::from("["); + for (i, item) in items.iter().enumerate() { + if i > 0 { + out.push(','); + } + out.push_str(item); + } + out.push(']'); + out +} + +pub fn default_output_devices_json() -> String { + output_devices_json(&[default_output_device()]) +} + +pub fn input_devices_json(devices: &[AudioInputDevice]) -> String { + let items: Vec = devices.iter().map(input_device_json).collect(); + let mut out = String::from("["); + for (i, item) in items.iter().enumerate() { + if i > 0 { + out.push(','); + } + out.push_str(item); + } + out.push(']'); + out +} + +pub fn default_input_devices_json() -> String { + input_devices_json(&[default_input_device()]) +} + +pub fn shape_output_devices(raw: &[(String, String, usize)]) -> Vec { + shape_audio_devices(raw) + .into_iter() + .map(|device| AudioOutputDevice { + device_id: device.device_id, + label: device.label, + is_default: device.is_default, + role: device.role, + endpoint_label: device.endpoint_label, + is_default_route: device.is_default_route, + }) + .collect() +} + +pub fn shape_input_devices(raw: &[(String, String, usize)]) -> Vec { + shape_audio_devices(raw) + .into_iter() + .map(|device| AudioInputDevice { + device_id: device.device_id, + label: device.label, + is_default: device.is_default, + role: device.role, + endpoint_label: device.endpoint_label, + is_default_route: device.is_default_route, + }) + .collect() +} + +pub const MAX_PLATFORM_AUDIO_DEVICES: usize = 64; +pub const SHAPED_AUDIO_DEVICES_MAX: usize = MAX_PLATFORM_AUDIO_DEVICES + 1; + +const _: () = assert!(SHAPED_AUDIO_DEVICES_MAX > MAX_PLATFORM_AUDIO_DEVICES); + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum PlayoutSwitchPlan { + ColdSelect, + HotSwap, +} + +pub fn bounded_audio_device_count(reported: i16) -> Result { + if reported < 0 { + return Err(format!( + "ADM reported negative audio device count: {reported}" + )); + } + let count = (reported as usize).min(MAX_PLATFORM_AUDIO_DEVICES); + assert!(count <= MAX_PLATFORM_AUDIO_DEVICES); + Ok(count) +} + +pub fn playout_switch_plan( + platform_playout_active: bool, + playout_initialized: bool, +) -> PlayoutSwitchPlan { + if platform_playout_active && playout_initialized { + return PlayoutSwitchPlan::HotSwap; + } + PlayoutSwitchPlan::ColdSelect +} + +pub fn resolve_playout_device_guid( + requested: &str, + raw: &[(String, String, usize)], +) -> Result { + let requested = requested.trim(); + if requested.is_empty() || requested == "default" { + return find_default_device_id(raw) + .ok_or_else(|| "no audio output devices available".to_string()); + } + let found = raw + .iter() + .find(|(id, _, _)| id.trim() == requested) + .map(|(id, _, _)| id.trim().to_string()); + found.ok_or_else(|| format!("audio output device not found: {requested}")) +} + +fn shape_audio_devices(raw: &[(String, String, usize)]) -> Vec { + let mut devices: Vec = Vec::with_capacity(SHAPED_AUDIO_DEVICES_MAX); + for (id, label, index) in raw.iter().filter(|(id, _, _)| !id.trim().is_empty()) { + if devices.len() == SHAPED_AUDIO_DEVICES_MAX { + break; + } + let shaped = shape_audio_device(id, label, *index); + let duplicate = devices + .iter() + .any(|existing| existing.device_id == shaped.device_id); + if !duplicate { + devices.push(shaped); + } + } + if !devices.iter().any(|device| device.is_default_route) + && devices.len() < SHAPED_AUDIO_DEVICES_MAX + && let Some(default_endpoint) = devices.iter().find(|device| { + device.is_default + && device.role == AudioDeviceRole::Endpoint + && !device.endpoint_label.is_empty() + }) + { + devices.insert( + 0, + ShapedAudioDevice { + device_id: "default".to_string(), + label: default_endpoint.endpoint_label.clone(), + is_default: true, + role: AudioDeviceRole::Default, + endpoint_label: default_endpoint.endpoint_label.clone(), + is_default_route: true, + }, + ); + } + assert!(devices.len() <= SHAPED_AUDIO_DEVICES_MAX); + assert!( + devices + .iter() + .filter(|device| device.is_default_route) + .count() + <= 1 + ); + devices +} + +fn shape_audio_device(id: &str, raw_label: &str, index: usize) -> ShapedAudioDevice { + let id = id.trim(); + assert!(!id.is_empty()); + let label = strip_usb_hardware_id_suffix(raw_label); + if let Some(endpoint_label) = default_route_endpoint_label(id, &label) { + return ShapedAudioDevice { + device_id: "default".to_string(), + label: endpoint_label.clone(), + is_default: true, + role: AudioDeviceRole::Default, + endpoint_label, + is_default_route: true, + }; + } + if let Some(endpoint_label) = communications_route_endpoint_label(id, &label) { + return ShapedAudioDevice { + device_id: id.to_string(), + label: endpoint_label.clone(), + is_default: index == 0, + role: AudioDeviceRole::Communications, + endpoint_label, + is_default_route: false, + }; + } + ShapedAudioDevice { + device_id: id.to_string(), + label: label.clone(), + is_default: index == 0, + role: AudioDeviceRole::Endpoint, + endpoint_label: label, + is_default_route: false, + } +} + +fn default_route_endpoint_label(id: &str, label: &str) -> Option { + let wrapped = strip_windows_role_prefix(label, "Default") + .or_else(|| strip_adm_default_wrapper(label)) + .or_else(|| strip_pulse_default_prefix(label)); + if let Some(endpoint_label) = wrapped { + return Some(strip_usb_hardware_id_suffix(&endpoint_label)); + } + if id.eq_ignore_ascii_case("default") { + if label.eq_ignore_ascii_case("default") { + return Some(String::new()); + } + return Some(label.to_string()); + } + None +} + +fn communications_route_endpoint_label(id: &str, label: &str) -> Option { + let wrapped = strip_windows_role_prefix(label, "Communications") + .or_else(|| strip_windows_role_prefix(label, "Communication")); + if let Some(endpoint_label) = wrapped { + return Some(strip_usb_hardware_id_suffix(&endpoint_label)); + } + if id.eq_ignore_ascii_case("communications") { + return Some(label.to_string()); + } + None +} + +pub fn is_default_route_device(id: &str, label: &str) -> bool { + let label = strip_usb_hardware_id_suffix(label); + default_route_endpoint_label(id.trim(), &label).is_some() +} + +pub fn find_default_device_id(raw: &[(String, String, usize)]) -> Option { + let default_route = raw + .iter() + .find(|(id, label, _)| !id.trim().is_empty() && is_default_route_device(id, label)); + if let Some((id, _, _)) = default_route { + return Some(id.trim().to_string()); + } + raw.iter() + .find(|(id, _, _)| !id.trim().is_empty()) + .map(|(id, _, _)| id.trim().to_string()) +} + +fn strip_windows_role_prefix(label: &str, role: &str) -> Option { + let (prefix, endpoint) = label.trim().split_once('-')?; + if prefix.trim().eq_ignore_ascii_case(role) { + let endpoint = endpoint.trim(); + if !endpoint.is_empty() { + return Some(endpoint.to_string()); + } + } + None +} + +fn strip_adm_default_wrapper(label: &str) -> Option { + const WRAPPER_PREFIX: &str = "default ("; + let trimmed = label.trim(); + if trimmed.len() < WRAPPER_PREFIX.len() + 1 { + return None; + } + if !trimmed.is_char_boundary(WRAPPER_PREFIX.len()) { + return None; + } + if !trimmed[..WRAPPER_PREFIX.len()].eq_ignore_ascii_case(WRAPPER_PREFIX) { + return None; + } + if !trimmed.ends_with(')') { + return None; + } + let inner = trimmed[WRAPPER_PREFIX.len()..trimmed.len() - 1].trim(); + if inner.is_empty() { + return None; + } + Some(inner.to_string()) +} + +fn strip_pulse_default_prefix(label: &str) -> Option { + let inner = label.trim().strip_prefix("default: ")?.trim(); + if inner.is_empty() { + return None; + } + Some(inner.to_string()) +} + +fn strip_usb_hardware_id_suffix(label: &str) -> String { + let trimmed = label.trim(); + let Some(open_index) = trimmed.rfind('(') else { + return trimmed.to_string(); + }; + if !trimmed.ends_with(')') || open_index + 1 >= trimmed.len() { + return trimmed.to_string(); + } + let suffix = &trimmed[open_index + 1..trimmed.len() - 1]; + if suffix.len() != 9 { + return trimmed.to_string(); + } + let suffix_bytes = suffix.as_bytes(); + if suffix_bytes.get(4) != Some(&b':') { + return trimmed.to_string(); + } + let is_hardware_id = suffix_bytes + .iter() + .enumerate() + .all(|(index, byte)| index == 4 || byte.is_ascii_hexdigit()); + if !is_hardware_id { + return trimmed.to_string(); + } + trimmed[..open_index].trim_end().to_string() +} + +pub const MICROPHONE_MAX_BITRATE_BPS_FLOOR: u64 = 8_000; +pub const MICROPHONE_MAX_BITRATE_BPS_CAP: u64 = 510_000; + +pub fn normalize_microphone_max_bitrate_bps( + max_bitrate_bps: Option, +) -> Result, String> { + let Some(raw) = max_bitrate_bps else { + return Ok(None); + }; + if !raw.is_finite() { + return Err("microphone maxBitrateBps must be finite".to_string()); + } + if raw <= 0.0 { + return Err("microphone maxBitrateBps must be positive".to_string()); + } + if raw.fract() != 0.0 { + return Err("microphone maxBitrateBps must be an integer".to_string()); + } + let clamped = (raw as u64).clamp( + MICROPHONE_MAX_BITRATE_BPS_FLOOR, + MICROPHONE_MAX_BITRATE_BPS_CAP, + ); + assert!(clamped >= MICROPHONE_MAX_BITRATE_BPS_FLOOR); + assert!(clamped <= MICROPHONE_MAX_BITRATE_BPS_CAP); + Ok(Some(clamped)) +} + +#[cfg(feature = "publisher")] +pub fn processing_options( + echo_cancellation: Option, + noise_suppression: Option, + auto_gain_control: Option, +) -> AudioProcessingOptions { + let mut options = AudioProcessingOptions::default(); + if let Some(enabled) = echo_cancellation { + options.echo_cancellation = enabled; + } + if let Some(enabled) = noise_suppression { + options.noise_suppression = enabled; + } + if let Some(enabled) = auto_gain_control { + options.auto_gain_control = enabled; + } + options +} + +pub const DEEP_FILTER_NOISE_REDUCTION_LEVEL_MIN: f64 = 0.0; +pub const DEEP_FILTER_NOISE_REDUCTION_LEVEL_MAX: f64 = 100.0; + +const _: () = + assert!(DEEP_FILTER_NOISE_REDUCTION_LEVEL_MIN < DEEP_FILTER_NOISE_REDUCTION_LEVEL_MAX); + +pub fn clamp_deep_filter_noise_reduction_level(level: f64) -> f64 { + if !level.is_finite() { + return DEEP_FILTER_NOISE_REDUCTION_LEVEL_MAX; + } + let clamped = level.clamp( + DEEP_FILTER_NOISE_REDUCTION_LEVEL_MIN, + DEEP_FILTER_NOISE_REDUCTION_LEVEL_MAX, + ); + assert!(clamped >= DEEP_FILTER_NOISE_REDUCTION_LEVEL_MIN); + assert!(clamped <= DEEP_FILTER_NOISE_REDUCTION_LEVEL_MAX); + clamped +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct MicrophoneApmIntent { + pub echo_cancellation: Option, + pub noise_suppression: Option, + pub auto_gain_control: Option, +} + +pub fn resolve_microphone_apm_intent( + echo_cancellation: Option, + noise_suppression: Option, + auto_gain_control: Option, + deep_filter_requested: bool, + native_deep_filter_available: bool, +) -> MicrophoneApmIntent { + if !deep_filter_requested { + return MicrophoneApmIntent { + echo_cancellation, + noise_suppression, + auto_gain_control, + }; + } + if native_deep_filter_available { + return MicrophoneApmIntent { + echo_cancellation, + noise_suppression: Some(false), + auto_gain_control: Some(false), + }; + } + MicrophoneApmIntent { + echo_cancellation, + noise_suppression: Some(true), + auto_gain_control: Some(true), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn clamp_volume_holds_the_contract_range() { + assert_eq!(clamp_volume(1.0), 1.0); + assert_eq!(clamp_volume(0.0), 0.0); + assert_eq!(clamp_volume(2.0), 2.0); + assert_eq!(clamp_volume(-1.0), 0.0); + assert_eq!(clamp_volume(5.0), 2.0); + assert_eq!(clamp_volume(2.5), 2.0); + assert_eq!(clamp_volume(0.5), 0.5); + assert_eq!(clamp_volume(1.75), 1.75); + } + + #[test] + fn clamp_volume_maps_non_finite_to_unity() { + assert_eq!(clamp_volume(f64::NAN), 1.0); + assert_eq!(clamp_volume(f64::INFINITY), 1.0); + assert_eq!(clamp_volume(f64::NEG_INFINITY), 1.0); + } + + #[test] + fn microphone_max_bitrate_passes_through_in_range_values() { + assert_eq!(normalize_microphone_max_bitrate_bps(None), Ok(None)); + assert_eq!( + normalize_microphone_max_bitrate_bps(Some(8_000.0)), + Ok(Some(8_000)) + ); + assert_eq!( + normalize_microphone_max_bitrate_bps(Some(96_000.0)), + Ok(Some(96_000)) + ); + assert_eq!( + normalize_microphone_max_bitrate_bps(Some(510_000.0)), + Ok(Some(510_000)) + ); + } + + #[test] + fn microphone_max_bitrate_clamps_to_floor_and_cap() { + assert_eq!( + normalize_microphone_max_bitrate_bps(Some(1.0)), + Ok(Some(MICROPHONE_MAX_BITRATE_BPS_FLOOR)) + ); + assert_eq!( + normalize_microphone_max_bitrate_bps(Some(7_999.0)), + Ok(Some(MICROPHONE_MAX_BITRATE_BPS_FLOOR)) + ); + assert_eq!( + normalize_microphone_max_bitrate_bps(Some(510_001.0)), + Ok(Some(MICROPHONE_MAX_BITRATE_BPS_CAP)) + ); + assert_eq!( + normalize_microphone_max_bitrate_bps(Some(1_000_000.0)), + Ok(Some(MICROPHONE_MAX_BITRATE_BPS_CAP)) + ); + } + + #[test] + fn microphone_max_bitrate_rejects_invalid_values() { + assert!(normalize_microphone_max_bitrate_bps(Some(f64::NAN)).is_err()); + assert!(normalize_microphone_max_bitrate_bps(Some(f64::INFINITY)).is_err()); + assert!(normalize_microphone_max_bitrate_bps(Some(f64::NEG_INFINITY)).is_err()); + assert!(normalize_microphone_max_bitrate_bps(Some(0.0)).is_err()); + assert!(normalize_microphone_max_bitrate_bps(Some(-64_000.0)).is_err()); + assert!(normalize_microphone_max_bitrate_bps(Some(64_000.5)).is_err()); + } + + #[test] + fn is_muted_only_at_zero() { + assert!(is_muted_volume(0.0)); + assert!(is_muted_volume(-1.0)); + assert!(!is_muted_volume(0.01)); + assert!(!is_muted_volume(1.0)); + assert!(!is_muted_volume(2.0)); + } + + #[test] + fn bounded_audio_device_count_rejects_negative_counts() { + assert!(bounded_audio_device_count(-1).is_err()); + assert!(bounded_audio_device_count(i16::MIN).is_err()); + } + + #[test] + fn bounded_audio_device_count_clamps_to_the_platform_cap() { + assert_eq!(bounded_audio_device_count(0), Ok(0)); + assert_eq!(bounded_audio_device_count(3), Ok(3)); + assert_eq!( + bounded_audio_device_count((MAX_PLATFORM_AUDIO_DEVICES + 1) as i16), + Ok(MAX_PLATFORM_AUDIO_DEVICES) + ); + assert_eq!( + bounded_audio_device_count(i16::MAX), + Ok(MAX_PLATFORM_AUDIO_DEVICES) + ); + } + + #[test] + fn playout_switch_plan_only_hot_swaps_live_platform_playout() { + assert_eq!( + playout_switch_plan(false, false), + PlayoutSwitchPlan::ColdSelect + ); + assert_eq!( + playout_switch_plan(true, false), + PlayoutSwitchPlan::ColdSelect + ); + assert_eq!( + playout_switch_plan(false, true), + PlayoutSwitchPlan::ColdSelect + ); + assert_eq!(playout_switch_plan(true, true), PlayoutSwitchPlan::HotSwap); + } + + #[test] + fn resolve_playout_device_guid_resolves_default_to_the_route_guid() { + let raw = vec![ + ( + "default".to_string(), + "default (Studio Display)".to_string(), + 0, + ), + ("speaker-guid".to_string(), "Studio Display".to_string(), 1), + ]; + + assert_eq!( + resolve_playout_device_guid("", &raw), + Ok("default".to_string()) + ); + assert_eq!( + resolve_playout_device_guid("default", &raw), + Ok("default".to_string()) + ); + } + + #[test] + fn resolve_playout_device_guid_rejects_default_when_no_devices_exist() { + let raw: Vec<(String, String, usize)> = vec![]; + + assert!(resolve_playout_device_guid("default", &raw).is_err()); + } + + #[test] + fn resolve_playout_device_guid_prevalidates_explicit_guids() { + let raw = vec![ + ("speaker-guid".to_string(), "Studio Display".to_string(), 0), + ("hdmi-guid".to_string(), "HDMI".to_string(), 1), + ]; + + assert_eq!( + resolve_playout_device_guid(" speaker-guid ", &raw), + Ok("speaker-guid".to_string()) + ); + assert!(resolve_playout_device_guid("missing-guid", &raw).is_err()); + } + + #[test] + fn output_device_json_locks_the_contract_shape() { + let dev = AudioOutputDevice { + device_id: "{0.0.0.00000000}.{guid}".into(), + label: "Speakers (Realtek)".into(), + is_default: true, + role: AudioDeviceRole::Endpoint, + endpoint_label: "Speakers (Realtek)".into(), + is_default_route: false, + }; + assert_eq!( + output_device_json(&dev), + "{\"deviceId\":\"{0.0.0.00000000}.{guid}\",\"label\":\"Speakers (Realtek)\",\"isDefault\":true,\"role\":\"endpoint\",\"endpointLabel\":\"Speakers (Realtek)\",\"isDefaultRoute\":false}" + ); + } + + #[test] + fn output_devices_json_array() { + assert_eq!(output_devices_json(&[]), "[]"); + let list = vec![ + AudioOutputDevice { + device_id: "a".into(), + label: "A".into(), + is_default: true, + role: AudioDeviceRole::Default, + endpoint_label: "A".into(), + is_default_route: true, + }, + AudioOutputDevice { + device_id: "b".into(), + label: "B".into(), + is_default: false, + role: AudioDeviceRole::Endpoint, + endpoint_label: "B".into(), + is_default_route: false, + }, + ]; + assert_eq!( + output_devices_json(&list), + "[{\"deviceId\":\"a\",\"label\":\"A\",\"isDefault\":true,\"role\":\"default\",\"endpointLabel\":\"A\",\"isDefaultRoute\":true},{\"deviceId\":\"b\",\"label\":\"B\",\"isDefault\":false,\"role\":\"endpoint\",\"endpointLabel\":\"B\",\"isDefaultRoute\":false}]" + ); + } + + #[test] + fn default_output_devices_json_exposes_a_default_route_placeholder() { + assert_eq!( + default_output_devices_json(), + "[{\"deviceId\":\"default\",\"label\":\"Default\",\"isDefault\":true,\"role\":\"default\",\"endpointLabel\":\"Default\",\"isDefaultRoute\":true}]" + ); + } + + #[test] + fn input_devices_json_array() { + assert_eq!(input_devices_json(&[]), "[]"); + let list = vec![ + AudioInputDevice { + device_id: "mic-a".into(), + label: "Built-in Microphone".into(), + is_default: true, + role: AudioDeviceRole::Default, + endpoint_label: "Built-in Microphone".into(), + is_default_route: true, + }, + AudioInputDevice { + device_id: "mic-b".into(), + label: "USB Mic".into(), + is_default: false, + role: AudioDeviceRole::Endpoint, + endpoint_label: "USB Mic".into(), + is_default_route: false, + }, + ]; + assert_eq!( + input_devices_json(&list), + "[{\"deviceId\":\"mic-a\",\"label\":\"Built-in Microphone\",\"isDefault\":true,\"role\":\"default\",\"endpointLabel\":\"Built-in Microphone\",\"isDefaultRoute\":true},{\"deviceId\":\"mic-b\",\"label\":\"USB Mic\",\"isDefault\":false,\"role\":\"endpoint\",\"endpointLabel\":\"USB Mic\",\"isDefaultRoute\":false}]" + ); + } + + #[test] + fn default_input_devices_json_exposes_a_default_route_placeholder() { + assert_eq!( + default_input_devices_json(), + "[{\"deviceId\":\"default\",\"label\":\"Default\",\"isDefault\":true,\"role\":\"default\",\"endpointLabel\":\"Default\",\"isDefaultRoute\":true}]" + ); + } + + #[test] + fn shape_adds_default_route_for_index_zero_endpoint_and_drops_empty_ids() { + let raw = vec![ + ( + "default-guid".to_string(), + "Default Speakers".to_string(), + 0, + ), + ("hdmi-guid".to_string(), "HDMI Out".to_string(), 1), + (String::new(), "Phantom".to_string(), 2), + ]; + let shaped = shape_output_devices(&raw); + assert_eq!(shaped.len(), 3); + assert!(shaped[0].is_default); + assert_eq!(shaped[0].device_id, "default"); + assert_eq!(shaped[0].role, AudioDeviceRole::Default); + assert!(shaped[0].is_default_route); + assert!(shaped[1].is_default); + assert_eq!(shaped[1].device_id, "default-guid"); + assert_eq!(shaped[1].role, AudioDeviceRole::Endpoint); + assert!(!shaped[2].is_default); + assert_eq!(shaped[2].label, "HDMI Out"); + } + + #[test] + fn shape_input_adds_default_route_for_index_zero_endpoint_and_drops_empty_ids() { + let raw = vec![ + ("default-mic".to_string(), "Default Mic".to_string(), 0), + ("usb-mic".to_string(), "USB Mic".to_string(), 1), + (String::new(), "Phantom".to_string(), 2), + ]; + let shaped = shape_input_devices(&raw); + assert_eq!(shaped.len(), 3); + assert!(shaped[0].is_default); + assert_eq!(shaped[0].device_id, "default"); + assert_eq!(shaped[0].role, AudioDeviceRole::Default); + assert!(shaped[0].is_default_route); + assert!(shaped[1].is_default); + assert_eq!(shaped[1].device_id, "default-mic"); + assert_eq!(shaped[1].role, AudioDeviceRole::Endpoint); + assert!(!shaped[2].is_default); + assert_eq!(shaped[2].label, "USB Mic"); + } + + #[test] + fn shape_recognizes_macos_virtual_default_without_duplicating_it() { + let raw = vec![ + ("default".to_string(), "default (WH-1000XM5)".to_string(), 0), + ("74".to_string(), "Studio Display Microphone".to_string(), 1), + ("81".to_string(), "WH-1000XM5".to_string(), 2), + ]; + let shaped = shape_input_devices(&raw); + assert_eq!(shaped.len(), 3); + assert_eq!(shaped[0].device_id, "default"); + assert_eq!(shaped[0].label, "WH-1000XM5"); + assert_eq!(shaped[0].endpoint_label, "WH-1000XM5"); + assert_eq!(shaped[0].role, AudioDeviceRole::Default); + assert!(shaped[0].is_default_route); + assert_eq!(shaped[1].device_id, "74"); + assert_eq!(shaped[1].role, AudioDeviceRole::Endpoint); + assert_eq!(shaped[2].device_id, "81"); + let default_route_count = shaped + .iter() + .filter(|device| device.is_default_route) + .count(); + assert_eq!(default_route_count, 1); + } + + #[test] + fn shape_collapses_duplicate_device_ids() { + let raw = vec![ + ("default".to_string(), "default (WH-1000XM5)".to_string(), 0), + ("default".to_string(), "default (WH-1000XM5)".to_string(), 1), + ("81".to_string(), "WH-1000XM5".to_string(), 2), + ("81".to_string(), "WH-1000XM5".to_string(), 3), + ]; + let shaped = shape_output_devices(&raw); + assert_eq!(shaped.len(), 2); + assert_eq!(shaped[0].device_id, "default"); + assert_eq!(shaped[1].device_id, "81"); + } + + #[test] + fn shape_recognizes_pulse_virtual_default_prefix() { + let raw = vec![ + ( + "pulse-default".to_string(), + "default: alsa_output.pci-0000_00_1f.3.analog-stereo".to_string(), + 0, + ), + ( + "pulse-sink".to_string(), + "Built-in Audio Analog Stereo".to_string(), + 1, + ), + ]; + let shaped = shape_output_devices(&raw); + assert_eq!(shaped.len(), 2); + assert_eq!(shaped[0].device_id, "default"); + assert_eq!( + shaped[0].label, + "alsa_output.pci-0000_00_1f.3.analog-stereo" + ); + assert!(shaped[0].is_default_route); + assert_eq!(shaped[1].role, AudioDeviceRole::Endpoint); + } + + #[test] + fn shape_recognizes_windows_adm2_communication_singular_prefix() { + let raw = vec![ + ( + "{0.0.1.00000000}.{guid-a}".to_string(), + "Default - Headset Microphone (2- Arctis 7 Chat)".to_string(), + 0, + ), + ( + "{0.0.1.00000000}.{guid-a}".to_string(), + "Communication - Headset Microphone (2- Arctis 7 Chat)".to_string(), + 1, + ), + ( + "{0.0.1.00000000}.{guid-b}".to_string(), + "Headset Microphone (2- Arctis 7 Chat)".to_string(), + 2, + ), + ]; + let shaped = shape_input_devices(&raw); + assert_eq!(shaped.len(), 3); + assert_eq!(shaped[0].device_id, "default"); + assert_eq!(shaped[0].label, "Headset Microphone (2- Arctis 7 Chat)"); + assert_eq!(shaped[1].role, AudioDeviceRole::Communications); + assert_eq!(shaped[2].role, AudioDeviceRole::Endpoint); + } + + #[test] + fn find_default_device_id_prefers_the_virtual_default_route() { + let macos = vec![ + ("default".to_string(), "default (WH-1000XM5)".to_string(), 0), + ("81".to_string(), "WH-1000XM5".to_string(), 1), + ]; + assert_eq!(find_default_device_id(&macos), Some("default".to_string())); + + let no_virtual_default = vec![ + ("guid-a".to_string(), "Speakers (Realtek)".to_string(), 0), + ("guid-b".to_string(), "HDMI Out".to_string(), 1), + ]; + assert_eq!( + find_default_device_id(&no_virtual_default), + Some("guid-a".to_string()) + ); + + let empty: Vec<(String, String, usize)> = vec![]; + assert_eq!(find_default_device_id(&empty), None); + let only_empty_ids = vec![(String::new(), "Phantom".to_string(), 0)]; + assert_eq!(find_default_device_id(&only_empty_ids), None); + } + + #[test] + fn adm_default_wrapper_requires_the_full_shape() { + assert_eq!( + strip_adm_default_wrapper("default (WH-1000XM5)"), + Some("WH-1000XM5".to_string()) + ); + assert_eq!( + strip_adm_default_wrapper("Default (MacBook Pro Speakers)"), + Some("MacBook Pro Speakers".to_string()) + ); + assert_eq!(strip_adm_default_wrapper("default ()"), None); + assert_eq!(strip_adm_default_wrapper("default"), None); + assert_eq!(strip_adm_default_wrapper("default (unterminated"), None); + assert_eq!(strip_adm_default_wrapper("WH-1000XM5"), None); + } + + #[test] + fn shape_uses_windows_default_and_communications_roles() { + let raw = vec![ + ( + "default-guid".to_string(), + "Default - Microphone (3- Logitech PRO X Wireless Gaming Headset) (046d:0aba)".to_string(), + 0, + ), + ( + "communications-guid".to_string(), + "Communications - Microphone (3- Logitech PRO X Wireless Gaming Headset) (046D:0ABA)".to_string(), + 1, + ), + ( + "endpoint-guid".to_string(), + "Microphone (3- Logitech PRO X Wireless Gaming Headset) (046d:0aba)".to_string(), + 2, + ), + ]; + let shaped = shape_input_devices(&raw); + assert_eq!(shaped.len(), 3); + assert_eq!(shaped[0].device_id, "default"); + assert_eq!( + shaped[0].label, + "Microphone (3- Logitech PRO X Wireless Gaming Headset)" + ); + assert_eq!(shaped[0].role, AudioDeviceRole::Default); + assert!(shaped[0].is_default_route); + assert_eq!( + shaped[1].label, + "Microphone (3- Logitech PRO X Wireless Gaming Headset)" + ); + assert_eq!(shaped[1].role, AudioDeviceRole::Communications); + assert_eq!( + shaped[2].label, + "Microphone (3- Logitech PRO X Wireless Gaming Headset)" + ); + assert_eq!(shaped[2].role, AudioDeviceRole::Endpoint); + } + + #[test] + fn clamp_deep_filter_noise_reduction_level_holds_the_contract_range() { + assert_eq!(clamp_deep_filter_noise_reduction_level(0.0), 0.0); + assert_eq!(clamp_deep_filter_noise_reduction_level(80.0), 80.0); + assert_eq!(clamp_deep_filter_noise_reduction_level(100.0), 100.0); + assert_eq!(clamp_deep_filter_noise_reduction_level(-5.0), 0.0); + assert_eq!(clamp_deep_filter_noise_reduction_level(150.0), 100.0); + assert_eq!(clamp_deep_filter_noise_reduction_level(f64::NAN), 100.0); + assert_eq!( + clamp_deep_filter_noise_reduction_level(f64::INFINITY), + 100.0 + ); + } + + #[test] + fn deep_filter_intent_passes_explicit_apm_through_when_not_requested() { + let intent = + resolve_microphone_apm_intent(Some(true), Some(false), Some(true), false, false); + assert_eq!(intent.echo_cancellation, Some(true)); + assert_eq!(intent.noise_suppression, Some(false)); + assert_eq!(intent.auto_gain_control, Some(true)); + } + + #[test] + fn deep_filter_intent_falls_back_to_apm_when_native_filter_is_unavailable() { + let intent = + resolve_microphone_apm_intent(Some(true), Some(false), Some(false), true, false); + assert_eq!(intent.echo_cancellation, Some(true)); + assert_eq!(intent.noise_suppression, Some(true)); + assert_eq!(intent.auto_gain_control, Some(true)); + } + + #[test] + fn deep_filter_intent_disables_apm_noise_paths_when_native_filter_runs() { + let intent = resolve_microphone_apm_intent(Some(true), Some(true), Some(true), true, true); + assert_eq!(intent.echo_cancellation, Some(true)); + assert_eq!(intent.noise_suppression, Some(false)); + assert_eq!(intent.auto_gain_control, Some(false)); + } + + #[cfg(feature = "publisher")] + #[test] + fn processing_options_preserve_defaults_unless_overridden() { + let defaults = AudioProcessingOptions::default(); + let options = processing_options(Some(false), None, Some(false)); + assert!(!options.echo_cancellation); + assert_eq!(options.noise_suppression, defaults.noise_suppression); + assert!(!options.auto_gain_control); + assert_eq!( + options.prefer_hardware_processing, + defaults.prefer_hardware_processing + ); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/src/bridge_version.rs b/fluxer_desktop/native/webrtc-sender/src/bridge_version.rs new file mode 100644 index 0000000..40207e9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/src/bridge_version.rs @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use napi_derive::napi; + +pub const ENGINE_BRIDGE_VERSION: u32 = 18; + +const _: () = assert!(ENGINE_BRIDGE_VERSION > 0); + +fn check_engine_bridge_version(version: u32) -> Result<(), String> { + if version == ENGINE_BRIDGE_VERSION { + return Ok(()); + } + Err(format!( + "voice engine bridge version mismatch: host sent {version}, native addon expects {ENGINE_BRIDGE_VERSION}" + )) +} + +#[napi] +pub fn get_engine_bridge_version() -> u32 { + ENGINE_BRIDGE_VERSION +} + +#[napi] +pub fn assert_engine_bridge_version(version: u32) -> napi::Result<()> { + check_engine_bridge_version(version).map_err(napi::Error::from_reason) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn matching_version_passes() { + assert!(check_engine_bridge_version(ENGINE_BRIDGE_VERSION).is_ok()); + assert!(assert_engine_bridge_version(ENGINE_BRIDGE_VERSION).is_ok()); + } + + #[test] + fn mismatched_version_fails_with_both_versions_in_message() { + let error = check_engine_bridge_version(ENGINE_BRIDGE_VERSION + 1).unwrap_err(); + assert!(error.contains("voice engine bridge version mismatch")); + assert!(error.contains(&(ENGINE_BRIDGE_VERSION + 1).to_string())); + assert!(error.contains(&ENGINE_BRIDGE_VERSION.to_string())); + } + + #[test] + fn zero_version_fails() { + assert!(check_engine_bridge_version(0).is_err()); + } + + #[test] + fn mismatch_surfaces_as_napi_error() { + let error = assert_engine_bridge_version(ENGINE_BRIDGE_VERSION - 1).unwrap_err(); + assert!( + error + .reason + .contains("voice engine bridge version mismatch") + ); + } + + #[test] + fn exported_getter_reports_the_constant() { + assert_eq!(get_engine_bridge_version(), ENGINE_BRIDGE_VERSION); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/src/camera.rs b/fluxer_desktop/native/webrtc-sender/src/camera.rs new file mode 100644 index 0000000..3c58597 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/src/camera.rs @@ -0,0 +1,1197 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub const DEFAULT_WIDTH: u32 = 1280; +pub const DEFAULT_HEIGHT: u32 = 720; +pub const DEFAULT_FPS: u32 = 30; +const MAX_CAMERA_DEVICE_ALIASES: usize = 4; +#[cfg(feature = "camera-native")] +const MAX_CAMERA_FORMAT_ATTEMPTS: usize = 16; +#[cfg(feature = "camera-native")] +const MAX_COMPATIBLE_CAMERA_FORMAT_CANDIDATES: usize = 128; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub enum CameraSelector { + Index(u32), + Id(String), +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CameraRequest { + pub selector: CameraSelector, + pub width: u32, + pub height: u32, + pub fps: u32, + pub mirror: bool, + pub background: crate::camera_background::CameraBackgroundConfig, +} + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct CameraDevice { + pub device_id: String, + pub label: String, + pub description: String, + pub index: Option, + pub device_id_aliases: Vec, +} + +fn push_camera_device_alias(aliases: &mut Vec, alias: &str) { + assert!(aliases.len() <= MAX_CAMERA_DEVICE_ALIASES); + let trimmed = alias.trim(); + if trimmed.is_empty() { + return; + } + for existing in aliases.iter().take(MAX_CAMERA_DEVICE_ALIASES) { + if existing == trimmed { + return; + } + } + if aliases.len() < MAX_CAMERA_DEVICE_ALIASES { + aliases.push(trimmed.to_string()); + } + assert!(aliases.len() <= MAX_CAMERA_DEVICE_ALIASES); +} + +fn camera_device_aliases(device_id: &str, index: &str) -> Vec { + let mut aliases = Vec::with_capacity(2); + assert_eq!(aliases.len(), 0); + push_camera_device_alias(&mut aliases, device_id); + push_camera_device_alias(&mut aliases, index); + assert!(aliases.len() <= MAX_CAMERA_DEVICE_ALIASES); + aliases +} + +impl CameraRequest { + pub fn from_opts( + device_id: Option<&str>, + width: Option, + height: Option, + frame_rate: Option, + mirror: bool, + background: crate::camera_background::CameraBackgroundConfig, + ) -> Self { + let selector = match device_id + .map(str::trim) + .filter(|s| !s.is_empty() && *s != "default") + { + Some(raw) => raw + .parse::() + .map(CameraSelector::Index) + .unwrap_or_else(|_| CameraSelector::Id(raw.to_string())), + None => CameraSelector::Index(0), + }; + let width = match width { + Some(w) if w >= 2 => w & !1, + _ => DEFAULT_WIDTH, + }; + let height = match height { + Some(h) if h >= 2 => h & !1, + _ => DEFAULT_HEIGHT, + }; + let fps = match frame_rate { + Some(f) if f > 0 => f, + _ => DEFAULT_FPS, + }; + Self { + selector, + width, + height, + fps, + mirror, + background, + } + } +} + +#[cfg(feature = "camera-native")] +fn accepted_camera_formats() -> &'static [nokhwa::utils::FrameFormat] { + use nokhwa::pixel_format::{FormatDecoder, RgbFormat}; + use nokhwa::utils::FrameFormat; + const FORMATS: &[FrameFormat] = &[ + FrameFormat::NV12, + FrameFormat::YUYV, + FrameFormat::RAWRGB, + FrameFormat::RAWBGR, + FrameFormat::MJPEG, + ]; + let formats = ::FORMATS; + assert!(FORMATS.iter().all(|format| formats.contains(format))); + assert!(formats.len() <= MAX_CAMERA_FORMAT_ATTEMPTS); + FORMATS +} + +#[cfg(feature = "camera-native")] +fn camera_format_priority(frame_format: nokhwa::utils::FrameFormat) -> usize { + accepted_camera_formats() + .iter() + .position(|candidate| *candidate == frame_format) + .unwrap_or(accepted_camera_formats().len()) +} + +#[cfg(feature = "camera-native")] +fn camera_format_score( + format: &nokhwa::utils::CameraFormat, + request: &CameraRequest, +) -> (u64, u32, usize) { + let resolution = format.resolution(); + let width_delta = u64::from(resolution.width().abs_diff(request.width)); + let height_delta = u64::from(resolution.height().abs_diff(request.height)); + ( + width_delta * width_delta + height_delta * height_delta, + format.frame_rate().abs_diff(request.fps), + camera_format_priority(format.format()), + ) +} + +#[cfg(feature = "camera-native")] +fn select_best_camera_format( + formats: &[nokhwa::utils::CameraFormat], + request: &CameraRequest, +) -> Option { + formats + .iter() + .take(MAX_COMPATIBLE_CAMERA_FORMAT_CANDIDATES) + .filter(|format| accepted_camera_formats().contains(&format.format())) + .min_by_key(|format| camera_format_score(format, request)) + .copied() +} + +#[cfg(feature = "camera-native")] +struct CameraFrameConverter { + i420: crate::yuv::I420, +} + +#[cfg(feature = "camera-native")] +impl CameraFrameConverter { + fn new(width: u32, height: u32) -> Option { + Some(Self { + i420: crate::yuv::I420::new(width, height)?, + }) + } + + fn width(&self) -> u32 { + self.i420.width + } + + fn height(&self) -> u32 { + self.i420.height + } + + fn convert<'a>( + &'a mut self, + frame: &nokhwa::Buffer, + width: u32, + height: u32, + ) -> Option<&'a mut crate::yuv::I420> { + if self.i420.width != width || self.i420.height != height { + return None; + } + if !camera_frame_to_i420_into(frame, width, height, &mut self.i420) { + return None; + } + Some(&mut self.i420) + } + + fn convert_nv12<'a>( + &'a mut self, + src: &[u8], + width: u32, + height: u32, + ) -> Option<&'a crate::yuv::I420> { + if self.i420.width != width || self.i420.height != height { + return None; + } + if !crate::yuv::nv12_to_i420_into(src, width, height, width, width, &mut self.i420) { + return None; + } + Some(&self.i420) + } +} + +#[cfg(feature = "camera-native")] +fn camera_frame_to_i420_into( + frame: &nokhwa::Buffer, + width: u32, + height: u32, + dst: &mut crate::yuv::I420, +) -> bool { + use nokhwa::utils::FrameFormat; + match frame.source_frame_format() { + FrameFormat::NV12 => { + crate::yuv::nv12_to_i420_into(frame.buffer(), width, height, width, width, dst) + } + FrameFormat::YUYV => { + crate::yuv::yuyv_to_i420_into(frame.buffer(), width, height, width * 2, dst) + } + FrameFormat::RAWRGB => crate::yuv::rgb_to_i420_into(frame.buffer(), width, height, dst), + FrameFormat::RAWBGR => crate::yuv::bgr_to_i420_into(frame.buffer(), width, height, dst), + FrameFormat::MJPEG | FrameFormat::GRAY => { + decode_camera_frame_to_i420_into(frame, width, height, dst) + } + } +} + +fn mirror_i420_in_place(frame: &mut crate::yuv::I420) { + assert!(frame.width >= 2); + assert!(frame.height >= 2); + mirror_plane_rows(&mut frame.y, frame.width as usize, frame.height as usize); + mirror_plane_rows( + &mut frame.u, + (frame.width / 2) as usize, + (frame.height / 2) as usize, + ); + mirror_plane_rows( + &mut frame.v, + (frame.width / 2) as usize, + (frame.height / 2) as usize, + ); +} + +fn mirror_plane_rows(plane: &mut [u8], width: usize, rows: usize) { + assert!(width >= 1); + assert!(rows >= 1); + assert!(plane.len() >= width.saturating_mul(rows)); + for row in 0..rows { + let start = row * width; + let end = start + width; + plane[start..end].reverse(); + } +} + +#[cfg(feature = "camera-native")] +fn decode_camera_frame_to_i420_into( + frame: &nokhwa::Buffer, + width: u32, + height: u32, + dst: &mut crate::yuv::I420, +) -> bool { + use nokhwa::pixel_format::RgbFormat; + let Ok(rgb) = frame.decode_image::() else { + return false; + }; + if rgb.width() != width || rgb.height() != height { + return false; + } + crate::yuv::rgb_to_i420_into(&rgb.into_raw(), width, height, dst) +} + +#[cfg(all(test, feature = "camera-native"))] +mod camera_native_tests { + use super::*; + use nokhwa::Buffer; + use nokhwa::utils::{CameraFormat, FrameFormat, Resolution}; + + fn request(width: u32, height: u32, fps: u32) -> CameraRequest { + CameraRequest { + selector: CameraSelector::Index(0), + width, + height, + fps, + mirror: false, + background: crate::camera_background::CameraBackgroundConfig::default(), + } + } + + #[test] + fn raw_format_selection_prefers_nv12_over_yuyv_and_mjpeg() { + let formats = vec![ + CameraFormat::new(Resolution::new(1280, 720), FrameFormat::MJPEG, 30), + CameraFormat::new(Resolution::new(1280, 720), FrameFormat::YUYV, 30), + CameraFormat::new(Resolution::new(1280, 720), FrameFormat::NV12, 30), + ]; + + let selected = select_best_camera_format(&formats, &request(1280, 720, 30)).unwrap(); + + assert_eq!(selected.format(), FrameFormat::NV12); + } + + #[test] + fn raw_format_selection_prefers_nearest_resolution_before_format_priority() { + let formats = vec![ + CameraFormat::new(Resolution::new(1920, 1080), FrameFormat::NV12, 30), + CameraFormat::new(Resolution::new(1024, 768), FrameFormat::YUYV, 30), + ]; + + let selected = select_best_camera_format(&formats, &request(1280, 720, 30)).unwrap(); + + assert_eq!(selected.resolution(), Resolution::new(1024, 768)); + assert_eq!(selected.format(), FrameFormat::YUYV); + } + + #[test] + fn camera_frame_to_i420_accepts_raw_nv12_without_rgb_decode() { + let data = [1u8, 2, 3, 4, 10, 20]; + let frame = Buffer::new(Resolution::new(2, 2), &data, FrameFormat::NV12); + let mut converter = CameraFrameConverter::new(2, 2).unwrap(); + + let i420 = converter.convert(&frame, 2, 2).unwrap(); + + assert_eq!(i420.y, vec![1, 2, 3, 4]); + assert_eq!(i420.u, vec![10]); + assert_eq!(i420.v, vec![20]); + } + + #[test] + fn camera_frame_to_i420_accepts_raw_yuyv_without_rgb_decode() { + let data = [1u8, 10, 2, 20, 3, 30, 4, 40]; + let frame = Buffer::new(Resolution::new(2, 2), &data, FrameFormat::YUYV); + let mut converter = CameraFrameConverter::new(2, 2).unwrap(); + + let i420 = converter.convert(&frame, 2, 2).unwrap(); + + assert_eq!(i420.y, vec![1, 2, 3, 4]); + assert_eq!(i420.u, vec![20]); + assert_eq!(i420.v, vec![30]); + } + + #[test] + fn native_camera_transport_contract_includes_platform_zero_copy_buffers() { + assert_eq!( + crate::native_camera::required_transport_names(), + ["cvPixelBuffer", "d3d11Texture", "dmabuf"] + ); + } +} + +#[cfg(feature = "publisher")] +#[cfg_attr(not(feature = "camera-native"), allow(dead_code))] +#[derive(Clone, Copy, Debug)] +pub struct OpenedCamera { + pub width: u32, + pub height: u32, + pub fps: u32, +} + +#[cfg(feature = "publisher")] +pub type LocalVideoFrameSink = Box; + +#[cfg(feature = "publisher")] +pub type LocalVideoFrameSinkActive = Box bool + Send>; + +#[cfg(feature = "publisher")] +pub struct CameraCaptureSinks { + pub source: livekit::webrtc::video_source::native::NativeVideoSource, + pub frame_sink: LocalVideoFrameSink, + pub frame_sink_active: LocalVideoFrameSinkActive, +} + +#[cfg(all( + feature = "publisher", + feature = "camera-native", + any(target_os = "windows", target_os = "macos", target_os = "linux") +))] +pub use live::spawn_capture_worker; + +#[cfg(all( + feature = "publisher", + not(all( + feature = "camera-native", + any(target_os = "windows", target_os = "macos", target_os = "linux") + )) +))] +pub fn spawn_capture_worker( + _request: CameraRequest, + result_tx: std::sync::mpsc::Sender>, + _source_rx: std::sync::mpsc::Receiver, + _stop: std::sync::Arc, +) { + let _ = result_tx.send(Err( + "native camera capture is not enabled for this platform build".to_string(), + )); +} + +#[cfg(all( + feature = "publisher", + feature = "camera-native", + any(target_os = "windows", target_os = "macos", target_os = "linux") +))] +pub use live::list_devices; + +#[cfg(all( + feature = "publisher", + not(all( + feature = "camera-native", + any(target_os = "windows", target_os = "macos", target_os = "linux") + )) +))] +pub fn list_devices() -> Result, String> { + Ok(Vec::new()) +} + +#[cfg(all( + feature = "publisher", + feature = "camera-native", + any(target_os = "windows", target_os = "macos", target_os = "linux") +))] +mod live { + use super::{ + CameraCaptureSinks, CameraDevice, CameraFrameConverter, CameraRequest, CameraSelector, + OpenedCamera, camera_device_aliases, + }; + use crate::yuv; + use livekit::webrtc::video_frame::{I420Buffer, NV12Buffer, VideoFrame, VideoRotation}; + use livekit::webrtc::video_source::native::NativeVideoSource; + use nokhwa::pixel_format::RgbFormat; + use nokhwa::utils::{ + ApiBackend, CameraFormat, CameraIndex, FrameFormat, RequestedFormat, RequestedFormatType, + Resolution, + }; + use nokhwa::{Camera, query}; + use std::panic::{AssertUnwindSafe, catch_unwind}; + use std::sync::Arc; + use std::sync::atomic::{AtomicBool, Ordering}; + use std::sync::mpsc::Sender; + #[cfg(target_os = "macos")] + use std::sync::mpsc::channel; + use std::time::{Duration, Instant}; + + const MAX_CAMERA_DEVICE_FALLBACK_CANDIDATES: usize = 64; + const CAMERA_FRAME_FAILURE_RETRY_DELAY: Duration = Duration::from_millis(5); + const CAMERA_FRAME_FAILURES_CONSECUTIVE_MAX: u32 = 600; + + #[cfg(target_os = "macos")] + fn ensure_camera_runtime_initialized() -> Result<(), String> { + if nokhwa::nokhwa_check() { + return Ok(()); + } + let (tx, rx) = channel(); + nokhwa::nokhwa_initialize(move |ok| { + let _ = tx.send(ok); + }); + match rx.recv_timeout(Duration::from_secs(30)) { + Ok(true) => Ok(()), + Ok(false) => Err("camera permission denied".to_string()), + Err(_) => Err("camera permission request timed out".to_string()), + } + } + + #[cfg(not(target_os = "macos"))] + fn ensure_camera_runtime_initialized() -> Result<(), String> { + Ok(()) + } + + fn camera_index_for_request(request: &CameraRequest) -> CameraIndex { + match &request.selector { + CameraSelector::Index(index) => CameraIndex::Index(*index), + CameraSelector::Id(id) => CameraIndex::String(id.clone()), + } + } + + fn camera_device_id_matches(requested_id: &str, device_id: &str, index: &str) -> bool { + assert!(!requested_id.trim().is_empty()); + assert_eq!(requested_id, requested_id.trim()); + assert!(MAX_CAMERA_DEVICE_FALLBACK_CANDIDATES > 0); + if requested_id == device_id.trim() { + return true; + } + requested_id == index.trim() + } + + fn fallback_camera_index_for_request(request: &CameraRequest) -> Option { + let CameraSelector::Id(requested_id) = &request.selector else { + return None; + }; + let requested_id = requested_id.trim(); + if requested_id.is_empty() { + return None; + } + let devices = query(ApiBackend::Auto).ok()?; + for device in devices + .into_iter() + .take(MAX_CAMERA_DEVICE_FALLBACK_CANDIDATES) + { + let index_string = device.index().as_string(); + let misc = device.misc(); + let device_id = if misc.trim().is_empty() { + index_string.as_str() + } else { + misc.as_str() + }; + if !camera_device_id_matches(requested_id, device_id, &index_string) { + continue; + } + if let Ok(index) = device.index().as_index() { + return Some(CameraIndex::Index(index)); + } + } + None + } + + fn requested_camera_format( + request: &CameraRequest, + frame_format: FrameFormat, + ) -> RequestedFormat<'static> { + let camera_format = CameraFormat::new( + Resolution::new(request.width, request.height), + frame_format, + request.fps, + ); + RequestedFormat::new::(RequestedFormatType::Closest(camera_format)) + } + + fn open_stream(mut camera: Camera) -> Result<(Camera, OpenedCamera), String> { + camera + .open_stream() + .map_err(|e| format!("open camera stream: {e}"))?; + let resolution = camera.resolution(); + let fps = camera.frame_rate(); + let opened = OpenedCamera { + width: resolution.width() & !1, + height: resolution.height() & !1, + fps, + }; + Ok((camera, opened)) + } + + fn open_best_compatible_camera( + index: CameraIndex, + request: &CameraRequest, + ) -> Result<(Camera, OpenedCamera), String> { + let requested = RequestedFormat::new::(RequestedFormatType::None); + let mut camera = + Camera::new(index, requested).map_err(|error| format!("open camera: {error}"))?; + let formats = camera + .compatible_camera_formats() + .map_err(|error| format!("query compatible formats: {error}"))?; + let best_format = super::select_best_camera_format(&formats, request).ok_or_else(|| { + format!( + "no compatible RGB-decodable camera format for {}x{}@{}", + request.width, request.height, request.fps + ) + })?; + let requested = RequestedFormat::new::(RequestedFormatType::Exact(best_format)); + camera + .set_camera_requset(requested) + .map_err(|error| format!("set selected format {best_format:?}: {error}"))?; + open_stream(camera).map_err(|error| format!("{error}; selected format {best_format:?}")) + } + + fn open_camera_with_index( + index: CameraIndex, + request: &CameraRequest, + ) -> Result<(Camera, OpenedCamera), String> { + assert!(request.width >= 2); + assert!(request.height >= 2); + assert!(request.fps > 0); + let mut errors = Vec::new(); + match open_best_compatible_camera(index.clone(), request) { + Ok(opened) => return Ok(opened), + Err(error) => errors.push(format!("best compatible format: {error}")), + } + for frame_format in super::accepted_camera_formats() + .iter() + .take(super::MAX_CAMERA_FORMAT_ATTEMPTS) + { + let requested = requested_camera_format(request, *frame_format); + match Camera::new(index.clone(), requested) { + Ok(camera) => match open_stream(camera) { + Ok(opened) => return Ok(opened), + Err(error) => errors.push(format!("{frame_format}: {error}")), + }, + Err(error) => errors.push(format!("{frame_format}: {error}")), + } + } + + let requested = RequestedFormat::new::(RequestedFormatType::None); + let mut camera = Camera::new(index, requested).map_err(|error| { + format!( + "open camera: {error}; tried requested formats: {}", + errors.join("; ") + ) + })?; + match camera.compatible_camera_formats() { + Ok(formats) => { + if let Some(best_format) = super::select_best_camera_format(&formats, request) { + let request = + RequestedFormat::new::(RequestedFormatType::Exact(best_format)); + if let Err(error) = camera.set_camera_requset(request) { + errors.push(format!("set fallback format {best_format:?}: {error}")); + } + } + } + Err(error) => errors.push(format!("query fallback formats: {error}")), + } + open_stream(camera) + .map_err(|error| format!("{error}; tried requested formats: {}", errors.join("; "))) + } + + fn open_camera(request: &CameraRequest) -> Result<(Camera, OpenedCamera), String> { + ensure_camera_runtime_initialized()?; + let primary_index = camera_index_for_request(request); + match open_camera_with_index(primary_index.clone(), request) { + Ok(opened) => Ok(opened), + Err(primary_error) => match fallback_camera_index_for_request(request) { + Some(fallback_index) if fallback_index != primary_index => { + open_camera_with_index(fallback_index.clone(), request).map_err(|fallback_error| { + format!( + "open camera by requested selector failed: {primary_error}; fallback index {} failed: {fallback_error}", + fallback_index.as_string() + ) + }) + } + _ => Err(primary_error), + }, + } + } + + pub fn list_devices() -> Result, String> { + let devices = query(ApiBackend::Auto).map_err(|e| format!("query cameras: {e}"))?; + Ok(devices + .into_iter() + .map(|device| { + let misc = device.misc(); + let index_string = device.index().as_string(); + let device_id = if misc.trim().is_empty() { + index_string.clone() + } else { + misc + }; + CameraDevice { + device_id_aliases: camera_device_aliases(&device_id, &index_string), + device_id, + label: device.human_name(), + description: device.description().to_string(), + index: device.index().as_index().ok(), + } + }) + .collect()) + } + + pub fn spawn_capture_worker( + request: CameraRequest, + result_tx: Sender>, + source_rx: std::sync::mpsc::Receiver, + stop: Arc, + ) { + std::thread::spawn(move || { + elevate_capture_thread_priority(); + let (mut camera, opened) = match open_camera(&request) { + Ok(pair) => pair, + Err(e) => { + let _ = result_tx.send(Err(e)); + return; + } + }; + if result_tx.send(Ok(opened)).is_err() { + return; + } + let sinks = match source_rx.recv() { + Ok(s) => s, + Err(_) => return, + }; + let outcome = catch_unwind(AssertUnwindSafe(|| { + run_capture_loop(&mut camera, opened, &request, &sinks, &stop); + })); + if outcome.is_err() { + eprintln!("webrtc-sender: camera capture worker panicked; stopping capture"); + } + let _ = camera.stop_stream(); + }); + } + + #[cfg(target_os = "macos")] + fn elevate_capture_thread_priority() { + use std::ffi::{c_int, c_uint}; + const QOS_CLASS_USER_INTERACTIVE: c_uint = 0x21; + const _: () = assert!(QOS_CLASS_USER_INTERACTIVE == 0x21); + unsafe extern "C" { + fn pthread_set_qos_class_self_np(qos_class: c_uint, relative_priority: c_int) -> c_int; + } + let _ = unsafe { pthread_set_qos_class_self_np(QOS_CLASS_USER_INTERACTIVE, 0) }; + } + + #[cfg(target_os = "windows")] + fn elevate_capture_thread_priority() { + use std::ffi::{c_int, c_void}; + const THREAD_PRIORITY_HIGHEST: c_int = 2; + const _: () = assert!(THREAD_PRIORITY_HIGHEST == 2); + #[link(name = "kernel32")] + unsafe extern "system" { + fn GetCurrentThread() -> *mut c_void; + fn SetThreadPriority(thread: *mut c_void, priority: c_int) -> c_int; + } + let _ = unsafe { SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST) }; + } + + #[cfg(target_os = "linux")] + fn elevate_capture_thread_priority() {} + + fn run_capture_loop( + camera: &mut Camera, + opened: OpenedCamera, + request: &CameraRequest, + sinks: &CameraCaptureSinks, + stop: &AtomicBool, + ) { + let start = Instant::now(); + let fps = opened.fps.max(1); + assert!(fps >= 1); + let mut deadline = Instant::now(); + let mut background_stage = + crate::camera_background::CameraBackgroundStage::new(request.background.clone()); + let mut frame_converter: Option = None; + let mut consecutive_failures: u32 = 0; + while !stop.load(Ordering::Acquire) { + let frame = match camera.frame() { + Ok(f) => f, + Err(error) => { + if camera_frame_failure_cap_reached(&mut consecutive_failures) { + eprintln!( + "webrtc-sender: camera frame capture failed \ + {CAMERA_FRAME_FAILURES_CONSECUTIVE_MAX} times in a row; \ + stopping capture: {error}" + ); + break; + } + std::thread::sleep(CAMERA_FRAME_FAILURE_RETRY_DELAY); + continue; + } + }; + consecutive_failures = 0; + let fw = frame.resolution().width() & !1; + let fh = frame.resolution().height() & !1; + if stop.load(Ordering::Acquire) { + break; + } + let timestamp_us = start.elapsed().as_micros() as i64; + let converter_needs_init = match frame_converter.as_ref() { + Some(converter) => converter.width() != fw || converter.height() != fh, + None => true, + }; + if converter_needs_init { + frame_converter = CameraFrameConverter::new(fw, fh); + } + let Some(converter) = frame_converter.as_mut() else { + continue; + }; + if !request.mirror + && !background_stage.is_enabled() + && try_publish_raw_camera_frame(sinks, &frame, fw, fh, timestamp_us, converter) + { + pace_camera_frame(fps, &mut deadline); + continue; + } + let Some(i420) = converter.convert(&frame, fw, fh) else { + continue; + }; + if !background_stage.apply_i420(i420, timestamp_us) { + continue; + } + if request.mirror { + super::mirror_i420_in_place(i420); + } + capture_i420(&sinks.source, i420, timestamp_us); + (sinks.frame_sink)(i420, timestamp_us); + + pace_camera_frame(fps, &mut deadline); + } + } + + fn camera_frame_failure_cap_reached(consecutive_failures: &mut u32) -> bool { + assert!(*consecutive_failures < CAMERA_FRAME_FAILURES_CONSECUTIVE_MAX); + *consecutive_failures += 1; + assert!(*consecutive_failures <= CAMERA_FRAME_FAILURES_CONSECUTIVE_MAX); + *consecutive_failures == CAMERA_FRAME_FAILURES_CONSECUTIVE_MAX + } + + fn pace_camera_frame(fps: u32, deadline: &mut Instant) { + assert!(fps >= 1); + let interval = Duration::from_secs_f64(1.0 / f64::from(fps)); + assert!(interval > Duration::ZERO); + let next = next_pacing_deadline(*deadline, interval, Instant::now()); + let now = Instant::now(); + if next > now { + std::thread::sleep(next - now); + } + *deadline = next; + } + + fn next_pacing_deadline(deadline: Instant, interval: Duration, now: Instant) -> Instant { + assert!(interval > Duration::ZERO); + let next = deadline + interval; + if next < now { now } else { next } + } + + fn try_publish_raw_camera_frame( + sinks: &CameraCaptureSinks, + frame: &nokhwa::Buffer, + width: u32, + height: u32, + timestamp_us: i64, + converter: &mut CameraFrameConverter, + ) -> bool { + assert!(width >= 2); + assert!(height >= 2); + if frame.source_frame_format() != FrameFormat::NV12 { + return false; + } + if !capture_tight_nv12(&sinks.source, frame.buffer(), width, height, timestamp_us) { + return false; + } + if !(sinks.frame_sink_active)() { + return true; + } + if let Some(i420) = converter.convert_nv12(frame.buffer(), width, height) { + (sinks.frame_sink)(i420, timestamp_us); + } + true + } + + fn capture_tight_nv12( + source: &NativeVideoSource, + data: &[u8], + width: u32, + height: u32, + timestamp_us: i64, + ) -> bool { + let y_len = (width as usize) * (height as usize); + let uv_len = y_len / 2; + if data.len() < y_len + uv_len { + return false; + } + let mut buffer = NV12Buffer::with_strides(width, height, width, width); + let (dst_stride_y, dst_stride_uv) = buffer.strides(); + let (dst_y, dst_uv) = buffer.data_mut(); + if !yuv::copy_nv12_planes( + data, + width, + height, + width, + width, + dst_y, + dst_uv, + dst_stride_y, + dst_stride_uv, + ) { + return false; + } + source.capture_frame(&VideoFrame { + rotation: VideoRotation::VideoRotation0, + timestamp_us, + frame_metadata: None, + buffer, + }); + true + } + + fn capture_i420(source: &NativeVideoSource, frame: &yuv::I420, timestamp_us: i64) { + let mut buffer = I420Buffer::new(frame.width, frame.height); + let (stride_y, stride_u, stride_v) = buffer.strides(); + { + let (dy, du, dv) = buffer.data_mut(); + copy_plane( + dy, + &frame.y, + frame.width as usize, + stride_y as usize, + frame.height as usize, + ); + let cw = (frame.width / 2) as usize; + let ch = (frame.height / 2) as usize; + copy_plane(du, &frame.u, cw, stride_u as usize, ch); + copy_plane(dv, &frame.v, cw, stride_v as usize, ch); + } + let video_frame = VideoFrame { + rotation: VideoRotation::VideoRotation0, + timestamp_us, + frame_metadata: None, + buffer, + }; + source.capture_frame(&video_frame); + } + + fn copy_plane(dst: &mut [u8], src: &[u8], width: usize, dst_stride: usize, rows: usize) { + for row in 0..rows { + let s = row * width; + let d = row * dst_stride; + if s + width <= src.len() && d + width <= dst.len() { + dst[d..d + width].copy_from_slice(&src[s..s + width]); + } + } + } + + #[cfg(test)] + mod tests { + use super::*; + + fn request(width: u32, height: u32, fps: u32) -> CameraRequest { + CameraRequest { + selector: CameraSelector::Index(0), + width, + height, + fps, + mirror: false, + background: crate::camera_background::CameraBackgroundConfig::default(), + } + } + + #[test] + fn fallback_format_selection_uses_decoder_supported_non_mjpeg_modes() { + let formats = vec![ + CameraFormat::new(Resolution::new(640, 480), FrameFormat::MJPEG, 30), + CameraFormat::new(Resolution::new(1280, 720), FrameFormat::YUYV, 30), + CameraFormat::new(Resolution::new(1920, 1080), FrameFormat::NV12, 60), + ]; + + let selected = + crate::camera::select_best_camera_format(&formats, &request(1280, 720, 30)) + .unwrap(); + + assert_eq!(selected.resolution(), Resolution::new(1280, 720)); + assert_eq!(selected.format(), FrameFormat::YUYV); + assert_eq!(selected.frame_rate(), 30); + } + + #[test] + fn fallback_format_selection_prefers_nearest_resolution_before_format_priority() { + let formats = vec![ + CameraFormat::new(Resolution::new(1920, 1080), FrameFormat::MJPEG, 30), + CameraFormat::new(Resolution::new(1024, 768), FrameFormat::YUYV, 30), + ]; + + let selected = + crate::camera::select_best_camera_format(&formats, &request(1280, 720, 30)) + .unwrap(); + + assert_eq!(selected.resolution(), Resolution::new(1024, 768)); + assert_eq!(selected.format(), FrameFormat::YUYV); + } + + #[test] + fn next_pacing_deadline_advances_by_one_interval_when_on_schedule() { + let interval = Duration::from_millis(33); + let deadline = Instant::now(); + let now = deadline; + + let next = next_pacing_deadline(deadline, interval, now); + + assert_eq!(next, deadline + interval); + } + + #[test] + fn next_pacing_deadline_resets_to_now_when_behind_schedule() { + let interval = Duration::from_millis(10); + let deadline = Instant::now(); + let now = deadline + Duration::from_millis(500); + + let next = next_pacing_deadline(deadline, interval, now); + + assert_eq!(next, now); + } + + #[test] + fn next_pacing_deadline_does_not_drift_across_consecutive_frames() { + let interval = Duration::from_millis(20); + let start = Instant::now(); + let mut deadline = start; + + for _ in 0..3 { + deadline = next_pacing_deadline(deadline, interval, start); + } + + assert_eq!(deadline, start + interval * 3); + } + + #[test] + fn camera_frame_failure_cap_trips_only_at_consecutive_max() { + let mut consecutive_failures: u32 = 0; + + for _ in 1..CAMERA_FRAME_FAILURES_CONSECUTIVE_MAX { + assert!(!camera_frame_failure_cap_reached(&mut consecutive_failures)); + } + + assert!(camera_frame_failure_cap_reached(&mut consecutive_failures)); + assert_eq!(consecutive_failures, CAMERA_FRAME_FAILURES_CONSECUTIVE_MAX); + } + + #[test] + fn camera_frame_failure_cap_resets_after_success() { + let mut consecutive_failures: u32 = 0; + assert!(!camera_frame_failure_cap_reached(&mut consecutive_failures)); + assert_eq!(consecutive_failures, 1); + + consecutive_failures = 0; + + assert!(!camera_frame_failure_cap_reached(&mut consecutive_failures)); + assert_eq!(consecutive_failures, 1); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn from_opts_applies_defaults() { + let req = CameraRequest::from_opts( + None, + None, + None, + None, + false, + crate::camera_background::CameraBackgroundConfig::default(), + ); + assert_eq!( + req, + CameraRequest { + selector: CameraSelector::Index(0), + width: 1280, + height: 720, + fps: 30, + mirror: false, + background: crate::camera_background::CameraBackgroundConfig::default(), + } + ); + } + + #[test] + fn from_opts_parses_device_index() { + assert_eq!( + CameraRequest::from_opts( + Some("2"), + None, + None, + None, + false, + crate::camera_background::CameraBackgroundConfig::default(), + ) + .selector, + CameraSelector::Index(2) + ); + assert_eq!( + CameraRequest::from_opts( + Some(" 3 "), + None, + None, + None, + false, + crate::camera_background::CameraBackgroundConfig::default(), + ) + .selector, + CameraSelector::Index(3) + ); + assert_eq!( + CameraRequest::from_opts( + Some(""), + None, + None, + None, + false, + crate::camera_background::CameraBackgroundConfig::default(), + ) + .selector, + CameraSelector::Index(0) + ); + assert_eq!( + CameraRequest::from_opts( + Some("default"), + None, + None, + None, + false, + crate::camera_background::CameraBackgroundConfig::default(), + ) + .selector, + CameraSelector::Index(0) + ); + } + + #[test] + fn from_opts_preserves_string_device_ids() { + assert_eq!( + CameraRequest::from_opts( + Some("front"), + None, + None, + None, + false, + crate::camera_background::CameraBackgroundConfig::default(), + ) + .selector, + CameraSelector::Id("front".to_string()) + ); + } + + #[test] + fn from_opts_forces_even_dims_and_honours_overrides() { + let req = CameraRequest::from_opts( + Some("1"), + Some(641), + Some(481), + Some(24), + true, + crate::camera_background::CameraBackgroundConfig::default(), + ); + assert_eq!( + req, + CameraRequest { + selector: CameraSelector::Index(1), + width: 640, + height: 480, + fps: 24, + mirror: true, + background: crate::camera_background::CameraBackgroundConfig::default(), + } + ); + } + + #[test] + fn from_opts_rejects_degenerate_dims_and_fps() { + let req = CameraRequest::from_opts( + None, + Some(0), + Some(1), + Some(0), + false, + crate::camera_background::CameraBackgroundConfig::default(), + ); + assert_eq!( + req, + CameraRequest { + selector: CameraSelector::Index(0), + width: 1280, + height: 720, + fps: 30, + mirror: false, + background: crate::camera_background::CameraBackgroundConfig::default(), + } + ); + } + + #[test] + fn mirror_i420_in_place_reverses_luma_and_chroma_rows() { + let mut frame = crate::yuv::I420 { + width: 4, + height: 4, + y: (0u8..16).collect(), + u: vec![16, 17, 18, 19], + v: vec![20, 21, 22, 23], + }; + + mirror_i420_in_place(&mut frame); + + assert_eq!( + frame.y, + vec![3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12] + ); + assert_eq!(frame.u, vec![17, 16, 19, 18]); + assert_eq!(frame.v, vec![21, 20, 23, 22]); + } + + #[test] + fn camera_device_aliases_include_distinct_device_id_and_index() { + let aliases = camera_device_aliases("native-camera-id", "0"); + assert_eq!( + aliases, + vec!["native-camera-id".to_string(), "0".to_string()] + ); + } + + #[test] + fn camera_device_aliases_deduplicate_empty_and_repeated_values() { + let aliases = camera_device_aliases("0", "0"); + assert_eq!(aliases, vec!["0".to_string()]); + let aliases = camera_device_aliases("", "2"); + assert_eq!(aliases, vec!["2".to_string()]); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/src/camera_background.rs b/fluxer_desktop/native/webrtc-sender/src/camera_background.rs new file mode 100644 index 0000000..e6c2a8c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/src/camera_background.rs @@ -0,0 +1,1992 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use image::{AnimationDecoder, ImageReader, imageops::FilterType}; +use std::sync::Arc; + +const MAX_CUSTOM_BACKGROUND_BYTES: u64 = 10 * 1024 * 1024; +const FRAME_EDGE_MAX: usize = 8192; +pub const CUSTOM_BACKGROUND_ANIMATION_FRAMES_MAX: usize = 64; +pub const CUSTOM_BACKGROUND_ANIMATION_BYTES_MAX: usize = 64 * 1024 * 1024; +const CUSTOM_BACKGROUND_FRAME_DELAY_MS_MIN: u64 = 20; +const CUSTOM_BACKGROUND_FRAME_DELAY_MS_MAX: u64 = 10_000; +const CUSTOM_BACKGROUND_FRAME_DELAY_MS_DEFAULT: u64 = 100; +const STATIC_BACKGROUND_EXTENSIONS: &[&str] = &["jpg", "jpeg", "png", "webp"]; +const ANIMATED_BACKGROUND_EXTENSIONS: &[&str] = &["gif", "webp"]; +const VIDEO_BACKGROUND_EXTENSIONS: &[&str] = &["mp4"]; +pub const CAMERA_EFFECT_STRENGTH_MAX: u32 = 100; +pub const CAMERA_EFFECT_STRENGTH_DEFAULT: u32 = 50; +const BLUR_STRENGTH_FACTOR_PERMILLE_MIN: u32 = 250; +const BLUR_STRENGTH_FACTOR_PERMILLE_MID: u32 = 1000; +const BLUR_STRENGTH_FACTOR_PERMILLE_MAX: u32 = 2500; +const BLUR_STRENGTH_FACTOR_PERMILLE_SCALE: u32 = 1000; +const BLUR_RADIUS_SCALED_MIN: usize = 1; +const BLUR_RADIUS_SCALED_MAX: usize = 60; +const BLUR_DOWNSAMPLE: usize = 2; +const BLUR_BOX_PASSES: usize = 3; +const BLUR_PASS_RADIUS_NUMERATOR: usize = 5; +const BLUR_PASS_RADIUS_DENOMINATOR: usize = 17; +const BLUR_WEIGHT_EPSILON: f32 = 0.5; +const PORTRAIT_ELLIPSE_INNER_PERMILLE: i64 = 900; +const PORTRAIT_ELLIPSE_OUTER_PERMILLE: i64 = 1200; + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum CameraBackgroundMode { + None, + Blur, + Custom, +} + +impl CameraBackgroundMode { + pub fn from_bridge_value(value: Option<&str>) -> Result { + match value.map(str::trim).filter(|value| !value.is_empty()) { + None | Some("none") | Some("non") => Ok(Self::None), + Some("blur") => Ok(Self::Blur), + Some("custom") => Ok(Self::Custom), + Some(_) => Err("unsupported native camera background mode".to_string()), + } + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum CameraBackgroundCustomMediaKind { + Static, + Animated, + Video, +} + +impl CameraBackgroundCustomMediaKind { + pub fn from_bridge_value(value: Option<&str>) -> Result, String> { + match value.map(str::trim).filter(|value| !value.is_empty()) { + None => Ok(None), + Some("static") => Ok(Some(Self::Static)), + Some("animated") => Ok(Some(Self::Animated)), + Some("video") => Ok(Some(Self::Video)), + Some(_) => Err("unsupported native custom camera background media kind".to_string()), + } + } +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct CameraBackgroundCustomSource { + pub path: String, + pub media_kind: CameraBackgroundCustomMediaKind, +} + +#[derive(Default)] +struct LiveBackgroundState { + frame: Option, + spare: Option, + target_dims: Option<(u32, u32)>, +} + +#[derive(Clone, Default)] +pub struct CameraBackgroundLiveSlot(Arc>); + +impl PartialEq for CameraBackgroundLiveSlot { + fn eq(&self, other: &Self) -> bool { + Arc::ptr_eq(&self.0, &other.0) + } +} + +impl Eq for CameraBackgroundLiveSlot {} + +impl std::fmt::Debug for CameraBackgroundLiveSlot { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("CameraBackgroundLiveSlot") + } +} + +impl CameraBackgroundLiveSlot { + pub fn new() -> Self { + Self::default() + } + + #[cfg(test)] + pub fn store(&self, frame: crate::yuv::I420) -> bool { + assert!(frame.width >= 2); + assert!(frame.height >= 2); + let target_dims = self.0.lock().target_dims; + let frame = match target_dims { + Some((width, height)) if (frame.width, frame.height) != (width, height) => { + match resize_i420(&frame, width, height) { + Some(resized) => resized, + None => return false, + } + } + _ => frame, + }; + self.0.lock().frame = Some(frame); + true + } + + pub fn store_tight_i420(&self, data: &[u8], width: u32, height: u32) -> bool { + assert!(width >= 2); + assert!(height >= 2); + let mut state = self.0.lock(); + if let Some((target_width, target_height)) = state.target_dims + && (width, height) != (target_width, target_height) + { + drop(state); + return self.store_resized_tight_i420(data, width, height, target_width, target_height); + } + let reuse = matches!(state.frame.as_ref(), Some(frame) if frame.width == width && frame.height == height); + if !reuse { + state.frame = crate::yuv::I420::new(width, height); + } + let Some(frame) = state.frame.as_mut() else { + return false; + }; + crate::yuv::copy_tight_i420_into(data, width, height, frame) + } + + fn store_resized_tight_i420( + &self, + data: &[u8], + width: u32, + height: u32, + target_width: u32, + target_height: u32, + ) -> bool { + assert!(target_width >= 2); + assert!(target_height >= 2); + let spare = self.0.lock().spare.take(); + let mut resized = match spare { + Some(frame) if frame.width == target_width && frame.height == target_height => frame, + _ => match crate::yuv::I420::new(target_width, target_height) { + Some(frame) => frame, + None => return false, + }, + }; + if !resize_tight_i420_into(data, width, height, &mut resized) { + return false; + } + let mut state = self.0.lock(); + if state.target_dims != Some((target_width, target_height)) { + return false; + } + state.spare = state + .frame + .replace(resized) + .filter(|frame| frame.width == target_width && frame.height == target_height); + true + } + + pub fn clear(&self) { + self.0.lock().frame = None; + assert!(self.0.lock().frame.is_none()); + } + + fn set_target_dims(&self, width: u32, height: u32) { + assert!(width >= 2); + assert!(height >= 2); + let mismatched = { + let mut state = self.0.lock(); + state.target_dims = Some((width, height)); + match state.frame.as_ref() { + Some(frame) if frame.width != width || frame.height != height => state.frame.take(), + _ => None, + } + }; + let Some(frame) = mismatched else { + return; + }; + let Some(resized) = resize_i420(&frame, width, height) else { + return; + }; + let mut state = self.0.lock(); + if state.target_dims == Some((width, height)) && state.frame.is_none() { + state.frame = Some(resized); + } + } + + fn with_latest(&self, callback: impl FnOnce(Option<&crate::yuv::I420>) -> R) -> R { + let state = self.0.lock(); + callback(state.frame.as_ref()) + } +} + +fn resize_tight_i420_into( + data: &[u8], + width: u32, + height: u32, + dst: &mut crate::yuv::I420, +) -> bool { + assert!(width >= 2); + assert!(height >= 2); + assert!(dst.width >= 2); + assert!(dst.height >= 2); + let w = width as usize; + let h = height as usize; + let cw = w / 2; + let ch = h / 2; + if data.len() < w * h + 2 * cw * ch { + return false; + } + let (y, chroma) = data.split_at(w * h); + let (u, v) = chroma.split_at(cw * ch); + let dw = dst.width as usize; + let dh = dst.height as usize; + crate::person_segmentation::resize_mask_bilinear(y, w, h, w, &mut dst.y, dw, dh); + crate::person_segmentation::resize_mask_bilinear(u, cw, ch, cw, &mut dst.u, dw / 2, dh / 2); + crate::person_segmentation::resize_mask_bilinear(v, cw, ch, cw, &mut dst.v, dw / 2, dh / 2); + true +} + +fn resize_i420(src: &crate::yuv::I420, width: u32, height: u32) -> Option { + let mut dst = crate::yuv::I420::new(width, height)?; + let src_width = src.width as usize; + let src_height = src.height as usize; + let dst_width = width as usize; + let dst_height = height as usize; + crate::person_segmentation::resize_mask_bilinear( + &src.y, src_width, src_height, src_width, &mut dst.y, dst_width, dst_height, + ); + crate::person_segmentation::resize_mask_bilinear( + &src.u, + src_width / 2, + src_height / 2, + src_width / 2, + &mut dst.u, + dst_width / 2, + dst_height / 2, + ); + crate::person_segmentation::resize_mask_bilinear( + &src.v, + src_width / 2, + src_height / 2, + src_width / 2, + &mut dst.v, + dst_width / 2, + dst_height / 2, + ); + Some(dst) +} + +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct CameraBackgroundConfig { + pub mode: CameraBackgroundMode, + pub custom: Option, + pub live_background: Option, + pub blur_strength: u32, +} + +impl Default for CameraBackgroundConfig { + fn default() -> Self { + Self { + mode: CameraBackgroundMode::None, + custom: None, + live_background: None, + blur_strength: CAMERA_EFFECT_STRENGTH_DEFAULT, + } + } +} + +pub fn clamp_camera_effect_strength(value: Option) -> u32 { + let strength = value + .unwrap_or(CAMERA_EFFECT_STRENGTH_DEFAULT) + .min(CAMERA_EFFECT_STRENGTH_MAX); + assert!(strength <= CAMERA_EFFECT_STRENGTH_MAX); + strength +} + +impl CameraBackgroundConfig { + pub fn from_bridge_values( + mode: Option<&str>, + custom_media_path: Option<&str>, + custom_media_kind: Option<&str>, + blur_strength: Option, + ) -> Result { + let mode = CameraBackgroundMode::from_bridge_value(mode)?; + let media_kind = CameraBackgroundCustomMediaKind::from_bridge_value(custom_media_kind)?; + let blur_strength = clamp_camera_effect_strength(blur_strength); + if mode != CameraBackgroundMode::Custom { + return Ok(Self { + mode, + custom: None, + live_background: None, + blur_strength, + }); + } + let path = custom_media_path + .map(str::trim) + .filter(|path| !path.is_empty()) + .ok_or_else(|| "custom native camera background requires a media path".to_string())?; + let media_kind = media_kind + .ok_or_else(|| "custom native camera background requires a media kind".to_string())?; + Ok(Self { + mode, + custom: Some(CameraBackgroundCustomSource { + path: path.to_string(), + media_kind, + }), + live_background: None, + blur_strength, + }) + } + + pub fn ensure_supported_for_publish(&self) -> Result<(), String> { + if self.mode != CameraBackgroundMode::Custom { + return Ok(()); + } + let source = self + .custom + .as_ref() + .ok_or_else(|| "custom native camera background requires a media source".to_string())?; + match source.media_kind { + CameraBackgroundCustomMediaKind::Static => validate_static_background_source(source), + CameraBackgroundCustomMediaKind::Animated => { + validate_timed_background_source(source, ANIMATED_BACKGROUND_EXTENSIONS, "animated") + } + CameraBackgroundCustomMediaKind::Video => { + validate_timed_background_source(source, VIDEO_BACKGROUND_EXTENSIONS, "video") + } + } + } +} + +enum CustomBackgroundSource { + Static(crate::yuv::I420), + Animated(AnimatedBackground), + Live(CameraBackgroundLiveSlot), +} + +struct AnimatedBackground { + frames: Vec, + schedule_end_us: Vec, + anchor_timestamp_us: Option, +} + +impl AnimatedBackground { + fn new(frames_with_delays: Vec<(crate::yuv::I420, u64)>) -> Option { + if frames_with_delays.is_empty() { + return None; + } + assert!(frames_with_delays.len() <= CUSTOM_BACKGROUND_ANIMATION_FRAMES_MAX); + let mut frames = Vec::with_capacity(frames_with_delays.len()); + let mut schedule_end_us = Vec::with_capacity(frames_with_delays.len()); + let mut total_us: u64 = 0; + for (frame, delay_ms) in frames_with_delays { + total_us = total_us.saturating_add(clamp_animation_delay_ms(delay_ms) * 1_000); + frames.push(frame); + schedule_end_us.push(total_us); + } + assert_eq!(frames.len(), schedule_end_us.len()); + assert!(total_us > 0); + Some(Self { + frames, + schedule_end_us, + anchor_timestamp_us: None, + }) + } + + fn frame_at(&mut self, timestamp_us: i64) -> &crate::yuv::I420 { + assert!(!self.frames.is_empty()); + assert_eq!(self.frames.len(), self.schedule_end_us.len()); + let anchor = *self.anchor_timestamp_us.get_or_insert(timestamp_us); + let elapsed_us = timestamp_us.saturating_sub(anchor).max(0) as u64; + let index = animation_frame_index(&self.schedule_end_us, elapsed_us); + &self.frames[index] + } +} + +fn clamp_animation_delay_ms(delay_ms: u64) -> u64 { + if delay_ms == 0 { + return CUSTOM_BACKGROUND_FRAME_DELAY_MS_DEFAULT; + } + delay_ms.clamp( + CUSTOM_BACKGROUND_FRAME_DELAY_MS_MIN, + CUSTOM_BACKGROUND_FRAME_DELAY_MS_MAX, + ) +} + +fn animation_frame_index(schedule_end_us: &[u64], elapsed_us: u64) -> usize { + assert!(!schedule_end_us.is_empty()); + assert!(schedule_end_us.len() <= CUSTOM_BACKGROUND_ANIMATION_FRAMES_MAX); + let total_us = *schedule_end_us.last().unwrap(); + assert!(total_us > 0); + let looped_us = elapsed_us % total_us; + schedule_end_us + .iter() + .position(|end_us| looped_us < *end_us) + .unwrap_or(schedule_end_us.len() - 1) +} + +pub struct CameraBackgroundTransform { + config: CameraBackgroundConfig, + width: u32, + height: u32, + radius_pass_luma: usize, + radius_pass_chroma: usize, + custom: Option, + mask_source: Option>, + person_mask: Vec, + person_mask_scratch: Vec, + refiner: crate::mask_refine::MaskRefiner, + blur_scratch: BlurScratch, +} + +impl CameraBackgroundTransform { + pub fn new(config: CameraBackgroundConfig, width: u32, height: u32) -> Result { + let mask_source = if config.mode == CameraBackgroundMode::None { + None + } else { + crate::person_segmentation::create_person_mask_source(width, height) + }; + Self::with_mask_source(config, width, height, mask_source) + } + + pub fn with_mask_source( + config: CameraBackgroundConfig, + width: u32, + height: u32, + mask_source: Option>, + ) -> Result { + assert!(width >= 2); + assert!(height >= 2); + if width as usize > FRAME_EDGE_MAX || height as usize > FRAME_EDGE_MAX { + return Err("camera frame dimensions exceed the background transform cap".to_string()); + } + let radius = scaled_blur_radius(blur_radius(width, height), config.blur_strength); + assert!(radius > 0); + let radius_pass_luma = blur_pass_radius(radius); + let radius_pass_chroma = (radius_pass_luma / 2).max(1); + let custom = load_custom_background_source(&config, width, height)?; + let plane_len = (width as usize) * (height as usize); + let mut person_mask = vec![crate::person_segmentation::PERSON_MASK_BACKGROUND; plane_len]; + fill_portrait_ellipse_mask(&mut person_mask, width as usize, height as usize); + Ok(Self { + config, + width, + height, + radius_pass_luma, + radius_pass_chroma, + custom, + mask_source, + person_mask, + person_mask_scratch: vec![0; plane_len], + refiner: crate::mask_refine::MaskRefiner::new(width as usize, height as usize), + blur_scratch: BlurScratch::new(width as usize, height as usize), + }) + } + + pub fn dimensions(&self) -> (u32, u32) { + (self.width, self.height) + } + + fn refresh_person_mask(&mut self, frame: &crate::yuv::I420) { + let Some(source) = self.mask_source.as_mut() else { + return; + }; + let plane_len = (self.width as usize) * (self.height as usize); + assert_eq!(self.person_mask.len(), plane_len); + assert_eq!(self.person_mask_scratch.len(), plane_len); + if source.mask_into(frame, &mut self.person_mask_scratch) { + self.refiner.refine(&frame.y, &mut self.person_mask_scratch); + std::mem::swap(&mut self.person_mask, &mut self.person_mask_scratch); + } + } + + pub fn apply_i420(&mut self, frame: &mut crate::yuv::I420, timestamp_us: i64) -> bool { + assert!(frame.width >= 2); + assert!(frame.height >= 2); + if self.config.mode == CameraBackgroundMode::None { + return true; + } + if frame.width != self.width || frame.height != self.height { + return false; + } + self.refresh_person_mask(frame); + if self.config.mode == CameraBackgroundMode::Custom { + let Some(custom) = self.custom.as_mut() else { + return false; + }; + return apply_custom_background(frame, custom, &self.person_mask, timestamp_us); + } + blur_i420_background( + frame, + &self.person_mask, + self.radius_pass_luma, + self.radius_pass_chroma, + &mut self.blur_scratch, + ) + } +} + +fn apply_custom_background( + frame: &mut crate::yuv::I420, + custom: &mut CustomBackgroundSource, + mask: &[u8], + timestamp_us: i64, +) -> bool { + assert!(frame.width >= 2); + assert!(frame.height >= 2); + match custom { + CustomBackgroundSource::Static(background) => { + composite_i420_background(frame, background, mask); + true + } + CustomBackgroundSource::Animated(animated) => { + let background = animated.frame_at(timestamp_us); + composite_i420_background(frame, background, mask); + true + } + CustomBackgroundSource::Live(slot) => { + slot.with_latest(|background| { + if let Some(background) = background + && background.width == frame.width + && background.height == frame.height + { + composite_i420_background(frame, background, mask); + } + }); + true + } + } +} + +type CameraBackgroundTransformFactory = + fn(CameraBackgroundConfig, u32, u32) -> Result; + +pub struct CameraBackgroundStage { + config: CameraBackgroundConfig, + factory: CameraBackgroundTransformFactory, + transform: Option, + attempted_dims: Option<(u32, u32)>, +} + +impl CameraBackgroundStage { + pub fn new(config: CameraBackgroundConfig) -> Self { + Self::with_factory(config, CameraBackgroundTransform::new) + } + + pub fn with_factory( + config: CameraBackgroundConfig, + factory: CameraBackgroundTransformFactory, + ) -> Self { + Self { + config, + factory, + transform: None, + attempted_dims: None, + } + } + + pub fn is_enabled(&self) -> bool { + self.config.mode != CameraBackgroundMode::None + } + + pub fn apply_i420(&mut self, frame: &mut crate::yuv::I420, timestamp_us: i64) -> bool { + assert!(frame.width >= 2); + assert!(frame.height >= 2); + if !self.is_enabled() { + return true; + } + let dims = (frame.width, frame.height); + let needs_transform = match self.transform.as_ref() { + Some(transform) => transform.dimensions() != dims, + None => true, + }; + if needs_transform { + if self.attempted_dims == Some(dims) { + return true; + } + self.attempted_dims = Some(dims); + self.transform = (self.factory)(self.config.clone(), dims.0, dims.1).ok(); + } + let Some(transform) = self.transform.as_mut() else { + return true; + }; + if transform.dimensions() != dims { + return true; + } + transform.apply_i420(frame, timestamp_us) + } +} + +fn validate_static_background_source(source: &CameraBackgroundCustomSource) -> Result<(), String> { + assert!(source.media_kind == CameraBackgroundCustomMediaKind::Static); + validate_background_file(source, STATIC_BACKGROUND_EXTENSIONS, "static")?; + let reader = ImageReader::open(&source.path) + .map_err(|_| "static native camera background is not readable".to_string())?; + let reader = reader + .with_guessed_format() + .map_err(|_| "static native camera background format is invalid".to_string())?; + let dimensions = reader + .into_dimensions() + .map_err(|_| "static native camera background dimensions are invalid".to_string())?; + if dimensions.0 < 2 || dimensions.1 < 2 { + return Err("static native camera background dimensions are too small".to_string()); + } + Ok(()) +} + +fn validate_timed_background_source( + source: &CameraBackgroundCustomSource, + extensions: &[&str], + label: &str, +) -> Result<(), String> { + assert!(!extensions.is_empty()); + assert!(!label.trim().is_empty()); + validate_background_file(source, extensions, label) +} + +fn validate_background_file( + source: &CameraBackgroundCustomSource, + extensions: &[&str], + label: &str, +) -> Result<(), String> { + assert!(!extensions.is_empty()); + assert!(!label.trim().is_empty()); + let metadata = std::fs::metadata(&source.path) + .map_err(|_| format!("{label} native camera background is not readable"))?; + if !metadata.is_file() { + return Err(format!("{label} native camera background is not a file")); + } + if metadata.len() == 0 || metadata.len() > MAX_CUSTOM_BACKGROUND_BYTES { + return Err(format!("{label} native camera background size is invalid")); + } + let extension = std::path::Path::new(&source.path) + .extension() + .and_then(|extension| extension.to_str()) + .unwrap_or("") + .to_ascii_lowercase(); + if !extensions.iter().any(|candidate| *candidate == extension) { + return Err(format!( + "{label} native camera background type is unsupported" + )); + } + Ok(()) +} + +fn load_custom_background_source( + config: &CameraBackgroundConfig, + width: u32, + height: u32, +) -> Result, String> { + if config.mode != CameraBackgroundMode::Custom { + return Ok(None); + } + let source = config + .custom + .as_ref() + .ok_or_else(|| "custom native camera background requires a media source".to_string())?; + match source.media_kind { + CameraBackgroundCustomMediaKind::Video => { + let slot = config.live_background.clone().unwrap_or_default(); + slot.set_target_dims(width, height); + Ok(Some(CustomBackgroundSource::Live(slot))) + } + CameraBackgroundCustomMediaKind::Animated => { + let frames = decode_animated_background_frames( + &source.path, + width, + height, + CUSTOM_BACKGROUND_ANIMATION_FRAMES_MAX, + CUSTOM_BACKGROUND_ANIMATION_BYTES_MAX, + ); + if let Some(animated) = AnimatedBackground::new(frames) { + return Ok(Some(CustomBackgroundSource::Animated(animated))); + } + Ok(Some(CustomBackgroundSource::Static( + decode_static_background_i420(&source.path, width, height)?, + ))) + } + CameraBackgroundCustomMediaKind::Static => Ok(Some(CustomBackgroundSource::Static( + decode_static_background_i420(&source.path, width, height)?, + ))), + } +} + +fn decode_static_background_i420( + path: &str, + width: u32, + height: u32, +) -> Result { + let image = ImageReader::open(path) + .map_err(|_| "custom native camera background is not readable".to_string())? + .with_guessed_format() + .map_err(|_| "custom native camera background format is invalid".to_string())? + .decode() + .map_err(|_| "custom native camera background decode failed".to_string())?; + let resized = image.resize_exact(width, height, FilterType::Triangle); + crate::yuv::rgb_to_i420(&resized.to_rgb8().into_raw(), width, height) + .ok_or_else(|| "custom native camera background conversion failed".to_string()) +} + +fn open_animation_frames(path: &str) -> Option> { + use image::codecs::gif::GifDecoder; + use image::codecs::webp::WebPDecoder; + let format = ImageReader::open(path) + .ok()? + .with_guessed_format() + .ok()? + .format()?; + let reader = std::io::BufReader::new(std::fs::File::open(path).ok()?); + match format { + image::ImageFormat::Gif => Some(GifDecoder::new(reader).ok()?.into_frames()), + image::ImageFormat::WebP => { + let decoder = WebPDecoder::new(reader).ok()?; + if !decoder.has_animation() { + return None; + } + Some(decoder.into_frames()) + } + _ => None, + } +} + +fn animation_frame_delay_ms(delay: image::Delay) -> u64 { + let (numer, denom) = delay.numer_denom_ms(); + if denom == 0 { + return CUSTOM_BACKGROUND_FRAME_DELAY_MS_DEFAULT; + } + clamp_animation_delay_ms((u64::from(numer) + u64::from(denom) / 2) / u64::from(denom)) +} + +fn decode_animated_background_frames( + path: &str, + width: u32, + height: u32, + frames_max: usize, + bytes_max: usize, +) -> Vec<(crate::yuv::I420, u64)> { + assert!(frames_max >= 1); + assert!(frames_max <= CUSTOM_BACKGROUND_ANIMATION_FRAMES_MAX); + assert!(bytes_max >= 1); + let Some(frames) = open_animation_frames(path) else { + return Vec::new(); + }; + let mut decoded = Vec::new(); + let mut decoded_bytes: usize = 0; + for frame in frames { + let Ok(frame) = frame else { + break; + }; + let delay_ms = animation_frame_delay_ms(frame.delay()); + let buffer = frame.into_buffer(); + decoded_bytes = decoded_bytes.saturating_add(buffer.as_raw().len()); + if decoded_bytes > bytes_max { + break; + } + let resized = image::DynamicImage::ImageRgba8(buffer) + .resize_exact(width, height, FilterType::Triangle) + .to_rgb8() + .into_raw(); + let Some(i420) = crate::yuv::rgb_to_i420(&resized, width, height) else { + continue; + }; + decoded.push((i420, delay_ms)); + if decoded.len() >= frames_max { + break; + } + } + assert!(decoded.len() <= frames_max); + decoded +} + +fn blur_radius(width: u32, height: u32) -> usize { + let radius = width.min(height) / 32; + radius.clamp(4, 24) as usize +} + +const _: () = assert!( + 24 * (BLUR_STRENGTH_FACTOR_PERMILLE_MAX as usize) + / (BLUR_STRENGTH_FACTOR_PERMILLE_SCALE as usize) + == BLUR_RADIUS_SCALED_MAX +); + +fn blur_strength_factor_permille(strength: u32) -> u32 { + assert!(strength <= CAMERA_EFFECT_STRENGTH_MAX); + if strength <= CAMERA_EFFECT_STRENGTH_DEFAULT { + let span = BLUR_STRENGTH_FACTOR_PERMILLE_MID - BLUR_STRENGTH_FACTOR_PERMILLE_MIN; + BLUR_STRENGTH_FACTOR_PERMILLE_MIN + span * strength / CAMERA_EFFECT_STRENGTH_DEFAULT + } else { + let span = BLUR_STRENGTH_FACTOR_PERMILLE_MAX - BLUR_STRENGTH_FACTOR_PERMILLE_MID; + let above_default = strength - CAMERA_EFFECT_STRENGTH_DEFAULT; + BLUR_STRENGTH_FACTOR_PERMILLE_MID + + span * above_default / (CAMERA_EFFECT_STRENGTH_MAX - CAMERA_EFFECT_STRENGTH_DEFAULT) + } +} + +pub(crate) fn scaled_blur_radius(base_radius: usize, strength: u32) -> usize { + assert!(base_radius >= BLUR_RADIUS_SCALED_MIN); + assert!(base_radius <= BLUR_RADIUS_SCALED_MAX); + let strength = strength.min(CAMERA_EFFECT_STRENGTH_MAX); + let factor = blur_strength_factor_permille(strength); + assert!(factor >= BLUR_STRENGTH_FACTOR_PERMILLE_MIN); + assert!(factor <= BLUR_STRENGTH_FACTOR_PERMILLE_MAX); + let scaled = base_radius * (factor as usize) / (BLUR_STRENGTH_FACTOR_PERMILLE_SCALE as usize); + scaled.clamp(BLUR_RADIUS_SCALED_MIN, BLUR_RADIUS_SCALED_MAX) +} + +#[derive(Clone, Copy)] +pub(crate) struct PlaneMask<'mask> { + values: &'mask [u8], + width: usize, + scale: usize, +} + +pub(crate) fn plane_mask(mask: &[u8], frame_width: usize, scale: usize) -> PlaneMask<'_> { + assert!(scale == 1 || scale == 2); + assert!(frame_width >= 1); + PlaneMask { + values: mask, + width: frame_width, + scale, + } +} + +pub(crate) fn blur_pass_radius(scaled_radius: usize) -> usize { + assert!(scaled_radius >= BLUR_RADIUS_SCALED_MIN); + assert!(scaled_radius <= BLUR_RADIUS_SCALED_MAX); + (scaled_radius * BLUR_PASS_RADIUS_NUMERATOR / BLUR_PASS_RADIUS_DENOMINATOR).max(1) +} + +pub(crate) struct BlurScratch { + value: Vec, + value_alternate: Vec, + weight: Vec, + weight_alternate: Vec, + box_scratch: Vec, + column_fixed: Vec, +} + +impl BlurScratch { + pub(crate) fn new(width: usize, height: usize) -> Self { + assert!(width >= 2); + assert!(height >= 2); + let low_len = width.div_ceil(BLUR_DOWNSAMPLE) * height.div_ceil(BLUR_DOWNSAMPLE); + Self { + value: vec![0.0; low_len], + value_alternate: vec![0.0; low_len], + weight: vec![0.0; low_len], + weight_alternate: vec![0.0; low_len], + box_scratch: vec![0.0; low_len], + column_fixed: vec![0; width], + } + } +} + +pub(crate) fn blur_i420_background( + frame: &mut crate::yuv::I420, + mask: &[u8], + radius_pass_luma: usize, + radius_pass_chroma: usize, + scratch: &mut BlurScratch, +) -> bool { + assert!(radius_pass_luma >= 1); + assert!(radius_pass_chroma >= 1); + let width = frame.width as usize; + let height = frame.height as usize; + if frame.y.len() != width * height { + return false; + } + if frame.u.len() != (width / 2) * (height / 2) || frame.v.len() != frame.u.len() { + return false; + } + let luma_mask = plane_mask(mask, width, 1); + let chroma_mask = plane_mask(mask, width, 2); + blur_plane_masked( + &mut frame.y, + width, + height, + luma_mask, + radius_pass_luma, + scratch, + ); + let chroma_width = width / 2; + let chroma_height = height / 2; + blur_plane_masked( + &mut frame.u, + chroma_width, + chroma_height, + chroma_mask, + radius_pass_chroma, + scratch, + ); + blur_plane_masked( + &mut frame.v, + chroma_width, + chroma_height, + chroma_mask, + radius_pass_chroma, + scratch, + ); + true +} + +pub(crate) fn blur_plane_masked( + plane: &mut [u8], + plane_width: usize, + plane_height: usize, + mask: PlaneMask<'_>, + radius_pass: usize, + scratch: &mut BlurScratch, +) { + assert!(plane_width >= 1); + assert!(plane_height >= 1); + assert_eq!(plane.len(), plane_width * plane_height); + assert!(radius_pass >= 1); + let low_width = plane_width.div_ceil(BLUR_DOWNSAMPLE); + let low_height = plane_height.div_ceil(BLUR_DOWNSAMPLE); + assert!(scratch.value.len() >= low_width * low_height); + downsample_masked_plane(plane, plane_width, plane_height, mask, scratch); + for _ in 0..BLUR_BOX_PASSES { + crate::mask_refine::box_filter_low( + &scratch.value, + &mut scratch.box_scratch, + &mut scratch.value_alternate, + low_width, + low_height, + radius_pass, + ); + std::mem::swap(&mut scratch.value, &mut scratch.value_alternate); + crate::mask_refine::box_filter_low( + &scratch.weight, + &mut scratch.box_scratch, + &mut scratch.weight_alternate, + low_width, + low_height, + radius_pass, + ); + std::mem::swap(&mut scratch.weight, &mut scratch.weight_alternate); + } + normalize_blurred_background(scratch, low_width * low_height); + composite_blurred_background( + plane, + plane_width, + plane_height, + mask, + low_width, + low_height, + scratch, + ); +} + +fn normalize_blurred_background(scratch: &mut BlurScratch, low_len: usize) { + assert!(scratch.value.len() >= low_len); + assert!(scratch.weight.len() >= low_len); + for (value, weight) in scratch.value[..low_len] + .iter_mut() + .zip(scratch.weight[..low_len].iter()) + { + if *weight > BLUR_WEIGHT_EPSILON { + *value = (*value / *weight).clamp(0.0, 255.0); + } else { + *value = 0.0; + } + } +} + +fn downsample_masked_plane( + plane: &[u8], + plane_width: usize, + plane_height: usize, + mask: PlaneMask<'_>, + scratch: &mut BlurScratch, +) { + let low_width = plane_width.div_ceil(BLUR_DOWNSAMPLE); + let low_height = plane_height.div_ceil(BLUR_DOWNSAMPLE); + assert!(scratch.value.len() >= low_width * low_height); + assert!(scratch.weight.len() >= low_width * low_height); + for low_y in 0..low_height { + let y_start = low_y * BLUR_DOWNSAMPLE; + let y_end = (y_start + BLUR_DOWNSAMPLE).min(plane_height); + for low_x in 0..low_width { + let x_start = low_x * BLUR_DOWNSAMPLE; + let x_end = (x_start + BLUR_DOWNSAMPLE).min(plane_width); + let mut value_sum: f32 = 0.0; + let mut weight_sum: f32 = 0.0; + for y in y_start..y_end { + let row = y * plane_width; + let mask_row = y * mask.scale * mask.width; + for x in x_start..x_end { + let person = mask.values[mask_row + x * mask.scale]; + let weight = f32::from(255 - person); + value_sum += f32::from(plane[row + x]) * weight; + weight_sum += weight; + } + } + let low_offset = low_y * low_width + low_x; + scratch.value[low_offset] = value_sum; + scratch.weight[low_offset] = weight_sum; + } + } +} + +fn composite_blurred_background( + plane: &mut [u8], + plane_width: usize, + plane_height: usize, + mask: PlaneMask<'_>, + low_width: usize, + low_height: usize, + scratch: &mut BlurScratch, +) { + assert!(low_width >= 1); + assert!(low_height >= 1); + assert!(scratch.column_fixed.len() >= plane_width); + for (x, slot) in scratch.column_fixed[..plane_width].iter_mut().enumerate() { + *slot = crate::mask_refine::bilinear_fixed_coord(x, plane_width, low_width); + } + for y in 0..plane_height { + let row_fixed = crate::mask_refine::bilinear_fixed_coord(y, plane_height, low_height); + let sy = (row_fixed / 256) as usize; + let fy = (row_fixed % 256) as f32 / 256.0; + let sy_next = (sy + 1).min(low_height - 1); + let row = y * plane_width; + let mask_row = y * mask.scale * mask.width; + for x in 0..plane_width { + let person = mask.values[mask_row + x * mask.scale]; + if person == crate::person_segmentation::PERSON_MASK_PERSON { + continue; + } + let col_fixed = scratch.column_fixed[x]; + let sx = (col_fixed / 256) as usize; + let fx = (col_fixed % 256) as f32 / 256.0; + let sx_next = (sx + 1).min(low_width - 1); + let value = crate::mask_refine::bilinear_sample( + &scratch.value, + low_width, + sx, + sx_next, + sy, + sy_next, + fx, + fy, + ); + let background = value.clamp(0.0, 255.0) as u16; + let alpha = u16::from(person); + let offset = row + x; + plane[offset] = + ((u16::from(plane[offset]) * alpha + background * (255 - alpha)) / 255) as u8; + } + } +} + +fn composite_i420_background(frame: &mut crate::yuv::I420, custom: &crate::yuv::I420, mask: &[u8]) { + assert_eq!(frame.width, custom.width); + assert_eq!(frame.height, custom.height); + let width = frame.width as usize; + let height = frame.height as usize; + let luma_mask = plane_mask(mask, width, 1); + let chroma_mask = plane_mask(mask, width, 2); + composite_masked_plane(&mut frame.y, &custom.y, width, height, luma_mask); + composite_masked_plane(&mut frame.u, &custom.u, width / 2, height / 2, chroma_mask); + composite_masked_plane(&mut frame.v, &custom.v, width / 2, height / 2, chroma_mask); +} + +pub(crate) fn composite_masked_plane( + plane: &mut [u8], + background: &[u8], + width: usize, + height: usize, + mask: PlaneMask<'_>, +) { + assert!(width >= 1); + assert!(height >= 1); + assert_eq!(plane.len(), width * height); + assert_eq!(background.len(), width * height); + assert!(mask.scale == 1 || mask.scale == 2); + assert!(mask.width >= width * mask.scale); + assert!(mask.values.len() >= mask.width * height * mask.scale); + + for y in 0..height { + let mask_row = y * mask.scale * mask.width; + for x in 0..width { + let person = u16::from(mask.values[mask_row + x * mask.scale]); + if person == u16::from(crate::person_segmentation::PERSON_MASK_PERSON) { + continue; + } + let offset = y * width + x; + if person == u16::from(crate::person_segmentation::PERSON_MASK_BACKGROUND) { + plane[offset] = background[offset]; + continue; + } + let background_alpha = 255 - person; + plane[offset] = ((u16::from(plane[offset]) * person + + u16::from(background[offset]) * background_alpha) + / 255) as u8; + } + } +} + +pub(crate) fn fill_portrait_ellipse_mask(mask: &mut [u8], width: usize, height: usize) { + assert!(width >= 1); + assert!(height >= 1); + assert!(mask.len() >= width * height); + let cx = (width as i64) / 2; + let cy = ((height as i64) * 9) / 20; + let rx = ((width as i64) * 3).max(1) / 10; + let ry = ((height as i64) * 9).max(1) / 20; + let inner = PORTRAIT_ELLIPSE_INNER_PERMILLE; + let outer = PORTRAIT_ELLIPSE_OUTER_PERMILLE; + assert!(outer > inner); + + for y in 0..height { + let dy = ((y as i64 - cy) * 1000) / ry.max(1); + for x in 0..width { + let dx = ((x as i64 - cx) * 1000) / rx.max(1); + let distance = (dx * dx + dy * dy) / 1000; + let offset = y * width + x; + if distance <= inner { + mask[offset] = crate::person_segmentation::PERSON_MASK_PERSON; + } else if distance >= outer { + mask[offset] = crate::person_segmentation::PERSON_MASK_BACKGROUND; + } else { + let ramp = (distance - inner) * 255 / (outer - inner); + mask[offset] = (255 - ramp) as u8; + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::person_segmentation::{ + PERSON_MASK_BACKGROUND, PERSON_MASK_PERSON, PersonMaskSource, + }; + use image::{ImageBuffer, Rgb}; + use std::path::PathBuf; + + struct LeftHalfPersonMask { + responses: Vec, + calls: usize, + } + + impl LeftHalfPersonMask { + fn new(responses: Vec) -> Self { + assert!(!responses.is_empty()); + Self { + responses, + calls: 0, + } + } + } + + impl PersonMaskSource for LeftHalfPersonMask { + fn mask_into(&mut self, frame: &crate::yuv::I420, mask: &mut [u8]) -> bool { + assert!(self.calls < self.responses.len()); + let succeed = self.responses[self.calls]; + self.calls += 1; + if !succeed { + return false; + } + let width = frame.width as usize; + let height = frame.height as usize; + assert!(mask.len() >= width * height); + for y in 0..height { + for x in 0..width { + mask[y * width + x] = if x < width / 2 { + PERSON_MASK_PERSON + } else { + PERSON_MASK_BACKGROUND + }; + } + } + true + } + } + + fn blur_transform_with_mask( + width: u32, + height: u32, + responses: Vec, + ) -> CameraBackgroundTransform { + CameraBackgroundTransform::with_mask_source( + CameraBackgroundConfig { + mode: CameraBackgroundMode::Blur, + custom: None, + live_background: None, + ..CameraBackgroundConfig::default() + }, + width, + height, + Some(Box::new(LeftHalfPersonMask::new(responses))), + ) + .unwrap() + } + + fn gradient_rgb(width: usize, height: usize) -> Vec { + let mut frame = vec![0u8; width * height * 3]; + for y in 0..height { + for x in 0..width { + let offset = (y * width + x) * 3; + frame[offset] = (x * 4).min(255) as u8; + frame[offset + 1] = (y * 4).min(255) as u8; + frame[offset + 2] = ((x + y) * 2).min(255) as u8; + } + } + frame + } + + fn gradient_i420(width: u32, height: u32) -> crate::yuv::I420 { + let rgb = gradient_rgb(width as usize, height as usize); + crate::yuv::rgb_to_i420(&rgb, width, height).unwrap() + } + + fn write_temp_png(name: &str) -> PathBuf { + let path = std::env::temp_dir().join(format!("fluxer-camera-background-{name}.png")); + let mut image = ImageBuffer::, Vec>::new(4, 4); + for (x, y, pixel) in image.enumerate_pixels_mut() { + *pixel = Rgb([(x * 40) as u8, (y * 40) as u8, 128]); + } + image.save(&path).unwrap(); + path + } + + fn write_temp_bytes(name: &str, extension: &str, bytes: &[u8]) -> PathBuf { + assert!(!name.trim().is_empty()); + assert!(!extension.trim().is_empty()); + assert!(!bytes.is_empty()); + let path = + std::env::temp_dir().join(format!("fluxer-camera-background-{name}.{extension}")); + std::fs::write(&path, bytes).unwrap(); + path + } + + #[test] + fn none_mode_leaves_frame_unchanged() { + let mut transform = CameraBackgroundTransform::new( + CameraBackgroundConfig { + mode: CameraBackgroundMode::None, + custom: None, + live_background: None, + ..CameraBackgroundConfig::default() + }, + 32, + 24, + ) + .unwrap(); + let mut frame = gradient_i420(32, 24); + let original = frame.clone(); + + assert!(transform.apply_i420(&mut frame, 0)); + + assert_eq!(frame.y, original.y); + assert_eq!(frame.u, original.u); + assert_eq!(frame.v, original.v); + } + + #[test] + fn blur_rejects_dimension_changes_without_allocating() { + let mut transform = CameraBackgroundTransform::with_mask_source( + CameraBackgroundConfig { + mode: CameraBackgroundMode::Blur, + custom: None, + live_background: None, + ..CameraBackgroundConfig::default() + }, + 64, + 48, + None, + ) + .unwrap(); + let mut frame = gradient_i420(32, 24); + + assert!(!transform.apply_i420(&mut frame, 0)); + } + + #[test] + fn bridge_values_accept_none_alias_blur_and_custom_media() { + let none = + CameraBackgroundConfig::from_bridge_values(Some("non"), None, None, None).unwrap(); + assert_eq!(none.mode, CameraBackgroundMode::None); + + let blur = + CameraBackgroundConfig::from_bridge_values(Some("blur"), None, None, None).unwrap(); + assert_eq!(blur.mode, CameraBackgroundMode::Blur); + + let custom = CameraBackgroundConfig::from_bridge_values( + Some("custom"), + Some("/tmp/background.webm"), + Some("video"), + None, + ) + .unwrap(); + assert_eq!(custom.mode, CameraBackgroundMode::Custom); + assert_eq!( + custom.custom.as_ref().map(|source| &source.media_kind), + Some(&CameraBackgroundCustomMediaKind::Video) + ); + } + + #[test] + fn custom_background_requires_media_path_and_accepts_timed_media_files() { + assert!( + CameraBackgroundConfig::from_bridge_values(Some("custom"), None, Some("static"), None,) + .is_err() + ); + let gif_path = write_temp_bytes("animated-custom", "gif", b"GIF89a"); + let mp4_path = write_temp_bytes("video-custom", "mp4", b"\0\0\0\x18ftypmp42"); + let animated = CameraBackgroundConfig::from_bridge_values( + Some("custom"), + gif_path.to_str(), + Some("animated"), + None, + ) + .unwrap(); + let video = CameraBackgroundConfig::from_bridge_values( + Some("custom"), + mp4_path.to_str(), + Some("video"), + None, + ) + .unwrap(); + + assert!(animated.ensure_supported_for_publish().is_ok()); + assert!(video.ensure_supported_for_publish().is_ok()); + let _ = std::fs::remove_file(gif_path); + let _ = std::fs::remove_file(mp4_path); + } + + #[test] + fn custom_background_rejects_unsupported_extensions() { + let path = write_temp_bytes("unsupported-custom", "webm", b"not-webm"); + let video = CameraBackgroundConfig::from_bridge_values( + Some("custom"), + path.to_str(), + Some("video"), + None, + ) + .unwrap(); + + let error = video.ensure_supported_for_publish().unwrap_err(); + + assert!(error.contains("type is unsupported")); + let _ = std::fs::remove_file(path); + } + + #[test] + fn blur_mode_transforms_i420_without_rgb_decode() { + let mut transform = CameraBackgroundTransform::with_mask_source( + CameraBackgroundConfig { + mode: CameraBackgroundMode::Blur, + custom: None, + live_background: None, + ..CameraBackgroundConfig::default() + }, + 64, + 48, + None, + ) + .unwrap(); + let mut frame = gradient_i420(64, 48); + let original = frame.clone(); + let center = 22 * 64 + 32; + let corner = 0usize; + + assert!(transform.apply_i420(&mut frame, 0)); + + assert_eq!(frame.y[center], original.y[center]); + assert_ne!(frame.y[corner], original.y[corner]); + } + + #[test] + fn static_custom_background_composites_i420_from_predecoded_image() { + let path = write_temp_png("static-custom-i420"); + let custom = CameraBackgroundConfig::from_bridge_values( + Some("custom"), + path.to_str(), + Some("static"), + None, + ) + .unwrap(); + let mut transform = + CameraBackgroundTransform::with_mask_source(custom, 64, 48, None).unwrap(); + let mut frame = crate::yuv::I420 { + width: 64, + height: 48, + y: vec![235; 64 * 48], + u: vec![128; 32 * 24], + v: vec![128; 32 * 24], + }; + let center = 22 * 64 + 32; + let corner = 0usize; + + assert!(transform.apply_i420(&mut frame, 0)); + + assert_eq!(frame.y[center], 235); + assert_ne!(frame.y[corner], 235); + let _ = std::fs::remove_file(path); + } + + #[test] + fn person_mask_keeps_person_pixels_sharp_regardless_of_frame_position() { + let mut transform = blur_transform_with_mask(64, 48, vec![true]); + let mut frame = gradient_i420(64, 48); + let original = frame.clone(); + let person_corner = 0usize; + let background_corner = 63usize; + + assert!(transform.apply_i420(&mut frame, 0)); + + assert_eq!(frame.y[person_corner], original.y[person_corner]); + assert_ne!(frame.y[background_corner], original.y[background_corner]); + } + + #[test] + fn person_mask_drives_chroma_planes_at_half_resolution() { + let mut transform = blur_transform_with_mask(64, 48, vec![true]); + let mut frame = gradient_i420(64, 48); + let original = frame.clone(); + let person_chroma = 0usize; + let background_chroma = 31usize; + + assert!(transform.apply_i420(&mut frame, 0)); + + assert_eq!(frame.u[person_chroma], original.u[person_chroma]); + assert_ne!(frame.u[background_chroma], original.u[background_chroma]); + } + + #[test] + fn person_mask_failure_falls_back_to_portrait_ellipse() { + let mut transform = blur_transform_with_mask(64, 48, vec![false]); + let mut frame = gradient_i420(64, 48); + let original = frame.clone(); + let portrait_center = 22 * 64 + 32; + let corner = 0usize; + + assert!(transform.apply_i420(&mut frame, 0)); + + assert_eq!(frame.y[portrait_center], original.y[portrait_center]); + assert_ne!(frame.y[corner], original.y[corner]); + } + + #[test] + fn person_mask_failure_reuses_previous_valid_mask() { + let mut transform = blur_transform_with_mask(64, 48, vec![true, false]); + let mut first = gradient_i420(64, 48); + assert!(transform.apply_i420(&mut first, 0)); + let mut second = gradient_i420(64, 48); + let original = second.clone(); + let person_corner = 0usize; + let background_corner = 63usize; + + assert!(transform.apply_i420(&mut second, 0)); + + assert_eq!(second.y[person_corner], original.y[person_corner]); + assert_ne!(second.y[background_corner], original.y[background_corner]); + } + + #[test] + fn person_mask_composites_custom_background_only_outside_person() { + let path = write_temp_png("masked-custom-i420"); + let custom = CameraBackgroundConfig::from_bridge_values( + Some("custom"), + path.to_str(), + Some("static"), + None, + ) + .unwrap(); + let mut transform = CameraBackgroundTransform::with_mask_source( + custom, + 64, + 48, + Some(Box::new(LeftHalfPersonMask::new(vec![true]))), + ) + .unwrap(); + let mut frame = crate::yuv::I420 { + width: 64, + height: 48, + y: vec![235; 64 * 48], + u: vec![128; 32 * 24], + v: vec![128; 32 * 24], + }; + let person_corner = 0usize; + let background_corner = 63usize; + + assert!(transform.apply_i420(&mut frame, 0)); + + assert_eq!(frame.y[person_corner], 235); + assert_ne!(frame.y[background_corner], 235); + let _ = std::fs::remove_file(path); + } + + #[test] + fn animated_custom_background_uses_first_decoded_frame() { + let path = write_temp_png("animated-first-frame"); + let renamed = path.with_extension("gif"); + let _ = std::fs::remove_file(&renamed); + let mut image = ImageBuffer::, Vec>::new(4, 4); + for pixel in image.pixels_mut() { + *pixel = Rgb([10, 20, 30]); + } + image.save(&renamed).unwrap(); + let custom = CameraBackgroundConfig::from_bridge_values( + Some("custom"), + renamed.to_str(), + Some("animated"), + None, + ) + .unwrap(); + + let transform = CameraBackgroundTransform::with_mask_source(custom, 64, 48, None); + + assert!(transform.is_ok()); + let _ = std::fs::remove_file(path); + let _ = std::fs::remove_file(renamed); + } + + #[test] + fn stage_degrades_to_raw_frames_when_transform_creation_fails() { + let mut stage = CameraBackgroundStage::with_factory( + CameraBackgroundConfig { + mode: CameraBackgroundMode::Custom, + custom: Some(CameraBackgroundCustomSource { + path: "/nonexistent/background.png".to_string(), + media_kind: CameraBackgroundCustomMediaKind::Static, + }), + live_background: None, + ..CameraBackgroundConfig::default() + }, + CameraBackgroundTransform::new, + ); + let mut frame = gradient_i420(64, 48); + let original = frame.clone(); + + assert!(stage.apply_i420(&mut frame, 0)); + assert!(stage.apply_i420(&mut frame, 0)); + + assert_eq!(frame.y, original.y); + } + + #[test] + fn stage_recreates_transform_when_frame_dimensions_change() { + fn factory_without_mask( + config: CameraBackgroundConfig, + width: u32, + height: u32, + ) -> Result { + CameraBackgroundTransform::with_mask_source(config, width, height, None) + } + let mut stage = CameraBackgroundStage::with_factory( + CameraBackgroundConfig { + mode: CameraBackgroundMode::Blur, + custom: None, + live_background: None, + ..CameraBackgroundConfig::default() + }, + factory_without_mask, + ); + let mut first = gradient_i420(64, 48); + assert!(stage.apply_i420(&mut first, 0)); + assert_ne!(first.y[0], gradient_i420(64, 48).y[0]); + + let mut second = gradient_i420(32, 24); + let original = second.clone(); + assert!(stage.apply_i420(&mut second, 0)); + + assert_ne!(second.y[0], original.y[0]); + } + + #[test] + fn stage_passes_frames_through_when_background_disabled() { + let mut stage = CameraBackgroundStage::new(CameraBackgroundConfig::default()); + let mut frame = gradient_i420(64, 48); + let original = frame.clone(); + + assert!(!stage.is_enabled()); + assert!(stage.apply_i420(&mut frame, 0)); + + assert_eq!(frame.y, original.y); + } + + fn solid_i420(width: u32, height: u32, luma: u8) -> crate::yuv::I420 { + let mut frame = crate::yuv::I420::new(width, height).unwrap(); + frame.y.fill(luma); + frame.u.fill(128); + frame.v.fill(128); + frame + } + + fn write_temp_gif(name: &str, colors: &[[u8; 3]], delay_ms: u32) -> PathBuf { + use image::codecs::gif::GifEncoder; + use image::{Delay, Frame, Rgba, RgbaImage}; + assert!(!colors.is_empty()); + assert!(delay_ms > 0); + let path = std::env::temp_dir().join(format!("fluxer-camera-background-{name}.gif")); + let file = std::fs::File::create(&path).unwrap(); + let mut encoder = GifEncoder::new(file); + for color in colors { + let mut image = RgbaImage::new(4, 4); + for pixel in image.pixels_mut() { + *pixel = Rgba([color[0], color[1], color[2], 255]); + } + encoder + .encode_frame(Frame::from_parts( + image, + 0, + 0, + Delay::from_numer_denom_ms(delay_ms, 1), + )) + .unwrap(); + } + path + } + + #[test] + fn animation_delay_clamps_to_named_bounds_and_defaults_zero() { + assert_eq!( + clamp_animation_delay_ms(0), + CUSTOM_BACKGROUND_FRAME_DELAY_MS_DEFAULT + ); + assert_eq!( + clamp_animation_delay_ms(1), + CUSTOM_BACKGROUND_FRAME_DELAY_MS_MIN + ); + assert_eq!(clamp_animation_delay_ms(50), 50); + assert_eq!( + clamp_animation_delay_ms(60_000), + CUSTOM_BACKGROUND_FRAME_DELAY_MS_MAX + ); + } + + #[test] + fn animation_frame_index_follows_cumulative_schedule() { + let schedule = [100_000u64, 250_000, 400_000]; + + assert_eq!(animation_frame_index(&schedule, 0), 0); + assert_eq!(animation_frame_index(&schedule, 99_999), 0); + assert_eq!(animation_frame_index(&schedule, 100_000), 1); + assert_eq!(animation_frame_index(&schedule, 249_999), 1); + assert_eq!(animation_frame_index(&schedule, 250_000), 2); + assert_eq!(animation_frame_index(&schedule, 399_999), 2); + } + + #[test] + fn animation_frame_index_wraps_around_loop() { + let schedule = [100_000u64, 200_000]; + + assert_eq!(animation_frame_index(&schedule, 200_000), 0); + assert_eq!(animation_frame_index(&schedule, 350_000), 1); + assert_eq!(animation_frame_index(&schedule, 1_000_000_000), 0); + } + + #[test] + fn animated_decode_respects_frame_and_byte_caps() { + let colors = [ + [255u8, 0, 0], + [0, 255, 0], + [0, 0, 255], + [255, 255, 0], + [0, 255, 255], + ]; + let path = write_temp_gif("caps", &colors, 100); + let path_str = path.to_str().unwrap(); + let frame_rgba_bytes = 4 * 4 * 4; + + let all = decode_animated_background_frames( + path_str, + 16, + 16, + CUSTOM_BACKGROUND_ANIMATION_FRAMES_MAX, + CUSTOM_BACKGROUND_ANIMATION_BYTES_MAX, + ); + let frame_capped = decode_animated_background_frames( + path_str, + 16, + 16, + 3, + CUSTOM_BACKGROUND_ANIMATION_BYTES_MAX, + ); + let byte_capped = decode_animated_background_frames( + path_str, + 16, + 16, + CUSTOM_BACKGROUND_ANIMATION_FRAMES_MAX, + frame_rgba_bytes * 2 + 1, + ); + + assert_eq!(all.len(), colors.len()); + assert!(all.iter().all(|(_, delay_ms)| *delay_ms == 100)); + assert_eq!(frame_capped.len(), 3); + assert_eq!(byte_capped.len(), 2); + let _ = std::fs::remove_file(path); + } + + #[test] + fn animated_gif_background_selects_frames_deterministically_from_timestamps() { + let path = write_temp_gif("two-frame", &[[0, 0, 0], [255, 255, 255]], 100); + let custom = CameraBackgroundConfig::from_bridge_values( + Some("custom"), + path.to_str(), + Some("animated"), + None, + ) + .unwrap(); + let mut transform = + CameraBackgroundTransform::with_mask_source(custom, 64, 48, None).unwrap(); + let corner = 0usize; + + let mut first = solid_i420(64, 48, 128); + assert!(transform.apply_i420(&mut first, 5_000_000)); + let mut second = solid_i420(64, 48, 128); + assert!(transform.apply_i420(&mut second, 5_150_000)); + let mut wrapped = solid_i420(64, 48, 128); + assert!(transform.apply_i420(&mut wrapped, 5_250_000)); + + assert!(first.y[corner] < 64, "dark frame luma {}", first.y[corner]); + assert!( + second.y[corner] > 192, + "bright frame luma {}", + second.y[corner] + ); + assert_eq!(wrapped.y[corner], first.y[corner]); + let _ = std::fs::remove_file(path); + } + + #[test] + fn video_background_composites_latest_pushed_live_frame() { + let slot = CameraBackgroundLiveSlot::new(); + let config = CameraBackgroundConfig { + mode: CameraBackgroundMode::Custom, + custom: Some(CameraBackgroundCustomSource { + path: "/tmp/background.mp4".to_string(), + media_kind: CameraBackgroundCustomMediaKind::Video, + }), + live_background: Some(slot.clone()), + ..CameraBackgroundConfig::default() + }; + let mut transform = + CameraBackgroundTransform::with_mask_source(config, 64, 48, None).unwrap(); + let original = solid_i420(64, 48, 128); + let corner = 0usize; + let center = 22 * 64 + 32; + + let mut passthrough = original.clone(); + assert!(transform.apply_i420(&mut passthrough, 0)); + assert_eq!(passthrough.y, original.y); + + assert!(slot.store(solid_i420(64, 48, 40))); + let mut composited = original.clone(); + assert!(transform.apply_i420(&mut composited, 33_000)); + assert_eq!(composited.y[corner], 40); + assert_eq!(composited.y[center], 128); + + slot.clear(); + let mut cleared = original.clone(); + assert!(transform.apply_i420(&mut cleared, 66_000)); + assert_eq!(cleared.y, original.y); + } + + #[test] + fn video_background_without_engine_slot_passes_frames_through() { + let config = CameraBackgroundConfig { + mode: CameraBackgroundMode::Custom, + custom: Some(CameraBackgroundCustomSource { + path: "/nonexistent/background.mp4".to_string(), + media_kind: CameraBackgroundCustomMediaKind::Video, + }), + live_background: None, + ..CameraBackgroundConfig::default() + }; + let mut transform = + CameraBackgroundTransform::with_mask_source(config, 64, 48, None).unwrap(); + let original = solid_i420(64, 48, 128); + let mut frame = original.clone(); + + assert!(transform.apply_i420(&mut frame, 0)); + + assert_eq!(frame.y, original.y); + } + + #[test] + fn live_slot_resizes_pushed_frames_to_camera_dims_on_store() { + let slot = CameraBackgroundLiveSlot::new(); + slot.set_target_dims(64, 48); + + assert!(slot.store(solid_i420(32, 24, 200))); + + slot.with_latest(|frame| { + let frame = frame.expect("stored frame present"); + assert_eq!((frame.width, frame.height), (64, 48)); + assert_eq!(frame.y[0], 200); + }); + } + + #[test] + fn live_slot_resizes_existing_frame_when_target_dims_arrive() { + let slot = CameraBackgroundLiveSlot::new(); + assert!(slot.store(solid_i420(32, 24, 75))); + + slot.set_target_dims(64, 48); + + slot.with_latest(|frame| { + let frame = frame.expect("stored frame retained"); + assert_eq!((frame.width, frame.height), (64, 48)); + assert_eq!(frame.y[0], 75); + }); + } + + #[test] + fn live_slot_keeps_only_the_latest_pushed_frame() { + let slot = CameraBackgroundLiveSlot::new(); + slot.set_target_dims(64, 48); + + assert!(slot.store(solid_i420(64, 48, 10))); + assert!(slot.store(solid_i420(64, 48, 20))); + + slot.with_latest(|frame| { + assert_eq!(frame.expect("latest frame present").y[0], 20); + }); + } + + #[test] + fn live_slot_store_tight_i420_reuses_same_size_plane_buffers() { + let slot = CameraBackgroundLiveSlot::new(); + let first = [1u8, 2, 3, 4, 5, 6]; + let second = [7u8, 8, 9, 10, 11, 12]; + + assert!(slot.store_tight_i420(&first, 2, 2)); + let first_ptrs = slot.with_latest(|frame| { + let frame = frame.expect("first frame present"); + (frame.y.as_ptr(), frame.u.as_ptr(), frame.v.as_ptr()) + }); + assert!(slot.store_tight_i420(&second, 2, 2)); + + slot.with_latest(|frame| { + let frame = frame.expect("second frame present"); + assert_eq!(frame.y.as_ptr(), first_ptrs.0); + assert_eq!(frame.u.as_ptr(), first_ptrs.1); + assert_eq!(frame.v.as_ptr(), first_ptrs.2); + assert_eq!(frame.y, vec![7, 8, 9, 10]); + assert_eq!(frame.u, vec![11]); + assert_eq!(frame.v, vec![12]); + }); + } + + #[test] + fn live_slot_resizes_tight_frames_to_target_dims_and_recycles_buffers() { + let slot = CameraBackgroundLiveSlot::new(); + slot.set_target_dims(64, 48); + let tight = |y: u8, u: u8, v: u8| { + let mut data = vec![y; 32 * 24]; + data.resize(32 * 24 + 16 * 12, u); + data.resize(32 * 24 + 2 * 16 * 12, v); + data + }; + + assert!(slot.store_tight_i420(&tight(40, 90, 160), 32, 24)); + let first_ptr = slot.with_latest(|frame| { + let frame = frame.expect("resized frame stored"); + assert_eq!((frame.width, frame.height), (64, 48)); + assert_eq!(frame.y[0], 40); + assert_eq!(frame.u[0], 90); + assert_eq!(frame.v[0], 160); + frame.y.as_ptr() + }); + + assert!(slot.store_tight_i420(&tight(10, 20, 30), 32, 24)); + slot.with_latest(|frame| { + assert_eq!(frame.expect("second frame stored").y[0], 10); + }); + + assert!(slot.store_tight_i420(&tight(50, 60, 70), 32, 24)); + slot.with_latest(|frame| { + let frame = frame.expect("third frame stored"); + assert_eq!(frame.y[0], 50); + assert_eq!(frame.y.as_ptr(), first_ptr); + }); + } + + #[test] + fn live_slot_rejects_truncated_tight_frames_needing_resize() { + let slot = CameraBackgroundLiveSlot::new(); + slot.set_target_dims(64, 48); + let short = vec![128u8; 32 * 24]; + assert!(!slot.store_tight_i420(&short, 32, 24)); + slot.with_latest(|frame| assert!(frame.is_none())); + } + + #[test] + fn scaled_blur_radius_spans_quarter_to_two_and_a_half_times_base() { + assert_eq!(scaled_blur_radius(24, 0), 6); + assert_eq!(scaled_blur_radius(24, CAMERA_EFFECT_STRENGTH_DEFAULT), 24); + assert_eq!(scaled_blur_radius(24, CAMERA_EFFECT_STRENGTH_MAX), 60); + assert_eq!(scaled_blur_radius(4, 0), 1); + assert_eq!(scaled_blur_radius(4, CAMERA_EFFECT_STRENGTH_DEFAULT), 4); + assert_eq!(scaled_blur_radius(4, CAMERA_EFFECT_STRENGTH_MAX), 10); + } + + #[test] + fn scaled_blur_radius_clamps_to_named_bounds() { + assert_eq!(scaled_blur_radius(1, 0), BLUR_RADIUS_SCALED_MIN); + assert_eq!( + scaled_blur_radius(BLUR_RADIUS_SCALED_MAX, CAMERA_EFFECT_STRENGTH_MAX), + BLUR_RADIUS_SCALED_MAX + ); + assert_eq!( + scaled_blur_radius(10, CAMERA_EFFECT_STRENGTH_MAX + 100), + scaled_blur_radius(10, CAMERA_EFFECT_STRENGTH_MAX) + ); + } + + #[test] + fn scaled_blur_radius_is_monotonic_in_strength() { + let mut previous = 0usize; + for strength in 0..=CAMERA_EFFECT_STRENGTH_MAX { + let radius = scaled_blur_radius(24, strength); + assert!(radius >= previous); + previous = radius; + } + } + + #[test] + fn bridge_values_default_and_clamp_effect_strengths() { + let defaults = + CameraBackgroundConfig::from_bridge_values(Some("blur"), None, None, None).unwrap(); + assert_eq!(defaults.blur_strength, CAMERA_EFFECT_STRENGTH_DEFAULT); + + let clamped = + CameraBackgroundConfig::from_bridge_values(Some("blur"), None, None, Some(250)) + .unwrap(); + assert_eq!(clamped.blur_strength, CAMERA_EFFECT_STRENGTH_MAX); + + let explicit = + CameraBackgroundConfig::from_bridge_values(Some("blur"), None, None, Some(10)).unwrap(); + assert_eq!(explicit.blur_strength, 10); + } + + #[test] + fn masked_blur_keeps_person_colors_out_of_the_blurred_background() { + let mut transform = blur_transform_with_mask(64, 48, vec![true]); + let mut frame = solid_i420(64, 48, 16); + for y in 0..48usize { + for x in 0..32usize { + frame.y[y * 64 + x] = 235; + } + } + + assert!(transform.apply_i420(&mut frame, 0)); + + assert_eq!(frame.y[24 * 64 + 20], 235); + assert_eq!(frame.y[24 * 64 + 48], 16); + assert_eq!(frame.y[24 * 64 + 63], 16); + } + + #[test] + fn blur_strength_scales_the_effective_blur_radius() { + fn blurred_corner(strength: u32) -> u8 { + let config = CameraBackgroundConfig::from_bridge_values( + Some("blur"), + None, + None, + Some(strength), + ) + .unwrap(); + let mut transform = + CameraBackgroundTransform::with_mask_source(config, 640, 360, None).unwrap(); + let mut frame = gradient_i420(640, 360); + assert!(transform.apply_i420(&mut frame, 0)); + frame.y[0] + } + + let weak = blurred_corner(0); + let default = blurred_corner(CAMERA_EFFECT_STRENGTH_DEFAULT); + let strong = blurred_corner(CAMERA_EFFECT_STRENGTH_MAX); + assert!(weak < default); + assert!(default < strong); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/src/config.rs b/fluxer_desktop/native/webrtc-sender/src/config.rs new file mode 100644 index 0000000..c170285 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/src/config.rs @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[cfg(test)] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct PublishConfig { + pub url: String, + pub token: String, + pub width: u32, + pub height: u32, + pub fps: u32, + pub codec: String, +} + +#[cfg(test)] +impl PublishConfig { + pub fn validate(&self) -> Result<(), String> { + if self.url.trim().is_empty() { + return Err("livekit url is empty".into()); + } + if !(self.url.starts_with("ws://") || self.url.starts_with("wss://")) { + return Err("livekit url must be ws:// or wss://".into()); + } + if self.token.trim().is_empty() { + return Err("livekit token is empty".into()); + } + if self.width < 2 || self.height < 2 { + return Err("capture dimensions too small".into()); + } + if !self.width.is_multiple_of(2) || !self.height.is_multiple_of(2) { + return Err("capture dimensions must be even".into()); + } + if self.width > 8192 || self.height > 8192 { + return Err("capture dimensions too large".into()); + } + if self.fps == 0 { + return Err("capture fps must be positive".into()); + } + if !self.codec.trim().is_empty() && canonical_codec_name(&self.codec).is_none() { + return Err("unsupported video codec".into()); + } + Ok(()) + } +} + +pub const SUPPORTED_CODECS: &[&str] = &["vp8", "h264", "vp9", "av1", "h265"]; + +pub fn canonical_codec_name(name: &str) -> Option<&'static str> { + let lower = name.trim().to_ascii_lowercase(); + if lower == "hevc" { + return Some("h265"); + } + SUPPORTED_CODECS.iter().copied().find(|&c| c == lower) +} + +#[cfg(test)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PublisherState { + Idle, + Connecting, + Publishing, + Closed, + Failed, +} + +#[cfg(test)] +impl PublisherState { + pub fn accepts_frames(self) -> bool { + matches!(self, PublisherState::Publishing) + } + + pub fn can_connect(self) -> bool { + matches!( + self, + PublisherState::Idle | PublisherState::Closed | PublisherState::Failed + ) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn cfg() -> PublishConfig { + PublishConfig { + url: "wss://sfu.example/rtc".into(), + token: "jwt".into(), + width: 1920, + height: 1080, + fps: 30, + codec: String::new(), + } + } + + #[test] + fn valid_config_passes() { + assert!(cfg().validate().is_ok()); + } + + #[test] + fn rejects_bad_url() { + let mut c = cfg(); + c.url = "https://sfu".into(); + assert!(c.validate().is_err()); + c.url = String::new(); + assert!(c.validate().is_err()); + } + + #[test] + fn rejects_empty_token() { + let mut c = cfg(); + c.token = " ".into(); + assert!(c.validate().is_err()); + } + + #[test] + fn rejects_odd_or_oob_dimensions() { + let mut c = cfg(); + c.width = 1921; + assert!(c.validate().is_err()); + c.width = 1920; + c.height = 0; + assert!(c.validate().is_err()); + c.height = 16384; + assert!(c.validate().is_err()); + } + + #[test] + fn accepts_dimension_boundaries_and_rejects_zero_fps() { + let mut c = cfg(); + c.width = 2; + c.height = 2; + c.fps = 1; + assert!(c.validate().is_ok()); + + c.width = 8192; + c.height = 8192; + assert!(c.validate().is_ok()); + + c.fps = 0; + assert_eq!( + c.validate(), + Err("capture fps must be positive".to_string()) + ); + } + + #[test] + fn rejects_unknown_non_empty_codec_but_allows_empty_default() { + let mut c = cfg(); + c.codec = String::new(); + assert!(c.validate().is_ok()); + + c.codec = " ".into(); + assert!(c.validate().is_ok()); + + c.codec = "h266".into(); + assert_eq!(c.validate(), Err("unsupported video codec".to_string())); + } + + #[test] + fn canonical_codec_name_accepts_all_five_case_insensitively() { + for (input, expected) in [ + ("vp8", "vp8"), + (" vp8 ", "vp8"), + ("VP8", "vp8"), + ("h264", "h264"), + ("H264", "h264"), + ("vp9", "vp9"), + ("Vp9", "vp9"), + ("av1", "av1"), + ("AV1", "av1"), + ("h265", "h265"), + ("H265", "h265"), + ("hevc", "h265"), + ("HEVC", "h265"), + ] { + assert_eq!(canonical_codec_name(input), Some(expected), "codec {input}"); + } + assert_eq!(SUPPORTED_CODECS.len(), 5); + } + + #[test] + fn canonical_codec_name_rejects_empty_and_unknown() { + assert_eq!(canonical_codec_name(""), None); + assert_eq!(canonical_codec_name("h266"), None); + assert_eq!(canonical_codec_name("rubbish"), None); + } + + #[test] + fn state_gates_frames_and_connect() { + assert!(PublisherState::Publishing.accepts_frames()); + assert!(!PublisherState::Connecting.accepts_frames()); + assert!(!PublisherState::Idle.accepts_frames()); + assert!(PublisherState::Idle.can_connect()); + assert!(PublisherState::Closed.can_connect()); + assert!(PublisherState::Failed.can_connect()); + assert!(!PublisherState::Failed.accepts_frames()); + assert!(!PublisherState::Publishing.can_connect()); + assert!(!PublisherState::Connecting.can_connect()); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/src/deep_filter.rs b/fluxer_desktop/native/webrtc-sender/src/deep_filter.rs new file mode 100644 index 0000000..6115457 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/src/deep_filter.rs @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::audio::{ + DEEP_FILTER_NOISE_REDUCTION_LEVEL_MAX, DEEP_FILTER_NOISE_REDUCTION_LEVEL_MIN, + clamp_deep_filter_noise_reduction_level, +}; +use df::tract::{DfParams, DfTract, RuntimeParams}; +use ndarray::Array2; + +pub const DEEP_FILTER_SAMPLE_RATE_HZ: u32 = 48_000; +pub const DEEP_FILTER_NUM_CHANNELS: u32 = 1; +pub const DEEP_FILTER_FRAME_SAMPLES: usize = 480; + +const SAMPLE_SCALE_I16_TO_F32: f32 = 1.0 / 32_768.0; +const SAMPLE_SCALE_F32_TO_I16: f32 = 32_767.0; + +const _: () = assert!(DEEP_FILTER_FRAME_SAMPLES == DEEP_FILTER_SAMPLE_RATE_HZ as usize / 100); +const _: () = assert!(DEEP_FILTER_NUM_CHANNELS == 1); + +pub struct DeepFilterProcessor { + model: DfTract, + input: Array2, + output: Array2, +} + +impl DeepFilterProcessor { + pub fn new(noise_reduction_level: f64) -> Result { + let level_db = clamp_deep_filter_noise_reduction_level(noise_reduction_level); + assert!(level_db >= DEEP_FILTER_NOISE_REDUCTION_LEVEL_MIN); + assert!(level_db <= DEEP_FILTER_NOISE_REDUCTION_LEVEL_MAX); + let params = RuntimeParams::default_with_ch(DEEP_FILTER_NUM_CHANNELS as usize) + .with_atten_lim(level_db as f32); + let model = DfTract::new(DfParams::default(), ¶ms) + .map_err(|error| format!("deep filter model init: {error:#}"))?; + if model.sr != DEEP_FILTER_SAMPLE_RATE_HZ as usize { + return Err(format!( + "deep filter model sample rate {} != {DEEP_FILTER_SAMPLE_RATE_HZ}", + model.sr + )); + } + if model.ch != DEEP_FILTER_NUM_CHANNELS as usize { + return Err(format!( + "deep filter model channels {} != {DEEP_FILTER_NUM_CHANNELS}", + model.ch + )); + } + if model.hop_size != DEEP_FILTER_FRAME_SAMPLES { + return Err(format!( + "deep filter model hop {} != {DEEP_FILTER_FRAME_SAMPLES}", + model.hop_size + )); + } + Ok(DeepFilterProcessor { + model, + input: Array2::zeros((1, DEEP_FILTER_FRAME_SAMPLES)), + output: Array2::zeros((1, DEEP_FILTER_FRAME_SAMPLES)), + }) + } + + pub fn process_frame(&mut self, samples: &mut [i16]) -> Result<(), String> { + assert_eq!(samples.len(), DEEP_FILTER_FRAME_SAMPLES); + assert_eq!(self.input.len(), DEEP_FILTER_FRAME_SAMPLES); + assert_eq!(self.output.len(), DEEP_FILTER_FRAME_SAMPLES); + for (target, sample) in self.input.iter_mut().zip(samples.iter()) { + *target = f32::from(*sample) * SAMPLE_SCALE_I16_TO_F32; + } + self.model + .process(self.input.view(), self.output.view_mut()) + .map_err(|error| format!("deep filter process: {error:#}"))?; + for (sample, enhanced) in samples.iter_mut().zip(self.output.iter()) { + *sample = sample_f32_to_i16(*enhanced); + } + Ok(()) + } +} + +fn sample_f32_to_i16(sample: f32) -> i16 { + if !sample.is_finite() { + return 0; + } + let clamped = sample.clamp(-1.0, 1.0); + assert!(clamped >= -1.0); + assert!(clamped <= 1.0); + (clamped * SAMPLE_SCALE_F32_TO_I16) as i16 +} + +#[cfg(test)] +mod tests { + use super::*; + + fn next_noise_sample(seed: &mut u32) -> i16 { + *seed = seed.wrapping_mul(1_664_525).wrapping_add(1_013_904_223); + ((*seed >> 16) as u16 as i16) / 4 + } + + fn noise_frame(seed: &mut u32) -> [i16; DEEP_FILTER_FRAME_SAMPLES] { + let mut frame = [0i16; DEEP_FILTER_FRAME_SAMPLES]; + for sample in frame.iter_mut() { + *sample = next_noise_sample(seed); + } + frame + } + + fn frame_rms(samples: &[i16]) -> f64 { + assert!(!samples.is_empty()); + let sum_squares: f64 = samples + .iter() + .map(|sample| { + let normalized = f64::from(*sample) / 32_768.0; + normalized * normalized + }) + .sum(); + (sum_squares / samples.len() as f64).sqrt() + } + + #[test] + fn sample_conversion_holds_the_contract_range() { + assert_eq!(sample_f32_to_i16(0.0), 0); + assert_eq!(sample_f32_to_i16(1.0), 32_767); + assert_eq!(sample_f32_to_i16(-1.0), -32_767); + assert_eq!(sample_f32_to_i16(2.0), 32_767); + assert_eq!(sample_f32_to_i16(-2.0), -32_767); + assert_eq!(sample_f32_to_i16(0.5), 16_383); + } + + #[test] + fn sample_conversion_maps_non_finite_to_silence() { + assert_eq!(sample_f32_to_i16(f32::NAN), 0); + assert_eq!(sample_f32_to_i16(f32::INFINITY), 0); + assert_eq!(sample_f32_to_i16(f32::NEG_INFINITY), 0); + } + + #[test] + fn zero_level_passes_audio_through() { + let mut processor = DeepFilterProcessor::new(0.0).expect("embedded model must initialize"); + let mut seed = 0x2545_f491u32; + for _ in 0..5 { + let original = noise_frame(&mut seed); + let mut processed = original; + processor + .process_frame(&mut processed) + .expect("processing must succeed"); + for (output, input) in processed.iter().zip(original.iter()) { + assert!((i32::from(*output) - i32::from(*input)).abs() <= 1); + } + } + } + + #[test] + fn full_level_attenuates_steady_noise() { + let mut processor = DeepFilterProcessor::new(DEEP_FILTER_NOISE_REDUCTION_LEVEL_MAX) + .expect("embedded model must initialize"); + let mut seed = 0x9e37_79b9u32; + let mut input_rms = 0.0; + let mut output_rms = 0.0; + for frame_index in 0..30 { + let mut frame = noise_frame(&mut seed); + let frame_input_rms = frame_rms(&frame); + processor + .process_frame(&mut frame) + .expect("processing must succeed"); + if frame_index >= 20 { + input_rms += frame_input_rms; + output_rms += frame_rms(&frame); + } + } + assert!(input_rms > 0.0); + assert!(output_rms < input_rms * 0.5); + } + + #[test] + fn frame_length_contract_is_enforced() { + let mut processor = DeepFilterProcessor::new(DEEP_FILTER_NOISE_REDUCTION_LEVEL_MAX) + .expect("embedded model must initialize"); + let mut short_frame = [0i16; DEEP_FILTER_FRAME_SAMPLES - 1]; + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + let _ = processor.process_frame(&mut short_frame); + })); + assert!(result.is_err()); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/src/engine.rs b/fluxer_desktop/native/webrtc-sender/src/engine.rs new file mode 100644 index 0000000..a4aa82d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/src/engine.rs @@ -0,0 +1,6915 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::audio; +use crate::camera; +use crate::deep_filter::{self, DeepFilterProcessor}; +use crate::events; +use crate::inbound_forwarder::{InboundForwarderRegistry, spawn_drain_forwarder}; +use crate::send_control::{ + AdaptiveAudioStats, AdaptiveVideoStats, DEFAULT_AUDIO_BUFFER_MAX_MS, DEFAULT_MIN_VIDEO_FPS, + SendHealthSnapshot, VideoTelemetryExtras, +}; +use crate::speaking::{ + self, SPEAKING_FRAME_TIMEOUT_MS, SPEAKING_HEARTBEAT_INTERVAL_MS, SPEAKING_RELEASE_MS_LOCAL, + SPEAKING_RELEASE_MS_REMOTE, SpeakingGate, SpeakingThresholds, +}; +use crate::stats as stats_mod; +#[cfg(target_os = "windows")] +use crate::texture_source::TextureFrameDesc; +use crate::texture_source::{self, TextureCapability}; +use crate::yuv; +use crossbeam_queue::ArrayQueue; +use fluxer_screen_frame_bus::{ + self as frame_bus, EnqueueOutcome, NativeScreenFrameSinkHandle, ScreenFrame as BusScreenFrame, + ScreenFrameSink, +}; +use napi::Status; +use napi::bindgen_prelude::{Buffer, Env, Function, ToNapiValue, Unknown}; +use napi::threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}; +use napi_derive::napi; +use parking_lot::Mutex; +use std::collections::HashMap; +use std::ffi::c_void; +#[cfg(target_os = "linux")] +use std::os::fd::{AsRawFd, OwnedFd}; +use std::panic::{AssertUnwindSafe, catch_unwind}; +use std::sync::atomic::{AtomicBool, AtomicI64, AtomicU8, AtomicU64, Ordering}; +use std::sync::{Arc, LazyLock, mpsc}; +use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH}; + +use futures_util::{FutureExt, StreamExt}; +use livekit::DataPacket; +use livekit::e2ee::key_provider::{KeyProvider, KeyProviderOptions}; +use livekit::e2ee::{E2eeOptions, EncryptionType}; +use livekit::id::{ParticipantIdentity, TrackSid}; +use livekit::options::{AudioEncoding, TrackPublishOptions, VideoCodec, VideoEncoding}; +use livekit::participant::{LocalParticipant, RemoteParticipant}; +use livekit::rtc_engine::lk_runtime::LkRuntime; +use livekit::track::{ + LocalAudioTrack, LocalTrack, LocalVideoTrack, RemoteTrack, TrackKind, TrackSource, VideoQuality, +}; +use livekit::webrtc::MediaType; +use livekit::webrtc::audio_frame::AudioFrame; +use livekit::webrtc::audio_source::native::NativeAudioSource; +use livekit::webrtc::audio_source::{AudioSourceOptions, RtcAudioSource}; +use livekit::webrtc::audio_stream::native::NativeAudioStream; +use livekit::webrtc::peer_connection_factory::PeerConnectionFactory; +use livekit::webrtc::peer_connection_factory::native::PeerConnectionFactoryExt; +use livekit::webrtc::prelude::RtcAudioTrack; +use livekit::webrtc::stats::RtcStats; +#[cfg(target_os = "macos")] +use livekit::webrtc::video_frame::native::NativeBuffer; +use livekit::webrtc::video_frame::{ + BoxVideoFrame, I420Buffer, NV12Buffer, VideoBuffer, VideoFrame, VideoRotation, +}; +use livekit::webrtc::video_source::native::NativeVideoSource; +use livekit::webrtc::video_source::{RtcVideoSource, VideoResolution}; +use livekit::webrtc::video_stream::native::NativeVideoStream; +use livekit::{PlatformAudio, RecordingDeviceId, Room, RoomEvent, RoomOptions}; +use tokio::sync::{Notify, watch}; + +const S_IDLE: u8 = 0; +const S_CONNECTING: u8 = 1; +const S_CONNECTED: u8 = 2; +const S_CLOSED: u8 = 3; +const S_FAILED: u8 = 4; + +const CONNECTION_STATES: [u8; 5] = [S_IDLE, S_CONNECTING, S_CONNECTED, S_CLOSED, S_FAILED]; + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum ConnectAdmission { + Proceed, + Superseded, +} + +fn admit_connect_attempt(intent: u64, latest_intent: u64) -> ConnectAdmission { + assert!(intent >= 1); + assert!(latest_intent >= intent); + if latest_intent == intent { + ConnectAdmission::Proceed + } else { + ConnectAdmission::Superseded + } +} + +async fn wait_connect_cancelled(mut cancel_rx: watch::Receiver, connect_epoch: u64) { + assert!(connect_epoch >= 1); + let observed_epoch = *cancel_rx.borrow(); + if observed_epoch >= connect_epoch { + return; + } + if cancel_rx.changed().await.is_ok() { + let observed_epoch = *cancel_rx.borrow(); + assert!(observed_epoch >= connect_epoch); + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum RoomLoopAction { + Forward, + Exit, +} + +fn room_event_loop_action(loop_epoch: u64, engine_epoch: u64) -> RoomLoopAction { + assert!(loop_epoch >= 1); + assert!(engine_epoch >= loop_epoch); + if engine_epoch == loop_epoch { + RoomLoopAction::Forward + } else { + RoomLoopAction::Exit + } +} + +fn connection_state_transition_valid(from: u8, to: u8) -> bool { + assert!(CONNECTION_STATES.contains(&from)); + assert!(CONNECTION_STATES.contains(&to)); + matches!( + (from, to), + (S_IDLE, S_CLOSED) + | (S_CLOSED, S_CONNECTING) + | (S_CLOSED, S_CLOSED) + | (S_CONNECTING, S_CONNECTED) + | (S_CONNECTING, S_FAILED) + | (S_CONNECTING, S_CLOSED) + | (S_CONNECTED, S_CLOSED) + | (S_FAILED, S_CLOSED) + ) +} + +fn store_connection_state(state: &AtomicU8, next: u8) { + let previous = state.swap(next, Ordering::AcqRel); + assert!(connection_state_transition_valid(previous, next)); +} + +fn store_room_loop_closed( + state_guard: &Mutex<()>, + connect_epoch: &AtomicU64, + state: &AtomicU8, + loop_epoch: u64, +) { + let _guard = state_guard.lock(); + let engine_epoch = connect_epoch.load(Ordering::Acquire); + if room_event_loop_action(loop_epoch, engine_epoch) == RoomLoopAction::Exit { + return; + } + store_connection_state(state, S_CLOSED); +} + +const EVENT_QUEUE_LIMIT: usize = 64; +const CAMERA_PREVIEW_TRACK_SID: &str = "local-camera-preview"; +const SCREEN_SHARE_AUDIO_MAX_BITRATE_BPS: u64 = 510_000; +const PLATFORM_RECORDING_DISABLE_REASSERT_DELAY_MS: u64 = 200; +const DEEP_FILTER_FIRST_FRAME_TIMEOUT_MS: u64 = 2_000; +const DEEP_FILTER_FRAME_TIMEOUT_MS: u64 = 1_000; +const DEEP_FILTER_READINESS_POLL_MS: u64 = 20; +const DEEP_FILTER_SOURCE_QUEUE_MS: u32 = 0; +const DEEP_FILTER_PIPE_QUEUE_FRAMES: usize = 8; +const MIN_AUDIO_SAMPLE_RATE_HZ: u32 = 8_000; +const MAX_AUDIO_SAMPLE_RATE_HZ: u32 = 192_000; +const MAX_AUDIO_CHANNELS: u32 = 8; +const MAX_PCM_FRAME_DURATION_MS: u64 = 1_000; +const MAX_PCM_FRAME_SAMPLES: usize = (MAX_AUDIO_SAMPLE_RATE_HZ as usize) + * (MAX_AUDIO_CHANNELS as usize) + * (MAX_PCM_FRAME_DURATION_MS as usize) + / 1_000; +const ENCODER_QUEUE_CAPACITY: usize = 8; +const FPS_PACING_FALLBACK: f64 = 60.0; +const LIVEKIT_TRACK_SOURCE_CAMERA: &str = "camera"; +const LIVEKIT_TRACK_SOURCE_SCREEN_SHARE: &str = "screen_share"; +const LIVEKIT_TRACK_SOURCE_SCREEN_SHARE_AUDIO: &str = "screen_share_audio"; +const REMOTE_TRACK_SUBSCRIPTION_PUBLICATIONS_MAX: usize = 64; +type EventTsfn = ThreadsafeFunction< + (String, String), + (), + (String, String), + napi::Status, + false, + true, + EVENT_QUEUE_LIMIT, +>; + +const VIDEO_FRAME_QUEUE_LIMIT: usize = 8; +type VideoFrameTsfn = ThreadsafeFunction< + (String, Buffer), + (), + (String, Buffer), + napi::Status, + false, + true, + VIDEO_FRAME_QUEUE_LIMIT, +>; + +static PREWARMED_LIVEKIT_RUNTIME: LazyLock>>> = + LazyLock::new(|| Mutex::new(None)); + +struct ScreenSource { + track_sid: TrackSid, + video_sender: AdaptiveVideoSender, + metadata: ScreenSourceMetadata, + bus_capture_id: Option, + frame_sink: Option>, +} + +#[derive(Clone)] +struct ScreenSourceMetadata { + track_sid: TrackSid, + width: u32, + height: u32, + codec: String, + target_bitrate_kbps: Option, + configured_fps: f64, +} + +type PcmScratch = Arc>>; + +fn new_pcm_scratch(sample_rate: u32, num_channels: u32) -> PcmScratch { + assert!(valid_audio_format(sample_rate, num_channels)); + let samples_max = (sample_rate as u64) + .saturating_mul(u64::from(num_channels)) + .saturating_mul(MAX_PCM_FRAME_DURATION_MS) + / 1_000; + assert!(samples_max <= MAX_PCM_FRAME_SAMPLES as u64); + Arc::new(tokio::sync::Mutex::new(Vec::with_capacity( + samples_max as usize, + ))) +} + +struct ScreenAudioSource { + source: NativeAudioSource, + sample_rate: u32, + num_channels: u32, + track_sid: TrackSid, + pcm_scratch: PcmScratch, + drain_stop: Arc, + ring: Arc, +} + +impl ScreenAudioSource { + fn matches_format(&self, sample_rate: u32, num_channels: u32) -> bool { + same_audio_format( + self.sample_rate, + self.num_channels, + sample_rate, + num_channels, + ) + } +} + +impl Drop for ScreenAudioSource { + fn drop(&mut self) { + self.drain_stop.store(true, Ordering::Release); + self.ring.wake.notify_one(); + } +} + +fn same_audio_format( + existing_sample_rate: u32, + existing_num_channels: u32, + sample_rate: u32, + num_channels: u32, +) -> bool { + existing_sample_rate == sample_rate && existing_num_channels == num_channels +} + +const SCREEN_AUDIO_RING_CAP: usize = 32; +const SCREEN_AUDIO_RECYCLE_CAP: usize = 32; +const SCREEN_AUDIO_CHUNK_F32_MAX: usize = 16_384; + +struct ScreenAudioChunk { + samples: Vec, + num_frames: u32, + channels: u32, + sample_rate_hz: u32, +} + +struct ScreenAudioRing { + filled: ArrayQueue, + recycled: ArrayQueue>, + dropped: AtomicU64, + wake: Notify, +} + +impl ScreenAudioRing { + fn new() -> Self { + Self { + filled: ArrayQueue::new(SCREEN_AUDIO_RING_CAP), + recycled: ArrayQueue::new(SCREEN_AUDIO_RECYCLE_CAP), + dropped: AtomicU64::new(0), + wake: Notify::new(), + } + } + + fn take_buffer(&self) -> Vec { + self.recycled + .pop() + .unwrap_or_else(|| Vec::with_capacity(SCREEN_AUDIO_CHUNK_F32_MAX)) + } + + fn recycle(&self, mut buffer: Vec) { + buffer.clear(); + let _ = self.recycled.push(buffer); + } + + fn push(&self, chunk: ScreenAudioChunk) { + if let Err(returned) = self.filled.push(chunk) { + if let Some(old) = self.filled.pop() { + self.dropped.fetch_add(1, Ordering::Relaxed); + self.recycle(old.samples); + } + if let Err(dropped) = self.filled.push(returned) { + self.dropped.fetch_add(1, Ordering::Relaxed); + self.recycle(dropped.samples); + } + } + self.wake.notify_one(); + } +} + +fn screen_audio_chunk_as_bytes(samples: &[f32]) -> &[u8] { + unsafe { + std::slice::from_raw_parts( + samples.as_ptr() as *const u8, + std::mem::size_of_val(samples), + ) + } +} + +async fn forward_screen_audio_chunk( + source: &NativeAudioSource, + scratch: &PcmScratch, + chunk: &ScreenAudioChunk, +) { + assert!(chunk.channels > 0); + let expected_samples = (chunk.num_frames as usize).checked_mul(chunk.channels as usize); + if expected_samples != Some(chunk.samples.len()) { + return; + } + let bytes = screen_audio_chunk_as_bytes(&chunk.samples); + let mut samples = scratch.lock().await; + if let Some(frame) = + f32_audio_frame_into(bytes, chunk.sample_rate_hz, chunk.channels, &mut samples) + { + let _ = source.capture_frame(&frame).await; + } +} + +fn spawn_screen_audio_drain( + ring: Arc, + source: NativeAudioSource, + scratch: PcmScratch, + drain_stop: Arc, +) { + tokio::spawn(async move { + loop { + while let Some(chunk) = ring.filled.pop() { + forward_screen_audio_chunk(&source, &scratch, &chunk).await; + ring.recycle(chunk.samples); + } + if drain_stop.load(Ordering::Acquire) { + break; + } + ring.wake.notified().await; + } + }); +} + +enum MicSource { + Device { + track_sid: TrackSid, + track: LocalAudioTrack, + }, + DeviceDeepFiltered { + track_sid: TrackSid, + track: LocalAudioTrack, + capture_track: LocalAudioTrack, + _tap_guard: RecordedAudioTapGuard, + _pipe_stop: DeepFilterPipeStop, + }, + Manual { + source: NativeAudioSource, + sample_rate: u32, + num_channels: u32, + track_sid: TrackSid, + track: LocalAudioTrack, + pcm_scratch: PcmScratch, + }, +} + +impl MicSource { + fn track_sid(&self) -> TrackSid { + match self { + Self::Device { track_sid, .. } + | Self::DeviceDeepFiltered { track_sid, .. } + | Self::Manual { track_sid, .. } => track_sid.clone(), + } + } + + fn track_sid_mut(&mut self) -> &mut TrackSid { + match self { + Self::Device { track_sid, .. } + | Self::DeviceDeepFiltered { track_sid, .. } + | Self::Manual { track_sid, .. } => track_sid, + } + } + + fn track(&self) -> LocalAudioTrack { + match self { + Self::Device { track, .. } + | Self::DeviceDeepFiltered { track, .. } + | Self::Manual { track, .. } => track.clone(), + } + } + + fn capture_track(&self) -> Option { + match self { + Self::DeviceDeepFiltered { capture_track, .. } => Some(capture_track.clone()), + Self::Device { .. } | Self::Manual { .. } => None, + } + } + + fn is_device(&self) -> bool { + matches!(self, Self::Device { .. } | Self::DeviceDeepFiltered { .. }) + } +} + +struct DeepFilterPipeStop { + stop: Arc, +} + +impl Drop for DeepFilterPipeStop { + fn drop(&mut self) { + self.stop.store(true, Ordering::Release); + } +} + +struct DeepFilterMicrophone { + track: LocalAudioTrack, + capture_track: LocalAudioTrack, + pipe_stop: DeepFilterPipeStop, + tap_guard: RecordedAudioTapGuard, +} + +#[derive(Clone, Copy)] +struct DeepFilterCaptureFrame { + samples: [i16; deep_filter::DEEP_FILTER_FRAME_SAMPLES], +} + +struct RecordedAudioTapGuard { + generation: u64, +} + +impl Drop for RecordedAudioTapGuard { + fn drop(&mut self) { + livekit::webrtc::recorded_audio::native::clear_recorded_audio_sink(self.generation); + } +} + +struct DeepFilterPipeParts { + source: NativeAudioSource, + pipe_stop: DeepFilterPipeStop, + diagnostics: DeepFilterDiagnostics, + frame_sender: mpsc::SyncSender, + ready: tokio::sync::oneshot::Receiver>, +} + +#[derive(Clone)] +struct DeepFilterDiagnostics { + degraded_frames: Arc, + events: Arc>>, + dropped_engine_events: Arc, +} + +impl DeepFilterDiagnostics { + fn record_degraded_frame(&self, detail: &str) { + let previous = self.degraded_frames.fetch_add(1, Ordering::Relaxed); + if previous == 0 { + emit_deep_filter_status( + &self.events, + &self.dropped_engine_events, + "degraded", + detail, + ); + } + } +} + +fn spawn_deep_filter_processing_thread( + noise_reduction_level: f64, + source: NativeAudioSource, + stop: Arc, + diagnostics: DeepFilterDiagnostics, + frame_receiver: mpsc::Receiver, +) -> Result>, String> { + assert!(!stop.load(Ordering::Acquire)); + assert_eq!(diagnostics.degraded_frames.load(Ordering::Acquire), 0); + let (ready_sender, ready_receiver) = tokio::sync::oneshot::channel(); + std::thread::Builder::new() + .name("fluxer-deep-filter-mic".to_string()) + .spawn(move || { + let processor = match DeepFilterProcessor::new(noise_reduction_level) { + Ok(processor) => processor, + Err(error) => { + let _ = ready_sender.send(Err(error)); + return; + } + }; + if ready_sender.send(Ok(())).is_err() { + return; + } + run_deep_filter_processing(processor, source, stop, diagnostics, frame_receiver); + }) + .map_err(|error| format!("spawn deep filter thread: {error}"))?; + Ok(ready_receiver) +} + +fn run_deep_filter_processing( + mut processor: DeepFilterProcessor, + source: NativeAudioSource, + stop: Arc, + diagnostics: DeepFilterDiagnostics, + frame_receiver: mpsc::Receiver, +) { + loop { + if stop.load(Ordering::Acquire) { + break; + } + let received = + frame_receiver.recv_timeout(Duration::from_millis(DEEP_FILTER_FRAME_TIMEOUT_MS)); + match received { + Ok(mut frame) => { + process_and_capture_deep_filter_frame( + &mut processor, + &source, + &diagnostics, + &mut frame.samples, + ); + } + Err(mpsc::RecvTimeoutError::Timeout) => {} + Err(mpsc::RecvTimeoutError::Disconnected) => break, + } + } +} + +fn process_and_capture_deep_filter_frame( + processor: &mut DeepFilterProcessor, + source: &NativeAudioSource, + diagnostics: &DeepFilterDiagnostics, + samples: &mut [i16; deep_filter::DEEP_FILTER_FRAME_SAMPLES], +) { + assert_eq!(samples.len(), deep_filter::DEEP_FILTER_FRAME_SAMPLES); + if let Err(error) = processor.process_frame(samples) { + diagnostics.record_degraded_frame(&error); + } + let processed = AudioFrame { + data: (&samples[..]).into(), + sample_rate: deep_filter::DEEP_FILTER_SAMPLE_RATE_HZ, + num_channels: deep_filter::DEEP_FILTER_NUM_CHANNELS, + samples_per_channel: deep_filter::DEEP_FILTER_FRAME_SAMPLES as u32, + }; + match source.capture_frame(&processed).now_or_never() { + Some(Ok(())) => {} + Some(Err(error)) => { + diagnostics.record_degraded_frame(&format!("capture frame: {error}")); + } + None => { + diagnostics.record_degraded_frame("capture frame did not complete synchronously"); + } + } +} + +fn install_deep_filter_capture_tap( + frame_sender: mpsc::SyncSender, + diagnostics: DeepFilterDiagnostics, + capture_count: Arc, +) -> u64 { + livekit::webrtc::recorded_audio::native::set_recorded_audio_sink( + move |data: &[i16], sample_rate: i32, channels: usize, frames: usize| { + if sample_rate != deep_filter::DEEP_FILTER_SAMPLE_RATE_HZ as i32 { + diagnostics.record_degraded_frame("unexpected capture sample rate"); + return; + } + if channels != deep_filter::DEEP_FILTER_NUM_CHANNELS as usize { + diagnostics.record_degraded_frame("unexpected capture channel count"); + return; + } + if frames != deep_filter::DEEP_FILTER_FRAME_SAMPLES { + diagnostics.record_degraded_frame("unexpected capture frame length"); + return; + } + if data.len() != deep_filter::DEEP_FILTER_FRAME_SAMPLES { + diagnostics.record_degraded_frame("unexpected capture buffer length"); + return; + } + let mut frame = DeepFilterCaptureFrame { + samples: [0i16; deep_filter::DEEP_FILTER_FRAME_SAMPLES], + }; + frame.samples.copy_from_slice(data); + match frame_sender.try_send(frame) { + Ok(()) => { + capture_count.fetch_add(1, Ordering::Relaxed); + } + Err(mpsc::TrySendError::Full(_)) => { + diagnostics.record_degraded_frame("processing queue full"); + } + Err(mpsc::TrySendError::Disconnected(_)) => {} + } + }, + ) +} + +async fn await_deep_filter_capture_started(capture_count: &AtomicU64) -> bool { + let max_polls = DEEP_FILTER_FIRST_FRAME_TIMEOUT_MS / DEEP_FILTER_READINESS_POLL_MS; + assert!(max_polls > 0); + assert!(DEEP_FILTER_READINESS_POLL_MS > 0); + for _ in 0..max_polls { + if capture_count.load(Ordering::Acquire) > 0 { + return true; + } + tokio::time::sleep(Duration::from_millis(DEEP_FILTER_READINESS_POLL_MS)).await; + } + capture_count.load(Ordering::Acquire) > 0 +} + +fn emit_deep_filter_status( + events: &Mutex>, + dropped_engine_events: &AtomicU64, + status: &str, + detail: &str, +) { + assert!(!status.is_empty()); + let payload = events::json_object(&[ + ("status", events::JsonValue::Str(status.to_string())), + ("detail", events::JsonValue::Str(detail.to_string())), + ]); + emit_engine_event( + events, + dropped_engine_events, + "deepFilterStatus".to_string(), + payload, + ); +} + +#[derive(Clone)] +struct DeviceCameraCapture { + source: NativeVideoSource, + participant_sid: String, + participant_identity: String, + track_name: String, + track_source: String, + request: camera::CameraRequest, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum CameraSwapOrder { + OpenNewThenStopOld, + StopOldThenOpenNew, +} + +fn camera_swap_order( + current: &camera::CameraSelector, + requested: &camera::CameraSelector, +) -> CameraSwapOrder { + if current == requested { + CameraSwapOrder::StopOldThenOpenNew + } else { + CameraSwapOrder::OpenNewThenStopOld + } +} + +enum CameraSource { + Device { + track_sid: Arc>, + stop: Arc, + capture: Option, + }, + NativeBuffered { + track_sid: Arc>, + video_sender: AdaptiveVideoSender, + frame_sink: Arc, + }, + Processed { + track_sid: Arc>, + video_sender: AdaptiveVideoSender, + }, +} + +impl CameraSource { + fn track_sid(&self) -> Arc> { + match self { + Self::Device { track_sid, .. } + | Self::NativeBuffered { track_sid, .. } + | Self::Processed { track_sid, .. } => track_sid.clone(), + } + } + + fn stop(&self) { + match self { + Self::Device { stop, .. } => stop.store(true, Ordering::Release), + Self::NativeBuffered { video_sender, .. } | Self::Processed { video_sender, .. } => { + video_sender.stop() + } + } + } + + fn processed_sender(&self) -> Option { + match self { + Self::Processed { video_sender, .. } => Some(video_sender.clone()), + Self::Device { .. } | Self::NativeBuffered { .. } => None, + } + } + + fn native_frame_sink(&self) -> Option> { + match self { + Self::NativeBuffered { frame_sink, .. } => Some(frame_sink.clone()), + Self::Device { .. } | Self::Processed { .. } => None, + } + } +} + +struct LocalTrackSlots<'a> { + camera: &'a Mutex>, + screen_camera: &'a Mutex>, + screen: &'a Mutex>, + screen_audio: &'a Mutex>, + mic: &'a Mutex>, +} + +fn apply_local_track_republish( + slots: &LocalTrackSlots<'_>, + previous_sid: &str, + republished_sid: &str, +) -> bool { + assert!(!previous_sid.is_empty()); + assert!(!republished_sid.is_empty()); + let mut matched: u32 = 0; + for slot in [slots.camera, slots.screen_camera] { + matched += republish_camera_slot_sid(slot, previous_sid, republished_sid); + } + matched += republish_screen_slot_sid(slots.screen, previous_sid, republished_sid); + matched += republish_screen_audio_slot_sid(slots.screen_audio, previous_sid, republished_sid); + matched += republish_mic_slot_sid(slots.mic, previous_sid, republished_sid); + assert!(matched <= 1); + matched == 1 +} + +fn republish_camera_slot_sid( + slot: &Mutex>, + previous_sid: &str, + republished_sid: &str, +) -> u32 { + let guard = slot.lock(); + let Some(source) = guard.as_ref() else { + return 0; + }; + let track_sid = source.track_sid(); + let mut sid = track_sid.lock(); + assert!(!sid.is_empty()); + if *sid == republished_sid { + return 1; + } + if *sid != previous_sid { + return 0; + } + *sid = republished_sid.to_string(); + 1 +} + +fn republish_screen_slot_sid( + slot: &Mutex>, + previous_sid: &str, + republished_sid: &str, +) -> u32 { + let mut guard = slot.lock(); + let Some(screen) = guard.as_mut() else { + return 0; + }; + republish_screen_sids( + &mut screen.track_sid, + &mut screen.metadata.track_sid, + previous_sid, + republished_sid, + ) +} + +fn republish_screen_sids( + track_sid: &mut TrackSid, + metadata_track_sid: &mut TrackSid, + previous_sid: &str, + republished_sid: &str, +) -> u32 { + assert_eq!(metadata_track_sid, track_sid); + let matched = republish_track_sid_value(track_sid, previous_sid, republished_sid); + if matched == 1 { + *metadata_track_sid = track_sid.clone(); + } + matched +} + +fn republish_screen_audio_slot_sid( + slot: &Mutex>, + previous_sid: &str, + republished_sid: &str, +) -> u32 { + let mut guard = slot.lock(); + let Some(audio) = guard.as_mut() else { + return 0; + }; + republish_track_sid_value(&mut audio.track_sid, previous_sid, republished_sid) +} + +fn republish_mic_slot_sid( + slot: &Mutex>, + previous_sid: &str, + republished_sid: &str, +) -> u32 { + let mut guard = slot.lock(); + let Some(mic) = guard.as_mut() else { + return 0; + }; + republish_track_sid_value(mic.track_sid_mut(), previous_sid, republished_sid) +} + +fn republish_track_sid_value( + track_sid: &mut TrackSid, + previous_sid: &str, + republished_sid: &str, +) -> u32 { + assert!(!previous_sid.is_empty()); + assert!(!republished_sid.is_empty()); + if track_sid.as_str() == republished_sid { + return 1; + } + if track_sid.as_str() != previous_sid { + return 0; + } + *track_sid = match TrackSid::try_from(republished_sid.to_string()) { + Ok(sid) => sid, + Err(sid) => panic!("republished track sid is not a track sid: {sid}"), + }; + 1 +} + +fn store_camera_slot(slot: &Mutex>, source: CameraSource) { + assert!(!source.track_sid().lock().is_empty()); + let previous = slot.lock().replace(source); + assert!(previous.is_none()); +} + +fn remove_camera_slot_if_held(slot: &Mutex>, track_sid: &Arc>) { + let mut guard = slot.lock(); + let held = guard + .as_ref() + .is_some_and(|source| Arc::ptr_eq(&source.track_sid(), track_sid)); + if held { + *guard = None; + } +} + +struct OpenedCameraWorker { + opened: camera::OpenedCamera, + sinks_tx: std::sync::mpsc::Sender, + stop: Arc, +} + +async fn open_camera_capture_worker( + request: camera::CameraRequest, +) -> napi::Result { + let stop = Arc::new(AtomicBool::new(false)); + let (result_tx, result_rx) = std::sync::mpsc::channel(); + let (sinks_tx, sinks_rx) = std::sync::mpsc::channel(); + camera::spawn_capture_worker(request, result_tx, sinks_rx, stop.clone()); + let opened = tokio::task::spawn_blocking(move || result_rx.recv()) + .await + .map_err(|e| napi::Error::from_reason(format!("camera open task: {e}")))? + .map_err(|_| napi::Error::from_reason("camera worker exited before open"))? + .map_err(napi::Error::from_reason)?; + assert!(opened.width >= 2); + assert!(opened.height >= 2); + Ok(OpenedCameraWorker { + opened, + sinks_tx, + stop, + }) +} + +fn commit_device_camera_swap( + slot: &Mutex>, + track_sid: &Arc>, + expected_stop: &Arc, + swapped_stop: &Arc, + swapped_request: &camera::CameraRequest, +) -> bool { + let mut guard = slot.lock(); + let Some(CameraSource::Device { + track_sid: held_sid, + stop, + capture, + }) = guard.as_mut() + else { + return false; + }; + if !Arc::ptr_eq(held_sid, track_sid) { + return false; + } + if !Arc::ptr_eq(stop, expected_stop) { + return false; + } + *stop = swapped_stop.clone(); + if let Some(capture) = capture.as_mut() { + capture.request = swapped_request.clone(); + } + true +} + +fn reconcile_camera_slot_sid( + local: &LocalParticipant, + track_sid: &Mutex, + track_source: TrackSource, +) { + let stored = track_sid.lock().clone(); + assert!(!stored.is_empty()); + let publications = local.track_publications(); + if publications.keys().any(|sid| sid.as_str() == stored) { + return; + } + let mut candidates = publications + .values() + .filter(|publication| publication.source() == track_source); + let Some(candidate) = candidates.next() else { + return; + }; + if candidates.next().is_some() { + return; + } + let republished_sid = candidate.sid().to_string(); + assert!(!republished_sid.is_empty()); + assert_ne!(republished_sid, stored); + *track_sid.lock() = republished_sid; +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum CameraPublicationKind { + Camera, + ScreenShare, +} + +impl CameraPublicationKind { + fn track_name(self) -> &'static str { + match self { + Self::Camera => LIVEKIT_TRACK_SOURCE_CAMERA, + Self::ScreenShare => LIVEKIT_TRACK_SOURCE_SCREEN_SHARE, + } + } + + fn track_source(self) -> TrackSource { + match self { + Self::Camera => TrackSource::Camera, + Self::ScreenShare => TrackSource::Screenshare, + } + } + + fn stream(self) -> Option<&'static str> { + match self { + Self::Camera => None, + Self::ScreenShare => Some(LIVEKIT_TRACK_SOURCE_SCREEN_SHARE), + } + } + + fn is_screencast(self) -> bool { + match self { + Self::Camera => false, + Self::ScreenShare => true, + } + } +} + +fn set_platform_adm_recording_enabled(enabled: bool) { + LkRuntime::instance() + .pc_factory() + .set_adm_recording_enabled(enabled); +} + +fn audio_device_count_result(reported: i16, label: &str) -> napi::Result { + audio::bounded_audio_device_count(reported) + .map_err(|error| napi::Error::from_reason(format!("{label}: {error}"))) +} + +fn collect_factory_playout_devices_from( + factory: &PeerConnectionFactory, +) -> napi::Result> { + let count = audio_device_count_result(factory.playout_devices(), "list audio output devices")?; + let mut raw = Vec::with_capacity(count); + for index in 0..count { + assert!(index < audio::MAX_PLATFORM_AUDIO_DEVICES); + let adm_index = u16::try_from(index).expect("bounded audio device index must fit u16"); + raw.push(( + factory.playout_device_guid(adm_index), + factory.playout_device_name(adm_index), + index, + )); + } + assert!(raw.len() <= audio::MAX_PLATFORM_AUDIO_DEVICES); + Ok(raw) +} + +fn collect_factory_recording_devices_from( + factory: &PeerConnectionFactory, +) -> napi::Result> { + let count = audio_device_count_result(factory.recording_devices(), "list audio input devices")?; + let mut raw = Vec::with_capacity(count); + for index in 0..count { + assert!(index < audio::MAX_PLATFORM_AUDIO_DEVICES); + let adm_index = u16::try_from(index).expect("bounded audio device index must fit u16"); + raw.push(( + factory.recording_device_guid(adm_index), + factory.recording_device_name(adm_index), + index, + )); + } + assert!(raw.len() <= audio::MAX_PLATFORM_AUDIO_DEVICES); + Ok(raw) +} + +fn collect_factory_playout_devices() -> napi::Result> { + let runtime = LkRuntime::instance(); + collect_factory_playout_devices_from(runtime.pc_factory()) +} + +fn collect_factory_recording_devices() -> napi::Result> { + let runtime = LkRuntime::instance(); + collect_factory_recording_devices_from(runtime.pc_factory()) +} + +fn set_factory_playout_device_by_guid( + factory: &PeerConnectionFactory, + guid: &str, +) -> napi::Result<()> { + assert!(!guid.trim().is_empty()); + if factory.set_playout_device_by_guid(guid) { + return Ok(()); + } + Err(napi::Error::from_reason( + "set audio output device: device not found", + )) +} + +fn hot_swap_factory_playout_device( + factory: &PeerConnectionFactory, + guid: &str, +) -> napi::Result<()> { + assert!(!guid.trim().is_empty()); + if !factory.stop_playout() { + return Err(napi::Error::from_reason( + "set audio output device: stop_playout failed", + )); + } + set_factory_playout_device_by_guid(factory, guid)?; + if !factory.init_playout() { + return Err(napi::Error::from_reason( + "set audio output device: init_playout failed", + )); + } + if !factory.start_playout() { + return Err(napi::Error::from_reason( + "set audio output device: start_playout failed", + )); + } + Ok(()) +} + +#[napi] +#[cfg_attr(test, allow(dead_code))] +pub fn prewarm_voice_engine() -> napi::Result<()> { + match catch_unwind(AssertUnwindSafe(prewarm_voice_engine_inner)) { + Ok(result) => result, + Err(_) => Err(napi::Error::from_reason( + "native voice engine prewarm panicked", + )), + } +} + +fn prewarm_voice_engine_inner() -> napi::Result<()> { + let runtime = { + let mut slot = PREWARMED_LIVEKIT_RUNTIME.lock(); + match slot.as_ref() { + Some(runtime) => runtime.clone(), + None => { + let runtime = LkRuntime::instance(); + *slot = Some(runtime.clone()); + runtime + } + } + }; + let factory = runtime.pc_factory(); + let _ = factory.get_rtp_sender_capabilities(MediaType::Audio); + let _ = factory.get_rtp_sender_capabilities(MediaType::Video); + let _ = factory.get_rtp_receiver_capabilities(MediaType::Audio); + let _ = factory.get_rtp_receiver_capabilities(MediaType::Video); + let audio_source = NativeAudioSource::new( + AudioSourceOptions::default(), + 48_000, + 1, + DEFAULT_AUDIO_BUFFER_MAX_MS as u32, + ); + let _audio_track = + LocalAudioTrack::create_audio_track("prewarm-audio", RtcAudioSource::Native(audio_source)); + if tokio::runtime::Handle::try_current().is_ok() { + let video_source = NativeVideoSource::new( + VideoResolution { + width: 16, + height: 16, + }, + false, + ); + let _video_track = LocalVideoTrack::create_video_track( + "prewarm-video", + RtcVideoSource::Native(video_source), + ); + } + Ok(()) +} + +#[napi] +#[cfg_attr(test, allow(dead_code))] +pub async fn probe_audio_device_module() -> napi::Result { + Ok(true) +} + +const ADM_DISPATCH_QUEUE_OPS_MAX: usize = 64; + +static ADM_DISPATCH: LazyLock>> = LazyLock::new(|| { + let (sender, receiver) = + mpsc::sync_channel::>(ADM_DISPATCH_QUEUE_OPS_MAX); + std::thread::Builder::new() + .name("fluxer-adm-dispatch".to_string()) + .spawn(move || { + while let Ok(operation) = receiver.recv() { + operation(); + } + }) + .expect("spawn audio device module dispatch thread"); + sender +}); + +fn enqueue_adm_operation(operation: Box) -> Result<(), String> { + match ADM_DISPATCH.try_send(operation) { + Ok(()) => Ok(()), + Err(mpsc::TrySendError::Full(_)) => { + Err("audio device module dispatch queue full".to_string()) + } + Err(mpsc::TrySendError::Disconnected(_)) => { + Err("audio device module dispatch thread exited".to_string()) + } + } +} + +async fn run_audio_device_module_blocking(operation: F) -> napi::Result +where + T: Send + 'static, + F: FnOnce() -> napi::Result + Send + 'static, +{ + let (result_sender, result_receiver) = tokio::sync::oneshot::channel(); + enqueue_adm_operation(Box::new(move || { + let _ = result_sender.send(operation()); + })) + .map_err(napi::Error::from_reason)?; + result_receiver + .await + .map_err(|_| napi::Error::from_reason("audio device module operation dropped"))? +} + +enum PendingVideoFrame { + I420Native { + buffer: I420Buffer, + timestamp_us: i64, + enqueued_at: Instant, + }, + #[allow(dead_code)] + Bgra { + data: Vec, + width: u32, + height: u32, + stride: u32, + timestamp_us: i64, + enqueued_at: Instant, + }, + #[allow(dead_code)] + Nv12 { + data: Vec, + width: u32, + height: u32, + stride_y: u32, + stride_uv: u32, + timestamp_us: i64, + enqueued_at: Instant, + }, + #[cfg(target_os = "windows")] + Texture { + desc: TextureFrameDesc, + capability: TextureCapability, + enqueued_at: Instant, + }, + #[cfg(target_os = "macos")] + MacCvPixelBuffer { + buffer: NativeBuffer, + timestamp_us: i64, + enqueued_at: Instant, + }, + #[cfg(target_os = "linux")] + Dmabuf { + desc: texture_source::DmabufFrameDesc, + capability: TextureCapability, + fds: Vec, + enqueued_at: Instant, + }, +} + +impl PendingVideoFrame { + fn enqueued_at(&self) -> Instant { + match self { + Self::I420Native { enqueued_at, .. } => *enqueued_at, + Self::Bgra { enqueued_at, .. } | Self::Nv12 { enqueued_at, .. } => *enqueued_at, + #[cfg(target_os = "windows")] + Self::Texture { enqueued_at, .. } => *enqueued_at, + #[cfg(target_os = "macos")] + Self::MacCvPixelBuffer { enqueued_at, .. } => *enqueued_at, + #[cfg(target_os = "linux")] + Self::Dmabuf { enqueued_at, .. } => *enqueued_at, + } + } +} + +#[derive(Clone)] +struct AdaptiveVideoSender { + pending: Arc>, + notify: Arc, + stop: Arc, + stats: Arc, + pacing: VideoPacingMode, + target_fps: f64, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum VideoPacingMode { + Sender, + Source, +} + +impl VideoPacingMode { + fn from_option(value: Option<&str>) -> Self { + match value { + Some("sender") => Self::Sender, + _ => Self::Source, + } + } + + fn as_label(self) -> &'static str { + match self { + Self::Sender => "sender", + Self::Source => "source", + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum EnqueueResult { + Accepted, + Coalesced, + Rejected, +} + +impl AdaptiveVideoSender { + fn new( + source: NativeVideoSource, + stats: Arc, + pacing: VideoPacingMode, + target_fps: f64, + ) -> Self { + let sanitized_target_fps = if target_fps.is_finite() && target_fps > 0.0 { + target_fps + } else { + FPS_PACING_FALLBACK + }; + let sender = Self { + pending: Arc::new(ArrayQueue::new(ENCODER_QUEUE_CAPACITY)), + notify: Arc::new(Notify::new()), + stop: Arc::new(AtomicBool::new(false)), + stats, + pacing, + target_fps: sanitized_target_fps, + }; + sender.start_worker(source); + sender + } + + fn enqueue(&self, frame: PendingVideoFrame) -> EnqueueResult { + if self.stop.load(Ordering::Acquire) { + self.stats.record_reject(); + return EnqueueResult::Rejected; + } + let now_ms = now_millis(); + assert_eq!(self.pending.capacity(), ENCODER_QUEUE_CAPACITY); + let coalesced = self.pending.force_push(frame).is_some(); + let depth = self.pending.len() as u64; + assert!(depth <= ENCODER_QUEUE_CAPACITY as u64); + self.stats + .record_enqueue_with_depth(now_ms, coalesced, depth); + self.notify.notify_one(); + if coalesced { + EnqueueResult::Coalesced + } else { + EnqueueResult::Accepted + } + } + + fn stop(&self) { + self.stop.store(true, Ordering::Release); + while self.pending.pop().is_some() {} + self.stats.record_queue_cleared(); + self.notify.notify_waiters(); + } + + fn start_worker(&self, source: NativeVideoSource) { + let pending = self.pending.clone(); + let notify = self.notify.clone(); + let stop = self.stop.clone(); + let stats = self.stats.clone(); + let pacing = self.pacing; + let target_fps = self.target_fps; + tokio::spawn(async move { + let mut last_capture_at: Option = None; + loop { + if stop.load(Ordering::Acquire) { + break; + } + let Some(frame) = pending.pop() else { + notify.notified().await; + continue; + }; + + let effective_fps = stats.current_fps().min(target_fps).max(1.0); + assert!(effective_fps.is_finite()); + assert!(effective_fps >= 1.0); + let pacing_interval = Duration::from_secs_f64(1.0 / effective_fps); + let throttled = effective_fps < target_fps; + if pacing == VideoPacingMode::Sender || throttled { + if let Some(last_capture) = last_capture_at { + let next_capture = last_capture + pacing_interval; + let now = tokio::time::Instant::now(); + if next_capture > now { + tokio::time::sleep_until(next_capture).await; + } + } + } + + let enqueued_at = frame.enqueued_at(); + let capture_started_tokio = tokio::time::Instant::now(); + let capture_started_at = Instant::now(); + let queue_age_ms = elapsed_ms(enqueued_at, capture_started_at); + let captured = publish_pending_video_frame(&source, frame); + let now_ms = now_millis(); + if captured { + let latency_ms = elapsed_ms(enqueued_at, Instant::now()); + stats.record_capture(now_ms, queue_age_ms, latency_ms); + } else { + stats.record_capture_failure(); + } + last_capture_at = Some(capture_started_tokio); + } + }); + } +} + +struct BusSenderSink { + sender: AdaptiveVideoSender, + #[cfg_attr(target_os = "macos", allow(dead_code))] + texture_capability: TextureCapability, +} + +impl ScreenFrameSink for BusSenderSink { + fn enqueue(&self, frame: BusScreenFrame) -> EnqueueOutcome { + let timestamp_us = frame.timestamp_us(); + let pending = match frame { + BusScreenFrame::Nv12(_) | BusScreenFrame::Bgra(_) => return EnqueueOutcome::Rejected, + #[cfg(target_os = "macos")] + BusScreenFrame::MacCvPixelBuffer(mac_frame) => { + let raw = mac_frame.into_raw_pixel_buffer(); + if raw.is_null() { + return EnqueueOutcome::Rejected; + } + let buffer = unsafe { NativeBuffer::from_cv_pixel_buffer(raw) }; + PendingVideoFrame::MacCvPixelBuffer { + buffer, + timestamp_us, + enqueued_at: Instant::now(), + } + } + #[cfg(target_os = "linux")] + BusScreenFrame::Dmabuf(dmabuf) => { + let frame_bus::DmabufDesc { + plane_count, + width, + height, + drm_format, + modifier, + strides, + offsets, + device_uuid, + .. + } = dmabuf.desc; + PendingVideoFrame::Dmabuf { + desc: texture_source::DmabufFrameDesc { + plane_count, + width, + height, + drm_format, + modifier, + strides, + offsets, + device_uuid, + timestamp_us, + }, + capability: self.texture_capability, + fds: dmabuf.fds, + enqueued_at: Instant::now(), + } + } + #[cfg(target_os = "windows")] + BusScreenFrame::SharedTexture(bus_desc) => { + let desc = texture_source::TextureFrameDesc { + handle: bus_desc.handle, + width: bus_desc.width, + height: bus_desc.height, + dxgi_format: bus_desc.dxgi_format, + timestamp_us, + }; + if texture_source::should_attempt_texture_encode(&self.texture_capability, &desc) + .is_err() + { + return EnqueueOutcome::Rejected; + } + PendingVideoFrame::Texture { + desc, + capability: self.texture_capability, + enqueued_at: Instant::now(), + } + } + }; + match self.sender.enqueue(pending) { + EnqueueResult::Accepted => EnqueueOutcome::Accepted, + EnqueueResult::Coalesced => EnqueueOutcome::Coalesced, + EnqueueResult::Rejected => EnqueueOutcome::Rejected, + } + } +} + +struct ScreenFrameSinkHandleExternal { + handle: NativeScreenFrameSinkHandle, +} + +unsafe extern "C" fn finalize_screen_frame_sink_handle( + _env: napi::sys::napi_env, + data: *mut c_void, + _hint: *mut c_void, +) { + if data.is_null() { + return; + } + let handle = unsafe { Box::from_raw(data as *mut NativeScreenFrameSinkHandle) }; + if handle.is_valid() { + unsafe { (handle.release)(handle.context) }; + } +} + +impl ToNapiValue for ScreenFrameSinkHandleExternal { + unsafe fn to_napi_value( + raw_env: napi::sys::napi_env, + value: Self, + ) -> napi::Result { + let mut napi_value = std::ptr::null_mut(); + let handle_ptr = Box::into_raw(Box::new(value.handle)); + let status = unsafe { + napi::sys::napi_create_external( + raw_env, + handle_ptr.cast(), + Some(finalize_screen_frame_sink_handle), + std::ptr::null_mut(), + &mut napi_value, + ) + }; + if status != napi::sys::Status::napi_ok { + let handle = unsafe { Box::from_raw(handle_ptr) }; + if handle.is_valid() { + unsafe { (handle.release)(handle.context) }; + } + return Err(napi::Error::new( + Status::GenericFailure, + "failed to create native screen frame sink handle external", + )); + } + Ok(napi_value) + } +} + +unsafe extern "C" fn retain_bus_sender_sink(context: *const c_void) { + if !context.is_null() { + unsafe { Arc::increment_strong_count(context as *const BusSenderSink) }; + } +} + +unsafe extern "C" fn release_bus_sender_sink(context: *const c_void) { + if !context.is_null() { + drop(unsafe { Arc::from_raw(context as *const BusSenderSink) }); + } +} + +fn bus_sender_sink_from_context<'a>(context: *const c_void) -> Option<&'a BusSenderSink> { + if context.is_null() { + return None; + } + Some(unsafe { &*(context as *const BusSenderSink) }) +} + +#[cfg(target_os = "macos")] +unsafe extern "C" fn enqueue_native_mac_cv_pixel_buffer( + context: *const c_void, + pixel_buffer: *mut c_void, + width: u32, + height: u32, + pixel_format: u32, + timestamp_us: i64, +) -> u32 { + let Some(sink) = bus_sender_sink_from_context(context) else { + return frame_bus::NATIVE_SCREEN_FRAME_SINK_REJECTED; + }; + if pixel_buffer.is_null() { + return frame_bus::NATIVE_SCREEN_FRAME_SINK_REJECTED; + } + let frame = unsafe { + frame_bus::MacCvPixelBufferFrame::from_retained( + pixel_buffer, + width, + height, + pixel_format, + timestamp_us, + ) + }; + NativeScreenFrameSinkHandle::native_outcome( + sink.enqueue(BusScreenFrame::MacCvPixelBuffer(frame)), + ) +} + +#[cfg(target_os = "linux")] +unsafe extern "C" fn enqueue_native_dmabuf( + context: *const c_void, + desc: frame_bus::DmabufDesc, + fds: *const i32, + fd_count: usize, +) -> u32 { + use std::os::fd::FromRawFd; + + let Some(sink) = bus_sender_sink_from_context(context) else { + return frame_bus::NATIVE_SCREEN_FRAME_SINK_REJECTED; + }; + let plane_count = desc.plane_count as usize; + if fds.is_null() || plane_count == 0 || plane_count > 4 || fd_count < plane_count { + return frame_bus::NATIVE_SCREEN_FRAME_SINK_REJECTED; + } + let raw_fds = unsafe { std::slice::from_raw_parts(fds, plane_count) }; + let owned_fds = raw_fds + .iter() + .map(|fd| unsafe { OwnedFd::from_raw_fd(*fd) }) + .collect(); + NativeScreenFrameSinkHandle::native_outcome(sink.enqueue(BusScreenFrame::Dmabuf( + frame_bus::DmabufFrame { + desc, + fds: owned_fds, + }, + ))) +} + +#[cfg(target_os = "windows")] +unsafe extern "C" fn enqueue_native_shared_texture( + context: *const c_void, + desc: frame_bus::SharedTextureDesc, +) -> u32 { + let Some(sink) = bus_sender_sink_from_context(context) else { + return frame_bus::NATIVE_SCREEN_FRAME_SINK_REJECTED; + }; + NativeScreenFrameSinkHandle::native_outcome(sink.enqueue(BusScreenFrame::SharedTexture(desc))) +} + +struct ScreenAudioSinkContext { + ring: Arc, +} + +unsafe extern "C" fn retain_screen_audio_sink(context: *const c_void) { + if !context.is_null() { + unsafe { Arc::increment_strong_count(context as *const ScreenAudioSinkContext) }; + } +} + +unsafe extern "C" fn release_screen_audio_sink(context: *const c_void) { + if !context.is_null() { + drop(unsafe { Arc::from_raw(context as *const ScreenAudioSinkContext) }); + } +} + +unsafe extern "C" fn enqueue_native_screen_audio( + context: *const c_void, + samples: *const f32, + num_frames: u32, + channels: u32, + sample_rate_hz: u32, + _timestamp_us: i64, +) -> u32 { + if context.is_null() { + return frame_bus::NATIVE_SCREEN_FRAME_SINK_REJECTED; + } + let ctx = unsafe { &*(context as *const ScreenAudioSinkContext) }; + if samples.is_null() || num_frames == 0 || channels == 0 { + return frame_bus::NATIVE_SCREEN_FRAME_SINK_REJECTED; + } + let total = (num_frames as usize).saturating_mul(channels as usize); + if total == 0 || total > SCREEN_AUDIO_CHUNK_F32_MAX { + return frame_bus::NATIVE_SCREEN_FRAME_SINK_REJECTED; + } + let slice = unsafe { std::slice::from_raw_parts(samples, total) }; + let mut buffer = ctx.ring.take_buffer(); + buffer.clear(); + buffer.extend_from_slice(slice); + ctx.ring.push(ScreenAudioChunk { + samples: buffer, + num_frames, + channels, + sample_rate_hz, + }); + frame_bus::NATIVE_SCREEN_FRAME_SINK_ACCEPTED +} + +fn create_screen_audio_sink_handle(ring: Arc) -> NativeScreenFrameSinkHandle { + let context = Arc::new(ScreenAudioSinkContext { ring }); + NativeScreenFrameSinkHandle { + magic: frame_bus::NATIVE_SCREEN_FRAME_SINK_HANDLE_MAGIC, + version: frame_bus::NATIVE_SCREEN_FRAME_SINK_HANDLE_VERSION, + context: Arc::into_raw(context) as *const c_void, + retain: retain_screen_audio_sink, + release: release_screen_audio_sink, + enqueue_screen_audio: Some(enqueue_native_screen_audio), + enqueue_nv12: None, + enqueue_bgra: None, + enqueue_mac_cv_pixel_buffer: None, + enqueue_dmabuf: None, + enqueue_shared_texture: None, + } +} + +fn create_native_screen_frame_sink_handle(sink: Arc) -> NativeScreenFrameSinkHandle { + NativeScreenFrameSinkHandle { + magic: frame_bus::NATIVE_SCREEN_FRAME_SINK_HANDLE_MAGIC, + version: frame_bus::NATIVE_SCREEN_FRAME_SINK_HANDLE_VERSION, + context: Arc::into_raw(sink) as *const c_void, + retain: retain_bus_sender_sink, + release: release_bus_sender_sink, + enqueue_screen_audio: None, + enqueue_nv12: None, + enqueue_bgra: None, + #[cfg(target_os = "macos")] + enqueue_mac_cv_pixel_buffer: Some(enqueue_native_mac_cv_pixel_buffer), + #[cfg(not(target_os = "macos"))] + enqueue_mac_cv_pixel_buffer: None, + #[cfg(target_os = "linux")] + enqueue_dmabuf: Some(enqueue_native_dmabuf), + #[cfg(not(target_os = "linux"))] + enqueue_dmabuf: None, + #[cfg(target_os = "windows")] + enqueue_shared_texture: Some(enqueue_native_shared_texture), + #[cfg(not(target_os = "windows"))] + enqueue_shared_texture: None, + } +} + +struct RoomEventForwarders { + events: Arc>>, + inbound_audio: Arc, + inbound_video: Arc, + dropped_video_frame_callbacks: Arc, + dropped_engine_events: Arc, + video_frames: Arc>>, + camera: Arc>>, + screen_camera: Arc>>, + screen: Arc>>, + screen_audio: Arc>>, + mic: Arc>>, + inbound_forwarders: Arc, + count_inbound_audio: bool, + speaking_thresholds: Arc, +} + +impl RoomEventForwarders { + fn handle_room_event(&self, event: &RoomEvent) { + if let RoomEvent::LocalTrackRepublished { + previous_sid, + publication, + .. + } = event + { + self.apply_republished_track_sid(previous_sid.as_str(), publication.sid().as_str()); + } + if let RoomEvent::TrackSubscribed { + track, + publication, + participant, + } = event + { + self.wire_track_subscribed_media(track, publication, participant); + } + if let RoomEvent::TrackUnsubscribed { publication, .. } = event { + self.cancel_inbound_forwarder(publication.sid().as_str()); + } + if let RoomEvent::ParticipantDisconnected(participant) = event { + self.inbound_forwarders + .cancel_for_participant(participant.sid().as_str()); + } + self.emit_mapped_event(event); + } + + fn cancel_inbound_forwarder(&self, track_sid: &str) { + self.inbound_forwarders.cancel(track_sid); + } + + fn apply_republished_track_sid(&self, previous_sid: &str, republished_sid: &str) { + let slots = LocalTrackSlots { + camera: &self.camera, + screen_camera: &self.screen_camera, + screen: &self.screen, + screen_audio: &self.screen_audio, + mic: &self.mic, + }; + let swapped = apply_local_track_republish(&slots, previous_sid, republished_sid); + if !swapped { + eprintln!( + "webrtc-sender: local track republish matched no slot \ + (previous {previous_sid}, republished {republished_sid}); \ + republished track left published" + ); + } + } + + fn wire_track_subscribed_media( + &self, + track: &RemoteTrack, + publication: &livekit::publication::RemoteTrackPublication, + participant: &RemoteParticipant, + ) { + match track { + RemoteTrack::Audio(audio) => { + let source = events::track_source_str(publication.source()); + if source == "microphone" { + SpeakingTap { + participant_sid: participant.sid().to_string(), + identity: participant.identity().to_string(), + track_sid: publication.sid().to_string(), + source: "microphone", + is_local: false, + release_ms: SPEAKING_RELEASE_MS_REMOTE, + thresholds: self.speaking_thresholds.clone(), + events: self.events.clone(), + dropped_engine_events: self.dropped_engine_events.clone(), + stop: Arc::new(AtomicBool::new(false)), + inbound_audio: self.count_inbound_audio.then(|| self.inbound_audio.clone()), + } + .spawn(audio.rtc_track()); + return; + } + if !self.count_inbound_audio { + return; + } + let counter = self.inbound_audio.clone(); + let stream = NativeAudioStream::new(audio.rtc_track(), 48000, 1); + let handle = spawn_drain_forwarder(stream, move |_frame| { + counter.fetch_add(1, Ordering::Relaxed); + }); + self.inbound_forwarders.register( + publication.sid().as_str(), + participant.sid().as_str(), + handle, + ); + } + RemoteTrack::Video(video) => { + self.spawn_inbound_video_forwarder(video, publication, participant); + } + } + } + + fn spawn_inbound_video_forwarder( + &self, + video: &livekit::track::RemoteVideoTrack, + publication: &livekit::publication::RemoteTrackPublication, + participant: &RemoteParticipant, + ) { + let counter = self.inbound_video.clone(); + let stream = NativeVideoStream::new(video.rtc_track()); + let meta_prefix = video_frame_meta_prefix( + participant.sid().as_str(), + participant.identity().as_str(), + publication.sid().as_str(), + &publication.name(), + events::track_source_str(publication.source()), + ); + let callback_slot = self.video_frames.clone(); + let dropped_callback_counter = self.dropped_video_frame_callbacks.clone(); + let mut skip_payload_after_queue_full = false; + let handle = spawn_drain_forwarder(stream, move |frame| { + counter.fetch_add(1, Ordering::Relaxed); + if callback_slot.lock().is_none() { + return; + } + if skip_payload_after_queue_full { + skip_payload_after_queue_full = false; + dropped_callback_counter.fetch_add(1, Ordering::Relaxed); + return; + } + let Some((meta, buffer)) = frame_to_callback_payload(&frame, &meta_prefix) else { + return; + }; + if let Some(tsfn) = callback_slot.lock().as_ref() { + let status = tsfn.call((meta, buffer), ThreadsafeFunctionCallMode::NonBlocking); + if status == Status::QueueFull { + skip_payload_after_queue_full = true; + dropped_callback_counter.fetch_add(1, Ordering::Relaxed); + } + } + }); + self.inbound_forwarders.register( + publication.sid().as_str(), + participant.sid().as_str(), + handle, + ); + } + + fn emit_mapped_event(&self, event: &RoomEvent) { + let (type_name, payload) = match events::map_room_event(event) { + Some((ty, json)) => (ty.to_string(), json), + None => { + let debug = format!("{event:?}"); + let variant = debug + .split([' ', '(', '{']) + .next() + .unwrap_or("unknown") + .to_string(); + (variant, "{}".to_string()) + } + }; + emit_engine_event( + &self.events, + &self.dropped_engine_events, + type_name, + payload, + ); + } +} + +struct PlatformAudioInstall { + platform_audio: Arc>>, + epoch_slot: Arc, + expected_epoch: u64, + device_mic_recording_requested: Arc, + events: Arc>>, + dropped_engine_events: Arc, +} + +impl PlatformAudioInstall { + fn run(self) { + assert!(self.expected_epoch >= 1); + if self.epoch_slot.load(Ordering::Acquire) != self.expected_epoch { + return; + } + if self.platform_audio.lock().is_some() { + return; + } + match PlatformAudio::new() { + Ok(platform_audio) => self.install(platform_audio), + Err(error) => { + let payload = events::json_object(&[( + "message", + events::JsonValue::Str(format!("platform audio unavailable: {error}")), + )]); + emit_engine_event( + &self.events, + &self.dropped_engine_events, + "audioPlaybackUnavailable".to_string(), + payload, + ); + } + } + } + + fn install(&self, platform_audio: PlatformAudio) { + { + let mut guard = self.platform_audio.lock(); + if self.epoch_slot.load(Ordering::Acquire) != self.expected_epoch { + return; + } + if guard.is_some() { + return; + } + *guard = Some(platform_audio); + } + if !self.device_mic_recording_requested.load(Ordering::Acquire) { + set_platform_adm_recording_enabled(false); + if let Some(audio) = self.platform_audio.lock().as_ref() { + let _ = audio.stop_recording(); + } + } + } +} + +#[napi] +pub struct VoiceEngine { + state: Arc, + room: Arc>>>, + screen: Arc>>, + screen_audio: Arc>>, + screen_audio_ring: Arc, + mic: Arc>>, + camera: Arc>>, + camera_preview: Arc>>>, + screen_camera: Arc>>, + events: Arc>>, + platform_audio: Arc>>, + device_mic_recording_requested: Arc, + participant_volumes: Arc>>, + byte_samples: Arc>>, + last_stats_json: Arc>>, + stats_running: Arc, + video_frames: Arc>>, + inbound_audio: Arc, + inbound_video: Arc, + dropped_video_frame_callbacks: Arc, + dropped_engine_events: Arc, + inbound_forwarders: Arc, + count_inbound_audio: Arc, + connect_epoch: Arc, + connect_intent: Arc, + connect_cancel: watch::Sender, + connect_serial: Arc>, + state_guard: Arc>, + texture_capability: Mutex, + send_video_stats: Arc>>>, + send_audio_stats: Arc, + max_audio_buffer_ms: Arc, + camera_live_background: crate::camera_background::CameraBackgroundLiveSlot, + speaking_thresholds: Arc, + mic_speaking_stop: Arc>>>, +} + +#[napi] +impl VoiceEngine { + #[napi(constructor)] + pub fn new() -> Self { + Self { + state: Arc::new(AtomicU8::new(S_IDLE)), + room: Arc::new(Mutex::new(None)), + screen: Arc::new(Mutex::new(None)), + screen_audio: Arc::new(Mutex::new(None)), + screen_audio_ring: Arc::new(ScreenAudioRing::new()), + mic: Arc::new(Mutex::new(None)), + camera: Arc::new(Mutex::new(None)), + camera_preview: Arc::new(Mutex::new(None)), + screen_camera: Arc::new(Mutex::new(None)), + events: Arc::new(Mutex::new(None)), + platform_audio: Arc::new(Mutex::new(None)), + device_mic_recording_requested: Arc::new(AtomicBool::new(false)), + participant_volumes: Arc::new(Mutex::new(HashMap::new())), + byte_samples: Arc::new(Mutex::new(HashMap::new())), + last_stats_json: Arc::new(Mutex::new(None)), + stats_running: Arc::new(AtomicBool::new(false)), + video_frames: Arc::new(Mutex::new(None)), + inbound_audio: Arc::new(AtomicU64::new(0)), + inbound_video: Arc::new(AtomicU64::new(0)), + dropped_video_frame_callbacks: Arc::new(AtomicU64::new(0)), + dropped_engine_events: Arc::new(AtomicU64::new(0)), + inbound_forwarders: Arc::new(InboundForwarderRegistry::new()), + count_inbound_audio: Arc::new(AtomicBool::new(false)), + connect_epoch: Arc::new(AtomicU64::new(0)), + connect_intent: Arc::new(AtomicU64::new(0)), + connect_cancel: watch::channel(0).0, + connect_serial: Arc::new(tokio::sync::Mutex::new(())), + state_guard: Arc::new(Mutex::new(())), + texture_capability: Mutex::new(TextureCapability::unavailable( + texture_source::TextureEncodeError::UnsupportedCodec, + )), + send_video_stats: Arc::new(Mutex::new(None)), + send_audio_stats: Arc::new(AdaptiveAudioStats::new( + DEFAULT_AUDIO_BUFFER_MAX_MS, + now_millis(), + )), + max_audio_buffer_ms: Arc::new(AtomicU64::new(DEFAULT_AUDIO_BUFFER_MAX_MS as u64)), + camera_live_background: crate::camera_background::CameraBackgroundLiveSlot::new(), + speaking_thresholds: Arc::new(SpeakingThresholds::new()), + mic_speaking_stop: Arc::new(Mutex::new(None)), + } + } + + fn stop_platform_recording(&self) { + if let Some(audio) = self.platform_audio.lock().as_ref() { + let _ = audio.stop_recording(); + } + } + + fn set_device_mic_recording_requested(&self, enabled: bool) { + if !enabled { + self.stop_platform_recording(); + } + self.device_mic_recording_requested + .store(enabled, Ordering::Release); + set_platform_adm_recording_enabled(enabled); + if !enabled { + self.stop_platform_recording(); + } + } + + fn disable_platform_recording_if_device_mic_inactive(&self) { + if !self.device_mic_recording_requested.load(Ordering::Acquire) { + set_platform_adm_recording_enabled(false); + self.stop_platform_recording(); + } + } + + fn schedule_delayed_platform_recording_disable_if_device_mic_inactive(&self) { + let device_mic_recording_requested = self.device_mic_recording_requested.clone(); + let platform_audio = self.platform_audio.clone(); + tokio::spawn(async move { + tokio::time::sleep(Duration::from_millis( + PLATFORM_RECORDING_DISABLE_REASSERT_DELAY_MS, + )) + .await; + if device_mic_recording_requested.load(Ordering::Acquire) { + return; + } + set_platform_adm_recording_enabled(false); + if let Some(audio) = platform_audio.lock().as_ref() { + let _ = audio.stop_recording(); + } + }); + } + + #[napi] + pub fn set_count_inbound_audio(&self, enabled: bool) { + self.count_inbound_audio.store(enabled, Ordering::Release); + } + + #[napi] + pub fn set_event_callback(&self, callback: Function<(String, String), ()>) -> napi::Result<()> { + let tsfn: EventTsfn = callback + .build_threadsafe_function::<(String, String)>() + .weak::() + .callee_handled::() + .max_queue_size::() + .build()?; + *self.events.lock() = Some(tsfn); + Ok(()) + } + + #[napi] + pub fn set_video_frame_callback( + &self, + callback: Function<(String, Buffer), ()>, + ) -> napi::Result<()> { + let tsfn: VideoFrameTsfn = callback + .build_threadsafe_function::<(String, Buffer)>() + .weak::() + .callee_handled::() + .max_queue_size::() + .build()?; + *self.video_frames.lock() = Some(tsfn); + Ok(()) + } + + #[napi] + pub fn clear_video_frame_callback(&self) { + *self.video_frames.lock() = None; + } + + #[napi] + pub async fn connect( + &self, + url: String, + token: String, + e2ee_key: Option, + connect_options: Option, + ) -> napi::Result<()> { + if url.trim().is_empty() || token.trim().is_empty() { + return Err(napi::Error::from_reason("url and token are required")); + } + let intent = self + .connect_intent + .fetch_add(1, Ordering::AcqRel) + .wrapping_add(1); + let _serial_guard = self.connect_serial.lock().await; + let latest_intent = self.connect_intent.load(Ordering::Acquire); + if admit_connect_attempt(intent, latest_intent) == ConnectAdmission::Superseded { + return Err(napi::Error::from_reason("connect superseded")); + } + self.shutdown_connection_locked().await; + assert!(self.room.lock().is_none()); + assert_eq!(self.state.load(Ordering::Acquire), S_CLOSED); + let connect_epoch = self.advance_connect_epoch(); + store_connection_state(&self.state, S_CONNECTING); + self.dial_room_locked(url, token, e2ee_key, connect_options, connect_epoch) + .await + } + + async fn dial_room_locked( + &self, + url: String, + token: String, + e2ee_key: Option, + connect_options: Option, + connect_epoch: u64, + ) -> napi::Result<()> { + assert!(connect_epoch >= 1); + let encryption = e2ee_key.map(|key| E2eeOptions { + key_provider: KeyProvider::with_shared_key(KeyProviderOptions::default(), key.to_vec()), + encryption_type: EncryptionType::Gcm, + }); + let connect_options = connect_options.unwrap_or_default(); + let mut room_options = RoomOptions::default(); + room_options.auto_subscribe = connect_options.auto_subscribe.unwrap_or(true); + room_options.adaptive_stream = connect_options.adaptive_stream.unwrap_or(true); + room_options.dynacast = connect_options.dynacast.unwrap_or(true); + room_options.encryption = encryption; + + let connect_cancelled = + wait_connect_cancelled(self.connect_cancel.subscribe(), connect_epoch); + let connect = tokio::select! { + connect = Room::connect(&url, &token, room_options) => connect, + () = connect_cancelled => { + return Err(napi::Error::from_reason("connect cancelled")); + } + }; + let (room, events_rx) = match connect { + Ok(pair) => pair, + Err(error) => { + self.record_dial_failure(connect_epoch); + return Err(napi::Error::from_reason(format!( + "livekit connect: {error}" + ))); + } + }; + let room = Arc::new(room); + let previous_room = self.room.lock().replace(room.clone()); + assert!(previous_room.is_none()); + *self.last_stats_json.lock() = None; + if !self.adopt_dialed_room(connect_epoch) { + let abandoned = self.room.lock().take(); + assert!(abandoned.is_some()); + *self.platform_audio.lock() = None; + let _ = room.close().await; + return Err(napi::Error::from_reason("connect cancelled")); + } + self.setup_platform_audio_deferred(connect_epoch); + self.spawn_room_event_loop(events_rx, connect_epoch); + self.start_stats_task(); + Ok(()) + } + + fn advance_connect_epoch(&self) -> u64 { + let _guard = self.state_guard.lock(); + self.connect_epoch + .fetch_add(1, Ordering::AcqRel) + .wrapping_add(1) + } + + fn cancel_connection_intent(&self) { + let _guard = self.state_guard.lock(); + let cancelled_epoch = self + .connect_epoch + .fetch_add(1, Ordering::AcqRel) + .wrapping_add(1); + store_connection_state(&self.state, S_CLOSED); + self.connect_cancel.send_replace(cancelled_epoch); + } + + fn record_dial_failure(&self, connect_epoch: u64) { + assert!(connect_epoch >= 1); + let _guard = self.state_guard.lock(); + if self.connect_epoch.load(Ordering::Acquire) != connect_epoch { + return; + } + store_connection_state(&self.state, S_FAILED); + } + + fn adopt_dialed_room(&self, connect_epoch: u64) -> bool { + assert!(connect_epoch >= 1); + let _guard = self.state_guard.lock(); + if self.connect_epoch.load(Ordering::Acquire) != connect_epoch { + return false; + } + store_connection_state(&self.state, S_CONNECTED); + true + } + + fn spawn_room_event_loop( + &self, + mut events_rx: tokio::sync::mpsc::UnboundedReceiver, + loop_epoch: u64, + ) { + assert!(loop_epoch >= 1); + assert!(self.room.lock().is_some()); + let forwarders = self.room_event_forwarders(); + let state = self.state.clone(); + let epoch_slot = self.connect_epoch.clone(); + let state_guard = self.state_guard.clone(); + tokio::spawn(async move { + while let Some(event) = events_rx.recv().await { + let engine_epoch = epoch_slot.load(Ordering::Acquire); + if room_event_loop_action(loop_epoch, engine_epoch) == RoomLoopAction::Exit { + return; + } + forwarders.handle_room_event(&event); + } + store_room_loop_closed(&state_guard, &epoch_slot, &state, loop_epoch); + }); + } + + fn room_event_forwarders(&self) -> RoomEventForwarders { + RoomEventForwarders { + events: self.events.clone(), + inbound_audio: self.inbound_audio.clone(), + inbound_video: self.inbound_video.clone(), + dropped_video_frame_callbacks: self.dropped_video_frame_callbacks.clone(), + dropped_engine_events: self.dropped_engine_events.clone(), + video_frames: self.video_frames.clone(), + camera: self.camera.clone(), + screen_camera: self.screen_camera.clone(), + screen: self.screen.clone(), + screen_audio: self.screen_audio.clone(), + mic: self.mic.clone(), + inbound_forwarders: self.inbound_forwarders.clone(), + count_inbound_audio: self.count_inbound_audio.load(Ordering::Acquire), + speaking_thresholds: self.speaking_thresholds.clone(), + } + } + + fn platform_audio_install(&self, expected_epoch: u64) -> PlatformAudioInstall { + assert!(expected_epoch >= 1); + PlatformAudioInstall { + platform_audio: self.platform_audio.clone(), + epoch_slot: self.connect_epoch.clone(), + expected_epoch, + device_mic_recording_requested: self.device_mic_recording_requested.clone(), + events: self.events.clone(), + dropped_engine_events: self.dropped_engine_events.clone(), + } + } + + fn setup_platform_audio_deferred(&self, connect_epoch: u64) { + assert!(connect_epoch >= 1); + let install = self.platform_audio_install(connect_epoch); + if let Err(error) = enqueue_adm_operation(Box::new(move || install.run())) { + eprintln!("webrtc-sender: platform audio install dispatch failed: {error}"); + } + } + + #[napi] + pub async fn ensure_platform_audio(&self) -> napi::Result<()> { + if self.state.load(Ordering::Acquire) != S_CONNECTED { + return Ok(()); + } + if self.platform_audio.lock().is_some() { + return Ok(()); + } + let connect_epoch = self.connect_epoch.load(Ordering::Acquire); + assert!(connect_epoch >= 1); + let install = self.platform_audio_install(connect_epoch); + run_audio_device_module_blocking(move || { + install.run(); + Ok(()) + }) + .await + } + + #[napi] + pub async fn publish_screen_share( + &self, + width: u32, + height: u32, + codec: String, + max_bitrate_bps: Option, + max_framerate: Option, + simulcast: Option, + publish_options: Option, + ) -> napi::Result<()> { + if self.state.load(Ordering::Acquire) != S_CONNECTED { + return Err(napi::Error::from_reason("not connected")); + } + if !valid_even_video_dims(width, height) { + return Err(napi::Error::from_reason("invalid screen dimensions")); + } + let track_name = publish_options + .as_ref() + .and_then(|opts| opts.track_name.as_ref()) + .map(|raw| raw.trim()) + .filter(|trimmed| !trimmed.is_empty()) + .unwrap_or("screen"); + let source = NativeVideoSource::new(VideoResolution { width, height }, true); + let track = + LocalVideoTrack::create_video_track(track_name, RtcVideoSource::Native(source.clone())); + let mut options = TrackPublishOptions { + source: TrackSource::Screenshare, + simulcast: simulcast.unwrap_or(true), + ..Default::default() + }; + if !codec.trim().is_empty() { + let canonical_codec = crate::config::canonical_codec_name(&codec) + .ok_or_else(|| napi::Error::from_reason("unsupported video codec"))?; + crate::hardware_encoder::require_publish_codec_runtime_support(canonical_codec) + .map_err(napi::Error::from_reason)?; + let video_codec = parse_codec(&codec) + .ok_or_else(|| napi::Error::from_reason("unsupported video codec"))?; + options.video_codec = video_codec; + } + *self.texture_capability.lock() = texture_capability_for_screen_codec(&codec); + if let Some(bitrate) = max_bitrate_bps.filter(|b| *b > 0.0) { + options.video_encoding = Some(VideoEncoding { + max_bitrate: bitrate as u64, + max_framerate: max_framerate.filter(|f| *f > 0.0).unwrap_or(30.0), + }); + } + let adaptive_send = publish_options + .as_ref() + .and_then(|opts| opts.adaptive_send) + .unwrap_or(true); + let min_video_fps = publish_options + .as_ref() + .and_then(|opts| opts.min_video_fps) + .filter(|fps| fps.is_finite() && *fps > 0.0) + .unwrap_or(DEFAULT_MIN_VIDEO_FPS); + let max_audio_buffer_ms = publish_options + .as_ref() + .and_then(|opts| opts.max_audio_buffer_ms) + .map(crate::send_control::clamp_audio_buffer_ms) + .unwrap_or(DEFAULT_AUDIO_BUFFER_MAX_MS); + self.max_audio_buffer_ms + .store(max_audio_buffer_ms as u64, Ordering::Relaxed); + self.send_audio_stats + .reset(max_audio_buffer_ms, now_millis()); + let local = { + let guard = self.room.lock(); + guard + .as_ref() + .map(|room| room.local_participant()) + .ok_or_else(|| napi::Error::from_reason("not connected"))? + }; + let existing_screen = { self.screen.lock().take() }; + if let Some(existing) = existing_screen { + existing.video_sender.stop(); + if let Some(prev_id) = existing.bus_capture_id.as_deref() { + frame_bus::unregister_sink(prev_id); + } + *self.send_video_stats.lock() = None; + local + .unpublish_track(&existing.track_sid) + .await + .map_err(|e| napi::Error::from_reason(format!("unpublish existing screen: {e}")))?; + } + let publication = local + .publish_track(LocalTrack::Video(track), options) + .await + .map_err(|e| { + *self.texture_capability.lock() = TextureCapability::unavailable( + texture_source::TextureEncodeError::UnsupportedCodec, + ); + napi::Error::from_reason(format!("publish_track: {e}")) + })?; + let video_stats = Arc::new(AdaptiveVideoStats::new( + max_framerate + .filter(|fps| fps.is_finite() && *fps > 0.0) + .unwrap_or(30.0), + min_video_fps, + adaptive_send, + now_millis(), + )); + let pacing = VideoPacingMode::from_option( + publish_options + .as_ref() + .and_then(|opts| opts.pacing.as_deref()), + ); + let pacing_target_fps = max_framerate + .filter(|fps| fps.is_finite() && *fps > 0.0) + .unwrap_or(FPS_PACING_FALLBACK); + let video_sender = + AdaptiveVideoSender::new(source, video_stats.clone(), pacing, pacing_target_fps); + let track_sid = publication.sid(); + *self.send_video_stats.lock() = Some(video_stats); + let bus_capture_id = publish_options + .as_ref() + .and_then(|opts| opts.capture_id.as_ref()) + .map(|raw| raw.trim().to_string()) + .filter(|trimmed| !trimmed.is_empty()); + let frame_sink = bus_capture_id.as_ref().map(|capture_id| { + let sink = Arc::new(BusSenderSink { + sender: video_sender.clone(), + texture_capability: *self.texture_capability.lock(), + }); + frame_bus::register_sink(capture_id, sink.clone()); + sink + }); + *self.screen.lock() = Some(ScreenSource { + track_sid: track_sid.clone(), + video_sender, + metadata: ScreenSourceMetadata { + track_sid, + width, + height, + codec: codec.trim().to_string(), + target_bitrate_kbps: max_bitrate_bps + .filter(|bps| bps.is_finite() && *bps > 0.0) + .map(|bps| bps / 1000.0), + configured_fps: max_framerate + .filter(|fps| fps.is_finite() && *fps > 0.0) + .unwrap_or(30.0), + }, + bus_capture_id, + frame_sink, + }); + Ok(()) + } + + #[napi] + pub fn create_screen_frame_sink_handle<'env>( + &self, + env: Env, + capture_id: String, + ) -> napi::Result>> { + let capture_id = capture_id.trim(); + if capture_id.is_empty() { + return Ok(None); + } + let sink = { + let guard = self.screen.lock(); + let Some(screen) = guard.as_ref() else { + return Ok(None); + }; + if screen.bus_capture_id.as_deref() != Some(capture_id) { + return Ok(None); + } + let Some(sink) = screen.frame_sink.as_ref() else { + return Ok(None); + }; + sink.clone() + }; + let external = ScreenFrameSinkHandleExternal { + handle: create_native_screen_frame_sink_handle(sink), + }; + let raw_env = env.raw(); + let value = unsafe { ScreenFrameSinkHandleExternal::to_napi_value(raw_env, external)? }; + Ok(Some(unsafe { Unknown::from_raw_unchecked(raw_env, value) })) + } + + #[napi] + pub fn create_screen_audio_sink_handle<'env>( + &self, + env: Env, + ) -> napi::Result>> { + let external = ScreenFrameSinkHandleExternal { + handle: create_screen_audio_sink_handle(self.screen_audio_ring.clone()), + }; + let raw_env = env.raw(); + let value = unsafe { ScreenFrameSinkHandleExternal::to_napi_value(raw_env, external)? }; + Ok(Some(unsafe { Unknown::from_raw_unchecked(raw_env, value) })) + } + + fn local_participant(&self) -> napi::Result { + let guard = self.room.lock(); + guard + .as_ref() + .map(|room| room.local_participant()) + .ok_or_else(|| napi::Error::from_reason("not connected")) + } + + fn platform_audio(&self) -> napi::Result { + let mut guard = self.platform_audio.lock(); + if let Some(audio) = guard.as_ref() { + return Ok(audio.clone()); + } + let audio = PlatformAudio::new() + .map_err(|e| napi::Error::from_reason(format!("platform audio unavailable: {e}")))?; + *guard = Some(audio.clone()); + drop(guard); + self.disable_platform_recording_if_device_mic_inactive(); + Ok(audio) + } + + fn start_mic_speaking_tap( + &self, + local: &LocalParticipant, + track: &LocalAudioTrack, + track_sid: &TrackSid, + ) { + let stop = Arc::new(AtomicBool::new(false)); + let previous = self.mic_speaking_stop.lock().replace(stop.clone()); + if let Some(previous) = previous { + previous.store(true, Ordering::Release); + } + SpeakingTap { + participant_sid: local.sid().to_string(), + identity: local.identity().to_string(), + track_sid: track_sid.to_string(), + source: "microphone", + is_local: true, + release_ms: SPEAKING_RELEASE_MS_LOCAL, + thresholds: self.speaking_thresholds.clone(), + events: self.events.clone(), + dropped_engine_events: self.dropped_engine_events.clone(), + stop, + inbound_audio: None, + } + .spawn(track.rtc_track()); + assert!(self.mic_speaking_stop.lock().is_some()); + } + + fn stop_mic_speaking_tap(&self) { + let stop = self.mic_speaking_stop.lock().take(); + if let Some(stop) = stop { + stop.store(true, Ordering::Release); + } + assert!(self.mic_speaking_stop.lock().is_none()); + } + + #[napi] + pub fn set_speaking_detection( + &self, + local_threshold_rms: f64, + remote_threshold_rms: f64, + ) -> napi::Result<()> { + if !local_threshold_rms.is_finite() { + return Err(napi::Error::from_reason( + "local speaking threshold must be finite", + )); + } + if !remote_threshold_rms.is_finite() { + return Err(napi::Error::from_reason( + "remote speaking threshold must be finite", + )); + } + self.speaking_thresholds + .set(local_threshold_rms, remote_threshold_rms); + Ok(()) + } + + async fn unpublish_existing_microphone( + &self, + local: &LocalParticipant, + keep_device_recording_requested: bool, + ) -> napi::Result<()> { + self.stop_mic_speaking_tap(); + let existing = { + let guard = self.mic.lock(); + guard.as_ref().map(|mic| (mic.track_sid(), mic.is_device())) + }; + if let Some((track_sid, was_device_mic)) = existing { + local + .unpublish_track(&track_sid) + .await + .map_err(|e| napi::Error::from_reason(format!("unpublish mic: {e}")))?; + if was_device_mic && !keep_device_recording_requested { + self.set_device_mic_recording_requested(false); + } + } + *self.mic.lock() = None; + Ok(()) + } + + async fn unpublish_existing_screen_audio(&self, local: &LocalParticipant) -> napi::Result<()> { + let existing = self.screen_audio.lock().take(); + if let Some(audio) = existing { + local + .unpublish_track(&audio.track_sid) + .await + .map_err(|e| napi::Error::from_reason(format!("unpublish screen audio: {e}")))?; + } + Ok(()) + } + + fn select_recording_device( + &self, + platform_audio: &PlatformAudio, + device_id: Option<&str>, + ) -> napi::Result> { + let requested_device_id = device_id.map(str::trim).unwrap_or_default(); + if requested_device_id.is_empty() || requested_device_id == "default" { + return Ok(Some("default".to_string())); + } + let raw = collect_factory_recording_devices()?; + let found = raw + .iter() + .any(|(id, _, _)| id.trim() == requested_device_id); + if !found { + return Err(napi::Error::from_reason(format!( + "recording device not found: {requested_device_id}" + ))); + } + let id = RecordingDeviceId::from_unchecked_guid(requested_device_id); + if self.mic.lock().is_some() { + platform_audio + .switch_recording_device(&id) + .map_err(|e| napi::Error::from_reason(format!("switch recording device: {e}")))?; + } else { + platform_audio + .set_recording_device(&id) + .map_err(|e| napi::Error::from_reason(format!("set recording device: {e}")))?; + } + Ok(Some(requested_device_id.to_string())) + } + + #[napi] + pub async fn publish_device_microphone(&self, opts: MicrophoneOptions) -> napi::Result<()> { + if self.state.load(Ordering::Acquire) != S_CONNECTED { + return Err(napi::Error::from_reason("not connected")); + } + let options = build_microphone_publish_options(&opts)?; + let local = self.local_participant()?; + let platform_audio = self.platform_audio()?; + let _selected_device_id = + self.select_recording_device(&platform_audio, opts.device_id.as_deref())?; + let deep_filter_requested = opts.deep_filter.unwrap_or(false); + let deep_filter_mic = if deep_filter_requested { + self.set_device_mic_recording_requested(true); + let noise_reduction_level = opts + .deep_filter_noise_reduction_level + .unwrap_or(audio::DEEP_FILTER_NOISE_REDUCTION_LEVEL_MAX); + self.try_start_deep_filter_microphone(&platform_audio, noise_reduction_level) + .await + } else { + None + }; + let apm_intent = audio::resolve_microphone_apm_intent( + opts.echo_cancellation, + opts.noise_suppression, + opts.auto_gain_control, + deep_filter_requested, + deep_filter_mic.is_some(), + ); + platform_audio + .configure_audio_processing(audio::processing_options( + apm_intent.echo_cancellation, + apm_intent.noise_suppression, + apm_intent.auto_gain_control, + )) + .map_err(|e| napi::Error::from_reason(format!("configure audio processing: {e}")))?; + self.unpublish_existing_microphone(&local, true).await?; + self.set_device_mic_recording_requested(true); + match deep_filter_mic { + Some(deep_filter_mic) => { + self.publish_deep_filter_microphone(&local, deep_filter_mic, options) + .await + } + None => self.publish_plain_device_microphone(&local, options).await, + } + } + + fn build_deep_filter_pipe( + &self, + noise_reduction_level: f64, + ) -> Result { + let source = NativeAudioSource::new( + AudioSourceOptions { + echo_cancellation: false, + noise_suppression: false, + auto_gain_control: false, + }, + deep_filter::DEEP_FILTER_SAMPLE_RATE_HZ, + deep_filter::DEEP_FILTER_NUM_CHANNELS, + DEEP_FILTER_SOURCE_QUEUE_MS, + ); + let stop = Arc::new(AtomicBool::new(false)); + let diagnostics = DeepFilterDiagnostics { + degraded_frames: Arc::new(AtomicU64::new(0)), + events: self.events.clone(), + dropped_engine_events: self.dropped_engine_events.clone(), + }; + let (frame_sender, frame_receiver) = + mpsc::sync_channel::(DEEP_FILTER_PIPE_QUEUE_FRAMES); + let ready = spawn_deep_filter_processing_thread( + noise_reduction_level, + source.clone(), + stop.clone(), + diagnostics.clone(), + frame_receiver, + )?; + Ok(DeepFilterPipeParts { + source, + pipe_stop: DeepFilterPipeStop { stop }, + diagnostics, + frame_sender, + ready, + }) + } + + async fn try_start_deep_filter_microphone( + &self, + platform_audio: &PlatformAudio, + noise_reduction_level: f64, + ) -> Option { + if let Err(error) = platform_audio.start_recording() { + self.emit_deep_filter_fallback(&format!("start recording: {error}")); + return None; + } + let capture_track = + LocalAudioTrack::create_audio_track("mic-capture", platform_audio.rtc_source()); + let DeepFilterPipeParts { + source, + pipe_stop, + diagnostics, + frame_sender, + ready, + } = match self.build_deep_filter_pipe(noise_reduction_level) { + Ok(parts) => parts, + Err(error) => { + self.emit_deep_filter_fallback(&error); + return None; + } + }; + match ready.await { + Ok(Ok(())) => {} + Ok(Err(error)) => { + self.emit_deep_filter_fallback(&error); + return None; + } + Err(_) => { + self.emit_deep_filter_fallback("deep filter thread exited before ready"); + return None; + } + } + let capture_count = Arc::new(AtomicU64::new(0)); + let generation = + install_deep_filter_capture_tap(frame_sender, diagnostics, capture_count.clone()); + let tap_guard = RecordedAudioTapGuard { generation }; + if !await_deep_filter_capture_started(&capture_count).await { + self.emit_deep_filter_fallback("no capture frames from device source"); + return None; + } + let track = LocalAudioTrack::create_audio_track("mic", RtcAudioSource::Native(source)); + emit_deep_filter_status(&self.events, &self.dropped_engine_events, "active", ""); + Some(DeepFilterMicrophone { + track, + capture_track, + pipe_stop, + tap_guard, + }) + } + + fn emit_deep_filter_fallback(&self, detail: &str) { + emit_deep_filter_status( + &self.events, + &self.dropped_engine_events, + "fallback", + detail, + ); + } + + async fn publish_deep_filter_microphone( + &self, + local: &LocalParticipant, + deep_filter_mic: DeepFilterMicrophone, + options: TrackPublishOptions, + ) -> napi::Result<()> { + let DeepFilterMicrophone { + track, + capture_track, + pipe_stop, + tap_guard, + } = deep_filter_mic; + let publication = match local + .publish_track(LocalTrack::Audio(track.clone()), options) + .await + { + Ok(publication) => publication, + Err(error) => { + if !self.mic.lock().as_ref().is_some_and(MicSource::is_device) { + self.set_device_mic_recording_requested(false); + } + return Err(napi::Error::from_reason(format!("publish mic: {error}"))); + } + }; + self.start_mic_speaking_tap(local, &track, &publication.sid()); + *self.mic.lock() = Some(MicSource::DeviceDeepFiltered { + track_sid: publication.sid(), + track, + capture_track, + _tap_guard: tap_guard, + _pipe_stop: pipe_stop, + }); + Ok(()) + } + + async fn publish_plain_device_microphone( + &self, + local: &LocalParticipant, + options: TrackPublishOptions, + ) -> napi::Result<()> { + let platform_audio = self.platform_audio()?; + let track = LocalAudioTrack::create_audio_track("mic", platform_audio.rtc_source()); + let publication = match local + .publish_track(LocalTrack::Audio(track.clone()), options) + .await + { + Ok(publication) => publication, + Err(error) => { + if !self.mic.lock().as_ref().is_some_and(MicSource::is_device) { + self.set_device_mic_recording_requested(false); + } + return Err(napi::Error::from_reason(format!("publish mic: {error}"))); + } + }; + self.start_mic_speaking_tap(local, &track, &publication.sid()); + *self.mic.lock() = Some(MicSource::Device { + track_sid: publication.sid(), + track, + }); + Ok(()) + } + + #[napi] + pub async fn publish_microphone( + &self, + sample_rate: u32, + num_channels: u32, + ) -> napi::Result<()> { + if self.state.load(Ordering::Acquire) != S_CONNECTED { + return Err(napi::Error::from_reason("not connected")); + } + if !valid_audio_format(sample_rate, num_channels) { + return Err(napi::Error::from_reason("invalid sample_rate/num_channels")); + } + let local = self.local_participant()?; + self.unpublish_existing_microphone(&local, false).await?; + self.set_device_mic_recording_requested(false); + let source = NativeAudioSource::new( + AudioSourceOptions::default(), + sample_rate, + num_channels, + self.max_audio_buffer_ms.load(Ordering::Relaxed) as u32, + ); + let track = + LocalAudioTrack::create_audio_track("mic", RtcAudioSource::Native(source.clone())); + let options = TrackPublishOptions { + source: TrackSource::Microphone, + red: true, + dtx: true, + ..Default::default() + }; + let publication = local + .publish_track(LocalTrack::Audio(track.clone()), options) + .await + .map_err(|e| napi::Error::from_reason(format!("publish mic: {e}")))?; + self.start_mic_speaking_tap(&local, &track, &publication.sid()); + *self.mic.lock() = Some(MicSource::Manual { + source, + sample_rate, + num_channels, + track_sid: publication.sid(), + track, + pcm_scratch: new_pcm_scratch(sample_rate, num_channels), + }); + Ok(()) + } + + #[napi] + pub async fn push_pcm( + &self, + data: Buffer, + sample_rate: u32, + num_channels: u32, + ) -> napi::Result { + let (source, scratch) = { + let guard = self.mic.lock(); + match guard.as_ref() { + Some(MicSource::Manual { + source, + sample_rate: mic_sample_rate, + num_channels: mic_num_channels, + pcm_scratch, + .. + }) if *mic_sample_rate == sample_rate && *mic_num_channels == num_channels => { + (source.clone(), pcm_scratch.clone()) + } + Some(_) => { + return Err(napi::Error::from_reason( + "pcm format does not match published mic or mic is device-backed", + )); + } + None => return Ok(false), + } + }; + let mut samples = scratch.lock().await; + let Some(frame) = + pcm16_audio_frame_into(data.as_ref(), sample_rate, num_channels, &mut samples) + else { + return Ok(false); + }; + self.send_audio_stats.record_push(now_millis()); + source + .capture_frame(&frame) + .await + .map_err(|e| napi::Error::from_reason(format!("capture_frame: {e}")))?; + Ok(true) + } + + #[napi] + pub async fn disconnect(&self) -> napi::Result<()> { + self.cancel_connection_intent(); + let _serial_guard = self.connect_serial.lock().await; + self.shutdown_connection_locked().await; + Ok(()) + } + + async fn shutdown_connection_locked(&self) { + self.cancel_connection_intent(); + if let Some(screen) = self.screen.lock().take() { + screen.video_sender.stop(); + if let Some(id) = screen.bus_capture_id.as_deref() { + frame_bus::unregister_sink(id); + } + } + *self.send_video_stats.lock() = None; + *self.texture_capability.lock() = + TextureCapability::unavailable(texture_source::TextureEncodeError::UnsupportedCodec); + *self.screen_audio.lock() = None; + self.stop_mic_speaking_tap(); + *self.mic.lock() = None; + self.set_device_mic_recording_requested(false); + self.stop_camera_preview_capture(); + if let Some(cam) = self.camera.lock().take() { + cam.stop(); + } + if let Some(cam) = self.screen_camera.lock().take() { + cam.stop(); + } + *self.platform_audio.lock() = None; + self.participant_volumes.lock().clear(); + self.byte_samples.lock().clear(); + *self.last_stats_json.lock() = None; + self.camera_live_background.clear(); + self.inbound_forwarders.clear(); + let room = self.room.lock().take(); + if let Some(room) = room { + let _ = room.close().await; + } + assert!(self.room.lock().is_none()); + assert_eq!(self.state.load(Ordering::Acquire), S_CLOSED); + } + + #[napi] + pub fn is_connected(&self) -> bool { + self.state.load(Ordering::Acquire) == S_CONNECTED + } + + #[napi] + pub async fn unpublish_screen_share(&self) -> napi::Result<()> { + let screen = self.screen.lock().take(); + let screen_camera = self.screen_camera.lock().take(); + if screen.is_none() && screen_camera.is_none() { + return Ok(()); + } + let screen_track_sid = screen.as_ref().map(|screen| screen.track_sid.clone()); + let screen_camera_track_sid = screen_camera + .as_ref() + .map(|screen_camera| screen_camera.track_sid().lock().clone()); + if let Some(screen) = screen { + screen.video_sender.stop(); + if let Some(id) = screen.bus_capture_id.as_deref() { + frame_bus::unregister_sink(id); + } + } + if let Some(screen_camera) = screen_camera { + screen_camera.stop(); + } + *self.texture_capability.lock() = + TextureCapability::unavailable(texture_source::TextureEncodeError::UnsupportedCodec); + *self.send_video_stats.lock() = None; + let local = { + let guard = self.room.lock(); + guard.as_ref().map(|room| room.local_participant()) + }; + let Some(local) = local else { + return Ok(()); + }; + let mut first_error: Option = None; + if let Some(track_sid) = screen_track_sid.as_ref() { + let result = local + .unpublish_track(track_sid) + .await + .map(|_| ()) + .map_err(|e| napi::Error::from_reason(format!("unpublish screen: {e}"))); + record_first_error(&mut first_error, result); + } + if let Some(track_sid) = screen_camera_track_sid { + assert!(!track_sid.is_empty()); + let result = match TrackSid::try_from(track_sid) { + Ok(track_sid) => local + .unpublish_track(&track_sid) + .await + .map(|_| ()) + .map_err(|e| { + napi::Error::from_reason(format!("unpublish device screen share: {e}")) + }), + Err(sid) => Err(napi::Error::from_reason(format!( + "unpublish device screen share: invalid track sid {sid}" + ))), + }; + record_first_error(&mut first_error, result); + } + let audio_result = self.unpublish_existing_screen_audio(&local).await; + record_first_error(&mut first_error, audio_result); + match first_error { + Some(error) => Err(error), + None => Ok(()), + } + } + + #[napi] + pub fn is_publishing_screen(&self) -> bool { + self.screen.lock().is_some() || self.screen_camera.lock().is_some() + } + + #[napi] + pub async fn publish_screen_share_audio( + &self, + sample_rate: u32, + num_channels: u32, + ) -> napi::Result<()> { + if self.state.load(Ordering::Acquire) != S_CONNECTED { + return Err(napi::Error::from_reason("not connected")); + } + if !valid_audio_format(sample_rate, num_channels) { + return Err(napi::Error::from_reason("invalid sample_rate/num_channels")); + } + self.disable_platform_recording_if_device_mic_inactive(); + { + let guard = self.screen_audio.lock(); + if guard + .as_ref() + .is_some_and(|audio| audio.matches_format(sample_rate, num_channels)) + { + drop(guard); + self.schedule_delayed_platform_recording_disable_if_device_mic_inactive(); + return Ok(()); + } + } + let local = self.local_participant()?; + self.unpublish_existing_screen_audio(&local).await?; + let source = NativeAudioSource::new( + AudioSourceOptions::default(), + sample_rate, + num_channels, + self.max_audio_buffer_ms.load(Ordering::Relaxed) as u32, + ); + let track = LocalAudioTrack::create_audio_track( + "screen-audio", + RtcAudioSource::Native(source.clone()), + ); + let options = TrackPublishOptions { + audio_encoding: Some(AudioEncoding { + max_bitrate: SCREEN_SHARE_AUDIO_MAX_BITRATE_BPS, + }), + source: TrackSource::ScreenshareAudio, + stream: LIVEKIT_TRACK_SOURCE_SCREEN_SHARE.to_string(), + red: true, + dtx: false, + ..Default::default() + }; + let publication = local + .publish_track(LocalTrack::Audio(track), options) + .await + .map_err(|e| napi::Error::from_reason(format!("publish screen audio: {e}")))?; + let drain_stop = Arc::new(AtomicBool::new(false)); + let pcm_scratch = new_pcm_scratch(sample_rate, num_channels); + spawn_screen_audio_drain( + self.screen_audio_ring.clone(), + source.clone(), + pcm_scratch.clone(), + drain_stop.clone(), + ); + *self.screen_audio.lock() = Some(ScreenAudioSource { + source, + sample_rate, + num_channels, + track_sid: publication.sid(), + pcm_scratch, + drain_stop, + ring: self.screen_audio_ring.clone(), + }); + self.disable_platform_recording_if_device_mic_inactive(); + self.schedule_delayed_platform_recording_disable_if_device_mic_inactive(); + Ok(()) + } + + #[napi] + pub async fn push_screen_share_pcm( + &self, + data: Buffer, + sample_rate: u32, + num_channels: u32, + ) -> napi::Result { + let (source, scratch) = { + let guard = self.screen_audio.lock(); + match guard.as_ref() { + Some(audio) + if audio.sample_rate == sample_rate && audio.num_channels == num_channels => + { + (audio.source.clone(), audio.pcm_scratch.clone()) + } + Some(_) => { + return Err(napi::Error::from_reason( + "pcm format does not match published screen-share audio", + )); + } + None => return Ok(false), + } + }; + let mut samples = scratch.lock().await; + let Some(frame) = + pcm16_audio_frame_into(data.as_ref(), sample_rate, num_channels, &mut samples) + else { + return Ok(false); + }; + self.send_audio_stats.record_push(now_millis()); + source + .capture_frame(&frame) + .await + .map_err(|e| napi::Error::from_reason(format!("capture screen audio frame: {e}")))?; + Ok(true) + } + + #[napi] + pub async fn push_screen_share_float( + &self, + data: Buffer, + sample_rate: u32, + num_channels: u32, + ) -> napi::Result { + let (source, scratch) = { + let guard = self.screen_audio.lock(); + match guard.as_ref() { + Some(audio) + if audio.sample_rate == sample_rate && audio.num_channels == num_channels => + { + (audio.source.clone(), audio.pcm_scratch.clone()) + } + Some(_) => { + return Err(napi::Error::from_reason( + "float pcm format does not match published screen-share audio", + )); + } + None => return Ok(false), + } + }; + let mut samples = scratch.lock().await; + let Some(frame) = + f32_audio_frame_into(data.as_ref(), sample_rate, num_channels, &mut samples) + else { + return Ok(false); + }; + self.send_audio_stats.record_push(now_millis()); + source + .capture_frame(&frame) + .await + .map_err(|e| napi::Error::from_reason(format!("capture screen audio frame: {e}")))?; + Ok(true) + } + + #[napi] + pub async fn unpublish_screen_share_audio(&self) -> napi::Result<()> { + let local = { + let guard = self.room.lock(); + guard.as_ref().map(|room| room.local_participant()) + }; + if let Some(local) = local { + self.unpublish_existing_screen_audio(&local).await?; + } else { + *self.screen_audio.lock() = None; + } + Ok(()) + } + + #[napi] + pub fn is_publishing_screen_audio(&self) -> bool { + self.screen_audio.lock().is_some() + } + + #[napi] + pub async fn set_mic_enabled(&self, enabled: bool) { + let mic = { + let guard = self.mic.lock(); + guard + .as_ref() + .map(|mic| (mic.track(), mic.capture_track(), mic.is_device())) + }; + let Some((track, capture_track, is_device_mic)) = mic else { + if !enabled { + self.set_device_mic_recording_requested(false); + } + return; + }; + if is_device_mic { + self.set_device_mic_recording_requested(enabled); + } + if enabled && capture_track.is_some() { + self.restart_platform_recording_for_deep_filter(); + } + for track in [Some(track), capture_track].into_iter().flatten() { + if enabled { + track.unmute(); + } else { + track.mute(); + } + } + } + + fn restart_platform_recording_for_deep_filter(&self) { + let Some(platform_audio) = self.platform_audio.lock().as_ref().cloned() else { + return; + }; + if let Err(error) = platform_audio.start_recording() { + emit_deep_filter_status( + &self.events, + &self.dropped_engine_events, + "degraded", + &format!("restart recording: {error}"), + ); + } + } + + #[napi] + pub async fn list_audio_output_devices(&self) -> napi::Result { + if self.platform_audio.lock().is_none() { + return Ok(audio::default_output_devices_json()); + } + run_audio_device_module_blocking(|| { + let raw = collect_factory_playout_devices()?; + let shaped = audio::shape_output_devices(&raw); + Ok(audio::output_devices_json(&shaped)) + }) + .await + } + + #[napi] + pub async fn list_audio_input_devices(&self) -> napi::Result { + if self.platform_audio.lock().is_none() { + return Ok(audio::default_input_devices_json()); + } + run_audio_device_module_blocking(|| { + let raw = collect_factory_recording_devices()?; + let shaped = audio::shape_input_devices(&raw); + Ok(audio::input_devices_json(&shaped)) + }) + .await + } + + #[napi] + pub async fn set_audio_output_device(&self, device_id: String) -> napi::Result<()> { + run_audio_device_module_blocking(move || { + let runtime = LkRuntime::instance(); + let factory = runtime.pc_factory(); + let raw = collect_factory_playout_devices_from(factory)?; + let guid = audio::resolve_playout_device_guid(&device_id, &raw).map_err(|error| { + napi::Error::from_reason(format!("set audio output device: {error}")) + })?; + let platform_playout_active = + factory.is_platform_adm_active() && factory.adm_playout_enabled(); + let plan = audio::playout_switch_plan( + platform_playout_active, + factory.playout_is_initialized(), + ); + match plan { + audio::PlayoutSwitchPlan::ColdSelect => { + set_factory_playout_device_by_guid(factory, &guid) + } + audio::PlayoutSwitchPlan::HotSwap => { + hot_swap_factory_playout_device(factory, &guid) + } + } + }) + .await + } + + #[napi] + pub async fn set_participant_volume(&self, participant_sid: String, volume: f64) { + let clamped = audio::clamp_volume(volume); + self.participant_volumes + .lock() + .insert(participant_sid.clone(), clamped); + let muted = audio::is_muted_volume(clamped); + + let participant = { + let guard = self.room.lock(); + guard.as_ref().and_then(|room| { + room.remote_participants() + .into_values() + .find(|p| p.sid().to_string() == participant_sid) + }) + }; + let Some(participant) = participant else { + return; + }; + for (_sid, publication) in participant.track_publications() { + if let Some(RemoteTrack::Audio(track)) = publication.track() { + if muted { + track.disable(); + } else { + track.enable(); + } + } + } + } + + #[napi] + pub async fn set_remote_track_subscription( + &self, + participant_identity: String, + source: String, + subscribed: bool, + enabled: bool, + quality: Option, + ) -> napi::Result<()> { + let Some(target_source) = parse_track_source(&source) else { + return Err(napi::Error::from_reason(format!( + "unsupported remote track source: {source}" + ))); + }; + let target_quality = quality.as_deref().and_then(parse_video_quality); + let participant = { + let guard = self.room.lock(); + guard.as_ref().and_then(|room| { + room.remote_participants() + .into_values() + .find(|p| p.identity().to_string() == participant_identity) + }) + }; + let Some(participant) = participant else { + if subscribed { + if is_optional_remote_subscription_target(target_source) { + return Ok(()); + } + return Err(remote_subscription_target_error( + "participant", + &participant_identity, + &source, + )); + } + return Ok(()); + }; + let mut visited = 0; + let mut matched = 0; + for publication in participant.track_publications().into_values() { + visited += 1; + if visited > REMOTE_TRACK_SUBSCRIPTION_PUBLICATIONS_MAX { + return Err(napi::Error::from_reason(format!( + "remote track subscription publication scan exceeded cap {}", + REMOTE_TRACK_SUBSCRIPTION_PUBLICATIONS_MAX + ))); + } + if publication.source() != target_source { + continue; + } + matched += 1; + publication.set_subscribed(subscribed); + if !subscribed { + continue; + } + publication.set_enabled(enabled); + if publication.kind() == TrackKind::Video + && let Some(quality) = target_quality + { + publication.set_video_quality(quality); + } + } + if subscribed && matched == 0 { + if is_optional_remote_subscription_target(target_source) { + return Ok(()); + } + return Err(remote_subscription_target_error( + "publication", + &participant_identity, + &source, + )); + } + Ok(()) + } + + #[napi] + pub async fn publish_data( + &self, + payload: Buffer, + reliable: bool, + topic: Option, + destination_identities: Option>, + ) -> napi::Result<()> { + let local = self.local_participant()?; + let packet = DataPacket { + payload: payload.to_vec(), + topic: topic.and_then(|value| { + let trimmed = value.trim(); + if trimmed.is_empty() { + None + } else { + Some(trimmed.to_string()) + } + }), + reliable, + destination_identities: destination_identities + .unwrap_or_default() + .into_iter() + .filter_map(|identity| { + let trimmed = identity.trim(); + if trimmed.is_empty() { + None + } else { + Some(ParticipantIdentity(trimmed.to_string())) + } + }) + .collect(), + }; + local + .publish_data(packet) + .await + .map_err(|e| napi::Error::from_reason(format!("publish data: {e}"))) + } + + async fn publish_camera_capture( + &self, + opts: CameraOptions, + publication_kind: CameraPublicationKind, + slot: &Mutex>, + ) -> napi::Result<()> { + if self.state.load(Ordering::Acquire) != S_CONNECTED { + return Err(napi::Error::from_reason("not connected")); + } + let mut background = native_camera_background_config(&opts)?; + background.live_background = Some(self.camera_live_background.clone()); + let request = camera::CameraRequest::from_opts( + opts.device_id.as_deref(), + opts.width, + opts.height, + opts.frame_rate, + opts.mirror.unwrap_or(false), + background, + ); + request + .background + .ensure_supported_for_publish() + .map_err(napi::Error::from_reason)?; + + let worker = open_camera_capture_worker(request.clone()).await?; + let source = NativeVideoSource::new( + VideoResolution { + width: worker.opened.width & !1, + height: worker.opened.height & !1, + }, + publication_kind.is_screencast(), + ); + let track = LocalVideoTrack::create_video_track( + publication_kind.track_name(), + RtcVideoSource::Native(source.clone()), + ); + let options = build_camera_publish_options(&opts, publication_kind)?; + let local = { + let guard = self.room.lock(); + guard + .as_ref() + .map(|room| room.local_participant()) + .ok_or_else(|| napi::Error::from_reason("not connected"))? + }; + let publication = match local.publish_track(LocalTrack::Video(track), options).await { + Ok(p) => p, + Err(e) => { + worker.stop.store(true, Ordering::Release); + return Err(napi::Error::from_reason(format!( + "publish {}: {e}", + publication_kind.track_name() + ))); + } + }; + + let track_sid = Arc::new(Mutex::new(publication.sid().to_string())); + let capture = DeviceCameraCapture { + source, + participant_sid: local.sid().to_string(), + participant_identity: local.identity().to_string(), + track_name: publication.name(), + track_source: events::track_source_str(publication.source()).to_string(), + request, + }; + store_camera_slot( + slot, + CameraSource::Device { + track_sid: track_sid.clone(), + stop: worker.stop.clone(), + capture: Some(capture.clone()), + }, + ); + reconcile_camera_slot_sid(&local, &track_sid, publication_kind.track_source()); + let sinks = self.device_camera_capture_sinks(&capture, &track_sid); + if worker.sinks_tx.send(sinks).is_err() { + worker.stop.store(true, Ordering::Release); + remove_camera_slot_if_held(slot, &track_sid); + let current_sid = track_sid.lock().clone(); + assert!(!current_sid.is_empty()); + if let Ok(current_sid) = TrackSid::try_from(current_sid) { + let _ = local.unpublish_track(¤t_sid).await; + } + return Err(napi::Error::from_reason( + "camera worker exited before publish", + )); + } + + Ok(()) + } + + fn device_camera_capture_sinks( + &self, + capture: &DeviceCameraCapture, + track_sid: &Arc>, + ) -> camera::CameraCaptureSinks { + assert!(!capture.participant_sid.is_empty()); + assert!(!track_sid.lock().is_empty()); + let frame_sink = local_video_frame_sink( + self.video_frames.clone(), + self.dropped_video_frame_callbacks.clone(), + capture.participant_sid.clone(), + capture.participant_identity.clone(), + track_sid.clone(), + capture.track_name.clone(), + capture.track_source.clone(), + ); + let frame_sink_active = local_video_frame_sink_active(self.video_frames.clone()); + camera::CameraCaptureSinks { + source: capture.source.clone(), + frame_sink, + frame_sink_active, + } + } + + async fn publish_native_camera_sink_source( + &self, + opts: CameraOptions, + slot: &Mutex>, + ) -> napi::Result>> { + if self.state.load(Ordering::Acquire) != S_CONNECTED { + return Err(napi::Error::from_reason("not connected")); + } + if !crate::native_camera::platform_native_backgrounds_available() { + return Err(napi::Error::from_reason( + crate::native_camera::unavailable_error(), + )); + } + let width = opts.width.unwrap_or(camera::DEFAULT_WIDTH) & !1; + let height = opts.height.unwrap_or(camera::DEFAULT_HEIGHT) & !1; + let frame_rate = opts.frame_rate.unwrap_or(camera::DEFAULT_FPS); + if !valid_even_video_dims(width, height) || frame_rate == 0 { + return Err(napi::Error::from_reason( + "invalid native camera sink options", + )); + } + native_camera_background_config(&opts)? + .ensure_supported_for_publish() + .map_err(napi::Error::from_reason)?; + + let source = NativeVideoSource::new(VideoResolution { width, height }, false); + let track = LocalVideoTrack::create_video_track( + CameraPublicationKind::Camera.track_name(), + RtcVideoSource::Native(source.clone()), + ); + let options = build_camera_publish_options(&opts, CameraPublicationKind::Camera)?; + let local = { + let guard = self.room.lock(); + guard + .as_ref() + .map(|room| room.local_participant()) + .ok_or_else(|| napi::Error::from_reason("not connected"))? + }; + let publication = local + .publish_track(LocalTrack::Video(track), options) + .await + .map_err(|e| napi::Error::from_reason(format!("publish native camera sink: {e}")))?; + + let target_fps = f64::from(frame_rate); + let stats = Arc::new(AdaptiveVideoStats::new( + target_fps, + DEFAULT_MIN_VIDEO_FPS.min(target_fps), + false, + now_millis(), + )); + let video_sender = + AdaptiveVideoSender::new(source, stats, VideoPacingMode::Source, target_fps); + let frame_sink = Arc::new(BusSenderSink { + sender: video_sender.clone(), + texture_capability: *self.texture_capability.lock(), + }); + let track_sid = Arc::new(Mutex::new(publication.sid().to_string())); + store_camera_slot( + slot, + CameraSource::NativeBuffered { + track_sid: track_sid.clone(), + video_sender, + frame_sink, + }, + ); + reconcile_camera_slot_sid(&local, &track_sid, TrackSource::Camera); + Ok(track_sid) + } + + #[napi] + pub async fn publish_camera(&self, opts: CameraOptions) -> napi::Result<()> { + if self.camera.lock().is_some() { + return Err(napi::Error::from_reason("camera already published")); + } + self.stop_camera_preview_capture(); + self.publish_camera_capture(opts, CameraPublicationKind::Camera, &self.camera) + .await + } + + #[napi] + pub async fn publish_native_camera_sink( + &self, + opts: CameraOptions, + ) -> napi::Result { + if self.camera.lock().is_some() { + return Err(napi::Error::from_reason("camera already published")); + } + self.stop_camera_preview_capture(); + let track_sid = self + .publish_native_camera_sink_source(opts, &self.camera) + .await?; + let track_sid = track_sid.lock().clone(); + assert!(!track_sid.is_empty()); + Ok(ProcessedCameraPublishResult { track_sid }) + } + + #[napi] + pub fn create_camera_frame_sink_handle<'env>( + &self, + env: Env, + ) -> napi::Result>> { + let sink = { + let guard = self.camera.lock(); + let Some(camera) = guard.as_ref() else { + return Ok(None); + }; + let Some(sink) = camera.native_frame_sink() else { + return Ok(None); + }; + sink + }; + let external = ScreenFrameSinkHandleExternal { + handle: create_native_screen_frame_sink_handle(sink), + }; + let raw_env = env.raw(); + let value = unsafe { ScreenFrameSinkHandleExternal::to_napi_value(raw_env, external)? }; + Ok(Some(unsafe { Unknown::from_raw_unchecked(raw_env, value) })) + } + + async fn publish_processed_camera_source( + &self, + opts: ProcessedCameraOptions, + slot: &Mutex>, + ) -> napi::Result>> { + if self.state.load(Ordering::Acquire) != S_CONNECTED { + return Err(napi::Error::from_reason("not connected")); + } + if !valid_even_video_dims(opts.width, opts.height) || opts.frame_rate == 0 { + return Err(napi::Error::from_reason("invalid processed camera options")); + } + let source = NativeVideoSource::new( + VideoResolution { + width: opts.width, + height: opts.height, + }, + false, + ); + let track = LocalVideoTrack::create_video_track( + CameraPublicationKind::Camera.track_name(), + RtcVideoSource::Native(source.clone()), + ); + let camera_opts = CameraOptions { + device_id: None, + width: Some(opts.width), + height: Some(opts.height), + frame_rate: Some(opts.frame_rate), + mirror: None, + background_mode: None, + background_custom_media_path: None, + background_custom_media_kind: None, + background_blur_strength: None, + codec: None, + max_bitrate_bps: None, + max_framerate: Some(f64::from(opts.frame_rate)), + }; + let options = build_camera_publish_options(&camera_opts, CameraPublicationKind::Camera)?; + let local = { + let guard = self.room.lock(); + guard + .as_ref() + .map(|room| room.local_participant()) + .ok_or_else(|| napi::Error::from_reason("not connected"))? + }; + let publication = local + .publish_track(LocalTrack::Video(track), options) + .await + .map_err(|e| napi::Error::from_reason(format!("publish processed camera: {e}")))?; + let target_fps = f64::from(opts.frame_rate); + let stats = Arc::new(AdaptiveVideoStats::new( + target_fps, + DEFAULT_MIN_VIDEO_FPS.min(target_fps), + false, + now_millis(), + )); + let video_sender = + AdaptiveVideoSender::new(source, stats, VideoPacingMode::Source, target_fps); + let track_sid = Arc::new(Mutex::new(publication.sid().to_string())); + store_camera_slot( + slot, + CameraSource::Processed { + track_sid: track_sid.clone(), + video_sender, + }, + ); + reconcile_camera_slot_sid(&local, &track_sid, TrackSource::Camera); + Ok(track_sid) + } + + #[napi] + pub async fn publish_processed_camera( + &self, + opts: ProcessedCameraOptions, + ) -> napi::Result { + if self.camera.lock().is_some() { + return Err(napi::Error::from_reason("camera already published")); + } + self.stop_camera_preview_capture(); + let track_sid = self + .publish_processed_camera_source(opts, &self.camera) + .await?; + let track_sid = track_sid.lock().clone(); + assert!(!track_sid.is_empty()); + Ok(ProcessedCameraPublishResult { track_sid }) + } + + #[napi] + pub fn push_processed_camera_frame(&self, frame: ProcessedCameraFrame) -> napi::Result { + let sender = { + let guard = self.camera.lock(); + guard.as_ref().and_then(CameraSource::processed_sender) + }; + let Some(sender) = sender else { + return Ok(false); + }; + let pending = processed_camera_frame_to_pending(frame)?; + Ok(!matches!(sender.enqueue(pending), EnqueueResult::Rejected)) + } + + #[napi] + pub fn push_camera_background_frame(&self, frame: ProcessedCameraFrame) -> napi::Result { + validate_processed_camera_frame(&frame)?; + Ok(self.camera_live_background.store_tight_i420( + frame.data.as_ref(), + frame.width, + frame.height, + )) + } + + #[napi] + pub fn clear_camera_background_frame(&self) { + self.camera_live_background.clear(); + } + + #[napi] + pub async fn publish_device_screen_share(&self, opts: CameraOptions) -> napi::Result<()> { + self.unpublish_screen_share().await?; + self.publish_camera_capture( + opts, + CameraPublicationKind::ScreenShare, + &self.screen_camera, + ) + .await + } + + #[napi] + pub fn list_camera_devices(&self) -> napi::Result> { + camera::list_devices() + .map(|devices| { + devices + .into_iter() + .map(|device| CameraDeviceInfo { + device_id: device.device_id, + label: device.label, + description: device.description, + index: device.index, + device_id_aliases: device.device_id_aliases, + }) + .collect() + }) + .map_err(napi::Error::from_reason) + } + + #[napi] + pub async fn unpublish_camera(&self) -> napi::Result<()> { + let Some(cam) = self.camera.lock().take() else { + return Ok(()); + }; + cam.stop(); + let track_sid = cam.track_sid().lock().clone(); + assert!(!track_sid.is_empty()); + let local = { + let guard = self.room.lock(); + guard.as_ref().map(|room| room.local_participant()) + }; + let Some(local) = local else { + return Ok(()); + }; + let track_sid = TrackSid::try_from(track_sid).map_err(|sid| { + napi::Error::from_reason(format!("unpublish camera: invalid track sid {sid}")) + })?; + local + .unpublish_track(&track_sid) + .await + .map_err(|e| napi::Error::from_reason(format!("unpublish camera: {e}")))?; + Ok(()) + } + + #[napi] + pub fn is_publishing_camera(&self) -> bool { + self.camera.lock().is_some() + } + + #[napi] + pub async fn start_camera_preview( + &self, + opts: CameraOptions, + ) -> napi::Result { + if self.camera.lock().is_some() { + return Err(napi::Error::from_reason( + "camera already published; use the published camera stream for preview", + )); + } + self.stop_camera_preview_capture(); + let mut background = native_camera_background_config(&opts)?; + background.live_background = Some(self.camera_live_background.clone()); + let request = camera::CameraRequest::from_opts( + opts.device_id.as_deref(), + opts.width, + opts.height, + opts.frame_rate, + opts.mirror.unwrap_or(false), + background, + ); + request + .background + .ensure_supported_for_publish() + .map_err(napi::Error::from_reason)?; + + let stop = Arc::new(AtomicBool::new(false)); + let (result_tx, result_rx) = std::sync::mpsc::channel(); + let (source_tx, source_rx) = std::sync::mpsc::channel(); + camera::spawn_capture_worker(request, result_tx, source_rx, stop.clone()); + + let opened = tokio::task::spawn_blocking(move || result_rx.recv()) + .await + .map_err(|e| napi::Error::from_reason(format!("camera preview open task: {e}")))? + .map_err(|_| napi::Error::from_reason("camera preview worker exited before open"))? + .map_err(napi::Error::from_reason)?; + let width = opened.width & !1; + let height = opened.height & !1; + assert!(width >= 2); + assert!(height >= 2); + + let source = NativeVideoSource::new(VideoResolution { width, height }, false); + let frame_sink = local_video_frame_sink( + self.video_frames.clone(), + self.dropped_video_frame_callbacks.clone(), + CAMERA_PREVIEW_TRACK_SID.to_string(), + String::new(), + Arc::new(Mutex::new(CAMERA_PREVIEW_TRACK_SID.to_string())), + "camera-preview".to_string(), + events::track_source_str(TrackSource::Camera).to_string(), + ); + let frame_sink_active = local_video_frame_sink_active(self.video_frames.clone()); + let sinks = camera::CameraCaptureSinks { + source, + frame_sink, + frame_sink_active, + }; + if source_tx.send(sinks).is_err() { + stop.store(true, Ordering::Release); + return Err(napi::Error::from_reason( + "camera preview worker exited before start", + )); + } + + if let Some(previous) = self.camera_preview.lock().replace(stop) { + previous.store(true, Ordering::Release); + } + Ok(CameraPreviewInfo { + track_sid: CAMERA_PREVIEW_TRACK_SID.to_string(), + width, + height, + frame_rate: opened.fps, + }) + } + + #[napi] + pub fn stop_camera_preview(&self) { + self.stop_camera_preview_capture(); + } + + fn stop_camera_preview_capture(&self) { + if let Some(stop) = self.camera_preview.lock().take() { + stop.store(true, Ordering::Release); + } + assert!(self.camera_preview.lock().is_none()); + } + + fn device_camera_capture( + &self, + ) -> napi::Result<(DeviceCameraCapture, Arc>, Arc)> { + let guard = self.camera.lock(); + match guard.as_ref() { + None => Err(napi::Error::from_reason("camera is not published")), + Some(CameraSource::Device { + track_sid, + stop, + capture: Some(capture), + }) => Ok((capture.clone(), track_sid.clone(), stop.clone())), + Some(_) => Err(napi::Error::from_reason( + "camera capture is not device-managed", + )), + } + } + + #[napi] + pub async fn update_camera_capture(&self, opts: CameraOptions) -> napi::Result<()> { + let (capture, track_sid, old_stop) = self.device_camera_capture()?; + self.stop_camera_preview_capture(); + let mut background = native_camera_background_config(&opts)?; + background.live_background = Some(self.camera_live_background.clone()); + let request = camera::CameraRequest::from_opts( + opts.device_id.as_deref(), + opts.width, + opts.height, + opts.frame_rate, + opts.mirror.unwrap_or(false), + background, + ); + request + .background + .ensure_supported_for_publish() + .map_err(napi::Error::from_reason)?; + match camera_swap_order(&capture.request.selector, &request.selector) { + CameraSwapOrder::OpenNewThenStopOld => { + self.swap_camera_open_new_then_stop_old(&capture, &track_sid, &old_stop, request) + .await + } + CameraSwapOrder::StopOldThenOpenNew => { + self.swap_camera_stop_old_then_open_new(&capture, &track_sid, &old_stop, request) + .await + } + } + } + + async fn start_device_camera_worker( + &self, + capture: &DeviceCameraCapture, + track_sid: &Arc>, + request: camera::CameraRequest, + ) -> napi::Result> { + let worker = open_camera_capture_worker(request).await?; + let sinks = self.device_camera_capture_sinks(capture, track_sid); + if worker.sinks_tx.send(sinks).is_err() { + worker.stop.store(true, Ordering::Release); + return Err(napi::Error::from_reason( + "camera worker exited before capture", + )); + } + Ok(worker.stop) + } + + async fn swap_camera_open_new_then_stop_old( + &self, + capture: &DeviceCameraCapture, + track_sid: &Arc>, + old_stop: &Arc, + request: camera::CameraRequest, + ) -> napi::Result<()> { + let swapped_stop = self + .start_device_camera_worker(capture, track_sid, request.clone()) + .await?; + old_stop.store(true, Ordering::Release); + let committed = + commit_device_camera_swap(&self.camera, track_sid, old_stop, &swapped_stop, &request); + if !committed { + swapped_stop.store(true, Ordering::Release); + return Err(napi::Error::from_reason( + "camera capture changed during update", + )); + } + Ok(()) + } + + async fn swap_camera_stop_old_then_open_new( + &self, + capture: &DeviceCameraCapture, + track_sid: &Arc>, + old_stop: &Arc, + request: camera::CameraRequest, + ) -> napi::Result<()> { + old_stop.store(true, Ordering::Release); + let error = match self + .start_device_camera_worker(capture, track_sid, request.clone()) + .await + { + Ok(swapped_stop) => { + let committed = commit_device_camera_swap( + &self.camera, + track_sid, + old_stop, + &swapped_stop, + &request, + ); + if !committed { + swapped_stop.store(true, Ordering::Release); + return Err(napi::Error::from_reason( + "camera capture changed during update", + )); + } + return Ok(()); + } + Err(error) => error, + }; + let restored_stop = self + .start_device_camera_worker(capture, track_sid, capture.request.clone()) + .await + .map_err(|restore_error| { + napi::Error::from_reason(format!( + "{}; restoring previous camera capture failed: {}", + error.reason, restore_error.reason + )) + })?; + let committed = commit_device_camera_swap( + &self.camera, + track_sid, + old_stop, + &restored_stop, + &capture.request, + ); + if !committed { + restored_stop.store(true, Ordering::Release); + return Err(napi::Error::from_reason( + "camera capture changed during update", + )); + } + Err(error) + } + + #[napi] + pub async fn get_connection_stats(&self) -> String { + if let Some(json) = self.last_stats_json.lock().clone() { + return json; + } + let stats = self.collect_stats().await; + stats_mod::stats_to_json(&stats) + } + + async fn collect_stats(&self) -> stats_mod::ConnectionStats { + let snapshot = { + let guard = self.room.lock(); + guard.as_ref().map(snapshot_room_participants) + }; + let screen_metadata = self.screen_source_metadata(); + let Some((room, local, remotes)) = snapshot else { + return stats_mod::ConnectionStats { + send: Some(self.send_health_snapshot()), + ..Default::default() + }; + }; + let mut stats = collect_stats_for(local, remotes, &self.byte_samples).await; + absorb_room_rtt(&room, &mut stats).await; + self.record_outbound_video_egress_fps(&stats); + let send = self.send_health_snapshot(); + annotate_screen_share_stats(&mut stats, screen_metadata, &send); + stats.send = Some(send); + stats + } + + fn record_outbound_video_egress_fps(&self, stats: &stats_mod::ConnectionStats) { + let video_stats = self.send_video_stats.lock().clone(); + if let (Some(video_stats), Some(fps)) = (video_stats, outbound_screenshare_video_fps(stats)) + { + video_stats.record_egress_fps(now_millis(), fps); + } + } + + fn video_telemetry_extras(&self) -> VideoTelemetryExtras { + let screen = self.screen.lock(); + match screen.as_ref() { + Some(screen) => VideoTelemetryExtras { + pacing_mode: screen.video_sender.pacing.as_label().to_string(), + pacing_target_fps: screen.video_sender.target_fps, + queue_capacity: ENCODER_QUEUE_CAPACITY as u64, + bus_active: screen.bus_capture_id.is_some(), + }, + None => VideoTelemetryExtras::default(), + } + } + + fn send_health_snapshot(&self) -> SendHealthSnapshot { + let video_stats = self.send_video_stats.lock().clone(); + match video_stats { + Some(stats) => stats.snapshot(&self.send_audio_stats, self.video_telemetry_extras()), + None => SendHealthSnapshot::idle(&self.send_audio_stats), + } + } + + fn screen_source_metadata(&self) -> Option { + self.screen + .lock() + .as_ref() + .map(|screen| screen.metadata.clone()) + } + + fn start_stats_task(&self) { + if self.stats_running.swap(true, Ordering::AcqRel) { + return; + } + let state = self.state.clone(); + let room_slot = self.room.clone(); + let screen_slot = self.screen.clone(); + let events_slot = self.events.clone(); + let dropped_engine_events = self.dropped_engine_events.clone(); + let byte_samples = self.byte_samples.clone(); + let last_stats_json = self.last_stats_json.clone(); + let send_video_stats = self.send_video_stats.clone(); + let send_audio_stats = self.send_audio_stats.clone(); + tokio::spawn(async move { + let mut interval = tokio::time::interval(std::time::Duration::from_secs(1)); + loop { + interval.tick().await; + if state.load(Ordering::Acquire) != S_CONNECTED { + continue; + } + let snapshot = { + let guard = room_slot.lock(); + guard.as_ref().map(snapshot_room_participants) + }; + let Some((room, local, remotes)) = snapshot else { + continue; + }; + let mut stats = collect_stats_for(local, remotes, &byte_samples).await; + absorb_room_rtt(&room, &mut stats).await; + let video_stats = send_video_stats.lock().clone(); + if let (Some(video), Some(fps)) = + (video_stats.as_ref(), outbound_screenshare_video_fps(&stats)) + { + video.record_egress_fps(now_millis(), fps); + } + let extras = { + let screen = screen_slot.lock(); + match screen.as_ref() { + Some(screen) => VideoTelemetryExtras { + pacing_mode: screen.video_sender.pacing.as_label().to_string(), + pacing_target_fps: screen.video_sender.target_fps, + queue_capacity: ENCODER_QUEUE_CAPACITY as u64, + bus_active: screen.bus_capture_id.is_some(), + }, + None => VideoTelemetryExtras::default(), + } + }; + let send = match video_stats { + Some(video) => video.snapshot(&send_audio_stats, extras), + None => SendHealthSnapshot::idle(&send_audio_stats), + }; + let screen_metadata = screen_slot + .lock() + .as_ref() + .map(|screen| screen.metadata.clone()); + annotate_screen_share_stats(&mut stats, screen_metadata, &send); + stats.send = Some(send); + let json = stats_mod::stats_to_json(&stats); + *last_stats_json.lock() = Some(json.clone()); + emit_engine_event( + &events_slot, + &dropped_engine_events, + "stats".to_string(), + json, + ); + } + }); + } + + #[napi] + pub fn inbound_audio_frames(&self) -> f64 { + self.inbound_audio.load(Ordering::Relaxed) as f64 + } + + #[napi] + pub fn inbound_video_frames(&self) -> f64 { + self.inbound_video.load(Ordering::Relaxed) as f64 + } + + #[napi] + pub fn dropped_video_frame_callbacks(&self) -> f64 { + self.dropped_video_frame_callbacks.load(Ordering::Relaxed) as f64 + } + + #[napi] + pub fn dropped_engine_events(&self) -> f64 { + self.dropped_engine_events.load(Ordering::Relaxed) as f64 + } +} + +#[napi(object)] +#[derive(Default)] +pub struct VoiceEngineV2BridgeConnectOptions { + pub auto_subscribe: Option, + pub adaptive_stream: Option, + pub dynacast: Option, +} + +#[napi(object)] +pub struct ScreenSharePublishOptions { + pub adaptive_send: Option, + pub min_video_fps: Option, + pub max_audio_buffer_ms: Option, + pub pacing: Option, + pub capture_id: Option, + pub track_name: Option, +} + +#[napi(object)] +pub struct MicrophoneOptions { + pub device_id: Option, + pub echo_cancellation: Option, + pub noise_suppression: Option, + pub auto_gain_control: Option, + pub deep_filter: Option, + pub deep_filter_noise_reduction_level: Option, + pub max_bitrate_bps: Option, +} + +#[napi(object)] +pub struct CameraOptions { + pub device_id: Option, + pub width: Option, + pub height: Option, + pub frame_rate: Option, + pub mirror: Option, + pub background_mode: Option, + pub background_custom_media_path: Option, + pub background_custom_media_kind: Option, + pub background_blur_strength: Option, + pub codec: Option, + pub max_bitrate_bps: Option, + pub max_framerate: Option, +} + +#[napi(object)] +pub struct ProcessedCameraOptions { + pub width: u32, + pub height: u32, + pub frame_rate: u32, +} + +#[napi(object)] +pub struct ProcessedCameraPublishResult { + pub track_sid: String, +} + +#[napi(object)] +pub struct CameraPreviewInfo { + pub track_sid: String, + pub width: u32, + pub height: u32, + pub frame_rate: u32, +} + +#[napi(object)] +pub struct ProcessedCameraFrame { + pub format: String, + pub width: u32, + pub height: u32, + pub timestamp_us: i64, + pub data: Buffer, +} + +#[napi(object)] +pub struct CameraDeviceInfo { + pub device_id: String, + pub label: String, + pub description: String, + pub index: Option, + pub device_id_aliases: Vec, +} + +fn snapshot_participants(room: &Room) -> (LocalParticipant, Vec) { + ( + room.local_participant(), + room.remote_participants().into_values().collect(), + ) +} + +fn snapshot_room_participants( + room: &Arc, +) -> (Arc, LocalParticipant, Vec) { + let (local, remotes) = snapshot_participants(room); + (room.clone(), local, remotes) +} + +fn outbound_screenshare_video_fps(stats: &stats_mod::ConnectionStats) -> Option { + stats + .outbound + .iter() + .filter(|entry| entry.kind == "video" && entry.source == LIVEKIT_TRACK_SOURCE_SCREEN_SHARE) + .filter_map(|entry| entry.fps) + .filter(|fps| fps.is_finite() && *fps >= 0.0) + .fold(None, |best, fps| { + Some(best.map_or(fps, |value: f64| value.max(fps))) + }) +} + +fn annotate_screen_share_stats( + stats: &mut stats_mod::ConnectionStats, + metadata: Option, + send: &SendHealthSnapshot, +) { + let Some(metadata) = metadata else { + return; + }; + for entry in stats + .outbound + .iter_mut() + .filter(|entry| entry.kind == "video" && entry.source == LIVEKIT_TRACK_SOURCE_SCREEN_SHARE) + { + if entry.track_sid != metadata.track_sid.to_string() { + continue; + } + if entry.codec.is_none() && !metadata.codec.is_empty() { + entry.codec = Some(metadata.codec.clone()); + } + entry.width = Some(metadata.width); + entry.height = Some(metadata.height); + entry.source_width = Some(metadata.width); + entry.source_height = Some(metadata.height); + entry.target_bitrate_kbps = metadata.target_bitrate_kbps; + entry.configured_fps = Some(metadata.configured_fps); + entry.target_fps = Some(send.outgoing_video_target_fps); + entry.effective_fps = Some(send.outgoing_video_effective_fps); + entry.frames_produced = Some(send.outgoing_video_frames_produced); + entry.frames_accepted = Some(send.outgoing_video_frames_accepted); + entry.frames_dropped = Some(send.outgoing_video_frames_dropped); + entry.frames_coalesced = Some(send.outgoing_video_frames_coalesced); + entry.frames_captured = Some(send.outgoing_video_frames_captured); + entry.capture_failures = Some(send.outgoing_video_capture_failures); + entry.max_queue_age_ms = Some(send.outgoing_video_max_queue_age_ms); + entry.max_push_latency_ms = Some(send.outgoing_video_max_push_latency_ms); + entry.adaptive_send_tier = Some(send.adaptive_send_tier.clone()); + entry.adaptive_send_reason = Some(send.adaptive_send_reason.clone()); + } +} + +async fn collect_stats_for( + local: LocalParticipant, + remotes: Vec, + byte_samples: &Arc>>, +) -> stats_mod::ConnectionStats { + let mut out = stats_mod::ConnectionStats::default(); + + for (sid, publication) in local.track_publications() { + let Some(track) = publication.track() else { + continue; + }; + let reports = match track.get_stats().await { + Ok(r) => r, + Err(_) => continue, + }; + let codec_map = codec_mime_map(&reports); + let source = events::track_source_str(publication.source()).to_string(); + let kind = events::track_kind_str(publication.kind()).to_string(); + let track_sid = sid.to_string(); + let audio_level_by_source_id = audio_level_by_source_id(&reports); + absorb_rtt(&reports, &mut out); + for report in &reports { + if let RtcStats::OutboundRtp(o) = report { + let bitrate = + sample_bitrate(byte_samples, &o.rtc.id, o.sent.bytes_sent, o.rtc.timestamp); + let fps = if kind == "video" { + Some(o.outbound.frames_per_second) + } else { + None + }; + out.outbound.push(stats_mod::OutboundEntry { + track_sid: track_sid.clone(), + source: source.clone(), + kind: kind.clone(), + codec: codec_mime_for(&codec_map, &o.stream.codec_id), + bitrate_kbps: bitrate, + packets_lost: 0, + packets_sent: o.sent.packets_sent, + fps, + audio_level: if kind == "audio" { + audio_level_by_source_id + .get(&o.outbound.media_source_id) + .copied() + .flatten() + } else { + None + }, + ..Default::default() + }); + } else if let RtcStats::RemoteInboundRtp(ri) = report + && let Some(entry) = out.outbound.iter_mut().find(|e| e.track_sid == track_sid) + { + entry.packets_lost = ri.received.packets_lost; + } + } + } + + for participant in remotes { + let participant_sid = participant.sid().to_string(); + for (sid, publication) in participant.track_publications() { + let Some(track) = publication.track() else { + continue; + }; + let reports = match track.get_stats().await { + Ok(r) => r, + Err(_) => continue, + }; + let codec_map = codec_mime_map(&reports); + let kind = events::track_kind_str(publication.kind()).to_string(); + let source = events::track_source_str(publication.source()).to_string(); + let participant_identity = participant.identity().to_string(); + let track_sid = sid.to_string(); + absorb_rtt(&reports, &mut out); + for report in &reports { + if let RtcStats::InboundRtp(i) = report { + let bitrate = sample_bitrate( + byte_samples, + &i.rtc.id, + i.inbound.bytes_received, + i.rtc.timestamp, + ); + let jitter_ms = stats_mod::jitter_seconds_to_ms(i.received.jitter); + let audio_level = if kind == "audio" { + stats_mod::sanitize_audio_level(i.inbound.audio_level) + } else { + None + }; + out.inbound.push(stats_mod::InboundEntry { + participant_sid: participant_sid.clone(), + participant_identity: Some(participant_identity.clone()), + track_sid: track_sid.clone(), + source: Some(source.clone()), + kind: kind.clone(), + codec: codec_mime_for(&codec_map, &i.stream.codec_id), + bitrate_kbps: bitrate, + packets_lost: i.received.packets_lost, + packets_received: i.received.packets_received, + jitter_ms, + audio_level, + fps: optional_non_negative_f64(i.inbound.frames_per_second), + width: optional_positive_u32(i.inbound.frame_width), + height: optional_positive_u32(i.inbound.frame_height), + source_width: optional_positive_u32(i.inbound.frame_width), + source_height: optional_positive_u32(i.inbound.frame_height), + }); + } + } + } + } + + out +} + +fn optional_positive_u32(value: u32) -> Option { + if value > 0 { Some(value) } else { None } +} + +fn optional_non_negative_f64(value: f64) -> Option { + if value.is_finite() && value >= 0.0 { + Some(value) + } else { + None + } +} + +fn audio_level_by_source_id(reports: &[RtcStats]) -> HashMap> { + reports + .iter() + .filter_map(|report| { + if let RtcStats::MediaSource(source) = report + && source.source.kind == "audio" + && !source.rtc.id.is_empty() + { + return Some(( + source.rtc.id.clone(), + stats_mod::sanitize_audio_level(source.audio.audio_level), + )); + } + None + }) + .collect() +} + +async fn absorb_room_rtt(room: &Room, out: &mut stats_mod::ConnectionStats) { + if out.rtt_ms.is_some() { + return; + } + + let Ok(stats) = room.get_stats().await else { + return; + }; + absorb_session_rtt(&stats.publisher_stats, &stats.subscriber_stats, out); +} + +fn absorb_session_rtt( + publisher_reports: &[RtcStats], + subscriber_reports: &[RtcStats], + out: &mut stats_mod::ConnectionStats, +) { + absorb_rtt(publisher_reports, out); + absorb_rtt(subscriber_reports, out); +} + +fn codec_mime_map(reports: &[RtcStats]) -> HashMap { + reports + .iter() + .filter_map(|report| match report { + RtcStats::Codec(codec) + if !codec.rtc.id.is_empty() && !codec.codec.mime_type.is_empty() => + { + Some((codec.rtc.id.clone(), codec.codec.mime_type.clone())) + } + _ => None, + }) + .collect() +} + +fn codec_mime_for(codec_map: &HashMap, codec_id: &str) -> Option { + if codec_id.is_empty() { + return None; + } + codec_map.get(codec_id).cloned() +} + +fn absorb_rtt(reports: &[RtcStats], out: &mut stats_mod::ConnectionStats) { + if out.rtt_ms.is_some() { + return; + } + for report in reports { + if let RtcStats::CandidatePair(cp) = report + && cp.candidate_pair.nominated + && let Some(ms) = + stats_mod::rtt_seconds_to_ms(cp.candidate_pair.current_round_trip_time) + { + out.rtt_ms = Some(ms); + return; + } + } +} + +fn sample_bitrate( + byte_samples: &Arc>>, + stat_id: &str, + bytes: u64, + timestamp_us: i64, +) -> f64 { + let cur = stats_mod::ByteRateSample { + bytes, + timestamp_us, + }; + let mut guard = byte_samples.lock(); + let prev = guard.get(stat_id).copied(); + let kbps = stats_mod::bitrate_kbps(prev, cur); + guard.insert(stat_id.to_string(), cur); + kbps +} + +fn copy_plane(dst: &mut [u8], src: &[u8], width: usize, dst_stride: usize, rows: usize) { + assert!(width >= 1); + assert!(rows >= 1); + assert!(dst_stride >= width); + assert!(src.len() >= width * rows); + assert!(dst.len() >= dst_stride * (rows - 1) + width); + if dst_stride == width { + dst[..width * rows].copy_from_slice(&src[..width * rows]); + return; + } + for row in 0..rows { + let s = row * width; + let d = row * dst_stride; + dst[d..d + width].copy_from_slice(&src[s..s + width]); + } +} + +fn tight_i420_len(width: u32, height: u32) -> usize { + let w = width as usize; + let h = height as usize; + let cw = w / 2; + let ch = h / 2; + w * h + 2 * cw * ch +} + +fn video_frame_meta_json( + participant_sid: &str, + participant_identity: &str, + track_sid: &str, + track_name: &str, + track_source: &str, + width: u32, + height: u32, + timestamp_us: i64, +) -> String { + let prefix = video_frame_meta_prefix( + participant_sid, + participant_identity, + track_sid, + track_name, + track_source, + ); + video_frame_meta_json_with_prefix(&prefix, width, height, timestamp_us) +} + +fn video_frame_meta_prefix( + participant_sid: &str, + participant_identity: &str, + track_sid: &str, + track_name: &str, + track_source: &str, +) -> String { + use events::JsonValue::{Raw, Str}; + events::json_object(&[ + ( + "bridgeVersion", + Raw(crate::bridge_version::ENGINE_BRIDGE_VERSION.to_string()), + ), + ("participantSid", Str(participant_sid.to_string())), + ("participantIdentity", Str(participant_identity.to_string())), + ("trackSid", Str(track_sid.to_string())), + ("trackName", Str(track_name.to_string())), + ("source", Str(track_source.to_string())), + ]) +} + +fn video_frame_meta_json_with_prefix( + prefix: &str, + width: u32, + height: u32, + timestamp_us: i64, +) -> String { + use std::fmt::Write as _; + assert!(prefix.starts_with('{')); + assert!(prefix.ends_with('}')); + let mut meta = String::with_capacity(prefix.len() + 64); + meta.push_str(&prefix[..prefix.len() - 1]); + let _ = write!( + meta, + ",\"width\":{width},\"height\":{height},\"timestampUs\":{timestamp_us}}}" + ); + meta +} + +fn i420_buffer_to_tight(buffer: &I420Buffer) -> Option<(Vec, u32, u32)> { + let width = buffer.width(); + let height = buffer.height(); + if width < 2 || height < 2 || !width.is_multiple_of(2) || !height.is_multiple_of(2) { + return None; + } + let w = width as usize; + let h = height as usize; + let cw = w / 2; + let ch = h / 2; + let (stride_y, stride_u, stride_v) = buffer.strides(); + let (src_y, src_u, src_v) = buffer.data(); + + let mut out = Vec::with_capacity(tight_i420_len(width, height)); + if !extend_tight(&mut out, src_y, w, stride_y as usize, h) { + return None; + } + if !extend_tight(&mut out, src_u, cw, stride_u as usize, ch) { + return None; + } + if !extend_tight(&mut out, src_v, cw, stride_v as usize, ch) { + return None; + } + assert_eq!(out.len(), tight_i420_len(width, height)); + Some((out, width, height)) +} + +fn extend_tight( + out: &mut Vec, + src: &[u8], + width: usize, + src_stride: usize, + rows: usize, +) -> bool { + assert!(width >= 1); + assert!(rows >= 1); + if src_stride < width { + return false; + } + if src_stride == width { + if src.len() < width * rows { + return false; + } + out.extend_from_slice(&src[..width * rows]); + return true; + } + if src.len() < src_stride * (rows - 1) + width { + return false; + } + for row in 0..rows { + let s = row * src_stride; + out.extend_from_slice(&src[s..s + width]); + } + true +} + +fn frame_to_callback_payload(frame: &BoxVideoFrame, meta_prefix: &str) -> Option<(String, Buffer)> { + let (data, width, height) = if let Some(i420) = frame.buffer.as_i420() { + i420_buffer_to_tight(i420)? + } else { + let i420 = frame.buffer.to_i420(); + i420_buffer_to_tight(&i420)? + }; + let meta = video_frame_meta_json_with_prefix(meta_prefix, width, height, frame.timestamp_us); + Some((meta, Buffer::from(data))) +} + +fn record_first_error(slot: &mut Option, result: napi::Result<()>) { + if let Err(error) = result + && slot.is_none() + { + *slot = Some(error); + } +} + +fn emit_engine_event( + events_slot: &Mutex>, + dropped_engine_events: &AtomicU64, + event_type: String, + payload: String, +) { + let guard = events_slot.lock(); + let Some(tsfn) = guard.as_ref() else { + return; + }; + let status = tsfn.call( + (event_type, payload), + ThreadsafeFunctionCallMode::NonBlocking, + ); + if status == Status::QueueFull { + dropped_engine_events.fetch_add(1, Ordering::Relaxed); + } +} + +const SPEAKING_TAP_SAMPLE_RATE_HZ: u32 = 48_000; +const SPEAKING_TAP_NUM_CHANNELS: u32 = 1; +const SPEAKING_TAP_FRAME_DURATION_MS_MAX: usize = 100; +const SPEAKING_TAP_FRAME_SAMPLES_MAX: usize = (SPEAKING_TAP_SAMPLE_RATE_HZ as usize) + * (MAX_AUDIO_CHANNELS as usize) + * SPEAKING_TAP_FRAME_DURATION_MS_MAX + / 1_000; + +struct SpeakingTap { + participant_sid: String, + identity: String, + track_sid: String, + source: &'static str, + is_local: bool, + release_ms: u64, + thresholds: Arc, + events: Arc>>, + dropped_engine_events: Arc, + stop: Arc, + inbound_audio: Option>, +} + +impl SpeakingTap { + fn spawn(self, rtc_track: RtcAudioTrack) { + assert!(!self.participant_sid.is_empty()); + assert!(!self.track_sid.is_empty()); + assert_eq!(self.source, "microphone"); + tokio::spawn(self.run(rtc_track)); + } + + async fn run(self, rtc_track: RtcAudioTrack) { + let mut stream = NativeAudioStream::new( + rtc_track, + SPEAKING_TAP_SAMPLE_RATE_HZ as i32, + SPEAKING_TAP_NUM_CHANNELS as i32, + ); + let started = Instant::now(); + let mut gate = SpeakingGate::new(speaking::SPEAKING_ATTACK_MS, self.release_ms); + let mut last_emit_ms: u64 = 0; + loop { + if self.stop.load(Ordering::Acquire) { + break; + } + let next_frame = tokio::time::timeout( + Duration::from_millis(SPEAKING_FRAME_TIMEOUT_MS), + stream.next(), + ) + .await; + let now_ms = started.elapsed().as_millis() as u64; + let rms = match &next_frame { + Ok(Some(frame)) => Self::frame_rms(frame), + Ok(None) => break, + Err(_) => 0.0, + }; + if let Ok(Some(_)) = &next_frame { + if let Some(counter) = &self.inbound_audio { + counter.fetch_add(1, Ordering::Relaxed); + } + } + let threshold_rms = if self.is_local { + self.thresholds.local_rms() + } else { + self.thresholds.remote_rms() + }; + if let Some(speaking) = gate.update(rms, threshold_rms, now_ms) { + self.emit_speaking(speaking); + last_emit_ms = now_ms; + } else if gate.speaking() { + if now_ms - last_emit_ms >= SPEAKING_HEARTBEAT_INTERVAL_MS { + self.emit_speaking(true); + last_emit_ms = now_ms; + } + } + } + if gate.speaking() { + self.emit_speaking(false); + } + } + + fn frame_rms(frame: &AudioFrame<'_>) -> f64 { + if frame.data.is_empty() { + return 0.0; + } + if frame.num_channels == 0 { + return 0.0; + } + if frame.num_channels > MAX_AUDIO_CHANNELS { + return 0.0; + } + if frame.data.len() > SPEAKING_TAP_FRAME_SAMPLES_MAX { + return 0.0; + } + if frame.data.len() % frame.num_channels as usize != 0 { + return 0.0; + } + speaking::frame_rms_i16(&frame.data) + } + + fn emit_speaking(&self, speaking: bool) { + let payload = events::json_object(&[ + ( + "participantSid", + events::JsonValue::Str(self.participant_sid.clone()), + ), + ("identity", events::JsonValue::Str(self.identity.clone())), + ("trackSid", events::JsonValue::Str(self.track_sid.clone())), + ("source", events::JsonValue::Str(self.source.to_string())), + ("isLocal", events::JsonValue::Raw(self.is_local.to_string())), + ("speaking", events::JsonValue::Raw(speaking.to_string())), + ]); + emit_engine_event( + &self.events, + &self.dropped_engine_events, + "speakingChanged".to_string(), + payload, + ); + } +} + +fn local_video_frame_sink_active( + video_frames: Arc>>, +) -> camera::LocalVideoFrameSinkActive { + Box::new(move || video_frames.lock().is_some()) +} + +fn local_video_frame_sink( + video_frames: Arc>>, + dropped_video_frame_callbacks: Arc, + participant_sid: String, + participant_identity: String, + track_sid: Arc>, + track_name: String, + track_source: String, +) -> camera::LocalVideoFrameSink { + assert!(!participant_sid.is_empty()); + if !participant_identity.is_empty() { + assert!(participant_identity.starts_with("user_")); + } + assert!(!track_sid.lock().is_empty()); + let last_timestamp_us = AtomicI64::new(i64::MIN); + Box::new(move |frame, timestamp_us| { + let previous_timestamp_us = last_timestamp_us.swap(timestamp_us, Ordering::Relaxed); + assert!(timestamp_us >= previous_timestamp_us); + if video_frames.lock().is_none() { + return; + } + let (meta, buffer) = local_video_frame_sink_payload( + frame, + timestamp_us, + &participant_sid, + &participant_identity, + &track_sid, + &track_name, + &track_source, + ); + if let Some(tsfn) = video_frames.lock().as_ref() { + let status = tsfn.call((meta, buffer), ThreadsafeFunctionCallMode::NonBlocking); + if status == Status::QueueFull { + dropped_video_frame_callbacks.fetch_add(1, Ordering::Relaxed); + } + } + }) +} + +fn local_video_frame_sink_payload( + frame: &yuv::I420, + timestamp_us: i64, + participant_sid: &str, + participant_identity: &str, + track_sid: &Mutex, + track_name: &str, + track_source: &str, +) -> (String, Buffer) { + let current_track_sid = track_sid.lock().clone(); + assert!(!current_track_sid.is_empty()); + local_i420_to_callback_payload( + frame, + timestamp_us, + participant_sid, + participant_identity, + ¤t_track_sid, + track_name, + track_source, + ) +} + +fn local_i420_to_callback_payload( + frame: &yuv::I420, + timestamp_us: i64, + participant_sid: &str, + participant_identity: &str, + track_sid: &str, + track_name: &str, + track_source: &str, +) -> (String, Buffer) { + assert!(frame.width >= 2); + assert!(frame.height >= 2); + assert!(frame.width.is_multiple_of(2)); + assert!(frame.height.is_multiple_of(2)); + let w = frame.width as usize; + let h = frame.height as usize; + let chroma_len = (w / 2) * (h / 2); + assert_eq!(frame.y.len(), w * h); + assert_eq!(frame.u.len(), chroma_len); + assert_eq!(frame.v.len(), chroma_len); + let tight_len = tight_i420_len(frame.width, frame.height); + let mut data = Vec::with_capacity(tight_len); + data.extend_from_slice(&frame.y); + data.extend_from_slice(&frame.u); + data.extend_from_slice(&frame.v); + assert_eq!(data.len(), tight_len); + let meta = video_frame_meta_json( + participant_sid, + participant_identity, + track_sid, + track_name, + track_source, + frame.width, + frame.height, + timestamp_us, + ); + (meta, Buffer::from(data)) +} + +fn validate_processed_camera_frame(frame: &ProcessedCameraFrame) -> napi::Result<()> { + if frame.format != "i420" { + return Err(napi::Error::from_reason( + "processed camera frame format must be i420", + )); + } + if !valid_even_video_dims(frame.width, frame.height) { + return Err(napi::Error::from_reason( + "processed camera frame dimensions are invalid", + )); + } + if frame.timestamp_us <= 0 { + return Err(napi::Error::from_reason( + "processed camera frame timestamp must be positive", + )); + } + let expected = tight_i420_len(frame.width, frame.height); + if frame.data.len() != expected { + return Err(napi::Error::from_reason( + "processed camera frame byte length does not match tight i420", + )); + } + Ok(()) +} + +fn processed_camera_frame_to_pending( + frame: ProcessedCameraFrame, +) -> napi::Result { + validate_processed_camera_frame(&frame)?; + let buffer = tight_i420_to_native_buffer(frame.data.as_ref(), frame.width, frame.height); + assert_eq!(buffer.width(), frame.width); + assert_eq!(buffer.height(), frame.height); + Ok(PendingVideoFrame::I420Native { + buffer, + timestamp_us: frame.timestamp_us, + enqueued_at: Instant::now(), + }) +} + +fn tight_i420_to_native_buffer(data: &[u8], width: u32, height: u32) -> I420Buffer { + assert!(valid_even_video_dims(width, height)); + assert_eq!(data.len(), tight_i420_len(width, height)); + let y_len = (width as usize) * (height as usize); + let chroma_len = y_len / 4; + let mut buffer = I420Buffer::new(width, height); + let (stride_y, stride_u, stride_v) = buffer.strides(); + { + let (dy, du, dv) = buffer.data_mut(); + copy_plane( + dy, + &data[..y_len], + width as usize, + stride_y as usize, + height as usize, + ); + let chroma_width = (width / 2) as usize; + let chroma_height = (height / 2) as usize; + copy_plane( + du, + &data[y_len..y_len + chroma_len], + chroma_width, + stride_u as usize, + chroma_height, + ); + copy_plane( + dv, + &data[y_len + chroma_len..], + chroma_width, + stride_v as usize, + chroma_height, + ); + } + buffer +} + +fn publish_pending_video_frame(source: &NativeVideoSource, frame: PendingVideoFrame) -> bool { + match frame { + PendingVideoFrame::I420Native { + buffer, + timestamp_us, + .. + } => capture_buffer_to_source(source, buffer, timestamp_us), + PendingVideoFrame::Bgra { + data, + width, + height, + stride, + timestamp_us, + .. + } => capture_bgra_to_source(source, &data, width, height, stride, timestamp_us), + PendingVideoFrame::Nv12 { + data, + width, + height, + stride_y, + stride_uv, + timestamp_us, + .. + } => capture_nv12_to_source( + source, + &data, + width, + height, + stride_y, + stride_uv, + timestamp_us, + ), + #[cfg(target_os = "windows")] + PendingVideoFrame::Texture { + desc, capability, .. + } => texture_source::bridge::try_publish_texture(source, &capability, &desc).is_ok(), + #[cfg(target_os = "macos")] + PendingVideoFrame::MacCvPixelBuffer { + buffer, + timestamp_us, + .. + } => capture_buffer_to_source(source, buffer, timestamp_us), + #[cfg(target_os = "linux")] + PendingVideoFrame::Dmabuf { + desc, + capability, + fds, + .. + } => { + let mut raw_fds = [-1; 4]; + for (index, fd) in fds.iter().enumerate().take(desc.plane_count as usize) { + raw_fds[index] = fd.as_raw_fd(); + } + texture_source::bridge::try_publish_dmabuf(source, &capability, &desc, raw_fds).is_ok() + } + } +} + +fn capture_buffer_to_source>( + source: &NativeVideoSource, + buffer: T, + timestamp_us: i64, +) -> bool { + source.capture_frame(&VideoFrame { + rotation: VideoRotation::VideoRotation0, + timestamp_us, + frame_metadata: None, + buffer, + }); + true +} + +fn capture_bgra_to_source( + source: &NativeVideoSource, + data: &[u8], + width: u32, + height: u32, + stride: u32, + timestamp_us: i64, +) -> bool { + let mut buffer = I420Buffer::new(width, height); + let (stride_y, stride_u, stride_v) = buffer.strides(); + let (dst_y, dst_u, dst_v) = buffer.data_mut(); + if !yuv::bgra_to_i420_planes( + data, width, height, stride, dst_y, dst_u, dst_v, stride_y, stride_u, stride_v, + ) { + return false; + } + source.capture_frame(&VideoFrame { + rotation: VideoRotation::VideoRotation0, + timestamp_us, + frame_metadata: None, + buffer, + }); + true +} + +fn capture_nv12_to_source( + source: &NativeVideoSource, + data: &[u8], + width: u32, + height: u32, + stride_y: u32, + stride_uv: u32, + timestamp_us: i64, +) -> bool { + let mut buffer = NV12Buffer::with_strides(width, height, width, width); + let (dst_stride_y, dst_stride_uv) = buffer.strides(); + let (dst_y, dst_uv) = buffer.data_mut(); + if !yuv::copy_nv12_planes( + data, + width, + height, + stride_y, + stride_uv, + dst_y, + dst_uv, + dst_stride_y, + dst_stride_uv, + ) { + return false; + } + source.capture_frame(&VideoFrame { + rotation: VideoRotation::VideoRotation0, + timestamp_us, + frame_metadata: None, + buffer, + }); + true +} + +fn valid_even_video_dims(width: u32, height: u32) -> bool { + width >= 2 + && height >= 2 + && width.is_multiple_of(2) + && height.is_multiple_of(2) + && width <= 8192 + && height <= 8192 +} + +fn now_millis() -> u64 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap_or_default() + .as_millis() as u64 +} + +fn elapsed_ms(start: Instant, end: Instant) -> u64 { + end.saturating_duration_since(start).as_millis() as u64 +} + +fn texture_capability_for_screen_codec(codec: &str) -> TextureCapability { + if !texture_source::codec_allows_native_gpu(codec) { + return TextureCapability::for_screen_codec(codec, false); + } + TextureCapability::for_screen_codec( + codec, + crate::hardware_encoder::hardware_encoder_capability().available, + ) +} + +fn build_microphone_publish_options(opts: &MicrophoneOptions) -> napi::Result { + let audio_encoding = audio::normalize_microphone_max_bitrate_bps(opts.max_bitrate_bps) + .map_err(napi::Error::from_reason)? + .map(|max_bitrate| AudioEncoding { max_bitrate }); + Ok(TrackPublishOptions { + source: TrackSource::Microphone, + red: true, + dtx: true, + audio_encoding, + ..Default::default() + }) +} + +fn build_camera_publish_options( + opts: &CameraOptions, + publication_kind: CameraPublicationKind, +) -> napi::Result { + let mut options = TrackPublishOptions { + source: publication_kind.track_source(), + simulcast: publication_kind.is_screencast(), + ..Default::default() + }; + if let Some(stream) = publication_kind.stream() { + options.stream = stream.to_string(); + } + if let Some(codec) = opts + .codec + .as_deref() + .map(str::trim) + .filter(|c| !c.is_empty()) + { + let canonical_codec = crate::config::canonical_codec_name(codec) + .ok_or_else(|| napi::Error::from_reason("unsupported video codec"))?; + crate::hardware_encoder::require_publish_codec_runtime_support(canonical_codec) + .map_err(napi::Error::from_reason)?; + options.video_codec = parse_codec(codec) + .ok_or_else(|| napi::Error::from_reason("unsupported video codec"))?; + } + if let Some(bitrate) = opts.max_bitrate_bps.filter(|b| b.is_finite() && *b > 0.0) { + options.video_encoding = Some(VideoEncoding { + max_bitrate: bitrate as u64, + max_framerate: opts + .max_framerate + .filter(|fps| fps.is_finite() && *fps > 0.0) + .or_else(|| opts.frame_rate.map(f64::from).filter(|fps| *fps > 0.0)) + .unwrap_or(30.0), + }); + } + Ok(options) +} + +fn native_camera_background_config( + opts: &CameraOptions, +) -> napi::Result { + crate::camera_background::CameraBackgroundConfig::from_bridge_values( + opts.background_mode.as_deref(), + opts.background_custom_media_path.as_deref(), + opts.background_custom_media_kind.as_deref(), + opts.background_blur_strength, + ) + .map_err(napi::Error::from_reason) +} + +fn parse_codec(name: &str) -> Option { + match crate::config::canonical_codec_name(name)? { + "vp8" => Some(VideoCodec::VP8), + "vp9" => Some(VideoCodec::VP9), + "h264" => Some(VideoCodec::H264), + "av1" => Some(VideoCodec::AV1), + "h265" => Some(VideoCodec::H265), + other => unreachable!("unhandled canonical codec {other}"), + } +} + +fn pcm16_audio_frame_into<'a>( + bytes: &[u8], + sample_rate: u32, + num_channels: u32, + samples: &'a mut Vec, +) -> Option> { + if !valid_audio_format(sample_rate, num_channels) { + return None; + } + let sample_count = bytes.len() / 2; + if sample_count == 0 || bytes.len() % 2 != 0 || sample_count % (num_channels as usize) != 0 { + return None; + } + let samples_per_channel = sample_count / num_channels as usize; + let max_samples_per_channel = + (sample_rate as u64).saturating_mul(MAX_PCM_FRAME_DURATION_MS) / 1_000; + if samples_per_channel as u64 > max_samples_per_channel { + return None; + } + let samples_per_channel = u32::try_from(samples_per_channel).ok()?; + assert!(sample_count <= MAX_PCM_FRAME_SAMPLES); + samples.clear(); + samples.reserve(sample_count); + for chunk in bytes.chunks_exact(2) { + samples.push(i16::from_le_bytes([chunk[0], chunk[1]])); + } + assert_eq!(samples.len(), sample_count); + Some(AudioFrame { + data: std::borrow::Cow::Borrowed(samples.as_slice()), + sample_rate, + num_channels, + samples_per_channel, + }) +} + +fn f32_sample_to_i16(sample: f32) -> i16 { + if !sample.is_finite() { + return 0; + } + let clamped = sample.clamp(-1.0, 1.0); + if clamped < 0.0 { + (clamped * 32768.0) as i16 + } else { + (clamped * 32767.0) as i16 + } +} + +fn f32_audio_frame_into<'a>( + bytes: &[u8], + sample_rate: u32, + num_channels: u32, + samples: &'a mut Vec, +) -> Option> { + if !valid_audio_format(sample_rate, num_channels) { + return None; + } + let sample_count = bytes.len() / 4; + if sample_count == 0 || bytes.len() % 4 != 0 || sample_count % (num_channels as usize) != 0 { + return None; + } + let samples_per_channel = sample_count / num_channels as usize; + let max_samples_per_channel = + (sample_rate as u64).saturating_mul(MAX_PCM_FRAME_DURATION_MS) / 1_000; + if samples_per_channel as u64 > max_samples_per_channel { + return None; + } + let samples_per_channel = u32::try_from(samples_per_channel).ok()?; + assert!(sample_count <= MAX_PCM_FRAME_SAMPLES); + samples.clear(); + samples.reserve(sample_count); + for chunk in bytes.chunks_exact(4) { + samples.push(f32_sample_to_i16(f32::from_le_bytes([ + chunk[0], chunk[1], chunk[2], chunk[3], + ]))); + } + assert_eq!(samples.len(), sample_count); + Some(AudioFrame { + data: std::borrow::Cow::Borrowed(samples.as_slice()), + sample_rate, + num_channels, + samples_per_channel, + }) +} + +fn valid_audio_format(sample_rate: u32, num_channels: u32) -> bool { + (MIN_AUDIO_SAMPLE_RATE_HZ..=MAX_AUDIO_SAMPLE_RATE_HZ).contains(&sample_rate) + && (1..=MAX_AUDIO_CHANNELS).contains(&num_channels) +} + +fn remote_subscription_target_error( + missing_target: &str, + participant_identity: &str, + source: &str, +) -> napi::Error { + assert!(!missing_target.is_empty()); + assert!(!participant_identity.is_empty()); + assert!(!source.is_empty()); + napi::Error::from_reason(format!( + "remote track subscription {missing_target} not found \ + for participant {participant_identity} source {source}" + )) +} + +fn parse_track_source(source: &str) -> Option { + match source.trim() { + LIVEKIT_TRACK_SOURCE_CAMERA => Some(TrackSource::Camera), + "microphone" => Some(TrackSource::Microphone), + LIVEKIT_TRACK_SOURCE_SCREEN_SHARE | "screenshare" => Some(TrackSource::Screenshare), + LIVEKIT_TRACK_SOURCE_SCREEN_SHARE_AUDIO | "screenshareAudio" => { + Some(TrackSource::ScreenshareAudio) + } + "unknown" => Some(TrackSource::Unknown), + _ => None, + } +} + +fn is_optional_remote_subscription_target(source: TrackSource) -> bool { + source == TrackSource::ScreenshareAudio +} + +fn parse_video_quality(quality: &str) -> Option { + match quality.trim() { + "low" => Some(VideoQuality::Low), + "medium" => Some(VideoQuality::Medium), + "high" => Some(VideoQuality::High), + _ => None, + } +} + +#[cfg(test)] +mod tests { + use super::*; + use livekit::webrtc::stats::{CandidatePairStats, dictionaries}; + + fn sender_for_tests() -> (AdaptiveVideoSender, Arc) { + let stats = Arc::new(AdaptiveVideoStats::new(60.0, 15.0, true, 0)); + let sender = AdaptiveVideoSender { + pending: Arc::new(ArrayQueue::new(ENCODER_QUEUE_CAPACITY)), + notify: Arc::new(Notify::new()), + stop: Arc::new(AtomicBool::new(false)), + stats: stats.clone(), + pacing: VideoPacingMode::Sender, + target_fps: 60.0, + }; + (sender, stats) + } + + fn tiny_bgra_frame() -> PendingVideoFrame { + tiny_bgra_frame_enqueued_at(Instant::now()) + } + + fn tiny_bgra_frame_enqueued_at(enqueued_at: Instant) -> PendingVideoFrame { + PendingVideoFrame::Bgra { + data: vec![0; 16], + width: 2, + height: 2, + stride: 8, + timestamp_us: 1, + enqueued_at, + } + } + + fn audio_frame_for_tests(samples: &[i16], num_channels: u32) -> AudioFrame<'_> { + AudioFrame { + data: std::borrow::Cow::Borrowed(samples), + sample_rate: SPEAKING_TAP_SAMPLE_RATE_HZ, + num_channels, + samples_per_channel: if num_channels == 0 { + 0 + } else { + (samples.len() / num_channels as usize) as u32 + }, + } + } + + fn valid_connection_state_transitions() -> [(u8, u8); 8] { + [ + (S_IDLE, S_CLOSED), + (S_CLOSED, S_CONNECTING), + (S_CLOSED, S_CLOSED), + (S_CONNECTING, S_CONNECTED), + (S_CONNECTING, S_FAILED), + (S_CONNECTING, S_CLOSED), + (S_CONNECTED, S_CLOSED), + (S_FAILED, S_CLOSED), + ] + } + + #[test] + fn extend_tight_packs_strided_planes_and_rejects_short_sources() { + let mut out = Vec::with_capacity(8); + let src = [1u8, 2, 9, 9, 3, 4, 9, 9]; + assert!(extend_tight(&mut out, &src, 2, 4, 2)); + assert_eq!(out, vec![1, 2, 3, 4]); + + out.clear(); + assert!(extend_tight(&mut out, &src[..6], 2, 4, 2)); + assert_eq!(out, vec![1, 2, 3, 4]); + + out.clear(); + assert!(!extend_tight(&mut out, &src[..5], 2, 4, 2)); + + out.clear(); + assert!(extend_tight(&mut out, &[5u8, 6, 7, 8], 4, 4, 1)); + assert_eq!(out, vec![5, 6, 7, 8]); + + out.clear(); + assert!(!extend_tight(&mut out, &[5u8, 6, 7], 4, 4, 1)); + + out.clear(); + assert!(!extend_tight(&mut out, &src, 4, 2, 1)); + } + + #[test] + fn connect_admission_proceeds_for_latest_intent() { + assert_eq!(admit_connect_attempt(1, 1), ConnectAdmission::Proceed); + assert_eq!(admit_connect_attempt(7, 7), ConnectAdmission::Proceed); + } + + #[test] + fn connect_admission_supersedes_stale_intent() { + assert_eq!(admit_connect_attempt(1, 2), ConnectAdmission::Superseded); + assert_eq!(admit_connect_attempt(3, 9), ConnectAdmission::Superseded); + } + + #[test] + #[should_panic] + fn connect_admission_rejects_intent_newer_than_latest() { + let _ = admit_connect_attempt(2, 1); + } + + #[tokio::test] + async fn connect_cancel_waiter_ignores_older_cancel_epoch() { + let (cancel_tx, cancel_rx) = watch::channel(2); + let mut waiter = Box::pin(wait_connect_cancelled(cancel_rx, 4)); + tokio::select! { + biased; + () = waiter.as_mut() => panic!("older cancel epoch must not cancel newer connect"), + _ = tokio::task::yield_now() => {} + } + + cancel_tx.send_replace(4); + waiter.await; + } + + #[tokio::test] + async fn connect_cancel_waiter_finishes_for_current_cancel_epoch() { + let (_cancel_tx, cancel_rx) = watch::channel(9); + + wait_connect_cancelled(cancel_rx, 9).await; + } + + #[test] + fn room_event_loop_forwards_for_current_epoch() { + assert_eq!(room_event_loop_action(1, 1), RoomLoopAction::Forward); + assert_eq!(room_event_loop_action(5, 5), RoomLoopAction::Forward); + } + + #[test] + fn room_event_loop_exits_for_stale_epoch() { + assert_eq!(room_event_loop_action(1, 2), RoomLoopAction::Exit); + assert_eq!(room_event_loop_action(4, 9), RoomLoopAction::Exit); + } + + #[test] + #[should_panic] + fn room_event_loop_rejects_epoch_from_the_future() { + let _ = room_event_loop_action(3, 2); + } + + #[test] + fn connection_state_transitions_accept_positive_space() { + for (from, to) in valid_connection_state_transitions() { + assert!(connection_state_transition_valid(from, to)); + } + } + + #[test] + fn connection_state_transitions_reject_negative_space() { + let valid = valid_connection_state_transitions(); + for from in CONNECTION_STATES { + for to in CONNECTION_STATES { + if valid.contains(&(from, to)) { + continue; + } + assert!(!connection_state_transition_valid(from, to)); + } + } + } + + #[test] + fn store_connection_state_applies_a_full_connect_cycle() { + let state = AtomicU8::new(S_IDLE); + store_connection_state(&state, S_CLOSED); + store_connection_state(&state, S_CONNECTING); + store_connection_state(&state, S_CONNECTED); + store_connection_state(&state, S_CLOSED); + store_connection_state(&state, S_CONNECTING); + store_connection_state(&state, S_FAILED); + store_connection_state(&state, S_CLOSED); + assert_eq!(state.load(Ordering::Acquire), S_CLOSED); + } + + #[test] + #[should_panic] + fn store_connection_state_panics_on_reconnect_without_close() { + let state = AtomicU8::new(S_CONNECTED); + store_connection_state(&state, S_CONNECTING); + } + + #[test] + fn stale_room_loop_end_does_not_clobber_newer_connection_state() { + let state_guard = Mutex::new(()); + let connect_epoch = AtomicU64::new(2); + let state = AtomicU8::new(S_CONNECTED); + + store_room_loop_closed(&state_guard, &connect_epoch, &state, 1); + + assert_eq!(state.load(Ordering::Acquire), S_CONNECTED); + } + + #[test] + fn current_room_loop_end_stores_closed() { + let state_guard = Mutex::new(()); + let connect_epoch = AtomicU64::new(3); + let state = AtomicU8::new(S_CONNECTED); + + store_room_loop_closed(&state_guard, &connect_epoch, &state, 3); + + assert_eq!(state.load(Ordering::Acquire), S_CLOSED); + } + + #[test] + fn video_pacing_mode_defaults_to_source_and_accepts_sender() { + assert_eq!(VideoPacingMode::from_option(None), VideoPacingMode::Source); + assert_eq!( + VideoPacingMode::from_option(Some("source")), + VideoPacingMode::Source + ); + assert_eq!( + VideoPacingMode::from_option(Some("sender")), + VideoPacingMode::Sender + ); + } + + fn microphone_options_with_bitrate(max_bitrate_bps: Option) -> MicrophoneOptions { + MicrophoneOptions { + device_id: None, + echo_cancellation: None, + noise_suppression: None, + auto_gain_control: None, + deep_filter: None, + deep_filter_noise_reduction_level: None, + max_bitrate_bps, + } + } + + #[test] + fn microphone_publish_options_default_to_no_audio_encoding() { + let options = build_microphone_publish_options(µphone_options_with_bitrate(None)) + .expect("microphone publish options"); + + assert_eq!(options.source, TrackSource::Microphone); + assert!(options.red); + assert!(options.dtx); + assert!(options.audio_encoding.is_none()); + } + + #[test] + fn microphone_publish_options_apply_the_clamped_max_bitrate() { + let options = + build_microphone_publish_options(µphone_options_with_bitrate(Some(96_000.0))) + .expect("microphone publish options"); + let encoding = options.audio_encoding.expect("audio encoding"); + assert_eq!(encoding.max_bitrate, 96_000); + + let floored = build_microphone_publish_options(µphone_options_with_bitrate(Some(1.0))) + .expect("microphone publish options") + .audio_encoding + .expect("audio encoding"); + assert_eq!(floored.max_bitrate, audio::MICROPHONE_MAX_BITRATE_BPS_FLOOR); + + let capped = + build_microphone_publish_options(µphone_options_with_bitrate(Some(1_000_000.0))) + .expect("microphone publish options") + .audio_encoding + .expect("audio encoding"); + assert_eq!(capped.max_bitrate, audio::MICROPHONE_MAX_BITRATE_BPS_CAP); + } + + #[test] + fn microphone_publish_options_reject_invalid_max_bitrate() { + assert!( + build_microphone_publish_options(µphone_options_with_bitrate(Some(0.0))).is_err() + ); + assert!( + build_microphone_publish_options(µphone_options_with_bitrate(Some(-64_000.0))) + .is_err() + ); + assert!( + build_microphone_publish_options(µphone_options_with_bitrate(Some(64_000.5))) + .is_err() + ); + assert!( + build_microphone_publish_options(µphone_options_with_bitrate(Some(f64::NAN))) + .is_err() + ); + } + + #[test] + fn camera_publish_options_use_camera_source_without_screen_stream() { + assert_eq!(CameraPublicationKind::Camera.track_name(), "camera"); + + let options = build_camera_publish_options( + &CameraOptions { + device_id: None, + width: None, + height: None, + frame_rate: None, + mirror: None, + background_mode: None, + background_custom_media_path: None, + background_custom_media_kind: None, + background_blur_strength: None, + codec: None, + max_bitrate_bps: None, + max_framerate: None, + }, + CameraPublicationKind::Camera, + ) + .expect("camera publish options"); + + assert_eq!(options.source, TrackSource::Camera); + assert!(options.stream.is_empty()); + assert!(options.video_encoding.is_none()); + } + + #[test] + fn camera_publish_options_can_publish_as_screen_share() { + assert_eq!( + CameraPublicationKind::ScreenShare.track_name(), + "screen_share" + ); + + let options = build_camera_publish_options( + &CameraOptions { + device_id: Some("studio-display-camera".to_string()), + width: Some(1280), + height: Some(720), + frame_rate: Some(24), + mirror: None, + background_mode: None, + background_custom_media_path: None, + background_custom_media_kind: None, + background_blur_strength: None, + codec: None, + max_bitrate_bps: Some(4_000_000.0), + max_framerate: None, + }, + CameraPublicationKind::ScreenShare, + ) + .expect("device screen share publish options"); + + assert_eq!(options.source, TrackSource::Screenshare); + assert_eq!(options.stream, "screen_share"); + let encoding = options.video_encoding.expect("video encoding"); + assert_eq!(encoding.max_bitrate, 4_000_000); + assert_eq!(encoding.max_framerate, 24.0); + } + + fn candidate_pair_report(nominated: bool, rtt_seconds: f64) -> RtcStats { + RtcStats::CandidatePair(CandidatePairStats { + candidate_pair: dictionaries::CandidatePairStats { + nominated, + current_round_trip_time: rtt_seconds, + ..Default::default() + }, + ..Default::default() + }) + } + + #[test] + fn parse_codec_maps_all_five_including_h265() { + assert_eq!(parse_codec("vp8"), Some(VideoCodec::VP8)); + assert_eq!(parse_codec("vp9"), Some(VideoCodec::VP9)); + assert_eq!(parse_codec("h264"), Some(VideoCodec::H264)); + assert_eq!(parse_codec("av1"), Some(VideoCodec::AV1)); + assert_eq!(parse_codec("h265"), Some(VideoCodec::H265)); + assert_eq!(parse_codec("H265"), Some(VideoCodec::H265)); + assert_eq!(parse_codec("hevc"), Some(VideoCodec::H265)); + assert_eq!(parse_codec("HEVC"), Some(VideoCodec::H265)); + assert_eq!(parse_codec("H264"), Some(VideoCodec::H264)); + } + + #[test] + fn parse_codec_rejects_empty_and_unknown() { + assert_eq!(parse_codec(""), None); + assert_eq!(parse_codec("rubbish"), None); + } + + #[test] + fn parse_track_source_accepts_renderer_and_livekit_spellings() { + assert_eq!(parse_track_source("camera"), Some(TrackSource::Camera)); + assert_eq!( + parse_track_source("screen_share"), + Some(TrackSource::Screenshare) + ); + assert_eq!( + parse_track_source("screenshare"), + Some(TrackSource::Screenshare) + ); + assert_eq!( + parse_track_source("screen_share_audio"), + Some(TrackSource::ScreenshareAudio) + ); + assert_eq!( + parse_track_source("screenshareAudio"), + Some(TrackSource::ScreenshareAudio) + ); + assert_eq!(parse_track_source("rubbish"), None); + } + + #[test] + fn optional_remote_subscription_target_is_limited_to_screen_share_audio() { + assert!(is_optional_remote_subscription_target( + TrackSource::ScreenshareAudio + )); + assert!(!is_optional_remote_subscription_target( + TrackSource::Screenshare + )); + assert!(!is_optional_remote_subscription_target(TrackSource::Camera)); + } + + fn pcm16_frame_accepted(bytes: &[u8], sample_rate: u32, num_channels: u32) -> bool { + let mut samples = Vec::new(); + pcm16_audio_frame_into(bytes, sample_rate, num_channels, &mut samples).is_some() + } + + fn f32_bytes(samples: &[f32]) -> Vec { + let mut bytes = Vec::with_capacity(samples.len() * 4); + for sample in samples { + bytes.extend_from_slice(&sample.to_le_bytes()); + } + bytes + } + + fn f32_frame_accepted(bytes: &[u8], sample_rate: u32, num_channels: u32) -> bool { + let mut samples = Vec::new(); + f32_audio_frame_into(bytes, sample_rate, num_channels, &mut samples).is_some() + } + + #[test] + fn speaking_tap_frame_rms_accepts_interleaved_stereo_frames() { + let samples = [8_192i16, -8_192, 8_192, -8_192]; + let frame = audio_frame_for_tests(&samples, 2); + + let rms = SpeakingTap::frame_rms(&frame); + + assert!((rms - 0.25).abs() < 0.001); + } + + #[test] + fn speaking_tap_frame_rms_rejects_bad_channel_counts_without_panic() { + let samples = [8_192i16, -8_192]; + let zero_channels = audio_frame_for_tests(&samples, 0); + let too_many_channels = audio_frame_for_tests(&samples, MAX_AUDIO_CHANNELS + 1); + + assert_eq!(SpeakingTap::frame_rms(&zero_channels), 0.0); + assert_eq!(SpeakingTap::frame_rms(&too_many_channels), 0.0); + } + + #[test] + fn speaking_tap_frame_rms_rejects_misaligned_interleaved_frames() { + let samples = [8_192i16, -8_192, 8_192]; + let frame = audio_frame_for_tests(&samples, 2); + + assert_eq!(SpeakingTap::frame_rms(&frame), 0.0); + } + + #[test] + fn speaking_tap_frame_rms_rejects_overlarge_frames_without_panic() { + let samples = vec![1i16; SPEAKING_TAP_FRAME_SAMPLES_MAX + 1]; + let frame = audio_frame_for_tests(&samples, 1); + + assert_eq!(SpeakingTap::frame_rms(&frame), 0.0); + } + + #[test] + fn pcm16_audio_frame_shapes_interleaved_stereo_samples() { + let bytes = [1u8, 0, 255, 255, 2, 0, 254, 255]; + let mut samples = Vec::new(); + let frame = pcm16_audio_frame_into(&bytes, 48_000, 2, &mut samples).expect("valid frame"); + + assert_eq!(frame.sample_rate, 48_000); + assert_eq!(frame.num_channels, 2); + assert_eq!(frame.samples_per_channel, 2); + assert_eq!(frame.data.as_ref(), &[1, -1, 2, -2]); + } + + #[test] + fn pcm16_audio_frame_reuses_scratch_capacity_across_frames() { + let bytes = [1u8, 0, 255, 255, 2, 0, 254, 255]; + let mut samples = Vec::with_capacity(bytes.len() / 2); + let scratch_ptr = samples.as_ptr(); + + for _ in 0..3 { + let frame = + pcm16_audio_frame_into(&bytes, 48_000, 2, &mut samples).expect("valid frame"); + assert_eq!(frame.data.as_ref(), &[1, -1, 2, -2]); + } + + assert_eq!(samples.as_ptr(), scratch_ptr); + assert_eq!(samples.capacity(), bytes.len() / 2); + } + + #[test] + fn f32_audio_frame_shapes_and_clamps_samples() { + let bytes = f32_bytes(&[-2.0, -1.0, -0.5, 0.0, 0.5, 1.0, 2.0, f32::NAN]); + let mut samples = Vec::new(); + let frame = f32_audio_frame_into(&bytes, 48_000, 2, &mut samples).expect("valid frame"); + + assert_eq!(frame.sample_rate, 48_000); + assert_eq!(frame.num_channels, 2); + assert_eq!(frame.samples_per_channel, 4); + assert_eq!( + frame.data.as_ref(), + &[-32768, -32768, -16384, 0, 16383, 32767, 32767, 0] + ); + } + + #[test] + fn f32_audio_frame_reuses_scratch_capacity_across_frames() { + let bytes = f32_bytes(&[0.25, -0.25, 0.75, -0.75]); + let mut samples = Vec::with_capacity(bytes.len() / 4); + let scratch_ptr = samples.as_ptr(); + + for _ in 0..3 { + let frame = f32_audio_frame_into(&bytes, 48_000, 2, &mut samples).expect("valid frame"); + assert_eq!(frame.data.as_ref(), &[8191, -8192, 24575, -24576]); + } + + assert_eq!(samples.as_ptr(), scratch_ptr); + assert_eq!(samples.capacity(), bytes.len() / 4); + } + + #[test] + fn pcm16_audio_frame_rejects_malformed_buffers() { + assert!(!pcm16_frame_accepted(&[], 48_000, 2)); + assert!(!pcm16_frame_accepted(&[0], 48_000, 2)); + assert!(!pcm16_frame_accepted(&[0, 0, 1, 0], 48_000, 3)); + assert!(!pcm16_frame_accepted(&[0, 0], 0, 1)); + assert!(!pcm16_frame_accepted(&[0, 0], 48_000, 0)); + } + + #[test] + fn f32_audio_frame_rejects_malformed_buffers() { + assert!(!f32_frame_accepted(&[], 48_000, 2)); + assert!(!f32_frame_accepted(&[0, 0, 0], 48_000, 2)); + assert!(!f32_frame_accepted(&f32_bytes(&[0.0, 1.0]), 48_000, 3)); + assert!(!f32_frame_accepted(&f32_bytes(&[0.0]), 0, 1)); + assert!(!f32_frame_accepted(&f32_bytes(&[0.0]), 48_000, 0)); + } + + #[test] + fn pcm16_audio_frame_rejects_implausible_format_and_overlong_chunks() { + assert!(valid_audio_format(8_000, 1)); + assert!(valid_audio_format(192_000, 8)); + assert!(!valid_audio_format(7_999, 1)); + assert!(!valid_audio_format(192_001, 1)); + assert!(!valid_audio_format(48_000, 9)); + + let one_second_stereo = vec![0u8; 48_000 * 2 * 2]; + assert!(pcm16_frame_accepted(&one_second_stereo, 48_000, 2)); + + let overlong_stereo = vec![0u8; (48_000 + 1) * 2 * 2]; + assert!(!pcm16_frame_accepted(&overlong_stereo, 48_000, 2)); + } + + #[test] + fn same_audio_format_matches_only_identical_sample_rate_and_channels() { + assert!(same_audio_format(48_000, 2, 48_000, 2)); + assert!(!same_audio_format(48_000, 2, 44_100, 2)); + assert!(!same_audio_format(48_000, 2, 48_000, 1)); + } + + #[test] + fn f32_audio_frame_rejects_overlong_chunks() { + let one_second_stereo = vec![0u8; 48_000 * 2 * 4]; + assert!(f32_frame_accepted(&one_second_stereo, 48_000, 2)); + + let overlong_stereo = vec![0u8; (48_000 + 1) * 2 * 4]; + assert!(!f32_frame_accepted(&overlong_stereo, 48_000, 2)); + } + + #[test] + fn pcm_scratch_capacity_is_bounded_by_named_caps() { + let scratch = new_pcm_scratch(48_000, 2); + let capacity = scratch.blocking_lock().capacity(); + assert_eq!(capacity, 48_000 * 2); + assert!(capacity <= MAX_PCM_FRAME_SAMPLES); + } + + #[test] + fn parse_video_quality_accepts_subscription_quality_levels() { + assert_eq!(parse_video_quality("low"), Some(VideoQuality::Low)); + assert_eq!(parse_video_quality("medium"), Some(VideoQuality::Medium)); + assert_eq!(parse_video_quality("high"), Some(VideoQuality::High)); + assert_eq!(parse_video_quality("off"), None); + } + + #[test] + fn video_input_validation_rejects_degenerate_odd_and_oversized_frames() { + assert!(valid_even_video_dims(2, 2)); + assert!(valid_even_video_dims(8192, 8192)); + assert!(!valid_even_video_dims(0, 2)); + assert!(!valid_even_video_dims(1, 2)); + assert!(!valid_even_video_dims(3, 2)); + assert!(!valid_even_video_dims(8194, 2)); + } + + #[test] + fn processed_camera_frame_to_pending_accepts_tight_i420_payloads() { + let frame = ProcessedCameraFrame { + format: "i420".to_string(), + width: 2, + height: 2, + timestamp_us: 123, + data: Buffer::from(vec![1, 1, 1, 1, 2, 3]), + }; + + let pending = processed_camera_frame_to_pending(frame).expect("processed camera frame"); + + match pending { + PendingVideoFrame::I420Native { + buffer, + timestamp_us, + .. + } => { + assert_eq!(buffer.width(), 2); + assert_eq!(buffer.height(), 2); + assert_eq!(timestamp_us, 123); + let (stride_y, _, _) = buffer.strides(); + let (y, u, v) = buffer.data(); + assert_eq!(&y[..2], &[1, 1]); + let second_row = stride_y as usize; + assert_eq!(&y[second_row..second_row + 2], &[1, 1]); + assert_eq!(u[0], 2); + assert_eq!(v[0], 3); + } + _ => panic!("expected native i420 processed camera frame"), + } + } + + #[test] + fn processed_camera_frame_to_pending_rejects_loose_i420_payloads() { + let frame = ProcessedCameraFrame { + format: "i420".to_string(), + width: 2, + height: 2, + timestamp_us: 123, + data: Buffer::from(vec![1, 2, 3]), + }; + + assert!(processed_camera_frame_to_pending(frame).is_err()); + } + + #[test] + fn adaptive_video_sender_stop_clears_pending_and_rejects_new_frames() { + let (sender, stats) = sender_for_tests(); + + assert_eq!(sender.enqueue(tiny_bgra_frame()), EnqueueResult::Accepted); + assert_eq!(sender.pending.len(), 1); + + sender.stop(); + assert_eq!(sender.pending.len(), 0); + assert_eq!(sender.enqueue(tiny_bgra_frame()), EnqueueResult::Rejected); + assert_eq!(sender.pending.len(), 0); + + let audio = AdaptiveAudioStats::new(DEFAULT_AUDIO_BUFFER_MAX_MS, 0); + let snapshot = stats.snapshot(&audio, VideoTelemetryExtras::default()); + assert_eq!(snapshot.outgoing_video_queue_depth, 0); + assert_eq!(snapshot.outgoing_video_frames_produced, 1); + assert_eq!(snapshot.outgoing_video_frames_accepted, 1); + assert_eq!(snapshot.outgoing_video_frames_dropped, 1); + assert_eq!(snapshot.outgoing_video_frames_coalesced, 0); + } + + #[test] + fn adaptive_video_sender_accumulates_frames_until_capacity() { + let (sender, stats) = sender_for_tests(); + + for _ in 0..ENCODER_QUEUE_CAPACITY { + assert_eq!(sender.enqueue(tiny_bgra_frame()), EnqueueResult::Accepted); + } + + assert_eq!(sender.pending.len(), ENCODER_QUEUE_CAPACITY); + let audio = AdaptiveAudioStats::new(DEFAULT_AUDIO_BUFFER_MAX_MS, 0); + let snapshot = stats.snapshot(&audio, VideoTelemetryExtras::default()); + assert_eq!( + snapshot.outgoing_video_queue_depth, + ENCODER_QUEUE_CAPACITY as u64 + ); + assert_eq!( + snapshot.outgoing_video_frames_produced, + ENCODER_QUEUE_CAPACITY as u64 + ); + assert_eq!( + snapshot.outgoing_video_frames_accepted, + ENCODER_QUEUE_CAPACITY as u64 + ); + assert_eq!(snapshot.outgoing_video_frames_dropped, 0); + assert_eq!(snapshot.outgoing_video_frames_coalesced, 0); + } + + #[test] + fn adaptive_video_sender_coalesces_oldest_when_capacity_exceeded() { + let (sender, stats) = sender_for_tests(); + + for _ in 0..ENCODER_QUEUE_CAPACITY { + assert_eq!(sender.enqueue(tiny_bgra_frame()), EnqueueResult::Accepted); + } + let coalesced_pushes = 3; + for _ in 0..coalesced_pushes { + assert_eq!(sender.enqueue(tiny_bgra_frame()), EnqueueResult::Coalesced); + } + + assert_eq!(sender.pending.len(), ENCODER_QUEUE_CAPACITY); + let audio = AdaptiveAudioStats::new(DEFAULT_AUDIO_BUFFER_MAX_MS, 0); + let snapshot = stats.snapshot(&audio, VideoTelemetryExtras::default()); + assert_eq!( + snapshot.outgoing_video_queue_depth, + ENCODER_QUEUE_CAPACITY as u64 + ); + let total_pushed = (ENCODER_QUEUE_CAPACITY + coalesced_pushes) as u64; + assert_eq!(snapshot.outgoing_video_frames_produced, total_pushed); + assert_eq!(snapshot.outgoing_video_frames_accepted, total_pushed); + assert_eq!(snapshot.outgoing_video_frames_dropped, 0); + assert_eq!( + snapshot.outgoing_video_frames_coalesced, + coalesced_pushes as u64 + ); + } + + #[test] + fn adaptive_video_sender_concurrent_producers_never_reject_until_stopped() { + let (sender, stats) = sender_for_tests(); + let producers = 8; + let frames_per_producer = 128; + let handles: Vec<_> = (0..producers) + .map(|_| { + let s = sender.clone(); + std::thread::spawn(move || { + for _ in 0..frames_per_producer { + let result = s.enqueue(tiny_bgra_frame()); + assert!( + matches!(result, EnqueueResult::Accepted | EnqueueResult::Coalesced), + "expected Accepted|Coalesced, got {:?}", + result + ); + } + }) + }) + .collect(); + for h in handles { + h.join().expect("producer panicked"); + } + let total = (producers * frames_per_producer) as u64; + let audio = AdaptiveAudioStats::new(DEFAULT_AUDIO_BUFFER_MAX_MS, 0); + let snapshot = stats.snapshot(&audio, VideoTelemetryExtras::default()); + assert_eq!(snapshot.outgoing_video_frames_produced, total); + assert_eq!(snapshot.outgoing_video_frames_accepted, total); + assert_eq!(snapshot.outgoing_video_frames_dropped, 0); + assert!( + snapshot.outgoing_video_queue_depth <= ENCODER_QUEUE_CAPACITY as u64, + "queue depth {} exceeded capacity {}", + snapshot.outgoing_video_queue_depth, + ENCODER_QUEUE_CAPACITY + ); + assert!( + snapshot.outgoing_video_frames_coalesced > 0, + "producers overran capacity but no coalesce was recorded" + ); + } + + #[test] + fn bus_sender_sink_rejects_copied_nv12_frames() { + let (sender, _stats) = sender_for_tests(); + let sink = BusSenderSink { + sender: sender.clone(), + texture_capability: texture_source::TextureCapability::unavailable( + texture_source::TextureEncodeError::NoHardwareEncoder, + ), + }; + let frame = BusScreenFrame::Nv12(frame_bus::Nv12Frame { + data: vec![0u8; 8 * 8 + 8 * 4].into(), + width: 8, + height: 8, + stride_y: 8, + stride_uv: 8, + timestamp_us: 1234, + }); + assert_eq!(sink.enqueue(frame), EnqueueOutcome::Rejected); + assert_eq!(sender.pending.len(), 0); + } + + #[test] + fn bus_sender_sink_rejects_copied_bgra_frames() { + let (sender, _stats) = sender_for_tests(); + let sink = BusSenderSink { + sender: sender.clone(), + texture_capability: texture_source::TextureCapability::unavailable( + texture_source::TextureEncodeError::NoHardwareEncoder, + ), + }; + let frame = BusScreenFrame::Bgra(frame_bus::BgraFrame { + data: vec![0; 4 * 8 * 8], + width: 8, + height: 8, + stride: 32, + timestamp_us: 5678, + }); + assert_eq!(sink.enqueue(frame), EnqueueOutcome::Rejected); + assert_eq!(sender.pending.len(), 0); + } + + #[test] + fn native_screen_frame_sink_handle_omits_copied_frame_callbacks() { + let (sender, _stats) = sender_for_tests(); + let sink = BusSenderSink { + sender: sender.clone(), + texture_capability: texture_source::TextureCapability::unavailable( + texture_source::TextureEncodeError::NoHardwareEncoder, + ), + }; + let handle = create_native_screen_frame_sink_handle(Arc::new(sink)); + assert!(handle.is_valid()); + assert!(handle.enqueue_nv12.is_none()); + assert!(handle.enqueue_bgra.is_none()); + assert!(handle.enqueue_screen_audio.is_none()); + unsafe { (handle.release)(handle.context) }; + } + + #[test] + fn screen_audio_sink_handle_exposes_only_audio_callback() { + let handle = create_screen_audio_sink_handle(Arc::new(ScreenAudioRing::new())); + assert!(handle.is_valid()); + assert!(handle.enqueue_screen_audio.is_some()); + assert!(handle.enqueue_nv12.is_none()); + assert!(handle.enqueue_mac_cv_pixel_buffer.is_none()); + unsafe { (handle.release)(handle.context) }; + } + + #[test] + fn screen_audio_ring_drops_oldest_when_full_and_counts() { + let ring = ScreenAudioRing::new(); + for i in 0..(SCREEN_AUDIO_RING_CAP + 4) { + ring.push(ScreenAudioChunk { + samples: vec![i as f32; 2], + num_frames: 1, + channels: 2, + sample_rate_hz: 48_000, + }); + } + assert_eq!(ring.dropped.load(Ordering::Relaxed), 4); + let mut drained = 0; + while ring.filled.pop().is_some() { + drained += 1; + } + assert_eq!(drained, SCREEN_AUDIO_RING_CAP); + } + + #[test] + fn screen_audio_ring_recycles_buffers() { + let ring = ScreenAudioRing::new(); + let mut buffer = ring.take_buffer(); + buffer.extend_from_slice(&[1.0, 2.0, 3.0]); + ring.recycle(buffer); + let reused = ring.take_buffer(); + assert!(reused.is_empty()); + assert!(reused.capacity() >= 3); + } + + #[test] + fn native_buffered_camera_source_exposes_only_native_frame_sink() { + let (sender, _stats) = sender_for_tests(); + let frame_sink = Arc::new(BusSenderSink { + sender: sender.clone(), + texture_capability: texture_source::TextureCapability::unavailable( + texture_source::TextureEncodeError::NoHardwareEncoder, + ), + }); + let track_sid = Arc::new(Mutex::new("TR_native_camera".to_string())); + let source = CameraSource::NativeBuffered { + track_sid: track_sid.clone(), + video_sender: sender.clone(), + frame_sink: frame_sink.clone(), + }; + + assert!(source.processed_sender().is_none()); + assert!(Arc::ptr_eq( + &source.native_frame_sink().unwrap(), + &frame_sink + )); + assert!(Arc::ptr_eq(&source.track_sid(), &track_sid)); + + source.stop(); + assert_eq!(sender.enqueue(tiny_bgra_frame()), EnqueueResult::Rejected); + } + + #[test] + fn adaptive_video_sender_returns_coalesced_when_queue_is_full() { + let (sender, _stats) = sender_for_tests(); + for _ in 0..ENCODER_QUEUE_CAPACITY { + assert_eq!(sender.enqueue(tiny_bgra_frame()), EnqueueResult::Accepted); + } + assert_eq!(sender.enqueue(tiny_bgra_frame()), EnqueueResult::Coalesced); + assert_eq!(sender.pending.len(), ENCODER_QUEUE_CAPACITY); + } + + #[test] + fn adaptive_video_sender_returns_rejected_after_stopping() { + let (sender, _stats) = sender_for_tests(); + sender.stop(); + assert_eq!(sender.enqueue(tiny_bgra_frame()), EnqueueResult::Rejected); + } + + #[cfg(target_os = "macos")] + #[test] + fn bus_sender_sink_rejects_mac_cv_pixel_buffer_with_null_pointer() { + let (sender, _stats) = sender_for_tests(); + let sink = BusSenderSink { + sender: sender.clone(), + texture_capability: texture_source::TextureCapability::unavailable( + texture_source::TextureEncodeError::NoHardwareEncoder, + ), + }; + let mac_frame = unsafe { + fluxer_screen_frame_bus::MacCvPixelBufferFrame::from_retained( + std::ptr::null_mut(), + 8, + 8, + 0, + 42, + ) + }; + let frame = BusScreenFrame::MacCvPixelBuffer(mac_frame); + assert_eq!(sink.enqueue(frame), EnqueueOutcome::Rejected); + assert_eq!(sender.pending.len(), 0); + } + + #[test] + fn parse_codec_agrees_with_pure_canonicaliser() { + for name in crate::config::SUPPORTED_CODECS { + assert!(parse_codec(name).is_some(), "engine must accept {name}"); + } + assert_eq!(parse_codec("hevc"), Some(VideoCodec::H265)); + } + + #[test] + fn session_rtt_uses_peer_connection_candidate_pair_without_track_stats() { + let mut stats = stats_mod::ConnectionStats::default(); + + absorb_session_rtt(&[candidate_pair_report(true, 0.037)], &[], &mut stats); + + assert_eq!(stats.rtt_ms, Some(37.0)); + } + + #[test] + fn session_rtt_falls_back_to_subscriber_and_preserves_existing_rtt() { + let mut stats = stats_mod::ConnectionStats::default(); + + absorb_session_rtt( + &[candidate_pair_report(false, 0.011)], + &[candidate_pair_report(true, 0.042)], + &mut stats, + ); + + assert_eq!(stats.rtt_ms, Some(42.0)); + absorb_session_rtt(&[candidate_pair_report(true, 0.100)], &[], &mut stats); + assert_eq!(stats.rtt_ms, Some(42.0)); + } + + #[test] + fn local_i420_payload_concatenates_tight_planes_and_meta_matches_contract() { + let frame = yuv::I420 { + width: 4, + height: 2, + y: vec![1u8; 8], + u: vec![2u8; 2], + v: vec![3u8; 2], + }; + let (meta, buffer) = local_i420_to_callback_payload( + &frame, + 123456, + "PA_localA12345", + "user_1_connection_1", + "TR_localCamera01", + "camera", + "camera", + ); + let expected_meta = format!( + "{{\"bridgeVersion\":{},\"participantSid\":\"PA_localA12345\",\"participantIdentity\":\"user_1_connection_1\",\"trackSid\":\"TR_localCamera01\",\"trackName\":\"camera\",\"source\":\"camera\",\"width\":4,\"height\":2,\"timestampUs\":123456}}", + crate::bridge_version::ENGINE_BRIDGE_VERSION + ); + assert_eq!(meta, expected_meta); + assert_eq!(&buffer[..], &[1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 3, 3]); + } + + #[test] + #[should_panic] + fn local_video_frame_sink_rejects_empty_track_sid() { + let _ = local_video_frame_sink( + Arc::new(Mutex::new(None)), + Arc::new(AtomicU64::new(0)), + "PA_localA12345".to_string(), + "user_1_connection_1".to_string(), + Arc::new(Mutex::new(String::new())), + "camera".to_string(), + "camera".to_string(), + ); + } + + #[test] + #[should_panic] + fn local_video_frame_sink_rejects_backwards_timestamps() { + let sink = local_video_frame_sink( + Arc::new(Mutex::new(None)), + Arc::new(AtomicU64::new(0)), + "PA_localA12345".to_string(), + "user_1_connection_1".to_string(), + Arc::new(Mutex::new("TR_localCamera01".to_string())), + "camera".to_string(), + "camera".to_string(), + ); + let frame = yuv::I420 { + width: 2, + height: 2, + y: vec![0u8; 4], + u: vec![0u8; 1], + v: vec![0u8; 1], + }; + sink(&frame, 100); + sink(&frame, 99); + } + + fn camera_source_for_tests(track_sid: &str) -> (CameraSource, Arc>) { + let shared = Arc::new(Mutex::new(track_sid.to_string())); + let source = CameraSource::Device { + track_sid: shared.clone(), + stop: Arc::new(AtomicBool::new(false)), + capture: None, + }; + (source, shared) + } + + struct OwnedTrackSlots { + camera: Mutex>, + screen_camera: Mutex>, + screen: Mutex>, + screen_audio: Mutex>, + mic: Mutex>, + } + + impl OwnedTrackSlots { + fn new() -> Self { + Self { + camera: Mutex::new(None), + screen_camera: Mutex::new(None), + screen: Mutex::new(None), + screen_audio: Mutex::new(None), + mic: Mutex::new(None), + } + } + + fn as_slots(&self) -> LocalTrackSlots<'_> { + LocalTrackSlots { + camera: &self.camera, + screen_camera: &self.screen_camera, + screen: &self.screen, + screen_audio: &self.screen_audio, + mic: &self.mic, + } + } + } + + fn track_sid_for_tests(sid: &str) -> TrackSid { + assert!(sid.starts_with("TR_")); + TrackSid::try_from(sid.to_string()).expect("test sid is TR_-prefixed") + } + + #[test] + fn local_track_republish_swaps_matching_camera_sid() { + let (source, shared) = camera_source_for_tests("TR_oldCamera0001"); + let owned = OwnedTrackSlots::new(); + *owned.camera.lock() = Some(source); + + let swapped = + apply_local_track_republish(&owned.as_slots(), "TR_oldCamera0001", "TR_newCamera0001"); + + assert!(swapped); + assert_eq!(*shared.lock(), "TR_newCamera0001"); + } + + #[test] + fn local_track_republish_swaps_matching_screen_camera_sid() { + let (source, shared) = camera_source_for_tests("TR_oldScreen0001"); + let owned = OwnedTrackSlots::new(); + *owned.screen_camera.lock() = Some(source); + + let swapped = + apply_local_track_republish(&owned.as_slots(), "TR_oldScreen0001", "TR_newScreen0001"); + + assert!(swapped); + assert_eq!(*shared.lock(), "TR_newScreen0001"); + } + + #[test] + fn local_track_republish_ignores_unrelated_previous_sid() { + let (camera_source, camera_shared) = camera_source_for_tests("TR_camera000001"); + let (screen_source, screen_shared) = camera_source_for_tests("TR_screen000001"); + let owned = OwnedTrackSlots::new(); + *owned.camera.lock() = Some(camera_source); + *owned.screen_camera.lock() = Some(screen_source); + + let swapped = + apply_local_track_republish(&owned.as_slots(), "TR_unrelated0001", "TR_newTrack0001"); + + assert!(!swapped); + assert_eq!(*camera_shared.lock(), "TR_camera000001"); + assert_eq!(*screen_shared.lock(), "TR_screen000001"); + } + + #[test] + fn local_track_republish_reports_already_applied_sid_as_matched() { + let (source, shared) = camera_source_for_tests("TR_newCamera0001"); + let owned = OwnedTrackSlots::new(); + *owned.camera.lock() = Some(source); + + let swapped = + apply_local_track_republish(&owned.as_slots(), "TR_oldCamera0001", "TR_newCamera0001"); + + assert!(swapped); + assert_eq!(*shared.lock(), "TR_newCamera0001"); + } + + #[test] + #[should_panic] + fn local_track_republish_rejects_empty_previous_sid() { + let owned = OwnedTrackSlots::new(); + apply_local_track_republish(&owned.as_slots(), "", "TR_newTrack0001"); + } + + #[test] + #[should_panic] + fn local_track_republish_rejects_empty_republished_sid() { + let owned = OwnedTrackSlots::new(); + apply_local_track_republish(&owned.as_slots(), "TR_oldTrack0001", ""); + } + + #[test] + #[should_panic] + fn local_track_republish_rejects_multiple_matching_slots() { + let (camera_source, _camera_shared) = camera_source_for_tests("TR_duplicate0001"); + let (screen_source, _screen_shared) = camera_source_for_tests("TR_duplicate0001"); + let owned = OwnedTrackSlots::new(); + *owned.camera.lock() = Some(camera_source); + *owned.screen_camera.lock() = Some(screen_source); + apply_local_track_republish(&owned.as_slots(), "TR_duplicate0001", "TR_newTrack0001"); + } + + #[test] + fn republish_track_sid_value_swaps_matching_sid() { + let mut sid = track_sid_for_tests("TR_oldMic00000001"); + let matched = republish_track_sid_value(&mut sid, "TR_oldMic00000001", "TR_newMic00000001"); + assert_eq!(matched, 1); + assert_eq!(sid.as_str(), "TR_newMic00000001"); + } + + #[test] + fn republish_track_sid_value_ignores_unrelated_sid() { + let mut sid = track_sid_for_tests("TR_screenAudio001"); + let matched = republish_track_sid_value(&mut sid, "TR_unrelated0001", "TR_newTrack0001"); + assert_eq!(matched, 0); + assert_eq!(sid.as_str(), "TR_screenAudio001"); + } + + #[test] + fn republish_track_sid_value_reports_already_applied_sid_as_matched() { + let mut sid = track_sid_for_tests("TR_newMic00000001"); + let matched = republish_track_sid_value(&mut sid, "TR_oldMic00000001", "TR_newMic00000001"); + assert_eq!(matched, 1); + assert_eq!(sid.as_str(), "TR_newMic00000001"); + } + + #[test] + fn republish_screen_sids_swaps_track_and_metadata_sids_together() { + let mut track_sid = track_sid_for_tests("TR_oldScreen0001"); + let mut metadata_track_sid = track_sid_for_tests("TR_oldScreen0001"); + let matched = republish_screen_sids( + &mut track_sid, + &mut metadata_track_sid, + "TR_oldScreen0001", + "TR_newScreen0001", + ); + assert_eq!(matched, 1); + assert_eq!(track_sid.as_str(), "TR_newScreen0001"); + assert_eq!(metadata_track_sid.as_str(), "TR_newScreen0001"); + } + + #[test] + fn republish_screen_sids_leaves_unrelated_sids_unchanged() { + let mut track_sid = track_sid_for_tests("TR_screen000001"); + let mut metadata_track_sid = track_sid_for_tests("TR_screen000001"); + let matched = republish_screen_sids( + &mut track_sid, + &mut metadata_track_sid, + "TR_unrelated0001", + "TR_newScreen0001", + ); + assert_eq!(matched, 0); + assert_eq!(track_sid.as_str(), "TR_screen000001"); + assert_eq!(metadata_track_sid.as_str(), "TR_screen000001"); + } + + #[test] + #[should_panic] + fn republish_screen_sids_rejects_diverged_metadata_sid() { + let mut track_sid = track_sid_for_tests("TR_screen000001"); + let mut metadata_track_sid = track_sid_for_tests("TR_diverged0001"); + republish_screen_sids( + &mut track_sid, + &mut metadata_track_sid, + "TR_screen000001", + "TR_newScreen0001", + ); + } + + #[test] + fn store_camera_slot_fills_empty_slot() { + let (source, shared) = camera_source_for_tests("TR_camera000001"); + let slot = Mutex::new(None); + store_camera_slot(&slot, source); + assert!(slot.lock().is_some()); + assert_eq!(*shared.lock(), "TR_camera000001"); + } + + #[test] + #[should_panic] + fn store_camera_slot_rejects_occupied_slot() { + let (first, _) = camera_source_for_tests("TR_camera000001"); + let (second, _) = camera_source_for_tests("TR_camera000002"); + let slot = Mutex::new(Some(first)); + store_camera_slot(&slot, second); + } + + #[test] + fn remove_camera_slot_if_held_removes_only_matching_source() { + let (source, shared) = camera_source_for_tests("TR_camera000001"); + let slot = Mutex::new(Some(source)); + let unrelated = Arc::new(Mutex::new("TR_camera000002".to_string())); + + remove_camera_slot_if_held(&slot, &unrelated); + assert!(slot.lock().is_some()); + + remove_camera_slot_if_held(&slot, &shared); + assert!(slot.lock().is_none()); + } + + fn camera_options_for_tests(device_id: Option<&str>) -> CameraOptions { + CameraOptions { + device_id: device_id.map(str::to_string), + width: None, + height: None, + frame_rate: None, + mirror: None, + background_mode: None, + background_custom_media_path: None, + background_custom_media_kind: None, + background_blur_strength: None, + codec: None, + max_bitrate_bps: None, + max_framerate: None, + } + } + + #[test] + fn camera_swap_order_opens_new_first_for_a_different_device() { + use crate::camera::CameraSelector; + assert_eq!( + camera_swap_order(&CameraSelector::Index(0), &CameraSelector::Index(1)), + CameraSwapOrder::OpenNewThenStopOld + ); + assert_eq!( + camera_swap_order( + &CameraSelector::Index(0), + &CameraSelector::Id("front".to_string()) + ), + CameraSwapOrder::OpenNewThenStopOld + ); + assert_eq!( + camera_swap_order( + &CameraSelector::Id("front".to_string()), + &CameraSelector::Id("back".to_string()) + ), + CameraSwapOrder::OpenNewThenStopOld + ); + } + + #[test] + fn camera_swap_order_stops_old_first_for_the_same_device() { + use crate::camera::CameraSelector; + assert_eq!( + camera_swap_order(&CameraSelector::Index(2), &CameraSelector::Index(2)), + CameraSwapOrder::StopOldThenOpenNew + ); + assert_eq!( + camera_swap_order( + &CameraSelector::Id("front".to_string()), + &CameraSelector::Id("front".to_string()) + ), + CameraSwapOrder::StopOldThenOpenNew + ); + } + + #[test] + fn camera_background_config_threads_effect_strengths_from_options() { + let mut opts = camera_options_for_tests(None); + opts.background_mode = Some("blur".to_string()); + opts.background_blur_strength = Some(80); + + let config = native_camera_background_config(&opts).unwrap(); + assert_eq!(config.blur_strength, 80); + + let defaults = native_camera_background_config(&camera_options_for_tests(None)).unwrap(); + assert_eq!( + defaults.blur_strength, + crate::camera_background::CAMERA_EFFECT_STRENGTH_DEFAULT + ); + } + + #[tokio::test] + async fn update_camera_capture_requires_a_published_device_camera() { + let engine = VoiceEngine::new(); + + let error = engine + .update_camera_capture(camera_options_for_tests(None)) + .await + .unwrap_err(); + assert_eq!(error.reason, "camera is not published"); + + let (sender, _stats) = sender_for_tests(); + *engine.camera.lock() = Some(CameraSource::Processed { + track_sid: Arc::new(Mutex::new("TR_processed0001".to_string())), + video_sender: sender, + }); + let error = engine + .update_camera_capture(camera_options_for_tests(None)) + .await + .unwrap_err(); + assert_eq!(error.reason, "camera capture is not device-managed"); + } + + #[tokio::test] + async fn update_camera_capture_rejects_device_slots_without_capture_context() { + let engine = VoiceEngine::new(); + let (source, _shared) = camera_source_for_tests("TR_camera000001"); + *engine.camera.lock() = Some(source); + + let error = engine + .update_camera_capture(camera_options_for_tests(Some("1"))) + .await + .unwrap_err(); + assert_eq!(error.reason, "camera capture is not device-managed"); + } + + #[test] + fn commit_device_camera_swap_replaces_only_the_expected_worker_stop() { + let (source, shared) = camera_source_for_tests("TR_camera000001"); + let slot = Mutex::new(Some(source)); + let expected = match slot.lock().as_ref().unwrap() { + CameraSource::Device { stop, .. } => stop.clone(), + _ => panic!("device slot expected"), + }; + let swapped = Arc::new(AtomicBool::new(false)); + let request = + camera::CameraRequest::from_opts(None, None, None, None, false, Default::default()); + + assert!(commit_device_camera_swap( + &slot, &shared, &expected, &swapped, &request + )); + assert!(!commit_device_camera_swap( + &slot, &shared, &expected, &swapped, &request + )); + + match slot.lock().as_ref().unwrap() { + CameraSource::Device { stop, .. } => assert!(Arc::ptr_eq(stop, &swapped)), + _ => panic!("device slot expected"), + } + } + + #[test] + fn commit_device_camera_swap_rejects_missing_or_foreign_slots() { + let request = + camera::CameraRequest::from_opts(None, None, None, None, false, Default::default()); + let expected = Arc::new(AtomicBool::new(false)); + let swapped = Arc::new(AtomicBool::new(false)); + + let empty = Mutex::new(None); + let foreign_sid = Arc::new(Mutex::new("TR_camera000001".to_string())); + assert!(!commit_device_camera_swap( + &empty, + &foreign_sid, + &expected, + &swapped, + &request + )); + + let (source, _shared) = camera_source_for_tests("TR_camera000001"); + let slot = Mutex::new(Some(source)); + assert!(!commit_device_camera_swap( + &slot, + &foreign_sid, + &expected, + &swapped, + &request + )); + } + + #[test] + fn local_video_frame_sink_payload_reflects_republished_sid() { + let (source, shared) = camera_source_for_tests("TR_oldCamera0001"); + let owned = OwnedTrackSlots::new(); + *owned.camera.lock() = Some(source); + let frame = yuv::I420 { + width: 4, + height: 2, + y: vec![1u8; 8], + u: vec![2u8; 2], + v: vec![3u8; 2], + }; + + let (meta_before, _) = local_video_frame_sink_payload( + &frame, + 100, + "PA_localA12345", + "user_1_connection_1", + &shared, + "camera", + "camera", + ); + assert!(meta_before.contains("\"trackSid\":\"TR_oldCamera0001\"")); + + assert!(apply_local_track_republish( + &owned.as_slots(), + "TR_oldCamera0001", + "TR_newCamera0001" + )); + let (meta_after, _) = local_video_frame_sink_payload( + &frame, + 200, + "PA_localA12345", + "user_1_connection_1", + &shared, + "camera", + "camera", + ); + assert!(meta_after.contains("\"trackSid\":\"TR_newCamera0001\"")); + } + + #[test] + fn record_first_error_keeps_earliest_error() { + let mut slot = None; + record_first_error(&mut slot, Ok(())); + assert!(slot.is_none()); + record_first_error(&mut slot, Err(napi::Error::from_reason("first"))); + record_first_error(&mut slot, Err(napi::Error::from_reason("second"))); + let error = slot.expect("first error retained"); + assert!(error.reason.contains("first")); + } + + #[test] + fn video_frame_meta_json_matches_contract() { + let json = video_frame_meta_json( + "PA_remoteB12345", + "user_2_connection_2", + "TR_remoteVideo01", + "screen", + "screen_share", + 1920, + 1080, + 123456, + ); + let expected = format!( + "{{\"bridgeVersion\":{},\"participantSid\":\"PA_remoteB12345\",\"participantIdentity\":\"user_2_connection_2\",\"trackSid\":\"TR_remoteVideo01\",\"trackName\":\"screen\",\"source\":\"screen_share\",\"width\":1920,\"height\":1080,\"timestampUs\":123456}}", + crate::bridge_version::ENGINE_BRIDGE_VERSION + ); + assert_eq!(json, expected); + } + + #[test] + fn tight_i420_len_matches_plane_geometry() { + assert_eq!(tight_i420_len(4, 2), 8 + 2 + 2); + assert_eq!(tight_i420_len(1920, 1080), 1920 * 1080 * 3 / 2); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/src/events.rs b/fluxer_desktop/native/webrtc-sender/src/events.rs new file mode 100644 index 0000000..090c655 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/src/events.rs @@ -0,0 +1,643 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub fn push_json_string(out: &mut String, value: &str) { + out.push('"'); + for ch in value.chars() { + match ch { + '"' => out.push_str("\\\""), + '\\' => out.push_str("\\\\"), + '\n' => out.push_str("\\n"), + '\r' => out.push_str("\\r"), + '\t' => out.push_str("\\t"), + '\u{08}' => out.push_str("\\b"), + '\u{0C}' => out.push_str("\\f"), + c if (c as u32) < 0x20 => { + out.push_str(&format!("\\u{:04x}", c as u32)); + } + c => out.push(c), + } + } + out.push('"'); +} + +pub enum JsonValue { + Str(String), + Raw(String), +} + +pub fn json_object(fields: &[(&str, JsonValue)]) -> String { + let mut out = String::from("{"); + for (i, (key, value)) in fields.iter().enumerate() { + if i > 0 { + out.push(','); + } + push_json_string(&mut out, key); + out.push(':'); + match value { + JsonValue::Str(s) => push_json_string(&mut out, s), + JsonValue::Raw(r) => out.push_str(r), + } + } + out.push('}'); + out +} + +pub fn json_string_array(items: &[String]) -> String { + let mut out = String::from("["); + for (i, item) in items.iter().enumerate() { + if i > 0 { + out.push(','); + } + push_json_string(&mut out, item); + } + out.push(']'); + out +} + +pub fn json_u8_array(items: &[u8]) -> String { + let mut out = String::from("["); + for (i, item) in items.iter().enumerate() { + if i > 0 { + out.push(','); + } + out.push_str(&item.to_string()); + } + out.push(']'); + out +} + +pub fn json_raw_array(items: &[String]) -> String { + let mut out = String::from("["); + for (i, item) in items.iter().enumerate() { + if i > 0 { + out.push(','); + } + out.push_str(item); + } + out.push(']'); + out +} + +pub fn json_string_map(items: &std::collections::HashMap) -> String { + let mut entries: Vec<(&String, &String)> = items.iter().collect(); + entries.sort_by(|left, right| left.0.cmp(right.0)); + let mut out = String::from("{"); + for (i, (key, value)) in entries.iter().enumerate() { + if i > 0 { + out.push(','); + } + push_json_string(&mut out, key); + out.push(':'); + push_json_string(&mut out, value); + } + out.push('}'); + out +} + +#[cfg(feature = "publisher")] +mod live { + use super::{ + JsonValue, json_object, json_raw_array, json_string_array, json_string_map, json_u8_array, + }; + use livekit::participant::{ + ConnectionQuality, LocalParticipant, Participant, RemoteParticipant, + }; + use livekit::publication::{ + LocalTrackPublication, RemoteTrackPublication, SubscriptionStatus, TrackPublication, + }; + use livekit::track::{TrackKind, TrackSource}; + use livekit::{DataPacketKind, RoomEvent}; + + pub fn track_kind_str(kind: TrackKind) -> &'static str { + match kind { + TrackKind::Audio => "audio", + TrackKind::Video => "video", + } + } + + pub fn track_source_str(source: TrackSource) -> &'static str { + match source { + TrackSource::Unknown => "unknown", + TrackSource::Camera => "camera", + TrackSource::Microphone => "microphone", + TrackSource::Screenshare => "screen_share", + TrackSource::ScreenshareAudio => "screen_share_audio", + } + } + + pub fn connection_quality_str(quality: ConnectionQuality) -> &'static str { + match quality { + ConnectionQuality::Excellent => "excellent", + ConnectionQuality::Good => "good", + ConnectionQuality::Poor => "poor", + ConnectionQuality::Lost => "lost", + } + } + + fn s(value: impl Into) -> JsonValue { + JsonValue::Str(value.into()) + } + + fn b(value: bool) -> JsonValue { + JsonValue::Raw(value.to_string()) + } + + fn subscription_status_str(status: SubscriptionStatus) -> &'static str { + match status { + SubscriptionStatus::Desired => "desired", + SubscriptionStatus::Subscribed => "subscribed", + SubscriptionStatus::Unsubscribed => "unsubscribed", + } + } + + fn participant_snapshot(participant: &Participant) -> String { + json_object(&[ + ("sid", s(participant.sid().to_string())), + ("identity", s(participant.identity().to_string())), + ("name", s(participant.name())), + ]) + } + + fn push_remote_participant_fields( + fields: &mut Vec<(&'static str, JsonValue)>, + participant: &RemoteParticipant, + ) { + fields.push(("participantSid", s(participant.sid().to_string()))); + fields.push(("identity", s(participant.identity().to_string()))); + fields.push(("participantName", s(participant.name()))); + } + + fn push_local_participant_fields( + fields: &mut Vec<(&'static str, JsonValue)>, + participant: &LocalParticipant, + ) { + fields.push(("participantSid", s(participant.sid().to_string()))); + fields.push(("identity", s(participant.identity().to_string()))); + fields.push(("participantName", s(participant.name()))); + } + + fn push_participant_fields( + fields: &mut Vec<(&'static str, JsonValue)>, + participant: &Participant, + ) { + fields.push(("participantSid", s(participant.sid().to_string()))); + fields.push(("identity", s(participant.identity().to_string()))); + fields.push(("participantName", s(participant.name()))); + } + + fn push_remote_publication_fields( + fields: &mut Vec<(&'static str, JsonValue)>, + publication: &RemoteTrackPublication, + ) { + fields.push(("trackSid", s(publication.sid().to_string()))); + fields.push(("trackName", s(publication.name()))); + fields.push(("kind", s(track_kind_str(publication.kind())))); + fields.push(("source", s(track_source_str(publication.source())))); + fields.push(("muted", b(publication.is_muted()))); + fields.push(("subscribed", b(publication.is_subscribed()))); + fields.push(( + "subscriptionStatus", + s(subscription_status_str(publication.subscription_status())), + )); + } + + fn push_local_publication_fields( + fields: &mut Vec<(&'static str, JsonValue)>, + publication: &LocalTrackPublication, + ) { + fields.push(("trackSid", s(publication.sid().to_string()))); + fields.push(("trackName", s(publication.name()))); + fields.push(("kind", s(track_kind_str(publication.kind())))); + fields.push(("source", s(track_source_str(publication.source())))); + fields.push(("muted", b(publication.is_muted()))); + } + + fn push_publication_fields( + fields: &mut Vec<(&'static str, JsonValue)>, + publication: &TrackPublication, + ) { + fields.push(("trackSid", s(publication.sid().to_string()))); + fields.push(("trackName", s(publication.name()))); + fields.push(("kind", s(track_kind_str(publication.kind())))); + fields.push(("source", s(track_source_str(publication.source())))); + fields.push(("muted", b(publication.is_muted()))); + } + + fn remote_track_payload( + participant: &RemoteParticipant, + publication: &RemoteTrackPublication, + ) -> String { + let mut fields = Vec::new(); + push_remote_participant_fields(&mut fields, participant); + push_remote_publication_fields(&mut fields, publication); + json_object(&fields) + } + + const CONNECTED_ROSTER_PARTICIPANTS_MAX: usize = 1024; + const CONNECTED_ROSTER_TRACKS_PER_PARTICIPANT_MAX: usize = 16; + + fn connected_payload( + participants_with_tracks: &[(RemoteParticipant, Vec)], + ) -> String { + let participant_count = participants_with_tracks + .len() + .min(CONNECTED_ROSTER_PARTICIPANTS_MAX); + let mut entries = Vec::with_capacity(participant_count); + for (participant, publications) in participants_with_tracks + .iter() + .take(CONNECTED_ROSTER_PARTICIPANTS_MAX) + { + let track_count = publications + .len() + .min(CONNECTED_ROSTER_TRACKS_PER_PARTICIPANT_MAX); + let mut tracks = Vec::with_capacity(track_count); + for publication in publications + .iter() + .take(CONNECTED_ROSTER_TRACKS_PER_PARTICIPANT_MAX) + { + tracks.push(remote_track_payload(participant, publication)); + } + entries.push(json_object(&[ + ("sid", s(participant.sid().to_string())), + ("identity", s(participant.identity().to_string())), + ("name", s(participant.name())), + ("tracks", JsonValue::Raw(json_raw_array(&tracks))), + ])); + } + assert!(entries.len() <= CONNECTED_ROSTER_PARTICIPANTS_MAX); + json_object(&[("participants", JsonValue::Raw(json_raw_array(&entries)))]) + } + + fn local_track_payload( + participant: &LocalParticipant, + publication: &LocalTrackPublication, + ) -> String { + let mut fields = Vec::new(); + push_local_participant_fields(&mut fields, participant); + push_local_publication_fields(&mut fields, publication); + json_object(&fields) + } + + pub fn map_room_event(event: &RoomEvent) -> Option<(&'static str, String)> { + map_participant_lifecycle_room_event(event) + .or_else(|| map_participant_profile_room_event(event)) + .or_else(|| map_track_room_event(event)) + .or_else(|| map_local_track_room_event(event)) + .or_else(|| map_connection_room_event(event)) + } + + fn map_participant_lifecycle_room_event(event: &RoomEvent) -> Option<(&'static str, String)> { + match event { + RoomEvent::ParticipantConnected(p) | RoomEvent::ParticipantActive(p) => Some(( + "participantJoined", + json_object(&[ + ("sid", s(p.sid().to_string())), + ("identity", s(p.identity().to_string())), + ("name", s(p.name())), + ]), + )), + RoomEvent::ParticipantDisconnected(p) => Some(( + "participantLeft", + json_object(&[ + ("sid", s(p.sid().to_string())), + ("identity", s(p.identity().to_string())), + ("name", s(p.name())), + ]), + )), + RoomEvent::ParticipantNameChanged { + participant, + old_name, + name, + } => Some(( + "participantNameChanged", + json_object(&[ + ("sid", s(participant.sid().to_string())), + ("identity", s(participant.identity().to_string())), + ("oldName", s(old_name.to_string())), + ("name", s(name.to_string())), + ]), + )), + _ => None, + } + } + + fn map_participant_profile_room_event(event: &RoomEvent) -> Option<(&'static str, String)> { + match event { + RoomEvent::ParticipantMetadataChanged { + participant, + old_metadata, + metadata, + } => Some(( + "participantMetadataChanged", + json_object(&[ + ("sid", s(participant.sid().to_string())), + ("identity", s(participant.identity().to_string())), + ("name", s(participant.name())), + ("oldMetadata", s(old_metadata.to_string())), + ("metadata", s(metadata.to_string())), + ( + "attributes", + JsonValue::Raw(json_string_map(&participant.attributes())), + ), + ]), + )), + RoomEvent::ParticipantAttributesChanged { + participant, + changed_attributes, + } => Some(( + "participantAttributesChanged", + json_object(&[ + ("sid", s(participant.sid().to_string())), + ("identity", s(participant.identity().to_string())), + ("name", s(participant.name())), + ( + "attributes", + JsonValue::Raw(json_string_map(&participant.attributes())), + ), + ( + "changedAttributes", + JsonValue::Raw(json_string_map(changed_attributes)), + ), + ]), + )), + _ => None, + } + } + + fn map_track_room_event(event: &RoomEvent) -> Option<(&'static str, String)> { + match event { + RoomEvent::TrackSubscribed { + publication, + participant, + .. + } => Some(( + "trackSubscribed", + remote_track_payload(participant, publication), + )), + RoomEvent::TrackUnsubscribed { + publication, + participant, + .. + } => Some(( + "trackUnsubscribed", + remote_track_payload(participant, publication), + )), + RoomEvent::TrackSubscriptionFailed { + participant, + error, + track_sid, + } => { + let mut fields = Vec::new(); + push_remote_participant_fields(&mut fields, participant); + if let Some(publication) = participant.get_track_publication(track_sid) { + push_remote_publication_fields(&mut fields, &publication); + } else { + fields.push(("trackSid", s(track_sid.to_string()))); + } + fields.push(("error", s(format!("{error}")))); + Some(("trackSubscriptionFailed", json_object(&fields))) + } + RoomEvent::TrackPublished { + publication, + participant, + } => Some(( + "trackPublished", + remote_track_payload(participant, publication), + )), + RoomEvent::TrackUnpublished { + publication, + participant, + } => Some(( + "trackUnpublished", + remote_track_payload(participant, publication), + )), + RoomEvent::TrackMuted { + participant, + publication, + } => { + let mut fields = Vec::new(); + push_participant_fields(&mut fields, participant); + push_publication_fields(&mut fields, publication); + Some(("trackMuted", json_object(&fields))) + } + RoomEvent::TrackUnmuted { + participant, + publication, + } => { + let mut fields = Vec::new(); + push_participant_fields(&mut fields, participant); + push_publication_fields(&mut fields, publication); + Some(("trackUnmuted", json_object(&fields))) + } + _ => None, + } + } + + fn map_connection_room_event(event: &RoomEvent) -> Option<(&'static str, String)> { + match event { + RoomEvent::ActiveSpeakersChanged { speakers } => { + let sids: Vec = speakers.iter().map(|p| p.sid().to_string()).collect(); + let participants: Vec = speakers.iter().map(participant_snapshot).collect(); + Some(( + "activeSpeakers", + json_object(&[ + ("sids", JsonValue::Raw(json_string_array(&sids))), + ( + "participants", + JsonValue::Raw(json_raw_array(&participants)), + ), + ]), + )) + } + RoomEvent::ConnectionQualityChanged { + quality, + participant, + } => Some(( + "connectionQuality", + json_object(&[ + ("sid", s(participant.sid().to_string())), + ("identity", s(participant.identity().to_string())), + ("name", s(participant.name())), + ("quality", s(connection_quality_str(*quality))), + ]), + )), + RoomEvent::DataReceived { + payload, + topic, + kind, + participant, + } => Some(( + "dataReceived", + data_received_payload( + payload.as_ref().as_slice(), + topic.as_deref(), + kind, + participant.as_ref(), + ), + )), + RoomEvent::E2eeStateChanged { participant, state } => Some(( + "e2eeState", + json_object(&[ + ("sid", s(participant.sid().to_string())), + ("identity", s(participant.identity().to_string())), + ("name", s(participant.name())), + ("state", s(format!("{state:?}").to_lowercase())), + ]), + )), + RoomEvent::ConnectionStateChanged(state) => Some(( + "connectionState", + json_object(&[("state", s(format!("{state:?}").to_lowercase()))]), + )), + RoomEvent::Disconnected { reason } => Some(( + "disconnected", + json_object(&[("reason", s(format!("{reason:?}").to_lowercase()))]), + )), + RoomEvent::Connected { + participants_with_tracks, + } => Some(("connected", connected_payload(participants_with_tracks))), + _ => None, + } + } + + fn data_received_payload( + payload: &[u8], + topic: Option<&str>, + kind: &DataPacketKind, + participant: Option<&RemoteParticipant>, + ) -> String { + let mut fields = vec![ + ("payloadBytes", JsonValue::Raw(json_u8_array(payload))), + ( + "reliable", + JsonValue::Raw(matches!(kind, DataPacketKind::Reliable).to_string()), + ), + ( + "kind", + s(match kind { + DataPacketKind::Reliable => "reliable", + DataPacketKind::Lossy => "lossy", + }), + ), + ]; + if let Some(topic) = topic { + fields.push(("topic", s(topic.to_string()))); + } + if let Ok(payload_text) = std::str::from_utf8(payload) { + fields.push(("payloadText", s(payload_text.to_string()))); + } + if let Some(participant) = participant { + push_remote_participant_fields(&mut fields, participant); + } + json_object(&fields) + } + + fn map_local_track_room_event(event: &RoomEvent) -> Option<(&'static str, String)> { + match event { + RoomEvent::LocalTrackPublished { + publication, + participant, + .. + } => Some(( + "localTrackPublished", + local_track_payload(participant, publication), + )), + RoomEvent::LocalTrackUnpublished { + publication, + participant, + } => Some(( + "localTrackUnpublished", + local_track_payload(participant, publication), + )), + RoomEvent::LocalTrackRepublished { + previous_sid, + publication, + participant, + .. + } => { + let mut fields = Vec::new(); + push_local_participant_fields(&mut fields, participant); + fields.push(("previousTrackSid", s(previous_sid.to_string()))); + push_local_publication_fields(&mut fields, publication); + Some(("localTrackRepublished", json_object(&fields))) + } + _ => None, + } + } +} + +#[cfg(feature = "publisher")] +pub use live::{map_room_event, track_kind_str, track_source_str}; + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn escapes_quote_backslash_and_control_chars() { + let mut out = String::new(); + push_json_string(&mut out, "a\"b\\c\nd\te"); + assert_eq!(out, "\"a\\\"b\\\\c\\nd\\te\""); + + let mut ctrl = String::new(); + push_json_string(&mut ctrl, "\u{01}"); + assert_eq!(ctrl, "\"\\u0001\""); + } + + #[test] + fn leaves_plain_ascii_and_unicode_untouched() { + let mut out = String::new(); + push_json_string(&mut out, "PA_abc123"); + assert_eq!(out, "\"PA_abc123\""); + } + + #[test] + fn json_object_preserves_order_and_mixes_str_and_raw() { + let json = json_object(&[ + ("sid", JsonValue::Str("PA_1".into())), + ( + "sids", + JsonValue::Raw(json_string_array(&["PA_1".into(), "PA_2".into()])), + ), + ]); + assert_eq!(json, "{\"sid\":\"PA_1\",\"sids\":[\"PA_1\",\"PA_2\"]}"); + } + + #[cfg(feature = "publisher")] + #[test] + fn track_source_strings_match_livekit_js_sources() { + use livekit::track::TrackSource; + + assert_eq!(track_source_str(TrackSource::Camera), "camera"); + assert_eq!(track_source_str(TrackSource::Microphone), "microphone"); + assert_eq!(track_source_str(TrackSource::Screenshare), "screen_share"); + assert_eq!( + track_source_str(TrackSource::ScreenshareAudio), + "screen_share_audio" + ); + } + + #[test] + fn empty_object_and_array() { + assert_eq!(json_object(&[]), "{}"); + assert_eq!(json_string_array(&[]), "[]"); + assert_eq!(json_u8_array(&[]), "[]"); + assert_eq!(json_raw_array(&[]), "[]"); + } + + #[test] + fn json_u8_array_serializes_bytes_as_numbers() { + assert_eq!(json_u8_array(&[0, 1, 127, 255]), "[0,1,127,255]"); + } + + #[test] + fn raw_array_preserves_prebuilt_json_objects() { + let items = vec![ + json_object(&[("sid", JsonValue::Str("PA_1".into()))]), + json_object(&[("sid", JsonValue::Str("PA_2".into()))]), + ]; + assert_eq!( + json_raw_array(&items), + "[{\"sid\":\"PA_1\"},{\"sid\":\"PA_2\"}]" + ); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/src/hardware_encoder.rs b/fluxer_desktop/native/webrtc-sender/src/hardware_encoder.rs new file mode 100644 index 0000000..80ecd8d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/src/hardware_encoder.rs @@ -0,0 +1,1420 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +#![allow(dead_code)] + +use napi_derive::napi; + +const NVENC_NATIVE_INPUTS: &[&str] = &["dmabuf"]; +const VIDEOTOOLBOX_CODECS: &[&str] = &["h264", "h265"]; + +#[napi(object)] +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct HardwareEncoderCapability { + pub available: bool, + pub backend: String, + pub compiled: bool, + pub runtime: bool, + pub codecs: Vec, + pub zero_copy: bool, + pub native_inputs: Vec, + pub reason: Option, + pub detail: Option, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct NvencRuntimeProbe { + libcuda: bool, + libnvidia_encode: bool, + get_max_supported_version: bool, + driver_version_compatible: bool, + create_instance: bool, + cuda_init: bool, + cuda_device_count: Option, + cuda_device: bool, + cuda_context: bool, + checked_cuda_devices: u32, + supported_cuda_devices: u32, + open_encode_session: bool, + query_encode_guids: bool, + supports_h264: bool, + supports_h265: bool, + last_open_encode_session_status: Option, +} + +impl NvencRuntimeProbe { + fn missing() -> Self { + Self { + libcuda: false, + libnvidia_encode: false, + get_max_supported_version: false, + driver_version_compatible: false, + create_instance: false, + cuda_init: false, + cuda_device_count: None, + cuda_device: false, + cuda_context: false, + checked_cuda_devices: 0, + supported_cuda_devices: 0, + open_encode_session: false, + query_encode_guids: false, + supports_h264: false, + supports_h265: false, + last_open_encode_session_status: None, + } + } + + fn ready(self) -> bool { + self.libcuda + && self.libnvidia_encode + && self.get_max_supported_version + && self.driver_version_compatible + && self.create_instance + && self.cuda_init + && self.cuda_device + && self.cuda_context + && self.open_encode_session + && self.has_supported_codec() + } + + fn has_supported_codec(self) -> bool { + self.supports_h264 || self.supports_h265 + } + + fn supported_codecs(self) -> Vec<&'static str> { + let mut codecs = Vec::new(); + if self.supports_h264 { + codecs.push("h264"); + } + if self.supports_h265 { + codecs.push("h265"); + } + codecs + } + + fn missing_detail(self, platform: CapabilityPlatform) -> String { + let (cuda_library, encode_library) = nvenc_library_names(platform); + let mut missing = Vec::new(); + if !self.libcuda { + missing.push(cuda_library.to_string()); + } + if !self.libnvidia_encode { + missing.push(encode_library.to_string()); + } + if self.libnvidia_encode && !self.get_max_supported_version { + missing.push("NvEncodeAPIGetMaxSupportedVersion".to_string()); + } + if self.get_max_supported_version && !self.driver_version_compatible { + missing.push("NVENC driver support for the compiled SDK version".to_string()); + } + if self.libnvidia_encode && !self.create_instance { + missing.push("NvEncodeAPICreateInstance".to_string()); + } + if self.libcuda && !self.cuda_init { + missing.push("cuInit".to_string()); + } + if self.cuda_init && self.cuda_device_count == Some(0) { + missing.push("CUDA devices".to_string()); + } + if self.cuda_init && !self.cuda_device { + missing.push("CUDA device".to_string()); + } + if self.cuda_device && !self.cuda_context { + missing.push("CUDA context".to_string()); + } + if self.cuda_context && !self.open_encode_session { + missing.push(format!( + "NVENC encode session ({})", + self.open_encode_session_detail() + )); + } + if self.open_encode_session && !self.query_encode_guids { + missing.push("NVENC encode GUID query".to_string()); + } + if self.open_encode_session && self.query_encode_guids && !self.has_supported_codec() { + missing.push("NVENC H264/H265 encode GUID".to_string()); + } + format!("missing {}", missing.join(", ")) + } + + fn unavailable_reason(self) -> &'static str { + if self.get_max_supported_version && !self.driver_version_compatible { + return "outdated_driver"; + } + if self.cuda_init && self.cuda_device_count == Some(0) { + return "no_devices"; + } + if self.cuda_context && !self.open_encode_session && self.checked_cuda_devices > 0 { + return "no_supported_devices"; + } + if self.open_encode_session && !self.has_supported_codec() { + return "no_supported_codecs"; + } + "runtime_prerequisite_missing" + } + + fn open_encode_session_detail(self) -> String { + let mut parts = vec![format!( + "checked {} CUDA device{}", + self.checked_cuda_devices, + if self.checked_cuda_devices == 1 { + "" + } else { + "s" + } + )]; + if let Some(status) = self.last_open_encode_session_status { + parts.push(format!( + "last status {} ({status})", + nvenc_status_name(status) + )); + } + parts.join("; ") + } +} + +fn nvenc_status_name(status: i32) -> &'static str { + match status { + 0 => "NV_ENC_SUCCESS", + 1 => "NV_ENC_ERR_NO_ENCODE_DEVICE", + 2 => "NV_ENC_ERR_UNSUPPORTED_DEVICE", + 3 => "NV_ENC_ERR_INVALID_ENCODERDEVICE", + 4 => "NV_ENC_ERR_INVALID_DEVICE", + 5 => "NV_ENC_ERR_DEVICE_NOT_EXIST", + 6 => "NV_ENC_ERR_INVALID_PTR", + 7 => "NV_ENC_ERR_INVALID_EVENT", + 8 => "NV_ENC_ERR_INVALID_PARAM", + 9 => "NV_ENC_ERR_INVALID_CALL", + 10 => "NV_ENC_ERR_OUT_OF_MEMORY", + 11 => "NV_ENC_ERR_ENCODER_NOT_INITIALIZED", + 12 => "NV_ENC_ERR_UNSUPPORTED_PARAM", + 13 => "NV_ENC_ERR_LOCK_BUSY", + 14 => "NV_ENC_ERR_NOT_ENOUGH_BUFFER", + 15 => "NV_ENC_ERR_INVALID_VERSION", + 16 => "NV_ENC_ERR_MAP_FAILED", + 17 => "NV_ENC_ERR_NEED_MORE_INPUT", + 18 => "NV_ENC_ERR_ENCODER_BUSY", + 19 => "NV_ENC_ERR_EVENT_NOT_REGISTERD", + 20 => "NV_ENC_ERR_GENERIC", + 21 => "NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY", + 22 => "NV_ENC_ERR_UNIMPLEMENTED", + 23 => "NV_ENC_ERR_RESOURCE_REGISTER_FAILED", + 24 => "NV_ENC_ERR_RESOURCE_NOT_REGISTERED", + 25 => "NV_ENC_ERR_RESOURCE_NOT_MAPPED", + 26 => "NV_ENC_ERR_NEED_MORE_OUTPUT", + _ => "NV_ENC_ERR_UNKNOWN", + } +} + +fn videotoolbox_codec_detail(create_status: Option, prepare_status: Option) -> String { + match (create_status, prepare_status) { + (Some(0), Some(0)) => "hardware session ready".to_string(), + (Some(0), Some(status)) => format!( + "created, prepare status {} ({status})", + videotoolbox_status_name(status) + ), + (Some(status), _) => format!( + "create status {} ({status})", + videotoolbox_status_name(status) + ), + (None, _) => "probe did not run".to_string(), + } +} + +fn videotoolbox_status_name(status: i32) -> &'static str { + match status { + 0 => "noErr", + -12901 => "kVTPropertyNotSupportedErr", + -12902 => "kVTPropertyReadOnlyErr", + -12903 => "kVTParameterErr", + -12904 => "kVTInvalidSessionErr", + -12905 => "kVTAllocationFailedErr", + -12906 => "kVTPixelTransferNotSupportedErr", + -12907 => "kVTCouldNotFindVideoDecoderErr", + -12908 => "kVTCouldNotCreateInstanceErr", + -12909 => "kVTCouldNotFindVideoEncoderErr", + -12910 => "kVTVideoDecoderBadDataErr", + -12911 => "kVTVideoDecoderUnsupportedDataFormatErr", + -12912 => "kVTVideoDecoderMalfunctionErr", + -12913 => "kVTVideoEncoderMalfunctionErr", + -12914 => "kVTVideoDecoderNotAvailableNowErr", + -12915 => "kVTVideoEncoderNotAvailableNowErr", + -12916 => "kVTFormatDescriptionChangeNotSupportedErr", + _ => "OSStatus", + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +enum CapabilityPlatform { + Linux, + MacOS, + Windows, + Unsupported(&'static str), +} + +fn nvenc_library_names(platform: CapabilityPlatform) -> (&'static str, &'static str) { + match platform { + CapabilityPlatform::Windows => { + if cfg!(target_pointer_width = "32") { + ("nvcuda.dll", "nvEncodeAPI.dll") + } else { + ("nvcuda.dll", "nvEncodeAPI64.dll") + } + } + _ => ("libcuda.so.1", "libnvidia-encode.so.1"), + } +} + +fn nvenc_zero_copy_inputs(platform: CapabilityPlatform) -> &'static [&'static str] { + match platform { + CapabilityPlatform::Linux => NVENC_NATIVE_INPUTS, + _ => &[], + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct VideoToolboxRuntimeProbe { + h264_create_status: Option, + h264_prepare_status: Option, + h265_create_status: Option, + h265_prepare_status: Option, +} + +impl VideoToolboxRuntimeProbe { + fn missing() -> Self { + Self { + h264_create_status: None, + h264_prepare_status: None, + h265_create_status: None, + h265_prepare_status: None, + } + } + + fn supports_h264(self) -> bool { + self.h264_create_status == Some(0) && self.h264_prepare_status == Some(0) + } + + fn supports_h265(self) -> bool { + self.h265_create_status == Some(0) && self.h265_prepare_status == Some(0) + } + + fn ready(self) -> bool { + self.supports_h264() || self.supports_h265() + } + + fn supported_codecs(self) -> Vec<&'static str> { + let mut codecs = Vec::new(); + if self.supports_h264() { + codecs.push("h264"); + } + if self.supports_h265() { + codecs.push("h265"); + } + codecs + } + + fn missing_detail(self) -> String { + format!( + "VideoToolbox hardware encoder sessions unavailable: h264 {}; h265 {}", + videotoolbox_codec_detail(self.h264_create_status, self.h264_prepare_status), + videotoolbox_codec_detail(self.h265_create_status, self.h265_prepare_status) + ) + } +} + +#[napi] +pub fn get_hardware_encoder_capability() -> HardwareEncoderCapability { + hardware_encoder_capability() +} + +pub fn hardware_encoder_capability() -> HardwareEncoderCapability { + if cfg!(target_os = "macos") { + return describe_videotoolbox_capability(probe_macos_videotoolbox_runtime()); + } + + let platform = current_platform(); + let compiled = match platform { + CapabilityPlatform::Linux => cfg!(fluxer_linux_nvenc), + CapabilityPlatform::Windows => { + cfg!(fluxer_windows_nvenc) && cfg!(fluxer_windows_nvenc_encoder) + } + _ => false, + }; + describe_capability( + platform, + compiled, + if compiled { + probe_nvenc_runtime() + } else { + None + }, + ) +} + +pub fn require_publish_codec_runtime_support(canonical_codec: &str) -> Result<(), String> { + let capability = hardware_encoder_capability(); + require_publish_codec_runtime_support_for_capability(canonical_codec, &capability) +} + +fn require_publish_codec_runtime_support_for_capability( + canonical_codec: &str, + capability: &HardwareEncoderCapability, +) -> Result<(), String> { + if !canonical_codec.eq_ignore_ascii_case("h265") { + return Ok(()); + } + if capability.available + && capability + .codecs + .iter() + .any(|codec| codec.eq_ignore_ascii_case(canonical_codec)) + { + return Ok(()); + } + let detail = capability + .detail + .as_deref() + .or(capability.reason.as_deref()) + .unwrap_or("hardware encoder is unavailable"); + Err(format!( + "H.265 publishing requires hardware encoder support; {detail}" + )) +} + +fn current_platform() -> CapabilityPlatform { + if cfg!(target_os = "linux") { + CapabilityPlatform::Linux + } else if cfg!(target_os = "macos") { + CapabilityPlatform::MacOS + } else if cfg!(target_os = "windows") { + CapabilityPlatform::Windows + } else { + CapabilityPlatform::Unsupported(std::env::consts::OS) + } +} + +fn describe_videotoolbox_capability( + runtime_probe: Option, +) -> HardwareEncoderCapability { + let runtime_probe = runtime_probe.unwrap_or_else(VideoToolboxRuntimeProbe::missing); + let runtime = runtime_probe.ready(); + if runtime { + let codecs = runtime_probe.supported_codecs(); + return HardwareEncoderCapability { + available: true, + backend: "videotoolbox".to_string(), + compiled: true, + runtime: true, + codecs: string_vec(&codecs), + zero_copy: false, + native_inputs: Vec::new(), + reason: None, + detail: Some(format!( + "VideoToolbox can create hardware-accelerated compression sessions for {}; supported hardware-codec probe set: {}", + codecs.join(", "), + VIDEOTOOLBOX_CODECS.join(", ") + )), + }; + } + + HardwareEncoderCapability { + available: false, + backend: "none".to_string(), + compiled: cfg!(fluxer_macos_videotoolbox), + runtime: false, + codecs: Vec::new(), + zero_copy: false, + native_inputs: Vec::new(), + reason: Some("no_supported_codecs".to_string()), + detail: Some(runtime_probe.missing_detail()), + } +} + +fn describe_capability( + platform: CapabilityPlatform, + compiled: bool, + runtime_probe: Option, +) -> HardwareEncoderCapability { + match platform { + CapabilityPlatform::Unsupported(os) => HardwareEncoderCapability { + available: false, + backend: "none".to_string(), + compiled: false, + runtime: false, + codecs: Vec::new(), + zero_copy: false, + native_inputs: Vec::new(), + reason: Some("unsupported_platform".to_string()), + detail: Some(format!( + "NVENC hardware encoder capability probing is supported on Linux and Windows only; current platform is {os}" + )), + }, + CapabilityPlatform::MacOS => describe_videotoolbox_capability(None), + CapabilityPlatform::Linux | CapabilityPlatform::Windows => { + let runtime_probe = runtime_probe.unwrap_or_else(NvencRuntimeProbe::missing); + let runtime = runtime_probe.ready(); + if compiled && runtime { + let codecs = runtime_probe.supported_codecs(); + let native_inputs = nvenc_zero_copy_inputs(platform); + let native_input_detail = if native_inputs.is_empty() { + "none".to_string() + } else { + native_inputs.join(", ") + }; + let (cuda_library, encode_library) = nvenc_library_names(platform); + return HardwareEncoderCapability { + available: true, + backend: "nvenc".to_string(), + compiled: true, + runtime: true, + codecs: string_vec(&codecs), + zero_copy: !native_inputs.is_empty(), + native_inputs: string_vec(native_inputs), + reason: None, + detail: Some(format!( + "{cuda_library} and {encode_library} can open an NVENC encode session for {}; native zero-copy inputs: {}", + codecs.join(", "), + native_input_detail + )), + }; + } + + let (reason, detail) = if !compiled { + match platform { + CapabilityPlatform::Linux => ( + "nvenc_not_compiled", + "NVENC support was not compiled for this Linux target; verify the architecture is supported and the vendored CUDA Driver API header is present".to_string(), + ), + CapabilityPlatform::Windows => ( + "nvenc_not_compiled", + "Windows NVENC publishing is not compiled into this addon yet; CUDA/NVENC runtime probing alone is not enough to safely publish H.265 without codec fallback".to_string(), + ), + _ => unreachable!(), + } + } else { + ( + runtime_probe.unavailable_reason(), + runtime_probe.missing_detail(platform), + ) + }; + + HardwareEncoderCapability { + available: false, + backend: "none".to_string(), + compiled, + runtime, + codecs: Vec::new(), + zero_copy: false, + native_inputs: Vec::new(), + reason: Some(reason.to_string()), + detail: Some(detail), + } + } + } +} + +fn string_vec(values: &[&str]) -> Vec { + values.iter().map(|value| (*value).to_string()).collect() +} + +#[cfg(any(target_os = "linux", target_os = "windows"))] +fn probe_nvenc_runtime() -> Option { + nvenc_probe::probe_runtime() +} + +#[cfg(any(target_os = "linux", target_os = "windows"))] +mod nvenc_probe { + use super::{NvencRuntimeProbe, current_platform, nvenc_library_names}; + use libloading::Library; + use std::ffi::c_void; + use std::mem::transmute; + + const CUDA_SUCCESS: i32 = 0; + const NV_ENC_SUCCESS: i32 = 0; + const NV_ENC_DEVICE_TYPE_CUDA: u32 = 1; + const NVENCAPI_MAJOR_VERSION: u32 = 12; + const NVENCAPI_MINOR_VERSION: u32 = 0; + const NVENCAPI_VERSION: u32 = NVENCAPI_MAJOR_VERSION | (NVENCAPI_MINOR_VERSION << 24); + const NVENCAPI_COMPARABLE_VERSION: u32 = (NVENCAPI_MAJOR_VERSION << 4) | NVENCAPI_MINOR_VERSION; + const NV_ENCODE_API_FUNCTION_LIST_VER: u32 = nvencapi_struct_version(2); + const NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER: u32 = nvencapi_struct_version(1); + + type CuInit = unsafe extern "system" fn(u32) -> i32; + type CuDeviceGetCount = unsafe extern "system" fn(*mut i32) -> i32; + type CuDeviceGet = unsafe extern "system" fn(*mut i32, i32) -> i32; + type CuCtxCreateLegacy = unsafe extern "system" fn(*mut *mut c_void, u32, i32) -> i32; + type CuCtxCreateCuda13 = + unsafe extern "system" fn(*mut *mut c_void, *mut c_void, u32, i32) -> i32; + type CuCtxDestroy = unsafe extern "system" fn(*mut c_void) -> i32; + type NvEncodeApiGetMaxSupportedVersion = unsafe extern "system" fn(*mut u32) -> i32; + type NvEncodeApiCreateInstance = unsafe extern "system" fn(*mut NvEncodeApiFunctionList) -> i32; + type NvEncGetEncodeGuidCount = unsafe extern "system" fn(*mut c_void, *mut u32) -> i32; + type NvEncGetEncodeGuids = + unsafe extern "system" fn(*mut c_void, *mut Guid, u32, *mut u32) -> i32; + type NvEncOpenEncodeSessionEx = + unsafe extern "system" fn(*mut NvEncOpenEncodeSessionExParams, *mut *mut c_void) -> i32; + type NvEncDestroyEncoder = unsafe extern "system" fn(*mut c_void) -> i32; + + #[repr(C)] + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + struct Guid { + data1: u32, + data2: u16, + data3: u16, + data4: [u8; 8], + } + + const NV_ENC_CODEC_H264_GUID: Guid = Guid { + data1: 0x6bc82762, + data2: 0x4e63, + data3: 0x4ca4, + data4: [0xaa, 0x85, 0x1e, 0x50, 0xf3, 0x21, 0xf6, 0xbf], + }; + const NV_ENC_CODEC_HEVC_GUID: Guid = Guid { + data1: 0x790cdc88, + data2: 0x4522, + data3: 0x4d7b, + data4: [0x94, 0x25, 0xbd, 0xa9, 0x97, 0x5f, 0x76, 0x03], + }; + + #[repr(C)] + struct NvEncodeApiFunctionList { + version: u32, + reserved: u32, + functions: [*mut c_void; 41], + reserved2: [*mut c_void; 277], + } + + impl NvEncodeApiFunctionList { + fn new() -> Self { + Self { + version: NV_ENCODE_API_FUNCTION_LIST_VER, + reserved: 0, + functions: [std::ptr::null_mut(); 41], + reserved2: [std::ptr::null_mut(); 277], + } + } + + fn destroy_encoder(&self) -> Option { + let pointer = self.functions[27]; + (!pointer.is_null()).then(|| unsafe { transmute(pointer) }) + } + + fn get_encode_guid_count(&self) -> Option { + let pointer = self.functions[1]; + (!pointer.is_null()).then(|| unsafe { transmute(pointer) }) + } + + fn get_encode_guids(&self) -> Option { + let pointer = self.functions[4]; + (!pointer.is_null()).then(|| unsafe { transmute(pointer) }) + } + + fn open_encode_session_ex(&self) -> Option { + let pointer = self.functions[29]; + (!pointer.is_null()).then(|| unsafe { transmute(pointer) }) + } + } + + #[repr(C)] + struct NvEncOpenEncodeSessionExParams { + version: u32, + device_type: u32, + device: *mut c_void, + reserved: *mut c_void, + api_version: u32, + reserved1: [u32; 253], + reserved2: [*mut c_void; 64], + } + + impl NvEncOpenEncodeSessionExParams { + fn cuda(device: *mut c_void) -> Self { + Self { + version: NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER, + device_type: NV_ENC_DEVICE_TYPE_CUDA, + device, + reserved: std::ptr::null_mut(), + api_version: NVENCAPI_VERSION, + reserved1: [0; 253], + reserved2: [std::ptr::null_mut(); 64], + } + } + } + + enum CuCtxCreate { + Legacy(CuCtxCreateLegacy), + Cuda13(CuCtxCreateCuda13), + } + + impl CuCtxCreate { + unsafe fn call(&self, context: *mut *mut c_void, device: i32) -> i32 { + match self { + Self::Legacy(create) => unsafe { create(context, 0, device) }, + Self::Cuda13(create) => unsafe { create(context, std::ptr::null_mut(), 0, device) }, + } + } + } + + const fn nvencapi_struct_version(version: u32) -> u32 { + NVENCAPI_VERSION | (version << 16) | (0x7 << 28) + } + + fn load_cuda_context_create(cuda: &Library) -> Option { + let cuda_version = option_env!("FLUXER_CUDA_VERSION") + .and_then(|value| value.parse::().ok()) + .unwrap_or(0); + if cuda_version >= 13000 { + unsafe { cuda.get::(b"cuCtxCreate\0") } + .map(|symbol| CuCtxCreate::Cuda13(*symbol)) + .ok() + .or_else(|| { + unsafe { cuda.get::(b"cuCtxCreate_v2\0") } + .map(|symbol| CuCtxCreate::Legacy(*symbol)) + .ok() + }) + } else { + unsafe { cuda.get::(b"cuCtxCreate_v2\0") } + .or_else(|_| unsafe { cuda.get::(b"cuCtxCreate\0") }) + .map(|symbol| CuCtxCreate::Legacy(*symbol)) + .ok() + } + } + + fn load_cuda_context_destroy(cuda: &Library) -> Option { + unsafe { cuda.get::(b"cuCtxDestroy_v2\0") } + .or_else(|_| unsafe { cuda.get::(b"cuCtxDestroy\0") }) + .map(|symbol| *symbol) + .ok() + } + + fn query_nvenc_codecs( + api: &NvEncodeApiFunctionList, + encoder: *mut c_void, + probe: &mut NvencRuntimeProbe, + ) { + let (Some(get_count), Some(get_guids)) = + (api.get_encode_guid_count(), api.get_encode_guids()) + else { + return; + }; + let mut count = 0; + if unsafe { get_count(encoder, &mut count) } != NV_ENC_SUCCESS || count == 0 { + return; + } + let mut guids = vec![ + Guid { + data1: 0, + data2: 0, + data3: 0, + data4: [0; 8], + }; + count as usize + ]; + let mut written = 0; + if unsafe { get_guids(encoder, guids.as_mut_ptr(), count, &mut written) } != NV_ENC_SUCCESS + { + return; + } + probe.query_encode_guids = true; + for guid in guids.into_iter().take(written as usize) { + if guid == NV_ENC_CODEC_H264_GUID { + probe.supports_h264 = true; + } else if guid == NV_ENC_CODEC_HEVC_GUID { + probe.supports_h265 = true; + } + } + } + + pub(super) fn probe_runtime() -> Option { + let mut probe = NvencRuntimeProbe::missing(); + let Some((cuda, encode)) = load_nvenc_libraries(&mut probe) else { + return Some(probe); + }; + let Some(api) = create_encode_api(&encode, &mut probe) else { + return Some(probe); + }; + let Some((cu_device_get, cuda_device_count)) = init_cuda(&cuda, &mut probe) else { + return Some(probe); + }; + probe_encode_sessions(&cuda, &api, cu_device_get, cuda_device_count, &mut probe); + Some(probe) + } + + fn load_nvenc_libraries(probe: &mut NvencRuntimeProbe) -> Option<(Library, Library)> { + let (cuda_library, encode_library) = nvenc_library_names(current_platform()); + let Ok(cuda) = (unsafe { Library::new(cuda_library) }) else { + return None; + }; + probe.libcuda = true; + + let Ok(encode) = (unsafe { Library::new(encode_library) }) else { + return None; + }; + probe.libnvidia_encode = true; + Some((cuda, encode)) + } + + fn create_encode_api( + encode: &Library, + probe: &mut NvencRuntimeProbe, + ) -> Option { + let Ok(get_max_supported_version) = (unsafe { + encode.get::(b"NvEncodeAPIGetMaxSupportedVersion\0") + }) else { + return None; + }; + probe.get_max_supported_version = true; + + let mut max_supported_version = 0; + if unsafe { get_max_supported_version(&mut max_supported_version) } != NV_ENC_SUCCESS + || NVENCAPI_COMPARABLE_VERSION > max_supported_version + { + return None; + } + probe.driver_version_compatible = true; + + let Ok(create_instance) = + (unsafe { encode.get::(b"NvEncodeAPICreateInstance\0") }) + else { + return None; + }; + let mut api = NvEncodeApiFunctionList::new(); + if unsafe { create_instance(&mut api) } != NV_ENC_SUCCESS { + return None; + } + probe.create_instance = true; + Some(api) + } + + fn init_cuda(cuda: &Library, probe: &mut NvencRuntimeProbe) -> Option<(CuDeviceGet, i32)> { + let Ok(cu_init) = (unsafe { cuda.get::(b"cuInit\0") }) else { + return None; + }; + if unsafe { cu_init(0) } != CUDA_SUCCESS { + return None; + } + probe.cuda_init = true; + + let Ok(cu_device_get) = (unsafe { cuda.get::(b"cuDeviceGet\0") }) else { + return None; + }; + let cu_device_get = *cu_device_get; + + let Ok(cu_device_get_count) = + (unsafe { cuda.get::(b"cuDeviceGetCount\0") }) + else { + return None; + }; + let mut cuda_device_count = 0; + if unsafe { cu_device_get_count(&mut cuda_device_count) } != CUDA_SUCCESS { + return None; + } + probe.cuda_device_count = Some(cuda_device_count); + if cuda_device_count <= 0 { + return None; + } + Some((cu_device_get, cuda_device_count)) + } + + fn probe_encode_sessions( + cuda: &Library, + api: &NvEncodeApiFunctionList, + cu_device_get: CuDeviceGet, + cuda_device_count: i32, + probe: &mut NvencRuntimeProbe, + ) { + let Some(cu_ctx_create) = load_cuda_context_create(cuda) else { + return; + }; + let Some(cu_ctx_destroy) = load_cuda_context_destroy(cuda) else { + return; + }; + let Some(open_encode_session_ex) = api.open_encode_session_ex() else { + return; + }; + let Some(destroy_encoder) = api.destroy_encoder() else { + return; + }; + + for ordinal in 0..cuda_device_count { + let mut cuda_device = 0; + if unsafe { cu_device_get(&mut cuda_device, ordinal) } != CUDA_SUCCESS { + continue; + } + probe.cuda_device = true; + + let mut cuda_context = std::ptr::null_mut(); + if unsafe { cu_ctx_create.call(&mut cuda_context, cuda_device) } != CUDA_SUCCESS + || cuda_context.is_null() + { + continue; + } + probe.cuda_context = true; + probe.checked_cuda_devices += 1; + + let mut params = NvEncOpenEncodeSessionExParams::cuda(cuda_context); + let mut encoder = std::ptr::null_mut(); + let status = unsafe { open_encode_session_ex(&mut params, &mut encoder) }; + probe.last_open_encode_session_status = Some(status); + if status == NV_ENC_SUCCESS && !encoder.is_null() { + query_nvenc_codecs(api, encoder, probe); + let _ = unsafe { destroy_encoder(encoder) }; + probe.supported_cuda_devices += 1; + probe.open_encode_session = true; + } + + let _ = unsafe { cu_ctx_destroy(cuda_context) }; + + if probe.open_encode_session && probe.has_supported_codec() { + break; + } + } + } +} + +#[cfg(not(any(target_os = "linux", target_os = "windows")))] +fn probe_nvenc_runtime() -> Option { + None +} + +#[cfg(target_os = "macos")] +fn probe_macos_videotoolbox_runtime() -> Option { + use std::ffi::c_void; + + type CFAllocatorRef = *const c_void; + type CFDictionaryRef = *const c_void; + type CFIndex = isize; + type CFStringRef = *const c_void; + type CFTypeRef = *const c_void; + type CMVideoCodecType = u32; + type OSStatus = i32; + type VTCompressionSessionRef = *mut c_void; + type VTCompressionOutputCallback = Option< + unsafe extern "C" fn( + output_callback_refcon: *mut c_void, + source_frame_refcon: *mut c_void, + status: OSStatus, + info_flags: u32, + sample_buffer: *mut c_void, + ), + >; + + const K_CM_VIDEO_CODEC_TYPE_H264: CMVideoCodecType = 0x6176_6331; + const K_CM_VIDEO_CODEC_TYPE_HEVC: CMVideoCodecType = 0x6876_6331; + + unsafe extern "C" { + static kCFBooleanTrue: CFTypeRef; + static kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder: CFStringRef; + + fn CFDictionaryCreate( + allocator: CFAllocatorRef, + keys: *const *const c_void, + values: *const *const c_void, + num_values: CFIndex, + key_callbacks: *const c_void, + value_callbacks: *const c_void, + ) -> CFDictionaryRef; + fn CFRelease(value: CFTypeRef); + fn VTCompressionSessionCreate( + allocator: CFAllocatorRef, + width: i32, + height: i32, + codec_type: CMVideoCodecType, + encoder_specification: CFDictionaryRef, + image_buffer_attributes: CFDictionaryRef, + compressed_data_allocator: CFAllocatorRef, + output_callback: VTCompressionOutputCallback, + output_callback_refcon: *mut c_void, + compression_session_out: *mut VTCompressionSessionRef, + ) -> OSStatus; + fn VTCompressionSessionInvalidate(session: VTCompressionSessionRef); + fn VTCompressionSessionPrepareToEncodeFrames(session: VTCompressionSessionRef) -> OSStatus; + } + + unsafe fn hardware_required_dictionary() -> CFDictionaryRef { + let key = unsafe { + kVTVideoEncoderSpecification_RequireHardwareAcceleratedVideoEncoder as *const c_void + }; + let value = unsafe { kCFBooleanTrue as *const c_void }; + let keys = [key]; + let values = [value]; + unsafe { + CFDictionaryCreate( + std::ptr::null(), + keys.as_ptr(), + values.as_ptr(), + 1, + std::ptr::null(), + std::ptr::null(), + ) + } + } + + unsafe fn probe_codec(codec_type: CMVideoCodecType) -> (Option, Option) { + let encoder_specification = unsafe { hardware_required_dictionary() }; + let mut session = std::ptr::null_mut(); + let create_status = unsafe { + VTCompressionSessionCreate( + std::ptr::null(), + 1280, + 720, + codec_type, + encoder_specification, + std::ptr::null(), + std::ptr::null(), + None, + std::ptr::null_mut(), + &mut session, + ) + }; + if !encoder_specification.is_null() { + unsafe { CFRelease(encoder_specification as CFTypeRef) }; + } + if create_status != 0 || session.is_null() { + return (Some(create_status), None); + } + + let prepare_status = unsafe { VTCompressionSessionPrepareToEncodeFrames(session) }; + unsafe { + VTCompressionSessionInvalidate(session); + CFRelease(session as CFTypeRef); + } + (Some(create_status), Some(prepare_status)) + } + + let (h264_create_status, h264_prepare_status) = + unsafe { probe_codec(K_CM_VIDEO_CODEC_TYPE_H264) }; + let (h265_create_status, h265_prepare_status) = + unsafe { probe_codec(K_CM_VIDEO_CODEC_TYPE_HEVC) }; + + Some(VideoToolboxRuntimeProbe { + h264_create_status, + h264_prepare_status, + h265_create_status, + h265_prepare_status, + }) +} + +#[cfg(not(target_os = "macos"))] +fn probe_macos_videotoolbox_runtime() -> Option { + None +} + +#[cfg(test)] +mod tests { + use super::*; + + fn runtime_ready() -> NvencRuntimeProbe { + NvencRuntimeProbe { + libcuda: true, + libnvidia_encode: true, + get_max_supported_version: true, + driver_version_compatible: true, + create_instance: true, + cuda_init: true, + cuda_device_count: Some(1), + cuda_device: true, + cuda_context: true, + checked_cuda_devices: 1, + supported_cuda_devices: 1, + open_encode_session: true, + query_encode_guids: true, + supports_h264: true, + supports_h265: true, + last_open_encode_session_status: Some(0), + } + } + + fn videotoolbox_ready() -> VideoToolboxRuntimeProbe { + VideoToolboxRuntimeProbe { + h264_create_status: Some(0), + h264_prepare_status: Some(0), + h265_create_status: Some(0), + h265_prepare_status: Some(0), + } + } + + #[test] + fn reports_available_nvenc_when_compiled_and_runtime_ready() { + let capability = + describe_capability(CapabilityPlatform::Linux, true, Some(runtime_ready())); + + assert!(capability.available); + assert_eq!(capability.backend, "nvenc"); + assert!(capability.compiled); + assert!(capability.runtime); + assert_eq!(capability.codecs, vec!["h264", "h265"]); + assert!(capability.zero_copy); + assert_eq!(capability.native_inputs, vec!["dmabuf"]); + assert_eq!(capability.reason, None); + assert!( + capability + .detail + .as_deref() + .unwrap_or_default() + .contains("h264, h265") + ); + } + + #[test] + fn reports_h264_only_when_hevc_guid_is_absent() { + let capability = describe_capability( + CapabilityPlatform::Linux, + true, + Some(NvencRuntimeProbe { + supports_h265: false, + ..runtime_ready() + }), + ); + + assert!(capability.available); + assert_eq!(capability.codecs, vec!["h264"]); + assert!(capability.zero_copy); + } + + #[test] + fn reports_windows_nvenc_without_linux_zero_copy_inputs() { + let capability = + describe_capability(CapabilityPlatform::Windows, true, Some(runtime_ready())); + + assert!(capability.available); + assert_eq!(capability.backend, "nvenc"); + assert!(capability.compiled); + assert!(capability.runtime); + assert_eq!(capability.codecs, vec!["h264", "h265"]); + assert!(!capability.zero_copy); + assert_eq!(capability.native_inputs, Vec::::new()); + assert_eq!(capability.reason, None); + let detail = capability.detail.as_deref().unwrap_or_default(); + assert!(detail.contains("nvcuda.dll"), "{detail}"); + assert!(detail.contains("native zero-copy inputs: none"), "{detail}"); + } + + #[test] + fn reports_windows_nvenc_runtime_dll_names_when_missing() { + let capability = describe_capability(CapabilityPlatform::Windows, true, None); + + assert!(!capability.available); + assert_eq!( + capability.reason.as_deref(), + Some("runtime_prerequisite_missing") + ); + let detail = capability.detail.as_deref().unwrap_or_default(); + assert!(detail.contains("nvcuda.dll"), "{detail}"); + if cfg!(target_pointer_width = "32") { + assert!(detail.contains("nvEncodeAPI.dll"), "{detail}"); + } else { + assert!(detail.contains("nvEncodeAPI64.dll"), "{detail}"); + } + } + + #[test] + fn reports_windows_nvenc_publish_backend_not_compiled() { + let capability = describe_capability(CapabilityPlatform::Windows, false, None); + + assert!(!capability.available); + assert_eq!(capability.reason.as_deref(), Some("nvenc_not_compiled")); + let detail = capability.detail.as_deref().unwrap_or_default(); + assert!( + detail.contains("Windows NVENC publishing is not compiled"), + "{detail}" + ); + let error = + require_publish_codec_runtime_support_for_capability("h265", &capability).unwrap_err(); + assert!( + error.contains("Windows NVENC publishing is not compiled"), + "{error}" + ); + } + + #[test] + fn publish_codec_runtime_gate_only_requires_hardware_for_h265() { + let unavailable = describe_capability(CapabilityPlatform::Linux, false, None); + + assert!(require_publish_codec_runtime_support_for_capability("h264", &unavailable).is_ok()); + assert!(require_publish_codec_runtime_support_for_capability("vp8", &unavailable).is_ok()); + let error = + require_publish_codec_runtime_support_for_capability("h265", &unavailable).unwrap_err(); + assert!(error.contains("H.265 publishing requires hardware encoder support")); + assert!(error.contains("vendored CUDA Driver API header")); + } + + #[test] + fn publish_codec_runtime_gate_accepts_h265_when_reported_available() { + let capability = describe_videotoolbox_capability(Some(videotoolbox_ready())); + + assert!(require_publish_codec_runtime_support_for_capability("h265", &capability).is_ok()); + assert!(require_publish_codec_runtime_support_for_capability("H265", &capability).is_ok()); + } + + #[test] + fn reports_no_supported_codecs_when_guid_query_finds_no_fluxer_codecs() { + let capability = describe_capability( + CapabilityPlatform::Linux, + true, + Some(NvencRuntimeProbe { + supports_h264: false, + supports_h265: false, + ..runtime_ready() + }), + ); + + assert!(!capability.available); + assert_eq!(capability.reason.as_deref(), Some("no_supported_codecs")); + assert_eq!(capability.codecs, Vec::::new()); + assert!( + capability + .detail + .as_deref() + .unwrap_or_default() + .contains("NVENC H264/H265 encode GUID") + ); + } + + #[test] + fn reports_not_compiled_before_runtime_missing() { + let capability = describe_capability( + CapabilityPlatform::Linux, + false, + Some(NvencRuntimeProbe { + libcuda: false, + libnvidia_encode: false, + get_max_supported_version: false, + driver_version_compatible: false, + create_instance: false, + cuda_init: false, + cuda_device_count: None, + cuda_device: false, + cuda_context: false, + checked_cuda_devices: 0, + supported_cuda_devices: 0, + open_encode_session: false, + query_encode_guids: false, + supports_h264: false, + supports_h265: false, + last_open_encode_session_status: None, + }), + ); + + assert!(!capability.available); + assert_eq!(capability.backend, "none"); + assert!(!capability.compiled); + assert!(!capability.runtime); + assert_eq!(capability.codecs, Vec::::new()); + assert!(!capability.zero_copy); + assert_eq!(capability.native_inputs, Vec::::new()); + assert_eq!(capability.reason.as_deref(), Some("nvenc_not_compiled")); + assert!( + capability + .detail + .as_deref() + .unwrap_or_default() + .contains("vendored CUDA Driver API header") + ); + } + + #[test] + fn reports_missing_runtime_prerequisites_when_compiled() { + let capability = describe_capability( + CapabilityPlatform::Linux, + true, + Some(NvencRuntimeProbe { + libcuda: true, + libnvidia_encode: true, + get_max_supported_version: true, + driver_version_compatible: true, + create_instance: true, + cuda_init: true, + cuda_device_count: Some(1), + cuda_device: true, + cuda_context: true, + checked_cuda_devices: 1, + supported_cuda_devices: 0, + open_encode_session: false, + query_encode_guids: false, + supports_h264: false, + supports_h265: false, + last_open_encode_session_status: Some(2), + }), + ); + + assert!(!capability.available); + assert_eq!(capability.backend, "none"); + assert!(capability.compiled); + assert!(!capability.runtime); + assert!(!capability.zero_copy); + assert_eq!(capability.native_inputs, Vec::::new()); + assert_eq!(capability.reason.as_deref(), Some("no_supported_devices")); + assert!( + capability + .detail + .as_deref() + .unwrap_or_default() + .contains("NVENC encode session") + ); + assert!( + capability + .detail + .as_deref() + .unwrap_or_default() + .contains("NV_ENC_ERR_UNSUPPORTED_DEVICE") + ); + } + + #[test] + fn reports_missing_runtime_prerequisites_when_probe_is_absent() { + let capability = describe_capability(CapabilityPlatform::Linux, true, None); + + assert!(!capability.available); + assert_eq!(capability.backend, "none"); + assert!(capability.compiled); + assert!(!capability.runtime); + assert!(!capability.zero_copy); + assert_eq!(capability.native_inputs, Vec::::new()); + assert_eq!( + capability.reason.as_deref(), + Some("runtime_prerequisite_missing") + ); + let detail = capability.detail.as_deref().unwrap_or_default(); + assert!(detail.contains("libcuda.so.1"), "{detail}"); + assert!(detail.contains("libnvidia-encode.so.1"), "{detail}"); + } + + #[test] + fn reports_driver_sdk_incompatibility_before_encode_session() { + let capability = describe_capability( + CapabilityPlatform::Linux, + true, + Some(NvencRuntimeProbe { + driver_version_compatible: false, + create_instance: false, + cuda_init: false, + cuda_device: false, + cuda_context: false, + open_encode_session: false, + ..runtime_ready() + }), + ); + + assert!(!capability.available); + assert_eq!(capability.backend, "none"); + assert!(capability.compiled); + assert!(!capability.runtime); + assert!(!capability.zero_copy); + assert_eq!(capability.native_inputs, Vec::::new()); + assert_eq!(capability.reason.as_deref(), Some("outdated_driver")); + let detail = capability.detail.as_deref().unwrap_or_default(); + assert!( + detail.contains("NVENC driver support for the compiled SDK version"), + "{detail}" + ); + assert!(!detail.contains("NVENC encode session"), "{detail}"); + } + + #[test] + fn reports_no_cuda_devices_using_obs_style_reason() { + let capability = describe_capability( + CapabilityPlatform::Linux, + true, + Some(NvencRuntimeProbe { + cuda_device_count: Some(0), + cuda_device: false, + cuda_context: false, + open_encode_session: false, + ..runtime_ready() + }), + ); + + assert!(!capability.available); + assert_eq!(capability.reason.as_deref(), Some("no_devices")); + assert!( + capability + .detail + .as_deref() + .unwrap_or_default() + .contains("CUDA devices") + ); + } + + #[test] + fn reports_available_videotoolbox_with_h264_and_h265() { + let capability = describe_videotoolbox_capability(Some(videotoolbox_ready())); + + assert!(capability.available); + assert_eq!(capability.backend, "videotoolbox"); + assert!(capability.compiled); + assert!(capability.runtime); + assert_eq!(capability.codecs, vec!["h264", "h265"]); + assert!(!capability.zero_copy); + assert_eq!(capability.native_inputs, Vec::::new()); + assert_eq!(capability.reason, None); + assert!( + capability + .detail + .as_deref() + .unwrap_or_default() + .contains("h264, h265") + ); + } + + #[test] + fn reports_videotoolbox_h264_only_when_hevc_session_is_unavailable() { + let capability = describe_videotoolbox_capability(Some(VideoToolboxRuntimeProbe { + h265_create_status: Some(-12909), + h265_prepare_status: None, + ..videotoolbox_ready() + })); + + assert!(capability.available); + assert_eq!(capability.backend, "videotoolbox"); + assert_eq!(capability.codecs, vec!["h264"]); + assert_eq!(capability.reason, None); + } + + #[test] + fn reports_videotoolbox_unavailable_with_status_details() { + let capability = describe_videotoolbox_capability(Some(VideoToolboxRuntimeProbe { + h264_create_status: Some(-12909), + h264_prepare_status: None, + h265_create_status: Some(-12915), + h265_prepare_status: None, + })); + + assert!(!capability.available); + assert_eq!(capability.backend, "none"); + assert!(!capability.runtime); + assert_eq!(capability.codecs, Vec::::new()); + assert_eq!(capability.reason.as_deref(), Some("no_supported_codecs")); + let detail = capability.detail.as_deref().unwrap_or_default(); + assert!( + detail.contains("kVTCouldNotFindVideoEncoderErr"), + "{detail}" + ); + assert!( + detail.contains("kVTVideoEncoderNotAvailableNowErr"), + "{detail}" + ); + } + + #[test] + fn reports_unsupported_platforms_clearly() { + let capability = describe_capability( + CapabilityPlatform::Unsupported("macos"), + true, + Some(runtime_ready()), + ); + + assert!(!capability.available); + assert_eq!(capability.backend, "none"); + assert!(!capability.compiled); + assert!(!capability.runtime); + assert!(!capability.zero_copy); + assert_eq!(capability.native_inputs, Vec::::new()); + assert_eq!(capability.reason.as_deref(), Some("unsupported_platform")); + assert!( + capability + .detail + .as_deref() + .unwrap_or_default() + .contains("macos") + ); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/src/inbound_forwarder.rs b/fluxer_desktop/native/webrtc-sender/src/inbound_forwarder.rs new file mode 100644 index 0000000..fa48cad --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/src/inbound_forwarder.rs @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use futures_core::Stream; +use napi::tokio; +use napi::tokio::task::AbortHandle; +use parking_lot::Mutex; +use std::collections::HashMap; +use std::future::poll_fn; +use std::pin::pin; + +pub const INBOUND_FORWARDERS_MAX: usize = 512; + +struct ForwarderEntry { + participant_sid: String, + handle: AbortHandle, +} + +pub struct InboundForwarderRegistry { + entries: Mutex>, +} + +impl InboundForwarderRegistry { + pub fn new() -> Self { + Self { + entries: Mutex::new(HashMap::new()), + } + } + + #[cfg(test)] + pub fn len(&self) -> usize { + self.entries.lock().len() + } + + #[cfg(test)] + pub fn contains(&self, track_sid: &str) -> bool { + self.entries.lock().contains_key(track_sid) + } + + pub fn register(&self, track_sid: &str, participant_sid: &str, handle: AbortHandle) -> bool { + if track_sid.is_empty() { + handle.abort(); + return false; + } + let mut entries = self.entries.lock(); + if let Some(previous) = entries.remove(track_sid) { + previous.handle.abort(); + } + if entries.len() >= INBOUND_FORWARDERS_MAX { + drop(entries); + handle.abort(); + eprintln!( + "webrtc-sender: inbound forwarder registry at cap {INBOUND_FORWARDERS_MAX}; \ + refusing forwarder for track {track_sid}" + ); + return false; + } + entries.insert( + track_sid.to_string(), + ForwarderEntry { + participant_sid: participant_sid.to_string(), + handle, + }, + ); + assert!(entries.len() <= INBOUND_FORWARDERS_MAX); + true + } + + pub fn cancel(&self, track_sid: &str) { + let removed = self.entries.lock().remove(track_sid); + if let Some(entry) = removed { + entry.handle.abort(); + } + } + + pub fn cancel_for_participant(&self, participant_sid: &str) { + let aborted: Vec = { + let mut entries = self.entries.lock(); + let matching: Vec = entries + .iter() + .filter(|(_, entry)| entry.participant_sid == participant_sid) + .map(|(track_sid, _)| track_sid.clone()) + .collect(); + matching + .into_iter() + .filter_map(|track_sid| entries.remove(&track_sid)) + .collect() + }; + for entry in aborted { + entry.handle.abort(); + } + } + + pub fn clear(&self) { + let drained: Vec = { + let mut entries = self.entries.lock(); + entries.drain().map(|(_, entry)| entry).collect() + }; + for entry in drained { + entry.handle.abort(); + } + } +} + +impl Default for InboundForwarderRegistry { + fn default() -> Self { + Self::new() + } +} + +pub fn spawn_drain_forwarder(stream: S, mut on_item: F) -> AbortHandle +where + S: Stream + Send + 'static, + S::Item: Send, + F: FnMut(S::Item) + Send + 'static, +{ + let task = tokio::spawn(async move { + let mut stream = pin!(stream); + loop { + let item = poll_fn(|cx| stream.as_mut().poll_next(cx)).await; + match item { + Some(item) => on_item(item), + None => return, + } + } + }); + task.abort_handle() +} + +#[cfg(test)] +mod tests { + use super::*; + use std::collections::VecDeque; + use std::pin::Pin; + use std::sync::Arc; + use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; + use std::task::{Context, Poll}; + use tokio::runtime::Builder; + use tokio::sync::Notify; + + struct CloseSignal { + closed: AtomicBool, + notify: Notify, + } + + impl CloseSignal { + fn new() -> Arc { + Arc::new(Self { + closed: AtomicBool::new(false), + notify: Notify::new(), + }) + } + + fn is_closed(&self) -> bool { + self.closed.load(Ordering::SeqCst) + } + + fn fire(&self) { + self.closed.store(true, Ordering::SeqCst); + self.notify.notify_one(); + } + + async fn wait(&self) { + if self.is_closed() { + return; + } + self.notify.notified().await; + assert!(self.is_closed()); + } + } + + struct FakeVideoStream { + signal: Arc, + } + + impl Stream for FakeVideoStream { + type Item = u64; + + fn poll_next(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + Poll::Pending + } + } + + impl Drop for FakeVideoStream { + fn drop(&mut self) { + self.signal.fire(); + } + } + + struct CountedStream { + items: VecDeque, + count: Arc, + done: Arc, + } + + impl Stream for CountedStream { + type Item = u64; + + fn poll_next(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll> { + match self.items.pop_front() { + Some(item) => { + self.count.fetch_add(1, Ordering::SeqCst); + Poll::Ready(Some(item)) + } + None => { + self.done.notify_one(); + Poll::Ready(None) + } + } + } + } + + fn runtime() -> tokio::runtime::Runtime { + Builder::new_multi_thread() + .worker_threads(1) + .build() + .expect("tokio runtime") + } + + fn dispatch_track_subscribed( + registry: &InboundForwarderRegistry, + track_sid: &str, + participant_sid: &str, + ) -> Arc { + let (signal, registered) = dispatch_with_outcome(registry, track_sid, participant_sid); + assert!(registered); + signal + } + + fn dispatch_with_outcome( + registry: &InboundForwarderRegistry, + track_sid: &str, + participant_sid: &str, + ) -> (Arc, bool) { + let signal = CloseSignal::new(); + let stream = FakeVideoStream { + signal: signal.clone(), + }; + let handle = spawn_drain_forwarder(stream, |_frame| {}); + let registered = registry.register(track_sid, participant_sid, handle); + (signal, registered) + } + + #[test] + fn teardown_symmetry_closes_the_stream() { + let runtime = runtime(); + runtime.block_on(async { + let registry = InboundForwarderRegistry::new(); + let signal = dispatch_track_subscribed(®istry, "TR_x", "PA_one"); + + assert_eq!(registry.len(), 1); + assert!(!signal.is_closed()); + + registry.cancel("TR_x"); + assert_eq!(registry.len(), 0); + + signal.wait().await; + assert!(signal.is_closed()); + }); + } + + #[test] + fn repeated_subscribe_unsubscribe_never_accumulates() { + let runtime = runtime(); + runtime.block_on(async { + let registry = InboundForwarderRegistry::new(); + let cycles = 32usize; + let mut signals = Vec::with_capacity(cycles); + + for _ in 0..cycles { + let signal = dispatch_track_subscribed(®istry, "TR_x", "PA_one"); + assert_eq!(registry.len(), 1); + registry.cancel("TR_x"); + assert_eq!(registry.len(), 0); + signal.wait().await; + signals.push(signal); + } + + assert_eq!(registry.len(), 0); + let closed_count = signals.iter().filter(|signal| signal.is_closed()).count(); + assert_eq!(closed_count, cycles); + }); + } + + #[test] + fn double_subscribe_keeps_one_live_forwarder() { + let runtime = runtime(); + runtime.block_on(async { + let registry = InboundForwarderRegistry::new(); + let first_signal = dispatch_track_subscribed(®istry, "TR_x", "PA_one"); + let second_signal = dispatch_track_subscribed(®istry, "TR_x", "PA_one"); + + assert_eq!(registry.len(), 1); + first_signal.wait().await; + assert!(first_signal.is_closed()); + assert!(!second_signal.is_closed()); + + registry.cancel("TR_x"); + assert_eq!(registry.len(), 0); + second_signal.wait().await; + assert!(second_signal.is_closed()); + }); + } + + #[test] + fn participant_disconnect_tears_down_all_forwarders() { + let runtime = runtime(); + runtime.block_on(async { + let registry = InboundForwarderRegistry::new(); + let video_signal = dispatch_track_subscribed(®istry, "TR_video", "PA_one"); + let audio_signal = dispatch_track_subscribed(®istry, "TR_audio", "PA_one"); + let other_signal = dispatch_track_subscribed(®istry, "TR_other", "PA_two"); + assert_eq!(registry.len(), 3); + + registry.cancel_for_participant("PA_one"); + assert_eq!(registry.len(), 1); + assert!(registry.contains("TR_other")); + + video_signal.wait().await; + audio_signal.wait().await; + assert!(video_signal.is_closed()); + assert!(audio_signal.is_closed()); + assert!(!other_signal.is_closed()); + + registry.clear(); + assert_eq!(registry.len(), 0); + other_signal.wait().await; + assert!(other_signal.is_closed()); + }); + } + + #[test] + fn clear_tears_down_every_forwarder() { + let runtime = runtime(); + runtime.block_on(async { + let registry = InboundForwarderRegistry::new(); + let mut signals = Vec::new(); + for index in 0..8 { + let track_sid = format!("TR_{index}"); + signals.push(dispatch_track_subscribed(®istry, &track_sid, "PA_one")); + } + assert_eq!(registry.len(), 8); + + registry.clear(); + assert_eq!(registry.len(), 0); + + for signal in &signals { + signal.wait().await; + } + assert!(signals.iter().all(|signal| signal.is_closed())); + }); + } + + #[test] + fn drain_forwarder_invokes_callback_per_item() { + let runtime = runtime(); + runtime.block_on(async { + let count = Arc::new(AtomicUsize::new(0)); + let polled = Arc::new(AtomicUsize::new(0)); + let polled_in_task = polled.clone(); + let done = Arc::new(Notify::new()); + let stream = CountedStream { + items: VecDeque::from(vec![1u64, 2, 3, 4]), + count: count.clone(), + done: done.clone(), + }; + let _handle = spawn_drain_forwarder(stream, move |_item| { + polled_in_task.fetch_add(1, Ordering::SeqCst); + }); + done.notified().await; + + assert_eq!(count.load(Ordering::SeqCst), 4); + assert_eq!(polled.load(Ordering::SeqCst), 4); + }); + } + + #[test] + fn register_refuses_and_closes_forwarder_at_cap() { + let runtime = runtime(); + runtime.block_on(async { + let registry = InboundForwarderRegistry::new(); + for index in 0..INBOUND_FORWARDERS_MAX { + let track_sid = format!("TR_{index}"); + let (_signal, registered) = dispatch_with_outcome(®istry, &track_sid, "PA_one"); + assert!(registered); + } + assert_eq!(registry.len(), INBOUND_FORWARDERS_MAX); + + let (overflow_signal, registered) = + dispatch_with_outcome(®istry, "TR_overflow", "PA_one"); + assert!(!registered); + assert_eq!(registry.len(), INBOUND_FORWARDERS_MAX); + assert!(!registry.contains("TR_overflow")); + + overflow_signal.wait().await; + assert!(overflow_signal.is_closed()); + + registry.clear(); + assert_eq!(registry.len(), 0); + }); + } + + #[test] + fn register_refuses_and_closes_forwarder_for_empty_sid() { + let runtime = runtime(); + runtime.block_on(async { + let registry = InboundForwarderRegistry::new(); + let (signal, registered) = dispatch_with_outcome(®istry, "", "PA_one"); + assert!(!registered); + assert_eq!(registry.len(), 0); + + signal.wait().await; + assert!(signal.is_closed()); + }); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/src/lib.rs b/fluxer_desktop/native/webrtc-sender/src/lib.rs new file mode 100644 index 0000000..7ff6bad --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/src/lib.rs @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![allow(unsafe_op_in_unsafe_fn)] +#![cfg_attr( + not(all(feature = "publisher", feature = "camera-native")), + allow(dead_code) +)] + +mod audio; +mod bridge_version; +mod camera; +mod camera_background; +mod config; +mod deep_filter; +mod events; +mod hardware_encoder; +mod inbound_forwarder; +mod mask_refine; +mod native_camera; +mod person_segmentation; +mod send_control; +mod speaking; +mod stats; +mod texture_source; +mod yuv; + +#[cfg(feature = "publisher")] +mod engine; + +#[cfg(feature = "bench-internals")] +pub mod bench_internals { + pub use crate::audio::DEEP_FILTER_NOISE_REDUCTION_LEVEL_MAX; + pub use crate::deep_filter::{DEEP_FILTER_FRAME_SAMPLES, DeepFilterProcessor}; + pub use crate::mask_refine::MaskRefiner; + + pub struct BlurScratch(crate::camera_background::BlurScratch); + + impl BlurScratch { + pub fn new(width: usize, height: usize) -> Self { + Self(crate::camera_background::BlurScratch::new(width, height)) + } + } + + pub fn blur_plane_masked( + plane: &mut [u8], + width: usize, + height: usize, + mask: &[u8], + radius_pass: usize, + scratch: &mut BlurScratch, + ) { + let mask = crate::camera_background::plane_mask(mask, width, 1); + crate::camera_background::blur_plane_masked( + plane, + width, + height, + mask, + radius_pass, + &mut scratch.0, + ); + } + + pub fn composite_masked_plane( + plane: &mut [u8], + background: &[u8], + width: usize, + height: usize, + mask: &[u8], + ) { + let mask = crate::camera_background::plane_mask(mask, width, 1); + crate::camera_background::composite_masked_plane(plane, background, width, height, mask); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/src/mask_refine.rs b/fluxer_desktop/native/webrtc-sender/src/mask_refine.rs new file mode 100644 index 0000000..d0c4f32 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/src/mask_refine.rs @@ -0,0 +1,626 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const FRAME_EDGE_MAX: usize = 8192; +const MASK_REFINE_DOWNSAMPLE: usize = 4; +const GUIDED_FILTER_RADIUS_LOW: usize = 4; +const GUIDED_FILTER_EPSILON: f32 = 1e-4; +const TEMPORAL_COMBINE_RATIO: f32 = 0.7; +const TEMPORAL_UNCERTAINTY_C1: f32 = 5.68842; +const TEMPORAL_UNCERTAINTY_C2: f32 = -0.748699; +const TEMPORAL_UNCERTAINTY_C3: f32 = -57.8051; +const TEMPORAL_UNCERTAINTY_C4: f32 = 291.309; +const TEMPORAL_UNCERTAINTY_C5: f32 = -624.717; +const SHAPE_SMOOTHSTEP_EDGE_LOW: f32 = 0.55; +const SHAPE_SMOOTHSTEP_EDGE_HIGH: f32 = 0.85; +const LUT_LEN: usize = 256; + +pub struct MaskRefiner { + width: usize, + height: usize, + low_width: usize, + low_height: usize, + previous_mask: Vec, + previous_mask_valid: bool, + temporal_weight_lut: [u16; LUT_LEN], + shape_lut: [u8; LUT_LEN], + column_fixed: Vec, + guide_low: Vec, + mask_low: Vec, + mean_guide: Vec, + mean_mask: Vec, + corr_guide_guide: Vec, + corr_guide_mask: Vec, + coeff_a: Vec, + coeff_b: Vec, + scratch: Vec, +} + +impl MaskRefiner { + pub fn new(width: usize, height: usize) -> Self { + assert!(width >= 2); + assert!(height >= 2); + assert!(width <= FRAME_EDGE_MAX); + assert!(height <= FRAME_EDGE_MAX); + let low_width = (width / MASK_REFINE_DOWNSAMPLE).max(1); + let low_height = (height / MASK_REFINE_DOWNSAMPLE).max(1); + let low_len = low_width * low_height; + let mut column_fixed = vec![0u32; width]; + for (x, slot) in column_fixed.iter_mut().enumerate() { + *slot = bilinear_fixed_coord(x, width, low_width); + } + Self { + width, + height, + low_width, + low_height, + previous_mask: vec![0; width * height], + previous_mask_valid: false, + temporal_weight_lut: temporal_weight_lut(), + shape_lut: shape_lut(), + column_fixed, + guide_low: vec![0.0; low_len], + mask_low: vec![0.0; low_len], + mean_guide: vec![0.0; low_len], + mean_mask: vec![0.0; low_len], + corr_guide_guide: vec![0.0; low_len], + corr_guide_mask: vec![0.0; low_len], + coeff_a: vec![0.0; low_len], + coeff_b: vec![0.0; low_len], + scratch: vec![0.0; low_len], + } + } + + pub fn refine(&mut self, luma: &[u8], mask: &mut [u8]) { + assert!(luma.len() >= self.width * self.height); + assert!(mask.len() >= self.width * self.height); + self.blend_temporal(mask); + self.downsample(luma, mask); + self.close_mask_low(); + self.solve_guided_coefficients(); + self.apply_guided_coefficients(luma, mask); + } + + fn blend_temporal(&mut self, mask: &mut [u8]) { + let len = self.width * self.height; + assert!(mask.len() >= len); + assert_eq!(self.previous_mask.len(), len); + if self.previous_mask_valid { + for (current, previous) in mask[..len].iter_mut().zip(self.previous_mask.iter()) { + let new_value = i32::from(*current); + let weight = i32::from(self.temporal_weight_lut[usize::from(*current)]); + let delta = (i32::from(*previous) - new_value) * weight; + *current = (new_value + ((delta + 128) >> 8)).clamp(0, 255) as u8; + } + } + self.previous_mask.copy_from_slice(&mask[..len]); + self.previous_mask_valid = true; + } + + fn downsample(&mut self, luma: &[u8], mask: &[u8]) { + assert_eq!(self.guide_low.len(), self.low_width * self.low_height); + assert_eq!(self.mask_low.len(), self.guide_low.len()); + for low_y in 0..self.low_height { + let y_start = low_y * MASK_REFINE_DOWNSAMPLE; + let y_end = (y_start + MASK_REFINE_DOWNSAMPLE).min(self.height); + for low_x in 0..self.low_width { + let x_start = low_x * MASK_REFINE_DOWNSAMPLE; + let x_end = (x_start + MASK_REFINE_DOWNSAMPLE).min(self.width); + let mut guide_sum: u32 = 0; + let mut mask_sum: u32 = 0; + for y in y_start..y_end { + let row = y * self.width; + for x in x_start..x_end { + guide_sum += u32::from(luma[row + x]); + mask_sum += u32::from(mask[row + x]); + } + } + let count = ((y_end - y_start) * (x_end - x_start)) as f32; + assert!(count >= 1.0); + let low_offset = low_y * self.low_width + low_x; + self.guide_low[low_offset] = guide_sum as f32 / (count * 255.0); + self.mask_low[low_offset] = mask_sum as f32 / (count * 255.0); + } + } + } + + fn close_mask_low(&mut self) { + morph_pass_low( + &self.mask_low, + &mut self.scratch, + &mut self.coeff_a, + self.low_width, + self.low_height, + f32::max, + ); + morph_pass_low( + &self.coeff_a, + &mut self.scratch, + &mut self.mask_low, + self.low_width, + self.low_height, + f32::min, + ); + } + + fn solve_guided_coefficients(&mut self) { + let len = self.low_width * self.low_height; + assert_eq!(self.coeff_a.len(), len); + assert_eq!(self.coeff_b.len(), len); + let radius = GUIDED_FILTER_RADIUS_LOW; + let w = self.low_width; + let h = self.low_height; + box_filter_low( + &self.guide_low, + &mut self.scratch, + &mut self.mean_guide, + w, + h, + radius, + ); + box_filter_low( + &self.mask_low, + &mut self.scratch, + &mut self.mean_mask, + w, + h, + radius, + ); + for i in 0..len { + self.coeff_a[i] = self.guide_low[i] * self.guide_low[i]; + self.coeff_b[i] = self.guide_low[i] * self.mask_low[i]; + } + box_filter_low( + &self.coeff_a, + &mut self.scratch, + &mut self.corr_guide_guide, + w, + h, + radius, + ); + box_filter_low( + &self.coeff_b, + &mut self.scratch, + &mut self.corr_guide_mask, + w, + h, + radius, + ); + for i in 0..len { + let variance = self.corr_guide_guide[i] - self.mean_guide[i] * self.mean_guide[i]; + let covariance = self.corr_guide_mask[i] - self.mean_guide[i] * self.mean_mask[i]; + let a = covariance / (variance.max(0.0) + GUIDED_FILTER_EPSILON); + self.coeff_a[i] = a; + self.coeff_b[i] = self.mean_mask[i] - a * self.mean_guide[i]; + } + box_filter_low( + &self.coeff_a, + &mut self.scratch, + &mut self.mean_guide, + w, + h, + radius, + ); + box_filter_low( + &self.coeff_b, + &mut self.scratch, + &mut self.mean_mask, + w, + h, + radius, + ); + } + + fn apply_guided_coefficients(&self, luma: &[u8], mask: &mut [u8]) { + assert!(luma.len() >= self.width * self.height); + assert!(mask.len() >= self.width * self.height); + let low_w = self.low_width; + for y in 0..self.height { + let row_fixed = bilinear_fixed_coord(y, self.height, self.low_height); + let sy = (row_fixed / 256) as usize; + let fy = (row_fixed % 256) as f32 / 256.0; + let sy_next = (sy + 1).min(self.low_height - 1); + let row = y * self.width; + for x in 0..self.width { + let col_fixed = self.column_fixed[x]; + let sx = (col_fixed / 256) as usize; + let fx = (col_fixed % 256) as f32 / 256.0; + let sx_next = (sx + 1).min(low_w - 1); + let a = bilinear_sample(&self.mean_guide, low_w, sx, sx_next, sy, sy_next, fx, fy); + let b = bilinear_sample(&self.mean_mask, low_w, sx, sx_next, sy, sy_next, fx, fy); + let q = a * (f32::from(luma[row + x]) / 255.0) + b; + let shaped = (q * 255.0 + 0.5).clamp(0.0, 255.0) as usize; + mask[row + x] = self.shape_lut[shaped.min(LUT_LEN - 1)]; + } + } + } +} + +pub(crate) fn bilinear_fixed_coord(index: usize, full_len: usize, low_len: usize) -> u32 { + assert!(full_len >= 1); + assert!(low_len >= 1); + if full_len == 1 { + return 0; + } + (index * (low_len - 1) * 256 / (full_len - 1)) as u32 +} + +#[expect(clippy::too_many_arguments)] +pub(crate) fn bilinear_sample( + plane: &[f32], + width: usize, + sx: usize, + sx_next: usize, + sy: usize, + sy_next: usize, + fx: f32, + fy: f32, +) -> f32 { + assert!(sy * width + sx_next < plane.len()); + assert!(sy_next * width + sx_next < plane.len()); + let top = plane[sy * width + sx] * (1.0 - fx) + plane[sy * width + sx_next] * fx; + let bottom = plane[sy_next * width + sx] * (1.0 - fx) + plane[sy_next * width + sx_next] * fx; + top * (1.0 - fy) + bottom * fy +} + +fn temporal_uncertainty(probability: f32) -> f32 { + assert!(probability >= 0.0); + assert!(probability <= 1.0); + let x = (probability - 0.5) * (probability - 0.5); + let polynomial = x + * (TEMPORAL_UNCERTAINTY_C1 + + x * (TEMPORAL_UNCERTAINTY_C2 + + x * (TEMPORAL_UNCERTAINTY_C3 + + x * (TEMPORAL_UNCERTAINTY_C4 + x * TEMPORAL_UNCERTAINTY_C5)))); + 1.0 - polynomial.min(1.0) +} + +fn temporal_weight_lut() -> [u16; LUT_LEN] { + let mut lut = [0u16; LUT_LEN]; + for (value, slot) in lut.iter_mut().enumerate() { + let probability = value as f32 / 255.0; + let weight = temporal_uncertainty(probability) * TEMPORAL_COMBINE_RATIO; + assert!(weight >= 0.0); + assert!(weight <= 1.0); + *slot = (weight * 256.0 + 0.5) as u16; + } + lut +} + +fn shape_lut() -> [u8; LUT_LEN] { + let span = SHAPE_SMOOTHSTEP_EDGE_HIGH - SHAPE_SMOOTHSTEP_EDGE_LOW; + assert!(span > 0.0); + let mut lut = [0u8; LUT_LEN]; + for (value, slot) in lut.iter_mut().enumerate() { + let probability = value as f32 / 255.0; + let t = ((probability - SHAPE_SMOOTHSTEP_EDGE_LOW) / span).clamp(0.0, 1.0); + let smooth = t * t * (3.0 - 2.0 * t); + *slot = (smooth * 255.0 + 0.5) as u8; + } + assert_eq!(lut[0], 0); + assert_eq!(lut[LUT_LEN - 1], 255); + lut +} + +pub(crate) fn box_filter_low( + src: &[f32], + scratch: &mut [f32], + dst: &mut [f32], + width: usize, + height: usize, + radius: usize, +) { + assert!(width >= 1); + assert!(height >= 1); + assert!(src.len() >= width * height); + assert!(scratch.len() >= width * height); + assert!(dst.len() >= width * height); + box_filter_rows_low(src, scratch, width, height, radius); + box_filter_columns_low(scratch, dst, width, height, radius); +} + +fn box_filter_rows_low(src: &[f32], dst: &mut [f32], width: usize, height: usize, radius: usize) { + assert!(width >= 1); + assert!(src.len() >= width * height); + for y in 0..height { + let row = y * width; + let mut start = 0usize; + let mut end = radius.min(width - 1); + let mut sum: f32 = src[row..=row + end].iter().sum(); + for x in 0..width { + dst[row + x] = sum / ((end - start + 1) as f32); + let next_end = (x + 1 + radius).min(width - 1); + if next_end > end { + sum += src[row + next_end]; + end = next_end; + } + let next_start = (x + 1).saturating_sub(radius); + if next_start > start { + sum -= src[row + start]; + start = next_start; + } + } + } +} + +fn box_filter_columns_low( + src: &[f32], + dst: &mut [f32], + width: usize, + height: usize, + radius: usize, +) { + assert!(width >= 1); + assert!(width <= FRAME_EDGE_MAX); + assert!(height >= 1); + let mut sums = [0.0f32; FRAME_EDGE_MAX]; + let mut start = 0usize; + let mut end = radius.min(height - 1); + for y in 0..=end { + let row = y * width; + for x in 0..width { + sums[x] += src[row + x]; + } + } + for y in 0..height { + let scale = 1.0 / ((end - start + 1) as f32); + let row = y * width; + for x in 0..width { + dst[row + x] = sums[x] * scale; + } + let next_end = (y + 1 + radius).min(height - 1); + if next_end > end { + let next_row = next_end * width; + for x in 0..width { + sums[x] += src[next_row + x]; + } + end = next_end; + } + let next_start = (y + 1).saturating_sub(radius); + if next_start > start { + let previous_row = start * width; + for x in 0..width { + sums[x] -= src[previous_row + x]; + } + start = next_start; + } + } +} + +fn morph_pass_low( + src: &[f32], + scratch: &mut [f32], + dst: &mut [f32], + width: usize, + height: usize, + select: fn(f32, f32) -> f32, +) { + assert!(width >= 1); + assert!(height >= 1); + assert!(src.len() >= width * height); + assert!(scratch.len() >= width * height); + assert!(dst.len() >= width * height); + for y in 0..height { + let row = y * width; + for x in 0..width { + let left = src[row + x.saturating_sub(1)]; + let right = src[row + (x + 1).min(width - 1)]; + scratch[row + x] = select(select(left, src[row + x]), right); + } + } + for y in 0..height { + let above = y.saturating_sub(1) * width; + let below = (y + 1).min(height - 1) * width; + let row = y * width; + for x in 0..width { + dst[row + x] = select( + select(scratch[above + x], scratch[row + x]), + scratch[below + x], + ); + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn gradient_luma(width: usize, height: usize) -> Vec { + let mut luma = vec![0u8; width * height]; + for (index, value) in luma.iter_mut().enumerate() { + *value = ((index % width) * 255 / (width - 1).max(1)) as u8; + } + luma + } + + fn left_half_mask(width: usize, height: usize) -> Vec { + let mut mask = vec![0u8; width * height]; + for y in 0..height { + for x in 0..width / 2 { + mask[y * width + x] = 255; + } + } + mask + } + + #[test] + fn temporal_weight_lut_smooths_uncertain_values_and_trusts_confident_ones() { + let lut = temporal_weight_lut(); + + assert!(lut[128] >= 170); + assert!(lut[128] <= 182); + assert!(lut[0] <= 8); + assert!(lut[255] <= 8); + assert!(lut[64] > lut[16]); + } + + #[test] + fn temporal_blend_pulls_uncertain_pixels_toward_previous_mask() { + let mut refiner = MaskRefiner::new(8, 8); + let mut first = vec![128u8; 64]; + refiner.blend_temporal(&mut first); + let mut second = vec![128u8; 64]; + second[0] = 255; + second[1] = 130; + + refiner.blend_temporal(&mut second); + + assert_eq!(second[0], 255); + assert!(second[1] < 130); + assert_eq!(second[63], 128); + } + + #[test] + fn temporal_blend_first_frame_passes_mask_through_unchanged() { + let mut refiner = MaskRefiner::new(8, 8); + let mut mask = vec![37u8; 64]; + + refiner.blend_temporal(&mut mask); + + assert!(mask.iter().all(|value| *value == 37)); + assert!(refiner.previous_mask_valid); + } + + #[test] + fn shape_lut_is_monotonic_and_saturates_at_both_ends() { + let lut = shape_lut(); + + for value in 1..LUT_LEN { + assert!(lut[value] >= lut[value - 1]); + } + assert_eq!(lut[(255.0 * SHAPE_SMOOTHSTEP_EDGE_LOW) as usize - 4], 0); + assert_eq!(lut[(255.0 * SHAPE_SMOOTHSTEP_EDGE_HIGH) as usize + 4], 255); + } + + #[test] + fn refine_keeps_solid_person_and_background_regions_saturated() { + let width = 64usize; + let height = 48usize; + let mut refiner = MaskRefiner::new(width, height); + let luma = { + let mut luma = vec![32u8; width * height]; + for y in 0..height { + for x in width / 2..width { + luma[y * width + x] = 224; + } + } + luma + }; + let mut mask = left_half_mask(width, height); + + refiner.refine(&luma, &mut mask); + + assert_eq!(mask[24 * width], 255); + assert_eq!(mask[24 * width + 4], 255); + assert_eq!(mask[24 * width + width - 1], 0); + assert_eq!(mask[24 * width + width - 5], 0); + } + + #[test] + fn refine_snaps_mask_transition_to_the_luma_edge() { + let width = 64usize; + let height = 48usize; + let mut refiner = MaskRefiner::new(width, height); + let mut luma = vec![16u8; width * height]; + for y in 0..height { + for x in 0..width / 2 { + luma[y * width + x] = 240; + } + } + let mut blurry_mask = vec![0u8; width * height]; + for y in 0..height { + for x in 0..width { + let distance = (width as i32 / 2 - x as i32).clamp(-12, 12); + blurry_mask[y * width + x] = (127 + distance * 10).clamp(0, 255) as u8; + } + } + + refiner.refine(&luma, &mut blurry_mask); + + let row = 24 * width; + assert!(blurry_mask[row + width / 2 - 8] > 220); + assert!(blurry_mask[row + width / 2 + 8] < 35); + } + + #[test] + fn refine_fills_small_holes_inside_the_person() { + let width = 64usize; + let height = 48usize; + let mut refiner = MaskRefiner::new(width, height); + let luma = vec![128u8; width * height]; + let mut mask = vec![255u8; width * height]; + mask[24 * width + 32] = 0; + + refiner.refine(&luma, &mut mask); + + assert!(mask[24 * width + 32] > 200); + assert_eq!(mask[0], 255); + } + + #[test] + fn refine_handles_minimum_dimensions_without_panicking() { + let mut refiner = MaskRefiner::new(2, 2); + let luma = vec![128u8; 4]; + let mut mask = vec![255u8; 4]; + + refiner.refine(&luma, &mut mask); + + assert_eq!(mask.len(), 4); + assert!(mask.iter().all(|value| *value == 255)); + } + + #[test] + fn box_filter_low_preserves_constant_planes_exactly() { + let width = 9usize; + let height = 7usize; + let src = vec![0.625f32; width * height]; + let mut scratch = vec![0.0f32; width * height]; + let mut dst = vec![0.0f32; width * height]; + + box_filter_low(&src, &mut scratch, &mut dst, width, height, 4); + + for value in dst { + assert!((value - 0.625).abs() < 1e-6); + } + } + + #[test] + fn morph_close_removes_single_pixel_pits_and_keeps_plateaus() { + let width = 8usize; + let height = 8usize; + let mut src = vec![1.0f32; width * height]; + src[3 * width + 3] = 0.0; + let mut scratch = vec![0.0f32; width * height]; + let mut maxed = vec![0.0f32; width * height]; + let mut closed = vec![0.0f32; width * height]; + + morph_pass_low(&src, &mut scratch, &mut maxed, width, height, f32::max); + morph_pass_low(&maxed, &mut scratch, &mut closed, width, height, f32::min); + + assert!(closed[3 * width + 3] > 0.99); + assert!(closed[0] > 0.99); + } + + #[test] + fn refine_converges_to_stable_mask_over_repeated_identical_frames() { + let width = 32usize; + let height = 24usize; + let mut refiner = MaskRefiner::new(width, height); + let luma = gradient_luma(width, height); + let raw = left_half_mask(width, height); + let mut previous_output = vec![0u8; width * height]; + for iteration in 0..8 { + let mut mask = raw.clone(); + refiner.refine(&luma, &mut mask); + if iteration == 7 { + let drift: i32 = mask + .iter() + .zip(previous_output.iter()) + .map(|(a, b)| (i32::from(*a) - i32::from(*b)).abs()) + .sum(); + assert!(drift <= (width * height) as i32); + } + previous_output.copy_from_slice(&mask); + } + assert_eq!(previous_output[12 * width], 255); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/src/native_camera.rs b/fluxer_desktop/native/webrtc-sender/src/native_camera.rs new file mode 100644 index 0000000..5e18fe9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/src/native_camera.rs @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later +#![allow(dead_code)] + +#[cfg(feature = "publisher")] +use livekit::webrtc::video_frame::{VideoBuffer, VideoFrame, VideoRotation, native::NativeBuffer}; +#[cfg(feature = "publisher")] +use livekit::webrtc::video_source::native::NativeVideoSource; +use napi_derive::napi; + +pub const NATIVE_CAMERA_FRAME_QUEUE_CAPACITY: usize = 3; +const MIN_NATIVE_CAMERA_EDGE: u32 = 2; +const MAX_NATIVE_CAMERA_EDGE: u32 = 8192; +const TRANSPORT_CV_PIXEL_BUFFER: &str = "cvPixelBuffer"; +const TRANSPORT_D3D11_TEXTURE: &str = "d3d11Texture"; +const TRANSPORT_DMABUF: &str = "dmabuf"; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum NativeCameraTransport { + CvPixelBuffer, + D3d11Texture, + Dmabuf, +} + +impl NativeCameraTransport { + pub fn as_str(self) -> &'static str { + match self { + Self::CvPixelBuffer => TRANSPORT_CV_PIXEL_BUFFER, + Self::D3d11Texture => TRANSPORT_D3D11_TEXTURE, + Self::Dmabuf => TRANSPORT_DMABUF, + } + } +} + +pub fn required_transports() -> &'static [NativeCameraTransport] { + const TRANSPORTS: &[NativeCameraTransport] = &[ + NativeCameraTransport::CvPixelBuffer, + NativeCameraTransport::D3d11Texture, + NativeCameraTransport::Dmabuf, + ]; + assert_eq!(TRANSPORTS.len(), 3); + TRANSPORTS +} + +pub fn required_transport_names() -> [&'static str; 3] { + let transports = required_transports(); + [ + transports[0].as_str(), + transports[1].as_str(), + transports[2].as_str(), + ] +} + +pub fn platform_native_backgrounds_available() -> bool { + platform_unavailable_reason().is_none() +} + +pub fn camera_backgrounds_available() -> bool { + cfg!(feature = "camera-native") +} + +#[napi] +pub fn has_native_camera_backgrounds() -> bool { + camera_backgrounds_available() +} + +pub fn platform_unavailable_reason() -> Option<&'static str> { + Some(match std::env::consts::OS { + "macos" => "macOS AVFoundation CVPixelBuffer camera backend is not compiled", + "windows" => "Windows Media Foundation D3D11 camera backend is not compiled", + "linux" => "Linux PipeWire/V4L2 dmabuf camera backend is not compiled", + _ => "native platform-buffer camera backend is unsupported on this platform", + }) +} + +pub fn validate_native_frame_dimensions(width: u32, height: u32) -> bool { + if width < MIN_NATIVE_CAMERA_EDGE { + return false; + } + if height < MIN_NATIVE_CAMERA_EDGE { + return false; + } + if !width.is_multiple_of(2) { + return false; + } + if !height.is_multiple_of(2) { + return false; + } + width <= MAX_NATIVE_CAMERA_EDGE && height <= MAX_NATIVE_CAMERA_EDGE +} + +pub fn unavailable_error() -> String { + let reason = platform_unavailable_reason().unwrap_or("native camera backend unavailable"); + format!( + "native camera backgrounds require platform camera buffers ({}, {}, {}): {reason}", + TRANSPORT_CV_PIXEL_BUFFER, TRANSPORT_D3D11_TEXTURE, TRANSPORT_DMABUF + ) +} + +#[cfg(feature = "publisher")] +pub fn publish_native_buffer( + source: &NativeVideoSource, + buffer: NativeBuffer, + timestamp_us: i64, +) -> bool { + assert!(timestamp_us >= 0); + let width = buffer.width(); + let height = buffer.height(); + if !validate_native_frame_dimensions(width, height) { + return false; + } + source.capture_frame(&VideoFrame { + rotation: VideoRotation::VideoRotation0, + timestamp_us, + frame_metadata: None, + buffer, + }); + true +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn required_transports_are_stable() { + assert_eq!( + required_transport_names(), + ["cvPixelBuffer", "d3d11Texture", "dmabuf"] + ); + } + + #[test] + fn queue_capacity_stays_bounded_for_realtime_capture() { + assert_eq!(NATIVE_CAMERA_FRAME_QUEUE_CAPACITY, 3); + assert!(NATIVE_CAMERA_FRAME_QUEUE_CAPACITY < 8); + } + + #[test] + fn camera_background_capability_tracks_native_camera_feature() { + assert_eq!( + camera_backgrounds_available(), + cfg!(feature = "camera-native") + ); + } + + #[test] + fn native_frame_dimensions_require_even_reasonable_sizes() { + assert!(validate_native_frame_dimensions(1280, 720)); + assert!(!validate_native_frame_dimensions(0, 720)); + assert!(!validate_native_frame_dimensions(1280, 1)); + assert!(!validate_native_frame_dimensions(1279, 720)); + assert!(!validate_native_frame_dimensions(1280, 721)); + assert!(!validate_native_frame_dimensions(16_384, 720)); + } + + #[test] + fn unavailable_error_names_all_required_native_transports() { + let error = unavailable_error(); + assert!(error.contains("cvPixelBuffer")); + assert!(error.contains("d3d11Texture")); + assert!(error.contains("dmabuf")); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/src/person_segmentation.rs b/fluxer_desktop/native/webrtc-sender/src/person_segmentation.rs new file mode 100644 index 0000000..33aeb4d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/src/person_segmentation.rs @@ -0,0 +1,681 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub const PERSON_MASK_BACKGROUND: u8 = 0; +pub const PERSON_MASK_PERSON: u8 = 255; +pub const SEGMENTATION_FRAME_BUDGET_MS: u64 = 12; +pub const SEGMENTATION_SLOW_FRAMES_CONSECUTIVE_MAX: u32 = 30; + +pub trait PersonMaskSource { + fn mask_into(&mut self, frame: &crate::yuv::I420, mask: &mut [u8]) -> bool; +} + +#[derive(Debug, Default)] +pub struct SegmentationQualityGovernor { + consecutive_slow_frames: u32, + downgraded: bool, +} + +impl SegmentationQualityGovernor { + pub fn new() -> Self { + Self::default() + } + + pub fn record_frame_duration_ms(&mut self, duration_ms: u64) -> bool { + assert!(self.consecutive_slow_frames < SEGMENTATION_SLOW_FRAMES_CONSECUTIVE_MAX); + if self.downgraded { + return false; + } + if duration_ms <= SEGMENTATION_FRAME_BUDGET_MS { + self.consecutive_slow_frames = 0; + return false; + } + self.consecutive_slow_frames += 1; + assert!(self.consecutive_slow_frames <= SEGMENTATION_SLOW_FRAMES_CONSECUTIVE_MAX); + if self.consecutive_slow_frames < SEGMENTATION_SLOW_FRAMES_CONSECUTIVE_MAX { + return false; + } + self.consecutive_slow_frames = 0; + self.downgraded = true; + true + } +} + +pub fn create_person_mask_source(width: u32, height: u32) -> Option> { + assert!(width >= 2); + assert!(height >= 2); + #[cfg(target_os = "macos")] + { + if let Some(source) = vision::VisionPersonMaskSource::new(width, height) { + return Some(Box::new(source) as Box); + } + } + selfie::SelfieMaskSource::new(width, height) + .map(|source| Box::new(source) as Box) +} + +pub fn resize_mask_bilinear( + src: &[u8], + src_width: usize, + src_height: usize, + src_stride: usize, + dst: &mut [u8], + dst_width: usize, + dst_height: usize, +) { + assert!(src_width >= 1); + assert!(src_height >= 1); + assert!(src_stride >= src_width); + assert!(dst_width >= 1); + assert!(dst_height >= 1); + assert!(src.len() >= src_stride * (src_height - 1) + src_width); + assert!(dst.len() >= dst_width * dst_height); + + for y in 0..dst_height { + let sy_fixed = if dst_height == 1 { + 0 + } else { + y * (src_height - 1) * 256 / (dst_height - 1) + }; + let sy = sy_fixed / 256; + let fy = (sy_fixed % 256) as u32; + let sy_next = (sy + 1).min(src_height - 1); + for x in 0..dst_width { + let sx_fixed = if dst_width == 1 { + 0 + } else { + x * (src_width - 1) * 256 / (dst_width - 1) + }; + let sx = sx_fixed / 256; + let fx = (sx_fixed % 256) as u32; + let sx_next = (sx + 1).min(src_width - 1); + let top = u32::from(src[sy * src_stride + sx]) * (256 - fx) + + u32::from(src[sy * src_stride + sx_next]) * fx; + let bottom = u32::from(src[sy_next * src_stride + sx]) * (256 - fx) + + u32::from(src[sy_next * src_stride + sx_next]) * fx; + dst[y * dst_width + x] = ((top * (256 - fy) + bottom * fy) >> 16) as u8; + } + } +} + +mod selfie { + use super::PersonMaskSource; + use std::sync::{Arc, OnceLock}; + use tract_onnx::prelude::*; + + const MODEL_BYTES: &[u8] = include_bytes!("../models/selfie_segmenter_landscape.onnx"); + const MODEL_INPUT_WIDTH: usize = 256; + const MODEL_INPUT_HEIGHT: usize = 144; + const MODEL_INPUT_CHANNELS: usize = 3; + const MODEL_INPUT_LEN: usize = MODEL_INPUT_WIDTH * MODEL_INPUT_HEIGHT * MODEL_INPUT_CHANNELS; + const MODEL_CHROMA_WIDTH: usize = MODEL_INPUT_WIDTH / 2; + const MODEL_CHROMA_HEIGHT: usize = MODEL_INPUT_HEIGHT / 2; + const INFERENCE_FRAME_INTERVAL_FULL: u32 = 1; + const INFERENCE_FRAME_INTERVAL_DOWNGRADED: u32 = 2; + + type SelfiePlan = TypedRunnableModel; + + fn shared_plan() -> Option> { + static PLAN: OnceLock>> = OnceLock::new(); + PLAN.get_or_init(|| match load_plan() { + Ok(plan) => Some(plan), + Err(error) => { + eprintln!( + "webrtc-sender: selfie segmentation model failed to load; camera \ + background effects fall back to the portrait ellipse: {error}" + ); + None + } + }) + .clone() + } + + fn load_plan() -> TractResult> { + let mut reader = std::io::Cursor::new(MODEL_BYTES); + tract_onnx::onnx() + .model_for_read(&mut reader)? + .with_input_fact( + 0, + f32::fact([ + 1, + MODEL_INPUT_HEIGHT, + MODEL_INPUT_WIDTH, + MODEL_INPUT_CHANNELS, + ]) + .into(), + )? + .into_optimized()? + .into_runnable() + } + + pub struct SelfieMaskSource { + width: u32, + height: u32, + plan: Arc, + luma_low: Vec, + chroma_u_low: Vec, + chroma_v_low: Vec, + input_rgb: Vec, + raw_mask_low: Vec, + raw_mask_valid: bool, + frame_counter: u32, + inference_interval: u32, + inference_error_logged: bool, + governor: super::SegmentationQualityGovernor, + } + + impl SelfieMaskSource { + pub fn new(width: u32, height: u32) -> Option { + assert!(width >= 2); + assert!(height >= 2); + let plan = shared_plan()?; + Some(Self { + width, + height, + plan, + luma_low: vec![0; MODEL_INPUT_WIDTH * MODEL_INPUT_HEIGHT], + chroma_u_low: vec![128; MODEL_CHROMA_WIDTH * MODEL_CHROMA_HEIGHT], + chroma_v_low: vec![128; MODEL_CHROMA_WIDTH * MODEL_CHROMA_HEIGHT], + input_rgb: vec![0.0; MODEL_INPUT_LEN], + raw_mask_low: vec![0; MODEL_INPUT_WIDTH * MODEL_INPUT_HEIGHT], + raw_mask_valid: false, + frame_counter: 0, + inference_interval: INFERENCE_FRAME_INTERVAL_FULL, + inference_error_logged: false, + governor: super::SegmentationQualityGovernor::new(), + }) + } + + fn fill_model_input(&mut self, frame: &crate::yuv::I420) { + assert_eq!(frame.width, self.width); + assert_eq!(frame.height, self.height); + let width = self.width as usize; + let height = self.height as usize; + super::resize_mask_bilinear( + &frame.y, + width, + height, + width, + &mut self.luma_low, + MODEL_INPUT_WIDTH, + MODEL_INPUT_HEIGHT, + ); + super::resize_mask_bilinear( + &frame.u, + width / 2, + height / 2, + width / 2, + &mut self.chroma_u_low, + MODEL_CHROMA_WIDTH, + MODEL_CHROMA_HEIGHT, + ); + super::resize_mask_bilinear( + &frame.v, + width / 2, + height / 2, + width / 2, + &mut self.chroma_v_low, + MODEL_CHROMA_WIDTH, + MODEL_CHROMA_HEIGHT, + ); + for y in 0..MODEL_INPUT_HEIGHT { + let row = y * MODEL_INPUT_WIDTH; + let chroma_row = (y / 2) * MODEL_CHROMA_WIDTH; + for x in 0..MODEL_INPUT_WIDTH { + let luma = i32::from(self.luma_low[row + x]) - 16; + let cb = i32::from(self.chroma_u_low[chroma_row + x / 2]) - 128; + let cr = i32::from(self.chroma_v_low[chroma_row + x / 2]) - 128; + let r = ((298 * luma + 409 * cr + 128) >> 8).clamp(0, 255); + let g = ((298 * luma - 100 * cb - 208 * cr + 128) >> 8).clamp(0, 255); + let b = ((298 * luma + 516 * cb + 128) >> 8).clamp(0, 255); + let offset = (row + x) * MODEL_INPUT_CHANNELS; + self.input_rgb[offset] = r as f32 / 255.0; + self.input_rgb[offset + 1] = g as f32 / 255.0; + self.input_rgb[offset + 2] = b as f32 / 255.0; + } + } + } + + fn run_inference(&mut self, frame: &crate::yuv::I420) -> bool { + self.fill_model_input(frame); + let produced = self.run_model(); + if !produced && !self.inference_error_logged { + self.inference_error_logged = true; + eprintln!( + "webrtc-sender: selfie segmentation inference failed; reusing the \ + previous person mask" + ); + } + produced + } + + fn run_model(&mut self) -> bool { + assert_eq!(self.input_rgb.len(), MODEL_INPUT_LEN); + let Ok(tensor) = Tensor::from_shape( + &[ + 1, + MODEL_INPUT_HEIGHT, + MODEL_INPUT_WIDTH, + MODEL_INPUT_CHANNELS, + ], + &self.input_rgb, + ) else { + return false; + }; + let Ok(result) = self.plan.run(tvec!(tensor.into())) else { + return false; + }; + let Some(output) = result.first() else { + return false; + }; + let Ok(alphas) = output.to_plain_array_view::() else { + return false; + }; + if alphas.len() != self.raw_mask_low.len() { + return false; + } + for (slot, alpha) in self.raw_mask_low.iter_mut().zip(alphas.iter()) { + *slot = (alpha * 255.0 + 0.5).clamp(0.0, 255.0) as u8; + } + true + } + + fn record_inference_duration(&mut self, duration_ms: u64) { + if self.governor.record_frame_duration_ms(duration_ms) { + self.inference_interval = INFERENCE_FRAME_INTERVAL_DOWNGRADED; + eprintln!( + "webrtc-sender: selfie segmentation exceeded the {}ms frame budget for {} \ + consecutive frames; downgrading to inference every {} frames", + super::SEGMENTATION_FRAME_BUDGET_MS, + super::SEGMENTATION_SLOW_FRAMES_CONSECUTIVE_MAX, + INFERENCE_FRAME_INTERVAL_DOWNGRADED + ); + } + } + } + + impl PersonMaskSource for SelfieMaskSource { + fn mask_into(&mut self, frame: &crate::yuv::I420, mask: &mut [u8]) -> bool { + let width = self.width as usize; + let height = self.height as usize; + assert!(mask.len() >= width * height); + if frame.width != self.width || frame.height != self.height { + return false; + } + assert!(self.inference_interval >= 1); + let due = self.frame_counter.is_multiple_of(self.inference_interval); + self.frame_counter = self.frame_counter.wrapping_add(1); + if due || !self.raw_mask_valid { + let started = std::time::Instant::now(); + if self.run_inference(frame) { + self.raw_mask_valid = true; + let duration_ms = started.elapsed().as_millis() as u64; + self.record_inference_duration(duration_ms); + } + } + if !self.raw_mask_valid { + return false; + } + super::resize_mask_bilinear( + &self.raw_mask_low, + MODEL_INPUT_WIDTH, + MODEL_INPUT_HEIGHT, + MODEL_INPUT_WIDTH, + mask, + width, + height, + ); + true + } + } + + #[cfg(test)] + mod tests { + use super::*; + + fn synthetic_frame(width: u32, height: u32) -> crate::yuv::I420 { + let mut frame = crate::yuv::I420::new(width, height).unwrap(); + for (index, value) in frame.y.iter_mut().enumerate() { + *value = ((index * 31 + 17) % 220) as u8 + 16; + } + frame.u.fill(128); + frame.v.fill(128); + frame + } + + #[test] + fn selfie_source_produces_full_range_mask_for_synthetic_frames() { + let mut source = SelfieMaskSource::new(128, 96).expect("bundled model loads"); + let frame = synthetic_frame(128, 96); + let mut mask = vec![0u8; 128 * 96]; + + assert!(source.mask_into(&frame, &mut mask)); + + assert_eq!(mask.len(), 128 * 96); + assert!(source.raw_mask_valid); + } + + #[test] + fn selfie_source_rejects_mismatched_frame_dimensions() { + let mut source = SelfieMaskSource::new(128, 96).expect("bundled model loads"); + let frame = synthetic_frame(64, 48); + let mut mask = vec![0u8; 128 * 96]; + + assert!(!source.mask_into(&frame, &mut mask)); + } + + #[test] + fn selfie_source_reuses_cached_mask_between_inference_frames() { + let mut source = SelfieMaskSource::new(64, 48).expect("bundled model loads"); + source.inference_interval = INFERENCE_FRAME_INTERVAL_DOWNGRADED; + let frame = synthetic_frame(64, 48); + let mut first = vec![0u8; 64 * 48]; + let mut second = vec![0u8; 64 * 48]; + + assert!(source.mask_into(&frame, &mut first)); + assert!(source.mask_into(&frame, &mut second)); + + assert_eq!(first, second); + } + } +} + +#[cfg(target_os = "macos")] +mod vision { + use super::PersonMaskSource; + use core::ptr::NonNull; + use objc2::rc::Retained; + use objc2_core_foundation::CFRetained; + use objc2_core_video::{ + CVPixelBuffer, CVPixelBufferGetBaseAddress, CVPixelBufferGetBaseAddressOfPlane, + CVPixelBufferGetBytesPerRow, CVPixelBufferGetBytesPerRowOfPlane, CVPixelBufferGetHeight, + CVPixelBufferGetPixelFormatType, CVPixelBufferGetWidth, CVPixelBufferLockBaseAddress, + CVPixelBufferLockFlags, CVPixelBufferUnlockBaseAddress, + }; + use objc2_vision::{ + VNGeneratePersonSegmentationRequest, VNGeneratePersonSegmentationRequestQualityLevel, + VNRequest, VNSequenceRequestHandler, + }; + + const PIXEL_FORMAT_NV12_FULL_RANGE: u32 = u32::from_be_bytes(*b"420f"); + const PIXEL_FORMAT_ONE_COMPONENT_8: u32 = u32::from_be_bytes(*b"L008"); + const MASK_PIXELS_MAX: usize = 8192 * 8192; + + pub struct VisionPersonMaskSource { + width: u32, + height: u32, + pixel_buffer: CFRetained, + request: Retained, + requests: Retained>, + handler: Retained, + quality_governor: super::SegmentationQualityGovernor, + } + + impl VisionPersonMaskSource { + pub fn new(width: u32, height: u32) -> Option { + assert!(width >= 2); + assert!(height >= 2); + assert!(width.is_multiple_of(2)); + assert!(height.is_multiple_of(2)); + let mut pixel_buffer_out: *mut CVPixelBuffer = core::ptr::null_mut(); + let status = unsafe { + objc2_core_video::CVPixelBufferCreate( + None, + width as usize, + height as usize, + PIXEL_FORMAT_NV12_FULL_RANGE, + None, + NonNull::new(&mut pixel_buffer_out)?, + ) + }; + if status != 0 { + return None; + } + let pixel_buffer = unsafe { CFRetained::from_raw(NonNull::new(pixel_buffer_out)?) }; + let request = unsafe { VNGeneratePersonSegmentationRequest::new() }; + unsafe { + request.setQualityLevel(VNGeneratePersonSegmentationRequestQualityLevel::Balanced); + request.setOutputPixelFormat(PIXEL_FORMAT_ONE_COMPONENT_8); + } + let request_as_base: Retained = + Retained::into_super(Retained::into_super(Retained::into_super(request.clone()))); + let requests = objc2_foundation::NSArray::from_retained_slice(&[request_as_base]); + assert_eq!(requests.len(), 1); + let handler = unsafe { VNSequenceRequestHandler::new() }; + Some(Self { + width, + height, + pixel_buffer, + request, + requests, + handler, + quality_governor: super::SegmentationQualityGovernor::new(), + }) + } + + fn downgrade_to_fast_quality(&self) { + unsafe { + self.request + .setQualityLevel(VNGeneratePersonSegmentationRequestQualityLevel::Fast); + } + eprintln!( + "webrtc-sender: person segmentation exceeded the {}ms frame budget for {} \ + consecutive frames; downgrading Vision quality from balanced to fast", + super::SEGMENTATION_FRAME_BUDGET_MS, + super::SEGMENTATION_SLOW_FRAMES_CONSECUTIVE_MAX + ); + } + + fn fill_pixel_buffer(&self, frame: &crate::yuv::I420) -> bool { + assert_eq!(frame.width, self.width); + assert_eq!(frame.height, self.height); + let width = self.width as usize; + let height = self.height as usize; + let lock_flags = CVPixelBufferLockFlags(0); + let lock_status = + unsafe { CVPixelBufferLockBaseAddress(&self.pixel_buffer, lock_flags) }; + if lock_status != 0 { + return false; + } + let y_base = CVPixelBufferGetBaseAddressOfPlane(&self.pixel_buffer, 0); + let y_stride = CVPixelBufferGetBytesPerRowOfPlane(&self.pixel_buffer, 0); + let uv_base = CVPixelBufferGetBaseAddressOfPlane(&self.pixel_buffer, 1); + let uv_stride = CVPixelBufferGetBytesPerRowOfPlane(&self.pixel_buffer, 1); + if y_base.is_null() || uv_base.is_null() || y_stride < width || uv_stride < width { + let _ = unsafe { CVPixelBufferUnlockBaseAddress(&self.pixel_buffer, lock_flags) }; + return false; + } + let chroma_width = width / 2; + let chroma_height = height / 2; + unsafe { + let y_base = y_base as *mut u8; + for row in 0..height { + let src = &frame.y[row * width..row * width + width]; + core::ptr::copy_nonoverlapping(src.as_ptr(), y_base.add(row * y_stride), width); + } + let uv_base = uv_base as *mut u8; + for row in 0..chroma_height { + let dst_row = uv_base.add(row * uv_stride); + for col in 0..chroma_width { + let chroma_index = row * chroma_width + col; + dst_row.add(col * 2).write(frame.u[chroma_index]); + dst_row.add(col * 2 + 1).write(frame.v[chroma_index]); + } + } + } + let unlock_status = + unsafe { CVPixelBufferUnlockBaseAddress(&self.pixel_buffer, lock_flags) }; + unlock_status == 0 + } + + fn copy_observation_mask( + mask_buffer: &CVPixelBuffer, + mask: &mut [u8], + width: usize, + height: usize, + ) -> bool { + if CVPixelBufferGetPixelFormatType(mask_buffer) != PIXEL_FORMAT_ONE_COMPONENT_8 { + return false; + } + let lock_flags = CVPixelBufferLockFlags::ReadOnly; + if unsafe { CVPixelBufferLockBaseAddress(mask_buffer, lock_flags) } != 0 { + return false; + } + let src_width = CVPixelBufferGetWidth(mask_buffer); + let src_height = CVPixelBufferGetHeight(mask_buffer); + let src_stride = CVPixelBufferGetBytesPerRow(mask_buffer); + let base = CVPixelBufferGetBaseAddress(mask_buffer); + let valid = !base.is_null() + && src_width >= 1 + && src_height >= 1 + && src_stride >= src_width + && src_width * src_height <= MASK_PIXELS_MAX; + if valid { + let src = unsafe { + core::slice::from_raw_parts( + base as *const u8, + src_stride * (src_height - 1) + src_width, + ) + }; + super::resize_mask_bilinear( + src, src_width, src_height, src_stride, mask, width, height, + ); + } + let _ = unsafe { CVPixelBufferUnlockBaseAddress(mask_buffer, lock_flags) }; + valid + } + } + + impl PersonMaskSource for VisionPersonMaskSource { + fn mask_into(&mut self, frame: &crate::yuv::I420, mask: &mut [u8]) -> bool { + let started = std::time::Instant::now(); + let produced = self.mask_into_timed(frame, mask); + let duration_ms = started.elapsed().as_millis() as u64; + if self.quality_governor.record_frame_duration_ms(duration_ms) { + self.downgrade_to_fast_quality(); + } + produced + } + } + + impl VisionPersonMaskSource { + fn mask_into_timed(&mut self, frame: &crate::yuv::I420, mask: &mut [u8]) -> bool { + let width = self.width as usize; + let height = self.height as usize; + assert!(mask.len() >= width * height); + if frame.width != self.width || frame.height != self.height { + return false; + } + if !self.fill_pixel_buffer(frame) { + return false; + } + let performed = unsafe { + self.handler + .performRequests_onCVPixelBuffer_error(&self.requests, &self.pixel_buffer) + }; + if performed.is_err() { + return false; + } + let Some(results) = (unsafe { self.request.results() }) else { + return false; + }; + let Some(observation) = results.firstObject() else { + return false; + }; + let mask_buffer = unsafe { observation.pixelBuffer() }; + Self::copy_observation_mask(&mask_buffer, mask, width, height) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn resize_identity_returns_same_values() { + let src = vec![0u8, 64, 128, 255]; + let mut dst = vec![0u8; 4]; + + resize_mask_bilinear(&src, 2, 2, 2, &mut dst, 2, 2); + + assert_eq!(dst, src); + } + + #[test] + fn resize_upscales_with_interpolated_midpoints() { + let src = vec![0u8, 255, 0, 255]; + let mut dst = vec![0u8; 9]; + + resize_mask_bilinear(&src, 2, 2, 2, &mut dst, 3, 3); + + assert_eq!(dst[0], 0); + assert_eq!(dst[2], 255); + assert!(dst[1] > 100); + assert!(dst[1] < 156); + } + + #[test] + fn resize_honours_source_stride_padding() { + let src = vec![10u8, 20, 99, 99, 30, 40, 99, 99]; + let mut dst = vec![0u8; 4]; + + resize_mask_bilinear(&src, 2, 2, 4, &mut dst, 2, 2); + + assert_eq!(dst, vec![10, 20, 30, 40]); + } + + #[test] + fn resize_collapses_to_single_pixel_average_free() { + let src = vec![200u8; 16]; + let mut dst = vec![0u8; 1]; + + resize_mask_bilinear(&src, 4, 4, 4, &mut dst, 1, 1); + + assert_eq!(dst, vec![200]); + } + + #[test] + fn mask_constants_span_full_alpha_range() { + assert_eq!(PERSON_MASK_BACKGROUND, 0); + assert_eq!(PERSON_MASK_PERSON, 255); + } + + #[test] + fn segmentation_governor_downgrades_after_consecutive_slow_frames() { + let mut governor = SegmentationQualityGovernor::new(); + + for _ in 1..SEGMENTATION_SLOW_FRAMES_CONSECUTIVE_MAX { + assert!(!governor.record_frame_duration_ms(SEGMENTATION_FRAME_BUDGET_MS + 1)); + } + + assert!(governor.record_frame_duration_ms(SEGMENTATION_FRAME_BUDGET_MS + 1)); + } + + #[test] + fn segmentation_governor_resets_count_after_a_frame_within_budget() { + let mut governor = SegmentationQualityGovernor::new(); + + for _ in 1..SEGMENTATION_SLOW_FRAMES_CONSECUTIVE_MAX { + assert!(!governor.record_frame_duration_ms(SEGMENTATION_FRAME_BUDGET_MS + 1)); + } + assert!(!governor.record_frame_duration_ms(SEGMENTATION_FRAME_BUDGET_MS)); + for _ in 1..SEGMENTATION_SLOW_FRAMES_CONSECUTIVE_MAX { + assert!(!governor.record_frame_duration_ms(SEGMENTATION_FRAME_BUDGET_MS + 1)); + } + + assert!(governor.record_frame_duration_ms(SEGMENTATION_FRAME_BUDGET_MS + 1)); + } + + #[test] + fn segmentation_governor_downgrades_only_once() { + let mut governor = SegmentationQualityGovernor::new(); + for _ in 0..SEGMENTATION_SLOW_FRAMES_CONSECUTIVE_MAX - 1 { + assert!(!governor.record_frame_duration_ms(SEGMENTATION_FRAME_BUDGET_MS + 1)); + } + assert!(governor.record_frame_duration_ms(SEGMENTATION_FRAME_BUDGET_MS + 1)); + + for _ in 0..SEGMENTATION_SLOW_FRAMES_CONSECUTIVE_MAX * 2 { + assert!(!governor.record_frame_duration_ms(SEGMENTATION_FRAME_BUDGET_MS + 100)); + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/src/send_control.rs b/fluxer_desktop/native/webrtc-sender/src/send_control.rs new file mode 100644 index 0000000..abb7005 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/src/send_control.rs @@ -0,0 +1,974 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use parking_lot::Mutex; +use std::sync::atomic::{AtomicU64, Ordering}; + +pub const DEFAULT_AUDIO_BUFFER_TARGET_MS: u32 = 200; +pub const DEFAULT_AUDIO_BUFFER_MAX_MS: u32 = 750; +pub const DEFAULT_MIN_VIDEO_FPS: f64 = 15.0; + +const PRESSURE_WINDOW_MS: u64 = 5_000; +const RECOVERY_WINDOW_COUNT: u32 = 12; +const AUDIO_REBUFFER_GAP_MS: u64 = 120; +const AUDIO_STABLE_GAP_MS: u64 = 60; +const AUDIO_BUFFER_STEP_MS: u32 = 100; + +#[derive(Clone, Debug, PartialEq)] +pub struct SendHealthSnapshot { + pub outgoing_video_queue_depth: u64, + pub outgoing_video_queue_capacity: u64, + pub outgoing_video_max_queue_depth: u64, + pub outgoing_video_frames_produced: u64, + pub outgoing_video_frames_accepted: u64, + pub outgoing_video_frames_dropped: u64, + pub outgoing_video_frames_coalesced: u64, + pub outgoing_video_frames_captured: u64, + pub outgoing_video_capture_failures: u64, + pub outgoing_video_effective_fps: f64, + pub outgoing_video_target_fps: f64, + pub outgoing_video_pacing_target_fps: f64, + pub outgoing_video_max_queue_age_ms: u64, + pub outgoing_video_max_push_latency_ms: u64, + pub outgoing_video_pacing_mode: String, + pub outgoing_video_bus_active: bool, + pub outgoing_audio_buffer_target_ms: u32, + pub outgoing_audio_buffer_max_ms: u32, + pub outgoing_audio_underruns: u64, + pub outgoing_audio_rebuffers: u64, + pub outgoing_audio_max_frame_gap_ms: u64, + pub adaptive_send_tier: String, + pub adaptive_send_reason: String, +} + +impl SendHealthSnapshot { + pub fn idle(audio: &AdaptiveAudioStats) -> Self { + Self { + outgoing_video_queue_depth: 0, + outgoing_video_queue_capacity: 0, + outgoing_video_max_queue_depth: 0, + outgoing_video_frames_produced: 0, + outgoing_video_frames_accepted: 0, + outgoing_video_frames_dropped: 0, + outgoing_video_frames_coalesced: 0, + outgoing_video_frames_captured: 0, + outgoing_video_capture_failures: 0, + outgoing_video_effective_fps: 0.0, + outgoing_video_target_fps: 0.0, + outgoing_video_pacing_target_fps: 0.0, + outgoing_video_max_queue_age_ms: 0, + outgoing_video_max_push_latency_ms: 0, + outgoing_video_pacing_mode: "idle".to_string(), + outgoing_video_bus_active: false, + outgoing_audio_buffer_target_ms: audio.target_buffer_ms(), + outgoing_audio_buffer_max_ms: audio.max_buffer_ms(), + outgoing_audio_underruns: audio.underruns.load(Ordering::Relaxed), + outgoing_audio_rebuffers: audio.rebuffers.load(Ordering::Relaxed), + outgoing_audio_max_frame_gap_ms: audio.max_frame_gap_ms.load(Ordering::Relaxed), + adaptive_send_tier: "idle".to_string(), + adaptive_send_reason: "notPublishing".to_string(), + } + } +} + +pub struct AdaptiveVideoController { + requested_fps: f64, + min_fps: f64, + adaptive: bool, + state: Mutex, +} + +struct AdaptiveVideoState { + current_fps: f64, + tier: String, + reason: String, + window_started_ms: u64, + window_produced: u64, + window_coalesced: u64, + window_dropped: u64, + window_max_queue_age_ms: u64, + window_max_push_latency_ms: u64, + window_egress_fps_sum: f64, + window_egress_fps_samples: u32, + stable_windows: u32, +} + +impl AdaptiveVideoController { + pub fn new(requested_fps: f64, min_fps: f64, adaptive: bool, now_ms: u64) -> Self { + let requested_fps = sanitize_fps(requested_fps, 30.0); + let min_fps = sanitize_fps(min_fps, DEFAULT_MIN_VIDEO_FPS).min(requested_fps); + Self { + requested_fps, + min_fps, + adaptive, + state: Mutex::new(AdaptiveVideoState { + current_fps: requested_fps, + tier: "full".to_string(), + reason: "stable".to_string(), + window_started_ms: now_ms, + window_produced: 0, + window_coalesced: 0, + window_dropped: 0, + window_max_queue_age_ms: 0, + window_max_push_latency_ms: 0, + window_egress_fps_sum: 0.0, + window_egress_fps_samples: 0, + stable_windows: 0, + }), + } + } + + pub fn current_fps(&self) -> f64 { + self.state.lock().current_fps + } + + pub fn tier_and_reason(&self) -> (String, String) { + let state = self.state.lock(); + (state.tier.clone(), state.reason.clone()) + } + + pub fn record_enqueue(&self, now_ms: u64, coalesced: bool) { + let mut state = self.state.lock(); + self.rotate_window(&mut state, now_ms); + state.window_produced += 1; + if coalesced { + state.window_coalesced += 1; + } + } + + #[cfg(test)] + pub fn record_drop(&self, now_ms: u64) { + let mut state = self.state.lock(); + self.rotate_window(&mut state, now_ms); + state.window_dropped += 1; + } + + pub fn record_capture(&self, now_ms: u64, queue_age_ms: u64, push_latency_ms: u64) { + let mut state = self.state.lock(); + self.rotate_window(&mut state, now_ms); + state.window_max_queue_age_ms = state.window_max_queue_age_ms.max(queue_age_ms); + state.window_max_push_latency_ms = state.window_max_push_latency_ms.max(push_latency_ms); + } + + pub fn record_egress_fps(&self, now_ms: u64, fps: f64) { + if !fps.is_finite() || fps < 0.0 { + return; + } + let mut state = self.state.lock(); + self.rotate_window(&mut state, now_ms); + state.window_egress_fps_sum += fps; + state.window_egress_fps_samples += 1; + } + + fn rotate_window(&self, state: &mut AdaptiveVideoState, now_ms: u64) { + if now_ms.saturating_sub(state.window_started_ms) < PRESSURE_WINDOW_MS { + return; + } + self.apply_window(state); + state.window_started_ms = now_ms; + state.window_produced = 0; + state.window_coalesced = 0; + state.window_dropped = 0; + state.window_max_queue_age_ms = 0; + state.window_max_push_latency_ms = 0; + state.window_egress_fps_sum = 0.0; + state.window_egress_fps_samples = 0; + } + + fn apply_window(&self, state: &mut AdaptiveVideoState) { + if !self.adaptive { + state.current_fps = self.requested_fps; + state.tier = "full".to_string(); + state.reason = "adaptiveDisabled".to_string(); + return; + } + + let frame_interval_ms = (1000.0 / state.current_fps.max(1.0)).ceil() as u64; + let latency_pressure = state.window_max_queue_age_ms > frame_interval_ms * 2 + || state.window_max_push_latency_ms > frame_interval_ms * 2; + let drop_ratio = state.window_dropped as f64 / state.window_produced.max(1) as f64; + let encoder_drop_pressure = state.window_produced >= 10 && drop_ratio > 0.05; + let average_egress_fps = if state.window_egress_fps_samples == 0 { + None + } else { + Some(state.window_egress_fps_sum / state.window_egress_fps_samples as f64) + }; + let egress_pressure = average_egress_fps.is_some_and(|fps| { + state.window_egress_fps_samples >= 2 + && state.window_produced >= 10 + && fps < state.current_fps * 0.75 + }); + let pressure = latency_pressure || encoder_drop_pressure || egress_pressure; + + if pressure { + let next = if state.current_fps > 30.0 { + 30.0 + } else if state.current_fps > self.min_fps { + self.min_fps + } else { + state.current_fps + }; + if next < state.current_fps { + state.current_fps = next; + state.tier = tier_for_fps(self.requested_fps, state.current_fps); + } + state.reason = if latency_pressure { + "sendLatencyPressure".to_string() + } else if encoder_drop_pressure { + "encoderDropPressure".to_string() + } else { + "encoderEgressPressure".to_string() + }; + state.stable_windows = 0; + return; + } + + state.stable_windows += 1; + if state.current_fps >= self.requested_fps { + state.reason = "stable".to_string(); + } + if state.stable_windows >= RECOVERY_WINDOW_COUNT && state.current_fps < self.requested_fps { + state.current_fps = (state.current_fps * 2.0).min(self.requested_fps); + state.tier = tier_for_fps(self.requested_fps, state.current_fps); + state.stable_windows = 0; + if state.current_fps >= self.requested_fps { + state.reason = "stable".to_string(); + } + } + } +} + +pub struct AdaptiveVideoStats { + produced: AtomicU64, + accepted: AtomicU64, + dropped: AtomicU64, + coalesced: AtomicU64, + captured: AtomicU64, + capture_failures: AtomicU64, + queue_depth: AtomicU64, + max_queue_depth: AtomicU64, + max_queue_age_ms: AtomicU64, + max_push_latency_ms: AtomicU64, + first_capture_ms: AtomicU64, + last_capture_ms: AtomicU64, + controller: AdaptiveVideoController, +} + +#[derive(Clone, Debug)] +pub struct VideoTelemetryExtras { + pub pacing_mode: String, + pub pacing_target_fps: f64, + pub queue_capacity: u64, + pub bus_active: bool, +} + +impl Default for VideoTelemetryExtras { + fn default() -> Self { + Self { + pacing_mode: "unknown".to_string(), + pacing_target_fps: 0.0, + queue_capacity: 0, + bus_active: false, + } + } +} + +impl AdaptiveVideoStats { + pub fn new(requested_fps: f64, min_fps: f64, adaptive: bool, now_ms: u64) -> Self { + Self { + produced: AtomicU64::new(0), + accepted: AtomicU64::new(0), + dropped: AtomicU64::new(0), + coalesced: AtomicU64::new(0), + captured: AtomicU64::new(0), + capture_failures: AtomicU64::new(0), + queue_depth: AtomicU64::new(0), + max_queue_depth: AtomicU64::new(0), + max_queue_age_ms: AtomicU64::new(0), + max_push_latency_ms: AtomicU64::new(0), + first_capture_ms: AtomicU64::new(0), + last_capture_ms: AtomicU64::new(0), + controller: AdaptiveVideoController::new(requested_fps, min_fps, adaptive, now_ms), + } + } + + #[cfg(test)] + pub fn record_enqueue(&self, now_ms: u64, replaced_pending: bool) { + self.record_enqueue_with_depth(now_ms, replaced_pending, 1); + } + + pub fn record_enqueue_with_depth(&self, now_ms: u64, replaced_pending: bool, queue_depth: u64) { + self.produced.fetch_add(1, Ordering::Relaxed); + self.accepted.fetch_add(1, Ordering::Relaxed); + self.queue_depth.store(queue_depth, Ordering::Relaxed); + update_max(&self.max_queue_depth, queue_depth); + if replaced_pending { + self.coalesced.fetch_add(1, Ordering::Relaxed); + } + self.controller.record_enqueue(now_ms, replaced_pending); + } + + #[cfg(test)] + pub fn record_drop(&self, now_ms: u64) { + self.dropped.fetch_add(1, Ordering::Relaxed); + self.controller.record_drop(now_ms); + } + + pub fn record_reject(&self) { + self.dropped.fetch_add(1, Ordering::Relaxed); + } + + pub fn record_capture(&self, now_ms: u64, queue_age_ms: u64, push_latency_ms: u64) { + update_min_nonzero(&self.first_capture_ms, now_ms); + update_max(&self.last_capture_ms, now_ms); + self.captured.fetch_add(1, Ordering::Relaxed); + self.queue_depth.store(0, Ordering::Relaxed); + update_max(&self.max_queue_age_ms, queue_age_ms); + update_max(&self.max_push_latency_ms, push_latency_ms); + self.controller + .record_capture(now_ms, queue_age_ms, push_latency_ms); + } + + pub fn record_egress_fps(&self, now_ms: u64, fps: f64) { + self.controller.record_egress_fps(now_ms, fps); + } + + pub fn record_capture_failure(&self) { + self.capture_failures.fetch_add(1, Ordering::Relaxed); + self.queue_depth.store(0, Ordering::Relaxed); + } + + pub fn record_queue_cleared(&self) { + self.queue_depth.store(0, Ordering::Relaxed); + } + + pub fn current_fps(&self) -> f64 { + self.controller.current_fps() + } + + fn effective_fps(&self) -> f64 { + let first = self.first_capture_ms.load(Ordering::Relaxed); + let last = self.last_capture_ms.load(Ordering::Relaxed); + let captured = self.captured.load(Ordering::Relaxed); + if first == 0 || last <= first || captured <= 1 { + return 0.0; + } + let elapsed_s = (last - first) as f64 / 1000.0; + ((captured - 1) as f64 / elapsed_s * 100.0).round() / 100.0 + } + + pub fn snapshot( + &self, + audio: &AdaptiveAudioStats, + extras: VideoTelemetryExtras, + ) -> SendHealthSnapshot { + let (tier, reason) = self.controller.tier_and_reason(); + SendHealthSnapshot { + outgoing_video_queue_depth: self.queue_depth.load(Ordering::Relaxed), + outgoing_video_queue_capacity: extras.queue_capacity, + outgoing_video_max_queue_depth: self.max_queue_depth.load(Ordering::Relaxed), + outgoing_video_frames_produced: self.produced.load(Ordering::Relaxed), + outgoing_video_frames_accepted: self.accepted.load(Ordering::Relaxed), + outgoing_video_frames_dropped: self.dropped.load(Ordering::Relaxed), + outgoing_video_frames_coalesced: self.coalesced.load(Ordering::Relaxed), + outgoing_video_frames_captured: self.captured.load(Ordering::Relaxed), + outgoing_video_capture_failures: self.capture_failures.load(Ordering::Relaxed), + outgoing_video_effective_fps: self.effective_fps(), + outgoing_video_target_fps: (self.current_fps() * 100.0).round() / 100.0, + outgoing_video_pacing_target_fps: (extras.pacing_target_fps * 100.0).round() / 100.0, + outgoing_video_max_queue_age_ms: self.max_queue_age_ms.load(Ordering::Relaxed), + outgoing_video_max_push_latency_ms: self.max_push_latency_ms.load(Ordering::Relaxed), + outgoing_video_pacing_mode: extras.pacing_mode, + outgoing_video_bus_active: extras.bus_active, + outgoing_audio_buffer_target_ms: audio.target_buffer_ms(), + outgoing_audio_buffer_max_ms: audio.max_buffer_ms(), + outgoing_audio_underruns: audio.underruns.load(Ordering::Relaxed), + outgoing_audio_rebuffers: audio.rebuffers.load(Ordering::Relaxed), + outgoing_audio_max_frame_gap_ms: audio.max_frame_gap_ms.load(Ordering::Relaxed), + adaptive_send_tier: tier, + adaptive_send_reason: reason, + } + } +} + +pub struct AdaptiveAudioStats { + max_buffer_ms: AtomicU64, + target_buffer_ms: AtomicU64, + underruns: AtomicU64, + rebuffers: AtomicU64, + max_frame_gap_ms: AtomicU64, + last_push_ms: AtomicU64, + stable_started_ms: AtomicU64, +} + +impl AdaptiveAudioStats { + pub fn new(max_buffer_ms: u32, now_ms: u64) -> Self { + let max_buffer_ms = clamp_audio_buffer_ms(max_buffer_ms); + Self { + max_buffer_ms: AtomicU64::new(max_buffer_ms as u64), + target_buffer_ms: AtomicU64::new( + DEFAULT_AUDIO_BUFFER_TARGET_MS.min(max_buffer_ms) as u64 + ), + underruns: AtomicU64::new(0), + rebuffers: AtomicU64::new(0), + max_frame_gap_ms: AtomicU64::new(0), + last_push_ms: AtomicU64::new(0), + stable_started_ms: AtomicU64::new(now_ms), + } + } + + pub fn reset(&self, max_buffer_ms: u32, now_ms: u64) { + let max_buffer_ms = clamp_audio_buffer_ms(max_buffer_ms); + self.max_buffer_ms + .store(max_buffer_ms as u64, Ordering::Relaxed); + self.target_buffer_ms.store( + DEFAULT_AUDIO_BUFFER_TARGET_MS.min(max_buffer_ms) as u64, + Ordering::Relaxed, + ); + self.underruns.store(0, Ordering::Relaxed); + self.rebuffers.store(0, Ordering::Relaxed); + self.max_frame_gap_ms.store(0, Ordering::Relaxed); + self.last_push_ms.store(0, Ordering::Relaxed); + self.stable_started_ms.store(now_ms, Ordering::Relaxed); + } + + pub fn record_push(&self, now_ms: u64) { + let Some(previous) = advance_monotonic(&self.last_push_ms, now_ms) else { + return; + }; + if previous == 0 { + self.stable_started_ms.store(now_ms, Ordering::Relaxed); + return; + } + let gap = now_ms - previous; + update_max(&self.max_frame_gap_ms, gap); + if gap > AUDIO_REBUFFER_GAP_MS { + self.rebuffers.fetch_add(1, Ordering::Relaxed); + if gap > self.target_buffer_ms() as u64 { + self.underruns.fetch_add(1, Ordering::Relaxed); + } + let next = (self.target_buffer_ms() + AUDIO_BUFFER_STEP_MS).min(self.max_buffer_ms()); + self.target_buffer_ms.store(next as u64, Ordering::Relaxed); + self.stable_started_ms.store(now_ms, Ordering::Relaxed); + return; + } + if gap <= AUDIO_STABLE_GAP_MS { + let stable_started = self.stable_started_ms.load(Ordering::Relaxed); + if now_ms.saturating_sub(stable_started) >= 30_000 { + let current = self.target_buffer_ms(); + let next = current + .saturating_sub(AUDIO_BUFFER_STEP_MS) + .max(DEFAULT_AUDIO_BUFFER_TARGET_MS.min(self.max_buffer_ms())); + self.target_buffer_ms.store(next as u64, Ordering::Relaxed); + self.stable_started_ms.store(now_ms, Ordering::Relaxed); + } + } else { + self.stable_started_ms.store(now_ms, Ordering::Relaxed); + } + } + + pub fn max_buffer_ms(&self) -> u32 { + self.max_buffer_ms.load(Ordering::Relaxed) as u32 + } + + pub fn target_buffer_ms(&self) -> u32 { + self.target_buffer_ms.load(Ordering::Relaxed) as u32 + } +} + +pub fn clamp_audio_buffer_ms(value: u32) -> u32 { + let clamped = value.clamp(DEFAULT_AUDIO_BUFFER_TARGET_MS, DEFAULT_AUDIO_BUFFER_MAX_MS); + clamped - (clamped % 10) +} + +fn sanitize_fps(value: f64, fallback: f64) -> f64 { + if value.is_finite() && value > 0.0 { + value + } else { + fallback + } +} + +fn tier_for_fps(requested_fps: f64, current_fps: f64) -> String { + if current_fps >= requested_fps { + "full".to_string() + } else if current_fps >= 30.0 { + "fps30".to_string() + } else { + "fps15".to_string() + } +} + +fn update_max(slot: &AtomicU64, value: u64) { + let mut current = slot.load(Ordering::Relaxed); + while value > current { + match slot.compare_exchange(current, value, Ordering::Relaxed, Ordering::Relaxed) { + Ok(_) => break, + Err(next) => current = next, + } + } +} + +fn update_min_nonzero(slot: &AtomicU64, value: u64) { + if value == 0 { + return; + } + let mut current = slot.load(Ordering::Relaxed); + loop { + if current != 0 && current <= value { + return; + } + match slot.compare_exchange(current, value, Ordering::Relaxed, Ordering::Relaxed) { + Ok(_) => return, + Err(next) => current = next, + } + } +} + +fn advance_monotonic(slot: &AtomicU64, value: u64) -> Option { + let mut current = slot.load(Ordering::Relaxed); + loop { + if current != 0 && value < current { + return None; + } + if value == current { + return Some(current); + } + match slot.compare_exchange(current, value, Ordering::Relaxed, Ordering::Relaxed) { + Ok(_) => return Some(current), + Err(next) => current = next, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::Arc; + + #[test] + fn video_controller_ignores_pure_coalescing_jitter() { + let controller = AdaptiveVideoController::new(60.0, 15.0, true, 0); + for _ in 0..200 { + controller.record_enqueue(1_000, true); + } + controller.record_enqueue(5_001, false); + + assert_eq!(controller.current_fps(), 60.0); + assert_eq!(controller.tier_and_reason().0, "full"); + assert_eq!(controller.tier_and_reason().1, "stable"); + } + + #[test] + fn video_controller_ignores_sustained_coalescing_jitter_across_windows() { + let controller = AdaptiveVideoController::new(60.0, 15.0, true, 0); + for _ in 0..200 { + controller.record_enqueue(1_000, true); + } + controller.record_enqueue(5_001, false); + assert_eq!(controller.current_fps(), 60.0); + + for _ in 0..200 { + controller.record_enqueue(6_000, true); + } + controller.record_enqueue(10_002, false); + assert_eq!(controller.current_fps(), 60.0); + assert_eq!(controller.tier_and_reason().1, "stable"); + } + + #[test] + fn video_controller_degrades_on_encoder_drop_pressure() { + let controller = AdaptiveVideoController::new(60.0, 15.0, true, 0); + for _ in 0..100 { + controller.record_enqueue(1_000, false); + } + for _ in 0..20 { + controller.record_drop(1_000); + } + controller.record_enqueue(5_001, false); + + assert_eq!(controller.current_fps(), 30.0); + assert_eq!(controller.tier_and_reason().1, "encoderDropPressure"); + } + + #[test] + fn video_controller_continues_degrading_under_sustained_latency_pressure() { + let controller = AdaptiveVideoController::new(60.0, 15.0, true, 0); + controller.record_capture(1_000, 80, 1); + controller.record_capture(5_001, 1, 1); + assert_eq!(controller.current_fps(), 30.0); + + controller.record_capture(6_000, 80, 1); + controller.record_capture(10_002, 1, 1); + + assert_eq!(controller.current_fps(), 15.0); + assert_eq!(controller.tier_and_reason().0, "fps15"); + assert_eq!(controller.tier_and_reason().1, "sendLatencyPressure"); + } + + #[test] + fn video_controller_degrades_on_latency_pressure_without_coalescing() { + let controller = AdaptiveVideoController::new(60.0, 15.0, true, 0); + controller.record_capture(1_000, 80, 5); + controller.record_capture(5_001, 1, 1); + + assert_eq!(controller.current_fps(), 30.0); + assert_eq!(controller.tier_and_reason().1, "sendLatencyPressure"); + } + + #[test] + fn video_controller_degrades_on_encoder_egress_pressure() { + let controller = AdaptiveVideoController::new(30.0, 15.0, true, 0); + for _ in 0..60 { + controller.record_enqueue(1_000, false); + } + controller.record_egress_fps(1_000, 7.0); + controller.record_egress_fps(2_000, 8.0); + controller.record_egress_fps(4_000, 7.0); + + controller.record_egress_fps(5_001, 7.0); + + assert_eq!(controller.current_fps(), 15.0); + assert_eq!(controller.tier_and_reason().0, "fps15"); + assert_eq!(controller.tier_and_reason().1, "encoderEgressPressure"); + } + + #[test] + fn video_controller_preserves_pressure_reason_while_degraded_but_stable() { + let controller = AdaptiveVideoController::new(30.0, 15.0, true, 0); + for _ in 0..60 { + controller.record_enqueue(1_000, false); + } + controller.record_egress_fps(1_000, 7.0); + controller.record_egress_fps(2_000, 8.0); + controller.record_egress_fps(4_000, 7.0); + controller.record_egress_fps(5_001, 7.0); + + assert_eq!(controller.current_fps(), 15.0); + assert_eq!(controller.tier_and_reason().1, "encoderEgressPressure"); + + controller.record_capture(10_002, 1, 1); + + assert_eq!(controller.current_fps(), 15.0); + assert_eq!(controller.tier_and_reason().0, "fps15"); + assert_eq!(controller.tier_and_reason().1, "encoderEgressPressure"); + + for index in 2..=12 { + controller.record_capture(10_002 + index * 5_001, 1, 1); + } + + assert_eq!(controller.current_fps(), 30.0); + assert_eq!( + controller.tier_and_reason(), + ("full".to_string(), "stable".to_string()) + ); + } + + #[test] + fn video_controller_ignores_single_encoder_egress_sample() { + let controller = AdaptiveVideoController::new(30.0, 15.0, true, 0); + for _ in 0..60 { + controller.record_enqueue(1_000, false); + } + controller.record_egress_fps(1_000, 7.0); + + controller.record_egress_fps(5_001, 7.0); + + assert_eq!(controller.current_fps(), 30.0); + assert_eq!(controller.tier_and_reason().1, "stable"); + } + + #[test] + fn video_controller_keeps_requested_fps_when_adaptive_send_is_disabled() { + let controller = AdaptiveVideoController::new(60.0, 15.0, false, 0); + for _ in 0..100 { + controller.record_enqueue(1_000, false); + } + controller.record_capture(5_001, 200, 200); + + assert_eq!(controller.current_fps(), 60.0); + assert_eq!( + controller.tier_and_reason(), + ("full".to_string(), "adaptiveDisabled".to_string()) + ); + } + + #[test] + fn video_controller_recovers_after_stable_windows() { + let controller = AdaptiveVideoController::new(60.0, 15.0, true, 0); + controller.record_capture(1_000, 80, 1); + controller.record_capture(5_001, 1, 1); + assert_eq!(controller.current_fps(), 30.0); + + for index in 1..=12 { + controller.record_capture(5_001 + index * 5_001, 1, 1); + } + assert_eq!(controller.current_fps(), 60.0); + } + + #[test] + fn video_controller_recovers_from_minimum_in_two_stable_steps() { + let controller = AdaptiveVideoController::new(60.0, 15.0, true, 0); + controller.record_capture(1_000, 80, 1); + controller.record_capture(5_001, 1, 1); + assert_eq!(controller.current_fps(), 30.0); + + controller.record_capture(6_000, 70, 1); + controller.record_capture(10_002, 1, 1); + assert_eq!(controller.current_fps(), 15.0); + + for index in 1..=12 { + controller.record_capture(10_002 + index * 5_001, 1, 1); + } + assert_eq!(controller.current_fps(), 30.0); + + for index in 13..=24 { + controller.record_capture(10_002 + index * 5_001, 1, 1); + } + assert_eq!(controller.current_fps(), 60.0); + } + + #[test] + fn video_controller_requires_a_full_stable_recovery_window() { + let controller = AdaptiveVideoController::new(60.0, 15.0, true, 0); + controller.record_capture(1_000, 80, 1); + controller.record_capture(5_001, 1, 1); + assert_eq!(controller.current_fps(), 30.0); + + for index in 1..12 { + controller.record_capture(5_001 + index * 5_001, 1, 1); + } + assert_eq!(controller.current_fps(), 30.0); + controller.record_capture(5_001 + 12 * 5_001, 1, 1); + assert_eq!(controller.current_fps(), 60.0); + } + + #[test] + fn video_stats_snapshot_counts_coalescing_drops_failures_and_effective_fps() { + let audio = AdaptiveAudioStats::new(750, 0); + let stats = AdaptiveVideoStats::new(60.0, 15.0, true, 0); + + stats.record_enqueue(1_000, false); + stats.record_enqueue(1_010, true); + stats.record_drop(1_011); + stats.record_capture(1_020, 20, 24); + stats.record_enqueue(2_000, false); + stats.record_capture(2_020, 20, 26); + stats.record_capture_failure(); + + let snapshot = stats.snapshot(&audio, VideoTelemetryExtras::default()); + assert_eq!(snapshot.outgoing_video_frames_produced, 3); + assert_eq!(snapshot.outgoing_video_frames_accepted, 3); + assert_eq!(snapshot.outgoing_video_frames_dropped, 1); + assert_eq!(snapshot.outgoing_video_frames_coalesced, 1); + assert_eq!(snapshot.outgoing_video_frames_captured, 2); + assert_eq!(snapshot.outgoing_video_capture_failures, 1); + assert_eq!(snapshot.outgoing_video_effective_fps, 1.0); + assert_eq!(snapshot.outgoing_video_max_queue_age_ms, 20); + assert_eq!(snapshot.outgoing_video_max_push_latency_ms, 26); + assert_eq!(snapshot.outgoing_video_queue_depth, 0); + } + + #[test] + fn video_stats_effective_fps_uses_capture_time_bounds_for_out_of_order_records() { + let audio = AdaptiveAudioStats::new(750, 0); + let stats = AdaptiveVideoStats::new(60.0, 15.0, true, 0); + + stats.record_capture(2_000, 5, 6); + stats.record_capture(1_000, 7, 8); + stats.record_capture(3_000, 9, 10); + stats.record_capture(2_500, 11, 12); + + let snapshot = stats.snapshot(&audio, VideoTelemetryExtras::default()); + assert_eq!(snapshot.outgoing_video_frames_captured, 4); + assert_eq!(snapshot.outgoing_video_effective_fps, 1.5); + assert_eq!(snapshot.outgoing_video_max_queue_age_ms, 11); + assert_eq!(snapshot.outgoing_video_max_push_latency_ms, 12); + } + + #[test] + fn video_stats_handles_concurrent_recording_without_lost_counts() { + let audio = AdaptiveAudioStats::new(750, 0); + let stats = Arc::new(AdaptiveVideoStats::new(60.0, 15.0, true, 0)); + let mut workers = Vec::new(); + for worker in 0..8 { + let stats = stats.clone(); + workers.push(std::thread::spawn(move || { + for index in 0..250 { + let now_ms = 1_000 + worker * 1_000 + index; + stats.record_enqueue(now_ms, index % 3 == 0); + if index % 5 == 0 { + stats.record_drop(now_ms); + } + if index % 7 == 0 { + stats.record_capture(now_ms + 1, 1, 2); + } + } + })); + } + for worker in workers { + worker.join().expect("worker should not panic"); + } + + let snapshot = stats.snapshot(&audio, VideoTelemetryExtras::default()); + assert_eq!(snapshot.outgoing_video_frames_produced, 2_000); + assert_eq!(snapshot.outgoing_video_frames_accepted, 2_000); + assert_eq!(snapshot.outgoing_video_frames_coalesced, 672); + assert_eq!(snapshot.outgoing_video_frames_dropped, 400); + assert_eq!(snapshot.outgoing_video_frames_captured, 288); + assert_eq!(snapshot.outgoing_video_max_queue_age_ms, 1); + assert_eq!(snapshot.outgoing_video_max_push_latency_ms, 2); + } + + #[test] + fn idle_snapshot_reflects_audio_pressure_without_video_state() { + let audio = AdaptiveAudioStats::new(750, 0); + audio.record_push(1_000); + audio.record_push(1_300); + + let snapshot = SendHealthSnapshot::idle(&audio); + assert_eq!(snapshot.outgoing_video_frames_produced, 0); + assert_eq!(snapshot.outgoing_audio_buffer_target_ms, 300); + assert_eq!(snapshot.outgoing_audio_buffer_max_ms, 750); + assert_eq!(snapshot.outgoing_audio_rebuffers, 1); + assert_eq!(snapshot.outgoing_audio_underruns, 1); + assert_eq!(snapshot.outgoing_audio_max_frame_gap_ms, 300); + assert_eq!(snapshot.adaptive_send_tier, "idle"); + assert_eq!(snapshot.adaptive_send_reason, "notPublishing"); + } + + #[test] + fn video_controller_stress_keeps_target_inside_configured_bounds() { + let controller = AdaptiveVideoController::new(144.0, 24.0, true, 0); + for window in 0..240 { + let base = window * 5_001; + match window % 4 { + 0 => controller.record_capture(base + 1_000, 200, 1), + 1 => { + for _ in 0..30 { + controller.record_enqueue(base + 1_000, true); + } + controller.record_capture(base + 1_500, 1, 1); + } + 2 => { + for _ in 0..60 { + controller.record_enqueue(base + 1_000, false); + } + controller.record_egress_fps(base + 1_500, 30.0); + controller.record_egress_fps(base + 2_500, 30.0); + } + _ => controller.record_capture(base + 1_000, 1, 1), + } + controller.record_capture(base + 5_001, 1, 1); + let fps = controller.current_fps(); + assert!( + (24.0..=144.0).contains(&fps), + "fps target escaped configured bounds: {fps}" + ); + } + } + + #[test] + fn audio_buffer_expands_on_gaps_and_shrinks_after_stability() { + let audio = AdaptiveAudioStats::new(750, 0); + audio.record_push(10); + audio.record_push(200); + assert_eq!(audio.target_buffer_ms(), 300); + assert_eq!(audio.rebuffers.load(Ordering::Relaxed), 1); + + for now_ms in (220..=30_240).step_by(20) { + audio.record_push(now_ms); + } + assert_eq!(audio.target_buffer_ms(), 200); + } + + #[test] + fn audio_buffer_ignores_clock_regression_without_false_rebuffer() { + let audio = AdaptiveAudioStats::new(750, 0); + + audio.record_push(1_000); + audio.record_push(1_020); + audio.record_push(900); + audio.record_push(1_040); + + assert_eq!(audio.target_buffer_ms(), 200); + assert_eq!(audio.rebuffers.load(Ordering::Relaxed), 0); + assert_eq!(audio.underruns.load(Ordering::Relaxed), 0); + assert_eq!(audio.max_frame_gap_ms.load(Ordering::Relaxed), 20); + } + + #[test] + fn audio_buffer_ignores_concurrent_stale_pushes_without_moving_last_push_backwards() { + let audio = Arc::new(AdaptiveAudioStats::new(750, 0)); + audio.record_push(10_000); + + let mut workers = Vec::new(); + for worker in 0..8 { + let audio = audio.clone(); + workers.push(std::thread::spawn(move || { + for index in 0..100 { + audio.record_push(1_000 + worker * 100 + index); + } + })); + } + for worker in workers { + worker.join().expect("worker should not panic"); + } + + audio.record_push(10_020); + + assert_eq!(audio.target_buffer_ms(), 200); + assert_eq!(audio.rebuffers.load(Ordering::Relaxed), 0); + assert_eq!(audio.underruns.load(Ordering::Relaxed), 0); + assert_eq!(audio.max_frame_gap_ms.load(Ordering::Relaxed), 20); + } + + #[test] + fn audio_buffer_growth_caps_at_configured_max_and_reset_clears_pressure() { + let audio = AdaptiveAudioStats::new(350, 0); + audio.record_push(10); + for index in 1..=10 { + audio.record_push(10 + index * 500); + } + assert_eq!(audio.target_buffer_ms(), 350); + assert_eq!(audio.rebuffers.load(Ordering::Relaxed), 10); + assert_eq!(audio.underruns.load(Ordering::Relaxed), 10); + + audio.reset(250, 10_000); + assert_eq!(audio.target_buffer_ms(), 200); + assert_eq!(audio.max_buffer_ms(), 250); + assert_eq!(audio.rebuffers.load(Ordering::Relaxed), 0); + assert_eq!(audio.underruns.load(Ordering::Relaxed), 0); + assert_eq!(audio.max_frame_gap_ms.load(Ordering::Relaxed), 0); + } + + #[test] + fn audio_buffer_stress_stays_within_realtime_bounds_under_jitter() { + let audio = AdaptiveAudioStats::new(620, 0); + let mut now_ms = 10; + audio.record_push(now_ms); + + for index in 0..5_000 { + now_ms += match index % 11 { + 0 => 180, + 1 | 2 => 80, + _ => 20, + }; + audio.record_push(now_ms); + assert!( + (DEFAULT_AUDIO_BUFFER_TARGET_MS..=620).contains(&audio.target_buffer_ms()), + "audio target escaped configured bounds" + ); + } + + assert_eq!(audio.max_buffer_ms(), 620); + assert!(audio.rebuffers.load(Ordering::Relaxed) > 0); + assert!(audio.max_frame_gap_ms.load(Ordering::Relaxed) >= 180); + } + + #[test] + fn audio_buffer_max_is_clamped_to_real_time_bounds() { + assert_eq!(clamp_audio_buffer_ms(50), 200); + assert_eq!(clamp_audio_buffer_ms(777), 750); + assert_eq!(clamp_audio_buffer_ms(333), 330); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/src/speaking.rs b/fluxer_desktop/native/webrtc-sender/src/speaking.rs new file mode 100644 index 0000000..4573143 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/src/speaking.rs @@ -0,0 +1,275 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::sync::atomic::{AtomicU64, Ordering}; + +pub const SPEAKING_ATTACK_MS: u64 = 30; +pub const SPEAKING_RELEASE_MS_LOCAL: u64 = 180; +pub const SPEAKING_RELEASE_MS_REMOTE: u64 = 220; +pub const SPEAKING_HEARTBEAT_INTERVAL_MS: u64 = 1_000; +pub const SPEAKING_FRAME_TIMEOUT_MS: u64 = 250; +pub const SPEAKING_THRESHOLD_RMS_LOCAL_DEFAULT: f64 = 0.008; +pub const SPEAKING_THRESHOLD_RMS_REMOTE_DEFAULT: f64 = 0.006; +pub const SPEAKING_THRESHOLD_RMS_MIN: f64 = 0.000_1; +pub const SPEAKING_THRESHOLD_RMS_MAX: f64 = 0.5; +pub const SPEAKING_FRAME_SAMPLES_MAX: usize = 1 << 20; + +const _: () = assert!(SPEAKING_ATTACK_MS < SPEAKING_RELEASE_MS_LOCAL); +const _: () = assert!(SPEAKING_ATTACK_MS < SPEAKING_RELEASE_MS_REMOTE); +const _: () = assert!(SPEAKING_RELEASE_MS_REMOTE < SPEAKING_HEARTBEAT_INTERVAL_MS); +const _: () = assert!(SPEAKING_FRAME_TIMEOUT_MS < SPEAKING_HEARTBEAT_INTERVAL_MS); + +pub fn clamp_speaking_threshold_rms(threshold_rms: f64) -> f64 { + if !threshold_rms.is_finite() { + return SPEAKING_THRESHOLD_RMS_MIN; + } + threshold_rms.clamp(SPEAKING_THRESHOLD_RMS_MIN, SPEAKING_THRESHOLD_RMS_MAX) +} + +pub struct SpeakingThresholds { + local_rms_bits: AtomicU64, + remote_rms_bits: AtomicU64, +} + +impl SpeakingThresholds { + pub fn new() -> Self { + Self { + local_rms_bits: AtomicU64::new(SPEAKING_THRESHOLD_RMS_LOCAL_DEFAULT.to_bits()), + remote_rms_bits: AtomicU64::new(SPEAKING_THRESHOLD_RMS_REMOTE_DEFAULT.to_bits()), + } + } + + pub fn set(&self, local_rms: f64, remote_rms: f64) { + let local = clamp_speaking_threshold_rms(local_rms); + let remote = clamp_speaking_threshold_rms(remote_rms); + assert!(local >= SPEAKING_THRESHOLD_RMS_MIN); + assert!(remote >= SPEAKING_THRESHOLD_RMS_MIN); + self.local_rms_bits + .store(local.to_bits(), Ordering::Release); + self.remote_rms_bits + .store(remote.to_bits(), Ordering::Release); + } + + pub fn local_rms(&self) -> f64 { + let value = f64::from_bits(self.local_rms_bits.load(Ordering::Acquire)); + assert!(value.is_finite()); + value + } + + pub fn remote_rms(&self) -> f64 { + let value = f64::from_bits(self.remote_rms_bits.load(Ordering::Acquire)); + assert!(value.is_finite()); + value + } +} + +impl Default for SpeakingThresholds { + fn default() -> Self { + Self::new() + } +} + +pub fn frame_rms_i16(samples: &[i16]) -> f64 { + assert!(!samples.is_empty()); + assert!(samples.len() <= SPEAKING_FRAME_SAMPLES_MAX); + let mut sum_squares: f64 = 0.0; + for sample in samples { + let normalized = f64::from(*sample) / 32_768.0; + sum_squares += normalized * normalized; + } + let rms = (sum_squares / samples.len() as f64).sqrt(); + assert!(rms.is_finite()); + assert!(rms >= 0.0); + rms.min(1.0) +} + +pub struct SpeakingGate { + attack_ms: u64, + release_ms: u64, + speaking: bool, + above_since_ms: Option, + below_since_ms: Option, + last_now_ms: u64, +} + +impl SpeakingGate { + pub fn new(attack_ms: u64, release_ms: u64) -> Self { + assert!(attack_ms < release_ms); + assert!(release_ms <= SPEAKING_HEARTBEAT_INTERVAL_MS); + Self { + attack_ms, + release_ms, + speaking: false, + above_since_ms: None, + below_since_ms: None, + last_now_ms: 0, + } + } + + pub fn speaking(&self) -> bool { + self.speaking + } + + pub fn update(&mut self, rms: f64, threshold_rms: f64, now_ms: u64) -> Option { + assert!(rms.is_finite()); + assert!(rms >= 0.0); + assert!(threshold_rms >= SPEAKING_THRESHOLD_RMS_MIN); + assert!(threshold_rms <= SPEAKING_THRESHOLD_RMS_MAX); + assert!(now_ms >= self.last_now_ms); + self.last_now_ms = now_ms; + if rms >= threshold_rms { + self.below_since_ms = None; + let above_since_ms = *self.above_since_ms.get_or_insert(now_ms); + if self.speaking { + return None; + } + if now_ms - above_since_ms < self.attack_ms { + return None; + } + self.speaking = true; + return Some(true); + } + self.above_since_ms = None; + let below_since_ms = *self.below_since_ms.get_or_insert(now_ms); + if !self.speaking { + return None; + } + if now_ms - below_since_ms < self.release_ms { + return None; + } + self.speaking = false; + Some(false) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + const THRESHOLD: f64 = 0.01; + + fn gate() -> SpeakingGate { + SpeakingGate::new(SPEAKING_ATTACK_MS, SPEAKING_RELEASE_MS_LOCAL) + } + + #[test] + fn stays_quiet_below_threshold() { + let mut gate = gate(); + for tick in 0..100u64 { + assert_eq!(gate.update(0.001, THRESHOLD, tick * 10), None); + } + assert!(!gate.speaking()); + } + + #[test] + fn attack_requires_sustained_signal() { + let mut gate = gate(); + assert_eq!(gate.update(0.5, THRESHOLD, 0), None); + assert_eq!(gate.update(0.5, THRESHOLD, 10), None); + assert_eq!(gate.update(0.5, THRESHOLD, 20), None); + assert_eq!(gate.update(0.5, THRESHOLD, 30), Some(true)); + assert!(gate.speaking()); + } + + #[test] + fn single_frame_blip_does_not_trigger() { + let mut gate = gate(); + assert_eq!(gate.update(0.5, THRESHOLD, 0), None); + assert_eq!(gate.update(0.001, THRESHOLD, 10), None); + assert_eq!(gate.update(0.5, THRESHOLD, 20), None); + assert_eq!(gate.update(0.001, THRESHOLD, 30), None); + assert!(!gate.speaking()); + } + + #[test] + fn release_bridges_inter_word_gaps() { + let mut gate = gate(); + for tick in 0..=3u64 { + gate.update(0.5, THRESHOLD, tick * 10); + } + assert!(gate.speaking()); + for tick in 4..=20u64 { + assert_eq!(gate.update(0.001, THRESHOLD, tick * 10), None); + } + assert!(gate.speaking()); + assert_eq!(gate.update(0.5, THRESHOLD, 210), None); + assert!(gate.speaking()); + } + + #[test] + fn release_fires_after_sustained_silence() { + let mut gate = gate(); + for tick in 0..=3u64 { + gate.update(0.5, THRESHOLD, tick * 10); + } + assert!(gate.speaking()); + assert_eq!(gate.update(0.001, THRESHOLD, 40), None); + assert_eq!(gate.update(0.001, THRESHOLD, 219), None); + assert_eq!(gate.update(0.001, THRESHOLD, 220), Some(false)); + assert!(!gate.speaking()); + } + + #[test] + fn retrigger_after_release_needs_full_attack() { + let mut gate = gate(); + for tick in 0..=3u64 { + gate.update(0.5, THRESHOLD, tick * 10); + } + gate.update(0.001, THRESHOLD, 40); + assert_eq!(gate.update(0.001, THRESHOLD, 220), Some(false)); + assert_eq!(gate.update(0.5, THRESHOLD, 230), None); + assert_eq!(gate.update(0.5, THRESHOLD, 260), Some(true)); + } + + #[test] + fn frame_rms_of_silence_is_zero() { + let samples = [0i16; 480]; + assert_eq!(frame_rms_i16(&samples), 0.0); + } + + #[test] + fn frame_rms_of_full_scale_square_wave_is_one() { + let mut samples = [i16::MIN; 480]; + for (index, sample) in samples.iter_mut().enumerate() { + if index % 2 == 0 { + *sample = i16::MAX; + } + } + let rms = frame_rms_i16(&samples); + assert!(rms > 0.999); + assert!(rms <= 1.0); + } + + #[test] + fn frame_rms_scales_with_amplitude() { + let loud = [8_192i16; 480]; + let quiet = [1_024i16; 480]; + assert!(frame_rms_i16(&loud) > frame_rms_i16(&quiet)); + assert!((frame_rms_i16(&loud) - 0.25).abs() < 0.001); + } + + #[test] + fn thresholds_default_and_clamp() { + let thresholds = SpeakingThresholds::new(); + assert_eq!(thresholds.local_rms(), SPEAKING_THRESHOLD_RMS_LOCAL_DEFAULT); + assert_eq!( + thresholds.remote_rms(), + SPEAKING_THRESHOLD_RMS_REMOTE_DEFAULT + ); + thresholds.set(-1.0, f64::NAN); + assert_eq!(thresholds.local_rms(), SPEAKING_THRESHOLD_RMS_MIN); + assert_eq!(thresholds.remote_rms(), SPEAKING_THRESHOLD_RMS_MIN); + thresholds.set(9.0, 0.02); + assert_eq!(thresholds.local_rms(), SPEAKING_THRESHOLD_RMS_MAX); + assert_eq!(thresholds.remote_rms(), 0.02); + } + + #[test] + fn gate_update_is_monotonic_in_time() { + let mut gate = gate(); + gate.update(0.5, THRESHOLD, 100); + let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + gate.update(0.5, THRESHOLD, 50); + })); + assert!(result.is_err()); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/src/stats.rs b/fluxer_desktop/native/webrtc-sender/src/stats.rs new file mode 100644 index 0000000..b129428 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/src/stats.rs @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::send_control::SendHealthSnapshot; +use fluxer_desktop_native::voice::stats as core_stats; + +pub type ByteRateSample = core_stats::ByteRateSample; +pub type OutboundEntry = core_stats::OutboundStatsEntry; +pub type InboundEntry = core_stats::InboundStatsEntry; + +#[derive(Clone, Debug, PartialEq, Default)] +pub struct ConnectionStats { + pub rtt_ms: Option, + pub outbound: Vec, + pub inbound: Vec, + pub send: Option, +} + +pub fn bitrate_kbps(prev: Option, cur: ByteRateSample) -> f64 { + core_stats::bitrate_kbps(prev, cur) +} + +#[cfg(test)] +pub fn sanitize_kbps(kbps: f64) -> f64 { + core_stats::sanitize_kbps(kbps) +} + +pub fn jitter_seconds_to_ms(jitter_s: f64) -> Option { + core_stats::jitter_seconds_to_ms(jitter_s) +} + +pub fn rtt_seconds_to_ms(rtt_s: f64) -> Option { + core_stats::rtt_seconds_to_ms(rtt_s) +} + +pub fn sanitize_audio_level(level: f64) -> Option { + core_stats::sanitize_audio_level(level) +} + +pub fn stats_to_json(stats: &ConnectionStats) -> String { + core_stats::stats_to_json(&core_stats::ConnectionStats { + rtt_ms: stats.rtt_ms, + outbound: stats.outbound.clone(), + inbound: stats.inbound.clone(), + send: stats.send.as_ref().map(send_health_to_core), + }) +} + +fn send_health_to_core(send: &SendHealthSnapshot) -> core_stats::SendHealthStats { + core_stats::SendHealthStats { + outgoing_video_queue_depth: send.outgoing_video_queue_depth, + outgoing_video_queue_capacity: send.outgoing_video_queue_capacity, + outgoing_video_max_queue_depth: send.outgoing_video_max_queue_depth, + outgoing_video_frames_produced: send.outgoing_video_frames_produced, + outgoing_video_frames_accepted: send.outgoing_video_frames_accepted, + outgoing_video_frames_dropped: send.outgoing_video_frames_dropped, + outgoing_video_frames_coalesced: send.outgoing_video_frames_coalesced, + outgoing_video_frames_captured: send.outgoing_video_frames_captured, + outgoing_video_capture_failures: send.outgoing_video_capture_failures, + outgoing_video_effective_fps: send.outgoing_video_effective_fps, + outgoing_video_target_fps: send.outgoing_video_target_fps, + outgoing_video_pacing_target_fps: send.outgoing_video_pacing_target_fps, + outgoing_video_max_queue_age_ms: send.outgoing_video_max_queue_age_ms, + outgoing_video_max_push_latency_ms: send.outgoing_video_max_push_latency_ms, + outgoing_video_pacing_mode: send.outgoing_video_pacing_mode.clone(), + outgoing_video_bus_active: send.outgoing_video_bus_active, + outgoing_audio_buffer_target_ms: send.outgoing_audio_buffer_target_ms, + outgoing_audio_buffer_max_ms: send.outgoing_audio_buffer_max_ms, + outgoing_audio_underruns: send.outgoing_audio_underruns, + outgoing_audio_rebuffers: send.outgoing_audio_rebuffers, + outgoing_audio_max_frame_gap_ms: send.outgoing_audio_max_frame_gap_ms, + adaptive_send_tier: send.adaptive_send_tier.clone(), + adaptive_send_reason: send.adaptive_send_reason.clone(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn bitrate_first_sample_is_zero() { + let cur = ByteRateSample { + bytes: 1000, + timestamp_us: 1_000_000, + }; + assert_eq!(bitrate_kbps(None, cur), 0.0); + } + + #[test] + fn bitrate_computes_kbps_from_byte_delta() { + let prev = ByteRateSample { + bytes: 0, + timestamp_us: 0, + }; + let cur = ByteRateSample { + bytes: 12_500, + timestamp_us: 1_000_000, + }; + assert_eq!(bitrate_kbps(Some(prev), cur), 100.0); + } + + #[test] + fn bitrate_half_second_doubles_rate() { + let prev = ByteRateSample { + bytes: 1000, + timestamp_us: 1_000_000, + }; + let cur = ByteRateSample { + bytes: 13_500, + timestamp_us: 1_500_000, + }; + assert_eq!(bitrate_kbps(Some(prev), cur), 200.0); + } + + #[test] + fn bitrate_rejects_backwards_time_and_bytes() { + let prev = ByteRateSample { + bytes: 5000, + timestamp_us: 2_000_000, + }; + assert_eq!( + bitrate_kbps( + Some(prev), + ByteRateSample { + bytes: 6000, + timestamp_us: 1_000_000 + } + ), + 0.0 + ); + assert_eq!( + bitrate_kbps( + Some(prev), + ByteRateSample { + bytes: 6000, + timestamp_us: 2_000_000 + } + ), + 0.0 + ); + assert_eq!( + bitrate_kbps( + Some(prev), + ByteRateSample { + bytes: 100, + timestamp_us: 3_000_000 + } + ), + 0.0 + ); + } + + #[test] + fn sanitize_kbps_drops_nan_inf_negative_and_rounds() { + assert_eq!(sanitize_kbps(f64::NAN), 0.0); + assert_eq!(sanitize_kbps(f64::INFINITY), 0.0); + assert_eq!(sanitize_kbps(-5.0), 0.0); + assert_eq!(sanitize_kbps(123.456), 123.5); + assert_eq!(sanitize_kbps(100.0), 100.0); + } + + #[test] + fn unit_conversions_seconds_to_ms() { + assert_eq!(jitter_seconds_to_ms(0.012), Some(12.0)); + assert_eq!(jitter_seconds_to_ms(-1.0), None); + assert_eq!(jitter_seconds_to_ms(f64::NAN), None); + assert_eq!(rtt_seconds_to_ms(0.045), Some(45.0)); + assert_eq!(rtt_seconds_to_ms(0.0), None); + assert_eq!(rtt_seconds_to_ms(f64::INFINITY), None); + } + + #[test] + fn sanitize_audio_level_clamps() { + assert_eq!(sanitize_audio_level(0.5), Some(0.5)); + assert_eq!(sanitize_audio_level(2.0), Some(1.0)); + assert_eq!(sanitize_audio_level(-0.1), Some(0.0)); + assert_eq!(sanitize_audio_level(f64::NAN), None); + } + + #[test] + fn empty_stats_serialise_to_null_rtt_and_empty_arrays() { + let stats = ConnectionStats::default(); + assert_eq!( + stats_to_json(&stats), + "{\"rttMs\":null,\"outbound\":[],\"inbound\":[],\"send\":null}" + ); + } + + #[test] + fn full_stats_serialise_to_exact_contract_shape() { + let stats = ConnectionStats { + rtt_ms: Some(42.0), + outbound: vec![ + OutboundEntry { + track_sid: "TR_mic1".into(), + source: "microphone".into(), + kind: "audio".into(), + codec: Some("audio/opus".into()), + bitrate_kbps: 32.0, + packets_lost: 0, + fps: None, + audio_level: Some(0.62), + ..Default::default() + }, + OutboundEntry { + track_sid: "TR_screen1".into(), + source: "screen_share".into(), + kind: "video".into(), + codec: Some("video/H265".into()), + bitrate_kbps: 2500.5, + packets_lost: 3, + packets_sent: 9000, + fps: Some(30.0), + audio_level: None, + width: Some(2176), + height: Some(1200), + source_width: Some(2176), + source_height: Some(1200), + target_bitrate_kbps: Some(50_000.0), + configured_fps: Some(60.0), + target_fps: Some(30.0), + effective_fps: Some(29.8), + frames_produced: Some(120), + frames_accepted: Some(118), + frames_dropped: Some(1), + frames_coalesced: Some(2), + frames_captured: Some(117), + capture_failures: Some(0), + max_queue_age_ms: Some(18), + max_push_latency_ms: Some(12), + adaptive_send_tier: Some("fps30".into()), + adaptive_send_reason: Some("encoderEgressPressure".into()), + }, + ], + inbound: vec![InboundEntry { + participant_sid: "PA_remote1".into(), + participant_identity: Some("user_2_connection_2".into()), + track_sid: "TR_remoteAudio".into(), + source: Some("microphone".into()), + kind: "audio".into(), + codec: Some("audio/opus".into()), + bitrate_kbps: 28.0, + packets_lost: 1, + packets_received: 990, + jitter_ms: Some(5.0), + audio_level: Some(0.75), + fps: None, + width: None, + height: None, + source_width: None, + source_height: None, + }], + send: None, + }; + let json = stats_to_json(&stats); + assert_eq!( + json, + "{\"rttMs\":42,\"outbound\":[\ +{\"trackSid\":\"TR_mic1\",\"source\":\"microphone\",\"kind\":\"audio\",\"bitrateKbps\":32,\"packetsLost\":0,\"packetsSent\":0,\"audioLevel\":0.62,\"codec\":\"audio/opus\"},\ +{\"trackSid\":\"TR_screen1\",\"source\":\"screen_share\",\"kind\":\"video\",\"bitrateKbps\":2500.5,\"packetsLost\":3,\"packetsSent\":9000,\"fps\":30,\"width\":2176,\"height\":1200,\"sourceWidth\":2176,\"sourceHeight\":1200,\"targetBitrateKbps\":50000,\"configuredFps\":60,\"targetFps\":30,\"effectiveFps\":29.8,\"framesProduced\":120,\"framesAccepted\":118,\"framesDropped\":1,\"framesCoalesced\":2,\"framesCaptured\":117,\"captureFailures\":0,\"maxQueueAgeMs\":18,\"maxPushLatencyMs\":12,\"adaptiveSendTier\":\"fps30\",\"adaptiveSendReason\":\"encoderEgressPressure\",\"codec\":\"video/H265\"}\ +],\"inbound\":[\ +{\"participantSid\":\"PA_remote1\",\"trackSid\":\"TR_remoteAudio\",\"kind\":\"audio\",\"bitrateKbps\":28,\"packetsLost\":1,\"packetsReceived\":990,\"participantIdentity\":\"user_2_connection_2\",\"source\":\"microphone\",\"jitterMs\":5,\"audioLevel\":0.75,\"codec\":\"audio/opus\"}\ +],\"send\":null}" + ); + let _ = json; + } + + #[test] + fn send_health_serialises_to_exact_contract_shape() { + let stats = ConnectionStats { + rtt_ms: None, + outbound: vec![], + inbound: vec![], + send: Some(SendHealthSnapshot { + outgoing_video_queue_depth: 1, + outgoing_video_queue_capacity: 8, + outgoing_video_max_queue_depth: 4, + outgoing_video_frames_produced: 2, + outgoing_video_frames_accepted: 3, + outgoing_video_frames_dropped: 4, + outgoing_video_frames_coalesced: 5, + outgoing_video_frames_captured: 6, + outgoing_video_capture_failures: 7, + outgoing_video_effective_fps: 59.94, + outgoing_video_target_fps: 30.0, + outgoing_video_pacing_target_fps: 60.0, + outgoing_video_max_queue_age_ms: 8, + outgoing_video_max_push_latency_ms: 9, + outgoing_video_pacing_mode: "source".to_string(), + outgoing_video_bus_active: true, + outgoing_audio_buffer_target_ms: 300, + outgoing_audio_buffer_max_ms: 750, + outgoing_audio_underruns: 10, + outgoing_audio_rebuffers: 11, + outgoing_audio_max_frame_gap_ms: 120, + adaptive_send_tier: "fps30".to_string(), + adaptive_send_reason: "sendLatencyPressure".to_string(), + }), + }; + + assert_eq!( + stats_to_json(&stats), + "{\"rttMs\":null,\"outbound\":[],\"inbound\":[],\"send\":{\ +\"outgoingVideoQueueDepth\":1,\ +\"outgoingVideoQueueCapacity\":8,\ +\"outgoingVideoMaxQueueDepth\":4,\ +\"outgoingVideoFramesProduced\":2,\ +\"outgoingVideoFramesAccepted\":3,\ +\"outgoingVideoFramesDropped\":4,\ +\"outgoingVideoFramesCoalesced\":5,\ +\"outgoingVideoFramesCaptured\":6,\ +\"outgoingVideoCaptureFailures\":7,\ +\"outgoingVideoEffectiveFps\":59.94,\ +\"outgoingVideoTargetFps\":30,\ +\"outgoingVideoPacingTargetFps\":60,\ +\"outgoingVideoMaxQueueAgeMs\":8,\ +\"outgoingVideoMaxPushLatencyMs\":9,\ +\"outgoingVideoPacingMode\":\"source\",\ +\"outgoingVideoBusActive\":true,\ +\"outgoingAudioBufferTargetMs\":300,\ +\"outgoingAudioBufferMaxMs\":750,\ +\"outgoingAudioUnderruns\":10,\ +\"outgoingAudioRebuffers\":11,\ +\"outgoingAudioMaxFrameGapMs\":120,\ +\"adaptiveSendTier\":\"fps30\",\ +\"adaptiveSendReason\":\"sendLatencyPressure\"\ +}}" + ); + } + + #[test] + fn video_inbound_omits_audio_level_audio_omits_fps() { + let stats = ConnectionStats { + rtt_ms: None, + outbound: vec![], + inbound: vec![InboundEntry { + participant_sid: "PA_x".into(), + participant_identity: None, + track_sid: "TR_v".into(), + source: Some("screen_share".into()), + kind: "video".into(), + codec: None, + bitrate_kbps: 1000.0, + packets_lost: 0, + packets_received: 5000, + jitter_ms: None, + audio_level: None, + fps: Some(29.94), + width: Some(3840), + height: Some(2160), + source_width: Some(3840), + source_height: Some(2160), + }], + send: None, + }; + let json = stats_to_json(&stats); + assert!(!json.contains("audioLevel")); + assert!(!json.contains("jitterMs")); + assert!(json.contains("\"source\":\"screen_share\"")); + assert!(json.contains("\"fps\":29.9")); + assert!(json.contains("\"width\":3840")); + assert!(json.contains("\"height\":2160")); + assert!(json.contains("\"rttMs\":null")); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/src/texture_source.rs b/fluxer_desktop/native/webrtc-sender/src/texture_source.rs new file mode 100644 index 0000000..b2eedc9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/src/texture_source.rs @@ -0,0 +1,747 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[cfg(any(target_os = "windows", test))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct TextureFrameDesc { + pub handle: u64, + pub width: u32, + pub height: u32, + pub dxgi_format: u32, + pub timestamp_us: i64, +} + +#[cfg(any(target_os = "linux", test))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct DmabufFrameDesc { + pub plane_count: u8, + pub width: u32, + pub height: u32, + pub drm_format: u32, + pub modifier: u64, + pub strides: [u32; 4], + pub offsets: [u32; 4], + pub device_uuid: [u8; 16], + pub timestamp_us: i64, +} + +#[cfg_attr(not(target_os = "linux"), allow(dead_code))] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum TextureEncodeError { + NoTexture, + InvalidDimensions, + UnsupportedFormat, + InvalidPlanes, + UnsupportedCodec, + NoHardwareEncoder, + SdkNativeTextureUnsupported, +} + +impl TextureEncodeError { + #[allow(dead_code)] + pub fn as_str(self) -> &'static str { + match self { + TextureEncodeError::NoTexture => "noTexture", + TextureEncodeError::InvalidDimensions => "invalidDimensions", + TextureEncodeError::UnsupportedFormat => "unsupportedFormat", + TextureEncodeError::InvalidPlanes => "invalidPlanes", + TextureEncodeError::UnsupportedCodec => "unsupportedCodec", + TextureEncodeError::NoHardwareEncoder => "noHardwareEncoder", + TextureEncodeError::SdkNativeTextureUnsupported => "sdkNativeTextureUnsupported", + } + } +} + +#[cfg(any(target_os = "windows", test))] +const DXGI_FORMAT_R8G8B8A8_UNORM: u32 = 28; +#[cfg(any(target_os = "windows", test))] +const DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: u32 = 29; +#[cfg(any(target_os = "windows", test))] +const DXGI_FORMAT_B8G8R8A8_UNORM: u32 = 87; +#[cfg(any(target_os = "windows", test))] +const DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: u32 = 91; +#[cfg(any(target_os = "windows", test))] +const DXGI_FORMAT_NV12: u32 = 103; + +#[cfg(any(target_os = "windows", test))] +pub fn dxgi_format_supported(dxgi_format: u32) -> bool { + matches!( + dxgi_format, + DXGI_FORMAT_R8G8B8A8_UNORM + | DXGI_FORMAT_R8G8B8A8_UNORM_SRGB + | DXGI_FORMAT_B8G8R8A8_UNORM + | DXGI_FORMAT_B8G8R8A8_UNORM_SRGB + | DXGI_FORMAT_NV12 + ) +} + +#[cfg(any(target_os = "linux", test))] +const fn fourcc(bytes: [u8; 4]) -> u32 { + u32::from_le_bytes(bytes) +} + +#[cfg(any(target_os = "linux", test))] +const DRM_FORMAT_XRGB8888: u32 = fourcc(*b"XR24"); +#[cfg(any(target_os = "linux", test))] +const DRM_FORMAT_ARGB8888: u32 = fourcc(*b"AR24"); +#[cfg(any(target_os = "linux", test))] +const DRM_FORMAT_XBGR8888: u32 = fourcc(*b"XB24"); +#[cfg(any(target_os = "linux", test))] +const DRM_FORMAT_ABGR8888: u32 = fourcc(*b"AB24"); +#[cfg(any(target_os = "linux", test))] +const DRM_FORMAT_XRGB2101010: u32 = fourcc(*b"XR30"); +#[cfg(any(target_os = "linux", test))] +const DRM_FORMAT_ARGB2101010: u32 = fourcc(*b"AR30"); +#[cfg(any(target_os = "linux", test))] +const DRM_FORMAT_XBGR2101010: u32 = fourcc(*b"XB30"); +#[cfg(any(target_os = "linux", test))] +const DRM_FORMAT_ABGR2101010: u32 = fourcc(*b"AB30"); +#[cfg(any(target_os = "linux", test))] +const DRM_FORMAT_NV12: u32 = fourcc(*b"NV12"); + +#[cfg(any(target_os = "linux", test))] +pub fn drm_format_supported(drm_format: u32) -> bool { + matches!( + drm_format, + DRM_FORMAT_XRGB8888 + | DRM_FORMAT_ARGB8888 + | DRM_FORMAT_XBGR8888 + | DRM_FORMAT_ABGR8888 + | DRM_FORMAT_XRGB2101010 + | DRM_FORMAT_ARGB2101010 + | DRM_FORMAT_XBGR2101010 + | DRM_FORMAT_ABGR2101010 + | DRM_FORMAT_NV12 + ) +} + +#[cfg(any(target_os = "linux", target_os = "windows", test))] +const MAX_TEXTURE_EDGE: u32 = 8192; + +#[cfg(any(target_os = "linux", target_os = "windows", test))] +fn validate_dimensions(width: u32, height: u32) -> Result<(), TextureEncodeError> { + if width < 2 + || height < 2 + || !width.is_multiple_of(2) + || !height.is_multiple_of(2) + || width > MAX_TEXTURE_EDGE + || height > MAX_TEXTURE_EDGE + { + return Err(TextureEncodeError::InvalidDimensions); + } + Ok(()) +} + +#[cfg(any(target_os = "windows", test))] +pub fn validate_texture_desc(desc: &TextureFrameDesc) -> Result<(), TextureEncodeError> { + if desc.handle == 0 { + return Err(TextureEncodeError::NoTexture); + } + validate_dimensions(desc.width, desc.height)?; + if !dxgi_format_supported(desc.dxgi_format) { + return Err(TextureEncodeError::UnsupportedFormat); + } + Ok(()) +} + +#[cfg(any(target_os = "linux", test))] +pub fn validate_dmabuf_desc(desc: &DmabufFrameDesc) -> Result<(), TextureEncodeError> { + validate_dimensions(desc.width, desc.height)?; + if !drm_format_supported(desc.drm_format) { + return Err(TextureEncodeError::UnsupportedFormat); + } + let plane_count = desc.plane_count as usize; + if !(1..=4).contains(&plane_count) { + return Err(TextureEncodeError::InvalidPlanes); + } + for plane in 0..plane_count { + if desc.strides[plane] == 0 { + return Err(TextureEncodeError::InvalidPlanes); + } + } + Ok(()) +} + +#[allow(clippy::too_many_arguments)] +#[cfg(test)] +pub fn dmabuf_desc_from_parts( + fds: &[i32], + plane_count: u32, + width: u32, + height: u32, + drm_format: u32, + modifier: u64, + strides: &[u32], + offsets: &[u32], + device_uuid: &[u8], + timestamp_us: f64, +) -> Option<(DmabufFrameDesc, [i32; 4])> { + let plane_count_u8 = u8::try_from(plane_count).ok()?; + let planes = plane_count as usize; + if !(1..=4).contains(&planes) + || fds.len() < planes + || strides.len() < planes + || offsets.len() < planes + || device_uuid.len() != 16 + { + return None; + } + if fds.iter().take(planes).any(|fd| *fd < 0) { + return None; + } + + let mut fd_array = [-1; 4]; + let mut stride_array = [0; 4]; + let mut offset_array = [0; 4]; + fd_array[..planes].copy_from_slice(&fds[..planes]); + stride_array[..planes].copy_from_slice(&strides[..planes]); + offset_array[..planes].copy_from_slice(&offsets[..planes]); + let mut uuid = [0u8; 16]; + uuid.copy_from_slice(device_uuid); + + Some(( + DmabufFrameDesc { + plane_count: plane_count_u8, + width, + height, + drm_format, + modifier, + strides: stride_array, + offsets: offset_array, + device_uuid: uuid, + timestamp_us: timestamp_us as i64, + }, + fd_array, + )) +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct TextureCapability { + pub available: bool, + pub reason: TextureEncodeError, +} + +impl TextureCapability { + pub fn unavailable(reason: TextureEncodeError) -> Self { + Self { + available: false, + reason, + } + } + + pub fn for_screen_codec(codec: &str, has_hardware_encoder: bool) -> Self { + if !codec_allows_native_gpu(codec) { + return Self::unavailable(TextureEncodeError::UnsupportedCodec); + } + if !has_hardware_encoder { + return Self { + available: false, + reason: TextureEncodeError::NoHardwareEncoder, + }; + } + Self { + available: true, + reason: TextureEncodeError::NoTexture, + } + } +} + +pub(crate) fn codec_allows_native_gpu(codec: &str) -> bool { + matches!( + codec.trim().to_ascii_lowercase().as_str(), + "h264" | "h265" | "hevc" + ) +} + +#[cfg(any(target_os = "windows", test))] +fn sdk_accepts_d3d11_texture_buffers() -> bool { + true +} + +#[cfg(target_os = "linux")] +fn sdk_accepts_dmabuf_texture_buffers() -> bool { + cfg!(target_os = "linux") +} + +#[cfg(any(target_os = "windows", test))] +pub fn should_attempt_texture_encode( + capability: &TextureCapability, + desc: &TextureFrameDesc, +) -> Result<(), TextureEncodeError> { + if !capability.available { + return Err(capability.reason); + } + if !sdk_accepts_d3d11_texture_buffers() { + return Err(TextureEncodeError::SdkNativeTextureUnsupported); + } + validate_texture_desc(desc) +} + +#[cfg(target_os = "linux")] +pub fn should_attempt_dmabuf_encode( + capability: &TextureCapability, + desc: &DmabufFrameDesc, +) -> Result<(), TextureEncodeError> { + if !capability.available { + return Err(capability.reason); + } + if !sdk_accepts_dmabuf_texture_buffers() { + return Err(TextureEncodeError::SdkNativeTextureUnsupported); + } + validate_dmabuf_desc(desc) +} + +#[cfg(test)] +pub fn should_attempt_texture_encode_for_tests( + capability: &TextureCapability, + desc: &TextureFrameDesc, + sdk_accepts_d3d11: bool, +) -> Result<(), TextureEncodeError> { + if !capability.available { + return Err(capability.reason); + } + if !sdk_accepts_d3d11 { + return Err(TextureEncodeError::SdkNativeTextureUnsupported); + } + validate_texture_desc(desc) +} + +#[cfg(test)] +pub fn should_attempt_dmabuf_encode_for_tests( + capability: &TextureCapability, + desc: &DmabufFrameDesc, + sdk_accepts_dmabuf: bool, +) -> Result<(), TextureEncodeError> { + if !capability.available { + return Err(capability.reason); + } + if !sdk_accepts_dmabuf { + return Err(TextureEncodeError::SdkNativeTextureUnsupported); + } + validate_dmabuf_desc(desc) +} + +#[cfg(all(feature = "publisher", any(target_os = "linux", target_os = "windows")))] +pub mod bridge { + #[cfg(target_os = "linux")] + use super::{DmabufFrameDesc, should_attempt_dmabuf_encode}; + use super::{TextureCapability, TextureEncodeError}; + #[cfg(target_os = "windows")] + use super::{TextureFrameDesc, should_attempt_texture_encode}; + use livekit::webrtc::video_frame::{VideoFrame, VideoRotation, native::NativeBuffer}; + use livekit::webrtc::video_source::native::NativeVideoSource; + + #[cfg(target_os = "windows")] + pub fn try_publish_texture( + source: &NativeVideoSource, + capability: &TextureCapability, + desc: &TextureFrameDesc, + ) -> Result<(), TextureEncodeError> { + should_attempt_texture_encode(capability, desc)?; + + let Some(buffer) = NativeBuffer::from_fluxer_d3d11_texture( + desc.handle, + desc.width, + desc.height, + desc.dxgi_format, + ) else { + return Err(TextureEncodeError::SdkNativeTextureUnsupported); + }; + publish_native_buffer(source, buffer, desc.timestamp_us); + Ok(()) + } + + #[cfg(target_os = "linux")] + pub fn try_publish_dmabuf( + source: &NativeVideoSource, + capability: &TextureCapability, + desc: &DmabufFrameDesc, + fds: [i32; 4], + ) -> Result<(), TextureEncodeError> { + should_attempt_dmabuf_encode(capability, desc)?; + let uuid_hi = u64::from_be_bytes(desc.device_uuid[0..8].try_into().unwrap_or([0; 8])); + let uuid_lo = u64::from_be_bytes(desc.device_uuid[8..16].try_into().unwrap_or([0; 8])); + let Some(buffer) = NativeBuffer::from_fluxer_dmabuf_texture( + fds, + desc.plane_count as u32, + desc.width, + desc.height, + desc.drm_format, + desc.modifier, + desc.strides, + desc.offsets, + uuid_hi, + uuid_lo, + ) else { + return Err(TextureEncodeError::SdkNativeTextureUnsupported); + }; + publish_native_buffer(source, buffer, desc.timestamp_us); + Ok(()) + } + + fn publish_native_buffer(source: &NativeVideoSource, buffer: NativeBuffer, timestamp_us: i64) { + source.capture_frame(&VideoFrame { + rotation: VideoRotation::VideoRotation0, + timestamp_us, + frame_metadata: None, + buffer, + }); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn good_desc() -> TextureFrameDesc { + TextureFrameDesc { + handle: 0xDEAD_BEEF, + width: 1920, + height: 1080, + dxgi_format: DXGI_FORMAT_B8G8R8A8_UNORM, + timestamp_us: 123_456, + } + } + + #[test] + fn dxgi_format_gate_accepts_8bit_rgba_bgra_only() { + assert!(dxgi_format_supported(DXGI_FORMAT_B8G8R8A8_UNORM)); + assert!(dxgi_format_supported(DXGI_FORMAT_B8G8R8A8_UNORM_SRGB)); + assert!(dxgi_format_supported(DXGI_FORMAT_R8G8B8A8_UNORM)); + assert!(dxgi_format_supported(DXGI_FORMAT_R8G8B8A8_UNORM_SRGB)); + assert!(dxgi_format_supported(DXGI_FORMAT_NV12)); + assert!(!dxgi_format_supported(24)); + assert!(!dxgi_format_supported(10)); + assert!(!dxgi_format_supported(0)); + } + + #[test] + fn drm_format_gate_accepts_obs_vkcapture_texture_formats() { + assert!(drm_format_supported(DRM_FORMAT_ARGB8888)); + assert!(drm_format_supported(DRM_FORMAT_ABGR8888)); + assert!(drm_format_supported(DRM_FORMAT_ARGB2101010)); + assert!(drm_format_supported(DRM_FORMAT_ABGR2101010)); + assert!(drm_format_supported(DRM_FORMAT_NV12)); + assert!( + !drm_format_supported(fourcc(*b"AB4H")), + "16-bit float DMA-BUF import is not encodable by the native NVENC bridge yet" + ); + assert!(!drm_format_supported(0)); + } + + #[test] + fn validate_rejects_zero_handle_as_no_texture() { + let mut d = good_desc(); + d.handle = 0; + assert_eq!( + validate_texture_desc(&d), + Err(TextureEncodeError::NoTexture) + ); + } + + #[test] + fn validate_rejects_odd_zero_and_oversized_dims() { + for (w, h) in [ + (1921, 1080), + (1920, 1081), + (0, 1080), + (1920, 0), + (8194, 1080), + (1920, 8194), + ] { + let mut d = good_desc(); + d.width = w; + d.height = h; + assert_eq!( + validate_texture_desc(&d), + Err(TextureEncodeError::InvalidDimensions), + "dims {w}x{h} should be rejected" + ); + } + } + + #[test] + fn validate_rejects_unsupported_format() { + let mut d = good_desc(); + d.dxgi_format = 24; + assert_eq!( + validate_texture_desc(&d), + Err(TextureEncodeError::UnsupportedFormat) + ); + } + + #[test] + fn validate_accepts_a_clean_bgra_texture() { + assert_eq!(validate_texture_desc(&good_desc()), Ok(())); + } + + fn good_dmabuf_desc() -> DmabufFrameDesc { + DmabufFrameDesc { + plane_count: 1, + width: 1920, + height: 1080, + drm_format: DRM_FORMAT_ARGB8888, + modifier: 0, + strides: [1920 * 4, 0, 0, 0], + offsets: [0, 0, 0, 0], + device_uuid: [1; 16], + timestamp_us: 123_456, + } + } + + #[test] + fn validate_dmabuf_accepts_supported_formats_with_optional_uuid() { + assert_eq!(validate_dmabuf_desc(&good_dmabuf_desc()), Ok(())); + let mut desc = good_dmabuf_desc(); + desc.drm_format = DRM_FORMAT_NV12; + desc.strides[0] = 1920; + assert_eq!(validate_dmabuf_desc(&desc), Ok(())); + desc = good_dmabuf_desc(); + desc.device_uuid = [0; 16]; + assert_eq!(validate_dmabuf_desc(&desc), Ok(())); + } + + #[test] + fn validate_dmabuf_rejects_invalid_planes() { + let mut desc = good_dmabuf_desc(); + desc.plane_count = 0; + assert_eq!( + validate_dmabuf_desc(&desc), + Err(TextureEncodeError::InvalidPlanes) + ); + desc = good_dmabuf_desc(); + desc.strides[0] = 0; + assert_eq!( + validate_dmabuf_desc(&desc), + Err(TextureEncodeError::InvalidPlanes) + ); + } + + #[test] + fn dmabuf_desc_from_parts_rejects_negative_fds() { + assert!( + dmabuf_desc_from_parts( + &[-1], + 1, + 1920, + 1080, + DRM_FORMAT_ARGB8888, + 0, + &[1920 * 4], + &[0], + &[1; 16], + 123.0, + ) + .is_none() + ); + } + + #[test] + fn dmabuf_desc_from_parts_populates_all_plane_arrays() { + let uuid = [7u8; 16]; + let (desc, fds) = dmabuf_desc_from_parts( + &[10, 11, 12, 99], + 3, + 1920, + 1080, + DRM_FORMAT_NV12, + 0xABCD, + &[1920, 960, 960, 777], + &[0, 2_073_600, 3_110_400, 999], + &uuid, + 123_456.75, + ) + .expect("valid multi-plane descriptor"); + + assert_eq!(desc.plane_count, 3); + assert_eq!(desc.width, 1920); + assert_eq!(desc.height, 1080); + assert_eq!(desc.drm_format, DRM_FORMAT_NV12); + assert_eq!(desc.modifier, 0xABCD); + assert_eq!(desc.strides, [1920, 960, 960, 0]); + assert_eq!(desc.offsets, [0, 2_073_600, 3_110_400, 0]); + assert_eq!(desc.device_uuid, uuid); + assert_eq!(desc.timestamp_us, 123_456); + assert_eq!(fds, [10, 11, 12, -1]); + } + + #[test] + fn dmabuf_desc_from_parts_rejects_incomplete_native_inputs() { + let uuid = [1u8; 16]; + for (fds, strides, offsets, uuid_bytes, label) in [ + (&[4][..], &[128][..], &[0][..], &uuid[..], "too few fds"), + ( + &[4, 5][..], + &[128][..], + &[0, 64][..], + &uuid[..], + "too few strides", + ), + ( + &[4, 5][..], + &[128, 128][..], + &[0][..], + &uuid[..], + "too few offsets", + ), + ( + &[4, 5][..], + &[128, 128][..], + &[0, 64][..], + &[1u8; 15][..], + "bad uuid", + ), + ] { + assert!( + dmabuf_desc_from_parts( + fds, + 2, + 128, + 128, + DRM_FORMAT_ARGB8888, + 0, + strides, + offsets, + uuid_bytes, + 0.0, + ) + .is_none(), + "{label} should be rejected" + ); + } + assert!( + dmabuf_desc_from_parts( + &[4, 5, 6, 7, 8], + 5, + 128, + 128, + DRM_FORMAT_ARGB8888, + 0, + &[128; 5], + &[0; 5], + &uuid, + 0.0, + ) + .is_none() + ); + } + + #[test] + fn validate_precedence_handle_before_dims_before_format() { + let d = TextureFrameDesc { + handle: 0, + width: 1921, + height: 0, + dxgi_format: 999, + timestamp_us: 0, + }; + assert_eq!( + validate_texture_desc(&d), + Err(TextureEncodeError::NoTexture) + ); + } + + #[test] + fn probe_is_available_only_for_explicit_hardware_codecs() { + for codec in ["h264", "H264", "h265", "hevc", "HEVC"] { + let cap = TextureCapability::for_screen_codec(codec, true); + assert!(cap.available, "{codec} should allow native GPU buffers"); + assert_eq!(cap.reason, TextureEncodeError::NoTexture); + } + for codec in ["", "vp8", "vp9", "av1", "rubbish"] { + let cap = TextureCapability::for_screen_codec(codec, true); + assert!( + !cap.available, + "{codec} should not allow native GPU buffers" + ); + assert_eq!(cap.reason, TextureEncodeError::UnsupportedCodec); + } + let cap = TextureCapability::for_screen_codec("h264", false); + assert!(!cap.available); + assert_eq!(cap.reason, TextureEncodeError::NoHardwareEncoder); + } + + #[test] + fn should_attempt_falls_back_when_capability_unavailable() { + let cap = TextureCapability { + available: false, + reason: TextureEncodeError::SdkNativeTextureUnsupported, + }; + assert_eq!( + should_attempt_texture_encode(&cap, &good_desc()), + Err(TextureEncodeError::SdkNativeTextureUnsupported) + ); + } + + #[test] + fn should_attempt_validates_frame_when_capability_available() { + let open = TextureCapability { + available: true, + reason: TextureEncodeError::NoTexture, + }; + assert_eq!( + should_attempt_texture_encode_for_tests(&open, &good_desc(), true), + Ok(()) + ); + assert_eq!(should_attempt_texture_encode(&open, &good_desc()), Ok(())); + let mut bad = good_desc(); + bad.handle = 0; + assert_eq!( + should_attempt_texture_encode_for_tests(&open, &bad, true), + Err(TextureEncodeError::NoTexture) + ); + bad = good_desc(); + bad.dxgi_format = 24; + assert_eq!( + should_attempt_texture_encode_for_tests(&open, &bad, true), + Err(TextureEncodeError::UnsupportedFormat) + ); + } + + #[test] + fn should_attempt_dmabuf_is_sdk_gated_after_validation_capability() { + let open = TextureCapability { + available: true, + reason: TextureEncodeError::NoTexture, + }; + assert_eq!( + should_attempt_dmabuf_encode_for_tests(&open, &good_dmabuf_desc(), true), + Ok(()) + ); + assert_eq!( + should_attempt_dmabuf_encode_for_tests(&open, &good_dmabuf_desc(), false), + Err(TextureEncodeError::SdkNativeTextureUnsupported) + ); + let mut invalid = good_dmabuf_desc(); + invalid.plane_count = 5; + assert_eq!( + should_attempt_dmabuf_encode_for_tests(&open, &invalid, true), + Err(TextureEncodeError::InvalidPlanes) + ); + } + + #[test] + fn error_strings_are_stable() { + assert_eq!(TextureEncodeError::NoTexture.as_str(), "noTexture"); + assert_eq!( + TextureEncodeError::InvalidDimensions.as_str(), + "invalidDimensions" + ); + assert_eq!( + TextureEncodeError::UnsupportedFormat.as_str(), + "unsupportedFormat" + ); + assert_eq!(TextureEncodeError::InvalidPlanes.as_str(), "invalidPlanes"); + assert_eq!( + TextureEncodeError::UnsupportedCodec.as_str(), + "unsupportedCodec" + ); + assert_eq!( + TextureEncodeError::NoHardwareEncoder.as_str(), + "noHardwareEncoder" + ); + assert_eq!( + TextureEncodeError::SdkNativeTextureUnsupported.as_str(), + "sdkNativeTextureUnsupported" + ); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/src/yuv.rs b/fluxer_desktop/native/webrtc-sender/src/yuv.rs new file mode 100644 index 0000000..bb8a536 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/src/yuv.rs @@ -0,0 +1,732 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct I420 { + pub width: u32, + pub height: u32, + pub y: Vec, + pub u: Vec, + pub v: Vec, +} + +impl I420 { + pub fn new(width: u32, height: u32) -> Option { + if !dims_ok(width, height) { + return None; + } + let w = width as usize; + let h = height as usize; + Some(Self { + width, + height, + y: vec![0u8; w * h], + u: vec![0u8; (w / 2) * (h / 2)], + v: vec![0u8; (w / 2) * (h / 2)], + }) + } + + fn has_layout(&self, width: u32, height: u32) -> bool { + if self.width != width || self.height != height { + return false; + } + let w = width as usize; + let h = height as usize; + self.y.len() == w * h && self.u.len() == (w / 2) * (h / 2) && self.v.len() == self.u.len() + } +} + +pub fn tight_i420_byte_len(width: u32, height: u32) -> Option { + if !dims_ok(width, height) { + return None; + } + let w = width as usize; + let h = height as usize; + let y_len = w.checked_mul(h)?; + let chroma_len = (w / 2).checked_mul(h / 2)?; + y_len.checked_add(chroma_len.checked_mul(2)?) +} + +pub fn copy_tight_i420_into(src: &[u8], width: u32, height: u32, dst: &mut I420) -> bool { + if !dst.has_layout(width, height) { + return false; + } + let Some(total_len) = tight_i420_byte_len(width, height) else { + return false; + }; + if src.len() != total_len { + return false; + } + let y_len = (width as usize) * (height as usize); + let chroma_len = y_len / 4; + dst.y.copy_from_slice(&src[..y_len]); + dst.u.copy_from_slice(&src[y_len..y_len + chroma_len]); + dst.v.copy_from_slice(&src[y_len + chroma_len..]); + true +} + +fn dims_ok(width: u32, height: u32) -> bool { + width >= 2 && height >= 2 && width.is_multiple_of(2) && height.is_multiple_of(2) +} + +#[cfg_attr(not(test), allow(dead_code))] +pub fn nv12_to_i420( + src: &[u8], + width: u32, + height: u32, + stride_y: u32, + stride_uv: u32, +) -> Option { + let mut dst = I420::new(width, height)?; + if !nv12_to_i420_into(src, width, height, stride_y, stride_uv, &mut dst) { + return None; + } + Some(dst) +} + +pub fn nv12_to_i420_into( + src: &[u8], + width: u32, + height: u32, + stride_y: u32, + stride_uv: u32, + dst: &mut I420, +) -> bool { + if !dims_ok(width, height) { + return false; + } + if !dst.has_layout(width, height) { + return false; + } + let w = width as usize; + let h = height as usize; + let cw = w / 2; + let ch = h / 2; + let sy = stride_y.max(width) as usize; + let suv = stride_uv.max(width) as usize; + + let Some(uv_offset) = sy.checked_mul(h) else { + return false; + }; + let Some(uv_len) = suv.checked_mul(ch) else { + return false; + }; + let Some(needed) = uv_offset.checked_add(uv_len) else { + return false; + }; + if src.len() < needed { + return false; + } + + for row in 0..h { + let s = row * sy; + dst.y[row * w..row * w + w].copy_from_slice(&src[s..s + w]); + } + + for row in 0..ch { + let base = uv_offset + row * suv; + for x in 0..cw { + dst.u[row * cw + x] = src[base + 2 * x]; + dst.v[row * cw + x] = src[base + 2 * x + 1]; + } + } + + true +} + +#[allow(clippy::too_many_arguments)] +pub fn copy_nv12_planes( + src: &[u8], + width: u32, + height: u32, + stride_y: u32, + stride_uv: u32, + dst_y: &mut [u8], + dst_uv: &mut [u8], + dst_stride_y: u32, + dst_stride_uv: u32, +) -> bool { + if !dims_ok(width, height) { + return false; + } + let w = width as usize; + let h = height as usize; + let ch = h / 2; + let sy = stride_y.max(width) as usize; + let suv = stride_uv.max(width) as usize; + let dsy = dst_stride_y as usize; + let dsuv = dst_stride_uv as usize; + if dsy < w || dsuv < w { + return false; + } + + let Some(uv_offset) = sy.checked_mul(h) else { + return false; + }; + let Some(uv_len) = suv.checked_mul(ch) else { + return false; + }; + let Some(needed) = uv_offset.checked_add(uv_len) else { + return false; + }; + if src.len() < needed || dst_y.len() < dsy * h || dst_uv.len() < dsuv * ch { + return false; + } + + for row in 0..h { + let s = row * sy; + let d = row * dsy; + dst_y[d..d + w].copy_from_slice(&src[s..s + w]); + } + for row in 0..ch { + let s = uv_offset + row * suv; + let d = row * dsuv; + dst_uv[d..d + w].copy_from_slice(&src[s..s + w]); + } + true +} + +#[cfg(test)] +pub fn yuyv_to_i420(src: &[u8], width: u32, height: u32, stride: u32) -> Option { + let mut dst = I420::new(width, height)?; + if !yuyv_to_i420_into(src, width, height, stride, &mut dst) { + return None; + } + Some(dst) +} + +pub fn yuyv_to_i420_into(src: &[u8], width: u32, height: u32, stride: u32, dst: &mut I420) -> bool { + if !dims_ok(width, height) { + return false; + } + if !dst.has_layout(width, height) { + return false; + } + let w = width as usize; + let h = height as usize; + let cw = w / 2; + let ch = h / 2; + let stride = stride.max(width * 2) as usize; + if src.len() < stride * h { + return false; + } + + for row in 0..h { + let row_base = row * stride; + for pair in 0..cw { + let src_offset = row_base + pair * 4; + let dst_offset = row * w + pair * 2; + dst.y[dst_offset] = src[src_offset]; + dst.y[dst_offset + 1] = src[src_offset + 2]; + } + } + for cy in 0..ch { + for cx in 0..cw { + let top = (cy * 2) * stride + cx * 4; + let bottom = (cy * 2 + 1) * stride + cx * 4; + dst.u[cy * cw + cx] = + ((u16::from(src[top + 1]) + u16::from(src[bottom + 1])) / 2) as u8; + dst.v[cy * cw + cx] = + ((u16::from(src[top + 3]) + u16::from(src[bottom + 3])) / 2) as u8; + } + } + + true +} + +fn clamp_u8(value: i32) -> u8 { + value.clamp(0, 255) as u8 +} + +fn rgb_to_y(r: i32, g: i32, b: i32) -> u8 { + clamp_u8(((66 * r + 129 * g + 25 * b + 128) >> 8) + 16) +} +fn rgb_to_u(r: i32, g: i32, b: i32) -> u8 { + clamp_u8(((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128) +} +fn rgb_to_v(r: i32, g: i32, b: i32) -> u8 { + clamp_u8(((112 * r - 94 * g - 18 * b + 128) >> 8) + 128) +} + +#[cfg(test)] +pub fn bgra_to_i420(src: &[u8], width: u32, height: u32, stride: u32) -> Option { + let mut dst = I420::new(width, height)?; + if !bgra_to_i420_planes( + src, + width, + height, + stride, + &mut dst.y, + &mut dst.u, + &mut dst.v, + width, + width / 2, + width / 2, + ) { + return None; + } + Some(dst) +} + +#[allow(clippy::too_many_arguments)] +pub fn bgra_to_i420_planes( + src: &[u8], + width: u32, + height: u32, + stride: u32, + dst_y: &mut [u8], + dst_u: &mut [u8], + dst_v: &mut [u8], + dst_stride_y: u32, + dst_stride_u: u32, + dst_stride_v: u32, +) -> bool { + if !dims_ok(width, height) { + return false; + } + let w = width as usize; + let h = height as usize; + let cw = w / 2; + let ch = h / 2; + let stride = stride.max(width * 4) as usize; + if src.len() < stride * h { + return false; + } + let dsy = dst_stride_y as usize; + let dsu = dst_stride_u as usize; + let dsv = dst_stride_v as usize; + if dsy < w || dsu < cw || dsv < cw { + return false; + } + if dst_y.len() < dsy * h || dst_u.len() < dsu * ch || dst_v.len() < dsv * ch { + return false; + } + + let px = |row: usize, col: usize| -> (i32, i32, i32) { + let o = row * stride + col * 4; + let b = src[o] as i32; + let g = src[o + 1] as i32; + let r = src[o + 2] as i32; + (r, g, b) + }; + + for row in 0..h { + for col in 0..w { + let (r, g, b) = px(row, col); + dst_y[row * dsy + col] = rgb_to_y(r, g, b); + } + } + for cy in 0..ch { + for cx in 0..cw { + let mut rs = 0; + let mut gs = 0; + let mut bs = 0; + for dy in 0..2 { + for dx in 0..2 { + let (r, g, b) = px(cy * 2 + dy, cx * 2 + dx); + rs += r; + gs += g; + bs += b; + } + } + let (r, g, b) = (rs / 4, gs / 4, bs / 4); + dst_u[cy * dsu + cx] = rgb_to_u(r, g, b); + dst_v[cy * dsv + cx] = rgb_to_v(r, g, b); + } + } + + true +} + +#[cfg_attr(not(feature = "camera-native"), allow(dead_code))] +pub fn rgb_to_i420(src: &[u8], width: u32, height: u32) -> Option { + let mut dst = I420::new(width, height)?; + if !rgb_to_i420_into(src, width, height, &mut dst) { + return None; + } + Some(dst) +} + +pub fn rgb_to_i420_into(src: &[u8], width: u32, height: u32, dst: &mut I420) -> bool { + if !dims_ok(width, height) { + return false; + } + if !dst.has_layout(width, height) { + return false; + } + let w = width as usize; + let h = height as usize; + let cw = w / 2; + let ch = h / 2; + let stride = w * 3; + if src.len() < stride * h { + return false; + } + + let px = |row: usize, col: usize| -> (i32, i32, i32) { + let o = row * stride + col * 3; + let r = src[o] as i32; + let g = src[o + 1] as i32; + let b = src[o + 2] as i32; + (r, g, b) + }; + + for row in 0..h { + for col in 0..w { + let (r, g, b) = px(row, col); + dst.y[row * w + col] = rgb_to_y(r, g, b); + } + } + for cy in 0..ch { + for cx in 0..cw { + let mut rs = 0; + let mut gs = 0; + let mut bs = 0; + for dy in 0..2 { + for dx in 0..2 { + let (r, g, b) = px(cy * 2 + dy, cx * 2 + dx); + rs += r; + gs += g; + bs += b; + } + } + let (r, g, b) = (rs / 4, gs / 4, bs / 4); + dst.u[cy * cw + cx] = rgb_to_u(r, g, b); + dst.v[cy * cw + cx] = rgb_to_v(r, g, b); + } + } + + true +} + +#[cfg_attr(not(feature = "camera-native"), allow(dead_code))] +pub fn bgr_to_i420_into(src: &[u8], width: u32, height: u32, dst: &mut I420) -> bool { + if !dims_ok(width, height) { + return false; + } + if !dst.has_layout(width, height) { + return false; + } + let w = width as usize; + let h = height as usize; + let cw = w / 2; + let ch = h / 2; + let stride = w * 3; + if src.len() < stride * h { + return false; + } + + let px = |row: usize, col: usize| -> (i32, i32, i32) { + let o = row * stride + col * 3; + let b = src[o] as i32; + let g = src[o + 1] as i32; + let r = src[o + 2] as i32; + (r, g, b) + }; + + for row in 0..h { + for col in 0..w { + let (r, g, b) = px(row, col); + dst.y[row * w + col] = rgb_to_y(r, g, b); + } + } + for cy in 0..ch { + for cx in 0..cw { + let mut rs = 0; + let mut gs = 0; + let mut bs = 0; + for dy in 0..2 { + for dx in 0..2 { + let (r, g, b) = px(cy * 2 + dy, cx * 2 + dx); + rs += r; + gs += g; + bs += b; + } + } + let (r, g, b) = (rs / 4, gs / 4, bs / 4); + dst.u[cy * cw + cx] = rgb_to_u(r, g, b); + dst.v[cy * cw + cx] = rgb_to_v(r, g, b); + } + } + + true +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn rejects_odd_or_tiny_dimensions() { + assert!(nv12_to_i420(&[0u8; 64], 3, 2, 3, 3).is_none()); + assert!(nv12_to_i420(&[0u8; 64], 2, 1, 2, 2).is_none()); + assert!(bgra_to_i420(&[0u8; 256], 2, 3, 8).is_none()); + assert!(bgra_to_i420(&[0u8; 256], 0, 2, 0).is_none()); + } + + #[test] + fn nv12_short_buffer_is_rejected() { + assert!(nv12_to_i420(&[0u8; 5], 2, 2, 2, 2).is_none()); + assert!(nv12_to_i420(&[0u8; 6], 2, 2, 2, 2).is_some()); + } + + #[test] + fn nv12_packed_2x2_deinterleaves() { + let src = [1u8, 2, 3, 4, 10, 20]; + let out = nv12_to_i420(&src, 2, 2, 2, 2).unwrap(); + assert_eq!(out.y, vec![1, 2, 3, 4]); + assert_eq!(out.u, vec![10]); + assert_eq!(out.v, vec![20]); + assert_eq!((out.width / 2, out.height / 2), (1, 1)); + } + + #[test] + fn yuyv_2x2_deinterleaves_and_vertically_averages_chroma() { + let src = [1u8, 10, 2, 20, 3, 30, 4, 40]; + let out = yuyv_to_i420(&src, 2, 2, 4).unwrap(); + + assert_eq!(out.y, vec![1, 2, 3, 4]); + assert_eq!(out.u, vec![20]); + assert_eq!(out.v, vec![30]); + } + + #[test] + fn yuyv_respects_row_padding() { + let src = [1u8, 10, 2, 20, 99, 99, 3, 30, 4, 40, 88, 88]; + let out = yuyv_to_i420(&src, 2, 2, 6).unwrap(); + + assert_eq!(out.y, vec![1, 2, 3, 4]); + assert_eq!(out.u, vec![20]); + assert_eq!(out.v, vec![30]); + } + + #[test] + fn nv12_4x4_deinterleaves_two_chroma_columns() { + let mut src = Vec::new(); + src.extend(0u8..16); + src.extend([100, 101, 102, 103, 104, 105, 106, 107]); + let out = nv12_to_i420(&src, 4, 4, 4, 4).unwrap(); + assert_eq!(out.y, (0u8..16).collect::>()); + assert_eq!(out.u, vec![100, 102, 104, 106]); + assert_eq!(out.v, vec![101, 103, 105, 107]); + } + + #[test] + fn nv12_respects_row_padding() { + let src = [1u8, 2, 0xFF, 0xFF, 3, 4, 0xFF, 0xFF, 10, 20, 0xFF, 0xFF]; + let out = nv12_to_i420(&src, 2, 2, 4, 4).unwrap(); + assert_eq!(out.y, vec![1, 2, 3, 4]); + assert_eq!(out.u, vec![10]); + assert_eq!(out.v, vec![20]); + } + + #[test] + fn copy_nv12_planes_preserves_nv12_layout() { + let src = [1u8, 2, 3, 4, 10, 20]; + let mut y = [0u8; 4]; + let mut uv = [0u8; 2]; + assert!(copy_nv12_planes(&src, 2, 2, 2, 2, &mut y, &mut uv, 2, 2)); + assert_eq!(y, [1, 2, 3, 4]); + assert_eq!(uv, [10, 20]); + } + + #[test] + fn copy_nv12_planes_respects_destination_stride() { + let src = [1u8, 2, 0xFF, 0xFF, 3, 4, 0xFF, 0xFF, 10, 20, 0xFF, 0xFF]; + let mut y = [0u8; 8]; + let mut uv = [0u8; 4]; + assert!(copy_nv12_planes(&src, 2, 2, 4, 4, &mut y, &mut uv, 4, 4)); + assert_eq!(y, [1, 2, 0, 0, 3, 4, 0, 0]); + assert_eq!(uv, [10, 20, 0, 0]); + } + + #[test] + fn copy_nv12_planes_rejects_short_buffers() { + let src = [0u8; 6]; + let mut y = [0u8; 3]; + let mut uv = [0u8; 2]; + assert!(!copy_nv12_planes(&src, 2, 2, 2, 2, &mut y, &mut uv, 2, 2)); + let mut y = [0u8; 4]; + assert!(!copy_nv12_planes( + &src[..5], + 2, + 2, + 2, + 2, + &mut y, + &mut uv, + 2, + 2 + )); + } + + fn solid_bgra(width: u32, height: u32, b: u8, g: u8, r: u8) -> Vec { + let mut v = Vec::with_capacity((width * height * 4) as usize); + for _ in 0..(width * height) { + v.extend([b, g, r, 255]); + } + v + } + + fn near(a: u8, b: u8, tol: i32) -> bool { + (a as i32 - b as i32).abs() <= tol + } + + #[test] + fn bgra_black_white_grey_levels() { + let black = bgra_to_i420(&solid_bgra(2, 2, 0, 0, 0), 2, 2, 8).unwrap(); + assert!(near(black.y[0], 16, 1), "black Y={}", black.y[0]); + assert!(near(black.u[0], 128, 1) && near(black.v[0], 128, 1)); + + let white = bgra_to_i420(&solid_bgra(2, 2, 255, 255, 255), 2, 2, 8).unwrap(); + assert!(near(white.y[0], 235, 2), "white Y={}", white.y[0]); + assert!(near(white.u[0], 128, 2) && near(white.v[0], 128, 2)); + } + + #[test] + fn bgra_primaries_have_expected_chroma_signs() { + let red = bgra_to_i420(&solid_bgra(2, 2, 0, 0, 255), 2, 2, 8).unwrap(); + assert!(red.v[0] > 200, "red V={}", red.v[0]); + let blue = bgra_to_i420(&solid_bgra(2, 2, 255, 0, 0), 2, 2, 8).unwrap(); + assert!(blue.u[0] > 200, "blue U={}", blue.u[0]); + let green = bgra_to_i420(&solid_bgra(2, 2, 0, 255, 0), 2, 2, 8).unwrap(); + assert!( + green.u[0] < 60 && green.v[0] < 60, + "green U={} V={}", + green.u[0], + green.v[0] + ); + } + + #[test] + fn bgra_plane_sizes() { + let out = bgra_to_i420(&solid_bgra(8, 6, 10, 20, 30), 8, 6, 32).unwrap(); + assert_eq!(out.y.len(), 8 * 6); + assert_eq!(out.u.len(), 4 * 3); + assert_eq!(out.v.len(), 4 * 3); + } + + #[test] + fn bgra_golden_2x2_solid_colour_exact_bytes() { + let src = solid_bgra(2, 2, 32, 64, 128); + let out = bgra_to_i420(&src, 2, 2, 8).unwrap(); + assert_eq!(out.width, 2); + assert_eq!(out.height, 2); + assert_eq!(out.y, vec![84, 84, 84, 84]); + assert_eq!(out.u, vec![105]); + assert_eq!(out.v, vec![158]); + } + + #[test] + fn bgra_golden_strided_2x2_skips_row_padding() { + let mut src = vec![0xFFu8; 16 * 2]; + for px in 0..2 { + let o = px * 4; + src[o..o + 4].copy_from_slice(&[0, 0, 0, 255]); + } + for px in 0..2 { + let o = 16 + px * 4; + src[o..o + 4].copy_from_slice(&[255, 255, 255, 255]); + } + let out = bgra_to_i420(&src, 2, 2, 16).unwrap(); + assert!( + near(out.y[0], 16, 1) && near(out.y[1], 16, 1), + "row0 Y={:?}", + &out.y[0..2] + ); + assert!( + near(out.y[2], 235, 2) && near(out.y[3], 235, 2), + "row1 Y={:?}", + &out.y[2..4] + ); + assert!( + near(out.u[0], 128, 2) && near(out.v[0], 128, 2), + "U={} V={}", + out.u[0], + out.v[0] + ); + } + + #[test] + fn bgra_to_i420_planes_matches_tight_conversion_with_destination_padding() { + let src = solid_bgra(4, 2, 32, 64, 128); + let tight = bgra_to_i420(&src, 4, 2, 16).unwrap(); + let mut y = [0u8; 10]; + let mut u = [0u8; 4]; + let mut v = [0u8; 4]; + assert!(bgra_to_i420_planes( + &src, 4, 2, 16, &mut y, &mut u, &mut v, 5, 2, 2 + )); + assert_eq!(&y[0..4], &tight.y[0..4]); + assert_eq!(&y[5..9], &tight.y[4..8]); + assert_eq!(&u[0..2], &tight.u[0..2]); + assert_eq!(&v[0..2], &tight.v[0..2]); + } + + #[test] + fn copy_tight_i420_into_reuses_existing_plane_storage() { + let mut dst = I420::new(4, 2).unwrap(); + let ptrs = (dst.y.as_ptr(), dst.u.as_ptr(), dst.v.as_ptr()); + let src: Vec = (0u8..12).collect(); + + assert_eq!(tight_i420_byte_len(4, 2), Some(12)); + assert!(copy_tight_i420_into(&src, 4, 2, &mut dst)); + + assert_eq!(dst.y.as_ptr(), ptrs.0); + assert_eq!(dst.u.as_ptr(), ptrs.1); + assert_eq!(dst.v.as_ptr(), ptrs.2); + assert_eq!(dst.y, vec![0, 1, 2, 3, 4, 5, 6, 7]); + assert_eq!(dst.u, vec![8, 9]); + assert_eq!(dst.v, vec![10, 11]); + } + + fn solid_rgb(width: u32, height: u32, r: u8, g: u8, b: u8) -> Vec { + let mut v = Vec::with_capacity((width * height * 3) as usize); + for _ in 0..(width * height) { + v.extend([r, g, b]); + } + v + } + + #[test] + fn rgb_rejects_odd_dims_and_short_buffer() { + assert!(rgb_to_i420(&[0u8; 64], 3, 2).is_none()); + assert!(rgb_to_i420(&[0u8; 64], 2, 1).is_none()); + assert!(rgb_to_i420(&[0u8; 11], 2, 2).is_none()); + assert!(rgb_to_i420(&[0u8; 12], 2, 2).is_some()); + } + + #[test] + fn rgb_golden_2x2_solid_colour_matches_bgra_path() { + let out = rgb_to_i420(&solid_rgb(2, 2, 128, 64, 32), 2, 2).unwrap(); + assert_eq!(out.width, 2); + assert_eq!(out.height, 2); + assert_eq!(out.y, vec![84, 84, 84, 84]); + assert_eq!(out.u, vec![105]); + assert_eq!(out.v, vec![158]); + } + + #[test] + fn rgb_plane_sizes_and_levels() { + let black = rgb_to_i420(&solid_rgb(2, 2, 0, 0, 0), 2, 2).unwrap(); + assert!(near(black.y[0], 16, 1)); + assert!(near(black.u[0], 128, 1) && near(black.v[0], 128, 1)); + let white = rgb_to_i420(&solid_rgb(4, 4, 255, 255, 255), 4, 4).unwrap(); + assert_eq!(white.y.len(), 16); + assert_eq!(white.u.len(), 4); + assert_eq!(white.v.len(), 4); + assert!(near(white.y[0], 235, 2)); + } + + #[test] + fn nv12_golden_4x2_packed_to_i420() { + let mut src = Vec::new(); + src.extend(0u8..8); + src.extend([40, 41, 42, 43]); + let out = nv12_to_i420(&src, 4, 2, 4, 4).unwrap(); + assert_eq!(out.width, 4); + assert_eq!(out.height, 2); + assert_eq!(out.y, (0u8..8).collect::>()); + assert_eq!(out.u, vec![40, 42]); + assert_eq!(out.v, vec![41, 43]); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/tests/event_contract.rs b/fluxer_desktop/native/webrtc-sender/tests/event_contract.rs new file mode 100644 index 0000000..f78e7e9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/tests/event_contract.rs @@ -0,0 +1,520 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use serde_json::Value; + +const OUTBOUND: &str = include_str!("fixtures/outbound_events.json"); +const INBOUND: &str = include_str!("fixtures/inbound_events.json"); +const VIDEO_FRAME_META: &str = include_str!("fixtures/video_frame_meta.json"); + +const TRACK_KINDS: &[&str] = &["audio", "video"]; +const TRACK_SOURCES: &[&str] = &[ + "unknown", + "camera", + "microphone", + "screen_share", + "screen_share_audio", +]; +const QUALITIES: &[&str] = &["excellent", "good", "poor", "lost"]; +const SUBSCRIPTION_STATUSES: &[&str] = &["desired", "subscribed", "unsubscribed"]; + +fn records(json: &str) -> Vec<(String, Value)> { + let parsed: Value = serde_json::from_str(json).expect("fixture is valid JSON"); + parsed + .as_array() + .expect("fixture is a JSON array") + .iter() + .map(|rec| { + let ty = rec + .get("eventType") + .and_then(Value::as_str) + .expect("record has an eventType string") + .to_string(); + let payload = rec.get("payload").expect("record has a payload").clone(); + (ty, payload) + }) + .collect() +} + +fn assert_exact_keys(event_type: &str, payload: &Value, expected: &[&str]) { + let obj = payload + .as_object() + .unwrap_or_else(|| panic!("{event_type}: payload is not a JSON object")); + let mut got: Vec<&str> = obj.keys().map(String::as_str).collect(); + got.sort_unstable(); + let mut want: Vec<&str> = expected.to_vec(); + want.sort_unstable(); + assert_eq!(got, want, "{event_type}: payload keys mismatch"); +} + +fn str_field<'a>(event_type: &str, payload: &'a Value, key: &str) -> &'a str { + payload + .get(key) + .and_then(Value::as_str) + .unwrap_or_else(|| panic!("{event_type}.{key} is not a string")) +} + +fn bool_field(event_type: &str, payload: &Value, key: &str) -> bool { + payload + .get(key) + .and_then(Value::as_bool) + .unwrap_or_else(|| panic!("{event_type}.{key} is not a boolean")) +} + +fn validate_string_map(event_type: &str, payload: &Value, key: &str) { + let obj = payload + .get(key) + .and_then(Value::as_object) + .unwrap_or_else(|| panic!("{event_type}.{key} is not an object")); + for (map_key, value) in obj { + assert!( + !map_key.is_empty(), + "{event_type}.{key} contains an empty key" + ); + assert!( + value.as_str().is_some(), + "{event_type}.{key}.{map_key} is not a string" + ); + } +} + +fn validate_participant_snapshot(event_type: &str, payload: &Value) { + assert_exact_keys(event_type, payload, &["sid", "identity", "name"]); + assert!(str_field(event_type, payload, "sid").starts_with("PA_")); + let _ = str_field(event_type, payload, "identity"); + let _ = str_field(event_type, payload, "name"); +} + +fn validate_track_payload(event_type: &str, payload: &Value, includes_subscription: bool) { + let mut keys = vec![ + "participantSid", + "identity", + "participantName", + "trackSid", + "trackName", + "kind", + "source", + "muted", + ]; + if includes_subscription { + keys.extend(["subscribed", "subscriptionStatus"]); + } + assert_exact_keys(event_type, payload, &keys); + assert!(str_field(event_type, payload, "participantSid").starts_with("PA_")); + let _ = str_field(event_type, payload, "identity"); + let _ = str_field(event_type, payload, "participantName"); + assert!(str_field(event_type, payload, "trackSid").starts_with("TR_")); + let _ = str_field(event_type, payload, "trackName"); + assert!(TRACK_KINDS.contains(&str_field(event_type, payload, "kind"))); + assert!(TRACK_SOURCES.contains(&str_field(event_type, payload, "source"))); + let _ = bool_field(event_type, payload, "muted"); + if includes_subscription { + let _ = bool_field(event_type, payload, "subscribed"); + assert!(SUBSCRIPTION_STATUSES.contains(&str_field( + event_type, + payload, + "subscriptionStatus" + ))); + } +} + +fn validate_record(event_type: &str, payload: &Value) { + match event_type { + "connected" | "Reconnecting" | "Reconnected" => assert_exact_keys(event_type, payload, &[]), + "connectionState" => { + assert_exact_keys(event_type, payload, &["state"]); + assert!(!str_field(event_type, payload, "state").is_empty()); + } + "disconnected" => { + assert_exact_keys(event_type, payload, &["reason"]); + assert!(!str_field(event_type, payload, "reason").is_empty()); + } + "participantJoined" => { + assert_exact_keys(event_type, payload, &["sid", "identity", "name"]); + assert!(str_field(event_type, payload, "sid").starts_with("PA_")); + let _ = str_field(event_type, payload, "identity"); + let _ = str_field(event_type, payload, "name"); + } + "participantLeft" => { + assert_exact_keys(event_type, payload, &["sid", "identity", "name"]); + assert!(str_field(event_type, payload, "sid").starts_with("PA_")); + let _ = str_field(event_type, payload, "name"); + } + "trackPublished" | "trackUnpublished" | "trackSubscribed" | "trackUnsubscribed" => { + validate_track_payload(event_type, payload, true); + } + "trackMuted" | "trackUnmuted" => { + validate_track_payload(event_type, payload, false); + assert_eq!( + bool_field(event_type, payload, "muted"), + event_type == "trackMuted" + ); + } + "trackSubscriptionFailed" => { + let has_publication = payload.get("kind").is_some(); + if has_publication { + assert_exact_keys( + event_type, + payload, + &[ + "participantSid", + "identity", + "participantName", + "trackSid", + "trackName", + "kind", + "source", + "muted", + "subscribed", + "subscriptionStatus", + "error", + ], + ); + } else { + assert_exact_keys( + event_type, + payload, + &[ + "participantSid", + "identity", + "participantName", + "trackSid", + "error", + ], + ); + } + assert!(str_field(event_type, payload, "participantSid").starts_with("PA_")); + assert!(str_field(event_type, payload, "trackSid").starts_with("TR_")); + if has_publication { + assert!(TRACK_KINDS.contains(&str_field(event_type, payload, "kind"))); + assert!(TRACK_SOURCES.contains(&str_field(event_type, payload, "source"))); + assert!(SUBSCRIPTION_STATUSES.contains(&str_field( + event_type, + payload, + "subscriptionStatus" + ))); + let _ = bool_field(event_type, payload, "muted"); + let _ = bool_field(event_type, payload, "subscribed"); + } + assert!(!str_field(event_type, payload, "error").is_empty()); + } + "localTrackPublished" => { + validate_track_payload(event_type, payload, false); + } + "localTrackUnpublished" => { + validate_track_payload(event_type, payload, false); + } + "localTrackRepublished" => { + let keys = [ + "participantSid", + "identity", + "participantName", + "previousTrackSid", + "trackSid", + "trackName", + "kind", + "source", + "muted", + ]; + assert_exact_keys(event_type, payload, &keys); + assert!(str_field(event_type, payload, "participantSid").starts_with("PA_")); + assert!(str_field(event_type, payload, "previousTrackSid").starts_with("TR_")); + assert!(str_field(event_type, payload, "trackSid").starts_with("TR_")); + assert!(TRACK_KINDS.contains(&str_field(event_type, payload, "kind"))); + assert!(TRACK_SOURCES.contains(&str_field(event_type, payload, "source"))); + let _ = bool_field(event_type, payload, "muted"); + } + "activeSpeakers" => { + assert_exact_keys(event_type, payload, &["sids", "participants"]); + let sids = payload + .get("sids") + .and_then(Value::as_array) + .expect("activeSpeakers.sids is an array"); + for sid in sids { + let sid = sid.as_str().expect("activeSpeakers.sids entry is a string"); + assert!( + sid.starts_with("PA_"), + "activeSpeakers sid {sid} lacks PA_ prefix" + ); + } + let participants = payload + .get("participants") + .and_then(Value::as_array) + .expect("activeSpeakers.participants is an array"); + for participant in participants { + validate_participant_snapshot("activeSpeakers.participants[]", participant); + } + } + "connectionQuality" => { + assert_exact_keys(event_type, payload, &["sid", "identity", "name", "quality"]); + assert!(str_field(event_type, payload, "sid").starts_with("PA_")); + let _ = str_field(event_type, payload, "identity"); + let _ = str_field(event_type, payload, "name"); + assert!(QUALITIES.contains(&str_field(event_type, payload, "quality"))); + } + "e2eeState" => { + assert_exact_keys(event_type, payload, &["sid", "identity", "name", "state"]); + assert!(str_field(event_type, payload, "sid").starts_with("PA_")); + let _ = str_field(event_type, payload, "identity"); + let _ = str_field(event_type, payload, "name"); + assert!(!str_field(event_type, payload, "state").is_empty()); + } + "stats" => { + assert_exact_keys(event_type, payload, &["rttMs", "outbound", "inbound"]); + let rtt = payload.get("rttMs").expect("stats.rttMs exists"); + assert!( + rtt.is_null() || rtt.as_f64().is_some(), + "stats.rttMs is null or number" + ); + let outbound = payload + .get("outbound") + .and_then(Value::as_array) + .expect("stats.outbound is an array"); + for entry in outbound { + let obj = entry + .as_object() + .expect("stats.outbound[] is a JSON object"); + assert!(obj.contains_key("trackSid")); + assert!(TRACK_SOURCES.contains(&str_field("stats.outbound[]", entry, "source"))); + assert!(TRACK_KINDS.contains(&str_field("stats.outbound[]", entry, "kind"))); + assert!(entry.get("bitrateKbps").and_then(Value::as_f64).is_some()); + assert!(entry.get("packetsLost").and_then(Value::as_u64).is_some()); + } + let inbound = payload + .get("inbound") + .and_then(Value::as_array) + .expect("stats.inbound is an array"); + for entry in inbound { + let obj = entry.as_object().expect("stats.inbound[] is a JSON object"); + assert!(obj.contains_key("participantSid")); + assert!(obj.contains_key("trackSid")); + assert!(TRACK_KINDS.contains(&str_field("stats.inbound[]", entry, "kind"))); + assert!(entry.get("bitrateKbps").and_then(Value::as_f64).is_some()); + assert!(entry.get("packetsLost").and_then(Value::as_u64).is_some()); + } + } + "audioPlaybackUnavailable" => { + assert_exact_keys(event_type, payload, &["message"]); + assert!(!str_field(event_type, payload, "message").is_empty()); + } + "participantNameChanged" => { + assert_exact_keys(event_type, payload, &["sid", "identity", "oldName", "name"]); + assert!(str_field(event_type, payload, "sid").starts_with("PA_")); + let _ = str_field(event_type, payload, "identity"); + let _ = str_field(event_type, payload, "oldName"); + let _ = str_field(event_type, payload, "name"); + } + "participantMetadataChanged" => { + assert_exact_keys( + event_type, + payload, + &[ + "sid", + "identity", + "name", + "oldMetadata", + "metadata", + "attributes", + ], + ); + assert!(str_field(event_type, payload, "sid").starts_with("PA_")); + let _ = str_field(event_type, payload, "identity"); + let _ = str_field(event_type, payload, "name"); + let _ = str_field(event_type, payload, "oldMetadata"); + let _ = str_field(event_type, payload, "metadata"); + validate_string_map(event_type, payload, "attributes"); + } + "participantAttributesChanged" => { + assert_exact_keys( + event_type, + payload, + &["sid", "identity", "name", "attributes", "changedAttributes"], + ); + assert!(str_field(event_type, payload, "sid").starts_with("PA_")); + let _ = str_field(event_type, payload, "identity"); + let _ = str_field(event_type, payload, "name"); + validate_string_map(event_type, payload, "attributes"); + validate_string_map(event_type, payload, "changedAttributes"); + } + other => panic!("unknown eventType in fixture: {other}"), + } +} + +#[test] +fn outbound_fixtures_match_contract() { + let recs = records(OUTBOUND); + assert!(!recs.is_empty(), "outbound fixture is non-empty"); + for (ty, payload) in &recs { + validate_record(ty, payload); + } +} + +#[test] +fn inbound_fixtures_match_contract() { + let recs = records(INBOUND); + assert!(!recs.is_empty(), "inbound fixture is non-empty"); + for (ty, payload) in &recs { + validate_record(ty, payload); + } +} + +#[test] +fn outbound_covers_the_single_identity_publish_path() { + let types: Vec = records(OUTBOUND).into_iter().map(|(ty, _)| ty).collect(); + for expected in [ + "connected", + "connectionState", + "Reconnecting", + "Reconnected", + "localTrackPublished", + "localTrackUnpublished", + "localTrackRepublished", + "e2eeState", + "activeSpeakers", + "stats", + "audioPlaybackUnavailable", + ] { + assert!( + types.iter().any(|t| t == expected), + "outbound missing {expected}" + ); + } + let published: Vec<(String, String)> = records(OUTBOUND) + .into_iter() + .filter(|(ty, _)| ty == "localTrackPublished") + .map(|(_, p)| { + ( + p.get("kind").and_then(Value::as_str).unwrap().to_string(), + p.get("source").and_then(Value::as_str).unwrap().to_string(), + ) + }) + .collect(); + assert!(published.contains(&("video".into(), "screen_share".into()))); + assert!(published.contains(&("audio".into(), "microphone".into()))); +} + +#[test] +fn inbound_covers_subscribe_and_lifecycle() { + let types: Vec = records(INBOUND).into_iter().map(|(ty, _)| ty).collect(); + for expected in [ + "participantJoined", + "participantNameChanged", + "participantMetadataChanged", + "participantAttributesChanged", + "trackPublished", + "trackSubscribed", + "trackMuted", + "trackUnmuted", + "trackSubscriptionFailed", + "trackUnsubscribed", + "trackUnpublished", + "e2eeState", + "activeSpeakers", + "connectionQuality", + "participantLeft", + ] { + assert!( + types.iter().any(|t| t == expected), + "inbound missing {expected}" + ); + } +} + +#[test] +fn video_frame_meta_matches_contract() { + let parsed: Value = serde_json::from_str(VIDEO_FRAME_META).expect("fixture is valid JSON"); + let recs = parsed.as_array().expect("video_frame_meta is a JSON array"); + assert!(!recs.is_empty(), "video_frame_meta fixture is non-empty"); + for meta in recs { + let obj = meta.as_object().expect("meta is a JSON object"); + let mut got: Vec<&str> = obj.keys().map(String::as_str).collect(); + got.sort_unstable(); + assert_eq!( + got, + vec![ + "bridgeVersion", + "height", + "participantSid", + "source", + "timestampUs", + "trackName", + "trackSid", + "width" + ], + "video_frame_meta key set mismatch" + ); + let bridge_version = obj + .get("bridgeVersion") + .and_then(Value::as_u64) + .expect("bridgeVersion is an unsigned integer"); + assert!( + bridge_version >= 1, + "bridgeVersion {bridge_version} must be positive" + ); + let participant_sid = obj + .get("participantSid") + .and_then(Value::as_str) + .expect("participantSid is a string"); + let track_sid = obj + .get("trackSid") + .and_then(Value::as_str) + .expect("trackSid is a string"); + let track_name = obj + .get("trackName") + .and_then(Value::as_str) + .expect("trackName is a string"); + let source = obj + .get("source") + .and_then(Value::as_str) + .expect("source is a string"); + assert!( + participant_sid.starts_with("PA_"), + "participantSid {participant_sid} lacks PA_" + ); + assert!( + track_sid.starts_with("TR_"), + "trackSid {track_sid} lacks TR_" + ); + assert!(!track_name.is_empty(), "trackName is not empty"); + assert!( + TRACK_SOURCES.contains(&source), + "source {source} is a known LiveKit source" + ); + let width = obj + .get("width") + .and_then(Value::as_u64) + .expect("width is an integer"); + let height = obj + .get("height") + .and_then(Value::as_u64) + .expect("height is an integer"); + assert!( + width >= 2 && width % 2 == 0, + "width {width} must be even and >= 2" + ); + assert!( + height >= 2 && height % 2 == 0, + "height {height} must be even and >= 2" + ); + assert!( + obj.get("timestampUs").and_then(Value::as_i64).is_some(), + "timestampUs is an i64-range integer" + ); + } + assert!( + recs.iter() + .filter_map(|m| m.get("timestampUs").and_then(Value::as_i64)) + .any(|ts| ts > u32::MAX as i64), + "expect a >2^32 timestampUs in the fixture to lock the i64 contract" + ); +} + +#[test] +fn e2ee_state_is_ok_on_the_wire() { + for json in [OUTBOUND, INBOUND] { + for (ty, payload) in records(json) { + if ty == "e2eeState" { + assert_eq!(payload.get("state").and_then(Value::as_str), Some("ok")); + } + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/tests/fixtures/inbound_events.json b/fluxer_desktop/native/webrtc-sender/tests/fixtures/inbound_events.json new file mode 100644 index 0000000..580487c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/tests/fixtures/inbound_events.json @@ -0,0 +1,234 @@ +[ + { + "eventType": "participantJoined", + "payload": { + "sid": "PA_remoteB12345", + "identity": "1428486264293441600", + "name": "Test User B" + } + }, + { + "eventType": "participantNameChanged", + "payload": { + "sid": "PA_remoteB12345", + "identity": "1428486264293441600", + "oldName": "Test User", + "name": "Test User B" + } + }, + { + "eventType": "participantMetadataChanged", + "payload": { + "sid": "PA_remoteB12345", + "identity": "1428486264293441600", + "name": "Test User B", + "oldMetadata": "", + "metadata": "{\"status\":\"presenting\"}", + "attributes": { + "role": "speaker" + } + } + }, + { + "eventType": "participantAttributesChanged", + "payload": { + "sid": "PA_remoteB12345", + "identity": "1428486264293441600", + "name": "Test User B", + "attributes": { + "role": "speaker", + "hand": "raised" + }, + "changedAttributes": { + "hand": "raised" + } + } + }, + { + "eventType": "trackPublished", + "payload": { + "participantSid": "PA_remoteB12345", + "identity": "1428486264293441600", + "participantName": "Test User B", + "trackSid": "TR_remoteVideo01", + "trackName": "remote-screen", + "kind": "video", + "source": "screen_share", + "muted": false, + "subscribed": false, + "subscriptionStatus": "desired" + } + }, + { + "eventType": "trackSubscribed", + "payload": { + "participantSid": "PA_remoteB12345", + "identity": "1428486264293441600", + "participantName": "Test User B", + "trackSid": "TR_remoteVideo01", + "trackName": "remote-screen", + "kind": "video", + "source": "screen_share", + "muted": false, + "subscribed": true, + "subscriptionStatus": "subscribed" + } + }, + { + "eventType": "trackPublished", + "payload": { + "participantSid": "PA_remoteB12345", + "identity": "1428486264293441600", + "participantName": "Test User B", + "trackSid": "TR_remoteAudio01", + "trackName": "remote-microphone", + "kind": "audio", + "source": "microphone", + "muted": false, + "subscribed": false, + "subscriptionStatus": "desired" + } + }, + { + "eventType": "trackSubscribed", + "payload": { + "participantSid": "PA_remoteB12345", + "identity": "1428486264293441600", + "participantName": "Test User B", + "trackSid": "TR_remoteAudio01", + "trackName": "remote-microphone", + "kind": "audio", + "source": "microphone", + "muted": false, + "subscribed": true, + "subscriptionStatus": "subscribed" + } + }, + { + "eventType": "trackMuted", + "payload": { + "participantSid": "PA_remoteB12345", + "identity": "1428486264293441600", + "participantName": "Test User B", + "trackSid": "TR_remoteAudio01", + "trackName": "remote-microphone", + "kind": "audio", + "source": "microphone", + "muted": true + } + }, + { + "eventType": "trackUnmuted", + "payload": { + "participantSid": "PA_remoteB12345", + "identity": "1428486264293441600", + "participantName": "Test User B", + "trackSid": "TR_remoteAudio01", + "trackName": "remote-microphone", + "kind": "audio", + "source": "microphone", + "muted": false + } + }, + { + "eventType": "trackSubscriptionFailed", + "payload": { + "participantSid": "PA_remoteB12345", + "identity": "1428486264293441600", + "participantName": "Test User B", + "trackSid": "TR_remoteCamera01", + "trackName": "remote-camera", + "kind": "video", + "source": "camera", + "muted": false, + "subscribed": false, + "subscriptionStatus": "desired", + "error": "could not find published track with sid: \"TR_remoteCamera01\"" + } + }, + { + "eventType": "trackSubscriptionFailed", + "payload": { + "participantSid": "PA_remoteB12345", + "identity": "1428486264293441600", + "participantName": "Test User B", + "trackSid": "TR_missingPublication01", + "error": "could not find published track with sid: \"TR_missingPublication01\"" + } + }, + { + "eventType": "e2eeState", + "payload": { + "sid": "PA_remoteB12345", + "identity": "1428486264293441600", + "name": "Test User B", + "state": "ok" + } + }, + { + "eventType": "activeSpeakers", + "payload": { + "sids": ["PA_remoteB12345"], + "participants": [ + { + "sid": "PA_remoteB12345", + "identity": "1428486264293441600", + "name": "Test User B" + } + ] + } + }, + { + "eventType": "connectionQuality", + "payload": { + "sid": "PA_remoteB12345", + "identity": "1428486264293441600", + "name": "Test User B", + "quality": "excellent" + } + }, + { + "eventType": "trackUnsubscribed", + "payload": { + "participantSid": "PA_remoteB12345", + "identity": "1428486264293441600", + "participantName": "Test User B", + "trackSid": "TR_remoteVideo01", + "trackName": "remote-screen", + "kind": "video", + "source": "screen_share", + "muted": false, + "subscribed": false, + "subscriptionStatus": "unsubscribed" + } + }, + { + "eventType": "trackUnpublished", + "payload": { + "participantSid": "PA_remoteB12345", + "identity": "1428486264293441600", + "participantName": "Test User B", + "trackSid": "TR_remoteVideo01", + "trackName": "remote-screen", + "kind": "video", + "source": "screen_share", + "muted": false, + "subscribed": false, + "subscriptionStatus": "unsubscribed" + } + }, + { + "eventType": "participantLeft", + "payload": { + "sid": "PA_remoteB12345", + "identity": "1428486264293441600", + "name": "Test User B" + } + }, + { + "eventType": "disconnected", + "payload": { + "reason": "clientinitiated" + } + } +] diff --git a/fluxer_desktop/native/webrtc-sender/tests/fixtures/outbound_events.json b/fluxer_desktop/native/webrtc-sender/tests/fixtures/outbound_events.json new file mode 100644 index 0000000..980f87d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/tests/fixtures/outbound_events.json @@ -0,0 +1,134 @@ +[ + { + "eventType": "connected", + "payload": {} + }, + { + "eventType": "connectionState", + "payload": { + "state": "connected" + } + }, + { + "eventType": "Reconnecting", + "payload": {} + }, + { + "eventType": "Reconnected", + "payload": {} + }, + { + "eventType": "localTrackPublished", + "payload": { + "participantSid": "PA_77jesWWtSMge", + "identity": "1428486264293441599", + "participantName": "Test User A", + "trackSid": "TR_VShJ9y2A49ai6i", + "trackName": "local-screen", + "kind": "video", + "source": "screen_share", + "muted": false + } + }, + { + "eventType": "localTrackPublished", + "payload": { + "participantSid": "PA_77jesWWtSMge", + "identity": "1428486264293441599", + "participantName": "Test User A", + "trackSid": "TR_AMannbg6PLtcVz", + "trackName": "local-microphone", + "kind": "audio", + "source": "microphone", + "muted": false + } + }, + { + "eventType": "localTrackUnpublished", + "payload": { + "participantSid": "PA_77jesWWtSMge", + "identity": "1428486264293441599", + "participantName": "Test User A", + "trackSid": "TR_AMannbg6PLtcVz", + "trackName": "local-microphone", + "kind": "audio", + "source": "microphone", + "muted": false + } + }, + { + "eventType": "localTrackRepublished", + "payload": { + "participantSid": "PA_77jesWWtSMge", + "identity": "1428486264293441599", + "participantName": "Test User A", + "previousTrackSid": "TR_VShJ9y2A49ai6i", + "trackSid": "TR_VSsTf93n5GqJrK", + "trackName": "local-screen", + "kind": "video", + "source": "screen_share", + "muted": false + } + }, + { + "eventType": "e2eeState", + "payload": { + "sid": "PA_77jesWWtSMge", + "identity": "1428486264293441599", + "name": "Test User A", + "state": "ok" + } + }, + { + "eventType": "activeSpeakers", + "payload": { + "sids": ["PA_77jesWWtSMge"], + "participants": [ + { + "sid": "PA_77jesWWtSMge", + "identity": "1428486264293441599", + "name": "Test User A" + } + ] + } + }, + { + "eventType": "stats", + "payload": { + "rttMs": 24.5, + "outbound": [ + { + "trackSid": "TR_VShJ9y2A49ai6i", + "source": "screen_share", + "kind": "video", + "bitrateKbps": 2500, + "packetsLost": 0, + "fps": 60 + }, + { + "trackSid": "TR_AMannbg6PLtcVz", + "source": "microphone", + "kind": "audio", + "bitrateKbps": 48, + "packetsLost": 1 + } + ], + "inbound": [ + { + "participantSid": "PA_Subscriber", + "trackSid": "TR_RemoteVideo", + "kind": "video", + "bitrateKbps": 1200, + "packetsLost": 0, + "jitterMs": 3.5 + } + ] + } + }, + { + "eventType": "audioPlaybackUnavailable", + "payload": { + "message": "platform audio unavailable: test fixture" + } + } +] diff --git a/fluxer_desktop/native/webrtc-sender/tests/fixtures/video_frame_meta.json b/fluxer_desktop/native/webrtc-sender/tests/fixtures/video_frame_meta.json new file mode 100644 index 0000000..1c16956 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/tests/fixtures/video_frame_meta.json @@ -0,0 +1,32 @@ +[ + { + "bridgeVersion": 14, + "participantSid": "PA_remoteB12345", + "trackSid": "TR_remoteVideo01", + "trackName": "screen", + "source": "screen_share", + "width": 1920, + "height": 1080, + "timestampUs": 123456789 + }, + { + "bridgeVersion": 14, + "participantSid": "PA_77jesWWtSMge", + "trackSid": "TR_VShJ9y2A49ai6i", + "trackName": "camera", + "source": "camera", + "width": 1280, + "height": 720, + "timestampUs": 0 + }, + { + "bridgeVersion": 14, + "participantSid": "PA_remoteB12345", + "trackSid": "TR_remoteVideo02", + "trackName": "screen", + "source": "screen_share", + "width": 640, + "height": 360, + "timestampUs": 4294967296 + } +] diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/.cargo-ok b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/.cargo-ok new file mode 100644 index 0000000..5f8b795 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/.cargo-ok @@ -0,0 +1 @@ +{"v":1} \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/.cargo_vcs_info.json b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/.cargo_vcs_info.json new file mode 100644 index 0000000..58c37db --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "ba0c31290ad6f7836ef96efd62167c3501ed07fa" + }, + "path_in_vcs": "libwebrtc" +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/CHANGELOG.md b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/CHANGELOG.md new file mode 100644 index 0000000..11b57de --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/CHANGELOG.md @@ -0,0 +1,293 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.3.26](https://github.com/livekit/rust-sdks/compare/rust-sdks/libwebrtc@0.3.25...rust-sdks/libwebrtc@0.3.26) - 2026-02-16 + +### Other + +- add is_screencast to VideoSource ([#896](https://github.com/livekit/rust-sdks/pull/896)) + +## [0.3.25](https://github.com/livekit/rust-sdks/compare/rust-sdks/libwebrtc@0.3.24...rust-sdks/libwebrtc@0.3.25) - 2026-02-09 + +### Fixed + +- fix the 440->441 samples issue and pass a noop callback for release ([#848](https://github.com/livekit/rust-sdks/pull/848)) + +### Other + +- Use workspace dependencies & settings ([#856](https://github.com/livekit/rust-sdks/pull/856)) +- allow apm >=10ms frames ([#843](https://github.com/livekit/rust-sdks/pull/843)) + +## [0.3.24](https://github.com/livekit/rust-sdks/compare/rust-sdks/libwebrtc@0.3.23...rust-sdks/libwebrtc@0.3.24) - 2026-01-15 + +### Other + +- updated the following local packages: webrtc-sys + +## [0.3.23](https://github.com/livekit/rust-sdks/compare/rust-sdks/libwebrtc@0.3.22...rust-sdks/libwebrtc@0.3.23) - 2025-12-19 + +### Fixed + +- Exclude the desktop-capturer module link for mobile. ([#817](https://github.com/livekit/rust-sdks/pull/817)) + +## [0.3.22](https://github.com/livekit/rust-sdks/compare/rust-sdks/libwebrtc@0.3.21...rust-sdks/libwebrtc@0.3.22) - 2025-12-17 + +### Other + +- Expose WebRTC's audio_mixer ([#806](https://github.com/livekit/rust-sdks/pull/806)) + +## [0.3.21](https://github.com/livekit/rust-sdks/compare/rust-sdks/libwebrtc@0.3.20...rust-sdks/libwebrtc@0.3.21) - 2025-12-04 + +### Other + +- move starting/stopping GLib event loop into libwebrtc crate ([#798](https://github.com/livekit/rust-sdks/pull/798)) +- Expose desktop capturer ([#725](https://github.com/livekit/rust-sdks/pull/725)) + +## [0.3.20](https://github.com/livekit/rust-sdks/compare/rust-sdks/libwebrtc@0.3.19...rust-sdks/libwebrtc@0.3.20) - 2025-11-20 + +### Other + +- Fix the fast path in capture_frame function, without buffering ([#778](https://github.com/livekit/rust-sdks/pull/778)) + +## [0.3.19](https://github.com/livekit/rust-sdks/compare/rust-sdks/libwebrtc@0.3.18...rust-sdks/libwebrtc@0.3.19) - 2025-10-27 + +### Other + +- updated the following local packages: webrtc-sys + +## [0.3.18](https://github.com/livekit/rust-sdks/compare/rust-sdks/libwebrtc@0.3.17...rust-sdks/libwebrtc@0.3.18) - 2025-10-22 + +### Other + +- License check ([#746](https://github.com/livekit/rust-sdks/pull/746)) + +## [0.3.17](https://github.com/livekit/rust-sdks/compare/rust-sdks/libwebrtc@0.3.16...rust-sdks/libwebrtc@0.3.17) - 2025-10-13 + +### Added + +- *(e2ee)* add data channel encryption ([#708](https://github.com/livekit/rust-sdks/pull/708)) + +### Other + +- Enable buffer scaling ([#473](https://github.com/livekit/rust-sdks/pull/473)) + +## [0.3.16](https://github.com/livekit/rust-sdks/compare/rust-sdks/libwebrtc@0.3.15...rust-sdks/libwebrtc@0.3.16) - 2025-10-03 + +### Other + +- updated the following local packages: webrtc-sys + +## [0.3.15](https://github.com/livekit/rust-sdks/compare/rust-sdks/libwebrtc@0.3.14...rust-sdks/libwebrtc@0.3.15) - 2025-09-29 + +### Fixed + +- fix Builds/E2E Tests CI. ([#715](https://github.com/livekit/rust-sdks/pull/715)) + +## [0.3.14](https://github.com/livekit/rust-sdks/compare/rust-sdks/libwebrtc@0.3.13...rust-sdks/libwebrtc@0.3.14) - 2025-09-09 + +### Other + +- updated the following local packages: webrtc-sys + +## [0.3.13](https://github.com/livekit/rust-sdks/compare/rust-sdks/libwebrtc@0.3.12...rust-sdks/libwebrtc@0.3.13) - 2025-09-03 + +### Other + +- updated the following local packages: webrtc-sys +# Changelog + +## [0.3.12](https://github.com/livekit/rust-sdks/compare/rust-sdks/libwebrtc@0.3.11...rust-sdks/libwebrtc@0.3.12) - 2025-06-17 + +### Other + +- updated the following local packages: livekit-protocol, webrtc-sys + +## [0.3.11](https://github.com/livekit/rust-sdks/compare/rust-sdks/libwebrtc@0.3.10...rust-sdks/libwebrtc@0.3.11) - 2025-06-11 + +### Fixed + +- fix uint32 overflow ([#615](https://github.com/livekit/rust-sdks/pull/615)) + +### Other + +- remove ([#633](https://github.com/livekit/rust-sdks/pull/633)) +- expose apm stream_delay ([#616](https://github.com/livekit/rust-sdks/pull/616)) +- Add i420_to_nv12 ([#605](https://github.com/livekit/rust-sdks/pull/605)) +- ffi-v0.13.0 ([#590](https://github.com/livekit/rust-sdks/pull/590)) +- add AudioProcessingModule ([#580](https://github.com/livekit/rust-sdks/pull/580)) + +## [0.3.10] - 2025-02-05 + +### Fixed + +- Fix build issue + +## [0.3.9] - 2025-01-17 + +### Added + +- Expose DataChannel.bufferedAmount property + +## [0.3.8] - 2024-12-14 + +### Added + +- bump libwebrtc to m125 +## 0.3.35 (2026-05-29) + +### Fixes + +- Add native video pipeline timing instrumentation for local video measurements, exposing local publish and subscribe timing through async streams and subscriber overlay GPU upload and receive-to-GPU latency metrics through explicit timing observers. + +## 0.3.34 (2026-05-21) + +### Fixes + +#### feat: add Android application context initialization for PlatformAudio support. + +Android requires `ContextUtils.initialize(applicationContext)` before WebRTC audio components can be created. This change: + +- Adds `livekit_ffi_initialize_android_context()` C FFI function for Unity and other FFI consumers +- Uses `CreateAndroidAudioDeviceModule()` instead of generic `CreateAudioDeviceModule()` on Android +- Handles empty device GUIDs on Android (falls back to index 0) +- Documents Android-specific limitations: single default device, no app-level device selection + +Platform notes: +- Android device enumeration returns only one "default" device with empty name/GUID +- Audio routing (speaker/earpiece/Bluetooth) is controlled by Android's AudioManager, not WebRTC + +## 0.3.33 (2026-05-14) + +### Fixes + +- feat: add scalability mode for AV1/VP9. - #1076 (@cloudwebrtc) +- Add `LIVEKIT_PREFERRED_HW_ENCODER` to prefer `nvenc` or `vaapi` hardware video encoding when both are available. +- Relocate unrelated types out of `livekit-protocol` + +#### Get WebRTC ADM into Rust - #1037 (@xianshijing-lk) + +This PR introduces platform audio device management via WebRTC's Audio Device Module (ADM). + +#### Features +- **ADM Proxy**: New `AdmProxy` class that switches between Dummy ADM (synthetic mode) and Platform ADM (real audio I/O) +- **PlatformAudio API**: High-level Rust API for microphone capture and speaker playout with AEC/AGC/NS +- **Device enumeration**: List and select recording/playout devices by index or GUID +- **Mode switching**: Seamlessly switch between synthetic mode (FFI callbacks) and platform mode (native speakers) while audio is active +- **FFI platform audio support**: Expose platform audio device enumeration and selection through `livekit-ffi` +- **Audio processing**: Configure echo cancellation, noise suppression, and auto gain control with platform-specific defaults (hardware on iOS, software elsewhere) + +#### Audio Modes +| Mode | Recording | Playout | Use Case | +|------|-----------|---------|----------| +| Synthetic | NativeAudioSource | Dummy ADM + FFI | Unity audio, agents | +| Platform | Platform ADM mic | Platform ADM speakers | VoIP with AEC | + +#### API +```rust +// Create PlatformAudio for microphone/speaker access +let audio = PlatformAudio::new()?; + +// Enumerate and select devices +for i in 0..audio.recording_devices() as u16 { + println!("Mic {}: {}", i, audio.recording_device_name(i)); +} +audio.set_recording_device(0)?; + +// Create audio track for publishing +let track = LocalAudioTrack::create_audio_track("mic", audio.rtc_source()); +``` + +## 0.3.32 (2026-05-11) + +### Fixes + +- Upgrade protocol to v1.45.8 + +## 0.3.31 (2026-05-10) + +### Fixes + +- Fix missing `libwebrtc.jar` for Android builds, harden build scripts +- fix race in download_webrtc to reduce flaky build - #1047 (@hechen-eng) +- Improve WebRTC build scripts and add external_audio_source patch - #1053 (@xianshijing-lk) + +## 0.3.30 (2026-04-23) + +### Features + +#### Add support for frame level packet trailer + +##890 by @chenosaurus + +- Add support to attach/parse frame level timestamps & frame ID to VideoTracks as a custom payload trailer. +- Breaking change in VideoFrame API, must include `frame_metadata` or use VideoFrame::new(). + +## 0.3.29 (2026-04-02) + +### Features + +#### chore: upgrade libwebrtc to m144. + +##965 by @cloudwebrtc + +### Fixes + +#### use the bounded buffer for video stream + +##956 by @xianshijing-lk + +Before this PR, it uses an unbounded buffer for video stream, that will cause multiple problems: +1, video will be lagged behind if rendering is slow or just wake up from background +2, it will be out of sync with audio + +This PRs provides options to set a bounded buffer for video stream, and use 1 buffer as the default option. + +## 0.3.28 (2026-03-31) + +### Fixes + +- Upgrade to thiserror 2 + +#### fix: fix unavailable sem symbol for Linux aarch64. + +##975 by @cloudwebrtc + +## 0.3.27 (2026-03-22) + +### Features + +#### E2EE: allow setting key_ring_size and key_derivation_algorithm, update webrtc to m144 + +##921 by @onestacked + +This PR uses [this webrtc-sdk PR](https://github.com/webrtc-sdk/webrtc/pull/224) to configure the KDF. + +I've tested this with https://codeberg.org/esoteric_programmer/matrix-jukebox and it is compatible with Element Call. + +Fixed: https://github.com/livekit/rust-sdks/issues/796 + +### Fixes + +- Fix H.264 codec matching + +#### add bounded buffer to audio_stream, and use 10 frames as the default + +##945 by @xianshijing-lk + +#### fix clang build issue from zed patches (#949) + +##950 by @cloudwebrtc + +* webrtc-sys: Use clang instead of gcc + +* Debug CI output for aarch64-linux + +* ci: Install lld for aarch64-linux FFI builders + +* webrtc-sys: Disable CREL diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/Cargo.lock b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/Cargo.lock new file mode 100644 index 0000000..b597dca --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/Cargo.lock @@ -0,0 +1,2439 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cc" +version = "1.2.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-expr" +version = "0.20.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c6b04e07d8080154ed4ac03546d9a2b303cc2fe1901ba0b35b301516e289368" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "clap" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "codespan-reporting" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" +dependencies = [ + "serde", + "termcolor", + "unicode-width", +] + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "cxx" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747d8437319e3a2f43d93b341c137927ca70c0f5dabeea7a005a73665e247c7e" +dependencies = [ + "cc", + "cxx-build", + "cxxbridge-cmd", + "cxxbridge-flags", + "cxxbridge-macro", + "foldhash", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0f4697d190a142477b16aef7da8a99bfdc41e7e8b1687583c0d23a79c7afc1e" +dependencies = [ + "cc", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-cmd" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0956799fa8678d4c50eed028f2de1c0552ae183c76e976cf7ca8c4e36a7c328" +dependencies = [ + "clap", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23384a836ab4f0ad98ace7e3955ad2de39de42378ab487dc28d3990392cb283a" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6acc6b5822b9526adfb4fc377b67128fdd60aac757cc4a741a6278603f763cf" +dependencies = [ + "indexmap", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "env_filter" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "gio-sys" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0071fe88dba8e40086c8ff9bbb62622999f49628344b1d1bf490a48a29d80f22" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "windows-sys 0.61.2", +] + +[[package]] +name = "glib" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16de123c2e6c90ce3b573b7330de19be649080ec612033d397d72da265f1bd8b" +dependencies = [ + "bitflags", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "smallvec", +] + +[[package]] +name = "glib-macros" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf59b675301228a696fe01c3073974643365080a76cc3ed5bc2cbc466ad87f17" +dependencies = [ + "heck", + "proc-macro-crate", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "glib-sys" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d95e1a3a19ae464a7286e14af9a90683c64d70c02532d88d87ce95056af3e6c" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "gobject-sys" +version = "0.21.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dca35da0d19a18f4575f3cb99fe1c9e029a2941af5662f326f738a21edaf294" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "iri-string" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8e7418f59cc01c88316161279a7f665217ae316b388e58a0d10e29f54f1e5eb" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jiff" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "js-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.183" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" + +[[package]] +name = "libwebrtc" +version = "0.3.35" +dependencies = [ + "cxx", + "env_logger", + "glib", + "jni", + "js-sys", + "lazy_static", + "livekit-runtime", + "log", + "parking_lot", + "rtrb", + "serde", + "serde_json", + "thiserror 2.0.18", + "tokio", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webrtc-sys", +] + +[[package]] +name = "link-cplusplus" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f78c730aaa7d0b9336a299029ea49f9ee53b0ed06e9202e8cb7db9bae7b8c82" +dependencies = [ + "cc", +] + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "livekit-runtime" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532e84c6cdc5fe774f2b5d9912597b5f3bea561927a48296d03e24549d21c3f6" +dependencies = [ + "tokio", + "tokio-stream", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rtrb" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7204ed6420f698836b76d4d5c2ec5dec7585fd5c3a788fd1cde855d1de598239" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustls" +version = "0.23.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "scratch" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68f2ec51b097e4c1a75b681a8bec621909b5e91f15bb7b840c4f2f7b01148b2" + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_spanned" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "system-deps" +version = "7.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c8f33736f986f16d69b6cb8b03f55ddcad5c41acc4ccc39dd88e84aa805e7f" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df7f62577c25e07834649fc3b39fafdc597c0a3527dc1c60129201ccfcbaa50c" + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 0.7.15", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_datetime" +version = "1.1.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.25.8+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16bff38f1d86c47f9ff0647e6838d7bb362522bdf44006c7068c2b1e606f1f3c" +dependencies = [ + "indexmap", + "toml_datetime 1.1.0+spec-1.1.0", + "toml_parser", + "winnow 1.0.0", +] + +[[package]] +name = "toml_parser" +version = "1.1.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" +dependencies = [ + "winnow 1.0.0", +] + +[[package]] +name = "toml_writer" +version = "1.1.0+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed" + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "version-compare" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webrtc-sys" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7428e4bf6894a737d9f75555706e51b39cf207d53ad869d4f870cd953b96c2c" +dependencies = [ + "cc", + "cxx", + "cxx-build", + "glob", + "log", + "pkg-config", + "webrtc-sys-build", +] + +[[package]] +name = "webrtc-sys-build" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d46da6b5a5cbd091fae0400f77189f4ca4807c0d9442b85838a584f28720570" +dependencies = [ + "anyhow", + "fs2", + "regex", + "reqwest", + "scratch", + "semver", + "zip", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + +[[package]] +name = "winnow" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" +dependencies = [ + "memchr", +] + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "byteorder", + "crc32fast", + "crossbeam-utils", + "flate2", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/Cargo.toml b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/Cargo.toml new file mode 100644 index 0000000..af277cc --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/Cargo.toml @@ -0,0 +1,110 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +name = "libwebrtc" +version = "0.3.35" +build = false +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Livekit safe bindings to libwebrtc" +homepage = "https://livekit.io" +readme = false +license = "Apache-2.0" +repository = "https://github.com/livekit/rust-sdks" + +[features] +default = ["glib-main-loop"] +glib-main-loop = ["dep:glib"] + +[lib] +name = "libwebrtc" +path = "src/lib.rs" + +[dependencies.log] +version = "0.4" + +[dependencies.serde] +version = "1" +features = ["derive"] + +[dependencies.serde_json] +version = "1.0" + +[dependencies.thiserror] +version = "2" + +[dev-dependencies.env_logger] +version = "0.11" + +[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies.glib] +version = "0.21.3" +optional = true + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.cxx] +version = "1.0" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.lazy_static] +version = "1.4" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.livekit-runtime] +version = "0.4.0" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.parking_lot] +version = "0.12" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.rtrb] +version = "0.3.3" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.tokio] +version = "1" +features = [ + "sync", + "macros", +] +default-features = false + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies.webrtc-sys] +version = "0.3.33" + +[target.'cfg(target_arch = "wasm32")'.dependencies.js-sys] +version = "0.3" + +[target.'cfg(target_arch = "wasm32")'.dependencies.wasm-bindgen] +version = "0.2" + +[target.'cfg(target_arch = "wasm32")'.dependencies.wasm-bindgen-futures] +version = "0.4" + +[target.'cfg(target_arch = "wasm32")'.dependencies.web-sys] +version = "0.3" +features = [ + "MessageEvent", + "RtcPeerConnection", + "RtcSignalingState", + "RtcSdpType", + "RtcSessionDescriptionInit", + "RtcPeerConnectionIceEvent", + "RtcIceCandidate", + "RtcDataChannel", + "RtcDataChannelEvent", + "RtcDataChannelState", + "EventTarget", + "WebGlRenderingContext", + "WebGlTexture", +] + +[target.'cfg(target_os = "android")'.dependencies.jni] +version = "0.21" diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/Cargo.toml.orig b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/Cargo.toml.orig new file mode 100644 index 0000000..ff78bd2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/Cargo.toml.orig @@ -0,0 +1,60 @@ +[package] +name = "libwebrtc" +version = "0.3.35" +edition.workspace = true +homepage = "https://livekit.io" +license.workspace = true +description = "Livekit safe bindings to libwebrtc" +repository.workspace = true + +[features] +default = [ "glib-main-loop" ] +# On Wayland, libwebrtc uses GDBus to communicate with the XDG Desktop Portal. +# GDBus requires a GLib event loop to be running. If you already have a GLib +# event loop running in your application, for example if you are using the +# GTK or GStreamer Rust bindings, disable this feature. +glib-main-loop = [ "dep:glib" ] + +[dependencies] +log = { workspace = true } +serde = { workspace = true, features = ["derive"] } +serde_json = { workspace = true } +thiserror = { workspace = true } + +[target.'cfg(any(target_os = "linux", target_os = "freebsd"))'.dependencies] +glib = { version = "0.21.3", optional = true } + +[target.'cfg(target_os = "android")'.dependencies] +jni = "0.21" + +[target.'cfg(not(target_arch = "wasm32"))'.dependencies] +webrtc-sys = { workspace = true } +livekit-runtime = { workspace = true } +lazy_static = { workspace = true } +parking_lot = { workspace = true } +tokio = { workspace = true, default-features = false, features = ["sync", "macros"] } +cxx = "1.0" +rtrb = "0.3.3" + +[target.'cfg(target_arch = "wasm32")'.dependencies] +wasm-bindgen = "0.2" +js-sys = "0.3" +wasm-bindgen-futures = "0.4" +web-sys = { version = "0.3", features = [ + "MessageEvent", + "RtcPeerConnection", + "RtcSignalingState", + "RtcSdpType", + "RtcSessionDescriptionInit", + "RtcPeerConnectionIceEvent", + "RtcIceCandidate", + "RtcDataChannel", + "RtcDataChannelEvent", + "RtcDataChannelState", + "EventTarget", + "WebGlRenderingContext", + "WebGlTexture", +] } + +[dev-dependencies] +env_logger = { workspace = true } diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/audio_frame.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/audio_frame.rs new file mode 100644 index 0000000..728c364 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/audio_frame.rs @@ -0,0 +1,35 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::borrow::Cow; + +#[derive(Debug, Clone)] +pub struct AudioFrame<'a> { + pub data: Cow<'a, [i16]>, + pub sample_rate: u32, + pub num_channels: u32, + pub samples_per_channel: u32, +} + +impl AudioFrame<'_> { + // Owned + pub fn new(sample_rate: u32, num_channels: u32, samples_per_channel: u32) -> Self { + Self { + data: vec![0; (num_channels * samples_per_channel) as usize].into(), + sample_rate, + num_channels, + samples_per_channel, + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/audio_source.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/audio_source.rs new file mode 100644 index 0000000..17038d9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/audio_source.rs @@ -0,0 +1,222 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::imp::audio_source as imp_as; + +/// Default sample rate used by WebRTC audio pipelines (48kHz). +pub const DEFAULT_SAMPLE_RATE: u32 = 48000; + +/// Default number of audio channels (mono). +pub const DEFAULT_NUM_CHANNELS: u32 = 1; + +#[derive(Default, Debug)] +pub struct AudioSourceOptions { + pub echo_cancellation: bool, + pub noise_suppression: bool, + pub auto_gain_control: bool, +} + +/// Audio source type for creating audio tracks. +/// +/// Choose the appropriate source based on your use case: +/// +/// | Use Case | Source | Description | +/// |----------|--------|-------------| +/// | Manual audio (TTS, files) | `RtcAudioSource::Native(source)` | Push frames manually | +/// | Microphone capture | `RtcAudioSource::Device` | Automatic via Platform ADM | +/// | Both (mic + screen) | Use both types | Multiple tracks supported | +/// +/// # Combining Sources +/// +/// You can have multiple audio tracks with different source types: +/// - Track A: `RtcAudioSource::Device` for microphone (via `PlatformAudio`) +/// - Track B: `RtcAudioSource::Native` for screen capture or TTS +#[non_exhaustive] +#[derive(Debug, Clone)] +pub enum RtcAudioSource { + /// Native audio source for manual audio frame capture. + /// + /// Use this with Synthetic ADM mode (the default). You push audio frames + /// manually via `NativeAudioSource::capture_frame()`. + /// + /// # Example + /// + /// ```rust,ignore + /// use livekit::webrtc::audio_source::native::NativeAudioSource; + /// use livekit::webrtc::audio_source::{AudioSourceOptions, RtcAudioSource}; + /// + /// let source = NativeAudioSource::new( + /// AudioSourceOptions::default(), + /// 48000, 2, 100, + /// ); + /// source.capture_frame(&frame).await?; + /// + /// let track = LocalAudioTrack::create_audio_track( + /// "audio", + /// RtcAudioSource::Native(source), + /// ); + /// ``` + #[cfg(not(target_arch = "wasm32"))] + Native(native::NativeAudioSource), + + /// Device audio source - uses Platform ADM for automatic microphone capture. + /// + /// WebRTC automatically captures audio from the selected recording device + /// (microphone). You do NOT push frames manually. + /// + /// # Usage + /// + /// Use `PlatformAudio` from the `livekit` crate, which manages the Platform ADM + /// lifecycle and provides `RtcAudioSource::Device` via `rtc_source()`: + /// + /// ```rust,ignore + /// use livekit::prelude::*; + /// + /// // Create PlatformAudio (enables Platform ADM) + /// let audio = PlatformAudio::new()?; + /// + /// // Optionally select a specific device + /// if let Some(device) = audio.recording_devices().next() { + /// audio.set_recording_device(&device.id)?; + /// } + /// + /// // Create track using the device source + /// let track = LocalAudioTrack::create_audio_track("mic", audio.rtc_source()); + /// ``` + /// + /// # Combining with NativeAudioSource + /// + /// You CAN use `NativeAudioSource` alongside Platform ADM to have multiple + /// audio tracks with different sources (e.g., microphone + screen capture). + /// + /// # Platform Support + /// + /// - **iOS**: CoreAudio with VPIO (Voice Processing IO) + /// - **macOS**: CoreAudio + /// - **Windows**: WASAPI + /// - **Linux**: PulseAudio / ALSA + /// - **Android**: AAudio / OpenSL ES + #[cfg(not(target_arch = "wasm32"))] + Device, +} + +impl RtcAudioSource { + /// Set audio processing options. + /// Note: For `Device` source, options are controlled by the Platform ADM. + pub fn set_audio_options(&self, options: AudioSourceOptions) { + match self { + #[cfg(not(target_arch = "wasm32"))] + RtcAudioSource::Native(source) => source.set_audio_options(options), + #[cfg(not(target_arch = "wasm32"))] + RtcAudioSource::Device => { + // Device source options are managed by the Platform ADM + // This is a no-op + } + } + } + + /// Get audio processing options. + /// Note: For `Device` source, returns default options (actual options are managed by ADM). + pub fn audio_options(&self) -> AudioSourceOptions { + match self { + #[cfg(not(target_arch = "wasm32"))] + RtcAudioSource::Native(source) => source.audio_options(), + #[cfg(not(target_arch = "wasm32"))] + RtcAudioSource::Device => AudioSourceOptions::default(), + } + } + + /// Get the sample rate. + /// Note: For `Device` source, returns [`DEFAULT_SAMPLE_RATE`] (48kHz). + pub fn sample_rate(&self) -> u32 { + match self { + #[cfg(not(target_arch = "wasm32"))] + RtcAudioSource::Native(source) => source.sample_rate(), + #[cfg(not(target_arch = "wasm32"))] + RtcAudioSource::Device => DEFAULT_SAMPLE_RATE, + } + } + + /// Get the number of channels. + /// Note: For `Device` source, returns [`DEFAULT_NUM_CHANNELS`] (mono). + pub fn num_channels(&self) -> u32 { + match self { + #[cfg(not(target_arch = "wasm32"))] + RtcAudioSource::Native(source) => source.num_channels(), + #[cfg(not(target_arch = "wasm32"))] + RtcAudioSource::Device => DEFAULT_NUM_CHANNELS, + } + } +} + +#[cfg(not(target_arch = "wasm32"))] +pub mod native { + use std::fmt::{Debug, Formatter}; + + use super::*; + use crate::{audio_frame::AudioFrame, RtcError}; + + #[derive(Clone)] + pub struct NativeAudioSource { + pub(crate) handle: imp_as::NativeAudioSource, + } + + impl Debug for NativeAudioSource { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + f.debug_struct("NativeAudioSource").finish() + } + } + + impl NativeAudioSource { + pub fn new( + options: AudioSourceOptions, + sample_rate: u32, + num_channels: u32, + queue_size_ms: u32, + ) -> NativeAudioSource { + Self { + handle: imp_as::NativeAudioSource::new( + options, + sample_rate, + num_channels, + queue_size_ms, + ), + } + } + + pub fn clear_buffer(&self) { + self.handle.clear_buffer() + } + + pub async fn capture_frame(&self, frame: &AudioFrame<'_>) -> Result<(), RtcError> { + self.handle.capture_frame(frame).await + } + + pub fn set_audio_options(&self, options: AudioSourceOptions) { + self.handle.set_audio_options(options) + } + + pub fn audio_options(&self) -> AudioSourceOptions { + self.handle.audio_options() + } + + pub fn sample_rate(&self) -> u32 { + self.handle.sample_rate() + } + + pub fn num_channels(&self) -> u32 { + self.handle.num_channels() + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/audio_stream.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/audio_stream.rs new file mode 100644 index 0000000..969b034 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/audio_stream.rs @@ -0,0 +1,113 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::imp::audio_stream as stream_imp; + +#[cfg(not(target_arch = "wasm32"))] +pub mod native { + use std::{ + fmt::{Debug, Formatter}, + pin::Pin, + task::{Context, Poll}, + }; + + use livekit_runtime::Stream; + + use super::stream_imp; + use crate::{audio_frame::AudioFrame, audio_track::RtcAudioTrack}; + + const DEFAULT_QUEUE_SIZE_FRAMES: usize = 10; + + #[derive(Clone, Debug, Default)] + pub struct NativeAudioStreamOptions { + /// Maximum number of queued WebRTC sink frames after the audio callback. + /// + /// Each queued frame corresponds to roughly 10 ms of decoded PCM audio + /// on the WebRTC sink path. + /// + /// `None` uses the default bounded queue size of 10 frames. `Some(0)` + /// opts into unbounded buffering. Positive values bound the queue, and + /// the stream drops the oldest queued frames on overflow so latency + /// stays bounded. + /// + /// If your application consumes both audio and video, keep the queue + /// sizing strategy coordinated across both streams. Using a much larger + /// queue, or unbounded buffering, for only one of them can increase + /// end-to-end latency for that stream and cause audio/video drift. + pub queue_size_frames: Option, + } + + pub struct NativeAudioStream { + pub(crate) handle: stream_imp::NativeAudioStream, + } + + impl Debug for NativeAudioStream { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + f.debug_struct("NativeAudioStream").field("track", &self.track()).finish() + } + } + + impl NativeAudioStream { + pub fn new(audio_track: RtcAudioTrack, sample_rate: i32, num_channels: i32) -> Self { + Self { + handle: stream_imp::NativeAudioStream::new( + audio_track, + sample_rate, + num_channels, + Some(DEFAULT_QUEUE_SIZE_FRAMES), + ), + } + } + + pub fn with_options( + audio_track: RtcAudioTrack, + sample_rate: i32, + num_channels: i32, + options: NativeAudioStreamOptions, + ) -> Self { + Self { + handle: stream_imp::NativeAudioStream::new( + audio_track, + sample_rate, + num_channels, + normalize_queue_size_frames(options.queue_size_frames), + ), + } + } + + pub fn track(&self) -> RtcAudioTrack { + self.handle.track() + } + + pub fn close(&mut self) { + self.handle.close() + } + } + + impl Stream for NativeAudioStream { + type Item = AudioFrame<'static>; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + Pin::new(&mut self.get_mut().handle).poll_next(cx) + } + } + + fn normalize_queue_size_frames(queue_size_frames: Option) -> Option { + match queue_size_frames { + None => Some(DEFAULT_QUEUE_SIZE_FRAMES), + Some(0) => None, + Some(value) => Some(value), + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/audio_track.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/audio_track.rs new file mode 100644 index 0000000..911085a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/audio_track.rs @@ -0,0 +1,39 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fmt::Debug; + +use crate::{ + imp::audio_track as imp_at, + media_stream_track::{media_stream_track, RtcTrackState}, +}; + +#[derive(Clone)] +pub struct RtcAudioTrack { + pub(crate) handle: imp_at::RtcAudioTrack, +} + +impl RtcAudioTrack { + media_stream_track!(); +} + +impl Debug for RtcAudioTrack { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("RtcAudioTrack") + .field("id", &self.id()) + .field("enabled", &self.enabled()) + .field("state", &self.state()) + .finish() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/data_channel.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/data_channel.rs new file mode 100644 index 0000000..ea9c066 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/data_channel.rs @@ -0,0 +1,125 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{fmt::Debug, str::Utf8Error}; + +use serde::Deserialize; +use thiserror::Error; + +use crate::{imp::data_channel as dc_imp, rtp_parameters::Priority}; + +#[derive(Clone, Debug)] +pub struct DataChannelInit { + pub ordered: bool, + pub max_retransmit_time: Option, + pub max_retransmits: Option, + pub protocol: String, + pub negotiated: bool, + pub id: i32, + pub priority: Option, +} + +impl Default for DataChannelInit { + fn default() -> Self { + Self { + ordered: true, + max_retransmit_time: None, + max_retransmits: None, + protocol: String::new(), + negotiated: false, + id: -1, + priority: None, + } + } +} + +#[derive(Debug, Error)] +pub enum DataChannelError { + #[error("failed to send data, dc not open? send buffer is full ?")] + Send, + #[error("only utf8 strings can be sent")] + Utf8(#[from] Utf8Error), +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum DataChannelState { + Connecting, + Open, + Closing, + Closed, +} + +#[derive(Debug)] +pub struct DataBuffer<'a> { + pub data: &'a [u8], + pub binary: bool, +} + +pub type OnStateChange = Box; +pub type OnMessage = Box; +pub type OnBufferedAmountChange = Box; + +#[derive(Clone)] +pub struct DataChannel { + pub(crate) handle: dc_imp::DataChannel, +} + +impl DataChannel { + pub fn send(&self, data: &[u8], binary: bool) -> Result<(), DataChannelError> { + self.handle.send(data, binary) + } + + pub fn id(&self) -> i32 { + self.handle.id() + } + + pub fn label(&self) -> String { + self.handle.label() + } + + pub fn state(&self) -> DataChannelState { + self.handle.state() + } + + pub fn close(&self) { + self.handle.close() + } + + pub fn buffered_amount(&self) -> u64 { + self.handle.buffered_amount() + } + + pub fn on_state_change(&self, callback: Option) { + self.handle.on_state_change(callback) + } + + pub fn on_message(&self, callback: Option) { + self.handle.on_message(callback) + } + + pub fn on_buffered_amount_change(&self, callback: Option) { + self.handle.on_buffered_amount_change(callback) + } +} + +impl Debug for DataChannel { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("DataChannel") + .field("id", &self.id()) + .field("label", &self.label()) + .field("state", &self.state()) + .finish() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/desktop_capturer.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/desktop_capturer.rs new file mode 100644 index 0000000..efa9c62 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/desktop_capturer.rs @@ -0,0 +1,230 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::imp::desktop_capturer as imp_dc; + +/// Configuration options for creating a desktop capturer. +/// +/// It contains a subset of libwebrtc's DesktopCaptureOptions. +/// +/// By default, it captures the entire screen and does not include the cursor. +/// +/// # Example +/// ```no_run +/// use libwebrtc::desktop_capturer::{DesktopCapturerOptions, DesktopCaptureSourceType}; +/// +/// let mut options = DesktopCapturerOptions::new(DesktopCaptureSourceType::Screen); +/// options.set_include_cursor(true); +/// ``` +pub struct DesktopCapturerOptions { + sys_handle: imp_dc::DesktopCapturerOptions, +} + +/// Specifies the type of source that a desktop capturer should capture. +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum DesktopCaptureSourceType { + Screen, + Window, + #[cfg(any(target_os = "macos", target_os = "linux"))] + Generic, +} + +impl DesktopCapturerOptions { + /// Creates a new `DesktopCapturerOptions` with default values. + /// + /// # Arguments + /// + /// * `source_type` - The type of source to capture (screen or window). + /// + /// # Defaults + /// + /// - Cursor is not included in captured frames (use [`set_include_cursor`](Self::set_include_cursor) to change) + /// - On macOS, the ScreenCaptureKit system picker is enabled (use [`set_sck_system_picker`](Self::set_sck_system_picker) to change) + pub fn new(source_type: DesktopCaptureSourceType) -> Self { + let source_type = match source_type { + DesktopCaptureSourceType::Screen => imp_dc::SourceType::Screen, + DesktopCaptureSourceType::Window => imp_dc::SourceType::Window, + #[cfg(any(target_os = "macos", target_os = "linux"))] + DesktopCaptureSourceType::Generic => imp_dc::SourceType::Generic, + }; + Self { sys_handle: imp_dc::DesktopCapturerOptions::new(source_type) } + } + + /// Sets whether to include the cursor in captured frames. + pub fn set_include_cursor(&mut self, include: bool) { + self.sys_handle = self.sys_handle.with_cursor(include); + } + + /// Sets whether to allow the ScreenCaptureKit system picker on macOS. + /// + /// This is enabled by default. + /// + /// When disabled, for capturing displays the client should get the source id + /// via a different way as [`DesktopCapturer::get_source_list`] returns an empty vector. + #[cfg(target_os = "macos")] + pub fn set_sck_system_picker(&mut self, allow_sck_system_picker: bool) { + self.sys_handle = self.sys_handle.with_sck_system_picker(allow_sck_system_picker); + } +} + +/// A desktop capturer for capturing screens or windows. +pub struct DesktopCapturer { + handle: imp_dc::DesktopCapturer, +} + +impl DesktopCapturer { + /// Creates a new `DesktopCapturer` with the specified callback and options. + /// + /// # Arguments + /// + /// * `options` - Configuration options for the capturer + /// + /// # Returns + /// + /// Returns `Some(DesktopCapturer)` if the capturer was created successfully, + /// or `None` if creation failed (e.g., due to platform limitations or permissions). + pub fn new(options: DesktopCapturerOptions) -> Option { + let desktop_capturer = imp_dc::DesktopCapturer::new(options.sys_handle); + if desktop_capturer.is_none() { + return None; + } + Some(Self { handle: desktop_capturer.unwrap() }) + } + + /// Starts capturing from the specified source. + /// + /// # Arguments + /// + /// * `source` - The capture source to use. It should be None when the capturer + /// is configured to use the system picker (on platforms that support it). + /// * `callback` - A function that will be called for each captured frame. The callback + /// receives a [`CaptureResult`] indicating success or error, and a [`DesktopFrame`] + /// containing the captured image data. + /// + /// # Note + /// + /// After calling this method, you must call [`capture_frame`](Self::capture_frame) + /// to actually capture frames. This method only initializes the capture session. + pub fn start_capture(&mut self, source: Option, mut callback: T) + where + T: FnMut(Result) + Send + 'static, + { + if let Some(source) = source { + self.handle.select_source(source.sys_handle.id()); + } + let inner_callback = move |result: Result| { + callback(capture_result_from_sys(result)); + }; + self.handle.start(inner_callback); + } + + /// Captures a single frame. + /// + /// You must call [`start_capture`](Self::start_capture) before calling this method. + pub fn capture_frame(&mut self) { + self.handle.capture_frame(); + } + + /// Retrieves a list of available capture sources. + /// + /// Returns a list of screens or windows that can be captured, depending + /// on whether the capturer was configured for window or screen capture. + /// + /// # Returns + /// + /// A vector of [`CaptureSource`] objects representing available capture sources. + pub fn get_source_list(&self) -> Vec { + let source_list = self.handle.get_source_list(); + source_list.into_iter().map(|source| CaptureSource { sys_handle: source }).collect() + } +} + +pub struct DesktopFrame { + sys_handle: imp_dc::DesktopFrame, +} + +impl DesktopFrame { + fn new(sys_handle: imp_dc::DesktopFrame) -> Self { + Self { sys_handle } + } + + pub fn width(&self) -> i32 { + self.sys_handle.width() as i32 + } + + pub fn height(&self) -> i32 { + self.sys_handle.height() as i32 + } + + pub fn stride(&self) -> u32 { + self.sys_handle.stride() as u32 + } + + pub fn left(&self) -> i32 { + self.sys_handle.left() + } + + pub fn top(&self) -> i32 { + self.sys_handle.top() + } + + pub fn data(&self) -> &[u8] { + self.sys_handle.data() + } +} + +#[derive(Clone)] +pub struct CaptureSource { + sys_handle: imp_dc::CaptureSource, +} + +impl CaptureSource { + pub fn id(&self) -> u64 { + self.sys_handle.id() + } + pub fn title(&self) -> String { + self.sys_handle.title() + } + pub fn display_id(&self) -> i64 { + self.sys_handle.display_id() + } +} + +impl std::fmt::Display for CaptureSource { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CaptureSource") + .field("id", &self.id()) + .field("title", &self.title()) + .field("display_id", &self.display_id()) + .finish() + } +} + +#[derive(Debug, PartialEq)] +pub enum CaptureError { + Temporary, + Permanent, +} + +fn capture_result_from_sys( + result: Result, +) -> Result { + match result { + Ok(frame) => Ok(DesktopFrame::new(frame)), + Err(error) => Err(match error { + imp_dc::CaptureError::Temporary => CaptureError::Temporary, + imp_dc::CaptureError::Permanent => CaptureError::Permanent, + }), + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/enum_dispatch.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/enum_dispatch.rs new file mode 100644 index 0000000..a82f6ed --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/enum_dispatch.rs @@ -0,0 +1,41 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// TODO(theomonnom): Async methods +#[macro_export] +macro_rules! enum_dispatch { + // This arm is used to avoid nested loops with the arguments + // The arguments are transformed to $combined_args tt + (@match [$($variant:ident),+]: $fnc:ident, $self:ident, $combined_args:tt) => { + match $self { + $( + Self::$variant(inner) => inner.$fnc$combined_args, + )+ + } + }; + + // Create the function and extract self fron the $args tt (little hack) + (@fnc [$($variant:ident),+]: $vis:vis fn $fnc:ident($self:ident: $sty:ty $(, $arg:ident: $t:ty)*) -> $ret:ty) => { + #[inline] + $vis fn $fnc($self: $sty, $($arg: $t),*) -> $ret { + $crate::enum_dispatch!(@match [$($variant),+]: $fnc, $self, ($($arg,)*)) + } + }; + + ($variants:tt; $($vis:vis fn $fnc:ident$args:tt -> $ret:ty;)+) => { + $( + $crate::enum_dispatch!(@fnc $variants: $vis fn $fnc$args -> $ret); + )+ + }; +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/ice_candidate.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/ice_candidate.rs new file mode 100644 index 0000000..f2a531f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/ice_candidate.rs @@ -0,0 +1,55 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fmt::Debug; + +use crate::{imp::ice_candidate as imp_ic, session_description::SdpParseError}; + +pub struct IceCandidate { + pub(crate) handle: imp_ic::IceCandidate, +} + +impl IceCandidate { + pub fn parse( + sdp_mid: &str, + sdp_mline_index: i32, + sdp: &str, + ) -> Result { + imp_ic::IceCandidate::parse(sdp_mid, sdp_mline_index, sdp) + } + + pub fn sdp_mid(&self) -> String { + self.handle.sdp_mid() + } + + pub fn sdp_mline_index(&self) -> i32 { + self.handle.sdp_mline_index() + } + + pub fn candidate(&self) -> String { + self.handle.candidate() + } +} + +impl ToString for IceCandidate { + fn to_string(&self) -> String { + self.handle.to_string() + } +} + +impl Debug for IceCandidate { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("IceCandidate").field("candidate", &self.to_string()).finish() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/lib.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/lib.rs new file mode 100644 index 0000000..a73d1ce --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/lib.rs @@ -0,0 +1,82 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use thiserror::Error; + +#[cfg_attr(target_arch = "wasm32", path = "web/mod.rs")] +#[cfg_attr(not(target_arch = "wasm32"), path = "native/mod.rs")] +mod imp; + +mod enum_dispatch; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum MediaType { + Audio, + Video, + Data, + Unsupported, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum RtcErrorType { + Internal, + InvalidSdp, + InvalidState, +} + +#[derive(Error, Debug)] +#[error("an RtcError occurred: {error_type:?} - {message}")] +pub struct RtcError { + pub error_type: RtcErrorType, + pub message: String, +} + +pub mod audio_frame; +pub mod audio_source; +pub mod audio_stream; +pub mod audio_track; +pub mod data_channel; +#[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))] +pub mod desktop_capturer; +pub mod ice_candidate; +pub mod media_stream; +pub mod media_stream_track; +pub mod peer_connection; +pub mod peer_connection_factory; +pub mod prelude; +pub mod recorded_audio; +pub mod rtp_parameters; +pub mod rtp_receiver; +pub mod rtp_sender; +pub mod rtp_transceiver; +pub mod session_description; +pub mod stats; +pub mod video_frame; +pub mod video_source; +pub mod video_stream; +pub mod video_track; + +#[cfg(not(target_arch = "wasm32"))] +pub mod native { + pub use webrtc_sys::webrtc::ffi::create_random_uuid; + + pub use crate::imp::{ + apm, audio_mixer, audio_resampler, frame_cryptor, packet_trailer, yuv_helper, + }; +} + +#[cfg(target_os = "android")] +pub mod android { + pub use crate::imp::android::*; +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/media_stream.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/media_stream.rs new file mode 100644 index 0000000..e30d3cb --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/media_stream.rs @@ -0,0 +1,46 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fmt::Debug; + +use crate::{audio_track::RtcAudioTrack, imp::media_stream as imp_ms, video_track::RtcVideoTrack}; + +#[derive(Clone)] +pub struct MediaStream { + pub(crate) handle: imp_ms::MediaStream, +} + +impl MediaStream { + pub fn id(&self) -> String { + self.handle.id() + } + + pub fn audio_tracks(&self) -> Vec { + self.handle.audio_tracks() + } + + pub fn video_tracks(&self) -> Vec { + self.handle.video_tracks() + } +} + +impl Debug for MediaStream { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("MediaStream") + .field("id", &self.id()) + .field("audio_tracks", &self.audio_tracks()) + .field("video_tracks", &self.video_tracks()) + .finish() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/media_stream_track.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/media_stream_track.rs new file mode 100644 index 0000000..ab2c323 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/media_stream_track.rs @@ -0,0 +1,86 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{audio_track::RtcAudioTrack, enum_dispatch, video_track::RtcVideoTrack}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum RtcTrackState { + Live, + Ended, +} + +#[derive(Debug, Clone)] +pub enum MediaStreamTrack { + Video(RtcVideoTrack), + Audio(RtcAudioTrack), +} + +#[cfg(not(target_arch = "wasm32"))] +impl MediaStreamTrack { + enum_dispatch!( + [Video, Audio]; + pub(crate) fn sys_handle(self: &Self) -> cxx::SharedPtr; + ); +} + +impl MediaStreamTrack { + enum_dispatch!( + [Video, Audio]; + pub fn id(self: &Self) -> String; + pub fn enabled(self: &Self) -> bool; + pub fn set_enabled(self: &Self, enabled: bool) -> bool; + pub fn state(self: &Self) -> RtcTrackState; + ); +} + +macro_rules! media_stream_track { + () => { + pub fn id(&self) -> String { + self.handle.id() + } + + pub fn enabled(&self) -> bool { + self.handle.enabled() + } + + pub fn set_enabled(&self, enabled: bool) -> bool { + self.handle.set_enabled(enabled) + } + + pub fn state(&self) -> RtcTrackState { + self.handle.state().into() + } + + #[cfg(not(target_arch = "wasm32"))] + pub(crate) fn sys_handle( + &self, + ) -> cxx::SharedPtr { + self.handle.sys_handle() + } + }; +} + +pub(crate) use media_stream_track; + +impl From for MediaStreamTrack { + fn from(track: RtcAudioTrack) -> Self { + Self::Audio(track) + } +} + +impl From for MediaStreamTrack { + fn from(track: RtcVideoTrack) -> Self { + Self::Video(track) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/android.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/android.rs new file mode 100644 index 0000000..6f270f5 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/android.rs @@ -0,0 +1,65 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use jni::objects::JObject; +use webrtc_sys::android::ffi as sys_android; + +/// Initialize Android WebRTC with the JVM. +/// +/// This is automatically called by [`initialize_android_context`], so you only +/// need to call this directly if you don't have access to an Android Context +/// (e.g., in `JNI_OnLoad`). +/// +/// This function is idempotent - safe to call multiple times. +pub fn initialize_android(vm: &jni::JavaVM) { + unsafe { + sys_android::init_android(vm.get_java_vm_pointer() as *mut _); + } +} + +/// Initialize Android WebRTC with the application context. +/// +/// This is the main initialization function for Android. It performs both: +/// 1. JVM initialization (same as [`initialize_android`]) +/// 2. Context initialization (required for PlatformAudio) +/// +/// This function is idempotent - safe to call multiple times. +/// +/// # Arguments +/// * `vm` - The JavaVM instance +/// * `context` - The Android application context +/// +/// # Returns +/// `true` if context initialization succeeded, `false` otherwise. +/// Note: JVM initialization always happens regardless of return value. +/// +/// # Example +/// ```ignore +/// use jni::JavaVM; +/// use jni::objects::JObject; +/// use livekit::webrtc::android::initialize_android_context; +/// +/// fn init(vm: JavaVM, context: JObject) { +/// // Just one call needed - handles both JVM and context init +/// initialize_android_context(&vm, &context); +/// } +/// ``` +pub fn initialize_android_context(vm: &jni::JavaVM, context: &JObject) -> bool { + unsafe { + sys_android::init_android_context( + vm.get_java_vm_pointer() as *mut _, + context.as_raw() as usize, + ) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/apm.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/apm.rs new file mode 100644 index 0000000..a5c53fb --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/apm.rs @@ -0,0 +1,119 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use cxx::UniquePtr; +use webrtc_sys::apm::ffi as sys_apm; + +use crate::{RtcError, RtcErrorType}; + +pub struct AudioProcessingModule { + sys_handle: UniquePtr, +} + +impl AudioProcessingModule { + pub fn new( + echo_canceller_enabled: bool, + gain_controller_enabled: bool, + high_pass_filter_enabled: bool, + noise_suppression_enabled: bool, + ) -> Self { + Self { + sys_handle: sys_apm::create_apm( + echo_canceller_enabled, + gain_controller_enabled, + high_pass_filter_enabled, + noise_suppression_enabled, + ), + } + } + + pub fn process_stream( + &mut self, + data: &mut [i16], + sample_rate: i32, + num_channels: i32, + ) -> Result<(), RtcError> { + let samples_per_10ms = (sample_rate as usize / 100) * num_channels as usize; + assert!( + data.len() % samples_per_10ms == 0 && data.len() >= samples_per_10ms, + "slice must have a multiple of 10ms worth of samples" + ); + + for chunk in data.chunks_mut(samples_per_10ms) { + if unsafe { + self.sys_handle.pin_mut().process_stream( + chunk.as_mut_ptr(), + chunk.len(), + chunk.as_mut_ptr(), + chunk.len(), + sample_rate, + num_channels, + ) + } != 0 + { + return Err(RtcError { + error_type: RtcErrorType::Internal, + message: "Failed to process stream".to_string(), + }); + } + } + + Ok(()) + } + + pub fn process_reverse_stream( + &mut self, + data: &mut [i16], + sample_rate: i32, + num_channels: i32, + ) -> Result<(), RtcError> { + let samples_per_10ms = (sample_rate as usize / 100) * num_channels as usize; + assert!( + data.len() % samples_per_10ms == 0 && data.len() >= samples_per_10ms, + "slice must have a multiple of 10ms worth of samples" + ); + + for chunk in data.chunks_mut(samples_per_10ms) { + if unsafe { + self.sys_handle.pin_mut().process_reverse_stream( + chunk.as_mut_ptr(), + chunk.len(), + chunk.as_mut_ptr(), + chunk.len(), + sample_rate, + num_channels, + ) + } != 0 + { + return Err(RtcError { + error_type: RtcErrorType::Internal, + message: "Failed to process reverse stream".to_string(), + }); + } + } + + Ok(()) + } + + pub fn set_stream_delay_ms(&mut self, delay_ms: i32) -> Result<(), RtcError> { + if self.sys_handle.pin_mut().set_stream_delay_ms(delay_ms) == 0 { + Ok(()) + } else { + Err(RtcError { + error_type: RtcErrorType::Internal, + message: "Failed to set stream delay".to_string(), + }) + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/audio_mixer.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/audio_mixer.rs new file mode 100644 index 0000000..eff05ec --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/audio_mixer.rs @@ -0,0 +1,108 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::audio_frame::AudioFrame; +use cxx::UniquePtr; +use std::sync::Arc; +use webrtc_sys::audio_mixer as sys; +use webrtc_sys::audio_mixer::ffi; + +pub struct AudioMixer { + sys_handle: UniquePtr, +} + +pub use ffi::AudioFrameInfo; + +pub trait AudioMixerSource { + fn ssrc(&self) -> i32; + fn preferred_sample_rate(&self) -> u32; + fn get_audio_frame_with_info(&self, target_sample_rate: u32) -> Option>; +} + +struct AudioMixerSourceImpl { + inner: T, +} +impl sys::AudioMixerSource for AudioMixerSourceImpl { + fn ssrc(&self) -> i32 { + self.inner.ssrc() + } + + fn preferred_sample_rate(&self) -> i32 { + self.inner.preferred_sample_rate() as i32 + } + + fn get_audio_frame_with_info( + &self, + target_sample_rate: i32, + native_frame: sys::NativeAudioFrame, + ) -> AudioFrameInfo { + if let Some(frame) = self.inner.get_audio_frame_with_info(target_sample_rate as u32) { + let samples_count = (frame.sample_rate as usize / 100) as usize; + assert_eq!( + frame.sample_rate, target_sample_rate as u32, + "sample rate must match target_sample_rate" + ); + assert_eq!( + frame.samples_per_channel as usize, samples_count, + "frame must contain 10ms of samples" + ); + assert_eq!( + frame.data.len(), + samples_count * frame.num_channels as usize, + "slice must contain 10ms of samples" + ); + + unsafe { + native_frame.update_frame( + 0, + frame.data.as_ptr(), + frame.samples_per_channel as usize, + frame.sample_rate as i32, + frame.num_channels as usize, + ); + } + return ffi::AudioFrameInfo::Normal; + } else { + return ffi::AudioFrameInfo::Muted; + } + } +} + +impl AudioMixer { + pub fn new() -> Self { + let sys_handle = ffi::create_audio_mixer(); + Self { sys_handle } + } + + pub fn add_source(&mut self, source: impl AudioMixerSource + 'static) { + let source_impl = AudioMixerSourceImpl { inner: source }; + let wrapper = Box::new(sys::AudioMixerSourceWrapper::new(Arc::new(source_impl))); + unsafe { + self.sys_handle.pin_mut().add_source(wrapper); + } + } + + pub fn remove_source(&mut self, ssrc: i32) { + unsafe { + self.sys_handle.pin_mut().remove_source(ssrc); + } + } + + pub fn mix(&mut self, num_channels: usize) -> &[i16] { + unsafe { + let len = self.sys_handle.pin_mut().mix(num_channels); + std::slice::from_raw_parts(self.sys_handle.data(), len) + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/audio_resampler.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/audio_resampler.rs new file mode 100644 index 0000000..4b43707 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/audio_resampler.rs @@ -0,0 +1,53 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use cxx::UniquePtr; +use webrtc_sys::audio_resampler as sys_ar; + +pub struct AudioResampler { + sys_handle: UniquePtr, +} + +impl Default for AudioResampler { + fn default() -> Self { + Self { sys_handle: sys_ar::ffi::create_audio_resampler() } + } +} + +impl AudioResampler { + pub fn remix_and_resample<'a>( + &'a mut self, + src: &[i16], + samples_per_channel: u32, + num_channels: u32, + sample_rate: u32, + dst_num_channels: u32, + dst_sample_rate: u32, + ) -> &'a [i16] { + assert!(src.len() >= (samples_per_channel * num_channels) as usize, "src buffer too small"); + + unsafe { + let len = self.sys_handle.pin_mut().remix_and_resample( + src.as_ptr(), + samples_per_channel as usize, + num_channels as usize, + sample_rate as i32, + dst_num_channels as usize, + dst_sample_rate as i32, + ); + + std::slice::from_raw_parts(self.sys_handle.data(), len / 2) + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/audio_source.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/audio_source.rs new file mode 100644 index 0000000..6b0d889 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/audio_source.rs @@ -0,0 +1,205 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use cxx::SharedPtr; +use tokio::sync::oneshot; +use webrtc_sys::audio_track as sys_at; + +use crate::{audio_frame::AudioFrame, audio_source::AudioSourceOptions, RtcError, RtcErrorType}; + +#[derive(Clone)] +pub struct NativeAudioSource { + sys_handle: SharedPtr, + sample_rate: u32, + num_channels: u32, + queue_size_samples: u32, +} + +impl NativeAudioSource { + /// Creates a new [`NativeAudioSource`]. + /// + /// # Arguments + /// * `options` – Configuration options for the source (e.g. echo cancellation, noise suppression). + /// * `sample_rate` – Sampling rate in Hz (for example, `48000`). + /// * `num_channels` – Number of audio channels (`1` for mono, `2` for stereo, etc.). + /// * `queue_size_ms` – Size of the internal buffering queue, in milliseconds. + /// + /// # Behavior + /// - If `queue_size_ms` is **zero**, buffering is **disabled** and audio frames are + /// delivered directly to webrtc sinks. In this mode, the caller **must provide 10 ms frames** + /// (i.e., `sample_rate / 100` samples per channel) when calling [`capture_frame`]. + /// - If `queue_size_ms` is **non-zero**, buffering is enabled. The value must be a + /// **multiple of 10**, representing the total buffering duration in milliseconds. + /// Frames will be queued and flushed to sinks asynchronously once the buffer + /// reaches the configured threshold. + /// + /// # Panics + /// assert if `queue_size_ms` is not a multiple of 10. + pub fn new( + options: AudioSourceOptions, + sample_rate: u32, + num_channels: u32, + queue_size_ms: u32, + ) -> NativeAudioSource { + assert!(queue_size_ms % 10 == 0, "queue_size_ms must be a multiple of 10"); + + let sys_handle = sys_at::ffi::new_audio_track_source( + options.into(), + sample_rate.try_into().unwrap(), + num_channels.try_into().unwrap(), + queue_size_ms.try_into().unwrap(), + ); + + let queue_size_samples = (queue_size_ms * sample_rate * num_channels) / 1000; + Self { sys_handle, sample_rate, num_channels, queue_size_samples } + } + + pub fn sys_handle(&self) -> SharedPtr { + self.sys_handle.clone() + } + + pub fn set_audio_options(&self, options: AudioSourceOptions) { + self.sys_handle.set_audio_options(&sys_at::ffi::AudioSourceOptions::from(options)) + } + + pub fn audio_options(&self) -> AudioSourceOptions { + self.sys_handle.audio_options().into() + } + + pub fn sample_rate(&self) -> u32 { + self.sample_rate + } + + pub fn num_channels(&self) -> u32 { + self.num_channels + } + + pub fn clear_buffer(&self) { + self.sys_handle.clear_buffer(); + } + + pub async fn capture_frame(&self, frame: &AudioFrame<'_>) -> Result<(), RtcError> { + if self.sample_rate != frame.sample_rate || self.num_channels != frame.num_channels { + return Err(RtcError { + error_type: RtcErrorType::InvalidState, + message: "sample_rate and num_channels don't match".to_owned(), + }); + } + + // Fast path: no buffering + if self.queue_size_samples == 0 { + // frame size must be 10ms for fast path + let expected_frames_per_ch = (self.sample_rate / 100) as usize; + if frame.data.len() % (self.num_channels as usize) != 0 { + return Err(RtcError { + error_type: RtcErrorType::InvalidState, + message: "frame.data length not divisible by channel count".to_owned(), + }); + } + let nb_frames = frame.data.len() / (self.num_channels as usize); + if nb_frames != expected_frames_per_ch { + return Err(RtcError { + error_type: RtcErrorType::InvalidState, + message: format!( + "direct capture requires 10ms frames: got {} frames, expected {}", + nb_frames, expected_frames_per_ch + ), + }); + } + + // Define a no-op callback for fast path (queue_size_ms=0) + // This is safer than passing null, which can cause UB in release mode optimizations + extern "C" fn noop_complete_callback(_ctx: *const sys_at::SourceContext) { + // No-op: fast path completes synchronously, no callback needed + } + + unsafe { + let data: &[i16] = frame.data.as_ref(); + // Use a valid no-op callback instead of null for safety + // In release mode, transmuting null pointers can cause UB + let noop_callback = sys_at::CompleteCallback(noop_complete_callback); + let ok = self.sys_handle.capture_frame( + data, + self.sample_rate, + self.num_channels, + nb_frames, + std::ptr::null(), // Context is still null - callback won't use it + noop_callback, + ); + if !ok { + return Err(RtcError { + error_type: RtcErrorType::InvalidState, + message: "failed to capture frame without buffering".to_owned(), + }); + } + } + return Ok(()); + } + + // Buffered path. + extern "C" fn lk_audio_source_complete(userdata: *const sys_at::SourceContext) { + let tx = unsafe { Box::from_raw(userdata as *mut oneshot::Sender<()>) }; + let _ = tx.send(()); + } + + // iterate over chunks of self._queue_size_samples + for chunk in frame.data.chunks(self.queue_size_samples as usize) { + let nb_frames = chunk.len() / self.num_channels as usize; + let (tx, rx) = oneshot::channel::<()>(); + let ctx = Box::new(tx); + let ctx_ptr = Box::into_raw(ctx) as *const sys_at::SourceContext; + + unsafe { + // In the fast path, C++ never store / invoke on_complete / ctx. + if !self.sys_handle.capture_frame( + chunk, + self.sample_rate, + self.num_channels, + nb_frames, + ctx_ptr, + sys_at::CompleteCallback(lk_audio_source_complete), + ) { + return Err(RtcError { + error_type: RtcErrorType::InvalidState, + message: "failed to capture frame".to_owned(), + }); + } + } + + let _ = rx.await; + } + + Ok(()) + } +} + +impl From for AudioSourceOptions { + fn from(options: sys_at::ffi::AudioSourceOptions) -> Self { + Self { + echo_cancellation: options.echo_cancellation, + noise_suppression: options.noise_suppression, + auto_gain_control: options.auto_gain_control, + } + } +} + +impl From for sys_at::ffi::AudioSourceOptions { + fn from(options: AudioSourceOptions) -> Self { + Self { + echo_cancellation: options.echo_cancellation, + noise_suppression: options.noise_suppression, + auto_gain_control: options.auto_gain_control, + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/audio_stream.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/audio_stream.rs new file mode 100644 index 0000000..9a16b5f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/audio_stream.rs @@ -0,0 +1,314 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{ + collections::VecDeque, + pin::Pin, + sync::{ + atomic::{AtomicBool, AtomicU64, Ordering}, + Arc, + }, + task::{Context, Poll, Waker}, +}; + +use cxx::SharedPtr; +use livekit_runtime::Stream; +use parking_lot::Mutex; +use rtrb::{Consumer, Producer, PushError, RingBuffer}; +use webrtc_sys::audio_track as sys_at; + +use crate::{audio_frame::AudioFrame, audio_track::RtcAudioTrack}; + +pub struct NativeAudioStream { + native_sink: SharedPtr, + audio_track: RtcAudioTrack, + frame_queue: Arc, +} + +impl NativeAudioStream { + pub fn new( + audio_track: RtcAudioTrack, + sample_rate: i32, + num_channels: i32, + queue_size_frames: Option, + ) -> Self { + let frame_queue = Arc::new(AudioFrameQueue::new(queue_size_frames)); + let observer = Arc::new(AudioTrackObserver { frame_queue: frame_queue.clone() }); + let native_sink = sys_at::ffi::new_native_audio_sink( + Box::new(sys_at::AudioSinkWrapper::new(observer.clone())), + sample_rate, + num_channels, + ); + + let audio = unsafe { sys_at::ffi::media_to_audio(audio_track.sys_handle()) }; + audio.add_sink(&native_sink); + + Self { native_sink, audio_track, frame_queue } + } + + pub fn track(&self) -> RtcAudioTrack { + self.audio_track.clone() + } + + pub fn close(&mut self) { + let audio = unsafe { sys_at::ffi::media_to_audio(self.audio_track.sys_handle()) }; + audio.remove_sink(&self.native_sink); + + self.frame_queue.close(); + } +} + +impl Drop for NativeAudioStream { + fn drop(&mut self) { + self.close(); + } +} + +impl Stream for NativeAudioStream { + type Item = AudioFrame<'static>; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.frame_queue.poll_recv(cx) + } +} + +pub struct AudioTrackObserver { + frame_queue: Arc, +} + +impl sys_at::AudioSink for AudioTrackObserver { + fn on_data(&self, data: &[i16], sample_rate: i32, nb_channels: usize, nb_frames: usize) { + self.frame_queue.push(AudioFrame { + data: data.to_owned().into(), + sample_rate: sample_rate as u32, + num_channels: nb_channels as u32, + samples_per_channel: nb_frames as u32, + }); + } +} + +struct AudioFrameQueue { + kind: AudioFrameQueueKind, + closed: AtomicBool, + dropped_frames: AtomicU64, + waker: Mutex>, +} + +enum AudioFrameQueueKind { + Bounded(BoundedAudioFrameQueue), + Unbounded(UnboundedAudioFrameQueue), +} + +struct BoundedAudioFrameQueue { + producer: Mutex>>, + consumer: Mutex>>, +} + +struct UnboundedAudioFrameQueue { + frames: Mutex>>, +} + +impl AudioFrameQueue { + fn new(capacity: Option) -> Self { + let kind = match capacity.filter(|capacity| *capacity > 0) { + Some(capacity) => { + let (producer, consumer) = RingBuffer::new(capacity); + AudioFrameQueueKind::Bounded(BoundedAudioFrameQueue { + producer: Mutex::new(producer), + consumer: Mutex::new(consumer), + }) + } + None => AudioFrameQueueKind::Unbounded(UnboundedAudioFrameQueue { + frames: Mutex::new(VecDeque::new()), + }), + }; + + Self { + kind, + closed: AtomicBool::new(false), + dropped_frames: AtomicU64::new(0), + waker: Mutex::new(None), + } + } + + fn push(&self, frame: AudioFrame<'static>) { + if self.closed.load(Ordering::Acquire) { + return; + } + + match &self.kind { + AudioFrameQueueKind::Bounded(queue) => self.push_bounded(queue, frame), + AudioFrameQueueKind::Unbounded(queue) => { + queue.frames.lock().push_back(frame); + } + } + + self.wake_receiver(); + } + + fn push_bounded(&self, queue: &BoundedAudioFrameQueue, mut frame: AudioFrame<'static>) { + loop { + let push_result = queue.producer.lock().push(frame); + match push_result { + Ok(()) => return, + Err(PushError::Full(returned_frame)) => { + frame = returned_frame; + + let dropped = queue.consumer.lock().pop().is_ok(); + + if dropped { + self.record_drop(); + } else { + return; + } + } + } + } + } + + fn close(&self) { + self.closed.store(true, Ordering::Release); + self.wake_receiver(); + + match &self.kind { + AudioFrameQueueKind::Bounded(queue) => { + let mut consumer = queue.consumer.lock(); + while consumer.pop().is_ok() {} + } + AudioFrameQueueKind::Unbounded(queue) => { + queue.frames.lock().clear(); + } + } + } + + fn poll_recv(&self, cx: &mut Context<'_>) -> Poll>> { + if let Some(frame) = self.try_pop() { + return Poll::Ready(Some(frame)); + } + + if self.closed.load(Ordering::Acquire) { + return Poll::Ready(None); + } + + *self.waker.lock() = Some(cx.waker().clone()); + + if let Some(frame) = self.try_pop() { + self.waker.lock().take(); + Poll::Ready(Some(frame)) + } else if self.closed.load(Ordering::Acquire) { + Poll::Ready(None) + } else { + Poll::Pending + } + } + + fn try_pop(&self) -> Option> { + match &self.kind { + AudioFrameQueueKind::Bounded(queue) => queue.consumer.lock().pop().ok(), + AudioFrameQueueKind::Unbounded(queue) => queue.frames.lock().pop_front(), + } + } + + fn wake_receiver(&self) { + let waker = self.waker.lock().take(); + if let Some(waker) = waker { + waker.wake(); + } + } + + fn record_drop(&self) { + let dropped_frames = self.dropped_frames.fetch_add(1, Ordering::Relaxed) + 1; + if dropped_frames == 1 || dropped_frames % 100 == 0 { + log::warn!( + "native audio stream queue overflow; dropped {} queued frames", + dropped_frames + ); + } + } +} + +#[cfg(test)] +mod tests { + use std::sync::atomic::Ordering; + + use super::AudioFrameQueue; + use crate::audio_frame::AudioFrame; + + fn test_frame(marker: i16) -> AudioFrame<'static> { + AudioFrame { + data: vec![marker].into(), + sample_rate: 48_000, + num_channels: 1, + samples_per_channel: 1, + } + } + + fn pop_marker(queue: &AudioFrameQueue) -> Option { + queue.try_pop().map(|frame| frame.data[0]) + } + + #[test] + fn bounded_queue_preserves_fifo_order_under_capacity() { + let queue = AudioFrameQueue::new(Some(3)); + + queue.push(test_frame(1)); + queue.push(test_frame(2)); + queue.push(test_frame(3)); + + assert_eq!(pop_marker(&queue), Some(1)); + assert_eq!(pop_marker(&queue), Some(2)); + assert_eq!(pop_marker(&queue), Some(3)); + assert_eq!(pop_marker(&queue), None); + } + + #[test] + fn bounded_queue_drops_oldest_when_full() { + let queue = AudioFrameQueue::new(Some(2)); + + queue.push(test_frame(1)); + queue.push(test_frame(2)); + queue.push(test_frame(3)); + + assert_eq!(queue.dropped_frames.load(Ordering::Relaxed), 1); + assert_eq!(pop_marker(&queue), Some(2)); + assert_eq!(pop_marker(&queue), Some(3)); + assert_eq!(pop_marker(&queue), None); + } + + #[test] + fn unbounded_queue_retains_all_frames() { + let queue = AudioFrameQueue::new(None); + + for marker in 1..=4 { + queue.push(test_frame(marker)); + } + + for marker in 1..=4 { + assert_eq!(pop_marker(&queue), Some(marker)); + } + assert_eq!(pop_marker(&queue), None); + assert_eq!(queue.dropped_frames.load(Ordering::Relaxed), 0); + } + + #[test] + fn close_clears_buffer_and_rejects_future_pushes() { + let queue = AudioFrameQueue::new(Some(2)); + + queue.push(test_frame(1)); + queue.close(); + queue.push(test_frame(2)); + + assert_eq!(pop_marker(&queue), None); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/audio_track.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/audio_track.rs new file mode 100644 index 0000000..41d9ed4 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/audio_track.rs @@ -0,0 +1,33 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use cxx::SharedPtr; +use sys_at::ffi::audio_to_media; +use webrtc_sys::audio_track as sys_at; + +use super::media_stream_track::impl_media_stream_track; +use crate::media_stream_track::RtcTrackState; + +#[derive(Clone)] +pub struct RtcAudioTrack { + pub(crate) sys_handle: SharedPtr, +} + +impl RtcAudioTrack { + impl_media_stream_track!(audio_to_media); + + pub fn sys_handle(&self) -> SharedPtr { + audio_to_media(self.sys_handle.clone()) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/data_channel.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/data_channel.rs new file mode 100644 index 0000000..366ebf3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/data_channel.rs @@ -0,0 +1,142 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{str, sync::Arc}; + +use cxx::SharedPtr; +use parking_lot::Mutex; +use webrtc_sys::data_channel as sys_dc; + +use crate::data_channel::{ + DataBuffer, DataChannelError, DataChannelInit, DataChannelState, OnBufferedAmountChange, + OnMessage, OnStateChange, +}; + +impl From for DataChannelState { + fn from(value: sys_dc::ffi::DataState) -> Self { + match value { + sys_dc::ffi::DataState::Connecting => Self::Connecting, + sys_dc::ffi::DataState::Open => Self::Open, + sys_dc::ffi::DataState::Closing => Self::Closing, + sys_dc::ffi::DataState::Closed => Self::Closed, + _ => panic!("unknown data channel state"), + } + } +} + +impl From for sys_dc::ffi::DataChannelInit { + fn from(value: DataChannelInit) -> Self { + Self { + ordered: value.ordered, + has_max_retransmit_time: value.max_retransmit_time.is_some(), + max_retransmit_time: value.max_retransmit_time.unwrap_or_default(), + has_max_retransmits: value.max_retransmits.is_some(), + max_retransmits: value.max_retransmits.unwrap_or_default(), + protocol: value.protocol, + id: value.id, + has_priority: false, + priority: sys_dc::ffi::Priority::Medium, + negotiated: value.negotiated, + } + } +} + +#[derive(Clone)] +pub struct DataChannel { + observer: Arc, + pub(crate) sys_handle: SharedPtr, +} + +impl DataChannel { + pub fn configure(sys_handle: SharedPtr) -> Self { + let observer = Arc::new(DataChannelObserver::default()); + let dc = Self { sys_handle: sys_handle.clone(), observer: observer.clone() }; + + dc.sys_handle + .register_observer(Box::new(sys_dc::DataChannelObserverWrapper::new(observer))); + dc + } + + pub fn send(&self, data: &[u8], binary: bool) -> Result<(), DataChannelError> { + if !binary { + str::from_utf8(data)?; + } + + let buffer = sys_dc::ffi::DataBuffer { ptr: data.as_ptr(), len: data.len(), binary }; + + self.sys_handle.send(&buffer).then_some(()).ok_or(DataChannelError::Send) + } + + pub fn id(&self) -> i32 { + self.sys_handle.id() + } + + pub fn label(&self) -> String { + self.sys_handle.label() + } + + pub fn state(&self) -> DataChannelState { + self.sys_handle.state().into() + } + + pub fn close(&self) { + self.sys_handle.close(); + } + + pub fn buffered_amount(&self) -> u64 { + self.sys_handle.buffered_amount() + } + + pub fn on_state_change(&self, handler: Option) { + *self.observer.state_change_handler.lock() = handler; + } + + pub fn on_message(&self, handler: Option) { + *self.observer.message_handler.lock() = handler; + } + + pub fn on_buffered_amount_change(&self, handler: Option) { + *self.observer.buffered_amount_change_handler.lock() = handler; + } +} + +#[derive(Default)] +struct DataChannelObserver { + state_change_handler: Mutex>, + message_handler: Mutex>, + buffered_amount_change_handler: Mutex>, +} + +impl sys_dc::DataChannelObserver for DataChannelObserver { + fn on_state_change(&self, state: sys_dc::ffi::DataState) { + let mut handler = self.state_change_handler.lock(); + if let Some(f) = handler.as_mut() { + f(state.into()); + } + } + + fn on_message(&self, data: &[u8], binary: bool) { + let mut handler = self.message_handler.lock(); + if let Some(f) = handler.as_mut() { + f(DataBuffer { data, binary }); + } + } + + fn on_buffered_amount_change(&self, sent_data_size: u64) { + let mut handler = self.buffered_amount_change_handler.lock(); + if let Some(f) = handler.as_mut() { + f(sent_data_size); + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/desktop_capturer.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/desktop_capturer.rs new file mode 100644 index 0000000..942c22a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/desktop_capturer.rs @@ -0,0 +1,241 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use cxx::UniquePtr; +use webrtc_sys::desktop_capturer::{self as sys_dc, ffi::new_desktop_capturer}; + +#[derive(Debug, Copy, Clone, PartialEq)] +pub(crate) enum SourceType { + Screen, + Window, + Generic, +} + +#[derive(Copy, Clone, Debug)] +pub(crate) struct DesktopCapturerOptions { + source_type: SourceType, + include_cursor: bool, + #[cfg(target_os = "macos")] + allow_sck_system_picker: bool, +} + +impl Default for DesktopCapturerOptions { + fn default() -> Self { + Self { + source_type: SourceType::Screen, + include_cursor: false, + #[cfg(target_os = "macos")] + allow_sck_system_picker: true, + } + } +} + +impl DesktopCapturerOptions { + pub(crate) fn new(source_type: SourceType) -> Self { + Self { source_type, ..Default::default() } + } + + pub(crate) fn with_cursor(mut self, include: bool) -> Self { + self.include_cursor = include; + self + } + + #[cfg(target_os = "macos")] + pub(crate) fn with_sck_system_picker(mut self, allow_sck_system_picker: bool) -> Self { + self.allow_sck_system_picker = allow_sck_system_picker; + self + } + + pub(crate) fn to_sys_handle(&self) -> sys_dc::ffi::DesktopCapturerOptions { + let source_type = match self.source_type { + SourceType::Screen => sys_dc::ffi::SourceType::Screen, + SourceType::Window => sys_dc::ffi::SourceType::Window, + SourceType::Generic => sys_dc::ffi::SourceType::Generic, + }; + let mut sys_handle = sys_dc::ffi::DesktopCapturerOptions { + source_type, + include_cursor: self.include_cursor, + allow_sck_system_picker: false, + }; + #[cfg(target_os = "macos")] + { + sys_handle.allow_sck_system_picker = self.allow_sck_system_picker; + } + sys_handle + } +} + +pub(crate) struct DesktopCapturer { + sys_handle: UniquePtr, + #[cfg(all(any(target_os = "linux", target_os = "freebsd"), feature = "glib-main-loop"))] + glib_loop: Option, +} + +impl DesktopCapturer { + pub(crate) fn new(options: DesktopCapturerOptions) -> Option { + let sys_handle = new_desktop_capturer(options.to_sys_handle()); + if sys_handle.is_null() { + None + } else { + Some(Self { + sys_handle, + #[cfg(all( + any(target_os = "linux", target_os = "freebsd"), + feature = "glib-main-loop" + ))] + glib_loop: None, + }) + } + } + + pub(crate) fn capture_frame(&self) { + self.sys_handle.capture_frame(); + } + + pub(crate) fn start(&mut self, callback: T) + where + T: FnMut(Result) + Send + 'static, + { + #[cfg(all(any(target_os = "linux", target_os = "freebsd"), feature = "glib-main-loop"))] + if std::env::var("WAYLAND_DISPLAY").is_ok() { + let main_loop = glib::MainLoop::new(None, false); + self.glib_loop = Some(main_loop.clone()); + let _handle = std::thread::spawn(move || { + main_loop.run(); + }); + } + let pin_handle = self.sys_handle.pin_mut(); + let callback = DesktopCallback::new(callback); + let callback_wrapper = sys_dc::DesktopCapturerCallbackWrapper::new(Box::new(callback)); + pin_handle.start(Box::new(callback_wrapper)); + } + + pub(crate) fn select_source(&self, id: u64) -> bool { + self.sys_handle.select_source(id) + } + + pub(crate) fn get_source_list(&self) -> Vec { + let mut sources = Vec::new(); + let source_list = self.sys_handle.get_source_list(); + for source in source_list.iter() { + sources.push(CaptureSource { sys_handle: source.clone() }); + } + sources + } +} + +#[cfg(all(any(target_os = "linux", target_os = "freebsd"), feature = "glib-main-loop"))] +impl Drop for DesktopCapturer { + fn drop(&mut self) { + if let Some(glib_loop) = &self.glib_loop { + glib_loop.quit(); + } + } +} + +pub(crate) struct DesktopFrame { + sys_handle: UniquePtr, +} + +impl DesktopFrame { + fn new(sys_handle: UniquePtr) -> Self { + Self { sys_handle } + } + + pub(crate) fn width(&self) -> i32 { + self.sys_handle.width() + } + + pub(crate) fn height(&self) -> i32 { + self.sys_handle.height() + } + + pub(crate) fn stride(&self) -> u32 { + self.sys_handle.stride() as u32 + } + + pub(crate) fn left(&self) -> i32 { + self.sys_handle.left() + } + + pub(crate) fn top(&self) -> i32 { + self.sys_handle.top() + } + + pub(crate) fn data(&self) -> &[u8] { + let data = self.sys_handle.data(); + unsafe { std::slice::from_raw_parts(data, self.stride() as usize * self.height() as usize) } + } +} + +struct DesktopCallback) + Send> { + callback: T, +} + +impl DesktopCallback +where + T: FnMut(Result) + Send, +{ + fn new(callback: T) -> Self { + Self { callback } + } + + fn capture_result_from_sys( + result: Result, sys_dc::CaptureError>, + ) -> Result { + match result { + Ok(frame) => Ok(DesktopFrame::new(frame)), + Err(error) => Err(match error { + sys_dc::CaptureError::Temporary => CaptureError::Temporary, + sys_dc::CaptureError::Permanent => CaptureError::Permanent, + }), + } + } +} + +impl sys_dc::DesktopCapturerCallback for DesktopCallback +where + T: FnMut(Result) + Send, +{ + fn on_capture_result( + &mut self, + result: Result, sys_dc::CaptureError>, + ) { + (self.callback)(DesktopCallback::::capture_result_from_sys(result)); + } +} + +#[derive(Clone)] +pub(crate) struct CaptureSource { + sys_handle: sys_dc::ffi::Source, +} + +impl CaptureSource { + pub(crate) fn id(&self) -> u64 { + self.sys_handle.id + } + + pub(crate) fn title(&self) -> String { + self.sys_handle.title.clone() + } + + pub(crate) fn display_id(&self) -> i64 { + self.sys_handle.display_id + } +} + +pub(crate) enum CaptureError { + Temporary, + Permanent, +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/frame_cryptor.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/frame_cryptor.rs new file mode 100644 index 0000000..7f463f9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/frame_cryptor.rs @@ -0,0 +1,316 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::sync::Arc; + +use cxx::SharedPtr; +use parking_lot::Mutex; +use webrtc_sys::frame_cryptor::{self as sys_fc}; + +use crate::{ + native::packet_trailer::PacketTrailerHandler, peer_connection_factory::PeerConnectionFactory, + rtp_receiver::RtpReceiver, rtp_sender::RtpSender, +}; + +pub type OnStateChange = Box; + +#[derive(Copy, Clone, Debug)] +#[non_exhaustive] +pub enum KeyDerivationAlgorithm { + PBKDF2, + HKDF, +} +impl Into for KeyDerivationAlgorithm { + fn into(self) -> sys_fc::ffi::KeyDerivationAlgorithm { + match self { + KeyDerivationAlgorithm::PBKDF2 => sys_fc::ffi::KeyDerivationAlgorithm::PBKDF2, + KeyDerivationAlgorithm::HKDF => sys_fc::ffi::KeyDerivationAlgorithm::HKDF, + } + } +} + +#[derive(Debug, Clone)] +pub struct KeyProviderOptions { + pub shared_key: bool, + pub ratchet_window_size: i32, + pub ratchet_salt: Vec, + pub failure_tolerance: i32, + pub key_ring_size: i32, + pub key_derivation_algorithm: KeyDerivationAlgorithm, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EncryptionAlgorithm { + AesGcm, + AesCbc, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EncryptionState { + New, + Ok, + EncryptionFailed, + DecryptionFailed, + MissingKey, + KeyRatcheted, + InternalError, +} + +#[derive(Debug, Clone)] +pub struct EncryptedPacket { + pub data: Vec, + pub iv: Vec, + pub key_index: u32, +} + +#[derive(Clone)] +pub struct KeyProvider { + pub(crate) sys_handle: SharedPtr, +} + +impl KeyProvider { + pub fn new(options: KeyProviderOptions) -> Self { + Self { sys_handle: sys_fc::ffi::new_key_provider(options.into()) } + } + + pub fn set_shared_key(&self, key_index: i32, key: Vec) -> bool { + self.sys_handle.set_shared_key(key_index, key) + } + + pub fn ratchet_shared_key(&self, key_index: i32) -> Option> { + self.sys_handle.ratchet_shared_key(key_index).ok() + } + + pub fn get_shared_key(&self, key_index: i32) -> Option> { + self.sys_handle.get_shared_key(key_index).ok() + } + + pub fn set_key(&self, participant_id: String, key_index: i32, key: Vec) -> bool { + self.sys_handle.set_key(participant_id, key_index, key) + } + + pub fn ratchet_key(&self, participant_id: String, key_index: i32) -> Option> { + self.sys_handle.ratchet_key(participant_id, key_index).ok() + } + + pub fn get_key(&self, participant_id: String, key_index: i32) -> Option> { + self.sys_handle.get_key(participant_id, key_index).ok() + } + + pub fn set_sif_trailer(&self, trailer: Vec) { + self.sys_handle.set_sif_trailer(trailer); + } +} + +#[derive(Clone)] +pub struct FrameCryptor { + observer: Arc, + pub(crate) sys_handle: SharedPtr, +} + +impl FrameCryptor { + pub fn new_for_rtp_sender( + peer_factory: &PeerConnectionFactory, + participant_id: String, + algorithm: EncryptionAlgorithm, + key_provider: KeyProvider, + sender: RtpSender, + ) -> Self { + let observer = Arc::new(RtcFrameCryptorObserver::default()); + let sys_handle = sys_fc::ffi::new_frame_cryptor_for_rtp_sender( + peer_factory.handle.sys_handle.clone(), + participant_id, + algorithm.into(), + key_provider.sys_handle, + sender.handle.sys_handle, + ); + let fc = Self { observer: observer.clone(), sys_handle: sys_handle.clone() }; + fc.sys_handle + .register_observer(Box::new(sys_fc::RtcFrameCryptorObserverWrapper::new(observer))); + fc + } + + pub fn new_for_rtp_receiver( + peer_factory: &PeerConnectionFactory, + participant_id: String, + algorithm: EncryptionAlgorithm, + key_provider: KeyProvider, + receiver: RtpReceiver, + ) -> Self { + let observer = Arc::new(RtcFrameCryptorObserver::default()); + let sys_handle = sys_fc::ffi::new_frame_cryptor_for_rtp_receiver( + peer_factory.handle.sys_handle.clone(), + participant_id, + algorithm.into(), + key_provider.sys_handle, + receiver.handle.sys_handle, + ); + let fc = Self { observer: observer.clone(), sys_handle: sys_handle.clone() }; + fc.sys_handle + .register_observer(Box::new(sys_fc::RtcFrameCryptorObserverWrapper::new(observer))); + fc + } + + pub fn set_enabled(self: &FrameCryptor, enabled: bool) { + self.sys_handle.set_enabled(enabled); + } + + pub fn enabled(self: &FrameCryptor) -> bool { + self.sys_handle.enabled() + } + + pub fn set_key_index(self: &FrameCryptor, index: i32) { + self.sys_handle.set_key_index(index); + } + + pub fn key_index(self: &FrameCryptor) -> i32 { + self.sys_handle.key_index() + } + + pub fn participant_id(self: &FrameCryptor) -> String { + self.sys_handle.participant_id() + } + + pub fn on_state_change(&self, handler: Option) { + *self.observer.state_change_handler.lock() = handler; + } + + pub fn set_packet_trailer_handler(&self, handler: &PacketTrailerHandler) { + self.sys_handle.set_packet_trailer_handler(handler.sys_handle()); + } +} + +#[derive(Clone)] +pub struct DataPacketCryptor { + pub(crate) sys_handle: SharedPtr, +} + +impl DataPacketCryptor { + pub fn new(algorithm: EncryptionAlgorithm, key_provider: KeyProvider) -> Self { + Self { + sys_handle: sys_fc::ffi::new_data_packet_cryptor( + algorithm.into(), + key_provider.sys_handle, + ), + } + } + + pub fn encrypt( + &self, + participant_id: &str, + key_index: u32, + data: &[u8], + ) -> Result> { + let data_vec: Vec = data.to_vec(); + match self.sys_handle.encrypt_data_packet(participant_id.to_string(), key_index, data_vec) { + Ok(packet) => Ok(packet.into()), + Err(e) => Err(format!("Encryption failed: {}", e).into()), + } + } + + pub fn decrypt( + &self, + participant_id: &str, + encrypted_packet: &EncryptedPacket, + ) -> Result, Box> { + match self + .sys_handle + .decrypt_data_packet(participant_id.to_string(), &encrypted_packet.clone().into()) + { + Ok(data) => Ok(data.into_iter().collect()), + Err(e) => Err(format!("Decryption failed: {}", e).into()), + } + } +} + +#[derive(Default)] +struct RtcFrameCryptorObserver { + state_change_handler: Mutex>, +} + +impl sys_fc::RtcFrameCryptorObserver for RtcFrameCryptorObserver { + fn on_frame_cryption_state_change( + &self, + participant_id: String, + state: sys_fc::ffi::FrameCryptionState, + ) { + let mut handler = self.state_change_handler.lock(); + if let Some(f) = handler.as_mut() { + f(participant_id, state.into()); + } + } +} + +impl From for EncryptionAlgorithm { + fn from(value: sys_fc::ffi::Algorithm) -> Self { + match value { + sys_fc::ffi::Algorithm::AesGcm => Self::AesGcm, + sys_fc::ffi::Algorithm::AesCbc => Self::AesCbc, + _ => panic!("unknown frame cyrptor Algorithm"), + } + } +} + +impl From for sys_fc::ffi::Algorithm { + fn from(value: EncryptionAlgorithm) -> Self { + match value { + EncryptionAlgorithm::AesGcm => Self::AesGcm, + EncryptionAlgorithm::AesCbc => Self::AesCbc, + } + } +} + +impl From for EncryptionState { + fn from(value: sys_fc::ffi::FrameCryptionState) -> Self { + match value { + sys_fc::ffi::FrameCryptionState::New => Self::New, + sys_fc::ffi::FrameCryptionState::Ok => Self::Ok, + sys_fc::ffi::FrameCryptionState::EncryptionFailed => Self::EncryptionFailed, + sys_fc::ffi::FrameCryptionState::DecryptionFailed => Self::DecryptionFailed, + sys_fc::ffi::FrameCryptionState::MissingKey => Self::MissingKey, + sys_fc::ffi::FrameCryptionState::KeyRatcheted => Self::KeyRatcheted, + sys_fc::ffi::FrameCryptionState::InternalError => Self::InternalError, + _ => panic!("unknown frame cyrptor FrameCryptionState"), + } + } +} + +impl From for sys_fc::ffi::KeyProviderOptions { + fn from(value: KeyProviderOptions) -> Self { + Self { + shared_key: value.shared_key, + ratchet_window_size: value.ratchet_window_size, + ratchet_salt: value.ratchet_salt, + failure_tolerance: value.failure_tolerance, + key_ring_size: value.key_ring_size, + key_derivation_algorithm: value.key_derivation_algorithm.into(), + } + } +} + +impl From for EncryptedPacket { + fn from(value: sys_fc::ffi::EncryptedPacket) -> Self { + Self { + data: value.data.into_iter().collect(), + iv: value.iv.into_iter().collect(), + key_index: value.key_index, + } + } +} + +impl From for sys_fc::ffi::EncryptedPacket { + fn from(value: EncryptedPacket) -> Self { + Self { data: value.data, iv: value.iv, key_index: value.key_index } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/ice_candidate.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/ice_candidate.rs new file mode 100644 index 0000000..752126b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/ice_candidate.rs @@ -0,0 +1,60 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use cxx::SharedPtr; +use webrtc_sys::jsep as sys_jsep; + +use crate::{ice_candidate as ic, session_description::SdpParseError}; + +#[derive(Clone)] +pub struct IceCandidate { + pub(crate) sys_handle: SharedPtr, +} + +impl IceCandidate { + pub fn parse( + sdp_mid: &str, + sdp_mline_index: i32, + sdp: &str, + ) -> Result { + let res = sys_jsep::ffi::create_ice_candidate( + sdp_mid.to_string(), + sdp_mline_index, + sdp.to_string(), + ); + + match res { + Ok(sys_handle) => Ok(ic::IceCandidate { handle: IceCandidate { sys_handle } }), + Err(e) => Err(unsafe { sys_jsep::ffi::SdpParseError::from(e.what()).into() }), + } + } + + pub fn sdp_mid(&self) -> String { + self.sys_handle.sdp_mid() + } + + pub fn sdp_mline_index(&self) -> i32 { + self.sys_handle.sdp_mline_index() + } + + pub fn candidate(&self) -> String { + self.sys_handle.candidate() + } +} + +impl ToString for IceCandidate { + fn to_string(&self) -> String { + self.sys_handle.stringify() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/media_stream.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/media_stream.rs new file mode 100644 index 0000000..67b13ec --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/media_stream.rs @@ -0,0 +1,49 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use cxx::SharedPtr; +use webrtc_sys::media_stream as sys_ms; + +use crate::{ + audio_track, + imp::{audio_track::RtcAudioTrack, video_track::RtcVideoTrack}, + video_track, +}; + +#[derive(Clone)] +pub struct MediaStream { + pub(crate) sys_handle: SharedPtr, +} + +impl MediaStream { + pub fn id(&self) -> String { + self.sys_handle.id() + } + + pub fn audio_tracks(&self) -> Vec { + self.sys_handle + .get_audio_tracks() + .into_iter() + .map(|t| audio_track::RtcAudioTrack { handle: RtcAudioTrack { sys_handle: t.ptr } }) + .collect() + } + + pub fn video_tracks(&self) -> Vec { + self.sys_handle + .get_video_tracks() + .into_iter() + .map(|t| video_track::RtcVideoTrack { handle: RtcVideoTrack::new(t.ptr) }) + .collect() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/media_stream_track.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/media_stream_track.rs new file mode 100644 index 0000000..43165e1 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/media_stream_track.rs @@ -0,0 +1,78 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use cxx::SharedPtr; +use webrtc_sys::{ + audio_track::ffi::media_to_audio, media_stream_track as sys_mst, + video_track::ffi::media_to_video, MEDIA_TYPE_AUDIO, MEDIA_TYPE_VIDEO, +}; + +use crate::{ + audio_track, + imp::{audio_track::RtcAudioTrack, video_track::RtcVideoTrack}, + media_stream_track::{MediaStreamTrack, RtcTrackState}, + video_track, +}; + +impl From for RtcTrackState { + fn from(state: sys_mst::ffi::TrackState) -> Self { + match state { + sys_mst::ffi::TrackState::Live => RtcTrackState::Live, + sys_mst::ffi::TrackState::Ended => RtcTrackState::Ended, + _ => panic!("unknown TrackState"), + } + } +} + +pub fn new_media_stream_track( + sys_handle: SharedPtr, +) -> MediaStreamTrack { + if sys_handle.kind() == MEDIA_TYPE_AUDIO { + MediaStreamTrack::Audio(audio_track::RtcAudioTrack { + handle: RtcAudioTrack { sys_handle: unsafe { media_to_audio(sys_handle) } }, + }) + } else if sys_handle.kind() == MEDIA_TYPE_VIDEO { + MediaStreamTrack::Video(video_track::RtcVideoTrack { + handle: RtcVideoTrack::new(unsafe { media_to_video(sys_handle) }), + }) + } else { + panic!("unknown track kind") + } +} + +macro_rules! impl_media_stream_track { + ($cast:expr) => { + pub fn id(&self) -> String { + let ptr = $cast(self.sys_handle.clone()); + ptr.id() + } + + pub fn enabled(&self) -> bool { + let ptr = $cast(self.sys_handle.clone()); + ptr.enabled() + } + + pub fn set_enabled(&self, enabled: bool) -> bool { + let ptr = $cast(self.sys_handle.clone()); + ptr.set_enabled(enabled) + } + + pub fn state(&self) -> RtcTrackState { + let ptr = $cast(self.sys_handle.clone()); + ptr.state().into() + } + }; +} + +pub(super) use impl_media_stream_track; diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/mod.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/mod.rs new file mode 100644 index 0000000..de56e33 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/mod.rs @@ -0,0 +1,72 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[cfg(target_os = "android")] +pub mod android; +pub mod apm; +pub mod audio_mixer; +pub mod audio_resampler; +pub mod audio_source; +pub mod audio_stream; +pub mod audio_track; +pub mod data_channel; +#[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))] +pub mod desktop_capturer; +pub mod frame_cryptor; +pub mod ice_candidate; +pub mod media_stream; +pub mod media_stream_track; +pub mod packet_trailer; +pub mod peer_connection; +pub mod peer_connection_factory; +pub mod rtp_parameters; +pub mod rtp_receiver; +pub mod rtp_sender; +pub mod rtp_transceiver; +pub mod session_description; +pub mod video_frame; +pub mod video_source; +pub mod video_stream; +pub mod video_track; +pub mod yuv_helper; + +use webrtc_sys::{rtc_error as sys_err, webrtc as sys_rtc}; + +use crate::{MediaType, RtcError, RtcErrorType}; + +impl From for RtcErrorType { + fn from(value: sys_err::ffi::RtcErrorType) -> Self { + match value { + sys_err::ffi::RtcErrorType::InvalidState => Self::InvalidState, + _ => Self::Internal, + } + } +} + +impl From for RtcError { + fn from(value: sys_err::ffi::RtcError) -> Self { + Self { error_type: value.error_type.into(), message: value.message } + } +} + +impl From for sys_rtc::ffi::MediaType { + fn from(value: MediaType) -> Self { + match value { + MediaType::Audio => Self::Audio, + MediaType::Video => Self::Video, + MediaType::Data => Self::Data, + MediaType::Unsupported => Self::Unsupported, + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/packet_trailer.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/packet_trailer.rs new file mode 100644 index 0000000..6ba68a9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/packet_trailer.rs @@ -0,0 +1,273 @@ +// Copyright 2026 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +//! Packet trailer support for end-to-end frame metadata propagation. +//! +//! This module provides functionality to embed user-supplied metadata +//! in encoded video frames as trailers. The timestamps/frameIDs are preserved +//! through the WebRTC pipeline and can be extracted on the receiver side. +//! +//! On the send side, user timestamps/frameIDs are stored in the handler's internal +//! map keyed by RTP timestamp. When the encoder produces a frame, +//! the transformer looks up the metadata via the frame's CaptureTime(). +//! +//! On the receive side, extracted frame metadata is stored in an +//! internal map keyed by RTP timestamp. Decoded frames look up their +//! metadata via lookup_frame_metadata(rtp_timestamp). + +use std::sync::Arc; + +use cxx::SharedPtr; +use webrtc_sys::packet_trailer::ffi as sys_pt; + +use crate::{ + peer_connection_factory::PeerConnectionFactory, rtp_receiver::RtpReceiver, + rtp_sender::RtpSender, +}; + +/// Stage reached by a native local video frame in the publish pipeline. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PublishTimingStage { + /// The adapted raw frame was handed to WebRTC's encoder path. + EncoderUpload, + /// WebRTC produced an encoded frame for packetization. + EncoderOutput, + /// The encoded frame was handed back to WebRTC's packetizer. + WebrtcPacketize, +} + +/// Stage reached by a native remote video frame in the subscribe pipeline. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SubscribeTimingStage { + /// WebRTC produced an encoded frame after RTP depacketization. + WebrtcReceive, + /// The encoded frame was handed to WebRTC's decoder. + DecoderUpload, + /// WebRTC produced a decoded frame for the native video sink. + DecoderOutput, +} + +/// Timestamped native local video publish pipeline event. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PublishTimingEvent { + /// Publish pipeline stage reached by the frame. + pub stage: PublishTimingStage, + /// Wall-clock time when this stage was observed, in microseconds since the Unix epoch. + pub timestamp_us: u64, + /// User capture timestamp associated with this frame, in microseconds since the Unix epoch. + pub capture_timestamp_us: u64, + /// Optional application frame ID associated with this frame. + pub frame_id: Option, +} + +/// Timestamped native remote video subscribe pipeline event. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct SubscribeTimingEvent { + /// Subscribe pipeline stage reached by the frame. + pub stage: SubscribeTimingStage, + /// Wall-clock time when this stage was observed, in microseconds since the Unix epoch. + pub timestamp_us: u64, + /// User capture timestamp associated with this frame, in microseconds since the Unix epoch. + pub capture_timestamp_us: u64, + /// Optional application frame ID associated with this frame. + pub frame_id: Option, +} + +/// Callback invoked for native local video publish timing events. +pub type PublishTimingObserver = Arc; +/// Callback invoked for native remote video subscribe timing events. +pub type SubscribeTimingObserver = Arc; + +impl From for PublishTimingStage { + fn from(stage: sys_pt::VideoPublishTimingStage) -> Self { + match stage { + sys_pt::VideoPublishTimingStage::EncoderUpload => Self::EncoderUpload, + sys_pt::VideoPublishTimingStage::EncoderOutput => Self::EncoderOutput, + sys_pt::VideoPublishTimingStage::WebrtcPacketize => Self::WebrtcPacketize, + _ => Self::WebrtcPacketize, + } + } +} + +impl From for PublishTimingEvent { + fn from(event: sys_pt::VideoPublishTimingEvent) -> Self { + Self { + stage: event.stage.into(), + timestamp_us: event.timestamp_us, + capture_timestamp_us: event.capture_timestamp_us, + frame_id: (event.frame_id != 0).then_some(event.frame_id), + } + } +} + +impl From for SubscribeTimingStage { + fn from(stage: sys_pt::VideoSubscribeTimingStage) -> Self { + match stage { + sys_pt::VideoSubscribeTimingStage::WebrtcReceive => Self::WebrtcReceive, + sys_pt::VideoSubscribeTimingStage::DecoderUpload => Self::DecoderUpload, + sys_pt::VideoSubscribeTimingStage::DecoderOutput => Self::DecoderOutput, + _ => Self::DecoderOutput, + } + } +} + +impl From for SubscribeTimingEvent { + fn from(event: sys_pt::VideoSubscribeTimingEvent) -> Self { + Self { + stage: event.stage.into(), + timestamp_us: event.timestamp_us, + capture_timestamp_us: event.capture_timestamp_us, + frame_id: (event.frame_id != 0).then_some(event.frame_id), + } + } +} + +/// Handler for packet trailer embedding/extraction on RTP streams. +/// +/// For sender side: Stores frame metadata keyed by capture timestamp +/// and embeds them as binary payload trailers on encoded frames before they +/// are sent. Use `store_frame_metadata()` to associate metadata with +/// a captured frame. +/// +/// For receiver side: Extracts frame metadata from received frames +/// and makes them available for retrieval via `lookup_frame_metadata()`. +#[derive(Clone)] +pub struct PacketTrailerHandler { + sys_handle: SharedPtr, +} + +impl PacketTrailerHandler { + /// Enable or disable timestamp embedding/extraction. + pub fn set_enabled(&self, enabled: bool) { + self.sys_handle.set_enabled(enabled); + } + + /// Check if timestamp embedding/extraction is enabled. + pub fn enabled(&self) -> bool { + self.sys_handle.enabled() + } + + /// Lookup the frame metadata for a given RTP timestamp (receiver side). + /// Returns `Some((user_timestamp, frame_id))` if found, `None` otherwise. + /// The entry is removed from the map after a successful lookup. + pub fn lookup_frame_metadata(&self, rtp_timestamp: u32) -> Option<(u64, u32)> { + let ts = self.sys_handle.lookup_timestamp(rtp_timestamp); + if ts != u64::MAX { + let frame_id = self.sys_handle.last_lookup_frame_id(); + Some((ts, frame_id)) + } else { + None + } + } + + /// Store frame metadata for a given capture timestamp (sender side). + /// + /// The `capture_timestamp_us` must be the TimestampAligner-adjusted + /// timestamp (as produced by `VideoTrackSource::on_captured_frame`), + /// NOT the original `timestamp_us` from the VideoFrame. The transformer + /// looks up the metadata by the frame's `CaptureTime()` which is + /// derived from the aligned value. + /// + /// In normal usage this is called automatically by the C++ layer -- + /// callers should set `user_timestamp` and `frame_id` on the + /// `VideoFrame` and let `capture_frame` / `on_captured_frame` handle + /// the rest. + pub fn store_frame_metadata( + &self, + capture_timestamp_us: i64, + user_timestamp: u64, + frame_id: u32, + ) { + self.sys_handle.store_frame_metadata(capture_timestamp_us, user_timestamp, frame_id); + } + + pub(crate) fn sys_handle(&self) -> SharedPtr { + self.sys_handle.clone() + } + + /// Set the callback receiving sender-side publish timing events. + pub fn set_publish_timing_observer(&self, observer: Option) { + if let Some(observer) = observer { + self.sys_handle.set_publish_timing_observer(Box::new( + webrtc_sys::packet_trailer::VideoPublishTimingObserverWrapper::new(Box::new( + move |event| observer(event.into()), + )), + )); + } else { + self.sys_handle.clear_publish_timing_observer(); + } + } + + /// Set the callback receiving receiver-side subscribe timing events. + pub fn set_subscribe_timing_observer(&self, observer: Option) { + if let Some(observer) = observer { + self.sys_handle.set_subscribe_timing_observer(Box::new( + webrtc_sys::packet_trailer::VideoSubscribeTimingObserverWrapper::new(Box::new( + move |event| observer(event.into()), + )), + )); + } else { + self.sys_handle.clear_subscribe_timing_observer(); + } + } + + pub(crate) fn emit_subscribe_timing( + &self, + stage: SubscribeTimingStage, + capture_timestamp_us: u64, + frame_id: u32, + ) { + let stage = match stage { + SubscribeTimingStage::WebrtcReceive => sys_pt::VideoSubscribeTimingStage::WebrtcReceive, + SubscribeTimingStage::DecoderUpload => sys_pt::VideoSubscribeTimingStage::DecoderUpload, + SubscribeTimingStage::DecoderOutput => sys_pt::VideoSubscribeTimingStage::DecoderOutput, + }; + self.sys_handle.emit_subscribe_timing(stage, capture_timestamp_us, frame_id); + } +} + +/// Create a sender-side packet trailer handler. +/// +/// This handler will embed frame metadata into encoded frames before +/// they are packetized and sent. Use `store_frame_metadata()` to +/// associate metadata with a captured frame's capture timestamp. +pub fn create_sender_handler( + peer_factory: &PeerConnectionFactory, + sender: &RtpSender, +) -> PacketTrailerHandler { + PacketTrailerHandler { + sys_handle: sys_pt::new_packet_trailer_sender( + peer_factory.handle.sys_handle.clone(), + sender.handle.sys_handle.clone(), + ), + } +} + +/// Create a receiver-side packet trailer handler. +/// +/// This handler will extract frame metadata from received frames +/// and store them in a map keyed by RTP timestamp. Use +/// `lookup_frame_metadata(rtp_timestamp)` to retrieve the metadata +/// for a specific decoded frame. +pub fn create_receiver_handler( + peer_factory: &PeerConnectionFactory, + receiver: &RtpReceiver, +) -> PacketTrailerHandler { + PacketTrailerHandler { + sys_handle: sys_pt::new_packet_trailer_receiver( + peer_factory.handle.sys_handle.clone(), + receiver.handle.sys_handle.clone(), + ), + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/peer_connection.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/peer_connection.rs new file mode 100644 index 0000000..cf053ed --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/peer_connection.rs @@ -0,0 +1,619 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::sync::Arc; + +use cxx::SharedPtr; +use parking_lot::Mutex; +use tokio::sync::{mpsc, oneshot}; +use webrtc_sys::{ + data_channel as sys_dc, jsep as sys_jsep, peer_connection as sys_pc, + peer_connection_factory as sys_pcf, rtc_error as sys_err, +}; + +use crate::{ + data_channel::{DataChannel, DataChannelInit}, + ice_candidate::IceCandidate, + imp::{ + data_channel as imp_dc, ice_candidate as imp_ic, media_stream as imp_ms, + media_stream_track as imp_mst, rtp_receiver as imp_rr, rtp_sender as imp_rs, + rtp_transceiver as imp_rt, session_description as imp_sdp, + }, + media_stream::MediaStream, + media_stream_track::MediaStreamTrack, + peer_connection::{ + AnswerOptions, IceCandidateError, IceConnectionState, IceGatheringState, OfferOptions, + OnConnectionChange, OnDataChannel, OnIceCandidate, OnIceCandidateError, + OnIceConnectionChange, OnIceGatheringChange, OnNegotiationNeeded, OnSignalingChange, + OnTrack, PeerConnectionState, SignalingState, TrackEvent, + }, + peer_connection_factory::{ + ContinualGatheringPolicy, IceServer, IceTransportsType, RtcConfiguration, + }, + rtp_receiver::RtpReceiver, + rtp_sender::RtpSender, + rtp_transceiver::{RtpTransceiver, RtpTransceiverInit}, + session_description::SessionDescription, + stats::RtcStats, + MediaType, RtcError, RtcErrorType, +}; + +impl From for sys_pc::ffi::RtcOfferAnswerOptions { + fn from(options: OfferOptions) -> Self { + Self { + ice_restart: options.ice_restart, + offer_to_receive_audio: options.offer_to_receive_audio as i32, + offer_to_receive_video: options.offer_to_receive_video as i32, + ..Default::default() + } + } +} + +impl From for sys_pc::ffi::RtcOfferAnswerOptions { + fn from(_options: AnswerOptions) -> Self { + Self::default() + } +} + +impl From for PeerConnectionState { + fn from(state: sys_pc::ffi::PeerConnectionState) -> Self { + match state { + sys_pc::ffi::PeerConnectionState::New => PeerConnectionState::New, + sys_pc::ffi::PeerConnectionState::Connecting => PeerConnectionState::Connecting, + sys_pc::ffi::PeerConnectionState::Connected => PeerConnectionState::Connected, + sys_pc::ffi::PeerConnectionState::Disconnected => PeerConnectionState::Disconnected, + sys_pc::ffi::PeerConnectionState::Failed => PeerConnectionState::Failed, + sys_pc::ffi::PeerConnectionState::Closed => PeerConnectionState::Closed, + _ => panic!("unknown PeerConnectionState"), + } + } +} + +impl From for IceConnectionState { + fn from(state: sys_pc::ffi::IceConnectionState) -> Self { + match state { + sys_pc::ffi::IceConnectionState::IceConnectionNew => IceConnectionState::New, + sys_pc::ffi::IceConnectionState::IceConnectionChecking => IceConnectionState::Checking, + sys_pc::ffi::IceConnectionState::IceConnectionConnected => { + IceConnectionState::Connected + } + sys_pc::ffi::IceConnectionState::IceConnectionCompleted => { + IceConnectionState::Completed + } + sys_pc::ffi::IceConnectionState::IceConnectionFailed => IceConnectionState::Failed, + sys_pc::ffi::IceConnectionState::IceConnectionDisconnected => { + IceConnectionState::Disconnected + } + sys_pc::ffi::IceConnectionState::IceConnectionClosed => IceConnectionState::Closed, + sys_pc::ffi::IceConnectionState::IceConnectionMax => IceConnectionState::Max, + _ => panic!("unknown IceConnectionState"), + } + } +} + +impl From for IceGatheringState { + fn from(state: sys_pc::ffi::IceGatheringState) -> Self { + match state { + sys_pc::ffi::IceGatheringState::IceGatheringNew => IceGatheringState::New, + sys_pc::ffi::IceGatheringState::IceGatheringGathering => IceGatheringState::Gathering, + sys_pc::ffi::IceGatheringState::IceGatheringComplete => IceGatheringState::Complete, + _ => panic!("unknown IceGatheringState"), + } + } +} + +impl From for SignalingState { + fn from(state: sys_pc::ffi::SignalingState) -> Self { + match state { + sys_pc::ffi::SignalingState::Stable => SignalingState::Stable, + sys_pc::ffi::SignalingState::HaveLocalOffer => SignalingState::HaveLocalOffer, + sys_pc::ffi::SignalingState::HaveRemoteOffer => SignalingState::HaveRemoteOffer, + sys_pc::ffi::SignalingState::HaveLocalPrAnswer => SignalingState::HaveLocalPrAnswer, + sys_pc::ffi::SignalingState::HaveRemotePrAnswer => SignalingState::HaveRemotePrAnswer, + sys_pc::ffi::SignalingState::Closed => SignalingState::Closed, + _ => panic!("unknown SignalingState"), + } + } +} + +impl From for sys_pc::ffi::IceServer { + fn from(value: IceServer) -> Self { + sys_pc::ffi::IceServer { + urls: value.urls, + username: value.username, + password: value.password, + } + } +} + +impl From for sys_pc::ffi::ContinualGatheringPolicy { + fn from(value: ContinualGatheringPolicy) -> Self { + match value { + ContinualGatheringPolicy::GatherOnce => { + sys_pc::ffi::ContinualGatheringPolicy::GatherOnce + } + ContinualGatheringPolicy::GatherContinually => { + sys_pc::ffi::ContinualGatheringPolicy::GatherContinually + } + } + } +} + +impl From for sys_pc::ffi::IceTransportsType { + fn from(value: IceTransportsType) -> Self { + match value { + IceTransportsType::Relay => sys_pc::ffi::IceTransportsType::Relay, + IceTransportsType::NoHost => sys_pc::ffi::IceTransportsType::NoHost, + IceTransportsType::All => sys_pc::ffi::IceTransportsType::All, + } + } +} + +impl From for sys_pc::ffi::RtcConfiguration { + fn from(value: RtcConfiguration) -> Self { + Self { + ice_servers: value.ice_servers.into_iter().map(Into::into).collect(), + continual_gathering_policy: value.continual_gathering_policy.into(), + ice_transport_type: value.ice_transport_type.into(), + } + } +} + +#[derive(Clone)] +pub struct PeerConnection { + observer: Arc, + pub(crate) sys_handle: SharedPtr, +} + +impl PeerConnection { + pub fn configure( + sys_handle: SharedPtr, + observer: Arc, + ) -> Self { + Self { sys_handle, observer } + } + + pub fn set_configuration(&self, config: RtcConfiguration) -> Result<(), RtcError> { + let res = self.sys_handle.set_configuration(config.into()); + + match res { + Ok(_) => Ok(()), + Err(e) => unsafe { Err(sys_err::ffi::RtcError::from(e.what()).into()) }, + } + } + + pub async fn create_offer( + &self, + options: OfferOptions, + ) -> Result { + let (tx, mut rx) = mpsc::channel::>(1); + let ctx = Box::new(sys_pc::PeerContext(Box::new(tx))); + type CtxType = mpsc::Sender>; + + self.sys_handle.create_offer( + options.into(), + ctx, + |ctx, sdp| { + let tx = *ctx.0.downcast::().unwrap(); + let _ = tx.blocking_send(Ok(SessionDescription { + handle: imp_sdp::SessionDescription { sys_handle: sdp }, + })); + }, + |ctx, error| { + let tx = *ctx.0.downcast::().unwrap(); + let _ = tx.blocking_send(Err(error.into())); + }, + ); + + rx.recv().await.unwrap() + } + + pub async fn create_answer( + &self, + options: AnswerOptions, + ) -> Result { + let (tx, mut rx) = mpsc::channel::>(1); + let ctx = Box::new(sys_pc::PeerContext(Box::new(tx))); + type CtxType = mpsc::Sender>; + + self.sys_handle.create_answer( + options.into(), + ctx, + |ctx, sdp| { + let tx = *ctx.0.downcast::().unwrap(); + let _ = tx.blocking_send(Ok(SessionDescription { + handle: imp_sdp::SessionDescription { sys_handle: sdp }, + })); + }, + |ctx, error| { + let tx = *ctx.0.downcast::().unwrap(); + let _ = tx.blocking_send(Err(error.into())); + }, + ); + + rx.recv().await.unwrap() + } + + pub async fn set_local_description(&self, desc: SessionDescription) -> Result<(), RtcError> { + let (tx, rx) = oneshot::channel::>(); + let ctx = Box::new(sys_pc::PeerContext(Box::new(tx))); + + self.sys_handle.set_local_description(desc.handle.sys_handle, ctx, |ctx, err| { + let tx = ctx.0.downcast::>>().unwrap(); + + if err.ok() { + let _ = tx.send(Ok(())); + } else { + let _ = tx.send(Err(err.into())); + } + }); + + rx.await.unwrap() + } + + pub async fn set_remote_description(&self, desc: SessionDescription) -> Result<(), RtcError> { + let (tx, rx) = oneshot::channel::>(); + let ctx = Box::new(sys_pc::PeerContext(Box::new(tx))); + + self.sys_handle.set_remote_description(desc.handle.sys_handle, ctx, |ctx, err| { + let tx = ctx.0.downcast::>>().unwrap(); + + if err.ok() { + let _ = tx.send(Ok(())); + } else { + let _ = tx.send(Err(err.into())); + } + }); + + rx.await.map_err(|_| RtcError { + error_type: RtcErrorType::Internal, + message: "set_remote_description cancelled".to_owned(), + })? + } + + pub async fn add_ice_candidate(&self, candidate: IceCandidate) -> Result<(), RtcError> { + let (tx, rx) = oneshot::channel::>(); + let ctx = Box::new(sys_pc::PeerContext(Box::new(tx))); + + self.sys_handle.add_ice_candidate(candidate.handle.sys_handle, ctx, |ctx, err| { + let tx = ctx.0.downcast::>>().unwrap(); + + if err.ok() { + let _ = tx.send(Ok(())); + } else { + let _ = tx.send(Err(err.into())); + } + }); + + rx.await.map_err(|_| RtcError { + error_type: RtcErrorType::Internal, + message: "add_ice_candidate cancelled".to_owned(), + })? + } + + pub fn create_data_channel( + &self, + label: &str, + init: DataChannelInit, + ) -> Result { + let res = self.sys_handle.create_data_channel(label.to_string(), init.into()); + + match res { + Ok(sys_handle) => { + Ok(DataChannel { handle: imp_dc::DataChannel::configure(sys_handle) }) + } + Err(e) => Err(unsafe { sys_err::ffi::RtcError::from(e.what()).into() }), + } + } + + pub fn add_track>( + &self, + track: MediaStreamTrack, + stream_ids: &[T], + ) -> Result { + let stream_ids = stream_ids.iter().map(|s| s.as_ref().to_owned()).collect(); + let res = self.sys_handle.add_track(track.sys_handle(), &stream_ids); + + match res { + Ok(sys_handle) => Ok(RtpSender { handle: imp_rs::RtpSender { sys_handle } }), + Err(e) => unsafe { Err(sys_err::ffi::RtcError::from(e.what()).into()) }, + } + } + + pub fn add_transceiver( + &self, + track: MediaStreamTrack, + init: RtpTransceiverInit, + ) -> Result { + let res = self.sys_handle.add_transceiver(track.sys_handle(), init.into()); + + match res { + Ok(sys_handle) => Ok(RtpTransceiver { handle: imp_rt::RtpTransceiver { sys_handle } }), + Err(e) => unsafe { Err(sys_err::ffi::RtcError::from(e.what()).into()) }, + } + } + + pub fn add_transceiver_for_media( + &self, + media_type: MediaType, + init: RtpTransceiverInit, + ) -> Result { + let res = self.sys_handle.add_transceiver_for_media(media_type.into(), init.into()); + + match res { + Ok(cxx_handle) => { + Ok(RtpTransceiver { handle: imp_rt::RtpTransceiver { sys_handle: cxx_handle } }) + } + Err(e) => unsafe { Err(sys_err::ffi::RtcError::from(e.what()).into()) }, + } + } + + pub fn restart_ice(&self) { + self.sys_handle.restart_ice(); + } + + pub fn close(&self) { + self.sys_handle.close(); + } + + pub fn connection_state(&self) -> PeerConnectionState { + self.sys_handle.connection_state().into() + } + + pub fn ice_connection_state(&self) -> IceConnectionState { + self.sys_handle.ice_connection_state().into() + } + + pub fn ice_gathering_state(&self) -> IceGatheringState { + self.sys_handle.ice_gathering_state().into() + } + + pub fn signaling_state(&self) -> SignalingState { + self.sys_handle.signaling_state().into() + } + + pub fn current_local_description(&self) -> Option { + let sdp = self.sys_handle.current_local_description(); + if sdp.is_null() { + return None; + } + + Some(SessionDescription { handle: imp_sdp::SessionDescription { sys_handle: sdp } }) + } + + pub fn current_remote_description(&self) -> Option { + let sdp = self.sys_handle.current_remote_description(); + if sdp.is_null() { + return None; + } + + Some(SessionDescription { handle: imp_sdp::SessionDescription { sys_handle: sdp } }) + } + + pub fn remove_track(&self, sender: RtpSender) -> Result<(), RtcError> { + self.sys_handle + .remove_track(sender.handle.sys_handle) + .map_err(|e| unsafe { sys_err::ffi::RtcError::from(e.what()).into() }) + } + + pub async fn get_stats(&self) -> Result, RtcError> { + let (tx, rx) = oneshot::channel::, RtcError>>(); + let ctx = Box::new(sys_pc::PeerContext(Box::new(tx))); + + self.sys_handle.get_stats(ctx, |ctx, stats| { + let tx = ctx.0.downcast::, RtcError>>>().unwrap(); + + if stats.is_empty() { + let _ = tx.send(Ok(vec![])); + return; + } + + // Unwrap because it should not happens + let vec = serde_json::from_str(&stats).unwrap(); + let _ = tx.send(Ok(vec)); + }); + + rx.await.map_err(|_| RtcError { + error_type: RtcErrorType::Internal, + message: "get_stats cancelled".to_owned(), + })? + } + + pub fn senders(&self) -> Vec { + self.sys_handle + .get_senders() + .into_iter() + .map(|sender| RtpSender { handle: imp_rs::RtpSender { sys_handle: sender.ptr } }) + .collect() + } + + pub fn receivers(&self) -> Vec { + self.sys_handle + .get_receivers() + .into_iter() + .map(|receiver| RtpReceiver { + handle: imp_rr::RtpReceiver { sys_handle: receiver.ptr }, + }) + .collect() + } + + pub fn transceivers(&self) -> Vec { + self.sys_handle + .get_transceivers() + .into_iter() + .map(|transceiver| RtpTransceiver { + handle: imp_rt::RtpTransceiver { sys_handle: transceiver.ptr }, + }) + .collect() + } + + pub fn on_connection_state_change(&self, f: Option) { + *self.observer.connection_change_handler.lock() = f; + } + + pub fn on_data_channel(&self, f: Option) { + *self.observer.data_channel_handler.lock() = f; + } + + pub fn on_ice_candidate(&self, f: Option) { + *self.observer.ice_candidate_handler.lock() = f; + } + + pub fn on_ice_candidate_error(&self, f: Option) { + *self.observer.ice_candidate_error_handler.lock() = f; + } + + pub fn on_ice_connection_state_change(&self, f: Option) { + *self.observer.ice_connection_change_handler.lock() = f; + } + + pub fn on_ice_gathering_state_change(&self, f: Option) { + *self.observer.ice_gathering_change_handler.lock() = f; + } + + pub fn on_negotiation_needed(&self, f: Option) { + *self.observer.negotiation_needed_handler.lock() = f; + } + + pub fn on_signaling_state_change(&self, f: Option) { + *self.observer.signaling_change_handler.lock() = f; + } + + pub fn on_track(&self, f: Option) { + *self.observer.track_handler.lock() = f; + } +} + +#[derive(Default)] +pub struct PeerObserver { + pub connection_change_handler: Mutex>, + pub data_channel_handler: Mutex>, + pub ice_candidate_handler: Mutex>, + pub ice_candidate_error_handler: Mutex>, + pub ice_connection_change_handler: Mutex>, + pub ice_gathering_change_handler: Mutex>, + pub negotiation_needed_handler: Mutex>, + pub signaling_change_handler: Mutex>, + pub track_handler: Mutex>, +} + +impl sys_pcf::PeerConnectionObserver for PeerObserver { + fn on_signaling_change(&self, new_state: sys_pc::ffi::SignalingState) { + if let Some(f) = self.signaling_change_handler.lock().as_mut() { + f(new_state.into()); + } + } + + fn on_add_stream(&self, _stream: SharedPtr) {} + + fn on_remove_stream(&self, _stream: SharedPtr) {} + + fn on_data_channel(&self, data_channel: SharedPtr) { + if let Some(f) = self.data_channel_handler.lock().as_mut() { + f(DataChannel { handle: imp_dc::DataChannel::configure(data_channel) }); + } + } + + fn on_renegotiation_needed(&self) {} + + fn on_negotiation_needed_event(&self, event: u32) { + if let Some(f) = self.negotiation_needed_handler.lock().as_mut() { + f(event); + } + } + + fn on_ice_connection_change(&self, _new_state: sys_pc::ffi::IceConnectionState) {} + + fn on_standardized_ice_connection_change(&self, new_state: sys_pc::ffi::IceConnectionState) { + if let Some(f) = self.ice_connection_change_handler.lock().as_mut() { + f(new_state.into()); + } + } + + fn on_connection_change(&self, new_state: sys_pc::ffi::PeerConnectionState) { + if let Some(f) = self.connection_change_handler.lock().as_mut() { + f(new_state.into()); + } + } + + fn on_ice_gathering_change(&self, new_state: sys_pc::ffi::IceGatheringState) { + if let Some(f) = self.ice_gathering_change_handler.lock().as_mut() { + f(new_state.into()); + } + } + + fn on_ice_candidate(&self, candidate: SharedPtr) { + if let Some(f) = self.ice_candidate_handler.lock().as_mut() { + f(IceCandidate { handle: imp_ic::IceCandidate { sys_handle: candidate } }); + } + } + + fn on_ice_candidate_error( + &self, + address: String, + port: i32, + url: String, + error_code: i32, + error_text: String, + ) { + if let Some(f) = self.ice_candidate_error_handler.lock().as_mut() { + f(IceCandidateError { address, port, url, error_code, error_text }); + } + } + + fn on_ice_candidates_removed( + &self, + _removed: Vec>, + ) { + } + + fn on_ice_connection_receiving_change(&self, _receiving: bool) {} + + fn on_ice_selected_candidate_pair_changed( + &self, + _event: sys_pcf::ffi::CandidatePairChangeEvent, + ) { + } + + fn on_add_track( + &self, + _receiver: SharedPtr, + _streams: Vec>, + ) { + } + + fn on_track(&self, transceiver: SharedPtr) { + if let Some(f) = self.track_handler.lock().as_mut() { + let receiver = transceiver.receiver(); + let streams = receiver.streams(); + let track = receiver.track(); + + f(TrackEvent { + receiver: RtpReceiver { handle: imp_rr::RtpReceiver { sys_handle: receiver } }, + streams: streams + .into_iter() + .map(|s| MediaStream { handle: imp_ms::MediaStream { sys_handle: s.ptr } }) + .collect(), + track: imp_mst::new_media_stream_track(track), + transceiver: RtpTransceiver { + handle: imp_rt::RtpTransceiver { sys_handle: transceiver }, + }, + }); + } + } + + fn on_remove_track(&self, _receiver: SharedPtr) {} + + fn on_interesting_usage(&self, _usage_pattern: i32) {} +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/peer_connection_factory.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/peer_connection_factory.rs new file mode 100644 index 0000000..99363f1 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/peer_connection_factory.rs @@ -0,0 +1,361 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::sync::Arc; + +use cxx::{SharedPtr, UniquePtr}; +use lazy_static::lazy_static; +use parking_lot::Mutex; +use webrtc_sys::{peer_connection_factory as sys_pcf, rtc_error as sys_err, webrtc as sys_rtc}; + +use crate::{ + audio_source::native::NativeAudioSource, + audio_track::RtcAudioTrack, + imp::{audio_track as imp_at, peer_connection as imp_pc, video_track as imp_vt}, + peer_connection::PeerConnection, + peer_connection_factory::RtcConfiguration, + rtp_parameters::RtpCapabilities, + video_source::native::NativeVideoSource, + video_track::RtcVideoTrack, + MediaType, RtcError, +}; + +lazy_static! { + static ref LOG_SINK: Mutex>> = Default::default(); +} + +#[derive(Clone)] +pub struct PeerConnectionFactory { + pub(crate) sys_handle: SharedPtr, +} + +impl Default for PeerConnectionFactory { + fn default() -> Self { + let mut log_sink = LOG_SINK.lock(); + if log_sink.is_none() { + *log_sink = Some(sys_rtc::ffi::new_log_sink(|msg, _| { + let msg = msg.strip_suffix("\r\n").or(msg.strip_suffix('\n')).unwrap_or(&msg); + log::debug!(target: "libwebrtc", "{}", msg); + })); + } + + let sys_handle = sys_pcf::ffi::create_peer_connection_factory(); + Self { sys_handle } + } +} + +impl PeerConnectionFactory { + pub fn create_peer_connection( + &self, + config: RtcConfiguration, + ) -> Result { + let observer = Arc::new(imp_pc::PeerObserver::default()); + let res = self.sys_handle.create_peer_connection( + config.into(), + Box::new(sys_pcf::PeerConnectionObserverWrapper::new(observer.clone())), + ); + + match res { + Ok(sys_handle) => Ok(PeerConnection { + handle: imp_pc::PeerConnection::configure(sys_handle, observer), + }), + Err(e) => Err(unsafe { sys_err::ffi::RtcError::from(e.what()).into() }), + } + } + + pub fn create_video_track(&self, label: &str, source: NativeVideoSource) -> RtcVideoTrack { + RtcVideoTrack { + handle: imp_vt::RtcVideoTrack::new( + self.sys_handle.create_video_track(label.to_string(), source.handle.sys_handle()), + ), + } + } + + pub fn create_audio_track(&self, label: &str, source: NativeAudioSource) -> RtcAudioTrack { + RtcAudioTrack { + handle: imp_at::RtcAudioTrack { + sys_handle: self + .sys_handle + .create_audio_track(label.to_string(), source.handle.sys_handle()), + }, + } + } + + /// Create an audio track that uses the Platform ADM for capture. + /// + /// This requires that `enable_platform_adm()` was called first. + /// The track will capture audio from the selected recording device. + pub fn create_device_audio_track(&self, label: &str) -> RtcAudioTrack { + RtcAudioTrack { + handle: imp_at::RtcAudioTrack { + sys_handle: self.sys_handle.create_device_audio_track(label.to_string()), + }, + } + } + + pub fn get_rtp_sender_capabilities(&self, media_type: MediaType) -> RtpCapabilities { + self.sys_handle.rtp_sender_capabilities(media_type.into()).into() + } + + pub fn get_rtp_receiver_capabilities(&self, media_type: MediaType) -> RtpCapabilities { + self.sys_handle.rtp_receiver_capabilities(media_type.into()).into() + } + + // ===== Device Management Methods ===== + + /// Get the number of playout (output) devices + pub fn playout_devices(&self) -> i16 { + self.sys_handle.audio_device().playout_devices() + } + + /// Get the number of recording (input) devices + pub fn recording_devices(&self) -> i16 { + self.sys_handle.audio_device().recording_devices() + } + + /// Get the name of a playout device by index + pub fn playout_device_name(&self, index: u16) -> String { + self.sys_handle.audio_device().playout_device_name(index) + } + + /// Get the name of a recording device by index + pub fn recording_device_name(&self, index: u16) -> String { + self.sys_handle.audio_device().recording_device_name(index) + } + + /// Get the GUID of a playout device by index + /// The GUID is a platform-specific unique identifier that is stable across device hot-plug events. + pub fn playout_device_guid(&self, index: u16) -> String { + self.sys_handle.audio_device().playout_device_guid(index) + } + + /// Get the GUID of a recording device by index + /// The GUID is a platform-specific unique identifier that is stable across device hot-plug events. + pub fn recording_device_guid(&self, index: u16) -> String { + self.sys_handle.audio_device().recording_device_guid(index) + } + + /// Set the playout device by index + pub fn set_playout_device(&self, index: u16) -> bool { + self.sys_handle.audio_device().set_playout_device(index) + } + + /// Set the recording device by index + pub fn set_recording_device(&self, index: u16) -> bool { + self.sys_handle.audio_device().set_recording_device(index) + } + + /// Set the playout device by GUID + /// This is preferred over index as GUIDs are stable across device hot-plug events. + pub fn set_playout_device_by_guid(&self, guid: &str) -> bool { + self.sys_handle.audio_device().set_playout_device_by_guid(guid.to_string()) + } + + /// Set the recording device by GUID + /// This is preferred over index as GUIDs are stable across device hot-plug events. + pub fn set_recording_device_by_guid(&self, guid: &str) -> bool { + self.sys_handle.audio_device().set_recording_device_by_guid(guid.to_string()) + } + + /// Stop recording (clears initialized state, allowing device switch) + pub fn stop_recording(&self) -> bool { + self.sys_handle.audio_device().stop_recording() + } + + /// Initialize recording + pub fn init_recording(&self) -> bool { + self.sys_handle.audio_device().init_recording() + } + + /// Start recording + pub fn start_recording(&self) -> bool { + self.sys_handle.audio_device().start_recording() + } + + /// Check if recording is initialized + pub fn recording_is_initialized(&self) -> bool { + self.sys_handle.audio_device().recording_is_initialized() + } + + /// Stop playout (clears initialized state, allowing device switch) + pub fn stop_playout(&self) -> bool { + self.sys_handle.audio_device().stop_playout() + } + + /// Initialize playout + pub fn init_playout(&self) -> bool { + self.sys_handle.audio_device().init_playout() + } + + /// Start playout + pub fn start_playout(&self) -> bool { + self.sys_handle.audio_device().start_playout() + } + + /// Check if playout is initialized + pub fn playout_is_initialized(&self) -> bool { + self.sys_handle.audio_device().playout_is_initialized() + } + + // ===== Built-in Audio Processing Methods ===== + // These control hardware AEC/AGC/NS on platforms that support it (iOS, some Android) + + /// Check if built-in (hardware) AEC is available on this device. + /// + /// Returns true on iOS (VPIO) and some Android devices. + /// Returns false on desktop platforms (macOS, Windows, Linux). + pub fn builtin_aec_is_available(&self) -> bool { + self.sys_handle.audio_device().builtin_aec_is_available() + } + + /// Check if built-in (hardware) AGC is available on this device. + /// + /// Returns true on iOS (VPIO) and some Android devices. + /// Returns false on desktop platforms (macOS, Windows, Linux). + pub fn builtin_agc_is_available(&self) -> bool { + self.sys_handle.audio_device().builtin_agc_is_available() + } + + /// Check if built-in (hardware) NS is available on this device. + /// + /// Returns true on iOS (VPIO) and some Android devices. + /// Returns false on desktop platforms (macOS, Windows, Linux). + pub fn builtin_ns_is_available(&self) -> bool { + self.sys_handle.audio_device().builtin_ns_is_available() + } + + /// Enable or disable built-in (hardware) AEC. + /// + /// When disabled on platforms that support it, WebRTC's software AEC + /// will be used instead. + pub fn enable_builtin_aec(&self, enable: bool) -> bool { + self.sys_handle.audio_device().enable_builtin_aec(enable) + } + + /// Enable or disable built-in (hardware) AGC. + /// + /// When disabled on platforms that support it, WebRTC's software AGC + /// will be used instead. + pub fn enable_builtin_agc(&self, enable: bool) -> bool { + self.sys_handle.audio_device().enable_builtin_agc(enable) + } + + /// Enable or disable built-in (hardware) NS. + /// + /// When disabled on platforms that support it, WebRTC's software NS + /// will be used instead. + pub fn enable_builtin_ns(&self, enable: bool) -> bool { + self.sys_handle.audio_device().enable_builtin_ns(enable) + } + + /// Control whether ADM recording (microphone) is enabled. + /// + /// When disabled, WebRTC's calls to InitRecording/StartRecording will be no-ops. + /// Use this when only using NativeAudioSource (no microphone capture needed). + /// This prevents the microphone from interfering with the audio pipeline. + pub fn set_adm_recording_enabled(&self, enabled: bool) { + self.sys_handle.audio_device().set_adm_recording_enabled(enabled) + } + + /// Check if ADM recording (microphone) is enabled. + pub fn adm_recording_enabled(&self) -> bool { + self.sys_handle.audio_device().adm_recording_enabled() + } + + /// Control whether ADM playout (speakers) is enabled. + /// + /// When disabled (default), playout uses synthetic mode - remote audio is + /// delivered via FFI callbacks to the application (e.g., Unity AudioSource). + /// When enabled, remote audio plays through the platform speakers with AEC. + pub fn set_adm_playout_enabled(&self, enabled: bool) { + self.sys_handle.audio_device().set_adm_playout_enabled(enabled) + } + + /// Check if ADM playout (speakers) is enabled. + pub fn adm_playout_enabled(&self) -> bool { + self.sys_handle.audio_device().adm_playout_enabled() + } + + // ===== Platform ADM Lifecycle Management ===== + + /// Acquires a reference to the Platform ADM. + /// + /// On first call, creates and initializes the Platform ADM. On subsequent + /// calls, just increments the reference count. + /// + /// Returns true if Platform ADM is ready for use, false if initialization failed. + pub fn acquire_platform_adm(&self) -> bool { + self.sys_handle.audio_device().acquire_platform_adm() + } + + /// Releases a reference to the Platform ADM. + /// + /// When the reference count reaches zero, the Platform ADM is terminated + /// and the proxy returns to synthetic mode. + pub fn release_platform_adm(&self) { + self.sys_handle.audio_device().release_platform_adm() + } + + /// Returns the current reference count for the Platform ADM. + pub fn platform_adm_ref_count(&self) -> i32 { + self.sys_handle.audio_device().platform_adm_ref_count() + } + + /// Returns true if Platform ADM is currently active (ref_count > 0). + pub fn is_platform_adm_active(&self) -> bool { + self.sys_handle.audio_device().is_platform_adm_active() + } + + /// Ensures the Platform ADM exists, retrying creation if an earlier + /// attempt failed (e.g. the OS audio stack was still starting up). + /// + /// Returns true if the Platform ADM is available after the call. + pub fn ensure_platform_adm(&self) -> bool { + self.sys_handle.audio_device().ensure_platform_adm() + } + + /// Returns true if the Platform ADM has been created and initialized. + /// Distinguishes "audio stack unavailable" from "zero audio devices". + pub fn platform_adm_available(&self) -> bool { + self.sys_handle.audio_device().platform_adm_available() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::sync::Mutex; + + static TEST_MUTEX: Mutex<()> = Mutex::new(()); + + #[tokio::test] + async fn test_peer_connection_factory_and_audio_device_controller_bridge() { + let _guard = TEST_MUTEX.lock().expect("test mutex poisoned"); + let _ = env_logger::builder().is_test(true).try_init(); + + let factory = PeerConnectionFactory::default(); + let source = NativeVideoSource::default(); + let _track = factory.create_video_track("test", source); + let recording_count = factory.recording_devices(); + let playout_count = factory.playout_devices(); + + assert!(recording_count >= 0); + assert!(playout_count >= 0); + + let initial_recording = factory.adm_recording_enabled(); + factory.set_adm_recording_enabled(!initial_recording); + assert_eq!(factory.adm_recording_enabled(), !initial_recording); + factory.set_adm_recording_enabled(initial_recording); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/rtp_parameters.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/rtp_parameters.rs new file mode 100644 index 0000000..fc6fc28 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/rtp_parameters.rs @@ -0,0 +1,263 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use webrtc_sys::{rtp_parameters as sys_rp, webrtc as sys_webrtc}; + +use crate::rtp_parameters::*; + +impl From for Priority { + fn from(value: sys_webrtc::ffi::Priority) -> Self { + match value { + sys_webrtc::ffi::Priority::VeryLow => Self::VeryLow, + sys_webrtc::ffi::Priority::Low => Self::Low, + sys_webrtc::ffi::Priority::Medium => Self::Medium, + sys_webrtc::ffi::Priority::High => Self::High, + _ => panic!("unknown Priority"), + } + } +} + +impl From for RtpHeaderExtensionParameters { + fn from(value: sys_rp::ffi::RtpExtension) -> Self { + Self { uri: value.uri, id: value.id, encrypted: value.encrypt } + } +} + +impl From for RtpParameters { + fn from(value: sys_rp::ffi::RtpParameters) -> Self { + Self { + codecs: value.codecs.into_iter().map(Into::into).collect(), + header_extensions: value.header_extensions.into_iter().map(Into::into).collect(), + rtcp: value.rtcp.into(), + } + } +} + +impl From for RtpCodecParameters { + fn from(value: sys_rp::ffi::RtpCodecParameters) -> Self { + Self { + mime_type: value.mime_type, + payload_type: value.payload_type as u8, + clock_rate: value.has_clock_rate.then_some(value.clock_rate as u64), + channels: value.has_num_channels.then_some(value.num_channels as u16), + } + } +} + +impl From for RtcpParameters { + fn from(value: sys_rp::ffi::RtcpParameters) -> Self { + Self { cname: value.cname, reduced_size: value.reduced_size } + } +} + +impl From for RtpEncodingParameters { + fn from(value: sys_rp::ffi::RtpEncodingParameters) -> Self { + Self { + active: value.active, + max_bitrate: value.has_max_bitrate_bps.then_some(value.max_bitrate_bps as u64), + max_framerate: value.has_max_framerate.then_some(value.max_framerate), + priority: value.network_priority.into(), + rid: value.rid, + scale_resolution_down_by: value + .has_scale_resolution_down_by + .then_some(value.scale_resolution_down_by), + scalability_mode: value.has_scalability_mode.then_some(value.scalability_mode), + } + } +} + +impl From for RtpCodecCapability { + fn from(value: sys_rp::ffi::RtpCodecCapability) -> Self { + Self { + channels: value.has_num_channels.then_some(value.num_channels as u16), + mime_type: value.mime_type, + clock_rate: value.has_clock_rate.then_some(value.clock_rate as u64), + sdp_fmtp_line: { + let parameters: Vec = value + .parameters + .into_iter() + .map(|key_value| { + if !key_value.key.is_empty() { + format!("{}={}", key_value.key, key_value.value) + } else { + key_value.value + } + }) + .collect(); + + if !parameters.is_empty() { + Some(parameters.join(";")) + } else { + None + } + }, + } + } +} + +impl From for RtpHeaderExtensionCapability { + fn from(value: sys_rp::ffi::RtpHeaderExtensionCapability) -> Self { + Self { direction: value.direction.into(), uri: value.uri } + } +} + +impl From for RtpCapabilities { + fn from(value: sys_rp::ffi::RtpCapabilities) -> Self { + Self { + codecs: value.codecs.into_iter().map(Into::into).collect(), + header_extensions: value.header_extensions.into_iter().map(Into::into).collect(), + } + } +} + +impl From for sys_webrtc::ffi::Priority { + fn from(value: Priority) -> Self { + match value { + Priority::VeryLow => Self::VeryLow, + Priority::Low => Self::Low, + Priority::Medium => Self::Medium, + Priority::High => Self::High, + } + } +} + +impl From for sys_rp::ffi::RtpExtension { + fn from(value: RtpHeaderExtensionParameters) -> Self { + Self { uri: value.uri, id: value.id, encrypt: value.encrypted } + } +} + +impl From for sys_rp::ffi::RtpParameters { + fn from(value: RtpParameters) -> Self { + Self { + codecs: value.codecs.into_iter().map(Into::into).collect(), + header_extensions: value.header_extensions.into_iter().map(Into::into).collect(), + encodings: Vec::new(), + rtcp: value.rtcp.into(), + transaction_id: "".to_string(), + mid: "".to_string(), + has_degradation_preference: false, + degradation_preference: sys_rp::ffi::DegradationPreference::Balanced, + } + } +} + +impl From for sys_rp::ffi::RtpCodecParameters { + fn from(value: RtpCodecParameters) -> Self { + Self { + payload_type: value.payload_type as i32, + mime_type: value.mime_type, + has_clock_rate: value.clock_rate.is_some(), + clock_rate: value.clock_rate.unwrap_or_default() as i32, + has_num_channels: value.channels.is_some(), + num_channels: value.channels.unwrap_or_default() as i32, + name: "".to_string(), + kind: sys_rp::ffi::MediaType::Audio, + has_max_ptime: false, + max_ptime: 0, + has_ptime: false, + ptime: 0, + rtcp_feedback: Vec::new(), + parameters: Vec::new(), + } + } +} + +impl From for sys_rp::ffi::RtcpParameters { + fn from(value: RtcpParameters) -> Self { + Self { + cname: value.cname, + reduced_size: value.reduced_size, + has_ssrc: false, + ssrc: 0, + mux: false, + } + } +} + +impl From for sys_rp::ffi::RtpEncodingParameters { + fn from(value: RtpEncodingParameters) -> Self { + Self { + active: value.active, + has_max_bitrate_bps: value.max_bitrate.is_some(), + max_bitrate_bps: value.max_bitrate.unwrap_or_default() as i32, + has_max_framerate: value.max_framerate.is_some(), + max_framerate: value.max_framerate.unwrap_or_default(), + network_priority: value.priority.into(), + rid: value.rid, + has_scale_resolution_down_by: value.scale_resolution_down_by.is_some(), + scale_resolution_down_by: value.scale_resolution_down_by.unwrap_or_default(), + adaptive_ptime: false, + bitrate_priority: sys_rp::DEFAULT_BITRATE_PRIORITY, + has_min_bitrate_bps: false, + min_bitrate_bps: 0, + has_num_temporal_layers: false, + num_temporal_layers: 0, + has_scalability_mode: value.scalability_mode.is_some(), + scalability_mode: value.scalability_mode.unwrap_or_default(), + has_ssrc: false, + ssrc: 0, + } + } +} + +impl From for sys_rp::ffi::RtpCodecCapability { + fn from(value: RtpCodecCapability) -> Self { + let mime_type: Vec<&str> = value.mime_type.split('/').collect(); + let kind = match mime_type[0] { + "audio" => sys_webrtc::ffi::MediaType::Audio, + "video" => sys_webrtc::ffi::MediaType::Video, + _ => panic!("invalid media type"), + }; + let name = mime_type[1].to_string(); + + Self { + name, + kind, + has_clock_rate: value.clock_rate.is_some(), + clock_rate: value.clock_rate.unwrap_or_default() as i32, + has_num_channels: value.channels.is_some(), + num_channels: value.channels.unwrap_or_default() as i32, + parameters: { + value + .sdp_fmtp_line + .map(|sdp_fmtp_line| { + sdp_fmtp_line + .split(';') + .map(|v| { + let key_value: Vec<&str> = v.split('=').collect(); + if key_value.len() == 2 { + sys_rp::ffi::StringKeyValue { + key: key_value[0].to_string(), + value: key_value[1].to_string(), + } + } else { + sys_rp::ffi::StringKeyValue { + key: "".to_string(), + value: key_value[0].to_string(), + } + } + }) + .collect() + }) + .unwrap_or_default() + }, + // Ignore + mime_type: String::default(), // !! + has_preferred_payload_type: false, + preferred_payload_type: 0, + rtcp_feedback: Vec::default(), + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/rtp_receiver.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/rtp_receiver.rs new file mode 100644 index 0000000..a39b4f9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/rtp_receiver.rs @@ -0,0 +1,65 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use cxx::SharedPtr; +use tokio::sync::oneshot; +use webrtc_sys::rtp_receiver as sys_rr; + +use crate::{ + imp::media_stream_track::new_media_stream_track, media_stream_track::MediaStreamTrack, + rtp_parameters::RtpParameters, stats::RtcStats, RtcError, RtcErrorType, +}; + +#[derive(Clone)] +pub struct RtpReceiver { + pub(crate) sys_handle: SharedPtr, +} + +impl RtpReceiver { + pub fn track(&self) -> Option { + let track_handle = self.sys_handle.track(); + if track_handle.is_null() { + return None; + } + + Some(new_media_stream_track(track_handle)) + } + + pub async fn get_stats(&self) -> Result, RtcError> { + let (tx, rx) = oneshot::channel::, RtcError>>(); + let ctx = Box::new(sys_rr::ReceiverContext(Box::new(tx))); + + self.sys_handle.get_stats(ctx, |ctx, stats| { + let tx = ctx.0.downcast::, RtcError>>>().unwrap(); + + if stats.is_empty() { + let _ = tx.send(Ok(vec![])); + return; + } + + // Unwrap because it should not happens + let vec = serde_json::from_str(&stats).unwrap(); + let _ = tx.send(Ok(vec)); + }); + + rx.await.map_err(|_| RtcError { + error_type: RtcErrorType::Internal, + message: "get_stats cancelled".to_owned(), + })? + } + + pub fn parameters(&self) -> RtpParameters { + self.sys_handle.get_parameters().into() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/rtp_sender.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/rtp_sender.rs new file mode 100644 index 0000000..80bcffc --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/rtp_sender.rs @@ -0,0 +1,83 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use cxx::SharedPtr; +use tokio::sync::oneshot; +use webrtc_sys::{rtc_error as sys_err, rtp_sender as sys_rs}; + +use super::media_stream_track::new_media_stream_track; +use crate::{ + media_stream_track::MediaStreamTrack, rtp_parameters::RtpParameters, stats::RtcStats, RtcError, + RtcErrorType, +}; + +#[derive(Clone)] +pub struct RtpSender { + pub(crate) sys_handle: SharedPtr, +} + +impl RtpSender { + pub fn track(&self) -> Option { + let track_handle = self.sys_handle.track(); + if track_handle.is_null() { + return None; + } + + Some(new_media_stream_track(track_handle)) + } + + pub async fn get_stats(&self) -> Result, RtcError> { + let (tx, rx) = oneshot::channel::, RtcError>>(); + let ctx = Box::new(sys_rs::SenderContext(Box::new(tx))); + + self.sys_handle.get_stats(ctx, |ctx, stats| { + let tx = ctx.0.downcast::, RtcError>>>().unwrap(); + + if stats.is_empty() { + let _ = tx.send(Ok(vec![])); + return; + } + + // Unwrap because it should not happens + let vec = serde_json::from_str(&stats).unwrap(); + let _ = tx.send(Ok(vec)); + }); + + rx.await.map_err(|_| RtcError { + error_type: RtcErrorType::Internal, + message: "get_stats cancelled".to_owned(), + })? + } + + pub fn set_track(&self, track: Option) -> Result<(), RtcError> { + if !self.sys_handle.set_track(track.map_or(SharedPtr::null(), |t| t.sys_handle())) { + return Err(RtcError { + error_type: RtcErrorType::InvalidState, + message: "Failed to set track".to_string(), + }); + } + + Ok(()) + } + + pub fn parameters(&self) -> RtpParameters { + self.sys_handle.get_parameters().into() + } + + pub fn set_parameters(&self, parameters: RtpParameters) -> Result<(), RtcError> { + self.sys_handle + .set_parameters(parameters.into()) + .map_err(|e| unsafe { sys_err::ffi::RtcError::from(e.what()).into() }) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/rtp_transceiver.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/rtp_transceiver.rs new file mode 100644 index 0000000..286eb0d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/rtp_transceiver.rs @@ -0,0 +1,98 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use cxx::SharedPtr; +use webrtc_sys::{rtc_error as sys_err, rtp_transceiver as sys_rt, webrtc as sys_webrtc}; + +use crate::{ + imp::{rtp_receiver::RtpReceiver, rtp_sender::RtpSender}, + rtp_parameters::RtpCodecCapability, + rtp_receiver, rtp_sender, + rtp_transceiver::{RtpTransceiverDirection, RtpTransceiverInit}, + RtcError, +}; + +impl From for RtpTransceiverDirection { + fn from(value: sys_webrtc::ffi::RtpTransceiverDirection) -> Self { + match value { + sys_webrtc::ffi::RtpTransceiverDirection::SendRecv => Self::SendRecv, + sys_webrtc::ffi::RtpTransceiverDirection::SendOnly => Self::SendOnly, + sys_webrtc::ffi::RtpTransceiverDirection::RecvOnly => Self::RecvOnly, + sys_webrtc::ffi::RtpTransceiverDirection::Inactive => Self::Inactive, + sys_webrtc::ffi::RtpTransceiverDirection::Stopped => Self::Stopped, + _ => panic!("unknown RtpTransceiverDirection"), + } + } +} + +impl From for sys_webrtc::ffi::RtpTransceiverDirection { + fn from(value: RtpTransceiverDirection) -> Self { + match value { + RtpTransceiverDirection::SendRecv => Self::SendRecv, + RtpTransceiverDirection::SendOnly => Self::SendOnly, + RtpTransceiverDirection::RecvOnly => Self::RecvOnly, + RtpTransceiverDirection::Inactive => Self::Inactive, + RtpTransceiverDirection::Stopped => Self::Stopped, + } + } +} + +impl From for sys_rt::ffi::RtpTransceiverInit { + fn from(value: RtpTransceiverInit) -> Self { + Self { + direction: value.direction.into(), + stream_ids: value.stream_ids, + send_encodings: value.send_encodings.into_iter().map(Into::into).collect(), + } + } +} + +#[derive(Clone)] +pub struct RtpTransceiver { + pub(crate) sys_handle: SharedPtr, +} + +impl RtpTransceiver { + pub fn mid(&self) -> Option { + self.sys_handle.mid().ok() + } + + pub fn current_direction(&self) -> Option { + self.sys_handle.current_direction().ok().map(Into::into) + } + + pub fn direction(&self) -> RtpTransceiverDirection { + self.sys_handle.direction().into() + } + + pub fn sender(&self) -> rtp_sender::RtpSender { + rtp_sender::RtpSender { handle: RtpSender { sys_handle: self.sys_handle.sender() } } + } + + pub fn receiver(&self) -> rtp_receiver::RtpReceiver { + rtp_receiver::RtpReceiver { handle: RtpReceiver { sys_handle: self.sys_handle.receiver() } } + } + + pub fn set_codec_preferences(&self, codecs: Vec) -> Result<(), RtcError> { + self.sys_handle + .set_codec_preferences(codecs.into_iter().map(Into::into).collect()) + .map_err(|e| unsafe { sys_err::ffi::RtcError::from(e.what()).into() }) + } + + pub fn stop(&self) -> Result<(), RtcError> { + self.sys_handle + .stop_standard() + .map_err(|e| unsafe { sys_err::ffi::RtcError::from(e.what()).into() }) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/session_description.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/session_description.rs new file mode 100644 index 0000000..743230e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/session_description.rs @@ -0,0 +1,82 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use cxx::UniquePtr; +use webrtc_sys::jsep as sys_jsep; + +use crate::session_description::{self, SdpParseError, SdpType}; + +impl From for SdpType { + fn from(sdp_type: sys_jsep::ffi::SdpType) -> Self { + match sdp_type { + sys_jsep::ffi::SdpType::Offer => SdpType::Offer, + sys_jsep::ffi::SdpType::PrAnswer => SdpType::PrAnswer, + sys_jsep::ffi::SdpType::Answer => SdpType::Answer, + sys_jsep::ffi::SdpType::Rollback => SdpType::Rollback, + _ => panic!("unknown SdpType"), + } + } +} + +impl From for sys_jsep::ffi::SdpType { + fn from(sdp_type: SdpType) -> Self { + match sdp_type { + SdpType::Offer => sys_jsep::ffi::SdpType::Offer, + SdpType::PrAnswer => sys_jsep::ffi::SdpType::PrAnswer, + SdpType::Answer => sys_jsep::ffi::SdpType::Answer, + SdpType::Rollback => sys_jsep::ffi::SdpType::Rollback, + } + } +} + +impl From for SdpParseError { + fn from(e: sys_jsep::ffi::SdpParseError) -> Self { + Self { line: e.line, description: e.description } + } +} + +pub struct SessionDescription { + pub(crate) sys_handle: UniquePtr, +} + +impl SessionDescription { + pub fn parse( + sdp: &str, + sdp_type: SdpType, + ) -> Result { + let res = sys_jsep::ffi::create_session_description(sdp_type.into(), sdp.to_owned()); + match res { + Ok(sys_handle) => Ok(session_description::SessionDescription { + handle: SessionDescription { sys_handle }, + }), + Err(e) => Err(unsafe { sys_jsep::ffi::SdpParseError::from(e.what()).into() }), + } + } + + pub fn sdp_type(&self) -> SdpType { + self.sys_handle.sdp_type().into() + } +} + +impl ToString for SessionDescription { + fn to_string(&self) -> String { + self.sys_handle.stringify() + } +} + +impl Clone for SessionDescription { + fn clone(&self) -> Self { + SessionDescription { sys_handle: self.sys_handle.clone() } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/video_frame.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/video_frame.rs new file mode 100644 index 0000000..de7bbda --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/video_frame.rs @@ -0,0 +1,956 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::slice; + +use cxx::UniquePtr; +use webrtc_sys::{video_frame as vf_sys, video_frame_buffer as vfb_sys}; + +use super::yuv_helper; +use crate::video_frame::{self as vf, VideoFormatType, VideoRotation}; + +/// We don't use vf::VideoFrameBuffer trait for the types inside this module to avoid confusion +/// because directly using platform specific types is not valid (e.g user callback) +/// All the types inside this module are only used internally. For public types, see the top level +/// video_frame.rs + +pub fn new_video_frame_buffer( + mut sys_handle: UniquePtr, +) -> Box { + unsafe { + match sys_handle.buffer_type() { + vfb_sys::ffi::VideoFrameBufferType::Native => { + Box::new(vf::native::NativeBuffer { handle: NativeBuffer { sys_handle } }) + } + vfb_sys::ffi::VideoFrameBufferType::I420 => Box::new(vf::I420Buffer { + handle: I420Buffer { sys_handle: sys_handle.pin_mut().get_i420() }, + }), + vfb_sys::ffi::VideoFrameBufferType::I420A => Box::new(vf::I420ABuffer { + handle: I420ABuffer { sys_handle: sys_handle.pin_mut().get_i420a() }, + }), + vfb_sys::ffi::VideoFrameBufferType::I422 => Box::new(vf::I422Buffer { + handle: I422Buffer { sys_handle: sys_handle.pin_mut().get_i422() }, + }), + vfb_sys::ffi::VideoFrameBufferType::I444 => Box::new(vf::I444Buffer { + handle: I444Buffer { sys_handle: sys_handle.pin_mut().get_i444() }, + }), + vfb_sys::ffi::VideoFrameBufferType::I010 => Box::new(vf::I010Buffer { + handle: I010Buffer { sys_handle: sys_handle.pin_mut().get_i010() }, + }), + vfb_sys::ffi::VideoFrameBufferType::NV12 => Box::new(vf::NV12Buffer { + handle: NV12Buffer { sys_handle: sys_handle.pin_mut().get_nv12() }, + }), + _ => unreachable!(), + } + } +} + +impl From for VideoRotation { + fn from(rotation: vf_sys::ffi::VideoRotation) -> Self { + match rotation { + vf_sys::ffi::VideoRotation::VideoRotation0 => Self::VideoRotation0, + vf_sys::ffi::VideoRotation::VideoRotation90 => Self::VideoRotation90, + vf_sys::ffi::VideoRotation::VideoRotation180 => Self::VideoRotation180, + vf_sys::ffi::VideoRotation::VideoRotation270 => Self::VideoRotation270, + _ => panic!("invalid VideoRotation"), + } + } +} + +impl From for vf_sys::ffi::VideoRotation { + fn from(rotation: VideoRotation) -> Self { + match rotation { + VideoRotation::VideoRotation0 => Self::VideoRotation0, + VideoRotation::VideoRotation90 => Self::VideoRotation90, + VideoRotation::VideoRotation180 => Self::VideoRotation180, + VideoRotation::VideoRotation270 => Self::VideoRotation270, + } + } +} + +macro_rules! recursive_cast { + ($ptr:expr $(, $fnc:ident)*) => { + { + let ptr = $ptr; + $( + let ptr = vfb_sys::ffi::$fnc(ptr); + )* + ptr + } + }; +} + +pub struct NativeBuffer { + sys_handle: UniquePtr, +} + +pub struct I420Buffer { + sys_handle: UniquePtr, +} + +pub struct I420ABuffer { + sys_handle: UniquePtr, +} + +pub struct I422Buffer { + sys_handle: UniquePtr, +} + +pub struct I444Buffer { + sys_handle: UniquePtr, +} + +pub struct I010Buffer { + sys_handle: UniquePtr, +} + +pub struct NV12Buffer { + sys_handle: UniquePtr, +} + +macro_rules! impl_to_argb { + (I420Buffer [$($variant:ident: $fnc:ident),+], $format:ident, $self:ident, $dst:ident, $dst_stride:ident, $dst_width:ident, $dst_height:ident) => { + match $format { + $( + VideoFormatType::$variant => { + let (data_y, data_u, data_v) = $self.data(); + yuv_helper::$fnc( + data_y, + $self.stride_y(), + data_u, + $self.stride_u(), + data_v, + $self.stride_v(), + $dst, + $dst_stride, + $dst_width, + $dst_height, + ) + } + )+ + } + }; + (I420ABuffer) => { + todo!(); + } +} + +#[allow(unused_unsafe)] +impl NativeBuffer { + pub fn from_fluxer_d3d11_texture( + handle: u64, + width: u32, + height: u32, + dxgi_format: u32, + ) -> Option { + let sys_handle = vfb_sys::ffi::new_fluxer_d3d11_texture_buffer( + handle, + width, + height, + dxgi_format, + ); + if sys_handle.is_null() { + return None; + } + Some(vf::native::NativeBuffer { + handle: NativeBuffer { sys_handle }, + }) + } + + #[allow(clippy::too_many_arguments)] + pub fn from_fluxer_dmabuf_texture( + fds: [i32; 4], + plane_count: u32, + width: u32, + height: u32, + drm_format: u32, + modifier: u64, + strides: [u32; 4], + offsets: [u32; 4], + device_uuid_hi: u64, + device_uuid_lo: u64, + ) -> Option { + let sys_handle = vfb_sys::ffi::new_fluxer_dmabuf_texture_buffer( + fds[0], + fds[1], + fds[2], + fds[3], + plane_count, + width, + height, + drm_format, + modifier, + strides[0], + strides[1], + strides[2], + strides[3], + offsets[0], + offsets[1], + offsets[2], + offsets[3], + device_uuid_hi, + device_uuid_lo, + ); + if sys_handle.is_null() { + return None; + } + Some(vf::native::NativeBuffer { + handle: NativeBuffer { sys_handle }, + }) + } + + #[cfg(any(target_os = "macos", target_os = "ios"))] + pub unsafe fn from_cv_pixel_buffer( + cv_pixel_buffer: *mut std::ffi::c_void, + ) -> vf::native::NativeBuffer { + vf::native::NativeBuffer { + handle: NativeBuffer { + sys_handle: vfb_sys::ffi::new_native_buffer_from_platform_image_buffer( + cv_pixel_buffer as *mut _, + ), + }, + } + } + + #[cfg(any(target_os = "macos", target_os = "ios"))] + pub fn get_cv_pixel_buffer(&self) -> *mut std::ffi::c_void { + unsafe { vfb_sys::ffi::native_buffer_to_platform_image_buffer(&self.sys_handle) as *mut _ } + } + + pub fn sys_handle(&self) -> &vfb_sys::ffi::VideoFrameBuffer { + &self.sys_handle + } + + pub fn width(&self) -> u32 { + self.sys_handle.width() + } + + pub fn height(&self) -> u32 { + self.sys_handle.height() + } + + pub fn to_i420(&self) -> I420Buffer { + I420Buffer { sys_handle: unsafe { self.sys_handle.to_i420() } } + } + + pub fn to_argb( + &self, + format: VideoFormatType, + dst: &mut [u8], + dst_stride: u32, + dst_width: i32, + dst_height: i32, + ) { + self.to_i420().to_argb(format, dst, dst_stride, dst_width, dst_height) + } +} + +impl I420Buffer { + pub fn new( + width: u32, + height: u32, + stride_y: u32, + stride_u: u32, + stride_v: u32, + ) -> vf::I420Buffer { + vf::I420Buffer { + handle: I420Buffer { + sys_handle: vfb_sys::ffi::new_i420_buffer( + width.try_into().unwrap(), + height.try_into().unwrap(), + stride_y.try_into().unwrap(), + stride_u.try_into().unwrap(), + stride_v.try_into().unwrap(), + ), + }, + } + } + + pub fn sys_handle(&self) -> &vfb_sys::ffi::VideoFrameBuffer { + unsafe { &*recursive_cast!(&*self.sys_handle, i420_to_yuv8, yuv8_to_yuv, yuv_to_vfb) } + } + + pub fn width(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i420_to_yuv8, yuv8_to_yuv, yuv_to_vfb); + (*ptr).width() + } + } + + pub fn height(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i420_to_yuv8, yuv8_to_yuv, yuv_to_vfb); + (*ptr).height() + } + } + + pub fn chroma_width(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i420_to_yuv8, yuv8_to_yuv); + (*ptr).chroma_width() + } + } + + pub fn chroma_height(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i420_to_yuv8, yuv8_to_yuv); + (*ptr).chroma_height() + } + } + + pub fn stride_y(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i420_to_yuv8, yuv8_to_yuv); + (*ptr).stride_y() + } + } + + pub fn stride_u(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i420_to_yuv8, yuv8_to_yuv); + (*ptr).stride_u() + } + } + + pub fn stride_v(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i420_to_yuv8, yuv8_to_yuv); + (*ptr).stride_v() + } + } + + pub fn to_i420(&self) -> I420Buffer { + I420Buffer { + sys_handle: unsafe { + // We make a copy of the buffer because internally, when calling ToI420() + // if the buffer is of type I420, libwebrtc will reuse the same underlying pointer + // for the new created type + let copy = vfb_sys::ffi::copy_i420_buffer(&self.sys_handle); + let ptr = recursive_cast!(&*copy, i420_to_yuv8, yuv8_to_yuv, yuv_to_vfb); + (*ptr).to_i420() + }, + } + } + + pub fn to_argb( + &self, + format: VideoFormatType, + dst: &mut [u8], + dst_stride: u32, + dst_width: i32, + dst_height: i32, + ) { + impl_to_argb!( + I420Buffer + [ + ARGB: i420_to_argb, + BGRA: i420_to_bgra, + ABGR: i420_to_abgr, + RGBA: i420_to_rgba + ], + format, self, dst, dst_stride, dst_width, dst_height + ) + } + + pub fn data(&self) -> (&[u8], &[u8], &[u8]) { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i420_to_yuv8); + let chroma_height = (self.height() + 1) / 2; + ( + slice::from_raw_parts((*ptr).data_y(), (self.stride_y() * self.height()) as usize), + slice::from_raw_parts((*ptr).data_u(), (self.stride_u() * chroma_height) as usize), + slice::from_raw_parts((*ptr).data_v(), (self.stride_v() * chroma_height) as usize), + ) + } + } + + pub fn scale(&mut self, scaled_width: i32, scaled_height: i32) -> vf::I420Buffer { + vf::I420Buffer { + handle: I420Buffer { + sys_handle: self.sys_handle.pin_mut().scale(scaled_width, scaled_height), + }, + } + } +} + +impl I420ABuffer { + pub fn sys_handle(&self) -> &vfb_sys::ffi::VideoFrameBuffer { + unsafe { &*recursive_cast!(&*self.sys_handle, i420a_to_yuv8, yuv8_to_yuv, yuv_to_vfb) } + } + + pub fn width(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i420a_to_yuv8, yuv8_to_yuv, yuv_to_vfb); + (*ptr).width() + } + } + + pub fn height(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i420a_to_yuv8, yuv8_to_yuv, yuv_to_vfb); + (*ptr).height() + } + } + + pub fn chroma_width(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i420a_to_yuv8, yuv8_to_yuv); + (*ptr).chroma_width() + } + } + + pub fn chroma_height(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i420a_to_yuv8, yuv8_to_yuv); + (*ptr).chroma_height() + } + } + + pub fn stride_y(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i420a_to_yuv8, yuv8_to_yuv); + (*ptr).stride_y() + } + } + + pub fn stride_u(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i420a_to_yuv8, yuv8_to_yuv); + (*ptr).stride_u() + } + } + + pub fn stride_v(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i420a_to_yuv8, yuv8_to_yuv); + (*ptr).stride_v() + } + } + + pub fn stride_a(&self) -> u32 { + self.sys_handle.stride_a() + } + + pub fn to_i420(&self) -> I420Buffer { + I420Buffer { + sys_handle: unsafe { + let ptr = + recursive_cast!(&*self.sys_handle, i420a_to_yuv8, yuv8_to_yuv, yuv_to_vfb); + (*ptr).to_i420() + }, + } + } + + pub fn to_argb( + &self, + format: VideoFormatType, + dst: &mut [u8], + dst_stride: u32, + dst_width: i32, + dst_height: i32, + ) { + self.to_i420().to_argb(format, dst, dst_stride, dst_width, dst_height) + } + + pub fn data(&self) -> (&[u8], &[u8], &[u8], Option<&[u8]>) { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i420a_to_yuv8); + let chroma_height = (self.height() + 1) / 2; + let data_a = self.sys_handle.data_a(); + let has_data_a = !data_a.is_null(); + ( + slice::from_raw_parts((*ptr).data_y(), (self.stride_y() * self.height()) as usize), + slice::from_raw_parts((*ptr).data_u(), (self.stride_u() * chroma_height) as usize), + slice::from_raw_parts((*ptr).data_v(), (self.stride_v() * chroma_height) as usize), + has_data_a.then_some(slice::from_raw_parts( + data_a, + (self.stride_a() * self.height()) as usize, + )), + ) + } + } + + pub fn scale(&mut self, scaled_width: i32, scaled_height: i32) -> vf::I420ABuffer { + vf::I420ABuffer { + handle: I420ABuffer { + sys_handle: self.sys_handle.pin_mut().scale(scaled_width, scaled_height), + }, + } + } +} + +impl I422Buffer { + pub fn new( + width: u32, + height: u32, + stride_y: u32, + stride_u: u32, + stride_v: u32, + ) -> vf::I422Buffer { + vf::I422Buffer { + handle: I422Buffer { + sys_handle: vfb_sys::ffi::new_i422_buffer( + width.try_into().unwrap(), + height.try_into().unwrap(), + stride_y.try_into().unwrap(), + stride_u.try_into().unwrap(), + stride_v.try_into().unwrap(), + ), + }, + } + } + + pub fn sys_handle(&self) -> &vfb_sys::ffi::VideoFrameBuffer { + unsafe { &*recursive_cast!(&*self.sys_handle, i422_to_yuv8, yuv8_to_yuv, yuv_to_vfb) } + } + + pub fn width(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i422_to_yuv8, yuv8_to_yuv, yuv_to_vfb); + (*ptr).width() + } + } + + pub fn height(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i422_to_yuv8, yuv8_to_yuv, yuv_to_vfb); + (*ptr).height() + } + } + + pub fn chroma_width(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i422_to_yuv8, yuv8_to_yuv); + (*ptr).chroma_width() + } + } + + pub fn chroma_height(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i422_to_yuv8, yuv8_to_yuv); + (*ptr).chroma_height() + } + } + + pub fn stride_y(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i422_to_yuv8, yuv8_to_yuv); + (*ptr).stride_y() + } + } + + pub fn stride_u(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i422_to_yuv8, yuv8_to_yuv); + (*ptr).stride_u() + } + } + + pub fn stride_v(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i422_to_yuv8, yuv8_to_yuv); + (*ptr).stride_v() + } + } + + pub fn to_i420(&self) -> I420Buffer { + I420Buffer { + sys_handle: unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i422_to_yuv8, yuv8_to_yuv, yuv_to_vfb); + (*ptr).to_i420() + }, + } + } + + pub fn to_argb( + &self, + format: VideoFormatType, + dst: &mut [u8], + dst_stride: u32, + dst_width: i32, + dst_height: i32, + ) { + self.to_i420().to_argb(format, dst, dst_stride, dst_width, dst_height) + } + + pub fn data(&self) -> (&[u8], &[u8], &[u8]) { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i422_to_yuv8); + ( + slice::from_raw_parts((*ptr).data_y(), (self.stride_y() * self.height()) as usize), + slice::from_raw_parts((*ptr).data_u(), (self.stride_u() * self.height()) as usize), + slice::from_raw_parts((*ptr).data_v(), (self.stride_v() * self.height()) as usize), + ) + } + } + + pub fn scale(&mut self, scaled_width: i32, scaled_height: i32) -> vf::I422Buffer { + vf::I422Buffer { + handle: I422Buffer { + sys_handle: self.sys_handle.pin_mut().scale(scaled_width, scaled_height), + }, + } + } +} +impl I444Buffer { + pub fn new( + width: u32, + height: u32, + stride_y: u32, + stride_u: u32, + stride_v: u32, + ) -> vf::I444Buffer { + vf::I444Buffer { + handle: I444Buffer { + sys_handle: vfb_sys::ffi::new_i444_buffer( + width.try_into().unwrap(), + height.try_into().unwrap(), + stride_y.try_into().unwrap(), + stride_u.try_into().unwrap(), + stride_v.try_into().unwrap(), + ), + }, + } + } + + pub fn sys_handle(&self) -> &vfb_sys::ffi::VideoFrameBuffer { + unsafe { &*recursive_cast!(&*self.sys_handle, i444_to_yuv8, yuv8_to_yuv, yuv_to_vfb) } + } + + pub fn width(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i444_to_yuv8, yuv8_to_yuv, yuv_to_vfb); + (*ptr).width() + } + } + + pub fn height(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i444_to_yuv8, yuv8_to_yuv, yuv_to_vfb); + (*ptr).height() + } + } + + pub fn chroma_width(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i444_to_yuv8, yuv8_to_yuv); + (*ptr).chroma_width() + } + } + + pub fn chroma_height(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i444_to_yuv8, yuv8_to_yuv); + (*ptr).chroma_height() + } + } + + pub fn stride_y(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i444_to_yuv8, yuv8_to_yuv); + (*ptr).stride_y() + } + } + + pub fn stride_u(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i444_to_yuv8, yuv8_to_yuv); + (*ptr).stride_u() + } + } + + pub fn stride_v(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i444_to_yuv8, yuv8_to_yuv); + (*ptr).stride_v() + } + } + + pub fn to_i420(&self) -> I420Buffer { + I420Buffer { + sys_handle: unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i444_to_yuv8, yuv8_to_yuv, yuv_to_vfb); + (*ptr).to_i420() + }, + } + } + + pub fn to_argb( + &self, + format: VideoFormatType, + dst: &mut [u8], + dst_stride: u32, + dst_width: i32, + dst_height: i32, + ) { + self.to_i420().to_argb(format, dst, dst_stride, dst_width, dst_height) + } + + pub fn data(&self) -> (&[u8], &[u8], &[u8]) { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i444_to_yuv8); + ( + slice::from_raw_parts((*ptr).data_y(), (self.stride_y() * self.height()) as usize), + slice::from_raw_parts((*ptr).data_u(), (self.stride_u() * self.height()) as usize), + slice::from_raw_parts((*ptr).data_v(), (self.stride_v() * self.height()) as usize), + ) + } + } + + pub fn scale(&mut self, scaled_width: i32, scaled_height: i32) -> vf::I444Buffer { + vf::I444Buffer { + handle: I444Buffer { + sys_handle: self.sys_handle.pin_mut().scale(scaled_width, scaled_height), + }, + } + } +} + +impl I010Buffer { + pub fn new( + width: u32, + height: u32, + stride_y: u32, + stride_u: u32, + stride_v: u32, + ) -> vf::I010Buffer { + vf::I010Buffer { + handle: I010Buffer { + sys_handle: vfb_sys::ffi::new_i010_buffer( + width.try_into().unwrap(), + height.try_into().unwrap(), + stride_y.try_into().unwrap(), + stride_u.try_into().unwrap(), + stride_v.try_into().unwrap(), + ), + }, + } + } + + pub fn sys_handle(&self) -> &vfb_sys::ffi::VideoFrameBuffer { + unsafe { &*recursive_cast!(&*self.sys_handle, i010_to_yuv16b, yuv16b_to_yuv, yuv_to_vfb) } + } + + pub fn width(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i010_to_yuv16b, yuv16b_to_yuv, yuv_to_vfb); + (*ptr).width() + } + } + + pub fn height(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i010_to_yuv16b, yuv16b_to_yuv, yuv_to_vfb); + (*ptr).height() + } + } + + pub fn chroma_width(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i010_to_yuv16b, yuv16b_to_yuv); + (*ptr).chroma_width() + } + } + + pub fn chroma_height(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i010_to_yuv16b, yuv16b_to_yuv); + (*ptr).chroma_height() + } + } + + pub fn stride_y(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i010_to_yuv16b, yuv16b_to_yuv); + (*ptr).stride_y() + } + } + + pub fn stride_u(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i010_to_yuv16b, yuv16b_to_yuv); + (*ptr).stride_u() + } + } + + pub fn stride_v(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i010_to_yuv16b, yuv16b_to_yuv); + (*ptr).stride_v() + } + } + + pub fn to_i420(&self) -> I420Buffer { + I420Buffer { + sys_handle: unsafe { + let ptr = + recursive_cast!(&*self.sys_handle, i010_to_yuv16b, yuv16b_to_yuv, yuv_to_vfb); + (*ptr).to_i420() + }, + } + } + + pub fn to_argb( + &self, + format: VideoFormatType, + dst: &mut [u8], + dst_stride: u32, + dst_width: i32, + dst_height: i32, + ) { + self.to_i420().to_argb(format, dst, dst_stride, dst_width, dst_height) + } + + pub fn data(&self) -> (&[u16], &[u16], &[u16]) { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, i010_to_yuv16b); + let chroma_height = (self.height() + 1) / 2; + ( + slice::from_raw_parts( + (*ptr).data_y(), + (self.stride_y() * self.height()) as usize / 2, + ), + slice::from_raw_parts( + (*ptr).data_u(), + (self.stride_u() * chroma_height) as usize / 2, + ), + slice::from_raw_parts( + (*ptr).data_v(), + (self.stride_v() * chroma_height) as usize / 2, + ), + ) + } + } + + pub fn scale(&mut self, scaled_width: i32, scaled_height: i32) -> vf::I010Buffer { + vf::I010Buffer { + handle: I010Buffer { + sys_handle: self.sys_handle.pin_mut().scale(scaled_width, scaled_height), + }, + } + } +} + +impl NV12Buffer { + pub fn new(width: u32, height: u32, stride_y: u32, stride_uv: u32) -> vf::NV12Buffer { + vf::NV12Buffer { + handle: NV12Buffer { + sys_handle: vfb_sys::ffi::new_nv12_buffer( + width.try_into().unwrap(), + height.try_into().unwrap(), + stride_y.try_into().unwrap(), + stride_uv.try_into().unwrap(), + ), + }, + } + } + + pub fn sys_handle(&self) -> &vfb_sys::ffi::VideoFrameBuffer { + unsafe { + &*recursive_cast!(&*self.sys_handle, nv12_to_biyuv8, biyuv8_to_biyuv, biyuv_to_vfb) + } + } + + pub fn width(&self) -> u32 { + unsafe { + let ptr = + recursive_cast!(&*self.sys_handle, nv12_to_biyuv8, biyuv8_to_biyuv, biyuv_to_vfb); + (*ptr).width() + } + } + + pub fn height(&self) -> u32 { + unsafe { + let ptr = + recursive_cast!(&*self.sys_handle, nv12_to_biyuv8, biyuv8_to_biyuv, biyuv_to_vfb); + (*ptr).height() + } + } + + pub fn chroma_width(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, nv12_to_biyuv8, biyuv8_to_biyuv); + (*ptr).chroma_width() + } + } + + pub fn chroma_height(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, nv12_to_biyuv8, biyuv8_to_biyuv); + (*ptr).chroma_height() + } + } + + pub fn stride_y(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, nv12_to_biyuv8, biyuv8_to_biyuv); + (*ptr).stride_y() + } + } + + pub fn stride_uv(&self) -> u32 { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, nv12_to_biyuv8, biyuv8_to_biyuv); + (*ptr).stride_uv() + } + } + + pub fn to_i420(&self) -> I420Buffer { + I420Buffer { + sys_handle: unsafe { + let ptr = recursive_cast!( + &*self.sys_handle, + nv12_to_biyuv8, + biyuv8_to_biyuv, + biyuv_to_vfb + ); + (*ptr).to_i420() + }, + } + } + + pub fn to_argb( + &self, + format: VideoFormatType, + dst: &mut [u8], + dst_stride: u32, + dst_width: i32, + dst_height: i32, + ) { + self.to_i420().to_argb(format, dst, dst_stride, dst_width, dst_height) + } + + pub fn data(&self) -> (&[u8], &[u8]) { + unsafe { + let ptr = recursive_cast!(&*self.sys_handle, nv12_to_biyuv8); + let chroma_height = (self.height() + 1) / 2; + + ( + slice::from_raw_parts((*ptr).data_y(), (self.stride_y() * self.height()) as usize), + slice::from_raw_parts( + (*ptr).data_uv(), + (self.stride_uv() * chroma_height) as usize, + ), + ) + } + } + + pub fn scale(&mut self, scaled_width: i32, scaled_height: i32) -> vf::NV12Buffer { + vf::NV12Buffer { + handle: NV12Buffer { + sys_handle: self.sys_handle.pin_mut().scale(scaled_width, scaled_height), + }, + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/video_source.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/video_source.rs new file mode 100644 index 0000000..9c7781e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/video_source.rs @@ -0,0 +1,147 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{ + sync::Arc, + time::{Duration, SystemTime, UNIX_EPOCH}, +}; + +use cxx::SharedPtr; +use livekit_runtime::interval; +use parking_lot::Mutex; +use webrtc_sys::{video_frame as vf_sys, video_frame::ffi::VideoRotation, video_track as vt_sys}; + +use crate::{ + native::packet_trailer::PacketTrailerHandler, + video_frame::{I420Buffer, VideoBuffer, VideoFrame}, + video_source::VideoResolution, +}; + +impl From for VideoResolution { + fn from(res: vt_sys::ffi::VideoResolution) -> Self { + Self { width: res.width, height: res.height } + } +} + +impl From for vt_sys::ffi::VideoResolution { + fn from(res: VideoResolution) -> Self { + Self { width: res.width, height: res.height } + } +} + +#[derive(Clone)] +pub struct NativeVideoSource { + sys_handle: SharedPtr, + inner: Arc>, +} + +struct VideoSourceInner { + captured_frames: usize, +} + +impl NativeVideoSource { + pub fn new(resolution: VideoResolution, is_screencast: bool) -> NativeVideoSource { + let source = Self { + sys_handle: vt_sys::ffi::new_video_track_source( + &vt_sys::ffi::VideoResolution::from(resolution.clone()), + is_screencast, + ), + inner: Arc::new(Mutex::new(VideoSourceInner { captured_frames: 0 })), + }; + + livekit_runtime::spawn({ + let source = source.clone(); + let i420 = I420Buffer::new(resolution.width, resolution.height); + async move { + let mut interval = interval(Duration::from_millis(100)); // 10 fps + + loop { + interval.tick().await; + + let inner = source.inner.lock(); + if inner.captured_frames > 0 { + break; + } + + let mut builder = vf_sys::ffi::new_video_frame_builder(); + builder.pin_mut().set_rotation(VideoRotation::VideoRotation0); + builder.pin_mut().set_video_frame_buffer(i420.as_ref().sys_handle()); + + let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + builder.pin_mut().set_timestamp_us(now.as_micros() as i64); + + source.sys_handle.on_captured_frame( + &builder.pin_mut().build(), + &vt_sys::ffi::FrameMetadata { + has_packet_trailer: false, + user_timestamp: 0, + frame_id: 0, + }, + ); + } + } + }); + + source + } + + pub fn sys_handle(&self) -> SharedPtr { + self.sys_handle.clone() + } + + pub fn capture_frame>(&self, frame: &VideoFrame) { + let mut builder = vf_sys::ffi::new_video_frame_builder(); + builder.pin_mut().set_rotation(frame.rotation.into()); + builder.pin_mut().set_video_frame_buffer(frame.buffer.as_ref().sys_handle()); + + let capture_ts = if frame.timestamp_us == 0 { + let now = SystemTime::now().duration_since(UNIX_EPOCH).unwrap(); + now.as_micros() as i64 + } else { + frame.timestamp_us + }; + builder.pin_mut().set_timestamp_us(capture_ts); + + let (has_trailer, user_ts, fid) = match frame.frame_metadata { + Some(meta) => (true, meta.user_timestamp.unwrap_or(0), meta.frame_id.unwrap_or(0)), + None => (false, 0, 0), + }; + + self.inner.lock().captured_frames += 1; + + self.sys_handle.on_captured_frame( + &builder.pin_mut().build(), + &vt_sys::ffi::FrameMetadata { + has_packet_trailer: has_trailer, + user_timestamp: user_ts, + frame_id: fid, + }, + ); + } + + /// Set the packet trailer handler used by this source. + /// + /// When set, any frame captured with a `user_timestamp` value will + /// automatically have its timestamp stored in the handler so the + /// `PacketTrailerTransformer` can embed it into the encoded frame. + /// The handler is set on the C++ VideoTrackSource so it has access to + /// the TimestampAligner-adjusted capture timestamp for correct keying. + pub fn set_packet_trailer_handler(&self, handler: PacketTrailerHandler) { + self.sys_handle.set_packet_trailer_handler(handler.sys_handle()); + } + + pub fn video_resolution(&self) -> VideoResolution { + self.sys_handle.video_resolution().into() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/video_stream.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/video_stream.rs new file mode 100644 index 0000000..cebc11b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/video_stream.rs @@ -0,0 +1,350 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{ + collections::VecDeque, + pin::Pin, + sync::{ + atomic::{AtomicBool, AtomicU64, Ordering}, + Arc, + }, + task::{Context, Poll, Waker}, +}; + +use cxx::{SharedPtr, UniquePtr}; +use livekit_runtime::Stream; +use parking_lot::Mutex; +use rtrb::{Consumer, Producer, PushError, RingBuffer}; +use webrtc_sys::video_track as sys_vt; + +use super::{packet_trailer::SubscribeTimingStage, video_frame::new_video_frame_buffer}; +use crate::{ + native::packet_trailer::PacketTrailerHandler, + video_frame::{BoxVideoFrame, FrameMetadata, VideoFrame}, + video_track::RtcVideoTrack, +}; + +pub struct NativeVideoStream { + native_sink: SharedPtr, + observer: Arc, + video_track: RtcVideoTrack, + frame_queue: Arc, +} + +impl NativeVideoStream { + pub fn new(video_track: RtcVideoTrack, queue_size_frames: Option) -> Self { + let frame_queue = Arc::new(VideoFrameQueue::new(queue_size_frames)); + // Auto-wire the packet trailer handler from the track if one is set. + let handler = video_track.handle.packet_trailer_handler(); + let observer = Arc::new(VideoTrackObserver { + frame_queue: frame_queue.clone(), + packet_trailer_handler: parking_lot::Mutex::new(handler), + }); + let native_sink = sys_vt::ffi::new_native_video_sink(Box::new( + sys_vt::VideoSinkWrapper::new(observer.clone()), + )); + + let video = unsafe { sys_vt::ffi::media_to_video(video_track.sys_handle()) }; + video.add_sink(&native_sink); + + Self { native_sink, observer, video_track, frame_queue } + } + + /// Set the packet trailer handler for this stream. + /// + /// When set, each frame produced by this stream will have its + /// `user_timestamp` field populated from the handler's receive + /// map (looked up by RTP timestamp). + /// + /// Note: If the handler was already set on the `RtcVideoTrack` before + /// creating this stream, it is automatically wired up. This method is + /// only needed if you want to override or set the handler after + /// construction. + pub fn set_packet_trailer_handler(&self, handler: PacketTrailerHandler) { + *self.observer.packet_trailer_handler.lock() = Some(handler); + } + + pub fn track(&self) -> RtcVideoTrack { + self.video_track.clone() + } + + pub fn close(&mut self) { + let video = unsafe { sys_vt::ffi::media_to_video(self.video_track.sys_handle()) }; + video.remove_sink(&self.native_sink); + self.frame_queue.close(); + } +} + +impl Drop for NativeVideoStream { + fn drop(&mut self) { + self.close(); + } +} + +impl Stream for NativeVideoStream { + type Item = BoxVideoFrame; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + self.frame_queue.poll_recv(cx) + } +} + +struct VideoTrackObserver { + frame_queue: Arc, + packet_trailer_handler: parking_lot::Mutex>, +} + +impl sys_vt::VideoSink for VideoTrackObserver { + fn on_frame(&self, frame: UniquePtr) { + let rtp_timestamp = frame.timestamp(); + let packet_trailer_handler = self.packet_trailer_handler.lock().clone(); + let frame_metadata = packet_trailer_handler + .as_ref() + .and_then(|handler| { + handler.lookup_frame_metadata(rtp_timestamp).map(|(ts, fid)| { + handler.emit_subscribe_timing(SubscribeTimingStage::DecoderOutput, ts, fid); + (ts, fid) + }) + }) + .map(|(ts, fid)| FrameMetadata { + user_timestamp: Some(ts), + frame_id: if fid != 0 { Some(fid) } else { None }, + }); + + self.frame_queue.push(VideoFrame { + rotation: frame.rotation().into(), + timestamp_us: frame.timestamp_us(), + frame_metadata, + buffer: new_video_frame_buffer(unsafe { frame.video_frame_buffer() }), + }); + } + + fn on_discarded_frame(&self) {} + + fn on_constraints_changed(&self, _constraints: sys_vt::ffi::VideoTrackSourceConstraints) {} +} + +struct VideoFrameQueue { + kind: VideoFrameQueueKind, + closed: AtomicBool, + dropped_frames: AtomicU64, + waker: Mutex>, +} + +enum VideoFrameQueueKind { + Bounded(BoundedVideoFrameQueue), + Unbounded(UnboundedVideoFrameQueue), +} + +struct BoundedVideoFrameQueue { + producer: Mutex>, + consumer: Mutex>, +} + +struct UnboundedVideoFrameQueue { + frames: Mutex>, +} + +impl VideoFrameQueue { + fn new(capacity: Option) -> Self { + let kind = match capacity.filter(|capacity| *capacity > 0) { + Some(capacity) => { + let (producer, consumer) = RingBuffer::new(capacity); + VideoFrameQueueKind::Bounded(BoundedVideoFrameQueue { + producer: Mutex::new(producer), + consumer: Mutex::new(consumer), + }) + } + None => VideoFrameQueueKind::Unbounded(UnboundedVideoFrameQueue { + frames: Mutex::new(VecDeque::new()), + }), + }; + + Self { + kind, + closed: AtomicBool::new(false), + dropped_frames: AtomicU64::new(0), + waker: Mutex::new(None), + } + } + + fn push(&self, frame: BoxVideoFrame) { + if self.closed.load(Ordering::Acquire) { + return; + } + + match &self.kind { + VideoFrameQueueKind::Bounded(queue) => self.push_bounded(queue, frame), + VideoFrameQueueKind::Unbounded(queue) => { + queue.frames.lock().push_back(frame); + } + } + + self.wake_receiver(); + } + + fn push_bounded(&self, queue: &BoundedVideoFrameQueue, mut frame: BoxVideoFrame) { + loop { + let push_result = queue.producer.lock().push(frame); + match push_result { + Ok(()) => return, + Err(PushError::Full(returned_frame)) => { + frame = returned_frame; + + let dropped = queue.consumer.lock().pop().is_ok(); + if dropped { + self.record_drop(); + } else { + return; + } + } + } + } + } + + fn close(&self) { + self.closed.store(true, Ordering::Release); + self.wake_receiver(); + + match &self.kind { + VideoFrameQueueKind::Bounded(queue) => { + let mut consumer = queue.consumer.lock(); + while consumer.pop().is_ok() {} + } + VideoFrameQueueKind::Unbounded(queue) => { + queue.frames.lock().clear(); + } + } + } + + fn poll_recv(&self, cx: &mut Context<'_>) -> Poll> { + if let Some(frame) = self.try_pop() { + return Poll::Ready(Some(frame)); + } + + if self.closed.load(Ordering::Acquire) { + return Poll::Ready(None); + } + + *self.waker.lock() = Some(cx.waker().clone()); + + if let Some(frame) = self.try_pop() { + self.waker.lock().take(); + Poll::Ready(Some(frame)) + } else if self.closed.load(Ordering::Acquire) { + Poll::Ready(None) + } else { + Poll::Pending + } + } + + fn try_pop(&self) -> Option { + match &self.kind { + VideoFrameQueueKind::Bounded(queue) => queue.consumer.lock().pop().ok(), + VideoFrameQueueKind::Unbounded(queue) => queue.frames.lock().pop_front(), + } + } + + fn wake_receiver(&self) { + let waker = self.waker.lock().take(); + if let Some(waker) = waker { + waker.wake(); + } + } + + fn record_drop(&self) { + let dropped_frames = self.dropped_frames.fetch_add(1, Ordering::Relaxed) + 1; + if dropped_frames == 1 || dropped_frames % 100 == 0 { + log::warn!( + "native video stream queue overflow; dropped {} queued frames", + dropped_frames + ); + } + } +} + +#[cfg(test)] +mod tests { + use std::sync::atomic::Ordering; + + use super::VideoFrameQueue; + use crate::video_frame::{BoxVideoFrame, I420Buffer, VideoFrame, VideoRotation}; + + fn test_frame(timestamp_us: i64) -> BoxVideoFrame { + VideoFrame { + rotation: VideoRotation::VideoRotation0, + timestamp_us, + frame_metadata: None, + buffer: Box::new(I420Buffer::new(2, 2)), + } + } + + fn pop_timestamp(queue: &VideoFrameQueue) -> Option { + queue.try_pop().map(|frame| frame.timestamp_us) + } + + #[test] + fn bounded_queue_preserves_fifo_order_under_capacity() { + let queue = VideoFrameQueue::new(Some(3)); + + queue.push(test_frame(1)); + queue.push(test_frame(2)); + queue.push(test_frame(3)); + + assert_eq!(pop_timestamp(&queue), Some(1)); + assert_eq!(pop_timestamp(&queue), Some(2)); + assert_eq!(pop_timestamp(&queue), Some(3)); + assert_eq!(pop_timestamp(&queue), None); + } + + #[test] + fn bounded_queue_drops_oldest_when_full() { + let queue = VideoFrameQueue::new(Some(2)); + + queue.push(test_frame(1)); + queue.push(test_frame(2)); + queue.push(test_frame(3)); + + assert_eq!(queue.dropped_frames.load(Ordering::Relaxed), 1); + assert_eq!(pop_timestamp(&queue), Some(2)); + assert_eq!(pop_timestamp(&queue), Some(3)); + assert_eq!(pop_timestamp(&queue), None); + } + + #[test] + fn unbounded_queue_retains_all_frames() { + let queue = VideoFrameQueue::new(None); + + for timestamp_us in 1..=4 { + queue.push(test_frame(timestamp_us)); + } + + for timestamp_us in 1..=4 { + assert_eq!(pop_timestamp(&queue), Some(timestamp_us)); + } + assert_eq!(pop_timestamp(&queue), None); + assert_eq!(queue.dropped_frames.load(Ordering::Relaxed), 0); + } + + #[test] + fn close_clears_buffer_and_rejects_future_pushes() { + let queue = VideoFrameQueue::new(Some(2)); + + queue.push(test_frame(1)); + queue.close(); + queue.push(test_frame(2)); + + assert_eq!(pop_timestamp(&queue), None); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/video_track.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/video_track.rs new file mode 100644 index 0000000..4fd12d3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/video_track.rs @@ -0,0 +1,56 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::sync::Arc; + +use cxx::SharedPtr; +use parking_lot::Mutex; +use sys_vt::ffi::video_to_media; +use webrtc_sys::video_track as sys_vt; + +use super::media_stream_track::impl_media_stream_track; +use super::packet_trailer::PacketTrailerHandler; +use crate::media_stream_track::RtcTrackState; + +#[derive(Clone)] +pub struct RtcVideoTrack { + pub(crate) sys_handle: SharedPtr, + packet_trailer_handler: Arc>>, +} + +impl RtcVideoTrack { + impl_media_stream_track!(video_to_media); + + pub(crate) fn new(sys_handle: SharedPtr) -> Self { + Self { sys_handle, packet_trailer_handler: Arc::new(Mutex::new(None)) } + } + + pub fn sys_handle(&self) -> SharedPtr { + video_to_media(self.sys_handle.clone()) + } + + /// Set the packet trailer handler for this track. + /// + /// When set, any `NativeVideoStream` created from this track will + /// automatically use this handler to populate `user_timestamp` + /// on each decoded frame. + pub fn set_packet_trailer_handler(&self, handler: PacketTrailerHandler) { + self.packet_trailer_handler.lock().replace(handler); + } + + /// Get the packet trailer handler, if one has been set. + pub fn packet_trailer_handler(&self) -> Option { + self.packet_trailer_handler.lock().clone() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/yuv_helper.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/yuv_helper.rs new file mode 100644 index 0000000..ed90d27 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/native/yuv_helper.rs @@ -0,0 +1,868 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#![allow(clippy::too_many_arguments)] + +use webrtc_sys::yuv_helper as yuv_sys; + +fn argb_assert_safety(src: &[u8], src_stride: u32, _width: i32, height: i32) { + let height_abs = height.unsigned_abs(); + let min = (src_stride * height_abs) as usize; + assert!(src.len() >= min, "src isn't large enough"); +} + +fn i420_assert_safety( + src_y: &[u8], + src_stride_y: u32, + src_u: &[u8], + src_stride_u: u32, + src_v: &[u8], + src_stride_v: u32, + _width: i32, + height: i32, +) { + let height_abs = height.unsigned_abs(); + let chroma_height = (height_abs + 1) / 2; + let min_y = (src_stride_y * height_abs) as usize; + let min_u = (src_stride_u * chroma_height) as usize; + let min_v = (src_stride_v * chroma_height) as usize; + + assert!(src_y.len() >= min_y, "src_y isn't large enough"); + assert!(src_u.len() >= min_u, "src_u isn't large enough"); + assert!(src_v.len() >= min_v, "src_v isn't large enough"); +} + +fn nv12_assert_safety( + src_y: &[u8], + src_stride_y: u32, + src_uv: &[u8], + src_stride_uv: u32, + _width: i32, + height: i32, +) { + let height_abs = height.unsigned_abs(); + let chroma_height = (height_abs + 1) / 2; + + let min_y = (src_stride_y * height_abs) as usize; + let min_uv = (src_stride_uv * chroma_height) as usize; + + assert!(src_y.len() >= min_y, "src_y isn't large enough"); + assert!(src_uv.len() >= min_uv, "src_uv isn't large enough"); +} + +fn i444_assert_safety( + src_y: &[u8], + src_stride_y: u32, + src_u: &[u8], + src_stride_u: u32, + src_v: &[u8], + src_stride_v: u32, + _width: i32, + height: i32, +) { + let height_abs = height.unsigned_abs(); + let min_y = (src_stride_y * height_abs) as usize; + let min_u = (src_stride_u * height_abs) as usize; + let min_v = (src_stride_v * height_abs) as usize; + + assert!(src_y.len() >= min_y, "src_y isn't large enough"); + assert!(src_u.len() >= min_u, "src_u isn't large enough"); + assert!(src_v.len() >= min_v, "src_v isn't large enough"); +} + +fn i422_assert_safety( + src_y: &[u8], + src_stride_y: u32, + src_u: &[u8], + src_stride_u: u32, + src_v: &[u8], + src_stride_v: u32, + _width: i32, + height: i32, +) { + let height_abs = height.unsigned_abs(); + let min_y = (src_stride_y * height_abs) as usize; + let min_u = (src_stride_u * height_abs) as usize; + let min_v = (src_stride_v * height_abs) as usize; + + assert!(src_y.len() >= min_y, "src_y isn't large enough"); + assert!(src_u.len() >= min_u, "src_u isn't large enough"); + assert!(src_v.len() >= min_v, "src_v isn't large enough"); +} + +fn i010_assert_safety( + src_y: &[u16], + src_stride_y: u32, + src_u: &[u16], + src_stride_u: u32, + src_v: &[u16], + src_stride_v: u32, + _width: i32, + height: i32, +) { + let height_abs: u32 = height.unsigned_abs(); + let chroma_height = height_abs / 2; + let min_y = (src_stride_y * height_abs) as usize / 2; + let min_u = (src_stride_u * chroma_height) as usize / 2; + let min_v = (src_stride_v * chroma_height) as usize / 2; + + assert!(src_y.len() >= min_y, "src_y isn't large enough"); + assert!(src_u.len() >= min_u, "src_u isn't large enough"); + assert!(src_v.len() >= min_v, "src_v isn't large enough"); +} + +macro_rules! i420_to_rgba { + ($x:ident) => { + pub fn $x( + src_y: &[u8], + src_stride_y: u32, + src_u: &[u8], + src_stride_u: u32, + src_v: &[u8], + src_stride_v: u32, + dst: &mut [u8], + dst_stride: u32, + width: i32, + height: i32, + ) { + i420_assert_safety( + src_y, + src_stride_y, + src_u, + src_stride_u, + src_v, + src_stride_v, + width, + height, + ); + argb_assert_safety(dst, dst_stride, width, height); + + unsafe { + yuv_sys::ffi::$x( + src_y.as_ptr(), + src_stride_y as i32, + src_u.as_ptr(), + src_stride_u as i32, + src_v.as_ptr(), + src_stride_v as i32, + dst.as_mut_ptr(), + dst_stride as i32, + width, + height, + ) + .unwrap(); + } + } + }; +} + +macro_rules! rgba_to_i420 { + ($x:ident) => { + pub fn $x( + src_argb: &[u8], + src_stride_argb: u32, + dst_y: &mut [u8], + dst_stride_y: u32, + dst_u: &mut [u8], + dst_stride_u: u32, + dst_v: &mut [u8], + dst_stride_v: u32, + width: i32, + height: i32, + ) { + i420_assert_safety( + dst_y, + dst_stride_y, + dst_u, + dst_stride_u, + dst_v, + dst_stride_v, + width, + height, + ); + argb_assert_safety(src_argb, src_stride_argb, width, height); + + unsafe { + yuv_sys::ffi::$x( + src_argb.as_ptr(), + src_stride_argb as i32, + dst_y.as_mut_ptr(), + dst_stride_y as i32, + dst_u.as_mut_ptr(), + dst_stride_u as i32, + dst_v.as_mut_ptr(), + dst_stride_v as i32, + width, + height, + ) + .unwrap(); + } + } + }; +} + +pub fn argb_to_rgb24( + src_argb: &[u8], + src_stride_argb: u32, + dst_rgb24: &mut [u8], + dst_stride_rgb24: u32, + width: i32, + height: i32, +) { + argb_assert_safety(src_argb, src_stride_argb, width, height); + argb_assert_safety(dst_rgb24, dst_stride_rgb24, width, height); + + unsafe { + yuv_sys::ffi::argb_to_rgb24( + src_argb.as_ptr(), + src_stride_argb as i32, + dst_rgb24.as_mut_ptr(), + dst_stride_rgb24 as i32, + width, + height, + ) + .unwrap(); + } +} + +// I420 <> RGB conversion +rgba_to_i420!(argb_to_i420); +rgba_to_i420!(abgr_to_i420); + +i420_to_rgba!(i420_to_argb); +i420_to_rgba!(i420_to_bgra); +i420_to_rgba!(i420_to_abgr); +i420_to_rgba!(i420_to_rgba); + +pub fn i420_to_nv12( + src_y: &[u8], + src_stride_y: u32, + src_u: &[u8], + src_stride_u: u32, + src_v: &[u8], + src_stride_v: u32, + dst_y: &mut [u8], + dst_stride_y: u32, + dst_uv: &mut [u8], + dst_stride_uv: u32, + width: i32, + height: i32, +) { + i420_assert_safety( + src_y, + src_stride_y, + src_u, + src_stride_u, + src_v, + src_stride_v, + width, + height, + ); + nv12_assert_safety(dst_y, dst_stride_y, dst_uv, dst_stride_uv, width, height); + + unsafe { + yuv_sys::ffi::i420_to_nv12( + src_y.as_ptr(), + src_stride_y as i32, + src_u.as_ptr(), + src_stride_u as i32, + src_v.as_ptr(), + src_stride_v as i32, + dst_y.as_mut_ptr(), + dst_stride_y as i32, + dst_uv.as_mut_ptr(), + dst_stride_uv as i32, + width, + height, + ) + .unwrap(); + } +} + +pub fn nv12_to_i420( + src_y: &[u8], + src_stride_y: u32, + src_uv: &[u8], + src_stride_uv: u32, + dst_y: &mut [u8], + dst_stride_y: u32, + dst_u: &mut [u8], + dst_stride_u: u32, + dst_v: &mut [u8], + dst_stride_v: u32, + width: i32, + height: i32, +) { + nv12_assert_safety(src_y, src_stride_y, src_uv, src_stride_uv, width, height); + i420_assert_safety( + dst_y, + dst_stride_y, + dst_u, + dst_stride_u, + dst_v, + dst_stride_v, + width, + height, + ); + + unsafe { + yuv_sys::ffi::nv12_to_i420( + src_y.as_ptr(), + src_stride_y as i32, + src_uv.as_ptr(), + src_stride_uv as i32, + dst_y.as_mut_ptr(), + dst_stride_y as i32, + dst_u.as_mut_ptr(), + dst_stride_u as i32, + dst_v.as_mut_ptr(), + dst_stride_v as i32, + width, + height, + ) + .unwrap(); + } +} + +pub fn i444_to_i420( + src_y: &[u8], + src_stride_y: u32, + src_u: &[u8], + src_stride_u: u32, + src_v: &[u8], + src_stride_v: u32, + dst_y: &mut [u8], + dst_stride_y: u32, + dst_u: &mut [u8], + dst_stride_u: u32, + dst_v: &mut [u8], + dst_stride_v: u32, + width: i32, + height: i32, +) { + i444_assert_safety( + src_y, + src_stride_y, + src_u, + src_stride_u, + src_v, + src_stride_v, + width, + height, + ); + i420_assert_safety( + dst_y, + dst_stride_y, + dst_u, + dst_stride_u, + dst_v, + dst_stride_v, + width, + height, + ); + + unsafe { + yuv_sys::ffi::i444_to_i420( + src_y.as_ptr(), + src_stride_y as i32, + src_u.as_ptr(), + src_stride_u as i32, + src_v.as_ptr(), + src_stride_v as i32, + dst_y.as_mut_ptr(), + dst_stride_y as i32, + dst_u.as_mut_ptr(), + dst_stride_u as i32, + dst_v.as_mut_ptr(), + dst_stride_v as i32, + width, + height, + ) + .unwrap(); + } +} + +pub fn i422_to_i420( + src_y: &[u8], + src_stride_y: u32, + src_u: &[u8], + src_stride_u: u32, + src_v: &[u8], + src_stride_v: u32, + dst_y: &mut [u8], + dst_stride_y: u32, + dst_u: &mut [u8], + dst_stride_u: u32, + dst_v: &mut [u8], + dst_stride_v: u32, + width: i32, + height: i32, +) { + i422_assert_safety( + src_y, + src_stride_y, + src_u, + src_stride_u, + src_v, + src_stride_v, + width, + height, + ); + i420_assert_safety( + dst_y, + dst_stride_y, + dst_u, + dst_stride_u, + dst_v, + dst_stride_v, + width, + height, + ); + + unsafe { + yuv_sys::ffi::i422_to_i420( + src_y.as_ptr(), + src_stride_y as i32, + src_u.as_ptr(), + src_stride_u as i32, + src_v.as_ptr(), + src_stride_v as i32, + dst_y.as_mut_ptr(), + dst_stride_y as i32, + dst_u.as_mut_ptr(), + dst_stride_u as i32, + dst_v.as_mut_ptr(), + dst_stride_v as i32, + width, + height, + ) + .unwrap() + } +} + +pub fn i010_to_i420( + src_y: &[u16], + src_stride_y: u32, + src_u: &[u16], + src_stride_u: u32, + src_v: &[u16], + src_stride_v: u32, + dst_y: &mut [u8], + dst_stride_y: u32, + dst_u: &mut [u8], + dst_stride_u: u32, + dst_v: &mut [u8], + dst_stride_v: u32, + width: i32, + height: i32, +) { + i010_assert_safety( + src_y, + src_stride_y, + src_u, + src_stride_u, + src_v, + src_stride_v, + width, + height, + ); + i420_assert_safety( + dst_y, + dst_stride_y, + dst_u, + dst_stride_u, + dst_v, + dst_stride_v, + width, + height, + ); + + unsafe { + yuv_sys::ffi::i010_to_i420( + src_y.as_ptr(), + src_stride_y as i32, + src_u.as_ptr(), + src_stride_u as i32, + src_v.as_ptr(), + src_stride_v as i32, + dst_y.as_mut_ptr(), + dst_stride_y as i32, + dst_u.as_mut_ptr(), + dst_stride_u as i32, + dst_v.as_mut_ptr(), + dst_stride_v as i32, + width, + height, + ) + .unwrap() + } +} + +pub fn nv12_to_argb( + src_y: &[u8], + src_stride_y: u32, + src_uv: &[u8], + src_stride_uv: u32, + dst_argb: &mut [u8], + dst_stride_argb: u32, + width: i32, + height: i32, +) { + nv12_assert_safety(src_y, src_stride_y, src_uv, src_stride_uv, width, height); + argb_assert_safety(dst_argb, dst_stride_argb, width, height); + + unsafe { + yuv_sys::ffi::nv12_to_argb( + src_y.as_ptr(), + src_stride_y as i32, + src_uv.as_ptr(), + src_stride_uv as i32, + dst_argb.as_mut_ptr(), + dst_stride_argb as i32, + width, + height, + ) + .unwrap(); + } +} + +pub fn nv12_to_abgr( + src_y: &[u8], + src_stride_y: u32, + src_uv: &[u8], + src_stride_uv: u32, + dst_abgr: &mut [u8], + dst_stride_abgr: u32, + width: i32, + height: i32, +) { + nv12_assert_safety(src_y, src_stride_y, src_uv, src_stride_uv, width, height); + argb_assert_safety(dst_abgr, dst_stride_abgr, width, height); + + unsafe { + yuv_sys::ffi::nv12_to_abgr( + src_y.as_ptr(), + src_stride_y as i32, + src_uv.as_ptr(), + src_stride_uv as i32, + dst_abgr.as_mut_ptr(), + dst_stride_abgr as i32, + width, + height, + ) + .unwrap(); + } +} + +pub fn i444_to_argb( + src_y: &[u8], + src_stride_y: u32, + src_u: &[u8], + src_stride_u: u32, + src_v: &[u8], + src_stride_v: u32, + dst_argb: &mut [u8], + dst_stride_argb: u32, + width: i32, + height: i32, +) { + i444_assert_safety( + src_y, + src_stride_y, + src_u, + src_stride_u, + src_v, + src_stride_v, + width, + height, + ); + argb_assert_safety(dst_argb, dst_stride_argb, width, height); + + unsafe { + yuv_sys::ffi::i444_to_argb( + src_y.as_ptr(), + src_stride_y as i32, + src_u.as_ptr(), + src_stride_u as i32, + src_v.as_ptr(), + src_stride_v as i32, + dst_argb.as_mut_ptr(), + dst_stride_argb as i32, + width, + height, + ) + .unwrap(); + } +} + +pub fn i444_to_abgr( + src_y: &[u8], + src_stride_y: u32, + src_u: &[u8], + src_stride_u: u32, + src_v: &[u8], + src_stride_v: u32, + dst_abgr: &mut [u8], + dst_stride_abgr: u32, + width: i32, + height: i32, +) { + i444_assert_safety( + src_y, + src_stride_y, + src_u, + src_stride_u, + src_v, + src_stride_v, + width, + height, + ); + argb_assert_safety(dst_abgr, dst_stride_abgr, width, height); + + unsafe { + yuv_sys::ffi::i444_to_abgr( + src_y.as_ptr(), + src_stride_y as i32, + src_u.as_ptr(), + src_stride_u as i32, + src_v.as_ptr(), + src_stride_v as i32, + dst_abgr.as_mut_ptr(), + dst_stride_abgr as i32, + width, + height, + ) + .unwrap() + } +} + +pub fn i422_to_argb( + src_y: &[u8], + src_stride_y: u32, + src_u: &[u8], + src_stride_u: u32, + src_v: &[u8], + src_stride_v: u32, + dst_argb: &mut [u8], + dst_stride_argb: u32, + width: i32, + height: i32, +) { + i422_assert_safety( + src_y, + src_stride_y, + src_u, + src_stride_u, + src_v, + src_stride_v, + width, + height, + ); + argb_assert_safety(dst_argb, dst_stride_argb, width, height); + + unsafe { + yuv_sys::ffi::i422_to_argb( + src_y.as_ptr(), + src_stride_y as i32, + src_u.as_ptr(), + src_stride_u as i32, + src_v.as_ptr(), + src_stride_v as i32, + dst_argb.as_mut_ptr(), + dst_stride_argb as i32, + width, + height, + ) + .unwrap(); + } +} + +pub fn i422_to_abgr( + src_y: &[u8], + src_stride_y: u32, + src_u: &[u8], + src_stride_u: u32, + src_v: &[u8], + src_stride_v: u32, + dst_abgr: &mut [u8], + dst_stride_abgr: u32, + width: i32, + height: i32, +) { + i422_assert_safety( + src_y, + src_stride_y, + src_u, + src_stride_u, + src_v, + src_stride_v, + width, + height, + ); + argb_assert_safety(dst_abgr, dst_stride_abgr, width, height); + + unsafe { + yuv_sys::ffi::i422_to_abgr( + src_y.as_ptr(), + src_stride_y as i32, + src_u.as_ptr(), + src_stride_u as i32, + src_v.as_ptr(), + src_stride_v as i32, + dst_abgr.as_mut_ptr(), + dst_stride_abgr as i32, + width, + height, + ) + .unwrap() + } +} + +pub fn i010_to_argb( + src_y: &[u16], + src_stride_y: u32, + src_u: &[u16], + src_stride_u: u32, + src_v: &[u16], + src_stride_v: u32, + dst_argb: &mut [u8], + dst_stride_argb: u32, + width: i32, + height: i32, +) { + i010_assert_safety( + src_y, + src_stride_y, + src_u, + src_stride_u, + src_v, + src_stride_v, + width, + height, + ); + argb_assert_safety(dst_argb, dst_stride_argb, width, height); + + unsafe { + yuv_sys::ffi::i010_to_argb( + src_y.as_ptr(), + src_stride_y as i32, + src_u.as_ptr(), + src_stride_u as i32, + src_v.as_ptr(), + src_stride_v as i32, + dst_argb.as_mut_ptr(), + dst_stride_argb as i32, + width, + height, + ) + .unwrap() + } +} + +pub fn i010_to_abgr( + src_y: &[u16], + src_stride_y: u32, + src_u: &[u16], + src_stride_u: u32, + src_v: &[u16], + src_stride_v: u32, + dst_abgr: &mut [u8], + dst_stride_abgr: u32, + width: i32, + height: i32, +) { + i010_assert_safety( + src_y, + src_stride_y, + src_u, + src_stride_u, + src_v, + src_stride_v, + width, + height, + ); + argb_assert_safety(dst_abgr, dst_stride_abgr, width, height); + + unsafe { + yuv_sys::ffi::i010_to_abgr( + src_y.as_ptr(), + src_stride_y as i32, + src_u.as_ptr(), + src_stride_u as i32, + src_v.as_ptr(), + src_stride_v as i32, + dst_abgr.as_mut_ptr(), + dst_stride_abgr as i32, + width, + height, + ) + .unwrap() + } +} + +pub fn abgr_to_nv12( + src_abgr: &[u8], + src_stride_abgr: u32, + dst_y: &mut [u8], + dst_stride_y: u32, + dst_uv: &mut [u8], + dst_stride_uv: u32, + width: i32, + height: i32, +) { + argb_assert_safety(src_abgr, src_stride_abgr, width, height); + nv12_assert_safety(dst_y, dst_stride_y, dst_uv, dst_stride_uv, width, height); + + unsafe { + yuv_sys::ffi::abgr_to_nv12( + src_abgr.as_ptr(), + src_stride_abgr as i32, + dst_y.as_mut_ptr(), + dst_stride_y as i32, + dst_uv.as_mut_ptr(), + dst_stride_uv as i32, + width, + height, + ) + .unwrap() + } +} + +pub fn argb_to_nv12( + src_argb: &[u8], + src_stride_argb: u32, + dst_y: &mut [u8], + dst_stride_y: u32, + dst_uv: &mut [u8], + dst_stride_uv: u32, + width: i32, + height: i32, +) { + argb_assert_safety(src_argb, src_stride_argb, width, height); + nv12_assert_safety(dst_y, dst_stride_y, dst_uv, dst_stride_uv, width, height); + + unsafe { + yuv_sys::ffi::argb_to_nv12( + src_argb.as_ptr(), + src_stride_argb as i32, + dst_y.as_mut_ptr(), + dst_stride_y as i32, + dst_uv.as_mut_ptr(), + dst_stride_uv as i32, + width, + height, + ) + .unwrap() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/peer_connection.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/peer_connection.rs new file mode 100644 index 0000000..d650317 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/peer_connection.rs @@ -0,0 +1,345 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fmt::Debug; + +use crate::{ + data_channel::{DataChannel, DataChannelInit}, + ice_candidate::IceCandidate, + imp::peer_connection as imp_pc, + media_stream::MediaStream, + media_stream_track::MediaStreamTrack, + peer_connection_factory::RtcConfiguration, + rtp_receiver::RtpReceiver, + rtp_sender::RtpSender, + rtp_transceiver::{RtpTransceiver, RtpTransceiverInit}, + session_description::SessionDescription, + stats::RtcStats, + MediaType, RtcError, +}; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum PeerConnectionState { + New, + Connecting, + Connected, + Disconnected, + Failed, + Closed, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum IceConnectionState { + New, + Checking, + Connected, + Completed, + Failed, + Disconnected, + Closed, + Max, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum IceGatheringState { + New, + Gathering, + Complete, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum SignalingState { + Stable, + HaveLocalOffer, + HaveLocalPrAnswer, + HaveRemoteOffer, + HaveRemotePrAnswer, + Closed, +} + +#[derive(Debug, Clone, Default)] +pub struct OfferOptions { + pub ice_restart: bool, + pub offer_to_receive_audio: bool, + pub offer_to_receive_video: bool, +} + +#[derive(Debug, Clone, Default)] +pub struct AnswerOptions {} + +#[derive(Debug, Clone)] +pub struct IceCandidateError { + pub address: String, + pub port: i32, + pub url: String, + pub error_code: i32, + pub error_text: String, +} + +#[derive(Debug, Clone)] +pub struct TrackEvent { + pub receiver: RtpReceiver, + pub streams: Vec, + pub track: MediaStreamTrack, + pub transceiver: RtpTransceiver, +} + +pub type OnConnectionChange = Box; +pub type OnDataChannel = Box; +pub type OnIceCandidate = Box; +pub type OnIceCandidateError = Box; +pub type OnIceConnectionChange = Box; +pub type OnIceGatheringChange = Box; +pub type OnNegotiationNeeded = Box; +pub type OnSignalingChange = Box; +pub type OnTrack = Box; + +#[derive(Clone)] +pub struct PeerConnection { + pub(crate) handle: imp_pc::PeerConnection, +} + +impl PeerConnection { + pub fn set_configuration(&self, config: RtcConfiguration) -> Result<(), RtcError> { + self.handle.set_configuration(config) + } + + pub async fn create_offer( + &self, + options: OfferOptions, + ) -> Result { + self.handle.create_offer(options).await + } + + pub async fn create_answer( + &self, + options: AnswerOptions, + ) -> Result { + self.handle.create_answer(options).await + } + + pub async fn set_local_description(&self, desc: SessionDescription) -> Result<(), RtcError> { + self.handle.set_local_description(desc).await + } + + pub async fn set_remote_description(&self, desc: SessionDescription) -> Result<(), RtcError> { + self.handle.set_remote_description(desc).await + } + + pub async fn add_ice_candidate(&self, candidate: IceCandidate) -> Result<(), RtcError> { + self.handle.add_ice_candidate(candidate).await + } + + pub fn create_data_channel( + &self, + label: &str, + init: DataChannelInit, + ) -> Result { + self.handle.create_data_channel(label, init) + } + + pub fn add_track>( + &self, + track: MediaStreamTrack, + streams_ids: &[T], + ) -> Result { + self.handle.add_track(track, streams_ids) + } + + pub fn remove_track(&self, sender: RtpSender) -> Result<(), RtcError> { + self.handle.remove_track(sender) + } + + pub async fn get_stats(&self) -> Result, RtcError> { + self.handle.get_stats().await + } + + pub fn add_transceiver( + &self, + track: MediaStreamTrack, + init: RtpTransceiverInit, + ) -> Result { + self.handle.add_transceiver(track, init) + } + + pub fn add_transceiver_for_media( + &self, + media_type: MediaType, + init: RtpTransceiverInit, + ) -> Result { + self.handle.add_transceiver_for_media(media_type, init) + } + + pub fn close(&self) { + self.handle.close() + } + + pub fn restart_ice(&self) { + self.handle.restart_ice() + } + + pub fn connection_state(&self) -> PeerConnectionState { + self.handle.connection_state() + } + + pub fn ice_connection_state(&self) -> IceConnectionState { + self.handle.ice_connection_state() + } + + pub fn ice_gathering_state(&self) -> IceGatheringState { + self.handle.ice_gathering_state() + } + + pub fn signaling_state(&self) -> SignalingState { + self.handle.signaling_state() + } + + pub fn current_local_description(&self) -> Option { + self.handle.current_local_description() + } + + pub fn current_remote_description(&self) -> Option { + self.handle.current_remote_description() + } + + pub fn senders(&self) -> Vec { + self.handle.senders() + } + + pub fn receivers(&self) -> Vec { + self.handle.receivers() + } + + pub fn transceivers(&self) -> Vec { + self.handle.transceivers() + } + + pub fn on_connection_state_change(&self, f: Option) { + self.handle.on_connection_state_change(f) + } + + pub fn on_data_channel(&self, f: Option) { + self.handle.on_data_channel(f) + } + + pub fn on_ice_candidate(&self, f: Option) { + self.handle.on_ice_candidate(f) + } + + pub fn on_ice_candidate_error(&self, f: Option) { + self.handle.on_ice_candidate_error(f) + } + + pub fn on_ice_connection_state_change(&self, f: Option) { + self.handle.on_ice_connection_state_change(f) + } + + pub fn on_ice_gathering_state_change(&self, f: Option) { + self.handle.on_ice_gathering_state_change(f) + } + + pub fn on_negotiation_needed(&self, f: Option) { + self.handle.on_negotiation_needed(f) + } + + pub fn on_signaling_state_change(&self, f: Option) { + self.handle.on_signaling_state_change(f) + } + + pub fn on_track(&self, f: Option) { + self.handle.on_track(f) + } +} + +impl Debug for PeerConnection { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("PeerConnection") + .field("state", &self.connection_state()) + .field("ice_state", &self.ice_connection_state()) + .finish() + } +} + +#[cfg(test)] +mod tests { + use log::trace; + use tokio::sync::mpsc; + + use crate::{peer_connection::*, peer_connection_factory::*}; + + #[tokio::test] + async fn create_pc() { + let _ = env_logger::builder().is_test(true).try_init(); + + let factory = PeerConnectionFactory::default(); + let config = RtcConfiguration { + ice_servers: vec![IceServer { + urls: vec!["stun:stun1.l.google.com:19302".to_string()], + username: "".into(), + password: "".into(), + }], + continual_gathering_policy: ContinualGatheringPolicy::GatherOnce, + ice_transport_type: IceTransportsType::All, + }; + + let bob = factory.create_peer_connection(config.clone()).unwrap(); + let alice = factory.create_peer_connection(config.clone()).unwrap(); + + let (bob_ice_tx, mut bob_ice_rx) = mpsc::unbounded_channel::(); + let (alice_ice_tx, mut alice_ice_rx) = mpsc::unbounded_channel::(); + let (alice_dc_tx, mut alice_dc_rx) = mpsc::unbounded_channel::(); + + bob.on_ice_candidate(Some(Box::new(move |candidate| { + bob_ice_tx.send(candidate).unwrap(); + }))); + + alice.on_ice_candidate(Some(Box::new(move |candidate| { + alice_ice_tx.send(candidate).unwrap(); + }))); + + alice.on_data_channel(Some(Box::new(move |dc| { + alice_dc_tx.send(dc).unwrap(); + }))); + + let bob_dc = bob.create_data_channel("test_dc", DataChannelInit::default()).unwrap(); + + let offer = bob.create_offer(OfferOptions::default()).await.unwrap(); + trace!("Bob offer: {:?}", offer); + bob.set_local_description(offer.clone()).await.unwrap(); + alice.set_remote_description(offer).await.unwrap(); + + let answer = alice.create_answer(AnswerOptions::default()).await.unwrap(); + trace!("Alice answer: {:?}", answer); + alice.set_local_description(answer.clone()).await.unwrap(); + bob.set_remote_description(answer).await.unwrap(); + + let bob_ice = bob_ice_rx.recv().await.unwrap(); + let alice_ice = alice_ice_rx.recv().await.unwrap(); + + bob.add_ice_candidate(alice_ice).await.unwrap(); + alice.add_ice_candidate(bob_ice).await.unwrap(); + + let (data_tx, mut data_rx) = mpsc::unbounded_channel::(); + let alice_dc = alice_dc_rx.recv().await.unwrap(); + alice_dc.on_message(Some(Box::new(move |buffer| { + data_tx.send(String::from_utf8_lossy(buffer.data).to_string()).unwrap(); + }))); + + bob_dc.send(b"This is a test", true).unwrap(); + assert_eq!(data_rx.recv().await.unwrap(), "This is a test"); + + alice.close(); + bob.close(); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/peer_connection_factory.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/peer_connection_factory.rs new file mode 100644 index 0000000..48ec7f3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/peer_connection_factory.rs @@ -0,0 +1,316 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fmt::Debug; + +use crate::{ + imp::peer_connection_factory as imp_pcf, peer_connection::PeerConnection, + rtp_parameters::RtpCapabilities, MediaType, RtcError, +}; + +#[derive(Debug, Clone)] +pub struct IceServer { + pub urls: Vec, + pub username: String, + pub password: String, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum ContinualGatheringPolicy { + GatherOnce, + GatherContinually, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum IceTransportsType { + Relay, + NoHost, + All, +} + +#[derive(Debug, Clone)] +pub struct RtcConfiguration { + pub ice_servers: Vec, + pub continual_gathering_policy: ContinualGatheringPolicy, + pub ice_transport_type: IceTransportsType, +} + +impl Default for RtcConfiguration { + fn default() -> Self { + Self { + ice_servers: vec![], + continual_gathering_policy: ContinualGatheringPolicy::GatherContinually, + ice_transport_type: IceTransportsType::All, + } + } +} + +#[derive(Clone, Default)] +pub struct PeerConnectionFactory { + pub(crate) handle: imp_pcf::PeerConnectionFactory, +} + +impl Debug for PeerConnectionFactory { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("PeerConnectionFactory").finish() + } +} + +impl PeerConnectionFactory { + pub fn create_peer_connection( + &self, + config: RtcConfiguration, + ) -> Result { + self.handle.create_peer_connection(config) + } + + pub fn get_rtp_sender_capabilities(&self, media_type: MediaType) -> RtpCapabilities { + self.handle.get_rtp_sender_capabilities(media_type) + } + + pub fn get_rtp_receiver_capabilities(&self, media_type: MediaType) -> RtpCapabilities { + self.handle.get_rtp_receiver_capabilities(media_type) + } +} + +pub mod native { + use super::PeerConnectionFactory; + use crate::{ + audio_source::native::NativeAudioSource, audio_track::RtcAudioTrack, + video_source::native::NativeVideoSource, video_track::RtcVideoTrack, + }; + + pub trait PeerConnectionFactoryExt { + fn create_video_track(&self, label: &str, source: NativeVideoSource) -> RtcVideoTrack; + fn create_audio_track(&self, label: &str, source: NativeAudioSource) -> RtcAudioTrack; + + /// Create an audio track that uses the Platform ADM for capture. + /// The track will capture audio from the selected recording device. + fn create_device_audio_track(&self, label: &str) -> RtcAudioTrack; + + // Device enumeration + fn playout_devices(&self) -> i16; + fn recording_devices(&self) -> i16; + fn playout_device_name(&self, index: u16) -> String; + fn recording_device_name(&self, index: u16) -> String; + /// Get device GUID (platform-specific unique identifier, stable across hot-plug) + fn playout_device_guid(&self, index: u16) -> String; + fn recording_device_guid(&self, index: u16) -> String; + + // Device selection by index + fn set_playout_device(&self, index: u16) -> bool; + fn set_recording_device(&self, index: u16) -> bool; + /// Device selection by GUID (preferred - stable across device changes) + fn set_playout_device_by_guid(&self, guid: &str) -> bool; + fn set_recording_device_by_guid(&self, guid: &str) -> bool; + + // Recording control (for device switching while active) + fn stop_recording(&self) -> bool; + fn init_recording(&self) -> bool; + fn start_recording(&self) -> bool; + fn recording_is_initialized(&self) -> bool; + + // Playout control (for device switching while active) + fn stop_playout(&self) -> bool; + fn init_playout(&self) -> bool; + fn start_playout(&self) -> bool; + fn playout_is_initialized(&self) -> bool; + + // Built-in audio processing (hardware AEC/AGC/NS) + // Only available on iOS and some Android devices + fn builtin_aec_is_available(&self) -> bool; + fn builtin_agc_is_available(&self) -> bool; + fn builtin_ns_is_available(&self) -> bool; + fn enable_builtin_aec(&self, enable: bool) -> bool; + fn enable_builtin_agc(&self, enable: bool) -> bool; + fn enable_builtin_ns(&self, enable: bool) -> bool; + + // ADM recording control + // Use this to disable microphone when only using NativeAudioSource + fn set_adm_recording_enabled(&self, enabled: bool); + fn adm_recording_enabled(&self) -> bool; + + // ADM playout control + // When disabled (default), playout uses synthetic mode - remote audio is + // delivered via FFI callbacks. When enabled, plays through platform speakers. + fn set_adm_playout_enabled(&self, enabled: bool); + fn adm_playout_enabled(&self) -> bool; + + // Platform ADM lifecycle management + // Call acquire_platform_adm when creating PlatformAudio. + // Call release_platform_adm when disposing PlatformAudio. + // The Platform ADM is only created when first acquired, and terminated + // when the last reference is released. + fn acquire_platform_adm(&self) -> bool; + fn release_platform_adm(&self); + fn platform_adm_ref_count(&self) -> i32; + fn is_platform_adm_active(&self) -> bool; + + // Ensures the Platform ADM exists, retrying creation if an earlier + // attempt failed (e.g. the OS audio stack was still starting up). + fn ensure_platform_adm(&self) -> bool; + // Distinguishes "audio stack unavailable" from "zero audio devices". + fn platform_adm_available(&self) -> bool; + } + + impl PeerConnectionFactoryExt for PeerConnectionFactory { + fn create_video_track(&self, label: &str, source: NativeVideoSource) -> RtcVideoTrack { + self.handle.create_video_track(label, source) + } + + fn create_audio_track(&self, label: &str, source: NativeAudioSource) -> RtcAudioTrack { + self.handle.create_audio_track(label, source) + } + + fn create_device_audio_track(&self, label: &str) -> RtcAudioTrack { + self.handle.create_device_audio_track(label) + } + + fn playout_devices(&self) -> i16 { + self.handle.playout_devices() + } + + fn recording_devices(&self) -> i16 { + self.handle.recording_devices() + } + + fn playout_device_name(&self, index: u16) -> String { + self.handle.playout_device_name(index) + } + + fn recording_device_name(&self, index: u16) -> String { + self.handle.recording_device_name(index) + } + + fn playout_device_guid(&self, index: u16) -> String { + self.handle.playout_device_guid(index) + } + + fn recording_device_guid(&self, index: u16) -> String { + self.handle.recording_device_guid(index) + } + + fn set_playout_device(&self, index: u16) -> bool { + self.handle.set_playout_device(index) + } + + fn set_recording_device(&self, index: u16) -> bool { + self.handle.set_recording_device(index) + } + + fn set_playout_device_by_guid(&self, guid: &str) -> bool { + self.handle.set_playout_device_by_guid(guid) + } + + fn set_recording_device_by_guid(&self, guid: &str) -> bool { + self.handle.set_recording_device_by_guid(guid) + } + + fn stop_recording(&self) -> bool { + self.handle.stop_recording() + } + + fn init_recording(&self) -> bool { + self.handle.init_recording() + } + + fn start_recording(&self) -> bool { + self.handle.start_recording() + } + + fn recording_is_initialized(&self) -> bool { + self.handle.recording_is_initialized() + } + + fn stop_playout(&self) -> bool { + self.handle.stop_playout() + } + + fn init_playout(&self) -> bool { + self.handle.init_playout() + } + + fn start_playout(&self) -> bool { + self.handle.start_playout() + } + + fn playout_is_initialized(&self) -> bool { + self.handle.playout_is_initialized() + } + + fn builtin_aec_is_available(&self) -> bool { + self.handle.builtin_aec_is_available() + } + + fn builtin_agc_is_available(&self) -> bool { + self.handle.builtin_agc_is_available() + } + + fn builtin_ns_is_available(&self) -> bool { + self.handle.builtin_ns_is_available() + } + + fn enable_builtin_aec(&self, enable: bool) -> bool { + self.handle.enable_builtin_aec(enable) + } + + fn enable_builtin_agc(&self, enable: bool) -> bool { + self.handle.enable_builtin_agc(enable) + } + + fn enable_builtin_ns(&self, enable: bool) -> bool { + self.handle.enable_builtin_ns(enable) + } + + fn set_adm_recording_enabled(&self, enabled: bool) { + self.handle.set_adm_recording_enabled(enabled) + } + + fn adm_recording_enabled(&self) -> bool { + self.handle.adm_recording_enabled() + } + + fn set_adm_playout_enabled(&self, enabled: bool) { + self.handle.set_adm_playout_enabled(enabled) + } + + fn adm_playout_enabled(&self) -> bool { + self.handle.adm_playout_enabled() + } + + fn acquire_platform_adm(&self) -> bool { + self.handle.acquire_platform_adm() + } + + fn release_platform_adm(&self) { + self.handle.release_platform_adm() + } + + fn platform_adm_ref_count(&self) -> i32 { + self.handle.platform_adm_ref_count() + } + + fn is_platform_adm_active(&self) -> bool { + self.handle.is_platform_adm_active() + } + + fn ensure_platform_adm(&self) -> bool { + self.handle.ensure_platform_adm() + } + + fn platform_adm_available(&self) -> bool { + self.handle.platform_adm_available() + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/prelude.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/prelude.rs new file mode 100644 index 0000000..2b61aa1 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/prelude.rs @@ -0,0 +1,43 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub use crate::{ + audio_frame::AudioFrame, + audio_source::{AudioSourceOptions, RtcAudioSource}, + audio_track::RtcAudioTrack, + data_channel::{DataBuffer, DataChannel, DataChannelError, DataChannelInit, DataChannelState}, + ice_candidate::IceCandidate, + media_stream::MediaStream, + media_stream_track::{MediaStreamTrack, RtcTrackState}, + peer_connection::{ + AnswerOptions, IceConnectionState, IceGatheringState, OfferOptions, PeerConnection, + PeerConnectionState, SignalingState, + }, + peer_connection_factory::{ + ContinualGatheringPolicy, IceServer, IceTransportsType, PeerConnectionFactory, + RtcConfiguration, + }, + rtp_parameters::*, + rtp_receiver::RtpReceiver, + rtp_sender::RtpSender, + rtp_transceiver::{RtpTransceiver, RtpTransceiverDirection, RtpTransceiverInit}, + session_description::{SdpType, SessionDescription}, + video_frame::{ + BoxVideoBuffer, BoxVideoFrame, I010Buffer, I420ABuffer, I420Buffer, I422Buffer, I444Buffer, + NV12Buffer, VideoBuffer, VideoBufferType, VideoFormatType, VideoFrame, VideoRotation, + }, + video_source::{RtcVideoSource, VideoResolution}, + video_track::RtcVideoTrack, + MediaType, RtcError, RtcErrorType, +}; diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/recorded_audio.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/recorded_audio.rs new file mode 100644 index 0000000..aabc37b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/recorded_audio.rs @@ -0,0 +1,44 @@ +// Copyright 2026 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[cfg(not(target_arch = "wasm32"))] +pub mod native { + use webrtc_sys::recorded_audio_tap::ffi as sys; + use webrtc_sys::recorded_audio_tap::RecordedAudioSinkWrapper; + + /// Handle to an installed recorded-audio sink. Dropping it does not clear + /// the sink; call [`clear_recorded_audio_sink`] with this generation. + pub type RecordedAudioSinkGeneration = u64; + + /// Installs a process-global tap on platform-ADM recorded microphone audio. + /// + /// `callback` is invoked on the ADM capture thread with one 48kHz mono + /// 10ms frame (480 samples) per call: `(samples, sample_rate_hz, + /// num_channels, samples_per_channel)`. It must be wait-free: do no + /// allocation or blocking work, only hand the frame to a bounded queue. + /// Returns a generation token to pass to [`clear_recorded_audio_sink`]. + pub fn set_recorded_audio_sink(callback: F) -> RecordedAudioSinkGeneration + where + F: Fn(&[i16], i32, usize, usize) + Send + Sync + 'static, + { + sys::set_recorded_audio_sink(Box::new(RecordedAudioSinkWrapper::new(Box::new(callback)))) + } + + /// Removes the recorded-audio sink, but only if `generation` is still the + /// installed one. A stale token is a no-op, so a late teardown cannot + /// clobber a sink a newer caller installed. + pub fn clear_recorded_audio_sink(generation: RecordedAudioSinkGeneration) { + sys::clear_recorded_audio_sink(generation); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/rtp_parameters.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/rtp_parameters.rs new file mode 100644 index 0000000..75d8b18 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/rtp_parameters.rs @@ -0,0 +1,98 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::rtp_transceiver::RtpTransceiverDirection; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum Priority { + VeryLow, + Low, + Medium, + High, +} + +#[derive(Debug, Clone)] +pub struct RtpHeaderExtensionParameters { + pub uri: String, + pub id: i32, + pub encrypted: bool, +} + +#[derive(Debug, Clone, Default)] +pub struct RtpParameters { + pub codecs: Vec, + pub header_extensions: Vec, + pub rtcp: RtcpParameters, +} + +#[derive(Debug, Clone, Default)] +pub struct RtpCodecParameters { + pub payload_type: u8, + pub mime_type: String, // read-only + pub clock_rate: Option, + pub channels: Option, +} + +#[derive(Debug, Clone, Default)] +pub struct RtcpParameters { + pub cname: String, + pub reduced_size: bool, +} + +#[derive(Debug, Clone)] +pub struct RtpEncodingParameters { + pub active: bool, + pub max_bitrate: Option, + pub max_framerate: Option, + pub priority: Priority, + pub rid: String, + pub scale_resolution_down_by: Option, + /// RTP scalability mode (e.g. "L3T3_KEY"). Required to enable true + /// SVC for codecs that support it (VP9, AV1). + pub scalability_mode: Option, +} + +#[derive(Debug, Clone)] +pub struct RtpCodecCapability { + pub channels: Option, + pub clock_rate: Option, + pub mime_type: String, + pub sdp_fmtp_line: Option, +} + +#[derive(Debug, Clone)] +pub struct RtpHeaderExtensionCapability { + pub uri: String, + pub direction: RtpTransceiverDirection, +} + +#[derive(Debug, Clone)] +pub struct RtpCapabilities { + pub codecs: Vec, + pub header_extensions: Vec, +} + +impl Default for RtpEncodingParameters { + fn default() -> Self { + Self { + active: true, + max_bitrate: None, + max_framerate: None, + priority: Priority::Low, + rid: String::default(), + scale_resolution_down_by: None, + scalability_mode: None, + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/rtp_receiver.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/rtp_receiver.rs new file mode 100644 index 0000000..a5318b1 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/rtp_receiver.rs @@ -0,0 +1,48 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fmt::Debug; + +use crate::{ + imp::rtp_receiver as imp_rr, media_stream_track::MediaStreamTrack, + rtp_parameters::RtpParameters, stats::RtcStats, RtcError, +}; + +#[derive(Clone)] +pub struct RtpReceiver { + pub(crate) handle: imp_rr::RtpReceiver, +} + +impl RtpReceiver { + pub fn track(&self) -> Option { + self.handle.track() + } + + pub async fn get_stats(&self) -> Result, RtcError> { + self.handle.get_stats().await + } + + pub fn parameters(&self) -> RtpParameters { + self.handle.parameters() + } +} + +impl Debug for RtpReceiver { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("RtpReceiver") + .field("track", &self.track()) + .field("cname", &self.parameters().rtcp.cname) + .finish() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/rtp_sender.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/rtp_sender.rs new file mode 100644 index 0000000..cf92509 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/rtp_sender.rs @@ -0,0 +1,53 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fmt::Debug; + +use crate::{ + imp::rtp_sender as imp_rs, media_stream_track::MediaStreamTrack, rtp_parameters::RtpParameters, + stats::RtcStats, RtcError, +}; + +#[derive(Clone)] +pub struct RtpSender { + pub(crate) handle: imp_rs::RtpSender, +} + +impl RtpSender { + pub fn track(&self) -> Option { + self.handle.track() + } + + pub async fn get_stats(&self) -> Result, RtcError> { + self.handle.get_stats().await + } + + pub fn set_track(&self, track: Option) -> Result<(), RtcError> { + self.handle.set_track(track) + } + + pub fn parameters(&self) -> RtpParameters { + self.handle.parameters() + } + + pub fn set_parameters(&self, parameters: RtpParameters) -> Result<(), RtcError> { + self.handle.set_parameters(parameters) + } +} + +impl Debug for RtpSender { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("RtpReceiver").field("cname", &self.parameters().rtcp.cname).finish() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/rtp_transceiver.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/rtp_transceiver.rs new file mode 100644 index 0000000..00a7a91 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/rtp_transceiver.rs @@ -0,0 +1,85 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fmt::Debug; + +use crate::{ + imp::rtp_transceiver as imp_rt, + rtp_parameters::{RtpCodecCapability, RtpEncodingParameters}, + rtp_receiver::RtpReceiver, + rtp_sender::RtpSender, + RtcError, +}; + +#[derive(Debug, Clone)] +pub struct RtpTransceiverInit { + pub direction: RtpTransceiverDirection, + pub stream_ids: Vec, + pub send_encodings: Vec, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum RtpTransceiverDirection { + SendRecv, + SendOnly, + RecvOnly, + Inactive, + Stopped, +} + +#[derive(Clone)] +pub struct RtpTransceiver { + pub(crate) handle: imp_rt::RtpTransceiver, +} + +impl RtpTransceiver { + pub fn mid(&self) -> Option { + self.handle.mid() + } + + pub fn current_direction(&self) -> Option { + self.handle.current_direction() + } + + pub fn direction(&self) -> RtpTransceiverDirection { + self.handle.direction() + } + + pub fn sender(&self) -> RtpSender { + self.handle.sender() + } + + pub fn receiver(&self) -> RtpReceiver { + self.handle.receiver() + } + + pub fn set_codec_preferences(&self, codecs: Vec) -> Result<(), RtcError> { + self.handle.set_codec_preferences(codecs) + } + + pub fn stop(&self) -> Result<(), RtcError> { + self.handle.stop() + } +} + +impl Debug for RtpTransceiver { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("RtpTransceiver") + .field("mid", &self.mid()) + .field("direction", &self.direction()) + .field("sender", &self.sender()) + .field("receiver", &self.receiver()) + .finish() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/session_description.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/session_description.rs new file mode 100644 index 0000000..4da7156 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/session_description.rs @@ -0,0 +1,90 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{ + fmt::{Debug, Display}, + str::FromStr, +}; + +use thiserror::Error; + +use crate::imp::session_description as sd_imp; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum SdpType { + Offer, + PrAnswer, + Answer, + Rollback, +} + +impl FromStr for SdpType { + type Err = &'static str; + + fn from_str(sdp_type: &str) -> Result { + match sdp_type { + "offer" => Ok(Self::Offer), + "pranswer" => Ok(Self::PrAnswer), + "answer" => Ok(Self::Answer), + "rollback" => Ok(Self::Rollback), + _ => Err("invalid SdpType"), + } + } +} + +impl Display for SdpType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let s = match self { + SdpType::Offer => "offer", + SdpType::PrAnswer => "pranswer", + SdpType::Answer => "answer", + SdpType::Rollback => "rollback", + }; + write!(f, "{}", s) + } +} + +#[derive(Clone)] +pub struct SessionDescription { + pub(crate) handle: sd_imp::SessionDescription, +} + +#[derive(Clone, Error, Debug)] +#[error("Failed to parse sdp: {line} - {description}")] +pub struct SdpParseError { + pub line: String, + pub description: String, +} + +impl SessionDescription { + pub fn parse(sdp: &str, sdp_type: SdpType) -> Result { + sd_imp::SessionDescription::parse(sdp, sdp_type) + } + + pub fn sdp_type(&self) -> SdpType { + self.handle.sdp_type() + } +} + +impl ToString for SessionDescription { + fn to_string(&self) -> String { + self.handle.to_string() + } +} + +impl Debug for SessionDescription { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("SessionDescription").field("sdp_type", &self.sdp_type()).finish() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/stats.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/stats.rs new file mode 100644 index 0000000..a6da734 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/stats.rs @@ -0,0 +1,624 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::collections::HashMap; + +use serde::Deserialize; + +use crate::data_channel::DataChannelState; + +/// Values from https://www.w3.org/TR/webrtc-stats/ (NOTE: Some of the structs are not in the SPEC +/// but inside libwebrtc) +/// serde will handle the magic of correctly deserializing the json into our structs. +/// The enums values are inside encapsulated inside option because we're not sure about their +/// default values (So we default to None instead of an arbitrary value) + +#[derive(Debug, Clone, Deserialize)] +#[serde(tag = "type")] +#[serde(rename_all = "kebab-case")] +pub enum RtcStats { + Codec(CodecStats), + InboundRtp(InboundRtpStats), + OutboundRtp(OutboundRtpStats), + RemoteInboundRtp(RemoteInboundRtpStats), + RemoteOutboundRtp(RemoteOutboundRtpStats), + MediaSource(MediaSourceStats), + MediaPlayout(MediaPlayoutStats), + PeerConnection(PeerConnectionStats), + DataChannel(DataChannelStats), + Transport(TransportStats), + CandidatePair(CandidatePairStats), + LocalCandidate(LocalCandidateStats), + RemoteCandidate(RemoteCandidateStats), + Certificate(CertificateStats), + Stream(StreamStats), + Track, // Deprecated +} + +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum QualityLimitationReason { + #[default] + None, + Cpu, + Bandwidth, + Other, +} + +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum IceRole { + #[default] + Unknown, + Controlling, + Controlled, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum DtlsTransportState { + New, + Connecting, + Connected, + Closed, + Failed, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum IceTransportState { + New, + Checking, + Connected, + Completed, + Disconnected, + Failed, + Closed, +} + +#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum DtlsRole { + Client, + Server, + #[default] + Unknown, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub enum IceCandidatePairState { + Frozen, + Waiting, + InProgress, // in-progress + Failed, + Succeeded, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum IceCandidateType { + Host, + Srflx, + Prflx, + Relay, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum IceServerTransportProtocol { + Udp, + Tcp, + Tls, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum IceTcpCandidateType { + Active, + Passive, + So, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct CodecStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub codec: dictionaries::CodecStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct InboundRtpStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub stream: dictionaries::RtpStreamStats, + + #[serde(flatten)] + pub received: dictionaries::ReceivedRtpStreamStats, + + #[serde(flatten)] + pub inbound: dictionaries::InboundRtpStreamStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct OutboundRtpStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub stream: dictionaries::RtpStreamStats, + + #[serde(flatten)] + pub sent: dictionaries::SentRtpStreamStats, + + #[serde(flatten)] + pub outbound: dictionaries::OutboundRtpStreamStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct RemoteInboundRtpStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub stream: dictionaries::RtpStreamStats, + + #[serde(flatten)] + pub received: dictionaries::ReceivedRtpStreamStats, + + #[serde(flatten)] + pub remote_inbound: dictionaries::RemoteInboundRtpStreamStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct RemoteOutboundRtpStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub stream: dictionaries::RtpStreamStats, + + #[serde(flatten)] + pub sent: dictionaries::SentRtpStreamStats, + + #[serde(flatten)] + pub remote_outbound: dictionaries::RemoteOutboundRtpStreamStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct MediaSourceStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub source: dictionaries::MediaSourceStats, + + #[serde(flatten)] + pub audio: dictionaries::AudioSourceStats, + + #[serde(flatten)] + pub video: dictionaries::VideoSourceStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct MediaPlayoutStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub audio_playout: dictionaries::AudioPlayoutStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct PeerConnectionStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub pc: dictionaries::PeerConnectionStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct DataChannelStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub dc: dictionaries::DataChannelStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct TransportStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub transport: dictionaries::TransportStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct CandidatePairStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub candidate_pair: dictionaries::CandidatePairStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct LocalCandidateStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub local_candidate: dictionaries::IceCandidateStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct RemoteCandidateStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub remote_candidate: dictionaries::IceCandidateStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct CertificateStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub certificate: dictionaries::CertificateStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct StreamStats { + #[serde(flatten)] + pub rtc: dictionaries::RtcStats, + + #[serde(flatten)] + pub stream: dictionaries::StreamStats, +} + +#[derive(Debug, Default, Clone, Deserialize)] +pub struct TrackStats {} + +pub mod dictionaries { + use super::*; + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct RtcStats { + pub id: String, + pub timestamp: i64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct CodecStats { + pub payload_type: u32, + pub transport_id: String, + pub mime_type: String, + pub clock_rate: u32, + pub channels: u32, + pub sdp_fmtp_line: String, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct RtpStreamStats { + pub ssrc: u32, + pub kind: String, + pub transport_id: String, + pub codec_id: String, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct ReceivedRtpStreamStats { + pub packets_received: u64, + pub packets_lost: i64, + pub jitter: f64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct InboundRtpStreamStats { + pub track_identifier: String, + pub mid: String, + pub remote_id: String, + pub frames_decoded: u32, + pub key_frames_decoded: u32, + pub frames_rendered: u32, + pub frames_dropped: u32, + pub frame_width: u32, + pub frame_height: u32, + pub frames_per_second: f64, + pub qp_sum: u64, + pub total_decode_time: f64, + pub total_inter_frame_delay: f64, + pub total_squared_inter_frame_delay: f64, + pub pause_count: u32, + pub total_pause_duration: f64, + pub freeze_count: u32, + pub total_freeze_duration: f64, + pub last_packet_received_timestamp: f64, + pub header_bytes_received: u64, + pub packets_discarded: u64, + pub fec_bytes_received: u64, + pub fec_packets_received: u64, + pub fec_packets_discarded: u64, + pub bytes_received: u64, + pub nack_count: u32, + pub fir_count: u32, + pub pli_count: u32, + pub total_processing_delay: f64, + pub estimated_playout_timestamp: f64, + pub jitter_buffer_delay: f64, + pub jitter_buffer_target_delay: f64, + pub jitter_buffer_emitted_count: u64, + pub jitter_buffer_minimum_delay: f64, + pub total_samples_received: u64, + pub concealed_samples: u64, + pub silent_concealed_samples: u64, + pub concealment_events: u64, + pub inserted_samples_for_deceleration: u64, + pub removed_samples_for_acceleration: u64, + pub audio_level: f64, + pub total_audio_energy: f64, + pub total_samples_duration: f64, + pub frames_received: u64, + pub decoder_implementation: String, + pub playout_id: String, + pub power_efficient_decoder: bool, + pub frames_assembled_from_multiple_packets: u64, + pub total_assembly_time: f64, + pub retransmitted_packets_received: u64, + pub retransmitted_bytes_received: u64, + pub rtx_ssrc: u32, + pub fec_ssrc: u32, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct SentRtpStreamStats { + pub packets_sent: u64, + pub bytes_sent: u64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct OutboundRtpStreamStats { + pub mid: String, + pub media_source_id: String, + pub remote_id: String, + pub rid: String, + pub header_bytes_sent: u64, + pub retransmitted_packets_sent: u64, + pub retransmitted_bytes_sent: u64, + pub rtx_ssrc: u32, + pub target_bitrate: f64, + pub total_encoded_bytes_target: u64, + pub frame_width: u32, + pub frame_height: u32, + pub frames_per_second: f64, + pub frames_sent: u32, + pub huge_frames_sent: u32, + pub frames_encoded: u32, + pub key_frames_encoded: u32, + pub qp_sum: u64, + pub total_encode_time: f64, + pub total_packet_send_delay: f64, + pub quality_limitation_reason: QualityLimitationReason, + pub quality_limitation_durations: HashMap, + pub quality_limitation_resolution_changes: u32, + pub nack_count: u32, + pub fir_count: u32, + pub pli_count: u32, + pub encoder_implementation: String, + pub power_efficient_encoder: bool, + pub active: bool, + pub scalibility_mode: String, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct RemoteInboundRtpStreamStats { + pub local_id: String, + pub round_trip_time: f64, + pub total_round_trip_time: f64, + pub fraction_lost: f64, + pub round_trip_time_measurements: u64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct RemoteOutboundRtpStreamStats { + pub local_id: String, + pub remote_timestamp: f64, + pub reports_sent: u64, + pub round_trip_time: f64, + pub total_round_trip_time: f64, + pub round_trip_time_measurements: u64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct MediaSourceStats { + pub track_identifier: String, + pub kind: String, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct AudioSourceStats { + pub audio_level: f64, + pub total_audio_energy: f64, + pub total_samples_duration: f64, + pub echo_return_loss: f64, + pub echo_return_loss_enhancement: f64, + pub dropped_samples_duration: f64, + pub dropped_samples_events: u32, + pub total_capture_delay: f64, + pub total_samples_captured: u64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct VideoSourceStats { + pub width: u32, + pub height: u32, + pub frames: u32, + pub frames_per_second: f64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct AudioPlayoutStats { + pub kind: String, + pub synthesized_samples_duration: f64, + pub synthesized_samples_events: u32, + pub total_samples_duration: f64, + pub total_playout_delay: f64, + pub total_samples_count: u64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct PeerConnectionStats { + pub data_channels_opened: u32, + pub data_channels_closed: u32, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct DataChannelStats { + pub label: String, + pub protocol: String, + pub data_channel_identifier: i32, + pub state: Option, + pub messages_sent: u32, + pub bytes_sent: u64, + pub messages_received: u32, + pub bytes_received: u64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct TransportStats { + pub packets_sent: u64, + pub packets_received: u64, + pub bytes_sent: u64, + pub bytes_received: u64, + pub ice_role: IceRole, + pub ice_local_username_fragment: String, + pub dtls_state: Option, + pub ice_state: Option, + pub selected_candidate_pair_id: String, + pub local_certificate_id: String, + pub remote_certificate_id: String, + pub tls_version: String, + pub dtls_cipher: String, + pub dtls_role: DtlsRole, + pub srtp_cipher: String, + pub selected_candidate_pair_changes: u32, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct CandidatePairStats { + pub transport_id: String, + pub local_candidate_id: String, + pub remote_candidate_id: String, + pub state: Option, + pub nominated: bool, + pub packets_sent: u64, + pub packets_received: u64, + pub bytes_sent: u64, + pub bytes_received: u64, + pub last_packet_sent_timestamp: f64, + pub last_packet_received_timestamp: f64, + pub total_round_trip_time: f64, + pub current_round_trip_time: f64, + pub available_outgoing_bitrate: f64, + pub available_incoming_bitrate: f64, + pub requests_received: u64, + pub requests_sent: u64, + pub responses_received: u64, + pub responses_sent: u64, + pub consent_requests_sent: u64, + pub packets_discarded_on_send: u32, + pub bytes_discarded_on_send: u64, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct IceCandidateStats { + pub transport_id: String, + pub address: String, + pub port: i32, + pub protocol: String, + pub candidate_type: Option, + pub priority: i32, + pub url: String, + pub relay_protocol: Option, + pub foundation: String, + pub related_address: String, + pub related_port: i32, + pub username_fragment: String, + pub tcp_type: Option, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct CertificateStats { + pub fingerprint: String, + pub fingerprint_algorithm: String, + pub base64_certificate: String, + pub issuer_certificate_id: String, + } + + #[derive(Debug, Default, Clone, Deserialize)] + #[serde(rename_all = "camelCase")] + #[serde(default)] + pub struct StreamStats { + pub id: String, + pub stream_identifier: String, + // pub timestamp: i64, + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/video_frame.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/video_frame.rs new file mode 100644 index 0000000..5cd2349 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/video_frame.rs @@ -0,0 +1,596 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fmt::Debug; + +use thiserror::Error; + +use crate::imp::video_frame as vf_imp; + +#[derive(Debug, Error)] +pub enum SinkError { + #[error("platform error: {0}")] + Platform(String), +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum VideoRotation { + VideoRotation0 = 0, + VideoRotation90 = 90, + VideoRotation180 = 180, + VideoRotation270 = 270, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum VideoFormatType { + ARGB, + BGRA, + ABGR, + RGBA, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[non_exhaustive] +pub enum VideoBufferType { + Native, + I420, + I420A, + I422, + I444, + I010, + NV12, +} + +/// Metadata carried alongside a video frame via the packet trailer mechanism. +/// +/// Each field corresponds to an independently negotiable packet trailer feature +/// (`PTF_USER_TIMESTAMP`, `PTF_FRAME_ID`), so individual fields are `Option`. +#[derive(Debug, Clone, Copy)] +pub struct FrameMetadata { + /// Wall-clock capture time in microseconds, when `PTF_USER_TIMESTAMP` is enabled. + pub user_timestamp: Option, + /// Monotonically increasing frame identifier, when `PTF_FRAME_ID` is enabled. + pub frame_id: Option, +} + +#[derive(Debug)] +pub struct VideoFrame +where + T: AsRef, +{ + pub rotation: VideoRotation, + pub timestamp_us: i64, // When the frame was captured in microseconds + /// Packet-trailer metadata, if any trailer features are active. + pub frame_metadata: Option, + pub buffer: T, +} + +impl> VideoFrame { + pub fn new(rotation: VideoRotation, buffer: T) -> Self { + Self { rotation, timestamp_us: 0, frame_metadata: None, buffer } + } +} + +pub type BoxVideoBuffer = Box; +pub type BoxVideoFrame = VideoFrame; + +pub(crate) mod internal { + use super::{I420Buffer, VideoFormatType}; + + pub trait BufferSealed: Send + Sync { + #[cfg(not(target_arch = "wasm32"))] + fn sys_handle(&self) -> &webrtc_sys::video_frame_buffer::ffi::VideoFrameBuffer; + + #[cfg(not(target_arch = "wasm32"))] + fn to_i420(&self) -> I420Buffer; + + #[cfg(not(target_arch = "wasm32"))] + fn to_argb( + &self, + format: VideoFormatType, + dst: &mut [u8], + dst_stride: u32, + dst_width: i32, + dst_height: i32, + ); + } +} + +pub trait VideoBuffer: internal::BufferSealed + Debug { + fn width(&self) -> u32; + fn height(&self) -> u32; + fn buffer_type(&self) -> VideoBufferType; + + #[cfg(not(target_arch = "wasm32"))] + fn as_native(&self) -> Option<&native::NativeBuffer> { + None + } + + fn as_i420(&self) -> Option<&I420Buffer> { + None + } + + fn as_i420a(&self) -> Option<&I420ABuffer> { + None + } + + fn as_i422(&self) -> Option<&I422Buffer> { + None + } + + fn as_i444(&self) -> Option<&I444Buffer> { + None + } + + fn as_i010(&self) -> Option<&I010Buffer> { + None + } + + fn as_nv12(&self) -> Option<&NV12Buffer> { + None + } +} + +macro_rules! new_buffer_type { + ($type:ident, $variant:ident, $as:ident) => { + pub struct $type { + pub(crate) handle: vf_imp::$type, + } + + impl $crate::video_frame::internal::BufferSealed for $type { + #[cfg(not(target_arch = "wasm32"))] + fn sys_handle(&self) -> &webrtc_sys::video_frame_buffer::ffi::VideoFrameBuffer { + self.handle.sys_handle() + } + + #[cfg(not(target_arch = "wasm32"))] + fn to_i420(&self) -> I420Buffer { + I420Buffer { handle: self.handle.to_i420() } + } + + #[cfg(not(target_arch = "wasm32"))] + fn to_argb( + &self, + format: VideoFormatType, + dst: &mut [u8], + stride: u32, + width: i32, + height: i32, + ) { + self.handle.to_argb(format, dst, stride, width, height) + } + } + + impl VideoBuffer for $type { + fn width(&self) -> u32 { + self.handle.width() + } + + fn height(&self) -> u32 { + self.handle.height() + } + + fn buffer_type(&self) -> VideoBufferType { + VideoBufferType::$variant + } + + fn $as(&self) -> Option<&$type> { + Some(self) + } + } + + impl Debug for $type { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct(stringify!($type)) + .field("width", &self.width()) + .field("height", &self.height()) + .finish() + } + } + + impl AsRef for $type { + fn as_ref(&self) -> &(dyn VideoBuffer + 'static) { + self + } + } + }; +} + +new_buffer_type!(I420Buffer, I420, as_i420); +new_buffer_type!(I420ABuffer, I420A, as_i420a); +new_buffer_type!(I422Buffer, I422, as_i422); +new_buffer_type!(I444Buffer, I444, as_i444); +new_buffer_type!(I010Buffer, I010, as_i010); +new_buffer_type!(NV12Buffer, NV12, as_nv12); + +impl I420Buffer { + pub fn with_strides( + width: u32, + height: u32, + stride_y: u32, + stride_u: u32, + stride_v: u32, + ) -> I420Buffer { + vf_imp::I420Buffer::new(width, height, stride_y, stride_u, stride_v) + } + + pub fn new(width: u32, height: u32) -> I420Buffer { + Self::with_strides(width, height, width, (width + 1) / 2, (width + 1) / 2) + } + + pub fn chroma_width(&self) -> u32 { + self.handle.chroma_width() + } + + pub fn chroma_height(&self) -> u32 { + self.handle.chroma_height() + } + + pub fn strides(&self) -> (u32, u32, u32) { + (self.handle.stride_y(), self.handle.stride_u(), self.handle.stride_v()) + } + + pub fn data(&self) -> (&[u8], &[u8], &[u8]) { + self.handle.data() + } + + pub fn data_mut(&mut self) -> (&mut [u8], &mut [u8], &mut [u8]) { + let (data_y, data_u, data_v) = self.handle.data(); + unsafe { + ( + std::slice::from_raw_parts_mut(data_y.as_ptr() as *mut u8, data_y.len()), + std::slice::from_raw_parts_mut(data_u.as_ptr() as *mut u8, data_u.len()), + std::slice::from_raw_parts_mut(data_v.as_ptr() as *mut u8, data_v.len()), + ) + } + } + + pub fn scale(&mut self, scaled_width: i32, scaled_height: i32) -> I420Buffer { + self.handle.scale(scaled_width, scaled_height) + } +} + +impl I420ABuffer { + pub fn chroma_width(&self) -> u32 { + self.handle.chroma_width() + } + + pub fn chroma_height(&self) -> u32 { + self.handle.chroma_height() + } + + pub fn strides(&self) -> (u32, u32, u32, u32) { + ( + self.handle.stride_y(), + self.handle.stride_u(), + self.handle.stride_v(), + self.handle.stride_a(), + ) + } + + #[allow(clippy::type_complexity)] + pub fn data(&self) -> (&[u8], &[u8], &[u8], Option<&[u8]>) { + self.handle.data() + } + + #[allow(clippy::type_complexity)] + pub fn data_mut(&self) -> (&mut [u8], &mut [u8], &mut [u8], Option<&mut [u8]>) { + let (data_y, data_u, data_v, data_a) = self.handle.data(); + unsafe { + ( + std::slice::from_raw_parts_mut(data_y.as_ptr() as *mut u8, data_y.len()), + std::slice::from_raw_parts_mut(data_u.as_ptr() as *mut u8, data_u.len()), + std::slice::from_raw_parts_mut(data_v.as_ptr() as *mut u8, data_v.len()), + data_a.map(|data_a| { + std::slice::from_raw_parts_mut(data_a.as_ptr() as *mut u8, data_a.len()) + }), + ) + } + } + + pub fn scale(&mut self, scaled_width: i32, scaled_height: i32) -> I420ABuffer { + self.handle.scale(scaled_width, scaled_height) + } +} + +impl I422Buffer { + pub fn with_strides( + width: u32, + height: u32, + stride_y: u32, + stride_u: u32, + stride_v: u32, + ) -> I422Buffer { + vf_imp::I422Buffer::new(width, height, stride_y, stride_u, stride_v) + } + + pub fn new(width: u32, height: u32) -> I422Buffer { + Self::with_strides(width, height, width, (width + 1) / 2, (width + 1) / 2) + } + + pub fn chroma_width(&self) -> u32 { + self.handle.chroma_width() + } + + pub fn chroma_height(&self) -> u32 { + self.handle.chroma_height() + } + + pub fn strides(&self) -> (u32, u32, u32) { + (self.handle.stride_y(), self.handle.stride_u(), self.handle.stride_v()) + } + + pub fn data(&self) -> (&[u8], &[u8], &[u8]) { + self.handle.data() + } + + pub fn data_mut(&mut self) -> (&mut [u8], &mut [u8], &mut [u8]) { + let (data_y, data_u, data_v) = self.handle.data(); + unsafe { + ( + std::slice::from_raw_parts_mut(data_y.as_ptr() as *mut u8, data_y.len()), + std::slice::from_raw_parts_mut(data_u.as_ptr() as *mut u8, data_u.len()), + std::slice::from_raw_parts_mut(data_v.as_ptr() as *mut u8, data_v.len()), + ) + } + } + + pub fn scale(&mut self, scaled_width: i32, scaled_height: i32) -> I422Buffer { + self.handle.scale(scaled_width, scaled_height) + } +} + +impl I444Buffer { + pub fn with_strides( + width: u32, + height: u32, + stride_y: u32, + stride_u: u32, + stride_v: u32, + ) -> I444Buffer { + vf_imp::I444Buffer::new(width, height, stride_y, stride_u, stride_v) + } + + pub fn new(width: u32, height: u32) -> I444Buffer { + Self::with_strides(width, height, width, width, width) + } + + pub fn chroma_width(&self) -> u32 { + self.handle.chroma_width() + } + + pub fn chroma_height(&self) -> u32 { + self.handle.chroma_height() + } + + pub fn strides(&self) -> (u32, u32, u32) { + (self.handle.stride_y(), self.handle.stride_u(), self.handle.stride_v()) + } + + pub fn data(&self) -> (&[u8], &[u8], &[u8]) { + self.handle.data() + } + + pub fn data_mut(&mut self) -> (&mut [u8], &mut [u8], &mut [u8]) { + let (data_y, data_u, data_v) = self.handle.data(); + unsafe { + ( + std::slice::from_raw_parts_mut(data_y.as_ptr() as *mut u8, data_y.len()), + std::slice::from_raw_parts_mut(data_u.as_ptr() as *mut u8, data_u.len()), + std::slice::from_raw_parts_mut(data_v.as_ptr() as *mut u8, data_v.len()), + ) + } + } + + pub fn scale(&mut self, scaled_width: i32, scaled_height: i32) -> I444Buffer { + self.handle.scale(scaled_width, scaled_height) + } +} + +impl I010Buffer { + pub fn with_strides( + width: u32, + height: u32, + stride_y: u32, + stride_u: u32, + stride_v: u32, + ) -> I010Buffer { + vf_imp::I010Buffer::new(width, height, stride_y, stride_u, stride_v) + } + + pub fn new(width: u32, height: u32) -> I010Buffer { + Self::with_strides(width, height, width, (width + 1) / 2, (width + 1) / 2) + } + + pub fn chroma_width(&self) -> u32 { + self.handle.chroma_width() + } + + pub fn chroma_height(&self) -> u32 { + self.handle.chroma_height() + } + + pub fn strides(&self) -> (u32, u32, u32) { + (self.handle.stride_y(), self.handle.stride_u(), self.handle.stride_v()) + } + + pub fn data(&self) -> (&[u16], &[u16], &[u16]) { + self.handle.data() + } + + pub fn data_mut(&mut self) -> (&mut [u16], &mut [u16], &mut [u16]) { + let (data_y, data_u, data_v) = self.handle.data(); + unsafe { + ( + std::slice::from_raw_parts_mut(data_y.as_ptr() as *mut u16, data_y.len()), + std::slice::from_raw_parts_mut(data_u.as_ptr() as *mut u16, data_u.len()), + std::slice::from_raw_parts_mut(data_v.as_ptr() as *mut u16, data_v.len()), + ) + } + } + + pub fn scale(&mut self, scaled_width: i32, scaled_height: i32) -> I010Buffer { + self.handle.scale(scaled_width, scaled_height) + } +} + +impl NV12Buffer { + pub fn with_strides(width: u32, height: u32, stride_y: u32, stride_uv: u32) -> NV12Buffer { + vf_imp::NV12Buffer::new(width, height, stride_y, stride_uv) + } + + pub fn new(width: u32, height: u32) -> NV12Buffer { + Self::with_strides(width, height, width, width + width % 2) + } + + pub fn chroma_width(&self) -> u32 { + self.handle.chroma_width() + } + + pub fn chroma_height(&self) -> u32 { + self.handle.chroma_height() + } + + pub fn strides(&self) -> (u32, u32) { + (self.handle.stride_y(), self.handle.stride_uv()) + } + + pub fn data(&self) -> (&[u8], &[u8]) { + self.handle.data() + } + + pub fn data_mut(&mut self) -> (&mut [u8], &mut [u8]) { + let (data_y, data_uv) = self.handle.data(); + unsafe { + ( + std::slice::from_raw_parts_mut(data_y.as_ptr() as *mut u8, data_y.len()), + std::slice::from_raw_parts_mut(data_uv.as_ptr() as *mut u8, data_uv.len()), + ) + } + } + + pub fn scale(&mut self, scaled_width: i32, scaled_height: i32) -> NV12Buffer { + self.handle.scale(scaled_width, scaled_height) + } +} + +#[cfg(not(target_arch = "wasm32"))] +pub mod native { + use std::fmt::Debug; + + use super::{vf_imp, I420Buffer, VideoBuffer, VideoBufferType, VideoFormatType}; + + new_buffer_type!(NativeBuffer, Native, as_native); + + impl NativeBuffer { + pub fn from_fluxer_d3d11_texture( + handle: u64, + width: u32, + height: u32, + dxgi_format: u32, + ) -> Option { + vf_imp::NativeBuffer::from_fluxer_d3d11_texture( + handle, + width, + height, + dxgi_format, + ) + } + + #[allow(clippy::too_many_arguments)] + pub fn from_fluxer_dmabuf_texture( + fds: [i32; 4], + plane_count: u32, + width: u32, + height: u32, + drm_format: u32, + modifier: u64, + strides: [u32; 4], + offsets: [u32; 4], + device_uuid_hi: u64, + device_uuid_lo: u64, + ) -> Option { + vf_imp::NativeBuffer::from_fluxer_dmabuf_texture( + fds, + plane_count, + width, + height, + drm_format, + modifier, + strides, + offsets, + device_uuid_hi, + device_uuid_lo, + ) + } + + /// Creates a `NativeBuffer` from a `CVPixelBufferRef` pointer. + /// + /// This function does not bump the reference count of the pixel buffer. + /// + /// Safety: The given pointer must be a valid `CVPixelBufferRef`. + #[cfg(any(target_os = "macos", target_os = "ios"))] + pub unsafe fn from_cv_pixel_buffer(cv_pixel_buffer: *mut std::ffi::c_void) -> Self { + vf_imp::NativeBuffer::from_cv_pixel_buffer(cv_pixel_buffer) + } + + /// Returns the `CVPixelBufferRef` that backs this buffer, or `null` if + /// this buffer is not currently backed by a `CVPixelBufferRef`. + /// + /// This function does not bump the reference count of the pixel buffer. + #[cfg(any(target_os = "macos", target_os = "ios"))] + pub fn get_cv_pixel_buffer(&self) -> *mut std::ffi::c_void { + self.handle.get_cv_pixel_buffer() + } + } + + pub trait VideoFrameBufferExt: VideoBuffer { + fn to_i420(&self) -> I420Buffer; + fn to_argb( + &self, + format: VideoFormatType, + dst: &mut [u8], + dst_stride: u32, + dst_width: i32, + dst_height: i32, + ); + } + + impl VideoFrameBufferExt for T { + fn to_i420(&self) -> I420Buffer { + self.to_i420() + } + + fn to_argb( + &self, + format: VideoFormatType, + dst: &mut [u8], + dst_stride: u32, + dst_width: i32, + dst_height: i32, + ) { + self.to_argb(format, dst, dst_stride, dst_width, dst_height) + } + } +} + +#[cfg(target_arch = "wasm32")] +pub mod web { + use super::VideoFrameBuffer; + + #[derive(Debug)] + pub struct WebGlBuffer {} + + impl VideoFrameBuffer for WebGlBuffer {} +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/video_source.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/video_source.rs new file mode 100644 index 0000000..0a71d00 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/video_source.rs @@ -0,0 +1,97 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::{enum_dispatch, imp::video_source as vs_imp}; + +#[derive(Debug, Clone)] +pub struct VideoResolution { + pub width: u32, + pub height: u32, +} + +impl Default for VideoResolution { + // Default to 720p + fn default() -> Self { + VideoResolution { width: 1280, height: 720 } + } +} + +#[non_exhaustive] +#[derive(Debug, Clone)] +pub enum RtcVideoSource { + // TODO(theomonnom): Web video sources (eq. to tracks on browsers?) + #[cfg(not(target_arch = "wasm32"))] + Native(native::NativeVideoSource), +} + +// TODO(theomonnom): Support enum dispatch with conditional compilation? +impl RtcVideoSource { + enum_dispatch!( + [Native]; + pub fn video_resolution(self: &Self) -> VideoResolution; + ); +} + +#[cfg(not(target_arch = "wasm32"))] +pub mod native { + use std::fmt::{Debug, Formatter}; + + use super::*; + use crate::native::packet_trailer::PacketTrailerHandler; + use crate::video_frame::{VideoBuffer, VideoFrame}; + + #[derive(Clone)] + pub struct NativeVideoSource { + pub(crate) handle: vs_imp::NativeVideoSource, + } + + impl Debug for NativeVideoSource { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + f.debug_struct("NativeVideoSource").finish() + } + } + + impl Default for NativeVideoSource { + fn default() -> Self { + Self::new(VideoResolution::default(), false) + } + } + + impl NativeVideoSource { + pub fn new(resolution: VideoResolution, is_screencast: bool) -> Self { + Self { handle: vs_imp::NativeVideoSource::new(resolution, is_screencast) } + } + + pub fn capture_frame>(&self, frame: &VideoFrame) { + self.handle.capture_frame(frame) + } + + /// Set the packet trailer handler used by this source. + /// + /// When set, any frame captured with a `user_timestamp` value will + /// automatically have its timestamp stored in the handler (keyed by + /// the TimestampAligner-adjusted capture timestamp) so the + /// `PacketTrailerTransformer` can embed it into the encoded frame. + pub fn set_packet_trailer_handler(&self, handler: PacketTrailerHandler) { + self.handle.set_packet_trailer_handler(handler) + } + + pub fn video_resolution(&self) -> VideoResolution { + self.handle.video_resolution() + } + } +} + +#[cfg(target_arch = "wasm32")] +pub mod web {} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/video_stream.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/video_stream.rs new file mode 100644 index 0000000..813304f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/video_stream.rs @@ -0,0 +1,123 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::imp::video_stream as stream_imp; + +// There is no shared sink between native and web platforms. +// Each platform requires different configuration (e.g: WebGlContext, ..) + +#[cfg(not(target_arch = "wasm32"))] +pub mod native { + use std::{ + fmt::{Debug, Formatter}, + pin::Pin, + task::{Context, Poll}, + }; + + use super::stream_imp; + use crate::{ + native::packet_trailer::PacketTrailerHandler, video_frame::BoxVideoFrame, + video_track::RtcVideoTrack, + }; + use livekit_runtime::Stream; + + const DEFAULT_QUEUE_SIZE_FRAMES: usize = 1; + + #[derive(Clone, Debug, Default)] + pub struct NativeVideoStreamOptions { + /// Maximum number of queued WebRTC sink frames after the video callback. + /// + /// `None` uses the default bounded queue size of 1 frame. `Some(0)` + /// opts into unbounded buffering. Positive values bound the queue, and + /// the stream drops the oldest queued frames on overflow so render + /// latency stays bounded. + /// + /// If your application consumes both audio and video, keep the queue + /// sizing strategy coordinated across both streams. Using a much larger + /// queue, or unbounded buffering, for only one of them can increase + /// end-to-end latency for that stream and cause audio/video drift. + pub queue_size_frames: Option, + } + + pub struct NativeVideoStream { + pub(crate) handle: stream_imp::NativeVideoStream, + } + + impl Debug for NativeVideoStream { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + f.debug_struct("NativeVideoStream").field("track", &self.track()).finish() + } + } + + impl NativeVideoStream { + pub fn new(video_track: RtcVideoTrack) -> Self { + Self { + handle: stream_imp::NativeVideoStream::new( + video_track, + Some(DEFAULT_QUEUE_SIZE_FRAMES), + ), + } + } + + pub fn with_options(video_track: RtcVideoTrack, options: NativeVideoStreamOptions) -> Self { + Self { + handle: stream_imp::NativeVideoStream::new( + video_track, + normalize_queue_size_frames(options.queue_size_frames), + ), + } + } + + /// Set the packet trailer handler for this stream. + /// + /// When set, each frame produced by this stream will have its + /// `user_timestamp` field populated by looking up the user + /// timestamp for each frame's RTP timestamp. + /// + /// Note: If the handler was already set on the `RtcVideoTrack` + /// before creating this stream, it is automatically wired up. + /// This method is only needed to override or set the handler + /// after construction. + pub fn set_packet_trailer_handler(&self, handler: PacketTrailerHandler) { + self.handle.set_packet_trailer_handler(handler); + } + + pub fn track(&self) -> RtcVideoTrack { + self.handle.track() + } + + pub fn close(&mut self) { + self.handle.close(); + } + } + + impl Stream for NativeVideoStream { + type Item = BoxVideoFrame; + + fn poll_next(self: Pin<&mut Self>, cx: &mut Context) -> Poll> { + Pin::new(&mut self.get_mut().handle).poll_next(cx) + } + } + + fn normalize_queue_size_frames(queue_size_frames: Option) -> Option { + match queue_size_frames { + None => Some(DEFAULT_QUEUE_SIZE_FRAMES), + Some(0) => None, + Some(value) => Some(value), + } + } +} + +#[cfg(target_arch = "wasm32")] +pub mod web {} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/video_track.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/video_track.rs new file mode 100644 index 0000000..c9deb51 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/video_track.rs @@ -0,0 +1,58 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::fmt::Debug; + +use crate::{ + imp::video_track as imp_vt, + media_stream_track::{media_stream_track, RtcTrackState}, +}; + +#[cfg(not(target_arch = "wasm32"))] +use crate::native::packet_trailer::PacketTrailerHandler; + +#[derive(Clone)] +pub struct RtcVideoTrack { + pub(crate) handle: imp_vt::RtcVideoTrack, +} + +impl RtcVideoTrack { + media_stream_track!(); + + /// Set the packet trailer handler for this track. + /// + /// When set, any `NativeVideoStream` created from this track will + /// automatically use this handler to populate `user_timestamp` + /// on each decoded frame. + #[cfg(not(target_arch = "wasm32"))] + pub fn set_packet_trailer_handler(&self, handler: PacketTrailerHandler) { + self.handle.set_packet_trailer_handler(handler); + } + + /// Get the packet trailer handler, if one has been set. + #[cfg(not(target_arch = "wasm32"))] + pub fn packet_trailer_handler(&self) -> Option { + self.handle.packet_trailer_handler() + } +} + +impl Debug for RtcVideoTrack { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("RtcVideoTrack") + .field("id", &self.id()) + .field("enabled", &self.enabled()) + .field("state", &self.state()) + .finish() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/web/data_channel.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/web/data_channel.rs new file mode 100644 index 0000000..7be72b4 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/web/data_channel.rs @@ -0,0 +1,117 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use core::str; +use std::cell::RefCell; +use std::rc::Rc; +use wasm_bindgen::prelude::*; +use web_sys::{MessageEvent, RtcDataChannelEvent, RtcDataChannelState}; + +use crate::data_channel::{ + DataChannelError, DataChannelTrait, DataState, OnBufferedAmountChange, OnMessage, OnStateChange, +}; + +impl From for DataState { + fn from(value: RtcDataChannelState) -> Self { + match value { + RtcDataChannelState::Connecting => Self::Connecting, + RtcDataChannelState::Open => Self::Open, + RtcDataChannelState::Closing => Self::Closing, + RtcDataChannelState::Closed => Self::Closed, + _ => panic!("unknown data channel state"), + } + } +} + +#[derive(Clone)] +pub struct DataChannel { + sys_handle: web_sys::RtcDataChannel, + on_closing: Rc>>, +} + +impl DataChannelTrait for DataChannel { + fn send(&self, data: &[u8], binary: bool) -> Result<(), DataChannelError> { + if binary { + self.sys_handle + .send_with_u8_array(data) + .map_err(|_| DataChannelError::Send) + } else { + let utf8 = str::from_utf8(data)?; + self.sys_handle + .send_with_str(utf8) + .map_err(|_| DataChannelError::Send) + } + } + + fn label(&self) -> String { + self.sys_handle.label() + } + + fn state(&self) -> DataState { + self.sys_handle.ready_state().into() + } + + fn close(&self) { + self.sys_handle.close(); + } + + fn on_state_change(&self, callback: Option) { + if let Some(mut callback) = callback { + let dc = self.clone(); + let js_callback = Closure::new(move |_: RtcDataChannelEvent| { + callback(dc.state()); + }); + let js_callback = js_callback.into_js_value(); + self.sys_handle + .set_onopen(Some(js_callback.unchecked_ref())); + self.sys_handle + .set_onclose(Some(js_callback.unchecked_ref())); + self.sys_handle + .add_event_listener_with_callback("closing", js_callback.unchecked_ref()) + .unwrap(); + + self.on_closing.replace(Some(js_callback)); + } else { + self.sys_handle.set_onopen(None); + self.sys_handle.set_onclose(None); + if let Some(on_closing) = self.on_closing.take() { + self.sys_handle + .remove_event_listener_with_callback("closing", on_closing.unchecked_ref()) + .unwrap(); + } + self.on_closing.replace(None); + } + } + + fn on_message(&self, callback: Option) { + let js_callback = callback.map(|mut callback| { + Closure::new(move |event: MessageEvent| { + if let Some(str) = event.as_string() { + callback(str.as_bytes(), false); + } + }) + .into_js_value() + }); + + self.sys_handle.set_onmessage( + js_callback + .as_ref() + .map(|callback| callback.unchecked_ref()), + ); + } + + fn on_buffered_amount_change(&self, _callback: Option) { + todo!("onbufferedamountlow instead?") + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/web/media_stream.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/web/media_stream.rs new file mode 100644 index 0000000..a2b5995 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/web/media_stream.rs @@ -0,0 +1,372 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use super::VideoTrack; +use crate::{ + media_stream::{ + BiplanarYuv8Buffer, BiplanarYuvBuffer, PlanarYuv16BBuffer, PlanarYuv8Buffer, + PlanarYuvBuffer, VideoFrameBuffer, + }, + video_frame::{BiplanarYuv8Buffer, I420Buffer, SinkError, VideoFrame, VideoFrameBuffer}, + I010Buffer, I420ABuffer, I422Buffer, I444Buffer, NV12Buffer, +}; +use std::sync::mpsc; +use web_sys::{WebGlRenderingContext, WebGlTexture}; + +#[derive(Debug)] +pub struct WebGlVideoSink { + track: Arc, + gl_ctx: WebGlRenderingContext, + tex: WebGlTexture, +} + +/// Create a new WebGL texture and update it inside requestAnimationFrame +impl WebGlVideoSink { + pub fn new( + track: Arc, + gl_ctx: WebGlRenderingContext, + ) -> Result<(Self, mpsc::Receiver>), SinkError> { + let (sender, receiver) = mpsc::channel(); + let tex = gl_ctx.create_texture()?; + Ok((Self { track, gl_ctx, tex }, receiver)) + } +} + +#[derive(Debug, Clone)] +pub struct WebGlBuffer { + width: i32, + height: i32, + tex: WebGlTexture, +} + +impl VideoFrameBuffer for WebGlBuffer { + fn width(&self) -> i32 { + self.width + } + + fn height(&self) -> i32 { + self.height + } +} + +/// The following types could be implemented if we want +/// to support VideoFrame with WebCodecs + +#[derive(Debug)] +pub struct I420Buffer {} + +#[derive(Debug)] +pub struct I420ABuffer {} + +#[derive(Debug)] +pub struct I422Buffer {} + +#[derive(Debug)] +pub struct I444Buffer {} + +#[derive(Debug)] +pub struct I010Buffer {} + +#[derive(Debug)] +pub struct NV12Buffer {} + +impl VideoFrameBuffer for I420Buffer { + fn width(&self) -> i32 { + unimplemented!() + } + + fn height(&self) -> i32 { + unimplemented!() + } +} + +impl VideoFrameBuffer for I420ABuffer { + fn width(&self) -> i32 { + unimplemented!() + } + + fn height(&self) -> i32 { + unimplemented!() + } +} + +impl VideoFrameBuffer for I422Buffer { + fn width(&self) -> i32 { + unimplemented!() + } + + fn height(&self) -> i32 { + unimplemented!() + } +} + +impl VideoFrameBuffer for I444Buffer { + fn width(&self) -> i32 { + unimplemented!() + } + + fn height(&self) -> i32 { + unimplemented!() + } +} + +impl VideoFrameBuffer for I010Buffer { + fn width(&self) -> i32 { + unimplemented!() + } + + fn height(&self) -> i32 { + unimplemented!() + } +} + +impl VideoFrameBuffer for NV12Buffer { + fn width(&self) -> i32 { + unimplemented!() + } + + fn height(&self) -> i32 { + unimplemented!() + } +} + +impl PlanarYuvBuffer for I420Buffer { + fn chroma_width(&self) -> i32 { + unimplemented!() + } + + fn chroma_height(&self) -> i32 { + unimplemented!() + } + + fn stride_y(&self) -> i32 { + unimplemented!() + } + + fn stride_u(&self) -> i32 { + unimplemented!() + } + + fn stride_v(&self) -> i32 { + unimplemented!() + } +} + +impl PlanarYuvBuffer for I420ABuffer { + fn chroma_width(&self) -> i32 { + unimplemented!() + } + + fn chroma_height(&self) -> i32 { + unimplemented!() + } + + fn stride_y(&self) -> i32 { + unimplemented!() + } + + fn stride_u(&self) -> i32 { + unimplemented!() + } + + fn stride_v(&self) -> i32 { + unimplemented!() + } +} + +impl PlanarYuvBuffer for I422Buffer { + fn chroma_width(&self) -> i32 { + unimplemented!() + } + + fn chroma_height(&self) -> i32 { + unimplemented!() + } + + fn stride_y(&self) -> i32 { + unimplemented!() + } + + fn stride_u(&self) -> i32 { + unimplemented!() + } + + fn stride_v(&self) -> i32 { + unimplemented!() + } +} + +impl PlanarYuvBuffer for I444Buffer { + fn chroma_width(&self) -> i32 { + unimplemented!() + } + + fn chroma_height(&self) -> i32 { + unimplemented!() + } + + fn stride_y(&self) -> i32 { + unimplemented!() + } + + fn stride_u(&self) -> i32 { + unimplemented!() + } + + fn stride_v(&self) -> i32 { + unimplemented!() + } +} + +impl PlanarYuvBuffer for I010Buffer { + fn chroma_width(&self) -> i32 { + unimplemented!() + } + + fn chroma_height(&self) -> i32 { + unimplemented!() + } + + fn stride_y(&self) -> i32 { + unimplemented!() + } + + fn stride_u(&self) -> i32 { + unimplemented!() + } + + fn stride_v(&self) -> i32 { + unimplemented!() + } +} + +impl PlanarYuvBuffer for NV12Buffer { + fn chroma_width(&self) -> i32 { + unimplemented!() + } + + fn chroma_height(&self) -> i32 { + unimplemented!() + } + + fn stride_y(&self) -> i32 { + unimplemented!() + } + + fn stride_u(&self) -> i32 { + unimplemented!() + } + + fn stride_v(&self) -> i32 { + unimplemented!() + } +} + +impl PlanarYuv8Buffer for I420Buffer { + fn data_y(&self) -> &[u8] { + unimplemented!() + } + + fn data_u(&self) -> &[u8] { + unimplemented!() + } + + fn data_v(&self) -> &[u8] { + unimplemented!() + } +} + +impl PlanarYuv8Buffer for I420ABuffer { + fn data_y(&self) -> &[u8] { + unimplemented!() + } + + fn data_u(&self) -> &[u8] { + unimplemented!() + } + + fn data_v(&self) -> &[u8] { + unimplemented!() + } +} + +impl PlanarYuv8Buffer for I422Buffer { + fn data_y(&self) -> &[u8] { + unimplemented!() + } + + fn data_u(&self) -> &[u8] { + unimplemented!() + } + + fn data_v(&self) -> &[u8] { + unimplemented!() + } +} + +impl PlanarYuv8Buffer for I444Buffer { + fn data_y(&self) -> &[u8] { + unimplemented!() + } + + fn data_u(&self) -> &[u8] { + unimplemented!() + } + + fn data_v(&self) -> &[u8] { + unimplemented!() + } +} + +impl PlanarYuv16BBuffer for I010Buffer { + fn data_y(&self) -> &[u16] { + unimplemented!() + } + + fn data_u(&self) -> &[u16] { + unimplemented!() + } + + fn data_v(&self) -> &[u16] { + unimplemented!() + } +} + +impl BiplanarYuvBuffer for NV12Buffer { + fn chroma_width(&self) -> i32 { + unimplemented!() + } + + fn chroma_height(&self) -> i32 { + unimplemented!() + } + + fn stride_y(&self) -> i32 { + unimplemented!() + } + + fn stride_uv(&self) -> i32 { + unimplemented!() + } +} + +impl BiplanarYuv8Buffer for NV12Buffer { + fn data_y(&self) -> &[u8] { + unimplemented!() + } + + fn data_uv(&self) -> &[u8] { + unimplemented!() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/web/mod.rs b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/web/mod.rs new file mode 100644 index 0000000..5deafe2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/libwebrtc/src/web/mod.rs @@ -0,0 +1,15 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +unimplemented!(); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/Cargo.toml b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/Cargo.toml new file mode 100644 index 0000000..18b120c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/Cargo.toml @@ -0,0 +1,148 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +name = "tract-linalg" +version = "0.19.16" +authors = ["Mathieu Poumeyrol "] +autobenches = false +description = "Tiny, no-nonsense, self contained, TensorFlow and ONNX inference" +readme = "README.md" +keywords = [ + "TensorFlow", + "NeuralNetworks", +] +categories = ["science"] +license = "MIT/Apache-2.0" +repository = "https://github.com/snipsco/tract" +resolver = "1" + +[[bench]] +name = "arm64" +bench = false +harness = false + +[[bench]] +name = "mat_vec" +harness = false + +[[bench]] +name = "mm_for_wavenet_hw" +harness = false + +[[bench]] +name = "conv_for_wavenet_hw" +harness = false + +[[bench]] +name = "mm_for_inception" +harness = false + +[[bench]] +name = "mm_for_asr_am" +harness = false + +[[bench]] +name = "sigmoid" +harness = false + +[[bench]] +name = "arm64simd" +bench = false +harness = false + +[[bench]] +name = "arm32neon" +bench = false +harness = false + +[[bench]] +name = "packing" +bench = false +harness = false + +[[bench]] +name = "virtual_im2col" +harness = false + +[[bench]] +name = "x86_64" +bench = false +harness = false + +[dependencies.derive-new] +version = "0.5.9" + +[dependencies.downcast-rs] +version = "1.2.0" + +[dependencies.dyn-clone] +version = "1.0.4" + +[dependencies.lazy_static] +version = "1.4.0" + +[dependencies.log] +version = "0.4.14" + +[dependencies.num-traits] +version = "0.2.14" + +[dependencies.paste] +version = "1.0.5" + +[dependencies.scan_fmt] +version = "0.2.6" + +[dependencies.tract-data] +version = "=0.19.16" + +[dev-dependencies.criterion] +version = "0.4" + +[dev-dependencies.nu-ansi-term] +version = "0.46" + +[dev-dependencies.proptest] +version = "1.0.0" + +[build-dependencies.cc] +version = "1.0.69" + +[build-dependencies.half] +version = "2" +features = [ + "std", + "num-traits", +] + +[build-dependencies.liquid] +version = "0.26" + +[build-dependencies.liquid-core] +version = "0.26" + +[build-dependencies.smallvec] +version = "1.6.1" + +[build-dependencies.unicode-normalization] +version = "0.1.19" + +[build-dependencies.walkdir] +version = "2.3.2" + +[features] +default = [] +no_fp16 = [] + +[badges.maintenance] +status = "actively-developed" diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/Cargo.toml.orig b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/Cargo.toml.orig new file mode 100644 index 0000000..2588350 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/Cargo.toml.orig @@ -0,0 +1,100 @@ +[package] +name = "tract-linalg" +version = "0.19.16" +license = "MIT/Apache-2.0" +authors = ["Mathieu Poumeyrol "] +description = "Tiny, no-nonsense, self contained, TensorFlow and ONNX inference" +repository = "https://github.com/snipsco/tract" +keywords = ["TensorFlow", "NeuralNetworks"] +categories = ["science"] +autobenches = false +edition = "2021" + +[badges] +maintenance = { status = "actively-developed" } + +[dependencies] +derive-new.workspace = true +downcast-rs.workspace = true +dyn-clone.workspace = true +lazy_static.workspace = true +log.workspace = true +num-traits.workspace = true +paste.workspace = true +scan_fmt.workspace = true +tract-data = { version = "=0.19.16", path = "../data" } + +[build-dependencies] +cc.workspace = true +liquid.workspace = true +liquid-core.workspace = true +unicode-normalization.workspace = true +smallvec.workspace = true +walkdir.workspace = true +half.workspace = true + +[dev-dependencies] +criterion.workspace = true +nu-ansi-term.workspace = true +proptest.workspace = true + +[features] +# This feature is meant to accomodate very restrictive / legacy toolchains that do +# have support for fp16 instructions, breaking tract compilation. +# It is not meant to be used in other situations, where run-time detection is +# preferred. +no_fp16 = [] +default = [] + +[[bench]] +bench = false +name = "arm64" +harness = false + +[[bench]] +name = "mat_vec" +harness = false + +[[bench]] +name = "mm_for_wavenet_hw" +harness = false + +[[bench]] +name = "conv_for_wavenet_hw" +harness = false + +[[bench]] +name = "mm_for_inception" +harness = false + +[[bench]] +name = "mm_for_asr_am" +harness = false + +[[bench]] +name = "sigmoid" +harness = false + +[[bench]] +bench = false +name = "arm64simd" +harness = false + +[[bench]] +bench = false +name = "arm32neon" +harness = false + +[[bench]] +bench = false +name = "packing" +harness = false + +[[bench]] +name = "virtual_im2col" +harness = false + +[[bench]] +bench = false +name = "x86_64" +harness = false diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/LICENSE b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/LICENSE new file mode 100644 index 0000000..09250ca --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/LICENSE @@ -0,0 +1,12 @@ +## License + +Licensed under either of + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/LICENSE-APACHE b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/LICENSE-MIT b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/LICENSE-MIT @@ -0,0 +1,23 @@ +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. diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/README.md b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/README.md new file mode 100644 index 0000000..ba7b722 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/README.md @@ -0,0 +1,27 @@ +# tract-linalg + +linalg stands for "linear algebra". This is a misnamer. This crates contains +low-level, architecture dependant optimisations used by tract-core. + +# Functions + +* MatMatMul: Extended matrix*matrix product: + * inspired by Gotoblass and BLIS micro kernel approach + * extended for convolution friendly addressing (fused img2col) + * fused output pipeline (min, max, and a few more simple, fast ops) + * f32*f32 -> f32 (à la sgemm) + * i8*i8 -> i32 accumulator -> i32 storage + * i8*i8 -> i32 accumulator -> i8 (with channel zeropoint and scale, and re-quantization pipeline) +* f32 sigmoid and f32 tanh: at f32 precision, by a rationale function (no exponentiation) +* byte-to-byte lookup table + +# Implementations + +| | generic fallback | armv6, vfp | armv7 neon | armv8 simd | x64 FMA +|-------------------|--------------------|---------------|-------------------|-------------------|----------------- +| MatMatMul f32 | | 4x4 | 8x4 | 8x8 | 16x6 +| MatMatMul i8->i8 | | | 8x4 | | 8x8 +| MatMatMul i8->i32 | | | | | 8x8 +| sigmoid f32 | | | 4n | 4n | +| tanh f32 | | | 4n | 4n | +| byte lookup | | | | | diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_f32_32x1_core.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_f32_32x1_core.tmpl new file mode 100644 index 0000000..e11e050 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_f32_32x1_core.tmpl @@ -0,0 +1,200 @@ +// vim: ft=arm + +// C tile regs +// +// q8[0] +// q8[1] +// q8[2] +// q8[3] +// +// .... +// +// q15[0] +// q15[1] +// q15[2] +// q15[3] + + .arm + .text + .global armv7neon_mmm_f32_32x1_{{core}}_{{suffix}} + .type armv7neon_mmm_f32_32x1_{{core}}_{{suffix}}, %function + +armv7neon_mmm_f32_32x1_{{core}}_{{suffix}}: + + pld [r0] + push { r4-r12 } + vpush { q4-q7 } + +{% include "dispatcher.tmpliq" %} + +.add_mat_mul: + + cmp r3, #0 + beq .non_linear_loop + + mov r1, r4 // packed A ptr + pld [r3] + pld [r5] + + pld [r1, #128] + pld [r1, #192] + pld [r1, #256] + pld [r1, #320] + pld [r1, #384] + pld [r1, #448] + pld [r1, #512] + +.packed_packed_loop_1: + pld [r5] // packed B ptr + +{% if core == "cortexa7" %} + + vldr d0, [r1] + vldr d1, [r1, #8] + vldr d2, [r1, #16] + vldr d3, [r1, #24] + vldr d4, [r1, #32] + vldr d5, [r1, #40] + vldr d6, [r1, #48] + vldr d7, [r1, #56] + vldr d8, [r1, #64] + vldr d9, [r1, #72] + vldr d10, [r1, #80] + vldr d11, [r1, #88] + vldr s30, [r5] + + pld [r1, #512] + pld [r1, #576] + pld [r5, #64] + + vmla.f32 q8, q0, d15[0] + vmla.f32 q9, q1, d15[0] + + vldr d0, [r1, #96] + vldr d1, [r1, #104] + vldr d2, [r1, #112] + vldr d3, [r1, #120] + + vmla.f32 q10, q2, d15[0] + vmla.f32 q11, q3, d15[0] + + vmla.f32 q12, q4, d15[0] + vmla.f32 q13, q5, d15[0] + + vmla.f32 q14, q0, d15[0] + vmla.f32 q15, q1, d15[0] + + add r1, #128 + add r5, #4 + +{% elsif core == "cortexa9" %} + + vld1.64 {d0-d3}, [r1]! + vld1.64 {d4-d7}, [r1]! + pld [r1, #512] + pld [r1, #576] + vld1.64 {d8-d11}, [r1]! + vld1.f32 d15[0], [r5]! + pld [r5, #64] + + vmla.f32 q8, q0, d15[0] + vmla.f32 q9, q1, d15[0] + vld1.64 {d0-d3}, [r1]! + + vmla.f32 q10, q2, d15[0] + vmla.f32 q11, q3, d15[0] + + vmla.f32 q12, q4, d15[0] + vmla.f32 q13, q5, d15[0] + + vmla.f32 q14, q0, d15[0] + vmla.f32 q15, q1, d15[0] + +{% else %} + + vldmia r1!, { q0-q3 } + vldmia r5!, { s30 } + + vmla.f32 q8, q0, d15[0] + vmla.f32 q9, q1, d15[0] + vldmia r1!, { q0-q1 } + + vmla.f32 q10, q2, d15[0] + vmla.f32 q11, q3, d15[0] + vldmia r1!, { q2-q3 } + + vmla.f32 q12, q0, d15[0] + vmla.f32 q13, q1, d15[0] + + vmla.f32 q14, q2, d15[0] + vmla.f32 q15, q3, d15[0] + +{% endif %} + + subs r3, r3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +{% include "armv7neon_mmm_f32_scalars.tmpliq" from:8, to:15 %} +{% include "armv7neon_mmm_f32_per_rows.tmpliq" mr:32, from:8, to:15 %} +{% include "armv7neon_mmm_f32_per_cols.tmpliq" mr:32, from:8, to:15 %} + +.add_unicast: + {% for reg in (0..15) %} + vld1.f32 d{{reg}}[0], [ r3 ], r4 + vld1.f32 d{{reg}}[1], [ r3 ], r4 + {% endfor %} + {% for reg in (0..7) %} + vadd.f32 q{{reg|plus:8}}, q{{reg|plus:8}}, q{{reg}} + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + vld1.f32 d0[0], [ r4 ] + vldmia r3!, { q4-q7 } + + vmla.f32 q8, q4, d0[0] + vmla.f32 q9, q5, d0[0] + + vmla.f32 q10, q6, d0[0] + vmla.f32 q11, q7, d0[0] + + vldmia r3!, { q4-q7 } + + vmla.f32 q12, q4, d0[0] + vmla.f32 q13, q5, d0[0] + + vmla.f32 q14, q6, d0[0] + vmla.f32 q15, q7, d0[0] + + b .non_linear_loop + +.store: + // r3, r4 <- ptr, rsc + cmp r4, #4 + bne .store_generic + + vst1.f64 {d16-d19}, [r3]! + vst1.f64 {d20-d23}, [r3]! + vst1.f64 {d24-d27}, [r3]! + vst1.f64 {d28-d31}, [r3]! + + b .non_linear_loop + +.store_generic: + + {% for reg in (16..31) %} + vst1.f32 d{{reg}}[0], [r3], r4 + vst1.f32 d{{reg}}[1], [r3], r4 + {% endfor %} + + b .non_linear_loop + +.return: + vpop { q4-q7 } + pop { r4-r12 } + + bx lr + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_f32_8x4_core.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_f32_8x4_core.tmpl new file mode 100644 index 0000000..19f4c28 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_f32_8x4_core.tmpl @@ -0,0 +1,139 @@ +// vim: ft=arm + +// C tile regs +// +// q8[0] q10[0] q12[0] q14[0] +// q8[1] q10[1] q12[1] q14[1] +// q8[2] q10[2] q12[2] q14[2] +// q8[3] q10[3] q12[3] q14[3] +// +// q9[0] q11[0] q13[0] q15[0] +// q9[1] q11[1] q13[1] q15[1] +// q9[2] q11[2] q13[2] q15[2] +// q9[3] q11[3] q13[3] q15[3] + +// packed A buffering (2x8 values): alternating q0, q1 with q2, q3 +// packed B buffering (2x4 values): alternating q4 with q5 + + .arm + .text + .global armv7neon_mmm_f32_8x4_{{core}}_{{suffix}} + .type armv7neon_mmm_f32_8x4_{{core}}_{{suffix}}, %function + +armv7neon_mmm_f32_8x4_{{core}}_{{suffix}}: + pld [r0] + push { r4-r12 } + vpush { q4-q7 } + +{% include "dispatcher.tmpliq" %} + +.add_mat_mul: + + cmp r3, #0 + beq .non_linear_loop + + mov r1, r4 // packed A ptr + pld [r3] + pld [r5] + + .packed_packed: + pld [r5] // packed B ptr + .packed_packed_loop_1: + +{% if core == "cortexa7" %} + vldr d0, [r1] + vldr d1, [r1, #8] + vldr d2, [r1, #16] + vldr d3, [r1, #24] + vldr d4, [r5] + vldr d5, [r5, #8] +{% elsif core == "cortexa9" %} + vld1.64 {d0-d3}, [r1]! + vld1.64 {d4, d5}, [r5]! +{% else %} + vldmia r1!, { q0, q1} + vldmia r5!, { q2 } +{% endif %} + +{% if core != "generic" %} + pld [r1, #512] + pld [r5, #512] +{% endif %} + + vmla.f32 q8, q0, d4[0] + vmla.f32 q9, q1, d4[0] + + vmla.f32 q10, q0, d4[1] + vmla.f32 q11, q1, d4[1] + + vmla.f32 q12, q0, d5[0] + vmla.f32 q13, q1, d5[0] + + vmla.f32 q14, q0, d5[1] + vmla.f32 q15, q1, d5[1] + +{% if core == "cortexa7" %} + add r1, #32 + add r5, #16 +{% endif %} + + subs r3, r3, #1 + bne .packed_packed_loop_1 + b .non_linear_loop + +{% include "armv7neon_mmm_f32_scalars.tmpliq" from:8, to:15 %} +{% include "armv7neon_mmm_f32_per_rows.tmpliq" mr:8, from:8, to:15 %} +{% include "armv7neon_mmm_f32_per_cols.tmpliq" mr:8, from:8, to:15 %} + +.add_unicast: + // r3, r4, r5 <- ptr, rsc, csc + {% for col in (0..3) %} + mov r2, r3 + {% for reg in (0..3) %} + vld1.f32 d0[0], [ r2 ], r4 + vld1.f32 d0[1], [ r2 ], r4 + vadd.f32 d{{col | times: 4 | plus: reg | plus : 16}}, d0 + {% endfor %} + add r3, r3, r5 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + vldmia r3!, { q0, q1 } + vldmia r4!, { q4 } + + vmla.f32 q8, q0, d8[0] + vmla.f32 q9, q1, d8[0] + + vmla.f32 q10, q0, d8[1] + vmla.f32 q11, q1, d8[1] + + vmla.f32 q12, q0, d9[0] + vmla.f32 q13, q1, d9[0] + + vmla.f32 q14, q0, d9[1] + vmla.f32 q15, q1, d9[1] + + b .non_linear_loop + +.store: + // r3,r4,r5 are c,rsc,csc + {% for col in (0..3) %} + mov r8, r3 + {% for reg in (0..3) %} + vst1.f32 d{{col | times: 4 | plus: reg | plus : 16}}[0], [ r8 ], r4 + vst1.f32 d{{col | times: 4 | plus: reg | plus : 16}}[1], [ r8 ], r4 + {% endfor %} + {% if col < 3 %} + add r3, r3, r5 + {% endif %} + {% endfor %} + b .non_linear_loop + +.return: + vpop { q4-q7 } + pop { r4-r12 } + + bx lr + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_f32_8x6_core.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_f32_8x6_core.tmpl new file mode 100644 index 0000000..9e22e03 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_f32_8x6_core.tmpl @@ -0,0 +1,153 @@ +// vim: ft=arm + + .arm + .text + .global armv7neon_mmm_f32_8x6_{{core}}_{{suffix}} + .type armv7neon_mmm_f32_8x6_{{core}}_{{suffix}}, %function + +armv7neon_mmm_f32_8x6_{{core}}_{{suffix}}: + + pld [r0] + push { r4-r12 } + vpush { q4-q7 } + +{% include "dispatcher.tmpliq" %} + +.add_mat_mul: + cmp r3, #0 + beq .non_linear_loop + + mov r1, r4 // packed A ptr + pld [r3] + pld [r5] + + .packed_packed_loop_1: + +{% if core == "cortexa7" %} + vldr d0, [r1] + vldr d1, [r1, #8] + vldr d2, [r1, #16] + vldr d3, [r1, #24] + vldr d4, [r5] + vldr d5, [r5, #8] + vldr d6, [r5, #16] +{% elsif core == "cortexa9" %} + vld1.64 {d0-d3}, [r1]! + vld1.64 {d4, d5, d6}, [r5]! +{% else %} + vldmia r1!, {q0-q1} + vldmia r5!, {d4-d6} +{% endif %} + +{% if core != "generic" %} + pld [r1, #512] + pld [r5, #512] +{% endif %} + + vmla.f32 q4, q0, d4[0] + vmla.f32 q5, q1, d4[0] + + vmla.f32 q6, q0, d4[1] + vmla.f32 q7, q1, d4[1] + + vmla.f32 q8, q0, d5[0] + vmla.f32 q9, q1, d5[0] + + vmla.f32 q10, q0, d5[1] + vmla.f32 q11, q1, d5[1] + + vmla.f32 q12, q0, d6[0] + vmla.f32 q13, q1, d6[0] + + vmla.f32 q14, q0, d6[1] + vmla.f32 q15, q1, d6[1] + +{% if core == "cortexa7" %} + add r1, #32 + add r5, #24 +{% endif %} + + subs r3, r3, #1 + bne .packed_packed_loop_1 + b .non_linear_loop + +{% include "armv7neon_mmm_f32_scalars.tmpliq" from:4, to:15 %} +{% include "armv7neon_mmm_f32_per_rows.tmpliq" mr:8, from:4, to:15 %} +{% include "armv7neon_mmm_f32_per_cols.tmpliq" mr:8, from:4, to:15 %} + +.add_unicast: + // r3, r4, r5, r6 <- ptr, rsc, csc, size + {% for col in (0..5) %} + mov r2, r3 + {% for reg in (0..3) %} + vld1.f32 d0[0], [ r2 ], r4 + vld1.f32 d0[1], [ r2 ], r4 + vadd.f32 d{{col | times: 4 | plus: reg | plus : 8}}, d0 + {% endfor %} + add r3, r3, r5 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + vldmia r3!, { q0, q1 } + vldmia r4!, { d4, d5, d6 } + + vmla.f32 q4, q0, d4[0] + vmla.f32 q5, q1, d4[0] + + vmla.f32 q6, q0, d4[1] + vmla.f32 q7, q1, d4[1] + + vmla.f32 q8, q0, d5[0] + vmla.f32 q9, q1, d5[0] + + vmla.f32 q10, q0, d5[1] + vmla.f32 q11, q1, d5[1] + + vmla.f32 q12, q0, d6[0] + vmla.f32 q13, q1, d6[0] + + vmla.f32 q14, q0, d6[1] + vmla.f32 q15, q1, d6[1] + + b .non_linear_loop + +.store: + // r3, r4, r5 <- ptr, rsc, csc + + cmp r4, #4 + bne .store_generic + + {% for col in (0..5) %} + mov r8, r3 + {% for reg in (0..3) %} + vst1.64 d{{col| times: 4 | plus: 8 | plus: reg}}, [ r8 ]! + {% endfor %} + {% if col < 5 %} + add r3, r3, r5 + {% endif %} + {% endfor %} + + b .non_linear_loop + +.store_generic: + {% for col in (0..5) %} + mov r8, r3 + {% for reg in (0..3) %} + vst1.f32 d{{col | times: 4 | plus: reg | plus : 8}}[0], [ r8 ], r4 + vst1.f32 d{{col | times: 4 | plus: reg | plus : 8}}[1], [ r8 ], r4 + {% endfor %} + {% if col < 5 %} + add r3, r3, r5 + {% endif %} + {% endfor %} + + b .non_linear_loop + +.return: + vpop { q4-q7 } + pop { r4-r12 } + + bx lr + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_f32_per_cols.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_f32_per_cols.tmpliq new file mode 100644 index 0000000..adc9b14 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_f32_per_cols.tmpliq @@ -0,0 +1,9 @@ +// vim: ft=arm + +{% include "armv7neon_mmm_q_per_col.tmpliq" label:"per_col_min", op:"vmin.f32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_col.tmpliq" label:"per_col_max", op:"vmax.f32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_col.tmpliq" label:"per_col_mul", op:"vmul.f32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_col.tmpliq" label:"per_col_add", op:"vadd.f32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_col.tmpliq" label:"per_col_sub", op:"vsub.f32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_col.tmpliq" label:"per_col_sub_flipped", op:"vsub.f32", mr:mr, from:from, to:to, flipped: true%} + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_f32_per_rows.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_f32_per_rows.tmpliq new file mode 100644 index 0000000..64dd5ca --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_f32_per_rows.tmpliq @@ -0,0 +1,9 @@ +// vim: ft=arm + +{% include "armv7neon_mmm_q_per_row.tmpliq" label:"per_row_min", op:"vmin.f32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_row.tmpliq" label:"per_row_max", op:"vmax.f32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_row.tmpliq" label:"per_row_mul", op:"vmul.f32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_row.tmpliq" label:"per_row_add", op:"vadd.f32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_row.tmpliq" label:"per_row_sub", op:"vsub.f32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_row.tmpliq" label:"per_row_sub_flipped", op:"vsub.f32", mr:mr, from:from, to:to, flipped: true%} + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_f32_scalars.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_f32_scalars.tmpliq new file mode 100644 index 0000000..5c18c9e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_f32_scalars.tmpliq @@ -0,0 +1,13 @@ +// vim: ft=arm + +{% include "armv7neon_mmm_q_scalar.tmpliq" label:"scalar_min", op:"vmin.f32", from:from, to:to%} +{% include "armv7neon_mmm_q_scalar.tmpliq" label:"scalar_max", op:"vmax.f32", from:from, to:to%} +{% include "armv7neon_mmm_q_scalar.tmpliq" label:"scalar_mul", op:"vmul.f32", from:from, to:to%} +{% include "armv7neon_mmm_q_scalar.tmpliq" label:"scalar_add", op:"vadd.f32", from:from, to:to%} +{% include "armv7neon_mmm_q_scalar.tmpliq" label:"scalar_sub", op:"vsub.f32", from:from, to:to%} +{% include "armv7neon_mmm_q_scalar.tmpliq" label:"scalar_sub_flipped", op:"vsub.f32", from:from, to:to, flipped:true%} + +.q_shl: +.q_shr: +.q_scale: + b .unsupported diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_i32_32x1.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_i32_32x1.tmpl new file mode 100644 index 0000000..bc8f1ab --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_i32_32x1.tmpl @@ -0,0 +1,140 @@ +// vim: ft=arm + +// C tile regs: q8..q16 + + .arm + .text + .global armv7neon_mmm_i32_32x1_{{suffix}} + .type armv7neon_mmm_i32_32x1_{{suffix}}, %function + +armv7neon_mmm_i32_32x1_{{suffix}}: + + pld [r0] + push { r4-r12 } + vpush { q4-q7 } + +{% include "dispatcher.tmpliq" %} + +.add_mat_mul: + cmp r3, #0 + beq .non_linear_loop + + mov r1, r4 // packed A ptr + pld [r3] + pld [r7] + + .packed_packed: + + .packed_packed_loop_1: + vldmia r1!, { q4-q5 } + + vld1.8 { d0[0] }, [ r5 ]! + vmovl.s8 q0, d0 + + vmovl.s8 q1, d8 + vmlal.s16 q8, d2, d0[0] + vmlal.s16 q9, d3, d0[0] + + vmovl.s8 q1, d9 + vmlal.s16 q10, d2, d0[0] + vmlal.s16 q11, d3, d0[0] + + vmovl.s8 q1, d10 + vmlal.s16 q12, d2, d0[0] + vmlal.s16 q13, d3, d0[0] + + vmovl.s8 q1, d11 + vmlal.s16 q14, d2, d0[0] + vmlal.s16 q15, d3, d0[0] + + subs r3, r3, #1 + bne .packed_packed_loop_1 + b .non_linear_loop + +{% include "armv7neon_mmm_i32_scalars.tmpliq" from:8, to:15 %} +{% include "armv7neon_mmm_i32_per_rows.tmpliq" mr:32, from:8, to:15 %} +{% include "armv7neon_mmm_i32_per_cols.tmpliq" mr:32, from:8, to:15 %} + +.add_unicast: + // r3, r4, r5, r6 <- ptr, rsc, csc, size + + cmp r6, #4 + beq .non_linear_addc_i32 + + {% for reg in (16..31) %} + vld1.s8 d0[0], [ r3 ], r4 + vld1.s8 d0[1], [ r3 ], r4 + vmovl.s8 q0, d0 + vmovl.s16 q0, d0 + vadd.i32 d{{reg}}, d0 + {% endfor %} + + b .non_linear_loop + +.non_linear_addc_i32: + {% for reg in (16..31) %} + vld1.s32 d0[0], [ r3 ], r4 + vld1.s32 d0[1], [ r3 ], r4 + vadd.i32 d{{reg}}, d0 + {% endfor %} + b .non_linear_loop + +.add_row_col_products: + vldm r3, { s0 } + + vldmia r4!, { q4-q7 } + + vmla.s32 q8, q4, d0[0] + vmla.s32 q9, q5, d0[0] + + vmla.s32 q10, q6, d0[0] + vmla.s32 q11, q7, d0[0] + + vldmia r4!, { q4-q7 } + + vmla.s32 q12, q4, d0[0] + vmla.s32 q13, q5, d0[0] + + vmla.s32 q14, q6, d0[0] + vmla.s32 q15, q7, d0[0] + + b .non_linear_loop + + {% include "armv7neon_mmm_i32_scale_q8_q15.tmpliq" %} + +.store: + // r3, r4, r5, r6 <- ptr, rsc, csc, size + cmp r6, #4 + beq .store_strides_i32 + + {% for reg in (8..15) %} + vmovn.s32 d{{reg | times: 2}}, q{{reg}} + vmovn.s16 d{{reg | times: 2}}, q{{reg}} + {% endfor %} + {% for reg in (8..15) %} + {%capture d%}{{reg | times: 2 }}{%endcapture%} + vst1.s8 d{{d}}[0], [ r3 ], r4 + vst1.s8 d{{d}}[1], [ r3 ], r4 + vst1.s8 d{{d}}[2], [ r3 ], r4 + vst1.s8 d{{d}}[3], [ r3 ], r4 + {% endfor %} + + b .non_linear_loop + +.store_strides_i32: + {% for reg in (8..15) %} + {%capture d%}{{reg | times: 2}}{%endcapture%} + vst1.s32 d{{d}}[0], [ r3 ], r4 + vst1.s32 d{{d}}[1], [ r3 ], r4 + vst1.s32 d{{d|plus:1}}[0], [ r3 ], r4 + vst1.s32 d{{d|plus:1}}[1], [ r3 ], r4 + {% endfor %} + + b .non_linear_loop + +.return: + vpop { q4-q7 } + pop { r4-r12 } + + bx lr + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_i32_8x4.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_i32_8x4.tmpl new file mode 100644 index 0000000..d99fce9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_i32_8x4.tmpl @@ -0,0 +1,263 @@ +// vim: ft=arm + +// C tile regs +// +// q8[0] q10[0] q12[0] q14[0] +// q8[1] q10[1] q12[1] q14[1] +// q8[2] q10[2] q12[2] q14[2] +// q8[3] q10[3] q12[3] q14[3] +// +// q9[0] q11[0] q13[0] q15[0] +// q9[1] q11[1] q13[1] q15[1] +// q9[2] q11[2] q13[2] q15[2] +// q9[3] q11[3] q13[3] q15[3] + + .arm + .text + .global armv7neon_mmm_i32_8x4_{{suffix}} + .type armv7neon_mmm_i32_8x4_{{suffix}}, %function + +armv7neon_mmm_i32_8x4_{{suffix}}: + + pld [r0] + push { r4-r12 } + vpush { q4-q7 } + +{% include "dispatcher.tmpliq" %} + +.add_mat_mul: + cmp r3, #0 + beq .non_linear_loop + + mov r1, r4 // packed A ptr + pld [r3] + pld [r5] + + .packed_packed: + pld [r5] // packed B ptr + + cmp r3, #4 + blt .packed_packed_loop_1 + + .packed_packed_loop_4: + pld [r1, #64] + pld [r5, #64] + + // q2: d4 -> d4,d5 A even cols (from r1) + // q3: d6 -> d6,d7 A odd cols (from r1) + // q0: s0 -> d0 : B even lines (from r5) + // q1: s4 -> d2 : B odd lines (from r5) + + // 0 + vldmia r1!, { d4 } + vldmia r5!, { s0 } + + vmovl.s8 q2, d4 + vmovl.s8 q0, d0 + + vmlal.s16 q8, d4, d0[0] + vmlal.s16 q9, d5, d0[0] + + vldmia r1!, { d6 } + + vmlal.s16 q10, d4, d0[1] + vmlal.s16 q11, d5, d0[1] + + vldmia r5!, { s4 } + + vmlal.s16 q12, d4, d0[2] + vmlal.s16 q13, d5, d0[2] + + vmlal.s16 q14, d4, d0[3] + vmlal.s16 q15, d5, d0[3] + + // 1 + vmovl.s8 q3, d6 + vmovl.s8 q1, d2 + + vmlal.s16 q8, d6, d2[0] + vldmia r1!, { d4 } + vmlal.s16 q9, d7, d2[0] + vldmia r5!, { s0 } + + vmlal.s16 q10, d6, d2[1] + vmlal.s16 q11, d7, d2[1] + + vmlal.s16 q12, d6, d2[2] + vmlal.s16 q13, d7, d2[2] + + vmlal.s16 q14, d6, d2[3] + vmlal.s16 q15, d7, d2[3] + + // 2 + vmovl.s8 q2, d4 + vmovl.s8 q0, d0 + + vmlal.s16 q8, d4, d0[0] + vmlal.s16 q9, d5, d0[0] + + vldmia r1!, { d6 } + + vmlal.s16 q10, d4, d0[1] + vmlal.s16 q11, d5, d0[1] + + vldmia r5!, { s4 } + + vmlal.s16 q12, d4, d0[2] + vmlal.s16 q13, d5, d0[2] + + vmlal.s16 q14, d4, d0[3] + vmlal.s16 q15, d5, d0[3] + + // 3 + vmovl.s8 q3, d6 + vmovl.s8 q1, d2 + + vmlal.s16 q8, d6, d2[0] + vmlal.s16 q9, d7, d2[0] + + vmlal.s16 q10, d6, d2[1] + vmlal.s16 q11, d7, d2[1] + + vmlal.s16 q12, d6, d2[2] + vmlal.s16 q13, d7, d2[2] + + vmlal.s16 q14, d6, d2[3] + vmlal.s16 q15, d7, d2[3] + + sub r3, r3, #4 + cmp r3, #4 + bge .packed_packed_loop_4 + + cmp r3, #0 + beq .non_linear_loop + + .packed_packed_loop_1: + + vldmia r1!, { s0, s1 } + vmovl.s8 q0, d0 + vldmia r5!, { s4 } + vmovl.s8 q1, d2 + + vmlal.s16 q8, d0, d2[0] + vmlal.s16 q9, d1, d2[0] + + vmlal.s16 q10, d0, d2[1] + vmlal.s16 q11, d1, d2[1] + + vmlal.s16 q12, d0, d2[2] + vmlal.s16 q13, d1, d2[2] + + vmlal.s16 q14, d0, d2[3] + vmlal.s16 q15, d1, d2[3] + + subs r3, r3, #1 + bne .packed_packed_loop_1 + b .non_linear_loop + +{% include "armv7neon_mmm_i32_scalars.tmpliq" from:8, to:15 %} +{% include "armv7neon_mmm_i32_per_rows.tmpliq" mr:8, from:8, to:15 %} +{% include "armv7neon_mmm_i32_per_cols.tmpliq" mr:8, from:8, to:15 %} + +.add_unicast: + // r3, r4, r5, r6 <- ptr, rsc, csc, size + cmp r6, #4 + beq .non_linear_addc_i32 + + {% for col in (0..3) %} + mov r8, r3 + {% for reg in (0..3) %} + vld1.s8 d0[0], [ r8 ], r4 + vld1.s8 d0[1], [ r8 ], r4 + vmovl.s8 q0, d0 + vmovl.s16 q0, d0 + vadd.i32 d{{col | times: 4 | plus: reg | plus : 16}}, d0 + {% endfor %} + add r3, r3, r5 + {% endfor %} + + b .non_linear_loop + +.non_linear_addc_i32: + + {% for col in (0..3) %} + mov r8, r3 + {% for reg in (0..3) %} + vld1.s32 d0[0], [ r8 ], r4 + vld1.s32 d0[1], [ r8 ], r4 + vadd.i32 d{{col | times: 4 | plus: reg | plus : 16}}, d0 + {% endfor %} + {% if col < 3 %} + add r3, r3, r5 + {% endif %} + {% endfor %} + +b .non_linear_loop + +.add_row_col_products: + vldmia r3!, { q0, q1 } + vldmia r4!, { q4 } + + vmla.s32 q8, q0, d8[0] + vmla.s32 q9, q1, d8[0] + + vmla.s32 q10, q0, d8[1] + vmla.s32 q11, q1, d8[1] + + vmla.s32 q12, q0, d9[0] + vmla.s32 q13, q1, d9[0] + + vmla.s32 q14, q0, d9[1] + vmla.s32 q15, q1, d9[1] + + b .non_linear_loop + + {% include "armv7neon_mmm_i32_scale_q8_q15.tmpliq" %} + +.store: + // r3, r4, r5, r6 <- ptr, rsc, csc, size + cmp r6, #4 + beq .store_strides_i32 + + {% for reg in (8..15) %} + vmovn.s32 d{{reg | times: 2}}, q{{reg}} + vmovn.s16 d{{reg | times: 2}}, q{{reg}} + {% endfor %} + {% for col in (0..3) %} + mov r8, r3 + {% for reg in (0..1) %} + {%capture d%}{{col | times: 2 | plus: reg | times: 2 | plus: 16}}{%endcapture%} + vst1.s8 d{{d}}[0], [ r8 ], r4 + vst1.s8 d{{d}}[1], [ r8 ], r4 + vst1.s8 d{{d}}[2], [ r8 ], r4 + vst1.s8 d{{d}}[3], [ r8 ], r4 + {% endfor %} + {% if col < 3 %} + add r3, r3, r5 + {% endif %} + {% endfor %} + + b .non_linear_loop + +.store_strides_i32: + + {% for col in (0..3) %} + mov r8, r3 + {% for reg in (0..3) %} + {% for lane in (0..1) %} + vst1.s32 d{{col | times: 4 | plus: reg | plus: 16}}[{{lane}}], [ r8 ], r4 + {% endfor %} + {% endfor %} + {% if col < 3 %} + add r3, r3, r5 + {% endif %} + {% endfor %} + + b .non_linear_loop + +.return: + vpop { q4-q7 } + pop { r4-r12 } + + bx lr + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_i32_per_cols.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_i32_per_cols.tmpliq new file mode 100644 index 0000000..3beef20 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_i32_per_cols.tmpliq @@ -0,0 +1,8 @@ +// vim: ft=arm + +{% include "armv7neon_mmm_q_per_col.tmpliq" label:"per_col_min", op:"vmin.s32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_col.tmpliq" label:"per_col_max", op:"vmax.s32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_col.tmpliq" label:"per_col_mul", op:"vmul.s32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_col.tmpliq" label:"per_col_add", op:"vadd.s32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_col.tmpliq" label:"per_col_sub", op:"vsub.s32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_col.tmpliq" label:"per_col_sub_flipped", op:"vsub.s32", mr:mr, from:from, to:to, flipped:true%} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_i32_per_rows.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_i32_per_rows.tmpliq new file mode 100644 index 0000000..f0739b3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_i32_per_rows.tmpliq @@ -0,0 +1,8 @@ +// vim: ft=arm + +{% include "armv7neon_mmm_q_per_row.tmpliq" label:"per_row_min", op:"vmin.s32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_row.tmpliq" label:"per_row_max", op:"vmax.s32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_row.tmpliq" label:"per_row_mul", op:"vmul.s32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_row.tmpliq" label:"per_row_add", op:"vadd.s32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_row.tmpliq" label:"per_row_sub", op:"vsub.s32", mr:mr, from:from, to:to %} +{% include "armv7neon_mmm_q_per_row.tmpliq" label:"per_row_sub_flipped", op:"vsub.s32", mr:mr, from:from, to:to, flipped:true%} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_i32_scalars.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_i32_scalars.tmpliq new file mode 100644 index 0000000..afeb1e3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_i32_scalars.tmpliq @@ -0,0 +1,9 @@ +// vim: ft=arm + +{% include "armv7neon_mmm_q_scalar.tmpliq" label:"scalar_min", op:"vmin.s32", from:from, to:to%} +{% include "armv7neon_mmm_q_scalar.tmpliq" label:"scalar_max", op:"vmax.s32", from:from, to:to%} +{% include "armv7neon_mmm_q_scalar.tmpliq" label:"scalar_mul", op:"vmul.s32", from:from, to:to%} +{% include "armv7neon_mmm_q_scalar.tmpliq" label:"scalar_add", op:"vadd.s32", from:from, to:to%} +{% include "armv7neon_mmm_q_scalar.tmpliq" label:"scalar_sub", op:"vsub.s32", from:from, to:to%} +{% include "armv7neon_mmm_q_scalar.tmpliq" label:"scalar_sub_flipped", op:"vsub.s32", from:from, to:to, flipped:true%} + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_i32_scale_q8_q15.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_i32_scale_q8_q15.tmpliq new file mode 100644 index 0000000..f594928 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_i32_scale_q8_q15.tmpliq @@ -0,0 +1,232 @@ +// vim: ft=arm + +.q_scale: + ldm r0, { r4, r5, r6, r7 } // fixme params are already loaded by disp. + vdup.s32 q0, r7 // q0 <- multiplier + + mov r3, #1 + vdup.s32 q1, r3 // q1 <- ones + vmovl.s32 q1, d2 + + add r5, #32 + neg r5, r5 + vdup.s32 q2, r5 // q2 <- -(shift + 32) + vmovl.s32 q2, d4 + + cmp r6, #1 + beq .q_scale_rounding_zero + cmp r6, #2 + beq .q_scale_rounding_away + cmp r6, #3 + beq .q_scale_rounding_minus_inf + cmp r6, #4 + beq .q_scale_rounding_plus_inf + cmp r6, #5 + beq .q_scale_rounding_even + cmp r6, #6 + beq .q_scale_rounding_odd + + b .unsupported + +.q_scale_rounding_zero: + {% for q in (8..15) %} + vclt.s32 q7, q{{q}}, #0 + vabs.s32 q{{q}}, q{{q}} + vqdmull.s32 q5, d{{q | times:2}}, d0[0] + vqdmull.s32 q6, d{{q | times:2 | plus:1}}, d0[0] + vsub.s64 q5, q1 + vsub.s64 q6, q1 + vqrshl.s64 q5, q2 + vqrshl.s64 q6, q2 + vmovn.s64 d{{q | times:2}}, q5 + vmovn.s64 d{{q | times:2 | plus: 1}}, q6 + vneg.s32 q5, q{{q}} + vbit.s32 q{{q}}, q5, q7 + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_away: + {% for q in (8..15) %} + vclt.s32 q7, q{{q}}, #0 + vabs.s32 q{{q}}, q{{q}} + vqdmull.s32 q5, d{{q | times:2}}, d0[0] + vqdmull.s32 q6, d{{q | times:2 | plus:1}}, d0[0] + vqrshl.s64 q5, q2 + vqrshl.s64 q6, q2 + vmovn.s64 d{{q | times:2}}, q5 + vmovn.s64 d{{q | times:2 | plus: 1}}, q6 + vneg.s32 q5, q{{q}} + vbit.s32 q{{q}}, q5, q7 + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_minus_inf: + {% for q in (8..15) %} + vqdmull.s32 q5, d{{q | times:2}}, d0[0] + vqdmull.s32 q6, d{{q | times:2 | plus:1}}, d0[0] + vsub.s64 q5, q1 + vsub.s64 q6, q1 + vqrshl.s64 q5, q2 + vqrshl.s64 q6, q2 + vmovn.s64 d{{q | times:2}}, q5 + vmovn.s64 d{{q | times:2 | plus: 1}}, q6 + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_plus_inf: + {% for q in (8..15) %} + vqdmull.s32 q5, d{{q | times:2}}, d0[0] + vqdmull.s32 q6, d{{q | times:2 | plus:1}}, d0[0] + vqrshl.s64 q5, q2 + vqrshl.s64 q6, q2 + vmovn.s64 d{{q | times:2}}, q5 + vmovn.s64 d{{q | times:2 | plus: 1}}, q6 + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_even: + {% for q in (8..15) %} + vclt.s32 q7, q{{q}}, #0 + vabs.s32 q{{q}}, q{{q}} + vqdmull.s32 q5, d{{q | times:2}}, d0[0] + vqdmull.s32 q6, d{{q | times:2 | plus:1}}, d0[0] + vqshl.s64 q3, q5, q2 + vqshl.s64 q4, q6, q2 + vand q3, q3, q1 + vand q4, q4, q1 + vsub.s64 q3, q3, q1 + vsub.s64 q4, q4, q1 + vadd.s64 q5, q3 + vadd.s64 q6, q4 + vqrshl.s64 q5, q2 + vqrshl.s64 q6, q2 + vmovn.s64 d{{q | times:2}}, q5 + vmovn.s64 d{{q | times:2 | plus: 1}}, q6 + vneg.s32 q5, q{{q}} + vbit.s32 q{{q}}, q5, q7 + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_odd: + {% for q in (8..15) %} + vclt.s32 q7, q{{q}}, #0 + vabs.s32 q{{q}}, q{{q}} + vqdmull.s32 q5, d{{q | times:2}}, d0[0] + vqdmull.s32 q6, d{{q | times:2 | plus:1}}, d0[0] + vqshl.s64 q3, q5, q2 + vqshl.s64 q4, q6, q2 + vand q3, q3, q1 + vand q4, q4, q1 + vsub.s64 q5, q3 + vsub.s64 q6, q4 + vqrshl.s64 q5, q2 + vqrshl.s64 q6, q2 + vmovn.s64 d{{q | times:2}}, q5 + vmovn.s64 d{{q | times:2 | plus: 1}}, q6 + vneg.s32 q5, q{{q}} + vbit.s32 q{{q}}, q5, q7 + {% endfor %} + + b .non_linear_loop + +.q_shl: + ldm r0, { r4, r5 } // fixme params are already loaded by disp. + vdup.s32 q2, r5 // q2 <- shift + + {% for q in (8..15) %} + vqrshl.s32 q{{q}}, q2 // Shift + {% endfor %} + + b .non_linear_loop + +.q_shr: + ldm r0, { r4, r5, r6 } // fixme params are already loaded by disp. + + mov r3, #1 + vdup.s32 q1, r3 // q1 <- ones + + neg r5, r5 + vdup.s32 q2, r5 // q2 <- shift + + cmp r6, #1 + beq .q_shr_rounding_zero + cmp r6, #2 + beq .q_shr_rounding_away + cmp r6, #3 + beq .q_shr_rounding_minus_inf + cmp r6, #4 + beq .q_shr_rounding_plus_inf + cmp r6, #5 + beq .q_shr_rounding_even + cmp r6, #6 + beq .q_shr_rounding_odd + + b .unsupported + +.q_shr_rounding_zero: + // return signum(x) * ((abs(x) - 1) >>r shift ) + {% for q in (8..15) %} + vclt.s32 q3, q{{q}}, #0 // Store the sign of the value + vabs.s32 q{{q}}, q{{q}} // Compute their abs + vsub.s32 q{{q}}, q1 // Substract 1 to abs(x) + vqrshl.s32 q{{q}}, q2 // Rounding shift (0.5 -> 1) + vneg.s32 q4, q{{q}} // Compute -((abs(x) - 1) >>r shift ) + vbit.s32 q{{q}}, q4, q3 // Restore sign of x with bit mask + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_away: + // return signum(x) * (abs(x) >>r shift ) + {% for q in (8..15) %} + vclt.s32 q3, q{{q}}, #0 // Store the sign of the value + vabs.s32 q{{q}}, q{{q}} // Compute their abs + vqrshl.s32 q{{q}}, q2 // Rounding shift (0.5 -> 1) + vneg.s32 q4, q{{q}} // Compute -(abs(x) >>r shift ) + vbit.s32 q{{q}}, q4, q3 // Restore sign of x with bit mask + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_minus_inf: + // return -(-x >>r shift) + {% for q in (8..15) %} + vneg.s32 q3, q{{q}} // Compute -x + vqrshl.s32 q3, q2 // Rounding shift (0.5 -> 1) + vneg.s32 q{{q}}, q3 // Compute -(-x >>r shift) + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_plus_inf: + // return x >>r shift + {% for q in (8..15) %} + vqrshl.s32 q{{q}}, q2 // Rounding shift (0.5 -> 1) + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_even: + // If (x >> shift) is odd -> (x - 0) >>r shift + // If (x >> shift) is even -> (x - 1) >>r shift + {% for q in (8..15) %} + vqshl.s32 q3, q{{q}}, q2 // Truncate shift (0.5 -> 0) + vand.s32 q4, q3, q1 // Store if x is odd + vsub.s32 q5, q4, q1 // If (x >> shift) is odd 0 else -1 + vadd.s32 q{{q}}, q{{q}}, q5 // If (x >> shift) is odd (x - 0) else (x - 1) + vqrshl.s32 q{{q}}, q2 // Rounding shift (0.5 -> 1) + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_odd: + // If (x >> shift) is even -> (x - 0) >>r shift + // If (x >> shift) is odd -> (x - 1) >>r shift + {% for q in (8..15) %} + vqshl.s32 q3, q{{q}}, q2 // Truncate shift (0.5 -> 0) + vand.s32 q4, q3, q1 // Store if x >> shift is odd + vneg.s32 q5, q4 // If x is odd -1 else 0 + vadd.s32 q{{q}}, q{{q}}, q5 // If x is odd (x - 1) else (x - 0) + vqrshl.s32 q{{q}}, q2 // Rounding shift (0.5 -> 1) + {% endfor %} + b .non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_q_per_col.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_q_per_col.tmpliq new file mode 100644 index 0000000..769d290 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_q_per_col.tmpliq @@ -0,0 +1,33 @@ +// vim: ft=arm + +.{{label}}: + +{% capture mr_over_4 %}{{ mr | divided_by: 4}}{%endcapture%} +{% capture mr_over_4_min_1 %}{{ mr | divided_by: 4 | minus: 1}}{%endcapture%} + +{%capture cols%}{{to | plus: 1| minus:from| divided_by:mr_over_4}}{%endcapture%} +{%capture cols_min_1%}{{to | plus: 1| minus:from| divided_by:mr_over_4|minus:1}}{%endcapture%} + +{% if cols == "1" %} + vld1.f32 d0[0], [ r3 ] +{% else %} + {%capture cols_over_2_minus_1%}{{cols | divided_by:2 | minus:1}}{%endcapture%} + {% for c in (0..cols_over_2_minus_1) %} + vldmia r3!, { d{{c}} } + {% endfor %} +{% endif %} + + +{% for right in (0..cols_min_1) %} + vdup.f32 q3, d{{right|divided_by:2}}[{{right| modulo:2}}] + {% for down in (0..mr_over_4_min_1) %} + {%capture acc%}{{mr_over_4|times:right|plus:from|plus:down}}{%endcapture%} + {% if flipped %} + {{op}} q{{acc}}, q{{acc}}, q3 + {% else %} + {{op}} q{{acc}}, q3, q{{acc}} + {% endif %} + {% endfor %} +{% endfor %} + + b .non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_q_per_row.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_q_per_row.tmpliq new file mode 100644 index 0000000..a0f2d40 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_q_per_row.tmpliq @@ -0,0 +1,24 @@ +// vim: ft=arm + +.{{label}}: + +{% capture mr_over_4 %}{{ mr | divided_by: 4}}{%endcapture%} +{% capture mr_over_4_min_1 %}{{ mr | divided_by: 4 | minus: 1}}{%endcapture%} + +{% for reg in (0..mr_over_4_min_1) %} + vldmia r3!, { q{{reg}} } +{% endfor %} + +{% if flipped %} + {% for acc in (from..to) %} + {% capture other%}{{acc | minus: from | modulo: mr_over_4}}{%endcapture%} + {{op}} q{{acc}}, q{{acc}}, q{{other}} + {% endfor %} +{% else %} + {% for acc in (from..to) %} + {% capture other%}{{acc | minus: from | modulo: mr_over_4}}{%endcapture%} + {{op}} q{{acc}}, q{{other}}, q{{acc}} + {% endfor %} +{% endif %} + +b .non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_q_scalar.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_q_scalar.tmpliq new file mode 100644 index 0000000..4f135b4 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_mmm_q_scalar.tmpliq @@ -0,0 +1,15 @@ +// vim: ft=arm + +.{{label}}: + vmov s0, r3 + vdup.32 q0, d0[0] + {% if flipped %} + {% for reg in (from..to) %} + {{op}} q{{reg}}, q{{reg}}, q0 + {% endfor %} + {% else %} + {% for reg in (from..to) %} + {{op}} q{{reg}}, q0, q{{reg}} + {% endfor %} + {% endif %} + b .non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_prefetch.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_prefetch.tmpl new file mode 100644 index 0000000..d153e66 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_prefetch.tmpl @@ -0,0 +1,22 @@ +// vim: ft=arm + +.arm +.text +.global armv7neon_prefetch_{{suffix}} +.type armv7neon_prefetch_{{suffix}}, %function + +armv7neon_prefetch_{{suffix}}: +loop: + pld [r0] + pld [r0, #32] + pld [r0, #64] + pld [r0, #96] + pld [r0, #128] + pld [r0, #160] + pld [r0, #192] + pld [r0, #224] + add r0, r0, #256 + cmp r0, r1 + blt loop + + bx lr diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_sigmoid_f32_4n.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_sigmoid_f32_4n.tmpl new file mode 100644 index 0000000..baa5072 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_sigmoid_f32_4n.tmpl @@ -0,0 +1,215 @@ +// vim: ft=arm + + .arm + .text + .global armv7neon_sigmoid_f32_4n_{{suffix}} + .type armv7neon_sigmoid_f32_4n_{{suffix}}, %function + +/* + s16–s31 (d8–d15, q4–q7) must be preserved + s0–s15 (d0–d7, q0–q3) and d16–d31 (q8–q15) do not need to be preserved +*/ + +armv7neon_sigmoid_f32_4n_{{suffix}}: + cmp r1, #0 + blxeq lr + + vpush { q4-q7 } + + adr r2, .coeffs_num + vldmia r2!, { s0-s13 } + +// q4 -> q4,5,6 +// q5 -> q7,8,9 +// q6 -> q10,11,12 +// q7 -> q13,14,15 + + + cmp r1, #12 + blt .loop + +.loop_3: + vldmia r0, { q4, q5, q6 } // q4 <- x + + vdup.32 q15, d0[0] + vmax.f32 q4, q15 + vmax.f32 q5, q15 + vmax.f32 q6, q15 + vdup.32 q15, d0[1] + vmin.f32 q4, q15 + vmin.f32 q5, q15 + vmin.f32 q6, q15 + + vmul.f32 q7, q4, q4 // q7 <- x2 + vmul.f32 q8, q5, q5 + vmul.f32 q9, q6, q6 + + vdup.32 q10, d1[0] + vdup.32 q11, d1[0] + vdup.32 q12, d1[0] + vdup.32 q13, d1[1] + vdup.32 q14, d1[1] + vdup.32 q15, d1[1] + vmla.f32 q13, q7, q10 + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + vdup.32 q10, d2[0] + vdup.32 q11, d2[0] + vdup.32 q12, d2[0] + vmla.f32 q10, q13, q7 + vmla.f32 q11, q14, q8 + vmla.f32 q12, q15, q9 + vdup.32 q13, d2[1] + vdup.32 q14, d2[1] + vdup.32 q15, d2[1] + vmla.f32 q13, q7, q10 + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + vdup.32 q10, d3[0] + vdup.32 q11, d3[0] + vdup.32 q12, d3[0] + vmla.f32 q10, q13, q7 + vmla.f32 q11, q14, q8 + vmla.f32 q12, q15, q9 + vdup.32 q13, d3[1] + vdup.32 q14, d3[1] + vdup.32 q15, d3[1] + vmla.f32 q13, q7, q10 + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + vdup.32 q10, d4[0] + vdup.32 q11, d4[0] + vdup.32 q12, d4[0] + vmla.f32 q10, q13, q7 + vmla.f32 q11, q14, q8 + vmla.f32 q12, q15, q9 + vmul.f32 q4, q4, q10 // q4 <- numerator + vmul.f32 q5, q5, q11 + vmul.f32 q6, q6, q12 + + vdup.32 q10, d4[1] + vdup.32 q11, d4[1] + vdup.32 q12, d4[1] + vdup.32 q13, d5[0] + vdup.32 q14, d5[0] + vdup.32 q15, d5[0] + vmla.f32 q13, q7, q10 + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + vdup.32 q10, d5[1] + vdup.32 q11, d5[1] + vdup.32 q12, d5[1] + vmla.f32 q10, q13, q7 + vmla.f32 q11, q14, q8 + vmla.f32 q12, q15, q9 + vdup.32 q13, d6[0] + vdup.32 q14, d6[0] + vdup.32 q15, d6[0] + vmla.f32 q13, q7, q10 // q13 <- denum + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + + vrecpe.f32 q7, q13 + vrecpe.f32 q8, q14 + vrecpe.f32 q9, q15 + vrecps.f32 q10, q7, q13 + vrecps.f32 q11, q8, q14 + vrecps.f32 q12, q9, q15 + vmul.f32 q7, q7, q10 + vmul.f32 q8, q8, q11 + vmul.f32 q9, q9, q12 + vrecps.f32 q10, q7, q13 + vrecps.f32 q11, q8, q14 + vrecps.f32 q12, q9, q15 + vmul.f32 q7, q7, q10 // q7 <- 1/q13 + vmul.f32 q8, q8, q11 + vmul.f32 q9, q9, q12 + + vdup.32 q10, d6[1] + vdup.32 q11, d6[1] + vdup.32 q12, d6[1] + vmla.f32 q10, q4, q7 + vmla.f32 q11, q5, q8 + vmla.f32 q12, q6, q9 + + vstmia r0!, { q10, q11, q12 } + + subs r1, #12 + cmp r1, #12 + bge .loop_3 + + cmp r1, #0; + beq .return + +.loop: + vldmia r0, { q4 } // q4 <- x + + vdup.32 q15, d0[0] + vmax.f32 q4, q15 + vdup.32 q15, d0[1] + vmin.f32 q4, q15 + + vmul.f32 q7, q4, q4 // q7 <- x2 + + vdup.32 q10, d1[0] + vdup.32 q13, d1[1] + vmla.f32 q13, q7, q10 + vdup.32 q10, d2[0] + vmla.f32 q10, q13, q7 + vdup.32 q13, d2[1] + vmla.f32 q13, q7, q10 + vdup.32 q10, d3[0] + vmla.f32 q10, q13, q7 + vdup.32 q13, d3[1] + vmla.f32 q13, q7, q10 + vdup.32 q10, d4[0] + vmla.f32 q10, q13, q7 + vmul.f32 q4, q4, q10 // q4 <- numerator + + vdup.32 q10, d4[1] + vdup.32 q13, d5[0] + vmla.f32 q13, q7, q10 + vdup.32 q10, d5[1] + vmla.f32 q10, q13, q7 + vdup.32 q13, d6[0] + vmla.f32 q13, q7, q10 // q13 <- denum + + vrecpe.f32 q7, q13 + vrecps.f32 q10, q7, q13 + vmul.f32 q7, q7, q10 + vrecps.f32 q10, q7, q13 + vmul.f32 q7, q7, q10 // q7 <- 1/q13 + + vdup.32 q10, d6[1] + vmla.f32 q10, q4, q7 + + vstmia r0!, { q10 } + + subs r1, #4; + bne .loop + +.return: + vpop { q4-q7 } + bx lr + +.coeffs_num: + .float -18.6 // low + .float 18.6 // high + .float -4.433153405e-18 // alpha_13 + .float 1.169974371e-14 + + .float -1.875289645e-11 + .float 4.257889523e-8 + .float 0.00004811817576 + .float 0.008163842030 + + .float 0.2499999971 + .float 3.922935744e-6 // beta_6 + .float 0.001524872358 + .float 0.1159886749 + + .float 1.0 + .float 0.5 // + .float 0.0 // padding + .float 0.0 + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_tanh_f32_4n.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_tanh_f32_4n.tmpl new file mode 100644 index 0000000..5165f6f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/armv7neon_tanh_f32_4n.tmpl @@ -0,0 +1,209 @@ +// vim: ft=arm + + .arm + .text + .global armv7neon_tanh_f32_4n_{{suffix}} + .type armv7neon_tanh_f32_4n_{{suffix}}, %function + +/* + s16–s31 (d8–d15, q4–q7) must be preserved + s0–s15 (d0–d7, q0–q3) and d16–d31 (q8–q15) do not need to be preserved +*/ + +armv7neon_tanh_f32_4n_{{suffix}}: + cmp r1, #0 + blxeq lr + + vpush { q4-q7 } + + adr r2, .coeffs_num + vldmia r2!, { s0-s13 } + +// q4 -> q4,5,6 +// q5 -> q7,8,9 +// q6 -> q10,11,12 +// q7 -> q13,14,15 + + cmp r1, #12 + blt .loop + +.loop_3: + vldmia r0, { q4, q5, q6 } // q4 <- x + + vdup.32 q15, d0[0] + vmax.f32 q4, q15 + vmax.f32 q5, q15 + vmax.f32 q6, q15 + vdup.32 q15, d0[1] + vmin.f32 q4, q15 + vmin.f32 q5, q15 + vmin.f32 q6, q15 + + vmul.f32 q7, q4, q4 // q7 <- x2 + vmul.f32 q8, q5, q5 + vmul.f32 q9, q6, q6 + + vdup.32 q10, d1[0] + vdup.32 q11, d1[0] + vdup.32 q12, d1[0] + vdup.32 q13, d1[1] + vdup.32 q14, d1[1] + vdup.32 q15, d1[1] + vmla.f32 q13, q7, q10 + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + vdup.32 q10, d2[0] + vdup.32 q11, d2[0] + vdup.32 q12, d2[0] + vmla.f32 q10, q13, q7 + vmla.f32 q11, q14, q8 + vmla.f32 q12, q15, q9 + vdup.32 q13, d2[1] + vdup.32 q14, d2[1] + vdup.32 q15, d2[1] + vmla.f32 q13, q7, q10 + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + vdup.32 q10, d3[0] + vdup.32 q11, d3[0] + vdup.32 q12, d3[0] + vmla.f32 q10, q13, q7 + vmla.f32 q11, q14, q8 + vmla.f32 q12, q15, q9 + vdup.32 q13, d3[1] + vdup.32 q14, d3[1] + vdup.32 q15, d3[1] + vmla.f32 q13, q7, q10 + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + vdup.32 q10, d4[0] + vdup.32 q11, d4[0] + vdup.32 q12, d4[0] + vmla.f32 q10, q13, q7 + vmla.f32 q11, q14, q8 + vmla.f32 q12, q15, q9 + vmul.f32 q4, q4, q10 // q4 <- numerator + vmul.f32 q5, q5, q11 + vmul.f32 q6, q6, q12 + + vdup.32 q10, d4[1] + vdup.32 q11, d4[1] + vdup.32 q12, d4[1] + vdup.32 q13, d5[0] + vdup.32 q14, d5[0] + vdup.32 q15, d5[0] + vmla.f32 q13, q7, q10 + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + vdup.32 q10, d5[1] + vdup.32 q11, d5[1] + vdup.32 q12, d5[1] + vmla.f32 q10, q13, q7 + vmla.f32 q11, q14, q8 + vmla.f32 q12, q15, q9 + vdup.32 q13, d6[0] + vdup.32 q14, d6[0] + vdup.32 q15, d6[0] + vmla.f32 q13, q7, q10 // q13 <- denum + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + + vrecpe.f32 q7, q13 + vrecpe.f32 q8, q14 + vrecpe.f32 q9, q15 + vrecps.f32 q10, q7, q13 + vrecps.f32 q11, q8, q14 + vrecps.f32 q12, q9, q15 + vmul.f32 q7, q7, q10 + vmul.f32 q8, q8, q11 + vmul.f32 q9, q9, q12 + vrecps.f32 q10, q7, q13 + vrecps.f32 q11, q8, q14 + vrecps.f32 q12, q9, q15 + vmul.f32 q7, q7, q10 // q7 <- 1/q13 + vmul.f32 q8, q8, q11 + vmul.f32 q9, q9, q12 + + vmul.f32 q10, q4, q7 + vmul.f32 q11, q5, q8 + vmul.f32 q12, q6, q9 + + vstmia r0!, { q10, q11, q12 } + + subs r1, #12 + cmp r1, #12 + bge .loop_3 + + cmp r1, #0; + beq .return + +.loop: + vldmia r0, { q4 } // q4 <- x + + vdup.32 q15, d0[0] + vmax.f32 q4, q15 + vdup.32 q15, d0[1] + vmin.f32 q4, q15 + + vmul.f32 q7, q4, q4 // q7 <- x2 + + vdup.32 q10, d1[0] + vdup.32 q13, d1[1] + vmla.f32 q13, q7, q10 + vdup.32 q10, d2[0] + vmla.f32 q10, q13, q7 + vdup.32 q13, d2[1] + vmla.f32 q13, q7, q10 + vdup.32 q10, d3[0] + vmla.f32 q10, q13, q7 + vdup.32 q13, d3[1] + vmla.f32 q13, q7, q10 + vdup.32 q10, d4[0] + vmla.f32 q10, q13, q7 + vmul.f32 q4, q4, q10 // q4 <- numerator + + vdup.32 q10, d4[1] + vdup.32 q13, d5[0] + vmla.f32 q13, q7, q10 + vdup.32 q10, d5[1] + vmla.f32 q10, q13, q7 + vdup.32 q13, d6[0] + vmla.f32 q13, q7, q10 // q13 <- denum + + vrecpe.f32 q7, q13 + vrecps.f32 q10, q7, q13 + vmul.f32 q7, q7, q10 + vrecps.f32 q10, q7, q13 + vmul.f32 q7, q7, q10 // q7 <- 1/q13 + + vmul.f32 q10, q4, q7 + + vstmia r0!, { q10 } + + subs r1, #4; + bne .loop + +.return: + vpop { q4-q7 } + bx lr + +.coeffs_num: + .float -8.9 // low + .float 8.9 // high + .float -8.488492677e-14 // alpha_13 + .float 5.277853000e-11 + + .float -2.022500419e-8 + .float 0.00001115424833 + .float 0.003103950131 + .float 0.1308400453 + + .float 0.9999999934 + .float 0.0002546136580 // beta_6 + .float 0.02449515379 + .float 0.4641733162 + + .float 1.0 + .float 0 // padding + .float 0 // padding + .float 0 // padding diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/dispatcher.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/dispatcher.tmpliq new file mode 100644 index 0000000..2c5e910 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armv7neon/dispatcher.tmpliq @@ -0,0 +1,38 @@ +// vim: ft=arm + +.non_linear: + +.non_linear_loop_entry: + sub r0, #20 + +.non_linear_loop: + add r0, #20 + ldm r0, { r2, r3, r4, r5, r6 } + + cmp r2, #{{ jump_table | size }} + movgt r2, #{{ jump_table | size }} + cmp r2, #0 + movlt r2, #{{ jump_table | size }} + + add pc, pc, r2, LSL#2 + nop // pc in Rn above is start of the add instruction + 8, hence a nop is needed + // This is A32 asm, for T32/Thump2 use nop.w and b.w to avoid problems. +{% for j in jump_table %} + b .{{j}} +{% endfor %} + b .unsupported + + +.unsupported: + mov r0, #1 + b .return + +.done: + mov r0, #0 + b .return + +.clear: +{% for r in (4..15) %} + veor q{{r}}, q{{r}}, q{{r}} +{% endfor %} + b .non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armvfpv2/armvfpv2_mmm_f32_4x4.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armvfpv2/armvfpv2_mmm_f32_4x4.tmpl new file mode 100644 index 0000000..1b61714 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armvfpv2/armvfpv2_mmm_f32_4x4.tmpl @@ -0,0 +1,479 @@ +// vim: ft=arm + + .arm + .text + .global armvfpv2_mmm_f32_4x4_{{suffix}} + .type armvfpv2_mmm_f32_4x4_{{suffix}}, %function + +// C tile: + +// s16 s20 s24 s28 +// s17 s21 s25 s29 +// s18 s22 s26 s30 +// s19 s23 s27 s31 + +// packed A: (2x4) alternating between (s0-s3) and (s4-s7) +// packed B: (2x4) alternating between (s8-s11) and (s12-15) + +// all vfp registers in use. + +armvfpv2_mmm_f32_4x4_{{suffix}}: + +/* + pld [r1] + pld [r1, #8] + pld [r2] + pld [r2, #8] +*/ + + push { r4-r12 } // no lr (we're a leaf), no fp. #24 bytes + + ldr r8, [sp, #28] + ldr r9, [sp, #24] + +// r8=rsc, r9=csc + + vmrs r6, FPSCR + bic r6, r6, #0x00370000 + vmsr FPSCR, r6 + + vpush { s16-s31 } + +{% include "dispatcher.tmpliq" %} + +.clear: + eor r6, r6 + vmov s16, r6 + vmov.f32 s17, s16 + vmov.f32 s18, s16 + vmov.f32 s19, s16 + vmov.f32 s20, s16 + vmov.f32 s21, s16 + vmov.f32 s22, s16 + vmov.f32 s23, s16 + vmov.f32 s24, s16 + vmov.f32 s25, s16 + vmov.f32 s26, s16 + vmov.f32 s27, s16 + vmov.f32 s28, s16 + vmov.f32 s29, s16 + vmov.f32 s30, s16 + vmov.f32 s31, s16 + b .non_linear_loop + +.add_mat_mul: + // r3 <- k, r4 <- a, r5 <- b + cmp r3, #0 + beq .non_linear_loop + + mov r1, r4 // packed A ptr + pld [r3] + pld [r5] + + .packed_packed: + cmp r3, #4 + blt .packed_packed_loop_1 + + .packed_packed_loop_4: + + // 1 + vldmia r1!, { s0, s1 } + vldmia r5!, { s8, s9 } + + vmla.f32 s16, s0, s8 + vldmia r1!, { s2, s3 } + vmla.f32 s17, s1, s8 + vldmia r5!, { s10, s11 } + vmla.f32 s18, s2, s8 + vmla.f32 s19, s3, s8 + + vmla.f32 s20, s0, s9 + vmla.f32 s21, s1, s9 + vmla.f32 s22, s2, s9 + vmla.f32 s23, s3, s9 + + vldmia r1!, { s4-s7 } + vmla.f32 s24, s0, s10 + vmla.f32 s25, s1, s10 + vmla.f32 s26, s2, s10 + vmla.f32 s27, s3, s10 + + vldmia r5!, { s12-s15 } + vmla.f32 s28, s0, s11 + vmla.f32 s29, s1, s11 + vmla.f32 s30, s2, s11 + vmla.f32 s31, s3, s11 + + // 2 + vmla.f32 s16, s4, s12 + vmla.f32 s17, s5, s12 + vmla.f32 s18, s6, s12 + vmla.f32 s19, s7, s12 + + vldmia r1!, { s0-s3 } + + vmla.f32 s20, s4, s13 + vmla.f32 s21, s5, s13 + vmla.f32 s22, s6, s13 + vmla.f32 s23, s7, s13 + + vldmia r5!, { s8-s11 } + + vmla.f32 s24, s4, s14 + vmla.f32 s25, s5, s14 + vmla.f32 s26, s6, s14 + vmla.f32 s27, s7, s14 + + vmla.f32 s28, s4, s15 + vmla.f32 s29, s5, s15 + vmla.f32 s30, s6, s15 + vmla.f32 s31, s7, s15 + + // 3 + vmla.f32 s16, s0, s8 + vmla.f32 s17, s1, s8 + vmla.f32 s18, s2, s8 + vmla.f32 s19, s3, s8 + + vldmia r1!, { s4-s7 } + + vmla.f32 s20, s0, s9 + vmla.f32 s21, s1, s9 + vmla.f32 s22, s2, s9 + vmla.f32 s23, s3, s9 + + vldmia r5!, { s12-s15 } + + vmla.f32 s24, s0, s10 + vmla.f32 s25, s1, s10 + vmla.f32 s26, s2, s10 + vmla.f32 s27, s3, s10 + + pld [r1] + + vmla.f32 s28, s0, s11 + vmla.f32 s29, s1, s11 + vmla.f32 s30, s2, s11 + vmla.f32 s31, s3, s11 + + pld [r6] + + // 4 + vmla.f32 s16, s4, s12 + vmla.f32 s17, s5, s12 + vmla.f32 s18, s6, s12 + vmla.f32 s19, s7, s12 + + vmla.f32 s20, s4, s13 + vmla.f32 s21, s5, s13 + vmla.f32 s22, s6, s13 + vmla.f32 s23, s7, s13 + + vmla.f32 s24, s4, s14 + vmla.f32 s25, s5, s14 + vmla.f32 s26, s6, s14 + vmla.f32 s27, s7, s14 + + vmla.f32 s28, s4, s15 + vmla.f32 s29, s5, s15 + vmla.f32 s30, s6, s15 + vmla.f32 s31, s7, s15 + + sub r3, r3, #4 + cmp r3, #4 + bge .packed_packed_loop_4 + + cmp r3, #0 + beq .non_linear_loop + + .packed_packed_loop_1: + + vldmia r1!, { s0, s1 } + vldmia r5!, { s8, s9 } + + vmla.f32 s16, s0, s8 + vldmia r1!, { s2, s3 } + vmla.f32 s17, s1, s8 + vldmia r5!, { s10, s11 } + vmla.f32 s18, s2, s8 + vmla.f32 s19, s3, s8 + + vmla.f32 s20, s0, s9 + vmla.f32 s21, s1, s9 + vmla.f32 s22, s2, s9 + vmla.f32 s23, s3, s9 + + vmla.f32 s24, s0, s10 + vmla.f32 s25, s1, s10 + vmla.f32 s26, s2, s10 + vmla.f32 s27, s3, s10 + + vmla.f32 s28, s0, s11 + vmla.f32 s29, s1, s11 + vmla.f32 s30, s2, s11 + vmla.f32 s31, s3, s11 + + subs r3, r3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +.add_unicast: + {% for col in (0..3) %} + mov r8, r3 + {% for reg in (0..3) %} + vldr s0, [ r8 ] + vadd.f32 s{{col|times:4|plus:reg|plus:16}}, s{{col|times:4|plus:reg|plus:16}}, s0 + {% if reg < 3 %} + add r8, r8, r4 + {% endif %} + {% endfor %} + {% if col < 3 %} + add r3, r3, r5 + {% endif %} + {% endfor %} + + b .non_linear_loop + +.scalar_min: + vmov s0, r3 + {% for reg in (16..31) %} + vcmp.f32 s{{reg}}, s0 + vmrs apsr_nzcv, fpscr + vmovge s{{reg}}, s0 + {% endfor %} + + b .non_linear_loop + +.scalar_max: + vmov s0, r3 + {% for reg in (16..31) %} + vcmp.f32 s{{reg}}, s0 + vmrs apsr_nzcv, fpscr + vmovle s{{reg}}, s0 + {% endfor %} + + b .non_linear_loop + +.scalar_add: + vmov s0, r3 + {% for s in (16..31) %} + vadd.f32 s{{s}}, s{{s}}, s0 + {% endfor %} + + b .non_linear_loop + +.scalar_mul: + vmov s0, r3 + {% for s in (16..31) %} + vmul.f32 s{{s}}, s{{s}}, s0 + {% endfor %} + + b .non_linear_loop + +.scalar_sub: + vmov s0, r3 + {% for s in (16..31) %} + vsub.f32 s{{s}}, s0, s{{s}} + {% endfor %} + + b .non_linear_loop + +.scalar_sub_flipped: + vmov s0, r3 + {% for s in (16..31) %} + vsub.f32 s{{s}}, s{{s}}, s0 + {% endfor %} + + b .non_linear_loop + + +.per_row_min: + vldm r3, {s0, s1, s2, s3} + {% for row in (0..3) %} + {% for col in (0..3) %} + {%capture s%}s{{col|times:4|plus:row|plus:16}}{%endcapture%} + vcmp.f32 {{s}}, s{{row}} + vmrs apsr_nzcv, fpscr + vmovge {{s}}, s{{row}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_row_max: + vldm r3, {s0, s1, s2, s3} + {% for row in (0..3) %} + {% for col in (0..3) %} + {%capture s%}s{{col|times:4|plus:row|plus:16}}{%endcapture%} + vcmp.f32 {{s}}, s{{row}} + vmrs apsr_nzcv, fpscr + vmovlt {{s}}, s{{row}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_row_add: + vldm r3, {s0, s1, s2, s3} + {% for row in (0..3) %} + {% for col in (0..3) %} + vadd.f32 s{{col|times:4|plus:row|plus:16}}, s{{col|times:4|plus:row|plus:16}}, s{{row}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_row_mul: + vldm r3, {s0, s1, s2, s3} + {% for row in (0..3) %} + {% for col in (0..3) %} + vmul.f32 s{{col|times:4|plus:row|plus:16}}, s{{col|times:4|plus:row|plus:16}}, s{{row}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_row_sub: + vldm r3, {s0, s1, s2, s3} + {% for row in (0..3) %} + {% for col in (0..3) %} + vsub.f32 s{{col|times:4|plus:row|plus:16}}, s{{row}}, s{{col|times:4|plus:row|plus:16}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_row_sub_flipped: + vldm r3, {s0, s1, s2, s3} + {% for row in (0..3) %} + {% for col in (0..3) %} + vsub.f32 s{{col|times:4|plus:row|plus:16}}, s{{col|times:4|plus:row|plus:16}}, s{{row}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_col_min: + vldm r3, {s0, s1, s2, s3} + {% for row in (0..3) %} + {% for col in (0..3) %} + {%capture s%}s{{col|times:4|plus:row|plus:16}}{%endcapture%} + vcmp.f32 {{s}}, s{{col}} + vmrs apsr_nzcv, fpscr + vmovge {{s}}, s{{col}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_col_max: + vldm r3, {s0, s1, s2, s3} + {% for row in (0..3) %} + {% for col in (0..3) %} + {%capture s%}s{{col|times:4|plus:row|plus:16}}{%endcapture%} + vcmp.f32 {{s}}, s{{col}} + vmrs apsr_nzcv, fpscr + vmovlt {{s}}, s{{col}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_col_add: + vldm r3, {s0, s1, s2, s3} + {% for row in (0..3) %} + {% for col in (0..3) %} + vadd.f32 s{{col|times:4|plus:row|plus:16}}, s{{col|times:4|plus:row|plus:16}}, s{{col}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_col_mul: + vldm r3, {s0, s1, s2, s3} + {% for row in (0..3) %} + {% for col in (0..3) %} + vmul.f32 s{{col|times:4|plus:row|plus:16}}, s{{col|times:4|plus:row|plus:16}}, s{{col}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_col_sub: + vldm r3, {s0, s1, s2, s3} + {% for row in (0..3) %} + {% for col in (0..3) %} + vsub.f32 s{{col|times:4|plus:row|plus:16}}, s{{col}}, s{{col|times:4|plus:row|plus:16}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_col_sub_flipped: + vldm r3, {s0, s1, s2, s3} + {% for row in (0..3) %} + {% for col in (0..3) %} + vsub.f32 s{{col|times:4|plus:row|plus:16}}, s{{col|times:4|plus:row|plus:16}}, s{{col}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + vldmia r3!, { s0, s1 } + vldmia r4!, { s8, s9 } + + vmla.f32 s16, s0, s8 + vldmia r3!, { s2, s3 } + vmla.f32 s17, s1, s8 + vldmia r4!, { s10, s11 } + vmla.f32 s18, s2, s8 + vmla.f32 s19, s3, s8 + + vmla.f32 s20, s0, s9 + vmla.f32 s21, s1, s9 + vmla.f32 s22, s2, s9 + vmla.f32 s23, s3, s9 + + vmla.f32 s24, s0, s10 + vmla.f32 s25, s1, s10 + vmla.f32 s26, s2, s10 + vmla.f32 s27, s3, s10 + + vmla.f32 s28, s0, s11 + vmla.f32 s29, s1, s11 + vmla.f32 s30, s2, s11 + vmla.f32 s31, s3, s11 + + b .non_linear_loop + +.store: + {% for col in (0..3) %} + mov r8, r3 + {% for reg in (0..3) %} + fsts s{{col|times:4|plus:reg|plus:16}}, [ r8 ] + {% if reg < 3 %} + add r8, r8, r4 + {% endif %} + {% endfor %} + {% if col < 3 %} + add r3, r3, r5 + {% endif %} + {% endfor %} + + mov r0, #0 + b .return + + +.q_scale: +.q_shl: +.q_shr: + b .unsupported + +.return: + vpop { s16-s31 } + pop { r4-r12 } + + bx lr + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armvfpv2/dispatcher.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armvfpv2/dispatcher.tmpliq new file mode 100644 index 0000000..5386a42 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm32/armvfpv2/dispatcher.tmpliq @@ -0,0 +1,32 @@ +// vim: ft=arm + +.non_linear: + +.non_linear_loop_entry: + sub r0, #20 + +.non_linear_loop: + add r0, #20 + ldm r0, { r2, r3, r4, r5, r6 } + + cmp r2, #{{ jump_table | size }} + movgt r2, #{{ jump_table | size }} + cmp r2, #0 + movlt r2, #{{ jump_table | size }} + + add pc, pc, r2, LSL#2 + nop // pc in Rn above is start of the add instruction + 8, hence a nop is needed + // This is A32 asm, for T32/Thump2 use nop.w and b.w to avoid problems. +{% for j in jump_table %} + b .{{j}} +{% endfor %} + b .unsupported + +.unsupported: + mov r0, #1 + b .return + +.done: + mov r0, #0 + b .return + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_8h_per_col.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_8h_per_col.tmpliq new file mode 100644 index 0000000..67c5f2b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_8h_per_col.tmpliq @@ -0,0 +1,35 @@ +// vim: ft=arm + +.{{label}}: + ldr x2, [x0, #8] + +{% capture mr_over_8 %}{{ mr | divided_by: 8}}{%endcapture%} +{% capture cols%}{{to | plus: 1| minus:from| divided_by:mr_over_8}}{%endcapture%} + +{% capture loads %}{{cols | divided_by: 8}}{% endcapture %} + +{%if cols == "1" %} + ld1 {v0.h}[0], [ x2 ] +{% elsif cols == "4" %} + ldr d0, [ x2 ] +{% else %} + {% for reg in (1..loads) %} + ldr q{{reg |minus:1}}, [ x2 ], #16 + {% endfor %} +{% endif %} + +// mr:{{mr}} {{ loads }} {{cols}} + +{% for col in (1..cols) %} + dup v3.8h, v{{col| minus: 1|divided_by:8}}.h[{{col| minus: 1|modulo:8}}] + {% for row in (1..mr_over_8) %} + {% capture acc %}{{ col|minus:1|times:mr_over_8|plus:row|minus:1|plus:from }}{% endcapture %} + {% if flipped %} + {{op}} v{{acc}}.8h, v{{acc}}.8h, v3.8h + {% else %} + {{op}} v{{acc}}.8h, v3.8h, v{{acc}}.8h + {% endif %} + {% endfor %} +{% endfor %} + +b .non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_8h_per_row.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_8h_per_row.tmpliq new file mode 100644 index 0000000..f756344 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_8h_per_row.tmpliq @@ -0,0 +1,25 @@ +// vim: ft=arm + +.{{label}}: + ldr x2, [x0, #8] + +{% capture mr_over_8 %}{{ mr | divided_by: 8 }}{%endcapture%} +{% capture mr_over_8_min_1 %}{{ mr | divided_by: 8 | minus: 1 }}{%endcapture%} + +{% for reg in (0..mr_over_8_min_1) %} + ldr q{{reg}}, [ x2 ], #16 +{% endfor %} + +{% if flipped %} + {% for acc in (from..to) %} + {% capture other%}{{acc | minus: from | modulo: mr_over_8}}{%endcapture%} + {{op}} v{{acc}}.8h, v{{acc}}.8h, v{{other}}.8h + {% endfor %} +{% else %} + {% for acc in (from..to) %} + {% capture other%}{{acc | minus: from | modulo: mr_over_8}}{%endcapture%} + {{op}} v{{acc}}.8h, v{{other}}.8h, v{{acc}}.8h + {% endfor %} +{% endif %} + +b .non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_8h_scalar.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_8h_scalar.tmpliq new file mode 100644 index 0000000..1916c26 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_8h_scalar.tmpliq @@ -0,0 +1,18 @@ +// vim: ft=arm + +.{{label}}: + add x2, x0, #8 + ld1 {v0.h}[0], [ x2 ] + dup v0.8h, v0.h[0] + {% if flipped %} + {% for reg in (from..to) %} + {{op}} v{{reg}}.8h, v{{reg}}.8h, v0.8h + {% endfor %} + {% else %} + {% for reg in (from..to) %} + {{op}} v{{reg}}.8h, v0.8h, v{{reg}}.8h + {% endfor %} + {% endif %} + + b .non_linear_loop + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_128x1/loop1/cortex_a53.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_128x1/loop1/cortex_a53.tmpli new file mode 100644 index 0000000..410816d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_128x1/loop1/cortex_a53.tmpli @@ -0,0 +1,65 @@ + fmla v16.4s, v0.4s, v8.s[0] + ldr x5, [x1, #128] + fmla v17.4s, v1.4s, v8.s[0] + ldr x6, [x1, #136] + fmla v18.4s, v2.4s, v8.s[0] + ldr x7, [x1, #144] + fmla v19.4s, v3.4s, v8.s[0] + ldr x9, [x1, #152] + ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [ x1 ], #64 + + fmla v20.4s, v4.4s, v8.s[0] + ldr x10, [x1, #96] + fmla v21.4s, v5.4s, v8.s[0] + ldr x11, [x1, #104] + fmla v22.4s, v6.4s, v8.s[0] + ldr x12, [x1, #112] + fmla v23.4s, v7.4s, v8.s[0] + ldr x13, [x1, #120] + + ld1 {{ v4.4s, v5.4s, v6.4s, v7.4s }}, [ x1 ] + + fmla v24.4s, v0.4s, v8.s[0] + ldr x14, [x1, #128] + fmla v25.4s, v1.4s, v8.s[0] + ldr x15, [x1, #136] + fmla v26.4s, v2.4s, v8.s[0] + ldr x20, [x1, #144] + fmla v27.4s, v3.4s, v8.s[0] + ldr x21, [x1, #152] + fmla v28.4s, v4.4s, v8.s[0] + ldr x22, [x1, #160] + fmla v29.4s, v5.4s, v8.s[0] + ldr x23, [x1, #168] + fmla v30.4s, v6.4s, v8.s[0] + ldr x24, [x1, #176] + fmla v31.4s, v7.4s, v8.s[0] + ldr x25, [x1, #184] + + ld1 {{ v8.s }}[0], [ x2 ], #4 + + prfm pldl1keep, [x1, #1024] + prfm pldl1keep, [x1, #1088] + prfm pldl1keep, [x1, #1152] + prfm pldl1keep, [x1, #1216] + prfm pldl1keep, [x2, #256] + + ins v0.d[0], x5 + ins v1.d[0], x7 + ins v2.d[0], x10 + ins v3.d[0], x12 + ins v4.d[0], x14 + ins v5.d[0], x20 + ins v6.d[0], x22 + ins v7.d[0], x24 + + ins v0.d[1], x6 + ins v1.d[1], x9 + ins v2.d[1], x11 + ins v3.d[1], x13 + ins v4.d[1], x15 + ins v5.d[1], x21 + ins v6.d[1], x23 + ins v7.d[1], x25 + + add x1, x1, #192 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_128x1/loop1/naive.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_128x1/loop1/naive.tmpli new file mode 100644 index 0000000..367339e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_128x1/loop1/naive.tmpli @@ -0,0 +1,32 @@ + ld1 {{ v9.8h, v10.8h, v11.8h, v12.8h }}, [x1], #64 + ld1 {{ v13.8h, v14.8h, v15.8h }}, [x1], #48 + + fmla v16.8h, v0.8h, v8.h[0] + fmla v17.8h, v1.8h, v8.h[0] + fmla v18.8h, v2.8h, v8.h[0] + fmla v19.8h, v3.8h, v8.h[0] + fmla v20.8h, v4.8h, v8.h[0] + fmla v21.8h, v5.8h, v8.h[0] + fmla v22.8h, v6.8h, v8.h[0] + fmla v23.8h, v7.8h, v8.h[0] + fmla v24.8h, v9.8h, v8.h[0] + ld1 {{ v9.8h }}, [ x1 ], #16 + ld1 {{ v0.8h, v1.8h, v2.8h, v3.8h }}, [x1], #64 + ld1 {{ v4.8h, v5.8h, v6.8h, v7.8h }}, [x1], #64 + fmla v25.8h, v10.8h, v8.h[0] + fmla v26.8h, v11.8h, v8.h[0] + fmla v27.8h, v12.8h, v8.h[0] + fmla v28.8h, v13.8h, v8.h[0] + fmla v29.8h, v14.8h, v8.h[0] + fmla v30.8h, v15.8h, v8.h[0] + + fmla v31.8h, v9.8h, v8.h[0] + + ld1 {{ v8.h }}[0], [ x2 ], #2 + + prfm pldl1keep, [x1, #1024] + prfm pldl1keep, [x1, #1088] + prfm pldl1keep, [x1, #1152] + prfm pldl1keep, [x1, #1216] + prfm pldl1keep, [x2, #256] + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_128x1/loop2/cortex_a55.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_128x1/loop2/cortex_a55.tmpli new file mode 100644 index 0000000..821ed3f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_128x1/loop2/cortex_a55.tmpli @@ -0,0 +1,85 @@ + ld1 {{ v9.4s, v10.4s, v11.4s }}, [x1], #48 + + fmla v16.8h, v0.8h, v8.h[0] + ldr w8, [x2], #4 + fmla v17.8h, v1.8h, v8.h[0] + ldr d12, [x1], #8 + fmla v18.8h, v2.8h, v8.h[0] + ldr x12, [x1], #8 + fmla v19.8h, v3.8h, v8.h[0] + ldr d13, [x1], #8 + fmla v20.8h, v4.8h, v8.h[0] + ldr x13, [x1], #8 + fmla v21.8h, v5.8h, v8.h[0] + ldr d14, [x1], #8 + fmla v22.8h, v6.8h, v8.h[0] + ldr x14, [x1], #8 + fmla v23.8h, v7.8h, v8.h[0] + ldr d15, [x1], #8 + fmla v24.8h, v9.8h, v8.h[0] + ldr x15, [x1], #8 + + ld1 {{ v0.8h, v1.8h, v2.8h, v3.8h }}, [x1], #64 + ins v8.s[1], w8 + ld1 {{ v4.8h, v5.8h, v6.8h, v7.8h }}, [x1], #64 + + fmla v25.8h, v10.8h, v8.h[0] + ins v12.d[1], x12 + fmla v26.8h, v11.8h, v8.h[0] + ins v13.d[1], x13 + fmla v27.8h, v12.8h, v8.h[0] + ins v14.d[1], x14 + fmla v28.8h, v13.8h, v8.h[0] + ins v15.d[1], x15 + + ld1 {{ v9.8h, v10.8h, v11.8h, v12.8h }}, [x1], #64 + + fmla v29.8h, v14.8h, v8.h[0] + ldr d13, [x1], #8 + fmla v30.8h, v15.8h, v8.h[0] + ldr x13, [x1], #8 + fmla v31.8h, v0.8h, v8.h[0] + ldr d14, [x1], #8 + + fmla v16.8h, v1.8h, v8.h[2] + ldr x14, [x1], #8 + fmla v17.8h, v2.8h, v8.h[2] + ldr d15, [x1], #8 + fmla v18.8h, v3.8h, v8.h[2] + ldr x15, [x1], #8 + fmla v19.8h, v4.8h, v8.h[2] + + ld1 {{ v0.8h }}, [x1], #16 + + fmla v20.8h, v5.8h, v8.h[2] + ldr d1, [x1], #8 + fmla v21.8h, v6.8h, v8.h[2] + ldr x10, [x1], #8 + + fmla v22.8h, v7.8h, v8.h[2] + + fmla v23.8h, v9.8h, v8.h[2] + ins v13.d[1], x13 + fmla v24.8h, v10.8h, v8.h[2] + ins v14.d[1], x14 + fmla v25.8h, v11.8h, v8.h[2] + ins v15.d[1], x15 + + fmla v26.8h, v12.8h, v8.h[2] + prfm pldl1keep, [x1, #1024] + fmla v27.8h, v13.8h, v8.h[2] + ins v1.d[1], x10 + fmla v28.8h, v14.8h, v8.h[2] + prfm pldl1keep, [x1, #1088] + fmla v29.8h, v15.8h, v8.h[2] + prfm pldl1keep, [x1, #1152] + fmla v30.8h, v0.8h, v8.h[2] + prfm pldl1keep, [x1, #1216] + fmla v31.8h, v1.8h, v8.h[2] + prfm pldl1keep, [x2, #256] + + ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [x1], #64 + ins v8.h[0], v8.h[3] + ld1 {{ v4.4s, v5.4s, v6.4s, v7.4s }}, [x1], #64 + + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_128x1_core.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_128x1_core.tmpl new file mode 100644 index 0000000..e0c68f6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_128x1_core.tmpl @@ -0,0 +1,212 @@ +// vim: ft=arm + +// C tile regs: v16 to v31, no need to preserve +// +// v16[0] v18[0] v20[0] v22[0] v24[0] v26[0] v28[0] v30[0] +// v16[1] v18[1] +// v16[2] v18[2] +// v16[3] v18[3] +// +// v17[0] v19[0] v21[0] v23[0] v25[0] v27[0] v29[0] v31[0] +// v17[1] v19[1] +// v17[2] v19[2] +// v17[3] v19[3] + +// no preservation either for v0-v7... +// v8..v15 are callee-preserved +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +{% if needs_pragma == true %} +.cpu generic+fp+simd+fp16 +{% endif %} +.global {{G}}arm64fp16_mmm_f16_128x1_{{core}}_{{suffix}} +{{G}}arm64fp16_mmm_f16_128x1_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.tmpliq" %} + +.add_mat_mul: + ldr x2, [x0, #24] // b + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + sub x3, x3, #1 + + + ld1 { v8.h }[0], [ x2 ], #2 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x1 ], #64 + ld1 { v4.4s, v5.4s, v6.4s, v7.4s }, [ x1 ], #64 + + cmp x3, #0 + beq .packed_packed_loop_1_last + + cmp x3, #4 + blt .packed_packed_loop_1 + +{% capture packed_packed_loop1 %} + {% include "arm64fp16_mmm_f16_128x1/loop1/naive.tmpli" %} +{% endcapture %} + +{% capture packed_packed_loop2 %} + {% include "arm64fp16_mmm_f16_128x1/loop2/cortex_a55.tmpli" %} +{% endcapture %} + +.p2align 4 +.packed_packed_loop_4: + {{ packed_packed_loop2 }} + {{ packed_packed_loop2 }} + + sub x3, x3, #4 + cmp x3, #4 + bge .packed_packed_loop_4 + + cmp x3, #0 + beq .packed_packed_loop_1_last + +.p2align 4 +.packed_packed_loop_1: + {{ packed_packed_loop1 }} + + subs x3, x3, #1 + bne .packed_packed_loop_1 + +// last loop can't read beyond actual input as it's likely not packed and padded +.packed_packed_loop_1_last: + ld1 { v9.8h, v10.8h, v11.8h, v12.8h }, [x1], #64 + ld1 { v13.8h, v14.8h, v15.8h }, [x1], #48 + + fmla v16.8h, v0.8h, v8.h[0] + fmla v17.8h, v1.8h, v8.h[0] + ld1 { v0.8h }, [ x1 ] + fmla v18.8h, v2.8h, v8.h[0] + fmla v19.8h, v3.8h, v8.h[0] + fmla v20.8h, v4.8h, v8.h[0] + fmla v21.8h, v5.8h, v8.h[0] + fmla v22.8h, v6.8h, v8.h[0] + fmla v23.8h, v7.8h, v8.h[0] + + fmla v24.8h, v9.8h, v8.h[0] + fmla v25.8h, v10.8h, v8.h[0] + fmla v26.8h, v11.8h, v8.h[0] + fmla v27.8h, v12.8h, v8.h[0] + fmla v28.8h, v13.8h, v8.h[0] + fmla v29.8h, v14.8h, v8.h[0] + fmla v30.8h, v15.8h, v8.h[0] + fmla v31.8h, v0.8h, v8.h[0] + + b .non_linear_loop + +{% include "arm64fp16_mmm_f16_scalars.tmpliq" from:16, to:31%} +{% include "arm64fp16_mmm_f16_per_rows.tmpliq" mr:128, from:16, to:31%} +{% include "arm64fp16_mmm_f16_per_cols.tmpliq" mr:128, from:16, to:31%} + +.add_unicast: + ldp x5, x6, [x0, #8] // c base ptr, rsc + cmp x6, #2 + beq .do_per_row_add + + {% for reg in (16..31) %} + {% for lane in (0..7) %} + ld1 {v0.h}[{{lane}}], [ x5 ], x6 + {% endfor %} + fadd v{{reg}}.8h, v{{reg}}.8h, v0.8h + {% endfor %} + + b .non_linear_loop + +.do_per_row_add: + ld1 {v0.8h-v3.8h}, [x5], #64 + ld1 {v4.8h-v7.8h}, [x5], #64 + ld1 {v8.8h-v11.8h}, [x5], #64 + ld1 {v12.8h-v15.8h}, [x5], #64 + + {% for r in (0..15) %} + fadd v{{r| plus: 16}}.8h, v{{r | plus: 16}}.8h, v{{r}}.8h + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x3, [x0, #16] + ldr x2, [x0, #8] + + ld1 {v8.h}[0], [ x3 ] + + {% for r in (0..7) %} + ldr q{{r}}, [x2], #16 + {% endfor %} + + fmla v16.8h, v0.8h, v8.h[0] + ldr q0, [x2], #16 + fmla v17.8h, v1.8h, v8.h[0] + ldr q1, [x2], #16 + fmla v18.8h, v2.8h, v8.h[0] + ldr q2, [x2], #16 + fmla v19.8h, v3.8h, v8.h[0] + ldr q3, [x2], #16 + fmla v20.8h, v4.8h, v8.h[0] + ldr q4, [x2], #16 + fmla v21.8h, v5.8h, v8.h[0] + ldr q5, [x2], #16 + fmla v22.8h, v6.8h, v8.h[0] + ldr q6, [x2], #16 + fmla v23.8h, v7.8h, v8.h[0] + ldr q7, [x2], #16 + + fmla v24.8h, v0.8h, v8.h[0] + fmla v25.8h, v1.8h, v8.h[0] + fmla v26.8h, v2.8h, v8.h[0] + fmla v27.8h, v3.8h, v8.h[0] + fmla v28.8h, v4.8h, v8.h[0] + fmla v29.8h, v5.8h, v8.h[0] + fmla v30.8h, v6.8h, v8.h[0] + fmla v31.8h, v7.8h, v8.h[0] + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc$ + + cmp x6, #2 + beq .store_strides_contig + + {% for reg in (16..31) %} + {% for lane in (0..7) %} + st1 { v{{reg}}.h }[{{lane}}], [ x5 ], x6 + {% endfor %} + {% endfor %} + b .non_linear_loop + +.store_strides_contig: + + {% for reg in (16..31) %} + st1 { v{{reg}}.8h }, [ x5 ], #16 + {% endfor %} + b .non_linear_loop + +.return: + + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_16x8/loop1/naive.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_16x8/loop1/naive.tmpli new file mode 100644 index 0000000..a55fe12 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_16x8/loop1/naive.tmpli @@ -0,0 +1,21 @@ + +fmla v16.8h, v0.8h, v4.h[0] +fmla v17.8h, v1.8h, v4.h[0] +fmla v18.8h, v0.8h, v4.h[1] +fmla v19.8h, v1.8h, v4.h[1] +fmla v20.8h, v0.8h, v4.h[2] +fmla v21.8h, v1.8h, v4.h[2] +fmla v22.8h, v0.8h, v4.h[3] +fmla v23.8h, v1.8h, v4.h[3] + +fmla v24.8h, v0.8h, v4.h[4] +fmla v25.8h, v1.8h, v4.h[4] +fmla v26.8h, v0.8h, v4.h[5] +fmla v27.8h, v1.8h, v4.h[5] +fmla v28.8h, v0.8h, v4.h[6] +fmla v29.8h, v1.8h, v4.h[6] +fmla v30.8h, v0.8h, v4.h[7] +fmla v31.8h, v1.8h, v4.h[7] + +ld1 {{ v0.8h, v1.8h }}, [x1], #32 +ld1 {{ v4.8h }}, [x2], #16 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_16x8/loop2/cortex_a55.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_16x8/loop2/cortex_a55.tmpli new file mode 100644 index 0000000..3fef68a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_16x8/loop2/cortex_a55.tmpli @@ -0,0 +1,54 @@ +fmla v16.8h, v0.8h, v4.h[0] +ldr d2, [x1], #8 +fmla v17.8h, v1.8h, v4.h[0] +ldr d6, [x2], #8 +fmla v18.8h, v0.8h, v4.h[1] +ldr x5, [x1], #8 +fmla v19.8h, v1.8h, v4.h[1] +ldr x7, [x2], #8 +fmla v20.8h, v0.8h, v4.h[2] +ldr d3, [x1], #8 +fmla v21.8h, v1.8h, v4.h[2] +fmla v22.8h, v0.8h, v4.h[3] +ldr x6, [x1], #8 +fmla v23.8h, v1.8h, v4.h[3] + +fmla v24.8h, v0.8h, v4.h[4] +fmla v25.8h, v1.8h, v4.h[4] +fmla v26.8h, v0.8h, v4.h[5] +fmla v27.8h, v1.8h, v4.h[5] +fmla v28.8h, v0.8h, v4.h[6] +ins v2.d[1], x5 +fmla v29.8h, v1.8h, v4.h[6] +ins v6.d[1], x7 +fmla v30.8h, v0.8h, v4.h[7] +ins v3.d[1], x6 +fmla v31.8h, v1.8h, v4.h[7] + +fmla v16.8h, v2.8h, v6.h[0] +ldr d0, [x1], #8 +fmla v17.8h, v3.8h, v6.h[0] +ldr d4, [x2], #8 +fmla v18.8h, v2.8h, v6.h[1] +ldr x5, [x1], #8 +fmla v19.8h, v3.8h, v6.h[1] +ldr x7, [x2], #8 +fmla v20.8h, v2.8h, v6.h[2] +ldr d1, [x1], #8 +fmla v21.8h, v3.8h, v6.h[2] +fmla v22.8h, v2.8h, v6.h[3] +ldr x6, [x1], #8 +fmla v23.8h, v3.8h, v6.h[3] + +fmla v24.8h, v2.8h, v6.h[4] +fmla v25.8h, v3.8h, v6.h[4] +fmla v26.8h, v2.8h, v6.h[5] +fmla v27.8h, v3.8h, v6.h[5] +fmla v28.8h, v2.8h, v6.h[6] +ins v0.d[1], x5 +fmla v29.8h, v3.8h, v6.h[6] +ins v4.d[1], x7 +fmla v30.8h, v2.8h, v6.h[7] +ins v1.d[1], x6 +fmla v31.8h, v3.8h, v6.h[7] + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_16x8_core.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_16x8_core.tmpl new file mode 100644 index 0000000..c709846 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_16x8_core.tmpl @@ -0,0 +1,173 @@ +// vim: ft=arm + +// x20..x27 are used, callee-preserved + +// C tile regs: v16 to v31, (scratch) +// +// v16[0] v18[0] v20[0] v22[0] v24[0] v26[0] v28[0] v30[0] +// v16[1] v18[1] +// v16[2] v18[2] +// v16[3] v18[3] +// +// v17[0] v19[0] v21[0] v23[0] v25[0] v27[0] v29[0] v31[0] +// v17[1] v19[1] +// v17[2] v19[2] +// v17[3] v19[3] + +// v8 is used, d8 (lower half) must preserved +// v0-v7 (scratch registers) +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +{% if needs_pragma == true %} +.cpu generic+fp+simd+fp16 +{% endif %} +.global {{G}}arm64fp16_mmm_f16_16x8_{{core}}_{{suffix}} +{{G}}arm64fp16_mmm_f16_16x8_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + str q8, [sp, #-16]! + +{% include "dispatcher.tmpliq" %} + +.add_mat_mul: + ldr x2, [x0, #24] // b + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + +.packed_packed: + ld1 { v0.4s, v1.4s }, [ x1 ], #32 + ld1 { v4.4s }, [ x2 ], #16 + +{% capture packed_packed_loop1 %} + {% include "arm64fp16_mmm_f16_16x8/loop1/naive.tmpli" %} +{% endcapture %} + +{% capture packed_packed_loop2 %} + {% if core == "a55" %} + {% include "arm64fp16_mmm_f16_16x8/loop2/cortex_a55.tmpli" %} + {% else %} + {{ packed_packed_loop1 }} + {{ packed_packed_loop1 }} + {% endif %} +{% endcapture %} + + cmp x3, #4 + blt .packed_packed_loop_1 + +.p2align 4 +.packed_packed_loop_4: + {{ packed_packed_loop2 }} + {{ packed_packed_loop2 }} + + sub x3, x3, #4 + cmp x3, #4 + bge .packed_packed_loop_4 + + + cmp x3, #0 + beq .non_linear_loop + +.p2align 4 +.packed_packed_loop_1: + {{ packed_packed_loop1 }} + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +{% include "arm64fp16_mmm_f16_scalars.tmpliq" from:16, to:31%} +{% include "arm64fp16_mmm_f16_per_rows.tmpliq" mr:16, from:16, to:31 %} +{% include "arm64fp16_mmm_f16_per_cols.tmpliq" mr:16, from:16, to:31 %} + +.add_unicast: + ldp x5, x6, [x0, #8] + ldp x7, x8, [x0, #24] + + {% for col in (8..15) %} + mov x4, x5 + {% for reg in (0..1) %} + {% for lane in (0..7) %} + ld1 {v0.h}[{{lane}}], [ x4 ], x6 + {% endfor %} + fadd v{{col | times:2 | plus: reg}}.8h, v{{col | times:2 | plus: reg}}.8h, v0.8h + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x2, [x0, #8] + ldr x3, [x0, #16] + + ld1 { v0.4s, v1.4s }, [ x2 ], #32 + ld1 { v4.4s }, [ x3 ], #16 + + fmla v16.8h, v0.8h, v4.h[0] + fmla v17.8h, v1.8h, v4.h[0] + fmla v18.8h, v0.8h, v4.h[1] + fmla v19.8h, v1.8h, v4.h[1] + fmla v20.8h, v0.8h, v4.h[2] + fmla v21.8h, v1.8h, v4.h[2] + fmla v22.8h, v0.8h, v4.h[3] + fmla v23.8h, v1.8h, v4.h[3] + + fmla v24.8h, v0.8h, v4.h[4] + fmla v25.8h, v1.8h, v4.h[4] + fmla v26.8h, v0.8h, v4.h[5] + fmla v27.8h, v1.8h, v4.h[5] + fmla v28.8h, v0.8h, v4.h[6] + fmla v29.8h, v1.8h, v4.h[6] + fmla v30.8h, v0.8h, v4.h[7] + fmla v31.8h, v1.8h, v4.h[7] + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x6, #2 + bne .store_strides_generic + + {% for col in (8..15) %} + str q{{col | times:2 }}, [ x5 ] + str q{{col | times:2 | plus: 1}}, [ x5, #16 ] + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.store_strides_generic: + + {% for col in (8..15) %} + mov x4, x5 + {% for reg in (0..1) %} + {% for lane in (0..7) %} + st1 { v{{col | times:2 | plus: reg}}.h }[{{lane}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.return: + ldr q8, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_32x4/loop1/naive.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_32x4/loop1/naive.tmpli new file mode 100644 index 0000000..fa0b848 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_32x4/loop1/naive.tmpli @@ -0,0 +1,21 @@ + +fmla v16.8h, v0.8h, v4.h[0] +fmla v17.8h, v1.8h, v4.h[0] +fmla v18.8h, v2.8h, v4.h[0] +fmla v19.8h, v3.8h, v4.h[0] +fmla v20.8h, v0.8h, v4.h[1] +fmla v21.8h, v1.8h, v4.h[1] +fmla v22.8h, v2.8h, v4.h[1] +fmla v23.8h, v3.8h, v4.h[1] + +fmla v24.8h, v0.8h, v4.h[2] +fmla v25.8h, v1.8h, v4.h[2] +fmla v26.8h, v2.8h, v4.h[2] +fmla v27.8h, v3.8h, v4.h[2] +fmla v28.8h, v0.8h, v4.h[3] +fmla v29.8h, v1.8h, v4.h[3] +fmla v30.8h, v2.8h, v4.h[3] +fmla v31.8h, v3.8h, v4.h[3] + +ld1 {{ v0.8h, v1.8h, v2.8h, v3.8h }}, [ x1 ], #64 +ldr d4, [x2], #8 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_32x4/loop2/cortex_a55.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_32x4/loop2/cortex_a55.tmpli new file mode 100644 index 0000000..2e64319 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_32x4/loop2/cortex_a55.tmpli @@ -0,0 +1,71 @@ +// mul a: v0, v1, v2, v3 b: v4 +// load a: v5(d5/x5), v6(d6,x6), v7(d7,x7), v8(d8, x8) +// load b: v9 as d9 + +fmla v16.8h, v0.8h, v4.h[0] +ldr d5, [x1], #8 +fmla v17.8h, v1.8h, v4.h[0] +ldr d9, [x2], #8 +fmla v18.8h, v2.8h, v4.h[0] +ldr x5, [x1], #8 +fmla v19.8h, v3.8h, v4.h[0] +fmla v20.8h, v0.8h, v4.h[1] +ldr d6, [x1], #8 +fmla v21.8h, v1.8h, v4.h[1] +ldr x6, [x1], #8 +fmla v22.8h, v2.8h, v4.h[1] +ldr d7, [x1], #8 +fmla v23.8h, v3.8h, v4.h[1] +ldr x7, [x1], #8 + +fmla v24.8h, v0.8h, v4.h[2] +ldr d8, [x1], #8 +fmla v25.8h, v1.8h, v4.h[2] +ldr x8, [x1], #8 +fmla v26.8h, v2.8h, v4.h[2] +ins v5.d[1], x5 +fmla v27.8h, v3.8h, v4.h[2] +ins v6.d[1], x6 +fmla v28.8h, v0.8h, v4.h[3] +ins v7.d[1], x7 +fmla v29.8h, v1.8h, v4.h[3] +ins v8.d[1], x8 +fmla v30.8h, v2.8h, v4.h[3] +ins v9.d[1], x9 +fmla v31.8h, v3.8h, v4.h[3] + +// mul a: v5, v6, v7, v8 b: v9 +// load a: v0(d0/x5), v1(d1,x6), v2(d2,x7), v3(d3, x8) +// load b: v4 as d4 + +fmla v16.8h, v5.8h, v9.h[0] +ldr d0, [x1], #8 +fmla v17.8h, v6.8h, v9.h[0] +ldr d4, [x2], #8 +fmla v18.8h, v7.8h, v9.h[0] +ldr x5, [x1], #8 +fmla v19.8h, v8.8h, v9.h[0] +fmla v20.8h, v5.8h, v9.h[1] +ldr d1, [x1], #8 +fmla v21.8h, v6.8h, v9.h[1] +ldr x6, [x1], #8 +fmla v22.8h, v7.8h, v9.h[1] +ldr d2, [x1], #8 +fmla v23.8h, v8.8h, v9.h[1] +ldr x7, [x1], #8 + +fmla v24.8h, v5.8h, v9.h[2] +ldr d3, [x1], #8 +fmla v25.8h, v6.8h, v9.h[2] +ldr x8, [x1], #8 +fmla v26.8h, v7.8h, v9.h[2] +ins v0.d[1], x5 +fmla v27.8h, v8.8h, v9.h[2] +ins v1.d[1], x6 +fmla v28.8h, v5.8h, v9.h[3] +ins v2.d[1], x7 +fmla v29.8h, v6.8h, v9.h[3] +ins v3.d[1], x8 +fmla v30.8h, v7.8h, v9.h[3] +ins v4.d[1], x9 +fmla v31.8h, v8.8h, v9.h[3] diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_32x4_core.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_32x4_core.tmpl new file mode 100644 index 0000000..bb75a8d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_32x4_core.tmpl @@ -0,0 +1,164 @@ +// vim: ft=arm + +// x20..x27 are used, callee-preserved + +// C tile regs: v16 to v31, (scratch) + +// v8 is used, d8 (lower half) must preserved +// v0-v7 (scratch registers) +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +{% if needs_pragma == true %} +.cpu generic+fp+simd+fp16 +{% endif %} +.global {{G}}arm64fp16_mmm_f16_32x4_{{core}}_{{suffix}} +{{G}}arm64fp16_mmm_f16_32x4_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + +{% include "dispatcher.tmpliq" %} + +.add_mat_mul: + ldr x2, [x0, #24] // b + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x1 ], #64 + ldr d4, [x2], #8 + +{% capture packed_packed_loop1 %} + {% include "arm64fp16_mmm_f16_32x4/loop1/naive.tmpli" %} +{% endcapture %} + +{% capture packed_packed_loop2 %} + {% if core == "a55" %} + {% include "arm64fp16_mmm_f16_32x4/loop2/cortex_a55.tmpli" %} + {% else %} + {{ packed_packed_loop1 }} + {{ packed_packed_loop1 }} + {% endif %} +{% endcapture %} + + cmp x3, #4 + blt .packed_packed_loop_1 + +.p2align 4 +.packed_packed_loop_4: + {{ packed_packed_loop2 }} + {{ packed_packed_loop2 }} + + sub x3, x3, #4 + cmp x3, #4 + bge .packed_packed_loop_4 + + cmp x3, #0 + beq .non_linear_loop + +.p2align 4 +.packed_packed_loop_1: + {{ packed_packed_loop1 }} + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +{% include "arm64fp16_mmm_f16_scalars.tmpliq" from:16, to:31%} +{% include "arm64fp16_mmm_f16_per_rows.tmpliq" mr:32, from:16, to:31 %} +{% include "arm64fp16_mmm_f16_per_cols.tmpliq" mr:32, from:16, to:31 %} + +.add_unicast: + ldp x5, x6, [x0, #8] + ldp x7, x8, [x0, #24] + + {% for col in (0..3) %} + mov x4, x5 + {% for reg in (0..3) %} + {% for lane in (0..7) %} + ld1 {v0.h}[{{lane}}], [ x4 ], x6 + {% endfor %} + fadd v{{col | times:4 | plus: 16| plus: reg}}.8h, v{{col | times:4 | plus: 16 | plus: reg}}.8h, v0.8h + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x2, [x0, #8] + ldr x3, [x0, #16] + + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [ x2 ] + ldr d4, [x3] + + fmla v16.8h, v0.8h, v4.h[0] + fmla v17.8h, v1.8h, v4.h[0] + fmla v18.8h, v2.8h, v4.h[0] + fmla v19.8h, v3.8h, v4.h[0] + fmla v20.8h, v0.8h, v4.h[1] + fmla v21.8h, v1.8h, v4.h[1] + fmla v22.8h, v2.8h, v4.h[1] + fmla v23.8h, v3.8h, v4.h[1] + + fmla v24.8h, v0.8h, v4.h[2] + fmla v25.8h, v1.8h, v4.h[2] + fmla v26.8h, v2.8h, v4.h[2] + fmla v27.8h, v3.8h, v4.h[2] + fmla v28.8h, v0.8h, v4.h[3] + fmla v29.8h, v1.8h, v4.h[3] + fmla v30.8h, v2.8h, v4.h[3] + fmla v31.8h, v3.8h, v4.h[3] + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x6, #2 + bne .store_strides_generic + + {% for col in (0..3) %} + str q{{col | times:4 | plus:16 | plus: 0}}, [ x5 ] + str q{{col | times:4 | plus:16 | plus: 1}}, [ x5, #16 ] + str q{{col | times:4 | plus:16 | plus: 2}}, [ x5, #32 ] + str q{{col | times:4 | plus:16 | plus: 3}}, [ x5, #48 ] + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.store_strides_generic: + + {% for col in (0..3) %} + mov x4, x5 + {% for reg in (0..3) %} + {% for lane in (0..7) %} + st1 { v{{col | times:4 | plus: 16 | plus: reg}}.h }[{{lane}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.return: + ldp d8, d9, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_per_cols.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_per_cols.tmpliq new file mode 100644 index 0000000..6e9552b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_per_cols.tmpliq @@ -0,0 +1,9 @@ +// vim: ft=arm + +{% include "arm64fp16_mmm_8h_per_col.tmpliq" label:"per_col_min", op:"fmin", mr:mr, from:from, to:to %} +{% include "arm64fp16_mmm_8h_per_col.tmpliq" label:"per_col_max", op:"fmax", mr:mr, from:from, to:to %} +{% include "arm64fp16_mmm_8h_per_col.tmpliq" label:"per_col_mul", op:"fmul", mr:mr, from:from, to:to %} +{% include "arm64fp16_mmm_8h_per_col.tmpliq" label:"per_col_add", op:"fadd", mr:mr, from:from, to:to %} +{% include "arm64fp16_mmm_8h_per_col.tmpliq" label:"per_col_sub", op:"fsub", mr:mr, from:from, to:to %} +{% include "arm64fp16_mmm_8h_per_col.tmpliq" label:"per_col_sub_flipped", op:"fsub", mr:mr, from:from, to:to, flipped: true%} + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_per_rows.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_per_rows.tmpliq new file mode 100644 index 0000000..477b8db --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_per_rows.tmpliq @@ -0,0 +1,9 @@ +// vim: ft=arm + +{% include "arm64fp16_mmm_8h_per_row.tmpliq" label:"per_row_min", op:"fmin", mr:mr, from:from, to:to %} +{% include "arm64fp16_mmm_8h_per_row.tmpliq" label:"per_row_max", op:"fmax", mr:mr, from:from, to:to %} +{% include "arm64fp16_mmm_8h_per_row.tmpliq" label:"per_row_mul", op:"fmul", mr:mr, from:from, to:to %} +{% include "arm64fp16_mmm_8h_per_row.tmpliq" label:"per_row_add", op:"fadd", mr:mr, from:from, to:to %} +{% include "arm64fp16_mmm_8h_per_row.tmpliq" label:"per_row_sub", op:"fsub", mr:mr, from:from, to:to %} +{% include "arm64fp16_mmm_8h_per_row.tmpliq" label:"per_row_sub_flipped", op:"fsub", mr:mr, from:from, to:to, flipped: true%} + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_scalars.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_scalars.tmpliq new file mode 100644 index 0000000..a2ffa89 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_mmm_f16_scalars.tmpliq @@ -0,0 +1,19 @@ +// vim: ft=arm + +{% include "arm64fp16_mmm_8h_scalar.tmpliq" label:"scalar_min", op:"fmin", from:from, to:to %} +{% include "arm64fp16_mmm_8h_scalar.tmpliq" label:"scalar_max", op:"fmax", from:from, to:to %} +{% include "arm64fp16_mmm_8h_scalar.tmpliq" label:"scalar_mul", op:"fmul", from:from, to:to %} +{% include "arm64fp16_mmm_8h_scalar.tmpliq" label:"scalar_add", op:"fadd", from:from, to:to %} +{% include "arm64fp16_mmm_8h_scalar.tmpliq" label:"scalar_sub", op:"fsub", from:from, to:to %} +{% include "arm64fp16_mmm_8h_scalar.tmpliq" label:"scalar_sub_flipped", op:"fsub", from:from, to:to, flipped:true %} + +.clear: +{% for r in (from..to) %} + eor v{{r}}.8b, v{{r}}.8b, v{{r}}.8b +{% endfor %} + b .non_linear_loop + +.q_scale: +.q_shl: +.q_shr: + b .unsupported diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_sigmoid_f16_8n.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_sigmoid_f16_8n.tmpl new file mode 100644 index 0000000..7d0e76e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_sigmoid_f16_8n.tmpl @@ -0,0 +1,131 @@ +// vim: ft=arm + +// no preservation either for v0-v7 and v16-v31 + +.text +.align 4 + +{% if needs_pragma == true %} +.cpu generic+fp+simd+fp16 +{% endif %} +.global {{G}}arm64fp16_sigmoid_f16_8n_{{suffix}} +{{G}}arm64fp16_sigmoid_f16_8n_{{suffix}}: + + cmp x1, #0 + beq .return + + adr x2, .coeffs_num + ld1 { v0.8h }, [x2] + dup v5.8h, v0.h[0] // v5 <- low, broadcasted + dup v6.8h, v0.h[1] // v6 <- high, broadcasted + dup v7.8h, v0.h[7] // v7 <- half, broadcasted + + cmp x1, #32 + blt .loop + +.loop4: + ld1 { v16.8h, v17.8h, v18.8h, v19.8h }, [x0] + + fmax v16.8h, v16.8h, v5.8h + fmax v17.8h, v17.8h, v5.8h + fmax v18.8h, v18.8h, v5.8h + fmax v19.8h, v19.8h, v5.8h + + fmin v16.8h, v16.8h, v6.8h + fmin v17.8h, v17.8h, v6.8h + fmin v18.8h, v18.8h, v6.8h + fmin v19.8h, v19.8h, v6.8h // v16 <- x + + fmul v20.8h, v16.8h, v16.8h + fmul v21.8h, v17.8h, v17.8h + fmul v22.8h, v18.8h, v18.8h + fmul v23.8h, v19.8h, v19.8h // v20 <- x2 + + dup v28.8h, v0.h[3] + fmla v28.8h, v20.8h, v0.h[2] + dup v29.8h, v0.h[3] + fmla v29.8h, v21.8h, v0.h[2] + dup v30.8h, v0.h[3] + fmla v30.8h, v22.8h, v0.h[2] + dup v31.8h, v0.h[3] + fmla v31.8h, v23.8h, v0.h[2] + + dup v24.8h, v0.h[4] + fmla v24.8h, v20.8h, v28.8h + dup v25.8h, v0.h[4] + fmla v25.8h, v21.8h, v29.8h + dup v26.8h, v0.h[4] + fmla v26.8h, v22.8h, v30.8h + dup v27.8h, v0.h[4] + fmla v27.8h, v23.8h, v31.8h + + fmul v16.8h, v16.8h, v24.8h + fmul v17.8h, v17.8h, v25.8h + fmul v18.8h, v18.8h, v26.8h + fmul v19.8h, v19.8h, v27.8h // v16 <- numerator + + dup v24.8h, v0.h[6] + dup v25.8h, v0.h[6] + dup v26.8h, v0.h[6] + dup v27.8h, v0.h[6] + fmla v24.8h, v20.8h, v0.h[5] + fmla v25.8h, v21.8h, v0.h[5] + fmla v26.8h, v22.8h, v0.h[5] + fmla v27.8h, v23.8h, v0.h[5] // v24 <- denum + + fdiv v16.8h, v16.8h, v24.8h + fdiv v17.8h, v17.8h, v25.8h + fdiv v18.8h, v18.8h, v26.8h + fdiv v19.8h, v19.8h, v27.8h + + fadd v16.8h, v16.8h, v7.8h + fadd v17.8h, v17.8h, v7.8h + fadd v18.8h, v18.8h, v7.8h + fadd v19.8h, v19.8h, v7.8h + + st1 { v16.8h, v17.8h, v18.8h, v19.8h }, [x0], #64 + + subs x1, x1, #32 + cmp x1, #32 + bge .loop4 + + cmp x1, #0 + beq .return + +.loop: + ld1 { v16.8h }, [x0] + + fmax v16.8h, v16.8h, v5.8h + fmin v16.8h, v16.8h, v6.8h // v16 <- x + fmul v20.8h, v16.8h, v16.8h // v20 <- x2 + + dup v28.8h, v0.h[3] + fmla v28.8h, v20.8h, v0.h[2] + dup v24.8h, v0.h[4] + fmla v24.8h, v20.8h, v28.8h + fmul v16.8h, v16.8h, v24.8h // v16 <- numerator + + dup v24.8h, v0.h[6] + fmla v24.8h, v20.8h, v0.h[5] // v24 <- denum + + fdiv v16.8h, v16.8h, v24.8h + fadd v16.8h, v16.8h, v7.8h + + st1 { v16.8h }, [x0], #16 + + subs x1, x1, #8 + bne .loop + +.return: + ret + +.coeffs_num: + {{ -6.92 | float16 }} + {{ 6.92 | float16 }} + {{ -0.0000124702 | float16 }} + {{ 0.00400222 | float16 }} + + {{ 0.249895 | float16 }} + {{ 0.098734 | float16 }} + {{ 1.0 | float16 }} + {{ 0.5 | float16 }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_tanh_f16_8n.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_tanh_f16_8n.tmpl new file mode 100644 index 0000000..cd01f04 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/arm64fp16_tanh_f16_8n.tmpl @@ -0,0 +1,124 @@ +// vim: ft=arm + +// no preservation either for v0-v7 and v16-v31 + +.text +.align 4 + +{% if needs_pragma == true %} +.cpu generic+fp+simd+fp16 +{% endif %} +.global {{G}}arm64fp16_tanh_f16_8n_{{suffix}} +{{G}}arm64fp16_tanh_f16_8n_{{suffix}}: + + cmp x1, #0 + beq .return + + adr x2, .coeffs_num + ld1 { v0.8h }, [x2] + dup v5.8h, v0.h[0] // v5 <- low, broadcasted + dup v6.8h, v0.h[1] // v6 <- high, broadcasted + + cmp x1, #32 + blt .loop + +.loop4: + ld1 { v16.8h, v17.8h, v18.8h, v19.8h }, [x0] + + fmax v16.8h, v16.8h, v5.8h + fmax v17.8h, v17.8h, v5.8h + fmax v18.8h, v18.8h, v5.8h + fmax v19.8h, v19.8h, v5.8h + + fmin v16.8h, v16.8h, v6.8h + fmin v17.8h, v17.8h, v6.8h + fmin v18.8h, v18.8h, v6.8h + fmin v19.8h, v19.8h, v6.8h // v16 <- x + + fmul v20.8h, v16.8h, v16.8h + fmul v21.8h, v17.8h, v17.8h + fmul v22.8h, v18.8h, v18.8h + fmul v23.8h, v19.8h, v19.8h // v20 <- x2 + + dup v24.8h, v0.h[3] + fmla v24.8h, v20.8h, v0.h[2] + dup v25.8h, v0.h[3] + fmla v25.8h, v21.8h, v0.h[2] + dup v26.8h, v0.h[3] + fmla v26.8h, v22.8h, v0.h[2] + dup v27.8h, v0.h[3] + fmla v27.8h, v23.8h, v0.h[2] + + fmul v16.8h, v16.8h, v24.8h + fmul v17.8h, v17.8h, v25.8h + fmul v18.8h, v18.8h, v26.8h + fmul v19.8h, v19.8h, v27.8h // v16 <- numerator + + dup v28.8h, v0.h[5] + fmla v28.8h, v20.8h, v0.h[4] + dup v29.8h, v0.h[5] + fmla v29.8h, v21.8h, v0.h[4] + dup v30.8h, v0.h[5] + fmla v30.8h, v22.8h, v0.h[4] + dup v31.8h, v0.h[5] + fmla v31.8h, v23.8h, v0.h[4] + + dup v24.8h, v0.h[6] + fmla v24.8h, v20.8h, v28.8h + dup v25.8h, v0.h[6] + fmla v25.8h, v21.8h, v29.8h + dup v26.8h, v0.h[6] + fmla v26.8h, v22.8h, v30.8h + dup v27.8h, v0.h[6] + fmla v27.8h, v23.8h, v31.8h // v24 <- denum + + fdiv v16.8h, v16.8h, v24.8h + fdiv v17.8h, v17.8h, v25.8h + fdiv v18.8h, v18.8h, v26.8h + fdiv v19.8h, v19.8h, v27.8h + + st1 { v16.8h, v17.8h, v18.8h, v19.8h }, [x0], #64 + + subs x1, x1, #32 + cmp x1, #32 + bge .loop4 + + cmp x1, #0 + beq .return + +.loop: + ld1 { v16.8h }, [x0] + + fmax v16.8h, v16.8h, v5.8h + fmin v16.8h, v16.8h, v6.8h // v16 <- x + fmul v20.8h, v16.8h, v16.8h // v20 <- x2 + + dup v24.8h, v0.h[3] + fmla v24.8h, v20.8h, v0.h[2] + fmul v16.8h, v16.8h, v24.8h // v16 <- numerator + + dup v28.8h, v0.h[5] + fmla v28.8h, v20.8h, v0.h[4] + dup v24.8h, v0.h[6] + fmla v24.8h, v20.8h, v28.8h // v24 <- denum + + fdiv v16.8h, v16.8h, v24.8h + + st1 { v16.8h }, [x0], #16 + + subs x1, x1, #8 + bne .loop + +.return: + ret + +.coeffs_num: + {{ -3.84 | float16 }} + {{ 3.84 | float16 }} + {{ 0.082654955 | float16 }} // alpha + {{ 0.99963124 | float16 }} + + {{ 0.0065383179 | float16 }} // beta + {{ 0.41401828 | float16 }} + {{ 1.0 | float16 }} + {{ 0 | float16 }} // padding diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/dispatcher.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/dispatcher.tmpliq new file mode 100644 index 0000000..150db46 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/dispatcher.tmpliq @@ -0,0 +1,37 @@ +// vim: ft=arm + +.non_linear: + sub x0, x0, 40 + +.non_linear_loop: + add x0, x0, 40 + ldr x2, [x0] + + mov x4, #{{ jump_table | size }} + + cmp x2, #{{ jump_table | size }} + csel x2, x2, x4, lt + cmp x2, #0 + csel x2, x4, x2, lt + + adr x3, .jmp_table + add x3, x3, x2, LSL#2 + br x3 + +.jmp_table: +{% for j in jump_table %} + b .{{j}} +{% endfor %} + b .unsupported + + add x0, x2, #4000 + b .return + +.unsupported: + mov x0, #1 + b .return + +.done: + mov x0, 0 + b .return + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/dummy_fmla_no_pragma.S b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/dummy_fmla_no_pragma.S new file mode 100644 index 0000000..3af092c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/dummy_fmla_no_pragma.S @@ -0,0 +1,13 @@ +// vim: ft=arm + +// serves as a canary build file to figure out which flag combination will accept half precision fmla + +.text +.align 4 + +// .cpu generic+fp+simd+fp16 +.global foo +foo: + fmla v16.8h, v0.8h, v8.h[0] + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/dummy_fmla_pragma.S b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/dummy_fmla_pragma.S new file mode 100644 index 0000000..6fb6105 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64fp16/dummy_fmla_pragma.S @@ -0,0 +1,13 @@ +// vim: ft=arm + +// serves as a canary build file to figure out which flag combination will accept half precision fmla + +.text +.align 4 + +.cpu generic+fp+simd+fp16 +.global foo +foo: + fmla v16.8h, v0.8h, v8.h[0] + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_4s_per_col.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_4s_per_col.tmpliq new file mode 100644 index 0000000..a4d592f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_4s_per_col.tmpliq @@ -0,0 +1,33 @@ +// vim: ft=arm + +.{{label}}: + ldr x2, [x0, #8] + +{% capture mr_over_4 %}{{ mr | divided_by: 4}}{%endcapture%} +{% capture cols%}{{to | plus: 1| minus:from| divided_by:mr_over_4}}{%endcapture%} + +{% capture loads %}{{cols | divided_by:4}}{% endcapture %} + +{%if cols == "1" %} + ld1 {v0.s}[0], [ x2 ] +{% else %} + {% for reg in (1..loads) %} + ldr q{{reg |minus:1}}, [ x2 ], #16 + {% endfor %} +{% endif %} + +// {{mr}} {{cols}} + +{% for col in (1..cols) %} + dup v3.4s, v{{col| minus: 1|divided_by:4}}.s[{{col| minus: 1|modulo:4}}] + {% for row in (1..mr_over_4) %} + {% capture acc %}{{ col|minus:1|times:mr_over_4|plus:row|minus:1|plus:from }}{% endcapture %} + {% if flipped %} + {{op}} v{{acc}}.4s, v{{acc}}.4s, v3.4s + {% else %} + {{op}} v{{acc}}.4s, v3.4s, v{{acc}}.4s + {% endif %} + {% endfor %} +{% endfor %} + +b .non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_4s_per_row.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_4s_per_row.tmpliq new file mode 100644 index 0000000..1db62f2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_4s_per_row.tmpliq @@ -0,0 +1,25 @@ +// vim: ft=arm + +.{{label}}: + ldr x2, [x0, #8] + +{% capture mr_over_4 %}{{ mr | divided_by: 4}}{%endcapture%} +{% capture mr_over_4_min_1 %}{{ mr | divided_by: 4 | minus: 1}}{%endcapture%} + +{% for reg in (0..mr_over_4_min_1) %} + ldr q{{reg}}, [ x2 ], #16 +{% endfor %} + +{% if flipped %} + {% for acc in (from..to) %} + {% capture other%}{{acc | minus: from | modulo: mr_over_4}}{%endcapture%} + {{op}} v{{acc}}.4s, v{{acc}}.4s, v{{other}}.4s + {% endfor %} +{% else %} + {% for acc in (from..to) %} + {% capture other%}{{acc | minus: from | modulo: mr_over_4}}{%endcapture%} + {{op}} v{{acc}}.4s, v{{other}}.4s, v{{acc}}.4s + {% endfor %} +{% endif %} + +b .non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_4s_scalar.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_4s_scalar.tmpliq new file mode 100644 index 0000000..db2e101 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_4s_scalar.tmpliq @@ -0,0 +1,18 @@ +// vim: ft=arm + +.{{label}}: + add x2, x0, #8 + ld1 {v0.s}[0], [ x2 ] + dup v0.4s, v0.s[0] + {% if flipped %} + {% for reg in (from..to) %} + {{op}} v{{reg}}.4s, v{{reg}}.4s, v0.4s + {% endfor %} + {% else %} + {% for reg in (from..to) %} + {{op}} v{{reg}}.4s, v0.4s, v{{reg}}.4s + {% endfor %} + {% endif %} + + b .non_linear_loop + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_w_no_preload.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_w_no_preload.tmpli new file mode 100644 index 0000000..04deaee --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_w_no_preload.tmpli @@ -0,0 +1,69 @@ +fmla v8.4s, v0.4s, v4.s[0] +ldr w4, [x1], #4 +fmla v9.4s, v1.4s, v4.s[0] +ldr w20, [x2], #4 +fmla v10.4s, v2.4s, v4.s[0] +ldr w5, [x1], #4 + +fmla v11.4s, v0.4s, v4.s[1] +ldr w21, [x2], #4 +fmla v12.4s, v1.4s, v4.s[1] +ldr w6, [x1], #4 +fmla v13.4s, v2.4s, v4.s[1] +ldr w22, [x2], #4 + +fmla v14.4s, v0.4s, v4.s[2] +ldr w7, [x1], #4 +fmla v15.4s, v1.4s, v4.s[2] +ldr w23, [x2], #4 +fmla v16.4s, v2.4s, v4.s[2] +ldr w8, [x1], #4 +fmla v17.4s, v0.4s, v4.s[3] +ldr w24, [x2], #4 +fmla v18.4s, v1.4s, v4.s[3] +ldr w9, [x1], #4 +fmla v19.4s, v2.4s, v4.s[3] +ldr w25, [x2], #4 + +fmla v20.4s, v0.4s, v5.s[0] +ldr w10, [x1], #4 +fmla v21.4s, v1.4s, v5.s[0] +ldr w26, [x2], #4 +fmla v22.4s, v2.4s, v5.s[0] +ldr w11, [x1], #4 +fmla v23.4s, v0.4s, v5.s[1] +ldr w27, [x2], #4 +fmla v24.4s, v1.4s, v5.s[1] +ldr w12, [x1], #4 +fmla v25.4s, v2.4s, v5.s[1] + +fmla v26.4s, v0.4s, v5.s[2] +ldr w13, [x1], #4 +fmla v27.4s, v1.4s, v5.s[2] +fmla v28.4s, v2.4s, v5.s[2] +ldr w14, [x1], #4 +fmla v29.4s, v0.4s, v5.s[3] +fmla v30.4s, v1.4s, v5.s[3] +ldr w15, [x1], #4 +fmla v31.4s, v2.4s, v5.s[3] + +ins v0.s[0], w4 +ins v1.s[0], w8 +ins v2.s[0], w12 +ins v4.s[0], w20 +ins v5.s[0], w24 +ins v0.s[1], w5 +ins v1.s[1], w9 +ins v2.s[1], w13 +ins v4.s[1], w21 +ins v5.s[1], w25 +ins v0.s[2], w6 +ins v1.s[2], w10 +ins v2.s[2], w14 +ins v4.s[2], w22 +ins v5.s[2], w26 +ins v0.s[3], w7 +ins v1.s[3], w11 +ins v2.s[3], w15 +ins v4.s[3], w23 +ins v5.s[3], w27 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_w_preload.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_w_preload.tmpli new file mode 100644 index 0000000..f97e252 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_w_preload.tmpli @@ -0,0 +1,82 @@ +fmla v8.4s, v0.4s, v4.s[0] + ldr w4, [x1] +fmla v9.4s, v1.4s, v4.s[0] + ldr w20, [x2], #4 +fmla v10.4s, v2.4s, v4.s[0] + ldr w5, [x1, #4] + +fmla v11.4s, v0.4s, v4.s[1] + ldr w21, [x2], #4 +fmla v12.4s, v1.4s, v4.s[1] + ldr w6, [x1, #8] +fmla v13.4s, v2.4s, v4.s[1] + ldr w22, [x2], #4 + +fmla v14.4s, v0.4s, v4.s[2] + ldr w7, [x1, #12] +fmla v15.4s, v1.4s, v4.s[2] + ldr w23, [x2], #4 +fmla v16.4s, v2.4s, v4.s[2] + ldr w8, [x1, #16] +fmla v17.4s, v0.4s, v4.s[3] + ldr w24, [x2], #4 +fmla v18.4s, v1.4s, v4.s[3] + ldr w9, [x1, #20] +fmla v19.4s, v2.4s, v4.s[3] + ldr w25, [x2], #4 + +fmla v20.4s, v0.4s, v5.s[0] + ldr w10, [x1, #24] +fmla v21.4s, v1.4s, v5.s[0] + ldr w26, [x2], #4 +fmla v22.4s, v2.4s, v5.s[0] + ldr w11, [x1, #28] +fmla v23.4s, v0.4s, v5.s[1] + ldr w27, [x2], #4 +fmla v24.4s, v1.4s, v5.s[1] + ldr w12, [x1, #32] +fmla v25.4s, v2.4s, v5.s[1] + ldr w13, [x1, #36] + +fmla v26.4s, v0.4s, v5.s[2] + ldr w14, [x1, #40] +fmla v27.4s, v1.4s, v5.s[2] + ldr w15, [x1, #44] +fmla v28.4s, v2.4s, v5.s[2] + prfm pldl1keep, [x1, #512] +fmla v29.4s, v0.4s, v5.s[3] + add x1, x1, #48 +fmla v30.4s, v1.4s, v5.s[3] + prfm pldl1keep, [x2, #384] +fmla v31.4s, v2.4s, v5.s[3] + + ins v0.s[0], w4 + + ins v1.s[0], w8 + ins v2.s[0], w12 + + ins v4.s[0], w20 + ins v5.s[0], w24 + + ins v0.s[1], w5 + ins v1.s[1], w9 + + ins v2.s[1], w13 + ins v4.s[1], w21 + + ins v5.s[1], w25 + ins v0.s[2], w6 + + ins v1.s[2], w10 + ins v2.s[2], w14 + + ins v4.s[2], w22 + ins v5.s[2], w26 + + ins v0.s[3], w7 + ins v1.s[3], w11 + + ins v2.s[3], w15 + ins v4.s[3], w23 + ins v5.s[3], w27 + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_x_preload.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_x_preload.tmpli new file mode 100644 index 0000000..a380bcc --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_x_preload.tmpli @@ -0,0 +1,60 @@ +fmla v8.4s, v0.4s, v4.s[0] + ldr x4, [x1] +fmla v9.4s, v1.4s, v4.s[0] + ldr x20, [x2] +fmla v10.4s, v2.4s, v4.s[0] + ldr x5, [x1, #8] + +fmla v11.4s, v0.4s, v4.s[1] + ldr x21, [x2, #8] +fmla v12.4s, v1.4s, v4.s[1] + ldr x6, [x1, #16] +fmla v13.4s, v2.4s, v4.s[1] + ldr x22, [x2, #16] + +fmla v14.4s, v0.4s, v4.s[2] + ldr x7, [x1, #24] +fmla v15.4s, v1.4s, v4.s[2] + ldr x23, [x2, #24] +fmla v16.4s, v2.4s, v4.s[2] + ldr x8, [x1, #32] +fmla v17.4s, v0.4s, v4.s[3] +fmla v18.4s, v1.4s, v4.s[3] + ldr x9, [x1, #40] +fmla v19.4s, v2.4s, v4.s[3] + +fmla v20.4s, v0.4s, v5.s[0] +fmla v21.4s, v1.4s, v5.s[0] +fmla v22.4s, v2.4s, v5.s[0] +fmla v23.4s, v0.4s, v5.s[1] +fmla v24.4s, v1.4s, v5.s[1] +fmla v25.4s, v2.4s, v5.s[1] + +fmla v26.4s, v0.4s, v5.s[2] +fmla v27.4s, v1.4s, v5.s[2] +fmla v28.4s, v2.4s, v5.s[2] + prfm pldl1keep, [x1, #512] +fmla v29.4s, v0.4s, v5.s[3] + add x1, x1, #48 +fmla v30.4s, v1.4s, v5.s[3] + prfm pldl1keep, [x2, #384] +fmla v31.4s, v2.4s, v5.s[3] + add x2, x2, #32 + + + ins v0.d[0], x4 + ins v2.d[0], x8 + + ins v4.d[0], x20 + ins v5.d[0], x22 + + ins v0.d[1], x5 + ins v2.d[1], x9 + + ins v4.d[1], x21 + ins v1.d[0], x6 + + ins v1.d[1], x7 + + ins v5.d[1], x23 + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/naive.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/naive.tmpli new file mode 100644 index 0000000..67be44d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/naive.tmpli @@ -0,0 +1,34 @@ +fmla v8.4s, v0.4s, v4.s[0] +fmla v9.4s, v1.4s, v4.s[0] +fmla v10.4s, v2.4s, v4.s[0] + +fmla v11.4s, v0.4s, v4.s[1] +fmla v12.4s, v1.4s, v4.s[1] +fmla v13.4s, v2.4s, v4.s[1] + +fmla v14.4s, v0.4s, v4.s[2] +fmla v15.4s, v1.4s, v4.s[2] +fmla v16.4s, v2.4s, v4.s[2] + +fmla v17.4s, v0.4s, v4.s[3] +fmla v18.4s, v1.4s, v4.s[3] +fmla v19.4s, v2.4s, v4.s[3] + +fmla v20.4s, v0.4s, v5.s[0] +fmla v21.4s, v1.4s, v5.s[0] +fmla v22.4s, v2.4s, v5.s[0] + +fmla v23.4s, v0.4s, v5.s[1] +fmla v24.4s, v1.4s, v5.s[1] +fmla v25.4s, v2.4s, v5.s[1] + +fmla v26.4s, v0.4s, v5.s[2] +fmla v27.4s, v1.4s, v5.s[2] +fmla v28.4s, v2.4s, v5.s[2] + +fmla v29.4s, v0.4s, v5.s[3] +fmla v30.4s, v1.4s, v5.s[3] +fmla v31.4s, v2.4s, v5.s[3] + +ld1 {{ v0.4s, v1.4s, v2.4s }}, [x1], #48 +ld1 {{ v4.4s, v5.4s }}, [x2], #32 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop2/cortex_a55.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop2/cortex_a55.tmpli new file mode 100644 index 0000000..f1ba56c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop2/cortex_a55.tmpli @@ -0,0 +1,107 @@ +// mul a: v0, v1, v2, b: v4, v5 +// load a: d3/x23, d6/x26, d7/x27 +// load b: x4, x5, x6, x7 + +fmla v8.4s, v0.4s, v4.s[0] +ldr d3, [x1], #8 +fmla v9.4s, v1.4s, v4.s[0] +ldr x4, [x2], #8 +fmla v10.4s, v2.4s, v4.s[0] +ldr x23, [x1], #8 +fmla v11.4s, v0.4s, v4.s[1] +ldr x5, [x2], #8 +fmla v12.4s, v1.4s, v4.s[1] +ldr d6, [x1], #8 +fmla v13.4s, v2.4s, v4.s[1] +ldr x6, [x2], #8 +fmla v14.4s, v0.4s, v4.s[2] +ldr x26, [x1], #8 +fmla v15.4s, v1.4s, v4.s[2] +ldr x7, [x2], #8 +fmla v16.4s, v2.4s, v4.s[2] +ldr d7, [x1], #8 +fmla v17.4s, v0.4s, v4.s[3] +ldr x27, [x1], #8 +fmla v18.4s, v1.4s, v4.s[3] + +fmla v19.4s, v2.4s, v4.s[3] +fmla v20.4s, v0.4s, v5.s[0] + +// ins b: v4 <- x4/x5 +// ins a: d3/x23, d6/x26, d7/x27 + +ins v4.d[0], x4 +fmla v21.4s, v1.4s, v5.s[0] +ins v4.d[1], x5 +fmla v22.4s, v2.4s, v5.s[0] +fmla v23.4s, v0.4s, v5.s[1] + +fmla v24.4s, v1.4s, v5.s[1] +fmla v25.4s, v2.4s, v5.s[1] +fmla v26.4s, v0.4s, v5.s[2] +fmla v27.4s, v1.4s, v5.s[2] +fmla v28.4s, v2.4s, v5.s[2] +fmla v29.4s, v0.4s, v5.s[3] +ins v3.d[1], x23 +fmla v30.4s, v1.4s, v5.s[3] +ins v6.d[1], x26 +fmla v31.4s, v2.4s, v5.s[3] +ins v7.d[1], x27 + +// mul a: v3, v6, v7, b: v4, v5 +// ins b, v5 <- x6, x7 +// load a: d0/x20, d1/x21, d2/x22 +// load b: x4, x5 + +fmla v8.4s, v3.4s, v4.s[0] +ins v5.d[0], x6 +fmla v9.4s, v6.4s, v4.s[0] +ins v5.d[1], x7 +fmla v10.4s, v7.4s, v4.s[0] +ldr d0, [x1], #8 +fmla v11.4s, v3.4s, v4.s[1] +ldr x4, [x2], #8 +fmla v12.4s, v6.4s, v4.s[1] +ldr x20, [x1], #8 +fmla v13.4s, v7.4s, v4.s[1] +ldr x5, [x2], #8 +fmla v14.4s, v3.4s, v4.s[2] +ldr d1, [x1], #8 +fmla v15.4s, v6.4s, v4.s[2] +ldr x6, [x2], #8 +fmla v16.4s, v7.4s, v4.s[2] +ldr x21, [x1], #8 +fmla v17.4s, v3.4s, v4.s[3] +ldr x7, [x2], #8 + +// load b: x6, x7 +fmla v18.4s, v6.4s, v4.s[3] +ldr d2, [x1], #8 +fmla v19.4s, v7.4s, v4.s[3] +ldr x22, [x1], #8 +fmla v20.4s, v3.4s, v5.s[0] +fmla v21.4s, v6.4s, v5.s[0] +fmla v22.4s, v7.4s, v5.s[0] +fmla v23.4s, v3.4s, v5.s[1] +fmla v24.4s, v6.4s, v5.s[1] +fmla v25.4s, v7.4s, v5.s[1] + +// ins a: d0/x20, d1/x21, d2/x22 +fmla v26.4s, v3.4s, v5.s[2] +ins v0.d[1], x20 +fmla v27.4s, v6.4s, v5.s[2] +ins v1.d[1], x21 +fmla v28.4s, v7.4s, v5.s[2] +ins v2.d[1], x22 + +// ins b: v4 <- x4, x5 +fmla v29.4s, v3.4s, v5.s[3] +ins v4.d[0], x4 +fmla v30.4s, v6.4s, v5.s[3] +ins v4.d[1], x5 +fmla v31.4s, v7.4s, v5.s[3] + +// ins b: v5 <- x6, x7 +ins v5.d[0], x6 +ins v5.d[1], x7 + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_12x8_core.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_12x8_core.tmpl new file mode 100644 index 0000000..4c55bf0 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_12x8_core.tmpl @@ -0,0 +1,162 @@ +// vim: ft=arm + +// C tile regs: +// - x19-x29 to preserve (but x19, x28, x29 not used) +// - d8..d15 to preserve +// - v16 to v31, no need to preserve +// +// v8 v11 v14 v17 v20 v23 v26 v29 +// v9 v12 v15 v18 v21 v24 v27 v30 +// v10 v13 v16 v19 v22 v25 v28 v31 + +// no preservation for v0-v7: +// packed A buffering (2x8 values): rotating over v0..v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +.cpu generic+fp+simd +.global {{G}}arm64simd_mmm_f32_12x8_{{core}}_{{suffix}} +{{G}}arm64simd_mmm_f32_12x8_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.tmpliq" %} + +.add_mat_mul: + ldr x2, [x0, #24] // b + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + + ld1 { v0.4s, v1.4s, v2.4s }, [ x1 ], #48 + ld1 { v4.4s, v5.4s }, [ x2 ], #32 + +{% capture packed_packed_loop1 %} + {% if core == "a53" %} + {% include "arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_x_preload.tmpli" %} + {% else %} + {% include "arm64simd_mmm_f32_12x8/packed_packed_loop1/naive.tmpli" %} + {% endif %} +{% endcapture %} + +{% capture packed_packed_loop2 %} + {% if core == "a55" %} + {% include "arm64simd_mmm_f32_12x8/packed_packed_loop2/cortex_a55.tmpli" %} + {% else %} + {{ packed_packed_loop1 }} + {{ packed_packed_loop1 }} + {% endif %} +{% endcapture %} + + cmp x3, #4 + blt .packed_packed_loop_1 + +.p2align 4 +.packed_packed_loop_4: + {{ packed_packed_loop2 }} + {{ packed_packed_loop2 }} + + sub x3, x3, #4 + cmp x3, #4 + bge .packed_packed_loop_4 + + cmp x3, #0 + beq .non_linear_loop + +.p2align 4 +.packed_packed_loop_1: + {{ packed_packed_loop1 }} + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +{% include "arm64simd_mmm_f32_scalars.tmpliq" from:8, to:31%} +{% include "arm64simd_mmm_f32_per_rows.tmpliq" mr:12, from:8, to:31 %} +{% include "arm64simd_mmm_f32_per_cols.tmpliq" mr:12, from:8, to:31 %} + +.add_unicast: + ldp x5, x6, [x0, #8 ] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + {% for col in (0..7) %} + mov x4, x5 + {% for reg in (0..2) %} + {% for lane in (0..3) %} + ld1 {v0.s}[{{lane}}], [ x4 ], x6 + {% endfor %} + fadd v{{col | times:3 | plus: 8| plus: reg}}.4s, v{{col | times:3 | plus: 8 | plus: reg}}.4s, v0.4s + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x2, [x0, #8] + ldr x3, [x0, #16] + + ld1 { v0.4s, v1.4s, v2.4s }, [ x2 ] + ld1 { v4.4s, v5.4s }, [ x3 ] + + {% for col in (0..7) %} + {% for reg in (0..2) %} + fmla v{{col | times:3 | plus: 8 | plus: reg}}.4s, v{{reg}}.4s, v{{col| divided_by:4 | plus: 4}}.s[{{col| modulo: 4}}] + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x6, #4 + bne .store_strides_generic + + {% for col in (0..7) %} + str q{{col | times:3 | plus: 8 }}, [ x5 ] + str q{{col | times:3 | plus: 9}}, [ x5, #16 ] + str q{{col | times:3 | plus: 10}}, [ x5, #32 ] + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.store_strides_generic: + {% for col in (0..7) %} + mov x4, x5 + {% for reg in (0..2) %} + {% for lane in (0..3) %} + st1 { v{{col | times:3 | plus: 8 | plus: reg}}.s }[{{lane}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.return: + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_16x4/packed_packed_loop1/cortex_a53.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_16x4/packed_packed_loop1/cortex_a53.tmpli new file mode 100644 index 0000000..2ae7a54 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_16x4/packed_packed_loop1/cortex_a53.tmpli @@ -0,0 +1,45 @@ +fmla v16.4s, v0.4s, v4.s[0] +ldr x5, [x1] +fmla v17.4s, v1.4s, v4.s[0] +ldr x6, [x1, #8] +fmla v18.4s, v2.4s, v4.s[0] +ldr x7, [x1, #16] +fmla v19.4s, v3.4s, v4.s[0] +ldr x8, [x1, #24] +fmla v20.4s, v0.4s, v4.s[1] +ldr x9, [x1, #32] +fmla v21.4s, v1.4s, v4.s[1] +ldr x10, [x1, #40] +fmla v22.4s, v2.4s, v4.s[1] +ldr x11, [x1, #48] +fmla v23.4s, v3.4s, v4.s[1] +ldr x12, [x1, #56] + +fmla v24.4s, v0.4s, v4.s[2] +ldr x24, [x2] +fmla v25.4s, v1.4s, v4.s[2] +ldr x25, [x2, #8] +fmla v26.4s, v2.4s, v4.s[2] +add x1, x1, #64 +fmla v27.4s, v3.4s, v4.s[2] +add x2, x2, #16 +fmla v28.4s, v0.4s, v4.s[3] +prfm pldl1keep, [x1, #256] +fmla v29.4s, v1.4s, v4.s[3] +prfm pldl1keep, [x2, #256] +fmla v30.4s, v2.4s, v4.s[3] +prfm pldl1keep, [x1, #256] +fmla v31.4s, v3.4s, v4.s[3] + +ins v0.d[0], x5 +ins v2.d[0], x9 +ins v1.d[0], x7 +ins v3.d[0], x11 +ins v4.d[0], x24 + +ins v0.d[1], x6 +ins v2.d[1], x10 +ins v1.d[1], x8 +ins v3.d[1], x12 +ins v4.d[1], x25 + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_16x4/packed_packed_loop1/naive.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_16x4/packed_packed_loop1/naive.tmpli new file mode 100644 index 0000000..6374665 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_16x4/packed_packed_loop1/naive.tmpli @@ -0,0 +1,21 @@ + +fmla v16.4s, v0.4s, v4.s[0] +fmla v17.4s, v1.4s, v4.s[0] +fmla v18.4s, v2.4s, v4.s[0] +fmla v19.4s, v3.4s, v4.s[0] +fmla v20.4s, v0.4s, v4.s[1] +fmla v21.4s, v1.4s, v4.s[1] +fmla v22.4s, v2.4s, v4.s[1] +fmla v23.4s, v3.4s, v4.s[1] + +fmla v24.4s, v0.4s, v4.s[2] +fmla v25.4s, v1.4s, v4.s[2] +fmla v26.4s, v2.4s, v4.s[2] +fmla v27.4s, v3.4s, v4.s[2] +fmla v28.4s, v0.4s, v4.s[3] +fmla v29.4s, v1.4s, v4.s[3] +fmla v30.4s, v2.4s, v4.s[3] +fmla v31.4s, v3.4s, v4.s[3] + +ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [ x1 ], #64 +ld1 {{ v4.4s }}, [ x2 ], #16 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_16x4/packed_packed_loop2/cortex_a55.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_16x4/packed_packed_loop2/cortex_a55.tmpli new file mode 100644 index 0000000..c0b2f50 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_16x4/packed_packed_loop2/cortex_a55.tmpli @@ -0,0 +1,73 @@ +// mul a: v0, v1, v2, v3 b: v4 +// load a: v5(d5/x5), v6(d6,x6), v7(d7,x7), v8(d8, x8) +// load b: v9(d9/x9) + +fmla v16.4s, v0.4s, v4.s[0] +ldr d5, [x1], #8 +fmla v17.4s, v1.4s, v4.s[0] +ldr d9, [x2], #8 +fmla v18.4s, v2.4s, v4.s[0] +ldr x5, [x1], #8 +fmla v19.4s, v3.4s, v4.s[0] +ldr x9, [x2], #8 +fmla v20.4s, v0.4s, v4.s[1] +ldr d6, [x1], #8 +fmla v21.4s, v1.4s, v4.s[1] +ldr x6, [x1], #8 +fmla v22.4s, v2.4s, v4.s[1] +ldr d7, [x1], #8 +fmla v23.4s, v3.4s, v4.s[1] +ldr x7, [x1], #8 + +fmla v24.4s, v0.4s, v4.s[2] +ldr d8, [x1], #8 +fmla v25.4s, v1.4s, v4.s[2] +ldr x8, [x1], #8 +fmla v26.4s, v2.4s, v4.s[2] +ins v5.d[1], x5 +fmla v27.4s, v3.4s, v4.s[2] +ins v6.d[1], x6 +fmla v28.4s, v0.4s, v4.s[3] +ins v7.d[1], x7 +fmla v29.4s, v1.4s, v4.s[3] +ins v8.d[1], x8 +fmla v30.4s, v2.4s, v4.s[3] +ins v9.d[1], x9 +fmla v31.4s, v3.4s, v4.s[3] + +// mul a: v5, v6, v7, v8 b: v9 +// load a: v0(d0/x5), v1(d1,x6), v2(d2,x7), v3(d3, x8) +// load b: v4(d4/x9) + +fmla v16.4s, v5.4s, v9.s[0] +ldr d0, [x1], #8 +fmla v17.4s, v6.4s, v9.s[0] +ldr d4, [x2], #8 +fmla v18.4s, v7.4s, v9.s[0] +ldr x5, [x1], #8 +fmla v19.4s, v8.4s, v9.s[0] +ldr x9, [x2], #8 +fmla v20.4s, v5.4s, v9.s[1] +ldr d1, [x1], #8 +fmla v21.4s, v6.4s, v9.s[1] +ldr x6, [x1], #8 +fmla v22.4s, v7.4s, v9.s[1] +ldr d2, [x1], #8 +fmla v23.4s, v8.4s, v9.s[1] +ldr x7, [x1], #8 + +fmla v24.4s, v5.4s, v9.s[2] +ldr d3, [x1], #8 +fmla v25.4s, v6.4s, v9.s[2] +ldr x8, [x1], #8 +fmla v26.4s, v7.4s, v9.s[2] +ins v0.d[1], x5 +fmla v27.4s, v8.4s, v9.s[2] +ins v1.d[1], x6 +fmla v28.4s, v5.4s, v9.s[3] +ins v2.d[1], x7 +fmla v29.4s, v6.4s, v9.s[3] +ins v3.d[1], x8 +fmla v30.4s, v7.4s, v9.s[3] +ins v4.d[1], x9 +fmla v31.4s, v8.4s, v9.s[3] diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_16x4_core.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_16x4_core.tmpl new file mode 100644 index 0000000..47ad331 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_16x4_core.tmpl @@ -0,0 +1,173 @@ +// vim: ft=arm + +// C tile regs: v16 to v31, (scratch) +// - x19-x29 to preserve (but x19, x28, x29 not used) +// - d8..d15 to preserve +// - v16 to v31, no need to preserve + +// v8 is used, d8 (lower half) must preserved +// v0-v7 (scratch registers) +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +.cpu generic+fp+simd +.global {{G}}arm64simd_mmm_f32_16x4_{{core}}_{{suffix}} +{{G}}arm64simd_mmm_f32_16x4_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.tmpliq" %} + +.add_mat_mul: + ldr x2, [x0, #24] // b + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x1 ], #64 + ld1 { v4.4s }, [ x2 ], #16 + +{% capture packed_packed_loop1 %} + {% if core == "a53" %} + {% include "arm64simd_mmm_f32_16x4/packed_packed_loop1/cortex_a53.tmpli" %} + {% else %} + {% include "arm64simd_mmm_f32_16x4/packed_packed_loop1/naive.tmpli" %} + {% endif %} +{% endcapture %} + +{% capture packed_packed_loop2 %} + {% if core == "a55" %} + {% include "arm64simd_mmm_f32_16x4/packed_packed_loop2/cortex_a55.tmpli" %} + {% else %} + {{ packed_packed_loop1 }} + {{ packed_packed_loop1 }} + {% endif %} +{% endcapture %} + + cmp x3, #4 + blt .packed_packed_loop_1 + +.p2align 4 +.packed_packed_loop_4: + {{ packed_packed_loop2 }} + {{ packed_packed_loop2 }} + + sub x3, x3, #4 + cmp x3, #4 + bge .packed_packed_loop_4 + + cmp x3, #0 + beq .non_linear_loop + +.p2align 4 +.packed_packed_loop_1: + {{ packed_packed_loop1 }} + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +{% include "arm64simd_mmm_f32_scalars.tmpliq" from:16, to:31%} +{% include "arm64simd_mmm_f32_per_rows.tmpliq" mr:16, from:16, to:31 %} +{% include "arm64simd_mmm_f32_per_cols.tmpliq" mr:16, from:16, to:31 %} + +.add_unicast: + ldp x5, x6, [x0, #8] + ldp x7, x8, [x0, #24] + + {% for col in (0..3) %} + mov x4, x5 + {% for reg in (0..3) %} + {% for lane in (0..3) %} + ld1 {v0.s}[{{lane}}], [ x4 ], x6 + {% endfor %} + fadd v{{col | times:4 | plus: 16| plus: reg}}.4s, v{{col | times:4 | plus: 16 | plus: reg}}.4s, v0.4s + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x2, [x0, #8] + ldr x3, [x0, #16] + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x2 ] + ld1 { v4.4s }, [ x3 ] + + fmla v16.4s, v0.4s, v4.s[0] + fmla v17.4s, v1.4s, v4.s[0] + fmla v18.4s, v2.4s, v4.s[0] + fmla v19.4s, v3.4s, v4.s[0] + fmla v20.4s, v0.4s, v4.s[1] + fmla v21.4s, v1.4s, v4.s[1] + fmla v22.4s, v2.4s, v4.s[1] + fmla v23.4s, v3.4s, v4.s[1] + + fmla v24.4s, v0.4s, v4.s[2] + fmla v25.4s, v1.4s, v4.s[2] + fmla v26.4s, v2.4s, v4.s[2] + fmla v27.4s, v3.4s, v4.s[2] + fmla v28.4s, v0.4s, v4.s[3] + fmla v29.4s, v1.4s, v4.s[3] + fmla v30.4s, v2.4s, v4.s[3] + fmla v31.4s, v3.4s, v4.s[3] + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x6, #4 + bne .store_strides_generic + + {% for col in (0..3) %} + str q{{col | times:4 | plus:16 | plus: 0}}, [ x5 ] + str q{{col | times:4 | plus:16 | plus: 1}}, [ x5, #16 ] + str q{{col | times:4 | plus:16 | plus: 2}}, [ x5, #32 ] + str q{{col | times:4 | plus:16 | plus: 3}}, [ x5, #48 ] + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.store_strides_generic: + + {% for col in (0..3) %} + mov x4, x5 + {% for reg in (0..3) %} + {% for lane in (0..3) %} + st1 { v{{col | times:4 | plus: 16 | plus: reg}}.s }[{{lane}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.return: + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_24x4/loop2/cortex_a55.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_24x4/loop2/cortex_a55.tmpli new file mode 100644 index 0000000..b273638 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_24x4/loop2/cortex_a55.tmpli @@ -0,0 +1,73 @@ +// mul a: v0, v1, v2, v3, v4, v5 b: v7 +// load a: v5(d5/x5), v6(d6,x6), v7(d7,x7), v8(d8, x8) +// load b: v9(d9/x9) + +fmla v16.4s, v0.4s, v4.s[0] +ldr d5, [x1], #8 +fmla v17.4s, v1.4s, v4.s[0] +ldr d9, [x2], #8 +fmla v18.4s, v2.4s, v4.s[0] +ldr x5, [x1], #8 +fmla v19.4s, v3.4s, v4.s[0] +ldr x9, [x2], #8 +fmla v20.4s, v0.4s, v4.s[1] +ldr d6, [x1], #8 +fmla v21.4s, v1.4s, v4.s[1] +ldr x6, [x1], #8 +fmla v22.4s, v2.4s, v4.s[1] +ldr d7, [x1], #8 +fmla v23.4s, v3.4s, v4.s[1] +ldr x7, [x1], #8 + +fmla v24.4s, v0.4s, v4.s[2] +ldr d8, [x1], #8 +fmla v25.4s, v1.4s, v4.s[2] +ldr x8, [x1], #8 +fmla v26.4s, v2.4s, v4.s[2] +ins v5.d[1], x5 +fmla v27.4s, v3.4s, v4.s[2] +ins v6.d[1], x6 +fmla v28.4s, v0.4s, v4.s[3] +ins v7.d[1], x7 +fmla v29.4s, v1.4s, v4.s[3] +ins v8.d[1], x8 +fmla v30.4s, v2.4s, v4.s[3] +ins v9.d[1], x9 +fmla v31.4s, v3.4s, v4.s[3] + +// mul a: v5, v6, v7, v8 b: v9 +// load a: v0(d0/x5), v1(d1,x6), v2(d2,x7), v3(d3, x8) +// load b: v4(d4/x9) + +fmla v16.4s, v5.4s, v9.s[0] +ldr d0, [x1], #8 +fmla v17.4s, v6.4s, v9.s[0] +ldr d4, [x2], #8 +fmla v18.4s, v7.4s, v9.s[0] +ldr x5, [x1], #8 +fmla v19.4s, v8.4s, v9.s[0] +ldr x9, [x2], #8 +fmla v20.4s, v5.4s, v9.s[1] +ldr d1, [x1], #8 +fmla v21.4s, v6.4s, v9.s[1] +ldr x6, [x1], #8 +fmla v22.4s, v7.4s, v9.s[1] +ldr d2, [x1], #8 +fmla v23.4s, v8.4s, v9.s[1] +ldr x7, [x1], #8 + +fmla v24.4s, v5.4s, v9.s[2] +ldr d3, [x1], #8 +fmla v25.4s, v6.4s, v9.s[2] +ldr x8, [x1], #8 +fmla v26.4s, v7.4s, v9.s[2] +ins v0.d[1], x5 +fmla v27.4s, v8.4s, v9.s[2] +ins v1.d[1], x6 +fmla v28.4s, v5.4s, v9.s[3] +ins v2.d[1], x7 +fmla v29.4s, v6.4s, v9.s[3] +ins v3.d[1], x8 +fmla v30.4s, v7.4s, v9.s[3] +ins v4.d[1], x9 +fmla v31.4s, v8.4s, v9.s[3] diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a53.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a53.tmpli new file mode 100644 index 0000000..6742473 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a53.tmpli @@ -0,0 +1,63 @@ + +fmla v8.4s, v0.4s, v7.s[0] + ldr x4, [x1] +fmla v9.4s, v1.4s, v7.s[0] + ldr x5, [x1, #8] +fmla v10.4s, v2.4s, v7.s[0] + ldr x6, [x1, #16] +fmla v11.4s, v3.4s, v7.s[0] + ldr x7, [x1, #24] +fmla v12.4s, v4.4s, v7.s[0] + ldr x8, [x1, #32] +fmla v13.4s, v5.4s, v7.s[0] + ldr x9, [x1, #40] + +fmla v14.4s, v0.4s, v7.s[1] + ldr x10, [x1, #48] +fmla v15.4s, v1.4s, v7.s[1] + ldr x11, [x1, #56] +fmla v16.4s, v2.4s, v7.s[1] + ldr x12, [x1, #64] +fmla v17.4s, v3.4s, v7.s[1] + ldr x13, [x1, #72] +fmla v18.4s, v4.4s, v7.s[1] + ldr x14, [x1, #80] +fmla v19.4s, v5.4s, v7.s[1] + ldr x15, [x1, #88] + +fmla v20.4s, v0.4s, v7.s[2] + ldr x20, [x2] +fmla v21.4s, v1.4s, v7.s[2] + ldr x21, [x2, #8] +fmla v22.4s, v2.4s, v7.s[2] + add x1, x1, #96 +fmla v23.4s, v3.4s, v7.s[2] + add x2, x2, #16 +fmla v24.4s, v4.4s, v7.s[2] + prfm pldl1keep, [x1, #256] +fmla v25.4s, v5.4s, v7.s[2] + prfm pldl1keep, [x2, #256] + +fmla v26.4s, v0.4s, v7.s[3] + prfm pldl1keep, [x1, #320] +fmla v27.4s, v1.4s, v7.s[3] +fmla v28.4s, v2.4s, v7.s[3] +fmla v29.4s, v3.4s, v7.s[3] +fmla v30.4s, v4.4s, v7.s[3] +fmla v31.4s, v5.4s, v7.s[3] + +ins v0.d[0], x4 +ins v1.d[0], x6 +ins v2.d[0], x8 +ins v3.d[0], x10 +ins v4.d[0], x12 +ins v5.d[0], x14 +ins v7.d[0], x20 + +ins v0.d[1], x5 +ins v1.d[1], x7 +ins v2.d[1], x9 +ins v3.d[1], x11 +ins v4.d[1], x13 +ins v5.d[1], x15 +ins v7.d[1], x21 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a55.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a55.tmpli new file mode 100644 index 0000000..93307bf --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a55.tmpli @@ -0,0 +1,53 @@ +fmla v8.4s, v0.4s, v7.s[0] +fmla v14.4s, v0.4s, v7.s[1] + prfm pldl1keep, [x2, #256] +fmla v20.4s, v0.4s, v7.s[2] +fmla v26.4s, v0.4s, v7.s[3] + ldr d0, [x1], #8 +fmla v9.4s, v1.4s, v7.s[0] + ldr x5, [x1], #8 +fmla v15.4s, v1.4s, v7.s[1] + ldr x20, [x2], #8 +fmla v21.4s, v1.4s, v7.s[2] + ldr x21, [x2], #8 +fmla v27.4s, v1.4s, v7.s[3] + ldr d1, [x1], #8 +fmla v10.4s, v2.4s, v7.s[0] + ldr x7, [x1], #8 +fmla v16.4s, v2.4s, v7.s[1] + prfm pldl1keep, [x1, #256] +fmla v22.4s, v2.4s, v7.s[2] + prfm pldl1keep, [x1, #320] +fmla v28.4s, v2.4s, v7.s[3] + ldr d2, [x1], #8 +fmla v11.4s, v3.4s, v7.s[0] + ldr x9, [x1], #8 +fmla v17.4s, v3.4s, v7.s[1] + ins v0.d[1], x5 +fmla v23.4s, v3.4s, v7.s[2] + ins v1.d[1], x7 +fmla v29.4s, v3.4s, v7.s[3] + ldr d3, [x1], #8 +fmla v12.4s, v4.4s, v7.s[0] + ldr x11, [x1], #8 +fmla v18.4s, v4.4s, v7.s[1] + ins v2.d[1], x9 +fmla v24.4s, v4.4s, v7.s[2] +fmla v30.4s, v4.4s, v7.s[3] + ldr d4, [x1], #8 +fmla v13.4s, v5.4s, v7.s[0] + ldr x13, [x1], #8 +fmla v19.4s, v5.4s, v7.s[1] + ldr x14, [x1], #8 +fmla v25.4s, v5.4s, v7.s[2] + ldr x15, [x1], #8 +fmla v31.4s, v5.4s, v7.s[3] + +ins v7.d[0], x20 +ins v7.d[1], x21 + +ins v5.d[0], x14 +ins v5.d[1], x15 + +ins v3.d[1], x11 +ins v4.d[1], x13 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_24x4/packed_packed_loop1/naive.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_24x4/packed_packed_loop1/naive.tmpli new file mode 100644 index 0000000..7b12d74 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_24x4/packed_packed_loop1/naive.tmpli @@ -0,0 +1,31 @@ +fmla v8.4s, v0.4s, v7.s[0] +fmla v9.4s, v1.4s, v7.s[0] +fmla v10.4s, v2.4s, v7.s[0] +fmla v11.4s, v3.4s, v7.s[0] +fmla v12.4s, v4.4s, v7.s[0] +fmla v13.4s, v5.4s, v7.s[0] + +fmla v14.4s, v0.4s, v7.s[1] +fmla v15.4s, v1.4s, v7.s[1] +fmla v16.4s, v2.4s, v7.s[1] +fmla v17.4s, v3.4s, v7.s[1] +fmla v18.4s, v4.4s, v7.s[1] +fmla v19.4s, v5.4s, v7.s[1] + +fmla v20.4s, v0.4s, v7.s[2] +fmla v21.4s, v1.4s, v7.s[2] +fmla v22.4s, v2.4s, v7.s[2] +fmla v23.4s, v3.4s, v7.s[2] +fmla v24.4s, v4.4s, v7.s[2] +fmla v25.4s, v5.4s, v7.s[2] + +fmla v26.4s, v0.4s, v7.s[3] +fmla v27.4s, v1.4s, v7.s[3] +fmla v28.4s, v2.4s, v7.s[3] +fmla v29.4s, v3.4s, v7.s[3] +fmla v30.4s, v4.4s, v7.s[3] +fmla v31.4s, v5.4s, v7.s[3] + +ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [ x1 ], #64 +ld1 {{ v4.4s, v5.4s }}, [ x1 ], #32 +ld1 {{ v7.4s }}, [ x2 ], #16 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_24x4_core.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_24x4_core.tmpl new file mode 100644 index 0000000..819caff --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_24x4_core.tmpl @@ -0,0 +1,184 @@ +// vim: ft=arm + +// x20..x27 are used, callee-preserved + +// C tile regs: v8 to v31, (scratch) +// - x19-x29 to preserve (but x19, x28, x29 not used) +// - d8..d15 to preserve +// - v16 to v31, no need to preserve + +// v8 is used, d8 (lower half) must preserved +// v0-v7 (scratch registers) +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +.cpu generic+fp+simd +.global {{G}}arm64simd_mmm_f32_24x4_{{core}}_{{suffix}} +{{G}}arm64simd_mmm_f32_24x4_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.tmpliq" %} + +.add_mat_mul: + ldr x2, [x0, #24] // b + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x1 ], #64 + ld1 { v4.4s, v5.4s }, [ x1 ], #32 + ld1 { v7.4s }, [ x2 ], #16 + +{% capture packed_packed_loop1 %} + {% if core == "a53" %} + {% include "arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a53.tmpli" %} + {% elsif core == "a55" %} + {% include "arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a55.tmpli" %} + {% else %} + {% include "arm64simd_mmm_f32_24x4/packed_packed_loop1/naive.tmpli" %} + {% endif %} +{% endcapture %} + + cmp x3, #4 + blt .packed_packed_loop_1 + +.p2align 4 +.packed_packed_loop_4: + {{ packed_packed_loop1 }} + {{ packed_packed_loop1 }} + {{ packed_packed_loop1 }} + {{ packed_packed_loop1 }} + + sub x3, x3, #4 + cmp x3, #4 + bge .packed_packed_loop_4 + + cmp x3, #0 + beq .non_linear_loop + +.p2align 4 +.packed_packed_loop_1: + {{ packed_packed_loop1 }} + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +{% include "arm64simd_mmm_f32_scalars.tmpliq" from:8, to:31 %} +{% include "arm64simd_mmm_f32_per_rows.tmpliq" mr:24, from:8, to:31 %} +{% include "arm64simd_mmm_f32_per_cols.tmpliq" mr:24, from:8, to:31 %} + +.add_unicast: + ldp x5, x6, [x0, #8] + ldp x7, x8, [x0, #24] + + {% for col in (0..3) %} + mov x4, x5 + {% for reg in (0..5) %} + {% for lane in (0..3) %} + ld1 {v0.s}[{{lane}}], [ x4 ], x6 + {% endfor %} + fadd v{{col | times:6 | plus: 8 | plus: reg}}.4s, v{{col | times:6 | plus: 8 | plus: reg}}.4s, v0.4s + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x2, [x0, #8] + ldr x3, [x0, #16] + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x2 ], #64 + ld1 { v7.4s }, [ x3 ] + ld1 { v4.4s, v5.4s }, [ x2 ] + + fmla v8.4s, v0.4s, v7.s[0] + fmla v9.4s, v1.4s, v7.s[0] + fmla v10.4s, v2.4s, v7.s[0] + fmla v11.4s, v3.4s, v7.s[0] + fmla v12.4s, v4.4s, v7.s[0] + fmla v13.4s, v5.4s, v7.s[0] + + fmla v14.4s, v0.4s, v7.s[1] + fmla v15.4s, v1.4s, v7.s[1] + fmla v16.4s, v2.4s, v7.s[1] + fmla v17.4s, v3.4s, v7.s[1] + fmla v18.4s, v4.4s, v7.s[1] + fmla v19.4s, v5.4s, v7.s[1] + + fmla v20.4s, v0.4s, v7.s[2] + fmla v21.4s, v1.4s, v7.s[2] + fmla v22.4s, v2.4s, v7.s[2] + fmla v23.4s, v3.4s, v7.s[2] + fmla v24.4s, v4.4s, v7.s[2] + fmla v25.4s, v5.4s, v7.s[2] + + fmla v26.4s, v0.4s, v7.s[3] + fmla v27.4s, v1.4s, v7.s[3] + fmla v28.4s, v2.4s, v7.s[3] + fmla v29.4s, v3.4s, v7.s[3] + fmla v30.4s, v4.4s, v7.s[3] + fmla v31.4s, v5.4s, v7.s[3] + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x6, #4 + bne .store_strides_generic + + {% for col in (0..3) %} + str q{{col | times:6 | plus:8 | plus: 0}}, [ x5 ] + str q{{col | times:6 | plus:8 | plus: 1}}, [ x5, #16 ] + str q{{col | times:6 | plus:8 | plus: 2}}, [ x5, #32 ] + str q{{col | times:6 | plus:8 | plus: 3}}, [ x5, #48 ] + str q{{col | times:6 | plus:8 | plus: 4}}, [ x5, #64 ] + str q{{col | times:6 | plus:8 | plus: 5}}, [ x5, #80 ] + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.store_strides_generic: + + {% for col in (0..3) %} + mov x4, x5 + {% for reg in (0..5) %} + {% for lane in (0..3) %} + st1 { v{{col | times:6 | plus:8 | plus: reg}}.s }[{{lane}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.return: + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop1/cortex_a53.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop1/cortex_a53.tmpli new file mode 100644 index 0000000..410816d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop1/cortex_a53.tmpli @@ -0,0 +1,65 @@ + fmla v16.4s, v0.4s, v8.s[0] + ldr x5, [x1, #128] + fmla v17.4s, v1.4s, v8.s[0] + ldr x6, [x1, #136] + fmla v18.4s, v2.4s, v8.s[0] + ldr x7, [x1, #144] + fmla v19.4s, v3.4s, v8.s[0] + ldr x9, [x1, #152] + ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [ x1 ], #64 + + fmla v20.4s, v4.4s, v8.s[0] + ldr x10, [x1, #96] + fmla v21.4s, v5.4s, v8.s[0] + ldr x11, [x1, #104] + fmla v22.4s, v6.4s, v8.s[0] + ldr x12, [x1, #112] + fmla v23.4s, v7.4s, v8.s[0] + ldr x13, [x1, #120] + + ld1 {{ v4.4s, v5.4s, v6.4s, v7.4s }}, [ x1 ] + + fmla v24.4s, v0.4s, v8.s[0] + ldr x14, [x1, #128] + fmla v25.4s, v1.4s, v8.s[0] + ldr x15, [x1, #136] + fmla v26.4s, v2.4s, v8.s[0] + ldr x20, [x1, #144] + fmla v27.4s, v3.4s, v8.s[0] + ldr x21, [x1, #152] + fmla v28.4s, v4.4s, v8.s[0] + ldr x22, [x1, #160] + fmla v29.4s, v5.4s, v8.s[0] + ldr x23, [x1, #168] + fmla v30.4s, v6.4s, v8.s[0] + ldr x24, [x1, #176] + fmla v31.4s, v7.4s, v8.s[0] + ldr x25, [x1, #184] + + ld1 {{ v8.s }}[0], [ x2 ], #4 + + prfm pldl1keep, [x1, #1024] + prfm pldl1keep, [x1, #1088] + prfm pldl1keep, [x1, #1152] + prfm pldl1keep, [x1, #1216] + prfm pldl1keep, [x2, #256] + + ins v0.d[0], x5 + ins v1.d[0], x7 + ins v2.d[0], x10 + ins v3.d[0], x12 + ins v4.d[0], x14 + ins v5.d[0], x20 + ins v6.d[0], x22 + ins v7.d[0], x24 + + ins v0.d[1], x6 + ins v1.d[1], x9 + ins v2.d[1], x11 + ins v3.d[1], x13 + ins v4.d[1], x15 + ins v5.d[1], x21 + ins v6.d[1], x23 + ins v7.d[1], x25 + + add x1, x1, #192 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop1/naive.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop1/naive.tmpli new file mode 100644 index 0000000..c65deb9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop1/naive.tmpli @@ -0,0 +1,32 @@ + ld1 {{ v9.4s, v10.4s, v11.4s, v12.4s }}, [x1], #64 + ld1 {{ v13.4s, v14.4s, v15.4s }}, [x1], #48 + + fmla v16.4s, v0.4s, v8.s[0] + fmla v17.4s, v1.4s, v8.s[0] + fmla v18.4s, v2.4s, v8.s[0] + fmla v19.4s, v3.4s, v8.s[0] + fmla v20.4s, v4.4s, v8.s[0] + fmla v21.4s, v5.4s, v8.s[0] + fmla v22.4s, v6.4s, v8.s[0] + fmla v23.4s, v7.4s, v8.s[0] + fmla v24.4s, v9.4s, v8.s[0] + ld1 {{ v9.4s }}, [ x1 ], #16 + ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [x1], #64 + ld1 {{ v4.4s, v5.4s, v6.4s, v7.4s }}, [x1], #64 + fmla v25.4s, v10.4s, v8.s[0] + fmla v26.4s, v11.4s, v8.s[0] + fmla v27.4s, v12.4s, v8.s[0] + fmla v28.4s, v13.4s, v8.s[0] + fmla v29.4s, v14.4s, v8.s[0] + fmla v30.4s, v15.4s, v8.s[0] + + fmla v31.4s, v9.4s, v8.s[0] + + ld1 {{ v8.s }}[0], [ x2 ], #4 + + prfm pldl1keep, [x1, #1024] + prfm pldl1keep, [x1, #1088] + prfm pldl1keep, [x1, #1152] + prfm pldl1keep, [x1, #1216] + prfm pldl1keep, [x2, #256] + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop2/cortex_a55.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop2/cortex_a55.tmpli new file mode 100644 index 0000000..2a5f066 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop2/cortex_a55.tmpli @@ -0,0 +1,85 @@ + ld1 {{ v9.4s, v10.4s, v11.4s }}, [x1], #48 + + fmla v16.4s, v0.4s, v8.s[0] + ldr x8, [x2], #8 + fmla v17.4s, v1.4s, v8.s[0] + ldr d12, [x1], #8 + fmla v18.4s, v2.4s, v8.s[0] + ldr x12, [x1], #8 + fmla v19.4s, v3.4s, v8.s[0] + ldr d13, [x1], #8 + fmla v20.4s, v4.4s, v8.s[0] + ldr x13, [x1], #8 + fmla v21.4s, v5.4s, v8.s[0] + ldr d14, [x1], #8 + fmla v22.4s, v6.4s, v8.s[0] + ldr x14, [x1], #8 + fmla v23.4s, v7.4s, v8.s[0] + ldr d15, [x1], #8 + fmla v24.4s, v9.4s, v8.s[0] + ldr x15, [x1], #8 + + ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [x1], #64 + ins v8.d[1], x8 + ld1 {{ v4.4s, v5.4s, v6.4s, v7.4s }}, [x1], #64 + + fmla v25.4s, v10.4s, v8.s[0] + ins v12.d[1], x12 + fmla v26.4s, v11.4s, v8.s[0] + ins v13.d[1], x13 + fmla v27.4s, v12.4s, v8.s[0] + ins v14.d[1], x14 + fmla v28.4s, v13.4s, v8.s[0] + ins v15.d[1], x15 + + ld1 {{ v9.4s, v10.4s, v11.4s, v12.4s }}, [x1], #64 + + fmla v29.4s, v14.4s, v8.s[0] + ldr d13, [x1], #8 + fmla v30.4s, v15.4s, v8.s[0] + ldr x13, [x1], #8 + fmla v31.4s, v0.4s, v8.s[0] + ldr d14, [x1], #8 + + fmla v16.4s, v1.4s, v8.s[2] + ldr x14, [x1], #8 + fmla v17.4s, v2.4s, v8.s[2] + ldr d15, [x1], #8 + fmla v18.4s, v3.4s, v8.s[2] + ldr x15, [x1], #8 + fmla v19.4s, v4.4s, v8.s[2] + + ld1 {{ v0.4s }}, [x1], #16 + + fmla v20.4s, v5.4s, v8.s[2] + ldr d1, [x1], #8 + fmla v21.4s, v6.4s, v8.s[2] + ldr x10, [x1], #8 + + fmla v22.4s, v7.4s, v8.s[2] + + fmla v23.4s, v9.4s, v8.s[2] + ins v13.d[1], x13 + fmla v24.4s, v10.4s, v8.s[2] + ins v14.d[1], x14 + fmla v25.4s, v11.4s, v8.s[2] + ins v15.d[1], x15 + + fmla v26.4s, v12.4s, v8.s[2] + prfm pldl1keep, [x1, #1024] + fmla v27.4s, v13.4s, v8.s[2] + ins v1.d[1], x10 + fmla v28.4s, v14.4s, v8.s[2] + prfm pldl1keep, [x1, #1088] + fmla v29.4s, v15.4s, v8.s[2] + prfm pldl1keep, [x1, #1152] + fmla v30.4s, v0.4s, v8.s[2] + prfm pldl1keep, [x1, #1216] + fmla v31.4s, v1.4s, v8.s[2] + prfm pldl1keep, [x2, #256] + + ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [x1], #64 + ins v8.s[0], v8.s[3] + ld1 {{ v4.4s, v5.4s, v6.4s, v7.4s }}, [x1], #64 + + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop2/naive.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop2/naive.tmpli new file mode 100644 index 0000000..cba3dad --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop2/naive.tmpli @@ -0,0 +1,66 @@ +// load a: v9, v10, v11, v12, v13, v14, v15 +// load a: v0, v1, v2, v3, v4, v4, v6, v7 + + ld1 {{ v9.4s, v10.4s, v11.4s, v12.4s }}, [x1], #64 + ld1 {{ v13.4s, v14.4s, v15.4s }}, [x1], #48 + + fmla v16.4s, v0.4s, v8.s[0] + fmla v17.4s, v1.4s, v8.s[0] + fmla v18.4s, v2.4s, v8.s[0] + fmla v19.4s, v3.4s, v8.s[0] + + ld1 {{ v0.4s, v1.4s }}, [x1], #32 + + fmla v20.4s, v4.4s, v8.s[0] + fmla v21.4s, v5.4s, v8.s[0] + + ld1 {{ v2.4s, v3.4s, v4.4s, v5.4s }}, [x1], #64 + fmla v22.4s, v6.4s, v8.s[0] + fmla v23.4s, v7.4s, v8.s[0] + + ld1 {{ v6.4s, v7.4s }}, [x1], #32 + + fmla v24.4s, v9.4s, v8.s[0] + fmla v25.4s, v10.4s, v8.s[0] + fmla v26.4s, v11.4s, v8.s[0] + fmla v27.4s, v12.4s, v8.s[0] + fmla v28.4s, v13.4s, v8.s[0] + fmla v29.4s, v14.4s, v8.s[0] + fmla v30.4s, v15.4s, v8.s[0] + + ld1 {{ v9.4s, v10.4s, v11.4s, v12.4s }}, [x1], #64 + ld1 {{ v13.4s, v14.4s, v15.4s }}, [x1], #48 + + fmla v31.4s, v0.4s, v8.s[0] + ld1 {{ v8.s }}[0], [ x2 ], #4 + + fmla v16.4s, v1.4s, v8.s[0] + ld1 {{ v0.4s, v1.4s }}, [x1], #32 + fmla v17.4s, v2.4s, v8.s[0] + fmla v18.4s, v3.4s, v8.s[0] + fmla v19.4s, v4.4s, v8.s[0] + + fmla v20.4s, v5.4s, v8.s[0] + fmla v21.4s, v6.4s, v8.s[0] + fmla v22.4s, v7.4s, v8.s[0] + fmla v23.4s, v9.4s, v8.s[0] + + fmla v24.4s, v10.4s, v8.s[0] + fmla v25.4s, v11.4s, v8.s[0] + fmla v26.4s, v12.4s, v8.s[0] + fmla v27.4s, v13.4s, v8.s[0] + fmla v28.4s, v14.4s, v8.s[0] + fmla v29.4s, v15.4s, v8.s[0] + fmla v30.4s, v0.4s, v8.s[0] + fmla v31.4s, v1.4s, v8.s[0] + ld1 {{ v8.s }}[0], [ x2 ], #4 + + ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [x1], #64 + ld1 {{ v4.4s, v5.4s, v6.4s, v7.4s }}, [x1], #64 + + prfm pldl1keep, [x1, #1024] + prfm pldl1keep, [x1, #1088] + prfm pldl1keep, [x1, #1152] + prfm pldl1keep, [x1, #1216] + prfm pldl1keep, [x2, #256] + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_64x1_core.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_64x1_core.tmpl new file mode 100644 index 0000000..9af4873 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_64x1_core.tmpl @@ -0,0 +1,224 @@ +// vim: ft=arm + +// C tile regs: +// - x19-x29 to preserve (but x19, x28, x29 not used) +// - d8..d15 to preserve +// - v16 to v31, no need to preserve +// +// v16[0] v18[0] v20[0] v22[0] v24[0] v26[0] v28[0] v30[0] +// v16[1] v18[1] +// v16[2] v18[2] +// v16[3] v18[3] +// +// v17[0] v19[0] v21[0] v23[0] v25[0] v27[0] v29[0] v31[0] +// v17[1] v19[1] +// v17[2] v19[2] +// v17[3] v19[3] + +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +.cpu generic+fp+simd +.global {{G}}arm64simd_mmm_f32_64x1_{{core}}_{{suffix}} +{{G}}arm64simd_mmm_f32_64x1_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.tmpliq" %} + +.add_mat_mul: + ldr x2, [x0, #24] // b + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + sub x3, x3, #1 + + + ld1 { v8.s }[0], [ x2 ], #4 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x1 ], #64 + ld1 { v4.4s, v5.4s, v6.4s, v7.4s }, [ x1 ], #64 + + cmp x3, #0 + beq .packed_packed_loop_1_last + + cmp x3, #4 + blt .packed_packed_loop_1 + +{% capture packed_packed_loop1 %} + {% if core == "a53" %} + {% include "arm64simd_mmm_f32_64x1/loop1/cortex_a53.tmpli" %} + {% else %} + {% include "arm64simd_mmm_f32_64x1/loop1/naive.tmpli" %} + {% endif %} +{% endcapture %} + +{% capture packed_packed_loop2 %} + {% if core == "a53" %} + {{ packed_packed_loop1 }} + {{ packed_packed_loop1 }} + {% elsif core == "a55" %} + {% include "arm64simd_mmm_f32_64x1/loop2/cortex_a55.tmpli" %} + {% else %} + {% include "arm64simd_mmm_f32_64x1/loop2/naive.tmpli" %} + {% endif %} +{% endcapture %} + +.p2align 4 +.packed_packed_loop_4: + {{ packed_packed_loop2 }} + {{ packed_packed_loop2 }} + + sub x3, x3, #4 + cmp x3, #4 + bge .packed_packed_loop_4 + + cmp x3, #0 + beq .packed_packed_loop_1_last + +.p2align 4 +.packed_packed_loop_1: + {{ packed_packed_loop1 }} + + subs x3, x3, #1 + bne .packed_packed_loop_1 + +// last loop can't read beyond actual input as it's likely not packed and padded +.packed_packed_loop_1_last: + ld1 { v9.4s, v10.4s, v11.4s, v12.4s }, [x1], #64 + ld1 { v13.4s, v14.4s, v15.4s }, [x1], #48 + + fmla v16.4s, v0.4s, v8.s[0] + fmla v17.4s, v1.4s, v8.s[0] + ld1 { v0.4s }, [ x1 ] + fmla v18.4s, v2.4s, v8.s[0] + fmla v19.4s, v3.4s, v8.s[0] + fmla v20.4s, v4.4s, v8.s[0] + fmla v21.4s, v5.4s, v8.s[0] + fmla v22.4s, v6.4s, v8.s[0] + fmla v23.4s, v7.4s, v8.s[0] + + fmla v24.4s, v9.4s, v8.s[0] + fmla v25.4s, v10.4s, v8.s[0] + fmla v26.4s, v11.4s, v8.s[0] + fmla v27.4s, v12.4s, v8.s[0] + fmla v28.4s, v13.4s, v8.s[0] + fmla v29.4s, v14.4s, v8.s[0] + fmla v30.4s, v15.4s, v8.s[0] + fmla v31.4s, v0.4s, v8.s[0] + + b .non_linear_loop + +{% include "arm64simd_mmm_f32_scalars.tmpliq" from:16, to:31%} +{% include "arm64simd_mmm_f32_per_rows.tmpliq" mr:64, from:16, to:31%} +{% include "arm64simd_mmm_f32_per_cols.tmpliq" mr:64, from:16, to:31%} + +.add_unicast: + ldp x5, x6, [x0, #8] // c base ptr, rsc + cmp x6, #4 + beq .do_per_row_add + + {% for reg in (16..31) %} + {% for lane in (0..3) %} + ld1 {v0.s}[{{lane}}], [ x5 ], x6 + {% endfor %} + fadd v{{reg}}.4s, v{{reg}}.4s, v0.4s + {% endfor %} + + b .non_linear_loop + +.do_per_row_add: + ld1 {v0.4s-v3.4s}, [x5], #64 + ld1 {v4.4s-v7.4s}, [x5], #64 + ld1 {v8.4s-v11.4s}, [x5], #64 + ld1 {v12.4s-v15.4s}, [x5], #64 + + {% for r in (0..15) %} + fadd v{{r| plus: 16}}.4s, v{{r | plus: 16}}.4s, v{{r}}.4s + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x3, [x0, #16] + ldr x2, [x0, #8] + + ld1 {v8.s}[0], [ x3 ] + + {% for r in (0..7) %} + ldr q{{r}}, [x2], #16 + {% endfor %} + + fmla v16.4s, v0.4s, v8.s[0] + ldr q0, [x2], #16 + fmla v17.4s, v1.4s, v8.s[0] + ldr q1, [x2], #16 + fmla v18.4s, v2.4s, v8.s[0] + ldr q2, [x2], #16 + fmla v19.4s, v3.4s, v8.s[0] + ldr q3, [x2], #16 + fmla v20.4s, v4.4s, v8.s[0] + ldr q4, [x2], #16 + fmla v21.4s, v5.4s, v8.s[0] + ldr q5, [x2], #16 + fmla v22.4s, v6.4s, v8.s[0] + ldr q6, [x2], #16 + fmla v23.4s, v7.4s, v8.s[0] + ldr q7, [x2], #16 + + fmla v24.4s, v0.4s, v8.s[0] + fmla v25.4s, v1.4s, v8.s[0] + fmla v26.4s, v2.4s, v8.s[0] + fmla v27.4s, v3.4s, v8.s[0] + fmla v28.4s, v4.4s, v8.s[0] + fmla v29.4s, v5.4s, v8.s[0] + fmla v30.4s, v6.4s, v8.s[0] + fmla v31.4s, v7.4s, v8.s[0] + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc$ + + cmp x6, #4 + beq .store_strides_contig + + {% for reg in (16..31) %} + {% for lane in (0..3) %} + st1 { v{{reg}}.s }[{{lane}}], [ x5 ], x6 + {% endfor %} + {% endfor %} + b .non_linear_loop + +.store_strides_contig: + + {% for reg in (16..31) %} + st1 { v{{reg}}.4s }, [ x5 ], #16 + {% endfor %} + b .non_linear_loop + +.return: + + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/broken_chains.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/broken_chains.tmpli new file mode 100644 index 0000000..9b3035b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/broken_chains.tmpli @@ -0,0 +1,25 @@ +ld1 {{ v2.4s, v3.4s }}, [x1], #32 +ld1 {{ v6.4s, v7.4s }}, [x2], #32 + +fmla v16.4s, v0.4s, v4.s[0] +fmla v17.4s, v1.4s, v4.s[0] +fmla v18.4s, v0.4s, v4.s[1] +fmla v19.4s, v1.4s, v4.s[1] +fmla v20.4s, v0.4s, v4.s[2] +fmla v21.4s, v1.4s, v4.s[2] +fmla v22.4s, v0.4s, v4.s[3] +fmla v23.4s, v1.4s, v4.s[3] + +fmla v24.4s, v0.4s, v5.s[0] +fmla v25.4s, v1.4s, v5.s[0] +fmla v26.4s, v0.4s, v5.s[1] +fmla v27.4s, v1.4s, v5.s[1] +fmla v28.4s, v0.4s, v5.s[2] +fmla v29.4s, v1.4s, v5.s[2] +fmla v30.4s, v0.4s, v5.s[3] +fmla v31.4s, v1.4s, v5.s[3] + +and v0.16b, v2.16b, v2.16b +and v1.16b, v3.16b, v3.16b +and v4.16b, v6.16b, v6.16b +and v5.16b, v7.16b, v7.16b diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_w_no_preload.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_w_no_preload.tmpli new file mode 100644 index 0000000..ac5bdc5 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_w_no_preload.tmpli @@ -0,0 +1,51 @@ + +fmla v16.4s, v0.4s, v4.s[0] +ldr w5, [x1], #4 +fmla v17.4s, v1.4s, v4.s[0] +ldr w20, [x2], #4 +fmla v18.4s, v0.4s, v4.s[1] +ldr w6, [x1], #4 +fmla v20.4s, v1.4s, v4.s[1] +ldr w21, [x2], #4 +fmla v20.4s, v0.4s, v4.s[2] +ldr w7, [x1], #4 +fmla v21.4s, v1.4s, v4.s[2] +ldr w22, [x2], #4 +fmla v22.4s, v0.4s, v4.s[3] +ldr w8, [x1], #4 +fmla v23.4s, v1.4s, v4.s[3] +ldr w23, [x2], #4 + +fmla v24.4s, v0.4s, v5.s[0] +ldr w9, [x1], #4 +fmla v25.4s, v1.4s, v5.s[0] +ldr w24, [x2], #4 +fmla v26.4s, v0.4s, v5.s[1] +ldr w10, [x1], #4 +fmla v27.4s, v1.4s, v5.s[1] +ldr w25, [x2], #4 +fmla v28.4s, v0.4s, v5.s[2] +ldr w11, [x1], #4 +fmla v29.4s, v1.4s, v5.s[2] +ldr w26, [x2], #4 +fmla v30.4s, v0.4s, v5.s[3] +ldr w12, [x1], #4 +fmla v31.4s, v1.4s, v5.s[3] +ldr w27, [x2], #4 + +ins v0.s[0], w5 +ins v4.s[0], w20 +ins v1.s[0], w9 +ins v5.s[0], w24 +ins v0.s[2], w7 +ins v4.s[2], w22 +ins v1.s[2], w11 +ins v5.s[2], w26 +ins v0.s[1], w6 +ins v4.s[1], w21 +ins v1.s[1], w10 +ins v5.s[1], w25 +ins v0.s[3], w8 +ins v4.s[3], w23 +ins v1.s[3], w12 +ins v5.s[3], w27 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_w_preload.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_w_preload.tmpli new file mode 100644 index 0000000..3afc78c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_w_preload.tmpli @@ -0,0 +1,54 @@ +fmla v16.4s, v0.4s, v4.s[0] +ldr w5, [x1], #4 +fmla v17.4s, v1.4s, v4.s[0] +ldr w20, [x2], #4 +fmla v18.4s, v0.4s, v4.s[1] +ldr w6, [x1], #4 +fmla v19.4s, v1.4s, v4.s[1] +ldr w21, [x2], #4 +fmla v20.4s, v0.4s, v4.s[2] +ldr w7, [x1], #4 +fmla v21.4s, v1.4s, v4.s[2] +ldr w22, [x2], #4 +fmla v22.4s, v0.4s, v4.s[3] +ldr w8, [x1], #4 +fmla v23.4s, v1.4s, v4.s[3] +ldr w23, [x2], #4 + +fmla v24.4s, v0.4s, v5.s[0] +ldr w9, [x1], #4 +fmla v25.4s, v1.4s, v5.s[0] +ldr w24, [x2], #4 +fmla v26.4s, v0.4s, v5.s[1] +ldr w10, [x1], #4 +fmla v27.4s, v1.4s, v5.s[1] +ldr w25, [x2], #4 +fmla v28.4s, v0.4s, v5.s[2] +ldr w11, [x1], #4 +fmla v29.4s, v1.4s, v5.s[2] +ldr w26, [x2], #4 +fmla v30.4s, v0.4s, v5.s[3] +ldr w12, [x1], #4 +fmla v31.4s, v1.4s, v5.s[3] +ldr w27, [x2], #4 + +prfm pldl1keep, [x1, #256] +prfm pldl1keep, [x2, #256] + +ins v0.s[0], w5 +ins v4.s[0], w20 +ins v1.s[0], w9 +ins v5.s[0], w24 +ins v0.s[2], w7 +ins v4.s[2], w22 +ins v1.s[2], w11 +ins v5.s[2], w26 +ins v0.s[1], w6 +ins v4.s[1], w21 +ins v1.s[1], w10 +ins v5.s[1], w25 +ins v0.s[3], w8 +ins v4.s[3], w23 +ins v1.s[3], w12 +ins v5.s[3], w27 + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_x_no_preload.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_x_no_preload.tmpli new file mode 100644 index 0000000..e3822d3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_x_no_preload.tmpli @@ -0,0 +1,35 @@ + +fmla v16.4s, v0.4s, v4.s[0] +ldr x5, [x1], #8 +fmla v17.4s, v1.4s, v4.s[0] +ldr x9, [x2], #8 +fmla v18.4s, v0.4s, v4.s[1] +ldr x6, [x1], #8 +fmla v19.4s, v1.4s, v4.s[1] +ldr x10, [x2], #8 +fmla v20.4s, v0.4s, v4.s[2] +ldr x7, [x1], #8 +fmla v21.4s, v1.4s, v4.s[2] +ldr x11, [x2], #8 +fmla v22.4s, v0.4s, v4.s[3] +ldr x8, [x1], #8 +fmla v23.4s, v1.4s, v4.s[3] +ldr x12, [x2], #8 + +fmla v24.4s, v0.4s, v5.s[0] +fmla v25.4s, v1.4s, v5.s[0] +fmla v26.4s, v0.4s, v5.s[1] +fmla v27.4s, v1.4s, v5.s[1] +fmla v28.4s, v0.4s, v5.s[2] +fmla v29.4s, v1.4s, v5.s[2] +fmla v30.4s, v0.4s, v5.s[3] +fmla v31.4s, v1.4s, v5.s[3] + +ins v2.d[0], x5 +ins v6.d[0], x9 +ins v3.d[0], x7 +ins v7.d[0], x11 +ins v2.d[1], x6 +ins v6.d[1], x10 +ins v3.d[1], x8 +ins v7.d[1], x12 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_x_preload.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_x_preload.tmpli new file mode 100644 index 0000000..11081e8 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_x_preload.tmpli @@ -0,0 +1,43 @@ + +fmla v16.4s, v0.4s, v4.s[0] +ldr x5, [x1], #8 +fmla v17.4s, v1.4s, v4.s[0] +ldr x9, [x2], #8 +fmla v18.4s, v0.4s, v4.s[1] +ldr x6, [x1], #8 +fmla v19.4s, v1.4s, v4.s[1] +ldr x10, [x2], #8 +fmla v20.4s, v0.4s, v4.s[2] +ldr x7, [x1], #8 +fmla v21.4s, v1.4s, v4.s[2] +ldr x11, [x2], #8 +fmla v22.4s, v0.4s, v4.s[3] +ldr x8, [x1], #8 +fmla v23.4s, v1.4s, v4.s[3] +ldr x12, [x2], #8 + +fmla v24.4s, v0.4s, v5.s[0] +prfm pldl1keep, [x1, #256] +fmla v25.4s, v1.4s, v5.s[0] +prfm pldl1keep, [x1, #320] +fmla v26.4s, v0.4s, v5.s[1] +prfm pldl1keep, [x1, #384] +fmla v27.4s, v1.4s, v5.s[1] +prfm pldl1keep, [x1, #448] +fmla v28.4s, v0.4s, v5.s[2] +prfm pldl1keep, [x2, #256] +fmla v29.4s, v1.4s, v5.s[2] +prfm pldl1keep, [x2, #320] +fmla v30.4s, v0.4s, v5.s[3] +prfm pldl1keep, [x2, #384] +fmla v31.4s, v1.4s, v5.s[3] +prfm pldl1keep, [x2, #448] + +ins v0.d[0], x5 +ins v4.d[0], x9 +ins v1.d[0], x7 +ins v5.d[0], x11 +ins v0.d[1], x6 +ins v4.d[1], x10 +ins v1.d[1], x8 +ins v5.d[1], x12 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/naive.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/naive.tmpli new file mode 100644 index 0000000..14abb2a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/naive.tmpli @@ -0,0 +1,21 @@ + +fmla v16.4s, v0.4s, v4.s[0] +fmla v17.4s, v1.4s, v4.s[0] +fmla v18.4s, v0.4s, v4.s[1] +fmla v19.4s, v1.4s, v4.s[1] +fmla v20.4s, v0.4s, v4.s[2] +fmla v21.4s, v1.4s, v4.s[2] +fmla v22.4s, v0.4s, v4.s[3] +fmla v23.4s, v1.4s, v4.s[3] + +fmla v24.4s, v0.4s, v5.s[0] +fmla v25.4s, v1.4s, v5.s[0] +fmla v26.4s, v0.4s, v5.s[1] +fmla v27.4s, v1.4s, v5.s[1] +fmla v28.4s, v0.4s, v5.s[2] +fmla v29.4s, v1.4s, v5.s[2] +fmla v30.4s, v0.4s, v5.s[3] +fmla v31.4s, v1.4s, v5.s[3] + +ld1 {{ v0.4s, v1.4s }}, [x1], #32 +ld1 {{ v4.4s, v5.4s }}, [x2], #32 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop2/broken_chains.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop2/broken_chains.tmpli new file mode 100644 index 0000000..5235ac6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop2/broken_chains.tmpli @@ -0,0 +1,41 @@ +ld1 {{ v2.4s, v3.4s }}, [x1], #32 +ld1 {{ v6.4s, v7.4s }}, [x2], #32 + +fmla v16.4s, v0.4s, v4.s[0] +fmla v17.4s, v1.4s, v4.s[0] +fmla v18.4s, v0.4s, v4.s[1] +fmla v19.4s, v1.4s, v4.s[1] +fmla v20.4s, v0.4s, v4.s[2] +fmla v21.4s, v1.4s, v4.s[2] +fmla v22.4s, v0.4s, v4.s[3] +fmla v23.4s, v1.4s, v4.s[3] + +fmla v24.4s, v0.4s, v5.s[0] +fmla v25.4s, v1.4s, v5.s[0] +fmla v26.4s, v0.4s, v5.s[1] +fmla v27.4s, v1.4s, v5.s[1] +fmla v28.4s, v0.4s, v5.s[2] +fmla v29.4s, v1.4s, v5.s[2] +fmla v30.4s, v0.4s, v5.s[3] +fmla v31.4s, v1.4s, v5.s[3] + +ld1 {{ v0.4s, v1.4s }}, [x1], #32 +ld1 {{ v4.4s, v5.4s }}, [x2], #32 + +fmla v16.4s, v2.4s, v6.s[0] +fmla v17.4s, v3.4s, v6.s[0] +fmla v18.4s, v2.4s, v6.s[1] +fmla v19.4s, v3.4s, v6.s[1] +fmla v20.4s, v2.4s, v6.s[2] +fmla v21.4s, v3.4s, v6.s[2] +fmla v22.4s, v2.4s, v6.s[3] +fmla v23.4s, v3.4s, v6.s[3] + +fmla v24.4s, v2.4s, v7.s[0] +fmla v25.4s, v3.4s, v7.s[0] +fmla v26.4s, v2.4s, v7.s[1] +fmla v27.4s, v3.4s, v7.s[1] +fmla v28.4s, v2.4s, v7.s[2] +fmla v29.4s, v3.4s, v7.s[2] +fmla v30.4s, v2.4s, v7.s[3] +fmla v31.4s, v3.4s, v7.s[3] diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop2/cortex_a55.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop2/cortex_a55.tmpli new file mode 100644 index 0000000..7f87596 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop2/cortex_a55.tmpli @@ -0,0 +1,60 @@ +fmla v16.4s, v0.4s, v4.s[0] +ldr d2, [x1], #8 +fmla v17.4s, v1.4s, v4.s[0] +ldr d6, [x2], #8 +fmla v18.4s, v0.4s, v4.s[1] +ldr x5, [x1], #8 +fmla v19.4s, v1.4s, v4.s[1] +ldr x7, [x2], #8 +fmla v20.4s, v0.4s, v4.s[2] +ldr d3, [x1], #8 +fmla v21.4s, v1.4s, v4.s[2] +ldr d7, [x2], #8 +fmla v22.4s, v0.4s, v4.s[3] +ldr x6, [x1], #8 +fmla v23.4s, v1.4s, v4.s[3] +ldr x8, [x2], #8 + +fmla v24.4s, v0.4s, v5.s[0] +fmla v25.4s, v1.4s, v5.s[0] +fmla v26.4s, v0.4s, v5.s[1] +fmla v27.4s, v1.4s, v5.s[1] +fmla v28.4s, v0.4s, v5.s[2] +ins v2.d[1], x5 +fmla v29.4s, v1.4s, v5.s[2] +ins v6.d[1], x7 +fmla v30.4s, v0.4s, v5.s[3] +ins v3.d[1], x6 +fmla v31.4s, v1.4s, v5.s[3] +ins v7.d[1], x8 + +fmla v16.4s, v2.4s, v6.s[0] +ldr d0, [x1], #8 +fmla v17.4s, v3.4s, v6.s[0] +ldr d4, [x2], #8 +fmla v18.4s, v2.4s, v6.s[1] +ldr x5, [x1], #8 +fmla v19.4s, v3.4s, v6.s[1] +ldr x7, [x2], #8 +fmla v20.4s, v2.4s, v6.s[2] +ldr d1, [x1], #8 +fmla v21.4s, v3.4s, v6.s[2] +ldr d5, [x2], #8 +fmla v22.4s, v2.4s, v6.s[3] +ldr x6, [x1], #8 +fmla v23.4s, v3.4s, v6.s[3] +ldr x8, [x2], #8 + +fmla v24.4s, v2.4s, v7.s[0] +fmla v25.4s, v3.4s, v7.s[0] +fmla v26.4s, v2.4s, v7.s[1] +fmla v27.4s, v3.4s, v7.s[1] +fmla v28.4s, v2.4s, v7.s[2] +ins v0.d[1], x5 +fmla v29.4s, v3.4s, v7.s[2] +ins v4.d[1], x7 +fmla v30.4s, v2.4s, v7.s[3] +ins v1.d[1], x6 +fmla v31.4s, v3.4s, v7.s[3] +ins v5.d[1], x8 + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8_core.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8_core.tmpl new file mode 100644 index 0000000..ed80988 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_8x8_core.tmpl @@ -0,0 +1,181 @@ +// vim: ft=arm + +// C tile regs: v16 to v31, (scratch) +// - x19-x29 to preserve (but x19, x28, x29 not used) +// - d8..d15 to preserve +// - v16 to v31, no need to preserve +// +// v16[0] v18[0] v20[0] v22[0] v24[0] v26[0] v28[0] v30[0] +// v16[1] v18[1] +// v16[2] v18[2] +// v16[3] v18[3] +// +// v17[0] v19[0] v21[0] v23[0] v25[0] v27[0] v29[0] v31[0] +// v17[1] v19[1] +// v17[2] v19[2] +// v17[3] v19[3] + +// v0-v7 (scratch registers) +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +.cpu generic+fp+simd +.global {{G}}arm64simd_mmm_f32_8x8_{{core}}_{{suffix}} +{{G}}arm64simd_mmm_f32_8x8_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.tmpliq" %} + +.add_mat_mul: + ldr x2, [x0, #24] // b + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + +.packed_packed: + ld1 { v0.4s, v1.4s }, [ x1 ], #32 + ld1 { v4.4s, v5.4s }, [ x2 ], #32 + +{% capture packed_packed_loop1 %} + {% if core == "a53" %} + {% include "arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_x_preload.tmpli" %} + {% else %} + {% include "arm64simd_mmm_f32_8x8/packed_packed_loop1/naive.tmpli" %} + {% endif %} +{% endcapture %} + +{% capture packed_packed_loop2 %} + {% if core == "a55" %} + {% include "arm64simd_mmm_f32_8x8/packed_packed_loop2/cortex_a55.tmpli" %} + {% else %} + {{ packed_packed_loop1 }} + {{ packed_packed_loop1 }} + {% endif %} +{% endcapture %} + + cmp x3, #4 + blt .packed_packed_loop_1 + +.p2align 4 +.packed_packed_loop_4: + {{ packed_packed_loop2 }} + {{ packed_packed_loop2 }} + + sub x3, x3, #4 + cmp x3, #4 + bge .packed_packed_loop_4 + + + cmp x3, #0 + beq .non_linear_loop + +.p2align 4 +.packed_packed_loop_1: + {{ packed_packed_loop1 }} + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +{% include "arm64simd_mmm_f32_scalars.tmpliq" from:16, to:31%} +{% include "arm64simd_mmm_f32_per_rows.tmpliq" mr:8, from:16, to:31 %} +{% include "arm64simd_mmm_f32_per_cols.tmpliq" mr:8, from:16, to:31 %} + +.add_unicast: + ldp x5, x6, [x0, #8] + ldp x7, x8, [x0, #24] + + {% for col in (8..15) %} + mov x4, x5 + {% for reg in (0..1) %} + {% for lane in (0..3) %} + ld1 {v0.s}[{{lane}}], [ x4 ], x6 + {% endfor %} + fadd v{{col | times:2 | plus: reg}}.4s, v{{col | times:2 | plus: reg}}.4s, v0.4s + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x2, [x0, #8] + ldr x3, [x0, #16] + + ld1 { v0.4s, v1.4s }, [ x2 ], #32 + ld1 { v4.4s, v5.4s }, [ x3 ], #32 + + fmla v16.4s, v0.4s, v4.s[0] + fmla v17.4s, v1.4s, v4.s[0] + fmla v18.4s, v0.4s, v4.s[1] + fmla v19.4s, v1.4s, v4.s[1] + fmla v20.4s, v0.4s, v4.s[2] + fmla v21.4s, v1.4s, v4.s[2] + fmla v22.4s, v0.4s, v4.s[3] + fmla v23.4s, v1.4s, v4.s[3] + + fmla v24.4s, v0.4s, v5.s[0] + fmla v25.4s, v1.4s, v5.s[0] + fmla v26.4s, v0.4s, v5.s[1] + fmla v27.4s, v1.4s, v5.s[1] + fmla v28.4s, v0.4s, v5.s[2] + fmla v29.4s, v1.4s, v5.s[2] + fmla v30.4s, v0.4s, v5.s[3] + fmla v31.4s, v1.4s, v5.s[3] + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x6, #4 + bne .store_strides_generic + + {% for col in (8..15) %} + str q{{col | times:2 }}, [ x5 ] + str q{{col | times:2 | plus: 1}}, [ x5, #16 ] + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.store_strides_generic: + + {% for col in (8..15) %} + mov x4, x5 + {% for reg in (0..1) %} + {% for lane in (0..3) %} + st1 { v{{col | times:2 | plus: reg}}.s }[{{lane}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.return: + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_per_cols.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_per_cols.tmpliq new file mode 100644 index 0000000..ce1ffe1 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_per_cols.tmpliq @@ -0,0 +1,9 @@ +// vim: ft=arm + +{% include "arm64simd_mmm_4s_per_col.tmpliq" label:"per_col_min", op:"fmin", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_col.tmpliq" label:"per_col_max", op:"fmax", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_col.tmpliq" label:"per_col_mul", op:"fmul", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_col.tmpliq" label:"per_col_add", op:"fadd", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_col.tmpliq" label:"per_col_sub", op:"fsub", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_col.tmpliq" label:"per_col_sub_flipped", op:"fsub", mr:mr, from:from, to:to, flipped: true%} + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_per_rows.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_per_rows.tmpliq new file mode 100644 index 0000000..c518a6b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_per_rows.tmpliq @@ -0,0 +1,9 @@ +// vim: ft=arm + +{% include "arm64simd_mmm_4s_per_row.tmpliq" label:"per_row_min", op:"fmin", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_row.tmpliq" label:"per_row_max", op:"fmax", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_row.tmpliq" label:"per_row_mul", op:"fmul", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_row.tmpliq" label:"per_row_add", op:"fadd", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_row.tmpliq" label:"per_row_sub", op:"fsub", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_row.tmpliq" label:"per_row_sub_flipped", op:"fsub", mr:mr, from:from, to:to, flipped: true%} + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_scalars.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_scalars.tmpliq new file mode 100644 index 0000000..a3b8bfd --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_f32_scalars.tmpliq @@ -0,0 +1,19 @@ +// vim: ft=arm + +{% include "arm64simd_mmm_4s_scalar.tmpliq" label:"scalar_min", op:"fmin", from:from, to:to %} +{% include "arm64simd_mmm_4s_scalar.tmpliq" label:"scalar_max", op:"fmax", from:from, to:to %} +{% include "arm64simd_mmm_4s_scalar.tmpliq" label:"scalar_mul", op:"fmul", from:from, to:to %} +{% include "arm64simd_mmm_4s_scalar.tmpliq" label:"scalar_add", op:"fadd", from:from, to:to %} +{% include "arm64simd_mmm_4s_scalar.tmpliq" label:"scalar_sub", op:"fsub", from:from, to:to %} +{% include "arm64simd_mmm_4s_scalar.tmpliq" label:"scalar_sub_flipped", op:"fsub", from:from, to:to, flipped:true %} + +.clear: +{% for r in (from..to) %} + eor v{{r}}.8b, v{{r}}.8b, v{{r}}.8b +{% endfor %} + b .non_linear_loop + +.q_scale: +.q_shl: +.q_shr: + b .unsupported diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_i32_64x1.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_i32_64x1.tmpl new file mode 100644 index 0000000..d10c0f4 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_i32_64x1.tmpl @@ -0,0 +1,154 @@ +// vim: ft=arm + +// C tile regs: +// - x19-x29 to preserve (but x19, x28, x29 not used) +// - d8..d15 to preserve +// - v16 to v31, no need to preserve + +// no preservation either for v0-v7... +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +.cpu generic+fp+simd +.global {{G}}arm64simd_mmm_i32_64x1_{{suffix}} +{{G}}arm64simd_mmm_i32_64x1_{{suffix}}: + +/* + prfm pldl1keep, [x1] + prfm pldl1keep, [x2] +*/ + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.tmpliq" %} + +.add_mat_mul: + ldr x2, [x0, #24] // b + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + +.packed_packed_loop_1: + ld1 {v9.b}[0], [ x2 ], 1 + sshll v9.8h, v9.8b, 0 + + ld1 { v0.8b-v3.8b }, [ x1 ], #32 + ld1 { v4.8b-v7.8b }, [ x1 ], #32 + + {% for reg in (0..7) %} + sshll v10.8h, v{{reg}}.8b, 0 + smlal v{{reg | times: 2 | plus: 16 }}.4s, v10.4h, v9.h[0] + smlal2 v{{reg | times: 2 | plus: 17 }}.4s, v10.8h, v9.h[0] + {% endfor %} + + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +.add_unicast: + ldp x5, x6, [x0, #8] + ldp x7, x8, [x0, #24] + + cmp x8, #4 + beq non_linear_addc_i32 + + {% for reg in (16..31) %} + {% for lane in (0..3) %} + ld1 {v0.b}[{{lane}}], [ x5 ], x6 + {% endfor %} + sshll v0.8h, v0.8b, 0 + sshll v0.4s, v0.4h, 0 + add v{{reg}}.4s, v{{reg}}.4s, v0.4s + {% endfor %} + + b .non_linear_loop + +non_linear_addc_i32: + {% for reg in (16..31) %} + {% for lane in (0..3) %} + ld1 {v0.s}[{{lane}}], [ x5 ], x6 + {% endfor %} + add v{{reg}}.4s, v{{reg}}.4s, v0.4s + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x2, [x0, #8] + ldr x3, [x0, #16] + + ld1 { v15.s }[0], [ x3 ] + xtn v15.4h, v15.4s + + ld1 { v0.4s-v3.4s }, [ x2 ], #64 + ld1 { v4.4s-v7.4s }, [ x2 ], #64 + + {% for reg in (0..7) %} + xtn v{{reg}}.4h, v{{reg}}.4s + smlal v{{reg|plus: 16}}.4s, v{{reg}}.4h, v15.h[0] + {% endfor %} + + ld1 { v0.4s-v3.4s }, [ x2 ], #64 + ld1 { v4.4s-v7.4s }, [ x2 ], #64 + + {% for reg in (0..7) %} + xtn v{{reg}}.4h, v{{reg}}.4s + smlal v{{reg|plus: 24}}.4s, v{{reg}}.4h, v15.h[0] + {% endfor %} + + b .non_linear_loop + +{% include "arm64simd_mmm_i32_scalars.tmpliq" from:16, to:31 %} +{% include "arm64simd_mmm_i32_per_rows.tmpliq" mr:64, from:16, to:31 %} +{% include "arm64simd_mmm_i32_per_cols.tmpliq" mr:64, from:16, to:31 %} +{% include "arm64simd_mmm_i32_scale_q16_q31.tmpliq" %} + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x8, #4 + beq .store_strides_i32 + + {% for reg in (16..31) %} + {% for lane in (0..3) %} + st1 { v{{reg}}.b }[{{lane | times: 4}}], [ x5 ], x6 + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.store_strides_i32: + {% for reg in (16..31) %} + {% for lane in (0..3) %} + st1 { v{{reg}}.s }[{{lane}}], [ x5 ], x6 + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.return: + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_i32_8x8.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_i32_8x8.tmpl new file mode 100644 index 0000000..9f23a17 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_i32_8x8.tmpl @@ -0,0 +1,200 @@ +// vim: ft=arm + +// C tile regs: +// - x19-x29 to preserve (but x19, x28, x29 not used) +// - d8..d15 to preserve +// - v16 to v31, no need to preserve +// +// v16[0] v18[0] v20[0] v22[0] v24[0] v26[0] v28[0] v30[0] +// v16[1] v18[1] +// v16[2] v18[2] +// v16[3] v18[3] +// +// v17[0] v19[0] v21[0] v23[0] v25[0] v27[0] v29[0] v31[0] +// v17[1] v19[1] +// v17[2] v19[2] +// v17[3] v19[3] + +// no preservation either for v0-v7... +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +.cpu generic+fp+simd +.global {{G}}arm64simd_mmm_i32_8x8_{{suffix}} +{{G}}arm64simd_mmm_i32_8x8_{{suffix}}: + +/* + prfm pldl1keep, [x1] + prfm pldl1keep, [x2] +*/ + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.tmpliq" %} + +.add_mat_mul: + ldr x2, [x0, #24] // b + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + +.packed_packed_loop_1: + + ld1 { v0.8b }, [ x1 ], #8 + sshll v0.8h, v0.8b, 0 + ld1 { v4.8b }, [ x2 ], #8 + sshll v4.8h, v4.8b, 0 + + smlal v16.4s, v0.4h, v4.h[0] + smlal2 v17.4s, v0.8h, v4.h[0] + smlal v18.4s, v0.4h, v4.h[1] + smlal2 v19.4s, v0.8h, v4.h[1] + smlal v20.4s, v0.4h, v4.h[2] + smlal2 v21.4s, v0.8h, v4.h[2] + smlal v22.4s, v0.4h, v4.h[3] + smlal2 v23.4s, v0.8h, v4.h[3] + + smlal v24.4s, v0.4h, v4.h[4] + smlal2 v25.4s, v0.8h, v4.h[4] + smlal v26.4s, v0.4h, v4.h[5] + smlal2 v27.4s, v0.8h, v4.h[5] + smlal v28.4s, v0.4h, v4.h[6] + smlal2 v29.4s, v0.8h, v4.h[6] + smlal v30.4s, v0.4h, v4.h[7] + smlal2 v31.4s, v0.8h, v4.h[7] + + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +{% include "arm64simd_mmm_i32_scalars.tmpliq" from:16, to:31%} +{% include "arm64simd_mmm_i32_per_rows.tmpliq" mr:8, from:16, to:31%} +{% include "arm64simd_mmm_i32_per_cols.tmpliq" mr:8, from:16, to:31%} + +.add_unicast: + ldp x5, x6, [x0, #8] + ldp x7, x8, [x0, #24] + + cmp x8, #4 + beq non_linear_addc_i32 + + {% for col in (8..15) %} + mov x4, x5 + {% for reg in (0..1) %} + {% for lane in (0..3) %} + ld1 {v0.b}[{{lane}}], [ x4 ], x6 + {% endfor %} + sshll v0.8h, v0.8b, 0 + sshll v0.4s, v0.4h, 0 + add v{{col | times:2 | plus: reg}}.4s, v{{col | times:2 | plus: reg}}.4s, v0.4s + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +non_linear_addc_i32: + {% for col in (8..15) %} + mov x4, x5 + {% for reg in (0..1) %} + {% for lane in (0..3) %} + ld1 {v0.s}[{{lane}}], [ x4 ], x6 + {% endfor %} + add v{{col | times:2 | plus: reg}}.4s, v{{col | times:2 | plus: reg}}.4s, v0.4s + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x2, [x0, #8] + ldr x3, [x0, #16] + + ld1 { v0.4s, v1.4s }, [ x2 ] + ld1 { v4.4s, v5.4s }, [ x3 ] + + xtn v0.4h, v0.4s + xtn v1.4h, v1.4s + xtn v4.4h, v4.4s + xtn v5.4h, v5.4s + + smlal v16.4s, v0.4h, v4.h[0] + smlal v17.4s, v1.4h, v4.h[0] + smlal v18.4s, v0.4h, v4.h[1] + smlal v19.4s, v1.4h, v4.h[1] + smlal v20.4s, v0.4h, v4.h[2] + smlal v21.4s, v1.4h, v4.h[2] + smlal v22.4s, v0.4h, v4.h[3] + smlal v23.4s, v1.4h, v4.h[3] + + smlal v24.4s, v0.4h, v5.h[0] + smlal v25.4s, v1.4h, v5.h[0] + smlal v26.4s, v0.4h, v5.h[1] + smlal v27.4s, v1.4h, v5.h[1] + smlal v28.4s, v0.4h, v5.h[2] + smlal v29.4s, v1.4h, v5.h[2] + smlal v30.4s, v0.4h, v5.h[3] + smlal v31.4s, v1.4h, v5.h[3] + + b .non_linear_loop + + {% include "arm64simd_mmm_i32_scale_q16_q31.tmpliq" %} + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x8, #4 + beq .store_strides_i32 + + {% for col in (8..15) %} + mov x4, x5 + {% for reg in (0..1) %} + {% for lane in (0..3) %} + st1 { v{{col | times:2 | plus: reg}}.b }[{{lane|times:4}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.store_strides_i32: + {% for col in (8..15) %} + mov x4, x5 + {% for reg in (0..1) %} + {% for lane in (0..3) %} + st1 { v{{col | times:2 | plus: reg}}.s }[{{lane}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.return: + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_i32_per_cols.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_i32_per_cols.tmpliq new file mode 100644 index 0000000..d770611 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_i32_per_cols.tmpliq @@ -0,0 +1,8 @@ +// vim: ft=arm + +{% include "arm64simd_mmm_4s_per_col.tmpliq" label:"per_col_min", op:"smin", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_col.tmpliq" label:"per_col_max", op:"smax", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_col.tmpliq" label:"per_col_mul", op:"mul", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_col.tmpliq" label:"per_col_add", op:"add", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_col.tmpliq" label:"per_col_sub", op:"sub", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_col.tmpliq" label:"per_col_sub_flipped", op:"sub", mr:mr, from:from, to:to, flipped: true %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_i32_per_rows.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_i32_per_rows.tmpliq new file mode 100644 index 0000000..12fdf9d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_i32_per_rows.tmpliq @@ -0,0 +1,8 @@ +// vim: ft=arm + +{% include "arm64simd_mmm_4s_per_row.tmpliq" label:"per_row_min", op:"smin", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_row.tmpliq" label:"per_row_max", op:"smax", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_row.tmpliq" label:"per_row_mul", op:"mul", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_row.tmpliq" label:"per_row_add", op:"add", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_row.tmpliq" label:"per_row_sub", op:"sub", mr:mr, from:from, to:to %} +{% include "arm64simd_mmm_4s_per_row.tmpliq" label:"per_row_sub_flipped", op:"sub", mr:mr, from:from, to:to, flipped: true %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_i32_scalars.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_i32_scalars.tmpliq new file mode 100644 index 0000000..3328ab1 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_i32_scalars.tmpliq @@ -0,0 +1,14 @@ +// vim: ft=arm + +{% include "arm64simd_mmm_4s_scalar.tmpliq" label:"scalar_min", op:"smin", from:from, to:to%} +{% include "arm64simd_mmm_4s_scalar.tmpliq" label:"scalar_max", op:"smax", from:from, to:to%} +{% include "arm64simd_mmm_4s_scalar.tmpliq" label:"scalar_mul", op:"mul", from:from, to:to%} +{% include "arm64simd_mmm_4s_scalar.tmpliq" label:"scalar_add", op:"add", from:from, to:to%} +{% include "arm64simd_mmm_4s_scalar.tmpliq" label:"scalar_sub", op:"sub", from:from, to:to%} +{% include "arm64simd_mmm_4s_scalar.tmpliq" label:"scalar_sub_flipped", op:"sub", from:from, to:to, flipped:true%} + +.clear: +{% for r in (from..to) %} + eor v{{r}}.8b, v{{r}}.8b, v{{r}}.8b +{% endfor %} + b .non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_i32_scale_q16_q31.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_i32_scale_q16_q31.tmpliq new file mode 100644 index 0000000..fec2f53 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_mmm_i32_scale_q16_q31.tmpliq @@ -0,0 +1,267 @@ + +// vim: ft=arm + +.q_scale: + ldp x5, x6, [x0, #8] // x5: shift, x6: policy + add x2, x0, #24 + ld1r { v2.4s }, [x2] // v2.4s <- multiplier + + mov w3, #1 + ins v4.d[0], x3 + dup v4.2d, v4.d[0] // v4.2d <- 1 + + add x5, x5, #32 // add 32 to shift + neg x5, x5 // broadcast shift + ins v1.d[0], x5 + dup v1.2d, v1.d[0] // v1.2s <- -(shift + 32) + + cmp x6, 1 + beq .q_scale_rounding_zero + cmp x6, 2 + beq .q_scale_rounding_away + cmp x6, 3 + beq .q_scale_rounding_minus_inf + cmp x6, 4 + beq .q_scale_rounding_plus_inf + cmp x6, 5 + beq .q_scale_rounding_even + cmp x6, 6 + beq .q_scale_rounding_odd + + b .unsupported + +.q_scale_rounding_zero: + // rust: signum * ((abs + nudge2) >> shift + // asm: signum * (2*abs - 1) >>r (shift + 1) + + {% for q in (16..31) %} + cmlt v0.4s, v{{q}}.4s, #0 + abs v{{q}}.4s, v{{q}}.4s + sqdmull v8.2d, v{{q}}.2s, v2.2s + sqdmull2 v9.2d, v{{q}}.4s, v2.4s //mul without shift and store results in v8 and v9 + + sub v8.2d, v8.2d, v4.2d + sqrshl v8.2d, v8.2d, v1.2d + + sub v9.2d, v9.2d, v4.2d + sqrshl v9.2d, v9.2d, v1.2d + + uzp1 v{{q}}.4s, v8.4s, v9.4s //combine back + + neg v3.4s, v{{q}}.4s + bit v{{q}}.16b, v3.16b, v0.16b + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_away: // signum * (abs >> (shift-1) + 1 >> 1) + + {% for q in (16..31) %} + cmlt v0.4s, v{{q}}.4s, #0 + abs v{{q}}.4s, v{{q}}.4s + sqdmull v8.2d, v{{q}}.2s, v2.2s + sqdmull2 v9.2d, v{{q}}.4s, v2.4s //mul without shift and store results in v8 and v9 + + sqrshl v8.2d, v8.2d, v1.2d + sqrshl v9.2d, v9.2d, v1.2d + + uzp1 v{{q}}.4s, v8.4s, v9.4s //combine back + + neg v3.4s, v{{q}}.4s + bit v{{q}}.16b, v3.16b, v0.16b + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_minus_inf: // val >> shift + + {% for q in (16..31) %} + sqdmull v8.2d, v{{q}}.2s, v2.2s + sqdmull2 v9.2d, v{{q}}.4s, v2.4s //mul without shift and store results in v8 and v9 + + sub v8.2d, v8.2d, v4.2d + sqrshl v8.2d, v8.2d, v1.2d + + sub v9.2d, v9.2d, v4.2d + sqrshl v9.2d, v9.2d, v1.2d + + uzp1 v{{q}}.4s, v8.4s, v9.4s //combine back + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_plus_inf: // (val >> shift-1)+1 >>1 + + {% for q in (16..31) %} + sqdmull v8.2d, v{{q}}.2s, v2.2s + sqdmull2 v9.2d, v{{q}}.4s, v2.4s //mul without shift and store results in v8 and v9 + + sqrshl v8.2d, v8.2d, v1.2d + sqrshl v9.2d, v9.2d, v1.2d + + uzp1 v{{q}}.4s, v8.4s, v9.4s //combine back + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_even: // signum * ((abs >> shift-1) + (abs & 0x1) - 1 >> 1) + + {% for q in (16..31) %} + cmlt v0.4s, v{{q}}.4s, #0 + abs v{{q}}.4s, v{{q}}.4s + sqdmull v8.2d, v{{q}}.2s, v2.2s + sqdmull2 v9.2d, v{{q}}.4s, v2.4s //mul without shift and store results in v8 and v9 + + sqshl v3.2d, v8.2d, v1.2d // abs >> shift - 1 + and v3.16b, v3.16b, v4.16b // abs & 0x1 + sub v3.2d, v3.2d, v4.2d //nudge : -1 if we want to round down, 0 if up + + add v8.2d, v8.2d, v3.2d + sqrshl v8.2d, v8.2d, v1.2d + + sqshl v3.2d, v9.2d, v1.2d + and v3.16b, v3.16b, v4.16b + sub v3.2d, v3.2d, v4.2d //nudge : -1 if we want to round down, 0 if up + + add v9.2d, v9.2d, v3.2d + sqrshl v9.2d, v9.2d, v1.2d + + uzp1 v{{q}}.4s, v8.4s, v9.4s //combine back + + neg v3.4s, v{{q}}.4s + bit v{{q}}.16b, v3.16b, v0.16b + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_odd: // signum * ((abs >> shift-1) - (abs & 0x1) >> 1) + + {% for q in (16..31) %} + cmlt v0.4s, v{{q}}.4s, #0 + abs v{{q}}.4s, v{{q}}.4s + sqdmull v8.2d, v{{q}}.2s, v2.2s + sqdmull2 v9.2d, v{{q}}.4s, v2.4s //mul without shift and store results in v8 and v9 + + sqshl v3.2d, v8.2d, v1.2d + and v3.16b, v3.16b, v4.16b //nudge : -1 if we want to round down, 0 if up + + sub v8.2d, v8.2d, v3.2d + sqrshl v8.2d, v8.2d, v1.2d + + sqshl v3.2d, v9.2d, v1.2d + and v3.16b, v3.16b, v4.16b //nudge : -1 if we want to round down, 0 if up + + sub v9.2d, v9.2d, v3.2d + sqrshl v9.2d, v9.2d, v1.2d + + uzp1 v{{q}}.4s, v8.4s, v9.4s //combine back + + neg v3.4s, v{{q}}.4s + bit v{{q}}.16b, v3.16b, v0.16b + {% endfor %} + + b .non_linear_loop + +.q_shl: + ldr x5, [x0, #8] // x5: shift + ins v1.s[0], w5 + dup v1.4s, v1.s[0] // v1.4s <- shift + + {% for q in (16..31) %} + sqrshl v{{q}}.4s, v{{q}}.4s, v1.4s + {% endfor %} + b .non_linear_loop + +.q_shr: + ldp x5, x6, [x0, #8] // x5: shift, x6: policy + + mov w3, #1 + ins v4.s[0], w3 + dup v4.4s, v4.s[0] // v4.4d <- 1 + + neg w5, w5 // broadcast shift + ins v1.s[0], w5 + dup v1.4s, v1.s[0] // v1.4s <- -shift + + cmp x6, 1 + beq .q_shr_rounding_zero + cmp x6, 2 + beq .q_shr_rounding_away + cmp x6, 3 + beq .q_shr_rounding_minus_inf + cmp x6, 4 + beq .q_shr_rounding_plus_inf + cmp x6, 5 + beq .q_shr_rounding_even + cmp x6, 6 + beq .q_shr_rounding_odd + + b .unsupported + +.q_shr_rounding_zero: + // asm: signum * (abs >>r shift) + {% for q in (16..31) %} + cmlt v0.4s, v{{q}}.4s, #0 + abs v{{q}}.4s, v{{q}}.4s + + sub v{{q}}.4s, v{{q}}.4s, v4.4s + sqrshl v{{q}}.4s, v{{q}}.4s, v1.4s + + neg v3.4s, v{{q}}.4s + bit v{{q}}.16b, v3.16b, v0.16b + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_away: + {% for q in (16..31) %} + cmlt v0.4s, v{{q}}.4s, #0 + abs v{{q}}.4s, v{{q}}.4s + + sqrshl v{{q}}.4s, v{{q}}.4s, v1.4s + + neg v3.4s, v{{q}}.4s + bit v{{q}}.16b, v3.16b, v0.16b + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_minus_inf: + {% for q in (16..31) %} + sqneg v{{q}}.4s, v{{q}}.4s + sqrshl v{{q}}.4s, v{{q}}.4s, v1.4s + sqneg v{{q}}.4s, v{{q}}.4s + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_plus_inf: + {% for q in (16..31) %} + sqrshl v{{q}}.4s, v{{q}}.4s, v1.4s + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_even: + // sqrshl is round(+inf), sqshl trauncates + // we look at parity of result by truncation: if it's odd, we have nothing more to do, we go towards +inf + // if it's even, we need to nudge towards 0 by adding -1 + // => nudge = (x >>l shift) & 0x1 - 1 (>>l is sqshl) + // => result is (x + nudge) >>r shift (with sqrshl) + {% for q in (16..31) %} + sqshl v3.4s, v{{q}}.4s, v1.4s // trunc + and v3.16b, v3.16b, v4.16b + sub v3.4s, v3.4s, v4.4s + add v{{q}}.4s, v{{q}}.4s, v3.4s + + sqrshl v{{q}}.4s, v{{q}}.4s, v1.4s + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_odd: + // here: nudge is -((x >>l shift) & 0x1) + {% for q in (16..31) %} + sqshl v3.4s, v{{q}}.4s, v1.4s // trunc + and v3.16b, v3.16b, v4.16b + neg v3.4s, v3.4s + add v{{q}}.4s, v{{q}}.4s, v3.4s + + sqrshl v{{q}}.4s, v{{q}}.4s, v1.4s + {% endfor %} + b .non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_sigmoid_f32_4n.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_sigmoid_f32_4n.tmpl new file mode 100644 index 0000000..84b927e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_sigmoid_f32_4n.tmpl @@ -0,0 +1,206 @@ +// vim: ft=arm + +// no preservation either for v0-v7 and v16-v31 + +.text +.align 4 + +.cpu generic+fp+simd +.global {{G}}arm64simd_sigmoid_f32_4n_{{suffix}} +{{G}}arm64simd_sigmoid_f32_4n_{{suffix}}: + + cmp x1, #0 + beq .return + + adr x2, .coeffs_num + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x2] + dup v5.4s, v0.s[0] // v5 <- low, broadcasted + dup v6.4s, v0.s[1] // v6 <- high, broadcasted + dup v7.4s, v3.s[1] // v7 <- 0.5, broadcasted + + cmp x1, #16 + blt .loop + +.loop4: + ld1 { v16.4s, v17.4s, v18.4s, v19.4s }, [x0] + + fmax v16.4s, v16.4s, v5.4s + fmax v17.4s, v17.4s, v5.4s + fmax v18.4s, v18.4s, v5.4s + fmax v19.4s, v19.4s, v5.4s + + fmin v16.4s, v16.4s, v6.4s + fmin v17.4s, v17.4s, v6.4s + fmin v18.4s, v18.4s, v6.4s + fmin v19.4s, v19.4s, v6.4s // v16 <- x + + fmul v20.4s, v16.4s, v16.4s + fmul v21.4s, v17.4s, v17.4s + fmul v22.4s, v18.4s, v18.4s + fmul v23.4s, v19.4s, v19.4s // v20 <- x2 + + dup v24.4s, v0.s[3] + fmla v24.4s, v20.4s, v0.s[2] + dup v25.4s, v0.s[3] + fmla v25.4s, v21.4s, v0.s[2] + dup v26.4s, v0.s[3] + fmla v26.4s, v22.4s, v0.s[2] + dup v27.4s, v0.s[3] + fmla v27.4s, v23.4s, v0.s[2] + + dup v28.4s, v1.s[0] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v1.s[0] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v1.s[0] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v1.s[0] + fmla v31.4s, v23.4s, v27.4s + + dup v24.4s, v1.s[1] + fmla v24.4s, v20.4s, v28.4s + dup v25.4s, v1.s[1] + fmla v25.4s, v21.4s, v29.4s + dup v26.4s, v1.s[1] + fmla v26.4s, v22.4s, v30.4s + dup v27.4s, v1.s[1] + fmla v27.4s, v23.4s, v31.4s + + dup v28.4s, v1.s[2] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v1.s[2] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v1.s[2] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v1.s[2] + fmla v31.4s, v23.4s, v27.4s + + dup v24.4s, v1.s[3] + fmla v24.4s, v20.4s, v28.4s + dup v25.4s, v1.s[3] + fmla v25.4s, v21.4s, v29.4s + dup v26.4s, v1.s[3] + fmla v26.4s, v22.4s, v30.4s + dup v27.4s, v1.s[3] + fmla v27.4s, v23.4s, v31.4s + + dup v28.4s, v2.s[0] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v2.s[0] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v2.s[0] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v2.s[0] + fmla v31.4s, v23.4s, v27.4s + + fmul v16.4s, v16.4s, v28.4s + fmul v17.4s, v17.4s, v29.4s + fmul v18.4s, v18.4s, v30.4s + fmul v19.4s, v19.4s, v31.4s // v16 <- numerator + + dup v24.4s, v2.s[2] + fmla v24.4s, v20.4s, v2.s[1] + dup v25.4s, v2.s[2] + fmla v25.4s, v21.4s, v2.s[1] + dup v26.4s, v2.s[2] + fmla v26.4s, v22.4s, v2.s[1] + dup v27.4s, v2.s[2] + fmla v27.4s, v23.4s, v2.s[1] + + dup v28.4s, v2.s[3] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v2.s[3] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v2.s[3] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v2.s[3] + fmla v31.4s, v23.4s, v27.4s + + dup v24.4s, v3.s[0] + fmla v24.4s, v20.4s, v28.4s + dup v25.4s, v3.s[0] + fmla v25.4s, v21.4s, v29.4s + dup v26.4s, v3.s[0] + fmla v26.4s, v22.4s, v30.4s + dup v27.4s, v3.s[0] + fmla v27.4s, v23.4s, v31.4s // v24 denum + + fdiv v16.4s, v16.4s, v24.4s + fdiv v17.4s, v17.4s, v25.4s + fdiv v18.4s, v18.4s, v26.4s + fdiv v19.4s, v19.4s, v27.4s + + fadd v16.4s, v16.4s, v7.4s + fadd v17.4s, v17.4s, v7.4s + fadd v18.4s, v18.4s, v7.4s + fadd v19.4s, v19.4s, v7.4s + + st1 { v16.4s, v17.4s, v18.4s, v19.4s }, [x0], #64 + + subs x1, x1, #16 + cmp x1, #16 + bge .loop4 + + cmp x1, #0 + beq .return + +.loop: + ld1 { v16.4s }, [x0] + + fmax v16.4s, v16.4s, v5.4s + fmin v16.4s, v16.4s, v6.4s // v16 <- x + fmul v20.4s, v16.4s, v16.4s // v20 <- x2 + + dup v24.4s, v0.s[3] + fmla v24.4s, v20.4s, v0.s[2] + dup v28.4s, v1.s[0] + fmla v28.4s, v20.4s, v24.4s + dup v24.4s, v1.s[1] + fmla v24.4s, v20.4s, v28.4s + dup v28.4s, v1.s[2] + fmla v28.4s, v20.4s, v24.4s + dup v24.4s, v1.s[3] + fmla v24.4s, v20.4s, v28.4s + dup v28.4s, v2.s[0] + fmla v28.4s, v20.4s, v24.4s + fmul v16.4s, v16.4s, v28.4s // v16 <- numerator + + dup v24.4s, v2.s[2] + fmla v24.4s, v20.4s, v2.s[1] + dup v28.4s, v2.s[3] + fmla v28.4s, v20.4s, v24.4s + dup v24.4s, v3.s[0] + fmla v24.4s, v20.4s, v28.4s // v24 <- denum + + fdiv v16.4s, v16.4s, v24.4s + fadd v16.4s, v16.4s, v7.4s + + st1 { v16.4s }, [x0], #16 + + subs x1, x1, #4 + bne .loop + +.return: + ret + +.coeffs_num: + .float -18.6 // low + .float 18.6 // high + .float -4.433153405e-18 // alpha_13 + .float 1.169974371e-14 + + .float -1.875289645e-11 + .float 4.257889523e-8 + .float 0.00004811817576 + .float 0.008163842030 + + .float 0.2499999971 + .float 3.922935744e-6 // beta_6 + .float 0.001524872358 + .float 0.1159886749 + + .float 1.0 + .float 0.5 // + .float 0.0 // padding + .float 0.0 + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_tanh_f32_4n.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_tanh_f32_4n.tmpl new file mode 100644 index 0000000..dc88569 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/arm64simd_tanh_f32_4n.tmpl @@ -0,0 +1,198 @@ +// vim: ft=arm + +// no preservation either for v0-v7 and v16-v31 + +.text +.align 4 + +.cpu generic+fp+simd +.global {{G}}arm64simd_tanh_f32_4n_{{suffix}} +{{G}}arm64simd_tanh_f32_4n_{{suffix}}: + + cmp x1, #0 + beq .return + + adr x2, .coeffs_num + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x2] + dup v5.4s, v0.s[0] // v5 <- low, broadcasted + dup v6.4s, v0.s[1] // v6 <- high, broadcasted + + cmp x1, #16 + blt .loop + +.loop4: + ld1 { v16.4s, v17.4s, v18.4s, v19.4s }, [x0] + + fmax v16.4s, v16.4s, v5.4s + fmax v17.4s, v17.4s, v5.4s + fmax v18.4s, v18.4s, v5.4s + fmax v19.4s, v19.4s, v5.4s + + fmin v16.4s, v16.4s, v6.4s + fmin v17.4s, v17.4s, v6.4s + fmin v18.4s, v18.4s, v6.4s + fmin v19.4s, v19.4s, v6.4s // v16 <- x + + fmul v20.4s, v16.4s, v16.4s + fmul v21.4s, v17.4s, v17.4s + fmul v22.4s, v18.4s, v18.4s + fmul v23.4s, v19.4s, v19.4s // v20 <- x2 + + dup v24.4s, v0.s[3] + fmla v24.4s, v20.4s, v0.s[2] + dup v25.4s, v0.s[3] + fmla v25.4s, v21.4s, v0.s[2] + dup v26.4s, v0.s[3] + fmla v26.4s, v22.4s, v0.s[2] + dup v27.4s, v0.s[3] + fmla v27.4s, v23.4s, v0.s[2] + + dup v28.4s, v1.s[0] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v1.s[0] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v1.s[0] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v1.s[0] + fmla v31.4s, v23.4s, v27.4s + + dup v24.4s, v1.s[1] + fmla v24.4s, v20.4s, v28.4s + dup v25.4s, v1.s[1] + fmla v25.4s, v21.4s, v29.4s + dup v26.4s, v1.s[1] + fmla v26.4s, v22.4s, v30.4s + dup v27.4s, v1.s[1] + fmla v27.4s, v23.4s, v31.4s + + dup v28.4s, v1.s[2] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v1.s[2] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v1.s[2] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v1.s[2] + fmla v31.4s, v23.4s, v27.4s + + dup v24.4s, v1.s[3] + fmla v24.4s, v20.4s, v28.4s + dup v25.4s, v1.s[3] + fmla v25.4s, v21.4s, v29.4s + dup v26.4s, v1.s[3] + fmla v26.4s, v22.4s, v30.4s + dup v27.4s, v1.s[3] + fmla v27.4s, v23.4s, v31.4s + + dup v28.4s, v2.s[0] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v2.s[0] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v2.s[0] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v2.s[0] + fmla v31.4s, v23.4s, v27.4s + + fmul v16.4s, v16.4s, v28.4s + fmul v17.4s, v17.4s, v29.4s + fmul v18.4s, v18.4s, v30.4s + fmul v19.4s, v19.4s, v31.4s // v16 <- numerator + + dup v24.4s, v2.s[2] + fmla v24.4s, v20.4s, v2.s[1] + dup v25.4s, v2.s[2] + fmla v25.4s, v21.4s, v2.s[1] + dup v26.4s, v2.s[2] + fmla v26.4s, v22.4s, v2.s[1] + dup v27.4s, v2.s[2] + fmla v27.4s, v23.4s, v2.s[1] + + dup v28.4s, v2.s[3] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v2.s[3] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v2.s[3] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v2.s[3] + fmla v31.4s, v23.4s, v27.4s + + dup v24.4s, v3.s[0] + fmla v24.4s, v20.4s, v28.4s + dup v25.4s, v3.s[0] + fmla v25.4s, v21.4s, v29.4s + dup v26.4s, v3.s[0] + fmla v26.4s, v22.4s, v30.4s + dup v27.4s, v3.s[0] + fmla v27.4s, v23.4s, v31.4s // v24 denum + + fdiv v16.4s, v16.4s, v24.4s + fdiv v17.4s, v17.4s, v25.4s + fdiv v18.4s, v18.4s, v26.4s + fdiv v19.4s, v19.4s, v27.4s + + st1 { v16.4s, v17.4s, v18.4s, v19.4s }, [x0], #64 + + subs x1, x1, #16 + cmp x1, #16 + bge .loop4 + + cmp x1, #0 + beq .return + +.loop: + ld1 { v16.4s }, [x0] + + fmax v16.4s, v16.4s, v5.4s + fmin v16.4s, v16.4s, v6.4s // v16 <- x + fmul v20.4s, v16.4s, v16.4s // v20 <- x2 + + dup v24.4s, v0.s[3] + fmla v24.4s, v20.4s, v0.s[2] + dup v28.4s, v1.s[0] + fmla v28.4s, v20.4s, v24.4s + dup v24.4s, v1.s[1] + fmla v24.4s, v20.4s, v28.4s + dup v28.4s, v1.s[2] + fmla v28.4s, v20.4s, v24.4s + dup v24.4s, v1.s[3] + fmla v24.4s, v20.4s, v28.4s + dup v28.4s, v2.s[0] + fmla v28.4s, v20.4s, v24.4s + fmul v16.4s, v16.4s, v28.4s // v16 <- numerator + + dup v24.4s, v2.s[2] + fmla v24.4s, v20.4s, v2.s[1] + dup v28.4s, v2.s[3] + fmla v28.4s, v20.4s, v24.4s + dup v24.4s, v3.s[0] + fmla v24.4s, v20.4s, v28.4s // v24 <- denum + + fdiv v16.4s, v16.4s, v24.4s + + st1 { v16.4s }, [x0], #16 + + subs x1, x1, #4 + bne .loop + +.return: + ret + +.coeffs_num: + .float -8.9 // low + .float 8.9 // high + .float -8.488492677e-14 // alpha_13 + .float 5.277853000e-11 + + .float -2.022500419e-8 + .float 0.00001115424833 + .float 0.003103950131 + .float 0.1308400453 + + .float 0.9999999934 + .float 0.0002546136580 // beta_6 + .float 0.02449515379 + .float 0.4641733162 + + .float 1.0 + .float 0 // padding + .float 0 // padding + .float 0 // padding diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/dispatcher.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/dispatcher.tmpliq new file mode 100644 index 0000000..150db46 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/arm64/arm64simd/dispatcher.tmpliq @@ -0,0 +1,37 @@ +// vim: ft=arm + +.non_linear: + sub x0, x0, 40 + +.non_linear_loop: + add x0, x0, 40 + ldr x2, [x0] + + mov x4, #{{ jump_table | size }} + + cmp x2, #{{ jump_table | size }} + csel x2, x2, x4, lt + cmp x2, #0 + csel x2, x4, x2, lt + + adr x3, .jmp_table + add x3, x3, x2, LSL#2 + br x3 + +.jmp_table: +{% for j in jump_table %} + b .{{j}} +{% endfor %} + b .unsupported + + add x0, x2, #4000 + b .return + +.unsupported: + mov x0, #1 + b .return + +.done: + mov x0, 0 + b .return + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/arm32neon.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/arm32neon.rs new file mode 100644 index 0000000..815eb9c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/arm32neon.rs @@ -0,0 +1,191 @@ +#![feature(asm)] +#![allow( + dead_code, + non_upper_case_globals, + unused_macros, + non_snake_case, + unused_assignments +)] + +use std::time::Instant; + +macro_rules! r2 { ($($stat:stmt)*) => { $( $stat )* $( $stat )* } } +macro_rules! r4 { ($($stat:stmt)*) => { r2!(r2!($($stat)*)) }} +macro_rules! r8 { ($($stat:stmt)*) => { r4!(r2!($($stat)*)) }} +macro_rules! r16 { ($($stat:stmt)*) => { r4!(r4!($($stat)*)) }} +macro_rules! r32 { ($($stat:stmt)*) => { r8!(r4!($($stat)*)) }} +macro_rules! r64 { ($($stat:stmt)*) => { r8!(r8!($($stat)*)) }} +macro_rules! r128 { ($($stat:stmt)*) => { r8!(r16!($($stat)*)) }} +macro_rules! r1024 { ($($stat:stmt)*) => { r8!(r128!($($stat)*)) }} +macro_rules! r4096 { ($($stat:stmt)*) => { r4!(r1024!($($stat)*)) }} + +const _F32: [f32; 1024] = [12.; 1024]; +const F32: *const f32 = _F32.as_ptr(); + +/* +fn ruin_cache() { +let _a = (0..1000000).collect::>(); +} +*/ + +macro_rules! b { + ($f: block, $inner_loop: expr, $measures: expr) => {{ + let mut values = Vec::with_capacity($measures); + for _ in 0..$measures { + // ruin_cache(); + let start = Instant::now(); + for _ in 0..$inner_loop { + unsafe { $f }; + } + values.push(start.elapsed()); + } + values.sort(); + values[$measures / 2].as_nanos() as f64 / 1e9 / $inner_loop as f64 + }}; +} + +fn main() { + let cycle = b!( + { + r1024!(asm!("orr r0, r0, r0", out("r0") _)); + }, + 1000, + 1000 + ) / 1024.; + let indep_fmla = b!( + { + r8!(asm!(" + vmla.f32 q0, q0, q0 + vmla.f32 q1, q1, q1 + vmla.f32 q2, q2, q2 + vmla.f32 q3, q3, q3 + vmla.f32 q4, q4, q4 + vmla.f32 q5, q5, q5 + vmla.f32 q6, q6, q6 + vmla.f32 q7, q7, q7 + ", out("q0") _, out("q1") _, out("q2") _, out("q3") _, out("q4") _, out("q5") _, out("q6") _, out("q7") _)); + }, + 1000, + 1000 + ) / 64.; + eprintln!("rcp tp: indep fmla: {}", indep_fmla / cycle); + let dep_accu_fmla = b!( + { + r16!(asm!(" + vmla.f32 q15, q0, q0 + vmla.f32 q15, q1, q1 + vmla.f32 q15, q2, q2 + vmla.f32 q15, q3, q3 + vmla.f32 q15, q4, q4 + vmla.f32 q15, q5, q5 + vmla.f32 q15, q6, q6 + vmla.f32 q15, q7, q7 + vmla.f32 q15, q8, q8 + vmla.f32 q15, q9, q9 + vmla.f32 q15, q10, q10 + vmla.f32 q15, q11, q11 + vmla.f32 q15, q12, q12 + vmla.f32 q15, q13, q13 + vmla.f32 q15, q14, q14 + ", out("q0") _, out("q1") _, out("q2") _, out("q3") _, out("q4") _, out("q5") _, out("q6") _, out("q7") _, + out("q8") _, out("q9") _, out("q10") _, out("q11") _, out("q12") _, out("q13") _, out("q14") _, out("q15") _)); + }, + 1000, + 1000 + ) / 16. + / 15.; + eprintln!("rcp tp: accu-dep fmla: {}", dep_accu_fmla / cycle); + let load_s_using_vld1_64 = b!( + { + let mut p = F32; + r16!(asm!(" + vld1.64 {{d0-d3}}, [{0}]! + vld1.64 {{d4-d7}}, [{0}]! + vld1.64 {{d8-d11}}, [{0}]! + vld1.64 {{d12-d15}}, [{0}]! + vld1.64 {{d16-d19}}, [{0}]! + vld1.64 {{d20-d23}}, [{0}]! + vld1.64 {{d24-d27}}, [{0}]! + vld1.64 {{d28-d31}}, [{0}]! + ", + inout(reg) p, + out("q0") _, out("q1") _, out("q2") _, out("q3") _, out("q4") _, out("q5") _, out("q6") _, out("q7") _, + out("q8") _, out("q9") _, out("q10") _, out("q11") _, out("q12") _, out("q13") _, out("q14") _, out("q15") _)); + }, + 1000, + 1000 + ) / 16. + / 64.; // each line load 8 s + eprintln!( + "rcp tp: load s using vld1_64 ia {}", + load_s_using_vld1_64 / cycle + ); + let load_s_using_vldm_q = b!( + { + let mut p = F32; + r16!(asm!(" + vldm {0}!, {{q0-q3}} + vldm {0}!, {{q4-q7}} + vldm {0}!, {{q8-q11}} + vldm {0}!, {{q12-q15}} + ", + inout(reg) p, + out("q0") _, out("q1") _, out("q2") _, out("q3") _, out("q4") _, out("q5") _, out("q6") _, out("q7") _, + out("q8") _, out("q9") _, out("q10") _, out("q11") _, out("q12") _, out("q13") _, out("q14") _, out("q15") _)); + }, + 1000, + 1000 + ) / 16. + / 64.; + eprintln!( + "rcp tp: load s using vldmia q: {}", + load_s_using_vldm_q / cycle + ); + let load = b!( + { + let mut p = F32; + r16!(asm!(" + vldr.64 d0, [{0}] + vldr.64 d1, [{0}, #8] + vldr.64 d2, [{0}, #16] + vldr.64 d3, [{0}, #24] + vldr.64 d4, [{0}, #32] + vldr.64 d5, [{0}, #40] + vldr.64 d6, [{0}, #48] + vldr.64 d7, [{0}, #56] + vldr.64 d8, [{0}, #64] + vldr.64 d9, [{0}, #72] + vldr.64 d10, [{0}, #80] + vldr.64 d11, [{0}, #88] + vldr.64 d12, [{0}, #96] + vldr.64 d13, [{0}, #104] + vldr.64 d14, [{0}, #112] + vldr.64 d15, [{0}, #120] + vldr.64 d16, [{0}, #128] + vldr.64 d17, [{0}, #136] + vldr.64 d18, [{0}, #144] + vldr.64 d19, [{0}, #152] + vldr.64 d20, [{0}, #160] + vldr.64 d21, [{0}, #168] + vldr.64 d22, [{0}, #176] + vldr.64 d23, [{0}, #184] + vldr.64 d24, [{0}, #192] + vldr.64 d25, [{0}, #200] + vldr.64 d26, [{0}, #208] + vldr.64 d27, [{0}, #216] + vldr.64 d28, [{0}, #224] + vldr.64 d29, [{0}, #232] + vldr.64 d30, [{0}, #240] + vldr.64 d31, [{0}, #248] + add {0}, #256 + ", + inout(reg) p, + out("q0") _, out("q1") _, out("q2") _, out("q3") _, out("q4") _, out("q5") _, out("q6") _, out("q7") _, + out("q8") _, out("q9") _, out("q10") _, out("q11") _, out("q12") _, out("q13") _, out("q14") _, out("q15") _)); + }, + 1000, + 1000 + ) / 16. + / 64.; + eprintln!("rcp tp: load s using vldr d + imm: {}", load / cycle); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/arm64.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/arm64.rs new file mode 100644 index 0000000..6d19732 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/arm64.rs @@ -0,0 +1,88 @@ +use std::time::Instant; + +use tract_data::prelude::*; +use tract_linalg::frame::mmm::LinearSpec; +use tract_linalg::frame::mmm::MatMatMulKer; +use tract_linalg::frame::mmm::MatMatMulKerSpec; +use tract_linalg::mmm::{InputStoreKer, OutputStoreKer}; + +fn ruin_cache() { + let _a = (0..1000000).collect::>(); +} + +fn bench_to_nanos>( + k: usize, + loops: usize, +) -> f64 { + let item_size = T::datum_type().size_of(); + let a = Tensor::zero_aligned::( + &[(k + K::end_padding_packed_a()) * K::mr()], + K::alignment_bytes_packed_a(), + ) + .unwrap(); + let b = Tensor::zero_aligned::( + &[(k + K::end_padding_packed_b()) * K::nr()], + K::alignment_bytes_packed_b(), + ) + .unwrap(); + let mut c = Tensor::zero::(&[K::mr() * K::nr()]).unwrap(); + let ref a = InputStoreKer::Packed { + ptr: unsafe { a.as_ptr_unchecked::() as _ }, + }; + let ref b = InputStoreKer::Packed { + ptr: unsafe { b.as_ptr_unchecked::() as _ }, + }; + let ref c = OutputStoreKer { + ptr: unsafe { c.as_ptr_mut_unchecked::() as _ }, + item_size, + col_byte_stride: (item_size * K::mr()) as isize, + row_byte_stride: item_size as isize, + }; + let ref linear = LinearSpec::Mul { k }; + let op = MatMatMulKerSpec { + a, + b, + c, + linear, + non_linear: std::ptr::null(), + }; + let mut values = Vec::with_capacity(loops); + for _ in 0..loops { + ruin_cache(); + let start = Instant::now(); + K::kernel(&op); + values.push(start.elapsed()); + } + values.sort(); + values[loops / 2].as_nanos() as f64 +} + +fn model>() -> (f64, f64) { + let x = 1000; + let zp = bench_to_nanos::(0, 10000); + let y = bench_to_nanos::(x, 1000); + let slope = (y - zp) / x as f64; + (slope, zp) +} + +fn as_match_line>() { + let coeffs = model::(); + println!( + "({:?}, {}, {}) => {} * k + {},", + K::name(), + K::mr(), + K::nr(), + (coeffs.0 * 1000.).round(), + (coeffs.1 * 1000.).round() + ); +} + +fn main() { + use tract_linalg::arm64::*; + as_match_line::(); + as_match_line::(); + as_match_line::(); + as_match_line::(); + as_match_line::(); + as_match_line::(); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/arm64simd.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/arm64simd.rs new file mode 100644 index 0000000..ec78c1b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/arm64simd.rs @@ -0,0 +1,1095 @@ +#![allow( + dead_code, + non_upper_case_globals, + unused_macros, + non_snake_case, + unused_assignments +)] +#![feature(aarch64_target_feature)] + +use std::arch::asm; + +mod nano; + +#[repr(C, align(8))] +struct Floats([f32; 4096]); +const _F32: Floats = Floats([12.; 4096]); +const F32: *const f32 = (&_F32) as *const Floats as *const f32; + +lazy_static::lazy_static! { + static ref TICK: f64 = unsafe { b8192!(asm!("orr x20, x20, x20", out("x20") _)) }; +} + +pub unsafe fn armv8(filter: Option<&str>) { + macro_rules! s32 { + ($label: literal, $n: expr, $stmt:block) => { + if $label.contains(filter.unwrap_or("")) { + println!("{:40} {:.2}", $label, b32!($stmt) / $n as f64 / *TICK); + } + }; + } + + macro_rules! s128 { + ($label: literal, $n: expr, $stmt:block) => { + if $label.contains(filter.unwrap_or("")) { + println!("{:40} {:.2}", $label, b128!($stmt) / $n as f64 / *TICK); + } + }; + } + + macro_rules! s1024 { + ($label: literal, $n: expr, $stmt:block) => { + if $label.contains(filter.unwrap_or("")) { + println!("{:40} {:.2}", $label, b1024!($stmt) / $n as f64 / *TICK); + } + }; + } + + macro_rules! s8192 { + ($label: literal, $n: expr, $stmt:block) => { + if $label.contains(filter.unwrap_or("")) { + println!("{:40} {:.2}", $label, b8192!($stmt) / $n as f64 / *TICK); + } + }; + } + + s128!("nop", 1, { asm!("nop") }); + s128!("vands", 4, { + asm!(" and v0.16b, v1.16b, v1.16b + and v2.16b, v3.16b, v3.16b + and v4.16b, v5.16b, v5.16b + and v6.16b, v7.16b, v7.16b ", + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + ) + }); + s128!("fmla", 16, { + asm!(" fmla v0.4s, v0.4s, v0.4s + fmla v1.4s, v1.4s, v1.4s + fmla v2.4s, v2.4s, v2.4s + fmla v3.4s, v3.4s, v3.4s + fmla v4.4s, v4.4s, v4.4s + fmla v5.4s, v5.4s, v5.4s + fmla v6.4s, v6.4s, v6.4s + fmla v7.4s, v7.4s, v7.4s + fmla v8.4s, v8.4s, v8.4s + fmla v9.4s, v9.4s, v9.4s + fmla v10.4s,v10.4s,v10.4s + fmla v11.4s,v11.4s,v11.4s + fmla v12.4s,v12.4s,v12.4s + fmla v13.4s,v13.4s,v13.4s + fmla v14.4s,v14.4s,v14.4s + fmla v15.4s,v15.4s,v15.4s ", + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + ) + }); + + s128!("fmla_with_dep", 1, { + asm!("fmla v0.4s, v0.4s, v0.4s", out("v0") _) + }); + s32!("w_load", 64, { + let mut p = F32; + r8!(asm!("ldr w20, [{0}] + ldr w21, [{0}] + ldr w22, [{0}] + ldr w23, [{0}] + ldr w24, [{0}] + ldr w25, [{0}] + ldr w26, [{0}] + ldr w27, [{0}]", + inout(reg) p, + out("x20") _, out("x21") _, out("x22") _, out("x23") _, + out("x24") _, out("x25") _, out("x26") _, out("x27") _, + )); + }); + s32!("x_load", 64, { + let mut p = F32; + r8!(asm!(" + ldr x20, [{0}] + ldr x21, [{0}] + ldr x22, [{0}] + ldr x23, [{0}] + ldr x24, [{0}] + ldr x25, [{0}] + ldr x26, [{0}] + ldr x27, [{0}] + ", + inout(reg) p, + out("x20") _, out("x21") _, out("x22") _, out("x23") _, + out("x24") _, out("x25") _, out("x26") _, out("x27") _, + )); + }); + s32!("d_load", 64, { + let mut p = F32; + r8!(asm!(" + ldr d20, [{0}] + ldr d21, [{0}] + ldr d22, [{0}] + ldr d23, [{0}] + ldr d24, [{0}] + ldr d25, [{0}] + ldr d26, [{0}] + ldr d27, [{0}] + ", + inout(reg) p, + out("v20") _, out("v21") _, out("v22") _, out("v23") _, + out("v24") _, out("v25") _, out("v26") _, out("v27") _, + )); + }); + s32!("s_load", 64, { + let mut p = F32; + r8!(asm!(" + ld1 {{v20.s}}[0], [{0}] + ld1 {{v21.s}}[0], [{0}] + ld1 {{v22.s}}[0], [{0}] + ld1 {{v23.s}}[0], [{0}] + ld1 {{v24.s}}[0], [{0}] + ld1 {{v25.s}}[0], [{0}] + ld1 {{v26.s}}[0], [{0}] + ld1 {{v27.s}}[0], [{0}] + ", + inout(reg) p, + out("v20") _, out("v21") _, out("v22") _, out("v23") _, + out("v24") _, out("v25") _, out("v26") _, out("v27") _, + )); + }); + s32!("d_load_as_v", 64, { + let mut p = F32; + r8!(asm!(" + ld1 {{v20.d}}[0], [{0}] + ld1 {{v21.d}}[0], [{0}] + ld1 {{v22.d}}[0], [{0}] + ld1 {{v23.d}}[0], [{0}] + ld1 {{v24.d}}[0], [{0}] + ld1 {{v25.d}}[0], [{0}] + ld1 {{v26.d}}[0], [{0}] + ld1 {{v27.d}}[0], [{0}] + ", + inout(reg) p, + out("v20") _, out("v21") _, out("v22") _, out("v23") _, + out("v24") _, out("v25") _, out("v26") _, out("v27") _, + )); + }); + s32!("v_load", 64, { + let mut p = F32; + r8!(asm!(" + ld1 {{v20.4s}}, [{0}] + ld1 {{v21.4s}}, [{0}] + ld1 {{v22.4s}}, [{0}] + ld1 {{v23.4s}}, [{0}] + ld1 {{v24.4s}}, [{0}] + ld1 {{v25.4s}}, [{0}] + ld1 {{v26.4s}}, [{0}] + ld1 {{v27.4s}}, [{0}] + ", + inout(reg) p, + out("v20") _, out("v21") _, out("v22") _, out("v23") _, + out("v24") _, out("v25") _, out("v26") _, out("v27") _, + )); + }); + s32!("v2_load", 64, { + let mut p = F32; + r8!(asm!(" + ld1 {{v0.4s, v1.4s}}, [{0}] + ld1 {{v2.4s, v3.4s}}, [{0}] + ld1 {{v4.4s, v5.4s}}, [{0}] + ld1 {{v6.4s, v7.4s}}, [{0}] + ld1 {{v8.4s, v9.4s}}, [{0}] + ld1 {{v10.4s, v11.4s}}, [{0}] + ld1 {{v12.4s, v13.4s}}, [{0}] + ld1 {{v14.4s, v15.4s}}, [{0}] + ", + inout(reg) p, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("v3_load", 32, { + let mut p = F32; + r8!(asm!(" + ld1 {{v0.4s, v1.4s, v2.4s}}, [{0}] + ld1 {{v3.4s, v4.4s, v5.4s}}, [{0}] + ld1 {{v6.4s, v7.4s, v8.4s}}, [{0}] + ld1 {{v9.4s, v10.4s, v11.4s}}, [{0}] + ", + inout(reg) p, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + )); + }); + s32!("v4_load", 32, { + let mut p = F32; + r8!(asm!(" + ld1 {{v0.4s, v1.4s, v2.4s, v3.4s}}, [{0}] + ld1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{0}] + ld1 {{v8.4s, v9.4s, v10.4s, v11.4s}}, [{0}] + ld1 {{v12.4s, v13.4s, v14.4s, v15.4s}}, [{0}] + ", + inout(reg) p, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("ins_32b", 64, { + r8!(asm!(" + ins v8.s[0], w20 + ins v9.s[0], w20 + ins v10.s[0], w20 + ins v11.s[0], w20 + ins v12.s[0], w20 + ins v13.s[0], w20 + ins v14.s[0], w20 + ins v15.s[0], w20 + ", + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("ins_32b_same_lane", 128, { + r8!(asm!(" + ins v0.s[0], w20 + ins v1.s[0], w20 + ins v4.s[0], w20 + ins v5.s[0], w20 + ins v0.s[1], w20 + ins v1.s[1], w20 + ins v4.s[1], w20 + ins v5.s[1], w20 + ins v0.s[2], w20 + ins v1.s[2], w20 + ins v4.s[2], w20 + ins v5.s[2], w20 + ins v0.s[3], w20 + ins v1.s[3], w20 + ins v4.s[3], w20 + ins v5.s[3], w20 + ", + out("v0") _, out("v1") _, out("v4") _, out("v5") _, + )); + }); + s32!("ins_64b", 64, { + r8!(asm!(" + ins v8.d[0], x20 + ins v9.d[0], x20 + ins v10.d[0], x20 + ins v11.d[0], x20 + ins v12.d[0], x20 + ins v13.d[0], x20 + ins v14.d[0], x20 + ins v15.d[0], x20 + ", + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("ins_64b_same_v", 64, { + r8!(asm!(" + ins v8.d[0], x20 + ins v8.d[1], x20 + ins v8.d[0], x20 + ins v8.d[1], x20 + ins v8.d[0], x20 + ins v8.d[1], x20 + ins v8.d[0], x20 + ins v8.d[1], x20 + ", + out("v8") _, + )); + }); + s32!("ins_64b_from_v", 64, { + r8!(asm!(" + ins v8.d[0], v9.d[0] + ins v8.d[1], v9.d[0] + ins v8.d[0], v9.d[1] + ins v8.d[1], v9.d[1] + ins v8.d[0], v9.d[0] + ins v8.d[1], v9.d[0] + ins v8.d[0], v9.d[1] + ins v8.d[1], v9.d[1] + ", + out("v8") _, + )); + }); + s32!("fmla_with_prfm", 64, { + let mut p = F32; + r8!(asm!(" + prfm pldl1keep, [{0}, #256] + fmla v0.4s, v0.4s, v0.4s + prfm pldl1keep, [{0}, #320] + fmla v1.4s, v1.4s, v1.4s + prfm pldl1keep, [{0}, #384] + fmla v2.4s, v2.4s, v2.4s + prfm pldl1keep, [{0}, #448] + fmla v3.4s, v3.4s, v3.4s + prfm pldl1keep, [{0}, #512] + fmla v4.4s, v4.4s, v4.4s + prfm pldl1keep, [{0}, #576] + fmla v5.4s, v5.4s, v5.4s + prfm pldl1keep, [{0}, #640] + fmla v6.4s, v6.4s, v6.4s + prfm pldl1keep, [{0}, #704] + fmla v7.4s, v7.4s, v7.4s + prfm pldl1keep, [{0}, #768] + ", + inout(reg) p, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + )); + }); + s32!("fmla_with_w_load", 64, { + let mut p = F32; + r8!(asm!(" + ldr w20, [{0}] + fmla v0.4s, v0.4s, v0.4s + ldr w21, [{0}] + fmla v1.4s, v1.4s, v1.4s + ldr w22, [{0}] + fmla v2.4s, v2.4s, v2.4s + ldr w23, [{0}] + fmla v3.4s, v3.4s, v3.4s + ldr w24, [{0}] + fmla v4.4s, v4.4s, v4.4s + ldr w25, [{0}] + fmla v5.4s, v5.4s, v5.4s + ldr w26, [{0}] + fmla v6.4s, v6.4s, v6.4s + ldr w27, [{0}] + fmla v7.4s, v7.4s, v7.4s + ", + inout(reg) p, + out("x20") _, out("x21") _, out("x22") _, out("x23") _, + out("x24") _, out("x25") _, out("x26") _, out("x27") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + )); + }); + s32!("fmla_with_w_load_inc", 64, { + let mut p = F32; + r8!(asm!(" + ldr w20, [{0}], #4 + fmla v0.4s, v0.4s, v0.4s + ldr w21, [{0}], #4 + fmla v1.4s, v1.4s, v1.4s + ldr w22, [{0}], #4 + fmla v2.4s, v2.4s, v2.4s + ldr w23, [{0}], #4 + fmla v3.4s, v3.4s, v3.4s + ldr w24, [{0}], #4 + fmla v4.4s, v4.4s, v4.4s + ldr w25, [{0}], #4 + fmla v5.4s, v5.4s, v5.4s + ldr w26, [{0}], #4 + fmla v6.4s, v6.4s, v6.4s + ldr w27, [{0}], #4 + fmla v7.4s, v7.4s, v7.4s + ", + inout(reg) p, + out("x20") _, out("x21") _, out("x22") _, out("x23") _, + out("x24") _, out("x25") _, out("x26") _, out("x27") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + )); + }); + s32!("fmla_with_w_load_inc_alt", 64, { + let mut p = F32; + let mut q = F32; + r8!(asm!(" + ldr w20, [{0}], #4 + fmla v0.4s, v0.4s, v0.4s + ldr w21, [{1}], #4 + fmla v1.4s, v1.4s, v1.4s + ldr w22, [{0}], #4 + fmla v2.4s, v2.4s, v2.4s + ldr w23, [{1}], #4 + fmla v3.4s, v3.4s, v3.4s + ldr w24, [{0}], #4 + fmla v4.4s, v4.4s, v4.4s + ldr w25, [{1}], #4 + fmla v5.4s, v5.4s, v5.4s + ldr w26, [{0}], #4 + fmla v6.4s, v6.4s, v6.4s + ldr w27, [{1}], #4 + fmla v7.4s, v7.4s, v7.4s + ", + inout(reg) p, inout(reg) q, + out("x20") _, out("x21") _, out("x22") _, out("x23") _, + out("x24") _, out("x25") _, out("x26") _, out("x27") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + )); + }); + s32!("fmla_with_w_load_offset", 64, { + let mut p = F32; + r8!(asm!(" + ldr w20, [{0}] + fmla v0.4s, v0.4s, v0.4s + ldr w21, [{0}, #4] + fmla v1.4s, v1.4s, v1.4s + ldr w22, [{0}, #8] + fmla v2.4s, v2.4s, v2.4s + ldr w23, [{0}, #12] + fmla v3.4s, v3.4s, v3.4s + ldr w24, [{0}, #16] + fmla v4.4s, v4.4s, v4.4s + ldr w25, [{0}, #20] + fmla v5.4s, v5.4s, v5.4s + ldr w26, [{0}, #24] + fmla v6.4s, v6.4s, v6.4s + ldr w27, [{0}, #28] + fmla v7.4s, v7.4s, v7.4s + ", + inout(reg) p, + out("x20") _, out("x21") _, out("x22") _, out("x23") _, + out("x24") _, out("x25") _, out("x26") _, out("x27") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + )); + }); + s32!("fmla_with_x_load", 64, { + let mut p = F32; + r8!(asm!(" + fmla v0.4s, v0.4s, v0.4s + ldr x20, [{0}] + fmla v1.4s, v1.4s, v1.4s + ldr x21, [{0}] + fmla v2.4s, v2.4s, v2.4s + ldr x22, [{0}] + fmla v3.4s, v3.4s, v3.4s + ldr x23, [{0}] + fmla v4.4s, v4.4s, v4.4s + ldr x24, [{0}] + fmla v5.4s, v5.4s, v5.4s + ldr x25, [{0}] + fmla v6.4s, v6.4s, v6.4s + ldr x26, [{0}] + fmla v7.4s, v7.4s, v7.4s + ldr x27, [{0}] + ", + inout(reg) p, + out("x20") _, out("x21") _, out("x22") _, out("x23") _, + out("x24") _, out("x25") _, out("x26") _, out("x27") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + )); + }); + s32!("fmla_with_s_load", 64, { + let mut p = F32; + r8!(asm!(" + ldr s16, [{0}] + fmla v0.4s, v0.4s, v0.4s + ldr s17, [{0}] + fmla v1.4s, v1.4s, v1.4s + ldr s18, [{0}] + fmla v2.4s, v2.4s, v2.4s + ldr s19, [{0}] + fmla v3.4s, v3.4s, v3.4s + ldr s20, [{0}] + fmla v4.4s, v4.4s, v4.4s + ldr s21, [{0}] + fmla v5.4s, v5.4s, v5.4s + ldr s22, [{0}] + fmla v6.4s, v6.4s, v6.4s + ldr s23, [{0}] + fmla v7.4s, v7.4s, v7.4s + ", + inout(reg) p, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("fmla_with_d_load", 64, { + let mut p = F32; + r8!(asm!(" + ldr d16, [{0}] + fmla v0.4s, v0.4s, v0.4s + ldr d17, [{0}] + fmla v1.4s, v1.4s, v1.4s + ldr d18, [{0}] + fmla v2.4s, v2.4s, v2.4s + ldr d19, [{0}] + fmla v3.4s, v3.4s, v3.4s + ldr d20, [{0}] + fmla v4.4s, v4.4s, v4.4s + ldr d21, [{0}] + fmla v5.4s, v5.4s, v5.4s + ldr d22, [{0}] + fmla v6.4s, v6.4s, v6.4s + ldr d23, [{0}] + fmla v7.4s, v7.4s, v7.4s + ", + inout(reg) p, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + out("v16") _, out("v17") _, out("v18") _, out("v19") _, + out("v20") _, out("v21") _, out("v22") _, out("v23") _, + )); + }); + s32!("fmla_with_d_load_as_v", 64, { + let mut p = F32; + r8!(asm!(" + fmla v0.4s, v0.4s, v0.4s + ld1 {{ v9.d }}[0], [{0}] + fmla v1.4s, v1.4s, v1.4s + ld1 {{ v10.d }}[0], [{0}] + fmla v2.4s, v2.4s, v2.4s + ld1 {{ v11.d }}[0], [{0}] + fmla v3.4s, v3.4s, v3.4s + ld1 {{ v12.d }}[0], [{0}] + fmla v4.4s, v4.4s, v4.4s + ld1 {{ v13.d }}[0], [{0}] + fmla v5.4s, v5.4s, v5.4s + ld1 {{ v14.d }}[0], [{0}] + fmla v6.4s, v6.4s, v6.4s + ld1 {{ v15.d }}[0], [{0}] + fmla v7.4s, v7.4s, v7.4s + ld1 {{ v16.d }}[0], [{0}] + ", + inout(reg) p, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("fmla_with_v_load", 64, { + let mut p = F32; + r8!(asm!(" + fmla v0.4s, v0.4s, v0.4s + ld1 {{ v9.4s }}, [{0}] + fmla v1.4s, v1.4s, v1.4s + ld1 {{ v10.4s }}, [{0}] + fmla v2.4s, v2.4s, v2.4s + ld1 {{ v11.4s }}, [{0}] + fmla v3.4s, v3.4s, v3.4s + ld1 {{ v12.4s }}, [{0}] + fmla v4.4s, v4.4s, v4.4s + ld1 {{ v13.4s }}, [{0}] + fmla v5.4s, v5.4s, v5.4s + ld1 {{ v14.4s }}, [{0}] + fmla v6.4s, v6.4s, v6.4s + ld1 {{ v15.4s }}, [{0}] + fmla v7.4s, v7.4s, v7.4s + ld1 {{ v16.4s }}, [{0}] + ", + inout(reg) p, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("fmla_with_ins_32b", 64, { + r8!(asm!(" + fmla v0.4s, v0.4s, v0.4s + ins v8.s[0], w20 + fmla v1.4s, v1.4s, v1.4s + ins v9.s[0], w20 + fmla v2.4s, v2.4s, v2.4s + ins v10.s[0], w20 + fmla v3.4s, v3.4s, v3.4s + ins v11.s[0], w20 + fmla v4.4s, v4.4s, v4.4s + ins v12.s[0], w20 + fmla v5.4s, v5.4s, v5.4s + ins v13.s[0], w20 + fmla v6.4s, v6.4s, v6.4s + ins v14.s[0], w20 + fmla v7.4s, v7.4s, v7.4s + ins v15.s[0], w20 + ", + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + out("x20") _, + )); + }); + s32!("fmla_with_ins_64b", 64, { + r8!(asm!(" + fmla v0.4s, v0.4s, v0.4s + ins v8.d[0], x20 + fmla v1.4s, v1.4s, v1.4s + ins v9.d[0], x20 + fmla v2.4s, v2.4s, v2.4s + ins v10.d[0], x20 + fmla v3.4s, v3.4s, v3.4s + ins v11.d[0], x20 + fmla v4.4s, v4.4s, v4.4s + ins v12.d[0], x20 + fmla v5.4s, v5.4s, v5.4s + ins v13.d[0], x20 + fmla v6.4s, v6.4s, v6.4s + ins v14.d[0], x20 + fmla v7.4s, v7.4s, v7.4s + ins v15.d[0], x20 + ", + out("x20") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("fmla_with_ins_64b_cross_parity", 64, { + r8!(asm!(" + fmla v0.4s, v0.4s, v0.4s + ins v9.d[0], x20 + fmla v1.4s, v1.4s, v1.4s + ins v10.d[0], x20 + fmla v2.4s, v2.4s, v2.4s + ins v11.d[0], x20 + fmla v3.4s, v6.4s, v3.4s + ins v12.d[0], x20 + fmla v4.4s, v4.4s, v4.4s + ins v13.d[0], x20 + fmla v5.4s, v5.4s, v5.4s + ins v14.d[0], x20 + fmla v6.4s, v6.4s, v6.4s + ins v15.d[0], x20 + fmla v7.4s, v7.4s, v7.4s + ins v8.d[0], x20 + ", + out("x20") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("ins_32b_with_load_s", 64, { + let mut p = F32; + r8!(asm!(" + ldr s0, [{0}] + ins v8.d[0], x20 + ldr s1, [{0}] + ins v9.d[0], x20 + ldr s2, [{0}] + ins v10.d[0], x20 + ldr s3, [{0}] + ins v11.d[0], x20 + ldr s4, [{0}] + ins v12.d[0], x20 + ldr s5, [{0}] + ins v13.d[0], x20 + ldr s6, [{0}] + ins v14.d[0], x20 + ldr s7, [{0}] + ins v15.d[0], x20 + ", + inout(reg) p, + out("x20") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("ins_32b_with_load_s_cross_parity", 64, { + let mut p = F32; + r8!(asm!(" + ldr s0, [{0}] + ins v9.d[0], x20 + ldr s1, [{0}] + ins v10.d[0], x20 + ldr s2, [{0}] + ins v11.d[0], x20 + ldr s3, [{0}] + ins v12.d[0], x20 + ldr s4, [{0}] + ins v13.d[0], x20 + ldr s5, [{0}] + ins v14.d[0], x20 + ldr s6, [{0}] + ins v15.d[0], x20 + ldr s7, [{0}] + ins v8.d[0], x20 + ", + inout(reg) p, + out("x20") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); +} + +fn has_asimdhp() -> bool { + std::fs::read_to_string("/proc/cpuinfo") + .unwrap() + .contains("asimdhp") +} + +#[target_feature(enable = "fp16")] +pub unsafe fn asimdhp(filter: Option<&str>) { + macro_rules! s32 { + ($label: literal, $n: expr, $stmt:block) => { + if $label.contains(filter.unwrap_or("")) { + println!("{:40} {:.2}", $label, b32!($stmt) / $n as f64 / *TICK); + } + }; + } + + s32!("fmlahp", 16, { + asm!(" fmla v0.8h, v0.8h, v0.8h + fmla v1.8h, v1.8h, v1.8h + fmla v2.8h, v2.8h, v2.8h + fmla v3.8h, v3.8h, v3.8h + fmla v4.8h, v4.8h, v4.8h + fmla v5.8h, v5.8h, v5.8h + fmla v6.8h, v6.8h, v6.8h + fmla v7.8h, v7.8h, v7.8h + fmla v8.8h, v8.8h, v8.8h + fmla v9.8h, v9.8h, v9.8h + fmla v10.8h,v10.8h,v10.8h + fmla v11.8h,v11.8h,v11.8h + fmla v12.8h,v12.8h,v12.8h + fmla v13.8h,v13.8h,v13.8h + fmla v14.8h,v14.8h,v14.8h + fmla v15.8h,v15.8h,v15.8h ", + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + ) + }); + + s32!("fcvt", 16, { + asm!(" fcvtn v0.4h, v0.4s + fcvtn v1.4h, v1.4s + fcvtn v2.4h, v2.4s + fcvtn v3.4h, v3.4s + fcvtn v4.4h, v4.4s + fcvtn v5.4h, v5.4s + fcvtn v6.4h, v6.4s + fcvtn v7.4h, v7.4s + fcvtn v8.4h, v8.4s + fcvtn v9.4h, v9.4s + fcvtn v10.4h, v10.4s + fcvtn v11.4h, v11.4s + fcvtn v12.4h, v12.4s + fcvtn v13.4h, v13.4s + fcvtn v14.4h, v14.4s + fcvtn v15.4h, v15.4s", + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + ) + }); + + s32!("fcvt2", 16, { + asm!(" fcvtn2 v0.8h, v0.4s + fcvtn2 v1.8h, v1.4s + fcvtn2 v2.8h, v2.4s + fcvtn2 v3.8h, v3.4s + fcvtn2 v4.8h, v4.4s + fcvtn2 v5.8h, v5.4s + fcvtn2 v6.8h, v6.4s + fcvtn2 v7.8h, v7.4s + fcvtn2 v8.8h, v8.4s + fcvtn2 v9.8h, v9.4s + fcvtn2 v10.8h, v10.4s + fcvtn2 v11.8h, v11.4s + fcvtn2 v12.8h, v12.4s + fcvtn2 v13.8h, v13.4s + fcvtn2 v14.8h, v14.4s + fcvtn2 v15.8h, v15.4s", + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + ) + }); + + s32!("fmlahp_with_dep", 1, { + asm!("fmla v0.8h, v0.8h, v0.8h", out("v0") _) + }); + s32!("fcvtn_with_dep", 1, { + asm!("fcvtn v0.4h, v0.4s", out("v0") _) + }); + s32!("fcvtn2_with_dep", 1, { + asm!("fcvtn2 v0.8h, v0.4s", out("v0") _) + }); +} + +macro_rules! ksimd { + ($filter: expr, $vector_size: expr, $geo: literal, $n: expr, $path: literal) => { + kloop!($filter, $vector_size, $geo, $n, "arm64simd", $path) + }; +} + +macro_rules! kfp16 { + ($filter: expr, $vector_size: expr, $geo: literal, $n: expr, $path: literal) => { + kloop!($filter, $vector_size, $geo, $n, "arm64fp16", $path) + }; +} + +macro_rules! kloop { + ($filter: expr, $vector_size: expr, $geo: literal, $n: expr, $dir: literal, $path: literal) => { + let label = $path.split("/").last().unwrap().split_once(".").unwrap().0; + let full_label = format!("{:8} {:40}", $geo, label); + if full_label.contains($filter.unwrap_or("")) { + let time = b2!({ + let mut p = F32; + let mut q = F32; + r4!(asm!(include_str!(concat!("../arm64/", $dir, "/", $path)), + inout("x1") p, inout("x2") q, out("x3") _, + out("x4") _, out("x5") _, out("x6") _, out("x7") _, + out("x8") _, out("x9") _, out("x10") _, out("x11") _, + out("x12") _, out("x13") _, out("x14") _, out("x15") _, + out("x20") _, out("x21") _, out("x22") _, out("x23") _, + out("x24") _, out("x25") _, out("x26") _, out("x27") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + out("v16") _, out("v17") _, out("v18") _, out("v19") _, + out("v20") _, out("v21") _, out("v22") _, out("v23") _, + out("v24") _, out("v25") _, out("v26") _, out("v27") _, + out("v28") _, out("v29") _, out("v30") _, out("v31") _, + )); + }) / 4.; + println!("{} {:3.0}% ({:0.2}/{} cy)", full_label, $n as f64 / $vector_size as f64 / time * 100. * *TICK, time / *TICK, $n as f64 / $vector_size as f64); + } + } +} + +unsafe fn f32_8x8(f: Option<&str>) { + ksimd!( + f, + 4, + "8x8x1xf32", + 64, + "arm64simd_mmm_f32_8x8/packed_packed_loop1/naive.tmpli" + ); + ksimd!( + f, + 4, + "8x8x1xf32", + 64, + "arm64simd_mmm_f32_8x8/packed_packed_loop1/broken_chains.tmpli" + ); + ksimd!( + f, + 4, + "8x8x1xf32", + 64, + "arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_x_no_preload.tmpli" + ); + ksimd!( + f, + 4, + "8x8x1xf32", + 64, + "arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_x_preload.tmpli" + ); + ksimd!( + f, + 4, + "8x8x1xf32", + 64, + "arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_w_no_preload.tmpli" + ); + ksimd!( + f, + 4, + "8x8x1xf32", + 64, + "arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_w_preload.tmpli" + ); + ksimd!( + f, + 4, + "8x8x2xf32", + 128, + "arm64simd_mmm_f32_8x8/packed_packed_loop2/broken_chains.tmpli" + ); + ksimd!( + f, + 4, + "8x8x2xf32", + 128, + "arm64simd_mmm_f32_8x8/packed_packed_loop2/cortex_a55.tmpli" + ); +} + +unsafe fn f32_12x8(f: Option<&str>) { + ksimd!( + f, + 4, + "12x8x1xf32", + 96, + "arm64simd_mmm_f32_12x8/packed_packed_loop1/naive.tmpli" + ); + ksimd!( + f, + 4, + "12x8x1xf32", + 96, + "arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_w_no_preload.tmpli" + ); + ksimd!( + f, + 4, + "12x8x1xf32", + 96, + "arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_w_preload.tmpli" + ); + ksimd!( + f, + 4, + "12x8x1xf32", + 96, + "arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_x_preload.tmpli" + ); + ksimd!( + f, + 4, + "12x8x2xf32", + 192, + "arm64simd_mmm_f32_12x8/packed_packed_loop2/cortex_a55.tmpli" + ); +} + +unsafe fn f32_16x4(f: Option<&str>) { + ksimd!( + f, + 4, + "16x4x1xf32", + 64, + "arm64simd_mmm_f32_16x4/packed_packed_loop1/naive.tmpli" + ); + ksimd!( + f, + 4, + "16x4x1xf32", + 64, + "arm64simd_mmm_f32_16x4/packed_packed_loop1/cortex_a53.tmpli" + ); + ksimd!( + f, + 4, + "16x4x2xf32", + 128, + "arm64simd_mmm_f32_16x4/packed_packed_loop2/cortex_a55.tmpli" + ); +} + +unsafe fn f32_24x4(f: Option<&str>) { + ksimd!( + f, + 4, + "24x4x1xf32", + 96, + "arm64simd_mmm_f32_24x4/packed_packed_loop1/naive.tmpli" + ); + ksimd!( + f, + 4, + "24x4x1xf32", + 96, + "arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a53.tmpli" + ); + ksimd!( + f, + 4, + "24x4x1xf32", + 96, + "arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a55.tmpli" + ); +} + +unsafe fn f32_64x1(f: Option<&str>) { + ksimd!( + f, + 4, + "64x1x1xf32", + 64, + "arm64simd_mmm_f32_64x1/loop1/naive.tmpli" + ); + ksimd!( + f, + 4, + "64x1x1xf32", + 64, + "arm64simd_mmm_f32_64x1/loop1/cortex_a53.tmpli" + ); + ksimd!( + f, + 4, + "64x1x2xf32", + 128, + "arm64simd_mmm_f32_64x1/loop2/naive.tmpli" + ); + ksimd!( + f, + 4, + "64x1x2xf32", + 128, + "arm64simd_mmm_f32_64x1/loop2/cortex_a55.tmpli" + ); +} + +// RUSTFLAGS="-C target-feature=+fp16" cargo +nightly dinghy -d khadas-paris bench --bench arm64simd +#[target_feature(enable = "fp16")] +unsafe fn f16_16x8(f: Option<&str>) { + kfp16!( + f, + 8, + "16x8x1xf16", + 128, + "arm64fp16_mmm_f16_16x8/loop1/naive.tmpli" + ); + kfp16!( + f, + 8, + "16x8x2xf16", + 256, + "arm64fp16_mmm_f16_16x8/loop2/cortex_a55.tmpli" + ); + kfp16!( + f, + 8, + "32x4x1xf16", + 128, + "arm64fp16_mmm_f16_32x4/loop1/naive.tmpli" + ); + kfp16!( + f, + 8, + "32x4x2xf16", + 256, + "arm64fp16_mmm_f16_32x4/loop2/cortex_a55.tmpli" + ); +} + +fn main() { + println!("freq {:.2}GHz\n", 1e-9 / *TICK); + + let filter = std::env::args().skip(1).filter(|a| a != "--bench").next(); + unsafe { + armv8(filter.as_deref()); + if has_asimdhp() { + asimdhp(filter.as_deref()); + } + f32_8x8(filter.as_deref()); + f32_12x8(filter.as_deref()); + f32_16x4(filter.as_deref()); + f32_24x4(filter.as_deref()); + f32_64x1(filter.as_deref()); + f16_16x8(filter.as_deref()); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/conv_for_wavenet_hw.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/conv_for_wavenet_hw.rs new file mode 100644 index 0000000..d5ec80e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/conv_for_wavenet_hw.rs @@ -0,0 +1,52 @@ +#[macro_use] +extern crate criterion; +extern crate tract_data; +extern crate tract_linalg; +use criterion::Criterion; +use tract_linalg::frame::mmm::FusedSpec; + +use tract_data::internal::*; +use DatumType::F32; + +fn conv(c: &mut Criterion, dilation: usize, pulse: usize, ci: usize, co: usize) { + c.bench_function( + &format!("conv_d{dilation}p{pulse}ci{ci}co{co}"), + move |be| unsafe { + let t = pulse + 2 * dilation; + let k = ci * 3; + let mm = tract_linalg::ops() + .mmm(F32, F32, F32, Some(co), Some(ci * 3), Some(pulse)) + .unwrap(); + mm.c_from_data_and_strides(F32.size_of(), co, t, t as _, 1); + let a = Tensor::zero_aligned::(&[mm.a_pack().len(k, co)], mm.a_pack().alignment()) + .unwrap(); + let input = Tensor::zero::(&[ci, t]).unwrap(); + let mut output = Tensor::zero::(&[co, t]).unwrap(); + be.iter(move || { + mm.run( + co, + t, + &[ + FusedSpec::AddMatMul { + a: mm.a_packed(F32.size_of(), k).wrap(&a.view()), + b: mm.b_packed(F32.size_of(), k).wrap(&input.view()).unwrap(), + k, + }, + FusedSpec::Store(mm.c_view(0, 1).wrap(&output.view_mut())), + ], + ) + .unwrap() + }); + }, + ); +} + +fn convs(c: &mut Criterion) { + conv(c, 1, 8, 16, 64); + conv(c, 2, 8, 16, 64); + conv(c, 4, 8, 16, 64); + conv(c, 8, 8, 16, 64); +} + +criterion_group!(benches, convs); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/mat_vec.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/mat_vec.rs new file mode 100644 index 0000000..1e3154f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/mat_vec.rs @@ -0,0 +1,52 @@ +use criterion::*; +use tract_data::internal::*; +use tract_linalg::frame::mmm::FusedSpec; + +use DatumType::F32; + +fn mat_vec_mul(c: &mut Criterion) { + let mut group = c.benchmark_group("mat_vec_mul"); + unsafe { + { + let (m, k) = &(768usize, 256usize); + group.throughput(Throughput::Elements((m * k) as u64)); + group.bench_with_input( + BenchmarkId::from_parameter(format!("{m}x{k}")), + &(m, k), + |be, (&m, &k)| { + let mm = tract_linalg::ops() + .mmm(F32, F32, F32, Some(m), Some(k), Some(1)) + .unwrap(); + let pa = Tensor::uninitialized_aligned::( + &[mm.a_pack().len(k, m)], + mm.a_pack().alignment(), + ) + .unwrap(); + let b = tensor1(&vec![0.0; k]); + let mut c = Tensor::zero::(&[m]).unwrap(); + be.iter(move || { + mm.run( + m, + 1, + &[ + FusedSpec::AddMatMul { + a: mm.a_packed(F32.size_of(), k).wrap(&pa.view()), + b: mm + .b_packed(b.datum_type().size_of(), k) + .wrap(&b.view()) + .unwrap(), + k, + }, + FusedSpec::Store(mm.c_view(0, 0).wrap(&c.view_mut())), + ], + ) + }); + }, + ); + } + } + group.finish(); +} + +criterion_group!(benches, mat_vec_mul); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/mm_for_asr_am.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/mm_for_asr_am.rs new file mode 100644 index 0000000..d08e37d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/mm_for_asr_am.rs @@ -0,0 +1,37 @@ +use criterion::*; + +mod utils; +use utils::*; + +fn all(c: &mut Criterion) { + // packed_packed: co, ci, n + // direct_conv(c, "asr_2M", 24, 5, 40, 200, 1); // lda + packed_packed(c, "asr_2M", 256, 200, 24); // tdnn1 + // direct_conv(c, "asr_2M", 24, 3, 256, 256, 1); // tdnn2 + // direct_conv(c, "asr_2M", 24, 3, 256, 256, 3); // tdnn3 + packed_packed(c, "asr_2M", 256, 256, 8); // fastlstm1 and 2 (input) x 8 (4 prod x 2 layers) + packed_packed(c, "asr_2M", 256, 128, 1); // fastlstm1 and 2 (hidden) x 64 (4 prod x 2 layers x 8 loops) + packed_packed(c, "asr_2M", 256, 256, 1); // fastlstm1 and 2 (rp) x 16 (2 layers x 8 loops) + // direct_conv(c, "asr_2M", 8, 3, 256, 256, 1); // tdnn4, tdd5 (x2) + packed_packed(c, "asr_2M", 1690, 256, 8); // output + + // 8M + packed_packed(c, "asr_8M", 512, 200, 24); // tdnn1 + packed_packed(c, "asr_8M", 512, 512, 24); // tdnn2 + packed_packed(c, "asr_8M", 512, 256, 1); // fastlstm1 and 2 (four parts, rec mat*vec) + packed_vec(c, "asr_8M", 512, 256, 1); // fastlstm1 and 2 (four parts, rec mat*vec) + + // pseudo 15M + packed_packed(c, "asr_pseudo15M", 768, 200, 24); // tdnn1 + packed_packed(c, "asr_pseudo15M", 768, 2304, 24); // tdnn2 + packed_packed(c, "asr_pseudo15M", 768, 2304, 8); // tdnn3,4,5 + packed_packed(c, "asr_pseudo15M", 768, 768, 8); // fastlstm1 and 2 (four parts, rec mat*mat) + packed_packed(c, "asr_pseudo15M", 768, 384, 1); // fastlstm1 and 2 (four parts, rec mat*vec) + packed_vec(c, "asr_pseudo15M", 768, 384, 1); // fastlstm1 and 2 (four parts, rec mat*vec) + + // 15M + packed_vec(c, "asr_15M", 768, 256, 1); // fastlstm1 and 2 (four parts, rec mat*vec) +} + +criterion_group!(benches, all); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/mm_for_inception.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/mm_for_inception.rs new file mode 100644 index 0000000..de509e3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/mm_for_inception.rs @@ -0,0 +1,47 @@ +extern crate criterion; +use criterion::*; +use tract_data::internal::*; +use tract_linalg::frame::mmm::FusedSpec; + +use DatumType::F32; + +fn mat_mul_smmm(be: &mut criterion::Bencher, &(m, k, n): &(usize, usize, usize)) { + unsafe { + let mm = tract_linalg::ops() + .mmm(F32, F32, F32, Some(m), Some(k), Some(n)) + .unwrap(); + let pa = + Tensor::uninitialized_aligned::(&[mm.a_pack().len(k, m)], mm.a_pack().alignment()) + .unwrap(); + let pb = + Tensor::uninitialized_aligned::(&[mm.b_pack().len(k, n)], mm.b_pack().alignment()) + .unwrap(); + let mut c = Tensor::zero::(&[m, n]).unwrap(); + be.iter(move || { + mm.run( + m, + n, + &[ + FusedSpec::AddMatMul { + a: mm.a_packed(F32.size_of(), k).wrap(&pa.view()), + b: mm.b_packed(F32.size_of(), k).wrap(&pb.view()).unwrap(), + k, + }, + FusedSpec::Store(mm.c_view(0, 1).wrap(&c.view_mut())), + ], + ) + }); + } +} + +fn mat_mul_prepacked(c: &mut Criterion, m: usize, k: usize, n: usize) { + let mut group = c.benchmark_group("mat_mul_prepacked"); + group.bench_function("smmm", |be| mat_mul_smmm(be, &(m, k, n))); +} + +fn s64x288x21609(c: &mut Criterion) { + mat_mul_prepacked(c, 64, 288, 21609) +} + +criterion::criterion_group!(benches, s64x288x21609); +criterion::criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/mm_for_wavenet_hw.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/mm_for_wavenet_hw.rs new file mode 100644 index 0000000..060db53 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/mm_for_wavenet_hw.rs @@ -0,0 +1,12 @@ +use criterion::*; + +mod utils; +use utils::*; + +fn s16x60x8(c: &mut Criterion) { + packed_packed(c, "wavenet", 32, 32, 8); // postproc + packed_packed(c, "wavenet", 16, 60, 8); +} + +criterion_group!(benches, s16x60x8); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/nano.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/nano.rs new file mode 100644 index 0000000..68221d3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/nano.rs @@ -0,0 +1,113 @@ +#![allow(unused_macros)] + +use std::time::Duration; +use std::time::Instant; + +#[macro_export] +macro_rules! r1 { ($($stat:stmt)*) => { $( $stat )* } } +#[macro_export] +macro_rules! r2 { ($($stat:stmt)*) => { $( $stat )* $( $stat )* } } +#[macro_export] +macro_rules! r4 { ($($stat:stmt)*) => { r2!(r2!($($stat)*)) }} +#[macro_export] +macro_rules! r8 { ($($stat:stmt)*) => { r2!(r4!($($stat)*)) }} +#[macro_export] +macro_rules! r16 { ($($stat:stmt)*) => { r2!(r8!($($stat)*)) }} +#[macro_export] +macro_rules! r32 { ($($stat:stmt)*) => { r2!(r16!($($stat)*)) }} +#[macro_export] +macro_rules! r64 { ($($stat:stmt)*) => { r2!(r32!($($stat)*)) }} +#[macro_export] +macro_rules! r128 { ($($stat:stmt)*) => { r2!(r64!($($stat)*)) }} +#[macro_export] +macro_rules! r256 { ($($stat:stmt)*) => { r2!(r128!($($stat)*)) }} +#[macro_export] +macro_rules! r512 { ($($stat:stmt)*) => { r2!(r256!($($stat)*)) }} +#[macro_export] +macro_rules! r1024 { ($($stat:stmt)*) => { r2!(r512!($($stat)*)) }} +#[macro_export] +macro_rules! r2048 { ($($stat:stmt)*) => { r2!(r1024!($($stat)*)) }} +#[macro_export] +macro_rules! r4096 { ($($stat:stmt)*) => { r2!(r2048!($($stat)*)) }} +#[macro_export] +macro_rules! r8192 { ($($stat:stmt)*) => { r2!(r4096!($($stat)*)) }} + +#[macro_export] +macro_rules! b1 { ($($stat:stmt)*) => { nano::run_bench(|| { r1!($($stat)*); }) / 1.0 } } +#[macro_export] +macro_rules! b2 { ($($stat:stmt)*) => { nano::run_bench(|| { r2!($($stat)*); }) / 2.0 } } +#[macro_export] +macro_rules! b4 { ($($stat:stmt)*) => { nano::run_bench(|| { r4!($($stat)*); }) / 4.0 } } +#[macro_export] +macro_rules! b8 { ($($stat:stmt)*) => { nano::run_bench(|| { r8!($($stat)*); }) / 8.0 } } +#[macro_export] +macro_rules! b16 { ($($stat:stmt)*) => { nano::run_bench(|| { r16!($($stat)*); }) / 16.0 } } +#[macro_export] +macro_rules! b32 { ($($stat:stmt)*) => { nano::run_bench(|| { r32!($($stat)*); }) / 32.0 } } +#[macro_export] +macro_rules! b64 { ($($stat:stmt)*) => { nano::run_bench(|| { r64!($($stat)*); }) / 64.0 } } +#[macro_export] +macro_rules! b128 { ($($stat:stmt)*) => { nano::run_bench(|| { r128!($($stat)*); }) / 128.0 } } +#[macro_export] +macro_rules! b256 { ($($stat:stmt)*) => { nano::run_bench(|| { r256!($($stat)*); }) / 256.0 } } +#[macro_export] +macro_rules! b512 { ($($stat:stmt)*) => { nano::run_bench(|| { r512!($($stat)*); }) / 512.0 } } +#[macro_export] +macro_rules! b1024 { ($($stat:stmt)*) => { nano::run_bench(|| { r1024!($($stat)*); }) / 1024.0 } } +#[macro_export] +macro_rules! b2048 { ($($stat:stmt)*) => { nano::run_bench(|| { r2048!($($stat)*); }) / 2048.0 } } +#[macro_export] +macro_rules! b4096 { ($($stat:stmt)*) => { nano::run_bench(|| { r4096!($($stat)*); }) / 4096.0 } } +#[macro_export] +macro_rules! b8192 { ($($stat:stmt)*) => { nano::run_bench(|| { r8192!($($stat)*); }) / 8192.0 } } + +fn black_box(dummy: T) -> T { + unsafe { + let ret = std::ptr::read_volatile(&dummy); + std::mem::forget(dummy); + ret + } +} + +pub fn run_bench T>(mut f: F) -> f64 { + let start = Instant::now(); + black_box(f()); + let once = start.elapsed(); + let evaled = if once < Duration::from_millis(1) { + let start = Instant::now(); + for _ in 0..1000 { + black_box(f()); + } + start.elapsed().as_secs_f64() / 1000. + } else { + once.as_secs_f64() + }; + let warmup = (0.3 / evaled) as usize; + let iters = (0.3 / evaled) as usize; + let chunks = 1000; + let chunk = (iters / chunks).max(50); + let chunks = (iters / chunk).max(50); + let mut measures = vec![0.0; chunks]; + for _ in 0..warmup { + black_box(f()); + } + for m in &mut measures { + let start = Instant::now(); + for _ in 0..chunk { + black_box(f()); + } + *m = start.elapsed().as_secs_f64() / chunk as f64 + } + measures.sort_by(|a, b| { + if a < b { + std::cmp::Ordering::Less + } else { + std::cmp::Ordering::Greater + } + }); + let q1 = measures[chunks / 4]; + let q3 = measures[chunks - chunks / 4]; + let iq = q3 - q1; + measures.retain(|&x| x >= q1 - 3. * iq && x <= q3 + 3. * iq); + measures.iter().copied().sum::() / measures.len() as f64 +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/packing.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/packing.rs new file mode 100644 index 0000000..bb6fceb --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/packing.rs @@ -0,0 +1,356 @@ +#![allow(non_snake_case)] + +use criterion::measurement::WallTime; +use criterion::*; +use tract_data::internal::*; +use tract_linalg::frame::MatMatMulImpl; +use tract_linalg::mmm::FusedSpec; +use tract_linalg::mmm::MatMatMul; +use tract_linalg::mmm::MatMatMulKer; +use tract_linalg::mmm::ScratchSpaceFusedNonLinear; +use Throughput::Elements; + +fn packa>(crit: &mut BenchmarkGroup, m: usize, k: usize) { + let a = Tensor::zero_dt(DatumType::F32, &[m, k]).unwrap(); + + unsafe { + let mmm = MatMatMulImpl::::new(); + let mut pa = Tensor::zero_aligned_dt( + DatumType::F32, + &[mmm.a_pack().len(k, m)], + mmm.a_pack().alignment(), + ) + .unwrap(); + + crit.throughput(Elements((m * k) as _)) + .bench_function("packa", |be| { + be.iter(|| mmm.a_pack().pack(&mut pa.view_mut(), &a.view(), 1, 0)); + }); + } +} + +fn packb>(crit: &mut BenchmarkGroup, k: usize, n: usize) { + let b = Tensor::zero_dt(DatumType::F32, &[k, n]).unwrap(); + + unsafe { + let mmm = MatMatMulImpl::::new(); + let mut pb = Tensor::zero_aligned_dt( + DatumType::F32, + &[mmm.b_pack().len(k, n)], + mmm.b_pack().alignment(), + ) + .unwrap(); + + crit.throughput(Elements((k * n) as _)) + .bench_function("packb", |be| { + be.iter(|| mmm.b_pack().pack(&mut pb.view_mut(), &b.view(), 0, 1)); + }); + } +} + +fn packed_a>(m: usize, k: usize) -> Tensor { + let mmm = MatMatMulImpl::::new(); + Tensor::zero_aligned_dt( + DatumType::F32, + &[mmm.a_pack().len(k, m)], + mmm.a_pack().alignment(), + ) + .unwrap() +} + +fn packed_b>(k: usize, n: usize) -> Tensor { + let mmm = MatMatMulImpl::::new(); + Tensor::zero_aligned_dt( + DatumType::F32, + &[mmm.b_pack().len(k, n)], + mmm.b_pack().alignment(), + ) + .unwrap() +} + +#[inline(always)] +unsafe fn valid_tile( + scratch: &mut ScratchSpaceFusedNonLinear, + ops: &[FusedSpec], + ia: usize, + ib: usize, +) { + scratch.for_valid_tile::(ops, ia, ib); + let err = K::kernel(&scratch.uspecs()); + debug_assert_eq!(err, 0, "Kernel return error {}", err); +} + +unsafe fn packedpacked(m: usize, k: usize, n: usize) -> (Tensor, Tensor, Tensor) +where + K: MatMatMulKer, +{ + let c = Tensor::zero_dt(DatumType::F32, &[m, n]).unwrap(); + let pa = packed_a::(m, k); + let pb = packed_b::(k, n); + (pa, pb, c) +} + +unsafe fn packedpacked_ops<'a, K: MatMatMulKer>( + k: usize, + pa: &'a Tensor, + pb: &'a Tensor, + c: &'a mut Tensor, +) -> TVec> { + let mmm = MatMatMulImpl::::new(); + tvec!( + FusedSpec::AddMatMul { + k, + a: mmm.a_packed(4, k).wrap(&pa.view()), + b: mmm.b_packed(4, k).wrap(&pb.view()).unwrap(), + }, + FusedSpec::Store(mmm.c_view(0, 1).wrap(&mut c.view_mut())), + ) +} + +unsafe fn packedpacking(m: usize, k: usize, n: usize) -> (Tensor, Tensor, Tensor) +where + K: MatMatMulKer, +{ + let c = Tensor::zero_dt(DatumType::F32, &[m, n]).unwrap(); + let pa = packed_a::(m, k); + let b = Tensor::zero_dt(DatumType::F32, &[k, n]).unwrap(); + (pa, b, c) +} + +unsafe fn packedpacking_ops<'a, K: MatMatMulKer>( + k: usize, + pa: &'a Tensor, + b: &'a Tensor, + c: &'a mut Tensor, +) -> TVec> { + let mmm = MatMatMulImpl::::new(); + tvec!( + FusedSpec::AddMatMul { + k, + a: mmm.a_packed(4, k).wrap(&pa.view()), + b: mmm.b_late_packing().wrap(&b.view()).unwrap(), + }, + FusedSpec::Store(mmm.c_view(0, 1).wrap(&mut c.view_mut())), + ) +} + +fn packedpacked_mr_nr>( + crit: &mut BenchmarkGroup, + m: usize, + k: usize, + n: usize, +) { + unsafe { + let (pa, pb, mut c) = packedpacked::(m, k, n); + let ops = packedpacked_ops::(k, &pa, &pb, &mut c); + let mut scratch = ScratchSpaceFusedNonLinear::::default(); + crit.throughput(Elements((m * k * n) as _)) + .bench_function("packedpacked_mr_nr", |be| { + be.iter(|| { + scratch.prepare::(&ops); + for ia in 0..m / K::mr() { + for ib in 0..n / K::nr() { + valid_tile(&mut scratch, &ops, ia, ib); + } + } + }) + }); + } +} + +fn packedpacked_nr_mr>( + crit: &mut BenchmarkGroup, + m: usize, + k: usize, + n: usize, +) { + unsafe { + let (pa, pb, mut c) = packedpacked::(m, k, n); + let ops = packedpacked_ops::(k, &pa, &pb, &mut c); + let mut scratch = ScratchSpaceFusedNonLinear::::default(); + crit.throughput(Elements((m * k * n) as _)) + .bench_function("packedpacked_nr_mr", |be| { + be.iter(|| { + scratch.prepare::(&ops); + for ib in 0..n / K::nr() { + for ia in 0..m / K::mr() { + valid_tile(&mut scratch, &ops, ia, ib); + } + } + }) + }); + } +} + +fn packedpacking_mr_nr>( + crit: &mut BenchmarkGroup, + m: usize, + k: usize, + n: usize, +) { + unsafe { + let (pa, b, mut c) = packedpacking::(m, k, n); + let ops = packedpacking_ops::(k, &pa, &b, &mut c); + let mut scratch = ScratchSpaceFusedNonLinear::::default(); + crit.throughput(Elements((m * k * n) as _)) + .bench_function("packedpacking_mr_nr", |be| { + be.iter(|| { + scratch.prepare::(&ops); + for ia in 0..m / K::mr() { + for ib in 0..n / K::nr() { + valid_tile(&mut scratch, &ops, ia, ib); + } + } + }) + }); + } +} + +fn packedpacking_nr_mr>( + crit: &mut BenchmarkGroup, + m: usize, + k: usize, + n: usize, +) { + unsafe { + let (pa, b, mut c) = packedpacking::(m, k, n); + let ops = packedpacking_ops::(k, &pa, &b, &mut c); + let mut scratch = ScratchSpaceFusedNonLinear::::default(); + crit.throughput(Elements((m * k * n) as _)) + .bench_function("packedpacking_nr_mr", |be| { + be.iter(|| { + scratch.prepare::(&ops); + for ib in 0..n / K::nr() { + for ia in 0..m / K::mr() { + valid_tile(&mut scratch, &ops, ia, ib); + } + } + }) + }); + } +} + +fn packedpacked_mc_nc_mr_nr>( + crit: &mut BenchmarkGroup, + m: usize, + k: usize, + n: usize, +) { + unsafe { + let (pa, pb, mut c) = packedpacked::(m, k, n); + let ops = packedpacked_ops::(k, &pa, &pb, &mut c); + let mut scratch = ScratchSpaceFusedNonLinear::::default(); + crit.throughput(Elements((m * k * n) as _)).bench_function( + "packedpacked_mc_nc_mr_nr", + |be| { + be.iter(|| { + scratch.prepare::(&ops); + let mc = 128 - K::mr() % 128; + let nc = 128 - K::nr() % 128; + // eprintln!("{}x{} {}x{} {}x{}", m, n, mc, nc, K::mr(), K::nr()); + for oa in 0..m.divceil(mc) { + for ob in 0..n.divceil(nc) { + for ia in 0..mc / K::mr() { + for ib in 0..nc / K::nr() { + let a = oa * mc / K::mr() + ia; + let b = ob * nc / K::nr() + ib; + if (a + 1) * K::mr() > m || (b + 1) * K::nr() > n { + continue; + } + valid_tile(&mut scratch, &ops, ia, ib); + } + } + } + } + }) + }, + ); + } +} + +fn packedpacking_mc_nc_mr_nr>( + crit: &mut BenchmarkGroup, + m: usize, + k: usize, + n: usize, +) { + unsafe { + let (pa, b, mut c) = packedpacking::(m, k, n); + let ops = packedpacking_ops::(k, &pa, &b, &mut c); + let mut scratch = ScratchSpaceFusedNonLinear::::default(); + crit.throughput(Elements((m * k * n) as _)).bench_function( + "packedpacking_mc_nc_mr_nr", + |be| { + be.iter(|| { + scratch.prepare::(&ops); + let mc = 128 - K::mr() % 128; + let nc = 128 - K::nr() % 128; + // eprintln!("{}x{} {}x{} {}x{}", m, n, mc, nc, K::mr(), K::nr()); + for oa in 0..m.divceil(mc) { + for ob in 0..n.divceil(nc) { + for ib in 0..nc / K::nr() { + for ia in 0..mc / K::mr() { + let a = oa * mc / K::mr() + ia; + let b = ob * nc / K::nr() + ib; + if (a + 1) * K::mr() > m || (b + 1) * K::nr() > n { + continue; + } + valid_tile(&mut scratch, &ops, ia, ib); + } + } + } + } + }) + }, + ); + } +} + +#[cfg(target_arch = "x86_64")] +type K = tract_linalg::x86_64_fma::mmm::MatMatMulF32x16x6; + +#[cfg(target_arch = "aarch64")] +type K = tract_linalg::arm64::MatMatMulF32x12x8; + +#[cfg(target_arch = "arm")] +type K = tract_linalg::arm32::armv7neon::MatMatMulF32x8x6CortexA9; + +fn matmul(c: &mut Criterion, m: usize, k: usize, n: usize) { + let mut c = c.benchmark_group(format!("{}x{}x{}", m, k, n)); + packa::(&mut c, m, k); + packb::(&mut c, k, n); + packedpacked_mr_nr::(&mut c, m, k, n); + packedpacked_nr_mr::(&mut c, m, k, n); + packedpacked_mc_nc_mr_nr::(&mut c, m, k, n); + packedpacking_mr_nr::(&mut c, m, k, n); + packedpacking_nr_mr::(&mut c, m, k, n); + packedpacking_mc_nc_mr_nr::(&mut c, m, k, n); + c.finish(); +} + +fn big(c: &mut Criterion) { + matmul(c, 512, 512, 512); + #[cfg(target_arch = "x86_64")] + matmul(c, 99, 891, 1048576); + #[cfg(target_arch = "x86_64")] + matmul(c, 128, 1024, 1048576); +} + +fn wavenet(c: &mut Criterion) { + matmul(c, 64, 48, 8); + matmul(c, 16, 64, 8); + matmul(c, 32, 64, 8); +} + +fn asr_15M(c: &mut Criterion) { + matmul(c, 768, 200, 18); + matmul(c, 768, 2304, 18); + matmul(c, 768, 2304, 6); +} + +fn inception(c: &mut Criterion) { + matmul(c, 64, 288, 21609); +} + +criterion_group!(benches, big, wavenet, asr_15M, inception); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/sigmoid.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/sigmoid.rs new file mode 100644 index 0000000..c9868b6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/sigmoid.rs @@ -0,0 +1,22 @@ +#[macro_use] +extern crate criterion; +extern crate tract_linalg; +use criterion::Criterion; + +fn ssigmoid(c: &mut Criterion, n: usize) { + c.bench_function(&format!("ssigmoid_{n}"), move |be| { + let mut s = (0..n).map(|i| i as f32 / 10.0).collect::>(); + let op = &(tract_linalg::ops().sigmoid_f32)(); + be.iter(|| op.run(&mut s)); + }); +} + +fn bs(c: &mut Criterion) { + ssigmoid(c, 4); + ssigmoid(c, 8); + ssigmoid(c, 128); + ssigmoid(c, 1024); +} + +criterion_group!(benches, bs); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/utils.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/utils.rs new file mode 100644 index 0000000..d6ebf72 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/utils.rs @@ -0,0 +1,154 @@ +#![allow(dead_code)] +use criterion::*; +use tract_data::internal::*; +use tract_linalg::frame::mmm::{FusedSpec, InputStore, PackedStore}; +use tract_linalg::frame::MatMatMul; + +use DatumType::*; + +pub fn packed_packed(c: &mut Criterion, name: &str, m: usize, k: usize, n: usize) { + let mut group = c.benchmark_group(format!("{name}/packed_packed")); + group.throughput(Throughput::Elements((m * k * n) as u64)); + let id = format!("{m}x{k}x{n}"); + group.bench_with_input( + BenchmarkId::new("f32/cold", &id), + &(F32, m, k, n, true), + mat_mat, + ); + group.bench_with_input( + BenchmarkId::new("f32/hot", &id), + &(F32, m, k, n, false), + mat_mat, + ); + group.bench_with_input( + BenchmarkId::new("i8/cold", &id), + &(I8, m, k, n, true), + mat_mat, + ); + group.bench_with_input( + BenchmarkId::new("i8/hot", &id), + &(I8, m, k, n, false), + mat_mat, + ); +} + +pub fn packed_vec(c: &mut Criterion, name: &str, m: usize, k: usize, n: usize) { + assert_eq!(n, 1); + let mut group = c.benchmark_group(format!("{name}/packed_vec")); + group.throughput(Throughput::Elements((m * k * n) as u64)); + let id = format!("{m}x{k}x{n}"); + group.bench_with_input( + BenchmarkId::new("f32/cold", &id), + &(F32, m, k, n, true), + mat_vec, + ); + group.bench_with_input( + BenchmarkId::new("f32/hot", &id), + &(F32, m, k, n, false), + mat_vec, + ); + group.bench_with_input( + BenchmarkId::new("i8/cold", &id), + &(I8, m, k, n, true), + mat_vec, + ); + group.bench_with_input( + BenchmarkId::new("i8/hot", &id), + &(I8, m, k, n, false), + mat_vec, + ); +} + +pub fn ruin_cache() { + let _a = (0..1000000).collect::>(); +} + +#[allow(clippy::too_many_arguments)] +unsafe fn run( + m: usize, + k: usize, + n: usize, + be: &mut Bencher, + mm: &dyn MatMatMul, + pa: PackedStore, + pb: InputStore, + cold: bool, +) { + let mut scratch = mm.allocate_scratch_space(); + be.iter_custom(move |iters| { + let mut dur = std::time::Duration::default(); + for _ in 0..iters { + if cold { + ruin_cache(); + } + let instant = std::time::Instant::now(); + mm.run_with_scratch_space( + m, + n, + scratch.as_mut(), + &[FusedSpec::AddMatMul { + a: pa, + b: pb.clone(), + k, + }], + ) + .unwrap(); + let time = instant.elapsed(); + dur += time; + } + dur + }); +} + +fn mat_mat(be: &mut Bencher, params: &(DatumType, usize, usize, usize, bool)) { + let (dt, m, k, n, _) = *params; + let mm = tract_linalg::ops() + .mmm(dt, dt, dt, Some(m), Some(k), Some(n)) + .unwrap(); + mat_mat_with_mm(be, &*mm, params) +} + +pub fn mat_mat_with_mm( + be: &mut Bencher, + mm: &dyn MatMatMul, + &(dt, m, k, n, cold): &(DatumType, usize, usize, usize, bool), +) { + let pa = + Tensor::zero_aligned_dt(dt, &[mm.a_pack().len(k, m)], mm.a_pack().alignment()).unwrap(); + let pb = + Tensor::zero_aligned_dt(dt, &[mm.b_pack().len(k, n)], mm.b_pack().alignment()).unwrap(); + unsafe { + run( + m, + k, + n, + be, + mm, + mm.a_packed(dt.size_of(), k).wrap(&pa.view()), + mm.b_packed(dt.size_of(), k).wrap(&pb.view()).unwrap(), + cold, + ); + } +} + +fn mat_vec(be: &mut Bencher, &(dt, m, k, n, cold): &(DatumType, usize, usize, usize, bool)) { + assert_eq!(n, 1); + let mm = tract_linalg::ops() + .mmm(dt, dt, dt, Some(m), Some(k), Some(n)) + .unwrap(); + let pa = + Tensor::zero_aligned_dt(dt, &[mm.a_pack().len(k, m)], mm.a_pack().alignment()).unwrap(); + let pb = Tensor::zero_dt(dt, &[k, 1]).unwrap(); + unsafe { + run( + m, + k, + n, + be, + &*mm, + mm.a_packed(dt.size_of(), k).wrap(&pa.view()), + mm.b_packed(dt.size_of(), k).wrap(&pb.view()).unwrap(), + cold, + ); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/virtual_im2col.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/virtual_im2col.rs new file mode 100644 index 0000000..87f01b6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/virtual_im2col.rs @@ -0,0 +1,51 @@ +use criterion::measurement::WallTime; +use criterion::*; +use tract_data::internal::*; + +#[allow(dead_code)] +#[path = "../tests/virtual_im2col.rs"] +mod virtual_im2col; +use virtual_im2col::ConvProblem; + +fn conv( + c: &mut BenchmarkGroup, + ci: usize, + h: usize, + w: usize, + co: usize, + kh: usize, + kw: usize, +) { + // CHW HWIO + let input = Tensor::zero::(&[ci, h, w]).unwrap(); + let filters = Tensor::zero::(&[kh, kw, ci, co]).unwrap(); + let mut cv = ConvProblem { + input, + filters, + lazy_im2col: false, + }; + c.bench_function("eager", |b| { + b.iter(|| { + cv.tract(); + }) + }); + cv.lazy_im2col = true; + c.bench_function("lazy", |b| { + b.iter(|| { + cv.tract(); + }) + }); +} + +fn ex1(c: &mut Criterion) { + let mut c = c.benchmark_group("ex1"); + conv(&mut c, 32, 256, 256, 32, 3, 3); +} + +fn big(c: &mut Criterion) { + let mut c = c.benchmark_group("big"); + conv(&mut c, 1, 1024, 1024, 99, 3, 3); +} + +criterion_group!(benches, ex1, big); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/x86_64.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/x86_64.rs new file mode 100644 index 0000000..a83c48e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/benches/x86_64.rs @@ -0,0 +1,198 @@ +#![allow( + dead_code, + non_upper_case_globals, + unused_macros, + non_snake_case, + unused_assignments +)] + +use std::arch::asm; + +mod nano; + +#[repr(C, align(64))] +struct Floats([f32; 4096]); +const _F32: Floats = Floats([12.; 4096]); +const F32: *const f32 = (&_F32) as *const Floats as *const f32; + +lazy_static::lazy_static! { + static ref TICK: f64 = unsafe { b8192!(asm!("or rax, rax", out("rax") _)) }; +} + +macro_rules! kloop { + ($filter: expr, $geo: literal, $n: expr, $path: literal, $ww: expr) => { + let label = $path.split("/").last().unwrap().split_once(".").unwrap().0; + let full_label = format!("{:8} {:40}", $geo, label); + if full_label.contains($filter.unwrap_or("")) { + let time = b2!({ + let mut p = F32; + let mut q = F32; + r128!(asm!(include_str!(concat!("../x86_64/fma/", $path)), + inout("rax") p, inout("rcx") q, + out("zmm0") _, out("zmm1") _, out("zmm2") _, out("zmm3") _, + out("zmm4") _, out("zmm5") _, out("zmm6") _, out("zmm7") _, + out("zmm8") _, out("zmm9") _, out("zmm10") _, out("zmm11") _, + out("zmm12") _, out("zmm13") _, out("zmm14") _, out("zmm15") _, + out("zmm20") _, out("zmm21") _, out("zmm22") _, out("zmm23") _, + out("zmm24") _, out("zmm25") _, out("zmm26") _, out("zmm27") _, + )); + }) / 128.; + + + println!("{} {:3.0}% ({:>5.2 }/{:3 } cy) {:.2} GFLOP/s", full_label, ($n as f64 / $ww as f64) / time * 100. * *TICK, time / *TICK, $n as f64 / $ww as f64, $n as f64 / time / 1e9 ); + } + } +} + +unsafe fn packed_packed_1x8(f: Option<&str>) { + println!("-- 1x8 kernels"); + kloop!(f, "1x8x1", (8 * 8), "8x8/packed_packed_loop1/avx.tmpli", 8); + kloop!( + f, + "1x8x2", + (8 * 8 * 2), + "8x8/packed_packed_loop1/avx-unroll.tmpli", + 8 + ); + println!(); +} + +unsafe fn packed_packed_2x6(f: Option<&str>) { + println!("-- 2x6 kernels"); + kloop!( + f, + "2x6x1", + (16 * 6), + "2x6/packed_packed_loop1/original.tmpli", + 8 + ); + kloop!( + f, + "2x6x2", + (16 * 6 * 2), + "2x6/packed_packed_loop1/original-unroll.tmpli", + 8 + ); + // if std::is_x86_feature_detected!("avx512f") { + // kloop!(f, "2x6x1", (32 * 6), "2x6/packed_packed_loop1/avx-512.tmpli", 16); + // kloop!(f, "2x6x2", (32 * 6 * 2), "2x6/packed_packed_loop1/avx-512-unroll.tmpli", 16); + // } + println!(); +} + +unsafe fn packed_packed_2x5(f: Option<&str>) { + println!("-- 2x5 kernels"); + kloop!(f, "2x5x1", (16 * 5), "2x5/packed_packed_loop1/avx.tmpli", 8); + kloop!( + f, + "2x5x2", + (16 * 5 * 2), + "2x5/packed_packed_loop1/avx-unroll.tmpli", + 8 + ); + // if std::is_x86_feature_detected!("avx512f") { + // kloop!(f, "2x5x1", (32 * 5), "2x5/packed_packed_loop1/avx-512.tmpli", 16); + // kloop!(f, "2x5x2", (32 * 5 * 2), "2x5/packed_packed_loop1/avx-512-unroll.tmpli", 16); + // } + println!(); +} + +unsafe fn packed_packed_3x4(f: Option<&str>) { + println!("-- 3x4 kernels"); + kloop!(f, "3x4x1", (24 * 4), "3x4/packed_packed_loop1/avx.tmpli", 8); + kloop!( + f, + "3x4x2", + (24 * 4 * 2), + "3x4/packed_packed_loop1/avx-unroll.tmpli", + 8 + ); + // if std::is_x86_feature_detected!("avx512f") { + // kloop!(f, "3x4x1", (48 * 4), "3x4/packed_packed_loop1/avx-512.tmpli", 16); + // kloop!(f, "3x4x2", (48 * 4 * 2), "3x4/packed_packed_loop1/avx-512-unroll.tmpli", 16); + // } + println!(); +} + +unsafe fn packed_packed_4x3(f: Option<&str>) { + println!("-- 4x3 kernels"); + kloop!(f, "4x3x1", (32 * 3), "4x3/packed_packed_loop1/avx.tmpli", 8); + kloop!( + f, + "4x3x2", + (32 * 3 * 2), + "4x3/packed_packed_loop1/avx-unroll.tmpli", + 8 + ); + // if std::is_x86_feature_detected!("avx512f") { + // kloop!(f, "4x3x1", (64 * 3), "4x3/packed_packed_loop1/avx-512.tmpli", 16); + // kloop!(f, "4x3x2", (64 * 3 * 2), "4x3/packed_packed_loop1/avx-512-unroll.tmpli", 16); + // } + println!(); +} + +unsafe fn packed_packed_5x2(f: Option<&str>) { + println!("-- 5x2 kernels"); + kloop!(f, "5x2x1", (40 * 2), "5x2/packed_packed_loop1/avx.tmpli", 8); + kloop!( + f, + "5x2x1", + (40 * 2 * 2), + "5x2/packed_packed_loop1/avx-unroll.tmpli", + 8 + ); + // if std::is_x86_feature_detected!("avx512f") { + // kloop!(f, "5x2x1", (80 * 2), "5x2/packed_packed_loop1/avx-512.tmpli", 16); + // kloop!(f, "5x2x2", (80 * 2 * 2), "5x2/packed_packed_loop1/avx-512-unroll.tmpli", 16); + // } + println!(); +} + +unsafe fn packed_packed_6x2(f: Option<&str>) { + println!("-- 6x2 kernels"); + kloop!(f, "6x2x1", (40 * 2), "6x2/packed_packed_loop1/avx.tmpli", 8); + kloop!( + f, + "6x2x2", + (40 * 2 * 2), + "6x2/packed_packed_loop1/avx-unroll.tmpli", + 8 + ); + // if std::is_x86_feature_detected!("avx512f") { + // kloop!(f, "6x2x1", (80 * 2), "6x2/packed_packed_loop1/avx-512.tmpli", 16); + // kloop!(f, "6x2x2", (80 * 2 * 2), "6x2/packed_packed_loop1/avx-512-unroll.tmpli", 16); + // } + println!(); +} + +#[allow(clippy::identity_op)] +unsafe fn packed_packed_8x1(f: Option<&str>) { + println!("-- 8x1 kernels"); + kloop!(f, "8x1x1", (64 * 1), "8x1/packed_packed_loop1/avx.tmpli", 8); + kloop!( + f, + "8x1x2", + (64 * 1 * 2), + "8x1/packed_packed_loop1/avx-unroll.tmpli", + 8 + ); + // if std::is_x86_feature_detected!("avx512f") { + // kloop!(f, "8x1x1", (128 * 1), "8x1/packed_packed_loop1/avx-512.tmpli", 16); + // kloop!(f, "8x1x2", (128 * 1 * 2), "8x1/packed_packed_loop1/avx-512-unroll.tmpli", 16); + // } + println!(); +} + +fn main() { + let filter = std::env::args().skip(1).find(|a| a != "--bench"); + unsafe { + packed_packed_1x8(filter.as_deref()); + packed_packed_2x6(filter.as_deref()); + packed_packed_2x5(filter.as_deref()); + packed_packed_3x4(filter.as_deref()); + packed_packed_4x3(filter.as_deref()); + packed_packed_5x2(filter.as_deref()); + packed_packed_6x2(filter.as_deref()); + packed_packed_8x1(filter.as_deref()); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/build.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/build.rs new file mode 100644 index 0000000..0138d59 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/build.rs @@ -0,0 +1,394 @@ +#![allow(clippy::box_default)] + +use liquid_core::Runtime; +use liquid_core::{Display_filter, Filter, FilterReflection, ParseFilter}; +use liquid_core::{Value, ValueView}; + +use std::{env, ffi, fs, path}; + +fn var(k: &str) -> String { + env::var(k).unwrap() +} + +fn use_masm() -> bool { + env::var("CARGO_CFG_TARGET_ENV") == Ok("msvc".to_string()) + && var("CARGO_CFG_TARGET_ARCH") == "x86_64" + && var("HOST").contains("-windows-") +} + +fn jump_table() -> Vec { + println!("cargo:rerun-if-changed=src/frame/mmm/fuse.rs"); + std::fs::read_to_string("src/frame/mmm/fuse.rs") + .unwrap() + .lines() + .filter(|l| l.contains("// jump_to:")) + .map(|l| l.split("jump_to:").nth(1).unwrap().to_owned()) + .collect() +} + +#[derive(Clone, Debug)] +struct ConfigForHalf { + extra_flags: Vec, + needs_pragma: bool, +} + +impl ConfigForHalf { + fn new(extra_flags: Vec, needs_pragma: bool) -> ConfigForHalf { + ConfigForHalf { + extra_flags, + needs_pragma, + } + } + + fn all() -> Vec { + let mut configs = vec![]; + for extra_flags in vec![ + vec![], + vec!["-march=armv8.2-a".to_string()], + vec!["-mcpu=cortex-a55".to_string()], + ] { + for needs_pragma in [false, true] { + configs.push(ConfigForHalf::new(extra_flags.clone(), needs_pragma)) + } + } + configs + } + + fn cc(&self) -> cc::Build { + let mut cc = cc::Build::new(); + for flag in &self.extra_flags { + cc.flag(flag); + } + cc + } + + fn works(&self) -> bool { + let filename = if self.needs_pragma { + "arm64/arm64fp16/dummy_fmla_pragma.S" + } else { + "arm64/arm64fp16/dummy_fmla_no_pragma.S" + }; + self.cc().file(filename).try_compile("dummy").is_ok() + } + + pub fn probe() -> Option { + Self::all().iter().find(|c| c.works()).cloned() + } +} + +fn main() { + println!("cargo::rustc-check-cfg=cfg(feature_cpu, values(\"fp16\"))"); + + let target = var("TARGET"); + let arch = var("CARGO_CFG_TARGET_ARCH"); + let os = var("CARGO_CFG_TARGET_OS"); + let out_dir = path::PathBuf::from(var("OUT_DIR")); + + let suffix = env!("CARGO_PKG_VERSION") + .replace('-', "_") + .replace('.', "_"); + make_extern_kernel_decl_macro(&out_dir, &suffix); + + match arch.as_ref() { + "x86_64" => { + let files = preprocess_files("x86_64/fma", &[], &suffix, false); + + if os == "windows" { + if use_masm() { + let mut lib_exe = cc::windows_registry::find(&target, "lib.exe") + .expect("Could not find lib.exe"); + lib_exe.arg(format!( + "/out:{}", + out_dir.join("x86_64_fma.lib").to_str().unwrap() + )); + for f in files { + let mut obj = f.clone(); + obj.set_extension("o"); + let mut ml_exe = cc::windows_registry::find(&target, "ml64.exe") + .expect("Could not find ml64.exe"); + if !ml_exe + .arg("/Fo") + .arg(&obj) + .arg("/c") + .arg(&f) + .status() + .unwrap() + .success() + { + for (i, l) in std::fs::read_to_string(&f).unwrap().lines().enumerate() { + println!("{i:8} {l}"); + } + panic!(); + } + lib_exe.arg(obj); + } + assert!(lib_exe.status().unwrap().success()); + println!( + "cargo:rustc-link-search=native={}", + out_dir.to_str().unwrap() + ); + println!("cargo:rustc-link-lib=static=x86_64_fma"); + } else { + cc::Build::new() + .files(files) + .flag("-mfma") + .compile("x86_64_fma"); + + // clang at least (dunno about gcc) outputs .asm files in the + // root directory that we need to clean up so we don't pollute + // the build output/working directory + let _ = fs::remove_file("fma_mmm_f32_16x6.asm"); + let _ = fs::remove_file("fma_mmm_i32_8x8.asm"); + let _ = fs::remove_file("fma_sigmoid_f32.asm"); + let _ = fs::remove_file("fma_tanh_f32.asm"); + } + } else { + cc::Build::new() + .files(files) + .flag("-mfma") + .compile("x86_64_fma"); + } + } + "arm" | "armv7" => { + let files = preprocess_files("arm32/armvfpv2", &[], &suffix, false); + cc::Build::new() + .files(files) + .flag("-marm") + .flag("-mfpu=vfp") + .compile("armvfpv2"); + let files = preprocess_files( + "arm32/armv7neon", + &[("core", vec!["cortexa7", "cortexa9", "generic"])], + &suffix, + false, + ); + cc::Build::new() + .files(files) + .flag("-marm") + .flag("-mfpu=neon") + .compile("armv7neon"); + } + "aarch64" => { + let files = preprocess_files( + "arm64/arm64simd", + &[("core", vec!["a53", "a55", "gen"])], + &suffix, + false, + ); + cc::Build::new().files(files).compile("arm64simd"); + if std::env::var("CARGO_FEATURE_NO_FP16").is_err() { + let config = + ConfigForHalf::probe().expect("No configuration found for fp16 support"); + let files = preprocess_files( + "arm64/arm64fp16", + &[("core", vec!["a55", "gen"])], + &suffix, + config.needs_pragma, + ); + config.cc().files(files).compile("arm64fp16") + } + } + _ => {} + } +} + +type Variant = (&'static str, Vec<&'static str>); + +fn preprocess_files( + input: impl AsRef, + variants: &[Variant], + suffix: &str, + needs_pragma: bool, +) -> Vec { + let out_dir = path::PathBuf::from(var("OUT_DIR")); + let mut files = vec![]; + let dir_entries = { + let mut dir_entries: Vec = input + .as_ref() + .read_dir() + .unwrap() + .map(|f| f.unwrap()) + .collect(); + dir_entries.sort_by_key(|a| a.path()); + dir_entries + }; + for f in dir_entries { + if f.path().extension() == Some(ffi::OsStr::new("tmpl")) { + let tmpl_file = f.path().file_name().unwrap().to_str().unwrap().to_owned(); + let concerned_variants: Vec<&Variant> = variants + .iter() + .filter(|v| tmpl_file.contains(v.0)) + .collect(); + let expanded_variants = concerned_variants.iter().map(|pair| pair.1.len()).product(); + for v in 0..expanded_variants { + let mut tmpl_file = tmpl_file.clone(); + let mut id = v; + let mut globals = vec![]; + for variable in variants { + let key = variable.0; + let value = variable.1[id % variable.1.len()]; + globals.push((key, value)); + tmpl_file = tmpl_file.replace(key, value); + id /= variable.1.len(); + } + let mut file = out_dir.join(tmpl_file); + file.set_extension("S"); + preprocess_file(f.path(), &file, &globals, suffix, needs_pragma); + files.push(file); + } + } + } + files +} + +fn strip_comments(s: String, msvc: bool) -> String { + if msvc { + s.lines() + .map(|line| line.replace("//", ";")) + .collect::>() + .join("\n") + } else { + s + } +} + +fn preprocess_file( + template: impl AsRef, + output: impl AsRef, + variants: &[(&'static str, &'static str)], + suffix: &str, + needs_pragma: bool, +) { + println!( + "cargo:rerun-if-changed={}", + template.as_ref().to_string_lossy() + ); + let family = var("CARGO_CFG_TARGET_FAMILY"); + let os = var("CARGO_CFG_TARGET_OS"); + // We also check to see if we're on a windows host, if we aren't, we won't be + // able to use the Microsoft assemblers, + let msvc = use_masm(); + println!( + "cargo:rerun-if-changed={}", + template.as_ref().to_string_lossy() + ); + let mut input = fs::read_to_string(&template).unwrap(); + input = strip_comments(input, msvc); + let l = if os == "macos" { + "L" + } else if family == "windows" { + "" + } else { + "." + } + .to_owned(); + let long = if msvc { "dd" } else { ".long" }; + let g = if os == "macos" || os == "ios" { + "_" + } else { + "" + }; + let mut globals = liquid::object!({ + "msvc": msvc, + "needs_pragma": needs_pragma, + "family": family, + "os": os, + "L": l, + "G": g, + "suffix": suffix, + "long": long, + "jump_table": jump_table(), + }); + for (k, v) in variants { + globals.insert(k.to_string().into(), liquid::model::Value::scalar(*v)); + } + let partials = load_partials(template.as_ref().parent().unwrap(), msvc); + if let Err(e) = liquid::ParserBuilder::with_stdlib() + .partials(liquid::partials::LazyCompiler::new(partials)) + .filter(F16) + .build() + .and_then(|p| p.parse(&input)) + .and_then(|r| r.render_to(&mut fs::File::create(&output).unwrap(), &globals)) + { + eprintln!("Processing {}", template.as_ref().to_string_lossy()); + eprintln!("{e}"); + panic!() + } +} + +fn load_partials(p: &path::Path, msvc: bool) -> liquid::partials::InMemorySource { + let mut mem = liquid::partials::InMemorySource::new(); + for f in walkdir::WalkDir::new(p) { + let f = f.unwrap(); + if f.path().is_dir() { + continue; + } + let ext = f + .path() + .extension() + .map(|s| s.to_string_lossy()) + .unwrap_or_else(|| "".into()); + let text = std::fs::read_to_string(f.path()).unwrap(); + let text = match ext.as_ref() { + "tmpli" => Some(text.replace("{{", "{").replace("}}", "}")), + "tmpliq" => Some(text), + _ => None, + }; + if let Some(text) = text { + let text = strip_comments(text, msvc); + let key = f + .path() + .strip_prefix(p) + .unwrap() + .to_str() + .unwrap() + .to_owned() + .replace('\\', "/"); + println!( + "cargo:rerun-if-changed={}", + f.path().to_string_lossy().replace('\\', "/") + ); + mem.add(key, text); + } + } + mem +} + +fn make_extern_kernel_decl_macro(out_dir: &path::Path, suffix: &str) { + let macro_decl = r#" + macro_rules! extern_kernel { + (fn $name: ident($($par_name:ident : $par_type: ty ),*) -> $rv: ty) => { + paste! { + extern "C" { pub fn [<$name _ _suffix>]($(par_name: $par_type),*) -> $rv; } + pub use [<$name _ _suffix>] as $name; + } + } + }"# + .replace("_suffix", suffix); + std::fs::write(out_dir.join("extern_kernel_macro.rs"), macro_decl).unwrap(); +} + +#[derive(Clone, ParseFilter, FilterReflection)] +#[filter( + name = "float16", + description = "Write a float16 constant with the .float16 directive in gcc, or as short in clang", + parsed(F16Filter) +)] +pub struct F16; + +#[derive(Debug, Default, Display_filter)] +#[name = "float16"] +struct F16Filter; + +impl Filter for F16Filter { + fn evaluate( + &self, + input: &dyn ValueView, + _runtime: &dyn Runtime, + ) -> liquid_core::Result { + let input: f32 = input.as_scalar().unwrap().to_float().unwrap() as f32; + let value = half::f16::from_f32(input); + let bits = value.to_bits(); + Ok(format!(".short {bits}").to_value()) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/proptest-regressions/generic/mmm.txt b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/proptest-regressions/generic/mmm.txt new file mode 100644 index 0000000..a6a8511 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/proptest-regressions/generic/mmm.txt @@ -0,0 +1,19 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 121b28bde52462d8007d6aa3b57effba3c4f3ce05a57803cbbcaa4b7096d083f # shrinks to pb = QWrappingMulHighDoublingProblem { c: [0, 1, 0, 0], mult: 0, boo: PhantomData } +cc d54877b98a8e851f187820853649131a4a0ad62e0967e5c5881577ab2ebc56f8 # shrinks to pb = QWrappingMulHighDoublingProblem { c: [0, 0, 0, 1], mult: 0, boo: PhantomData } +cc a80e1dd4a5bb0c6858ccdaf91a5aa4a8b9b71594473a91aa82583e0db4ec6ef5 # shrinks to pb = QWrappingMulHighDoublingProblem { c: [0, 0, 0, 1], mult: 0, boo: PhantomData } +cc 5aac55722aca2ca476544313178602ef71fbdda4960d3c711605cc70c6e93c06 # shrinks to pb = QWrappingMulHighDoublingProblem { c: [0, 0, 0, 0, 0, -1], mult: 0, boo: PhantomData } +cc 462968c12ee0fcc1e6a78b7fc4f412d0c7f0766299893886b8ec49cf978eff49 # shrinks to pb = QWrappingMulHighDoublingProblem { c: [0, 0, 0, -1], mult: 0, boo: PhantomData } +cc 8ac3a83562b28a2ee6fa3896f929b7210ee7d4f559d9e9c2c06c9dd7e7fa93b5 # shrinks to pb = QWrappingMulHighDoublingProblem { c: [0, 0, 0, 0, 0, 1], mult: 0, boo: PhantomData } +cc d0369cc2a5339619ebabdd3dbdac60ea9ebf542b23ef536db03fba2de3651c21 # shrinks to pb = QWrappingMulHighDoublingProblem { c: [0, 0, 0, 0, 0, -2], mult: 0, boo: PhantomData } +cc 9c92a9f97a6d24ffaa21370b1eb5aec9880b508a384d037287df8de7f8d4c608 # shrinks to pb = QWrappingMulHighDoublingProblem { c: [0, 0, 0, 0, 0, 1], mult: 0, boo: PhantomData } +cc f3574fa61a7e297de8cf76c8268dee6c6d062dfc5f98738618bdeb69d5b9b595 # shrinks to pb = QWrappingMulHighDoublingProblem { c: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0], mult: 0, boo: PhantomData } +cc bb5421af3bb30e0ac316287dc41563ce69eca8ca74abf5f5a7f8e82d7cab6628 # shrinks to pb = QWrappingMulHighDoublingProblem { c: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], mult: 0, boo: PhantomData } +cc 45859e240aae3fa0e7667d46dd475d9f3de42e87ace8b3678db322b4a18509af # shrinks to pb = QWrappingMulHighDoublingProblem { c: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255], mult: 0, boo: PhantomData } +cc 65d3a24331e3919a11d220118f97fe21caf769d1c590737cad9aef927959b4e9 # shrinks to pb = QWrappingMulHighDoublingProblem { c: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1], mult: 0, boo: PhantomData } +cc 378b328c0e46a5038f5876614a1ac16dc796482676f2ac81a670f5dbabb490ec # shrinks to pb = ConvProblem { ci: 1, co: 19, kt: 1, stride: 1, dilation: 4, filters: 19,1,F32 0, 0, 0, 0, 0, 0, 0, 0.026, -0.286, -0.627, -0.248, -0.291..., data: 1,5,F32 0.873, -0.377, -0.818, -0.415, 0.746, phantom: PhantomData } diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/proptest-regressions/x86_64_fma/mmm.txt b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/proptest-regressions/x86_64_fma/mmm.txt new file mode 100644 index 0000000..cb183ab --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/proptest-regressions/x86_64_fma/mmm.txt @@ -0,0 +1,16 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 058e808c67cb65d0a9013ff2cc0d9f726f19bef9b21e205396db15871be2586d # shrinks to pb = ReturnCProblem { c: [0.0, 0.0, -0.0, 0.0, 0.0, -0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.0, 0.0, 0.0, 0.0, -0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.0, 0.0, -0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -1000702600000000000000.0], boo: PhantomData } +cc 65e8110dc380469eec961f5d4ef07bd31b7f44defe08af00e6dcc812228bcc7f # shrinks to pb = QRightShiftProblem { c: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], shift: 0, policy: Away, boo: PhantomData } +cc 1f7e80660ef4800d7bbb6acafe9bbff1e396f0f78b296d20deb51b439a40736c # shrinks to pb = QScaleProblem { c: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], mult: 536870912, shift: 0, policy: Odd, boo: PhantomData } +cc 57c3e684c05eb9cc964eaaf22ccb620a2d9ef5ea5bd38c6dff6c5344b8192619 # shrinks to pb = QScaleProblem { c: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], mult: 536870912, shift: 0, policy: Odd, boo: PhantomData } +cc 9fce04b53146b7615e54728101dd8ad24ffd157d63c536eb1ca888070c8b1719 # shrinks to pb = PackedOffsetsProblem { a: [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], b: [0.0, 0.0, 0.0, 0.0, 1.0, 9.0, 7.0, 7.0, 0.0, 5.0, 2.0, 3.0, 0.0, 3.0, 7.0, 2.0, 2.0, 6.0, 4.0, 4.0, 1.0, 5.0, 5.0, 1.0, 3.0, 7.0, 3.0, 4.0, 7.0, 5.0, 8.0, 8.0, 1.0, 5.0, 6.0, 8.0, 7.0, 6.0], cols_offsets: [28], rows_offsets: [9], add_one: false, _phantom: PhantomData } +cc 708172a1bb5110aa22d15b4a0daa777fae18dda1ab481328ab200f54d72357b6 # shrinks to pb = ConvProblem { ci: 1, co: 17, kt: 1, stride: 3, dilation: 4, filters: 17,1,F32 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.044..., data: 1,3,F32 -0.416, -0.932, -0.21, phantom: PhantomData } +cc e62d087b655259ebda7622f40e0049a49fb803777a3a98343c6855e69944ccfc # shrinks to pb = ConvProblem { ci: 1, co: 17, kt: 1, stride: 4, dilation: 4, filters: 17,1,F32 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0..., data: 1,4,F32 0.251, 0, 0, 0, phantom: PhantomData } +cc 15bfa5078b5479e12e74820888d477cbdd628faa03aa1bef26738dfa8cbff014 # shrinks to (m, k, n, ref a, ref b) = (1, 1, 2, 1,1,F32 0, 1,2,F32 0, 0) +cc dde4ea4eafe9beb60bffe8307cdb62c6143849f247ad9edbb64acd586f57d722 # shrinks to (m, k, n, ref a, ref b) = (1, 1, 2, 1,1,F32 0, 1,2,F32 0, 0) +cc 066cecc4f004061abab40982576e9f10c56dd090751a699e2cffeb3f7260b0e1 # shrinks to (m, k, n, ref a, ref b) = (1, 1, 2, 1,1,F32 0, 1,2,F32 0, 0) diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32.rs new file mode 100644 index 0000000..f158a9c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32.rs @@ -0,0 +1,105 @@ +use std::{env, fs}; +pub mod armv7neon; +mod armvfpv2; +mod cortex_a7; +mod cortex_a9; +use armv7neon::*; + +use crate::frame::element_wise::ElementWiseKer; +use crate::frame::mmm::kernel::MatMatMulKer; + +use crate::Ops; + +fn has_neon_cpuinfo() -> std::io::Result { + let cpu_info = fs::read_to_string("/proc/cpuinfo")?; + let neon = cpu_info.split("\n").any(|line| { + line.starts_with("Features") && (line.contains("neon") || line.contains("asimd")) + }); + Ok(neon) +} + +fn cpu_part() -> Option { + fs::read_to_string("/proc/cpuinfo") + .ok() + .and_then(|cpuinfo| { + cpuinfo + .lines() + .find(|line| line.starts_with("CPU part")) + .and_then(|s| s.trim().split_whitespace().last()) + .and_then(|s| s.strip_prefix("0x")) + .and_then(|s| usize::from_str_radix(s, 16).ok()) + }) +} + +fn has_neon() -> bool { + if let Ok(v) = env::var("TRACT_CPU_ARM32_NEON") { + return v == "true" || v == "1"; + } + has_neon_cpuinfo().unwrap_or(false) +} + +pub fn plug(ops: &mut Ops) { + let impls = vec![ + armv7neon_mmm_f32_8x4_cortexa7::mmm(), + armv7neon_mmm_f32_8x6_cortexa7::mmm(), + armv7neon_mmm_f32_8x4_cortexa9::mmm(), + armv7neon_mmm_f32_8x6_cortexa9::mmm(), + armv7neon_mmm_f32_8x4_generic::mmm(), + armv7neon_mmm_f32_8x6_generic::mmm(), + crate::generic::mmm::generic_f32_4x4::mmm(), + ]; + ops.mmm_f32_impls = impls.clone(); + if has_neon() { + log::info!("armv7neon activated (smmm, ssigmoid), stanh)"); + let cpu = cpu_part().unwrap_or(0); + + fn prefer_8x4(_m: Option, _k: Option, n: Option) -> bool { + n.map(|n| n % 4 == 0 && n % 6 != 0 && n <= 12) + .unwrap_or(false) + } + + ops.mmv_f32 = match cpu { + 0xc07 => Box::new(|_, _| armv7neon::armv7neon_mmm_f32_32x1_cortexa7::mmm()), + 0xc09 => Box::new(|_, _| armv7neon::armv7neon_mmm_f32_32x1_cortexa9::mmm()), + _ => Box::new(|_, _| armv7neon::armv7neon_mmm_f32_32x1_generic::mmm()), + }; + + ops.mmm_f32 = match cpu { + 0xc07 => { + let model = cortex_a7::model(); + Box::new(move |m, k, n| model.pick(&impls, m, k, n)) + } + 0xc09 => { + let model = cortex_a9::model(); + Box::new(move |m, k, n| model.pick(&impls, m, k, n)) + } + _ => Box::new(|m, k, n| { + if prefer_8x4(m, k, n) { + armv7neon::armv7neon_mmm_f32_8x4_generic::mmm() + } else { + armv7neon::armv7neon_mmm_f32_8x6_generic::mmm() + } + }), + }; + ops.qmmm_i32 = Box::new(|_, _, _| armv7neon::armv7neon_mmm_i32_8x4::mmm()); + ops.qmmv_i32 = Box::new(|_, _| armv7neon::armv7neon_mmm_i32_32x1::mmm()); + ops.sigmoid_f32 = Box::new(|| armv7neon_sigmoid_f32_4n::ew()); + ops.tanh_f32 = Box::new(|| armv7neon_tanh_f32_4n::ew()); + } else { + log::info!("armvfpv2 activated for smmm"); + ops.mmm_f32 = Box::new(|_, _, _| armvfpv2::armvfpv2_mmm_f32_4x4::mmm()); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn may_have_neon() { + println!("Has neon ? {:?}", has_neon()); + if let Ok(neon) = env::var("TRACT_CPU_EXPECT_ARM32_NEON") { + assert_eq!(neon == "true", has_neon()); + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32/armv7neon.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32/armv7neon.rs new file mode 100644 index 0000000..1980893 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32/armv7neon.rs @@ -0,0 +1,30 @@ +use crate::frame::element_wise::*; +use crate::frame::mmm::*; + +extern_kernel!(fn armv7neon_prefetch(start: *const u8, end: *const u8) -> ()); + +#[inline(always)] +pub fn prefetch(start: *const u8, len: usize) { + unsafe { armv7neon_prefetch(start, start.offset(len as isize)) } +} + +MMMKernel!(i32, armv7neon_mmm_i32_8x4; 8, 4; 32, 4; 0, 0; prefetch, crate::arm32::has_neon()); +MMMKernel!(i32, armv7neon_mmm_i32_32x1; 32,1 ; 32, 4; 0, 0; prefetch, crate::arm32::has_neon()); +MMMKernel!(f32, armv7neon_mmm_f32_8x4_cortexa7; 8, 4; 4, 4; 0, 0; prefetch, crate::arm32::has_neon()); +MMMKernel!(f32, armv7neon_mmm_f32_8x4_cortexa9; 8, 4; 4, 4; 0, 0; prefetch, crate::arm32::has_neon()); +MMMKernel!(f32, armv7neon_mmm_f32_8x4_generic; 8, 4; 4, 4; 0, 0; prefetch, crate::arm32::has_neon()); +MMMKernel!(f32, armv7neon_mmm_f32_8x6_cortexa7; 8, 6; 4, 4; 0, 0; prefetch, crate::arm32::has_neon()); +MMMKernel!(f32, armv7neon_mmm_f32_8x6_cortexa9; 8, 6; 4, 4; 0, 0; prefetch, crate::arm32::has_neon()); +MMMKernel!(f32, armv7neon_mmm_f32_8x6_generic; 8, 6; 4, 4; 0, 0; prefetch, crate::arm32::has_neon()); +MMMKernel!(f32, armv7neon_mmm_f32_32x1_cortexa7; 32, 1; 4, 4; 0, 0; prefetch, crate::arm32::has_neon()); +MMMKernel!(f32, armv7neon_mmm_f32_32x1_cortexa9; 32, 1; 4, 4; 0, 0; prefetch, crate::arm32::has_neon()); +MMMKernel!(f32, armv7neon_mmm_f32_32x1_generic; 32, 1; 4, 4; 0, 0; prefetch, crate::arm32::has_neon()); + +sigmoid_impl!( + f32, + armv7neon_sigmoid_f32_4n, + 4, + 4, + crate::arm32::has_neon() +); +tanh_impl!(f32, armv7neon_tanh_f32_4n, 4, 4, crate::arm32::has_neon()); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32/armvfpv2.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32/armvfpv2.rs new file mode 100644 index 0000000..503af55 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32/armvfpv2.rs @@ -0,0 +1,3 @@ +use crate::frame::mmm::*; + +MMMKernel!(f32, armvfpv2_mmm_f32_4x4; 4, 4; 4, 4; 0, 0; no_prefetch, true); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32/cortex_a7.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32/cortex_a7.rs new file mode 100644 index 0000000..02d8b14 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32/cortex_a7.rs @@ -0,0 +1,861 @@ +use crate::frame::mmm::CostModel; +pub fn model() -> CostModel<'static> { + CostModel { + big_product_mkn_threshold: 4193728.0, + big_product_kernel_choice: "armv7neon_mmm_f32_8x6_cortexa7", + kernels: &[ + "armv7neon_mmm_f32_8x4_cortexa7", + "armv7neon_mmm_f32_8x4_cortexa9", + "armv7neon_mmm_f32_8x4_generic", + "armv7neon_mmm_f32_8x6_cortexa7", + "armv7neon_mmm_f32_8x6_cortexa9", + "armv7neon_mmm_f32_8x6_generic", + "generic_f32_4x4", + ], + mrs: &[4, 8], + nrs: &[4, 6], + feat_norm_mean: &[ + 4.589878771602424, + 4.5739692460187005, + 4.598167981532298, + 13.762015999153403, + 1.5038983903420524, + 0.749874245472837, + 3.465165995975855, + 0.8777665995975855, + 1.5022635814889336, + 0.7570422535211268, + 2.482142857142857, + 0.8333752515090543, + ], + feat_norm_stddev: &[ + 1.2587312982588519, + 1.2603116830524392, + 1.2581181647300588, + 1.3169322340874257, + 1.1192637768418767, + 0.43308528195884044, + 2.2762097127791114, + 0.32755518043295856, + 1.1069539235554247, + 0.42886977033219037, + 1.7067987601825914, + 0.37264049924995035, + ], + w1: &[ + 0.06765510141849518, + 0.024555781856179237, + -0.8821254968643188, + -0.004870870150625706, + -0.10525479167699814, + 0.1827959418296814, + 0.1633400171995163, + -0.2377464473247528, + -0.17880690097808838, + 0.19097138941287994, + 0.04676022008061409, + -0.11329511553049088, + 0.4089120030403137, + -0.3100685477256775, + -0.1652061492204666, + -0.19124962389469147, + -0.03810987249016762, + -0.00785011239349842, + 0.09714752435684204, + -0.11142419278621674, + 0.19261880218982697, + -0.2893339991569519, + -0.19540216028690338, + 0.39759594202041626, + -0.00619965186342597, + -0.8473111391067505, + 0.343344122171402, + -0.12575943768024445, + 0.029266485944390297, + -0.02900734543800354, + -0.019343264400959015, + 0.08306540548801422, + -0.1927606761455536, + 0.23312175273895264, + 0.2576882541179657, + -0.35881471633911133, + -0.27300119400024414, + -0.2995607852935791, + -0.7934547662734985, + -0.9349930286407471, + -0.011614155024290085, + -0.12521372735500336, + 0.011371670290827751, + 0.05779163911938667, + 0.17875070869922638, + -0.23169392347335815, + -0.09749509394168854, + 0.07436174154281616, + 0.24035069346427917, + -0.1262669861316681, + 0.3874961733818054, + -0.11149000376462936, + 0.03639678284525871, + 0.17740628123283386, + 0.03768332302570343, + -0.20480288565158844, + -0.1955408751964569, + 0.44144806265830994, + 0.3628064692020416, + -0.2537013292312622, + 0.019405143335461617, + 0.06186319515109062, + 0.5196826457977295, + 0.3010406494140625, + 0.04013144597411156, + 0.03517461195588112, + -0.037290964275598526, + 0.009919736534357071, + -0.3135205805301666, + 0.4654330909252167, + 0.46720823645591736, + 0.29665476083755493, + 0.09099660068750381, + -0.7376689314842224, + -0.07840575277805328, + -0.5192644000053406, + 0.019796665757894516, + -0.021734869107604027, + 0.13953897356987, + -0.04154204577207565, + 0.10942933708429337, + -0.13621817529201508, + -0.04218055680394173, + 0.09188657253980637, + -0.16021296381950378, + -0.19393481314182281, + 0.3737955689430237, + 0.08288388699293137, + -0.08280416578054428, + -0.13087297976016998, + -0.09470323473215103, + 0.2779513895511627, + 0.03663017228245735, + 0.36601993441581726, + 0.8102841377258301, + 0.6883901953697205, + -0.33066609501838684, + -0.34960171580314636, + 0.923985481262207, + 0.5853908061981201, + 0.07039576023817062, + -0.11843020468950272, + -0.06797836720943451, + 0.0974433571100235, + -0.4707315266132355, + 0.37827417254447937, + 0.15521520376205444, + -0.7403592467308044, + -0.25005313754081726, + 0.596679151058197, + -0.7277861833572388, + -0.6915309429168701, + -0.0050544412806630135, + -0.12311484664678574, + 0.04149714484810829, + 0.05289606750011444, + 0.2448417991399765, + -0.47261708974838257, + -0.3535511791706085, + 0.4614925682544708, + 0.9230178594589233, + -0.5351396799087524, + 0.8224894404411316, + 0.37244901061058044, + -0.08826857805252075, + -0.0452042818069458, + 0.0035054143518209457, + 0.09203510731458664, + 0.08918709307909012, + -0.0694250762462616, + -0.053435735404491425, + 0.1012222170829773, + 0.3401939570903778, + -0.38458573818206787, + 0.3040490746498108, + 0.7614821791648865, + -0.17064380645751953, + 0.22403603792190552, + 0.08646601438522339, + -0.08289062976837158, + -0.20126193761825562, + 0.2795524299144745, + 0.13253425061702728, + -0.07332615554332733, + 0.2151418924331665, + 0.16798575222492218, + 0.003749655559659004, + 0.2437056005001068, + -0.09098415076732635, + 0.18923071026802063, + 0.07854695618152618, + -0.25417080521583557, + 0.15693743526935577, + -0.30657434463500977, + -0.19041943550109863, + 0.26519766449928284, + 0.24278832972049713, + -0.18357035517692566, + -0.015992645174264908, + 0.43973660469055176, + 0.02785446122288704, + 0.3032245934009552, + -0.021606506779789925, + -0.2682349383831024, + -0.10395143181085587, + 0.050348248332738876, + 0.12892353534698486, + -0.10498340427875519, + -0.027477847412228584, + 0.09730125963687897, + -0.16150422394275665, + -0.21831916272640228, + 0.10376061499118805, + -0.25544440746307373, + 0.031593386083841324, + 0.11986788362264633, + 0.22690074145793915, + -0.3509098291397095, + -0.1881190538406372, + -0.04210145026445389, + 0.6883101463317871, + -0.07829979062080383, + 0.4657376706600189, + 0.9263871908187866, + 0.08322961628437042, + 0.04429711028933525, + -0.08905605971813202, + -0.06788893789052963, + -0.056182388216257095, + -0.04881853610277176, + -0.04854113608598709, + 0.15449045598506927, + 0.32911357283592224, + -0.5772383809089661, + -0.00027374469209462404, + -0.2995521128177643, + -0.027322502806782722, + 0.5023694038391113, + 0.045783523470163345, + -0.4035968780517578, + 0.053967904299497604, + 0.00014662329340353608, + 0.021607715636491776, + -0.028252260759472847, + -0.05918470770120621, + -0.1273883581161499, + 0.0679078996181488, + 0.25051605701446533, + -0.0745333656668663, + 0.18680104613304138, + -0.12048312276601791, + 0.013110226020216942, + -0.07659415900707245, + 0.2906968295574188, + 0.3136366307735443, + -0.47699007391929626, + 0.02583535574376583, + -0.15701107680797577, + 0.045304182916879654, + 0.23456838726997375, + -0.06186807528138161, + 0.3926846981048584, + -0.13252438604831696, + -0.16362214088439941, + 0.013557562604546547, + -0.09991434961557388, + 0.09150815010070801, + -0.006477471441030502, + 0.2915862202644348, + 0.5867642164230347, + -0.37984445691108704, + 0.033169880509376526, + 0.024414243176579475, + -0.0384003147482872, + -0.06395144015550613, + 0.07380940765142441, + -0.025898484513163567, + 0.03951931372284889, + -0.2343142330646515, + 0.27318838238716125, + 0.1105947494506836, + 0.290696382522583, + -0.17851489782333374, + -0.17699271440505981, + -0.210996612906456, + -0.10575137287378311, + 0.15886521339416504, + 0.10631759464740753, + 0.22946283221244812, + -0.3170112073421478, + -0.49773311614990234, + -0.10753292590379715, + -0.1114523783326149, + -0.10953730344772339, + 0.4754663109779358, + 0.20793643593788147, + 0.021392812952399254, + -0.0691467821598053, + 0.03368104621767998, + -0.017844771966338158, + 0.1657843142747879, + -0.5556477904319763, + -1.108074426651001, + -0.822117805480957, + -0.06053074076771736, + -0.4072379469871521, + 0.09109722077846527, + -0.5544739961624146, + -0.13978064060211182, + -0.36262163519859314, + 0.20034632086753845, + 0.050625383853912354, + 0.1497042030096054, + -0.18745489418506622, + 0.0894727036356926, + 0.00417149206623435, + 0.2228451371192932, + 0.00852279644459486, + -0.028313757851719856, + 0.04104698821902275, + -0.0874263271689415, + 0.19788521528244019, + -0.019343160092830658, + -0.03962515667080879, + 0.2092486023902893, + -0.44425246119499207, + -0.48542261123657227, + -0.04222029820084572, + 0.7616084218025208, + 0.512810468673706, + -0.17871123552322388, + 0.5459727644920349, + -0.13069608807563782, + 0.09155352413654327, + 0.11548610031604767, + -0.15368784964084625, + 0.038799818605184555, + -0.049028217792510986, + -0.03215758875012398, + -0.050522346049547195, + 0.1663637012243271, + -0.15482299029827118, + -0.9425870180130005, + -0.7017998695373535, + 0.04315050691366196, + -0.019968662410974503, + 0.03749818727374077, + -0.07611791789531708, + 0.32011789083480835, + -0.6925904750823975, + -0.49334919452667236, + 0.23214411735534668, + 1.1447347402572632, + -0.6757001876831055, + 0.7940422296524048, + 0.40169182419776917, + -0.018513813614845276, + 0.048821814358234406, + -0.016693273559212685, + 0.008068449795246124, + 0.04566117003560066, + -0.09829569607973099, + -0.026971371844410896, + 0.05381541699171066, + -0.3659301698207855, + 0.3473235070705414, + 0.14521746337413788, + 0.11228122562170029, + -0.041056130081415176, + -0.11228874325752258, + 0.006667478010058403, + 0.15931302309036255, + -0.30010080337524414, + 0.3464723229408264, + 0.4476386308670044, + -0.3498152494430542, + 0.2616507112979889, + -0.19995814561843872, + 0.10946320742368698, + 0.4034257233142853, + -0.08651446551084518, + 0.018647747114300728, + 0.11572548002004623, + -0.100877545773983, + -0.16341210901737213, + 0.2377898246049881, + 0.3417612910270691, + -0.49084869027137756, + -0.02805873565375805, + -0.09811390936374664, + 0.17161016166210175, + 0.3627470135688782, + -0.08954513072967529, + 0.06629404425621033, + 0.012786897830665112, + 0.01578289456665516, + -0.32630467414855957, + 0.4854920506477356, + 0.12709765136241913, + -0.4909423291683197, + -0.3745254874229431, + -0.6513142585754395, + -0.040075208991765976, + -0.569782018661499, + -0.009953420609235764, + 0.04735071584582329, + 0.0230120699852705, + -0.07381311058998108, + -0.06293600797653198, + 0.20196016132831573, + 0.26551517844200134, + -0.42071688175201416, + 0.28809165954589844, + 0.19747501611709595, + -0.5686206221580505, + -0.5285986661911011, + 0.02009684592485428, + 0.11322621256113052, + -0.1082596555352211, + -0.0856761634349823, + -0.04493662342429161, + -0.6179490089416504, + -0.1442672610282898, + 0.028762176632881165, + 0.12426868081092834, + -0.5771384835243225, + 0.1608373522758484, + 0.004147801548242569, + -0.047590240836143494, + 0.10347189754247665, + 0.11780986934900284, + -0.08490656316280365, + -0.0746934711933136, + 0.15699702501296997, + 0.1298881322145462, + -0.14411042630672455, + -0.08601037412881851, + 0.2997709810733795, + -0.05418943241238594, + -0.1772651970386505, + 0.04576871916651726, + -0.13510753214359283, + -0.057203926146030426, + 0.18647770583629608, + 0.0055348677560687065, + -0.12238732725381851, + -0.11199415475130081, + 0.43077343702316284, + 0.1349855363368988, + 0.21327465772628784, + 0.05924845486879349, + 0.12549948692321777, + -0.060076650232076645, + 0.23921678960323334, + 0.02152605727314949, + -0.1352948695421219, + 0.09325127303600311, + -0.14411674439907074, + 0.010495728813111782, + 0.11577513813972473, + -0.07580242305994034, + 0.42641204595565796, + -0.5557231903076172, + -0.12044595927000046, + 0.024152765050530434, + -0.14175696671009064, + 0.024960221722722054, + 0.10017693042755127, + -0.07402117550373077, + 0.09156208485364914, + 0.455565482378006, + 0.424320250749588, + -0.07668061554431915, + 0.10318724811077118, + -0.32521969079971313, + -0.2653461694717407, + -0.03919212520122528, + 0.12909358739852905, + -0.17091549932956696, + 0.07353391498327255, + 0.11510979384183884, + -0.23758216202259064, + -0.3059186339378357, + -0.046047650277614594, + 0.17527209222316742, + 0.19020265340805054, + -0.20766229927539825, + -0.23476286232471466, + -0.14011070132255554, + 0.1085173636674881, + -0.020777594298124313, + 0.014691418968141079, + 0.21648286283016205, + -0.21576255559921265, + 0.28203028440475464, + 0.6320008635520935, + -0.23609709739685059, + 0.16072526574134827, + 0.30149686336517334, + -0.05675647035241127, + -0.018186205998063087, + -0.1844293773174286, + 0.13510139286518097, + 0.05780869722366333, + 0.07202577590942383, + 0.07459436357021332, + 0.18700383603572845, + -0.09449177235364914, + 0.057188909500837326, + 0.21453143656253815, + -0.30002379417419434, + -0.12217795103788376, + 0.03723505884408951, + -0.18360234797000885, + -0.029992947354912758, + 0.10999765247106552, + 0.09575961530208588, + -0.36028456687927246, + -0.4311397075653076, + 0.5812231302261353, + ], + b1: &[ + 0.3801889419555664, + -0.5001883506774902, + 0.19484910368919373, + 0.6488791704177856, + 0.38620173931121826, + 0.8780303597450256, + -0.1126403734087944, + 0.021730314940214157, + -0.7806469202041626, + -0.04312174394726753, + 0.3102167546749115, + 0.9241658449172974, + 0.8900863528251648, + -0.2938256561756134, + -0.5012822151184082, + -0.00329477502964437, + 0.5169500708580017, + 0.4563848376274109, + -0.4903448224067688, + 0.27919942140579224, + -0.4288303554058075, + -0.1836952418088913, + -0.09118890762329102, + 0.5528226494789124, + -0.19896377623081207, + 0.33588215708732605, + 0.07895006239414215, + 0.07812929153442383, + 0.6203332543373108, + 0.8427650332450867, + -0.684628427028656, + 0.5408275723457336, + -0.5548633933067322, + -0.49557214975357056, + 0.7953769564628601, + -0.4109633266925812, + -0.6270897388458252, + -0.43285393714904785, + -0.7562689781188965, + -0.7167727947235107, + ], + w2: &[ + 0.15592391788959503, + 0.25119924545288086, + -0.499594122171402, + -0.5441639423370361, + -0.11186911165714264, + -0.6334478855133057, + 0.28880706429481506, + -0.592946469783783, + 0.7188563942909241, + -0.49322614073753357, + -0.1398385912179947, + -0.1868145614862442, + 0.9288992881774902, + -0.07525540888309479, + 0.2288437783718109, + 0.09932874143123627, + 0.2782813012599945, + -0.12644614279270172, + -0.14151062071323395, + 0.38845404982566833, + 0.2691279947757721, + -0.9148958921432495, + 0.19230225682258606, + 0.6098687052726746, + -0.24782557785511017, + -0.6989489197731018, + -0.30721813440322876, + -0.4890380799770355, + -0.43724432587623596, + -0.38428765535354614, + -0.6491377353668213, + -0.28134995698928833, + -0.36228886246681213, + -0.05963568389415741, + 0.5086851119995117, + 0.4664144814014435, + 0.3797634541988373, + 0.5596290826797485, + -0.1977449357509613, + 0.6540879607200623, + -0.24533972144126892, + 0.6865915656089783, + -0.18364377319812775, + 0.0013501447392627597, + -0.4037604331970215, + -0.287411093711853, + -0.43570032715797424, + -0.4085054099559784, + 0.7341827750205994, + -0.29973891377449036, + -0.18240050971508026, + -0.23446109890937805, + 0.7225431799888611, + 0.008502814918756485, + 0.04582007974386215, + 0.03352205455303192, + 0.12457727640867233, + -0.2019437849521637, + -0.1299249827861786, + -0.09946829080581665, + 0.40665051341056824, + -0.6841736435890198, + -0.523845911026001, + 0.21656402945518494, + 0.6046024560928345, + -0.6393186450004578, + -0.3965637981891632, + -0.7872777581214905, + -0.13687947392463684, + -0.19312888383865356, + -0.5453231930732727, + -0.21912647783756256, + 0.011589044705033302, + 0.2665385603904724, + 0.3249806761741638, + 0.293254017829895, + 0.1047254130244255, + 0.4246895909309387, + -0.0033608688972890377, + 0.4066942632198334, + 0.06138676777482033, + 0.382074236869812, + 0.0787188857793808, + -0.28631800413131714, + -0.3500039279460907, + -0.1490340679883957, + -0.14991725981235504, + -0.180477574467659, + 0.15140952169895172, + -0.35168370604515076, + 0.38904908299446106, + -0.11262823641300201, + -0.18404939770698547, + 0.5045862197875977, + 0.23344825208187103, + 0.6740546226501465, + -0.054060351103544235, + -0.47260594367980957, + 0.287933886051178, + 0.28975099325180054, + 0.2366262525320053, + -0.1751112937927246, + -0.15358465909957886, + -0.062381260097026825, + 0.45881521701812744, + -0.12647950649261475, + 0.45258036255836487, + -0.21084383130073547, + -0.15994171798229218, + -0.4229416847229004, + -0.18642400205135345, + -0.2506699860095978, + 0.20604389905929565, + 0.16662882268428802, + -0.23073841631412506, + 0.045810505747795105, + 0.33520498871803284, + 0.37685254216194153, + 0.11563336104154587, + 0.22259201109409332, + -0.010484708473086357, + -0.45855188369750977, + 0.24794596433639526, + 0.33667632937431335, + 0.20378778874874115, + 0.4198003113269806, + 0.23384596407413483, + 0.23601709306240082, + -0.509751558303833, + 0.5694931149482727, + -0.08933047205209732, + 0.037133198231458664, + 0.20635388791561127, + -0.2857131361961365, + -0.4278101921081543, + -0.26602792739868164, + 0.1998632550239563, + 0.4324374794960022, + -0.13389578461647034, + 0.11837134510278702, + -0.17028754949569702, + 0.37928706407546997, + 0.10062910616397858, + -0.04736608266830444, + -0.04692180082201958, + 0.6633663773536682, + -0.3517492711544037, + 0.2055688351392746, + 0.44142597913742065, + 0.42460545897483826, + 0.4567111134529114, + 0.3061029016971588, + -0.16390416026115417, + -0.3541538417339325, + 0.2544074058532715, + -0.18162837624549866, + -0.21904821693897247, + -0.2520917057991028, + -0.07266020774841309, + -0.23432950675487518, + -0.1989256739616394, + 0.09460597485303879, + -0.24563294649124146, + 0.9719013571739197, + 0.2578149139881134, + 0.26680076122283936, + -0.39480605721473694, + 0.22382304072380066, + -0.4284250736236572, + 0.4294125437736511, + -0.04923247918486595, + 0.5011574625968933, + 0.1887599676847458, + -0.02984841726720333, + -0.16428305208683014, + -0.33957910537719727, + -0.16184143722057343, + 0.37313663959503174, + -0.11775537580251694, + -0.34507161378860474, + -0.24848994612693787, + 0.3492432236671448, + -0.2122095823287964, + -0.022055158391594887, + 0.07298140972852707, + 0.36230477690696716, + -0.2514148950576782, + 0.11675992608070374, + 0.4010731875896454, + 0.31790846586227417, + 0.0585796944797039, + 0.30878275632858276, + 0.5536429286003113, + -0.061644136905670166, + -0.06381722539663315, + -0.1873038411140442, + -0.24746698141098022, + -0.3139619529247284, + -0.19278131425380707, + -0.48264867067337036, + 0.5122742056846619, + 0.09536745399236679, + 0.17870695888996124, + 0.18145892024040222, + 0.2471739798784256, + -0.16399677097797394, + -0.18874068558216095, + 0.21305255591869354, + -0.6930050253868103, + -0.4031701982021332, + 0.5250658392906189, + 0.4295860230922699, + -0.464653879404068, + -0.026941847056150436, + -0.08213993161916733, + 0.34638163447380066, + -0.15401627123355865, + 0.021148433908820152, + 0.19726167619228363, + -0.25100240111351013, + 3.085673233726993e-05, + 0.16563303768634796, + -0.008333534933626652, + -0.02890022285282612, + -0.284770667552948, + 0.3429299592971802, + 0.6073935627937317, + -0.10915102809667587, + 0.3420248329639435, + 0.07347360253334045, + 0.18400518596172333, + 0.2084905058145523, + 0.3218590021133423, + 0.16883575916290283, + -0.6880696415901184, + -0.37455135583877563, + 0.04792584478855133, + -0.04572531208395958, + -0.17001567780971527, + -0.12369263172149658, + -0.3716808259487152, + -0.04167286679148674, + 0.04307235777378082, + -0.1655367612838745, + -0.47902533411979675, + -0.21886907517910004, + 0.4065888226032257, + 0.30626556277275085, + 0.25965678691864014, + 0.07168732583522797, + -0.17138782143592834, + -0.6293558478355408, + -0.6350710988044739, + 0.25923609733581543, + 0.5668261647224426, + -0.030662082135677338, + -0.7059182524681091, + -0.25901535153388977, + 0.25449642539024353, + -0.3232290744781494, + 0.42758384346961975, + 0.7120643258094788, + 0.023215001448988914, + -0.40807682275772095, + 0.1332295536994934, + -0.33705568313598633, + 0.1038941740989685, + 0.39904412627220154, + -0.567590057849884, + -0.26575762033462524, + 0.7635160088539124, + -0.38967835903167725, + -0.08988548815250397, + 0.4150312840938568, + -0.540441632270813, + 0.33467426896095276, + -0.03507159277796745, + 0.00720902718603611, + 0.6702240109443665, + 0.2707512676715851, + ], + b2: &[ + 0.3580038547515869, + 0.06861710548400879, + -0.04651366174221039, + 0.24638813734054565, + 0.1557426154613495, + -0.40271297097206116, + -0.405432790517807, + ], + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32/cortex_a7.txt b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32/cortex_a7.txt new file mode 100644 index 0000000..8cfc692 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32/cortex_a7.txt @@ -0,0 +1,1701 @@ +armv7neon_mmm_f32_8x4_cortexa7 16 128 8 0.000019373978862224142 +armv7neon_mmm_f32_8x6_generic 24 4 18 0.000005589467629481233 +armv7neon_mmm_f32_8x6_cortexa7 24 32 5 0.0000068541067994687505 +armv7neon_mmm_f32_8x6_cortexa7 24 128 12 0.000038981217414944424 +generic_f32_4x4 4 32 3 0.000001790047741390679 +armv7neon_mmm_f32_8x4_generic 17 32 12 0.000013250293625322834 +armv7neon_mmm_f32_8x4_cortexa7 9 4 9 0.00000392359813080775 +armv7neon_mmm_f32_8x6_generic 16 128 18 0.00003780372998360008 +armv7neon_mmm_f32_8x6_generic 7 32 7 0.000004508038841829227 +armv7neon_mmm_f32_8x4_cortexa9 25 128 4 0.00001979557034383245 +armv7neon_mmm_f32_8x4_cortexa7 24 128 5 0.00002916219359816615 +generic_f32_4x4 8 4 7 0.000002792935337190097 +armv7neon_mmm_f32_8x6_generic 25 32 5 0.000008513973930688824 +armv7neon_mmm_f32_8x4_generic 8 128 4 0.000004984706018353683 +armv7neon_mmm_f32_8x6_cortexa7 23 4 6 0.0000024972411854271217 +armv7neon_mmm_f32_8x6_cortexa9 17 32 18 0.000018034380706056615 +generic_f32_4x4 9 32 5 0.000007787097193216308 +armv7neon_mmm_f32_8x4_cortexa7 7 32 11 0.000005445588694072235 +armv7neon_mmm_f32_8x4_cortexa9 7 4 3 0.0000011467255960994079 +armv7neon_mmm_f32_8x6_cortexa7 7 128 6 0.0000071177868474168225 +armv7neon_mmm_f32_8x6_generic 15 128 5 0.000013291532044598022 +armv7neon_mmm_f32_8x6_cortexa7 25 4 18 0.000007704060547781454 +armv7neon_mmm_f32_8x4_generic 15 128 11 0.000028366457109510148 +armv7neon_mmm_f32_8x6_cortexa7 7 4 12 0.000002031241642569242 +generic_f32_4x4 12 32 9 0.000011111678190760999 +armv7neon_mmm_f32_8x6_cortexa9 24 32 17 0.000018402463967012353 +armv7neon_mmm_f32_8x4_cortexa7 8 4 12 0.0000019146048075747953 +armv7neon_mmm_f32_8x4_cortexa7 23 4 8 0.00000383750508652125 +armv7neon_mmm_f32_8x4_cortexa7 25 4 7 0.0000052043882186278224 +armv7neon_mmm_f32_8x4_generic 16 32 12 0.000008739868831484192 +generic_f32_4x4 12 4 8 0.00000347003352464419 +armv7neon_mmm_f32_8x6_cortexa7 25 32 19 0.00003152139986100901 +armv7neon_mmm_f32_8x6_cortexa9 25 32 18 0.000023769189598361993 +armv7neon_mmm_f32_8x4_cortexa7 15 128 5 0.00001982738009364039 +armv7neon_mmm_f32_8x6_generic 9 128 11 0.000025754948633914126 +armv7neon_mmm_f32_8x4_cortexa7 15 4 4 0.0000016751657126978973 +armv7neon_mmm_f32_8x4_cortexa9 24 32 8 0.000009220363478862854 +generic_f32_4x4 3 32 13 0.000005627827228507873 +armv7neon_mmm_f32_8x6_cortexa9 23 128 13 0.00006012344410992782 +armv7neon_mmm_f32_8x6_cortexa7 24 128 6 0.00001973568156590405 +armv7neon_mmm_f32_8x4_generic 17 128 8 0.000027722972081041346 +armv7neon_mmm_f32_8x4_generic 9 128 4 0.000009605985891246275 +armv7neon_mmm_f32_8x4_cortexa7 15 32 3 0.000003754036592781278 +armv7neon_mmm_f32_8x4_cortexa7 25 128 8 0.00003835708390551408 +armv7neon_mmm_f32_8x4_cortexa9 9 32 4 0.0000035373293731924376 +generic_f32_4x4 7 4 11 0.000004044788183106201 +armv7neon_mmm_f32_8x6_cortexa9 7 32 12 0.000004744792047894666 +armv7neon_mmm_f32_8x4_cortexa9 23 4 8 0.0000038749254387558 +generic_f32_4x4 9 32 8 0.0000075635927841880176 +armv7neon_mmm_f32_8x4_cortexa7 9 4 12 0.000003727778947810718 +armv7neon_mmm_f32_8x6_cortexa7 23 32 5 0.0000067956400405037536 +generic_f32_4x4 8 4 5 0.000002723326536848814 +armv7neon_mmm_f32_8x6_cortexa9 15 32 5 0.000004690163698982028 +armv7neon_mmm_f32_8x4_cortexa9 9 32 3 0.0000037317693401172363 +armv7neon_mmm_f32_8x6_cortexa7 25 128 13 0.00007831443093305969 +armv7neon_mmm_f32_8x4_cortexa7 24 32 12 0.00001331331475399187 +armv7neon_mmm_f32_8x6_cortexa7 24 128 19 0.00007802209851667727 +armv7neon_mmm_f32_8x4_generic 16 128 11 0.00002788579513287149 +armv7neon_mmm_f32_8x4_cortexa7 17 128 4 0.000014706021218529391 +armv7neon_mmm_f32_8x6_generic 8 4 13 0.0000023378593097707485 +armv7neon_mmm_f32_8x6_generic 7 4 7 0.0000019186954240278283 +armv7neon_mmm_f32_8x4_cortexa9 16 4 8 0.000002428666090877155 +armv7neon_mmm_f32_8x6_generic 24 128 13 0.00005734829975884668 +generic_f32_4x4 8 128 11 0.000021025833076703056 +armv7neon_mmm_f32_8x6_cortexa9 17 4 19 0.00000803059286526004 +armv7neon_mmm_f32_8x6_cortexa7 25 4 7 0.00000571019568400475 +armv7neon_mmm_f32_8x4_cortexa9 7 128 3 0.000005476007735837036 +generic_f32_4x4 9 32 9 0.000011314156862861827 +armv7neon_mmm_f32_8x4_cortexa9 8 32 3 0.000002175330632998311 +armv7neon_mmm_f32_8x4_cortexa7 9 128 13 0.00003871137553728846 +armv7neon_mmm_f32_8x6_cortexa7 16 32 19 0.00001594300556874623 +armv7neon_mmm_f32_8x4_cortexa7 17 4 13 0.000007095518187604073 +armv7neon_mmm_f32_8x4_generic 17 4 12 0.00000511222434537087 +armv7neon_mmm_f32_8x4_generic 23 32 3 0.000005240662819589997 +armv7neon_mmm_f32_8x6_generic 7 4 19 0.000003459698540347226 +armv7neon_mmm_f32_8x6_cortexa9 7 128 19 0.000027297846707037145 +armv7neon_mmm_f32_8x4_cortexa7 23 4 9 0.000005799914306902319 +armv7neon_mmm_f32_8x4_generic 23 4 4 0.0000021622122978664557 +armv7neon_mmm_f32_8x4_cortexa9 17 128 12 0.00004402054920694197 +armv7neon_mmm_f32_8x6_cortexa9 15 4 18 0.000004616359494672357 +armv7neon_mmm_f32_8x4_cortexa9 8 4 4 0.0000010041412858292361 +armv7neon_mmm_f32_8x4_cortexa9 8 32 13 0.000006496433067761439 +generic_f32_4x4 11 128 3 0.000011040772633263298 +armv7neon_mmm_f32_8x6_generic 24 4 11 0.000004523362892147976 +generic_f32_4x4 9 4 4 0.0000021034125372056796 +generic_f32_4x4 8 4 9 0.000003722094027081023 +generic_f32_4x4 4 4 8 0.0000014952745701485973 +armv7neon_mmm_f32_8x4_generic 16 128 3 0.000009908089584838082 +armv7neon_mmm_f32_8x4_generic 25 32 7 0.000012421086220512836 +armv7neon_mmm_f32_8x6_cortexa7 8 4 5 0.0000013403828133224992 +armv7neon_mmm_f32_8x4_cortexa7 23 32 4 0.00000503241852364444 +armv7neon_mmm_f32_8x6_cortexa9 7 32 6 0.000002618507739501653 +armv7neon_mmm_f32_8x4_cortexa7 7 4 13 0.0000031793808774197963 +armv7neon_mmm_f32_8x4_generic 16 4 7 0.00000282397574390546 +armv7neon_mmm_f32_8x4_cortexa9 17 128 4 0.00001500697741030371 +armv7neon_mmm_f32_8x6_cortexa7 24 4 6 0.000002233395192000468 +armv7neon_mmm_f32_8x6_cortexa9 15 128 6 0.0000138021942440989 +armv7neon_mmm_f32_8x4_cortexa9 15 128 11 0.000030164013207782058 +generic_f32_4x4 7 4 5 0.0000028577946255424533 +armv7neon_mmm_f32_8x4_cortexa9 16 128 5 0.000020020455876347578 +armv7neon_mmm_f32_8x6_cortexa9 16 128 6 0.000013512716817189273 +armv7neon_mmm_f32_8x4_cortexa7 16 4 11 0.0000037996360400635168 +armv7neon_mmm_f32_8x4_generic 7 32 4 0.0000021000959358733572 +armv7neon_mmm_f32_8x4_cortexa9 9 4 11 0.000004001256873073548 +armv7neon_mmm_f32_8x6_generic 24 128 7 0.00003837839936999645 +armv7neon_mmm_f32_8x4_cortexa9 15 128 4 0.00001034155567067011 +armv7neon_mmm_f32_8x4_generic 7 128 9 0.000014585897754194483 +armv7neon_mmm_f32_8x4_cortexa9 7 128 11 0.000015558039194528556 +armv7neon_mmm_f32_8x4_generic 25 32 11 0.000018056248294844393 +armv7neon_mmm_f32_8x6_cortexa7 9 32 6 0.0000044289694676171784 +armv7neon_mmm_f32_8x4_cortexa9 9 32 7 0.000006775203907440964 +armv7neon_mmm_f32_8x4_cortexa7 17 32 4 0.000004906536483038752 +armv7neon_mmm_f32_8x6_generic 25 128 13 0.00007697771420855431 +armv7neon_mmm_f32_8x6_generic 15 32 13 0.000012236156128465641 +armv7neon_mmm_f32_8x4_generic 9 32 11 0.000009363916532723868 +armv7neon_mmm_f32_8x6_cortexa9 16 32 7 0.000008475723903784643 +armv7neon_mmm_f32_8x4_cortexa9 24 4 8 0.000003400276973989579 +armv7neon_mmm_f32_8x4_generic 8 32 9 0.000004816590922050574 +armv7neon_mmm_f32_8x6_cortexa9 7 32 18 0.0000068452889719178265 +armv7neon_mmm_f32_8x4_cortexa9 9 32 5 0.000006726253331965845 +generic_f32_4x4 11 32 13 0.000015033889948226767 +armv7neon_mmm_f32_8x4_generic 17 4 11 0.000005556840437336695 +generic_f32_4x4 13 32 7 0.000010313627379766583 +generic_f32_4x4 9 4 3 0.0000023801388628702307 +armv7neon_mmm_f32_8x4_generic 8 128 3 0.00000520201390212873 +armv7neon_mmm_f32_8x4_generic 8 32 12 0.000004612565409735615 +armv7neon_mmm_f32_8x4_cortexa9 16 128 4 0.000010106142800191435 +armv7neon_mmm_f32_8x4_cortexa7 8 4 5 0.0000016424950539860917 +armv7neon_mmm_f32_8x4_generic 8 32 8 0.000003246701797046072 +armv7neon_mmm_f32_8x6_cortexa7 17 128 7 0.00003957490867985703 +armv7neon_mmm_f32_8x4_cortexa7 7 32 12 0.000005449789255433999 +armv7neon_mmm_f32_8x4_cortexa7 15 32 5 0.000006774977020301673 +armv7neon_mmm_f32_8x6_generic 7 4 13 0.0000027111208216897315 +armv7neon_mmm_f32_8x4_cortexa7 16 32 5 0.000006561637517767873 +generic_f32_4x4 3 128 9 0.000011017761032513612 +armv7neon_mmm_f32_8x4_cortexa7 23 128 5 0.00002948816149020999 +armv7neon_mmm_f32_8x4_cortexa7 17 32 11 0.00001417097516045665 +armv7neon_mmm_f32_8x6_cortexa7 16 4 12 0.000002812978829286788 +armv7neon_mmm_f32_8x6_cortexa9 23 32 5 0.0000068076361854689625 +armv7neon_mmm_f32_8x4_cortexa7 24 4 3 0.0000025622772317463136 +armv7neon_mmm_f32_8x4_cortexa9 16 32 13 0.000012465423813216206 +generic_f32_4x4 4 32 11 0.000004083209759238612 +armv7neon_mmm_f32_8x4_generic 7 32 5 0.000003626955080135103 +armv7neon_mmm_f32_8x6_cortexa7 23 128 6 0.000020078583004924116 +armv7neon_mmm_f32_8x6_cortexa9 25 128 19 0.00010584384897256878 +armv7neon_mmm_f32_8x6_generic 17 4 12 0.000004107213127611975 +armv7neon_mmm_f32_8x6_cortexa7 16 4 17 0.000004432708536915881 +armv7neon_mmm_f32_8x6_cortexa7 16 32 18 0.00001183035782221589 +armv7neon_mmm_f32_8x4_cortexa9 25 4 3 0.000003166221225598654 +armv7neon_mmm_f32_8x4_cortexa9 24 128 9 0.00004426766902863704 +armv7neon_mmm_f32_8x6_generic 25 4 18 0.000007554578118985422 +armv7neon_mmm_f32_8x6_cortexa7 15 32 5 0.000004708546886703234 +armv7neon_mmm_f32_8x6_cortexa9 9 128 6 0.000013657373073808522 +armv7neon_mmm_f32_8x6_cortexa7 25 32 7 0.00001623345709716141 +armv7neon_mmm_f32_8x4_cortexa7 9 32 5 0.000006618675517469529 +armv7neon_mmm_f32_8x4_cortexa7 7 32 13 0.000007030082349548267 +armv7neon_mmm_f32_8x4_cortexa9 17 4 7 0.0000040753817592878874 +armv7neon_mmm_f32_8x6_generic 15 4 5 0.0000019991656588201214 +generic_f32_4x4 12 128 5 0.00002108090993489815 +armv7neon_mmm_f32_8x6_cortexa9 25 128 17 0.00007984886358906369 +generic_f32_4x4 8 128 5 0.000014230862544269676 +armv7neon_mmm_f32_8x6_generic 24 128 18 0.000056743692014886694 +armv7neon_mmm_f32_8x4_cortexa9 8 32 8 0.0000034133387591211906 +armv7neon_mmm_f32_8x4_generic 9 128 12 0.000027800478110813017 +generic_f32_4x4 7 32 7 0.000005509675967097076 +armv7neon_mmm_f32_8x6_cortexa9 16 32 13 0.000012310372306220252 +armv7neon_mmm_f32_8x4_generic 15 128 13 0.00003762533200230424 +generic_f32_4x4 4 128 5 0.000007369710897637148 +armv7neon_mmm_f32_8x6_generic 16 128 6 0.000012947918354537283 +armv7neon_mmm_f32_8x4_cortexa7 8 32 3 0.000002138296476186818 +armv7neon_mmm_f32_8x4_generic 9 128 11 0.000028058363419186727 +armv7neon_mmm_f32_8x4_cortexa7 7 128 13 0.00002008308819140427 +armv7neon_mmm_f32_8x6_cortexa9 15 128 12 0.000027068411466415602 +armv7neon_mmm_f32_8x6_generic 8 128 12 0.000012940488826817833 +armv7neon_mmm_f32_8x4_generic 24 32 7 0.00000940385598649166 +armv7neon_mmm_f32_8x6_cortexa7 15 128 19 0.00005293187157460067 +armv7neon_mmm_f32_8x4_cortexa9 25 128 3 0.000020446918539621497 +armv7neon_mmm_f32_8x4_cortexa7 17 4 9 0.000005534537134563046 +armv7neon_mmm_f32_8x6_cortexa9 15 128 19 0.00005366369135659099 +armv7neon_mmm_f32_8x4_generic 25 4 4 0.000002522928321866339 +armv7neon_mmm_f32_8x6_generic 16 4 19 0.000005280876227150452 +armv7neon_mmm_f32_8x6_generic 23 128 12 0.000038470533759474454 +armv7neon_mmm_f32_8x4_generic 16 32 3 0.00000367665371967417 +armv7neon_mmm_f32_8x6_cortexa7 7 32 12 0.000004702345831209106 +armv7neon_mmm_f32_8x6_generic 16 32 11 0.000008300619599261807 +armv7neon_mmm_f32_8x4_cortexa9 23 4 5 0.000004136206632272795 +armv7neon_mmm_f32_8x4_cortexa9 23 32 7 0.000010130368574328868 +armv7neon_mmm_f32_8x4_generic 7 128 12 0.000014642209408823472 +armv7neon_mmm_f32_8x4_generic 16 4 3 0.0000018668229452058654 +armv7neon_mmm_f32_8x4_cortexa9 24 128 8 0.00002933769331717579 +armv7neon_mmm_f32_8x4_cortexa9 15 32 5 0.0000068812833008684075 +armv7neon_mmm_f32_8x6_generic 16 4 17 0.000004317183704154257 +armv7neon_mmm_f32_8x6_cortexa9 17 32 7 0.000012443561351360363 +generic_f32_4x4 13 128 7 0.000028155411441347644 +armv7neon_mmm_f32_8x6_cortexa9 25 4 12 0.000005357355009390057 +armv7neon_mmm_f32_8x6_cortexa7 9 128 18 0.00003939231793264078 +generic_f32_4x4 9 128 13 0.00004157559429631316 +armv7neon_mmm_f32_8x6_cortexa9 23 32 11 0.000012894467716591857 +armv7neon_mmm_f32_8x6_generic 17 32 19 0.000023157961942761285 +armv7neon_mmm_f32_8x6_generic 23 128 13 0.00005762004261565962 +armv7neon_mmm_f32_8x6_generic 25 32 12 0.000015419463508057784 +armv7neon_mmm_f32_8x6_generic 23 32 19 0.000023607314480173466 +generic_f32_4x4 11 4 9 0.0000056154966368798884 +armv7neon_mmm_f32_8x4_generic 23 4 3 0.000002512455070432762 +armv7neon_mmm_f32_8x6_generic 17 128 12 0.000038205761728052566 +armv7neon_mmm_f32_8x6_cortexa9 25 4 6 0.0000029441583370270357 +armv7neon_mmm_f32_8x6_cortexa7 8 128 5 0.00000718614713533821 +armv7neon_mmm_f32_8x6_cortexa9 9 32 11 0.000008580295552398777 +armv7neon_mmm_f32_8x4_generic 17 4 8 0.0000036027501506567706 +armv7neon_mmm_f32_8x4_cortexa9 24 128 12 0.000043681280972474085 +armv7neon_mmm_f32_8x6_cortexa7 23 128 19 0.00007892740320659124 +armv7neon_mmm_f32_8x4_cortexa7 7 4 7 0.000001867957586629068 +armv7neon_mmm_f32_8x4_cortexa7 17 128 3 0.000015105393976853567 +armv7neon_mmm_f32_8x6_cortexa9 16 32 5 0.000004737870196682113 +armv7neon_mmm_f32_8x6_cortexa7 17 32 11 0.000012513663992372357 +armv7neon_mmm_f32_8x4_cortexa9 8 4 13 0.000002615891904894993 +generic_f32_4x4 9 128 8 0.00002092560518618388 +armv7neon_mmm_f32_8x6_cortexa9 24 4 13 0.000006118924275544352 +armv7neon_mmm_f32_8x4_cortexa9 23 128 12 0.00004443207751479167 +armv7neon_mmm_f32_8x4_cortexa9 9 32 13 0.000012776770370453443 +armv7neon_mmm_f32_8x4_cortexa7 7 128 4 0.000005416694010439176 +generic_f32_4x4 3 32 3 0.0000017607189056083412 +generic_f32_4x4 8 32 13 0.00000985693332598433 +armv7neon_mmm_f32_8x4_generic 15 128 9 0.00002827090625785394 +armv7neon_mmm_f32_8x6_generic 17 4 5 0.0000026659420735671337 +armv7neon_mmm_f32_8x6_cortexa9 7 4 19 0.000003496314614706576 +generic_f32_4x4 9 32 12 0.00001103802239472411 +armv7neon_mmm_f32_8x6_cortexa7 24 32 19 0.00002361513173387037 +armv7neon_mmm_f32_8x6_cortexa9 24 32 13 0.00001816124851845242 +armv7neon_mmm_f32_8x6_cortexa7 24 4 19 0.000007820954390784165 +armv7neon_mmm_f32_8x4_cortexa9 16 4 9 0.0000037661558813876353 +armv7neon_mmm_f32_8x6_generic 25 4 13 0.000007941364297025098 +armv7neon_mmm_f32_8x6_cortexa7 23 128 7 0.0000397790896264826 +armv7neon_mmm_f32_8x4_cortexa7 23 32 12 0.000014064248486107495 +armv7neon_mmm_f32_8x6_cortexa9 25 32 17 0.000024451766821636896 +armv7neon_mmm_f32_8x4_generic 16 32 13 0.000011863952520137507 +armv7neon_mmm_f32_8x6_cortexa9 15 4 13 0.000004662604065748643 +armv7neon_mmm_f32_8x4_cortexa7 16 128 13 0.00003851383301865242 +armv7neon_mmm_f32_8x4_cortexa9 17 4 3 0.0000024871767077518544 +armv7neon_mmm_f32_8x6_generic 9 128 18 0.000038275747204244 +armv7neon_mmm_f32_8x4_cortexa7 23 4 7 0.000004236340851810833 +armv7neon_mmm_f32_8x4_generic 24 32 12 0.000012855667391841598 +armv7neon_mmm_f32_8x6_cortexa9 15 32 17 0.000012917112497463966 +armv7neon_mmm_f32_8x4_generic 23 128 11 0.000042177094013740186 +armv7neon_mmm_f32_8x4_cortexa7 16 128 12 0.000028624335558632763 +armv7neon_mmm_f32_8x6_cortexa7 23 4 17 0.000006797176857939227 +armv7neon_mmm_f32_8x4_generic 25 4 3 0.0000031059316600251953 +armv7neon_mmm_f32_8x6_cortexa7 23 4 19 0.000008441504854510506 +armv7neon_mmm_f32_8x6_cortexa7 23 128 17 0.00005957716872417335 +armv7neon_mmm_f32_8x4_generic 24 32 3 0.000005255472766158138 +armv7neon_mmm_f32_8x6_cortexa7 7 32 6 0.0000025969460967210575 +generic_f32_4x4 12 128 9 0.00003118081783563989 +armv7neon_mmm_f32_8x6_cortexa7 24 32 7 0.000012316039376583595 +armv7neon_mmm_f32_8x4_cortexa9 9 128 11 0.00002984202658897139 +armv7neon_mmm_f32_8x6_generic 24 4 12 0.00000390465698063092 +armv7neon_mmm_f32_8x6_generic 24 128 5 0.000019825223712031248 +generic_f32_4x4 11 128 12 0.00003129955987835105 +armv7neon_mmm_f32_8x4_generic 15 32 8 0.000006474371340367591 +armv7neon_mmm_f32_8x6_cortexa7 25 128 17 0.00007864370121172857 +armv7neon_mmm_f32_8x4_cortexa7 7 4 9 0.000002506847196975368 +armv7neon_mmm_f32_8x6_cortexa9 25 4 11 0.000005933707367961421 +armv7neon_mmm_f32_8x4_cortexa9 16 4 5 0.000002787654264318038 +armv7neon_mmm_f32_8x6_generic 17 32 13 0.00001761805014832438 +armv7neon_mmm_f32_8x4_cortexa9 7 32 4 0.0000021905945506812955 +armv7neon_mmm_f32_8x6_cortexa9 17 128 11 0.00004027130474721357 +generic_f32_4x4 7 32 9 0.000007912119634032119 +armv7neon_mmm_f32_8x4_generic 23 4 13 0.0000073828945636745854 +generic_f32_4x4 12 128 11 0.00003129061011102201 +armv7neon_mmm_f32_8x6_cortexa7 9 4 5 0.0000019755012039429016 +armv7neon_mmm_f32_8x6_cortexa9 8 4 13 0.000002405709930798169 +armv7neon_mmm_f32_8x6_cortexa9 24 4 7 0.0000044131675061571695 +armv7neon_mmm_f32_8x4_cortexa7 25 128 3 0.00002004605546076412 +armv7neon_mmm_f32_8x6_cortexa7 25 32 11 0.000016504451247846665 +generic_f32_4x4 11 4 3 0.0000024035209703323816 +generic_f32_4x4 5 128 9 0.00002116685987655445 +armv7neon_mmm_f32_8x4_generic 9 32 9 0.000009310383786805071 +armv7neon_mmm_f32_8x6_cortexa7 15 32 12 0.00000858295982977344 +generic_f32_4x4 9 4 9 0.0000055122591860013075 +armv7neon_mmm_f32_8x4_cortexa7 24 128 4 0.000014582241975346975 +armv7neon_mmm_f32_8x6_cortexa7 7 32 11 0.0000047102983594351624 +armv7neon_mmm_f32_8x6_cortexa7 17 32 5 0.000006703902999940797 +armv7neon_mmm_f32_8x4_cortexa9 9 32 12 0.000009569569775041666 +generic_f32_4x4 11 32 4 0.000004102122527868448 +armv7neon_mmm_f32_8x4_cortexa7 23 128 11 0.00004393294052475175 +armv7neon_mmm_f32_8x6_cortexa7 17 128 18 0.00005867315609945908 +armv7neon_mmm_f32_8x6_generic 15 4 19 0.000005888722731281036 +armv7neon_mmm_f32_8x4_cortexa9 25 128 12 0.000058416478283637004 +armv7neon_mmm_f32_8x6_cortexa7 15 128 18 0.00003981402002143062 +armv7neon_mmm_f32_8x6_generic 17 32 11 0.000012156545175144957 +armv7neon_mmm_f32_8x6_cortexa9 17 128 5 0.000020505815042923473 +armv7neon_mmm_f32_8x4_generic 23 4 8 0.0000037989035755978144 +armv7neon_mmm_f32_8x4_cortexa9 23 128 4 0.000015133679307570648 +generic_f32_4x4 12 32 3 0.000004382513702192395 +generic_f32_4x4 12 4 5 0.000003824564176783666 +generic_f32_4x4 5 32 7 0.000005434572832921092 +generic_f32_4x4 3 4 8 0.0000017566767353451639 +armv7neon_mmm_f32_8x6_cortexa7 7 32 13 0.0000067229229930212575 +armv7neon_mmm_f32_8x4_generic 8 4 13 0.0000025649388423337978 +generic_f32_4x4 11 32 12 0.000011236073634740423 +armv7neon_mmm_f32_8x6_generic 25 4 17 0.000008196880964751204 +armv7neon_mmm_f32_8x6_cortexa9 7 32 11 0.000004753650782271882 +armv7neon_mmm_f32_8x4_cortexa9 7 128 4 0.000005529569938400554 +generic_f32_4x4 13 4 13 0.000009131495505295274 +armv7neon_mmm_f32_8x6_cortexa9 9 4 7 0.00000312421222650566 +armv7neon_mmm_f32_8x6_generic 17 32 7 0.000011970222410677647 +armv7neon_mmm_f32_8x4_cortexa7 7 4 3 0.0000011523161892903704 +armv7neon_mmm_f32_8x6_cortexa7 23 128 18 0.00005908293678660039 +armv7neon_mmm_f32_8x4_cortexa9 23 4 13 0.000007508665249371487 +armv7neon_mmm_f32_8x4_cortexa9 7 32 8 0.000003875838102020609 +armv7neon_mmm_f32_8x6_cortexa7 24 32 11 0.00001256332295885272 +armv7neon_mmm_f32_8x6_cortexa9 23 128 6 0.000020319056425196668 +armv7neon_mmm_f32_8x4_generic 23 32 4 0.00000488125169723422 +armv7neon_mmm_f32_8x6_generic 24 32 18 0.00001705594140661088 +armv7neon_mmm_f32_8x4_generic 7 4 3 0.000001143118878212571 +armv7neon_mmm_f32_8x4_generic 24 4 3 0.000002536821660220204 +armv7neon_mmm_f32_8x6_cortexa9 7 4 17 0.000002840883149941229 +armv7neon_mmm_f32_8x6_cortexa7 17 32 13 0.000018113836070828896 +armv7neon_mmm_f32_8x6_cortexa7 24 4 5 0.000002893802636827323 +armv7neon_mmm_f32_8x4_cortexa9 7 128 8 0.00001055308814900546 +armv7neon_mmm_f32_8x4_cortexa9 15 4 4 0.0000017017415253459248 +armv7neon_mmm_f32_8x6_generic 9 128 17 0.00003847119775045202 +armv7neon_mmm_f32_8x4_cortexa9 17 128 7 0.00003001254835210308 +generic_f32_4x4 7 128 3 0.0000075145364744355526 +armv7neon_mmm_f32_8x6_cortexa9 24 128 13 0.00005963153865876477 +armv7neon_mmm_f32_8x6_generic 16 4 11 0.0000031955847464919697 +armv7neon_mmm_f32_8x6_generic 24 128 17 0.000057273707239946334 +armv7neon_mmm_f32_8x6_cortexa9 16 128 17 0.00004016422558583101 +armv7neon_mmm_f32_8x4_generic 24 4 7 0.000003971324732226714 +armv7neon_mmm_f32_8x6_cortexa9 25 32 11 0.0000166679830493271 +armv7neon_mmm_f32_8x4_generic 7 128 3 0.000005169798410902207 +armv7neon_mmm_f32_8x6_cortexa7 15 4 11 0.000003482445418673263 +armv7neon_mmm_f32_8x6_cortexa7 8 128 6 0.0000069109869984566186 +armv7neon_mmm_f32_8x4_cortexa9 24 32 13 0.000018445950950001155 +armv7neon_mmm_f32_8x6_generic 8 4 11 0.0000018570966900607452 +armv7neon_mmm_f32_8x6_cortexa9 24 4 6 0.000002228168146536169 +armv7neon_mmm_f32_8x6_generic 8 128 7 0.000013146479087947972 +armv7neon_mmm_f32_8x6_cortexa9 25 32 12 0.000016067383961246167 +armv7neon_mmm_f32_8x4_cortexa9 16 32 5 0.000006662778599544951 +armv7neon_mmm_f32_8x6_generic 9 4 11 0.0000031451298378122295 +armv7neon_mmm_f32_8x4_generic 16 32 7 0.000006438918321306648 +armv7neon_mmm_f32_8x6_cortexa9 7 32 7 0.000004657116192635584 +armv7neon_mmm_f32_8x6_generic 17 32 12 0.00001176030032709091 +armv7neon_mmm_f32_8x4_generic 15 4 8 0.0000028163110410039656 +armv7neon_mmm_f32_8x4_cortexa9 7 4 9 0.0000025260788973778824 +generic_f32_4x4 8 4 11 0.000003795516523798154 +armv7neon_mmm_f32_8x6_generic 15 32 6 0.0000044168833936544535 +armv7neon_mmm_f32_8x4_cortexa7 8 128 9 0.000014761720958613642 +armv7neon_mmm_f32_8x6_cortexa7 9 32 7 0.000008410754850495816 +armv7neon_mmm_f32_8x4_cortexa7 16 4 13 0.000004684953060253782 +armv7neon_mmm_f32_8x4_generic 16 32 5 0.000006354175934259964 +armv7neon_mmm_f32_8x4_generic 24 4 4 0.0000019148396982311507 +armv7neon_mmm_f32_8x4_generic 17 128 4 0.000014107689106847269 +armv7neon_mmm_f32_8x6_cortexa9 24 32 11 0.000012681731796133347 +generic_f32_4x4 3 4 5 0.0000017351550450721446 +armv7neon_mmm_f32_8x4_cortexa7 16 4 9 0.0000037512996244278985 +armv7neon_mmm_f32_8x6_cortexa9 23 32 13 0.000018600736039744996 +generic_f32_4x4 7 32 13 0.000010363567629711427 +generic_f32_4x4 7 128 13 0.000028209372228861907 +armv7neon_mmm_f32_8x6_generic 24 32 13 0.000017489604886914003 +armv7neon_mmm_f32_8x4_cortexa7 24 32 8 0.00000906737294441132 +armv7neon_mmm_f32_8x6_generic 15 32 5 0.000004544284306242495 +armv7neon_mmm_f32_8x6_cortexa9 16 128 19 0.00005301309753082383 +armv7neon_mmm_f32_8x6_cortexa7 15 128 11 0.000026895954754680612 +armv7neon_mmm_f32_8x4_cortexa9 15 32 12 0.000009927381424295026 +armv7neon_mmm_f32_8x4_cortexa9 16 128 12 0.00002922630339731252 +armv7neon_mmm_f32_8x4_cortexa9 24 4 11 0.000005481062579336857 +armv7neon_mmm_f32_8x6_generic 7 128 18 0.000019821467429857938 +armv7neon_mmm_f32_8x6_cortexa9 7 32 5 0.000002575926161215081 +armv7neon_mmm_f32_8x6_cortexa7 15 32 7 0.000008595071585263579 +armv7neon_mmm_f32_8x4_cortexa9 17 32 4 0.000005001583148868544 +armv7neon_mmm_f32_8x4_cortexa7 25 32 4 0.000006334740769256929 +armv7neon_mmm_f32_8x4_cortexa7 25 128 7 0.00003905527355458101 +generic_f32_4x4 4 4 5 0.0000016159570864189527 +armv7neon_mmm_f32_8x4_cortexa9 17 128 11 0.000044522329607090446 +armv7neon_mmm_f32_8x6_cortexa9 8 128 18 0.0000200309695734825 +armv7neon_mmm_f32_8x6_generic 24 32 19 0.000022973011330361666 +generic_f32_4x4 3 4 13 0.000002984204583865987 +armv7neon_mmm_f32_8x6_cortexa9 15 4 5 0.000002015871636210361 +generic_f32_4x4 7 4 9 0.000003999946420645267 +armv7neon_mmm_f32_8x4_generic 7 4 9 0.0000024834088306212943 +armv7neon_mmm_f32_8x4_cortexa7 25 128 9 0.00005782975703195741 +armv7neon_mmm_f32_8x4_generic 25 32 8 0.000011779346411467398 +armv7neon_mmm_f32_8x6_cortexa9 25 32 7 0.000016399753838712434 +armv7neon_mmm_f32_8x6_generic 24 4 6 0.000002204607070591687 +armv7neon_mmm_f32_8x6_cortexa7 9 32 17 0.000012402833540807695 +armv7neon_mmm_f32_8x4_cortexa7 25 128 13 0.00007672799293438395 +armv7neon_mmm_f32_8x6_generic 24 128 12 0.0000379228420372398 +armv7neon_mmm_f32_8x4_cortexa7 16 128 3 0.00001030544963732427 +armv7neon_mmm_f32_8x4_generic 15 128 3 0.000009885955298583161 +armv7neon_mmm_f32_8x4_generic 15 4 12 0.0000039769457516235065 +armv7neon_mmm_f32_8x6_generic 23 4 11 0.000004727342544050073 +armv7neon_mmm_f32_8x6_cortexa7 15 4 12 0.0000032858094110604857 +armv7neon_mmm_f32_8x4_cortexa7 16 4 3 0.0000018824484779381146 +armv7neon_mmm_f32_8x6_cortexa9 23 4 7 0.000004580846920018228 +generic_f32_4x4 11 4 8 0.00000379091798312253 +armv7neon_mmm_f32_8x6_cortexa9 23 32 18 0.00001843667314281391 +armv7neon_mmm_f32_8x6_generic 8 32 18 0.0000060026095209057905 +armv7neon_mmm_f32_8x4_cortexa7 8 32 7 0.00000356603711659208 +armv7neon_mmm_f32_8x6_generic 15 128 18 0.00003865113024109526 +armv7neon_mmm_f32_8x6_generic 7 32 5 0.0000025012501670870884 +generic_f32_4x4 12 128 3 0.00001108291043110956 +armv7neon_mmm_f32_8x6_cortexa9 9 128 17 0.00004019255842003589 +armv7neon_mmm_f32_8x4_cortexa9 15 128 3 0.000010489952376847188 +armv7neon_mmm_f32_8x4_cortexa9 7 4 5 0.0000018283099982388463 +armv7neon_mmm_f32_8x6_cortexa7 23 32 13 0.000018410568030938504 +armv7neon_mmm_f32_8x6_generic 15 4 17 0.000004759242853957918 +armv7neon_mmm_f32_8x6_cortexa7 17 128 5 0.00002027503703993735 +armv7neon_mmm_f32_8x4_generic 7 4 12 0.000002526741586689988 +armv7neon_mmm_f32_8x6_cortexa9 17 4 13 0.000006256702206265106 +armv7neon_mmm_f32_8x6_cortexa7 16 128 13 0.000039449772577428465 +generic_f32_4x4 5 32 4 0.0000028896370502300425 +armv7neon_mmm_f32_8x6_cortexa9 23 4 6 0.000002496256395769302 +armv7neon_mmm_f32_8x4_generic 17 4 9 0.000005469161034223508 +armv7neon_mmm_f32_8x4_generic 17 4 13 0.0000070099392970628585 +armv7neon_mmm_f32_8x6_generic 24 4 17 0.000006212264617752923 +armv7neon_mmm_f32_8x6_cortexa7 23 32 18 0.000018244504523002484 +armv7neon_mmm_f32_8x4_cortexa7 17 32 3 0.000005314636591625876 +armv7neon_mmm_f32_8x6_generic 23 4 13 0.00000640173388890707 +armv7neon_mmm_f32_8x6_cortexa7 25 4 19 0.00001046601238496823 +armv7neon_mmm_f32_8x4_cortexa7 9 128 5 0.000019669882407282677 +generic_f32_4x4 5 4 3 0.0000017330186385522962 +armv7neon_mmm_f32_8x6_generic 9 4 13 0.000004297017223755962 +armv7neon_mmm_f32_8x4_cortexa9 9 128 7 0.0000201239354954616 +armv7neon_mmm_f32_8x4_cortexa7 8 4 7 0.0000016739241733780589 +armv7neon_mmm_f32_8x6_cortexa7 7 4 6 0.000001271018362845475 +generic_f32_4x4 13 32 9 0.000014839920846066823 +armv7neon_mmm_f32_8x4_generic 25 4 9 0.000007058701573468786 +armv7neon_mmm_f32_8x4_cortexa7 7 32 5 0.000003729557835063842 +armv7neon_mmm_f32_8x6_cortexa9 7 128 5 0.000007173464418699434 +armv7neon_mmm_f32_8x6_cortexa7 24 128 13 0.00005881823134405547 +armv7neon_mmm_f32_8x6_cortexa9 17 32 12 0.000012231167345130436 +armv7neon_mmm_f32_8x4_cortexa7 8 4 4 0.0000009861752945826601 +armv7neon_mmm_f32_8x4_cortexa9 23 128 3 0.00001548775471015623 +armv7neon_mmm_f32_8x4_generic 8 128 9 0.000014166892351007583 +armv7neon_mmm_f32_8x4_cortexa9 25 32 3 0.000007055926590582058 +armv7neon_mmm_f32_8x6_cortexa7 8 32 6 0.0000023905642959740767 +armv7neon_mmm_f32_8x4_generic 24 4 12 0.000004705581936932163 +generic_f32_4x4 13 32 13 0.000019466458499229495 +armv7neon_mmm_f32_8x6_cortexa7 8 128 12 0.000013354945269855691 +generic_f32_4x4 4 32 8 0.000002784564616216757 +generic_f32_4x4 11 4 13 0.000007235980721358389 +armv7neon_mmm_f32_8x4_cortexa9 15 4 9 0.000004211132231298468 +armv7neon_mmm_f32_8x4_generic 23 4 7 0.000004197975758631663 +armv7neon_mmm_f32_8x4_generic 24 4 13 0.000006669346538195287 +generic_f32_4x4 9 4 11 0.000005605492408222339 +armv7neon_mmm_f32_8x4_cortexa9 7 4 7 0.0000018866714926916992 +armv7neon_mmm_f32_8x6_generic 7 4 12 0.00000203404671528183 +armv7neon_mmm_f32_8x6_cortexa9 16 32 12 0.000008156581758904169 +armv7neon_mmm_f32_8x6_generic 9 4 19 0.000005498641044027106 +generic_f32_4x4 7 4 8 0.000002762174951002869 +armv7neon_mmm_f32_8x4_cortexa9 16 4 3 0.0000019059695727093323 +armv7neon_mmm_f32_8x4_generic 25 4 12 0.00000651099979096713 +armv7neon_mmm_f32_8x6_cortexa7 24 128 5 0.000020414031398736494 +armv7neon_mmm_f32_8x6_cortexa7 7 128 18 0.000020361101695155362 +generic_f32_4x4 11 128 5 0.00002125700072345171 +armv7neon_mmm_f32_8x6_cortexa7 17 32 18 0.000017837908064150246 +armv7neon_mmm_f32_8x6_cortexa7 25 32 13 0.00002390699968855355 +armv7neon_mmm_f32_8x4_generic 23 4 5 0.0000040641271507242515 +generic_f32_4x4 8 4 3 0.0000017865876036353628 +armv7neon_mmm_f32_8x6_cortexa9 16 4 17 0.00000442887972122732 +armv7neon_mmm_f32_8x4_cortexa9 25 32 8 0.000012384930380190053 +generic_f32_4x4 5 4 5 0.000002798692518373374 +armv7neon_mmm_f32_8x6_cortexa9 8 128 6 0.0000070012080134920035 +armv7neon_mmm_f32_8x6_cortexa7 17 32 6 0.000006327458516647519 +armv7neon_mmm_f32_8x4_cortexa7 24 32 7 0.0000097071140215999 +armv7neon_mmm_f32_8x6_generic 23 32 12 0.000012023256652212629 +armv7neon_mmm_f32_8x6_generic 9 32 11 0.000008252674922417165 +armv7neon_mmm_f32_8x6_generic 9 32 6 0.000004271117675286997 +armv7neon_mmm_f32_8x4_cortexa7 7 4 4 0.0000011809046933853917 +armv7neon_mmm_f32_8x6_cortexa7 15 32 19 0.00001661331304395181 +armv7neon_mmm_f32_8x4_generic 25 128 5 0.00003728262011412803 +armv7neon_mmm_f32_8x6_cortexa9 17 128 19 0.00007942199229108003 +generic_f32_4x4 11 128 9 0.000031521930244918895 +armv7neon_mmm_f32_8x6_cortexa7 25 4 6 0.000002951526384640025 +armv7neon_mmm_f32_8x6_generic 25 128 18 0.00007559930226918245 +armv7neon_mmm_f32_8x6_generic 24 32 7 0.000011956301096424158 +armv7neon_mmm_f32_8x4_cortexa7 24 4 4 0.000001942280219822285 +armv7neon_mmm_f32_8x4_cortexa7 16 4 5 0.000002773541103005484 +generic_f32_4x4 13 4 7 0.000005120137461912069 +armv7neon_mmm_f32_8x4_generic 9 4 9 0.000003880248068324046 +armv7neon_mmm_f32_8x4_cortexa9 8 4 5 0.0000016579504301247745 +armv7neon_mmm_f32_8x6_cortexa7 7 32 18 0.000006779881862785529 +armv7neon_mmm_f32_8x4_generic 24 32 9 0.000013438884904462624 +armv7neon_mmm_f32_8x4_generic 17 32 11 0.000013721051068287207 +armv7neon_mmm_f32_8x4_cortexa7 23 32 3 0.000005392987682793705 +armv7neon_mmm_f32_8x6_cortexa9 17 4 18 0.000005996181221285945 +armv7neon_mmm_f32_8x4_generic 23 128 12 0.000041706025206470595 +armv7neon_mmm_f32_8x4_generic 25 128 7 0.00003743774855697319 +generic_f32_4x4 3 4 12 0.000002379029629256848 +armv7neon_mmm_f32_8x6_cortexa9 24 128 18 0.00005914765901143539 +armv7neon_mmm_f32_8x4_cortexa7 23 32 5 0.00000981049054917298 +armv7neon_mmm_f32_8x6_cortexa7 16 4 19 0.000005412079670267658 +armv7neon_mmm_f32_8x4_cortexa7 8 32 4 0.0000019274117652778804 +armv7neon_mmm_f32_8x6_generic 17 128 6 0.000019306808921524692 +armv7neon_mmm_f32_8x6_cortexa7 7 128 12 0.000013761467144605992 +armv7neon_mmm_f32_8x6_cortexa7 16 32 7 0.000008404964853562951 +armv7neon_mmm_f32_8x6_cortexa9 9 32 7 0.000008475033766481017 +armv7neon_mmm_f32_8x6_cortexa9 15 128 5 0.000013894752583522413 +armv7neon_mmm_f32_8x6_generic 16 128 17 0.000038424998460484644 +armv7neon_mmm_f32_8x4_cortexa7 8 32 9 0.00000497069436204451 +armv7neon_mmm_f32_8x6_generic 9 32 5 0.000004444687551155496 +armv7neon_mmm_f32_8x4_cortexa7 24 32 5 0.00000958372113096483 +armv7neon_mmm_f32_8x6_cortexa9 24 4 12 0.000003962114122866607 +armv7neon_mmm_f32_8x4_generic 15 4 11 0.000004231967658632821 +armv7neon_mmm_f32_8x6_cortexa7 24 32 13 0.000017973816622821935 +armv7neon_mmm_f32_8x4_cortexa9 23 32 13 0.000019157368846217136 +armv7neon_mmm_f32_8x4_generic 16 32 9 0.00000912682021206548 +armv7neon_mmm_f32_8x6_generic 16 32 5 0.000004588157612379825 +armv7neon_mmm_f32_8x6_generic 16 4 13 0.0000041677198842138695 +armv7neon_mmm_f32_8x6_generic 15 128 17 0.00003886891043603058 +armv7neon_mmm_f32_8x6_cortexa9 7 128 11 0.000013961383587786538 +armv7neon_mmm_f32_8x4_generic 7 128 5 0.000009860600106626588 +armv7neon_mmm_f32_8x4_cortexa9 16 4 12 0.0000033867538649589902 +armv7neon_mmm_f32_8x6_cortexa9 17 32 19 0.000024087066037173788 +armv7neon_mmm_f32_8x6_generic 9 128 5 0.000013189504068813325 +armv7neon_mmm_f32_8x6_cortexa9 8 32 18 0.000006222642744442069 +armv7neon_mmm_f32_8x6_cortexa7 24 32 12 0.000011846425063097506 +generic_f32_4x4 5 4 13 0.000004993325421206121 +armv7neon_mmm_f32_8x6_generic 8 128 11 0.000013157428320463883 +generic_f32_4x4 7 32 4 0.0000029484328830194284 +armv7neon_mmm_f32_8x4_cortexa9 7 32 9 0.000005475212517453128 +armv7neon_mmm_f32_8x4_cortexa9 9 128 13 0.00003949164898061412 +armv7neon_mmm_f32_8x4_cortexa9 23 4 11 0.000005947124934955378 +armv7neon_mmm_f32_8x6_generic 23 32 11 0.00001239533186039336 +armv7neon_mmm_f32_8x6_generic 7 32 17 0.000006661670995420206 +armv7neon_mmm_f32_8x4_cortexa7 7 4 8 0.0000018776318580783529 +armv7neon_mmm_f32_8x6_generic 8 32 5 0.0000025552685069830137 +armv7neon_mmm_f32_8x4_cortexa9 9 32 9 0.000009774693013502346 +generic_f32_4x4 13 128 5 0.000027995490696525238 +armv7neon_mmm_f32_8x4_cortexa9 8 4 11 0.0000021722237770380927 +armv7neon_mmm_f32_8x4_generic 23 128 9 0.0000419391925884448 +armv7neon_mmm_f32_8x4_cortexa9 25 32 7 0.000013031732285626503 +armv7neon_mmm_f32_8x4_cortexa7 25 32 12 0.000017966835523846677 +armv7neon_mmm_f32_8x4_cortexa7 16 32 11 0.000009494335431932482 +armv7neon_mmm_f32_8x6_cortexa7 17 128 19 0.0000783675036460944 +armv7neon_mmm_f32_8x6_cortexa7 17 4 7 0.0000044328101817797105 +generic_f32_4x4 3 32 8 0.000003088179719444515 +generic_f32_4x4 12 4 11 0.000005408469300584859 +armv7neon_mmm_f32_8x4_cortexa7 23 32 13 0.000018862031131309997 +armv7neon_mmm_f32_8x4_generic 23 32 11 0.000014024445889402156 +generic_f32_4x4 4 128 11 0.00001075990813733738 +armv7neon_mmm_f32_8x6_generic 24 4 7 0.000004301779173383875 +armv7neon_mmm_f32_8x6_cortexa9 25 128 12 0.000052850505948183045 +armv7neon_mmm_f32_8x4_generic 15 32 4 0.0000034946744383909553 +armv7neon_mmm_f32_8x4_generic 17 4 3 0.0000024363476636608272 +armv7neon_mmm_f32_8x4_cortexa7 16 128 4 0.000009891301569266948 +armv7neon_mmm_f32_8x6_cortexa9 25 4 13 0.000008105640345047328 +armv7neon_mmm_f32_8x4_cortexa9 16 4 4 0.000001487225488304516 +generic_f32_4x4 5 32 9 0.000007777979342543209 +armv7neon_mmm_f32_8x6_cortexa7 8 4 19 0.0000029885727357209406 +generic_f32_4x4 8 32 9 0.000007585061806416855 +armv7neon_mmm_f32_8x4_cortexa7 17 128 13 0.000057643034032365925 +armv7neon_mmm_f32_8x4_generic 7 128 4 0.000005216038661083481 +armv7neon_mmm_f32_8x4_cortexa7 16 32 12 0.000009049743188132303 +armv7neon_mmm_f32_8x4_cortexa9 17 4 8 0.000003682177568678712 +armv7neon_mmm_f32_8x4_cortexa9 17 32 11 0.000014400917572892256 +armv7neon_mmm_f32_8x4_cortexa9 15 4 12 0.0000040529055101759995 +armv7neon_mmm_f32_8x4_cortexa7 15 4 11 0.000004271052070252677 +armv7neon_mmm_f32_8x6_cortexa7 16 128 5 0.000013791269931666714 +armv7neon_mmm_f32_8x6_generic 8 32 7 0.000004325189484240455 +generic_f32_4x4 3 128 7 0.000007522472627920054 +armv7neon_mmm_f32_8x6_cortexa9 9 32 6 0.000004463650285453612 +generic_f32_4x4 11 32 7 0.000007948963253145964 +armv7neon_mmm_f32_8x4_cortexa9 24 128 4 0.000014890215129529216 +armv7neon_mmm_f32_8x6_cortexa9 16 4 19 0.000005414025318591343 +armv7neon_mmm_f32_8x6_cortexa7 17 4 6 0.00000238128254631235 +armv7neon_mmm_f32_8x4_cortexa9 24 4 5 0.000003916194410369112 +armv7neon_mmm_f32_8x4_cortexa9 8 128 9 0.000015067666888020707 +armv7neon_mmm_f32_8x6_cortexa7 23 32 11 0.000012775953874196187 +generic_f32_4x4 8 32 3 0.0000030910360857362123 +armv7neon_mmm_f32_8x4_cortexa7 7 32 3 0.0000021042104056045423 +armv7neon_mmm_f32_8x6_cortexa9 25 32 5 0.000008813546121201035 +armv7neon_mmm_f32_8x6_cortexa7 25 4 17 0.000008369215065652083 +generic_f32_4x4 3 32 9 0.000004340286298208673 +armv7neon_mmm_f32_8x4_generic 17 4 5 0.000003911088956924729 +generic_f32_4x4 9 4 13 0.000007067880206932553 +armv7neon_mmm_f32_8x4_cortexa7 8 128 13 0.00001944164572907436 +armv7neon_mmm_f32_8x4_cortexa7 25 4 8 0.000004598061889383109 +armv7neon_mmm_f32_8x4_generic 15 32 5 0.000006570904917754489 +armv7neon_mmm_f32_8x4_cortexa9 7 4 4 0.000001199774667010064 +armv7neon_mmm_f32_8x4_cortexa9 16 32 9 0.000009580069903775671 +armv7neon_mmm_f32_8x6_generic 17 4 19 0.000007879961865942272 +armv7neon_mmm_f32_8x4_cortexa7 23 128 13 0.00005805408197282095 +armv7neon_mmm_f32_8x6_generic 25 32 13 0.000023213828889712974 +armv7neon_mmm_f32_8x6_cortexa9 23 4 17 0.000006797095061554012 +armv7neon_mmm_f32_8x6_cortexa9 24 32 6 0.000006232031189380453 +armv7neon_mmm_f32_8x4_cortexa7 15 128 11 0.00002955574134898906 +armv7neon_mmm_f32_8x6_cortexa9 24 4 11 0.000004626561901203258 +armv7neon_mmm_f32_8x6_cortexa9 8 4 7 0.0000018344586562526734 +armv7neon_mmm_f32_8x6_generic 15 4 18 0.000004518763751153566 +armv7neon_mmm_f32_8x6_cortexa9 16 128 11 0.000027030003785723685 +armv7neon_mmm_f32_8x4_cortexa7 9 4 4 0.0000015888326511808313 +armv7neon_mmm_f32_8x4_cortexa7 7 4 12 0.0000025479152659232096 +armv7neon_mmm_f32_8x4_cortexa7 23 128 7 0.000029563915293732792 +armv7neon_mmm_f32_8x4_cortexa9 25 4 13 0.000009216194666264275 +generic_f32_4x4 4 4 7 0.0000016464048874437295 +armv7neon_mmm_f32_8x4_cortexa7 7 128 11 0.00001525304959159917 +armv7neon_mmm_f32_8x4_cortexa9 9 4 9 0.000003956900236836743 +generic_f32_4x4 4 128 8 0.000007233357504684728 +armv7neon_mmm_f32_8x4_cortexa9 16 128 9 0.000029612198853914824 +armv7neon_mmm_f32_8x4_generic 15 128 12 0.000028154522908512787 +armv7neon_mmm_f32_8x6_cortexa9 8 128 5 0.000007227187608771384 +armv7neon_mmm_f32_8x4_cortexa9 25 128 7 0.000039766026339765396 +generic_f32_4x4 9 128 5 0.00002114949804637606 +armv7neon_mmm_f32_8x4_generic 17 32 4 0.000004758199768605913 +armv7neon_mmm_f32_8x6_cortexa9 7 4 13 0.0000027386176506007003 +generic_f32_4x4 7 32 8 0.000005375358081458721 +armv7neon_mmm_f32_8x6_cortexa9 8 4 18 0.000002220900812833037 +armv7neon_mmm_f32_8x4_cortexa9 16 128 11 0.000029669482734966696 +armv7neon_mmm_f32_8x6_cortexa7 8 32 18 0.000006161276860676508 +armv7neon_mmm_f32_8x4_cortexa7 8 32 8 0.000003353396080834466 +armv7neon_mmm_f32_8x6_generic 16 128 13 0.000038174416411990654 +armv7neon_mmm_f32_8x6_generic 15 32 19 0.00001612698422472605 +armv7neon_mmm_f32_8x4_cortexa9 9 128 3 0.000010402587362113396 +armv7neon_mmm_f32_8x6_cortexa9 9 4 11 0.0000032196424297717835 +armv7neon_mmm_f32_8x4_cortexa9 8 128 11 0.000015086238127499093 +armv7neon_mmm_f32_8x6_generic 16 128 19 0.00005061640609152132 +armv7neon_mmm_f32_8x4_generic 16 128 9 0.00002782092751958622 +generic_f32_4x4 4 32 4 0.000001643238458184986 +armv7neon_mmm_f32_8x6_cortexa7 17 4 11 0.0000046002558984855774 +generic_f32_4x4 5 4 9 0.0000039003390490700075 +generic_f32_4x4 9 128 7 0.000021241983402647673 +generic_f32_4x4 7 4 13 0.000005155057897108874 +armv7neon_mmm_f32_8x6_generic 17 4 17 0.000006303527455540143 +armv7neon_mmm_f32_8x4_generic 16 4 13 0.000004620655887935485 +armv7neon_mmm_f32_8x4_cortexa7 8 128 5 0.000010060854723538398 +armv7neon_mmm_f32_8x4_cortexa9 16 32 11 0.000009653502461801779 +armv7neon_mmm_f32_8x6_generic 23 128 5 0.00001973212006224306 +armv7neon_mmm_f32_8x4_generic 8 4 7 0.000001657225281211141 +armv7neon_mmm_f32_8x6_generic 16 4 12 0.0000027740993216529004 +armv7neon_mmm_f32_8x6_cortexa9 15 128 18 0.00004029185998372536 +armv7neon_mmm_f32_8x4_cortexa9 25 32 4 0.000006456592320044242 +armv7neon_mmm_f32_8x6_cortexa7 25 128 12 0.000052118173447989496 +armv7neon_mmm_f32_8x4_cortexa7 25 32 9 0.000018537854346104505 +armv7neon_mmm_f32_8x6_cortexa7 16 4 7 0.000003147284440858119 +armv7neon_mmm_f32_8x4_generic 17 128 5 0.000028048493049013062 +armv7neon_mmm_f32_8x6_cortexa7 23 4 18 0.000006335447404352516 +armv7neon_mmm_f32_8x6_cortexa9 24 4 19 0.000007813810057423332 +armv7neon_mmm_f32_8x4_cortexa7 17 128 8 0.000028915520714406696 +armv7neon_mmm_f32_8x4_cortexa7 17 128 7 0.000029363768223094683 +generic_f32_4x4 3 32 11 0.0000043748460947431475 +armv7neon_mmm_f32_8x4_cortexa9 9 128 5 0.0000200735894165016 +armv7neon_mmm_f32_8x4_cortexa7 17 4 3 0.000002458111098809617 +generic_f32_4x4 5 128 11 0.0000211987993849004 +generic_f32_4x4 12 32 7 0.00000782487986470221 +armv7neon_mmm_f32_8x4_generic 7 128 11 0.000014659497491826627 +armv7neon_mmm_f32_8x6_cortexa7 17 128 11 0.00003969667780371537 +armv7neon_mmm_f32_8x6_generic 7 4 6 0.0000012612776856940052 +armv7neon_mmm_f32_8x6_cortexa9 7 4 6 0.0000012711169589040185 +armv7neon_mmm_f32_8x6_cortexa7 8 4 13 0.0000024259808837385263 +armv7neon_mmm_f32_8x4_cortexa7 24 128 9 0.000043285670969312205 +armv7neon_mmm_f32_8x6_generic 15 128 19 0.00005126058311511034 +generic_f32_4x4 13 128 11 0.00004170340313011054 +armv7neon_mmm_f32_8x6_cortexa9 8 128 12 0.000013510136481590433 +armv7neon_mmm_f32_8x6_generic 23 32 17 0.00001815155326925384 +armv7neon_mmm_f32_8x4_generic 24 32 8 0.000008758373656325976 +armv7neon_mmm_f32_8x4_generic 15 32 13 0.000012566353080909321 +armv7neon_mmm_f32_8x4_generic 15 4 9 0.000004138286673960469 +armv7neon_mmm_f32_8x4_cortexa7 16 32 4 0.000003365053985432853 +armv7neon_mmm_f32_8x4_cortexa7 9 128 3 0.0000101978289798725 +generic_f32_4x4 11 32 8 0.000007681371193157391 +generic_f32_4x4 7 128 9 0.00002127278439974851 +armv7neon_mmm_f32_8x4_cortexa7 24 4 13 0.000006759425188306811 +armv7neon_mmm_f32_8x4_cortexa7 24 32 9 0.000013896132257150766 +armv7neon_mmm_f32_8x4_cortexa9 25 32 9 0.000018829874971593756 +armv7neon_mmm_f32_8x4_cortexa7 9 32 4 0.000003474275688786252 +armv7neon_mmm_f32_8x6_cortexa7 8 32 7 0.0000044833206937891445 +generic_f32_4x4 3 32 7 0.0000030714723992174605 +armv7neon_mmm_f32_8x6_cortexa9 17 4 11 0.000004585922035285582 +armv7neon_mmm_f32_8x4_cortexa9 24 128 5 0.000029781338716311416 +armv7neon_mmm_f32_8x6_generic 15 32 18 0.000012214298327316169 +armv7neon_mmm_f32_8x6_generic 25 32 6 0.000007957229153899586 +armv7neon_mmm_f32_8x6_cortexa9 9 32 18 0.000012307833924250695 +armv7neon_mmm_f32_8x6_generic 8 32 13 0.000006159219915111089 +armv7neon_mmm_f32_8x4_cortexa7 8 128 3 0.000005401956819775254 +armv7neon_mmm_f32_8x4_generic 15 32 9 0.000009577293347035024 +armv7neon_mmm_f32_8x4_cortexa9 25 128 13 0.00007830393224178603 +generic_f32_4x4 4 4 13 0.0000026249296920626683 +armv7neon_mmm_f32_8x6_cortexa9 8 32 6 0.000002404882860716393 +armv7neon_mmm_f32_8x6_cortexa9 9 32 5 0.0000045921033389599396 +armv7neon_mmm_f32_8x6_generic 17 128 11 0.000038492804993535863 +armv7neon_mmm_f32_8x4_generic 17 32 9 0.000013624172849176604 +armv7neon_mmm_f32_8x4_cortexa9 24 4 9 0.000005393334776799241 +armv7neon_mmm_f32_8x4_cortexa9 23 32 8 0.000009723809855547939 +armv7neon_mmm_f32_8x4_cortexa9 25 4 4 0.0000025900920077984026 +armv7neon_mmm_f32_8x6_generic 23 4 5 0.0000027608446978317408 +armv7neon_mmm_f32_8x4_generic 8 4 12 0.0000019060496583230645 +armv7neon_mmm_f32_8x4_cortexa7 15 32 8 0.000006678781571694655 +generic_f32_4x4 4 4 9 0.0000021081574199754717 +armv7neon_mmm_f32_8x4_cortexa7 17 128 9 0.00004346458158147509 +armv7neon_mmm_f32_8x4_generic 25 4 8 0.000004544403472760322 +armv7neon_mmm_f32_8x4_generic 24 128 4 0.000013981279265161887 +armv7neon_mmm_f32_8x6_cortexa7 25 4 12 0.000005353769509302435 +armv7neon_mmm_f32_8x4_cortexa9 15 4 8 0.000002871100263594495 +armv7neon_mmm_f32_8x6_cortexa7 9 4 12 0.0000030500030063725167 +armv7neon_mmm_f32_8x4_cortexa9 15 128 7 0.000020348668200781455 +generic_f32_4x4 4 4 3 0.0000011380263710773134 +armv7neon_mmm_f32_8x4_generic 17 32 7 0.000009461004385359903 +armv7neon_mmm_f32_8x6_cortexa7 16 4 11 0.000003292952568894936 +armv7neon_mmm_f32_8x4_generic 9 128 7 0.0000189264627670604 +armv7neon_mmm_f32_8x4_cortexa7 9 32 12 0.000009404582356065284 +armv7neon_mmm_f32_8x6_cortexa7 24 4 11 0.000004645745440552646 +armv7neon_mmm_f32_8x4_generic 7 4 8 0.0000018628381186813514 +generic_f32_4x4 13 32 12 0.000014451033360231452 +armv7neon_mmm_f32_8x6_generic 7 128 11 0.00001335338354211472 +armv7neon_mmm_f32_8x4_cortexa9 24 32 5 0.000009736881086291142 +armv7neon_mmm_f32_8x4_generic 9 32 8 0.000006244209351860461 +armv7neon_mmm_f32_8x4_cortexa7 23 32 7 0.000009963814618687657 +armv7neon_mmm_f32_8x6_cortexa7 24 128 7 0.000039514548121680776 +armv7neon_mmm_f32_8x4_cortexa7 17 32 5 0.000009649055437118146 +armv7neon_mmm_f32_8x6_generic 7 128 7 0.000013256390141337729 +armv7neon_mmm_f32_8x6_cortexa7 9 128 17 0.00003956707654525446 +armv7neon_mmm_f32_8x4_generic 9 128 8 0.000018708339822835962 +generic_f32_4x4 9 4 7 0.00000399084452417743 +armv7neon_mmm_f32_8x4_generic 15 128 8 0.000018936209183566976 +armv7neon_mmm_f32_8x6_cortexa7 9 32 19 0.00001616084997042782 +armv7neon_mmm_f32_8x6_generic 16 128 5 0.00001333369244237612 +armv7neon_mmm_f32_8x4_cortexa7 9 32 3 0.0000036715654135840626 +armv7neon_mmm_f32_8x4_cortexa9 23 32 5 0.000009965038150728467 +armv7neon_mmm_f32_8x6_cortexa7 9 128 7 0.000026494406670274787 +armv7neon_mmm_f32_8x4_cortexa9 24 128 13 0.00005851957886711075 +armv7neon_mmm_f32_8x6_cortexa7 9 4 18 0.000004273474311217914 +generic_f32_4x4 5 32 12 0.000007635355402928665 +armv7neon_mmm_f32_8x4_cortexa9 7 128 7 0.000010528249080204253 +armv7neon_mmm_f32_8x6_cortexa9 24 32 19 0.00002386884326098149 +armv7neon_mmm_f32_8x6_generic 8 4 7 0.0000017826372979170567 +armv7neon_mmm_f32_8x4_generic 7 4 5 0.000001794625708774814 +armv7neon_mmm_f32_8x6_cortexa7 9 4 6 0.000001801617764943881 +armv7neon_mmm_f32_8x4_generic 25 32 13 0.00002352372957802219 +armv7neon_mmm_f32_8x4_generic 24 128 13 0.00005496909042425587 +armv7neon_mmm_f32_8x6_cortexa9 23 32 17 0.000018875847839557392 +generic_f32_4x4 12 32 4 0.000003926824640983302 +armv7neon_mmm_f32_8x6_cortexa9 7 128 18 0.000020631769324589798 +armv7neon_mmm_f32_8x6_cortexa9 8 128 19 0.000026726033464302463 +armv7neon_mmm_f32_8x6_cortexa7 7 4 5 0.0000012346138819052426 +armv7neon_mmm_f32_8x4_generic 17 128 7 0.00002816390176070489 +armv7neon_mmm_f32_8x6_generic 9 32 19 0.000015684300494904117 +armv7neon_mmm_f32_8x4_cortexa7 9 128 4 0.000010001875449763685 +armv7neon_mmm_f32_8x4_generic 23 32 13 0.000018251850356188044 +generic_f32_4x4 13 4 3 0.000003021647059953821 +armv7neon_mmm_f32_8x6_cortexa9 25 128 7 0.000053220906812986594 +armv7neon_mmm_f32_8x4_cortexa9 16 128 8 0.00001967608213206112 +armv7neon_mmm_f32_8x6_generic 24 32 5 0.000006620165371496042 +armv7neon_mmm_f32_8x4_cortexa7 9 4 7 0.0000028674611086712135 +armv7neon_mmm_f32_8x4_cortexa9 17 4 11 0.000005653151614630126 +generic_f32_4x4 13 4 4 0.000002594385628155042 +armv7neon_mmm_f32_8x6_generic 25 128 5 0.000026094659734105824 +armv7neon_mmm_f32_8x4_generic 17 32 3 0.000005161144602046728 +armv7neon_mmm_f32_8x4_cortexa9 24 32 11 0.000014220571969428708 +generic_f32_4x4 4 128 3 0.000004015357652724343 +armv7neon_mmm_f32_8x4_cortexa9 8 128 5 0.00001027176965651363 +armv7neon_mmm_f32_8x6_cortexa7 15 4 17 0.000004874430261781061 +armv7neon_mmm_f32_8x4_cortexa7 15 4 12 0.0000040169005856724945 +armv7neon_mmm_f32_8x4_cortexa7 8 32 12 0.00000477050855780904 +armv7neon_mmm_f32_8x6_cortexa7 9 4 7 0.0000031516329281183407 +armv7neon_mmm_f32_8x4_generic 7 32 11 0.000005308351521432454 +armv7neon_mmm_f32_8x4_cortexa7 25 32 5 0.000012670365676368953 +generic_f32_4x4 5 128 5 0.000014294465011666321 +armv7neon_mmm_f32_8x6_cortexa7 7 32 17 0.000006818696484359277 +armv7neon_mmm_f32_8x4_cortexa9 24 32 7 0.00000986925847989351 +generic_f32_4x4 4 128 13 0.000014106152963483687 +armv7neon_mmm_f32_8x4_cortexa9 16 128 3 0.000010512285712675924 +generic_f32_4x4 8 128 4 0.000007240602279573043 +armv7neon_mmm_f32_8x4_cortexa9 7 128 9 0.00001548686209510202 +armv7neon_mmm_f32_8x4_cortexa9 7 32 3 0.000002138015694680355 +armv7neon_mmm_f32_8x4_generic 24 32 13 0.000017536918553280824 +armv7neon_mmm_f32_8x6_cortexa9 23 4 12 0.0000044455569065126315 +armv7neon_mmm_f32_8x4_cortexa7 8 4 13 0.0000026000896469710663 +armv7neon_mmm_f32_8x4_cortexa7 25 32 8 0.000012179917551949525 +generic_f32_4x4 5 32 5 0.000005390504735926875 +generic_f32_4x4 11 128 13 0.00004175553018581371 +armv7neon_mmm_f32_8x4_cortexa7 7 32 4 0.0000021521595449855865 +armv7neon_mmm_f32_8x4_cortexa9 8 4 9 0.000002148291671877107 +armv7neon_mmm_f32_8x6_cortexa7 24 4 18 0.000005654885858199984 +armv7neon_mmm_f32_8x4_generic 25 128 9 0.00005533756635099798 +armv7neon_mmm_f32_8x4_cortexa9 9 128 9 0.00002978120797293901 +armv7neon_mmm_f32_8x6_cortexa7 8 32 12 0.000004281164608278993 +armv7neon_mmm_f32_8x4_generic 9 4 13 0.000004935869563311462 +armv7neon_mmm_f32_8x4_cortexa7 23 128 3 0.000015184873420309865 +generic_f32_4x4 12 4 12 0.000004912969841843077 +armv7neon_mmm_f32_8x6_generic 16 4 7 0.0000030460066806302122 +armv7neon_mmm_f32_8x4_cortexa7 9 4 13 0.000004992355989646887 +armv7neon_mmm_f32_8x6_cortexa9 15 4 12 0.0000032832021366886516 +armv7neon_mmm_f32_8x6_cortexa9 17 4 7 0.000004416231510276644 +armv7neon_mmm_f32_8x4_generic 9 128 5 0.000018874961280340985 +armv7neon_mmm_f32_8x4_cortexa7 25 4 9 0.000007146809416343016 +armv7neon_mmm_f32_8x6_cortexa9 23 4 5 0.000002791403862004092 +generic_f32_4x4 3 128 11 0.000011052312539972672 +armv7neon_mmm_f32_8x4_cortexa7 16 32 13 0.000012274313788252517 +generic_f32_4x4 5 4 8 0.000002691374011509211 +armv7neon_mmm_f32_8x6_generic 8 4 12 0.0000016391318699699268 +generic_f32_4x4 8 4 12 0.0000034590196765442797 +armv7neon_mmm_f32_8x6_generic 17 128 18 0.00005685255235859907 +armv7neon_mmm_f32_8x6_cortexa9 17 128 6 0.00002017521356859541 +armv7neon_mmm_f32_8x6_generic 25 128 6 0.00002553797390337986 +armv7neon_mmm_f32_8x6_cortexa9 25 128 5 0.000027210481604244293 +armv7neon_mmm_f32_8x4_cortexa7 15 128 7 0.000019945561157727263 +armv7neon_mmm_f32_8x4_generic 7 4 11 0.000002550958711536068 +generic_f32_4x4 13 4 8 0.000004699429428545618 +armv7neon_mmm_f32_8x4_cortexa9 9 4 5 0.0000028517696924614755 +armv7neon_mmm_f32_8x4_cortexa9 8 4 3 0.0000012050980961045702 +armv7neon_mmm_f32_8x6_cortexa9 24 128 17 0.000059842842681725297 +armv7neon_mmm_f32_8x6_cortexa9 25 128 6 0.000026691322740044253 +generic_f32_4x4 9 32 3 0.000004333031052520466 +armv7neon_mmm_f32_8x6_cortexa7 9 32 11 0.000008502744568312225 +armv7neon_mmm_f32_8x6_cortexa9 16 32 11 0.000008643328393303817 +armv7neon_mmm_f32_8x6_cortexa9 16 4 11 0.00000327884229270897 +armv7neon_mmm_f32_8x4_generic 8 128 5 0.000009661930895932827 +armv7neon_mmm_f32_8x4_cortexa9 15 4 5 0.0000029989007242676373 +armv7neon_mmm_f32_8x4_cortexa7 23 32 11 0.000014477721812785248 +armv7neon_mmm_f32_8x6_cortexa7 9 4 13 0.000004411946245984795 +armv7neon_mmm_f32_8x6_generic 7 128 12 0.000013345094425498999 +armv7neon_mmm_f32_8x4_cortexa9 24 128 11 0.00004426398857587651 +armv7neon_mmm_f32_8x6_generic 25 128 7 0.00005086677641643256 +armv7neon_mmm_f32_8x6_generic 16 32 17 0.00001197546406643872 +generic_f32_4x4 4 32 13 0.000005196854104063007 +armv7neon_mmm_f32_8x4_cortexa7 16 4 7 0.000002852179426317928 +armv7neon_mmm_f32_8x6_cortexa7 23 4 7 0.000004598943528457281 +armv7neon_mmm_f32_8x6_generic 17 32 6 0.000006120980761955415 +armv7neon_mmm_f32_8x6_cortexa7 17 32 17 0.00001829474178461942 +generic_f32_4x4 7 4 12 0.000003918843035175344 +armv7neon_mmm_f32_8x6_generic 9 4 7 0.0000030530125579888694 +armv7neon_mmm_f32_8x6_cortexa9 23 4 13 0.000006537462226861253 +generic_f32_4x4 12 32 11 0.000011229611140019307 +armv7neon_mmm_f32_8x4_cortexa7 7 32 9 0.000005392896316953727 +armv7neon_mmm_f32_8x4_cortexa9 15 4 7 0.00000310030600994376 +armv7neon_mmm_f32_8x4_cortexa9 23 32 3 0.000005486033134258231 +armv7neon_mmm_f32_8x6_cortexa7 25 4 11 0.000005954018792233075 +armv7neon_mmm_f32_8x4_generic 23 128 8 0.000027951206051686114 +armv7neon_mmm_f32_8x6_generic 23 4 12 0.000004342351795004267 +armv7neon_mmm_f32_8x4_cortexa9 8 32 5 0.0000035943022440195 +armv7neon_mmm_f32_8x6_cortexa9 7 4 18 0.0000028075381670446466 +armv7neon_mmm_f32_8x4_generic 8 32 13 0.0000061830339053399435 +armv7neon_mmm_f32_8x6_generic 15 32 7 0.000008321528944284549 +armv7neon_mmm_f32_8x6_cortexa7 16 128 6 0.00001332317233161335 +armv7neon_mmm_f32_8x4_generic 9 4 12 0.000003689645307663438 +armv7neon_mmm_f32_8x4_cortexa9 16 32 7 0.000006751619359417928 +generic_f32_4x4 7 4 4 0.0000016418721244643219 +armv7neon_mmm_f32_8x4_generic 7 128 13 0.000019287387814897854 +armv7neon_mmm_f32_8x6_cortexa9 23 4 11 0.000004831555332090134 +armv7neon_mmm_f32_8x4_cortexa9 8 128 8 0.00001009639921913338 +armv7neon_mmm_f32_8x6_generic 23 32 5 0.000006582337877689147 +armv7neon_mmm_f32_8x4_generic 25 128 3 0.00001920626106575073 +armv7neon_mmm_f32_8x4_generic 7 32 8 0.000003711210711678028 +armv7neon_mmm_f32_8x6_cortexa7 16 128 19 0.00005218314273770285 +armv7neon_mmm_f32_8x6_cortexa9 7 128 12 0.000013936034942170719 +armv7neon_mmm_f32_8x6_generic 17 32 5 0.000006487710283772798 +armv7neon_mmm_f32_8x4_generic 8 128 7 0.000009698770362126403 +armv7neon_mmm_f32_8x4_generic 15 4 7 0.0000030411777158900867 +armv7neon_mmm_f32_8x6_generic 25 128 19 0.00010155326542007438 +armv7neon_mmm_f32_8x6_cortexa7 9 128 5 0.000013650595324888383 +armv7neon_mmm_f32_8x4_cortexa9 9 32 11 0.00000982080901853953 +generic_f32_4x4 9 32 11 0.00001141647780993558 +generic_f32_4x4 8 32 11 0.000007662846153151327 +armv7neon_mmm_f32_8x6_generic 8 128 17 0.000019396378758926843 +armv7neon_mmm_f32_8x6_cortexa9 8 4 17 0.00000248907607573393 +generic_f32_4x4 13 128 12 0.000041173556961397094 +armv7neon_mmm_f32_8x4_generic 25 32 3 0.0000067405543706379265 +armv7neon_mmm_f32_8x6_cortexa9 16 4 13 0.000004284450398155638 +armv7neon_mmm_f32_8x6_generic 7 32 13 0.000006564286527582003 +generic_f32_4x4 9 4 12 0.000005239644799087451 +generic_f32_4x4 3 128 12 0.00001105053431182824 +armv7neon_mmm_f32_8x4_cortexa9 16 32 12 0.000009204841041049037 +armv7neon_mmm_f32_8x4_cortexa9 8 4 7 0.0000016950881959591487 +armv7neon_mmm_f32_8x4_cortexa9 8 4 8 0.00000148340332010083 +armv7neon_mmm_f32_8x4_cortexa9 24 32 4 0.000004874669124633379 +armv7neon_mmm_f32_8x4_cortexa9 16 128 7 0.00002009832072993214 +armv7neon_mmm_f32_8x4_generic 16 4 5 0.000002739180418018979 +armv7neon_mmm_f32_8x4_cortexa9 8 4 12 0.000001953874992361998 +armv7neon_mmm_f32_8x4_generic 15 4 4 0.0000016638026379811712 +armv7neon_mmm_f32_8x4_cortexa9 25 128 8 0.000039087913140379676 +armv7neon_mmm_f32_8x6_cortexa9 17 4 12 0.000004212564422068874 +armv7neon_mmm_f32_8x6_cortexa9 8 32 5 0.00000263061064210809 +armv7neon_mmm_f32_8x4_generic 8 4 5 0.000001623245576042727 +armv7neon_mmm_f32_8x6_cortexa7 15 128 6 0.000013618693064061564 +armv7neon_mmm_f32_8x6_cortexa7 7 4 18 0.000002804843066747126 +generic_f32_4x4 3 4 11 0.0000023913102926543824 +armv7neon_mmm_f32_8x6_generic 16 4 6 0.000001638807649221633 +armv7neon_mmm_f32_8x6_generic 8 128 6 0.0000067051548997103826 +armv7neon_mmm_f32_8x6_cortexa9 16 4 6 0.0000016568160988445508 +armv7neon_mmm_f32_8x6_cortexa9 16 128 7 0.000026851544464649677 +armv7neon_mmm_f32_8x4_cortexa9 8 128 12 0.000014872272678272856 +armv7neon_mmm_f32_8x6_generic 24 128 19 0.00007580588220559739 +generic_f32_4x4 3 128 5 0.0000074971724012246544 +generic_f32_4x4 3 128 8 0.000007539035525450043 +armv7neon_mmm_f32_8x6_cortexa9 24 128 7 0.00004003350952160301 +armv7neon_mmm_f32_8x4_cortexa7 23 32 9 0.000014356067971382847 +armv7neon_mmm_f32_8x6_cortexa9 15 32 18 0.000012709917183710011 +generic_f32_4x4 9 128 3 0.000011011983185480526 +armv7neon_mmm_f32_8x4_generic 15 128 7 0.000019154850070723174 +generic_f32_4x4 7 32 12 0.000007829205768353918 +generic_f32_4x4 12 4 3 0.000002428328614613921 +armv7neon_mmm_f32_8x4_cortexa7 8 128 4 0.000005191807529366239 +armv7neon_mmm_f32_8x4_cortexa9 17 128 3 0.00001540571905602067 +armv7neon_mmm_f32_8x4_cortexa9 17 128 9 0.000044358855138628255 +armv7neon_mmm_f32_8x4_generic 8 32 4 0.0000018712794095957982 +armv7neon_mmm_f32_8x6_cortexa9 9 128 7 0.000026855539948208353 +armv7neon_mmm_f32_8x6_cortexa7 7 128 13 0.000020286298173428065 +armv7neon_mmm_f32_8x4_cortexa7 7 32 7 0.0000037916453247576294 +armv7neon_mmm_f32_8x4_cortexa7 16 128 11 0.000029071525948432538 +armv7neon_mmm_f32_8x4_generic 8 128 8 0.000009479495707619602 +armv7neon_mmm_f32_8x4_cortexa7 9 32 13 0.000012575930597830614 +armv7neon_mmm_f32_8x6_cortexa7 7 4 7 0.0000019563323222872665 +armv7neon_mmm_f32_8x4_cortexa7 15 128 3 0.000010282742767020747 +armv7neon_mmm_f32_8x4_generic 17 128 9 0.00004166868490626211 +armv7neon_mmm_f32_8x6_cortexa9 15 32 11 0.000008848994549844927 +armv7neon_mmm_f32_8x4_cortexa7 16 128 7 0.000019694781274526964 +armv7neon_mmm_f32_8x6_cortexa9 7 128 17 0.00002067047797610865 +armv7neon_mmm_f32_8x4_cortexa7 16 4 8 0.000002421339765366324 +armv7neon_mmm_f32_8x6_cortexa9 25 4 19 0.000010458910160208884 +armv7neon_mmm_f32_8x4_cortexa7 17 4 12 0.000005174031688170145 +armv7neon_mmm_f32_8x4_cortexa7 8 128 7 0.000010096523777664552 +armv7neon_mmm_f32_8x4_cortexa7 9 32 11 0.000009664173576551376 +armv7neon_mmm_f32_8x4_cortexa9 15 32 13 0.000013172771066560177 +armv7neon_mmm_f32_8x6_cortexa7 15 32 6 0.0000045760506363113526 +armv7neon_mmm_f32_8x4_generic 16 128 7 0.000018904285841254675 +armv7neon_mmm_f32_8x6_cortexa9 24 32 7 0.000012425961839460377 +armv7neon_mmm_f32_8x4_cortexa7 8 128 8 0.00000988276098193371 +armv7neon_mmm_f32_8x4_cortexa7 25 32 13 0.000024332756642022888 +armv7neon_mmm_f32_8x6_cortexa9 8 4 12 0.000001657247635058824 +armv7neon_mmm_f32_8x4_cortexa7 8 4 9 0.000002131892294337394 +generic_f32_4x4 8 4 8 0.000002499295427359815 +armv7neon_mmm_f32_8x6_cortexa9 16 128 5 0.000013927438070206447 +generic_f32_4x4 3 4 7 0.0000017546173861422715 +armv7neon_mmm_f32_8x4_cortexa7 23 128 12 0.000043445426131070976 +armv7neon_mmm_f32_8x4_generic 8 128 12 0.00001395887153959496 +armv7neon_mmm_f32_8x4_cortexa9 7 4 11 0.0000025905119707065483 +generic_f32_4x4 12 128 4 0.00001060459652081298 +armv7neon_mmm_f32_8x6_generic 17 128 13 0.00005710733590612565 +armv7neon_mmm_f32_8x4_cortexa7 7 4 11 0.000002572252868831556 +armv7neon_mmm_f32_8x6_generic 15 128 11 0.000026019339889436572 +generic_f32_4x4 9 4 5 0.000003906378066863749 +armv7neon_mmm_f32_8x6_cortexa9 15 4 17 0.00000485956993190203 +armv7neon_mmm_f32_8x6_generic 9 32 17 0.000012038137584352969 +armv7neon_mmm_f32_8x4_cortexa9 8 32 11 0.000005081279026778461 +armv7neon_mmm_f32_8x4_cortexa7 25 128 11 0.000057856501014562726 +armv7neon_mmm_f32_8x6_cortexa9 25 32 19 0.00003186559880573022 +armv7neon_mmm_f32_8x6_cortexa9 8 32 12 0.000004319717163614961 +armv7neon_mmm_f32_8x6_cortexa9 8 32 19 0.000008329624592686573 +armv7neon_mmm_f32_8x6_cortexa9 9 32 13 0.000012409078932821099 +armv7neon_mmm_f32_8x6_cortexa9 17 128 12 0.00003982138220281054 +armv7neon_mmm_f32_8x4_cortexa9 8 32 4 0.0000019683045582115826 +armv7neon_mmm_f32_8x6_cortexa9 17 128 13 0.000059714513355137016 +armv7neon_mmm_f32_8x6_cortexa9 25 32 13 0.00002415767938328582 +armv7neon_mmm_f32_8x4_cortexa7 7 128 8 0.000010344849965132014 +armv7neon_mmm_f32_8x6_cortexa9 8 128 11 0.000013785904590501993 +armv7neon_mmm_f32_8x4_cortexa7 24 32 13 0.000018144082756384124 +armv7neon_mmm_f32_8x6_generic 15 4 13 0.000004567420998191548 +armv7neon_mmm_f32_8x4_generic 9 32 13 0.000012173282994755266 +generic_f32_4x4 4 4 4 0.0000009923563076021443 +armv7neon_mmm_f32_8x4_cortexa9 24 128 7 0.000029905631027460932 +armv7neon_mmm_f32_8x4_cortexa7 9 128 8 0.0000195010462183809 +armv7neon_mmm_f32_8x6_cortexa9 9 4 5 0.000001915573042137183 +generic_f32_4x4 7 128 12 0.000021189012747949428 +armv7neon_mmm_f32_8x4_generic 23 32 9 0.000013896926880805095 +armv7neon_mmm_f32_8x4_generic 8 128 13 0.000018647991490366943 +armv7neon_mmm_f32_8x4_cortexa7 15 32 11 0.00000998072291720506 +generic_f32_4x4 3 128 4 0.0000040235161328678346 +armv7neon_mmm_f32_8x4_generic 9 128 3 0.000009799742651068634 +armv7neon_mmm_f32_8x6_generic 25 4 11 0.000005815747914439624 +armv7neon_mmm_f32_8x4_cortexa9 9 4 12 0.0000037593832702220446 +armv7neon_mmm_f32_8x4_generic 8 32 7 0.0000034630101008340834 +armv7neon_mmm_f32_8x4_cortexa9 24 4 7 0.000004049582371373125 +armv7neon_mmm_f32_8x6_generic 9 32 12 0.000008052850558834548 +armv7neon_mmm_f32_8x4_cortexa7 25 4 3 0.000003139298464286286 +armv7neon_mmm_f32_8x4_generic 15 4 3 0.0000018347526374757481 +armv7neon_mmm_f32_8x6_cortexa9 25 4 18 0.000007705086964181997 +generic_f32_4x4 5 128 13 0.00002796513369706675 +armv7neon_mmm_f32_8x4_cortexa9 23 4 12 0.0000055210994766811515 +generic_f32_4x4 7 128 4 0.000007400928983118127 +generic_f32_4x4 12 128 8 0.000020684245069264504 +armv7neon_mmm_f32_8x4_generic 9 4 7 0.0000028402738019775784 +generic_f32_4x4 13 4 9 0.000007094831203377165 +armv7neon_mmm_f32_8x4_cortexa9 17 4 4 0.000002109614550782161 +generic_f32_4x4 9 32 7 0.00000788121798038347 +armv7neon_mmm_f32_8x4_cortexa9 17 32 9 0.000014308883462633102 +armv7neon_mmm_f32_8x4_generic 16 128 12 0.00002742841574783841 +armv7neon_mmm_f32_8x6_generic 9 32 13 0.000011930679710766873 +armv7neon_mmm_f32_8x4_cortexa7 24 128 7 0.000029306857678894273 +armv7neon_mmm_f32_8x4_cortexa9 15 4 11 0.0000043086270265983256 +generic_f32_4x4 11 4 11 0.000005690020384668188 +generic_f32_4x4 11 4 5 0.000003971103754939711 +armv7neon_mmm_f32_8x4_generic 23 128 7 0.000028363425957340315 +armv7neon_mmm_f32_8x4_cortexa7 25 32 7 0.000012820527346969179 +armv7neon_mmm_f32_8x6_generic 17 4 13 0.0000061204912028192775 +armv7neon_mmm_f32_8x4_cortexa7 23 4 13 0.000007468189054284558 +generic_f32_4x4 4 32 7 0.0000029448449923973884 +armv7neon_mmm_f32_8x4_cortexa9 25 32 12 0.000018265077104449187 +armv7neon_mmm_f32_8x6_cortexa9 17 32 5 0.000006709520484710961 +armv7neon_mmm_f32_8x4_cortexa9 23 128 7 0.00003016688393762355 +armv7neon_mmm_f32_8x4_cortexa9 16 128 13 0.000039189527740613905 +armv7neon_mmm_f32_8x4_cortexa7 16 128 5 0.000019619696312768993 +armv7neon_mmm_f32_8x4_generic 17 128 13 0.00005528191915660002 +armv7neon_mmm_f32_8x6_generic 17 128 19 0.00007601141239533076 +armv7neon_mmm_f32_8x4_generic 8 32 5 0.0000034293993184402013 +generic_f32_4x4 9 32 4 0.000004039130783975973 +armv7neon_mmm_f32_8x4_cortexa9 23 32 9 0.000014579239774429512 +armv7neon_mmm_f32_8x6_cortexa7 8 128 11 0.000013612858256767989 +armv7neon_mmm_f32_8x6_cortexa9 15 32 12 0.000008658441674303882 +armv7neon_mmm_f32_8x4_generic 9 128 13 0.00003711806630993475 +armv7neon_mmm_f32_8x6_generic 17 4 11 0.000004486220214719747 +armv7neon_mmm_f32_8x6_generic 24 32 17 0.00001769011948694583 +armv7neon_mmm_f32_8x6_generic 9 4 18 0.00000416889219962654 +armv7neon_mmm_f32_8x6_cortexa7 23 32 19 0.000024297125708617196 +armv7neon_mmm_f32_8x6_cortexa9 9 32 19 0.000016308328501596863 +generic_f32_4x4 5 32 13 0.000010150287138446791 +generic_f32_4x4 11 128 8 0.000021041592663498075 +armv7neon_mmm_f32_8x4_generic 17 32 8 0.000009030738946480375 +armv7neon_mmm_f32_8x4_generic 25 4 5 0.0000050194271140758065 +armv7neon_mmm_f32_8x4_generic 8 128 11 0.000014188042949903446 +generic_f32_4x4 13 32 11 0.000014981513303829195 +armv7neon_mmm_f32_8x4_cortexa9 8 128 4 0.000005303802744401548 +armv7neon_mmm_f32_8x6_cortexa7 17 4 5 0.0000027556390651666856 +armv7neon_mmm_f32_8x6_generic 25 128 12 0.000050521454291978235 +armv7neon_mmm_f32_8x6_cortexa9 16 4 7 0.0000031344346513242154 +armv7neon_mmm_f32_8x6_cortexa7 16 32 5 0.000004750660751815082 +armv7neon_mmm_f32_8x4_cortexa9 17 128 8 0.000029514324154126036 +armv7neon_mmm_f32_8x4_cortexa7 8 4 8 0.000001465122696586523 +armv7neon_mmm_f32_8x6_cortexa9 8 128 17 0.00002029296708644369 +armv7neon_mmm_f32_8x4_generic 25 32 12 0.00001736483445253461 +generic_f32_4x4 3 32 5 0.0000030443235090952762 +generic_f32_4x4 7 128 11 0.000021331736800406107 +armv7neon_mmm_f32_8x4_generic 24 128 3 0.000014605163771206865 +armv7neon_mmm_f32_8x4_cortexa9 15 32 8 0.000006785141126690698 +armv7neon_mmm_f32_8x6_cortexa7 23 4 11 0.000004845198031614994 +armv7neon_mmm_f32_8x6_cortexa7 8 4 18 0.0000022246057256413265 +armv7neon_mmm_f32_8x4_generic 23 32 12 0.000013609481500575639 +armv7neon_mmm_f32_8x6_generic 7 128 17 0.00001990485288299381 +armv7neon_mmm_f32_8x6_cortexa7 25 128 19 0.00010434796032564001 +armv7neon_mmm_f32_8x6_generic 15 4 7 0.0000032121848403447733 +armv7neon_mmm_f32_8x4_generic 25 32 9 0.00001793284879597201 +armv7neon_mmm_f32_8x4_cortexa7 25 4 5 0.0000050795046947410985 +armv7neon_mmm_f32_8x4_cortexa7 17 32 12 0.000013704665363244266 +generic_f32_4x4 5 32 3 0.0000030334097589492826 +armv7neon_mmm_f32_8x4_cortexa9 17 128 5 0.000030021544003020483 +armv7neon_mmm_f32_8x4_cortexa7 24 4 9 0.000005360352425382144 +armv7neon_mmm_f32_8x6_cortexa7 9 32 18 0.000012173297962511017 +armv7neon_mmm_f32_8x4_cortexa7 15 4 13 0.000005358787865343933 +armv7neon_mmm_f32_8x6_cortexa9 17 32 11 0.000012635537581820717 +armv7neon_mmm_f32_8x4_generic 9 32 12 0.000009105324476823614 +armv7neon_mmm_f32_8x4_cortexa7 17 128 11 0.00004382978043849 +armv7neon_mmm_f32_8x6_generic 17 4 6 0.0000023082534746308964 +armv7neon_mmm_f32_8x6_cortexa7 8 128 17 0.000020059146620211786 +armv7neon_mmm_f32_8x4_cortexa7 15 4 7 0.000003068689058039639 +armv7neon_mmm_f32_8x4_cortexa9 15 4 13 0.000005392948403824124 +armv7neon_mmm_f32_8x4_cortexa7 15 4 8 0.0000028443546860500008 +armv7neon_mmm_f32_8x4_cortexa9 24 4 3 0.000002583780394094933 +generic_f32_4x4 13 4 11 0.0000072216668884748755 +armv7neon_mmm_f32_8x6_cortexa7 25 4 13 0.00000811613473861232 +armv7neon_mmm_f32_8x6_generic 7 32 18 0.000006623134760255213 +armv7neon_mmm_f32_8x4_cortexa7 16 4 4 0.0000014656631347323804 +armv7neon_mmm_f32_8x6_cortexa9 24 128 12 0.00003980745750816077 +armv7neon_mmm_f32_8x6_cortexa9 17 32 6 0.00000638262644761529 +generic_f32_4x4 8 128 12 0.000020674001160178604 +armv7neon_mmm_f32_8x6_cortexa9 9 128 5 0.000013783853077105059 +armv7neon_mmm_f32_8x4_cortexa7 23 4 11 0.00000590971284549428 +armv7neon_mmm_f32_8x6_generic 17 128 7 0.00003951740163826464 +armv7neon_mmm_f32_8x6_cortexa7 23 128 12 0.00003972987011474437 +generic_f32_4x4 11 32 9 0.000011454015846057979 +armv7neon_mmm_f32_8x4_generic 7 128 8 0.000009944618306958537 +armv7neon_mmm_f32_8x4_cortexa7 9 128 12 0.00002904338895016975 +generic_f32_4x4 7 32 3 0.000003062772103932835 +armv7neon_mmm_f32_8x6_generic 7 32 19 0.000008594590275392258 +armv7neon_mmm_f32_8x4_cortexa9 24 4 4 0.0000019652194228578485 +armv7neon_mmm_f32_8x6_generic 25 32 17 0.00002354000930758944 +armv7neon_mmm_f32_8x4_cortexa7 24 4 12 0.000004776517171804484 +generic_f32_4x4 9 128 12 0.00003113614108946529 +armv7neon_mmm_f32_8x4_generic 25 4 13 0.000009058320096239628 +armv7neon_mmm_f32_8x4_cortexa9 15 32 7 0.000007013841485260632 +armv7neon_mmm_f32_8x6_cortexa7 8 32 13 0.000006373539570632082 +armv7neon_mmm_f32_8x6_cortexa9 7 128 7 0.000013849670236184067 +armv7neon_mmm_f32_8x6_cortexa7 16 4 5 0.0000021140389285040373 +armv7neon_mmm_f32_8x6_cortexa7 15 4 19 0.000006018452488388481 +armv7neon_mmm_f32_8x4_cortexa9 25 4 11 0.000007297177417716392 +armv7neon_mmm_f32_8x4_cortexa7 7 128 3 0.0000053704412970208095 +armv7neon_mmm_f32_8x6_cortexa7 25 32 6 0.000008216173586447742 +armv7neon_mmm_f32_8x6_cortexa9 23 32 6 0.000006530580171598388 +armv7neon_mmm_f32_8x6_cortexa9 8 4 11 0.0000019099336489036645 +armv7neon_mmm_f32_8x4_cortexa7 8 4 3 0.0000011915557137944658 +armv7neon_mmm_f32_8x4_cortexa9 7 32 13 0.000007135759350139172 +armv7neon_mmm_f32_8x4_cortexa9 15 128 8 0.000020152163526097688 +armv7neon_mmm_f32_8x6_generic 16 128 12 0.000025531348127989936 +armv7neon_mmm_f32_8x4_cortexa9 9 32 8 0.00000655179781932232 +armv7neon_mmm_f32_8x4_generic 25 4 7 0.000005150952172982353 +armv7neon_mmm_f32_8x6_generic 23 128 7 0.00003975509670222853 +generic_f32_4x4 7 128 7 0.000014417564018686427 +armv7neon_mmm_f32_8x6_generic 7 32 6 0.0000025435007870682203 +generic_f32_4x4 12 32 13 0.000014516218594466092 +armv7neon_mmm_f32_8x6_generic 24 4 13 0.000005989131584497686 +armv7neon_mmm_f32_8x6_cortexa7 24 4 13 0.000006130318576111218 +armv7neon_mmm_f32_8x4_cortexa7 24 4 8 0.0000033735678137031244 +armv7neon_mmm_f32_8x6_cortexa9 15 32 6 0.000004614375157776428 +armv7neon_mmm_f32_8x4_cortexa7 17 128 5 0.000029428735157155848 +generic_f32_4x4 11 32 3 0.000004361903012775443 +armv7neon_mmm_f32_8x6_cortexa9 24 128 11 0.00004051475238084589 +armv7neon_mmm_f32_8x6_cortexa7 8 4 11 0.0000019289152314594922 +armv7neon_mmm_f32_8x4_generic 7 32 12 0.00000529445196979877 +armv7neon_mmm_f32_8x4_cortexa9 25 4 9 0.0000071851687447521495 +armv7neon_mmm_f32_8x4_cortexa9 7 128 13 0.0000205036178463644 +armv7neon_mmm_f32_8x4_cortexa7 25 128 4 0.00001951695951825782 +generic_f32_4x4 11 32 11 0.0000115439545889861 +armv7neon_mmm_f32_8x6_cortexa7 23 32 7 0.000012514048765991971 +generic_f32_4x4 13 32 8 0.000009849544291047146 +armv7neon_mmm_f32_8x4_cortexa9 24 4 13 0.0000068036126797526975 +armv7neon_mmm_f32_8x6_cortexa9 23 128 7 0.00004044892329500272 +armv7neon_mmm_f32_8x4_cortexa9 24 32 9 0.000014128824207386303 +armv7neon_mmm_f32_8x6_cortexa9 15 4 19 0.000006004743734908054 +armv7neon_mmm_f32_8x6_generic 16 128 7 0.000025803175203093092 +armv7neon_mmm_f32_8x4_generic 24 4 9 0.000005289440520085159 +armv7neon_mmm_f32_8x4_generic 7 128 7 0.000009924053412179527 +armv7neon_mmm_f32_8x6_cortexa7 15 4 5 0.0000020760458197512403 +armv7neon_mmm_f32_8x4_cortexa9 23 32 12 0.000014302742013508701 +armv7neon_mmm_f32_8x6_cortexa9 23 128 18 0.00006018418795895432 +armv7neon_mmm_f32_8x4_cortexa7 7 128 12 0.000015239535080520215 +armv7neon_mmm_f32_8x6_generic 15 4 6 0.0000018544842241934815 +armv7neon_mmm_f32_8x6_cortexa7 25 32 18 0.000023497641993495917 +generic_f32_4x4 9 4 8 0.0000037065692747303202 +armv7neon_mmm_f32_8x6_generic 9 4 17 0.0000044060023407811705 +armv7neon_mmm_f32_8x4_generic 17 4 7 0.000004004011518049962 +armv7neon_mmm_f32_8x4_generic 16 4 9 0.0000037009767742197034 +armv7neon_mmm_f32_8x4_generic 16 4 11 0.0000037573621028760724 +armv7neon_mmm_f32_8x6_cortexa9 23 4 18 0.000006335024379033378 +generic_f32_4x4 7 32 11 0.00000797109252276388 +generic_f32_4x4 8 32 8 0.00000506136423984456 +armv7neon_mmm_f32_8x6_cortexa9 16 32 18 0.000011962958224342696 +armv7neon_mmm_f32_8x4_generic 9 32 3 0.0000035695396503977124 +armv7neon_mmm_f32_8x4_cortexa7 15 128 9 0.000029527917733416504 +armv7neon_mmm_f32_8x4_cortexa7 16 32 3 0.0000037787003081727403 +armv7neon_mmm_f32_8x4_cortexa9 17 4 5 0.000003983936158752065 +generic_f32_4x4 5 4 7 0.0000028427432000736736 +armv7neon_mmm_f32_8x6_generic 24 128 6 0.000019285772023622517 +armv7neon_mmm_f32_8x6_cortexa9 24 128 6 0.000020079389293074595 +armv7neon_mmm_f32_8x4_cortexa7 24 128 11 0.000043650740679119 +armv7neon_mmm_f32_8x6_cortexa7 15 4 6 0.0000019214162046321274 +armv7neon_mmm_f32_8x6_generic 24 128 11 0.000039928070800596723 +armv7neon_mmm_f32_8x6_cortexa7 17 128 13 0.00005915137603637775 +armv7neon_mmm_f32_8x6_cortexa7 9 128 12 0.00002647047584440748 +generic_f32_4x4 8 32 7 0.000005396919284647475 +armv7neon_mmm_f32_8x4_cortexa7 16 32 7 0.000006638663857963081 +armv7neon_mmm_f32_8x4_cortexa7 9 128 9 0.000029246794882198228 +armv7neon_mmm_f32_8x4_generic 15 32 12 0.000009467451463683956 +armv7neon_mmm_f32_8x6_generic 23 128 6 0.00001954577461053118 +armv7neon_mmm_f32_8x4_cortexa9 7 32 7 0.000003851842872992441 +armv7neon_mmm_f32_8x6_cortexa9 16 32 6 0.000004321654529645647 +generic_f32_4x4 13 32 4 0.00000517924119222049 +armv7neon_mmm_f32_8x4_generic 8 4 9 0.000002104797317503441 +armv7neon_mmm_f32_8x4_cortexa7 24 128 3 0.000015233751299574878 +armv7neon_mmm_f32_8x6_cortexa9 16 128 18 0.000039798911987149584 +armv7neon_mmm_f32_8x6_cortexa7 23 128 13 0.00005946225660703637 +generic_f32_4x4 7 32 5 0.000005462565009156774 +armv7neon_mmm_f32_8x6_generic 8 128 19 0.000025872976344928823 +generic_f32_4x4 5 4 12 0.0000037781682890084256 +armv7neon_mmm_f32_8x4_cortexa9 7 4 13 0.000003202055925058074 +armv7neon_mmm_f32_8x6_cortexa9 8 32 13 0.000006422608725593242 +armv7neon_mmm_f32_8x6_generic 9 128 7 0.000025813127546183672 +armv7neon_mmm_f32_8x4_generic 23 128 3 0.000014644120134860135 +armv7neon_mmm_f32_8x4_generic 8 4 3 0.0000011807711987459403 +armv7neon_mmm_f32_8x6_cortexa7 16 128 18 0.00003920283874886212 +armv7neon_mmm_f32_8x4_cortexa9 15 32 3 0.0000038264927174656245 +armv7neon_mmm_f32_8x6_cortexa7 25 32 12 0.000015882492018463845 +armv7neon_mmm_f32_8x6_generic 15 128 13 0.00003967007811496026 +armv7neon_mmm_f32_8x6_generic 25 4 12 0.00000523795399145237 +armv7neon_mmm_f32_8x4_cortexa9 16 4 7 0.0000028765969548151297 +armv7neon_mmm_f32_8x4_cortexa7 7 4 5 0.0000018096325702613645 +armv7neon_mmm_f32_8x6_generic 25 4 19 0.00001025642311454773 +armv7neon_mmm_f32_8x6_generic 9 128 6 0.000013045121912181557 +armv7neon_mmm_f32_8x6_cortexa7 15 32 11 0.000008770690105208661 +armv7neon_mmm_f32_8x6_generic 23 128 17 0.00005984628206960245 +armv7neon_mmm_f32_8x4_cortexa9 8 32 9 0.000005054839401749921 +generic_f32_4x4 9 32 13 0.000014812319931001716 +armv7neon_mmm_f32_8x4_cortexa9 8 32 7 0.0000036300075704923473 +generic_f32_4x4 12 128 12 0.00003080051055479753 +armv7neon_mmm_f32_8x4_generic 9 4 4 0.0000015775072922596973 +generic_f32_4x4 11 128 11 0.00003163786674887252 +generic_f32_4x4 11 128 7 0.000021315035021180117 +armv7neon_mmm_f32_8x4_generic 24 128 11 0.00004302931660486898 +armv7neon_mmm_f32_8x6_cortexa7 7 128 7 0.000013660184612749007 +armv7neon_mmm_f32_8x6_generic 15 4 12 0.0000032030364372184837 +armv7neon_mmm_f32_8x6_cortexa7 15 128 17 0.00004014988958130008 +armv7neon_mmm_f32_8x4_cortexa7 17 32 7 0.000009763008233938806 +armv7neon_mmm_f32_8x4_generic 23 4 11 0.000005850624187247901 +armv7neon_mmm_f32_8x6_cortexa9 25 4 7 0.000005691323284198507 +armv7neon_mmm_f32_8x6_generic 9 4 5 0.0000018995170310061328 +generic_f32_4x4 11 32 5 0.000007873582158405535 +armv7neon_mmm_f32_8x6_generic 17 4 7 0.0000043144070272553465 +armv7neon_mmm_f32_8x6_cortexa9 7 32 13 0.000006785328240517207 +armv7neon_mmm_f32_8x4_generic 25 128 12 0.00005645345647499795 +armv7neon_mmm_f32_8x4_generic 9 4 8 0.0000026364183937099707 +armv7neon_mmm_f32_8x4_generic 9 4 3 0.000001754783422972911 +armv7neon_mmm_f32_8x6_cortexa9 24 4 5 0.000002822627399800846 +armv7neon_mmm_f32_8x4_cortexa9 23 4 7 0.000004277460484355386 +armv7neon_mmm_f32_8x4_cortexa9 16 4 13 0.000004703052607837251 +armv7neon_mmm_f32_8x4_cortexa7 9 32 8 0.0000064472411584695615 +generic_f32_4x4 5 128 3 0.000007484402044593335 +armv7neon_mmm_f32_8x6_cortexa7 24 4 12 0.000003960884362652896 +armv7neon_mmm_f32_8x4_cortexa7 7 32 8 0.0000038146920144727004 +armv7neon_mmm_f32_8x4_cortexa9 24 4 12 0.000004810650577018378 +armv7neon_mmm_f32_8x4_cortexa9 9 4 13 0.000005031022350762627 +armv7neon_mmm_f32_8x6_cortexa9 9 128 12 0.000026841942459090627 +generic_f32_4x4 3 4 3 0.0000011084107564139803 +armv7neon_mmm_f32_8x4_generic 23 128 13 0.000057838678203389286 +armv7neon_mmm_f32_8x6_cortexa7 23 128 5 0.000020389937552256748 +armv7neon_mmm_f32_8x4_cortexa7 15 32 13 0.000012977109930123348 +armv7neon_mmm_f32_8x4_generic 16 32 11 0.00000919341672636989 +armv7neon_mmm_f32_8x6_cortexa9 17 128 17 0.000060215116898669664 +armv7neon_mmm_f32_8x4_generic 24 4 8 0.000003324206058446326 +armv7neon_mmm_f32_8x4_cortexa7 17 4 5 0.000003957357480955988 +armv7neon_mmm_f32_8x6_cortexa9 16 32 19 0.000016111976497239984 +generic_f32_4x4 13 4 12 0.000006710960240784156 +armv7neon_mmm_f32_8x6_cortexa9 23 128 5 0.000020639189093056077 +armv7neon_mmm_f32_8x6_generic 24 32 11 0.000012182073684360654 +armv7neon_mmm_f32_8x6_cortexa9 9 128 13 0.00004021781819635084 +armv7neon_mmm_f32_8x4_cortexa9 17 4 12 0.000005225946660532542 +armv7neon_mmm_f32_8x6_cortexa9 15 4 7 0.0000032840412749160575 +armv7neon_mmm_f32_8x4_cortexa9 17 128 13 0.000059220265497108164 +armv7neon_mmm_f32_8x6_cortexa9 7 4 11 0.0000020564874758047424 +generic_f32_4x4 8 128 8 0.000013969303780467269 +armv7neon_mmm_f32_8x4_cortexa7 25 4 12 0.000006595762756379242 +armv7neon_mmm_f32_8x6_generic 16 32 19 0.000015466876363304513 +armv7neon_mmm_f32_8x4_generic 24 4 11 0.0000053731939607204945 +generic_f32_4x4 8 128 13 0.00002774022280530327 +generic_f32_4x4 7 128 8 0.00001428610505820359 +armv7neon_mmm_f32_8x4_cortexa9 15 128 12 0.00002999051242337208 +armv7neon_mmm_f32_8x4_cortexa9 17 32 5 0.00000980239512919155 +armv7neon_mmm_f32_8x4_cortexa9 9 128 8 0.00001992992679699177 +armv7neon_mmm_f32_8x4_generic 25 32 5 0.00001226452231341447 +generic_f32_4x4 11 128 4 0.0000107871041093641 +generic_f32_4x4 13 32 5 0.000010185534520279402 +armv7neon_mmm_f32_8x6_cortexa7 9 32 13 0.000012306258740029597 +generic_f32_4x4 3 4 9 0.0000023591282065930476 +armv7neon_mmm_f32_8x4_cortexa7 15 128 12 0.000029408783696922103 +armv7neon_mmm_f32_8x6_cortexa9 25 4 5 0.000003460717936029046 +armv7neon_mmm_f32_8x6_cortexa9 15 32 7 0.000008659647810573247 +armv7neon_mmm_f32_8x6_generic 15 32 12 0.000008324450366911187 +armv7neon_mmm_f32_8x4_generic 7 4 4 0.0000011733671526145581 +armv7neon_mmm_f32_8x6_generic 8 32 17 0.000006245151171661497 +generic_f32_4x4 4 128 12 0.000010589994729591713 +generic_f32_4x4 13 32 3 0.000005623254183753802 +armv7neon_mmm_f32_8x4_cortexa9 17 32 8 0.000009489997139764281 +armv7neon_mmm_f32_8x6_cortexa7 15 128 12 0.0000267366663985427 +armv7neon_mmm_f32_8x6_generic 8 32 12 0.000004170620267459084 +generic_f32_4x4 12 128 13 0.00004142579938644744 +armv7neon_mmm_f32_8x6_cortexa7 7 4 17 0.0000028372537187841064 +armv7neon_mmm_f32_8x6_cortexa7 17 4 18 0.000005987713490844775 +armv7neon_mmm_f32_8x6_cortexa9 7 4 5 0.0000012358111649104604 +armv7neon_mmm_f32_8x4_generic 7 4 7 0.0000018542946008174326 +armv7neon_mmm_f32_8x4_generic 7 4 13 0.0000031498896105686864 +armv7neon_mmm_f32_8x6_cortexa9 15 4 11 0.0000034645213247919744 +armv7neon_mmm_f32_8x6_cortexa7 23 4 5 0.0000028436717084834024 +armv7neon_mmm_f32_8x4_generic 17 128 3 0.00001456481059501176 +armv7neon_mmm_f32_8x4_cortexa7 17 4 7 0.00000404574535805843 +armv7neon_mmm_f32_8x6_cortexa9 23 4 19 0.000008441880755451333 +armv7neon_mmm_f32_8x6_generic 16 4 5 0.000002037583190427157 +armv7neon_mmm_f32_8x4_cortexa7 17 4 11 0.000005618041346469936 +generic_f32_4x4 8 128 3 0.000007542706448510692 +armv7neon_mmm_f32_8x6_cortexa7 16 4 6 0.0000016599211327997808 +armv7neon_mmm_f32_8x4_generic 24 32 11 0.000013531694734026834 +armv7neon_mmm_f32_8x4_cortexa7 24 128 13 0.00005774273822898698 +generic_f32_4x4 12 32 8 0.000007328921605870887 +armv7neon_mmm_f32_8x4_generic 17 32 5 0.000009344539695515143 +armv7neon_mmm_f32_8x6_cortexa7 25 4 5 0.0000035354392629416224 +armv7neon_mmm_f32_8x4_generic 9 128 9 0.000028070243170981636 +armv7neon_mmm_f32_8x4_generic 7 32 7 0.0000036902878668462197 +generic_f32_4x4 4 32 5 0.000002913735845387932 +armv7neon_mmm_f32_8x4_cortexa7 23 128 9 0.00004401834026329482 +armv7neon_mmm_f32_8x6_cortexa7 15 32 17 0.000012793909984998035 +armv7neon_mmm_f32_8x4_cortexa7 24 32 11 0.000013985445748841752 +armv7neon_mmm_f32_8x4_generic 17 128 12 0.0000426589070381794 +armv7neon_mmm_f32_8x6_generic 9 128 19 0.000053237918402024005 +armv7neon_mmm_f32_8x6_cortexa7 17 4 17 0.000006443148021217296 +armv7neon_mmm_f32_8x4_cortexa7 8 128 12 0.000014559920878726009 +armv7neon_mmm_f32_8x6_cortexa9 8 4 19 0.000002972990505690349 +armv7neon_mmm_f32_8x6_cortexa7 25 128 5 0.000027009599886340098 +armv7neon_mmm_f32_8x6_cortexa7 7 32 5 0.0000025526809022378453 +generic_f32_4x4 13 128 13 0.00005568030788678991 +armv7neon_mmm_f32_8x4_cortexa7 15 32 12 0.000009774179275886764 +armv7neon_mmm_f32_8x4_generic 16 128 8 0.000018479196791574824 +armv7neon_mmm_f32_8x6_generic 25 128 17 0.00007786728690688477 +armv7neon_mmm_f32_8x6_generic 7 32 11 0.000004604319680609591 +armv7neon_mmm_f32_8x4_cortexa9 15 128 5 0.000020252737794258734 +armv7neon_mmm_f32_8x4_cortexa9 25 4 12 0.000006639922481547946 +armv7neon_mmm_f32_8x4_cortexa7 23 128 8 0.000029341008489406463 +armv7neon_mmm_f32_8x4_cortexa7 8 32 5 0.000003533774210652598 +generic_f32_4x4 8 128 7 0.000014303959630243704 +armv7neon_mmm_f32_8x4_cortexa9 25 32 5 0.000012867678837939383 +armv7neon_mmm_f32_8x6_generic 8 128 13 0.000019410057867203144 +armv7neon_mmm_f32_8x4_generic 15 4 5 0.0000029437961456768246 +generic_f32_4x4 12 4 9 0.000005303987106806706 +armv7neon_mmm_f32_8x6_generic 24 4 19 0.000007657205135344466 +armv7neon_mmm_f32_8x4_generic 9 4 11 0.000003929237868686891 +generic_f32_4x4 13 128 9 0.00004175491457338789 +armv7neon_mmm_f32_8x6_cortexa7 9 4 11 0.0000032396587513198363 +armv7neon_mmm_f32_8x6_cortexa7 9 32 5 0.000004609642726421324 +armv7neon_mmm_f32_8x6_cortexa7 8 128 13 0.000019980200207777093 +generic_f32_4x4 8 4 13 0.000004697441883300323 +armv7neon_mmm_f32_8x4_generic 24 128 5 0.000028404231306910303 +armv7neon_mmm_f32_8x4_cortexa7 15 128 8 0.00001974335359333113 +armv7neon_mmm_f32_8x4_generic 8 4 4 0.000000975840577396329 +armv7neon_mmm_f32_8x6_cortexa9 17 4 6 0.000002372991977511254 +armv7neon_mmm_f32_8x6_cortexa7 24 4 17 0.0000063580907262403335 +armv7neon_mmm_f32_8x6_cortexa9 24 4 17 0.00000634338841510274 +armv7neon_mmm_f32_8x6_cortexa7 8 128 19 0.000026457168307267402 +armv7neon_mmm_f32_8x4_cortexa7 15 32 4 0.0000035973058136569266 +armv7neon_mmm_f32_8x6_cortexa7 23 128 11 0.000040137380065831114 +armv7neon_mmm_f32_8x6_generic 8 32 11 0.0000044080486884835774 +armv7neon_mmm_f32_8x6_cortexa7 16 32 13 0.000012184909260827915 +armv7neon_mmm_f32_8x4_generic 24 128 12 0.00004230513222395041 +armv7neon_mmm_f32_8x6_generic 16 32 6 0.000004173665390794717 +armv7neon_mmm_f32_8x4_generic 15 128 5 0.000019049336126167508 +generic_f32_4x4 5 32 11 0.000007837409156830309 +armv7neon_mmm_f32_8x6_cortexa7 15 128 5 0.00001375002565612756 +armv7neon_mmm_f32_8x6_generic 17 128 17 0.00005946391401813929 +generic_f32_4x4 8 128 9 0.00002095153754448952 +generic_f32_4x4 12 4 7 0.000003927725122844973 +armv7neon_mmm_f32_8x6_cortexa9 25 32 6 0.000008303393078126922 +armv7neon_mmm_f32_8x4_generic 17 128 11 0.000043129532556527814 +armv7neon_mmm_f32_8x6_generic 23 128 19 0.00007849962174893098 +armv7neon_mmm_f32_8x4_cortexa9 25 32 11 0.00001896209821400977 +generic_f32_4x4 13 128 4 0.000014088650845963764 +armv7neon_mmm_f32_8x6_generic 17 32 17 0.000017768659574740396 +armv7neon_mmm_f32_8x6_generic 25 32 11 0.000016016335806526446 +generic_f32_4x4 4 128 9 0.000010729958874422145 +armv7neon_mmm_f32_8x6_generic 9 128 12 0.00002573025108782965 +armv7neon_mmm_f32_8x6_cortexa7 9 128 19 0.000052645037711891825 +armv7neon_mmm_f32_8x4_cortexa7 15 32 9 0.000009881132385003031 +armv7neon_mmm_f32_8x6_generic 15 4 11 0.0000033903277250974355 +armv7neon_mmm_f32_8x4_cortexa9 23 4 9 0.000005836030607874567 +armv7neon_mmm_f32_8x6_cortexa9 15 32 13 0.00001271636871027449 +armv7neon_mmm_f32_8x4_cortexa9 7 128 12 0.00001554481747780415 +armv7neon_mmm_f32_8x6_cortexa7 9 128 11 0.00002665664619685107 +armv7neon_mmm_f32_8x6_generic 23 4 19 0.000008276510705477435 +armv7neon_mmm_f32_8x6_cortexa7 9 128 13 0.00003963680038872068 +armv7neon_mmm_f32_8x6_generic 9 4 12 0.0000029664494321281043 +armv7neon_mmm_f32_8x4_cortexa7 9 4 8 0.0000026618049788927614 +armv7neon_mmm_f32_8x6_generic 25 4 5 0.0000034227395786859638 +armv7neon_mmm_f32_8x6_cortexa7 7 4 11 0.00000205419298901934 +armv7neon_mmm_f32_8x4_generic 15 128 4 0.000009727827829578247 +armv7neon_mmm_f32_8x4_generic 23 32 5 0.000009507106099903071 +generic_f32_4x4 13 128 3 0.000014531958964466404 +generic_f32_4x4 8 4 4 0.0000014932684997984591 +armv7neon_mmm_f32_8x6_cortexa7 9 32 12 0.0000083110843565406 +armv7neon_mmm_f32_8x4_cortexa9 9 128 4 0.000010214368441429339 +armv7neon_mmm_f32_8x6_cortexa7 24 128 11 0.000039934073240007294 +armv7neon_mmm_f32_8x4_cortexa9 25 128 9 0.0000592519545970783 +armv7neon_mmm_f32_8x4_cortexa7 17 32 13 0.000018457944384762052 +armv7neon_mmm_f32_8x6_generic 15 32 17 0.000012429374164019118 +armv7neon_mmm_f32_8x6_generic 7 4 5 0.0000012252588749082722 +generic_f32_4x4 4 4 11 0.000002142420120818127 +armv7neon_mmm_f32_8x4_cortexa7 17 32 8 0.000009331391202514006 +armv7neon_mmm_f32_8x6_cortexa9 9 32 12 0.000008401621869943723 +armv7neon_mmm_f32_8x6_cortexa7 16 32 17 0.00001235175359963665 +armv7neon_mmm_f32_8x4_cortexa9 16 4 11 0.000003828421619878369 +armv7neon_mmm_f32_8x4_cortexa9 9 4 8 0.000002691222544669364 +armv7neon_mmm_f32_8x4_cortexa9 15 32 4 0.0000036579305511926493 +armv7neon_mmm_f32_8x6_cortexa7 8 32 11 0.0000045660588709311665 +generic_f32_4x4 5 4 11 0.000003955847289217088 +generic_f32_4x4 7 128 5 0.00001437315927626327 +generic_f32_4x4 7 4 7 0.0000028946154117180987 +generic_f32_4x4 8 32 12 0.000007313658366544497 +armv7neon_mmm_f32_8x4_cortexa7 16 4 12 0.000003356919407698039 +armv7neon_mmm_f32_8x4_cortexa9 17 32 12 0.000013931859029317578 +armv7neon_mmm_f32_8x6_generic 7 128 13 0.000019807746403591995 +armv7neon_mmm_f32_8x6_generic 16 32 12 0.000007859148665693045 +armv7neon_mmm_f32_8x6_cortexa9 7 32 17 0.000006883792927542178 +armv7neon_mmm_f32_8x4_generic 24 32 4 0.000004632821669832301 +armv7neon_mmm_f32_8x6_cortexa7 15 32 18 0.000012578243798367562 +armv7neon_mmm_f32_8x6_cortexa9 17 128 7 0.000040256201289956 +armv7neon_mmm_f32_8x6_cortexa9 25 128 13 0.00007968673747995729 +armv7neon_mmm_f32_8x6_cortexa7 16 32 12 0.000008069060391993281 +armv7neon_mmm_f32_8x6_cortexa9 8 128 13 0.000020267415746870704 +armv7neon_mmm_f32_8x4_cortexa7 23 4 12 0.0000054788039005168164 +armv7neon_mmm_f32_8x4_cortexa7 7 128 9 0.000015183203798286496 +armv7neon_mmm_f32_8x4_cortexa9 25 128 11 0.00005936651957856526 +armv7neon_mmm_f32_8x6_cortexa9 7 128 13 0.00002061155210279855 +armv7neon_mmm_f32_8x6_cortexa9 16 128 12 0.000026606165960514827 +armv7neon_mmm_f32_8x4_cortexa7 24 4 7 0.000004016126966910536 +armv7neon_mmm_f32_8x4_cortexa9 16 32 4 0.000003429114676947513 +armv7neon_mmm_f32_8x4_cortexa7 9 128 7 0.000019732799833911844 +armv7neon_mmm_f32_8x6_generic 17 128 5 0.00001976574761517156 +armv7neon_mmm_f32_8x6_cortexa7 16 128 12 0.00002622611042000424 +armv7neon_mmm_f32_8x4_generic 23 128 4 0.000014300391832012492 +generic_f32_4x4 4 32 9 0.0000040473563381120025 +armv7neon_mmm_f32_8x6_cortexa9 16 4 12 0.00000281228428913529 +armv7neon_mmm_f32_8x6_generic 15 128 7 0.000025996819067612833 +armv7neon_mmm_f32_8x4_generic 25 128 13 0.00007519120583347806 +armv7neon_mmm_f32_8x6_cortexa7 25 128 18 0.00007816972627698026 +armv7neon_mmm_f32_8x6_generic 23 4 7 0.000004478475265472157 +armv7neon_mmm_f32_8x6_cortexa7 23 32 17 0.000018694963726284716 +armv7neon_mmm_f32_8x4_cortexa9 24 128 3 0.000015528002576413032 +armv7neon_mmm_f32_8x4_generic 15 4 13 0.000005299524671067579 +armv7neon_mmm_f32_8x6_cortexa9 8 32 7 0.000004505823387651013 +armv7neon_mmm_f32_8x4_cortexa9 9 4 7 0.0000028943361192655857 +armv7neon_mmm_f32_8x4_generic 9 32 4 0.0000033743539224745525 +armv7neon_mmm_f32_8x4_cortexa9 23 128 5 0.000030175843280997272 +armv7neon_mmm_f32_8x6_generic 8 4 6 0.000001069506404381519 +armv7neon_mmm_f32_8x6_cortexa9 9 32 17 0.000012530842578999487 +armv7neon_mmm_f32_8x6_generic 25 4 7 0.0000055691779651847645 +armv7neon_mmm_f32_8x6_cortexa7 24 32 18 0.000017489152288872757 +armv7neon_mmm_f32_8x4_generic 8 32 3 0.0000020846296384167996 +armv7neon_mmm_f32_8x4_generic 24 128 9 0.000042880640962818326 +armv7neon_mmm_f32_8x6_cortexa7 16 32 6 0.000004280562843189148 +generic_f32_4x4 11 4 7 0.000004035828927778453 +armv7neon_mmm_f32_8x6_cortexa7 17 128 6 0.000019945123788870955 +armv7neon_mmm_f32_8x6_cortexa7 23 4 13 0.000006544590047318178 +generic_f32_4x4 3 128 13 0.00001453817180213847 +armv7neon_mmm_f32_8x6_cortexa9 7 4 7 0.000001955919692684627 +armv7neon_mmm_f32_8x4_generic 8 4 8 0.0000014466616080596564 +armv7neon_mmm_f32_8x4_cortexa9 7 4 12 0.00000257120273391917 +armv7neon_mmm_f32_8x6_cortexa7 23 32 12 0.000012365351347550488 +armv7neon_mmm_f32_8x4_cortexa7 24 128 8 0.000028841373491782905 +generic_f32_4x4 12 4 4 0.0000019842828386500398 +armv7neon_mmm_f32_8x4_cortexa7 25 32 11 0.000018655746399310692 +armv7neon_mmm_f32_8x4_cortexa7 15 4 5 0.0000029733858994116004 +armv7neon_mmm_f32_8x4_cortexa9 8 128 3 0.000005515258544137823 +armv7neon_mmm_f32_8x6_cortexa7 24 4 7 0.000004423529851941163 +generic_f32_4x4 8 32 4 0.0000027872687254075905 +armv7neon_mmm_f32_8x6_generic 17 32 18 0.000017325844518327246 +armv7neon_mmm_f32_8x4_cortexa7 25 4 13 0.000009174920440845347 +armv7neon_mmm_f32_8x4_generic 25 128 8 0.00003833303029711307 +armv7neon_mmm_f32_8x4_generic 16 4 4 0.0000014454956826662534 +armv7neon_mmm_f32_8x4_cortexa7 17 4 4 0.0000020761952577578897 +armv7neon_mmm_f32_8x6_cortexa7 8 32 5 0.0000026618366158480003 +generic_f32_4x4 3 4 4 0.0000011319274511202646 +armv7neon_mmm_f32_8x6_cortexa7 8 32 17 0.000006455239935042666 +armv7neon_mmm_f32_8x4_cortexa7 15 4 3 0.0000018508727527174426 +armv7neon_mmm_f32_8x4_cortexa9 7 128 5 0.000010464038311697658 +armv7neon_mmm_f32_8x4_cortexa7 9 128 11 0.000029303633762621288 +armv7neon_mmm_f32_8x6_generic 15 128 6 0.000013191467223518409 +armv7neon_mmm_f32_8x6_generic 8 32 19 0.00000799220605890635 +armv7neon_mmm_f32_8x4_cortexa7 25 32 3 0.000006944685928632884 +generic_f32_4x4 5 128 7 0.000014344402542266391 +armv7neon_mmm_f32_8x6_generic 25 32 18 0.00002286640455940777 +armv7neon_mmm_f32_8x6_generic 23 4 17 0.000006658878255694999 +armv7neon_mmm_f32_8x4_cortexa9 15 32 11 0.000010153701137788554 +armv7neon_mmm_f32_8x4_cortexa7 8 32 13 0.000006386976234107792 +generic_f32_4x4 9 128 11 0.0000315155364444221 +armv7neon_mmm_f32_8x4_generic 16 128 5 0.00001883838484826069 +armv7neon_mmm_f32_8x6_generic 8 4 18 0.0000021936894870634253 +armv7neon_mmm_f32_8x6_cortexa7 24 32 17 0.00001822762304619792 +armv7neon_mmm_f32_8x6_generic 24 32 6 0.000006012607283304525 +armv7neon_mmm_f32_8x4_cortexa9 7 4 8 0.0000018992159881966028 +generic_f32_4x4 5 32 8 0.000005268936385502091 +armv7neon_mmm_f32_8x6_generic 23 32 13 0.000017886686307111186 +armv7neon_mmm_f32_8x6_generic 23 128 11 0.00004012008266709576 +armv7neon_mmm_f32_8x4_generic 7 32 3 0.000002052664755929161 +armv7neon_mmm_f32_8x4_generic 7 32 13 0.0000068232658761941584 +armv7neon_mmm_f32_8x4_cortexa9 15 4 3 0.0000018728759441468222 +armv7neon_mmm_f32_8x4_cortexa9 17 32 7 0.000009921472462970692 +armv7neon_mmm_f32_8x4_cortexa7 23 4 5 0.000004108135771777712 +armv7neon_mmm_f32_8x6_generic 17 4 18 0.0000058654454576938545 +armv7neon_mmm_f32_8x6_generic 8 32 6 0.0000023314322550231567 +armv7neon_mmm_f32_8x6_cortexa9 24 32 12 0.000011982465286422162 +armv7neon_mmm_f32_8x6_cortexa9 9 4 12 0.000003049708218088651 +armv7neon_mmm_f32_8x6_cortexa7 9 4 19 0.000005629342662833259 +generic_f32_4x4 3 32 12 0.00000437213523735793 +armv7neon_mmm_f32_8x6_generic 23 128 18 0.00005941769593137103 +armv7neon_mmm_f32_8x6_cortexa7 15 4 18 0.000004622352036845354 +armv7neon_mmm_f32_8x6_cortexa9 24 128 19 0.00007950053817997623 +armv7neon_mmm_f32_8x4_generic 16 128 4 0.000009494054447373921 +armv7neon_mmm_f32_8x6_cortexa7 7 128 17 0.000020414604977074606 +armv7neon_mmm_f32_8x4_cortexa9 7 32 12 0.000005535747117413851 +armv7neon_mmm_f32_8x6_cortexa9 17 4 5 0.000002695145363194299 +generic_f32_4x4 4 128 4 0.000003870015760471616 +armv7neon_mmm_f32_8x4_generic 15 32 11 0.000009681542653487903 +armv7neon_mmm_f32_8x6_cortexa7 7 4 13 0.000002737908308165428 +armv7neon_mmm_f32_8x4_cortexa7 9 32 7 0.000006661491356810654 +armv7neon_mmm_f32_8x4_generic 16 4 8 0.0000023864199459892154 +armv7neon_mmm_f32_8x6_generic 24 32 12 0.000011535291893158489 +armv7neon_mmm_f32_8x6_cortexa9 15 128 11 0.00002729544131868298 +armv7neon_mmm_f32_8x4_cortexa7 15 128 13 0.00003933216865420138 +generic_f32_4x4 11 4 4 0.0000021466514853413024 +armv7neon_mmm_f32_8x4_cortexa7 15 32 7 0.000006892235042247499 +armv7neon_mmm_f32_8x4_generic 24 128 7 0.000028572043647170913 +armv7neon_mmm_f32_8x4_cortexa9 9 128 12 0.000029617239836207004 +armv7neon_mmm_f32_8x6_generic 23 32 6 0.000006267673062941748 +armv7neon_mmm_f32_8x6_cortexa9 23 128 17 0.00006059797167110977 +armv7neon_mmm_f32_8x6_cortexa9 9 128 18 0.00004014798753858387 +armv7neon_mmm_f32_8x6_generic 25 32 19 0.00003066922065226773 +armv7neon_mmm_f32_8x6_generic 9 32 7 0.00000813819010478461 +armv7neon_mmm_f32_8x6_generic 8 128 18 0.000019253370491633585 +armv7neon_mmm_f32_8x6_generic 8 4 5 0.0000012747243711687536 +armv7neon_mmm_f32_8x4_cortexa7 25 4 4 0.000002551159854962235 +armv7neon_mmm_f32_8x4_cortexa9 8 128 13 0.000019869699675831398 +armv7neon_mmm_f32_8x6_cortexa9 9 128 19 0.00005342392770358122 +armv7neon_mmm_f32_8x6_cortexa9 7 4 12 0.000002051630860797947 +armv7neon_mmm_f32_8x6_cortexa9 16 128 13 0.00004015173417099066 +armv7neon_mmm_f32_8x6_cortexa9 23 128 12 0.0000403164031404094 +armv7neon_mmm_f32_8x4_generic 23 128 5 0.000028627967286799668 +armv7neon_mmm_f32_8x4_cortexa9 25 4 8 0.000004634829587692317 +armv7neon_mmm_f32_8x4_cortexa7 15 128 4 0.000010126569600218532 +armv7neon_mmm_f32_8x4_cortexa9 23 32 4 0.000005123975664670012 +armv7neon_mmm_f32_8x6_generic 8 4 17 0.0000024242275965441413 +armv7neon_mmm_f32_8x6_cortexa7 25 128 7 0.000052727920949761586 +armv7neon_mmm_f32_8x4_cortexa7 16 32 9 0.000009437089513621594 +armv7neon_mmm_f32_8x4_generic 23 4 9 0.000005734226973840947 +armv7neon_mmm_f32_8x4_cortexa7 17 128 12 0.0000433599443382883 +armv7neon_mmm_f32_8x4_cortexa7 24 32 3 0.000005409349524302138 +armv7neon_mmm_f32_8x6_cortexa7 8 4 7 0.0000018575873013342354 +armv7neon_mmm_f32_8x6_generic 7 128 19 0.000026452799909726964 +armv7neon_mmm_f32_8x4_cortexa9 24 32 12 0.000013540981249723916 +armv7neon_mmm_f32_8x4_cortexa9 23 128 9 0.000044894692860566556 +armv7neon_mmm_f32_8x6_cortexa9 9 4 17 0.000004505536489134122 +armv7neon_mmm_f32_8x6_generic 15 32 11 0.000008507986633036132 +armv7neon_mmm_f32_8x6_cortexa7 7 32 19 0.000008805049700237493 +armv7neon_mmm_f32_8x4_generic 17 4 4 0.0000020583169447652696 +armv7neon_mmm_f32_8x4_generic 7 32 9 0.000005237513207003328 +armv7neon_mmm_f32_8x6_cortexa9 8 4 5 0.0000012863007612366299 +armv7neon_mmm_f32_8x6_cortexa7 16 128 17 0.00003972939922069338 +armv7neon_mmm_f32_8x6_cortexa9 16 32 17 0.000012476036240479964 +generic_f32_4x4 5 128 4 0.000007343673936530293 +armv7neon_mmm_f32_8x6_cortexa7 17 4 13 0.000006264666040125929 +armv7neon_mmm_f32_8x6_cortexa9 25 128 18 0.00007930397959821057 +armv7neon_mmm_f32_8x6_cortexa9 16 4 5 0.0000020584584410478216 +armv7neon_mmm_f32_8x4_cortexa9 7 32 5 0.00000378972657151533 +armv7neon_mmm_f32_8x6_cortexa9 16 4 18 0.000003945842563203367 +armv7neon_mmm_f32_8x4_generic 23 32 7 0.000009665874611953724 +armv7neon_mmm_f32_8x6_cortexa7 25 128 6 0.000026459671946913376 +generic_f32_4x4 7 4 3 0.0000017591858474192314 +armv7neon_mmm_f32_8x6_cortexa7 16 4 18 0.000003943668999181098 +generic_f32_4x4 4 128 7 0.0000074032510257738404 +armv7neon_mmm_f32_8x4_generic 16 128 13 0.0000374668929079076 +armv7neon_mmm_f32_8x4_cortexa9 9 4 3 0.0000017875665004976476 +armv7neon_mmm_f32_8x4_cortexa9 15 128 9 0.00003010940924012135 +armv7neon_mmm_f32_8x6_cortexa9 17 32 13 0.000018295003723366057 +armv7neon_mmm_f32_8x6_cortexa9 9 4 13 0.000004395459186352696 +armv7neon_mmm_f32_8x6_cortexa7 8 4 12 0.0000016616372440035075 +generic_f32_4x4 5 128 12 0.000021001699183635672 +armv7neon_mmm_f32_8x6_generic 25 4 6 0.000002871872138224116 +armv7neon_mmm_f32_8x6_generic 7 128 5 0.000006875620566369796 +generic_f32_4x4 5 4 4 0.000001600585638499612 +armv7neon_mmm_f32_8x4_cortexa9 23 4 4 0.0000022070594896923405 +armv7neon_mmm_f32_8x6_cortexa9 8 32 17 0.0000065161951430999815 +armv7neon_mmm_f32_8x6_cortexa7 17 4 19 0.000008057471663557758 +armv7neon_mmm_f32_8x6_cortexa9 8 128 7 0.000013701107804035602 +armv7neon_mmm_f32_8x4_cortexa9 23 128 11 0.00004502007320686303 +armv7neon_mmm_f32_8x4_cortexa7 8 32 11 0.000004991462488279611 +armv7neon_mmm_f32_8x6_cortexa9 24 32 18 0.00001769207861513086 +armv7neon_mmm_f32_8x4_generic 25 128 4 0.000019508351532460557 +armv7neon_mmm_f32_8x6_cortexa7 23 4 12 0.0000044400455773711846 +armv7neon_mmm_f32_8x6_cortexa9 24 32 5 0.000006843511403564935 +armv7neon_mmm_f32_8x6_cortexa9 25 128 11 0.000053747765759359946 +armv7neon_mmm_f32_8x4_cortexa7 25 4 11 0.000007253341541711691 +armv7neon_mmm_f32_8x6_cortexa9 23 32 19 0.000024549141146277552 +armv7neon_mmm_f32_8x6_cortexa7 16 128 11 0.000026732044849478285 +armv7neon_mmm_f32_8x6_cortexa9 23 128 19 0.0000801357471530611 +generic_f32_4x4 3 32 4 0.0000017966908912230104 +armv7neon_mmm_f32_8x6_cortexa7 24 128 17 0.00005929496892410179 +armv7neon_mmm_f32_8x6_cortexa9 23 32 12 0.000012490651961414628 +armv7neon_mmm_f32_8x4_cortexa7 16 128 9 0.000029084669529414875 +armv7neon_mmm_f32_8x6_cortexa7 7 128 19 0.000026966388966864694 +armv7neon_mmm_f32_8x4_cortexa7 23 32 8 0.000009566852853696024 +armv7neon_mmm_f32_8x4_generic 17 32 13 0.00001785315018140605 +armv7neon_mmm_f32_8x4_cortexa7 23 4 4 0.00000218193596958567 +armv7neon_mmm_f32_8x4_generic 9 32 7 0.000006462010776076977 +armv7neon_mmm_f32_8x4_generic 23 32 8 0.000009259693411977238 +armv7neon_mmm_f32_8x6_generic 15 128 12 0.000025997314252108204 +generic_f32_4x4 12 32 5 0.000007719446523382617 +armv7neon_mmm_f32_8x6_cortexa9 15 128 17 0.000040755037497853366 +armv7neon_mmm_f32_8x4_generic 24 32 5 0.00000927870929356689 +armv7neon_mmm_f32_8x4_cortexa9 23 32 11 0.00001470624762457902 +armv7neon_mmm_f32_8x4_cortexa7 7 128 7 0.000010321518649139075 +armv7neon_mmm_f32_8x4_cortexa9 17 32 3 0.000005401396003956081 +armv7neon_mmm_f32_8x4_cortexa7 25 128 5 0.00003910615015651058 +armv7neon_mmm_f32_8x6_cortexa9 17 128 18 0.00005978117998553773 +armv7neon_mmm_f32_8x6_generic 7 4 11 0.000002036996116605385 +armv7neon_mmm_f32_8x6_cortexa9 15 4 6 0.0000019083928751646085 +armv7neon_mmm_f32_8x6_cortexa9 9 4 19 0.000005613783088587898 +armv7neon_mmm_f32_8x4_cortexa7 24 32 4 0.0000047895509427620865 +armv7neon_mmm_f32_8x4_cortexa9 8 32 12 0.000004854203771648976 +armv7neon_mmm_f32_8x4_generic 25 4 11 0.000007170056956827459 +armv7neon_mmm_f32_8x6_cortexa7 24 32 6 0.000006172583934492533 +armv7neon_mmm_f32_8x6_generic 23 32 18 0.000017730914760808268 +generic_f32_4x4 9 128 4 0.00001072609031817021 +armv7neon_mmm_f32_8x6_generic 16 32 7 0.000008131169126293927 +armv7neon_mmm_f32_8x6_cortexa9 8 32 11 0.000004596930720411251 +generic_f32_4x4 3 128 3 0.000003987497974732173 +armv7neon_mmm_f32_8x6_generic 16 32 18 0.000011518507884431023 +generic_f32_4x4 12 4 13 0.0000067677797185944386 +armv7neon_mmm_f32_8x6_cortexa7 17 4 12 0.00000420578831765641 +armv7neon_mmm_f32_8x6_cortexa7 25 128 11 0.00005298746347142357 +armv7neon_mmm_f32_8x6_cortexa9 17 4 17 0.0000064380265240168815 +armv7neon_mmm_f32_8x4_cortexa9 16 32 3 0.000003850508528044626 +armv7neon_mmm_f32_8x4_cortexa7 15 4 9 0.0000041824910345443945 +armv7neon_mmm_f32_8x6_cortexa7 16 4 13 0.000004283476073931167 +armv7neon_mmm_f32_8x6_cortexa9 15 32 19 0.00001676006214460718 +armv7neon_mmm_f32_8x6_cortexa9 9 4 6 0.0000017882273242368877 +generic_f32_4x4 11 4 12 0.000005385894826689573 +armv7neon_mmm_f32_8x6_generic 25 128 11 0.00005279747695062305 +armv7neon_mmm_f32_8x4_generic 25 128 11 0.000057163651606893397 +armv7neon_mmm_f32_8x6_generic 16 32 13 0.000011809288098621582 +armv7neon_mmm_f32_8x6_cortexa7 16 128 7 0.00002656376898311115 +armv7neon_mmm_f32_8x6_generic 8 4 19 0.0000028980842866481956 +armv7neon_mmm_f32_8x6_cortexa9 25 4 17 0.000008360160748075129 +armv7neon_mmm_f32_8x6_cortexa7 25 32 17 0.00002416762541107772 +armv7neon_mmm_f32_8x4_cortexa7 9 4 11 0.000003971891623914232 +armv7neon_mmm_f32_8x6_cortexa7 8 128 18 0.000019766077162036943 +armv7neon_mmm_f32_8x4_generic 15 32 3 0.0000036533992903578352 +armv7neon_mmm_f32_8x6_cortexa7 25 32 5 0.0000087944366611451 +armv7neon_mmm_f32_8x6_cortexa7 7 128 5 0.000007076691588612229 +armv7neon_mmm_f32_8x6_generic 23 4 6 0.000002427909563551317 +armv7neon_mmm_f32_8x4_cortexa9 17 4 9 0.000005573474441160942 +armv7neon_mmm_f32_8x4_cortexa9 25 4 5 0.000005112325491179266 +generic_f32_4x4 8 32 5 0.000005323391922801219 +armv7neon_mmm_f32_8x6_cortexa7 24 128 18 0.00005856236723783224 +armv7neon_mmm_f32_8x6_generic 23 4 18 0.000006215918837505929 +armv7neon_mmm_f32_8x4_generic 16 32 4 0.000003260594187485516 +armv7neon_mmm_f32_8x4_generic 8 4 11 0.000002124764231070715 +armv7neon_mmm_f32_8x6_cortexa9 15 128 7 0.000027104995655962794 +armv7neon_mmm_f32_8x4_cortexa7 23 4 3 0.000002539994674766333 +armv7neon_mmm_f32_8x4_cortexa9 15 128 13 0.000040120613837028105 +armv7neon_mmm_f32_8x4_generic 9 32 5 0.000006416826225047117 +armv7neon_mmm_f32_8x6_generic 9 128 13 0.000039231938860404315 +armv7neon_mmm_f32_8x4_cortexa7 17 32 9 0.000014080751383917914 +armv7neon_mmm_f32_8x4_generic 24 4 5 0.0000038475341959329615 +armv7neon_mmm_f32_8x6_cortexa7 17 32 19 0.00002385320479583871 +armv7neon_mmm_f32_8x4_cortexa9 25 4 7 0.000005243554539274239 +armv7neon_mmm_f32_8x4_cortexa9 8 128 7 0.000010306643098752432 +armv7neon_mmm_f32_8x4_cortexa7 9 32 9 0.000009612025649062918 +armv7neon_mmm_f32_8x6_generic 7 128 6 0.000006918233017670394 +armv7neon_mmm_f32_8x6_cortexa7 23 32 6 0.000006480642874727307 +armv7neon_mmm_f32_8x4_cortexa7 17 4 8 0.0000036467312614380765 +armv7neon_mmm_f32_8x6_cortexa9 9 4 18 0.000004263423188010347 +armv7neon_mmm_f32_8x4_cortexa9 23 4 3 0.000002565453674896801 +armv7neon_mmm_f32_8x6_cortexa7 17 128 17 0.00005934218053895787 +armv7neon_mmm_f32_8x6_cortexa9 9 128 11 0.00002703084778716899 +armv7neon_mmm_f32_8x6_cortexa7 8 32 19 0.000008254990878887053 +armv7neon_mmm_f32_8x4_cortexa7 24 4 11 0.000005437311620791031 +armv7neon_mmm_f32_8x6_cortexa7 17 32 12 0.000012103957972190128 +armv7neon_mmm_f32_8x6_cortexa7 16 32 11 0.000008575915963542344 +armv7neon_mmm_f32_8x6_cortexa7 15 128 13 0.00003995376024317136 +armv7neon_mmm_f32_8x4_cortexa7 25 128 12 0.00005747997493377876 +armv7neon_mmm_f32_8x6_cortexa7 9 4 17 0.000004513992669521213 +armv7neon_mmm_f32_8x4_cortexa9 7 32 11 0.000005542086596158504 +armv7neon_mmm_f32_8x4_generic 16 32 8 0.00000600791906169857 +armv7neon_mmm_f32_8x6_cortexa9 8 4 6 0.000001079992540189233 +armv7neon_mmm_f32_8x6_cortexa7 15 4 7 0.0000033103105460849674 +armv7neon_mmm_f32_8x6_generic 7 4 18 0.0000027798204465348195 +armv7neon_mmm_f32_8x4_cortexa9 9 4 4 0.0000016086729123039774 +armv7neon_mmm_f32_8x4_cortexa9 17 4 13 0.000007139907020236834 +armv7neon_mmm_f32_8x6_generic 7 4 17 0.000002811022144491351 +armv7neon_mmm_f32_8x4_generic 16 4 12 0.00000330778427406012 +armv7neon_mmm_f32_8x6_cortexa7 17 32 7 0.000012331946306204285 +armv7neon_mmm_f32_8x4_cortexa7 23 128 4 0.000014866985895709814 +armv7neon_mmm_f32_8x6_generic 8 128 5 0.000006930294425648014 +armv7neon_mmm_f32_8x6_cortexa7 7 128 11 0.000013755100434106755 +armv7neon_mmm_f32_8x4_cortexa7 9 4 3 0.0000017694089316929863 +armv7neon_mmm_f32_8x4_cortexa9 25 128 5 0.00003989811331140034 +armv7neon_mmm_f32_8x6_cortexa9 24 4 18 0.000005659337689416026 +armv7neon_mmm_f32_8x6_cortexa7 7 32 7 0.000004615694133689407 +armv7neon_mmm_f32_8x4_generic 23 4 12 0.000005414200630328292 +armv7neon_mmm_f32_8x6_cortexa7 15 128 7 0.00002674534329947021 +armv7neon_mmm_f32_8x4_cortexa9 23 128 13 0.0000596298628444228 +armv7neon_mmm_f32_8x6_cortexa9 7 32 19 0.000008889380829716555 +armv7neon_mmm_f32_8x6_cortexa7 8 4 17 0.0000025025489216272165 +armv7neon_mmm_f32_8x6_cortexa9 24 128 5 0.000020677151329306246 +armv7neon_mmm_f32_8x6_cortexa9 17 32 17 0.000018491383989214777 +armv7neon_mmm_f32_8x4_cortexa7 8 4 11 0.000002146399138479012 +armv7neon_mmm_f32_8x6_cortexa7 7 4 19 0.0000034947036576981197 +armv7neon_mmm_f32_8x6_cortexa7 15 32 13 0.00001261074648998989 +armv7neon_mmm_f32_8x6_cortexa7 17 128 12 0.000039467248529014934 +armv7neon_mmm_f32_8x6_cortexa9 15 128 13 0.0000405486222699268 +armv7neon_mmm_f32_8x6_cortexa7 8 4 6 0.0000010838257676938484 +armv7neon_mmm_f32_8x4_cortexa7 8 128 11 0.00001478367268084039 +generic_f32_4x4 13 4 5 0.000005005970941882336 +armv7neon_mmm_f32_8x4_generic 9 4 5 0.0000027981911123646093 +armv7neon_mmm_f32_8x4_cortexa9 24 32 3 0.000005499754513818659 +armv7neon_mmm_f32_8x4_cortexa9 16 32 8 0.00000632372871965183 +armv7neon_mmm_f32_8x6_generic 25 32 7 0.000015750403152655726 +armv7neon_mmm_f32_8x6_cortexa7 9 128 6 0.000013469652928515112 +generic_f32_4x4 5 128 8 0.000014178354640152658 +armv7neon_mmm_f32_8x4_cortexa9 25 32 13 0.00002471922478461092 +armv7neon_mmm_f32_8x4_generic 15 32 7 0.000006693321105057111 +armv7neon_mmm_f32_8x4_cortexa9 23 128 8 0.000029940232861406133 +armv7neon_mmm_f32_8x4_cortexa7 7 128 5 0.000010257823038892243 +generic_f32_4x4 4 32 12 0.000003911119050209719 +armv7neon_mmm_f32_8x6_cortexa7 8 128 7 0.000013528068455942153 +armv7neon_mmm_f32_8x4_cortexa7 24 4 5 0.000003893712023496801 +armv7neon_mmm_f32_8x4_cortexa7 9 4 5 0.00000282867688987085 +armv7neon_mmm_f32_8x6_generic 9 4 6 0.0000017337687872243595 +armv7neon_mmm_f32_8x4_generic 8 32 11 0.000004839072297522764 +armv7neon_mmm_f32_8x4_cortexa7 16 32 8 0.000006215610136029107 +armv7neon_mmm_f32_8x4_cortexa7 24 128 12 0.00004296408794693615 +armv7neon_mmm_f32_8x6_generic 23 32 7 0.000012139369977001298 +armv7neon_mmm_f32_8x6_cortexa9 23 32 7 0.00001262657347999726 +armv7neon_mmm_f32_8x6_generic 9 32 18 0.000011811540149507637 +generic_f32_4x4 4 4 12 0.0000019950687694365225 +armv7neon_mmm_f32_8x4_cortexa9 17 32 13 0.000018760347719013833 +armv7neon_mmm_f32_8x4_generic 24 128 8 0.000027893007985374452 +armv7neon_mmm_f32_8x6_generic 7 32 12 0.000004596558109391685 +generic_f32_4x4 12 32 12 0.0000107075710637508 +armv7neon_mmm_f32_8x6_generic 16 4 18 0.000003888078280974674 +generic_f32_4x4 12 128 7 0.000021189568041458556 +armv7neon_mmm_f32_8x6_generic 16 128 11 0.000025972024072475202 +armv7neon_mmm_f32_8x6_cortexa7 15 4 13 0.000004683720683139814 +armv7neon_mmm_f32_8x6_cortexa9 7 128 6 0.000007213870080063144 +armv7neon_mmm_f32_8x6_cortexa9 23 128 11 0.00004071170407195971 +armv7neon_mmm_f32_8x6_generic 24 4 5 0.000002793903924978298 +armv7neon_mmm_f32_8x4_cortexa9 15 32 9 0.000010036115634563751 +generic_f32_4x4 9 128 9 0.000031422044792444864 +armv7neon_mmm_f32_8x4_generic 25 32 4 0.000006133898665196312 +generic_f32_4x4 13 128 8 0.00002772292203595808 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32/cortex_a9.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32/cortex_a9.rs new file mode 100644 index 0000000..23115c9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32/cortex_a9.rs @@ -0,0 +1,861 @@ +use crate::frame::mmm::CostModel; +pub fn model() -> CostModel<'static> { + CostModel { + big_product_mkn_threshold: 4194036.0, + big_product_kernel_choice: "armv7neon_mmm_f32_8x6_cortexa9", + kernels: &[ + "armv7neon_mmm_f32_8x4_cortexa7", + "armv7neon_mmm_f32_8x4_cortexa9", + "armv7neon_mmm_f32_8x4_generic", + "armv7neon_mmm_f32_8x6_cortexa7", + "armv7neon_mmm_f32_8x6_cortexa9", + "armv7neon_mmm_f32_8x6_generic", + "generic_f32_4x4", + ], + mrs: &[4, 8], + nrs: &[4, 6], + feat_norm_mean: &[ + 4.582296677813486, + 4.595402322442016, + 4.571260231028445, + 13.748959231283994, + 1.5179177668804225, + 0.7575757575757576, + 3.5337608449641644, + 0.8831887338111405, + 1.5048409405255878, + 0.7526719476926946, + 2.489123601156796, + 0.8326417704011065, + ], + feat_norm_stddev: &[ + 1.2635817489024164, + 1.2723436827339079, + 1.2620157548883217, + 1.3497763942449361, + 1.1141159992246472, + 0.42854956435545316, + 2.2880460409304937, + 0.32119525880720723, + 1.1154901716833412, + 0.43145902105435263, + 1.7051378780434328, + 0.37329539587896904, + ], + w1: &[ + 0.5391961336135864, + -0.32089367508888245, + 0.203999862074852, + -0.10011337697505951, + 0.09040801972150803, + -0.14198464155197144, + 0.031854499131441116, + 0.12334256619215012, + 0.15339604020118713, + -0.20091375708580017, + -0.014548280276358128, + 0.12154694646596909, + 0.31225234270095825, + 0.10782113671302795, + 0.44618168473243713, + 0.8267014026641846, + -0.1204405128955841, + -0.08261110633611679, + -0.052502430975437164, + 0.3066086769104004, + 0.1493932157754898, + -0.14119412004947662, + -0.1985343098640442, + 0.19361039996147156, + -0.4636686146259308, + 0.08120443671941757, + 0.03210291638970375, + 0.17303235828876495, + 0.16502155363559723, + -0.19771894812583923, + -0.11060577630996704, + 0.08698348701000214, + -0.07793445140123367, + 0.32749465107917786, + 0.3663202226161957, + -0.4629170894622803, + -0.1586134433746338, + 0.4272242486476898, + -0.12016090005636215, + -0.17830348014831543, + -0.05493386462330818, + -0.036517318338155746, + 0.01293050218373537, + 0.016577009111642838, + 0.10738552361726761, + -0.3662779629230499, + -0.2917434275150299, + 0.5752639770507812, + 0.11406347155570984, + 0.8622727394104004, + 0.07158719748258591, + 0.29530274868011475, + -0.11287810653448105, + 0.12262264639139175, + 0.02478562481701374, + 0.17749948799610138, + -0.036227867007255554, + 0.10140471905469894, + -0.011896232143044472, + -0.021761735901236534, + 0.06046223267912865, + 0.5727048516273499, + -0.007826486602425575, + 0.3863913118839264, + -0.04224887117743492, + 0.056023009121418, + -0.02467598207294941, + 0.0385640449821949, + 0.0219524335116148, + -0.03437826409935951, + -0.2060588151216507, + 0.2895224988460541, + 0.10751669108867645, + 0.00845037866383791, + -0.1836385875940323, + -0.24757762253284454, + -0.09606243669986725, + 0.03918633610010147, + 0.07913251221179962, + 0.06499160826206207, + -0.08156774938106537, + 0.08835449814796448, + 0.13896305859088898, + -0.16936920583248138, + 0.010146846994757652, + -0.42553824186325073, + 0.39916151762008667, + -0.004584060981869698, + -0.10256388038396835, + 0.041573416441679, + 0.05155385658144951, + 0.015019520185887814, + 0.09554271399974823, + -0.20487457513809204, + -0.4146610200405121, + -0.773110032081604, + 0.3662724494934082, + -0.23762361705303192, + 0.6974321603775024, + 0.8990052938461304, + 0.02772649936378002, + 0.042197681963443756, + -0.0022736566606909037, + -0.028843341395258904, + -0.4559306204319, + 0.6326258778572083, + 0.4568879008293152, + -0.4892531633377075, + -0.032289132475852966, + 0.04378330707550049, + -0.4118069112300873, + 0.2493579089641571, + -0.021955665200948715, + -0.01538186427205801, + -0.21400974690914154, + -0.09971866756677628, + 0.02185226045548916, + -0.18125569820404053, + -0.13828244805335999, + -0.20846466720104218, + -0.10373540222644806, + 0.4842098653316498, + -0.06586655229330063, + 0.03369470313191414, + 0.013142148964107037, + 0.017437899485230446, + 0.15891534090042114, + 0.5269678831100464, + 0.02546108327805996, + -0.004250233061611652, + -5.8676625485531986e-05, + 0.06777831166982651, + -0.14051207900047302, + 0.6876491904258728, + -0.3455996811389923, + 0.0378129817545414, + 0.15291574597358704, + -0.03829087316989899, + -0.05761529877781868, + -0.05344394966959953, + 0.1421334147453308, + -0.3614322543144226, + -0.21606910228729248, + 0.1558765172958374, + 0.14480257034301758, + -0.1799984872341156, + 0.4238421618938446, + -0.08961529284715652, + -0.04010967165231705, + 0.14250615239143372, + -0.0038367861416190863, + -0.044531334191560745, + -0.08958051353693008, + -0.1577986180782318, + -0.5795103907585144, + -1.1048516035079956, + 0.16444185376167297, + -0.09989812225103378, + -0.26304998993873596, + 0.040687527507543564, + 0.065303735435009, + -0.06267901510000229, + 0.08742637187242508, + 0.02480895072221756, + 0.23719966411590576, + -0.09509539604187012, + 0.39278310537338257, + 0.18978112936019897, + 0.11301649361848831, + -0.16268616914749146, + -0.14119602739810944, + -0.04518252611160278, + 0.10456270724534988, + 0.008367948234081268, + 0.004280170891433954, + 0.01894286274909973, + -0.1547478288412094, + 0.197267547249794, + 0.20271208882331848, + -0.28377917408943176, + -0.26751258969306946, + 0.15954937040805817, + 0.33988064527511597, + 0.16848208010196686, + 0.11668887734413147, + -0.057433612644672394, + -0.049777109175920486, + 0.00744214653968811, + -0.012330793775618076, + -0.08413149416446686, + -0.2053118497133255, + 0.09235486388206482, + -0.1354941576719284, + 0.41610953211784363, + 0.8428494334220886, + 0.880882740020752, + 0.024029193446040154, + -0.08453702926635742, + 0.00771496444940567, + -0.013013732619583607, + -0.23804998397827148, + 0.4110376536846161, + 0.23720477521419525, + -0.13951541483402252, + -0.1747516244649887, + -0.34215790033340454, + 0.014357345178723335, + 0.34224632382392883, + 0.03783192113041878, + 0.01125166192650795, + -0.08253959566354752, + 0.015717405825853348, + -0.22759634256362915, + 0.3980898857116699, + 0.2427154779434204, + -0.3319437801837921, + 0.11146843433380127, + -0.9666317105293274, + -0.12227121740579605, + -0.1948898285627365, + -0.030186548829078674, + 0.0011711223050951958, + -0.040062546730041504, + -0.16316139698028564, + -0.14714862406253815, + 0.13224393129348755, + -0.0019320327555760741, + -0.09674090147018433, + 0.3630145490169525, + -0.019513679668307304, + -0.07729464769363403, + -0.34592965245246887, + 0.15215164422988892, + 0.046678490936756134, + 0.06675180792808533, + -0.08943335711956024, + 0.006386714521795511, + 0.10086977481842041, + -0.07409387081861496, + -0.19604018330574036, + -0.042700666934251785, + 0.12124726921319962, + 0.5694677233695984, + 0.25033196806907654, + 0.01862989366054535, + 0.0053687929175794125, + -0.0017405126709491014, + -0.01638556271791458, + -0.32222822308540344, + 0.5348804593086243, + 0.5546748042106628, + 1.2770946025848389, + 0.11648745834827423, + -0.058405984193086624, + -0.2997635006904602, + -0.2040756195783615, + 0.15525077283382416, + -0.12436354905366898, + -0.089121975004673, + 0.06441225856542587, + 0.2444663643836975, + -0.3495825529098511, + -0.05243751034140587, + 0.08752834796905518, + 0.08800745010375977, + -0.09807545691728592, + -0.3823537230491638, + -0.13047000765800476, + 0.029333092272281647, + 0.11618250608444214, + -0.0638590008020401, + -0.09598273783922195, + -0.07390140742063522, + 0.09151650220155716, + -0.1700282245874405, + 0.23608872294425964, + 0.24879834055900574, + -0.15922772884368896, + -0.33795130252838135, + -0.053850702941417694, + 0.1014639139175415, + -0.05480973795056343, + -0.06753639131784439, + 0.04606246575713158, + -0.07082260400056839, + 0.07848796248435974, + 0.05011916160583496, + -0.05570689216256142, + -0.14584510028362274, + -0.8908579349517822, + -0.5959509611129761, + -0.8982105255126953, + 0.0788002535700798, + -0.03575791418552399, + 0.052424680441617966, + -0.08019822835922241, + 0.10848221182823181, + 0.0957408994436264, + 0.1457311511039734, + -0.1956494003534317, + -0.21669772267341614, + 0.9854136109352112, + -0.23215851187705994, + 0.16359730064868927, + 0.02025810070335865, + -0.08975380659103394, + -0.013868067413568497, + -0.22188447415828705, + 0.020666224882006645, + -0.22304703295230865, + 0.06407633423805237, + 0.19804184138774872, + -0.05285267159342766, + -0.5510660409927368, + -0.8522927761077881, + -0.6061599850654602, + 0.08484024554491043, + -0.08973539620637894, + 0.013228937052190304, + -0.07834818214178085, + 0.02858446165919304, + -0.3826225996017456, + 0.059726644307374954, + 0.1139102503657341, + -0.19311848282814026, + 0.05770142376422882, + 0.22584261000156403, + 0.34312352538108826, + -0.15085645020008087, + 0.34372228384017944, + 0.08070214092731476, + 0.5744000673294067, + -0.08693907409906387, + -0.003695777617394924, + -0.1334235966205597, + 0.06418291479349136, + 0.02848576195538044, + -0.34958112239837646, + -0.3419312834739685, + -0.09599799662828445, + 0.015022341161966324, + 0.03255023807287216, + 0.09713662415742874, + -0.1730588674545288, + 0.1904430240392685, + -0.32815566658973694, + -0.16749203205108643, + 0.35736411809921265, + -0.503787100315094, + 0.5057004690170288, + -0.47198373079299927, + 0.11386436969041824, + -0.0722493901848793, + 0.03358639404177666, + 0.005928087048232555, + -0.05637047439813614, + 0.06552420556545258, + -0.07283362001180649, + -0.09314802289009094, + 0.13586974143981934, + -0.5054865479469299, + -0.18127793073654175, + 0.08853171765804291, + -0.13333705067634583, + -0.2623322308063507, + 0.17757390439510345, + 0.04408252611756325, + -0.0277855321764946, + -0.05175777152180672, + 0.40444689989089966, + -0.03518976643681526, + -0.36402902007102966, + -0.019589770585298538, + -0.05277400091290474, + -0.27273234724998474, + -0.07373850792646408, + -0.058221735060214996, + 0.14292845129966736, + -0.005004828795790672, + -0.05554938316345215, + 0.20361287891864777, + -0.30462127923965454, + -0.1140812486410141, + 0.16081976890563965, + -0.07133162021636963, + -0.20463652908802032, + 0.34733739495277405, + 0.17099761962890625, + 0.025868643075227737, + -0.02960631065070629, + -0.02717636525630951, + 0.02027258090674877, + -0.13165302574634552, + 0.36201152205467224, + 0.5002728700637817, + 0.39691421389579773, + -0.04605599492788315, + 0.28801581263542175, + -1.0140656232833862, + -0.5481916666030884, + 0.0896061584353447, + -0.049390073865652084, + 0.08813252300024033, + -0.1784677952528, + 0.34480658173561096, + -0.36402803659439087, + 0.16948284208774567, + 0.45740315318107605, + -0.23747704923152924, + 0.580975353717804, + -0.24338461458683014, + -0.11410018056631088, + 0.06431885808706284, + -0.0317281149327755, + -0.024683356285095215, + -0.10083278268575668, + 0.024547407403588295, + -0.16270779073238373, + -0.07757837325334549, + 0.19732129573822021, + 0.03790999948978424, + -0.18804220855236053, + 0.8675169348716736, + 0.5377629399299622, + -0.0036910742055624723, + -0.0016441351035609841, + -0.030448857694864273, + 0.07757671177387238, + -0.1475408971309662, + 0.613543689250946, + 0.30266445875167847, + 0.12106148898601532, + 0.05485830456018448, + -0.04748840630054474, + -0.23233623802661896, + -0.1949906051158905, + 0.05692804977297783, + 0.07474583387374878, + -0.11879625171422958, + 0.07200933247804642, + -0.012743310071527958, + -0.02546215057373047, + -0.3765566349029541, + 0.28637346625328064, + -0.18051809072494507, + 0.5034835934638977, + -0.34970414638519287, + -0.2386687994003296, + -0.03804561868309975, + -0.03649319335818291, + -0.10303670912981033, + 0.1299818456172943, + 0.24685724079608917, + -0.34168556332588196, + -0.086674265563488, + 0.32085898518562317, + 0.48488491773605347, + -0.522548258304596, + 0.309568852186203, + 0.167385995388031, + 0.11308691650629044, + 0.14733079075813293, + -0.22416195273399353, + 0.14763982594013214, + -0.07242503017187119, + 0.07601745426654816, + -0.10375087708234787, + -0.03409396857023239, + -0.35759225487709045, + 0.18936687707901, + 0.28248289227485657, + 0.26482364535331726, + 0.061123836785554886, + -0.021603189408779144, + -0.13469825685024261, + 0.07248867303133011, + -0.03464066982269287, + 0.06557167321443558, + 0.16093865036964417, + -0.1718607246875763, + ], + b1: &[ + -0.3893989324569702, + -0.2791002690792084, + 0.07853052020072937, + -0.4629746377468109, + -0.7148261070251465, + 0.8680436015129089, + -0.46459102630615234, + 0.0404132716357708, + -0.44012945890426636, + 0.08434166759252548, + 0.32190972566604614, + -0.20194832980632782, + -0.3781348764896393, + -0.23968002200126648, + -0.581799328327179, + 0.6500483155250549, + -0.6192854046821594, + 0.5922245383262634, + 0.44006091356277466, + 0.2982949912548065, + 0.6136102676391602, + -0.597486138343811, + -0.3697699308395386, + -0.45241132378578186, + 0.60771644115448, + -0.3373708128929138, + 0.5697194337844849, + 0.4784911870956421, + -0.49601855874061584, + 0.5023709535598755, + 0.21592296659946442, + -0.45412343740463257, + 0.5104787945747375, + 0.558862566947937, + 0.4729066491127014, + -0.5520593523979187, + -0.5120576620101929, + -0.7157037258148193, + 0.12596718966960907, + 0.4773174524307251, + ], + w2: &[ + 0.1379607617855072, + 0.09308824688196182, + -0.2596932649612427, + 0.4461972713470459, + 0.3480601906776428, + 0.036684323102235794, + 0.4057384729385376, + -0.3081648051738739, + 0.4561280608177185, + 0.2749394178390503, + -0.1400817334651947, + 0.3145979046821594, + -0.16919250786304474, + 0.7247185707092285, + 0.3479674756526947, + -0.7546817064285278, + 0.38135531544685364, + -0.3939172029495239, + -0.038021210581064224, + 0.026914050802588463, + -0.5281358361244202, + 0.39009571075439453, + 0.4090450406074524, + 0.5053343772888184, + -0.23938016593456268, + 0.488080233335495, + -0.38536468148231506, + -0.23763014376163483, + 0.2661689519882202, + -0.14746293425559998, + -0.7541974186897278, + 0.27726081013679504, + -0.4072169065475464, + -0.8030230402946472, + -0.386343389749527, + 0.6674754619598389, + 0.06677238643169403, + 0.5055669546127319, + -0.44330647587776184, + -0.3423362970352173, + -0.10948927700519562, + 0.11290912330150604, + -0.2759379744529724, + 0.5522158741950989, + -0.5766478776931763, + 0.7288797497749329, + -0.4967955946922302, + -0.5466133952140808, + 0.7254890203475952, + 0.1274457424879074, + 0.3098924458026886, + 0.2524661719799042, + -0.7162019610404968, + 0.19503603875637054, + -0.5212412476539612, + 0.0968603864312172, + 0.4835629463195801, + -0.5865079164505005, + 0.27647316455841064, + 0.1975109577178955, + -0.845225989818573, + 0.4172143042087555, + -0.014424118213355541, + -0.24702520668506622, + -0.16123531758785248, + -0.047759659588336945, + -0.09985388815402985, + 0.10430619865655899, + 0.53556889295578, + 0.2595883011817932, + 0.11729882657527924, + 0.36996161937713623, + -0.41997936367988586, + -0.3332042694091797, + 0.2527308464050293, + 0.6039140820503235, + 0.35183605551719666, + 0.42042237520217896, + -0.2265913337469101, + -0.06852111965417862, + 0.3749903440475464, + 0.3698897361755371, + -0.43096107244491577, + 0.1275794953107834, + 0.27926334738731384, + -0.3282606303691864, + 0.290679931640625, + -0.14467079937458038, + 0.3357028663158417, + -0.0683436468243599, + -0.35492125153541565, + -0.14275093376636505, + -0.1504347324371338, + 0.1782987266778946, + 0.07464402168989182, + -0.2788643538951874, + 0.5896115303039551, + -0.314520001411438, + -0.3235827684402466, + -0.2899278700351715, + -0.21264874935150146, + 0.41862159967422485, + 0.3237628936767578, + 0.2948566973209381, + -0.6101413369178772, + -0.025511808693408966, + -0.4238346517086029, + -0.28283095359802246, + 0.32077667117118835, + -0.34138476848602295, + -0.5257527232170105, + 0.24129967391490936, + -0.38175472617149353, + -0.20559589564800262, + -0.11267697811126709, + 0.32475054264068604, + 0.29545050859451294, + 0.0010625360300764441, + 0.4097916781902313, + -0.3120468556880951, + 0.3134985566139221, + 0.33620578050613403, + -0.27408266067504883, + -0.0118736382573843, + 0.21356475353240967, + -0.6716119647026062, + 0.14166241884231567, + 0.020748334005475044, + 0.27158322930336, + -0.27066248655319214, + -0.5078546404838562, + 0.39642488956451416, + 0.4044502079486847, + 0.1363500952720642, + 0.38089585304260254, + -0.18438327312469482, + -0.08652642369270325, + 0.05718545988202095, + -0.5758764743804932, + 0.0948563665151596, + 0.298057496547699, + -0.07299521565437317, + -0.24248233437538147, + 0.29135069251060486, + -0.44556060433387756, + 0.6689074039459229, + -0.12930674850940704, + -0.12669484317302704, + 0.1074564978480339, + -0.20472179353237152, + 0.14787982404232025, + -0.13180267810821533, + 0.3045596182346344, + -0.3345180153846741, + -0.3405822217464447, + 0.22327540814876556, + 0.02809770777821541, + 0.17404714226722717, + 0.22873322665691376, + -0.3915692865848541, + -0.39005470275878906, + -0.4675980806350708, + 0.44798821210861206, + -0.31790846586227417, + -0.21734853088855743, + 0.2172199934720993, + -0.3485357165336609, + 0.1241735890507698, + -0.6933310031890869, + -0.09649480134248734, + 0.24731965363025665, + -0.20421941578388214, + 0.13033808767795563, + -0.4282769560813904, + -0.22173112630844116, + 0.08912057429552078, + -0.3927532434463501, + 0.3523387908935547, + 0.36073970794677734, + -0.036902282387018204, + 0.5880261063575745, + -0.29945725202560425, + -0.40845751762390137, + -0.3265145421028137, + 0.370391309261322, + -0.3553546965122223, + 0.5133077502250671, + 0.1800842434167862, + -0.34683868288993835, + 0.28811708092689514, + 0.3033837080001831, + -0.4140017628669739, + 0.4362258017063141, + 0.3689269423484802, + 0.3121638596057892, + -0.3287503123283386, + -0.15226924419403076, + -0.17191028594970703, + -0.10683685541152954, + 0.34219542145729065, + 0.34955963492393494, + 0.22892920672893524, + -0.20123478770256042, + -0.3934169411659241, + 0.25449705123901367, + -0.541163444519043, + 0.21640898287296295, + 0.19343338906764984, + -0.14020974934101105, + 0.010480044409632683, + -0.24229897558689117, + -0.4682120084762573, + 0.02336042746901512, + 0.039344485849142075, + 0.42446646094322205, + -0.3173693120479584, + 0.23609045147895813, + 0.20335273444652557, + -0.19347436726093292, + -0.05698636546730995, + 0.17990583181381226, + 0.30915674567222595, + 0.3115670382976532, + 0.4147215485572815, + -0.38558056950569153, + -0.12379863113164902, + 0.025996098294854164, + -0.3010733425617218, + 0.03275908902287483, + -0.6039671897888184, + 0.06267470866441727, + -0.012677585706114769, + 0.3484704792499542, + 0.24301587045192719, + -0.40881243348121643, + -0.16732162237167358, + 0.190901979804039, + -0.5619192719459534, + 0.30009278655052185, + -0.43359509110450745, + 0.26643550395965576, + 0.5083268880844116, + 0.3491555452346802, + 0.4731655716896057, + 0.6301924586296082, + -0.8111121654510498, + 0.6473397016525269, + -0.001451796037144959, + 0.3649038076400757, + -0.6002859473228455, + -0.41925248503685, + 0.05584913119673729, + 0.7823511362075806, + 0.421135276556015, + 0.5779385566711426, + -0.49475061893463135, + 0.5293950438499451, + -0.45432502031326294, + -0.680946946144104, + -0.3506624102592468, + -0.21028658747673035, + 0.4775547385215759, + 0.25049126148223877, + 0.2707470655441284, + -0.3469635546207428, + 0.5959001779556274, + -0.5623777508735657, + -0.6334168910980225, + 0.4096938669681549, + -0.3921370208263397, + -0.27649807929992676, + 0.4424516260623932, + -0.28308066725730896, + -0.22009265422821045, + -0.386872798204422, + 0.5130718350410461, + 0.5702601075172424, + 0.7469420433044434, + -0.09606175124645233, + -0.4271978437900543, + ], + b2: &[ + -0.07522959262132645, + 0.3644154667854309, + -0.25166040658950806, + -0.12973527610301971, + 0.25026997923851013, + -0.2794199585914612, + -0.17614373564720154, + ], + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32/cortex_a9.txt b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32/cortex_a9.txt new file mode 100644 index 0000000..201cd54 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm32/cortex_a9.txt @@ -0,0 +1,1701 @@ +armv7neon_mmm_f32_8x6_generic 17 128 19 0.00006235573582381347 +armv7neon_mmm_f32_8x4_cortexa7 23 32 3 0.000006021597781788675 +armv7neon_mmm_f32_8x6_cortexa7 17 128 7 0.000041163109831630036 +armv7neon_mmm_f32_8x6_generic 9 4 5 0.0000020753617625129768 +generic_f32_4x4 13 4 3 0.000003220368712907131 +armv7neon_mmm_f32_8x4_cortexa7 9 128 3 0.000011219671010907719 +armv7neon_mmm_f32_8x6_generic 24 4 12 0.00000416032880372066 +armv7neon_mmm_f32_8x6_cortexa9 15 128 12 0.00002134037524275856 +armv7neon_mmm_f32_8x4_cortexa7 16 32 7 0.000007487730700761545 +armv7neon_mmm_f32_8x4_generic 25 128 4 0.000015723210051937644 +armv7neon_mmm_f32_8x6_cortexa9 15 128 13 0.000031729639665247244 +armv7neon_mmm_f32_8x4_cortexa7 17 128 5 0.00003233807616782481 +generic_f32_4x4 5 4 9 0.000004114340189363069 +armv7neon_mmm_f32_8x4_cortexa9 7 4 12 0.0000026805076801341797 +armv7neon_mmm_f32_8x6_cortexa7 8 4 7 0.0000020191503624854738 +generic_f32_4x4 5 32 4 0.0000030555103653558445 +armv7neon_mmm_f32_8x4_cortexa7 8 128 12 0.000016128094616247412 +generic_f32_4x4 13 128 8 0.000029057855790622486 +armv7neon_mmm_f32_8x6_cortexa7 23 128 5 0.00002125083853467516 +generic_f32_4x4 5 128 4 0.000007724128084704853 +generic_f32_4x4 9 128 5 0.000022305019807747277 +armv7neon_mmm_f32_8x6_generic 23 32 11 0.000011268391904458938 +armv7neon_mmm_f32_8x4_generic 7 4 12 0.0000026796237449338948 +armv7neon_mmm_f32_8x4_cortexa9 8 32 11 0.000004490236277399054 +armv7neon_mmm_f32_8x6_cortexa7 23 4 7 0.000005431543985839428 +armv7neon_mmm_f32_8x4_cortexa9 7 32 11 0.000004893302315959357 +armv7neon_mmm_f32_8x6_generic 17 4 6 0.0000024671248311817606 +armv7neon_mmm_f32_8x6_cortexa7 7 4 7 0.0000023786534435590155 +armv7neon_mmm_f32_8x6_cortexa9 23 32 7 0.000011142179078429717 +armv7neon_mmm_f32_8x6_cortexa7 23 128 18 0.00006187753967619513 +armv7neon_mmm_f32_8x4_cortexa9 17 4 13 0.000007583246734362711 +armv7neon_mmm_f32_8x4_cortexa7 25 32 8 0.000013742616132230942 +armv7neon_mmm_f32_8x4_cortexa9 15 128 13 0.00003174823725370825 +armv7neon_mmm_f32_8x6_cortexa7 8 32 17 0.0000068525292311713665 +armv7neon_mmm_f32_8x4_generic 23 4 12 0.000005946433902061308 +armv7neon_mmm_f32_8x4_generic 7 32 12 0.000004913908866329809 +armv7neon_mmm_f32_8x4_generic 9 4 5 0.0000029635862825605913 +armv7neon_mmm_f32_8x6_generic 24 128 7 0.00003097026633557495 +armv7neon_mmm_f32_8x4_generic 24 4 13 0.000007343587112941961 +armv7neon_mmm_f32_8x4_generic 23 4 8 0.000004129384710709428 +generic_f32_4x4 11 128 8 0.00002206478635932785 +armv7neon_mmm_f32_8x4_generic 8 4 9 0.000002203244153911471 +armv7neon_mmm_f32_8x4_cortexa9 17 32 3 0.000004810722960669039 +armv7neon_mmm_f32_8x4_cortexa7 7 128 12 0.000016707563395180685 +armv7neon_mmm_f32_8x6_cortexa7 7 4 12 0.000002638802872509435 +armv7neon_mmm_f32_8x4_cortexa7 17 128 9 0.0000480527815279415 +armv7neon_mmm_f32_8x4_cortexa7 7 128 7 0.000011282871511736955 +armv7neon_mmm_f32_8x4_cortexa9 16 4 5 0.000002910382588603331 +armv7neon_mmm_f32_8x4_generic 17 128 13 0.00004671426916376633 +armv7neon_mmm_f32_8x4_cortexa7 9 128 8 0.00002150638840328972 +armv7neon_mmm_f32_8x4_cortexa9 25 32 12 0.000015994893141833952 +armv7neon_mmm_f32_8x4_cortexa7 23 32 11 0.000016244020387064702 +armv7neon_mmm_f32_8x6_cortexa9 7 4 11 0.000002387141196478666 +armv7neon_mmm_f32_8x6_cortexa7 7 4 5 0.0000013875129728218855 +armv7neon_mmm_f32_8x4_cortexa7 15 32 4 0.0000039730943727200555 +armv7neon_mmm_f32_8x4_cortexa7 24 32 3 0.000006034875853921332 +armv7neon_mmm_f32_8x4_cortexa9 8 4 5 0.0000016776748569042105 +armv7neon_mmm_f32_8x6_cortexa7 16 4 18 0.0000045013629937264245 +armv7neon_mmm_f32_8x4_cortexa9 8 128 8 0.000008024678845849025 +armv7neon_mmm_f32_8x6_cortexa9 25 4 18 0.000008301730396887936 +armv7neon_mmm_f32_8x4_generic 7 32 11 0.000004892898505691065 +armv7neon_mmm_f32_8x4_cortexa9 16 128 4 0.000008030995808345325 +armv7neon_mmm_f32_8x4_cortexa7 16 32 3 0.000004194758783711011 +armv7neon_mmm_f32_8x4_generic 25 32 3 0.000006291923472603681 +generic_f32_4x4 3 32 13 0.000005952451348897524 +armv7neon_mmm_f32_8x6_cortexa9 23 32 19 0.000021623159632105592 +armv7neon_mmm_f32_8x4_generic 9 4 7 0.0000030054177268153855 +armv7neon_mmm_f32_8x4_cortexa9 7 128 3 0.0000044229477614846184 +armv7neon_mmm_f32_8x4_generic 25 32 12 0.0000159741241286288 +armv7neon_mmm_f32_8x6_generic 8 32 5 0.000002351046891403544 +armv7neon_mmm_f32_8x6_generic 8 128 17 0.00001572220287732098 +armv7neon_mmm_f32_8x6_cortexa9 25 4 13 0.00000878947090986632 +armv7neon_mmm_f32_8x6_cortexa7 9 128 18 0.00004088792544048485 +armv7neon_mmm_f32_8x4_generic 9 128 8 0.000015874731663155136 +armv7neon_mmm_f32_8x4_generic 16 128 5 0.000015914646023810615 +armv7neon_mmm_f32_8x6_cortexa7 17 32 18 0.000019117240551030633 +armv7neon_mmm_f32_8x4_cortexa9 7 4 11 0.0000026740706182831685 +armv7neon_mmm_f32_8x4_cortexa7 9 128 13 0.00004271080548146737 +armv7neon_mmm_f32_8x6_generic 16 128 11 0.000020976534312868846 +armv7neon_mmm_f32_8x4_cortexa7 16 128 7 0.00002177826510527881 +armv7neon_mmm_f32_8x4_generic 15 32 7 0.000006163372672989967 +armv7neon_mmm_f32_8x4_generic 9 128 11 0.000023697623425846144 +armv7neon_mmm_f32_8x6_cortexa7 16 4 6 0.0000018480490329829836 +armv7neon_mmm_f32_8x4_cortexa9 25 4 7 0.00000554548266542983 +armv7neon_mmm_f32_8x6_generic 15 128 13 0.00003163198682165344 +armv7neon_mmm_f32_8x6_cortexa7 25 32 18 0.000025184955719578352 +armv7neon_mmm_f32_8x6_generic 8 4 6 0.000001113907974305815 +armv7neon_mmm_f32_8x6_cortexa7 16 32 5 0.0000050492542920704264 +armv7neon_mmm_f32_8x6_generic 8 32 19 0.000007097287986832066 +armv7neon_mmm_f32_8x6_cortexa9 25 32 5 0.000007806390304931795 +armv7neon_mmm_f32_8x6_cortexa9 15 128 11 0.000021399956173572563 +armv7neon_mmm_f32_8x6_generic 16 32 6 0.000003721529767067075 +armv7neon_mmm_f32_8x4_cortexa7 17 32 5 0.000010843745704621824 +armv7neon_mmm_f32_8x4_generic 7 128 7 0.00000843066632668338 +armv7neon_mmm_f32_8x4_generic 23 32 13 0.000016850629087203773 +armv7neon_mmm_f32_8x4_cortexa9 16 32 9 0.000008424770761452617 +armv7neon_mmm_f32_8x4_cortexa9 15 32 13 0.000011652714145510248 +armv7neon_mmm_f32_8x4_generic 23 128 5 0.000023910719482642124 +armv7neon_mmm_f32_8x4_cortexa9 23 128 3 0.000012498528953898262 +armv7neon_mmm_f32_8x4_generic 16 32 8 0.000005593957529271591 +armv7neon_mmm_f32_8x6_cortexa7 7 128 13 0.000021419187824431687 +armv7neon_mmm_f32_8x6_cortexa9 7 128 6 0.000005900049478814555 +armv7neon_mmm_f32_8x4_cortexa7 24 32 11 0.000015824034451655578 +armv7neon_mmm_f32_8x6_cortexa7 16 4 17 0.000004987657461648502 +armv7neon_mmm_f32_8x4_cortexa7 23 32 8 0.000010735439695395534 +armv7neon_mmm_f32_8x4_cortexa9 25 4 5 0.000005392929349101573 +armv7neon_mmm_f32_8x4_generic 25 4 5 0.000005418182338760064 +armv7neon_mmm_f32_8x4_generic 7 128 9 0.000012382199177609924 +armv7neon_mmm_f32_8x4_cortexa9 9 32 9 0.000008579188782470308 +armv7neon_mmm_f32_8x4_generic 8 128 12 0.000011801915167566902 +armv7neon_mmm_f32_8x4_cortexa9 23 4 8 0.0000041152642927900605 +armv7neon_mmm_f32_8x4_generic 7 4 11 0.0000026764041239252324 +generic_f32_4x4 13 128 12 0.00004319526080896362 +generic_f32_4x4 5 32 5 0.000005725142687375275 +generic_f32_4x4 5 128 12 0.00002200938035136394 +armv7neon_mmm_f32_8x4_generic 23 4 5 0.000004369620528795486 +armv7neon_mmm_f32_8x6_generic 9 4 11 0.000003424705713246475 +armv7neon_mmm_f32_8x6_generic 17 32 12 0.000010395428642618952 +generic_f32_4x4 13 32 11 0.000015867743214986118 +armv7neon_mmm_f32_8x6_cortexa9 9 32 17 0.000010850143335646025 +armv7neon_mmm_f32_8x6_cortexa9 23 4 6 0.0000028251957000612876 +armv7neon_mmm_f32_8x6_cortexa9 9 128 7 0.000020914788448206477 +armv7neon_mmm_f32_8x4_cortexa7 15 128 11 0.000032581738066809436 +armv7neon_mmm_f32_8x4_cortexa9 25 128 3 0.000016370587474295946 +armv7neon_mmm_f32_8x4_cortexa7 23 4 4 0.0000024826703257808083 +armv7neon_mmm_f32_8x4_generic 9 128 4 0.000008108848864529598 +armv7neon_mmm_f32_8x6_cortexa9 25 128 5 0.00002136490649839919 +armv7neon_mmm_f32_8x6_generic 23 128 19 0.00006253554336626349 +generic_f32_4x4 12 4 12 0.000005236763691820686 +armv7neon_mmm_f32_8x6_cortexa9 8 4 7 0.0000019309843288986318 +armv7neon_mmm_f32_8x4_cortexa7 15 4 5 0.000003357403897484634 +armv7neon_mmm_f32_8x6_cortexa9 24 4 6 0.0000024072433754401144 +armv7neon_mmm_f32_8x6_cortexa7 25 128 7 0.000054619400677372974 +armv7neon_mmm_f32_8x6_generic 7 4 19 0.000004265433096040261 +generic_f32_4x4 7 4 12 0.0000041235655802886916 +armv7neon_mmm_f32_8x6_generic 17 128 17 0.000046385914441781564 +armv7neon_mmm_f32_8x4_generic 8 32 7 0.0000032122117166780275 +armv7neon_mmm_f32_8x4_cortexa9 16 4 7 0.0000030113926906216977 +generic_f32_4x4 13 128 13 0.00005791151370731608 +armv7neon_mmm_f32_8x4_cortexa7 9 32 9 0.000010771893810221605 +armv7neon_mmm_f32_8x4_cortexa7 23 4 7 0.000004820473947807521 +armv7neon_mmm_f32_8x4_cortexa9 24 4 13 0.00000731015216867438 +generic_f32_4x4 3 4 5 0.0000018607872137198219 +armv7neon_mmm_f32_8x4_generic 15 32 9 0.000008854455084657494 +armv7neon_mmm_f32_8x6_cortexa9 15 32 6 0.000004174355801441308 +armv7neon_mmm_f32_8x6_cortexa9 16 4 5 0.000002250377451226225 +armv7neon_mmm_f32_8x6_cortexa9 16 32 19 0.000013757586520904355 +armv7neon_mmm_f32_8x6_cortexa9 24 128 11 0.000031276431090202235 +armv7neon_mmm_f32_8x4_generic 25 128 7 0.00003164406261833124 +armv7neon_mmm_f32_8x4_generic 24 4 4 0.0000020584132727266 +generic_f32_4x4 7 128 11 0.000022350186275600858 +armv7neon_mmm_f32_8x4_cortexa9 23 4 4 0.0000022753692017814563 +armv7neon_mmm_f32_8x6_cortexa9 17 32 19 0.00002069369907930334 +generic_f32_4x4 4 128 13 0.00001478326569151377 +armv7neon_mmm_f32_8x6_cortexa9 23 32 18 0.00001626278262596922 +armv7neon_mmm_f32_8x6_generic 7 128 18 0.000016667585395966858 +armv7neon_mmm_f32_8x4_cortexa9 8 128 3 0.000004452778028984887 +armv7neon_mmm_f32_8x4_cortexa7 16 128 13 0.000042499574729319346 +generic_f32_4x4 3 32 11 0.000004620575096918176 +generic_f32_4x4 4 4 8 0.0000015603089093623964 +armv7neon_mmm_f32_8x6_cortexa9 15 32 7 0.00000772763531212548 +armv7neon_mmm_f32_8x4_cortexa7 9 4 11 0.000004505536773333099 +armv7neon_mmm_f32_8x6_cortexa9 15 32 18 0.000011416109977905379 +armv7neon_mmm_f32_8x4_cortexa7 24 128 12 0.00004730343110835064 +armv7neon_mmm_f32_8x4_cortexa9 8 4 7 0.000001726865000473268 +armv7neon_mmm_f32_8x4_cortexa9 17 4 12 0.00000554999550214869 +armv7neon_mmm_f32_8x4_cortexa9 24 32 3 0.000004919761516416024 +armv7neon_mmm_f32_8x4_cortexa7 7 4 7 0.0000020327105105148146 +generic_f32_4x4 8 32 11 0.000008129741741591272 +armv7neon_mmm_f32_8x6_cortexa9 17 128 6 0.000015685032106978185 +armv7neon_mmm_f32_8x6_cortexa7 24 4 13 0.0000069628322548194484 +generic_f32_4x4 11 32 7 0.000008418658676221946 +armv7neon_mmm_f32_8x4_cortexa9 23 32 13 0.000016859244394935546 +armv7neon_mmm_f32_8x6_cortexa7 15 4 19 0.000007361880271251662 +armv7neon_mmm_f32_8x6_cortexa7 25 128 19 0.00010862057908423823 +generic_f32_4x4 7 128 5 0.00001508121452285347 +armv7neon_mmm_f32_8x4_generic 8 4 12 0.0000020475145659989298 +armv7neon_mmm_f32_8x6_generic 23 32 6 0.000005742234510888845 +armv7neon_mmm_f32_8x6_cortexa9 8 32 7 0.000003926599359988243 +armv7neon_mmm_f32_8x4_cortexa7 16 32 5 0.00000738840656931522 +generic_f32_4x4 3 128 5 0.000007875053042737703 +armv7neon_mmm_f32_8x6_cortexa9 9 128 5 0.000010860557674890965 +armv7neon_mmm_f32_8x6_generic 15 32 11 0.000007797932920988772 +generic_f32_4x4 8 4 3 0.0000019148150797610994 +armv7neon_mmm_f32_8x4_generic 23 32 9 0.000012836974653040903 +armv7neon_mmm_f32_8x4_cortexa7 25 4 3 0.0000035757992765103294 +armv7neon_mmm_f32_8x4_generic 7 32 8 0.0000034181328210189654 +armv7neon_mmm_f32_8x6_cortexa9 25 32 7 0.000014158102081231813 +armv7neon_mmm_f32_8x6_generic 9 4 18 0.000004486229240869606 +armv7neon_mmm_f32_8x4_cortexa7 8 4 4 0.0000010559381010362017 +armv7neon_mmm_f32_8x4_cortexa9 25 32 3 0.000006292048691193805 +armv7neon_mmm_f32_8x4_cortexa9 24 4 11 0.000005847758703659802 +armv7neon_mmm_f32_8x4_cortexa9 7 128 12 0.00001242120677392976 +armv7neon_mmm_f32_8x4_cortexa7 16 128 9 0.00003210848238315819 +armv7neon_mmm_f32_8x4_cortexa9 24 128 7 0.00002382518082369683 +armv7neon_mmm_f32_8x4_generic 24 32 3 0.0000049198649021802165 +armv7neon_mmm_f32_8x4_cortexa9 25 4 11 0.000007781278370753281 +armv7neon_mmm_f32_8x6_cortexa7 17 32 12 0.000012940679584474003 +armv7neon_mmm_f32_8x6_cortexa7 16 32 17 0.000013190821660739104 +armv7neon_mmm_f32_8x6_cortexa7 25 4 19 0.000012097338566670792 +armv7neon_mmm_f32_8x6_generic 16 32 18 0.000010138504593611297 +armv7neon_mmm_f32_8x4_generic 9 128 5 0.000015947315101595927 +armv7neon_mmm_f32_8x6_cortexa9 23 4 5 0.0000031081830446744932 +armv7neon_mmm_f32_8x6_cortexa9 24 128 7 0.00003103549849311001 +armv7neon_mmm_f32_8x4_cortexa7 25 4 13 0.000010646545231619916 +generic_f32_4x4 9 128 12 0.00003260110763311798 +armv7neon_mmm_f32_8x6_cortexa9 23 4 17 0.000007631985122319121 +armv7neon_mmm_f32_8x4_cortexa9 8 128 13 0.00001574791956845722 +armv7neon_mmm_f32_8x6_cortexa9 23 4 18 0.000007303182081264746 +armv7neon_mmm_f32_8x4_cortexa7 24 128 13 0.00006349168486923089 +armv7neon_mmm_f32_8x4_generic 23 32 11 0.000012994166844502801 +generic_f32_4x4 7 32 4 0.0000031041482961797043 +armv7neon_mmm_f32_8x4_cortexa9 9 32 7 0.000005959447070414145 +armv7neon_mmm_f32_8x4_cortexa7 23 128 11 0.00004845207607490223 +armv7neon_mmm_f32_8x6_cortexa7 17 4 11 0.0000052419075338197185 +generic_f32_4x4 8 4 13 0.00000500809427570026 +armv7neon_mmm_f32_8x4_generic 24 32 7 0.000008731713037367055 +armv7neon_mmm_f32_8x6_cortexa9 25 128 13 0.00006164900543131858 +armv7neon_mmm_f32_8x6_cortexa9 17 4 7 0.000004817790997463295 +armv7neon_mmm_f32_8x6_cortexa9 8 32 19 0.000007141501791335855 +armv7neon_mmm_f32_8x4_cortexa9 16 4 4 0.000001530544742707957 +armv7neon_mmm_f32_8x6_generic 25 4 17 0.000008895187762533163 +armv7neon_mmm_f32_8x4_cortexa7 23 4 3 0.0000028668169166829235 +armv7neon_mmm_f32_8x6_cortexa7 24 128 11 0.00004128096933839809 +generic_f32_4x4 7 4 13 0.000005447538386200191 +armv7neon_mmm_f32_8x6_cortexa9 9 128 17 0.0000311696244066944 +armv7neon_mmm_f32_8x6_cortexa7 25 4 17 0.000009561336620584211 +armv7neon_mmm_f32_8x4_generic 16 128 8 0.000015609467528565242 +armv7neon_mmm_f32_8x6_cortexa9 7 4 6 0.0000015274572782560943 +armv7neon_mmm_f32_8x6_cortexa9 7 4 19 0.000004327266570693739 +generic_f32_4x4 4 128 3 0.0000042186208925339794 +armv7neon_mmm_f32_8x6_cortexa9 16 32 6 0.000003783089782648409 +armv7neon_mmm_f32_8x4_generic 24 4 12 0.000005240725524660556 +armv7neon_mmm_f32_8x6_generic 24 4 5 0.000003075012528242585 +armv7neon_mmm_f32_8x4_cortexa7 16 128 3 0.000011341419957358612 +generic_f32_4x4 13 32 7 0.000010885101829856154 +generic_f32_4x4 8 128 13 0.00002903953507476824 +armv7neon_mmm_f32_8x6_cortexa9 7 32 12 0.000004528441801230068 +armv7neon_mmm_f32_8x4_cortexa9 8 128 9 0.000011968397967338213 +generic_f32_4x4 9 4 11 0.000005879906070039223 +armv7neon_mmm_f32_8x4_generic 9 128 9 0.000023684049431562338 +armv7neon_mmm_f32_8x6_cortexa7 23 128 19 0.00008252903643379173 +armv7neon_mmm_f32_8x6_generic 23 128 7 0.000031381294348434004 +armv7neon_mmm_f32_8x4_generic 15 4 13 0.000005669760296114337 +armv7neon_mmm_f32_8x4_generic 15 4 11 0.000004507200935249084 +armv7neon_mmm_f32_8x4_generic 15 4 3 0.0000019251390208258458 +generic_f32_4x4 9 32 7 0.00000832450452449881 +generic_f32_4x4 9 4 9 0.000005828318767488796 +armv7neon_mmm_f32_8x4_cortexa7 16 4 3 0.000002093301960254744 +armv7neon_mmm_f32_8x4_generic 15 128 12 0.000023812063445324994 +armv7neon_mmm_f32_8x6_cortexa7 7 4 19 0.000004468409864573403 +generic_f32_4x4 8 4 7 0.000002955604897568221 +armv7neon_mmm_f32_8x6_cortexa9 9 32 12 0.000007238338427088594 +armv7neon_mmm_f32_8x6_generic 16 32 12 0.00000694068903036478 +armv7neon_mmm_f32_8x6_cortexa7 25 128 6 0.000027308370544312844 +armv7neon_mmm_f32_8x6_cortexa9 24 4 17 0.000006795821587814609 +armv7neon_mmm_f32_8x6_cortexa7 15 128 12 0.00002803261400606977 +generic_f32_4x4 9 32 8 0.000008044423595514706 +armv7neon_mmm_f32_8x4_generic 23 128 9 0.00003548379543389711 +armv7neon_mmm_f32_8x4_cortexa7 17 32 3 0.000005926615429744039 +armv7neon_mmm_f32_8x4_generic 8 128 7 0.000008235490634332163 +armv7neon_mmm_f32_8x4_cortexa7 9 4 12 0.000004255018272123806 +armv7neon_mmm_f32_8x4_cortexa9 16 32 11 0.000008524184894246237 +armv7neon_mmm_f32_8x4_cortexa9 24 32 4 0.000004264845803875226 +generic_f32_4x4 3 4 11 0.000002555428269031667 +armv7neon_mmm_f32_8x4_cortexa7 24 4 4 0.0000022261643758837446 +armv7neon_mmm_f32_8x4_cortexa7 15 128 3 0.000011306220407671872 +armv7neon_mmm_f32_8x6_generic 8 4 18 0.000002337607386621077 +armv7neon_mmm_f32_8x4_cortexa9 25 4 4 0.0000026788269418985845 +generic_f32_4x4 11 4 7 0.000004308040885452502 +armv7neon_mmm_f32_8x4_cortexa9 15 128 4 0.000008223517905022214 +generic_f32_4x4 5 32 11 0.000008288284199113315 +armv7neon_mmm_f32_8x6_cortexa9 9 4 12 0.0000032467302759009237 +armv7neon_mmm_f32_8x4_generic 24 128 7 0.00002382735987416326 +armv7neon_mmm_f32_8x6_generic 24 32 13 0.000015487434262812744 +armv7neon_mmm_f32_8x6_generic 17 128 13 0.00004629753684559049 +armv7neon_mmm_f32_8x6_cortexa7 24 128 13 0.00006117633525152988 +armv7neon_mmm_f32_8x6_generic 24 4 19 0.000008326315174295047 +armv7neon_mmm_f32_8x6_cortexa7 17 32 7 0.000013248073815175261 +armv7neon_mmm_f32_8x4_cortexa9 8 4 9 0.0000022033600613983073 +generic_f32_4x4 13 4 12 0.000007132280989099253 +armv7neon_mmm_f32_8x4_cortexa7 16 128 4 0.000010902014380146801 +armv7neon_mmm_f32_8x6_cortexa7 9 4 6 0.0000019491119088955803 +generic_f32_4x4 3 4 7 0.0000019042920757312053 +armv7neon_mmm_f32_8x4_generic 7 32 5 0.0000033764800411714497 +armv7neon_mmm_f32_8x6_cortexa7 23 4 19 0.000010215694576435441 +armv7neon_mmm_f32_8x4_generic 17 4 3 0.0000025969094160852644 +generic_f32_4x4 5 4 11 0.0000041349188443565096 +armv7neon_mmm_f32_8x6_cortexa7 9 32 17 0.000013295032359470915 +armv7neon_mmm_f32_8x4_cortexa7 15 128 13 0.00004313466302520057 +armv7neon_mmm_f32_8x4_cortexa9 15 4 8 0.0000030055981545968782 +armv7neon_mmm_f32_8x6_generic 16 4 12 0.0000029570565051151263 +armv7neon_mmm_f32_8x6_cortexa9 8 4 11 0.000002014524477563071 +armv7neon_mmm_f32_8x4_cortexa9 8 4 3 0.0000012065261711370315 +armv7neon_mmm_f32_8x4_cortexa7 7 32 7 0.000004136959156912223 +armv7neon_mmm_f32_8x4_generic 17 4 9 0.000005894535633035915 +generic_f32_4x4 9 4 13 0.00000752174902729491 +armv7neon_mmm_f32_8x6_cortexa9 7 32 5 0.000002364950726746234 +generic_f32_4x4 13 32 4 0.000005521387429490463 +armv7neon_mmm_f32_8x4_generic 8 128 3 0.000004474358471860054 +armv7neon_mmm_f32_8x4_cortexa7 23 4 12 0.0000063640165444956975 +armv7neon_mmm_f32_8x4_generic 7 32 4 0.0000019290673256470733 +armv7neon_mmm_f32_8x4_generic 17 32 12 0.000012180617018748219 +armv7neon_mmm_f32_8x6_generic 17 4 11 0.000004899442085076861 +armv7neon_mmm_f32_8x4_cortexa7 24 128 7 0.000032404931744711066 +armv7neon_mmm_f32_8x4_cortexa9 16 32 12 0.000008099332243893017 +armv7neon_mmm_f32_8x6_generic 9 128 18 0.00003081307981321012 +armv7neon_mmm_f32_8x6_generic 15 128 11 0.00002132277281707897 +armv7neon_mmm_f32_8x6_cortexa7 7 128 6 0.000007540283032400861 +armv7neon_mmm_f32_8x6_generic 25 32 12 0.000013597212519317418 +armv7neon_mmm_f32_8x6_generic 23 4 17 0.000007516814516170969 +armv7neon_mmm_f32_8x6_cortexa7 24 32 11 0.000013420883457543118 +generic_f32_4x4 9 32 11 0.000012087233281631852 +armv7neon_mmm_f32_8x6_cortexa7 24 4 17 0.000007204721001644005 +armv7neon_mmm_f32_8x6_cortexa7 25 32 7 0.000017455571757923716 +armv7neon_mmm_f32_8x4_cortexa9 23 32 11 0.000012992255106920675 +armv7neon_mmm_f32_8x4_cortexa9 9 4 9 0.000004090278120441601 +armv7neon_mmm_f32_8x4_cortexa7 23 32 13 0.000021193261455653347 +armv7neon_mmm_f32_8x6_cortexa7 24 32 19 0.00002532279550192562 +armv7neon_mmm_f32_8x4_generic 25 128 11 0.00004692401023814237 +armv7neon_mmm_f32_8x4_cortexa9 17 32 7 0.000008780640229470475 +armv7neon_mmm_f32_8x4_cortexa7 9 128 9 0.00003233195046796576 +armv7neon_mmm_f32_8x4_cortexa7 23 128 5 0.000032464808470770534 +armv7neon_mmm_f32_8x4_cortexa9 9 4 5 0.00000293936322263601 +armv7neon_mmm_f32_8x6_cortexa7 25 32 6 0.000008732005752739124 +armv7neon_mmm_f32_8x6_cortexa9 8 4 5 0.0000013801093771363888 +armv7neon_mmm_f32_8x6_cortexa7 16 128 6 0.000013837384210811002 +armv7neon_mmm_f32_8x4_generic 23 32 8 0.000008530859801735756 +armv7neon_mmm_f32_8x4_cortexa7 17 4 8 0.000004162931637169246 +armv7neon_mmm_f32_8x6_generic 7 32 13 0.000006254936643865642 +armv7neon_mmm_f32_8x6_cortexa7 9 32 7 0.00000901443584736606 +armv7neon_mmm_f32_8x4_cortexa7 15 4 7 0.0000034347738366310684 +generic_f32_4x4 9 32 13 0.000015744382160735945 +armv7neon_mmm_f32_8x6_generic 9 32 18 0.000010478111403829218 +armv7neon_mmm_f32_8x6_cortexa9 23 128 11 0.00003166377905937062 +armv7neon_mmm_f32_8x4_generic 15 128 7 0.000016210803535229283 +armv7neon_mmm_f32_8x4_cortexa7 23 4 9 0.000006649300631510142 +armv7neon_mmm_f32_8x4_generic 17 32 3 0.000004810545788546653 +armv7neon_mmm_f32_8x6_generic 24 32 12 0.000010144077161084113 +armv7neon_mmm_f32_8x4_cortexa9 15 32 7 0.000006152877670399033 +armv7neon_mmm_f32_8x4_cortexa9 23 4 12 0.000005900735151833346 +armv7neon_mmm_f32_8x4_cortexa7 16 32 4 0.0000037548262311678015 +armv7neon_mmm_f32_8x6_cortexa7 23 4 18 0.000007713970848848978 +armv7neon_mmm_f32_8x6_cortexa7 16 32 6 0.000004565838055826264 +armv7neon_mmm_f32_8x4_cortexa9 25 32 5 0.000011311149331127226 +armv7neon_mmm_f32_8x6_cortexa9 16 128 18 0.00003053013458613619 +armv7neon_mmm_f32_8x6_cortexa7 15 32 17 0.000013972664044561828 +armv7neon_mmm_f32_8x4_generic 16 32 4 0.000003010794297325924 +armv7neon_mmm_f32_8x6_generic 7 128 7 0.000011021195155003848 +generic_f32_4x4 4 32 13 0.000005510533372512351 +armv7neon_mmm_f32_8x4_cortexa9 16 32 7 0.000005969619454408248 +armv7neon_mmm_f32_8x4_cortexa9 24 32 12 0.000011900133300984794 +armv7neon_mmm_f32_8x4_cortexa7 8 128 3 0.000005902896580930324 +armv7neon_mmm_f32_8x4_generic 16 32 3 0.000003439802295093395 +armv7neon_mmm_f32_8x4_cortexa7 17 32 4 0.000005503311716430611 +armv7neon_mmm_f32_8x6_generic 9 32 12 0.000007171024246490819 +armv7neon_mmm_f32_8x6_cortexa7 23 32 5 0.000007315917802939159 +armv7neon_mmm_f32_8x6_cortexa7 23 128 12 0.0000413645417403771 +armv7neon_mmm_f32_8x4_generic 16 32 7 0.000005981518355563633 +generic_f32_4x4 7 4 7 0.000003097228954014147 +armv7neon_mmm_f32_8x6_cortexa7 24 4 18 0.000006499393673193748 +armv7neon_mmm_f32_8x6_cortexa9 24 4 7 0.000004723972074276078 +armv7neon_mmm_f32_8x4_cortexa7 16 32 11 0.00001075379201180745 +generic_f32_4x4 5 32 8 0.000005591832396338314 +armv7neon_mmm_f32_8x6_cortexa7 7 4 17 0.000003519015871119777 +armv7neon_mmm_f32_8x6_generic 25 4 13 0.000008668362849414556 +armv7neon_mmm_f32_8x4_cortexa7 15 128 4 0.000011117502498464982 +armv7neon_mmm_f32_8x6_cortexa7 15 4 17 0.000005776336577667951 +armv7neon_mmm_f32_8x4_cortexa9 8 32 13 0.000005695220879131919 +armv7neon_mmm_f32_8x4_cortexa7 24 4 12 0.000005659072390762546 +armv7neon_mmm_f32_8x4_generic 23 128 4 0.00001202844464043909 +armv7neon_mmm_f32_8x6_generic 17 128 7 0.00003102141310178231 +armv7neon_mmm_f32_8x4_generic 16 128 11 0.00002360551094138009 +armv7neon_mmm_f32_8x4_cortexa7 25 128 9 0.00006388979998675838 +armv7neon_mmm_f32_8x4_generic 9 32 9 0.00000858128920884699 +armv7neon_mmm_f32_8x6_cortexa7 9 4 12 0.0000034111339123867222 +armv7neon_mmm_f32_8x6_cortexa9 17 128 11 0.00003127371093460866 +armv7neon_mmm_f32_8x4_cortexa9 15 128 11 0.00002401650595136234 +armv7neon_mmm_f32_8x6_cortexa9 23 128 12 0.00003137052216487605 +armv7neon_mmm_f32_8x6_cortexa9 16 128 17 0.00003103196066568324 +generic_f32_4x4 7 32 3 0.000003268544296254094 +armv7neon_mmm_f32_8x4_generic 9 4 8 0.0000027781499181131053 +armv7neon_mmm_f32_8x4_cortexa7 16 32 9 0.000010640273784126178 +armv7neon_mmm_f32_8x4_cortexa9 25 4 9 0.00000761410675406794 +generic_f32_4x4 3 4 12 0.00000255471395132772 +generic_f32_4x4 12 32 13 0.00001543431169651167 +armv7neon_mmm_f32_8x6_cortexa9 15 4 7 0.0000037646981265010737 +armv7neon_mmm_f32_8x4_cortexa7 7 4 9 0.0000027630250787792764 +armv7neon_mmm_f32_8x6_cortexa9 15 4 13 0.000005407861539689391 +armv7neon_mmm_f32_8x4_cortexa7 24 4 5 0.000004483592435521624 +armv7neon_mmm_f32_8x6_cortexa9 16 128 5 0.000010995701297253243 +armv7neon_mmm_f32_8x4_cortexa9 7 4 13 0.000003344708907386396 +generic_f32_4x4 3 4 4 0.0000012029323677452384 +armv7neon_mmm_f32_8x6_cortexa7 8 4 5 0.0000014134340225429588 +armv7neon_mmm_f32_8x4_cortexa7 16 4 5 0.000003166142335812665 +armv7neon_mmm_f32_8x6_cortexa9 15 4 17 0.000005534126802188086 +armv7neon_mmm_f32_8x4_generic 17 128 4 0.000011910311848497855 +armv7neon_mmm_f32_8x4_cortexa9 17 128 9 0.00003520115855849476 +armv7neon_mmm_f32_8x6_cortexa7 24 128 17 0.00006131644295051564 +armv7neon_mmm_f32_8x4_generic 8 32 12 0.000004263261337563466 +armv7neon_mmm_f32_8x4_generic 15 128 13 0.000031724034290535855 +generic_f32_4x4 13 128 4 0.000014842273639272524 +armv7neon_mmm_f32_8x6_generic 16 128 17 0.00003115527449889593 +armv7neon_mmm_f32_8x4_cortexa7 25 128 13 0.00008540107215655228 +armv7neon_mmm_f32_8x4_cortexa9 17 4 8 0.000003867693626667699 +armv7neon_mmm_f32_8x6_generic 9 4 6 0.0000018447023821962963 +armv7neon_mmm_f32_8x4_generic 15 4 5 0.0000031342530980756114 +armv7neon_mmm_f32_8x4_cortexa9 25 32 11 0.000016711914928453156 +armv7neon_mmm_f32_8x4_cortexa9 9 4 7 0.0000029971874356879734 +armv7neon_mmm_f32_8x4_cortexa9 17 32 8 0.000008299083390781581 +armv7neon_mmm_f32_8x6_generic 9 128 11 0.000020922916297729766 +armv7neon_mmm_f32_8x6_cortexa9 25 4 11 0.000006455717226984232 +generic_f32_4x4 9 128 13 0.00004380056172414816 +armv7neon_mmm_f32_8x4_generic 9 4 3 0.0000018459881520629983 +generic_f32_4x4 13 32 13 0.000020718460422326147 +generic_f32_4x4 11 128 11 0.00003310679264480642 +armv7neon_mmm_f32_8x4_cortexa7 17 128 12 0.00004762075330394028 +armv7neon_mmm_f32_8x6_cortexa9 25 4 6 0.0000031406906098450826 +armv7neon_mmm_f32_8x4_cortexa7 15 4 3 0.0000020595941028725178 +generic_f32_4x4 4 32 5 0.000003094039509493947 +armv7neon_mmm_f32_8x4_cortexa7 17 4 13 0.000008167970707514618 +armv7neon_mmm_f32_8x6_cortexa9 16 128 7 0.000020852896757305592 +armv7neon_mmm_f32_8x4_generic 24 32 12 0.000011875110028324704 +armv7neon_mmm_f32_8x6_cortexa7 23 32 6 0.0000070036066823672265 +armv7neon_mmm_f32_8x4_generic 24 32 9 0.000012379847822938195 +armv7neon_mmm_f32_8x6_generic 16 32 7 0.000007288146983219525 +armv7neon_mmm_f32_8x4_generic 8 32 4 0.0000017213389522499293 +armv7neon_mmm_f32_8x6_cortexa7 25 32 12 0.00001698633380929768 +armv7neon_mmm_f32_8x4_generic 25 32 8 0.000010843062239811664 +armv7neon_mmm_f32_8x6_generic 16 128 5 0.000010970239776322642 +armv7neon_mmm_f32_8x6_cortexa9 23 32 12 0.00001105222841817834 +armv7neon_mmm_f32_8x4_cortexa7 7 4 12 0.0000028259675178908435 +armv7neon_mmm_f32_8x4_generic 17 32 13 0.000016456626258678596 +armv7neon_mmm_f32_8x6_generic 23 32 19 0.000021489093420466062 +armv7neon_mmm_f32_8x6_cortexa9 9 32 7 0.000007388528782309879 +armv7neon_mmm_f32_8x6_generic 16 128 13 0.0000308218952023933 +armv7neon_mmm_f32_8x4_cortexa9 24 128 4 0.000011799248370378306 +armv7neon_mmm_f32_8x6_cortexa7 15 4 5 0.000002311967551514126 +armv7neon_mmm_f32_8x6_cortexa9 15 32 19 0.000015002244448339703 +armv7neon_mmm_f32_8x4_cortexa7 25 32 4 0.000007113231524121585 +armv7neon_mmm_f32_8x4_generic 9 4 9 0.0000040996991473354 +generic_f32_4x4 11 4 11 0.00000604219727392619 +armv7neon_mmm_f32_8x4_generic 23 128 12 0.0000351784079527385 +armv7neon_mmm_f32_8x4_generic 8 32 8 0.0000029996111096440974 +generic_f32_4x4 3 128 11 0.000011575521961547323 +armv7neon_mmm_f32_8x6_generic 17 4 19 0.000008636925572322338 +armv7neon_mmm_f32_8x6_cortexa7 17 32 17 0.000019651252774271738 +armv7neon_mmm_f32_8x4_cortexa7 25 32 5 0.000014279031192163009 +armv7neon_mmm_f32_8x6_cortexa9 7 128 5 0.000005734808712355398 +armv7neon_mmm_f32_8x4_cortexa9 15 4 5 0.0000031170038028798296 +armv7neon_mmm_f32_8x4_cortexa9 24 128 13 0.000046424963618177915 +armv7neon_mmm_f32_8x6_generic 16 4 5 0.0000022248730615242665 +generic_f32_4x4 8 128 9 0.0000220091711298822 +armv7neon_mmm_f32_8x6_cortexa9 24 32 17 0.000015790292495872178 +armv7neon_mmm_f32_8x6_cortexa7 25 4 12 0.000006064814099731578 +armv7neon_mmm_f32_8x4_generic 9 32 3 0.0000033141535571016215 +armv7neon_mmm_f32_8x4_cortexa7 24 32 7 0.000010955777874109968 +armv7neon_mmm_f32_8x6_generic 7 32 19 0.000008240412026502634 +armv7neon_mmm_f32_8x6_generic 15 128 12 0.000021273134898855332 +armv7neon_mmm_f32_8x4_generic 9 128 12 0.000023427967692188372 +generic_f32_4x4 9 128 4 0.000011233611698379809 +armv7neon_mmm_f32_8x4_generic 23 32 3 0.000004898603928972602 +armv7neon_mmm_f32_8x4_cortexa9 15 4 7 0.000003196236363013327 +generic_f32_4x4 13 128 9 0.000043631376927673965 +armv7neon_mmm_f32_8x6_cortexa7 8 32 13 0.000006769099621081802 +armv7neon_mmm_f32_8x6_cortexa9 16 32 11 0.000007489162129911177 +armv7neon_mmm_f32_8x6_cortexa7 15 4 11 0.000004047240611447739 +armv7neon_mmm_f32_8x6_cortexa9 8 128 12 0.000010521407486118232 +armv7neon_mmm_f32_8x6_generic 24 128 12 0.000030475350289631447 +armv7neon_mmm_f32_8x6_cortexa9 8 4 18 0.0000023963651215663894 +armv7neon_mmm_f32_8x4_generic 7 4 13 0.0000033498033347340617 +armv7neon_mmm_f32_8x6_cortexa7 24 128 5 0.000021225542006705262 +armv7neon_mmm_f32_8x6_generic 8 128 7 0.000010658035873946595 +armv7neon_mmm_f32_8x6_cortexa9 16 128 13 0.00003086535920353521 +armv7neon_mmm_f32_8x6_cortexa7 9 4 13 0.000005015620872516637 +armv7neon_mmm_f32_8x6_cortexa9 8 4 13 0.0000025412882105309308 +armv7neon_mmm_f32_8x4_cortexa9 24 32 11 0.000012564889907233448 +armv7neon_mmm_f32_8x4_generic 7 128 13 0.000016356824884577277 +armv7neon_mmm_f32_8x4_cortexa7 15 32 11 0.00001113088620100476 +armv7neon_mmm_f32_8x6_generic 23 32 18 0.000016159850846463288 +armv7neon_mmm_f32_8x6_generic 15 32 19 0.000014889331973563625 +armv7neon_mmm_f32_8x4_generic 7 128 11 0.000012426167611771446 +generic_f32_4x4 4 4 4 0.000001047996290990507 +armv7neon_mmm_f32_8x4_generic 23 4 9 0.000006177048756338135 +armv7neon_mmm_f32_8x6_cortexa9 16 4 11 0.0000034974057138003183 +armv7neon_mmm_f32_8x6_cortexa9 7 128 12 0.00001128966410991496 +armv7neon_mmm_f32_8x4_cortexa9 9 4 8 0.0000027723235031605195 +armv7neon_mmm_f32_8x4_generic 7 128 12 0.000012422711675746062 +armv7neon_mmm_f32_8x4_cortexa7 25 128 12 0.0000632498211882511 +armv7neon_mmm_f32_8x4_generic 7 128 5 0.000008380542348646574 +armv7neon_mmm_f32_8x4_cortexa9 17 4 3 0.000002593764492859534 +generic_f32_4x4 9 4 3 0.0000025396068006223777 +generic_f32_4x4 9 4 12 0.0000055571336153173335 +armv7neon_mmm_f32_8x4_cortexa7 16 128 8 0.000021301840263398342 +armv7neon_mmm_f32_8x4_generic 9 32 12 0.000008360119586696751 +armv7neon_mmm_f32_8x6_cortexa9 23 32 13 0.00001642300983789001 +armv7neon_mmm_f32_8x4_cortexa7 9 32 13 0.000014115360584179656 +armv7neon_mmm_f32_8x4_generic 7 4 4 0.0000011988761531825325 +generic_f32_4x4 4 32 12 0.000004173516705669169 +armv7neon_mmm_f32_8x6_cortexa7 8 4 17 0.000002748952290655516 +armv7neon_mmm_f32_8x6_cortexa9 15 128 17 0.000031856750398890617 +armv7neon_mmm_f32_8x4_cortexa9 17 128 4 0.00001190476277599742 +armv7neon_mmm_f32_8x6_cortexa7 23 4 17 0.000008057629471932516 +armv7neon_mmm_f32_8x4_cortexa7 24 128 9 0.00004787648599530554 +armv7neon_mmm_f32_8x4_cortexa7 23 4 5 0.000004689549145067684 +armv7neon_mmm_f32_8x6_cortexa9 17 4 18 0.000006445994468840472 +armv7neon_mmm_f32_8x6_generic 9 128 17 0.00003107927056434744 +armv7neon_mmm_f32_8x4_generic 24 128 8 0.000023288445002113136 +armv7neon_mmm_f32_8x6_cortexa9 16 32 17 0.000010725045258389392 +armv7neon_mmm_f32_8x4_cortexa7 17 128 11 0.00004814359255643903 +armv7neon_mmm_f32_8x4_generic 7 4 3 0.0000011794687664451176 +generic_f32_4x4 7 32 5 0.000005808524626140803 +armv7neon_mmm_f32_8x6_generic 24 32 11 0.000010897102444398395 +armv7neon_mmm_f32_8x4_generic 7 4 7 0.0000019250466424784192 +armv7neon_mmm_f32_8x4_cortexa9 7 32 13 0.000006310599463529341 +generic_f32_4x4 4 32 8 0.000002964574479014875 +armv7neon_mmm_f32_8x6_cortexa7 17 4 12 0.000004769238750897463 +armv7neon_mmm_f32_8x6_generic 8 32 17 0.000005580365730578937 +armv7neon_mmm_f32_8x4_cortexa9 7 4 5 0.0000018767262951495324 +armv7neon_mmm_f32_8x4_cortexa9 9 128 3 0.000008376722642527261 +generic_f32_4x4 8 32 8 0.000005427007518436356 +generic_f32_4x4 3 32 7 0.000003269319162898471 +armv7neon_mmm_f32_8x6_generic 23 128 11 0.000031615917676258355 +generic_f32_4x4 11 32 3 0.000004643343538119153 +armv7neon_mmm_f32_8x6_generic 17 4 13 0.000006685914546845586 +armv7neon_mmm_f32_8x4_cortexa9 7 128 13 0.000016356503104634956 +armv7neon_mmm_f32_8x6_generic 8 4 11 0.000001987955413940483 +armv7neon_mmm_f32_8x6_cortexa9 23 32 6 0.0000058061790469852035 +armv7neon_mmm_f32_8x6_cortexa9 17 128 12 0.00003080777419909108 +generic_f32_4x4 3 4 13 0.00000320269810797615 +armv7neon_mmm_f32_8x6_cortexa9 17 32 12 0.000010479324211722284 +armv7neon_mmm_f32_8x4_cortexa9 7 4 4 0.000001192052740790223 +armv7neon_mmm_f32_8x4_cortexa9 24 128 12 0.00003457434886740767 +armv7neon_mmm_f32_8x6_cortexa9 23 32 11 0.000011348364973457056 +armv7neon_mmm_f32_8x4_generic 16 4 12 0.000003656453112482303 +armv7neon_mmm_f32_8x6_cortexa9 16 128 6 0.000010542301986141948 +armv7neon_mmm_f32_8x4_generic 24 128 4 0.000011845830939195392 +armv7neon_mmm_f32_8x6_cortexa7 9 32 18 0.000013032529505137248 +armv7neon_mmm_f32_8x4_generic 24 128 12 0.00003455867526442596 +armv7neon_mmm_f32_8x4_generic 23 128 7 0.00002405539721722316 +armv7neon_mmm_f32_8x4_cortexa9 23 32 4 0.000004484967279906347 +armv7neon_mmm_f32_8x4_cortexa9 17 32 4 0.000004369095064954239 +armv7neon_mmm_f32_8x6_cortexa7 8 4 12 0.000001838051428755667 +armv7neon_mmm_f32_8x4_generic 15 4 8 0.000003030902158049641 +armv7neon_mmm_f32_8x4_cortexa7 8 128 9 0.000016361621231447548 +armv7neon_mmm_f32_8x6_generic 15 4 5 0.000002193007903880869 +armv7neon_mmm_f32_8x6_cortexa9 24 32 18 0.000015047680763476656 +armv7neon_mmm_f32_8x6_generic 25 128 6 0.00002065407414270236 +armv7neon_mmm_f32_8x4_generic 25 32 9 0.00001655596981137993 +armv7neon_mmm_f32_8x4_cortexa7 7 4 8 0.000002046321793305707 +armv7neon_mmm_f32_8x4_generic 24 32 5 0.000008563586932451436 +armv7neon_mmm_f32_8x6_cortexa7 25 32 19 0.00003389329143278319 +generic_f32_4x4 3 4 3 0.0000011908985597580253 +generic_f32_4x4 11 128 13 0.000043852326643092945 +armv7neon_mmm_f32_8x6_cortexa9 7 32 6 0.0000025216564013293366 +generic_f32_4x4 8 128 11 0.00002204318798460829 +generic_f32_4x4 9 32 5 0.00000828023901975494 +armv7neon_mmm_f32_8x6_generic 25 4 11 0.000006352168944400001 +armv7neon_mmm_f32_8x4_cortexa9 24 32 5 0.000008590022043400085 +armv7neon_mmm_f32_8x4_generic 8 32 3 0.0000019449019781287236 +armv7neon_mmm_f32_8x4_cortexa7 24 128 8 0.0000317811111212887 +armv7neon_mmm_f32_8x4_cortexa9 7 128 9 0.000012372639572013476 +armv7neon_mmm_f32_8x6_generic 15 128 19 0.00004250719000509339 +armv7neon_mmm_f32_8x6_cortexa7 17 4 7 0.000005084587454728339 +armv7neon_mmm_f32_8x6_generic 15 32 5 0.000004169790668084077 +generic_f32_4x4 9 128 3 0.000011555287825087273 +armv7neon_mmm_f32_8x4_cortexa7 25 128 7 0.00004317760438423037 +generic_f32_4x4 8 32 12 0.00000779706808746927 +armv7neon_mmm_f32_8x6_generic 8 32 12 0.000003719914023610946 +armv7neon_mmm_f32_8x6_cortexa7 15 128 17 0.000041839968426337905 +armv7neon_mmm_f32_8x6_cortexa7 8 128 13 0.000020713409071600263 +armv7neon_mmm_f32_8x6_cortexa9 24 4 5 0.000003104317520805454 +armv7neon_mmm_f32_8x6_cortexa9 9 4 13 0.000004778161797091213 +armv7neon_mmm_f32_8x4_cortexa9 25 128 12 0.00004644468928643539 +generic_f32_4x4 11 4 9 0.000005973042856611991 +armv7neon_mmm_f32_8x4_cortexa7 23 128 8 0.00003217939318511314 +armv7neon_mmm_f32_8x6_generic 24 4 11 0.00000490822983265492 +armv7neon_mmm_f32_8x4_generic 17 4 12 0.000005542606566473033 +armv7neon_mmm_f32_8x6_cortexa9 16 32 5 0.000004236072973244724 +armv7neon_mmm_f32_8x6_cortexa7 24 32 12 0.000012668891408140166 +armv7neon_mmm_f32_8x6_cortexa7 17 128 19 0.00008180995267293593 +armv7neon_mmm_f32_8x4_cortexa7 8 4 9 0.0000023929041278364927 +armv7neon_mmm_f32_8x4_generic 17 4 11 0.000005996779011118073 +armv7neon_mmm_f32_8x4_generic 23 32 5 0.000008783310902330428 +armv7neon_mmm_f32_8x4_cortexa9 24 4 5 0.000004126054215412369 +armv7neon_mmm_f32_8x4_cortexa7 15 4 4 0.0000018664924896343623 +generic_f32_4x4 12 32 12 0.000011419939741453591 +armv7neon_mmm_f32_8x6_cortexa9 9 128 19 0.00004140314991254094 +armv7neon_mmm_f32_8x6_cortexa7 24 4 19 0.000008976504261442944 +armv7neon_mmm_f32_8x6_cortexa7 23 32 12 0.000013517680029411935 +armv7neon_mmm_f32_8x6_generic 23 128 17 0.00004767212397281204 +armv7neon_mmm_f32_8x6_cortexa7 9 128 12 0.000027472854173113385 +generic_f32_4x4 13 4 4 0.0000027493566687604084 +armv7neon_mmm_f32_8x6_cortexa7 7 32 7 0.000005097398974457722 +armv7neon_mmm_f32_8x4_generic 25 4 3 0.0000033394241283747185 +armv7neon_mmm_f32_8x4_cortexa7 25 4 5 0.000005839267769079541 +generic_f32_4x4 8 4 9 0.0000039467461932377605 +armv7neon_mmm_f32_8x6_cortexa7 8 32 6 0.0000025355396885136015 +armv7neon_mmm_f32_8x4_cortexa7 7 4 13 0.0000035454436358925364 +generic_f32_4x4 12 128 5 0.000022119589677203316 +armv7neon_mmm_f32_8x4_cortexa9 17 32 5 0.000008619398425928128 +generic_f32_4x4 5 32 12 0.00000809048145029521 +armv7neon_mmm_f32_8x6_generic 7 32 12 0.000004497135112528872 +armv7neon_mmm_f32_8x6_cortexa9 17 128 18 0.00004612047264624732 +armv7neon_mmm_f32_8x4_cortexa9 24 128 9 0.00003503924267370847 +armv7neon_mmm_f32_8x6_cortexa9 25 128 18 0.00006126219444229572 +armv7neon_mmm_f32_8x4_cortexa9 7 32 7 0.0000034009841492082025 +armv7neon_mmm_f32_8x6_cortexa7 8 32 18 0.0000065795660386672954 +armv7neon_mmm_f32_8x4_cortexa7 9 4 7 0.0000032333021816508883 +generic_f32_4x4 11 4 5 0.000004260253310204167 +armv7neon_mmm_f32_8x4_cortexa7 15 32 12 0.00001091466040761069 +armv7neon_mmm_f32_8x6_generic 25 128 17 0.00006242813577501301 +armv7neon_mmm_f32_8x6_cortexa7 23 128 17 0.00006230757214971725 +armv7neon_mmm_f32_8x4_cortexa7 9 4 3 0.000001972675027149826 +armv7neon_mmm_f32_8x6_generic 9 32 19 0.000013969471313273173 +armv7neon_mmm_f32_8x4_cortexa7 9 4 4 0.0000017499654922061095 +generic_f32_4x4 8 4 8 0.0000026159902565290668 +armv7neon_mmm_f32_8x6_generic 23 128 12 0.000031313730389866355 +armv7neon_mmm_f32_8x4_cortexa7 15 128 7 0.000021942752305858642 +armv7neon_mmm_f32_8x6_cortexa7 9 32 6 0.000004675388926372666 +armv7neon_mmm_f32_8x6_cortexa7 8 32 5 0.000002772093015202498 +armv7neon_mmm_f32_8x6_cortexa7 9 32 19 0.000017344416985284313 +armv7neon_mmm_f32_8x4_cortexa9 8 128 7 0.000008229649908268804 +generic_f32_4x4 3 128 12 0.000011565133509989783 +generic_f32_4x4 9 32 12 0.000011785850787897203 +armv7neon_mmm_f32_8x6_cortexa9 17 128 13 0.00004651964090971228 +armv7neon_mmm_f32_8x6_cortexa9 25 32 6 0.0000071127343061349165 +armv7neon_mmm_f32_8x6_cortexa7 25 128 13 0.00008178262598187339 +armv7neon_mmm_f32_8x4_cortexa9 25 32 13 0.000021697932623977668 +armv7neon_mmm_f32_8x4_cortexa9 16 4 11 0.0000040619610195960516 +armv7neon_mmm_f32_8x4_generic 23 128 3 0.000012468771697124281 +generic_f32_4x4 11 32 13 0.000015931672245334774 +generic_f32_4x4 8 32 9 0.000008097203894273956 +armv7neon_mmm_f32_8x4_cortexa9 17 4 5 0.000004171816997731455 +armv7neon_mmm_f32_8x6_generic 15 32 17 0.000011434650801767795 +armv7neon_mmm_f32_8x4_cortexa7 25 128 3 0.000022089826731240803 +armv7neon_mmm_f32_8x4_cortexa9 17 32 9 0.000012562108538363274 +generic_f32_4x4 7 128 4 0.000007740927731735862 +armv7neon_mmm_f32_8x6_cortexa7 16 128 17 0.00004105266320070572 +armv7neon_mmm_f32_8x6_generic 17 128 5 0.000016103121774230763 +armv7neon_mmm_f32_8x6_cortexa9 25 128 19 0.0000820798385767341 +armv7neon_mmm_f32_8x4_generic 15 32 12 0.000008721032730753763 +armv7neon_mmm_f32_8x4_generic 25 4 9 0.000007623970700084407 +armv7neon_mmm_f32_8x4_generic 17 32 11 0.000012672883096785458 +armv7neon_mmm_f32_8x6_generic 16 32 19 0.000013691087754459915 +armv7neon_mmm_f32_8x6_generic 9 4 12 0.0000031605677124646883 +generic_f32_4x4 5 4 5 0.0000029759889706969683 +armv7neon_mmm_f32_8x4_generic 16 32 9 0.00000841650213766426 +generic_f32_4x4 8 32 13 0.000010487684258287977 +generic_f32_4x4 4 128 5 0.000007733485289678424 +armv7neon_mmm_f32_8x6_cortexa7 8 4 19 0.0000033370479940342483 +armv7neon_mmm_f32_8x6_cortexa7 16 4 13 0.000004820409587660123 +armv7neon_mmm_f32_8x6_cortexa7 7 128 5 0.000007382676538540774 +armv7neon_mmm_f32_8x6_cortexa9 9 4 7 0.0000034194906166596 +armv7neon_mmm_f32_8x4_generic 24 128 3 0.000012489994751068132 +armv7neon_mmm_f32_8x6_generic 17 4 5 0.0000029369592565703416 +armv7neon_mmm_f32_8x6_cortexa9 7 32 7 0.000004299364469367014 +generic_f32_4x4 8 128 8 0.000014675074170610452 +armv7neon_mmm_f32_8x6_generic 24 32 6 0.000005321602550624656 +generic_f32_4x4 8 128 3 0.000007920826245860217 +armv7neon_mmm_f32_8x6_cortexa7 9 128 17 0.000041149683356221505 +armv7neon_mmm_f32_8x6_generic 15 128 18 0.0000317697933254728 +armv7neon_mmm_f32_8x4_generic 15 32 11 0.000008972113808644295 +armv7neon_mmm_f32_8x6_generic 17 128 11 0.00003128672479118261 +armv7neon_mmm_f32_8x6_generic 16 32 5 0.000004210171223198828 +armv7neon_mmm_f32_8x4_cortexa9 23 32 7 0.000008919434958029703 +armv7neon_mmm_f32_8x6_cortexa7 23 4 11 0.000005630918362501218 +generic_f32_4x4 13 4 13 0.000009734041681688549 +armv7neon_mmm_f32_8x4_cortexa7 23 4 8 0.000004419109488183114 +armv7neon_mmm_f32_8x6_generic 17 128 18 0.00004641763657018708 +armv7neon_mmm_f32_8x6_cortexa7 23 32 7 0.000013604910503058097 +armv7neon_mmm_f32_8x4_cortexa9 16 4 8 0.0000025811126658507737 +armv7neon_mmm_f32_8x6_generic 16 4 17 0.000004664791456959476 +armv7neon_mmm_f32_8x6_cortexa9 15 128 19 0.00004234538403110192 +armv7neon_mmm_f32_8x4_cortexa7 7 128 8 0.000011300292613931975 +generic_f32_4x4 4 32 7 0.000003116445195774716 +armv7neon_mmm_f32_8x4_generic 17 128 3 0.000012383648487655847 +armv7neon_mmm_f32_8x6_cortexa9 24 32 12 0.00001022987009394415 +armv7neon_mmm_f32_8x4_cortexa9 17 32 11 0.000012684638765009217 +armv7neon_mmm_f32_8x6_cortexa9 9 128 18 0.000030893903863542745 +armv7neon_mmm_f32_8x6_cortexa7 9 4 18 0.000004852117278755586 +armv7neon_mmm_f32_8x6_cortexa9 8 128 13 0.00001571685239499597 +armv7neon_mmm_f32_8x6_cortexa9 23 4 7 0.000005166723329090456 +armv7neon_mmm_f32_8x6_cortexa9 7 128 18 0.000016699359662800567 +armv7neon_mmm_f32_8x4_generic 17 32 4 0.0000043747418052874395 +armv7neon_mmm_f32_8x4_cortexa9 25 128 13 0.00006245169757364539 +armv7neon_mmm_f32_8x4_generic 24 128 11 0.00003520805509267024 +armv7neon_mmm_f32_8x4_generic 7 4 8 0.000001940897025671589 +armv7neon_mmm_f32_8x4_cortexa9 24 32 8 0.000008105445830543554 +generic_f32_4x4 3 32 5 0.000003239638778592903 +armv7neon_mmm_f32_8x6_generic 16 128 6 0.000010481759011880018 +armv7neon_mmm_f32_8x6_cortexa9 23 4 11 0.000005380241199288215 +armv7neon_mmm_f32_8x6_cortexa7 24 4 11 0.000005233250260928121 +armv7neon_mmm_f32_8x6_cortexa9 23 4 19 0.000009671598448323651 +armv7neon_mmm_f32_8x6_cortexa7 23 4 6 0.0000029172933842802905 +armv7neon_mmm_f32_8x4_cortexa9 23 32 8 0.000008528926728389173 +armv7neon_mmm_f32_8x4_cortexa9 24 128 8 0.000023207480788900347 +generic_f32_4x4 7 32 11 0.00000844108827118094 +generic_f32_4x4 12 4 5 0.000004095794522926841 +armv7neon_mmm_f32_8x4_generic 25 4 11 0.000007781652426587433 +armv7neon_mmm_f32_8x4_generic 16 32 12 0.000008090083939378293 +armv7neon_mmm_f32_8x4_generic 9 128 7 0.000016030853255705617 +armv7neon_mmm_f32_8x6_generic 15 4 19 0.000006928708092302355 +armv7neon_mmm_f32_8x6_cortexa7 24 128 12 0.00004054881877870741 +generic_f32_4x4 8 4 4 0.0000015677362682744682 +armv7neon_mmm_f32_8x6_generic 7 128 19 0.000021822554568277356 +armv7neon_mmm_f32_8x6_cortexa9 24 4 12 0.00000423613681186459 +armv7neon_mmm_f32_8x4_cortexa7 16 4 4 0.0000016584422934049926 +armv7neon_mmm_f32_8x6_cortexa7 15 4 12 0.0000039842977137122266 +armv7neon_mmm_f32_8x4_cortexa7 7 128 4 0.000005876303683712701 +armv7neon_mmm_f32_8x6_cortexa9 7 4 5 0.000001360389294622995 +armv7neon_mmm_f32_8x4_cortexa7 24 4 9 0.000006182082152017003 +armv7neon_mmm_f32_8x6_generic 7 128 17 0.000016508734849042377 +armv7neon_mmm_f32_8x4_generic 17 128 9 0.000035220470083517575 +generic_f32_4x4 11 32 4 0.000004333160180811584 +armv7neon_mmm_f32_8x6_cortexa7 7 32 12 0.000005349730883357719 +armv7neon_mmm_f32_8x4_generic 8 32 5 0.0000031582408088385206 +armv7neon_mmm_f32_8x6_cortexa9 8 32 6 0.000002149345359409538 +generic_f32_4x4 5 32 13 0.000010767359583748404 +armv7neon_mmm_f32_8x4_generic 17 32 5 0.000008612220700720838 +generic_f32_4x4 5 4 4 0.0000016841544626715604 +armv7neon_mmm_f32_8x4_cortexa9 9 32 8 0.000005740276944412148 +armv7neon_mmm_f32_8x6_cortexa9 23 128 7 0.00003146196425036688 +armv7neon_mmm_f32_8x4_cortexa9 25 128 9 0.00004693016970364154 +armv7neon_mmm_f32_8x4_generic 24 4 8 0.0000036269745603468488 +armv7neon_mmm_f32_8x6_cortexa9 24 32 5 0.000006081990180966961 +armv7neon_mmm_f32_8x6_generic 7 4 6 0.000001511590523783866 +generic_f32_4x4 13 128 7 0.00002944642529063838 +armv7neon_mmm_f32_8x4_cortexa9 8 4 11 0.000002254110071814015 +armv7neon_mmm_f32_8x6_cortexa9 24 128 19 0.00006153515122537746 +armv7neon_mmm_f32_8x4_cortexa9 16 128 13 0.00003108122366900544 +generic_f32_4x4 11 128 9 0.000033046521573243154 +armv7neon_mmm_f32_8x4_generic 7 4 9 0.0000026137777410885136 +armv7neon_mmm_f32_8x6_cortexa7 7 4 13 0.0000034217472919863715 +armv7neon_mmm_f32_8x6_generic 16 4 11 0.000003453597254301285 +armv7neon_mmm_f32_8x4_cortexa7 17 32 11 0.000015933978694713537 +armv7neon_mmm_f32_8x6_cortexa7 23 4 12 0.0000053181832633687915 +armv7neon_mmm_f32_8x6_generic 17 32 6 0.000005447406276972202 +armv7neon_mmm_f32_8x4_cortexa7 7 32 4 0.000002302756052490624 +armv7neon_mmm_f32_8x4_cortexa9 15 4 13 0.000005662932902369066 +armv7neon_mmm_f32_8x4_cortexa7 17 32 8 0.000010470053145450968 +armv7neon_mmm_f32_8x4_generic 17 4 8 0.000003846416592921501 +armv7neon_mmm_f32_8x4_cortexa7 25 32 7 0.000014416885420743293 +armv7neon_mmm_f32_8x6_cortexa9 7 4 7 0.000002316979424271203 +armv7neon_mmm_f32_8x6_cortexa9 16 4 17 0.000004718807435203576 +generic_f32_4x4 11 32 12 0.000011882704283190954 +armv7neon_mmm_f32_8x4_cortexa7 23 32 9 0.000016122379106643026 +armv7neon_mmm_f32_8x6_cortexa7 9 32 11 0.000009102642142770847 +armv7neon_mmm_f32_8x6_generic 25 4 7 0.000006116994655414601 +armv7neon_mmm_f32_8x4_generic 23 4 3 0.000002685832196401119 +armv7neon_mmm_f32_8x6_cortexa9 25 32 11 0.000014409038834226244 +armv7neon_mmm_f32_8x6_generic 23 32 12 0.000010966793413222081 +armv7neon_mmm_f32_8x4_cortexa7 15 4 8 0.0000032350546103453447 +armv7neon_mmm_f32_8x6_cortexa7 15 128 7 0.000027943938585492828 +generic_f32_4x4 7 4 4 0.0000017389973856077342 +armv7neon_mmm_f32_8x6_generic 8 4 12 0.0000017181145762324274 +armv7neon_mmm_f32_8x4_cortexa9 15 4 11 0.000004502163536054048 +armv7neon_mmm_f32_8x6_generic 15 128 6 0.000010890629782885533 +armv7neon_mmm_f32_8x4_cortexa7 23 128 12 0.00004796960186341638 +armv7neon_mmm_f32_8x6_generic 8 32 7 0.000003896823245256928 +armv7neon_mmm_f32_8x6_cortexa9 15 128 7 0.000021306419489905895 +armv7neon_mmm_f32_8x6_cortexa7 7 4 11 0.0000024493622993257104 +armv7neon_mmm_f32_8x6_cortexa7 7 32 19 0.000009918011828071643 +armv7neon_mmm_f32_8x4_cortexa9 24 4 7 0.000004277922454765341 +armv7neon_mmm_f32_8x4_cortexa7 9 4 5 0.0000031825583861368363 +armv7neon_mmm_f32_8x6_cortexa9 8 32 11 0.000004007622996506884 +armv7neon_mmm_f32_8x6_generic 15 4 11 0.0000038091887644203395 +armv7neon_mmm_f32_8x6_cortexa7 23 128 11 0.000041674027523638683 +armv7neon_mmm_f32_8x6_cortexa7 17 4 18 0.0000068571632794369916 +armv7neon_mmm_f32_8x6_cortexa9 9 128 12 0.000020793132831426426 +armv7neon_mmm_f32_8x6_generic 25 32 19 0.000027146627303420713 +armv7neon_mmm_f32_8x6_cortexa7 9 128 19 0.00005467645413703322 +armv7neon_mmm_f32_8x6_cortexa7 8 32 11 0.0000048195055146364755 +armv7neon_mmm_f32_8x4_cortexa7 8 128 5 0.000011083761554457595 +generic_f32_4x4 7 128 12 0.000022151037088381854 +generic_f32_4x4 9 128 11 0.00003295464355640065 +armv7neon_mmm_f32_8x6_cortexa9 25 4 17 0.000009038405830025483 +generic_f32_4x4 8 32 3 0.0000032862084964825317 +armv7neon_mmm_f32_8x4_cortexa9 23 128 12 0.00003536277613858418 +armv7neon_mmm_f32_8x6_cortexa9 16 128 11 0.000021144715462112054 +armv7neon_mmm_f32_8x6_generic 17 32 5 0.000005941189535355633 +armv7neon_mmm_f32_8x6_generic 15 32 13 0.000011297818131567483 +armv7neon_mmm_f32_8x6_generic 16 32 11 0.000007448947403712638 +generic_f32_4x4 5 4 7 0.0000029999149449467176 +armv7neon_mmm_f32_8x4_generic 8 32 13 0.000005690794573889263 +armv7neon_mmm_f32_8x4_cortexa9 23 128 4 0.000012046555463702128 +armv7neon_mmm_f32_8x4_cortexa7 25 128 5 0.00004305932269978789 +armv7neon_mmm_f32_8x6_cortexa7 24 32 7 0.000013181202441221999 +armv7neon_mmm_f32_8x4_cortexa9 23 4 3 0.0000026820954481134975 +generic_f32_4x4 12 4 8 0.0000036441773649361156 +armv7neon_mmm_f32_8x6_cortexa9 24 128 17 0.000046509213530800746 +armv7neon_mmm_f32_8x6_cortexa9 8 4 6 0.0000011575433314376175 +generic_f32_4x4 3 32 4 0.0000018882099872634265 +armv7neon_mmm_f32_8x4_cortexa7 9 32 3 0.000004070589988484743 +armv7neon_mmm_f32_8x4_cortexa7 17 32 9 0.000015851096904703647 +generic_f32_4x4 13 32 12 0.00001535208709935954 +armv7neon_mmm_f32_8x6_cortexa9 25 4 19 0.000011387388616753858 +armv7neon_mmm_f32_8x6_cortexa9 23 128 5 0.00001624764366012842 +armv7neon_mmm_f32_8x4_cortexa9 9 128 12 0.00002346244933254337 +armv7neon_mmm_f32_8x4_cortexa9 25 4 12 0.000007150024568853224 +armv7neon_mmm_f32_8x6_cortexa9 15 32 12 0.000007825900269054534 +armv7neon_mmm_f32_8x4_cortexa7 25 4 12 0.000007707673254391108 +generic_f32_4x4 11 32 5 0.000008363778534607299 +armv7neon_mmm_f32_8x4_cortexa7 17 128 8 0.00003194246552397465 +armv7neon_mmm_f32_8x6_generic 8 32 6 0.0000021066884422501677 +armv7neon_mmm_f32_8x6_cortexa7 16 4 11 0.0000036716657035155366 +armv7neon_mmm_f32_8x4_generic 17 128 7 0.00002384609528431003 +armv7neon_mmm_f32_8x4_cortexa7 9 128 7 0.000021759641210777887 +armv7neon_mmm_f32_8x6_cortexa7 17 32 11 0.000013412538846571892 +generic_f32_4x4 7 32 8 0.000005690920861795519 +armv7neon_mmm_f32_8x6_generic 25 128 19 0.00008258096370056387 +armv7neon_mmm_f32_8x6_cortexa7 15 4 7 0.000003922803920269034 +armv7neon_mmm_f32_8x4_cortexa9 16 4 12 0.0000036509568763084446 +generic_f32_4x4 11 128 4 0.000011287445429820831 +armv7neon_mmm_f32_8x6_cortexa7 15 128 5 0.000014299033646853434 +armv7neon_mmm_f32_8x6_cortexa9 24 128 12 0.00003053734816197179 +armv7neon_mmm_f32_8x6_cortexa9 17 32 6 0.000005510675534687666 +armv7neon_mmm_f32_8x6_cortexa9 9 32 19 0.000014080838768872731 +generic_f32_4x4 13 128 5 0.000029375850896877852 +armv7neon_mmm_f32_8x6_cortexa7 25 32 5 0.000009454087084930695 +armv7neon_mmm_f32_8x4_generic 8 4 8 0.0000015055905623190838 +armv7neon_mmm_f32_8x4_cortexa7 16 4 13 0.00000545158088053187 +armv7neon_mmm_f32_8x6_generic 8 128 6 0.0000054873008191504395 +generic_f32_4x4 5 128 7 0.000015018624971335435 +armv7neon_mmm_f32_8x6_generic 15 128 7 0.00002123110926361701 +armv7neon_mmm_f32_8x4_cortexa7 7 32 3 0.000002283721924125051 +armv7neon_mmm_f32_8x6_generic 9 128 19 0.00004153518147749415 +armv7neon_mmm_f32_8x4_generic 9 32 11 0.000008626343403146056 +armv7neon_mmm_f32_8x6_cortexa9 9 4 6 0.0000018884740207207647 +armv7neon_mmm_f32_8x6_cortexa7 9 128 6 0.000013948466701679792 +generic_f32_4x4 11 4 12 0.000005708922503580876 +armv7neon_mmm_f32_8x6_cortexa7 24 128 6 0.000020527418679269756 +armv7neon_mmm_f32_8x4_cortexa7 8 4 11 0.0000024406051533290663 +armv7neon_mmm_f32_8x4_cortexa9 23 128 9 0.000035493958882244616 +armv7neon_mmm_f32_8x6_cortexa9 17 4 11 0.00000498803165144454 +armv7neon_mmm_f32_8x6_generic 8 32 18 0.000005320856703844532 +armv7neon_mmm_f32_8x4_cortexa9 9 128 4 0.00000810350837903274 +generic_f32_4x4 9 4 8 0.000003892676050216947 +armv7neon_mmm_f32_8x6_cortexa9 24 128 5 0.000016255452753216072 +armv7neon_mmm_f32_8x6_generic 7 4 5 0.0000013389729556607902 +armv7neon_mmm_f32_8x6_generic 17 32 19 0.000020571678410473347 +armv7neon_mmm_f32_8x6_cortexa9 7 32 13 0.0000063039500398145465 +armv7neon_mmm_f32_8x4_cortexa9 17 128 7 0.000023826784148315835 +armv7neon_mmm_f32_8x4_generic 24 4 7 0.000004274685140650307 +armv7neon_mmm_f32_8x6_cortexa9 17 32 17 0.000015955807283560996 +armv7neon_mmm_f32_8x6_cortexa7 17 4 13 0.000007179865477053954 +armv7neon_mmm_f32_8x4_cortexa9 7 32 12 0.00000488792089076881 +generic_f32_4x4 4 128 11 0.000011283150201350866 +armv7neon_mmm_f32_8x6_cortexa7 7 128 17 0.00002152414507851334 +armv7neon_mmm_f32_8x4_generic 25 32 13 0.0000216921434601138 +armv7neon_mmm_f32_8x6_generic 15 32 12 0.000007763276203034865 +generic_f32_4x4 7 128 9 0.00002231642099570999 +armv7neon_mmm_f32_8x4_generic 23 4 7 0.0000044972515990527706 +armv7neon_mmm_f32_8x4_generic 16 128 9 0.00002352868039758383 +armv7neon_mmm_f32_8x4_generic 24 32 8 0.000008105856924464553 +armv7neon_mmm_f32_8x4_cortexa7 25 4 9 0.000008239024137829502 +armv7neon_mmm_f32_8x4_generic 8 4 7 0.000001726186960943308 +armv7neon_mmm_f32_8x6_cortexa7 24 4 12 0.000004476686880431161 +armv7neon_mmm_f32_8x4_generic 8 4 5 0.0000016761254405249265 +armv7neon_mmm_f32_8x6_cortexa9 17 32 18 0.000015401920673930987 +armv7neon_mmm_f32_8x6_cortexa7 8 4 18 0.000002497401986754502 +armv7neon_mmm_f32_8x6_cortexa7 23 32 18 0.000019976606065621178 +armv7neon_mmm_f32_8x4_cortexa9 16 128 5 0.000015920451629989893 +armv7neon_mmm_f32_8x6_cortexa7 16 128 19 0.00005438554195188942 +armv7neon_mmm_f32_8x4_generic 25 32 11 0.00001669591843012648 +armv7neon_mmm_f32_8x6_cortexa9 7 32 18 0.000006518795701369289 +generic_f32_4x4 3 128 7 0.000007891377185866899 +armv7neon_mmm_f32_8x6_cortexa9 9 128 11 0.000021029476636249923 +armv7neon_mmm_f32_8x4_generic 9 32 8 0.00000573930624839856 +armv7neon_mmm_f32_8x4_cortexa9 23 32 9 0.000012851357918328979 +armv7neon_mmm_f32_8x4_cortexa7 25 128 11 0.00006422947640130408 +armv7neon_mmm_f32_8x4_cortexa7 9 128 5 0.000021690553836350107 +armv7neon_mmm_f32_8x4_cortexa7 8 128 4 0.000005680428664405247 +armv7neon_mmm_f32_8x6_cortexa9 17 32 11 0.000010949048608533127 +armv7neon_mmm_f32_8x4_cortexa9 9 32 13 0.00001119516860361631 +armv7neon_mmm_f32_8x4_cortexa9 8 128 11 0.000012017424644185006 +armv7neon_mmm_f32_8x4_cortexa9 17 4 9 0.000005874110633384251 +armv7neon_mmm_f32_8x6_cortexa7 9 4 7 0.0000035812085152963736 +armv7neon_mmm_f32_8x4_cortexa7 8 4 12 0.0000022112784369711703 +armv7neon_mmm_f32_8x6_generic 23 32 13 0.000016306177258992233 +armv7neon_mmm_f32_8x4_cortexa7 9 32 11 0.000010819099493176479 +armv7neon_mmm_f32_8x4_generic 25 128 12 0.00004665173717365099 +armv7neon_mmm_f32_8x4_cortexa9 25 4 8 0.000004903249015406008 +armv7neon_mmm_f32_8x4_generic 15 128 8 0.000016011765752869696 +generic_f32_4x4 4 4 5 0.00000173237663586998 +generic_f32_4x4 7 32 13 0.000010951743398686358 +armv7neon_mmm_f32_8x6_cortexa9 7 32 11 0.000004373180249911381 +armv7neon_mmm_f32_8x6_generic 9 4 7 0.000003340351034825373 +armv7neon_mmm_f32_8x6_cortexa7 8 128 6 0.000007164475272224828 +generic_f32_4x4 8 4 11 0.000003982256860993786 +armv7neon_mmm_f32_8x6_generic 15 128 5 0.000010930509860719574 +armv7neon_mmm_f32_8x4_cortexa9 25 128 11 0.00004712675673590545 +armv7neon_mmm_f32_8x4_cortexa7 7 4 11 0.000002822400310815202 +armv7neon_mmm_f32_8x6_cortexa7 17 32 13 0.000019470812344800868 +armv7neon_mmm_f32_8x4_cortexa7 23 32 4 0.000005619794374416781 +generic_f32_4x4 11 32 8 0.000008140965167056766 +armv7neon_mmm_f32_8x4_cortexa7 17 4 5 0.000004516405012537028 +armv7neon_mmm_f32_8x4_cortexa7 17 128 4 0.00001625158956213561 +armv7neon_mmm_f32_8x6_generic 16 128 18 0.000030484389052945016 +armv7neon_mmm_f32_8x6_cortexa7 17 128 11 0.00004127386103261296 +armv7neon_mmm_f32_8x6_cortexa7 24 32 6 0.000006586776362035912 +armv7neon_mmm_f32_8x4_generic 9 32 5 0.000005900796369564617 +armv7neon_mmm_f32_8x6_cortexa7 7 128 11 0.000014441097326114378 +armv7neon_mmm_f32_8x6_cortexa9 24 4 19 0.000008421665590898334 +armv7neon_mmm_f32_8x4_cortexa7 15 32 9 0.000011049029459002566 +armv7neon_mmm_f32_8x6_cortexa9 17 32 7 0.000010792251610714281 +armv7neon_mmm_f32_8x4_generic 24 32 11 0.000012578955321334167 +armv7neon_mmm_f32_8x6_generic 8 4 17 0.0000025848174603713075 +armv7neon_mmm_f32_8x4_cortexa9 8 128 12 0.000011812308047768464 +generic_f32_4x4 12 128 8 0.00002174331661540877 +armv7neon_mmm_f32_8x4_cortexa7 16 128 12 0.0000317241936699183 +armv7neon_mmm_f32_8x6_cortexa9 9 4 5 0.000002126610313888205 +armv7neon_mmm_f32_8x6_cortexa9 8 32 12 0.0000037644801015301184 +armv7neon_mmm_f32_8x4_cortexa9 9 32 4 0.000003081152195958602 +generic_f32_4x4 7 32 9 0.000008408374041494756 +generic_f32_4x4 4 128 4 0.0000040497405415986515 +armv7neon_mmm_f32_8x4_cortexa9 15 128 7 0.000016202393035594662 +armv7neon_mmm_f32_8x6_cortexa9 25 128 6 0.000020788151245438416 +armv7neon_mmm_f32_8x4_cortexa7 8 128 13 0.000021488179929071295 +armv7neon_mmm_f32_8x4_cortexa7 25 32 9 0.00002091079321932589 +armv7neon_mmm_f32_8x4_cortexa9 8 128 5 0.000008180420598290364 +armv7neon_mmm_f32_8x4_generic 7 128 3 0.000004423621357206741 +armv7neon_mmm_f32_8x4_cortexa9 15 4 9 0.000004394233374561228 +armv7neon_mmm_f32_8x6_generic 7 4 12 0.0000025127271377138368 +armv7neon_mmm_f32_8x4_generic 23 128 8 0.00002364193719899604 +armv7neon_mmm_f32_8x4_cortexa7 7 32 11 0.000005969501269507202 +armv7neon_mmm_f32_8x6_cortexa9 16 4 7 0.0000033362358276857998 +armv7neon_mmm_f32_8x4_generic 25 4 4 0.000002679652210124942 +armv7neon_mmm_f32_8x6_cortexa7 24 128 19 0.00008148701915820504 +armv7neon_mmm_f32_8x4_cortexa7 25 32 12 0.000020301609592643522 +armv7neon_mmm_f32_8x6_generic 25 4 12 0.000005597867792560783 +armv7neon_mmm_f32_8x4_cortexa9 16 4 13 0.0000050370172264265826 +generic_f32_4x4 11 4 8 0.000003987191713644613 +armv7neon_mmm_f32_8x4_cortexa7 7 32 9 0.00000592156011559423 +armv7neon_mmm_f32_8x6_cortexa7 25 4 13 0.000009317851927845776 +armv7neon_mmm_f32_8x6_generic 7 32 6 0.0000025009886657895365 +armv7neon_mmm_f32_8x4_cortexa9 9 32 5 0.0000059031902576708605 +armv7neon_mmm_f32_8x4_cortexa7 25 4 7 0.000005997625417148922 +armv7neon_mmm_f32_8x4_generic 15 128 9 0.000023965770004599912 +armv7neon_mmm_f32_8x4_generic 9 128 3 0.000008336141118552254 +armv7neon_mmm_f32_8x6_cortexa9 15 4 5 0.0000022485015750854945 +armv7neon_mmm_f32_8x4_cortexa7 25 32 13 0.000027508557090202434 +armv7neon_mmm_f32_8x4_cortexa9 7 32 4 0.0000019276370605991633 +armv7neon_mmm_f32_8x6_cortexa7 7 32 13 0.000007512654299558133 +generic_f32_4x4 12 128 12 0.000032295866967880904 +armv7neon_mmm_f32_8x6_generic 8 128 13 0.00001569076394434193 +armv7neon_mmm_f32_8x4_cortexa9 17 128 8 0.000023412675748873913 +armv7neon_mmm_f32_8x4_cortexa9 16 4 3 0.000001964056838467611 +armv7neon_mmm_f32_8x4_generic 16 4 4 0.0000015347326629006993 +armv7neon_mmm_f32_8x4_cortexa9 24 4 4 0.0000020572993016505205 +armv7neon_mmm_f32_8x4_cortexa9 8 32 3 0.0000019507567628415824 +armv7neon_mmm_f32_8x4_cortexa7 8 32 7 0.000004003993118643105 +armv7neon_mmm_f32_8x4_generic 17 4 13 0.000007623324255157955 +armv7neon_mmm_f32_8x6_cortexa9 9 4 11 0.0000035090633204775533 +armv7neon_mmm_f32_8x6_generic 9 4 19 0.00000601244922696809 +armv7neon_mmm_f32_8x6_generic 9 128 7 0.000020875839952630357 +generic_f32_4x4 4 32 11 0.000004326527977488747 +armv7neon_mmm_f32_8x6_cortexa9 25 32 18 0.00002022815888923194 +armv7neon_mmm_f32_8x6_cortexa9 15 4 18 0.000005434062942764287 +armv7neon_mmm_f32_8x4_cortexa9 24 4 3 0.000002704298016252254 +armv7neon_mmm_f32_8x4_generic 24 4 9 0.000005701271915969865 +armv7neon_mmm_f32_8x6_cortexa9 7 128 19 0.000021850407441008384 +armv7neon_mmm_f32_8x4_cortexa9 15 4 3 0.0000019251432800014627 +armv7neon_mmm_f32_8x6_cortexa7 15 4 18 0.000005711033859652051 +armv7neon_mmm_f32_8x6_cortexa7 9 4 17 0.000005102785803942399 +armv7neon_mmm_f32_8x4_generic 8 128 4 0.000004232482987549164 +armv7neon_mmm_f32_8x4_cortexa9 25 128 7 0.00003172200454891407 +armv7neon_mmm_f32_8x4_cortexa9 9 32 12 0.000008361571818242635 +armv7neon_mmm_f32_8x6_cortexa7 24 32 17 0.000019514235657015186 +armv7neon_mmm_f32_8x6_cortexa7 23 128 6 0.000020944404830412158 +armv7neon_mmm_f32_8x6_cortexa9 17 128 5 0.000016134142374025674 +armv7neon_mmm_f32_8x4_cortexa7 8 4 7 0.000001873057019803265 +armv7neon_mmm_f32_8x6_cortexa7 25 128 5 0.00002802974521426182 +armv7neon_mmm_f32_8x6_cortexa9 17 4 17 0.000006958050831218578 +armv7neon_mmm_f32_8x4_cortexa9 15 32 5 0.00000608182468505122 +armv7neon_mmm_f32_8x4_generic 7 128 4 0.0000044404498411270055 +generic_f32_4x4 13 4 5 0.000005350599856702572 +armv7neon_mmm_f32_8x4_generic 16 4 9 0.0000039608523692233575 +armv7neon_mmm_f32_8x6_cortexa9 16 4 12 0.000003013935297522993 +armv7neon_mmm_f32_8x6_generic 15 4 13 0.000005319052004340107 +armv7neon_mmm_f32_8x4_cortexa9 8 4 13 0.0000027408020700732427 +armv7neon_mmm_f32_8x6_generic 8 128 18 0.000015505866042830093 +armv7neon_mmm_f32_8x6_cortexa7 15 32 18 0.000013888471960481304 +armv7neon_mmm_f32_8x4_cortexa7 15 128 8 0.00002174159034943732 +armv7neon_mmm_f32_8x4_generic 9 32 4 0.0000030856158641387716 +armv7neon_mmm_f32_8x4_generic 24 4 5 0.000004129514406774627 +generic_f32_4x4 4 4 7 0.0000017497295508923947 +armv7neon_mmm_f32_8x6_generic 23 32 17 0.00001653018733416135 +generic_f32_4x4 7 128 13 0.000029515913541067617 +armv7neon_mmm_f32_8x6_cortexa9 24 32 19 0.000020360891894208073 +armv7neon_mmm_f32_8x6_cortexa9 9 32 18 0.000010557768641496435 +armv7neon_mmm_f32_8x4_generic 8 4 13 0.000002736501222546797 +armv7neon_mmm_f32_8x4_generic 16 32 11 0.000008543993772071838 +armv7neon_mmm_f32_8x4_cortexa7 7 32 12 0.0000059782618274442185 +armv7neon_mmm_f32_8x6_cortexa7 8 128 11 0.000014095624176931188 +armv7neon_mmm_f32_8x4_cortexa7 7 32 13 0.00000775363354362587 +armv7neon_mmm_f32_8x4_cortexa7 24 128 5 0.000032299734402349505 +armv7neon_mmm_f32_8x6_cortexa9 25 32 12 0.000013697867410484751 +armv7neon_mmm_f32_8x4_cortexa7 24 128 4 0.000016113162651910352 +armv7neon_mmm_f32_8x6_cortexa9 9 32 5 0.0000041006560791439575 +generic_f32_4x4 7 4 11 0.000004296735480110931 +armv7neon_mmm_f32_8x6_generic 8 32 11 0.000003977003783384346 +armv7neon_mmm_f32_8x4_generic 24 32 13 0.000016227668114322495 +armv7neon_mmm_f32_8x6_generic 7 32 5 0.0000023419854212729095 +generic_f32_4x4 3 128 3 0.00000421323412258659 +generic_f32_4x4 8 4 12 0.0000036950676170218278 +armv7neon_mmm_f32_8x4_cortexa7 15 4 12 0.000004609879642718733 +armv7neon_mmm_f32_8x6_generic 8 128 11 0.000010745832002669769 +armv7neon_mmm_f32_8x6_cortexa7 16 32 18 0.000012726640475291294 +armv7neon_mmm_f32_8x6_generic 24 4 13 0.000006477448734654056 +armv7neon_mmm_f32_8x4_cortexa9 23 128 5 0.000023893090361578605 +armv7neon_mmm_f32_8x6_cortexa9 7 128 7 0.000011059489910827611 +armv7neon_mmm_f32_8x4_cortexa7 24 4 13 0.000007916232793484605 +armv7neon_mmm_f32_8x6_generic 24 32 5 0.000006050244864860637 +armv7neon_mmm_f32_8x6_generic 23 4 5 0.000003050740283377829 +armv7neon_mmm_f32_8x4_generic 17 4 7 0.000004317179186717401 +generic_f32_4x4 13 128 3 0.000015316541870867625 +armv7neon_mmm_f32_8x6_generic 25 128 11 0.00004196449161824797 +armv7neon_mmm_f32_8x6_generic 24 32 7 0.000010654786791684403 +armv7neon_mmm_f32_8x6_generic 9 128 13 0.0000310078542141026 +armv7neon_mmm_f32_8x4_cortexa7 17 128 13 0.00006395441906545403 +armv7neon_mmm_f32_8x6_cortexa7 25 4 11 0.000006802751222800881 +armv7neon_mmm_f32_8x6_cortexa7 16 32 12 0.000008630037205832748 +armv7neon_mmm_f32_8x6_cortexa9 8 128 6 0.000005530202507023102 +armv7neon_mmm_f32_8x4_cortexa7 24 4 3 0.000002895093469687887 +armv7neon_mmm_f32_8x6_cortexa7 23 32 13 0.00002011431966689971 +armv7neon_mmm_f32_8x6_cortexa7 8 128 17 0.000020781906415830542 +armv7neon_mmm_f32_8x6_cortexa7 8 128 7 0.00001401297102177983 +generic_f32_4x4 12 128 4 0.000011113783764067 +armv7neon_mmm_f32_8x4_cortexa9 23 128 8 0.000023633711148043694 +armv7neon_mmm_f32_8x6_cortexa7 8 32 7 0.00000473866242690848 +armv7neon_mmm_f32_8x6_cortexa7 23 128 13 0.00006200535049902772 +armv7neon_mmm_f32_8x6_generic 25 128 12 0.000040933940992834904 +armv7neon_mmm_f32_8x6_generic 8 4 7 0.0000019018312390917206 +armv7neon_mmm_f32_8x6_cortexa9 9 128 13 0.0000311113672962118 +armv7neon_mmm_f32_8x6_cortexa7 17 32 6 0.000006712613519894369 +armv7neon_mmm_f32_8x6_generic 17 32 17 0.00001584305104507533 +armv7neon_mmm_f32_8x6_cortexa9 25 4 5 0.000003846834546374256 +armv7neon_mmm_f32_8x4_generic 23 128 11 0.00003563405640596688 +armv7neon_mmm_f32_8x6_cortexa7 7 32 18 0.000007762095957340233 +armv7neon_mmm_f32_8x4_cortexa7 8 4 3 0.000001278724116798178 +armv7neon_mmm_f32_8x4_generic 8 32 9 0.000004428552791795867 +armv7neon_mmm_f32_8x4_cortexa7 9 32 8 0.000007212913220352459 +armv7neon_mmm_f32_8x6_cortexa7 16 32 11 0.000009132197785046325 +armv7neon_mmm_f32_8x6_generic 15 4 6 0.0000021293993921351435 +armv7neon_mmm_f32_8x6_cortexa9 8 32 17 0.000005618722861518267 +generic_f32_4x4 12 32 8 0.000007824605805291677 +generic_f32_4x4 3 4 9 0.0000025426825999668207 +armv7neon_mmm_f32_8x6_cortexa9 15 128 6 0.000010938928121944877 +armv7neon_mmm_f32_8x6_generic 8 128 12 0.000010479271785006985 +armv7neon_mmm_f32_8x6_cortexa7 25 4 18 0.000008843120394707296 +generic_f32_4x4 5 128 8 0.000014867005692635484 +armv7neon_mmm_f32_8x4_generic 25 4 12 0.000007134980758480078 +armv7neon_mmm_f32_8x6_generic 23 4 6 0.0000027550692223444384 +generic_f32_4x4 8 128 4 0.000007594935908642548 +armv7neon_mmm_f32_8x4_generic 24 4 3 0.0000027070026756528944 +armv7neon_mmm_f32_8x6_generic 15 4 7 0.0000036879615500804916 +armv7neon_mmm_f32_8x6_cortexa7 8 4 13 0.0000026670243954904784 +armv7neon_mmm_f32_8x6_generic 17 4 7 0.000004739940833422853 +armv7neon_mmm_f32_8x6_generic 24 128 11 0.00003139196614178293 +armv7neon_mmm_f32_8x6_cortexa9 24 128 13 0.0000461884138564535 +generic_f32_4x4 9 128 9 0.00003291260190607621 +armv7neon_mmm_f32_8x4_cortexa9 7 128 11 0.000012420788787006248 +armv7neon_mmm_f32_8x6_generic 17 128 12 0.00003088346853188957 +armv7neon_mmm_f32_8x4_cortexa7 8 4 13 0.000002968959195086507 +armv7neon_mmm_f32_8x6_cortexa9 24 128 6 0.000015548482246921477 +armv7neon_mmm_f32_8x4_cortexa7 17 32 7 0.000010943141781236415 +armv7neon_mmm_f32_8x6_cortexa7 16 4 12 0.000003160151719580885 +generic_f32_4x4 5 128 5 0.000014996303737029157 +armv7neon_mmm_f32_8x6_generic 25 4 6 0.0000030664133228659144 +armv7neon_mmm_f32_8x4_cortexa9 9 4 11 0.0000041810312425253365 +armv7neon_mmm_f32_8x6_generic 15 4 18 0.000005353071509503547 +armv7neon_mmm_f32_8x6_cortexa7 8 32 12 0.000004562116220290897 +armv7neon_mmm_f32_8x4_cortexa9 8 32 7 0.0000032056568363054847 +armv7neon_mmm_f32_8x4_generic 16 4 11 0.0000040609047361039316 +armv7neon_mmm_f32_8x6_cortexa9 17 4 19 0.00000876633957316096 +armv7neon_mmm_f32_8x6_cortexa7 15 128 18 0.00004180200146350024 +generic_f32_4x4 5 128 9 0.000022192516532888443 +armv7neon_mmm_f32_8x6_cortexa9 24 32 11 0.000010954278251461477 +armv7neon_mmm_f32_8x6_cortexa9 8 32 5 0.000002372663230804753 +armv7neon_mmm_f32_8x6_generic 8 4 5 0.0000013605175071348517 +armv7neon_mmm_f32_8x6_cortexa7 24 4 6 0.0000025054020094278158 +armv7neon_mmm_f32_8x6_generic 24 128 5 0.000016222838915570737 +generic_f32_4x4 12 32 4 0.000004159176762509275 +generic_f32_4x4 13 4 11 0.000007582485226342242 +generic_f32_4x4 9 4 5 0.000004196638831417376 +armv7neon_mmm_f32_8x6_cortexa7 25 32 17 0.000026071317528366354 +armv7neon_mmm_f32_8x4_cortexa9 16 128 8 0.000015686411664235285 +generic_f32_4x4 13 128 11 0.00004380249651194818 +armv7neon_mmm_f32_8x4_cortexa7 24 32 13 0.000020563104321953744 +armv7neon_mmm_f32_8x4_cortexa9 23 4 7 0.0000044861575535086865 +generic_f32_4x4 12 128 3 0.000011613772328414718 +armv7neon_mmm_f32_8x6_cortexa7 16 4 19 0.000006163473976132481 +generic_f32_4x4 7 4 8 0.0000029313315346635237 +armv7neon_mmm_f32_8x4_cortexa9 23 32 12 0.000012545342985454958 +armv7neon_mmm_f32_8x6_cortexa9 7 128 11 0.000011136453503507945 +armv7neon_mmm_f32_8x6_cortexa7 9 128 13 0.000041110907972239005 +armv7neon_mmm_f32_8x4_cortexa9 7 32 8 0.000003418961974304266 +armv7neon_mmm_f32_8x4_cortexa7 24 32 12 0.000015105629538772432 +generic_f32_4x4 7 128 3 0.000007903637217361153 +armv7neon_mmm_f32_8x6_cortexa7 9 4 11 0.000003661168986658601 +generic_f32_4x4 4 128 9 0.00001126621217728165 +armv7neon_mmm_f32_8x4_cortexa7 23 128 3 0.000016760586455833876 +armv7neon_mmm_f32_8x6_cortexa9 16 32 12 0.000007056930252112048 +armv7neon_mmm_f32_8x6_cortexa9 8 4 19 0.000003165350777332514 +armv7neon_mmm_f32_8x6_generic 8 32 13 0.000005498586776368978 +generic_f32_4x4 3 128 13 0.000015227075547579779 +generic_f32_4x4 12 4 13 0.000007217490541690208 +armv7neon_mmm_f32_8x6_cortexa7 16 128 18 0.000040584526180649477 +armv7neon_mmm_f32_8x6_cortexa9 25 4 7 0.000006216705461889991 +armv7neon_mmm_f32_8x4_generic 24 128 9 0.000035021164515684145 +generic_f32_4x4 3 32 12 0.000004609887398147942 +armv7neon_mmm_f32_8x4_generic 25 128 9 0.00004684932933132534 +armv7neon_mmm_f32_8x6_generic 7 4 17 0.0000033628632970040393 +generic_f32_4x4 4 128 12 0.000011148897561634414 +armv7neon_mmm_f32_8x4_generic 8 128 8 0.000008028324048468155 +armv7neon_mmm_f32_8x4_generic 8 4 11 0.0000022515619534309057 +armv7neon_mmm_f32_8x4_cortexa7 9 32 5 0.00000739044702644143 +armv7neon_mmm_f32_8x4_generic 8 128 5 0.000008179707225297848 +armv7neon_mmm_f32_8x4_generic 9 4 4 0.0000016079691037580586 +armv7neon_mmm_f32_8x4_generic 16 128 4 0.00000803169255760427 +armv7neon_mmm_f32_8x4_generic 25 128 3 0.000016355748669331108 +armv7neon_mmm_f32_8x6_cortexa9 24 4 18 0.0000061179448610506165 +armv7neon_mmm_f32_8x6_cortexa9 16 4 19 0.000005803928147585998 +armv7neon_mmm_f32_8x6_cortexa7 17 128 6 0.00002064753338392539 +armv7neon_mmm_f32_8x4_cortexa9 9 4 12 0.000003936841431536775 +armv7neon_mmm_f32_8x4_cortexa7 17 32 13 0.000020815595163068456 +armv7neon_mmm_f32_8x6_cortexa7 23 32 11 0.000013810001606181058 +armv7neon_mmm_f32_8x4_cortexa7 15 32 13 0.00001449569792912252 +armv7neon_mmm_f32_8x4_cortexa7 7 4 3 0.0000012380899483592991 +armv7neon_mmm_f32_8x6_cortexa9 23 128 17 0.00004728149304262184 +armv7neon_mmm_f32_8x4_cortexa7 15 32 3 0.000004158381257275162 +armv7neon_mmm_f32_8x4_cortexa7 9 4 9 0.000004430434184731522 +armv7neon_mmm_f32_8x6_generic 23 128 13 0.00004715382202102794 +armv7neon_mmm_f32_8x6_cortexa9 9 4 19 0.000006116079821760871 +armv7neon_mmm_f32_8x6_cortexa9 7 4 18 0.0000035442600221807646 +armv7neon_mmm_f32_8x4_generic 15 128 5 0.000016261604433183137 +armv7neon_mmm_f32_8x4_cortexa9 8 128 4 0.000004251382345566181 +armv7neon_mmm_f32_8x6_cortexa7 25 128 11 0.000055294824394928404 +armv7neon_mmm_f32_8x6_cortexa9 23 128 13 0.00004716175024845285 +generic_f32_4x4 12 128 11 0.00003283409583810123 +armv7neon_mmm_f32_8x6_cortexa7 15 128 6 0.000014244472752339967 +armv7neon_mmm_f32_8x4_generic 17 32 8 0.000008307634701683482 +armv7neon_mmm_f32_8x4_generic 16 4 13 0.000005034888545956025 +armv7neon_mmm_f32_8x6_cortexa7 9 128 11 0.000027682771225447373 +armv7neon_mmm_f32_8x4_generic 25 128 8 0.000030970064243633 +armv7neon_mmm_f32_8x6_cortexa9 15 32 5 0.0000042126470351483745 +armv7neon_mmm_f32_8x6_cortexa9 8 128 7 0.000010688502492077545 +armv7neon_mmm_f32_8x6_cortexa7 8 32 19 0.000008785622923040567 +generic_f32_4x4 8 32 7 0.000005699367356537332 +armv7neon_mmm_f32_8x4_cortexa9 23 4 13 0.000007982321009915726 +armv7neon_mmm_f32_8x6_cortexa9 7 32 19 0.000008296837727225683 +armv7neon_mmm_f32_8x4_cortexa9 17 128 12 0.00003482723156416195 +armv7neon_mmm_f32_8x4_cortexa7 16 4 12 0.000003962627391393735 +armv7neon_mmm_f32_8x4_generic 24 32 4 0.000004275975584058462 +armv7neon_mmm_f32_8x4_generic 15 4 4 0.00000172768002087017 +generic_f32_4x4 12 128 13 0.000043289852359027704 +armv7neon_mmm_f32_8x6_cortexa7 24 32 18 0.00001876149316286456 +armv7neon_mmm_f32_8x4_cortexa9 23 128 13 0.000047081790611742305 +armv7neon_mmm_f32_8x4_generic 25 4 8 0.000004894765654717258 +generic_f32_4x4 13 32 9 0.000015809325344243507 +armv7neon_mmm_f32_8x6_cortexa9 15 32 11 0.000007869115330631703 +armv7neon_mmm_f32_8x6_cortexa7 16 32 19 0.000017064208112188813 +armv7neon_mmm_f32_8x4_cortexa7 9 32 12 0.000010571719001405112 +armv7neon_mmm_f32_8x6_cortexa9 7 4 12 0.000002545629385927187 +armv7neon_mmm_f32_8x4_cortexa9 24 128 5 0.00002368492009732677 +armv7neon_mmm_f32_8x6_cortexa7 25 4 5 0.000004026698373090156 +generic_f32_4x4 9 32 9 0.000012044684258478282 +armv7neon_mmm_f32_8x6_cortexa9 25 128 12 0.000040909803644855135 +generic_f32_4x4 8 128 12 0.00002170370665519434 +armv7neon_mmm_f32_8x6_generic 23 4 13 0.000007322110702891982 +armv7neon_mmm_f32_8x4_generic 8 128 9 0.000011964404434625917 +armv7neon_mmm_f32_8x6_generic 24 4 6 0.00000234486203657902 +armv7neon_mmm_f32_8x4_generic 16 128 12 0.000023289775015844785 +armv7neon_mmm_f32_8x4_generic 25 32 4 0.00000566239966628999 +armv7neon_mmm_f32_8x4_cortexa7 8 128 7 0.000011133117389377919 +armv7neon_mmm_f32_8x4_cortexa7 23 128 7 0.000032614653457132354 +generic_f32_4x4 11 128 3 0.000011596634661777904 +armv7neon_mmm_f32_8x6_generic 9 32 7 0.000007301874181587425 +armv7neon_mmm_f32_8x6_cortexa7 25 4 6 0.0000032842534990270153 +armv7neon_mmm_f32_8x4_cortexa7 25 4 11 0.000008398472143479583 +armv7neon_mmm_f32_8x4_cortexa9 17 128 3 0.000012367498051511246 +armv7neon_mmm_f32_8x4_cortexa9 15 4 4 0.0000017226474127640693 +armv7neon_mmm_f32_8x6_cortexa9 8 4 12 0.0000017663174571893155 +armv7neon_mmm_f32_8x6_generic 24 32 18 0.000014939528824089691 +armv7neon_mmm_f32_8x4_cortexa9 15 128 3 0.000008424928636646119 +armv7neon_mmm_f32_8x4_cortexa9 25 32 8 0.000010844406621465396 +armv7neon_mmm_f32_8x6_generic 24 128 6 0.00001549274717546449 +armv7neon_mmm_f32_8x4_cortexa9 7 4 8 0.0000019411905486089406 +generic_f32_4x4 5 4 12 0.000003970968097394295 +armv7neon_mmm_f32_8x4_cortexa7 17 128 3 0.000016675513856755485 +armv7neon_mmm_f32_8x6_generic 15 128 17 0.0000319988391989146 +armv7neon_mmm_f32_8x4_generic 8 4 3 0.0000012046114173177262 +armv7neon_mmm_f32_8x4_generic 9 4 13 0.000005271381310779438 +armv7neon_mmm_f32_8x6_generic 25 4 5 0.000003787680148419445 +armv7neon_mmm_f32_8x6_cortexa9 16 4 13 0.00000455965655028581 +armv7neon_mmm_f32_8x6_generic 16 128 12 0.000020507038874403576 +armv7neon_mmm_f32_8x4_cortexa9 7 4 3 0.0000011789464739113475 +armv7neon_mmm_f32_8x4_cortexa7 15 4 9 0.000004718086287004601 +generic_f32_4x4 3 32 8 0.0000032590697277049294 +armv7neon_mmm_f32_8x4_cortexa7 25 32 11 0.000021029318586240916 +armv7neon_mmm_f32_8x4_cortexa9 7 128 7 0.000008425064090676705 +armv7neon_mmm_f32_8x4_cortexa9 23 128 7 0.000024027014834706714 +armv7neon_mmm_f32_8x4_cortexa9 9 128 13 0.000031337531340777375 +generic_f32_4x4 12 32 3 0.0000046491920750409604 +armv7neon_mmm_f32_8x4_cortexa7 9 128 12 0.00003204704023977606 +armv7neon_mmm_f32_8x6_cortexa9 15 128 5 0.000010976146066847136 +armv7neon_mmm_f32_8x6_generic 9 128 5 0.000010821808595127155 +armv7neon_mmm_f32_8x6_cortexa9 16 32 18 0.000010217778748183444 +armv7neon_mmm_f32_8x6_cortexa9 8 32 13 0.000005537991894862071 +armv7neon_mmm_f32_8x6_cortexa7 15 128 11 0.000028075729994494098 +armv7neon_mmm_f32_8x6_cortexa7 24 4 7 0.000004992618899850428 +armv7neon_mmm_f32_8x6_generic 15 32 6 0.000004128781340215273 +armv7neon_mmm_f32_8x6_generic 15 32 18 0.000011342396294923752 +generic_f32_4x4 3 128 9 0.000011614205849736826 +armv7neon_mmm_f32_8x4_generic 7 32 9 0.000004862452767728157 +armv7neon_mmm_f32_8x4_cortexa9 17 4 7 0.000004308345799806778 +armv7neon_mmm_f32_8x6_cortexa9 16 32 13 0.000010598291750058129 +armv7neon_mmm_f32_8x4_generic 17 32 7 0.000008775628241420315 +armv7neon_mmm_f32_8x4_cortexa9 8 4 8 0.00000151354666080862 +armv7neon_mmm_f32_8x6_cortexa7 9 4 19 0.0000064774482704190535 +armv7neon_mmm_f32_8x4_cortexa9 9 4 4 0.000001610951102254284 +armv7neon_mmm_f32_8x4_cortexa7 7 128 3 0.00000588268473785512 +armv7neon_mmm_f32_8x4_cortexa9 9 128 7 0.00001614593088465898 +armv7neon_mmm_f32_8x6_cortexa9 9 128 6 0.000010694366815766805 +armv7neon_mmm_f32_8x6_cortexa9 17 4 5 0.000003000192050393217 +armv7neon_mmm_f32_8x4_cortexa9 8 32 4 0.0000017239720455885444 +armv7neon_mmm_f32_8x6_cortexa7 25 128 18 0.0000814675708223554 +generic_f32_4x4 12 128 7 0.000022191318720751985 +generic_f32_4x4 5 128 13 0.000029341360540777852 +generic_f32_4x4 7 4 5 0.000003063610185404798 +armv7neon_mmm_f32_8x4_cortexa7 23 32 12 0.000015790133040211727 +armv7neon_mmm_f32_8x6_cortexa7 17 128 13 0.00006139081303315256 +armv7neon_mmm_f32_8x4_cortexa9 8 32 12 0.000004268120941798172 +armv7neon_mmm_f32_8x6_generic 16 32 17 0.000010656142728555816 +generic_f32_4x4 13 4 8 0.000004925203028119758 +armv7neon_mmm_f32_8x6_generic 16 4 6 0.0000017414671485444925 +armv7neon_mmm_f32_8x6_generic 23 4 7 0.000005090461887505994 +armv7neon_mmm_f32_8x4_cortexa9 7 128 4 0.000004439163092495918 +armv7neon_mmm_f32_8x4_cortexa9 15 32 4 0.000003199516110418427 +generic_f32_4x4 13 4 9 0.000007521941079552681 +armv7neon_mmm_f32_8x4_generic 9 128 13 0.00003132672472959027 +armv7neon_mmm_f32_8x6_cortexa7 9 128 5 0.000014186965083572584 +armv7neon_mmm_f32_8x4_cortexa9 16 32 13 0.000010950995485777078 +armv7neon_mmm_f32_8x4_cortexa7 24 4 11 0.000006343455120468695 +armv7neon_mmm_f32_8x4_generic 23 32 12 0.000012518582750578088 +armv7neon_mmm_f32_8x4_cortexa9 23 4 5 0.000004357470789736493 +armv7neon_mmm_f32_8x6_generic 9 128 12 0.000020738827544116163 +armv7neon_mmm_f32_8x4_generic 7 128 8 0.000008441257822863349 +armv7neon_mmm_f32_8x6_generic 25 4 18 0.000008206815180054958 +armv7neon_mmm_f32_8x6_cortexa7 16 128 7 0.00002754330318348781 +armv7neon_mmm_f32_8x4_cortexa9 15 128 12 0.000023818850811398474 +armv7neon_mmm_f32_8x6_cortexa7 7 32 5 0.0000027477786822716826 +generic_f32_4x4 4 32 9 0.000004310962413140207 +armv7neon_mmm_f32_8x6_cortexa9 9 32 11 0.000007472405716714093 +generic_f32_4x4 9 4 7 0.0000042110601811121985 +armv7neon_mmm_f32_8x4_cortexa9 25 128 5 0.00003145000193606305 +armv7neon_mmm_f32_8x6_generic 16 128 7 0.000020961637268522957 +armv7neon_mmm_f32_8x6_cortexa7 8 128 18 0.00002061441681055432 +armv7neon_mmm_f32_8x6_cortexa7 15 32 11 0.000009542090957976056 +armv7neon_mmm_f32_8x4_cortexa9 8 32 8 0.0000030204030341535007 +armv7neon_mmm_f32_8x6_cortexa9 16 4 18 0.000004289256443600587 +armv7neon_mmm_f32_8x4_cortexa7 15 32 8 0.000007451057144596197 +armv7neon_mmm_f32_8x6_cortexa7 25 128 12 0.00005431857023175915 +armv7neon_mmm_f32_8x6_generic 7 128 11 0.000011099973931488493 +armv7neon_mmm_f32_8x4_cortexa9 16 128 11 0.000023648803168223167 +armv7neon_mmm_f32_8x6_cortexa9 17 4 12 0.0000044808063412694585 +armv7neon_mmm_f32_8x6_generic 25 32 7 0.000014053446208239492 +armv7neon_mmm_f32_8x6_generic 16 128 19 0.0000411706712809725 +armv7neon_mmm_f32_8x4_generic 16 32 13 0.000010937227005963747 +armv7neon_mmm_f32_8x4_cortexa7 8 32 3 0.000002329652194595725 +armv7neon_mmm_f32_8x4_cortexa9 23 4 11 0.000006308879061820964 +armv7neon_mmm_f32_8x6_cortexa7 16 4 7 0.0000035065261101064537 +armv7neon_mmm_f32_8x6_generic 9 128 6 0.000010599309996894829 +armv7neon_mmm_f32_8x4_cortexa7 24 128 11 0.000048035927729516165 +armv7neon_mmm_f32_8x6_generic 7 32 11 0.000004333152377602563 +armv7neon_mmm_f32_8x4_cortexa7 15 4 11 0.000004824968358151188 +armv7neon_mmm_f32_8x6_cortexa7 7 32 6 0.0000029036965940094368 +generic_f32_4x4 12 4 3 0.0000026010633583656195 +armv7neon_mmm_f32_8x6_cortexa9 8 32 18 0.000005373878070970815 +armv7neon_mmm_f32_8x4_generic 16 128 7 0.000016057054409709098 +armv7neon_mmm_f32_8x4_generic 17 128 5 0.000023713194238003484 +armv7neon_mmm_f32_8x6_generic 24 128 19 0.0000639824281045487 +generic_f32_4x4 5 4 13 0.000005260715034760787 +armv7neon_mmm_f32_8x6_cortexa9 16 128 19 0.000040932625772708764 +armv7neon_mmm_f32_8x4_generic 16 32 5 0.00000587187854127478 +armv7neon_mmm_f32_8x4_generic 15 32 4 0.0000032031359648686487 +armv7neon_mmm_f32_8x4_cortexa9 24 32 13 0.00001619115724351509 +armv7neon_mmm_f32_8x4_generic 15 32 8 0.000005976862649343733 +armv7neon_mmm_f32_8x6_cortexa9 16 32 7 0.000007329062657266122 +armv7neon_mmm_f32_8x4_cortexa9 25 32 7 0.00001147457512424339 +armv7neon_mmm_f32_8x4_cortexa9 24 128 3 0.000012480179872163079 +armv7neon_mmm_f32_8x6_generic 16 4 13 0.000004503173649426117 +armv7neon_mmm_f32_8x6_generic 16 4 19 0.000005734445256719681 +armv7neon_mmm_f32_8x4_cortexa7 24 4 8 0.000003952435244053743 +armv7neon_mmm_f32_8x6_cortexa9 9 32 6 0.000003904765036328731 +armv7neon_mmm_f32_8x6_cortexa7 23 4 13 0.000007856276222270553 +armv7neon_mmm_f32_8x6_cortexa9 15 4 6 0.000002182843988681052 +armv7neon_mmm_f32_8x6_generic 23 4 18 0.000007195841362691159 +armv7neon_mmm_f32_8x4_cortexa7 16 32 12 0.000010255406371017558 +armv7neon_mmm_f32_8x4_cortexa9 25 128 8 0.0000309762014941751 +armv7neon_mmm_f32_8x6_cortexa7 25 128 17 0.00008204444005539351 +armv7neon_mmm_f32_8x6_cortexa7 17 4 6 0.0000026268622339640763 +armv7neon_mmm_f32_8x4_cortexa9 15 32 9 0.000008859604622252251 +generic_f32_4x4 5 32 9 0.000008264557275071169 +generic_f32_4x4 13 32 5 0.000010823964577863127 +generic_f32_4x4 5 4 3 0.000001851289980714991 +armv7neon_mmm_f32_8x4_generic 15 128 4 0.000008225264190077282 +armv7neon_mmm_f32_8x6_generic 17 32 18 0.000015295586544658026 +armv7neon_mmm_f32_8x6_cortexa9 9 4 18 0.000004572833723975051 +armv7neon_mmm_f32_8x4_cortexa7 15 4 13 0.000006075307141015665 +armv7neon_mmm_f32_8x6_cortexa7 17 32 19 0.000025630924279728742 +generic_f32_4x4 7 32 7 0.0000058385014382941885 +armv7neon_mmm_f32_8x4_cortexa9 16 128 7 0.000016054999854639244 +armv7neon_mmm_f32_8x4_cortexa7 25 4 4 0.0000029326008363454604 +armv7neon_mmm_f32_8x6_cortexa7 16 128 12 0.000027204121533574832 +armv7neon_mmm_f32_8x4_generic 15 128 3 0.000008426038232256742 +armv7neon_mmm_f32_8x4_cortexa9 7 32 5 0.0000033516027030490246 +armv7neon_mmm_f32_8x4_cortexa7 16 4 7 0.0000032624014554442194 +armv7neon_mmm_f32_8x4_cortexa9 16 128 3 0.000008462143402947945 +armv7neon_mmm_f32_8x4_generic 25 4 7 0.000005557466852852917 +armv7neon_mmm_f32_8x4_generic 23 32 4 0.000004495607981128392 +armv7neon_mmm_f32_8x4_cortexa9 15 4 12 0.000004291414852711907 +generic_f32_4x4 4 4 13 0.0000027727007811134473 +armv7neon_mmm_f32_8x4_generic 16 4 8 0.0000025796442872451462 +armv7neon_mmm_f32_8x4_cortexa9 23 4 9 0.000006179316744537218 +armv7neon_mmm_f32_8x4_cortexa7 25 32 3 0.000007764249905705397 +armv7neon_mmm_f32_8x6_generic 17 32 7 0.000010687826852062675 +generic_f32_4x4 13 4 7 0.000005399817435719611 +generic_f32_4x4 12 4 4 0.0000021094685699886824 +armv7neon_mmm_f32_8x6_cortexa9 7 4 17 0.0000034025163658270913 +generic_f32_4x4 4 4 9 0.0000022359497586694876 +armv7neon_mmm_f32_8x6_cortexa9 17 128 17 0.00004660020476545699 +armv7neon_mmm_f32_8x4_generic 15 4 12 0.00000428385702551851 +armv7neon_mmm_f32_8x4_generic 9 4 11 0.000004185007110545824 +armv7neon_mmm_f32_8x6_cortexa9 24 128 18 0.000045657972165917084 +armv7neon_mmm_f32_8x6_cortexa9 15 4 19 0.000007027336593004323 +armv7neon_mmm_f32_8x4_cortexa9 9 128 9 0.00002367365046977455 +armv7neon_mmm_f32_8x4_cortexa9 16 4 9 0.000003960113146754688 +armv7neon_mmm_f32_8x4_cortexa7 8 32 9 0.000005561279017749759 +armv7neon_mmm_f32_8x4_cortexa9 23 32 3 0.000004897369194486712 +armv7neon_mmm_f32_8x4_cortexa9 16 32 3 0.0000034377989421061634 +armv7neon_mmm_f32_8x6_generic 25 32 5 0.00000775592482259445 +armv7neon_mmm_f32_8x4_cortexa9 9 128 11 0.000023733495515623523 +armv7neon_mmm_f32_8x4_generic 23 32 7 0.000008935361824557136 +armv7neon_mmm_f32_8x4_cortexa7 7 128 9 0.00001664390737173506 +generic_f32_4x4 5 4 8 0.0000028477215638418112 +armv7neon_mmm_f32_8x6_generic 23 4 19 0.000009557906532901389 +armv7neon_mmm_f32_8x6_cortexa9 16 128 12 0.000020636825667982075 +armv7neon_mmm_f32_8x6_generic 9 32 11 0.000007421682594966975 +armv7neon_mmm_f32_8x4_generic 23 128 13 0.000047353976390946026 +armv7neon_mmm_f32_8x4_cortexa7 17 4 7 0.000004622886247456753 +armv7neon_mmm_f32_8x6_cortexa9 8 128 5 0.000005754129281131009 +armv7neon_mmm_f32_8x4_cortexa7 9 128 11 0.000032319021391833177 +armv7neon_mmm_f32_8x4_cortexa9 24 4 9 0.000005698490674755626 +armv7neon_mmm_f32_8x6_generic 17 4 18 0.000006326121915439696 +armv7neon_mmm_f32_8x4_cortexa9 16 128 12 0.000023196293109823396 +armv7neon_mmm_f32_8x4_cortexa9 25 32 9 0.000016534317998128363 +armv7neon_mmm_f32_8x6_cortexa7 24 4 5 0.0000032362274636792855 +armv7neon_mmm_f32_8x6_cortexa7 9 32 13 0.000013201464669659568 +armv7neon_mmm_f32_8x4_generic 25 128 5 0.000031432252054608495 +armv7neon_mmm_f32_8x4_cortexa7 16 32 8 0.000007004694197132091 +armv7neon_mmm_f32_8x4_cortexa7 25 128 8 0.00004234315278499361 +armv7neon_mmm_f32_8x6_cortexa9 25 128 11 0.00004162498921103509 +armv7neon_mmm_f32_8x4_cortexa9 17 4 11 0.000005990145971795821 +armv7neon_mmm_f32_8x6_generic 7 128 5 0.000005711586442858516 +generic_f32_4x4 5 32 7 0.000005745084475312582 +armv7neon_mmm_f32_8x4_cortexa7 17 4 3 0.0000027778998365925647 +armv7neon_mmm_f32_8x6_generic 7 32 17 0.000006323975084622719 +armv7neon_mmm_f32_8x4_cortexa7 24 32 4 0.0000053627426905310905 +armv7neon_mmm_f32_8x6_cortexa9 7 128 13 0.000016465054999968026 +armv7neon_mmm_f32_8x6_cortexa9 25 32 17 0.000021029276181809713 +armv7neon_mmm_f32_8x6_generic 7 4 18 0.0000035040816774469106 +armv7neon_mmm_f32_8x6_cortexa7 17 128 12 0.00004084848200658872 +armv7neon_mmm_f32_8x6_cortexa7 16 128 11 0.000027707012596319906 +armv7neon_mmm_f32_8x6_cortexa7 8 128 12 0.0000138330433065325 +armv7neon_mmm_f32_8x4_cortexa9 7 32 9 0.000004837549683885907 +armv7neon_mmm_f32_8x4_generic 7 32 7 0.000003409099607922408 +armv7neon_mmm_f32_8x6_cortexa7 16 4 5 0.0000023317089411955496 +armv7neon_mmm_f32_8x6_cortexa7 7 32 17 0.000007581121899800583 +armv7neon_mmm_f32_8x4_cortexa7 16 4 9 0.0000042985642379541856 +armv7neon_mmm_f32_8x6_generic 16 4 7 0.0000032960593218992506 +armv7neon_mmm_f32_8x4_generic 15 32 5 0.0000060704297110345465 +armv7neon_mmm_f32_8x6_cortexa9 24 32 6 0.00000538028834244819 +armv7neon_mmm_f32_8x6_generic 25 32 18 0.0000200993477092098 +armv7neon_mmm_f32_8x6_generic 23 128 6 0.00001591506566691279 +armv7neon_mmm_f32_8x6_generic 16 32 13 0.00001049572423866497 +armv7neon_mmm_f32_8x4_cortexa7 15 32 5 0.000007565304970371118 +generic_f32_4x4 12 32 7 0.000008258205556820225 +armv7neon_mmm_f32_8x4_cortexa7 17 4 9 0.000006349279290785205 +generic_f32_4x4 8 128 5 0.000014938883570524155 +armv7neon_mmm_f32_8x4_generic 15 32 3 0.0000034015953691065083 +armv7neon_mmm_f32_8x6_cortexa7 7 4 18 0.000003692100644999123 +generic_f32_4x4 8 128 7 0.000015006901203342376 +generic_f32_4x4 4 4 3 0.0000012181206340328286 +generic_f32_4x4 12 32 5 0.000008206014399831754 +armv7neon_mmm_f32_8x6_cortexa9 8 128 19 0.000020717968248585576 +generic_f32_4x4 5 32 3 0.000003225094305304002 +armv7neon_mmm_f32_8x6_generic 24 4 18 0.000006021495896771489 +armv7neon_mmm_f32_8x6_cortexa7 16 128 13 0.00004093551587387655 +armv7neon_mmm_f32_8x4_cortexa9 15 128 8 0.00001604448990415154 +armv7neon_mmm_f32_8x6_generic 9 32 13 0.000010651318368732651 +armv7neon_mmm_f32_8x4_cortexa7 8 32 8 0.000003772107419321899 +generic_f32_4x4 9 32 3 0.000004621846936980697 +armv7neon_mmm_f32_8x4_cortexa9 16 32 5 0.000005906329928132943 +armv7neon_mmm_f32_8x6_cortexa9 16 4 6 0.000001808587040184933 +armv7neon_mmm_f32_8x6_cortexa9 9 32 13 0.000010778660548919894 +armv7neon_mmm_f32_8x4_cortexa9 16 32 4 0.0000030105150384631114 +armv7neon_mmm_f32_8x6_generic 17 4 17 0.000006838981837746129 +armv7neon_mmm_f32_8x6_cortexa7 7 128 19 0.000028499453971524397 +armv7neon_mmm_f32_8x6_cortexa7 23 32 17 0.000020319754403004648 +armv7neon_mmm_f32_8x6_generic 9 32 17 0.000010765407372129566 +armv7neon_mmm_f32_8x4_generic 23 4 13 0.000008003285656461567 +armv7neon_mmm_f32_8x4_generic 17 4 4 0.000002161502194915875 +armv7neon_mmm_f32_8x4_generic 23 4 11 0.000006323737070277622 +armv7neon_mmm_f32_8x4_generic 9 4 12 0.00000394067683542442 +armv7neon_mmm_f32_8x4_cortexa7 23 128 4 0.00001636523555605817 +armv7neon_mmm_f32_8x4_cortexa9 17 32 12 0.000012187305627907507 +armv7neon_mmm_f32_8x4_generic 25 32 5 0.000011305023296577953 +armv7neon_mmm_f32_8x4_cortexa7 23 128 13 0.0000641420493251813 +armv7neon_mmm_f32_8x6_cortexa9 17 4 13 0.0000067882601578195375 +armv7neon_mmm_f32_8x6_cortexa9 24 32 13 0.00001554482246687919 +armv7neon_mmm_f32_8x4_cortexa7 15 128 12 0.00003239866815806022 +armv7neon_mmm_f32_8x6_cortexa9 23 4 13 0.000007410621365009766 +armv7neon_mmm_f32_8x4_cortexa9 17 128 5 0.000023718649742852314 +armv7neon_mmm_f32_8x6_cortexa9 15 32 13 0.000011397983577281118 +armv7neon_mmm_f32_8x6_cortexa7 15 32 5 0.0000050262586648579474 +armv7neon_mmm_f32_8x4_cortexa9 16 128 9 0.000023527233248412315 +armv7neon_mmm_f32_8x6_cortexa7 17 4 17 0.000007344312997274551 +armv7neon_mmm_f32_8x6_cortexa7 15 128 13 0.00004174083709909147 +armv7neon_mmm_f32_8x4_cortexa9 24 128 11 0.00003518379984445077 +armv7neon_mmm_f32_8x6_cortexa7 15 4 6 0.0000022388984932072717 +armv7neon_mmm_f32_8x6_generic 17 32 11 0.000010862418538636322 +armv7neon_mmm_f32_8x6_cortexa9 17 128 19 0.00006205059984963943 +armv7neon_mmm_f32_8x6_cortexa7 24 128 7 0.00004110240285622754 +armv7neon_mmm_f32_8x6_generic 25 32 11 0.00001430805053910281 +armv7neon_mmm_f32_8x4_cortexa9 23 128 11 0.00003562503787212338 +armv7neon_mmm_f32_8x4_generic 8 128 13 0.00001577265144197705 +armv7neon_mmm_f32_8x4_cortexa9 15 32 3 0.0000034017274319352206 +generic_f32_4x4 13 32 3 0.000005959878460430609 +armv7neon_mmm_f32_8x4_cortexa9 8 32 9 0.000004432308412032044 +armv7neon_mmm_f32_8x4_cortexa7 8 4 5 0.0000018214760004994945 +armv7neon_mmm_f32_8x4_generic 16 4 5 0.0000029127359283609326 +armv7neon_mmm_f32_8x4_cortexa7 8 32 11 0.000005611833179795977 +armv7neon_mmm_f32_8x4_generic 17 128 12 0.00003481391410825688 +armv7neon_mmm_f32_8x6_cortexa7 17 4 19 0.00000928821589263059 +armv7neon_mmm_f32_8x6_generic 8 4 19 0.00000313513885546113 +generic_f32_4x4 11 32 11 0.000012240954381721343 +armv7neon_mmm_f32_8x4_cortexa9 9 128 8 0.000015834005248731945 +armv7neon_mmm_f32_8x6_generic 25 128 7 0.00004137887977916487 +armv7neon_mmm_f32_8x6_cortexa7 25 32 13 0.000025709475387022286 +armv7neon_mmm_f32_8x6_cortexa7 24 32 5 0.000007312970559712554 +armv7neon_mmm_f32_8x4_cortexa9 7 32 3 0.0000019121426586981612 +armv7neon_mmm_f32_8x4_cortexa9 8 4 12 0.0000020507155861457753 +armv7neon_mmm_f32_8x4_cortexa9 25 128 4 0.000015694825557140796 +armv7neon_mmm_f32_8x6_cortexa9 9 4 17 0.0000048654285121987666 +armv7neon_mmm_f32_8x4_generic 15 32 13 0.000011594181812430167 +generic_f32_4x4 8 4 5 0.0000029234671714958714 +armv7neon_mmm_f32_8x6_generic 25 128 18 0.00006398210071667638 +armv7neon_mmm_f32_8x4_generic 25 32 7 0.000011481869355677313 +armv7neon_mmm_f32_8x6_cortexa7 15 32 13 0.000013835762311238379 +armv7neon_mmm_f32_8x6_generic 7 32 7 0.000004270239691930375 +armv7neon_mmm_f32_8x4_cortexa9 24 4 12 0.0000052513903304867065 +armv7neon_mmm_f32_8x4_cortexa7 7 128 13 0.000022090489564676277 +armv7neon_mmm_f32_8x4_cortexa7 9 32 7 0.0000074510499136902 +armv7neon_mmm_f32_8x4_generic 15 128 11 0.00002405185440078953 +armv7neon_mmm_f32_8x6_cortexa7 24 128 18 0.00006059904552298362 +armv7neon_mmm_f32_8x4_cortexa9 24 4 8 0.0000036249029546823233 +armv7neon_mmm_f32_8x4_generic 17 4 5 0.000004186211638893127 +armv7neon_mmm_f32_8x4_cortexa7 15 128 5 0.00002190540588603946 +armv7neon_mmm_f32_8x6_cortexa7 9 32 5 0.000004913938240718097 +armv7neon_mmm_f32_8x4_cortexa7 25 128 4 0.000021428570118948462 +armv7neon_mmm_f32_8x6_generic 9 4 17 0.000004759335274456197 +armv7neon_mmm_f32_8x4_cortexa9 23 32 5 0.000008792076478618836 +armv7neon_mmm_f32_8x4_cortexa9 17 128 13 0.00004665611088212803 +generic_f32_4x4 11 128 12 0.00003275274568553372 +armv7neon_mmm_f32_8x6_cortexa7 7 128 12 0.000014620372931092478 +armv7neon_mmm_f32_8x6_generic 24 128 13 0.000046299674326571746 +generic_f32_4x4 12 4 9 0.000005632703760211813 +armv7neon_mmm_f32_8x6_cortexa7 15 32 6 0.000004965054605236343 +armv7neon_mmm_f32_8x6_cortexa9 7 128 17 0.000016538122105202102 +armv7neon_mmm_f32_8x6_cortexa9 17 128 7 0.00003112401925909139 +armv7neon_mmm_f32_8x4_cortexa7 7 32 5 0.0000040832664009078 +armv7neon_mmm_f32_8x4_cortexa9 25 4 3 0.000003352094433744027 +armv7neon_mmm_f32_8x4_cortexa7 17 4 4 0.0000023752236037367475 +armv7neon_mmm_f32_8x4_cortexa9 8 4 4 0.000000986252516977325 +armv7neon_mmm_f32_8x6_cortexa7 9 4 5 0.0000022147026524486433 +armv7neon_mmm_f32_8x6_cortexa9 24 4 13 0.000006569367159583456 +armv7neon_mmm_f32_8x6_cortexa9 25 32 13 0.000020818742897252734 +armv7neon_mmm_f32_8x4_cortexa9 15 32 12 0.000008751598473767715 +armv7neon_mmm_f32_8x4_cortexa7 7 4 4 0.000001249360695162341 +armv7neon_mmm_f32_8x6_generic 25 4 19 0.000011234098334898354 +generic_f32_4x4 7 4 9 0.000004262590318570047 +generic_f32_4x4 12 128 9 0.000032724926249473206 +armv7neon_mmm_f32_8x4_cortexa7 23 4 11 0.0000067800221845095914 +armv7neon_mmm_f32_8x4_cortexa7 23 4 13 0.00000857826635505948 +armv7neon_mmm_f32_8x4_generic 7 32 3 0.0000019119582227554076 +armv7neon_mmm_f32_8x6_cortexa9 25 4 12 0.000005714183543712814 +generic_f32_4x4 7 128 8 0.000014963178444176004 +armv7neon_mmm_f32_8x6_generic 23 4 11 0.000005289174016444985 +armv7neon_mmm_f32_8x4_generic 15 4 7 0.000003212917021733202 +armv7neon_mmm_f32_8x4_cortexa9 17 4 4 0.000002157304549786207 +armv7neon_mmm_f32_8x4_cortexa7 25 4 8 0.000005296217018377082 +armv7neon_mmm_f32_8x4_cortexa7 16 4 8 0.000002789622045679376 +armv7neon_mmm_f32_8x4_cortexa9 9 32 11 0.00000862398264754348 +armv7neon_mmm_f32_8x6_generic 16 4 18 0.000004182651246093671 +armv7neon_mmm_f32_8x4_cortexa9 7 4 7 0.0000019258225946178918 +armv7neon_mmm_f32_8x4_generic 7 32 13 0.0000063109264345399555 +armv7neon_mmm_f32_8x6_generic 15 32 7 0.000007649176449323901 +armv7neon_mmm_f32_8x4_cortexa7 9 4 13 0.00000566559429146675 +generic_f32_4x4 11 4 13 0.00000768804413911549 +armv7neon_mmm_f32_8x6_cortexa7 15 128 19 0.000055484906874277455 +armv7neon_mmm_f32_8x6_generic 8 128 19 0.000020785294814215668 +armv7neon_mmm_f32_8x4_cortexa7 23 32 7 0.000011137926376204002 +armv7neon_mmm_f32_8x4_cortexa9 25 32 4 0.000005634291322373054 +armv7neon_mmm_f32_8x4_cortexa7 16 4 11 0.0000043943657333441155 +generic_f32_4x4 3 128 4 0.000004206283247834624 +armv7neon_mmm_f32_8x6_cortexa9 23 32 17 0.000016635289447796483 +armv7neon_mmm_f32_8x6_cortexa7 23 4 5 0.0000032303127776050868 +generic_f32_4x4 7 128 7 0.000015111291354960824 +armv7neon_mmm_f32_8x6_cortexa7 8 128 19 0.000027387915918929853 +armv7neon_mmm_f32_8x4_cortexa7 24 32 9 0.000015681272955767534 +armv7neon_mmm_f32_8x6_cortexa7 15 32 7 0.000009363971820493224 +armv7neon_mmm_f32_8x6_generic 15 4 17 0.000005464908042174986 +generic_f32_4x4 11 4 4 0.000002266320385947867 +armv7neon_mmm_f32_8x4_cortexa7 7 4 5 0.0000019827556786453945 +armv7neon_mmm_f32_8x4_generic 7 4 5 0.0000018753720795687383 +armv7neon_mmm_f32_8x6_cortexa7 23 32 19 0.000026555363914009545 +generic_f32_4x4 12 32 11 0.000011904804943326893 +generic_f32_4x4 9 32 4 0.000004278605538448279 +armv7neon_mmm_f32_8x4_cortexa9 9 4 3 0.0000018374246302226002 +armv7neon_mmm_f32_8x4_generic 17 128 11 0.00003530676669688071 +armv7neon_mmm_f32_8x4_cortexa7 23 32 5 0.000011014184598781204 +armv7neon_mmm_f32_8x4_cortexa7 8 128 8 0.000010889817517963246 +armv7neon_mmm_f32_8x4_generic 24 128 5 0.000023658482563487358 +armv7neon_mmm_f32_8x4_generic 23 4 4 0.0000022865778486536074 +armv7neon_mmm_f32_8x6_cortexa7 16 32 7 0.000008963466531834949 +armv7neon_mmm_f32_8x4_cortexa7 16 128 11 0.00003220406926334493 +armv7neon_mmm_f32_8x4_cortexa9 7 4 9 0.000002612401333065891 +armv7neon_mmm_f32_8x6_generic 24 128 18 0.00004570910076801179 +armv7neon_mmm_f32_8x6_cortexa7 15 32 19 0.00001825949129411215 +armv7neon_mmm_f32_8x4_cortexa7 7 128 11 0.00001669033881154567 +armv7neon_mmm_f32_8x6_cortexa7 17 128 17 0.00006152323359147728 +armv7neon_mmm_f32_8x6_generic 23 4 12 0.0000049613667404571205 +armv7neon_mmm_f32_8x6_cortexa9 17 4 6 0.0000025304413562385254 +armv7neon_mmm_f32_8x6_generic 25 32 13 0.000020634077057855525 +generic_f32_4x4 3 128 8 0.000007895048382688004 +armv7neon_mmm_f32_8x6_generic 7 128 6 0.000005880420229157795 +armv7neon_mmm_f32_8x6_cortexa7 7 4 6 0.0000015435048498918165 +armv7neon_mmm_f32_8x6_cortexa9 15 4 12 0.0000038035548284711065 +armv7neon_mmm_f32_8x4_generic 16 128 13 0.00003105986968794038 +armv7neon_mmm_f32_8x6_generic 17 128 6 0.00001561934075445538 +armv7neon_mmm_f32_8x6_generic 23 32 5 0.000006038048456367719 +generic_f32_4x4 3 4 8 0.0000018887559388867697 +armv7neon_mmm_f32_8x6_cortexa7 17 128 5 0.000021122166031945488 +armv7neon_mmm_f32_8x4_cortexa7 17 4 12 0.000005984255865199454 +armv7neon_mmm_f32_8x6_cortexa9 8 4 17 0.00000262350755833462 +armv7neon_mmm_f32_8x6_generic 24 32 17 0.000015705211312591065 +armv7neon_mmm_f32_8x6_generic 23 32 7 0.00001103425525544337 +armv7neon_mmm_f32_8x6_generic 23 128 18 0.00004698363257869806 +armv7neon_mmm_f32_8x4_cortexa7 9 128 4 0.000010997242917041898 +generic_f32_4x4 12 32 9 0.000011855594903877275 +armv7neon_mmm_f32_8x4_cortexa7 8 32 4 0.000002107136592547487 +armv7neon_mmm_f32_8x4_cortexa7 9 32 4 0.0000038494164534325605 +generic_f32_4x4 8 32 4 0.0000029596097342538777 +armv7neon_mmm_f32_8x6_cortexa9 7 4 13 0.0000033183395006378194 +armv7neon_mmm_f32_8x4_cortexa9 7 128 5 0.00000837292374457436 +armv7neon_mmm_f32_8x6_cortexa7 9 32 12 0.000008879244649291164 +armv7neon_mmm_f32_8x6_cortexa9 23 32 5 0.00000608726564780247 +generic_f32_4x4 4 4 11 0.000002253269465041155 +armv7neon_mmm_f32_8x6_generic 15 4 12 0.000003737614965115773 +armv7neon_mmm_f32_8x4_cortexa7 8 32 13 0.000007179427212160366 +armv7neon_mmm_f32_8x6_generic 7 4 7 0.00000227244051714339 +armv7neon_mmm_f32_8x6_generic 7 128 13 0.000016430484681121403 +armv7neon_mmm_f32_8x4_cortexa9 25 4 13 0.000009867842979190694 +armv7neon_mmm_f32_8x4_cortexa9 24 32 7 0.000008714828341166468 +armv7neon_mmm_f32_8x6_generic 7 128 12 0.00001125899429533851 +armv7neon_mmm_f32_8x4_cortexa7 24 128 3 0.000016779227724494087 +armv7neon_mmm_f32_8x6_cortexa7 25 32 11 0.000017700250695390733 +armv7neon_mmm_f32_8x6_cortexa9 8 128 17 0.00001578057628845984 +armv7neon_mmm_f32_8x6_cortexa9 15 128 18 0.00003176376886582956 +armv7neon_mmm_f32_8x6_generic 7 32 18 0.000006481310483268657 +armv7neon_mmm_f32_8x6_generic 24 32 19 0.000020260301856480532 +armv7neon_mmm_f32_8x6_cortexa7 25 4 7 0.000006556992855989281 +armv7neon_mmm_f32_8x4_cortexa7 9 4 8 0.0000030004666340701197 +armv7neon_mmm_f32_8x6_generic 8 4 13 0.0000025029052985754286 +armv7neon_mmm_f32_8x4_cortexa7 16 32 13 0.00001386814401860724 +armv7neon_mmm_f32_8x4_generic 16 128 3 0.000008461514063719399 +generic_f32_4x4 8 32 5 0.000005666217550078829 +armv7neon_mmm_f32_8x4_generic 17 32 9 0.000012567178399696468 +armv7neon_mmm_f32_8x4_cortexa7 15 128 9 0.00003252514906200416 +armv7neon_mmm_f32_8x6_generic 25 128 13 0.00006413295578186804 +armv7neon_mmm_f32_8x6_generic 7 4 11 0.0000023405037923286433 +generic_f32_4x4 7 32 12 0.000008239110132919408 +armv7neon_mmm_f32_8x6_cortexa9 23 128 6 0.00001597150399238562 +armv7neon_mmm_f32_8x4_generic 25 4 13 0.000009863674729272788 +armv7neon_mmm_f32_8x4_cortexa7 16 128 5 0.00002172651909764639 +armv7neon_mmm_f32_8x6_cortexa7 15 32 12 0.000009468415752921276 +armv7neon_mmm_f32_8x6_cortexa9 24 4 11 0.000004968600626897013 +armv7neon_mmm_f32_8x4_generic 24 128 13 0.00004635289274725685 +armv7neon_mmm_f32_8x6_generic 23 128 5 0.000016196412852382603 +armv7neon_mmm_f32_8x6_cortexa7 16 32 13 0.000013024830178376121 +armv7neon_mmm_f32_8x4_cortexa9 9 32 3 0.0000033151175658832473 +armv7neon_mmm_f32_8x6_generic 7 4 13 0.000003265379138781528 +armv7neon_mmm_f32_8x4_cortexa9 17 32 13 0.000016459192383496957 +armv7neon_mmm_f32_8x6_cortexa7 24 32 13 0.000019272536127977423 +generic_f32_4x4 4 32 3 0.0000019007760051192194 +generic_f32_4x4 9 128 8 0.000021954328754919918 +armv7neon_mmm_f32_8x4_cortexa7 8 32 12 0.000005371382475600821 +generic_f32_4x4 7 4 3 0.000001903414095718297 +armv7neon_mmm_f32_8x6_generic 24 4 7 0.000004666010098240403 +armv7neon_mmm_f32_8x4_cortexa9 16 32 8 0.000005563656358158919 +generic_f32_4x4 3 32 9 0.000004602770059818119 +armv7neon_mmm_f32_8x4_generic 9 32 13 0.000011194068458304182 +armv7neon_mmm_f32_8x4_cortexa9 15 32 11 0.000008943973907120027 +generic_f32_4x4 11 128 7 0.000022328923764966492 +armv7neon_mmm_f32_8x6_generic 8 128 5 0.000005731544283554465 +armv7neon_mmm_f32_8x6_cortexa9 8 128 11 0.00001076946555010939 +armv7neon_mmm_f32_8x4_cortexa7 7 128 5 0.000011229094508072489 +armv7neon_mmm_f32_8x4_cortexa9 15 128 9 0.000023961524252030835 +generic_f32_4x4 11 128 5 0.000022274598932900346 +generic_f32_4x4 5 128 3 0.000007857636386132928 +generic_f32_4x4 4 128 7 0.00000774651849385228 +armv7neon_mmm_f32_8x4_cortexa9 15 128 5 0.000016175167728379695 +armv7neon_mmm_f32_8x6_cortexa7 17 4 5 0.0000031159889361188713 +generic_f32_4x4 12 4 11 0.000005685376438998431 +armv7neon_mmm_f32_8x4_cortexa7 8 32 5 0.000003934735618425319 +generic_f32_4x4 4 32 4 0.0000017328412461691005 +armv7neon_mmm_f32_8x4_cortexa7 8 128 11 0.000016332637899779713 +armv7neon_mmm_f32_8x6_cortexa9 15 32 17 0.000011527836971780865 +armv7neon_mmm_f32_8x4_generic 15 4 9 0.000004398255246018968 +armv7neon_mmm_f32_8x4_cortexa9 15 32 8 0.000005965121319547414 +armv7neon_mmm_f32_8x6_cortexa7 8 4 11 0.0000020902150325449643 +armv7neon_mmm_f32_8x6_cortexa7 15 4 13 0.000005648201530441972 +armv7neon_mmm_f32_8x6_cortexa9 23 128 18 0.00004690308298866514 +armv7neon_mmm_f32_8x6_cortexa7 17 128 18 0.00006100420785992372 +generic_f32_4x4 3 32 3 0.000001876030465879534 +generic_f32_4x4 4 128 8 0.000007586370052411088 +armv7neon_mmm_f32_8x6_cortexa9 8 128 18 0.000015532022218663232 +armv7neon_mmm_f32_8x4_cortexa7 17 4 11 0.000006454744732596089 +armv7neon_mmm_f32_8x6_cortexa9 15 4 11 0.0000038999665208551116 +armv7neon_mmm_f32_8x6_cortexa9 25 128 17 0.00006218409021295646 +armv7neon_mmm_f32_8x6_cortexa7 9 128 7 0.000027592791728379908 +armv7neon_mmm_f32_8x4_cortexa7 17 128 7 0.0000324174579838398 +armv7neon_mmm_f32_8x6_generic 17 32 13 0.00001566115346007812 +generic_f32_4x4 9 4 4 0.0000022019063671444208 +armv7neon_mmm_f32_8x4_cortexa9 17 128 11 0.0000353036171900936 +armv7neon_mmm_f32_8x4_cortexa7 17 32 12 0.0000154170992202297 +generic_f32_4x4 4 4 12 0.0000020915219079462592 +armv7neon_mmm_f32_8x4_generic 16 4 3 0.000001964189345127758 +generic_f32_4x4 11 4 3 0.0000025875707471480883 +armv7neon_mmm_f32_8x4_cortexa7 24 32 8 0.000010240674992864977 +armv7neon_mmm_f32_8x4_cortexa7 24 4 7 0.00000464383472294094 +armv7neon_mmm_f32_8x6_generic 24 128 17 0.00004653762565754179 +armv7neon_mmm_f32_8x4_cortexa9 9 128 5 0.000015984472800286198 +armv7neon_mmm_f32_8x6_cortexa9 17 32 13 0.000015793322899399953 +armv7neon_mmm_f32_8x4_cortexa7 23 128 9 0.00004832080937818461 +armv7neon_mmm_f32_8x6_generic 17 4 12 0.0000043886752215018795 +armv7neon_mmm_f32_8x6_generic 24 4 17 0.000006718970310010203 +armv7neon_mmm_f32_8x6_cortexa9 23 4 12 0.000005053037256901505 +armv7neon_mmm_f32_8x4_generic 25 128 13 0.00006314133455535656 +generic_f32_4x4 9 128 7 0.000022234980219629062 +armv7neon_mmm_f32_8x6_cortexa7 7 128 7 0.000014376478927807395 +armv7neon_mmm_f32_8x6_cortexa7 16 128 5 0.0000143212403106337 +armv7neon_mmm_f32_8x6_generic 25 32 6 0.000007045905360966285 +armv7neon_mmm_f32_8x6_generic 25 32 17 0.000020892213993308648 +armv7neon_mmm_f32_8x6_cortexa9 24 32 7 0.000010711106258433335 +armv7neon_mmm_f32_8x4_cortexa7 15 32 7 0.000007644594488957809 +armv7neon_mmm_f32_8x4_generic 9 32 7 0.0000059612924557323865 +armv7neon_mmm_f32_8x4_cortexa7 24 32 5 0.000010813567327083286 +generic_f32_4x4 11 32 9 0.000012181640310257489 +armv7neon_mmm_f32_8x4_generic 8 32 11 0.0000044856528446547376 +armv7neon_mmm_f32_8x6_cortexa7 8 128 5 0.00000740698393210008 +armv7neon_mmm_f32_8x6_generic 9 4 13 0.00000467415217244372 +armv7neon_mmm_f32_8x4_cortexa9 8 32 5 0.0000031557846756887957 +armv7neon_mmm_f32_8x6_generic 9 32 6 0.000003833293115622011 +generic_f32_4x4 5 128 11 0.000022197811800866368 +armv7neon_mmm_f32_8x4_cortexa7 8 4 8 0.000001630228451378558 +armv7neon_mmm_f32_8x6_cortexa7 7 128 18 0.00002168993830362326 +armv7neon_mmm_f32_8x4_generic 16 4 7 0.000003012988093329698 +armv7neon_mmm_f32_8x6_generic 9 32 5 0.0000040586320817732015 +armv7neon_mmm_f32_8x4_generic 8 128 11 0.000012020324272970633 +armv7neon_mmm_f32_8x4_cortexa9 9 4 13 0.00000525401614258308 +armv7neon_mmm_f32_8x6_cortexa9 17 32 5 0.000005959198344084862 +armv7neon_mmm_f32_8x6_cortexa9 25 128 7 0.00004136188393626292 +armv7neon_mmm_f32_8x6_cortexa7 8 4 6 0.0000011677036715350285 +armv7neon_mmm_f32_8x6_cortexa7 7 32 11 0.000005167860829086469 +armv7neon_mmm_f32_8x4_generic 24 4 11 0.000005847812768700571 +armv7neon_mmm_f32_8x4_generic 17 128 8 0.0000233987853798507 +armv7neon_mmm_f32_8x4_cortexa9 7 128 8 0.000008435297227106425 +armv7neon_mmm_f32_8x4_generic 8 4 4 0.000000981928574441578 +generic_f32_4x4 13 32 8 0.000010469226231367714 +generic_f32_4x4 12 4 7 0.000004168142600643075 +armv7neon_mmm_f32_8x6_generic 25 128 5 0.000021400005561991285 +armv7neon_mmm_f32_8x6_cortexa9 23 128 19 0.00006309986803011645 +armv7neon_mmm_f32_8x6_cortexa7 17 32 5 0.000007188792393610533 +armv7neon_mmm_f32_8x6_cortexa9 7 32 17 0.00000637568429598606 +armv7neon_mmm_f32_8x6_cortexa9 25 32 19 0.000027309010551769435 +armv7neon_mmm_f32_8x4_cortexa7 7 32 8 0.000004145572586004413 +armv7neon_mmm_f32_8x4_cortexa9 24 32 9 0.000012468417684719209 +armv7neon_mmm_f32_8x6_cortexa7 23 128 7 0.00004157442562492112 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm64.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm64.rs new file mode 100644 index 0000000..07f7899 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm64.rs @@ -0,0 +1,182 @@ +#![allow(clippy::excessive_precision)] +mod arm64simd; +pub mod cortex_a53; +mod cortex_a55; +//mod cortex_a72; +//mod cortex_a73; +pub use arm64simd::*; + +use crate::Ops; + +use crate::frame::element_wise::ElementWiseKer; +use crate::frame::mmm::kernel::MatMatMulKer; + +lazy_static::lazy_static! { + static ref KIND: Kind = Kind::choose(); +} + +// https://en.wikipedia.org/wiki/Comparison_of_ARMv8-A_cores +const PART_A53: &str = "0xd03"; +const PART_A55: &str = "0xd05"; +#[allow(dead_code)] +const PART_A72: &str = "0xd08"; +#[allow(dead_code)] +const PART_A73: &str = "0xd09"; +#[allow(dead_code)] +const PART_A75: &str = "0xd0a"; + +fn max_cpuid() -> std::io::Result { + let cpu_info = std::fs::read_to_string("/proc/cpuinfo")?; + let max = cpu_info + .lines() + .filter(|line| line.starts_with("CPU part")) + .map(|line| line.split_whitespace().last().unwrap_or("")) + .max(); + Ok(max.unwrap_or("").to_string()) +} + +#[inline] +pub fn has_fp16() -> bool { + cfg!(feature_cpu = "fp16") +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +enum Kind { + Generic, + AppleM, + CortexA53, + CortexA55, + CortexA72, + CortexA73, + CortexA75, +} + +impl Kind { + fn choose() -> Kind { + let kind = if let Ok(kind) = std::env::var("TRACT_CPU_AARCH64_KIND") { + log::info!("CPU kind forced with TRACT_CPU_AARCH64_KIND: {}", kind); + let kind = kind.to_lowercase(); + if kind.contains("a53") { + Kind::CortexA53 + } else if kind.contains("a55") { + Kind::CortexA55 + } else if kind.contains("a72") { + Kind::CortexA72 + } else if kind.contains("a73") { + Kind::CortexA73 + } else if kind.contains("a75") { + Kind::CortexA75 + } else if kind.contains("applem") { + Kind::AppleM + } else { + Kind::Generic + } + } else if cfg!(target_os = "macos") { + Kind::AppleM + } else { + let part = if let Ok(part) = std::env::var("TRACT_CPU_AARCH64_OVERRIDE_CPU_PART") { + log::info!( + "CPU part forced with TRACT_CPU_AARCH64_OVERRIDE_CPU_PART: {}", + part + ); + part + } else if cfg!(target_os = "linux") { + let part = max_cpuid().unwrap_or_else(|_| "0x00".to_string()); + log::info!("CPU part auto detected: {}", part); + part + } else { + log::info!("Unknown CPU part"); + "0x00".to_string() + }; + match &*part { + PART_A53 => Kind::CortexA53, + PART_A55 => Kind::CortexA55, + PART_A72 => Kind::CortexA72, + PART_A73 => Kind::CortexA73, + PART_A75 => Kind::CortexA75, + _ => Kind::Generic, + } + }; + log::info!("CPU optimisation: {:?}", kind); + kind + } +} + +pub fn plug(ops: &mut Ops) { + let impls = vec![ + arm64simd_mmm_f32_12x8_gen::mmm(), + arm64simd_mmm_f32_12x8_a53::mmm(), + arm64simd_mmm_f32_12x8_a55::mmm(), + arm64simd_mmm_f32_8x8_gen::mmm(), + arm64simd_mmm_f32_8x8_a53::mmm(), + arm64simd_mmm_f32_8x8_a55::mmm(), + arm64simd_mmm_f32_16x4_gen::mmm(), + arm64simd_mmm_f32_16x4_a53::mmm(), + arm64simd_mmm_f32_16x4_a55::mmm(), + arm64simd_mmm_f32_24x4_gen::mmm(), + arm64simd_mmm_f32_24x4_a53::mmm(), + arm64simd_mmm_f32_24x4_a55::mmm(), + crate::generic::mmm::generic_f32_4x4::mmm(), + ]; + ops.mmm_f32_impls = impls.clone(); + ops.qmmm_i32 = Box::new(|_, _, _| arm64simd_mmm_i32_8x8::mmm()); + ops.qmmv_i32 = Box::new(|_, _| arm64simd_mmm_i32_64x1::mmm()); + ops.mmv_f32 = match *KIND { + Kind::CortexA53 => Box::new(|_, _| arm64simd_mmm_f32_64x1_a53::mmm()), + Kind::CortexA55 => Box::new(|_, _| arm64simd_mmm_f32_64x1_a55::mmm()), + _ => Box::new(|_, _| arm64simd_mmm_f32_64x1_gen::mmm()), + }; + let model = match *KIND { + Kind::CortexA53 => Some(cortex_a53::model()), + Kind::CortexA55 => Some(cortex_a55::model()), + _ => None, + }; + ops.mmm_f32 = if let Some(model) = model { + Box::new(move |m, k, n| model.pick(&impls, m, k, n)) + } else { + Box::new(move |_, _, n| { + if n.unwrap_or(8) < 8 { + arm64simd_mmm_f32_16x4_gen::mmm() + } else { + arm64simd_mmm_f32_8x8_gen::mmm() + } + }) + }; + #[cfg(feature = "no_fp16")] + if has_fp16() { + log::warn!( + "This is a build with fp16 disabled, while your platform CPU seems to support it." + ); + } + #[cfg(not(feature = "no_fp16"))] + if has_fp16() { + if *KIND == Kind::CortexA55 { + ops.mmm_f16 = Box::new(|_, _, n| { + use tract_data::internal::DimLike; + if n.unwrap_or(1024).divceil(4) * 4 < n.unwrap_or(1024).divceil(8) * 8 { + arm64fp16_mmm_f16_32x4_a55::mmm() + } else { + arm64fp16_mmm_f16_16x8_a55::mmm() + } + }); + ops.mmv_f16 = Box::new(|_, _| arm64fp16_mmm_f16_128x1_a55::mmm()); + } else { + ops.mmm_f16 = Box::new(|_, _, n| { + use tract_data::internal::DimLike; + if n.unwrap_or(1024).divceil(4) * 4 < n.unwrap_or(1024).divceil(8) * 8 { + arm64fp16_mmm_f16_32x4_gen::mmm() + } else { + arm64fp16_mmm_f16_16x8_gen::mmm() + } + }); + ops.mmv_f16 = Box::new(|_, _| arm64fp16_mmm_f16_128x1_gen::mmm()); + } + } + ops.sigmoid_f32 = Box::new(|| arm64simd_sigmoid_f32_4n::ew()); + ops.tanh_f32 = Box::new(|| arm64simd_tanh_f32_4n::ew()); + #[cfg(not(feature = "no_fp16"))] + if has_fp16() { + ops.tanh_f16 = Box::new(|| arm64fp16_tanh_f16_8n::ew()); + ops.sigmoid_f16 = Box::new(|| arm64fp16_sigmoid_f16_8n::ew()); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm64/arm64simd.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm64/arm64simd.rs new file mode 100644 index 0000000..81d55e5 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm64/arm64simd.rs @@ -0,0 +1,52 @@ +use crate::frame::element_wise::ElementWiseKer; +use crate::frame::mmm::*; +#[cfg(not(feature = "no_fp16"))] +use tract_data::half::f16; + +MMMKernel!(f32, arm64simd_mmm_f32_8x8_a55; 8, 8; 16, 16; 1, 1; no_prefetch, true); +MMMKernel!(f32, arm64simd_mmm_f32_12x8_a55; 12, 8; 16, 16; 1, 1; no_prefetch, true); +MMMKernel!(f32, arm64simd_mmm_f32_16x4_a55; 16, 4; 16, 16; 1, 1; no_prefetch, true); +MMMKernel!(f32, arm64simd_mmm_f32_24x4_a55; 24, 4; 16, 16; 1, 1; no_prefetch, true); +MMMKernel!(f32, arm64simd_mmm_f32_64x1_a55; 64, 1; 16, 16; 1, 1; no_prefetch, true); + +MMMKernel!(f32, arm64simd_mmm_f32_16x4_a53; 16, 4; 16, 16; 1, 1; no_prefetch, true); +MMMKernel!(f32, arm64simd_mmm_f32_24x4_a53; 24, 4; 16, 16; 1, 1; no_prefetch, true); +MMMKernel!(f32, arm64simd_mmm_f32_8x8_a53; 8, 8; 16, 16; 1, 1; no_prefetch, true); +MMMKernel!(f32, arm64simd_mmm_f32_12x8_a53; 12, 8; 16, 16; 1, 1; no_prefetch, true); +MMMKernel!(f32, arm64simd_mmm_f32_64x1_a53; 64, 1; 16, 16; 1, 1; no_prefetch, true); + +MMMKernel!(f32, arm64simd_mmm_f32_16x4_gen; 16, 4; 16, 16; 1, 1; no_prefetch, true); +MMMKernel!(f32, arm64simd_mmm_f32_24x4_gen; 24, 4; 16, 16; 1, 1; no_prefetch, true); +MMMKernel!(f32, arm64simd_mmm_f32_8x8_gen; 8, 8; 16, 16; 1, 1; no_prefetch, true); +MMMKernel!(f32, arm64simd_mmm_f32_12x8_gen; 12, 8; 16, 16; 1, 1; no_prefetch, true); +MMMKernel!(f32, arm64simd_mmm_f32_64x1_gen; 64, 1; 16, 16; 1, 1; no_prefetch, true); + +MMMKernel!(i32, arm64simd_mmm_i32_8x8; 8, 8; 16, 16; 0,0; no_prefetch, true); +MMMKernel!(i32, arm64simd_mmm_i32_64x1; 64, 1; 16, 1; 0,0; no_prefetch, true); + +#[cfg(not(feature = "no_fp16"))] +MMMKernel!(f16, arm64fp16_mmm_f16_16x8_gen; 16, 8; 16, 16; 1, 1; no_prefetch, crate::arm64::has_fp16()); +#[cfg(not(feature = "no_fp16"))] +MMMKernel!(f16, arm64fp16_mmm_f16_16x8_a55; 16, 8; 16, 16; 1, 1; no_prefetch, crate::arm64::has_fp16()); +#[cfg(not(feature = "no_fp16"))] +MMMKernel!(f16, arm64fp16_mmm_f16_32x4_gen; 32, 4; 16, 16; 1, 1; no_prefetch, crate::arm64::has_fp16()); +#[cfg(not(feature = "no_fp16"))] +MMMKernel!(f16, arm64fp16_mmm_f16_32x4_a55; 32, 4; 16, 16; 1, 1; no_prefetch, crate::arm64::has_fp16()); +#[cfg(not(feature = "no_fp16"))] +MMMKernel!(f16, arm64fp16_mmm_f16_128x1_gen; 128, 1; 16, 16; 1, 1; no_prefetch, crate::arm64::has_fp16()); +#[cfg(not(feature = "no_fp16"))] +MMMKernel!(f16, arm64fp16_mmm_f16_128x1_a55; 128, 1; 16, 16; 1, 1; no_prefetch, crate::arm64::has_fp16()); + +tanh_impl!(f32, arm64simd_tanh_f32_4n, 4, 4, true); +sigmoid_impl!(f32, arm64simd_sigmoid_f32_4n, 4, 4, true); + +#[cfg(not(feature = "no_fp16"))] +tanh_impl!(f16, arm64fp16_tanh_f16_8n, 8, 8, crate::arm64::has_fp16()); +#[cfg(not(feature = "no_fp16"))] +sigmoid_impl!( + f16, + arm64fp16_sigmoid_f16_8n, + 8, + 8, + crate::arm64::has_fp16() +); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm64/cortex_a53.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm64/cortex_a53.rs new file mode 100644 index 0000000..19b72a3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm64/cortex_a53.rs @@ -0,0 +1,1197 @@ +use crate::frame::mmm::CostModel; +pub fn model() -> CostModel<'static> { + CostModel { + big_product_mkn_threshold: 4193280.0, + big_product_kernel_choice: "arm64simd_mmm_f32_12x8_a53", + kernels: &[ + "arm64simd_mmm_f32_12x8_a53", + "arm64simd_mmm_f32_12x8_gen", + "arm64simd_mmm_f32_16x4_a53", + "arm64simd_mmm_f32_16x4_gen", + "arm64simd_mmm_f32_24x4_a53", + "arm64simd_mmm_f32_24x4_gen", + "arm64simd_mmm_f32_8x8_a53", + "arm64simd_mmm_f32_8x8_gen", + "generic_f32_4x4", + ], + mrs: &[4, 8, 12, 16, 24], + nrs: &[4, 8], + feat_norm_mean: &[ + 4.592185479105843, + 4.595318666792368, + 4.579484503710355, + 13.76698864960861, + 1.5094315895372235, + 0.7603118712273642, + 3.47170523138833, + 0.8752515090543259, + 5.487801810865191, + 0.9224094567404426, + 7.414361167002012, + 0.9387575452716298, + 11.415367203219317, + 0.959758551307847, + 1.5074195171026157, + 0.750125754527163, + 3.47170523138833, + 0.875125754527163, + ], + feat_norm_stddev: &[ + 1.2629893666668983, + 1.2446322895476982, + 1.258916587498509, + 1.3105293102858375, + 1.1063478713873012, + 0.4268931127321023, + 2.3025561444671223, + 0.330433510637837, + 3.431728816936762, + 0.2675261685447694, + 4.624258056138275, + 0.23977451171303063, + 6.954988241153163, + 0.19652499713600946, + 1.1207056563030822, + 0.4329400731304941, + 2.292868878895526, + 0.3305762669799629, + ], + w1: &[ + -0.6321063041687012, + 0.24184978008270264, + -0.4356610178947449, + -0.1422707587480545, + 0.10410869866609573, + 0.09415467828512192, + 0.1568029671907425, + -0.25644537806510925, + -0.37143954634666443, + 0.15696385502815247, + 0.050514884293079376, + -0.07972156256437302, + -0.253411203622818, + 0.27587205171585083, + 0.02698700875043869, + -0.07245094329118729, + -0.013899300247430801, + 0.022088056430220604, + 0.2630922496318817, + -0.06870237737894058, + 0.40947580337524414, + 0.22110328078269958, + 0.03808840364217758, + -0.008957616984844208, + -0.11127127707004547, + 0.07818343490362167, + 0.025474127382040024, + -0.09513817727565765, + 0.10613243281841278, + 0.029441041871905327, + 0.0819312185049057, + -0.03519295156002045, + -0.3130439519882202, + 0.4705337882041931, + 0.4476615786552429, + -0.616556704044342, + 0.2223544716835022, + -0.23584842681884766, + -0.3312308192253113, + 0.18874213099479675, + -0.033394988626241684, + 0.09006354957818985, + 0.014722823165357113, + 0.0877116397023201, + 0.07635975629091263, + 0.04284617677330971, + -0.029695890843868256, + -0.05645013228058815, + -0.096514992415905, + 0.16431200504302979, + 0.11922749876976013, + -0.08329842984676361, + -0.15593503415584564, + 0.33497852087020874, + 0.5143201947212219, + -0.4143742322921753, + -0.07121813297271729, + 0.032980211079120636, + -0.014759342186152935, + -0.10575086623430252, + -0.08755142986774445, + 0.053559254854917526, + 0.2959750294685364, + -0.210640087723732, + -0.09462635219097137, + 0.14600691199302673, + 0.22388464212417603, + -0.185477152466774, + -0.100673608481884, + -0.10946766287088394, + 0.03957876190543175, + -0.10485030710697174, + 0.01792730763554573, + 0.15610192716121674, + -0.14726269245147705, + 0.30900657176971436, + 0.21081387996673584, + -0.06592089682817459, + 0.03168980032205582, + 0.20096036791801453, + 0.021350117400288582, + -0.04456694424152374, + 0.35106319189071655, + 0.04561518132686615, + -0.14208926260471344, + 0.06227286159992218, + -0.20092618465423584, + 0.08163813501596451, + 0.23094142973423004, + -0.0332462415099144, + 0.26035502552986145, + 0.4639679193496704, + 0.11891252547502518, + 0.4722647964954376, + -0.025709064677357674, + 0.1651654839515686, + -0.009242026135325432, + 0.02252785675227642, + 0.13325856626033783, + -0.32073062658309937, + -0.05948975682258606, + -0.07114000618457794, + -0.04468341916799545, + -0.002579547930508852, + 0.2056179940700531, + -0.14614446461200714, + -0.11110267788171768, + 0.09043771028518677, + 0.135812908411026, + -0.3300320506095886, + 0.290109783411026, + 0.23399846255779266, + -0.04882314056158066, + -7.729629578534514e-05, + 0.04754950851202011, + 0.003435821272432804, + 0.1115187332034111, + -0.08208155632019043, + 0.018088344484567642, + -0.01600349321961403, + -0.025757616385817528, + 0.060233402997255325, + -0.08445348590612411, + 0.375010222196579, + 0.7828134298324585, + -0.836024820804596, + 0.041282471269369125, + -0.07747451961040497, + 0.31279265880584717, + -0.05552798509597778, + -0.03274049609899521, + -0.1147448793053627, + -0.1660863310098648, + 0.390122652053833, + 0.29283249378204346, + -0.0705522671341896, + -0.2927100956439972, + 0.038575850427150726, + -0.15336857736110687, + -0.028894517570734024, + -0.06372164189815521, + 0.2578844130039215, + 0.060502175241708755, + -0.14235782623291016, + 0.6358739137649536, + -0.2645033001899719, + 0.01847453974187374, + 0.3809853792190552, + 0.0059107388369739056, + -0.07365082949399948, + -0.17490413784980774, + 0.26099810004234314, + 0.38216090202331543, + -0.44192376732826233, + -0.1497800052165985, + 0.11983825266361237, + 0.05704215168952942, + -0.09331715852022171, + -0.027353238314390182, + 0.07132093608379364, + 0.013686291873455048, + -0.14973664283752441, + -0.6386663317680359, + -0.42794787883758545, + 0.43632233142852783, + -0.022474655881524086, + 0.011099671013653278, + 0.08784982562065125, + 0.046248968690633774, + 0.011553826741874218, + 0.0328642763197422, + 0.08678832650184631, + 0.3153251111507416, + -0.15444470942020416, + -0.5339609980583191, + 0.10007581859827042, + -0.02821769379079342, + -0.3091129660606384, + -0.6009559631347656, + -0.555920422077179, + 0.9594710469245911, + -0.5884919166564941, + -0.08316593617200851, + 0.07074970006942749, + 0.026868166401982307, + 0.03690064698457718, + -0.2468167096376419, + 0.20655325055122375, + 0.2654767632484436, + -0.11032287031412125, + 0.09603621065616608, + 0.12746618688106537, + 0.11097392439842224, + -0.046335164457559586, + 0.2753968834877014, + -0.4040895402431488, + -0.20803606510162354, + 0.29299837350845337, + -0.21050886809825897, + -0.02308674342930317, + 0.32019543647766113, + -0.010012545622885227, + -0.07219666987657547, + 0.03816547617316246, + -0.03670865297317505, + -0.023583250120282173, + -0.2030763179063797, + 0.4087490737438202, + 0.19682352244853973, + -0.061049312353134155, + -0.34018784761428833, + 0.4121433198451996, + -0.10742263495922089, + -0.2883375287055969, + 0.15564028918743134, + -0.014489974826574326, + -0.40427249670028687, + 0.04029366746544838, + -0.46333804726600647, + -0.5811125636100769, + 0.1686166524887085, + -0.08247993886470795, + 0.02783152647316456, + -0.07444962859153748, + -0.11033248156309128, + 0.17976728081703186, + -0.05866902321577072, + -0.037863120436668396, + 0.016240332275629044, + 0.08362828195095062, + 0.04285397008061409, + -0.2676204442977905, + -0.18113869428634644, + 0.10164932906627655, + 0.5798585414886475, + -0.2936221659183502, + -0.16815273463726044, + 0.3153108060359955, + 0.1320323497056961, + 0.29474350810050964, + -0.31565147638320923, + 0.032277628779411316, + 0.5137525796890259, + 0.13915763795375824, + -0.08313784748315811, + 0.0871160700917244, + 0.07447603344917297, + -0.4863177537918091, + 0.022499559447169304, + 0.07244526594877243, + -0.1484450399875641, + -0.08256664127111435, + 0.09993510693311691, + 0.33980417251586914, + -0.5465939044952393, + -0.18684262037277222, + 0.050183601677417755, + 0.015223318710923195, + -0.32613685727119446, + 0.2532300353050232, + 0.21044038236141205, + -0.24877160787582397, + 0.17659279704093933, + -0.14793306589126587, + 0.054353710263967514, + -0.07312241941690445, + 0.04128497466444969, + -0.0071349963545799255, + -0.17010675370693207, + 0.3045605719089508, + -0.391606867313385, + 0.19206605851650238, + 0.10403380542993546, + -0.3808597922325134, + -0.016270365566015244, + -0.09313700348138809, + 0.11184006929397583, + 0.01242944784462452, + -0.03349926695227623, + -0.1107369139790535, + 0.2315940409898758, + 0.03170541673898697, + -0.48357459902763367, + 0.21056240797042847, + -0.25072887539863586, + 0.3221265375614166, + 0.5108669400215149, + -0.6159838438034058, + -0.5540208220481873, + 0.38405123353004456, + 0.1323588639497757, + -0.11752784997224808, + 0.07821227610111237, + 0.0494898185133934, + 0.28607267141342163, + -0.45723024010658264, + -0.5914809703826904, + -0.15741930902004242, + -0.09551641345024109, + -0.769051730632782, + -0.2119017094373703, + -0.8505933284759521, + 0.025818098336458206, + 0.11196669936180115, + 0.013385393656790257, + -0.02640729956328869, + -0.061663247644901276, + -0.012524818070232868, + -0.8237857222557068, + -0.40553018450737, + -0.06807617098093033, + -0.07508324831724167, + -0.011943532153964043, + 0.07591933757066727, + 0.18625806272029877, + -0.14417743682861328, + 0.0031204342376440763, + -0.031199704855680466, + -0.037418268620967865, + -0.062444642186164856, + 0.0434197299182415, + -0.12462416291236877, + -0.256317675113678, + -0.0023087849840521812, + 0.20042477548122406, + 0.17625926434993744, + -0.21970611810684204, + 0.1626158505678177, + -0.09550918638706207, + -0.10577445477247238, + -0.17239737510681152, + 0.28190216422080994, + 0.003485368099063635, + -0.24596424400806427, + 0.5330491662025452, + -0.6179713010787964, + -0.19186368584632874, + 0.04049135372042656, + 0.005797799210995436, + 0.10468537360429764, + -0.03522713482379913, + 0.2554764151573181, + -0.6601210832595825, + 0.3554987609386444, + -0.1528356373310089, + -0.2578294575214386, + -0.01912580616772175, + 0.14837700128555298, + 0.28032413125038147, + 0.6525465250015259, + -0.16390740871429443, + -0.12456659972667694, + -0.04434182122349739, + 0.44120529294013977, + -0.06832294911146164, + 0.4077378511428833, + -0.07938709110021591, + 0.23457404971122742, + -0.05966708064079285, + 0.09640492498874664, + 0.7555295825004578, + -0.3110663592815399, + 0.035311225801706314, + 0.25391876697540283, + 0.09088675677776337, + 0.03320888802409172, + -0.1745719611644745, + 0.2270633578300476, + 0.2851920425891876, + -0.07204318791627884, + -0.05483328923583031, + 0.189837247133255, + -0.15304607152938843, + -0.08311894536018372, + -0.06649994850158691, + -0.0776129737496376, + 0.11864881962537766, + -0.06670717149972916, + -0.00406235596165061, + -0.6984686255455017, + 0.28291743993759155, + -0.04160117730498314, + -0.09169034659862518, + 0.14924104511737823, + 0.46138641238212585, + -0.29699283838272095, + -0.6411864757537842, + 0.26037612557411194, + 0.21487018465995789, + -0.20806393027305603, + -0.4174681007862091, + 0.1901395320892334, + 0.049021925777196884, + 0.2822348475456238, + -0.03862098604440689, + 0.029824024066329002, + 0.2657202184200287, + -0.43108099699020386, + 0.37041717767715454, + -0.025845345109701157, + -0.09200481325387955, + -0.017871620133519173, + 0.281535267829895, + -0.20838744938373566, + -0.400356650352478, + 0.4133286476135254, + -0.08745774626731873, + 0.02171195112168789, + 0.4766440987586975, + -0.24629971385002136, + 0.2504408657550812, + -0.5850875973701477, + -0.49699774384498596, + 0.7086884379386902, + -0.479250967502594, + 0.6140879392623901, + 0.0023341099731624126, + -0.06628652662038803, + -0.0873338133096695, + -0.2862805724143982, + 0.28077220916748047, + 0.030578527599573135, + -0.281633198261261, + -0.7042887806892395, + -0.03409203886985779, + 0.3272986114025116, + 0.3397904634475708, + -0.7069221138954163, + 0.09408266842365265, + -0.05243761092424393, + -0.20503726601600647, + 0.15679042041301727, + 0.4723545014858246, + -0.39158886671066284, + 0.17581138014793396, + 0.10779093205928802, + -0.013951681554317474, + 0.052481986582279205, + -0.36543500423431396, + 0.29497984051704407, + 0.4044850766658783, + -0.3766767382621765, + -0.07298431545495987, + 0.9660398364067078, + 0.27753373980522156, + -0.11616200953722, + 0.05277060344815254, + -0.05379771068692207, + 0.026094499975442886, + -0.011136082001030445, + -0.13593854010105133, + 0.033518679440021515, + 0.6947338581085205, + 0.6335914134979248, + -0.06526267528533936, + 0.019844267517328262, + 0.10042254626750946, + -0.16847042739391327, + -0.15717101097106934, + -0.7462965250015259, + -0.0653005987405777, + 0.057602036744356155, + 0.010834889486432076, + -0.46870648860931396, + -0.1872870922088623, + 0.3152116537094116, + 0.0731910765171051, + -0.13902369141578674, + 0.10666802525520325, + 0.3094567656517029, + -0.926356315612793, + -0.38388797640800476, + -0.02191060781478882, + -0.005548040382564068, + -0.20935170352458954, + 0.24779647588729858, + 0.12304577976465225, + -0.2883053123950958, + 0.019766222685575485, + -0.029659172520041466, + 0.06051887571811676, + -0.01741836965084076, + 0.04409812018275261, + 0.011840295046567917, + -0.14320705831050873, + 0.31673386693000793, + -0.069312185049057, + -0.00935965683311224, + 0.019028477370738983, + -0.1078404039144516, + -0.12472966313362122, + 0.10027194768190384, + 0.31244829297065735, + -0.10855710506439209, + -0.3165830969810486, + 0.4076120853424072, + 0.05742274224758148, + 0.17263729870319366, + 0.3141464293003082, + -0.13655878603458405, + 0.07613589614629745, + -0.10808823257684708, + -0.19837258756160736, + 0.16735948622226715, + 0.055960867553949356, + 0.005388774909079075, + -0.30227115750312805, + -0.009724846109747887, + -0.11610261350870132, + 0.05133519321680069, + -0.029441826045513153, + 0.06810834258794785, + -0.13311177492141724, + 0.2196519374847412, + 0.19138571619987488, + -0.2621391713619232, + 0.11996466666460037, + -0.05961257219314575, + 0.1763487011194229, + -0.10918399691581726, + -0.14629563689231873, + 0.5217060446739197, + -0.0012722538085654378, + 0.08564157783985138, + -0.6640400290489197, + -0.41702714562416077, + 0.045037489384412766, + -0.059789709746837616, + -0.05092751979827881, + 0.10446680337190628, + -0.05335049331188202, + 0.0846114456653595, + 0.04981796815991402, + -0.14310699701309204, + 0.01863306201994419, + -0.0474325567483902, + 0.23124581575393677, + -0.6166588068008423, + -0.7533295154571533, + -1.1133880615234375, + -0.1241607666015625, + -0.5540894865989685, + 0.2806711494922638, + -0.4259497821331024, + -0.07380827516317368, + 0.009988346137106419, + 0.3110937178134918, + 0.0072226757183671, + 0.2422133982181549, + -0.351376473903656, + -0.5103139877319336, + 0.5470908284187317, + -0.14952707290649414, + -0.005531645845621824, + -0.24725599586963654, + 0.1639375537633896, + 0.07172811776399612, + -0.1566568911075592, + 0.32833099365234375, + 0.06875353306531906, + -0.17773276567459106, + -0.09706790000200272, + -0.019849322736263275, + 0.1257631778717041, + 0.02103520557284355, + 0.12721672654151917, + 0.012451020069420338, + 0.039879027754068375, + 0.17779605090618134, + -0.09887054562568665, + -0.08146625012159348, + 0.05893132835626602, + 0.18479469418525696, + -0.2479601502418518, + -0.26928654313087463, + 0.3720027506351471, + -0.45930227637290955, + 0.3673400282859802, + 0.016545426100492477, + 0.13507097959518433, + -0.006458526011556387, + 0.036685895174741745, + 0.309455007314682, + -0.23917894065380096, + -0.11758854985237122, + 0.2146540731191635, + -0.11578961461782455, + 0.006646907888352871, + -0.04229713976383209, + 0.09812270104885101, + 0.06730903685092926, + 0.28935620188713074, + -0.02212020941078663, + 0.007341589778661728, + -0.1257125288248062, + -0.4639318287372589, + 0.41743314266204834, + 0.40524497628211975, + -0.20389464497566223, + 0.1286880075931549, + 0.05365758389234543, + -0.14487741887569427, + 0.1511518359184265, + 0.11219878494739532, + 0.13080842792987823, + -0.175934836268425, + -0.08939457684755325, + 0.16476190090179443, + -0.061722587794065475, + 0.15382836759090424, + 0.15293729305267334, + -0.23814627528190613, + -0.778872013092041, + 0.2813372313976288, + 0.20388194918632507, + -0.34535032510757446, + -0.014981378801167011, + 0.1560390293598175, + 0.534339189529419, + 0.7075706124305725, + -0.20866382122039795, + 0.050050001591444016, + -0.030285198241472244, + 0.430580735206604, + 0.06858251988887787, + 0.32321590185165405, + 0.006104054860770702, + 0.11919829249382019, + -0.09377042204141617, + -0.028785547241568565, + 0.489607572555542, + -0.321664422750473, + 0.020770607516169548, + 0.5259214639663696, + -0.0682888925075531, + 0.10569659620523453, + -0.18257132172584534, + 0.2565872073173523, + 0.2177353799343109, + 0.029641704633831978, + 0.0678875744342804, + 0.1679811030626297, + -0.04851052165031433, + -0.1633165180683136, + -0.007416700944304466, + -0.06638842821121216, + 0.06177712231874466, + -0.0709109827876091, + -0.11213518679141998, + -0.20582593977451324, + 0.7092531323432922, + 0.43438467383384705, + -0.0060964771546423435, + -0.12442151457071304, + -0.008676152676343918, + 0.21390584111213684, + -0.014475004747509956, + -0.7601429224014282, + 0.15622451901435852, + -0.3261253833770752, + 0.005610095337033272, + -0.5111817121505737, + -0.003055301494896412, + 0.32741662859916687, + -0.022710084915161133, + -0.24255472421646118, + -0.6487520933151245, + 0.08797790110111237, + 0.2754897177219391, + -0.2213398665189743, + -0.17206217348575592, + 0.1177680641412735, + 0.16599608957767487, + -0.19922694563865662, + -0.07098120450973511, + -0.1628963202238083, + 0.03356413170695305, + -0.24303652346134186, + -0.2067747414112091, + 0.1192406490445137, + -0.020932691171765327, + 0.07735628634691238, + 0.24762177467346191, + -0.3007707893848419, + -0.43011191487312317, + -0.07597793638706207, + 0.2528873085975647, + -0.3795652985572815, + 0.14651291072368622, + 0.07552091032266617, + 0.026706784963607788, + -0.11118876934051514, + 0.0460294634103775, + 0.4268769323825836, + 0.32645294070243835, + -0.09493713080883026, + 0.18892213702201843, + 0.17980137467384338, + 0.06521839648485184, + 0.03702569752931595, + 0.05443478748202324, + -0.030978504568338394, + -0.11806164681911469, + -0.20229215919971466, + 0.6260767579078674, + 0.6068219542503357, + -0.060956377536058426, + 0.05200914293527603, + 0.04499080404639244, + -0.09300816804170609, + 0.0501115508377552, + 0.9676806926727295, + -0.12394528090953827, + 0.17313909530639648, + -0.0274575874209404, + 1.0245190858840942, + -0.24425312876701355, + 0.3827340602874756, + 0.270155131816864, + -0.7169324159622192, + ], + b1: &[ + -0.518636167049408, + 0.7074531316757202, + -0.4965735971927643, + 0.6063699126243591, + -0.3258720934391022, + 0.4608336389064789, + 0.8324258327484131, + -0.6118353605270386, + 0.8226121664047241, + 0.3534131944179535, + -0.43312883377075195, + -0.05448569357395172, + -0.5826212167739868, + 0.8478071689605713, + 0.23062080144882202, + -0.30911386013031006, + -0.5776869058609009, + 0.5107449293136597, + 0.18762148916721344, + 0.2889731228351593, + -0.5579098463058472, + 0.7818499207496643, + 0.7910265922546387, + -0.4228874444961548, + 0.6197248697280884, + -0.4563252627849579, + 0.27223169803619385, + -0.2859383523464203, + -0.4862801730632782, + -0.7853735089302063, + -0.1534343808889389, + -0.5592636466026306, + -0.6364999413490295, + -0.5210756063461304, + 0.3506944477558136, + -0.5348182916641235, + -0.5098673105239868, + 0.45690369606018066, + -0.3907462954521179, + 0.8493368029594421, + ], + w2: &[ + -0.525189995765686, + 0.44041961431503296, + -0.4107511341571808, + 0.3741440176963806, + -0.02630656771361828, + 0.27733951807022095, + 0.3907228410243988, + -0.05409616604447365, + 0.3991526663303375, + 0.24264170229434967, + -0.657869279384613, + -0.3758363425731659, + -0.5133534669876099, + 0.3480457663536072, + 0.5088834166526794, + 0.0942729115486145, + -0.4167974889278412, + 0.4895906448364258, + 0.17553496360778809, + 0.3702719211578369, + -0.5372111201286316, + -0.1560969352722168, + -0.30670106410980225, + -0.48799967765808105, + 0.4005548357963562, + -0.3075137138366699, + 0.656658947467804, + -0.4914362132549286, + -0.36532747745513916, + -0.5505443811416626, + 0.1328023225069046, + -0.3564044237136841, + -0.467242956161499, + -0.3465808629989624, + 0.4501214027404785, + -0.4742763936519623, + -0.35285890102386475, + 0.46182748675346375, + -0.28942185640335083, + 0.2825036346912384, + -0.1725425124168396, + -0.17012473940849304, + 0.5306965708732605, + -0.34125325083732605, + 0.21301832795143127, + -0.49370092153549194, + -0.06135714799165726, + 0.5665233135223389, + -0.01510544028133154, + -0.0015591675182804465, + 0.4308379292488098, + 0.09525317698717117, + 0.06129995733499527, + -0.06124228611588478, + -0.28377535939216614, + -0.038286369293928146, + 0.19221894443035126, + -0.45041826367378235, + -0.4307488799095154, + -0.30516454577445984, + 0.3670405447483063, + -0.1779327690601349, + -0.36808863282203674, + 0.344722718000412, + -0.2691067159175873, + 0.5803861021995544, + -0.42112261056900024, + 0.1169033870100975, + 0.35742461681365967, + 0.16161565482616425, + 0.44920068979263306, + 0.2572435438632965, + 0.263318806886673, + 0.7236857414245605, + -0.2759736180305481, + 0.37376394867897034, + 0.37350600957870483, + -0.4067005515098572, + 0.18588955700397491, + -0.4281120300292969, + 0.4204690456390381, + -0.448592871427536, + 0.11808016151189804, + -0.4660882353782654, + 0.33337321877479553, + -0.11569353938102722, + -0.589764416217804, + -0.17854063212871552, + -0.44001755118370056, + 0.7101057767868042, + 0.057653751224279404, + 0.3937684893608093, + 0.257487416267395, + -0.38924211263656616, + 0.08511713892221451, + 0.10950952023267746, + 0.0917661041021347, + -0.25429144501686096, + 0.6342174410820007, + -0.15891794860363007, + -0.021509289741516113, + 0.535305380821228, + 0.28721731901168823, + -0.32432296872138977, + -0.26846611499786377, + 0.07051636278629303, + -0.12710770964622498, + 0.14568471908569336, + 0.6293584704399109, + 0.4198862612247467, + -0.8883509039878845, + 0.5271400809288025, + 0.17345309257507324, + 0.1771862506866455, + -0.214192733168602, + 0.17817191779613495, + 0.44757506251335144, + 0.04112042486667633, + 0.6819244027137756, + -0.7277362942695618, + 0.19224950671195984, + -0.2905896008014679, + 0.5791959762573242, + -0.4898945093154907, + 0.47323065996170044, + -0.40173205733299255, + -0.36294564604759216, + 0.6861273050308228, + -0.2955973744392395, + -0.19740070402622223, + 0.4044080674648285, + -0.11244003474712372, + 0.58234703540802, + -0.31175708770751953, + -0.3454722762107849, + 0.12274620682001114, + 0.29693669080734253, + -0.41234102845191956, + -0.1583351045846939, + -0.2763107419013977, + 0.34174609184265137, + -0.7301539182662964, + -0.4137580394744873, + 0.5135444402694702, + -0.19664454460144043, + 0.3913029730319977, + -0.47720086574554443, + 0.2519521415233612, + 0.3860025703907013, + 0.4073657691478729, + 0.06604084372520447, + 0.32879960536956787, + 0.4341438114643097, + 0.4072171449661255, + -0.3755425810813904, + 0.29250237345695496, + 0.4723772704601288, + -0.39177075028419495, + 0.3535446524620056, + -0.5977760553359985, + -0.11535356938838959, + -0.8606860637664795, + 0.3202466070652008, + 0.534551203250885, + -0.10786011070013046, + 0.5766461491584778, + -1.0034655332565308, + -0.08353354036808014, + 0.20165663957595825, + -0.8530645370483398, + 0.2801732122898102, + -0.2713226079940796, + 0.460101842880249, + 0.5550602078437805, + 0.11862986534833908, + -0.8431587219238281, + -0.41269758343696594, + -0.36862486600875854, + 0.08385410159826279, + 0.1634000688791275, + -0.22930988669395447, + -0.39085301756858826, + 0.8845512270927429, + 0.2522968053817749, + 0.3779301643371582, + 0.3454946279525757, + -0.14984408020973206, + 0.2937467098236084, + 0.3651972711086273, + 1.1317671537399292, + -0.4535387456417084, + 0.07272656261920929, + -0.29987066984176636, + -0.03405649587512016, + 0.1012202724814415, + -0.12492970377206802, + -0.048626113682985306, + -0.3150321841239929, + -0.4124220013618469, + -0.7775830030441284, + 0.25562793016433716, + -0.4026365876197815, + 0.27681317925453186, + -0.3169574439525604, + 0.414761483669281, + -0.37095436453819275, + -0.2815983295440674, + 0.6821384429931641, + -0.23631460964679718, + -0.391885370016098, + 0.32081300020217896, + 0.029309673234820366, + 0.3151959478855133, + -0.23872429132461548, + -0.2680605947971344, + 0.2245175689458847, + 0.28024742007255554, + -0.5187304615974426, + -0.17155316472053528, + -0.18662460148334503, + 0.44196388125419617, + -0.7731465697288513, + -0.39956656098365784, + 0.4926709830760956, + -0.2705640196800232, + 0.5851831436157227, + -0.28655296564102173, + 0.21914565563201904, + 0.42291808128356934, + 0.3754308521747589, + 0.12476411461830139, + 0.4564429223537445, + 0.41455739736557007, + 0.24721866846084595, + -0.39062193036079407, + 0.47335484623908997, + 0.4390261769294739, + -0.2776612639427185, + 0.36352279782295227, + -0.4658246338367462, + 0.5458199977874756, + 0.2368425875902176, + -0.28375834226608276, + -0.21349868178367615, + -0.12575705349445343, + -0.314109742641449, + 0.2133757472038269, + -0.4604170322418213, + -0.5457999110221863, + 0.347943514585495, + 0.3864844739437103, + 0.2128392457962036, + 0.06274894624948502, + -0.5941122174263, + -0.4954967200756073, + 0.3897503614425659, + 0.6681548953056335, + 0.011607992462813854, + -0.5754616260528564, + -0.4551040530204773, + 0.14332124590873718, + 0.5475043058395386, + 0.35485684871673584, + 0.516143798828125, + -0.43508225679397583, + -0.2927212119102478, + -0.38220953941345215, + 0.22585861384868622, + -0.49666696786880493, + -0.47814127802848816, + 0.6455125212669373, + -0.4184291362762451, + 0.5714888572692871, + -0.06349734216928482, + -0.337534636259079, + 0.08359762281179428, + -0.6663680672645569, + -0.05490731820464134, + 0.27789443731307983, + 0.44944822788238525, + -0.12919825315475464, + -0.24064187705516815, + 0.3863179683685303, + -0.21315856277942657, + -0.010893935337662697, + -0.49465489387512207, + -0.1953386515378952, + 0.4405977129936218, + -0.362499862909317, + -0.15224213898181915, + 0.503758430480957, + 0.13674911856651306, + 0.24574719369411469, + -0.2888658046722412, + -0.5966756939888, + 0.24279867112636566, + 0.43060633540153503, + -0.2950061857700348, + -0.3071616590023041, + -0.31878525018692017, + 0.5719135999679565, + -0.46542906761169434, + -0.33102989196777344, + 0.2584391236305237, + -0.3341030776500702, + 0.35185420513153076, + -0.5347702503204346, + 0.2021929919719696, + 0.3747906982898712, + 0.3017856478691101, + 0.4192887842655182, + 0.2290816456079483, + 0.26369208097457886, + 0.30613088607788086, + -0.2766033113002777, + 0.48649486899375916, + 0.28767234086990356, + -0.31826111674308777, + 0.47518086433410645, + -0.2643313407897949, + 0.38674306869506836, + -0.20252466201782227, + 0.2426745593547821, + -0.2963939607143402, + 0.35027387738227844, + -0.40756842494010925, + -0.17158618569374084, + 0.6504075527191162, + -0.23639068007469177, + -0.5520732998847961, + 0.34597641229629517, + 0.12782879173755646, + 0.46479496359825134, + -0.4128115773200989, + -0.4125882685184479, + 0.20131008327007294, + 0.4997844099998474, + -0.21766024827957153, + -0.2570849657058716, + -0.1471637338399887, + 0.5070111155509949, + -0.6722937226295471, + -0.5443961024284363, + 0.5341878533363342, + -0.29976886510849, + 0.6135430932044983, + -0.3595261573791504, + 0.49033448100090027, + 0.3653552234172821, + 0.2656362056732178, + 0.10900922119617462, + 0.4813465476036072, + 0.41922783851623535, + 0.2692069411277771, + -0.4056242108345032, + 0.33006641268730164, + 0.27100467681884766, + -0.5306692123413086, + 0.2701503336429596, + -0.6044796705245972, + ], + b2: &[ + 0.044342152774333954, + -0.28361865878105164, + -0.0350283607840538, + -0.129508376121521, + -0.006770995445549488, + -0.24053514003753662, + 0.3617520332336426, + -0.3381704092025757, + -0.24953331053256989, + ], + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm64/cortex_a55.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm64/cortex_a55.rs new file mode 100644 index 0000000..b87ac1a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm64/cortex_a55.rs @@ -0,0 +1,1365 @@ +use crate::frame::mmm::CostModel; +pub fn model() -> CostModel<'static> { + CostModel { + big_product_mkn_threshold: 263214080.0, + big_product_kernel_choice: "arm64simd_mmm_f32_12x8_a55", + kernels: &[ + "arm64simd_mmm_f32_12x8_a53", + "arm64simd_mmm_f32_12x8_a55", + "arm64simd_mmm_f32_12x8_gen", + "arm64simd_mmm_f32_16x4_a53", + "arm64simd_mmm_f32_16x4_a55", + "arm64simd_mmm_f32_16x4_gen", + "arm64simd_mmm_f32_24x4_a53", + "arm64simd_mmm_f32_24x4_a55", + "arm64simd_mmm_f32_24x4_gen", + "arm64simd_mmm_f32_8x8_a53", + "arm64simd_mmm_f32_8x8_a55", + "arm64simd_mmm_f32_8x8_gen", + "generic_f32_4x4", + ], + mrs: &[4, 8, 12, 16, 24], + nrs: &[4, 8], + feat_norm_mean: &[ + 5.27886946965165, + 6.250454700699139, + 5.241114620514529, + 16.770438790865423, + 1.540625, + 0.770625, + 3.518125, + 0.8775, + 5.560625, + 0.923125, + 7.453125, + 0.943125, + 11.613125, + 0.9575, + 1.509375, + 0.771875, + 3.581875, + 0.898125, + ], + feat_norm_stddev: &[ + 0.9509890252368828, + 0.6930410342704738, + 1.0261938261805659, + 1.600617293156687, + 1.0981118382819681, + 0.42043086158725473, + 2.338198341538852, + 0.3278623949159141, + 3.494112850120205, + 0.26639300736881577, + 4.56457037785321, + 0.2316036147710134, + 7.043415558830455, + 0.20172691937369452, + 1.0925484471523377, + 0.4196236222795381, + 2.273113830052299, + 0.30248385804039707, + ], + w1: &[ + -0.13682155311107635, + -0.1783919334411621, + 0.26539096236228943, + 0.19552235305309296, + -0.10618806630373001, + 0.13501706719398499, + 0.21776071190834045, + 0.08390733599662781, + -0.2215081751346588, + 0.18829140067100525, + -0.20535176992416382, + -0.0463368222117424, + 0.05815611779689789, + -0.13855215907096863, + -0.024539709091186523, + -0.4855460524559021, + -0.414151668548584, + -0.7574286460876465, + 0.8987273573875427, + 0.5316352844238281, + 0.8244147896766663, + 0.8388808369636536, + -0.02545193023979664, + 0.04357631504535675, + -0.007071307860314846, + 0.18223997950553894, + -0.04292978346347809, + 0.004330582916736603, + -0.013073648326098919, + -0.04028080403804779, + -0.09901119023561478, + 0.062175191938877106, + -0.006247916258871555, + 0.009531030431389809, + 0.09731218218803406, + 0.004297865089029074, + 0.6260067224502563, + -0.10042139887809753, + 0.807989239692688, + 0.6866835951805115, + -0.018399836495518684, + -0.07194910198450089, + -0.18889868259429932, + -0.07729395478963852, + -0.03907148540019989, + 0.017019111663103104, + 0.06159460172057152, + -0.02395886555314064, + 0.23730705678462982, + -0.15546496212482452, + -0.04492897167801857, + -0.003982013091444969, + -0.09160511195659637, + 0.03185845538973808, + -0.27577653527259827, + 0.5699247121810913, + -0.6027079820632935, + -0.4136800467967987, + -0.04364140331745148, + -0.11226192861795425, + 0.16899903118610382, + -0.11524038016796112, + 0.12308179587125778, + 0.027925828471779823, + -0.06269104778766632, + 0.11644940823316574, + -0.369202196598053, + 0.3338239789009094, + -0.06509242206811905, + 0.2303273230791092, + 0.018171854317188263, + -0.08709719777107239, + 0.18228614330291748, + -0.071574367582798, + -0.012407014146447182, + -0.284942090511322, + -0.1326635330915451, + -0.08634418249130249, + 0.11018547415733337, + -0.09423547983169556, + 0.13697068393230438, + -0.03515861555933952, + 0.014629656448960304, + -0.21159854531288147, + 0.15693463385105133, + -0.021487032994627953, + 0.032396819442510605, + 0.028369005769491196, + 0.08724819868803024, + -0.13204769790172577, + 0.4691336452960968, + 0.1237262561917305, + -0.06020978465676308, + 0.24037614464759827, + 0.05237792432308197, + -0.10641840100288391, + 0.1820996105670929, + 0.6079273819923401, + -0.4903985857963562, + 0.40744978189468384, + 0.43370547890663147, + 0.5092437863349915, + 0.0810965895652771, + 0.4670366048812866, + -0.11692337691783905, + -0.013550599105656147, + -0.364605575799942, + 0.34470170736312866, + -0.01755279302597046, + 0.30621764063835144, + 0.35784396529197693, + 0.42736300826072693, + 0.022546129301190376, + 0.08497388660907745, + 0.07601173967123032, + 0.0696730837225914, + -0.21918217837810516, + 0.6236687898635864, + -0.0793512761592865, + -0.0668395534157753, + 0.010559543035924435, + 0.46621084213256836, + -0.2632196843624115, + -0.03991322219371796, + 0.1392994076013565, + 0.003188274335116148, + -0.2655166983604431, + -0.22143644094467163, + -0.19157607853412628, + -0.39395904541015625, + -0.021266555413603783, + 0.08848410844802856, + 0.08152330666780472, + 0.013220606371760368, + -0.18424198031425476, + 0.05234640836715698, + 0.05919161066412926, + -0.16255362331867218, + -0.04549096152186394, + 0.044437166303396225, + 0.21704396605491638, + -0.5149197578430176, + -0.6047705411911011, + -0.8048356175422668, + -0.36901935935020447, + -0.19035962224006653, + 1.3252514600753784, + 0.19824109971523285, + 0.07630860805511475, + -0.011165079660713673, + 0.011559495702385902, + 0.10554458945989609, + -0.12820255756378174, + 0.29352235794067383, + -0.06662449240684509, + -0.15792854130268097, + 0.0345480814576149, + -0.04881494492292404, + -0.06912268698215485, + -0.00013739474525209516, + -0.1597173660993576, + 0.34323570132255554, + 0.34446775913238525, + 0.3795395493507385, + 0.017453864216804504, + 0.1102253794670105, + 0.04026523232460022, + 0.1107630804181099, + 0.10295291990041733, + 0.5326219201087952, + 0.1749747395515442, + -0.2661803066730499, + 0.11752097308635712, + 0.08010037988424301, + -0.5501991510391235, + -0.059987347573041916, + -0.04125819355249405, + 0.16356444358825684, + 0.020170046016573906, + -0.08306766301393509, + 0.17777052521705627, + 0.4687126874923706, + 0.7723219394683838, + 0.7309747934341431, + -0.019829215481877327, + 0.10945341736078262, + 0.06796073168516159, + -0.12042505294084549, + -0.26762208342552185, + 0.10846878588199615, + 0.013867417350411415, + 0.01077105849981308, + -0.10193657130002975, + -0.1757654845714569, + -0.245382159948349, + 0.20442898571491241, + 0.10115985572338104, + 0.2514199912548065, + -0.3793720304965973, + -0.6926521062850952, + -0.6686761975288391, + -0.607191264629364, + 0.16187654435634613, + -0.0073340232484042645, + -0.09948350489139557, + -0.21431320905685425, + -0.12334707379341125, + -0.15290899574756622, + -0.026063116267323494, + 0.26553207635879517, + 0.18921764194965363, + -0.1665697544813156, + -0.00264778733253479, + 0.20274107158184052, + 0.3660823404788971, + -0.35731416940689087, + 0.50246661901474, + 0.2781502604484558, + 0.1629776805639267, + -0.03493829071521759, + -0.16012291610240936, + -0.08139592409133911, + -0.1440155804157257, + 0.32721832394599915, + 0.1312151998281479, + 0.17874418199062347, + 0.06143738701939583, + -0.05158458650112152, + 0.4802771806716919, + -0.6857288479804993, + 0.08245638012886047, + -0.09577414393424988, + -0.12872998416423798, + 0.16155612468719482, + 0.24089869856834412, + 0.44030725955963135, + -0.30994167923927307, + 0.12139064073562622, + 0.029418930411338806, + -0.051672156900167465, + -0.10080718994140625, + -0.007311842869967222, + -0.15189751982688904, + -0.1559375822544098, + 0.2731820344924927, + -0.03627878054976463, + 0.10538394004106522, + 0.15048423409461975, + 0.12981411814689636, + 0.0002639668236952275, + 0.05666665732860565, + 0.08173252642154694, + -0.16131722927093506, + -0.043261025100946426, + -0.14845971763134003, + -0.29335740208625793, + 0.039398159831762314, + -0.02791670151054859, + 0.22897064685821533, + -0.12178067117929459, + -0.4062419831752777, + 0.3934949040412903, + -0.05093907564878464, + -0.06126153841614723, + -0.07318481802940369, + -0.08793392032384872, + -0.01818496361374855, + -0.24753189086914062, + -0.30580347776412964, + 0.44876909255981445, + 0.5379880666732788, + 0.11587893962860107, + 0.2174995243549347, + -0.035063862800598145, + -0.0010147193679586053, + -0.12281838059425354, + -0.21301835775375366, + 0.3645245432853699, + 0.39920729398727417, + -0.45564430952072144, + 0.03503882512450218, + 0.6949061155319214, + -0.5742982625961304, + 0.38680514693260193, + -0.018345845863223076, + 0.04529440030455589, + -0.04468340799212456, + -0.020917288959026337, + 0.2523670792579651, + -0.4574699103832245, + 0.17178472876548767, + -0.12147565186023712, + 0.043810319155454636, + -0.17998050153255463, + -0.09663069248199463, + -0.03498067706823349, + 0.06111514940857887, + -0.11410824209451675, + 0.18208050727844238, + -0.09109053015708923, + 0.08489643037319183, + 0.15014725923538208, + 0.18506401777267456, + -0.060843177139759064, + -0.11932594329118729, + 0.11290943622589111, + -0.23226700723171234, + -0.2114422470331192, + -0.36001038551330566, + -0.29864072799682617, + -0.05599717050790787, + -0.21294310688972473, + -0.1301364004611969, + -0.4993196725845337, + 0.097460076212883, + 0.030209479853510857, + 0.35134217143058777, + -0.9156147837638855, + 0.0173207875341177, + -0.9142565131187439, + 0.13512593507766724, + -0.1926516443490982, + -0.2812888026237488, + 0.04805266484618187, + 0.5790673494338989, + -0.28300249576568604, + -0.10372477024793625, + 0.2964925169944763, + 0.16425621509552002, + -0.25588271021842957, + 0.37744808197021484, + -0.07827199995517731, + -0.7785226702690125, + -0.4873232841491699, + -0.0240982286632061, + -0.31732890009880066, + -0.7271391749382019, + -0.40648236870765686, + -0.08706668019294739, + -0.0876365602016449, + -0.08107846975326538, + 0.049622420221567154, + 0.5049374103546143, + -0.09109669923782349, + -0.2958216369152069, + 0.23400314152240753, + 0.0727144181728363, + -0.06163109838962555, + -0.3235352635383606, + -0.08323507010936737, + 0.06926267594099045, + 0.12505480647087097, + 0.06806384027004242, + -0.1783592253923416, + -0.09036792814731598, + 0.007250780239701271, + 0.07478834688663483, + 0.37752634286880493, + 0.10522382706403732, + -0.3126020133495331, + -0.339804470539093, + -0.2922729253768921, + -0.04612985998392105, + 0.06431944668292999, + 0.08483731746673584, + 0.12883307039737701, + -0.015924949198961258, + 0.10468991845846176, + -0.3394957184791565, + 0.23376204073429108, + -0.22720825672149658, + 0.005506275221705437, + -0.22926953434944153, + -0.10148110240697861, + 0.06526672840118408, + -0.2586720287799835, + -0.32853958010673523, + 0.3440588712692261, + -0.11197478324174881, + -0.24647162854671478, + 0.32472386956214905, + 0.18955329060554504, + 0.22783295810222626, + 0.27004650235176086, + 0.06792190670967102, + -0.25404539704322815, + -0.0421239472925663, + 0.19141103327274323, + -0.1919824779033661, + 0.024490466341376305, + -0.45774775743484497, + 0.15080632269382477, + -0.21607035398483276, + -0.15506379306316376, + -0.4421549439430237, + -0.3747740089893341, + -0.40712970495224, + -0.01002188865095377, + -0.18514835834503174, + -0.052659012377262115, + -0.009491002187132835, + -0.04560127854347229, + 0.5816720724105835, + -0.8684999942779541, + -0.6074734330177307, + -0.6023196578025818, + 0.09026342630386353, + -0.8521136045455933, + -0.677777886390686, + -0.7927519083023071, + 0.05012498050928116, + 0.006620208732783794, + 0.09600439667701721, + 0.006934305187314749, + -0.41822823882102966, + 0.5416979193687439, + 1.3451576232910156, + 0.6131516098976135, + -0.1447380781173706, + 0.09429032355546951, + 0.06888633966445923, + 0.09988542646169662, + -0.09572823345661163, + 0.09141702950000763, + 0.05828794091939926, + -0.20784544944763184, + -0.14200495183467865, + 0.014049896970391273, + -0.081334687769413, + 0.15918458998203278, + 0.001768372836522758, + 0.009856577031314373, + 0.5256384611129761, + 0.49961280822753906, + 0.5969673991203308, + 0.37020817399024963, + -0.07463415712118149, + -0.0038648881018161774, + 0.014317997731268406, + 0.07256675511598587, + 0.27220791578292847, + -0.14287996292114258, + -0.18170645833015442, + -0.021593274548649788, + -0.15909305214881897, + 0.3259168863296509, + -0.11064229905605316, + 0.12034989148378372, + 0.36166661977767944, + -0.21680544316768646, + -0.14505243301391602, + -0.24518895149230957, + -0.054052721709012985, + 0.11477477848529816, + 0.10946492105722427, + -0.004644579254090786, + -0.11873581260442734, + 0.00934956781566143, + 0.026955196633934975, + -0.0947655513882637, + -0.0432097427546978, + 0.2264525443315506, + 0.4585563540458679, + -0.2117093950510025, + 0.06864829361438751, + 0.01817937195301056, + -0.09130346775054932, + -0.031736359000205994, + -0.6623827219009399, + 0.07924489676952362, + 0.30316102504730225, + 0.06474705785512924, + 0.12052184343338013, + -0.06878554821014404, + 0.048135798424482346, + 0.14442582428455353, + -0.1945008486509323, + 0.16308918595314026, + 0.13180820643901825, + -0.3005691170692444, + -0.08318639546632767, + -0.0371159091591835, + -0.036223117262125015, + 0.27411049604415894, + -0.008904200047254562, + -0.21584218740463257, + -0.22458405792713165, + -0.2840893864631653, + 0.9380438327789307, + -0.026274412870407104, + -0.03674294427037239, + -0.039288733154535294, + 0.20259428024291992, + -0.2627299726009369, + -0.03588804602622986, + -0.09061996638774872, + 0.0026293552946299314, + -1.1599351167678833, + -0.0888570249080658, + 0.3020864427089691, + 0.10419020056724548, + -0.2301473766565323, + -0.2372182309627533, + 0.255910724401474, + -0.9108321666717529, + -0.17266617715358734, + -0.21715109050273895, + -0.4768790900707245, + 0.02349638193845749, + 0.06996935606002808, + 0.2306048572063446, + -0.2647320032119751, + -0.5029106140136719, + 0.18124276399612427, + 0.05404527485370636, + -0.556660532951355, + -0.20282964408397675, + 0.1787903904914856, + -0.13809867203235626, + 0.012665750458836555, + -0.007909105159342289, + -0.11666542291641235, + 0.192016139626503, + 0.20280246436595917, + 0.04091315343976021, + 0.21129484474658966, + 0.06015581637620926, + -0.1396055370569229, + 0.11048803478479385, + -0.22130873799324036, + 0.10175041109323502, + 0.15478093922138214, + -0.06699641793966293, + 0.16655825078487396, + -0.5767931938171387, + 0.23376262187957764, + -0.06561370939016342, + 0.08572515100240707, + 0.22690269351005554, + -0.10714394599199295, + 0.2328615039587021, + 0.06609856337308884, + 0.15064586699008942, + 0.1398843675851822, + 9.159173350781202e-05, + -0.006412057671695948, + 0.1231503039598465, + 0.2868848741054535, + -0.37850138545036316, + -0.4390513002872467, + -0.10716433078050613, + -0.16492293775081635, + -0.17774488031864166, + -0.006263014394789934, + -0.15535981953144073, + -0.15121980011463165, + -0.022719506174325943, + -0.3260766863822937, + 0.1365034133195877, + 0.7772430777549744, + 0.8306354880332947, + 0.8039601445198059, + 0.16534824669361115, + -0.03939266875386238, + -0.15611104667186737, + 0.21217003464698792, + -0.022034769877791405, + -0.025939559563994408, + 0.1058378517627716, + -0.08505864441394806, + 0.08503950387239456, + -0.0037705348804593086, + -0.0026697057764977217, + 0.3492349088191986, + 0.15157155692577362, + -0.3159380555152893, + -0.10824967920780182, + -0.04872310906648636, + 0.19715555012226105, + -0.2658633291721344, + -0.06968845427036285, + 0.009916169568896294, + 0.18593478202819824, + -0.038871243596076965, + -0.3416462540626526, + 0.1855567842721939, + 0.21629339456558228, + -0.10832708328962326, + -0.04190235957503319, + 0.2388715296983719, + -0.11624565720558167, + -0.10361404716968536, + 0.0536813959479332, + 0.12528158724308014, + -0.262010782957077, + 0.05081893876194954, + 0.29551735520362854, + 0.05958620831370354, + -0.01989975944161415, + -0.19261345267295837, + 0.01736867055296898, + -0.07923264801502228, + -0.4404444694519043, + 0.3125889301300049, + 0.10095971822738647, + 0.17173698544502258, + 0.23782190680503845, + -0.07170403748750687, + 0.013639729470014572, + 0.19007621705532074, + 0.1901141107082367, + -0.052342064678668976, + -0.9643150568008423, + -0.12307217717170715, + -0.21010802686214447, + -0.5640560984611511, + 0.010125457309186459, + 0.1314179003238678, + 0.10721258819103241, + -0.24371789395809174, + -0.5925355553627014, + 0.49424877762794495, + -0.03528435528278351, + -0.21386614441871643, + 1.4134130477905273, + -0.2751445770263672, + 0.007012579124420881, + -0.023824317380785942, + 0.004113825503736734, + -0.06332013010978699, + 0.286077082157135, + 0.04896686226129532, + 0.31404414772987366, + 0.15028351545333862, + 0.003490754636004567, + 0.0802399218082428, + -0.230818971991539, + -0.022719932720065117, + 0.26083019375801086, + -0.2885863184928894, + 0.07537354528903961, + 0.12282905727624893, + -0.38638314604759216, + 0.1752759963274002, + -0.07370781153440475, + 0.13994526863098145, + 0.13313405215740204, + 0.2851952016353607, + 0.905279278755188, + 0.34521353244781494, + -0.36453402042388916, + 0.46360254287719727, + -0.002040385501459241, + -0.003476516343653202, + -0.19058215618133545, + 0.27096763253211975, + 0.08722586184740067, + 0.03202880546450615, + -0.06164764240384102, + 0.011678489856421947, + 0.21189850568771362, + -0.40100231766700745, + 0.022941868752241135, + 0.0394427627325058, + 0.0675845518708229, + -0.22503064572811127, + 0.14730903506278992, + 0.24842065572738647, + -0.34360530972480774, + 0.21811245381832123, + -0.05238509923219681, + -0.008763357996940613, + -0.1336073875427246, + 0.15671105682849884, + 0.4475333094596863, + -0.5187726616859436, + 0.005388418212532997, + -0.07889139652252197, + 0.10729073733091354, + 0.22381159663200378, + 0.07434546202421188, + -0.0843898206949234, + 0.13574494421482086, + 0.01853088103234768, + -0.41072791814804077, + 0.40448933839797974, + -0.8231801986694336, + -0.4780847728252411, + -0.11237931996583939, + 0.012673617340624332, + -0.04672158136963844, + -0.23933981359004974, + 0.01667654886841774, + -0.14681674540042877, + 0.077765092253685, + 0.15309257805347443, + 0.03254099190235138, + -0.015896232798695564, + -0.029608771204948425, + -0.288953959941864, + -0.32651081681251526, + 0.06307528167963028, + -0.09873636066913605, + 0.08938323706388474, + 0.27018269896507263, + 0.018129458650946617, + -0.050469521433115005, + -0.17951229214668274, + 0.02319747768342495, + 0.06737810373306274, + 0.2690926194190979, + -0.10778623819351196, + -0.04740763455629349, + 0.30407941341400146, + -0.08746829628944397, + -0.2184152454137802, + 0.14826175570487976, + 0.18092381954193115, + 0.07989493757486343, + -0.1297195851802826, + ], + b1: &[ + -0.5191351175308228, + 0.6662623882293701, + 0.610133707523346, + -1.1585999727249146, + 0.6903770565986633, + 0.4241520166397095, + 0.754120945930481, + -0.7599878907203674, + -0.3445088267326355, + 0.9317805767059326, + -0.2041703462600708, + 0.17219330370426178, + 1.1566059589385986, + -0.41121166944503784, + -0.6977726817131042, + 0.7911778092384338, + 0.6611397862434387, + -0.6938921213150024, + -0.03742314130067825, + -0.16022440791130066, + 0.11257349699735641, + 0.07743008434772491, + -0.6286312937736511, + 0.544836699962616, + -0.15634237229824066, + -0.5572881698608398, + 0.9681645035743713, + -0.7440500855445862, + 0.10288882255554199, + 0.9043763875961304, + 0.14654643833637238, + -0.024421239271759987, + -0.4609592854976654, + 0.917902410030365, + 0.2704138457775116, + 0.6341348886489868, + 0.034945350140333176, + 0.5565919876098633, + 0.1746397614479065, + -0.6341800093650818, + ], + w2: &[ + 0.07229708135128021, + 0.2507615387439728, + 0.16330942511558533, + 0.5204483866691589, + 0.24313874542713165, + -0.5474504232406616, + -0.28332123160362244, + -0.2225571572780609, + -0.1043124571442604, + 0.06595291197299957, + 0.21239061653614044, + -0.14725270867347717, + -0.8134568333625793, + 0.07381946593523026, + -0.24956485629081726, + 0.4919748604297638, + 0.2962062954902649, + 0.3260444402694702, + 0.07504145801067352, + -0.053836897015571594, + 0.2531750500202179, + -0.04855559393763542, + -0.5578967332839966, + -0.5225025415420532, + 0.055111128836870193, + -0.21510563790798187, + 0.5871708989143372, + -0.19132649898529053, + 0.007392226252704859, + -0.298953115940094, + 0.16707110404968262, + -0.04706822335720062, + 0.07302752882242203, + -0.08172990381717682, + 0.23955324292182922, + -0.15824700891971588, + -0.3977665305137634, + 0.5267415642738342, + -0.11258449405431747, + -0.3343915045261383, + 0.23245088756084442, + -0.7491211891174316, + -0.6333310604095459, + 0.0232061930000782, + -0.2315434217453003, + -0.3745144307613373, + -0.03209906071424484, + -0.4041699469089508, + 0.041345734149217606, + 0.19181972742080688, + -0.2760458290576935, + -0.07779327034950256, + 0.24569696187973022, + -0.18802686035633087, + -0.6544056534767151, + 0.556419849395752, + 0.11468080431222916, + -0.32528090476989746, + 0.38538315892219543, + 0.33702555298805237, + -0.442532479763031, + 0.00750756124034524, + -0.45737770199775696, + -0.06860284507274628, + -0.4411284625530243, + -0.23914210498332977, + 0.06834587454795837, + 0.14571186900138855, + 0.6887655258178711, + 0.5702284574508667, + 0.3135473430156708, + -0.3360161781311035, + -0.5353860259056091, + 0.06292688101530075, + 0.735708475112915, + 0.7143703103065491, + -0.3693147897720337, + 0.525284469127655, + 0.39448651671409607, + -0.09941494464874268, + 0.09564384818077087, + 0.5881519913673401, + 0.05619557946920395, + 0.4508857727050781, + -0.2834583520889282, + -0.16902177035808563, + 0.24799591302871704, + -0.182522252202034, + 0.0468696765601635, + 0.14808374643325806, + -0.013205822557210922, + -0.12705814838409424, + 0.0614711195230484, + 0.14103399217128754, + -0.2599405348300934, + 0.028414186090230942, + -0.2865449786186218, + -0.08163938671350479, + 0.13120926916599274, + 0.17990124225616455, + -0.16350798308849335, + -0.09809352457523346, + -0.013590727932751179, + -0.17736633121967316, + 0.05107983574271202, + 0.3411618173122406, + -0.2772451341152191, + 0.32397109270095825, + 0.046551186591386795, + 0.13246433436870575, + 0.05053735896945, + 0.24057962000370026, + -0.04693610221147537, + -0.1650579869747162, + 0.1331019252538681, + 0.09457181394100189, + -0.16547952592372894, + -0.09469929337501526, + 0.30049434304237366, + 0.12664170563220978, + -0.013082812540233135, + 0.390655517578125, + 0.6400918364524841, + -0.0010483618825674057, + -0.03533017635345459, + 0.16345657408237457, + 0.05697643384337425, + 0.1748565286397934, + 0.0036667422391474247, + -0.05557025969028473, + 0.016822226345539093, + -0.12541711330413818, + -0.4695605933666229, + 0.008447905071079731, + 0.16371716558933258, + -0.1481284201145172, + -0.10916673392057419, + 0.1754710078239441, + -0.05557332932949066, + 0.17406205832958221, + 0.03734235838055611, + -0.0014076621737331152, + 0.16409075260162354, + -0.0339696928858757, + 0.11525241285562515, + 0.11995170265436172, + -0.39020177721977234, + 0.01936984248459339, + -0.14390763640403748, + -0.18344464898109436, + -0.08675119280815125, + 0.19569827616214752, + 0.48439380526542664, + -0.232485830783844, + -0.004231136757880449, + 0.15202505886554718, + 0.01103641465306282, + -0.1192987710237503, + -0.17487019300460815, + 0.27336806058883667, + -0.5894135236740112, + -0.03331466019153595, + 0.21942859888076782, + 0.30420297384262085, + 0.2666693329811096, + 0.4481956958770752, + -0.020630693063139915, + 0.8494743704795837, + 0.5691520571708679, + 0.5711295008659363, + 0.00404204148799181, + 0.5070351958274841, + 0.09074786305427551, + 0.15874768793582916, + 0.7676622271537781, + 0.6556511521339417, + 0.1220490038394928, + 0.7263025641441345, + -0.07173441350460052, + 0.14413252472877502, + 0.49090006947517395, + -0.3324028253555298, + 0.45898303389549255, + 0.5931536555290222, + 0.19021296501159668, + -0.7473744750022888, + -0.834629476070404, + -0.1385311633348465, + -0.05174582824110985, + 0.018871335312724113, + -0.42817312479019165, + 0.20682017505168915, + 0.016382897272706032, + -0.6684255599975586, + 0.3525462746620178, + -0.42306870222091675, + -0.0817568302154541, + 0.3572525084018707, + -0.23954586684703827, + -0.4869120717048645, + 0.016070470213890076, + 0.5639761686325073, + 0.17797298729419708, + 0.2919785678386688, + -0.3837592601776123, + 0.13362792134284973, + 0.09925093501806259, + 0.12642522156238556, + 0.09690988808870316, + -0.08732952922582626, + 0.24605968594551086, + -0.3894798457622528, + -0.174991175532341, + 0.2573908269405365, + 0.22514064610004425, + -0.24535547196865082, + -0.2993263006210327, + 0.24350187182426453, + 0.03375721350312233, + 0.16244018077850342, + -0.16753582656383514, + -0.08621060848236084, + 0.1272309273481369, + 0.007472787983715534, + 0.20557984709739685, + 0.1578531116247177, + -0.5838948488235474, + 0.08410368114709854, + -0.2831973135471344, + -0.28126293420791626, + -0.08023717254400253, + 0.5180243849754333, + 0.2208152413368225, + -0.3613019585609436, + -0.06204051896929741, + -0.13526616990566254, + 0.09384715557098389, + -0.27185022830963135, + -0.05938927084207535, + 0.284194678068161, + 0.04228530079126358, + 0.5006632208824158, + 0.6578063368797302, + -0.07014274597167969, + -0.3233219087123871, + -0.01618030108511448, + 0.2888641357421875, + -0.08185673505067825, + -0.17689819633960724, + -0.2994365096092224, + 0.016244128346443176, + 0.02359011210501194, + 0.1367129534482956, + -0.01653127372264862, + -0.09157261997461319, + -0.3516620397567749, + -0.09030301123857498, + -0.07817772775888443, + 0.17603041231632233, + -0.01393663790076971, + -0.029468189924955368, + -0.0814921036362648, + -0.12077502906322479, + -0.10759524255990982, + -0.0750858411192894, + 0.2511105239391327, + -0.20753242075443268, + -0.05136517807841301, + -0.024205535650253296, + -0.3384825587272644, + 0.020664114505052567, + 0.11200296878814697, + 0.08333364874124527, + -0.24177855253219604, + -0.07010341435670853, + 0.020779477432370186, + -0.20839253067970276, + -0.0016562794335186481, + 0.023504814133048058, + 0.3570723235607147, + -0.30022287368774414, + -0.3554439842700958, + -0.027536675333976746, + -1.1282703876495361, + -0.08706718683242798, + 0.0742080882191658, + 0.18080361187458038, + -0.02274167723953724, + -0.704075813293457, + -0.9722687602043152, + 0.1188407614827156, + -0.029379399493336678, + 0.8019110560417175, + -0.34810709953308105, + 0.04902748018503189, + -0.7494327425956726, + 0.5064789056777954, + -0.11681736260652542, + 0.2257058471441269, + -0.4354608356952667, + 0.3252757489681244, + -0.1591869592666626, + -0.5933760404586792, + -0.5259361863136292, + 0.22252318263053894, + 0.30712220072746277, + 0.29186123609542847, + -0.7899709343910217, + 0.3455640971660614, + -0.8577526807785034, + 0.19282177090644836, + 0.29095181822776794, + -0.3287593424320221, + 0.0454283282160759, + -0.5983009338378906, + -0.08342050760984421, + -0.8976981043815613, + 0.10165920853614807, + 0.13396088778972626, + 0.2290259599685669, + 0.02499830722808838, + 0.7539560794830322, + 0.1477266401052475, + 0.3097168207168579, + -0.3993585705757141, + 0.0817292109131813, + 0.038499560207128525, + 0.048502497375011444, + 0.10572300106287003, + -0.17650842666625977, + 0.30300378799438477, + -0.3586488962173462, + -0.09699319303035736, + 0.28980425000190735, + 0.1152607873082161, + -0.30993735790252686, + -0.3226162791252136, + 0.2082981914281845, + 0.08206543326377869, + 0.09643732011318207, + -0.09098457545042038, + -0.09191355854272842, + 0.04240717366337776, + -0.08706614375114441, + 0.3119218051433563, + 0.24132680892944336, + -0.5137639045715332, + 0.03463784605264664, + -0.29585450887680054, + -0.3583862781524658, + -0.09919128566980362, + 0.5263358950614929, + 0.19875890016555786, + -0.4007430374622345, + -0.044145308434963226, + -0.24342355132102966, + 0.16471655666828156, + -0.25901785492897034, + 0.012997856363654137, + 0.3298455476760864, + -0.23130790889263153, + 0.4484388828277588, + 0.35633817315101624, + 0.26454973220825195, + 0.15214529633522034, + -0.12443697452545166, + -0.405061811208725, + 0.17236965894699097, + -0.36522531509399414, + -0.074102483689785, + 0.09564346820116043, + -0.26696014404296875, + -0.7053405046463013, + -0.4750596880912781, + 0.2850874066352844, + -0.42413032054901123, + 0.3273111581802368, + 0.013779409229755402, + -0.7248923182487488, + -0.49210208654403687, + 0.5041399002075195, + -0.14308881759643555, + 0.629442036151886, + -0.8470776677131653, + 0.36798736453056335, + -0.17092065513134003, + 0.5437707304954529, + -0.26034078001976013, + -0.4502609074115753, + 0.2898317873477936, + -0.3266198933124542, + 0.1681036651134491, + 0.6064534783363342, + 0.48974573612213135, + -0.3461318910121918, + -0.36192092299461365, + 0.3675844371318817, + -0.731248676776886, + -0.21227769553661346, + -0.4246974289417267, + 0.17397946119308472, + -0.3643985986709595, + 0.205714613199234, + 0.629838228225708, + 0.10543780773878098, + 0.010421440936625004, + 0.6487590670585632, + -0.685522198677063, + 0.010746597312390804, + 0.371294766664505, + -0.68584144115448, + 0.69797283411026, + -0.39890381693840027, + 0.2957388460636139, + 0.10036955028772354, + -0.31620606780052185, + -0.5876231789588928, + -0.5783882737159729, + -0.4745366871356964, + 0.20689401030540466, + -0.2748165428638458, + 0.34110450744628906, + 0.817054033279419, + 0.8686729073524475, + -0.6139298677444458, + -0.19506172835826874, + -0.03448706120252609, + 0.635860025882721, + -0.38243091106414795, + 0.8843176960945129, + 0.08922040462493896, + -0.8030375242233276, + 0.01003911904990673, + 0.49227485060691833, + 0.02043282799422741, + -0.1812848448753357, + 0.8425045609474182, + -0.18937410414218903, + 0.2360723465681076, + -0.0486280657351017, + 0.1306903064250946, + 0.44811540842056274, + -0.09772484004497528, + 0.3676001727581024, + -0.10864408314228058, + 0.10239739716053009, + 0.26535993814468384, + -0.19465096294879913, + -0.05268852412700653, + 0.013907784596085548, + 0.11859709769487381, + -0.008244873955845833, + -0.12678827345371246, + 0.16795198619365692, + 0.09826375544071198, + -0.13783332705497742, + -0.32474759221076965, + -0.018496913835406303, + -0.12179988622665405, + 0.22411927580833435, + -0.10514824092388153, + 0.038778163492679596, + 0.33486974239349365, + 0.31644245982170105, + 0.05365574359893799, + 0.24912847578525543, + -0.31889432668685913, + 0.24240325391292572, + -0.19231560826301575, + 0.18558776378631592, + -0.022984078153967857, + 0.11608095467090607, + 0.15418484807014465, + -0.14139854907989502, + 0.01758008636534214, + -0.12027571350336075, + 0.2522386610507965, + -0.2922046184539795, + 0.049236513674259186, + 0.19894357025623322, + 0.39957553148269653, + 0.3346879780292511, + 0.3187335133552551, + 0.4501717686653137, + -0.8946970701217651, + 0.18189306557178497, + -0.08766483515501022, + 0.2782788574695587, + 0.3587392270565033, + -0.33824455738067627, + 0.6033147573471069, + -0.6243746876716614, + -0.6177958250045776, + 0.6629742383956909, + 0.4856598377227783, + -0.3099081814289093, + -0.678487241268158, + 0.47894829511642456, + -0.03139176964759827, + 0.16848357021808624, + -0.5739434957504272, + -0.16708984971046448, + 0.11146949231624603, + 0.090438611805439, + 0.4812713861465454, + 0.5129365921020508, + -0.7324693202972412, + 0.26365718245506287, + -0.4824923276901245, + -0.5487518310546875, + -0.20128659904003143, + 0.5759150385856628, + 0.3504473567008972, + -0.36605504155158997, + -0.4257725477218628, + -0.25298258662223816, + 0.512897789478302, + -0.4181336462497711, + -0.516604483127594, + 0.37244912981987, + ], + b2: &[ + 0.14859354496002197, + -0.018167857080698013, + -0.3407953083515167, + -0.14991576969623566, + 0.4018653333187103, + -0.2384500652551651, + -0.4047893285751343, + 0.15702210366725922, + -0.3152092695236206, + 0.29297566413879395, + 0.26403820514678955, + -0.2573520541191101, + -0.11290331929922104, + ], + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm64/cortex_a72.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm64/cortex_a72.rs new file mode 100644 index 0000000..6819f4d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm64/cortex_a72.rs @@ -0,0 +1,4 @@ +use crate::frame::mmm::cost_model::CostModel; +pub fn models() -> Vec<(&'static str, CostModel<'static>)> { + vec![] +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm64/cortex_a73.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm64/cortex_a73.rs new file mode 100644 index 0000000..6819f4d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/arm64/cortex_a73.rs @@ -0,0 +1,4 @@ +use crate::frame::mmm::cost_model::CostModel; +pub fn models() -> Vec<(&'static str, CostModel<'static>)> { + vec![] +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame.rs new file mode 100644 index 0000000..2d61415 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame.rs @@ -0,0 +1,17 @@ +#[macro_use] +pub mod element_wise; +#[macro_use] +pub mod lut; +#[macro_use] +pub mod mmm; +pub mod pack; +#[macro_use] +pub mod sigmoid; +#[macro_use] +pub mod tanh; + +pub use pack::Packer; +pub use pack::PackingWriter; + +pub use self::element_wise::{ElementWise, ElementWiseImpl}; +pub use self::mmm::{MatMatMul, MatMatMulImpl}; diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/element_wise.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/element_wise.rs new file mode 100644 index 0000000..749002a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/element_wise.rs @@ -0,0 +1,186 @@ +use std::alloc::*; +use std::fmt::Debug; +use std::marker::PhantomData; +use tract_data::anyhow; + +use crate::LADatum; + +macro_rules! ew_impl { + ($ti: ident, $func: ident, $nr: expr, $alignment_items: expr) => { + paste! { + mod [] { + #[allow(unused_imports)] + use tract_data::prelude::f16; + extern_kernel!(fn $func(ptr: *mut $ti, count: usize) -> ()); + } + + #[derive(Copy, Clone, Debug)] + #[allow(non_camel_case_types)] + pub struct $func; + + impl ElementWiseKer<$ti> for $func { + #[inline(always)] + fn name() -> &'static str { + stringify!($func) + } + #[inline(always)] + fn nr() -> usize { + $nr + } + #[inline(always)] + fn alignment_items() -> usize { + $alignment_items + } + #[inline(always)] + fn alignment_bytes() -> usize { + $alignment_items * std::mem::size_of::<$ti>() + } + #[inline(never)] + fn run(buf: &mut [$ti]) { + unsafe { []::$func(buf.as_mut_ptr(), buf.len()) } + } + } + } + }; +} + +struct TempBuffer { + layout: Layout, + buffer: *mut u8, +} + +impl Default for TempBuffer { + fn default() -> Self { + TempBuffer { + layout: Layout::new::<()>(), + buffer: std::ptr::null_mut(), + } + } +} + +impl TempBuffer { + fn ensure(&mut self, size: usize, alignment: usize) { + unsafe { + if size > self.layout.size() || alignment > self.layout.align() { + let size = size.max(self.layout.size()); + let alignment = alignment.max(self.layout.align()); + if !self.buffer.is_null() { + std::alloc::dealloc(self.buffer, self.layout); + } + self.layout = Layout::from_size_align_unchecked(size, alignment); + self.buffer = std::alloc::alloc(self.layout); + assert!(!self.buffer.is_null()); + } + } + } +} + +impl Drop for TempBuffer { + fn drop(&mut self) { + unsafe { + if !self.buffer.is_null() { + std::alloc::dealloc(self.buffer, self.layout); + } + } + } +} + +std::thread_local! { + static TMP: std::cell::RefCell = std::cell::RefCell::new(TempBuffer::default()); +} + +pub trait ElementWise: Send + Sync + Debug + dyn_clone::DynClone +where + T: Copy + Debug + PartialEq + Send + Sync, +{ + fn run(&self, vec: &mut [T]) -> anyhow::Result<()>; +} + +dyn_clone::clone_trait_object!( ElementWise where T: Copy); + +#[derive(Debug, Clone, new)] +pub struct ElementWiseImpl +where + T: LADatum, + K: ElementWiseKer + Clone, +{ + phantom: PhantomData<(K, T)>, +} + +impl ElementWise for ElementWiseImpl +where + T: LADatum, + K: ElementWiseKer + Clone, +{ + fn run(&self, vec: &mut [T]) -> anyhow::Result<()> { + if vec.is_empty() { + return Ok(()); + } + unsafe { + TMP.with(|buffer| { + let mut buffer = buffer.borrow_mut(); + buffer.ensure(K::nr() * T::datum_type().size_of(), K::alignment_bytes()); + let tmp = std::slice::from_raw_parts_mut(buffer.buffer as *mut T, K::nr()); + let mut compute_via_temp_buffer = |slice: &mut [T]| { + tmp[..slice.len()].copy_from_slice(slice); + K::run(tmp); + slice.copy_from_slice(&tmp[..slice.len()]) + }; + let prefix_len = vec + .as_ptr() + .align_offset(K::alignment_bytes()) + .min(vec.len()); + if prefix_len > 0 { + compute_via_temp_buffer(&mut vec[..prefix_len]); + } + let aligned_len = (vec.len() - prefix_len) / K::nr() * K::nr(); + if aligned_len > 0 { + K::run(&mut vec[prefix_len..][..aligned_len]); + } + if prefix_len + aligned_len < vec.len() { + compute_via_temp_buffer(&mut vec[prefix_len + aligned_len..]); + } + }) + } + Ok(()) + } +} + +pub trait ElementWiseKer: Send + Sync + Debug + dyn_clone::DynClone + Clone + 'static +where + T: LADatum, +{ + fn name() -> &'static str; + fn alignment_bytes() -> usize; + fn alignment_items() -> usize; + fn nr() -> usize; + fn run(vec: &mut [T]); + fn ew() -> Box> { + Box::new(ElementWiseImpl::::new()) + } +} + +#[cfg(test)] +pub mod test { + use crate::{LADatum, frame::element_wise::*}; + use proptest::test_runner::{TestCaseError, TestCaseResult}; + use tract_data::internal::*; + + pub fn test_element_wise, T: LADatum, F: Fn(T) -> T>( + values: &[T], + reference: F, + ) -> TestCaseResult { + let op = ElementWiseImpl::::new(); + let mut values = values.to_vec(); + while values.len() < K::nr() { + values.push(T::zero()); + } + let expected = values.iter().copied().map(reference).collect::>(); + let mut found = values; + op.run(&mut found).unwrap(); + tensor1(&found) + .close_enough(&tensor1(&expected), true) + .map_err(|e| TestCaseError::fail(e.root_cause().to_string()))?; + Ok(()) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/lut.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/lut.rs new file mode 100644 index 0000000..dd4548b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/lut.rs @@ -0,0 +1,158 @@ +use std::fmt; +use std::hash::Hash; +use std::marker::PhantomData; +use tract_data::internal::*; + +pub trait Lut: fmt::Debug + dyn_clone::DynClone + Send + Sync + DynHash { + fn table(&self) -> &[u8]; + fn run(&self, buf: &mut [u8]); +} + +dyn_clone::clone_trait_object!(Lut); + +impl std::hash::Hash for Box { + fn hash(&self, state: &mut H) { + use std::any::Any; + std::hash::Hash::hash(&self.type_id(), state); + self.dyn_hash(state) + } +} + +#[derive(Debug, Clone, Hash)] +pub struct LutImpl { + table: Tensor, + _boo: PhantomData, +} + +impl DynHash for LutImpl { + fn dyn_hash(&self, state: &mut dyn std::hash::Hasher) { + tract_data::hash::dyn_hash(self, state) + } +} + +impl LutImpl { + pub fn new(table: &[u8]) -> LutImpl { + unsafe { + LutImpl { + table: Tensor::from_raw_aligned::( + &[table.len()], + table, + K::table_alignment_bytes(), + ) + .unwrap(), + _boo: PhantomData, + } + } + } +} + +impl Lut for LutImpl { + fn table(&self) -> &[u8] { + self.table.as_slice().unwrap() + } + + fn run(&self, buf: &mut [u8]) { + unsafe { + let table: *const u8 = self.table.as_ptr_unchecked(); + let align = K::input_alignment_bytes(); + let aligned_start = (buf.as_ptr() as usize + align - 1) / align * align; + let prefix = (aligned_start - buf.as_ptr() as usize).min(buf.len()); + for i in 0..(prefix as isize) { + let ptr = buf.as_mut_ptr().offset(i); + *ptr = *table.offset(*ptr as isize); + } + let remaining = buf.len() - prefix; + if remaining == 0 { + return; + } + let n = K::n(); + let aligned_len = remaining / n * n; + if aligned_len > 0 { + K::run(buf.as_mut_ptr().add(prefix), aligned_len, table); + } + let remaining = buf.len() - aligned_len - prefix; + for i in 0..remaining { + let ptr = buf.as_mut_ptr().add(i + prefix + aligned_len); + *ptr = *table.offset(*ptr as isize); + } + } + } +} + +pub trait LutKer: Clone + fmt::Debug + Send + Sync + Hash { + fn name() -> &'static str; + fn n() -> usize; + fn input_alignment_bytes() -> usize; + fn table_alignment_bytes() -> usize; + unsafe fn run(buf: *mut u8, len: usize, table: *const u8); +} + +#[cfg(test)] +#[macro_use] +pub mod test { + use super::*; + use proptest::prelude::*; + + #[derive(Debug)] + pub struct LutProblem { + pub table: Vec, + pub data: Vec, + } + + impl Arbitrary for LutProblem { + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_p: ()) -> Self::Strategy { + proptest::collection::vec(any::(), 1..256) + .prop_flat_map(|table| { + let data = proptest::collection::vec(0..table.len() as u8, 0..100); + (Just(table), data) + }) + .prop_map(|(table, data)| LutProblem { table, data }) + .boxed() + } + } + + impl LutProblem { + pub fn reference(&self) -> Vec { + self.data.iter().map(|x| self.table[*x as usize]).collect() + } + + pub fn test(&self) -> Vec { + let lut = LutImpl::::new(&self.table); + let mut data = self.data.clone(); + lut.run(&mut data); + data + } + } + + #[macro_export] + macro_rules! lut_frame_tests { + ($cond:expr, $ker:ty) => { + mod lut { + use proptest::prelude::*; + #[allow(unused_imports)] + use $crate::frame::lut::test::*; + + proptest::proptest! { + #[test] + fn lut_prop(pb in any::()) { + if $cond { + prop_assert_eq!(pb.test::<$ker>(), pb.reference()) + } + } + } + + #[test] + fn test_empty() { + let pb = LutProblem { + table: vec![0], + data: vec![], + }; + assert_eq!(pb.test::<$ker>(), pb.reference()) + } + } + }; + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm.rs new file mode 100644 index 0000000..b33a13c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm.rs @@ -0,0 +1,95 @@ +pub mod cost_model; +#[macro_use] +pub(crate) mod fuse; +#[macro_use] +pub(crate) mod kernel; +pub(crate) mod input_store; +#[macro_use] +#[allow(clippy::module_inception)] +pub(crate) mod mmm; +mod scratch; +mod storage; +#[cfg(test)] +#[macro_use] +pub mod tests; + +pub use cost_model::*; +pub use fuse::*; +pub use input_store::*; +pub use kernel::*; +pub use mmm::*; +pub use scratch::*; +pub use storage::*; + +pub fn no_prefetch(_ptr: *const u8, _len: usize) {} + +macro_rules! MMMKernel { + ($ti:ident, $func:ident; $mr: expr, $nr: expr; $alignment_bytes_packed_a: expr, $alignment_bytes_packed_b: expr; $end_padding_packed_a: expr, $end_padding_packed_b: expr ; $prefetch: ident, $cond: expr) => { + paste! { + mod [] { + use crate::frame::mmm::*; + #[allow(unused_imports)] + use tract_data::prelude::f16; + extern_kernel!(fn $func(op: *const FusedKerSpec<$ti>) -> isize); + } + + #[allow(non_camel_case_types)] + #[derive(Copy, Clone, Debug, new)] + pub struct $func; + + impl MatMatMulKer<$ti> for $func { + #[inline(always)] + fn name() -> &'static str { + stringify!($func) + } + #[inline(always)] + fn mr() -> usize { + $mr + } + #[inline(always)] + fn nr() -> usize { + $nr + } + #[inline(always)] + fn alignment_bytes_packed_a() -> usize { + $alignment_bytes_packed_a + } + #[inline(always)] + fn alignment_bytes_packed_b() -> usize { + $alignment_bytes_packed_b + } + #[inline(always)] + fn end_padding_packed_a() -> usize { + $end_padding_packed_a + } + #[inline(always)] + fn end_padding_packed_b() -> usize { + $end_padding_packed_b + } + #[inline(always)] + fn kernel(spec: &[FusedKerSpec<$ti>]) -> isize { + debug_assert!(spec.len() > 0); + debug_assert!(matches!(spec[spec.len() - 1], FusedKerSpec::Done)); + unsafe { []::$func(spec.as_ptr()) } + } + #[inline(always)] + fn prefetch(ptr: *const u8, len: usize) { + ($prefetch)(ptr, len) + } + } + } + test_mmm_kernel!($ti, $func, $cond); + }; +} + +macro_rules! test_mmm_kernel { + (f16, $func:ident, $cond: expr) => { + test_mmm_kernel_f16!($func, $cond); + }; + (f32, $func:ident, $cond: expr) => { + test_mmm_kernel_f32!($func, $cond); + }; + (i32, $func:ident, $cond: expr) => { + test_mmm_kernel_i32!($func, $cond); + }; +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/cost_model.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/cost_model.rs new file mode 100644 index 0000000..c94f04d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/cost_model.rs @@ -0,0 +1,94 @@ +use tract_data::internal::*; +use tract_data::itertools::{Itertools, izip}; + +use super::MatMatMul; + +fn order_f(&a: &F, &b: &F) -> std::cmp::Ordering { + if a < b { + std::cmp::Ordering::Less + } else { + std::cmp::Ordering::Greater + } +} + +#[derive(Debug)] +pub struct CostModel<'a> { + pub big_product_mkn_threshold: f32, + pub big_product_kernel_choice: &'a str, + pub kernels: &'a [&'a str], + pub mrs: &'a [u32], + pub nrs: &'a [u32], + pub feat_norm_mean: &'a [f32], + pub feat_norm_stddev: &'a [f32], + pub w1: &'a [f32], + pub b1: &'a [f32], + pub w2: &'a [f32], + pub b2: &'a [f32], +} + +impl<'a> CostModel<'a> { + pub fn features(&self, m: usize, k: usize, n: usize) -> Vec { + let mut feat = vec![ + (m as f32).ln(), + (k as f32).ln(), + (n as f32).ln(), + (n as f32 * m as f32 * k as f32).ln(), + ]; + for &mr in self.mrs { + let mr = mr as usize; + feat.push((m % mr) as f32); + feat.push((m % mr != 0) as usize as f32); + } + for &nr in self.nrs { + let nr = nr as usize; + feat.push((n % nr) as f32); + feat.push((n % nr != 0) as usize as f32); + } + feat + } + + fn normalize(&self, feat: &mut [f32]) { + izip!(feat, self.feat_norm_mean, self.feat_norm_stddev) + .for_each(|(x, m, s)| *x = (*x - m) / s) + } + + fn dnn(x: &[f32], w: &[f32], b: &[f32]) -> Vec { + let x = tract_ndarray::Array1::from_vec(x.to_vec()); + let w = tract_ndarray::Array2::from_shape_vec([b.len(), x.len()], w.to_vec()).unwrap(); + let b = tract_ndarray::Array1::from_vec(b.to_vec()); + (w.dot(&x) + b).to_vec() + } + + pub fn predict(&self, m: usize, k: usize, n: usize) -> &str { + let mut x = self.features(m, k, n); + self.normalize(&mut x); + let mut hidden = Self::dnn(&x, self.w1, self.b1); + (crate::generic().tanh_f32)().run(&mut hidden).unwrap(); + let output = Self::dnn(&hidden, self.w2, self.b2); + let ix = output.iter().copied().position_max_by(order_f).unwrap(); + self.kernels[ix] + } + + pub fn pick( + &self, + impls: &[Box], + m: Option, + k: Option, + n: Option, + ) -> Box { + if let (Some(m), Some(k), Some(n)) = (m, k, n) { + let choice = self.predict(m, k, n); + impls + .iter() + .find(|k| k.kernel_name() == choice) + .unwrap() + .clone() + } else { + impls + .iter() + .find(|k| k.kernel_name() == self.big_product_kernel_choice) + .unwrap() + .clone() + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/fuse.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/fuse.rs new file mode 100644 index 0000000..b3cc5ac --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/fuse.rs @@ -0,0 +1,819 @@ +use std::fmt::Debug; + +use super::{InputStore, OutputStore, OutputStoreKer, PackedStore}; +use tract_data::internal::*; + +#[repr(usize)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum RoundingPolicy { + Native, + Zero, + Away, + MinusInf, + PlusInf, + Even, + Odd, +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum BinOp { + Min, + Max, + Add, + Mul, + Sub, + SubF, +} + +impl BinOp { + pub fn flip(&self) -> BinOp { + use BinOp::*; + match self { + Sub => SubF, + SubF => Sub, + sym => *sym, + } + } +} + +#[derive(Clone, Debug)] +pub enum FusedSpec<'t> { + BinScalar(&'t Tensor, BinOp), + BinPerRow(&'t Tensor, BinOp), + BinPerCol(&'t Tensor, BinOp), + AddRowColProducts(&'t Tensor, &'t Tensor), + AddUnicast(OutputStore), + QScale(isize, RoundingPolicy, i32), + RoundingShiftRight(usize, RoundingPolicy), + ShiftLeft(usize), + Store(OutputStore), + AddMatMul { + k: usize, + a: PackedStore, + b: InputStore, + }, +} + +impl<'t> FusedSpec<'t> { + pub fn prefer_col_outer(&self) -> bool { + if let FusedSpec::AddMatMul { b, .. } = self { + match b { + InputStore::Packed { .. } => false, + InputStore::VirtualPacking { .. } => true, + InputStore::LatePacking { .. } => true, + } + } else { + false + } + } +} + +// Careful here, the jump_to comments are used by the build script. +#[repr(C, usize)] +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +#[rustfmt::skip] +pub enum FusedKerSpec { + Done, // jump_to:done + Clear, // jump_to:clear + + ScalarMin(TI), // jump_to:scalar_min + ScalarMax(TI), // jump_to:scalar_max + ScalarAdd(TI), // jump_to:scalar_add + ScalarMul(TI), // jump_to:scalar_mul + ScalarSub(TI), // jump_to:scalar_sub + ScalarSubF(TI), // jump_to:scalar_sub_flipped + + PerRowMin(*const TI), // jump_to:per_row_min + PerRowMax(*const TI), // jump_to:per_row_max + PerRowAdd(*const TI), // jump_to:per_row_add + PerRowMul(*const TI), // jump_to:per_row_mul + PerRowSub(*const TI), // jump_to:per_row_sub + PerRowSubF(*const TI), // jump_to:per_row_sub_flipped + + PerColMin(*const TI), // jump_to:per_col_min + PerColMax(*const TI), // jump_to:per_col_max + PerColAdd(*const TI), // jump_to:per_col_add + PerColMul(*const TI), // jump_to:per_col_mul + PerColSub(*const TI), // jump_to:per_col_sub + PerColSubF(*const TI), // jump_to:per_col_sub_flipped + + QScale(isize, RoundingPolicy, i32), // jump_to:q_scale + RoundingShiftRight(usize, RoundingPolicy), // jump_to:q_shr + ShiftLeft(usize), // jump_to:q_shl + AddUnicast(OutputStoreKer), // jump_to:add_unicast + AddRowColProducts(*const TI, *const TI), // jump_to:add_row_col_products + Store(OutputStoreKer), // jump_to:store + + // jump_to:add_mat_mul + AddMatMul { k: usize, pa: *const u8, pb: *const u8, cpu_variant: usize }, +} + +#[cfg(test)] +#[macro_use] +pub mod test { + use crate::frame::mmm::storage::*; + use crate::frame::mmm::*; + use crate::generic::{ScaleShiftAndRound, Scaler}; + use num_traits::{AsPrimitive, Bounded}; + use proptest::prelude::*; + use tract_data::internal::*; + + #[test] + fn check_non_linear_enum_size() { + assert_eq!( + std::mem::size_of::(), + std::mem::size_of::() + ); + assert_eq!( + std::mem::size_of::>(), + std::mem::size_of::() + std::mem::size_of::() + ); + assert_eq!( + std::mem::size_of::>(), + 5 * std::mem::size_of::() + ); + } + + #[macro_export] + macro_rules! mmm_kernel_fuse_tests { + ($cond:expr, $ker:ident, $tc:ty, $ti: ty) => { + mod fuse { + use super::super::$ker; + #[allow(unused_imports)] + use tract_data::prelude::f16; + #[allow(unused_imports)] + use $crate::frame::mmm::fuse::test; + use $crate::frame::mmm::fuse::test::tile; + + #[test] + fn return_zeros() { + if $cond { + test::return_zeros::<$ker, $tc, $ti>() + } + } + + proptest::proptest! { + #[test] + fn return_c_prop(c in tile::<$ker, $tc, $ti>()) { + if $cond { + test::return_c::<$ker, $tc, $ti>(&c) + } + } + } + + #[test] + fn return_c_min_row() { + if $cond { + test::return_c_min_row::<$ker, $tc, $ti>() + } + } + + #[test] + fn return_c_max_row() { + if $cond { + test::return_c_max_row::<$ker, $tc, $ti>() + } + } + + #[test] + fn return_c_add_row() { + if $cond { + test::return_c_add_row::<$ker, $tc, $ti>() + } + } + + #[test] + fn return_c_mul_row() { + if $cond { + test::return_c_mul_row::<$ker, $tc, $ti>() + } + } + + #[test] + fn return_c_sub_row() { + if $cond { + test::return_c_sub_row::<$ker, $tc, $ti>() + } + } + + #[test] + fn return_c_subf_row() { + if $cond { + test::return_c_subf_row::<$ker, $tc, $ti>() + } + } + + #[test] + fn return_c_mul_col() { + if $cond { + test::return_c_mul_col::<$ker, $tc, $ti>() + } + } + + #[test] + fn return_c_add_col() { + if $cond { + test::return_c_add_col::<$ker, $tc, $ti>() + } + } + + #[test] + fn return_c_add_row_col_product() { + if $cond { + test::return_c_add_row_col_product::<$ker, $tc, $ti>() + } + } + + #[test] + fn return_c_scalar_max() { + if $cond { + test::return_c_scalar_max::<$ker, $tc, $ti>() + } + } + + #[test] + fn return_c_scalar_min() { + if $cond { + test::return_c_scalar_min::<$ker, $tc, $ti>() + } + } + + #[test] + fn return_c_scalar_add() { + if $cond { + test::return_c_scalar_add::<$ker, $tc, $ti>() + } + } + + #[test] + fn return_c_scalar_mul() { + if $cond { + test::return_c_scalar_mul::<$ker, $tc, $ti>() + } + } + + #[test] + fn return_c_scalar_sub() { + if $cond { + test::return_c_scalar_sub::<$ker, $tc, $ti>() + } + } + + #[test] + fn return_c_scalar_subf() { + if $cond { + test::return_c_scalar_subf::<$ker, $tc, $ti>() + } + } + + #[test] + fn return_c_plus_d() { + if $cond { + test::return_c_plus_d::<$ker, $tc, $ti>() + } + } + } + }; + } + + #[macro_export] + macro_rules! qmmm_kernel_fuse_tests { + ($cond:expr, $ker:ident, $ta:ty, $tb:ty, $tc:ty, $ti: ty) => { + mod fuseq { + use $crate::frame::mmm::fuse::RoundingPolicy; + #[allow(unused_imports)] + use $crate::frame::mmm::fuse::test; + use $crate::frame::mmm::fuse::test::QScaleProblem; + use $crate::frame::mmm::kernel::MatMatMulKer; + use $crate::generic::Scaler; + use proptest::prelude::*; + use super::super::$ker; + + // FIXME: Scaler should be arbitrary + macro_rules! test_q_scale { + ($policy: ident) => { + paste! { + #[test] + fn []() { + if $cond { + let len = (<$ker>::mr() * <$ker>::nr()) as i64; + let v = (0..len).map(|i| (i - len / 2) as $tc).collect(); + QScaleProblem::<$ker, $tc, $ti>::new(v, Scaler::new(0.5f32, RoundingPolicy::$policy)).run() + } + } + + #[test] + fn []() { + if $cond { + let len = (<$ker>::mr() * <$ker>::nr()) as i64; + let v = (0..len).map(|i| (i - len / 2) as $tc).collect(); + QScaleProblem::<$ker, $tc, $ti>::new(v, Scaler::new(-0.5f32, RoundingPolicy::$policy)).run() + } + } + + #[test] + fn []() { + if $cond { + let len = (<$ker>::mr() * <$ker>::nr()) as i64; + let v = (0..len).map(|i| (i - len / 2) as $tc).collect(); + QScaleProblem::<$ker, $tc, $ti>::new(v, Scaler::new(0.25f32, RoundingPolicy::$policy)).run() + } + } + + #[test] + fn []() { + if $cond { + let len = (<$ker>::mr() * <$ker>::nr()) as i64; + let v = (0..len).map(|i| (i - len / 2) as $tc).collect(); + QScaleProblem::<$ker, $tc, $ti>::new(v, Scaler::new(1f32 / 5., RoundingPolicy::$policy)).run() + } + } + + #[test] + fn []() { + if $cond { + let len = (<$ker>::mr() * <$ker>::nr()) as i64; + let v = (0..len).map(|i| (i - len / 2) as $tc).collect(); + QScaleProblem::<$ker, $tc, $ti>::new(v, Scaler::new(4f32, RoundingPolicy::$policy)).run() + } + } + + #[test] + fn []() { + if $cond { + let len = (<$ker>::mr() * <$ker>::nr()) as i64; + let v = (0..len).map(|i| (i - len / 2) as $tc).collect(); + QScaleProblem::<$ker, $tc, $ti>::new(v, Scaler::new(14., RoundingPolicy::$policy)).run() + } + } + } + } + } + + test_q_scale!(Zero); + test_q_scale!(Away); + test_q_scale!(MinusInf); + test_q_scale!(PlusInf); + test_q_scale!(Even); + test_q_scale!(Odd); + + proptest::proptest! { + #[test] + fn return_q_scale_prop(pb in any::>()) { + if $cond { + pb.run() + } + } + } + + #[test] + fn return_c_scale_bigpot() { + if $cond { + test::return_c_scale_bigpot::<$ker, $tc, $ti>() + } + } + } + }; + } + + pub fn mmm_stride_storage(v: &[T], rsc: usize) -> OutputStoreKer { + OutputStoreKer { + ptr: v.as_ptr() as _, + row_byte_stride: (std::mem::size_of::() * rsc) as isize, + col_byte_stride: std::mem::size_of::() as isize, + item_size: std::mem::size_of::(), + } + } + + use crate::LADatum; + pub fn return_zeros() + where + K: MatMatMulKer, + TC: LADatum, + TI: LADatum + Bounded + PartialEq, + { + let v = vec![TC::max_value(); K::mr() * K::nr()]; + let c = mmm_stride_storage(&v, K::nr()); + let non_linear = tvec![ + FusedKerSpec::Clear, + FusedKerSpec::Store(c), + FusedKerSpec::Done + ]; + let err = K::kernel(&non_linear); + assert_eq!(err, 0); + let expected = vec![TC::zero(); v.len()]; + assert_eq!(v, expected); + } + + pub fn fused_ops(c: &[TC], ops: &[FusedKerSpec], expect: E) + where + K: MatMatMulKer, + TC: Datum + AsPrimitive, + TI: LADatum + AsPrimitive, + E: Fn(usize, usize, TI) -> TI, + { + assert!(c.len() == K::mr() * K::nr()); + let v = c.to_vec(); + let c = mmm_stride_storage(&v, K::nr()); + let mut ops = ops.to_vec(); + ops.insert(0, FusedKerSpec::AddUnicast(c)); + ops.insert(0, FusedKerSpec::Clear); + ops.push(FusedKerSpec::Store(c)); + ops.push(FusedKerSpec::Done); + let expected = (0..v.len()) + .map(|ix| expect(ix / K::nr(), ix % K::nr(), v[ix].as_()).as_()) + .collect::>(); + let err = K::kernel(&ops); + assert_eq!(err, 0); + if v != expected { + println!("found, expected:"); + for m in 0..K::mr() { + for n in 0..K::nr() { + use nu_ansi_term::Color::*; + let f = v[m * K::nr() + n]; + let e = expected[m * K::nr() + n]; + let color = if f != e { Red } else { Green }; + print!("{} ", color.paint(format!("{:4}", f))); + } + print!(" "); + for n in 0..K::nr() { + print!("{:4} ", expected[m * K::nr() + n]); + } + println!(); + } + } + assert_eq!(v, expected); + } + + pub fn return_c(v: &[TC]) + where + K: MatMatMulKer, + TC: LADatum + AsPrimitive, + TI: LADatum + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + fused_ops::(v, &[], |_, _, c| c + 1.as_() - 1.as_()) + } + + pub fn return_c_plus_d() + where + K: MatMatMulKer, + TC: LADatum + AsPrimitive, + TI: LADatum + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + let len = K::mr() * K::nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + let d: Vec = (0..len).map(|f| ((3 * f) % 7).as_()).collect(); + fused_ops::( + &v, + &[FusedKerSpec::AddUnicast(mmm_stride_storage(&d, K::nr()))], + |row, col, c| c + d[row * K::nr() + col], + ); + } + + pub fn return_c_min_row() + where + K: MatMatMulKer, + TC: LADatum + AsPrimitive, + TI: LADatum + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + let len = K::mr() * K::nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + let bias: Vec = (0..K::mr()).map(|f| f.as_()).collect(); + fused_ops::( + &v, + &[FusedKerSpec::PerRowMin(bias.as_ptr())], + |row, _, c| { + if c < bias[row] { c } else { bias[row] } + }, + ) + } + + pub fn return_c_max_row() + where + K: MatMatMulKer, + TC: LADatum + AsPrimitive, + TI: LADatum + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + let len = K::mr() * K::nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + let bias: Vec = (0..K::mr()).map(|f| f.as_()).collect(); + fused_ops::( + &v, + &[FusedKerSpec::PerRowMax(bias.as_ptr())], + |row, _, c| { + if c > bias[row] { c } else { bias[row] } + }, + ) + } + + pub fn return_c_add_row() + where + K: MatMatMulKer, + TC: LADatum + AsPrimitive, + TI: LADatum + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + let len = K::mr() * K::nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + let bias: Vec = (0..K::mr()).map(|f| f.as_()).collect(); + fused_ops::( + &v, + &[FusedKerSpec::PerRowAdd(bias.as_ptr())], + |row, _, c| c + bias[row], + ) + } + + pub fn return_c_mul_row() + where + K: MatMatMulKer, + TC: LADatum + AsPrimitive, + TI: LADatum + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + let len = K::mr() * K::nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + let bias: Vec = (0..K::mr()).map(|f| f.as_()).collect(); + fused_ops::( + &v, + &[FusedKerSpec::PerRowMul(bias.as_ptr())], + |row, _, c| c * bias[row], + ) + } + + pub fn return_c_sub_row() + where + K: MatMatMulKer, + TC: LADatum + AsPrimitive, + TI: LADatum + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + let len = K::mr() * K::nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + let bias: Vec = (0..K::mr()).map(|f| f.as_()).collect(); + fused_ops::( + &v, + &[FusedKerSpec::PerRowSub(bias.as_ptr())], + |row, _, c| bias[row] - c, + ) + } + + pub fn return_c_subf_row() + where + K: MatMatMulKer, + TC: LADatum + AsPrimitive, + TI: LADatum + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + let len = K::mr() * K::nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + let bias: Vec = (0..K::mr()).map(|f| f.as_()).collect(); + fused_ops::( + &v, + &[FusedKerSpec::PerRowSubF(bias.as_ptr())], + |row, _, c| c - bias[row], + ) + } + + pub fn return_c_add_col() + where + K: MatMatMulKer, + TC: LADatum + AsPrimitive, + TI: LADatum + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + let len = K::mr() * K::nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + let bias: Vec = (0..K::nr()).map(|f| f.as_()).collect(); + fused_ops::( + &v, + &[FusedKerSpec::PerColAdd(bias.as_ptr())], + |_, col, c| c + bias[col], + ) + } + + pub fn return_c_mul_col() + where + K: MatMatMulKer, + TC: LADatum + AsPrimitive, + TI: LADatum + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + let len = K::mr() * K::nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + let bias: Vec = (0..K::nr()).map(|f| f.as_()).collect(); + fused_ops::( + &v, + &[FusedKerSpec::PerColMul(bias.as_ptr())], + |_, col, c| c * bias[col], + ) + } + + pub fn return_c_add_row_col_product() + where + K: MatMatMulKer, + TC: LADatum + AsPrimitive, + TI: LADatum + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + let len = K::mr() * K::nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + let rows: Vec = (0..K::mr()).map(|f| f.as_()).collect(); + let cols: Vec = (0..K::nr()).map(|f| f.as_()).collect(); + fused_ops::( + &v, + &[FusedKerSpec::AddRowColProducts( + rows.as_ptr(), + cols.as_ptr(), + )], + |row, col, c| c + cols[col] * rows[row], + ) + } + + pub fn return_c_scalar_min() + where + K: MatMatMulKer, + TC: LADatum + AsPrimitive, + TI: LADatum + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + let len = K::mr() * K::nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + fused_ops::(&v, &[FusedKerSpec::ScalarMin(5.as_())], |_, _, c| { + if c > 5.as_() { 5.as_() } else { c } + }) + } + + pub fn return_c_scalar_max() + where + K: MatMatMulKer, + TC: LADatum + AsPrimitive, + TI: LADatum + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + let len = K::mr() * K::nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + fused_ops::(&v, &[FusedKerSpec::ScalarMax(5.as_())], |_, _, c| { + if c < 5.as_() { 5.as_() } else { c } + }) + } + + pub fn return_c_scalar_add() + where + K: MatMatMulKer, + TC: LADatum + AsPrimitive, + TI: LADatum + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + let len = K::mr() * K::nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + fused_ops::(&v, &[FusedKerSpec::ScalarAdd(5.as_())], |_, _, c| { + c + 5.as_() + }) + } + + pub fn return_c_scalar_mul() + where + K: MatMatMulKer, + TC: LADatum + AsPrimitive, + TI: LADatum + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + let len = K::mr() * K::nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + fused_ops::(&v, &[FusedKerSpec::ScalarMul(5.as_())], |_, _, c| { + c * 5.as_() + }) + } + + pub fn return_c_scalar_sub() + where + K: MatMatMulKer, + TC: LADatum + AsPrimitive, + TI: LADatum + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + let len = K::mr() * K::nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + let five: TI = 5.as_(); + fused_ops::(&v, &[FusedKerSpec::ScalarSub(5.as_())], |_, _, c| five - c) + } + + pub fn return_c_scalar_subf() + where + K: MatMatMulKer, + TC: LADatum + AsPrimitive, + TI: LADatum + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + let len = K::mr() * K::nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + let five: TI = 5.as_(); + fused_ops::(&v, &[FusedKerSpec::ScalarSubF(5.as_())], |_, _, c| c - five) + } + + pub fn return_c_scale_bigpot() + where + K: MatMatMulKer, + TC: LADatum + AsPrimitive, + TI: LADatum + AsPrimitive + ScaleShiftAndRound, + isize: AsPrimitive + AsPrimitive, + { + let len = K::mr() * K::nr(); + let v: Vec = (-(len as isize) / 2..).take(len).map(|f| f.as_()).collect(); + fused_ops::(&v, &[FusedKerSpec::ShiftLeft(1)], |_, _, c| c.q_shl(1)) + } + + #[derive(Debug, new)] + pub struct QScaleProblem + where + K: MatMatMulKer, + TC: LADatum, + TI: LADatum + AsPrimitive, + i64: AsPrimitive, + { + pub c: Vec, + pub scaler: Scaler, + pub boo: std::marker::PhantomData<(K, TC, TI)>, + } + + impl Arbitrary for QScaleProblem + where + K: MatMatMulKer, + TC: LADatum + Arbitrary, + TI: LADatum + AsPrimitive, + i64: AsPrimitive, + { + type Parameters = (); + type Strategy = BoxedStrategy; + fn arbitrary_with(_p: ()) -> Self::Strategy { + use RoundingPolicy::*; + let len = K::mr() * K::nr(); + ( + proptest::collection::vec((-20i64..20).prop_map(|i| i.as_()), len..=len), + -5i32..5, + prop_oneof!(Just(1f32), 0f32..1f32), + proptest::prop_oneof![ + Just(Zero), + Just(Away), + Just(PlusInf), + Just(MinusInf), + Just(Odd), + Just(Even) + ], + ) + .prop_map(|(c, scale_pot, scale_mult, policy)| QScaleProblem { + c, + scaler: Scaler::new(scale_mult * 2f32.powi(scale_pot), policy), + boo: std::marker::PhantomData, + }) + .boxed() + } + } + + impl QScaleProblem + where + K: MatMatMulKer, + TC: LADatum + AsPrimitive, + TI: LADatum + AsPrimitive + ScaleShiftAndRound + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + i64: AsPrimitive, + { + pub fn run(&self) { + if let FusedSpec::QScale(shift, policy, mult) = self.scaler.as_fused_spec() { + fused_ops::( + &self.c, + &[FusedKerSpec::QScale(shift, policy, mult)], + |_, _, c| c.q_scale(self.scaler), + ) + } else if let FusedSpec::RoundingShiftRight(shift, policy) = self.scaler.as_fused_spec() + { + fused_ops::( + &self.c, + &[FusedKerSpec::RoundingShiftRight(shift, policy)], + |_, _, c| c.q_shr(shift, policy), + ) + } else if let FusedSpec::ShiftLeft(shift) = self.scaler.as_fused_spec() { + fused_ops::(&self.c, &[FusedKerSpec::ShiftLeft(shift)], |_, _, c| { + c.q_shl(shift) + }) + } else { + unreachable!() + } + } + } + + pub fn tile() -> BoxedStrategy> + where + K: MatMatMulKer, + TC: LADatum, + TI: LADatum + AsPrimitive, + i8: AsPrimitive, + { + let len = K::mr() * K::nr(); + proptest::collection::vec(any::().prop_map(|c| c.as_()), len..=len).boxed() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/input_store.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/input_store.rs new file mode 100644 index 0000000..5b4dc4e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/input_store.rs @@ -0,0 +1,184 @@ +use std::alloc::Layout; +use std::fmt; +use std::ops::Range; +use tract_data::internal::DynHash; +use tract_data::internal::*; + +use crate::frame::Packer; + +pub trait VirtualInputSpec: DynHash + dyn_clone::DynClone + std::fmt::Debug + Sync + Send { + fn wrap(&self, view: &TensorView) -> Box; +} +dyn_clone::clone_trait_object!(VirtualInputSpec); + +pub trait VirtualInput: dyn_clone::DynClone + std::fmt::Debug + Sync + Send { + fn input(&self, packer: &Packer, packed_output: *mut u8, k: Range, mn: Range); +} +dyn_clone::clone_trait_object!(VirtualInput); + +impl std::hash::Hash for Box { + fn hash(&self, state: &mut H) { + use std::any::Any; + std::hash::Hash::hash(&self.type_id(), state); + self.dyn_hash(state) + } +} + +#[derive(Clone, Debug, Hash)] +pub enum InputStoreSpec { + Prepacked(PackedStoreSpec), + LatePacking { + packer: Packer, + k_axis: usize, + mn_axis: usize, + }, + VirtualPacking { + packer: Packer, + func: Box, + k: usize, + }, +} + +#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] +pub struct PackedStoreSpec { + pub(crate) panel_bytes: usize, +} + +impl InputStoreSpec { + #[inline] + pub unsafe fn wrap(&self, tensor: &TensorView) -> TractResult { + use InputStore::*; + use InputStoreSpec as S; + match self { + S::Prepacked(PackedStoreSpec { panel_bytes }) => Ok(Packed(PackedStore { + ptr: tensor.as_ptr_unchecked::() as _, + panel_bytes: *panel_bytes as isize, + })), + S::LatePacking { + packer, + k_axis, + mn_axis, + } => Ok(InputStore::LatePacking { + packer: packer.clone(), + ptr: tensor.as_ptr_unchecked::() as _, + dt: tensor.datum_type(), + k: tensor.shape()[*k_axis], + mn: tensor.shape()[*mn_axis], + k_stride: tensor.strides()[*k_axis], + mn_stride: tensor.strides()[*mn_axis], + }), + S::VirtualPacking { packer, func, k } => Ok(InputStore::VirtualPacking { + packer: packer.clone(), + input: func.wrap(tensor), + k: *k, + dt: tensor.datum_type(), + }), + } + } +} + +impl fmt::Display for InputStoreSpec { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + match self { + InputStoreSpec::Prepacked { .. } => write!(fmt, "Packed"), + InputStoreSpec::LatePacking { .. } => write!(fmt, "LatePacking"), + InputStoreSpec::VirtualPacking { .. } => write!(fmt, "VirtualPacking"), + } + } +} + +impl PackedStoreSpec { + #[inline] + pub unsafe fn wrap(&self, tensor: &TensorView) -> PackedStore { + PackedStore { + ptr: tensor.as_ptr_unchecked::() as _, + panel_bytes: self.panel_bytes as isize, + } + } +} + +#[derive(Clone, Debug)] +pub enum InputStore { + Packed(PackedStore), + LatePacking { + packer: Packer, + ptr: *const u8, + dt: DatumType, + k: usize, + mn: usize, + k_stride: isize, + mn_stride: isize, + }, + VirtualPacking { + packer: Packer, + input: Box, + k: usize, + dt: DatumType, // TODO discard me ? + }, +} + +#[derive(Clone, Copy, Debug)] +pub struct PackedStore { + ptr: *const u8, + panel_bytes: isize, +} + +impl InputStore { + pub(super) unsafe fn scratch_panel_buffer_layout(&self) -> Option { + match self { + InputStore::Packed(_) => None, + InputStore::LatePacking { packer, dt, k, .. } + | InputStore::VirtualPacking { packer, dt, k, .. } => { + let size = packer.single_panel_len(*k) * dt.size_of(); + let align = packer.alignment(); + Some(Layout::from_size_align_unchecked(size, align)) + } + } + } + + #[inline] + pub(super) unsafe fn panel_b(&self, i: usize, buffer: Option<*const u8>) -> *const u8 { + match self { + InputStore::Packed(packed) => packed.panel(i), + InputStore::LatePacking { + packer, + ptr, + dt, + k, + mn, + mn_stride, + k_stride, + } => { + dispatch_copy!(Packer::pack_t(dt)( + packer, + buffer.unwrap() as _, + *ptr as _, + *mn, + *k_stride, + *mn_stride, + 0..*k, + packer.r * i..packer.r * (i + 1) + )); + buffer.unwrap() + } + InputStore::VirtualPacking { + packer, input, k, .. + } => { + input.input( + packer, + buffer.unwrap() as _, + 0..*k, + packer.r * i..packer.r * (i + 1), + ); + buffer.unwrap() + } + } + } +} + +impl PackedStore { + #[inline] + pub(super) unsafe fn panel(&self, i: usize) -> *const u8 { + self.ptr.offset(self.panel_bytes * i as isize) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/kernel.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/kernel.rs new file mode 100644 index 0000000..4153740 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/kernel.rs @@ -0,0 +1,399 @@ +use std::fmt::Debug; + +use crate::LADatum; +use crate::frame::mmm::FusedKerSpec; + +use super::{MatMatMul, MatMatMulImpl}; + +pub trait MatMatMulKer: Copy + Clone + Debug + Send + Sync + 'static +where + TI: LADatum, +{ + fn name() -> &'static str; + fn kernel(op: &[FusedKerSpec]) -> isize; + fn mr() -> usize; + fn nr() -> usize; + fn alignment_bytes_packed_a() -> usize; + fn end_padding_packed_a() -> usize; + fn alignment_bytes_packed_b() -> usize; + fn end_padding_packed_b() -> usize; + + #[allow(unused_variables)] + fn prefetch(ptr: *const u8, len: usize) {} + + fn mmm() -> Box { + Box::>::default() + } +} + +#[macro_export] +macro_rules! test_mmm_kernel_f16 { + ($k: ident, $cond: expr) => { + paste! { + #[cfg(test)] + #[allow(non_snake_case)] + mod [] { + mmm_kernel_tests!($cond, $k, f16, f16, f16, f16); + mmm_frame_tests!($cond, $k, f16, f16, f16, f16); + mmm_kernel_fuse_tests!($cond, $k, f16, f16); + } + } + }; +} + +#[macro_export] +macro_rules! test_mmm_kernel_f32 { + ($k: ident, $cond: expr) => { + paste! { + #[cfg(test)] + #[allow(non_snake_case)] + mod [] { + mmm_kernel_tests!($cond, $k, f32, f32, f32, f32); + mmm_frame_tests!($cond, $k, f32, f32, f32, f32); + mmm_kernel_fuse_tests!($cond, $k, f32, f32); + //qmmm_kernel_fuse_tests!($cond, $k, f32, f32, f32, f32); + } + } + }; +} + +#[macro_export] +macro_rules! test_mmm_kernel_f64 { + ($k: ident, $cond: expr) => { + paste! { + #[cfg(test)] + #[allow(non_snake_case)] + mod [] { + mmm_kernel_tests!($cond, $k, f64, f64, f64, f64); + mmm_frame_tests!($cond, $k, f64, f64, f64, f64); + mmm_kernel_fuse_tests!($cond, $k, f64, f64); + //qmmm_kernel_fuse_tests!($cond, $k, f64, f64, f64, f64); + } + } + }; +} + +#[macro_export] +macro_rules! test_mmm_kernel_i32 { + ($k: ident, $cond: expr) => { + paste! { + #[cfg(test)] + #[allow(non_snake_case)] + mod [] { + mmm_kernel_tests!($cond, $k, i8, i8, i8, i32); + mmm_kernel_fuse_tests!($cond, $k, i8, i32); + mmm_frame_tests!($cond, $k, i8, i8, i8, i32); + } + #[cfg(test)] + mod [] { + qmmm_kernel_fuse_tests!($cond, $k, i8, i8, i8, i32); + } + #[cfg(test)] + mod [] { + qmmm_kernel_fuse_tests!($cond, $k, i8, i8, i32, i32); + } + } + }; +} + +#[cfg(test)] +#[macro_use] +pub mod test { + use super::*; + use crate::frame::mmm::OutputStoreKer; + use num_traits::{AsPrimitive, One, Zero}; + use proptest::collection::vec; + use proptest::prelude::*; + use std::fmt; + use std::marker::PhantomData; + use tract_data::internal::*; + + #[macro_export] + macro_rules! mmm_kernel_tests { + ($cond:expr, $ker:ident, $ta:ty, $tb:ty, $tc:ty, $ti: ty) => { + mod kernel { + use super::super::$ker; + use num_traits::Zero; + use proptest::prelude::*; + #[allow(unused_imports)] + use tract_data::prelude::f16; + #[allow(unused_imports)] + use $crate::frame::mmm::kernel::test; + use $crate::frame::mmm::kernel::test::PackedPackedProblem; + use $crate::frame::mmm::MatMatMulKer; + + proptest::proptest! { + #[test] + fn packed_packed_prop(pb in any::>()) { + if $cond { + prop_assert_eq!(pb.run(), pb.reference()) + } + } + } + + #[test] + fn packed_packed_1() { + if $cond { + test::packed_packed::<$ker, $ta, $tb, $tc, $ti>(1) + } + } + + #[test] + fn packed_packed_2() { + if $cond { + test::packed_packed::<$ker, $ta, $tb, $tc, $ti>(2) + } + } + + #[test] + fn packed_packed_13() { + if $cond { + test::packed_packed::<$ker, $ta, $tb, $tc, $ti>(13) + } + } + + #[test] + fn packed_packed_empty() { + if $cond { + let pb = PackedPackedProblem::<$ker, $ta, $tb, $tc, $ti>::new( + 0, + vec!(<$ta>::zero(); 0), + vec!(<$tb>::zero(); 0), + false, + false); + assert_eq!(pb.run(), pb.reference()) + } + } + + #[test] + fn packed_packed_bug_1() { + if $cond { + let pb = PackedPackedProblem::<$ker, $ta, $tb, $tc, $ti>::new( + 1, + vec!(<$ta>::zero(); <$ker>::mr()), + vec!(<$tb>::zero(); <$ker>::nr()), + true, + true); + assert_eq!(pb.run(), pb.reference()) + } + } + + #[test] + fn packed_vec_k1() { + if $cond { + test::packed_vec::<$ker, $ta, $tb, $tc, $ti>(1) + } + } + + #[test] + fn packed_vec_k2() { + if $cond { + test::packed_vec::<$ker, $ta, $tb, $tc, $ti>(2) + } + } + + #[test] + fn packed_vec_k4() { + if $cond { + test::packed_vec::<$ker, $ta, $tb, $tc, $ti>(4) + } + } + + #[test] + fn packed_vec_k13() { + if $cond { + test::packed_vec::<$ker, $ta, $tb, $tc, $ti>(13) + } + } + } + }; + } + + #[derive(Debug, new)] + pub struct PackedPackedProblem + where + K: MatMatMulKer, + TA: 'static + Debug + AsPrimitive, + TB: 'static + Debug + AsPrimitive, + TC: Copy + PartialEq + 'static + Debug, + TI: LADatum + fmt::Display + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + pub k: usize, + pub a: Vec, + pub b: Vec, + pub trans_c: bool, + pub add_one: bool, + pub _phantom: PhantomData<(K, TC, TI)>, + } + + impl Arbitrary for PackedPackedProblem + where + K: MatMatMulKer, + TA: 'static + Debug + AsPrimitive, + TB: 'static + Debug + AsPrimitive, + TC: Copy + PartialEq + 'static + Debug, + TI: LADatum + fmt::Display + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_: ()) -> Self::Strategy { + (0usize..20, any::(), any::()) + .prop_flat_map(|(k, trans_c, add_one)| { + let m = k * K::mr(); + let n = k * K::nr(); + let a = (0usize..10).prop_map(|x| x.as_()); + let b = (0usize..10).prop_map(|x| x.as_()); + ( + Just(k), + Just(trans_c), + Just(add_one), + vec(a, m..=m), + vec(b, n..=n), + ) + }) + .prop_map(|(k, trans_c, add_one, a, b)| Self { + k, + a, + b, + trans_c, + add_one, + _phantom: PhantomData, + }) + .boxed() + } + } + + impl PackedPackedProblem + where + K: MatMatMulKer, + TA: 'static + Debug + AsPrimitive + Datum, + TB: 'static + Debug + AsPrimitive + Datum, + TC: Copy + Zero + PartialEq + 'static + Debug, + TI: LADatum + fmt::Display + AsPrimitive, + usize: AsPrimitive + AsPrimitive, + { + pub fn reference(&self) -> Vec { + let init = if self.add_one { TI::one() } else { TI::zero() }; + let mut vi = vec![init; K::mr() * K::nr()]; + let mr = K::mr(); + let nr = K::nr(); + for m in 0..mr { + for n in 0..nr { + for k in 0..self.k { + let a: TI = self.a[m + mr * k].as_(); + let b: TI = self.b[n + nr * k].as_(); + let offset = if self.trans_c { m + n * mr } else { n + m * nr }; + vi[offset] += a * b; + } + } + } + vi.into_iter().map(|ti| ti.as_()).collect() + } + + pub fn run(&self) -> Vec { + unsafe { + let a = self + .a + .iter() + .cloned() + .chain(vec![0.as_(); K::end_padding_packed_a() * K::mr()]) + .collect::>(); + let pa = Tensor::from_slice_align(&a, K::alignment_bytes_packed_a()).unwrap(); + let b = self + .b + .iter() + .cloned() + .chain(vec![0.as_(); K::end_padding_packed_b() * K::nr()]) + .collect::>(); + let pb = Tensor::from_slice_align(&b, K::alignment_bytes_packed_b()).unwrap(); + let mut v = vec![TC::zero(); K::mr() * K::nr()]; + let c = if self.trans_c { + mmm_stride_storage(&mut v, 1, K::mr()) + } else { + mmm_stride_storage(&mut v, K::nr(), 1) + }; + let b_store = pb.as_ptr_unchecked::() as _; + + let mut non_linear_ops = tvec!(FusedKerSpec::AddMatMul { + k: self.k, + pa: pa.as_ptr_unchecked::() as _, + pb: b_store, + cpu_variant: 0, + }); + if self.add_one { + non_linear_ops.push(FusedKerSpec::ScalarAdd(TI::one())); + } + non_linear_ops.push(FusedKerSpec::Store(c)); + non_linear_ops.push(FusedKerSpec::Done); + non_linear_ops.insert(0, FusedKerSpec::Clear); + let err = K::kernel(&non_linear_ops); + assert_eq!(err, 0); + v + } + } + } + + pub fn packed_packed(k: usize) + where + K: MatMatMulKer, + TA: Copy + One + Datum + AsPrimitive, + TB: Copy + One + Datum + AsPrimitive, + TC: Copy + PartialEq + Zero + 'static + Debug, + TI: LADatum + AsPrimitive, + usize: AsPrimitive + AsPrimitive + AsPrimitive, + { + let a = vec![TA::one(); K::mr() * k]; + let b = vec![TB::one(); K::nr() * k]; + let pb = PackedPackedProblem::::new(k, a, b, false, false); + assert_eq!(pb.run(), pb.reference()) + } + + pub fn mmm_stride_storage(v: &mut [T], rsc: usize, csc: usize) -> OutputStoreKer { + OutputStoreKer { + ptr: v.as_mut_ptr() as _, + row_byte_stride: (std::mem::size_of::() * rsc) as isize, + col_byte_stride: (std::mem::size_of::() * csc) as isize, + item_size: std::mem::size_of::(), + } + } + + pub fn packed_vec(k: usize) + where + K: MatMatMulKer, + TA: Copy + One + AsPrimitive + Debug + Datum, + TB: Copy + One + AsPrimitive + Debug + Datum, + TC: Copy + PartialEq + Zero + 'static + Debug, + TI: LADatum + AsPrimitive, + usize: AsPrimitive, + { + let pa = unsafe { + Tensor::from_slice_align( + &vec![TA::one(); K::mr() * (k + K::end_padding_packed_a())], + K::alignment_bytes_packed_a(), + ) + .unwrap() + }; + let b = vec![TB::one(); (k + 1) * K::nr()]; + let mut c: Vec = vec![TC::zero(); K::mr() * K::nr()]; + let tile = mmm_stride_storage(&mut c, 1, 0); + let b_store = b.as_ptr() as _; + let non_linear_ops = tvec!( + FusedKerSpec::Clear, + FusedKerSpec::AddMatMul { + pa: unsafe { pa.as_ptr_unchecked::() as _ }, + pb: b_store, + k, + cpu_variant: 0, + }, + FusedKerSpec::Store(tile), + FusedKerSpec::Done + ); + let err = K::kernel(&non_linear_ops); + assert_eq!(err, 0); + let expected = vec![k.as_(); K::mr()]; + assert_eq!(c[..K::mr()], expected); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/mmm.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/mmm.rs new file mode 100644 index 0000000..e297bbb --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/mmm.rs @@ -0,0 +1,362 @@ +use super::ScratchSpaceFusedNonLinear; +use super::*; +use crate::LADatum; +use crate::frame::Packer; +use anyhow::Context; +use std::fmt; +use std::fmt::Debug; +use std::marker::PhantomData; +use tract_data::anyhow; +use tract_data::internal::*; + +pub trait MatMatMul: + Debug + fmt::Display + dyn_clone::DynClone + Send + Sync + std::any::Any +{ + fn kernel_name(&self) -> &'static str; + fn mr(&self) -> usize; + fn nr(&self) -> usize; + + fn a_pack(&self) -> Packer; + fn b_pack(&self) -> Packer; + + fn internal_type(&self) -> DatumType; + + unsafe fn a_packed(&self, item_size: usize, k: usize) -> PackedStoreSpec; + + unsafe fn b_packed(&self, item_size: usize, k: usize) -> InputStoreSpec; + unsafe fn b_late_packing(&self) -> InputStoreSpec { + self.b_late_packing_with_axes(0, 1) + } + unsafe fn b_late_packing_with_axes(&self, k_axis: usize, n_axis: usize) -> InputStoreSpec; + unsafe fn b_virtual_input(&self, func: Box, k: usize) -> InputStoreSpec; + + unsafe fn c_view(&self, m_axis: usize, n_axis: usize) -> OutputStoreSpec; + unsafe fn c_from_data_and_strides( + &self, + item_size: usize, + m: usize, + n: usize, + row_stride: isize, + col_stride: isize, + ) -> OutputStoreSpec; + + unsafe fn run(&self, m: usize, n: usize, non_linear: &[FusedSpec]) -> anyhow::Result<()> { + let mut scratch = self.allocate_scratch_space(); + self.run_with_scratch_space(m, n, &mut *scratch, non_linear) + } + + unsafe fn allocate_scratch_space(&self) -> Box; + unsafe fn can_use_scratch_space(&self, scratch: &dyn ScratchSpace) -> bool; + unsafe fn run_with_scratch_space( + &self, + m: usize, + n: usize, + scratch: &mut dyn ScratchSpace, + non_linear: &[FusedSpec], + ) -> anyhow::Result<()>; + + unsafe fn run_with_scratch_space_vec( + &self, + m: usize, + scratch: &mut dyn ScratchSpace, + non_linear: &[FusedSpec], + ) -> anyhow::Result<()>; + + unsafe fn run_with_scratch_space_col_outer( + &self, + m: usize, + n: usize, + scratch: &mut dyn ScratchSpace, + non_linear: &[FusedSpec], + ) -> anyhow::Result<()>; +} + +dyn_clone::clone_trait_object!(MatMatMul); + +impl PartialEq for Box { + fn eq(&self, other: &Box) -> bool { + self.as_ref().type_id() == other.as_ref().type_id() + } +} + +impl std::hash::Hash for Box { + fn hash(&self, state: &mut H) { + self.as_ref().type_id().hash(state) + } +} + +#[derive(Clone)] +pub struct MatMatMulImpl +where + TI: LADatum, + K: MatMatMulKer + 'static, +{ + phantom: PhantomData<(K, TI)>, +} + +unsafe impl Send for MatMatMulImpl +where + TI: LADatum, + K: MatMatMulKer + 'static, +{ +} + +unsafe impl Sync for MatMatMulImpl +where + TI: LADatum, + K: MatMatMulKer + 'static, +{ +} + +impl Default for MatMatMulImpl +where + TI: LADatum, + K: MatMatMulKer + 'static, +{ + fn default() -> Self { + MatMatMulImpl { + phantom: PhantomData, + } + } +} + +impl fmt::Debug for MatMatMulImpl +where + TI: LADatum, + K: MatMatMulKer + 'static, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "MMM ({} {}x{})", K::name(), K::mr(), K::nr()) + } +} + +impl MatMatMul for MatMatMulImpl +where + TI: LADatum, + K: MatMatMulKer + 'static, +{ + fn kernel_name(&self) -> &'static str { + K::name() + } + + fn mr(&self) -> usize { + K::mr() + } + + fn nr(&self) -> usize { + K::nr() + } + + fn a_pack(&self) -> Packer { + Packer::new( + K::mr(), + K::alignment_bytes_packed_a(), + K::end_padding_packed_a(), + ) + } + + fn b_pack(&self) -> Packer { + Packer::new( + K::nr(), + K::alignment_bytes_packed_b(), + K::end_padding_packed_b(), + ) + } + + fn internal_type(&self) -> DatumType { + TI::datum_type() + } + + unsafe fn a_packed(&self, item_size: usize, k: usize) -> PackedStoreSpec { + PackedStoreSpec { + panel_bytes: (k * K::mr() * item_size), + } + } + + unsafe fn b_packed(&self, item_size: usize, k: usize) -> InputStoreSpec { + let panel_bytes = k * K::nr() * item_size; + InputStoreSpec::Prepacked(PackedStoreSpec { panel_bytes }) + } + + unsafe fn b_late_packing_with_axes(&self, k_axis: usize, n_axis: usize) -> InputStoreSpec { + InputStoreSpec::LatePacking { + packer: self.b_pack(), + k_axis, + mn_axis: n_axis, + } + } + + unsafe fn b_virtual_input(&self, func: Box, k: usize) -> InputStoreSpec { + InputStoreSpec::VirtualPacking { + packer: self.b_pack(), + func, + k, + } + } + + unsafe fn c_view(&self, m_axis: usize, n_axis: usize) -> OutputStoreSpec { + OutputStoreSpec::View { + m_axis, + n_axis, + mr: K::mr(), + nr: K::nr(), + } + } + + unsafe fn c_from_data_and_strides( + &self, + item_size: usize, + m: usize, + n: usize, + row_stride: isize, + col_stride: isize, + ) -> OutputStoreSpec { + OutputStoreSpec::Strides { + row_byte_stride: row_stride * item_size as isize, + col_byte_stride: col_stride * item_size as isize, + mr: K::mr(), + nr: K::nr(), + m, + n, + } + } + + unsafe fn allocate_scratch_space(&self) -> Box { + Box::>::default() + } + + unsafe fn can_use_scratch_space(&self, scratch: &dyn ScratchSpace) -> bool { + scratch + .downcast_ref::>() + .is_some() + } + + unsafe fn run_with_scratch_space_vec( + &self, + m: usize, + scratch: &mut dyn ScratchSpace, + non_linear: &[FusedSpec], + ) -> anyhow::Result<()> { + let mr = K::mr(); + let scratch = scratch + .downcast_mut::>() + .context("Wrong scratch space type")?; + scratch.prepare::(non_linear)?; + for ia in 0..m / mr { + scratch.for_valid_tile::(non_linear, ia, 0); + let err = K::kernel(scratch.uspecs()); + debug_assert_eq!(err, 0, "Kernel return error {err}"); + } + if m % mr != 0 { + scratch.for_border_tile::(non_linear, m / mr, 0); + let err = K::kernel(scratch.uspecs()); + debug_assert_eq!(err, 0, "Kernel return error {err}"); + scratch.postprocess_tile::(non_linear, m / mr, 0, m % mr, 1); + } + Ok(()) + } + + unsafe fn run_with_scratch_space_col_outer( + &self, + m: usize, + n: usize, + scratch: &mut dyn ScratchSpace, + non_linear: &[FusedSpec], + ) -> anyhow::Result<()> { + let mr = K::mr(); + let nr = K::nr(); + let scratch = scratch + .downcast_mut::>() + .context("Wrong scratch space type")?; + scratch.prepare::(non_linear)?; + for ib in 0..n / nr { + for ia in 0..m / mr { + scratch.for_valid_tile::(non_linear, ia, ib); + let err = K::kernel(scratch.uspecs()); + debug_assert_eq!(err, 0, "Kernel return error {err}"); + } + if m % mr != 0 { + scratch.for_border_tile::(non_linear, m / mr, ib); + let err = K::kernel(scratch.uspecs()); + debug_assert_eq!(err, 0, "Kernel return error {err}"); + scratch.postprocess_tile::(non_linear, m / mr, ib, m % mr, nr); + } + } + if n % nr != 0 { + for ia in 0..m / mr { + scratch.for_border_tile::(non_linear, ia, n / nr); + let err = K::kernel(scratch.uspecs()); + debug_assert_eq!(err, 0, "Kernel return error {err}"); + scratch.postprocess_tile::(non_linear, ia, n / nr, mr, n % nr); + } + if m % mr != 0 { + scratch.for_border_tile::(non_linear, m / mr, n / nr); + let err = K::kernel(scratch.uspecs()); + debug_assert_eq!(err, 0, "Kernel return error {err}"); + scratch.postprocess_tile::(non_linear, m / mr, n / nr, m % mr, n % nr); + } + } + Ok(()) + } + + unsafe fn run_with_scratch_space( + &self, + m: usize, + n: usize, + scratch: &mut dyn ScratchSpace, + non_linear: &[FusedSpec], + ) -> anyhow::Result<()> { + let mr = K::mr(); + let nr = K::nr(); + if n == 1 && K::nr() == 1 { + return self.run_with_scratch_space_vec(m, scratch, non_linear); + } + if non_linear.iter().any(|f| f.prefer_col_outer()) { + return self.run_with_scratch_space_col_outer(m, n, scratch, non_linear); + } + let scratch = scratch + .downcast_mut::>() + .context("Wrong scratch space type")?; + scratch.prepare::(non_linear)?; + for ia in 0..m / mr { + for ib in 0..n / nr { + scratch.for_valid_tile::(non_linear, ia, ib); + let err = K::kernel(scratch.uspecs()); + debug_assert_eq!(err, 0, "Kernel return error {err}"); + } + } + if m % mr != 0 { + for ib in 0..n / nr { + scratch.for_border_tile::(non_linear, m / mr, ib); + let err = K::kernel(scratch.uspecs()); + debug_assert_eq!(err, 0, "Kernel return error {err}"); + scratch.postprocess_tile::(non_linear, m / mr, ib, m % mr, nr); + } + } + if n % nr != 0 { + for ia in 0..m / mr { + scratch.for_border_tile::(non_linear, ia, n / nr); + let err = K::kernel(scratch.uspecs()); + debug_assert_eq!(err, 0, "Kernel return error {err}"); + scratch.postprocess_tile::(non_linear, ia, n / nr, mr, n % nr); + } + if m % mr != 0 { + scratch.for_border_tile::(non_linear, m / mr, n / nr); + let err = K::kernel(scratch.uspecs()); + debug_assert_eq!(err, 0, "Kernel return error {err}"); + scratch.postprocess_tile::(non_linear, m / mr, n / nr, m % mr, n % nr); + } + } + Ok(()) + } +} + +impl fmt::Display for MatMatMulImpl +where + TI: LADatum, + K: MatMatMulKer, +{ + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "({} {}x{})", K::name(), K::mr(), K::nr()) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/scratch.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/scratch.rs new file mode 100644 index 0000000..9c378a5 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/scratch.rs @@ -0,0 +1,429 @@ +use std::alloc::Layout; +use std::fmt::Debug; +use tract_data::internal::*; + +use crate::LADatum; + +use super::{BinOp, FusedKerSpec, FusedSpec, MatMatMulKer, OutputStoreKer}; +use downcast_rs::{Downcast, impl_downcast}; +use tract_data::internal::num_integer::Integer; + +pub trait ScratchSpace: Downcast + Send {} +impl_downcast!(ScratchSpace); + +#[derive(Debug)] +pub struct ScratchSpaceFusedNonLinear { + uspecs: Vec>, + layout: Layout, + buffer: *const u8, + loc_dependant: TVec, +} + +impl Default for ScratchSpaceFusedNonLinear { + fn default() -> Self { + ScratchSpaceFusedNonLinear { + uspecs: vec![], + layout: unsafe { Layout::from_size_align_unchecked(0, 1) }, + buffer: std::ptr::null(), + loc_dependant: tvec!(), + } + } +} + +#[derive(Debug, new)] +struct LocDependant { + spec: usize, + uspec: usize, + loc: *const u8, + buffer: Option<*const u8>, +} + +impl ScratchSpace for ScratchSpaceFusedNonLinear {} +unsafe impl Send for ScratchSpaceFusedNonLinear {} + +impl Drop for ScratchSpaceFusedNonLinear { + fn drop(&mut self) { + if !self.buffer.is_null() { + unsafe { + std::alloc::dealloc(self.buffer as _, self.layout); + } + } + } +} + +struct AddMatMulTemp(*const u8, usize); + +impl ScratchSpaceFusedNonLinear { + pub unsafe fn prepare>(&mut self, specs: &[FusedSpec]) -> TractResult<()> { + use FusedKerSpec as FKS; + use FusedSpec as FS; + self.uspecs.clear(); + self.loc_dependant.clear(); + self.uspecs.reserve(specs.len() + 2); + self.uspecs.push(FusedKerSpec::Clear); + let mut offset = 0; + let mut align = std::mem::size_of::<*const ()>(); + fn ld(spec: usize, uspec: usize, loc: *const u8) -> LocDependant { + LocDependant { + spec, + uspec, + loc, + buffer: None, + } + } + // we're cheating here, storing offset as the buf pointer first + for (ix, spec) in specs.iter().enumerate() { + let uspec = match spec { + FS::BinScalar(t, op) => match op { + BinOp::Min => FKS::ScalarMin(*t.to_scalar()?), + BinOp::Max => FKS::ScalarMax(*t.to_scalar()?), + BinOp::Mul => FKS::ScalarMul(*t.to_scalar()?), + BinOp::Add => FKS::ScalarAdd(*t.to_scalar()?), + BinOp::Sub => FKS::ScalarSub(*t.to_scalar()?), + BinOp::SubF => FKS::ScalarSubF(*t.to_scalar()?), + }, + FS::ShiftLeft(s) => FKS::ShiftLeft(*s), + FS::RoundingShiftRight(s, rp) => FKS::RoundingShiftRight(*s, *rp), + FS::QScale(s, rp, m) => FKS::QScale(*s, *rp, *m), + FS::BinPerRow(_, _) => { + self.loc_dependant + .push(ld(ix, self.uspecs.len(), offset as _)); + offset += TI::datum_type().size_of() * K::mr(); + FusedKerSpec::Done + } + FS::BinPerCol(_, _) => { + self.loc_dependant + .push(ld(ix, self.uspecs.len(), offset as _)); + offset += TI::datum_type().size_of() * K::nr(); + FusedKerSpec::Done + } + FS::AddRowColProducts(_, _) => { + self.loc_dependant + .push(ld(ix, self.uspecs.len(), offset as _)); + offset += TI::datum_type().size_of() * (K::mr() + K::nr()); + FusedKerSpec::Done + } + FS::Store(_) | FS::AddUnicast(_) => { + self.loc_dependant + .push(ld(ix, self.uspecs.len(), offset as _)); + offset += TI::datum_type().size_of() * K::mr() * K::nr(); + FusedKerSpec::Done + } + FS::AddMatMul { b, .. } => { + let mut ld = ld(ix, self.uspecs.len(), offset as _); + offset += std::mem::size_of::(); + if let Some(tmp) = b.scratch_panel_buffer_layout() { + align = tmp.align().lcm(&align); + offset = Integer::next_multiple_of(&offset, &tmp.align()); + ld.buffer = Some(offset as _); + offset += tmp.size(); + } + self.loc_dependant.push(ld); + FusedKerSpec::Done + } + }; + self.uspecs.push(uspec); + } + self.uspecs.push(FKS::Done); + if offset > self.layout.size() || align > self.layout.align() { + if !self.buffer.is_null() { + std::alloc::dealloc(self.buffer as _, self.layout); + } + self.layout = Layout::from_size_align_unchecked(offset, align); + self.buffer = std::alloc::alloc(self.layout); + assert!(!self.buffer.is_null()); + } + for LocDependant { + loc, buffer, spec, .. + } in &mut self.loc_dependant + { + *loc = self.buffer.offset(*loc as _); + if let Some(b) = buffer { + *b = self.buffer.offset(*b as _); + } + let spec = specs.get_unchecked(*spec); + #[allow(clippy::single_match)] + match spec { + FS::AddMatMul { .. } => { + let scratch = *loc as *mut AddMatMulTemp; + (*scratch).1 = usize::MAX; + } + _ => (), + }; + } + Ok(()) + } + + #[inline(always)] + pub unsafe fn for_valid_tile>( + &mut self, + specs: &[FusedSpec], + down: usize, + right: usize, + ) { + use FusedKerSpec as FKS; + use FusedSpec as FS; + let ScratchSpaceFusedNonLinear { + uspecs, + loc_dependant, + .. + } = self; + debug_assert!(specs.len() + 2 == uspecs.len()); + for LocDependant { + spec, + uspec, + loc, + buffer, + } in loc_dependant.iter_mut() + { + let spec = specs.get_unchecked(*spec); + *uspecs.get_unchecked_mut(*uspec) = match spec { + FS::BinPerRow(v, op) => { + let v = v.as_ptr_unchecked::().add(down * K::mr()); + match op { + BinOp::Min => FKS::PerRowMin(v), + BinOp::Max => FKS::PerRowMax(v), + BinOp::Add => FKS::PerRowAdd(v), + BinOp::Mul => FKS::PerRowMul(v), + BinOp::Sub => FKS::PerRowSub(v), + BinOp::SubF => FKS::PerRowSubF(v), + } + } + FS::BinPerCol(v, op) => { + let v = v.as_ptr_unchecked::().add(right * K::nr()); + match op { + BinOp::Min => FKS::PerColMin(v), + BinOp::Max => FKS::PerColMax(v), + BinOp::Add => FKS::PerColAdd(v), + BinOp::Mul => FKS::PerColMul(v), + BinOp::Sub => FKS::PerColSub(v), + BinOp::SubF => FKS::PerColSubF(v), + } + } + FS::AddRowColProducts(rows, cols) => { + let row_ptr = rows.as_ptr_unchecked::().add(down * K::mr()); + let col_ptr = cols.as_ptr_unchecked::().add(right * K::nr()); + FKS::AddRowColProducts(row_ptr, col_ptr) + } + FS::AddUnicast(store) => FKS::AddUnicast(store.tile_c(down, right)), + FS::Store(c_store) => FKS::Store(c_store.tile_c(down, right)), + FS::AddMatMul { k, a, b } => { + let pa = a.panel(down); + K::prefetch(pa as _, 512); + let scratch = *loc as *mut AddMatMulTemp; + if (*scratch).1 != right { + (*scratch).0 = b.panel_b(right, *buffer); + (*scratch).1 = right; + } + FKS::AddMatMul { + k: *k, + pa, + pb: (*scratch).0, + cpu_variant: 0, + } + } + _ => std::hint::unreachable_unchecked(), + }; + } + } + + #[inline(never)] + pub unsafe fn for_border_tile>( + &mut self, + specs: &[FusedSpec], + down: usize, + right: usize, + ) { + use FusedKerSpec as FKS; + use FusedSpec as FS; + let ScratchSpaceFusedNonLinear { + uspecs, + loc_dependant, + .. + } = self; + debug_assert!(specs.len() + 2 == uspecs.len()); + for LocDependant { + spec, + uspec, + loc, + buffer, + } in loc_dependant.iter_mut() + { + let spec = specs.get_unchecked(*spec); + *uspecs.get_unchecked_mut(*uspec) = match spec { + FS::BinPerRow(v, op) => { + let buf = std::slice::from_raw_parts_mut(*loc as *mut TI, K::mr()); + let have = v.len().saturating_sub(down * K::mr()).min(K::mr()); + let ptr = if have < K::mr() { + if have > 0 { + buf.get_unchecked_mut(..have).copy_from_slice( + v.as_slice_unchecked() + .get_unchecked(down * K::mr()..) + .get_unchecked(..have), + ); + } + if cfg!(debug_assertions) { + buf.get_unchecked_mut(have..) + .iter_mut() + .for_each(|x| *x = TI::zero()); + } + buf.as_ptr() + } else { + v.as_ptr_unchecked::().add(down * K::mr()) + }; + match op { + BinOp::Min => FKS::PerRowMin(ptr), + BinOp::Max => FKS::PerRowMax(ptr), + BinOp::Add => FKS::PerRowAdd(ptr), + BinOp::Mul => FKS::PerRowMul(ptr), + BinOp::Sub => FKS::PerRowSub(ptr), + BinOp::SubF => FKS::PerRowSubF(ptr), + } + } + FS::BinPerCol(v, op) => { + let buf = std::slice::from_raw_parts_mut(*loc as *mut TI, K::nr()); + let have = v.len().saturating_sub(right * K::nr()).min(K::nr()); + let ptr = if have < K::nr() { + if have > 0 { + buf.get_unchecked_mut(..have).copy_from_slice( + v.as_slice_unchecked() + .get_unchecked(right * K::nr()..) + .get_unchecked(..have), + ); + } + if cfg!(debug_assertions) { + buf.get_unchecked_mut(have..) + .iter_mut() + .for_each(|x| *x = TI::zero()); + } + buf.as_ptr() + } else { + v.as_ptr_unchecked::().add(right * K::nr()) + }; + match op { + BinOp::Min => FKS::PerColMin(ptr), + BinOp::Max => FKS::PerColMax(ptr), + BinOp::Add => FKS::PerColAdd(ptr), + BinOp::Mul => FKS::PerColMul(ptr), + BinOp::Sub => FKS::PerColSub(ptr), + BinOp::SubF => FKS::PerColSubF(ptr), + } + } + FS::AddRowColProducts(rows, cols) => { + let r = std::slice::from_raw_parts_mut(*loc as *mut TI, K::mr()); + let have = rows.len() - down * K::mr(); + let row_ptr = if have < K::mr() { + r.get_unchecked_mut(..have).copy_from_slice( + rows.as_slice_unchecked() + .get_unchecked(down * K::mr()..) + .get_unchecked(..have), + ); + if cfg!(debug_assertions) { + r.get_unchecked_mut(have..) + .iter_mut() + .for_each(|x| *x = TI::zero()); + } + r.as_ptr() + } else { + rows.as_ptr_unchecked::().add(down * K::mr()) + }; + let c = std::slice::from_raw_parts_mut((*loc as *mut TI).add(K::mr()), K::nr()); + let have = cols.len() - right * K::nr(); + let col_ptr = if have < K::nr() { + c.get_unchecked_mut(..have).copy_from_slice( + cols.as_slice_unchecked() + .get_unchecked(right * K::nr()..) + .get_unchecked(..have), + ); + if cfg!(debug_assertions) { + r.get_unchecked_mut(have..) + .iter_mut() + .for_each(|x| *x = TI::zero()); + } + c.as_ptr() + } else { + cols.as_ptr_unchecked::().add(right * K::nr()) + }; + FKS::AddRowColProducts(row_ptr, col_ptr) + } + FS::AddUnicast(store) => { + let row_byte_stride = store.row_byte_stride; + let col_byte_stride = store.col_byte_stride; + let tile_offset = row_byte_stride * down as isize * K::mr() as isize + + col_byte_stride * right as isize * K::nr() as isize; + let tile_ptr = store.ptr.offset(tile_offset); + let tmp_d_tile = + std::slice::from_raw_parts_mut(*loc as *mut TI, K::mr() * K::nr()); + let m = (store.m - down * K::mr()).min(K::mr()); + let n = (store.n - right * K::nr()).min(K::nr()); + for r in 0..m as isize { + for c in 0..n as isize { + let inner_offset = c * col_byte_stride + r * row_byte_stride; + if inner_offset + tile_offset + < (store.item_size * store.item_count) as isize + { + *tmp_d_tile.get_unchecked_mut(r as usize + c as usize * K::mr()) = + *(tile_ptr.offset(inner_offset) as *const TI); + } + } + } + FKS::AddUnicast(OutputStoreKer { + ptr: tmp_d_tile.as_ptr() as _, + row_byte_stride: std::mem::size_of::() as isize, + col_byte_stride: (std::mem::size_of::() * K::mr()) as isize, + item_size: std::mem::size_of::(), + }) + } + FS::Store(c_store) => { + let tmpc = OutputStoreKer { + ptr: *loc as _, + item_size: c_store.item_size, + row_byte_stride: c_store.item_size as isize, + col_byte_stride: (c_store.item_size * K::mr()) as isize, + }; + FKS::Store(tmpc) + } + FS::AddMatMul { k, a, b } => { + let pa = a.panel(down); + K::prefetch(pa as _, 512); + let scratch = *loc as *mut AddMatMulTemp; + if (*scratch).1 != right { + (*scratch).0 = b.panel_b(right, *buffer); + (*scratch).1 = right; + } + FKS::AddMatMul { + k: *k, + pa, + pb: (*scratch).0, + cpu_variant: 0, + } + } + _ => std::hint::unreachable_unchecked(), + }; + } + } + + #[inline] + pub fn uspecs(&self) -> &[FusedKerSpec] { + &self.uspecs + } + + pub unsafe fn postprocess_tile>( + &mut self, + specs: &[FusedSpec], + down: usize, + right: usize, + m_remnant: usize, + n_remnant: usize, + ) where + TI: LADatum, + { + for LocDependant { spec, uspec, .. } in self.loc_dependant.iter() { + let spec = specs.get_unchecked(*spec); + let ker_spec = self.uspecs.get_unchecked(*uspec); + if let (FusedSpec::Store(c_store), FusedKerSpec::Store(tmp)) = (spec, ker_spec) { + c_store.set_from_tile(down, right, m_remnant, n_remnant, tmp) + } + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/storage.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/storage.rs new file mode 100644 index 0000000..bd17227 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/storage.rs @@ -0,0 +1,160 @@ +use std::fmt::Debug; +use tract_data::internal::*; + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub enum OutputStoreSpec { + View { + m_axis: usize, + n_axis: usize, + mr: usize, + nr: usize, + }, + Strides { + row_byte_stride: isize, + col_byte_stride: isize, + mr: usize, + nr: usize, + m: usize, + n: usize, + }, +} + +#[derive(Clone, Copy, Debug)] +pub struct OutputStore { + pub(crate) ptr: *mut u8, + pub(crate) row_byte_stride: isize, + pub(crate) col_byte_stride: isize, + pub(crate) panel_row_byte_stride: isize, + pub(crate) panel_col_byte_stride: isize, + pub(crate) item_size: usize, + pub(crate) item_count: usize, + pub(crate) mr: usize, + pub(crate) m: usize, + pub(crate) n: usize, +} + +impl OutputStoreSpec { + #[inline] + pub unsafe fn wrap(&self, tensor: &TensorView) -> OutputStore { + let (mr, nr, row_byte_stride, col_byte_stride) = self.compute_strides(tensor); + let (m, n) = match self { + OutputStoreSpec::View { m_axis, n_axis, .. } => { + (tensor.shape()[*m_axis], tensor.shape()[*n_axis]) + } + OutputStoreSpec::Strides { m, n, .. } => (*m, *n), + }; + OutputStore { + ptr: tensor.as_ptr_unchecked::() as _, + row_byte_stride, + col_byte_stride, + panel_row_byte_stride: row_byte_stride * mr as isize, + panel_col_byte_stride: col_byte_stride * nr as isize, + item_size: tensor.datum_type().size_of(), + mr, + item_count: tensor.len(), + m, + n, + } + } + + #[inline] + unsafe fn compute_strides(&self, tensor: &TensorView) -> (usize, usize, isize, isize) { + let size_of = tensor.datum_type().size_of() as isize; + match self { + OutputStoreSpec::View { + m_axis, + n_axis, + mr, + nr, + .. + } => { + let tensor_strides = tensor.strides(); + let row_item_stride = *tensor_strides.get_unchecked(*m_axis); + let col_item_stride = *tensor_strides.get_unchecked(*n_axis); + let row_byte_stride = row_item_stride * size_of; + let col_byte_stride = col_item_stride * size_of; + (*mr, *nr, row_byte_stride, col_byte_stride) + } + OutputStoreSpec::Strides { + row_byte_stride, + col_byte_stride, + mr, + nr, + .. + } => (*mr, *nr, *row_byte_stride, *col_byte_stride), + } + } +} + +impl OutputStore { + #[inline] + pub(super) unsafe fn tile_c(&self, down: usize, right: usize) -> OutputStoreKer { + let (down, right) = (down as isize, right as isize); + OutputStoreKer { + ptr: self + .ptr + .offset(self.panel_row_byte_stride * down + self.panel_col_byte_stride * right) + as *mut _, + row_byte_stride: self.row_byte_stride, + col_byte_stride: self.col_byte_stride, + item_size: self.item_size, + } + } + + #[inline] + pub fn item_size(&self) -> usize { + self.item_size + } + + #[inline] + pub(super) unsafe fn set_from_tile( + &self, + down: usize, + right: usize, + height: usize, + width: usize, + tile: &OutputStoreKer, + ) { + if self.item_size() == 1 { + self.set_from_tile_t::(down, right, height, width, tile) + } else if self.item_size() == 2 { + self.set_from_tile_t::(down, right, height, width, tile) + } else if self.item_size() == 4 { + self.set_from_tile_t::(down, right, height, width, tile) + } else { + self.set_from_tile_t::(down, right, height, width, tile) + } + } + + #[inline] + unsafe fn set_from_tile_t( + &self, + down: usize, + right: usize, + height: usize, + width: usize, + tile: &OutputStoreKer, + ) { + let tile = tile.ptr as *mut T; + let dst = self.ptr.add( + self.panel_row_byte_stride as usize * down + + self.panel_col_byte_stride as usize * right, + ); + for y in 0..height as isize { + for x in 0..width as isize { + let value = tile.offset(y + x * self.mr as isize); + let dst = dst.offset(y * self.row_byte_stride + x * self.col_byte_stride); + *(dst as *mut T) = *value; + } + } + } +} + +#[repr(C)] +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +pub struct OutputStoreKer { + pub ptr: *mut u8, + pub row_byte_stride: isize, + pub col_byte_stride: isize, + pub item_size: usize, +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/tests.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/tests.rs new file mode 100644 index 0000000..d007a8d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/mmm/tests.rs @@ -0,0 +1,601 @@ +use super::*; +use crate::LADatum; +use num_traits::AsPrimitive; +use proptest::prelude::*; +use std::ops::Neg; +use tract_data::internal::*; + +#[macro_export] +macro_rules! mmm_frame_tests { + ($cond:expr, $ker:ident, $ta:ty, $tb:ty, $tc:ty, $ti:ty) => { + mod frame { + #[allow(unused_imports)] + use $crate::frame::mmm::tests::*; + use tract_data::internal::*; + use super::super::$ker; + + proptest::proptest! { + #[test] + fn mat_mul_prepacked_prop((m, k, n, ref a, ref b) in strat_mat_mat_mul::<$ta, $tb>()) { + if $cond { + test_mat_mat_mul_prep::<$ker, $ta, $tb, $tc, $ti>(m, k, n, &a, &b)? + } + } + + #[test] + fn mat_mul_prepacked_late((m, k, n, ref a, ref b) in strat_mat_mat_mul::<$ta, $tb>()) { + if $cond { + test_mat_mat_mul_late::<$ker, $ta, $tb, $tc, $ti>(m, k, n, &a, &b)? + } + } + + #[test] + fn mat_vec_prepacked_prop((m, k, ref a, ref b) in strat_mat_vec_mul::<$ta, $tb>()) { + if $cond { + test_mat_vec_mul_prep::<$ker, $ta, $tb, $tc, $ti>(m, k, &*a, b)? + } + } + } + + #[test] + fn mat_mul_1() { + if $cond { + let a = tensor2(&[[-3i32, 3, 5, -5], [6, 0, -6, -5], [0, 0, 9, 7]]).cast_to::<$ta>().unwrap().into_owned(); + let b = tensor2(&[[-8i32, 5],[ 5, -3], [5, 7],[ -8, -1]]).cast_to::<$tb>().unwrap().into_owned(); + test_mat_mat_mul_prep::<$ker, $ta, $tb, $tc, $ti>(3, 4, 2, &a, &b).unwrap() + } + } + + #[test] + fn mat_mul_2() { + if $cond { + let a = tensor2(&[[1i32]]).cast_to::<$ta>().unwrap().into_owned(); + let b = tensor2(&[[0i32, 0, 1]]).cast_to::<$tb>().unwrap().into_owned(); + test_mat_mat_mul_prep::<$ker, $ta, $tb, $tc, $ti>(1, 1, 3, &a, &b).unwrap() + } + } + + #[test] + fn mat_mul_3() { + if $cond { + let a = tensor2(&[[-3i32, 3, 5, -5], [6, 0, -6, -5], [0, 0, 9, 7]]).cast_to::<$ta>().unwrap().into_owned(); + let b = tensor2(&[[-8i32, 5],[ 5, -3], [5, 7],[ -8, -1]]).cast_to::<$tb>().unwrap().into_owned(); + test_mat_mat_mul_prep::<$ker, $ta, $tb, $tc, $ti>(3, 4, 2, &a, &b).unwrap() + } + } + + #[test] + fn mat_mul_4() { + if $cond { + let a = tensor2(&[[122, 82]]).cast_to::<$ta>().unwrap().into_owned(); + let b = tensor2(&[[0, 0, 37],[ 0, 0, 57]]).cast_to::<$tb>().unwrap().into_owned(); + test_mat_mat_mul_prep::<$ker, $ta, $tb, $tc, $ti>(1, 2, 3, &a, &b).unwrap() + } + } + + #[test] + fn mat_mul_1_2_1() { + if $cond { + test_mat_mat_mul_prep::<$ker, $ta, $tb, $tc, $ti>( + 1, + 2, + 1, + &tensor2(&[[0, 1]]).cast_to::<$ta>().unwrap(), + &tensor2(&[[0], [1]]).cast_to::<$tb>().unwrap(), + ) + .unwrap() + } + } + + #[test] + fn late_packing_1() { + if $cond { + let a = tensor2(&[[1f32, 2f32]]).cast_to::<$ta>().unwrap().into_owned(); + let b = tensor2(&[[0f32, 0., 0.], [0., 0., 1.]]).cast_to::<$tb>().unwrap().into_owned(); + test_mat_mat_mul_late::<$ker, $ta, $tb, $tc, $ti>(1, 2, 3, &a, &b).unwrap() + } + } + + #[test] + fn mat_vec_1() { + if $cond { + let a = tensor2(&[[0], [1]]).cast_to::<$ta>().unwrap().into_owned(); + let b = tensor1(&[1]).cast_to::<$tb>().unwrap().into_owned(); + test_mat_vec_mul_prep::<$ker, $ta, $tb, $tc, $ti>(2, 1, &a, &b).unwrap() + } + } + + #[test] + fn mat_vec_2() { + if $cond { + let a = tensor1(&[0, 0, 0, 0, 0, 0, -4, 1]).into_shape(&[8,1]).unwrap(); + let a = a.cast_to::<$ta>().unwrap(); + let b = tensor1(&[-64]).cast_to::<$tb>().unwrap().into_owned(); + test_mat_vec_mul_prep::<$ker, $ta, $tb, $tc, $ti>(8, 1, &a, &b).unwrap() + } + } + + #[test] + fn mat_vec_3() { + if $cond { + let a = tensor1(&[0, 0]).into_shape(&[1, 2]).unwrap(); + let a = a.cast_to::<$ta>().unwrap(); + let b = tensor1(&[0, 0]).cast_to::<$tb>().unwrap().into_owned(); + test_mat_vec_mul_prep::<$ker, $ta, $tb, $tc, $ti>(1, 2, &a, &b).unwrap() + } + } + + #[test] + fn row_mul_2_1_3() { + if $cond { + unsafe { row_mul::<$ker, $ta, $tb, $tc, $ti>(2, 3).unwrap() } + } + } + + #[test] + fn row_add_2_1_3() { + if $cond { + unsafe { row_add::<$ker, $ta, $tb, $tc, $ti>(2, 3).unwrap() } + } + } + + #[test] + fn col_mul_2_1_3() { + if $cond { + unsafe { col_mul::<$ker, $ta, $tb, $tc, $ti>(2, 3).unwrap() } + } + } + + #[test] + fn col_add_2_1_3() { + if $cond { + unsafe { col_add::<$ker, $ta, $tb, $tc, $ti>(2, 3).unwrap() } + } + } + + #[test] + fn max_2_1_3() { + if $cond { + unsafe { max::<$ker, $ta, $tb, $tc, $ti>(2, 3).unwrap() } + } + } + + #[test] + fn min_2_1_3() { + if $cond { + unsafe { min::<$ker, $ta, $tb, $tc, $ti>(2, 3).unwrap() } + } + } + + #[test] + fn add_d_2_1_3() { + if $cond { + unsafe { add_d::<$ker, $ta, $tb, $tc, $ti>(2, 3).unwrap() } + } + } + + #[test] + fn add_d_big() { + if $cond { + unsafe { add_d::<$ker, $ta, $tb, $tc, $ti>(197, 1).unwrap() } + } + } + } + }; +} + +fn tensor(dt: DatumType, shape: Vec) -> BoxedStrategy { + let len = shape.iter().product::(); + // for f16, positive numbers only to avoid worst rounding side effects + // and not too big either to avoid overflow :) + let number = if dt == f16::datum_type() { + (0i16..100).boxed() + } else { + any::().prop_map(|i| i as i16).boxed() + }; + proptest::collection::vec(number, len..=len) + .prop_map(move |vec| { + tract_ndarray::ArrayD::from_shape_vec(shape.clone(), vec) + .unwrap() + .into_tensor() + .cast_to_dt(dt) + .unwrap() + .into_owned() + }) + .boxed() +} + +pub fn strat_mat_mat_mul() +-> BoxedStrategy<(usize, usize, usize, Tensor, Tensor)> { + (1usize..5, 1usize..5, 1usize..5) + .prop_flat_map(move |(m, k, n)| { + ( + Just(m), + Just(k), + Just(n), + tensor(TA::datum_type(), vec![m, k]), + tensor(TB::datum_type(), vec![k, n]), + ) + }) + .boxed() +} + +pub fn strat_mat_vec_mul() -> BoxedStrategy<(usize, usize, Tensor, Tensor)> +{ + (1usize..15, 1usize..15) + .prop_flat_map(move |(m, k)| { + ( + Just(m), + Just(k), + tensor(TA::datum_type(), vec![m, k]), + tensor(TB::datum_type(), vec![k, 1]), + ) + }) + .boxed() +} + +pub fn test_mat_mat_mul_prep + 'static, TA, TB, TC, TI>( + m: usize, + k: usize, + n: usize, + a: &Tensor, + b: &Tensor, +) -> Result<(), proptest::test_runner::TestCaseError> +where + TA: LADatum + AsPrimitive + 'static, + TB: LADatum + AsPrimitive + 'static, + TC: LADatum + AsPrimitive + 'static, + TI: LADatum + AsPrimitive, + i32: AsPrimitive, + usize: AsPrimitive, +{ + assert_eq!(a.datum_type(), TA::datum_type()); + let op = MatMatMulImpl::::default(); + unsafe { + let mut packed_a = + Tensor::uninitialized_aligned::(&[op.a_pack().len(k, m)], op.a_pack().alignment()) + .unwrap(); + op.a_pack().pack(packed_a.view_mut(), a.view(), 1, 0); + + let mut packed_b = + Tensor::uninitialized_aligned::(&[op.b_pack().len(k, n)], op.b_pack().alignment()) + .unwrap(); + op.b_pack().pack(packed_b.view_mut(), b.view(), 0, 1); + + fused_ops::( + m, + n, + &[FusedSpec::AddMatMul { + a: op + .a_packed(TA::datum_type().size_of(), k) + .wrap(&packed_a.view()), + b: op + .b_packed(TB::datum_type().size_of(), k) + .wrap(&packed_b.view()) + .unwrap(), + k, + }], + |r, c| { + let mut v: TI = TI::zero(); + for i in 0..k { + let a: TI = a.as_slice::().unwrap()[i + k * r].as_(); + let b: TI = b.as_slice::().unwrap()[c + i * n].as_(); + v += a * b; + } + v.as_() + }, + ) + } +} + +pub fn test_mat_mat_mul_late + 'static, TA, TB, TC, TI>( + m: usize, + k: usize, + n: usize, + a: &Tensor, + b: &Tensor, +) -> Result<(), proptest::test_runner::TestCaseError> +where + TA: LADatum + AsPrimitive + 'static, + TB: LADatum + AsPrimitive + 'static, + TC: LADatum + AsPrimitive + 'static, + TI: LADatum + AsPrimitive + 'static + Neg, + i32: AsPrimitive, + usize: AsPrimitive, +{ + assert_eq!(a.datum_type(), TA::datum_type()); + let op = MatMatMulImpl::::default(); + unsafe { + let mut packed_a = + Tensor::uninitialized_aligned::(&[op.a_pack().len(k, m)], op.a_pack().alignment()) + .unwrap(); + op.a_pack().pack(packed_a.view_mut(), a.view(), 1, 0); + + fused_ops::( + m, + n, + &[FusedSpec::AddMatMul { + a: op + .a_packed(TA::datum_type().size_of(), k) + .wrap(&packed_a.view()), + b: op.b_late_packing().wrap(&b.view()).unwrap(), + k, + }], + |r, c| { + let mut v: TI = TI::zero(); + for i in 0..k { + let a: TI = a.as_slice::().unwrap()[i + k * r].as_(); + let b: TI = b.as_slice::().unwrap()[c + i * n].as_(); + v += a * b; + } + v.as_() + }, + ) + } +} + +pub fn test_mat_vec_mul_prep + 'static, TA, TB, TC, TI>( + m: usize, + k: usize, + a: &Tensor, + b: &Tensor, +) -> Result<(), proptest::test_runner::TestCaseError> +where + TA: LADatum + AsPrimitive + 'static, + TB: LADatum + AsPrimitive + 'static, + TC: LADatum + AsPrimitive + 'static, + TI: LADatum + AsPrimitive + 'static + Neg, + i32: AsPrimitive, + usize: AsPrimitive, +{ + unsafe { + let op = MatMatMulImpl::::default(); + let mut packed_a = + Tensor::uninitialized_aligned::(&[op.a_pack().len(k, m)], op.a_pack().alignment()) + .unwrap(); + let mut packed_b = + Tensor::uninitialized_aligned::(&[op.b_pack().len(k, 1)], op.b_pack().alignment()) + .unwrap(); + op.a_pack().pack(&mut packed_a.view_mut(), &a.view(), 1, 0); + let b = b.clone().into_shape(&[k, 1]).unwrap(); + op.b_pack().pack(&mut packed_b.view_mut(), &b.view(), 0, 1); + + let pa = op + .a_packed(TA::datum_type().size_of(), k) + .wrap(&packed_a.view()); + let pb = op + .b_packed(b.datum_type().size_of(), k) + .wrap(&packed_b.view()) + .unwrap(); + + fused_ops::( + m, + 1, + &[FusedSpec::AddMatMul { k, a: pa, b: pb }], + |r, _| { + let mut inter = TI::zero(); + for i in 0..k { + let a: TI = a.as_slice::().unwrap()[i + k * r].as_(); + let b: TI = b.as_slice::().unwrap()[i].as_(); + inter += a * b; + } + inter.as_() + }, + ) + } +} + +pub unsafe fn fused_ops + 'static, TA, TB, TC, TI, F: Fn(usize, usize) -> TC>( + m: usize, + n: usize, + spec: &[FusedSpec], + expect: F, +) -> proptest::test_runner::TestCaseResult +where + TA: LADatum + AsPrimitive + 'static, + TB: LADatum + AsPrimitive + 'static, + TC: LADatum + AsPrimitive + 'static, + TI: LADatum + AsPrimitive + 'static, + i32: AsPrimitive, + usize: AsPrimitive, +{ + let op = MatMatMulImpl::::default(); + + let mut found = Tensor::zero::(&[m, n]).unwrap(); + let c_store = op + .c_from_data_and_strides(TC::datum_type().size_of(), m, n, n as isize, 1) + .wrap(&found.view_mut()); + let mut spec: TVec = spec.into(); + spec.push(FusedSpec::Store(c_store)); + + op.run(m, n, &spec).unwrap(); + let expected = + tract_ndarray::prelude::Array2::from_shape_fn((m, n), |(r, c)| expect(r, c)).into_tensor(); + if found.close_enough(&expected, true).is_err() { + println!("found, expected:"); + for r in 0..m { + for c in 0..n { + let f = found.as_slice_unchecked::()[r * n + c]; + let e = expected.as_slice_unchecked::()[r * n + c]; + let mut s = format!("{:4} ", f); + if f != e { + s = nu_ansi_term::Color::Red.paint(s).to_string(); + } + print!("{:4} ", s); + } + print!(" "); + for c in 0..n { + print!("{:4} ", expected.as_slice_unchecked::()[r * n + c]); + } + println!(); + } + } + found + .close_enough(&expected, true) + .map_err(|e| TestCaseError::Fail(e.to_string().into())) +} + +pub unsafe fn row_add + 'static, TA, TB, TC, TI>( + m: usize, + n: usize, +) -> proptest::test_runner::TestCaseResult +where + TA: LADatum + AsPrimitive + 'static, + TB: LADatum + AsPrimitive + 'static, + TC: LADatum + AsPrimitive + 'static, + TI: LADatum + AsPrimitive + 'static + Neg, + i32: AsPrimitive, + usize: AsPrimitive, +{ + let bias = (0..m).map(|i| i.as_()).collect::>(); + fused_ops::( + m, + n, + &[FusedSpec::BinPerRow(&tensor1(&bias), BinOp::Add)], + |r, _| bias[r].as_(), + ) +} + +pub unsafe fn row_mul + 'static, TA, TB, TC, TI>( + m: usize, + n: usize, +) -> proptest::test_runner::TestCaseResult +where + TA: LADatum + AsPrimitive + 'static, + TB: LADatum + AsPrimitive + 'static, + TC: LADatum + AsPrimitive + 'static, + TI: LADatum + AsPrimitive + 'static + Neg, + i32: AsPrimitive, + usize: AsPrimitive, +{ + let bias = (0..m).map(|i| i.as_()).collect::>(); + fused_ops::( + m, + n, + &[ + FusedSpec::BinScalar(&tensor0(1i32.as_()), BinOp::Add), + FusedSpec::BinPerRow(&tensor1(&bias), BinOp::Mul), + ], + |r, _| bias[r].as_(), + ) +} + +pub unsafe fn col_add + 'static, TA, TB, TC, TI>( + m: usize, + n: usize, +) -> proptest::test_runner::TestCaseResult +where + TA: LADatum + AsPrimitive + 'static, + TB: LADatum + AsPrimitive + 'static, + TC: LADatum + AsPrimitive + 'static, + TI: LADatum + AsPrimitive + 'static + Neg, + i32: AsPrimitive, + usize: AsPrimitive, +{ + let bias = (0..n).map(|i| i.as_()).collect::>(); + fused_ops::( + m, + n, + &[FusedSpec::BinPerCol(&tensor1(&bias), BinOp::Add)], + |_, c| bias[c].as_(), + ) +} + +pub unsafe fn col_mul + 'static, TA, TB, TC, TI>( + m: usize, + n: usize, +) -> proptest::test_runner::TestCaseResult +where + TA: LADatum + AsPrimitive + 'static, + TB: LADatum + AsPrimitive + 'static, + TC: LADatum + AsPrimitive + 'static, + TI: LADatum + AsPrimitive + 'static + Neg, + i32: AsPrimitive, + usize: AsPrimitive, +{ + let bias = (0..n).map(|i| i.as_()).collect::>(); + fused_ops::( + m, + n, + &[ + FusedSpec::BinScalar(&tensor0(1i32.as_()), BinOp::Add), + FusedSpec::BinPerCol(&tensor1(&bias), BinOp::Mul), + ], + |_, c| bias[c].as_(), + ) +} + +pub unsafe fn add_d + 'static, TA, TB, TC, TI>( + m: usize, + n: usize, +) -> proptest::test_runner::TestCaseResult +where + TA: LADatum + AsPrimitive + 'static, + TB: LADatum + AsPrimitive + 'static, + TC: LADatum + AsPrimitive + 'static, + TI: LADatum + AsPrimitive + 'static + Neg, + i32: AsPrimitive, + usize: AsPrimitive, +{ + let d = (0..m * n).map(|i| i.as_()).collect::>(); + let d = tensor1(&d).into_shape(&[m, n]).unwrap(); + let store_spec = OutputStoreSpec::View { + m_axis: 0, + n_axis: 1, + mr: K::mr(), + nr: K::nr(), + }; + fused_ops::( + m, + n, + &[FusedSpec::AddUnicast(store_spec.wrap(&d.view()))], + |r, c| { + d.to_array_view_unchecked::() + .into_dimensionality() + .unwrap()[(r, c)] + .as_() + }, + ) +} + +pub unsafe fn max, TA, TB, TC, TI>( + m: usize, + n: usize, +) -> proptest::test_runner::TestCaseResult +where + TA: LADatum + AsPrimitive + 'static, + TB: LADatum + AsPrimitive + 'static, + TC: LADatum + AsPrimitive + 'static, + TI: LADatum + AsPrimitive + 'static + Neg, + i32: AsPrimitive, + usize: AsPrimitive, +{ + let five: TI = 5.as_(); + fused_ops::( + m, + n, + &[FusedSpec::BinScalar(&tensor0(five), BinOp::Max)], + |_, _| five.as_(), + ) +} + +pub unsafe fn min, TA, TB, TC, TI>( + m: usize, + n: usize, +) -> proptest::test_runner::TestCaseResult +where + TA: LADatum + AsPrimitive + 'static, + TB: LADatum + AsPrimitive + 'static, + TC: LADatum + AsPrimitive + 'static, + TI: LADatum + AsPrimitive + 'static + Neg, + i32: AsPrimitive, + usize: AsPrimitive, +{ + let five: TI = 5.as_(); + fused_ops::( + m, + n, + &[FusedSpec::BinScalar(&tensor0(five), BinOp::Min)], + |_, _| TC::zero(), + ) +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/pack.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/pack.rs new file mode 100644 index 0000000..0d74f23 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/pack.rs @@ -0,0 +1,629 @@ +use std::fmt::Debug; +use std::marker::PhantomData; +use std::ops::Range; +use tract_data::internal::*; + +#[derive(Clone, Debug, Eq, PartialEq, Hash)] +pub struct Packer { + pub r: usize, + alignment: usize, + end_padding_record: usize, +} + +impl Packer { + pub fn new(nr: usize, alignment: usize, end_padding_record: usize) -> Packer { + Packer { + r: nr, + alignment, + end_padding_record, + } + } + + pub fn alignment(&self) -> usize { + self.alignment + } + + pub fn panel_width(&self) -> usize { + self.r + } + + pub fn len(&self, k: D, n: D) -> D { + (n.divceil(self.r) * (k + self.end_padding_record)) * self.r + } + + pub fn single_panel_len(&self, k: usize) -> usize { + (k + self.end_padding_record) * self.r + } + + #[allow(clippy::too_many_arguments)] + pub unsafe fn pack_t( + &self, + pb: *mut T, + b: *const T, + mn: usize, + k_stride: isize, + mn_stride: isize, + k_range: Range, + mn_range: Range, + ) { + if self.r == 1 && k_stride == 1 && mn == 1 { + pb.copy_from_nonoverlapping(b.add(k_range.start), k_range.len()) + } else if mn_stride == 1 { + let size_of = T::datum_type().size_of(); + let rbytes = self.r * size_of; + let mn_valid_end = mn_range.end.min(mn); + let mn_range_bytes = mn_range.start * size_of..mn_valid_end * size_of; + let k_stride_bytes = k_stride * size_of as isize; + let bb = b as *const u8; + let pbb = pb as *mut u8; + match rbytes { + 16 => pack_mn_major::<[u8; 16]>(bb, pbb, k_stride_bytes, mn_range_bytes, k_range), + 24 => pack_mn_major::<[u8; 24]>(bb, pbb, k_stride_bytes, mn_range_bytes, k_range), + 32 => pack_mn_major::<[u8; 32]>(bb, pbb, k_stride_bytes, mn_range_bytes, k_range), + 48 => pack_mn_major::<[u8; 48]>(bb, pbb, k_stride_bytes, mn_range_bytes, k_range), + 64 => pack_mn_major::<[u8; 64]>(bb, pbb, k_stride_bytes, mn_range_bytes, k_range), + _ => { + let mut packer = self.write_with_k_outer(pb, k_range.len(), mn_range.len()); + for k in k_range { + for x in mn_range.start..mn_valid_end { + packer.write(*b.offset(x as isize + k_stride * k as isize)) + } + for _x in mn_valid_end..mn_range.end { + packer.write(T::default()) + } + } + } + } + } else if k_stride == 1 { + let mut packer = self.write_with_k_inner(pb, k_range.len(), mn); + let mn_valid_end = mn_range.end.min(mn); + for x in mn_range.start..mn_valid_end { + for k in k_range.clone() { + packer.write(*b.offset(x as isize * mn_stride + k as isize)) + } + } + // just ignore invalid mn_range + } else { + let mut packer = self.write_with_k_outer(pb, k_range.len(), mn); + let mn_valid_end = mn_range.end.min(mn); + for k in k_range { + for x in mn_range.start..mn_valid_end { + packer.write(*b.offset(x as isize * mn_stride + k_stride * k as isize)) + } + for _x in mn_valid_end..mn_range.end { + packer.write(T::default()) + } + } + } + } + + pub unsafe fn pack_segment<'a, 'b>( + &self, + mut pb: impl std::borrow::BorrowMut>, + b: impl std::borrow::Borrow>, + k_axis: usize, + mn_axis: usize, + k_range: Range, + mn_range: Range, + ) { + debug_assert_eq!(pb.borrow().len(), self.len(k_range.len(), mn_range.len())); + let pb = pb.borrow_mut(); + let b = b.borrow(); + let dt = pb.datum_type(); + dispatch_copy!(Self::pack_t(dt)( + self, + pb.as_ptr_mut_unchecked(), + b.as_ptr_unchecked(), + b.shape()[mn_axis], + b.strides()[k_axis], + b.strides()[mn_axis], + k_range, + mn_range + )); + } + + pub unsafe fn pack<'a, 'b>( + &self, + pb: impl std::borrow::BorrowMut>, + b: impl std::borrow::Borrow>, + k_axis: usize, + mn_axis: usize, + ) { + let k = b.borrow().shape()[k_axis]; + let mn = b.borrow().shape()[mn_axis]; + self.pack_segment(pb, b, k_axis, mn_axis, 0..k, 0..mn); + } + + pub fn write_with_k_outer<'p, T: Copy + Debug>( + &self, + pb: *mut T, + k: usize, + mn: usize, + ) -> KOutWriter<'p, T> { + KOutWriter::new(pb, self.r, mn, k) + } + + pub fn write_single_panel_with_k_outer<'p, T: Copy + Debug>( + &self, + pb: *mut T, + ) -> KOutSinglePanelWriter<'p, T> { + KOutSinglePanelWriter::new(pb) + } + + pub fn write_with_k_inner<'p, T: Copy + Debug>( + &self, + pb: *mut T, + k: usize, + mn: usize, + ) -> KInWriter<'p, T> { + KInWriter::new(pb, self.r, mn, k) + } +} + +pub trait PackingWriter { + fn write(&mut self, t: T); +} + +#[derive(Debug)] +pub struct KOutSinglePanelWriter<'p, T> +where + T: Copy + std::fmt::Debug, +{ + ptr: *mut T, + _phantom: PhantomData<&'p T>, +} + +impl<'p, T> KOutSinglePanelWriter<'p, T> +where + T: Copy + std::fmt::Debug, +{ + pub fn new(ptr: *mut T) -> KOutSinglePanelWriter<'p, T> { + KOutSinglePanelWriter { + ptr, + _phantom: PhantomData, + } + } +} + +impl<'p, T> PackingWriter for KOutSinglePanelWriter<'p, T> +where + T: Copy + std::fmt::Debug, +{ + #[inline(always)] + fn write(&mut self, t: T) { + unsafe { + *self.ptr = t; + self.ptr = self.ptr.offset(1); + } + } +} + +#[derive(Debug)] +pub struct KOutWriter<'p, T> +where + T: Copy + std::fmt::Debug, +{ + ptr: *mut T, + panels: usize, + panel_width: usize, + last_panel_width: usize, + remain: usize, + current_panel: usize, + next_panel: isize, + next_lane: isize, + _phantom: PhantomData<&'p T>, +} + +impl<'p, T> KOutWriter<'p, T> +where + T: Copy + std::fmt::Debug, +{ + pub fn new(ptr: *mut T, panel_width: usize, mn: usize, k: usize) -> KOutWriter<'p, T> { + let panels = (mn + panel_width - 1) / panel_width; + let last_panel_width = mn - (panels - 1) * panel_width; + KOutWriter { + ptr, + panels, + panel_width, + last_panel_width, + remain: if panels > 1 { + panel_width + } else { + last_panel_width + }, + current_panel: 0, + next_panel: ((k - 1) * panel_width) as isize, + next_lane: panel_width as isize + - ((last_panel_width + (panels - 1) * panel_width * k) as isize), + _phantom: PhantomData, + } + } +} + +impl<'p, T> PackingWriter for KOutWriter<'p, T> +where + T: Copy + std::fmt::Debug, +{ + #[inline(always)] + fn write(&mut self, t: T) { + unsafe { + *self.ptr = t; + self.remain -= 1; + self.ptr = self.ptr.offset(1); + if self.remain == 0 { + self.current_panel += 1; + if self.current_panel == self.panels { + self.ptr = self.ptr.offset(self.next_lane); + self.current_panel = 0; + } else { + self.ptr = self.ptr.offset(self.next_panel); + } + if self.current_panel == self.panels - 1 { + self.remain = self.last_panel_width; + } else { + self.remain = self.panel_width; + } + } + } + } +} + +#[derive(Debug)] +pub struct KInWriter<'p, T> +where + T: Copy + Debug, +{ + ptr: *mut T, + k: usize, + panels: usize, + panel_width: usize, + last_panel_width: usize, + remain_on_k: usize, + remain_on_mn: usize, + current_panel: usize, + next_mn_offset: isize, + next_panel_offset: isize, + _phantom: PhantomData<&'p T>, +} + +impl<'p, T> KInWriter<'p, T> +where + T: Copy + Debug, +{ + pub fn new(ptr: *mut T, panel_width: usize, mn: usize, k: usize) -> KInWriter<'p, T> { + let panels = (mn + panel_width - 1) / panel_width; + let last_panel_width = mn - (panels - 1) * panel_width; + KInWriter { + ptr, + k, + panels, + panel_width, + last_panel_width, + remain_on_k: k, + remain_on_mn: if panels == 1 { + last_panel_width + } else { + panel_width + }, + current_panel: 0, + next_mn_offset: 1 - (k * panel_width) as isize, + next_panel_offset: 1 - panel_width as isize, + _phantom: PhantomData, + } + } +} + +impl<'p, T> PackingWriter for KInWriter<'p, T> +where + T: Copy + std::fmt::Debug, +{ + #[inline(always)] + fn write(&mut self, t: T) { + unsafe { + *self.ptr = t; + self.remain_on_k -= 1; + self.ptr = self.ptr.add(self.panel_width); + if self.remain_on_k == 0 { + self.remain_on_k = self.k; + self.remain_on_mn -= 1; + if self.remain_on_mn > 0 { + self.ptr = self.ptr.offset(self.next_mn_offset); + } else { + self.ptr = self.ptr.offset(self.next_panel_offset); + self.current_panel += 1; + if self.current_panel == self.panels - 1 { + self.remain_on_mn = self.last_panel_width; + } else { + self.remain_on_mn = self.panel_width; + } + } + } + } + } +} + +#[inline(never)] +unsafe fn pack_mn_major( + b: *const u8, + packed: *mut u8, + k_stride_bytes: isize, + mn_range_bytes: Range, + k_range: Range, +) { + let mnr = std::mem::size_of::(); + let full_panes = mn_range_bytes.len() / mnr; + let partial_pane = mn_range_bytes.len() % mnr; + for k in 0..k_range.len() { + let mut p_row = packed.add(k * mnr); + let mut b_row = + b.offset((k_range.start + k) as isize * k_stride_bytes + mn_range_bytes.start as isize); + for _ in 0..full_panes { + p_row.copy_from_nonoverlapping(b_row, mnr); + p_row = p_row.add(k_range.len() * mnr); + b_row = b_row.add(mnr); + } + if partial_pane > 0 { + p_row.copy_from_nonoverlapping(b_row, partial_pane); + } + } +} + +#[cfg(test)] +mod test { + use std::ops::Range; + + use proptest::prelude::*; + use tract_data::internal::*; + use tract_ndarray::prelude::*; + + #[derive(Debug)] + struct PackProblem { + k: usize, + mn: usize, + is_a: bool, + r: usize, + k_range: Range, + mn_range: Range, + } + + impl PackProblem { + fn input(&self) -> Array2 { + let shape = if self.is_a { + (self.mn, self.k) + } else { + (self.k, self.mn) + }; + let data = (0..(self.k * self.mn) as u32).collect(); + Array2::from_shape_vec(shape, data).unwrap() + } + + fn packer(&self) -> Array3 { + let panels = self.mn_range.len().divceil(self.r); + let packer = super::Packer::new(self.r, 1, 0); + let input = self.input().into_tensor(); + let mut output = + Tensor::zero::(&[packer.len(self.k_range.len(), self.mn_range.len())]) + .unwrap(); + unsafe { + packer.pack_segment( + output.view_mut(), + input.view(), + self.is_a as usize, + !self.is_a as usize, + self.k_range.clone(), + self.mn_range.clone(), + ) + }; + output + .into_array::() + .unwrap() + .into_shape((panels, self.k_range.len(), self.r)) + .unwrap() + } + + fn reference(&self) -> Array3 { + let input = self.input(); + let panels = self.mn_range.len().divceil(self.r); + Array3::from_shape_fn([panels, self.k_range.len(), self.r], |(panel, k, x)| { + if self.mn_range.start + panel * self.r + x >= self.mn_range.end { + 0 + } else { + let mn = panel * self.r + x + self.mn_range.start; + let k = k + self.k_range.start; + let coords = if self.is_a { (mn, k) } else { (k, mn) }; + *input.get(coords).unwrap_or(&0) + } + }) + } + + fn check(&self) { + assert_eq!(self.packer(), self.reference()) + } + } + + impl Arbitrary for PackProblem { + type Parameters = (); + type Strategy = BoxedStrategy; + fn arbitrary_with(_args: ()) -> Self::Strategy { + (any::(), 1usize..9, 1usize..20, 1usize..20) + .prop_flat_map(|(is_a, r, k, mn)| { + ( + Just((is_a, r, k, mn)), + sub_range_strat(0..k), + sub_range_strat(0..mn), + ) + }) + .prop_map(|((is_a, r, k, mn), k_range, mn_range)| PackProblem { + k, + mn, + is_a, + r, + k_range, + mn_range, + }) + .boxed() + } + } + + fn sub_range_strat(range: Range) -> BoxedStrategy> { + (0..range.len()) + .prop_flat_map(|cropped| (Just(cropped), 0..=cropped)) + .prop_map(move |(cropped, left)| range.start + left..range.end - (cropped - left)) + .boxed() + } + + proptest::proptest! { + #[test] + fn prop(pb in any::()) { + pb.check(); + } + + #[test] + fn subrange_prop(_range in sub_range_strat(0..20)) { + } + + } + + #[test] + fn simple_b_1() { + PackProblem { + k: 2, + mn: 1, + is_a: false, + r: 1, + k_range: 0..2, + mn_range: 0..1, + } + .check(); + } + + #[test] + fn simple_b_2() { + PackProblem { + k: 2, + mn: 2, + is_a: false, + r: 1, + k_range: 0..2, + mn_range: 0..2, + } + .check() + } + + #[test] + fn simple_b_3() { + PackProblem { + k: 2, + mn: 1, + is_a: false, + r: 4, + k_range: 0..2, + mn_range: 0..1, + } + .check(); + } + + #[test] + fn simple_a_1() { + PackProblem { + k: 2, + mn: 2, + is_a: true, + r: 1, + k_range: 0..2, + mn_range: 0..2, + } + .check(); + } + + #[test] + fn simple_a_2() { + PackProblem { + k: 2, + mn: 3, + is_a: true, + r: 2, + k_range: 0..2, + mn_range: 0..3, + } + .check(); + } + + #[test] + fn range_k_0() { + PackProblem { + k: 2, + mn: 1, + is_a: false, + r: 1, + k_range: 1..2, + mn_range: 0..1, + } + .check(); + } + + #[test] + fn range_k_1() { + PackProblem { + k: 2, + mn: 2, + is_a: false, + r: 1, + k_range: 0..2, + mn_range: 0..1, + } + .check(); + } + + #[test] + fn range_k_2() { + PackProblem { + k: 2, + mn: 1, + is_a: false, + r: 6, + k_range: 1..2, + mn_range: 0..1, + } + .check(); + } + + #[test] + fn range_mn_0() { + PackProblem { + k: 1, + mn: 2, + is_a: false, + r: 2, + k_range: 0..1, + mn_range: 0..1, + } + .check(); + } + + #[test] + fn range_b_4() { + PackProblem { + k: 1, + mn: 2, + is_a: false, + r: 6, + k_range: 0..1, + mn_range: 1..2, + } + .check(); + } + + #[test] + fn range_b_5() { + PackProblem { + k: 1, + mn: 7, + is_a: false, + r: 6, + k_range: 0..1, + mn_range: 1..7, + } + .check(); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/sigmoid.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/sigmoid.rs new file mode 100644 index 0000000..2473aa5 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/sigmoid.rs @@ -0,0 +1,96 @@ +macro_rules! sigmoid_impl { + ($ti: ident, $func: ident, $nr: expr, $alignment_items: expr, $cond: expr) => { + ew_impl!($ti, $func, $nr, $alignment_items); + #[cfg(test)] + paste! { + mod [] { + use super::*; + sigmoid_frame_tests!($cond, $ti, $func); + } + } + }; +} + +#[cfg(test)] +#[macro_use] +pub mod test { + use crate::{LADatum, frame::element_wise::*}; + use num_traits::{AsPrimitive, Float}; + use proptest::test_runner::TestCaseResult; + + #[macro_export] + macro_rules! sigmoid_frame_tests { + ($cond:expr, $t: ty, $ker:ty) => { + proptest::proptest! { + #[test] + fn sigmoid(xs in proptest::collection::vec(-25f32..25.0, 0..100)) { + if $cond { + $crate::frame::sigmoid::test::test_sigmoid::<$ker, $t>(&*xs).unwrap() + } + } + } + + #[test] + fn sigmoid_4_magic() { + if $cond { + $crate::frame::sigmoid::test::test_sigmoid::<$ker, $t>(&[ + 0f32, -20.0, 20.0, 0.0, + ]) + .unwrap() + } + } + + #[test] + fn sigmoid_4zeros() { + if $cond { + $crate::frame::sigmoid::test::test_sigmoid::<$ker, $t>(&[0.0; 4]).unwrap(); + } + } + + #[test] + fn sigmoid_20_ones() { + if $cond { + $crate::frame::sigmoid::test::test_sigmoid::<$ker, $t>(&[1.0; 20]).unwrap(); + } + } + + #[test] + fn sigmoid_18_zeros() { + if $cond { + $crate::frame::sigmoid::test::test_sigmoid::<$ker, $t>(&[0.0; 18]).unwrap(); + } + } + + #[test] + fn sigmoid_asymptots() { + use tract_data::internal::*; + use $crate::frame::element_wise::*; + if $cond { + let mut input: Vec<$t> = [-100f32, 100f32] + .iter() + .map(|x| >::as_(*x)) + .collect(); + let expected: Vec<$t> = [-0f32, 1f32] + .iter() + .map(|x| >::as_(*x)) + .collect(); + <$ker>::ew().run(&mut input).unwrap(); + tensor1(&input) + .close_enough(&tensor1(&expected), Approximation::Close) + .unwrap(); + } + } + }; + } + + pub fn test_sigmoid, T: LADatum + Float>(values: &[f32]) -> TestCaseResult + where + f32: AsPrimitive, + T: AsPrimitive, + { + let values: Vec = values.iter().copied().map(|x| x.as_()).collect(); + crate::frame::element_wise::test::test_element_wise::(&values, |x| { + (1f32).as_() / (1f32.as_() + (-x).exp()) + }) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/tanh.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/tanh.rs new file mode 100644 index 0000000..10bb20d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/frame/tanh.rs @@ -0,0 +1,101 @@ +macro_rules! tanh_impl { + ($ti: ident, $func: ident, $nr: expr, $alignment_items: expr, $cond: expr) => { + ew_impl!($ti, $func, $nr, $alignment_items); + #[cfg(test)] + paste! { + mod [] { + use super::*; + tanh_frame_tests!($cond, $ti, $func); + } + } + }; +} + +#[cfg(test)] +#[macro_use] +pub mod test { + use crate::LADatum; + use crate::frame::element_wise::*; + use num_traits::AsPrimitive; + use num_traits::float::Float; + use proptest::test_runner::TestCaseResult; + + #[macro_export] + macro_rules! tanh_frame_tests { + ($cond:expr, $t:ty, $ker:ty) => { + proptest::proptest! { + #[test] + fn tanh(xs in proptest::collection::vec(-25f32..25.0, 0..100)) { + if $cond { + $crate::frame::tanh::test::test_tanh::<$ker, $t>(&*xs).unwrap() + } + } + } + + #[test] + fn tanh_4_magic() { + if $cond { + $crate::frame::tanh::test::test_tanh::<$ker, $t>(&[0f32, -20.0, 20.0, 0.0]) + .unwrap() + } + } + + #[test] + fn tanh_4zeros() { + if $cond { + $crate::frame::tanh::test::test_tanh::<$ker, $t>(&[0.0; 4]).unwrap(); + } + } + + #[test] + fn tanh_20_ones() { + if $cond { + $crate::frame::tanh::test::test_tanh::<$ker, $t>(&[1.0; 20]).unwrap(); + } + } + + #[test] + fn tanh_18_zeros() { + if $cond { + $crate::frame::tanh::test::test_tanh::<$ker, $t>(&[0.0; 18]).unwrap(); + } + } + + #[test] + fn tanh_foo() { + if $cond { + $crate::frame::tanh::test::test_tanh::<$ker, $t>(&[0.67503357]).unwrap(); + } + } + + #[test] + fn tanh_asymptots() { + use tract_data::internal::*; + use $crate::frame::element_wise::*; + if $cond { + let mut input: Vec<$t> = [-100f32, 100f32] + .iter() + .map(|x| >::as_(*x)) + .collect(); + let expected: Vec<$t> = [-1f32, 1f32] + .iter() + .map(|x| >::as_(*x)) + .collect(); + <$ker>::ew().run(&mut input).unwrap(); + tensor1(&input) + .close_enough(&tensor1(&expected), Approximation::Close) + .unwrap(); + } + } + }; + } + + pub fn test_tanh, T: LADatum + Float>(values: &[f32]) -> TestCaseResult + where + f32: AsPrimitive, + T: AsPrimitive, + { + let values: Vec = values.iter().copied().map(|x| x.as_()).collect(); + crate::frame::element_wise::test::test_element_wise::(&values, |x| x.tanh()) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic.rs new file mode 100644 index 0000000..33ff7d0 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic.rs @@ -0,0 +1,14 @@ +pub mod erf; +pub mod lut; +pub mod mmm; +pub mod rounding; +pub mod sigmoid; +pub mod tanh; + +pub use self::erf::SErf4; +pub use self::lut::GenericLut8; +pub use self::mmm::GenericMmm4x1; +pub use self::mmm::GenericMmm4x4; +pub use self::rounding::{ScaleShiftAndRound, Scaler}; +pub use self::sigmoid::{HSigmoid8, SSigmoid4}; +pub use self::tanh::{HTanh8, STanh4}; diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic/erf.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic/erf.rs new file mode 100644 index 0000000..780d832 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic/erf.rs @@ -0,0 +1,51 @@ +use crate::element_wise::ElementWiseKer; + +#[allow(non_upper_case_globals)] +#[allow(clippy::excessive_precision)] +fn serf(x: &mut f32) { + const a1: f32 = 0.0705230784; + const a2: f32 = 0.0422820123; + const a3: f32 = 0.0092705272; + const a4: f32 = 0.0001520143; + const a5: f32 = 0.0002765672; + const a6: f32 = 0.0000430638; + + let signum = x.signum(); + let abs = x.abs(); + let y = a6 * abs; + let y = (a5 + y) * abs; + let y = (a4 + y) * abs; + let y = (a3 + y) * abs; + let y = (a2 + y) * abs; + let y = (a1 + y) * abs; + let y = 1.0 - (y + 1.0).powi(16).recip(); + + *x = y.copysign(signum) +} + +#[derive(Clone, Debug)] +pub struct SErf4; + +impl ElementWiseKer for SErf4 { + fn name() -> &'static str { + "generic" + } + + fn alignment_items() -> usize { + 16 + } + + fn alignment_bytes() -> usize { + 16 + } + + fn nr() -> usize { + 4 + } + + fn run(x: &mut [f32]) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(serf) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic/lut.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic/lut.rs new file mode 100644 index 0000000..e690c4c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic/lut.rs @@ -0,0 +1,45 @@ +use crate::frame::lut::LutKer; + +#[derive(Clone, Debug, Hash)] +pub struct GenericLut8; + +impl LutKer for GenericLut8 { + fn name() -> &'static str { + "generic" + } + + fn input_alignment_bytes() -> usize { + 1 + } + + fn table_alignment_bytes() -> usize { + 1 + } + + fn n() -> usize { + 8 + } + + unsafe fn run(buf: *mut u8, len: usize, table: *const u8) { + debug_assert!(len % Self::n() == 0); + debug_assert!(buf as usize % Self::input_alignment_bytes() == 0); + debug_assert!(table as usize % Self::table_alignment_bytes() == 0); + for i in 0..((len / 8) as isize) { + let ptr = buf.offset(8 * i); + *ptr.offset(0) = *table.offset(*ptr.offset(0) as isize); + *ptr.offset(1) = *table.offset(*ptr.offset(1) as isize); + *ptr.offset(2) = *table.offset(*ptr.offset(2) as isize); + *ptr.offset(3) = *table.offset(*ptr.offset(3) as isize); + *ptr.offset(4) = *table.offset(*ptr.offset(4) as isize); + *ptr.offset(5) = *table.offset(*ptr.offset(5) as isize); + *ptr.offset(6) = *table.offset(*ptr.offset(6) as isize); + *ptr.offset(7) = *table.offset(*ptr.offset(7) as isize); + } + } +} + +#[cfg(test)] +#[macro_use] +pub mod test { + lut_frame_tests!(true, crate::generic::GenericLut8); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic/mmm.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic/mmm.rs new file mode 100644 index 0000000..dbd412d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic/mmm.rs @@ -0,0 +1,594 @@ +#![allow(clippy::needless_range_loop)] +use num_traits::AsPrimitive; +use std::marker::PhantomData; +use std::{fmt, ops}; + +use tract_data::prelude::*; + +use super::*; +use crate::LADatum; +use crate::frame::mmm::*; + +#[derive(Copy, Clone, Debug)] +pub struct GenericMmm4x4(PhantomData<(TA, TB, TI)>) +where + TA: Datum + Copy + fmt::Debug + AsPrimitive, + TB: Datum + Copy + fmt::Debug + AsPrimitive, + TI: LADatum + ScaleShiftAndRound; + +unsafe impl Send for GenericMmm4x4 +where + TA: Datum + Copy + fmt::Debug + AsPrimitive, + TB: Datum + Copy + fmt::Debug + AsPrimitive, + TI: LADatum + ScaleShiftAndRound, +{ +} + +unsafe impl Sync for GenericMmm4x4 +where + TA: Datum + Copy + fmt::Debug + AsPrimitive, + TB: Datum + Copy + fmt::Debug + AsPrimitive, + TI: LADatum + ScaleShiftAndRound, +{ +} + +macro_rules! scalar { + ($ab: expr, $m: expr, $f: expr) => { + for i in 0..$ab.len() { + for j in 0..$ab[0].len() { + $ab[i][j] = $f($m, $ab[i][j]) + } + } + }; +} + +macro_rules! per_row { + ($ab: expr, $m: expr, $f: expr) => { + for i in 0..$ab.len() { + for j in 0..$ab[0].len() { + $ab[i][j] = $f(*$m.add(i), $ab[i][j]) + } + } + }; +} + +macro_rules! per_col { + ($ab: expr, $m: expr, $f: expr) => { + for i in 0..$ab.len() { + for j in 0..$ab[0].len() { + $ab[i][j] = $f(*$m.add(j), $ab[i][j]) + } + } + }; +} + +impl MatMatMulKer for GenericMmm4x4 +where + TA: Datum + Copy + fmt::Debug + AsPrimitive, + TB: Datum + Copy + fmt::Debug + AsPrimitive, + TI: LADatum + ScaleShiftAndRound, + usize: AsPrimitive, +{ + #[inline(always)] + fn name() -> &'static str { + match TI::datum_type() { + DatumType::F16 => "generic_f16_4x4", + DatumType::F32 => "generic_f32_4x4", + DatumType::I32 => "generic_i32_4x4", + DatumType::F64 => "generic_f64_4x4", + _ => panic!(), + } + } + #[inline(always)] + fn mr() -> usize { + 4 + } + #[inline(always)] + fn nr() -> usize { + 4 + } + fn end_padding_packed_a() -> usize { + 0 + } + fn end_padding_packed_b() -> usize { + 0 + } + #[inline(always)] + fn alignment_bytes_packed_a() -> usize { + std::mem::size_of::() + } + #[inline(always)] + fn alignment_bytes_packed_b() -> usize { + std::mem::size_of::() + } + #[inline(never)] + fn kernel(spec: &[FusedKerSpec]) -> isize { + unsafe { + let mut ab = [[TI::zero(); 4]; 4]; + let mut pnl = spec.as_ptr(); + loop { + if pnl.is_null() { + break; + } + match *pnl { + FusedKerSpec::Done => break, + FusedKerSpec::Clear => ab = std::mem::zeroed(), + FusedKerSpec::ScalarAdd(a) => scalar!(ab, a, |a, b| a + b), + FusedKerSpec::ScalarMul(a) => scalar!(ab, a, |a, b| a * b), + FusedKerSpec::ScalarMin(m) => scalar!(ab, m, |a, b| if a < b { a } else { b }), + FusedKerSpec::ScalarMax(m) => scalar!(ab, m, |a, b| if a > b { a } else { b }), + FusedKerSpec::ScalarSub(m) => scalar!(ab, m, |a, b| a - b), + FusedKerSpec::ScalarSubF(m) => scalar!(ab, m, |a, b| b - a), + FusedKerSpec::PerRowMin(m) => per_row!(ab, m, |a, b| if a < b { a } else { b }), + FusedKerSpec::PerRowMax(m) => per_row!(ab, m, |a, b| if a > b { a } else { b }), + FusedKerSpec::PerRowAdd(m) => per_row!(ab, m, |a, b| a + b), + FusedKerSpec::PerRowMul(m) => per_row!(ab, m, |a, b| a * b), + FusedKerSpec::PerRowSub(m) => per_row!(ab, m, |a, b| a - b), + FusedKerSpec::PerRowSubF(m) => per_row!(ab, m, |a, b| b - a), + FusedKerSpec::PerColMin(m) => per_col!(ab, m, |a, b| if a < b { a } else { b }), + FusedKerSpec::PerColMax(m) => per_col!(ab, m, |a, b| if a > b { a } else { b }), + FusedKerSpec::PerColAdd(m) => per_col!(ab, m, |a, b| a + b), + FusedKerSpec::PerColMul(m) => per_col!(ab, m, |a, b| a * b), + FusedKerSpec::PerColSub(m) => per_col!(ab, m, |a, b| a - b), + FusedKerSpec::PerColSubF(m) => per_col!(ab, m, |a, b| b - a), + FusedKerSpec::AddRowColProducts(rows, cols) => { + for i in 0..4 { + for j in 0..4 { + ab[i][j] += *rows.add(i) * *cols.add(j); + } + } + } + FusedKerSpec::AddUnicast(tile) => add_unicast::(&tile, &mut ab), + FusedKerSpec::ShiftLeft(shift) => { + for i in 0..4 { + for j in 0..4 { + ab[i][j] = ab[i][j].q_shl(shift); + } + } + } + FusedKerSpec::RoundingShiftRight(shift, rp) => { + for i in 0..4 { + for j in 0..4 { + ab[i][j] = ab[i][j].q_shr(shift, rp); + } + } + } + FusedKerSpec::QScale(shift, rp, mult) => { + for i in 0..4 { + for j in 0..4 { + ab[i][j] = + ab[i][j].q_scale(Scaler::from_fuse_params(shift, rp, mult)); + } + } + } + FusedKerSpec::AddMatMul { k, pa, pb, .. } => { + let a = pa as *const TA; + let b = pb as *const TB; + for i in 0..k { + let a = std::slice::from_raw_parts(a.offset(4 * i as isize), 4); + let b = std::slice::from_raw_parts(b.offset(4 * i as isize), 4); + ab[0][0] += a[0].as_() * b[0].as_(); + ab[0][1] += a[0].as_() * b[1].as_(); + ab[0][2] += a[0].as_() * b[2].as_(); + ab[0][3] += a[0].as_() * b[3].as_(); + ab[1][0] += a[1].as_() * b[0].as_(); + ab[1][1] += a[1].as_() * b[1].as_(); + ab[1][2] += a[1].as_() * b[2].as_(); + ab[1][3] += a[1].as_() * b[3].as_(); + ab[2][0] += a[2].as_() * b[0].as_(); + ab[2][1] += a[2].as_() * b[1].as_(); + ab[2][2] += a[2].as_() * b[2].as_(); + ab[2][3] += a[2].as_() * b[3].as_(); + ab[3][0] += a[3].as_() * b[0].as_(); + ab[3][1] += a[3].as_() * b[1].as_(); + ab[3][2] += a[3].as_() * b[2].as_(); + ab[3][3] += a[3].as_() * b[3].as_(); + } + } + FusedKerSpec::Store(tile) => store(&tile, &ab), + }; + pnl = pnl.add(1); + } + } + 0 + } +} + +#[derive(Copy, Clone, Debug)] +pub struct GenericMmm4x1(PhantomData<(TA, TB, TI)>) +where + TA: Datum + Copy + fmt::Debug + AsPrimitive, + TB: Datum + Copy + fmt::Debug + AsPrimitive, + TI: LADatum + ScaleShiftAndRound; + +unsafe impl Send for GenericMmm4x1 +where + TA: Datum + Copy + fmt::Debug + AsPrimitive, + TB: Datum + Copy + fmt::Debug + AsPrimitive, + TI: LADatum + ScaleShiftAndRound, +{ +} + +unsafe impl Sync for GenericMmm4x1 +where + TA: Datum + Copy + fmt::Debug + AsPrimitive, + TB: Datum + Copy + fmt::Debug + AsPrimitive, + TI: LADatum + ScaleShiftAndRound, +{ +} + +impl MatMatMulKer for GenericMmm4x1 +where + TA: Datum + Copy + fmt::Debug + AsPrimitive, + TB: Datum + Copy + fmt::Debug + AsPrimitive, + TI: LADatum + ScaleShiftAndRound, + usize: AsPrimitive, +{ + #[inline(always)] + fn name() -> &'static str { + match TI::datum_type() { + DatumType::F16 => "generic_f16_4x1", + DatumType::F32 => "generic_f32_4x1", + DatumType::I32 => "generic_i32_4x1", + DatumType::F64 => "generic_f64_4x1", + _ => panic!(), + } + } + #[inline(always)] + fn mr() -> usize { + 4 + } + #[inline(always)] + fn nr() -> usize { + 1 + } + fn end_padding_packed_a() -> usize { + 0 + } + fn end_padding_packed_b() -> usize { + 0 + } + #[inline(always)] + fn alignment_bytes_packed_a() -> usize { + std::mem::size_of::() + } + #[inline(always)] + fn alignment_bytes_packed_b() -> usize { + std::mem::size_of::() + } + #[inline(never)] + fn kernel(spec: &[FusedKerSpec]) -> isize { + unsafe { + let mut ab = [[TI::zero(); 1]; 4]; + let mut pnl = spec.as_ptr(); + loop { + if pnl.is_null() { + break; + } + match *pnl { + FusedKerSpec::Done => break, + FusedKerSpec::Clear => ab = std::mem::zeroed(), + FusedKerSpec::ScalarAdd(a) => scalar!(ab, a, |a, b| a + b), + FusedKerSpec::ScalarMul(a) => scalar!(ab, a, |a, b| a * b), + FusedKerSpec::ScalarMin(m) => scalar!(ab, m, |a, b| if a < b { a } else { b }), + FusedKerSpec::ScalarMax(m) => scalar!(ab, m, |a, b| if a > b { a } else { b }), + FusedKerSpec::ScalarSub(m) => scalar!(ab, m, |a, b| a - b), + FusedKerSpec::ScalarSubF(m) => scalar!(ab, m, |a, b| b - a), + FusedKerSpec::PerRowMul(m) => per_row!(ab, m, |a, b| a * b), + FusedKerSpec::PerRowMin(m) => per_row!(ab, m, |a, b| if a < b { a } else { b }), + FusedKerSpec::PerRowMax(m) => per_row!(ab, m, |a, b| if a > b { a } else { b }), + FusedKerSpec::PerRowAdd(m) => per_row!(ab, m, |a, b| a + b), + FusedKerSpec::PerRowSub(m) => per_row!(ab, m, |a, b| a - b), + FusedKerSpec::PerRowSubF(m) => per_row!(ab, m, |a, b| b - a), + FusedKerSpec::PerColMin(m) => per_col!(ab, m, |a, b| if a < b { a } else { b }), + FusedKerSpec::PerColMax(m) => per_col!(ab, m, |a, b| if a > b { a } else { b }), + FusedKerSpec::PerColAdd(m) => per_col!(ab, m, |a, b| a + b), + FusedKerSpec::PerColMul(m) => per_col!(ab, m, |a, b| a * b), + FusedKerSpec::PerColSub(m) => per_col!(ab, m, |a, b| a - b), + FusedKerSpec::PerColSubF(m) => per_col!(ab, m, |a, b| b - a), + FusedKerSpec::AddRowColProducts(rows, cols) => { + let col = *cols; + for i in 0..4 { + ab[i][0] += *rows.add(i) * col; + } + } + FusedKerSpec::AddUnicast(tile) => add_unicast::( + &tile, + &mut [ + std::slice::from_raw_parts_mut(ab.as_ptr().offset(0) as _, 1), + std::slice::from_raw_parts_mut(ab.as_ptr().offset(1) as _, 1), + std::slice::from_raw_parts_mut(ab.as_ptr().offset(2) as _, 1), + std::slice::from_raw_parts_mut(ab.as_ptr().offset(3) as _, 1), + ], + ), + FusedKerSpec::ShiftLeft(shift) => { + for i in 0..4 { + ab[i][0] = ab[i][0].q_shl(shift); + } + } + FusedKerSpec::RoundingShiftRight(shift, rp) => { + for i in 0..4 { + ab[i][0] = ab[i][0].q_shr(shift, rp); + } + } + FusedKerSpec::QScale(shift, rp, mult) => { + for i in 0..4 { + ab[i][0] = ab[i][0].q_scale(Scaler::from_fuse_params(shift, rp, mult)); + } + } + FusedKerSpec::AddMatMul { k, pa, pb, .. } => { + let a = pa as *const TA; + let b = pb as *const TB; + for i in 0..k { + let a = std::slice::from_raw_parts(a.offset(4 * i as isize), 4); + let b = *b.add(i); + ab[0][0] += a[0].as_() * b.as_(); + ab[1][0] += a[1].as_() * b.as_(); + ab[2][0] += a[2].as_() * b.as_(); + ab[3][0] += a[3].as_() * b.as_(); + } + } + FusedKerSpec::Store(tile) => store( + &tile, + &[ + std::slice::from_raw_parts(ab.as_ptr().offset(0) as _, 1), + std::slice::from_raw_parts(ab.as_ptr().offset(1) as _, 1), + std::slice::from_raw_parts(ab.as_ptr().offset(2) as _, 1), + std::slice::from_raw_parts(ab.as_ptr().offset(3) as _, 1), + ], + ), + } + pnl = pnl.add(1); + } + } + 0 + } +} + +#[cfg(test)] +#[derive(Copy, Clone, Debug)] +pub struct GenericMmmTest3x2(PhantomData<(TA, TB, TI)>) +where + TA: Datum + Copy + fmt::Debug + AsPrimitive, + TB: Datum + Copy + fmt::Debug + AsPrimitive, + TI: LADatum + ScaleShiftAndRound; + +#[cfg(test)] +unsafe impl Send for GenericMmmTest3x2 +where + TA: Datum + Copy + fmt::Debug + AsPrimitive, + TB: Datum + Copy + fmt::Debug + AsPrimitive, + TI: LADatum + ScaleShiftAndRound, +{ +} + +#[cfg(test)] +unsafe impl Sync for GenericMmmTest3x2 +where + TA: Datum + Copy + fmt::Debug + AsPrimitive, + TB: Datum + Copy + fmt::Debug + AsPrimitive, + TI: LADatum + ScaleShiftAndRound, +{ +} + +#[cfg(test)] +impl MatMatMulKer for GenericMmmTest3x2 +where + TA: Datum + Copy + fmt::Debug + AsPrimitive, + TB: Datum + Copy + fmt::Debug + AsPrimitive, + TI: LADatum + ScaleShiftAndRound, + usize: AsPrimitive, +{ + #[inline(always)] + fn name() -> &'static str { + match TI::datum_type() { + DatumType::F16 => "generic_f16_3x2", + DatumType::F32 => "generic_f32_3x2", + DatumType::I32 => "generic_i32_3x2", + DatumType::F64 => "generic_f64_3x2", + _ => panic!(), + } + } + #[inline(always)] + fn mr() -> usize { + 3 + } + #[inline(always)] + fn nr() -> usize { + 2 + } + fn end_padding_packed_a() -> usize { + 0 + } + fn end_padding_packed_b() -> usize { + 0 + } + #[inline(always)] + fn alignment_bytes_packed_a() -> usize { + std::mem::size_of::() + } + #[inline(always)] + fn alignment_bytes_packed_b() -> usize { + std::mem::size_of::() + } + #[inline(never)] + fn kernel(spec: &[FusedKerSpec]) -> isize { + unsafe { + let mut ab = [[TI::zero(); 2]; 3]; + let mut pnl = spec.as_ptr(); + loop { + if pnl.is_null() { + break; + } + match *pnl { + FusedKerSpec::Done => break, + FusedKerSpec::Clear => ab = std::mem::zeroed(), + FusedKerSpec::ScalarAdd(a) => scalar!(ab, a, |a, b| a + b), + FusedKerSpec::ScalarMul(a) => scalar!(ab, a, |a, b| a * b), + FusedKerSpec::ScalarMin(m) => scalar!(ab, m, |a, b| if a < b { a } else { b }), + FusedKerSpec::ScalarMax(m) => scalar!(ab, m, |a, b| if a > b { a } else { b }), + FusedKerSpec::ScalarSub(m) => scalar!(ab, m, |a, b| a - b), + FusedKerSpec::ScalarSubF(m) => scalar!(ab, m, |a, b| b - a), + FusedKerSpec::PerRowMin(m) => per_row!(ab, m, |a, b| if a < b { a } else { b }), + FusedKerSpec::PerRowMax(m) => per_row!(ab, m, |a, b| if a > b { a } else { b }), + FusedKerSpec::PerRowAdd(m) => per_row!(ab, m, |a, b| a + b), + FusedKerSpec::PerRowMul(m) => per_row!(ab, m, |a, b| a * b), + FusedKerSpec::PerRowSub(m) => per_row!(ab, m, |a, b| a - b), + FusedKerSpec::PerRowSubF(m) => per_row!(ab, m, |a, b| b - a), + FusedKerSpec::PerColMin(m) => per_col!(ab, m, |a, b| if a < b { a } else { b }), + FusedKerSpec::PerColMax(m) => per_col!(ab, m, |a, b| if a > b { a } else { b }), + FusedKerSpec::PerColAdd(m) => per_col!(ab, m, |a, b| a + b), + FusedKerSpec::PerColMul(m) => per_col!(ab, m, |a, b| a * b), + FusedKerSpec::PerColSub(m) => per_col!(ab, m, |a, b| a - b), + FusedKerSpec::PerColSubF(m) => per_col!(ab, m, |a, b| b - a), + FusedKerSpec::AddRowColProducts(rows, cols) => { + for i in 0..3 { + for j in 0..2 { + ab[i][j] += *rows.add(i) * *cols.add(j); + } + } + } + FusedKerSpec::AddUnicast(tile) => add_unicast::(&tile, &mut ab), + FusedKerSpec::ShiftLeft(shift) => { + for i in 0..3 { + for j in 0..2 { + ab[i][j] = ab[i][j].q_shl(shift); + } + } + } + FusedKerSpec::RoundingShiftRight(shift, rp) => { + for i in 0..3 { + for j in 0..2 { + ab[i][j] = ab[i][j].q_shr(shift, rp) + } + } + } + FusedKerSpec::QScale(shift, rp, mult) => { + for i in 0..3 { + for j in 0..2 { + ab[i][j] = + ab[i][j].q_scale(Scaler::from_fuse_params(shift, rp, mult)); + } + } + } + FusedKerSpec::AddMatMul { k, pa, pb, .. } => { + let a = pa as *const TA; + let b = pb as *const TB; + for i in 0..k { + let a = std::slice::from_raw_parts(a.offset(3 * i as isize), 3); + let b = std::slice::from_raw_parts(b.offset(2 * i as isize), 2); + ab[0][0] += a[0].as_() * b[0].as_(); + ab[0][1] += a[0].as_() * b[1].as_(); + ab[1][0] += a[1].as_() * b[0].as_(); + ab[1][1] += a[1].as_() * b[1].as_(); + ab[2][0] += a[2].as_() * b[0].as_(); + ab[2][1] += a[2].as_() * b[1].as_(); + } + } + FusedKerSpec::Store(tile) => store(&tile, &ab), + } + pnl = pnl.add(1); + } + } + 0 + } +} + +unsafe fn store_t(tile: &OutputStoreKer, ab: &[AB]) +where + TC: Copy, + AB: AsRef<[TI]> + fmt::Debug, +{ + for i in 0usize..ab.len() { + for j in 0usize..ab[0].as_ref().len() { + let loc: *mut TC = tile + .ptr + .offset(tile.row_byte_stride * i as isize + tile.col_byte_stride * j as isize) + as _; + let val: *const TC = (&ab[i].as_ref()[j]) as *const TI as _; + *loc = *val + } + } +} + +unsafe fn store(tile: &OutputStoreKer, ab: &[AB]) +where + AB: AsRef<[TI]> + fmt::Debug, +{ + match tile.item_size { + 1 => store_t::(tile, ab), + 2 => store_t::(tile, ab), + 4 => store_t::(tile, ab), + 8 => store_t::(tile, ab), + _ => unimplemented!(), + } +} + +unsafe fn add_unicast(tile: &OutputStoreKer, ab: &mut [AB]) +where + TI: LADatum + ops::AddAssign, + AB: AsMut<[TI]> + fmt::Debug, +{ + if tile.item_size == TI::datum_type().size_of() { + for i in 0usize..ab.len() { + for j in 0usize..ab[0].as_mut().len() { + let value: *const TI = tile + .ptr + .offset(tile.row_byte_stride * i as isize + tile.col_byte_stride * j as isize) + as _; + ab[i].as_mut()[j] += *value; + } + } + } else if TI::datum_type() == i32::datum_type() && tile.item_size == 1 { + for i in 0usize..ab.len() { + for j in 0usize..ab[0].as_mut().len() { + let value: i8 = *(tile + .ptr + .offset(tile.row_byte_stride * i as isize + tile.col_byte_stride * j as isize) + as *const i8); + let acc: *mut i32 = ab[i].as_mut().as_mut_ptr().add(j) as *mut i32; + *acc += value as i32; + } + } + } else { + unimplemented!("Missing AddUnicast type"); + } +} + +#[allow(non_camel_case_types)] +pub type generic_f16_4x4 = GenericMmm4x4; +test_mmm_kernel_f16!(generic_f16_4x4, true); + +#[allow(non_camel_case_types)] +pub type generic_f32_4x4 = GenericMmm4x4; +test_mmm_kernel_f32!(generic_f32_4x4, true); + +#[allow(non_camel_case_types)] +pub type generic_f64_4x4 = GenericMmm4x4; +test_mmm_kernel_f64!(generic_f64_4x4, true); + +#[allow(non_camel_case_types)] +pub type generic_i32_4x4 = GenericMmm4x4; +test_mmm_kernel_i32!(generic_i32_4x4, true); + +#[allow(non_camel_case_types)] +pub type generic_f32_4x1 = GenericMmm4x1; +test_mmm_kernel_f32!(generic_f32_4x1, true); + +#[allow(non_camel_case_types)] +pub type generic_f64_4x1 = GenericMmm4x1; +test_mmm_kernel_f64!(generic_f64_4x1, true); + +#[allow(non_camel_case_types)] +pub type generic_i32_4x1 = GenericMmm4x1; +test_mmm_kernel_i32!(generic_i32_4x1, true); + +#[cfg(test)] +#[allow(non_camel_case_types)] +type generic_f32_3x2 = GenericMmmTest3x2; +test_mmm_kernel_f32!(generic_f32_3x2, true); + +#[cfg(test)] +#[allow(non_camel_case_types)] +type generic_i32_3x2 = GenericMmmTest3x2; +test_mmm_kernel_i32!(generic_i32_3x2, true); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic/rounding.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic/rounding.rs new file mode 100644 index 0000000..09525cb --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic/rounding.rs @@ -0,0 +1,534 @@ +use crate::frame::mmm::*; +use std::hash::{Hash, Hasher}; +use std::ops::Mul; +use tract_data::prelude::f16; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Scaler { + pub scale: f32, + pub mult: Option, + pub shift: isize, + pub policy: RoundingPolicy, +} + +impl Eq for Scaler {} + +#[allow(clippy::derived_hash_with_manual_eq)] +impl Hash for Scaler { + fn hash(&self, state: &mut H) + where + H: Hasher, + { + Hash::hash(&self.scale.to_bits(), state) + } +} + +impl Scaler { + pub fn new(scale: f32, policy: RoundingPolicy) -> Self { + let (mult, shift) = Self::convert_scale_to_mult_shift(scale); + Self { + scale, + mult, + shift, + policy, + } + } + + pub fn as_fused_spec(&self) -> FusedSpec<'_> { + if let Some(multiplier) = self.mult { + FusedSpec::QScale(self.shift, self.policy, multiplier) + } else if self.shift > 0 { + FusedSpec::RoundingShiftRight(self.shift as usize, self.policy) + } else { + FusedSpec::ShiftLeft((-self.shift) as usize) + } + } + + // FIXME: Only to avoid fused op breaking + pub fn from_fuse_params(shift: isize, policy: RoundingPolicy, mult: i32) -> Self { + let scale = mult as f32 * 2f32.powi(-(31 + shift as i32)); + Self { + scale, + mult: Some(mult), + shift, + policy, + } + } + + #[inline] + // This function convert a scale (actually a fraction of two integers Q/D) + // into an integer multiplier and a shift (the multiplier being 1/2D in Q0_31). + fn convert_scale_to_mult_shift(scale: f32) -> (Option, isize) { + // Zero is a special case to handle + if scale == 0.0 { + return (None, 0); + } + + // Convert f32 to bits representation with the following pattern + // Bit | 31 | 30-23 | 22-0 | + // | Sign | Exponent | Fraction | + let scale_bits = scale.to_bits(); + + // Get actual value of the exponent + let current_exponent = (scale_bits >> 23) & 0xff; + + // Extract fractional part of the float with: + // - 0x007fffff that represents the mask of the 23 lower bits (fractional part) + // (partial because it doesn't include the hidden bit (24) of the float representation) + let partial_frac = scale_bits & 0x007fffff; + + if partial_frac == 0 { + let shift = 127 - current_exponent as isize; + (None, shift) + } else { + // We add 0x800000 that represents the hidden bit set to one. + // Here the frac is encoded as a Q8_23. + let frac = partial_frac | 0x800000; + + // We rescale the result to be in Q0_31 + // We should have shifted the result by 8 but the frac value is in [1.0, 2.0) + // so we cannot do that (we would need one bit for the integer). + // Instead we devide the frac by two to be in [0.5, 1.0) in Q0_31 + // which lead to a shift of (8-1 = 7). + let half_frac = (frac << 7) as i32; + + // Compute the actual value of the shift + // Here, we remove one as half_frac needs to be multiplied by 2. + let shift = 127 - current_exponent as isize - 1; + (Some(half_frac), shift) + } + } +} + +impl Mul for Scaler { + type Output = f16; + + #[inline] + fn mul(self, rhs: f16) -> Self::Output { + f16::from_f32(self.scale) * rhs + } +} + +impl Mul for Scaler { + type Output = f32; + + #[inline] + fn mul(self, rhs: f32) -> Self::Output { + self.scale * rhs + } +} + +impl Mul for Scaler { + type Output = f64; + + #[inline] + fn mul(self, rhs: f64) -> Self::Output { + self.scale as f64 * rhs + } +} + +impl Mul for f16 { + type Output = f16; + + #[inline] + fn mul(self, rhs: Scaler) -> Self::Output { + rhs * self + } +} + +impl Mul for f32 { + type Output = f32; + + #[inline] + fn mul(self, rhs: Scaler) -> Self::Output { + rhs * self + } +} + +impl Mul for f64 { + type Output = f64; + + #[inline] + fn mul(self, rhs: Scaler) -> Self::Output { + rhs * self + } +} + +impl Mul for Scaler { + type Output = i32; + + #[inline] + fn mul(self, rhs: i32) -> Self::Output { + let (val, shift) = if let Some(multiplier) = self.mult { + (multiplier as i64 * rhs as i64, self.shift + 31) + } else { + (rhs as i64, self.shift) + }; + + // Round according to rounding policy + use RoundingPolicy::*; + if shift > 0 { + let half: i64 = 1 << (shift - 1); + let nudge: i64 = match self.policy { + Zero => -1, + MinusInf => -((val >= 0) as i64), + PlusInf => -((val <= 0) as i64), + Away => 0, + Even => ((val.abs() >> shift) & 0x1) - 1, + Odd => -((val.abs() >> shift) & 0x1), + _ => panic!(), + }; + + (val.signum() * ((val.abs() + half + nudge) >> shift)) as i32 + } else { + (val << -shift) as i32 + } + } +} + +impl Mul for i32 { + type Output = i32; + + #[inline] + fn mul(self, rhs: Scaler) -> Self::Output { + rhs * self + } +} + +pub trait ScaleShiftAndRound { + fn q_scale(self, scaler: Scaler) -> Self; + fn q_shl(self, shift: usize) -> Self; + fn q_shr(self, shift: usize, rp: RoundingPolicy) -> Self; +} + +impl ScaleShiftAndRound for f64 { + fn q_scale(self, scaler: Scaler) -> Self { + self * scaler + } + fn q_shl(self, shift: usize) -> Self { + self * 2f64.powi(shift as i32) + } + fn q_shr(self, shift: usize, _rp: RoundingPolicy) -> Self { + self * 2f64.powi(-(shift as i32)) + } +} + +impl ScaleShiftAndRound for f32 { + fn q_scale(self, scaler: Scaler) -> Self { + self * scaler + } + fn q_shl(self, shift: usize) -> Self { + self * 2f32.powi(shift as i32) + } + fn q_shr(self, shift: usize, _rp: RoundingPolicy) -> Self { + self * 2f32.powi(-(shift as i32)) + } +} + +impl ScaleShiftAndRound for f16 { + fn q_scale(self, scaler: Scaler) -> Self { + self * scaler + } + fn q_shl(self, shift: usize) -> Self { + self * f16::from_f32(2f32.powi(shift as i32)) + } + fn q_shr(self, shift: usize, _rp: RoundingPolicy) -> Self { + self * f16::from_f32(2f32.powi(-(shift as i32))) + } +} + +impl ScaleShiftAndRound for i32 { + fn q_scale(self, scaler: Scaler) -> Self { + self * scaler + } + fn q_shr(self, shift: usize, rp: RoundingPolicy) -> Self { + use RoundingPolicy::*; + let half: i32 = 1 << (shift - 1); + let nudge: i32 = match rp { + Zero => -1, + MinusInf => -((self >= 0) as i32), + PlusInf => -((self <= 0) as i32), + Away => 0, + Even => ((self.abs() >> shift) & 0x1) - 1, + Odd => -((self.abs() >> shift) & 0x1), + _ => panic!(), + }; + self.signum() * ((self.abs() + half + nudge) >> shift) + } + fn q_shl(self, shift: usize) -> Self { + self << shift + } +} + +// 6 / 4 -> 1.5 -> arrondi: 2. rien a faire +// 2 / 4 -> 0.5 -> arrondi: 1. veut 0 -> nudge = -1 + +#[cfg(test)] +mod test { + use super::RoundingPolicy::*; + use super::*; + + #[test] + fn test_scale_rounding_f32() { + assert_eq!(0f32.q_scale(Scaler::new(0.5, Zero)), 0.0); + assert_eq!(1f32.q_scale(Scaler::new(0.5, Zero)), 0.5); + assert_eq!(2f32.q_scale(Scaler::new(0.5, Zero)), 1.0); + assert_eq!(3f32.q_scale(Scaler::new(0.5, Zero)), 1.5); + assert_eq!((-1f32).q_scale(Scaler::new(0.5, Zero)), -0.5); + assert_eq!((-2f32).q_scale(Scaler::new(0.5, Zero)), -1.0); + assert_eq!((-3f32).q_scale(Scaler::new(0.5, Zero)), -1.5); + } + + #[test] + fn test_shift_rounding_zero() { + assert_eq!(0i32.q_shr(1, Zero), 0); + assert_eq!(1i32.q_shr(1, Zero), 0); + assert_eq!(2i32.q_shr(1, Zero), 1); + assert_eq!(3i32.q_shr(1, Zero), 1); + assert_eq!(0i32.q_shr(2, Zero), 0); + assert_eq!(1i32.q_shr(2, Zero), 0); + assert_eq!(2i32.q_shr(2, Zero), 0); + assert_eq!(3i32.q_shr(2, Zero), 1); + assert_eq!(4i32.q_shr(2, Zero), 1); + assert_eq!(5i32.q_shr(2, Zero), 1); + assert_eq!(6i32.q_shr(2, Zero), 1); + assert_eq!((-1i32).q_shr(2, Zero), 0); + assert_eq!((-2i32).q_shr(2, Zero), 0); + assert_eq!((-3i32).q_shr(2, Zero), -1); + assert_eq!((-4i32).q_shr(2, Zero), -1); + assert_eq!((-5i32).q_shr(2, Zero), -1); + assert_eq!((-6i32).q_shr(2, Zero), -1); + } + + #[test] + fn test_scale_rounding_zero() { + assert_eq!(0i32.q_scale(Scaler::new(0.5, Zero)), 0); + assert_eq!(1i32.q_scale(Scaler::new(0.5, Zero)), 0); + assert_eq!(2i32.q_scale(Scaler::new(0.5, Zero)), 1); + assert_eq!(3i32.q_scale(Scaler::new(0.5, Zero)), 1); + assert_eq!((-1i32).q_scale(Scaler::new(0.5, Zero)), 0); + assert_eq!((-2i32).q_scale(Scaler::new(0.5, Zero)), -1); + assert_eq!((-3i32).q_scale(Scaler::new(0.5, Zero)), -1); + assert_eq!(2i32.q_scale(Scaler::new(0.25, Zero)), 0); + assert_eq!(3i32.q_scale(Scaler::new(0.25, Zero)), 1); + assert_eq!(4i32.q_scale(Scaler::new(0.25, Zero)), 1); + assert_eq!(5i32.q_scale(Scaler::new(0.25, Zero)), 1); + assert_eq!(6i32.q_scale(Scaler::new(0.25, Zero)), 1); + assert_eq!((-2i32).q_scale(Scaler::new(0.25, Zero)), 0); + assert_eq!((-3i32).q_scale(Scaler::new(0.25, Zero)), -1); + assert_eq!((-4i32).q_scale(Scaler::new(0.25, Zero)), -1); + assert_eq!((-5i32).q_scale(Scaler::new(0.25, Zero)), -1); + assert_eq!((-6i32).q_scale(Scaler::new(0.25, Zero)), -1); + } + + #[test] + fn test_shift_rounding_away() { + assert_eq!(0i32.q_shr(1, Away), 0); + assert_eq!(1i32.q_shr(1, Away), 1); + assert_eq!(2i32.q_shr(1, Away), 1); + assert_eq!(3i32.q_shr(1, Away), 2); + assert_eq!(0i32.q_shr(2, Away), 0); + assert_eq!(1i32.q_shr(2, Away), 0); + assert_eq!(2i32.q_shr(2, Away), 1); + assert_eq!(3i32.q_shr(2, Away), 1); + assert_eq!(4i32.q_shr(2, Away), 1); + assert_eq!(5i32.q_shr(2, Away), 1); + assert_eq!(6i32.q_shr(2, Away), 2); + assert_eq!((-1i32).q_shr(2, Away), 0); + assert_eq!((-2i32).q_shr(2, Away), -1); + assert_eq!((-3i32).q_shr(2, Away), -1); + assert_eq!((-4i32).q_shr(2, Away), -1); + assert_eq!((-5i32).q_shr(2, Away), -1); + assert_eq!((-6i32).q_shr(2, Away), -2); + } + + #[test] + fn test_scale_rounding_away() { + assert_eq!(0i32.q_scale(Scaler::new(0.5, Away)), 0); + assert_eq!(1i32.q_scale(Scaler::new(0.5, Away)), 1); + assert_eq!(2i32.q_scale(Scaler::new(0.5, Away)), 1); + assert_eq!(3i32.q_scale(Scaler::new(0.5, Away)), 2); + assert_eq!((-1i32).q_scale(Scaler::new(0.5, Away)), -1); + assert_eq!((-2i32).q_scale(Scaler::new(0.5, Away)), -1); + assert_eq!((-3i32).q_scale(Scaler::new(0.5, Away)), -2); + assert_eq!(2i32.q_scale(Scaler::new(0.25, Away)), 1); + assert_eq!(3i32.q_scale(Scaler::new(0.25, Away)), 1); + assert_eq!(4i32.q_scale(Scaler::new(0.25, Away)), 1); + assert_eq!(5i32.q_scale(Scaler::new(0.25, Away)), 1); + assert_eq!(6i32.q_scale(Scaler::new(0.25, Away)), 2); + assert_eq!((-2i32).q_scale(Scaler::new(0.25, Away)), -1); + assert_eq!((-3i32).q_scale(Scaler::new(0.25, Away)), -1); + assert_eq!((-4i32).q_scale(Scaler::new(0.25, Away)), -1); + assert_eq!((-5i32).q_scale(Scaler::new(0.25, Away)), -1); + assert_eq!((-6i32).q_scale(Scaler::new(0.25, Away)), -2); + } + + #[test] + fn test_shift_rounding_plus_inf() { + assert_eq!(0i32.q_shr(1, PlusInf), 0); + assert_eq!(1i32.q_shr(1, PlusInf), 1); + assert_eq!(2i32.q_shr(1, PlusInf), 1); + assert_eq!(3i32.q_shr(1, PlusInf), 2); + assert_eq!(0i32.q_shr(2, PlusInf), 0); + assert_eq!(1i32.q_shr(2, PlusInf), 0); + assert_eq!(2i32.q_shr(2, PlusInf), 1); + assert_eq!(3i32.q_shr(2, PlusInf), 1); + assert_eq!(4i32.q_shr(2, PlusInf), 1); + assert_eq!(5i32.q_shr(2, PlusInf), 1); + assert_eq!(6i32.q_shr(2, PlusInf), 2); + assert_eq!((-1i32).q_shr(2, PlusInf), 0); + assert_eq!((-2i32).q_shr(2, PlusInf), 0); + assert_eq!((-3i32).q_shr(2, PlusInf), -1); + assert_eq!((-4i32).q_shr(2, PlusInf), -1); + assert_eq!((-5i32).q_shr(2, PlusInf), -1); + assert_eq!((-6i32).q_shr(2, PlusInf), -1); + } + + #[test] + fn test_scale_rounding_plus_inf() { + assert_eq!(0i32.q_scale(Scaler::new(0.5, PlusInf)), 0); + assert_eq!(1i32.q_scale(Scaler::new(0.5, PlusInf)), 1); + assert_eq!(2i32.q_scale(Scaler::new(0.5, PlusInf)), 1); + assert_eq!(3i32.q_scale(Scaler::new(0.5, PlusInf)), 2); + assert_eq!((-1i32).q_scale(Scaler::new(0.5, PlusInf)), 0); + assert_eq!((-2i32).q_scale(Scaler::new(0.5, PlusInf)), -1); + assert_eq!((-3i32).q_scale(Scaler::new(0.5, PlusInf)), -1); + assert_eq!(2i32.q_scale(Scaler::new(0.25, PlusInf)), 1); + assert_eq!(3i32.q_scale(Scaler::new(0.25, PlusInf)), 1); + assert_eq!(4i32.q_scale(Scaler::new(0.25, PlusInf)), 1); + assert_eq!(5i32.q_scale(Scaler::new(0.25, PlusInf)), 1); + assert_eq!(6i32.q_scale(Scaler::new(0.25, PlusInf)), 2); + assert_eq!((-2i32).q_scale(Scaler::new(0.25, PlusInf)), 0); + assert_eq!((-3i32).q_scale(Scaler::new(0.25, PlusInf)), -1); + assert_eq!((-4i32).q_scale(Scaler::new(0.25, PlusInf)), -1); + assert_eq!((-5i32).q_scale(Scaler::new(0.25, PlusInf)), -1); + assert_eq!((-6i32).q_scale(Scaler::new(0.25, PlusInf)), -1); + } + + #[test] + fn test_shift_rounding_minus_inf() { + assert_eq!(0i32.q_shr(1, MinusInf), 0); + assert_eq!(1i32.q_shr(1, MinusInf), 0); + assert_eq!(2i32.q_shr(1, MinusInf), 1); + assert_eq!(3i32.q_shr(1, MinusInf), 1); + assert_eq!(0i32.q_shr(2, MinusInf), 0); + assert_eq!(1i32.q_shr(2, MinusInf), 0); + assert_eq!(2i32.q_shr(2, MinusInf), 0); + assert_eq!(3i32.q_shr(2, MinusInf), 1); + assert_eq!(4i32.q_shr(2, MinusInf), 1); + assert_eq!(5i32.q_shr(2, MinusInf), 1); + assert_eq!(6i32.q_shr(2, MinusInf), 1); + assert_eq!((-1i32).q_shr(2, MinusInf), 0); + assert_eq!((-2i32).q_shr(2, MinusInf), -1); + assert_eq!((-3i32).q_shr(2, MinusInf), -1); + assert_eq!((-4i32).q_shr(2, MinusInf), -1); + assert_eq!((-5i32).q_shr(2, MinusInf), -1); + assert_eq!((-6i32).q_shr(2, MinusInf), -2); + } + + #[test] + fn test_scale_rounding_minus_inf() { + assert_eq!(0i32.q_scale(Scaler::new(0.5, MinusInf)), 0); + assert_eq!(1i32.q_scale(Scaler::new(0.5, MinusInf)), 0); + assert_eq!(2i32.q_scale(Scaler::new(0.5, MinusInf)), 1); + assert_eq!(3i32.q_scale(Scaler::new(0.5, MinusInf)), 1); + assert_eq!((-1i32).q_scale(Scaler::new(0.5, MinusInf)), -1); + assert_eq!((-2i32).q_scale(Scaler::new(0.5, MinusInf)), -1); + assert_eq!((-3i32).q_scale(Scaler::new(0.5, MinusInf)), -2); + assert_eq!(2i32.q_scale(Scaler::new(0.25, MinusInf)), 0); + assert_eq!(3i32.q_scale(Scaler::new(0.25, MinusInf)), 1); + assert_eq!(4i32.q_scale(Scaler::new(0.25, MinusInf)), 1); + assert_eq!(5i32.q_scale(Scaler::new(0.25, MinusInf)), 1); + assert_eq!(6i32.q_scale(Scaler::new(0.25, MinusInf)), 1); + assert_eq!((-2i32).q_scale(Scaler::new(0.25, MinusInf)), -1); + assert_eq!((-3i32).q_scale(Scaler::new(0.25, MinusInf)), -1); + assert_eq!((-4i32).q_scale(Scaler::new(0.25, MinusInf)), -1); + assert_eq!((-5i32).q_scale(Scaler::new(0.25, MinusInf)), -1); + assert_eq!((-6i32).q_scale(Scaler::new(0.25, MinusInf)), -2); + //assert_eq!((-9i32).q_scale(ONE_OVER_TWO_IN_Q0_30, 5, MinusInf), 0); + } + + #[test] + fn test_shift_rounding_even() { + assert_eq!(0i32.q_shr(1, Even), 0); + assert_eq!(1i32.q_shr(1, Even), 0); + assert_eq!(2i32.q_shr(1, Even), 1); + assert_eq!(3i32.q_shr(1, Even), 2); + assert_eq!(0i32.q_shr(2, Even), 0); + assert_eq!(1i32.q_shr(2, Even), 0); + assert_eq!(2i32.q_shr(2, Even), 0); + assert_eq!(3i32.q_shr(2, Even), 1); + assert_eq!(4i32.q_shr(2, Even), 1); + assert_eq!(5i32.q_shr(2, Even), 1); + assert_eq!(6i32.q_shr(2, Even), 2); + assert_eq!((-1i32).q_shr(2, Even), 0); + assert_eq!((-2i32).q_shr(2, Even), 0); + assert_eq!((-3i32).q_shr(2, Even), -1); + assert_eq!((-4i32).q_shr(2, Even), -1); + assert_eq!((-5i32).q_shr(2, Even), -1); + assert_eq!((-6i32).q_shr(2, Even), -2); + } + + #[test] + fn test_scale_rounding_even() { + assert_eq!(0i32.q_scale(Scaler::new(0.5, Even)), 0); + assert_eq!(1i32.q_scale(Scaler::new(0.5, Even)), 0); + assert_eq!(2i32.q_scale(Scaler::new(0.5, Even)), 1); + assert_eq!(3i32.q_scale(Scaler::new(0.5, Even)), 2); + assert_eq!((-1i32).q_scale(Scaler::new(0.5, Even)), 0); + assert_eq!((-2i32).q_scale(Scaler::new(0.5, Even)), -1); + assert_eq!((-3i32).q_scale(Scaler::new(0.5, Even)), -2); + assert_eq!(2i32.q_scale(Scaler::new(0.25, Even)), 0); + assert_eq!(3i32.q_scale(Scaler::new(0.25, Even)), 1); + assert_eq!(4i32.q_scale(Scaler::new(0.25, Even)), 1); + assert_eq!(5i32.q_scale(Scaler::new(0.25, Even)), 1); + assert_eq!(6i32.q_scale(Scaler::new(0.25, Even)), 2); + assert_eq!((-2i32).q_scale(Scaler::new(0.25, Even)), 0); + assert_eq!((-3i32).q_scale(Scaler::new(0.25, Even)), -1); + assert_eq!((-4i32).q_scale(Scaler::new(0.25, Even)), -1); + assert_eq!((-5i32).q_scale(Scaler::new(0.25, Even)), -1); + assert_eq!((-6i32).q_scale(Scaler::new(0.25, Even)), -2); + } + + #[test] + fn test_shift_rounding_odd() { + assert_eq!(0i32.q_shr(1, Odd), 0); + assert_eq!(1i32.q_shr(1, Odd), 1); + assert_eq!(2i32.q_shr(1, Odd), 1); + assert_eq!(3i32.q_shr(1, Odd), 1); + assert_eq!(0i32.q_shr(2, Odd), 0); + assert_eq!(1i32.q_shr(2, Odd), 0); + assert_eq!(2i32.q_shr(2, Odd), 1); + assert_eq!(3i32.q_shr(2, Odd), 1); + assert_eq!(4i32.q_shr(2, Odd), 1); + assert_eq!(5i32.q_shr(2, Odd), 1); + assert_eq!(6i32.q_shr(2, Odd), 1); + assert_eq!((-1i32).q_shr(2, Odd), 0); + assert_eq!((-2i32).q_shr(2, Odd), -1); + assert_eq!((-3i32).q_shr(2, Odd), -1); + assert_eq!((-4i32).q_shr(2, Odd), -1); + assert_eq!((-5i32).q_shr(2, Odd), -1); + assert_eq!((-6i32).q_shr(2, Odd), -1); + } + + #[test] + fn test_scale_rounding_odd() { + assert_eq!(0i32.q_scale(Scaler::new(0.5, Odd)), 0); + assert_eq!(1i32.q_scale(Scaler::new(0.5, Odd)), 1); + assert_eq!(2i32.q_scale(Scaler::new(0.5, Odd)), 1); + assert_eq!(3i32.q_scale(Scaler::new(0.5, Odd)), 1); + assert_eq!((-1i32).q_scale(Scaler::new(0.5, Odd)), -1); + assert_eq!((-2i32).q_scale(Scaler::new(0.5, Odd)), -1); + assert_eq!((-3i32).q_scale(Scaler::new(0.5, Odd)), -1); + assert_eq!(2i32.q_scale(Scaler::new(0.25, Odd)), 1); + assert_eq!(3i32.q_scale(Scaler::new(0.25, Odd)), 1); + assert_eq!(4i32.q_scale(Scaler::new(0.25, Odd)), 1); + assert_eq!(5i32.q_scale(Scaler::new(0.25, Odd)), 1); + assert_eq!(6i32.q_scale(Scaler::new(0.25, Odd)), 1); + assert_eq!((-2i32).q_scale(Scaler::new(0.25, Odd)), -1); + assert_eq!((-3i32).q_scale(Scaler::new(0.25, Odd)), -1); + assert_eq!((-4i32).q_scale(Scaler::new(0.25, Odd)), -1); + assert_eq!((-5i32).q_scale(Scaler::new(0.25, Odd)), -1); + assert_eq!((-6i32).q_scale(Scaler::new(0.25, Odd)), -1); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic/sigmoid.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic/sigmoid.rs new file mode 100644 index 0000000..c4d8a81 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic/sigmoid.rs @@ -0,0 +1,142 @@ +#![allow(clippy::excessive_precision)] +use crate::frame::element_wise::ElementWiseKer; +use tract_data::internal::*; + +pub fn ssigmoid(x: f32) -> f32 { + const LOW: f32 = -18.6; + const HIGH: f32 = -LOW; + + const ALPHA_13: f32 = -4.433153405e-18; + const ALPHA_11: f32 = 1.169974371e-14; + const ALPHA_9: f32 = -1.875289645e-11; + const ALPHA_7: f32 = 4.257889523e-8; + const ALPHA_5: f32 = 0.00004811817576; + const ALPHA_3: f32 = 0.008163842030; + const ALPHA_1: f32 = 0.2499999971; + const BETA_6: f32 = 3.922935744e-6; + const BETA_4: f32 = 0.001524872358; + const BETA_2: f32 = 0.1159886749; + const BETA_0: f32 = 1.0; + + let x = x.clamp(LOW, HIGH); + + let x2 = x * x; + + let p = ALPHA_13; + let p = x2 * p + ALPHA_11; + let p = x2 * p + ALPHA_9; + let p = x2 * p + ALPHA_7; + let p = x2 * p + ALPHA_5; + let p = x2 * p + ALPHA_3; + let p = x2 * p + ALPHA_1; + let p = p * x; + + let q = BETA_6; + let q = x2 * q + BETA_4; + let q = x2 * q + BETA_2; + let q = x2 * q + BETA_0; + + p / q + 0.5 +} + +pub fn hsigmoid(x: f16) -> f16 { + /* + * (x (0.249895 + x^2 (0.00400222 - 0.0000124702 x^2))) + * / + * (1. + 0.098734 x^2) + */ + + const LOW: f16 = f16::from_f32_const(-6.92); + const HIGH: f16 = f16::from_f32_const(6.92); + + const ALPHA_5: f16 = f16::from_f32_const(-0.0000124702); + const ALPHA_3: f16 = f16::from_f32_const(0.00400222); + const ALPHA_1: f16 = f16::from_f32_const(0.249895); + + const BETA_2: f16 = f16::from_f32_const(0.098734); + const BETA_0: f16 = f16::from_f32_const(1.0); + + let x = x.clamp(LOW, HIGH); + + let x2 = x * x; + + let p = ALPHA_5; + let p = x2 * p + ALPHA_3; + let p = x2 * p + ALPHA_1; + let p = p * x; + + let q = BETA_2; + let q = x2 * q + BETA_0; + + p / q + f16::from_f32_const(0.5) +} + +#[derive(Clone, Debug)] +pub struct SSigmoid4; + +impl ElementWiseKer for SSigmoid4 { + fn name() -> &'static str { + "generic" + } + + fn alignment_bytes() -> usize { + 16 + } + + fn alignment_items() -> usize { + 4 + } + + fn nr() -> usize { + 4 + } + + fn run(x: &mut [f32]) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px = ssigmoid(*px)) + } +} + +#[derive(Clone, Debug)] +pub struct HSigmoid8; + +impl ElementWiseKer for HSigmoid8 { + fn name() -> &'static str { + "generic" + } + + fn alignment_bytes() -> usize { + 16 + } + + fn alignment_items() -> usize { + 4 + } + + fn nr() -> usize { + 8 + } + + fn run(x: &mut [f16]) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px = hsigmoid(*px)) + } +} + +#[cfg(test)] +#[macro_use] +pub mod s { + sigmoid_frame_tests!(true, f32, crate::generic::sigmoid::SSigmoid4); +} + +#[cfg(test)] +#[macro_use] +pub mod h { + sigmoid_frame_tests!( + true, + tract_data::internal::f16, + crate::generic::sigmoid::HSigmoid8 + ); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic/tanh.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic/tanh.rs new file mode 100644 index 0000000..500a3cc --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/generic/tanh.rs @@ -0,0 +1,137 @@ +#![allow(clippy::excessive_precision)] +use crate::frame::element_wise::ElementWiseKer; +use tract_data::internal::*; + +pub fn stanh(x: f32) -> f32 { + const LOW: f32 = -8.9; + const HIGH: f32 = 8.9; + + const ALPHA_13: f32 = -8.488492677e-14; + const ALPHA_11: f32 = 5.277853000e-11; + const ALPHA_9: f32 = -2.022500419e-8; + const ALPHA_7: f32 = 0.00001115424833; + const ALPHA_5: f32 = 0.003103950131; + const ALPHA_3: f32 = 0.1308400453; + const ALPHA_1: f32 = 0.9999999934; + + const BETA_6: f32 = 0.0002546136580; + const BETA_4: f32 = 0.02449515379; + const BETA_2: f32 = 0.4641733162; + const BETA_0: f32 = 1.0; + + let x = x.clamp(LOW, HIGH); + + let x2 = x * x; + + let p = ALPHA_13; + let p = x2 * p + ALPHA_11; + let p = x2 * p + ALPHA_9; + let p = x2 * p + ALPHA_7; + let p = x2 * p + ALPHA_5; + let p = x2 * p + ALPHA_3; + let p = x2 * p + ALPHA_1; + let p = p * x; + + let q = BETA_6; + let q = x2 * q + BETA_4; + let q = x2 * q + BETA_2; + let q = x2 * q + BETA_0; + + p / q +} + +pub fn htanh(x: f16) -> f16 { + const LOW: f16 = f16::from_f32_const(-3.84); + const HIGH: f16 = f16::from_f32_const(3.84); + + const ALPHA_3: f16 = f16::from_f32_const(0.082654955); + const ALPHA_1: f16 = f16::from_f32_const(0.99963124); + + const BETA_4: f16 = f16::from_f32_const(0.0065383179); + const BETA_2: f16 = f16::from_f32_const(0.41401828); + const BETA_0: f16 = f16::from_f32_const(1.0); + + let x = x.clamp(LOW, HIGH); + + let x2 = x * x; + + let p = ALPHA_3; + let p = x2 * p + ALPHA_1; + let p = p * x; + + let q = BETA_4; + let q = x2 * q + BETA_2; + let q = x2 * q + BETA_0; + + p / q +} + +#[derive(Clone, Debug)] +pub struct STanh4; + +impl ElementWiseKer for STanh4 { + fn name() -> &'static str { + "generic" + } + + fn alignment_items() -> usize { + 16 + } + + fn alignment_bytes() -> usize { + 16 + } + + fn nr() -> usize { + 4 + } + + fn run(x: &mut [f32]) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px = stanh(*px)) + } +} + +#[cfg(test)] +#[macro_use] +pub mod s { + tanh_frame_tests!(true, f32, crate::generic::tanh::STanh4); +} + +#[derive(Clone, Debug)] +pub struct HTanh8; + +impl ElementWiseKer for HTanh8 { + fn name() -> &'static str { + "generic" + } + + fn alignment_items() -> usize { + 16 + } + + fn alignment_bytes() -> usize { + 16 + } + + fn nr() -> usize { + 8 + } + + fn run(x: &mut [f16]) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px = htanh(*px)) + } +} + +#[cfg(test)] +#[macro_use] +pub mod h { + tanh_frame_tests!( + true, + tract_data::internal::f16, + crate::generic::tanh::HTanh8 + ); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/lib.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/lib.rs new file mode 100644 index 0000000..a2bffe4 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/lib.rs @@ -0,0 +1,219 @@ +#![allow(clippy::missing_safety_doc)] +#[macro_use] +extern crate derive_new; +extern crate lazy_static; +extern crate log; +extern crate num_traits; +#[macro_use] +extern crate paste; +#[cfg(test)] +extern crate proptest; + +include!(concat!(env!("OUT_DIR"), "/extern_kernel_macro.rs")); + +#[macro_use] +pub mod frame; +pub mod generic; +use frame::MatMatMul; +use frame::element_wise::ElementWiseKer; +pub use generic::{ScaleShiftAndRound, Scaler}; +#[cfg(target_arch = "x86_64")] +pub mod x86_64_fma; + +#[cfg(target_arch = "aarch64")] +pub mod arm64; + +#[cfg(target_arch = "arm")] +pub mod arm32; + +pub use self::frame::{element_wise, lut, mmm}; + +use crate::frame::mmm::kernel::MatMatMulKer; +use tract_data::prelude::*; + +type MMMImpl = Box< + dyn Fn(Option, Option, Option) -> Box + Send + Sync, +>; + +type MMVImpl = Box, Option) -> Box + Send + Sync>; + +#[allow(clippy::type_complexity)] +pub struct Ops { + mmm_f64: MMMImpl, + mmv_f64: MMVImpl, + + mmm_f32_impls: Vec>, + mmm_f32: MMMImpl, + mmv_f32: MMVImpl, + + mmm_f16: MMMImpl, + mmv_f16: MMVImpl, + + qmmm_i32: MMMImpl, + qmmv_i32: MMVImpl, + + pub sigmoid_f16: Box Box> + Send + Sync>, + pub sigmoid_f32: Box Box> + Send + Sync>, + pub tanh_f16: Box Box> + Send + Sync>, + pub tanh_f32: Box Box> + Send + Sync>, + pub erf_f32: Box Box> + Send + Sync>, + pub lut_u8: Box Box + Send + Sync>, +} + +impl Ops { + pub fn mmm_f32_impls(&self) -> &[Box] { + &self.mmm_f32_impls + } + + pub fn mmm( + &self, + a: DatumType, + b: DatumType, + c: DatumType, + m: Option, + k: Option, + n: Option, + ) -> Option> { + use DatumType::*; + match (a.unquantized(), b.unquantized(), c.unquantized()) { + (F64, F64, F64) => Some(if n == Some(1) { + (self.mmv_f64)(m, k) + } else { + (self.mmm_f64)(m, k, n) + }), + (F32, F32, F32) => Some(if n == Some(1) { + (self.mmv_f32)(m, k) + } else { + (self.mmm_f32)(m, k, n) + }), + (F16, F16, F16) => Some(if n == Some(1) { + (self.mmv_f16)(m, k) + } else { + (self.mmm_f16)(m, k, n) + }), + (I8, I8, I32) => Some(if n == Some(1) { + (self.qmmv_i32)(m, k) + } else { + (self.qmmm_i32)(m, k, n) + }), + (I8, I8, I8) => Some(if n == Some(1) { + (self.qmmv_i32)(m, k) + } else { + (self.qmmm_i32)(m, k, n) + }), + _ => None, + } + } +} + +pub fn generic() -> Ops { + Ops { + mmm_f64: Box::new(|_, _, _| generic::GenericMmm4x4::::mmm()), + mmv_f64: Box::new(|_, _| generic::GenericMmm4x1::::mmm()), + mmm_f32_impls: vec![generic::GenericMmm4x4::::mmm()], + mmm_f32: Box::new(|_, _, _| generic::GenericMmm4x4::::mmm()), + mmv_f32: Box::new(|_, _| generic::GenericMmm4x1::::mmm()), + mmm_f16: Box::new(|_, _, _| generic::GenericMmm4x4::::mmm()), + mmv_f16: Box::new(|_, _| generic::GenericMmm4x1::::mmm()), + qmmm_i32: Box::new(|_, _, _| generic::GenericMmm4x4::::mmm()), + qmmv_i32: Box::new(|_, _| generic::GenericMmm4x1::::mmm()), + sigmoid_f16: Box::new(|| generic::HSigmoid8::ew()), + sigmoid_f32: Box::new(|| generic::SSigmoid4::ew()), + tanh_f16: Box::new(|| generic::HTanh8::ew()), + tanh_f32: Box::new(|| generic::STanh4::ew()), + erf_f32: Box::new(|| generic::SErf4::ew()), + lut_u8: Box::new(|table: &[u8]| Box::new(lut::LutImpl::::new(table))), + } +} + +#[allow(unreachable_code, unused_mut)] +pub fn best() -> Ops { + let mut ops = generic(); + #[cfg(target_arch = "x86_64")] + x86_64_fma::plug(&mut ops); + #[cfg(target_arch = "arm")] + arm32::plug(&mut ops); + #[cfg(target_arch = "aarch64")] + arm64::plug(&mut ops); + ops +} + +lazy_static::lazy_static! { + static ref OPS: Ops = { + best() + }; +} + +pub fn ops() -> &'static Ops { + &OPS +} + +use num_traits::*; +use std::fmt::Debug; +use std::ops::*; + +pub trait LADatum: + Sized + + std::fmt::Display + + Debug + + Copy + + Clone + + Zero + + One + + 'static + + Add + + Sub + + Mul + + AddAssign + + PartialOrd + + Bounded + + tract_data::prelude::Datum +{ + #[cfg(test)] + fn strat() -> proptest::prelude::BoxedStrategy; +} + +#[cfg(test)] +use proptest::prelude::*; + +impl LADatum for f16 { + #[cfg(test)] + fn strat() -> BoxedStrategy { + f32::strat().prop_map(|f| f.as_()).boxed() + } +} + +impl LADatum for f32 { + #[cfg(test)] + fn strat() -> BoxedStrategy { + (-1000isize..1000).prop_map(|i| i as f32 / 1000.0).boxed() + } +} + +impl LADatum for f64 { + #[cfg(test)] + fn strat() -> BoxedStrategy { + (-1000isize..1000).prop_map(|i| i as f64 / 1000.0).boxed() + } +} + +impl LADatum for u8 { + #[cfg(test)] + fn strat() -> BoxedStrategy { + any::().boxed() + } +} + +impl LADatum for i8 { + #[cfg(test)] + fn strat() -> BoxedStrategy { + any::().boxed() + } +} + +impl LADatum for i32 { + #[cfg(test)] + fn strat() -> BoxedStrategy { + any::().boxed() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/x86_64_fma.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/x86_64_fma.rs new file mode 100644 index 0000000..e99f0dc --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/x86_64_fma.rs @@ -0,0 +1,93 @@ +use crate::Ops; +use crate::frame::element_wise::ElementWiseKer; +use crate::frame::mmm::kernel::MatMatMulKer; + +pub mod mmm; + +mod intel; + +tanh_impl!(f32, fma_tanh_f32, 8, 8, is_x86_feature_detected!("fma")); +sigmoid_impl!(f32, fma_sigmoid_f32, 8, 8, is_x86_feature_detected!("fma")); + +pub fn plug(ops: &mut Ops) { + if is_x86_feature_detected!("fma") { + ops.mmv_f32 = Box::new(|_, _| mmm::fma_mmm_f32_64x1::mmm()); + + ops.mmm_f32 = Box::new(|_, _, n| { + if n.is_none() { + return mmm::fma_mmm_f32_16x6::mmm(); + } + + let n = n.unwrap(); + + match n { + 1 => unreachable!("should've been mmv"), + 2 => return mmm::fma_mmm_f32_40x2::mmm(), + 3 => return mmm::fma_mmm_f32_32x3::mmm(), + 4 => return mmm::fma_mmm_f32_24x4::mmm(), + 5 => return mmm::fma_mmm_f32_16x5::mmm(), + 6 => return mmm::fma_mmm_f32_16x6::mmm(), + 8 => return mmm::fma_mmm_f32_8x8::mmm(), + _ => {} + }; + + let scaling_baseline = 60.0; + let kernel_normalized_perf = [ + 44.0 / scaling_baseline, // 8x8 + 54.0 / scaling_baseline, // 2x6 + 54.0 / scaling_baseline, // 2x5 + 54.0 / scaling_baseline, // 3x4 + 54.0 / scaling_baseline, // 4x3 + 54.0 / scaling_baseline, // 5x2 + ]; + + fn compute_efficiency(n: usize, kernel_width: usize, scale: f32) -> f32 { + let kernel_width = kernel_width as f32; + let n = n as f32; + let batch_count = (n / kernel_width).ceil(); + let actual_count = batch_count * kernel_width; + let multi_batch_penalty = 1.0 - batch_count / 100.0; + n / actual_count * scale * multi_batch_penalty + } + + let efficiencies = [ + compute_efficiency(n, 8, kernel_normalized_perf[0]), + compute_efficiency(n, 6, kernel_normalized_perf[1]), + compute_efficiency(n, 5, kernel_normalized_perf[2]), + compute_efficiency(n, 4, kernel_normalized_perf[3]), + compute_efficiency(n, 3, kernel_normalized_perf[4]), + compute_efficiency(n, 2, kernel_normalized_perf[5]), + ]; + + let best_idx = efficiencies + .iter() + .copied() + .enumerate() + .fold((0, 0.0), |max, val| if val.1 > max.1 { val } else { max }); + + match best_idx.0 { + 0 => mmm::fma_mmm_f32_8x8::mmm(), + 1 => mmm::fma_mmm_f32_16x6::mmm(), + 2 => mmm::fma_mmm_f32_16x5::mmm(), + 3 => mmm::fma_mmm_f32_24x4::mmm(), + 4 => mmm::fma_mmm_f32_32x3::mmm(), + 5 => mmm::fma_mmm_f32_40x2::mmm(), + _ => unreachable!("not a valid index"), + } + }); + ops.mmm_f32_impls.push(mmm::fma_mmm_f32_16x6::mmm()); + ops.mmm_f32_impls.push(mmm::fma_mmm_f32_16x5::mmm()); + ops.mmm_f32_impls.push(mmm::fma_mmm_f32_24x4::mmm()); + ops.mmm_f32_impls.push(mmm::fma_mmm_f32_32x3::mmm()); + ops.mmm_f32_impls.push(mmm::fma_mmm_f32_40x2::mmm()); + ops.mmm_f32_impls.push(mmm::fma_mmm_f32_8x8::mmm()); + + ops.sigmoid_f32 = Box::new(|| fma_sigmoid_f32::ew()); + ops.tanh_f32 = Box::new(|| fma_tanh_f32::ew()); + log::info!("mmm_f32, sigmoid_f32, tanh_f32: x86_64/fma activated"); + } + if is_x86_feature_detected!("avx2") { + ops.qmmm_i32 = Box::new(|_, _, _| mmm::avx2_mmm_i32_8x8::mmm()); + log::info!("mmm_i8_i8 and mmm_i8_i32: x86_64/avx2 activated"); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/x86_64_fma/intel.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/x86_64_fma/intel.rs new file mode 100644 index 0000000..a713103 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/x86_64_fma/intel.rs @@ -0,0 +1,5 @@ +use crate::frame::mmm::cost_model::CostModel; +#[allow(dead_code)] +pub fn models() -> Vec<(&'static str, CostModel<'static>)> { + vec![] +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/x86_64_fma/mmm.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/x86_64_fma/mmm.rs new file mode 100644 index 0000000..4f9ae71 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/src/x86_64_fma/mmm.rs @@ -0,0 +1,11 @@ +use crate::frame::mmm::*; + +MMMKernel!(f32, fma_mmm_f32_8x8; 8, 8; 32, 4; 0, 0; no_prefetch, is_x86_feature_detected!("fma")); +MMMKernel!(f32, fma_mmm_f32_16x6; 16, 6; 32, 4; 0, 0; no_prefetch, is_x86_feature_detected!("fma")); +MMMKernel!(f32, fma_mmm_f32_16x5; 16, 5; 32, 4; 0, 0; no_prefetch, is_x86_feature_detected!("fma")); +MMMKernel!(f32, fma_mmm_f32_24x4; 24, 4; 32, 4; 0, 0; no_prefetch, is_x86_feature_detected!("fma")); +MMMKernel!(f32, fma_mmm_f32_32x3; 32, 3; 32, 4; 0, 0; no_prefetch, is_x86_feature_detected!("fma")); +MMMKernel!(f32, fma_mmm_f32_40x2; 40, 2; 32, 4; 0, 0; no_prefetch, is_x86_feature_detected!("fma")); +MMMKernel!(f32, fma_mmm_f32_64x1; 64, 1; 32, 4; 0, 0; no_prefetch, is_x86_feature_detected!("fma")); + +MMMKernel!(i32, avx2_mmm_i32_8x8; 8, 8; 32, 4; 0, 0; no_prefetch, is_x86_feature_detected!("avx2")); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/tests/virtual_im2col.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/tests/virtual_im2col.rs new file mode 100644 index 0000000..2f4fa83 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/tests/virtual_im2col.rs @@ -0,0 +1,362 @@ +use proptest::arbitrary::Arbitrary; +use proptest::prelude::*; +use proptest::strategy::{BoxedStrategy, Strategy}; +use tract_data::internal::*; +use tract_linalg::frame::mmm::FusedSpec; +use tract_linalg::frame::mmm::{VirtualInput, VirtualInputSpec}; +use tract_linalg::frame::PackingWriter; +use DatumType::F32; + +proptest::proptest! { + #[test] + fn prop(pb in any::()) { + pb.check() + } +} + +#[test] +fn test1() { + ConvProblem { + lazy_im2col: false, + input: tensor3(&[[[1f32]]]), + filters: tensor4(&[[[[-1f32]]]]), + } + .check() +} + +#[test] +fn test_axes_0() { + // CHW HWIO CHW + // 121 1112 221 + ConvProblem { + lazy_im2col: false, + input: tensor3(&[[[0f32], [-1.0]]]), + filters: tensor4(&[[[[0f32, -1f32]]]]), + } + .check() +} + +#[test] +fn test_axes_1() { + ConvProblem { + lazy_im2col: false, + input: tensor3(&[[[0f32, 1.]]]), + filters: tensor4(&[[[[1f32]]]]), + } + .check() +} + +#[test] +fn test_lazy_0() { + ConvProblem { + lazy_im2col: true, + input: tensor3(&[[[1f32]]]), + filters: tensor4(&[[[[1f32]]]]), + } + .check() +} + +#[test] +fn test_lazy_1() { + ConvProblem { + lazy_im2col: true, + input: tensor3(&[[[0f32], [0.], [0.]]]), + filters: tensor4(&[[[[0f32]]]]), + } + .check() +} + +#[test] +fn test_lazy_2() { + ConvProblem { + lazy_im2col: true, + input: tensor3(&[[[0f32, 0.], [0., 1.]]]), + filters: tensor4(&[[[[0f32]], [[1.]]]]), + } + .check() +} + +#[test] +fn test_lazy_3() { + // CHW HWIO CHW + // 212 1221 111 + // im2col: k=4, n=1, k <- kh, kw, c + // 0 X X X X kh=0, kw=0, c=0 + // 1 X X X X kh=0, kw=0, c=1 + // 0 X X X X kh=0, kw=1, c=0 + // 0 X X X X kh=0, kw=1, c=1 + ConvProblem { + lazy_im2col: true, + input: tensor3(&[[[0f32, 0.]], [[1., 0.]]]), + filters: tensor4(&[[[[0f32], [0.]], [[1.], [0.]]]]), + } + .check() +} + +// 2D valid, no group, no dil, no stride, HWIO, CHW +#[derive(Clone, Debug)] +pub struct ConvProblem { + pub lazy_im2col: bool, + pub input: Tensor, + pub filters: Tensor, +} + +fn mknhw(filters: &[usize], input: &[usize]) -> (usize, usize, usize, usize, usize) { + let m = filters[3]; + let k = filters[0..3].iter().product::(); + let h = input[1] - filters[0] + 1; + let w = input[2] - filters[1] + 1; + let n = h * w; + (m, k, n, h, w) +} + +impl ConvProblem { + fn reference(&self) -> Tensor { + let (m, _, _, h, w) = mknhw(self.filters.shape(), self.input.shape()); + let output_shape = [m, h, w]; + let mut output = Tensor::zero::(&output_shape).unwrap(); + let mut output_view = output.to_array_view_mut::().unwrap(); + let input_view = self.input.to_array_view::().unwrap(); + let filters_view = self.filters.to_array_view::().unwrap(); + for geo_out in tract_ndarray::indices(&output_shape[1..]) { + for ker_geo in tract_ndarray::indices(&self.filters.shape()[0..2]) { + for ci in 0..self.filters.shape()[2] { + for co in 0..self.filters.shape()[3] { + let output_coord = [co, geo_out[0], geo_out[1]]; + let input_coord = [ci, geo_out[0] + ker_geo[0], geo_out[1] + ker_geo[1]]; + let ker_coord = [ker_geo[0], ker_geo[1], ci, co]; + output_view[output_coord] += + filters_view[ker_coord] * input_view[input_coord]; + } + } + } + } + output + } + + pub fn tract(&self) -> Tensor { + let (m, k, n, h, w) = mknhw(self.filters.shape(), self.input.shape()); + let output_shape = [m, h, w]; + let internal_output_shape = [m, h * w]; + let mmm = tract_linalg::ops() + .mmm(F32, F32, F32, Some(m), Some(k), Some(n)) + .unwrap(); + let output = Tensor::zero::(&internal_output_shape).unwrap(); + let mut packed_filter = + Tensor::zero_aligned::(&[mmm.a_pack().len(k, m)], mmm.a_pack().alignment()) + .unwrap(); + let reshaped_filters = self.filters.clone().into_shape(&[k, m]).unwrap(); + unsafe { + mmm.a_pack() + .pack(packed_filter.view_mut(), reshaped_filters.view(), 0, 1); + let a_store = mmm.a_packed(F32.size_of(), k).wrap(&packed_filter.view()); + let im2col: Box = if self.lazy_im2col { + Box::new(LazyIm2colSpec { + full_kernel_shape: self.filters.shape().into(), + }) + } else { + Box::new(EagerIm2colSpec { + full_kernel_shape: self.filters.shape().into(), + }) + }; + let b_store = mmm + .b_virtual_input(im2col, k) + .wrap(&self.input.view()) + .unwrap(); + let c_store = mmm.c_view(0, 1).wrap(&output.view()); + mmm.run( + m, + n, + &[ + FusedSpec::AddMatMul { + k, + a: a_store, + b: b_store, + }, + FusedSpec::Store(c_store), + ], + ) + .unwrap() + } + output.into_shape(&output_shape).unwrap() + } + + fn check(&self) { + let found = self.tract(); + let expected = self.reference(); + if found.close_enough(&expected, true).is_err() { + println!("found: "); + println!("{:?}", found.to_array_view::().unwrap()); + println!("expected: "); + println!("{:?}", expected.to_array_view::().unwrap()); + } + found.close_enough(&expected, true).unwrap() + } +} + +impl Arbitrary for ConvProblem { + type Parameters = (); + type Strategy = BoxedStrategy; + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + ( + any::(), + 1..4usize, + 1..4usize, + 1..4usize, + 1..4usize, + 0..3usize, + 0..3usize, + ) + .prop_flat_map(|(eager_im2col, h, w, i, o, extra_h, extra_w)| { + let filters = tensor(vec![h, w, i, o]); + let input = tensor(vec![i, h + extra_h, w + extra_w]); + (Just(eager_im2col), filters, input) + }) + .prop_map(|(eager_im2col, filters, input)| ConvProblem { + lazy_im2col: eager_im2col, + filters, + input, + }) + .boxed() + } +} + +fn tensor(shape: Vec) -> BoxedStrategy { + let len = shape.iter().product::(); + proptest::collection::vec(any::(), len..=len) + .prop_map(move |vec| { + tract_ndarray::ArrayD::from_shape_vec(shape.clone(), vec) + .unwrap() + .into_tensor() + .cast_to_dt(F32) + .unwrap() + .into_owned() + }) + .boxed() +} + +#[derive(Clone, Debug, Hash)] +struct EagerIm2colSpec { + full_kernel_shape: TVec, +} + +impl_dyn_hash!(EagerIm2colSpec); + +impl VirtualInputSpec for EagerIm2colSpec { + fn wrap(&self, input: &TensorView) -> Box { + let (_, k, n, h, w) = mknhw(&self.full_kernel_shape, input.shape()); + // let input = input.to_array_view::().unwrap(); + let ci = input.shape()[0]; + let kh = self.full_kernel_shape[0]; + let kw = self.full_kernel_shape[1]; + let im2col = tract_ndarray::Array5::::from_shape_fn( + [kh, kw, ci, h, w], + |(kh, kw, ci, h, w)| *input.at([ci, h + kh, w + kw]).unwrap(), + ) + .into_shape([k, n]) + .unwrap(); + Box::new(EagerIm2col { + im2col: im2col.into_tensor(), + }) + } +} + +#[derive(Clone, Debug)] +struct EagerIm2col { + im2col: Tensor, +} + +impl VirtualInput for EagerIm2col { + fn input( + &self, + packer: &tract_linalg::frame::Packer, + packed: *mut u8, + k_range: std::ops::Range, + mn_range: std::ops::Range, + ) { + let mn = self.im2col.shape()[1]; + unsafe { + packer.pack_t::( + packed as _, + self.im2col.as_ptr().unwrap(), + mn, + mn as isize, + 1, + k_range, + mn_range, + ); + } + } +} + +#[derive(Clone, Debug, Hash)] +struct LazyIm2colSpec { + full_kernel_shape: TVec, +} + +impl_dyn_hash!(LazyIm2colSpec); + +impl VirtualInputSpec for LazyIm2colSpec { + fn wrap(&self, input: &TensorView) -> Box { + let (_, _, _, h, w) = mknhw(&self.full_kernel_shape, input.shape()); + let kh = self.full_kernel_shape[0]; + let kw = self.full_kernel_shape[1]; + let ci = self.full_kernel_shape[2]; + let input_strides = input.strides(); + let k_offsets = (0..kh as isize) + .flat_map(|kh| { + (0..kw as isize).flat_map(move |kw| { + (0..ci as isize).map(move |ci| { + ci * input_strides[0] + kh * input_strides[1] + kw * input_strides[2] + }) + }) + }) + .collect(); + let n_offsets = (0..h as isize) + .flat_map(|h| { + (0..w as isize).map(move |w| (h * input_strides[1] + w * input_strides[2])) + }) + .collect(); + unsafe { + Box::new(LazyIm2col { + image: input.as_ptr_unchecked(), + k_offsets, + n_offsets, + }) + } + } +} + +#[derive(Clone, Debug)] +struct LazyIm2col { + image: *const f32, + n_offsets: Vec, + k_offsets: Vec, +} +unsafe impl Send for LazyIm2col {} +unsafe impl Sync for LazyIm2col {} + +impl VirtualInput for LazyIm2col { + fn input( + &self, + packer: &tract_linalg::frame::Packer, + packed: *mut u8, + k_range: std::ops::Range, + mn_range: std::ops::Range, + ) { + let mn_end = mn_range.end.min(self.n_offsets.len()); + let n_range = mn_range.start..mn_end; + unsafe { + let mut writer = packer.write_with_k_outer(packed as _, k_range.len(), n_range.len()); + for k in k_range.start..k_range.end { + for n in n_range.start..n_range.end { + writer.write( + *self.image.offset( + self.n_offsets.get_unchecked(n) + self.k_offsets.get_unchecked(k), + ), + ) + } + } + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/2x5/packed_packed_loop1/avx-unroll.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/2x5/packed_packed_loop1/avx-unroll.tmpli new file mode 100644 index 0000000..6cb8246 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/2x5/packed_packed_loop1/avx-unroll.tmpli @@ -0,0 +1,52 @@ + // Accumulators: 0-9 + // Columns: 14-15 + // Rows: 10-13 + vbroadcastss ymm10, dword ptr [rcx] + vbroadcastss ymm11, dword ptr [rcx + 4] + vbroadcastss ymm12, dword ptr [rcx + 8] + vbroadcastss ymm13, dword ptr [rcx + 12] + + vmovaps ymm14, [rax] + vmovaps ymm15, [rax + 32] + + vfmadd231ps ymm0, ymm14, ymm10 + vfmadd231ps ymm1, ymm15, ymm10 + + vfmadd231ps ymm2, ymm14, ymm11 + vfmadd231ps ymm3, ymm15, ymm11 + + vbroadcastss ymm11, dword ptr [rcx + 16] + + vfmadd231ps ymm4, ymm14, ymm12 + vfmadd231ps ymm5, ymm15, ymm12 + + vfmadd231ps ymm6, ymm14, ymm13 + vfmadd231ps ymm7, ymm15, ymm13 + + vfmadd231ps ymm8, ymm14, ymm11 + vfmadd231ps ymm9, ymm15, ymm11 + + vbroadcastss ymm10, dword ptr [rcx + 20] + vbroadcastss ymm11, dword ptr [rcx + 24] + vbroadcastss ymm12, dword ptr [rcx + 28] + vbroadcastss ymm13, dword ptr [rcx + 32] + + vmovaps ymm14, [rax + 64] + vmovaps ymm15, [rax + 96] + + vfmadd231ps ymm0, ymm14, ymm10 + vfmadd231ps ymm1, ymm15, ymm10 + + vfmadd231ps ymm2, ymm14, ymm11 + vfmadd231ps ymm3, ymm15, ymm11 + + vbroadcastss ymm11, dword ptr [rcx + 36] + + vfmadd231ps ymm4, ymm14, ymm12 + vfmadd231ps ymm5, ymm15, ymm12 + + vfmadd231ps ymm6, ymm14, ymm13 + vfmadd231ps ymm7, ymm15, ymm13 + + vfmadd231ps ymm8, ymm14, ymm11 + vfmadd231ps ymm9, ymm15, ymm11 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/2x5/packed_packed_loop1/avx.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/2x5/packed_packed_loop1/avx.tmpli new file mode 100644 index 0000000..59a29b6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/2x5/packed_packed_loop1/avx.tmpli @@ -0,0 +1,30 @@ + // Accumulators: 0-9 + // Columns: 14-15 + // Rows: 10-13 + vbroadcastss ymm10, dword ptr [rcx] + vbroadcastss ymm11, dword ptr [rcx + 4] + vbroadcastss ymm12, dword ptr [rcx + 8] + vbroadcastss ymm13, dword ptr [rcx + 12] + + vmovaps ymm14, [rax] + vmovaps ymm15, [rax + 32] + + vfmadd231ps ymm0, ymm14, ymm10 + vfmadd231ps ymm1, ymm15, ymm10 + + vfmadd231ps ymm2, ymm14, ymm11 + vfmadd231ps ymm3, ymm15, ymm11 + + // Use register 11 as it's "middle" use, leading to a decent + // trade-off between required use next iteration and when it has + // to be used this iteration. + vbroadcastss ymm11, dword ptr [rcx + 16] + + vfmadd231ps ymm4, ymm14, ymm12 + vfmadd231ps ymm5, ymm15, ymm12 + + vfmadd231ps ymm6, ymm14, ymm13 + vfmadd231ps ymm7, ymm15, ymm13 + + vfmadd231ps ymm8, ymm14, ymm11 + vfmadd231ps ymm9, ymm15, ymm11 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/2x6/packed_packed_loop1/original-unroll.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/2x6/packed_packed_loop1/original-unroll.tmpli new file mode 100644 index 0000000..f1ef4b0 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/2x6/packed_packed_loop1/original-unroll.tmpli @@ -0,0 +1,68 @@ + // Tile size: 2x6 + // Accumulators: 0-11 + // Col regs: ymm14-15 + // Row regs: ymm12-13 + + vbroadcastss ymm14, dword ptr [rcx] + vmovaps ymm12, [rax] + vmovaps ymm13, [rax + 32] + vbroadcastss ymm15, dword ptr [rcx + 4] + + vfmadd231ps ymm0, ymm12, ymm14 + vfmadd231ps ymm1, ymm13, ymm14 + + vbroadcastss ymm14, dword ptr [rcx + 8] + + vfmadd231ps ymm2, ymm12, ymm15 + vfmadd231ps ymm3, ymm13, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 12] + + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm5, ymm13, ymm14 + + vbroadcastss ymm14, dword ptr [rcx + 16] + + vfmadd231ps ymm6, ymm12, ymm15 + vfmadd231ps ymm7, ymm13, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 20] + + vfmadd231ps ymm8, ymm12, ymm14 + vfmadd231ps ymm9, ymm13, ymm14 + + vbroadcastss ymm14, dword ptr [rcx+24] + + vfmadd231ps ymm10, ymm12, ymm15 + vfmadd231ps ymm11, ymm13, ymm15 + + // Iteration two + vmovaps ymm12, [rax + 64] + vmovaps ymm13, [rax + 96] + vbroadcastss ymm15, dword ptr [rcx + 24 + 4] + + vfmadd231ps ymm0, ymm12, ymm14 + vfmadd231ps ymm1, ymm13, ymm14 + + vbroadcastss ymm14, dword ptr [rcx + 24 + 8] + + vfmadd231ps ymm2, ymm12, ymm15 + vfmadd231ps ymm3, ymm13, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 24 + 12] + + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm5, ymm13, ymm14 + + vbroadcastss ymm14, dword ptr [rcx + 24 + 16] + + vfmadd231ps ymm6, ymm12, ymm15 + vfmadd231ps ymm7, ymm13, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 24 + 20] + + vfmadd231ps ymm8, ymm12, ymm14 + vfmadd231ps ymm9, ymm13, ymm14 + + vfmadd231ps ymm10, ymm12, ymm15 + vfmadd231ps ymm11, ymm13, ymm15 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/2x6/packed_packed_loop1/original.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/2x6/packed_packed_loop1/original.tmpli new file mode 100644 index 0000000..706da6c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/2x6/packed_packed_loop1/original.tmpli @@ -0,0 +1,36 @@ + // Tile size: 2x6 + // Accumulators: 0-11 + // Col regs: ymm14-15 + // Row regs: ymm12-13 + + // Load ordered by earliest use for first 2x2 block + vbroadcastss ymm14, dword ptr [rcx] + vmovaps ymm12, [rax] + vmovaps ymm13, [rax + 32] + vbroadcastss ymm15, dword ptr [rcx + 4] + + vfmadd231ps ymm0, ymm12, ymm14 + vfmadd231ps ymm1, ymm13, ymm14 + + vbroadcastss ymm14, dword ptr [rcx + 8] + + vfmadd231ps ymm2, ymm12, ymm15 + vfmadd231ps ymm3, ymm13, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 12] + + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm5, ymm13, ymm14 + + vbroadcastss ymm14, dword ptr [rcx + 16] + + vfmadd231ps ymm6, ymm12, ymm15 + vfmadd231ps ymm7, ymm13, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 20] + + vfmadd231ps ymm8, ymm12, ymm14 + vfmadd231ps ymm9, ymm13, ymm14 + + vfmadd231ps ymm10, ymm12, ymm15 + vfmadd231ps ymm11, ymm13, ymm15 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/3x4/packed_packed_loop1/avx-unroll.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/3x4/packed_packed_loop1/avx-unroll.tmpli new file mode 100644 index 0000000..a9e6ea3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/3x4/packed_packed_loop1/avx-unroll.tmpli @@ -0,0 +1,60 @@ + // Tile size: 3x4 + // Accumulators: 0-11 + // Col regs: ymm12-14 + // Row regs: ymm15 + + vmovaps ymm12, [rax] + vmovaps ymm13, [rax+32] + vmovaps ymm14, [rax+64] + + vbroadcastss ymm15, dword ptr [rcx + 0] + + vfmadd231ps ymm0, ymm12, ymm15 + vfmadd231ps ymm1, ymm13, ymm15 + vfmadd231ps ymm2, ymm14, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 4] + + vfmadd231ps ymm3, ymm12, ymm15 + vfmadd231ps ymm4, ymm13, ymm15 + vfmadd231ps ymm5, ymm14, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 8] + + vfmadd231ps ymm6, ymm12, ymm15 + vfmadd231ps ymm7, ymm13, ymm15 + vfmadd231ps ymm8, ymm14, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 12] + + vfmadd231ps ymm9, ymm12, ymm15 + vfmadd231ps ymm10, ymm13, ymm15 + vfmadd231ps ymm11, ymm14, ymm15 + + vmovaps ymm12, [rax + 96] + vmovaps ymm13, [rax + 128] + vmovaps ymm14, [rax + 160] + + vbroadcastss ymm15, dword ptr [rcx + 16] + + vfmadd231ps ymm0, ymm12, ymm15 + vfmadd231ps ymm1, ymm13, ymm15 + vfmadd231ps ymm2, ymm14, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 20] + + vfmadd231ps ymm3, ymm12, ymm15 + vfmadd231ps ymm4, ymm13, ymm15 + vfmadd231ps ymm5, ymm14, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 24] + + vfmadd231ps ymm6, ymm12, ymm15 + vfmadd231ps ymm7, ymm13, ymm15 + vfmadd231ps ymm8, ymm14, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 28] + + vfmadd231ps ymm9, ymm12, ymm15 + vfmadd231ps ymm10, ymm13, ymm15 + vfmadd231ps ymm11, ymm14, ymm15 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/3x4/packed_packed_loop1/avx.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/3x4/packed_packed_loop1/avx.tmpli new file mode 100644 index 0000000..eff5cd2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/3x4/packed_packed_loop1/avx.tmpli @@ -0,0 +1,32 @@ + // Tile size: 3x4 + // Accumulators: 0-11 + // Col regs: ymm12-14 + // Row regs: ymm15 + + vmovaps ymm12, [rax] + vmovaps ymm13, [rax+32] + vmovaps ymm14, [rax+64] + + vbroadcastss ymm15, dword ptr [rcx + 0] + + vfmadd231ps ymm0, ymm12, ymm15 + vfmadd231ps ymm1, ymm13, ymm15 + vfmadd231ps ymm2, ymm14, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 4] + + vfmadd231ps ymm3, ymm12, ymm15 + vfmadd231ps ymm4, ymm13, ymm15 + vfmadd231ps ymm5, ymm14, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 8] + + vfmadd231ps ymm6, ymm12, ymm15 + vfmadd231ps ymm7, ymm13, ymm15 + vfmadd231ps ymm8, ymm14, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 12] + + vfmadd231ps ymm9, ymm12, ymm15 + vfmadd231ps ymm10, ymm13, ymm15 + vfmadd231ps ymm11, ymm14, ymm15 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/4x3/packed_packed_loop1/avx-unroll.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/4x3/packed_packed_loop1/avx-unroll.tmpli new file mode 100644 index 0000000..4ca0e0f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/4x3/packed_packed_loop1/avx-unroll.tmpli @@ -0,0 +1,66 @@ + // Tile size: 4x3 + // Accumulators: 0-11 + // Col regs: ymm12 + // Row regs: ymm13-15 + + // Load col of A + vmovaps ymm12, [rax] + + // Fill 3 cols of B + vbroadcastss ymm13, dword ptr [rcx + 0] + vbroadcastss ymm14, dword ptr [rcx + 4] + vbroadcastss ymm15, dword ptr [rcx + 8] + + // N.B. Stepping cols in inner loop + vfmadd231ps ymm0, ymm12, ymm13 + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm8, ymm12, ymm15 + + vmovaps ymm12, [rax+32] + + vfmadd231ps ymm1, ymm12, ymm13 + vfmadd231ps ymm5, ymm12, ymm14 + vfmadd231ps ymm9, ymm12, ymm15 + + vmovaps ymm12, [rax+64] + + vfmadd231ps ymm2, ymm12, ymm13 + vfmadd231ps ymm6, ymm12, ymm14 + vfmadd231ps ymm10, ymm12, ymm15 + + vmovaps ymm12, [rax+96] + + vfmadd231ps ymm3, ymm12, ymm13 + vfmadd231ps ymm7, ymm12, ymm14 + vfmadd231ps ymm11, ymm12, ymm15 + + // Load col of A, switching col! + vmovaps ymm13, [rax + 128] + + // Fill 3 cols of B + vbroadcastss ymm14, dword ptr [rcx + 12] + vbroadcastss ymm15, dword ptr [rcx + 16] + vbroadcastss ymm12, dword ptr [rcx + 20] + + // N.B. Stepping cols in inner loop + vfmadd231ps ymm0, ymm13, ymm14 + vfmadd231ps ymm4, ymm13, ymm15 + vfmadd231ps ymm8, ymm13, ymm12 + + vmovaps ymm13, [rax + 160] + + vfmadd231ps ymm1, ymm13, ymm14 + vfmadd231ps ymm5, ymm13, ymm15 + vfmadd231ps ymm9, ymm13, ymm12 + + vmovaps ymm13, [rax + 192] + + vfmadd231ps ymm2, ymm13, ymm14 + vfmadd231ps ymm6, ymm13, ymm15 + vfmadd231ps ymm10, ymm13, ymm12 + + vmovaps ymm13, [rax + 224] + + vfmadd231ps ymm3, ymm13, ymm14 + vfmadd231ps ymm7, ymm13, ymm15 + vfmadd231ps ymm11, ymm13, ymm12 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/4x3/packed_packed_loop1/avx.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/4x3/packed_packed_loop1/avx.tmpli new file mode 100644 index 0000000..1892c31 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/4x3/packed_packed_loop1/avx.tmpli @@ -0,0 +1,35 @@ + // Tile size: 4x3 + // Accumulators: 0-11 + // Col regs: ymm12 + // Row regs: ymm13-15 + + // Load col of A + vmovaps ymm12, [rax] + + // Fill 3 cols of B + vbroadcastss ymm13, dword ptr [rcx + 0] + vbroadcastss ymm14, dword ptr [rcx + 4] + vbroadcastss ymm15, dword ptr [rcx + 8] + + // N.B. Stepping cols in inner loop + vfmadd231ps ymm0, ymm12, ymm13 + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm8, ymm12, ymm15 + + vmovaps ymm12, [rax+32] + + vfmadd231ps ymm1, ymm12, ymm13 + vfmadd231ps ymm5, ymm12, ymm14 + vfmadd231ps ymm9, ymm12, ymm15 + + vmovaps ymm12, [rax+64] + + vfmadd231ps ymm2, ymm12, ymm13 + vfmadd231ps ymm6, ymm12, ymm14 + vfmadd231ps ymm10, ymm12, ymm15 + + vmovaps ymm12, [rax+96] + + vfmadd231ps ymm3, ymm12, ymm13 + vfmadd231ps ymm7, ymm12, ymm14 + vfmadd231ps ymm11, ymm12, ymm15 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/5x2/packed_packed_loop1/avx-unroll.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/5x2/packed_packed_loop1/avx-unroll.tmpli new file mode 100644 index 0000000..b03885f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/5x2/packed_packed_loop1/avx-unroll.tmpli @@ -0,0 +1,60 @@ + // Tile size: 5x2 + // Accumulators: 0-9 + // Col regs: ymm10-13 + // Row regs: ymm14-15 + + vmovaps ymm10, [rax] + vbroadcastss ymm14, dword ptr [rcx + 0] + vbroadcastss ymm15, dword ptr [rcx + 4] + vmovaps ymm11, [rax + 32] + + // NB stepping column-wise + vfmadd231ps ymm0, ymm10, ymm14 + vfmadd231ps ymm5, ymm10, ymm15 + + vmovaps ymm12, [rax + 64] + + vfmadd231ps ymm1, ymm11, ymm14 + vfmadd231ps ymm6, ymm11, ymm15 + + vmovaps ymm13, [rax + 96] + + vfmadd231ps ymm2, ymm12, ymm14 + vfmadd231ps ymm7, ymm12, ymm15 + + vmovaps ymm10, [rax + 128] + + vfmadd231ps ymm3, ymm13, ymm14 + vfmadd231ps ymm8, ymm13, ymm15 + + vmovaps ymm11, [rax + 160] + + vfmadd231ps ymm4, ymm10, ymm14 + vfmadd231ps ymm9, ymm10, ymm15 + + vbroadcastss ymm14, dword ptr [rcx + 8] + vbroadcastss ymm15, dword ptr [rcx + 12] + + vmovaps ymm12, [rax + 192] + + // NB stepping column-wise + vfmadd231ps ymm0, ymm11, ymm14 + vfmadd231ps ymm5, ymm11, ymm15 + + vmovaps ymm13, [rax + 224] + + vfmadd231ps ymm1, ymm12, ymm14 + vfmadd231ps ymm6, ymm12, ymm15 + + vmovaps ymm10, [rax + 256] + + vfmadd231ps ymm2, ymm13, ymm14 + vfmadd231ps ymm7, ymm13, ymm15 + + vmovaps ymm11, [rax + 288] + + vfmadd231ps ymm3, ymm10, ymm14 + vfmadd231ps ymm8, ymm10, ymm15 + + vfmadd231ps ymm4, ymm11, ymm14 + vfmadd231ps ymm9, ymm11, ymm15 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/5x2/packed_packed_loop1/avx.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/5x2/packed_packed_loop1/avx.tmpli new file mode 100644 index 0000000..450ef19 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/5x2/packed_packed_loop1/avx.tmpli @@ -0,0 +1,31 @@ + // Tile size: 5x2 + // Accumulators: 0-9 + // Col regs: ymm10-13 + // Row regs: ymm14-15 + + vmovaps ymm10, [rax] + vbroadcastss ymm14, dword ptr [rcx + 0] + vbroadcastss ymm15, dword ptr [rcx + 4] + vmovaps ymm11, [rax + 32] + + // NB stepping column-wise + vfmadd231ps ymm0, ymm10, ymm14 + vfmadd231ps ymm5, ymm10, ymm15 + + vmovaps ymm12, [rax + 64] + + vfmadd231ps ymm1, ymm11, ymm14 + vfmadd231ps ymm6, ymm11, ymm15 + + vmovaps ymm13, [rax + 96] + + vfmadd231ps ymm2, ymm12, ymm14 + vfmadd231ps ymm7, ymm12, ymm15 + + vmovaps ymm11, [rax + 128] + + vfmadd231ps ymm3, ymm13, ymm14 + vfmadd231ps ymm8, ymm13, ymm15 + + vfmadd231ps ymm4, ymm11, ymm14 + vfmadd231ps ymm9, ymm11, ymm15 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/6x2/packed_packed_loop1/avx-unroll.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/6x2/packed_packed_loop1/avx-unroll.tmpli new file mode 100644 index 0000000..b56e71d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/6x2/packed_packed_loop1/avx-unroll.tmpli @@ -0,0 +1,67 @@ + // Tile size: 6x2 + // Accumulators: 0-9 + // Col regs: ymm10-13 + // Row regs: ymm14-15 + + vmovaps ymm12, [rax] + vbroadcastss ymm14, dword ptr [rcx + 0] + vbroadcastss ymm15, dword ptr [rcx + 4] + vmovaps ymm13, [rax + 32] + + vfmadd231ps ymm0, ymm12, ymm14 + vfmadd231ps ymm6, ymm12, ymm15 + + vmovaps ymm12, [rax + 64] + + vfmadd231ps ymm1, ymm13, ymm14 + vfmadd231ps ymm7, ymm13, ymm15 + + vmovaps ymm13, [rax + 96] + + vfmadd231ps ymm2, ymm12, ymm14 + vfmadd231ps ymm8, ymm12, ymm15 + + vmovaps ymm12, [rax + 128] + + vfmadd231ps ymm3, ymm13, ymm14 + vfmadd231ps ymm9, ymm13, ymm15 + + vmovaps ymm13, [rax + 160] + + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm10, ymm12, ymm15 + + vmovaps ymm12, [rax + 192] + vbroadcastss ymm14, dword ptr [rcx + 8] + + vfmadd231ps ymm5, ymm13, ymm14 + vfmadd231ps ymm11, ymm13, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 12] + vmovaps ymm13, [rax + 224] + + vfmadd231ps ymm0, ymm12, ymm14 + vfmadd231ps ymm6, ymm12, ymm15 + + vmovaps ymm12, [rax + 256] + + vfmadd231ps ymm1, ymm13, ymm14 + vfmadd231ps ymm7, ymm13, ymm15 + + vmovaps ymm13, [rax + 288] + + vfmadd231ps ymm2, ymm12, ymm14 + vfmadd231ps ymm8, ymm12, ymm15 + + vmovaps ymm12, [rax + 320] + + vfmadd231ps ymm3, ymm13, ymm14 + vfmadd231ps ymm9, ymm13, ymm15 + + vmovaps ymm13, [rax + 352] + + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm10, ymm12, ymm15 + + vfmadd231ps ymm5, ymm13, ymm14 + vfmadd231ps ymm11, ymm13, ymm15 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/6x2/packed_packed_loop1/avx.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/6x2/packed_packed_loop1/avx.tmpli new file mode 100644 index 0000000..d03fa9b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/6x2/packed_packed_loop1/avx.tmpli @@ -0,0 +1,35 @@ + // Tile size: 6x2 + // Accumulators: 0-11 + // Col regs: 12-13 + // Row regs: 14-15 + + vmovaps ymm12, [rax] + vbroadcastss ymm14, dword ptr [rcx + 0] + vbroadcastss ymm15, dword ptr [rcx + 4] + vmovaps ymm13, [rax + 32] + + vfmadd231ps ymm0, ymm12, ymm14 + vfmadd231ps ymm6, ymm12, ymm15 + + vmovaps ymm12, [rax + 64] + + vfmadd231ps ymm1, ymm13, ymm14 + vfmadd231ps ymm7, ymm13, ymm15 + + vmovaps ymm13, [rax + 96] + + vfmadd231ps ymm2, ymm12, ymm14 + vfmadd231ps ymm8, ymm12, ymm15 + + vmovaps ymm12, [rax + 128] + + vfmadd231ps ymm3, ymm13, ymm14 + vfmadd231ps ymm9, ymm13, ymm15 + + vmovaps ymm13, [rax + 160] + + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm10, ymm12, ymm15 + + vfmadd231ps ymm5, ymm13, ymm14 + vfmadd231ps ymm11, ymm13, ymm15 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/8x1/packed_packed_loop1/avx-unroll.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/8x1/packed_packed_loop1/avx-unroll.tmpli new file mode 100644 index 0000000..7d27318 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/8x1/packed_packed_loop1/avx-unroll.tmpli @@ -0,0 +1,53 @@ + // Accumulators: 0-7 + // Columns: 14-15 + // Rows: 8-13 + vbroadcastss ymm14, dword ptr [rcx] + vbroadcastss ymm15, dword ptr [rcx+4] + + vmovaps ymm8, [rax + 0] + vmovaps ymm9, [rax + 32] + + vmovaps ymm10, [rax + 64] + vmovaps ymm11, [rax + 96] + + vfmadd231ps ymm0, ymm8, ymm14 + vfmadd231ps ymm1, ymm9, ymm14 + + vmovaps ymm12, [rax + 128] + vmovaps ymm13, [rax + 160] + + vfmadd231ps ymm2, ymm10, ymm14 + vfmadd231ps ymm3, ymm11, ymm14 + + vmovaps ymm8, [rax + 192] + vmovaps ymm9, [rax + 224] + + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm5, ymm13, ymm14 + + vmovaps ymm10, [rax + 256] + vmovaps ymm11, [rax + 288] + + vfmadd231ps ymm6, ymm8, ymm14 + vfmadd231ps ymm7, ymm9, ymm14 + + // STEP 2 + vmovaps ymm12, [rax + 320] + vmovaps ymm8, [rax + 352] + + vfmadd231ps ymm0, ymm10, ymm15 + vfmadd231ps ymm1, ymm11, ymm15 + + vmovaps ymm9, [rax + 384] + vmovaps ymm10, [rax + 416] + vfmadd231ps ymm2, ymm12, ymm15 + vfmadd231ps ymm3, ymm8, ymm15 + + vmovaps ymm11, [rax + 448] + vmovaps ymm12, [rax + 480] + + vfmadd231ps ymm4, ymm9, ymm15 + vfmadd231ps ymm5, ymm10, ymm15 + + vfmadd231ps ymm6, ymm11, ymm15 + vfmadd231ps ymm7, ymm12, ymm15 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/8x1/packed_packed_loop1/avx.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/8x1/packed_packed_loop1/avx.tmpli new file mode 100644 index 0000000..d1a6e9a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/8x1/packed_packed_loop1/avx.tmpli @@ -0,0 +1,28 @@ + // Tile size: 8x1 + // Accumulators: 0-7 + // Col regs: 8-14 + // Row regs: 15 + vbroadcastss ymm15, dword ptr [rcx] + + vmovaps ymm8, [rax + 0] + vmovaps ymm9, [rax + 32] + vmovaps ymm10, [rax + 64] + vmovaps ymm11, [rax + 96] + + vfmadd231ps ymm0, ymm8, ymm15 + vfmadd231ps ymm1, ymm9, ymm15 + + vmovaps ymm12, [rax + 128] + vmovaps ymm13, [rax + 160] + + vfmadd231ps ymm2, ymm10, ymm15 + vfmadd231ps ymm3, ymm11, ymm15 + + vmovaps ymm14, [rax + 192] + vmovaps ymm11, [rax + 224] + + vfmadd231ps ymm4, ymm12, ymm15 + vfmadd231ps ymm5, ymm13, ymm15 + + vfmadd231ps ymm6, ymm14, ymm15 + vfmadd231ps ymm7, ymm11, ymm15 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/8x8/packed_packed_loop1/avx-unroll.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/8x8/packed_packed_loop1/avx-unroll.tmpli new file mode 100644 index 0000000..f8e8193 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/8x8/packed_packed_loop1/avx-unroll.tmpli @@ -0,0 +1,58 @@ + // Tile size: 1x8 + // Accumulators: 0-7 + // Col regs: 8-14 + // Row regs: 15 + + + vmovaps ymm15, [rax] + + vbroadcastss ymm8, dword ptr [rcx + 0 * 4] + vfmadd231ps ymm0, ymm15, ymm8 + + vbroadcastss ymm9, dword ptr [rcx + 1 * 4] + vfmadd231ps ymm1, ymm15, ymm9 + + vbroadcastss ymm10, dword ptr [rcx + 2 * 4] + vfmadd231ps ymm2, ymm15, ymm10 + + vbroadcastss ymm11, dword ptr [rcx + 3 * 4] + vfmadd231ps ymm3, ymm15, ymm11 + + vbroadcastss ymm12, dword ptr [rcx + 4 * 4] + vfmadd231ps ymm4, ymm15, ymm12 + + vbroadcastss ymm13, dword ptr [rcx + 5 * 4] + vfmadd231ps ymm5, ymm15, ymm13 + + vbroadcastss ymm10, dword ptr [rcx + 6 * 4] + vfmadd231ps ymm6, ymm15, ymm10 + + vbroadcastss ymm11, dword ptr [rcx + 7 * 4] + vfmadd231ps ymm7, ymm15, ymm11 + + + vmovaps ymm15, [rax] + + vbroadcastss ymm8, dword ptr [rcx + 0 * 4] + vfmadd231ps ymm0, ymm15, ymm8 + + vbroadcastss ymm9, dword ptr [rcx + 1 * 4] + vfmadd231ps ymm1, ymm15, ymm9 + + vbroadcastss ymm10, dword ptr [rcx + 2 * 4] + vfmadd231ps ymm2, ymm15, ymm10 + + vbroadcastss ymm11, dword ptr [rcx + 3 * 4] + vfmadd231ps ymm3, ymm15, ymm11 + + vbroadcastss ymm12, dword ptr [rcx + 4 * 4] + vfmadd231ps ymm4, ymm15, ymm12 + + vbroadcastss ymm13, dword ptr [rcx + 5 * 4] + vfmadd231ps ymm5, ymm15, ymm13 + + vbroadcastss ymm10, dword ptr [rcx + 6 * 4] + vfmadd231ps ymm6, ymm15, ymm10 + + vbroadcastss ymm11, dword ptr [rcx + 7 * 4] + vfmadd231ps ymm7, ymm15, ymm11 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/8x8/packed_packed_loop1/avx.tmpli b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/8x8/packed_packed_loop1/avx.tmpli new file mode 100644 index 0000000..1af4afe --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/8x8/packed_packed_loop1/avx.tmpli @@ -0,0 +1,30 @@ + // Tile size: 1x8 + // Accumulators: 0-7 + // Col regs: 8-14 + // Row regs: 15 + + vmovaps ymm15, [rax] + + vbroadcastss ymm8, dword ptr [rcx + 0 * 4] + vfmadd231ps ymm0, ymm15, ymm8 + + vbroadcastss ymm9, dword ptr [rcx + 1 * 4] + vfmadd231ps ymm1, ymm15, ymm9 + + vbroadcastss ymm10, dword ptr [rcx + 2 * 4] + vfmadd231ps ymm2, ymm15, ymm10 + + vbroadcastss ymm11, dword ptr [rcx + 3 * 4] + vfmadd231ps ymm3, ymm15, ymm11 + + vbroadcastss ymm12, dword ptr [rcx + 4 * 4] + vfmadd231ps ymm4, ymm15, ymm12 + + vbroadcastss ymm13, dword ptr [rcx + 5 * 4] + vfmadd231ps ymm5, ymm15, ymm13 + + vbroadcastss ymm10, dword ptr [rcx + 6 * 4] + vfmadd231ps ymm6, ymm15, ymm10 + + vbroadcastss ymm11, dword ptr [rcx + 7 * 4] + vfmadd231ps ymm7, ymm15, ymm11 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/avx2_mmm_i32_8x8.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/avx2_mmm_i32_8x8.tmpl new file mode 100644 index 0000000..43f2668 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/avx2_mmm_i32_8x8.tmpl @@ -0,0 +1,661 @@ +{% comment %} +// vim: set syntax=asm : + +/* mmm 8x8: + + ymm0 ymm1 ymm2 ymm3 ymm4 ymm5 ymm6 ymm7 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +{% endcomment %} + +{% if msvc %} + +_text segment +avx2_mmm_i32_8x8_{{suffix}} proc + +{% else %} + +.intel_syntax noprefix +.text +.p2align 5 +.globl {{G}}avx2_mmm_i32_8x8_{{suffix}} +{{G}}avx2_mmm_i32_8x8_{{suffix}}: +.cfi_startproc + +{% endif %} + + push rbp + mov rbp, rsp + +{% if family == "windows" %} +// https://www.agner.org/optimize/calling_conventions.pdf xmm6-15 are not scratch +// https://stackoverflow.com/questions/43358429/save-value-of-xmm-registers + and rsp,-16 + lea rsp,[rsp-160] + vmovaps [rsp], xmm6 + vmovaps [rsp+16*1],xmm7 + vmovaps [rsp+16*2],xmm8 + vmovaps [rsp+16*3],xmm9 + vmovaps [rsp+16*4],xmm10 + vmovaps [rsp+16*5],xmm11 + vmovaps [rsp+16*6],xmm12 + vmovaps [rsp+16*7],xmm13 + vmovaps [rsp+16*8],xmm14 + vmovaps [rsp+16*9],xmm15 + + push rdi + push rsi + + mov rdi, rcx + +{% endif %} + + push rbx + push r12 + push r13 + push r14 + push r15 + + sub rsp, 8 + +{% if family == "unix" %} +.cfi_def_cfa_offset 64 +{% endif %} + + stmxcsr [rsp + 4] +{% if msvc %} + mov rax, 1FC0h +{% else %} + mov rax, 0x1FC0 +{% endif %} + mov [rsp], eax + ldmxcsr [rsp] + +{% include "dispatcher.tmpliq" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rbx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rcx, [rdi + 8] // k + test rcx, rcx + jz {{L}}non_linear_loop + +{{L}}main_loop_packed_packed: + movq xmm8, qword ptr [rax] // read 8 bytes + vpmovsxbw ymm8, xmm8 // promote byte to i32x8 + + vpbroadcastb ymm9, byte ptr [rbx] // broadcast 1 byte from B + vpbroadcastb ymm10, byte ptr [rbx + 1] // broadcast 1 byte from B + vpbroadcastb ymm11, byte ptr [rbx + 2] // broadcast 1 byte from B + vpbroadcastb ymm12, byte ptr [rbx + 3] // broadcast 1 byte from B + vpmovsxbw ymm9, xmm9 // promote byte to i32x8 + vpmovsxbw ymm10, xmm10 // promote byte to i32x8 + vpmovsxbw ymm11, xmm11 // promote byte to i32x8 + vpmovsxbw ymm12, xmm12 // promote byte to i32x8 + + vpmullw ymm9, ymm9, ymm8 + vpmullw ymm10, ymm10, ymm8 + vpmullw ymm11, ymm11, ymm8 + vpmullw ymm12, ymm12, ymm8 + vpmovsxwd ymm9, xmm9 // promote byte to i32x8 + vpmovsxwd ymm10, xmm10 // promote byte to i32x8 + vpmovsxwd ymm11, xmm11 // promote byte to i32x8 + vpmovsxwd ymm12, xmm12 // promote byte to i32x8 + vpaddd ymm0, ymm0, ymm9 + vpaddd ymm1, ymm1, ymm10 + vpaddd ymm2, ymm2, ymm11 + vpaddd ymm3, ymm3, ymm12 + + vpbroadcastb ymm9, byte ptr [rbx + 4] + vpbroadcastb ymm10, byte ptr [rbx + 5] + vpbroadcastb ymm11, byte ptr [rbx + 6] + vpbroadcastb ymm12, byte ptr [rbx + 7] + vpmovsxbw ymm9, xmm9 + vpmovsxbw ymm10, xmm10 + vpmovsxbw ymm11, xmm11 + vpmovsxbw ymm12, xmm12 + + vpmullw ymm9, ymm9, ymm8 + vpmullw ymm10, ymm10, ymm8 + vpmullw ymm11, ymm11, ymm8 + vpmullw ymm12, ymm12, ymm8 + vpmovsxwd ymm9, xmm9 // promote byte to i32x8 + vpmovsxwd ymm10, xmm10 // promote byte to i32x8 + vpmovsxwd ymm11, xmm11 // promote byte to i32x8 + vpmovsxwd ymm12, xmm12 // promote byte to i32x8 + vpaddd ymm4, ymm4, ymm9 + vpaddd ymm5, ymm5, ymm10 + vpaddd ymm6, ymm6, ymm11 + vpaddd ymm7, ymm7, ymm12 + + add rbx, 8 + add rax, 8 + dec rcx + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +{% include "fma_mmm_i32_scalars.tmpliq" from:0, to:7 %} +{% include "fma_mmm_i32_per_rows.tmpliq" mr:8,from:0, to:7 %} +{% include "fma_mmm_i32_per_cols.tmpliq" mr:8,from:0, to:7 %} + +{{L}}add_unicast: + + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + mov r8, [rdi + 32] // item size + + cmp r8, 4 + je {{L}}non_linear_addc_i32 + +{% comment %} +// This is not great as vgatherdps reads 32-bits values and goes beyond our buffer. Probably harmless though. +// Commented and replaced with the "mov al" loop beyond to pacify valgrind. +// ymm14 and ymm15 are the same as in the non_linear_addc_i32 case (compute them before the test right above here. +// {% for i in (0..7) %} +// vpcmpeqd ymm15, ymm15, ymm15 +// vgatherdps ymm12, [ r10 + ymm14 ], ymm15 // 0xxx 1xxx 2xxx 3xxx 4xxx 5xxx 6xxx 7xxx +// +// // we need to go through vpmovsxbd, shuffling naively erases signs +// vpshufb ymm12, ymm12, ymm10 // 0123 0123 0123 0123 4567 4567 4567 4567 +// +// vpermd ymm12, ymm11, ymm12 // 0123 4567 +// vpmovsxbd ymm12, xmm12 // sign extend +// +// vpaddd ymm{{i}}, ymm{{i}}, ymm12 +// add r10, rbx +// {% endfor %} +{% endcomment %} + + {% for col in (0..7) %} + mov r8, r10 + {% for half in (0..1) %} + {% for lane in (0..3) %} + mov al, [ r8 ] + add r8, rsi + movsx eax, al + pinsrd xmm10, eax, {{lane}} + {% endfor %} + vperm2f128 ymm10, ymm10, ymm10, 1 + {% endfor %} + vpaddd ymm{{col}}, ymm{{col}}, ymm10 + add r10, rbx + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}non_linear_addc_i32: + + mov eax, 0 +{% for i in (0..3) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} + vpermq ymm14, ymm14, 78 // 0b01001110 +{% for i in (0..3) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} + vpermq ymm14, ymm14, 78 // 0b01001110 + + +{% if msvc %} + vpbroadcastd ymm10, dword ptr [ offset byte_shuffle ] + vmovups ymm11, dword ptr [ offset i128_shuffle ] +{% else %} + vpbroadcastd ymm10, [ rip + {{L}}byte_shuffle ] + vmovups ymm11, [ rip + {{L}}i128_shuffle ] +{% endif %} + +{% for i in (0..7) %} + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm12, [ r10 + ymm14 ], ymm15 + vpaddd ymm{{i}}, ymm{{i}}, ymm12 + add r10, rbx +{% endfor %} + + jmp {{L}}non_linear_loop + +{% if msvc %} +.data +byte_shuffle dd 201851904 // 0x0c080400 +i128_shuffle dd 0, 4 +.code +{% else %} +{{L}}byte_shuffle: .int 201851904 // 0x0c080400 +{{L}}i128_shuffle: .int 0, 4 +{% endif %} + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vmovups ymm12, [rax] + +{% for i in (0..7) %} + vbroadcastss ymm14, dword ptr [rbx + {{i|times:4}} ] + vpmulld ymm15, ymm12, ymm14 + vpaddd ymm{{i}}, ymm{{i}}, ymm15 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_scale: + mov r8, [ rdi + 16 ] // policy + vbroadcastss ymm8, dword ptr [rdi + 24] // multi + + mov rax, 1 + movq xmm9, rax + vpbroadcastq ymm9, xmm9 // ymm9 <- 1 + + mov rax, [ rdi + 8 ] // xmm10 <- shift + 31 + add rax, 31 + movq xmm10, rax + vpbroadcastq ymm10, xmm10 + + mov rax, 1 + movq xmm11, rax + vpsubq ymm12, ymm10, ymm9 // shift+31 - 1 + vpsllq ymm11, ymm9, xmm12 // ymm11 <- 1 << (shift + 31 - 1) + + cmp r8, 1 + je {{L}}q_scale_rounding_zero + cmp r8, 2 + je {{L}}q_scale_rounding_away + cmp r8, 3 + je {{L}}q_scale_rounding_minus_inf + cmp r8, 4 + je {{L}}q_scale_rounding_plus_inf + cmp r8, 5 + je {{L}}q_scale_rounding_even + cmp r8, 6 + je {{L}}q_scale_rounding_odd + + jmp {{L}}unsupported + +{{L}}q_scale_rounding_zero: // signum * ( (abs + nudge) >> shift ) +{% for i in (0..7) %} + vpabsd ymm14, ymm{{i}} + vpsrldq ymm15, ymm14, 4 // ymm15 <- a1, a2, a3, a4, a5, a6, a7, 0 + vpmuldq ymm14, ymm14, ymm8 // ymm14 <- a0*c, a2*c, a4*c, a6*c + vpmuldq ymm15, ymm15, ymm8 // ymm15 <- a1*c, a3*c, a5*c, a7*c + + vpaddq ymm14, ymm14, ymm11 + vpaddq ymm15, ymm15, ymm11 + + vpsubq ymm14, ymm14, ymm9 + vpsubq ymm15, ymm15, ymm9 + + vpsrlq ymm14, ymm14, xmm10 + vpsrlq ymm15, ymm15, xmm10 + + vpslldq ymm15, ymm15, 4 + vpblendd ymm14, ymm15, ymm14, 85 // 0x55 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}q_scale_rounding_away: // signum * ( (abs + nudge) >> shift ) +{% for i in (0..7) %} + vpabsd ymm14, ymm{{i}} + vpsrldq ymm15, ymm14, 4 // ymm15 <- a1, a2, a3, a4, a5, a6, a7, 0 + vpmuldq ymm14, ymm14, ymm8 // ymm14 <- a0*c, a2*c, a4*c, a6*c + vpmuldq ymm15, ymm15, ymm8 // ymm15 <- a1*c, a3*c, a5*c, a7*c + + vpaddq ymm14, ymm14, ymm11 + vpaddq ymm15, ymm15, ymm11 + + vpsrlq ymm14, ymm14, xmm10 + vpsrlq ymm15, ymm15, xmm10 + + vpslldq ymm15, ymm15, 4 + vpblendd ymm14, ymm15, ymm14, 85 // 0x55 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}q_scale_rounding_minus_inf: // signum * ( (abs << 32 + 1<<30+shift) >> shift ) +{% for i in (0..7) %} + vpabsd ymm14, ymm{{i}} + // sign extract for nudging in the right direction + vpxor ymm13, ymm13, ymm13 + vpcmpgtd ymm13, ymm{{i}}, ymm13 // ymm13 <- s0, s1, ..s8 (signums, as all ones or all zeros) + vpsrld ymm13, ymm13, 31 // then just 0 or 1 + + vpsrldq ymm15, ymm14, 4 // ymm15 <- a1, a2, a3, a4, a5, a6, a7, 0 + vpmuldq ymm14, ymm14, ymm8 // ymm14 <- a0*c, a2*c, a4*c, a6*c + vpmuldq ymm15, ymm15, ymm8 // ymm15 <- a1*c, a3*c, a5*c, a7*c + + vpaddq ymm14, ymm14, ymm11 + vpaddq ymm15, ymm15, ymm11 + + // reinterpret ymm13=s0i32..s7 as i64 and blend with zero to pick the even ones as i64 + vpxor ymm12, ymm12, ymm12 + vpblendd ymm12, ymm12, ymm13, 85 // 0x55 + vpsubq ymm14, ymm14, ymm12 + + vpsrldq ymm13, ymm13, 4 // ymm13 <- s1, s2, .., s7, 0 + vpxor ymm12, ymm12, ymm12 + vpblendd ymm12, ymm12, ymm13, 85 // 0x55 + vpsubq ymm15, ymm15, ymm12 + + vpsrlq ymm14, ymm14, xmm10 + vpsrlq ymm15, ymm15, xmm10 + + vpslldq ymm15, ymm15, 4 + vpblendd ymm14, ymm15, ymm14, 85 // 0x55 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}q_scale_rounding_plus_inf: // signum * ( (abs << 32 + 1<<30+shift) >> shift ) + + vpbroadcastd ymm9, xmm9 + +{% for i in (0..7) %} + vpabsd ymm14, ymm{{i}} + vpxor ymm13, ymm13, ymm13 + + // sign extract for nudging in the right direction + vpcmpgtd ymm13, ymm{{i}}, ymm13 // ymm13 <- s0, s1, ..s8 (signums, as all ones or all zeros) + vpaddd ymm13, ymm13, ymm9 // if val >= 0 { 0i32 } else { 1i32 } + + vpsrldq ymm15, ymm14, 4 // ymm15 <- a1, a2, a3, a4, a5, a6, a7, 0 + vpmuldq ymm14, ymm14, ymm8 // ymm14 <- a0*c, a2*c, a4*c, a6*c + vpmuldq ymm15, ymm15, ymm8 // ymm15 <- a1*c, a3*c, a5*c, a7*c + + vpaddq ymm14, ymm14, ymm11 + vpaddq ymm15, ymm15, ymm11 + + // reinterpret ymm13=s0i32..s7 as i64 and blend with zero to pick the even ones as i64 + vpxor ymm12, ymm12, ymm12 + vpblendd ymm12, ymm12, ymm13, 85 // 0x55 + vpsubq ymm14, ymm14, ymm12 + + vpsrldq ymm13, ymm13, 4 // ymm13 <- s1, s2, .., s7, 0 + vpxor ymm12, ymm12, ymm12 + vpblendd ymm12, ymm12, ymm13, 85 // 0x55 + vpsubq ymm15, ymm15, ymm12 + + vpsrlq ymm14, ymm14, xmm10 + vpsrlq ymm15, ymm15, xmm10 + + vpslldq ymm15, ymm15, 4 + vpblendd ymm14, ymm15, ymm14, 85 // 0x55 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}q_scale_rounding_even: // signum * ( (abs + nudge) >> shift ) +{% for i in (0..7) %} + vpabsd ymm14, ymm{{i}} + vpsrldq ymm15, ymm14, 4 // ymm15 <- a1, a2, a3, a4, a5, a6, a7, 0 + vpmuldq ymm14, ymm14, ymm8 // ymm14 <- a0*c, a2*c, a4*c, a6*c + vpmuldq ymm15, ymm15, ymm8 // ymm15 <- a1*c, a3*c, a5*c, a7*c + + vpsrlq ymm12, ymm14, xmm10 + vpand ymm12, ymm12, ymm9 + vpaddq ymm14, ymm14, ymm12 + vpsubq ymm14, ymm14, ymm9 + + vpsrlq ymm12, ymm15, xmm10 + vpand ymm12, ymm12, ymm9 + vpaddq ymm15, ymm15, ymm12 + vpsubq ymm15, ymm15, ymm9 + + vpaddq ymm14, ymm14, ymm11 + vpaddq ymm15, ymm15, ymm11 + + vpsrlq ymm14, ymm14, xmm10 + vpsrlq ymm15, ymm15, xmm10 + + vpslldq ymm15, ymm15, 4 + vpblendd ymm14, ymm15, ymm14, 85 // 0x55 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_scale_rounding_odd: // signum * ( (abs + nudge) >> shift ) +{% for i in (0..7) %} + vpabsd ymm14, ymm{{i}} + vpsrldq ymm15, ymm14, 4 // ymm15 <- a1, a2, a3, a4, a5, a6, a7, 0 + vpmuldq ymm14, ymm14, ymm8 // ymm14 <- a0*c, a2*c, a4*c, a6*c + vpmuldq ymm15, ymm15, ymm8 // ymm15 <- a1*c, a3*c, a5*c, a7*c + + vpsrlq ymm12, ymm14, xmm10 + vpand ymm12, ymm12, ymm9 + vpsubq ymm14, ymm14, ymm12 + + vpsrlq ymm12, ymm15, xmm10 + vpand ymm12, ymm12, ymm9 + vpsubq ymm15, ymm15, ymm12 + + vpaddq ymm14, ymm14, ymm11 + vpaddq ymm15, ymm15, ymm11 + + vpsrlq ymm14, ymm14, xmm10 + vpsrlq ymm15, ymm15, xmm10 + + vpslldq ymm15, ymm15, 4 + vpblendd ymm14, ymm15, ymm14, 85 // 0x55 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}q_shl: + mov eax, [ rdi + 8 ] // xmm10 <- -shift (8 times) + movd xmm10, eax + vpbroadcastd ymm10, xmm10 + +{% for i in (0..7) %} + vpsllvd ymm{{i}}, ymm{{i}}, ymm10 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_shr: + mov r8, [ rdi + 16 ] // policy + + mov eax, 1 + movd xmm9, eax + vpbroadcastd ymm9, xmm9 // ymm9 <- 1u32 (8 times) + + mov eax, [ rdi + 8 ] // xmm10 <- shift (8 times) + movd xmm10, eax + vpbroadcastd ymm10, xmm10 + + mov ebx, 1 + mov cl, al + sub cl, 1 // rcx <- shift -1 + sal ebx, cl // rbx <- (1 << (shift - 1)) + movd xmm11, ebx + vpbroadcastd ymm11, xmm11 // ymm11 <- "half" + + vpxor ymm12, ymm12, ymm12 // ymm12 <- zeroes + + cmp r8, 1 + je {{L}}q_shr_rounding_zero + cmp r8, 2 + je {{L}}q_shr_rounding_away + cmp r8, 3 + je {{L}}q_shr_rounding_minus_inf + cmp r8, 4 + je {{L}}q_shr_rounding_plus_inf + cmp r8, 5 + je {{L}}q_shr_rounding_even + cmp r8, 6 + je {{L}}q_shr_rounding_odd + + jmp {{L}}unsupported + +{{L}}q_shr_rounding_zero: +{% for i in (0..7) %} + vpabsd ymm14, ymm{{i}} + vpsubd ymm14, ymm14, ymm9 + vpaddd ymm14, ymm14, ymm11 + vpsravd ymm14, ymm14, ymm10 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_shr_rounding_away: +{% for i in (0..7) %} + vpabsd ymm14, ymm{{i}} + vpaddd ymm14, ymm14, ymm11 + vpsravd ymm14, ymm14, ymm10 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_shr_rounding_minus_inf: +{% for i in (0..7) %} + vpsubd ymm{{i}}, ymm{{i}}, ymm9 + vpaddd ymm{{i}}, ymm{{i}}, ymm11 + vpsravd ymm{{i}}, ymm{{i}}, ymm10 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_shr_rounding_plus_inf: +{% for i in (0..7) %} + vpaddd ymm{{i}}, ymm{{i}}, ymm11 + vpsravd ymm{{i}}, ymm{{i}}, ymm10 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_shr_rounding_even: +{% for i in (0..7) %} + vpabsd ymm14, ymm{{i}} + vpsravd ymm13, ymm14, ymm10 + vpand ymm13, ymm13, ymm9 + vpsubd ymm13, ymm13, ymm9 // nudge = ((abs >>l shift) & 0x01) - 1 + vpaddd ymm14, ymm14, ymm13 // add nudge + vpaddd ymm14, ymm14, ymm11 // add half + vpsravd ymm14, ymm14, ymm10 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_shr_rounding_odd: +{% for i in (0..7) %} + vpabsd ymm14, ymm{{i}} + vpsravd ymm13, ymm14, ymm10 + vpand ymm13, ymm13, ymm9 + vpsubd ymm13, ymm12, ymm13 // nudge = - ((abs >>l shift) & 0x01) + vpaddd ymm14, ymm14, ymm13 // add nudge + vpaddd ymm14, ymm14, ymm11 // add half + vpsravd ymm14, ymm14, ymm10 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rdx, [rdi + 24] // col stride + mov rcx, [rdi + 32] // item size + + cmp rcx, 4 + je {{L}}store_strides_i32 + + {% for col in (0..7) %} + mov r10, r8 + {% for row in (0..3) %} + extractps ebx, xmm{{col}}, {{row}} + mov byte ptr [r10], bl + add r10, rsi + {% endfor %} + vperm2f128 ymm{{col}}, ymm{{col}}, ymm{{col}}, 1 + {% for row in (0..3) %} + extractps ebx, xmm{{col}}, {{row}} + mov byte ptr [r10], bl + add r10, rsi + {% endfor %} + add r8, rdx + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store_strides_i32: + {% for col in (0..7) %} + mov r10, r8 + {% for row in (0..3) %} + extractps ebx, xmm{{col}}, {{row}} + mov dword ptr [r10], ebx + add r10, rsi + {% endfor %} + vperm2f128 ymm{{col}}, ymm{{col}}, ymm{{col}}, 1 + {% for row in (0..3) %} + extractps ebx, xmm{{col}}, {{row}} + mov dword ptr [r10], ebx + add r10, rsi + {% endfor %} + add r8, rdx + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}return: + ldmxcsr [rsp + 4] + add rsp, 8 + + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + +{% if family == "windows" %} + pop rsi + pop rdi + + vmovaps xmm15, [rsp+16*9] + vmovaps xmm14, [rsp+16*8] + vmovaps xmm13, [rsp+16*7] + vmovaps xmm12, [rsp+16*6] + vmovaps xmm11, [rsp+16*5] + vmovaps xmm10, [rsp+16*4] + vmovaps xmm9, [rsp+16*3] + vmovaps xmm8, [rsp+16*2] + vmovaps xmm7, [rsp+16*1] + vmovaps xmm6, [rsp] +{% endif %} + + mov rsp, rbp + pop rbp + ret + + +{{L}}one_32bit: +{% if msvc %} + dd 1 +{% else %} + .int 1 +{% endif %} + +{% if msvc %} +avx2_mmm_i32_8x8_{{suffix}} endp +_text ends +end +{% else %} +.cfi_endproc +{% endif %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/dispatcher.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/dispatcher.tmpliq new file mode 100644 index 0000000..1c63f72 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/dispatcher.tmpliq @@ -0,0 +1,40 @@ +// vim: set syntax=asm : + +{{L}}non_linear: + +{{L}}non_linear_loop_enter: + sub rdi, 40 +{{L}}non_linear_loop: + add rdi, 40 + mov rax, [rdi] + + mov r8, {{ jump_table | size }} + cmp rax, 0 + cmovl rax, r8 + cmp rax, {{ jump_table | size }} + cmovg rax, r8 + +{% if msvc %} + lea r8, [ offset {{L}}jmp_table ] +{% else %} + lea r8, [ rip + {{L}}jmp_table ] +{% endif %} + movsxd r9, dword ptr [ r8 + rax * 4 ] + lea r8, [ r8 + r9 ] + jmp r8 + +{{L}}jmp_table: +{% for j in jump_table %} + {{long}} {{L}}{{j}}-{{L}}jmp_table +{% endfor %} + {{long}} {{L}}unsupported-{{L}}jmp_table + +{{L}}unsupported: + mov rax, 1 + jmp {{L}}return + + +{{L}}done: + mov rax, 0 + jmp {{L}}return + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_16x5.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_16x5.tmpl new file mode 100644 index 0000000..40ae63f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_16x5.tmpl @@ -0,0 +1,142 @@ +{% comment %} +// vim: set syntax=asm : +/* mmm 16 x 5: + + ymm0 ymm2 ymm4 ymm6 ymm8 + ymm1 ymm3 ymm5 ymm7 ymm9 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +{% endcomment %} + +{% include "preamble.tmpliq" size:"16x5", suffix:suffix, G:G %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + test rbx, rbx + jz {{L}}non_linear_loop + +{{L}}main_loop_packed_packed: + {% include "2x5/packed_packed_loop1/avx.tmpli" %} + + add rcx, 20 + add rax, 64 + dec rbx + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +// NON LINEAR / ADDC + +{% include "fma_mmm_f32_scalars.tmpliq" from:0, to:9 %} +{% include "fma_mmm_f32_per_rows.tmpliq" mr:16, from:0, to:9 %} +{% include "fma_mmm_f32_per_cols.tmpliq" mr:16, from:0, to:9 %} + +{{L}}add_unicast: + + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + mov eax, 0 +{% for i in (0..3) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in (0..3) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} + + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + + lea r8, [ r10 + rsi * 8 ] + +{% for i in (0..4) %} + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm12, [ r10 + ymm14 ], ymm15 + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm13, [ r8 + ymm14 ], ymm15 + add r10, rbx + add r8, rbx + vaddps ymm{{i | times:2 }}, ymm{{i | times:2}}, ymm12 + vaddps ymm{{i | times:2 | plus: 1}}, ymm{{i | times:2 | plus:1 }}, ymm13 +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vmovups ymm12, [rax] + vmovups ymm13, [rax + 32] + +{% for i in (0..4) %} + vbroadcastss ymm14, dword ptr [rbx + {{i|times:4}} ] + vfmadd231ps ymm{{i|times:2}}, ymm12, ymm14 + vfmadd231ps ymm{{i|times:2|plus:1}}, ymm13, ymm14 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + lea r12, [ r8 + 4 * rbx ] + lea r11, [ r10 + rbx ] + cmp rbx, 64 + jne {{L}}store_strides_generic + + {% for row in (0..1) %} + {% for col in (0..4) %} + vmovups ymmword ptr [r{{col|plus:8}}], ymm{{col|times:2|plus:row}} + add r{{col|plus:8}}, 32 + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store_strides_generic: + // tops of cols + + {% for quarter in (0..3) %} + {% if quarter != 0 %} + // move next four rows at top (xmm0,2,..10) + vperm2f128 ymm0, ymm0, ymm1, {{quarter}} + vperm2f128 ymm2, ymm2, ymm3, {{quarter}} + vperm2f128 ymm4, ymm4, ymm5, {{quarter}} + vperm2f128 ymm6, ymm6, ymm7, {{quarter}} + vperm2f128 ymm8, ymm8, ymm9, {{quarter}} + {% endif %} + {% for row in (0..3) %} + {% for i in (0..4) %} + vextractps dword ptr [r{{i | plus: 8}}], xmm{{i | times:2}}, {{row}} + add r{{i | plus: 8}}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{% include "postamble.tmpliq" size:"16x5", suffix:suffix, G:G, L:L %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_16x6.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_16x6.tmpl new file mode 100644 index 0000000..77893d5 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_16x6.tmpl @@ -0,0 +1,132 @@ +{% comment %} +// vim: set syntax=asm : + +/* mmm 16 x 6: + + ymm0 ymm2 ymm4 ymm6 ymm8 ymm10 + ymm1 ymm3 ymm5 ymm7 ymm9 ymm11 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +{% endcomment %} + +{% include "preamble.tmpliq" size:"16x6", suffix:suffix, G:G %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + test rcx, rcx + jz {{L}}non_linear_loop + +{{L}}main_loop_packed_packed: + {% include "2x6/packed_packed_loop1/original.tmpli" %} + + add rcx, 24 + add rax, 64 + dec rbx + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +// NON LINEAR / ADDC + +{% include "fma_mmm_f32_scalars.tmpliq" from:0, to:11 %} +{% include "fma_mmm_f32_per_rows.tmpliq" mr:16, from:0, to:11 %} +{% include "fma_mmm_f32_per_cols.tmpliq" mr:16, from:0, to:11 %} + +{{L}}add_unicast: + + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + mov eax, 0 +{% for i in (0..3) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in (0..3) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} + + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + + lea r8, [ r10 + rsi * 8 ] + +{% for i in (0..5) %} + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm12, [ r10 + ymm14 ], ymm15 + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm13, [ r8 + ymm14 ], ymm15 + add r10, rbx + add r8, rbx + vaddps ymm{{i | times:2 }}, ymm{{i | times:2}}, ymm12 + vaddps ymm{{i | times:2 | plus: 1}}, ymm{{i | times:2 | plus:1 }}, ymm13 +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vmovups ymm12, [rax] + vmovups ymm13, [rax + 32] + +{% for i in (0..5) %} + vbroadcastss ymm14, dword ptr [rbx + {{i|times:4}} ] + vfmadd231ps ymm{{i|times:2}}, ymm12, ymm14 + vfmadd231ps ymm{{i|times:2|plus:1}}, ymm13, ymm14 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + // tops of cols + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + lea r12, [ r8 + 4 * rbx ] + lea r11, [ r10 + rbx ] + lea r13, [ r12 + rbx ] + + {% for quarter in (0..3) %} + {% if quarter != 0 %} + // move next four rows at top (xmm0,2,..10) + vperm2f128 ymm0, ymm0, ymm1, {{quarter}} + vperm2f128 ymm2, ymm2, ymm3, {{quarter}} + vperm2f128 ymm4, ymm4, ymm5, {{quarter}} + vperm2f128 ymm6, ymm6, ymm7, {{quarter}} + vperm2f128 ymm8, ymm8, ymm9, {{quarter}} + vperm2f128 ymm10, ymm10, ymm11, {{quarter}} + {% endif %} + {% for row in (0..3) %} + {% for i in (0..5) %} + vextractps dword ptr [r{{i | plus: 8}}], xmm{{i | times:2}}, {{row}} + add r{{i | plus: 8}}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{% include "postamble.tmpliq" size:"16x6", suffix:suffix, G:G, L:L %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_24x4.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_24x4.tmpl new file mode 100644 index 0000000..04a2178 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_24x4.tmpl @@ -0,0 +1,157 @@ +{% comment %} +// vim: set syntax=asm : +/* mmm 24 x 4: + + ymm0 ymm3 ymm6 ymm10 + ymm1 ymm4 ymm7 ymm11 + ymm2 ymm5 ymm8 ymm12 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +{% endcomment %} + +{% include "preamble.tmpliq" size:"24x4", suffix:suffix, G:G %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + test rbx, rbx + jz {{L}}non_linear_loop + +{{L}}main_loop_packed_packed: + {% include "3x4/packed_packed_loop1/avx.tmpli" %} + + add rcx, 16 + add rax, 96 + dec rbx + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +// NON LINEAR / ADDC + +{% include "fma_mmm_f32_scalars.tmpliq" from:0, to:11 %} +{% include "fma_mmm_f32_per_rows.tmpliq" mr:24, from:0, to:11 %} +{% include "fma_mmm_f32_per_cols.tmpliq" mr:24, from:0, to:11 %} + +{{L}}add_unicast: + + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + cmp rsi, 4 + jne {{L}}unicast_generic + + lea r9, [ r8 + rbx ] + lea r10, [ r9 + rbx] + lea r11, [ r10 + rbx ] + lea r12, [ r11 + rbx ] + +{% for col in (0..3) %} + {% for row in (0..2) %} + vmovups ymm12, [ r{{col|plus:8}} ] + add r{{col|plus:8}}, 32 + vaddps ymm{{col|times:3|plus:row}}, ymm{{col|times:3|plus:row}}, ymm12 + {% endfor %} +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}unicast_generic: + mov eax, 0 +{% for i in (0..3) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in (0..3) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} + +// mov r12, [0] + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + + lea r9, [ r8 + rsi * 8 ] + lea r10, [ r9 + rsi * 8 ] + +{% for col in (0..3) %} + {% for row in (0..2) %} + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm12, [ r{{row|plus:8}} + ymm14 ], ymm15 + add r{{row|plus:8}}, rbx + vaddps ymm{{col|times:3|plus:row}}, ymm{{col|times:3|plus:row}}, ymm12 + {% endfor %} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vmovups ymm12, [rax] + vmovups ymm13, [rax + 32] + vmovups ymm15, [rax + 64] +{% for i in (0..3) %} + vbroadcastss ymm14, dword ptr [rbx + {{i|times:4}} ] + vfmadd231ps ymm{{i|times:3}}, ymm12, ymm14 + vfmadd231ps ymm{{i|times:3|plus:1}}, ymm13, ymm14 + vfmadd231ps ymm{{i|times:3|plus:2}}, ymm15, ymm14 +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + lea r11, [ r10 + rbx ] + + cmp rsi, 4 + jne {{L}}store_strides_generic + + {% for col in (0..3) %} + {% for row in (0..2) %} + vmovups ymmword ptr [r{{col|plus:8}}], ymm{{col|times:3|plus:row}} + add r{{col|plus:8}}, 32 + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store_strides_generic: + {% for col in (0..3) %} + {% for row in (0..2) %} + {% for i in (0..3) %} + vextractps dword ptr [r{{col | plus: 8}}], xmm{{col | times:3 | plus:row}}, {{i}} + add r{{col | plus: 8}}, rsi + {% endfor %} + vperm2f128 ymm{{col | times:3 | plus:row}}, ymm{{col | times:3 | plus:row}}, ymm{{col | times:3 | plus:row}}, 1 + {% for i in (0..3) %} + vextractps dword ptr [r{{col | plus: 8}}], xmm{{col | times:3|plus:row}}, {{i}} + add r{{col | plus: 8}}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + jmp {{L}}non_linear_loop + +{% include "postamble.tmpliq" size:"24x4", suffix:suffix, G:G, L:L %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_32x3.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_32x3.tmpl new file mode 100644 index 0000000..766514f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_32x3.tmpl @@ -0,0 +1,159 @@ +{% comment %} +// vim: set syntax=asm : +/* mmm 16 x 5: + + ymm0 ymm4 ymm8 + ymm1 ymm5 ymm9 + ymm2 ymm6 ymm10 + ymm3 ymm7 ymm11 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +{% endcomment %} + +{% include "preamble.tmpliq" size:"32x3", suffix:suffix, G:G %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + test rbx, rbx + jz {{L}}non_linear_loop + +{{L}}main_loop_packed_packed: + {% include "4x3/packed_packed_loop1/avx.tmpli" %} + + add rcx, 12 + add rax, 128 + dec rbx + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +// NON LINEAR / ADDC + +{% include "fma_mmm_f32_scalars.tmpliq" from:0, to:11 %} +{% include "fma_mmm_f32_per_rows.tmpliq" mr:32, from:0, to:11 %} +{% include "fma_mmm_f32_per_cols.tmpliq" mr:32, from:0, to:11 %} + +{{L}}add_unicast: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + cmp rsi, 4 + jne {{L}}unicast_generic + + lea r9, [ r8 + rbx ] + lea r10, [ r9 + rbx] + lea r11, [ r10 + rbx ] + +{% for col in (0..2) %} + {% for row in (0..3) %} + vmovups ymm12, [ r{{col|plus:8}} ] + add r{{col|plus:8}}, 32 + vaddps ymm{{col|times:4|plus:row}}, ymm{{col|times:4|plus:row}}, ymm12 + {% endfor %} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}unicast_generic: + mov eax, 0 +{% for i in (0..3) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in (0..3) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} + +// mov r12, [0] + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + + lea r9, [ r8 + rsi * 8 ] + lea r10, [ r9 + rsi * 8 ] + lea r11, [ r10 + rsi * 8 ] + +{% for col in (0..2) %} + {% for row in (0..3) %} + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm12, [ r{{row|plus:8}} + ymm14 ], ymm15 + add r{{row|plus:8}}, rbx + vaddps ymm{{col|times:4|plus:row}}, ymm{{col|times:4|plus:row}}, ymm12 + {% endfor %} +{% endfor %} + + jmp {{L}}non_linear_loop + + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vbroadcastss ymm13, dword ptr [rbx] + vbroadcastss ymm14, dword ptr [rbx + 4] + vbroadcastss ymm15, dword ptr [rbx + 8] +{% for i in (0..3) %} + vmovups ymm12, [rax + {{i|times:32}}] + vfmadd231ps ymm{{0|plus:i}}, ymm12, ymm13 + vfmadd231ps ymm{{4|plus:i}}, ymm12, ymm14 + vfmadd231ps ymm{{8|plus:i}}, ymm12, ymm15 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + + cmp rsi, 4 + jne {{L}}store_strides_generic + + + {% for col in (0..2) %} + {% for row in (0..3) %} + vmovups ymmword ptr [r{{col|plus:8}}], ymm{{col|times:4|plus:row}} + add r{{col|plus:8}}, 32 + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store_strides_generic: + + {% for col in (0..2) %} + {% for row in (0..3) %} + {% for i in (0..3) %} + vextractps dword ptr [r{{col | plus: 8}}], xmm{{col | times:4 | plus:row}}, {{i}} + add r{{col | plus: 8}}, rsi + {% endfor %} + vperm2f128 ymm{{col | times:4 | plus:row}}, ymm{{col | times:4 | plus:row}}, ymm{{col | times:4 | plus:row}}, 1 + {% for i in (0..3) %} + vextractps dword ptr [r{{col | plus: 8}}], xmm{{col | times:4|plus:row}}, {{i}} + add r{{col | plus: 8}}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + jmp {{L}}non_linear_loop + +{% include "postamble.tmpliq" size:"32x3", suffix:suffix, G:G, L:L %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_40x2.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_40x2.tmpl new file mode 100644 index 0000000..7025a61 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_40x2.tmpl @@ -0,0 +1,159 @@ +{% comment %} +// vim: set syntax=asm : +/* mmm 40 x 5: + + ymm0 ymm5 + ymm1 ymm6 + ymm2 ymm7 + ymm3 ymm8 + ymm4 ymm9 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +{% endcomment %} + +{% include "preamble.tmpliq" size:"40x2", suffix:suffix, G:G %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + test rbx, rbx + jz {{L}}non_linear_loop + +{{L}}main_loop_packed_packed: + {% include "5x2/packed_packed_loop1/avx.tmpli" %} + + add rcx, 8 + add rax, 160 + dec rbx + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +// NON LINEAR / ADDC + +{% include "fma_mmm_f32_scalars.tmpliq" from:0, to:9 %} +{% include "fma_mmm_f32_per_rows.tmpliq" mr:40, from:0, to:9 %} +{% include "fma_mmm_f32_per_cols.tmpliq" mr:40, from:0, to:9 %} + +{{L}}add_unicast: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + cmp rsi, 4 + jne {{L}}unicast_generic + + lea r9, [ r8 + rbx ] + lea r10, [ r9 + rbx] + lea r11, [ r10 + rbx ] + lea r12, [ r11 + rbx ] + + +{% for col in (0..1) %} + {% for row in (0..4) %} + vmovups ymm12, [ r{{col|plus:8}} ] + add r{{col|plus:8}}, 32 + vaddps ymm{{col|times:5|plus:row}}, ymm{{col|times:5|plus:row}}, ymm12 + {% endfor %} +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}unicast_generic: + mov eax, 0 +{% for i in (0..3) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in (0..3) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} + + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + + lea r9, [ r8 + rsi * 8] + lea r10, [ r9 + rsi * 8] + lea r11, [ r10 + rsi * 8] + lea r12, [ r11 + rsi * 8] + +{% for col in (0..1) %} + {% for row in (0..4) %} + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm12, [ r{{row|plus:8}} + ymm14 ], ymm15 + add r{{row|plus:8}}, rbx + vaddps ymm{{col|times:5|plus:row}}, ymm{{col|times:5|plus:row}}, ymm12 + {% endfor %} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vbroadcastss ymm10, dword ptr [rbx] + vbroadcastss ymm11, dword ptr [rbx + 4] +{% for i in (0..4) %} + vmovups ymm12, [rax + {{i|times:32}}] + vfmadd231ps ymm{{0|plus:i}}, ymm12, ymm10 + vfmadd231ps ymm{{5|plus:i}}, ymm12, ymm11 +{% endfor %} + jmp {{L}}non_linear_loop + + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + lea r11, [ r10 + rbx ] + lea r12, [ r10 + 2 * rbx ] + + cmp rsi, 4 + jne {{L}}store_strides_generic + + {% for col in (0..1) %} + {% for row in (0..4) %} + vmovups ymmword ptr [r{{col|plus:8}}], ymm{{col|times:5|plus:row}} + add r{{col|plus:8}}, 32 + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store_strides_generic: + {% for col in (0..1) %} + {% for row in (0..4) %} + {% for i in (0..3) %} + vextractps dword ptr [r{{col | plus: 8}}], xmm{{col | times:5 | plus:row}}, {{i}} + add r{{col | plus: 8}}, rsi + {% endfor %} + vperm2f128 ymm{{col | times:5 | plus:row}}, ymm{{col | times:5 | plus:row}}, ymm{{col | times:5 | plus:row}}, 1 + {% for i in (0..3) %} + vextractps dword ptr [r{{col | plus: 8}}], xmm{{col | times:5|plus:row}}, {{i}} + add r{{col | plus: 8}}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + jmp {{L}}non_linear_loop + +{% include "postamble.tmpliq" size:"40x2", suffix:suffix, G:G, L:L %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_64x1.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_64x1.tmpl new file mode 100644 index 0000000..907240b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_64x1.tmpl @@ -0,0 +1,118 @@ +{% comment %} +// vim: set syntax=asm : + +/* mmm 64 x 1 + + ymm0 + ymm1 + ... + ymm8 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +{% endcomment %} + +{% include "preamble.tmpliq" size:"64x1", suffix:suffix, G:G %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rbx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rcx, [rdi + 8] // k + test rcx, rcx + jz {{L}}non_linear_loop + +{{L}}main_loop_packed_packed: + vbroadcastss ymm14, dword ptr [rbx] + +{% for i in (0..7) %} + vmovaps ymm12, [rax + {{i | times: 32}}] + vfmadd231ps ymm{{i}}, ymm12, ymm14 +{% endfor %} + + add rbx, 4 + add rax, 256 + dec rcx + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +{% include "fma_mmm_f32_scalars.tmpliq" from:0, to:7 %} +{% include "fma_mmm_f32_per_rows.tmpliq" mr:64, from:0, to:7 %} +{% include "fma_mmm_f32_per_cols.tmpliq" mr:64, from:0, to:7 %} + +{{L}}add_unicast: + + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + + mov eax, 0 +{% for i in (0..3) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in (0..3) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} + + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + +{% for i in (0..7) %} + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm12, [ r10 + ymm14 ], ymm15 + + vaddps ymm{{i}}, ymm{{i}}, ymm12 + lea r10, [ r10 + rsi * 8 ] +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vbroadcastss ymm14, dword ptr [rbx] + +{% for i in (0..7) %} + vmovups ymm12, [rax + {{i|times:32}}] + vfmadd231ps ymm{{i}}, ymm12, ymm14 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + + {% for vec in (0..7) %} + {% for half in (0..1) %} + {% if half == 0 %} + movaps xmm9, xmm{{vec}} + {% else %} + vperm2f128 ymm9, ymm{{vec}}, ymm{{vec}}, 1 + {% endif %} + {% for row in (0..3) %} + vextractps dword ptr [r8], xmm9, {{row}} + add r8, rsi + {% endfor %} + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + + +{% include "postamble.tmpliq" size:"64x1", suffix:suffix, G:G, L:L %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_8x8.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_8x8.tmpl new file mode 100644 index 0000000..9417af2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_8x8.tmpl @@ -0,0 +1,128 @@ +{% comment %} +// vim: set syntax=asm : + +/* mmm 16 x 6: + + ymm0 ymm2 ymm4 ymm6 ymm8 ymm10 + ymm1 ymm3 ymm5 ymm7 ymm9 ymm11 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +{% endcomment %} + +{% include "preamble.tmpliq" size:"8x8", suffix:suffix, G:G %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rbx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rcx, [rdi + 8] // k + test rcx, rcx + jz {{L}}non_linear_loop + +{{L}}main_loop_packed_packed: + vmovaps ymm12, [rax] + + {% for i in (0..7) %} + vbroadcastss ymm14, dword ptr [rbx + {{i}} * 4] + vfmadd231ps ymm{{i}}, ymm12, ymm14 + {% endfor %} + + add rax, 32 + add rbx, 32 + dec rcx + jnz {{L}}main_loop_packed_packed + jmp {{L}}non_linear_loop + +// NON LINEAR / ADDC + +{% include "fma_mmm_f32_scalars.tmpliq" from:0, to:7 %} +{% include "fma_mmm_f32_per_rows.tmpliq" mr:8, from:0, to:7 %} +{% include "fma_mmm_f32_per_cols.tmpliq" mr:8, from:0, to:7 %} + +{{L}}add_unicast: + + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + mov eax, 0 +{% for i in (0..3) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in (0..3) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} + + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + +{% for i in (0..7) %} + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm12, [ r10 + ymm14 ], ymm15 + add r10, rbx + vaddps ymm{{i}}, ymm{{i}}, ymm12 +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vmovups ymm12, [rax] + +{% for i in (0..7) %} + vbroadcastss ymm14, dword ptr [rbx + {{i|times:4}} ] + vfmadd231ps ymm{{i}}, ymm12, ymm14 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + // tops of cols + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + lea r12, [ r8 + 4 * rbx ] + lea r11, [ r10 + rbx ] + lea r13, [ r12 + rbx ] + lea r14, [ r12 + 2 * rbx ] + lea r15, [ r13 + 2 * rbx ] + + {% for quarter in (0..1) %} + {% if quarter != 0 %} + // move next four rows at top (xmm0,2,..10) + {% for r in (0..7) %} + vperm2f128 ymm{{r}}, ymm{{r}}, ymm{{r}}, {{quarter}} + {% endfor %} + {% endif %} + {% for row in (0..3) %} + {% for i in (0..7) %} + vextractps dword ptr [r{{i | plus: 8}}], xmm{{i}}, {{row}} + add r{{i | plus: 8}}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + + +{% include "postamble.tmpliq" size:"8x8", suffix:suffix, G:G, L:L %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_per_cols.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_per_cols.tmpliq new file mode 100644 index 0000000..f5c9de9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_per_cols.tmpliq @@ -0,0 +1,9 @@ +// vim: set syntax=asm : + +{% include "fma_mmm_ymm_per_col.tmpliq" label:"per_col_min", op:"vminps", mr:mr, from:from, to:to%} +{% include "fma_mmm_ymm_per_col.tmpliq" label:"per_col_max", op:"vmaxps", mr:mr, from:from, to:to%} +{% include "fma_mmm_ymm_per_col.tmpliq" label:"per_col_add", op:"vaddps", mr:mr, from:from, to:to%} +{% include "fma_mmm_ymm_per_col.tmpliq" label:"per_col_mul", op:"vmulps", mr:mr, from:from, to:to%} +{% include "fma_mmm_ymm_per_col.tmpliq" label:"per_col_sub", op:"vsubps", from:from, to:to%} +{% include "fma_mmm_ymm_per_col.tmpliq" label:"per_col_sub_flipped", op:"vsubps", from:from, to:to, flipped: true%} + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_per_rows.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_per_rows.tmpliq new file mode 100644 index 0000000..aeeac13 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_per_rows.tmpliq @@ -0,0 +1,9 @@ +// vim: set syntax=asm : + +{% include "fma_mmm_ymm_per_row.tmpliq" label:"per_row_min", op:"vminps", mr:mr, from:from, to:to%} +{% include "fma_mmm_ymm_per_row.tmpliq" label:"per_row_max", op:"vmaxps", mr:mr, from:from, to:to%} +{% include "fma_mmm_ymm_per_row.tmpliq" label:"per_row_add", op:"vaddps", mr:mr, from:from, to:to%} +{% include "fma_mmm_ymm_per_row.tmpliq" label:"per_row_mul", op:"vmulps", mr:mr, from:from, to:to%} +{% include "fma_mmm_ymm_per_row.tmpliq" label:"per_row_sub", op:"vsubps", from:from, to:to%} +{% include "fma_mmm_ymm_per_row.tmpliq" label:"per_row_sub_flipped", op:"vsubps", from:from, to:to, flipped: true%} + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_scalars.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_scalars.tmpliq new file mode 100644 index 0000000..c43b72b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_f32_scalars.tmpliq @@ -0,0 +1,14 @@ +// vim: set syntax=asm : + +{% include "fma_mmm_ymm_scalar.tmpliq" label:"scalar_min", op:"vminps", from:from, to:to%} +{% include "fma_mmm_ymm_scalar.tmpliq" label:"scalar_max", op:"vmaxps", from:from, to:to%} +{% include "fma_mmm_ymm_scalar.tmpliq" label:"scalar_add", op:"vaddps", from:from, to:to%} +{% include "fma_mmm_ymm_scalar.tmpliq" label:"scalar_mul", op:"vmulps", from:from, to:to%} +{% include "fma_mmm_ymm_scalar.tmpliq" label:"scalar_sub", op:"vsubps", from:from, to:to%} +{% include "fma_mmm_ymm_scalar.tmpliq" label:"scalar_sub_flipped", op:"vsubps", from:from, to:to, flipped: true%} + +{{L}}q_scale: +{{L}}q_shl: +{{L}}q_shr: + jmp {{L}}unsupported + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_i32_per_cols.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_i32_per_cols.tmpliq new file mode 100644 index 0000000..aa975d1 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_i32_per_cols.tmpliq @@ -0,0 +1,9 @@ +// vim: set syntax=asm : + +{% include "fma_mmm_ymm_per_col.tmpliq" label:"per_col_min", op:"vpminsd", mr:mr, from:from, to:to%} +{% include "fma_mmm_ymm_per_col.tmpliq" label:"per_col_max", op:"vpmaxsd", mr:mr, from:from, to:to%} +{% include "fma_mmm_ymm_per_col.tmpliq" label:"per_col_add", op:"vpaddd", mr:mr, from:from, to:to%} +{% include "fma_mmm_ymm_per_col.tmpliq" label:"per_col_mul", op:"vpmulld", mr:mr, from:from, to:to%} +{% include "fma_mmm_ymm_per_col.tmpliq" label:"per_col_sub", op:"vpsubd", from:from, to:to%} +{% include "fma_mmm_ymm_per_col.tmpliq" label:"per_col_sub_flipped", op:"vpsubd", from:from, to:to, flipped: true%} + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_i32_per_rows.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_i32_per_rows.tmpliq new file mode 100644 index 0000000..e2b85cc --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_i32_per_rows.tmpliq @@ -0,0 +1,9 @@ +// vim: set syntax=asm : + +{% include "fma_mmm_ymm_per_row.tmpliq" label:"per_row_min", op:"vpminsd", mr:mr, from:from, to:to%} +{% include "fma_mmm_ymm_per_row.tmpliq" label:"per_row_max", op:"vpmaxsd", mr:mr, from:from, to:to%} +{% include "fma_mmm_ymm_per_row.tmpliq" label:"per_row_add", op:"vpaddd", mr:mr, from:from, to:to%} +{% include "fma_mmm_ymm_per_row.tmpliq" label:"per_row_mul", op:"vpmulld", mr:mr, from:from, to:to%} +{% include "fma_mmm_ymm_per_row.tmpliq" label:"per_row_sub", op:"vpsubd", from:from, to:to%} +{% include "fma_mmm_ymm_per_row.tmpliq" label:"per_row_sub_flipped", op:"vpsubd", from:from, to:to, flipped: true%} + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_i32_scalars.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_i32_scalars.tmpliq new file mode 100644 index 0000000..d1b163a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_i32_scalars.tmpliq @@ -0,0 +1,8 @@ +// vim: set syntax=asm : + +{% include "fma_mmm_ymm_scalar.tmpliq" label:"scalar_min", op:"vpminsd", from:from, to:to%} +{% include "fma_mmm_ymm_scalar.tmpliq" label:"scalar_max", op:"vpmaxsd", from:from, to:to%} +{% include "fma_mmm_ymm_scalar.tmpliq" label:"scalar_mul", op:"vpmulld", from:from, to:to%} +{% include "fma_mmm_ymm_scalar.tmpliq" label:"scalar_add", op:"vpaddd", from:from, to:to%} +{% include "fma_mmm_ymm_scalar.tmpliq" label:"scalar_sub", op:"vpsubd", from:from, to:to%} +{% include "fma_mmm_ymm_scalar.tmpliq" label:"scalar_sub_flipped", op:"vpsubd", from:from, to:to, flipped: true%} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_ymm_per_col.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_ymm_per_col.tmpliq new file mode 100644 index 0000000..d25dd59 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_ymm_per_col.tmpliq @@ -0,0 +1,29 @@ +// vim: set syntax=asm : + +{{L}}{{label}}: + mov rax, [ rdi + 8 ] + +{% capture mr_over_8 %}{{ mr | divided_by: 8}}{%endcapture%} +{% capture mr_over_8_min_1 %}{{ mr | divided_by: 8 | minus: 1}}{%endcapture%} + +{%capture tmp%}{{to | plus: 1 }}{%endcapture%} + +{%capture cols%}{{to | plus: 1| minus:from| divided_by:mr_over_8}}{%endcapture%} +{%capture cols_min_1%}{{to | plus: 1| minus:from| divided_by:mr_over_8|minus:1}}{%endcapture%} +// {{to|minus:from|plus:1}} cols:{{cols}} + +{% for right in (0..cols_min_1) %} + vbroadcastss ymm{{tmp}}, dword ptr [ rax ] + add rax, 4 + + {% for down in (0..mr_over_8_min_1) %} + {%capture acc%}{{mr_over_8|times:right|plus:from|plus:down}}{%endcapture%} + {% if flipped %} + {{op}} ymm{{acc}}, ymm{{acc}}, ymm{{tmp}} + {% else %} + {{op}} ymm{{acc}}, ymm{{tmp}}, ymm{{acc}} + {% endif %} + {% endfor %} +{% endfor %} + + jmp {{L}}non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_ymm_per_row.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_ymm_per_row.tmpliq new file mode 100644 index 0000000..bcae97a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_ymm_per_row.tmpliq @@ -0,0 +1,23 @@ +// vim: set syntax=asm : + +{{L}}{{label}}: + mov rax, [ rdi + 8 ] + +{% capture mr_over_8 %}{{ mr | divided_by: 8}}{%endcapture%} +{% capture mr_over_8_min_1 %}{{ mr | divided_by: 8 | minus: 1}}{%endcapture%} + +{% for ix in (0..mr_over_8_min_1) %} + vmovups ymm{{to | plus: 1 | plus: ix}}, [rax + {{ix | times: 32}}] +{% endfor %} + +{% if flipped %} + {% for acc in (from..to) %} + {{op}} ymm{{acc}}, ymm{{acc}}, ymm{{ acc | modulo: mr_over_8 | plus: to | plus: 1 }} + {% endfor %} +{% else %} + {% for acc in (from..to) %} + {{op}} ymm{{acc}}, ymm{{ acc | modulo: mr_over_8 | plus: to | plus: 1 }}, ymm{{acc}} + {% endfor %} +{% endif %} + + jmp {{L}}non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_ymm_scalar.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_ymm_scalar.tmpliq new file mode 100644 index 0000000..71ce1a8 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_mmm_ymm_scalar.tmpliq @@ -0,0 +1,15 @@ +// vim: set syntax=asm : + +{{L}}{{label}}: + vbroadcastss ymm12, dword ptr [rdi + 8] + {% if flipped %} + {% for reg in (from..to) %} + {{op}} ymm{{reg}}, ymm{{reg}}, ymm12 + {% endfor %} + {% else %} + {% for reg in (from..to) %} + {{op}} ymm{{reg}}, ymm12, ymm{{reg}} + {% endfor %} + {% endif %} + + jmp {{L}}non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_sigmoid_f32.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_sigmoid_f32.tmpl new file mode 100644 index 0000000..e8b0aaf --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_sigmoid_f32.tmpl @@ -0,0 +1,321 @@ +{% comment %} +// vim: set syntax=asm : + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) + +{% endcomment %} + +{% if msvc %} + +_text segment +fma_sigmoid_f32_{{suffix}} proc + +{% else %} + +.intel_syntax noprefix +.text +.p2align 5 +.globl {{G}}fma_sigmoid_f32_{{suffix}} +{{G}}fma_sigmoid_f32_{{suffix}}: +.cfi_startproc +{% endif %} + + push rbp + mov rbp, rsp + + +{% if family == "windows" %} +// https://www.agner.org/optimize/calling_conventions.pdf xmm6-15 are not scratch +// https://stackoverflow.com/questions/43358429/save-value-of-xmm-registers + and rsp,-16 + lea rsp,[rsp-160] + vmovaps [rsp], xmm6 + vmovaps [rsp+16*1],xmm7 + vmovaps [rsp+16*2],xmm8 + vmovaps [rsp+16*3],xmm9 + vmovaps [rsp+16*4],xmm10 + vmovaps [rsp+16*5],xmm11 + vmovaps [rsp+16*6],xmm12 + vmovaps [rsp+16*7],xmm13 + vmovaps [rsp+16*8],xmm14 + vmovaps [rsp+16*9],xmm15 + + // move around arguments to mimick SysV rdi,rsi passing + push rdi + push rsi + mov rdi, rcx + mov rsi, rdx + +{% endif %} + + push rbx + push r12 + push r13 + push r14 + push r15 + + sub rsp, 8 + +{% if family == "unix" %} +// FIXME +// .cfi_def_cfa_offset 64 +{% endif %} + + stmxcsr [rsp + 4] +{% if msvc %} + mov rax, 1FC0h +{% else %} + mov rax, 0x1FC0 +{% endif %} + mov [rsp], eax + ldmxcsr [rsp] +// ---------------------------------------------------------------------- + +{%capture offset%}{% if msvc %} offset {%else%} rip + {%endif%} {%endcapture%} + + cmp rsi, 0 + je {{L}}done + + cmp rsi, 32 + jl {{L}}loop_1 + +{{L}}loop_4: + + vmovaps ymm4, [rdi] + vmovaps ymm5, [rdi + 32] + vmovaps ymm6, [rdi + 64] + vmovaps ymm7, [rdi + 96] + + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_low] + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_high] + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_13] + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_11] + + vmaxps ymm4, ymm4, ymm0 + vmaxps ymm5, ymm5, ymm0 + vmaxps ymm6, ymm6, ymm0 + vmaxps ymm7, ymm7, ymm0 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_9] + + vminps ymm4, ymm4, ymm1 + vminps ymm5, ymm5, ymm1 + vminps ymm6, ymm6, ymm1 + vminps ymm7, ymm7, ymm1 // ymm4..7 <- x + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_alpha_7] + + vmulps ymm8, ymm4, ymm4 + vmulps ymm9, ymm5, ymm5 + vmulps ymm10, ymm6, ymm6 + vmulps ymm11, ymm7, ymm7 // ymm8..11 <- x^2 + + vmovaps ymm12, ymm2 + vmovaps ymm13, ymm2 + vmovaps ymm14, ymm2 + vmovaps ymm15, ymm2 + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_5] + vfmadd132ps ymm12, ymm3, ymm8 + vfmadd132ps ymm13, ymm3, ymm9 + vfmadd132ps ymm14, ymm3, ymm10 + vfmadd132ps ymm15, ymm3, ymm11 + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_3] + vfmadd132ps ymm12, ymm0, ymm8 + vfmadd132ps ymm13, ymm0, ymm9 + vfmadd132ps ymm14, ymm0, ymm10 + vfmadd132ps ymm15, ymm0, ymm11 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_1] + vfmadd132ps ymm12, ymm1, ymm8 + vfmadd132ps ymm13, ymm1, ymm9 + vfmadd132ps ymm14, ymm1, ymm10 + vfmadd132ps ymm15, ymm1, ymm11 + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_beta_6] + vfmadd132ps ymm12, ymm2, ymm8 + vfmadd132ps ymm13, ymm2, ymm9 + vfmadd132ps ymm14, ymm2, ymm10 + vfmadd132ps ymm15, ymm2, ymm11 + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_beta_4] + vfmadd132ps ymm12, ymm3, ymm8 + vfmadd132ps ymm13, ymm3, ymm9 + vfmadd132ps ymm14, ymm3, ymm10 + vfmadd132ps ymm15, ymm3, ymm11 + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_beta_2] + vfmadd132ps ymm12, ymm0, ymm8 + vfmadd132ps ymm13, ymm0, ymm9 + vfmadd132ps ymm14, ymm0, ymm10 + vfmadd132ps ymm15, ymm0, ymm11 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_beta_0] + vmulps ymm4, ymm4, ymm12 + vmulps ymm5, ymm5, ymm13 + vmulps ymm6, ymm6, ymm14 + vmulps ymm7, ymm7, ymm15 // ymm4..7 <- num + + vmovaps ymm12, ymm1 + vmovaps ymm13, ymm1 + vmovaps ymm14, ymm1 + vmovaps ymm15, ymm1 + + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_half] + vfmadd132ps ymm12, ymm2, ymm8 + vfmadd132ps ymm13, ymm2, ymm9 + vfmadd132ps ymm14, ymm2, ymm10 + vfmadd132ps ymm15, ymm2, ymm11 + vfmadd132ps ymm12, ymm3, ymm8 + vfmadd132ps ymm13, ymm3, ymm9 + vfmadd132ps ymm14, ymm3, ymm10 + vfmadd132ps ymm15, ymm3, ymm11 + vfmadd132ps ymm12, ymm0, ymm8 + vfmadd132ps ymm13, ymm0, ymm9 + vfmadd132ps ymm14, ymm0, ymm10 + vfmadd132ps ymm15, ymm0, ymm11 // ymm12..14 <- denum + + vdivps ymm4, ymm4, ymm12 + vdivps ymm5, ymm5, ymm13 + vdivps ymm6, ymm6, ymm14 + vdivps ymm7, ymm7, ymm15 + vaddps ymm4, ymm4, ymm1 + vaddps ymm5, ymm5, ymm1 + vaddps ymm6, ymm6, ymm1 + vaddps ymm7, ymm7, ymm1 + + vmovaps [rdi], ymm4 + vmovaps [rdi + 32], ymm5 + vmovaps [rdi + 64], ymm6 + vmovaps [rdi + 96], ymm7 + + add rdi, 128 + sub rsi, 32 + cmp rsi, 32 + jg {{L}}loop_4 + + cmp rsi, 0 + je {{L}}done + +{{L}}loop_1: + vmovaps ymm4, [rdi] + + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_low] + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_high] + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_13] + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_11] + + vmaxps ymm4, ymm4, ymm0 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_9] + + vminps ymm4, ymm4, ymm1 // ymm4 <- x + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_alpha_7] + + vmulps ymm8, ymm4, ymm4 // ymm8 <- x^2 + + vmovaps ymm12, ymm2 + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_5] + vfmadd132ps ymm12, ymm3, ymm8 + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_3] + vfmadd132ps ymm12, ymm0, ymm8 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_1] + vfmadd132ps ymm12, ymm1, ymm8 + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_beta_6] + vfmadd132ps ymm12, ymm2, ymm8 + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_beta_4] + vfmadd132ps ymm12, ymm3, ymm8 + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_beta_2] + vfmadd132ps ymm12, ymm0, ymm8 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_beta_0] + vmulps ymm4, ymm4, ymm12 + + vmovaps ymm12, ymm1 + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_half] + vfmadd132ps ymm12, ymm2, ymm8 + vfmadd132ps ymm12, ymm3, ymm8 + vfmadd132ps ymm12, ymm0, ymm8 + + vdivps ymm4, ymm4, ymm12 + vaddps ymm4, ymm4, ymm1 + + vmovaps [rdi], ymm4 + add rdi, 32 + sub rsi, 8 + jnz {{L}}loop_1 +{{L}}done: + +// ---------------------------------------------------------------------- + + ldmxcsr [rsp + 4] + + add rsp, 8 + + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + +{% if family == "windows" %} + pop rsi + pop rdi + + vmovaps xmm15, [rsp+16*9] + vmovaps xmm14, [rsp+16*8] + vmovaps xmm13, [rsp+16*7] + vmovaps xmm12, [rsp+16*6] + vmovaps xmm11, [rsp+16*5] + vmovaps xmm10, [rsp+16*4] + vmovaps xmm9, [rsp+16*3] + vmovaps xmm8, [rsp+16*2] + vmovaps xmm7, [rsp+16*1] + vmovaps xmm6, [rsp] +{% endif %} + + mov rsp, rbp + pop rbp + ret + +{%capture float%}{% if msvc %} real4 {%else%} .float {%endif%}{%endcapture%} + +{{L}}coeffs_num_low: + {{float}} -18.6 // low +{{L}}coeffs_num_high: + {{float}} 18.6 // high + +{{L}}coeffs_num_alpha_13: + {{float}} -4.433153405e-18 +{{L}}coeffs_num_alpha_11: + {{float}} 1.169974371e-14 +{{L}}coeffs_num_alpha_9: + {{float}} -1.875289645e-11 +{{L}}coeffs_num_alpha_7: + {{float}} 4.257889523e-8 +{{L}}coeffs_num_alpha_5: + {{float}} 0.00004811817576 +{{L}}coeffs_num_alpha_3: + {{float}} 0.008163842030 +{{L}}coeffs_num_alpha_1: + {{float}} 0.2499999971 + +{{L}}coeffs_num_beta_6: + {{float}} 3.922935744e-6 +{{L}}coeffs_num_beta_4: + {{float}} 0.001524872358 +{{L}}coeffs_num_beta_2: + {{float}} 0.1159886749 +{{L}}coeffs_num_beta_0: + {{float}} 1.0; + +{{L}}coeffs_num_half: + {{float}} 0.5 + +{% if msvc %} +fma_sigmoid_f32_{{suffix}} endp +_text ends +end +{% else %} +.cfi_endproc +{% endif %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_tanh_f32.tmpl b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_tanh_f32.tmpl new file mode 100644 index 0000000..7b3c640 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/fma_tanh_f32.tmpl @@ -0,0 +1,313 @@ +{% comment %} +// vim: set syntax=asm : + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) + +{% endcomment %} + +{% if msvc %} + +_text segment +fma_tanh_f32_{{suffix}} proc + +{% else %} + +.intel_syntax noprefix +.text +.p2align 5 +.globl {{G}}fma_tanh_f32_{{suffix}} +{{G}}fma_tanh_f32_{{suffix}}: +.cfi_startproc +{% endif %} + + push rbp + mov rbp, rsp + + +{% if family == "windows" %} +// https://www.agner.org/optimize/calling_conventions.pdf xmm6-15 are not scratch +// https://stackoverflow.com/questions/43358429/save-value-of-xmm-registers + and rsp,-16 + lea rsp,[rsp-160] + vmovaps [rsp], xmm6 + vmovaps [rsp+16*1],xmm7 + vmovaps [rsp+16*2],xmm8 + vmovaps [rsp+16*3],xmm9 + vmovaps [rsp+16*4],xmm10 + vmovaps [rsp+16*5],xmm11 + vmovaps [rsp+16*6],xmm12 + vmovaps [rsp+16*7],xmm13 + vmovaps [rsp+16*8],xmm14 + vmovaps [rsp+16*9],xmm15 + + // move around arguments to mimick SysV rdi,rsi passing + push rdi + push rsi + mov rdi, rcx + mov rsi, rdx + +{% endif %} + + push rbx + push r12 + push r13 + push r14 + push r15 + + sub rsp, 8 + +{% if family == "unix" %} +// FIXME +// .cfi_def_cfa_offset 64 +{% endif %} + + stmxcsr [rsp + 4] +{% if msvc %} + mov rax, 1FC0h +{% else %} + mov rax, 0x1FC0 +{% endif %} + mov [rsp], eax + ldmxcsr [rsp] +// ---------------------------------------------------------------------- + +{%capture offset%}{% if msvc %} offset {%else%} rip + {%endif%} {%endcapture%} + + cmp rsi, 0 + je {{L}}done + + cmp rsi, 32 + jl {{L}}loop_1 + +{{L}}loop_4: + + vmovaps ymm4, [rdi] + vmovaps ymm5, [rdi + 32] + vmovaps ymm6, [rdi + 64] + vmovaps ymm7, [rdi + 96] + + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_low] + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_high] + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_13] + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_11] + + vmaxps ymm4, ymm4, ymm0 + vmaxps ymm5, ymm5, ymm0 + vmaxps ymm6, ymm6, ymm0 + vmaxps ymm7, ymm7, ymm0 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_9] + + vminps ymm4, ymm4, ymm1 + vminps ymm5, ymm5, ymm1 + vminps ymm6, ymm6, ymm1 + vminps ymm7, ymm7, ymm1 // ymm4..7 <- x + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_alpha_7] + + vmulps ymm8, ymm4, ymm4 + vmulps ymm9, ymm5, ymm5 + vmulps ymm10, ymm6, ymm6 + vmulps ymm11, ymm7, ymm7 // ymm8..11 <- x^2 + + vmovaps ymm12, ymm2 + vmovaps ymm13, ymm2 + vmovaps ymm14, ymm2 + vmovaps ymm15, ymm2 + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_5] + vfmadd132ps ymm12, ymm3, ymm8 + vfmadd132ps ymm13, ymm3, ymm9 + vfmadd132ps ymm14, ymm3, ymm10 + vfmadd132ps ymm15, ymm3, ymm11 + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_3] + vfmadd132ps ymm12, ymm0, ymm8 + vfmadd132ps ymm13, ymm0, ymm9 + vfmadd132ps ymm14, ymm0, ymm10 + vfmadd132ps ymm15, ymm0, ymm11 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_1] + vfmadd132ps ymm12, ymm1, ymm8 + vfmadd132ps ymm13, ymm1, ymm9 + vfmadd132ps ymm14, ymm1, ymm10 + vfmadd132ps ymm15, ymm1, ymm11 + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_beta_6] + vfmadd132ps ymm12, ymm2, ymm8 + vfmadd132ps ymm13, ymm2, ymm9 + vfmadd132ps ymm14, ymm2, ymm10 + vfmadd132ps ymm15, ymm2, ymm11 + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_beta_4] + vfmadd132ps ymm12, ymm3, ymm8 + vfmadd132ps ymm13, ymm3, ymm9 + vfmadd132ps ymm14, ymm3, ymm10 + vfmadd132ps ymm15, ymm3, ymm11 + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_beta_2] + vfmadd132ps ymm12, ymm0, ymm8 + vfmadd132ps ymm13, ymm0, ymm9 + vfmadd132ps ymm14, ymm0, ymm10 + vfmadd132ps ymm15, ymm0, ymm11 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_beta_0] + vmulps ymm4, ymm4, ymm12 + vmulps ymm5, ymm5, ymm13 + vmulps ymm6, ymm6, ymm14 + vmulps ymm7, ymm7, ymm15 // ymm4..7 <- num + + vmovaps ymm12, ymm1 + vmovaps ymm13, ymm1 + vmovaps ymm14, ymm1 + vmovaps ymm15, ymm1 + vfmadd132ps ymm12, ymm2, ymm8 + vfmadd132ps ymm13, ymm2, ymm9 + vfmadd132ps ymm14, ymm2, ymm10 + vfmadd132ps ymm15, ymm2, ymm11 + vfmadd132ps ymm12, ymm3, ymm8 + vfmadd132ps ymm13, ymm3, ymm9 + vfmadd132ps ymm14, ymm3, ymm10 + vfmadd132ps ymm15, ymm3, ymm11 + vfmadd132ps ymm12, ymm0, ymm8 + vfmadd132ps ymm13, ymm0, ymm9 + vfmadd132ps ymm14, ymm0, ymm10 + vfmadd132ps ymm15, ymm0, ymm11 // ymm12..14 <- denum + + vdivps ymm4, ymm4, ymm12 + vdivps ymm5, ymm5, ymm13 + vdivps ymm6, ymm6, ymm14 + vdivps ymm7, ymm7, ymm15 + + vmovaps [rdi], ymm4 + vmovaps [rdi + 32], ymm5 + vmovaps [rdi + 64], ymm6 + vmovaps [rdi + 96], ymm7 + + add rdi, 128 + sub rsi, 32 + cmp rsi, 32 + jg {{L}}loop_4 + + cmp rsi, 0 + je {{L}}done + +{{L}}loop_1: + vmovaps ymm4, [rdi] + + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_low] + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_high] + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_13] + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_11] + + vmaxps ymm4, ymm4, ymm0 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_9] + + vminps ymm4, ymm4, ymm1 // ymm4 <- x + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_alpha_7] + + vmulps ymm8, ymm4, ymm4 // ymm8 <- x^2 + + vmovaps ymm12, ymm2 + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_5] + vfmadd132ps ymm12, ymm3, ymm8 + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_3] + vfmadd132ps ymm12, ymm0, ymm8 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_1] + vfmadd132ps ymm12, ymm1, ymm8 + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_beta_6] + vfmadd132ps ymm12, ymm2, ymm8 + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_beta_4] + vfmadd132ps ymm12, ymm3, ymm8 + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_beta_2] + vfmadd132ps ymm12, ymm0, ymm8 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_beta_0] + vmulps ymm4, ymm4, ymm12 + + vmovaps ymm12, ymm1 + vfmadd132ps ymm12, ymm2, ymm8 + vfmadd132ps ymm12, ymm3, ymm8 + vfmadd132ps ymm12, ymm0, ymm8 + + vdivps ymm4, ymm4, ymm12 + + vmovaps [rdi], ymm4 + add rdi, 32 + sub rsi, 8 + jnz {{L}}loop_1 + +{{L}}done: + +// ---------------------------------------------------------------------- + + ldmxcsr [rsp + 4] + + add rsp, 8 + + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + +{% if family == "windows" %} + pop rsi + pop rdi + + vmovaps xmm15, [rsp+16*9] + vmovaps xmm14, [rsp+16*8] + vmovaps xmm13, [rsp+16*7] + vmovaps xmm12, [rsp+16*6] + vmovaps xmm11, [rsp+16*5] + vmovaps xmm10, [rsp+16*4] + vmovaps xmm9, [rsp+16*3] + vmovaps xmm8, [rsp+16*2] + vmovaps xmm7, [rsp+16*1] + vmovaps xmm6, [rsp] +{% endif %} + + mov rsp, rbp + pop rbp + ret + +{%capture float%}{% if msvc %} real4 {%else%} .float {%endif%}{%endcapture%} + +{{L}}coeffs_num_low: + {{float}} -8.9 +{{L}}coeffs_num_high: + {{float}} 8.9 + +{{L}}coeffs_num_alpha_13: + {{float}} -8.488492677e-14 +{{L}}coeffs_num_alpha_11: + {{float}} 5.277853000e-11 +{{L}}coeffs_num_alpha_9: + {{float}} -2.022500419e-8 +{{L}}coeffs_num_alpha_7: + {{float}} 0.00001115424833 +{{L}}coeffs_num_alpha_5: + {{float}} 0.003103950131 +{{L}}coeffs_num_alpha_3: + {{float}} 0.1308400453 +{{L}}coeffs_num_alpha_1: + {{float}} 0.9999999934 + +{{L}}coeffs_num_beta_6: + {{float}} 0.0002546136580 +{{L}}coeffs_num_beta_4: + {{float}} 0.02449515379 +{{L}}coeffs_num_beta_2: + {{float}} 0.4641733162 +{{L}}coeffs_num_beta_0: + {{float}} 1.0 + + + +{% if msvc %} +fma_tanh_f32_{{suffix}} endp +_text ends +end +{% else %} +.cfi_endproc +{% endif %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/postamble.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/postamble.tmpliq new file mode 100644 index 0000000..793e90e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/postamble.tmpliq @@ -0,0 +1,38 @@ +{{L}}return: + ldmxcsr [rsp + 4] + add rsp, 8 + + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + +{% if family == "windows" %} + pop rsi + pop rdi + + vmovaps xmm15, [rsp+16*9] + vmovaps xmm14, [rsp+16*8] + vmovaps xmm13, [rsp+16*7] + vmovaps xmm12, [rsp+16*6] + vmovaps xmm11, [rsp+16*5] + vmovaps xmm10, [rsp+16*4] + vmovaps xmm9, [rsp+16*3] + vmovaps xmm8, [rsp+16*2] + vmovaps xmm7, [rsp+16*1] + vmovaps xmm6, [rsp] +{% endif %} + + mov rsp, rbp + pop rbp + ret + +{% if msvc %} +fma_mmm_f32_{{size}}_{{suffix}} endp +_text ends +end + +{% else %} +.cfi_endproc +{% endif %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/preamble.tmpliq b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/preamble.tmpliq new file mode 100644 index 0000000..6a2394c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.19.16/x86_64/fma/preamble.tmpliq @@ -0,0 +1,64 @@ + +{% if msvc %} + +_text segment +fma_mmm_f32_{{size}}_{{suffix}} proc + +{% else %} + +.intel_syntax noprefix +.text +.p2align 5 +.globl {{G}}fma_mmm_f32_{{size}}_{{suffix}} +{{G}}fma_mmm_f32_{{size}}_{{suffix}}: +.cfi_startproc + +{% endif %} + + push rbp + mov rbp, rsp + +{% if family == "windows" %} +// https://www.agner.org/optimize/calling_conventions.pdf xmm6-15 are not scratch +// https://stackoverflow.com/questions/43358429/save-value-of-xmm-registers + and rsp,-16 + lea rsp,[rsp-160] + vmovaps [rsp], xmm6 + vmovaps [rsp+16*1],xmm7 + vmovaps [rsp+16*2],xmm8 + vmovaps [rsp+16*3],xmm9 + vmovaps [rsp+16*4],xmm10 + vmovaps [rsp+16*5],xmm11 + vmovaps [rsp+16*6],xmm12 + vmovaps [rsp+16*7],xmm13 + vmovaps [rsp+16*8],xmm14 + vmovaps [rsp+16*9],xmm15 + + push rdi + push rsi + + mov rdi, rcx + +{% endif %} + + push rbx + push r12 + push r13 + push r14 + push r15 + + sub rsp, 8 + +{% if family == "unix" %} +.cfi_def_cfa_offset 64 +{% endif %} + stmxcsr [rsp + 4] +{% if msvc %} + mov rax, 1FC0h +{% else %} + mov rax, 0x1FC0 +{% endif %} + mov [rsp], eax + ldmxcsr [rsp] + +{% include "dispatcher.tmpliq" %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/Cargo.toml b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/Cargo.toml new file mode 100644 index 0000000..029f55d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/Cargo.toml @@ -0,0 +1,272 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2024" +name = "tract-linalg" +version = "0.23.1" +authors = ["Mathieu Poumeyrol "] +build = "build.rs" +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Tiny, no-nonsense, self contained, TensorFlow and ONNX inference" +readme = "README.md" +keywords = [ + "TensorFlow", + "NeuralNetworks", +] +categories = ["science"] +license = "MIT OR Apache-2.0" +repository = "https://github.com/snipsco/tract" +resolver = "2" + +[badges.maintenance] +status = "actively-developed" + +[features] +apple-amx-ios = [] +complex = ["tract-data/complex"] +default = [] +hwbench = ["rayon"] +multithread-mm = ["rayon"] +no_fp16 = [] + +[lib] +name = "tract_linalg" +path = "src/lib.rs" + +[[test]] +name = "virtual_im2col" +path = "tests/virtual_im2col.rs" + +[[bench]] +name = "activations_avx512" +path = "benches/activations_avx512.rs" +harness = false + +[[bench]] +name = "activations_avx512_f16" +path = "benches/activations_avx512_f16.rs" +harness = false + +[[bench]] +name = "activations_avx512_fp16" +path = "benches/activations_avx512_fp16.rs" +harness = false + +[[bench]] +name = "arm32neon" +path = "benches/arm32neon.rs" +bench = false +harness = false + +[[bench]] +name = "arm64" +path = "benches/arm64.rs" +bench = false +harness = false + +[[bench]] +name = "arm64simd" +path = "benches/arm64simd.rs" +bench = false +harness = false + +[[bench]] +name = "avx512_zombies" +path = "benches/avx512_zombies.rs" +harness = false + +[[bench]] +name = "erf" +path = "benches/erf.rs" +harness = false + +[[bench]] +name = "gelu" +path = "benches/gelu.rs" +harness = false + +[[bench]] +name = "hardswish" +path = "benches/hardswish.rs" +harness = false + +[[bench]] +name = "intel" +path = "benches/intel.rs" +bench = false +harness = false + +[[bench]] +name = "leaky_relu" +path = "benches/leaky_relu.rs" +bench = false +harness = false + +[[bench]] +name = "mat_vec" +path = "benches/mat_vec.rs" +harness = false + +[[bench]] +name = "mm_for_asr_am" +path = "benches/mm_for_asr_am.rs" +harness = false + +[[bench]] +name = "mm_for_inception" +path = "benches/mm_for_inception.rs" +harness = false + +[[bench]] +name = "mm_for_wavenet_hw" +path = "benches/mm_for_wavenet_hw.rs" +harness = false + +[[bench]] +name = "qmmm_i8" +path = "benches/qmmm_i8.rs" +harness = false + +[[bench]] +name = "rms_norm" +path = "benches/rms_norm.rs" +harness = false + +[[bench]] +name = "sigmoid" +path = "benches/sigmoid.rs" +harness = false + +[[bench]] +name = "silu" +path = "benches/silu.rs" +harness = false + +[[bench]] +name = "softmax" +path = "benches/softmax.rs" +harness = false + +[[bench]] +name = "virtual_im2col" +path = "benches/virtual_im2col.rs" +harness = false + +[[bench]] +name = "vnni_i32" +path = "benches/vnni_i32.rs" +harness = false + +[[bench]] +name = "wasm" +path = "benches/wasm.rs" +harness = false + +[[bench]] +name = "x86_64" +path = "benches/x86_64.rs" +bench = false +harness = false + +[dependencies.byteorder] +version = "1.4.3" + +[dependencies.derive-new] +version = "0.7" + +[dependencies.downcast-rs] +version = "2.0" + +[dependencies.dyn-clone] +version = "1.0.4" + +[dependencies.dyn-eq] +version = "0.1" + +[dependencies.dyn-hash] +version = "1.0" + +[dependencies.lazy_static] +version = "1.5.0" + +[dependencies.log] +version = "0.4.14" + +[dependencies.num-traits] +version = "0.2.14" + +[dependencies.pastey] +version = "0.2" + +[dependencies.rayon] +version = "1.10" +optional = true + +[dependencies.scan_fmt] +version = "0.2.6" + +[dependencies.tract-data] +version = "=0.23.1" + +[dev-dependencies.core_affinity] +version = "0.8.0" + +[dev-dependencies.env_logger] +version = "0.11" + +[dev-dependencies.libc] +version = "0.2.164" + +[dev-dependencies.nu-ansi-term] +version = "0.50" + +[build-dependencies.cc] +version = "1.0.69" + +[build-dependencies.half] +version = ">=2.4,<3.0" +features = [ + "std", + "num-traits", +] + +[build-dependencies.minijinja] +version = "2.19.0" + +[build-dependencies.walkdir] +version = "2.3.2" + +[target.'cfg(not(target_family = "wasm"))'.dev-dependencies.criterion] +version = "0.8" + +[target.'cfg(not(target_family = "wasm"))'.dev-dependencies.proptest] +version = "1.0.0" + +[target.'cfg(target_family = "wasm")'.dev-dependencies.criterion] +version = "0.8" +features = [ + "plotters", + "cargo_bench_support", +] +default-features = false + +[target.'cfg(target_family = "wasm")'.dev-dependencies.proptest] +version = "1.0.0" +features = [ + "std", + "bit-set", +] +default-features = false diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/Cargo.toml.orig b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/Cargo.toml.orig new file mode 100644 index 0000000..a30c717 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/Cargo.toml.orig @@ -0,0 +1,169 @@ +[package] +name = "tract-linalg" +version = "0.23.1" +license = "MIT OR Apache-2.0" +authors = ["Mathieu Poumeyrol "] +description = "Tiny, no-nonsense, self contained, TensorFlow and ONNX inference" +repository = "https://github.com/snipsco/tract" +keywords = ["TensorFlow", "NeuralNetworks"] +categories = ["science"] +autobenches = false +edition = "2024" + +[badges] +maintenance = { status = "actively-developed" } + +[dependencies] +byteorder.workspace = true +derive-new.workspace = true +downcast-rs.workspace = true +dyn-clone.workspace = true +dyn-eq.workspace = true +dyn-hash.workspace = true +lazy_static.workspace = true +log.workspace = true +num-traits.workspace = true +pastey.workspace = true +rayon = { workspace = true, optional = true } +scan_fmt.workspace = true +tract-data.workspace = true + +[build-dependencies] +cc.workspace = true +half.workspace = true +minijinja.workspace = true +walkdir.workspace = true + +[dev-dependencies] +env_logger.workspace = true +libc.workspace = true +nu-ansi-term.workspace = true +core_affinity.workspace = true + +[target.'cfg(not(target_family = "wasm"))'.dev-dependencies] +criterion.workspace = true +proptest.workspace = true + +[target.'cfg(target_family = "wasm")'.dev-dependencies] +# Wasm doesn't support the `rayon` feature of criterion +criterion = { version = "0.8", default-features = false, features = ["plotters", "cargo_bench_support"] } +# Wasm doesn't support the `fork` feature of proptest. +proptest = { version = "1.0.0", default-features = false, features = ["std", "bit-set"] } + +[features] +# This feature is meant to accomodate very restrictive / legacy toolchains that do +# have support for fp16 instructions, breaking tract compilation. +# It is not meant to be used in other situations, where run-time detection is +# preferred. +no_fp16 = [] +apple-amx-ios = [] +default = [ ] +multithread-mm = [ "rayon" ] +complex = [ "tract-data/complex" ] +hwbench = [ "rayon" ] + +[[bench]] +bench = false +name = "arm64" +harness = false + +[[bench]] +name = "mat_vec" +harness = false + +[[bench]] +name = "mm_for_wavenet_hw" +harness = false + +[[bench]] +name = "mm_for_inception" +harness = false + +[[bench]] +name = "mm_for_asr_am" +harness = false + +[[bench]] +name = "qmmm_i8" +harness = false + +[[bench]] +name = "hardswish" +harness = false + +[[bench]] +name = "silu" +harness = false + +[[bench]] +name = "gelu" +harness = false + +[[bench]] +name = "sigmoid" +harness = false + +[[bench]] +name = "softmax" +harness = false + +[[bench]] +name = "activations_avx512_f16" +harness = false + +[[bench]] +name = "erf" +harness = false + +[[bench]] +name = "rms_norm" +harness = false + +[[bench]] +name = "activations_avx512_fp16" +harness = false + +[[bench]] +bench = false +name = "arm64simd" +harness = false + +[[bench]] +bench = false +name = "arm32neon" +harness = false + +[[bench]] +name = "virtual_im2col" +harness = false + +[[bench]] +bench = false +name = "x86_64" +harness = false + +[[bench]] +bench = false +name = "intel" +harness = false + +[[bench]] +bench = false +name = "leaky_relu" +harness = false + +[[bench]] +name = "avx512_zombies" +harness = false + +[[bench]] +name = "activations_avx512" +harness = false + +[[bench]] +name = "wasm" +harness = false + +[[bench]] +name = "vnni_i32" +harness = false diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/LICENSE b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/LICENSE new file mode 100644 index 0000000..09250ca --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/LICENSE @@ -0,0 +1,12 @@ +## License + +Licensed under either of + * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) +at your option. + +### Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted +for inclusion in the work by you, as defined in the Apache-2.0 license, shall +be dual licensed as above, without any additional terms or conditions. diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/LICENSE-APACHE b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/LICENSE-APACHE new file mode 100644 index 0000000..16fe87b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/LICENSE-MIT b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/LICENSE-MIT new file mode 100644 index 0000000..31aa793 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/LICENSE-MIT @@ -0,0 +1,23 @@ +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. diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/MULTITHREAD_BENCHMARKS.md b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/MULTITHREAD_BENCHMARKS.md new file mode 100644 index 0000000..6849b94 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/MULTITHREAD_BENCHMARKS.md @@ -0,0 +1,139 @@ +# Multithreaded MMM benchmarks + +Validation data for the `multithread-mm` rayon path with this PR's +`chunked_dispatch_rayon` + `THREADING_PANEL_THRESHOLD` + `RayonGlobal` +changes. + +## Setup + +- **Base**: tract `main` (commit `41b7b02`), with the merged WASM kernel + kit (PRs `#2164` + `#2173`). +- **Vanilla baseline**: same commit, MMM dispatch unchanged + (1D `into_par_iter` over single panel axis). +- **Patched**: this PR applied — chunked 2D dispatch, threshold, RayonGlobal. +- **Both binaries built identically**: same compiler, same kernel kit, same + `+atomics +bulk-memory +mutable-globals +simd128` target features for + WASM. +- **Driver**: Playwright headless, real browser engines. Median of 60 + iterations after 3-iter warmup. +- **Output verification**: FNV-1a hash of result tensor. All 60 cells + produce identical hash (`20ea4579c427f925` for DFN3, + shape-deterministic for synthetic) — bit-equal output preserved. + +## Synthetic dense matmul (the parallelism-bound case) + +| Shape | Engine | Vanilla (1 thread) | Patched, 4 threads | Speedup | +|---|---|---|---|---| +| **1024×1024×1024** (transformer FFN) | Chromium | 55.4 ms | 16.6 ms | **3.34×** | +| | WebKit | 79.8 ms | 25.6 ms | **3.12×** | +| | Firefox | 1107 ms | 325 ms | **3.40×** | +| **512×768×768** (BERT FFN) | Chromium | 15.7 ms | 5.1 ms | **3.07×** | +| | WebKit | 22.6 ms | 6.8 ms | **3.30×** | +| | Firefox | 311 ms | 91 ms | **3.42×** | +| **256×256×128** | Chromium | 0.47 ms | 0.22 ms | **2.19×** | +| | WebKit | 0.66 ms | 0.20 ms | **3.30×** | +| | Firefox | 9.0 ms | 2.6 ms | **3.41×** | +| 64×256×64 (DFN-like small) | Chromium | 0.07 ms | 0.07 ms | 1.0× (within noise) | +| | WebKit | 0.08 ms | 0.04 ms | 2.00× | +| | Firefox | 1.18 ms | 0.38 ms | **3.11×** | +| 32×32×32 (tiny) | All | sub-ms | sub-ms | **1.0× (threshold gates)** | + +The threshold correctly gates the smallest shape; threading kicks in once +panel count clears the gate, and scales near-linearly with thread count +on all three engines. + +## Real model (DeepFilterNet 3, full streaming inference) + +5-frame chunks at 48 kHz (50 ms of audio per iteration). + +| Engine | Vanilla mono | Patched, 4 threads | RTF (vanilla → patched) | Speedup | +|---|---|---|---|---| +| Chromium | 3.31 ms | 3.17 ms | 0.066 → 0.063 | 1.04× (within noise) | +| WebKit | 4.32 ms | 4.00 ms | 0.086 → 0.080 | 1.08× | +| Firefox | 34.20 ms | 33.34 ms | 0.684 → 0.667 | 1.03× | + +DFN3 is Amdahl-bound: only ~25% of runtime is in MMMs that clear the +threshold; the rest is FFT, complex multiplication, and small RNN-internal +matmuls that the threshold deliberately keeps single-threaded. The +threshold's role here is to **prevent regression**, not deliver speedup. +This is correct behavior — DFN3-class workloads should not pay rayon +overhead on tiny ops. + +## Native (macOS aarch64, generic kernels) + +Spot-check on the rayon path before/after. Tract's existing rayon path +already worked well on native; the change is mostly a refactor. + +| Shape | Vanilla 1D | Patched 2D | Change | +|---|---|---|---| +| 256×256, 4 threads | 2.13 ms | 2.12 ms | net-neutral | +| 512×512, 4 threads | 9.93 ms | 9.80 ms | +1% | +| 64×256, 4 threads | 615 µs | 625 µs | −2% | + +Within noise on common shapes. The 2D dispatch shows a latent benefit on +shapes 1D parallelism handles poorly (e.g. m=8 n=2048, where 1D over m +can only feed 2 threads); not yet measured directly on native but the +dispatch math is the same on both targets. + +## Determinism + +Across all measured cells (synthetic + DFN3, 3 engines, 1/2/3/4 threads): + +- **WASM**: 60 cells, all produce identical hash per `(shape, mode)` pair. +- **Native**: existing tract proptests (3524 lib tests) pass with this + PR's `multithread-mm` enabled. + +## Tuning the threshold + +The default `THREADING_PANEL_THRESHOLD` is `64` panels (m_panels × +n_panels). Adjust at runtime via: + +```rust +use tract_linalg::multithread::set_threading_panel_threshold; + +set_threading_panel_threshold(0); // thread every size, no gate +set_threading_panel_threshold(256); // gate harder — transformer-only +set_threading_panel_threshold(64); // default +``` + +Useful when profiling or specialising the build for a known workload class: + +| Workload class | Suggested threshold | +|---|---| +| Streaming RNN / mobile vision (many small MMMs) | 64 (default) or higher | +| Mid-size dense (BERT-class) | 32–64 | +| Large dense only (transformer FFN, LLM) | 16 or lower | + +The constant lives in `linalg/src/multithread.rs`; readers go through +`current_threading_panel_threshold()` (`AtomicUsize::Relaxed`, no lock on +the dispatch hot path). + +## Reproduction + +The harness uses [Vonage's libDF fork](https://github.com/czoli1976/DeepFilterNet) +(branch `dfn3-wasm-opt-tract-022-kernel-kit`) migrated to tract main, with +a `wasm-bindgen-rayon`-based threading bootstrap. Build: + +```bash +RUSTFLAGS="-C target-feature=+atomics,+bulk-memory,+mutable-globals,+simd128" \ +wasm-pack build --target web --release \ + --no-default-features --features wasm-mt -- \ + -Z build-std=std,panic_abort +``` + +JS-side: + +```javascript +import init, { initThreadPool, df_set_thread_count } from './pkg/df.js'; +await init(); +await initThreadPool(navigator.hardwareConcurrency); // wasm-bindgen-rayon +df_set_thread_count(4); // sets Executor::RayonGlobal in tract-linalg +``` + +Without `Executor::RayonGlobal` (this PR), `df_set_thread_count` would +need to construct an `Arc` — which fails on +`wasm32-unknown-unknown` because `rayon::ThreadPoolBuilder::new().build()` +internally calls `std::thread::spawn` (unsupported there). That's the +crux of why this enabling change is needed in tract-linalg itself: any +browser threading via wasm-bindgen-rayon would otherwise silently fall +back to single-threaded. diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/README.md b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/README.md new file mode 100644 index 0000000..ba7b722 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/README.md @@ -0,0 +1,27 @@ +# tract-linalg + +linalg stands for "linear algebra". This is a misnamer. This crates contains +low-level, architecture dependant optimisations used by tract-core. + +# Functions + +* MatMatMul: Extended matrix*matrix product: + * inspired by Gotoblass and BLIS micro kernel approach + * extended for convolution friendly addressing (fused img2col) + * fused output pipeline (min, max, and a few more simple, fast ops) + * f32*f32 -> f32 (à la sgemm) + * i8*i8 -> i32 accumulator -> i32 storage + * i8*i8 -> i32 accumulator -> i8 (with channel zeropoint and scale, and re-quantization pipeline) +* f32 sigmoid and f32 tanh: at f32 precision, by a rationale function (no exponentiation) +* byte-to-byte lookup table + +# Implementations + +| | generic fallback | armv6, vfp | armv7 neon | armv8 simd | x64 FMA +|-------------------|--------------------|---------------|-------------------|-------------------|----------------- +| MatMatMul f32 | | 4x4 | 8x4 | 8x8 | 16x6 +| MatMatMul i8->i8 | | | 8x4 | | 8x8 +| MatMatMul i8->i32 | | | | | 8x8 +| sigmoid f32 | | | 4n | 4n | +| tanh f32 | | | 4n | 4n | +| byte lookup | | | | | diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/SME_PHASE1_BENCH.xlsx b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/SME_PHASE1_BENCH.xlsx new file mode 100644 index 0000000..b90cc5f Binary files /dev/null and b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/SME_PHASE1_BENCH.xlsx differ diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/WASM_RELAXED_SIMD.md b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/WASM_RELAXED_SIMD.md new file mode 100644 index 0000000..3797a0a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/WASM_RELAXED_SIMD.md @@ -0,0 +1,105 @@ +# tract-linalg on `wasm32` — relaxed-simd FMA + +The WASM MMM kernels (`wasm_f32_4x4`, `4x1`, `8x1`, `16x1`, `32x1`, `8x8`) +and the WASM sigmoid/tanh activations all flip between two emit modes at +compile time, gated on `cfg(target_feature = "relaxed-simd")`: + +- **Without** `+relaxed-simd`: pure `f32x4_add(_, f32x4_mul(_, _))` (mul+add). + Runs on any WASM runtime that supports `simd128`. +- **With** `+relaxed-simd`: `f32x4_relaxed_madd(_, _, _)`. Fused, single-rounded + multiply-add on hosts whose CPU has hardware FMA (all ARM64, x86_64 + FMA3). + Universal browser/runtime support since 2023 (Chrome 114+, Firefox 120+, + Safari 17+, wasmtime 16+). + +The speedup of the relaxed path over the baseline is typically **1.40–1.55× at +the kernel level** and **1.08–1.46× end-to-end** across vision CNNs, +transformer attention and RNN audio models. Bit-pattern drift versus the +mul+add path is bounded at one ulp (FMA single-rounding); within +`Approximation::Close` (1e-4). + +## Build flags + +```sh +# Baseline (any wasm32 runtime supporting simd128) +RUSTFLAGS='-C target-feature=+simd128' \ + cargo build --release --target wasm32-wasip1 -p tract-linalg + +# Relaxed (requires host support for relaxed-simd; ~1.40× faster on FMA-capable hosts) +RUSTFLAGS='-C target-feature=+simd128,+relaxed-simd' \ + cargo build --release --target wasm32-wasip1 -p tract-linalg +``` + +Same on `wasm32-unknown-unknown` if shipping for the browser. + +## Why two binaries (and not in-process runtime dispatch) + +WASM validates the entire module at instantiation, before any code runs. +A binary containing `f32x4.relaxed_madd` fails to instantiate on hosts without +relaxed-simd — `LinkError` / `CompileError`, not a runtime trap. So the +x86/ARM pattern (one binary, both paths in source, runtime CPU detection picks +at execution time) cannot be replicated in-binary on WASM: the FMA opcodes are +either present (and host support is required) or absent. + +Runtime dispatch happens one layer up — at the host runtime / consumer layer +— by selecting the correct binary at module-load time. + +## Consumer-side dispatch + +### Browser / `WebAssembly.validate` + +```js +async function loadTract(baseUrl) { + const candidate = await fetch(`${baseUrl}/tract-relaxed.wasm`); + const bytes = await candidate.arrayBuffer(); + + const wantRelaxed = WebAssembly.validate(bytes, { + builtins: ['relaxed_simd'], + }); + + const url = wantRelaxed + ? `${baseUrl}/tract-relaxed.wasm` + : `${baseUrl}/tract.wasm`; + + const final = await fetch(url); + return WebAssembly.instantiateStreaming(final); +} +``` + +Fallback for hosts without the `WebAssembly.validate(bytes, { ... })` +options-arg: try-instantiate the relaxed binary, catch `LinkError` / +`CompileError`, retry with the baseline. + +### `wasmtime` (server / native) + +```rust +use wasmtime::{Config, Engine}; + +let mut config = Config::new(); +config.wasm_relaxed_simd(true); // gate on host-CPU detection if needed +let engine = Engine::new(&config)?; + +let bytes = std::fs::read(if relaxed_supported { + "tract-relaxed.wasm" +} else { + "tract.wasm" +})?; +let module = wasmtime::Module::new(&engine, &bytes)?; +``` + +`wasmtime::Engine`'s `wasm_relaxed_simd` configures the runtime; a separate +`wasmtime::Module::validate()` call against the engine is the equivalent of +the browser's `WebAssembly.validate` for picking which binary to load. + +## Quality + +The two binaries are **not bit-identical**. FMA's single-rounding produces +≤1 ulp drift from explicit mul+add. Verified end-to-end on Inception v3 and +DFN3 sub-models: + +| model | output shape | baseline L2 | relaxed L2 | +|--------------|--------------------|-------------:|-------------:| +| Inception v3 | [1, 1001] | 6.477089e-2 | 6.477089e-2 | +| DFN3 df_dec | [1, 100, 96, 10] | 1.080686e-2 | 1.080686e-2 | + +L2 norms are bit-identical to 7 sig figs; per-element values diverge in the +7th–8th decimal place. Within tract's `Approximation::Close` (1e-4). diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_32x1_core.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_32x1_core.S.j2 new file mode 100644 index 0000000..daf485b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_32x1_core.S.j2 @@ -0,0 +1,204 @@ +// vim: ft=arm + +// C tile regs +// +// q8[0] +// q8[1] +// q8[2] +// q8[3] +// +// .... +// +// q15[0] +// q15[1] +// q15[2] +// q15[3] + + .arm + .text + .global armv7neon_mmm_f32_32x1_{{core}}_{{suffix}} + .type armv7neon_mmm_f32_32x1_{{core}}_{{suffix}}, %function + +armv7neon_mmm_f32_32x1_{{core}}_{{suffix}}: + + pld [r0] + push { r4-r12 } + vpush { q4-q7 } + +{% include "dispatcher.j2" %} + +.add_mat_mul: + + cmp r3, #0 + beq .non_linear_loop + + mov r1, r4 // packed A ptr + pld [r3] + pld [r5] + + pld [r1, #128] + pld [r1, #192] + pld [r1, #256] + pld [r1, #320] + pld [r1, #384] + pld [r1, #448] + pld [r1, #512] + +.packed_packed_loop_1: + pld [r5] // packed B ptr + +{% if core == "cortexa7" %} + + vldr d0, [r1] + vldr d1, [r1, #8] + vldr d2, [r1, #16] + vldr d3, [r1, #24] + vldr d4, [r1, #32] + vldr d5, [r1, #40] + vldr d6, [r1, #48] + vldr d7, [r1, #56] + vldr d8, [r1, #64] + vldr d9, [r1, #72] + vldr d10, [r1, #80] + vldr d11, [r1, #88] + vldr s30, [r5] + + pld [r1, #512] + pld [r1, #576] + pld [r5, #64] + + vmla.f32 q8, q0, d15[0] + vmla.f32 q9, q1, d15[0] + + vldr d0, [r1, #96] + vldr d1, [r1, #104] + vldr d2, [r1, #112] + vldr d3, [r1, #120] + + vmla.f32 q10, q2, d15[0] + vmla.f32 q11, q3, d15[0] + + vmla.f32 q12, q4, d15[0] + vmla.f32 q13, q5, d15[0] + + vmla.f32 q14, q0, d15[0] + vmla.f32 q15, q1, d15[0] + + add r1, #128 + add r5, #4 + +{% elif core == "cortexa9" %} + + vld1.64 {d0-d3}, [r1]! + vld1.64 {d4-d7}, [r1]! + pld [r1, #512] + pld [r1, #576] + vld1.64 {d8-d11}, [r1]! + vld1.f32 d15[0], [r5]! + pld [r5, #64] + + vmla.f32 q8, q0, d15[0] + vmla.f32 q9, q1, d15[0] + vld1.64 {d0-d3}, [r1]! + + vmla.f32 q10, q2, d15[0] + vmla.f32 q11, q3, d15[0] + + vmla.f32 q12, q4, d15[0] + vmla.f32 q13, q5, d15[0] + + vmla.f32 q14, q0, d15[0] + vmla.f32 q15, q1, d15[0] + +{% else %} + + vldmia r1!, { q0-q3 } + vldmia r5!, { s30 } + + vmla.f32 q8, q0, d15[0] + vmla.f32 q9, q1, d15[0] + vldmia r1!, { q0-q1 } + + vmla.f32 q10, q2, d15[0] + vmla.f32 q11, q3, d15[0] + vldmia r1!, { q2-q3 } + + vmla.f32 q12, q0, d15[0] + vmla.f32 q13, q1, d15[0] + + vmla.f32 q14, q2, d15[0] + vmla.f32 q15, q3, d15[0] + +{% endif %} + + subs r3, r3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +{% set from = 8 %}{% set to = 15 %}{% include "armv7neon_mmm_f32_scalars.j2" %} +{% set mr = 32 %}{% set from = 8 %}{% set to = 15 %}{% include "armv7neon_mmm_f32_per_rows.j2" %} +{% set mr = 32 %}{% set from = 8 %}{% set to = 15 %}{% include "armv7neon_mmm_f32_per_cols.j2" %} + +.add_unicast: + {% for reg in range(0, 16) %} + vld1.f32 d{{reg}}[0], [ r3 ], r4 + vld1.f32 d{{reg}}[1], [ r3 ], r4 + {% endfor %} + {% for reg in range(0, 8) %} + vadd.f32 q{{ reg + 8 }}, q{{ reg + 8 }}, q{{reg}} + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + vld1.f32 d0[0], [ r4 ] + vldmia r3!, { q4-q7 } + + vmla.f32 q8, q4, d0[0] + vmla.f32 q9, q5, d0[0] + + vmla.f32 q10, q6, d0[0] + vmla.f32 q11, q7, d0[0] + + vldmia r3!, { q4-q7 } + + vmla.f32 q12, q4, d0[0] + vmla.f32 q13, q5, d0[0] + + vmla.f32 q14, q6, d0[0] + vmla.f32 q15, q7, d0[0] + + b .non_linear_loop + +.store: + // r3, r4 <- ptr, rsc + cmp r4, #4 + bne .store_generic + + vst1.f64 {d16-d19}, [r3]! + vst1.f64 {d20-d23}, [r3]! + vst1.f64 {d24-d27}, [r3]! + vst1.f64 {d28-d31}, [r3]! + + b .non_linear_loop + +.store_generic: + + {% for reg in range(16, 32) %} + vst1.f32 d{{reg}}[0], [r3], r4 + vst1.f32 d{{reg}}[1], [r3], r4 + {% endfor %} + + b .non_linear_loop + +.load_tile: + vldmia r3!, { q8-q15 } + b .non_linear_loop + +.return: + vpop { q4-q7 } + pop { r4-r12 } + + bx lr + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_8x1_core.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_8x1_core.S.j2 new file mode 100644 index 0000000..e2f6377 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_8x1_core.S.j2 @@ -0,0 +1,98 @@ +// vim: ft=arm + + .arm + .text + .global armv7neon_mmm_f32_8x1_{{core}}_{{suffix}} + .type armv7neon_mmm_f32_8x1_{{core}}_{{suffix}}, %function + +armv7neon_mmm_f32_8x1_{{core}}_{{suffix}}: + + pld [r0] + push { r4-r12 } + vpush { q4-q7 } + +{% include "dispatcher.j2" %} + +.add_mat_mul: + + cmp r3, #0 + beq .non_linear_loop + + mov r1, r4 // packed A ptr + pld [r3] + pld [r5] + + pld [r1, #128] + pld [r1, #192] + pld [r1, #256] + pld [r1, #320] + pld [r1, #384] + pld [r1, #448] + pld [r1, #512] + +.packed_packed_loop_1: + pld [r5] // packed B ptr + + vldmia r1!, { q0-q1 } + vldmia r5!, { s30 } + + vmla.f32 q8, q0, d15[0] + vmla.f32 q9, q1, d15[0] + + subs r3, r3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +{% set from = 8 %}{% set to = 9 %}{% include "armv7neon_mmm_f32_scalars.j2" %} +{% set mr = 8 %}{% set from = 8 %}{% set to = 9 %}{% include "armv7neon_mmm_f32_per_rows.j2" %} +{% set mr = 8 %}{% set from = 8 %}{% set to = 9 %}{% include "armv7neon_mmm_f32_per_cols.j2" %} + +.add_unicast: + {% for reg in range(0, 4) %} + vld1.f32 d{{reg}}[0], [ r3 ], r4 + vld1.f32 d{{reg}}[1], [ r3 ], r4 + {% endfor %} + {% for reg in range(0, 2) %} + vadd.f32 q{{ reg + 8 }}, q{{ reg + 8 }}, q{{reg}} + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + vld1.f32 d0[0], [ r4 ] + vldmia r3!, { q4-q5 } + + vmla.f32 q8, q4, d0[0] + vmla.f32 q9, q5, d0[0] + + b .non_linear_loop + +.store: + // r3, r4 <- ptr, rsc + cmp r4, #4 + bne .store_generic + + vst1.f64 {d16-d19}, [r3]! + + b .non_linear_loop + +.store_generic: + + {% for reg in range(16, 20) %} + vst1.f32 d{{reg}}[0], [r3], r4 + vst1.f32 d{{reg}}[1], [r3], r4 + {% endfor %} + + b .non_linear_loop + +.load_tile: + vldmia r3!, { q8-q15 } + b .non_linear_loop + +.return: + vpop { q4-q7 } + pop { r4-r12 } + + bx lr + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_8x4_core.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_8x4_core.S.j2 new file mode 100644 index 0000000..b393c08 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_8x4_core.S.j2 @@ -0,0 +1,143 @@ +// vim: ft=arm + +// C tile regs +// +// q8[0] q10[0] q12[0] q14[0] +// q8[1] q10[1] q12[1] q14[1] +// q8[2] q10[2] q12[2] q14[2] +// q8[3] q10[3] q12[3] q14[3] +// +// q9[0] q11[0] q13[0] q15[0] +// q9[1] q11[1] q13[1] q15[1] +// q9[2] q11[2] q13[2] q15[2] +// q9[3] q11[3] q13[3] q15[3] + +// packed A buffering (2x8 values): alternating q0, q1 with q2, q3 +// packed B buffering (2x4 values): alternating q4 with q5 + + .arm + .text + .global armv7neon_mmm_f32_8x4_{{core}}_{{suffix}} + .type armv7neon_mmm_f32_8x4_{{core}}_{{suffix}}, %function + +armv7neon_mmm_f32_8x4_{{core}}_{{suffix}}: + pld [r0] + push { r4-r12 } + vpush { q4-q7 } + +{% include "dispatcher.j2" %} + +.add_mat_mul: + + cmp r3, #0 + beq .non_linear_loop + + mov r1, r4 // packed A ptr + pld [r3] + pld [r5] + + .packed_packed: + pld [r5] // packed B ptr + .packed_packed_loop_1: + +{% if core == "cortexa7" %} + vldr d0, [r1] + vldr d1, [r1, #8] + vldr d2, [r1, #16] + vldr d3, [r1, #24] + vldr d4, [r5] + vldr d5, [r5, #8] +{% elif core == "cortexa9" %} + vld1.64 {d0-d3}, [r1]! + vld1.64 {d4, d5}, [r5]! +{% else %} + vldmia r1!, { q0, q1} + vldmia r5!, { q2 } +{% endif %} + +{% if core != "generic" %} + pld [r1, #512] + pld [r5, #512] +{% endif %} + + vmla.f32 q8, q0, d4[0] + vmla.f32 q9, q1, d4[0] + + vmla.f32 q10, q0, d4[1] + vmla.f32 q11, q1, d4[1] + + vmla.f32 q12, q0, d5[0] + vmla.f32 q13, q1, d5[0] + + vmla.f32 q14, q0, d5[1] + vmla.f32 q15, q1, d5[1] + +{% if core == "cortexa7" %} + add r1, #32 + add r5, #16 +{% endif %} + + subs r3, r3, #1 + bne .packed_packed_loop_1 + b .non_linear_loop + +{% set from = 8 %}{% set to = 15 %}{% include "armv7neon_mmm_f32_scalars.j2" %} +{% set mr = 8 %}{% set from = 8 %}{% set to = 15 %}{% include "armv7neon_mmm_f32_per_rows.j2" %} +{% set mr = 8 %}{% set from = 8 %}{% set to = 15 %}{% include "armv7neon_mmm_f32_per_cols.j2" %} + +.add_unicast: + // r3, r4, r5 <- ptr, rsc, csc + {% for col in range(0, 4) %} + mov r2, r3 + {% for reg in range(0, 4) %} + vld1.f32 d0[0], [ r2 ], r4 + vld1.f32 d0[1], [ r2 ], r4 + vadd.f32 d{{ col * 4 + reg + 16 }}, d0 + {% endfor %} + add r3, r3, r5 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + vldmia r3!, { q0, q1 } + vldmia r4!, { q4 } + + vmla.f32 q8, q0, d8[0] + vmla.f32 q9, q1, d8[0] + + vmla.f32 q10, q0, d8[1] + vmla.f32 q11, q1, d8[1] + + vmla.f32 q12, q0, d9[0] + vmla.f32 q13, q1, d9[0] + + vmla.f32 q14, q0, d9[1] + vmla.f32 q15, q1, d9[1] + + b .non_linear_loop + +.store: + // r3,r4,r5 are c,rsc,csc + {% for col in range(0, 4) %} + mov r8, r3 + {% for reg in range(0, 4) %} + vst1.f32 d{{ col * 4 + reg + 16 }}[0], [ r8 ], r4 + vst1.f32 d{{ col * 4 + reg + 16 }}[1], [ r8 ], r4 + {% endfor %} + {% if col < 3 %} + add r3, r3, r5 + {% endif %} + {% endfor %} + b .non_linear_loop + +.load_tile: + vldmia r3!, { q8-q15 } + b .non_linear_loop + +.return: + vpop { q4-q7 } + pop { r4-r12 } + + bx lr + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_8x6_core.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_8x6_core.S.j2 new file mode 100644 index 0000000..c532a98 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_8x6_core.S.j2 @@ -0,0 +1,158 @@ +// vim: ft=arm + + .arm + .text + .global armv7neon_mmm_f32_8x6_{{core}}_{{suffix}} + .type armv7neon_mmm_f32_8x6_{{core}}_{{suffix}}, %function + +armv7neon_mmm_f32_8x6_{{core}}_{{suffix}}: + + pld [r0] + push { r4-r12 } + vpush { q4-q7 } + +{% include "dispatcher.j2" %} + +.add_mat_mul: + cmp r3, #0 + beq .non_linear_loop + + mov r1, r4 // packed A ptr + pld [r3] + pld [r5] + + .packed_packed_loop_1: + +{% if core == "cortexa7" %} + vldr d0, [r1] + vldr d1, [r1, #8] + vldr d2, [r1, #16] + vldr d3, [r1, #24] + vldr d4, [r5] + vldr d5, [r5, #8] + vldr d6, [r5, #16] +{% elif core == "cortexa9" %} + vld1.64 {d0-d3}, [r1]! + vld1.64 {d4, d5, d6}, [r5]! +{% else %} + vldmia r1!, {q0-q1} + vldmia r5!, {d4-d6} +{% endif %} + +{% if core != "generic" %} + pld [r1, #512] + pld [r5, #512] +{% endif %} + + vmla.f32 q4, q0, d4[0] + vmla.f32 q5, q1, d4[0] + + vmla.f32 q6, q0, d4[1] + vmla.f32 q7, q1, d4[1] + + vmla.f32 q8, q0, d5[0] + vmla.f32 q9, q1, d5[0] + + vmla.f32 q10, q0, d5[1] + vmla.f32 q11, q1, d5[1] + + vmla.f32 q12, q0, d6[0] + vmla.f32 q13, q1, d6[0] + + vmla.f32 q14, q0, d6[1] + vmla.f32 q15, q1, d6[1] + +{% if core == "cortexa7" %} + add r1, #32 + add r5, #24 +{% endif %} + + subs r3, r3, #1 + bne .packed_packed_loop_1 + b .non_linear_loop + +{% set from = 4 %}{% set to = 15 %}{% include "armv7neon_mmm_f32_scalars.j2" %} +{% set mr = 8 %}{% set from = 4 %}{% set to = 15 %}{% include "armv7neon_mmm_f32_per_rows.j2" %} +{% set mr = 8 %}{% set from = 4 %}{% set to = 15 %}{% include "armv7neon_mmm_f32_per_cols.j2" %} + +.add_unicast: + // r3, r4, r5, r6 <- ptr, rsc, csc, size + {% for col in range(0, 6) %} + mov r2, r3 + {% for reg in range(0, 4) %} + vld1.f32 d0[0], [ r2 ], r4 + vld1.f32 d0[1], [ r2 ], r4 + vadd.f32 d{{ col * 4 + reg + 8 }}, d0 + {% endfor %} + add r3, r3, r5 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + vldmia r3!, { q0, q1 } + vldmia r4!, { d4, d5, d6 } + + vmla.f32 q4, q0, d4[0] + vmla.f32 q5, q1, d4[0] + + vmla.f32 q6, q0, d4[1] + vmla.f32 q7, q1, d4[1] + + vmla.f32 q8, q0, d5[0] + vmla.f32 q9, q1, d5[0] + + vmla.f32 q10, q0, d5[1] + vmla.f32 q11, q1, d5[1] + + vmla.f32 q12, q0, d6[0] + vmla.f32 q13, q1, d6[0] + + vmla.f32 q14, q0, d6[1] + vmla.f32 q15, q1, d6[1] + + b .non_linear_loop + +.store: + // r3, r4, r5 <- ptr, rsc, csc + + cmp r4, #4 + bne .store_generic + + {% for col in range(0, 6) %} + mov r8, r3 + {% for reg in range(0, 4) %} + vst1.64 d{{ col * 4 + 8 + reg }}, [ r8 ]! + {% endfor %} + {% if col < 5 %} + add r3, r3, r5 + {% endif %} + {% endfor %} + + b .non_linear_loop + +.store_generic: + {% for col in range(0, 6) %} + mov r8, r3 + {% for reg in range(0, 4) %} + vst1.f32 d{{ col * 4 + reg + 8 }}[0], [ r8 ], r4 + vst1.f32 d{{ col * 4 + reg + 8 }}[1], [ r8 ], r4 + {% endfor %} + {% if col < 5 %} + add r3, r3, r5 + {% endif %} + {% endfor %} + + b .non_linear_loop + +.load_tile: + vldmia r3!, { q4-q7 } + vldmia r3!, { q8-q15 } + b .non_linear_loop + +.return: + vpop { q4-q7 } + pop { r4-r12 } + + bx lr + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_per_cols.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_per_cols.j2 new file mode 100644 index 0000000..8bd452d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_per_cols.j2 @@ -0,0 +1,9 @@ +// vim: ft=arm +{% from "armv7neon_mmm_q_ops.j2" import per_col %} + +{{ per_col("per_col_min", "vmin.f32", mr, from, to) }} +{{ per_col("per_col_max", "vmax.f32", mr, from, to) }} +{{ per_col("per_col_mul", "vmul.f32", mr, from, to) }} +{{ per_col("per_col_add", "vadd.f32", mr, from, to) }} +{{ per_col("per_col_sub", "vsub.f32", mr, from, to) }} +{{ per_col("per_col_sub_flipped", "vsub.f32", mr, from, to, flipped=true) }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_per_rows.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_per_rows.j2 new file mode 100644 index 0000000..e4c29e3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_per_rows.j2 @@ -0,0 +1,9 @@ +// vim: ft=arm +{% from "armv7neon_mmm_q_ops.j2" import per_row %} + +{{ per_row("per_row_min", "vmin.f32", mr, from, to) }} +{{ per_row("per_row_max", "vmax.f32", mr, from, to) }} +{{ per_row("per_row_mul", "vmul.f32", mr, from, to) }} +{{ per_row("per_row_add", "vadd.f32", mr, from, to) }} +{{ per_row("per_row_sub", "vsub.f32", mr, from, to) }} +{{ per_row("per_row_sub_flipped", "vsub.f32", mr, from, to, flipped=true) }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_scalars.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_scalars.j2 new file mode 100644 index 0000000..5bcaef9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_f32_scalars.j2 @@ -0,0 +1,25 @@ +// vim: ft=arm +{% from "armv7neon_mmm_q_ops.j2" import scalar %} + +{{ scalar("scalar_min", "vmin.f32", from, to) }} +{{ scalar("scalar_max", "vmax.f32", from, to) }} +{{ scalar("scalar_mul", "vmul.f32", from, to) }} +{{ scalar("scalar_add", "vadd.f32", from, to) }} +{{ scalar("scalar_sub", "vsub.f32", from, to) }} +{{ scalar("scalar_sub_flipped", "vsub.f32", from, to, flipped=true) }} + +.leaky_relu: + vmov s0, r3 + vdup.32 q0, d0[0] + {% for reg in range(from, to + 1) %} + vmul.f32 q2, q{{reg}}, q0 + vcgt.f32 q1, q{{reg}}, 0 + vbsl q1, q{{reg}}, q2 + vmov q{{reg}}, q1 + {% endfor %} + b .non_linear_loop + +.q_shl: +.q_shr: +.q_scale: + b .unsupported diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_i32_32x1.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_i32_32x1.S.j2 new file mode 100644 index 0000000..3e86c11 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_i32_32x1.S.j2 @@ -0,0 +1,174 @@ +// vim: ft=arm + +// C tile regs: q8..q16 + + .arm + .text + .global armv7neon_mmm_i32_32x1_{{suffix}} + .type armv7neon_mmm_i32_32x1_{{suffix}}, %function + +armv7neon_mmm_i32_32x1_{{suffix}}: + + pld [r0] + push { r4-r12 } + vpush { q4-q7 } + +{% include "dispatcher.j2" %} + +.add_mat_mul: + // r3 r4 r5 r6 + // k a b packing + cmp r3, #0 + beq .non_linear_loop + + mov r1, r4 // packed A ptr + pld [r3] + pld [r7] + + cmp r6, #1 + beq .packed_packed_i8i8 + + .packed_packed: + + .packed_packed_loop_1: + vldmia r1!, { q4-q7 } + + vld1.32 { d0[0] }, [ r5 ]! + + vmla.s32 q8, q4, d0[0] + + vldmia r1!, { q1-q4 } + + vmla.s32 q9, q5, d0[0] + vmla.s32 q10, q6, d0[0] + vmla.s32 q11, q7, d0[0] + + vmla.s32 q12, q1, d0[0] + vmla.s32 q13, q2, d0[0] + + vmla.s32 q14, q3, d0[0] + vmla.s32 q15, q4, d0[0] + + subs r3, r3, #1 + bne .packed_packed_loop_1 + b .non_linear_loop + + .packed_packed_i8i8: + + .packed_packed_loop_i8i8_1: + vldmia r1!, { q4-q5 } + + vld1.8 { d0[0] }, [ r5 ]! + vmovl.s8 q0, d0 + + vmovl.s8 q1, d8 + vmlal.s16 q8, d2, d0[0] + vmlal.s16 q9, d3, d0[0] + + vmovl.s8 q1, d9 + vmlal.s16 q10, d2, d0[0] + vmlal.s16 q11, d3, d0[0] + + vmovl.s8 q1, d10 + vmlal.s16 q12, d2, d0[0] + vmlal.s16 q13, d3, d0[0] + + vmovl.s8 q1, d11 + vmlal.s16 q14, d2, d0[0] + vmlal.s16 q15, d3, d0[0] + + subs r3, r3, #1 + bne .packed_packed_loop_i8i8_1 + b .non_linear_loop + +{% set from = 8 %}{% set to = 15 %}{% include "armv7neon_mmm_i32_scalars.j2" %} +{% set mr = 32 %}{% set from = 8 %}{% set to = 15 %}{% include "armv7neon_mmm_i32_per_rows.j2" %} +{% set mr = 32 %}{% set from = 8 %}{% set to = 15 %}{% include "armv7neon_mmm_i32_per_cols.j2" %} + +.add_unicast: + // r3, r4, r5, r6 <- ptr, rsc, csc, size + + cmp r6, #4 + beq .non_linear_addc_i32 + + {% for reg in range(16, 32) %} + vld1.s8 d0[0], [ r3 ], r4 + vld1.s8 d0[1], [ r3 ], r4 + vmovl.s8 q0, d0 + vmovl.s16 q0, d0 + vadd.i32 d{{reg}}, d0 + {% endfor %} + + b .non_linear_loop + +.non_linear_addc_i32: + {% for reg in range(16, 32) %} + vld1.s32 d0[0], [ r3 ], r4 + vld1.s32 d0[1], [ r3 ], r4 + vadd.i32 d{{reg}}, d0 + {% endfor %} + b .non_linear_loop + +.add_row_col_products: + vldm r4, { s0 } + + vldmia r3!, { q4-q7 } + + vmla.s32 q8, q4, d0[0] + vmla.s32 q9, q5, d0[0] + + vmla.s32 q10, q6, d0[0] + vmla.s32 q11, q7, d0[0] + + vldmia r3!, { q4-q7 } + + vmla.s32 q12, q4, d0[0] + vmla.s32 q13, q5, d0[0] + + vmla.s32 q14, q6, d0[0] + vmla.s32 q15, q7, d0[0] + + b .non_linear_loop + + {% include "armv7neon_mmm_i32_scale_q8_q15.j2" %} + +.store: + // r3, r4, r5, r6 <- ptr, rsc, csc, size + cmp r6, #4 + beq .store_strides_i32 + + {% for reg in range(8, 16) %} + vmovn.s32 d{{ reg * 2 }}, q{{reg}} + vmovn.s16 d{{ reg * 2 }}, q{{reg}} + {% endfor %} + {% for reg in range(8, 16) %} + {% set d = reg * 2 %} + vst1.s8 d{{d}}[0], [ r3 ], r4 + vst1.s8 d{{d}}[1], [ r3 ], r4 + vst1.s8 d{{d}}[2], [ r3 ], r4 + vst1.s8 d{{d}}[3], [ r3 ], r4 + {% endfor %} + + b .non_linear_loop + +.store_strides_i32: + {% for reg in range(8, 16) %} + {% set d = reg * 2 %} + vst1.s32 d{{d}}[0], [ r3 ], r4 + vst1.s32 d{{d}}[1], [ r3 ], r4 + vst1.s32 d{{ d + 1 }}[0], [ r3 ], r4 + vst1.s32 d{{ d + 1 }}[1], [ r3 ], r4 + {% endfor %} + + b .non_linear_loop + +.load_tile: + vldmia r3!, { q8-q15 } + b .non_linear_loop + +.return: + vpop { q4-q7 } + pop { r4-r12 } + + bx lr + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_i32_8x4.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_i32_8x4.S.j2 new file mode 100644 index 0000000..b55d35b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_i32_8x4.S.j2 @@ -0,0 +1,294 @@ +// vim: ft=arm + +// C tile regs +// +// q8[0] q10[0] q12[0] q14[0] +// q8[1] q10[1] q12[1] q14[1] +// q8[2] q10[2] q12[2] q14[2] +// q8[3] q10[3] q12[3] q14[3] +// +// q9[0] q11[0] q13[0] q15[0] +// q9[1] q11[1] q13[1] q15[1] +// q9[2] q11[2] q13[2] q15[2] +// q9[3] q11[3] q13[3] q15[3] + + .arm + .text + .global armv7neon_mmm_i32_8x4_{{suffix}} + .type armv7neon_mmm_i32_8x4_{{suffix}}, %function + +armv7neon_mmm_i32_8x4_{{suffix}}: + + pld [r0] + push { r4-r12 } + vpush { q4-q7 } + +{% include "dispatcher.j2" %} + +.add_mat_mul: + // r3 r4 r5 r6 + // k a b packing + cmp r3, #0 + beq .non_linear_loop + + mov r1, r4 // packed A ptr + pld [r3] + pld [r5] + + cmp r6, #1 + beq .packed_packed_i8i8 + + .packed_packed_loop_1: + + vldmia r1!, { q0, q1 } + vldmia r5!, { q2 } + + vmla.s32 q8, q0, d4[0] + vmla.s32 q9, q1, d4[0] + + vmla.s32 q10, q0, d4[1] + vmla.s32 q11, q1, d4[1] + + vmla.s32 q12, q0, d5[0] + vmla.s32 q13, q1, d5[0] + + vmla.s32 q14, q0, d5[1] + vmla.s32 q15, q1, d5[1] + + subs r3, r3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + + .packed_packed_i8i8: + pld [r5] // packed B ptr + + cmp r3, #4 + blt .packed_packed_loop_i8i8_1 + + .packed_packed_loop_i8i8_4: + pld [r1, #64] + pld [r5, #64] + + // q2: d4 -> d4,d5 A even cols (from r1) + // q3: d6 -> d6,d7 A odd cols (from r1) + // q0: s0 -> d0 : B even lines (from r5) + // q1: s4 -> d2 : B odd lines (from r5) + + // 0 + vldmia r1!, { d4 } + vldmia r5!, { s0 } + + vmovl.s8 q2, d4 + vmovl.s8 q0, d0 + + vmlal.s16 q8, d4, d0[0] + vmlal.s16 q9, d5, d0[0] + + vldmia r1!, { d6 } + + vmlal.s16 q10, d4, d0[1] + vmlal.s16 q11, d5, d0[1] + + vldmia r5!, { s4 } + + vmlal.s16 q12, d4, d0[2] + vmlal.s16 q13, d5, d0[2] + + vmlal.s16 q14, d4, d0[3] + vmlal.s16 q15, d5, d0[3] + + // 1 + vmovl.s8 q3, d6 + vmovl.s8 q1, d2 + + vmlal.s16 q8, d6, d2[0] + vldmia r1!, { d4 } + vmlal.s16 q9, d7, d2[0] + vldmia r5!, { s0 } + + vmlal.s16 q10, d6, d2[1] + vmlal.s16 q11, d7, d2[1] + + vmlal.s16 q12, d6, d2[2] + vmlal.s16 q13, d7, d2[2] + + vmlal.s16 q14, d6, d2[3] + vmlal.s16 q15, d7, d2[3] + + // 2 + vmovl.s8 q2, d4 + vmovl.s8 q0, d0 + + vmlal.s16 q8, d4, d0[0] + vmlal.s16 q9, d5, d0[0] + + vldmia r1!, { d6 } + + vmlal.s16 q10, d4, d0[1] + vmlal.s16 q11, d5, d0[1] + + vldmia r5!, { s4 } + + vmlal.s16 q12, d4, d0[2] + vmlal.s16 q13, d5, d0[2] + + vmlal.s16 q14, d4, d0[3] + vmlal.s16 q15, d5, d0[3] + + // 3 + vmovl.s8 q3, d6 + vmovl.s8 q1, d2 + + vmlal.s16 q8, d6, d2[0] + vmlal.s16 q9, d7, d2[0] + + vmlal.s16 q10, d6, d2[1] + vmlal.s16 q11, d7, d2[1] + + vmlal.s16 q12, d6, d2[2] + vmlal.s16 q13, d7, d2[2] + + vmlal.s16 q14, d6, d2[3] + vmlal.s16 q15, d7, d2[3] + + sub r3, r3, #4 + cmp r3, #4 + bge .packed_packed_loop_i8i8_4 + + cmp r3, #0 + beq .non_linear_loop + + .packed_packed_loop_i8i8_1: + + vldmia r1!, { s0, s1 } + vmovl.s8 q0, d0 + vldmia r5!, { s4 } + vmovl.s8 q1, d2 + + vmlal.s16 q8, d0, d2[0] + vmlal.s16 q9, d1, d2[0] + + vmlal.s16 q10, d0, d2[1] + vmlal.s16 q11, d1, d2[1] + + vmlal.s16 q12, d0, d2[2] + vmlal.s16 q13, d1, d2[2] + + vmlal.s16 q14, d0, d2[3] + vmlal.s16 q15, d1, d2[3] + + subs r3, r3, #1 + bne .packed_packed_loop_i8i8_1 + b .non_linear_loop + +{% set from = 8 %}{% set to = 15 %}{% include "armv7neon_mmm_i32_scalars.j2" %} +{% set mr = 8 %}{% set from = 8 %}{% set to = 15 %}{% include "armv7neon_mmm_i32_per_rows.j2" %} +{% set mr = 8 %}{% set from = 8 %}{% set to = 15 %}{% include "armv7neon_mmm_i32_per_cols.j2" %} + +.add_unicast: + // r3, r4, r5, r6 <- ptr, rsc, csc, size + cmp r6, #4 + beq .non_linear_addc_i32 + + {% for col in range(0, 4) %} + mov r8, r3 + {% for reg in range(0, 4) %} + vld1.s8 d0[0], [ r8 ], r4 + vld1.s8 d0[1], [ r8 ], r4 + vmovl.s8 q0, d0 + vmovl.s16 q0, d0 + vadd.i32 d{{ col * 4 + reg + 16 }}, d0 + {% endfor %} + add r3, r3, r5 + {% endfor %} + + b .non_linear_loop + +.non_linear_addc_i32: + + {% for col in range(0, 4) %} + mov r8, r3 + {% for reg in range(0, 4) %} + vld1.s32 d0[0], [ r8 ], r4 + vld1.s32 d0[1], [ r8 ], r4 + vadd.i32 d{{ col * 4 + reg + 16 }}, d0 + {% endfor %} + {% if col < 3 %} + add r3, r3, r5 + {% endif %} + {% endfor %} + +b .non_linear_loop + +.add_row_col_products: + vldmia r3!, { q0, q1 } + vldmia r4!, { q4 } + + vmla.s32 q8, q0, d8[0] + vmla.s32 q9, q1, d8[0] + + vmla.s32 q10, q0, d8[1] + vmla.s32 q11, q1, d8[1] + + vmla.s32 q12, q0, d9[0] + vmla.s32 q13, q1, d9[0] + + vmla.s32 q14, q0, d9[1] + vmla.s32 q15, q1, d9[1] + + b .non_linear_loop + + {% include "armv7neon_mmm_i32_scale_q8_q15.j2" %} + +.store: + // r3, r4, r5, r6 <- ptr, rsc, csc, size + cmp r6, #4 + beq .store_strides_i32 + + {% for reg in range(8, 16) %} + vmovn.s32 d{{ reg * 2 }}, q{{reg}} + vmovn.s16 d{{ reg * 2 }}, q{{reg}} + {% endfor %} + {% for col in range(0, 4) %} + mov r8, r3 + {% for reg in range(0, 2) %} + {% set d = (col * 2 + reg) * 2 + 16 %} + vst1.s8 d{{d}}[0], [ r8 ], r4 + vst1.s8 d{{d}}[1], [ r8 ], r4 + vst1.s8 d{{d}}[2], [ r8 ], r4 + vst1.s8 d{{d}}[3], [ r8 ], r4 + {% endfor %} + {% if col < 3 %} + add r3, r3, r5 + {% endif %} + {% endfor %} + + b .non_linear_loop + +.store_strides_i32: + + {% for col in range(0, 4) %} + mov r8, r3 + {% for reg in range(0, 4) %} + {% for lane in range(0, 2) %} + vst1.s32 d{{ col * 4 + reg + 16 }}[{{lane}}], [ r8 ], r4 + {% endfor %} + {% endfor %} + {% if col < 3 %} + add r3, r3, r5 + {% endif %} + {% endfor %} + + b .non_linear_loop + +.load_tile: + vldmia r3!, { q8-q15 } + b .non_linear_loop + +.return: + vpop { q4-q7 } + pop { r4-r12 } + + bx lr + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_i32_per_cols.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_i32_per_cols.j2 new file mode 100644 index 0000000..886cee6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_i32_per_cols.j2 @@ -0,0 +1,9 @@ +// vim: ft=arm +{% from "armv7neon_mmm_q_ops.j2" import per_col %} + +{{ per_col("per_col_min", "vmin.s32", mr, from, to) }} +{{ per_col("per_col_max", "vmax.s32", mr, from, to) }} +{{ per_col("per_col_mul", "vmul.s32", mr, from, to) }} +{{ per_col("per_col_add", "vadd.s32", mr, from, to) }} +{{ per_col("per_col_sub", "vsub.s32", mr, from, to) }} +{{ per_col("per_col_sub_flipped", "vsub.s32", mr, from, to, flipped=true) }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_i32_per_rows.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_i32_per_rows.j2 new file mode 100644 index 0000000..5f9c3dc --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_i32_per_rows.j2 @@ -0,0 +1,9 @@ +// vim: ft=arm +{% from "armv7neon_mmm_q_ops.j2" import per_row %} + +{{ per_row("per_row_min", "vmin.s32", mr, from, to) }} +{{ per_row("per_row_max", "vmax.s32", mr, from, to) }} +{{ per_row("per_row_mul", "vmul.s32", mr, from, to) }} +{{ per_row("per_row_add", "vadd.s32", mr, from, to) }} +{{ per_row("per_row_sub", "vsub.s32", mr, from, to) }} +{{ per_row("per_row_sub_flipped", "vsub.s32", mr, from, to, flipped=true) }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_i32_scalars.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_i32_scalars.j2 new file mode 100644 index 0000000..50a9482 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_i32_scalars.j2 @@ -0,0 +1,20 @@ +// vim: ft=arm +{% from "armv7neon_mmm_q_ops.j2" import scalar %} + +{{ scalar("scalar_min", "vmin.s32", from, to) }} +{{ scalar("scalar_max", "vmax.s32", from, to) }} +{{ scalar("scalar_mul", "vmul.s32", from, to) }} +{{ scalar("scalar_add", "vadd.s32", from, to) }} +{{ scalar("scalar_sub", "vsub.s32", from, to) }} +{{ scalar("scalar_sub_flipped", "vsub.s32", from, to, flipped=true) }} + +.leaky_relu: + vmov s0, r3 + vdup.32 q0, d0[0] + {% for reg in range(from, to + 1) %} + vmul.s32 q2, q{{reg}}, q0 + vcgt.s32 q1, q{{reg}}, 0 + vbsl q1, q{{reg}}, q2 + vmov q{{reg}}, q1 + {% endfor %} + b .non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_i32_scale_q8_q15.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_i32_scale_q8_q15.j2 new file mode 100644 index 0000000..0f50315 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_i32_scale_q8_q15.j2 @@ -0,0 +1,232 @@ +// vim: ft=arm + +.q_scale: + ldm r0, { r4, r5, r6, r7 } // fixme params are already loaded by disp. + vdup.s32 q0, r7 // q0 <- multiplier + + mov r3, #1 + vdup.s32 q1, r3 // q1 <- ones + vmovl.s32 q1, d2 + + add r5, #32 + neg r5, r5 + vdup.s32 q2, r5 // q2 <- -(shift + 32) + vmovl.s32 q2, d4 + + cmp r6, #1 + beq .q_scale_rounding_zero + cmp r6, #2 + beq .q_scale_rounding_away + cmp r6, #3 + beq .q_scale_rounding_minus_inf + cmp r6, #4 + beq .q_scale_rounding_plus_inf + cmp r6, #5 + beq .q_scale_rounding_even + cmp r6, #6 + beq .q_scale_rounding_odd + + b .unsupported + +.q_scale_rounding_zero: + {% for q in range(8, 16) %} + vclt.s32 q7, q{{q}}, #0 + vabs.s32 q{{q}}, q{{q}} + vqdmull.s32 q5, d{{ q * 2 }}, d0[0] + vqdmull.s32 q6, d{{ q * 2 + 1 }}, d0[0] + vsub.s64 q5, q1 + vsub.s64 q6, q1 + vqrshl.s64 q5, q2 + vqrshl.s64 q6, q2 + vmovn.s64 d{{ q * 2 }}, q5 + vmovn.s64 d{{ q * 2 + 1 }}, q6 + vneg.s32 q5, q{{q}} + vbit.s32 q{{q}}, q5, q7 + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_away: + {% for q in range(8, 16) %} + vclt.s32 q7, q{{q}}, #0 + vabs.s32 q{{q}}, q{{q}} + vqdmull.s32 q5, d{{ q * 2 }}, d0[0] + vqdmull.s32 q6, d{{ q * 2 + 1 }}, d0[0] + vqrshl.s64 q5, q2 + vqrshl.s64 q6, q2 + vmovn.s64 d{{ q * 2 }}, q5 + vmovn.s64 d{{ q * 2 + 1 }}, q6 + vneg.s32 q5, q{{q}} + vbit.s32 q{{q}}, q5, q7 + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_minus_inf: + {% for q in range(8, 16) %} + vqdmull.s32 q5, d{{ q * 2 }}, d0[0] + vqdmull.s32 q6, d{{ q * 2 + 1 }}, d0[0] + vsub.s64 q5, q1 + vsub.s64 q6, q1 + vqrshl.s64 q5, q2 + vqrshl.s64 q6, q2 + vmovn.s64 d{{ q * 2 }}, q5 + vmovn.s64 d{{ q * 2 + 1 }}, q6 + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_plus_inf: + {% for q in range(8, 16) %} + vqdmull.s32 q5, d{{ q * 2 }}, d0[0] + vqdmull.s32 q6, d{{ q * 2 + 1 }}, d0[0] + vqrshl.s64 q5, q2 + vqrshl.s64 q6, q2 + vmovn.s64 d{{ q * 2 }}, q5 + vmovn.s64 d{{ q * 2 + 1 }}, q6 + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_even: + {% for q in range(8, 16) %} + vclt.s32 q7, q{{q}}, #0 + vabs.s32 q{{q}}, q{{q}} + vqdmull.s32 q5, d{{ q * 2 }}, d0[0] + vqdmull.s32 q6, d{{ q * 2 + 1 }}, d0[0] + vqshl.s64 q3, q5, q2 + vqshl.s64 q4, q6, q2 + vand q3, q3, q1 + vand q4, q4, q1 + vsub.s64 q3, q3, q1 + vsub.s64 q4, q4, q1 + vadd.s64 q5, q3 + vadd.s64 q6, q4 + vqrshl.s64 q5, q2 + vqrshl.s64 q6, q2 + vmovn.s64 d{{ q * 2 }}, q5 + vmovn.s64 d{{ q * 2 + 1 }}, q6 + vneg.s32 q5, q{{q}} + vbit.s32 q{{q}}, q5, q7 + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_odd: + {% for q in range(8, 16) %} + vclt.s32 q7, q{{q}}, #0 + vabs.s32 q{{q}}, q{{q}} + vqdmull.s32 q5, d{{ q * 2 }}, d0[0] + vqdmull.s32 q6, d{{ q * 2 + 1 }}, d0[0] + vqshl.s64 q3, q5, q2 + vqshl.s64 q4, q6, q2 + vand q3, q3, q1 + vand q4, q4, q1 + vsub.s64 q5, q3 + vsub.s64 q6, q4 + vqrshl.s64 q5, q2 + vqrshl.s64 q6, q2 + vmovn.s64 d{{ q * 2 }}, q5 + vmovn.s64 d{{ q * 2 + 1 }}, q6 + vneg.s32 q5, q{{q}} + vbit.s32 q{{q}}, q5, q7 + {% endfor %} + + b .non_linear_loop + +.q_shl: + ldm r0, { r4, r5 } // fixme params are already loaded by disp. + vdup.s32 q2, r5 // q2 <- shift + + {% for q in range(8, 16) %} + vqrshl.s32 q{{q}}, q2 // Shift + {% endfor %} + + b .non_linear_loop + +.q_shr: + ldm r0, { r4, r5, r6 } // fixme params are already loaded by disp. + + mov r3, #1 + vdup.s32 q1, r3 // q1 <- ones + + neg r5, r5 + vdup.s32 q2, r5 // q2 <- shift + + cmp r6, #1 + beq .q_shr_rounding_zero + cmp r6, #2 + beq .q_shr_rounding_away + cmp r6, #3 + beq .q_shr_rounding_minus_inf + cmp r6, #4 + beq .q_shr_rounding_plus_inf + cmp r6, #5 + beq .q_shr_rounding_even + cmp r6, #6 + beq .q_shr_rounding_odd + + b .unsupported + +.q_shr_rounding_zero: + // return signum(x) * ((abs(x) - 1) >>r shift ) + {% for q in range(8, 16) %} + vclt.s32 q3, q{{q}}, #0 // Store the sign of the value + vabs.s32 q{{q}}, q{{q}} // Compute their abs + vsub.s32 q{{q}}, q1 // Substract 1 to abs(x) + vqrshl.s32 q{{q}}, q2 // Rounding shift (0.5 -> 1) + vneg.s32 q4, q{{q}} // Compute -((abs(x) - 1) >>r shift ) + vbit.s32 q{{q}}, q4, q3 // Restore sign of x with bit mask + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_away: + // return signum(x) * (abs(x) >>r shift ) + {% for q in range(8, 16) %} + vclt.s32 q3, q{{q}}, #0 // Store the sign of the value + vabs.s32 q{{q}}, q{{q}} // Compute their abs + vqrshl.s32 q{{q}}, q2 // Rounding shift (0.5 -> 1) + vneg.s32 q4, q{{q}} // Compute -(abs(x) >>r shift ) + vbit.s32 q{{q}}, q4, q3 // Restore sign of x with bit mask + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_minus_inf: + // return -(-x >>r shift) + {% for q in range(8, 16) %} + vneg.s32 q3, q{{q}} // Compute -x + vqrshl.s32 q3, q2 // Rounding shift (0.5 -> 1) + vneg.s32 q{{q}}, q3 // Compute -(-x >>r shift) + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_plus_inf: + // return x >>r shift + {% for q in range(8, 16) %} + vqrshl.s32 q{{q}}, q2 // Rounding shift (0.5 -> 1) + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_even: + // If (x >> shift) is odd -> (x - 0) >>r shift + // If (x >> shift) is even -> (x - 1) >>r shift + {% for q in range(8, 16) %} + vqshl.s32 q3, q{{q}}, q2 // Truncate shift (0.5 -> 0) + vand.s32 q4, q3, q1 // Store if x is odd + vsub.s32 q5, q4, q1 // If (x >> shift) is odd 0 else -1 + vadd.s32 q{{q}}, q{{q}}, q5 // If (x >> shift) is odd (x - 0) else (x - 1) + vqrshl.s32 q{{q}}, q2 // Rounding shift (0.5 -> 1) + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_odd: + // If (x >> shift) is even -> (x - 0) >>r shift + // If (x >> shift) is odd -> (x - 1) >>r shift + {% for q in range(8, 16) %} + vqshl.s32 q3, q{{q}}, q2 // Truncate shift (0.5 -> 0) + vand.s32 q4, q3, q1 // Store if x >> shift is odd + vneg.s32 q5, q4 // If x is odd -1 else 0 + vadd.s32 q{{q}}, q{{q}}, q5 // If x is odd (x - 1) else (x - 0) + vqrshl.s32 q{{q}}, q2 // Rounding shift (0.5 -> 1) + {% endfor %} + b .non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_q_ops.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_q_ops.j2 new file mode 100644 index 0000000..261b50e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_mmm_q_ops.j2 @@ -0,0 +1,77 @@ +// Jinja2 macros for armv7neon scalar / per-row / per-col operations. +// Shared by both f32 and i32 kernel variants. + +{% macro scalar(label, op, from, to, flipped=false) %} +.{{label}}: + vmov s0, r3 + vdup.32 q0, d0[0] + {% if flipped %} + {% for reg in range(from, to + 1) %} + {{op}} q{{reg}}, q{{reg}}, q0 + {% endfor %} + {% else %} + {% for reg in range(from, to + 1) %} + {{op}} q{{reg}}, q0, q{{reg}} + {% endfor %} + {% endif %} + b .non_linear_loop +{% endmacro %} + +{% macro per_row(label, op, mr, from, to, flipped=false) %} +.{{label}}: + +{% set mr_over_4 = mr // 4 %} +{% set mr_over_4_min_1 = mr // 4 - 1 %} + +{% for reg in range(0, mr_over_4_min_1 + 1) %} + vldmia r3!, { q{{reg}} } +{% endfor %} + +{% if flipped %} + {% for acc in range(from, to + 1) %} + {% set other = (acc - from) % mr_over_4 %} + {{op}} q{{acc}}, q{{acc}}, q{{other}} + {% endfor %} +{% else %} + {% for acc in range(from, to + 1) %} + {% set other = (acc - from) % mr_over_4 %} + {{op}} q{{acc}}, q{{other}}, q{{acc}} + {% endfor %} +{% endif %} + +b .non_linear_loop +{% endmacro %} + +{% macro per_col(label, op, mr, from, to, flipped=false) %} +.{{label}}: + +{% set mr_over_4 = mr // 4 %} +{% set mr_over_4_min_1 = mr // 4 - 1 %} + +{% set cols = (to + 1 - from) // mr_over_4 %} +{% set cols_min_1 = (to + 1 - from) // mr_over_4 - 1 %} + +{% if cols == 1 %} + vld1.f32 d0[0], [ r3 ] +{% else %} + {% set cols_over_2_minus_1 = cols // 2 - 1 %} + {% for c in range(0, cols_over_2_minus_1 + 1) %} + vldmia r3!, { d{{c}} } + {% endfor %} +{% endif %} + + +{% for right in range(0, cols_min_1 + 1) %} + vdup.f32 q3, d{{ right // 2 }}[{{ right % 2 }}] + {% for down in range(0, mr_over_4_min_1 + 1) %} + {% set acc = mr_over_4 * right + from + down %} + {% if flipped %} + {{op}} q{{acc}}, q{{acc}}, q3 + {% else %} + {{op}} q{{acc}}, q3, q{{acc}} + {% endif %} + {% endfor %} +{% endfor %} + + b .non_linear_loop +{% endmacro %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_prefetch.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_prefetch.S.j2 new file mode 100644 index 0000000..d153e66 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_prefetch.S.j2 @@ -0,0 +1,22 @@ +// vim: ft=arm + +.arm +.text +.global armv7neon_prefetch_{{suffix}} +.type armv7neon_prefetch_{{suffix}}, %function + +armv7neon_prefetch_{{suffix}}: +loop: + pld [r0] + pld [r0, #32] + pld [r0, #64] + pld [r0, #96] + pld [r0, #128] + pld [r0, #160] + pld [r0, #192] + pld [r0, #224] + add r0, r0, #256 + cmp r0, r1 + blt loop + + bx lr diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_sigmoid_f32_4n.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_sigmoid_f32_4n.S.j2 new file mode 100644 index 0000000..baa5072 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_sigmoid_f32_4n.S.j2 @@ -0,0 +1,215 @@ +// vim: ft=arm + + .arm + .text + .global armv7neon_sigmoid_f32_4n_{{suffix}} + .type armv7neon_sigmoid_f32_4n_{{suffix}}, %function + +/* + s16–s31 (d8–d15, q4–q7) must be preserved + s0–s15 (d0–d7, q0–q3) and d16–d31 (q8–q15) do not need to be preserved +*/ + +armv7neon_sigmoid_f32_4n_{{suffix}}: + cmp r1, #0 + blxeq lr + + vpush { q4-q7 } + + adr r2, .coeffs_num + vldmia r2!, { s0-s13 } + +// q4 -> q4,5,6 +// q5 -> q7,8,9 +// q6 -> q10,11,12 +// q7 -> q13,14,15 + + + cmp r1, #12 + blt .loop + +.loop_3: + vldmia r0, { q4, q5, q6 } // q4 <- x + + vdup.32 q15, d0[0] + vmax.f32 q4, q15 + vmax.f32 q5, q15 + vmax.f32 q6, q15 + vdup.32 q15, d0[1] + vmin.f32 q4, q15 + vmin.f32 q5, q15 + vmin.f32 q6, q15 + + vmul.f32 q7, q4, q4 // q7 <- x2 + vmul.f32 q8, q5, q5 + vmul.f32 q9, q6, q6 + + vdup.32 q10, d1[0] + vdup.32 q11, d1[0] + vdup.32 q12, d1[0] + vdup.32 q13, d1[1] + vdup.32 q14, d1[1] + vdup.32 q15, d1[1] + vmla.f32 q13, q7, q10 + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + vdup.32 q10, d2[0] + vdup.32 q11, d2[0] + vdup.32 q12, d2[0] + vmla.f32 q10, q13, q7 + vmla.f32 q11, q14, q8 + vmla.f32 q12, q15, q9 + vdup.32 q13, d2[1] + vdup.32 q14, d2[1] + vdup.32 q15, d2[1] + vmla.f32 q13, q7, q10 + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + vdup.32 q10, d3[0] + vdup.32 q11, d3[0] + vdup.32 q12, d3[0] + vmla.f32 q10, q13, q7 + vmla.f32 q11, q14, q8 + vmla.f32 q12, q15, q9 + vdup.32 q13, d3[1] + vdup.32 q14, d3[1] + vdup.32 q15, d3[1] + vmla.f32 q13, q7, q10 + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + vdup.32 q10, d4[0] + vdup.32 q11, d4[0] + vdup.32 q12, d4[0] + vmla.f32 q10, q13, q7 + vmla.f32 q11, q14, q8 + vmla.f32 q12, q15, q9 + vmul.f32 q4, q4, q10 // q4 <- numerator + vmul.f32 q5, q5, q11 + vmul.f32 q6, q6, q12 + + vdup.32 q10, d4[1] + vdup.32 q11, d4[1] + vdup.32 q12, d4[1] + vdup.32 q13, d5[0] + vdup.32 q14, d5[0] + vdup.32 q15, d5[0] + vmla.f32 q13, q7, q10 + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + vdup.32 q10, d5[1] + vdup.32 q11, d5[1] + vdup.32 q12, d5[1] + vmla.f32 q10, q13, q7 + vmla.f32 q11, q14, q8 + vmla.f32 q12, q15, q9 + vdup.32 q13, d6[0] + vdup.32 q14, d6[0] + vdup.32 q15, d6[0] + vmla.f32 q13, q7, q10 // q13 <- denum + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + + vrecpe.f32 q7, q13 + vrecpe.f32 q8, q14 + vrecpe.f32 q9, q15 + vrecps.f32 q10, q7, q13 + vrecps.f32 q11, q8, q14 + vrecps.f32 q12, q9, q15 + vmul.f32 q7, q7, q10 + vmul.f32 q8, q8, q11 + vmul.f32 q9, q9, q12 + vrecps.f32 q10, q7, q13 + vrecps.f32 q11, q8, q14 + vrecps.f32 q12, q9, q15 + vmul.f32 q7, q7, q10 // q7 <- 1/q13 + vmul.f32 q8, q8, q11 + vmul.f32 q9, q9, q12 + + vdup.32 q10, d6[1] + vdup.32 q11, d6[1] + vdup.32 q12, d6[1] + vmla.f32 q10, q4, q7 + vmla.f32 q11, q5, q8 + vmla.f32 q12, q6, q9 + + vstmia r0!, { q10, q11, q12 } + + subs r1, #12 + cmp r1, #12 + bge .loop_3 + + cmp r1, #0; + beq .return + +.loop: + vldmia r0, { q4 } // q4 <- x + + vdup.32 q15, d0[0] + vmax.f32 q4, q15 + vdup.32 q15, d0[1] + vmin.f32 q4, q15 + + vmul.f32 q7, q4, q4 // q7 <- x2 + + vdup.32 q10, d1[0] + vdup.32 q13, d1[1] + vmla.f32 q13, q7, q10 + vdup.32 q10, d2[0] + vmla.f32 q10, q13, q7 + vdup.32 q13, d2[1] + vmla.f32 q13, q7, q10 + vdup.32 q10, d3[0] + vmla.f32 q10, q13, q7 + vdup.32 q13, d3[1] + vmla.f32 q13, q7, q10 + vdup.32 q10, d4[0] + vmla.f32 q10, q13, q7 + vmul.f32 q4, q4, q10 // q4 <- numerator + + vdup.32 q10, d4[1] + vdup.32 q13, d5[0] + vmla.f32 q13, q7, q10 + vdup.32 q10, d5[1] + vmla.f32 q10, q13, q7 + vdup.32 q13, d6[0] + vmla.f32 q13, q7, q10 // q13 <- denum + + vrecpe.f32 q7, q13 + vrecps.f32 q10, q7, q13 + vmul.f32 q7, q7, q10 + vrecps.f32 q10, q7, q13 + vmul.f32 q7, q7, q10 // q7 <- 1/q13 + + vdup.32 q10, d6[1] + vmla.f32 q10, q4, q7 + + vstmia r0!, { q10 } + + subs r1, #4; + bne .loop + +.return: + vpop { q4-q7 } + bx lr + +.coeffs_num: + .float -18.6 // low + .float 18.6 // high + .float -4.433153405e-18 // alpha_13 + .float 1.169974371e-14 + + .float -1.875289645e-11 + .float 4.257889523e-8 + .float 0.00004811817576 + .float 0.008163842030 + + .float 0.2499999971 + .float 3.922935744e-6 // beta_6 + .float 0.001524872358 + .float 0.1159886749 + + .float 1.0 + .float 0.5 // + .float 0.0 // padding + .float 0.0 + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_tanh_f32_4n.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_tanh_f32_4n.S.j2 new file mode 100644 index 0000000..5165f6f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/armv7neon_tanh_f32_4n.S.j2 @@ -0,0 +1,209 @@ +// vim: ft=arm + + .arm + .text + .global armv7neon_tanh_f32_4n_{{suffix}} + .type armv7neon_tanh_f32_4n_{{suffix}}, %function + +/* + s16–s31 (d8–d15, q4–q7) must be preserved + s0–s15 (d0–d7, q0–q3) and d16–d31 (q8–q15) do not need to be preserved +*/ + +armv7neon_tanh_f32_4n_{{suffix}}: + cmp r1, #0 + blxeq lr + + vpush { q4-q7 } + + adr r2, .coeffs_num + vldmia r2!, { s0-s13 } + +// q4 -> q4,5,6 +// q5 -> q7,8,9 +// q6 -> q10,11,12 +// q7 -> q13,14,15 + + cmp r1, #12 + blt .loop + +.loop_3: + vldmia r0, { q4, q5, q6 } // q4 <- x + + vdup.32 q15, d0[0] + vmax.f32 q4, q15 + vmax.f32 q5, q15 + vmax.f32 q6, q15 + vdup.32 q15, d0[1] + vmin.f32 q4, q15 + vmin.f32 q5, q15 + vmin.f32 q6, q15 + + vmul.f32 q7, q4, q4 // q7 <- x2 + vmul.f32 q8, q5, q5 + vmul.f32 q9, q6, q6 + + vdup.32 q10, d1[0] + vdup.32 q11, d1[0] + vdup.32 q12, d1[0] + vdup.32 q13, d1[1] + vdup.32 q14, d1[1] + vdup.32 q15, d1[1] + vmla.f32 q13, q7, q10 + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + vdup.32 q10, d2[0] + vdup.32 q11, d2[0] + vdup.32 q12, d2[0] + vmla.f32 q10, q13, q7 + vmla.f32 q11, q14, q8 + vmla.f32 q12, q15, q9 + vdup.32 q13, d2[1] + vdup.32 q14, d2[1] + vdup.32 q15, d2[1] + vmla.f32 q13, q7, q10 + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + vdup.32 q10, d3[0] + vdup.32 q11, d3[0] + vdup.32 q12, d3[0] + vmla.f32 q10, q13, q7 + vmla.f32 q11, q14, q8 + vmla.f32 q12, q15, q9 + vdup.32 q13, d3[1] + vdup.32 q14, d3[1] + vdup.32 q15, d3[1] + vmla.f32 q13, q7, q10 + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + vdup.32 q10, d4[0] + vdup.32 q11, d4[0] + vdup.32 q12, d4[0] + vmla.f32 q10, q13, q7 + vmla.f32 q11, q14, q8 + vmla.f32 q12, q15, q9 + vmul.f32 q4, q4, q10 // q4 <- numerator + vmul.f32 q5, q5, q11 + vmul.f32 q6, q6, q12 + + vdup.32 q10, d4[1] + vdup.32 q11, d4[1] + vdup.32 q12, d4[1] + vdup.32 q13, d5[0] + vdup.32 q14, d5[0] + vdup.32 q15, d5[0] + vmla.f32 q13, q7, q10 + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + vdup.32 q10, d5[1] + vdup.32 q11, d5[1] + vdup.32 q12, d5[1] + vmla.f32 q10, q13, q7 + vmla.f32 q11, q14, q8 + vmla.f32 q12, q15, q9 + vdup.32 q13, d6[0] + vdup.32 q14, d6[0] + vdup.32 q15, d6[0] + vmla.f32 q13, q7, q10 // q13 <- denum + vmla.f32 q14, q8, q11 + vmla.f32 q15, q9, q12 + + vrecpe.f32 q7, q13 + vrecpe.f32 q8, q14 + vrecpe.f32 q9, q15 + vrecps.f32 q10, q7, q13 + vrecps.f32 q11, q8, q14 + vrecps.f32 q12, q9, q15 + vmul.f32 q7, q7, q10 + vmul.f32 q8, q8, q11 + vmul.f32 q9, q9, q12 + vrecps.f32 q10, q7, q13 + vrecps.f32 q11, q8, q14 + vrecps.f32 q12, q9, q15 + vmul.f32 q7, q7, q10 // q7 <- 1/q13 + vmul.f32 q8, q8, q11 + vmul.f32 q9, q9, q12 + + vmul.f32 q10, q4, q7 + vmul.f32 q11, q5, q8 + vmul.f32 q12, q6, q9 + + vstmia r0!, { q10, q11, q12 } + + subs r1, #12 + cmp r1, #12 + bge .loop_3 + + cmp r1, #0; + beq .return + +.loop: + vldmia r0, { q4 } // q4 <- x + + vdup.32 q15, d0[0] + vmax.f32 q4, q15 + vdup.32 q15, d0[1] + vmin.f32 q4, q15 + + vmul.f32 q7, q4, q4 // q7 <- x2 + + vdup.32 q10, d1[0] + vdup.32 q13, d1[1] + vmla.f32 q13, q7, q10 + vdup.32 q10, d2[0] + vmla.f32 q10, q13, q7 + vdup.32 q13, d2[1] + vmla.f32 q13, q7, q10 + vdup.32 q10, d3[0] + vmla.f32 q10, q13, q7 + vdup.32 q13, d3[1] + vmla.f32 q13, q7, q10 + vdup.32 q10, d4[0] + vmla.f32 q10, q13, q7 + vmul.f32 q4, q4, q10 // q4 <- numerator + + vdup.32 q10, d4[1] + vdup.32 q13, d5[0] + vmla.f32 q13, q7, q10 + vdup.32 q10, d5[1] + vmla.f32 q10, q13, q7 + vdup.32 q13, d6[0] + vmla.f32 q13, q7, q10 // q13 <- denum + + vrecpe.f32 q7, q13 + vrecps.f32 q10, q7, q13 + vmul.f32 q7, q7, q10 + vrecps.f32 q10, q7, q13 + vmul.f32 q7, q7, q10 // q7 <- 1/q13 + + vmul.f32 q10, q4, q7 + + vstmia r0!, { q10 } + + subs r1, #4; + bne .loop + +.return: + vpop { q4-q7 } + bx lr + +.coeffs_num: + .float -8.9 // low + .float 8.9 // high + .float -8.488492677e-14 // alpha_13 + .float 5.277853000e-11 + + .float -2.022500419e-8 + .float 0.00001115424833 + .float 0.003103950131 + .float 0.1308400453 + + .float 0.9999999934 + .float 0.0002546136580 // beta_6 + .float 0.02449515379 + .float 0.4641733162 + + .float 1.0 + .float 0 // padding + .float 0 // padding + .float 0 // padding diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/dispatcher.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/dispatcher.j2 new file mode 100644 index 0000000..e1cd141 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armv7neon/dispatcher.j2 @@ -0,0 +1,38 @@ +// vim: ft=arm + +.non_linear: + +.non_linear_loop_entry: + sub r0, #20 + +.non_linear_loop: + add r0, #20 + ldm r0, { r2, r3, r4, r5, r6 } + + cmp r2, #{{ jump_table | length }} + movgt r2, #{{ jump_table | length }} + cmp r2, #0 + movlt r2, #{{ jump_table | length }} + + add pc, pc, r2, LSL#2 + nop // pc in Rn above is start of the add instruction + 8, hence a nop is needed + // This is A32 asm, for T32/Thump2 use nop.w and b.w to avoid problems. +{% for j in jump_table %} + b .{{j}} +{% endfor %} + b .unsupported + + +.unsupported: + mov r0, #1 + b .return + +.done: + mov r0, #0 + b .return + +.clear: +{% for r in range(4, 16) %} + veor q{{r}}, q{{r}}, q{{r}} +{% endfor %} + b .non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armvfpv2/armvfpv2_mmm_f32_4x4.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armvfpv2/armvfpv2_mmm_f32_4x4.S.j2 new file mode 100644 index 0000000..9ebfbe2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armvfpv2/armvfpv2_mmm_f32_4x4.S.j2 @@ -0,0 +1,491 @@ +// vim: ft=arm + + .arm + .text + .global armvfpv2_mmm_f32_4x4_{{suffix}} + .type armvfpv2_mmm_f32_4x4_{{suffix}}, %function + +// C tile: + +// s16 s20 s24 s28 +// s17 s21 s25 s29 +// s18 s22 s26 s30 +// s19 s23 s27 s31 + +// packed A: (2x4) alternating between (s0-s3) and (s4-s7) +// packed B: (2x4) alternating between (s8-s11) and (s12-15) + +// all vfp registers in use. + +armvfpv2_mmm_f32_4x4_{{suffix}}: + +/* + pld [r1] + pld [r1, #8] + pld [r2] + pld [r2, #8] +*/ + + push { r4-r12 } // no lr (we're a leaf), no fp. #24 bytes + + ldr r8, [sp, #28] + ldr r9, [sp, #24] + +// r8=rsc, r9=csc + + vmrs r6, FPSCR + bic r6, r6, #0x00370000 + vmsr FPSCR, r6 + + vpush { s16-s31 } + +{% include "dispatcher.j2" %} + +.clear: + eor r6, r6 + vmov s16, r6 + vmov.f32 s17, s16 + vmov.f32 s18, s16 + vmov.f32 s19, s16 + vmov.f32 s20, s16 + vmov.f32 s21, s16 + vmov.f32 s22, s16 + vmov.f32 s23, s16 + vmov.f32 s24, s16 + vmov.f32 s25, s16 + vmov.f32 s26, s16 + vmov.f32 s27, s16 + vmov.f32 s28, s16 + vmov.f32 s29, s16 + vmov.f32 s30, s16 + vmov.f32 s31, s16 + b .non_linear_loop + +.add_mat_mul: + // r3 <- k, r4 <- a, r5 <- b + cmp r3, #0 + beq .non_linear_loop + + mov r1, r4 // packed A ptr + pld [r3] + pld [r5] + + .packed_packed: + cmp r3, #4 + blt .packed_packed_loop_1 + + .packed_packed_loop_4: + + // 1 + vldmia r1!, { s0, s1 } + vldmia r5!, { s8, s9 } + + vmla.f32 s16, s0, s8 + vldmia r1!, { s2, s3 } + vmla.f32 s17, s1, s8 + vldmia r5!, { s10, s11 } + vmla.f32 s18, s2, s8 + vmla.f32 s19, s3, s8 + + vmla.f32 s20, s0, s9 + vmla.f32 s21, s1, s9 + vmla.f32 s22, s2, s9 + vmla.f32 s23, s3, s9 + + vldmia r1!, { s4-s7 } + vmla.f32 s24, s0, s10 + vmla.f32 s25, s1, s10 + vmla.f32 s26, s2, s10 + vmla.f32 s27, s3, s10 + + vldmia r5!, { s12-s15 } + vmla.f32 s28, s0, s11 + vmla.f32 s29, s1, s11 + vmla.f32 s30, s2, s11 + vmla.f32 s31, s3, s11 + + // 2 + vmla.f32 s16, s4, s12 + vmla.f32 s17, s5, s12 + vmla.f32 s18, s6, s12 + vmla.f32 s19, s7, s12 + + vldmia r1!, { s0-s3 } + + vmla.f32 s20, s4, s13 + vmla.f32 s21, s5, s13 + vmla.f32 s22, s6, s13 + vmla.f32 s23, s7, s13 + + vldmia r5!, { s8-s11 } + + vmla.f32 s24, s4, s14 + vmla.f32 s25, s5, s14 + vmla.f32 s26, s6, s14 + vmla.f32 s27, s7, s14 + + vmla.f32 s28, s4, s15 + vmla.f32 s29, s5, s15 + vmla.f32 s30, s6, s15 + vmla.f32 s31, s7, s15 + + // 3 + vmla.f32 s16, s0, s8 + vmla.f32 s17, s1, s8 + vmla.f32 s18, s2, s8 + vmla.f32 s19, s3, s8 + + vldmia r1!, { s4-s7 } + + vmla.f32 s20, s0, s9 + vmla.f32 s21, s1, s9 + vmla.f32 s22, s2, s9 + vmla.f32 s23, s3, s9 + + vldmia r5!, { s12-s15 } + + vmla.f32 s24, s0, s10 + vmla.f32 s25, s1, s10 + vmla.f32 s26, s2, s10 + vmla.f32 s27, s3, s10 + + pld [r1] + + vmla.f32 s28, s0, s11 + vmla.f32 s29, s1, s11 + vmla.f32 s30, s2, s11 + vmla.f32 s31, s3, s11 + + pld [r6] + + // 4 + vmla.f32 s16, s4, s12 + vmla.f32 s17, s5, s12 + vmla.f32 s18, s6, s12 + vmla.f32 s19, s7, s12 + + vmla.f32 s20, s4, s13 + vmla.f32 s21, s5, s13 + vmla.f32 s22, s6, s13 + vmla.f32 s23, s7, s13 + + vmla.f32 s24, s4, s14 + vmla.f32 s25, s5, s14 + vmla.f32 s26, s6, s14 + vmla.f32 s27, s7, s14 + + vmla.f32 s28, s4, s15 + vmla.f32 s29, s5, s15 + vmla.f32 s30, s6, s15 + vmla.f32 s31, s7, s15 + + sub r3, r3, #4 + cmp r3, #4 + bge .packed_packed_loop_4 + + cmp r3, #0 + beq .non_linear_loop + + .packed_packed_loop_1: + + vldmia r1!, { s0, s1 } + vldmia r5!, { s8, s9 } + + vmla.f32 s16, s0, s8 + vldmia r1!, { s2, s3 } + vmla.f32 s17, s1, s8 + vldmia r5!, { s10, s11 } + vmla.f32 s18, s2, s8 + vmla.f32 s19, s3, s8 + + vmla.f32 s20, s0, s9 + vmla.f32 s21, s1, s9 + vmla.f32 s22, s2, s9 + vmla.f32 s23, s3, s9 + + vmla.f32 s24, s0, s10 + vmla.f32 s25, s1, s10 + vmla.f32 s26, s2, s10 + vmla.f32 s27, s3, s10 + + vmla.f32 s28, s0, s11 + vmla.f32 s29, s1, s11 + vmla.f32 s30, s2, s11 + vmla.f32 s31, s3, s11 + + subs r3, r3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +.add_unicast: + {% for col in range(0, 4) %} + mov r8, r3 + {% for reg in range(0, 4) %} + vldr s0, [ r8 ] + vadd.f32 s{{ col * 4 + reg + 16 }}, s{{ col * 4 + reg + 16 }}, s0 + {% if reg < 3 %} + add r8, r8, r4 + {% endif %} + {% endfor %} + {% if col < 3 %} + add r3, r3, r5 + {% endif %} + {% endfor %} + + b .non_linear_loop + +.scalar_min: + vmov s0, r3 + {% for reg in range(16, 32) %} + vcmp.f32 s{{reg}}, s0 + vmrs apsr_nzcv, fpscr + vmovge s{{reg}}, s0 + {% endfor %} + + b .non_linear_loop + +.scalar_max: + vmov s0, r3 + {% for reg in range(16, 32) %} + vcmp.f32 s{{reg}}, s0 + vmrs apsr_nzcv, fpscr + vmovle s{{reg}}, s0 + {% endfor %} + + b .non_linear_loop + +.scalar_add: + vmov s0, r3 + {% for s in range(16, 32) %} + vadd.f32 s{{s}}, s{{s}}, s0 + {% endfor %} + + b .non_linear_loop + +.scalar_mul: + vmov s0, r3 + {% for s in range(16, 32) %} + vmul.f32 s{{s}}, s{{s}}, s0 + {% endfor %} + + b .non_linear_loop + +.scalar_sub: + vmov s0, r3 + {% for s in range(16, 32) %} + vsub.f32 s{{s}}, s0, s{{s}} + {% endfor %} + + b .non_linear_loop + +.scalar_sub_flipped: + vmov s0, r3 + {% for s in range(16, 32) %} + vsub.f32 s{{s}}, s{{s}}, s0 + {% endfor %} + + b .non_linear_loop + +.leaky_relu: + vmov s0, r3 + {% for reg in range(16, 32) %} + vmul.f32 s1, s0, s{{reg}} + vcmp.f32 s{{reg}}, #0 + vmrs apsr_nzcv, fpscr + vmovlt s{{reg}}, s1 + {% endfor %} + b .non_linear_loop + +.per_row_min: + vldm r3, {s0, s1, s2, s3} + {% for row in range(0, 4) %} + {% for col in range(0, 4) %} + {% set s %}s{{ col * 4 + row + 16 }}{% endset %} + vcmp.f32 {{s}}, s{{row}} + vmrs apsr_nzcv, fpscr + vmovge {{s}}, s{{row}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_row_max: + vldm r3, {s0, s1, s2, s3} + {% for row in range(0, 4) %} + {% for col in range(0, 4) %} + {% set s %}s{{ col * 4 + row + 16 }}{% endset %} + vcmp.f32 {{s}}, s{{row}} + vmrs apsr_nzcv, fpscr + vmovlt {{s}}, s{{row}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_row_add: + vldm r3, {s0, s1, s2, s3} + {% for row in range(0, 4) %} + {% for col in range(0, 4) %} + vadd.f32 s{{ col * 4 + row + 16 }}, s{{ col * 4 + row + 16 }}, s{{row}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_row_mul: + vldm r3, {s0, s1, s2, s3} + {% for row in range(0, 4) %} + {% for col in range(0, 4) %} + vmul.f32 s{{ col * 4 + row + 16 }}, s{{ col * 4 + row + 16 }}, s{{row}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_row_sub: + vldm r3, {s0, s1, s2, s3} + {% for row in range(0, 4) %} + {% for col in range(0, 4) %} + vsub.f32 s{{ col * 4 + row + 16 }}, s{{row}}, s{{ col * 4 + row + 16 }} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_row_sub_flipped: + vldm r3, {s0, s1, s2, s3} + {% for row in range(0, 4) %} + {% for col in range(0, 4) %} + vsub.f32 s{{ col * 4 + row + 16 }}, s{{ col * 4 + row + 16 }}, s{{row}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_col_min: + vldm r3, {s0, s1, s2, s3} + {% for row in range(0, 4) %} + {% for col in range(0, 4) %} + {% set s %}s{{ col * 4 + row + 16 }}{% endset %} + vcmp.f32 {{s}}, s{{col}} + vmrs apsr_nzcv, fpscr + vmovge {{s}}, s{{col}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_col_max: + vldm r3, {s0, s1, s2, s3} + {% for row in range(0, 4) %} + {% for col in range(0, 4) %} + {% set s %}s{{ col * 4 + row + 16 }}{% endset %} + vcmp.f32 {{s}}, s{{col}} + vmrs apsr_nzcv, fpscr + vmovlt {{s}}, s{{col}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_col_add: + vldm r3, {s0, s1, s2, s3} + {% for row in range(0, 4) %} + {% for col in range(0, 4) %} + vadd.f32 s{{ col * 4 + row + 16 }}, s{{ col * 4 + row + 16 }}, s{{col}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_col_mul: + vldm r3, {s0, s1, s2, s3} + {% for row in range(0, 4) %} + {% for col in range(0, 4) %} + vmul.f32 s{{ col * 4 + row + 16 }}, s{{ col * 4 + row + 16 }}, s{{col}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_col_sub: + vldm r3, {s0, s1, s2, s3} + {% for row in range(0, 4) %} + {% for col in range(0, 4) %} + vsub.f32 s{{ col * 4 + row + 16 }}, s{{col}}, s{{ col * 4 + row + 16 }} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.per_col_sub_flipped: + vldm r3, {s0, s1, s2, s3} + {% for row in range(0, 4) %} + {% for col in range(0, 4) %} + vsub.f32 s{{ col * 4 + row + 16 }}, s{{ col * 4 + row + 16 }}, s{{col}} + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + vldmia r3!, { s0, s1 } + vldmia r4!, { s8, s9 } + + vmla.f32 s16, s0, s8 + vldmia r3!, { s2, s3 } + vmla.f32 s17, s1, s8 + vldmia r4!, { s10, s11 } + vmla.f32 s18, s2, s8 + vmla.f32 s19, s3, s8 + + vmla.f32 s20, s0, s9 + vmla.f32 s21, s1, s9 + vmla.f32 s22, s2, s9 + vmla.f32 s23, s3, s9 + + vmla.f32 s24, s0, s10 + vmla.f32 s25, s1, s10 + vmla.f32 s26, s2, s10 + vmla.f32 s27, s3, s10 + + vmla.f32 s28, s0, s11 + vmla.f32 s29, s1, s11 + vmla.f32 s30, s2, s11 + vmla.f32 s31, s3, s11 + + b .non_linear_loop + +.store: + {% for col in range(0, 4) %} + mov r8, r3 + {% for reg in range(0, 4) %} + fsts s{{ col * 4 + reg + 16 }}, [ r8 ] + {% if reg < 3 %} + add r8, r8, r4 + {% endif %} + {% endfor %} + {% if col < 3 %} + add r3, r3, r5 + {% endif %} + {% endfor %} + + mov r0, #0 + b .return + +.load_tile: + vldmia r3!, { s16-s31 } + b .non_linear_loop + +.q_scale: +.q_shl: +.q_shr: + b .unsupported + +.return: + vpop { s16-s31 } + pop { r4-r12 } + + bx lr + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armvfpv2/dispatcher.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armvfpv2/dispatcher.j2 new file mode 100644 index 0000000..ce3e688 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm32/armvfpv2/dispatcher.j2 @@ -0,0 +1,32 @@ +// vim: ft=arm + +.non_linear: + +.non_linear_loop_entry: + sub r0, #20 + +.non_linear_loop: + add r0, #20 + ldm r0, { r2, r3, r4, r5, r6 } + + cmp r2, #{{ jump_table | length }} + movgt r2, #{{ jump_table | length }} + cmp r2, #0 + movlt r2, #{{ jump_table | length }} + + add pc, pc, r2, LSL#2 + nop // pc in Rn above is start of the add instruction + 8, hence a nop is needed + // This is A32 asm, for T32/Thump2 use nop.w and b.w to avoid problems. +{% for j in jump_table %} + b .{{j}} +{% endfor %} + b .unsupported + +.unsupported: + mov r0, #1 + b .return + +.done: + mov r0, #0 + b .return + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/apple_amx/apple_amx_mmm_f16_64x1.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/apple_amx/apple_amx_mmm_f16_64x1.S.j2 new file mode 100644 index 0000000..b03fe48 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/apple_amx/apple_amx_mmm_f16_64x1.S.j2 @@ -0,0 +1,533 @@ +// vim: ft=arm +.text +.align 4 + +/* Z: 32x1 + z0[0] .. z0[15] z1[0] .. z1[15] +*/ + + +.global {{G}}apple_amx_mmm_f16_64x1_{{suffix}} +{{G}}apple_amx_mmm_f16_64x1_{{suffix}}: + +{{ AMX_SET }} + + // set x1 to a 128 bytes aligned block for loads + mov x1, sp + lsr x1, x1, #7 + lsl x1, x1, #7 + sub x1, x1, 128 + +{% include "dispatcher.j2" %} + +.leaky_relu: +.q_scale: +.q_shl: +.q_shr: + b .unsupported + +.add_mat_mul: + + ldr x2, [x0, #24] // b + ldp x3, x4, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + + orr x4, x4, {{ 0 | setting(62) }} // load a pair of A + + mov x5, {{ 0 | setting(43) }} // f16 + orr x5, x5, {{ 0 | setting(38) }} // Broadcast Y + + orr x6, x5, {{ 0 | setting(20) }} // z offset + orr x6, x6, {{ 0 | setting(16) }} // x offset + + cmp x3, #32 + blt .packed_packed_loop_1 + + mov x9, {{ 0 | setting(32) }} // Y broadcast offset += 1 + + .packed_packed_loop_32: + mov x7, x5 + mov x8, x6 + {{ amx("ldy", 2) }} + {% for k in range(0, 32) %} + {{ amx("ldx", 4) }} + add x4, x4, 128 + {{ amx("vecfp", 7) }} + {{ amx("vecfp", 8) }} + add x7, x7, x9 + add x8, x8, x9 + {% endfor %} + add x2, x2, #64 + sub x3, x3, #32 + cmp x3, #32 + bge .packed_packed_loop_32 + + cmp x3, #0 + beq .non_linear_loop + + .packed_packed_loop_1: + ldr w7, [x2], #2 + str w7, [x1] + {{ amx("ldx", 4) }} + {{ amx("ldy", 1) }} + {{ amx("vecfp", 5) }} + {{ amx("vecfp", 6) }} + add x4, x4, 128 + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +.clear: + // top left + eor x2, x2, x2 + orr x2, x2, {{ 0 | setting(27) }} + orr x2, x2, {{ 0 | setting(28) }} + orr x2, x2, {{ 0 | setting(29) }} // Z = 0 + {{ amx("fma32", 2) }} + + // top right + orr x2, x2, {{ 0 | setting(20) }} // Z row = 1 + {{ amx("fma32", 2) }} + + // bottom right + orr x2, x2, {{ 0 | setting(21) }} // Z row = 3 + {{ amx("fma32", 2) }} + + // bottom left + eor x2, x2, {{ 0 | setting(20) }} // Z row = 2 + {{ amx("fma32", 2) }} + + b .non_linear_loop + +.per_col_sub: + + // performs a unary neg on Z + eor x2, x2, x2 // X[0] = Z[0] + // extr[hxyz] is suport confusing + + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(28) }} + orr x4, x4, {{ 0 | setting(27) }} // Z=-X + + {{ amx("extrx", 2) }} + {{ amx("fms16", 4) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + add x4, x4, {{ 0 | setting(20) }} // next Z row + {{ amx("extrx", 2) }} // extr[hxyz] is confusing + {{ amx("fms16", 4) }} + + // continue + +.per_col_add: + ldr x2, [x0, #8] + + // broadcast value to x0 + ld1 { v0.h }[0], [x2] + dup v0.8h, v0.h[0] + st1 { v0.8h }, [x1], #16 + st1 { v0.8h }, [x1], #16 + st1 { v0.8h }, [x1], #16 + st1 { v0.8h }, [x1], #16 + sub x1, x1, #64 + + {{ amx("ldx", 1) }} // load into x0 by default + + mov x2, {{ 0 | setting(28) }} // z += y + {{ amx("fma16", 2) }} + + orr x2, x2, {{ 0 | setting(20) }} // target is now z1 + {{ amx("fma16", 2) }} + + b .non_linear_loop + +.per_col_sub_flipped: + ldr x2, [x0, #8] + + // broadcast value to x0 + ld1 { v0.h }[0], [x2] + dup v0.8h, v0.h[0] + st1 { v0.8h }, [x1], #16 + st1 { v0.8h }, [x1], #16 + st1 { v0.8h }, [x1], #16 + st1 { v0.8h }, [x1], #16 + sub x1, x1, #64 + + {{ amx("ldx", 1) }} // load into x0 by default + + mov x2, {{ 0 | setting(28) }} // z += y + {{ amx("fms16", 2) }} + + orr x2, x2, {{ 0 | setting(20) }} // target is now z1 + {{ amx("fms16", 2) }} + + b .non_linear_loop + +.per_row_sub_flipped: + ldr x2, [x0, #8] + + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x2], #64 + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1], #64 + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x2] + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + sub x1, x1, #64 + + orr x2, x1, {{ 0 | setting(62) }} // load a pair + {{ amx("ldy", 2) }} + + mov x2, {{ 0 | setting(63) }} // vector mode + orr x2, x2, {{ 0 | setting(29) }} // z -= y + + // top left + {{ amx("fms16", 2) }} + + // bottom left + orr x2, x2, {{ 0 | setting(20) }} // Z row = 1 + orr x2, x2, {{ 0 | setting(6) }} // Y offset + {{ amx("fms16", 2) }} + + b .non_linear_loop + +.per_row_sub: + // performs a unary neg on Z + eor x2, x2, x2 // X[0] = Z[0] + + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(28) }} + orr x4, x4, {{ 0 | setting(27) }} // Z=-X + + {{ amx("extrx", 2) }} + {{ amx("fms16", 4) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + add x4, x4, {{ 0 | setting(20) }} // next Z row + {{ amx("extrx", 2) }} + {{ amx("fms16", 4) }} + + // continue + +.per_row_add: + ldr x2, [x0, #8] + + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x2], #64 + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1], #64 + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x2] + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + sub x1, x1, #64 + + orr x2, x1, {{ 0 | setting(62) }} // load a pair + {{ amx("ldy", 2) }} + + mov x2, {{ 0 | setting(63) }} // vector mode + orr x2, x2, {{ 0 | setting(29) }} // z += y + + // top left + {{ amx("fma16", 2) }} + + // bottom left + orr x2, x2, {{ 0 | setting(20) }} // Z row = 1 + orr x2, x2, {{ 0 | setting(6) }} // Y offset + {{ amx("fma16", 2) }} + + b .non_linear_loop + +.per_row_min: + mov x2, 5 + b .per_row_min_max +.per_row_max: + mov x2, 7 +.per_row_min_max: + ldr x5, [x0, #8] + + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x5], #64 + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1], #64 + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x5] + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + sub x1, x1, #64 + + orr x5, x1, {{ 0 | setting(62) }} // load a pair + {{ amx("ldx", 5) }} + + lsl x2, x2, 47 // max(x,z) (or min) + orr x2, x2, {{ 0 | setting(44) }} // f32 + {{ amx("vecfp", 2) }} + + orr x2, x2, {{ 0 | setting(16) }} // x1 + orr x2, x2, {{ 0 | setting(20) }} // z1 + {{ amx("vecfp", 2) }} + + b .non_linear_loop + +.per_col_min: + mov x2, 5 + b .per_col_min_max +.per_col_max: + mov x2, 7 +.per_col_min_max: + ldr x4, [x0, #8] + + // broadcast value to x0 + ld1 { v0.h }[0], [x4] + dup v0.8h, v0.h[0] + st1 { v0.8h }, [x1], #16 + st1 { v0.8h }, [x1], #16 + st1 { v0.8h }, [x1], #16 + st1 { v0.8h }, [x1], #16 + sub x1, x1, #64 + + {{ amx("ldx", 1) }} + + lsl x2, x2, 47 // max(x,z) (or min) + orr x2, x2, {{ 0 | setting(43) }} // f32 + + {{ amx("vecfp", 2) }} + orr x2, x2, {{ 0 | setting(20) }} // z offset + {{ amx("vecfp", 2) }} + + b .non_linear_loop + +.per_col_mul: + ldr x4, [x0, #8] + + // broadcast value to y0 + ld1 { v0.h }[0], [x4] + dup v0.8h, v0.h[0] + st1 { v0.8h }, [x1], #16 + st1 { v0.8h }, [x1], #16 + st1 { v0.8h }, [x1], #16 + st1 { v0.8h }, [x1], #16 + sub x1, x1, #64 + + {{ amx("ldy", 1) }} + + eor x2, x2, x2 // X[0] = Z[0] + {{ amx("extrx", 2) }} + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(27) }} // Z=X*Y + {{ amx("fma16", 4) }} + orr x2, x2, {{ 0 | setting(20) }} // Z1 + {{ amx("extrx", 2) }} + orr x4, x4, {{ 0 | setting(20) }} // Z1 + {{ amx("fma16", 4) }} + + b .non_linear_loop + +.per_row_mul: + ldr x2, [x0, #8] + + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x2], #64 + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1], #64 + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x2] + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + sub x1, x1, #64 + + orr x2, x1, {{ 0 | setting(62) }} // pair + {{ amx("ldy", 2) }} + + eor x2, x2, x2 // X[0] = Z[0] + {{ amx("extrx", 2) }} + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(27) }} // Z=X*Y + {{ amx("fma16", 4) }} + orr x2, x2, {{ 0 | setting(20) }} // Z1 + {{ amx("extrx", 2) }} + orr x4, x4, {{ 0 | setting(20) }} // Z1 + orr x4, x4, {{ 0 | setting(6) }} // Y1 + {{ amx("fma16", 4) }} + + b .non_linear_loop + +.scalar_sub: + // performs a unary neg on Z, then go to scalar_add + eor x2, x2, x2 // X[0] = Z[0] + + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(28) }} + orr x4, x4, {{ 0 | setting(27) }} // Z=-X + {{ amx("extrx", 2) }} + {{ amx("fms16", 4) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + add x4, x4, {{ 0 | setting(20) }} // next Z row + {{ amx("extrx", 2) }} + {{ amx("fms16", 4) }} + + // continue on purpose + +.scalar_add: + ldr w5, [x0, #8] + + fmov h0, w5 + dup v0.8h, v0.h[0] + dup v1.8h, v0.h[0] + dup v2.8h, v0.h[0] + dup v3.8h, v0.h[0] + + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + {{ amx("ldx", 1) }} // load 16 values + + mov x2, {{ 0 | setting(28) }} // Z+=X + {{ amx("fma16", 2) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + {{ amx("fma16", 2) }} + b .non_linear_loop + +.scalar_sub_flipped: + ldr w5, [x0, #8] + fmov s0, w5 + dup v0.8h, v0.h[0] + dup v1.8h, v0.h[0] + dup v2.8h, v0.h[0] + dup v3.8h, v0.h[0] + + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + {{ amx("ldx", 1) }} // load 16 values + + mov x2, {{ 0 | setting(28) }} // Z-=X + {{ amx("fms16", 2) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + {{ amx("fms16", 2) }} + b .non_linear_loop + +.scalar_mul: + ldr w5, [x0, #8] + fmov h0, w5 + dup v0.8h, v0.h[0] + dup v1.8h, v0.h[0] + dup v2.8h, v0.h[0] + dup v3.8h, v0.h[0] + + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + {{ amx("ldy", 1) }} + + eor x2, x2, x2 // X[0] = Z[0] + + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(27) }} // Z=X*Y + + {{ amx("extrx", 2) }} + {{ amx("fma16", 4) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + add x4, x4, {{ 0 | setting(20) }} // next Z row + {{ amx("extrx", 2) }} + {{ amx("fma16", 4) }} + + b .non_linear_loop + +.scalar_min: + mov x2, 5 + b .scalar_min_max +.scalar_max: + mov x2, 7 +.scalar_min_max: + ldr w5, [x0, #8] + fmov h0, w5 + dup v0.8h, v0.h[0] + dup v1.8h, v0.h[0] + dup v2.8h, v0.h[0] + dup v3.8h, v0.h[0] + + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + {{ amx("ldx", 1) }} // load 16 values + + lsl x2, x2, 47 + orr x2, x2, {{ 0 | setting(43) }} // f16 + + {{ amx("vecfp", 2) }} + add x2, x2, {{ 0 | setting(20) }} // next Z + {{ amx("vecfp", 2) }} + + b .non_linear_loop + +.add_unicast: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + {% for neon in range(0, 8) %} + {% for lane in range(0, 8) %} + ld1 { v{{neon}}.h }[{{lane}}], [x5], x6 + {% endfor %} + {% endfor %} + mov x8, x1 + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x8], #64 + st1 { v4.8h, v5.8h, v6.8h, v7.8h }, [x8], #64 + + orr x8, x1, {{ 0 | setting(62) }} // pair + {{ amx("ldy", 8) }} + + eor x2, x2, x2 + orr x2, x2, {{ 0 | setting(63) }} // vector mode + orr x2, x2, {{ 0 | setting(29) }} // perform Z0+=Y0 + {{ amx("fma16", 2) }} + orr x2, x2, {{ 0 | setting(20) }} // Z1 + orr x2, x2, 64 // offset Y + {{ amx("fma16", 2) }} + + b .non_linear_loop + +.add_row_col_products: + ldp x5, x6, [x0, #8] // a base ptr, b base ptr + + ld1 { v0.h }[0], [x6] + st1 { v0.h }[0], [x1] + {{ amx("ldy", 1) }} + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x5], #64 + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1], #64 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x5] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + sub x1, x1, #64 + + orr x2, x1, {{ 0 | setting(62) }} // load a pair + {{ amx("ldx", 2) }} + + mov x2, {{ 0 | setting(43) }} // f16 + orr x2, x2, {{ 0 | setting(38) }} // Broadcast Y + {{ amx("vecfp", 2) }} + + orr x2, x2, {{ 0 | setting(20) }} // Z row = 1 + orr x2, x2, {{ 0 | setting(16) }} // X offset + {{ amx("vecfp", 2) }} + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + ands x8, x5, 0x7f + bne .store_generic + cmp x6, 4 + bne .store_generic + cmp x7, 4 + bne .store_generic + + orr x5, x5, {{ 0 | setting(62) }} // pair + {{ amx("stz", 5) }} + b .non_linear_loop + + .store_generic: + + orr x8, x1, {{ 0 | setting(62) }} // pair + {{ amx("stz", 8) }} + + mov x8, x1 + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x8], #64 + ld1 { v4.8h, v5.8h, v6.8h, v7.8h }, [x8], #64 + {% for neon in range(0, 8) %} + {% for lane in range(0, 8) %} + st1 { v{{neon}}.h }[{{lane}}], [x5], x6 + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.load_tile: + ldr x2, [x0, #16] // row major ptr + orr x2, x2, {{ 0 | setting(62) }} // load pairs + {{ amx("ldz", 2) }} + b .non_linear_loop + +.return: +{{ AMX_CLR }} +ret diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/apple_amx/apple_amx_mmm_f16_64x32.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/apple_amx/apple_amx_mmm_f16_64x32.S.j2 new file mode 100644 index 0000000..8414946 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/apple_amx/apple_amx_mmm_f16_64x32.S.j2 @@ -0,0 +1,658 @@ +// vim: ft=arm +.text +.align 4 + +/* Z: 64x32 tile. each Z reg is f16x32 + Z0 + Z2 + ... + Z62 + + Z1 + Z3 + S63 +*/ + + +.global {{G}}apple_amx_mmm_f16_64x32_{{suffix}} +{{G}}apple_amx_mmm_f16_64x32_{{suffix}}: + +{{ AMX_SET }} + + // set x1 to a 128 bytes aligned block for loads + mov x1, sp + lsr x1, x1, #7 + lsl x1, x1, #7 + sub x1, x1, 128 + +{% include "dispatcher.j2" %} + +.leaky_relu: +.q_scale: +.q_shl: +.q_shr: + b .unsupported + +.add_mat_mul: + + ldr x2, [x0, #24] // b + ldp x3, x4, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + + orr x4, x4, {{ 0 | setting(62) }} // load pairs (A) + + eor x5, x5, x5 // top left + + orr x7, x5, {{ 0 | setting(20) }} + orr x7, x7, {{ 0 | setting(6) }} // bottom left + + .packed_packed_loop_1: + {{ amx("ldx", 2) }} + {{ amx("ldy", 4) }} + add x2, x2, 64 + add x4, x4, 128 + + {{ amx("fma16", 5) }} + {{ amx("fma16", 7) }} + + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +.clear: + // top left + eor x2, x2, x2 + orr x2, x2, {{ 0 | setting(27) }} + orr x2, x2, {{ 0 | setting(28) }} + orr x2, x2, {{ 0 | setting(29) }} // Z = 0 + {{ amx("fma32", 2) }} + + // top right + orr x2, x2, {{ 0 | setting(20) }} // Z row = 1 + {{ amx("fma32", 2) }} + + // bottom right + orr x2, x2, {{ 0 | setting(21) }} // Z row = 3 + {{ amx("fma32", 2) }} + + // bottom left + eor x2, x2, {{ 0 | setting(20) }} // Z row = 2 + {{ amx("fma32", 2) }} + + mov x3, #16 + str x3, [x1] + + b .non_linear_loop + +.per_col_sub: + + // performs a unary neg on Z + eor x2, x2, x2 // X[0] = Z[0] + + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(28) }} + orr x4, x4, {{ 0 | setting(27) }} // Z=-X + + mov x6, 64 + .per_col_sub_loop: + {{ amx("extrx", 2) }} + {{ amx("fms16", 4) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + add x4, x4, {{ 0 | setting(20) }} // next Z row + subs x6, x6, 1 + bne .per_col_sub_loop + + // continue + +.per_col_add: + ldr x2, [x0, #8] + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x2] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + {{ amx("ldx", 1) }} + + mov x2, {{ 0 | setting(28) }} // z += y + + // top left + {{ amx("fma16", 2) }} + + // bottom left + orr x2, x2, {{ 0 | setting(20) }} // Z row = 2 + {{ amx("fma16", 2) }} + + b .non_linear_loop + +.per_col_sub_flipped: + ldr x2, [x0, #8] + + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x2] + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + + {{ amx("ldx", 1) }} + + mov x2, {{ 0 | setting(28) }} // z += y + + {{ amx("fms16", 2) }} + orr x2, x2, {{ 0 | setting(20) }} // Z row = 1 + {{ amx("fms16", 2) }} + + b .non_linear_loop + +.per_row_sub_flipped: + ldr x2, [x0, #8] + + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x2], #64 + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1], #64 + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x2] + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + sub x1, x1, #64 + + orr x2, x1, {{ 0 | setting(62) }} // load a pair + {{ amx("ldy", 2) }} + + mov x2, {{ 0 | setting(29) }} // z += y + + // top left + {{ amx("fms16", 2) }} + + // bottom right + orr x2, x2, {{ 0 | setting(20) }} // Z row = 3 + orr x2, x2, {{ 0 | setting(6) }} // Y offset + {{ amx("fms16", 2) }} + + b .non_linear_loop + +.per_row_sub: + // performs a unary neg on Z + eor x2, x2, x2 // X[0] = Z[0] + + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(28) }} + orr x4, x4, {{ 0 | setting(27) }} // Z=-X + + mov x6, 64 + .per_row_sub_loop: + {{ amx("extrx", 2) }} + {{ amx("fms16", 4) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + add x4, x4, {{ 0 | setting(20) }} // next Z row + subs x6, x6, 1 + bne .per_row_sub_loop + + // continue + +.per_row_add: + ldr x2, [x0, #8] + + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x2], #64 + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1], #64 + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x2] + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + sub x1, x1, #64 + + orr x2, x1, {{ 0 | setting(62) }} // load a pair + {{ amx("ldy", 2) }} + + mov x2, {{ 0 | setting(29) }} // z += y + + // top left + {{ amx("fma16", 2) }} + + // bottom right + orr x2, x2, {{ 0 | setting(20) }} // Z row = 1 + orr x2, x2, {{ 0 | setting(6) }} // Y offset + {{ amx("fma16", 2) }} + + b .non_linear_loop + +.per_row_min: + mov x2, 5 + b .per_row_min_max +.per_row_max: + mov x2, 7 +.per_row_min_max: + ldr x5, [x0, #8] + + add x6, x5, 64 + + lsl x2, x2, 47 // max(x,z) (or min) + orr x2, x2, {{ 0 | setting(43) }} // f16 + + orr x8, x2, {{ 0 | setting(20) }} // bottom left + + mov x4, 32 + .loop_per_row_max: + // top half + ld1 { v0.h }[0], [x5], #2 + dup v0.8h, v0.h[0] + dup v1.8h, v0.h[0] + dup v2.8h, v0.h[0] + dup v3.8h, v0.h[0] + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + + {{ amx("ldx", 1) }} + {{ amx("vecfp", 2) }} + + add x2, x2, {{ 0 | setting(21) }} + + // bottom half + ld1 { v0.h }[0], [x6], #2 + dup v0.8h, v0.h[0] + dup v1.8h, v0.h[0] + dup v2.8h, v0.h[0] + dup v3.8h, v0.h[0] + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + + {{ amx("ldx", 1) }} + {{ amx("vecfp", 8) }} + + add x8, x8, {{ 0 | setting(21) }} + + subs x4, x4, 1 + bne .loop_per_row_max + + b .non_linear_loop + +.per_col_min: + mov x2, 5 + b .per_col_min_max +.per_col_max: + mov x2, 7 +.per_col_min_max: + ldr x4, [x0, #8] + + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x4] + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + {{ amx("ldx", 1) }} + + lsl x2, x2, 47 // max(x,z) (or min) + orr x2, x2, {{ 0 | setting(43) }} // f16 + + mov x4, 64 + .loop_per_col_max: + {{ amx("vecfp", 2) }} + add x2, x2, {{ 0 | setting(20) }} + subs x4, x4, 1 + bne .loop_per_col_max + + b .non_linear_loop + +.per_col_mul: + ldr x4, [x0, #8] + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x4] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + {{ amx("ldy", 1) }} + + eor x2, x2, x2 // X[0] = Z[0] + + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(27) }} // Z=X*Y + + mov x6, 64 + .loop_per_col_mul: + {{ amx("extrx", 2) }} + {{ amx("fma16", 4) }} + add x2, x2, {{ 0 | setting(20) }} + add x4, x4, {{ 0 | setting(20) }} + subs x6, x6, 1 + bne .loop_per_col_mul + + b .non_linear_loop + +.per_row_mul: + ldr x14, [x0, #8] + add x15, x14, 64 + + // extrx + eor x2, x2, x2 // X[0] = Z[0] (top left) + + eor x4, x4, x4 + orr x4, x4, {{ 0 | setting(20) }} // X[0] = Z[1] (bottom left) + + // fma16 + eor x6, x6, x6 + orr x6, x6, {{ 0 | setting(63) }} // vector mode + orr x6, x6, {{ 0 | setting(27) }} // Z=X*Y Z[0]=X[0]*Y[0] + + orr x8, x6, {{ 0 | setting(20) }} // Z[1] + + mov x10, 32 + .loop_per_row_mul: + // top + ld1 { v0.h }[0], [x14], #2 + dup v0.8h, v0.h[0] + dup v1.8h, v0.h[0] + dup v2.8h, v0.h[0] + dup v3.8h, v0.h[0] + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + + {{ amx("ldy", 1) }} + {{ amx("extrx", 2) }} + {{ amx("fma16", 6) }} + + add x2, x2, {{ 0 | setting(21) }} + add x6, x6, {{ 0 | setting(21) }} + + // bottom + ld1 { v0.h }[0], [x15], #2 + dup v0.8h, v0.h[0] + dup v1.8h, v0.h[0] + dup v2.8h, v0.h[0] + dup v3.8h, v0.h[0] + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + + {{ amx("ldy", 1) }} + {{ amx("extrx", 4) }} + {{ amx("fma16", 8) }} + + add x4, x4, {{ 0 | setting(21) }} + add x8, x8, {{ 0 | setting(21) }} + + subs x10, x10, 1 + bne .loop_per_row_mul + + b .non_linear_loop + +.scalar_sub: + // performs a unary neg on Z, then go to scalar_add + eor x2, x2, x2 // X[0] = Z[0] + + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(28) }} + orr x4, x4, {{ 0 | setting(27) }} // Z=-X + + mov x6, 64 + .scalar_sub_loop: + {{ amx("extrx", 2) }} + {{ amx("fms16", 4) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + add x4, x4, {{ 0 | setting(20) }} // next Z row + subs x6, x6, 1 + bne .scalar_sub_loop + + // continue on purpose + +.scalar_add: + ldr w5, [x0, #8] + + fmov h0, w5 + dup v0.8h, v0.h[0] + dup v1.8h, v0.h[0] + dup v2.8h, v0.h[0] + dup v3.8h, v0.h[0] + + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + {{ amx("ldx", 1) }} // load 16 values + + mov x2, {{ 0 | setting(28) }} // Z+=X + {{ amx("fma16", 2) }} + add x2, x2, {{ 0 | setting(20) }} // Z1 + {{ amx("fma16", 2) }} + b .non_linear_loop + +.scalar_sub_flipped: + ldr w5, [x0, #8] + + fmov h0, w5 + dup v0.8h, v0.h[0] + dup v1.8h, v0.h[0] + dup v2.8h, v0.h[0] + dup v3.8h, v0.h[0] + + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + {{ amx("ldx", 1) }} // load 32 values + + mov x2, {{ 0 | setting(28) }} // Z-=X + {{ amx("fms16", 2) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + {{ amx("fms16", 2) }} + b .non_linear_loop + +.scalar_mul: + ldr w5, [x0, #8] + + fmov h0, w5 + dup v0.8h, v0.h[0] + dup v1.8h, v0.h[0] + dup v2.8h, v0.h[0] + dup v3.8h, v0.h[0] + + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + {{ amx("ldy", 1) }} // load 32 values + + eor x2, x2, x2 // X[0] = Z[0] + + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(27) }} // Z=X*Y + + mov x6, 64 + .scalar_mul_loop: + {{ amx("extrx", 2) }} + {{ amx("fma16", 4) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + add x4, x4, {{ 0 | setting(20) }} // next Z row + subs x6, x6, 1 + bne .scalar_mul_loop + + b .non_linear_loop + +.scalar_min: + mov x2, 5 + b .scalar_min_max +.scalar_max: + mov x2, 7 +.scalar_min_max: + ldr w5, [x0, #8] + + fmov h0, w5 + dup v0.8h, v0.h[0] + dup v1.8h, v0.h[0] + dup v2.8h, v0.h[0] + dup v3.8h, v0.h[0] + + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + {{ amx("ldx", 1) }} // load 16 values + + lsl x2, x2, 47 + orr x2, x2, {{ 0 | setting(43) }} // f32 + + mov x3, 64 + .loop_scalar_max: + add x2, x2, {{ 0 | setting(20) }} // next Z + {{ amx("vecfp", 2) }} + subs x3, x3, 1 + bne .loop_scalar_max + + b .non_linear_loop + +.add_unicast: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + mov x3, 0 // x3 is the row + .loop_load: + // z reg is (row % 32) * 2 + (row / 32) + and x9, x3, 0x1f + lsl x9, x9, 1 + lsr x10, x3, 5 + add x9, x9, x10 + + mov x4, x5 + {% for neon in range(0, 4) %} + {% for lane in range(0, 8) %} + ld1 { v{{neon}}.h }[{{lane}}], [x4], x7 + {% endfor %} + {% endfor %} + + st1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + {{ amx("ldy", 1) }} + + lsl x2, x9, 20 // Z register to update + orr x2, x2, {{ 0 | setting(63) }} // vector mode + orr x2, x2, {{ 0 | setting(29) }} // perform Z+=Y + {{ amx("fma16", 2) }} + + add x5, x5, x6 + add x3, x3, 1 + cmp x3, 64 + bne .loop_load + + /* + mov x3, 0 // x3 is the row + .loop_load: + and x9, x3, 0xf // x9 = row % 16 + lsl x9, x9, 2 // x9 = (row % 16) * 4 + lsr x10, x3, 4 // x10 = row / 16 + lsl x10, x10, 1 // x10 = (row / 16) * 2 + add x9, x9, x10 // x9 = x9 + x10 + + mov x4, x5 + {% for neon in range(0, 4) %} + {% for lane in range(0, 4) %} + ld1 { v{{neon}}.s }[{{lane}}], [x4], x7 + {% endfor %} + {% endfor %} + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + {% for neon in range(0, 4) %} + {% for lane in range(0, 4) %} + ld1 { v{{neon}}.s }[{{lane}}], [x4], x7 + {% endfor %} + {% endfor %} + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x8] + + mov x2, x1 + orr x2, x2, {{ 0 | setting(62) }} // load 32 values + {{ amx("ldy", 2) }} + + lsl x2, x9, 20 // left Z register to update + orr x2, x2, {{ 0 | setting(63) }} // vector mode + orr x2, x2, {{ 0 | setting(29) }} // perform Z+=Y + {{ amx("fma32", 2) }} + + add x2, x2, {{ 0 | setting(20) }} + orr x2, x2, 64 // offset Y by 16 values + {{ amx("fma32", 2) }} + + add x5, x5, x6 + add x3, x3, 1 + cmp x3, 32 + bne .loop_load + +*/ + + b .non_linear_loop + +.add_row_col_products: + ldp x5, x6, [x0, #8] // a base ptr, b base ptr + + add x8, x1, 64 + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x5], #64 + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1], #64 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x5] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + sub x1, x1, #64 + + orr x2, x1, {{ 0 | setting(62) }} // load a pair + {{ amx("ldy", 2) }} + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x6] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + + {{ amx("ldx", 1) }} + + // top + eor x2, x2, x2 + {{ amx("fma16", 2) }} + + // bottom right + orr x2, x2, {{ 0 | setting(20) }} // Z row = 1 + orr x2, x2, {{ 0 | setting(6) }} // Y offset + {{ amx("fma16", 2) }} + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x7, 2 + bne .store_generic + ands x8, x5, 0x7f + bne .store_generic + ands x8, x6, 0x7f + bne .store_generic + + lsl x8, x6, 5 + add x8, x8, x5 // x8 = 32*rsc + orr x8, x8, {{ 0 | setting(56) }} // first to x8 is z1 + + mov x4, {{ 0 | setting(57) }} // Zreg += 2 + add x4, x4, x6 // +rsc + + mov x3, 32 + .loop_store_direct: + {{ amx("stz", 5) }} + {{ amx("stz", 8) }} + add x5, x5, x4 + add x8, x8, x4 + subs x3, x3, 1 + bne .loop_store_direct + + b .non_linear_loop + +.store_generic: + + mov x3, 0 // row id + .loop_store: + // z reg is (row % 32) * 2 + (row / 32) + and x9, x3, 0x1f + lsl x9, x9, 1 + lsr x10, x3, 5 + add x9, x9, x10 + + lsl x2, x9, 56 + orr x2, x2, x1 + {{ amx("stz", 2) }} // f16 x 32 + + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x1] + + mov x4, x5 + {% for neon in range(0, 4) %} + {% for lane in range(0, 8) %} + st1 { v{{neon}}.h }[{{lane}}], [x4], x7 + {% endfor %} + {% endfor %} + add x5, x5, x6 + + add x3, x3, 1 + cmp x3, 64 + bne .loop_store + b .non_linear_loop + +.load_tile: + ldr x2, [x0, #16] // row major ptr + orr x3, x2, {{ 0 | setting(56) }} + add x3, x3, #2048 + + mov x4, {{ 0 | setting(57) }} // z+=2 + add x4, x4, #64 + + mov x8, 32 + .loop_load_tile: + {{ amx("ldz", 2) }} + {{ amx("ldz", 3) }} + add x2, x2, x4 + add x3, x3, x4 + subs x8, x8, 1 + bne .loop_load_tile + + b .non_linear_loop + +.return: +{{ AMX_CLR }} +ret diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/apple_amx/apple_amx_mmm_f32_32x1.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/apple_amx/apple_amx_mmm_f32_32x1.S.j2 new file mode 100644 index 0000000..98fa22c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/apple_amx/apple_amx_mmm_f32_32x1.S.j2 @@ -0,0 +1,533 @@ +// vim: ft=arm +.text +.align 4 + +/* Z: 32x1 + z0[0] .. z0[15] z1[0] .. z1[15] +*/ + + +.global {{G}}apple_amx_mmm_f32_32x1_{{suffix}} +{{G}}apple_amx_mmm_f32_32x1_{{suffix}}: + +{{ AMX_SET }} + + // set x1 to a 128 bytes aligned block for loads + mov x1, sp + lsr x1, x1, #7 + lsl x1, x1, #7 + sub x1, x1, 128 + +{% include "dispatcher.j2" %} + +.leaky_relu: +.q_scale: +.q_shl: +.q_shr: + b .unsupported + +.add_mat_mul: + + ldr x2, [x0, #24] // b + ldp x3, x4, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + + orr x4, x4, {{ 0 | setting(62) }} // load a pair of A + + mov x5, {{ 0 | setting(44) }} // f32 + orr x5, x5, {{ 0 | setting(38) }} // Broadcast Y + + orr x6, x5, {{ 0 | setting(20) }} // z offset + orr x6, x6, {{ 0 | setting(16) }} // x offset + + cmp x3, #16 + blt .packed_packed_loop_1 + + mov x9, {{ 0 | setting(32) }} // Y broadcast offset += 1 + + .packed_packed_loop_16: + mov x7, x5 + mov x8, x6 + {{ amx("ldy", 2) }} + {% for k in range(0, 16) %} + {{ amx("ldx", 4) }} + add x4, x4, 128 + {{ amx("vecfp", 7) }} + {{ amx("vecfp", 8) }} + add x7, x7, x9 + add x8, x8, x9 + {% endfor %} + add x2, x2, #64 + sub x3, x3, #16 + cmp x3, #16 + bge .packed_packed_loop_16 + + cmp x3, #0 + beq .non_linear_loop + + .packed_packed_loop_1: + ldr w7, [x2], #4 + str w7, [x1] + {{ amx("ldx", 4) }} + {{ amx("ldy", 1) }} + {{ amx("vecfp", 5) }} + {{ amx("vecfp", 6) }} + add x4, x4, 128 + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +.clear: + // top left + eor x2, x2, x2 + orr x2, x2, {{ 0 | setting(27) }} + orr x2, x2, {{ 0 | setting(28) }} + orr x2, x2, {{ 0 | setting(29) }} // Z = 0 + {{ amx("fma32", 2) }} + + // top right + orr x2, x2, {{ 0 | setting(20) }} // Z row = 1 + {{ amx("fma32", 2) }} + + // bottom right + orr x2, x2, {{ 0 | setting(21) }} // Z row = 3 + {{ amx("fma32", 2) }} + + // bottom left + eor x2, x2, {{ 0 | setting(20) }} // Z row = 2 + {{ amx("fma32", 2) }} + + b .non_linear_loop + +.per_col_sub: + + // performs a unary neg on Z + eor x2, x2, x2 // X[0] = Z[0] + // extr[hxyz] is suport confusing + + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(28) }} + orr x4, x4, {{ 0 | setting(27) }} // Z=-X + + {{ amx("extrx", 2) }} + {{ amx("fms32", 4) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + add x4, x4, {{ 0 | setting(20) }} // next Z row + {{ amx("extrx", 2) }} // extr[hxyz] is confusing + {{ amx("fms32", 4) }} + + // continue + +.per_col_add: + ldr x2, [x0, #8] + + // broadcast value to x0 + ld1 { v0.s }[0], [x2] + dup v0.4s, v0.s[0] + st1 { v0.4s }, [x1], #16 + st1 { v0.4s }, [x1], #16 + st1 { v0.4s }, [x1], #16 + st1 { v0.4s }, [x1], #16 + sub x1, x1, #64 + + {{ amx("ldx", 1) }} // load into x0 by default + + mov x2, {{ 0 | setting(28) }} // z += y + {{ amx("fma32", 2) }} + + orr x2, x2, {{ 0 | setting(20) }} // target is now z1 + {{ amx("fma32", 2) }} + + b .non_linear_loop + +.per_col_sub_flipped: + ldr x2, [x0, #8] + + // broadcast value to x0 + ld1 { v0.s }[0], [x2] + dup v0.4s, v0.s[0] + st1 { v0.4s }, [x1], #16 + st1 { v0.4s }, [x1], #16 + st1 { v0.4s }, [x1], #16 + st1 { v0.4s }, [x1], #16 + sub x1, x1, #64 + + {{ amx("ldx", 1) }} // load into x0 by default + + mov x2, {{ 0 | setting(28) }} // z += y + {{ amx("fms32", 2) }} + + orr x2, x2, {{ 0 | setting(20) }} // target is now z1 + {{ amx("fms32", 2) }} + + b .non_linear_loop + +.per_row_sub_flipped: + ldr x2, [x0, #8] + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x2], #64 + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1], #64 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x2] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + sub x1, x1, #64 + + orr x2, x1, {{ 0 | setting(62) }} // load a pair + {{ amx("ldy", 2) }} + + mov x2, {{ 0 | setting(63) }} // vector mode + orr x2, x2, {{ 0 | setting(29) }} // z -= y + + // top left + {{ amx("fms32", 2) }} + + // bottom left + orr x2, x2, {{ 0 | setting(20) }} // Z row = 1 + orr x2, x2, {{ 0 | setting(6) }} // Y offset + {{ amx("fms32", 2) }} + + b .non_linear_loop + +.per_row_sub: + // performs a unary neg on Z + eor x2, x2, x2 // X[0] = Z[0] + + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(28) }} + orr x4, x4, {{ 0 | setting(27) }} // Z=-X + + {{ amx("extrx", 2) }} + {{ amx("fms32", 4) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + add x4, x4, {{ 0 | setting(20) }} // next Z row + {{ amx("extrx", 2) }} + {{ amx("fms32", 4) }} + + // continue + +.per_row_add: + ldr x2, [x0, #8] + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x2], #64 + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1], #64 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x2] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + sub x1, x1, #64 + + orr x2, x1, {{ 0 | setting(62) }} // load a pair + {{ amx("ldy", 2) }} + + mov x2, {{ 0 | setting(63) }} // vector mode + orr x2, x2, {{ 0 | setting(29) }} // z += y + + // top left + {{ amx("fma32", 2) }} + + // bottom left + orr x2, x2, {{ 0 | setting(20) }} // Z row = 1 + orr x2, x2, {{ 0 | setting(6) }} // Y offset + {{ amx("fma32", 2) }} + + b .non_linear_loop + +.per_row_min: + mov x2, 5 + b .per_row_min_max +.per_row_max: + mov x2, 7 +.per_row_min_max: + ldr x5, [x0, #8] + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x5], #64 + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1], #64 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x5] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + sub x1, x1, #64 + + orr x5, x1, {{ 0 | setting(62) }} // load a pair + {{ amx("ldx", 5) }} + + lsl x2, x2, 47 // max(x,z) (or min) + orr x2, x2, {{ 0 | setting(44) }} // f32 + {{ amx("vecfp", 2) }} + + orr x2, x2, {{ 0 | setting(16) }} // x1 + orr x2, x2, {{ 0 | setting(20) }} // z1 + {{ amx("vecfp", 2) }} + + b .non_linear_loop + +.per_col_min: + mov x2, 5 + b .per_col_min_max +.per_col_max: + mov x2, 7 +.per_col_min_max: + ldr x4, [x0, #8] + + // broadcast value to x0 + ld1 { v0.s }[0], [x4] + dup v0.4s, v0.s[0] + st1 { v0.4s }, [x1], #16 + st1 { v0.4s }, [x1], #16 + st1 { v0.4s }, [x1], #16 + st1 { v0.4s }, [x1], #16 + sub x1, x1, #64 + + {{ amx("ldx", 1) }} + + lsl x2, x2, 47 // max(x,z) (or min) + orr x2, x2, {{ 0 | setting(44) }} // f32 + + {{ amx("vecfp", 2) }} + orr x2, x2, {{ 0 | setting(20) }} // z offset + {{ amx("vecfp", 2) }} + + b .non_linear_loop + +.per_col_mul: + ldr x4, [x0, #8] + + // broadcast value to y0 + ld1 { v0.s }[0], [x4] + dup v0.4s, v0.s[0] + st1 { v0.4s }, [x1], #16 + st1 { v0.4s }, [x1], #16 + st1 { v0.4s }, [x1], #16 + st1 { v0.4s }, [x1], #16 + sub x1, x1, #64 + + {{ amx("ldy", 1) }} + + eor x2, x2, x2 // X[0] = Z[0] + {{ amx("extrx", 2) }} + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(27) }} // Z=X*Y + {{ amx("fma32", 4) }} + orr x2, x2, {{ 0 | setting(20) }} // Z1 + {{ amx("extrx", 2) }} + orr x4, x4, {{ 0 | setting(20) }} // Z1 + {{ amx("fma32", 4) }} + + b .non_linear_loop + +.per_row_mul: + ldr x2, [x0, #8] + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x2], #64 + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1], #64 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x2] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + sub x1, x1, #64 + + orr x2, x1, {{ 0 | setting(62) }} // pair + {{ amx("ldy", 2) }} + + eor x2, x2, x2 // X[0] = Z[0] + {{ amx("extrx", 2) }} + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(27) }} // Z=X*Y + {{ amx("fma32", 4) }} + orr x2, x2, {{ 0 | setting(20) }} // Z1 + {{ amx("extrx", 2) }} + orr x4, x4, {{ 0 | setting(20) }} // Z1 + orr x4, x4, {{ 0 | setting(6) }} // Y1 + {{ amx("fma32", 4) }} + + b .non_linear_loop + +.scalar_sub: + // performs a unary neg on Z, then go to scalar_add + eor x2, x2, x2 // X[0] = Z[0] + + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(28) }} + orr x4, x4, {{ 0 | setting(27) }} // Z=-X + {{ amx("extrx", 2) }} + {{ amx("fms32", 4) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + add x4, x4, {{ 0 | setting(20) }} // next Z row + {{ amx("extrx", 2) }} + {{ amx("fms32", 4) }} + + // continue on purpose + +.scalar_add: + ldr w5, [x0, #8] + + fmov s0, w5 + dup v0.4s, v0.s[0] + dup v1.4s, v0.s[0] + dup v2.4s, v0.s[0] + dup v3.4s, v0.s[0] + + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + {{ amx("ldx", 1) }} // load 16 values + + mov x2, {{ 0 | setting(28) }} // Z+=X + {{ amx("fma32", 2) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + {{ amx("fma32", 2) }} + b .non_linear_loop + +.scalar_sub_flipped: + ldr w5, [x0, #8] + fmov s0, w5 + dup v0.4s, v0.s[0] + dup v1.4s, v0.s[0] + dup v2.4s, v0.s[0] + dup v3.4s, v0.s[0] + + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + {{ amx("ldx", 1) }} // load 16 values + + mov x2, {{ 0 | setting(28) }} // Z-=X + {{ amx("fms32", 2) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + {{ amx("fms32", 2) }} + b .non_linear_loop + +.scalar_mul: + ldr w5, [x0, #8] + fmov s0, w5 + dup v0.4s, v0.s[0] + dup v1.4s, v0.s[0] + dup v2.4s, v0.s[0] + dup v3.4s, v0.s[0] + + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + {{ amx("ldy", 1) }} // load 16 values + + eor x2, x2, x2 // X[0] = Z[0] + + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(27) }} // Z=X*Y + + {{ amx("extrx", 2) }} + {{ amx("fma32", 4) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + add x4, x4, {{ 0 | setting(20) }} // next Z row + {{ amx("extrx", 2) }} + {{ amx("fma32", 4) }} + + b .non_linear_loop + +.scalar_min: + mov x2, 5 + b .scalar_min_max +.scalar_max: + mov x2, 7 +.scalar_min_max: + ldr w5, [x0, #8] + fmov s0, w5 + dup v0.4s, v0.s[0] + dup v1.4s, v0.s[0] + dup v2.4s, v0.s[0] + dup v3.4s, v0.s[0] + + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + {{ amx("ldx", 1) }} // load 16 values + + lsl x2, x2, 47 + orr x2, x2, {{ 0 | setting(44) }} // f32 + + {{ amx("vecfp", 2) }} + add x2, x2, {{ 0 | setting(20) }} // next Z + {{ amx("vecfp", 2) }} + + b .non_linear_loop + +.add_unicast: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + {% for neon in range(0, 8) %} + {% for lane in range(0, 4) %} + ld1 { v{{neon}}.s }[{{lane}}], [x5], x6 + {% endfor %} + {% endfor %} + mov x8, x1 + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x8], #64 + st1 { v4.4s, v5.4s, v6.4s, v7.4s }, [x8], #64 + + orr x8, x1, {{ 0 | setting(62) }} // pair + {{ amx("ldy", 8) }} + + eor x2, x2, x2 + orr x2, x2, {{ 0 | setting(63) }} // vector mode + orr x2, x2, {{ 0 | setting(29) }} // perform Z0+=Y0 + {{ amx("fma32", 2) }} + orr x2, x2, {{ 0 | setting(20) }} // Z1 + orr x2, x2, 64 // offset Y by 16 values + {{ amx("fma32", 2) }} + + b .non_linear_loop + +.add_row_col_products: + ldp x5, x6, [x0, #8] // a base ptr, b base ptr + + ld1 { v0.s }[0], [x6] + st1 { v0.s }[0], [x1] + {{ amx("ldy", 1) }} + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x5], #64 + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1], #64 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x5] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + sub x1, x1, #64 + + orr x2, x1, {{ 0 | setting(62) }} // load a pair + {{ amx("ldx", 2) }} + + mov x2, {{ 0 | setting(44) }} // f32 + orr x2, x2, {{ 0 | setting(38) }} // Broadcast Y + {{ amx("vecfp", 2) }} + + orr x2, x2, {{ 0 | setting(20) }} // Z row = 1 + orr x2, x2, {{ 0 | setting(16) }} // X offset + {{ amx("vecfp", 2) }} + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + ands x8, x5, 0x7f + bne .store_generic + cmp x6, 4 + bne .store_generic + cmp x7, 4 + bne .store_generic + + orr x5, x5, {{ 0 | setting(62) }} // pair + {{ amx("stz", 5) }} + b .non_linear_loop + + .store_generic: + + orr x8, x1, {{ 0 | setting(62) }} // pair + {{ amx("stz", 8) }} + + mov x8, x1 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x8], #64 + ld1 { v4.4s, v5.4s, v6.4s, v7.4s }, [x8], #64 + {% for neon in range(0, 8) %} + {% for lane in range(0, 4) %} + st1 { v{{neon}}.s }[{{lane}}], [x5], x6 + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.load_tile: + ldr x2, [x0, #16] // row major ptr + orr x2, x2, {{ 0 | setting(62) }} // load pairs + {{ amx("ldz", 2) }} + b .non_linear_loop + +.return: +{{ AMX_CLR }} +ret diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/apple_amx/apple_amx_mmm_f32_32x32.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/apple_amx/apple_amx_mmm_f32_32x32.S.j2 new file mode 100644 index 0000000..df63d47 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/apple_amx/apple_amx_mmm_f32_32x32.S.j2 @@ -0,0 +1,764 @@ +// vim: ft=arm +.text +.align 4 + +/* Z: 32x32 + z0[0] .. z0[15] z1[0] .. z1[15] + z4[0] .. z4[15] z5[0] .. z5[15] + .. +z60[0] .. z60[15] z61[0] .. z61[15] + + z2[0] .. z2[15] z3[0] .. z3[15] + z5[0] .. z5[15] z6[0] .. z6[15] + .. +z62[0] .. z62[15] z63[0] .. z63[15] +*/ + + +.global {{G}}apple_amx_mmm_f32_32x32_{{suffix}} +{{G}}apple_amx_mmm_f32_32x32_{{suffix}}: + +{{ AMX_SET }} + + // set x1 to a 128 bytes aligned block for loads + mov x1, sp + lsr x1, x1, #7 + lsl x1, x1, #7 + sub x1, x1, 128 + +{% include "dispatcher.j2" %} + +.leaky_relu: +.q_scale: +.q_shl: +.q_shr: + b .unsupported + +.add_mat_mul: + + ldr x2, [x0, #24] // b + ldp x3, x4, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + + orr x4, x4, {{ 0 | setting(62) }} // load pairs (A) + orr x2, x2, {{ 0 | setting(62) }} // load pairs (B) + + eor x5, x5, x5 // top left + + orr x6, x5, {{ 0 | setting(20) }} // Z row = 1 + orr x6, x6, {{ 0 | setting(16) }} // top right + + orr x7, x5, {{ 0 | setting(21) }} + orr x7, x7, {{ 0 | setting(6) }} // bottom left + + orr x8, x7, x6 // bottom right + + .packed_packed_loop_1: + {{ amx("ldx", 2) }} + {{ amx("ldy", 4) }} + add x2, x2, 128 + add x4, x4, 128 + + {{ amx("fma32", 5) }} + {{ amx("fma32", 6) }} + {{ amx("fma32", 7) }} + {{ amx("fma32", 8) }} + + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +.clear: + // top left + eor x2, x2, x2 + orr x2, x2, {{ 0 | setting(27) }} + orr x2, x2, {{ 0 | setting(28) }} + orr x2, x2, {{ 0 | setting(29) }} // Z = 0 + {{ amx("fma32", 2) }} + + // top right + orr x2, x2, {{ 0 | setting(20) }} // Z row = 1 + {{ amx("fma32", 2) }} + + // bottom right + orr x2, x2, {{ 0 | setting(21) }} // Z row = 3 + {{ amx("fma32", 2) }} + + // bottom left + eor x2, x2, {{ 0 | setting(20) }} // Z row = 2 + {{ amx("fma32", 2) }} + + b .non_linear_loop + +.per_col_sub: + + // performs a unary neg on Z + eor x2, x2, x2 // X[0] = Z[0] + + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(28) }} + orr x4, x4, {{ 0 | setting(27) }} // Z=-X + + mov x6, 64 + .per_col_sub_loop: + {{ amx("extrx", 2) }} + {{ amx("fms32", 4) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + add x4, x4, {{ 0 | setting(20) }} // next Z row + subs x6, x6, 1 + bne .per_col_sub_loop + + // continue + +.per_col_add: + ldr x2, [x0, #8] + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x2], #64 + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1], #64 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x2] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + sub x1, x1, #64 + + orr x1, x1, {{ 0 | setting(62) }} // load a pair + {{ amx("ldx", 1) }} + + mov x2, {{ 0 | setting(28) }} // z += y + + // top left + {{ amx("fma32", 2) }} + + // bottom left + orr x2, x2, {{ 0 | setting(21) }} // Z row = 2 + {{ amx("fma32", 2) }} + + // bottom right + orr x2, x2, {{ 0 | setting(16) }} // X offset + orr x2, x2, {{ 0 | setting(20) }} // Z row = 3 + {{ amx("fma32", 2) }} + + // top right + eor x2, x2, {{ 0 | setting(21) }} // Z row = 1 + {{ amx("fma32", 2) }} + + b .non_linear_loop + +.per_col_sub_flipped: + ldr x2, [x0, #8] + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x2], #64 + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1], #64 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x2] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + sub x1, x1, #64 + + orr x1, x1, {{ 0 | setting(62) }} // load a pair + {{ amx("ldx", 1) }} + + mov x2, {{ 0 | setting(28) }} // z += y + + // top left + {{ amx("fms32", 2) }} + + // bottom left + orr x2, x2, {{ 0 | setting(21) }} // Z row = 2 + {{ amx("fms32", 2) }} + + // bottom right + orr x2, x2, {{ 0 | setting(16) }} // X offset + orr x2, x2, {{ 0 | setting(20) }} // Z row = 3 + {{ amx("fms32", 2) }} + + // top right + eor x2, x2, {{ 0 | setting(21) }} // Z row = 1 + {{ amx("fms32", 2) }} + + + b .non_linear_loop + +.per_row_sub_flipped: + ldr x2, [x0, #8] + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x2], #64 + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1], #64 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x2] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + sub x1, x1, #64 + + orr x2, x1, {{ 0 | setting(62) }} // load a pair + {{ amx("ldy", 2) }} + + mov x2, {{ 0 | setting(29) }} // z += y + + // top left + {{ amx("fms32", 2) }} + + // top right + orr x2, x2, {{ 0 | setting(20) }} // Z row = 1 + {{ amx("fms32", 2) }} + + // bottom right + orr x2, x2, {{ 0 | setting(21) }} // Z row = 3 + orr x2, x2, {{ 0 | setting(6) }} // Y offset + {{ amx("fms32", 2) }} + + // bottom left + eor x2, x2, {{ 0 | setting(20) }} // Z row = 2 + {{ amx("fms32", 2) }} + + b .non_linear_loop + +.per_row_sub: + // performs a unary neg on Z + eor x2, x2, x2 // X[0] = Z[0] + + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(28) }} + orr x4, x4, {{ 0 | setting(27) }} // Z=-X + + mov x6, 64 + .per_row_sub_loop: + {{ amx("extrx", 2) }} + {{ amx("fms32", 4) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + add x4, x4, {{ 0 | setting(20) }} // next Z row + subs x6, x6, 1 + bne .per_row_sub_loop + + // continue + +.per_row_add: + ldr x2, [x0, #8] + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x2], #64 + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1], #64 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x2] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + sub x1, x1, #64 + + orr x2, x1, {{ 0 | setting(62) }} // load a pair + {{ amx("ldy", 2) }} + + mov x2, {{ 0 | setting(29) }} // z += y + + // top left + {{ amx("fma32", 2) }} + + // top right + orr x2, x2, {{ 0 | setting(20) }} // Z row = 1 + {{ amx("fma32", 2) }} + + // bottom right + orr x2, x2, {{ 0 | setting(21) }} // Z row = 3 + orr x2, x2, {{ 0 | setting(6) }} // Y offset + {{ amx("fma32", 2) }} + + // bottom left + eor x2, x2, {{ 0 | setting(20) }} // Z row = 2 + {{ amx("fma32", 2) }} + + b .non_linear_loop + +.per_row_min: + mov x2, 5 + b .per_row_min_max +.per_row_max: + mov x2, 7 +.per_row_min_max: + ldr x5, [x0, #8] + + add x6, x5, 64 + + lsl x2, x2, 47 // max(x,z) (or min) + orr x2, x2, {{ 0 | setting(44) }} // f32 + orr x3, x2, {{ 0 | setting(20) }} // right half: z offset + + orr x8, x2, {{ 0 | setting(21) }} // bottom left + orr x9, x3, {{ 0 | setting(21) }} // bottom right + + mov x4, 16 + .loop_per_row_max: + // top half + ld1 { v0.s }[0], [x5], #4 + dup v0.4s, v0.s[0] + dup v1.4s, v0.s[0] + dup v2.4s, v0.s[0] + dup v3.4s, v0.s[0] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + + {{ amx("ldx", 1) }} + {{ amx("vecfp", 2) }} + {{ amx("vecfp", 3) }} + + add x2, x2, {{ 0 | setting(22) }} + add x3, x3, {{ 0 | setting(22) }} + + // bottom half + ld1 { v0.s }[0], [x6], #4 + dup v0.4s, v0.s[0] + dup v1.4s, v0.s[0] + dup v2.4s, v0.s[0] + dup v3.4s, v0.s[0] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + + {{ amx("ldx", 1) }} + {{ amx("vecfp", 8) }} + {{ amx("vecfp", 9) }} + + add x8, x8, {{ 0 | setting(22) }} + add x9, x9, {{ 0 | setting(22) }} + + subs x4, x4, 1 + bne .loop_per_row_max + + b .non_linear_loop + +.per_col_min: + mov x2, 5 + b .per_col_min_max +.per_col_max: + mov x2, 7 +.per_col_min_max: + ldr x4, [x0, #8] + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x4], #64 + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1], #64 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x4] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + sub x1, x1, #64 + + orr x3, x1, {{ 0 | setting(62) }} // load a pair + {{ amx("ldx", 3) }} + + lsl x2, x2, 47 // max(x,z) (or min) + orr x2, x2, {{ 0 | setting(44) }} // f32 + + orr x3, x2, {{ 0 | setting(16) }} // right half: x offset + orr x3, x3, {{ 0 | setting(20) }} // right half: z offset + + mov x4, 32 + .loop_per_col_max: + {{ amx("vecfp", 2) }} + {{ amx("vecfp", 3) }} + add x2, x2, {{ 0 | setting(21) }} + add x3, x3, {{ 0 | setting(21) }} + subs x4, x4, 1 + bne .loop_per_col_max + + b .non_linear_loop + +.per_col_mul: + ldr x4, [x0, #8] + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x4], #64 + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1], #64 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x4] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + sub x1, x1, #64 + + orr x2, x1, {{ 0 | setting(62) }} // load a pair + {{ amx("ldy", 2) }} + + eor x2, x2, x2 // X[0] = Z[0] + + eor x3, x3, x3 + orr x3, x3, {{ 0 | setting(20) }} // Z[1] + orr x3, x3, {{ 0 | setting(16) }} // X[1] + + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(27) }} // Z=X*Y + + mov x5, {{ 0 | setting(63) }} // vector mode + orr x5, x5, {{ 0 | setting(27) }} // Z=X*Y + orr x5, x5, {{ 0 | setting(20) }} // Z right + orr x5, x5, {{ 0 | setting(16) }} // X[1] (right) + orr x5, x5, {{ 0 | setting(6) }} // Y[1] (right) + + mov x6, 32 + .loop_per_col_mul: + {{ amx("extrx", 2) }} + {{ amx("extrx", 3) }} + {{ amx("fma32", 4) }} + {{ amx("fma32", 5) }} + add x2, x2, {{ 0 | setting(21) }} + add x3, x3, {{ 0 | setting(21) }} + add x4, x4, {{ 0 | setting(21) }} + add x5, x5, {{ 0 | setting(21) }} + subs x6, x6, 1 + bne .loop_per_col_mul + + b .non_linear_loop + +.per_row_mul: + ldr x14, [x0, #8] + add x15, x14, 64 + + // extrx + eor x2, x2, x2 // X[0] = Z[0] (top left) + + eor x3, x3, x3 + orr x3, x3, {{ 0 | setting(20) }} // Z[1] + orr x3, x3, {{ 0 | setting(16) }} // X[1] = Z[1] (top right) + + eor x4, x4, x4 + orr x4, x4, {{ 0 | setting(21) }} // X[0] = Z[2] (bottom left) + + orr x5, x4, {{ 0 | setting(20) }} + orr x5, x5, {{ 0 | setting(16) }} // X[1] = Z[3] (bottom right) + + // fma32 + eor x6, x6, x6 + orr x6, x6, {{ 0 | setting(63) }} // vector mode + orr x6, x6, {{ 0 | setting(27) }} // Z=X*Y Z[0]=X[0]*Y[0] + + orr x7, x6, {{ 0 | setting(20) }} // Z[1] + orr x7, x7, {{ 0 | setting(16) }} // X[1] Z[1] = X[1]*Y[0] + + orr x8, x6, {{ 0 | setting(21) }} // Z[2] + orr x8, x8, {{ 0 | setting(21) }} // Z[2] + + orr x9, x8, {{ 0 | setting(20) }} // Z[3] + orr x9, x9, {{ 0 | setting(16) }} // X[1] + + mov x10, 16 + .loop_per_row_mul: + // top + ld1 { v0.s }[0], [x14], #4 + dup v0.4s, v0.s[0] + dup v1.4s, v0.s[0] + dup v2.4s, v0.s[0] + dup v3.4s, v0.s[0] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + + {{ amx("ldy", 1) }} + {{ amx("extrx", 2) }} + {{ amx("extrx", 3) }} + {{ amx("fma32", 6) }} + {{ amx("fma32", 7) }} + + add x2, x2, {{ 0 | setting(22) }} + add x3, x3, {{ 0 | setting(22) }} + add x6, x6, {{ 0 | setting(22) }} + add x7, x7, {{ 0 | setting(22) }} + + // bottom + ld1 { v0.s }[0], [x15], #4 + dup v0.4s, v0.s[0] + dup v1.4s, v0.s[0] + dup v2.4s, v0.s[0] + dup v3.4s, v0.s[0] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + + {{ amx("ldy", 1) }} + {{ amx("extrx", 4) }} + {{ amx("extrx", 5) }} + {{ amx("fma32", 8) }} + {{ amx("fma32", 9) }} + + add x4, x4, {{ 0 | setting(22) }} + add x5, x5, {{ 0 | setting(22) }} + add x8, x8, {{ 0 | setting(22) }} + add x9, x9, {{ 0 | setting(22) }} + + subs x10, x10, 1 + bne .loop_per_row_mul + + b .non_linear_loop + +.scalar_sub: + // performs a unary neg on Z, then go to scalar_add + eor x2, x2, x2 // X[0] = Z[0] + + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(28) }} + orr x4, x4, {{ 0 | setting(27) }} // Z=-X + + mov x6, 64 + .scalar_sub_loop: + {{ amx("extrx", 2) }} + {{ amx("fms32", 4) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + add x4, x4, {{ 0 | setting(20) }} // next Z row + subs x6, x6, 1 + bne .scalar_sub_loop + + // continue on purpose + +.scalar_add: + ldr w5, [x0, #8] + + fmov s0, w5 + dup v0.4s, v0.s[0] + dup v1.4s, v0.s[0] + dup v2.4s, v0.s[0] + dup v3.4s, v0.s[0] + + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + {{ amx("ldx", 1) }} // load 16 values + + mov x2, {{ 0 | setting(28) }} // Z+=X + {% for chunk in range(0, 4) %} + {{ amx("fma32", 2) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + {% endfor %} + b .non_linear_loop + +.scalar_sub_flipped: + ldr w5, [x0, #8] + fmov s0, w5 + dup v0.4s, v0.s[0] + dup v1.4s, v0.s[0] + dup v2.4s, v0.s[0] + dup v3.4s, v0.s[0] + + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + {{ amx("ldx", 1) }} // load 16 values + + mov x2, {{ 0 | setting(28) }} // Z-=X + {% for chunk in range(0, 4) %} + {{ amx("fms32", 2) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + {% endfor %} + b .non_linear_loop + +.scalar_mul: + ldr w5, [x0, #8] + fmov s0, w5 + dup v0.4s, v0.s[0] + dup v1.4s, v0.s[0] + dup v2.4s, v0.s[0] + dup v3.4s, v0.s[0] + + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + {{ amx("ldy", 1) }} // load 16 values + + eor x2, x2, x2 // X[0] = Z[0] + + mov x4, {{ 0 | setting(63) }} // vector mode + orr x4, x4, {{ 0 | setting(27) }} // Z=X*Y + + mov x6, 64 + .scalar_mul_loop: + {{ amx("extrx", 2) }} + {{ amx("fma32", 4) }} + add x2, x2, {{ 0 | setting(20) }} // next Z row + add x4, x4, {{ 0 | setting(20) }} // next Z row + subs x6, x6, 1 + bne .scalar_mul_loop + + b .non_linear_loop + +.scalar_min: + mov x2, 5 + b .scalar_min_max +.scalar_max: + mov x2, 7 +.scalar_min_max: + ldr w5, [x0, #8] + fmov s0, w5 + dup v0.4s, v0.s[0] + dup v1.4s, v0.s[0] + dup v2.4s, v0.s[0] + dup v3.4s, v0.s[0] + + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + {{ amx("ldx", 1) }} // load 16 values + + lsl x2, x2, 47 + orr x2, x2, {{ 0 | setting(44) }} // f32 + + mov x3, 64 + .loop_scalar_max: + add x2, x2, {{ 0 | setting(20) }} // next Z + {{ amx("vecfp", 2) }} + subs x3, x3, 1 + bne .loop_scalar_max + + b .non_linear_loop + +.add_unicast: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + add x8, x1, 64 + + mov x3, 0 // x3 is the row + .loop_load: + and x9, x3, 0xf // x9 = row % 16 + lsl x9, x9, 2 // x9 = (row % 16) * 4 + lsr x10, x3, 4 // x10 = row / 16 + lsl x10, x10, 1 // x10 = (row / 16) * 2 + add x9, x9, x10 // x9 = x9 + x10 + + mov x4, x5 + {% for neon in range(0, 4) %} + {% for lane in range(0, 4) %} + ld1 { v{{neon}}.s }[{{lane}}], [x4], x7 + {% endfor %} + {% endfor %} + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + {% for neon in range(0, 4) %} + {% for lane in range(0, 4) %} + ld1 { v{{neon}}.s }[{{lane}}], [x4], x7 + {% endfor %} + {% endfor %} + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x8] + + mov x2, x1 + orr x2, x2, {{ 0 | setting(62) }} // load 32 values + {{ amx("ldy", 2) }} + + lsl x2, x9, 20 // left Z register to update + orr x2, x2, {{ 0 | setting(63) }} // vector mode + orr x2, x2, {{ 0 | setting(29) }} // perform Z+=Y + {{ amx("fma32", 2) }} + + add x2, x2, {{ 0 | setting(20) }} + orr x2, x2, 64 // offset Y by 16 values + {{ amx("fma32", 2) }} + + add x5, x5, x6 + add x3, x3, 1 + cmp x3, 32 + bne .loop_load + + b .non_linear_loop + +.add_row_col_products: + ldp x5, x6, [x0, #8] // a base ptr, b base ptr + + add x8, x1, 64 + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x5], #64 + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1], #64 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x5] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + sub x1, x1, #64 + + orr x2, x1, {{ 0 | setting(62) }} // load a pair + {{ amx("ldy", 2) }} + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x6], #64 + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1], #64 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x6] + st1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + sub x1, x1, #64 + + orr x2, x1, {{ 0 | setting(62) }} // load a pair + {{ amx("ldx", 2) }} + + // top left + eor x2, x2, x2 + {{ amx("fma32", 2) }} + + // top right + orr x2, x2, {{ 0 | setting(20) }} // Z row = 1 + orr x2, x2, {{ 0 | setting(16) }} // X offset + {{ amx("fma32", 2) }} + + // bottom right + orr x2, x2, {{ 0 | setting(21) }} // Z row = 3 + orr x2, x2, {{ 0 | setting(6) }} // Y offset + {{ amx("fma32", 2) }} + + // bottom left + eor x2, x2, {{ 0 | setting(20) }} // Z row = 2 + eor x2, x2, {{ 0 | setting(16) }} // X offset <- + {{ amx("fma32", 2) }} + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x7, 4 + bne .store_generic + ands x8, x5, 0x7f + bne .store_generic + ands x8, x6, 0x7f + bne .store_generic + + orr x5, x5, {{ 0 | setting(62) }} // pair + lsl x8, x6, 4 + add x8, x8, x5 // x8 = 16*rsc + orr x8, x8, {{ 0 | setting(57) }} // first to x8 is z2 + + mov x4, {{ 0 | setting(58) }} // Zreg += 4 + add x4, x4, x6 // +rsc + + mov x3, 16 + .loop_store_direct: + {{ amx("stz", 5) }} + {{ amx("stz", 8) }} + add x5, x5, x4 + add x8, x8, x4 + subs x3, x3, 1 + bne .loop_store_direct + + b .non_linear_loop + +.store_generic: + + add x8, x1, 64 + + mov x3, 0 // row id + .loop_store: + and x9, x3, 0xf // x9 = row % 16 + lsl x9, x9, 2 // x9 = (row % 16) * 4 + lsr x10, x3, 4 // x10 = row / 16 + lsl x10, x10, 1 // x10 = (row / 16) * 2 + add x9, x9, x10 // x9 = x9 + x10 + + lsl x2, x9, 56 + orr x2, x2, {{ 0 | setting(62) }} + orr x2, x2, x1 + {{ amx("stz", 2) }} + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x1] + + mov x4, x5 + {% for neon in range(0, 4) %} + {% for lane in range(0, 4) %} + st1 { v{{neon}}.s }[{{lane}}], [x4], x7 + {% endfor %} + {% endfor %} + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x8] + {% for neon in range(0, 4) %} + {% for lane in range(0, 4) %} + st1 { v{{neon}}.s }[{{lane}}], [x4], x7 + {% endfor %} + {% endfor %} + add x5, x5, x6 + + add x3, x3, 1 + cmp x3, 32 + bne .loop_store + + b .non_linear_loop + +.load_tile: + ldr x2, [x0, #16] // row major ptr + orr x2, x2, {{ 0 | setting(62) }} // load pairs + mov x3, x2 + orr x3, x3, {{ 0 | setting(57) }} + add x3, x3, #2048 + + mov x4, {{ 0 | setting(58) }} // z+=4 + add x4, x4, #128 + + mov x8, 16 + .loop_load_tile: + {{ amx("ldz", 2) }} + {{ amx("ldz", 3) }} + add x2, x2, x4 + add x3, x3, x4 + subs x8, x8, 1 + bne .loop_load_tile + + b .non_linear_loop + +.return: +{{ AMX_CLR }} +ret diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/apple_amx/dispatcher.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/apple_amx/dispatcher.j2 new file mode 100644 index 0000000..5663fd2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/apple_amx/dispatcher.j2 @@ -0,0 +1,37 @@ +// vim: ft=arm + +.non_linear: + sub x0, x0, 40 + +.non_linear_loop: + add x0, x0, 40 + ldr x2, [x0] + + mov x4, #{{ jump_table | length }} + + cmp x2, #{{ jump_table | length }} + csel x2, x2, x4, lt + cmp x2, #0 + csel x2, x4, x2, lt + + adr x3, .jmp_table + add x3, x3, x2, LSL#2 + br x3 + +.jmp_table: +{% for j in jump_table %} + b .{{j}} +{% endfor %} + b .unsupported + + add x0, x2, #4000 + b .return + +.unsupported: + mov x0, #1 + b .return + +.done: + mov x0, 0 + b .return + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_leaky_relu_f16_8n.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_leaky_relu_f16_8n.S.j2 new file mode 100644 index 0000000..b0d13dc --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_leaky_relu_f16_8n.S.j2 @@ -0,0 +1,71 @@ +// vim: ft=arm + +// no preservation either for v0-v7 and v16-v31 + +.text +.align 4 + +{% if needs_pragma == true %} +.cpu generic+fp+simd+fp16 +{% endif %} +.global {{G}}arm64fp16_leaky_relu_f16_8n_{{suffix}} +{{G}}arm64fp16_leaky_relu_f16_8n_{{suffix}}: + + cmp x1, #0 + beq .return + + mov v31.h[0], w2 + dup v31.8h, v31.h[0] + mov x2, x0 + + cmp x1, #64 + blt .loop + + ld1 { v16.8h, v17.8h, v18.8h, v19.8h }, [x2], #64 +.loop4: + + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [x2], #64 + + fmul v20.8h, v16.8h, v31.8h + fmul v21.8h, v17.8h, v31.8h + fmul v22.8h, v18.8h, v31.8h + fmul v23.8h, v19.8h, v31.8h + + fcmge v24.8h, v16.8h, #0.0 + fcmge v25.8h, v17.8h, #0.0 + fcmge v26.8h, v18.8h, #0.0 + fcmge v27.8h, v19.8h, #0.0 + + bsl v24.16b, v16.16b, v20.16b + bsl v25.16b, v17.16b, v21.16b + bsl v26.16b, v18.16b, v22.16b + bsl v27.16b, v19.16b, v23.16b + + st1 { v24.8h, v25.8h, v26.8h, v27.8h }, [x0], #64 + + and v16.16b, v0.16b, v0.16b + and v17.16b, v1.16b, v1.16b + and v18.16b, v2.16b, v2.16b + and v19.16b, v3.16b, v3.16b + + subs x1, x1, #32 + cmp x1, #64 + bge .loop4 + + cmp x1, #0 + beq .return + +.loop: + ld1 { v16.8h }, [x0] + + fmul v17.8h, v16.8h, v31.8h + fcmge v18.8h, v16.8h, #0.0 + bsl v18.16b, v16.16b, v17.16b + + st1 { v18.8h }, [x0], #16 + + subs x1, x1, #8 + bne .loop + +.return: + ret diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_8h_ops.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_8h_ops.j2 new file mode 100644 index 0000000..b914612 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_8h_ops.j2 @@ -0,0 +1,86 @@ + +{% macro scalar(label, op, from, to, flipped=false) %} +.{{label}}: + add x2, x0, #8 + ld1 {v0.h}[0], [ x2 ] + dup v0.8h, v0.h[0] + {% if flipped %} + {% for reg in range(from, to + 1) %} + {{op}} v{{reg}}.8h, v{{reg}}.8h, v0.8h + {% endfor %} + {% else %} + {% for reg in range(from, to + 1) %} + {{op}} v{{reg}}.8h, v0.8h, v{{reg}}.8h + {% endfor %} + {% endif %} + + b .non_linear_loop +{% endmacro %} + +{% macro per_row(label, op, mr, from, to, flipped=false) %} +.{{label}}: + ldr x2, [x0, #8] + +{% set mr_over_8 = mr // 8 %} +{% set mr_over_8_min_1 = mr // 8 - 1 %} + +{% for reg in range(0, mr_over_8_min_1 + 1) %} + ldr q{{reg}}, [ x2 ], #16 +{% endfor %} + +{% if flipped %} + {% for acc in range(from, to + 1) %} + {% set other = (acc - from) % mr_over_8 %} + {{op}} v{{acc}}.8h, v{{acc}}.8h, v{{other}}.8h + {% endfor %} +{% else %} + {% for acc in range(from, to + 1) %} + {% set other = (acc - from) % mr_over_8 %} + {{op}} v{{acc}}.8h, v{{other}}.8h, v{{acc}}.8h + {% endfor %} +{% endif %} + +b .non_linear_loop +{% endmacro %} + +{% macro per_col(label, op, mr, from, to, flipped=false) %} +.{{label}}: + ldr x2, [x0, #8] + +{% set mr_over_8 = mr // 8 %} +{% set cols = (to + 1 - from) // mr_over_8 %} + +{% set loads = cols // 8 %} + +{%if cols == 1 %} + ld1 {v0.h}[0], [ x2 ] +{% elif cols == 3 %} + ld1 {v0.s}[0], [ x2 ], #4 + ld1 {v0.h}[2], [ x2 ] +{% elif cols == 4 %} + ldr d0, [ x2 ] +{% elif cols == 6 %} + ldr d0, [ x2 ], #8 + ld1 {v0.s}[2], [ x2 ] +{% else %} + {% for reg in range(1, loads + 1) %} + ldr q{{ reg - 1 }}, [ x2 ], #16 + {% endfor %} +{% endif %} + +// mr:{{mr}} {{ loads }} {{cols}} + +{% for col in range(1, cols + 1) %} + dup v3.8h, v{{ (col - 1) // 8 }}.h[{{ (col - 1) % 8 }}] + {% for row in range(1, mr_over_8 + 1) %} + {% set acc = (col - 1) * mr_over_8 + row - 1 + from %} + {% if flipped %} + {{op}} v{{acc}}.8h, v{{acc}}.8h, v3.8h + {% else %} + {{op}} v{{acc}}.8h, v3.8h, v{{acc}}.8h + {% endif %} + {% endfor %} +{% endfor %} + +b .non_linear_loop +{% endmacro %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_128x1/loop1/cortex_a53.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_128x1/loop1/cortex_a53.S.raw new file mode 100644 index 0000000..410816d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_128x1/loop1/cortex_a53.S.raw @@ -0,0 +1,65 @@ + fmla v16.4s, v0.4s, v8.s[0] + ldr x5, [x1, #128] + fmla v17.4s, v1.4s, v8.s[0] + ldr x6, [x1, #136] + fmla v18.4s, v2.4s, v8.s[0] + ldr x7, [x1, #144] + fmla v19.4s, v3.4s, v8.s[0] + ldr x9, [x1, #152] + ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [ x1 ], #64 + + fmla v20.4s, v4.4s, v8.s[0] + ldr x10, [x1, #96] + fmla v21.4s, v5.4s, v8.s[0] + ldr x11, [x1, #104] + fmla v22.4s, v6.4s, v8.s[0] + ldr x12, [x1, #112] + fmla v23.4s, v7.4s, v8.s[0] + ldr x13, [x1, #120] + + ld1 {{ v4.4s, v5.4s, v6.4s, v7.4s }}, [ x1 ] + + fmla v24.4s, v0.4s, v8.s[0] + ldr x14, [x1, #128] + fmla v25.4s, v1.4s, v8.s[0] + ldr x15, [x1, #136] + fmla v26.4s, v2.4s, v8.s[0] + ldr x20, [x1, #144] + fmla v27.4s, v3.4s, v8.s[0] + ldr x21, [x1, #152] + fmla v28.4s, v4.4s, v8.s[0] + ldr x22, [x1, #160] + fmla v29.4s, v5.4s, v8.s[0] + ldr x23, [x1, #168] + fmla v30.4s, v6.4s, v8.s[0] + ldr x24, [x1, #176] + fmla v31.4s, v7.4s, v8.s[0] + ldr x25, [x1, #184] + + ld1 {{ v8.s }}[0], [ x2 ], #4 + + prfm pldl1keep, [x1, #1024] + prfm pldl1keep, [x1, #1088] + prfm pldl1keep, [x1, #1152] + prfm pldl1keep, [x1, #1216] + prfm pldl1keep, [x2, #256] + + ins v0.d[0], x5 + ins v1.d[0], x7 + ins v2.d[0], x10 + ins v3.d[0], x12 + ins v4.d[0], x14 + ins v5.d[0], x20 + ins v6.d[0], x22 + ins v7.d[0], x24 + + ins v0.d[1], x6 + ins v1.d[1], x9 + ins v2.d[1], x11 + ins v3.d[1], x13 + ins v4.d[1], x15 + ins v5.d[1], x21 + ins v6.d[1], x23 + ins v7.d[1], x25 + + add x1, x1, #192 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_128x1/loop1/naive.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_128x1/loop1/naive.S.raw new file mode 100644 index 0000000..367339e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_128x1/loop1/naive.S.raw @@ -0,0 +1,32 @@ + ld1 {{ v9.8h, v10.8h, v11.8h, v12.8h }}, [x1], #64 + ld1 {{ v13.8h, v14.8h, v15.8h }}, [x1], #48 + + fmla v16.8h, v0.8h, v8.h[0] + fmla v17.8h, v1.8h, v8.h[0] + fmla v18.8h, v2.8h, v8.h[0] + fmla v19.8h, v3.8h, v8.h[0] + fmla v20.8h, v4.8h, v8.h[0] + fmla v21.8h, v5.8h, v8.h[0] + fmla v22.8h, v6.8h, v8.h[0] + fmla v23.8h, v7.8h, v8.h[0] + fmla v24.8h, v9.8h, v8.h[0] + ld1 {{ v9.8h }}, [ x1 ], #16 + ld1 {{ v0.8h, v1.8h, v2.8h, v3.8h }}, [x1], #64 + ld1 {{ v4.8h, v5.8h, v6.8h, v7.8h }}, [x1], #64 + fmla v25.8h, v10.8h, v8.h[0] + fmla v26.8h, v11.8h, v8.h[0] + fmla v27.8h, v12.8h, v8.h[0] + fmla v28.8h, v13.8h, v8.h[0] + fmla v29.8h, v14.8h, v8.h[0] + fmla v30.8h, v15.8h, v8.h[0] + + fmla v31.8h, v9.8h, v8.h[0] + + ld1 {{ v8.h }}[0], [ x2 ], #2 + + prfm pldl1keep, [x1, #1024] + prfm pldl1keep, [x1, #1088] + prfm pldl1keep, [x1, #1152] + prfm pldl1keep, [x1, #1216] + prfm pldl1keep, [x2, #256] + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_128x1/loop2/cortex_a55.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_128x1/loop2/cortex_a55.S.raw new file mode 100644 index 0000000..821ed3f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_128x1/loop2/cortex_a55.S.raw @@ -0,0 +1,85 @@ + ld1 {{ v9.4s, v10.4s, v11.4s }}, [x1], #48 + + fmla v16.8h, v0.8h, v8.h[0] + ldr w8, [x2], #4 + fmla v17.8h, v1.8h, v8.h[0] + ldr d12, [x1], #8 + fmla v18.8h, v2.8h, v8.h[0] + ldr x12, [x1], #8 + fmla v19.8h, v3.8h, v8.h[0] + ldr d13, [x1], #8 + fmla v20.8h, v4.8h, v8.h[0] + ldr x13, [x1], #8 + fmla v21.8h, v5.8h, v8.h[0] + ldr d14, [x1], #8 + fmla v22.8h, v6.8h, v8.h[0] + ldr x14, [x1], #8 + fmla v23.8h, v7.8h, v8.h[0] + ldr d15, [x1], #8 + fmla v24.8h, v9.8h, v8.h[0] + ldr x15, [x1], #8 + + ld1 {{ v0.8h, v1.8h, v2.8h, v3.8h }}, [x1], #64 + ins v8.s[1], w8 + ld1 {{ v4.8h, v5.8h, v6.8h, v7.8h }}, [x1], #64 + + fmla v25.8h, v10.8h, v8.h[0] + ins v12.d[1], x12 + fmla v26.8h, v11.8h, v8.h[0] + ins v13.d[1], x13 + fmla v27.8h, v12.8h, v8.h[0] + ins v14.d[1], x14 + fmla v28.8h, v13.8h, v8.h[0] + ins v15.d[1], x15 + + ld1 {{ v9.8h, v10.8h, v11.8h, v12.8h }}, [x1], #64 + + fmla v29.8h, v14.8h, v8.h[0] + ldr d13, [x1], #8 + fmla v30.8h, v15.8h, v8.h[0] + ldr x13, [x1], #8 + fmla v31.8h, v0.8h, v8.h[0] + ldr d14, [x1], #8 + + fmla v16.8h, v1.8h, v8.h[2] + ldr x14, [x1], #8 + fmla v17.8h, v2.8h, v8.h[2] + ldr d15, [x1], #8 + fmla v18.8h, v3.8h, v8.h[2] + ldr x15, [x1], #8 + fmla v19.8h, v4.8h, v8.h[2] + + ld1 {{ v0.8h }}, [x1], #16 + + fmla v20.8h, v5.8h, v8.h[2] + ldr d1, [x1], #8 + fmla v21.8h, v6.8h, v8.h[2] + ldr x10, [x1], #8 + + fmla v22.8h, v7.8h, v8.h[2] + + fmla v23.8h, v9.8h, v8.h[2] + ins v13.d[1], x13 + fmla v24.8h, v10.8h, v8.h[2] + ins v14.d[1], x14 + fmla v25.8h, v11.8h, v8.h[2] + ins v15.d[1], x15 + + fmla v26.8h, v12.8h, v8.h[2] + prfm pldl1keep, [x1, #1024] + fmla v27.8h, v13.8h, v8.h[2] + ins v1.d[1], x10 + fmla v28.8h, v14.8h, v8.h[2] + prfm pldl1keep, [x1, #1088] + fmla v29.8h, v15.8h, v8.h[2] + prfm pldl1keep, [x1, #1152] + fmla v30.8h, v0.8h, v8.h[2] + prfm pldl1keep, [x1, #1216] + fmla v31.8h, v1.8h, v8.h[2] + prfm pldl1keep, [x2, #256] + + ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [x1], #64 + ins v8.h[0], v8.h[3] + ld1 {{ v4.4s, v5.4s, v6.4s, v7.4s }}, [x1], #64 + + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_128x1_core.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_128x1_core.S.j2 new file mode 100644 index 0000000..33006dd --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_128x1_core.S.j2 @@ -0,0 +1,203 @@ +// vim: ft=arm + +// C tile regs: v16 to v31, no need to preserve + +// no preservation either for v0-v7... +// v8..v15 are callee-preserved +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +{% if needs_pragma == true %} +.cpu generic+fp+simd+fp16 +{% endif %} +.global {{G}}arm64fp16_mmm_f16_128x1_{{core}}_{{suffix}} +{{G}}arm64fp16_mmm_f16_128x1_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.j2" %} + +.add_mat_mul: + ldr x2, [x0, #24] // b + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + sub x3, x3, #1 + + + ld1 { v8.h }[0], [ x2 ], #2 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x1 ], #64 + ld1 { v4.4s, v5.4s, v6.4s, v7.4s }, [ x1 ], #64 + + cmp x3, #0 + beq .packed_packed_loop_1_last + + cmp x3, #4 + blt .packed_packed_loop_1 + +{% set packed_packed_loop1 %} + {% include "arm64fp16_mmm_f16_128x1/loop1/naive.S.raw" %} +{% endset %} + +{% set packed_packed_loop2 %} + {% include "arm64fp16_mmm_f16_128x1/loop2/cortex_a55.S.raw" %} +{% endset %} + +.p2align 4 +.packed_packed_loop_4: + {{ packed_packed_loop2 }} + {{ packed_packed_loop2 }} + + sub x3, x3, #4 + cmp x3, #4 + bge .packed_packed_loop_4 + + cmp x3, #0 + beq .packed_packed_loop_1_last + +.p2align 4 +.packed_packed_loop_1: + {{ packed_packed_loop1 }} + + subs x3, x3, #1 + bne .packed_packed_loop_1 + +// last loop can't read beyond actual input as it's likely not packed and padded +.packed_packed_loop_1_last: + ld1 { v9.8h, v10.8h, v11.8h, v12.8h }, [x1], #64 + ld1 { v13.8h, v14.8h, v15.8h }, [x1], #48 + + fmla v16.8h, v0.8h, v8.h[0] + fmla v17.8h, v1.8h, v8.h[0] + ld1 { v0.8h }, [ x1 ] + fmla v18.8h, v2.8h, v8.h[0] + fmla v19.8h, v3.8h, v8.h[0] + fmla v20.8h, v4.8h, v8.h[0] + fmla v21.8h, v5.8h, v8.h[0] + fmla v22.8h, v6.8h, v8.h[0] + fmla v23.8h, v7.8h, v8.h[0] + + fmla v24.8h, v9.8h, v8.h[0] + fmla v25.8h, v10.8h, v8.h[0] + fmla v26.8h, v11.8h, v8.h[0] + fmla v27.8h, v12.8h, v8.h[0] + fmla v28.8h, v13.8h, v8.h[0] + fmla v29.8h, v14.8h, v8.h[0] + fmla v30.8h, v15.8h, v8.h[0] + fmla v31.8h, v0.8h, v8.h[0] + + b .non_linear_loop + +{% set from = 16 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_scalars.j2" %} +{% set mr = 128 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_per_rows.j2" %} +{% set mr = 128 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_per_cols.j2" %} +{% set from = 16 %}{% set to = 31 %}{% include "arm64fp16_mmm_load_tile.j2" %} + +.add_unicast: + ldp x5, x6, [x0, #8] // c base ptr, rsc + cmp x6, #2 + beq .do_per_row_add + + {% for reg in range(16, 32) %} + {% for lane in range(0, 8) %} + ld1 {v0.h}[{{lane}}], [ x5 ], x6 + {% endfor %} + fadd v{{reg}}.8h, v{{reg}}.8h, v0.8h + {% endfor %} + + b .non_linear_loop + +.do_per_row_add: + ld1 {v0.8h-v3.8h}, [x5], #64 + ld1 {v4.8h-v7.8h}, [x5], #64 + ld1 {v8.8h-v11.8h}, [x5], #64 + ld1 {v12.8h-v15.8h}, [x5], #64 + + {% for r in range(0, 16) %} + fadd v{{ r + 16 }}.8h, v{{ r + 16 }}.8h, v{{r}}.8h + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x3, [x0, #16] + ldr x2, [x0, #8] + + ld1 {v8.h}[0], [ x3 ] + + {% for r in range(0, 8) %} + ldr q{{r}}, [x2], #16 + {% endfor %} + + fmla v16.8h, v0.8h, v8.h[0] + ldr q0, [x2], #16 + fmla v17.8h, v1.8h, v8.h[0] + ldr q1, [x2], #16 + fmla v18.8h, v2.8h, v8.h[0] + ldr q2, [x2], #16 + fmla v19.8h, v3.8h, v8.h[0] + ldr q3, [x2], #16 + fmla v20.8h, v4.8h, v8.h[0] + ldr q4, [x2], #16 + fmla v21.8h, v5.8h, v8.h[0] + ldr q5, [x2], #16 + fmla v22.8h, v6.8h, v8.h[0] + ldr q6, [x2], #16 + fmla v23.8h, v7.8h, v8.h[0] + ldr q7, [x2], #16 + + fmla v24.8h, v0.8h, v8.h[0] + fmla v25.8h, v1.8h, v8.h[0] + fmla v26.8h, v2.8h, v8.h[0] + fmla v27.8h, v3.8h, v8.h[0] + fmla v28.8h, v4.8h, v8.h[0] + fmla v29.8h, v5.8h, v8.h[0] + fmla v30.8h, v6.8h, v8.h[0] + fmla v31.8h, v7.8h, v8.h[0] + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc$ + + cmp x6, #2 + beq .store_strides_contig + + {% for reg in range(16, 32) %} + {% for lane in range(0, 8) %} + st1 { v{{reg}}.h }[{{lane}}], [ x5 ], x6 + {% endfor %} + {% endfor %} + b .non_linear_loop + +.store_strides_contig: + + {% for reg in range(16, 32) %} + st1 { v{{reg}}.8h }, [ x5 ], #16 + {% endfor %} + b .non_linear_loop + +.return: + + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_16x8/loop1/naive.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_16x8/loop1/naive.S.raw new file mode 100644 index 0000000..a55fe12 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_16x8/loop1/naive.S.raw @@ -0,0 +1,21 @@ + +fmla v16.8h, v0.8h, v4.h[0] +fmla v17.8h, v1.8h, v4.h[0] +fmla v18.8h, v0.8h, v4.h[1] +fmla v19.8h, v1.8h, v4.h[1] +fmla v20.8h, v0.8h, v4.h[2] +fmla v21.8h, v1.8h, v4.h[2] +fmla v22.8h, v0.8h, v4.h[3] +fmla v23.8h, v1.8h, v4.h[3] + +fmla v24.8h, v0.8h, v4.h[4] +fmla v25.8h, v1.8h, v4.h[4] +fmla v26.8h, v0.8h, v4.h[5] +fmla v27.8h, v1.8h, v4.h[5] +fmla v28.8h, v0.8h, v4.h[6] +fmla v29.8h, v1.8h, v4.h[6] +fmla v30.8h, v0.8h, v4.h[7] +fmla v31.8h, v1.8h, v4.h[7] + +ld1 {{ v0.8h, v1.8h }}, [x1], #32 +ld1 {{ v4.8h }}, [x2], #16 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_16x8/loop2/cortex_a55.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_16x8/loop2/cortex_a55.S.raw new file mode 100644 index 0000000..3fef68a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_16x8/loop2/cortex_a55.S.raw @@ -0,0 +1,54 @@ +fmla v16.8h, v0.8h, v4.h[0] +ldr d2, [x1], #8 +fmla v17.8h, v1.8h, v4.h[0] +ldr d6, [x2], #8 +fmla v18.8h, v0.8h, v4.h[1] +ldr x5, [x1], #8 +fmla v19.8h, v1.8h, v4.h[1] +ldr x7, [x2], #8 +fmla v20.8h, v0.8h, v4.h[2] +ldr d3, [x1], #8 +fmla v21.8h, v1.8h, v4.h[2] +fmla v22.8h, v0.8h, v4.h[3] +ldr x6, [x1], #8 +fmla v23.8h, v1.8h, v4.h[3] + +fmla v24.8h, v0.8h, v4.h[4] +fmla v25.8h, v1.8h, v4.h[4] +fmla v26.8h, v0.8h, v4.h[5] +fmla v27.8h, v1.8h, v4.h[5] +fmla v28.8h, v0.8h, v4.h[6] +ins v2.d[1], x5 +fmla v29.8h, v1.8h, v4.h[6] +ins v6.d[1], x7 +fmla v30.8h, v0.8h, v4.h[7] +ins v3.d[1], x6 +fmla v31.8h, v1.8h, v4.h[7] + +fmla v16.8h, v2.8h, v6.h[0] +ldr d0, [x1], #8 +fmla v17.8h, v3.8h, v6.h[0] +ldr d4, [x2], #8 +fmla v18.8h, v2.8h, v6.h[1] +ldr x5, [x1], #8 +fmla v19.8h, v3.8h, v6.h[1] +ldr x7, [x2], #8 +fmla v20.8h, v2.8h, v6.h[2] +ldr d1, [x1], #8 +fmla v21.8h, v3.8h, v6.h[2] +fmla v22.8h, v2.8h, v6.h[3] +ldr x6, [x1], #8 +fmla v23.8h, v3.8h, v6.h[3] + +fmla v24.8h, v2.8h, v6.h[4] +fmla v25.8h, v3.8h, v6.h[4] +fmla v26.8h, v2.8h, v6.h[5] +fmla v27.8h, v3.8h, v6.h[5] +fmla v28.8h, v2.8h, v6.h[6] +ins v0.d[1], x5 +fmla v29.8h, v3.8h, v6.h[6] +ins v4.d[1], x7 +fmla v30.8h, v2.8h, v6.h[7] +ins v1.d[1], x6 +fmla v31.8h, v3.8h, v6.h[7] + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_16x8_core.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_16x8_core.S.j2 new file mode 100644 index 0000000..ad32ebf --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_16x8_core.S.j2 @@ -0,0 +1,174 @@ +// vim: ft=arm + +// x20..x27 are used, callee-preserved + +// C tile regs: v16 to v31, (scratch) +// +// v16[0] v18[0] v20[0] v22[0] v24[0] v26[0] v28[0] v30[0] +// v16[1] v18[1] +// v16[2] v18[2] +// v16[3] v18[3] +// +// v17[0] v19[0] v21[0] v23[0] v25[0] v27[0] v29[0] v31[0] +// v17[1] v19[1] +// v17[2] v19[2] +// v17[3] v19[3] + +// v8 is used, d8 (lower half) must preserved +// v0-v7 (scratch registers) +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +{% if needs_pragma == true %} +.cpu generic+fp+simd+fp16 +{% endif %} +.global {{G}}arm64fp16_mmm_f16_16x8_{{core}}_{{suffix}} +{{G}}arm64fp16_mmm_f16_16x8_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + str q8, [sp, #-16]! + +{% include "dispatcher.j2" %} + +.add_mat_mul: + ldr x2, [x0, #24] // b + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + +.packed_packed: + ld1 { v0.4s, v1.4s }, [ x1 ], #32 + ld1 { v4.4s }, [ x2 ], #16 + +{% set packed_packed_loop1 %} + {% include "arm64fp16_mmm_f16_16x8/loop1/naive.S.raw" %} +{% endset %} + +{% set packed_packed_loop2 %} + {% if core == "a55" %} + {% include "arm64fp16_mmm_f16_16x8/loop2/cortex_a55.S.raw" %} + {% else %} + {{ packed_packed_loop1 }} + {{ packed_packed_loop1 }} + {% endif %} +{% endset %} + + cmp x3, #4 + blt .packed_packed_loop_1 + +.p2align 4 +.packed_packed_loop_4: + {{ packed_packed_loop2 }} + {{ packed_packed_loop2 }} + + sub x3, x3, #4 + cmp x3, #4 + bge .packed_packed_loop_4 + + + cmp x3, #0 + beq .non_linear_loop + +.p2align 4 +.packed_packed_loop_1: + {{ packed_packed_loop1 }} + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +{% set from = 16 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_scalars.j2" %} +{% set mr = 16 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_per_rows.j2" %} +{% set mr = 16 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_per_cols.j2" %} +{% set from = 16 %}{% set to = 31 %}{% include "arm64fp16_mmm_load_tile.j2" %} + +.add_unicast: + ldp x5, x6, [x0, #8] + ldp x7, x8, [x0, #24] + + {% for col in range(8, 16) %} + mov x4, x5 + {% for reg in range(0, 2) %} + {% for lane in range(0, 8) %} + ld1 {v0.h}[{{lane}}], [ x4 ], x6 + {% endfor %} + fadd v{{ col * 2 + reg }}.8h, v{{ col * 2 + reg }}.8h, v0.8h + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x2, [x0, #8] + ldr x3, [x0, #16] + + ld1 { v0.4s, v1.4s }, [ x2 ], #32 + ld1 { v4.4s }, [ x3 ], #16 + + fmla v16.8h, v0.8h, v4.h[0] + fmla v17.8h, v1.8h, v4.h[0] + fmla v18.8h, v0.8h, v4.h[1] + fmla v19.8h, v1.8h, v4.h[1] + fmla v20.8h, v0.8h, v4.h[2] + fmla v21.8h, v1.8h, v4.h[2] + fmla v22.8h, v0.8h, v4.h[3] + fmla v23.8h, v1.8h, v4.h[3] + + fmla v24.8h, v0.8h, v4.h[4] + fmla v25.8h, v1.8h, v4.h[4] + fmla v26.8h, v0.8h, v4.h[5] + fmla v27.8h, v1.8h, v4.h[5] + fmla v28.8h, v0.8h, v4.h[6] + fmla v29.8h, v1.8h, v4.h[6] + fmla v30.8h, v0.8h, v4.h[7] + fmla v31.8h, v1.8h, v4.h[7] + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x6, #2 + bne .store_strides_generic + + {% for col in range(8, 16) %} + str q{{ col * 2 }}, [ x5 ] + str q{{ col * 2 + 1 }}, [ x5, #16 ] + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.store_strides_generic: + + {% for col in range(8, 16) %} + mov x4, x5 + {% for reg in range(0, 2) %} + {% for lane in range(0, 8) %} + st1 { v{{ col * 2 + reg }}.h }[{{lane}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.return: + ldr q8, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_32x4/loop1/naive.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_32x4/loop1/naive.S.raw new file mode 100644 index 0000000..fa0b848 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_32x4/loop1/naive.S.raw @@ -0,0 +1,21 @@ + +fmla v16.8h, v0.8h, v4.h[0] +fmla v17.8h, v1.8h, v4.h[0] +fmla v18.8h, v2.8h, v4.h[0] +fmla v19.8h, v3.8h, v4.h[0] +fmla v20.8h, v0.8h, v4.h[1] +fmla v21.8h, v1.8h, v4.h[1] +fmla v22.8h, v2.8h, v4.h[1] +fmla v23.8h, v3.8h, v4.h[1] + +fmla v24.8h, v0.8h, v4.h[2] +fmla v25.8h, v1.8h, v4.h[2] +fmla v26.8h, v2.8h, v4.h[2] +fmla v27.8h, v3.8h, v4.h[2] +fmla v28.8h, v0.8h, v4.h[3] +fmla v29.8h, v1.8h, v4.h[3] +fmla v30.8h, v2.8h, v4.h[3] +fmla v31.8h, v3.8h, v4.h[3] + +ld1 {{ v0.8h, v1.8h, v2.8h, v3.8h }}, [ x1 ], #64 +ldr d4, [x2], #8 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_32x4/loop2/cortex_a55.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_32x4/loop2/cortex_a55.S.raw new file mode 100644 index 0000000..2e64319 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_32x4/loop2/cortex_a55.S.raw @@ -0,0 +1,71 @@ +// mul a: v0, v1, v2, v3 b: v4 +// load a: v5(d5/x5), v6(d6,x6), v7(d7,x7), v8(d8, x8) +// load b: v9 as d9 + +fmla v16.8h, v0.8h, v4.h[0] +ldr d5, [x1], #8 +fmla v17.8h, v1.8h, v4.h[0] +ldr d9, [x2], #8 +fmla v18.8h, v2.8h, v4.h[0] +ldr x5, [x1], #8 +fmla v19.8h, v3.8h, v4.h[0] +fmla v20.8h, v0.8h, v4.h[1] +ldr d6, [x1], #8 +fmla v21.8h, v1.8h, v4.h[1] +ldr x6, [x1], #8 +fmla v22.8h, v2.8h, v4.h[1] +ldr d7, [x1], #8 +fmla v23.8h, v3.8h, v4.h[1] +ldr x7, [x1], #8 + +fmla v24.8h, v0.8h, v4.h[2] +ldr d8, [x1], #8 +fmla v25.8h, v1.8h, v4.h[2] +ldr x8, [x1], #8 +fmla v26.8h, v2.8h, v4.h[2] +ins v5.d[1], x5 +fmla v27.8h, v3.8h, v4.h[2] +ins v6.d[1], x6 +fmla v28.8h, v0.8h, v4.h[3] +ins v7.d[1], x7 +fmla v29.8h, v1.8h, v4.h[3] +ins v8.d[1], x8 +fmla v30.8h, v2.8h, v4.h[3] +ins v9.d[1], x9 +fmla v31.8h, v3.8h, v4.h[3] + +// mul a: v5, v6, v7, v8 b: v9 +// load a: v0(d0/x5), v1(d1,x6), v2(d2,x7), v3(d3, x8) +// load b: v4 as d4 + +fmla v16.8h, v5.8h, v9.h[0] +ldr d0, [x1], #8 +fmla v17.8h, v6.8h, v9.h[0] +ldr d4, [x2], #8 +fmla v18.8h, v7.8h, v9.h[0] +ldr x5, [x1], #8 +fmla v19.8h, v8.8h, v9.h[0] +fmla v20.8h, v5.8h, v9.h[1] +ldr d1, [x1], #8 +fmla v21.8h, v6.8h, v9.h[1] +ldr x6, [x1], #8 +fmla v22.8h, v7.8h, v9.h[1] +ldr d2, [x1], #8 +fmla v23.8h, v8.8h, v9.h[1] +ldr x7, [x1], #8 + +fmla v24.8h, v5.8h, v9.h[2] +ldr d3, [x1], #8 +fmla v25.8h, v6.8h, v9.h[2] +ldr x8, [x1], #8 +fmla v26.8h, v7.8h, v9.h[2] +ins v0.d[1], x5 +fmla v27.8h, v8.8h, v9.h[2] +ins v1.d[1], x6 +fmla v28.8h, v5.8h, v9.h[3] +ins v2.d[1], x7 +fmla v29.8h, v6.8h, v9.h[3] +ins v3.d[1], x8 +fmla v30.8h, v7.8h, v9.h[3] +ins v4.d[1], x9 +fmla v31.8h, v8.8h, v9.h[3] diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_32x4_core.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_32x4_core.S.j2 new file mode 100644 index 0000000..4394379 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_32x4_core.S.j2 @@ -0,0 +1,165 @@ +// vim: ft=arm + +// x20..x27 are used, callee-preserved + +// C tile regs: v16 to v31, (scratch) + +// v8 is used, d8 (lower half) must preserved +// v0-v7 (scratch registers) +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +{% if needs_pragma == true %} +.cpu generic+fp+simd+fp16 +{% endif %} +.global {{G}}arm64fp16_mmm_f16_32x4_{{core}}_{{suffix}} +{{G}}arm64fp16_mmm_f16_32x4_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + +{% include "dispatcher.j2" %} + +.add_mat_mul: + ldr x2, [x0, #24] // b + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x1 ], #64 + ldr d4, [x2], #8 + +{% set packed_packed_loop1 %} + {% include "arm64fp16_mmm_f16_32x4/loop1/naive.S.raw" %} +{% endset %} + +{% set packed_packed_loop2 %} + {% if core == "a55" %} + {% include "arm64fp16_mmm_f16_32x4/loop2/cortex_a55.S.raw" %} + {% else %} + {{ packed_packed_loop1 }} + {{ packed_packed_loop1 }} + {% endif %} +{% endset %} + + cmp x3, #4 + blt .packed_packed_loop_1 + +.p2align 4 +.packed_packed_loop_4: + {{ packed_packed_loop2 }} + {{ packed_packed_loop2 }} + + sub x3, x3, #4 + cmp x3, #4 + bge .packed_packed_loop_4 + + cmp x3, #0 + beq .non_linear_loop + +.p2align 4 +.packed_packed_loop_1: + {{ packed_packed_loop1 }} + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +{% set from = 16 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_scalars.j2" %} +{% set mr = 32 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_per_rows.j2" %} +{% set mr = 32 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_per_cols.j2" %} +{% set from = 16 %}{% set to = 31 %}{% include "arm64fp16_mmm_load_tile.j2" %} + +.add_unicast: + ldp x5, x6, [x0, #8] + ldp x7, x8, [x0, #24] + + {% for col in range(0, 4) %} + mov x4, x5 + {% for reg in range(0, 4) %} + {% for lane in range(0, 8) %} + ld1 {v0.h}[{{lane}}], [ x4 ], x6 + {% endfor %} + fadd v{{ col * 4 + 16 + reg }}.8h, v{{ col * 4 + 16 + reg }}.8h, v0.8h + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x2, [x0, #8] + ldr x3, [x0, #16] + + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [ x2 ] + ldr d4, [x3] + + fmla v16.8h, v0.8h, v4.h[0] + fmla v17.8h, v1.8h, v4.h[0] + fmla v18.8h, v2.8h, v4.h[0] + fmla v19.8h, v3.8h, v4.h[0] + fmla v20.8h, v0.8h, v4.h[1] + fmla v21.8h, v1.8h, v4.h[1] + fmla v22.8h, v2.8h, v4.h[1] + fmla v23.8h, v3.8h, v4.h[1] + + fmla v24.8h, v0.8h, v4.h[2] + fmla v25.8h, v1.8h, v4.h[2] + fmla v26.8h, v2.8h, v4.h[2] + fmla v27.8h, v3.8h, v4.h[2] + fmla v28.8h, v0.8h, v4.h[3] + fmla v29.8h, v1.8h, v4.h[3] + fmla v30.8h, v2.8h, v4.h[3] + fmla v31.8h, v3.8h, v4.h[3] + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x6, #2 + bne .store_strides_generic + + {% for col in range(0, 4) %} + str q{{ col * 4 + 16 + 0 }}, [ x5 ] + str q{{ col * 4 + 16 + 1 }}, [ x5, #16 ] + str q{{ col * 4 + 16 + 2 }}, [ x5, #32 ] + str q{{ col * 4 + 16 + 3 }}, [ x5, #48 ] + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.store_strides_generic: + + {% for col in range(0, 4) %} + mov x4, x5 + {% for reg in range(0, 4) %} + {% for lane in range(0, 8) %} + st1 { v{{ col * 4 + 16 + reg }}.h }[{{lane}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.return: + ldp d8, d9, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_32x6.core.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_32x6.core.S.j2 new file mode 100644 index 0000000..ff8d243 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_32x6.core.S.j2 @@ -0,0 +1,148 @@ +// vim: ft=arm + +// C tile regs: v16 to v31, no need to preserve + +// no preservation either for v0-v7... +// v8..v15 are callee-preserved +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +{% if needs_pragma == true %} +.cpu generic+fp+simd+fp16 +{% endif %} +.global {{G}}arm64fp16_mmm_f16_32x6_{{core}}_{{suffix}} +{{G}}arm64fp16_mmm_f16_32x6_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.j2" %} + +.add_mat_mul: + ldp x2, x4, [x0, #24] // b, packing + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + +.p2align 4 +.packed_packed_loop_1: + ld1 { v7.8h }, [ x2 ] + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [ x1 ], #64 + add x2, x2, 12 + +{% for row in range(0, 4) %} + {% for col in range(0, 6) %} + fmla v{{ col * 4 + 8 + row }}.8h, v{{row}}.8h, v7.h[{{col}}] + {% endfor %} + /* + {% for col in range(0, 2) %} + fmla v{{ (col + 4) * 4 + 8 + row }}.8h, v{{row}}.8h, v6.h[{{col}}] + {% endfor %} + */ +{% endfor %} + + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +{% set from = 8 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_scalars.j2" %} +{% set mr = 32 %}{% set from = 8 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_per_rows.j2" %} +{% set mr = 32 %}{% set from = 8 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_per_cols.j2" %} +{% set from = 8 %}{% set to = 31 %}{% include "arm64fp16_mmm_load_tile.j2" %} + +.add_unicast: + ldp x5, x6, [x0, #8] + ldp x7, x8, [x0, #24] + + {% for col in range(0, 6) %} + mov x4, x5 + {% for reg in range(0, 4) %} + {% for lane in range(0, 8) %} + ld1 {v0.h}[{{lane}}], [ x4 ], x6 + {% endfor %} + fadd v{{ col * 4 + 8 + reg }}.8h, v{{ col * 4 + 8 + reg }}.8h, v0.8h + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.do_per_row_add: + ld1 {v0.8h-v3.8h}, [x5], #64 + ld1 {v4.8h-v7.8h}, [x5], #64 + + {% for r in range(0, 8) %} + fadd v{{ r + 24 }}.8h, v{{ r + 24 }}.8h, v{{r}}.8h + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldp x2, x3, [x0, #8] + + ld1 { v7.d }[0], [ x3 ], #8 + ld1 { v7.s }[2], [ x3 ], #4 + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [ x2 ], #64 + +{% for row in range(0, 4) %} + {% for col in range(0, 6) %} + fmla v{{ col * 4 + 8 + row }}.8h, v{{row}}.8h, v7.h[{{col}}] + {% endfor %} +{% endfor %} + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x6, #2 + beq .store_strides_contig + + {% for col in range(0, 6) %} + mov x4, x5 + {% for reg in range(0, 4) %} + {% for lane in range(0, 8) %} + st1 { v{{ col * 4 + 8 + reg }}.h }[{{lane}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + b .non_linear_loop + +.store_strides_contig: + + {% for col in range(0, 6) %} + mov x4, x5 + {% for r in range(0, 4) %} + st1 { v{{ col * 4 + 8 + r }}.8h }, [ x4 ], 16 + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.return: + + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_64x1.core.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_64x1.core.S.j2 new file mode 100644 index 0000000..a77d618 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_64x1.core.S.j2 @@ -0,0 +1,264 @@ +// vim: ft=arm + +// C tile regs: v16 to v31, no need to preserve + +// no preservation either for v0-v7... +// v8..v15 are callee-preserved +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +{% if needs_pragma == true %} +.cpu generic+fp+simd+fp16 +{% endif %} +.global {{G}}arm64fp16_mmm_f16_64x1_{{core}}_{{suffix}} +{{G}}arm64fp16_mmm_f16_64x1_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.j2" %} + +.add_mat_mul: + ldp x2, x4, [x0, #24] // b, packing + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + + cmp x4, #1 + beq .q4f16se + + cmp x4, #2 + beq .q4f16 + + + +.p2align 4 +.packed_packed_loop_1: + ld1 { v8.h }[0], [ x2 ], #2 + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [ x1 ], #64 + ld1 { v4.8h, v5.8h, v6.8h, v7.8h }, [ x1 ], #64 + + fmla v24.8h, v0.8h, v8.h[0] + fmla v25.8h, v1.8h, v8.h[0] + fmla v26.8h, v2.8h, v8.h[0] + fmla v27.8h, v3.8h, v8.h[0] + fmla v28.8h, v4.8h, v8.h[0] + fmla v29.8h, v5.8h, v8.h[0] + fmla v30.8h, v6.8h, v8.h[0] + fmla v31.8h, v7.8h, v8.h[0] + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +.p2align 8 +.q40f16_const: + .byte 0xc8, 0xc7, 0xc6, 0xc5, 0xc4, 0xc2, 0xc0, 0xbc + .byte 0x00, 0x3c, 0x40, 0x42, 0x44, 0x45, 0x46, 0x47 + +.q4f16se: + adr x4, .q40f16_const + movi v15.16b, 15 + ld1 {v13.16b}, [ x4 ] + eor v12.16b, v12.16b, v12.16b + +.q4f16se_outerloop: +{% for i in range(0, 8) %} + eor v{{ i + 16 }}.16b, v{{ i + 16 }}.16b, v{{ i + 16 }}.16b +{% endfor %} + mov x4, #32 + +.p2align 4 +.q4f16se_innerloop: + ld1 { v9.16b-v10.16b }, [x1], #32 + ld1 { v8.h }[0], [ x2 ], #2 + + and v0.16b, v9.16b, v15.16b + ushr v2.16b, v9.16b, 4 + + and v4.16b, v10.16b, v15.16b + ushr v6.16b, v10.16b, 4 + + tbl v0.16b, { v13.16b }, v0.16b + tbl v2.16b, { v13.16b }, v2.16b + tbl v4.16b, { v13.16b }, v4.16b + tbl v6.16b, { v13.16b }, v6.16b + + zip2 v1.16b, v12.16b, v0.16b + zip2 v3.16b, v12.16b, v2.16b + zip2 v5.16b, v12.16b, v4.16b + zip2 v7.16b, v12.16b, v6.16b + + zip1 v0.16b, v12.16b, v0.16b + zip1 v2.16b, v12.16b, v2.16b + zip1 v4.16b, v12.16b, v4.16b + zip1 v6.16b, v12.16b, v6.16b + +{% for i in range(0, 8) %} + fmla v{{ i + 16 }}.8h, v{{i}}.8h, v8.h[0] +{% endfor %} + + subs x4, x4, #1 + bne .q4f16se_innerloop + + // scales + ld1 { v0.8h-v3.8h }, [ x1 ], #64 + ld1 { v4.8h-v7.8h }, [ x1 ], #64 + +{% for i in range(0, 8) %} + fmla v{{ i + 24 }}.8h, v{{i}}.8h, v{{ i + 16 }}.8h +{% endfor %} + + subs x3, x3, #32 + bne .q4f16se_outerloop + + b .non_linear_loop + +.q4f16: + adr x4, .q40f16_const + movi v15.16b, 15 + ld1 {v13.16b}, [ x4 ] + eor v12.16b, v12.16b, v12.16b + +.q4f16_outerloop: + // scales + ld1 { v16.8h-v19.8h }, [ x1 ], #64 + ld1 { v20.8h-v23.8h }, [ x1 ], #64 + mov x4, #32 + +.p2align 4 +.q4f16_innerloop: + ld1 { v9.16b-v10.16b }, [x1], #32 + ld1 { v8.h }[0], [ x2 ], #2 + + and v0.16b, v9.16b, v15.16b + ushr v2.16b, v9.16b, 4 + + and v4.16b, v10.16b, v15.16b + ushr v6.16b, v10.16b, 4 + + tbl v0.16b, { v13.16b }, v0.16b + tbl v2.16b, { v13.16b }, v2.16b + tbl v4.16b, { v13.16b }, v4.16b + tbl v6.16b, { v13.16b }, v6.16b + + zip2 v1.16b, v12.16b, v0.16b + zip2 v3.16b, v12.16b, v2.16b + zip2 v5.16b, v12.16b, v4.16b + zip2 v7.16b, v12.16b, v6.16b + + zip1 v0.16b, v12.16b, v0.16b + zip1 v2.16b, v12.16b, v2.16b + zip1 v4.16b, v12.16b, v4.16b + zip1 v6.16b, v12.16b, v6.16b + +{% for i in range(0, 8) %} + fmul v{{i}}.8h, v{{i}}.8h, v{{ i + 16 }}.8h +{% endfor %} + +{% for i in range(0, 8) %} + fmla v{{ i + 24 }}.8h, v{{i}}.8h, v8.h[0] +{% endfor %} + + subs x4, x4, #1 + bne .q4f16_innerloop + + subs x3, x3, #32 + bne .q4f16_outerloop + + b .non_linear_loop + +{% set from = 24 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_scalars.j2" %} +{% set mr = 64 %}{% set from = 24 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_per_rows.j2" %} +{% set mr = 64 %}{% set from = 24 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_per_cols.j2" %} +{% set from = 24 %}{% set to = 31 %}{% include "arm64fp16_mmm_load_tile.j2" %} + +.add_unicast: + ldp x5, x6, [x0, #8] // c base ptr, rsc + cmp x6, #2 + beq .do_per_row_add + + {% for reg in range(24, 32) %} + {% for lane in range(0, 8) %} + ld1 {v0.h}[{{lane}}], [ x5 ], x6 + {% endfor %} + fadd v{{reg}}.8h, v{{reg}}.8h, v0.8h + {% endfor %} + + b .non_linear_loop + +.do_per_row_add: + ld1 {v0.8h-v3.8h}, [x5], #64 + ld1 {v4.8h-v7.8h}, [x5], #64 + + {% for r in range(0, 8) %} + fadd v{{ r + 24 }}.8h, v{{ r + 24 }}.8h, v{{r}}.8h + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x3, [x0, #16] + ldr x2, [x0, #8] + + ld1 {v8.h}[0], [ x3 ] + + {% for r in range(0, 8) %} + ldr q{{r}}, [x2], #16 + {% endfor %} + + fmla v24.8h, v0.8h, v8.h[0] + fmla v25.8h, v1.8h, v8.h[0] + fmla v26.8h, v2.8h, v8.h[0] + fmla v27.8h, v3.8h, v8.h[0] + fmla v28.8h, v4.8h, v8.h[0] + fmla v29.8h, v5.8h, v8.h[0] + fmla v30.8h, v6.8h, v8.h[0] + fmla v31.8h, v7.8h, v8.h[0] + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc$ + + cmp x6, #2 + beq .store_strides_contig + + {% for reg in range(24, 32) %} + {% for lane in range(0, 8) %} + st1 { v{{reg}}.h }[{{lane}}], [ x5 ], x6 + {% endfor %} + {% endfor %} + b .non_linear_loop + +.store_strides_contig: + + {% for reg in range(24, 32) %} + st1 { v{{reg}}.8h }, [ x5 ], #16 + {% endfor %} + + b .non_linear_loop + +.return: + + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_64x3.core.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_64x3.core.S.j2 new file mode 100644 index 0000000..9d77890 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_64x3.core.S.j2 @@ -0,0 +1,165 @@ +// vim: ft=arm + +// C tile regs: v16 to v31, no need to preserve + +// no preservation either for v0-v7... +// v8..v15 are callee-preserved +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +{% if needs_pragma == true %} +.cpu generic+fp+simd+fp16 +{% endif %} +.global {{G}}arm64fp16_mmm_f16_64x3_{{core}}_{{suffix}} +{{G}}arm64fp16_mmm_f16_64x3_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.j2" %} + +.add_mat_mul: + ldp x2, x4, [x0, #24] // b, packing + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + +.p2align 4 +.packed_packed_loop_1: + ld1 { v7.4s }, [ x2 ] + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [ x1 ], #64 + ld1 { v4.8h, v5.8h, v6.8h }, [ x1 ], #48 + add x2, x2, #6 + +{% for col in range(0, 3) %} + fmla v{{ col * 8 + 8 }}.8h, v0.8h, v7.h[{{ col }}] +{% endfor %} + + ld1 { v0.8h }, [ x1 ], #16 + +{% for row in range(1, 7) %} + {% for col in range(0, 3) %} + fmla v{{ col * 8 + 8 + row }}.8h, v{{row}}.8h, v7.h[{{ col }}] + {% endfor %} +{% endfor %} + +{% for col in range(0, 3) %} + fmla v{{ col * 8 + 15 }}.8h, v0.8h, v7.h[{{ col }}] +{% endfor %} + + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +{% set from = 8 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_scalars.j2" %} +{% set mr = 64 %}{% set from = 8 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_per_rows.j2" %} +{% set mr = 64 %}{% set from = 8 %}{% set to = 31 %}{% include "arm64fp16_mmm_f16_per_cols.j2" %} +{% set from = 8 %}{% set to = 31 %}{% include "arm64fp16_mmm_load_tile.j2" %} + +.add_unicast: + ldp x5, x6, [x0, #8] + ldp x7, x8, [x0, #24] + + {% for col in range(0, 3) %} + mov x4, x5 + {% for reg in range(0, 8) %} + {% for lane in range(0, 8) %} + ld1 {v0.h}[{{lane}}], [ x4 ], x6 + {% endfor %} + fadd v{{ col * 8 + 8 + reg }}.8h, v{{ col * 8 + 8 + reg }}.8h, v0.8h + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.do_per_row_add: + ld1 {v0.8h-v3.8h}, [x5], #64 + ld1 {v4.8h-v7.8h}, [x5], #64 + + {% for r in range(0, 8) %} + fadd v{{ r + 24 }}.8h, v{{ r + 24 }}.8h, v{{r}}.8h + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldp x2, x3, [x0, #8] + + ld1 { v7.s }[0], [ x3 ], #4 + ld1 { v7.h }[2], [ x3 ], #2 + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [ x2 ], #64 + ld1 { v4.8h, v5.8h, v6.8h }, [ x2 ], #48 + +{% for col in range(0, 3) %} + fmla v{{ col * 8 + 8 }}.8h, v0.8h, v7.h[{{ col }}] +{% endfor %} + + ld1 { v0.8h }, [ x2 ], #16 + +{% for row in range(1, 7) %} + {% for col in range(0, 3) %} + fmla v{{ col * 8 + 8 + row }}.8h, v{{row}}.8h, v7.h[{{ col }}] + {% endfor %} +{% endfor %} + +{% for col in range(0, 3) %} + fmla v{{ col * 8 + 15 }}.8h, v0.8h, v7.h[{{ col }}] +{% endfor %} + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x6, #2 + beq .store_strides_contig + + {% for col in range(0, 3) %} + mov x4, x5 + {% for reg in range(0, 8) %} + {% for lane in range(0, 8) %} + st1 { v{{ col * 8 + 8 + reg }}.h }[{{lane}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + b .non_linear_loop + +.store_strides_contig: + + {% for col in range(0, 3) %} + mov x4, x5 + {% for r in range(0, 8) %} + st1 { v{{ col * 8 + 8 + r }}.8h }, [ x4 ], 16 + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.return: + + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_per_cols.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_per_cols.j2 new file mode 100644 index 0000000..ad80363 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_per_cols.j2 @@ -0,0 +1,10 @@ +// vim: ft=arm + +{% from "arm64fp16_mmm_8h_ops.j2" import per_col %} + +{{ per_col(label="per_col_min", op="fmin", mr=mr, from=from, to=to) }} +{{ per_col(label="per_col_max", op="fmax", mr=mr, from=from, to=to) }} +{{ per_col(label="per_col_mul", op="fmul", mr=mr, from=from, to=to) }} +{{ per_col(label="per_col_add", op="fadd", mr=mr, from=from, to=to) }} +{{ per_col(label="per_col_sub", op="fsub", mr=mr, from=from, to=to) }} +{{ per_col(label="per_col_sub_flipped", op="fsub", mr=mr, from=from, to=to, flipped=true) }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_per_rows.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_per_rows.j2 new file mode 100644 index 0000000..34ba0e7 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_per_rows.j2 @@ -0,0 +1,10 @@ +// vim: ft=arm + +{% from "arm64fp16_mmm_8h_ops.j2" import per_row %} + +{{ per_row(label="per_row_min", op="fmin", mr=mr, from=from, to=to) }} +{{ per_row(label="per_row_max", op="fmax", mr=mr, from=from, to=to) }} +{{ per_row(label="per_row_mul", op="fmul", mr=mr, from=from, to=to) }} +{{ per_row(label="per_row_add", op="fadd", mr=mr, from=from, to=to) }} +{{ per_row(label="per_row_sub", op="fsub", mr=mr, from=from, to=to) }} +{{ per_row(label="per_row_sub_flipped", op="fsub", mr=mr, from=from, to=to, flipped=true) }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_scalars.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_scalars.j2 new file mode 100644 index 0000000..89b28b8 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_f16_scalars.j2 @@ -0,0 +1,38 @@ +// vim: ft=arm + +{% from "arm64fp16_mmm_8h_ops.j2" import scalar %} + +{{ scalar(label="scalar_min", op="fmin", from=from, to=to) }} +{{ scalar(label="scalar_max", op="fmax", from=from, to=to) }} +{{ scalar(label="scalar_mul", op="fmul", from=from, to=to) }} +{{ scalar(label="scalar_add", op="fadd", from=from, to=to) }} +{{ scalar(label="scalar_sub", op="fsub", from=from, to=to) }} +{{ scalar(label="scalar_sub_flipped", op="fsub", from=from, to=to, flipped=true) }} + +.clear: +{% for r in range(from, to + 1) %} + eor v{{r}}.8b, v{{r}}.8b, v{{r}}.8b +{% endfor %} + b .non_linear_loop + +.leaky_relu: + add x2, x0, #8 + ld1 {v4.s}[0], [ x2 ] + dup v4.8h, v4.h[0] + + // bsl cond/dst, then, else + // fcmge dst, src, #0.0 + {% for r in range(from, to + 1) %} + fmul v0.8h, v{{r}}.8h, v4.8h + fcmge v1.8h, v{{r}}.8h, #0.0 + bsl v1.16b, v{{r}}.16b, v0.16b + and v{{r}}.16b, v1.16b, v1.16b + {% endfor %} + + b .non_linear_loop + + +.q_scale: +.q_shl: +.q_shr: + b .unsupported diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_load_tile.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_load_tile.j2 new file mode 100644 index 0000000..3631e95 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_mmm_load_tile.j2 @@ -0,0 +1,10 @@ +// vim: ft=arm + +.load_tile: + ldr x2, [ x0, #8 ] + {% for reg in range(from, to + 1) %} + ld1 { v{{reg}}.4s }, [ x2 ], #16 + {% endfor %} + + b .non_linear_loop + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_sigmoid_f16_8n.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_sigmoid_f16_8n.S.j2 new file mode 100644 index 0000000..7d0e76e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_sigmoid_f16_8n.S.j2 @@ -0,0 +1,131 @@ +// vim: ft=arm + +// no preservation either for v0-v7 and v16-v31 + +.text +.align 4 + +{% if needs_pragma == true %} +.cpu generic+fp+simd+fp16 +{% endif %} +.global {{G}}arm64fp16_sigmoid_f16_8n_{{suffix}} +{{G}}arm64fp16_sigmoid_f16_8n_{{suffix}}: + + cmp x1, #0 + beq .return + + adr x2, .coeffs_num + ld1 { v0.8h }, [x2] + dup v5.8h, v0.h[0] // v5 <- low, broadcasted + dup v6.8h, v0.h[1] // v6 <- high, broadcasted + dup v7.8h, v0.h[7] // v7 <- half, broadcasted + + cmp x1, #32 + blt .loop + +.loop4: + ld1 { v16.8h, v17.8h, v18.8h, v19.8h }, [x0] + + fmax v16.8h, v16.8h, v5.8h + fmax v17.8h, v17.8h, v5.8h + fmax v18.8h, v18.8h, v5.8h + fmax v19.8h, v19.8h, v5.8h + + fmin v16.8h, v16.8h, v6.8h + fmin v17.8h, v17.8h, v6.8h + fmin v18.8h, v18.8h, v6.8h + fmin v19.8h, v19.8h, v6.8h // v16 <- x + + fmul v20.8h, v16.8h, v16.8h + fmul v21.8h, v17.8h, v17.8h + fmul v22.8h, v18.8h, v18.8h + fmul v23.8h, v19.8h, v19.8h // v20 <- x2 + + dup v28.8h, v0.h[3] + fmla v28.8h, v20.8h, v0.h[2] + dup v29.8h, v0.h[3] + fmla v29.8h, v21.8h, v0.h[2] + dup v30.8h, v0.h[3] + fmla v30.8h, v22.8h, v0.h[2] + dup v31.8h, v0.h[3] + fmla v31.8h, v23.8h, v0.h[2] + + dup v24.8h, v0.h[4] + fmla v24.8h, v20.8h, v28.8h + dup v25.8h, v0.h[4] + fmla v25.8h, v21.8h, v29.8h + dup v26.8h, v0.h[4] + fmla v26.8h, v22.8h, v30.8h + dup v27.8h, v0.h[4] + fmla v27.8h, v23.8h, v31.8h + + fmul v16.8h, v16.8h, v24.8h + fmul v17.8h, v17.8h, v25.8h + fmul v18.8h, v18.8h, v26.8h + fmul v19.8h, v19.8h, v27.8h // v16 <- numerator + + dup v24.8h, v0.h[6] + dup v25.8h, v0.h[6] + dup v26.8h, v0.h[6] + dup v27.8h, v0.h[6] + fmla v24.8h, v20.8h, v0.h[5] + fmla v25.8h, v21.8h, v0.h[5] + fmla v26.8h, v22.8h, v0.h[5] + fmla v27.8h, v23.8h, v0.h[5] // v24 <- denum + + fdiv v16.8h, v16.8h, v24.8h + fdiv v17.8h, v17.8h, v25.8h + fdiv v18.8h, v18.8h, v26.8h + fdiv v19.8h, v19.8h, v27.8h + + fadd v16.8h, v16.8h, v7.8h + fadd v17.8h, v17.8h, v7.8h + fadd v18.8h, v18.8h, v7.8h + fadd v19.8h, v19.8h, v7.8h + + st1 { v16.8h, v17.8h, v18.8h, v19.8h }, [x0], #64 + + subs x1, x1, #32 + cmp x1, #32 + bge .loop4 + + cmp x1, #0 + beq .return + +.loop: + ld1 { v16.8h }, [x0] + + fmax v16.8h, v16.8h, v5.8h + fmin v16.8h, v16.8h, v6.8h // v16 <- x + fmul v20.8h, v16.8h, v16.8h // v20 <- x2 + + dup v28.8h, v0.h[3] + fmla v28.8h, v20.8h, v0.h[2] + dup v24.8h, v0.h[4] + fmla v24.8h, v20.8h, v28.8h + fmul v16.8h, v16.8h, v24.8h // v16 <- numerator + + dup v24.8h, v0.h[6] + fmla v24.8h, v20.8h, v0.h[5] // v24 <- denum + + fdiv v16.8h, v16.8h, v24.8h + fadd v16.8h, v16.8h, v7.8h + + st1 { v16.8h }, [x0], #16 + + subs x1, x1, #8 + bne .loop + +.return: + ret + +.coeffs_num: + {{ -6.92 | float16 }} + {{ 6.92 | float16 }} + {{ -0.0000124702 | float16 }} + {{ 0.00400222 | float16 }} + + {{ 0.249895 | float16 }} + {{ 0.098734 | float16 }} + {{ 1.0 | float16 }} + {{ 0.5 | float16 }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_tanh_f16_8n.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_tanh_f16_8n.S.j2 new file mode 100644 index 0000000..cd01f04 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/arm64fp16_tanh_f16_8n.S.j2 @@ -0,0 +1,124 @@ +// vim: ft=arm + +// no preservation either for v0-v7 and v16-v31 + +.text +.align 4 + +{% if needs_pragma == true %} +.cpu generic+fp+simd+fp16 +{% endif %} +.global {{G}}arm64fp16_tanh_f16_8n_{{suffix}} +{{G}}arm64fp16_tanh_f16_8n_{{suffix}}: + + cmp x1, #0 + beq .return + + adr x2, .coeffs_num + ld1 { v0.8h }, [x2] + dup v5.8h, v0.h[0] // v5 <- low, broadcasted + dup v6.8h, v0.h[1] // v6 <- high, broadcasted + + cmp x1, #32 + blt .loop + +.loop4: + ld1 { v16.8h, v17.8h, v18.8h, v19.8h }, [x0] + + fmax v16.8h, v16.8h, v5.8h + fmax v17.8h, v17.8h, v5.8h + fmax v18.8h, v18.8h, v5.8h + fmax v19.8h, v19.8h, v5.8h + + fmin v16.8h, v16.8h, v6.8h + fmin v17.8h, v17.8h, v6.8h + fmin v18.8h, v18.8h, v6.8h + fmin v19.8h, v19.8h, v6.8h // v16 <- x + + fmul v20.8h, v16.8h, v16.8h + fmul v21.8h, v17.8h, v17.8h + fmul v22.8h, v18.8h, v18.8h + fmul v23.8h, v19.8h, v19.8h // v20 <- x2 + + dup v24.8h, v0.h[3] + fmla v24.8h, v20.8h, v0.h[2] + dup v25.8h, v0.h[3] + fmla v25.8h, v21.8h, v0.h[2] + dup v26.8h, v0.h[3] + fmla v26.8h, v22.8h, v0.h[2] + dup v27.8h, v0.h[3] + fmla v27.8h, v23.8h, v0.h[2] + + fmul v16.8h, v16.8h, v24.8h + fmul v17.8h, v17.8h, v25.8h + fmul v18.8h, v18.8h, v26.8h + fmul v19.8h, v19.8h, v27.8h // v16 <- numerator + + dup v28.8h, v0.h[5] + fmla v28.8h, v20.8h, v0.h[4] + dup v29.8h, v0.h[5] + fmla v29.8h, v21.8h, v0.h[4] + dup v30.8h, v0.h[5] + fmla v30.8h, v22.8h, v0.h[4] + dup v31.8h, v0.h[5] + fmla v31.8h, v23.8h, v0.h[4] + + dup v24.8h, v0.h[6] + fmla v24.8h, v20.8h, v28.8h + dup v25.8h, v0.h[6] + fmla v25.8h, v21.8h, v29.8h + dup v26.8h, v0.h[6] + fmla v26.8h, v22.8h, v30.8h + dup v27.8h, v0.h[6] + fmla v27.8h, v23.8h, v31.8h // v24 <- denum + + fdiv v16.8h, v16.8h, v24.8h + fdiv v17.8h, v17.8h, v25.8h + fdiv v18.8h, v18.8h, v26.8h + fdiv v19.8h, v19.8h, v27.8h + + st1 { v16.8h, v17.8h, v18.8h, v19.8h }, [x0], #64 + + subs x1, x1, #32 + cmp x1, #32 + bge .loop4 + + cmp x1, #0 + beq .return + +.loop: + ld1 { v16.8h }, [x0] + + fmax v16.8h, v16.8h, v5.8h + fmin v16.8h, v16.8h, v6.8h // v16 <- x + fmul v20.8h, v16.8h, v16.8h // v20 <- x2 + + dup v24.8h, v0.h[3] + fmla v24.8h, v20.8h, v0.h[2] + fmul v16.8h, v16.8h, v24.8h // v16 <- numerator + + dup v28.8h, v0.h[5] + fmla v28.8h, v20.8h, v0.h[4] + dup v24.8h, v0.h[6] + fmla v24.8h, v20.8h, v28.8h // v24 <- denum + + fdiv v16.8h, v16.8h, v24.8h + + st1 { v16.8h }, [x0], #16 + + subs x1, x1, #8 + bne .loop + +.return: + ret + +.coeffs_num: + {{ -3.84 | float16 }} + {{ 3.84 | float16 }} + {{ 0.082654955 | float16 }} // alpha + {{ 0.99963124 | float16 }} + + {{ 0.0065383179 | float16 }} // beta + {{ 0.41401828 | float16 }} + {{ 1.0 | float16 }} + {{ 0 | float16 }} // padding diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/dispatcher.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/dispatcher.j2 new file mode 100644 index 0000000..5663fd2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/dispatcher.j2 @@ -0,0 +1,37 @@ +// vim: ft=arm + +.non_linear: + sub x0, x0, 40 + +.non_linear_loop: + add x0, x0, 40 + ldr x2, [x0] + + mov x4, #{{ jump_table | length }} + + cmp x2, #{{ jump_table | length }} + csel x2, x2, x4, lt + cmp x2, #0 + csel x2, x4, x2, lt + + adr x3, .jmp_table + add x3, x3, x2, LSL#2 + br x3 + +.jmp_table: +{% for j in jump_table %} + b .{{j}} +{% endfor %} + b .unsupported + + add x0, x2, #4000 + b .return + +.unsupported: + mov x0, #1 + b .return + +.done: + mov x0, 0 + b .return + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/dummy_fmla_no_pragma.S b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/dummy_fmla_no_pragma.S new file mode 100644 index 0000000..3af092c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/dummy_fmla_no_pragma.S @@ -0,0 +1,13 @@ +// vim: ft=arm + +// serves as a canary build file to figure out which flag combination will accept half precision fmla + +.text +.align 4 + +// .cpu generic+fp+simd+fp16 +.global foo +foo: + fmla v16.8h, v0.8h, v8.h[0] + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/dummy_fmla_pragma.S b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/dummy_fmla_pragma.S new file mode 100644 index 0000000..6fb6105 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64fp16/dummy_fmla_pragma.S @@ -0,0 +1,13 @@ +// vim: ft=arm + +// serves as a canary build file to figure out which flag combination will accept half precision fmla + +.text +.align 4 + +.cpu generic+fp+simd+fp16 +.global foo +foo: + fmla v16.8h, v0.8h, v8.h[0] + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_4s_ops.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_4s_ops.j2 new file mode 100644 index 0000000..5770b78 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_4s_ops.j2 @@ -0,0 +1,81 @@ + +{% macro scalar(label, op, from, to, flipped=false) %} +.{{label}}: + add x2, x0, #8 + ld1 {v0.s}[0], [ x2 ] + dup v0.4s, v0.s[0] + {% if flipped %} + {% for reg in range(from, to + 1) %} + {{op}} v{{reg}}.4s, v{{reg}}.4s, v0.4s + {% endfor %} + {% else %} + {% for reg in range(from, to + 1) %} + {{op}} v{{reg}}.4s, v0.4s, v{{reg}}.4s + {% endfor %} + {% endif %} + + b .non_linear_loop +{% endmacro %} + +{% macro per_row(label, op, mr, from, to, flipped=false) %} +.{{label}}: + ldr x2, [x0, #8] + +{% set mr_over_4 = mr // 4 %} +{% set mr_over_4_min_1 = mr // 4 - 1 %} + +{% for reg in range(0, mr_over_4_min_1 + 1) %} + ldr q{{reg}}, [ x2 ], #16 +{% endfor %} + +{% if flipped %} + {% for acc in range(from, to + 1) %} + {% set other = (acc - from) % mr_over_4 %} + {{op}} v{{acc}}.4s, v{{acc}}.4s, v{{other}}.4s + {% endfor %} +{% else %} + {% for acc in range(from, to + 1) %} + {% set other = (acc - from) % mr_over_4 %} + {{op}} v{{acc}}.4s, v{{other}}.4s, v{{acc}}.4s + {% endfor %} +{% endif %} + +b .non_linear_loop +{% endmacro %} + +{% macro per_col(label, op, mr, from, to, flipped=false) %} +.{{label}}: + ldr x2, [x0, #8] + +{% set mr_over_4 = mr // 4 %} +{% set cols = (to + 1 - from) // mr_over_4 %} + +{% set loads = cols // 4 %} + +{%if cols == 1 %} + ld1 {v0.s}[0], [ x2 ] +{% elif cols == 3 %} + ldr d0, [ x2 ], #8 + ld1 {v0.s}[2], [ x2 ] +{% else %} + {% for reg in range(1, loads + 1) %} + ldr q{{ reg - 1 }}, [ x2 ], #16 + {% endfor %} +{% endif %} + +// {{mr}} {{cols}} + +{% for col in range(1, cols + 1) %} + dup v3.4s, v{{ (col - 1) // 4 }}.s[{{ (col - 1) % 4 }}] + {% for row in range(1, mr_over_4 + 1) %} + {% set acc = (col - 1) * mr_over_4 + row - 1 + from %} + {% if flipped %} + {{op}} v{{acc}}.4s, v{{acc}}.4s, v3.4s + {% else %} + {{op}} v{{acc}}.4s, v3.4s, v{{acc}}.4s + {% endif %} + {% endfor %} +{% endfor %} + +b .non_linear_loop +{% endmacro %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_w_no_preload.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_w_no_preload.S.raw new file mode 100644 index 0000000..04deaee --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_w_no_preload.S.raw @@ -0,0 +1,69 @@ +fmla v8.4s, v0.4s, v4.s[0] +ldr w4, [x1], #4 +fmla v9.4s, v1.4s, v4.s[0] +ldr w20, [x2], #4 +fmla v10.4s, v2.4s, v4.s[0] +ldr w5, [x1], #4 + +fmla v11.4s, v0.4s, v4.s[1] +ldr w21, [x2], #4 +fmla v12.4s, v1.4s, v4.s[1] +ldr w6, [x1], #4 +fmla v13.4s, v2.4s, v4.s[1] +ldr w22, [x2], #4 + +fmla v14.4s, v0.4s, v4.s[2] +ldr w7, [x1], #4 +fmla v15.4s, v1.4s, v4.s[2] +ldr w23, [x2], #4 +fmla v16.4s, v2.4s, v4.s[2] +ldr w8, [x1], #4 +fmla v17.4s, v0.4s, v4.s[3] +ldr w24, [x2], #4 +fmla v18.4s, v1.4s, v4.s[3] +ldr w9, [x1], #4 +fmla v19.4s, v2.4s, v4.s[3] +ldr w25, [x2], #4 + +fmla v20.4s, v0.4s, v5.s[0] +ldr w10, [x1], #4 +fmla v21.4s, v1.4s, v5.s[0] +ldr w26, [x2], #4 +fmla v22.4s, v2.4s, v5.s[0] +ldr w11, [x1], #4 +fmla v23.4s, v0.4s, v5.s[1] +ldr w27, [x2], #4 +fmla v24.4s, v1.4s, v5.s[1] +ldr w12, [x1], #4 +fmla v25.4s, v2.4s, v5.s[1] + +fmla v26.4s, v0.4s, v5.s[2] +ldr w13, [x1], #4 +fmla v27.4s, v1.4s, v5.s[2] +fmla v28.4s, v2.4s, v5.s[2] +ldr w14, [x1], #4 +fmla v29.4s, v0.4s, v5.s[3] +fmla v30.4s, v1.4s, v5.s[3] +ldr w15, [x1], #4 +fmla v31.4s, v2.4s, v5.s[3] + +ins v0.s[0], w4 +ins v1.s[0], w8 +ins v2.s[0], w12 +ins v4.s[0], w20 +ins v5.s[0], w24 +ins v0.s[1], w5 +ins v1.s[1], w9 +ins v2.s[1], w13 +ins v4.s[1], w21 +ins v5.s[1], w25 +ins v0.s[2], w6 +ins v1.s[2], w10 +ins v2.s[2], w14 +ins v4.s[2], w22 +ins v5.s[2], w26 +ins v0.s[3], w7 +ins v1.s[3], w11 +ins v2.s[3], w15 +ins v4.s[3], w23 +ins v5.s[3], w27 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_w_preload.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_w_preload.S.raw new file mode 100644 index 0000000..f97e252 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_w_preload.S.raw @@ -0,0 +1,82 @@ +fmla v8.4s, v0.4s, v4.s[0] + ldr w4, [x1] +fmla v9.4s, v1.4s, v4.s[0] + ldr w20, [x2], #4 +fmla v10.4s, v2.4s, v4.s[0] + ldr w5, [x1, #4] + +fmla v11.4s, v0.4s, v4.s[1] + ldr w21, [x2], #4 +fmla v12.4s, v1.4s, v4.s[1] + ldr w6, [x1, #8] +fmla v13.4s, v2.4s, v4.s[1] + ldr w22, [x2], #4 + +fmla v14.4s, v0.4s, v4.s[2] + ldr w7, [x1, #12] +fmla v15.4s, v1.4s, v4.s[2] + ldr w23, [x2], #4 +fmla v16.4s, v2.4s, v4.s[2] + ldr w8, [x1, #16] +fmla v17.4s, v0.4s, v4.s[3] + ldr w24, [x2], #4 +fmla v18.4s, v1.4s, v4.s[3] + ldr w9, [x1, #20] +fmla v19.4s, v2.4s, v4.s[3] + ldr w25, [x2], #4 + +fmla v20.4s, v0.4s, v5.s[0] + ldr w10, [x1, #24] +fmla v21.4s, v1.4s, v5.s[0] + ldr w26, [x2], #4 +fmla v22.4s, v2.4s, v5.s[0] + ldr w11, [x1, #28] +fmla v23.4s, v0.4s, v5.s[1] + ldr w27, [x2], #4 +fmla v24.4s, v1.4s, v5.s[1] + ldr w12, [x1, #32] +fmla v25.4s, v2.4s, v5.s[1] + ldr w13, [x1, #36] + +fmla v26.4s, v0.4s, v5.s[2] + ldr w14, [x1, #40] +fmla v27.4s, v1.4s, v5.s[2] + ldr w15, [x1, #44] +fmla v28.4s, v2.4s, v5.s[2] + prfm pldl1keep, [x1, #512] +fmla v29.4s, v0.4s, v5.s[3] + add x1, x1, #48 +fmla v30.4s, v1.4s, v5.s[3] + prfm pldl1keep, [x2, #384] +fmla v31.4s, v2.4s, v5.s[3] + + ins v0.s[0], w4 + + ins v1.s[0], w8 + ins v2.s[0], w12 + + ins v4.s[0], w20 + ins v5.s[0], w24 + + ins v0.s[1], w5 + ins v1.s[1], w9 + + ins v2.s[1], w13 + ins v4.s[1], w21 + + ins v5.s[1], w25 + ins v0.s[2], w6 + + ins v1.s[2], w10 + ins v2.s[2], w14 + + ins v4.s[2], w22 + ins v5.s[2], w26 + + ins v0.s[3], w7 + ins v1.s[3], w11 + + ins v2.s[3], w15 + ins v4.s[3], w23 + ins v5.s[3], w27 + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_x_preload.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_x_preload.S.raw new file mode 100644 index 0000000..0ea9472 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_x_preload.S.raw @@ -0,0 +1,66 @@ +fmla v8.4s, v0.4s, v4.s[0] + ldr x4, [x1] +fmla v9.4s, v1.4s, v4.s[0] + ldr x20, [x2] +fmla v10.4s, v2.4s, v4.s[0] + ldr x5, [x1, #8] + +fmla v11.4s, v0.4s, v4.s[1] + ldr x21, [x2, #8] +fmla v12.4s, v1.4s, v4.s[1] + ldr x6, [x1, #16] +fmla v13.4s, v2.4s, v4.s[1] + ldr x22, [x2, #16] + +fmla v14.4s, v0.4s, v4.s[2] + ldr x7, [x1, #24] +fmla v15.4s, v1.4s, v4.s[2] + ldr x23, [x2, #24] +fmla v16.4s, v2.4s, v4.s[2] + ldr x8, [x1, #32] +fmla v17.4s, v0.4s, v4.s[3] +fmla v18.4s, v1.4s, v4.s[3] + ldr x9, [x1, #40] +fmla v19.4s, v2.4s, v4.s[3] + +fmla v20.4s, v0.4s, v5.s[0] + prfm pldl1keep, [x1, #256] +fmla v21.4s, v1.4s, v5.s[0] + prfm pldl1keep, [x1, #320] +fmla v22.4s, v2.4s, v5.s[0] + prfm pldl1keep, [x1, #384] +fmla v23.4s, v0.4s, v5.s[1] + prfm pldl1keep, [x1, #448] +fmla v24.4s, v1.4s, v5.s[1] + prfm pldl1keep, [x2, #256] +fmla v25.4s, v2.4s, v5.s[1] + prfm pldl1keep, [x2, #320] + +fmla v26.4s, v0.4s, v5.s[2] +fmla v27.4s, v1.4s, v5.s[2] +fmla v28.4s, v2.4s, v5.s[2] + prfm pldl1keep, [x1, #512] +fmla v29.4s, v0.4s, v5.s[3] + add x1, x1, #48 +fmla v30.4s, v1.4s, v5.s[3] + prfm pldl1keep, [x2, #384] +fmla v31.4s, v2.4s, v5.s[3] + add x2, x2, #32 + + + ins v0.d[0], x4 + ins v2.d[0], x8 + + ins v4.d[0], x20 + ins v5.d[0], x22 + + ins v0.d[1], x5 + ins v2.d[1], x9 + + ins v4.d[1], x21 + ins v1.d[0], x6 + + ins v1.d[1], x7 + + ins v5.d[1], x23 + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/naive.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/naive.S.raw new file mode 100644 index 0000000..67be44d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop1/naive.S.raw @@ -0,0 +1,34 @@ +fmla v8.4s, v0.4s, v4.s[0] +fmla v9.4s, v1.4s, v4.s[0] +fmla v10.4s, v2.4s, v4.s[0] + +fmla v11.4s, v0.4s, v4.s[1] +fmla v12.4s, v1.4s, v4.s[1] +fmla v13.4s, v2.4s, v4.s[1] + +fmla v14.4s, v0.4s, v4.s[2] +fmla v15.4s, v1.4s, v4.s[2] +fmla v16.4s, v2.4s, v4.s[2] + +fmla v17.4s, v0.4s, v4.s[3] +fmla v18.4s, v1.4s, v4.s[3] +fmla v19.4s, v2.4s, v4.s[3] + +fmla v20.4s, v0.4s, v5.s[0] +fmla v21.4s, v1.4s, v5.s[0] +fmla v22.4s, v2.4s, v5.s[0] + +fmla v23.4s, v0.4s, v5.s[1] +fmla v24.4s, v1.4s, v5.s[1] +fmla v25.4s, v2.4s, v5.s[1] + +fmla v26.4s, v0.4s, v5.s[2] +fmla v27.4s, v1.4s, v5.s[2] +fmla v28.4s, v2.4s, v5.s[2] + +fmla v29.4s, v0.4s, v5.s[3] +fmla v30.4s, v1.4s, v5.s[3] +fmla v31.4s, v2.4s, v5.s[3] + +ld1 {{ v0.4s, v1.4s, v2.4s }}, [x1], #48 +ld1 {{ v4.4s, v5.4s }}, [x2], #32 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop2/cortex_a55.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop2/cortex_a55.S.raw new file mode 100644 index 0000000..f1ba56c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_12x8/packed_packed_loop2/cortex_a55.S.raw @@ -0,0 +1,107 @@ +// mul a: v0, v1, v2, b: v4, v5 +// load a: d3/x23, d6/x26, d7/x27 +// load b: x4, x5, x6, x7 + +fmla v8.4s, v0.4s, v4.s[0] +ldr d3, [x1], #8 +fmla v9.4s, v1.4s, v4.s[0] +ldr x4, [x2], #8 +fmla v10.4s, v2.4s, v4.s[0] +ldr x23, [x1], #8 +fmla v11.4s, v0.4s, v4.s[1] +ldr x5, [x2], #8 +fmla v12.4s, v1.4s, v4.s[1] +ldr d6, [x1], #8 +fmla v13.4s, v2.4s, v4.s[1] +ldr x6, [x2], #8 +fmla v14.4s, v0.4s, v4.s[2] +ldr x26, [x1], #8 +fmla v15.4s, v1.4s, v4.s[2] +ldr x7, [x2], #8 +fmla v16.4s, v2.4s, v4.s[2] +ldr d7, [x1], #8 +fmla v17.4s, v0.4s, v4.s[3] +ldr x27, [x1], #8 +fmla v18.4s, v1.4s, v4.s[3] + +fmla v19.4s, v2.4s, v4.s[3] +fmla v20.4s, v0.4s, v5.s[0] + +// ins b: v4 <- x4/x5 +// ins a: d3/x23, d6/x26, d7/x27 + +ins v4.d[0], x4 +fmla v21.4s, v1.4s, v5.s[0] +ins v4.d[1], x5 +fmla v22.4s, v2.4s, v5.s[0] +fmla v23.4s, v0.4s, v5.s[1] + +fmla v24.4s, v1.4s, v5.s[1] +fmla v25.4s, v2.4s, v5.s[1] +fmla v26.4s, v0.4s, v5.s[2] +fmla v27.4s, v1.4s, v5.s[2] +fmla v28.4s, v2.4s, v5.s[2] +fmla v29.4s, v0.4s, v5.s[3] +ins v3.d[1], x23 +fmla v30.4s, v1.4s, v5.s[3] +ins v6.d[1], x26 +fmla v31.4s, v2.4s, v5.s[3] +ins v7.d[1], x27 + +// mul a: v3, v6, v7, b: v4, v5 +// ins b, v5 <- x6, x7 +// load a: d0/x20, d1/x21, d2/x22 +// load b: x4, x5 + +fmla v8.4s, v3.4s, v4.s[0] +ins v5.d[0], x6 +fmla v9.4s, v6.4s, v4.s[0] +ins v5.d[1], x7 +fmla v10.4s, v7.4s, v4.s[0] +ldr d0, [x1], #8 +fmla v11.4s, v3.4s, v4.s[1] +ldr x4, [x2], #8 +fmla v12.4s, v6.4s, v4.s[1] +ldr x20, [x1], #8 +fmla v13.4s, v7.4s, v4.s[1] +ldr x5, [x2], #8 +fmla v14.4s, v3.4s, v4.s[2] +ldr d1, [x1], #8 +fmla v15.4s, v6.4s, v4.s[2] +ldr x6, [x2], #8 +fmla v16.4s, v7.4s, v4.s[2] +ldr x21, [x1], #8 +fmla v17.4s, v3.4s, v4.s[3] +ldr x7, [x2], #8 + +// load b: x6, x7 +fmla v18.4s, v6.4s, v4.s[3] +ldr d2, [x1], #8 +fmla v19.4s, v7.4s, v4.s[3] +ldr x22, [x1], #8 +fmla v20.4s, v3.4s, v5.s[0] +fmla v21.4s, v6.4s, v5.s[0] +fmla v22.4s, v7.4s, v5.s[0] +fmla v23.4s, v3.4s, v5.s[1] +fmla v24.4s, v6.4s, v5.s[1] +fmla v25.4s, v7.4s, v5.s[1] + +// ins a: d0/x20, d1/x21, d2/x22 +fmla v26.4s, v3.4s, v5.s[2] +ins v0.d[1], x20 +fmla v27.4s, v6.4s, v5.s[2] +ins v1.d[1], x21 +fmla v28.4s, v7.4s, v5.s[2] +ins v2.d[1], x22 + +// ins b: v4 <- x4, x5 +fmla v29.4s, v3.4s, v5.s[3] +ins v4.d[0], x4 +fmla v30.4s, v6.4s, v5.s[3] +ins v4.d[1], x5 +fmla v31.4s, v7.4s, v5.s[3] + +// ins b: v5 <- x6, x7 +ins v5.d[0], x6 +ins v5.d[1], x7 + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_12x8_core.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_12x8_core.S.j2 new file mode 100644 index 0000000..a482149 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_12x8_core.S.j2 @@ -0,0 +1,163 @@ +// vim: ft=arm + +// C tile regs: +// - x19-x29 to preserve (but x19, x28, x29 not used) +// - d8..d15 to preserve +// - v16 to v31, no need to preserve +// +// v8 v11 v14 v17 v20 v23 v26 v29 +// v9 v12 v15 v18 v21 v24 v27 v30 +// v10 v13 v16 v19 v22 v25 v28 v31 + +// no preservation for v0-v7: +// packed A buffering (2x8 values): rotating over v0..v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +.cpu generic+fp+simd +.global {{G}}arm64simd_mmm_f32_12x8_{{core}}_{{suffix}} +{{G}}arm64simd_mmm_f32_12x8_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.j2" %} + +.add_mat_mul: + ldr x2, [x0, #24] // b + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + + ld1 { v0.4s, v1.4s, v2.4s }, [ x1 ], #48 + ld1 { v4.4s, v5.4s }, [ x2 ], #32 + +{% set packed_packed_loop1 %} + {% if core == "a53" %} + {% include "arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_x_preload.S.raw" %} + {% else %} + {% include "arm64simd_mmm_f32_12x8/packed_packed_loop1/naive.S.raw" %} + {% endif %} +{% endset %} + +{% set packed_packed_loop2 %} + {% if core == "a55" %} + {% include "arm64simd_mmm_f32_12x8/packed_packed_loop2/cortex_a55.S.raw" %} + {% else %} + {{ packed_packed_loop1 }} + {{ packed_packed_loop1 }} + {% endif %} +{% endset %} + + cmp x3, #4 + blt .packed_packed_loop_1 + +.p2align 4 +.packed_packed_loop_4: + {{ packed_packed_loop2 }} + {{ packed_packed_loop2 }} + + sub x3, x3, #4 + cmp x3, #4 + bge .packed_packed_loop_4 + + cmp x3, #0 + beq .non_linear_loop + +.p2align 4 +.packed_packed_loop_1: + {{ packed_packed_loop1 }} + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +{% set from = 8 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_scalars.j2" %} +{% set mr = 12 %}{% set from = 8 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_per_rows.j2" %} +{% set mr = 12 %}{% set from = 8 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_per_cols.j2" %} +{% set from = 8 %}{% set to = 31 %}{% include "arm64simd_mmm_load_tile.j2" %} + +.add_unicast: + ldp x5, x6, [x0, #8 ] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + {% for col in range(0, 8) %} + mov x4, x5 + {% for reg in range(0, 3) %} + {% for lane in range(0, 4) %} + ld1 {v0.s}[{{lane}}], [ x4 ], x6 + {% endfor %} + fadd v{{ col * 3 + 8 + reg }}.4s, v{{ col * 3 + 8 + reg }}.4s, v0.4s + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x2, [x0, #8] + ldr x3, [x0, #16] + + ld1 { v0.4s, v1.4s, v2.4s }, [ x2 ] + ld1 { v4.4s, v5.4s }, [ x3 ] + + {% for col in range(0, 8) %} + {% for reg in range(0, 3) %} + fmla v{{ col * 3 + 8 + reg }}.4s, v{{reg}}.4s, v{{ col // 4 + 4 }}.s[{{ col % 4 }}] + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x6, #4 + bne .store_strides_generic + + {% for col in range(0, 8) %} + str q{{ col * 3 + 8 }}, [ x5 ] + str q{{ col * 3 + 9 }}, [ x5, #16 ] + str q{{ col * 3 + 10 }}, [ x5, #32 ] + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.store_strides_generic: + {% for col in range(0, 8) %} + mov x4, x5 + {% for reg in range(0, 3) %} + {% for lane in range(0, 4) %} + st1 { v{{ col * 3 + 8 + reg }}.s }[{{lane}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.return: + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_16x4/packed_packed_loop1/cortex_a53.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_16x4/packed_packed_loop1/cortex_a53.S.raw new file mode 100644 index 0000000..7c2225f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_16x4/packed_packed_loop1/cortex_a53.S.raw @@ -0,0 +1,46 @@ +fmla v16.4s, v0.4s, v4.s[0] +ldr x5, [x1] +fmla v17.4s, v1.4s, v4.s[0] +ldr x6, [x1, #8] +fmla v18.4s, v2.4s, v4.s[0] +ldr x7, [x1, #16] +fmla v19.4s, v3.4s, v4.s[0] +ldr x8, [x1, #24] +fmla v20.4s, v0.4s, v4.s[1] +ldr x9, [x1, #32] +fmla v21.4s, v1.4s, v4.s[1] +ldr x10, [x1, #40] +fmla v22.4s, v2.4s, v4.s[1] +ldr x11, [x1, #48] +fmla v23.4s, v3.4s, v4.s[1] +ldr x12, [x1, #56] + +fmla v24.4s, v0.4s, v4.s[2] +ldr x24, [x2] +fmla v25.4s, v1.4s, v4.s[2] +ldr x25, [x2, #8] +fmla v26.4s, v2.4s, v4.s[2] +add x1, x1, #64 +fmla v27.4s, v3.4s, v4.s[2] +add x2, x2, #16 +fmla v28.4s, v0.4s, v4.s[3] +prfm pldl1keep, [x1, #256] +fmla v29.4s, v1.4s, v4.s[3] +prfm pldl1keep, [x1, #320] +fmla v30.4s, v2.4s, v4.s[3] +prfm pldl1keep, [x2, #256] +fmla v31.4s, v3.4s, v4.s[3] +prfm pldl1keep, [x2, #320] + +ins v0.d[0], x5 +ins v2.d[0], x9 +ins v1.d[0], x7 +ins v3.d[0], x11 +ins v4.d[0], x24 + +ins v0.d[1], x6 +ins v2.d[1], x10 +ins v1.d[1], x8 +ins v3.d[1], x12 +ins v4.d[1], x25 + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_16x4/packed_packed_loop1/naive.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_16x4/packed_packed_loop1/naive.S.raw new file mode 100644 index 0000000..6374665 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_16x4/packed_packed_loop1/naive.S.raw @@ -0,0 +1,21 @@ + +fmla v16.4s, v0.4s, v4.s[0] +fmla v17.4s, v1.4s, v4.s[0] +fmla v18.4s, v2.4s, v4.s[0] +fmla v19.4s, v3.4s, v4.s[0] +fmla v20.4s, v0.4s, v4.s[1] +fmla v21.4s, v1.4s, v4.s[1] +fmla v22.4s, v2.4s, v4.s[1] +fmla v23.4s, v3.4s, v4.s[1] + +fmla v24.4s, v0.4s, v4.s[2] +fmla v25.4s, v1.4s, v4.s[2] +fmla v26.4s, v2.4s, v4.s[2] +fmla v27.4s, v3.4s, v4.s[2] +fmla v28.4s, v0.4s, v4.s[3] +fmla v29.4s, v1.4s, v4.s[3] +fmla v30.4s, v2.4s, v4.s[3] +fmla v31.4s, v3.4s, v4.s[3] + +ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [ x1 ], #64 +ld1 {{ v4.4s }}, [ x2 ], #16 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_16x4/packed_packed_loop2/cortex_a55.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_16x4/packed_packed_loop2/cortex_a55.S.raw new file mode 100644 index 0000000..c0b2f50 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_16x4/packed_packed_loop2/cortex_a55.S.raw @@ -0,0 +1,73 @@ +// mul a: v0, v1, v2, v3 b: v4 +// load a: v5(d5/x5), v6(d6,x6), v7(d7,x7), v8(d8, x8) +// load b: v9(d9/x9) + +fmla v16.4s, v0.4s, v4.s[0] +ldr d5, [x1], #8 +fmla v17.4s, v1.4s, v4.s[0] +ldr d9, [x2], #8 +fmla v18.4s, v2.4s, v4.s[0] +ldr x5, [x1], #8 +fmla v19.4s, v3.4s, v4.s[0] +ldr x9, [x2], #8 +fmla v20.4s, v0.4s, v4.s[1] +ldr d6, [x1], #8 +fmla v21.4s, v1.4s, v4.s[1] +ldr x6, [x1], #8 +fmla v22.4s, v2.4s, v4.s[1] +ldr d7, [x1], #8 +fmla v23.4s, v3.4s, v4.s[1] +ldr x7, [x1], #8 + +fmla v24.4s, v0.4s, v4.s[2] +ldr d8, [x1], #8 +fmla v25.4s, v1.4s, v4.s[2] +ldr x8, [x1], #8 +fmla v26.4s, v2.4s, v4.s[2] +ins v5.d[1], x5 +fmla v27.4s, v3.4s, v4.s[2] +ins v6.d[1], x6 +fmla v28.4s, v0.4s, v4.s[3] +ins v7.d[1], x7 +fmla v29.4s, v1.4s, v4.s[3] +ins v8.d[1], x8 +fmla v30.4s, v2.4s, v4.s[3] +ins v9.d[1], x9 +fmla v31.4s, v3.4s, v4.s[3] + +// mul a: v5, v6, v7, v8 b: v9 +// load a: v0(d0/x5), v1(d1,x6), v2(d2,x7), v3(d3, x8) +// load b: v4(d4/x9) + +fmla v16.4s, v5.4s, v9.s[0] +ldr d0, [x1], #8 +fmla v17.4s, v6.4s, v9.s[0] +ldr d4, [x2], #8 +fmla v18.4s, v7.4s, v9.s[0] +ldr x5, [x1], #8 +fmla v19.4s, v8.4s, v9.s[0] +ldr x9, [x2], #8 +fmla v20.4s, v5.4s, v9.s[1] +ldr d1, [x1], #8 +fmla v21.4s, v6.4s, v9.s[1] +ldr x6, [x1], #8 +fmla v22.4s, v7.4s, v9.s[1] +ldr d2, [x1], #8 +fmla v23.4s, v8.4s, v9.s[1] +ldr x7, [x1], #8 + +fmla v24.4s, v5.4s, v9.s[2] +ldr d3, [x1], #8 +fmla v25.4s, v6.4s, v9.s[2] +ldr x8, [x1], #8 +fmla v26.4s, v7.4s, v9.s[2] +ins v0.d[1], x5 +fmla v27.4s, v8.4s, v9.s[2] +ins v1.d[1], x6 +fmla v28.4s, v5.4s, v9.s[3] +ins v2.d[1], x7 +fmla v29.4s, v6.4s, v9.s[3] +ins v3.d[1], x8 +fmla v30.4s, v7.4s, v9.s[3] +ins v4.d[1], x9 +fmla v31.4s, v8.4s, v9.s[3] diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_16x4_core.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_16x4_core.S.j2 new file mode 100644 index 0000000..8520625 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_16x4_core.S.j2 @@ -0,0 +1,174 @@ +// vim: ft=arm + +// C tile regs: v16 to v31, (scratch) +// - x19-x29 to preserve (but x19, x28, x29 not used) +// - d8..d15 to preserve +// - v16 to v31, no need to preserve + +// v8 is used, d8 (lower half) must preserved +// v0-v7 (scratch registers) +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +.cpu generic+fp+simd +.global {{G}}arm64simd_mmm_f32_16x4_{{core}}_{{suffix}} +{{G}}arm64simd_mmm_f32_16x4_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.j2" %} + +.add_mat_mul: + ldr x2, [x0, #24] // b + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x1 ], #64 + ld1 { v4.4s }, [ x2 ], #16 + +{% set packed_packed_loop1 %} + {% if core == "a53" %} + {% include "arm64simd_mmm_f32_16x4/packed_packed_loop1/cortex_a53.S.raw" %} + {% else %} + {% include "arm64simd_mmm_f32_16x4/packed_packed_loop1/naive.S.raw" %} + {% endif %} +{% endset %} + +{% set packed_packed_loop2 %} + {% if core == "a55" %} + {% include "arm64simd_mmm_f32_16x4/packed_packed_loop2/cortex_a55.S.raw" %} + {% else %} + {{ packed_packed_loop1 }} + {{ packed_packed_loop1 }} + {% endif %} +{% endset %} + + cmp x3, #4 + blt .packed_packed_loop_1 + +.p2align 4 +.packed_packed_loop_4: + {{ packed_packed_loop2 }} + {{ packed_packed_loop2 }} + + sub x3, x3, #4 + cmp x3, #4 + bge .packed_packed_loop_4 + + cmp x3, #0 + beq .non_linear_loop + +.p2align 4 +.packed_packed_loop_1: + {{ packed_packed_loop1 }} + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_scalars.j2" %} +{% set mr = 16 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_per_rows.j2" %} +{% set mr = 16 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_per_cols.j2" %} +{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_load_tile.j2" %} + +.add_unicast: + ldp x5, x6, [x0, #8] + ldp x7, x8, [x0, #24] + + {% for col in range(0, 4) %} + mov x4, x5 + {% for reg in range(0, 4) %} + {% for lane in range(0, 4) %} + ld1 {v0.s}[{{lane}}], [ x4 ], x6 + {% endfor %} + fadd v{{ col * 4 + 16 + reg }}.4s, v{{ col * 4 + 16 + reg }}.4s, v0.4s + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x2, [x0, #8] + ldr x3, [x0, #16] + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x2 ] + ld1 { v4.4s }, [ x3 ] + + fmla v16.4s, v0.4s, v4.s[0] + fmla v17.4s, v1.4s, v4.s[0] + fmla v18.4s, v2.4s, v4.s[0] + fmla v19.4s, v3.4s, v4.s[0] + fmla v20.4s, v0.4s, v4.s[1] + fmla v21.4s, v1.4s, v4.s[1] + fmla v22.4s, v2.4s, v4.s[1] + fmla v23.4s, v3.4s, v4.s[1] + + fmla v24.4s, v0.4s, v4.s[2] + fmla v25.4s, v1.4s, v4.s[2] + fmla v26.4s, v2.4s, v4.s[2] + fmla v27.4s, v3.4s, v4.s[2] + fmla v28.4s, v0.4s, v4.s[3] + fmla v29.4s, v1.4s, v4.s[3] + fmla v30.4s, v2.4s, v4.s[3] + fmla v31.4s, v3.4s, v4.s[3] + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x6, #4 + bne .store_strides_generic + + {% for col in range(0, 4) %} + str q{{ col * 4 + 16 + 0 }}, [ x5 ] + str q{{ col * 4 + 16 + 1 }}, [ x5, #16 ] + str q{{ col * 4 + 16 + 2 }}, [ x5, #32 ] + str q{{ col * 4 + 16 + 3 }}, [ x5, #48 ] + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.store_strides_generic: + + {% for col in range(0, 4) %} + mov x4, x5 + {% for reg in range(0, 4) %} + {% for lane in range(0, 4) %} + st1 { v{{ col * 4 + 16 + reg }}.s }[{{lane}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.return: + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_24x4/loop2/cortex_a55.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_24x4/loop2/cortex_a55.S.raw new file mode 100644 index 0000000..b273638 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_24x4/loop2/cortex_a55.S.raw @@ -0,0 +1,73 @@ +// mul a: v0, v1, v2, v3, v4, v5 b: v7 +// load a: v5(d5/x5), v6(d6,x6), v7(d7,x7), v8(d8, x8) +// load b: v9(d9/x9) + +fmla v16.4s, v0.4s, v4.s[0] +ldr d5, [x1], #8 +fmla v17.4s, v1.4s, v4.s[0] +ldr d9, [x2], #8 +fmla v18.4s, v2.4s, v4.s[0] +ldr x5, [x1], #8 +fmla v19.4s, v3.4s, v4.s[0] +ldr x9, [x2], #8 +fmla v20.4s, v0.4s, v4.s[1] +ldr d6, [x1], #8 +fmla v21.4s, v1.4s, v4.s[1] +ldr x6, [x1], #8 +fmla v22.4s, v2.4s, v4.s[1] +ldr d7, [x1], #8 +fmla v23.4s, v3.4s, v4.s[1] +ldr x7, [x1], #8 + +fmla v24.4s, v0.4s, v4.s[2] +ldr d8, [x1], #8 +fmla v25.4s, v1.4s, v4.s[2] +ldr x8, [x1], #8 +fmla v26.4s, v2.4s, v4.s[2] +ins v5.d[1], x5 +fmla v27.4s, v3.4s, v4.s[2] +ins v6.d[1], x6 +fmla v28.4s, v0.4s, v4.s[3] +ins v7.d[1], x7 +fmla v29.4s, v1.4s, v4.s[3] +ins v8.d[1], x8 +fmla v30.4s, v2.4s, v4.s[3] +ins v9.d[1], x9 +fmla v31.4s, v3.4s, v4.s[3] + +// mul a: v5, v6, v7, v8 b: v9 +// load a: v0(d0/x5), v1(d1,x6), v2(d2,x7), v3(d3, x8) +// load b: v4(d4/x9) + +fmla v16.4s, v5.4s, v9.s[0] +ldr d0, [x1], #8 +fmla v17.4s, v6.4s, v9.s[0] +ldr d4, [x2], #8 +fmla v18.4s, v7.4s, v9.s[0] +ldr x5, [x1], #8 +fmla v19.4s, v8.4s, v9.s[0] +ldr x9, [x2], #8 +fmla v20.4s, v5.4s, v9.s[1] +ldr d1, [x1], #8 +fmla v21.4s, v6.4s, v9.s[1] +ldr x6, [x1], #8 +fmla v22.4s, v7.4s, v9.s[1] +ldr d2, [x1], #8 +fmla v23.4s, v8.4s, v9.s[1] +ldr x7, [x1], #8 + +fmla v24.4s, v5.4s, v9.s[2] +ldr d3, [x1], #8 +fmla v25.4s, v6.4s, v9.s[2] +ldr x8, [x1], #8 +fmla v26.4s, v7.4s, v9.s[2] +ins v0.d[1], x5 +fmla v27.4s, v8.4s, v9.s[2] +ins v1.d[1], x6 +fmla v28.4s, v5.4s, v9.s[3] +ins v2.d[1], x7 +fmla v29.4s, v6.4s, v9.s[3] +ins v3.d[1], x8 +fmla v30.4s, v7.4s, v9.s[3] +ins v4.d[1], x9 +fmla v31.4s, v8.4s, v9.s[3] diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a53.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a53.S.raw new file mode 100644 index 0000000..b77e405 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a53.S.raw @@ -0,0 +1,68 @@ + +fmla v8.4s, v0.4s, v7.s[0] + ldr x4, [x1] +fmla v9.4s, v1.4s, v7.s[0] + ldr x5, [x1, #8] +fmla v10.4s, v2.4s, v7.s[0] + ldr x6, [x1, #16] +fmla v11.4s, v3.4s, v7.s[0] + ldr x7, [x1, #24] +fmla v12.4s, v4.4s, v7.s[0] + ldr x8, [x1, #32] +fmla v13.4s, v5.4s, v7.s[0] + ldr x9, [x1, #40] + +fmla v14.4s, v0.4s, v7.s[1] + ldr x10, [x1, #48] +fmla v15.4s, v1.4s, v7.s[1] + ldr x11, [x1, #56] +fmla v16.4s, v2.4s, v7.s[1] + ldr x12, [x1, #64] +fmla v17.4s, v3.4s, v7.s[1] + ldr x13, [x1, #72] +fmla v18.4s, v4.4s, v7.s[1] + ldr x14, [x1, #80] +fmla v19.4s, v5.4s, v7.s[1] + ldr x15, [x1, #88] + +fmla v20.4s, v0.4s, v7.s[2] + ldr x20, [x2] +fmla v21.4s, v1.4s, v7.s[2] + ldr x21, [x2, #8] +fmla v22.4s, v2.4s, v7.s[2] + add x1, x1, #96 +fmla v23.4s, v3.4s, v7.s[2] + add x2, x2, #16 +fmla v24.4s, v4.4s, v7.s[2] + prfm pldl1keep, [x1, #256] +fmla v25.4s, v5.4s, v7.s[2] + prfm pldl1keep, [x2, #256] + +fmla v26.4s, v0.4s, v7.s[3] + prfm pldl1keep, [x1, #320] +fmla v27.4s, v1.4s, v7.s[3] + prfm pldl1keep, [x1, #384] +fmla v28.4s, v2.4s, v7.s[3] + prfm pldl1keep, [x1, #448] +fmla v29.4s, v3.4s, v7.s[3] + prfm pldl1keep, [x2, #320] +fmla v30.4s, v4.4s, v7.s[3] + prfm pldl1keep, [x2, #384] +fmla v31.4s, v5.4s, v7.s[3] + prfm pldl1keep, [x2, #448] + +ins v0.d[0], x4 +ins v1.d[0], x6 +ins v2.d[0], x8 +ins v3.d[0], x10 +ins v4.d[0], x12 +ins v5.d[0], x14 +ins v7.d[0], x20 + +ins v0.d[1], x5 +ins v1.d[1], x7 +ins v2.d[1], x9 +ins v3.d[1], x11 +ins v4.d[1], x13 +ins v5.d[1], x15 +ins v7.d[1], x21 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a55.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a55.S.raw new file mode 100644 index 0000000..93307bf --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a55.S.raw @@ -0,0 +1,53 @@ +fmla v8.4s, v0.4s, v7.s[0] +fmla v14.4s, v0.4s, v7.s[1] + prfm pldl1keep, [x2, #256] +fmla v20.4s, v0.4s, v7.s[2] +fmla v26.4s, v0.4s, v7.s[3] + ldr d0, [x1], #8 +fmla v9.4s, v1.4s, v7.s[0] + ldr x5, [x1], #8 +fmla v15.4s, v1.4s, v7.s[1] + ldr x20, [x2], #8 +fmla v21.4s, v1.4s, v7.s[2] + ldr x21, [x2], #8 +fmla v27.4s, v1.4s, v7.s[3] + ldr d1, [x1], #8 +fmla v10.4s, v2.4s, v7.s[0] + ldr x7, [x1], #8 +fmla v16.4s, v2.4s, v7.s[1] + prfm pldl1keep, [x1, #256] +fmla v22.4s, v2.4s, v7.s[2] + prfm pldl1keep, [x1, #320] +fmla v28.4s, v2.4s, v7.s[3] + ldr d2, [x1], #8 +fmla v11.4s, v3.4s, v7.s[0] + ldr x9, [x1], #8 +fmla v17.4s, v3.4s, v7.s[1] + ins v0.d[1], x5 +fmla v23.4s, v3.4s, v7.s[2] + ins v1.d[1], x7 +fmla v29.4s, v3.4s, v7.s[3] + ldr d3, [x1], #8 +fmla v12.4s, v4.4s, v7.s[0] + ldr x11, [x1], #8 +fmla v18.4s, v4.4s, v7.s[1] + ins v2.d[1], x9 +fmla v24.4s, v4.4s, v7.s[2] +fmla v30.4s, v4.4s, v7.s[3] + ldr d4, [x1], #8 +fmla v13.4s, v5.4s, v7.s[0] + ldr x13, [x1], #8 +fmla v19.4s, v5.4s, v7.s[1] + ldr x14, [x1], #8 +fmla v25.4s, v5.4s, v7.s[2] + ldr x15, [x1], #8 +fmla v31.4s, v5.4s, v7.s[3] + +ins v7.d[0], x20 +ins v7.d[1], x21 + +ins v5.d[0], x14 +ins v5.d[1], x15 + +ins v3.d[1], x11 +ins v4.d[1], x13 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_24x4/packed_packed_loop1/naive.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_24x4/packed_packed_loop1/naive.S.raw new file mode 100644 index 0000000..7b12d74 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_24x4/packed_packed_loop1/naive.S.raw @@ -0,0 +1,31 @@ +fmla v8.4s, v0.4s, v7.s[0] +fmla v9.4s, v1.4s, v7.s[0] +fmla v10.4s, v2.4s, v7.s[0] +fmla v11.4s, v3.4s, v7.s[0] +fmla v12.4s, v4.4s, v7.s[0] +fmla v13.4s, v5.4s, v7.s[0] + +fmla v14.4s, v0.4s, v7.s[1] +fmla v15.4s, v1.4s, v7.s[1] +fmla v16.4s, v2.4s, v7.s[1] +fmla v17.4s, v3.4s, v7.s[1] +fmla v18.4s, v4.4s, v7.s[1] +fmla v19.4s, v5.4s, v7.s[1] + +fmla v20.4s, v0.4s, v7.s[2] +fmla v21.4s, v1.4s, v7.s[2] +fmla v22.4s, v2.4s, v7.s[2] +fmla v23.4s, v3.4s, v7.s[2] +fmla v24.4s, v4.4s, v7.s[2] +fmla v25.4s, v5.4s, v7.s[2] + +fmla v26.4s, v0.4s, v7.s[3] +fmla v27.4s, v1.4s, v7.s[3] +fmla v28.4s, v2.4s, v7.s[3] +fmla v29.4s, v3.4s, v7.s[3] +fmla v30.4s, v4.4s, v7.s[3] +fmla v31.4s, v5.4s, v7.s[3] + +ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [ x1 ], #64 +ld1 {{ v4.4s, v5.4s }}, [ x1 ], #32 +ld1 {{ v7.4s }}, [ x2 ], #16 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_24x4_core.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_24x4_core.S.j2 new file mode 100644 index 0000000..af08dd0 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_24x4_core.S.j2 @@ -0,0 +1,185 @@ +// vim: ft=arm + +// x20..x27 are used, callee-preserved + +// C tile regs: v8 to v31, (scratch) +// - x19-x29 to preserve (but x19, x28, x29 not used) +// - d8..d15 to preserve +// - v16 to v31, no need to preserve + +// v8 is used, d8 (lower half) must preserved +// v0-v7 (scratch registers) +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +.cpu generic+fp+simd +.global {{G}}arm64simd_mmm_f32_24x4_{{core}}_{{suffix}} +{{G}}arm64simd_mmm_f32_24x4_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.j2" %} + +.add_mat_mul: + ldr x2, [x0, #24] // b + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x1 ], #64 + ld1 { v4.4s, v5.4s }, [ x1 ], #32 + ld1 { v7.4s }, [ x2 ], #16 + +{% set packed_packed_loop1 %} + {% if core == "a53" %} + {% include "arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a53.S.raw" %} + {% elif core == "a55" %} + {% include "arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a55.S.raw" %} + {% else %} + {% include "arm64simd_mmm_f32_24x4/packed_packed_loop1/naive.S.raw" %} + {% endif %} +{% endset %} + + cmp x3, #4 + blt .packed_packed_loop_1 + +.p2align 4 +.packed_packed_loop_4: + {{ packed_packed_loop1 }} + {{ packed_packed_loop1 }} + {{ packed_packed_loop1 }} + {{ packed_packed_loop1 }} + + sub x3, x3, #4 + cmp x3, #4 + bge .packed_packed_loop_4 + + cmp x3, #0 + beq .non_linear_loop + +.p2align 4 +.packed_packed_loop_1: + {{ packed_packed_loop1 }} + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +{% set from = 8 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_scalars.j2" %} +{% set mr = 24 %}{% set from = 8 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_per_rows.j2" %} +{% set mr = 24 %}{% set from = 8 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_per_cols.j2" %} +{% set from = 8 %}{% set to = 31 %}{% include "arm64simd_mmm_load_tile.j2" %} + +.add_unicast: + ldp x5, x6, [x0, #8] + ldp x7, x8, [x0, #24] + + {% for col in range(0, 4) %} + mov x4, x5 + {% for reg in range(0, 6) %} + {% for lane in range(0, 4) %} + ld1 {v0.s}[{{lane}}], [ x4 ], x6 + {% endfor %} + fadd v{{ col * 6 + 8 + reg }}.4s, v{{ col * 6 + 8 + reg }}.4s, v0.4s + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x2, [x0, #8] + ldr x3, [x0, #16] + + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x2 ], #64 + ld1 { v7.4s }, [ x3 ] + ld1 { v4.4s, v5.4s }, [ x2 ] + + fmla v8.4s, v0.4s, v7.s[0] + fmla v9.4s, v1.4s, v7.s[0] + fmla v10.4s, v2.4s, v7.s[0] + fmla v11.4s, v3.4s, v7.s[0] + fmla v12.4s, v4.4s, v7.s[0] + fmla v13.4s, v5.4s, v7.s[0] + + fmla v14.4s, v0.4s, v7.s[1] + fmla v15.4s, v1.4s, v7.s[1] + fmla v16.4s, v2.4s, v7.s[1] + fmla v17.4s, v3.4s, v7.s[1] + fmla v18.4s, v4.4s, v7.s[1] + fmla v19.4s, v5.4s, v7.s[1] + + fmla v20.4s, v0.4s, v7.s[2] + fmla v21.4s, v1.4s, v7.s[2] + fmla v22.4s, v2.4s, v7.s[2] + fmla v23.4s, v3.4s, v7.s[2] + fmla v24.4s, v4.4s, v7.s[2] + fmla v25.4s, v5.4s, v7.s[2] + + fmla v26.4s, v0.4s, v7.s[3] + fmla v27.4s, v1.4s, v7.s[3] + fmla v28.4s, v2.4s, v7.s[3] + fmla v29.4s, v3.4s, v7.s[3] + fmla v30.4s, v4.4s, v7.s[3] + fmla v31.4s, v5.4s, v7.s[3] + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x6, #4 + bne .store_strides_generic + + {% for col in range(0, 4) %} + str q{{ col * 6 + 8 + 0 }}, [ x5 ] + str q{{ col * 6 + 8 + 1 }}, [ x5, #16 ] + str q{{ col * 6 + 8 + 2 }}, [ x5, #32 ] + str q{{ col * 6 + 8 + 3 }}, [ x5, #48 ] + str q{{ col * 6 + 8 + 4 }}, [ x5, #64 ] + str q{{ col * 6 + 8 + 5 }}, [ x5, #80 ] + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.store_strides_generic: + + {% for col in range(0, 4) %} + mov x4, x5 + {% for reg in range(0, 6) %} + {% for lane in range(0, 4) %} + st1 { v{{ col * 6 + 8 + reg }}.s }[{{lane}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.return: + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_32x1_core.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_32x1_core.S.j2 new file mode 100644 index 0000000..171bbae --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_32x1_core.S.j2 @@ -0,0 +1,403 @@ +// vim: ft=arm + +// C tile regs: +// - x19-x29 to preserve (but x19, x28, x29 not used) +// - d8..d15 to preserve +// - v16 to v31, no need to preserve +// +// v16[0] v18[0] v20[0] v22[0] v24[0] v26[0] v28[0] v30[0] +// v16[1] v18[1] +// v16[2] v18[2] +// v16[3] v18[3] +// +// v17[0] v19[0] v21[0] v23[0] v25[0] v27[0] v29[0] v31[0] +// v17[1] v19[1] +// v17[2] v19[2] +// v17[3] v19[3] + +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +.cpu generic+fp+simd +.global {{G}}arm64simd_mmm_f32_32x1_{{core}}_{{suffix}} +{{G}}arm64simd_mmm_f32_32x1_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.j2" %} + +.add_mat_mul: + ldp x2, x4, [x0, #24] // b, packing + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + + cmp x4, #1 + beq .q4f16se + cmp x4, #2 + beq .q4f32se + cmp x4, #3 + beq .f16f16 + cmp x4, #4 + beq .f32f16 + cmp x4, #5 + beq .f16f32 + + sub x3, x3, #1 + +.p2align 4 +.packed_packed_loop_1: + ld1 { v8.s }[0], [ x2 ], #4 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x1 ], #64 + ld1 { v4.4s, v5.4s, v6.4s, v7.4s }, [ x1 ], #64 + + fmla v24.4s, v0.4s, v8.s[0] + fmla v25.4s, v1.4s, v8.s[0] + fmla v26.4s, v2.4s, v8.s[0] + fmla v27.4s, v3.4s, v8.s[0] + fmla v28.4s, v4.4s, v8.s[0] + fmla v29.4s, v5.4s, v8.s[0] + fmla v30.4s, v6.4s, v8.s[0] + fmla v31.4s, v7.4s, v8.s[0] + + subs x3, x3, #1 + bge .packed_packed_loop_1 + + b .non_linear_loop + +.p2align 8 +.q40f16_const: + .byte 0xc8, 0xc7, 0xc6, 0xc5, 0xc4, 0xc2, 0xc0, 0xbc + .byte 0x00, 0x3c, 0x40, 0x42, 0x44, 0x45, 0x46, 0x47 + +.q4f16se: + adr x4, .q40f16_const + movi v15.16b, 15 + ld1 {v13.16b}, [ x4 ] + eor v12.16b, v12.16b, v12.16b + +.q4f16se_outerloop: +{% for i in range(0, 8) %} + eor v{{ i + 16 }}.16b, v{{ i + 16 }}.16b, v{{ i + 16 }}.16b +{% endfor %} + mov x4, #32 + +.p2align 4 +.q4f16se_innerloop: + ld1 { v10.16b }, [ x1 ], #16 + ld1 { v11.h }[0], [ x2 ], #2 + + and v9.16b, v10.16b, v15.16b + ushr v10.16b, v10.16b, 4 + + tbl v9.16b, { v13.16b }, v9.16b + tbl v10.16b, { v13.16b }, v10.16b + + zip1 v0.16b, v12.16b, v9.16b + zip2 v2.16b, v12.16b, v9.16b + zip1 v4.16b, v12.16b, v10.16b + zip2 v6.16b, v12.16b, v10.16b + + fcvtl v11.4s, v11.4h + + fcvtl2 v1.4s, v0.8h + fcvtl2 v3.4s, v2.8h + fcvtl2 v5.4s, v4.8h + fcvtl2 v7.4s, v6.8h + fcvtl v0.4s, v0.4h + fcvtl v2.4s, v2.4h + fcvtl v4.4s, v4.4h + fcvtl v6.4s, v6.4h + +{% for i in range(0, 8) %} + fmla v{{ i + 16 }}.4s, v{{i}}.4s, v11.s[0] +{% endfor %} + + subs x4, x4, #1 + bne .q4f16se_innerloop + + // scales + ld1 { v0.8h-v3.8h }, [ x1 ], #64 + + fcvtl v4.4s, v0.4h + fcvtl2 v5.4s, v0.8h + fcvtl v6.4s, v1.4h + fcvtl2 v7.4s, v1.8h + fcvtl v8.4s, v2.4h + fcvtl2 v9.4s, v2.8h + fcvtl v10.4s, v3.4h + fcvtl2 v11.4s, v3.8h + +{% for i in range(0, 8) %} + fmla v{{ i + 24 }}.4s, v{{ i + 4 }}.4s, v{{ i + 16 }}.4s +{% endfor %} + + subs x3, x3, #32 + bne .q4f16se_outerloop + + b .non_linear_loop + +.q4f32se: + adr x4, .q40f16_const + movi v15.16b, 15 + ld1 {v13.16b}, [ x4 ] + eor v12.16b, v12.16b, v12.16b + +.q4f32se_outerloop: +{% for i in range(0, 8) %} + eor v{{ i + 16 }}.16b, v{{ i + 16 }}.16b, v{{ i + 16 }}.16b +{% endfor %} + mov x4, #32 + +.p2align 4 +.q4f32se_innerloop: + ld1 { v10.16b }, [ x1 ], #16 + ld1 { v11.s }[0], [ x2 ], #4 + + and v9.16b, v10.16b, v15.16b + ushr v10.16b, v10.16b, 4 + + tbl v9.16b, { v13.16b }, v9.16b + tbl v10.16b, { v13.16b }, v10.16b + + zip1 v0.16b, v12.16b, v9.16b + zip2 v2.16b, v12.16b, v9.16b + zip1 v4.16b, v12.16b, v10.16b + zip2 v6.16b, v12.16b, v10.16b + + fcvtl2 v1.4s, v0.8h + fcvtl2 v3.4s, v2.8h + fcvtl2 v5.4s, v4.8h + fcvtl2 v7.4s, v6.8h + fcvtl v0.4s, v0.4h + fcvtl v2.4s, v2.4h + fcvtl v4.4s, v4.4h + fcvtl v6.4s, v6.4h + +{% for i in range(0, 8) %} + fmla v{{ i + 16 }}.4s, v{{i}}.4s, v11.s[0] +{% endfor %} + + subs x4, x4, #1 + bne .q4f32se_innerloop + + // scales + ld1 { v0.8h-v3.8h }, [ x1 ], #64 + + fcvtl v4.4s, v0.4h + fcvtl2 v5.4s, v0.8h + fcvtl v6.4s, v1.4h + fcvtl2 v7.4s, v1.8h + fcvtl v8.4s, v2.4h + fcvtl2 v9.4s, v2.8h + fcvtl v10.4s, v3.4h + fcvtl2 v11.4s, v3.8h + +{% for i in range(0, 8) %} + fmla v{{ i + 24 }}.4s, v{{ i + 4 }}.4s, v{{ i + 16 }}.4s +{% endfor %} + + subs x3, x3, #32 + bne .q4f32se_outerloop + + b .non_linear_loop + +.p2align 4 +.f16f16: + sub x3, x3, #1 +.f16f16_loop: + ld1 { v9.h }[0], [ x2 ], #2 + ld1 { v10.8h-v13.8h }, [ x1 ], #64 + + fcvtl v8.4s, v9.4h + {% for reg in range(0, 4) %} + fcvtl v{{ reg * 2 }}.4s, v{{ reg + 10 }}.4h + fcvtl2 v{{ reg * 2 + 1 }}.4s, v{{ reg + 10 }}.8h + {% endfor %} + + fmla v24.4s, v0.4s, v8.s[0] + fmla v25.4s, v1.4s, v8.s[0] + fmla v26.4s, v2.4s, v8.s[0] + fmla v27.4s, v3.4s, v8.s[0] + fmla v28.4s, v4.4s, v8.s[0] + fmla v29.4s, v5.4s, v8.s[0] + fmla v30.4s, v6.4s, v8.s[0] + fmla v31.4s, v7.4s, v8.s[0] + + subs x3, x3, #1 + bge .f16f16_loop + + b .non_linear_loop + +.p2align 4 +.f32f16: + sub x3, x3, #1 +.f32f16_loop: + ld1 { v9.h }[0], [ x2 ], #2 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x1 ], #64 + ld1 { v4.4s, v5.4s, v6.4s, v7.4s }, [ x1 ], #64 + + fcvtl v8.4s, v9.4h + + fmla v24.4s, v0.4s, v8.s[0] + fmla v25.4s, v1.4s, v8.s[0] + fmla v26.4s, v2.4s, v8.s[0] + fmla v27.4s, v3.4s, v8.s[0] + fmla v28.4s, v4.4s, v8.s[0] + fmla v29.4s, v5.4s, v8.s[0] + fmla v30.4s, v6.4s, v8.s[0] + fmla v31.4s, v7.4s, v8.s[0] + + subs x3, x3, #1 + bge .f32f16_loop + + b .non_linear_loop + +.p2align 4 +.f16f32: + sub x3, x3, #1 +.f16f32_loop: + ld1 { v8.s }[0], [ x2 ], #4 + ld1 { v10.8h-v13.8h }, [ x1 ], #64 + + {% for reg in range(0, 4) %} + fcvtl v{{ reg * 2 }}.4s, v{{ reg + 10 }}.4h + fcvtl2 v{{ reg * 2 + 1 }}.4s, v{{ reg + 10 }}.8h + {% endfor %} + + fmla v24.4s, v0.4s, v8.s[0] + fmla v25.4s, v1.4s, v8.s[0] + fmla v26.4s, v2.4s, v8.s[0] + fmla v27.4s, v3.4s, v8.s[0] + fmla v28.4s, v4.4s, v8.s[0] + fmla v29.4s, v5.4s, v8.s[0] + fmla v30.4s, v6.4s, v8.s[0] + fmla v31.4s, v7.4s, v8.s[0] + + subs x3, x3, #1 + bge .f16f32_loop + + b .non_linear_loop + +{% set from = 24 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_scalars.j2" %} +{% set mr = 32 %}{% set from = 24 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_per_rows.j2" %} +{% set mr = 32 %}{% set from = 24 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_per_cols.j2" %} +{% set from = 24 %}{% set to = 31 %}{% include "arm64simd_mmm_load_tile.j2" %} + +.add_unicast: + ldp x5, x6, [x0, #8] // c base ptr, rsc + cmp x6, #4 + beq .do_per_row_add + + {% for reg in range(24, 32) %} + {% for lane in range(0, 4) %} + ld1 {v0.s}[{{lane}}], [ x5 ], x6 + {% endfor %} + fadd v{{reg}}.4s, v{{reg}}.4s, v0.4s + {% endfor %} + + b .non_linear_loop + +.do_per_row_add: + ld1 {v0.4s-v3.4s}, [x5], #64 + ld1 {v4.4s-v7.4s}, [x5], #64 + + {% for r in range(0, 8) %} + fadd v{{ r + 24 }}.4s, v{{ r + 24 }}.4s, v{{r}}.4s + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x3, [x0, #16] + ldr x2, [x0, #8] + + ld1 {v8.s}[0], [ x3 ] + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x2 ], #64 + ld1 { v4.4s, v5.4s, v6.4s, v7.4s }, [ x2 ], #64 + + fmla v24.4s, v0.4s, v8.s[0] + fmla v25.4s, v1.4s, v8.s[0] + fmla v26.4s, v2.4s, v8.s[0] + fmla v27.4s, v3.4s, v8.s[0] + fmla v28.4s, v4.4s, v8.s[0] + fmla v29.4s, v5.4s, v8.s[0] + fmla v30.4s, v6.4s, v8.s[0] + fmla v31.4s, v7.4s, v8.s[0] + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x8, #2 + beq .store_f16 + + cmp x6, #4 + beq .store_strides_contig + + {% for reg in range(24, 32) %} + {% for lane in range(0, 4) %} + st1 { v{{reg}}.s }[{{lane}}], [ x5 ], x6 + {% endfor %} + {% endfor %} + b .non_linear_loop + +.store_strides_contig: + + {% for reg in range(24, 32) %} + st1 { v{{reg}}.4s }, [ x5 ], #16 + {% endfor %} + b .non_linear_loop + +.store_f16: + {% for reg in range(0, 4) %} + fcvtn v{{reg}}.4h, v{{ reg * 2 + 24 }}.4s + fcvtn2 v{{reg}}.8h, v{{ reg * 2 + 25 }}.4s + {% endfor %} + + cmp x6, #2 + beq .store_strides_contig_f16 + + {% for reg in range(0, 4) %} + {% for lane in range(0, 8) %} + st1 { v{{reg}}.h }[{{lane}}], [ x5 ], x6 + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.store_strides_contig_f16: + + {% for reg in range(0, 4) %} + st1 { v{{reg}}.8h }, [ x5 ], #16 + {% endfor %} + b .non_linear_loop + +.return: + + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_32x3_core.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_32x3_core.S.j2 new file mode 100644 index 0000000..2a746cf --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_32x3_core.S.j2 @@ -0,0 +1,307 @@ +// vim: ft=arm + +// C tile regs: v16 to v31, no need to preserve + +// no preservation either for v0-v7... +// v8..v15 are callee-preserved +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +.global {{G}}arm64simd_mmm_f32_32x3_{{core}}_{{suffix}} +{{G}}arm64simd_mmm_f32_32x3_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.j2" %} + +.add_mat_mul: + ldp x2, x4, [x0, #24] // b, packing + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + + cmp x4, #1 + beq .f32f16 + cmp x4, #2 + beq .f16f32 + cmp x4, #3 + beq .f16f16 + +.p2align 4 +.packed_packed_loop_1: + ld1 { v7.4s }, [ x2 ] + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x1 ], #64 + ld1 { v4.4s, v5.4s, v6.4s }, [ x1 ], #48 + add x2, x2, #12 + +{% for col in range(0, 3) %} + fmla v{{ col * 8 + 8 }}.4s, v0.4s, v7.s[{{ col }}] +{% endfor %} + + ld1 { v0.4s }, [ x1 ], #16 + +{% for row in range(1, 7) %} + {% for col in range(0, 3) %} + fmla v{{ col * 8 + 8 + row }}.4s, v{{row}}.4s, v7.s[{{ col }}] + {% endfor %} +{% endfor %} + +{% for col in range(0, 3) %} + fmla v{{ col * 8 + 15 }}.4s, v0.4s, v7.s[{{ col }}] +{% endfor %} + + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +.p2align 4 +.f32f16: + ld1 { v7.4h }, [ x2 ] + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x1 ], #64 + ld1 { v4.4s, v5.4s, v6.4s }, [ x1 ], #48 + fcvtl v7.4s, v7.4h + add x2, x2, #6 + +{% for col in range(0, 3) %} + fmla v{{ col * 8 + 8 }}.4s, v0.4s, v7.s[{{ col }}] +{% endfor %} + + ld1 { v0.4s }, [ x1 ], #16 + +{% for row in range(1, 7) %} + {% for col in range(0, 3) %} + fmla v{{ col * 8 + 8 + row }}.4s, v{{row}}.4s, v7.s[{{ col }}] + {% endfor %} +{% endfor %} + +{% for col in range(0, 3) %} + fmla v{{ col * 8 + 15 }}.4s, v0.4s, v7.s[{{ col }}] +{% endfor %} + + subs x3, x3, #1 + bne .f32f16 + + b .non_linear_loop + +.p2align 4 +.f16f32: + ld1 { v7.4s }, [ x2 ] + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [ x1 ], #64 + add x2, x2, #12 + + fcvtl v4.4s, v0.4h + fcvtl2 v5.4s, v0.8h + fcvtl v6.4s, v1.4h + fcvtl2 v0.4s, v1.8h + + {% for col in range(0, 3) %} + fmla v{{ col * 8 + 8 }}.4s, v4.4s, v7.s[{{ col }}] + fmla v{{ col * 8 + 9 }}.4s, v5.4s, v7.s[{{ col }}] + fmla v{{ col * 8 + 10 }}.4s, v6.4s, v7.s[{{ col }}] + fmla v{{ col * 8 + 11 }}.4s, v0.4s, v7.s[{{ col }}] + {% endfor %} + + fcvtl v4.4s, v2.4h + fcvtl2 v5.4s, v2.8h + fcvtl v6.4s, v3.4h + fcvtl2 v1.4s, v3.8h + + {% for col in range(0, 3) %} + fmla v{{ col * 8 + 12 }}.4s, v4.4s, v7.s[{{ col }}] + fmla v{{ col * 8 + 13 }}.4s, v5.4s, v7.s[{{ col }}] + fmla v{{ col * 8 + 14 }}.4s, v6.4s, v7.s[{{ col }}] + fmla v{{ col * 8 + 15 }}.4s, v1.4s, v7.s[{{ col }}] + {% endfor %} + + subs x3, x3, #1 + bne .f16f32 + + b .non_linear_loop + +.p2align 4 +.f16f16: + ld1 { v7.4h }, [ x2 ] + ld1 { v0.8h, v1.8h, v2.8h, v3.8h }, [ x1 ], #64 + add x2, x2, #6 + + fcvtl v7.4s, v7.4h + + fcvtl v4.4s, v0.4h + fcvtl2 v5.4s, v0.8h + fcvtl v6.4s, v1.4h + fcvtl2 v0.4s, v1.8h + + {% for col in range(0, 3) %} + fmla v{{ col * 8 + 8 }}.4s, v4.4s, v7.s[{{ col }}] + fmla v{{ col * 8 + 9 }}.4s, v5.4s, v7.s[{{ col }}] + fmla v{{ col * 8 + 10 }}.4s, v6.4s, v7.s[{{ col }}] + fmla v{{ col * 8 + 11 }}.4s, v0.4s, v7.s[{{ col }}] + {% endfor %} + + fcvtl v4.4s, v2.4h + fcvtl2 v5.4s, v2.8h + fcvtl v6.4s, v3.4h + fcvtl2 v1.4s, v3.8h + + {% for col in range(0, 3) %} + fmla v{{ col * 8 + 12 }}.4s, v4.4s, v7.s[{{ col }}] + fmla v{{ col * 8 + 13 }}.4s, v5.4s, v7.s[{{ col }}] + fmla v{{ col * 8 + 14 }}.4s, v6.4s, v7.s[{{ col }}] + fmla v{{ col * 8 + 15 }}.4s, v1.4s, v7.s[{{ col }}] + {% endfor %} + + subs x3, x3, #1 + bne .f16f16 + + b .non_linear_loop + + +{% set from = 8 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_scalars.j2" %} +{% set mr = 32 %}{% set from = 8 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_per_rows.j2" %} +{% set mr = 32 %}{% set from = 8 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_per_cols.j2" %} +{% set from = 8 %}{% set to = 31 %}{% include "arm64simd_mmm_load_tile.j2" %} + +.add_unicast: + ldp x5, x6, [x0, #8] + ldp x7, x8, [x0, #24] + + {% for col in range(0, 3) %} + mov x4, x5 + {% for reg in range(0, 8) %} + {% for lane in range(0, 4) %} + ld1 {v0.s}[{{lane}}], [ x4 ], x6 + {% endfor %} + fadd v{{ col * 8 + 8 + reg }}.4s, v{{ col * 8 + 8 + reg }}.4s, v0.4s + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldp x2, x3, [x0, #8] + + ld1 { v7.d }[0], [ x3 ], #8 + ld1 { v7.s }[2], [ x3 ], #4 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x2 ], #64 + ld1 { v4.4s, v5.4s, v6.4s }, [ x2 ], #48 + +{% for col in range(0, 3) %} + fmla v{{ col * 8 + 8 }}.4s, v0.4s, v7.s[{{ col }}] +{% endfor %} + + ld1 { v0.4s }, [ x2 ], #16 + +{% for row in range(1, 7) %} + {% for col in range(0, 3) %} + fmla v{{ col * 8 + 8 + row }}.4s, v{{row}}.4s, v7.s[{{ col }}] + {% endfor %} +{% endfor %} + +{% for col in range(0, 3) %} + fmla v{{ col * 8 + 15 }}.4s, v0.4s, v7.s[{{ col }}] +{% endfor %} + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x8, #2 + beq .store_f16 + + cmp x6, #4 + beq .store_strides_contig + + + {% for col in range(0, 3) %} + mov x4, x5 + {% for reg in range(0, 8) %} + {% for lane in range(0, 4) %} + st1 { v{{ col * 8 + 8 + reg }}.s }[{{lane}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + b .non_linear_loop + +.store_strides_contig: + + {% for col in range(0, 3) %} + mov x4, x5 + {% for r in range(0, 8) %} + st1 { v{{ col * 8 + 8 + r }}.4s }, [ x4 ], 16 + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.store_f16: + + cmp x6, #2 + beq .store_strides_contig_f16 + + {% for col in range(0, 3) %} + {% for reg in range(0, 4) %} + fcvtn v{{reg}}.4h, v{{ (col * 4 + reg) * 2 + 8 }}.4s + fcvtn2 v{{reg}}.8h, v{{ (col * 4 + reg) * 2 + 9 }}.4s + {% endfor %} + + mov x4, x5 + {% for reg in range(0, 4) %} + {% for lane in range(0, 8) %} + st1 { v{{reg}}.h }[{{lane}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + + {% endfor %} + + + b .non_linear_loop + +.store_strides_contig_f16: + + {% for col in range(0, 3) %} + {% for reg in range(0, 4) %} + fcvtn v{{reg}}.4h, v{{ (col * 4 + reg) * 2 + 8 }}.4s + fcvtn2 v{{reg}}.8h, v{{ (col * 4 + reg) * 2 + 9 }}.4s + {% endfor %} + + mov x4, x5 + {% for reg in range(0, 4) %} + st1 { v{{reg}}.4s }, [ x4 ], #16 + {% endfor %} + add x5, x5, x7 + + {% endfor %} + b .non_linear_loop + + +.return: + + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop1/cortex_a53.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop1/cortex_a53.S.raw new file mode 100644 index 0000000..410816d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop1/cortex_a53.S.raw @@ -0,0 +1,65 @@ + fmla v16.4s, v0.4s, v8.s[0] + ldr x5, [x1, #128] + fmla v17.4s, v1.4s, v8.s[0] + ldr x6, [x1, #136] + fmla v18.4s, v2.4s, v8.s[0] + ldr x7, [x1, #144] + fmla v19.4s, v3.4s, v8.s[0] + ldr x9, [x1, #152] + ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [ x1 ], #64 + + fmla v20.4s, v4.4s, v8.s[0] + ldr x10, [x1, #96] + fmla v21.4s, v5.4s, v8.s[0] + ldr x11, [x1, #104] + fmla v22.4s, v6.4s, v8.s[0] + ldr x12, [x1, #112] + fmla v23.4s, v7.4s, v8.s[0] + ldr x13, [x1, #120] + + ld1 {{ v4.4s, v5.4s, v6.4s, v7.4s }}, [ x1 ] + + fmla v24.4s, v0.4s, v8.s[0] + ldr x14, [x1, #128] + fmla v25.4s, v1.4s, v8.s[0] + ldr x15, [x1, #136] + fmla v26.4s, v2.4s, v8.s[0] + ldr x20, [x1, #144] + fmla v27.4s, v3.4s, v8.s[0] + ldr x21, [x1, #152] + fmla v28.4s, v4.4s, v8.s[0] + ldr x22, [x1, #160] + fmla v29.4s, v5.4s, v8.s[0] + ldr x23, [x1, #168] + fmla v30.4s, v6.4s, v8.s[0] + ldr x24, [x1, #176] + fmla v31.4s, v7.4s, v8.s[0] + ldr x25, [x1, #184] + + ld1 {{ v8.s }}[0], [ x2 ], #4 + + prfm pldl1keep, [x1, #1024] + prfm pldl1keep, [x1, #1088] + prfm pldl1keep, [x1, #1152] + prfm pldl1keep, [x1, #1216] + prfm pldl1keep, [x2, #256] + + ins v0.d[0], x5 + ins v1.d[0], x7 + ins v2.d[0], x10 + ins v3.d[0], x12 + ins v4.d[0], x14 + ins v5.d[0], x20 + ins v6.d[0], x22 + ins v7.d[0], x24 + + ins v0.d[1], x6 + ins v1.d[1], x9 + ins v2.d[1], x11 + ins v3.d[1], x13 + ins v4.d[1], x15 + ins v5.d[1], x21 + ins v6.d[1], x23 + ins v7.d[1], x25 + + add x1, x1, #192 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop1/naive.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop1/naive.S.raw new file mode 100644 index 0000000..c65deb9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop1/naive.S.raw @@ -0,0 +1,32 @@ + ld1 {{ v9.4s, v10.4s, v11.4s, v12.4s }}, [x1], #64 + ld1 {{ v13.4s, v14.4s, v15.4s }}, [x1], #48 + + fmla v16.4s, v0.4s, v8.s[0] + fmla v17.4s, v1.4s, v8.s[0] + fmla v18.4s, v2.4s, v8.s[0] + fmla v19.4s, v3.4s, v8.s[0] + fmla v20.4s, v4.4s, v8.s[0] + fmla v21.4s, v5.4s, v8.s[0] + fmla v22.4s, v6.4s, v8.s[0] + fmla v23.4s, v7.4s, v8.s[0] + fmla v24.4s, v9.4s, v8.s[0] + ld1 {{ v9.4s }}, [ x1 ], #16 + ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [x1], #64 + ld1 {{ v4.4s, v5.4s, v6.4s, v7.4s }}, [x1], #64 + fmla v25.4s, v10.4s, v8.s[0] + fmla v26.4s, v11.4s, v8.s[0] + fmla v27.4s, v12.4s, v8.s[0] + fmla v28.4s, v13.4s, v8.s[0] + fmla v29.4s, v14.4s, v8.s[0] + fmla v30.4s, v15.4s, v8.s[0] + + fmla v31.4s, v9.4s, v8.s[0] + + ld1 {{ v8.s }}[0], [ x2 ], #4 + + prfm pldl1keep, [x1, #1024] + prfm pldl1keep, [x1, #1088] + prfm pldl1keep, [x1, #1152] + prfm pldl1keep, [x1, #1216] + prfm pldl1keep, [x2, #256] + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop2/cortex_a55.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop2/cortex_a55.S.raw new file mode 100644 index 0000000..2a5f066 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop2/cortex_a55.S.raw @@ -0,0 +1,85 @@ + ld1 {{ v9.4s, v10.4s, v11.4s }}, [x1], #48 + + fmla v16.4s, v0.4s, v8.s[0] + ldr x8, [x2], #8 + fmla v17.4s, v1.4s, v8.s[0] + ldr d12, [x1], #8 + fmla v18.4s, v2.4s, v8.s[0] + ldr x12, [x1], #8 + fmla v19.4s, v3.4s, v8.s[0] + ldr d13, [x1], #8 + fmla v20.4s, v4.4s, v8.s[0] + ldr x13, [x1], #8 + fmla v21.4s, v5.4s, v8.s[0] + ldr d14, [x1], #8 + fmla v22.4s, v6.4s, v8.s[0] + ldr x14, [x1], #8 + fmla v23.4s, v7.4s, v8.s[0] + ldr d15, [x1], #8 + fmla v24.4s, v9.4s, v8.s[0] + ldr x15, [x1], #8 + + ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [x1], #64 + ins v8.d[1], x8 + ld1 {{ v4.4s, v5.4s, v6.4s, v7.4s }}, [x1], #64 + + fmla v25.4s, v10.4s, v8.s[0] + ins v12.d[1], x12 + fmla v26.4s, v11.4s, v8.s[0] + ins v13.d[1], x13 + fmla v27.4s, v12.4s, v8.s[0] + ins v14.d[1], x14 + fmla v28.4s, v13.4s, v8.s[0] + ins v15.d[1], x15 + + ld1 {{ v9.4s, v10.4s, v11.4s, v12.4s }}, [x1], #64 + + fmla v29.4s, v14.4s, v8.s[0] + ldr d13, [x1], #8 + fmla v30.4s, v15.4s, v8.s[0] + ldr x13, [x1], #8 + fmla v31.4s, v0.4s, v8.s[0] + ldr d14, [x1], #8 + + fmla v16.4s, v1.4s, v8.s[2] + ldr x14, [x1], #8 + fmla v17.4s, v2.4s, v8.s[2] + ldr d15, [x1], #8 + fmla v18.4s, v3.4s, v8.s[2] + ldr x15, [x1], #8 + fmla v19.4s, v4.4s, v8.s[2] + + ld1 {{ v0.4s }}, [x1], #16 + + fmla v20.4s, v5.4s, v8.s[2] + ldr d1, [x1], #8 + fmla v21.4s, v6.4s, v8.s[2] + ldr x10, [x1], #8 + + fmla v22.4s, v7.4s, v8.s[2] + + fmla v23.4s, v9.4s, v8.s[2] + ins v13.d[1], x13 + fmla v24.4s, v10.4s, v8.s[2] + ins v14.d[1], x14 + fmla v25.4s, v11.4s, v8.s[2] + ins v15.d[1], x15 + + fmla v26.4s, v12.4s, v8.s[2] + prfm pldl1keep, [x1, #1024] + fmla v27.4s, v13.4s, v8.s[2] + ins v1.d[1], x10 + fmla v28.4s, v14.4s, v8.s[2] + prfm pldl1keep, [x1, #1088] + fmla v29.4s, v15.4s, v8.s[2] + prfm pldl1keep, [x1, #1152] + fmla v30.4s, v0.4s, v8.s[2] + prfm pldl1keep, [x1, #1216] + fmla v31.4s, v1.4s, v8.s[2] + prfm pldl1keep, [x2, #256] + + ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [x1], #64 + ins v8.s[0], v8.s[3] + ld1 {{ v4.4s, v5.4s, v6.4s, v7.4s }}, [x1], #64 + + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop2/naive.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop2/naive.S.raw new file mode 100644 index 0000000..cba3dad --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_64x1/loop2/naive.S.raw @@ -0,0 +1,66 @@ +// load a: v9, v10, v11, v12, v13, v14, v15 +// load a: v0, v1, v2, v3, v4, v4, v6, v7 + + ld1 {{ v9.4s, v10.4s, v11.4s, v12.4s }}, [x1], #64 + ld1 {{ v13.4s, v14.4s, v15.4s }}, [x1], #48 + + fmla v16.4s, v0.4s, v8.s[0] + fmla v17.4s, v1.4s, v8.s[0] + fmla v18.4s, v2.4s, v8.s[0] + fmla v19.4s, v3.4s, v8.s[0] + + ld1 {{ v0.4s, v1.4s }}, [x1], #32 + + fmla v20.4s, v4.4s, v8.s[0] + fmla v21.4s, v5.4s, v8.s[0] + + ld1 {{ v2.4s, v3.4s, v4.4s, v5.4s }}, [x1], #64 + fmla v22.4s, v6.4s, v8.s[0] + fmla v23.4s, v7.4s, v8.s[0] + + ld1 {{ v6.4s, v7.4s }}, [x1], #32 + + fmla v24.4s, v9.4s, v8.s[0] + fmla v25.4s, v10.4s, v8.s[0] + fmla v26.4s, v11.4s, v8.s[0] + fmla v27.4s, v12.4s, v8.s[0] + fmla v28.4s, v13.4s, v8.s[0] + fmla v29.4s, v14.4s, v8.s[0] + fmla v30.4s, v15.4s, v8.s[0] + + ld1 {{ v9.4s, v10.4s, v11.4s, v12.4s }}, [x1], #64 + ld1 {{ v13.4s, v14.4s, v15.4s }}, [x1], #48 + + fmla v31.4s, v0.4s, v8.s[0] + ld1 {{ v8.s }}[0], [ x2 ], #4 + + fmla v16.4s, v1.4s, v8.s[0] + ld1 {{ v0.4s, v1.4s }}, [x1], #32 + fmla v17.4s, v2.4s, v8.s[0] + fmla v18.4s, v3.4s, v8.s[0] + fmla v19.4s, v4.4s, v8.s[0] + + fmla v20.4s, v5.4s, v8.s[0] + fmla v21.4s, v6.4s, v8.s[0] + fmla v22.4s, v7.4s, v8.s[0] + fmla v23.4s, v9.4s, v8.s[0] + + fmla v24.4s, v10.4s, v8.s[0] + fmla v25.4s, v11.4s, v8.s[0] + fmla v26.4s, v12.4s, v8.s[0] + fmla v27.4s, v13.4s, v8.s[0] + fmla v28.4s, v14.4s, v8.s[0] + fmla v29.4s, v15.4s, v8.s[0] + fmla v30.4s, v0.4s, v8.s[0] + fmla v31.4s, v1.4s, v8.s[0] + ld1 {{ v8.s }}[0], [ x2 ], #4 + + ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [x1], #64 + ld1 {{ v4.4s, v5.4s, v6.4s, v7.4s }}, [x1], #64 + + prfm pldl1keep, [x1, #1024] + prfm pldl1keep, [x1, #1088] + prfm pldl1keep, [x1, #1152] + prfm pldl1keep, [x1, #1216] + prfm pldl1keep, [x2, #256] + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_64x1_core.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_64x1_core.S.j2 new file mode 100644 index 0000000..78f6f06 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_64x1_core.S.j2 @@ -0,0 +1,225 @@ +// vim: ft=arm + +// C tile regs: +// - x19-x29 to preserve (but x19, x28, x29 not used) +// - d8..d15 to preserve +// - v16 to v31, no need to preserve +// +// v16[0] v18[0] v20[0] v22[0] v24[0] v26[0] v28[0] v30[0] +// v16[1] v18[1] +// v16[2] v18[2] +// v16[3] v18[3] +// +// v17[0] v19[0] v21[0] v23[0] v25[0] v27[0] v29[0] v31[0] +// v17[1] v19[1] +// v17[2] v19[2] +// v17[3] v19[3] + +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +.cpu generic+fp+simd +.global {{G}}arm64simd_mmm_f32_64x1_{{core}}_{{suffix}} +{{G}}arm64simd_mmm_f32_64x1_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.j2" %} + +.add_mat_mul: + ldr x2, [x0, #24] // b + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + sub x3, x3, #1 + + + ld1 { v8.s }[0], [ x2 ], #4 + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [ x1 ], #64 + ld1 { v4.4s, v5.4s, v6.4s, v7.4s }, [ x1 ], #64 + + cmp x3, #0 + beq .packed_packed_loop_1_last + + cmp x3, #4 + blt .packed_packed_loop_1 + +{% set packed_packed_loop1 %} + {% if core == "a53" %} + {% include "arm64simd_mmm_f32_64x1/loop1/cortex_a53.S.raw" %} + {% else %} + {% include "arm64simd_mmm_f32_64x1/loop1/naive.S.raw" %} + {% endif %} +{% endset %} + +{% set packed_packed_loop2 %} + {% if core == "a53" %} + {{ packed_packed_loop1 }} + {{ packed_packed_loop1 }} + {% elif core == "a55" %} + {% include "arm64simd_mmm_f32_64x1/loop2/cortex_a55.S.raw" %} + {% else %} + {% include "arm64simd_mmm_f32_64x1/loop2/naive.S.raw" %} + {% endif %} +{% endset %} + +.p2align 4 +.packed_packed_loop_4: + {{ packed_packed_loop2 }} + {{ packed_packed_loop2 }} + + sub x3, x3, #4 + cmp x3, #4 + bge .packed_packed_loop_4 + + cmp x3, #0 + beq .packed_packed_loop_1_last + +.p2align 4 +.packed_packed_loop_1: + {{ packed_packed_loop1 }} + + subs x3, x3, #1 + bne .packed_packed_loop_1 + +// last loop can't read beyond actual input as it's likely not packed and padded +.packed_packed_loop_1_last: + ld1 { v9.4s, v10.4s, v11.4s, v12.4s }, [x1], #64 + ld1 { v13.4s, v14.4s, v15.4s }, [x1], #48 + + fmla v16.4s, v0.4s, v8.s[0] + fmla v17.4s, v1.4s, v8.s[0] + ld1 { v0.4s }, [ x1 ] + fmla v18.4s, v2.4s, v8.s[0] + fmla v19.4s, v3.4s, v8.s[0] + fmla v20.4s, v4.4s, v8.s[0] + fmla v21.4s, v5.4s, v8.s[0] + fmla v22.4s, v6.4s, v8.s[0] + fmla v23.4s, v7.4s, v8.s[0] + + fmla v24.4s, v9.4s, v8.s[0] + fmla v25.4s, v10.4s, v8.s[0] + fmla v26.4s, v11.4s, v8.s[0] + fmla v27.4s, v12.4s, v8.s[0] + fmla v28.4s, v13.4s, v8.s[0] + fmla v29.4s, v14.4s, v8.s[0] + fmla v30.4s, v15.4s, v8.s[0] + fmla v31.4s, v0.4s, v8.s[0] + + b .non_linear_loop + +{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_scalars.j2" %} +{% set mr = 64 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_per_rows.j2" %} +{% set mr = 64 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_per_cols.j2" %} +{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_load_tile.j2" %} + +.add_unicast: + ldp x5, x6, [x0, #8] // c base ptr, rsc + cmp x6, #4 + beq .do_per_row_add + + {% for reg in range(16, 32) %} + {% for lane in range(0, 4) %} + ld1 {v0.s}[{{lane}}], [ x5 ], x6 + {% endfor %} + fadd v{{reg}}.4s, v{{reg}}.4s, v0.4s + {% endfor %} + + b .non_linear_loop + +.do_per_row_add: + ld1 {v0.4s-v3.4s}, [x5], #64 + ld1 {v4.4s-v7.4s}, [x5], #64 + ld1 {v8.4s-v11.4s}, [x5], #64 + ld1 {v12.4s-v15.4s}, [x5], #64 + + {% for r in range(0, 16) %} + fadd v{{ r + 16 }}.4s, v{{ r + 16 }}.4s, v{{r}}.4s + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x3, [x0, #16] + ldr x2, [x0, #8] + + ld1 {v8.s}[0], [ x3 ] + + {% for r in range(0, 8) %} + ldr q{{r}}, [x2], #16 + {% endfor %} + + fmla v16.4s, v0.4s, v8.s[0] + ldr q0, [x2], #16 + fmla v17.4s, v1.4s, v8.s[0] + ldr q1, [x2], #16 + fmla v18.4s, v2.4s, v8.s[0] + ldr q2, [x2], #16 + fmla v19.4s, v3.4s, v8.s[0] + ldr q3, [x2], #16 + fmla v20.4s, v4.4s, v8.s[0] + ldr q4, [x2], #16 + fmla v21.4s, v5.4s, v8.s[0] + ldr q5, [x2], #16 + fmla v22.4s, v6.4s, v8.s[0] + ldr q6, [x2], #16 + fmla v23.4s, v7.4s, v8.s[0] + ldr q7, [x2], #16 + + fmla v24.4s, v0.4s, v8.s[0] + fmla v25.4s, v1.4s, v8.s[0] + fmla v26.4s, v2.4s, v8.s[0] + fmla v27.4s, v3.4s, v8.s[0] + fmla v28.4s, v4.4s, v8.s[0] + fmla v29.4s, v5.4s, v8.s[0] + fmla v30.4s, v6.4s, v8.s[0] + fmla v31.4s, v7.4s, v8.s[0] + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc$ + + cmp x6, #4 + beq .store_strides_contig + + {% for reg in range(16, 32) %} + {% for lane in range(0, 4) %} + st1 { v{{reg}}.s }[{{lane}}], [ x5 ], x6 + {% endfor %} + {% endfor %} + b .non_linear_loop + +.store_strides_contig: + + {% for reg in range(16, 32) %} + st1 { v{{reg}}.4s }, [ x5 ], #16 + {% endfor %} + b .non_linear_loop + +.return: + + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/broken_chains.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/broken_chains.S.raw new file mode 100644 index 0000000..9b3035b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/broken_chains.S.raw @@ -0,0 +1,25 @@ +ld1 {{ v2.4s, v3.4s }}, [x1], #32 +ld1 {{ v6.4s, v7.4s }}, [x2], #32 + +fmla v16.4s, v0.4s, v4.s[0] +fmla v17.4s, v1.4s, v4.s[0] +fmla v18.4s, v0.4s, v4.s[1] +fmla v19.4s, v1.4s, v4.s[1] +fmla v20.4s, v0.4s, v4.s[2] +fmla v21.4s, v1.4s, v4.s[2] +fmla v22.4s, v0.4s, v4.s[3] +fmla v23.4s, v1.4s, v4.s[3] + +fmla v24.4s, v0.4s, v5.s[0] +fmla v25.4s, v1.4s, v5.s[0] +fmla v26.4s, v0.4s, v5.s[1] +fmla v27.4s, v1.4s, v5.s[1] +fmla v28.4s, v0.4s, v5.s[2] +fmla v29.4s, v1.4s, v5.s[2] +fmla v30.4s, v0.4s, v5.s[3] +fmla v31.4s, v1.4s, v5.s[3] + +and v0.16b, v2.16b, v2.16b +and v1.16b, v3.16b, v3.16b +and v4.16b, v6.16b, v6.16b +and v5.16b, v7.16b, v7.16b diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_w_no_preload.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_w_no_preload.S.raw new file mode 100644 index 0000000..ac5bdc5 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_w_no_preload.S.raw @@ -0,0 +1,51 @@ + +fmla v16.4s, v0.4s, v4.s[0] +ldr w5, [x1], #4 +fmla v17.4s, v1.4s, v4.s[0] +ldr w20, [x2], #4 +fmla v18.4s, v0.4s, v4.s[1] +ldr w6, [x1], #4 +fmla v20.4s, v1.4s, v4.s[1] +ldr w21, [x2], #4 +fmla v20.4s, v0.4s, v4.s[2] +ldr w7, [x1], #4 +fmla v21.4s, v1.4s, v4.s[2] +ldr w22, [x2], #4 +fmla v22.4s, v0.4s, v4.s[3] +ldr w8, [x1], #4 +fmla v23.4s, v1.4s, v4.s[3] +ldr w23, [x2], #4 + +fmla v24.4s, v0.4s, v5.s[0] +ldr w9, [x1], #4 +fmla v25.4s, v1.4s, v5.s[0] +ldr w24, [x2], #4 +fmla v26.4s, v0.4s, v5.s[1] +ldr w10, [x1], #4 +fmla v27.4s, v1.4s, v5.s[1] +ldr w25, [x2], #4 +fmla v28.4s, v0.4s, v5.s[2] +ldr w11, [x1], #4 +fmla v29.4s, v1.4s, v5.s[2] +ldr w26, [x2], #4 +fmla v30.4s, v0.4s, v5.s[3] +ldr w12, [x1], #4 +fmla v31.4s, v1.4s, v5.s[3] +ldr w27, [x2], #4 + +ins v0.s[0], w5 +ins v4.s[0], w20 +ins v1.s[0], w9 +ins v5.s[0], w24 +ins v0.s[2], w7 +ins v4.s[2], w22 +ins v1.s[2], w11 +ins v5.s[2], w26 +ins v0.s[1], w6 +ins v4.s[1], w21 +ins v1.s[1], w10 +ins v5.s[1], w25 +ins v0.s[3], w8 +ins v4.s[3], w23 +ins v1.s[3], w12 +ins v5.s[3], w27 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_w_preload.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_w_preload.S.raw new file mode 100644 index 0000000..3afc78c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_w_preload.S.raw @@ -0,0 +1,54 @@ +fmla v16.4s, v0.4s, v4.s[0] +ldr w5, [x1], #4 +fmla v17.4s, v1.4s, v4.s[0] +ldr w20, [x2], #4 +fmla v18.4s, v0.4s, v4.s[1] +ldr w6, [x1], #4 +fmla v19.4s, v1.4s, v4.s[1] +ldr w21, [x2], #4 +fmla v20.4s, v0.4s, v4.s[2] +ldr w7, [x1], #4 +fmla v21.4s, v1.4s, v4.s[2] +ldr w22, [x2], #4 +fmla v22.4s, v0.4s, v4.s[3] +ldr w8, [x1], #4 +fmla v23.4s, v1.4s, v4.s[3] +ldr w23, [x2], #4 + +fmla v24.4s, v0.4s, v5.s[0] +ldr w9, [x1], #4 +fmla v25.4s, v1.4s, v5.s[0] +ldr w24, [x2], #4 +fmla v26.4s, v0.4s, v5.s[1] +ldr w10, [x1], #4 +fmla v27.4s, v1.4s, v5.s[1] +ldr w25, [x2], #4 +fmla v28.4s, v0.4s, v5.s[2] +ldr w11, [x1], #4 +fmla v29.4s, v1.4s, v5.s[2] +ldr w26, [x2], #4 +fmla v30.4s, v0.4s, v5.s[3] +ldr w12, [x1], #4 +fmla v31.4s, v1.4s, v5.s[3] +ldr w27, [x2], #4 + +prfm pldl1keep, [x1, #256] +prfm pldl1keep, [x2, #256] + +ins v0.s[0], w5 +ins v4.s[0], w20 +ins v1.s[0], w9 +ins v5.s[0], w24 +ins v0.s[2], w7 +ins v4.s[2], w22 +ins v1.s[2], w11 +ins v5.s[2], w26 +ins v0.s[1], w6 +ins v4.s[1], w21 +ins v1.s[1], w10 +ins v5.s[1], w25 +ins v0.s[3], w8 +ins v4.s[3], w23 +ins v1.s[3], w12 +ins v5.s[3], w27 + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_x_no_preload.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_x_no_preload.S.raw new file mode 100644 index 0000000..e3822d3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_x_no_preload.S.raw @@ -0,0 +1,35 @@ + +fmla v16.4s, v0.4s, v4.s[0] +ldr x5, [x1], #8 +fmla v17.4s, v1.4s, v4.s[0] +ldr x9, [x2], #8 +fmla v18.4s, v0.4s, v4.s[1] +ldr x6, [x1], #8 +fmla v19.4s, v1.4s, v4.s[1] +ldr x10, [x2], #8 +fmla v20.4s, v0.4s, v4.s[2] +ldr x7, [x1], #8 +fmla v21.4s, v1.4s, v4.s[2] +ldr x11, [x2], #8 +fmla v22.4s, v0.4s, v4.s[3] +ldr x8, [x1], #8 +fmla v23.4s, v1.4s, v4.s[3] +ldr x12, [x2], #8 + +fmla v24.4s, v0.4s, v5.s[0] +fmla v25.4s, v1.4s, v5.s[0] +fmla v26.4s, v0.4s, v5.s[1] +fmla v27.4s, v1.4s, v5.s[1] +fmla v28.4s, v0.4s, v5.s[2] +fmla v29.4s, v1.4s, v5.s[2] +fmla v30.4s, v0.4s, v5.s[3] +fmla v31.4s, v1.4s, v5.s[3] + +ins v2.d[0], x5 +ins v6.d[0], x9 +ins v3.d[0], x7 +ins v7.d[0], x11 +ins v2.d[1], x6 +ins v6.d[1], x10 +ins v3.d[1], x8 +ins v7.d[1], x12 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_x_preload.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_x_preload.S.raw new file mode 100644 index 0000000..11081e8 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_x_preload.S.raw @@ -0,0 +1,43 @@ + +fmla v16.4s, v0.4s, v4.s[0] +ldr x5, [x1], #8 +fmla v17.4s, v1.4s, v4.s[0] +ldr x9, [x2], #8 +fmla v18.4s, v0.4s, v4.s[1] +ldr x6, [x1], #8 +fmla v19.4s, v1.4s, v4.s[1] +ldr x10, [x2], #8 +fmla v20.4s, v0.4s, v4.s[2] +ldr x7, [x1], #8 +fmla v21.4s, v1.4s, v4.s[2] +ldr x11, [x2], #8 +fmla v22.4s, v0.4s, v4.s[3] +ldr x8, [x1], #8 +fmla v23.4s, v1.4s, v4.s[3] +ldr x12, [x2], #8 + +fmla v24.4s, v0.4s, v5.s[0] +prfm pldl1keep, [x1, #256] +fmla v25.4s, v1.4s, v5.s[0] +prfm pldl1keep, [x1, #320] +fmla v26.4s, v0.4s, v5.s[1] +prfm pldl1keep, [x1, #384] +fmla v27.4s, v1.4s, v5.s[1] +prfm pldl1keep, [x1, #448] +fmla v28.4s, v0.4s, v5.s[2] +prfm pldl1keep, [x2, #256] +fmla v29.4s, v1.4s, v5.s[2] +prfm pldl1keep, [x2, #320] +fmla v30.4s, v0.4s, v5.s[3] +prfm pldl1keep, [x2, #384] +fmla v31.4s, v1.4s, v5.s[3] +prfm pldl1keep, [x2, #448] + +ins v0.d[0], x5 +ins v4.d[0], x9 +ins v1.d[0], x7 +ins v5.d[0], x11 +ins v0.d[1], x6 +ins v4.d[1], x10 +ins v1.d[1], x8 +ins v5.d[1], x12 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/naive.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/naive.S.raw new file mode 100644 index 0000000..14abb2a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop1/naive.S.raw @@ -0,0 +1,21 @@ + +fmla v16.4s, v0.4s, v4.s[0] +fmla v17.4s, v1.4s, v4.s[0] +fmla v18.4s, v0.4s, v4.s[1] +fmla v19.4s, v1.4s, v4.s[1] +fmla v20.4s, v0.4s, v4.s[2] +fmla v21.4s, v1.4s, v4.s[2] +fmla v22.4s, v0.4s, v4.s[3] +fmla v23.4s, v1.4s, v4.s[3] + +fmla v24.4s, v0.4s, v5.s[0] +fmla v25.4s, v1.4s, v5.s[0] +fmla v26.4s, v0.4s, v5.s[1] +fmla v27.4s, v1.4s, v5.s[1] +fmla v28.4s, v0.4s, v5.s[2] +fmla v29.4s, v1.4s, v5.s[2] +fmla v30.4s, v0.4s, v5.s[3] +fmla v31.4s, v1.4s, v5.s[3] + +ld1 {{ v0.4s, v1.4s }}, [x1], #32 +ld1 {{ v4.4s, v5.4s }}, [x2], #32 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop2/broken_chains.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop2/broken_chains.S.raw new file mode 100644 index 0000000..5235ac6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop2/broken_chains.S.raw @@ -0,0 +1,41 @@ +ld1 {{ v2.4s, v3.4s }}, [x1], #32 +ld1 {{ v6.4s, v7.4s }}, [x2], #32 + +fmla v16.4s, v0.4s, v4.s[0] +fmla v17.4s, v1.4s, v4.s[0] +fmla v18.4s, v0.4s, v4.s[1] +fmla v19.4s, v1.4s, v4.s[1] +fmla v20.4s, v0.4s, v4.s[2] +fmla v21.4s, v1.4s, v4.s[2] +fmla v22.4s, v0.4s, v4.s[3] +fmla v23.4s, v1.4s, v4.s[3] + +fmla v24.4s, v0.4s, v5.s[0] +fmla v25.4s, v1.4s, v5.s[0] +fmla v26.4s, v0.4s, v5.s[1] +fmla v27.4s, v1.4s, v5.s[1] +fmla v28.4s, v0.4s, v5.s[2] +fmla v29.4s, v1.4s, v5.s[2] +fmla v30.4s, v0.4s, v5.s[3] +fmla v31.4s, v1.4s, v5.s[3] + +ld1 {{ v0.4s, v1.4s }}, [x1], #32 +ld1 {{ v4.4s, v5.4s }}, [x2], #32 + +fmla v16.4s, v2.4s, v6.s[0] +fmla v17.4s, v3.4s, v6.s[0] +fmla v18.4s, v2.4s, v6.s[1] +fmla v19.4s, v3.4s, v6.s[1] +fmla v20.4s, v2.4s, v6.s[2] +fmla v21.4s, v3.4s, v6.s[2] +fmla v22.4s, v2.4s, v6.s[3] +fmla v23.4s, v3.4s, v6.s[3] + +fmla v24.4s, v2.4s, v7.s[0] +fmla v25.4s, v3.4s, v7.s[0] +fmla v26.4s, v2.4s, v7.s[1] +fmla v27.4s, v3.4s, v7.s[1] +fmla v28.4s, v2.4s, v7.s[2] +fmla v29.4s, v3.4s, v7.s[2] +fmla v30.4s, v2.4s, v7.s[3] +fmla v31.4s, v3.4s, v7.s[3] diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop2/cortex_a55.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop2/cortex_a55.S.raw new file mode 100644 index 0000000..7f87596 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8/packed_packed_loop2/cortex_a55.S.raw @@ -0,0 +1,60 @@ +fmla v16.4s, v0.4s, v4.s[0] +ldr d2, [x1], #8 +fmla v17.4s, v1.4s, v4.s[0] +ldr d6, [x2], #8 +fmla v18.4s, v0.4s, v4.s[1] +ldr x5, [x1], #8 +fmla v19.4s, v1.4s, v4.s[1] +ldr x7, [x2], #8 +fmla v20.4s, v0.4s, v4.s[2] +ldr d3, [x1], #8 +fmla v21.4s, v1.4s, v4.s[2] +ldr d7, [x2], #8 +fmla v22.4s, v0.4s, v4.s[3] +ldr x6, [x1], #8 +fmla v23.4s, v1.4s, v4.s[3] +ldr x8, [x2], #8 + +fmla v24.4s, v0.4s, v5.s[0] +fmla v25.4s, v1.4s, v5.s[0] +fmla v26.4s, v0.4s, v5.s[1] +fmla v27.4s, v1.4s, v5.s[1] +fmla v28.4s, v0.4s, v5.s[2] +ins v2.d[1], x5 +fmla v29.4s, v1.4s, v5.s[2] +ins v6.d[1], x7 +fmla v30.4s, v0.4s, v5.s[3] +ins v3.d[1], x6 +fmla v31.4s, v1.4s, v5.s[3] +ins v7.d[1], x8 + +fmla v16.4s, v2.4s, v6.s[0] +ldr d0, [x1], #8 +fmla v17.4s, v3.4s, v6.s[0] +ldr d4, [x2], #8 +fmla v18.4s, v2.4s, v6.s[1] +ldr x5, [x1], #8 +fmla v19.4s, v3.4s, v6.s[1] +ldr x7, [x2], #8 +fmla v20.4s, v2.4s, v6.s[2] +ldr d1, [x1], #8 +fmla v21.4s, v3.4s, v6.s[2] +ldr d5, [x2], #8 +fmla v22.4s, v2.4s, v6.s[3] +ldr x6, [x1], #8 +fmla v23.4s, v3.4s, v6.s[3] +ldr x8, [x2], #8 + +fmla v24.4s, v2.4s, v7.s[0] +fmla v25.4s, v3.4s, v7.s[0] +fmla v26.4s, v2.4s, v7.s[1] +fmla v27.4s, v3.4s, v7.s[1] +fmla v28.4s, v2.4s, v7.s[2] +ins v0.d[1], x5 +fmla v29.4s, v3.4s, v7.s[2] +ins v4.d[1], x7 +fmla v30.4s, v2.4s, v7.s[3] +ins v1.d[1], x6 +fmla v31.4s, v3.4s, v7.s[3] +ins v5.d[1], x8 + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8_core.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8_core.S.j2 new file mode 100644 index 0000000..e79319d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_8x8_core.S.j2 @@ -0,0 +1,182 @@ +// vim: ft=arm + +// C tile regs: v16 to v31, (scratch) +// - x19-x29 to preserve (but x19, x28, x29 not used) +// - d8..d15 to preserve +// - v16 to v31, no need to preserve +// +// v16[0] v18[0] v20[0] v22[0] v24[0] v26[0] v28[0] v30[0] +// v16[1] v18[1] +// v16[2] v18[2] +// v16[3] v18[3] +// +// v17[0] v19[0] v21[0] v23[0] v25[0] v27[0] v29[0] v31[0] +// v17[1] v19[1] +// v17[2] v19[2] +// v17[3] v19[3] + +// v0-v7 (scratch registers) +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +.cpu generic+fp+simd +.global {{G}}arm64simd_mmm_f32_8x8_{{core}}_{{suffix}} +{{G}}arm64simd_mmm_f32_8x8_{{core}}_{{suffix}}: + + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.j2" %} + +.add_mat_mul: + ldr x2, [x0, #24] // b + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + +.packed_packed: + ld1 { v0.4s, v1.4s }, [ x1 ], #32 + ld1 { v4.4s, v5.4s }, [ x2 ], #32 + +{% set packed_packed_loop1 %} + {% if core == "a53" %} + {% include "arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_x_preload.S.raw" %} + {% else %} + {% include "arm64simd_mmm_f32_8x8/packed_packed_loop1/naive.S.raw" %} + {% endif %} +{% endset %} + +{% set packed_packed_loop2 %} + {% if core == "a55" %} + {% include "arm64simd_mmm_f32_8x8/packed_packed_loop2/cortex_a55.S.raw" %} + {% else %} + {{ packed_packed_loop1 }} + {{ packed_packed_loop1 }} + {% endif %} +{% endset %} + + cmp x3, #4 + blt .packed_packed_loop_1 + +.p2align 4 +.packed_packed_loop_4: + {{ packed_packed_loop2 }} + {{ packed_packed_loop2 }} + + sub x3, x3, #4 + cmp x3, #4 + bge .packed_packed_loop_4 + + + cmp x3, #0 + beq .non_linear_loop + +.p2align 4 +.packed_packed_loop_1: + {{ packed_packed_loop1 }} + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_scalars.j2" %} +{% set mr = 8 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_per_rows.j2" %} +{% set mr = 8 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_f32_per_cols.j2" %} +{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_load_tile.j2" %} + +.add_unicast: + ldp x5, x6, [x0, #8] + ldp x7, x8, [x0, #24] + + {% for col in range(8, 16) %} + mov x4, x5 + {% for reg in range(0, 2) %} + {% for lane in range(0, 4) %} + ld1 {v0.s}[{{lane}}], [ x4 ], x6 + {% endfor %} + fadd v{{ col * 2 + reg }}.4s, v{{ col * 2 + reg }}.4s, v0.4s + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x2, [x0, #8] + ldr x3, [x0, #16] + + ld1 { v0.4s, v1.4s }, [ x2 ], #32 + ld1 { v4.4s, v5.4s }, [ x3 ], #32 + + fmla v16.4s, v0.4s, v4.s[0] + fmla v17.4s, v1.4s, v4.s[0] + fmla v18.4s, v0.4s, v4.s[1] + fmla v19.4s, v1.4s, v4.s[1] + fmla v20.4s, v0.4s, v4.s[2] + fmla v21.4s, v1.4s, v4.s[2] + fmla v22.4s, v0.4s, v4.s[3] + fmla v23.4s, v1.4s, v4.s[3] + + fmla v24.4s, v0.4s, v5.s[0] + fmla v25.4s, v1.4s, v5.s[0] + fmla v26.4s, v0.4s, v5.s[1] + fmla v27.4s, v1.4s, v5.s[1] + fmla v28.4s, v0.4s, v5.s[2] + fmla v29.4s, v1.4s, v5.s[2] + fmla v30.4s, v0.4s, v5.s[3] + fmla v31.4s, v1.4s, v5.s[3] + + b .non_linear_loop + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x6, #4 + bne .store_strides_generic + + {% for col in range(8, 16) %} + str q{{ col * 2 }}, [ x5 ] + str q{{ col * 2 + 1 }}, [ x5, #16 ] + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.store_strides_generic: + + {% for col in range(8, 16) %} + mov x4, x5 + {% for reg in range(0, 2) %} + {% for lane in range(0, 4) %} + st1 { v{{ col * 2 + reg }}.s }[{{lane}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.return: + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_per_cols.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_per_cols.j2 new file mode 100644 index 0000000..1210cca --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_per_cols.j2 @@ -0,0 +1,10 @@ +// vim: ft=arm + +{% from "arm64simd_mmm_4s_ops.j2" import per_col %} + +{{ per_col(label="per_col_min", op="fmin", mr=mr, from=from, to=to) }} +{{ per_col(label="per_col_max", op="fmax", mr=mr, from=from, to=to) }} +{{ per_col(label="per_col_mul", op="fmul", mr=mr, from=from, to=to) }} +{{ per_col(label="per_col_add", op="fadd", mr=mr, from=from, to=to) }} +{{ per_col(label="per_col_sub", op="fsub", mr=mr, from=from, to=to) }} +{{ per_col(label="per_col_sub_flipped", op="fsub", mr=mr, from=from, to=to, flipped=true) }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_per_rows.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_per_rows.j2 new file mode 100644 index 0000000..1390027 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_per_rows.j2 @@ -0,0 +1,10 @@ +// vim: ft=arm + +{% from "arm64simd_mmm_4s_ops.j2" import per_row %} + +{{ per_row(label="per_row_min", op="fmin", mr=mr, from=from, to=to) }} +{{ per_row(label="per_row_max", op="fmax", mr=mr, from=from, to=to) }} +{{ per_row(label="per_row_mul", op="fmul", mr=mr, from=from, to=to) }} +{{ per_row(label="per_row_add", op="fadd", mr=mr, from=from, to=to) }} +{{ per_row(label="per_row_sub", op="fsub", mr=mr, from=from, to=to) }} +{{ per_row(label="per_row_sub_flipped", op="fsub", mr=mr, from=from, to=to, flipped=true) }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_scalars.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_scalars.j2 new file mode 100644 index 0000000..98a1c4c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_f32_scalars.j2 @@ -0,0 +1,38 @@ +// vim: ft=arm + +{% from "arm64simd_mmm_4s_ops.j2" import scalar %} + +{{ scalar(label="scalar_min", op="fmin", from=from, to=to) }} +{{ scalar(label="scalar_max", op="fmax", from=from, to=to) }} +{{ scalar(label="scalar_mul", op="fmul", from=from, to=to) }} +{{ scalar(label="scalar_add", op="fadd", from=from, to=to) }} +{{ scalar(label="scalar_sub", op="fsub", from=from, to=to) }} +{{ scalar(label="scalar_sub_flipped", op="fsub", from=from, to=to, flipped=true) }} + + +.clear: +{% for r in range(from, to + 1) %} + eor v{{r}}.8b, v{{r}}.8b, v{{r}}.8b +{% endfor %} + b .non_linear_loop + +.leaky_relu: + add x2, x0, #8 + ld1 {v4.s}[0], [ x2 ] + dup v4.4s, v4.s[0] + + // bsl cond/dst, then, else + // fcmge dst, src, #0.0 + {% for r in range(from, to + 1) %} + fmul v0.4s, v{{r}}.4s, v4.4s + fcmge v1.4s, v{{r}}.4s, #0.0 + bsl v1.16b, v{{r}}.16b, v0.16b + and v{{r}}.16b, v1.16b, v1.16b + {% endfor %} + + b .non_linear_loop + +.q_scale: +.q_shl: +.q_shr: + b .unsupported diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_64x1.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_64x1.S.j2 new file mode 100644 index 0000000..14b4578 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_64x1.S.j2 @@ -0,0 +1,180 @@ +// vim: ft=arm + +// C tile regs: +// - x19-x29 to preserve (but x19, x28, x29 not used) +// - d8..d15 to preserve +// - v16 to v31, no need to preserve + +// no preservation either for v0-v7... +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +.cpu generic+fp+simd +.global {{G}}arm64simd_mmm_i32_64x1_{{suffix}} +{{G}}arm64simd_mmm_i32_64x1_{{suffix}}: + +/* + prfm pldl1keep, [x1] + prfm pldl1keep, [x2] +*/ + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.j2" %} + +.add_mat_mul: + ldp x2, x4, [x0, #24] // b, packing + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + + cmp x4, #1 + beq .packed_packed_loop_1_i8i8 + +.packed_packed_loop_1: + ld1 {v9.s}[0], [ x2 ], 4 + + ld1 { v0.4s-v3.4s }, [ x1 ], #64 + ld1 { v4.4s-v7.4s }, [ x1 ], #64 + {% for reg in range(0, 4) %} + mla v{{ reg * 2 + 16 }}.4s, v{{ reg * 2 }}.4s, v9.s[0] + mla v{{ reg * 2 + 17 }}.4s, v{{ reg * 2 + 1 }}.4s, v9.s[0] + {% endfor %} + + ld1 { v0.4s-v3.4s }, [ x1 ], #64 + ld1 { v4.4s-v7.4s }, [ x1 ], #64 + {% for reg in range(0, 4) %} + mla v{{ reg * 2 + 24 }}.4s, v{{ reg * 2 }}.4s, v9.s[0] + mla v{{ reg * 2 + 25 }}.4s, v{{ reg * 2 + 1 }}.4s, v9.s[0] + {% endfor %} + + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +.packed_packed_loop_1_i8i8: + ld1 {v9.b}[0], [ x2 ], 1 + sshll v9.8h, v9.8b, 0 + + ld1 { v0.8b-v3.8b }, [ x1 ], #32 + ld1 { v4.8b-v7.8b }, [ x1 ], #32 + + {% for reg in range(0, 8) %} + sshll v10.8h, v{{reg}}.8b, 0 + smlal v{{ reg * 2 + 16 }}.4s, v10.4h, v9.h[0] + smlal2 v{{ reg * 2 + 17 }}.4s, v10.8h, v9.h[0] + {% endfor %} + + subs x3, x3, #1 + bne .packed_packed_loop_1_i8i8 + + b .non_linear_loop + +.add_unicast: + ldp x5, x6, [x0, #8] + ldp x7, x8, [x0, #24] + + cmp x8, #4 + beq non_linear_addc_i32 + + {% for reg in range(16, 32) %} + {% for lane in range(0, 4) %} + ld1 {v0.b}[{{lane}}], [ x5 ], x6 + {% endfor %} + sshll v0.8h, v0.8b, 0 + sshll v0.4s, v0.4h, 0 + add v{{reg}}.4s, v{{reg}}.4s, v0.4s + {% endfor %} + + b .non_linear_loop + +non_linear_addc_i32: + {% for reg in range(16, 32) %} + {% for lane in range(0, 4) %} + ld1 {v0.s}[{{lane}}], [ x5 ], x6 + {% endfor %} + add v{{reg}}.4s, v{{reg}}.4s, v0.4s + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x2, [x0, #8] + ldr x3, [x0, #16] + + ld1 { v15.s }[0], [ x3 ] + xtn v15.4h, v15.4s + + ld1 { v0.4s-v3.4s }, [ x2 ], #64 + ld1 { v4.4s-v7.4s }, [ x2 ], #64 + + {% for reg in range(0, 8) %} + xtn v{{reg}}.4h, v{{reg}}.4s + smlal v{{ reg + 16 }}.4s, v{{reg}}.4h, v15.h[0] + {% endfor %} + + ld1 { v0.4s-v3.4s }, [ x2 ], #64 + ld1 { v4.4s-v7.4s }, [ x2 ], #64 + + {% for reg in range(0, 8) %} + xtn v{{reg}}.4h, v{{reg}}.4s + smlal v{{ reg + 24 }}.4s, v{{reg}}.4h, v15.h[0] + {% endfor %} + + b .non_linear_loop + +{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_i32_scalars.j2" %} +{% set mr = 64 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_i32_per_rows.j2" %} +{% set mr = 64 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_i32_per_cols.j2" %} +{% include "arm64simd_mmm_i32_scale_q16_q31.j2" %} +{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_load_tile.j2" %} + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x8, #4 + beq .store_strides_i32 + + {% for reg in range(16, 32) %} + {% for lane in range(0, 4) %} + st1 { v{{reg}}.b }[{{ lane * 4 }}], [ x5 ], x6 + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.store_strides_i32: + {% for reg in range(16, 32) %} + {% for lane in range(0, 4) %} + st1 { v{{reg}}.s }[{{lane}}], [ x5 ], x6 + {% endfor %} + {% endfor %} + + b .non_linear_loop + +.return: + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_8x8.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_8x8.S.j2 new file mode 100644 index 0000000..6f924fa --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_8x8.S.j2 @@ -0,0 +1,234 @@ +// vim: ft=arm + +// C tile regs: +// - x19-x29 to preserve (but x19, x28, x29 not used) +// - d8..d15 to preserve +// - v16 to v31, no need to preserve +// +// v16[0] v18[0] v20[0] v22[0] v24[0] v26[0] v28[0] v30[0] +// v16[1] v18[1] +// v16[2] v18[2] +// v16[3] v18[3] +// +// v17[0] v19[0] v21[0] v23[0] v25[0] v27[0] v29[0] v31[0] +// v17[1] v19[1] +// v17[2] v19[2] +// v17[3] v19[3] + +// no preservation either for v0-v7... +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +.cpu generic+fp+simd +.global {{G}}arm64simd_mmm_i32_8x8_{{suffix}} +{{G}}arm64simd_mmm_i32_8x8_{{suffix}}: + +/* + prfm pldl1keep, [x1] + prfm pldl1keep, [x2] +*/ + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.j2" %} + +.add_mat_mul: + ldp x2, x4, [x0, #24] // b, packing + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + + cmp x4, #1 + beq .packed_packed_loop_1_i8i8 + +.packed_packed_loop_1: + + ld1 { v0.4s, v1.4s }, [ x1 ], #32 + ld1 { v4.4s, v5.4s }, [ x2 ], #32 + + mla v16.4s, v0.4s, v4.s[0] + mla v17.4s, v1.4s, v4.s[0] + mla v18.4s, v0.4s, v4.s[1] + mla v19.4s, v1.4s, v4.s[1] + + mla v20.4s, v0.4s, v4.s[2] + mla v21.4s, v1.4s, v4.s[2] + mla v22.4s, v0.4s, v4.s[3] + mla v23.4s, v1.4s, v4.s[3] + + mla v24.4s, v0.4s, v5.s[0] + mla v25.4s, v1.4s, v5.s[0] + mla v26.4s, v0.4s, v5.s[1] + mla v27.4s, v1.4s, v5.s[1] + + mla v28.4s, v0.4s, v5.s[2] + mla v29.4s, v1.4s, v5.s[2] + mla v30.4s, v0.4s, v5.s[3] + mla v31.4s, v1.4s, v5.s[3] + + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +.packed_packed_loop_1_i8i8: + + ld1 { v0.8b }, [ x1 ], #8 + sshll v0.8h, v0.8b, 0 + ld1 { v4.8b }, [ x2 ], #8 + sshll v4.8h, v4.8b, 0 + + smlal v16.4s, v0.4h, v4.h[0] + smlal2 v17.4s, v0.8h, v4.h[0] + smlal v18.4s, v0.4h, v4.h[1] + smlal2 v19.4s, v0.8h, v4.h[1] + smlal v20.4s, v0.4h, v4.h[2] + smlal2 v21.4s, v0.8h, v4.h[2] + smlal v22.4s, v0.4h, v4.h[3] + smlal2 v23.4s, v0.8h, v4.h[3] + + smlal v24.4s, v0.4h, v4.h[4] + smlal2 v25.4s, v0.8h, v4.h[4] + smlal v26.4s, v0.4h, v4.h[5] + smlal2 v27.4s, v0.8h, v4.h[5] + smlal v28.4s, v0.4h, v4.h[6] + smlal2 v29.4s, v0.8h, v4.h[6] + smlal v30.4s, v0.4h, v4.h[7] + smlal2 v31.4s, v0.8h, v4.h[7] + + subs x3, x3, #1 + bne .packed_packed_loop_1_i8i8 + + b .non_linear_loop + +{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_i32_scalars.j2" %} +{% set mr = 8 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_i32_per_rows.j2" %} +{% set mr = 8 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_i32_per_cols.j2" %} +{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_load_tile.j2" %} + +.add_unicast: + ldp x5, x6, [x0, #8] + ldp x7, x8, [x0, #24] + + cmp x8, #4 + beq non_linear_addc_i32 + + {% for col in range(8, 16) %} + mov x4, x5 + {% for reg in range(0, 2) %} + {% for lane in range(0, 4) %} + ld1 {v0.b}[{{lane}}], [ x4 ], x6 + {% endfor %} + sshll v0.8h, v0.8b, 0 + sshll v0.4s, v0.4h, 0 + add v{{ col * 2 + reg }}.4s, v{{ col * 2 + reg }}.4s, v0.4s + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +non_linear_addc_i32: + {% for col in range(8, 16) %} + mov x4, x5 + {% for reg in range(0, 2) %} + {% for lane in range(0, 4) %} + ld1 {v0.s}[{{lane}}], [ x4 ], x6 + {% endfor %} + add v{{ col * 2 + reg }}.4s, v{{ col * 2 + reg }}.4s, v0.4s + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x2, [x0, #8] + ldr x3, [x0, #16] + + ld1 { v0.4s, v1.4s }, [ x2 ] + ld1 { v4.4s, v5.4s }, [ x3 ] + + xtn v0.4h, v0.4s + xtn v1.4h, v1.4s + xtn v4.4h, v4.4s + xtn v5.4h, v5.4s + + smlal v16.4s, v0.4h, v4.h[0] + smlal v17.4s, v1.4h, v4.h[0] + smlal v18.4s, v0.4h, v4.h[1] + smlal v19.4s, v1.4h, v4.h[1] + smlal v20.4s, v0.4h, v4.h[2] + smlal v21.4s, v1.4h, v4.h[2] + smlal v22.4s, v0.4h, v4.h[3] + smlal v23.4s, v1.4h, v4.h[3] + + smlal v24.4s, v0.4h, v5.h[0] + smlal v25.4s, v1.4h, v5.h[0] + smlal v26.4s, v0.4h, v5.h[1] + smlal v27.4s, v1.4h, v5.h[1] + smlal v28.4s, v0.4h, v5.h[2] + smlal v29.4s, v1.4h, v5.h[2] + smlal v30.4s, v0.4h, v5.h[3] + smlal v31.4s, v1.4h, v5.h[3] + + b .non_linear_loop + + {% include "arm64simd_mmm_i32_scale_q16_q31.j2" %} + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x8, #4 + beq .store_strides_i32 + + {% for col in range(8, 16) %} + mov x4, x5 + {% for reg in range(0, 2) %} + {% for lane in range(0, 4) %} + st1 { v{{ col * 2 + reg }}.b }[{{ lane * 4 }}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.store_strides_i32: + {% for col in range(8, 16) %} + mov x4, x5 + {% for reg in range(0, 2) %} + {% for lane in range(0, 4) %} + st1 { v{{ col * 2 + reg }}.s }[{{lane}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.return: + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_8x8_dot.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_8x8_dot.S.j2 new file mode 100644 index 0000000..0a63a74 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_8x8_dot.S.j2 @@ -0,0 +1,235 @@ +// vim: ft=arm + +// C tile regs: +// - x19-x29 to preserve (but x19, x28, x29 not used) +// - d8..d15 to preserve +// - v16 to v31, no need to preserve +// +// v16[0] v18[0] v20[0] v22[0] v24[0] v26[0] v28[0] v30[0] +// v16[1] v18[1] +// v16[2] v18[2] +// v16[3] v18[3] +// +// v17[0] v19[0] v21[0] v23[0] v25[0] v27[0] v29[0] v31[0] +// v17[1] v19[1] +// v17[2] v19[2] +// v17[3] v19[3] + +// no preservation either for v0-v7... +// packed A buffering (2x8 values): alternating v0, v1 with v2, v3 +// packed B buffering (2x8 values): alternating v4, v5 with v6, v7 + +.text +.align 4 + +.cpu generic+fp+simd+dotprod +.global {{G}}arm64simd_mmm_i32_8x8_dot_{{suffix}} +{{G}}arm64simd_mmm_i32_8x8_dot_{{suffix}}: + +/* + prfm pldl1keep, [x1] + prfm pldl1keep, [x2] +*/ + stp x20, x21, [sp, #-16]! + stp x22, x23, [sp, #-16]! + stp x24, x25, [sp, #-16]! + stp x26, x27, [sp, #-16]! + + stp d8, d9, [sp, #-16]! + stp d10, d11, [sp, #-16]! + stp d12, d13, [sp, #-16]! + stp d14, d15, [sp, #-16]! + +{% include "dispatcher.j2" %} + +.add_mat_mul: + ldp x2, x4, [x0, #24] // b, packing + ldp x3, x1, [x0, #8] // k, a + + cmp x3, #0 + beq .non_linear_loop + + cmp x4, #1 + beq .packed_packed_loop_1_i8i8 + +.packed_packed_loop_1: + + ld1 { v0.4s, v1.4s }, [ x1 ], #32 + ld1 { v4.4s, v5.4s }, [ x2 ], #32 + + mla v16.4s, v0.4s, v4.s[0] + mla v17.4s, v1.4s, v4.s[0] + mla v18.4s, v0.4s, v4.s[1] + mla v19.4s, v1.4s, v4.s[1] + + mla v20.4s, v0.4s, v4.s[2] + mla v21.4s, v1.4s, v4.s[2] + mla v22.4s, v0.4s, v4.s[3] + mla v23.4s, v1.4s, v4.s[3] + + mla v24.4s, v0.4s, v5.s[0] + mla v25.4s, v1.4s, v5.s[0] + mla v26.4s, v0.4s, v5.s[1] + mla v27.4s, v1.4s, v5.s[1] + + mla v28.4s, v0.4s, v5.s[2] + mla v29.4s, v1.4s, v5.s[2] + mla v30.4s, v0.4s, v5.s[3] + mla v31.4s, v1.4s, v5.s[3] + + subs x3, x3, #1 + bne .packed_packed_loop_1 + + b .non_linear_loop + +.packed_packed_loop_1_i8i8: + // PackedI8K4 (K=4-inner, r=8): per 4-K block, A is m0-3 (v0) / m4-7 (v1), + // B is n0-3 (v4) / n4-7 (v5), each lane a 4xi8 group. SDOT by-element dots + // a B column's 4 K against all 4 m rows of an A half. Same v16..v31 tile + // layout as the SMLAL kernel: v[16 + n*2 + m_half] = C[m_half*4..][n]. + ld1 { v0.16b, v1.16b }, [ x1 ], #32 + ld1 { v4.16b, v5.16b }, [ x2 ], #32 + + sdot v16.4s, v0.16b, v4.4b[0] + sdot v17.4s, v1.16b, v4.4b[0] + sdot v18.4s, v0.16b, v4.4b[1] + sdot v19.4s, v1.16b, v4.4b[1] + sdot v20.4s, v0.16b, v4.4b[2] + sdot v21.4s, v1.16b, v4.4b[2] + sdot v22.4s, v0.16b, v4.4b[3] + sdot v23.4s, v1.16b, v4.4b[3] + + sdot v24.4s, v0.16b, v5.4b[0] + sdot v25.4s, v1.16b, v5.4b[0] + sdot v26.4s, v0.16b, v5.4b[1] + sdot v27.4s, v1.16b, v5.4b[1] + sdot v28.4s, v0.16b, v5.4b[2] + sdot v29.4s, v1.16b, v5.4b[2] + sdot v30.4s, v0.16b, v5.4b[3] + sdot v31.4s, v1.16b, v5.4b[3] + + subs x3, x3, #4 + bgt .packed_packed_loop_1_i8i8 + + b .non_linear_loop + +{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_i32_scalars.j2" %} +{% set mr = 8 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_i32_per_rows.j2" %} +{% set mr = 8 %}{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_i32_per_cols.j2" %} +{% set from = 16 %}{% set to = 31 %}{% include "arm64simd_mmm_load_tile.j2" %} + +.add_unicast: + ldp x5, x6, [x0, #8] + ldp x7, x8, [x0, #24] + + cmp x8, #4 + beq non_linear_addc_i32 + + {% for col in range(8, 16) %} + mov x4, x5 + {% for reg in range(0, 2) %} + {% for lane in range(0, 4) %} + ld1 {v0.b}[{{lane}}], [ x4 ], x6 + {% endfor %} + sshll v0.8h, v0.8b, 0 + sshll v0.4s, v0.4h, 0 + add v{{ col * 2 + reg }}.4s, v{{ col * 2 + reg }}.4s, v0.4s + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +non_linear_addc_i32: + {% for col in range(8, 16) %} + mov x4, x5 + {% for reg in range(0, 2) %} + {% for lane in range(0, 4) %} + ld1 {v0.s}[{{lane}}], [ x4 ], x6 + {% endfor %} + add v{{ col * 2 + reg }}.4s, v{{ col * 2 + reg }}.4s, v0.4s + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.add_row_col_products: + ldr x2, [x0, #8] + ldr x3, [x0, #16] + + ld1 { v0.4s, v1.4s }, [ x2 ] + ld1 { v4.4s, v5.4s }, [ x3 ] + + xtn v0.4h, v0.4s + xtn v1.4h, v1.4s + xtn v4.4h, v4.4s + xtn v5.4h, v5.4s + + smlal v16.4s, v0.4h, v4.h[0] + smlal v17.4s, v1.4h, v4.h[0] + smlal v18.4s, v0.4h, v4.h[1] + smlal v19.4s, v1.4h, v4.h[1] + smlal v20.4s, v0.4h, v4.h[2] + smlal v21.4s, v1.4h, v4.h[2] + smlal v22.4s, v0.4h, v4.h[3] + smlal v23.4s, v1.4h, v4.h[3] + + smlal v24.4s, v0.4h, v5.h[0] + smlal v25.4s, v1.4h, v5.h[0] + smlal v26.4s, v0.4h, v5.h[1] + smlal v27.4s, v1.4h, v5.h[1] + smlal v28.4s, v0.4h, v5.h[2] + smlal v29.4s, v1.4h, v5.h[2] + smlal v30.4s, v0.4h, v5.h[3] + smlal v31.4s, v1.4h, v5.h[3] + + b .non_linear_loop + + {% include "arm64simd_mmm_i32_scale_q16_q31.j2" %} + +.store: + ldp x5, x6, [x0, #8] // c base ptr, rsc + ldp x7, x8, [x0, #24] // csc, item_size + + cmp x8, #4 + beq .store_strides_i32 + + {% for col in range(8, 16) %} + mov x4, x5 + {% for reg in range(0, 2) %} + {% for lane in range(0, 4) %} + st1 { v{{ col * 2 + reg }}.b }[{{ lane * 4 }}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.store_strides_i32: + {% for col in range(8, 16) %} + mov x4, x5 + {% for reg in range(0, 2) %} + {% for lane in range(0, 4) %} + st1 { v{{ col * 2 + reg }}.s }[{{lane}}], [ x4 ], x6 + {% endfor %} + {% endfor %} + add x5, x5, x7 + {% endfor %} + + b .non_linear_loop + +.return: + ldp d14, d15, [sp], #16 + ldp d12, d13, [sp], #16 + ldp d10, d11, [sp], #16 + ldp d8, d9, [sp], #16 + + ldp x26, x27, [sp], #16 + ldp x24, x25, [sp], #16 + ldp x22, x23, [sp], #16 + ldp x20, x21, [sp], #16 + + ret + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_per_cols.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_per_cols.j2 new file mode 100644 index 0000000..bf40cd2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_per_cols.j2 @@ -0,0 +1,10 @@ +// vim: ft=arm + +{% from "arm64simd_mmm_4s_ops.j2" import per_col %} + +{{ per_col(label="per_col_min", op="smin", mr=mr, from=from, to=to) }} +{{ per_col(label="per_col_max", op="smax", mr=mr, from=from, to=to) }} +{{ per_col(label="per_col_mul", op="mul", mr=mr, from=from, to=to) }} +{{ per_col(label="per_col_add", op="add", mr=mr, from=from, to=to) }} +{{ per_col(label="per_col_sub", op="sub", mr=mr, from=from, to=to) }} +{{ per_col(label="per_col_sub_flipped", op="sub", mr=mr, from=from, to=to, flipped=true) }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_per_rows.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_per_rows.j2 new file mode 100644 index 0000000..3446ffa --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_per_rows.j2 @@ -0,0 +1,10 @@ +// vim: ft=arm + +{% from "arm64simd_mmm_4s_ops.j2" import per_row %} + +{{ per_row(label="per_row_min", op="smin", mr=mr, from=from, to=to) }} +{{ per_row(label="per_row_max", op="smax", mr=mr, from=from, to=to) }} +{{ per_row(label="per_row_mul", op="mul", mr=mr, from=from, to=to) }} +{{ per_row(label="per_row_add", op="add", mr=mr, from=from, to=to) }} +{{ per_row(label="per_row_sub", op="sub", mr=mr, from=from, to=to) }} +{{ per_row(label="per_row_sub_flipped", op="sub", mr=mr, from=from, to=to, flipped=true) }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_scalars.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_scalars.j2 new file mode 100644 index 0000000..df65831 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_scalars.j2 @@ -0,0 +1,32 @@ +// vim: ft=arm + +{% from "arm64simd_mmm_4s_ops.j2" import scalar %} + +{{ scalar(label="scalar_min", op="smin", from=from, to=to) }} +{{ scalar(label="scalar_max", op="smax", from=from, to=to) }} +{{ scalar(label="scalar_mul", op="mul", from=from, to=to) }} +{{ scalar(label="scalar_add", op="add", from=from, to=to) }} +{{ scalar(label="scalar_sub", op="sub", from=from, to=to) }} +{{ scalar(label="scalar_sub_flipped", op="sub", from=from, to=to, flipped=true) }} + +.clear: +{% for r in range(from, to + 1) %} + eor v{{r}}.8b, v{{r}}.8b, v{{r}}.8b +{% endfor %} + b .non_linear_loop + +.leaky_relu: + add x2, x0, #8 + ld1 {v4.s}[0], [ x2 ] + dup v4.4s, v4.s[0] + + // bsl cond/dst, then, else + // fcmge dst, src, #0.0 + {% for r in range(from, to + 1) %} + mul v0.4s, v{{r}}.4s, v4.4s + cmge v1.4s, v{{r}}.4s, #0 + bsl v1.16b, v{{r}}.16b, v0.16b + and v{{r}}.16b, v1.16b, v1.16b + {% endfor %} + + b .non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_scale_q16_q31.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_scale_q16_q31.j2 new file mode 100644 index 0000000..59e802d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_i32_scale_q16_q31.j2 @@ -0,0 +1,267 @@ + +// vim: ft=arm + +.q_scale: + ldp x5, x6, [x0, #8] // x5: shift, x6: policy + add x2, x0, #24 + ld1r { v2.4s }, [x2] // v2.4s <- multiplier + + mov w3, #1 + ins v4.d[0], x3 + dup v4.2d, v4.d[0] // v4.2d <- 1 + + add x5, x5, #32 // add 32 to shift + neg x5, x5 // broadcast shift + ins v1.d[0], x5 + dup v1.2d, v1.d[0] // v1.2s <- -(shift + 32) + + cmp x6, 1 + beq .q_scale_rounding_zero + cmp x6, 2 + beq .q_scale_rounding_away + cmp x6, 3 + beq .q_scale_rounding_minus_inf + cmp x6, 4 + beq .q_scale_rounding_plus_inf + cmp x6, 5 + beq .q_scale_rounding_even + cmp x6, 6 + beq .q_scale_rounding_odd + + b .unsupported + +.q_scale_rounding_zero: + // rust: signum * ((abs + nudge2) >> shift + // asm: signum * (2*abs - 1) >>r (shift + 1) + + {% for q in range(16, 32) %} + cmlt v0.4s, v{{q}}.4s, #0 + abs v{{q}}.4s, v{{q}}.4s + sqdmull v8.2d, v{{q}}.2s, v2.2s + sqdmull2 v9.2d, v{{q}}.4s, v2.4s //mul without shift and store results in v8 and v9 + + sub v8.2d, v8.2d, v4.2d + sqrshl v8.2d, v8.2d, v1.2d + + sub v9.2d, v9.2d, v4.2d + sqrshl v9.2d, v9.2d, v1.2d + + uzp1 v{{q}}.4s, v8.4s, v9.4s //combine back + + neg v3.4s, v{{q}}.4s + bit v{{q}}.16b, v3.16b, v0.16b + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_away: // signum * (abs >> (shift-1) + 1 >> 1) + + {% for q in range(16, 32) %} + cmlt v0.4s, v{{q}}.4s, #0 + abs v{{q}}.4s, v{{q}}.4s + sqdmull v8.2d, v{{q}}.2s, v2.2s + sqdmull2 v9.2d, v{{q}}.4s, v2.4s //mul without shift and store results in v8 and v9 + + sqrshl v8.2d, v8.2d, v1.2d + sqrshl v9.2d, v9.2d, v1.2d + + uzp1 v{{q}}.4s, v8.4s, v9.4s //combine back + + neg v3.4s, v{{q}}.4s + bit v{{q}}.16b, v3.16b, v0.16b + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_minus_inf: // val >> shift + + {% for q in range(16, 32) %} + sqdmull v8.2d, v{{q}}.2s, v2.2s + sqdmull2 v9.2d, v{{q}}.4s, v2.4s //mul without shift and store results in v8 and v9 + + sub v8.2d, v8.2d, v4.2d + sqrshl v8.2d, v8.2d, v1.2d + + sub v9.2d, v9.2d, v4.2d + sqrshl v9.2d, v9.2d, v1.2d + + uzp1 v{{q}}.4s, v8.4s, v9.4s //combine back + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_plus_inf: // (val >> shift-1)+1 >>1 + + {% for q in range(16, 32) %} + sqdmull v8.2d, v{{q}}.2s, v2.2s + sqdmull2 v9.2d, v{{q}}.4s, v2.4s //mul without shift and store results in v8 and v9 + + sqrshl v8.2d, v8.2d, v1.2d + sqrshl v9.2d, v9.2d, v1.2d + + uzp1 v{{q}}.4s, v8.4s, v9.4s //combine back + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_even: // signum * ((abs >> shift-1) + (abs & 0x1) - 1 >> 1) + + {% for q in range(16, 32) %} + cmlt v0.4s, v{{q}}.4s, #0 + abs v{{q}}.4s, v{{q}}.4s + sqdmull v8.2d, v{{q}}.2s, v2.2s + sqdmull2 v9.2d, v{{q}}.4s, v2.4s //mul without shift and store results in v8 and v9 + + sqshl v3.2d, v8.2d, v1.2d // abs >> shift - 1 + and v3.16b, v3.16b, v4.16b // abs & 0x1 + sub v3.2d, v3.2d, v4.2d //nudge : -1 if we want to round down, 0 if up + + add v8.2d, v8.2d, v3.2d + sqrshl v8.2d, v8.2d, v1.2d + + sqshl v3.2d, v9.2d, v1.2d + and v3.16b, v3.16b, v4.16b + sub v3.2d, v3.2d, v4.2d //nudge : -1 if we want to round down, 0 if up + + add v9.2d, v9.2d, v3.2d + sqrshl v9.2d, v9.2d, v1.2d + + uzp1 v{{q}}.4s, v8.4s, v9.4s //combine back + + neg v3.4s, v{{q}}.4s + bit v{{q}}.16b, v3.16b, v0.16b + {% endfor %} + + b .non_linear_loop + +.q_scale_rounding_odd: // signum * ((abs >> shift-1) - (abs & 0x1) >> 1) + + {% for q in range(16, 32) %} + cmlt v0.4s, v{{q}}.4s, #0 + abs v{{q}}.4s, v{{q}}.4s + sqdmull v8.2d, v{{q}}.2s, v2.2s + sqdmull2 v9.2d, v{{q}}.4s, v2.4s //mul without shift and store results in v8 and v9 + + sqshl v3.2d, v8.2d, v1.2d + and v3.16b, v3.16b, v4.16b //nudge : -1 if we want to round down, 0 if up + + sub v8.2d, v8.2d, v3.2d + sqrshl v8.2d, v8.2d, v1.2d + + sqshl v3.2d, v9.2d, v1.2d + and v3.16b, v3.16b, v4.16b //nudge : -1 if we want to round down, 0 if up + + sub v9.2d, v9.2d, v3.2d + sqrshl v9.2d, v9.2d, v1.2d + + uzp1 v{{q}}.4s, v8.4s, v9.4s //combine back + + neg v3.4s, v{{q}}.4s + bit v{{q}}.16b, v3.16b, v0.16b + {% endfor %} + + b .non_linear_loop + +.q_shl: + ldr x5, [x0, #8] // x5: shift + ins v1.s[0], w5 + dup v1.4s, v1.s[0] // v1.4s <- shift + + {% for q in range(16, 32) %} + sqrshl v{{q}}.4s, v{{q}}.4s, v1.4s + {% endfor %} + b .non_linear_loop + +.q_shr: + ldp x5, x6, [x0, #8] // x5: shift, x6: policy + + mov w3, #1 + ins v4.s[0], w3 + dup v4.4s, v4.s[0] // v4.4d <- 1 + + neg w5, w5 // broadcast shift + ins v1.s[0], w5 + dup v1.4s, v1.s[0] // v1.4s <- -shift + + cmp x6, 1 + beq .q_shr_rounding_zero + cmp x6, 2 + beq .q_shr_rounding_away + cmp x6, 3 + beq .q_shr_rounding_minus_inf + cmp x6, 4 + beq .q_shr_rounding_plus_inf + cmp x6, 5 + beq .q_shr_rounding_even + cmp x6, 6 + beq .q_shr_rounding_odd + + b .unsupported + +.q_shr_rounding_zero: + // asm: signum * (abs >>r shift) + {% for q in range(16, 32) %} + cmlt v0.4s, v{{q}}.4s, #0 + abs v{{q}}.4s, v{{q}}.4s + + sub v{{q}}.4s, v{{q}}.4s, v4.4s + sqrshl v{{q}}.4s, v{{q}}.4s, v1.4s + + neg v3.4s, v{{q}}.4s + bit v{{q}}.16b, v3.16b, v0.16b + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_away: + {% for q in range(16, 32) %} + cmlt v0.4s, v{{q}}.4s, #0 + abs v{{q}}.4s, v{{q}}.4s + + sqrshl v{{q}}.4s, v{{q}}.4s, v1.4s + + neg v3.4s, v{{q}}.4s + bit v{{q}}.16b, v3.16b, v0.16b + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_minus_inf: + {% for q in range(16, 32) %} + sqneg v{{q}}.4s, v{{q}}.4s + sqrshl v{{q}}.4s, v{{q}}.4s, v1.4s + sqneg v{{q}}.4s, v{{q}}.4s + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_plus_inf: + {% for q in range(16, 32) %} + sqrshl v{{q}}.4s, v{{q}}.4s, v1.4s + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_even: + // sqrshl is round(+inf), sqshl trauncates + // we look at parity of result by truncation: if it's odd, we have nothing more to do, we go towards +inf + // if it's even, we need to nudge towards 0 by adding -1 + // => nudge = (x >>l shift) & 0x1 - 1 (>>l is sqshl) + // => result is (x + nudge) >>r shift (with sqrshl) + {% for q in range(16, 32) %} + sqshl v3.4s, v{{q}}.4s, v1.4s // trunc + and v3.16b, v3.16b, v4.16b + sub v3.4s, v3.4s, v4.4s + add v{{q}}.4s, v{{q}}.4s, v3.4s + + sqrshl v{{q}}.4s, v{{q}}.4s, v1.4s + {% endfor %} + b .non_linear_loop + +.q_shr_rounding_odd: + // here: nudge is -((x >>l shift) & 0x1) + {% for q in range(16, 32) %} + sqshl v3.4s, v{{q}}.4s, v1.4s // trunc + and v3.16b, v3.16b, v4.16b + neg v3.4s, v3.4s + add v{{q}}.4s, v{{q}}.4s, v3.4s + + sqrshl v{{q}}.4s, v{{q}}.4s, v1.4s + {% endfor %} + b .non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_load_tile.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_load_tile.j2 new file mode 100644 index 0000000..3631e95 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_mmm_load_tile.j2 @@ -0,0 +1,10 @@ +// vim: ft=arm + +.load_tile: + ldr x2, [ x0, #8 ] + {% for reg in range(from, to + 1) %} + ld1 { v{{reg}}.4s }, [ x2 ], #16 + {% endfor %} + + b .non_linear_loop + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_sigmoid_f32_4n.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_sigmoid_f32_4n.S.j2 new file mode 100644 index 0000000..84b927e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_sigmoid_f32_4n.S.j2 @@ -0,0 +1,206 @@ +// vim: ft=arm + +// no preservation either for v0-v7 and v16-v31 + +.text +.align 4 + +.cpu generic+fp+simd +.global {{G}}arm64simd_sigmoid_f32_4n_{{suffix}} +{{G}}arm64simd_sigmoid_f32_4n_{{suffix}}: + + cmp x1, #0 + beq .return + + adr x2, .coeffs_num + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x2] + dup v5.4s, v0.s[0] // v5 <- low, broadcasted + dup v6.4s, v0.s[1] // v6 <- high, broadcasted + dup v7.4s, v3.s[1] // v7 <- 0.5, broadcasted + + cmp x1, #16 + blt .loop + +.loop4: + ld1 { v16.4s, v17.4s, v18.4s, v19.4s }, [x0] + + fmax v16.4s, v16.4s, v5.4s + fmax v17.4s, v17.4s, v5.4s + fmax v18.4s, v18.4s, v5.4s + fmax v19.4s, v19.4s, v5.4s + + fmin v16.4s, v16.4s, v6.4s + fmin v17.4s, v17.4s, v6.4s + fmin v18.4s, v18.4s, v6.4s + fmin v19.4s, v19.4s, v6.4s // v16 <- x + + fmul v20.4s, v16.4s, v16.4s + fmul v21.4s, v17.4s, v17.4s + fmul v22.4s, v18.4s, v18.4s + fmul v23.4s, v19.4s, v19.4s // v20 <- x2 + + dup v24.4s, v0.s[3] + fmla v24.4s, v20.4s, v0.s[2] + dup v25.4s, v0.s[3] + fmla v25.4s, v21.4s, v0.s[2] + dup v26.4s, v0.s[3] + fmla v26.4s, v22.4s, v0.s[2] + dup v27.4s, v0.s[3] + fmla v27.4s, v23.4s, v0.s[2] + + dup v28.4s, v1.s[0] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v1.s[0] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v1.s[0] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v1.s[0] + fmla v31.4s, v23.4s, v27.4s + + dup v24.4s, v1.s[1] + fmla v24.4s, v20.4s, v28.4s + dup v25.4s, v1.s[1] + fmla v25.4s, v21.4s, v29.4s + dup v26.4s, v1.s[1] + fmla v26.4s, v22.4s, v30.4s + dup v27.4s, v1.s[1] + fmla v27.4s, v23.4s, v31.4s + + dup v28.4s, v1.s[2] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v1.s[2] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v1.s[2] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v1.s[2] + fmla v31.4s, v23.4s, v27.4s + + dup v24.4s, v1.s[3] + fmla v24.4s, v20.4s, v28.4s + dup v25.4s, v1.s[3] + fmla v25.4s, v21.4s, v29.4s + dup v26.4s, v1.s[3] + fmla v26.4s, v22.4s, v30.4s + dup v27.4s, v1.s[3] + fmla v27.4s, v23.4s, v31.4s + + dup v28.4s, v2.s[0] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v2.s[0] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v2.s[0] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v2.s[0] + fmla v31.4s, v23.4s, v27.4s + + fmul v16.4s, v16.4s, v28.4s + fmul v17.4s, v17.4s, v29.4s + fmul v18.4s, v18.4s, v30.4s + fmul v19.4s, v19.4s, v31.4s // v16 <- numerator + + dup v24.4s, v2.s[2] + fmla v24.4s, v20.4s, v2.s[1] + dup v25.4s, v2.s[2] + fmla v25.4s, v21.4s, v2.s[1] + dup v26.4s, v2.s[2] + fmla v26.4s, v22.4s, v2.s[1] + dup v27.4s, v2.s[2] + fmla v27.4s, v23.4s, v2.s[1] + + dup v28.4s, v2.s[3] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v2.s[3] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v2.s[3] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v2.s[3] + fmla v31.4s, v23.4s, v27.4s + + dup v24.4s, v3.s[0] + fmla v24.4s, v20.4s, v28.4s + dup v25.4s, v3.s[0] + fmla v25.4s, v21.4s, v29.4s + dup v26.4s, v3.s[0] + fmla v26.4s, v22.4s, v30.4s + dup v27.4s, v3.s[0] + fmla v27.4s, v23.4s, v31.4s // v24 denum + + fdiv v16.4s, v16.4s, v24.4s + fdiv v17.4s, v17.4s, v25.4s + fdiv v18.4s, v18.4s, v26.4s + fdiv v19.4s, v19.4s, v27.4s + + fadd v16.4s, v16.4s, v7.4s + fadd v17.4s, v17.4s, v7.4s + fadd v18.4s, v18.4s, v7.4s + fadd v19.4s, v19.4s, v7.4s + + st1 { v16.4s, v17.4s, v18.4s, v19.4s }, [x0], #64 + + subs x1, x1, #16 + cmp x1, #16 + bge .loop4 + + cmp x1, #0 + beq .return + +.loop: + ld1 { v16.4s }, [x0] + + fmax v16.4s, v16.4s, v5.4s + fmin v16.4s, v16.4s, v6.4s // v16 <- x + fmul v20.4s, v16.4s, v16.4s // v20 <- x2 + + dup v24.4s, v0.s[3] + fmla v24.4s, v20.4s, v0.s[2] + dup v28.4s, v1.s[0] + fmla v28.4s, v20.4s, v24.4s + dup v24.4s, v1.s[1] + fmla v24.4s, v20.4s, v28.4s + dup v28.4s, v1.s[2] + fmla v28.4s, v20.4s, v24.4s + dup v24.4s, v1.s[3] + fmla v24.4s, v20.4s, v28.4s + dup v28.4s, v2.s[0] + fmla v28.4s, v20.4s, v24.4s + fmul v16.4s, v16.4s, v28.4s // v16 <- numerator + + dup v24.4s, v2.s[2] + fmla v24.4s, v20.4s, v2.s[1] + dup v28.4s, v2.s[3] + fmla v28.4s, v20.4s, v24.4s + dup v24.4s, v3.s[0] + fmla v24.4s, v20.4s, v28.4s // v24 <- denum + + fdiv v16.4s, v16.4s, v24.4s + fadd v16.4s, v16.4s, v7.4s + + st1 { v16.4s }, [x0], #16 + + subs x1, x1, #4 + bne .loop + +.return: + ret + +.coeffs_num: + .float -18.6 // low + .float 18.6 // high + .float -4.433153405e-18 // alpha_13 + .float 1.169974371e-14 + + .float -1.875289645e-11 + .float 4.257889523e-8 + .float 0.00004811817576 + .float 0.008163842030 + + .float 0.2499999971 + .float 3.922935744e-6 // beta_6 + .float 0.001524872358 + .float 0.1159886749 + + .float 1.0 + .float 0.5 // + .float 0.0 // padding + .float 0.0 + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_tanh_f32_4n.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_tanh_f32_4n.S.j2 new file mode 100644 index 0000000..dc88569 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/arm64simd_tanh_f32_4n.S.j2 @@ -0,0 +1,198 @@ +// vim: ft=arm + +// no preservation either for v0-v7 and v16-v31 + +.text +.align 4 + +.cpu generic+fp+simd +.global {{G}}arm64simd_tanh_f32_4n_{{suffix}} +{{G}}arm64simd_tanh_f32_4n_{{suffix}}: + + cmp x1, #0 + beq .return + + adr x2, .coeffs_num + ld1 { v0.4s, v1.4s, v2.4s, v3.4s }, [x2] + dup v5.4s, v0.s[0] // v5 <- low, broadcasted + dup v6.4s, v0.s[1] // v6 <- high, broadcasted + + cmp x1, #16 + blt .loop + +.loop4: + ld1 { v16.4s, v17.4s, v18.4s, v19.4s }, [x0] + + fmax v16.4s, v16.4s, v5.4s + fmax v17.4s, v17.4s, v5.4s + fmax v18.4s, v18.4s, v5.4s + fmax v19.4s, v19.4s, v5.4s + + fmin v16.4s, v16.4s, v6.4s + fmin v17.4s, v17.4s, v6.4s + fmin v18.4s, v18.4s, v6.4s + fmin v19.4s, v19.4s, v6.4s // v16 <- x + + fmul v20.4s, v16.4s, v16.4s + fmul v21.4s, v17.4s, v17.4s + fmul v22.4s, v18.4s, v18.4s + fmul v23.4s, v19.4s, v19.4s // v20 <- x2 + + dup v24.4s, v0.s[3] + fmla v24.4s, v20.4s, v0.s[2] + dup v25.4s, v0.s[3] + fmla v25.4s, v21.4s, v0.s[2] + dup v26.4s, v0.s[3] + fmla v26.4s, v22.4s, v0.s[2] + dup v27.4s, v0.s[3] + fmla v27.4s, v23.4s, v0.s[2] + + dup v28.4s, v1.s[0] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v1.s[0] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v1.s[0] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v1.s[0] + fmla v31.4s, v23.4s, v27.4s + + dup v24.4s, v1.s[1] + fmla v24.4s, v20.4s, v28.4s + dup v25.4s, v1.s[1] + fmla v25.4s, v21.4s, v29.4s + dup v26.4s, v1.s[1] + fmla v26.4s, v22.4s, v30.4s + dup v27.4s, v1.s[1] + fmla v27.4s, v23.4s, v31.4s + + dup v28.4s, v1.s[2] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v1.s[2] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v1.s[2] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v1.s[2] + fmla v31.4s, v23.4s, v27.4s + + dup v24.4s, v1.s[3] + fmla v24.4s, v20.4s, v28.4s + dup v25.4s, v1.s[3] + fmla v25.4s, v21.4s, v29.4s + dup v26.4s, v1.s[3] + fmla v26.4s, v22.4s, v30.4s + dup v27.4s, v1.s[3] + fmla v27.4s, v23.4s, v31.4s + + dup v28.4s, v2.s[0] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v2.s[0] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v2.s[0] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v2.s[0] + fmla v31.4s, v23.4s, v27.4s + + fmul v16.4s, v16.4s, v28.4s + fmul v17.4s, v17.4s, v29.4s + fmul v18.4s, v18.4s, v30.4s + fmul v19.4s, v19.4s, v31.4s // v16 <- numerator + + dup v24.4s, v2.s[2] + fmla v24.4s, v20.4s, v2.s[1] + dup v25.4s, v2.s[2] + fmla v25.4s, v21.4s, v2.s[1] + dup v26.4s, v2.s[2] + fmla v26.4s, v22.4s, v2.s[1] + dup v27.4s, v2.s[2] + fmla v27.4s, v23.4s, v2.s[1] + + dup v28.4s, v2.s[3] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v2.s[3] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v2.s[3] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v2.s[3] + fmla v31.4s, v23.4s, v27.4s + + dup v24.4s, v3.s[0] + fmla v24.4s, v20.4s, v28.4s + dup v25.4s, v3.s[0] + fmla v25.4s, v21.4s, v29.4s + dup v26.4s, v3.s[0] + fmla v26.4s, v22.4s, v30.4s + dup v27.4s, v3.s[0] + fmla v27.4s, v23.4s, v31.4s // v24 denum + + fdiv v16.4s, v16.4s, v24.4s + fdiv v17.4s, v17.4s, v25.4s + fdiv v18.4s, v18.4s, v26.4s + fdiv v19.4s, v19.4s, v27.4s + + st1 { v16.4s, v17.4s, v18.4s, v19.4s }, [x0], #64 + + subs x1, x1, #16 + cmp x1, #16 + bge .loop4 + + cmp x1, #0 + beq .return + +.loop: + ld1 { v16.4s }, [x0] + + fmax v16.4s, v16.4s, v5.4s + fmin v16.4s, v16.4s, v6.4s // v16 <- x + fmul v20.4s, v16.4s, v16.4s // v20 <- x2 + + dup v24.4s, v0.s[3] + fmla v24.4s, v20.4s, v0.s[2] + dup v28.4s, v1.s[0] + fmla v28.4s, v20.4s, v24.4s + dup v24.4s, v1.s[1] + fmla v24.4s, v20.4s, v28.4s + dup v28.4s, v1.s[2] + fmla v28.4s, v20.4s, v24.4s + dup v24.4s, v1.s[3] + fmla v24.4s, v20.4s, v28.4s + dup v28.4s, v2.s[0] + fmla v28.4s, v20.4s, v24.4s + fmul v16.4s, v16.4s, v28.4s // v16 <- numerator + + dup v24.4s, v2.s[2] + fmla v24.4s, v20.4s, v2.s[1] + dup v28.4s, v2.s[3] + fmla v28.4s, v20.4s, v24.4s + dup v24.4s, v3.s[0] + fmla v24.4s, v20.4s, v28.4s // v24 <- denum + + fdiv v16.4s, v16.4s, v24.4s + + st1 { v16.4s }, [x0], #16 + + subs x1, x1, #4 + bne .loop + +.return: + ret + +.coeffs_num: + .float -8.9 // low + .float 8.9 // high + .float -8.488492677e-14 // alpha_13 + .float 5.277853000e-11 + + .float -2.022500419e-8 + .float 0.00001115424833 + .float 0.003103950131 + .float 0.1308400453 + + .float 0.9999999934 + .float 0.0002546136580 // beta_6 + .float 0.02449515379 + .float 0.4641733162 + + .float 1.0 + .float 0 // padding + .float 0 // padding + .float 0 // padding diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/dispatcher.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/dispatcher.j2 new file mode 100644 index 0000000..5663fd2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/dispatcher.j2 @@ -0,0 +1,37 @@ +// vim: ft=arm + +.non_linear: + sub x0, x0, 40 + +.non_linear_loop: + add x0, x0, 40 + ldr x2, [x0] + + mov x4, #{{ jump_table | length }} + + cmp x2, #{{ jump_table | length }} + csel x2, x2, x4, lt + cmp x2, #0 + csel x2, x4, x2, lt + + adr x3, .jmp_table + add x3, x3, x2, LSL#2 + br x3 + +.jmp_table: +{% for j in jump_table %} + b .{{j}} +{% endfor %} + b .unsupported + + add x0, x2, #4000 + b .return + +.unsupported: + mov x0, #1 + b .return + +.done: + mov x0, 0 + b .return + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/dummy_dotprod.S b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/dummy_dotprod.S new file mode 100644 index 0000000..4304549 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/arm64simd/dummy_dotprod.S @@ -0,0 +1,13 @@ +// Build-time capability probe for the assembler, used by build.rs +// (assembler_supports_dotprod). Older binutils — notably the Debian stretch +// aarch64 cross-toolchain in CI — predate FEAT_DotProd and cannot assemble +// `sdot` even with `.cpu generic+fp+simd+dotprod`. If this file fails to +// assemble, build.rs skips the SDOT kernel and the `tract_arm64_dotprod` cfg, +// and the runtime falls back to the SMLAL 8x8 i32 kernel. Not linked into +// anything. +.cpu generic+fp+simd+dotprod +.text +.globl tract_dotprod_probe +tract_dotprod_probe: + sdot v0.4s, v1.16b, v2.4b[0] + ret diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/sme/dispatcher.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/sme/dispatcher.j2 new file mode 100644 index 0000000..5663fd2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/sme/dispatcher.j2 @@ -0,0 +1,37 @@ +// vim: ft=arm + +.non_linear: + sub x0, x0, 40 + +.non_linear_loop: + add x0, x0, 40 + ldr x2, [x0] + + mov x4, #{{ jump_table | length }} + + cmp x2, #{{ jump_table | length }} + csel x2, x2, x4, lt + cmp x2, #0 + csel x2, x4, x2, lt + + adr x3, .jmp_table + add x3, x3, x2, LSL#2 + br x3 + +.jmp_table: +{% for j in jump_table %} + b .{{j}} +{% endfor %} + b .unsupported + + add x0, x2, #4000 + b .return + +.unsupported: + mov x0, #1 + b .return + +.done: + mov x0, 0 + b .return + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/sme/dummy_sme.S b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/sme/dummy_sme.S new file mode 100644 index 0000000..c35f9c0 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/sme/dummy_sme.S @@ -0,0 +1,14 @@ +// Build-time capability probe for the assembler, used by build.rs +// (assembler_supports_sme). Older binutils — notably the Debian stretch +// aarch64 cross-toolchain in CI — predate SME and cannot assemble these +// mnemonics even with `.arch armv9-a+sme2`. If this file fails to assemble, +// build.rs skips the SME kernels and the `tract_sme` cfg, and the runtime +// falls back to the portable path. Not linked into anything. +.arch armv9-a+sme2 +.text +.globl tract_sme_probe +tract_sme_probe: + smstart + zero {za} + smstop + ret diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/sme/sme_mmm_f32_32x32.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/sme/sme_mmm_f32_32x32.S.j2 new file mode 100644 index 0000000..5396f76 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/sme/sme_mmm_f32_32x32.S.j2 @@ -0,0 +1,474 @@ +// vim: ft=arm +// +// SME f32 32x32 matmul kernel. +// +// ZA tile layout (4 .S tiles, 16x16 each, indexed left/right x top/bottom): +// +// ZA0.S : C[0..16, 0..16] (top-left) +// ZA1.S : C[0..16, 16..32] (top-right) +// ZA2.S : C[16..32, 0..16] (bottom-left) +// ZA3.S : C[16..32, 16..32] (bottom-right) +// +// Inner K-step: load 32 f32 of A (split z0+z2) and 32 of B (split z1+z3), +// issue 4 FMOPAs (one per tile). All 4 tiles are independent → SME unit +// reaches 1 fmopa/cycle = ~2 TFLOPS on M4. +// +// Calling convention (extern "C", AAPCS64): +// x0 = const *FusedKerSpec, advanced 40 B per dispatcher iteration. +// x1 = stack-resident 4 KiB scratch buffer for tile spills (Phase 1B+). +// +// Streaming mode: PSTATE.SM=1 from prologue smstart to epilogue smstop. +// V0..V31 (low 128 bits = Z0..Z31 low) are destroyed by the smstart/smstop +// pair; v8..v15 are saved/restored to stack across the streaming region per +// AAPCS callee-save rules. + +.arch armv9-a+sme2 +.text +.align 4 + +.global {{G}}sme_mmm_f32_32x32_{{suffix}} +{{G}}sme_mmm_f32_32x32_{{suffix}}: + + // Save callee-saved q8..q15 (AAPCS preserves low 64 bits of v8..v15; + // we save the full 128-bit Q to keep the stack layout simple). + stp q8, q9, [sp, #-128]! + stp q10, q11, [sp, #32] + stp q12, q13, [sp, #64] + stp q14, q15, [sp, #96] + + // Allocate 4 KiB tile-spill scratch (kept live across the whole call). + sub sp, sp, #4096 + mov x1, sp + + smstart + ptrue p0.b + +{% include "dispatcher.j2" %} + +// -------- supported fuse ops --------------------------------------------- + +.add_mat_mul: + ldr x2, [x0, #24] // b + ldp x3, x4, [x0, #8] // k, a + + cmp x3, #0 + b.eq .non_linear_loop + +.Lmatmul_loop: + ld1w {z0.s}, p0/z, [x4] + ld1w {z2.s}, p0/z, [x4, #1, mul vl] + ld1w {z1.s}, p0/z, [x2] + ld1w {z3.s}, p0/z, [x2, #1, mul vl] + add x4, x4, #128 + add x2, x2, #128 + + fmopa za0.s, p0/m, p0/m, z0.s, z1.s // C[0..16, 0..16] + fmopa za1.s, p0/m, p0/m, z0.s, z3.s // C[0..16, 16..32] + fmopa za2.s, p0/m, p0/m, z2.s, z1.s // C[16..32, 0..16] + fmopa za3.s, p0/m, p0/m, z2.s, z3.s // C[16..32, 16..32] + + subs x3, x3, #1 + b.ne .Lmatmul_loop + b .non_linear_loop + +.clear: + zero {za} + b .non_linear_loop + +.store: + // FusedKerSpec::Store(OutputStoreKer { ptr, row_byte_stride, + // col_byte_stride, item_size }) + // [x0, #8] = ptr [x0, #16] = row_byte_stride + // [x0, #24] = col_byte_stride [x0, #32] = item_size + ldp x5, x6, [x0, #8] // x5 = ptr, x6 = row_byte_stride + ldp x7, x8, [x0, #24] // x7 = col_byte_stride, x8 = item_size + + // Fast path: contiguous f32 columns (col_stride == 4) → direct ZA→user. + // st1w-from-ZA does not accept "[Xn, #imm, MUL VL]" offsets, so we keep + // two parallel base pointers for the left and right halves of each row. + cmp x7, #4 + b.ne .Lstore_generic + cmp x8, #4 + b.ne .Lstore_generic + + add x4, x5, #64 // right-half base + mov w12, #0 +.Lstore_top: + st1w {za0h.s[w12, 0]}, p0, [x5] + st1w {za1h.s[w12, 0]}, p0, [x4] + add x5, x5, x6 + add x4, x4, x6 + add w12, w12, #1 + cmp w12, #16 + b.lt .Lstore_top + mov w12, #0 +.Lstore_bot: + st1w {za2h.s[w12, 0]}, p0, [x5] + st1w {za3h.s[w12, 0]}, p0, [x4] + add x5, x5, x6 + add x4, x4, x6 + add w12, w12, #1 + cmp w12, #16 + b.lt .Lstore_bot + b .non_linear_loop + +.Lstore_generic: + // Slow path: spill ZA → x1 (scratch buffer, 32x32 row-major, 128 B/row) + // using two parallel pointers, then per-element strided scatter. + mov x4, x1 // left-half pointer + add x9, x1, #64 // right-half pointer + mov w12, #0 +.Lstore_spill_top: + st1w {za0h.s[w12, 0]}, p0, [x4] + st1w {za1h.s[w12, 0]}, p0, [x9] + add x4, x4, #128 + add x9, x9, #128 + add w12, w12, #1 + cmp w12, #16 + b.lt .Lstore_spill_top + mov w12, #0 +.Lstore_spill_bot: + st1w {za2h.s[w12, 0]}, p0, [x4] + st1w {za3h.s[w12, 0]}, p0, [x9] + add x4, x4, #128 + add x9, x9, #128 + add w12, w12, #1 + cmp w12, #16 + b.lt .Lstore_spill_bot + + // Strided f32 scatter: 32 rows × 32 cols. + mov x3, #0 +.Lstore_row: + mov x4, x5 + mov x10, #0 + lsl x9, x3, #7 // row*128 byte offset in scratch + add x11, x1, x9 +.Lstore_col: + ldr w9, [x11], #4 + str w9, [x4] + add x4, x4, x7 + add x10, x10, #1 + cmp x10, #32 + b.lt .Lstore_col + add x5, x5, x6 + add x3, x3, #1 + cmp x3, #32 + b.lt .Lstore_row + b .non_linear_loop + +// -------- scalar ops ------------------------------------------------------ +// +// FusedKerSpec::Scalar{Add,Mul,Sub,SubF,Min,Max}(TI) — broadcast scalar from +// [x0, #8] to all lanes, apply elementwise across the 4-tile 32x32 grid. +// +// Sub vs SubF semantics (matching apple_amx + tests/fuse.rs): +// ScalarSub → result = scalar - z (mnemonic fsubr) +// ScalarSubF → result = z - scalar (mnemonic fsub) +// +// Slice-op loop: for each slice index w12, extract ZA tile slice → Z reg, +// op with broadcast-scalar in z4, insert Z back. Two halves × 4 tiles total. + +{% macro scalar_op(label, op) %} +{{label}}: + ldr w2, [x0, #8] + dup z4.s, w2 + mov w12, #0 +.L{{label|replace('.', '')}}_top: + mov z6.s, p0/m, za0h.s[w12, 0] + mov z7.s, p0/m, za1h.s[w12, 0] + {{op}} z6.s, p0/m, z6.s, z4.s + {{op}} z7.s, p0/m, z7.s, z4.s + mov za0h.s[w12, 0], p0/m, z6.s + mov za1h.s[w12, 0], p0/m, z7.s + add w12, w12, #1 + cmp w12, #16 + b.lt .L{{label|replace('.', '')}}_top + mov w12, #0 +.L{{label|replace('.', '')}}_bot: + mov z6.s, p0/m, za2h.s[w12, 0] + mov z7.s, p0/m, za3h.s[w12, 0] + {{op}} z6.s, p0/m, z6.s, z4.s + {{op}} z7.s, p0/m, z7.s, z4.s + mov za2h.s[w12, 0], p0/m, z6.s + mov za3h.s[w12, 0], p0/m, z7.s + add w12, w12, #1 + cmp w12, #16 + b.lt .L{{label|replace('.', '')}}_bot + b .non_linear_loop +{% endmacro %} + +{{ scalar_op('.scalar_add', 'fadd') }} +{{ scalar_op('.scalar_mul', 'fmul') }} +{{ scalar_op('.scalar_sub', 'fsubr') }} +{{ scalar_op('.scalar_sub_flipped', 'fsub') }} +{{ scalar_op('.scalar_min', 'fmin') }} +{{ scalar_op('.scalar_max', 'fmax') }} + +// -------- per-col ops ----------------------------------------------------- +// +// 32-element column vector → z4 (cols 0-15) + z5 (cols 16-31). +// Same z4/z5 is applied to every row across the 4-tile grid. + +{% macro per_col_op(label, op) %} +{{label}}: + ldr x2, [x0, #8] + ld1w {z4.s}, p0/z, [x2] + ld1w {z5.s}, p0/z, [x2, #1, mul vl] + mov w12, #0 +.L{{label|replace('.', '')}}_top: + mov z6.s, p0/m, za0h.s[w12, 0] + mov z7.s, p0/m, za1h.s[w12, 0] + {{op}} z6.s, p0/m, z6.s, z4.s + {{op}} z7.s, p0/m, z7.s, z5.s + mov za0h.s[w12, 0], p0/m, z6.s + mov za1h.s[w12, 0], p0/m, z7.s + add w12, w12, #1 + cmp w12, #16 + b.lt .L{{label|replace('.', '')}}_top + mov w12, #0 +.L{{label|replace('.', '')}}_bot: + mov z6.s, p0/m, za2h.s[w12, 0] + mov z7.s, p0/m, za3h.s[w12, 0] + {{op}} z6.s, p0/m, z6.s, z4.s + {{op}} z7.s, p0/m, z7.s, z5.s + mov za2h.s[w12, 0], p0/m, z6.s + mov za3h.s[w12, 0], p0/m, z7.s + add w12, w12, #1 + cmp w12, #16 + b.lt .L{{label|replace('.', '')}}_bot + b .non_linear_loop +{% endmacro %} + +{{ per_col_op('.per_col_add', 'fadd') }} +{{ per_col_op('.per_col_mul', 'fmul') }} +{{ per_col_op('.per_col_sub', 'fsubr') }} +{{ per_col_op('.per_col_sub_flipped', 'fsub') }} +{{ per_col_op('.per_col_min', 'fmin') }} +{{ per_col_op('.per_col_max', 'fmax') }} + +// -------- per-row ops ----------------------------------------------------- +// +// 32-element row vector at x2 (top 16 rows) and x2+64 (bottom 16 rows). +// Load one f32 per iteration and broadcast (no SVE indexed-broadcast for +// arbitrary i across 16 lanes, so we just walk the bias pointer). + +{% macro per_row_op(label, op) %} +{{label}}: + ldr x2, [x0, #8] + add x3, x2, #64 + mov w12, #0 +.L{{label|replace('.', '')}}_top: + ldr w4, [x2], #4 + dup z4.s, w4 + mov z6.s, p0/m, za0h.s[w12, 0] + mov z7.s, p0/m, za1h.s[w12, 0] + {{op}} z6.s, p0/m, z6.s, z4.s + {{op}} z7.s, p0/m, z7.s, z4.s + mov za0h.s[w12, 0], p0/m, z6.s + mov za1h.s[w12, 0], p0/m, z7.s + add w12, w12, #1 + cmp w12, #16 + b.lt .L{{label|replace('.', '')}}_top + mov w12, #0 +.L{{label|replace('.', '')}}_bot: + ldr w4, [x3], #4 + dup z4.s, w4 + mov z6.s, p0/m, za2h.s[w12, 0] + mov z7.s, p0/m, za3h.s[w12, 0] + {{op}} z6.s, p0/m, z6.s, z4.s + {{op}} z7.s, p0/m, z7.s, z4.s + mov za2h.s[w12, 0], p0/m, z6.s + mov za3h.s[w12, 0], p0/m, z7.s + add w12, w12, #1 + cmp w12, #16 + b.lt .L{{label|replace('.', '')}}_bot + b .non_linear_loop +{% endmacro %} + +{{ per_row_op('.per_row_add', 'fadd') }} +{{ per_row_op('.per_row_mul', 'fmul') }} +{{ per_row_op('.per_row_sub', 'fsubr') }} +{{ per_row_op('.per_row_sub_flipped', 'fsub') }} +{{ per_row_op('.per_row_min', 'fmin') }} +{{ per_row_op('.per_row_max', 'fmax') }} + +// -------- AddRowColProducts: ZA += rows ⊗ cols (rank-1 update) ------------ +// +// Same shape as a K=1 matmul step: load 32 f32 of rows + 32 of cols, four +// FMOPAs into the 2x2 ZA grid. + +.add_row_col_products: + ldp x2, x3, [x0, #8] // rows ptr, cols ptr + ld1w {z0.s}, p0/z, [x2] + ld1w {z2.s}, p0/z, [x2, #1, mul vl] + ld1w {z1.s}, p0/z, [x3] + ld1w {z3.s}, p0/z, [x3, #1, mul vl] + fmopa za0.s, p0/m, p0/m, z0.s, z1.s + fmopa za1.s, p0/m, p0/m, z0.s, z3.s + fmopa za2.s, p0/m, p0/m, z2.s, z1.s + fmopa za3.s, p0/m, p0/m, z2.s, z3.s + b .non_linear_loop + +// -------- AddUnicast: ZA += C[i][j] from strided buffer ------------------- +// +// FusedKerSpec::AddUnicast(OutputStoreKer { ptr, row_byte_stride, +// col_byte_stride, item_size }) +// Fast path: contiguous f32 cols (col_stride == 4) — load each row via +// ld1w then in-place fadd to ZA slice. + +.add_unicast: + ldp x5, x6, [x0, #8] // ptr, row_byte_stride + ldp x7, x8, [x0, #24] // col_byte_stride, item_size + + cmp x7, #4 + b.ne .Laddu_generic + cmp x8, #4 + b.ne .Laddu_generic + + add x4, x5, #64 + mov w12, #0 +.Laddu_top: + ld1w {z8.s}, p0/z, [x5] + ld1w {z9.s}, p0/z, [x4] + mov z6.s, p0/m, za0h.s[w12, 0] + mov z7.s, p0/m, za1h.s[w12, 0] + fadd z6.s, p0/m, z6.s, z8.s + fadd z7.s, p0/m, z7.s, z9.s + mov za0h.s[w12, 0], p0/m, z6.s + mov za1h.s[w12, 0], p0/m, z7.s + add x5, x5, x6 + add x4, x4, x6 + add w12, w12, #1 + cmp w12, #16 + b.lt .Laddu_top + mov w12, #0 +.Laddu_bot: + ld1w {z8.s}, p0/z, [x5] + ld1w {z9.s}, p0/z, [x4] + mov z6.s, p0/m, za2h.s[w12, 0] + mov z7.s, p0/m, za3h.s[w12, 0] + fadd z6.s, p0/m, z6.s, z8.s + fadd z7.s, p0/m, z7.s, z9.s + mov za2h.s[w12, 0], p0/m, z6.s + mov za3h.s[w12, 0], p0/m, z7.s + add x5, x5, x6 + add x4, x4, x6 + add w12, w12, #1 + cmp w12, #16 + b.lt .Laddu_bot + b .non_linear_loop + +.Laddu_generic: + // Generic strided gather: walk 32 rows × 32 cols, accumulate one + // element at a time into the spill scratch, then re-load each slice + // into ZA and fadd. + // + // Phase 1B keeps this slow but correct — it triggers only for non- + // contiguous AddUnicast which auto-tests don't exercise. + mov x3, #0 // row idx + mov x9, x1 // scratch ptr +.Laddu_gen_row: + mov x10, #0 + mov x11, x5 +.Laddu_gen_col: + ldr w4, [x11] + str w4, [x9], #4 + add x11, x11, x7 + add x10, x10, #1 + cmp x10, #32 + b.lt .Laddu_gen_col + add x5, x5, x6 + add x3, x3, #1 + cmp x3, #32 + b.lt .Laddu_gen_row + + // Now scratch holds 32x32 f32 row-major. Same loop as fast path but + // reading from scratch (contiguous). + mov x9, x1 + add x4, x9, #64 + mov w12, #0 +.Laddu_gen_apply_top: + ld1w {z8.s}, p0/z, [x9] + ld1w {z10.s}, p0/z, [x4] + mov z6.s, p0/m, za0h.s[w12, 0] + mov z7.s, p0/m, za1h.s[w12, 0] + fadd z6.s, p0/m, z6.s, z8.s + fadd z7.s, p0/m, z7.s, z10.s + mov za0h.s[w12, 0], p0/m, z6.s + mov za1h.s[w12, 0], p0/m, z7.s + add x9, x9, #128 + add x4, x4, #128 + add w12, w12, #1 + cmp w12, #16 + b.lt .Laddu_gen_apply_top + mov w12, #0 +.Laddu_gen_apply_bot: + ld1w {z8.s}, p0/z, [x9] + ld1w {z10.s}, p0/z, [x4] + mov z6.s, p0/m, za2h.s[w12, 0] + mov z7.s, p0/m, za3h.s[w12, 0] + fadd z6.s, p0/m, z6.s, z8.s + fadd z7.s, p0/m, z7.s, z10.s + mov za2h.s[w12, 0], p0/m, z6.s + mov za3h.s[w12, 0], p0/m, z7.s + add x9, x9, #128 + add x4, x4, #128 + add w12, w12, #1 + cmp w12, #16 + b.lt .Laddu_gen_apply_bot + b .non_linear_loop + +// -------- LoadTile: ZA := row-major tile from memory ---------------------- +// +// FusedKerSpec::LoadTile(col_major_ptr, row_major_ptr): +// [x0, #8] = col-major ptr (unused; AMX prefers this for its layout) +// [x0, #16] = row-major ptr (32x32 f32, 128 B per row) +// +// We use the row-major pointer because the ZA H-tile store path is itself +// row-major and matches naturally. + +.load_tile: + ldr x2, [x0, #16] + add x4, x2, #64 + mov w12, #0 +.Lloadtile_top: + ld1w {z6.s}, p0/z, [x2] + ld1w {z7.s}, p0/z, [x4] + mov za0h.s[w12, 0], p0/m, z6.s + mov za1h.s[w12, 0], p0/m, z7.s + add x2, x2, #128 + add x4, x4, #128 + add w12, w12, #1 + cmp w12, #16 + b.lt .Lloadtile_top + mov w12, #0 +.Lloadtile_bot: + ld1w {z6.s}, p0/z, [x2] + ld1w {z7.s}, p0/z, [x4] + mov za2h.s[w12, 0], p0/m, z6.s + mov za3h.s[w12, 0], p0/m, z7.s + add x2, x2, #128 + add x4, x4, #128 + add w12, w12, #1 + cmp w12, #16 + b.lt .Lloadtile_bot + b .non_linear_loop + +// -------- still not implemented (low priority for Phase 1) ---------------- + +.leaky_relu: +.q_scale: +.q_shl: +.q_shr: + b .unsupported + +// -------- epilogue -------------------------------------------------------- + +.return: + smstop + add sp, sp, #4096 + ldp q14, q15, [sp, #96] + ldp q12, q13, [sp, #64] + ldp q10, q11, [sp, #32] + ldp q8, q9, [sp], #128 + ret diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/sme/sme_mmv_f32_64x1.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/sme/sme_mmv_f32_64x1.S.j2 new file mode 100644 index 0000000..2b1fddd --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/sme/sme_mmv_f32_64x1.S.j2 @@ -0,0 +1,268 @@ +// vim: ft=arm +// +// SME2 f32 64x1 GEMV kernel. +// +// Accumulator layout: ZA tile slot rows 0..3 (one vgx4 group starting at +// w8=0). The 64-element output column maps to these 4 slots × 16 f32 each. +// +// Inner K-step: load 64 f32 of A's column into {z0.s-z3.s} via one SME2 +// multi-vec LD1W, broadcast B[k] into z4 with LD1RW, issue ONE multi-vec +// vgx4 FMLA-into-ZA. Measured peak on M4: ~125 GFLOPS (ZA-write port). +// Plain SME single-vec predicated FMLA into Z regs caps at ~31 GFLOPS so +// is not used. Detection therefore gates on FEAT_SME2, not just FEAT_SME. +// +// Calling convention (extern "C", AAPCS64): +// x0 = const *FusedKerSpec, advanced 40 B per dispatcher iteration. +// x1 = stack-resident 256 B scratch buffer for the strided-store path. +// w8 = 0 throughout (vgx-group base index, set in prologue). +// +// Streaming-mode rules: PSTATE.SM=1 from prologue smstart to epilogue +// smstop. v8..v15 saved/restored across the streaming region. + +.arch armv9-a+sme2 +.text +.align 4 + +.global {{G}}sme_mmv_f32_64x1_{{suffix}} +{{G}}sme_mmv_f32_64x1_{{suffix}}: + + stp q8, q9, [sp, #-128]! + stp q10, q11, [sp, #32] + stp q12, q13, [sp, #64] + stp q14, q15, [sp, #96] + + // 256 B = 64 f32 spill buffer for the strided-store / AddUnicast paths. + sub sp, sp, #256 + mov x1, sp + + smstart + ptrue p0.b + ptrue pn8.b + mov w8, #0 + +{% include "dispatcher.j2" %} + +// -------- supported fuse ops ----------------------------------------------- + +.add_mat_mul: + ldr x2, [x0, #24] // b ptr + ldp x3, x4, [x0, #8] // k, a ptr + cmp x3, #0 + b.eq .non_linear_loop + +.Lmmv_loop: + ld1w {z0.s-z3.s}, pn8/z, [x4] + add x4, x4, #256 + ld1rw {z4.s}, p0/z, [x2] + add x2, x2, #4 + fmla za.s[w8, 0, vgx4], {z0.s-z3.s}, z4.s[0] + subs x3, x3, #1 + b.ne .Lmmv_loop + b .non_linear_loop + +.clear: + zero {za} + b .non_linear_loop + +.store: + // FusedKerSpec::Store(OutputStoreKer { ptr, row_byte_stride, + // col_byte_stride, item_size }) + // [x0, #8] = ptr [x0, #16] = row_byte_stride + // [x0, #24] = col_byte_stride [x0, #32] = item_size + // x8 must NOT be touched (it's the vgx-base index, set to 0 in prologue). + ldp x5, x6, [x0, #8] // ptr, row_byte_stride + ldp x7, x9, [x0, #24] // col_byte_stride, item_size + + // At NR=1 the output column is one element per row; the fast path + // triggers when (row_byte_stride==4 AND item_size==4) i.e. the 64 + // outputs are contiguous in memory. + cmp x6, #4 + b.ne .Lstore_generic + cmp x9, #4 + b.ne .Lstore_generic + + mov {z0.s-z3.s}, za.s[w8, 0, vgx4] + st1w {z0.s}, p0, [x5] + st1w {z1.s}, p0, [x5, #1, mul vl] + st1w {z2.s}, p0, [x5, #2, mul vl] + st1w {z3.s}, p0, [x5, #3, mul vl] + b .non_linear_loop + +.Lstore_generic: + // Spill ZA → 256 B scratch buffer x1, then per-element strided write. + mov {z0.s-z3.s}, za.s[w8, 0, vgx4] + st1w {z0.s}, p0, [x1] + st1w {z1.s}, p0, [x1, #1, mul vl] + st1w {z2.s}, p0, [x1, #2, mul vl] + st1w {z3.s}, p0, [x1, #3, mul vl] + + mov x3, #0 + mov x9, x1 +.Lstore_scatter: + ldr w10, [x9], #4 + str w10, [x5] + add x5, x5, x6 + add x3, x3, #1 + cmp x3, #64 + b.lt .Lstore_scatter + b .non_linear_loop + +// -------- LoadTile: ZA := tile from row-major source ----------------------- +// +// FusedKerSpec::LoadTile(col_major_ptr, row_major_ptr) — same as Phase 1's +// 32x32 LoadTile. NR=1 collapses both pointers to the same 64-element vec; +// we use the row-major form at [x0, #16]. + +.load_tile: + ldr x2, [x0, #16] + ld1w {z0.s-z3.s}, pn8/z, [x2] + mov za.s[w8, 0, vgx4], {z0.s-z3.s} + b .non_linear_loop + +// -------- AddRowColProducts: ZA += rows ⊗ cols (rank-1 K=1) --------------- +// +// NR=1: cols is a single f32, rows is a 64-element vector. Effectively one +// K-step of add_mat_mul with K=1. + +.add_row_col_products: + ldp x2, x3, [x0, #8] // rows ptr, cols ptr + ld1w {z0.s-z3.s}, pn8/z, [x2] + ld1rw {z4.s}, p0/z, [x3] + fmla za.s[w8, 0, vgx4], {z0.s-z3.s}, z4.s[0] + b .non_linear_loop + +// -------- AddUnicast: ZA += C from strided buffer -------------------------- +// +// NR=1 implies a 64-element column vec layout. Fast path = contiguous f32 +// rows (row_stride == 4); generic path gathers strided. + +.add_unicast: + ldp x5, x6, [x0, #8] // ptr, row_byte_stride + ldp x7, x9, [x0, #24] // col_byte_stride, item_size + + cmp x6, #4 + b.ne .Laddu_generic + cmp x9, #4 + b.ne .Laddu_generic + + // Fast path: contiguous load via 4-vec LD1W. + ld1w {z16.s-z19.s}, pn8/z, [x5] + mov {z0.s-z3.s}, za.s[w8, 0, vgx4] + fadd z0.s, p0/m, z0.s, z16.s + fadd z1.s, p0/m, z1.s, z17.s + fadd z2.s, p0/m, z2.s, z18.s + fadd z3.s, p0/m, z3.s, z19.s + mov za.s[w8, 0, vgx4], {z0.s-z3.s} + b .non_linear_loop + +.Laddu_generic: + // Per-element strided gather into scratch, then contiguous accumulate. + mov x3, #0 + mov x9, x1 +.Laddu_gather: + ldr w10, [x5] + str w10, [x9], #4 + add x5, x5, x6 + add x3, x3, #1 + cmp x3, #64 + b.lt .Laddu_gather + ld1w {z16.s-z19.s}, pn8/z, [x1] + mov {z0.s-z3.s}, za.s[w8, 0, vgx4] + fadd z0.s, p0/m, z0.s, z16.s + fadd z1.s, p0/m, z1.s, z17.s + fadd z2.s, p0/m, z2.s, z18.s + fadd z3.s, p0/m, z3.s, z19.s + mov za.s[w8, 0, vgx4], {z0.s-z3.s} + b .non_linear_loop + +// -------- scalar / per_col ops (degenerate at NR=1; per_col == scalar) ----- +// +// Per Phase 1's mapping: +// ScalarSub → result = scalar - z (fsubr) +// ScalarSubF → result = z - scalar (fsub) +// Same convention applies to PerCol*. + +{% macro scalar_op(label, op) %} +{{label}}: + ldr w2, [x0, #8] + dup z4.s, w2 + mov {z0.s-z3.s}, za.s[w8, 0, vgx4] + {{op}} z0.s, p0/m, z0.s, z4.s + {{op}} z1.s, p0/m, z1.s, z4.s + {{op}} z2.s, p0/m, z2.s, z4.s + {{op}} z3.s, p0/m, z3.s, z4.s + mov za.s[w8, 0, vgx4], {z0.s-z3.s} + b .non_linear_loop +{% endmacro %} + +{{ scalar_op('.scalar_add', 'fadd') }} +{{ scalar_op('.scalar_mul', 'fmul') }} +{{ scalar_op('.scalar_sub', 'fsubr') }} +{{ scalar_op('.scalar_sub_flipped', 'fsub') }} +{{ scalar_op('.scalar_min', 'fmin') }} +{{ scalar_op('.scalar_max', 'fmax') }} + +// per_col at NR=1 takes a *pointer* to 1 f32 at [x0, #8]; dereference +// and broadcast. Result is functionally identical to scalar but the +// load path differs. + +{% macro per_col_op(label, op) %} +{{label}}: + ldr x2, [x0, #8] + ld1rw {z4.s}, p0/z, [x2] + mov {z0.s-z3.s}, za.s[w8, 0, vgx4] + {{op}} z0.s, p0/m, z0.s, z4.s + {{op}} z1.s, p0/m, z1.s, z4.s + {{op}} z2.s, p0/m, z2.s, z4.s + {{op}} z3.s, p0/m, z3.s, z4.s + mov za.s[w8, 0, vgx4], {z0.s-z3.s} + b .non_linear_loop +{% endmacro %} + +{{ per_col_op('.per_col_add', 'fadd') }} +{{ per_col_op('.per_col_mul', 'fmul') }} +{{ per_col_op('.per_col_sub', 'fsubr') }} +{{ per_col_op('.per_col_sub_flipped', 'fsub') }} +{{ per_col_op('.per_col_min', 'fmin') }} +{{ per_col_op('.per_col_max', 'fmax') }} + +// -------- per_row ops: 64-element bias, lane-wise op against accumulator -- + +{% macro per_row_op(label, op) %} +{{label}}: + ldr x2, [x0, #8] + ld1w {z16.s-z19.s}, pn8/z, [x2] + mov {z0.s-z3.s}, za.s[w8, 0, vgx4] + {{op}} z0.s, p0/m, z0.s, z16.s + {{op}} z1.s, p0/m, z1.s, z17.s + {{op}} z2.s, p0/m, z2.s, z18.s + {{op}} z3.s, p0/m, z3.s, z19.s + mov za.s[w8, 0, vgx4], {z0.s-z3.s} + b .non_linear_loop +{% endmacro %} + +{{ per_row_op('.per_row_add', 'fadd') }} +{{ per_row_op('.per_row_mul', 'fmul') }} +{{ per_row_op('.per_row_sub', 'fsubr') }} +{{ per_row_op('.per_row_sub_flipped', 'fsub') }} +{{ per_row_op('.per_row_min', 'fmin') }} +{{ per_row_op('.per_row_max', 'fmax') }} + +// -------- not yet implemented ---------------------------------------------- + +.leaky_relu: +.q_scale: +.q_shl: +.q_shr: + b .unsupported + +// -------- epilogue --------------------------------------------------------- + +.return: + smstop + add sp, sp, #256 + ldp q14, q15, [sp, #96] + ldp q12, q13, [sp, #64] + ldp q10, q11, [sp, #32] + ldp q8, q9, [sp], #128 + ret diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/sme/sme_qmmm_i32_32x32.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/sme/sme_qmmm_i32_32x32.S.j2 new file mode 100644 index 0000000..7c7e2f5 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/sme/sme_qmmm_i32_32x32.S.j2 @@ -0,0 +1,681 @@ +// vim: ft=arm +// +// SME2 i32 32x32 quantized matmul kernel. +// +// ZA tile layout (4 .S tiles, 16x16 i32 each): +// ZA0.S : C[0..16, 0..16] (top-left) +// ZA1.S : C[0..16, 16..32] (top-right) +// ZA2.S : C[16..32, 0..16] (bottom-left) +// ZA3.S : C[16..32, 16..32] (bottom-right) +// +// Inner K-step (K decrements by 4 per iter, since SMOPA at i8 reduces 4): +// ld1b {z0, z1}, pn8/z, [A] ; 32 M × 4 K = 128 i8 of A +// ld1b {z2, z3}, pn8/z, [B] ; 32 N × 4 K = 128 i8 of B +// smopa za0.s, p0/m, p0/m, z0.b, z2.b ; ZA0 += A[0..16] × B[0..16] +// smopa za1.s, p0/m, p0/m, z0.b, z3.b +// smopa za2.s, p0/m, p0/m, z1.b, z2.b +// smopa za3.s, p0/m, p0/m, z1.b, z3.b +// +// SMOPA at i8 throughput: 4-way K reduction per insn × 16x16 cells = 1024 +// MACs per insn. With 4-tile rotation we approach 4 SMOPAs/cycle = 4 K +// reduction × 16x16 = 4096 MACs/cycle ≈ ~16 TOPS theoretical peak. +// +// Calling convention (extern "C", AAPCS64): +// x0 = const *FusedKerSpec, advanced 40 B per dispatcher iteration +// x1 = 4 KiB scratch buffer for tile spills (used by store-generic / q_scale) +// +// Tract packing requirement: i8 inputs packed with K_alignment=4 (SMOPA +// requires K%4=0). The PackedFormat::with_k_alignment(4) handles this. + +.arch armv9-a+sme2 +.text +.align 4 + +.global {{G}}sme_qmmm_i32_32x32_{{suffix}} +{{G}}sme_qmmm_i32_32x32_{{suffix}}: + + stp q8, q9, [sp, #-128]! + stp q10, q11, [sp, #32] + stp q12, q13, [sp, #64] + stp q14, q15, [sp, #96] + + sub sp, sp, #4096 + mov x1, sp + + smstart + ptrue p0.b + ptrue pn8.b + mov w8, #0 + +{% include "dispatcher.j2" %} + +// -------- AddMatMul: ZA += A·B at i8 with K=4 reduction per SMOPA ---------- + +.add_mat_mul: + ldr x9, [x0, #32] // packing index + ldr x2, [x0, #24] // b ptr + ldp x3, x4, [x0, #8] // k, a ptr + cmp x3, #0 + b.eq .non_linear_loop + cmp x9, #1 + b.eq .Lmatmul_loop +// i32i32 fallback (packing != 1, auto-test path): ZA += A[:,k] (x) B[k,:], one +// K-step at a time via predicated MLA rank-1 updates. One instruction per line: +// the Apple/LLVM AArch64 assembler treats `;` as a COMMENT, so semicolon-packed +// statements silently drop everything after the first `;`. +.Lk32: + ld1w {z2.s}, p0/z, [x2] // B[k, 0..16] + ld1w {z3.s}, p0/z, [x2, #1, mul vl] // B[k, 16..32] + mov w12, #0 +.Lkt: + ldr w10, [x4, w12, uxtw #2] // A[k, w12] + dup z4.s, w10 + mov z16.s, p0/m, za0h.s[w12, 0] + mov z17.s, p0/m, za1h.s[w12, 0] + mla z16.s, p0/m, z2.s, z4.s // C[w12, 0..16] += A[w12] * B[0..16] + mla z17.s, p0/m, z3.s, z4.s // C[w12, 16..32] += A[w12] * B[16..32] + mov za0h.s[w12, 0], p0/m, z16.s + mov za1h.s[w12, 0], p0/m, z17.s + add w10, w12, #16 + ldr w10, [x4, w10, uxtw #2] // A[k, w12+16] + dup z4.s, w10 + mov z18.s, p0/m, za2h.s[w12, 0] + mov z19.s, p0/m, za3h.s[w12, 0] + mla z18.s, p0/m, z2.s, z4.s // C[w12+16, 0..16] += A[w12+16] * B[0..16] + mla z19.s, p0/m, z3.s, z4.s // C[w12+16, 16..32] += A[w12+16] * B[16..32] + mov za2h.s[w12, 0], p0/m, z18.s + mov za3h.s[w12, 0], p0/m, z19.s + add w12, w12, #1 + cmp w12, #16 + b.lt .Lkt + add x4, x4, #128 + add x2, x2, #128 + subs x3, x3, #1 + b.ne .Lk32 + b .non_linear_loop + +.Lmatmul_loop: + ld1b {z0.b, z1.b}, pn8/z, [x4] + ld1b {z2.b, z3.b}, pn8/z, [x2] + add x4, x4, #128 + add x2, x2, #128 + smopa za0.s, p0/m, p0/m, z0.b, z2.b + smopa za1.s, p0/m, p0/m, z0.b, z3.b + smopa za2.s, p0/m, p0/m, z1.b, z2.b + smopa za3.s, p0/m, p0/m, z1.b, z3.b + subs x3, x3, #4 + b.gt .Lmatmul_loop + b .non_linear_loop + +.clear: + zero {za} + b .non_linear_loop + +// -------- Store: i32 tile -> memory (port of Phase 1 f32 store) ----------- + +.store: + ldp x5, x6, [x0, #8] // ptr, row_byte_stride + ldp x7, x9, [x0, #24] // col_byte_stride, item_size + + cmp x7, #4 + b.ne .Lstore_generic + cmp x9, #4 + b.ne .Lstore_generic + + add x4, x5, #64 + mov w12, #0 +.Lstore_top: + st1w {za0h.s[w12, 0]}, p0, [x5] + st1w {za1h.s[w12, 0]}, p0, [x4] + add x5, x5, x6 + add x4, x4, x6 + add w12, w12, #1 + cmp w12, #16 + b.lt .Lstore_top + mov w12, #0 +.Lstore_bot: + st1w {za2h.s[w12, 0]}, p0, [x5] + st1w {za3h.s[w12, 0]}, p0, [x4] + add x5, x5, x6 + add x4, x4, x6 + add w12, w12, #1 + cmp w12, #16 + b.lt .Lstore_bot + b .non_linear_loop + +.Lstore_generic: + mov x13, x9 // preserve item_size before x9 is reused as a ptr + mov x4, x1 + add x9, x1, #64 + mov w12, #0 +.Lstore_spill_top: + st1w {za0h.s[w12, 0]}, p0, [x4] + st1w {za1h.s[w12, 0]}, p0, [x9] + add x4, x4, #128 + add x9, x9, #128 + add w12, w12, #1 + cmp w12, #16 + b.lt .Lstore_spill_top + mov w12, #0 +.Lstore_spill_bot: + st1w {za2h.s[w12, 0]}, p0, [x4] + st1w {za3h.s[w12, 0]}, p0, [x9] + add x4, x4, #128 + add x9, x9, #128 + add w12, w12, #1 + cmp w12, #16 + b.lt .Lstore_spill_bot + + mov x3, #0 +.Lstore_row: + mov x4, x5 + mov x10, #0 + lsl x9, x3, #7 + add x11, x1, x9 +.Lstore_col: + ldr w9, [x11], #4 + cmp x13, #1 // item_size: 1 -> strb, 2 -> strh, else (4) -> str + b.eq .Lstore_b1 + cmp x13, #2 + b.eq .Lstore_b2 + str w9, [x4] + b .Lstore_cnext +.Lstore_b1: + strb w9, [x4] + b .Lstore_cnext +.Lstore_b2: + strh w9, [x4] +.Lstore_cnext: + add x4, x4, x7 + add x10, x10, #1 + cmp x10, #32 + b.lt .Lstore_col + add x5, x5, x6 + add x3, x3, #1 + cmp x3, #32 + b.lt .Lstore_row + b .non_linear_loop + +// -------- LoadTile: ZA := row-major i32 tile from memory ------------------- + +.load_tile: + ldr x2, [x0, #16] + add x4, x2, #64 + mov w12, #0 +.Lloadtile_top: + ld1w {z6.s}, p0/z, [x2] + ld1w {z7.s}, p0/z, [x4] + mov za0h.s[w12, 0], p0/m, z6.s + mov za1h.s[w12, 0], p0/m, z7.s + add x2, x2, #128 + add x4, x4, #128 + add w12, w12, #1 + cmp w12, #16 + b.lt .Lloadtile_top + mov w12, #0 +.Lloadtile_bot: + ld1w {z6.s}, p0/z, [x2] + ld1w {z7.s}, p0/z, [x4] + mov za2h.s[w12, 0], p0/m, z6.s + mov za3h.s[w12, 0], p0/m, z7.s + add x2, x2, #128 + add x4, x4, #128 + add w12, w12, #1 + cmp w12, #16 + b.lt .Lloadtile_bot + b .non_linear_loop + +// -------- AddUnicast: ZA += C (strided load + add) ------------------------ + +.add_unicast: + ldp x5, x6, [x0, #8] // ptr, row_byte_stride + ldp x7, x9, [x0, #24] // col_byte_stride, item_size + + cmp x7, #4 + b.ne .Laddu_generic + cmp x9, #4 + b.ne .Laddu_generic + + add x4, x5, #64 + mov w12, #0 +.Laddu_top: + ld1w {z8.s}, p0/z, [x5] + ld1w {z9.s}, p0/z, [x4] + mov z6.s, p0/m, za0h.s[w12, 0] + mov z7.s, p0/m, za1h.s[w12, 0] + add z6.s, p0/m, z6.s, z8.s + add z7.s, p0/m, z7.s, z9.s + mov za0h.s[w12, 0], p0/m, z6.s + mov za1h.s[w12, 0], p0/m, z7.s + add x5, x5, x6 + add x4, x4, x6 + add w12, w12, #1 + cmp w12, #16 + b.lt .Laddu_top + mov w12, #0 +.Laddu_bot: + ld1w {z8.s}, p0/z, [x5] + ld1w {z9.s}, p0/z, [x4] + mov z6.s, p0/m, za2h.s[w12, 0] + mov z7.s, p0/m, za3h.s[w12, 0] + add z6.s, p0/m, z6.s, z8.s + add z7.s, p0/m, z7.s, z9.s + mov za2h.s[w12, 0], p0/m, z6.s + mov za3h.s[w12, 0], p0/m, z7.s + add x5, x5, x6 + add x4, x4, x6 + add w12, w12, #1 + cmp w12, #16 + b.lt .Laddu_bot + b .non_linear_loop + +.Laddu_generic: + // Strided gather to scratch, then contig accumulate (mirrors Phase 1). + mov x3, #0 + mov x10, x1 +.Laddu_gather_row: + mov x11, x5 + mov x4, #0 +.Laddu_gather_col: + ldr w9, [x11] + str w9, [x10], #4 + add x11, x11, x7 + add x4, x4, #1 + cmp x4, #32 + b.lt .Laddu_gather_col + add x5, x5, x6 + add x3, x3, #1 + cmp x3, #32 + b.lt .Laddu_gather_row + + mov x4, x1 + add x9, x1, #64 + mov w12, #0 +.Laddu_apply_top: + ld1w {z8.s}, p0/z, [x4] + ld1w {z10.s}, p0/z, [x9] + mov z6.s, p0/m, za0h.s[w12, 0] + mov z7.s, p0/m, za1h.s[w12, 0] + add z6.s, p0/m, z6.s, z8.s + add z7.s, p0/m, z7.s, z10.s + mov za0h.s[w12, 0], p0/m, z6.s + mov za1h.s[w12, 0], p0/m, z7.s + add x4, x4, #128 + add x9, x9, #128 + add w12, w12, #1 + cmp w12, #16 + b.lt .Laddu_apply_top + mov w12, #0 +.Laddu_apply_bot: + ld1w {z8.s}, p0/z, [x4] + ld1w {z10.s}, p0/z, [x9] + mov z6.s, p0/m, za2h.s[w12, 0] + mov z7.s, p0/m, za3h.s[w12, 0] + add z6.s, p0/m, z6.s, z8.s + add z7.s, p0/m, z7.s, z10.s + mov za2h.s[w12, 0], p0/m, z6.s + mov za3h.s[w12, 0], p0/m, z7.s + add x4, x4, #128 + add x9, x9, #128 + add w12, w12, #1 + cmp w12, #16 + b.lt .Laddu_apply_bot + b .non_linear_loop + +// -------- AddRowColProducts: ZA += rows ⊗ cols (i32 outer product) -------- +// +// rows: 32 i32 (broadcast per M-row), cols: 32 i32 (lane vector per N-col). +// Per ZA row, we need: ZA[i, j] += rows[i] * cols[j]. Slice-by-slice. + +.add_row_col_products: + ldp x2, x3, [x0, #8] // rows ptr, cols ptr + ld1w {z4.s}, p0/z, [x3] // cols[0..16] + ld1w {z5.s}, p0/z, [x3, #1, mul vl] // cols[16..32] + + // Top 16 rows + mov w12, #0 +.Larcp_top: + ldr w9, [x2], #4 + dup z16.s, w9 // broadcast rows[i] to z16 + mov z6.s, p0/m, za0h.s[w12, 0] + mov z7.s, p0/m, za1h.s[w12, 0] + mla z6.s, p0/m, z16.s, z4.s // z6 += z16 * cols[0..16] + mla z7.s, p0/m, z16.s, z5.s // z7 += z16 * cols[16..32] + mov za0h.s[w12, 0], p0/m, z6.s + mov za1h.s[w12, 0], p0/m, z7.s + add w12, w12, #1 + cmp w12, #16 + b.lt .Larcp_top + // Bottom 16 rows + mov w12, #0 +.Larcp_bot: + ldr w9, [x2], #4 + dup z16.s, w9 + mov z6.s, p0/m, za2h.s[w12, 0] + mov z7.s, p0/m, za3h.s[w12, 0] + mla z6.s, p0/m, z16.s, z4.s + mla z7.s, p0/m, z16.s, z5.s + mov za2h.s[w12, 0], p0/m, z6.s + mov za3h.s[w12, 0], p0/m, z7.s + add w12, w12, #1 + cmp w12, #16 + b.lt .Larcp_bot + b .non_linear_loop + +// -------- scalar fuse ops: broadcast scalar, apply lane-wise -------------- +// +// Sub vs SubF (matches Phase 1's f32 convention): +// ScalarSub → result = scalar - z (mnemonic: subr) +// ScalarSubF → result = z - scalar (mnemonic: sub) + +{% macro scalar_op_i32(label, op) %} +{{label}}: + ldr w2, [x0, #8] + dup z4.s, w2 + mov w12, #0 +.L{{label|replace('.', '')}}_top: + mov z6.s, p0/m, za0h.s[w12, 0] + mov z7.s, p0/m, za1h.s[w12, 0] + {{op}} z6.s, p0/m, z6.s, z4.s + {{op}} z7.s, p0/m, z7.s, z4.s + mov za0h.s[w12, 0], p0/m, z6.s + mov za1h.s[w12, 0], p0/m, z7.s + add w12, w12, #1 + cmp w12, #16 + b.lt .L{{label|replace('.', '')}}_top + mov w12, #0 +.L{{label|replace('.', '')}}_bot: + mov z6.s, p0/m, za2h.s[w12, 0] + mov z7.s, p0/m, za3h.s[w12, 0] + {{op}} z6.s, p0/m, z6.s, z4.s + {{op}} z7.s, p0/m, z7.s, z4.s + mov za2h.s[w12, 0], p0/m, z6.s + mov za3h.s[w12, 0], p0/m, z7.s + add w12, w12, #1 + cmp w12, #16 + b.lt .L{{label|replace('.', '')}}_bot + b .non_linear_loop +{% endmacro %} + +{{ scalar_op_i32('.scalar_add', 'add') }} +{{ scalar_op_i32('.scalar_mul', 'mul') }} +{{ scalar_op_i32('.scalar_sub', 'subr') }} +{{ scalar_op_i32('.scalar_sub_flipped', 'sub') }} +{{ scalar_op_i32('.scalar_min', 'smin') }} +{{ scalar_op_i32('.scalar_max', 'smax') }} + +// -------- per_col fuse ops: 32-elem vector, broadcast across M rows ------ + +{% macro per_col_op_i32(label, op) %} +{{label}}: + ldr x2, [x0, #8] + ld1w {z4.s}, p0/z, [x2] + ld1w {z5.s}, p0/z, [x2, #1, mul vl] + mov w12, #0 +.L{{label|replace('.', '')}}_top: + mov z6.s, p0/m, za0h.s[w12, 0] + mov z7.s, p0/m, za1h.s[w12, 0] + {{op}} z6.s, p0/m, z6.s, z4.s + {{op}} z7.s, p0/m, z7.s, z5.s + mov za0h.s[w12, 0], p0/m, z6.s + mov za1h.s[w12, 0], p0/m, z7.s + add w12, w12, #1 + cmp w12, #16 + b.lt .L{{label|replace('.', '')}}_top + mov w12, #0 +.L{{label|replace('.', '')}}_bot: + mov z6.s, p0/m, za2h.s[w12, 0] + mov z7.s, p0/m, za3h.s[w12, 0] + {{op}} z6.s, p0/m, z6.s, z4.s + {{op}} z7.s, p0/m, z7.s, z5.s + mov za2h.s[w12, 0], p0/m, z6.s + mov za3h.s[w12, 0], p0/m, z7.s + add w12, w12, #1 + cmp w12, #16 + b.lt .L{{label|replace('.', '')}}_bot + b .non_linear_loop +{% endmacro %} + +{{ per_col_op_i32('.per_col_add', 'add') }} +{{ per_col_op_i32('.per_col_mul', 'mul') }} +{{ per_col_op_i32('.per_col_sub', 'subr') }} +{{ per_col_op_i32('.per_col_sub_flipped', 'sub') }} +{{ per_col_op_i32('.per_col_min', 'smin') }} +{{ per_col_op_i32('.per_col_max', 'smax') }} + +// -------- per_row fuse ops: 32-elem vector, one scalar per M row --------- + +{% macro per_row_op_i32(label, op) %} +{{label}}: + ldr x2, [x0, #8] + add x3, x2, #64 + mov w12, #0 +.L{{label|replace('.', '')}}_top: + ldr w4, [x2], #4 + dup z4.s, w4 + mov z6.s, p0/m, za0h.s[w12, 0] + mov z7.s, p0/m, za1h.s[w12, 0] + {{op}} z6.s, p0/m, z6.s, z4.s + {{op}} z7.s, p0/m, z7.s, z4.s + mov za0h.s[w12, 0], p0/m, z6.s + mov za1h.s[w12, 0], p0/m, z7.s + add w12, w12, #1 + cmp w12, #16 + b.lt .L{{label|replace('.', '')}}_top + mov w12, #0 +.L{{label|replace('.', '')}}_bot: + ldr w4, [x3], #4 + dup z4.s, w4 + mov z6.s, p0/m, za2h.s[w12, 0] + mov z7.s, p0/m, za3h.s[w12, 0] + {{op}} z6.s, p0/m, z6.s, z4.s + {{op}} z7.s, p0/m, z7.s, z4.s + mov za2h.s[w12, 0], p0/m, z6.s + mov za3h.s[w12, 0], p0/m, z7.s + add w12, w12, #1 + cmp w12, #16 + b.lt .L{{label|replace('.', '')}}_bot + b .non_linear_loop +{% endmacro %} + +{{ per_row_op_i32('.per_row_add', 'add') }} +{{ per_row_op_i32('.per_row_mul', 'mul') }} +{{ per_row_op_i32('.per_row_sub', 'subr') }} +{{ per_row_op_i32('.per_row_sub_flipped', 'sub') }} +{{ per_row_op_i32('.per_row_min', 'smin') }} +{{ per_row_op_i32('.per_row_max', 'smax') }} + +// -------- Quantization fuse ops (bit-exact port of generic/rounding.rs) ---- +// +// Strategy: spill the 32x32 i32 ZA tile to the 4 KiB scratch (x1), quantize +// element-wise in SCALAR GP registers (streaming-mode legal: smull/lsr/asr/ +// cneg/cset/... are base A64 and unaffected by PSTATE.SM), then reload to ZA. +// Quant is not the hot path; this mirrors the scalar approach already proven +// in arm64/sve/sve_mmm_i32.c. Everything is inlined (no `bl` — a nested call +// would clobber x30 and corrupt the final `ret`). +// +// Bit-exactness: the reference forms the FULL i64 product (mult*v) and does a +// single magnitude-rounding shift by (shift+31) with a per-policy nudge. A +// vector sqdmulh+srshl truncates the low 31 bits before the second shift, so +// it is NOT equivalent — hence the i64 scalar port. +// +// RoundingPolicy: Native=0 Zero=1 Away=2 MinusInf=3 PlusInf=4 Even=5 Odd=6. + +// Spill ZA0..ZA3 -> scratch[x1] as a contiguous 32x32 row-major i32 matrix +// (same layout the generic store path uses). Clobbers x4, x9, w12. +{% macro za_spill(sfx) %} + mov x4, x1 + add x9, x1, #64 + mov w12, #0 +.Lspt_{{sfx}}: + st1w {za0h.s[w12, 0]}, p0, [x4] + st1w {za1h.s[w12, 0]}, p0, [x9] + add x4, x4, #128 + add x9, x9, #128 + add w12, w12, #1 + cmp w12, #16 + b.lt .Lspt_{{sfx}} + mov w12, #0 +.Lspb_{{sfx}}: + st1w {za2h.s[w12, 0]}, p0, [x4] + st1w {za3h.s[w12, 0]}, p0, [x9] + add x4, x4, #128 + add x9, x9, #128 + add w12, w12, #1 + cmp w12, #16 + b.lt .Lspb_{{sfx}} +{% endmacro %} + +// Reload scratch[x1] (32x32 row-major i32) -> ZA0..ZA3. Clobbers x4,x9,w12,z6,z7. +{% macro za_reload(sfx) %} + mov x4, x1 + add x9, x1, #64 + mov w12, #0 +.Lrlt_{{sfx}}: + ld1w {z6.s}, p0/z, [x4] + ld1w {z7.s}, p0/z, [x9] + mov za0h.s[w12, 0], p0/m, z6.s + mov za1h.s[w12, 0], p0/m, z7.s + add x4, x4, #128 + add x9, x9, #128 + add w12, w12, #1 + cmp w12, #16 + b.lt .Lrlt_{{sfx}} + mov w12, #0 +.Lrlb_{{sfx}}: + ld1w {z6.s}, p0/z, [x4] + ld1w {z7.s}, p0/z, [x9] + mov za2h.s[w12, 0], p0/m, z6.s + mov za3h.s[w12, 0], p0/m, z7.s + add x4, x4, #128 + add x9, x9, #128 + add w12, w12, #1 + cmp w12, #16 + b.lt .Lrlb_{{sfx}} +{% endmacro %} + +// Magnitude-rounding shared by q_scale and q_shr (mirrors `Mul for Scaler` +// / `i32::q_shr`). In: x12 = val (i64), x5 = shift, x6 = policy. Out: w14 (i32). +// Clobbers x13,x15,x16,x17. Preserves x5,x6,x7,x10,x11,x12. +{% macro round_mag(sfx) %} + cmp x5, #0 + b.gt .Lrpos_{{sfx}} + neg x13, x5 + lsl x13, x12, x13 // val << (-shift) + mov w14, w13 + b .Lrend_{{sfx}} +.Lrpos_{{sfx}}: + cmp x12, #0 + cneg x15, x12, mi // x15 = |val| + sub x13, x5, #1 + mov x16, #1 + lsl x16, x16, x13 // x16 = half = 1 << (shift-1) + cmp x6, #2 // Away -> nudge 0 + b.eq .Lrn0_{{sfx}} + cmp x6, #1 // Zero -> nudge -1 + b.ne .Lrna_{{sfx}} + mov x17, #-1 + b .Lrnd_{{sfx}} +.Lrna_{{sfx}}: + cmp x6, #3 // MinusInf -> -(val >= 0) + b.ne .Lrnb_{{sfx}} + cmp x12, #0 + cset x17, ge + neg x17, x17 + b .Lrnd_{{sfx}} +.Lrnb_{{sfx}}: + cmp x6, #4 // PlusInf -> -(val <= 0) + b.ne .Lrnc_{{sfx}} + cmp x12, #0 + cset x17, le + neg x17, x17 + b .Lrnd_{{sfx}} +.Lrnc_{{sfx}}: + cmp x6, #5 // Even -> ((|val|>>shift)&1) - 1 + b.ne .Lrno_{{sfx}} + lsr x17, x15, x5 + and x17, x17, #1 + sub x17, x17, #1 + b .Lrnd_{{sfx}} +.Lrno_{{sfx}}: // Odd -> -((|val|>>shift)&1) + lsr x17, x15, x5 + and x17, x17, #1 + neg x17, x17 + b .Lrnd_{{sfx}} +.Lrn0_{{sfx}}: + mov x17, #0 +.Lrnd_{{sfx}}: + add x15, x15, x16 + add x15, x15, x17 + lsr x15, x15, x5 // (|val| + half + nudge) >> shift + cmp x12, #0 + cneg x14, x15, mi // signum(val) * mag +.Lrend_{{sfx}}: +{% endmacro %} + +// QScale(shift, policy, mult): val = mult*v (i64); shift += 31; magnitude round. +.q_scale: + ldr x5, [x0, #8] // shift (isize) + ldr x6, [x0, #16] // policy + ldr w7, [x0, #24] // mult (i32) + add x5, x5, #31 + {{ za_spill('qsc') }} + mov x10, x1 + mov x11, #1024 +.Lqsc_loop: + ldr w9, [x10] + smull x12, w7, w9 // val = (i64)mult * (i64)v + {{ round_mag('qsc') }} + str w14, [x10], #4 + subs x11, x11, #1 + b.ne .Lqsc_loop + {{ za_reload('qsc') }} + b .non_linear_loop + +// RoundingShiftRight(shift, policy): val = v (i64); magnitude round (shift>0). +.q_shr: + ldr x5, [x0, #8] // shift (usize, >= 1) + ldr x6, [x0, #16] // policy + {{ za_spill('qsr') }} + mov x10, x1 + mov x11, #1024 +.Lqsr_loop: + ldr w9, [x10] + sxtw x12, w9 // val = (i64)v + {{ round_mag('qsr') }} + str w14, [x10], #4 + subs x11, x11, #1 + b.ne .Lqsr_loop + {{ za_reload('qsr') }} + b .non_linear_loop + +// ShiftLeft(shift): result = v << shift (32-bit wrapping, matches i32::q_shl). +.q_shl: + ldr x5, [x0, #8] // shift (usize) + {{ za_spill('qsl') }} + mov x10, x1 + mov x11, #1024 +.Lqsl_loop: + ldr w9, [x10] + lsl w9, w9, w5 + str w9, [x10], #4 + subs x11, x11, #1 + b.ne .Lqsl_loop + {{ za_reload('qsl') }} + b .non_linear_loop + +// -------- LeakyRelu (excluded via CAN_FUSE_I32) --------------------------- + +.leaky_relu: + b .unsupported + +// -------- epilogue -------------------------------------------------------- + +.return: + smstop + add sp, sp, #4096 + ldp q14, q15, [sp, #96] + ldp q12, q13, [sp, #64] + ldp q10, q11, [sp, #32] + ldp q8, q9, [sp], #128 + ret diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/sve/sve_mmm_f16.c b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/sve/sve_mmm_f16.c new file mode 100644 index 0000000..55bb28b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/arm64/sve/sve_mmm_f16.c @@ -0,0 +1,153 @@ +// SVE f16 GEMM kernel for tract's MMM framework (the mmm_f16 slot). +// +// Tile MR=8 x NR=8 with native f16 accumulation, the f16 sibling of +// sve_mmm_f32.c. The hot AddMatMul is the same vector-length-agnostic +// broadcast-A rank-1 update, but over f16 lanes: the NR columns are walked in +// svcnth() chunks with whilelt predication and folded with svmla_n_f16 (native +// f16 fused multiply-add, as the NEON arm64fp16 kernels do), so one binary is +// correct and full-width at any SVE VL 128..2048-bit. +// +// Gated on FEAT_SVE2 AND FEAT_FP16 (Rust side). Built with +fp16. Consumes +// tract's native f16 K-major packing. Fuse ops act on the MRxNR tile in memory +// (scalar; not the hot path); as with the f32 kernel, LeakyRelu and the i32 +// quantization ops are excluded by CAN_FUSE. Returns 0 on success, 1 otherwise. + +#include +#include +#include + +#define MR 8 +#define NR 8 + +enum { + DONE = 0, CLEAR, LOAD_TILE, + SCALAR_MIN, SCALAR_MAX, SCALAR_ADD, SCALAR_MUL, SCALAR_SUB, SCALAR_SUBF, + LEAKY_RELU, + PER_ROW_MIN, PER_ROW_MAX, PER_ROW_ADD, PER_ROW_MUL, PER_ROW_SUB, PER_ROW_SUBF, + PER_COL_MIN, PER_COL_MAX, PER_COL_ADD, PER_COL_MUL, PER_COL_SUB, PER_COL_SUBF, + Q_SCALE, Q_SHR, Q_SHL, + ADD_UNICAST, ADD_ROW_COL_PRODUCTS, STORE, ADD_MAT_MUL +}; + +typedef struct { + uint64_t disc; + uint64_t f0, f1, f2, f3; +} spec_t; + +static inline __fp16 f16_of(uint64_t bits) { + __fp16 f; + uint16_t lo = (uint16_t)bits; + memcpy(&f, &lo, 2); + return f; +} + +// AddMatMul: ab[m][n] += sum_k pa[k*MR+m] * pb[k*NR+n]. VLA over NR (f16 lanes). +static void add_mat_mul(__fp16 ab[MR][NR], const __fp16 *pa, const __fp16 *pb, long k) { + for (long n0 = 0; n0 < NR; n0 += svcnth()) { + svbool_t pg = svwhilelt_b16((uint64_t)n0, (uint64_t)NR); + svfloat16_t a0 = svld1_f16(pg, &ab[0][n0]), a1 = svld1_f16(pg, &ab[1][n0]); + svfloat16_t a2 = svld1_f16(pg, &ab[2][n0]), a3 = svld1_f16(pg, &ab[3][n0]); + svfloat16_t a4 = svld1_f16(pg, &ab[4][n0]), a5 = svld1_f16(pg, &ab[5][n0]); + svfloat16_t a6 = svld1_f16(pg, &ab[6][n0]), a7 = svld1_f16(pg, &ab[7][n0]); + for (long kk = 0; kk < k; kk++) { + svfloat16_t b = svld1_f16(pg, &pb[kk * NR + n0]); + const __fp16 *arow = &pa[kk * MR]; + a0 = svmla_n_f16_x(pg, a0, b, arow[0]); + a1 = svmla_n_f16_x(pg, a1, b, arow[1]); + a2 = svmla_n_f16_x(pg, a2, b, arow[2]); + a3 = svmla_n_f16_x(pg, a3, b, arow[3]); + a4 = svmla_n_f16_x(pg, a4, b, arow[4]); + a5 = svmla_n_f16_x(pg, a5, b, arow[5]); + a6 = svmla_n_f16_x(pg, a6, b, arow[6]); + a7 = svmla_n_f16_x(pg, a7, b, arow[7]); + } + svst1_f16(pg, &ab[0][n0], a0); svst1_f16(pg, &ab[1][n0], a1); + svst1_f16(pg, &ab[2][n0], a2); svst1_f16(pg, &ab[3][n0], a3); + svst1_f16(pg, &ab[4][n0], a4); svst1_f16(pg, &ab[5][n0], a5); + svst1_f16(pg, &ab[6][n0], a6); svst1_f16(pg, &ab[7][n0], a7); + } +} + +// Store the MRxNR f16 tile with arbitrary row/col byte strides. +static void store_tile(__fp16 ab[MR][NR], const spec_t *s) { + uint8_t *ptr = (uint8_t *)s->f0; + long rstride = (long)s->f1, cstride = (long)s->f2, isz = (long)s->f3; + for (long i = 0; i < MR; i++) + for (long j = 0; j < NR; j++) { + uint8_t *p = ptr + i * rstride + j * cstride; + if (isz == 2) + *(__fp16 *)p = ab[i][j]; + else if (isz == 4) + *(float *)p = (float)ab[i][j]; + else + memcpy(p, &ab[i][j], isz); + } +} + +intptr_t sve_mmm_f16_kernel(const spec_t *ops) { + __fp16 ab[MR][NR]; + memset(ab, 0, sizeof(ab)); + for (const spec_t *s = ops;; s++) { + switch (s->disc) { + case DONE: + return 0; + case CLEAR: + memset(ab, 0, sizeof(ab)); + break; + case ADD_MAT_MUL: { + long k = (long)s->f0; + add_mat_mul(ab, (const __fp16 *)s->f1, (const __fp16 *)s->f2, k); + break; + } + case STORE: + store_tile(ab, s); + break; + case LOAD_TILE: { + const __fp16 *src = (const __fp16 *)s->f1; + for (long i = 0; i < MR; i++) + for (long j = 0; j < NR; j++) ab[i][j] = src[i * NR + j]; + break; + } + case ADD_UNICAST: { + uint8_t *ptr = (uint8_t *)s->f0; + long rstride = (long)s->f1, cstride = (long)s->f2, isz = (long)s->f3; + for (long i = 0; i < MR; i++) + for (long j = 0; j < NR; j++) { + const uint8_t *p = ptr + i * rstride + j * cstride; + if (isz == 2) + ab[i][j] += *(const __fp16 *)p; + else + ab[i][j] += (__fp16) * (const float *)p; + } + break; + } + case ADD_ROW_COL_PRODUCTS: { + const __fp16 *rows = (const __fp16 *)s->f0; + const __fp16 *cols = (const __fp16 *)s->f1; + for (long i = 0; i < MR; i++) + for (long j = 0; j < NR; j++) ab[i][j] += rows[i] * cols[j]; + break; + } + case SCALAR_MIN: { __fp16 v=f16_of(s->f0); for(long i=0;if0); for(long i=0;iv?ab[i][j]:v; break; } + case SCALAR_ADD: { __fp16 v=f16_of(s->f0); for(long i=0;if0); for(long i=0;if0); for(long i=0;if0); for(long i=0;if0; for(long i=0;if0; for(long i=0;im[i]?ab[i][j]:m[i]; break; } + case PER_ROW_ADD: { const __fp16*m=(const __fp16*)s->f0; for(long i=0;if0; for(long i=0;if0; for(long i=0;if0; for(long i=0;if0; for(long i=0;if0; for(long i=0;im[j]?ab[i][j]:m[j]; break; } + case PER_COL_ADD: { const __fp16*m=(const __fp16*)s->f0; for(long i=0;if0; for(long i=0;if0; for(long i=0;if0; for(long i=0;i array (40 bytes / entry, +// discriminant u64 at offset 0, fields at 8/16/24/32) until Done, exactly like +// the asm dispatcher. f32 GEMM consumes tract's native K-major packing +// (pa[k*MR+m], pb[k*NR+n]) so no custom packing format is required. +// +// Returns 0 on success, 1 if asked to do an unsupported fused op. + +#include +#include +#include + +#define MR 8 +#define NR 8 + +// FusedKerSpec discriminants (must match frame/mmm/fuse.rs enum order). +enum { + DONE = 0, CLEAR, LOAD_TILE, + SCALAR_MIN, SCALAR_MAX, SCALAR_ADD, SCALAR_MUL, SCALAR_SUB, SCALAR_SUBF, + LEAKY_RELU, + PER_ROW_MIN, PER_ROW_MAX, PER_ROW_ADD, PER_ROW_MUL, PER_ROW_SUB, PER_ROW_SUBF, + PER_COL_MIN, PER_COL_MAX, PER_COL_ADD, PER_COL_MUL, PER_COL_SUB, PER_COL_SUBF, + Q_SCALE, Q_SHR, Q_SHL, + ADD_UNICAST, ADD_ROW_COL_PRODUCTS, STORE, ADD_MAT_MUL +}; + +typedef struct { + uint64_t disc; + uint64_t f0, f1, f2, f3; // fields at byte offsets 8, 16, 24, 32 +} spec_t; + +// AddMatMul: ab[m][n] += sum_k pa[k*MR+m] * pb[k*NR+n]. VLA over NR. +static void add_mat_mul(float ab[MR][NR], const float *pa, const float *pb, long k) { + for (long n0 = 0; n0 < NR; n0 += svcntw()) { + svbool_t pg = svwhilelt_b32((uint64_t)n0, (uint64_t)NR); + svfloat32_t a0 = svld1_f32(pg, &ab[0][n0]), a1 = svld1_f32(pg, &ab[1][n0]); + svfloat32_t a2 = svld1_f32(pg, &ab[2][n0]), a3 = svld1_f32(pg, &ab[3][n0]); + svfloat32_t a4 = svld1_f32(pg, &ab[4][n0]), a5 = svld1_f32(pg, &ab[5][n0]); + svfloat32_t a6 = svld1_f32(pg, &ab[6][n0]), a7 = svld1_f32(pg, &ab[7][n0]); + for (long kk = 0; kk < k; kk++) { + svfloat32_t b = svld1_f32(pg, &pb[kk * NR + n0]); + const float *arow = &pa[kk * MR]; + a0 = svmla_n_f32_x(pg, a0, b, arow[0]); + a1 = svmla_n_f32_x(pg, a1, b, arow[1]); + a2 = svmla_n_f32_x(pg, a2, b, arow[2]); + a3 = svmla_n_f32_x(pg, a3, b, arow[3]); + a4 = svmla_n_f32_x(pg, a4, b, arow[4]); + a5 = svmla_n_f32_x(pg, a5, b, arow[5]); + a6 = svmla_n_f32_x(pg, a6, b, arow[6]); + a7 = svmla_n_f32_x(pg, a7, b, arow[7]); + } + svst1_f32(pg, &ab[0][n0], a0); svst1_f32(pg, &ab[1][n0], a1); + svst1_f32(pg, &ab[2][n0], a2); svst1_f32(pg, &ab[3][n0], a3); + svst1_f32(pg, &ab[4][n0], a4); svst1_f32(pg, &ab[5][n0], a5); + svst1_f32(pg, &ab[6][n0], a6); svst1_f32(pg, &ab[7][n0], a7); + } +} + +static inline float f32_of(uint64_t bits) { + float f; + uint32_t lo = (uint32_t)bits; + memcpy(&f, &lo, 4); + return f; +} + +// Store the MRxNR tile to memory with arbitrary row/col byte strides. +static void store_tile(float ab[MR][NR], const spec_t *s) { + uint8_t *ptr = (uint8_t *)s->f0; + long rstride = (long)s->f1, cstride = (long)s->f2, isz = (long)s->f3; + for (long i = 0; i < MR; i++) + for (long j = 0; j < NR; j++) { + uint8_t *p = ptr + i * rstride + j * cstride; + if (isz == 4) + *(float *)p = ab[i][j]; + else + memcpy(p, &ab[i][j], isz); + } +} + +// Returns isize (64-bit) to match tract's kernel ABI — NOT int (would leave the +// upper 32 bits of x0 undefined). +intptr_t sve_mmm_f32_kernel(const spec_t *ops) { + float ab[MR][NR]; + memset(ab, 0, sizeof(ab)); + for (const spec_t *s = ops;; s++) { + switch (s->disc) { + case DONE: + return 0; + case CLEAR: + memset(ab, 0, sizeof(ab)); + break; + case ADD_MAT_MUL: { + long k = (long)s->f0; + const float *pa = (const float *)s->f1; + const float *pb = (const float *)s->f2; + add_mat_mul(ab, pa, pb, k); + break; + } + case STORE: + store_tile(ab, s); + break; + case LOAD_TILE: { + // LoadTile(col_major_ptr, row_major_ptr); use the row-major one. + const float *src = (const float *)s->f1; + for (long i = 0; i < MR; i++) + for (long j = 0; j < NR; j++) ab[i][j] = src[i * NR + j]; + break; + } + case ADD_UNICAST: { + uint8_t *ptr = (uint8_t *)s->f0; + long rstride = (long)s->f1, cstride = (long)s->f2; + for (long i = 0; i < MR; i++) + for (long j = 0; j < NR; j++) + ab[i][j] += *(const float *)(ptr + i * rstride + j * cstride); + break; + } + case ADD_ROW_COL_PRODUCTS: { + const float *rows = (const float *)s->f0; + const float *cols = (const float *)s->f1; + for (long i = 0; i < MR; i++) + for (long j = 0; j < NR; j++) ab[i][j] += rows[i] * cols[j]; + break; + } + // ---- scalar fuse ops ---- + case SCALAR_MIN: { float v = f32_of(s->f0); for (long i=0;if0); for (long i=0;iv?ab[i][j]:v; break; } + case SCALAR_ADD: { float v = f32_of(s->f0); for (long i=0;if0); for (long i=0;if0); for (long i=0;if0); for (long i=0;if0; for(long i=0;if0; for(long i=0;im[i]?ab[i][j]:m[i]; break; } + case PER_ROW_ADD: { const float*m=(const float*)s->f0; for(long i=0;if0; for(long i=0;if0; for(long i=0;if0; for(long i=0;if0; for(long i=0;if0; for(long i=0;im[j]?ab[i][j]:m[j]; break; } + case PER_COL_ADD: { const float*m=(const float*)s->f0; for(long i=0;if0; for(long i=0;if0; for(long i=0;if0; for(long i=0;i int32 GEMM kernel for tract's MMM framework (the qmmm_i32 slot). +// +// Tile MR=8 x NR=8, i32 accumulator. The hot AddMatMul is the vector-length- +// agnostic *widening* broadcast-A rank-1 update: per K-step it loads NR signed +// bytes of a B row and sign-extends them to i32 with `svld1sb_s32`, then folds +// MR `svmla_n_s32` updates with the (sign-extended) scalar from the A column. +// The NR columns are walked in svcntw() chunks with whilelt predication, so the +// SAME code is correct and full-width at any SVE vector length (128..2048-bit). +// +// Why widening MLA and not SDOT: SDOT reduces 4 K-contiguous i8 per i32 lane, so +// it needs A and B packed K-contiguous within a lane. tract's PackedFormat is +// K-major (mn-inner: for each k, r contiguous mn values), which is exactly what +// the per-k widening update consumes — and what arm64simd's i32 kernel uses via +// NEON SMLAL. A SDOT/SMMLA path would need a custom interleaved packer; that is +// a separate (max-throughput) kernel, not this one. +// +// int8 inputs arrive via tract's native i8i8 packing (AddMatMul packing == 1). +// The default i32i32 packing (packing == 0) is also handled (scalar) so the +// generic auto-test surface (mmm_packed_packed_tests i32i32:0) passes. +// +// ABI: identical 40-byte FusedKerSpec walk as the f32 kernel (discriminant +// u64 at offset 0, fields at 8/16/24/32). Fuse ops act on the MRxNR i32 tile in +// memory (scalar C — not the hot path), including the quantization ops +// q_scale / q_shr (rounding) / q_shl, ported bit-exact from +// linalg/src/generic/rounding.rs. +// +// Returns 0 on success, 1 if asked to do an unsupported fused op / packing. + +#include +#include +#include + +#define MR 8 +#define NR 8 + +// FusedKerSpec discriminants (must match frame/mmm/fuse.rs enum order). +enum { + DONE = 0, CLEAR, LOAD_TILE, + SCALAR_MIN, SCALAR_MAX, SCALAR_ADD, SCALAR_MUL, SCALAR_SUB, SCALAR_SUBF, + LEAKY_RELU, + PER_ROW_MIN, PER_ROW_MAX, PER_ROW_ADD, PER_ROW_MUL, PER_ROW_SUB, PER_ROW_SUBF, + PER_COL_MIN, PER_COL_MAX, PER_COL_ADD, PER_COL_MUL, PER_COL_SUB, PER_COL_SUBF, + Q_SCALE, Q_SHR, Q_SHL, + ADD_UNICAST, ADD_ROW_COL_PRODUCTS, STORE, ADD_MAT_MUL +}; + +// RoundingPolicy is #[repr(usize)] in fuse.rs: Native=0, Zero=1, Away=2, +// MinusInf=3, PlusInf=4, Even=5, Odd=6. +enum { RP_NATIVE = 0, RP_ZERO, RP_AWAY, RP_MINUSINF, RP_PLUSINF, RP_EVEN, RP_ODD }; + +typedef struct { + uint64_t disc; + uint64_t f0, f1, f2, f3; // fields at byte offsets 8, 16, 24, 32 +} spec_t; + +// AddMatMul, i8 x i8 -> i32 (packing 1): ab[m][n] += sum_k pa[k*MR+m]*pb[k*NR+n]. +// VLA widening rank-1 update over NR. +static void add_mat_mul_i8(int32_t ab[MR][NR], const int8_t *pa, const int8_t *pb, long k) { + for (long n0 = 0; n0 < NR; n0 += svcntw()) { + svbool_t pg = svwhilelt_b32((uint64_t)n0, (uint64_t)NR); + svint32_t a0 = svld1_s32(pg, &ab[0][n0]), a1 = svld1_s32(pg, &ab[1][n0]); + svint32_t a2 = svld1_s32(pg, &ab[2][n0]), a3 = svld1_s32(pg, &ab[3][n0]); + svint32_t a4 = svld1_s32(pg, &ab[4][n0]), a5 = svld1_s32(pg, &ab[5][n0]); + svint32_t a6 = svld1_s32(pg, &ab[6][n0]), a7 = svld1_s32(pg, &ab[7][n0]); + for (long kk = 0; kk < k; kk++) { + // Load NR int8 of B row kk, sign-extending each lane to i32. + svint32_t b = svld1sb_s32(pg, &pb[kk * NR + n0]); + const int8_t *arow = &pa[kk * MR]; + a0 = svmla_n_s32_x(pg, a0, b, (int32_t)arow[0]); + a1 = svmla_n_s32_x(pg, a1, b, (int32_t)arow[1]); + a2 = svmla_n_s32_x(pg, a2, b, (int32_t)arow[2]); + a3 = svmla_n_s32_x(pg, a3, b, (int32_t)arow[3]); + a4 = svmla_n_s32_x(pg, a4, b, (int32_t)arow[4]); + a5 = svmla_n_s32_x(pg, a5, b, (int32_t)arow[5]); + a6 = svmla_n_s32_x(pg, a6, b, (int32_t)arow[6]); + a7 = svmla_n_s32_x(pg, a7, b, (int32_t)arow[7]); + } + svst1_s32(pg, &ab[0][n0], a0); svst1_s32(pg, &ab[1][n0], a1); + svst1_s32(pg, &ab[2][n0], a2); svst1_s32(pg, &ab[3][n0], a3); + svst1_s32(pg, &ab[4][n0], a4); svst1_s32(pg, &ab[5][n0], a5); + svst1_s32(pg, &ab[6][n0], a6); svst1_s32(pg, &ab[7][n0], a7); + } +} + +// AddMatMul, i32 x i32 -> i32 (packing 0, default): only used by the auto-test +// surface, never in production (quantized matmul uses the i8i8 packing). Scalar. +static void add_mat_mul_i32(int32_t ab[MR][NR], const int32_t *pa, const int32_t *pb, long k) { + for (long kk = 0; kk < k; kk++) { + const int32_t *arow = &pa[kk * MR], *brow = &pb[kk * NR]; + for (long i = 0; i < MR; i++) + for (long j = 0; j < NR; j++) ab[i][j] += arow[i] * brow[j]; + } +} + +// ---- Quantization helpers, ported bit-exact from generic/rounding.rs ---- + +// i32::q_shr(shift, rp): rounding arithmetic shift right. +static int32_t q_shr_i32(int32_t v, long shift, int rp) { + int32_t half = (int32_t)1 << (shift - 1); + int32_t a = v < 0 ? -v : v; // abs (test inputs are small; matches Rust .abs()) + int32_t nudge; + switch (rp) { + case RP_ZERO: nudge = -1; break; + case RP_MINUSINF: nudge = -(int32_t)(v >= 0); break; + case RP_PLUSINF: nudge = -(int32_t)(v <= 0); break; + case RP_AWAY: nudge = 0; break; + case RP_EVEN: nudge = ((a >> shift) & 0x1) - 1; break; + case RP_ODD: nudge = -((a >> shift) & 0x1); break; + default: nudge = 0; break; // Native: unreachable for q ops + } + int32_t sign = (v > 0) - (v < 0); // signum: -1 / 0 / 1 + return sign * ((a + half + nudge) >> shift); +} + +// i32::q_scale(Scaler{mult, shift, policy}) with mult always present (the QScale +// fused op carries an explicit multiplier). Mirrors `Mul for Scaler`. +static int32_t q_scale_i32(int32_t v, long shift_in, int policy, int32_t mult) { + int64_t val = (int64_t)mult * (int64_t)v; + long shift = shift_in + 31; + if (shift > 0) { + int64_t half = (int64_t)1 << (shift - 1); + int64_t a = val < 0 ? -val : val; + int64_t nudge; + switch (policy) { + case RP_ZERO: nudge = -1; break; + case RP_MINUSINF: nudge = -(int64_t)(val >= 0); break; + case RP_PLUSINF: nudge = -(int64_t)(val <= 0); break; + case RP_AWAY: nudge = 0; break; + case RP_EVEN: nudge = ((a >> shift) & 0x1) - 1; break; + case RP_ODD: nudge = -((a >> shift) & 0x1); break; + default: nudge = 0; break; + } + int64_t sign = (val > 0) - (val < 0); + return (int32_t)(sign * ((a + half + nudge) >> shift)); + } else { + return (int32_t)(val << (-shift)); + } +} + +// Store the MRxNR i32 tile to memory with arbitrary row/col byte strides, +// truncating to the destination item size (matches generic store_t semantics +// for the tested widths 1 and 4). +static void store_tile(int32_t ab[MR][NR], const spec_t *s) { + uint8_t *ptr = (uint8_t *)s->f0; + long rstride = (long)s->f1, cstride = (long)s->f2, isz = (long)s->f3; + for (long i = 0; i < MR; i++) + for (long j = 0; j < NR; j++) { + uint8_t *p = ptr + i * rstride + j * cstride; + int32_t v = ab[i][j]; + switch (isz) { + case 1: *(uint8_t *)p = (uint8_t)v; break; + case 2: *(uint16_t *)p = (uint16_t)v; break; + case 4: *(int32_t *)p = v; break; + case 8: { int64_t w = v; memcpy(p, &w, 8); break; } + default: memcpy(p, &v, isz < 4 ? (size_t)isz : 4); break; + } + } +} + +// Returns isize (64-bit) to match tract's kernel ABI. +intptr_t sve_mmm_i32_kernel(const spec_t *ops) { + int32_t ab[MR][NR]; + memset(ab, 0, sizeof(ab)); + for (const spec_t *s = ops;; s++) { + switch (s->disc) { + case DONE: + return 0; + case CLEAR: + memset(ab, 0, sizeof(ab)); + break; + case ADD_MAT_MUL: { + long k = (long)s->f0; + long packing = (long)s->f3; + if (packing == 1) { + add_mat_mul_i8(ab, (const int8_t *)s->f1, (const int8_t *)s->f2, k); + } else if (packing == 0) { + add_mat_mul_i32(ab, (const int32_t *)s->f1, (const int32_t *)s->f2, k); + } else { + return 1; + } + break; + } + case STORE: + store_tile(ab, s); + break; + case LOAD_TILE: { + // LoadTile(col_major_ptr, row_major_ptr); use the row-major one. + const int32_t *src = (const int32_t *)s->f1; + for (long i = 0; i < MR; i++) + for (long j = 0; j < NR; j++) ab[i][j] = src[i * NR + j]; + break; + } + case ADD_UNICAST: { + uint8_t *ptr = (uint8_t *)s->f0; + long rstride = (long)s->f1, cstride = (long)s->f2, isz = (long)s->f3; + for (long i = 0; i < MR; i++) + for (long j = 0; j < NR; j++) { + const uint8_t *p = ptr + i * rstride + j * cstride; + if (isz == 1) + ab[i][j] += *(const int8_t *)p; // sign-extend + else + ab[i][j] += *(const int32_t *)p; + } + break; + } + case ADD_ROW_COL_PRODUCTS: { + const int32_t *rows = (const int32_t *)s->f0; + const int32_t *cols = (const int32_t *)s->f1; + for (long i = 0; i < MR; i++) + for (long j = 0; j < NR; j++) ab[i][j] += rows[i] * cols[j]; + break; + } + // ---- quantization fuse ops ---- + case Q_SCALE: { + long shift = (long)s->f0; + int policy = (int)s->f1; + int32_t mult = (int32_t)s->f2; + for (long i = 0; i < MR; i++) + for (long j = 0; j < NR; j++) ab[i][j] = q_scale_i32(ab[i][j], shift, policy, mult); + break; + } + case Q_SHR: { + long shift = (long)s->f0; + int policy = (int)s->f1; + for (long i = 0; i < MR; i++) + for (long j = 0; j < NR; j++) ab[i][j] = q_shr_i32(ab[i][j], shift, policy); + break; + } + case Q_SHL: { + long shift = (long)s->f0; + for (long i = 0; i < MR; i++) + for (long j = 0; j < NR; j++) ab[i][j] = ab[i][j] << shift; + break; + } + // ---- scalar fuse ops (value is an i32 in the low 32 bits of f0) ---- + case SCALAR_MIN: { int32_t v=(int32_t)s->f0; for(long i=0;if0; for(long i=0;iv?ab[i][j]:v; break; } + case SCALAR_ADD: { int32_t v=(int32_t)s->f0; for(long i=0;if0; for(long i=0;if0; for(long i=0;if0; for(long i=0;if0; for(long i=0;if0; for(long i=0;im[i]?ab[i][j]:m[i]; break; } + case PER_ROW_ADD: { const int32_t*m=(const int32_t*)s->f0; for(long i=0;if0; for(long i=0;if0; for(long i=0;if0; for(long i=0;if0; for(long i=0;if0; for(long i=0;im[j]?ab[i][j]:m[j]; break; } + case PER_COL_ADD: { const int32_t*m=(const int32_t*)s->f0; for(long i=0;if0; for(long i=0;if0; for(long i=0;if0; for(long i=0;i int32 GEMV kernel for tract's MMM framework (the qmmv_i32 slot, +// dispatched when N == 1: matrix x int8 column vector). +// +// Tile MR=64 x NR=1, i32 accumulator. The hot AddMatMul is the vector-length- +// agnostic widening update vectorized over M: per K-step it loads MR signed +// bytes of the A-panel column, sign-extends to i32 (svld1sb_s32), and folds a +// single svmla_n_s32 with the (sign-extended) scalar B[k]. The MR rows are +// walked in svcntw() chunks with whilelt predication, so the SAME code is +// correct and full-width at any SVE vector length (128..2048-bit). +// +// Same rationale as the 8x8 kernel: widening MLA (not SDOT) consumes tract's +// native K-major i8i8 packing directly. int8 inputs arrive via that packing +// (AddMatMul packing == 1); the default i32i32 packing (packing == 0) is handled +// scalar for the auto-test surface. +// +// ABI: identical 40-byte FusedKerSpec walk. At NR=1, per_col / scalar fuse +// ops degenerate to a single broadcast value and per_row is element-wise over +// the MR outputs. Quantization ops q_scale / q_shr / q_shl are ported bit-exact +// from linalg/src/generic/rounding.rs. Returns 0 on success, 1 on an +// unsupported fused op / packing. + +#include +#include +#include + +#define MR 64 +#define NR 1 + +enum { + DONE = 0, CLEAR, LOAD_TILE, + SCALAR_MIN, SCALAR_MAX, SCALAR_ADD, SCALAR_MUL, SCALAR_SUB, SCALAR_SUBF, + LEAKY_RELU, + PER_ROW_MIN, PER_ROW_MAX, PER_ROW_ADD, PER_ROW_MUL, PER_ROW_SUB, PER_ROW_SUBF, + PER_COL_MIN, PER_COL_MAX, PER_COL_ADD, PER_COL_MUL, PER_COL_SUB, PER_COL_SUBF, + Q_SCALE, Q_SHR, Q_SHL, + ADD_UNICAST, ADD_ROW_COL_PRODUCTS, STORE, ADD_MAT_MUL +}; + +enum { RP_NATIVE = 0, RP_ZERO, RP_AWAY, RP_MINUSINF, RP_PLUSINF, RP_EVEN, RP_ODD }; + +typedef struct { + uint64_t disc; + uint64_t f0, f1, f2, f3; +} spec_t; + +// AddMatMul, i8 x i8 -> i32 (packing 1): ab[m] += sum_k pa[k*MR+m]*pb[k]. +// VLA widening update over MR. +static void add_mat_mul_i8(int32_t ab[MR], const int8_t *pa, const int8_t *pb, long k) { + for (long m0 = 0; m0 < MR; m0 += svcntw()) { + svbool_t pg = svwhilelt_b32((uint64_t)m0, (uint64_t)MR); + svint32_t acc = svld1_s32(pg, &ab[m0]); + for (long kk = 0; kk < k; kk++) { + svint32_t a = svld1sb_s32(pg, &pa[kk * MR + m0]); // load i8 col, sign-extend + acc = svmla_n_s32_x(pg, acc, a, (int32_t)pb[kk]); + } + svst1_s32(pg, &ab[m0], acc); + } +} + +// AddMatMul, i32 x i32 -> i32 (packing 0, default): auto-test surface only. +static void add_mat_mul_i32(int32_t ab[MR], const int32_t *pa, const int32_t *pb, long k) { + for (long kk = 0; kk < k; kk++) { + int32_t b = pb[kk]; + const int32_t *acol = &pa[kk * MR]; + for (long m = 0; m < MR; m++) ab[m] += acol[m] * b; + } +} + +// ---- quantization helpers, ported bit-exact from generic/rounding.rs ---- + +static int32_t q_shr_i32(int32_t v, long shift, int rp) { + int32_t half = (int32_t)1 << (shift - 1); + int32_t a = v < 0 ? -v : v; + int32_t nudge; + switch (rp) { + case RP_ZERO: nudge = -1; break; + case RP_MINUSINF: nudge = -(int32_t)(v >= 0); break; + case RP_PLUSINF: nudge = -(int32_t)(v <= 0); break; + case RP_AWAY: nudge = 0; break; + case RP_EVEN: nudge = ((a >> shift) & 0x1) - 1; break; + case RP_ODD: nudge = -((a >> shift) & 0x1); break; + default: nudge = 0; break; + } + int32_t sign = (v > 0) - (v < 0); + return sign * ((a + half + nudge) >> shift); +} + +static int32_t q_scale_i32(int32_t v, long shift_in, int policy, int32_t mult) { + int64_t val = (int64_t)mult * (int64_t)v; + long shift = shift_in + 31; + if (shift > 0) { + int64_t half = (int64_t)1 << (shift - 1); + int64_t a = val < 0 ? -val : val; + int64_t nudge; + switch (policy) { + case RP_ZERO: nudge = -1; break; + case RP_MINUSINF: nudge = -(int64_t)(val >= 0); break; + case RP_PLUSINF: nudge = -(int64_t)(val <= 0); break; + case RP_AWAY: nudge = 0; break; + case RP_EVEN: nudge = ((a >> shift) & 0x1) - 1; break; + case RP_ODD: nudge = -((a >> shift) & 0x1); break; + default: nudge = 0; break; + } + int64_t sign = (val > 0) - (val < 0); + return (int32_t)(sign * ((a + half + nudge) >> shift)); + } else { + return (int32_t)(val << (-shift)); + } +} + +intptr_t sve_mmm_i32_64x1_kernel(const spec_t *ops) { + int32_t ab[MR]; + memset(ab, 0, sizeof(ab)); + for (const spec_t *s = ops;; s++) { + switch (s->disc) { + case DONE: + return 0; + case CLEAR: + memset(ab, 0, sizeof(ab)); + break; + case ADD_MAT_MUL: { + long k = (long)s->f0, packing = (long)s->f3; + if (packing == 1) + add_mat_mul_i8(ab, (const int8_t *)s->f1, (const int8_t *)s->f2, k); + else if (packing == 0) + add_mat_mul_i32(ab, (const int32_t *)s->f1, (const int32_t *)s->f2, k); + else + return 1; + break; + } + case STORE: { + uint8_t *ptr = (uint8_t *)s->f0; + long rstride = (long)s->f1, isz = (long)s->f3; + for (long m = 0; m < MR; m++) { + uint8_t *p = ptr + m * rstride; + int32_t v = ab[m]; + switch (isz) { + case 1: *(uint8_t *)p = (uint8_t)v; break; + case 2: *(uint16_t *)p = (uint16_t)v; break; + case 4: *(int32_t *)p = v; break; + case 8: { int64_t w = v; memcpy(p, &w, 8); break; } + default: memcpy(p, &v, isz < 4 ? (size_t)isz : 4); break; + } + } + break; + } + case LOAD_TILE: { + const int32_t *src = (const int32_t *)s->f1; // row-major MR values + for (long m = 0; m < MR; m++) ab[m] = src[m]; + break; + } + case ADD_UNICAST: { + uint8_t *ptr = (uint8_t *)s->f0; + long rstride = (long)s->f1, isz = (long)s->f3; + for (long m = 0; m < MR; m++) { + const uint8_t *p = ptr + m * rstride; + if (isz == 1) + ab[m] += *(const int8_t *)p; + else + ab[m] += *(const int32_t *)p; + } + break; + } + case ADD_ROW_COL_PRODUCTS: { + const int32_t *rows = (const int32_t *)s->f0; + const int32_t *cols = (const int32_t *)s->f1; + for (long m = 0; m < MR; m++) ab[m] += rows[m] * cols[0]; + break; + } + case Q_SCALE: { + long shift = (long)s->f0; int policy = (int)s->f1; int32_t mult = (int32_t)s->f2; + for (long m = 0; m < MR; m++) ab[m] = q_scale_i32(ab[m], shift, policy, mult); + break; + } + case Q_SHR: { + long shift = (long)s->f0; int policy = (int)s->f1; + for (long m = 0; m < MR; m++) ab[m] = q_shr_i32(ab[m], shift, policy); + break; + } + case Q_SHL: { + long shift = (long)s->f0; + for (long m = 0; m < MR; m++) ab[m] = ab[m] << shift; + break; + } + // scalar fuse ops (single i32 in low 32 bits of f0) + case SCALAR_MIN: { int32_t v=(int32_t)s->f0; for(long m=0;mf0; for(long m=0;mv?ab[m]:v; break; } + case SCALAR_ADD: { int32_t v=(int32_t)s->f0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mm_[m]?ab[m]:m_[m]; break; } + case PER_ROW_ADD: { const int32_t*m_=(const int32_t*)s->f0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mv?ab[m]:v; break; } + case PER_COL_ADD: { int32_t v=*(const int32_t*)s->f0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;m +#include +#include + +#define MR 64 +#define NR 1 + +enum { + DONE = 0, CLEAR, LOAD_TILE, + SCALAR_MIN, SCALAR_MAX, SCALAR_ADD, SCALAR_MUL, SCALAR_SUB, SCALAR_SUBF, + LEAKY_RELU, + PER_ROW_MIN, PER_ROW_MAX, PER_ROW_ADD, PER_ROW_MUL, PER_ROW_SUB, PER_ROW_SUBF, + PER_COL_MIN, PER_COL_MAX, PER_COL_ADD, PER_COL_MUL, PER_COL_SUB, PER_COL_SUBF, + Q_SCALE, Q_SHR, Q_SHL, + ADD_UNICAST, ADD_ROW_COL_PRODUCTS, STORE, ADD_MAT_MUL +}; + +typedef struct { + uint64_t disc; + uint64_t f0, f1, f2, f3; +} spec_t; + +static inline __fp16 f16_of(uint64_t bits) { + __fp16 f; + uint16_t lo = (uint16_t)bits; + memcpy(&f, &lo, 2); + return f; +} + +// AddMatMul: ab[m] += sum_k pa[k*MR+m] * pb[k]. VLA over MR (f16 lanes). +static void add_mat_mul(__fp16 ab[MR], const __fp16 *pa, const __fp16 *pb, long k) { + for (long m0 = 0; m0 < MR; m0 += svcnth()) { + svbool_t pg = svwhilelt_b16((uint64_t)m0, (uint64_t)MR); + svfloat16_t acc = svld1_f16(pg, &ab[m0]); + for (long kk = 0; kk < k; kk++) { + svfloat16_t a = svld1_f16(pg, &pa[kk * MR + m0]); + acc = svmla_n_f16_x(pg, acc, a, pb[kk]); + } + svst1_f16(pg, &ab[m0], acc); + } +} + +intptr_t sve_mmv_f16_64x1_kernel(const spec_t *ops) { + __fp16 ab[MR]; + memset(ab, 0, sizeof(ab)); + for (const spec_t *s = ops;; s++) { + switch (s->disc) { + case DONE: + return 0; + case CLEAR: + memset(ab, 0, sizeof(ab)); + break; + case ADD_MAT_MUL: { + long k = (long)s->f0; + add_mat_mul(ab, (const __fp16 *)s->f1, (const __fp16 *)s->f2, k); + break; + } + case STORE: { + uint8_t *ptr = (uint8_t *)s->f0; + long rstride = (long)s->f1, isz = (long)s->f3; + for (long m = 0; m < MR; m++) { + uint8_t *p = ptr + m * rstride; + if (isz == 2) + *(__fp16 *)p = ab[m]; + else if (isz == 4) + *(float *)p = (float)ab[m]; + else + memcpy(p, &ab[m], isz); + } + break; + } + case LOAD_TILE: { + const __fp16 *src = (const __fp16 *)s->f1; + for (long m = 0; m < MR; m++) ab[m] = src[m]; + break; + } + case ADD_UNICAST: { + uint8_t *ptr = (uint8_t *)s->f0; + long rstride = (long)s->f1, isz = (long)s->f3; + for (long m = 0; m < MR; m++) { + const uint8_t *p = ptr + m * rstride; + if (isz == 2) + ab[m] += *(const __fp16 *)p; + else + ab[m] += (__fp16) * (const float *)p; + } + break; + } + case ADD_ROW_COL_PRODUCTS: { + const __fp16 *rows = (const __fp16 *)s->f0; + const __fp16 *cols = (const __fp16 *)s->f1; + for (long m = 0; m < MR; m++) ab[m] += rows[m] * cols[0]; + break; + } + case SCALAR_MIN: { __fp16 v=f16_of(s->f0); for(long m=0;mf0); for(long m=0;mv?ab[m]:v; break; } + case SCALAR_ADD: { __fp16 v=f16_of(s->f0); for(long m=0;mf0); for(long m=0;mf0); for(long m=0;mf0); for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mm_[m]?ab[m]:m_[m]; break; } + case PER_ROW_ADD: { const __fp16*m_=(const __fp16*)s->f0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mv?ab[m]:v; break; } + case PER_COL_ADD: { __fp16 v=*(const __fp16*)s->f0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;m ABI +// and fuse-op surface. At NR=1, per_col / scalar fuse ops degenerate to a single +// broadcast value and per_row is element-wise over the MR outputs. As with the +// f32 GEMM kernel, LeakyRelu and the i32 quantization ops are excluded by +// CAN_FUSE. Returns 0 on success, 1 on an unsupported fused op. + +#include +#include +#include + +#define MR 64 +#define NR 1 + +enum { + DONE = 0, CLEAR, LOAD_TILE, + SCALAR_MIN, SCALAR_MAX, SCALAR_ADD, SCALAR_MUL, SCALAR_SUB, SCALAR_SUBF, + LEAKY_RELU, + PER_ROW_MIN, PER_ROW_MAX, PER_ROW_ADD, PER_ROW_MUL, PER_ROW_SUB, PER_ROW_SUBF, + PER_COL_MIN, PER_COL_MAX, PER_COL_ADD, PER_COL_MUL, PER_COL_SUB, PER_COL_SUBF, + Q_SCALE, Q_SHR, Q_SHL, + ADD_UNICAST, ADD_ROW_COL_PRODUCTS, STORE, ADD_MAT_MUL +}; + +typedef struct { + uint64_t disc; + uint64_t f0, f1, f2, f3; +} spec_t; + +static inline float f32_of(uint64_t bits) { + float f; + uint32_t lo = (uint32_t)bits; + memcpy(&f, &lo, 4); + return f; +} + +// AddMatMul: ab[m] += sum_k pa[k*MR+m] * pb[k]. VLA over MR. +static void add_mat_mul(float ab[MR], const float *pa, const float *pb, long k) { + for (long m0 = 0; m0 < MR; m0 += svcntw()) { + svbool_t pg = svwhilelt_b32((uint64_t)m0, (uint64_t)MR); + svfloat32_t acc = svld1_f32(pg, &ab[m0]); + for (long kk = 0; kk < k; kk++) { + svfloat32_t a = svld1_f32(pg, &pa[kk * MR + m0]); + acc = svmla_n_f32_x(pg, acc, a, pb[kk]); + } + svst1_f32(pg, &ab[m0], acc); + } +} + +intptr_t sve_mmv_f32_64x1_kernel(const spec_t *ops) { + float ab[MR]; + memset(ab, 0, sizeof(ab)); + for (const spec_t *s = ops;; s++) { + switch (s->disc) { + case DONE: + return 0; + case CLEAR: + memset(ab, 0, sizeof(ab)); + break; + case ADD_MAT_MUL: { + long k = (long)s->f0; + add_mat_mul(ab, (const float *)s->f1, (const float *)s->f2, k); + break; + } + case STORE: { + uint8_t *ptr = (uint8_t *)s->f0; + long rstride = (long)s->f1, isz = (long)s->f3; + for (long m = 0; m < MR; m++) { + uint8_t *p = ptr + m * rstride; + if (isz == 4) + *(float *)p = ab[m]; + else + memcpy(p, &ab[m], isz); + } + break; + } + case LOAD_TILE: { + const float *src = (const float *)s->f1; // row-major MR values + for (long m = 0; m < MR; m++) ab[m] = src[m]; + break; + } + case ADD_UNICAST: { + uint8_t *ptr = (uint8_t *)s->f0; + long rstride = (long)s->f1; + for (long m = 0; m < MR; m++) ab[m] += *(const float *)(ptr + m * rstride); + break; + } + case ADD_ROW_COL_PRODUCTS: { + const float *rows = (const float *)s->f0; + const float *cols = (const float *)s->f1; + for (long m = 0; m < MR; m++) ab[m] += rows[m] * cols[0]; + break; + } + // scalar fuse ops (f32 bits in low 32 bits of f0) + case SCALAR_MIN: { float v=f32_of(s->f0); for(long m=0;mf0); for(long m=0;mv?ab[m]:v; break; } + case SCALAR_ADD: { float v=f32_of(s->f0); for(long m=0;mf0); for(long m=0;mf0); for(long m=0;mf0); for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mm_[m]?ab[m]:m_[m]; break; } + case PER_ROW_ADD: { const float*m_=(const float*)s->f0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mv?ab[m]:v; break; } + case PER_COL_ADD: { float v=*(const float*)s->f0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;mf0; for(long m=0;m Tensor { + let mut t = unsafe { Tensor::uninitialized_aligned::(&[N], 64).unwrap() }; + let s = unsafe { t.as_slice_mut_unchecked::() }; + for (i, x) in s.iter_mut().enumerate() { + *x = (i as f32 / 10.0).sin() * 5.0; + } + t +} + +// Enable FTZ/DAZ (flush-to-zero, denormals-are-zero) for the whole process so +// repeated in-place application of a kernel to its own output cannot collapse +// into denormal arithmetic (extremely slow on x86) and distort the timing. +// Mirrors what the sigmoid/tanh kernels already do internally via MXCSR. +#[cfg(target_arch = "x86_64")] +fn enable_ftz_daz() { + // Set MXCSR bit 15 (FTZ) and bit 6 (DAZ) directly; the safe intrinsic + // wrappers are deprecated in favour of inline asm. + unsafe { + let mut mxcsr: u32 = 0; + std::arch::asm!("stmxcsr [{p}]", p = in(reg) &mut mxcsr); + mxcsr |= (1 << 15) | (1 << 6); + std::arch::asm!("ldmxcsr [{p}]", p = in(reg) &mxcsr); + } +} + +// In-place throughput, matching the convention of the existing element-wise +// benches (sigmoid.rs / silu.rs). +macro_rules! bench_pair { + ($c:expr, $name:expr, $pred_label:expr, $pred:ty, $avx512:ty $(, $param:expr)?) => {{ + let mut group = $c.benchmark_group($name); + group.throughput(Throughput::Elements(N as u64)); + let mut tp = aligned_input(); + let sp = unsafe { tp.as_slice_mut_unchecked::() }; + group.bench_function($pred_label, |b| { + b.iter(|| <$pred>::run(sp, ($($param)?))) + }); + if std::is_x86_feature_detected!("avx512f") { + let mut ta = aligned_input(); + let sa = unsafe { ta.as_slice_mut_unchecked::() }; + group.bench_function("avx512", |b| { + b.iter(|| <$avx512>::run(sa, ($($param)?))) + }); + } + group.finish(); + }}; +} + +fn benches(c: &mut Criterion) { + #[cfg(target_arch = "x86_64")] + enable_ftz_daz(); + use tract_linalg::x86_64_fma::act::*; + use tract_linalg::x86_64_fma::{ + avx512_sigmoid_f32, avx512_tanh_f32, fma_sigmoid_f32, fma_tanh_f32, + }; + + bench_pair!(c, "sigmoid_f32", "fma", fma_sigmoid_f32, avx512_sigmoid_f32); + bench_pair!(c, "tanh_f32", "fma", fma_tanh_f32, avx512_tanh_f32); + bench_pair!( + c, + "hardswish_f32", + "generic", + tract_linalg::generic::SHardSwish4, + x86_64_avx512_hardswish_f32_64n + ); + bench_pair!( + c, + "leaky_relu_f32", + "generic", + tract_linalg::generic::SLeakyRelu4, + x86_64_avx512_leaky_relu_f32_64n, + 0.1f32 + ); + bench_pair!( + c, + "silu_f32", + "generic", + tract_linalg::generic::SSiLU4, + x86_64_avx512_silu_f32_16n + ); + bench_pair!( + c, + "gelu_f32", + "generic", + tract_linalg::generic::SGelu4, + x86_64_avx512_gelu_f32_16n + ); +} + +criterion_group!(g, benches); +criterion_main!(g); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/activations_avx512_f16.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/activations_avx512_f16.rs new file mode 100644 index 0000000..a73a735 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/activations_avx512_f16.rs @@ -0,0 +1,83 @@ +// Microbenchmark: AVX-512 f16 element-wise activations vs the generic scalar +// f16 kernels (no FMA f16 predecessor exists on x86 — the generic baseline +// already runs `(*v - max).to_f32()`-style per-element conversions). Buffers +// are 64-byte aligned (alignment that the AVX-512 path uses internally) and +// a multiple of 64 elements. + +use criterion::*; +use tract_data::prelude::*; +use tract_linalg::element_wise::ElementWiseKer; + +const N: usize = 1024; + +fn aligned_input() -> Tensor { + let mut t = unsafe { Tensor::uninitialized_aligned::(&[N], 64).unwrap() }; + let s = unsafe { t.as_slice_mut_unchecked::() }; + for (i, x) in s.iter_mut().enumerate() { + *x = f16::from_f32((i as f32 / 10.0).sin() * 5.0); + } + t +} + +macro_rules! bench_pair { + ($c:expr, $name:expr, $pred:ty, $avx512:ty $(, $param:expr)?) => {{ + let mut group = $c.benchmark_group($name); + group.throughput(Throughput::Elements(N as u64)); + let mut tp = aligned_input(); + let sp = unsafe { tp.as_slice_mut_unchecked::() }; + group.bench_function("generic", |b| { + b.iter(|| <$pred>::run(sp, ($($param)?))) + }); + if std::is_x86_feature_detected!("avx512f") { + let mut ta = aligned_input(); + let sa = unsafe { ta.as_slice_mut_unchecked::() }; + group.bench_function("avx512", |b| { + b.iter(|| <$avx512>::run(sa, ($($param)?))) + }); + } + group.finish(); + }}; +} + +fn benches(c: &mut Criterion) { + bench_pair!( + c, + "sigmoid_f16", + tract_linalg::generic::sigmoid::HSigmoid8, + tract_linalg::x86_64_fma::act_f16::x86_64_avx512_sigmoid_f16_16n + ); + bench_pair!( + c, + "tanh_f16", + tract_linalg::generic::tanh::HTanh8, + tract_linalg::x86_64_fma::act_f16::x86_64_avx512_tanh_f16_16n + ); + bench_pair!( + c, + "hardswish_f16", + tract_linalg::generic::hardswish::HHardSwish8, + tract_linalg::x86_64_fma::act_f16::x86_64_avx512_hardswish_f16_64n + ); + bench_pair!( + c, + "leaky_relu_f16", + tract_linalg::generic::leaky_relu::HLeakyRelu8, + tract_linalg::x86_64_fma::act_f16::x86_64_avx512_leaky_relu_f16_64n, + f16::from_f32(0.1) + ); + bench_pair!( + c, + "silu_f16", + tract_linalg::generic::silu::HSiLU8, + tract_linalg::x86_64_fma::act_f16::x86_64_avx512_silu_f16_16n + ); + bench_pair!( + c, + "gelu_f16", + tract_linalg::generic::gelu::HGelu8, + tract_linalg::x86_64_fma::act_f16::x86_64_avx512_gelu_f16_16n + ); +} + +criterion_group!(g, benches); +criterion_main!(g); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/activations_avx512_fp16.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/activations_avx512_fp16.rs new file mode 100644 index 0000000..c754b79 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/activations_avx512_fp16.rs @@ -0,0 +1,68 @@ +// Microbench: AVX-512_FP16 native f16 element-wise activations vs the +// f32-roundtrip versions in `act_f16.rs` (which were the AVX-512 f16 path +// before native f16 ISA was available). Both run on 64-byte-aligned, 1024- +// element buffers — same workload as the existing activations_avx512_f16 +// bench, just adding the native-fp16 column. + +use criterion::*; +use tract_data::prelude::*; +use tract_linalg::element_wise::ElementWiseKer; + +const N: usize = 1024; + +fn aligned_input() -> Tensor { + let mut t = unsafe { Tensor::uninitialized_aligned::(&[N], 64).unwrap() }; + let s = unsafe { t.as_slice_mut_unchecked::() }; + for (i, x) in s.iter_mut().enumerate() { + *x = f16::from_f32((i as f32 / 10.0).sin() * 5.0); + } + t +} + +macro_rules! bench_triple { + ($c:expr, $name:expr, $pred:ty, $roundtrip:ty, $native:ty $(, $param:expr)?) => {{ + let mut group = $c.benchmark_group($name); + group.throughput(Throughput::Elements(N as u64)); + let mut tg = aligned_input(); + let sg = unsafe { tg.as_slice_mut_unchecked::() }; + group.bench_function("generic", |b| { + b.iter(|| <$pred>::run(sg, ($($param)?))) + }); + if std::is_x86_feature_detected!("avx512f") { + let mut tr = aligned_input(); + let sr = unsafe { tr.as_slice_mut_unchecked::() }; + group.bench_function("avx512_f32roundtrip", |b| { + b.iter(|| <$roundtrip>::run(sr, ($($param)?))) + }); + } + if std::is_x86_feature_detected!("avx512fp16") { + let mut tn = aligned_input(); + let sn = unsafe { tn.as_slice_mut_unchecked::() }; + group.bench_function("avx512fp16_native", |b| { + b.iter(|| <$native>::run(sn, ($($param)?))) + }); + } + group.finish(); + }}; +} + +fn benches(c: &mut Criterion) { + bench_triple!( + c, + "hardswish_f16", + tract_linalg::generic::hardswish::HHardSwish8, + tract_linalg::x86_64_fma::act_f16::x86_64_avx512_hardswish_f16_64n, + tract_linalg::x86_64_fma::act_f16_fp16::x86_64_avx512fp16_hardswish_f16_128n + ); + bench_triple!( + c, + "leaky_relu_f16", + tract_linalg::generic::leaky_relu::HLeakyRelu8, + tract_linalg::x86_64_fma::act_f16::x86_64_avx512_leaky_relu_f16_64n, + tract_linalg::x86_64_fma::act_f16_fp16::x86_64_avx512fp16_leaky_relu_f16_128n, + f16::from_f32(0.1) + ); +} + +criterion_group!(g, benches); +criterion_main!(g); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/arm32neon.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/arm32neon.rs new file mode 100644 index 0000000..815eb9c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/arm32neon.rs @@ -0,0 +1,191 @@ +#![feature(asm)] +#![allow( + dead_code, + non_upper_case_globals, + unused_macros, + non_snake_case, + unused_assignments +)] + +use std::time::Instant; + +macro_rules! r2 { ($($stat:stmt)*) => { $( $stat )* $( $stat )* } } +macro_rules! r4 { ($($stat:stmt)*) => { r2!(r2!($($stat)*)) }} +macro_rules! r8 { ($($stat:stmt)*) => { r4!(r2!($($stat)*)) }} +macro_rules! r16 { ($($stat:stmt)*) => { r4!(r4!($($stat)*)) }} +macro_rules! r32 { ($($stat:stmt)*) => { r8!(r4!($($stat)*)) }} +macro_rules! r64 { ($($stat:stmt)*) => { r8!(r8!($($stat)*)) }} +macro_rules! r128 { ($($stat:stmt)*) => { r8!(r16!($($stat)*)) }} +macro_rules! r1024 { ($($stat:stmt)*) => { r8!(r128!($($stat)*)) }} +macro_rules! r4096 { ($($stat:stmt)*) => { r4!(r1024!($($stat)*)) }} + +const _F32: [f32; 1024] = [12.; 1024]; +const F32: *const f32 = _F32.as_ptr(); + +/* +fn ruin_cache() { +let _a = (0..1000000).collect::>(); +} +*/ + +macro_rules! b { + ($f: block, $inner_loop: expr, $measures: expr) => {{ + let mut values = Vec::with_capacity($measures); + for _ in 0..$measures { + // ruin_cache(); + let start = Instant::now(); + for _ in 0..$inner_loop { + unsafe { $f }; + } + values.push(start.elapsed()); + } + values.sort(); + values[$measures / 2].as_nanos() as f64 / 1e9 / $inner_loop as f64 + }}; +} + +fn main() { + let cycle = b!( + { + r1024!(asm!("orr r0, r0, r0", out("r0") _)); + }, + 1000, + 1000 + ) / 1024.; + let indep_fmla = b!( + { + r8!(asm!(" + vmla.f32 q0, q0, q0 + vmla.f32 q1, q1, q1 + vmla.f32 q2, q2, q2 + vmla.f32 q3, q3, q3 + vmla.f32 q4, q4, q4 + vmla.f32 q5, q5, q5 + vmla.f32 q6, q6, q6 + vmla.f32 q7, q7, q7 + ", out("q0") _, out("q1") _, out("q2") _, out("q3") _, out("q4") _, out("q5") _, out("q6") _, out("q7") _)); + }, + 1000, + 1000 + ) / 64.; + eprintln!("rcp tp: indep fmla: {}", indep_fmla / cycle); + let dep_accu_fmla = b!( + { + r16!(asm!(" + vmla.f32 q15, q0, q0 + vmla.f32 q15, q1, q1 + vmla.f32 q15, q2, q2 + vmla.f32 q15, q3, q3 + vmla.f32 q15, q4, q4 + vmla.f32 q15, q5, q5 + vmla.f32 q15, q6, q6 + vmla.f32 q15, q7, q7 + vmla.f32 q15, q8, q8 + vmla.f32 q15, q9, q9 + vmla.f32 q15, q10, q10 + vmla.f32 q15, q11, q11 + vmla.f32 q15, q12, q12 + vmla.f32 q15, q13, q13 + vmla.f32 q15, q14, q14 + ", out("q0") _, out("q1") _, out("q2") _, out("q3") _, out("q4") _, out("q5") _, out("q6") _, out("q7") _, + out("q8") _, out("q9") _, out("q10") _, out("q11") _, out("q12") _, out("q13") _, out("q14") _, out("q15") _)); + }, + 1000, + 1000 + ) / 16. + / 15.; + eprintln!("rcp tp: accu-dep fmla: {}", dep_accu_fmla / cycle); + let load_s_using_vld1_64 = b!( + { + let mut p = F32; + r16!(asm!(" + vld1.64 {{d0-d3}}, [{0}]! + vld1.64 {{d4-d7}}, [{0}]! + vld1.64 {{d8-d11}}, [{0}]! + vld1.64 {{d12-d15}}, [{0}]! + vld1.64 {{d16-d19}}, [{0}]! + vld1.64 {{d20-d23}}, [{0}]! + vld1.64 {{d24-d27}}, [{0}]! + vld1.64 {{d28-d31}}, [{0}]! + ", + inout(reg) p, + out("q0") _, out("q1") _, out("q2") _, out("q3") _, out("q4") _, out("q5") _, out("q6") _, out("q7") _, + out("q8") _, out("q9") _, out("q10") _, out("q11") _, out("q12") _, out("q13") _, out("q14") _, out("q15") _)); + }, + 1000, + 1000 + ) / 16. + / 64.; // each line load 8 s + eprintln!( + "rcp tp: load s using vld1_64 ia {}", + load_s_using_vld1_64 / cycle + ); + let load_s_using_vldm_q = b!( + { + let mut p = F32; + r16!(asm!(" + vldm {0}!, {{q0-q3}} + vldm {0}!, {{q4-q7}} + vldm {0}!, {{q8-q11}} + vldm {0}!, {{q12-q15}} + ", + inout(reg) p, + out("q0") _, out("q1") _, out("q2") _, out("q3") _, out("q4") _, out("q5") _, out("q6") _, out("q7") _, + out("q8") _, out("q9") _, out("q10") _, out("q11") _, out("q12") _, out("q13") _, out("q14") _, out("q15") _)); + }, + 1000, + 1000 + ) / 16. + / 64.; + eprintln!( + "rcp tp: load s using vldmia q: {}", + load_s_using_vldm_q / cycle + ); + let load = b!( + { + let mut p = F32; + r16!(asm!(" + vldr.64 d0, [{0}] + vldr.64 d1, [{0}, #8] + vldr.64 d2, [{0}, #16] + vldr.64 d3, [{0}, #24] + vldr.64 d4, [{0}, #32] + vldr.64 d5, [{0}, #40] + vldr.64 d6, [{0}, #48] + vldr.64 d7, [{0}, #56] + vldr.64 d8, [{0}, #64] + vldr.64 d9, [{0}, #72] + vldr.64 d10, [{0}, #80] + vldr.64 d11, [{0}, #88] + vldr.64 d12, [{0}, #96] + vldr.64 d13, [{0}, #104] + vldr.64 d14, [{0}, #112] + vldr.64 d15, [{0}, #120] + vldr.64 d16, [{0}, #128] + vldr.64 d17, [{0}, #136] + vldr.64 d18, [{0}, #144] + vldr.64 d19, [{0}, #152] + vldr.64 d20, [{0}, #160] + vldr.64 d21, [{0}, #168] + vldr.64 d22, [{0}, #176] + vldr.64 d23, [{0}, #184] + vldr.64 d24, [{0}, #192] + vldr.64 d25, [{0}, #200] + vldr.64 d26, [{0}, #208] + vldr.64 d27, [{0}, #216] + vldr.64 d28, [{0}, #224] + vldr.64 d29, [{0}, #232] + vldr.64 d30, [{0}, #240] + vldr.64 d31, [{0}, #248] + add {0}, #256 + ", + inout(reg) p, + out("q0") _, out("q1") _, out("q2") _, out("q3") _, out("q4") _, out("q5") _, out("q6") _, out("q7") _, + out("q8") _, out("q9") _, out("q10") _, out("q11") _, out("q12") _, out("q13") _, out("q14") _, out("q15") _)); + }, + 1000, + 1000 + ) / 16. + / 64.; + eprintln!("rcp tp: load s using vldr d + imm: {}", load / cycle); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/arm64.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/arm64.rs new file mode 100644 index 0000000..1518ecb --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/arm64.rs @@ -0,0 +1,87 @@ +use std::time::Instant; + +use tract_data::prelude::*; +use tract_linalg::LADatum; +use tract_linalg::frame::mmm::FusedSpec; +use tract_linalg::frame::mmm::MatMatMulKer; + +fn ruin_cache() { + let _a = (0..1000000).collect::>(); +} + +fn bench_to_nanos>( + k: usize, + loops: usize, +) -> f64 { + let item_size = T::datum_type().size_of(); + let a = Tensor::zero_aligned::( + &[(k + K::end_padding_packed_a()) * K::mr()], + K::alignment_bytes_packed_a(), + ) + .unwrap(); + let b = Tensor::zero_aligned::( + &[(k + K::end_padding_packed_b()) * K::nr()], + K::alignment_bytes_packed_b(), + ) + .unwrap(); + let mut c = Tensor::zero::(&[K::mr() * K::nr()]).unwrap(); + let ref a = InputStoreKer::Packed { + ptr: unsafe { a.as_ptr_unchecked::() as _ }, + }; + let ref b = InputStoreKer::Packed { + ptr: unsafe { b.as_ptr_unchecked::() as _ }, + }; + let ref c = OutputStoreKer { + ptr: unsafe { c.as_ptr_mut_unchecked::() as _ }, + item_size, + col_byte_stride: (item_size * K::mr()) as isize, + row_byte_stride: item_size as isize, + }; + let ref linear = LinearSpec::Mul { k }; + let op = MatMatMulKerSpec { + a, + b, + c, + linear, + non_linear: std::ptr::null(), + }; + let mut values = Vec::with_capacity(loops); + for _ in 0..loops { + ruin_cache(); + let start = Instant::now(); + K::kernel(&op); + values.push(start.elapsed()); + } + values.sort(); + values[loops / 2].as_nanos() as f64 +} + +fn model>() -> (f64, f64) { + let x = 1000; + let zp = bench_to_nanos::(0, 10000); + let y = bench_to_nanos::(x, 1000); + let slope = (y - zp) / x as f64; + (slope, zp) +} + +fn as_match_line>() { + let coeffs = model::(); + println!( + "({:?}, {}, {}) => {} * k + {},", + K::name(), + K::mr(), + K::nr(), + (coeffs.0 * 1000.).round(), + (coeffs.1 * 1000.).round() + ); +} + +fn main() { + use tract_linalg::arm64::*; + as_match_line::(); + as_match_line::(); + as_match_line::(); + as_match_line::(); + as_match_line::(); + as_match_line::(); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/arm64simd.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/arm64simd.rs new file mode 100644 index 0000000..4bd67b9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/arm64simd.rs @@ -0,0 +1,1106 @@ +#![allow( + dead_code, + non_upper_case_globals, + unused_macros, + non_snake_case, + unused_assignments +)] + +use std::arch::asm; + +// mod nano; + +#[repr(C, align(8))] +struct Floats([f32; 4096]); +const _F32: Floats = Floats([12.; 4096]); +const F32: *const f32 = (&_F32) as *const Floats as *const f32; + +lazy_static::lazy_static! { + static ref TICK: f64 = unsafe { b8192!(asm!("orr x20, x20, x20", out("x20") _)) }; +} + +pub unsafe fn armv8(filter: Option<&str>) { + macro_rules! s32 { + ($label: literal, $n: expr, $stmt:block) => { + if $label.contains(filter.unwrap_or("")) { + println!("{:40} {:.2}", $label, b32!($stmt) / $n as f64 / *TICK); + } + }; + } + + macro_rules! s128 { + ($label: literal, $n: expr, $stmt:block) => { + if $label.contains(filter.unwrap_or("")) { + println!("{:40} {:.2}", $label, b128!($stmt) / $n as f64 / *TICK); + } + }; + } + + macro_rules! s1024 { + ($label: literal, $n: expr, $stmt:block) => { + if $label.contains(filter.unwrap_or("")) { + println!("{:40} {:.2}", $label, b1024!($stmt) / $n as f64 / *TICK); + } + }; + } + + macro_rules! s8192 { + ($label: literal, $n: expr, $stmt:block) => { + if $label.contains(filter.unwrap_or("")) { + println!("{:40} {:.2}", $label, b8192!($stmt) / $n as f64 / *TICK); + } + }; + } + + s128!("nop", 1, { asm!("nop") }); + s128!("vands", 4, { + asm!(" and v0.16b, v1.16b, v1.16b + and v2.16b, v3.16b, v3.16b + and v4.16b, v5.16b, v5.16b + and v6.16b, v7.16b, v7.16b ", + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + ) + }); + s128!("fmax", 4, { + asm!(" fmax v0.4s, v1.4s, v1.4s + fmax v2.4s, v3.4s, v3.4s + fmax v4.4s, v5.4s, v5.4s + fmax v6.4s, v7.4s, v7.4s ", + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + ) + }); + s128!("fmax_with_dep", 1, { + asm!("fmax v0.4s, v0.4s, v0.4s", out("v0") _) + }); + s128!("fmla", 16, { + asm!(" fmla v0.4s, v0.4s, v0.4s + fmla v1.4s, v1.4s, v1.4s + fmla v2.4s, v2.4s, v2.4s + fmla v3.4s, v3.4s, v3.4s + fmla v4.4s, v4.4s, v4.4s + fmla v5.4s, v5.4s, v5.4s + fmla v6.4s, v6.4s, v6.4s + fmla v7.4s, v7.4s, v7.4s + fmla v8.4s, v8.4s, v8.4s + fmla v9.4s, v9.4s, v9.4s + fmla v10.4s,v10.4s,v10.4s + fmla v11.4s,v11.4s,v11.4s + fmla v12.4s,v12.4s,v12.4s + fmla v13.4s,v13.4s,v13.4s + fmla v14.4s,v14.4s,v14.4s + fmla v15.4s,v15.4s,v15.4s ", + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + ) + }); + + s128!("fmla_with_dep", 1, { + asm!("fmla v0.4s, v0.4s, v0.4s", out("v0") _) + }); + s32!("w_load", 64, { + let mut p = F32; + r8!(asm!("ldr w20, [{0}] + ldr w21, [{0}] + ldr w22, [{0}] + ldr w23, [{0}] + ldr w24, [{0}] + ldr w25, [{0}] + ldr w26, [{0}] + ldr w27, [{0}]", + inout(reg) p, + out("x20") _, out("x21") _, out("x22") _, out("x23") _, + out("x24") _, out("x25") _, out("x26") _, out("x27") _, + )); + }); + s32!("x_load", 64, { + let mut p = F32; + r8!(asm!(" + ldr x20, [{0}] + ldr x21, [{0}] + ldr x22, [{0}] + ldr x23, [{0}] + ldr x24, [{0}] + ldr x25, [{0}] + ldr x26, [{0}] + ldr x27, [{0}] + ", + inout(reg) p, + out("x20") _, out("x21") _, out("x22") _, out("x23") _, + out("x24") _, out("x25") _, out("x26") _, out("x27") _, + )); + }); + s32!("d_load", 64, { + let mut p = F32; + r8!(asm!(" + ldr d20, [{0}] + ldr d21, [{0}] + ldr d22, [{0}] + ldr d23, [{0}] + ldr d24, [{0}] + ldr d25, [{0}] + ldr d26, [{0}] + ldr d27, [{0}] + ", + inout(reg) p, + out("v20") _, out("v21") _, out("v22") _, out("v23") _, + out("v24") _, out("v25") _, out("v26") _, out("v27") _, + )); + }); + s32!("s_load", 64, { + let mut p = F32; + r8!(asm!(" + ld1 {{v20.s}}[0], [{0}] + ld1 {{v21.s}}[0], [{0}] + ld1 {{v22.s}}[0], [{0}] + ld1 {{v23.s}}[0], [{0}] + ld1 {{v24.s}}[0], [{0}] + ld1 {{v25.s}}[0], [{0}] + ld1 {{v26.s}}[0], [{0}] + ld1 {{v27.s}}[0], [{0}] + ", + inout(reg) p, + out("v20") _, out("v21") _, out("v22") _, out("v23") _, + out("v24") _, out("v25") _, out("v26") _, out("v27") _, + )); + }); + s32!("d_load_as_v", 64, { + let mut p = F32; + r8!(asm!(" + ld1 {{v20.d}}[0], [{0}] + ld1 {{v21.d}}[0], [{0}] + ld1 {{v22.d}}[0], [{0}] + ld1 {{v23.d}}[0], [{0}] + ld1 {{v24.d}}[0], [{0}] + ld1 {{v25.d}}[0], [{0}] + ld1 {{v26.d}}[0], [{0}] + ld1 {{v27.d}}[0], [{0}] + ", + inout(reg) p, + out("v20") _, out("v21") _, out("v22") _, out("v23") _, + out("v24") _, out("v25") _, out("v26") _, out("v27") _, + )); + }); + s32!("v_load", 64, { + let mut p = F32; + r8!(asm!(" + ld1 {{v20.4s}}, [{0}] + ld1 {{v21.4s}}, [{0}] + ld1 {{v22.4s}}, [{0}] + ld1 {{v23.4s}}, [{0}] + ld1 {{v24.4s}}, [{0}] + ld1 {{v25.4s}}, [{0}] + ld1 {{v26.4s}}, [{0}] + ld1 {{v27.4s}}, [{0}] + ", + inout(reg) p, + out("v20") _, out("v21") _, out("v22") _, out("v23") _, + out("v24") _, out("v25") _, out("v26") _, out("v27") _, + )); + }); + s32!("v2_load", 64, { + let mut p = F32; + r8!(asm!(" + ld1 {{v0.4s, v1.4s}}, [{0}] + ld1 {{v2.4s, v3.4s}}, [{0}] + ld1 {{v4.4s, v5.4s}}, [{0}] + ld1 {{v6.4s, v7.4s}}, [{0}] + ld1 {{v8.4s, v9.4s}}, [{0}] + ld1 {{v10.4s, v11.4s}}, [{0}] + ld1 {{v12.4s, v13.4s}}, [{0}] + ld1 {{v14.4s, v15.4s}}, [{0}] + ", + inout(reg) p, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("v3_load", 32, { + let mut p = F32; + r8!(asm!(" + ld1 {{v0.4s, v1.4s, v2.4s}}, [{0}] + ld1 {{v3.4s, v4.4s, v5.4s}}, [{0}] + ld1 {{v6.4s, v7.4s, v8.4s}}, [{0}] + ld1 {{v9.4s, v10.4s, v11.4s}}, [{0}] + ", + inout(reg) p, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + )); + }); + s32!("v4_load", 32, { + let mut p = F32; + r8!(asm!(" + ld1 {{v0.4s, v1.4s, v2.4s, v3.4s}}, [{0}] + ld1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{0}] + ld1 {{v8.4s, v9.4s, v10.4s, v11.4s}}, [{0}] + ld1 {{v12.4s, v13.4s, v14.4s, v15.4s}}, [{0}] + ", + inout(reg) p, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("ins_32b", 64, { + r8!(asm!(" + ins v8.s[0], w20 + ins v9.s[0], w20 + ins v10.s[0], w20 + ins v11.s[0], w20 + ins v12.s[0], w20 + ins v13.s[0], w20 + ins v14.s[0], w20 + ins v15.s[0], w20 + ", + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("ins_32b_same_lane", 128, { + r8!(asm!(" + ins v0.s[0], w20 + ins v1.s[0], w20 + ins v4.s[0], w20 + ins v5.s[0], w20 + ins v0.s[1], w20 + ins v1.s[1], w20 + ins v4.s[1], w20 + ins v5.s[1], w20 + ins v0.s[2], w20 + ins v1.s[2], w20 + ins v4.s[2], w20 + ins v5.s[2], w20 + ins v0.s[3], w20 + ins v1.s[3], w20 + ins v4.s[3], w20 + ins v5.s[3], w20 + ", + out("v0") _, out("v1") _, out("v4") _, out("v5") _, + )); + }); + s32!("ins_64b", 64, { + r8!(asm!(" + ins v8.d[0], x20 + ins v9.d[0], x20 + ins v10.d[0], x20 + ins v11.d[0], x20 + ins v12.d[0], x20 + ins v13.d[0], x20 + ins v14.d[0], x20 + ins v15.d[0], x20 + ", + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("ins_64b_same_v", 64, { + r8!(asm!(" + ins v8.d[0], x20 + ins v8.d[1], x20 + ins v8.d[0], x20 + ins v8.d[1], x20 + ins v8.d[0], x20 + ins v8.d[1], x20 + ins v8.d[0], x20 + ins v8.d[1], x20 + ", + out("v8") _, + )); + }); + s32!("ins_64b_from_v", 64, { + r8!(asm!(" + ins v8.d[0], v9.d[0] + ins v8.d[1], v9.d[0] + ins v8.d[0], v9.d[1] + ins v8.d[1], v9.d[1] + ins v8.d[0], v9.d[0] + ins v8.d[1], v9.d[0] + ins v8.d[0], v9.d[1] + ins v8.d[1], v9.d[1] + ", + out("v8") _, + )); + }); + s32!("fmla_with_prfm", 64, { + let mut p = F32; + r8!(asm!(" + prfm pldl1keep, [{0}, #256] + fmla v0.4s, v0.4s, v0.4s + prfm pldl1keep, [{0}, #320] + fmla v1.4s, v1.4s, v1.4s + prfm pldl1keep, [{0}, #384] + fmla v2.4s, v2.4s, v2.4s + prfm pldl1keep, [{0}, #448] + fmla v3.4s, v3.4s, v3.4s + prfm pldl1keep, [{0}, #512] + fmla v4.4s, v4.4s, v4.4s + prfm pldl1keep, [{0}, #576] + fmla v5.4s, v5.4s, v5.4s + prfm pldl1keep, [{0}, #640] + fmla v6.4s, v6.4s, v6.4s + prfm pldl1keep, [{0}, #704] + fmla v7.4s, v7.4s, v7.4s + prfm pldl1keep, [{0}, #768] + ", + inout(reg) p, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + )); + }); + s32!("fmla_with_w_load", 64, { + let mut p = F32; + r8!(asm!(" + ldr w20, [{0}] + fmla v0.4s, v0.4s, v0.4s + ldr w21, [{0}] + fmla v1.4s, v1.4s, v1.4s + ldr w22, [{0}] + fmla v2.4s, v2.4s, v2.4s + ldr w23, [{0}] + fmla v3.4s, v3.4s, v3.4s + ldr w24, [{0}] + fmla v4.4s, v4.4s, v4.4s + ldr w25, [{0}] + fmla v5.4s, v5.4s, v5.4s + ldr w26, [{0}] + fmla v6.4s, v6.4s, v6.4s + ldr w27, [{0}] + fmla v7.4s, v7.4s, v7.4s + ", + inout(reg) p, + out("x20") _, out("x21") _, out("x22") _, out("x23") _, + out("x24") _, out("x25") _, out("x26") _, out("x27") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + )); + }); + s32!("fmla_with_w_load_inc", 64, { + let mut p = F32; + r8!(asm!(" + ldr w20, [{0}], #4 + fmla v0.4s, v0.4s, v0.4s + ldr w21, [{0}], #4 + fmla v1.4s, v1.4s, v1.4s + ldr w22, [{0}], #4 + fmla v2.4s, v2.4s, v2.4s + ldr w23, [{0}], #4 + fmla v3.4s, v3.4s, v3.4s + ldr w24, [{0}], #4 + fmla v4.4s, v4.4s, v4.4s + ldr w25, [{0}], #4 + fmla v5.4s, v5.4s, v5.4s + ldr w26, [{0}], #4 + fmla v6.4s, v6.4s, v6.4s + ldr w27, [{0}], #4 + fmla v7.4s, v7.4s, v7.4s + ", + inout(reg) p, + out("x20") _, out("x21") _, out("x22") _, out("x23") _, + out("x24") _, out("x25") _, out("x26") _, out("x27") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + )); + }); + s32!("fmla_with_w_load_inc_alt", 64, { + let mut p = F32; + let mut q = F32; + r8!(asm!(" + ldr w20, [{0}], #4 + fmla v0.4s, v0.4s, v0.4s + ldr w21, [{1}], #4 + fmla v1.4s, v1.4s, v1.4s + ldr w22, [{0}], #4 + fmla v2.4s, v2.4s, v2.4s + ldr w23, [{1}], #4 + fmla v3.4s, v3.4s, v3.4s + ldr w24, [{0}], #4 + fmla v4.4s, v4.4s, v4.4s + ldr w25, [{1}], #4 + fmla v5.4s, v5.4s, v5.4s + ldr w26, [{0}], #4 + fmla v6.4s, v6.4s, v6.4s + ldr w27, [{1}], #4 + fmla v7.4s, v7.4s, v7.4s + ", + inout(reg) p, inout(reg) q, + out("x20") _, out("x21") _, out("x22") _, out("x23") _, + out("x24") _, out("x25") _, out("x26") _, out("x27") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + )); + }); + s32!("fmla_with_w_load_offset", 64, { + let mut p = F32; + r8!(asm!(" + ldr w20, [{0}] + fmla v0.4s, v0.4s, v0.4s + ldr w21, [{0}, #4] + fmla v1.4s, v1.4s, v1.4s + ldr w22, [{0}, #8] + fmla v2.4s, v2.4s, v2.4s + ldr w23, [{0}, #12] + fmla v3.4s, v3.4s, v3.4s + ldr w24, [{0}, #16] + fmla v4.4s, v4.4s, v4.4s + ldr w25, [{0}, #20] + fmla v5.4s, v5.4s, v5.4s + ldr w26, [{0}, #24] + fmla v6.4s, v6.4s, v6.4s + ldr w27, [{0}, #28] + fmla v7.4s, v7.4s, v7.4s + ", + inout(reg) p, + out("x20") _, out("x21") _, out("x22") _, out("x23") _, + out("x24") _, out("x25") _, out("x26") _, out("x27") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + )); + }); + s32!("fmla_with_x_load", 64, { + let mut p = F32; + r8!(asm!(" + fmla v0.4s, v0.4s, v0.4s + ldr x20, [{0}] + fmla v1.4s, v1.4s, v1.4s + ldr x21, [{0}] + fmla v2.4s, v2.4s, v2.4s + ldr x22, [{0}] + fmla v3.4s, v3.4s, v3.4s + ldr x23, [{0}] + fmla v4.4s, v4.4s, v4.4s + ldr x24, [{0}] + fmla v5.4s, v5.4s, v5.4s + ldr x25, [{0}] + fmla v6.4s, v6.4s, v6.4s + ldr x26, [{0}] + fmla v7.4s, v7.4s, v7.4s + ldr x27, [{0}] + ", + inout(reg) p, + out("x20") _, out("x21") _, out("x22") _, out("x23") _, + out("x24") _, out("x25") _, out("x26") _, out("x27") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + )); + }); + s32!("fmla_with_s_load", 64, { + let mut p = F32; + r8!(asm!(" + ldr s16, [{0}] + fmla v0.4s, v0.4s, v0.4s + ldr s17, [{0}] + fmla v1.4s, v1.4s, v1.4s + ldr s18, [{0}] + fmla v2.4s, v2.4s, v2.4s + ldr s19, [{0}] + fmla v3.4s, v3.4s, v3.4s + ldr s20, [{0}] + fmla v4.4s, v4.4s, v4.4s + ldr s21, [{0}] + fmla v5.4s, v5.4s, v5.4s + ldr s22, [{0}] + fmla v6.4s, v6.4s, v6.4s + ldr s23, [{0}] + fmla v7.4s, v7.4s, v7.4s + ", + inout(reg) p, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("fmla_with_d_load", 64, { + let mut p = F32; + r8!(asm!(" + ldr d16, [{0}] + fmla v0.4s, v0.4s, v0.4s + ldr d17, [{0}] + fmla v1.4s, v1.4s, v1.4s + ldr d18, [{0}] + fmla v2.4s, v2.4s, v2.4s + ldr d19, [{0}] + fmla v3.4s, v3.4s, v3.4s + ldr d20, [{0}] + fmla v4.4s, v4.4s, v4.4s + ldr d21, [{0}] + fmla v5.4s, v5.4s, v5.4s + ldr d22, [{0}] + fmla v6.4s, v6.4s, v6.4s + ldr d23, [{0}] + fmla v7.4s, v7.4s, v7.4s + ", + inout(reg) p, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + out("v16") _, out("v17") _, out("v18") _, out("v19") _, + out("v20") _, out("v21") _, out("v22") _, out("v23") _, + )); + }); + s32!("fmla_with_d_load_as_v", 64, { + let mut p = F32; + r8!(asm!(" + fmla v0.4s, v0.4s, v0.4s + ld1 {{ v9.d }}[0], [{0}] + fmla v1.4s, v1.4s, v1.4s + ld1 {{ v10.d }}[0], [{0}] + fmla v2.4s, v2.4s, v2.4s + ld1 {{ v11.d }}[0], [{0}] + fmla v3.4s, v3.4s, v3.4s + ld1 {{ v12.d }}[0], [{0}] + fmla v4.4s, v4.4s, v4.4s + ld1 {{ v13.d }}[0], [{0}] + fmla v5.4s, v5.4s, v5.4s + ld1 {{ v14.d }}[0], [{0}] + fmla v6.4s, v6.4s, v6.4s + ld1 {{ v15.d }}[0], [{0}] + fmla v7.4s, v7.4s, v7.4s + ld1 {{ v16.d }}[0], [{0}] + ", + inout(reg) p, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("fmla_with_v_load", 64, { + let mut p = F32; + r8!(asm!(" + fmla v0.4s, v0.4s, v0.4s + ld1 {{ v9.4s }}, [{0}] + fmla v1.4s, v1.4s, v1.4s + ld1 {{ v10.4s }}, [{0}] + fmla v2.4s, v2.4s, v2.4s + ld1 {{ v11.4s }}, [{0}] + fmla v3.4s, v3.4s, v3.4s + ld1 {{ v12.4s }}, [{0}] + fmla v4.4s, v4.4s, v4.4s + ld1 {{ v13.4s }}, [{0}] + fmla v5.4s, v5.4s, v5.4s + ld1 {{ v14.4s }}, [{0}] + fmla v6.4s, v6.4s, v6.4s + ld1 {{ v15.4s }}, [{0}] + fmla v7.4s, v7.4s, v7.4s + ld1 {{ v16.4s }}, [{0}] + ", + inout(reg) p, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("fmla_with_ins_32b", 64, { + r8!(asm!(" + fmla v0.4s, v0.4s, v0.4s + ins v8.s[0], w20 + fmla v1.4s, v1.4s, v1.4s + ins v9.s[0], w20 + fmla v2.4s, v2.4s, v2.4s + ins v10.s[0], w20 + fmla v3.4s, v3.4s, v3.4s + ins v11.s[0], w20 + fmla v4.4s, v4.4s, v4.4s + ins v12.s[0], w20 + fmla v5.4s, v5.4s, v5.4s + ins v13.s[0], w20 + fmla v6.4s, v6.4s, v6.4s + ins v14.s[0], w20 + fmla v7.4s, v7.4s, v7.4s + ins v15.s[0], w20 + ", + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + out("x20") _, + )); + }); + s32!("fmla_with_ins_64b", 64, { + r8!(asm!(" + fmla v0.4s, v0.4s, v0.4s + ins v8.d[0], x20 + fmla v1.4s, v1.4s, v1.4s + ins v9.d[0], x20 + fmla v2.4s, v2.4s, v2.4s + ins v10.d[0], x20 + fmla v3.4s, v3.4s, v3.4s + ins v11.d[0], x20 + fmla v4.4s, v4.4s, v4.4s + ins v12.d[0], x20 + fmla v5.4s, v5.4s, v5.4s + ins v13.d[0], x20 + fmla v6.4s, v6.4s, v6.4s + ins v14.d[0], x20 + fmla v7.4s, v7.4s, v7.4s + ins v15.d[0], x20 + ", + out("x20") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("fmla_with_ins_64b_cross_parity", 64, { + r8!(asm!(" + fmla v0.4s, v0.4s, v0.4s + ins v9.d[0], x20 + fmla v1.4s, v1.4s, v1.4s + ins v10.d[0], x20 + fmla v2.4s, v2.4s, v2.4s + ins v11.d[0], x20 + fmla v3.4s, v6.4s, v3.4s + ins v12.d[0], x20 + fmla v4.4s, v4.4s, v4.4s + ins v13.d[0], x20 + fmla v5.4s, v5.4s, v5.4s + ins v14.d[0], x20 + fmla v6.4s, v6.4s, v6.4s + ins v15.d[0], x20 + fmla v7.4s, v7.4s, v7.4s + ins v8.d[0], x20 + ", + out("x20") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("ins_32b_with_load_s", 64, { + let mut p = F32; + r8!(asm!(" + ldr s0, [{0}] + ins v8.d[0], x20 + ldr s1, [{0}] + ins v9.d[0], x20 + ldr s2, [{0}] + ins v10.d[0], x20 + ldr s3, [{0}] + ins v11.d[0], x20 + ldr s4, [{0}] + ins v12.d[0], x20 + ldr s5, [{0}] + ins v13.d[0], x20 + ldr s6, [{0}] + ins v14.d[0], x20 + ldr s7, [{0}] + ins v15.d[0], x20 + ", + inout(reg) p, + out("x20") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); + s32!("ins_32b_with_load_s_cross_parity", 64, { + let mut p = F32; + r8!(asm!(" + ldr s0, [{0}] + ins v9.d[0], x20 + ldr s1, [{0}] + ins v10.d[0], x20 + ldr s2, [{0}] + ins v11.d[0], x20 + ldr s3, [{0}] + ins v12.d[0], x20 + ldr s4, [{0}] + ins v13.d[0], x20 + ldr s5, [{0}] + ins v14.d[0], x20 + ldr s6, [{0}] + ins v15.d[0], x20 + ldr s7, [{0}] + ins v8.d[0], x20 + ", + inout(reg) p, + out("x20") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + )); + }); +} + +fn has_asimdhp() -> bool { + std::fs::read_to_string("/proc/cpuinfo") + .unwrap() + .contains("asimdhp") +} + +#[target_feature(enable = "fp16")] +pub unsafe fn asimdhp(filter: Option<&str>) { + macro_rules! s32 { + ($label: literal, $n: expr, $stmt:block) => { + if $label.contains(filter.unwrap_or("")) { + println!("{:40} {:.2}", $label, b32!($stmt) / $n as f64 / *TICK); + } + }; + } + + s32!("fmlahp", 16, { + asm!(" fmla v0.8h, v0.8h, v0.8h + fmla v1.8h, v1.8h, v1.8h + fmla v2.8h, v2.8h, v2.8h + fmla v3.8h, v3.8h, v3.8h + fmla v4.8h, v4.8h, v4.8h + fmla v5.8h, v5.8h, v5.8h + fmla v6.8h, v6.8h, v6.8h + fmla v7.8h, v7.8h, v7.8h + fmla v8.8h, v8.8h, v8.8h + fmla v9.8h, v9.8h, v9.8h + fmla v10.8h,v10.8h,v10.8h + fmla v11.8h,v11.8h,v11.8h + fmla v12.8h,v12.8h,v12.8h + fmla v13.8h,v13.8h,v13.8h + fmla v14.8h,v14.8h,v14.8h + fmla v15.8h,v15.8h,v15.8h ", + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + ) + }); + + s32!("fcvt", 16, { + asm!(" fcvtn v0.4h, v0.4s + fcvtn v1.4h, v1.4s + fcvtn v2.4h, v2.4s + fcvtn v3.4h, v3.4s + fcvtn v4.4h, v4.4s + fcvtn v5.4h, v5.4s + fcvtn v6.4h, v6.4s + fcvtn v7.4h, v7.4s + fcvtn v8.4h, v8.4s + fcvtn v9.4h, v9.4s + fcvtn v10.4h, v10.4s + fcvtn v11.4h, v11.4s + fcvtn v12.4h, v12.4s + fcvtn v13.4h, v13.4s + fcvtn v14.4h, v14.4s + fcvtn v15.4h, v15.4s", + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + ) + }); + + s32!("fcvt2", 16, { + asm!(" fcvtn2 v0.8h, v0.4s + fcvtn2 v1.8h, v1.4s + fcvtn2 v2.8h, v2.4s + fcvtn2 v3.8h, v3.4s + fcvtn2 v4.8h, v4.4s + fcvtn2 v5.8h, v5.4s + fcvtn2 v6.8h, v6.4s + fcvtn2 v7.8h, v7.4s + fcvtn2 v8.8h, v8.4s + fcvtn2 v9.8h, v9.4s + fcvtn2 v10.8h, v10.4s + fcvtn2 v11.8h, v11.4s + fcvtn2 v12.8h, v12.4s + fcvtn2 v13.8h, v13.4s + fcvtn2 v14.8h, v14.4s + fcvtn2 v15.8h, v15.4s", + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + ) + }); + + s32!("fmlahp_with_dep", 1, { + asm!("fmla v0.8h, v0.8h, v0.8h", out("v0") _) + }); + s32!("fcvtn_with_dep", 1, { + asm!("fcvtn v0.4h, v0.4s", out("v0") _) + }); + s32!("fcvtn2_with_dep", 1, { + asm!("fcvtn2 v0.8h, v0.4s", out("v0") _) + }); +} + +macro_rules! ksimd { + ($filter: expr, $vector_size: expr, $geo: literal, $n: expr, $path: literal) => { + kloop!($filter, $vector_size, $geo, $n, "arm64simd", $path) + }; +} + +macro_rules! kfp16 { + ($filter: expr, $vector_size: expr, $geo: literal, $n: expr, $path: literal) => { + kloop!($filter, $vector_size, $geo, $n, "arm64fp16", $path) + }; +} + +macro_rules! kloop { + ($filter: expr, $vector_size: expr, $geo: literal, $n: expr, $dir: literal, $path: literal) => { + let label = $path.split("/").last().unwrap().split_once(".").unwrap().0; + let full_label = format!("{:8} {:40}", $geo, label); + if full_label.contains($filter.unwrap_or("")) { + let time = b2!({ + let mut p = F32; + let mut q = F32; + r4!(asm!(include_str!(concat!("../arm64/", $dir, "/", $path)), + inout("x1") p, inout("x2") q, out("x3") _, + out("x4") _, out("x5") _, out("x6") _, out("x7") _, + out("x8") _, out("x9") _, out("x10") _, out("x11") _, + out("x12") _, out("x13") _, out("x14") _, out("x15") _, + out("x20") _, out("x21") _, out("x22") _, out("x23") _, + out("x24") _, out("x25") _, out("x26") _, out("x27") _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + out("v16") _, out("v17") _, out("v18") _, out("v19") _, + out("v20") _, out("v21") _, out("v22") _, out("v23") _, + out("v24") _, out("v25") _, out("v26") _, out("v27") _, + out("v28") _, out("v29") _, out("v30") _, out("v31") _, + )); + }) / 4.; + println!("{} {:3.0}% ({:0.2}/{} cy)", full_label, $n as f64 / $vector_size as f64 / time * 100. * *TICK, time / *TICK, $n as f64 / $vector_size as f64); + } + } +} + +unsafe fn f32_8x8(f: Option<&str>) { + ksimd!( + f, + 4, + "8x8x1xf32", + 64, + "arm64simd_mmm_f32_8x8/packed_packed_loop1/naive.tmpli" + ); + ksimd!( + f, + 4, + "8x8x1xf32", + 64, + "arm64simd_mmm_f32_8x8/packed_packed_loop1/broken_chains.tmpli" + ); + ksimd!( + f, + 4, + "8x8x1xf32", + 64, + "arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_x_no_preload.tmpli" + ); + ksimd!( + f, + 4, + "8x8x1xf32", + 64, + "arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_x_preload.tmpli" + ); + ksimd!( + f, + 4, + "8x8x1xf32", + 64, + "arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_w_no_preload.tmpli" + ); + ksimd!( + f, + 4, + "8x8x1xf32", + 64, + "arm64simd_mmm_f32_8x8/packed_packed_loop1/ldr_w_preload.tmpli" + ); + ksimd!( + f, + 4, + "8x8x2xf32", + 128, + "arm64simd_mmm_f32_8x8/packed_packed_loop2/broken_chains.tmpli" + ); + ksimd!( + f, + 4, + "8x8x2xf32", + 128, + "arm64simd_mmm_f32_8x8/packed_packed_loop2/cortex_a55.tmpli" + ); +} + +unsafe fn f32_12x8(f: Option<&str>) { + ksimd!( + f, + 4, + "12x8x1xf32", + 96, + "arm64simd_mmm_f32_12x8/packed_packed_loop1/naive.tmpli" + ); + ksimd!( + f, + 4, + "12x8x1xf32", + 96, + "arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_w_no_preload.tmpli" + ); + ksimd!( + f, + 4, + "12x8x1xf32", + 96, + "arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_w_preload.tmpli" + ); + ksimd!( + f, + 4, + "12x8x1xf32", + 96, + "arm64simd_mmm_f32_12x8/packed_packed_loop1/ldr_x_preload.tmpli" + ); + ksimd!( + f, + 4, + "12x8x2xf32", + 192, + "arm64simd_mmm_f32_12x8/packed_packed_loop2/cortex_a55.tmpli" + ); +} + +unsafe fn f32_16x4(f: Option<&str>) { + ksimd!( + f, + 4, + "16x4x1xf32", + 64, + "arm64simd_mmm_f32_16x4/packed_packed_loop1/naive.tmpli" + ); + ksimd!( + f, + 4, + "16x4x1xf32", + 64, + "arm64simd_mmm_f32_16x4/packed_packed_loop1/cortex_a53.tmpli" + ); + ksimd!( + f, + 4, + "16x4x2xf32", + 128, + "arm64simd_mmm_f32_16x4/packed_packed_loop2/cortex_a55.tmpli" + ); +} + +unsafe fn f32_24x4(f: Option<&str>) { + ksimd!( + f, + 4, + "24x4x1xf32", + 96, + "arm64simd_mmm_f32_24x4/packed_packed_loop1/naive.tmpli" + ); + ksimd!( + f, + 4, + "24x4x1xf32", + 96, + "arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a53.tmpli" + ); + ksimd!( + f, + 4, + "24x4x1xf32", + 96, + "arm64simd_mmm_f32_24x4/packed_packed_loop1/cortex_a55.tmpli" + ); +} + +unsafe fn f32_64x1(f: Option<&str>) { + ksimd!( + f, + 4, + "64x1x1xf32", + 64, + "arm64simd_mmm_f32_64x1/loop1/naive.tmpli" + ); + ksimd!( + f, + 4, + "64x1x1xf32", + 64, + "arm64simd_mmm_f32_64x1/loop1/cortex_a53.tmpli" + ); + ksimd!( + f, + 4, + "64x1x2xf32", + 128, + "arm64simd_mmm_f32_64x1/loop2/naive.tmpli" + ); + ksimd!( + f, + 4, + "64x1x2xf32", + 128, + "arm64simd_mmm_f32_64x1/loop2/cortex_a55.tmpli" + ); +} + +// RUSTFLAGS="-C target-feature=+fp16" cargo +nightly dinghy -d khadas-paris bench --bench arm64simd +#[target_feature(enable = "fp16")] +unsafe fn f16_16x8(f: Option<&str>) { + kfp16!( + f, + 8, + "16x8x1xf16", + 128, + "arm64fp16_mmm_f16_16x8/loop1/naive.tmpli" + ); + kfp16!( + f, + 8, + "16x8x2xf16", + 256, + "arm64fp16_mmm_f16_16x8/loop2/cortex_a55.tmpli" + ); + kfp16!( + f, + 8, + "32x4x1xf16", + 128, + "arm64fp16_mmm_f16_32x4/loop1/naive.tmpli" + ); + kfp16!( + f, + 8, + "32x4x2xf16", + 256, + "arm64fp16_mmm_f16_32x4/loop2/cortex_a55.tmpli" + ); +} + +fn main() { + println!("freq {:.2}GHz\n", 1e-9 / *TICK); + + let filter = std::env::args().skip(1).filter(|a| a != "--bench").next(); + unsafe { + armv8(filter.as_deref()); + if has_asimdhp() { + asimdhp(filter.as_deref()); + } + f32_8x8(filter.as_deref()); + f32_12x8(filter.as_deref()); + f32_16x4(filter.as_deref()); + f32_24x4(filter.as_deref()); + f32_64x1(filter.as_deref()); + f16_16x8(filter.as_deref()); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/avx512_zombies.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/avx512_zombies.rs new file mode 100644 index 0000000..7b4301a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/avx512_zombies.rs @@ -0,0 +1,72 @@ +#![allow(dead_code)] + +use criterion::{Criterion, criterion_group, criterion_main}; +use tract_linalg::mmm::MatMatMul; + +#[path = "utils.rs"] +mod utils; +use utils::mat_mat_with_mm; + +fn run(c: &mut Criterion, name: &str, mmm: &dyn MatMatMul, m: usize, k: usize, n: usize) { + let mut group = c.benchmark_group(format!("avx512_zombie/{name}")); + let id = format!("{m}x{k}x{n}"); + group.bench_with_input( + criterion::BenchmarkId::new("hot", &id), + &(tract_data::prelude::DatumType::F32, m, k, n, false), + |b, p| mat_mat_with_mm(b, mmm, p), + ); + group.bench_with_input( + criterion::BenchmarkId::new("cold", &id), + &(tract_data::prelude::DatumType::F32, m, k, n, true), + |b, p| mat_mat_with_mm(b, mmm, p), + ); +} + +fn benches(c: &mut Criterion) { + if !std::is_x86_feature_detected!("avx512f") { + eprintln!("avx512f not available, skipping"); + return; + } + + use tract_data::prelude::DatumType::F32; + use tract_linalg::x86_64_fma::mmm::*; + + // Representative large-K, square-ish M case. + let (m, k) = (64usize, 256usize); + + // N = 5 : zombie was 32x5 vs old 64x3. + run(c, "N5_64x3_explicit", &*avx512_mmm_f32_64x3.mmm(), m, k, 5); + run(c, "N5_32x5_explicit", &*avx512_mmm_f32_32x5.mmm(), m, k, 5); + + // N = 6 : zombie was 32x6 vs old 64x3. + run(c, "N6_64x3_explicit", &*avx512_mmm_f32_64x3.mmm(), m, k, 6); + run(c, "N6_32x6_explicit", &*avx512_mmm_f32_32x6.mmm(), m, k, 6); + + // N = 8 : zombie was 16x8 vs old 48x4. + run(c, "N8_48x4_explicit", &*avx512_mmm_f32_48x4.mmm(), m, k, 8); + run(c, "N8_16x8_explicit", &*avx512_mmm_f32_16x8.mmm(), m, k, 8); + + // What does the live dispatcher pick for these shapes? If the picker + // is healthy these match the zombie numbers above, kernel name printed + // to stderr at startup. + for n in [5usize, 6, 8] { + let mmm = tract_linalg::ops() + .mmm(F32, Some(m), Some(k), Some(n)) + .unwrap(); + eprintln!("dispatcher@m={m},k={k},n={n} picked {}", mmm.name()); + run(c, &format!("N{n}_dispatch"), &*mmm, m, k, n); + } + + // Trace-only: a few shapes where M-padding overhead with the old + // picker was high. We expect the M-aware picker to pick smaller-mr + // kernels here. + for (m, n) in [(20usize, 2), (33, 3), (50, 4), (17, 5), (1000, 64)] { + let mmm = tract_linalg::ops() + .mmm(F32, Some(m), Some(k), Some(n)) + .unwrap(); + eprintln!("dispatcher@m={m},k={k},n={n} picked {}", mmm.name()); + } +} + +criterion_group!(g, benches); +criterion_main!(g); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/erf.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/erf.rs new file mode 100644 index 0000000..ce0fca7 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/erf.rs @@ -0,0 +1,40 @@ +// Microbenchmark: AVX-512 (zmm, 16-wide) erf kernel vs the generic scalar +// SErf4 (no FMA predecessor exists on x86). All buffers are 64-byte aligned +// (AVX-512 alignment_bytes) and a multiple of 64 elements so the kernel's +// nr() = 64 divides the length. + +use criterion::*; +use tract_data::prelude::*; +use tract_linalg::element_wise::ElementWiseKer; + +const N: usize = 1024; + +fn aligned_input() -> Tensor { + let mut t = unsafe { Tensor::uninitialized_aligned::(&[N], 64).unwrap() }; + let s = unsafe { t.as_slice_mut_unchecked::() }; + for (i, x) in s.iter_mut().enumerate() { + *x = (i as f32 / 10.0).sin() * 5.0; + } + t +} + +fn erf_f32(c: &mut Criterion) { + let mut g = c.benchmark_group("erf_f32"); + g.throughput(Throughput::Elements(N as u64)); + let mut tp = aligned_input(); + let sp = unsafe { tp.as_slice_mut_unchecked::() }; + g.bench_function("generic", |b| { + b.iter(|| tract_linalg::generic::SErf4::run(sp, ())) + }); + if std::is_x86_feature_detected!("avx512f") { + let mut ta = aligned_input(); + let sa = unsafe { ta.as_slice_mut_unchecked::() }; + g.bench_function("avx512", |b| { + b.iter(|| tract_linalg::x86_64_fma::erf::x86_64_avx512_erf_f32_64n::run(sa, ())) + }); + } + g.finish(); +} + +criterion_group!(g, erf_f32); +criterion_main!(g); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/gelu.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/gelu.rs new file mode 100644 index 0000000..31b1df8 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/gelu.rs @@ -0,0 +1,44 @@ +use criterion::*; +use tract_data::prelude::*; + +use tract_linalg::element_wise::ElementWiseKer; + +fn gelu_f32(c: &mut Criterion) { + let mut group = c.benchmark_group("gelu_f32"); + group.throughput(Throughput::Elements(1024)); + let mut input = unsafe { Tensor::uninitialized_aligned::(&[1024], 16).unwrap() }; + let input = unsafe { input.as_slice_mut_unchecked::() }; + for (i, x) in input.iter_mut().enumerate() { + *x = (i as f32 / 10.0).sin() * 5.0; + } + group.bench_function("rust_scalar", |b| b.iter(|| rust_scalar(input))); + group.bench_function("linalg", |b| b.iter(|| linalg(input))); + #[cfg(target_arch = "aarch64")] + group.bench_function("linalg-asm-compose", |b| { + b.iter(|| tract_linalg::arm64::arm64simd_gelu_f32_4n::run(input, ())) + }); + #[cfg(target_arch = "aarch64")] + group.bench_function("linalg-asm-fused", |b| { + b.iter(|| tract_linalg::arm64::arm64simd_gelu_f32_4n_fused::run(input, ())) + }); +} + +#[inline(never)] +fn rust_scalar(input: &mut [f32]) { + // Match tract's GeluApproximate scalar formula (pow=3). + const SQRT_2_OVER_PI: f32 = 0.7978845608028654; + const COEF: f32 = 0.044715; + for x in input { + let v = *x; + let inner = SQRT_2_OVER_PI * (v + COEF * v * v * v); + *x = 0.5 * v * (1.0 + inner.tanh()); + } +} + +#[inline(never)] +fn linalg(input: &mut [f32]) { + (tract_linalg::ops().gelu_f32)().run(input).unwrap(); +} + +criterion_group!(benches, gelu_f32); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/hardswish.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/hardswish.rs new file mode 100644 index 0000000..0f96dc4 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/hardswish.rs @@ -0,0 +1,34 @@ +use criterion::*; +use tract_data::prelude::*; + +use tract_linalg::element_wise::ElementWiseKer; + +fn hardswish_f32(c: &mut Criterion) { + let mut group = c.benchmark_group("hardswish_f32"); + group.throughput(Throughput::Elements(1024)); + let mut input = unsafe { Tensor::uninitialized_aligned::(&[1024], 16).unwrap() }; + let input = unsafe { input.as_slice_mut_unchecked::() }; + group.bench_function("rust", |b| b.iter(|| rust_f32(input))); + group.bench_function("linalg", |b| b.iter(|| linalg32(input))); + #[cfg(target_arch = "aarch64")] + group.bench_function("linalg-asm", |b| { + b.iter(|| tract_linalg::arm64::arm64simd_hardswish_f32_8n::run(input, ())) + }); +} + +#[inline(never)] +fn rust_f32(input: &mut [f32]) { + const INV6: f32 = 1.0 / 6.0; + for x in input { + let relu6 = ((*x + 3.0).min(6.0)).max(0.0); + *x = *x * relu6 * INV6; + } +} + +#[inline(never)] +fn linalg32(input: &mut [f32]) { + (tract_linalg::ops().hardswish_f32)().run(input).unwrap(); +} + +criterion_group!(benches, hardswish_f32); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/intel.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/intel.rs new file mode 100644 index 0000000..44fd84a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/intel.rs @@ -0,0 +1,215 @@ +#![allow(dead_code)] +use std::time::Instant; + +use tract_data::prelude::*; +use tract_linalg::frame::mmm::*; + +fn ruin_cache() { + // return; + let _a = (0..1000000).collect::>(); +} + +pub fn reference(mr: usize, k: usize, nr: usize) -> Vec +where + T: Datum + Copy + num_traits::Zero + tract_linalg::LADatum, + K: MatMatMulKer, +{ + let mut vi = vec![0.0; k * nr]; + + for m in 0..mr { + for n in 0..nr { + for _ in 0..k { + let a: f32 = 1.0; + let b = 1.0; + let offset = { n + m * nr }; + vi[offset] += a * b; + } + } + } + vi +} + +fn bench_to_nanos< + T: Datum + Copy + num_traits::Zero + tract_linalg::LADatum, + K: MatMatMulKer, +>( + loops: usize, + m: usize, + n: usize, + k: usize, +) -> f64 { + let kernel = K::mmm(); + + let mut a = Tensor::zero_aligned::( + &[(k + K::end_padding_packed_a()) * m], + K::alignment_bytes_packed_a(), + ) + .unwrap(); + + let mut a_plain = a.try_as_plain_mut().unwrap(); + let mut v = a_plain.to_array_view_mut::().unwrap(); + v += 1.0; + drop(v); + drop(a_plain); + let mut b = Tensor::zero_aligned::( + &[(k + K::end_padding_packed_b()) * n], + K::alignment_bytes_packed_b(), + ) + .unwrap(); + + let mut b_plain = b.try_as_plain_mut().unwrap(); + let mut v = b_plain.to_array_view_mut::().unwrap(); + v += 1.0; + drop(v); + drop(b_plain); + let mut c = Tensor::zero::(&[n, m]).unwrap(); + + let ops = unsafe { + [ + FusedSpec::AddMatMul { + k, + a: kernel.a_packed(4, k).wrap(&a.view()), + b: kernel.b_packed(4, k).wrap(&b.view()), + }, + // FusedSpec::AddUnicast(kernel.c_view(1, 0).wrap(&c.view_mut())), + FusedSpec::Store(kernel.c_view(1, 0).wrap(&c.view_mut())), + ] + }; + + let mut values = Vec::with_capacity(loops); + + for _ in 0..loops { + ruin_cache(); + let start = Instant::now(); + unsafe { kernel.run(m, n, &ops).unwrap() }; + values.push(start.elapsed()); + } + + eprintln!( + "{:?} -> {:?}", + values.first().unwrap(), + values.last().unwrap() + ); + + values.sort(); + values[loops / 2].as_nanos() as f64 +} + +fn model>() +-> (f64, f64) { + let x = 1000; + let zp = bench_to_nanos::(1000, K::mr() * 4, K::nr() * 4, 0); + let y = bench_to_nanos::(1000, K::mr() * 4, K::nr() * 4, x); + let slope = (y - zp) / x as f64; + (slope, zp) +} + +fn as_match_line>() { + let coeffs = model::(); + println!( + "({:?}, {}, {}) => {} * k + {}", + K::name(), + K::mr(), + K::nr(), + (coeffs.0), + (coeffs.1), + ); +} + +fn main() { + let core_id = core_affinity::get_core_ids().unwrap()[0]; + core_affinity::set_for_current(core_id); + // as_match_line::(); + // as_match_line::(); + // as_match_line::(); + // as_match_line::(); + // as_match_line::(); + // as_match_line::(); + // as_match_line::(); + // as_match_line::(); + // as_match_line::(); + + // mmv_perf_m(); + mmm_perf_batch_size(); +} + +// for mmv +fn mmv_perf_m() { + use tract_linalg::x86_64_fma::mmm::*; + let core_id = core_affinity::get_core_ids().unwrap()[0]; + core_affinity::set_for_current(core_id); + fn bench>( + m: usize, + ) { + let val = bench_to_nanos::(1000, m, 1, 100) / (m * 100) as f64; + print!("{val}\t"); + } + + print!("N\t"); + print!("fma_mmm_f32_64x1\t"); + print!("avx512_mmm_f32_128x1\t"); + print!("avx512_mmm_f32_16x1\t"); + println!(); + for n in 1..=128 { + eprintln!("{n}"); + print!("{n}\t"); + bench::(n); + bench::(n); + bench::(n); + println!(); + } +} + +// output a csv file with the perf of the kernels wrt batch size +fn mmm_perf_batch_size() { + use tract_linalg::x86_64_fma::mmm::*; + let core_id = core_affinity::get_core_ids().unwrap()[0]; + core_affinity::set_for_current(core_id); + fn bench>( + n: usize, + ) { + let val = + bench_to_nanos::(1000, K::mr() * 4, n, 100) / (K::mr() * 4 * 100 * n) as f64; + print!("{val}\t"); + } + + print!("N\t"); + print!("fma_mmm_f32_8x8\t"); + print!("fma_mmm_f32_16x6\t"); + print!("fma_mmm_f32_16x5\t"); + print!("fma_mmm_f32_24x4\t"); + print!("fma_mmm_f32_32x3\t"); + print!("fma_mmm_f32_40x2\t"); + print!("fma_mmm_f32_64x1\t"); + print!("avx512_mmm_f32_128x1\t"); + print!("avx512_mmm_f32_16x1\t"); + print!("avx512_mmm_f32_16x12\t"); + print!("avx512_mmm_f32_16x8\t"); + print!("avx512_mmm_f32_32x6\t"); + print!("avx512_mmm_f32_32x5\t"); + print!("avx512_mmm_f32_48x4\t"); + print!("avx512_mmm_f32_64x3\t"); + print!("avx512_mmm_f32_80x2\t"); + println!(); + for n in 1..=128 { + eprintln!("{n}"); + print!("{n}\t"); + bench::(n); + bench::(n); + bench::(n); + bench::(n); + bench::(n); + bench::(n); + bench::(n); + bench::(n); + bench::(n); + bench::(n); + bench::(n); + bench::(n); + bench::(n); + bench::(n); + bench::(n); + bench::(n); + println!(); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/leaky_relu.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/leaky_relu.rs new file mode 100644 index 0000000..7e796a2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/leaky_relu.rs @@ -0,0 +1,72 @@ +use criterion::*; +use tract_data::prelude::*; + +use tract_linalg::element_wise::ElementWiseKer; + +fn leaky_relu_f16(c: &mut Criterion) { + let mut group = c.benchmark_group("leaky_relu_f16"); + group.throughput(Throughput::Elements(1024)); + let mut input = unsafe { Tensor::uninitialized_aligned::(&[1024], 16).unwrap() }; + let input = input.as_slice_mut::().unwrap(); + let alpha = f16::from_f32(0.1); + group.bench_function("rust", |b| b.iter(|| rust_fp16(input, alpha))); + group.bench_function("rust_with_f16", |b| { + b.iter(|| unsafe { rust_with_fp16(input, alpha) }) + }); + group.bench_function("linalg", |b| b.iter(|| linalg16(input, alpha))); + group.bench_function("linalg-asm", |b| { + b.iter(|| tract_linalg::arm64::arm64fp16_leaky_relu_f16_16n::run(input, alpha)) + }); +} + +#[inline(never)] +fn rust_fp16(input: &mut [f16], alpha: f16) { + for x in input { + *x = if *x > f16::ZERO { *x } else { *x * alpha } + } +} + +#[target_feature(enable = "fp16")] +#[inline(never)] +unsafe fn rust_with_fp16(input: &mut [f16], alpha: f16) { + for x in input { + *x = if *x > f16::ZERO { *x } else { *x * alpha } + } +} + +#[inline(never)] +fn linalg16(input: &mut [f16], alpha: f16) { + (tract_linalg::ops().leaky_relu_f16)() + .run_with_params(input, alpha) + .unwrap(); +} + +fn leaky_relu_f32(c: &mut Criterion) { + let mut group = c.benchmark_group("leaky_relu_f32"); + group.throughput(Throughput::Elements(1024)); + let mut input = unsafe { Tensor::uninitialized_aligned::(&[1024], 16).unwrap() }; + let input = input.as_slice_mut::().unwrap(); + let alpha = 0.1f32; + group.bench_function("rust", |b| b.iter(|| rust_fp32(input, alpha))); + group.bench_function("linalg", |b| b.iter(|| linalg32(input, alpha))); + group.bench_function("linalg-asm", |b| { + b.iter(|| tract_linalg::arm64::arm64simd_leaky_relu_f32_8n::run(input, alpha)) + }); +} + +#[inline(never)] +fn rust_fp32(input: &mut [f32], alpha: f32) { + for x in input { + *x = if *x > 0.0 { *x } else { *x * alpha } + } +} + +#[inline(never)] +fn linalg32(input: &mut [f32], alpha: f32) { + (tract_linalg::ops().leaky_relu_f32)() + .run_with_params(input, alpha) + .unwrap(); +} + +criterion_group!(benches, leaky_relu_f32, leaky_relu_f16); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/mat_vec.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/mat_vec.rs new file mode 100644 index 0000000..c3dc284 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/mat_vec.rs @@ -0,0 +1,48 @@ +use criterion::*; +use tract_data::internal::*; +use tract_linalg::mmm::{AsInputValue, FusedSpec}; + +use DatumType::F32; + +fn mat_vec_mul(c: &mut Criterion) { + let mut group = c.benchmark_group("mat_vec_mul"); + unsafe { + { + let (m, k) = &(768usize, 256usize); + group.throughput(Throughput::Elements((m * k) as u64)); + group.bench_with_input( + BenchmarkId::from_parameter(format!("{m}x{k}")), + &(m, k), + |be, &(&m, &k)| { + let mmm = tract_linalg::ops() + .mmm(F32, Some(m), Some(k), Some(1)) + .unwrap(); + let packing = &mmm.packings()[0]; + let a = Tensor::zero::(&[m, k]).unwrap(); + let pa = packing.0.prepare_one(&a, 1, 0).unwrap(); + let b = Tensor::zero::(&[k, 1]).unwrap(); + let pb = packing.1.prepare_one(&b, 0, 1).unwrap(); + let mut c = Tensor::zero::(&[m]).unwrap(); + be.iter(move || { + mmm.run( + m, + 1, + &[ + FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa), + b: AsInputValue::Borrowed(&*pb), + packing: 0, + }, + FusedSpec::Store(mmm.c_view(Some(0), Some(0)).wrap(&c.view_mut())), + ], + ) + }); + }, + ); + } + } + group.finish(); +} + +criterion_group!(benches, mat_vec_mul); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/mm_for_asr_am.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/mm_for_asr_am.rs new file mode 100644 index 0000000..a800b4e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/mm_for_asr_am.rs @@ -0,0 +1,37 @@ +use criterion::*; + +mod utils; +use utils::*; + +fn all(c: &mut Criterion) { + // packed_packed: co, ci, n + // direct_conv(c, "asr_2M", 24, 5, 40, 200, 1); // lda + packed_packed(c, "asr_2M", 256, 200, 24); // tdnn1 + // direct_conv(c, "asr_2M", 24, 3, 256, 256, 1); // tdnn2 + // direct_conv(c, "asr_2M", 24, 3, 256, 256, 3); // tdnn3 + packed_packed(c, "asr_2M", 256, 256, 8); // fastlstm1 and 2 (input) x 8 (4 prod x 2 layers) + packed_packed(c, "asr_2M", 256, 128, 1); // fastlstm1 and 2 (hidden) x 64 (4 prod x 2 layers x 8 loops) + packed_packed(c, "asr_2M", 256, 256, 1); // fastlstm1 and 2 (rp) x 16 (2 layers x 8 loops) + // direct_conv(c, "asr_2M", 8, 3, 256, 256, 1); // tdnn4, tdd5 (x2) + packed_packed(c, "asr_2M", 1690, 256, 8); // output + + // 8M + packed_packed(c, "asr_8M", 512, 200, 24); // tdnn1 + packed_packed(c, "asr_8M", 512, 512, 24); // tdnn2 + packed_packed(c, "asr_8M", 512, 256, 1); // fastlstm1 and 2 (four parts, rec mat*vec) + packed_vec(c, "asr_8M", 512, 256, 1); // fastlstm1 and 2 (four parts, rec mat*vec) + + // pseudo 15M + packed_packed(c, "asr_pseudo15M", 768, 200, 24); // tdnn1 + packed_packed(c, "asr_pseudo15M", 768, 2304, 24); // tdnn2 + packed_packed(c, "asr_pseudo15M", 768, 2304, 8); // tdnn3,4,5 + packed_packed(c, "asr_pseudo15M", 768, 768, 8); // fastlstm1 and 2 (four parts, rec mat*mat) + packed_packed(c, "asr_pseudo15M", 768, 384, 1); // fastlstm1 and 2 (four parts, rec mat*vec) + packed_vec(c, "asr_pseudo15M", 768, 384, 1); // fastlstm1 and 2 (four parts, rec mat*vec) + + // 15M + packed_vec(c, "asr_15M", 768, 256, 1); // fastlstm1 and 2 (four parts, rec mat*vec) +} + +criterion_group!(benches, all); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/mm_for_inception.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/mm_for_inception.rs new file mode 100644 index 0000000..aacc30b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/mm_for_inception.rs @@ -0,0 +1,47 @@ +extern crate criterion; +use criterion::*; +use tract_data::internal::*; +use tract_linalg::mmm::{AsInputValue, FusedSpec}; + +use DatumType::F32; + +fn mat_mul_smmm(be: &mut criterion::Bencher, &(m, k, n): &(usize, usize, usize)) { + unsafe { + let mmm = tract_linalg::ops() + .mmm(F32, Some(m), Some(k), Some(n)) + .unwrap(); + let a = Tensor::zero::(&[m, k]).unwrap(); + let b = Tensor::zero::(&[k, n]).unwrap(); + let packing = &mmm.packings()[0]; + let pa = packing.0.prepare_one(&a, 1, 0).unwrap(); + let pb = packing.1.prepare_one(&b, 0, 1).unwrap(); + + let mut c = Tensor::zero::(&[m, n]).unwrap(); + be.iter(move || { + mmm.run( + m, + n, + &[ + FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa), + b: AsInputValue::Borrowed(&*pb), + packing: 0, + }, + FusedSpec::Store(mmm.c_view(Some(0), Some(1)).wrap(&c.view_mut())), + ], + ) + }); + } +} + +fn mat_mul_prepacked(c: &mut Criterion, m: usize, k: usize, n: usize) { + let mut group = c.benchmark_group("mat_mul_prepacked"); + group.bench_function("smmm", |be| mat_mul_smmm(be, &(m, k, n))); +} + +fn s64x288x21609(c: &mut Criterion) { + mat_mul_prepacked(c, 64, 288, 21609) +} + +criterion::criterion_group!(benches, s64x288x21609); +criterion::criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/mm_for_wavenet_hw.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/mm_for_wavenet_hw.rs new file mode 100644 index 0000000..060db53 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/mm_for_wavenet_hw.rs @@ -0,0 +1,12 @@ +use criterion::*; + +mod utils; +use utils::*; + +fn s16x60x8(c: &mut Criterion) { + packed_packed(c, "wavenet", 32, 32, 8); // postproc + packed_packed(c, "wavenet", 16, 60, 8); +} + +criterion_group!(benches, s16x60x8); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/qmmm_i8.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/qmmm_i8.rs new file mode 100644 index 0000000..da56d5e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/qmmm_i8.rs @@ -0,0 +1,58 @@ +// int8 -> i32 GEMM (qmmm_i32) microbench. A/B the SME SMOPA kernel vs the NEON +// fallback by running twice: default (SME) vs TRACT_SME_DISABLE=1 (arm64simd 8x8). +extern crate criterion; +use criterion::*; +use tract_data::internal::*; +use tract_linalg::mmm::{AsInputValue, FusedSpec}; + +use DatumType::I32; + +fn qmmm(be: &mut criterion::Bencher, &(m, k, n): &(usize, usize, usize)) { + unsafe { + let mmm = tract_linalg::ops() + .mmm(I32, Some(m), Some(k), Some(n)) + .unwrap(); + // packing index 1 == i8i8 for both sme_qmmm_i32_32x32 and arm64simd_mmm_i32_8x8. + let a = Tensor::zero::(&[m, k]).unwrap(); + let b = Tensor::zero::(&[k, n]).unwrap(); + let packing = &mmm.packings()[1]; + let pa = packing.0.prepare_one(&a, 1, 0).unwrap(); + let pb = packing.1.prepare_one(&b, 0, 1).unwrap(); + let mut c = Tensor::zero::(&[m, n]).unwrap(); + be.iter(move || { + mmm.run( + m, + n, + &[ + FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa), + b: AsInputValue::Borrowed(&*pb), + packing: 1, + }, + FusedSpec::Store(mmm.c_view(Some(0), Some(1)).wrap(&c.view_mut())), + ], + ) + }); + } +} + +fn bench(c: &mut Criterion) { + let mut g = c.benchmark_group("qmmm_i8"); + g.sample_size(20); + for &shape in &[ + (256usize, 256usize, 256usize), + (512, 512, 512), + (1024, 1024, 1024), + (128, 768, 768), + (384, 768, 768), + (64, 2048, 2048), + ] { + let (m, k, n) = shape; + g.throughput(Throughput::Elements((m * k * n) as u64)); + g.bench_function(format!("{m}x{k}x{n}"), |be| qmmm(be, &shape)); + } + g.finish(); +} + +criterion::criterion_group!(benches, bench); +criterion::criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/rms_norm.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/rms_norm.rs new file mode 100644 index 0000000..8dcf2e0 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/rms_norm.rs @@ -0,0 +1,62 @@ +// Microbench: fused RmsNorm vs the 4-call composition that tract-core currently +// uses (MeanOfSquares + Add + Rsqrt + Mul). The composition is reconstructed +// inline here in the same shape as `core::ops::nn::rms_norm::RmsNorm::eval` +// drives it. Both versions run on a 64-byte-aligned f32 row. + +use criterion::*; +use tract_data::prelude::*; + +fn aligned_row(n: usize) -> Tensor { + let mut t = unsafe { Tensor::uninitialized_aligned::(&[n], 64).unwrap() }; + let s = unsafe { t.as_slice_mut_unchecked::() }; + for (i, x) in s.iter_mut().enumerate() { + *x = (i as f32 / 10.0).sin() * 5.0; + } + t +} + +#[inline(never)] +fn composed_rms_norm(buf: &mut [f32], eps: f32) { + // Same shape as tract-core's RmsNorm::eval: separate passes for sum-of-squares, + // mean, +eps, rsqrt, multiply — each writing/reading the row once. + let mut sum_sq = 0.0_f32; + for &x in buf.iter() { + sum_sq += x * x; + } + let mean_sq = sum_sq / buf.len() as f32; + let added = mean_sq + eps; + let inv_std = added.sqrt().recip(); + for x in buf.iter_mut() { + *x *= inv_std; + } +} + +fn rms_norm(c: &mut Criterion) { + for &n in &[1024usize, 2048, 4096] { + let id = format!("{n}"); + let mut g = c.benchmark_group(format!("rms_norm_f32/{id}")); + g.throughput(Throughput::Elements(n as u64)); + let mut t = aligned_row(n); + let s = unsafe { t.as_slice_mut_unchecked::() }; + g.bench_function("composed", |b| b.iter(|| composed_rms_norm(s, 1e-5))); + g.bench_function("generic", |b| { + b.iter(|| tract_linalg::generic::rms_norm::rms_norm_f32(s, 1e-5)) + }); + #[cfg(target_arch = "x86_64")] + if std::is_x86_feature_detected!("avx512f") { + g.bench_function("avx512", |b| { + b.iter(|| tract_linalg::x86_64_fma::rms_norm::rms_norm_f32(s, 1e-5)) + }); + } + #[cfg(target_arch = "aarch64")] + { + g.bench_function("neon", |b| { + b.iter(|| tract_linalg::arm64::arm64simd_rms_norm_f32(s, 1e-5)) + }); + } + g.finish(); + } +} + +criterion_group!(g, rms_norm); +criterion_main!(g); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/sigmoid.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/sigmoid.rs new file mode 100644 index 0000000..c095b24 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/sigmoid.rs @@ -0,0 +1,34 @@ +#[macro_use] +extern crate criterion; +extern crate tract_linalg; +use criterion::Criterion; + +fn ssigmoid(c: &mut Criterion, n: usize) { + c.bench_function(&format!("ssigmoid_tract_{n}"), move |be| { + let mut s = (0..n).map(|i| i as f32 / 10.0).collect::>(); + let op = &(tract_linalg::ops().sigmoid_f32)(); + be.iter(|| op.run(&mut s)); + }); +} + +#[inline(never)] +fn rust_sigmoid(x: &mut [f32]) { + for v in x { + *v = 1.0 / (1.0 + (-*v).exp()); + } +} + +fn ssigmoid_scalar(c: &mut Criterion, n: usize) { + c.bench_function(&format!("ssigmoid_scalar_{n}"), move |be| { + let mut s = (0..n).map(|i| i as f32 / 10.0).collect::>(); + be.iter(|| rust_sigmoid(&mut s)); + }); +} + +fn bs(c: &mut Criterion) { + ssigmoid(c, 1024); + ssigmoid_scalar(c, 1024); +} + +criterion_group!(benches, bs); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/silu.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/silu.rs new file mode 100644 index 0000000..acc44eb --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/silu.rs @@ -0,0 +1,40 @@ +use criterion::*; +use tract_data::prelude::*; + +use tract_linalg::element_wise::ElementWiseKer; + +fn silu_f32(c: &mut Criterion) { + let mut group = c.benchmark_group("silu_f32"); + group.throughput(Throughput::Elements(1024)); + let mut input = unsafe { Tensor::uninitialized_aligned::(&[1024], 16).unwrap() }; + let input = unsafe { input.as_slice_mut_unchecked::() }; + for (i, x) in input.iter_mut().enumerate() { + *x = (i as f32 / 10.0).sin() * 5.0; + } + group.bench_function("rust_scalar", |b| b.iter(|| rust_scalar(input))); + group.bench_function("linalg", |b| b.iter(|| linalg(input))); + #[cfg(target_arch = "aarch64")] + group.bench_function("linalg-asm-compose", |b| { + b.iter(|| tract_linalg::arm64::arm64simd_silu_f32_4n::run(input, ())) + }); + #[cfg(target_arch = "aarch64")] + group.bench_function("linalg-asm-fused", |b| { + b.iter(|| tract_linalg::arm64::arm64simd_silu_f32_4n_fused::run(input, ())) + }); +} + +#[inline(never)] +fn rust_scalar(input: &mut [f32]) { + for x in input { + let sigmoid = 1.0 / (1.0 + (-*x).exp()); + *x = *x * sigmoid; + } +} + +#[inline(never)] +fn linalg(input: &mut [f32]) { + (tract_linalg::ops().silu_f32)().run(input).unwrap(); +} + +criterion_group!(benches, silu_f32); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/softmax.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/softmax.rs new file mode 100644 index 0000000..4ade182 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/softmax.rs @@ -0,0 +1,170 @@ +use criterion::*; +use tract_data::prelude::*; +use tract_linalg::element_wise::ElementWiseKer; +use tract_linalg::generic::reduce::softmax_l2::{HSoftMaxL2, SSoftMaxL2}; +use tract_linalg::reduce::{MapReduceKer, ReduceKer}; + +#[inline(never)] +fn loop1_f32_naive(slice: &mut [f32]) -> f32 { + let mut max = f32::MIN; + for x in &*slice { + if *x > max { + max = *x; + } + } + max +} + +#[inline(never)] +fn loop2_f32(slice: &mut [f32], max: f32) -> f32 { + let mut sum = 0.; + for x in slice.iter_mut() { + *x = (*x - max).exp(); + sum += *x; + } + sum +} + +#[inline(never)] +fn loop3_f32(slice: &mut [f32], sum: f32) { + let recip = sum.recip(); + for x in slice { + *x *= recip; + } +} + +#[inline(never)] +fn rust_f32(slice: &mut [f32]) { + let max = loop1_f32_naive(slice); + let sum = loop2_f32(slice, max); + loop3_f32(slice, sum); +} + +fn softmax_f32(c: &mut Criterion) { + let mut group = c.benchmark_group("softmax_f32"); + // 1536 = 24*64 = 48*32: a multiple of both the FMA (32) and AVX-512 (64) tile + // widths, 64-byte aligned so both kernels run entirely on their fast aligned + // path (no prefix/suffix scalar fixup) for a fair before/after comparison. + group.throughput(Throughput::Elements(1536)); + let mut input = unsafe { Tensor::uninitialized_aligned::(&[1536], 64).unwrap() }; + let mut plain = input.try_as_plain_mut().unwrap(); + let input = plain.as_slice_mut::().unwrap(); + // Deterministic finite values so every kernel sees identical, well-behaved + // input (uninitialized memory could contain NaN/huge values that perturb the + // fast-compact-exp int conversion and skew the comparison). + for (i, x) in input.iter_mut().enumerate() { + *x = ((i % 97) as f32) * 0.1 - 5.0; + } + group.bench_function("rust", |b| b.iter(|| rust_f32(input))); + group.bench_function("loop1/naive", |b| b.iter(|| loop1_f32_naive(input))); + group.bench_function("loop1/generic", |b| { + b.iter(|| tract_linalg::generic::reduce::max::SMax4::red().run(input)) + }); + #[cfg(target_arch = "x86_64")] + group.bench_function("loop1/iasm", |b| { + b.iter(|| { + tract_linalg::x86_64_fma::max::x86_64_fma_max_f32_32n::red() + .run(input) + .unwrap(); + }) + }); + #[cfg(target_arch = "x86_64")] + if is_x86_feature_detected!("avx512f") { + group.bench_function("loop1/avx512", |b| { + b.iter(|| { + tract_linalg::x86_64_fma::max::x86_64_avx512_max_f32_64n::red() + .run(input) + .unwrap(); + }) + }); + } + #[cfg(target_arch = "aarch64")] + group.bench_function("loop1/intr", |b| { + b.iter(|| { + tract_linalg::arm64::arm64simd_max_f32_16n::red() + .run(input) + .unwrap(); + }) + }); + group.bench_function("loop2/naive", |b| b.iter(|| loop2_f32(input, 1.0))); + group.bench_function("loop2/generic", |b| { + b.iter(|| SSoftMaxL2::red().run_with_params(input, 10.)) + }); + #[cfg(target_arch = "x86_64")] + group.bench_function("loop2/iasm", |b| { + b.iter(|| { + tract_linalg::x86_64_fma::softmax::x86_64_fma_softmax2_fastcompact_f32_32n::red() + .run_with_params(input, 10.) + .unwrap() + }); + }); + #[cfg(target_arch = "x86_64")] + if is_x86_feature_detected!("avx512f") { + group.bench_function("loop2/avx512", |b| { + b.iter(|| { + tract_linalg::x86_64_fma::softmax::x86_64_avx512_softmax2_fastcompact_f32_64n::red() + .run_with_params(input, 10.) + .unwrap() + }); + }); + } + #[cfg(target_arch = "aarch64")] + group.bench_function("loop2/iasm", |b| { + b.iter(|| { + tract_linalg::arm64::arm64simd_softmax2_fastcompact_f32_16n::red() + .run_with_params(input, 0.21) + .unwrap() + }); + }); + group.bench_function("loop3/naive", |b| b.iter(|| loop3_f32(input, 0.21))); + group.bench_function("loop3/generic", |b| { + b.iter(|| { + tract_linalg::generic::by_scalar::SMulByScalar4::ew().run_with_params(input, 0.21) + }) + }); + #[cfg(target_arch = "x86_64")] + group.bench_function("loop3/iasm", |b| { + b.iter(|| { + tract_linalg::x86_64_fma::by_scalar::x86_64_avx_f32_mul_by_scalar_32n::ew() + .run_with_params(input, 0.21) + .unwrap() + }); + }); + #[cfg(target_arch = "aarch64")] + group.bench_function("loop3/iasm", |b| { + b.iter(|| { + tract_linalg::arm64::arm64simd_mul_by_scalar_f32_16n::ew() + .run_with_params(input, 0.21) + .unwrap() + }); + }); +} + +fn softmax_f16(c: &mut Criterion) { + let mut group = c.benchmark_group("softmax_f16"); + // 1536 = 64*24 (multiple of avx512 f16 nr=64 and generic h nr=8). + const N: usize = 1536; + group.throughput(Throughput::Elements(N as u64)); + let mut input = unsafe { Tensor::uninitialized_aligned::(&[N], 64).unwrap() }; + let mut plain = input.try_as_plain_mut().unwrap(); + let input = plain.as_slice_mut::().unwrap(); + for (i, x) in input.iter_mut().enumerate() { + *x = f16::from_f32((i as f32 / 10.0).sin() * 5.0); + } + group.bench_function("loop2/generic", |b| { + b.iter(|| HSoftMaxL2::red().run_with_params(input, f16::from_f32(10.0))) + }); + #[cfg(target_arch = "x86_64")] + if std::is_x86_feature_detected!("avx512f") { + group.bench_function("loop2/avx512", |b| { + b.iter(|| { + tract_linalg::x86_64_fma::softmax::x86_64_avx512_softmax2_fastcompact_f16_64n::red() + .run_with_params(input, f16::from_f32(10.0)) + .unwrap() + }); + }); + } +} + +criterion_group!(benches, softmax_f32, softmax_f16); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/utils.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/utils.rs new file mode 100644 index 0000000..d83cf37 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/utils.rs @@ -0,0 +1,126 @@ +#![allow(dead_code)] +use criterion::*; +use tract_data::internal::*; +use tract_linalg::mmm::{FusedSpec, MMMInputValue, MatMatMul}; + +use DatumType::*; +use tract_linalg::mmm::AsInputValue; + +pub fn packed_packed(c: &mut Criterion, name: &str, m: usize, k: usize, n: usize) { + let mut group = c.benchmark_group(format!("{name}/packed_packed")); + group.throughput(Throughput::Elements((m * k * n) as u64)); + let id = format!("{m}x{k}x{n}"); + group.bench_with_input( + BenchmarkId::new("f32/cold", &id), + &(F32, m, k, n, true), + mat_mat, + ); + group.bench_with_input( + BenchmarkId::new("f32/hot", &id), + &(F32, m, k, n, false), + mat_mat, + ); + group.bench_with_input( + BenchmarkId::new("i8/cold", &id), + &(I8, m, k, n, true), + mat_mat, + ); + group.bench_with_input( + BenchmarkId::new("i8/hot", &id), + &(I8, m, k, n, false), + mat_mat, + ); +} + +pub fn packed_vec(c: &mut Criterion, name: &str, m: usize, k: usize, n: usize) { + assert_eq!(n, 1); + let mut group = c.benchmark_group(format!("{name}/packed_vec")); + group.throughput(Throughput::Elements((m * k * n) as u64)); + let id = format!("{m}x{k}x{n}"); + group.bench_with_input( + BenchmarkId::new("f32/cold", &id), + &(F32, m, k, n, true), + mat_mat, + ); + group.bench_with_input( + BenchmarkId::new("f32/hot", &id), + &(F32, m, k, n, false), + mat_mat, + ); + group.bench_with_input( + BenchmarkId::new("i8/cold", &id), + &(I8, m, k, n, true), + mat_mat, + ); + group.bench_with_input( + BenchmarkId::new("i8/hot", &id), + &(I8, m, k, n, false), + mat_mat, + ); +} + +pub fn ruin_cache() { + let _a = (0..1000000).collect::>(); +} + +#[allow(clippy::too_many_arguments)] +unsafe fn run( + m: usize, + _k: usize, + n: usize, + be: &mut Bencher, + mmm: &dyn MatMatMul, + a: &dyn MMMInputValue, + b: &dyn MMMInputValue, + cold: bool, +) { + let mut scratch = unsafe { mmm.allocate_scratch_space() }; + be.iter_custom(move |iters| { + let mut dur = std::time::Duration::default(); + for _ in 0..iters { + if cold { + ruin_cache(); + } + let instant = std::time::Instant::now(); + unsafe { + mmm.run_with_scratch_space( + m, + n, + scratch.as_mut(), + &[FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(a), + b: AsInputValue::Borrowed(b), + packing: 0, + }], + ) + .unwrap() + }; + let time = instant.elapsed(); + dur += time; + } + dur + }); +} + +fn mat_mat(be: &mut Bencher, params: &(DatumType, usize, usize, usize, bool)) { + let (dt, m, k, n, _) = *params; + let mm = tract_linalg::ops() + .mmm(dt, Some(m), Some(k), Some(n)) + .unwrap(); + mat_mat_with_mm(be, &*mm, params) +} + +pub fn mat_mat_with_mm( + be: &mut Bencher, + mmm: &dyn MatMatMul, + &(dt, m, k, n, cold): &(DatumType, usize, usize, usize, bool), +) { + let a = Tensor::zero_dt(dt, &[m, k]).unwrap(); + let b = Tensor::zero_dt(dt, &[k, n]).unwrap(); + let packing = &mmm.packings()[0]; + let pa = packing.0.prepare_one(&a, 1, 0).unwrap(); + let pb = packing.1.prepare_one(&b, 0, 1).unwrap(); + unsafe { + run(m, k, n, be, mmm, &*pa, &*pb, cold); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/virtual_im2col.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/virtual_im2col.rs new file mode 100644 index 0000000..0f9ffb9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/virtual_im2col.rs @@ -0,0 +1,51 @@ +use criterion::measurement::WallTime; +use criterion::*; +use tract_data::internal::*; + +#[allow(dead_code)] +#[path = "../tests/virtual_im2col.rs"] +mod virtual_im2col; +use virtual_im2col::ConvProblem; + +fn conv( + c: &mut BenchmarkGroup, + ci: usize, + h: usize, + w: usize, + co: usize, + kh: usize, + kw: usize, +) { + // CHW HWIO + let input = Tensor::zero::(&[ci, h, w]).unwrap(); + let filters = Tensor::zero::(&[kh, kw, ci, co]).unwrap(); + let mut cv = ConvProblem { + input, + filters, + lazy_im2col: false, + }; + c.bench_function("eager", |b| { + b.iter(|| { + cv.tract().unwrap(); + }) + }); + cv.lazy_im2col = true; + c.bench_function("lazy", |b| { + b.iter(|| { + cv.tract().unwrap(); + }) + }); +} + +fn ex1(c: &mut Criterion) { + let mut c = c.benchmark_group("ex1"); + conv(&mut c, 32, 256, 256, 32, 3, 3); +} + +fn big(c: &mut Criterion) { + let mut c = c.benchmark_group("big"); + conv(&mut c, 1, 1024, 1024, 99, 3, 3); +} + +criterion_group!(benches, ex1, big); +criterion_main!(benches); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/vnni_i32.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/vnni_i32.rs new file mode 100644 index 0000000..4db122f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/vnni_i32.rs @@ -0,0 +1,70 @@ +#![allow(dead_code)] +// Kernel-level benchmark: AVX-512 VNNI int8 GEMM (avx512vnni_mmm_i32_8x8, VPDPBUSD +// over the K=4-inner PackedI8K4 layout) vs the AVX2 int8 path (avx2_mmm_i32_8x8, +// vpmaddubsw-style widening). Both run the i8i8 packing (index 1) over the same +// M/K/N so the only difference is the matmul inner loop. +use criterion::*; +use tract_data::internal::*; +use tract_linalg::mmm::{AsInputValue, FusedSpec, MatMatMul}; + +fn run_kernel(be: &mut Bencher, mmm: &dyn MatMatMul, m: usize, k: usize, n: usize) { + let a = Tensor::zero_dt(DatumType::I8, &[m, k]).unwrap(); + let b = Tensor::zero_dt(DatumType::I8, &[k, n]).unwrap(); + let (pack_a, pack_b) = &mmm.packings()[1]; + let pa = pack_a.prepare_one(&a, 1, 0).unwrap(); + let pb = pack_b.prepare_one(&b, 0, 1).unwrap(); + let mut scratch = unsafe { mmm.allocate_scratch_space() }; + be.iter_custom(|iters| { + let mut dur = std::time::Duration::default(); + for _ in 0..iters { + let t = std::time::Instant::now(); + unsafe { + mmm.run_with_scratch_space( + m, + n, + scratch.as_mut(), + &[FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa), + b: AsInputValue::Borrowed(&*pb), + packing: 1, + }], + ) + .unwrap() + }; + dur += t.elapsed(); + } + dur + }); +} + +fn benches(c: &mut Criterion) { + if !std::is_x86_feature_detected!("avx512vnni") { + eprintln!("avx512vnni not available, skipping"); + return; + } + use tract_linalg::x86_64_fma::mmm::*; + for &(m, k, n) in &[ + (64usize, 256usize, 64usize), + (256, 256, 256), + (512, 512, 512), + (1024, 1024, 64), + ] { + let id = format!("{m}x{k}x{n}"); + let mut g = c.benchmark_group("vnni_i32/packed_packed"); + g.throughput(Throughput::Elements((m * k * n) as u64)); + g.bench_with_input( + BenchmarkId::new("avx2", &id), + &(m, k, n), + |b, &(m, k, n)| run_kernel(b, &*avx2_mmm_i32_8x8.mmm(), m, k, n), + ); + g.bench_with_input( + BenchmarkId::new("avx512vnni", &id), + &(m, k, n), + |b, &(m, k, n)| run_kernel(b, &*avx512vnni_mmm_i32_8x8.mmm(), m, k, n), + ); + g.finish(); + } +} + +criterion_group!(g, benches); +criterion_main!(g); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/wasm.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/wasm.rs new file mode 100644 index 0000000..875338a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/wasm.rs @@ -0,0 +1,704 @@ +//! WASM kernel microbenches. Run on wasm32 only. +//! +//! RUSTFLAGS='-C target-feature=+simd128' \ +//! CARGO_TARGET_WASM32_WASIP1_RUNNER='wasmtime --env RUST_TEST_NOCAPTURE=1 --' \ +//! cargo bench --release --target wasm32-wasip1 -p tract-linalg --bench wasm +//! +//! Re-run with `+simd128,+relaxed-simd` to compare baseline mul+add against +//! the FMA emit driven by the `madd_f32x4!` macro in `linalg/src/wasm.rs`. + +#[cfg(not(target_arch = "wasm32"))] +fn main() { + eprintln!("this bench only runs on wasm32 targets — skipping on host"); +} + +#[cfg(target_arch = "wasm32")] +fn main() { + let target = if cfg!(target_feature = "relaxed-simd") { + "+simd128,+relaxed-simd (FMA)" + } else { + "+simd128 only (mul+add)" + }; + + eprintln!("=== WASM 8x8 GEMM microbench ({target}) ==="); + bench_8x8::run(); + + eprintln!(); + eprintln!("=== Isolated 32x1 GEMV microbench ({target}) ==="); + bench_32x1::run(); + + eprintln!(); + eprintln!("=== Isolated 16x1 GEMV microbench ({target}) ==="); + bench_16x1::run(); + + eprintln!(); + eprintln!("=== int8 (i8->i32) 4x4 GEMM: wasm SIMD vs generic scalar ({target}) ==="); + bench_i8_4x4::run(); + + #[cfg(target_feature = "relaxed-simd")] + { + eprintln!(); + eprintln!("=== int8 relaxed-dot prototype: relaxed_dot vs widening (4x4 tile) ==="); + bench_relaxed_dot::run(); + } + #[cfg(not(target_feature = "relaxed-simd"))] + eprintln!("\n(int8 relaxed-dot prototype skipped — rebuild with +relaxed-simd)"); +} + +#[cfg(target_arch = "wasm32")] +mod bench_8x8 { + //! Microbench: time `wasm_f32_8x8` (the GEMM kernel for N>=2) at shapes + //! relevant to DFN3, transformer FFN, and CNN→GEMM workloads. + + use std::time::Instant; + use tract_data::internal::*; + use tract_linalg::mmm::{AsInputValue, FusedSpec}; + + fn run_one( + kernel: &dyn tract_linalg::mmm::MatMatMul, + m: usize, + k: usize, + n: usize, + iters: usize, + ) -> f64 { + let packing = &kernel.packings()[0]; + let a = Tensor::zero::(&[m, k]).unwrap(); + let pa = packing.0.prepare_one(&a, 1, 0).unwrap(); + let b = Tensor::zero::(&[k, n]).unwrap(); + let pb = packing.1.prepare_one(&b, 0, 1).unwrap(); + let mut c = Tensor::zero::(&[m, n]).unwrap(); + + for _ in 0..50 { + unsafe { + kernel + .run( + m, + n, + &[ + FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa), + b: AsInputValue::Borrowed(&*pb), + packing: 0, + }, + FusedSpec::Store(kernel.c_view(Some(0), Some(1)).wrap(&c.view_mut())), + ], + ) + .unwrap(); + } + } + + let t0 = Instant::now(); + for _ in 0..iters { + unsafe { + kernel + .run( + m, + n, + &[ + FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa), + b: AsInputValue::Borrowed(&*pb), + packing: 0, + }, + FusedSpec::Store(kernel.c_view(Some(0), Some(1)).wrap(&c.view_mut())), + ], + ) + .unwrap(); + } + } + let elapsed = t0.elapsed(); + elapsed.as_secs_f64() / iters as f64 * 1e9 + } + + fn pick(name: &str) -> Box { + let mut ops = tract_linalg::generic(); + tract_linalg::wasm::plug(&mut ops); + for impl_ in ops.mmm_impls() { + if impl_.name() == name { + return impl_.clone(); + } + } + panic!("kernel {name} not registered") + } + + fn bench_shape(label: &str, m: usize, k: usize, n: usize, iters: usize) { + let k88 = pick("wasm_f32_8x8"); + let ns = run_one(&*k88, m, k, n, iters); + let m_tiles = m.div_ceil(8); + let n_tiles = n.div_ceil(8); + let total_tiles = m_tiles * n_tiles; + let per_tile_ns = ns / total_tiles as f64; + eprintln!( + "{label} (m={m} k={k} n={n}, iters={iters}): {ns:.0} ns/call \ + ({total_tiles} 8x8 tiles, {per_tile_ns:.1} ns/tile)" + ); + } + + pub fn run() { + // DFN3 N>1 GEMM case (the primary 8x8 hit on DFN3). + bench_shape("DFN3-style m=64 k=64 n=8", 64, 64, 8, 50_000); + // Larger N — typical batched/transformer GEMM. + bench_shape("m=64 k=64 n=64", 64, 64, 64, 10_000); + bench_shape("m=128 k=128 n=8", 128, 128, 8, 20_000); + bench_shape("m=128 k=128 n=64", 128, 128, 64, 5_000); + bench_shape("m=256 k=256 n=8", 256, 256, 8, 5_000); + bench_shape("m=256 k=256 n=64", 256, 256, 64, 1_000); + // Whisper-tiny FFN-ish (large K, small N). + bench_shape("m=384 k=1536 n=8", 384, 1536, 8, 1_000); + } +} + +#[cfg(target_arch = "wasm32")] +mod bench_32x1 { + //! Isolated, statistics-aware microbench for `wasm_f32_32x1` to investigate + //! the apparent regression at M=100/256 in `microbench_dispatch_gemv`. That + //! bench loops all 4 GEMV kernels back-to-back at every shape, biasing the + //! later-running kernel (32x1) with cache contention and thermal buildup. + //! This module benches 32x1 alone, with min-of-N reporting across + //! repetitions to expose variance honestly. + + use std::time::Instant; + use tract_data::internal::*; + use tract_linalg::mmm::{AsInputValue, FusedSpec}; + + fn run_one(kernel: &dyn tract_linalg::mmm::MatMatMul, m: usize, k: usize, iters: usize) -> f64 { + let packing = &kernel.packings()[0]; + let a = Tensor::zero::(&[m, k]).unwrap(); + let pa = packing.0.prepare_one(&a, 1, 0).unwrap(); + let b = Tensor::zero::(&[k, 1]).unwrap(); + let pb = packing.1.prepare_one(&b, 0, 1).unwrap(); + let mut c = Tensor::zero::(&[m, 1]).unwrap(); + + // Generous warmup — 200 calls primes the JIT and hot caches. + for _ in 0..200 { + unsafe { + kernel + .run( + m, + 1, + &[ + FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa), + b: AsInputValue::Borrowed(&*pb), + packing: 0, + }, + FusedSpec::Store(kernel.c_view(Some(0), Some(0)).wrap(&c.view_mut())), + ], + ) + .unwrap(); + } + } + + let t0 = Instant::now(); + for _ in 0..iters { + unsafe { + kernel + .run( + m, + 1, + &[ + FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa), + b: AsInputValue::Borrowed(&*pb), + packing: 0, + }, + FusedSpec::Store(kernel.c_view(Some(0), Some(0)).wrap(&c.view_mut())), + ], + ) + .unwrap(); + } + } + let elapsed = t0.elapsed(); + elapsed.as_secs_f64() / iters as f64 * 1e9 + } + + fn pick(name: &str) -> Box { + let mut ops = tract_linalg::generic(); + tract_linalg::wasm::plug(&mut ops); + for impl_ in ops.mmm_impls() { + if impl_.name() == name { + return impl_.clone(); + } + } + panic!("kernel {name} not registered") + } + + fn bench_min_of_n(label: &str, m: usize, k: usize, iters: usize, repetitions: usize) { + let kernel = pick("wasm_f32_32x1"); + let mut samples: Vec = Vec::with_capacity(repetitions); + for _ in 0..repetitions { + samples.push(run_one(&*kernel, m, k, iters)); + } + samples.sort_by(|a, b| a.partial_cmp(b).unwrap()); + let min = samples[0]; + let median = samples[samples.len() / 2]; + let max = samples[samples.len() - 1]; + let pct_spread = (max - min) / min * 100.0; + eprintln!( + "{label} (m={m} k={k}, {iters} iters × {repetitions} reps): \ + min={min:.0} median={median:.0} max={max:.0} ns/call (spread {pct_spread:.0}%)" + ); + } + + pub fn run() { + // Suspect shapes from microbench_dispatch_gemv (apparent regression): + bench_min_of_n("M=100 k=256", 100, 256, 10_000, 10); + bench_min_of_n("M=256 k=256", 256, 256, 5_000, 10); + bench_min_of_n("M=256 k=512", 256, 512, 2_000, 10); + // Reference shapes (showed clean speedup before): + bench_min_of_n("M=24 k=256", 24, 256, 30_000, 10); + bench_min_of_n("M=64 k=96", 64, 96, 20_000, 10); + } +} + +#[cfg(target_arch = "wasm32")] +mod bench_16x1 { + //! Isolated 16x1 GEMV microbench — same methodology as bench_32x1. + //! 16x1 has 4 SIMD accumulators per K-step, which under +relaxed-simd + //! exposes the destructive-fmla accumulator recurrence (4-cycle latency + //! throttling throughput to 1 FMA/cycle even though Apple Silicon pipes + //! can do 4). Used to validate that the fix in linalg/src/wasm.rs (which + //! routes 16x1 through `madd_f32x4_nofma!` to use separate mul+add) + //! recovers the regression PR #2199 missed. + use std::time::Instant; + use tract_data::internal::*; + use tract_linalg::mmm::{AsInputValue, FusedSpec}; + + fn run_one(kernel: &dyn tract_linalg::mmm::MatMatMul, m: usize, k: usize, iters: usize) -> f64 { + let packing = &kernel.packings()[0]; + let a = Tensor::zero::(&[m, k]).unwrap(); + let pa = packing.0.prepare_one(&a, 1, 0).unwrap(); + let b = Tensor::zero::(&[k, 1]).unwrap(); + let pb = packing.1.prepare_one(&b, 0, 1).unwrap(); + let mut c = Tensor::zero::(&[m, 1]).unwrap(); + + for _ in 0..200 { + unsafe { + kernel + .run( + m, + 1, + &[ + FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa), + b: AsInputValue::Borrowed(&*pb), + packing: 0, + }, + FusedSpec::Store(kernel.c_view(Some(0), Some(0)).wrap(&c.view_mut())), + ], + ) + .unwrap(); + } + } + + let t0 = Instant::now(); + for _ in 0..iters { + unsafe { + kernel + .run( + m, + 1, + &[ + FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa), + b: AsInputValue::Borrowed(&*pb), + packing: 0, + }, + FusedSpec::Store(kernel.c_view(Some(0), Some(0)).wrap(&c.view_mut())), + ], + ) + .unwrap(); + } + } + let elapsed = t0.elapsed(); + elapsed.as_secs_f64() / iters as f64 * 1e9 + } + + fn pick(name: &str) -> Box { + let mut ops = tract_linalg::generic(); + tract_linalg::wasm::plug(&mut ops); + for impl_ in ops.mmm_impls() { + if impl_.name() == name { + return impl_.clone(); + } + } + panic!("kernel {name} not registered") + } + + fn bench_min_of_n(label: &str, m: usize, k: usize, iters: usize, repetitions: usize) { + let kernel = pick("wasm_f32_16x1"); + let mut samples: Vec = Vec::with_capacity(repetitions); + for _ in 0..repetitions { + samples.push(run_one(&*kernel, m, k, iters)); + } + samples.sort_by(|a, b| a.partial_cmp(b).unwrap()); + let min = samples[0]; + let median = samples[samples.len() / 2]; + let max = samples[samples.len() - 1]; + let pct_spread = (max - min) / min * 100.0; + eprintln!( + "{label} (m={m} k={k}, {iters} iters × {repetitions} reps): \ + min={min:.0} median={median:.0} max={max:.0} ns/call (spread {pct_spread:.0}%)" + ); + } + + pub fn run() { + // 16x1's natural band per plug()'s mmv_f32 closure: M ∈ 9..=16 + bench_min_of_n("M=9 k=256", 9, 256, 30_000, 10); + bench_min_of_n("M=12 k=256", 12, 256, 30_000, 10); + bench_min_of_n("M=16 k=96", 16, 96, 30_000, 10); + bench_min_of_n("M=16 k=256", 16, 256, 20_000, 10); + bench_min_of_n("M=16 k=512", 16, 512, 10_000, 10); + bench_min_of_n("M=16 k=1024", 16, 1024, 5_000, 10); + } +} + +#[cfg(target_arch = "wasm32")] +mod bench_i8_4x4 { + //! int8 (i8->i32) GEMM microbench: the new SIMD `wasm_i32_4x4` vs the scalar + //! `generic_i32_4x4` fallback. Both kernels expose the *identical* i8i8 + //! PackedI8K4 packing (packing index 1), the same 4x4 tile and i32 + //! accumulator — so the ratio is a clean read on what the SIMD + //! widening-extmul AddMatMul buys over the generic scalar loop. min-of-N + //! reporting per kernel to keep the variance honest. + + use std::time::Instant; + use tract_data::internal::*; + use tract_linalg::mmm::{AsInputValue, FusedSpec, MatMatMul}; + + // i8i8 packing slot is index 1 on both generic_i32_4x4 and wasm_i32_4x4. + const I8I8: usize = 1; + + fn run_one(kernel: &dyn MatMatMul, m: usize, k: usize, n: usize, iters: usize) -> f64 { + let packing = &kernel.packings()[I8I8]; + let a = Tensor::zero::(&[m, k]).unwrap(); + let pa = packing.0.prepare_one(&a, 1, 0).unwrap(); + let b = Tensor::zero::(&[k, n]).unwrap(); + let pb = packing.1.prepare_one(&b, 0, 1).unwrap(); + let mut c = Tensor::zero::(&[m, n]).unwrap(); + + // Warmup: prime the JIT and hot caches. + for _ in 0..50 { + unsafe { + kernel + .run( + m, + n, + &[ + FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa), + b: AsInputValue::Borrowed(&*pb), + packing: I8I8, + }, + FusedSpec::Store(kernel.c_view(Some(0), Some(1)).wrap(&c.view_mut())), + ], + ) + .unwrap(); + } + } + + let t0 = Instant::now(); + for _ in 0..iters { + unsafe { + kernel + .run( + m, + n, + &[ + FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa), + b: AsInputValue::Borrowed(&*pb), + packing: I8I8, + }, + FusedSpec::Store(kernel.c_view(Some(0), Some(1)).wrap(&c.view_mut())), + ], + ) + .unwrap(); + } + } + let elapsed = t0.elapsed(); + elapsed.as_secs_f64() / iters as f64 * 1e9 + } + + fn pick(name: &str) -> Box { + let mut ops = tract_linalg::generic(); + tract_linalg::wasm::plug(&mut ops); + for impl_ in ops.mmm_impls() { + if impl_.name() == name { + return impl_.clone(); + } + } + panic!("kernel {name} not registered") + } + + fn min_of_n( + kernel: &dyn MatMatMul, + m: usize, + k: usize, + n: usize, + iters: usize, + reps: usize, + ) -> f64 { + let mut samples: Vec = (0..reps).map(|_| run_one(kernel, m, k, n, iters)).collect(); + samples.sort_by(|a, b| a.partial_cmp(b).unwrap()); + samples[0] + } + + fn bench(label: &str, m: usize, k: usize, n: usize, iters: usize, reps: usize) { + let wasm = pick("wasm_i32_4x4"); + let generic = pick("generic_i32_4x4"); + let w = min_of_n(&*wasm, m, k, n, iters, reps); + let g = min_of_n(&*generic, m, k, n, iters, reps); + let tiles = m.div_ceil(4) * n.div_ceil(4); + eprintln!( + "{label} (m={m} k={k} n={n}, {iters} iters × {reps} reps): \ + wasm={w:.0} generic={g:.0} ns/call speedup={:.2}x \ + ({tiles} 4x4 tiles, wasm {:.1} ns/tile)", + g / w, + w / tiles as f64 + ); + } + + pub fn run() { + // Square GEMMs across sizes (compute-bound, the SIMD path's home turf). + bench("square m=64 k=64 n=64", 64, 64, 64, 5_000, 8); + bench("square m=128 k=128 n=128", 128, 128, 128, 1_000, 8); + bench("square m=256 k=256 n=256", 256, 256, 256, 200, 8); + // Transformer-ish: large K, moderate M/N (MiniLM/FFN projections). + bench("m=128 k=384 n=384", 128, 384, 384, 500, 8); + bench("m=64 k=1536 n=64", 64, 1536, 64, 1_000, 8); + // CNN→GEMM (InceptionV1-style im2col), small N. + bench("m=256 k=256 n=16", 256, 256, 16, 2_000, 8); + } +} + +// Prototype: int8 4x4 tile via `i32x4_relaxed_dot_i8x16_i7x16_add` (SDOT-analog, +// 4 i8 MACs/lane, no widening) vs the deterministic widening path. Only compiles +// under +relaxed-simd. Isolates a single cache-resident 4x4 tile so the ratio is +// a pure instruction-density read. Includes a bit-exactness check on wasmtime. +#[cfg(all(target_arch = "wasm32", target_feature = "relaxed-simd"))] +mod bench_relaxed_dot { + use std::arch::wasm32::*; + use std::hint::black_box; + use std::time::Instant; + + // Logical A is [4][k] row-major (a[m*k + ik]); logical B is [k][4] row-major + // (b[ik*4 + n]). Reference 4x4 = sum_ik A[m][ik] * B[ik][n]. + fn reference_tile(a: &[i8], b: &[i8], k: usize) -> [i32; 16] { + let mut c = [0i32; 16]; + for ik in 0..k { + for m in 0..4 { + for n in 0..4 { + c[m * 4 + n] += a[m * k + ik] as i32 * b[ik * 4 + n] as i32; + } + } + } + c + } + + // K-major A: out[ik*4 + m] = A[m][ik] (m inner) — what the widening kernel reads. + fn pack_a_kmajor(a: &[i8], k: usize) -> Vec { + let mut o = vec![0i8; k * 4]; + for ik in 0..k { + for m in 0..4 { + o[ik * 4 + m] = a[m * k + ik]; + } + } + o + } + // K-major B is exactly the logical [ik*4 + n] layout already. + + // M-major A, K contiguous, K padded to mult of 4: out[m*kp + ik] = A[m][ik]. + fn pack_a_mmajor(a: &[i8], k: usize) -> (Vec, usize) { + let kp = k.div_ceil(4) * 4; + let mut o = vec![0i8; 4 * kp]; + for m in 0..4 { + for ik in 0..k { + o[m * kp + ik] = a[m * k + ik]; + } + } + (o, kp) + } + // K=4-inner B: out[kb*16 + n*4 + kr] = B[4kb+kr][n] — the relaxed-dot layout. + fn pack_b_k4(b: &[i8], k: usize) -> Vec { + let kp = k.div_ceil(4) * 4; + let mut o = vec![0i8; kp * 4]; + for kb in 0..kp / 4 { + for kr in 0..4 { + let kk = 4 * kb + kr; + if kk >= k { + continue; + } + for n in 0..4 { + o[kb * 16 + n * 4 + kr] = b[kk * 4 + n]; + } + } + } + o + } + + // Current deterministic approach: widen B to i32x4 per k, splat A, mul+add. + unsafe fn widening_tile(a_km: *const i8, b_km: *const i8, k: usize) -> [i32; 16] { + unsafe { + let mut acc = [i32x4_splat(0); 4]; + for ik in 0..k { + let bw = v128_load32_zero(b_km.add(4 * ik) as *const u32); + let bw = i16x8_extend_low_i8x16(bw); + let bw = i32x4_extend_low_i16x8(bw); + let ar = a_km.add(4 * ik); + acc[0] = i32x4_add(acc[0], i32x4_mul(i32x4_splat(*ar.add(0) as i32), bw)); + acc[1] = i32x4_add(acc[1], i32x4_mul(i32x4_splat(*ar.add(1) as i32), bw)); + acc[2] = i32x4_add(acc[2], i32x4_mul(i32x4_splat(*ar.add(2) as i32), bw)); + acc[3] = i32x4_add(acc[3], i32x4_mul(i32x4_splat(*ar.add(3) as i32), bw)); + } + let mut c = [0i32; 16]; + for m in 0..4 { + v128_store(c[m * 4..].as_mut_ptr() as *mut v128, acc[m]); + } + c + } + } + + // Relaxed-dot: per 4-K block, one v128 B-load shared across 4 rows; each row + // broadcasts its 4 K-bytes and issues one relaxed_dot. 64 MACs in 4 dots. + unsafe fn relaxed_tile(apk: *const i8, bpk: *const i8, kp: usize) -> [i32; 16] { + unsafe { + let mut acc = [i32x4_splat(0); 4]; + for kb in 0..kp / 4 { + let b_all = v128_load(bpk.add(kb * 16) as *const v128); + for m in 0..4 { + let a4 = (apk.add(m * kp + kb * 4) as *const i32).read_unaligned(); + let a_m = i32x4_splat(a4); + acc[m] = i32x4_relaxed_dot_i8x16_i7x16_add(a_m, b_all, acc[m]); + } + } + let mut c = [0i32; 16]; + for m in 0..4 { + v128_store(c[m * 4..].as_mut_ptr() as *mut v128, acc[m]); + } + c + } + } + + fn gen_data(k: usize, seed: i32, bits7: bool) -> Vec { + (0..k * 4) + .map(|i| { + let v = ((i as i32) + .wrapping_mul(97) + .wrapping_add(seed) + .wrapping_mul(31)) + & 0xff; + let v = (v - 128) as i8; // full i8 range + if bits7 { + (v as i32).clamp(-63, 63) as i8 + } else { + v + } + }) + .collect() + } + + fn check(label: &str, k: usize, b_bits7: bool) { + let a = gen_data(k, 1, false); + let b = gen_data(k, 7, b_bits7); + let reference = reference_tile(&a, &b, k); + + let a_km = pack_a_kmajor(&a, k); + let w = unsafe { widening_tile(a_km.as_ptr(), b.as_ptr(), k) }; + assert_eq!(w, reference, "widening_tile mismatch ({label})"); + + let (a_mm, kp) = pack_a_mmajor(&a, k); + let b_k4 = pack_b_k4(&b, k); + let r = unsafe { relaxed_tile(a_mm.as_ptr(), b_k4.as_ptr(), kp) }; + let exact = r == reference; + eprintln!( + " correctness {label} (k={k}, B={}): widening=exact relaxed={}", + if b_bits7 { "7-bit" } else { "full-i8" }, + if exact { + "EXACT" + } else { + "DIFFERS (non-deterministic intermediate)" + } + ); + if b_bits7 { + assert!(exact, "relaxed_dot must be exact when B is 7-bit ({label})"); + } + } + + fn time_relaxed(apk: &[i8], bpk: &[i8], kp: usize, iters: usize) -> f64 { + let mut sink = 0i32; + for _ in 0..50 { + sink ^= unsafe { relaxed_tile(apk.as_ptr(), bpk.as_ptr(), kp) }[0]; + } + let t0 = Instant::now(); + for _ in 0..iters { + let c = unsafe { + relaxed_tile( + black_box(apk).as_ptr(), + black_box(bpk).as_ptr(), + black_box(kp), + ) + }; + sink ^= c[5]; + } + black_box(sink); + t0.elapsed().as_secs_f64() / iters as f64 * 1e9 + } + + fn time_widening(a_km: &[i8], b_km: &[i8], k: usize, iters: usize) -> f64 { + let mut sink = 0i32; + for _ in 0..50 { + sink ^= unsafe { widening_tile(a_km.as_ptr(), b_km.as_ptr(), k) }[0]; + } + let t0 = Instant::now(); + for _ in 0..iters { + let c = unsafe { + widening_tile( + black_box(a_km).as_ptr(), + black_box(b_km).as_ptr(), + black_box(k), + ) + }; + sink ^= c[5]; + } + black_box(sink); + t0.elapsed().as_secs_f64() / iters as f64 * 1e9 + } + + fn min_of_n(f: &mut dyn FnMut() -> f64, reps: usize) -> f64 { + let mut s: Vec = (0..reps).map(|_| f()).collect(); + s.sort_by(|a, b| a.partial_cmp(b).unwrap()); + s[0] + } + + fn bench(k: usize, iters: usize, reps: usize) { + let a = gen_data(k, 1, false); + let b = gen_data(k, 7, false); + let a_km = pack_a_kmajor(&a, k); + let (a_mm, kp) = pack_a_mmajor(&a, k); + let b_k4 = pack_b_k4(&b, k); + + let w = min_of_n(&mut || time_widening(&a_km, &b, k, iters), reps); + let r = min_of_n(&mut || time_relaxed(&a_mm, &b_k4, kp, iters), reps); + eprintln!( + " 4x4 tile k={k} ({iters} iters × {reps} reps): \ + widening={w:.1} relaxed={r:.1} ns/call speedup={:.2}x", + w / r + ); + } + + pub fn run() { + // Bit-exactness on wasmtime: full-i8 (engine-dependent intermediate) and + // 7-bit B (guaranteed no i16 overflow → deterministic on any engine). + check("k=64", 64, false); + check("k=64", 64, true); + check("k=260-padded", 260, false); + check("k=260-padded", 260, true); + eprintln!(); + // Throughput: single cache-resident 4x4 tile across K depths. + bench(64, 200_000, 8); + bench(256, 50_000, 8); + bench(1024, 10_000, 8); + bench(1536, 8_000, 8); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/x86_64.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/x86_64.rs new file mode 100644 index 0000000..4ed58af --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/benches/x86_64.rs @@ -0,0 +1,554 @@ +#![allow( + dead_code, + non_upper_case_globals, + unused_macros, + non_snake_case, + unused_assignments +)] + +use std::arch::asm; + +// mod nano; + +#[repr(C, align(64))] +struct Floats([f32; 256 * 1024 * 64]); +const _F32: Floats = Floats([12.; 256 * 1024 * 64]); +const F32: *const f32 = (&_F32) as *const Floats as *const f32; + +lazy_static::lazy_static! { + static ref TICK: f64 = unsafe { b8192!(asm!("or rax, rax", out("rax") _)) }; +} + +macro_rules! kloop { + ($filter: expr, $geo: literal, $n: expr, $path: literal, $ww: expr, $u: expr, $arch: expr) => { + let label = $path.split("/").last().unwrap().split_once(".").unwrap().0; + let full_label = format!("{:8} {:40}", $geo, label); + let repeats = 32; + let ks = 256; + if full_label.contains($filter.unwrap_or("")) { + let time = b1!({ + + let mut p = F32; + let mut q = F32; + let mut k = ks; + let mut r = repeats; + asm!( + concat!(r#" +2: + mov rax, r9 + mov rcx, r10 + mov r8, r12 +3: + "#, include_str!( concat!("../x86_64/", $arch, "/", $path)), "\n sub r8, ", $u, r#" +jnz 3b + +sub r11, 1 +jnz 2b +"#), + inout("r9") p, inout("r10") q, inout("r12") k, inout("r11") r, out("rax") _, out("rcx") _, + out("r8") _, + out("zmm0") _, out("zmm1") _, out("zmm2") _, out("zmm3") _, + out("zmm4") _, out("zmm5") _, out("zmm6") _, out("zmm7") _, + out("zmm8") _, out("zmm9") _, out("zmm10") _, out("zmm11") _, + out("zmm12") _, out("zmm13") _, out("zmm14") _, out("zmm15") _, + out("zmm20") _, out("zmm21") _, out("zmm22") _, out("zmm23") _, + out("zmm24") _, out("zmm25") _, out("zmm26") _, out("zmm27") _, + out("zmm28") _, out("zmm29") _, out("zmm30") _, out("zmm31") _, + ); + }); + + // We have k=1024 * 64 but some tests step twice per iteration + let iterations = (ks * repeats / $u); + // Those that step twice process twice as many elements per iteration + let elems_per_iteration = $n * $u; + + let time_per_iteration = time / iterations as f64; + + let total_floats = elems_per_iteration * iterations; + let flops = total_floats as f64 / time; + + let total_time_ms = time * 1e6; + let fmas_per_iteration = ($n as f64 / $ww as f64) * $u as f64; + let ticks_per_iteration = time_per_iteration / *TICK; + println!("{} {:3.5} {:3.0}% ({:>5.2 }/{:3 } cy) {:.2} GFLOP/s", full_label, total_time_ms, fmas_per_iteration / ticks_per_iteration * 100., ticks_per_iteration, fmas_per_iteration, flops / 1e9 ); + } + }; + + ($filter: expr, $geo: literal, $n: expr, $path: literal, $ww: expr) => { + kloop!($filter, $geo, $n, $path, $ww, 1, "fma") + }; + ($filter: expr, $geo: literal, $n: expr, $path: literal, $ww: expr, $u: expr) => { + kloop!($filter, $geo, $n, $path, $ww, $u, "fma") + }; +} + +unsafe fn packed_packed_1x12(f: Option<&str>) { + println!("-- 1x12 kernels"); + if std::is_x86_feature_detected!("avx512f") { + kloop!( + f, + "1x12x1", + (16 * 1 * 12), + "1x12/packed_packed_loop1/avx-512.tmpli", + 16, + 1, + "avx512" + ); + } + println!(); +} + +unsafe fn packed_packed_1x8(f: Option<&str>) { + println!("-- 1x8 kernels"); + kloop!(f, "1x8x1", (8 * 8), "8x8/packed_packed_loop1/avx.tmpli", 8); + kloop!( + f, + "1x8x2", + (8 * 8), + "8x8/packed_packed_loop1/avx-unroll.tmpli", + 8, + 2 + ); + if std::is_x86_feature_detected!("avx512f") { + kloop!( + f, + "1x8x1", + (16 * 1 * 8), + "8x8/packed_packed_loop1/avx-512.tmpli", + 16, + 1, + "avx512" + ); + } + println!(); +} + +unsafe fn packed_packed_2x6(f: Option<&str>) { + println!("-- 2x6 kernels"); + kloop!( + f, + "2x6x1", + (16 * 6), + "2x6/packed_packed_loop1/original.tmpli", + 8 + ); + kloop!( + f, + "2x6x2", + (16 * 6), + "2x6/packed_packed_loop1/original-unroll.tmpli", + 8, + 2 + ); + if std::is_x86_feature_detected!("avx512f") { + kloop!( + f, + "2x6x1", + (16 * 2 * 6), + "2x6/packed_packed_loop1/avx-512.tmpli", + 16, + 1, + "avx512" + ); + kloop!( + f, + "2x6x2", + (16 * 2 * 6), + "2x6/packed_packed_loop1/avx-512-unroll.tmpli", + 16, + 2, + "avx512" + ); + } + println!(); +} + +unsafe fn packed_packed_2x5(f: Option<&str>) { + println!("-- 2x5 kernels"); + kloop!(f, "2x5x1", (16 * 5), "2x5/packed_packed_loop1/avx.tmpli", 8); + kloop!( + f, + "2x5x2", + (16 * 5), + "2x5/packed_packed_loop1/avx-unroll.tmpli", + 8, + 2 + ); + if std::is_x86_feature_detected!("avx512f") { + kloop!( + f, + "2x5x1", + (32 * 5), + "2x5/packed_packed_loop1/avx-512.tmpli", + 16, + 1, + "avx512" + ); + kloop!( + f, + "2x5x2", + (32 * 5), + "2x5/packed_packed_loop1/avx-512-unroll.tmpli", + 16, + 2, + "avx512" + ); + } + println!(); +} + +unsafe fn packed_packed_3x4(f: Option<&str>) { + println!("-- 3x4 kernels"); + kloop!(f, "3x4x1", (24 * 4), "3x4/packed_packed_loop1/avx.tmpli", 8); + kloop!( + f, + "3x4x2", + (24 * 4), + "3x4/packed_packed_loop1/avx-unroll.tmpli", + 8, + 2 + ); + if std::is_x86_feature_detected!("avx512f") { + kloop!( + f, + "3x4x1", + (16 * 3 * 4), + "3x4/packed_packed_loop1/avx-512.tmpli", + 16, + 1, + "avx512" + ); + kloop!( + f, + "3x4x2", + (16 * 3 * 4), + "3x4/packed_packed_loop1/avx-512-unroll.tmpli", + 16, + 2, + "avx512" + ); + } + println!(); +} + +unsafe fn packed_packed_4x3(f: Option<&str>) { + println!("-- 4x3 kernels"); + kloop!(f, "4x3x1", (32 * 3), "4x3/packed_packed_loop1/avx.tmpli", 8); + kloop!( + f, + "4x3x2", + (32 * 3), + "4x3/packed_packed_loop1/avx-unroll.tmpli", + 8, + 2 + ); + if std::is_x86_feature_detected!("avx512f") { + kloop!( + f, + "4x3x1", + (16 * 4 * 3), + "4x3/packed_packed_loop1/avx-512.tmpli", + 16, + 1, + "avx512" + ); + kloop!( + f, + "4x3x2", + (16 * 4 * 3), + "4x3/packed_packed_loop1/avx-512-unroll.tmpli", + 16, + 2, + "avx512" + ); + } + println!(); +} + +unsafe fn packed_packed_5x2(f: Option<&str>) { + println!("-- 5x2 kernels"); + kloop!(f, "5x2x1", (40 * 2), "5x2/packed_packed_loop1/avx.tmpli", 8); + kloop!( + f, + "5x2x1", + (40 * 2), + "5x2/packed_packed_loop1/avx-unroll.tmpli", + 8, + 2 + ); + if std::is_x86_feature_detected!("avx512f") { + kloop!( + f, + "5x2x1", + (16 * 5 * 2), + "5x2/packed_packed_loop1/avx-512.tmpli", + 16, + 1, + "avx512" + ); + kloop!( + f, + "5x2x2", + (16 * 5 * 2), + "5x2/packed_packed_loop1/avx-512-unroll.tmpli", + 16, + 2, + "avx512" + ); + } + println!(); +} + +unsafe fn packed_packed_6x2(f: Option<&str>) { + println!("-- 6x2 kernels"); + kloop!(f, "6x2x1", (48 * 2), "6x2/packed_packed_loop1/avx.tmpli", 8); + kloop!( + f, + "6x2x2", + (48 * 2), + "6x2/packed_packed_loop1/avx-unroll.tmpli", + 8, + 2 + ); + if std::is_x86_feature_detected!("avx512f") { + kloop!( + f, + "6x2x1", + (16 * 6 * 2), + "6x2/packed_packed_loop1/avx-512.tmpli", + 16, + 1, + "avx512" + ); + kloop!( + f, + "6x2x2", + (16 * 6 * 2), + "6x2/packed_packed_loop1/avx-512-unroll.tmpli", + 16, + 2, + "avx512" + ); + } + println!(); +} + +unsafe fn packed_packed_8x2(f: Option<&str>) { + println!("-- 8x2 kernels"); + if std::is_x86_feature_detected!("avx512f") { + kloop!( + f, + "8x2x1", + (16 * 8 * 2), + "8x2/packed_packed_loop1/avx-512.tmpli", + 16, + 1, + "avx512" + ); + } + println!(); +} + +unsafe fn packed_packed_8x1(f: Option<&str>) { + println!("-- 8x1 kernels"); + kloop!(f, "8x1x1", (64 * 1), "8x1/packed_packed_loop1/avx.tmpli", 8); + kloop!( + f, + "8x1x2", + (64 * 1), + "8x1/packed_packed_loop1/avx-unroll.tmpli", + 8, + 2 + ); + if std::is_x86_feature_detected!("avx512f") { + kloop!( + f, + "8x1x1", + (16 * 8 * 1), + "8x1/packed_packed_loop1/avx-512.tmpli", + 16, + 1, + "avx512" + ); + kloop!( + f, + "8x1x2", + (16 * 8 * 1), + "8x1/packed_packed_loop1/avx-512-unroll.tmpli", + 16, + 2, + "avx512" + ); + } + println!(); +} + +unsafe fn packed_packed_6x1(f: Option<&str>) { + println!("-- 6x1 kernels"); + kloop!(f, "6x1x1", (48 * 1), "6x1/packed_packed_loop1/avx.tmpli", 8); + kloop!( + f, + "6x1x2", + (48 * 1), + "6x1/packed_packed_loop1/avx-unroll.tmpli", + 8, + 2 + ); + if std::is_x86_feature_detected!("avx512f") { + kloop!( + f, + "6x1x1", + (16 * 6 * 1), + "6x1/packed_packed_loop1/avx-512.tmpli", + 16, + 1, + "avx512" + ); + kloop!( + f, + "6x1x2", + (16 * 6 * 1), + "6x1/packed_packed_loop1/avx-512-unroll.tmpli", + 16, + 2, + "avx512" + ); + } + println!(); +} + +unsafe fn packed_packed_7x1(f: Option<&str>) { + println!("-- 7x1 kernels"); + if std::is_x86_feature_detected!("avx512f") { + kloop!( + f, + "7x1x1", + (16 * 7 * 1), + "7x1/packed_packed_loop1/avx-512.tmpli", + 16, + 1, + "avx512" + ); + kloop!( + f, + "7x1x2", + (16 * 7 * 1), + "7x1/packed_packed_loop1/avx-512-unroll.tmpli", + 16, + 2, + "avx512" + ); + } + println!(); +} + +unsafe fn packed_packed_1x1(f: Option<&str>) { + if std::is_x86_feature_detected!("avx512f") { + kloop!( + f, + "1x1x1", + (16 * 1 * 1), + "1x1/packed_packed_loop1/avx-512.tmpli", + 16, + 1, + "avx512" + ); + kloop!( + f, + "1x1x2", + (16 * 1 * 1), + "1x1/packed_packed_loop1/unroll.tmpli", + 16, + 2, + "avx512" + ); + kloop!( + f, + "1x1x4", + (16 * 1 * 1), + "1x1/packed_packed_loop1/unroll-4.tmpli", + 16, + 4, + "avx512" + ); + kloop!( + f, + "1x1x8", + (16 * 1 * 1), + "1x1/packed_packed_loop1/unroll-8.tmpli", + 16, + 8, + "avx512" + ); + kloop!( + f, + "1x1x16", + (16 * 1 * 1), + "1x1/packed_packed_loop1/unroll-16.tmpli", + 16, + 16, + "avx512" + ); + } + println!(); +} + +unsafe fn packed_packed_10x1(f: Option<&str>) { + println!("-- 10x1 kernels"); + kloop!( + f, + "10x1x1", + (80 * 1), + "10x1/packed_packed_loop1/avx.tmpli", + 8 + ); + kloop!( + f, + "10x1x2", + (80 * 1), + "10x1/packed_packed_loop1/avx-unroll.tmpli", + 8, + 2 + ); + if std::is_x86_feature_detected!("avx512f") { + kloop!( + f, + "10x1x1", + (16 * 10 * 1), + "10x1/packed_packed_loop1/avx-512.tmpli", + 16, + 1, + "avx512" + ); + kloop!( + f, + "10x1x2", + (16 * 10 * 1), + "10x1/packed_packed_loop1/avx-512-unroll.tmpli", + 16, + 2, + "avx512" + ); + } + println!(); +} + +fn main() { + let filter = std::env::args().skip(1).find(|a| a != "--bench"); + unsafe { + packed_packed_1x1(filter.as_deref()); + packed_packed_1x12(filter.as_deref()); + packed_packed_1x8(filter.as_deref()); + packed_packed_2x6(filter.as_deref()); + packed_packed_2x5(filter.as_deref()); + packed_packed_3x4(filter.as_deref()); + packed_packed_4x3(filter.as_deref()); + packed_packed_5x2(filter.as_deref()); + packed_packed_6x2(filter.as_deref()); + packed_packed_8x2(filter.as_deref()); + packed_packed_6x1(filter.as_deref()); + packed_packed_7x1(filter.as_deref()); + packed_packed_8x1(filter.as_deref()); + packed_packed_10x1(filter.as_deref()); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/build.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/build.rs new file mode 100644 index 0000000..5c739f3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/build.rs @@ -0,0 +1,546 @@ +use std::{env, fs, path}; + +fn var(k: &str) -> String { + env::var(k).unwrap() +} + +fn use_masm() -> bool { + env::var("CARGO_CFG_TARGET_ENV") == Ok("msvc".to_string()) + && var("CARGO_CFG_TARGET_ARCH") == "x86_64" + && var("HOST").contains("-windows-") +} + +fn include_amx() -> bool { + let arch = var("CARGO_CFG_TARGET_ARCH"); + let os = var("CARGO_CFG_TARGET_OS"); + os == "macos" + || (env::var("CARGO_FEATURE_APPLE_AMX_IOS").is_ok() && os == "ios" && arch == "aarch64") +} + +fn include_sme() -> bool { + let arch = var("CARGO_CFG_TARGET_ARCH"); + let os = var("CARGO_CFG_TARGET_OS"); + arch == "aarch64" && (os == "macos" || os == "linux") +} + +// Probe whether the target assembler can actually assemble SME instructions. +// Old binutils (e.g. the Debian stretch aarch64 cross-toolchain used in CI) +// predate SME and reject the mnemonics even with `.arch armv9-a+sme2`, which +// breaks the build. When the probe fails we skip the SME kernels entirely; +// the matching `tract_sme` cfg keeps the Rust side from referencing the +// (now absent) kernel symbols, and dispatch falls back to the portable path. +fn assembler_supports_sme() -> bool { + cc::Build::new() + .file("arm64/sme/dummy_sme.S") + .cargo_metadata(false) + .cargo_warnings(false) + .warnings(false) + .try_compile("tract_sme_probe") + .is_ok() +} + +// Probe whether the target assembler can encode FEAT_DotProd `sdot` (the +// indexed int8 form used by arm64simd_mmm_i32_8x8_dot). Old binutils — notably +// the Debian stretch aarch64 cross-toolchain in CI — predate FEAT_DotProd and +// reject `.cpu ...+dotprod` / `sdot` outright. When the probe fails we skip the +// SDOT kernel and the `tract_arm64_dotprod` cfg; the runtime falls back to the +// SMLAL 8x8 i32 kernel. +fn assembler_supports_dotprod() -> bool { + cc::Build::new() + .file("arm64/arm64simd/dummy_dotprod.S") + .cargo_metadata(false) + .cargo_warnings(false) + .warnings(false) + .try_compile("tract_dotprod_probe") + .is_ok() +} + +// Probe whether the target assembler can encode `vpdpbusd ymm` (AVX-512 VNNI +// with AVX-512 VL, i.e. the 256-bit form). binutils gained this in ~2.30 +// (2018); the Debian stretch toolchain ships 2.28 and rejects the mnemonic. +// When the probe fails we skip the VNNI kernel and the `tract_avx512vnni` cfg; +// the runtime falls back to the AVX2 i32 path. +fn assembler_supports_avx512vnni() -> bool { + cc::Build::new() + .file("x86_64/avx512vnni/dummy_vnni.S") + .cargo_metadata(false) + .cargo_warnings(false) + .warnings(false) + .try_compile("tract_avx512vnni_probe") + .is_ok() +} + +fn include_sve() -> bool { + // SVE/SVE2 lives on ARMv9 server/mobile cores (Neoverse V1+/N2+, Cortex-X2+, + // Graviton 3/4) — Linux aarch64. No Apple silicon has SVE. + var("CARGO_CFG_TARGET_ARCH") == "aarch64" && var("CARGO_CFG_TARGET_OS") == "linux" +} + +// Probe whether the C compiler supports SVE intrinsics (arm_sve.h + `+sve`). +// Old toolchains (e.g. the Debian stretch cross-gcc) lack them; when the probe +// fails we skip the SVE kernels and the `tract_sve` cfg, so the Rust side never +// references the (absent) symbols and dispatch falls back to NEON. +fn compiler_supports_sve() -> bool { + let out_dir = path::PathBuf::from(var("OUT_DIR")); + let probe = out_dir.join("sve_probe.c"); + fs::write(&probe, "#include \nint p(void){ return (int)svcntw(); }\n").unwrap(); + cc::Build::new() + .file(&probe) + .flag("-march=armv8.2-a+sve") + .cargo_metadata(false) + .cargo_warnings(false) + .warnings(false) + .try_compile("tract_sve_probe") + .is_ok() +} + +fn jump_table() -> Vec { + println!("cargo:rerun-if-changed=src/frame/mmm/fuse.rs"); + std::fs::read_to_string("src/frame/mmm/fuse.rs") + .unwrap() + .lines() + .filter(|l| l.contains("// jump_to:")) + .map(|l| l.split("jump_to:").nth(1).unwrap().to_owned()) + .collect() +} + +#[derive(Clone, Debug)] +struct ConfigForHalf { + extra_flags: Vec, + needs_pragma: bool, +} + +impl ConfigForHalf { + fn new(extra_flags: Vec, needs_pragma: bool) -> ConfigForHalf { + ConfigForHalf { extra_flags, needs_pragma } + } + + fn all() -> Vec { + let mut configs = vec![]; + for extra_flags in + [vec![], vec!["-march=armv8.2-a".to_string()], vec!["-mcpu=cortex-a55".to_string()]] + { + for needs_pragma in [false, true] { + configs.push(ConfigForHalf::new(extra_flags.clone(), needs_pragma)) + } + } + configs + } + + fn cc(&self) -> cc::Build { + let mut cc = cc::Build::new(); + for flag in &self.extra_flags { + cc.flag(flag); + } + cc + } + + fn works(&self) -> bool { + let filename = if self.needs_pragma { + "arm64/arm64fp16/dummy_fmla_pragma.S" + } else { + "arm64/arm64fp16/dummy_fmla_no_pragma.S" + }; + self.cc().file(filename).try_compile("dummy").is_ok() + } + + pub fn probe() -> Option { + Self::all().iter().find(|c| c.works()).cloned() + } +} + +fn main() { + let target = var("TARGET"); + let arch = var("CARGO_CFG_TARGET_ARCH"); + let os = var("CARGO_CFG_TARGET_OS"); + let out_dir = path::PathBuf::from(var("OUT_DIR")); + + let suffix = env!("CARGO_PKG_VERSION").replace(['-', '.'], "_"); + make_extern_kernel_decl_macro(&out_dir, &suffix); + + // `tract_sme` is set below only when both include_sme() and the assembler + // SME probe succeed; declare it so rustc's unexpected-cfg lint stays quiet. + println!("cargo:rustc-check-cfg=cfg(tract_sme)"); + // Set below only when include_sve() and the SVE compiler probe both pass. + println!("cargo:rustc-check-cfg=cfg(tract_sve)"); + // Set below only when the aarch64 assembler probe for `sdot` passes. + println!("cargo:rustc-check-cfg=cfg(tract_arm64_dotprod)"); + // Set below only when the x86_64 assembler probe for vpdpbusd ymm passes. + println!("cargo:rustc-check-cfg=cfg(tract_avx512vnni)"); + + match arch.as_ref() { + "x86_64" => { + let mut files = preprocess_files("x86_64/fma", &[], &suffix, false); + // The VNNI kernel is compiled separately (conditional on a probe) to + // avoid breaking old assemblers. Remove it from the main file list. + files.retain(|f| { + !f.file_name().and_then(|n| n.to_str()).map_or(false, |n| n.contains("avx512vnni")) + }); + files.extend(preprocess_files("x86_64/avx512", &[], &suffix, false)); + + if os == "windows" { + if use_masm() { + let mut lib_exe = cc::windows_registry::find(&target, "lib.exe") + .expect("Could not find lib.exe"); + lib_exe + .arg(format!("/out:{}", out_dir.join("x86_64_fma.lib").to_str().unwrap())); + for f in files { + let mut obj = f.clone(); + obj.set_extension("o"); + let mut ml_exe = cc::windows_registry::find(&target, "ml64.exe") + .expect("Could not find ml64.exe"); + if !ml_exe + .arg("/Fo") + .arg(&obj) + .arg("/c") + .arg(&f) + .status() + .unwrap() + .success() + { + for (i, l) in std::fs::read_to_string(&f).unwrap().lines().enumerate() { + println!("{i:8} {l}"); + } + panic!(); + } + lib_exe.arg(obj); + } + assert!(lib_exe.status().unwrap().success()); + println!("cargo:rustc-link-search=native={}", out_dir.to_str().unwrap()); + println!("cargo:rustc-link-lib=static=x86_64_fma"); + } else { + cc::Build::new() + .files(files) + .flag("-mfma") + .flag("-mf16c") + .compile("x86_64_fma"); + + // clang at least (dunno about gcc) outputs .asm files in the + // root directory that we need to clean up so we don't pollute + // the build output/working directory + let _ = fs::remove_file("fma_mmm_f32_16x6.asm"); + let _ = fs::remove_file("fma_mmm_i32_8x8.asm"); + let _ = fs::remove_file("fma_sigmoid_f32.asm"); + let _ = fs::remove_file("fma_tanh_f32.asm"); + } + } else { + cc::Build::new().files(files).flag("-mfma").compile("x86_64_fma"); + } + // VNNI kernel compiled separately so old assemblers (binutils < 2.30, + // e.g. Debian stretch) that can't encode `vpdpbusd ymm` don't break + // the whole x86_64 build. The `tract_avx512vnni` cfg gates the + // matching Rust extern declarations and dispatch registration. + // + // The template stays in x86_64/fma/ (alongside dispatcher.j2 and the + // other partials it includes) so the jinja env can resolve its includes. + if assembler_supports_avx512vnni() { + let tmpl = path::Path::new("x86_64/fma/avx512vnni_mmm_i32_8x8.S.j2"); + let out = out_dir.join(format!("avx512vnni_mmm_i32_8x8_{suffix}.S")); + preprocess_file(tmpl, &out, &[], &suffix, false); + cc::Build::new().file(&out).flag("-mfma").compile("x86_64_avx512vnni"); + println!("cargo:rustc-cfg=tract_avx512vnni"); + } + } + "arm" | "armv7" => { + let files = preprocess_files("arm32/armvfpv2", &[], &suffix, false); + cc::Build::new().files(files).flag("-marm").flag("-mfpu=vfp").compile("armvfpv2"); + let files = preprocess_files( + "arm32/armv7neon", + &[("core", vec!["cortexa7", "cortexa9", "generic"])], + &suffix, + false, + ); + cc::Build::new().files(files).flag("-marm").flag("-mfpu=neon").compile("armv7neon"); + } + "aarch64" => { + let mut files = preprocess_files( + "arm64/arm64simd", + &[("core", vec!["a53", "a55", "gen"])], + &suffix, + false, + ); + // The SDOT kernel is compiled separately (conditional on a probe) so + // old assemblers (binutils < 2.30, e.g. Debian stretch) that can't + // encode `sdot` don't break the whole arm64simd build. Remove it + // from the main file list. + files.retain(|f| { + !f.file_name().and_then(|n| n.to_str()).map_or(false, |n| n.contains("_dot")) + }); + cc::Build::new().files(files).compile("arm64simd"); + // The template stays in arm64/arm64simd/ (alongside the jinja partials + // it includes) so the env can resolve its includes. The + // `tract_arm64_dotprod` cfg gates the matching Rust extern + dispatch. + if assembler_supports_dotprod() { + let tmpl = path::Path::new("arm64/arm64simd/arm64simd_mmm_i32_8x8_dot.S.j2"); + let out = out_dir.join(format!("arm64simd_mmm_i32_8x8_dot_{suffix}.S")); + preprocess_file(tmpl, &out, &[], &suffix, false); + cc::Build::new().file(&out).compile("arm64simd_dot"); + println!("cargo:rustc-cfg=tract_arm64_dotprod"); + } + if include_amx() { + let files = preprocess_files("arm64/apple_amx", &[], &suffix, false); + cc::Build::new().files(files).compile("appleamx"); + } + if include_sme() && assembler_supports_sme() { + let files = preprocess_files("arm64/sme", &[], &suffix, false); + cc::Build::new().files(files).compile("sme"); + println!("cargo:rustc-cfg=tract_sme"); + } + if include_sve() && compiler_supports_sve() { + // VLA SVE kernels (C intrinsics, fixed symbols — not suffix-templated). + cc::Build::new() + .file("arm64/sve/sve_mmm_f32.c") + .file("arm64/sve/sve_mmv_f32_64x1.c") + .file("arm64/sve/sve_mmm_i32.c") + .file("arm64/sve/sve_mmm_i32_64x1.c") + .flag("-march=armv8.2-a+sve") + .compile("tract_sve_kernels"); + // f16 kernels need native FP16 arithmetic (+fp16); compiled + // separately so the +sve-only kernels above never gain fp16 + // codegen. Runtime-gated on has_fp16() as well as SVE2. + cc::Build::new() + .file("arm64/sve/sve_mmm_f16.c") + .file("arm64/sve/sve_mmv_f16_64x1.c") + .flag("-march=armv8.2-a+sve+fp16") + .compile("tract_sve_f16_kernels"); + println!("cargo:rustc-cfg=tract_sve"); + } + if std::env::var("CARGO_FEATURE_NO_FP16").is_err() { + let config = + ConfigForHalf::probe().expect("No configuration found for fp16 support"); + let files = preprocess_files( + "arm64/arm64fp16", + &[("core", vec!["a55", "gen"])], + &suffix, + config.needs_pragma, + ); + config.cc().files(files).compile("arm64fp16") + } + } + _ => {} + } +} + +type Variant = (&'static str, Vec<&'static str>); + +fn preprocess_files( + input: impl AsRef, + variants: &[Variant], + suffix: &str, + needs_pragma: bool, +) -> Vec { + let out_dir = path::PathBuf::from(var("OUT_DIR")); + let mut files = vec![]; + let dir_entries = { + let mut dir_entries: Vec = + input.as_ref().read_dir().unwrap().map(|f| f.unwrap()).collect(); + dir_entries.sort_by_key(|a| a.path()); + dir_entries + }; + for f in dir_entries { + let fname = f.path().file_name().unwrap().to_str().unwrap().to_owned(); + if fname.ends_with(".S.j2") { + let tmpl_file = fname; + let concerned_variants: Vec<&Variant> = + variants.iter().filter(|v| tmpl_file.contains(v.0)).collect(); + let expanded_variants = concerned_variants.iter().map(|pair| pair.1.len()).product(); + for v in 0..expanded_variants { + let mut tmpl_file = tmpl_file.clone(); + let mut id = v; + let mut globals = vec![]; + for variable in variants { + let key = variable.0; + let value = variable.1[id % variable.1.len()]; + globals.push((key, value)); + tmpl_file = tmpl_file.replace(key, value); + id /= variable.1.len(); + } + let out_name = tmpl_file.strip_suffix(".S.j2").unwrap(); + let file = out_dir.join(format!("{out_name}.S")); + preprocess_file(f.path(), &file, &globals, suffix, needs_pragma); + files.push(file); + } + } + } + files +} + +/// Replace `//` assembly comments with `;` for MSVC assembler. +/// Must be called on rendered output (not on Jinja2 source, which uses `//` for integer division). +fn strip_comments(s: &str) -> String { + s.lines().map(|line| line.replace("//", ";")).collect::>().join("\n") +} + +fn preprocess_file( + template: impl AsRef, + output: impl AsRef, + variants: &[(&'static str, &'static str)], + suffix: &str, + needs_pragma: bool, +) { + println!("cargo:rerun-if-changed={}", template.as_ref().to_string_lossy()); + let family = var("CARGO_CFG_TARGET_FAMILY"); + let os = var("CARGO_CFG_TARGET_OS"); + + let msvc = use_masm(); + println!("cargo:rerun-if-changed={}", template.as_ref().to_string_lossy()); + let input = fs::read_to_string(&template).unwrap(); + let l = if os == "macos" { + "L" + } else if family == "windows" { + "" + } else { + ".L" + } + .to_owned(); + let long = if msvc { "dd" } else { ".long" }; + let g = if os == "macos" || os == "ios" || os == "watchos" || os == "tvos" { "_" } else { "" }; + let align = if msvc { "align" } else { ".align" }; + let offset = if msvc { "offset" } else { "rip + " }; + + let mut env = build_jinja_env(template.as_ref().parent().unwrap()); + + let main_name = template.as_ref().file_name().unwrap().to_str().unwrap(); + env.add_template_owned(main_name.to_string(), input).unwrap_or_else(|e| { + eprintln!("Parsing {}: {e}", template.as_ref().to_string_lossy()); + panic!(); + }); + + let tmpl = env.get_template(main_name).unwrap(); + + let mut ctx = std::collections::BTreeMap::::new(); + ctx.insert("msvc".into(), msvc.into()); + ctx.insert("needs_pragma".into(), needs_pragma.into()); + ctx.insert("family".into(), family.into()); + ctx.insert("os".into(), os.into()); + ctx.insert("L".into(), l.into()); + ctx.insert("G".into(), g.into()); + ctx.insert("suffix".into(), suffix.into()); + ctx.insert("long".into(), long.into()); + ctx.insert("jump_table".into(), minijinja::Value::from_serialize(&jump_table())); + ctx.insert("align".into(), align.into()); + ctx.insert("offset".into(), offset.into()); + + for (k, v) in variants { + ctx.insert(k.to_string(), (*v).into()); + } + + if include_amx() { + let (amx_set, amx_clr) = amx_globals(); + ctx.insert("AMX_SET".into(), amx_set.into()); + ctx.insert("AMX_CLR".into(), amx_clr.into()); + } + + match tmpl.render(&ctx) { + Ok(rendered) => { + let rendered = if msvc { strip_comments(&rendered) } else { rendered }; + fs::write(&output, rendered).unwrap(); + } + Err(e) => { + eprintln!("Rendering {}: {e:#}", template.as_ref().to_string_lossy()); + panic!(); + } + } +} + +fn build_jinja_env(template_dir: &path::Path) -> minijinja::Environment<'static> { + let mut env = minijinja::Environment::new(); + + // Custom filters + env.add_filter("float16", float16_filter); + env.add_filter("setting", setting_filter); + env.add_filter("lsl", lsl_filter); + env.add_filter("u", unsigned_filter); + + // Custom function: amx("op", gpr) -> assembly .word directive + env.add_function("amx", amx_function); + + // Load all partials (.j2 = Jinja2 macros/includes, .S.raw = raw assembly with brace escaping) + for f in walkdir::WalkDir::new(template_dir) { + let f = f.unwrap(); + if f.path().is_dir() { + continue; + } + + let fname = f.path().file_name().unwrap().to_str().unwrap().to_owned(); + let text = std::fs::read_to_string(f.path()).unwrap_or_else(|_| panic!("file {f:?}")); + let text = if fname.ends_with(".S.raw") { + Some(text.replace("{{", "{").replace("}}", "}")) + } else if fname.ends_with(".j2") && !fname.ends_with(".S.j2") { + Some(text) + } else { + None + }; + if let Some(text) = text { + let key = f + .path() + .strip_prefix(template_dir) + .unwrap() + .to_str() + .unwrap() + .to_owned() + .replace('\\', "/"); + println!("cargo:rerun-if-changed={}", f.path().to_string_lossy().replace('\\', "/")); + env.add_template_owned(key, text).unwrap_or_else(|e| { + eprintln!("Parsing partial {}: {e}", f.path().to_string_lossy()); + panic!(); + }); + } + } + + env +} + +fn make_extern_kernel_decl_macro(out_dir: &path::Path, suffix: &str) { + let macro_decl = r#" + macro_rules! extern_kernel { + (fn $name: ident($($par_name:ident : $par_type: ty ),*) -> $rv: ty) => { + paste! { + unsafe extern "C" { pub fn [<$name _ _suffix>]($(par_name: $par_type),*) -> $rv; } + pub use [<$name _ _suffix>] as $name; + } + } + }"# + .replace("_suffix", suffix); + std::fs::write(out_dir.join("extern_kernel_macro.rs"), macro_decl).unwrap(); +} + +// --- Custom filters and functions --- + +fn float16_filter(value: f64) -> String { + let bits = half::f16::from_f32(value as f32).to_bits(); + format!(".short {bits}") +} + +fn setting_filter(value: i64, bit: i64) -> String { + let result = value | (1i64 << bit); + result.to_string() +} + +fn lsl_filter(value: i64, shift: i64) -> String { + let result = value << shift; + result.to_string() +} + +fn unsigned_filter(value: i64) -> String { + let result = value as u64; + result.to_string() +} + +fn amx_function(op: String, gpr: u32) -> String { + let ops = [ + "ldx", "ldy", "stx", "sty", "ldz", "stz", "ldzi", "stzi", "extrx", "extry", "fma64", + "fms64", "fma32", "fms32", "mac16", "fma16", "fms16", "setclr", "vecint", "vecfp", + "matint", "matfp", "genlut", + ]; + let op_id = ops.iter().position(|x| *x == op.as_str()).unwrap(); + format!(".word 0x{:x} \t\t\t\t// AMX {} x{}\n", 0x201000 + (op_id << 5) + gpr as usize, op, gpr) +} + +fn amx_nop_op_imm5(op: usize, imm5: usize) -> String { + format!("nop\nnop\nnop\n.word 0x{:x}\n", (0x201000 + (op << 5) + imm5)) +} + +fn amx_globals() -> (String, String) { + (amx_nop_op_imm5(17, 0), amx_nop_op_imm5(17, 1)) +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/apple.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/apple.rs new file mode 100644 index 0000000..5d7fcbe --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/apple.rs @@ -0,0 +1,58 @@ +#[cfg(any(target_os = "macos", all(target_os = "ios", feature = "apple-amx-ios")))] + +#[cfg(target_os = "ios")] +lazy_static::lazy_static! { + static ref IPHONE_MODEL_MAJOR:Option = { + use std::ffi::{c_char, c_void, CStr, CString}; + use std::ptr::null_mut; + + extern "C" { + fn sysctlbyname( + name: *const c_char, + oldp: *mut c_void, + oldlenp: *mut isize, + newp: *mut c_void, + newlen: isize, + ); + } + + unsafe { + let mut len: isize = 0; + let name = CString::new("hw.machine").unwrap(); + sysctlbyname(name.as_ptr(), null_mut(), &mut len, null_mut(), 0); + let mut buf = vec![0u8; len as _]; + sysctlbyname(name.as_ptr(), buf.as_mut_ptr() as _, &mut len, null_mut(), 0); + let version = CStr::from_bytes_with_nul(&buf).unwrap().to_string_lossy().into_owned(); + let Some((major, _)) = version.trim_start_matches("iPhone").split_once(",") else { return None }; + major.parse::().ok() + } + }; +} + +#[cfg(target_os = "macos")] +pub fn has_amx() -> bool { + true +} + +#[cfg(all(target_os = "ios", feature = "apple-amx-ios"))] +fn has_amx() -> bool { + // iPhone12,1 is the one branded "iPhone 11", with Apple A13 bionic, first CPU featuring amx + IPHONE_MODEL_MAJOR.map(|it| it >= 12).unwrap_or(false) +} + +#[inline] +#[cfg(target_os = "ios")] +pub fn has_fp16() -> bool { + // iPhone10,1 is the one branded "iPhone 8", with Apple A11 bionic, first CPU featuring fp16 + IPHONE_MODEL_MAJOR.map(|it| it >= 10).unwrap_or(false) +} + +#[inline] +#[cfg(not(target_os = "ios"))] +pub fn has_fp16() -> bool { + cfg!(target_os = "macos") + || cfg!(feature_cpu = "fp16") + || *KIND == Kind::CortexA55 + || *KIND == Kind::CortexA75 + || *HAS_FP16 +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32.rs new file mode 100644 index 0000000..38986bb --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32.rs @@ -0,0 +1,104 @@ +use std::{env, fs}; +pub mod armv7neon; +mod armvfpv2; +mod cortex_a7; +mod cortex_a9; +use armv7neon::*; + +use crate::frame::element_wise::ElementWiseKer; + +use crate::Ops; + +fn has_neon_cpuinfo() -> std::io::Result { + let cpu_info = fs::read_to_string("/proc/cpuinfo")?; + let neon = cpu_info.split("\n").any(|line| { + line.starts_with("Features") && (line.contains("neon") || line.contains("asimd")) + }); + Ok(neon) +} + +fn cpu_part() -> Option { + fs::read_to_string("/proc/cpuinfo") + .ok() + .and_then(|cpuinfo| { + cpuinfo + .lines() + .find(|line| line.starts_with("CPU part")) + .and_then(|s| s.trim().split_whitespace().last()) + .and_then(|s| s.strip_prefix("0x")) + .and_then(|s| usize::from_str_radix(s, 16).ok()) + }) +} + +fn has_neon() -> bool { + if let Ok(v) = env::var("TRACT_CPU_ARM32_NEON") { + return v == "true" || v == "1"; + } + has_neon_cpuinfo().unwrap_or(false) +} + +pub fn plug(ops: &mut Ops) { + if has_neon() { + log::info!("armv7neon activated (smmm, ssigmoid), stanh)"); + armv7neon::plug(ops); + + let cpu = cpu_part().unwrap_or(0); + + fn prefer_8x4(_m: Option, _k: Option, n: Option) -> bool { + n.map(|n| n % 4 == 0 && n % 6 != 0 && n <= 12) + .unwrap_or(false) + } + + let cost_managed_impls = vec![ + armv7neon_mmm_f32_8x4_cortexa7.mmm(), + armv7neon_mmm_f32_8x6_cortexa7.mmm(), + armv7neon_mmm_f32_8x4_cortexa9.mmm(), + armv7neon_mmm_f32_8x6_cortexa9.mmm(), + armv7neon_mmm_f32_8x4_generic.mmm(), + armv7neon_mmm_f32_8x6_generic.mmm(), + crate::generic::mmm::generic_f32_4x4.mmm(), + ]; + ops.mmv_f32 = match cpu { + 0xc07 => Box::new(|_, _| armv7neon::armv7neon_mmm_f32_32x1_cortexa7.mmm()), + 0xc09 => Box::new(|_, _| armv7neon::armv7neon_mmm_f32_32x1_cortexa9.mmm()), + _ => Box::new(|_, _| armv7neon::armv7neon_mmm_f32_32x1_generic.mmm()), + }; + + ops.mmm_f32 = match cpu { + 0xc07 => { + let model = cortex_a7::model(); + Box::new(move |m, k, n| model.pick(&cost_managed_impls, m, k, n)) + } + 0xc09 => { + let model = cortex_a9::model(); + Box::new(move |m, k, n| model.pick(&cost_managed_impls, m, k, n)) + } + _ => Box::new(|m, k, n| { + if prefer_8x4(m, k, n) { + armv7neon::armv7neon_mmm_f32_8x4_generic.mmm() + } else { + armv7neon::armv7neon_mmm_f32_8x6_generic.mmm() + } + }), + }; + ops.qmmm_i32 = Box::new(|_, _, _| armv7neon::armv7neon_mmm_i32_8x4.mmm()); + ops.qmmv_i32 = Box::new(|_, _| armv7neon::armv7neon_mmm_i32_32x1.mmm()); + ops.sigmoid_f32 = Box::new(|| armv7neon_sigmoid_f32_4n::ew()); + ops.tanh_f32 = Box::new(|| armv7neon_tanh_f32_4n::ew()); + } else { + armvfpv2::plug(ops); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn may_have_neon() { + println!("Has neon ? {:?}", has_neon()); + if let Ok(neon) = env::var("TRACT_CPU_EXPECT_ARM32_NEON") { + assert_eq!(neon == "true", has_neon()); + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32/armv7neon.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32/armv7neon.rs new file mode 100644 index 0000000..3342ead --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32/armv7neon.rs @@ -0,0 +1,52 @@ +use crate::Ops; +use crate::frame::mmm::ImplementationQuality::ManuallyOptimized; +use crate::pack::PackedFormat; + +const NEON: fn() -> bool = || crate::arm32::has_neon(); + +MMMExternKernel!(armv7neon_mmm_f32_8x4_cortexa7 ( 8, 4)@(16, 4) where(NEON) quality(ManuallyOptimized)); +MMMExternKernel!(armv7neon_mmm_f32_8x4_cortexa9 ( 8, 4)@(16, 4) where(NEON) quality(ManuallyOptimized)); +MMMExternKernel!(armv7neon_mmm_f32_8x4_generic ( 8, 4)@(16, 4) where(NEON) quality(ManuallyOptimized)); +MMMExternKernel!(armv7neon_mmm_f32_8x6_cortexa7 ( 8, 6)@(16, 4) where(NEON) quality(ManuallyOptimized)); +MMMExternKernel!(armv7neon_mmm_f32_8x6_cortexa9 ( 8, 6)@(16, 4) where(NEON) quality(ManuallyOptimized)); +MMMExternKernel!(armv7neon_mmm_f32_8x6_generic ( 8, 6)@(16, 4) where(NEON) quality(ManuallyOptimized)); +MMMExternKernel!(armv7neon_mmm_f32_8x1_generic ( 8, 1)@(16, 4) where(NEON) quality(ManuallyOptimized)); +MMMExternKernel!(armv7neon_mmm_f32_32x1_cortexa7(32, 1)@(16, 4) where(NEON) quality(ManuallyOptimized)); +MMMExternKernel!(armv7neon_mmm_f32_32x1_cortexa9(32, 1)@(16, 4) where(NEON) quality(ManuallyOptimized)); +MMMExternKernel!(armv7neon_mmm_f32_32x1_generic (32, 1)@(16, 4) where(NEON) quality(ManuallyOptimized)); + +MMMExternKernel!(armv7neon_mmm_i32_8x4(8, 4)@(32, 4) where(NEON) + packing[1] = i8i8 => |k| k.with_packing(PackedFormat::new(DatumType::I8, 8, 32), PackedFormat::new(DatumType::I8, 4, 32)); + quality(ManuallyOptimized) + store(i8) +); + +MMMExternKernel!(armv7neon_mmm_i32_32x1(32, 1)@(32, 4) where(NEON) + packing[1] = i8i8 => |k| k.with_packing(PackedFormat::new(DatumType::I8, 32, 32), PackedFormat::new(DatumType::I8, 1, 4)); + quality(ManuallyOptimized) + store(i8) +); + +pub fn plug(ops: &mut Ops) { + ops.mmm_impls.extend_from_slice(&[ + armv7neon_mmm_f32_8x4_cortexa7.mmm(), + armv7neon_mmm_f32_8x4_cortexa9.mmm(), + armv7neon_mmm_f32_8x4_generic.mmm(), + armv7neon_mmm_f32_8x6_cortexa7.mmm(), + armv7neon_mmm_f32_8x6_cortexa9.mmm(), + armv7neon_mmm_f32_8x6_generic.mmm(), + armv7neon_mmm_f32_8x1_generic.mmm(), + armv7neon_mmm_f32_32x1_cortexa7.mmm(), + armv7neon_mmm_f32_32x1_cortexa9.mmm(), + armv7neon_mmm_f32_32x1_generic.mmm(), + ]); +} + +sigmoid_impl!( + f32, + armv7neon_sigmoid_f32_4n, + 4, + 4, + crate::arm32::has_neon() +); +tanh_impl!(f32, armv7neon_tanh_f32_4n, 4, 4, crate::arm32::has_neon()); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32/armvfpv2.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32/armvfpv2.rs new file mode 100644 index 0000000..e6e01f7 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32/armvfpv2.rs @@ -0,0 +1,11 @@ +use crate::Ops; +use crate::frame::mmm::ImplementationQuality::ManuallyOptimized; +use crate::frame::mmm::*; + +MMMExternKernel!(armvfpv2_mmm_f32_4x4(4, 4)@(4, 4) quality(ManuallyOptimized)); + +pub fn plug(ops: &mut Ops) { + log::info!("armvfpv2 activated for smmm"); + ops.mmm_f32 = Box::new(|_, _, _| armvfpv2_mmm_f32_4x4.mmm()); + ops.mmm_impls.push(armvfpv2_mmm_f32_4x4.mmm()); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32/cortex_a7.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32/cortex_a7.rs new file mode 100644 index 0000000..02d8b14 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32/cortex_a7.rs @@ -0,0 +1,861 @@ +use crate::frame::mmm::CostModel; +pub fn model() -> CostModel<'static> { + CostModel { + big_product_mkn_threshold: 4193728.0, + big_product_kernel_choice: "armv7neon_mmm_f32_8x6_cortexa7", + kernels: &[ + "armv7neon_mmm_f32_8x4_cortexa7", + "armv7neon_mmm_f32_8x4_cortexa9", + "armv7neon_mmm_f32_8x4_generic", + "armv7neon_mmm_f32_8x6_cortexa7", + "armv7neon_mmm_f32_8x6_cortexa9", + "armv7neon_mmm_f32_8x6_generic", + "generic_f32_4x4", + ], + mrs: &[4, 8], + nrs: &[4, 6], + feat_norm_mean: &[ + 4.589878771602424, + 4.5739692460187005, + 4.598167981532298, + 13.762015999153403, + 1.5038983903420524, + 0.749874245472837, + 3.465165995975855, + 0.8777665995975855, + 1.5022635814889336, + 0.7570422535211268, + 2.482142857142857, + 0.8333752515090543, + ], + feat_norm_stddev: &[ + 1.2587312982588519, + 1.2603116830524392, + 1.2581181647300588, + 1.3169322340874257, + 1.1192637768418767, + 0.43308528195884044, + 2.2762097127791114, + 0.32755518043295856, + 1.1069539235554247, + 0.42886977033219037, + 1.7067987601825914, + 0.37264049924995035, + ], + w1: &[ + 0.06765510141849518, + 0.024555781856179237, + -0.8821254968643188, + -0.004870870150625706, + -0.10525479167699814, + 0.1827959418296814, + 0.1633400171995163, + -0.2377464473247528, + -0.17880690097808838, + 0.19097138941287994, + 0.04676022008061409, + -0.11329511553049088, + 0.4089120030403137, + -0.3100685477256775, + -0.1652061492204666, + -0.19124962389469147, + -0.03810987249016762, + -0.00785011239349842, + 0.09714752435684204, + -0.11142419278621674, + 0.19261880218982697, + -0.2893339991569519, + -0.19540216028690338, + 0.39759594202041626, + -0.00619965186342597, + -0.8473111391067505, + 0.343344122171402, + -0.12575943768024445, + 0.029266485944390297, + -0.02900734543800354, + -0.019343264400959015, + 0.08306540548801422, + -0.1927606761455536, + 0.23312175273895264, + 0.2576882541179657, + -0.35881471633911133, + -0.27300119400024414, + -0.2995607852935791, + -0.7934547662734985, + -0.9349930286407471, + -0.011614155024290085, + -0.12521372735500336, + 0.011371670290827751, + 0.05779163911938667, + 0.17875070869922638, + -0.23169392347335815, + -0.09749509394168854, + 0.07436174154281616, + 0.24035069346427917, + -0.1262669861316681, + 0.3874961733818054, + -0.11149000376462936, + 0.03639678284525871, + 0.17740628123283386, + 0.03768332302570343, + -0.20480288565158844, + -0.1955408751964569, + 0.44144806265830994, + 0.3628064692020416, + -0.2537013292312622, + 0.019405143335461617, + 0.06186319515109062, + 0.5196826457977295, + 0.3010406494140625, + 0.04013144597411156, + 0.03517461195588112, + -0.037290964275598526, + 0.009919736534357071, + -0.3135205805301666, + 0.4654330909252167, + 0.46720823645591736, + 0.29665476083755493, + 0.09099660068750381, + -0.7376689314842224, + -0.07840575277805328, + -0.5192644000053406, + 0.019796665757894516, + -0.021734869107604027, + 0.13953897356987, + -0.04154204577207565, + 0.10942933708429337, + -0.13621817529201508, + -0.04218055680394173, + 0.09188657253980637, + -0.16021296381950378, + -0.19393481314182281, + 0.3737955689430237, + 0.08288388699293137, + -0.08280416578054428, + -0.13087297976016998, + -0.09470323473215103, + 0.2779513895511627, + 0.03663017228245735, + 0.36601993441581726, + 0.8102841377258301, + 0.6883901953697205, + -0.33066609501838684, + -0.34960171580314636, + 0.923985481262207, + 0.5853908061981201, + 0.07039576023817062, + -0.11843020468950272, + -0.06797836720943451, + 0.0974433571100235, + -0.4707315266132355, + 0.37827417254447937, + 0.15521520376205444, + -0.7403592467308044, + -0.25005313754081726, + 0.596679151058197, + -0.7277861833572388, + -0.6915309429168701, + -0.0050544412806630135, + -0.12311484664678574, + 0.04149714484810829, + 0.05289606750011444, + 0.2448417991399765, + -0.47261708974838257, + -0.3535511791706085, + 0.4614925682544708, + 0.9230178594589233, + -0.5351396799087524, + 0.8224894404411316, + 0.37244901061058044, + -0.08826857805252075, + -0.0452042818069458, + 0.0035054143518209457, + 0.09203510731458664, + 0.08918709307909012, + -0.0694250762462616, + -0.053435735404491425, + 0.1012222170829773, + 0.3401939570903778, + -0.38458573818206787, + 0.3040490746498108, + 0.7614821791648865, + -0.17064380645751953, + 0.22403603792190552, + 0.08646601438522339, + -0.08289062976837158, + -0.20126193761825562, + 0.2795524299144745, + 0.13253425061702728, + -0.07332615554332733, + 0.2151418924331665, + 0.16798575222492218, + 0.003749655559659004, + 0.2437056005001068, + -0.09098415076732635, + 0.18923071026802063, + 0.07854695618152618, + -0.25417080521583557, + 0.15693743526935577, + -0.30657434463500977, + -0.19041943550109863, + 0.26519766449928284, + 0.24278832972049713, + -0.18357035517692566, + -0.015992645174264908, + 0.43973660469055176, + 0.02785446122288704, + 0.3032245934009552, + -0.021606506779789925, + -0.2682349383831024, + -0.10395143181085587, + 0.050348248332738876, + 0.12892353534698486, + -0.10498340427875519, + -0.027477847412228584, + 0.09730125963687897, + -0.16150422394275665, + -0.21831916272640228, + 0.10376061499118805, + -0.25544440746307373, + 0.031593386083841324, + 0.11986788362264633, + 0.22690074145793915, + -0.3509098291397095, + -0.1881190538406372, + -0.04210145026445389, + 0.6883101463317871, + -0.07829979062080383, + 0.4657376706600189, + 0.9263871908187866, + 0.08322961628437042, + 0.04429711028933525, + -0.08905605971813202, + -0.06788893789052963, + -0.056182388216257095, + -0.04881853610277176, + -0.04854113608598709, + 0.15449045598506927, + 0.32911357283592224, + -0.5772383809089661, + -0.00027374469209462404, + -0.2995521128177643, + -0.027322502806782722, + 0.5023694038391113, + 0.045783523470163345, + -0.4035968780517578, + 0.053967904299497604, + 0.00014662329340353608, + 0.021607715636491776, + -0.028252260759472847, + -0.05918470770120621, + -0.1273883581161499, + 0.0679078996181488, + 0.25051605701446533, + -0.0745333656668663, + 0.18680104613304138, + -0.12048312276601791, + 0.013110226020216942, + -0.07659415900707245, + 0.2906968295574188, + 0.3136366307735443, + -0.47699007391929626, + 0.02583535574376583, + -0.15701107680797577, + 0.045304182916879654, + 0.23456838726997375, + -0.06186807528138161, + 0.3926846981048584, + -0.13252438604831696, + -0.16362214088439941, + 0.013557562604546547, + -0.09991434961557388, + 0.09150815010070801, + -0.006477471441030502, + 0.2915862202644348, + 0.5867642164230347, + -0.37984445691108704, + 0.033169880509376526, + 0.024414243176579475, + -0.0384003147482872, + -0.06395144015550613, + 0.07380940765142441, + -0.025898484513163567, + 0.03951931372284889, + -0.2343142330646515, + 0.27318838238716125, + 0.1105947494506836, + 0.290696382522583, + -0.17851489782333374, + -0.17699271440505981, + -0.210996612906456, + -0.10575137287378311, + 0.15886521339416504, + 0.10631759464740753, + 0.22946283221244812, + -0.3170112073421478, + -0.49773311614990234, + -0.10753292590379715, + -0.1114523783326149, + -0.10953730344772339, + 0.4754663109779358, + 0.20793643593788147, + 0.021392812952399254, + -0.0691467821598053, + 0.03368104621767998, + -0.017844771966338158, + 0.1657843142747879, + -0.5556477904319763, + -1.108074426651001, + -0.822117805480957, + -0.06053074076771736, + -0.4072379469871521, + 0.09109722077846527, + -0.5544739961624146, + -0.13978064060211182, + -0.36262163519859314, + 0.20034632086753845, + 0.050625383853912354, + 0.1497042030096054, + -0.18745489418506622, + 0.0894727036356926, + 0.00417149206623435, + 0.2228451371192932, + 0.00852279644459486, + -0.028313757851719856, + 0.04104698821902275, + -0.0874263271689415, + 0.19788521528244019, + -0.019343160092830658, + -0.03962515667080879, + 0.2092486023902893, + -0.44425246119499207, + -0.48542261123657227, + -0.04222029820084572, + 0.7616084218025208, + 0.512810468673706, + -0.17871123552322388, + 0.5459727644920349, + -0.13069608807563782, + 0.09155352413654327, + 0.11548610031604767, + -0.15368784964084625, + 0.038799818605184555, + -0.049028217792510986, + -0.03215758875012398, + -0.050522346049547195, + 0.1663637012243271, + -0.15482299029827118, + -0.9425870180130005, + -0.7017998695373535, + 0.04315050691366196, + -0.019968662410974503, + 0.03749818727374077, + -0.07611791789531708, + 0.32011789083480835, + -0.6925904750823975, + -0.49334919452667236, + 0.23214411735534668, + 1.1447347402572632, + -0.6757001876831055, + 0.7940422296524048, + 0.40169182419776917, + -0.018513813614845276, + 0.048821814358234406, + -0.016693273559212685, + 0.008068449795246124, + 0.04566117003560066, + -0.09829569607973099, + -0.026971371844410896, + 0.05381541699171066, + -0.3659301698207855, + 0.3473235070705414, + 0.14521746337413788, + 0.11228122562170029, + -0.041056130081415176, + -0.11228874325752258, + 0.006667478010058403, + 0.15931302309036255, + -0.30010080337524414, + 0.3464723229408264, + 0.4476386308670044, + -0.3498152494430542, + 0.2616507112979889, + -0.19995814561843872, + 0.10946320742368698, + 0.4034257233142853, + -0.08651446551084518, + 0.018647747114300728, + 0.11572548002004623, + -0.100877545773983, + -0.16341210901737213, + 0.2377898246049881, + 0.3417612910270691, + -0.49084869027137756, + -0.02805873565375805, + -0.09811390936374664, + 0.17161016166210175, + 0.3627470135688782, + -0.08954513072967529, + 0.06629404425621033, + 0.012786897830665112, + 0.01578289456665516, + -0.32630467414855957, + 0.4854920506477356, + 0.12709765136241913, + -0.4909423291683197, + -0.3745254874229431, + -0.6513142585754395, + -0.040075208991765976, + -0.569782018661499, + -0.009953420609235764, + 0.04735071584582329, + 0.0230120699852705, + -0.07381311058998108, + -0.06293600797653198, + 0.20196016132831573, + 0.26551517844200134, + -0.42071688175201416, + 0.28809165954589844, + 0.19747501611709595, + -0.5686206221580505, + -0.5285986661911011, + 0.02009684592485428, + 0.11322621256113052, + -0.1082596555352211, + -0.0856761634349823, + -0.04493662342429161, + -0.6179490089416504, + -0.1442672610282898, + 0.028762176632881165, + 0.12426868081092834, + -0.5771384835243225, + 0.1608373522758484, + 0.004147801548242569, + -0.047590240836143494, + 0.10347189754247665, + 0.11780986934900284, + -0.08490656316280365, + -0.0746934711933136, + 0.15699702501296997, + 0.1298881322145462, + -0.14411042630672455, + -0.08601037412881851, + 0.2997709810733795, + -0.05418943241238594, + -0.1772651970386505, + 0.04576871916651726, + -0.13510753214359283, + -0.057203926146030426, + 0.18647770583629608, + 0.0055348677560687065, + -0.12238732725381851, + -0.11199415475130081, + 0.43077343702316284, + 0.1349855363368988, + 0.21327465772628784, + 0.05924845486879349, + 0.12549948692321777, + -0.060076650232076645, + 0.23921678960323334, + 0.02152605727314949, + -0.1352948695421219, + 0.09325127303600311, + -0.14411674439907074, + 0.010495728813111782, + 0.11577513813972473, + -0.07580242305994034, + 0.42641204595565796, + -0.5557231903076172, + -0.12044595927000046, + 0.024152765050530434, + -0.14175696671009064, + 0.024960221722722054, + 0.10017693042755127, + -0.07402117550373077, + 0.09156208485364914, + 0.455565482378006, + 0.424320250749588, + -0.07668061554431915, + 0.10318724811077118, + -0.32521969079971313, + -0.2653461694717407, + -0.03919212520122528, + 0.12909358739852905, + -0.17091549932956696, + 0.07353391498327255, + 0.11510979384183884, + -0.23758216202259064, + -0.3059186339378357, + -0.046047650277614594, + 0.17527209222316742, + 0.19020265340805054, + -0.20766229927539825, + -0.23476286232471466, + -0.14011070132255554, + 0.1085173636674881, + -0.020777594298124313, + 0.014691418968141079, + 0.21648286283016205, + -0.21576255559921265, + 0.28203028440475464, + 0.6320008635520935, + -0.23609709739685059, + 0.16072526574134827, + 0.30149686336517334, + -0.05675647035241127, + -0.018186205998063087, + -0.1844293773174286, + 0.13510139286518097, + 0.05780869722366333, + 0.07202577590942383, + 0.07459436357021332, + 0.18700383603572845, + -0.09449177235364914, + 0.057188909500837326, + 0.21453143656253815, + -0.30002379417419434, + -0.12217795103788376, + 0.03723505884408951, + -0.18360234797000885, + -0.029992947354912758, + 0.10999765247106552, + 0.09575961530208588, + -0.36028456687927246, + -0.4311397075653076, + 0.5812231302261353, + ], + b1: &[ + 0.3801889419555664, + -0.5001883506774902, + 0.19484910368919373, + 0.6488791704177856, + 0.38620173931121826, + 0.8780303597450256, + -0.1126403734087944, + 0.021730314940214157, + -0.7806469202041626, + -0.04312174394726753, + 0.3102167546749115, + 0.9241658449172974, + 0.8900863528251648, + -0.2938256561756134, + -0.5012822151184082, + -0.00329477502964437, + 0.5169500708580017, + 0.4563848376274109, + -0.4903448224067688, + 0.27919942140579224, + -0.4288303554058075, + -0.1836952418088913, + -0.09118890762329102, + 0.5528226494789124, + -0.19896377623081207, + 0.33588215708732605, + 0.07895006239414215, + 0.07812929153442383, + 0.6203332543373108, + 0.8427650332450867, + -0.684628427028656, + 0.5408275723457336, + -0.5548633933067322, + -0.49557214975357056, + 0.7953769564628601, + -0.4109633266925812, + -0.6270897388458252, + -0.43285393714904785, + -0.7562689781188965, + -0.7167727947235107, + ], + w2: &[ + 0.15592391788959503, + 0.25119924545288086, + -0.499594122171402, + -0.5441639423370361, + -0.11186911165714264, + -0.6334478855133057, + 0.28880706429481506, + -0.592946469783783, + 0.7188563942909241, + -0.49322614073753357, + -0.1398385912179947, + -0.1868145614862442, + 0.9288992881774902, + -0.07525540888309479, + 0.2288437783718109, + 0.09932874143123627, + 0.2782813012599945, + -0.12644614279270172, + -0.14151062071323395, + 0.38845404982566833, + 0.2691279947757721, + -0.9148958921432495, + 0.19230225682258606, + 0.6098687052726746, + -0.24782557785511017, + -0.6989489197731018, + -0.30721813440322876, + -0.4890380799770355, + -0.43724432587623596, + -0.38428765535354614, + -0.6491377353668213, + -0.28134995698928833, + -0.36228886246681213, + -0.05963568389415741, + 0.5086851119995117, + 0.4664144814014435, + 0.3797634541988373, + 0.5596290826797485, + -0.1977449357509613, + 0.6540879607200623, + -0.24533972144126892, + 0.6865915656089783, + -0.18364377319812775, + 0.0013501447392627597, + -0.4037604331970215, + -0.287411093711853, + -0.43570032715797424, + -0.4085054099559784, + 0.7341827750205994, + -0.29973891377449036, + -0.18240050971508026, + -0.23446109890937805, + 0.7225431799888611, + 0.008502814918756485, + 0.04582007974386215, + 0.03352205455303192, + 0.12457727640867233, + -0.2019437849521637, + -0.1299249827861786, + -0.09946829080581665, + 0.40665051341056824, + -0.6841736435890198, + -0.523845911026001, + 0.21656402945518494, + 0.6046024560928345, + -0.6393186450004578, + -0.3965637981891632, + -0.7872777581214905, + -0.13687947392463684, + -0.19312888383865356, + -0.5453231930732727, + -0.21912647783756256, + 0.011589044705033302, + 0.2665385603904724, + 0.3249806761741638, + 0.293254017829895, + 0.1047254130244255, + 0.4246895909309387, + -0.0033608688972890377, + 0.4066942632198334, + 0.06138676777482033, + 0.382074236869812, + 0.0787188857793808, + -0.28631800413131714, + -0.3500039279460907, + -0.1490340679883957, + -0.14991725981235504, + -0.180477574467659, + 0.15140952169895172, + -0.35168370604515076, + 0.38904908299446106, + -0.11262823641300201, + -0.18404939770698547, + 0.5045862197875977, + 0.23344825208187103, + 0.6740546226501465, + -0.054060351103544235, + -0.47260594367980957, + 0.287933886051178, + 0.28975099325180054, + 0.2366262525320053, + -0.1751112937927246, + -0.15358465909957886, + -0.062381260097026825, + 0.45881521701812744, + -0.12647950649261475, + 0.45258036255836487, + -0.21084383130073547, + -0.15994171798229218, + -0.4229416847229004, + -0.18642400205135345, + -0.2506699860095978, + 0.20604389905929565, + 0.16662882268428802, + -0.23073841631412506, + 0.045810505747795105, + 0.33520498871803284, + 0.37685254216194153, + 0.11563336104154587, + 0.22259201109409332, + -0.010484708473086357, + -0.45855188369750977, + 0.24794596433639526, + 0.33667632937431335, + 0.20378778874874115, + 0.4198003113269806, + 0.23384596407413483, + 0.23601709306240082, + -0.509751558303833, + 0.5694931149482727, + -0.08933047205209732, + 0.037133198231458664, + 0.20635388791561127, + -0.2857131361961365, + -0.4278101921081543, + -0.26602792739868164, + 0.1998632550239563, + 0.4324374794960022, + -0.13389578461647034, + 0.11837134510278702, + -0.17028754949569702, + 0.37928706407546997, + 0.10062910616397858, + -0.04736608266830444, + -0.04692180082201958, + 0.6633663773536682, + -0.3517492711544037, + 0.2055688351392746, + 0.44142597913742065, + 0.42460545897483826, + 0.4567111134529114, + 0.3061029016971588, + -0.16390416026115417, + -0.3541538417339325, + 0.2544074058532715, + -0.18162837624549866, + -0.21904821693897247, + -0.2520917057991028, + -0.07266020774841309, + -0.23432950675487518, + -0.1989256739616394, + 0.09460597485303879, + -0.24563294649124146, + 0.9719013571739197, + 0.2578149139881134, + 0.26680076122283936, + -0.39480605721473694, + 0.22382304072380066, + -0.4284250736236572, + 0.4294125437736511, + -0.04923247918486595, + 0.5011574625968933, + 0.1887599676847458, + -0.02984841726720333, + -0.16428305208683014, + -0.33957910537719727, + -0.16184143722057343, + 0.37313663959503174, + -0.11775537580251694, + -0.34507161378860474, + -0.24848994612693787, + 0.3492432236671448, + -0.2122095823287964, + -0.022055158391594887, + 0.07298140972852707, + 0.36230477690696716, + -0.2514148950576782, + 0.11675992608070374, + 0.4010731875896454, + 0.31790846586227417, + 0.0585796944797039, + 0.30878275632858276, + 0.5536429286003113, + -0.061644136905670166, + -0.06381722539663315, + -0.1873038411140442, + -0.24746698141098022, + -0.3139619529247284, + -0.19278131425380707, + -0.48264867067337036, + 0.5122742056846619, + 0.09536745399236679, + 0.17870695888996124, + 0.18145892024040222, + 0.2471739798784256, + -0.16399677097797394, + -0.18874068558216095, + 0.21305255591869354, + -0.6930050253868103, + -0.4031701982021332, + 0.5250658392906189, + 0.4295860230922699, + -0.464653879404068, + -0.026941847056150436, + -0.08213993161916733, + 0.34638163447380066, + -0.15401627123355865, + 0.021148433908820152, + 0.19726167619228363, + -0.25100240111351013, + 3.085673233726993e-05, + 0.16563303768634796, + -0.008333534933626652, + -0.02890022285282612, + -0.284770667552948, + 0.3429299592971802, + 0.6073935627937317, + -0.10915102809667587, + 0.3420248329639435, + 0.07347360253334045, + 0.18400518596172333, + 0.2084905058145523, + 0.3218590021133423, + 0.16883575916290283, + -0.6880696415901184, + -0.37455135583877563, + 0.04792584478855133, + -0.04572531208395958, + -0.17001567780971527, + -0.12369263172149658, + -0.3716808259487152, + -0.04167286679148674, + 0.04307235777378082, + -0.1655367612838745, + -0.47902533411979675, + -0.21886907517910004, + 0.4065888226032257, + 0.30626556277275085, + 0.25965678691864014, + 0.07168732583522797, + -0.17138782143592834, + -0.6293558478355408, + -0.6350710988044739, + 0.25923609733581543, + 0.5668261647224426, + -0.030662082135677338, + -0.7059182524681091, + -0.25901535153388977, + 0.25449642539024353, + -0.3232290744781494, + 0.42758384346961975, + 0.7120643258094788, + 0.023215001448988914, + -0.40807682275772095, + 0.1332295536994934, + -0.33705568313598633, + 0.1038941740989685, + 0.39904412627220154, + -0.567590057849884, + -0.26575762033462524, + 0.7635160088539124, + -0.38967835903167725, + -0.08988548815250397, + 0.4150312840938568, + -0.540441632270813, + 0.33467426896095276, + -0.03507159277796745, + 0.00720902718603611, + 0.6702240109443665, + 0.2707512676715851, + ], + b2: &[ + 0.3580038547515869, + 0.06861710548400879, + -0.04651366174221039, + 0.24638813734054565, + 0.1557426154613495, + -0.40271297097206116, + -0.405432790517807, + ], + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32/cortex_a7.txt b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32/cortex_a7.txt new file mode 100644 index 0000000..8cfc692 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32/cortex_a7.txt @@ -0,0 +1,1701 @@ +armv7neon_mmm_f32_8x4_cortexa7 16 128 8 0.000019373978862224142 +armv7neon_mmm_f32_8x6_generic 24 4 18 0.000005589467629481233 +armv7neon_mmm_f32_8x6_cortexa7 24 32 5 0.0000068541067994687505 +armv7neon_mmm_f32_8x6_cortexa7 24 128 12 0.000038981217414944424 +generic_f32_4x4 4 32 3 0.000001790047741390679 +armv7neon_mmm_f32_8x4_generic 17 32 12 0.000013250293625322834 +armv7neon_mmm_f32_8x4_cortexa7 9 4 9 0.00000392359813080775 +armv7neon_mmm_f32_8x6_generic 16 128 18 0.00003780372998360008 +armv7neon_mmm_f32_8x6_generic 7 32 7 0.000004508038841829227 +armv7neon_mmm_f32_8x4_cortexa9 25 128 4 0.00001979557034383245 +armv7neon_mmm_f32_8x4_cortexa7 24 128 5 0.00002916219359816615 +generic_f32_4x4 8 4 7 0.000002792935337190097 +armv7neon_mmm_f32_8x6_generic 25 32 5 0.000008513973930688824 +armv7neon_mmm_f32_8x4_generic 8 128 4 0.000004984706018353683 +armv7neon_mmm_f32_8x6_cortexa7 23 4 6 0.0000024972411854271217 +armv7neon_mmm_f32_8x6_cortexa9 17 32 18 0.000018034380706056615 +generic_f32_4x4 9 32 5 0.000007787097193216308 +armv7neon_mmm_f32_8x4_cortexa7 7 32 11 0.000005445588694072235 +armv7neon_mmm_f32_8x4_cortexa9 7 4 3 0.0000011467255960994079 +armv7neon_mmm_f32_8x6_cortexa7 7 128 6 0.0000071177868474168225 +armv7neon_mmm_f32_8x6_generic 15 128 5 0.000013291532044598022 +armv7neon_mmm_f32_8x6_cortexa7 25 4 18 0.000007704060547781454 +armv7neon_mmm_f32_8x4_generic 15 128 11 0.000028366457109510148 +armv7neon_mmm_f32_8x6_cortexa7 7 4 12 0.000002031241642569242 +generic_f32_4x4 12 32 9 0.000011111678190760999 +armv7neon_mmm_f32_8x6_cortexa9 24 32 17 0.000018402463967012353 +armv7neon_mmm_f32_8x4_cortexa7 8 4 12 0.0000019146048075747953 +armv7neon_mmm_f32_8x4_cortexa7 23 4 8 0.00000383750508652125 +armv7neon_mmm_f32_8x4_cortexa7 25 4 7 0.0000052043882186278224 +armv7neon_mmm_f32_8x4_generic 16 32 12 0.000008739868831484192 +generic_f32_4x4 12 4 8 0.00000347003352464419 +armv7neon_mmm_f32_8x6_cortexa7 25 32 19 0.00003152139986100901 +armv7neon_mmm_f32_8x6_cortexa9 25 32 18 0.000023769189598361993 +armv7neon_mmm_f32_8x4_cortexa7 15 128 5 0.00001982738009364039 +armv7neon_mmm_f32_8x6_generic 9 128 11 0.000025754948633914126 +armv7neon_mmm_f32_8x4_cortexa7 15 4 4 0.0000016751657126978973 +armv7neon_mmm_f32_8x4_cortexa9 24 32 8 0.000009220363478862854 +generic_f32_4x4 3 32 13 0.000005627827228507873 +armv7neon_mmm_f32_8x6_cortexa9 23 128 13 0.00006012344410992782 +armv7neon_mmm_f32_8x6_cortexa7 24 128 6 0.00001973568156590405 +armv7neon_mmm_f32_8x4_generic 17 128 8 0.000027722972081041346 +armv7neon_mmm_f32_8x4_generic 9 128 4 0.000009605985891246275 +armv7neon_mmm_f32_8x4_cortexa7 15 32 3 0.000003754036592781278 +armv7neon_mmm_f32_8x4_cortexa7 25 128 8 0.00003835708390551408 +armv7neon_mmm_f32_8x4_cortexa9 9 32 4 0.0000035373293731924376 +generic_f32_4x4 7 4 11 0.000004044788183106201 +armv7neon_mmm_f32_8x6_cortexa9 7 32 12 0.000004744792047894666 +armv7neon_mmm_f32_8x4_cortexa9 23 4 8 0.0000038749254387558 +generic_f32_4x4 9 32 8 0.0000075635927841880176 +armv7neon_mmm_f32_8x4_cortexa7 9 4 12 0.000003727778947810718 +armv7neon_mmm_f32_8x6_cortexa7 23 32 5 0.0000067956400405037536 +generic_f32_4x4 8 4 5 0.000002723326536848814 +armv7neon_mmm_f32_8x6_cortexa9 15 32 5 0.000004690163698982028 +armv7neon_mmm_f32_8x4_cortexa9 9 32 3 0.0000037317693401172363 +armv7neon_mmm_f32_8x6_cortexa7 25 128 13 0.00007831443093305969 +armv7neon_mmm_f32_8x4_cortexa7 24 32 12 0.00001331331475399187 +armv7neon_mmm_f32_8x6_cortexa7 24 128 19 0.00007802209851667727 +armv7neon_mmm_f32_8x4_generic 16 128 11 0.00002788579513287149 +armv7neon_mmm_f32_8x4_cortexa7 17 128 4 0.000014706021218529391 +armv7neon_mmm_f32_8x6_generic 8 4 13 0.0000023378593097707485 +armv7neon_mmm_f32_8x6_generic 7 4 7 0.0000019186954240278283 +armv7neon_mmm_f32_8x4_cortexa9 16 4 8 0.000002428666090877155 +armv7neon_mmm_f32_8x6_generic 24 128 13 0.00005734829975884668 +generic_f32_4x4 8 128 11 0.000021025833076703056 +armv7neon_mmm_f32_8x6_cortexa9 17 4 19 0.00000803059286526004 +armv7neon_mmm_f32_8x6_cortexa7 25 4 7 0.00000571019568400475 +armv7neon_mmm_f32_8x4_cortexa9 7 128 3 0.000005476007735837036 +generic_f32_4x4 9 32 9 0.000011314156862861827 +armv7neon_mmm_f32_8x4_cortexa9 8 32 3 0.000002175330632998311 +armv7neon_mmm_f32_8x4_cortexa7 9 128 13 0.00003871137553728846 +armv7neon_mmm_f32_8x6_cortexa7 16 32 19 0.00001594300556874623 +armv7neon_mmm_f32_8x4_cortexa7 17 4 13 0.000007095518187604073 +armv7neon_mmm_f32_8x4_generic 17 4 12 0.00000511222434537087 +armv7neon_mmm_f32_8x4_generic 23 32 3 0.000005240662819589997 +armv7neon_mmm_f32_8x6_generic 7 4 19 0.000003459698540347226 +armv7neon_mmm_f32_8x6_cortexa9 7 128 19 0.000027297846707037145 +armv7neon_mmm_f32_8x4_cortexa7 23 4 9 0.000005799914306902319 +armv7neon_mmm_f32_8x4_generic 23 4 4 0.0000021622122978664557 +armv7neon_mmm_f32_8x4_cortexa9 17 128 12 0.00004402054920694197 +armv7neon_mmm_f32_8x6_cortexa9 15 4 18 0.000004616359494672357 +armv7neon_mmm_f32_8x4_cortexa9 8 4 4 0.0000010041412858292361 +armv7neon_mmm_f32_8x4_cortexa9 8 32 13 0.000006496433067761439 +generic_f32_4x4 11 128 3 0.000011040772633263298 +armv7neon_mmm_f32_8x6_generic 24 4 11 0.000004523362892147976 +generic_f32_4x4 9 4 4 0.0000021034125372056796 +generic_f32_4x4 8 4 9 0.000003722094027081023 +generic_f32_4x4 4 4 8 0.0000014952745701485973 +armv7neon_mmm_f32_8x4_generic 16 128 3 0.000009908089584838082 +armv7neon_mmm_f32_8x4_generic 25 32 7 0.000012421086220512836 +armv7neon_mmm_f32_8x6_cortexa7 8 4 5 0.0000013403828133224992 +armv7neon_mmm_f32_8x4_cortexa7 23 32 4 0.00000503241852364444 +armv7neon_mmm_f32_8x6_cortexa9 7 32 6 0.000002618507739501653 +armv7neon_mmm_f32_8x4_cortexa7 7 4 13 0.0000031793808774197963 +armv7neon_mmm_f32_8x4_generic 16 4 7 0.00000282397574390546 +armv7neon_mmm_f32_8x4_cortexa9 17 128 4 0.00001500697741030371 +armv7neon_mmm_f32_8x6_cortexa7 24 4 6 0.000002233395192000468 +armv7neon_mmm_f32_8x6_cortexa9 15 128 6 0.0000138021942440989 +armv7neon_mmm_f32_8x4_cortexa9 15 128 11 0.000030164013207782058 +generic_f32_4x4 7 4 5 0.0000028577946255424533 +armv7neon_mmm_f32_8x4_cortexa9 16 128 5 0.000020020455876347578 +armv7neon_mmm_f32_8x6_cortexa9 16 128 6 0.000013512716817189273 +armv7neon_mmm_f32_8x4_cortexa7 16 4 11 0.0000037996360400635168 +armv7neon_mmm_f32_8x4_generic 7 32 4 0.0000021000959358733572 +armv7neon_mmm_f32_8x4_cortexa9 9 4 11 0.000004001256873073548 +armv7neon_mmm_f32_8x6_generic 24 128 7 0.00003837839936999645 +armv7neon_mmm_f32_8x4_cortexa9 15 128 4 0.00001034155567067011 +armv7neon_mmm_f32_8x4_generic 7 128 9 0.000014585897754194483 +armv7neon_mmm_f32_8x4_cortexa9 7 128 11 0.000015558039194528556 +armv7neon_mmm_f32_8x4_generic 25 32 11 0.000018056248294844393 +armv7neon_mmm_f32_8x6_cortexa7 9 32 6 0.0000044289694676171784 +armv7neon_mmm_f32_8x4_cortexa9 9 32 7 0.000006775203907440964 +armv7neon_mmm_f32_8x4_cortexa7 17 32 4 0.000004906536483038752 +armv7neon_mmm_f32_8x6_generic 25 128 13 0.00007697771420855431 +armv7neon_mmm_f32_8x6_generic 15 32 13 0.000012236156128465641 +armv7neon_mmm_f32_8x4_generic 9 32 11 0.000009363916532723868 +armv7neon_mmm_f32_8x6_cortexa9 16 32 7 0.000008475723903784643 +armv7neon_mmm_f32_8x4_cortexa9 24 4 8 0.000003400276973989579 +armv7neon_mmm_f32_8x4_generic 8 32 9 0.000004816590922050574 +armv7neon_mmm_f32_8x6_cortexa9 7 32 18 0.0000068452889719178265 +armv7neon_mmm_f32_8x4_cortexa9 9 32 5 0.000006726253331965845 +generic_f32_4x4 11 32 13 0.000015033889948226767 +armv7neon_mmm_f32_8x4_generic 17 4 11 0.000005556840437336695 +generic_f32_4x4 13 32 7 0.000010313627379766583 +generic_f32_4x4 9 4 3 0.0000023801388628702307 +armv7neon_mmm_f32_8x4_generic 8 128 3 0.00000520201390212873 +armv7neon_mmm_f32_8x4_generic 8 32 12 0.000004612565409735615 +armv7neon_mmm_f32_8x4_cortexa9 16 128 4 0.000010106142800191435 +armv7neon_mmm_f32_8x4_cortexa7 8 4 5 0.0000016424950539860917 +armv7neon_mmm_f32_8x4_generic 8 32 8 0.000003246701797046072 +armv7neon_mmm_f32_8x6_cortexa7 17 128 7 0.00003957490867985703 +armv7neon_mmm_f32_8x4_cortexa7 7 32 12 0.000005449789255433999 +armv7neon_mmm_f32_8x4_cortexa7 15 32 5 0.000006774977020301673 +armv7neon_mmm_f32_8x6_generic 7 4 13 0.0000027111208216897315 +armv7neon_mmm_f32_8x4_cortexa7 16 32 5 0.000006561637517767873 +generic_f32_4x4 3 128 9 0.000011017761032513612 +armv7neon_mmm_f32_8x4_cortexa7 23 128 5 0.00002948816149020999 +armv7neon_mmm_f32_8x4_cortexa7 17 32 11 0.00001417097516045665 +armv7neon_mmm_f32_8x6_cortexa7 16 4 12 0.000002812978829286788 +armv7neon_mmm_f32_8x6_cortexa9 23 32 5 0.0000068076361854689625 +armv7neon_mmm_f32_8x4_cortexa7 24 4 3 0.0000025622772317463136 +armv7neon_mmm_f32_8x4_cortexa9 16 32 13 0.000012465423813216206 +generic_f32_4x4 4 32 11 0.000004083209759238612 +armv7neon_mmm_f32_8x4_generic 7 32 5 0.000003626955080135103 +armv7neon_mmm_f32_8x6_cortexa7 23 128 6 0.000020078583004924116 +armv7neon_mmm_f32_8x6_cortexa9 25 128 19 0.00010584384897256878 +armv7neon_mmm_f32_8x6_generic 17 4 12 0.000004107213127611975 +armv7neon_mmm_f32_8x6_cortexa7 16 4 17 0.000004432708536915881 +armv7neon_mmm_f32_8x6_cortexa7 16 32 18 0.00001183035782221589 +armv7neon_mmm_f32_8x4_cortexa9 25 4 3 0.000003166221225598654 +armv7neon_mmm_f32_8x4_cortexa9 24 128 9 0.00004426766902863704 +armv7neon_mmm_f32_8x6_generic 25 4 18 0.000007554578118985422 +armv7neon_mmm_f32_8x6_cortexa7 15 32 5 0.000004708546886703234 +armv7neon_mmm_f32_8x6_cortexa9 9 128 6 0.000013657373073808522 +armv7neon_mmm_f32_8x6_cortexa7 25 32 7 0.00001623345709716141 +armv7neon_mmm_f32_8x4_cortexa7 9 32 5 0.000006618675517469529 +armv7neon_mmm_f32_8x4_cortexa7 7 32 13 0.000007030082349548267 +armv7neon_mmm_f32_8x4_cortexa9 17 4 7 0.0000040753817592878874 +armv7neon_mmm_f32_8x6_generic 15 4 5 0.0000019991656588201214 +generic_f32_4x4 12 128 5 0.00002108090993489815 +armv7neon_mmm_f32_8x6_cortexa9 25 128 17 0.00007984886358906369 +generic_f32_4x4 8 128 5 0.000014230862544269676 +armv7neon_mmm_f32_8x6_generic 24 128 18 0.000056743692014886694 +armv7neon_mmm_f32_8x4_cortexa9 8 32 8 0.0000034133387591211906 +armv7neon_mmm_f32_8x4_generic 9 128 12 0.000027800478110813017 +generic_f32_4x4 7 32 7 0.000005509675967097076 +armv7neon_mmm_f32_8x6_cortexa9 16 32 13 0.000012310372306220252 +armv7neon_mmm_f32_8x4_generic 15 128 13 0.00003762533200230424 +generic_f32_4x4 4 128 5 0.000007369710897637148 +armv7neon_mmm_f32_8x6_generic 16 128 6 0.000012947918354537283 +armv7neon_mmm_f32_8x4_cortexa7 8 32 3 0.000002138296476186818 +armv7neon_mmm_f32_8x4_generic 9 128 11 0.000028058363419186727 +armv7neon_mmm_f32_8x4_cortexa7 7 128 13 0.00002008308819140427 +armv7neon_mmm_f32_8x6_cortexa9 15 128 12 0.000027068411466415602 +armv7neon_mmm_f32_8x6_generic 8 128 12 0.000012940488826817833 +armv7neon_mmm_f32_8x4_generic 24 32 7 0.00000940385598649166 +armv7neon_mmm_f32_8x6_cortexa7 15 128 19 0.00005293187157460067 +armv7neon_mmm_f32_8x4_cortexa9 25 128 3 0.000020446918539621497 +armv7neon_mmm_f32_8x4_cortexa7 17 4 9 0.000005534537134563046 +armv7neon_mmm_f32_8x6_cortexa9 15 128 19 0.00005366369135659099 +armv7neon_mmm_f32_8x4_generic 25 4 4 0.000002522928321866339 +armv7neon_mmm_f32_8x6_generic 16 4 19 0.000005280876227150452 +armv7neon_mmm_f32_8x6_generic 23 128 12 0.000038470533759474454 +armv7neon_mmm_f32_8x4_generic 16 32 3 0.00000367665371967417 +armv7neon_mmm_f32_8x6_cortexa7 7 32 12 0.000004702345831209106 +armv7neon_mmm_f32_8x6_generic 16 32 11 0.000008300619599261807 +armv7neon_mmm_f32_8x4_cortexa9 23 4 5 0.000004136206632272795 +armv7neon_mmm_f32_8x4_cortexa9 23 32 7 0.000010130368574328868 +armv7neon_mmm_f32_8x4_generic 7 128 12 0.000014642209408823472 +armv7neon_mmm_f32_8x4_generic 16 4 3 0.0000018668229452058654 +armv7neon_mmm_f32_8x4_cortexa9 24 128 8 0.00002933769331717579 +armv7neon_mmm_f32_8x4_cortexa9 15 32 5 0.0000068812833008684075 +armv7neon_mmm_f32_8x6_generic 16 4 17 0.000004317183704154257 +armv7neon_mmm_f32_8x6_cortexa9 17 32 7 0.000012443561351360363 +generic_f32_4x4 13 128 7 0.000028155411441347644 +armv7neon_mmm_f32_8x6_cortexa9 25 4 12 0.000005357355009390057 +armv7neon_mmm_f32_8x6_cortexa7 9 128 18 0.00003939231793264078 +generic_f32_4x4 9 128 13 0.00004157559429631316 +armv7neon_mmm_f32_8x6_cortexa9 23 32 11 0.000012894467716591857 +armv7neon_mmm_f32_8x6_generic 17 32 19 0.000023157961942761285 +armv7neon_mmm_f32_8x6_generic 23 128 13 0.00005762004261565962 +armv7neon_mmm_f32_8x6_generic 25 32 12 0.000015419463508057784 +armv7neon_mmm_f32_8x6_generic 23 32 19 0.000023607314480173466 +generic_f32_4x4 11 4 9 0.0000056154966368798884 +armv7neon_mmm_f32_8x4_generic 23 4 3 0.000002512455070432762 +armv7neon_mmm_f32_8x6_generic 17 128 12 0.000038205761728052566 +armv7neon_mmm_f32_8x6_cortexa9 25 4 6 0.0000029441583370270357 +armv7neon_mmm_f32_8x6_cortexa7 8 128 5 0.00000718614713533821 +armv7neon_mmm_f32_8x6_cortexa9 9 32 11 0.000008580295552398777 +armv7neon_mmm_f32_8x4_generic 17 4 8 0.0000036027501506567706 +armv7neon_mmm_f32_8x4_cortexa9 24 128 12 0.000043681280972474085 +armv7neon_mmm_f32_8x6_cortexa7 23 128 19 0.00007892740320659124 +armv7neon_mmm_f32_8x4_cortexa7 7 4 7 0.000001867957586629068 +armv7neon_mmm_f32_8x4_cortexa7 17 128 3 0.000015105393976853567 +armv7neon_mmm_f32_8x6_cortexa9 16 32 5 0.000004737870196682113 +armv7neon_mmm_f32_8x6_cortexa7 17 32 11 0.000012513663992372357 +armv7neon_mmm_f32_8x4_cortexa9 8 4 13 0.000002615891904894993 +generic_f32_4x4 9 128 8 0.00002092560518618388 +armv7neon_mmm_f32_8x6_cortexa9 24 4 13 0.000006118924275544352 +armv7neon_mmm_f32_8x4_cortexa9 23 128 12 0.00004443207751479167 +armv7neon_mmm_f32_8x4_cortexa9 9 32 13 0.000012776770370453443 +armv7neon_mmm_f32_8x4_cortexa7 7 128 4 0.000005416694010439176 +generic_f32_4x4 3 32 3 0.0000017607189056083412 +generic_f32_4x4 8 32 13 0.00000985693332598433 +armv7neon_mmm_f32_8x4_generic 15 128 9 0.00002827090625785394 +armv7neon_mmm_f32_8x6_generic 17 4 5 0.0000026659420735671337 +armv7neon_mmm_f32_8x6_cortexa9 7 4 19 0.000003496314614706576 +generic_f32_4x4 9 32 12 0.00001103802239472411 +armv7neon_mmm_f32_8x6_cortexa7 24 32 19 0.00002361513173387037 +armv7neon_mmm_f32_8x6_cortexa9 24 32 13 0.00001816124851845242 +armv7neon_mmm_f32_8x6_cortexa7 24 4 19 0.000007820954390784165 +armv7neon_mmm_f32_8x4_cortexa9 16 4 9 0.0000037661558813876353 +armv7neon_mmm_f32_8x6_generic 25 4 13 0.000007941364297025098 +armv7neon_mmm_f32_8x6_cortexa7 23 128 7 0.0000397790896264826 +armv7neon_mmm_f32_8x4_cortexa7 23 32 12 0.000014064248486107495 +armv7neon_mmm_f32_8x6_cortexa9 25 32 17 0.000024451766821636896 +armv7neon_mmm_f32_8x4_generic 16 32 13 0.000011863952520137507 +armv7neon_mmm_f32_8x6_cortexa9 15 4 13 0.000004662604065748643 +armv7neon_mmm_f32_8x4_cortexa7 16 128 13 0.00003851383301865242 +armv7neon_mmm_f32_8x4_cortexa9 17 4 3 0.0000024871767077518544 +armv7neon_mmm_f32_8x6_generic 9 128 18 0.000038275747204244 +armv7neon_mmm_f32_8x4_cortexa7 23 4 7 0.000004236340851810833 +armv7neon_mmm_f32_8x4_generic 24 32 12 0.000012855667391841598 +armv7neon_mmm_f32_8x6_cortexa9 15 32 17 0.000012917112497463966 +armv7neon_mmm_f32_8x4_generic 23 128 11 0.000042177094013740186 +armv7neon_mmm_f32_8x4_cortexa7 16 128 12 0.000028624335558632763 +armv7neon_mmm_f32_8x6_cortexa7 23 4 17 0.000006797176857939227 +armv7neon_mmm_f32_8x4_generic 25 4 3 0.0000031059316600251953 +armv7neon_mmm_f32_8x6_cortexa7 23 4 19 0.000008441504854510506 +armv7neon_mmm_f32_8x6_cortexa7 23 128 17 0.00005957716872417335 +armv7neon_mmm_f32_8x4_generic 24 32 3 0.000005255472766158138 +armv7neon_mmm_f32_8x6_cortexa7 7 32 6 0.0000025969460967210575 +generic_f32_4x4 12 128 9 0.00003118081783563989 +armv7neon_mmm_f32_8x6_cortexa7 24 32 7 0.000012316039376583595 +armv7neon_mmm_f32_8x4_cortexa9 9 128 11 0.00002984202658897139 +armv7neon_mmm_f32_8x6_generic 24 4 12 0.00000390465698063092 +armv7neon_mmm_f32_8x6_generic 24 128 5 0.000019825223712031248 +generic_f32_4x4 11 128 12 0.00003129955987835105 +armv7neon_mmm_f32_8x4_generic 15 32 8 0.000006474371340367591 +armv7neon_mmm_f32_8x6_cortexa7 25 128 17 0.00007864370121172857 +armv7neon_mmm_f32_8x4_cortexa7 7 4 9 0.000002506847196975368 +armv7neon_mmm_f32_8x6_cortexa9 25 4 11 0.000005933707367961421 +armv7neon_mmm_f32_8x4_cortexa9 16 4 5 0.000002787654264318038 +armv7neon_mmm_f32_8x6_generic 17 32 13 0.00001761805014832438 +armv7neon_mmm_f32_8x4_cortexa9 7 32 4 0.0000021905945506812955 +armv7neon_mmm_f32_8x6_cortexa9 17 128 11 0.00004027130474721357 +generic_f32_4x4 7 32 9 0.000007912119634032119 +armv7neon_mmm_f32_8x4_generic 23 4 13 0.0000073828945636745854 +generic_f32_4x4 12 128 11 0.00003129061011102201 +armv7neon_mmm_f32_8x6_cortexa7 9 4 5 0.0000019755012039429016 +armv7neon_mmm_f32_8x6_cortexa9 8 4 13 0.000002405709930798169 +armv7neon_mmm_f32_8x6_cortexa9 24 4 7 0.0000044131675061571695 +armv7neon_mmm_f32_8x4_cortexa7 25 128 3 0.00002004605546076412 +armv7neon_mmm_f32_8x6_cortexa7 25 32 11 0.000016504451247846665 +generic_f32_4x4 11 4 3 0.0000024035209703323816 +generic_f32_4x4 5 128 9 0.00002116685987655445 +armv7neon_mmm_f32_8x4_generic 9 32 9 0.000009310383786805071 +armv7neon_mmm_f32_8x6_cortexa7 15 32 12 0.00000858295982977344 +generic_f32_4x4 9 4 9 0.0000055122591860013075 +armv7neon_mmm_f32_8x4_cortexa7 24 128 4 0.000014582241975346975 +armv7neon_mmm_f32_8x6_cortexa7 7 32 11 0.0000047102983594351624 +armv7neon_mmm_f32_8x6_cortexa7 17 32 5 0.000006703902999940797 +armv7neon_mmm_f32_8x4_cortexa9 9 32 12 0.000009569569775041666 +generic_f32_4x4 11 32 4 0.000004102122527868448 +armv7neon_mmm_f32_8x4_cortexa7 23 128 11 0.00004393294052475175 +armv7neon_mmm_f32_8x6_cortexa7 17 128 18 0.00005867315609945908 +armv7neon_mmm_f32_8x6_generic 15 4 19 0.000005888722731281036 +armv7neon_mmm_f32_8x4_cortexa9 25 128 12 0.000058416478283637004 +armv7neon_mmm_f32_8x6_cortexa7 15 128 18 0.00003981402002143062 +armv7neon_mmm_f32_8x6_generic 17 32 11 0.000012156545175144957 +armv7neon_mmm_f32_8x6_cortexa9 17 128 5 0.000020505815042923473 +armv7neon_mmm_f32_8x4_generic 23 4 8 0.0000037989035755978144 +armv7neon_mmm_f32_8x4_cortexa9 23 128 4 0.000015133679307570648 +generic_f32_4x4 12 32 3 0.000004382513702192395 +generic_f32_4x4 12 4 5 0.000003824564176783666 +generic_f32_4x4 5 32 7 0.000005434572832921092 +generic_f32_4x4 3 4 8 0.0000017566767353451639 +armv7neon_mmm_f32_8x6_cortexa7 7 32 13 0.0000067229229930212575 +armv7neon_mmm_f32_8x4_generic 8 4 13 0.0000025649388423337978 +generic_f32_4x4 11 32 12 0.000011236073634740423 +armv7neon_mmm_f32_8x6_generic 25 4 17 0.000008196880964751204 +armv7neon_mmm_f32_8x6_cortexa9 7 32 11 0.000004753650782271882 +armv7neon_mmm_f32_8x4_cortexa9 7 128 4 0.000005529569938400554 +generic_f32_4x4 13 4 13 0.000009131495505295274 +armv7neon_mmm_f32_8x6_cortexa9 9 4 7 0.00000312421222650566 +armv7neon_mmm_f32_8x6_generic 17 32 7 0.000011970222410677647 +armv7neon_mmm_f32_8x4_cortexa7 7 4 3 0.0000011523161892903704 +armv7neon_mmm_f32_8x6_cortexa7 23 128 18 0.00005908293678660039 +armv7neon_mmm_f32_8x4_cortexa9 23 4 13 0.000007508665249371487 +armv7neon_mmm_f32_8x4_cortexa9 7 32 8 0.000003875838102020609 +armv7neon_mmm_f32_8x6_cortexa7 24 32 11 0.00001256332295885272 +armv7neon_mmm_f32_8x6_cortexa9 23 128 6 0.000020319056425196668 +armv7neon_mmm_f32_8x4_generic 23 32 4 0.00000488125169723422 +armv7neon_mmm_f32_8x6_generic 24 32 18 0.00001705594140661088 +armv7neon_mmm_f32_8x4_generic 7 4 3 0.000001143118878212571 +armv7neon_mmm_f32_8x4_generic 24 4 3 0.000002536821660220204 +armv7neon_mmm_f32_8x6_cortexa9 7 4 17 0.000002840883149941229 +armv7neon_mmm_f32_8x6_cortexa7 17 32 13 0.000018113836070828896 +armv7neon_mmm_f32_8x6_cortexa7 24 4 5 0.000002893802636827323 +armv7neon_mmm_f32_8x4_cortexa9 7 128 8 0.00001055308814900546 +armv7neon_mmm_f32_8x4_cortexa9 15 4 4 0.0000017017415253459248 +armv7neon_mmm_f32_8x6_generic 9 128 17 0.00003847119775045202 +armv7neon_mmm_f32_8x4_cortexa9 17 128 7 0.00003001254835210308 +generic_f32_4x4 7 128 3 0.0000075145364744355526 +armv7neon_mmm_f32_8x6_cortexa9 24 128 13 0.00005963153865876477 +armv7neon_mmm_f32_8x6_generic 16 4 11 0.0000031955847464919697 +armv7neon_mmm_f32_8x6_generic 24 128 17 0.000057273707239946334 +armv7neon_mmm_f32_8x6_cortexa9 16 128 17 0.00004016422558583101 +armv7neon_mmm_f32_8x4_generic 24 4 7 0.000003971324732226714 +armv7neon_mmm_f32_8x6_cortexa9 25 32 11 0.0000166679830493271 +armv7neon_mmm_f32_8x4_generic 7 128 3 0.000005169798410902207 +armv7neon_mmm_f32_8x6_cortexa7 15 4 11 0.000003482445418673263 +armv7neon_mmm_f32_8x6_cortexa7 8 128 6 0.0000069109869984566186 +armv7neon_mmm_f32_8x4_cortexa9 24 32 13 0.000018445950950001155 +armv7neon_mmm_f32_8x6_generic 8 4 11 0.0000018570966900607452 +armv7neon_mmm_f32_8x6_cortexa9 24 4 6 0.000002228168146536169 +armv7neon_mmm_f32_8x6_generic 8 128 7 0.000013146479087947972 +armv7neon_mmm_f32_8x6_cortexa9 25 32 12 0.000016067383961246167 +armv7neon_mmm_f32_8x4_cortexa9 16 32 5 0.000006662778599544951 +armv7neon_mmm_f32_8x6_generic 9 4 11 0.0000031451298378122295 +armv7neon_mmm_f32_8x4_generic 16 32 7 0.000006438918321306648 +armv7neon_mmm_f32_8x6_cortexa9 7 32 7 0.000004657116192635584 +armv7neon_mmm_f32_8x6_generic 17 32 12 0.00001176030032709091 +armv7neon_mmm_f32_8x4_generic 15 4 8 0.0000028163110410039656 +armv7neon_mmm_f32_8x4_cortexa9 7 4 9 0.0000025260788973778824 +generic_f32_4x4 8 4 11 0.000003795516523798154 +armv7neon_mmm_f32_8x6_generic 15 32 6 0.0000044168833936544535 +armv7neon_mmm_f32_8x4_cortexa7 8 128 9 0.000014761720958613642 +armv7neon_mmm_f32_8x6_cortexa7 9 32 7 0.000008410754850495816 +armv7neon_mmm_f32_8x4_cortexa7 16 4 13 0.000004684953060253782 +armv7neon_mmm_f32_8x4_generic 16 32 5 0.000006354175934259964 +armv7neon_mmm_f32_8x4_generic 24 4 4 0.0000019148396982311507 +armv7neon_mmm_f32_8x4_generic 17 128 4 0.000014107689106847269 +armv7neon_mmm_f32_8x6_cortexa9 24 32 11 0.000012681731796133347 +generic_f32_4x4 3 4 5 0.0000017351550450721446 +armv7neon_mmm_f32_8x4_cortexa7 16 4 9 0.0000037512996244278985 +armv7neon_mmm_f32_8x6_cortexa9 23 32 13 0.000018600736039744996 +generic_f32_4x4 7 32 13 0.000010363567629711427 +generic_f32_4x4 7 128 13 0.000028209372228861907 +armv7neon_mmm_f32_8x6_generic 24 32 13 0.000017489604886914003 +armv7neon_mmm_f32_8x4_cortexa7 24 32 8 0.00000906737294441132 +armv7neon_mmm_f32_8x6_generic 15 32 5 0.000004544284306242495 +armv7neon_mmm_f32_8x6_cortexa9 16 128 19 0.00005301309753082383 +armv7neon_mmm_f32_8x6_cortexa7 15 128 11 0.000026895954754680612 +armv7neon_mmm_f32_8x4_cortexa9 15 32 12 0.000009927381424295026 +armv7neon_mmm_f32_8x4_cortexa9 16 128 12 0.00002922630339731252 +armv7neon_mmm_f32_8x4_cortexa9 24 4 11 0.000005481062579336857 +armv7neon_mmm_f32_8x6_generic 7 128 18 0.000019821467429857938 +armv7neon_mmm_f32_8x6_cortexa9 7 32 5 0.000002575926161215081 +armv7neon_mmm_f32_8x6_cortexa7 15 32 7 0.000008595071585263579 +armv7neon_mmm_f32_8x4_cortexa9 17 32 4 0.000005001583148868544 +armv7neon_mmm_f32_8x4_cortexa7 25 32 4 0.000006334740769256929 +armv7neon_mmm_f32_8x4_cortexa7 25 128 7 0.00003905527355458101 +generic_f32_4x4 4 4 5 0.0000016159570864189527 +armv7neon_mmm_f32_8x4_cortexa9 17 128 11 0.000044522329607090446 +armv7neon_mmm_f32_8x6_cortexa9 8 128 18 0.0000200309695734825 +armv7neon_mmm_f32_8x6_generic 24 32 19 0.000022973011330361666 +generic_f32_4x4 3 4 13 0.000002984204583865987 +armv7neon_mmm_f32_8x6_cortexa9 15 4 5 0.000002015871636210361 +generic_f32_4x4 7 4 9 0.000003999946420645267 +armv7neon_mmm_f32_8x4_generic 7 4 9 0.0000024834088306212943 +armv7neon_mmm_f32_8x4_cortexa7 25 128 9 0.00005782975703195741 +armv7neon_mmm_f32_8x4_generic 25 32 8 0.000011779346411467398 +armv7neon_mmm_f32_8x6_cortexa9 25 32 7 0.000016399753838712434 +armv7neon_mmm_f32_8x6_generic 24 4 6 0.000002204607070591687 +armv7neon_mmm_f32_8x6_cortexa7 9 32 17 0.000012402833540807695 +armv7neon_mmm_f32_8x4_cortexa7 25 128 13 0.00007672799293438395 +armv7neon_mmm_f32_8x6_generic 24 128 12 0.0000379228420372398 +armv7neon_mmm_f32_8x4_cortexa7 16 128 3 0.00001030544963732427 +armv7neon_mmm_f32_8x4_generic 15 128 3 0.000009885955298583161 +armv7neon_mmm_f32_8x4_generic 15 4 12 0.0000039769457516235065 +armv7neon_mmm_f32_8x6_generic 23 4 11 0.000004727342544050073 +armv7neon_mmm_f32_8x6_cortexa7 15 4 12 0.0000032858094110604857 +armv7neon_mmm_f32_8x4_cortexa7 16 4 3 0.0000018824484779381146 +armv7neon_mmm_f32_8x6_cortexa9 23 4 7 0.000004580846920018228 +generic_f32_4x4 11 4 8 0.00000379091798312253 +armv7neon_mmm_f32_8x6_cortexa9 23 32 18 0.00001843667314281391 +armv7neon_mmm_f32_8x6_generic 8 32 18 0.0000060026095209057905 +armv7neon_mmm_f32_8x4_cortexa7 8 32 7 0.00000356603711659208 +armv7neon_mmm_f32_8x6_generic 15 128 18 0.00003865113024109526 +armv7neon_mmm_f32_8x6_generic 7 32 5 0.0000025012501670870884 +generic_f32_4x4 12 128 3 0.00001108291043110956 +armv7neon_mmm_f32_8x6_cortexa9 9 128 17 0.00004019255842003589 +armv7neon_mmm_f32_8x4_cortexa9 15 128 3 0.000010489952376847188 +armv7neon_mmm_f32_8x4_cortexa9 7 4 5 0.0000018283099982388463 +armv7neon_mmm_f32_8x6_cortexa7 23 32 13 0.000018410568030938504 +armv7neon_mmm_f32_8x6_generic 15 4 17 0.000004759242853957918 +armv7neon_mmm_f32_8x6_cortexa7 17 128 5 0.00002027503703993735 +armv7neon_mmm_f32_8x4_generic 7 4 12 0.000002526741586689988 +armv7neon_mmm_f32_8x6_cortexa9 17 4 13 0.000006256702206265106 +armv7neon_mmm_f32_8x6_cortexa7 16 128 13 0.000039449772577428465 +generic_f32_4x4 5 32 4 0.0000028896370502300425 +armv7neon_mmm_f32_8x6_cortexa9 23 4 6 0.000002496256395769302 +armv7neon_mmm_f32_8x4_generic 17 4 9 0.000005469161034223508 +armv7neon_mmm_f32_8x4_generic 17 4 13 0.0000070099392970628585 +armv7neon_mmm_f32_8x6_generic 24 4 17 0.000006212264617752923 +armv7neon_mmm_f32_8x6_cortexa7 23 32 18 0.000018244504523002484 +armv7neon_mmm_f32_8x4_cortexa7 17 32 3 0.000005314636591625876 +armv7neon_mmm_f32_8x6_generic 23 4 13 0.00000640173388890707 +armv7neon_mmm_f32_8x6_cortexa7 25 4 19 0.00001046601238496823 +armv7neon_mmm_f32_8x4_cortexa7 9 128 5 0.000019669882407282677 +generic_f32_4x4 5 4 3 0.0000017330186385522962 +armv7neon_mmm_f32_8x6_generic 9 4 13 0.000004297017223755962 +armv7neon_mmm_f32_8x4_cortexa9 9 128 7 0.0000201239354954616 +armv7neon_mmm_f32_8x4_cortexa7 8 4 7 0.0000016739241733780589 +armv7neon_mmm_f32_8x6_cortexa7 7 4 6 0.000001271018362845475 +generic_f32_4x4 13 32 9 0.000014839920846066823 +armv7neon_mmm_f32_8x4_generic 25 4 9 0.000007058701573468786 +armv7neon_mmm_f32_8x4_cortexa7 7 32 5 0.000003729557835063842 +armv7neon_mmm_f32_8x6_cortexa9 7 128 5 0.000007173464418699434 +armv7neon_mmm_f32_8x6_cortexa7 24 128 13 0.00005881823134405547 +armv7neon_mmm_f32_8x6_cortexa9 17 32 12 0.000012231167345130436 +armv7neon_mmm_f32_8x4_cortexa7 8 4 4 0.0000009861752945826601 +armv7neon_mmm_f32_8x4_cortexa9 23 128 3 0.00001548775471015623 +armv7neon_mmm_f32_8x4_generic 8 128 9 0.000014166892351007583 +armv7neon_mmm_f32_8x4_cortexa9 25 32 3 0.000007055926590582058 +armv7neon_mmm_f32_8x6_cortexa7 8 32 6 0.0000023905642959740767 +armv7neon_mmm_f32_8x4_generic 24 4 12 0.000004705581936932163 +generic_f32_4x4 13 32 13 0.000019466458499229495 +armv7neon_mmm_f32_8x6_cortexa7 8 128 12 0.000013354945269855691 +generic_f32_4x4 4 32 8 0.000002784564616216757 +generic_f32_4x4 11 4 13 0.000007235980721358389 +armv7neon_mmm_f32_8x4_cortexa9 15 4 9 0.000004211132231298468 +armv7neon_mmm_f32_8x4_generic 23 4 7 0.000004197975758631663 +armv7neon_mmm_f32_8x4_generic 24 4 13 0.000006669346538195287 +generic_f32_4x4 9 4 11 0.000005605492408222339 +armv7neon_mmm_f32_8x4_cortexa9 7 4 7 0.0000018866714926916992 +armv7neon_mmm_f32_8x6_generic 7 4 12 0.00000203404671528183 +armv7neon_mmm_f32_8x6_cortexa9 16 32 12 0.000008156581758904169 +armv7neon_mmm_f32_8x6_generic 9 4 19 0.000005498641044027106 +generic_f32_4x4 7 4 8 0.000002762174951002869 +armv7neon_mmm_f32_8x4_cortexa9 16 4 3 0.0000019059695727093323 +armv7neon_mmm_f32_8x4_generic 25 4 12 0.00000651099979096713 +armv7neon_mmm_f32_8x6_cortexa7 24 128 5 0.000020414031398736494 +armv7neon_mmm_f32_8x6_cortexa7 7 128 18 0.000020361101695155362 +generic_f32_4x4 11 128 5 0.00002125700072345171 +armv7neon_mmm_f32_8x6_cortexa7 17 32 18 0.000017837908064150246 +armv7neon_mmm_f32_8x6_cortexa7 25 32 13 0.00002390699968855355 +armv7neon_mmm_f32_8x4_generic 23 4 5 0.0000040641271507242515 +generic_f32_4x4 8 4 3 0.0000017865876036353628 +armv7neon_mmm_f32_8x6_cortexa9 16 4 17 0.00000442887972122732 +armv7neon_mmm_f32_8x4_cortexa9 25 32 8 0.000012384930380190053 +generic_f32_4x4 5 4 5 0.000002798692518373374 +armv7neon_mmm_f32_8x6_cortexa9 8 128 6 0.0000070012080134920035 +armv7neon_mmm_f32_8x6_cortexa7 17 32 6 0.000006327458516647519 +armv7neon_mmm_f32_8x4_cortexa7 24 32 7 0.0000097071140215999 +armv7neon_mmm_f32_8x6_generic 23 32 12 0.000012023256652212629 +armv7neon_mmm_f32_8x6_generic 9 32 11 0.000008252674922417165 +armv7neon_mmm_f32_8x6_generic 9 32 6 0.000004271117675286997 +armv7neon_mmm_f32_8x4_cortexa7 7 4 4 0.0000011809046933853917 +armv7neon_mmm_f32_8x6_cortexa7 15 32 19 0.00001661331304395181 +armv7neon_mmm_f32_8x4_generic 25 128 5 0.00003728262011412803 +armv7neon_mmm_f32_8x6_cortexa9 17 128 19 0.00007942199229108003 +generic_f32_4x4 11 128 9 0.000031521930244918895 +armv7neon_mmm_f32_8x6_cortexa7 25 4 6 0.000002951526384640025 +armv7neon_mmm_f32_8x6_generic 25 128 18 0.00007559930226918245 +armv7neon_mmm_f32_8x6_generic 24 32 7 0.000011956301096424158 +armv7neon_mmm_f32_8x4_cortexa7 24 4 4 0.000001942280219822285 +armv7neon_mmm_f32_8x4_cortexa7 16 4 5 0.000002773541103005484 +generic_f32_4x4 13 4 7 0.000005120137461912069 +armv7neon_mmm_f32_8x4_generic 9 4 9 0.000003880248068324046 +armv7neon_mmm_f32_8x4_cortexa9 8 4 5 0.0000016579504301247745 +armv7neon_mmm_f32_8x6_cortexa7 7 32 18 0.000006779881862785529 +armv7neon_mmm_f32_8x4_generic 24 32 9 0.000013438884904462624 +armv7neon_mmm_f32_8x4_generic 17 32 11 0.000013721051068287207 +armv7neon_mmm_f32_8x4_cortexa7 23 32 3 0.000005392987682793705 +armv7neon_mmm_f32_8x6_cortexa9 17 4 18 0.000005996181221285945 +armv7neon_mmm_f32_8x4_generic 23 128 12 0.000041706025206470595 +armv7neon_mmm_f32_8x4_generic 25 128 7 0.00003743774855697319 +generic_f32_4x4 3 4 12 0.000002379029629256848 +armv7neon_mmm_f32_8x6_cortexa9 24 128 18 0.00005914765901143539 +armv7neon_mmm_f32_8x4_cortexa7 23 32 5 0.00000981049054917298 +armv7neon_mmm_f32_8x6_cortexa7 16 4 19 0.000005412079670267658 +armv7neon_mmm_f32_8x4_cortexa7 8 32 4 0.0000019274117652778804 +armv7neon_mmm_f32_8x6_generic 17 128 6 0.000019306808921524692 +armv7neon_mmm_f32_8x6_cortexa7 7 128 12 0.000013761467144605992 +armv7neon_mmm_f32_8x6_cortexa7 16 32 7 0.000008404964853562951 +armv7neon_mmm_f32_8x6_cortexa9 9 32 7 0.000008475033766481017 +armv7neon_mmm_f32_8x6_cortexa9 15 128 5 0.000013894752583522413 +armv7neon_mmm_f32_8x6_generic 16 128 17 0.000038424998460484644 +armv7neon_mmm_f32_8x4_cortexa7 8 32 9 0.00000497069436204451 +armv7neon_mmm_f32_8x6_generic 9 32 5 0.000004444687551155496 +armv7neon_mmm_f32_8x4_cortexa7 24 32 5 0.00000958372113096483 +armv7neon_mmm_f32_8x6_cortexa9 24 4 12 0.000003962114122866607 +armv7neon_mmm_f32_8x4_generic 15 4 11 0.000004231967658632821 +armv7neon_mmm_f32_8x6_cortexa7 24 32 13 0.000017973816622821935 +armv7neon_mmm_f32_8x4_cortexa9 23 32 13 0.000019157368846217136 +armv7neon_mmm_f32_8x4_generic 16 32 9 0.00000912682021206548 +armv7neon_mmm_f32_8x6_generic 16 32 5 0.000004588157612379825 +armv7neon_mmm_f32_8x6_generic 16 4 13 0.0000041677198842138695 +armv7neon_mmm_f32_8x6_generic 15 128 17 0.00003886891043603058 +armv7neon_mmm_f32_8x6_cortexa9 7 128 11 0.000013961383587786538 +armv7neon_mmm_f32_8x4_generic 7 128 5 0.000009860600106626588 +armv7neon_mmm_f32_8x4_cortexa9 16 4 12 0.0000033867538649589902 +armv7neon_mmm_f32_8x6_cortexa9 17 32 19 0.000024087066037173788 +armv7neon_mmm_f32_8x6_generic 9 128 5 0.000013189504068813325 +armv7neon_mmm_f32_8x6_cortexa9 8 32 18 0.000006222642744442069 +armv7neon_mmm_f32_8x6_cortexa7 24 32 12 0.000011846425063097506 +generic_f32_4x4 5 4 13 0.000004993325421206121 +armv7neon_mmm_f32_8x6_generic 8 128 11 0.000013157428320463883 +generic_f32_4x4 7 32 4 0.0000029484328830194284 +armv7neon_mmm_f32_8x4_cortexa9 7 32 9 0.000005475212517453128 +armv7neon_mmm_f32_8x4_cortexa9 9 128 13 0.00003949164898061412 +armv7neon_mmm_f32_8x4_cortexa9 23 4 11 0.000005947124934955378 +armv7neon_mmm_f32_8x6_generic 23 32 11 0.00001239533186039336 +armv7neon_mmm_f32_8x6_generic 7 32 17 0.000006661670995420206 +armv7neon_mmm_f32_8x4_cortexa7 7 4 8 0.0000018776318580783529 +armv7neon_mmm_f32_8x6_generic 8 32 5 0.0000025552685069830137 +armv7neon_mmm_f32_8x4_cortexa9 9 32 9 0.000009774693013502346 +generic_f32_4x4 13 128 5 0.000027995490696525238 +armv7neon_mmm_f32_8x4_cortexa9 8 4 11 0.0000021722237770380927 +armv7neon_mmm_f32_8x4_generic 23 128 9 0.0000419391925884448 +armv7neon_mmm_f32_8x4_cortexa9 25 32 7 0.000013031732285626503 +armv7neon_mmm_f32_8x4_cortexa7 25 32 12 0.000017966835523846677 +armv7neon_mmm_f32_8x4_cortexa7 16 32 11 0.000009494335431932482 +armv7neon_mmm_f32_8x6_cortexa7 17 128 19 0.0000783675036460944 +armv7neon_mmm_f32_8x6_cortexa7 17 4 7 0.0000044328101817797105 +generic_f32_4x4 3 32 8 0.000003088179719444515 +generic_f32_4x4 12 4 11 0.000005408469300584859 +armv7neon_mmm_f32_8x4_cortexa7 23 32 13 0.000018862031131309997 +armv7neon_mmm_f32_8x4_generic 23 32 11 0.000014024445889402156 +generic_f32_4x4 4 128 11 0.00001075990813733738 +armv7neon_mmm_f32_8x6_generic 24 4 7 0.000004301779173383875 +armv7neon_mmm_f32_8x6_cortexa9 25 128 12 0.000052850505948183045 +armv7neon_mmm_f32_8x4_generic 15 32 4 0.0000034946744383909553 +armv7neon_mmm_f32_8x4_generic 17 4 3 0.0000024363476636608272 +armv7neon_mmm_f32_8x4_cortexa7 16 128 4 0.000009891301569266948 +armv7neon_mmm_f32_8x6_cortexa9 25 4 13 0.000008105640345047328 +armv7neon_mmm_f32_8x4_cortexa9 16 4 4 0.000001487225488304516 +generic_f32_4x4 5 32 9 0.000007777979342543209 +armv7neon_mmm_f32_8x6_cortexa7 8 4 19 0.0000029885727357209406 +generic_f32_4x4 8 32 9 0.000007585061806416855 +armv7neon_mmm_f32_8x4_cortexa7 17 128 13 0.000057643034032365925 +armv7neon_mmm_f32_8x4_generic 7 128 4 0.000005216038661083481 +armv7neon_mmm_f32_8x4_cortexa7 16 32 12 0.000009049743188132303 +armv7neon_mmm_f32_8x4_cortexa9 17 4 8 0.000003682177568678712 +armv7neon_mmm_f32_8x4_cortexa9 17 32 11 0.000014400917572892256 +armv7neon_mmm_f32_8x4_cortexa9 15 4 12 0.0000040529055101759995 +armv7neon_mmm_f32_8x4_cortexa7 15 4 11 0.000004271052070252677 +armv7neon_mmm_f32_8x6_cortexa7 16 128 5 0.000013791269931666714 +armv7neon_mmm_f32_8x6_generic 8 32 7 0.000004325189484240455 +generic_f32_4x4 3 128 7 0.000007522472627920054 +armv7neon_mmm_f32_8x6_cortexa9 9 32 6 0.000004463650285453612 +generic_f32_4x4 11 32 7 0.000007948963253145964 +armv7neon_mmm_f32_8x4_cortexa9 24 128 4 0.000014890215129529216 +armv7neon_mmm_f32_8x6_cortexa9 16 4 19 0.000005414025318591343 +armv7neon_mmm_f32_8x6_cortexa7 17 4 6 0.00000238128254631235 +armv7neon_mmm_f32_8x4_cortexa9 24 4 5 0.000003916194410369112 +armv7neon_mmm_f32_8x4_cortexa9 8 128 9 0.000015067666888020707 +armv7neon_mmm_f32_8x6_cortexa7 23 32 11 0.000012775953874196187 +generic_f32_4x4 8 32 3 0.0000030910360857362123 +armv7neon_mmm_f32_8x4_cortexa7 7 32 3 0.0000021042104056045423 +armv7neon_mmm_f32_8x6_cortexa9 25 32 5 0.000008813546121201035 +armv7neon_mmm_f32_8x6_cortexa7 25 4 17 0.000008369215065652083 +generic_f32_4x4 3 32 9 0.000004340286298208673 +armv7neon_mmm_f32_8x4_generic 17 4 5 0.000003911088956924729 +generic_f32_4x4 9 4 13 0.000007067880206932553 +armv7neon_mmm_f32_8x4_cortexa7 8 128 13 0.00001944164572907436 +armv7neon_mmm_f32_8x4_cortexa7 25 4 8 0.000004598061889383109 +armv7neon_mmm_f32_8x4_generic 15 32 5 0.000006570904917754489 +armv7neon_mmm_f32_8x4_cortexa9 7 4 4 0.000001199774667010064 +armv7neon_mmm_f32_8x4_cortexa9 16 32 9 0.000009580069903775671 +armv7neon_mmm_f32_8x6_generic 17 4 19 0.000007879961865942272 +armv7neon_mmm_f32_8x4_cortexa7 23 128 13 0.00005805408197282095 +armv7neon_mmm_f32_8x6_generic 25 32 13 0.000023213828889712974 +armv7neon_mmm_f32_8x6_cortexa9 23 4 17 0.000006797095061554012 +armv7neon_mmm_f32_8x6_cortexa9 24 32 6 0.000006232031189380453 +armv7neon_mmm_f32_8x4_cortexa7 15 128 11 0.00002955574134898906 +armv7neon_mmm_f32_8x6_cortexa9 24 4 11 0.000004626561901203258 +armv7neon_mmm_f32_8x6_cortexa9 8 4 7 0.0000018344586562526734 +armv7neon_mmm_f32_8x6_generic 15 4 18 0.000004518763751153566 +armv7neon_mmm_f32_8x6_cortexa9 16 128 11 0.000027030003785723685 +armv7neon_mmm_f32_8x4_cortexa7 9 4 4 0.0000015888326511808313 +armv7neon_mmm_f32_8x4_cortexa7 7 4 12 0.0000025479152659232096 +armv7neon_mmm_f32_8x4_cortexa7 23 128 7 0.000029563915293732792 +armv7neon_mmm_f32_8x4_cortexa9 25 4 13 0.000009216194666264275 +generic_f32_4x4 4 4 7 0.0000016464048874437295 +armv7neon_mmm_f32_8x4_cortexa7 7 128 11 0.00001525304959159917 +armv7neon_mmm_f32_8x4_cortexa9 9 4 9 0.000003956900236836743 +generic_f32_4x4 4 128 8 0.000007233357504684728 +armv7neon_mmm_f32_8x4_cortexa9 16 128 9 0.000029612198853914824 +armv7neon_mmm_f32_8x4_generic 15 128 12 0.000028154522908512787 +armv7neon_mmm_f32_8x6_cortexa9 8 128 5 0.000007227187608771384 +armv7neon_mmm_f32_8x4_cortexa9 25 128 7 0.000039766026339765396 +generic_f32_4x4 9 128 5 0.00002114949804637606 +armv7neon_mmm_f32_8x4_generic 17 32 4 0.000004758199768605913 +armv7neon_mmm_f32_8x6_cortexa9 7 4 13 0.0000027386176506007003 +generic_f32_4x4 7 32 8 0.000005375358081458721 +armv7neon_mmm_f32_8x6_cortexa9 8 4 18 0.000002220900812833037 +armv7neon_mmm_f32_8x4_cortexa9 16 128 11 0.000029669482734966696 +armv7neon_mmm_f32_8x6_cortexa7 8 32 18 0.000006161276860676508 +armv7neon_mmm_f32_8x4_cortexa7 8 32 8 0.000003353396080834466 +armv7neon_mmm_f32_8x6_generic 16 128 13 0.000038174416411990654 +armv7neon_mmm_f32_8x6_generic 15 32 19 0.00001612698422472605 +armv7neon_mmm_f32_8x4_cortexa9 9 128 3 0.000010402587362113396 +armv7neon_mmm_f32_8x6_cortexa9 9 4 11 0.0000032196424297717835 +armv7neon_mmm_f32_8x4_cortexa9 8 128 11 0.000015086238127499093 +armv7neon_mmm_f32_8x6_generic 16 128 19 0.00005061640609152132 +armv7neon_mmm_f32_8x4_generic 16 128 9 0.00002782092751958622 +generic_f32_4x4 4 32 4 0.000001643238458184986 +armv7neon_mmm_f32_8x6_cortexa7 17 4 11 0.0000046002558984855774 +generic_f32_4x4 5 4 9 0.0000039003390490700075 +generic_f32_4x4 9 128 7 0.000021241983402647673 +generic_f32_4x4 7 4 13 0.000005155057897108874 +armv7neon_mmm_f32_8x6_generic 17 4 17 0.000006303527455540143 +armv7neon_mmm_f32_8x4_generic 16 4 13 0.000004620655887935485 +armv7neon_mmm_f32_8x4_cortexa7 8 128 5 0.000010060854723538398 +armv7neon_mmm_f32_8x4_cortexa9 16 32 11 0.000009653502461801779 +armv7neon_mmm_f32_8x6_generic 23 128 5 0.00001973212006224306 +armv7neon_mmm_f32_8x4_generic 8 4 7 0.000001657225281211141 +armv7neon_mmm_f32_8x6_generic 16 4 12 0.0000027740993216529004 +armv7neon_mmm_f32_8x6_cortexa9 15 128 18 0.00004029185998372536 +armv7neon_mmm_f32_8x4_cortexa9 25 32 4 0.000006456592320044242 +armv7neon_mmm_f32_8x6_cortexa7 25 128 12 0.000052118173447989496 +armv7neon_mmm_f32_8x4_cortexa7 25 32 9 0.000018537854346104505 +armv7neon_mmm_f32_8x6_cortexa7 16 4 7 0.000003147284440858119 +armv7neon_mmm_f32_8x4_generic 17 128 5 0.000028048493049013062 +armv7neon_mmm_f32_8x6_cortexa7 23 4 18 0.000006335447404352516 +armv7neon_mmm_f32_8x6_cortexa9 24 4 19 0.000007813810057423332 +armv7neon_mmm_f32_8x4_cortexa7 17 128 8 0.000028915520714406696 +armv7neon_mmm_f32_8x4_cortexa7 17 128 7 0.000029363768223094683 +generic_f32_4x4 3 32 11 0.0000043748460947431475 +armv7neon_mmm_f32_8x4_cortexa9 9 128 5 0.0000200735894165016 +armv7neon_mmm_f32_8x4_cortexa7 17 4 3 0.000002458111098809617 +generic_f32_4x4 5 128 11 0.0000211987993849004 +generic_f32_4x4 12 32 7 0.00000782487986470221 +armv7neon_mmm_f32_8x4_generic 7 128 11 0.000014659497491826627 +armv7neon_mmm_f32_8x6_cortexa7 17 128 11 0.00003969667780371537 +armv7neon_mmm_f32_8x6_generic 7 4 6 0.0000012612776856940052 +armv7neon_mmm_f32_8x6_cortexa9 7 4 6 0.0000012711169589040185 +armv7neon_mmm_f32_8x6_cortexa7 8 4 13 0.0000024259808837385263 +armv7neon_mmm_f32_8x4_cortexa7 24 128 9 0.000043285670969312205 +armv7neon_mmm_f32_8x6_generic 15 128 19 0.00005126058311511034 +generic_f32_4x4 13 128 11 0.00004170340313011054 +armv7neon_mmm_f32_8x6_cortexa9 8 128 12 0.000013510136481590433 +armv7neon_mmm_f32_8x6_generic 23 32 17 0.00001815155326925384 +armv7neon_mmm_f32_8x4_generic 24 32 8 0.000008758373656325976 +armv7neon_mmm_f32_8x4_generic 15 32 13 0.000012566353080909321 +armv7neon_mmm_f32_8x4_generic 15 4 9 0.000004138286673960469 +armv7neon_mmm_f32_8x4_cortexa7 16 32 4 0.000003365053985432853 +armv7neon_mmm_f32_8x4_cortexa7 9 128 3 0.0000101978289798725 +generic_f32_4x4 11 32 8 0.000007681371193157391 +generic_f32_4x4 7 128 9 0.00002127278439974851 +armv7neon_mmm_f32_8x4_cortexa7 24 4 13 0.000006759425188306811 +armv7neon_mmm_f32_8x4_cortexa7 24 32 9 0.000013896132257150766 +armv7neon_mmm_f32_8x4_cortexa9 25 32 9 0.000018829874971593756 +armv7neon_mmm_f32_8x4_cortexa7 9 32 4 0.000003474275688786252 +armv7neon_mmm_f32_8x6_cortexa7 8 32 7 0.0000044833206937891445 +generic_f32_4x4 3 32 7 0.0000030714723992174605 +armv7neon_mmm_f32_8x6_cortexa9 17 4 11 0.000004585922035285582 +armv7neon_mmm_f32_8x4_cortexa9 24 128 5 0.000029781338716311416 +armv7neon_mmm_f32_8x6_generic 15 32 18 0.000012214298327316169 +armv7neon_mmm_f32_8x6_generic 25 32 6 0.000007957229153899586 +armv7neon_mmm_f32_8x6_cortexa9 9 32 18 0.000012307833924250695 +armv7neon_mmm_f32_8x6_generic 8 32 13 0.000006159219915111089 +armv7neon_mmm_f32_8x4_cortexa7 8 128 3 0.000005401956819775254 +armv7neon_mmm_f32_8x4_generic 15 32 9 0.000009577293347035024 +armv7neon_mmm_f32_8x4_cortexa9 25 128 13 0.00007830393224178603 +generic_f32_4x4 4 4 13 0.0000026249296920626683 +armv7neon_mmm_f32_8x6_cortexa9 8 32 6 0.000002404882860716393 +armv7neon_mmm_f32_8x6_cortexa9 9 32 5 0.0000045921033389599396 +armv7neon_mmm_f32_8x6_generic 17 128 11 0.000038492804993535863 +armv7neon_mmm_f32_8x4_generic 17 32 9 0.000013624172849176604 +armv7neon_mmm_f32_8x4_cortexa9 24 4 9 0.000005393334776799241 +armv7neon_mmm_f32_8x4_cortexa9 23 32 8 0.000009723809855547939 +armv7neon_mmm_f32_8x4_cortexa9 25 4 4 0.0000025900920077984026 +armv7neon_mmm_f32_8x6_generic 23 4 5 0.0000027608446978317408 +armv7neon_mmm_f32_8x4_generic 8 4 12 0.0000019060496583230645 +armv7neon_mmm_f32_8x4_cortexa7 15 32 8 0.000006678781571694655 +generic_f32_4x4 4 4 9 0.0000021081574199754717 +armv7neon_mmm_f32_8x4_cortexa7 17 128 9 0.00004346458158147509 +armv7neon_mmm_f32_8x4_generic 25 4 8 0.000004544403472760322 +armv7neon_mmm_f32_8x4_generic 24 128 4 0.000013981279265161887 +armv7neon_mmm_f32_8x6_cortexa7 25 4 12 0.000005353769509302435 +armv7neon_mmm_f32_8x4_cortexa9 15 4 8 0.000002871100263594495 +armv7neon_mmm_f32_8x6_cortexa7 9 4 12 0.0000030500030063725167 +armv7neon_mmm_f32_8x4_cortexa9 15 128 7 0.000020348668200781455 +generic_f32_4x4 4 4 3 0.0000011380263710773134 +armv7neon_mmm_f32_8x4_generic 17 32 7 0.000009461004385359903 +armv7neon_mmm_f32_8x6_cortexa7 16 4 11 0.000003292952568894936 +armv7neon_mmm_f32_8x4_generic 9 128 7 0.0000189264627670604 +armv7neon_mmm_f32_8x4_cortexa7 9 32 12 0.000009404582356065284 +armv7neon_mmm_f32_8x6_cortexa7 24 4 11 0.000004645745440552646 +armv7neon_mmm_f32_8x4_generic 7 4 8 0.0000018628381186813514 +generic_f32_4x4 13 32 12 0.000014451033360231452 +armv7neon_mmm_f32_8x6_generic 7 128 11 0.00001335338354211472 +armv7neon_mmm_f32_8x4_cortexa9 24 32 5 0.000009736881086291142 +armv7neon_mmm_f32_8x4_generic 9 32 8 0.000006244209351860461 +armv7neon_mmm_f32_8x4_cortexa7 23 32 7 0.000009963814618687657 +armv7neon_mmm_f32_8x6_cortexa7 24 128 7 0.000039514548121680776 +armv7neon_mmm_f32_8x4_cortexa7 17 32 5 0.000009649055437118146 +armv7neon_mmm_f32_8x6_generic 7 128 7 0.000013256390141337729 +armv7neon_mmm_f32_8x6_cortexa7 9 128 17 0.00003956707654525446 +armv7neon_mmm_f32_8x4_generic 9 128 8 0.000018708339822835962 +generic_f32_4x4 9 4 7 0.00000399084452417743 +armv7neon_mmm_f32_8x4_generic 15 128 8 0.000018936209183566976 +armv7neon_mmm_f32_8x6_cortexa7 9 32 19 0.00001616084997042782 +armv7neon_mmm_f32_8x6_generic 16 128 5 0.00001333369244237612 +armv7neon_mmm_f32_8x4_cortexa7 9 32 3 0.0000036715654135840626 +armv7neon_mmm_f32_8x4_cortexa9 23 32 5 0.000009965038150728467 +armv7neon_mmm_f32_8x6_cortexa7 9 128 7 0.000026494406670274787 +armv7neon_mmm_f32_8x4_cortexa9 24 128 13 0.00005851957886711075 +armv7neon_mmm_f32_8x6_cortexa7 9 4 18 0.000004273474311217914 +generic_f32_4x4 5 32 12 0.000007635355402928665 +armv7neon_mmm_f32_8x4_cortexa9 7 128 7 0.000010528249080204253 +armv7neon_mmm_f32_8x6_cortexa9 24 32 19 0.00002386884326098149 +armv7neon_mmm_f32_8x6_generic 8 4 7 0.0000017826372979170567 +armv7neon_mmm_f32_8x4_generic 7 4 5 0.000001794625708774814 +armv7neon_mmm_f32_8x6_cortexa7 9 4 6 0.000001801617764943881 +armv7neon_mmm_f32_8x4_generic 25 32 13 0.00002352372957802219 +armv7neon_mmm_f32_8x4_generic 24 128 13 0.00005496909042425587 +armv7neon_mmm_f32_8x6_cortexa9 23 32 17 0.000018875847839557392 +generic_f32_4x4 12 32 4 0.000003926824640983302 +armv7neon_mmm_f32_8x6_cortexa9 7 128 18 0.000020631769324589798 +armv7neon_mmm_f32_8x6_cortexa9 8 128 19 0.000026726033464302463 +armv7neon_mmm_f32_8x6_cortexa7 7 4 5 0.0000012346138819052426 +armv7neon_mmm_f32_8x4_generic 17 128 7 0.00002816390176070489 +armv7neon_mmm_f32_8x6_generic 9 32 19 0.000015684300494904117 +armv7neon_mmm_f32_8x4_cortexa7 9 128 4 0.000010001875449763685 +armv7neon_mmm_f32_8x4_generic 23 32 13 0.000018251850356188044 +generic_f32_4x4 13 4 3 0.000003021647059953821 +armv7neon_mmm_f32_8x6_cortexa9 25 128 7 0.000053220906812986594 +armv7neon_mmm_f32_8x4_cortexa9 16 128 8 0.00001967608213206112 +armv7neon_mmm_f32_8x6_generic 24 32 5 0.000006620165371496042 +armv7neon_mmm_f32_8x4_cortexa7 9 4 7 0.0000028674611086712135 +armv7neon_mmm_f32_8x4_cortexa9 17 4 11 0.000005653151614630126 +generic_f32_4x4 13 4 4 0.000002594385628155042 +armv7neon_mmm_f32_8x6_generic 25 128 5 0.000026094659734105824 +armv7neon_mmm_f32_8x4_generic 17 32 3 0.000005161144602046728 +armv7neon_mmm_f32_8x4_cortexa9 24 32 11 0.000014220571969428708 +generic_f32_4x4 4 128 3 0.000004015357652724343 +armv7neon_mmm_f32_8x4_cortexa9 8 128 5 0.00001027176965651363 +armv7neon_mmm_f32_8x6_cortexa7 15 4 17 0.000004874430261781061 +armv7neon_mmm_f32_8x4_cortexa7 15 4 12 0.0000040169005856724945 +armv7neon_mmm_f32_8x4_cortexa7 8 32 12 0.00000477050855780904 +armv7neon_mmm_f32_8x6_cortexa7 9 4 7 0.0000031516329281183407 +armv7neon_mmm_f32_8x4_generic 7 32 11 0.000005308351521432454 +armv7neon_mmm_f32_8x4_cortexa7 25 32 5 0.000012670365676368953 +generic_f32_4x4 5 128 5 0.000014294465011666321 +armv7neon_mmm_f32_8x6_cortexa7 7 32 17 0.000006818696484359277 +armv7neon_mmm_f32_8x4_cortexa9 24 32 7 0.00000986925847989351 +generic_f32_4x4 4 128 13 0.000014106152963483687 +armv7neon_mmm_f32_8x4_cortexa9 16 128 3 0.000010512285712675924 +generic_f32_4x4 8 128 4 0.000007240602279573043 +armv7neon_mmm_f32_8x4_cortexa9 7 128 9 0.00001548686209510202 +armv7neon_mmm_f32_8x4_cortexa9 7 32 3 0.000002138015694680355 +armv7neon_mmm_f32_8x4_generic 24 32 13 0.000017536918553280824 +armv7neon_mmm_f32_8x6_cortexa9 23 4 12 0.0000044455569065126315 +armv7neon_mmm_f32_8x4_cortexa7 8 4 13 0.0000026000896469710663 +armv7neon_mmm_f32_8x4_cortexa7 25 32 8 0.000012179917551949525 +generic_f32_4x4 5 32 5 0.000005390504735926875 +generic_f32_4x4 11 128 13 0.00004175553018581371 +armv7neon_mmm_f32_8x4_cortexa7 7 32 4 0.0000021521595449855865 +armv7neon_mmm_f32_8x4_cortexa9 8 4 9 0.000002148291671877107 +armv7neon_mmm_f32_8x6_cortexa7 24 4 18 0.000005654885858199984 +armv7neon_mmm_f32_8x4_generic 25 128 9 0.00005533756635099798 +armv7neon_mmm_f32_8x4_cortexa9 9 128 9 0.00002978120797293901 +armv7neon_mmm_f32_8x6_cortexa7 8 32 12 0.000004281164608278993 +armv7neon_mmm_f32_8x4_generic 9 4 13 0.000004935869563311462 +armv7neon_mmm_f32_8x4_cortexa7 23 128 3 0.000015184873420309865 +generic_f32_4x4 12 4 12 0.000004912969841843077 +armv7neon_mmm_f32_8x6_generic 16 4 7 0.0000030460066806302122 +armv7neon_mmm_f32_8x4_cortexa7 9 4 13 0.000004992355989646887 +armv7neon_mmm_f32_8x6_cortexa9 15 4 12 0.0000032832021366886516 +armv7neon_mmm_f32_8x6_cortexa9 17 4 7 0.000004416231510276644 +armv7neon_mmm_f32_8x4_generic 9 128 5 0.000018874961280340985 +armv7neon_mmm_f32_8x4_cortexa7 25 4 9 0.000007146809416343016 +armv7neon_mmm_f32_8x6_cortexa9 23 4 5 0.000002791403862004092 +generic_f32_4x4 3 128 11 0.000011052312539972672 +armv7neon_mmm_f32_8x4_cortexa7 16 32 13 0.000012274313788252517 +generic_f32_4x4 5 4 8 0.000002691374011509211 +armv7neon_mmm_f32_8x6_generic 8 4 12 0.0000016391318699699268 +generic_f32_4x4 8 4 12 0.0000034590196765442797 +armv7neon_mmm_f32_8x6_generic 17 128 18 0.00005685255235859907 +armv7neon_mmm_f32_8x6_cortexa9 17 128 6 0.00002017521356859541 +armv7neon_mmm_f32_8x6_generic 25 128 6 0.00002553797390337986 +armv7neon_mmm_f32_8x6_cortexa9 25 128 5 0.000027210481604244293 +armv7neon_mmm_f32_8x4_cortexa7 15 128 7 0.000019945561157727263 +armv7neon_mmm_f32_8x4_generic 7 4 11 0.000002550958711536068 +generic_f32_4x4 13 4 8 0.000004699429428545618 +armv7neon_mmm_f32_8x4_cortexa9 9 4 5 0.0000028517696924614755 +armv7neon_mmm_f32_8x4_cortexa9 8 4 3 0.0000012050980961045702 +armv7neon_mmm_f32_8x6_cortexa9 24 128 17 0.000059842842681725297 +armv7neon_mmm_f32_8x6_cortexa9 25 128 6 0.000026691322740044253 +generic_f32_4x4 9 32 3 0.000004333031052520466 +armv7neon_mmm_f32_8x6_cortexa7 9 32 11 0.000008502744568312225 +armv7neon_mmm_f32_8x6_cortexa9 16 32 11 0.000008643328393303817 +armv7neon_mmm_f32_8x6_cortexa9 16 4 11 0.00000327884229270897 +armv7neon_mmm_f32_8x4_generic 8 128 5 0.000009661930895932827 +armv7neon_mmm_f32_8x4_cortexa9 15 4 5 0.0000029989007242676373 +armv7neon_mmm_f32_8x4_cortexa7 23 32 11 0.000014477721812785248 +armv7neon_mmm_f32_8x6_cortexa7 9 4 13 0.000004411946245984795 +armv7neon_mmm_f32_8x6_generic 7 128 12 0.000013345094425498999 +armv7neon_mmm_f32_8x4_cortexa9 24 128 11 0.00004426398857587651 +armv7neon_mmm_f32_8x6_generic 25 128 7 0.00005086677641643256 +armv7neon_mmm_f32_8x6_generic 16 32 17 0.00001197546406643872 +generic_f32_4x4 4 32 13 0.000005196854104063007 +armv7neon_mmm_f32_8x4_cortexa7 16 4 7 0.000002852179426317928 +armv7neon_mmm_f32_8x6_cortexa7 23 4 7 0.000004598943528457281 +armv7neon_mmm_f32_8x6_generic 17 32 6 0.000006120980761955415 +armv7neon_mmm_f32_8x6_cortexa7 17 32 17 0.00001829474178461942 +generic_f32_4x4 7 4 12 0.000003918843035175344 +armv7neon_mmm_f32_8x6_generic 9 4 7 0.0000030530125579888694 +armv7neon_mmm_f32_8x6_cortexa9 23 4 13 0.000006537462226861253 +generic_f32_4x4 12 32 11 0.000011229611140019307 +armv7neon_mmm_f32_8x4_cortexa7 7 32 9 0.000005392896316953727 +armv7neon_mmm_f32_8x4_cortexa9 15 4 7 0.00000310030600994376 +armv7neon_mmm_f32_8x4_cortexa9 23 32 3 0.000005486033134258231 +armv7neon_mmm_f32_8x6_cortexa7 25 4 11 0.000005954018792233075 +armv7neon_mmm_f32_8x4_generic 23 128 8 0.000027951206051686114 +armv7neon_mmm_f32_8x6_generic 23 4 12 0.000004342351795004267 +armv7neon_mmm_f32_8x4_cortexa9 8 32 5 0.0000035943022440195 +armv7neon_mmm_f32_8x6_cortexa9 7 4 18 0.0000028075381670446466 +armv7neon_mmm_f32_8x4_generic 8 32 13 0.0000061830339053399435 +armv7neon_mmm_f32_8x6_generic 15 32 7 0.000008321528944284549 +armv7neon_mmm_f32_8x6_cortexa7 16 128 6 0.00001332317233161335 +armv7neon_mmm_f32_8x4_generic 9 4 12 0.000003689645307663438 +armv7neon_mmm_f32_8x4_cortexa9 16 32 7 0.000006751619359417928 +generic_f32_4x4 7 4 4 0.0000016418721244643219 +armv7neon_mmm_f32_8x4_generic 7 128 13 0.000019287387814897854 +armv7neon_mmm_f32_8x6_cortexa9 23 4 11 0.000004831555332090134 +armv7neon_mmm_f32_8x4_cortexa9 8 128 8 0.00001009639921913338 +armv7neon_mmm_f32_8x6_generic 23 32 5 0.000006582337877689147 +armv7neon_mmm_f32_8x4_generic 25 128 3 0.00001920626106575073 +armv7neon_mmm_f32_8x4_generic 7 32 8 0.000003711210711678028 +armv7neon_mmm_f32_8x6_cortexa7 16 128 19 0.00005218314273770285 +armv7neon_mmm_f32_8x6_cortexa9 7 128 12 0.000013936034942170719 +armv7neon_mmm_f32_8x6_generic 17 32 5 0.000006487710283772798 +armv7neon_mmm_f32_8x4_generic 8 128 7 0.000009698770362126403 +armv7neon_mmm_f32_8x4_generic 15 4 7 0.0000030411777158900867 +armv7neon_mmm_f32_8x6_generic 25 128 19 0.00010155326542007438 +armv7neon_mmm_f32_8x6_cortexa7 9 128 5 0.000013650595324888383 +armv7neon_mmm_f32_8x4_cortexa9 9 32 11 0.00000982080901853953 +generic_f32_4x4 9 32 11 0.00001141647780993558 +generic_f32_4x4 8 32 11 0.000007662846153151327 +armv7neon_mmm_f32_8x6_generic 8 128 17 0.000019396378758926843 +armv7neon_mmm_f32_8x6_cortexa9 8 4 17 0.00000248907607573393 +generic_f32_4x4 13 128 12 0.000041173556961397094 +armv7neon_mmm_f32_8x4_generic 25 32 3 0.0000067405543706379265 +armv7neon_mmm_f32_8x6_cortexa9 16 4 13 0.000004284450398155638 +armv7neon_mmm_f32_8x6_generic 7 32 13 0.000006564286527582003 +generic_f32_4x4 9 4 12 0.000005239644799087451 +generic_f32_4x4 3 128 12 0.00001105053431182824 +armv7neon_mmm_f32_8x4_cortexa9 16 32 12 0.000009204841041049037 +armv7neon_mmm_f32_8x4_cortexa9 8 4 7 0.0000016950881959591487 +armv7neon_mmm_f32_8x4_cortexa9 8 4 8 0.00000148340332010083 +armv7neon_mmm_f32_8x4_cortexa9 24 32 4 0.000004874669124633379 +armv7neon_mmm_f32_8x4_cortexa9 16 128 7 0.00002009832072993214 +armv7neon_mmm_f32_8x4_generic 16 4 5 0.000002739180418018979 +armv7neon_mmm_f32_8x4_cortexa9 8 4 12 0.000001953874992361998 +armv7neon_mmm_f32_8x4_generic 15 4 4 0.0000016638026379811712 +armv7neon_mmm_f32_8x4_cortexa9 25 128 8 0.000039087913140379676 +armv7neon_mmm_f32_8x6_cortexa9 17 4 12 0.000004212564422068874 +armv7neon_mmm_f32_8x6_cortexa9 8 32 5 0.00000263061064210809 +armv7neon_mmm_f32_8x4_generic 8 4 5 0.000001623245576042727 +armv7neon_mmm_f32_8x6_cortexa7 15 128 6 0.000013618693064061564 +armv7neon_mmm_f32_8x6_cortexa7 7 4 18 0.000002804843066747126 +generic_f32_4x4 3 4 11 0.0000023913102926543824 +armv7neon_mmm_f32_8x6_generic 16 4 6 0.000001638807649221633 +armv7neon_mmm_f32_8x6_generic 8 128 6 0.0000067051548997103826 +armv7neon_mmm_f32_8x6_cortexa9 16 4 6 0.0000016568160988445508 +armv7neon_mmm_f32_8x6_cortexa9 16 128 7 0.000026851544464649677 +armv7neon_mmm_f32_8x4_cortexa9 8 128 12 0.000014872272678272856 +armv7neon_mmm_f32_8x6_generic 24 128 19 0.00007580588220559739 +generic_f32_4x4 3 128 5 0.0000074971724012246544 +generic_f32_4x4 3 128 8 0.000007539035525450043 +armv7neon_mmm_f32_8x6_cortexa9 24 128 7 0.00004003350952160301 +armv7neon_mmm_f32_8x4_cortexa7 23 32 9 0.000014356067971382847 +armv7neon_mmm_f32_8x6_cortexa9 15 32 18 0.000012709917183710011 +generic_f32_4x4 9 128 3 0.000011011983185480526 +armv7neon_mmm_f32_8x4_generic 15 128 7 0.000019154850070723174 +generic_f32_4x4 7 32 12 0.000007829205768353918 +generic_f32_4x4 12 4 3 0.000002428328614613921 +armv7neon_mmm_f32_8x4_cortexa7 8 128 4 0.000005191807529366239 +armv7neon_mmm_f32_8x4_cortexa9 17 128 3 0.00001540571905602067 +armv7neon_mmm_f32_8x4_cortexa9 17 128 9 0.000044358855138628255 +armv7neon_mmm_f32_8x4_generic 8 32 4 0.0000018712794095957982 +armv7neon_mmm_f32_8x6_cortexa9 9 128 7 0.000026855539948208353 +armv7neon_mmm_f32_8x6_cortexa7 7 128 13 0.000020286298173428065 +armv7neon_mmm_f32_8x4_cortexa7 7 32 7 0.0000037916453247576294 +armv7neon_mmm_f32_8x4_cortexa7 16 128 11 0.000029071525948432538 +armv7neon_mmm_f32_8x4_generic 8 128 8 0.000009479495707619602 +armv7neon_mmm_f32_8x4_cortexa7 9 32 13 0.000012575930597830614 +armv7neon_mmm_f32_8x6_cortexa7 7 4 7 0.0000019563323222872665 +armv7neon_mmm_f32_8x4_cortexa7 15 128 3 0.000010282742767020747 +armv7neon_mmm_f32_8x4_generic 17 128 9 0.00004166868490626211 +armv7neon_mmm_f32_8x6_cortexa9 15 32 11 0.000008848994549844927 +armv7neon_mmm_f32_8x4_cortexa7 16 128 7 0.000019694781274526964 +armv7neon_mmm_f32_8x6_cortexa9 7 128 17 0.00002067047797610865 +armv7neon_mmm_f32_8x4_cortexa7 16 4 8 0.000002421339765366324 +armv7neon_mmm_f32_8x6_cortexa9 25 4 19 0.000010458910160208884 +armv7neon_mmm_f32_8x4_cortexa7 17 4 12 0.000005174031688170145 +armv7neon_mmm_f32_8x4_cortexa7 8 128 7 0.000010096523777664552 +armv7neon_mmm_f32_8x4_cortexa7 9 32 11 0.000009664173576551376 +armv7neon_mmm_f32_8x4_cortexa9 15 32 13 0.000013172771066560177 +armv7neon_mmm_f32_8x6_cortexa7 15 32 6 0.0000045760506363113526 +armv7neon_mmm_f32_8x4_generic 16 128 7 0.000018904285841254675 +armv7neon_mmm_f32_8x6_cortexa9 24 32 7 0.000012425961839460377 +armv7neon_mmm_f32_8x4_cortexa7 8 128 8 0.00000988276098193371 +armv7neon_mmm_f32_8x4_cortexa7 25 32 13 0.000024332756642022888 +armv7neon_mmm_f32_8x6_cortexa9 8 4 12 0.000001657247635058824 +armv7neon_mmm_f32_8x4_cortexa7 8 4 9 0.000002131892294337394 +generic_f32_4x4 8 4 8 0.000002499295427359815 +armv7neon_mmm_f32_8x6_cortexa9 16 128 5 0.000013927438070206447 +generic_f32_4x4 3 4 7 0.0000017546173861422715 +armv7neon_mmm_f32_8x4_cortexa7 23 128 12 0.000043445426131070976 +armv7neon_mmm_f32_8x4_generic 8 128 12 0.00001395887153959496 +armv7neon_mmm_f32_8x4_cortexa9 7 4 11 0.0000025905119707065483 +generic_f32_4x4 12 128 4 0.00001060459652081298 +armv7neon_mmm_f32_8x6_generic 17 128 13 0.00005710733590612565 +armv7neon_mmm_f32_8x4_cortexa7 7 4 11 0.000002572252868831556 +armv7neon_mmm_f32_8x6_generic 15 128 11 0.000026019339889436572 +generic_f32_4x4 9 4 5 0.000003906378066863749 +armv7neon_mmm_f32_8x6_cortexa9 15 4 17 0.00000485956993190203 +armv7neon_mmm_f32_8x6_generic 9 32 17 0.000012038137584352969 +armv7neon_mmm_f32_8x4_cortexa9 8 32 11 0.000005081279026778461 +armv7neon_mmm_f32_8x4_cortexa7 25 128 11 0.000057856501014562726 +armv7neon_mmm_f32_8x6_cortexa9 25 32 19 0.00003186559880573022 +armv7neon_mmm_f32_8x6_cortexa9 8 32 12 0.000004319717163614961 +armv7neon_mmm_f32_8x6_cortexa9 8 32 19 0.000008329624592686573 +armv7neon_mmm_f32_8x6_cortexa9 9 32 13 0.000012409078932821099 +armv7neon_mmm_f32_8x6_cortexa9 17 128 12 0.00003982138220281054 +armv7neon_mmm_f32_8x4_cortexa9 8 32 4 0.0000019683045582115826 +armv7neon_mmm_f32_8x6_cortexa9 17 128 13 0.000059714513355137016 +armv7neon_mmm_f32_8x6_cortexa9 25 32 13 0.00002415767938328582 +armv7neon_mmm_f32_8x4_cortexa7 7 128 8 0.000010344849965132014 +armv7neon_mmm_f32_8x6_cortexa9 8 128 11 0.000013785904590501993 +armv7neon_mmm_f32_8x4_cortexa7 24 32 13 0.000018144082756384124 +armv7neon_mmm_f32_8x6_generic 15 4 13 0.000004567420998191548 +armv7neon_mmm_f32_8x4_generic 9 32 13 0.000012173282994755266 +generic_f32_4x4 4 4 4 0.0000009923563076021443 +armv7neon_mmm_f32_8x4_cortexa9 24 128 7 0.000029905631027460932 +armv7neon_mmm_f32_8x4_cortexa7 9 128 8 0.0000195010462183809 +armv7neon_mmm_f32_8x6_cortexa9 9 4 5 0.000001915573042137183 +generic_f32_4x4 7 128 12 0.000021189012747949428 +armv7neon_mmm_f32_8x4_generic 23 32 9 0.000013896926880805095 +armv7neon_mmm_f32_8x4_generic 8 128 13 0.000018647991490366943 +armv7neon_mmm_f32_8x4_cortexa7 15 32 11 0.00000998072291720506 +generic_f32_4x4 3 128 4 0.0000040235161328678346 +armv7neon_mmm_f32_8x4_generic 9 128 3 0.000009799742651068634 +armv7neon_mmm_f32_8x6_generic 25 4 11 0.000005815747914439624 +armv7neon_mmm_f32_8x4_cortexa9 9 4 12 0.0000037593832702220446 +armv7neon_mmm_f32_8x4_generic 8 32 7 0.0000034630101008340834 +armv7neon_mmm_f32_8x4_cortexa9 24 4 7 0.000004049582371373125 +armv7neon_mmm_f32_8x6_generic 9 32 12 0.000008052850558834548 +armv7neon_mmm_f32_8x4_cortexa7 25 4 3 0.000003139298464286286 +armv7neon_mmm_f32_8x4_generic 15 4 3 0.0000018347526374757481 +armv7neon_mmm_f32_8x6_cortexa9 25 4 18 0.000007705086964181997 +generic_f32_4x4 5 128 13 0.00002796513369706675 +armv7neon_mmm_f32_8x4_cortexa9 23 4 12 0.0000055210994766811515 +generic_f32_4x4 7 128 4 0.000007400928983118127 +generic_f32_4x4 12 128 8 0.000020684245069264504 +armv7neon_mmm_f32_8x4_generic 9 4 7 0.0000028402738019775784 +generic_f32_4x4 13 4 9 0.000007094831203377165 +armv7neon_mmm_f32_8x4_cortexa9 17 4 4 0.000002109614550782161 +generic_f32_4x4 9 32 7 0.00000788121798038347 +armv7neon_mmm_f32_8x4_cortexa9 17 32 9 0.000014308883462633102 +armv7neon_mmm_f32_8x4_generic 16 128 12 0.00002742841574783841 +armv7neon_mmm_f32_8x6_generic 9 32 13 0.000011930679710766873 +armv7neon_mmm_f32_8x4_cortexa7 24 128 7 0.000029306857678894273 +armv7neon_mmm_f32_8x4_cortexa9 15 4 11 0.0000043086270265983256 +generic_f32_4x4 11 4 11 0.000005690020384668188 +generic_f32_4x4 11 4 5 0.000003971103754939711 +armv7neon_mmm_f32_8x4_generic 23 128 7 0.000028363425957340315 +armv7neon_mmm_f32_8x4_cortexa7 25 32 7 0.000012820527346969179 +armv7neon_mmm_f32_8x6_generic 17 4 13 0.0000061204912028192775 +armv7neon_mmm_f32_8x4_cortexa7 23 4 13 0.000007468189054284558 +generic_f32_4x4 4 32 7 0.0000029448449923973884 +armv7neon_mmm_f32_8x4_cortexa9 25 32 12 0.000018265077104449187 +armv7neon_mmm_f32_8x6_cortexa9 17 32 5 0.000006709520484710961 +armv7neon_mmm_f32_8x4_cortexa9 23 128 7 0.00003016688393762355 +armv7neon_mmm_f32_8x4_cortexa9 16 128 13 0.000039189527740613905 +armv7neon_mmm_f32_8x4_cortexa7 16 128 5 0.000019619696312768993 +armv7neon_mmm_f32_8x4_generic 17 128 13 0.00005528191915660002 +armv7neon_mmm_f32_8x6_generic 17 128 19 0.00007601141239533076 +armv7neon_mmm_f32_8x4_generic 8 32 5 0.0000034293993184402013 +generic_f32_4x4 9 32 4 0.000004039130783975973 +armv7neon_mmm_f32_8x4_cortexa9 23 32 9 0.000014579239774429512 +armv7neon_mmm_f32_8x6_cortexa7 8 128 11 0.000013612858256767989 +armv7neon_mmm_f32_8x6_cortexa9 15 32 12 0.000008658441674303882 +armv7neon_mmm_f32_8x4_generic 9 128 13 0.00003711806630993475 +armv7neon_mmm_f32_8x6_generic 17 4 11 0.000004486220214719747 +armv7neon_mmm_f32_8x6_generic 24 32 17 0.00001769011948694583 +armv7neon_mmm_f32_8x6_generic 9 4 18 0.00000416889219962654 +armv7neon_mmm_f32_8x6_cortexa7 23 32 19 0.000024297125708617196 +armv7neon_mmm_f32_8x6_cortexa9 9 32 19 0.000016308328501596863 +generic_f32_4x4 5 32 13 0.000010150287138446791 +generic_f32_4x4 11 128 8 0.000021041592663498075 +armv7neon_mmm_f32_8x4_generic 17 32 8 0.000009030738946480375 +armv7neon_mmm_f32_8x4_generic 25 4 5 0.0000050194271140758065 +armv7neon_mmm_f32_8x4_generic 8 128 11 0.000014188042949903446 +generic_f32_4x4 13 32 11 0.000014981513303829195 +armv7neon_mmm_f32_8x4_cortexa9 8 128 4 0.000005303802744401548 +armv7neon_mmm_f32_8x6_cortexa7 17 4 5 0.0000027556390651666856 +armv7neon_mmm_f32_8x6_generic 25 128 12 0.000050521454291978235 +armv7neon_mmm_f32_8x6_cortexa9 16 4 7 0.0000031344346513242154 +armv7neon_mmm_f32_8x6_cortexa7 16 32 5 0.000004750660751815082 +armv7neon_mmm_f32_8x4_cortexa9 17 128 8 0.000029514324154126036 +armv7neon_mmm_f32_8x4_cortexa7 8 4 8 0.000001465122696586523 +armv7neon_mmm_f32_8x6_cortexa9 8 128 17 0.00002029296708644369 +armv7neon_mmm_f32_8x4_generic 25 32 12 0.00001736483445253461 +generic_f32_4x4 3 32 5 0.0000030443235090952762 +generic_f32_4x4 7 128 11 0.000021331736800406107 +armv7neon_mmm_f32_8x4_generic 24 128 3 0.000014605163771206865 +armv7neon_mmm_f32_8x4_cortexa9 15 32 8 0.000006785141126690698 +armv7neon_mmm_f32_8x6_cortexa7 23 4 11 0.000004845198031614994 +armv7neon_mmm_f32_8x6_cortexa7 8 4 18 0.0000022246057256413265 +armv7neon_mmm_f32_8x4_generic 23 32 12 0.000013609481500575639 +armv7neon_mmm_f32_8x6_generic 7 128 17 0.00001990485288299381 +armv7neon_mmm_f32_8x6_cortexa7 25 128 19 0.00010434796032564001 +armv7neon_mmm_f32_8x6_generic 15 4 7 0.0000032121848403447733 +armv7neon_mmm_f32_8x4_generic 25 32 9 0.00001793284879597201 +armv7neon_mmm_f32_8x4_cortexa7 25 4 5 0.0000050795046947410985 +armv7neon_mmm_f32_8x4_cortexa7 17 32 12 0.000013704665363244266 +generic_f32_4x4 5 32 3 0.0000030334097589492826 +armv7neon_mmm_f32_8x4_cortexa9 17 128 5 0.000030021544003020483 +armv7neon_mmm_f32_8x4_cortexa7 24 4 9 0.000005360352425382144 +armv7neon_mmm_f32_8x6_cortexa7 9 32 18 0.000012173297962511017 +armv7neon_mmm_f32_8x4_cortexa7 15 4 13 0.000005358787865343933 +armv7neon_mmm_f32_8x6_cortexa9 17 32 11 0.000012635537581820717 +armv7neon_mmm_f32_8x4_generic 9 32 12 0.000009105324476823614 +armv7neon_mmm_f32_8x4_cortexa7 17 128 11 0.00004382978043849 +armv7neon_mmm_f32_8x6_generic 17 4 6 0.0000023082534746308964 +armv7neon_mmm_f32_8x6_cortexa7 8 128 17 0.000020059146620211786 +armv7neon_mmm_f32_8x4_cortexa7 15 4 7 0.000003068689058039639 +armv7neon_mmm_f32_8x4_cortexa9 15 4 13 0.000005392948403824124 +armv7neon_mmm_f32_8x4_cortexa7 15 4 8 0.0000028443546860500008 +armv7neon_mmm_f32_8x4_cortexa9 24 4 3 0.000002583780394094933 +generic_f32_4x4 13 4 11 0.0000072216668884748755 +armv7neon_mmm_f32_8x6_cortexa7 25 4 13 0.00000811613473861232 +armv7neon_mmm_f32_8x6_generic 7 32 18 0.000006623134760255213 +armv7neon_mmm_f32_8x4_cortexa7 16 4 4 0.0000014656631347323804 +armv7neon_mmm_f32_8x6_cortexa9 24 128 12 0.00003980745750816077 +armv7neon_mmm_f32_8x6_cortexa9 17 32 6 0.00000638262644761529 +generic_f32_4x4 8 128 12 0.000020674001160178604 +armv7neon_mmm_f32_8x6_cortexa9 9 128 5 0.000013783853077105059 +armv7neon_mmm_f32_8x4_cortexa7 23 4 11 0.00000590971284549428 +armv7neon_mmm_f32_8x6_generic 17 128 7 0.00003951740163826464 +armv7neon_mmm_f32_8x6_cortexa7 23 128 12 0.00003972987011474437 +generic_f32_4x4 11 32 9 0.000011454015846057979 +armv7neon_mmm_f32_8x4_generic 7 128 8 0.000009944618306958537 +armv7neon_mmm_f32_8x4_cortexa7 9 128 12 0.00002904338895016975 +generic_f32_4x4 7 32 3 0.000003062772103932835 +armv7neon_mmm_f32_8x6_generic 7 32 19 0.000008594590275392258 +armv7neon_mmm_f32_8x4_cortexa9 24 4 4 0.0000019652194228578485 +armv7neon_mmm_f32_8x6_generic 25 32 17 0.00002354000930758944 +armv7neon_mmm_f32_8x4_cortexa7 24 4 12 0.000004776517171804484 +generic_f32_4x4 9 128 12 0.00003113614108946529 +armv7neon_mmm_f32_8x4_generic 25 4 13 0.000009058320096239628 +armv7neon_mmm_f32_8x4_cortexa9 15 32 7 0.000007013841485260632 +armv7neon_mmm_f32_8x6_cortexa7 8 32 13 0.000006373539570632082 +armv7neon_mmm_f32_8x6_cortexa9 7 128 7 0.000013849670236184067 +armv7neon_mmm_f32_8x6_cortexa7 16 4 5 0.0000021140389285040373 +armv7neon_mmm_f32_8x6_cortexa7 15 4 19 0.000006018452488388481 +armv7neon_mmm_f32_8x4_cortexa9 25 4 11 0.000007297177417716392 +armv7neon_mmm_f32_8x4_cortexa7 7 128 3 0.0000053704412970208095 +armv7neon_mmm_f32_8x6_cortexa7 25 32 6 0.000008216173586447742 +armv7neon_mmm_f32_8x6_cortexa9 23 32 6 0.000006530580171598388 +armv7neon_mmm_f32_8x6_cortexa9 8 4 11 0.0000019099336489036645 +armv7neon_mmm_f32_8x4_cortexa7 8 4 3 0.0000011915557137944658 +armv7neon_mmm_f32_8x4_cortexa9 7 32 13 0.000007135759350139172 +armv7neon_mmm_f32_8x4_cortexa9 15 128 8 0.000020152163526097688 +armv7neon_mmm_f32_8x6_generic 16 128 12 0.000025531348127989936 +armv7neon_mmm_f32_8x4_cortexa9 9 32 8 0.00000655179781932232 +armv7neon_mmm_f32_8x4_generic 25 4 7 0.000005150952172982353 +armv7neon_mmm_f32_8x6_generic 23 128 7 0.00003975509670222853 +generic_f32_4x4 7 128 7 0.000014417564018686427 +armv7neon_mmm_f32_8x6_generic 7 32 6 0.0000025435007870682203 +generic_f32_4x4 12 32 13 0.000014516218594466092 +armv7neon_mmm_f32_8x6_generic 24 4 13 0.000005989131584497686 +armv7neon_mmm_f32_8x6_cortexa7 24 4 13 0.000006130318576111218 +armv7neon_mmm_f32_8x4_cortexa7 24 4 8 0.0000033735678137031244 +armv7neon_mmm_f32_8x6_cortexa9 15 32 6 0.000004614375157776428 +armv7neon_mmm_f32_8x4_cortexa7 17 128 5 0.000029428735157155848 +generic_f32_4x4 11 32 3 0.000004361903012775443 +armv7neon_mmm_f32_8x6_cortexa9 24 128 11 0.00004051475238084589 +armv7neon_mmm_f32_8x6_cortexa7 8 4 11 0.0000019289152314594922 +armv7neon_mmm_f32_8x4_generic 7 32 12 0.00000529445196979877 +armv7neon_mmm_f32_8x4_cortexa9 25 4 9 0.0000071851687447521495 +armv7neon_mmm_f32_8x4_cortexa9 7 128 13 0.0000205036178463644 +armv7neon_mmm_f32_8x4_cortexa7 25 128 4 0.00001951695951825782 +generic_f32_4x4 11 32 11 0.0000115439545889861 +armv7neon_mmm_f32_8x6_cortexa7 23 32 7 0.000012514048765991971 +generic_f32_4x4 13 32 8 0.000009849544291047146 +armv7neon_mmm_f32_8x4_cortexa9 24 4 13 0.0000068036126797526975 +armv7neon_mmm_f32_8x6_cortexa9 23 128 7 0.00004044892329500272 +armv7neon_mmm_f32_8x4_cortexa9 24 32 9 0.000014128824207386303 +armv7neon_mmm_f32_8x6_cortexa9 15 4 19 0.000006004743734908054 +armv7neon_mmm_f32_8x6_generic 16 128 7 0.000025803175203093092 +armv7neon_mmm_f32_8x4_generic 24 4 9 0.000005289440520085159 +armv7neon_mmm_f32_8x4_generic 7 128 7 0.000009924053412179527 +armv7neon_mmm_f32_8x6_cortexa7 15 4 5 0.0000020760458197512403 +armv7neon_mmm_f32_8x4_cortexa9 23 32 12 0.000014302742013508701 +armv7neon_mmm_f32_8x6_cortexa9 23 128 18 0.00006018418795895432 +armv7neon_mmm_f32_8x4_cortexa7 7 128 12 0.000015239535080520215 +armv7neon_mmm_f32_8x6_generic 15 4 6 0.0000018544842241934815 +armv7neon_mmm_f32_8x6_cortexa7 25 32 18 0.000023497641993495917 +generic_f32_4x4 9 4 8 0.0000037065692747303202 +armv7neon_mmm_f32_8x6_generic 9 4 17 0.0000044060023407811705 +armv7neon_mmm_f32_8x4_generic 17 4 7 0.000004004011518049962 +armv7neon_mmm_f32_8x4_generic 16 4 9 0.0000037009767742197034 +armv7neon_mmm_f32_8x4_generic 16 4 11 0.0000037573621028760724 +armv7neon_mmm_f32_8x6_cortexa9 23 4 18 0.000006335024379033378 +generic_f32_4x4 7 32 11 0.00000797109252276388 +generic_f32_4x4 8 32 8 0.00000506136423984456 +armv7neon_mmm_f32_8x6_cortexa9 16 32 18 0.000011962958224342696 +armv7neon_mmm_f32_8x4_generic 9 32 3 0.0000035695396503977124 +armv7neon_mmm_f32_8x4_cortexa7 15 128 9 0.000029527917733416504 +armv7neon_mmm_f32_8x4_cortexa7 16 32 3 0.0000037787003081727403 +armv7neon_mmm_f32_8x4_cortexa9 17 4 5 0.000003983936158752065 +generic_f32_4x4 5 4 7 0.0000028427432000736736 +armv7neon_mmm_f32_8x6_generic 24 128 6 0.000019285772023622517 +armv7neon_mmm_f32_8x6_cortexa9 24 128 6 0.000020079389293074595 +armv7neon_mmm_f32_8x4_cortexa7 24 128 11 0.000043650740679119 +armv7neon_mmm_f32_8x6_cortexa7 15 4 6 0.0000019214162046321274 +armv7neon_mmm_f32_8x6_generic 24 128 11 0.000039928070800596723 +armv7neon_mmm_f32_8x6_cortexa7 17 128 13 0.00005915137603637775 +armv7neon_mmm_f32_8x6_cortexa7 9 128 12 0.00002647047584440748 +generic_f32_4x4 8 32 7 0.000005396919284647475 +armv7neon_mmm_f32_8x4_cortexa7 16 32 7 0.000006638663857963081 +armv7neon_mmm_f32_8x4_cortexa7 9 128 9 0.000029246794882198228 +armv7neon_mmm_f32_8x4_generic 15 32 12 0.000009467451463683956 +armv7neon_mmm_f32_8x6_generic 23 128 6 0.00001954577461053118 +armv7neon_mmm_f32_8x4_cortexa9 7 32 7 0.000003851842872992441 +armv7neon_mmm_f32_8x6_cortexa9 16 32 6 0.000004321654529645647 +generic_f32_4x4 13 32 4 0.00000517924119222049 +armv7neon_mmm_f32_8x4_generic 8 4 9 0.000002104797317503441 +armv7neon_mmm_f32_8x4_cortexa7 24 128 3 0.000015233751299574878 +armv7neon_mmm_f32_8x6_cortexa9 16 128 18 0.000039798911987149584 +armv7neon_mmm_f32_8x6_cortexa7 23 128 13 0.00005946225660703637 +generic_f32_4x4 7 32 5 0.000005462565009156774 +armv7neon_mmm_f32_8x6_generic 8 128 19 0.000025872976344928823 +generic_f32_4x4 5 4 12 0.0000037781682890084256 +armv7neon_mmm_f32_8x4_cortexa9 7 4 13 0.000003202055925058074 +armv7neon_mmm_f32_8x6_cortexa9 8 32 13 0.000006422608725593242 +armv7neon_mmm_f32_8x6_generic 9 128 7 0.000025813127546183672 +armv7neon_mmm_f32_8x4_generic 23 128 3 0.000014644120134860135 +armv7neon_mmm_f32_8x4_generic 8 4 3 0.0000011807711987459403 +armv7neon_mmm_f32_8x6_cortexa7 16 128 18 0.00003920283874886212 +armv7neon_mmm_f32_8x4_cortexa9 15 32 3 0.0000038264927174656245 +armv7neon_mmm_f32_8x6_cortexa7 25 32 12 0.000015882492018463845 +armv7neon_mmm_f32_8x6_generic 15 128 13 0.00003967007811496026 +armv7neon_mmm_f32_8x6_generic 25 4 12 0.00000523795399145237 +armv7neon_mmm_f32_8x4_cortexa9 16 4 7 0.0000028765969548151297 +armv7neon_mmm_f32_8x4_cortexa7 7 4 5 0.0000018096325702613645 +armv7neon_mmm_f32_8x6_generic 25 4 19 0.00001025642311454773 +armv7neon_mmm_f32_8x6_generic 9 128 6 0.000013045121912181557 +armv7neon_mmm_f32_8x6_cortexa7 15 32 11 0.000008770690105208661 +armv7neon_mmm_f32_8x6_generic 23 128 17 0.00005984628206960245 +armv7neon_mmm_f32_8x4_cortexa9 8 32 9 0.000005054839401749921 +generic_f32_4x4 9 32 13 0.000014812319931001716 +armv7neon_mmm_f32_8x4_cortexa9 8 32 7 0.0000036300075704923473 +generic_f32_4x4 12 128 12 0.00003080051055479753 +armv7neon_mmm_f32_8x4_generic 9 4 4 0.0000015775072922596973 +generic_f32_4x4 11 128 11 0.00003163786674887252 +generic_f32_4x4 11 128 7 0.000021315035021180117 +armv7neon_mmm_f32_8x4_generic 24 128 11 0.00004302931660486898 +armv7neon_mmm_f32_8x6_cortexa7 7 128 7 0.000013660184612749007 +armv7neon_mmm_f32_8x6_generic 15 4 12 0.0000032030364372184837 +armv7neon_mmm_f32_8x6_cortexa7 15 128 17 0.00004014988958130008 +armv7neon_mmm_f32_8x4_cortexa7 17 32 7 0.000009763008233938806 +armv7neon_mmm_f32_8x4_generic 23 4 11 0.000005850624187247901 +armv7neon_mmm_f32_8x6_cortexa9 25 4 7 0.000005691323284198507 +armv7neon_mmm_f32_8x6_generic 9 4 5 0.0000018995170310061328 +generic_f32_4x4 11 32 5 0.000007873582158405535 +armv7neon_mmm_f32_8x6_generic 17 4 7 0.0000043144070272553465 +armv7neon_mmm_f32_8x6_cortexa9 7 32 13 0.000006785328240517207 +armv7neon_mmm_f32_8x4_generic 25 128 12 0.00005645345647499795 +armv7neon_mmm_f32_8x4_generic 9 4 8 0.0000026364183937099707 +armv7neon_mmm_f32_8x4_generic 9 4 3 0.000001754783422972911 +armv7neon_mmm_f32_8x6_cortexa9 24 4 5 0.000002822627399800846 +armv7neon_mmm_f32_8x4_cortexa9 23 4 7 0.000004277460484355386 +armv7neon_mmm_f32_8x4_cortexa9 16 4 13 0.000004703052607837251 +armv7neon_mmm_f32_8x4_cortexa7 9 32 8 0.0000064472411584695615 +generic_f32_4x4 5 128 3 0.000007484402044593335 +armv7neon_mmm_f32_8x6_cortexa7 24 4 12 0.000003960884362652896 +armv7neon_mmm_f32_8x4_cortexa7 7 32 8 0.0000038146920144727004 +armv7neon_mmm_f32_8x4_cortexa9 24 4 12 0.000004810650577018378 +armv7neon_mmm_f32_8x4_cortexa9 9 4 13 0.000005031022350762627 +armv7neon_mmm_f32_8x6_cortexa9 9 128 12 0.000026841942459090627 +generic_f32_4x4 3 4 3 0.0000011084107564139803 +armv7neon_mmm_f32_8x4_generic 23 128 13 0.000057838678203389286 +armv7neon_mmm_f32_8x6_cortexa7 23 128 5 0.000020389937552256748 +armv7neon_mmm_f32_8x4_cortexa7 15 32 13 0.000012977109930123348 +armv7neon_mmm_f32_8x4_generic 16 32 11 0.00000919341672636989 +armv7neon_mmm_f32_8x6_cortexa9 17 128 17 0.000060215116898669664 +armv7neon_mmm_f32_8x4_generic 24 4 8 0.000003324206058446326 +armv7neon_mmm_f32_8x4_cortexa7 17 4 5 0.000003957357480955988 +armv7neon_mmm_f32_8x6_cortexa9 16 32 19 0.000016111976497239984 +generic_f32_4x4 13 4 12 0.000006710960240784156 +armv7neon_mmm_f32_8x6_cortexa9 23 128 5 0.000020639189093056077 +armv7neon_mmm_f32_8x6_generic 24 32 11 0.000012182073684360654 +armv7neon_mmm_f32_8x6_cortexa9 9 128 13 0.00004021781819635084 +armv7neon_mmm_f32_8x4_cortexa9 17 4 12 0.000005225946660532542 +armv7neon_mmm_f32_8x6_cortexa9 15 4 7 0.0000032840412749160575 +armv7neon_mmm_f32_8x4_cortexa9 17 128 13 0.000059220265497108164 +armv7neon_mmm_f32_8x6_cortexa9 7 4 11 0.0000020564874758047424 +generic_f32_4x4 8 128 8 0.000013969303780467269 +armv7neon_mmm_f32_8x4_cortexa7 25 4 12 0.000006595762756379242 +armv7neon_mmm_f32_8x6_generic 16 32 19 0.000015466876363304513 +armv7neon_mmm_f32_8x4_generic 24 4 11 0.0000053731939607204945 +generic_f32_4x4 8 128 13 0.00002774022280530327 +generic_f32_4x4 7 128 8 0.00001428610505820359 +armv7neon_mmm_f32_8x4_cortexa9 15 128 12 0.00002999051242337208 +armv7neon_mmm_f32_8x4_cortexa9 17 32 5 0.00000980239512919155 +armv7neon_mmm_f32_8x4_cortexa9 9 128 8 0.00001992992679699177 +armv7neon_mmm_f32_8x4_generic 25 32 5 0.00001226452231341447 +generic_f32_4x4 11 128 4 0.0000107871041093641 +generic_f32_4x4 13 32 5 0.000010185534520279402 +armv7neon_mmm_f32_8x6_cortexa7 9 32 13 0.000012306258740029597 +generic_f32_4x4 3 4 9 0.0000023591282065930476 +armv7neon_mmm_f32_8x4_cortexa7 15 128 12 0.000029408783696922103 +armv7neon_mmm_f32_8x6_cortexa9 25 4 5 0.000003460717936029046 +armv7neon_mmm_f32_8x6_cortexa9 15 32 7 0.000008659647810573247 +armv7neon_mmm_f32_8x6_generic 15 32 12 0.000008324450366911187 +armv7neon_mmm_f32_8x4_generic 7 4 4 0.0000011733671526145581 +armv7neon_mmm_f32_8x6_generic 8 32 17 0.000006245151171661497 +generic_f32_4x4 4 128 12 0.000010589994729591713 +generic_f32_4x4 13 32 3 0.000005623254183753802 +armv7neon_mmm_f32_8x4_cortexa9 17 32 8 0.000009489997139764281 +armv7neon_mmm_f32_8x6_cortexa7 15 128 12 0.0000267366663985427 +armv7neon_mmm_f32_8x6_generic 8 32 12 0.000004170620267459084 +generic_f32_4x4 12 128 13 0.00004142579938644744 +armv7neon_mmm_f32_8x6_cortexa7 7 4 17 0.0000028372537187841064 +armv7neon_mmm_f32_8x6_cortexa7 17 4 18 0.000005987713490844775 +armv7neon_mmm_f32_8x6_cortexa9 7 4 5 0.0000012358111649104604 +armv7neon_mmm_f32_8x4_generic 7 4 7 0.0000018542946008174326 +armv7neon_mmm_f32_8x4_generic 7 4 13 0.0000031498896105686864 +armv7neon_mmm_f32_8x6_cortexa9 15 4 11 0.0000034645213247919744 +armv7neon_mmm_f32_8x6_cortexa7 23 4 5 0.0000028436717084834024 +armv7neon_mmm_f32_8x4_generic 17 128 3 0.00001456481059501176 +armv7neon_mmm_f32_8x4_cortexa7 17 4 7 0.00000404574535805843 +armv7neon_mmm_f32_8x6_cortexa9 23 4 19 0.000008441880755451333 +armv7neon_mmm_f32_8x6_generic 16 4 5 0.000002037583190427157 +armv7neon_mmm_f32_8x4_cortexa7 17 4 11 0.000005618041346469936 +generic_f32_4x4 8 128 3 0.000007542706448510692 +armv7neon_mmm_f32_8x6_cortexa7 16 4 6 0.0000016599211327997808 +armv7neon_mmm_f32_8x4_generic 24 32 11 0.000013531694734026834 +armv7neon_mmm_f32_8x4_cortexa7 24 128 13 0.00005774273822898698 +generic_f32_4x4 12 32 8 0.000007328921605870887 +armv7neon_mmm_f32_8x4_generic 17 32 5 0.000009344539695515143 +armv7neon_mmm_f32_8x6_cortexa7 25 4 5 0.0000035354392629416224 +armv7neon_mmm_f32_8x4_generic 9 128 9 0.000028070243170981636 +armv7neon_mmm_f32_8x4_generic 7 32 7 0.0000036902878668462197 +generic_f32_4x4 4 32 5 0.000002913735845387932 +armv7neon_mmm_f32_8x4_cortexa7 23 128 9 0.00004401834026329482 +armv7neon_mmm_f32_8x6_cortexa7 15 32 17 0.000012793909984998035 +armv7neon_mmm_f32_8x4_cortexa7 24 32 11 0.000013985445748841752 +armv7neon_mmm_f32_8x4_generic 17 128 12 0.0000426589070381794 +armv7neon_mmm_f32_8x6_generic 9 128 19 0.000053237918402024005 +armv7neon_mmm_f32_8x6_cortexa7 17 4 17 0.000006443148021217296 +armv7neon_mmm_f32_8x4_cortexa7 8 128 12 0.000014559920878726009 +armv7neon_mmm_f32_8x6_cortexa9 8 4 19 0.000002972990505690349 +armv7neon_mmm_f32_8x6_cortexa7 25 128 5 0.000027009599886340098 +armv7neon_mmm_f32_8x6_cortexa7 7 32 5 0.0000025526809022378453 +generic_f32_4x4 13 128 13 0.00005568030788678991 +armv7neon_mmm_f32_8x4_cortexa7 15 32 12 0.000009774179275886764 +armv7neon_mmm_f32_8x4_generic 16 128 8 0.000018479196791574824 +armv7neon_mmm_f32_8x6_generic 25 128 17 0.00007786728690688477 +armv7neon_mmm_f32_8x6_generic 7 32 11 0.000004604319680609591 +armv7neon_mmm_f32_8x4_cortexa9 15 128 5 0.000020252737794258734 +armv7neon_mmm_f32_8x4_cortexa9 25 4 12 0.000006639922481547946 +armv7neon_mmm_f32_8x4_cortexa7 23 128 8 0.000029341008489406463 +armv7neon_mmm_f32_8x4_cortexa7 8 32 5 0.000003533774210652598 +generic_f32_4x4 8 128 7 0.000014303959630243704 +armv7neon_mmm_f32_8x4_cortexa9 25 32 5 0.000012867678837939383 +armv7neon_mmm_f32_8x6_generic 8 128 13 0.000019410057867203144 +armv7neon_mmm_f32_8x4_generic 15 4 5 0.0000029437961456768246 +generic_f32_4x4 12 4 9 0.000005303987106806706 +armv7neon_mmm_f32_8x6_generic 24 4 19 0.000007657205135344466 +armv7neon_mmm_f32_8x4_generic 9 4 11 0.000003929237868686891 +generic_f32_4x4 13 128 9 0.00004175491457338789 +armv7neon_mmm_f32_8x6_cortexa7 9 4 11 0.0000032396587513198363 +armv7neon_mmm_f32_8x6_cortexa7 9 32 5 0.000004609642726421324 +armv7neon_mmm_f32_8x6_cortexa7 8 128 13 0.000019980200207777093 +generic_f32_4x4 8 4 13 0.000004697441883300323 +armv7neon_mmm_f32_8x4_generic 24 128 5 0.000028404231306910303 +armv7neon_mmm_f32_8x4_cortexa7 15 128 8 0.00001974335359333113 +armv7neon_mmm_f32_8x4_generic 8 4 4 0.000000975840577396329 +armv7neon_mmm_f32_8x6_cortexa9 17 4 6 0.000002372991977511254 +armv7neon_mmm_f32_8x6_cortexa7 24 4 17 0.0000063580907262403335 +armv7neon_mmm_f32_8x6_cortexa9 24 4 17 0.00000634338841510274 +armv7neon_mmm_f32_8x6_cortexa7 8 128 19 0.000026457168307267402 +armv7neon_mmm_f32_8x4_cortexa7 15 32 4 0.0000035973058136569266 +armv7neon_mmm_f32_8x6_cortexa7 23 128 11 0.000040137380065831114 +armv7neon_mmm_f32_8x6_generic 8 32 11 0.0000044080486884835774 +armv7neon_mmm_f32_8x6_cortexa7 16 32 13 0.000012184909260827915 +armv7neon_mmm_f32_8x4_generic 24 128 12 0.00004230513222395041 +armv7neon_mmm_f32_8x6_generic 16 32 6 0.000004173665390794717 +armv7neon_mmm_f32_8x4_generic 15 128 5 0.000019049336126167508 +generic_f32_4x4 5 32 11 0.000007837409156830309 +armv7neon_mmm_f32_8x6_cortexa7 15 128 5 0.00001375002565612756 +armv7neon_mmm_f32_8x6_generic 17 128 17 0.00005946391401813929 +generic_f32_4x4 8 128 9 0.00002095153754448952 +generic_f32_4x4 12 4 7 0.000003927725122844973 +armv7neon_mmm_f32_8x6_cortexa9 25 32 6 0.000008303393078126922 +armv7neon_mmm_f32_8x4_generic 17 128 11 0.000043129532556527814 +armv7neon_mmm_f32_8x6_generic 23 128 19 0.00007849962174893098 +armv7neon_mmm_f32_8x4_cortexa9 25 32 11 0.00001896209821400977 +generic_f32_4x4 13 128 4 0.000014088650845963764 +armv7neon_mmm_f32_8x6_generic 17 32 17 0.000017768659574740396 +armv7neon_mmm_f32_8x6_generic 25 32 11 0.000016016335806526446 +generic_f32_4x4 4 128 9 0.000010729958874422145 +armv7neon_mmm_f32_8x6_generic 9 128 12 0.00002573025108782965 +armv7neon_mmm_f32_8x6_cortexa7 9 128 19 0.000052645037711891825 +armv7neon_mmm_f32_8x4_cortexa7 15 32 9 0.000009881132385003031 +armv7neon_mmm_f32_8x6_generic 15 4 11 0.0000033903277250974355 +armv7neon_mmm_f32_8x4_cortexa9 23 4 9 0.000005836030607874567 +armv7neon_mmm_f32_8x6_cortexa9 15 32 13 0.00001271636871027449 +armv7neon_mmm_f32_8x4_cortexa9 7 128 12 0.00001554481747780415 +armv7neon_mmm_f32_8x6_cortexa7 9 128 11 0.00002665664619685107 +armv7neon_mmm_f32_8x6_generic 23 4 19 0.000008276510705477435 +armv7neon_mmm_f32_8x6_cortexa7 9 128 13 0.00003963680038872068 +armv7neon_mmm_f32_8x6_generic 9 4 12 0.0000029664494321281043 +armv7neon_mmm_f32_8x4_cortexa7 9 4 8 0.0000026618049788927614 +armv7neon_mmm_f32_8x6_generic 25 4 5 0.0000034227395786859638 +armv7neon_mmm_f32_8x6_cortexa7 7 4 11 0.00000205419298901934 +armv7neon_mmm_f32_8x4_generic 15 128 4 0.000009727827829578247 +armv7neon_mmm_f32_8x4_generic 23 32 5 0.000009507106099903071 +generic_f32_4x4 13 128 3 0.000014531958964466404 +generic_f32_4x4 8 4 4 0.0000014932684997984591 +armv7neon_mmm_f32_8x6_cortexa7 9 32 12 0.0000083110843565406 +armv7neon_mmm_f32_8x4_cortexa9 9 128 4 0.000010214368441429339 +armv7neon_mmm_f32_8x6_cortexa7 24 128 11 0.000039934073240007294 +armv7neon_mmm_f32_8x4_cortexa9 25 128 9 0.0000592519545970783 +armv7neon_mmm_f32_8x4_cortexa7 17 32 13 0.000018457944384762052 +armv7neon_mmm_f32_8x6_generic 15 32 17 0.000012429374164019118 +armv7neon_mmm_f32_8x6_generic 7 4 5 0.0000012252588749082722 +generic_f32_4x4 4 4 11 0.000002142420120818127 +armv7neon_mmm_f32_8x4_cortexa7 17 32 8 0.000009331391202514006 +armv7neon_mmm_f32_8x6_cortexa9 9 32 12 0.000008401621869943723 +armv7neon_mmm_f32_8x6_cortexa7 16 32 17 0.00001235175359963665 +armv7neon_mmm_f32_8x4_cortexa9 16 4 11 0.000003828421619878369 +armv7neon_mmm_f32_8x4_cortexa9 9 4 8 0.000002691222544669364 +armv7neon_mmm_f32_8x4_cortexa9 15 32 4 0.0000036579305511926493 +armv7neon_mmm_f32_8x6_cortexa7 8 32 11 0.0000045660588709311665 +generic_f32_4x4 5 4 11 0.000003955847289217088 +generic_f32_4x4 7 128 5 0.00001437315927626327 +generic_f32_4x4 7 4 7 0.0000028946154117180987 +generic_f32_4x4 8 32 12 0.000007313658366544497 +armv7neon_mmm_f32_8x4_cortexa7 16 4 12 0.000003356919407698039 +armv7neon_mmm_f32_8x4_cortexa9 17 32 12 0.000013931859029317578 +armv7neon_mmm_f32_8x6_generic 7 128 13 0.000019807746403591995 +armv7neon_mmm_f32_8x6_generic 16 32 12 0.000007859148665693045 +armv7neon_mmm_f32_8x6_cortexa9 7 32 17 0.000006883792927542178 +armv7neon_mmm_f32_8x4_generic 24 32 4 0.000004632821669832301 +armv7neon_mmm_f32_8x6_cortexa7 15 32 18 0.000012578243798367562 +armv7neon_mmm_f32_8x6_cortexa9 17 128 7 0.000040256201289956 +armv7neon_mmm_f32_8x6_cortexa9 25 128 13 0.00007968673747995729 +armv7neon_mmm_f32_8x6_cortexa7 16 32 12 0.000008069060391993281 +armv7neon_mmm_f32_8x6_cortexa9 8 128 13 0.000020267415746870704 +armv7neon_mmm_f32_8x4_cortexa7 23 4 12 0.0000054788039005168164 +armv7neon_mmm_f32_8x4_cortexa7 7 128 9 0.000015183203798286496 +armv7neon_mmm_f32_8x4_cortexa9 25 128 11 0.00005936651957856526 +armv7neon_mmm_f32_8x6_cortexa9 7 128 13 0.00002061155210279855 +armv7neon_mmm_f32_8x6_cortexa9 16 128 12 0.000026606165960514827 +armv7neon_mmm_f32_8x4_cortexa7 24 4 7 0.000004016126966910536 +armv7neon_mmm_f32_8x4_cortexa9 16 32 4 0.000003429114676947513 +armv7neon_mmm_f32_8x4_cortexa7 9 128 7 0.000019732799833911844 +armv7neon_mmm_f32_8x6_generic 17 128 5 0.00001976574761517156 +armv7neon_mmm_f32_8x6_cortexa7 16 128 12 0.00002622611042000424 +armv7neon_mmm_f32_8x4_generic 23 128 4 0.000014300391832012492 +generic_f32_4x4 4 32 9 0.0000040473563381120025 +armv7neon_mmm_f32_8x6_cortexa9 16 4 12 0.00000281228428913529 +armv7neon_mmm_f32_8x6_generic 15 128 7 0.000025996819067612833 +armv7neon_mmm_f32_8x4_generic 25 128 13 0.00007519120583347806 +armv7neon_mmm_f32_8x6_cortexa7 25 128 18 0.00007816972627698026 +armv7neon_mmm_f32_8x6_generic 23 4 7 0.000004478475265472157 +armv7neon_mmm_f32_8x6_cortexa7 23 32 17 0.000018694963726284716 +armv7neon_mmm_f32_8x4_cortexa9 24 128 3 0.000015528002576413032 +armv7neon_mmm_f32_8x4_generic 15 4 13 0.000005299524671067579 +armv7neon_mmm_f32_8x6_cortexa9 8 32 7 0.000004505823387651013 +armv7neon_mmm_f32_8x4_cortexa9 9 4 7 0.0000028943361192655857 +armv7neon_mmm_f32_8x4_generic 9 32 4 0.0000033743539224745525 +armv7neon_mmm_f32_8x4_cortexa9 23 128 5 0.000030175843280997272 +armv7neon_mmm_f32_8x6_generic 8 4 6 0.000001069506404381519 +armv7neon_mmm_f32_8x6_cortexa9 9 32 17 0.000012530842578999487 +armv7neon_mmm_f32_8x6_generic 25 4 7 0.0000055691779651847645 +armv7neon_mmm_f32_8x6_cortexa7 24 32 18 0.000017489152288872757 +armv7neon_mmm_f32_8x4_generic 8 32 3 0.0000020846296384167996 +armv7neon_mmm_f32_8x4_generic 24 128 9 0.000042880640962818326 +armv7neon_mmm_f32_8x6_cortexa7 16 32 6 0.000004280562843189148 +generic_f32_4x4 11 4 7 0.000004035828927778453 +armv7neon_mmm_f32_8x6_cortexa7 17 128 6 0.000019945123788870955 +armv7neon_mmm_f32_8x6_cortexa7 23 4 13 0.000006544590047318178 +generic_f32_4x4 3 128 13 0.00001453817180213847 +armv7neon_mmm_f32_8x6_cortexa9 7 4 7 0.000001955919692684627 +armv7neon_mmm_f32_8x4_generic 8 4 8 0.0000014466616080596564 +armv7neon_mmm_f32_8x4_cortexa9 7 4 12 0.00000257120273391917 +armv7neon_mmm_f32_8x6_cortexa7 23 32 12 0.000012365351347550488 +armv7neon_mmm_f32_8x4_cortexa7 24 128 8 0.000028841373491782905 +generic_f32_4x4 12 4 4 0.0000019842828386500398 +armv7neon_mmm_f32_8x4_cortexa7 25 32 11 0.000018655746399310692 +armv7neon_mmm_f32_8x4_cortexa7 15 4 5 0.0000029733858994116004 +armv7neon_mmm_f32_8x4_cortexa9 8 128 3 0.000005515258544137823 +armv7neon_mmm_f32_8x6_cortexa7 24 4 7 0.000004423529851941163 +generic_f32_4x4 8 32 4 0.0000027872687254075905 +armv7neon_mmm_f32_8x6_generic 17 32 18 0.000017325844518327246 +armv7neon_mmm_f32_8x4_cortexa7 25 4 13 0.000009174920440845347 +armv7neon_mmm_f32_8x4_generic 25 128 8 0.00003833303029711307 +armv7neon_mmm_f32_8x4_generic 16 4 4 0.0000014454956826662534 +armv7neon_mmm_f32_8x4_cortexa7 17 4 4 0.0000020761952577578897 +armv7neon_mmm_f32_8x6_cortexa7 8 32 5 0.0000026618366158480003 +generic_f32_4x4 3 4 4 0.0000011319274511202646 +armv7neon_mmm_f32_8x6_cortexa7 8 32 17 0.000006455239935042666 +armv7neon_mmm_f32_8x4_cortexa7 15 4 3 0.0000018508727527174426 +armv7neon_mmm_f32_8x4_cortexa9 7 128 5 0.000010464038311697658 +armv7neon_mmm_f32_8x4_cortexa7 9 128 11 0.000029303633762621288 +armv7neon_mmm_f32_8x6_generic 15 128 6 0.000013191467223518409 +armv7neon_mmm_f32_8x6_generic 8 32 19 0.00000799220605890635 +armv7neon_mmm_f32_8x4_cortexa7 25 32 3 0.000006944685928632884 +generic_f32_4x4 5 128 7 0.000014344402542266391 +armv7neon_mmm_f32_8x6_generic 25 32 18 0.00002286640455940777 +armv7neon_mmm_f32_8x6_generic 23 4 17 0.000006658878255694999 +armv7neon_mmm_f32_8x4_cortexa9 15 32 11 0.000010153701137788554 +armv7neon_mmm_f32_8x4_cortexa7 8 32 13 0.000006386976234107792 +generic_f32_4x4 9 128 11 0.0000315155364444221 +armv7neon_mmm_f32_8x4_generic 16 128 5 0.00001883838484826069 +armv7neon_mmm_f32_8x6_generic 8 4 18 0.0000021936894870634253 +armv7neon_mmm_f32_8x6_cortexa7 24 32 17 0.00001822762304619792 +armv7neon_mmm_f32_8x6_generic 24 32 6 0.000006012607283304525 +armv7neon_mmm_f32_8x4_cortexa9 7 4 8 0.0000018992159881966028 +generic_f32_4x4 5 32 8 0.000005268936385502091 +armv7neon_mmm_f32_8x6_generic 23 32 13 0.000017886686307111186 +armv7neon_mmm_f32_8x6_generic 23 128 11 0.00004012008266709576 +armv7neon_mmm_f32_8x4_generic 7 32 3 0.000002052664755929161 +armv7neon_mmm_f32_8x4_generic 7 32 13 0.0000068232658761941584 +armv7neon_mmm_f32_8x4_cortexa9 15 4 3 0.0000018728759441468222 +armv7neon_mmm_f32_8x4_cortexa9 17 32 7 0.000009921472462970692 +armv7neon_mmm_f32_8x4_cortexa7 23 4 5 0.000004108135771777712 +armv7neon_mmm_f32_8x6_generic 17 4 18 0.0000058654454576938545 +armv7neon_mmm_f32_8x6_generic 8 32 6 0.0000023314322550231567 +armv7neon_mmm_f32_8x6_cortexa9 24 32 12 0.000011982465286422162 +armv7neon_mmm_f32_8x6_cortexa9 9 4 12 0.000003049708218088651 +armv7neon_mmm_f32_8x6_cortexa7 9 4 19 0.000005629342662833259 +generic_f32_4x4 3 32 12 0.00000437213523735793 +armv7neon_mmm_f32_8x6_generic 23 128 18 0.00005941769593137103 +armv7neon_mmm_f32_8x6_cortexa7 15 4 18 0.000004622352036845354 +armv7neon_mmm_f32_8x6_cortexa9 24 128 19 0.00007950053817997623 +armv7neon_mmm_f32_8x4_generic 16 128 4 0.000009494054447373921 +armv7neon_mmm_f32_8x6_cortexa7 7 128 17 0.000020414604977074606 +armv7neon_mmm_f32_8x4_cortexa9 7 32 12 0.000005535747117413851 +armv7neon_mmm_f32_8x6_cortexa9 17 4 5 0.000002695145363194299 +generic_f32_4x4 4 128 4 0.000003870015760471616 +armv7neon_mmm_f32_8x4_generic 15 32 11 0.000009681542653487903 +armv7neon_mmm_f32_8x6_cortexa7 7 4 13 0.000002737908308165428 +armv7neon_mmm_f32_8x4_cortexa7 9 32 7 0.000006661491356810654 +armv7neon_mmm_f32_8x4_generic 16 4 8 0.0000023864199459892154 +armv7neon_mmm_f32_8x6_generic 24 32 12 0.000011535291893158489 +armv7neon_mmm_f32_8x6_cortexa9 15 128 11 0.00002729544131868298 +armv7neon_mmm_f32_8x4_cortexa7 15 128 13 0.00003933216865420138 +generic_f32_4x4 11 4 4 0.0000021466514853413024 +armv7neon_mmm_f32_8x4_cortexa7 15 32 7 0.000006892235042247499 +armv7neon_mmm_f32_8x4_generic 24 128 7 0.000028572043647170913 +armv7neon_mmm_f32_8x4_cortexa9 9 128 12 0.000029617239836207004 +armv7neon_mmm_f32_8x6_generic 23 32 6 0.000006267673062941748 +armv7neon_mmm_f32_8x6_cortexa9 23 128 17 0.00006059797167110977 +armv7neon_mmm_f32_8x6_cortexa9 9 128 18 0.00004014798753858387 +armv7neon_mmm_f32_8x6_generic 25 32 19 0.00003066922065226773 +armv7neon_mmm_f32_8x6_generic 9 32 7 0.00000813819010478461 +armv7neon_mmm_f32_8x6_generic 8 128 18 0.000019253370491633585 +armv7neon_mmm_f32_8x6_generic 8 4 5 0.0000012747243711687536 +armv7neon_mmm_f32_8x4_cortexa7 25 4 4 0.000002551159854962235 +armv7neon_mmm_f32_8x4_cortexa9 8 128 13 0.000019869699675831398 +armv7neon_mmm_f32_8x6_cortexa9 9 128 19 0.00005342392770358122 +armv7neon_mmm_f32_8x6_cortexa9 7 4 12 0.000002051630860797947 +armv7neon_mmm_f32_8x6_cortexa9 16 128 13 0.00004015173417099066 +armv7neon_mmm_f32_8x6_cortexa9 23 128 12 0.0000403164031404094 +armv7neon_mmm_f32_8x4_generic 23 128 5 0.000028627967286799668 +armv7neon_mmm_f32_8x4_cortexa9 25 4 8 0.000004634829587692317 +armv7neon_mmm_f32_8x4_cortexa7 15 128 4 0.000010126569600218532 +armv7neon_mmm_f32_8x4_cortexa9 23 32 4 0.000005123975664670012 +armv7neon_mmm_f32_8x6_generic 8 4 17 0.0000024242275965441413 +armv7neon_mmm_f32_8x6_cortexa7 25 128 7 0.000052727920949761586 +armv7neon_mmm_f32_8x4_cortexa7 16 32 9 0.000009437089513621594 +armv7neon_mmm_f32_8x4_generic 23 4 9 0.000005734226973840947 +armv7neon_mmm_f32_8x4_cortexa7 17 128 12 0.0000433599443382883 +armv7neon_mmm_f32_8x4_cortexa7 24 32 3 0.000005409349524302138 +armv7neon_mmm_f32_8x6_cortexa7 8 4 7 0.0000018575873013342354 +armv7neon_mmm_f32_8x6_generic 7 128 19 0.000026452799909726964 +armv7neon_mmm_f32_8x4_cortexa9 24 32 12 0.000013540981249723916 +armv7neon_mmm_f32_8x4_cortexa9 23 128 9 0.000044894692860566556 +armv7neon_mmm_f32_8x6_cortexa9 9 4 17 0.000004505536489134122 +armv7neon_mmm_f32_8x6_generic 15 32 11 0.000008507986633036132 +armv7neon_mmm_f32_8x6_cortexa7 7 32 19 0.000008805049700237493 +armv7neon_mmm_f32_8x4_generic 17 4 4 0.0000020583169447652696 +armv7neon_mmm_f32_8x4_generic 7 32 9 0.000005237513207003328 +armv7neon_mmm_f32_8x6_cortexa9 8 4 5 0.0000012863007612366299 +armv7neon_mmm_f32_8x6_cortexa7 16 128 17 0.00003972939922069338 +armv7neon_mmm_f32_8x6_cortexa9 16 32 17 0.000012476036240479964 +generic_f32_4x4 5 128 4 0.000007343673936530293 +armv7neon_mmm_f32_8x6_cortexa7 17 4 13 0.000006264666040125929 +armv7neon_mmm_f32_8x6_cortexa9 25 128 18 0.00007930397959821057 +armv7neon_mmm_f32_8x6_cortexa9 16 4 5 0.0000020584584410478216 +armv7neon_mmm_f32_8x4_cortexa9 7 32 5 0.00000378972657151533 +armv7neon_mmm_f32_8x6_cortexa9 16 4 18 0.000003945842563203367 +armv7neon_mmm_f32_8x4_generic 23 32 7 0.000009665874611953724 +armv7neon_mmm_f32_8x6_cortexa7 25 128 6 0.000026459671946913376 +generic_f32_4x4 7 4 3 0.0000017591858474192314 +armv7neon_mmm_f32_8x6_cortexa7 16 4 18 0.000003943668999181098 +generic_f32_4x4 4 128 7 0.0000074032510257738404 +armv7neon_mmm_f32_8x4_generic 16 128 13 0.0000374668929079076 +armv7neon_mmm_f32_8x4_cortexa9 9 4 3 0.0000017875665004976476 +armv7neon_mmm_f32_8x4_cortexa9 15 128 9 0.00003010940924012135 +armv7neon_mmm_f32_8x6_cortexa9 17 32 13 0.000018295003723366057 +armv7neon_mmm_f32_8x6_cortexa9 9 4 13 0.000004395459186352696 +armv7neon_mmm_f32_8x6_cortexa7 8 4 12 0.0000016616372440035075 +generic_f32_4x4 5 128 12 0.000021001699183635672 +armv7neon_mmm_f32_8x6_generic 25 4 6 0.000002871872138224116 +armv7neon_mmm_f32_8x6_generic 7 128 5 0.000006875620566369796 +generic_f32_4x4 5 4 4 0.000001600585638499612 +armv7neon_mmm_f32_8x4_cortexa9 23 4 4 0.0000022070594896923405 +armv7neon_mmm_f32_8x6_cortexa9 8 32 17 0.0000065161951430999815 +armv7neon_mmm_f32_8x6_cortexa7 17 4 19 0.000008057471663557758 +armv7neon_mmm_f32_8x6_cortexa9 8 128 7 0.000013701107804035602 +armv7neon_mmm_f32_8x4_cortexa9 23 128 11 0.00004502007320686303 +armv7neon_mmm_f32_8x4_cortexa7 8 32 11 0.000004991462488279611 +armv7neon_mmm_f32_8x6_cortexa9 24 32 18 0.00001769207861513086 +armv7neon_mmm_f32_8x4_generic 25 128 4 0.000019508351532460557 +armv7neon_mmm_f32_8x6_cortexa7 23 4 12 0.0000044400455773711846 +armv7neon_mmm_f32_8x6_cortexa9 24 32 5 0.000006843511403564935 +armv7neon_mmm_f32_8x6_cortexa9 25 128 11 0.000053747765759359946 +armv7neon_mmm_f32_8x4_cortexa7 25 4 11 0.000007253341541711691 +armv7neon_mmm_f32_8x6_cortexa9 23 32 19 0.000024549141146277552 +armv7neon_mmm_f32_8x6_cortexa7 16 128 11 0.000026732044849478285 +armv7neon_mmm_f32_8x6_cortexa9 23 128 19 0.0000801357471530611 +generic_f32_4x4 3 32 4 0.0000017966908912230104 +armv7neon_mmm_f32_8x6_cortexa7 24 128 17 0.00005929496892410179 +armv7neon_mmm_f32_8x6_cortexa9 23 32 12 0.000012490651961414628 +armv7neon_mmm_f32_8x4_cortexa7 16 128 9 0.000029084669529414875 +armv7neon_mmm_f32_8x6_cortexa7 7 128 19 0.000026966388966864694 +armv7neon_mmm_f32_8x4_cortexa7 23 32 8 0.000009566852853696024 +armv7neon_mmm_f32_8x4_generic 17 32 13 0.00001785315018140605 +armv7neon_mmm_f32_8x4_cortexa7 23 4 4 0.00000218193596958567 +armv7neon_mmm_f32_8x4_generic 9 32 7 0.000006462010776076977 +armv7neon_mmm_f32_8x4_generic 23 32 8 0.000009259693411977238 +armv7neon_mmm_f32_8x6_generic 15 128 12 0.000025997314252108204 +generic_f32_4x4 12 32 5 0.000007719446523382617 +armv7neon_mmm_f32_8x6_cortexa9 15 128 17 0.000040755037497853366 +armv7neon_mmm_f32_8x4_generic 24 32 5 0.00000927870929356689 +armv7neon_mmm_f32_8x4_cortexa9 23 32 11 0.00001470624762457902 +armv7neon_mmm_f32_8x4_cortexa7 7 128 7 0.000010321518649139075 +armv7neon_mmm_f32_8x4_cortexa9 17 32 3 0.000005401396003956081 +armv7neon_mmm_f32_8x4_cortexa7 25 128 5 0.00003910615015651058 +armv7neon_mmm_f32_8x6_cortexa9 17 128 18 0.00005978117998553773 +armv7neon_mmm_f32_8x6_generic 7 4 11 0.000002036996116605385 +armv7neon_mmm_f32_8x6_cortexa9 15 4 6 0.0000019083928751646085 +armv7neon_mmm_f32_8x6_cortexa9 9 4 19 0.000005613783088587898 +armv7neon_mmm_f32_8x4_cortexa7 24 32 4 0.0000047895509427620865 +armv7neon_mmm_f32_8x4_cortexa9 8 32 12 0.000004854203771648976 +armv7neon_mmm_f32_8x4_generic 25 4 11 0.000007170056956827459 +armv7neon_mmm_f32_8x6_cortexa7 24 32 6 0.000006172583934492533 +armv7neon_mmm_f32_8x6_generic 23 32 18 0.000017730914760808268 +generic_f32_4x4 9 128 4 0.00001072609031817021 +armv7neon_mmm_f32_8x6_generic 16 32 7 0.000008131169126293927 +armv7neon_mmm_f32_8x6_cortexa9 8 32 11 0.000004596930720411251 +generic_f32_4x4 3 128 3 0.000003987497974732173 +armv7neon_mmm_f32_8x6_generic 16 32 18 0.000011518507884431023 +generic_f32_4x4 12 4 13 0.0000067677797185944386 +armv7neon_mmm_f32_8x6_cortexa7 17 4 12 0.00000420578831765641 +armv7neon_mmm_f32_8x6_cortexa7 25 128 11 0.00005298746347142357 +armv7neon_mmm_f32_8x6_cortexa9 17 4 17 0.0000064380265240168815 +armv7neon_mmm_f32_8x4_cortexa9 16 32 3 0.000003850508528044626 +armv7neon_mmm_f32_8x4_cortexa7 15 4 9 0.0000041824910345443945 +armv7neon_mmm_f32_8x6_cortexa7 16 4 13 0.000004283476073931167 +armv7neon_mmm_f32_8x6_cortexa9 15 32 19 0.00001676006214460718 +armv7neon_mmm_f32_8x6_cortexa9 9 4 6 0.0000017882273242368877 +generic_f32_4x4 11 4 12 0.000005385894826689573 +armv7neon_mmm_f32_8x6_generic 25 128 11 0.00005279747695062305 +armv7neon_mmm_f32_8x4_generic 25 128 11 0.000057163651606893397 +armv7neon_mmm_f32_8x6_generic 16 32 13 0.000011809288098621582 +armv7neon_mmm_f32_8x6_cortexa7 16 128 7 0.00002656376898311115 +armv7neon_mmm_f32_8x6_generic 8 4 19 0.0000028980842866481956 +armv7neon_mmm_f32_8x6_cortexa9 25 4 17 0.000008360160748075129 +armv7neon_mmm_f32_8x6_cortexa7 25 32 17 0.00002416762541107772 +armv7neon_mmm_f32_8x4_cortexa7 9 4 11 0.000003971891623914232 +armv7neon_mmm_f32_8x6_cortexa7 8 128 18 0.000019766077162036943 +armv7neon_mmm_f32_8x4_generic 15 32 3 0.0000036533992903578352 +armv7neon_mmm_f32_8x6_cortexa7 25 32 5 0.0000087944366611451 +armv7neon_mmm_f32_8x6_cortexa7 7 128 5 0.000007076691588612229 +armv7neon_mmm_f32_8x6_generic 23 4 6 0.000002427909563551317 +armv7neon_mmm_f32_8x4_cortexa9 17 4 9 0.000005573474441160942 +armv7neon_mmm_f32_8x4_cortexa9 25 4 5 0.000005112325491179266 +generic_f32_4x4 8 32 5 0.000005323391922801219 +armv7neon_mmm_f32_8x6_cortexa7 24 128 18 0.00005856236723783224 +armv7neon_mmm_f32_8x6_generic 23 4 18 0.000006215918837505929 +armv7neon_mmm_f32_8x4_generic 16 32 4 0.000003260594187485516 +armv7neon_mmm_f32_8x4_generic 8 4 11 0.000002124764231070715 +armv7neon_mmm_f32_8x6_cortexa9 15 128 7 0.000027104995655962794 +armv7neon_mmm_f32_8x4_cortexa7 23 4 3 0.000002539994674766333 +armv7neon_mmm_f32_8x4_cortexa9 15 128 13 0.000040120613837028105 +armv7neon_mmm_f32_8x4_generic 9 32 5 0.000006416826225047117 +armv7neon_mmm_f32_8x6_generic 9 128 13 0.000039231938860404315 +armv7neon_mmm_f32_8x4_cortexa7 17 32 9 0.000014080751383917914 +armv7neon_mmm_f32_8x4_generic 24 4 5 0.0000038475341959329615 +armv7neon_mmm_f32_8x6_cortexa7 17 32 19 0.00002385320479583871 +armv7neon_mmm_f32_8x4_cortexa9 25 4 7 0.000005243554539274239 +armv7neon_mmm_f32_8x4_cortexa9 8 128 7 0.000010306643098752432 +armv7neon_mmm_f32_8x4_cortexa7 9 32 9 0.000009612025649062918 +armv7neon_mmm_f32_8x6_generic 7 128 6 0.000006918233017670394 +armv7neon_mmm_f32_8x6_cortexa7 23 32 6 0.000006480642874727307 +armv7neon_mmm_f32_8x4_cortexa7 17 4 8 0.0000036467312614380765 +armv7neon_mmm_f32_8x6_cortexa9 9 4 18 0.000004263423188010347 +armv7neon_mmm_f32_8x4_cortexa9 23 4 3 0.000002565453674896801 +armv7neon_mmm_f32_8x6_cortexa7 17 128 17 0.00005934218053895787 +armv7neon_mmm_f32_8x6_cortexa9 9 128 11 0.00002703084778716899 +armv7neon_mmm_f32_8x6_cortexa7 8 32 19 0.000008254990878887053 +armv7neon_mmm_f32_8x4_cortexa7 24 4 11 0.000005437311620791031 +armv7neon_mmm_f32_8x6_cortexa7 17 32 12 0.000012103957972190128 +armv7neon_mmm_f32_8x6_cortexa7 16 32 11 0.000008575915963542344 +armv7neon_mmm_f32_8x6_cortexa7 15 128 13 0.00003995376024317136 +armv7neon_mmm_f32_8x4_cortexa7 25 128 12 0.00005747997493377876 +armv7neon_mmm_f32_8x6_cortexa7 9 4 17 0.000004513992669521213 +armv7neon_mmm_f32_8x4_cortexa9 7 32 11 0.000005542086596158504 +armv7neon_mmm_f32_8x4_generic 16 32 8 0.00000600791906169857 +armv7neon_mmm_f32_8x6_cortexa9 8 4 6 0.000001079992540189233 +armv7neon_mmm_f32_8x6_cortexa7 15 4 7 0.0000033103105460849674 +armv7neon_mmm_f32_8x6_generic 7 4 18 0.0000027798204465348195 +armv7neon_mmm_f32_8x4_cortexa9 9 4 4 0.0000016086729123039774 +armv7neon_mmm_f32_8x4_cortexa9 17 4 13 0.000007139907020236834 +armv7neon_mmm_f32_8x6_generic 7 4 17 0.000002811022144491351 +armv7neon_mmm_f32_8x4_generic 16 4 12 0.00000330778427406012 +armv7neon_mmm_f32_8x6_cortexa7 17 32 7 0.000012331946306204285 +armv7neon_mmm_f32_8x4_cortexa7 23 128 4 0.000014866985895709814 +armv7neon_mmm_f32_8x6_generic 8 128 5 0.000006930294425648014 +armv7neon_mmm_f32_8x6_cortexa7 7 128 11 0.000013755100434106755 +armv7neon_mmm_f32_8x4_cortexa7 9 4 3 0.0000017694089316929863 +armv7neon_mmm_f32_8x4_cortexa9 25 128 5 0.00003989811331140034 +armv7neon_mmm_f32_8x6_cortexa9 24 4 18 0.000005659337689416026 +armv7neon_mmm_f32_8x6_cortexa7 7 32 7 0.000004615694133689407 +armv7neon_mmm_f32_8x4_generic 23 4 12 0.000005414200630328292 +armv7neon_mmm_f32_8x6_cortexa7 15 128 7 0.00002674534329947021 +armv7neon_mmm_f32_8x4_cortexa9 23 128 13 0.0000596298628444228 +armv7neon_mmm_f32_8x6_cortexa9 7 32 19 0.000008889380829716555 +armv7neon_mmm_f32_8x6_cortexa7 8 4 17 0.0000025025489216272165 +armv7neon_mmm_f32_8x6_cortexa9 24 128 5 0.000020677151329306246 +armv7neon_mmm_f32_8x6_cortexa9 17 32 17 0.000018491383989214777 +armv7neon_mmm_f32_8x4_cortexa7 8 4 11 0.000002146399138479012 +armv7neon_mmm_f32_8x6_cortexa7 7 4 19 0.0000034947036576981197 +armv7neon_mmm_f32_8x6_cortexa7 15 32 13 0.00001261074648998989 +armv7neon_mmm_f32_8x6_cortexa7 17 128 12 0.000039467248529014934 +armv7neon_mmm_f32_8x6_cortexa9 15 128 13 0.0000405486222699268 +armv7neon_mmm_f32_8x6_cortexa7 8 4 6 0.0000010838257676938484 +armv7neon_mmm_f32_8x4_cortexa7 8 128 11 0.00001478367268084039 +generic_f32_4x4 13 4 5 0.000005005970941882336 +armv7neon_mmm_f32_8x4_generic 9 4 5 0.0000027981911123646093 +armv7neon_mmm_f32_8x4_cortexa9 24 32 3 0.000005499754513818659 +armv7neon_mmm_f32_8x4_cortexa9 16 32 8 0.00000632372871965183 +armv7neon_mmm_f32_8x6_generic 25 32 7 0.000015750403152655726 +armv7neon_mmm_f32_8x6_cortexa7 9 128 6 0.000013469652928515112 +generic_f32_4x4 5 128 8 0.000014178354640152658 +armv7neon_mmm_f32_8x4_cortexa9 25 32 13 0.00002471922478461092 +armv7neon_mmm_f32_8x4_generic 15 32 7 0.000006693321105057111 +armv7neon_mmm_f32_8x4_cortexa9 23 128 8 0.000029940232861406133 +armv7neon_mmm_f32_8x4_cortexa7 7 128 5 0.000010257823038892243 +generic_f32_4x4 4 32 12 0.000003911119050209719 +armv7neon_mmm_f32_8x6_cortexa7 8 128 7 0.000013528068455942153 +armv7neon_mmm_f32_8x4_cortexa7 24 4 5 0.000003893712023496801 +armv7neon_mmm_f32_8x4_cortexa7 9 4 5 0.00000282867688987085 +armv7neon_mmm_f32_8x6_generic 9 4 6 0.0000017337687872243595 +armv7neon_mmm_f32_8x4_generic 8 32 11 0.000004839072297522764 +armv7neon_mmm_f32_8x4_cortexa7 16 32 8 0.000006215610136029107 +armv7neon_mmm_f32_8x4_cortexa7 24 128 12 0.00004296408794693615 +armv7neon_mmm_f32_8x6_generic 23 32 7 0.000012139369977001298 +armv7neon_mmm_f32_8x6_cortexa9 23 32 7 0.00001262657347999726 +armv7neon_mmm_f32_8x6_generic 9 32 18 0.000011811540149507637 +generic_f32_4x4 4 4 12 0.0000019950687694365225 +armv7neon_mmm_f32_8x4_cortexa9 17 32 13 0.000018760347719013833 +armv7neon_mmm_f32_8x4_generic 24 128 8 0.000027893007985374452 +armv7neon_mmm_f32_8x6_generic 7 32 12 0.000004596558109391685 +generic_f32_4x4 12 32 12 0.0000107075710637508 +armv7neon_mmm_f32_8x6_generic 16 4 18 0.000003888078280974674 +generic_f32_4x4 12 128 7 0.000021189568041458556 +armv7neon_mmm_f32_8x6_generic 16 128 11 0.000025972024072475202 +armv7neon_mmm_f32_8x6_cortexa7 15 4 13 0.000004683720683139814 +armv7neon_mmm_f32_8x6_cortexa9 7 128 6 0.000007213870080063144 +armv7neon_mmm_f32_8x6_cortexa9 23 128 11 0.00004071170407195971 +armv7neon_mmm_f32_8x6_generic 24 4 5 0.000002793903924978298 +armv7neon_mmm_f32_8x4_cortexa9 15 32 9 0.000010036115634563751 +generic_f32_4x4 9 128 9 0.000031422044792444864 +armv7neon_mmm_f32_8x4_generic 25 32 4 0.000006133898665196312 +generic_f32_4x4 13 128 8 0.00002772292203595808 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32/cortex_a9.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32/cortex_a9.rs new file mode 100644 index 0000000..23115c9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32/cortex_a9.rs @@ -0,0 +1,861 @@ +use crate::frame::mmm::CostModel; +pub fn model() -> CostModel<'static> { + CostModel { + big_product_mkn_threshold: 4194036.0, + big_product_kernel_choice: "armv7neon_mmm_f32_8x6_cortexa9", + kernels: &[ + "armv7neon_mmm_f32_8x4_cortexa7", + "armv7neon_mmm_f32_8x4_cortexa9", + "armv7neon_mmm_f32_8x4_generic", + "armv7neon_mmm_f32_8x6_cortexa7", + "armv7neon_mmm_f32_8x6_cortexa9", + "armv7neon_mmm_f32_8x6_generic", + "generic_f32_4x4", + ], + mrs: &[4, 8], + nrs: &[4, 6], + feat_norm_mean: &[ + 4.582296677813486, + 4.595402322442016, + 4.571260231028445, + 13.748959231283994, + 1.5179177668804225, + 0.7575757575757576, + 3.5337608449641644, + 0.8831887338111405, + 1.5048409405255878, + 0.7526719476926946, + 2.489123601156796, + 0.8326417704011065, + ], + feat_norm_stddev: &[ + 1.2635817489024164, + 1.2723436827339079, + 1.2620157548883217, + 1.3497763942449361, + 1.1141159992246472, + 0.42854956435545316, + 2.2880460409304937, + 0.32119525880720723, + 1.1154901716833412, + 0.43145902105435263, + 1.7051378780434328, + 0.37329539587896904, + ], + w1: &[ + 0.5391961336135864, + -0.32089367508888245, + 0.203999862074852, + -0.10011337697505951, + 0.09040801972150803, + -0.14198464155197144, + 0.031854499131441116, + 0.12334256619215012, + 0.15339604020118713, + -0.20091375708580017, + -0.014548280276358128, + 0.12154694646596909, + 0.31225234270095825, + 0.10782113671302795, + 0.44618168473243713, + 0.8267014026641846, + -0.1204405128955841, + -0.08261110633611679, + -0.052502430975437164, + 0.3066086769104004, + 0.1493932157754898, + -0.14119412004947662, + -0.1985343098640442, + 0.19361039996147156, + -0.4636686146259308, + 0.08120443671941757, + 0.03210291638970375, + 0.17303235828876495, + 0.16502155363559723, + -0.19771894812583923, + -0.11060577630996704, + 0.08698348701000214, + -0.07793445140123367, + 0.32749465107917786, + 0.3663202226161957, + -0.4629170894622803, + -0.1586134433746338, + 0.4272242486476898, + -0.12016090005636215, + -0.17830348014831543, + -0.05493386462330818, + -0.036517318338155746, + 0.01293050218373537, + 0.016577009111642838, + 0.10738552361726761, + -0.3662779629230499, + -0.2917434275150299, + 0.5752639770507812, + 0.11406347155570984, + 0.8622727394104004, + 0.07158719748258591, + 0.29530274868011475, + -0.11287810653448105, + 0.12262264639139175, + 0.02478562481701374, + 0.17749948799610138, + -0.036227867007255554, + 0.10140471905469894, + -0.011896232143044472, + -0.021761735901236534, + 0.06046223267912865, + 0.5727048516273499, + -0.007826486602425575, + 0.3863913118839264, + -0.04224887117743492, + 0.056023009121418, + -0.02467598207294941, + 0.0385640449821949, + 0.0219524335116148, + -0.03437826409935951, + -0.2060588151216507, + 0.2895224988460541, + 0.10751669108867645, + 0.00845037866383791, + -0.1836385875940323, + -0.24757762253284454, + -0.09606243669986725, + 0.03918633610010147, + 0.07913251221179962, + 0.06499160826206207, + -0.08156774938106537, + 0.08835449814796448, + 0.13896305859088898, + -0.16936920583248138, + 0.010146846994757652, + -0.42553824186325073, + 0.39916151762008667, + -0.004584060981869698, + -0.10256388038396835, + 0.041573416441679, + 0.05155385658144951, + 0.015019520185887814, + 0.09554271399974823, + -0.20487457513809204, + -0.4146610200405121, + -0.773110032081604, + 0.3662724494934082, + -0.23762361705303192, + 0.6974321603775024, + 0.8990052938461304, + 0.02772649936378002, + 0.042197681963443756, + -0.0022736566606909037, + -0.028843341395258904, + -0.4559306204319, + 0.6326258778572083, + 0.4568879008293152, + -0.4892531633377075, + -0.032289132475852966, + 0.04378330707550049, + -0.4118069112300873, + 0.2493579089641571, + -0.021955665200948715, + -0.01538186427205801, + -0.21400974690914154, + -0.09971866756677628, + 0.02185226045548916, + -0.18125569820404053, + -0.13828244805335999, + -0.20846466720104218, + -0.10373540222644806, + 0.4842098653316498, + -0.06586655229330063, + 0.03369470313191414, + 0.013142148964107037, + 0.017437899485230446, + 0.15891534090042114, + 0.5269678831100464, + 0.02546108327805996, + -0.004250233061611652, + -5.8676625485531986e-05, + 0.06777831166982651, + -0.14051207900047302, + 0.6876491904258728, + -0.3455996811389923, + 0.0378129817545414, + 0.15291574597358704, + -0.03829087316989899, + -0.05761529877781868, + -0.05344394966959953, + 0.1421334147453308, + -0.3614322543144226, + -0.21606910228729248, + 0.1558765172958374, + 0.14480257034301758, + -0.1799984872341156, + 0.4238421618938446, + -0.08961529284715652, + -0.04010967165231705, + 0.14250615239143372, + -0.0038367861416190863, + -0.044531334191560745, + -0.08958051353693008, + -0.1577986180782318, + -0.5795103907585144, + -1.1048516035079956, + 0.16444185376167297, + -0.09989812225103378, + -0.26304998993873596, + 0.040687527507543564, + 0.065303735435009, + -0.06267901510000229, + 0.08742637187242508, + 0.02480895072221756, + 0.23719966411590576, + -0.09509539604187012, + 0.39278310537338257, + 0.18978112936019897, + 0.11301649361848831, + -0.16268616914749146, + -0.14119602739810944, + -0.04518252611160278, + 0.10456270724534988, + 0.008367948234081268, + 0.004280170891433954, + 0.01894286274909973, + -0.1547478288412094, + 0.197267547249794, + 0.20271208882331848, + -0.28377917408943176, + -0.26751258969306946, + 0.15954937040805817, + 0.33988064527511597, + 0.16848208010196686, + 0.11668887734413147, + -0.057433612644672394, + -0.049777109175920486, + 0.00744214653968811, + -0.012330793775618076, + -0.08413149416446686, + -0.2053118497133255, + 0.09235486388206482, + -0.1354941576719284, + 0.41610953211784363, + 0.8428494334220886, + 0.880882740020752, + 0.024029193446040154, + -0.08453702926635742, + 0.00771496444940567, + -0.013013732619583607, + -0.23804998397827148, + 0.4110376536846161, + 0.23720477521419525, + -0.13951541483402252, + -0.1747516244649887, + -0.34215790033340454, + 0.014357345178723335, + 0.34224632382392883, + 0.03783192113041878, + 0.01125166192650795, + -0.08253959566354752, + 0.015717405825853348, + -0.22759634256362915, + 0.3980898857116699, + 0.2427154779434204, + -0.3319437801837921, + 0.11146843433380127, + -0.9666317105293274, + -0.12227121740579605, + -0.1948898285627365, + -0.030186548829078674, + 0.0011711223050951958, + -0.040062546730041504, + -0.16316139698028564, + -0.14714862406253815, + 0.13224393129348755, + -0.0019320327555760741, + -0.09674090147018433, + 0.3630145490169525, + -0.019513679668307304, + -0.07729464769363403, + -0.34592965245246887, + 0.15215164422988892, + 0.046678490936756134, + 0.06675180792808533, + -0.08943335711956024, + 0.006386714521795511, + 0.10086977481842041, + -0.07409387081861496, + -0.19604018330574036, + -0.042700666934251785, + 0.12124726921319962, + 0.5694677233695984, + 0.25033196806907654, + 0.01862989366054535, + 0.0053687929175794125, + -0.0017405126709491014, + -0.01638556271791458, + -0.32222822308540344, + 0.5348804593086243, + 0.5546748042106628, + 1.2770946025848389, + 0.11648745834827423, + -0.058405984193086624, + -0.2997635006904602, + -0.2040756195783615, + 0.15525077283382416, + -0.12436354905366898, + -0.089121975004673, + 0.06441225856542587, + 0.2444663643836975, + -0.3495825529098511, + -0.05243751034140587, + 0.08752834796905518, + 0.08800745010375977, + -0.09807545691728592, + -0.3823537230491638, + -0.13047000765800476, + 0.029333092272281647, + 0.11618250608444214, + -0.0638590008020401, + -0.09598273783922195, + -0.07390140742063522, + 0.09151650220155716, + -0.1700282245874405, + 0.23608872294425964, + 0.24879834055900574, + -0.15922772884368896, + -0.33795130252838135, + -0.053850702941417694, + 0.1014639139175415, + -0.05480973795056343, + -0.06753639131784439, + 0.04606246575713158, + -0.07082260400056839, + 0.07848796248435974, + 0.05011916160583496, + -0.05570689216256142, + -0.14584510028362274, + -0.8908579349517822, + -0.5959509611129761, + -0.8982105255126953, + 0.0788002535700798, + -0.03575791418552399, + 0.052424680441617966, + -0.08019822835922241, + 0.10848221182823181, + 0.0957408994436264, + 0.1457311511039734, + -0.1956494003534317, + -0.21669772267341614, + 0.9854136109352112, + -0.23215851187705994, + 0.16359730064868927, + 0.02025810070335865, + -0.08975380659103394, + -0.013868067413568497, + -0.22188447415828705, + 0.020666224882006645, + -0.22304703295230865, + 0.06407633423805237, + 0.19804184138774872, + -0.05285267159342766, + -0.5510660409927368, + -0.8522927761077881, + -0.6061599850654602, + 0.08484024554491043, + -0.08973539620637894, + 0.013228937052190304, + -0.07834818214178085, + 0.02858446165919304, + -0.3826225996017456, + 0.059726644307374954, + 0.1139102503657341, + -0.19311848282814026, + 0.05770142376422882, + 0.22584261000156403, + 0.34312352538108826, + -0.15085645020008087, + 0.34372228384017944, + 0.08070214092731476, + 0.5744000673294067, + -0.08693907409906387, + -0.003695777617394924, + -0.1334235966205597, + 0.06418291479349136, + 0.02848576195538044, + -0.34958112239837646, + -0.3419312834739685, + -0.09599799662828445, + 0.015022341161966324, + 0.03255023807287216, + 0.09713662415742874, + -0.1730588674545288, + 0.1904430240392685, + -0.32815566658973694, + -0.16749203205108643, + 0.35736411809921265, + -0.503787100315094, + 0.5057004690170288, + -0.47198373079299927, + 0.11386436969041824, + -0.0722493901848793, + 0.03358639404177666, + 0.005928087048232555, + -0.05637047439813614, + 0.06552420556545258, + -0.07283362001180649, + -0.09314802289009094, + 0.13586974143981934, + -0.5054865479469299, + -0.18127793073654175, + 0.08853171765804291, + -0.13333705067634583, + -0.2623322308063507, + 0.17757390439510345, + 0.04408252611756325, + -0.0277855321764946, + -0.05175777152180672, + 0.40444689989089966, + -0.03518976643681526, + -0.36402902007102966, + -0.019589770585298538, + -0.05277400091290474, + -0.27273234724998474, + -0.07373850792646408, + -0.058221735060214996, + 0.14292845129966736, + -0.005004828795790672, + -0.05554938316345215, + 0.20361287891864777, + -0.30462127923965454, + -0.1140812486410141, + 0.16081976890563965, + -0.07133162021636963, + -0.20463652908802032, + 0.34733739495277405, + 0.17099761962890625, + 0.025868643075227737, + -0.02960631065070629, + -0.02717636525630951, + 0.02027258090674877, + -0.13165302574634552, + 0.36201152205467224, + 0.5002728700637817, + 0.39691421389579773, + -0.04605599492788315, + 0.28801581263542175, + -1.0140656232833862, + -0.5481916666030884, + 0.0896061584353447, + -0.049390073865652084, + 0.08813252300024033, + -0.1784677952528, + 0.34480658173561096, + -0.36402803659439087, + 0.16948284208774567, + 0.45740315318107605, + -0.23747704923152924, + 0.580975353717804, + -0.24338461458683014, + -0.11410018056631088, + 0.06431885808706284, + -0.0317281149327755, + -0.024683356285095215, + -0.10083278268575668, + 0.024547407403588295, + -0.16270779073238373, + -0.07757837325334549, + 0.19732129573822021, + 0.03790999948978424, + -0.18804220855236053, + 0.8675169348716736, + 0.5377629399299622, + -0.0036910742055624723, + -0.0016441351035609841, + -0.030448857694864273, + 0.07757671177387238, + -0.1475408971309662, + 0.613543689250946, + 0.30266445875167847, + 0.12106148898601532, + 0.05485830456018448, + -0.04748840630054474, + -0.23233623802661896, + -0.1949906051158905, + 0.05692804977297783, + 0.07474583387374878, + -0.11879625171422958, + 0.07200933247804642, + -0.012743310071527958, + -0.02546215057373047, + -0.3765566349029541, + 0.28637346625328064, + -0.18051809072494507, + 0.5034835934638977, + -0.34970414638519287, + -0.2386687994003296, + -0.03804561868309975, + -0.03649319335818291, + -0.10303670912981033, + 0.1299818456172943, + 0.24685724079608917, + -0.34168556332588196, + -0.086674265563488, + 0.32085898518562317, + 0.48488491773605347, + -0.522548258304596, + 0.309568852186203, + 0.167385995388031, + 0.11308691650629044, + 0.14733079075813293, + -0.22416195273399353, + 0.14763982594013214, + -0.07242503017187119, + 0.07601745426654816, + -0.10375087708234787, + -0.03409396857023239, + -0.35759225487709045, + 0.18936687707901, + 0.28248289227485657, + 0.26482364535331726, + 0.061123836785554886, + -0.021603189408779144, + -0.13469825685024261, + 0.07248867303133011, + -0.03464066982269287, + 0.06557167321443558, + 0.16093865036964417, + -0.1718607246875763, + ], + b1: &[ + -0.3893989324569702, + -0.2791002690792084, + 0.07853052020072937, + -0.4629746377468109, + -0.7148261070251465, + 0.8680436015129089, + -0.46459102630615234, + 0.0404132716357708, + -0.44012945890426636, + 0.08434166759252548, + 0.32190972566604614, + -0.20194832980632782, + -0.3781348764896393, + -0.23968002200126648, + -0.581799328327179, + 0.6500483155250549, + -0.6192854046821594, + 0.5922245383262634, + 0.44006091356277466, + 0.2982949912548065, + 0.6136102676391602, + -0.597486138343811, + -0.3697699308395386, + -0.45241132378578186, + 0.60771644115448, + -0.3373708128929138, + 0.5697194337844849, + 0.4784911870956421, + -0.49601855874061584, + 0.5023709535598755, + 0.21592296659946442, + -0.45412343740463257, + 0.5104787945747375, + 0.558862566947937, + 0.4729066491127014, + -0.5520593523979187, + -0.5120576620101929, + -0.7157037258148193, + 0.12596718966960907, + 0.4773174524307251, + ], + w2: &[ + 0.1379607617855072, + 0.09308824688196182, + -0.2596932649612427, + 0.4461972713470459, + 0.3480601906776428, + 0.036684323102235794, + 0.4057384729385376, + -0.3081648051738739, + 0.4561280608177185, + 0.2749394178390503, + -0.1400817334651947, + 0.3145979046821594, + -0.16919250786304474, + 0.7247185707092285, + 0.3479674756526947, + -0.7546817064285278, + 0.38135531544685364, + -0.3939172029495239, + -0.038021210581064224, + 0.026914050802588463, + -0.5281358361244202, + 0.39009571075439453, + 0.4090450406074524, + 0.5053343772888184, + -0.23938016593456268, + 0.488080233335495, + -0.38536468148231506, + -0.23763014376163483, + 0.2661689519882202, + -0.14746293425559998, + -0.7541974186897278, + 0.27726081013679504, + -0.4072169065475464, + -0.8030230402946472, + -0.386343389749527, + 0.6674754619598389, + 0.06677238643169403, + 0.5055669546127319, + -0.44330647587776184, + -0.3423362970352173, + -0.10948927700519562, + 0.11290912330150604, + -0.2759379744529724, + 0.5522158741950989, + -0.5766478776931763, + 0.7288797497749329, + -0.4967955946922302, + -0.5466133952140808, + 0.7254890203475952, + 0.1274457424879074, + 0.3098924458026886, + 0.2524661719799042, + -0.7162019610404968, + 0.19503603875637054, + -0.5212412476539612, + 0.0968603864312172, + 0.4835629463195801, + -0.5865079164505005, + 0.27647316455841064, + 0.1975109577178955, + -0.845225989818573, + 0.4172143042087555, + -0.014424118213355541, + -0.24702520668506622, + -0.16123531758785248, + -0.047759659588336945, + -0.09985388815402985, + 0.10430619865655899, + 0.53556889295578, + 0.2595883011817932, + 0.11729882657527924, + 0.36996161937713623, + -0.41997936367988586, + -0.3332042694091797, + 0.2527308464050293, + 0.6039140820503235, + 0.35183605551719666, + 0.42042237520217896, + -0.2265913337469101, + -0.06852111965417862, + 0.3749903440475464, + 0.3698897361755371, + -0.43096107244491577, + 0.1275794953107834, + 0.27926334738731384, + -0.3282606303691864, + 0.290679931640625, + -0.14467079937458038, + 0.3357028663158417, + -0.0683436468243599, + -0.35492125153541565, + -0.14275093376636505, + -0.1504347324371338, + 0.1782987266778946, + 0.07464402168989182, + -0.2788643538951874, + 0.5896115303039551, + -0.314520001411438, + -0.3235827684402466, + -0.2899278700351715, + -0.21264874935150146, + 0.41862159967422485, + 0.3237628936767578, + 0.2948566973209381, + -0.6101413369178772, + -0.025511808693408966, + -0.4238346517086029, + -0.28283095359802246, + 0.32077667117118835, + -0.34138476848602295, + -0.5257527232170105, + 0.24129967391490936, + -0.38175472617149353, + -0.20559589564800262, + -0.11267697811126709, + 0.32475054264068604, + 0.29545050859451294, + 0.0010625360300764441, + 0.4097916781902313, + -0.3120468556880951, + 0.3134985566139221, + 0.33620578050613403, + -0.27408266067504883, + -0.0118736382573843, + 0.21356475353240967, + -0.6716119647026062, + 0.14166241884231567, + 0.020748334005475044, + 0.27158322930336, + -0.27066248655319214, + -0.5078546404838562, + 0.39642488956451416, + 0.4044502079486847, + 0.1363500952720642, + 0.38089585304260254, + -0.18438327312469482, + -0.08652642369270325, + 0.05718545988202095, + -0.5758764743804932, + 0.0948563665151596, + 0.298057496547699, + -0.07299521565437317, + -0.24248233437538147, + 0.29135069251060486, + -0.44556060433387756, + 0.6689074039459229, + -0.12930674850940704, + -0.12669484317302704, + 0.1074564978480339, + -0.20472179353237152, + 0.14787982404232025, + -0.13180267810821533, + 0.3045596182346344, + -0.3345180153846741, + -0.3405822217464447, + 0.22327540814876556, + 0.02809770777821541, + 0.17404714226722717, + 0.22873322665691376, + -0.3915692865848541, + -0.39005470275878906, + -0.4675980806350708, + 0.44798821210861206, + -0.31790846586227417, + -0.21734853088855743, + 0.2172199934720993, + -0.3485357165336609, + 0.1241735890507698, + -0.6933310031890869, + -0.09649480134248734, + 0.24731965363025665, + -0.20421941578388214, + 0.13033808767795563, + -0.4282769560813904, + -0.22173112630844116, + 0.08912057429552078, + -0.3927532434463501, + 0.3523387908935547, + 0.36073970794677734, + -0.036902282387018204, + 0.5880261063575745, + -0.29945725202560425, + -0.40845751762390137, + -0.3265145421028137, + 0.370391309261322, + -0.3553546965122223, + 0.5133077502250671, + 0.1800842434167862, + -0.34683868288993835, + 0.28811708092689514, + 0.3033837080001831, + -0.4140017628669739, + 0.4362258017063141, + 0.3689269423484802, + 0.3121638596057892, + -0.3287503123283386, + -0.15226924419403076, + -0.17191028594970703, + -0.10683685541152954, + 0.34219542145729065, + 0.34955963492393494, + 0.22892920672893524, + -0.20123478770256042, + -0.3934169411659241, + 0.25449705123901367, + -0.541163444519043, + 0.21640898287296295, + 0.19343338906764984, + -0.14020974934101105, + 0.010480044409632683, + -0.24229897558689117, + -0.4682120084762573, + 0.02336042746901512, + 0.039344485849142075, + 0.42446646094322205, + -0.3173693120479584, + 0.23609045147895813, + 0.20335273444652557, + -0.19347436726093292, + -0.05698636546730995, + 0.17990583181381226, + 0.30915674567222595, + 0.3115670382976532, + 0.4147215485572815, + -0.38558056950569153, + -0.12379863113164902, + 0.025996098294854164, + -0.3010733425617218, + 0.03275908902287483, + -0.6039671897888184, + 0.06267470866441727, + -0.012677585706114769, + 0.3484704792499542, + 0.24301587045192719, + -0.40881243348121643, + -0.16732162237167358, + 0.190901979804039, + -0.5619192719459534, + 0.30009278655052185, + -0.43359509110450745, + 0.26643550395965576, + 0.5083268880844116, + 0.3491555452346802, + 0.4731655716896057, + 0.6301924586296082, + -0.8111121654510498, + 0.6473397016525269, + -0.001451796037144959, + 0.3649038076400757, + -0.6002859473228455, + -0.41925248503685, + 0.05584913119673729, + 0.7823511362075806, + 0.421135276556015, + 0.5779385566711426, + -0.49475061893463135, + 0.5293950438499451, + -0.45432502031326294, + -0.680946946144104, + -0.3506624102592468, + -0.21028658747673035, + 0.4775547385215759, + 0.25049126148223877, + 0.2707470655441284, + -0.3469635546207428, + 0.5959001779556274, + -0.5623777508735657, + -0.6334168910980225, + 0.4096938669681549, + -0.3921370208263397, + -0.27649807929992676, + 0.4424516260623932, + -0.28308066725730896, + -0.22009265422821045, + -0.386872798204422, + 0.5130718350410461, + 0.5702601075172424, + 0.7469420433044434, + -0.09606175124645233, + -0.4271978437900543, + ], + b2: &[ + -0.07522959262132645, + 0.3644154667854309, + -0.25166040658950806, + -0.12973527610301971, + 0.25026997923851013, + -0.2794199585914612, + -0.17614373564720154, + ], + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32/cortex_a9.txt b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32/cortex_a9.txt new file mode 100644 index 0000000..201cd54 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm32/cortex_a9.txt @@ -0,0 +1,1701 @@ +armv7neon_mmm_f32_8x6_generic 17 128 19 0.00006235573582381347 +armv7neon_mmm_f32_8x4_cortexa7 23 32 3 0.000006021597781788675 +armv7neon_mmm_f32_8x6_cortexa7 17 128 7 0.000041163109831630036 +armv7neon_mmm_f32_8x6_generic 9 4 5 0.0000020753617625129768 +generic_f32_4x4 13 4 3 0.000003220368712907131 +armv7neon_mmm_f32_8x4_cortexa7 9 128 3 0.000011219671010907719 +armv7neon_mmm_f32_8x6_generic 24 4 12 0.00000416032880372066 +armv7neon_mmm_f32_8x6_cortexa9 15 128 12 0.00002134037524275856 +armv7neon_mmm_f32_8x4_cortexa7 16 32 7 0.000007487730700761545 +armv7neon_mmm_f32_8x4_generic 25 128 4 0.000015723210051937644 +armv7neon_mmm_f32_8x6_cortexa9 15 128 13 0.000031729639665247244 +armv7neon_mmm_f32_8x4_cortexa7 17 128 5 0.00003233807616782481 +generic_f32_4x4 5 4 9 0.000004114340189363069 +armv7neon_mmm_f32_8x4_cortexa9 7 4 12 0.0000026805076801341797 +armv7neon_mmm_f32_8x6_cortexa7 8 4 7 0.0000020191503624854738 +generic_f32_4x4 5 32 4 0.0000030555103653558445 +armv7neon_mmm_f32_8x4_cortexa7 8 128 12 0.000016128094616247412 +generic_f32_4x4 13 128 8 0.000029057855790622486 +armv7neon_mmm_f32_8x6_cortexa7 23 128 5 0.00002125083853467516 +generic_f32_4x4 5 128 4 0.000007724128084704853 +generic_f32_4x4 9 128 5 0.000022305019807747277 +armv7neon_mmm_f32_8x6_generic 23 32 11 0.000011268391904458938 +armv7neon_mmm_f32_8x4_generic 7 4 12 0.0000026796237449338948 +armv7neon_mmm_f32_8x4_cortexa9 8 32 11 0.000004490236277399054 +armv7neon_mmm_f32_8x6_cortexa7 23 4 7 0.000005431543985839428 +armv7neon_mmm_f32_8x4_cortexa9 7 32 11 0.000004893302315959357 +armv7neon_mmm_f32_8x6_generic 17 4 6 0.0000024671248311817606 +armv7neon_mmm_f32_8x6_cortexa7 7 4 7 0.0000023786534435590155 +armv7neon_mmm_f32_8x6_cortexa9 23 32 7 0.000011142179078429717 +armv7neon_mmm_f32_8x6_cortexa7 23 128 18 0.00006187753967619513 +armv7neon_mmm_f32_8x4_cortexa9 17 4 13 0.000007583246734362711 +armv7neon_mmm_f32_8x4_cortexa7 25 32 8 0.000013742616132230942 +armv7neon_mmm_f32_8x4_cortexa9 15 128 13 0.00003174823725370825 +armv7neon_mmm_f32_8x6_cortexa7 8 32 17 0.0000068525292311713665 +armv7neon_mmm_f32_8x4_generic 23 4 12 0.000005946433902061308 +armv7neon_mmm_f32_8x4_generic 7 32 12 0.000004913908866329809 +armv7neon_mmm_f32_8x4_generic 9 4 5 0.0000029635862825605913 +armv7neon_mmm_f32_8x6_generic 24 128 7 0.00003097026633557495 +armv7neon_mmm_f32_8x4_generic 24 4 13 0.000007343587112941961 +armv7neon_mmm_f32_8x4_generic 23 4 8 0.000004129384710709428 +generic_f32_4x4 11 128 8 0.00002206478635932785 +armv7neon_mmm_f32_8x4_generic 8 4 9 0.000002203244153911471 +armv7neon_mmm_f32_8x4_cortexa9 17 32 3 0.000004810722960669039 +armv7neon_mmm_f32_8x4_cortexa7 7 128 12 0.000016707563395180685 +armv7neon_mmm_f32_8x6_cortexa7 7 4 12 0.000002638802872509435 +armv7neon_mmm_f32_8x4_cortexa7 17 128 9 0.0000480527815279415 +armv7neon_mmm_f32_8x4_cortexa7 7 128 7 0.000011282871511736955 +armv7neon_mmm_f32_8x4_cortexa9 16 4 5 0.000002910382588603331 +armv7neon_mmm_f32_8x4_generic 17 128 13 0.00004671426916376633 +armv7neon_mmm_f32_8x4_cortexa7 9 128 8 0.00002150638840328972 +armv7neon_mmm_f32_8x4_cortexa9 25 32 12 0.000015994893141833952 +armv7neon_mmm_f32_8x4_cortexa7 23 32 11 0.000016244020387064702 +armv7neon_mmm_f32_8x6_cortexa9 7 4 11 0.000002387141196478666 +armv7neon_mmm_f32_8x6_cortexa7 7 4 5 0.0000013875129728218855 +armv7neon_mmm_f32_8x4_cortexa7 15 32 4 0.0000039730943727200555 +armv7neon_mmm_f32_8x4_cortexa7 24 32 3 0.000006034875853921332 +armv7neon_mmm_f32_8x4_cortexa9 8 4 5 0.0000016776748569042105 +armv7neon_mmm_f32_8x6_cortexa7 16 4 18 0.0000045013629937264245 +armv7neon_mmm_f32_8x4_cortexa9 8 128 8 0.000008024678845849025 +armv7neon_mmm_f32_8x6_cortexa9 25 4 18 0.000008301730396887936 +armv7neon_mmm_f32_8x4_generic 7 32 11 0.000004892898505691065 +armv7neon_mmm_f32_8x4_cortexa9 16 128 4 0.000008030995808345325 +armv7neon_mmm_f32_8x4_cortexa7 16 32 3 0.000004194758783711011 +armv7neon_mmm_f32_8x4_generic 25 32 3 0.000006291923472603681 +generic_f32_4x4 3 32 13 0.000005952451348897524 +armv7neon_mmm_f32_8x6_cortexa9 23 32 19 0.000021623159632105592 +armv7neon_mmm_f32_8x4_generic 9 4 7 0.0000030054177268153855 +armv7neon_mmm_f32_8x4_cortexa9 7 128 3 0.0000044229477614846184 +armv7neon_mmm_f32_8x4_generic 25 32 12 0.0000159741241286288 +armv7neon_mmm_f32_8x6_generic 8 32 5 0.000002351046891403544 +armv7neon_mmm_f32_8x6_generic 8 128 17 0.00001572220287732098 +armv7neon_mmm_f32_8x6_cortexa9 25 4 13 0.00000878947090986632 +armv7neon_mmm_f32_8x6_cortexa7 9 128 18 0.00004088792544048485 +armv7neon_mmm_f32_8x4_generic 9 128 8 0.000015874731663155136 +armv7neon_mmm_f32_8x4_generic 16 128 5 0.000015914646023810615 +armv7neon_mmm_f32_8x6_cortexa7 17 32 18 0.000019117240551030633 +armv7neon_mmm_f32_8x4_cortexa9 7 4 11 0.0000026740706182831685 +armv7neon_mmm_f32_8x4_cortexa7 9 128 13 0.00004271080548146737 +armv7neon_mmm_f32_8x6_generic 16 128 11 0.000020976534312868846 +armv7neon_mmm_f32_8x4_cortexa7 16 128 7 0.00002177826510527881 +armv7neon_mmm_f32_8x4_generic 15 32 7 0.000006163372672989967 +armv7neon_mmm_f32_8x4_generic 9 128 11 0.000023697623425846144 +armv7neon_mmm_f32_8x6_cortexa7 16 4 6 0.0000018480490329829836 +armv7neon_mmm_f32_8x4_cortexa9 25 4 7 0.00000554548266542983 +armv7neon_mmm_f32_8x6_generic 15 128 13 0.00003163198682165344 +armv7neon_mmm_f32_8x6_cortexa7 25 32 18 0.000025184955719578352 +armv7neon_mmm_f32_8x6_generic 8 4 6 0.000001113907974305815 +armv7neon_mmm_f32_8x6_cortexa7 16 32 5 0.0000050492542920704264 +armv7neon_mmm_f32_8x6_generic 8 32 19 0.000007097287986832066 +armv7neon_mmm_f32_8x6_cortexa9 25 32 5 0.000007806390304931795 +armv7neon_mmm_f32_8x6_cortexa9 15 128 11 0.000021399956173572563 +armv7neon_mmm_f32_8x6_generic 16 32 6 0.000003721529767067075 +armv7neon_mmm_f32_8x4_cortexa7 17 32 5 0.000010843745704621824 +armv7neon_mmm_f32_8x4_generic 7 128 7 0.00000843066632668338 +armv7neon_mmm_f32_8x4_generic 23 32 13 0.000016850629087203773 +armv7neon_mmm_f32_8x4_cortexa9 16 32 9 0.000008424770761452617 +armv7neon_mmm_f32_8x4_cortexa9 15 32 13 0.000011652714145510248 +armv7neon_mmm_f32_8x4_generic 23 128 5 0.000023910719482642124 +armv7neon_mmm_f32_8x4_cortexa9 23 128 3 0.000012498528953898262 +armv7neon_mmm_f32_8x4_generic 16 32 8 0.000005593957529271591 +armv7neon_mmm_f32_8x6_cortexa7 7 128 13 0.000021419187824431687 +armv7neon_mmm_f32_8x6_cortexa9 7 128 6 0.000005900049478814555 +armv7neon_mmm_f32_8x4_cortexa7 24 32 11 0.000015824034451655578 +armv7neon_mmm_f32_8x6_cortexa7 16 4 17 0.000004987657461648502 +armv7neon_mmm_f32_8x4_cortexa7 23 32 8 0.000010735439695395534 +armv7neon_mmm_f32_8x4_cortexa9 25 4 5 0.000005392929349101573 +armv7neon_mmm_f32_8x4_generic 25 4 5 0.000005418182338760064 +armv7neon_mmm_f32_8x4_generic 7 128 9 0.000012382199177609924 +armv7neon_mmm_f32_8x4_cortexa9 9 32 9 0.000008579188782470308 +armv7neon_mmm_f32_8x4_generic 8 128 12 0.000011801915167566902 +armv7neon_mmm_f32_8x4_cortexa9 23 4 8 0.0000041152642927900605 +armv7neon_mmm_f32_8x4_generic 7 4 11 0.0000026764041239252324 +generic_f32_4x4 13 128 12 0.00004319526080896362 +generic_f32_4x4 5 32 5 0.000005725142687375275 +generic_f32_4x4 5 128 12 0.00002200938035136394 +armv7neon_mmm_f32_8x4_generic 23 4 5 0.000004369620528795486 +armv7neon_mmm_f32_8x6_generic 9 4 11 0.000003424705713246475 +armv7neon_mmm_f32_8x6_generic 17 32 12 0.000010395428642618952 +generic_f32_4x4 13 32 11 0.000015867743214986118 +armv7neon_mmm_f32_8x6_cortexa9 9 32 17 0.000010850143335646025 +armv7neon_mmm_f32_8x6_cortexa9 23 4 6 0.0000028251957000612876 +armv7neon_mmm_f32_8x6_cortexa9 9 128 7 0.000020914788448206477 +armv7neon_mmm_f32_8x4_cortexa7 15 128 11 0.000032581738066809436 +armv7neon_mmm_f32_8x4_cortexa9 25 128 3 0.000016370587474295946 +armv7neon_mmm_f32_8x4_cortexa7 23 4 4 0.0000024826703257808083 +armv7neon_mmm_f32_8x4_generic 9 128 4 0.000008108848864529598 +armv7neon_mmm_f32_8x6_cortexa9 25 128 5 0.00002136490649839919 +armv7neon_mmm_f32_8x6_generic 23 128 19 0.00006253554336626349 +generic_f32_4x4 12 4 12 0.000005236763691820686 +armv7neon_mmm_f32_8x6_cortexa9 8 4 7 0.0000019309843288986318 +armv7neon_mmm_f32_8x4_cortexa7 15 4 5 0.000003357403897484634 +armv7neon_mmm_f32_8x6_cortexa9 24 4 6 0.0000024072433754401144 +armv7neon_mmm_f32_8x6_cortexa7 25 128 7 0.000054619400677372974 +armv7neon_mmm_f32_8x6_generic 7 4 19 0.000004265433096040261 +generic_f32_4x4 7 4 12 0.0000041235655802886916 +armv7neon_mmm_f32_8x6_generic 17 128 17 0.000046385914441781564 +armv7neon_mmm_f32_8x4_generic 8 32 7 0.0000032122117166780275 +armv7neon_mmm_f32_8x4_cortexa9 16 4 7 0.0000030113926906216977 +generic_f32_4x4 13 128 13 0.00005791151370731608 +armv7neon_mmm_f32_8x4_cortexa7 9 32 9 0.000010771893810221605 +armv7neon_mmm_f32_8x4_cortexa7 23 4 7 0.000004820473947807521 +armv7neon_mmm_f32_8x4_cortexa9 24 4 13 0.00000731015216867438 +generic_f32_4x4 3 4 5 0.0000018607872137198219 +armv7neon_mmm_f32_8x4_generic 15 32 9 0.000008854455084657494 +armv7neon_mmm_f32_8x6_cortexa9 15 32 6 0.000004174355801441308 +armv7neon_mmm_f32_8x6_cortexa9 16 4 5 0.000002250377451226225 +armv7neon_mmm_f32_8x6_cortexa9 16 32 19 0.000013757586520904355 +armv7neon_mmm_f32_8x6_cortexa9 24 128 11 0.000031276431090202235 +armv7neon_mmm_f32_8x4_generic 25 128 7 0.00003164406261833124 +armv7neon_mmm_f32_8x4_generic 24 4 4 0.0000020584132727266 +generic_f32_4x4 7 128 11 0.000022350186275600858 +armv7neon_mmm_f32_8x4_cortexa9 23 4 4 0.0000022753692017814563 +armv7neon_mmm_f32_8x6_cortexa9 17 32 19 0.00002069369907930334 +generic_f32_4x4 4 128 13 0.00001478326569151377 +armv7neon_mmm_f32_8x6_cortexa9 23 32 18 0.00001626278262596922 +armv7neon_mmm_f32_8x6_generic 7 128 18 0.000016667585395966858 +armv7neon_mmm_f32_8x4_cortexa9 8 128 3 0.000004452778028984887 +armv7neon_mmm_f32_8x4_cortexa7 16 128 13 0.000042499574729319346 +generic_f32_4x4 3 32 11 0.000004620575096918176 +generic_f32_4x4 4 4 8 0.0000015603089093623964 +armv7neon_mmm_f32_8x6_cortexa9 15 32 7 0.00000772763531212548 +armv7neon_mmm_f32_8x4_cortexa7 9 4 11 0.000004505536773333099 +armv7neon_mmm_f32_8x6_cortexa9 15 32 18 0.000011416109977905379 +armv7neon_mmm_f32_8x4_cortexa7 24 128 12 0.00004730343110835064 +armv7neon_mmm_f32_8x4_cortexa9 8 4 7 0.000001726865000473268 +armv7neon_mmm_f32_8x4_cortexa9 17 4 12 0.00000554999550214869 +armv7neon_mmm_f32_8x4_cortexa9 24 32 3 0.000004919761516416024 +armv7neon_mmm_f32_8x4_cortexa7 7 4 7 0.0000020327105105148146 +generic_f32_4x4 8 32 11 0.000008129741741591272 +armv7neon_mmm_f32_8x6_cortexa9 17 128 6 0.000015685032106978185 +armv7neon_mmm_f32_8x6_cortexa7 24 4 13 0.0000069628322548194484 +generic_f32_4x4 11 32 7 0.000008418658676221946 +armv7neon_mmm_f32_8x4_cortexa9 23 32 13 0.000016859244394935546 +armv7neon_mmm_f32_8x6_cortexa7 15 4 19 0.000007361880271251662 +armv7neon_mmm_f32_8x6_cortexa7 25 128 19 0.00010862057908423823 +generic_f32_4x4 7 128 5 0.00001508121452285347 +armv7neon_mmm_f32_8x4_generic 8 4 12 0.0000020475145659989298 +armv7neon_mmm_f32_8x6_generic 23 32 6 0.000005742234510888845 +armv7neon_mmm_f32_8x6_cortexa9 8 32 7 0.000003926599359988243 +armv7neon_mmm_f32_8x4_cortexa7 16 32 5 0.00000738840656931522 +generic_f32_4x4 3 128 5 0.000007875053042737703 +armv7neon_mmm_f32_8x6_cortexa9 9 128 5 0.000010860557674890965 +armv7neon_mmm_f32_8x6_generic 15 32 11 0.000007797932920988772 +generic_f32_4x4 8 4 3 0.0000019148150797610994 +armv7neon_mmm_f32_8x4_generic 23 32 9 0.000012836974653040903 +armv7neon_mmm_f32_8x4_cortexa7 25 4 3 0.0000035757992765103294 +armv7neon_mmm_f32_8x4_generic 7 32 8 0.0000034181328210189654 +armv7neon_mmm_f32_8x6_cortexa9 25 32 7 0.000014158102081231813 +armv7neon_mmm_f32_8x6_generic 9 4 18 0.000004486229240869606 +armv7neon_mmm_f32_8x4_cortexa7 8 4 4 0.0000010559381010362017 +armv7neon_mmm_f32_8x4_cortexa9 25 32 3 0.000006292048691193805 +armv7neon_mmm_f32_8x4_cortexa9 24 4 11 0.000005847758703659802 +armv7neon_mmm_f32_8x4_cortexa9 7 128 12 0.00001242120677392976 +armv7neon_mmm_f32_8x4_cortexa7 16 128 9 0.00003210848238315819 +armv7neon_mmm_f32_8x4_cortexa9 24 128 7 0.00002382518082369683 +armv7neon_mmm_f32_8x4_generic 24 32 3 0.0000049198649021802165 +armv7neon_mmm_f32_8x4_cortexa9 25 4 11 0.000007781278370753281 +armv7neon_mmm_f32_8x6_cortexa7 17 32 12 0.000012940679584474003 +armv7neon_mmm_f32_8x6_cortexa7 16 32 17 0.000013190821660739104 +armv7neon_mmm_f32_8x6_cortexa7 25 4 19 0.000012097338566670792 +armv7neon_mmm_f32_8x6_generic 16 32 18 0.000010138504593611297 +armv7neon_mmm_f32_8x4_generic 9 128 5 0.000015947315101595927 +armv7neon_mmm_f32_8x6_cortexa9 23 4 5 0.0000031081830446744932 +armv7neon_mmm_f32_8x6_cortexa9 24 128 7 0.00003103549849311001 +armv7neon_mmm_f32_8x4_cortexa7 25 4 13 0.000010646545231619916 +generic_f32_4x4 9 128 12 0.00003260110763311798 +armv7neon_mmm_f32_8x6_cortexa9 23 4 17 0.000007631985122319121 +armv7neon_mmm_f32_8x4_cortexa9 8 128 13 0.00001574791956845722 +armv7neon_mmm_f32_8x6_cortexa9 23 4 18 0.000007303182081264746 +armv7neon_mmm_f32_8x4_cortexa7 24 128 13 0.00006349168486923089 +armv7neon_mmm_f32_8x4_generic 23 32 11 0.000012994166844502801 +generic_f32_4x4 7 32 4 0.0000031041482961797043 +armv7neon_mmm_f32_8x4_cortexa9 9 32 7 0.000005959447070414145 +armv7neon_mmm_f32_8x4_cortexa7 23 128 11 0.00004845207607490223 +armv7neon_mmm_f32_8x6_cortexa7 17 4 11 0.0000052419075338197185 +generic_f32_4x4 8 4 13 0.00000500809427570026 +armv7neon_mmm_f32_8x4_generic 24 32 7 0.000008731713037367055 +armv7neon_mmm_f32_8x6_cortexa9 25 128 13 0.00006164900543131858 +armv7neon_mmm_f32_8x6_cortexa9 17 4 7 0.000004817790997463295 +armv7neon_mmm_f32_8x6_cortexa9 8 32 19 0.000007141501791335855 +armv7neon_mmm_f32_8x4_cortexa9 16 4 4 0.000001530544742707957 +armv7neon_mmm_f32_8x6_generic 25 4 17 0.000008895187762533163 +armv7neon_mmm_f32_8x4_cortexa7 23 4 3 0.0000028668169166829235 +armv7neon_mmm_f32_8x6_cortexa7 24 128 11 0.00004128096933839809 +generic_f32_4x4 7 4 13 0.000005447538386200191 +armv7neon_mmm_f32_8x6_cortexa9 9 128 17 0.0000311696244066944 +armv7neon_mmm_f32_8x6_cortexa7 25 4 17 0.000009561336620584211 +armv7neon_mmm_f32_8x4_generic 16 128 8 0.000015609467528565242 +armv7neon_mmm_f32_8x6_cortexa9 7 4 6 0.0000015274572782560943 +armv7neon_mmm_f32_8x6_cortexa9 7 4 19 0.000004327266570693739 +generic_f32_4x4 4 128 3 0.0000042186208925339794 +armv7neon_mmm_f32_8x6_cortexa9 16 32 6 0.000003783089782648409 +armv7neon_mmm_f32_8x4_generic 24 4 12 0.000005240725524660556 +armv7neon_mmm_f32_8x6_generic 24 4 5 0.000003075012528242585 +armv7neon_mmm_f32_8x4_cortexa7 16 128 3 0.000011341419957358612 +generic_f32_4x4 13 32 7 0.000010885101829856154 +generic_f32_4x4 8 128 13 0.00002903953507476824 +armv7neon_mmm_f32_8x6_cortexa9 7 32 12 0.000004528441801230068 +armv7neon_mmm_f32_8x4_cortexa9 8 128 9 0.000011968397967338213 +generic_f32_4x4 9 4 11 0.000005879906070039223 +armv7neon_mmm_f32_8x4_generic 9 128 9 0.000023684049431562338 +armv7neon_mmm_f32_8x6_cortexa7 23 128 19 0.00008252903643379173 +armv7neon_mmm_f32_8x6_generic 23 128 7 0.000031381294348434004 +armv7neon_mmm_f32_8x4_generic 15 4 13 0.000005669760296114337 +armv7neon_mmm_f32_8x4_generic 15 4 11 0.000004507200935249084 +armv7neon_mmm_f32_8x4_generic 15 4 3 0.0000019251390208258458 +generic_f32_4x4 9 32 7 0.00000832450452449881 +generic_f32_4x4 9 4 9 0.000005828318767488796 +armv7neon_mmm_f32_8x4_cortexa7 16 4 3 0.000002093301960254744 +armv7neon_mmm_f32_8x4_generic 15 128 12 0.000023812063445324994 +armv7neon_mmm_f32_8x6_cortexa7 7 4 19 0.000004468409864573403 +generic_f32_4x4 8 4 7 0.000002955604897568221 +armv7neon_mmm_f32_8x6_cortexa9 9 32 12 0.000007238338427088594 +armv7neon_mmm_f32_8x6_generic 16 32 12 0.00000694068903036478 +armv7neon_mmm_f32_8x6_cortexa7 25 128 6 0.000027308370544312844 +armv7neon_mmm_f32_8x6_cortexa9 24 4 17 0.000006795821587814609 +armv7neon_mmm_f32_8x6_cortexa7 15 128 12 0.00002803261400606977 +generic_f32_4x4 9 32 8 0.000008044423595514706 +armv7neon_mmm_f32_8x4_generic 23 128 9 0.00003548379543389711 +armv7neon_mmm_f32_8x4_cortexa7 17 32 3 0.000005926615429744039 +armv7neon_mmm_f32_8x4_generic 8 128 7 0.000008235490634332163 +armv7neon_mmm_f32_8x4_cortexa7 9 4 12 0.000004255018272123806 +armv7neon_mmm_f32_8x4_cortexa9 16 32 11 0.000008524184894246237 +armv7neon_mmm_f32_8x4_cortexa9 24 32 4 0.000004264845803875226 +generic_f32_4x4 3 4 11 0.000002555428269031667 +armv7neon_mmm_f32_8x4_cortexa7 24 4 4 0.0000022261643758837446 +armv7neon_mmm_f32_8x4_cortexa7 15 128 3 0.000011306220407671872 +armv7neon_mmm_f32_8x6_generic 8 4 18 0.000002337607386621077 +armv7neon_mmm_f32_8x4_cortexa9 25 4 4 0.0000026788269418985845 +generic_f32_4x4 11 4 7 0.000004308040885452502 +armv7neon_mmm_f32_8x4_cortexa9 15 128 4 0.000008223517905022214 +generic_f32_4x4 5 32 11 0.000008288284199113315 +armv7neon_mmm_f32_8x6_cortexa9 9 4 12 0.0000032467302759009237 +armv7neon_mmm_f32_8x4_generic 24 128 7 0.00002382735987416326 +armv7neon_mmm_f32_8x6_generic 24 32 13 0.000015487434262812744 +armv7neon_mmm_f32_8x6_generic 17 128 13 0.00004629753684559049 +armv7neon_mmm_f32_8x6_cortexa7 24 128 13 0.00006117633525152988 +armv7neon_mmm_f32_8x6_generic 24 4 19 0.000008326315174295047 +armv7neon_mmm_f32_8x6_cortexa7 17 32 7 0.000013248073815175261 +armv7neon_mmm_f32_8x4_cortexa9 8 4 9 0.0000022033600613983073 +generic_f32_4x4 13 4 12 0.000007132280989099253 +armv7neon_mmm_f32_8x4_cortexa7 16 128 4 0.000010902014380146801 +armv7neon_mmm_f32_8x6_cortexa7 9 4 6 0.0000019491119088955803 +generic_f32_4x4 3 4 7 0.0000019042920757312053 +armv7neon_mmm_f32_8x4_generic 7 32 5 0.0000033764800411714497 +armv7neon_mmm_f32_8x6_cortexa7 23 4 19 0.000010215694576435441 +armv7neon_mmm_f32_8x4_generic 17 4 3 0.0000025969094160852644 +generic_f32_4x4 5 4 11 0.0000041349188443565096 +armv7neon_mmm_f32_8x6_cortexa7 9 32 17 0.000013295032359470915 +armv7neon_mmm_f32_8x4_cortexa7 15 128 13 0.00004313466302520057 +armv7neon_mmm_f32_8x4_cortexa9 15 4 8 0.0000030055981545968782 +armv7neon_mmm_f32_8x6_generic 16 4 12 0.0000029570565051151263 +armv7neon_mmm_f32_8x6_cortexa9 8 4 11 0.000002014524477563071 +armv7neon_mmm_f32_8x4_cortexa9 8 4 3 0.0000012065261711370315 +armv7neon_mmm_f32_8x4_cortexa7 7 32 7 0.000004136959156912223 +armv7neon_mmm_f32_8x4_generic 17 4 9 0.000005894535633035915 +generic_f32_4x4 9 4 13 0.00000752174902729491 +armv7neon_mmm_f32_8x6_cortexa9 7 32 5 0.000002364950726746234 +generic_f32_4x4 13 32 4 0.000005521387429490463 +armv7neon_mmm_f32_8x4_generic 8 128 3 0.000004474358471860054 +armv7neon_mmm_f32_8x4_cortexa7 23 4 12 0.0000063640165444956975 +armv7neon_mmm_f32_8x4_generic 7 32 4 0.0000019290673256470733 +armv7neon_mmm_f32_8x4_generic 17 32 12 0.000012180617018748219 +armv7neon_mmm_f32_8x6_generic 17 4 11 0.000004899442085076861 +armv7neon_mmm_f32_8x4_cortexa7 24 128 7 0.000032404931744711066 +armv7neon_mmm_f32_8x4_cortexa9 16 32 12 0.000008099332243893017 +armv7neon_mmm_f32_8x6_generic 9 128 18 0.00003081307981321012 +armv7neon_mmm_f32_8x6_generic 15 128 11 0.00002132277281707897 +armv7neon_mmm_f32_8x6_cortexa7 7 128 6 0.000007540283032400861 +armv7neon_mmm_f32_8x6_generic 25 32 12 0.000013597212519317418 +armv7neon_mmm_f32_8x6_generic 23 4 17 0.000007516814516170969 +armv7neon_mmm_f32_8x6_cortexa7 24 32 11 0.000013420883457543118 +generic_f32_4x4 9 32 11 0.000012087233281631852 +armv7neon_mmm_f32_8x6_cortexa7 24 4 17 0.000007204721001644005 +armv7neon_mmm_f32_8x6_cortexa7 25 32 7 0.000017455571757923716 +armv7neon_mmm_f32_8x4_cortexa9 23 32 11 0.000012992255106920675 +armv7neon_mmm_f32_8x4_cortexa9 9 4 9 0.000004090278120441601 +armv7neon_mmm_f32_8x4_cortexa7 23 32 13 0.000021193261455653347 +armv7neon_mmm_f32_8x6_cortexa7 24 32 19 0.00002532279550192562 +armv7neon_mmm_f32_8x4_generic 25 128 11 0.00004692401023814237 +armv7neon_mmm_f32_8x4_cortexa9 17 32 7 0.000008780640229470475 +armv7neon_mmm_f32_8x4_cortexa7 9 128 9 0.00003233195046796576 +armv7neon_mmm_f32_8x4_cortexa7 23 128 5 0.000032464808470770534 +armv7neon_mmm_f32_8x4_cortexa9 9 4 5 0.00000293936322263601 +armv7neon_mmm_f32_8x6_cortexa7 25 32 6 0.000008732005752739124 +armv7neon_mmm_f32_8x6_cortexa9 8 4 5 0.0000013801093771363888 +armv7neon_mmm_f32_8x6_cortexa7 16 128 6 0.000013837384210811002 +armv7neon_mmm_f32_8x4_generic 23 32 8 0.000008530859801735756 +armv7neon_mmm_f32_8x4_cortexa7 17 4 8 0.000004162931637169246 +armv7neon_mmm_f32_8x6_generic 7 32 13 0.000006254936643865642 +armv7neon_mmm_f32_8x6_cortexa7 9 32 7 0.00000901443584736606 +armv7neon_mmm_f32_8x4_cortexa7 15 4 7 0.0000034347738366310684 +generic_f32_4x4 9 32 13 0.000015744382160735945 +armv7neon_mmm_f32_8x6_generic 9 32 18 0.000010478111403829218 +armv7neon_mmm_f32_8x6_cortexa9 23 128 11 0.00003166377905937062 +armv7neon_mmm_f32_8x4_generic 15 128 7 0.000016210803535229283 +armv7neon_mmm_f32_8x4_cortexa7 23 4 9 0.000006649300631510142 +armv7neon_mmm_f32_8x4_generic 17 32 3 0.000004810545788546653 +armv7neon_mmm_f32_8x6_generic 24 32 12 0.000010144077161084113 +armv7neon_mmm_f32_8x4_cortexa9 15 32 7 0.000006152877670399033 +armv7neon_mmm_f32_8x4_cortexa9 23 4 12 0.000005900735151833346 +armv7neon_mmm_f32_8x4_cortexa7 16 32 4 0.0000037548262311678015 +armv7neon_mmm_f32_8x6_cortexa7 23 4 18 0.000007713970848848978 +armv7neon_mmm_f32_8x6_cortexa7 16 32 6 0.000004565838055826264 +armv7neon_mmm_f32_8x4_cortexa9 25 32 5 0.000011311149331127226 +armv7neon_mmm_f32_8x6_cortexa9 16 128 18 0.00003053013458613619 +armv7neon_mmm_f32_8x6_cortexa7 15 32 17 0.000013972664044561828 +armv7neon_mmm_f32_8x4_generic 16 32 4 0.000003010794297325924 +armv7neon_mmm_f32_8x6_generic 7 128 7 0.000011021195155003848 +generic_f32_4x4 4 32 13 0.000005510533372512351 +armv7neon_mmm_f32_8x4_cortexa9 16 32 7 0.000005969619454408248 +armv7neon_mmm_f32_8x4_cortexa9 24 32 12 0.000011900133300984794 +armv7neon_mmm_f32_8x4_cortexa7 8 128 3 0.000005902896580930324 +armv7neon_mmm_f32_8x4_generic 16 32 3 0.000003439802295093395 +armv7neon_mmm_f32_8x4_cortexa7 17 32 4 0.000005503311716430611 +armv7neon_mmm_f32_8x6_generic 9 32 12 0.000007171024246490819 +armv7neon_mmm_f32_8x6_cortexa7 23 32 5 0.000007315917802939159 +armv7neon_mmm_f32_8x6_cortexa7 23 128 12 0.0000413645417403771 +armv7neon_mmm_f32_8x4_generic 16 32 7 0.000005981518355563633 +generic_f32_4x4 7 4 7 0.000003097228954014147 +armv7neon_mmm_f32_8x6_cortexa7 24 4 18 0.000006499393673193748 +armv7neon_mmm_f32_8x6_cortexa9 24 4 7 0.000004723972074276078 +armv7neon_mmm_f32_8x4_cortexa7 16 32 11 0.00001075379201180745 +generic_f32_4x4 5 32 8 0.000005591832396338314 +armv7neon_mmm_f32_8x6_cortexa7 7 4 17 0.000003519015871119777 +armv7neon_mmm_f32_8x6_generic 25 4 13 0.000008668362849414556 +armv7neon_mmm_f32_8x4_cortexa7 15 128 4 0.000011117502498464982 +armv7neon_mmm_f32_8x6_cortexa7 15 4 17 0.000005776336577667951 +armv7neon_mmm_f32_8x4_cortexa9 8 32 13 0.000005695220879131919 +armv7neon_mmm_f32_8x4_cortexa7 24 4 12 0.000005659072390762546 +armv7neon_mmm_f32_8x4_generic 23 128 4 0.00001202844464043909 +armv7neon_mmm_f32_8x6_generic 17 128 7 0.00003102141310178231 +armv7neon_mmm_f32_8x4_generic 16 128 11 0.00002360551094138009 +armv7neon_mmm_f32_8x4_cortexa7 25 128 9 0.00006388979998675838 +armv7neon_mmm_f32_8x4_generic 9 32 9 0.00000858128920884699 +armv7neon_mmm_f32_8x6_cortexa7 9 4 12 0.0000034111339123867222 +armv7neon_mmm_f32_8x6_cortexa9 17 128 11 0.00003127371093460866 +armv7neon_mmm_f32_8x4_cortexa9 15 128 11 0.00002401650595136234 +armv7neon_mmm_f32_8x6_cortexa9 23 128 12 0.00003137052216487605 +armv7neon_mmm_f32_8x6_cortexa9 16 128 17 0.00003103196066568324 +generic_f32_4x4 7 32 3 0.000003268544296254094 +armv7neon_mmm_f32_8x4_generic 9 4 8 0.0000027781499181131053 +armv7neon_mmm_f32_8x4_cortexa7 16 32 9 0.000010640273784126178 +armv7neon_mmm_f32_8x4_cortexa9 25 4 9 0.00000761410675406794 +generic_f32_4x4 3 4 12 0.00000255471395132772 +generic_f32_4x4 12 32 13 0.00001543431169651167 +armv7neon_mmm_f32_8x6_cortexa9 15 4 7 0.0000037646981265010737 +armv7neon_mmm_f32_8x4_cortexa7 7 4 9 0.0000027630250787792764 +armv7neon_mmm_f32_8x6_cortexa9 15 4 13 0.000005407861539689391 +armv7neon_mmm_f32_8x4_cortexa7 24 4 5 0.000004483592435521624 +armv7neon_mmm_f32_8x6_cortexa9 16 128 5 0.000010995701297253243 +armv7neon_mmm_f32_8x4_cortexa9 7 4 13 0.000003344708907386396 +generic_f32_4x4 3 4 4 0.0000012029323677452384 +armv7neon_mmm_f32_8x6_cortexa7 8 4 5 0.0000014134340225429588 +armv7neon_mmm_f32_8x4_cortexa7 16 4 5 0.000003166142335812665 +armv7neon_mmm_f32_8x6_cortexa9 15 4 17 0.000005534126802188086 +armv7neon_mmm_f32_8x4_generic 17 128 4 0.000011910311848497855 +armv7neon_mmm_f32_8x4_cortexa9 17 128 9 0.00003520115855849476 +armv7neon_mmm_f32_8x6_cortexa7 24 128 17 0.00006131644295051564 +armv7neon_mmm_f32_8x4_generic 8 32 12 0.000004263261337563466 +armv7neon_mmm_f32_8x4_generic 15 128 13 0.000031724034290535855 +generic_f32_4x4 13 128 4 0.000014842273639272524 +armv7neon_mmm_f32_8x6_generic 16 128 17 0.00003115527449889593 +armv7neon_mmm_f32_8x4_cortexa7 25 128 13 0.00008540107215655228 +armv7neon_mmm_f32_8x4_cortexa9 17 4 8 0.000003867693626667699 +armv7neon_mmm_f32_8x6_generic 9 4 6 0.0000018447023821962963 +armv7neon_mmm_f32_8x4_generic 15 4 5 0.0000031342530980756114 +armv7neon_mmm_f32_8x4_cortexa9 25 32 11 0.000016711914928453156 +armv7neon_mmm_f32_8x4_cortexa9 9 4 7 0.0000029971874356879734 +armv7neon_mmm_f32_8x4_cortexa9 17 32 8 0.000008299083390781581 +armv7neon_mmm_f32_8x6_generic 9 128 11 0.000020922916297729766 +armv7neon_mmm_f32_8x6_cortexa9 25 4 11 0.000006455717226984232 +generic_f32_4x4 9 128 13 0.00004380056172414816 +armv7neon_mmm_f32_8x4_generic 9 4 3 0.0000018459881520629983 +generic_f32_4x4 13 32 13 0.000020718460422326147 +generic_f32_4x4 11 128 11 0.00003310679264480642 +armv7neon_mmm_f32_8x4_cortexa7 17 128 12 0.00004762075330394028 +armv7neon_mmm_f32_8x6_cortexa9 25 4 6 0.0000031406906098450826 +armv7neon_mmm_f32_8x4_cortexa7 15 4 3 0.0000020595941028725178 +generic_f32_4x4 4 32 5 0.000003094039509493947 +armv7neon_mmm_f32_8x4_cortexa7 17 4 13 0.000008167970707514618 +armv7neon_mmm_f32_8x6_cortexa9 16 128 7 0.000020852896757305592 +armv7neon_mmm_f32_8x4_generic 24 32 12 0.000011875110028324704 +armv7neon_mmm_f32_8x6_cortexa7 23 32 6 0.0000070036066823672265 +armv7neon_mmm_f32_8x4_generic 24 32 9 0.000012379847822938195 +armv7neon_mmm_f32_8x6_generic 16 32 7 0.000007288146983219525 +armv7neon_mmm_f32_8x4_generic 8 32 4 0.0000017213389522499293 +armv7neon_mmm_f32_8x6_cortexa7 25 32 12 0.00001698633380929768 +armv7neon_mmm_f32_8x4_generic 25 32 8 0.000010843062239811664 +armv7neon_mmm_f32_8x6_generic 16 128 5 0.000010970239776322642 +armv7neon_mmm_f32_8x6_cortexa9 23 32 12 0.00001105222841817834 +armv7neon_mmm_f32_8x4_cortexa7 7 4 12 0.0000028259675178908435 +armv7neon_mmm_f32_8x4_generic 17 32 13 0.000016456626258678596 +armv7neon_mmm_f32_8x6_generic 23 32 19 0.000021489093420466062 +armv7neon_mmm_f32_8x6_cortexa9 9 32 7 0.000007388528782309879 +armv7neon_mmm_f32_8x6_generic 16 128 13 0.0000308218952023933 +armv7neon_mmm_f32_8x4_cortexa9 24 128 4 0.000011799248370378306 +armv7neon_mmm_f32_8x6_cortexa7 15 4 5 0.000002311967551514126 +armv7neon_mmm_f32_8x6_cortexa9 15 32 19 0.000015002244448339703 +armv7neon_mmm_f32_8x4_cortexa7 25 32 4 0.000007113231524121585 +armv7neon_mmm_f32_8x4_generic 9 4 9 0.0000040996991473354 +generic_f32_4x4 11 4 11 0.00000604219727392619 +armv7neon_mmm_f32_8x4_generic 23 128 12 0.0000351784079527385 +armv7neon_mmm_f32_8x4_generic 8 32 8 0.0000029996111096440974 +generic_f32_4x4 3 128 11 0.000011575521961547323 +armv7neon_mmm_f32_8x6_generic 17 4 19 0.000008636925572322338 +armv7neon_mmm_f32_8x6_cortexa7 17 32 17 0.000019651252774271738 +armv7neon_mmm_f32_8x4_cortexa7 25 32 5 0.000014279031192163009 +armv7neon_mmm_f32_8x6_cortexa9 7 128 5 0.000005734808712355398 +armv7neon_mmm_f32_8x4_cortexa9 15 4 5 0.0000031170038028798296 +armv7neon_mmm_f32_8x4_cortexa9 24 128 13 0.000046424963618177915 +armv7neon_mmm_f32_8x6_generic 16 4 5 0.0000022248730615242665 +generic_f32_4x4 8 128 9 0.0000220091711298822 +armv7neon_mmm_f32_8x6_cortexa9 24 32 17 0.000015790292495872178 +armv7neon_mmm_f32_8x6_cortexa7 25 4 12 0.000006064814099731578 +armv7neon_mmm_f32_8x4_generic 9 32 3 0.0000033141535571016215 +armv7neon_mmm_f32_8x4_cortexa7 24 32 7 0.000010955777874109968 +armv7neon_mmm_f32_8x6_generic 7 32 19 0.000008240412026502634 +armv7neon_mmm_f32_8x6_generic 15 128 12 0.000021273134898855332 +armv7neon_mmm_f32_8x4_generic 9 128 12 0.000023427967692188372 +generic_f32_4x4 9 128 4 0.000011233611698379809 +armv7neon_mmm_f32_8x4_generic 23 32 3 0.000004898603928972602 +armv7neon_mmm_f32_8x4_cortexa9 15 4 7 0.000003196236363013327 +generic_f32_4x4 13 128 9 0.000043631376927673965 +armv7neon_mmm_f32_8x6_cortexa7 8 32 13 0.000006769099621081802 +armv7neon_mmm_f32_8x6_cortexa9 16 32 11 0.000007489162129911177 +armv7neon_mmm_f32_8x6_cortexa7 15 4 11 0.000004047240611447739 +armv7neon_mmm_f32_8x6_cortexa9 8 128 12 0.000010521407486118232 +armv7neon_mmm_f32_8x6_generic 24 128 12 0.000030475350289631447 +armv7neon_mmm_f32_8x6_cortexa9 8 4 18 0.0000023963651215663894 +armv7neon_mmm_f32_8x4_generic 7 4 13 0.0000033498033347340617 +armv7neon_mmm_f32_8x6_cortexa7 24 128 5 0.000021225542006705262 +armv7neon_mmm_f32_8x6_generic 8 128 7 0.000010658035873946595 +armv7neon_mmm_f32_8x6_cortexa9 16 128 13 0.00003086535920353521 +armv7neon_mmm_f32_8x6_cortexa7 9 4 13 0.000005015620872516637 +armv7neon_mmm_f32_8x6_cortexa9 8 4 13 0.0000025412882105309308 +armv7neon_mmm_f32_8x4_cortexa9 24 32 11 0.000012564889907233448 +armv7neon_mmm_f32_8x4_generic 7 128 13 0.000016356824884577277 +armv7neon_mmm_f32_8x4_cortexa7 15 32 11 0.00001113088620100476 +armv7neon_mmm_f32_8x6_generic 23 32 18 0.000016159850846463288 +armv7neon_mmm_f32_8x6_generic 15 32 19 0.000014889331973563625 +armv7neon_mmm_f32_8x4_generic 7 128 11 0.000012426167611771446 +generic_f32_4x4 4 4 4 0.000001047996290990507 +armv7neon_mmm_f32_8x4_generic 23 4 9 0.000006177048756338135 +armv7neon_mmm_f32_8x6_cortexa9 16 4 11 0.0000034974057138003183 +armv7neon_mmm_f32_8x6_cortexa9 7 128 12 0.00001128966410991496 +armv7neon_mmm_f32_8x4_cortexa9 9 4 8 0.0000027723235031605195 +armv7neon_mmm_f32_8x4_generic 7 128 12 0.000012422711675746062 +armv7neon_mmm_f32_8x4_cortexa7 25 128 12 0.0000632498211882511 +armv7neon_mmm_f32_8x4_generic 7 128 5 0.000008380542348646574 +armv7neon_mmm_f32_8x4_cortexa9 17 4 3 0.000002593764492859534 +generic_f32_4x4 9 4 3 0.0000025396068006223777 +generic_f32_4x4 9 4 12 0.0000055571336153173335 +armv7neon_mmm_f32_8x4_cortexa7 16 128 8 0.000021301840263398342 +armv7neon_mmm_f32_8x4_generic 9 32 12 0.000008360119586696751 +armv7neon_mmm_f32_8x6_cortexa9 23 32 13 0.00001642300983789001 +armv7neon_mmm_f32_8x4_cortexa7 9 32 13 0.000014115360584179656 +armv7neon_mmm_f32_8x4_generic 7 4 4 0.0000011988761531825325 +generic_f32_4x4 4 32 12 0.000004173516705669169 +armv7neon_mmm_f32_8x6_cortexa7 8 4 17 0.000002748952290655516 +armv7neon_mmm_f32_8x6_cortexa9 15 128 17 0.000031856750398890617 +armv7neon_mmm_f32_8x4_cortexa9 17 128 4 0.00001190476277599742 +armv7neon_mmm_f32_8x6_cortexa7 23 4 17 0.000008057629471932516 +armv7neon_mmm_f32_8x4_cortexa7 24 128 9 0.00004787648599530554 +armv7neon_mmm_f32_8x4_cortexa7 23 4 5 0.000004689549145067684 +armv7neon_mmm_f32_8x6_cortexa9 17 4 18 0.000006445994468840472 +armv7neon_mmm_f32_8x6_generic 9 128 17 0.00003107927056434744 +armv7neon_mmm_f32_8x4_generic 24 128 8 0.000023288445002113136 +armv7neon_mmm_f32_8x6_cortexa9 16 32 17 0.000010725045258389392 +armv7neon_mmm_f32_8x4_cortexa7 17 128 11 0.00004814359255643903 +armv7neon_mmm_f32_8x4_generic 7 4 3 0.0000011794687664451176 +generic_f32_4x4 7 32 5 0.000005808524626140803 +armv7neon_mmm_f32_8x6_generic 24 32 11 0.000010897102444398395 +armv7neon_mmm_f32_8x4_generic 7 4 7 0.0000019250466424784192 +armv7neon_mmm_f32_8x4_cortexa9 7 32 13 0.000006310599463529341 +generic_f32_4x4 4 32 8 0.000002964574479014875 +armv7neon_mmm_f32_8x6_cortexa7 17 4 12 0.000004769238750897463 +armv7neon_mmm_f32_8x6_generic 8 32 17 0.000005580365730578937 +armv7neon_mmm_f32_8x4_cortexa9 7 4 5 0.0000018767262951495324 +armv7neon_mmm_f32_8x4_cortexa9 9 128 3 0.000008376722642527261 +generic_f32_4x4 8 32 8 0.000005427007518436356 +generic_f32_4x4 3 32 7 0.000003269319162898471 +armv7neon_mmm_f32_8x6_generic 23 128 11 0.000031615917676258355 +generic_f32_4x4 11 32 3 0.000004643343538119153 +armv7neon_mmm_f32_8x6_generic 17 4 13 0.000006685914546845586 +armv7neon_mmm_f32_8x4_cortexa9 7 128 13 0.000016356503104634956 +armv7neon_mmm_f32_8x6_generic 8 4 11 0.000001987955413940483 +armv7neon_mmm_f32_8x6_cortexa9 23 32 6 0.0000058061790469852035 +armv7neon_mmm_f32_8x6_cortexa9 17 128 12 0.00003080777419909108 +generic_f32_4x4 3 4 13 0.00000320269810797615 +armv7neon_mmm_f32_8x6_cortexa9 17 32 12 0.000010479324211722284 +armv7neon_mmm_f32_8x4_cortexa9 7 4 4 0.000001192052740790223 +armv7neon_mmm_f32_8x4_cortexa9 24 128 12 0.00003457434886740767 +armv7neon_mmm_f32_8x6_cortexa9 23 32 11 0.000011348364973457056 +armv7neon_mmm_f32_8x4_generic 16 4 12 0.000003656453112482303 +armv7neon_mmm_f32_8x6_cortexa9 16 128 6 0.000010542301986141948 +armv7neon_mmm_f32_8x4_generic 24 128 4 0.000011845830939195392 +armv7neon_mmm_f32_8x6_cortexa7 9 32 18 0.000013032529505137248 +armv7neon_mmm_f32_8x4_generic 24 128 12 0.00003455867526442596 +armv7neon_mmm_f32_8x4_generic 23 128 7 0.00002405539721722316 +armv7neon_mmm_f32_8x4_cortexa9 23 32 4 0.000004484967279906347 +armv7neon_mmm_f32_8x4_cortexa9 17 32 4 0.000004369095064954239 +armv7neon_mmm_f32_8x6_cortexa7 8 4 12 0.000001838051428755667 +armv7neon_mmm_f32_8x4_generic 15 4 8 0.000003030902158049641 +armv7neon_mmm_f32_8x4_cortexa7 8 128 9 0.000016361621231447548 +armv7neon_mmm_f32_8x6_generic 15 4 5 0.000002193007903880869 +armv7neon_mmm_f32_8x6_cortexa9 24 32 18 0.000015047680763476656 +armv7neon_mmm_f32_8x6_generic 25 128 6 0.00002065407414270236 +armv7neon_mmm_f32_8x4_generic 25 32 9 0.00001655596981137993 +armv7neon_mmm_f32_8x4_cortexa7 7 4 8 0.000002046321793305707 +armv7neon_mmm_f32_8x4_generic 24 32 5 0.000008563586932451436 +armv7neon_mmm_f32_8x6_cortexa7 25 32 19 0.00003389329143278319 +generic_f32_4x4 3 4 3 0.0000011908985597580253 +generic_f32_4x4 11 128 13 0.000043852326643092945 +armv7neon_mmm_f32_8x6_cortexa9 7 32 6 0.0000025216564013293366 +generic_f32_4x4 8 128 11 0.00002204318798460829 +generic_f32_4x4 9 32 5 0.00000828023901975494 +armv7neon_mmm_f32_8x6_generic 25 4 11 0.000006352168944400001 +armv7neon_mmm_f32_8x4_cortexa9 24 32 5 0.000008590022043400085 +armv7neon_mmm_f32_8x4_generic 8 32 3 0.0000019449019781287236 +armv7neon_mmm_f32_8x4_cortexa7 24 128 8 0.0000317811111212887 +armv7neon_mmm_f32_8x4_cortexa9 7 128 9 0.000012372639572013476 +armv7neon_mmm_f32_8x6_generic 15 128 19 0.00004250719000509339 +armv7neon_mmm_f32_8x6_cortexa7 17 4 7 0.000005084587454728339 +armv7neon_mmm_f32_8x6_generic 15 32 5 0.000004169790668084077 +generic_f32_4x4 9 128 3 0.000011555287825087273 +armv7neon_mmm_f32_8x4_cortexa7 25 128 7 0.00004317760438423037 +generic_f32_4x4 8 32 12 0.00000779706808746927 +armv7neon_mmm_f32_8x6_generic 8 32 12 0.000003719914023610946 +armv7neon_mmm_f32_8x6_cortexa7 15 128 17 0.000041839968426337905 +armv7neon_mmm_f32_8x6_cortexa7 8 128 13 0.000020713409071600263 +armv7neon_mmm_f32_8x6_cortexa9 24 4 5 0.000003104317520805454 +armv7neon_mmm_f32_8x6_cortexa9 9 4 13 0.000004778161797091213 +armv7neon_mmm_f32_8x4_cortexa9 25 128 12 0.00004644468928643539 +generic_f32_4x4 11 4 9 0.000005973042856611991 +armv7neon_mmm_f32_8x4_cortexa7 23 128 8 0.00003217939318511314 +armv7neon_mmm_f32_8x6_generic 24 4 11 0.00000490822983265492 +armv7neon_mmm_f32_8x4_generic 17 4 12 0.000005542606566473033 +armv7neon_mmm_f32_8x6_cortexa9 16 32 5 0.000004236072973244724 +armv7neon_mmm_f32_8x6_cortexa7 24 32 12 0.000012668891408140166 +armv7neon_mmm_f32_8x6_cortexa7 17 128 19 0.00008180995267293593 +armv7neon_mmm_f32_8x4_cortexa7 8 4 9 0.0000023929041278364927 +armv7neon_mmm_f32_8x4_generic 17 4 11 0.000005996779011118073 +armv7neon_mmm_f32_8x4_generic 23 32 5 0.000008783310902330428 +armv7neon_mmm_f32_8x4_cortexa9 24 4 5 0.000004126054215412369 +armv7neon_mmm_f32_8x4_cortexa7 15 4 4 0.0000018664924896343623 +generic_f32_4x4 12 32 12 0.000011419939741453591 +armv7neon_mmm_f32_8x6_cortexa9 9 128 19 0.00004140314991254094 +armv7neon_mmm_f32_8x6_cortexa7 24 4 19 0.000008976504261442944 +armv7neon_mmm_f32_8x6_cortexa7 23 32 12 0.000013517680029411935 +armv7neon_mmm_f32_8x6_generic 23 128 17 0.00004767212397281204 +armv7neon_mmm_f32_8x6_cortexa7 9 128 12 0.000027472854173113385 +generic_f32_4x4 13 4 4 0.0000027493566687604084 +armv7neon_mmm_f32_8x6_cortexa7 7 32 7 0.000005097398974457722 +armv7neon_mmm_f32_8x4_generic 25 4 3 0.0000033394241283747185 +armv7neon_mmm_f32_8x4_cortexa7 25 4 5 0.000005839267769079541 +generic_f32_4x4 8 4 9 0.0000039467461932377605 +armv7neon_mmm_f32_8x6_cortexa7 8 32 6 0.0000025355396885136015 +armv7neon_mmm_f32_8x4_cortexa7 7 4 13 0.0000035454436358925364 +generic_f32_4x4 12 128 5 0.000022119589677203316 +armv7neon_mmm_f32_8x4_cortexa9 17 32 5 0.000008619398425928128 +generic_f32_4x4 5 32 12 0.00000809048145029521 +armv7neon_mmm_f32_8x6_generic 7 32 12 0.000004497135112528872 +armv7neon_mmm_f32_8x6_cortexa9 17 128 18 0.00004612047264624732 +armv7neon_mmm_f32_8x4_cortexa9 24 128 9 0.00003503924267370847 +armv7neon_mmm_f32_8x6_cortexa9 25 128 18 0.00006126219444229572 +armv7neon_mmm_f32_8x4_cortexa9 7 32 7 0.0000034009841492082025 +armv7neon_mmm_f32_8x6_cortexa7 8 32 18 0.0000065795660386672954 +armv7neon_mmm_f32_8x4_cortexa7 9 4 7 0.0000032333021816508883 +generic_f32_4x4 11 4 5 0.000004260253310204167 +armv7neon_mmm_f32_8x4_cortexa7 15 32 12 0.00001091466040761069 +armv7neon_mmm_f32_8x6_generic 25 128 17 0.00006242813577501301 +armv7neon_mmm_f32_8x6_cortexa7 23 128 17 0.00006230757214971725 +armv7neon_mmm_f32_8x4_cortexa7 9 4 3 0.000001972675027149826 +armv7neon_mmm_f32_8x6_generic 9 32 19 0.000013969471313273173 +armv7neon_mmm_f32_8x4_cortexa7 9 4 4 0.0000017499654922061095 +generic_f32_4x4 8 4 8 0.0000026159902565290668 +armv7neon_mmm_f32_8x6_generic 23 128 12 0.000031313730389866355 +armv7neon_mmm_f32_8x4_cortexa7 15 128 7 0.000021942752305858642 +armv7neon_mmm_f32_8x6_cortexa7 9 32 6 0.000004675388926372666 +armv7neon_mmm_f32_8x6_cortexa7 8 32 5 0.000002772093015202498 +armv7neon_mmm_f32_8x6_cortexa7 9 32 19 0.000017344416985284313 +armv7neon_mmm_f32_8x4_cortexa9 8 128 7 0.000008229649908268804 +generic_f32_4x4 3 128 12 0.000011565133509989783 +generic_f32_4x4 9 32 12 0.000011785850787897203 +armv7neon_mmm_f32_8x6_cortexa9 17 128 13 0.00004651964090971228 +armv7neon_mmm_f32_8x6_cortexa9 25 32 6 0.0000071127343061349165 +armv7neon_mmm_f32_8x6_cortexa7 25 128 13 0.00008178262598187339 +armv7neon_mmm_f32_8x4_cortexa9 25 32 13 0.000021697932623977668 +armv7neon_mmm_f32_8x4_cortexa9 16 4 11 0.0000040619610195960516 +armv7neon_mmm_f32_8x4_generic 23 128 3 0.000012468771697124281 +generic_f32_4x4 11 32 13 0.000015931672245334774 +generic_f32_4x4 8 32 9 0.000008097203894273956 +armv7neon_mmm_f32_8x4_cortexa9 17 4 5 0.000004171816997731455 +armv7neon_mmm_f32_8x6_generic 15 32 17 0.000011434650801767795 +armv7neon_mmm_f32_8x4_cortexa7 25 128 3 0.000022089826731240803 +armv7neon_mmm_f32_8x4_cortexa9 17 32 9 0.000012562108538363274 +generic_f32_4x4 7 128 4 0.000007740927731735862 +armv7neon_mmm_f32_8x6_cortexa7 16 128 17 0.00004105266320070572 +armv7neon_mmm_f32_8x6_generic 17 128 5 0.000016103121774230763 +armv7neon_mmm_f32_8x6_cortexa9 25 128 19 0.0000820798385767341 +armv7neon_mmm_f32_8x4_generic 15 32 12 0.000008721032730753763 +armv7neon_mmm_f32_8x4_generic 25 4 9 0.000007623970700084407 +armv7neon_mmm_f32_8x4_generic 17 32 11 0.000012672883096785458 +armv7neon_mmm_f32_8x6_generic 16 32 19 0.000013691087754459915 +armv7neon_mmm_f32_8x6_generic 9 4 12 0.0000031605677124646883 +generic_f32_4x4 5 4 5 0.0000029759889706969683 +armv7neon_mmm_f32_8x4_generic 16 32 9 0.00000841650213766426 +generic_f32_4x4 8 32 13 0.000010487684258287977 +generic_f32_4x4 4 128 5 0.000007733485289678424 +armv7neon_mmm_f32_8x6_cortexa7 8 4 19 0.0000033370479940342483 +armv7neon_mmm_f32_8x6_cortexa7 16 4 13 0.000004820409587660123 +armv7neon_mmm_f32_8x6_cortexa7 7 128 5 0.000007382676538540774 +armv7neon_mmm_f32_8x6_cortexa9 9 4 7 0.0000034194906166596 +armv7neon_mmm_f32_8x4_generic 24 128 3 0.000012489994751068132 +armv7neon_mmm_f32_8x6_generic 17 4 5 0.0000029369592565703416 +armv7neon_mmm_f32_8x6_cortexa9 7 32 7 0.000004299364469367014 +generic_f32_4x4 8 128 8 0.000014675074170610452 +armv7neon_mmm_f32_8x6_generic 24 32 6 0.000005321602550624656 +generic_f32_4x4 8 128 3 0.000007920826245860217 +armv7neon_mmm_f32_8x6_cortexa7 9 128 17 0.000041149683356221505 +armv7neon_mmm_f32_8x6_generic 15 128 18 0.0000317697933254728 +armv7neon_mmm_f32_8x4_generic 15 32 11 0.000008972113808644295 +armv7neon_mmm_f32_8x6_generic 17 128 11 0.00003128672479118261 +armv7neon_mmm_f32_8x6_generic 16 32 5 0.000004210171223198828 +armv7neon_mmm_f32_8x4_cortexa9 23 32 7 0.000008919434958029703 +armv7neon_mmm_f32_8x6_cortexa7 23 4 11 0.000005630918362501218 +generic_f32_4x4 13 4 13 0.000009734041681688549 +armv7neon_mmm_f32_8x4_cortexa7 23 4 8 0.000004419109488183114 +armv7neon_mmm_f32_8x6_generic 17 128 18 0.00004641763657018708 +armv7neon_mmm_f32_8x6_cortexa7 23 32 7 0.000013604910503058097 +armv7neon_mmm_f32_8x4_cortexa9 16 4 8 0.0000025811126658507737 +armv7neon_mmm_f32_8x6_generic 16 4 17 0.000004664791456959476 +armv7neon_mmm_f32_8x6_cortexa9 15 128 19 0.00004234538403110192 +armv7neon_mmm_f32_8x4_cortexa7 7 128 8 0.000011300292613931975 +generic_f32_4x4 4 32 7 0.000003116445195774716 +armv7neon_mmm_f32_8x4_generic 17 128 3 0.000012383648487655847 +armv7neon_mmm_f32_8x6_cortexa9 24 32 12 0.00001022987009394415 +armv7neon_mmm_f32_8x4_cortexa9 17 32 11 0.000012684638765009217 +armv7neon_mmm_f32_8x6_cortexa9 9 128 18 0.000030893903863542745 +armv7neon_mmm_f32_8x6_cortexa7 9 4 18 0.000004852117278755586 +armv7neon_mmm_f32_8x6_cortexa9 8 128 13 0.00001571685239499597 +armv7neon_mmm_f32_8x6_cortexa9 23 4 7 0.000005166723329090456 +armv7neon_mmm_f32_8x6_cortexa9 7 128 18 0.000016699359662800567 +armv7neon_mmm_f32_8x4_generic 17 32 4 0.0000043747418052874395 +armv7neon_mmm_f32_8x4_cortexa9 25 128 13 0.00006245169757364539 +armv7neon_mmm_f32_8x4_generic 24 128 11 0.00003520805509267024 +armv7neon_mmm_f32_8x4_generic 7 4 8 0.000001940897025671589 +armv7neon_mmm_f32_8x4_cortexa9 24 32 8 0.000008105445830543554 +generic_f32_4x4 3 32 5 0.000003239638778592903 +armv7neon_mmm_f32_8x6_generic 16 128 6 0.000010481759011880018 +armv7neon_mmm_f32_8x6_cortexa9 23 4 11 0.000005380241199288215 +armv7neon_mmm_f32_8x6_cortexa7 24 4 11 0.000005233250260928121 +armv7neon_mmm_f32_8x6_cortexa9 23 4 19 0.000009671598448323651 +armv7neon_mmm_f32_8x6_cortexa7 23 4 6 0.0000029172933842802905 +armv7neon_mmm_f32_8x4_cortexa9 23 32 8 0.000008528926728389173 +armv7neon_mmm_f32_8x4_cortexa9 24 128 8 0.000023207480788900347 +generic_f32_4x4 7 32 11 0.00000844108827118094 +generic_f32_4x4 12 4 5 0.000004095794522926841 +armv7neon_mmm_f32_8x4_generic 25 4 11 0.000007781652426587433 +armv7neon_mmm_f32_8x4_generic 16 32 12 0.000008090083939378293 +armv7neon_mmm_f32_8x4_generic 9 128 7 0.000016030853255705617 +armv7neon_mmm_f32_8x6_generic 15 4 19 0.000006928708092302355 +armv7neon_mmm_f32_8x6_cortexa7 24 128 12 0.00004054881877870741 +generic_f32_4x4 8 4 4 0.0000015677362682744682 +armv7neon_mmm_f32_8x6_generic 7 128 19 0.000021822554568277356 +armv7neon_mmm_f32_8x6_cortexa9 24 4 12 0.00000423613681186459 +armv7neon_mmm_f32_8x4_cortexa7 16 4 4 0.0000016584422934049926 +armv7neon_mmm_f32_8x6_cortexa7 15 4 12 0.0000039842977137122266 +armv7neon_mmm_f32_8x4_cortexa7 7 128 4 0.000005876303683712701 +armv7neon_mmm_f32_8x6_cortexa9 7 4 5 0.000001360389294622995 +armv7neon_mmm_f32_8x4_cortexa7 24 4 9 0.000006182082152017003 +armv7neon_mmm_f32_8x6_generic 7 128 17 0.000016508734849042377 +armv7neon_mmm_f32_8x4_generic 17 128 9 0.000035220470083517575 +generic_f32_4x4 11 32 4 0.000004333160180811584 +armv7neon_mmm_f32_8x6_cortexa7 7 32 12 0.000005349730883357719 +armv7neon_mmm_f32_8x4_generic 8 32 5 0.0000031582408088385206 +armv7neon_mmm_f32_8x6_cortexa9 8 32 6 0.000002149345359409538 +generic_f32_4x4 5 32 13 0.000010767359583748404 +armv7neon_mmm_f32_8x4_generic 17 32 5 0.000008612220700720838 +generic_f32_4x4 5 4 4 0.0000016841544626715604 +armv7neon_mmm_f32_8x4_cortexa9 9 32 8 0.000005740276944412148 +armv7neon_mmm_f32_8x6_cortexa9 23 128 7 0.00003146196425036688 +armv7neon_mmm_f32_8x4_cortexa9 25 128 9 0.00004693016970364154 +armv7neon_mmm_f32_8x4_generic 24 4 8 0.0000036269745603468488 +armv7neon_mmm_f32_8x6_cortexa9 24 32 5 0.000006081990180966961 +armv7neon_mmm_f32_8x6_generic 7 4 6 0.000001511590523783866 +generic_f32_4x4 13 128 7 0.00002944642529063838 +armv7neon_mmm_f32_8x4_cortexa9 8 4 11 0.000002254110071814015 +armv7neon_mmm_f32_8x6_cortexa9 24 128 19 0.00006153515122537746 +armv7neon_mmm_f32_8x4_cortexa9 16 128 13 0.00003108122366900544 +generic_f32_4x4 11 128 9 0.000033046521573243154 +armv7neon_mmm_f32_8x4_generic 7 4 9 0.0000026137777410885136 +armv7neon_mmm_f32_8x6_cortexa7 7 4 13 0.0000034217472919863715 +armv7neon_mmm_f32_8x6_generic 16 4 11 0.000003453597254301285 +armv7neon_mmm_f32_8x4_cortexa7 17 32 11 0.000015933978694713537 +armv7neon_mmm_f32_8x6_cortexa7 23 4 12 0.0000053181832633687915 +armv7neon_mmm_f32_8x6_generic 17 32 6 0.000005447406276972202 +armv7neon_mmm_f32_8x4_cortexa7 7 32 4 0.000002302756052490624 +armv7neon_mmm_f32_8x4_cortexa9 15 4 13 0.000005662932902369066 +armv7neon_mmm_f32_8x4_cortexa7 17 32 8 0.000010470053145450968 +armv7neon_mmm_f32_8x4_generic 17 4 8 0.000003846416592921501 +armv7neon_mmm_f32_8x4_cortexa7 25 32 7 0.000014416885420743293 +armv7neon_mmm_f32_8x6_cortexa9 7 4 7 0.000002316979424271203 +armv7neon_mmm_f32_8x6_cortexa9 16 4 17 0.000004718807435203576 +generic_f32_4x4 11 32 12 0.000011882704283190954 +armv7neon_mmm_f32_8x4_cortexa7 23 32 9 0.000016122379106643026 +armv7neon_mmm_f32_8x6_cortexa7 9 32 11 0.000009102642142770847 +armv7neon_mmm_f32_8x6_generic 25 4 7 0.000006116994655414601 +armv7neon_mmm_f32_8x4_generic 23 4 3 0.000002685832196401119 +armv7neon_mmm_f32_8x6_cortexa9 25 32 11 0.000014409038834226244 +armv7neon_mmm_f32_8x6_generic 23 32 12 0.000010966793413222081 +armv7neon_mmm_f32_8x4_cortexa7 15 4 8 0.0000032350546103453447 +armv7neon_mmm_f32_8x6_cortexa7 15 128 7 0.000027943938585492828 +generic_f32_4x4 7 4 4 0.0000017389973856077342 +armv7neon_mmm_f32_8x6_generic 8 4 12 0.0000017181145762324274 +armv7neon_mmm_f32_8x4_cortexa9 15 4 11 0.000004502163536054048 +armv7neon_mmm_f32_8x6_generic 15 128 6 0.000010890629782885533 +armv7neon_mmm_f32_8x4_cortexa7 23 128 12 0.00004796960186341638 +armv7neon_mmm_f32_8x6_generic 8 32 7 0.000003896823245256928 +armv7neon_mmm_f32_8x6_cortexa9 15 128 7 0.000021306419489905895 +armv7neon_mmm_f32_8x6_cortexa7 7 4 11 0.0000024493622993257104 +armv7neon_mmm_f32_8x6_cortexa7 7 32 19 0.000009918011828071643 +armv7neon_mmm_f32_8x4_cortexa9 24 4 7 0.000004277922454765341 +armv7neon_mmm_f32_8x4_cortexa7 9 4 5 0.0000031825583861368363 +armv7neon_mmm_f32_8x6_cortexa9 8 32 11 0.000004007622996506884 +armv7neon_mmm_f32_8x6_generic 15 4 11 0.0000038091887644203395 +armv7neon_mmm_f32_8x6_cortexa7 23 128 11 0.000041674027523638683 +armv7neon_mmm_f32_8x6_cortexa7 17 4 18 0.0000068571632794369916 +armv7neon_mmm_f32_8x6_cortexa9 9 128 12 0.000020793132831426426 +armv7neon_mmm_f32_8x6_generic 25 32 19 0.000027146627303420713 +armv7neon_mmm_f32_8x6_cortexa7 9 128 19 0.00005467645413703322 +armv7neon_mmm_f32_8x6_cortexa7 8 32 11 0.0000048195055146364755 +armv7neon_mmm_f32_8x4_cortexa7 8 128 5 0.000011083761554457595 +generic_f32_4x4 7 128 12 0.000022151037088381854 +generic_f32_4x4 9 128 11 0.00003295464355640065 +armv7neon_mmm_f32_8x6_cortexa9 25 4 17 0.000009038405830025483 +generic_f32_4x4 8 32 3 0.0000032862084964825317 +armv7neon_mmm_f32_8x4_cortexa9 23 128 12 0.00003536277613858418 +armv7neon_mmm_f32_8x6_cortexa9 16 128 11 0.000021144715462112054 +armv7neon_mmm_f32_8x6_generic 17 32 5 0.000005941189535355633 +armv7neon_mmm_f32_8x6_generic 15 32 13 0.000011297818131567483 +armv7neon_mmm_f32_8x6_generic 16 32 11 0.000007448947403712638 +generic_f32_4x4 5 4 7 0.0000029999149449467176 +armv7neon_mmm_f32_8x4_generic 8 32 13 0.000005690794573889263 +armv7neon_mmm_f32_8x4_cortexa9 23 128 4 0.000012046555463702128 +armv7neon_mmm_f32_8x4_cortexa7 25 128 5 0.00004305932269978789 +armv7neon_mmm_f32_8x6_cortexa7 24 32 7 0.000013181202441221999 +armv7neon_mmm_f32_8x4_cortexa9 23 4 3 0.0000026820954481134975 +generic_f32_4x4 12 4 8 0.0000036441773649361156 +armv7neon_mmm_f32_8x6_cortexa9 24 128 17 0.000046509213530800746 +armv7neon_mmm_f32_8x6_cortexa9 8 4 6 0.0000011575433314376175 +generic_f32_4x4 3 32 4 0.0000018882099872634265 +armv7neon_mmm_f32_8x4_cortexa7 9 32 3 0.000004070589988484743 +armv7neon_mmm_f32_8x4_cortexa7 17 32 9 0.000015851096904703647 +generic_f32_4x4 13 32 12 0.00001535208709935954 +armv7neon_mmm_f32_8x6_cortexa9 25 4 19 0.000011387388616753858 +armv7neon_mmm_f32_8x6_cortexa9 23 128 5 0.00001624764366012842 +armv7neon_mmm_f32_8x4_cortexa9 9 128 12 0.00002346244933254337 +armv7neon_mmm_f32_8x4_cortexa9 25 4 12 0.000007150024568853224 +armv7neon_mmm_f32_8x6_cortexa9 15 32 12 0.000007825900269054534 +armv7neon_mmm_f32_8x4_cortexa7 25 4 12 0.000007707673254391108 +generic_f32_4x4 11 32 5 0.000008363778534607299 +armv7neon_mmm_f32_8x4_cortexa7 17 128 8 0.00003194246552397465 +armv7neon_mmm_f32_8x6_generic 8 32 6 0.0000021066884422501677 +armv7neon_mmm_f32_8x6_cortexa7 16 4 11 0.0000036716657035155366 +armv7neon_mmm_f32_8x4_generic 17 128 7 0.00002384609528431003 +armv7neon_mmm_f32_8x4_cortexa7 9 128 7 0.000021759641210777887 +armv7neon_mmm_f32_8x6_cortexa7 17 32 11 0.000013412538846571892 +generic_f32_4x4 7 32 8 0.000005690920861795519 +armv7neon_mmm_f32_8x6_generic 25 128 19 0.00008258096370056387 +armv7neon_mmm_f32_8x6_cortexa7 15 4 7 0.000003922803920269034 +armv7neon_mmm_f32_8x4_cortexa9 16 4 12 0.0000036509568763084446 +generic_f32_4x4 11 128 4 0.000011287445429820831 +armv7neon_mmm_f32_8x6_cortexa7 15 128 5 0.000014299033646853434 +armv7neon_mmm_f32_8x6_cortexa9 24 128 12 0.00003053734816197179 +armv7neon_mmm_f32_8x6_cortexa9 17 32 6 0.000005510675534687666 +armv7neon_mmm_f32_8x6_cortexa9 9 32 19 0.000014080838768872731 +generic_f32_4x4 13 128 5 0.000029375850896877852 +armv7neon_mmm_f32_8x6_cortexa7 25 32 5 0.000009454087084930695 +armv7neon_mmm_f32_8x4_generic 8 4 8 0.0000015055905623190838 +armv7neon_mmm_f32_8x4_cortexa7 16 4 13 0.00000545158088053187 +armv7neon_mmm_f32_8x6_generic 8 128 6 0.0000054873008191504395 +generic_f32_4x4 5 128 7 0.000015018624971335435 +armv7neon_mmm_f32_8x6_generic 15 128 7 0.00002123110926361701 +armv7neon_mmm_f32_8x4_cortexa7 7 32 3 0.000002283721924125051 +armv7neon_mmm_f32_8x6_generic 9 128 19 0.00004153518147749415 +armv7neon_mmm_f32_8x4_generic 9 32 11 0.000008626343403146056 +armv7neon_mmm_f32_8x6_cortexa9 9 4 6 0.0000018884740207207647 +armv7neon_mmm_f32_8x6_cortexa7 9 128 6 0.000013948466701679792 +generic_f32_4x4 11 4 12 0.000005708922503580876 +armv7neon_mmm_f32_8x6_cortexa7 24 128 6 0.000020527418679269756 +armv7neon_mmm_f32_8x4_cortexa7 8 4 11 0.0000024406051533290663 +armv7neon_mmm_f32_8x4_cortexa9 23 128 9 0.000035493958882244616 +armv7neon_mmm_f32_8x6_cortexa9 17 4 11 0.00000498803165144454 +armv7neon_mmm_f32_8x6_generic 8 32 18 0.000005320856703844532 +armv7neon_mmm_f32_8x4_cortexa9 9 128 4 0.00000810350837903274 +generic_f32_4x4 9 4 8 0.000003892676050216947 +armv7neon_mmm_f32_8x6_cortexa9 24 128 5 0.000016255452753216072 +armv7neon_mmm_f32_8x6_generic 7 4 5 0.0000013389729556607902 +armv7neon_mmm_f32_8x6_generic 17 32 19 0.000020571678410473347 +armv7neon_mmm_f32_8x6_cortexa9 7 32 13 0.0000063039500398145465 +armv7neon_mmm_f32_8x4_cortexa9 17 128 7 0.000023826784148315835 +armv7neon_mmm_f32_8x4_generic 24 4 7 0.000004274685140650307 +armv7neon_mmm_f32_8x6_cortexa9 17 32 17 0.000015955807283560996 +armv7neon_mmm_f32_8x6_cortexa7 17 4 13 0.000007179865477053954 +armv7neon_mmm_f32_8x4_cortexa9 7 32 12 0.00000488792089076881 +generic_f32_4x4 4 128 11 0.000011283150201350866 +armv7neon_mmm_f32_8x6_cortexa7 7 128 17 0.00002152414507851334 +armv7neon_mmm_f32_8x4_generic 25 32 13 0.0000216921434601138 +armv7neon_mmm_f32_8x6_generic 15 32 12 0.000007763276203034865 +generic_f32_4x4 7 128 9 0.00002231642099570999 +armv7neon_mmm_f32_8x4_generic 23 4 7 0.0000044972515990527706 +armv7neon_mmm_f32_8x4_generic 16 128 9 0.00002352868039758383 +armv7neon_mmm_f32_8x4_generic 24 32 8 0.000008105856924464553 +armv7neon_mmm_f32_8x4_cortexa7 25 4 9 0.000008239024137829502 +armv7neon_mmm_f32_8x4_generic 8 4 7 0.000001726186960943308 +armv7neon_mmm_f32_8x6_cortexa7 24 4 12 0.000004476686880431161 +armv7neon_mmm_f32_8x4_generic 8 4 5 0.0000016761254405249265 +armv7neon_mmm_f32_8x6_cortexa9 17 32 18 0.000015401920673930987 +armv7neon_mmm_f32_8x6_cortexa7 8 4 18 0.000002497401986754502 +armv7neon_mmm_f32_8x6_cortexa7 23 32 18 0.000019976606065621178 +armv7neon_mmm_f32_8x4_cortexa9 16 128 5 0.000015920451629989893 +armv7neon_mmm_f32_8x6_cortexa7 16 128 19 0.00005438554195188942 +armv7neon_mmm_f32_8x4_generic 25 32 11 0.00001669591843012648 +armv7neon_mmm_f32_8x6_cortexa9 7 32 18 0.000006518795701369289 +generic_f32_4x4 3 128 7 0.000007891377185866899 +armv7neon_mmm_f32_8x6_cortexa9 9 128 11 0.000021029476636249923 +armv7neon_mmm_f32_8x4_generic 9 32 8 0.00000573930624839856 +armv7neon_mmm_f32_8x4_cortexa9 23 32 9 0.000012851357918328979 +armv7neon_mmm_f32_8x4_cortexa7 25 128 11 0.00006422947640130408 +armv7neon_mmm_f32_8x4_cortexa7 9 128 5 0.000021690553836350107 +armv7neon_mmm_f32_8x4_cortexa7 8 128 4 0.000005680428664405247 +armv7neon_mmm_f32_8x6_cortexa9 17 32 11 0.000010949048608533127 +armv7neon_mmm_f32_8x4_cortexa9 9 32 13 0.00001119516860361631 +armv7neon_mmm_f32_8x4_cortexa9 8 128 11 0.000012017424644185006 +armv7neon_mmm_f32_8x4_cortexa9 17 4 9 0.000005874110633384251 +armv7neon_mmm_f32_8x6_cortexa7 9 4 7 0.0000035812085152963736 +armv7neon_mmm_f32_8x4_cortexa7 8 4 12 0.0000022112784369711703 +armv7neon_mmm_f32_8x6_generic 23 32 13 0.000016306177258992233 +armv7neon_mmm_f32_8x4_cortexa7 9 32 11 0.000010819099493176479 +armv7neon_mmm_f32_8x4_generic 25 128 12 0.00004665173717365099 +armv7neon_mmm_f32_8x4_cortexa9 25 4 8 0.000004903249015406008 +armv7neon_mmm_f32_8x4_generic 15 128 8 0.000016011765752869696 +generic_f32_4x4 4 4 5 0.00000173237663586998 +generic_f32_4x4 7 32 13 0.000010951743398686358 +armv7neon_mmm_f32_8x6_cortexa9 7 32 11 0.000004373180249911381 +armv7neon_mmm_f32_8x6_generic 9 4 7 0.000003340351034825373 +armv7neon_mmm_f32_8x6_cortexa7 8 128 6 0.000007164475272224828 +generic_f32_4x4 8 4 11 0.000003982256860993786 +armv7neon_mmm_f32_8x6_generic 15 128 5 0.000010930509860719574 +armv7neon_mmm_f32_8x4_cortexa9 25 128 11 0.00004712675673590545 +armv7neon_mmm_f32_8x4_cortexa7 7 4 11 0.000002822400310815202 +armv7neon_mmm_f32_8x6_cortexa7 17 32 13 0.000019470812344800868 +armv7neon_mmm_f32_8x4_cortexa7 23 32 4 0.000005619794374416781 +generic_f32_4x4 11 32 8 0.000008140965167056766 +armv7neon_mmm_f32_8x4_cortexa7 17 4 5 0.000004516405012537028 +armv7neon_mmm_f32_8x4_cortexa7 17 128 4 0.00001625158956213561 +armv7neon_mmm_f32_8x6_generic 16 128 18 0.000030484389052945016 +armv7neon_mmm_f32_8x6_cortexa7 17 128 11 0.00004127386103261296 +armv7neon_mmm_f32_8x6_cortexa7 24 32 6 0.000006586776362035912 +armv7neon_mmm_f32_8x4_generic 9 32 5 0.000005900796369564617 +armv7neon_mmm_f32_8x6_cortexa7 7 128 11 0.000014441097326114378 +armv7neon_mmm_f32_8x6_cortexa9 24 4 19 0.000008421665590898334 +armv7neon_mmm_f32_8x4_cortexa7 15 32 9 0.000011049029459002566 +armv7neon_mmm_f32_8x6_cortexa9 17 32 7 0.000010792251610714281 +armv7neon_mmm_f32_8x4_generic 24 32 11 0.000012578955321334167 +armv7neon_mmm_f32_8x6_generic 8 4 17 0.0000025848174603713075 +armv7neon_mmm_f32_8x4_cortexa9 8 128 12 0.000011812308047768464 +generic_f32_4x4 12 128 8 0.00002174331661540877 +armv7neon_mmm_f32_8x4_cortexa7 16 128 12 0.0000317241936699183 +armv7neon_mmm_f32_8x6_cortexa9 9 4 5 0.000002126610313888205 +armv7neon_mmm_f32_8x6_cortexa9 8 32 12 0.0000037644801015301184 +armv7neon_mmm_f32_8x4_cortexa9 9 32 4 0.000003081152195958602 +generic_f32_4x4 7 32 9 0.000008408374041494756 +generic_f32_4x4 4 128 4 0.0000040497405415986515 +armv7neon_mmm_f32_8x4_cortexa9 15 128 7 0.000016202393035594662 +armv7neon_mmm_f32_8x6_cortexa9 25 128 6 0.000020788151245438416 +armv7neon_mmm_f32_8x4_cortexa7 8 128 13 0.000021488179929071295 +armv7neon_mmm_f32_8x4_cortexa7 25 32 9 0.00002091079321932589 +armv7neon_mmm_f32_8x4_cortexa9 8 128 5 0.000008180420598290364 +armv7neon_mmm_f32_8x4_generic 7 128 3 0.000004423621357206741 +armv7neon_mmm_f32_8x4_cortexa9 15 4 9 0.000004394233374561228 +armv7neon_mmm_f32_8x6_generic 7 4 12 0.0000025127271377138368 +armv7neon_mmm_f32_8x4_generic 23 128 8 0.00002364193719899604 +armv7neon_mmm_f32_8x4_cortexa7 7 32 11 0.000005969501269507202 +armv7neon_mmm_f32_8x6_cortexa9 16 4 7 0.0000033362358276857998 +armv7neon_mmm_f32_8x4_generic 25 4 4 0.000002679652210124942 +armv7neon_mmm_f32_8x6_cortexa7 24 128 19 0.00008148701915820504 +armv7neon_mmm_f32_8x4_cortexa7 25 32 12 0.000020301609592643522 +armv7neon_mmm_f32_8x6_generic 25 4 12 0.000005597867792560783 +armv7neon_mmm_f32_8x4_cortexa9 16 4 13 0.0000050370172264265826 +generic_f32_4x4 11 4 8 0.000003987191713644613 +armv7neon_mmm_f32_8x4_cortexa7 7 32 9 0.00000592156011559423 +armv7neon_mmm_f32_8x6_cortexa7 25 4 13 0.000009317851927845776 +armv7neon_mmm_f32_8x6_generic 7 32 6 0.0000025009886657895365 +armv7neon_mmm_f32_8x4_cortexa9 9 32 5 0.0000059031902576708605 +armv7neon_mmm_f32_8x4_cortexa7 25 4 7 0.000005997625417148922 +armv7neon_mmm_f32_8x4_generic 15 128 9 0.000023965770004599912 +armv7neon_mmm_f32_8x4_generic 9 128 3 0.000008336141118552254 +armv7neon_mmm_f32_8x6_cortexa9 15 4 5 0.0000022485015750854945 +armv7neon_mmm_f32_8x4_cortexa7 25 32 13 0.000027508557090202434 +armv7neon_mmm_f32_8x4_cortexa9 7 32 4 0.0000019276370605991633 +armv7neon_mmm_f32_8x6_cortexa7 7 32 13 0.000007512654299558133 +generic_f32_4x4 12 128 12 0.000032295866967880904 +armv7neon_mmm_f32_8x6_generic 8 128 13 0.00001569076394434193 +armv7neon_mmm_f32_8x4_cortexa9 17 128 8 0.000023412675748873913 +armv7neon_mmm_f32_8x4_cortexa9 16 4 3 0.000001964056838467611 +armv7neon_mmm_f32_8x4_generic 16 4 4 0.0000015347326629006993 +armv7neon_mmm_f32_8x4_cortexa9 24 4 4 0.0000020572993016505205 +armv7neon_mmm_f32_8x4_cortexa9 8 32 3 0.0000019507567628415824 +armv7neon_mmm_f32_8x4_cortexa7 8 32 7 0.000004003993118643105 +armv7neon_mmm_f32_8x4_generic 17 4 13 0.000007623324255157955 +armv7neon_mmm_f32_8x6_cortexa9 9 4 11 0.0000035090633204775533 +armv7neon_mmm_f32_8x6_generic 9 4 19 0.00000601244922696809 +armv7neon_mmm_f32_8x6_generic 9 128 7 0.000020875839952630357 +generic_f32_4x4 4 32 11 0.000004326527977488747 +armv7neon_mmm_f32_8x6_cortexa9 25 32 18 0.00002022815888923194 +armv7neon_mmm_f32_8x6_cortexa9 15 4 18 0.000005434062942764287 +armv7neon_mmm_f32_8x4_cortexa9 24 4 3 0.000002704298016252254 +armv7neon_mmm_f32_8x4_generic 24 4 9 0.000005701271915969865 +armv7neon_mmm_f32_8x6_cortexa9 7 128 19 0.000021850407441008384 +armv7neon_mmm_f32_8x4_cortexa9 15 4 3 0.0000019251432800014627 +armv7neon_mmm_f32_8x6_cortexa7 15 4 18 0.000005711033859652051 +armv7neon_mmm_f32_8x6_cortexa7 9 4 17 0.000005102785803942399 +armv7neon_mmm_f32_8x4_generic 8 128 4 0.000004232482987549164 +armv7neon_mmm_f32_8x4_cortexa9 25 128 7 0.00003172200454891407 +armv7neon_mmm_f32_8x4_cortexa9 9 32 12 0.000008361571818242635 +armv7neon_mmm_f32_8x6_cortexa7 24 32 17 0.000019514235657015186 +armv7neon_mmm_f32_8x6_cortexa7 23 128 6 0.000020944404830412158 +armv7neon_mmm_f32_8x6_cortexa9 17 128 5 0.000016134142374025674 +armv7neon_mmm_f32_8x4_cortexa7 8 4 7 0.000001873057019803265 +armv7neon_mmm_f32_8x6_cortexa7 25 128 5 0.00002802974521426182 +armv7neon_mmm_f32_8x6_cortexa9 17 4 17 0.000006958050831218578 +armv7neon_mmm_f32_8x4_cortexa9 15 32 5 0.00000608182468505122 +armv7neon_mmm_f32_8x4_generic 7 128 4 0.0000044404498411270055 +generic_f32_4x4 13 4 5 0.000005350599856702572 +armv7neon_mmm_f32_8x4_generic 16 4 9 0.0000039608523692233575 +armv7neon_mmm_f32_8x6_cortexa9 16 4 12 0.000003013935297522993 +armv7neon_mmm_f32_8x6_generic 15 4 13 0.000005319052004340107 +armv7neon_mmm_f32_8x4_cortexa9 8 4 13 0.0000027408020700732427 +armv7neon_mmm_f32_8x6_generic 8 128 18 0.000015505866042830093 +armv7neon_mmm_f32_8x6_cortexa7 15 32 18 0.000013888471960481304 +armv7neon_mmm_f32_8x4_cortexa7 15 128 8 0.00002174159034943732 +armv7neon_mmm_f32_8x4_generic 9 32 4 0.0000030856158641387716 +armv7neon_mmm_f32_8x4_generic 24 4 5 0.000004129514406774627 +generic_f32_4x4 4 4 7 0.0000017497295508923947 +armv7neon_mmm_f32_8x6_generic 23 32 17 0.00001653018733416135 +generic_f32_4x4 7 128 13 0.000029515913541067617 +armv7neon_mmm_f32_8x6_cortexa9 24 32 19 0.000020360891894208073 +armv7neon_mmm_f32_8x6_cortexa9 9 32 18 0.000010557768641496435 +armv7neon_mmm_f32_8x4_generic 8 4 13 0.000002736501222546797 +armv7neon_mmm_f32_8x4_generic 16 32 11 0.000008543993772071838 +armv7neon_mmm_f32_8x4_cortexa7 7 32 12 0.0000059782618274442185 +armv7neon_mmm_f32_8x6_cortexa7 8 128 11 0.000014095624176931188 +armv7neon_mmm_f32_8x4_cortexa7 7 32 13 0.00000775363354362587 +armv7neon_mmm_f32_8x4_cortexa7 24 128 5 0.000032299734402349505 +armv7neon_mmm_f32_8x6_cortexa9 25 32 12 0.000013697867410484751 +armv7neon_mmm_f32_8x4_cortexa7 24 128 4 0.000016113162651910352 +armv7neon_mmm_f32_8x6_cortexa9 9 32 5 0.0000041006560791439575 +generic_f32_4x4 7 4 11 0.000004296735480110931 +armv7neon_mmm_f32_8x6_generic 8 32 11 0.000003977003783384346 +armv7neon_mmm_f32_8x4_generic 24 32 13 0.000016227668114322495 +armv7neon_mmm_f32_8x6_generic 7 32 5 0.0000023419854212729095 +generic_f32_4x4 3 128 3 0.00000421323412258659 +generic_f32_4x4 8 4 12 0.0000036950676170218278 +armv7neon_mmm_f32_8x4_cortexa7 15 4 12 0.000004609879642718733 +armv7neon_mmm_f32_8x6_generic 8 128 11 0.000010745832002669769 +armv7neon_mmm_f32_8x6_cortexa7 16 32 18 0.000012726640475291294 +armv7neon_mmm_f32_8x6_generic 24 4 13 0.000006477448734654056 +armv7neon_mmm_f32_8x4_cortexa9 23 128 5 0.000023893090361578605 +armv7neon_mmm_f32_8x6_cortexa9 7 128 7 0.000011059489910827611 +armv7neon_mmm_f32_8x4_cortexa7 24 4 13 0.000007916232793484605 +armv7neon_mmm_f32_8x6_generic 24 32 5 0.000006050244864860637 +armv7neon_mmm_f32_8x6_generic 23 4 5 0.000003050740283377829 +armv7neon_mmm_f32_8x4_generic 17 4 7 0.000004317179186717401 +generic_f32_4x4 13 128 3 0.000015316541870867625 +armv7neon_mmm_f32_8x6_generic 25 128 11 0.00004196449161824797 +armv7neon_mmm_f32_8x6_generic 24 32 7 0.000010654786791684403 +armv7neon_mmm_f32_8x6_generic 9 128 13 0.0000310078542141026 +armv7neon_mmm_f32_8x4_cortexa7 17 128 13 0.00006395441906545403 +armv7neon_mmm_f32_8x6_cortexa7 25 4 11 0.000006802751222800881 +armv7neon_mmm_f32_8x6_cortexa7 16 32 12 0.000008630037205832748 +armv7neon_mmm_f32_8x6_cortexa9 8 128 6 0.000005530202507023102 +armv7neon_mmm_f32_8x4_cortexa7 24 4 3 0.000002895093469687887 +armv7neon_mmm_f32_8x6_cortexa7 23 32 13 0.00002011431966689971 +armv7neon_mmm_f32_8x6_cortexa7 8 128 17 0.000020781906415830542 +armv7neon_mmm_f32_8x6_cortexa7 8 128 7 0.00001401297102177983 +generic_f32_4x4 12 128 4 0.000011113783764067 +armv7neon_mmm_f32_8x4_cortexa9 23 128 8 0.000023633711148043694 +armv7neon_mmm_f32_8x6_cortexa7 8 32 7 0.00000473866242690848 +armv7neon_mmm_f32_8x6_cortexa7 23 128 13 0.00006200535049902772 +armv7neon_mmm_f32_8x6_generic 25 128 12 0.000040933940992834904 +armv7neon_mmm_f32_8x6_generic 8 4 7 0.0000019018312390917206 +armv7neon_mmm_f32_8x6_cortexa9 9 128 13 0.0000311113672962118 +armv7neon_mmm_f32_8x6_cortexa7 17 32 6 0.000006712613519894369 +armv7neon_mmm_f32_8x6_generic 17 32 17 0.00001584305104507533 +armv7neon_mmm_f32_8x6_cortexa9 25 4 5 0.000003846834546374256 +armv7neon_mmm_f32_8x4_generic 23 128 11 0.00003563405640596688 +armv7neon_mmm_f32_8x6_cortexa7 7 32 18 0.000007762095957340233 +armv7neon_mmm_f32_8x4_cortexa7 8 4 3 0.000001278724116798178 +armv7neon_mmm_f32_8x4_generic 8 32 9 0.000004428552791795867 +armv7neon_mmm_f32_8x4_cortexa7 9 32 8 0.000007212913220352459 +armv7neon_mmm_f32_8x6_cortexa7 16 32 11 0.000009132197785046325 +armv7neon_mmm_f32_8x6_generic 15 4 6 0.0000021293993921351435 +armv7neon_mmm_f32_8x6_cortexa9 8 32 17 0.000005618722861518267 +generic_f32_4x4 12 32 8 0.000007824605805291677 +generic_f32_4x4 3 4 9 0.0000025426825999668207 +armv7neon_mmm_f32_8x6_cortexa9 15 128 6 0.000010938928121944877 +armv7neon_mmm_f32_8x6_generic 8 128 12 0.000010479271785006985 +armv7neon_mmm_f32_8x6_cortexa7 25 4 18 0.000008843120394707296 +generic_f32_4x4 5 128 8 0.000014867005692635484 +armv7neon_mmm_f32_8x4_generic 25 4 12 0.000007134980758480078 +armv7neon_mmm_f32_8x6_generic 23 4 6 0.0000027550692223444384 +generic_f32_4x4 8 128 4 0.000007594935908642548 +armv7neon_mmm_f32_8x4_generic 24 4 3 0.0000027070026756528944 +armv7neon_mmm_f32_8x6_generic 15 4 7 0.0000036879615500804916 +armv7neon_mmm_f32_8x6_cortexa7 8 4 13 0.0000026670243954904784 +armv7neon_mmm_f32_8x6_generic 17 4 7 0.000004739940833422853 +armv7neon_mmm_f32_8x6_generic 24 128 11 0.00003139196614178293 +armv7neon_mmm_f32_8x6_cortexa9 24 128 13 0.0000461884138564535 +generic_f32_4x4 9 128 9 0.00003291260190607621 +armv7neon_mmm_f32_8x4_cortexa9 7 128 11 0.000012420788787006248 +armv7neon_mmm_f32_8x6_generic 17 128 12 0.00003088346853188957 +armv7neon_mmm_f32_8x4_cortexa7 8 4 13 0.000002968959195086507 +armv7neon_mmm_f32_8x6_cortexa9 24 128 6 0.000015548482246921477 +armv7neon_mmm_f32_8x4_cortexa7 17 32 7 0.000010943141781236415 +armv7neon_mmm_f32_8x6_cortexa7 16 4 12 0.000003160151719580885 +generic_f32_4x4 5 128 5 0.000014996303737029157 +armv7neon_mmm_f32_8x6_generic 25 4 6 0.0000030664133228659144 +armv7neon_mmm_f32_8x4_cortexa9 9 4 11 0.0000041810312425253365 +armv7neon_mmm_f32_8x6_generic 15 4 18 0.000005353071509503547 +armv7neon_mmm_f32_8x6_cortexa7 8 32 12 0.000004562116220290897 +armv7neon_mmm_f32_8x4_cortexa9 8 32 7 0.0000032056568363054847 +armv7neon_mmm_f32_8x4_generic 16 4 11 0.0000040609047361039316 +armv7neon_mmm_f32_8x6_cortexa9 17 4 19 0.00000876633957316096 +armv7neon_mmm_f32_8x6_cortexa7 15 128 18 0.00004180200146350024 +generic_f32_4x4 5 128 9 0.000022192516532888443 +armv7neon_mmm_f32_8x6_cortexa9 24 32 11 0.000010954278251461477 +armv7neon_mmm_f32_8x6_cortexa9 8 32 5 0.000002372663230804753 +armv7neon_mmm_f32_8x6_generic 8 4 5 0.0000013605175071348517 +armv7neon_mmm_f32_8x6_cortexa7 24 4 6 0.0000025054020094278158 +armv7neon_mmm_f32_8x6_generic 24 128 5 0.000016222838915570737 +generic_f32_4x4 12 32 4 0.000004159176762509275 +generic_f32_4x4 13 4 11 0.000007582485226342242 +generic_f32_4x4 9 4 5 0.000004196638831417376 +armv7neon_mmm_f32_8x6_cortexa7 25 32 17 0.000026071317528366354 +armv7neon_mmm_f32_8x4_cortexa9 16 128 8 0.000015686411664235285 +generic_f32_4x4 13 128 11 0.00004380249651194818 +armv7neon_mmm_f32_8x4_cortexa7 24 32 13 0.000020563104321953744 +armv7neon_mmm_f32_8x4_cortexa9 23 4 7 0.0000044861575535086865 +generic_f32_4x4 12 128 3 0.000011613772328414718 +armv7neon_mmm_f32_8x6_cortexa7 16 4 19 0.000006163473976132481 +generic_f32_4x4 7 4 8 0.0000029313315346635237 +armv7neon_mmm_f32_8x4_cortexa9 23 32 12 0.000012545342985454958 +armv7neon_mmm_f32_8x6_cortexa9 7 128 11 0.000011136453503507945 +armv7neon_mmm_f32_8x6_cortexa7 9 128 13 0.000041110907972239005 +armv7neon_mmm_f32_8x4_cortexa9 7 32 8 0.000003418961974304266 +armv7neon_mmm_f32_8x4_cortexa7 24 32 12 0.000015105629538772432 +generic_f32_4x4 7 128 3 0.000007903637217361153 +armv7neon_mmm_f32_8x6_cortexa7 9 4 11 0.000003661168986658601 +generic_f32_4x4 4 128 9 0.00001126621217728165 +armv7neon_mmm_f32_8x4_cortexa7 23 128 3 0.000016760586455833876 +armv7neon_mmm_f32_8x6_cortexa9 16 32 12 0.000007056930252112048 +armv7neon_mmm_f32_8x6_cortexa9 8 4 19 0.000003165350777332514 +armv7neon_mmm_f32_8x6_generic 8 32 13 0.000005498586776368978 +generic_f32_4x4 3 128 13 0.000015227075547579779 +generic_f32_4x4 12 4 13 0.000007217490541690208 +armv7neon_mmm_f32_8x6_cortexa7 16 128 18 0.000040584526180649477 +armv7neon_mmm_f32_8x6_cortexa9 25 4 7 0.000006216705461889991 +armv7neon_mmm_f32_8x4_generic 24 128 9 0.000035021164515684145 +generic_f32_4x4 3 32 12 0.000004609887398147942 +armv7neon_mmm_f32_8x4_generic 25 128 9 0.00004684932933132534 +armv7neon_mmm_f32_8x6_generic 7 4 17 0.0000033628632970040393 +generic_f32_4x4 4 128 12 0.000011148897561634414 +armv7neon_mmm_f32_8x4_generic 8 128 8 0.000008028324048468155 +armv7neon_mmm_f32_8x4_generic 8 4 11 0.0000022515619534309057 +armv7neon_mmm_f32_8x4_cortexa7 9 32 5 0.00000739044702644143 +armv7neon_mmm_f32_8x4_generic 8 128 5 0.000008179707225297848 +armv7neon_mmm_f32_8x4_generic 9 4 4 0.0000016079691037580586 +armv7neon_mmm_f32_8x4_generic 16 128 4 0.00000803169255760427 +armv7neon_mmm_f32_8x4_generic 25 128 3 0.000016355748669331108 +armv7neon_mmm_f32_8x6_cortexa9 24 4 18 0.0000061179448610506165 +armv7neon_mmm_f32_8x6_cortexa9 16 4 19 0.000005803928147585998 +armv7neon_mmm_f32_8x6_cortexa7 17 128 6 0.00002064753338392539 +armv7neon_mmm_f32_8x4_cortexa9 9 4 12 0.000003936841431536775 +armv7neon_mmm_f32_8x4_cortexa7 17 32 13 0.000020815595163068456 +armv7neon_mmm_f32_8x6_cortexa7 23 32 11 0.000013810001606181058 +armv7neon_mmm_f32_8x4_cortexa7 15 32 13 0.00001449569792912252 +armv7neon_mmm_f32_8x4_cortexa7 7 4 3 0.0000012380899483592991 +armv7neon_mmm_f32_8x6_cortexa9 23 128 17 0.00004728149304262184 +armv7neon_mmm_f32_8x4_cortexa7 15 32 3 0.000004158381257275162 +armv7neon_mmm_f32_8x4_cortexa7 9 4 9 0.000004430434184731522 +armv7neon_mmm_f32_8x6_generic 23 128 13 0.00004715382202102794 +armv7neon_mmm_f32_8x6_cortexa9 9 4 19 0.000006116079821760871 +armv7neon_mmm_f32_8x6_cortexa9 7 4 18 0.0000035442600221807646 +armv7neon_mmm_f32_8x4_generic 15 128 5 0.000016261604433183137 +armv7neon_mmm_f32_8x4_cortexa9 8 128 4 0.000004251382345566181 +armv7neon_mmm_f32_8x6_cortexa7 25 128 11 0.000055294824394928404 +armv7neon_mmm_f32_8x6_cortexa9 23 128 13 0.00004716175024845285 +generic_f32_4x4 12 128 11 0.00003283409583810123 +armv7neon_mmm_f32_8x6_cortexa7 15 128 6 0.000014244472752339967 +armv7neon_mmm_f32_8x4_generic 17 32 8 0.000008307634701683482 +armv7neon_mmm_f32_8x4_generic 16 4 13 0.000005034888545956025 +armv7neon_mmm_f32_8x6_cortexa7 9 128 11 0.000027682771225447373 +armv7neon_mmm_f32_8x4_generic 25 128 8 0.000030970064243633 +armv7neon_mmm_f32_8x6_cortexa9 15 32 5 0.0000042126470351483745 +armv7neon_mmm_f32_8x6_cortexa9 8 128 7 0.000010688502492077545 +armv7neon_mmm_f32_8x6_cortexa7 8 32 19 0.000008785622923040567 +generic_f32_4x4 8 32 7 0.000005699367356537332 +armv7neon_mmm_f32_8x4_cortexa9 23 4 13 0.000007982321009915726 +armv7neon_mmm_f32_8x6_cortexa9 7 32 19 0.000008296837727225683 +armv7neon_mmm_f32_8x4_cortexa9 17 128 12 0.00003482723156416195 +armv7neon_mmm_f32_8x4_cortexa7 16 4 12 0.000003962627391393735 +armv7neon_mmm_f32_8x4_generic 24 32 4 0.000004275975584058462 +armv7neon_mmm_f32_8x4_generic 15 4 4 0.00000172768002087017 +generic_f32_4x4 12 128 13 0.000043289852359027704 +armv7neon_mmm_f32_8x6_cortexa7 24 32 18 0.00001876149316286456 +armv7neon_mmm_f32_8x4_cortexa9 23 128 13 0.000047081790611742305 +armv7neon_mmm_f32_8x4_generic 25 4 8 0.000004894765654717258 +generic_f32_4x4 13 32 9 0.000015809325344243507 +armv7neon_mmm_f32_8x6_cortexa9 15 32 11 0.000007869115330631703 +armv7neon_mmm_f32_8x6_cortexa7 16 32 19 0.000017064208112188813 +armv7neon_mmm_f32_8x4_cortexa7 9 32 12 0.000010571719001405112 +armv7neon_mmm_f32_8x6_cortexa9 7 4 12 0.000002545629385927187 +armv7neon_mmm_f32_8x4_cortexa9 24 128 5 0.00002368492009732677 +armv7neon_mmm_f32_8x6_cortexa7 25 4 5 0.000004026698373090156 +generic_f32_4x4 9 32 9 0.000012044684258478282 +armv7neon_mmm_f32_8x6_cortexa9 25 128 12 0.000040909803644855135 +generic_f32_4x4 8 128 12 0.00002170370665519434 +armv7neon_mmm_f32_8x6_generic 23 4 13 0.000007322110702891982 +armv7neon_mmm_f32_8x4_generic 8 128 9 0.000011964404434625917 +armv7neon_mmm_f32_8x6_generic 24 4 6 0.00000234486203657902 +armv7neon_mmm_f32_8x4_generic 16 128 12 0.000023289775015844785 +armv7neon_mmm_f32_8x4_generic 25 32 4 0.00000566239966628999 +armv7neon_mmm_f32_8x4_cortexa7 8 128 7 0.000011133117389377919 +armv7neon_mmm_f32_8x4_cortexa7 23 128 7 0.000032614653457132354 +generic_f32_4x4 11 128 3 0.000011596634661777904 +armv7neon_mmm_f32_8x6_generic 9 32 7 0.000007301874181587425 +armv7neon_mmm_f32_8x6_cortexa7 25 4 6 0.0000032842534990270153 +armv7neon_mmm_f32_8x4_cortexa7 25 4 11 0.000008398472143479583 +armv7neon_mmm_f32_8x4_cortexa9 17 128 3 0.000012367498051511246 +armv7neon_mmm_f32_8x4_cortexa9 15 4 4 0.0000017226474127640693 +armv7neon_mmm_f32_8x6_cortexa9 8 4 12 0.0000017663174571893155 +armv7neon_mmm_f32_8x6_generic 24 32 18 0.000014939528824089691 +armv7neon_mmm_f32_8x4_cortexa9 15 128 3 0.000008424928636646119 +armv7neon_mmm_f32_8x4_cortexa9 25 32 8 0.000010844406621465396 +armv7neon_mmm_f32_8x6_generic 24 128 6 0.00001549274717546449 +armv7neon_mmm_f32_8x4_cortexa9 7 4 8 0.0000019411905486089406 +generic_f32_4x4 5 4 12 0.000003970968097394295 +armv7neon_mmm_f32_8x4_cortexa7 17 128 3 0.000016675513856755485 +armv7neon_mmm_f32_8x6_generic 15 128 17 0.0000319988391989146 +armv7neon_mmm_f32_8x4_generic 8 4 3 0.0000012046114173177262 +armv7neon_mmm_f32_8x4_generic 9 4 13 0.000005271381310779438 +armv7neon_mmm_f32_8x6_generic 25 4 5 0.000003787680148419445 +armv7neon_mmm_f32_8x6_cortexa9 16 4 13 0.00000455965655028581 +armv7neon_mmm_f32_8x6_generic 16 128 12 0.000020507038874403576 +armv7neon_mmm_f32_8x4_cortexa9 7 4 3 0.0000011789464739113475 +armv7neon_mmm_f32_8x4_cortexa7 15 4 9 0.000004718086287004601 +generic_f32_4x4 3 32 8 0.0000032590697277049294 +armv7neon_mmm_f32_8x4_cortexa7 25 32 11 0.000021029318586240916 +armv7neon_mmm_f32_8x4_cortexa9 7 128 7 0.000008425064090676705 +armv7neon_mmm_f32_8x4_cortexa9 23 128 7 0.000024027014834706714 +armv7neon_mmm_f32_8x4_cortexa9 9 128 13 0.000031337531340777375 +generic_f32_4x4 12 32 3 0.0000046491920750409604 +armv7neon_mmm_f32_8x4_cortexa7 9 128 12 0.00003204704023977606 +armv7neon_mmm_f32_8x6_cortexa9 15 128 5 0.000010976146066847136 +armv7neon_mmm_f32_8x6_generic 9 128 5 0.000010821808595127155 +armv7neon_mmm_f32_8x6_cortexa9 16 32 18 0.000010217778748183444 +armv7neon_mmm_f32_8x6_cortexa9 8 32 13 0.000005537991894862071 +armv7neon_mmm_f32_8x6_cortexa7 15 128 11 0.000028075729994494098 +armv7neon_mmm_f32_8x6_cortexa7 24 4 7 0.000004992618899850428 +armv7neon_mmm_f32_8x6_generic 15 32 6 0.000004128781340215273 +armv7neon_mmm_f32_8x6_generic 15 32 18 0.000011342396294923752 +generic_f32_4x4 3 128 9 0.000011614205849736826 +armv7neon_mmm_f32_8x4_generic 7 32 9 0.000004862452767728157 +armv7neon_mmm_f32_8x4_cortexa9 17 4 7 0.000004308345799806778 +armv7neon_mmm_f32_8x6_cortexa9 16 32 13 0.000010598291750058129 +armv7neon_mmm_f32_8x4_generic 17 32 7 0.000008775628241420315 +armv7neon_mmm_f32_8x4_cortexa9 8 4 8 0.00000151354666080862 +armv7neon_mmm_f32_8x6_cortexa7 9 4 19 0.0000064774482704190535 +armv7neon_mmm_f32_8x4_cortexa9 9 4 4 0.000001610951102254284 +armv7neon_mmm_f32_8x4_cortexa7 7 128 3 0.00000588268473785512 +armv7neon_mmm_f32_8x4_cortexa9 9 128 7 0.00001614593088465898 +armv7neon_mmm_f32_8x6_cortexa9 9 128 6 0.000010694366815766805 +armv7neon_mmm_f32_8x6_cortexa9 17 4 5 0.000003000192050393217 +armv7neon_mmm_f32_8x4_cortexa9 8 32 4 0.0000017239720455885444 +armv7neon_mmm_f32_8x6_cortexa7 25 128 18 0.0000814675708223554 +generic_f32_4x4 12 128 7 0.000022191318720751985 +generic_f32_4x4 5 128 13 0.000029341360540777852 +generic_f32_4x4 7 4 5 0.000003063610185404798 +armv7neon_mmm_f32_8x4_cortexa7 23 32 12 0.000015790133040211727 +armv7neon_mmm_f32_8x6_cortexa7 17 128 13 0.00006139081303315256 +armv7neon_mmm_f32_8x4_cortexa9 8 32 12 0.000004268120941798172 +armv7neon_mmm_f32_8x6_generic 16 32 17 0.000010656142728555816 +generic_f32_4x4 13 4 8 0.000004925203028119758 +armv7neon_mmm_f32_8x6_generic 16 4 6 0.0000017414671485444925 +armv7neon_mmm_f32_8x6_generic 23 4 7 0.000005090461887505994 +armv7neon_mmm_f32_8x4_cortexa9 7 128 4 0.000004439163092495918 +armv7neon_mmm_f32_8x4_cortexa9 15 32 4 0.000003199516110418427 +generic_f32_4x4 13 4 9 0.000007521941079552681 +armv7neon_mmm_f32_8x4_generic 9 128 13 0.00003132672472959027 +armv7neon_mmm_f32_8x6_cortexa7 9 128 5 0.000014186965083572584 +armv7neon_mmm_f32_8x4_cortexa9 16 32 13 0.000010950995485777078 +armv7neon_mmm_f32_8x4_cortexa7 24 4 11 0.000006343455120468695 +armv7neon_mmm_f32_8x4_generic 23 32 12 0.000012518582750578088 +armv7neon_mmm_f32_8x4_cortexa9 23 4 5 0.000004357470789736493 +armv7neon_mmm_f32_8x6_generic 9 128 12 0.000020738827544116163 +armv7neon_mmm_f32_8x4_generic 7 128 8 0.000008441257822863349 +armv7neon_mmm_f32_8x6_generic 25 4 18 0.000008206815180054958 +armv7neon_mmm_f32_8x6_cortexa7 16 128 7 0.00002754330318348781 +armv7neon_mmm_f32_8x4_cortexa9 15 128 12 0.000023818850811398474 +armv7neon_mmm_f32_8x6_cortexa7 7 32 5 0.0000027477786822716826 +generic_f32_4x4 4 32 9 0.000004310962413140207 +armv7neon_mmm_f32_8x6_cortexa9 9 32 11 0.000007472405716714093 +generic_f32_4x4 9 4 7 0.0000042110601811121985 +armv7neon_mmm_f32_8x4_cortexa9 25 128 5 0.00003145000193606305 +armv7neon_mmm_f32_8x6_generic 16 128 7 0.000020961637268522957 +armv7neon_mmm_f32_8x6_cortexa7 8 128 18 0.00002061441681055432 +armv7neon_mmm_f32_8x6_cortexa7 15 32 11 0.000009542090957976056 +armv7neon_mmm_f32_8x4_cortexa9 8 32 8 0.0000030204030341535007 +armv7neon_mmm_f32_8x6_cortexa9 16 4 18 0.000004289256443600587 +armv7neon_mmm_f32_8x4_cortexa7 15 32 8 0.000007451057144596197 +armv7neon_mmm_f32_8x6_cortexa7 25 128 12 0.00005431857023175915 +armv7neon_mmm_f32_8x6_generic 7 128 11 0.000011099973931488493 +armv7neon_mmm_f32_8x4_cortexa9 16 128 11 0.000023648803168223167 +armv7neon_mmm_f32_8x6_cortexa9 17 4 12 0.0000044808063412694585 +armv7neon_mmm_f32_8x6_generic 25 32 7 0.000014053446208239492 +armv7neon_mmm_f32_8x6_generic 16 128 19 0.0000411706712809725 +armv7neon_mmm_f32_8x4_generic 16 32 13 0.000010937227005963747 +armv7neon_mmm_f32_8x4_cortexa7 8 32 3 0.000002329652194595725 +armv7neon_mmm_f32_8x4_cortexa9 23 4 11 0.000006308879061820964 +armv7neon_mmm_f32_8x6_cortexa7 16 4 7 0.0000035065261101064537 +armv7neon_mmm_f32_8x6_generic 9 128 6 0.000010599309996894829 +armv7neon_mmm_f32_8x4_cortexa7 24 128 11 0.000048035927729516165 +armv7neon_mmm_f32_8x6_generic 7 32 11 0.000004333152377602563 +armv7neon_mmm_f32_8x4_cortexa7 15 4 11 0.000004824968358151188 +armv7neon_mmm_f32_8x6_cortexa7 7 32 6 0.0000029036965940094368 +generic_f32_4x4 12 4 3 0.0000026010633583656195 +armv7neon_mmm_f32_8x6_cortexa9 8 32 18 0.000005373878070970815 +armv7neon_mmm_f32_8x4_generic 16 128 7 0.000016057054409709098 +armv7neon_mmm_f32_8x4_generic 17 128 5 0.000023713194238003484 +armv7neon_mmm_f32_8x6_generic 24 128 19 0.0000639824281045487 +generic_f32_4x4 5 4 13 0.000005260715034760787 +armv7neon_mmm_f32_8x6_cortexa9 16 128 19 0.000040932625772708764 +armv7neon_mmm_f32_8x4_generic 16 32 5 0.00000587187854127478 +armv7neon_mmm_f32_8x4_generic 15 32 4 0.0000032031359648686487 +armv7neon_mmm_f32_8x4_cortexa9 24 32 13 0.00001619115724351509 +armv7neon_mmm_f32_8x4_generic 15 32 8 0.000005976862649343733 +armv7neon_mmm_f32_8x6_cortexa9 16 32 7 0.000007329062657266122 +armv7neon_mmm_f32_8x4_cortexa9 25 32 7 0.00001147457512424339 +armv7neon_mmm_f32_8x4_cortexa9 24 128 3 0.000012480179872163079 +armv7neon_mmm_f32_8x6_generic 16 4 13 0.000004503173649426117 +armv7neon_mmm_f32_8x6_generic 16 4 19 0.000005734445256719681 +armv7neon_mmm_f32_8x4_cortexa7 24 4 8 0.000003952435244053743 +armv7neon_mmm_f32_8x6_cortexa9 9 32 6 0.000003904765036328731 +armv7neon_mmm_f32_8x6_cortexa7 23 4 13 0.000007856276222270553 +armv7neon_mmm_f32_8x6_cortexa9 15 4 6 0.000002182843988681052 +armv7neon_mmm_f32_8x6_generic 23 4 18 0.000007195841362691159 +armv7neon_mmm_f32_8x4_cortexa7 16 32 12 0.000010255406371017558 +armv7neon_mmm_f32_8x4_cortexa9 25 128 8 0.0000309762014941751 +armv7neon_mmm_f32_8x6_cortexa7 25 128 17 0.00008204444005539351 +armv7neon_mmm_f32_8x6_cortexa7 17 4 6 0.0000026268622339640763 +armv7neon_mmm_f32_8x4_cortexa9 15 32 9 0.000008859604622252251 +generic_f32_4x4 5 32 9 0.000008264557275071169 +generic_f32_4x4 13 32 5 0.000010823964577863127 +generic_f32_4x4 5 4 3 0.000001851289980714991 +armv7neon_mmm_f32_8x4_generic 15 128 4 0.000008225264190077282 +armv7neon_mmm_f32_8x6_generic 17 32 18 0.000015295586544658026 +armv7neon_mmm_f32_8x6_cortexa9 9 4 18 0.000004572833723975051 +armv7neon_mmm_f32_8x4_cortexa7 15 4 13 0.000006075307141015665 +armv7neon_mmm_f32_8x6_cortexa7 17 32 19 0.000025630924279728742 +generic_f32_4x4 7 32 7 0.0000058385014382941885 +armv7neon_mmm_f32_8x4_cortexa9 16 128 7 0.000016054999854639244 +armv7neon_mmm_f32_8x4_cortexa7 25 4 4 0.0000029326008363454604 +armv7neon_mmm_f32_8x6_cortexa7 16 128 12 0.000027204121533574832 +armv7neon_mmm_f32_8x4_generic 15 128 3 0.000008426038232256742 +armv7neon_mmm_f32_8x4_cortexa9 7 32 5 0.0000033516027030490246 +armv7neon_mmm_f32_8x4_cortexa7 16 4 7 0.0000032624014554442194 +armv7neon_mmm_f32_8x4_cortexa9 16 128 3 0.000008462143402947945 +armv7neon_mmm_f32_8x4_generic 25 4 7 0.000005557466852852917 +armv7neon_mmm_f32_8x4_generic 23 32 4 0.000004495607981128392 +armv7neon_mmm_f32_8x4_cortexa9 15 4 12 0.000004291414852711907 +generic_f32_4x4 4 4 13 0.0000027727007811134473 +armv7neon_mmm_f32_8x4_generic 16 4 8 0.0000025796442872451462 +armv7neon_mmm_f32_8x4_cortexa9 23 4 9 0.000006179316744537218 +armv7neon_mmm_f32_8x4_cortexa7 25 32 3 0.000007764249905705397 +armv7neon_mmm_f32_8x6_generic 17 32 7 0.000010687826852062675 +generic_f32_4x4 13 4 7 0.000005399817435719611 +generic_f32_4x4 12 4 4 0.0000021094685699886824 +armv7neon_mmm_f32_8x6_cortexa9 7 4 17 0.0000034025163658270913 +generic_f32_4x4 4 4 9 0.0000022359497586694876 +armv7neon_mmm_f32_8x6_cortexa9 17 128 17 0.00004660020476545699 +armv7neon_mmm_f32_8x4_generic 15 4 12 0.00000428385702551851 +armv7neon_mmm_f32_8x4_generic 9 4 11 0.000004185007110545824 +armv7neon_mmm_f32_8x6_cortexa9 24 128 18 0.000045657972165917084 +armv7neon_mmm_f32_8x6_cortexa9 15 4 19 0.000007027336593004323 +armv7neon_mmm_f32_8x4_cortexa9 9 128 9 0.00002367365046977455 +armv7neon_mmm_f32_8x4_cortexa9 16 4 9 0.000003960113146754688 +armv7neon_mmm_f32_8x4_cortexa7 8 32 9 0.000005561279017749759 +armv7neon_mmm_f32_8x4_cortexa9 23 32 3 0.000004897369194486712 +armv7neon_mmm_f32_8x4_cortexa9 16 32 3 0.0000034377989421061634 +armv7neon_mmm_f32_8x6_generic 25 32 5 0.00000775592482259445 +armv7neon_mmm_f32_8x4_cortexa9 9 128 11 0.000023733495515623523 +armv7neon_mmm_f32_8x4_generic 23 32 7 0.000008935361824557136 +armv7neon_mmm_f32_8x4_cortexa7 7 128 9 0.00001664390737173506 +generic_f32_4x4 5 4 8 0.0000028477215638418112 +armv7neon_mmm_f32_8x6_generic 23 4 19 0.000009557906532901389 +armv7neon_mmm_f32_8x6_cortexa9 16 128 12 0.000020636825667982075 +armv7neon_mmm_f32_8x6_generic 9 32 11 0.000007421682594966975 +armv7neon_mmm_f32_8x4_generic 23 128 13 0.000047353976390946026 +armv7neon_mmm_f32_8x4_cortexa7 17 4 7 0.000004622886247456753 +armv7neon_mmm_f32_8x6_cortexa9 8 128 5 0.000005754129281131009 +armv7neon_mmm_f32_8x4_cortexa7 9 128 11 0.000032319021391833177 +armv7neon_mmm_f32_8x4_cortexa9 24 4 9 0.000005698490674755626 +armv7neon_mmm_f32_8x6_generic 17 4 18 0.000006326121915439696 +armv7neon_mmm_f32_8x4_cortexa9 16 128 12 0.000023196293109823396 +armv7neon_mmm_f32_8x4_cortexa9 25 32 9 0.000016534317998128363 +armv7neon_mmm_f32_8x6_cortexa7 24 4 5 0.0000032362274636792855 +armv7neon_mmm_f32_8x6_cortexa7 9 32 13 0.000013201464669659568 +armv7neon_mmm_f32_8x4_generic 25 128 5 0.000031432252054608495 +armv7neon_mmm_f32_8x4_cortexa7 16 32 8 0.000007004694197132091 +armv7neon_mmm_f32_8x4_cortexa7 25 128 8 0.00004234315278499361 +armv7neon_mmm_f32_8x6_cortexa9 25 128 11 0.00004162498921103509 +armv7neon_mmm_f32_8x4_cortexa9 17 4 11 0.000005990145971795821 +armv7neon_mmm_f32_8x6_generic 7 128 5 0.000005711586442858516 +generic_f32_4x4 5 32 7 0.000005745084475312582 +armv7neon_mmm_f32_8x4_cortexa7 17 4 3 0.0000027778998365925647 +armv7neon_mmm_f32_8x6_generic 7 32 17 0.000006323975084622719 +armv7neon_mmm_f32_8x4_cortexa7 24 32 4 0.0000053627426905310905 +armv7neon_mmm_f32_8x6_cortexa9 7 128 13 0.000016465054999968026 +armv7neon_mmm_f32_8x6_cortexa9 25 32 17 0.000021029276181809713 +armv7neon_mmm_f32_8x6_generic 7 4 18 0.0000035040816774469106 +armv7neon_mmm_f32_8x6_cortexa7 17 128 12 0.00004084848200658872 +armv7neon_mmm_f32_8x6_cortexa7 16 128 11 0.000027707012596319906 +armv7neon_mmm_f32_8x6_cortexa7 8 128 12 0.0000138330433065325 +armv7neon_mmm_f32_8x4_cortexa9 7 32 9 0.000004837549683885907 +armv7neon_mmm_f32_8x4_generic 7 32 7 0.000003409099607922408 +armv7neon_mmm_f32_8x6_cortexa7 16 4 5 0.0000023317089411955496 +armv7neon_mmm_f32_8x6_cortexa7 7 32 17 0.000007581121899800583 +armv7neon_mmm_f32_8x4_cortexa7 16 4 9 0.0000042985642379541856 +armv7neon_mmm_f32_8x6_generic 16 4 7 0.0000032960593218992506 +armv7neon_mmm_f32_8x4_generic 15 32 5 0.0000060704297110345465 +armv7neon_mmm_f32_8x6_cortexa9 24 32 6 0.00000538028834244819 +armv7neon_mmm_f32_8x6_generic 25 32 18 0.0000200993477092098 +armv7neon_mmm_f32_8x6_generic 23 128 6 0.00001591506566691279 +armv7neon_mmm_f32_8x6_generic 16 32 13 0.00001049572423866497 +armv7neon_mmm_f32_8x4_cortexa7 15 32 5 0.000007565304970371118 +generic_f32_4x4 12 32 7 0.000008258205556820225 +armv7neon_mmm_f32_8x4_cortexa7 17 4 9 0.000006349279290785205 +generic_f32_4x4 8 128 5 0.000014938883570524155 +armv7neon_mmm_f32_8x4_generic 15 32 3 0.0000034015953691065083 +armv7neon_mmm_f32_8x6_cortexa7 7 4 18 0.000003692100644999123 +generic_f32_4x4 8 128 7 0.000015006901203342376 +generic_f32_4x4 4 4 3 0.0000012181206340328286 +generic_f32_4x4 12 32 5 0.000008206014399831754 +armv7neon_mmm_f32_8x6_cortexa9 8 128 19 0.000020717968248585576 +generic_f32_4x4 5 32 3 0.000003225094305304002 +armv7neon_mmm_f32_8x6_generic 24 4 18 0.000006021495896771489 +armv7neon_mmm_f32_8x6_cortexa7 16 128 13 0.00004093551587387655 +armv7neon_mmm_f32_8x4_cortexa9 15 128 8 0.00001604448990415154 +armv7neon_mmm_f32_8x6_generic 9 32 13 0.000010651318368732651 +armv7neon_mmm_f32_8x4_cortexa7 8 32 8 0.000003772107419321899 +generic_f32_4x4 9 32 3 0.000004621846936980697 +armv7neon_mmm_f32_8x4_cortexa9 16 32 5 0.000005906329928132943 +armv7neon_mmm_f32_8x6_cortexa9 16 4 6 0.000001808587040184933 +armv7neon_mmm_f32_8x6_cortexa9 9 32 13 0.000010778660548919894 +armv7neon_mmm_f32_8x4_cortexa9 16 32 4 0.0000030105150384631114 +armv7neon_mmm_f32_8x6_generic 17 4 17 0.000006838981837746129 +armv7neon_mmm_f32_8x6_cortexa7 7 128 19 0.000028499453971524397 +armv7neon_mmm_f32_8x6_cortexa7 23 32 17 0.000020319754403004648 +armv7neon_mmm_f32_8x6_generic 9 32 17 0.000010765407372129566 +armv7neon_mmm_f32_8x4_generic 23 4 13 0.000008003285656461567 +armv7neon_mmm_f32_8x4_generic 17 4 4 0.000002161502194915875 +armv7neon_mmm_f32_8x4_generic 23 4 11 0.000006323737070277622 +armv7neon_mmm_f32_8x4_generic 9 4 12 0.00000394067683542442 +armv7neon_mmm_f32_8x4_cortexa7 23 128 4 0.00001636523555605817 +armv7neon_mmm_f32_8x4_cortexa9 17 32 12 0.000012187305627907507 +armv7neon_mmm_f32_8x4_generic 25 32 5 0.000011305023296577953 +armv7neon_mmm_f32_8x4_cortexa7 23 128 13 0.0000641420493251813 +armv7neon_mmm_f32_8x6_cortexa9 17 4 13 0.0000067882601578195375 +armv7neon_mmm_f32_8x6_cortexa9 24 32 13 0.00001554482246687919 +armv7neon_mmm_f32_8x4_cortexa7 15 128 12 0.00003239866815806022 +armv7neon_mmm_f32_8x6_cortexa9 23 4 13 0.000007410621365009766 +armv7neon_mmm_f32_8x4_cortexa9 17 128 5 0.000023718649742852314 +armv7neon_mmm_f32_8x6_cortexa9 15 32 13 0.000011397983577281118 +armv7neon_mmm_f32_8x6_cortexa7 15 32 5 0.0000050262586648579474 +armv7neon_mmm_f32_8x4_cortexa9 16 128 9 0.000023527233248412315 +armv7neon_mmm_f32_8x6_cortexa7 17 4 17 0.000007344312997274551 +armv7neon_mmm_f32_8x6_cortexa7 15 128 13 0.00004174083709909147 +armv7neon_mmm_f32_8x4_cortexa9 24 128 11 0.00003518379984445077 +armv7neon_mmm_f32_8x6_cortexa7 15 4 6 0.0000022388984932072717 +armv7neon_mmm_f32_8x6_generic 17 32 11 0.000010862418538636322 +armv7neon_mmm_f32_8x6_cortexa9 17 128 19 0.00006205059984963943 +armv7neon_mmm_f32_8x6_cortexa7 24 128 7 0.00004110240285622754 +armv7neon_mmm_f32_8x6_generic 25 32 11 0.00001430805053910281 +armv7neon_mmm_f32_8x4_cortexa9 23 128 11 0.00003562503787212338 +armv7neon_mmm_f32_8x4_generic 8 128 13 0.00001577265144197705 +armv7neon_mmm_f32_8x4_cortexa9 15 32 3 0.0000034017274319352206 +generic_f32_4x4 13 32 3 0.000005959878460430609 +armv7neon_mmm_f32_8x4_cortexa9 8 32 9 0.000004432308412032044 +armv7neon_mmm_f32_8x4_cortexa7 8 4 5 0.0000018214760004994945 +armv7neon_mmm_f32_8x4_generic 16 4 5 0.0000029127359283609326 +armv7neon_mmm_f32_8x4_cortexa7 8 32 11 0.000005611833179795977 +armv7neon_mmm_f32_8x4_generic 17 128 12 0.00003481391410825688 +armv7neon_mmm_f32_8x6_cortexa7 17 4 19 0.00000928821589263059 +armv7neon_mmm_f32_8x6_generic 8 4 19 0.00000313513885546113 +generic_f32_4x4 11 32 11 0.000012240954381721343 +armv7neon_mmm_f32_8x4_cortexa9 9 128 8 0.000015834005248731945 +armv7neon_mmm_f32_8x6_generic 25 128 7 0.00004137887977916487 +armv7neon_mmm_f32_8x6_cortexa7 25 32 13 0.000025709475387022286 +armv7neon_mmm_f32_8x6_cortexa7 24 32 5 0.000007312970559712554 +armv7neon_mmm_f32_8x4_cortexa9 7 32 3 0.0000019121426586981612 +armv7neon_mmm_f32_8x4_cortexa9 8 4 12 0.0000020507155861457753 +armv7neon_mmm_f32_8x4_cortexa9 25 128 4 0.000015694825557140796 +armv7neon_mmm_f32_8x6_cortexa9 9 4 17 0.0000048654285121987666 +armv7neon_mmm_f32_8x4_generic 15 32 13 0.000011594181812430167 +generic_f32_4x4 8 4 5 0.0000029234671714958714 +armv7neon_mmm_f32_8x6_generic 25 128 18 0.00006398210071667638 +armv7neon_mmm_f32_8x4_generic 25 32 7 0.000011481869355677313 +armv7neon_mmm_f32_8x6_cortexa7 15 32 13 0.000013835762311238379 +armv7neon_mmm_f32_8x6_generic 7 32 7 0.000004270239691930375 +armv7neon_mmm_f32_8x4_cortexa9 24 4 12 0.0000052513903304867065 +armv7neon_mmm_f32_8x4_cortexa7 7 128 13 0.000022090489564676277 +armv7neon_mmm_f32_8x4_cortexa7 9 32 7 0.0000074510499136902 +armv7neon_mmm_f32_8x4_generic 15 128 11 0.00002405185440078953 +armv7neon_mmm_f32_8x6_cortexa7 24 128 18 0.00006059904552298362 +armv7neon_mmm_f32_8x4_cortexa9 24 4 8 0.0000036249029546823233 +armv7neon_mmm_f32_8x4_generic 17 4 5 0.000004186211638893127 +armv7neon_mmm_f32_8x4_cortexa7 15 128 5 0.00002190540588603946 +armv7neon_mmm_f32_8x6_cortexa7 9 32 5 0.000004913938240718097 +armv7neon_mmm_f32_8x4_cortexa7 25 128 4 0.000021428570118948462 +armv7neon_mmm_f32_8x6_generic 9 4 17 0.000004759335274456197 +armv7neon_mmm_f32_8x4_cortexa9 23 32 5 0.000008792076478618836 +armv7neon_mmm_f32_8x4_cortexa9 17 128 13 0.00004665611088212803 +generic_f32_4x4 11 128 12 0.00003275274568553372 +armv7neon_mmm_f32_8x6_cortexa7 7 128 12 0.000014620372931092478 +armv7neon_mmm_f32_8x6_generic 24 128 13 0.000046299674326571746 +generic_f32_4x4 12 4 9 0.000005632703760211813 +armv7neon_mmm_f32_8x6_cortexa7 15 32 6 0.000004965054605236343 +armv7neon_mmm_f32_8x6_cortexa9 7 128 17 0.000016538122105202102 +armv7neon_mmm_f32_8x6_cortexa9 17 128 7 0.00003112401925909139 +armv7neon_mmm_f32_8x4_cortexa7 7 32 5 0.0000040832664009078 +armv7neon_mmm_f32_8x4_cortexa9 25 4 3 0.000003352094433744027 +armv7neon_mmm_f32_8x4_cortexa7 17 4 4 0.0000023752236037367475 +armv7neon_mmm_f32_8x4_cortexa9 8 4 4 0.000000986252516977325 +armv7neon_mmm_f32_8x6_cortexa7 9 4 5 0.0000022147026524486433 +armv7neon_mmm_f32_8x6_cortexa9 24 4 13 0.000006569367159583456 +armv7neon_mmm_f32_8x6_cortexa9 25 32 13 0.000020818742897252734 +armv7neon_mmm_f32_8x4_cortexa9 15 32 12 0.000008751598473767715 +armv7neon_mmm_f32_8x4_cortexa7 7 4 4 0.000001249360695162341 +armv7neon_mmm_f32_8x6_generic 25 4 19 0.000011234098334898354 +generic_f32_4x4 7 4 9 0.000004262590318570047 +generic_f32_4x4 12 128 9 0.000032724926249473206 +armv7neon_mmm_f32_8x4_cortexa7 23 4 11 0.0000067800221845095914 +armv7neon_mmm_f32_8x4_cortexa7 23 4 13 0.00000857826635505948 +armv7neon_mmm_f32_8x4_generic 7 32 3 0.0000019119582227554076 +armv7neon_mmm_f32_8x6_cortexa9 25 4 12 0.000005714183543712814 +generic_f32_4x4 7 128 8 0.000014963178444176004 +armv7neon_mmm_f32_8x6_generic 23 4 11 0.000005289174016444985 +armv7neon_mmm_f32_8x4_generic 15 4 7 0.000003212917021733202 +armv7neon_mmm_f32_8x4_cortexa9 17 4 4 0.000002157304549786207 +armv7neon_mmm_f32_8x4_cortexa7 25 4 8 0.000005296217018377082 +armv7neon_mmm_f32_8x4_cortexa7 16 4 8 0.000002789622045679376 +armv7neon_mmm_f32_8x4_cortexa9 9 32 11 0.00000862398264754348 +armv7neon_mmm_f32_8x6_generic 16 4 18 0.000004182651246093671 +armv7neon_mmm_f32_8x4_cortexa9 7 4 7 0.0000019258225946178918 +armv7neon_mmm_f32_8x4_generic 7 32 13 0.0000063109264345399555 +armv7neon_mmm_f32_8x6_generic 15 32 7 0.000007649176449323901 +armv7neon_mmm_f32_8x4_cortexa7 9 4 13 0.00000566559429146675 +generic_f32_4x4 11 4 13 0.00000768804413911549 +armv7neon_mmm_f32_8x6_cortexa7 15 128 19 0.000055484906874277455 +armv7neon_mmm_f32_8x6_generic 8 128 19 0.000020785294814215668 +armv7neon_mmm_f32_8x4_cortexa7 23 32 7 0.000011137926376204002 +armv7neon_mmm_f32_8x4_cortexa9 25 32 4 0.000005634291322373054 +armv7neon_mmm_f32_8x4_cortexa7 16 4 11 0.0000043943657333441155 +generic_f32_4x4 3 128 4 0.000004206283247834624 +armv7neon_mmm_f32_8x6_cortexa9 23 32 17 0.000016635289447796483 +armv7neon_mmm_f32_8x6_cortexa7 23 4 5 0.0000032303127776050868 +generic_f32_4x4 7 128 7 0.000015111291354960824 +armv7neon_mmm_f32_8x6_cortexa7 8 128 19 0.000027387915918929853 +armv7neon_mmm_f32_8x4_cortexa7 24 32 9 0.000015681272955767534 +armv7neon_mmm_f32_8x6_cortexa7 15 32 7 0.000009363971820493224 +armv7neon_mmm_f32_8x6_generic 15 4 17 0.000005464908042174986 +generic_f32_4x4 11 4 4 0.000002266320385947867 +armv7neon_mmm_f32_8x4_cortexa7 7 4 5 0.0000019827556786453945 +armv7neon_mmm_f32_8x4_generic 7 4 5 0.0000018753720795687383 +armv7neon_mmm_f32_8x6_cortexa7 23 32 19 0.000026555363914009545 +generic_f32_4x4 12 32 11 0.000011904804943326893 +generic_f32_4x4 9 32 4 0.000004278605538448279 +armv7neon_mmm_f32_8x4_cortexa9 9 4 3 0.0000018374246302226002 +armv7neon_mmm_f32_8x4_generic 17 128 11 0.00003530676669688071 +armv7neon_mmm_f32_8x4_cortexa7 23 32 5 0.000011014184598781204 +armv7neon_mmm_f32_8x4_cortexa7 8 128 8 0.000010889817517963246 +armv7neon_mmm_f32_8x4_generic 24 128 5 0.000023658482563487358 +armv7neon_mmm_f32_8x4_generic 23 4 4 0.0000022865778486536074 +armv7neon_mmm_f32_8x6_cortexa7 16 32 7 0.000008963466531834949 +armv7neon_mmm_f32_8x4_cortexa7 16 128 11 0.00003220406926334493 +armv7neon_mmm_f32_8x4_cortexa9 7 4 9 0.000002612401333065891 +armv7neon_mmm_f32_8x6_generic 24 128 18 0.00004570910076801179 +armv7neon_mmm_f32_8x6_cortexa7 15 32 19 0.00001825949129411215 +armv7neon_mmm_f32_8x4_cortexa7 7 128 11 0.00001669033881154567 +armv7neon_mmm_f32_8x6_cortexa7 17 128 17 0.00006152323359147728 +armv7neon_mmm_f32_8x6_generic 23 4 12 0.0000049613667404571205 +armv7neon_mmm_f32_8x6_cortexa9 17 4 6 0.0000025304413562385254 +armv7neon_mmm_f32_8x6_generic 25 32 13 0.000020634077057855525 +generic_f32_4x4 3 128 8 0.000007895048382688004 +armv7neon_mmm_f32_8x6_generic 7 128 6 0.000005880420229157795 +armv7neon_mmm_f32_8x6_cortexa7 7 4 6 0.0000015435048498918165 +armv7neon_mmm_f32_8x6_cortexa9 15 4 12 0.0000038035548284711065 +armv7neon_mmm_f32_8x4_generic 16 128 13 0.00003105986968794038 +armv7neon_mmm_f32_8x6_generic 17 128 6 0.00001561934075445538 +armv7neon_mmm_f32_8x6_generic 23 32 5 0.000006038048456367719 +generic_f32_4x4 3 4 8 0.0000018887559388867697 +armv7neon_mmm_f32_8x6_cortexa7 17 128 5 0.000021122166031945488 +armv7neon_mmm_f32_8x4_cortexa7 17 4 12 0.000005984255865199454 +armv7neon_mmm_f32_8x6_cortexa9 8 4 17 0.00000262350755833462 +armv7neon_mmm_f32_8x6_generic 24 32 17 0.000015705211312591065 +armv7neon_mmm_f32_8x6_generic 23 32 7 0.00001103425525544337 +armv7neon_mmm_f32_8x6_generic 23 128 18 0.00004698363257869806 +armv7neon_mmm_f32_8x4_cortexa7 9 128 4 0.000010997242917041898 +generic_f32_4x4 12 32 9 0.000011855594903877275 +armv7neon_mmm_f32_8x4_cortexa7 8 32 4 0.000002107136592547487 +armv7neon_mmm_f32_8x4_cortexa7 9 32 4 0.0000038494164534325605 +generic_f32_4x4 8 32 4 0.0000029596097342538777 +armv7neon_mmm_f32_8x6_cortexa9 7 4 13 0.0000033183395006378194 +armv7neon_mmm_f32_8x4_cortexa9 7 128 5 0.00000837292374457436 +armv7neon_mmm_f32_8x6_cortexa7 9 32 12 0.000008879244649291164 +armv7neon_mmm_f32_8x6_cortexa9 23 32 5 0.00000608726564780247 +generic_f32_4x4 4 4 11 0.000002253269465041155 +armv7neon_mmm_f32_8x6_generic 15 4 12 0.000003737614965115773 +armv7neon_mmm_f32_8x4_cortexa7 8 32 13 0.000007179427212160366 +armv7neon_mmm_f32_8x6_generic 7 4 7 0.00000227244051714339 +armv7neon_mmm_f32_8x6_generic 7 128 13 0.000016430484681121403 +armv7neon_mmm_f32_8x4_cortexa9 25 4 13 0.000009867842979190694 +armv7neon_mmm_f32_8x4_cortexa9 24 32 7 0.000008714828341166468 +armv7neon_mmm_f32_8x6_generic 7 128 12 0.00001125899429533851 +armv7neon_mmm_f32_8x4_cortexa7 24 128 3 0.000016779227724494087 +armv7neon_mmm_f32_8x6_cortexa7 25 32 11 0.000017700250695390733 +armv7neon_mmm_f32_8x6_cortexa9 8 128 17 0.00001578057628845984 +armv7neon_mmm_f32_8x6_cortexa9 15 128 18 0.00003176376886582956 +armv7neon_mmm_f32_8x6_generic 7 32 18 0.000006481310483268657 +armv7neon_mmm_f32_8x6_generic 24 32 19 0.000020260301856480532 +armv7neon_mmm_f32_8x6_cortexa7 25 4 7 0.000006556992855989281 +armv7neon_mmm_f32_8x4_cortexa7 9 4 8 0.0000030004666340701197 +armv7neon_mmm_f32_8x6_generic 8 4 13 0.0000025029052985754286 +armv7neon_mmm_f32_8x4_cortexa7 16 32 13 0.00001386814401860724 +armv7neon_mmm_f32_8x4_generic 16 128 3 0.000008461514063719399 +generic_f32_4x4 8 32 5 0.000005666217550078829 +armv7neon_mmm_f32_8x4_generic 17 32 9 0.000012567178399696468 +armv7neon_mmm_f32_8x4_cortexa7 15 128 9 0.00003252514906200416 +armv7neon_mmm_f32_8x6_generic 25 128 13 0.00006413295578186804 +armv7neon_mmm_f32_8x6_generic 7 4 11 0.0000023405037923286433 +generic_f32_4x4 7 32 12 0.000008239110132919408 +armv7neon_mmm_f32_8x6_cortexa9 23 128 6 0.00001597150399238562 +armv7neon_mmm_f32_8x4_generic 25 4 13 0.000009863674729272788 +armv7neon_mmm_f32_8x4_cortexa7 16 128 5 0.00002172651909764639 +armv7neon_mmm_f32_8x6_cortexa7 15 32 12 0.000009468415752921276 +armv7neon_mmm_f32_8x6_cortexa9 24 4 11 0.000004968600626897013 +armv7neon_mmm_f32_8x4_generic 24 128 13 0.00004635289274725685 +armv7neon_mmm_f32_8x6_generic 23 128 5 0.000016196412852382603 +armv7neon_mmm_f32_8x6_cortexa7 16 32 13 0.000013024830178376121 +armv7neon_mmm_f32_8x4_cortexa9 9 32 3 0.0000033151175658832473 +armv7neon_mmm_f32_8x6_generic 7 4 13 0.000003265379138781528 +armv7neon_mmm_f32_8x4_cortexa9 17 32 13 0.000016459192383496957 +armv7neon_mmm_f32_8x6_cortexa7 24 32 13 0.000019272536127977423 +generic_f32_4x4 4 32 3 0.0000019007760051192194 +generic_f32_4x4 9 128 8 0.000021954328754919918 +armv7neon_mmm_f32_8x4_cortexa7 8 32 12 0.000005371382475600821 +generic_f32_4x4 7 4 3 0.000001903414095718297 +armv7neon_mmm_f32_8x6_generic 24 4 7 0.000004666010098240403 +armv7neon_mmm_f32_8x4_cortexa9 16 32 8 0.000005563656358158919 +generic_f32_4x4 3 32 9 0.000004602770059818119 +armv7neon_mmm_f32_8x4_generic 9 32 13 0.000011194068458304182 +armv7neon_mmm_f32_8x4_cortexa9 15 32 11 0.000008943973907120027 +generic_f32_4x4 11 128 7 0.000022328923764966492 +armv7neon_mmm_f32_8x6_generic 8 128 5 0.000005731544283554465 +armv7neon_mmm_f32_8x6_cortexa9 8 128 11 0.00001076946555010939 +armv7neon_mmm_f32_8x4_cortexa7 7 128 5 0.000011229094508072489 +armv7neon_mmm_f32_8x4_cortexa9 15 128 9 0.000023961524252030835 +generic_f32_4x4 11 128 5 0.000022274598932900346 +generic_f32_4x4 5 128 3 0.000007857636386132928 +generic_f32_4x4 4 128 7 0.00000774651849385228 +armv7neon_mmm_f32_8x4_cortexa9 15 128 5 0.000016175167728379695 +armv7neon_mmm_f32_8x6_cortexa7 17 4 5 0.0000031159889361188713 +generic_f32_4x4 12 4 11 0.000005685376438998431 +armv7neon_mmm_f32_8x4_cortexa7 8 32 5 0.000003934735618425319 +generic_f32_4x4 4 32 4 0.0000017328412461691005 +armv7neon_mmm_f32_8x4_cortexa7 8 128 11 0.000016332637899779713 +armv7neon_mmm_f32_8x6_cortexa9 15 32 17 0.000011527836971780865 +armv7neon_mmm_f32_8x4_generic 15 4 9 0.000004398255246018968 +armv7neon_mmm_f32_8x4_cortexa9 15 32 8 0.000005965121319547414 +armv7neon_mmm_f32_8x6_cortexa7 8 4 11 0.0000020902150325449643 +armv7neon_mmm_f32_8x6_cortexa7 15 4 13 0.000005648201530441972 +armv7neon_mmm_f32_8x6_cortexa9 23 128 18 0.00004690308298866514 +armv7neon_mmm_f32_8x6_cortexa7 17 128 18 0.00006100420785992372 +generic_f32_4x4 3 32 3 0.000001876030465879534 +generic_f32_4x4 4 128 8 0.000007586370052411088 +armv7neon_mmm_f32_8x6_cortexa9 8 128 18 0.000015532022218663232 +armv7neon_mmm_f32_8x4_cortexa7 17 4 11 0.000006454744732596089 +armv7neon_mmm_f32_8x6_cortexa9 15 4 11 0.0000038999665208551116 +armv7neon_mmm_f32_8x6_cortexa9 25 128 17 0.00006218409021295646 +armv7neon_mmm_f32_8x6_cortexa7 9 128 7 0.000027592791728379908 +armv7neon_mmm_f32_8x4_cortexa7 17 128 7 0.0000324174579838398 +armv7neon_mmm_f32_8x6_generic 17 32 13 0.00001566115346007812 +generic_f32_4x4 9 4 4 0.0000022019063671444208 +armv7neon_mmm_f32_8x4_cortexa9 17 128 11 0.0000353036171900936 +armv7neon_mmm_f32_8x4_cortexa7 17 32 12 0.0000154170992202297 +generic_f32_4x4 4 4 12 0.0000020915219079462592 +armv7neon_mmm_f32_8x4_generic 16 4 3 0.000001964189345127758 +generic_f32_4x4 11 4 3 0.0000025875707471480883 +armv7neon_mmm_f32_8x4_cortexa7 24 32 8 0.000010240674992864977 +armv7neon_mmm_f32_8x4_cortexa7 24 4 7 0.00000464383472294094 +armv7neon_mmm_f32_8x6_generic 24 128 17 0.00004653762565754179 +armv7neon_mmm_f32_8x4_cortexa9 9 128 5 0.000015984472800286198 +armv7neon_mmm_f32_8x6_cortexa9 17 32 13 0.000015793322899399953 +armv7neon_mmm_f32_8x4_cortexa7 23 128 9 0.00004832080937818461 +armv7neon_mmm_f32_8x6_generic 17 4 12 0.0000043886752215018795 +armv7neon_mmm_f32_8x6_generic 24 4 17 0.000006718970310010203 +armv7neon_mmm_f32_8x6_cortexa9 23 4 12 0.000005053037256901505 +armv7neon_mmm_f32_8x4_generic 25 128 13 0.00006314133455535656 +generic_f32_4x4 9 128 7 0.000022234980219629062 +armv7neon_mmm_f32_8x6_cortexa7 7 128 7 0.000014376478927807395 +armv7neon_mmm_f32_8x6_cortexa7 16 128 5 0.0000143212403106337 +armv7neon_mmm_f32_8x6_generic 25 32 6 0.000007045905360966285 +armv7neon_mmm_f32_8x6_generic 25 32 17 0.000020892213993308648 +armv7neon_mmm_f32_8x6_cortexa9 24 32 7 0.000010711106258433335 +armv7neon_mmm_f32_8x4_cortexa7 15 32 7 0.000007644594488957809 +armv7neon_mmm_f32_8x4_generic 9 32 7 0.0000059612924557323865 +armv7neon_mmm_f32_8x4_cortexa7 24 32 5 0.000010813567327083286 +generic_f32_4x4 11 32 9 0.000012181640310257489 +armv7neon_mmm_f32_8x4_generic 8 32 11 0.0000044856528446547376 +armv7neon_mmm_f32_8x6_cortexa7 8 128 5 0.00000740698393210008 +armv7neon_mmm_f32_8x6_generic 9 4 13 0.00000467415217244372 +armv7neon_mmm_f32_8x4_cortexa9 8 32 5 0.0000031557846756887957 +armv7neon_mmm_f32_8x6_generic 9 32 6 0.000003833293115622011 +generic_f32_4x4 5 128 11 0.000022197811800866368 +armv7neon_mmm_f32_8x4_cortexa7 8 4 8 0.000001630228451378558 +armv7neon_mmm_f32_8x6_cortexa7 7 128 18 0.00002168993830362326 +armv7neon_mmm_f32_8x4_generic 16 4 7 0.000003012988093329698 +armv7neon_mmm_f32_8x6_generic 9 32 5 0.0000040586320817732015 +armv7neon_mmm_f32_8x4_generic 8 128 11 0.000012020324272970633 +armv7neon_mmm_f32_8x4_cortexa9 9 4 13 0.00000525401614258308 +armv7neon_mmm_f32_8x6_cortexa9 17 32 5 0.000005959198344084862 +armv7neon_mmm_f32_8x6_cortexa9 25 128 7 0.00004136188393626292 +armv7neon_mmm_f32_8x6_cortexa7 8 4 6 0.0000011677036715350285 +armv7neon_mmm_f32_8x6_cortexa7 7 32 11 0.000005167860829086469 +armv7neon_mmm_f32_8x4_generic 24 4 11 0.000005847812768700571 +armv7neon_mmm_f32_8x4_generic 17 128 8 0.0000233987853798507 +armv7neon_mmm_f32_8x4_cortexa9 7 128 8 0.000008435297227106425 +armv7neon_mmm_f32_8x4_generic 8 4 4 0.000000981928574441578 +generic_f32_4x4 13 32 8 0.000010469226231367714 +generic_f32_4x4 12 4 7 0.000004168142600643075 +armv7neon_mmm_f32_8x6_generic 25 128 5 0.000021400005561991285 +armv7neon_mmm_f32_8x6_cortexa9 23 128 19 0.00006309986803011645 +armv7neon_mmm_f32_8x6_cortexa7 17 32 5 0.000007188792393610533 +armv7neon_mmm_f32_8x6_cortexa9 7 32 17 0.00000637568429598606 +armv7neon_mmm_f32_8x6_cortexa9 25 32 19 0.000027309010551769435 +armv7neon_mmm_f32_8x4_cortexa7 7 32 8 0.000004145572586004413 +armv7neon_mmm_f32_8x4_cortexa9 24 32 9 0.000012468417684719209 +armv7neon_mmm_f32_8x6_cortexa7 23 128 7 0.00004157442562492112 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64.rs new file mode 100644 index 0000000..7841870 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64.rs @@ -0,0 +1,542 @@ +#![allow(clippy::excessive_precision)] +#[cfg(any(target_os = "macos", all(target_os = "ios", feature = "apple-amx-ios")))] +mod apple_amx; +mod arm64simd; +pub mod cortex_a53; +mod cortex_a55; +// `tract_sme` is set by build.rs only when the assembler can assemble SME +// (gates out e.g. the old Debian stretch aarch64 toolchain). +#[cfg(all(any(target_os = "macos", target_os = "linux"), tract_sme))] +mod sme; +mod sve; +//mod cortex_a72; +//mod cortex_a73; +pub use arm64simd::*; + +#[cfg(not(feature = "no_fp16"))] +pub mod arm64fp16; +#[cfg(not(feature = "no_fp16"))] +pub use arm64fp16::*; + +use crate::f16; +use crate::{BinOp, DatumType, LinalgRegistry, Ops}; + +use crate::frame::by_scalar::ByScalarKer; +use crate::frame::element_wise::ElementWiseKer; +use crate::frame::reduce::{MapReduceKer, ReduceKer}; +use crate::frame::unicast::UnicastKer; + +// https://en.wikipedia.org/wiki/Comparison_of_ARMv8-A_cores +const PART_A53: &str = "0xd03"; +const PART_A55: &str = "0xd05"; +#[allow(dead_code)] +const PART_A72: &str = "0xd08"; +#[allow(dead_code)] +const PART_A73: &str = "0xd09"; +#[allow(dead_code)] +const PART_A75: &str = "0xd0a"; +#[allow(dead_code)] +const PART_NEOVERSE_N1: &str = "0xd0c"; +#[allow(dead_code)] +const PART_NEOVERSE_N2: &str = "0xd49"; +#[allow(dead_code)] +const PART_NEOVERSE_N3: &str = "0xd8e"; +#[allow(dead_code)] +const PART_NEOVERSE_V1: &str = "0xd40"; +#[allow(dead_code)] +const PART_NEOVERSE_V2: &str = "0xd4f"; +#[allow(dead_code)] +const PART_NEOVERSE_V3: &str = "0xd83"; + +fn max_cpuid() -> std::io::Result { + let cpu_info = std::fs::read_to_string("/proc/cpuinfo")?; + let max = cpu_info + .lines() + .filter(|line| line.starts_with("CPU part")) + .map(|line| line.split_whitespace().last().unwrap_or("")) + .max(); + Ok(max.unwrap_or("").to_string()) +} + +lazy_static::lazy_static! { + static ref KIND: Kind = Kind::choose(); + + static ref CPU_FEATURES: Vec = { + #[cfg(test)] crate::setup_test_logger(); + let Ok(cpu_info) = std::fs::read_to_string("/proc/cpuinfo") else { + log::warn!("Could not read /proc/cpuinfo. CPU Features detection may be impaired."); + return vec!(); + }; + if let Some(line) = cpu_info + .lines() + .find(|line| line.starts_with("Features")) { + line.split_once(':').unwrap().1.split_whitespace().map(|s| s.to_string()).collect() + } else { + log::warn!("Could not find \"Features :\" lines in /proc/cpuinfo. CPU Features detection may be impaired."); + vec!() + } + }; + + static ref HAS_FP16: bool = { + CPU_FEATURES.iter().any(|s| &**s == "asimdhp") + }; +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +fn apple_string_from_c_bytes(buf: &[u8]) -> String { + use std::ffi::CStr; + + CStr::from_bytes_until_nul(buf) + .ok() + .map(|s| s.to_string_lossy().into_owned()) + .unwrap_or_default() +} + +#[cfg(any(target_os = "macos", target_os = "ios"))] +fn apple_get_syscall(key: &str) -> String { + use std::ffi::{CString, c_char, c_int, c_void}; + use std::ptr::null_mut; + + unsafe extern "C" { + fn sysctlbyname( + name: *const c_char, + oldp: *mut c_void, + oldlenp: *mut usize, + newp: *mut c_void, + newlen: usize, + ) -> c_int; + } + + let Ok(name) = CString::new(key) else { + return String::new(); + }; + + unsafe { + let mut len_needed: usize = 0; + if sysctlbyname(name.as_ptr(), null_mut(), &mut len_needed, null_mut(), 0) != 0 { + return String::new(); + } + + let mut buf = vec![0u8; len_needed.saturating_add(1)]; + let mut len: usize = buf.len(); + if sysctlbyname( + name.as_ptr(), + buf.as_mut_ptr() as _, + &mut len, + null_mut(), + 0, + ) != 0 + { + return String::new(); + } + + buf.truncate(len.min(buf.len())); + if buf.last().copied() != Some(0) { + buf.push(0); + } + + apple_string_from_c_bytes(&buf) + } +} + +#[cfg(all(test, any(target_os = "macos", target_os = "ios")))] +mod tests { + use super::*; + + #[test] + fn apple_string_from_c_bytes_returns_empty_without_nul() { + assert_eq!(apple_string_from_c_bytes(b"hello"), ""); + } + + #[test] + fn apple_string_from_c_bytes_stops_at_first_nul() { + assert_eq!(apple_string_from_c_bytes(b"hello\0world\0"), "hello"); + } + + #[test] + fn apple_get_syscall_does_not_panic() { + let _ = apple_get_syscall("machdep.cpu.brand_string"); + } +} + +#[cfg(target_os = "macos")] +pub fn has_amx() -> bool { + !apple_get_syscall("machdep.cpu.brand_string").contains("(Virtual)") +} + +#[cfg(target_os = "ios")] +lazy_static::lazy_static! { + static ref IPHONE_MODEL_MAJOR:Option = { + let version = apple_get_syscall("hw.machine"); + let Some((major, _)) = version.trim_start_matches("iPhone").split_once(",") else { return None }; + major.parse::().ok() + }; +} + +#[cfg(all(target_os = "ios", feature = "apple-amx-ios"))] +fn has_amx() -> bool { + // iPhone12,1 is the one branded "iPhone 11", with Apple A13 bionic, first CPU featuring amx + IPHONE_MODEL_MAJOR.map(|it| it >= 12).unwrap_or(false) +} + +#[inline] +#[cfg(target_os = "ios")] +pub fn has_fp16() -> bool { + // iPhone10,1 is the one branded "iPhone 8", with Apple A11 bionic, first CPU featuring fp16 + IPHONE_MODEL_MAJOR.map(|it| it >= 10).unwrap_or(false) +} + +#[inline] +#[cfg(not(target_os = "ios"))] +pub fn has_fp16() -> bool { + cfg!(target_os = "macos") + || cfg!(feature_cpu = "fp16") + || *KIND == Kind::CortexA55 + || *KIND == Kind::CortexA75 + || *HAS_FP16 +} + +// FEAT_DotProd (SDOT/UDOT), ARMv8.2. TRACT_DOTPROD_DISABLE=1 forces it off so +// callers can A/B the SDOT kernel against the SMLAL 8x8 fallback on one binary. +#[cfg(target_os = "macos")] +pub fn has_dotprod() -> bool { + // Every Apple arm64 CPU (M1+/A11+) implements FEAT_DotProd. + std::env::var_os("TRACT_DOTPROD_DISABLE").is_none() +} + +#[cfg(target_os = "linux")] +pub fn has_dotprod() -> bool { + if std::env::var_os("TRACT_DOTPROD_DISABLE").is_some() { + return false; + } + // HWCAP_ASIMDDP = 1 << 20 on aarch64. + const HWCAP_ASIMDDP: u64 = 1 << 20; + const AT_HWCAP: u64 = 16; + unsafe extern "C" { + fn getauxval(t: u64) -> u64; + } + unsafe { (getauxval(AT_HWCAP) & HWCAP_ASIMDDP) != 0 } +} + +#[cfg(not(any(target_os = "macos", target_os = "linux", target_os = "ios")))] +pub fn has_dotprod() -> bool { + false +} + +#[cfg(target_os = "ios")] +pub fn has_dotprod() -> bool { + // A11+ (iPhone10,1+) implement FEAT_DotProd. + std::env::var_os("TRACT_DOTPROD_DISABLE").is_none() + && IPHONE_MODEL_MAJOR.map(|it| it >= 10).unwrap_or(false) +} + +#[target_feature(enable = "fp16")] +#[inline] +pub unsafe fn add_f16(a: f16, b: f16) -> f16 { + unsafe { + let result: u16; + std::arch::asm!( + "fadd {0:h}, {1:h}, {2:h}", + lateout(vreg) result, + in(vreg) a.to_bits(), + in(vreg) b.to_bits(), + options(pure, nomem, nostack, preserves_flags)); + f16::from_bits(result) + } +} + +#[target_feature(enable = "fp16")] +#[inline] +pub unsafe fn mul_f16(a: f16, b: f16) -> f16 { + unsafe { + let result: u16; + std::arch::asm!( + "fmul {0:h}, {1:h}, {2:h}", + lateout(vreg) result, + in(vreg) a.to_bits(), + in(vreg) b.to_bits(), + options(pure, nomem, nostack, preserves_flags)); + f16::from_bits(result) + } +} + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +pub enum Kind { + Generic, + AppleM, + Neoverse, + CortexA53, + CortexA55, + CortexA72, + CortexA73, + CortexA75, +} + +impl Kind { + pub fn choose() -> Kind { + #[cfg(test)] + crate::setup_test_logger(); + let kind = if let Ok(kind) = std::env::var("TRACT_CPU_AARCH64_KIND") { + log::info!("CPU kind forced with TRACT_CPU_AARCH64_KIND: {}", kind); + let kind = kind.to_lowercase(); + if kind.contains("a53") { + Kind::CortexA53 + } else if kind.contains("a55") { + Kind::CortexA55 + } else if kind.contains("a72") { + Kind::CortexA72 + } else if kind.contains("a73") { + Kind::CortexA73 + } else if kind.contains("a75") { + Kind::CortexA75 + } else if kind.contains("neoverse") { + Kind::Neoverse + } else if kind.contains("applem") { + Kind::AppleM + } else { + Kind::Generic + } + } else if cfg!(target_os = "macos") { + Kind::AppleM + } else { + let part = if let Ok(part) = std::env::var("TRACT_CPU_AARCH64_OVERRIDE_CPU_PART") { + log::info!( + "CPU part forced with TRACT_CPU_AARCH64_OVERRIDE_CPU_PART: {}", + part + ); + part + } else if cfg!(target_os = "linux") { + let part = max_cpuid().unwrap_or_else(|_| "0x00".to_string()); + log::info!("CPU part auto detected: {}", part); + part + } else { + log::info!("Unknown CPU part"); + "0x00".to_string() + }; + match &*part { + PART_A53 => Kind::CortexA53, + PART_A55 => Kind::CortexA55, + PART_A72 => Kind::CortexA72, + PART_A73 => Kind::CortexA73, + PART_A75 => Kind::CortexA75, + PART_NEOVERSE_N1 | PART_NEOVERSE_N2 | PART_NEOVERSE_N3 | PART_NEOVERSE_V1 + | PART_NEOVERSE_V2 | PART_NEOVERSE_V3 => Kind::Neoverse, + _ => Kind::Generic, + } + }; + log::info!("CPU optimisation: {:?}", kind); + kind + } +} + +pub(crate) fn register_all_unicast(registry: &mut LinalgRegistry) { + registry.insert( + (BinOp::Mul, DatumType::F32), + Box::new(|| arm64simd_unicast_mul_f32_16n::bin()), + ); + registry.insert( + (BinOp::Mul, DatumType::F16), + Box::new(|| arm64fp16_unicast_mul_f16_32n::bin()), + ); + registry.insert( + (BinOp::Add, DatumType::F32), + Box::new(|| arm64simd_unicast_add_f32_16n::bin()), + ); + registry.insert( + (BinOp::Add, DatumType::F16), + Box::new(|| arm64fp16_unicast_add_f16_32n::bin()), + ); + registry.insert( + (BinOp::Sub, DatumType::F32), + Box::new(|| arm64simd_unicast_sub_f32_16n::bin()), + ); + registry.insert( + (BinOp::Sub, DatumType::F16), + Box::new(|| arm64fp16_unicast_sub_f16_32n::bin()), + ); + registry.insert( + (BinOp::SubF, DatumType::F32), + Box::new(|| arm64simd_unicast_subf_f32_16n::bin()), + ); + registry.insert( + (BinOp::SubF, DatumType::F16), + Box::new(|| arm64fp16_unicast_subf_f16_32n::bin()), + ); + registry.insert( + (BinOp::Min, DatumType::F32), + Box::new(|| arm64simd_unicast_min_f32_16n::bin()), + ); + registry.insert( + (BinOp::Min, DatumType::F16), + Box::new(|| arm64fp16_unicast_min_f16_32n::bin()), + ); + registry.insert( + (BinOp::Max, DatumType::F32), + Box::new(|| arm64simd_unicast_max_f32_16n::bin()), + ); + registry.insert( + (BinOp::Max, DatumType::F16), + Box::new(|| arm64fp16_unicast_max_f16_32n::bin()), + ); +} + +pub(crate) fn register_all_by_scalar(registry: &mut LinalgRegistry) { + registry.insert( + (BinOp::Mul, DatumType::F32), + Box::new(|| arm64simd_mul_by_scalar_f32_16n::bin()), + ); + registry.insert( + (BinOp::Mul, DatumType::F16), + Box::new(|| arm64fp16_mul_by_scalar_f16_32n::bin()), + ); + registry.insert( + (BinOp::Add, DatumType::F32), + Box::new(|| arm64simd_add_by_scalar_f32_16n::bin()), + ); + registry.insert( + (BinOp::Add, DatumType::F16), + Box::new(|| arm64fp16_add_by_scalar_f16_32n::bin()), + ); + registry.insert( + (BinOp::Sub, DatumType::F32), + Box::new(|| arm64simd_sub_by_scalar_f32_16n::bin()), + ); + registry.insert( + (BinOp::Sub, DatumType::F16), + Box::new(|| arm64fp16_sub_by_scalar_f16_32n::bin()), + ); + registry.insert( + (BinOp::SubF, DatumType::F32), + Box::new(|| arm64simd_subf_by_scalar_f32_16n::bin()), + ); + registry.insert( + (BinOp::SubF, DatumType::F16), + Box::new(|| arm64fp16_subf_by_scalar_f16_32n::bin()), + ); + registry.insert( + (BinOp::Min, DatumType::F32), + Box::new(|| arm64simd_min_by_scalar_f32_16n::bin()), + ); + registry.insert( + (BinOp::Min, DatumType::F16), + Box::new(|| arm64fp16_min_by_scalar_f16_32n::bin()), + ); + registry.insert( + (BinOp::Max, DatumType::F32), + Box::new(|| arm64simd_max_by_scalar_f32_16n::bin()), + ); + registry.insert( + (BinOp::Max, DatumType::F16), + Box::new(|| arm64fp16_max_by_scalar_f16_32n::bin()), + ); +} + +pub fn plug(ops: &mut Ops) { + arm64simd::plug(ops); + + #[cfg(not(feature = "no_fp16"))] + if has_fp16() { + arm64fp16::plug(ops); + } + + // SDOT (~4x the SMLAL 8x8) when FEAT_DotProd is present, else the SMLAL 8x8 fallback. + // The SDOT kernel only exists when the assembler could encode `sdot` + // (`tract_arm64_dotprod`, set by build.rs); otherwise always use the SMLAL 8x8. + #[cfg(tract_arm64_dotprod)] + if has_dotprod() { + ops.qmmm_i32 = Box::new(|_, _, _| arm64simd_mmm_i32_8x8_dot.mmm()); + } else { + ops.qmmm_i32 = Box::new(|_, _, _| arm64simd_mmm_i32_8x8.mmm()); + } + #[cfg(not(tract_arm64_dotprod))] + { + ops.qmmm_i32 = Box::new(|_, _, _| arm64simd_mmm_i32_8x8.mmm()); + } + ops.qmmv_i32 = Box::new(|_, _| arm64simd_mmm_i32_64x1.mmm()); + ops.mmv_f32 = match *KIND { + Kind::CortexA53 => Box::new(|_, _| arm64simd_mmm_f32_64x1_a53.mmm()), + Kind::CortexA55 => Box::new(|_, _| arm64simd_mmm_f32_64x1_a55.mmm()), + _ => Box::new(|_, _| arm64simd_mmm_f32_64x1_gen.mmm()), + }; + let model = match *KIND { + Kind::CortexA53 => Some(cortex_a53::model()), + Kind::CortexA55 => Some(cortex_a55::model()), + _ => None, + }; + let impls = ops.mmm_impls.clone(); + ops.mmm_f32 = if let Some(model) = model { + Box::new(move |m, k, n| model.pick(&impls, m, k, n)) + } else { + Box::new(move |_, _, n| { + if n.unwrap_or(8) < 8 { + arm64simd_mmm_f32_16x4_gen.mmm() + } else { + arm64simd_mmm_f32_8x8_gen.mmm() + } + }) + }; + #[cfg(feature = "no_fp16")] + if has_fp16() { + log::warn!( + "This is a build with fp16 disabled, while your platform CPU seems to support it." + ); + } + #[cfg(not(feature = "no_fp16"))] + if has_fp16() { + if *KIND == Kind::CortexA55 { + log::info!("Cortex-A55 mmm_f16 and mmv_f16 activated"); + ops.mmm_f16 = Box::new(|_, _, n| { + use tract_data::internal::DimLike; + if n.unwrap_or(1024).divceil(4) * 4 < n.unwrap_or(1024).divceil(8) * 8 { + arm64fp16_mmm_f16_32x4_a55.mmm() + } else { + arm64fp16_mmm_f16_16x8_a55.mmm() + } + }); + ops.mmv_f16 = Box::new(|_, _| arm64fp16_mmm_f16_128x1_a55.mmm()); + } else { + log::info!("ARMv8.2 mmm_f16 and mmv_f16 activated"); + ops.mmm_f16 = Box::new(|_, _, n| { + use tract_data::internal::DimLike; + if n.unwrap_or(1024).divceil(4) * 4 < n.unwrap_or(1024).divceil(8) * 8 { + arm64fp16_mmm_f16_32x4_gen.mmm() + } else { + arm64fp16_mmm_f16_16x8_gen.mmm() + } + }); + ops.mmv_f16 = Box::new(|_, _| arm64fp16_mmm_f16_128x1_gen.mmm()); + } + } + ops.leaky_relu_f32 = Box::new(|| arm64simd_leaky_relu_f32_8n::ew()); + ops.hardswish_f32 = Box::new(|| arm64simd_hardswish_f32_8n::ew()); + ops.silu_f32 = Box::new(|| arm64simd_silu_f32_4n_fused::ew()); + ops.gelu_f32 = Box::new(|| arm64simd_gelu_f32_4n_fused::ew()); + ops.sigmoid_f32 = Box::new(|| arm64simd_sigmoid_f32_4n::ew()); + ops.tanh_f32 = Box::new(|| arm64simd_tanh_f32_4n::ew()); + ops.max_f32 = Box::new(|| arm64simd_max_f32_16n::red()); + ops.sum_f32 = Box::new(|| arm64simd_sum_f32_16n::red()); + ops.mul_by_scalar_f32 = Box::new(|| arm64simd_mul_by_scalar_f32_16n::ew()); + ops.softmax2_fastcompact_f32 = Box::new(|| arm64simd_softmax2_fastcompact_f32_16n::red()); + ops.rms_norm_f32 = Box::new(arm64simd_rms_norm_f32); + #[cfg(not(feature = "no_fp16"))] + if has_fp16() { + log::info!("ARMv8.2 tanh_f16 and sigmoid_f16 activated"); + ops.leaky_relu_f16 = Box::new(|| arm64fp16_leaky_relu_f16_16n::ew()); + ops.tanh_f16 = Box::new(|| arm64fp16_tanh_f16_8n::ew()); + ops.sigmoid_f16 = Box::new(|| arm64fp16_sigmoid_f16_8n::ew()); + ops.max_f16 = Box::new(|| arm64fp16_max_f16_32n::red()); + ops.sum_f16 = Box::new(|| arm64fp16_sum_f16_32n::red()); + ops.mul_by_scalar_f16 = Box::new(|| arm64fp16_mul_by_scalar_f16_32n::ew()); + } else { + log::info!("No native fp16 support"); + } + #[cfg(any(target_os = "macos", all(target_os = "ios", feature = "apple-amx-ios")))] + { + apple_amx::plug(ops); + } + #[cfg(all(any(target_os = "macos", target_os = "linux"), tract_sme))] + { + sme::plug(ops); + } + sve::plug(ops); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/apple_amx.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/apple_amx.rs new file mode 100644 index 0000000..9ed15de --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/apple_amx.rs @@ -0,0 +1,74 @@ +use crate::Ops; +use crate::frame::mmm::ImplementationQuality::ManuallyOptimized; +use crate::mmm::*; +use tract_data::prelude::*; + +use super::has_amx; +use super::{arm64fp16_mmm_f16_16x8_gen, arm64simd_mmm_f32_8x8_gen, arm64simd_mmm_f32_64x1_gen}; + +const AMX: fn() -> bool = crate::arm64::has_amx; +const CAN_FUSE: fn(&FusedSpec) -> bool = |f| !matches!(f, &FusedSpec::LeakyRelu(_)); + +MMMExternKernel!(apple_amx_mmm_f32_32x32(32, 32)@(128, 128) where(AMX) can_fuse(CAN_FUSE) quality(ManuallyOptimized)); +MMMExternKernel!(apple_amx_mmm_f32_32x1(32, 1)@(128, 128) where(AMX) can_fuse(CAN_FUSE) quality(ManuallyOptimized)); +MMMExternKernel!(apple_amx_mmm_f16_64x32(64, 32)@(128, 128) where(AMX) can_fuse(CAN_FUSE) quality(ManuallyOptimized)); +MMMExternKernel!(apple_amx_mmm_f16_64x1(64, 1)@(128, 128) where(AMX) can_fuse(CAN_FUSE) quality(ManuallyOptimized)); + +pub fn plug(ops: &mut Ops) { + if has_amx() { + log::info!( + "AMX optimisation activated (A7v2: AMX only for f32 mmm with M>=32 AND N>=32; \ + smaller shapes + all f32 mmv route to NEON kernels)" + ); + // ----- A7v2 dispatch logic (data-driven) ----- + // + // Empirical finding from /tmp/amx_vs_neon.md microbench (Apple M1 Pro): + // the AMX 32x32 kernel beats NEON 8x8 only when BOTH M and N are at + // least 32 — the AMX tile dimensions. At smaller shapes the per-tile + // padding waste + AMX dispatch overhead make NEON faster. + // + // Predicate validation: 88.3% accuracy on 512-shape sweep. + // + // Canary impact (measured 2026-05-13, see notes/tract-amx-low-m-investigation.md): + // turning AMX off entirely yielded: + // df_dec 1.55× faster mobilenetv2 1.59× faster + // erb_dec 1.49× squeezenet 1.22× + // enc 1.17× yolov8n 1.15× SLOWER + // inception_v3 1.43× SLOWER sam2_tiny 1.54× SLOWER + // The shape-aware predicate keeps the AMX wins for the heavy models + // (Inception, YOLO, SAM2) while routing small shapes to NEON. + ops.mmm_f32 = Box::new(|m, _, n| { + let big_enough = m.is_some_and(|m| m >= 32) && n.is_some_and(|n| n >= 32); + if big_enough { + apple_amx_mmm_f32_32x32.mmm() + } else { + arm64simd_mmm_f32_8x8_gen.mmm() + } + }); + // mmv (n=1) f32: AMX 32x1 is dominated by NEON 64x1 across the entire + // shape sweep — confirmed by canary deltas on DFN3 (which is mmv-heavy). + // Always use NEON. + ops.mmv_f32 = Box::new(|_, _| arm64simd_mmm_f32_64x1_gen.mmm()); + + // ----- f16 paths kept conservative for now ----- + // + // We didn't run the f16 microbench yet, so retain the original logic + // and the previous low-M-routes-to-NEON heuristic. + ops.mmm_f16 = Box::new(|m, _, _| { + if m.is_some_and(|m| m <= 16) { + arm64fp16_mmm_f16_16x8_gen.mmm() + } else { + apple_amx_mmm_f16_64x32.mmm() + } + }); + ops.mmv_f16 = Box::new(|_, _| apple_amx_mmm_f16_64x1.mmm()); + ops.mmm_impls.extend_from_slice(&[ + apple_amx_mmm_f32_32x32.mmm(), + apple_amx_mmm_f32_32x1.mmm(), + apple_amx_mmm_f16_64x32.mmm(), + apple_amx_mmm_f16_64x1.mmm(), + ]); + } else { + log::info!("No AMX optimisation"); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16.rs new file mode 100644 index 0000000..7fecb46 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16.rs @@ -0,0 +1,70 @@ +use tract_data::half::f16; + +mod by_scalar; +mod leaky_relu; +mod max; +pub mod panel_extract; +mod sum; +mod unicast; +pub use by_scalar::*; +pub use leaky_relu::*; +pub use max::*; +pub use sum::*; +pub use unicast::*; + +use crate::Ops; +use crate::block_quant::PackedBlockQuantFormat; +use crate::block_quant::Q4_0; +use crate::frame::mmm::ImplementationQuality::ManuallyOptimized; + +const FP16: fn() -> bool = crate::arm64::has_fp16; + +MMMExternKernel!(arm64fp16_mmm_f16_16x8_gen(16, 8)@(16, 16) where(FP16) quality(ManuallyOptimized)); +MMMExternKernel!(arm64fp16_mmm_f16_16x8_a55(16, 8)@(16, 16) where(FP16) quality(ManuallyOptimized)); +MMMExternKernel!(arm64fp16_mmm_f16_32x4_gen(32, 4)@(16, 16) where(FP16) quality(ManuallyOptimized)); +MMMExternKernel!(arm64fp16_mmm_f16_32x4_a55(32, 4)@(16, 16) where(FP16) quality(ManuallyOptimized)); +MMMExternKernel!(arm64fp16_mmm_f16_128x1_gen(128,1)@(16, 16) where(FP16) quality(ManuallyOptimized)); +MMMExternKernel!(arm64fp16_mmm_f16_128x1_a55(128,1)@(16, 16) where(FP16) quality(ManuallyOptimized)); + +MMMExternKernel!(arm64fp16_mmm_f16_64x3_gen(64, 3)@(16, 16) where(FP16) quality(ManuallyOptimized)); +MMMExternKernel!(arm64fp16_mmm_f16_32x6_gen(32, 6)@(16, 16) where(FP16) quality(ManuallyOptimized)); + +MMMExternKernel! { arm64fp16_mmm_f16_64x1_gen(64, 1)@(16, 16) where(FP16) + packing[1] = q40f16z16se => |k| k.with_packing_a(PackedBlockQuantFormat::new(&Q4_0, 64, 16, true)); + packing[2] = q40f16z16 => |k| k.with_packing_a(PackedBlockQuantFormat::new(&Q4_0, 64, 16, false)); + quality(ManuallyOptimized) +} + +pub fn plug(ops: &mut Ops) { + panel_extract::plug(ops); + ops.mmm_impls.extend_from_slice(&[ + arm64fp16_mmm_f16_16x8_a55.mmm(), + arm64fp16_mmm_f16_16x8_gen.mmm(), + arm64fp16_mmm_f16_32x4_a55.mmm(), + arm64fp16_mmm_f16_32x4_gen.mmm(), + arm64fp16_mmm_f16_128x1_a55.mmm(), + arm64fp16_mmm_f16_128x1_gen.mmm(), + arm64fp16_mmm_f16_64x3_gen.mmm(), + arm64fp16_mmm_f16_32x6_gen.mmm(), + arm64fp16_mmm_f16_64x1_gen.mmm(), + ]); +} + +tanh_impl!(f16, arm64fp16_tanh_f16_8n, 8, 8, crate::arm64::has_fp16()); +sigmoid_impl!( + f16, + arm64fp16_sigmoid_f16_8n, + 8, + 8, + crate::arm64::has_fp16() +); + +#[cfg(test)] +mod test { + + #[test] + fn kits() { + let mut ops = crate::generic(); + super::plug(&mut ops); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16/by_scalar.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16/by_scalar.rs new file mode 100644 index 0000000..e791890 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16/by_scalar.rs @@ -0,0 +1,258 @@ +use crate::f16; + +by_scalar_impl_wrap!( + f16, + arm64fp16_mul_by_scalar_f16_32n, + 32, + 4, + f16, + fn run(buf: &mut [f16], s: f16) { + assert!(buf.len() % 16 == 0); + assert!(buf.len() > 0); + #[target_feature(enable = "fp16")] + unsafe fn run(buf: &mut [f16], s: f16) { + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + std::arch::asm!(" + dup v0.8h, v0.h[0] + 2: + ld1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{ptr}] + fmul v4.8h, v4.8h, v0.8h + fmul v5.8h, v5.8h, v0.8h + fmul v6.8h, v6.8h, v0.8h + fmul v7.8h, v7.8h, v0.8h + st1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{ptr}], 64 + subs {len}, {len}, 32 + bne 2b + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + in("v0") s.to_bits(), + out("v4") _, out("v5") _, out("v6") _, out("v7") _,); + } + } + unsafe { run(buf, s) } + } +); + +by_scalar_impl_wrap!( + f16, + arm64fp16_add_by_scalar_f16_32n, + 32, + 4, + f16, + fn run(buf: &mut [f16], s: f16) { + assert!(buf.len() % 16 == 0); + assert!(buf.len() > 0); + #[target_feature(enable = "fp16")] + unsafe fn run(buf: &mut [f16], s: f16) { + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + std::arch::asm!(" + dup v0.8h, v0.h[0] + 2: + ld1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{ptr}] + fadd v4.8h, v4.8h, v0.8h + fadd v5.8h, v5.8h, v0.8h + fadd v6.8h, v6.8h, v0.8h + fadd v7.8h, v7.8h, v0.8h + st1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{ptr}], 64 + subs {len}, {len}, 32 + bne 2b + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + in("v0") s.to_bits(), + out("v4") _, out("v5") _, out("v6") _, out("v7") _,); + } + } + unsafe { run(buf, s) } + } +); + +by_scalar_impl_wrap!( + f16, + arm64fp16_sub_by_scalar_f16_32n, + 32, + 4, + f16, + fn run(buf: &mut [f16], s: f16) { + assert!(buf.len() % 16 == 0); + assert!(buf.len() > 0); + #[target_feature(enable = "fp16")] + unsafe fn run(buf: &mut [f16], s: f16) { + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + std::arch::asm!(" + dup v0.8h, v0.h[0] + 2: + ld1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{ptr}] + fsub v4.8h, v4.8h, v0.8h + fsub v5.8h, v5.8h, v0.8h + fsub v6.8h, v6.8h, v0.8h + fsub v7.8h, v7.8h, v0.8h + st1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{ptr}], 64 + subs {len}, {len}, 32 + bne 2b + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + in("v0") s.to_bits(), + out("v4") _, out("v5") _, out("v6") _, out("v7") _,); + } + } + unsafe { run(buf, s) } + } +); + +by_scalar_impl_wrap!( + f16, + arm64fp16_subf_by_scalar_f16_32n, + 32, + 4, + f16, + fn run(buf: &mut [f16], s: f16) { + assert!(buf.len() % 16 == 0); + assert!(buf.len() > 0); + #[target_feature(enable = "fp16")] + unsafe fn run(buf: &mut [f16], s: f16) { + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + std::arch::asm!(" + dup v0.8h, v0.h[0] + 2: + ld1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{ptr}] + fsub v4.8h, v0.8h, v4.8h + fsub v5.8h, v0.8h, v5.8h + fsub v6.8h, v0.8h, v6.8h + fsub v7.8h, v0.8h, v7.8h + st1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{ptr}], 64 + subs {len}, {len}, 32 + bne 2b + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + in("v0") s.to_bits(), + out("v4") _, out("v5") _, out("v6") _, out("v7") _,); + } + } + unsafe { run(buf, s) } + } +); + +by_scalar_impl_wrap!( + f16, + arm64fp16_min_by_scalar_f16_32n, + 32, + 4, + f16, + fn run(buf: &mut [f16], s: f16) { + assert!(buf.len() % 16 == 0); + assert!(buf.len() > 0); + #[target_feature(enable = "fp16")] + unsafe fn run(buf: &mut [f16], s: f16) { + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + std::arch::asm!(" + dup v0.8h, v0.h[0] + 2: + ld1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{ptr}] + fmin v4.8h, v4.8h, v0.8h + fmin v5.8h, v5.8h, v0.8h + fmin v6.8h, v6.8h, v0.8h + fmin v7.8h, v7.8h, v0.8h + st1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{ptr}], 64 + subs {len}, {len}, 32 + bne 2b + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + in("v0") s.to_bits(), + out("v4") _, out("v5") _, out("v6") _, out("v7") _,); + } + } + unsafe { run(buf, s) } + } +); + +by_scalar_impl_wrap!( + f16, + arm64fp16_max_by_scalar_f16_32n, + 32, + 4, + f16, + fn run(buf: &mut [f16], s: f16) { + assert!(buf.len() % 16 == 0); + assert!(buf.len() > 0); + #[target_feature(enable = "fp16")] + unsafe fn run(buf: &mut [f16], s: f16) { + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + std::arch::asm!(" + dup v0.8h, v0.h[0] + 2: + ld1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{ptr}] + fmax v4.8h, v4.8h, v0.8h + fmax v5.8h, v5.8h, v0.8h + fmax v6.8h, v6.8h, v0.8h + fmax v7.8h, v7.8h, v0.8h + st1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{ptr}], 64 + subs {len}, {len}, 32 + bne 2b + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + in("v0") s.to_bits(), + out("v4") _, out("v5") _, out("v6") _, out("v7") _,); + } + } + unsafe { run(buf, s) } + } +); + +#[cfg(test)] +mod test_arm64fp16_mul_by_scalar_f16_32n { + use super::*; + by_scalar_frame_tests!( + crate::arm64::has_fp16(), + f16, + arm64fp16_mul_by_scalar_f16_32n, + |a, b| a * b + ); + by_scalar_frame_tests!( + crate::arm64::has_fp16(), + f16, + arm64fp16_add_by_scalar_f16_32n, + |a, b| a + b + ); + by_scalar_frame_tests!( + crate::arm64::has_fp16(), + f16, + arm64fp16_sub_by_scalar_f16_32n, + |a, b| a - b + ); + by_scalar_frame_tests!( + crate::arm64::has_fp16(), + f16, + arm64fp16_subf_by_scalar_f16_32n, + |a, b| b - a + ); + by_scalar_frame_tests!( + crate::arm64::has_fp16(), + f16, + arm64fp16_min_by_scalar_f16_32n, + |a, b| a.min(b) + ); + by_scalar_frame_tests!( + crate::arm64::has_fp16(), + f16, + arm64fp16_max_by_scalar_f16_32n, + |a, b| a.max(b) + ); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16/leaky_relu.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16/leaky_relu.rs new file mode 100644 index 0000000..f4f6204 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16/leaky_relu.rs @@ -0,0 +1,56 @@ +use tract_data::internal::f16; + +ew_impl_wrap!( + f16, + arm64fp16_leaky_relu_f16_16n, + 16, + 8, + f16, + #[inline(never)] + fn run(buf: &mut [f16], alpha: f16) { + assert!(buf.len() % 8 == 0); + assert!(buf.len() > 0); + #[target_feature(enable = "fp16")] + unsafe fn run(buf: &mut [f16], alpha: f16) { + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + std::arch::asm!(" + dup v0.8h, {alpha:v}.h[0] + dup v1.8h, {one:v}.h[0] + 2: + ldp q3, q4, [{ptr}] + + fcmgt v5.8h, v3.8h, #0.0 + fcmgt v6.8h, v4.8h, #0.0 + bsl v5.16b, v1.16b, v0.16b + bsl v6.16b, v1.16b, v0.16b + fmul v3.8h, v3.8h, v5.8h + fmul v4.8h, v4.8h, v6.8h + + stp q3, q4, [{ptr}], #32 + subs {len}, {len}, 16 + bne 2b + ", + one = in(vreg) f16::from_f32(1.0f32).to_bits(), + alpha = in(vreg) alpha.to_bits(), + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + out("v0") _, + out("v1") _, + out("q3") _, + out("q4") _, + out("q5") _, + out("q6") _, + ); + } + } + unsafe { run(buf, alpha) } + } +); + +#[cfg(test)] +pub mod test_arm64simd_leaky_relu_f16_16n { + use super::*; + leaky_relu_frame_tests!(crate::arm64::has_fp16(), f16, arm64fp16_leaky_relu_f16_16n); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16/max.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16/max.rs new file mode 100644 index 0000000..7a7b103 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16/max.rs @@ -0,0 +1,63 @@ +use tract_data::half::f16; + +reduce_impl_wrap!( + f16, + arm64fp16_max_f16_32n, + 32, + 8, + (), + f16::MIN, + #[inline(never)] + fn run(buf: &[f16], _: ()) -> f16 { + assert!(buf.len() % 32 == 0); + assert!(buf.len() > 0); + #[target_feature(enable = "fp16")] + unsafe fn run(buf: &[f16]) -> f16 { + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + let mut out: u16; + std::arch::asm!(" + ins v0.h[0], {min:w} + dup v0.8h, v0.h[0] + dup v1.8h, v0.h[0] + dup v2.8h, v0.h[0] + dup v3.8h, v0.h[0] + + 2: + ld1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{ptr}], 64 + fmax v0.8h, v0.8h, v4.8h + fmax v1.8h, v1.8h, v5.8h + fmax v2.8h, v2.8h, v6.8h + fmax v3.8h, v3.8h, v7.8h + + subs {len}, {len}, 32 + bne 2b + + fmax v0.8h, v0.8h, v1.8h + fmax v2.8h, v2.8h, v3.8h + fmax v0.8h, v0.8h, v2.8h + fmaxv h0, v0.8h + ", + // using v0 as inout triggers https://github.com/rust-lang/rust/issues/120374 + min = in(reg) f16::MIN.to_bits(), + ptr = inout(reg) ptr => _, + len = inout(reg) len => _, + out("v0") out, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _,); + f16::from_bits(out) + } + } + unsafe { run(buf) } + }, + #[inline(never)] + fn reduce_two(a: f16, b: f16) -> f16 { + a.max(b) + } +); + +#[cfg(test)] +mod test_arm64fp16_max_f16_32n { + use super::*; + crate::max_frame_tests!(crate::arm64::has_fp16(), f16, arm64fp16_max_f16_32n); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16/panel_extract.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16/panel_extract.rs new file mode 100644 index 0000000..629282d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16/panel_extract.rs @@ -0,0 +1,94 @@ +use super::FP16; +use crate::Ops; +use crate::block_quant::{PackedBlockQuantFormat, Q4_0}; +use crate::pack::Packing; +use tract_data::internal::*; + +pub fn plug(ops: &mut Ops) { + ops.panel_extractors.push(packed_64_q40_to_f16.clone()); +} + +panel_extractor!(kernel_packed_64_q40_to_f16 as packed_64_q40_to_f16( + Box::new(PackedBlockQuantFormat::new(&Q4_0, 64, 16, true)), + f16::packing(64).align(16) +) where(FP16)); + +#[target_feature(enable = "fp16")] +unsafe fn kernel_packed_64_q40_to_f16(input: *const u8, output: *mut u8, k: usize) { + unsafe { + if k == 0 { + return; + } + let lookup_table: [u8; 16] = [ + 0xc8, 0xc7, 0xc6, 0xc5, 0xc4, 0xc2, 0xc0, 0xbc, 0x00, 0x3c, 0x40, 0x42, 0x44, 0x45, + 0x46, 0x47, + ]; + std::arch::asm!(" + ld1 {{v13.16b}}, [{lookup_table}] + movi v15.16b, 15 + eor v12.16b, v12.16b, v12.16b + + 2: + add {scales}, {i}, 1024 // scales at end: 32 (cols) * 64 (rows) / 2 (half byte) + ld1 {{v16.16b-v19.16b}}, [{scales}], #64 + ld1 {{v20.16b-v23.16b}}, [{scales}] + + mov {k2}, 32 + 3: + ld1 {{ v9.16b-v10.16b }}, [{i}], #32 + + and v0.16b, v9.16b, v15.16b + ushr v2.16b, v9.16b, 4 + + and v4.16b, v10.16b, v15.16b + ushr v6.16b, v10.16b, 4 + + tbl v0.16b, {{ v13.16b }}, v0.16b + tbl v2.16b, {{ v13.16b }}, v2.16b + tbl v4.16b, {{ v13.16b }}, v4.16b + tbl v6.16b, {{ v13.16b }}, v6.16b + + zip2 v1.16b, v12.16b, v0.16b + zip2 v3.16b, v12.16b, v2.16b + zip2 v5.16b, v12.16b, v4.16b + zip2 v7.16b, v12.16b, v6.16b + + zip1 v0.16b, v12.16b, v0.16b + zip1 v2.16b, v12.16b, v2.16b + zip1 v4.16b, v12.16b, v4.16b + zip1 v6.16b, v12.16b, v6.16b + + fmul v0.8h, v0.8h, v16.8h + fmul v1.8h, v1.8h, v17.8h + fmul v2.8h, v2.8h, v18.8h + fmul v3.8h, v3.8h, v19.8h + fmul v4.8h, v4.8h, v20.8h + fmul v5.8h, v5.8h, v21.8h + fmul v6.8h, v6.8h, v22.8h + fmul v7.8h, v7.8h, v23.8h + + st1 {{v0.16b-v3.16b}}, [{o}], #64 + st1 {{v4.16b-v7.16b}}, [{o}], #64 + + subs {k2}, {k2}, #1 + bne 3b + + add {i}, {i}, 128 // skip scales + subs {k}, {k}, 32 + bne 2b + ", + lookup_table = in(reg) &lookup_table, + k = inout(reg) k => _, + k2 = out(reg) _, + scales = out(reg) _, + i = inout(reg) input => _, + o = inout(reg) output => _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + out("v16") _, out("v17") _, out("v18") _, out("v19") _, + out("v20") _, out("v21") _, out("v22") _, out("v23") _, + ); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16/sum.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16/sum.rs new file mode 100644 index 0000000..e13139c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16/sum.rs @@ -0,0 +1,62 @@ +use crate::num_traits::Zero; +use tract_data::half::f16; + +reduce_impl_wrap!( + f16, + arm64fp16_sum_f16_32n, + 32, + 8, + (), + f16::zero(), + #[inline(never)] + fn run(buf: &[f16], _: ()) -> f16 { + assert!(buf.len() % 32 == 0); + assert!(buf.len() > 0); + #[target_feature(enable = "fp16")] + unsafe fn run(buf: &[f16]) -> f16 { + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + let mut out: u16; + std::arch::asm!(" + movi v0.8h, #0 + movi v1.8h, #0 + movi v2.8h, #0 + movi v3.8h, #0 + 2: + ld1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{ptr}], 64 + fadd v0.8h, v0.8h, v4.8h + fadd v1.8h, v1.8h, v5.8h + fadd v2.8h, v2.8h, v6.8h + fadd v3.8h, v3.8h, v7.8h + + subs {len}, {len}, 32 + bne 2b + + fadd v0.8h, v0.8h, v1.8h + fadd v2.8h, v2.8h, v3.8h + fadd v0.8h, v0.8h, v2.8h + faddp v0.8h, v0.8h, v0.8h + faddp v0.8h, v0.8h, v0.8h + faddp v0.8h, v0.8h, v0.8h + ", + ptr = inout(reg) ptr => _, + len = inout(reg) len => _, + out("s0") out, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _,); + f16::from_bits(out) + } + } + unsafe { run(buf) } + }, + #[inline(never)] + fn reduce_two(a: f16, b: f16) -> f16 { + a + b + } +); + +#[cfg(test)] +mod test_arm64fp16_sum_f16_32n { + use super::*; + crate::sum_frame_tests!(crate::arm64::has_fp16(), f16, arm64fp16_sum_f16_32n); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16/unicast.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16/unicast.rs new file mode 100644 index 0000000..d57ba10 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64fp16/unicast.rs @@ -0,0 +1,271 @@ +use tract_data::half::f16; + +unicast_impl_wrap!( + f16, + arm64fp16_unicast_mul_f16_32n, + 32, + 8, + #[inline(never)] + fn run(a: &mut [f16], b: &[f16]) { + assert!(a.len() == b.len()); + assert!(a.len() % 32 == 0); + assert!(a.len() > 0); + #[target_feature(enable = "fp16")] + unsafe fn run(a: &mut [f16], b: &[f16]) { + unsafe { + let len = a.len(); + let a_ptr = a.as_ptr(); + let b_ptr = b.as_ptr(); + std::arch::asm!(" + 2: + ld1 {{v0.8h, v1.8h, v2.8h, v3.8h}}, [{a_ptr}] + ld1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{b_ptr}], 64 + fmul v0.8h, v0.8h, v4.8h + fmul v1.8h, v1.8h, v5.8h + fmul v2.8h, v2.8h, v6.8h + fmul v3.8h, v3.8h, v7.8h + st1 {{v0.8h, v1.8h, v2.8h, v3.8h}}, [{a_ptr}], 64 + subs {len}, {len}, 32 + bne 2b + ", + len = inout(reg) len => _, + a_ptr = inout(reg) a_ptr => _, + b_ptr = inout(reg) b_ptr => _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _,); + } + } + unsafe { run(a, b) } + } +); + +unicast_impl_wrap!( + f16, + arm64fp16_unicast_add_f16_32n, + 32, + 8, + #[inline(never)] + fn run(a: &mut [f16], b: &[f16]) { + assert!(a.len() == b.len()); + assert!(a.len() % 32 == 0); + assert!(a.len() > 0); + #[target_feature(enable = "fp16")] + unsafe fn run(a: &mut [f16], b: &[f16]) { + unsafe { + let len = a.len(); + let a_ptr = a.as_ptr(); + let b_ptr = b.as_ptr(); + std::arch::asm!(" + 2: + ld1 {{v0.8h, v1.8h, v2.8h, v3.8h}}, [{a_ptr}] + ld1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{b_ptr}], 64 + fadd v0.8h, v0.8h, v4.8h + fadd v1.8h, v1.8h, v5.8h + fadd v2.8h, v2.8h, v6.8h + fadd v3.8h, v3.8h, v7.8h + st1 {{v0.8h, v1.8h, v2.8h, v3.8h}}, [{a_ptr}], 64 + subs {len}, {len}, 32 + bne 2b + ", + len = inout(reg) len => _, + a_ptr = inout(reg) a_ptr => _, + b_ptr = inout(reg) b_ptr => _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _,); + } + } + unsafe { run(a, b) } + } +); + +unicast_impl_wrap!( + f16, + arm64fp16_unicast_sub_f16_32n, + 32, + 8, + #[inline(never)] + fn run(a: &mut [f16], b: &[f16]) { + assert!(a.len() == b.len()); + assert!(a.len() % 32 == 0); + assert!(a.len() > 0); + #[target_feature(enable = "fp16")] + unsafe fn run(a: &mut [f16], b: &[f16]) { + unsafe { + let len = a.len(); + let a_ptr = a.as_ptr(); + let b_ptr = b.as_ptr(); + std::arch::asm!(" + 2: + ld1 {{v0.8h, v1.8h, v2.8h, v3.8h}}, [{a_ptr}] + ld1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{b_ptr}], 64 + fsub v0.8h, v0.8h, v4.8h + fsub v1.8h, v1.8h, v5.8h + fsub v2.8h, v2.8h, v6.8h + fsub v3.8h, v3.8h, v7.8h + st1 {{v0.8h, v1.8h, v2.8h, v3.8h}}, [{a_ptr}], 64 + subs {len}, {len}, 32 + bne 2b + ", + len = inout(reg) len => _, + a_ptr = inout(reg) a_ptr => _, + b_ptr = inout(reg) b_ptr => _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _,); + } + } + unsafe { run(a, b) } + } +); + +unicast_impl_wrap!( + f16, + arm64fp16_unicast_subf_f16_32n, + 32, + 8, + #[inline(never)] + fn run(a: &mut [f16], b: &[f16]) { + assert!(a.len() == b.len()); + assert!(a.len() % 32 == 0); + assert!(a.len() > 0); + #[target_feature(enable = "fp16")] + unsafe fn run(a: &mut [f16], b: &[f16]) { + unsafe { + let len = a.len(); + let a_ptr = a.as_ptr(); + let b_ptr = b.as_ptr(); + std::arch::asm!(" + 2: + ld1 {{v0.8h, v1.8h, v2.8h, v3.8h}}, [{a_ptr}] + ld1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{b_ptr}], 64 + fsub v0.8h, v4.8h, v0.8h + fsub v1.8h, v5.8h, v1.8h + fsub v2.8h, v6.8h, v2.8h + fsub v3.8h, v7.8h, v3.8h + st1 {{v0.8h, v1.8h, v2.8h, v3.8h}}, [{a_ptr}], 64 + subs {len}, {len}, 32 + bne 2b + ", + len = inout(reg) len => _, + a_ptr = inout(reg) a_ptr => _, + b_ptr = inout(reg) b_ptr => _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _,); + } + } + unsafe { run(a, b) } + } +); + +unicast_impl_wrap!( + f16, + arm64fp16_unicast_min_f16_32n, + 32, + 8, + #[inline(never)] + fn run(a: &mut [f16], b: &[f16]) { + assert!(a.len() == b.len()); + assert!(a.len() % 32 == 0); + assert!(a.len() > 0); + #[target_feature(enable = "fp16")] + unsafe fn run(a: &mut [f16], b: &[f16]) { + unsafe { + let len = a.len(); + let a_ptr = a.as_ptr(); + let b_ptr = b.as_ptr(); + std::arch::asm!(" + 2: + ld1 {{v0.8h, v1.8h, v2.8h, v3.8h}}, [{a_ptr}] + ld1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{b_ptr}], 64 + fmin v0.8h, v0.8h, v4.8h + fmin v1.8h, v1.8h, v5.8h + fmin v2.8h, v2.8h, v6.8h + fmin v3.8h, v3.8h, v7.8h + st1 {{v0.8h, v1.8h, v2.8h, v3.8h}}, [{a_ptr}], 64 + subs {len}, {len}, 32 + bne 2b + ", + len = inout(reg) len => _, + a_ptr = inout(reg) a_ptr => _, + b_ptr = inout(reg) b_ptr => _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _,); + } + } + unsafe { run(a, b) } + } +); + +unicast_impl_wrap!( + f16, + arm64fp16_unicast_max_f16_32n, + 32, + 8, + #[inline(never)] + fn run(a: &mut [f16], b: &[f16]) { + assert!(a.len() == b.len()); + assert!(a.len() % 32 == 0); + assert!(a.len() > 0); + #[target_feature(enable = "fp16")] + unsafe fn run(a: &mut [f16], b: &[f16]) { + unsafe { + let len = a.len(); + let a_ptr = a.as_ptr(); + let b_ptr = b.as_ptr(); + std::arch::asm!(" + 2: + ld1 {{v0.8h, v1.8h, v2.8h, v3.8h}}, [{a_ptr}] + ld1 {{v4.8h, v5.8h, v6.8h, v7.8h}}, [{b_ptr}], 64 + fmax v0.8h, v0.8h, v4.8h + fmax v1.8h, v1.8h, v5.8h + fmax v2.8h, v2.8h, v6.8h + fmax v3.8h, v3.8h, v7.8h + st1 {{v0.8h, v1.8h, v2.8h, v3.8h}}, [{a_ptr}], 64 + subs {len}, {len}, 32 + bne 2b + ", + len = inout(reg) len => _, + a_ptr = inout(reg) a_ptr => _, + b_ptr = inout(reg) b_ptr => _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _,); + } + } + unsafe { run(a, b) } + } +); + +#[cfg(test)] +mod test_arm64fp16_unicast_mul_f16_32n { + use super::*; + use proptest::strategy::Strategy; + crate::unicast_frame_tests!( + crate::arm64::has_fp16(), + f16, + arm64fp16_unicast_mul_f16_32n, + |a, b| a * b + ); + crate::unicast_frame_tests!( + crate::arm64::has_fp16(), + f16, + arm64fp16_unicast_add_f16_32n, + |a, b| a + b + ); + crate::unicast_frame_tests!( + crate::arm64::has_fp16(), + f16, + arm64fp16_unicast_sub_f16_32n, + |a, b| a - b + ); + crate::unicast_frame_tests!( + crate::arm64::has_fp16(), + f16, + arm64fp16_unicast_subf_f16_32n, + |a, b| b - a + ); + crate::unicast_frame_tests!( + crate::arm64::has_fp16(), + f16, + arm64fp16_unicast_min_f16_32n, + |a, b| a.min(b) + ); + crate::unicast_frame_tests!( + crate::arm64::has_fp16(), + f16, + arm64fp16_unicast_max_f16_32n, + |a, b| a.max(b) + ); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd.rs new file mode 100644 index 0000000..598d6e8 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd.rs @@ -0,0 +1,146 @@ +mod by_scalar; +mod gelu; +mod gelu_fused; +mod hardswish; +mod leaky_relu; +mod max; +mod panel_extract; +mod rms_norm; +mod silu; +mod silu_fused; +mod softmax; +mod sum; +mod unicast; + +pub use by_scalar::*; +pub use gelu::arm64simd_gelu_f32_4n; +pub use gelu_fused::arm64simd_gelu_f32_4n_fused; +pub use hardswish::arm64simd_hardswish_f32_8n; +pub use leaky_relu::arm64simd_leaky_relu_f32_8n; +pub use max::arm64simd_max_f32_16n; +pub use rms_norm::rms_norm_f32 as arm64simd_rms_norm_f32; +pub use silu::arm64simd_silu_f32_4n; +pub use silu_fused::arm64simd_silu_f32_4n_fused; +pub use softmax::arm64simd_softmax2_fastcompact_f32_16n; +pub use sum::arm64simd_sum_f32_16n; +pub use unicast::*; + +use crate::Ops; +use crate::block_quant::{PackedBlockQuantFormat, Q4_0}; +use crate::frame::mmm::ImplementationQuality::ManuallyOptimized; +use crate::pack::PackedFormat; + +use super::Kind; + +fn a55() -> isize { + if *super::KIND == Kind::CortexA55 { + 1 + } else { + -1 + } +} + +fn a53() -> isize { + if *super::KIND == Kind::CortexA53 { + 1 + } else { + -1 + } +} + +MMMExternKernel!(arm64simd_mmm_f32_8x8_a55 (8, 8)@(16, 16) quality(ManuallyOptimized) boost(a55)); +MMMExternKernel!(arm64simd_mmm_f32_12x8_a55(12, 8)@(16, 16) quality(ManuallyOptimized) boost(a55)); +MMMExternKernel!(arm64simd_mmm_f32_16x4_a55(16, 4)@(16, 16) quality(ManuallyOptimized) boost(a55)); +MMMExternKernel!(arm64simd_mmm_f32_24x4_a55(24, 4)@(16, 16) quality(ManuallyOptimized) boost(a55)); +MMMExternKernel!(arm64simd_mmm_f32_64x1_a55(64, 1)@(16, 16) quality(ManuallyOptimized) boost(a55)); + +MMMExternKernel!(arm64simd_mmm_f32_16x4_a53(16, 4)@(16, 16) quality(ManuallyOptimized) boost(a53)); +MMMExternKernel!(arm64simd_mmm_f32_24x4_a53(24, 4)@(16, 16) quality(ManuallyOptimized) boost(a53)); +MMMExternKernel!(arm64simd_mmm_f32_8x8_a53 (8, 8)@(16, 16) quality(ManuallyOptimized) boost(a53)); +MMMExternKernel!(arm64simd_mmm_f32_12x8_a53(12, 8)@(16, 16) quality(ManuallyOptimized) boost(a53)); +MMMExternKernel!(arm64simd_mmm_f32_64x1_a53(64, 1)@(16, 16) quality(ManuallyOptimized) boost(a53)); + +MMMExternKernel!(arm64simd_mmm_f32_16x4_gen(16, 4)@(16, 16) quality(ManuallyOptimized)); +MMMExternKernel!(arm64simd_mmm_f32_24x4_gen(24, 4)@(16, 16) quality(ManuallyOptimized)); +MMMExternKernel!(arm64simd_mmm_f32_8x8_gen (8, 8)@(16, 16) quality(ManuallyOptimized)); +MMMExternKernel!(arm64simd_mmm_f32_12x8_gen(12, 8)@(16, 16) quality(ManuallyOptimized)); +MMMExternKernel!(arm64simd_mmm_f32_64x1_gen(64, 1)@(16, 16) quality(ManuallyOptimized)); + +fn q40p32z16se() -> PackedBlockQuantFormat { + PackedBlockQuantFormat::new(&Q4_0, 32, 16, true) +} + +MMMExternKernel!(arm64simd_mmm_f32_32x1_gen(32, 1)@(16, 16) + packing[1] = q40f16 => |k| k.with_packing(q40p32z16se(), f16::packing(1)); + packing[2] = q40f32 => |k| k.with_packing(q40p32z16se(), f32::packing(1)); + packing[3] = f16f16 => |k| k.with_packing(f16::packing(32), f16::packing(1)); + packing[4] = f32f16 => |k| k.with_packing(f32::packing(32), f16::packing(1)); + packing[5] = f16f32 => |k| k.with_packing(f16::packing(32), f32::packing(1)); + quality(ManuallyOptimized) + store(f16) +); + +MMMExternKernel!(arm64simd_mmm_f32_32x3_gen(32, 3)@(16, 16) + packing[1] = f32f16 => |k| k.with_packing(f32::packing(32), f16::packing(3)); + packing[2] = f16f32 => |k| k.with_packing(f16::packing(32), f32::packing(3)); + packing[3] = f16f16 => |k| k.with_packing(f16::packing(32), f16::packing(3)); + quality(ManuallyOptimized) + store(f16) +); + +MMMExternKernel!(arm64simd_mmm_i32_8x8(8, 8)@(16, 16) + packing[1] = i8i8 => |k| k.with_packing(PackedFormat::new(DatumType::I8, 8, 16), PackedFormat::new(DatumType::I8, 8, 16)); + quality(ManuallyOptimized) + store(i8) +); + +// SDOT (FEAT_DotProd) variant: 4-K reduction per instruction (~4x the SMLAL +// 8x8 above). Uses the K=4-inner PackedI8K4 packing; identical v16..v31 tile +// layout, so it reuses all the i32 fuse/store/q_scale machinery. +// +// Gated on `tract_arm64_dotprod` (set by build.rs when the assembler can encode +// `sdot`; binutils < 2.30 cannot). On old toolchains the kernel is omitted and +// dispatch falls back to the SMLAL 8x8 i32 kernel. +#[cfg(tract_arm64_dotprod)] +MMMExternKernel!(arm64simd_mmm_i32_8x8_dot(8, 8)@(16, 16) + where(super::has_dotprod) + packing[1] = i8i8 => |k| k.with_packing(crate::pack::PackedI8K4::new(8), crate::pack::PackedI8K4::new(8)); + quality(ManuallyOptimized) + store(i8) +); + +MMMExternKernel!(arm64simd_mmm_i32_64x1(64, 1)@(16, 1) + packing[1] = i8i8 => |k| k.with_packing(PackedFormat::new(DatumType::I8, 64,16), PackedFormat::new(DatumType::I8, 1, 1)); + quality(ManuallyOptimized) + store(i8) +); + +pub fn plug(ops: &mut Ops) { + ops.mmm_impls.extend([ + arm64simd_mmm_f32_12x8_gen.mmm(), + arm64simd_mmm_f32_12x8_a53.mmm(), + arm64simd_mmm_f32_12x8_a55.mmm(), + arm64simd_mmm_f32_8x8_gen.mmm(), + arm64simd_mmm_f32_8x8_a53.mmm(), + arm64simd_mmm_f32_8x8_a55.mmm(), + arm64simd_mmm_f32_16x4_gen.mmm(), + arm64simd_mmm_f32_16x4_a53.mmm(), + arm64simd_mmm_f32_16x4_a55.mmm(), + arm64simd_mmm_f32_24x4_gen.mmm(), + arm64simd_mmm_f32_24x4_a53.mmm(), + arm64simd_mmm_f32_24x4_a55.mmm(), + arm64simd_mmm_f32_32x1_gen.mmm(), + arm64simd_mmm_f32_32x3_gen.mmm(), + arm64simd_mmm_f32_64x1_gen.mmm(), + arm64simd_mmm_f32_64x1_a53.mmm(), + arm64simd_mmm_f32_64x1_a55.mmm(), + arm64simd_mmm_i32_8x8.mmm(), + arm64simd_mmm_i32_64x1.mmm(), + ]); + #[cfg(tract_arm64_dotprod)] + ops.mmm_impls.push(arm64simd_mmm_i32_8x8_dot.mmm()); + panel_extract::plug(ops); +} + +tanh_impl!(f32, arm64simd_tanh_f32_4n, 4, 4, true); +sigmoid_impl!(f32, arm64simd_sigmoid_f32_4n, 4, 4, true); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/by_scalar.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/by_scalar.rs new file mode 100644 index 0000000..49b2c75 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/by_scalar.rs @@ -0,0 +1,202 @@ +by_scalar_impl_wrap!( + f32, + arm64simd_mul_by_scalar_f32_16n, + 16, + 4, + f32, + fn run(buf: &mut [f32], s: f32) { + assert!(buf.len() % 16 == 0); + assert!(buf.len() > 0); + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + std::arch::asm!(" + dup v0.4s, v0.s[0] + 2: + ld1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{ptr}] + fmul v4.4s, v4.4s, v0.4s + fmul v5.4s, v5.4s, v0.4s + fmul v6.4s, v6.4s, v0.4s + fmul v7.4s, v7.4s, v0.4s + st1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{ptr}], 64 + subs {len}, {len}, 16 + bne 2b + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + in("v0") s, + out("v4") _, out("v5") _, out("v6") _, out("v7") _,); + } + } +); + +by_scalar_impl_wrap!( + f32, + arm64simd_add_by_scalar_f32_16n, + 16, + 4, + f32, + fn run(buf: &mut [f32], s: f32) { + assert!(buf.len() % 16 == 0); + assert!(buf.len() > 0); + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + std::arch::asm!(" + dup v0.4s, v0.s[0] + 2: + ld1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{ptr}] + fadd v4.4s, v4.4s, v0.4s + fadd v5.4s, v5.4s, v0.4s + fadd v6.4s, v6.4s, v0.4s + fadd v7.4s, v7.4s, v0.4s + st1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{ptr}], 64 + subs {len}, {len}, 16 + bne 2b + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + in("v0") s, + out("v4") _, out("v5") _, out("v6") _, out("v7") _,); + } + } +); + +by_scalar_impl_wrap!( + f32, + arm64simd_sub_by_scalar_f32_16n, + 16, + 4, + f32, + fn run(buf: &mut [f32], s: f32) { + assert!(buf.len() % 16 == 0); + assert!(buf.len() > 0); + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + std::arch::asm!(" + dup v0.4s, v0.s[0] + 2: + ld1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{ptr}] + fsub v4.4s, v4.4s, v0.4s + fsub v5.4s, v5.4s, v0.4s + fsub v6.4s, v6.4s, v0.4s + fsub v7.4s, v7.4s, v0.4s + st1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{ptr}], 64 + subs {len}, {len}, 16 + bne 2b + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + in("v0") s, + out("v4") _, out("v5") _, out("v6") _, out("v7") _,); + } + } +); + +by_scalar_impl_wrap!( + f32, + arm64simd_subf_by_scalar_f32_16n, + 16, + 4, + f32, + fn run(buf: &mut [f32], s: f32) { + assert!(buf.len() % 16 == 0); + assert!(buf.len() > 0); + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + std::arch::asm!(" + dup v0.4s, v0.s[0] + 2: + ld1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{ptr}] + fsub v4.4s, v0.4s, v4.4s + fsub v5.4s, v0.4s, v5.4s + fsub v6.4s, v0.4s, v6.4s + fsub v7.4s, v0.4s, v7.4s + st1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{ptr}], 64 + subs {len}, {len}, 16 + bne 2b + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + in("v0") s, + out("v4") _, out("v5") _, out("v6") _, out("v7") _,); + } + } +); + +by_scalar_impl_wrap!( + f32, + arm64simd_min_by_scalar_f32_16n, + 16, + 4, + f32, + fn run(buf: &mut [f32], s: f32) { + assert!(buf.len() % 16 == 0); + assert!(buf.len() > 0); + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + std::arch::asm!(" + dup v0.4s, v0.s[0] + 2: + ld1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{ptr}] + fmin v4.4s, v4.4s, v0.4s + fmin v5.4s, v5.4s, v0.4s + fmin v6.4s, v6.4s, v0.4s + fmin v7.4s, v7.4s, v0.4s + st1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{ptr}], 64 + subs {len}, {len}, 16 + bne 2b + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + in("v0") s, + out("v4") _, out("v5") _, out("v6") _, out("v7") _,); + } + } +); + +by_scalar_impl_wrap!( + f32, + arm64simd_max_by_scalar_f32_16n, + 16, + 4, + f32, + fn run(buf: &mut [f32], s: f32) { + assert!(buf.len() % 16 == 0); + assert!(buf.len() > 0); + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + std::arch::asm!(" + dup v0.4s, v0.s[0] + 2: + ld1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{ptr}] + fmax v4.4s, v4.4s, v0.4s + fmax v5.4s, v5.4s, v0.4s + fmax v6.4s, v6.4s, v0.4s + fmax v7.4s, v7.4s, v0.4s + st1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{ptr}], 64 + subs {len}, {len}, 16 + bne 2b + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + in("v0") s, + out("v4") _, out("v5") _, out("v6") _, out("v7") _,); + } + } +); + +#[cfg(test)] +mod test_arm64simd_mul_by_scalar_f32_16n { + use super::*; + by_scalar_frame_tests!(true, f32, arm64simd_mul_by_scalar_f32_16n, |a, b| a * b); + by_scalar_frame_tests!(true, f32, arm64simd_add_by_scalar_f32_16n, |a, b| a + b); + by_scalar_frame_tests!(true, f32, arm64simd_sub_by_scalar_f32_16n, |a, b| a - b); + by_scalar_frame_tests!(true, f32, arm64simd_subf_by_scalar_f32_16n, |a, b| b - a); + by_scalar_frame_tests!(true, f32, arm64simd_min_by_scalar_f32_16n, |a, b| a.min(b)); + by_scalar_frame_tests!(true, f32, arm64simd_max_by_scalar_f32_16n, |a, b| a.max(b)); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/gelu.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/gelu.rs new file mode 100644 index 0000000..751bd14 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/gelu.rs @@ -0,0 +1,45 @@ +// Tanh-form GELU (pow=3) matching tract's GeluApproximate: +// gelu(x) = 0.5 * x * (1 + tanh(sqrt(2/pi) * (x + 0.044715 * x^3))) +// +// Composed at the kernel level: save the original x, compute the tanh +// argument, call tract's NEON tanh kernel in place, then finish with the +// 0.5 * x * (1 + tanh) multiply. Chunked to keep the scratch buffer L1-resident. + +ew_impl_wrap!( + f32, + arm64simd_gelu_f32_4n, + 4, + 4, + (), + #[inline(never)] + fn run(buf: &mut [f32], _: ()) { + const SQRT_2_OVER_PI: f32 = 0.7978845608028654; + const COEF: f32 = 0.044715; + const CHUNK: usize = 256; + let mut scratch = [0f32; CHUNK]; + let mut start = 0; + while start < buf.len() { + let end = (start + CHUNK).min(buf.len()); + let chunk = &mut buf[start..end]; + let n = chunk.len(); + // Save original x and pre-compute the tanh argument in place. + for i in 0..n { + let x = chunk[i]; + scratch[i] = x; + chunk[i] = SQRT_2_OVER_PI * (x + COEF * x * x * x); + } + super::arm64simd_tanh_f32_4n::run(chunk, ()); + // chunk now holds tanh(arg). Combine with saved x. + for i in 0..n { + chunk[i] = 0.5 * scratch[i] * (1.0 + chunk[i]); + } + start = end; + } + } +); + +#[cfg(test)] +pub mod test_arm64simd_gelu_f32_4n { + use super::*; + gelu_frame_tests!(true, f32, arm64simd_gelu_f32_4n); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/gelu_fused.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/gelu_fused.rs new file mode 100644 index 0000000..10bff88 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/gelu_fused.rs @@ -0,0 +1,292 @@ +// Fused GELU (tanh-form, pow=3): +// gelu(x) = 0.5 * x * (1 + tanh(sqrt(2/pi) * (x + 0.044715 * x^3))) +// +// loop4 (16 lanes per iter) + loop1 (4-lane tail). Clones the tanh Padé +// polynomial from arm64simd_tanh_f32_4n.S.j2, with pre-tanh argument +// computation up front and the final 0.5*x*(1+tanh) combined via fmla. +// Single memory pass (load + store), no scratch buffer. + +ew_impl_wrap!( + f32, + arm64simd_gelu_f32_4n_fused, + 4, + 4, + (), + #[inline(never)] + fn run(buf: &mut [f32], _: ()) { + // Tanh Padé coefficients (matches arm64simd_tanh_f32_4n.S.j2) + + // 3 GELU constants packed into the last vector lanes: + // index 13: 0.5 + // index 14: sqrt(2/pi) ≈ 0.7978846 + // index 15: 0.044715 * sqrt(2/pi) ≈ 0.0356774 + static COEFFS: [f32; 16] = [ + -8.9, + 8.9, + -8.488492677e-14, + 5.277853000e-11, + -2.022500419e-8, + 0.00001115424833, + 0.003103950131, + 0.1308400453, + 0.9999999934, + 0.0002546136580, + 0.02449515379, + 0.4641733162, + 1.0, + 0.5, + 0.7978845608028654, + 0.03567739613, + ]; + + assert!(buf.len() % 4 == 0); + if buf.is_empty() { + return; + } + + unsafe { + let len = buf.len(); + let ptr = buf.as_mut_ptr(); + let coef_ptr = COEFFS.as_ptr(); + + // Register layout (loop4): + // v0-v3: coefficients + // v4: sqrt(2/pi) (broadcast of v3.s[2]) + // v5: tanh clamp low (-8.9, dup v0.s[0]) + // v6: tanh clamp high (8.9, dup v0.s[1]) + // v7: 0.5 (broadcast of v3.s[1]) + // v8-v11: 0.5 * original x (saved after load) + // v16-v19: working (load -> pre_tanh -> clamped -> numerator) + // v20-v23: x² for tanh polynomial + // v24-v27: polynomial intermediates (denominator at end) + // v28-v31: polynomial intermediates (also x³ temp before tanh) + std::arch::asm!(" + ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [{coef}] + dup v5.4s, v0.s[0] + dup v6.4s, v0.s[1] + dup v7.4s, v3.s[1] + dup v4.4s, v3.s[2] + + cmp {len}, #16 + blt 9f + + 1: + ld1 {{ v16.4s, v17.4s, v18.4s, v19.4s }}, [{ptr}] + + // Save 0.5 * original x into v8-v11 + fmul v8.4s, v16.4s, v7.4s + fmul v9.4s, v17.4s, v7.4s + fmul v10.4s, v18.4s, v7.4s + fmul v11.4s, v19.4s, v7.4s + + // Compute x^3 into v28-v31 + fmul v28.4s, v16.4s, v16.4s + fmul v29.4s, v17.4s, v17.4s + fmul v30.4s, v18.4s, v18.4s + fmul v31.4s, v19.4s, v19.4s + + fmul v28.4s, v28.4s, v16.4s + fmul v29.4s, v29.4s, v17.4s + fmul v30.4s, v30.4s, v18.4s + fmul v31.4s, v31.4s, v19.4s + + // pre_tanh = sqrt(2/pi)*x + 0.0356774 * x^3 + fmul v16.4s, v16.4s, v4.4s + fmul v17.4s, v17.4s, v4.4s + fmul v18.4s, v18.4s, v4.4s + fmul v19.4s, v19.4s, v4.4s + + fmla v16.4s, v28.4s, v3.s[3] + fmla v17.4s, v29.4s, v3.s[3] + fmla v18.4s, v30.4s, v3.s[3] + fmla v19.4s, v31.4s, v3.s[3] + + // Clamp pre_tanh argument + fmax v16.4s, v16.4s, v5.4s + fmax v17.4s, v17.4s, v5.4s + fmax v18.4s, v18.4s, v5.4s + fmax v19.4s, v19.4s, v5.4s + + fmin v16.4s, v16.4s, v6.4s + fmin v17.4s, v17.4s, v6.4s + fmin v18.4s, v18.4s, v6.4s + fmin v19.4s, v19.4s, v6.4s + + // Tanh Padé polynomial (cloned from arm64simd_tanh_f32_4n.S.j2) + fmul v20.4s, v16.4s, v16.4s + fmul v21.4s, v17.4s, v17.4s + fmul v22.4s, v18.4s, v18.4s + fmul v23.4s, v19.4s, v19.4s + + dup v24.4s, v0.s[3] + fmla v24.4s, v20.4s, v0.s[2] + dup v25.4s, v0.s[3] + fmla v25.4s, v21.4s, v0.s[2] + dup v26.4s, v0.s[3] + fmla v26.4s, v22.4s, v0.s[2] + dup v27.4s, v0.s[3] + fmla v27.4s, v23.4s, v0.s[2] + + dup v28.4s, v1.s[0] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v1.s[0] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v1.s[0] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v1.s[0] + fmla v31.4s, v23.4s, v27.4s + + dup v24.4s, v1.s[1] + fmla v24.4s, v20.4s, v28.4s + dup v25.4s, v1.s[1] + fmla v25.4s, v21.4s, v29.4s + dup v26.4s, v1.s[1] + fmla v26.4s, v22.4s, v30.4s + dup v27.4s, v1.s[1] + fmla v27.4s, v23.4s, v31.4s + + dup v28.4s, v1.s[2] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v1.s[2] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v1.s[2] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v1.s[2] + fmla v31.4s, v23.4s, v27.4s + + dup v24.4s, v1.s[3] + fmla v24.4s, v20.4s, v28.4s + dup v25.4s, v1.s[3] + fmla v25.4s, v21.4s, v29.4s + dup v26.4s, v1.s[3] + fmla v26.4s, v22.4s, v30.4s + dup v27.4s, v1.s[3] + fmla v27.4s, v23.4s, v31.4s + + dup v28.4s, v2.s[0] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v2.s[0] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v2.s[0] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v2.s[0] + fmla v31.4s, v23.4s, v27.4s + + fmul v16.4s, v16.4s, v28.4s + fmul v17.4s, v17.4s, v29.4s + fmul v18.4s, v18.4s, v30.4s + fmul v19.4s, v19.4s, v31.4s + + dup v24.4s, v2.s[2] + fmla v24.4s, v20.4s, v2.s[1] + dup v25.4s, v2.s[2] + fmla v25.4s, v21.4s, v2.s[1] + dup v26.4s, v2.s[2] + fmla v26.4s, v22.4s, v2.s[1] + dup v27.4s, v2.s[2] + fmla v27.4s, v23.4s, v2.s[1] + + dup v28.4s, v2.s[3] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v2.s[3] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v2.s[3] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v2.s[3] + fmla v31.4s, v23.4s, v27.4s + + dup v24.4s, v3.s[0] + fmla v24.4s, v20.4s, v28.4s + dup v25.4s, v3.s[0] + fmla v25.4s, v21.4s, v29.4s + dup v26.4s, v3.s[0] + fmla v26.4s, v22.4s, v30.4s + dup v27.4s, v3.s[0] + fmla v27.4s, v23.4s, v31.4s + + // tanh(pre_arg) = num/denom + fdiv v16.4s, v16.4s, v24.4s + fdiv v17.4s, v17.4s, v25.4s + fdiv v18.4s, v18.4s, v26.4s + fdiv v19.4s, v19.4s, v27.4s + + // result = 0.5*x * (1 + tanh) = (0.5*x) + (0.5*x) * tanh + fmla v8.4s, v8.4s, v16.4s + fmla v9.4s, v9.4s, v17.4s + fmla v10.4s, v10.4s, v18.4s + fmla v11.4s, v11.4s, v19.4s + + st1 {{ v8.4s, v9.4s, v10.4s, v11.4s }}, [{ptr}], #64 + sub {len}, {len}, #16 + cmp {len}, #16 + bge 1b + + 9: + cbz {len}, 3f + + 2: + ld1 {{ v16.4s }}, [{ptr}] + fmul v8.4s, v16.4s, v7.4s + + fmul v28.4s, v16.4s, v16.4s + fmul v28.4s, v28.4s, v16.4s + + fmul v16.4s, v16.4s, v4.4s + fmla v16.4s, v28.4s, v3.s[3] + + fmax v16.4s, v16.4s, v5.4s + fmin v16.4s, v16.4s, v6.4s + + fmul v20.4s, v16.4s, v16.4s + + dup v24.4s, v0.s[3] + fmla v24.4s, v20.4s, v0.s[2] + dup v28.4s, v1.s[0] + fmla v28.4s, v20.4s, v24.4s + dup v24.4s, v1.s[1] + fmla v24.4s, v20.4s, v28.4s + dup v28.4s, v1.s[2] + fmla v28.4s, v20.4s, v24.4s + dup v24.4s, v1.s[3] + fmla v24.4s, v20.4s, v28.4s + dup v28.4s, v2.s[0] + fmla v28.4s, v20.4s, v24.4s + fmul v16.4s, v16.4s, v28.4s + + dup v24.4s, v2.s[2] + fmla v24.4s, v20.4s, v2.s[1] + dup v28.4s, v2.s[3] + fmla v28.4s, v20.4s, v24.4s + dup v24.4s, v3.s[0] + fmla v24.4s, v20.4s, v28.4s + + fdiv v16.4s, v16.4s, v24.4s + + fmla v8.4s, v8.4s, v16.4s + + st1 {{ v8.4s }}, [{ptr}], #16 + subs {len}, {len}, 4 + bne 2b + + 3: + ", + coef = in(reg) coef_ptr, + ptr = inout(reg) ptr => _, + len = inout(reg) len => _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v16") _, out("v17") _, out("v18") _, out("v19") _, + out("v20") _, out("v21") _, out("v22") _, out("v23") _, + out("v24") _, out("v25") _, out("v26") _, out("v27") _, + out("v28") _, out("v29") _, out("v30") _, out("v31") _, + options(nostack), + ); + } + } +); + +#[cfg(test)] +pub mod test_arm64simd_gelu_f32_4n_fused { + use super::*; + gelu_frame_tests!(true, f32, arm64simd_gelu_f32_4n_fused); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/hardswish.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/hardswish.rs new file mode 100644 index 0000000..ad25ba6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/hardswish.rs @@ -0,0 +1,63 @@ +ew_impl_wrap!( + f32, + arm64simd_hardswish_f32_8n, + 8, + 4, + (), + #[inline(never)] + fn run(buf: &mut [f32], _: ()) { + assert!(buf.len() % 8 == 0); + assert!(buf.len() > 0); + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + std::arch::asm!(" + dup v0.4s, {three:v}.s[0] + dup v1.4s, {six:v}.s[0] + dup v2.4s, {inv6:v}.s[0] + movi v3.4s, #0 + 2: + ldp q4, q5, [{ptr}] + + fadd v6.4s, v4.4s, v0.4s + fadd v7.4s, v5.4s, v0.4s + + fmin v6.4s, v6.4s, v1.4s + fmin v7.4s, v7.4s, v1.4s + + fmax v6.4s, v6.4s, v3.4s + fmax v7.4s, v7.4s, v3.4s + + fmul v6.4s, v6.4s, v4.4s + fmul v7.4s, v7.4s, v5.4s + + fmul v6.4s, v6.4s, v2.4s + fmul v7.4s, v7.4s, v2.4s + + stp q6, q7, [{ptr}], #32 + subs {len}, {len}, 8 + bne 2b + ", + three = in(vreg) 3.0f32, + six = in(vreg) 6.0f32, + inv6 = in(vreg) 1.0f32 / 6.0f32, + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + out("v0") _, + out("v1") _, + out("v2") _, + out("v3") _, + out("q4") _, + out("q5") _, + out("q6") _, + out("q7") _, + ); + } + } +); + +#[cfg(test)] +pub mod test_arm64simd_hardswish_f32_8n { + use super::*; + hardswish_frame_tests!(true, f32, arm64simd_hardswish_f32_8n); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/leaky_relu.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/leaky_relu.rs new file mode 100644 index 0000000..d716668 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/leaky_relu.rs @@ -0,0 +1,50 @@ +ew_impl_wrap!( + f32, + arm64simd_leaky_relu_f32_8n, + 8, + 4, + f32, + #[inline(never)] + fn run(buf: &mut [f32], alpha: f32) { + assert!(buf.len() % 8 == 0); + assert!(buf.len() > 0); + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + std::arch::asm!(" + dup v0.4s, {alpha:v}.s[0] + dup v1.4s, {one:v}.s[0] + 2: + ldp q3, q4, [{ptr}] + + fcmgt v5.4s, v3.4s, #0.0 + fcmgt v6.4s, v4.4s, #0.0 + bsl v5.16b, v1.16b, v0.16b + bsl v6.16b, v1.16b, v0.16b + fmul v3.4s, v3.4s, v5.4s + fmul v4.4s, v4.4s, v6.4s + + stp q3, q4, [{ptr}], #32 + subs {len}, {len}, 8 + bne 2b + ", + one = in(vreg) 1.0f32, + alpha = in(vreg) alpha, + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + out("v0") _, + out("v1") _, + out("q3") _, + out("q4") _, + out("q5") _, + out("q6") _, + ); + } + } +); + +#[cfg(test)] +pub mod test_arm64simd_leaky_relu_f32_8n { + use super::*; + leaky_relu_frame_tests!(true, f32, arm64simd_leaky_relu_f32_8n); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/max.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/max.rs new file mode 100644 index 0000000..3c32aa7 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/max.rs @@ -0,0 +1,52 @@ +use std::arch::aarch64::{float32x4_t, vdupq_n_f32, vgetq_lane_f32}; + +reduce_impl_wrap!( + f32, + arm64simd_max_f32_16n, + 16, + 4, + (), + f32::MIN, + #[inline(never)] + fn run(buf: &[f32], _: ()) -> f32 { + assert!(buf.len() % 16 == 0); + assert!(buf.len() > 0); + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + let mut out: float32x4_t = vdupq_n_f32(f32::MIN); + std::arch::asm!(" + and v1.16b, v0.16b, v0.16b + and v2.16b, v0.16b, v0.16b + and v3.16b, v0.16b, v0.16b + 2: + ld1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{ptr}], 64 + fmax v0.4s, v0.4s, v4.4s + fmax v1.4s, v1.4s, v5.4s + fmax v2.4s, v2.4s, v6.4s + fmax v3.4s, v3.4s, v7.4s + subs {len}, {len}, 16 + bne 2b + fmax v0.4s, v0.4s, v1.4s + fmax v2.4s, v2.4s, v3.4s + fmax v0.4s, v0.4s, v2.4s + fmaxv s0, v0.4s + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + inout("v0") out, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _,); + vgetq_lane_f32(out, 0) + } + }, + #[inline(never)] + fn reduce_two(a: f32, b: f32) -> f32 { + a.max(b) + } +); + +#[cfg(test)] +mod test_arm64simd_max_f32_16n { + use super::*; + crate::max_frame_tests!(true, f32, arm64simd_max_f32_16n); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/panel_extract.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/panel_extract.rs new file mode 100644 index 0000000..755fc29 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/panel_extract.rs @@ -0,0 +1,98 @@ +use crate::Ops; +use crate::pack::Packing; + +pub fn plug(ops: &mut Ops) { + ops.panel_extractors.push(packed_32_q40_to_f32.clone()); +} + +panel_extractor!(kernel_packed_32_q40_to_f32 as packed_32_q40_to_f32( + Box::new(super::q40p32z16se()), + f32::packing(32).align(16) +)); + +unsafe fn kernel_packed_32_q40_to_f32(input: *const u8, output: *mut u8, k: usize) { + unsafe { + if k == 0 { + return; + } + let lookup_table: [u8; 16] = [ + 0xc8, 0xc7, 0xc6, 0xc5, 0xc4, 0xc2, 0xc0, 0xbc, 0x00, 0x3c, 0x40, 0x42, 0x44, 0x45, + 0x46, 0x47, + ]; + std::arch::asm!(" + ld1 {{v13.16b}}, [{lookup_table}] + movi v15.16b, 15 + eor v12.16b, v12.16b, v12.16b + + 2: + add {scales}, {i}, 512 // scales at end: 32 (cols) * 32 (rows) / 2 (half byte) + ld1 {{v0.8h-v3.8h}}, [{scales}] + + fcvtl v16.4s, v0.4h + fcvtl2 v17.4s, v0.8h + fcvtl v18.4s, v1.4h + fcvtl2 v19.4s, v1.8h + fcvtl v20.4s, v2.4h + fcvtl2 v21.4s, v2.8h + fcvtl v22.4s, v3.4h + fcvtl2 v23.4s, v3.8h + + mov {k2}, 32 + 3: + ld1 {{ v9.16b }}, [{i}], #16 + + and v0.16b, v9.16b, v15.16b + ushr v4.16b, v9.16b, 4 + + tbl v0.16b, {{ v13.16b }}, v0.16b + tbl v4.16b, {{ v13.16b }}, v4.16b + + zip2 v2.16b, v12.16b, v0.16b + zip2 v6.16b, v12.16b, v4.16b + + zip1 v0.16b, v12.16b, v0.16b + zip1 v4.16b, v12.16b, v4.16b + + fcvtl2 v1.4s, v0.8h + fcvtl v0.4s, v0.4h + fcvtl2 v3.4s, v2.8h + fcvtl v2.4s, v2.4h + fcvtl2 v5.4s, v4.8h + fcvtl v4.4s, v4.4h + fcvtl2 v7.4s, v6.8h + fcvtl v6.4s, v6.4h + + fmul v0.4s, v0.4s, v16.4s + fmul v1.4s, v1.4s, v17.4s + fmul v2.4s, v2.4s, v18.4s + fmul v3.4s, v3.4s, v19.4s + fmul v4.4s, v4.4s, v20.4s + fmul v5.4s, v5.4s, v21.4s + fmul v6.4s, v6.4s, v22.4s + fmul v7.4s, v7.4s, v23.4s + + st1 {{v0.16b-v3.16b}}, [{o}], #64 + st1 {{v4.16b-v7.16b}}, [{o}], #64 + + subs {k2}, {k2}, #1 + bne 3b + + add {i}, {i}, 64 // skip scales + subs {k}, {k}, 32 + bne 2b + ", + lookup_table = in(reg) &lookup_table, + k = inout(reg) k => _, + k2 = out(reg) _, + scales = out(reg) _, + i = inout(reg) input => _, + o = inout(reg) output => _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v12") _, out("v13") _, out("v14") _, out("v15") _, + out("v16") _, out("v17") _, out("v18") _, out("v19") _, + out("v20") _, out("v21") _, out("v22") _, out("v23") _, + ); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/rms_norm.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/rms_norm.rs new file mode 100644 index 0000000..5f25299 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/rms_norm.rs @@ -0,0 +1,164 @@ +// NEON (aarch64, 128-bit, 4 f32 lanes) fused row-wise RmsNorm. +// +// Mirrors the AVX-512 kernel structure from `x86_64_fma/rms_norm.rs`: +// +// Pass 1 (sum of squares): acc += x² over 4 v-registers (4 lanes each → +// 16 f32 / iter), horizontal reduce to a scalar, +// then rsqrt(mean + eps) in scalar. +// Pass 2 (multiply-back): broadcast inv_std into v0, multiply each +// 4-v-register chunk in place. +// Scalar tail handles the (len % 16 != 0) remainder. +// +// Drops into `Ops::rms_norm_f32` (added by the parent PR) — the core-side +// dispatcher in `core::ops::nn::RmsNorm::eval` is already arch-neutral and +// will pick this up automatically. + +#[target_feature(enable = "neon")] +unsafe fn rms_norm_f32_inner(buf: &mut [f32], eps: f32) { + use std::arch::aarch64::*; + let n = buf.len(); + let chunks = n / 16; + let tail_start = chunks * 16; + let ptr = buf.as_mut_ptr(); + + // --- Pass 1: sum of squares --- + let mut sum_sq: f32 = 0.0; + if chunks > 0 { + let p = ptr; + let c = chunks; + let mut sum_v: float32x4_t = vdupq_n_f32(0.0); + unsafe { + std::arch::asm!(" + movi v1.4s, 0 + movi v2.4s, 0 + movi v3.4s, 0 + 2: + ld1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{p}], 64 + fmla v0.4s, v4.4s, v4.4s + fmla v1.4s, v5.4s, v5.4s + fmla v2.4s, v6.4s, v6.4s + fmla v3.4s, v7.4s, v7.4s + subs {c}, {c}, 1 + bne 2b + fadd v0.4s, v0.4s, v1.4s + fadd v2.4s, v2.4s, v3.4s + fadd v0.4s, v0.4s, v2.4s + ", + p = inout(reg) p => _, + c = inout(reg) c => _, + inout("v0") sum_v, + out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + ); + } + // horizontal sum across the 4 surviving lanes + sum_sq = vaddvq_f32(sum_v); + } + // scalar tail + for i in tail_start..n { + let x = unsafe { *buf.get_unchecked(i) }; + sum_sq += x * x; + } + + // --- Compute inv_std (scalar) --- + let mean_sq = sum_sq / (n as f32); + let inv_std = (mean_sq + eps).sqrt().recip(); + + // --- Pass 2: multiply by inv_std --- + if chunks > 0 { + let p = ptr; + let c = chunks; + let inv_v: float32x4_t = vdupq_n_f32(inv_std); + unsafe { + std::arch::asm!(" + 2: + ld1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{p}] + fmul v4.4s, v4.4s, v0.4s + fmul v5.4s, v5.4s, v0.4s + fmul v6.4s, v6.4s, v0.4s + fmul v7.4s, v7.4s, v0.4s + st1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{p}], 64 + subs {c}, {c}, 1 + bne 2b + ", + p = inout(reg) p => _, + c = inout(reg) c => _, + in("v0") inv_v, + out("v4") _, out("v5") _, out("v6") _, out("v7") _, + ); + } + } + for i in tail_start..n { + unsafe { + *buf.get_unchecked_mut(i) *= inv_std; + } + } +} + +pub fn rms_norm_f32(buf: &mut [f32], eps: f32) { + if buf.is_empty() { + return; + } + unsafe { rms_norm_f32_inner(buf, eps) } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn ref_rms_norm(buf: &mut [f32], eps: f32) { + let n = buf.len() as f32; + let sum_sq: f32 = buf.iter().map(|x| x * x).sum(); + let mean_sq = sum_sq / n; + let inv_std = (mean_sq + eps).sqrt().recip(); + for x in buf.iter_mut() { + *x *= inv_std; + } + } + + fn close_enough(got: &[f32], want: &[f32], tol: f32) { + assert_eq!(got.len(), want.len()); + for (i, (g, w)) in got.iter().zip(want.iter()).enumerate() { + let diff = (g - w).abs(); + assert!(diff <= tol, "lane {i}: got {g}, want {w}, diff {diff}"); + } + } + + #[test] + fn matches_reference_16() { + // 16 = exactly one inner iteration, no tail. + let mut x: Vec = (0..16).map(|i| (i as f32 * 0.13).sin() * 5.0).collect(); + let mut y = x.clone(); + rms_norm_f32(&mut x, 1e-5); + ref_rms_norm(&mut y, 1e-5); + close_enough(&x, &y, 1e-5); + } + + #[test] + fn matches_reference_1024_with_tail() { + // 1024 + 7 = exercises the scalar tail loop (len % 16 = 7). + let n = 1024 + 7; + let mut x: Vec = (0..n).map(|i| (i as f32 * 0.07).cos() * 3.0).collect(); + let mut y = x.clone(); + rms_norm_f32(&mut x, 1e-5); + ref_rms_norm(&mut y, 1e-5); + close_enough(&x, &y, 1e-4); + } + + #[test] + fn matches_reference_short_below_chunk() { + // 8 elements — shorter than one NEON iteration; all scalar tail. + let mut x: Vec = vec![0.5, -1.5, 2.5, -3.5, 0.0, 4.0, -4.0, 1.0]; + let mut y = x.clone(); + rms_norm_f32(&mut x, 1e-5); + ref_rms_norm(&mut y, 1e-5); + close_enough(&x, &y, 1e-5); + } + + #[test] + fn empty_is_noop() { + let mut x: Vec = vec![]; + rms_norm_f32(&mut x, 1e-5); + assert!(x.is_empty()); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/silu.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/silu.rs new file mode 100644 index 0000000..c07322c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/silu.rs @@ -0,0 +1,34 @@ +ew_impl_wrap!( + f32, + arm64simd_silu_f32_4n, + 4, + 4, + (), + #[inline(never)] + fn run(buf: &mut [f32], _: ()) { + // SiLU(x) = x * sigmoid(x). Compose by saving the input chunk to a + // stack scratch buffer, running tract's NEON sigmoid kernel in place, + // then multiplying back by the saved original. Multiply loop + // auto-vectorises on aarch64. + const CHUNK: usize = 256; + let mut scratch = [0f32; CHUNK]; + let mut start = 0; + while start < buf.len() { + let end = (start + CHUNK).min(buf.len()); + let chunk = &mut buf[start..end]; + let n = chunk.len(); + scratch[..n].copy_from_slice(chunk); + super::arm64simd_sigmoid_f32_4n::run(chunk, ()); + for i in 0..n { + chunk[i] *= scratch[i]; + } + start = end; + } + } +); + +#[cfg(test)] +pub mod test_arm64simd_silu_f32_4n { + use super::*; + silu_frame_tests!(true, f32, arm64simd_silu_f32_4n); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/silu_fused.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/silu_fused.rs new file mode 100644 index 0000000..b45c33d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/silu_fused.rs @@ -0,0 +1,246 @@ +// Fused SiLU: x * sigmoid(x). +// loop4 (16 lanes per iter) + loop1 (4-lane tail). +// Clones the sigmoid Padé polynomial from arm64simd_sigmoid_f32_4n.S.j2, +// with the input saved before clamp (in v8-v11) and multiplied back at the +// end. Single memory pass (load + store), no scratch buffer. + +ew_impl_wrap!( + f32, + arm64simd_silu_f32_4n_fused, + 4, + 4, + (), + #[inline(never)] + fn run(buf: &mut [f32], _: ()) { + // Sigmoid Padé coefficients (matches arm64simd_sigmoid_f32_4n.S.j2). + static COEFFS: [f32; 16] = [ + -18.6, + 18.6, + -4.433153405e-18, + 1.169974371e-14, + -1.875289645e-11, + 4.257889523e-8, + 0.00004811817576, + 0.008163842030, + 0.2499999971, + 3.922935744e-6, + 0.001524872358, + 0.1159886749, + 1.0, + 0.5, + 0.0, + 0.0, + ]; + + assert!(buf.len() % 4 == 0); + if buf.is_empty() { + return; + } + + unsafe { + let len = buf.len(); + let ptr = buf.as_mut_ptr(); + let coef_ptr = COEFFS.as_ptr(); + + std::arch::asm!(" + ld1 {{ v0.4s, v1.4s, v2.4s, v3.4s }}, [{coef}] + dup v5.4s, v0.s[0] + dup v6.4s, v0.s[1] + dup v7.4s, v3.s[1] + + cmp {len}, #16 + blt 9f + + 1: + ld1 {{ v16.4s, v17.4s, v18.4s, v19.4s }}, [{ptr}] + + mov v8.16b, v16.16b + mov v9.16b, v17.16b + mov v10.16b, v18.16b + mov v11.16b, v19.16b + + fmax v16.4s, v16.4s, v5.4s + fmax v17.4s, v17.4s, v5.4s + fmax v18.4s, v18.4s, v5.4s + fmax v19.4s, v19.4s, v5.4s + + fmin v16.4s, v16.4s, v6.4s + fmin v17.4s, v17.4s, v6.4s + fmin v18.4s, v18.4s, v6.4s + fmin v19.4s, v19.4s, v6.4s + + fmul v20.4s, v16.4s, v16.4s + fmul v21.4s, v17.4s, v17.4s + fmul v22.4s, v18.4s, v18.4s + fmul v23.4s, v19.4s, v19.4s + + dup v24.4s, v0.s[3] + fmla v24.4s, v20.4s, v0.s[2] + dup v25.4s, v0.s[3] + fmla v25.4s, v21.4s, v0.s[2] + dup v26.4s, v0.s[3] + fmla v26.4s, v22.4s, v0.s[2] + dup v27.4s, v0.s[3] + fmla v27.4s, v23.4s, v0.s[2] + + dup v28.4s, v1.s[0] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v1.s[0] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v1.s[0] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v1.s[0] + fmla v31.4s, v23.4s, v27.4s + + dup v24.4s, v1.s[1] + fmla v24.4s, v20.4s, v28.4s + dup v25.4s, v1.s[1] + fmla v25.4s, v21.4s, v29.4s + dup v26.4s, v1.s[1] + fmla v26.4s, v22.4s, v30.4s + dup v27.4s, v1.s[1] + fmla v27.4s, v23.4s, v31.4s + + dup v28.4s, v1.s[2] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v1.s[2] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v1.s[2] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v1.s[2] + fmla v31.4s, v23.4s, v27.4s + + dup v24.4s, v1.s[3] + fmla v24.4s, v20.4s, v28.4s + dup v25.4s, v1.s[3] + fmla v25.4s, v21.4s, v29.4s + dup v26.4s, v1.s[3] + fmla v26.4s, v22.4s, v30.4s + dup v27.4s, v1.s[3] + fmla v27.4s, v23.4s, v31.4s + + dup v28.4s, v2.s[0] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v2.s[0] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v2.s[0] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v2.s[0] + fmla v31.4s, v23.4s, v27.4s + + fmul v16.4s, v16.4s, v28.4s + fmul v17.4s, v17.4s, v29.4s + fmul v18.4s, v18.4s, v30.4s + fmul v19.4s, v19.4s, v31.4s + + dup v24.4s, v2.s[2] + fmla v24.4s, v20.4s, v2.s[1] + dup v25.4s, v2.s[2] + fmla v25.4s, v21.4s, v2.s[1] + dup v26.4s, v2.s[2] + fmla v26.4s, v22.4s, v2.s[1] + dup v27.4s, v2.s[2] + fmla v27.4s, v23.4s, v2.s[1] + + dup v28.4s, v2.s[3] + fmla v28.4s, v20.4s, v24.4s + dup v29.4s, v2.s[3] + fmla v29.4s, v21.4s, v25.4s + dup v30.4s, v2.s[3] + fmla v30.4s, v22.4s, v26.4s + dup v31.4s, v2.s[3] + fmla v31.4s, v23.4s, v27.4s + + dup v24.4s, v3.s[0] + fmla v24.4s, v20.4s, v28.4s + dup v25.4s, v3.s[0] + fmla v25.4s, v21.4s, v29.4s + dup v26.4s, v3.s[0] + fmla v26.4s, v22.4s, v30.4s + dup v27.4s, v3.s[0] + fmla v27.4s, v23.4s, v31.4s + + fdiv v16.4s, v16.4s, v24.4s + fdiv v17.4s, v17.4s, v25.4s + fdiv v18.4s, v18.4s, v26.4s + fdiv v19.4s, v19.4s, v27.4s + + fadd v16.4s, v16.4s, v7.4s + fadd v17.4s, v17.4s, v7.4s + fadd v18.4s, v18.4s, v7.4s + fadd v19.4s, v19.4s, v7.4s + + fmul v16.4s, v16.4s, v8.4s + fmul v17.4s, v17.4s, v9.4s + fmul v18.4s, v18.4s, v10.4s + fmul v19.4s, v19.4s, v11.4s + + st1 {{ v16.4s, v17.4s, v18.4s, v19.4s }}, [{ptr}], #64 + sub {len}, {len}, #16 + cmp {len}, #16 + bge 1b + + 9: + cbz {len}, 3f + + 2: + ld1 {{ v16.4s }}, [{ptr}] + mov v8.16b, v16.16b + + fmax v16.4s, v16.4s, v5.4s + fmin v16.4s, v16.4s, v6.4s + fmul v20.4s, v16.4s, v16.4s + + dup v24.4s, v0.s[3] + fmla v24.4s, v20.4s, v0.s[2] + dup v28.4s, v1.s[0] + fmla v28.4s, v20.4s, v24.4s + dup v24.4s, v1.s[1] + fmla v24.4s, v20.4s, v28.4s + dup v28.4s, v1.s[2] + fmla v28.4s, v20.4s, v24.4s + dup v24.4s, v1.s[3] + fmla v24.4s, v20.4s, v28.4s + dup v28.4s, v2.s[0] + fmla v28.4s, v20.4s, v24.4s + fmul v16.4s, v16.4s, v28.4s + + dup v24.4s, v2.s[2] + fmla v24.4s, v20.4s, v2.s[1] + dup v28.4s, v2.s[3] + fmla v28.4s, v20.4s, v24.4s + dup v24.4s, v3.s[0] + fmla v24.4s, v20.4s, v28.4s + + fdiv v16.4s, v16.4s, v24.4s + fadd v16.4s, v16.4s, v7.4s + + fmul v16.4s, v16.4s, v8.4s + + st1 {{ v16.4s }}, [{ptr}], #16 + subs {len}, {len}, 4 + bne 2b + + 3: + ", + coef = in(reg) coef_ptr, + ptr = inout(reg) ptr => _, + len = inout(reg) len => _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _, + out("v5") _, out("v6") _, out("v7") _, + out("v8") _, out("v9") _, out("v10") _, out("v11") _, + out("v16") _, out("v17") _, out("v18") _, out("v19") _, + out("v20") _, out("v21") _, out("v22") _, out("v23") _, + out("v24") _, out("v25") _, out("v26") _, out("v27") _, + out("v28") _, out("v29") _, out("v30") _, out("v31") _, + options(nostack), + ); + } + } +); + +#[cfg(test)] +pub mod test_arm64simd_silu_f32_4n_fused { + use super::*; + silu_frame_tests!(true, f32, arm64simd_silu_f32_4n_fused); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/softmax.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/softmax.rs new file mode 100644 index 0000000..a9cfc16 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/softmax.rs @@ -0,0 +1,110 @@ +map_reduce_impl_wrap!( + f32, + arm64simd_softmax2_fastcompact_f32_16n, + 16, + 4, + f32, + f32::MIN, + 0f32, + #[inline(never)] + fn run(buf: &mut [f32], max: f32) -> f32 { + assert!(buf.len() % 16 == 0); + assert!(buf.len() > 0); + let len = buf.len(); + let ptr = buf.as_ptr(); + let mut acc; + const MLN2: f32 = 0.6931471805f32; + const A: f32 = 8388608.0f32; + const B: f32 = 1065353216.0f32; + const C: f32 = 60801.0f32; + const SLOPE: f32 = A / MLN2; + const OFFSET: f32 = B - C; + unsafe { + std::arch::asm!(" + // v0-v3 sum acc + eor v0.16b, v0.16b, v0.16b + eor v1.16b, v1.16b, v1.16b + eor v2.16b, v2.16b, v2.16b + eor v3.16b, v3.16b, v3.16b + + dup v4.4s, v4.s[0] // max + dup v5.4s, v5.s[0] // slope + dup v6.4s, v6.s[0] // offset + eor v7.16b, v7.16b, v7.16b // zero for max + 2: + ld1 {{v8.4s, v9.4s, v10.4s, v11.4s}}, [{ptr}] + + fsub v8.4s, v8.4s, v4.4s + fsub v9.4s, v9.4s, v4.4s + fsub v10.4s, v10.4s, v4.4s + fsub v11.4s, v11.4s, v4.4s + + fmul v8.4s, v8.4s, v5.4s + fmul v9.4s, v9.4s, v5.4s + fmul v10.4s, v10.4s, v5.4s + fmul v11.4s, v11.4s, v5.4s + + fadd v8.4s, v8.4s, v6.4s + fadd v9.4s, v9.4s, v6.4s + fadd v10.4s, v10.4s, v6.4s + fadd v11.4s, v11.4s, v6.4s + + fmax v8.4s, v8.4s, v7.4s + fmax v9.4s, v9.4s, v7.4s + fmax v10.4s, v10.4s, v7.4s + fmax v11.4s, v11.4s, v7.4s + + fcvtnu v8.4s, v8.4s + fcvtnu v9.4s, v9.4s + fcvtnu v10.4s, v10.4s + fcvtnu v11.4s, v11.4s + + fadd v0.4s, v0.4s, v8.4s + fadd v1.4s, v1.4s, v9.4s + fadd v2.4s, v2.4s, v10.4s + fadd v3.4s, v3.4s, v11.4s + + st1 {{v8.4s, v9.4s, v10.4s, v11.4s}}, [{ptr}], 64 + subs {len}, {len}, 16 + bne 2b + + fadd v0.4s, v0.4s, v1.4s + fadd v2.4s, v2.4s, v3.4s + fadd v0.4s, v0.4s, v2.4s + + ext v1.16b, v0.16b, v0.16b, 4 + ext v2.16b, v0.16b, v0.16b, 8 + ext v3.16b, v0.16b, v0.16b, 12 + fadd v0.4s, v0.4s, v1.4s + fadd v2.4s, v2.4s, v3.4s + fadd v0.4s, v0.4s, v2.4s + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + out("v0") acc, + out("v1") _, + out("v2") _, + out("v3") _, + inout("v4") max => _, + inout("v5") SLOPE => _, + inout("v6") OFFSET => _, + out("v7") _, + out("v8") _, + out("v9") _, + out("v10") _, + out("v11") _, + ); + } + acc + }, + #[inline(never)] + fn reduce_two(a: f32, b: f32) -> f32 { + a + b + } +); + +#[cfg(test)] +mod test_arm64simd_softmax2_fastcompact_f32_16n { + use super::*; + crate::softmax_l2_frame_tests!(true, f32, arm64simd_softmax2_fastcompact_f32_16n); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/sum.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/sum.rs new file mode 100644 index 0000000..87116a8 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/sum.rs @@ -0,0 +1,59 @@ +use crate::num_traits::Zero; + +reduce_impl_wrap!( + f32, + arm64simd_sum_f32_16n, + 16, + 4, + (), + f32::zero(), + #[inline(never)] + fn run(buf: &[f32], _: ()) -> f32 { + assert!(buf.len() % 16 == 0); + assert!(buf.len() > 0); + unsafe fn run(buf: &[f32]) -> f32 { + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + let mut out: u32; + std::arch::asm!(" + movi v0.4s, #0 + movi v1.4s, #0 + movi v2.4s, #0 + movi v3.4s, #0 + 2: + ld1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{ptr}], 64 + fadd v0.4s, v0.4s, v4.4s + fadd v1.4s, v1.4s, v5.4s + fadd v2.4s, v2.4s, v6.4s + fadd v3.4s, v3.4s, v7.4s + + subs {len}, {len}, 16 + bne 2b + + fadd v0.4s, v0.4s, v1.4s + fadd v2.4s, v2.4s, v3.4s + fadd v0.4s, v0.4s, v2.4s + faddp v0.4s, v0.4s, v0.4s + faddp v0.4s, v0.4s, v0.4s + ", + ptr = inout(reg) ptr => _, + len = inout(reg) len => _, + out("s0") out, out("v1") _, out("v2") _, out("v3") _, + out("v4") _, out("v5") _, out("v6") _, out("v7") _,); + f32::from_bits(out) + } + } + unsafe { run(buf) } + }, + #[inline(never)] + fn reduce_two(a: f32, b: f32) -> f32 { + a + b + } +); + +#[cfg(test)] +mod test_arm64simd_sum_f32_16n { + use super::*; + crate::sum_frame_tests!(true, f32, arm64simd_sum_f32_16n); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/unicast.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/unicast.rs new file mode 100644 index 0000000..a7a4d41 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/arm64simd/unicast.rs @@ -0,0 +1,233 @@ +unicast_impl_wrap!( + f32, + arm64simd_unicast_mul_f32_16n, + 16, + 4, + #[inline(never)] + fn run(a: &mut [f32], b: &[f32]) { + assert!(a.len() == b.len()); + assert!(a.len() % 16 == 0); + assert!(a.len() > 0); + unsafe fn run(a: &mut [f32], b: &[f32]) { + unsafe { + let len = a.len(); + let a_ptr = a.as_ptr(); + let b_ptr = b.as_ptr(); + std::arch::asm!(" + 2: + ld1 {{v0.4s, v1.4s, v2.4s, v3.4s}}, [{a_ptr}] + ld1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{b_ptr}], 64 + fmul v0.4s, v0.4s, v4.4s + fmul v1.4s, v1.4s, v5.4s + fmul v2.4s, v2.4s, v6.4s + fmul v3.4s, v3.4s, v7.4s + st1 {{v0.4s, v1.4s, v2.4s, v3.4s}}, [{a_ptr}], 64 + subs {len}, {len}, 16 + bne 2b + ", + len = inout(reg) len => _, + a_ptr = inout(reg) a_ptr => _, + b_ptr = inout(reg) b_ptr => _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _,); + } + } + unsafe { run(a, b) } + } +); + +unicast_impl_wrap!( + f32, + arm64simd_unicast_add_f32_16n, + 16, + 4, + #[inline(never)] + fn run(a: &mut [f32], b: &[f32]) { + assert!(a.len() == b.len()); + assert!(a.len() % 16 == 0); + assert!(a.len() > 0); + unsafe fn run(a: &mut [f32], b: &[f32]) { + unsafe { + let len = a.len(); + let a_ptr = a.as_ptr(); + let b_ptr = b.as_ptr(); + std::arch::asm!(" + 2: + ld1 {{v0.4s, v1.4s, v2.4s, v3.4s}}, [{a_ptr}] + ld1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{b_ptr}], 64 + fadd v0.4s, v0.4s, v4.4s + fadd v1.4s, v1.4s, v5.4s + fadd v2.4s, v2.4s, v6.4s + fadd v3.4s, v3.4s, v7.4s + st1 {{v0.4s, v1.4s, v2.4s, v3.4s}}, [{a_ptr}], 64 + subs {len}, {len}, 16 + bne 2b + ", + len = inout(reg) len => _, + a_ptr = inout(reg) a_ptr => _, + b_ptr = inout(reg) b_ptr => _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _,); + } + } + unsafe { run(a, b) } + } +); + +unicast_impl_wrap!( + f32, + arm64simd_unicast_sub_f32_16n, + 16, + 4, + #[inline(never)] + fn run(a: &mut [f32], b: &[f32]) { + assert!(a.len() == b.len()); + assert!(a.len() % 16 == 0); + assert!(a.len() > 0); + unsafe fn run(a: &mut [f32], b: &[f32]) { + unsafe { + let len = a.len(); + let a_ptr = a.as_ptr(); + let b_ptr = b.as_ptr(); + std::arch::asm!(" + 2: + ld1 {{v0.4s, v1.4s, v2.4s, v3.4s}}, [{a_ptr}] + ld1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{b_ptr}], 64 + fsub v0.4s, v0.4s, v4.4s + fsub v1.4s, v1.4s, v5.4s + fsub v2.4s, v2.4s, v6.4s + fsub v3.4s, v3.4s, v7.4s + st1 {{v0.4s, v1.4s, v2.4s, v3.4s}}, [{a_ptr}], 64 + subs {len}, {len}, 16 + bne 2b + ", + len = inout(reg) len => _, + a_ptr = inout(reg) a_ptr => _, + b_ptr = inout(reg) b_ptr => _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _,); + } + } + unsafe { run(a, b) } + } +); + +unicast_impl_wrap!( + f32, + arm64simd_unicast_subf_f32_16n, + 16, + 4, + #[inline(never)] + fn run(a: &mut [f32], b: &[f32]) { + assert!(a.len() == b.len()); + assert!(a.len() % 16 == 0); + assert!(a.len() > 0); + unsafe fn run(a: &mut [f32], b: &[f32]) { + unsafe { + let len = a.len(); + let a_ptr = a.as_ptr(); + let b_ptr = b.as_ptr(); + std::arch::asm!(" + 2: + ld1 {{v0.4s, v1.4s, v2.4s, v3.4s}}, [{a_ptr}] + ld1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{b_ptr}], 64 + fsub v0.4s, v4.4s, v0.4s + fsub v1.4s, v5.4s, v1.4s + fsub v2.4s, v6.4s, v2.4s + fsub v3.4s, v7.4s, v3.4s + st1 {{v0.4s, v1.4s, v2.4s, v3.4s}}, [{a_ptr}], 64 + subs {len}, {len}, 16 + bne 2b + ", + len = inout(reg) len => _, + a_ptr = inout(reg) a_ptr => _, + b_ptr = inout(reg) b_ptr => _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _,); + } + } + unsafe { run(a, b) } + } +); + +unicast_impl_wrap!( + f32, + arm64simd_unicast_max_f32_16n, + 16, + 4, + #[inline(never)] + fn run(a: &mut [f32], b: &[f32]) { + assert!(a.len() == b.len()); + assert!(a.len() % 16 == 0); + assert!(a.len() > 0); + unsafe fn run(a: &mut [f32], b: &[f32]) { + unsafe { + let len = a.len(); + let a_ptr = a.as_ptr(); + let b_ptr = b.as_ptr(); + std::arch::asm!(" + 2: + ld1 {{v0.4s, v1.4s, v2.4s, v3.4s}}, [{a_ptr}] + ld1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{b_ptr}], 64 + fmax v0.4s, v0.4s, v4.4s + fmax v1.4s, v1.4s, v5.4s + fmax v2.4s, v2.4s, v6.4s + fmax v3.4s, v3.4s, v7.4s + st1 {{v0.4s, v1.4s, v2.4s, v3.4s}}, [{a_ptr}], 64 + subs {len}, {len}, 16 + bne 2b + ", + len = inout(reg) len => _, + a_ptr = inout(reg) a_ptr => _, + b_ptr = inout(reg) b_ptr => _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _,); + } + } + unsafe { run(a, b) } + } +); + +unicast_impl_wrap!( + f32, + arm64simd_unicast_min_f32_16n, + 16, + 4, + #[inline(never)] + fn run(a: &mut [f32], b: &[f32]) { + assert!(a.len() == b.len()); + assert!(a.len() % 16 == 0); + assert!(a.len() > 0); + unsafe fn run(a: &mut [f32], b: &[f32]) { + unsafe { + let len = a.len(); + let a_ptr = a.as_ptr(); + let b_ptr = b.as_ptr(); + std::arch::asm!(" + 2: + ld1 {{v0.4s, v1.4s, v2.4s, v3.4s}}, [{a_ptr}] + ld1 {{v4.4s, v5.4s, v6.4s, v7.4s}}, [{b_ptr}], 64 + fmin v0.4s, v0.4s, v4.4s + fmin v1.4s, v1.4s, v5.4s + fmin v2.4s, v2.4s, v6.4s + fmin v3.4s, v3.4s, v7.4s + st1 {{v0.4s, v1.4s, v2.4s, v3.4s}}, [{a_ptr}], 64 + subs {len}, {len}, 16 + bne 2b + ", + len = inout(reg) len => _, + a_ptr = inout(reg) a_ptr => _, + b_ptr = inout(reg) b_ptr => _, + out("v0") _, out("v1") _, out("v2") _, out("v3") _,); + } + } + unsafe { run(a, b) } + } +); + +#[cfg(test)] +mod test_arm64simd_unicast_mul_f32_16n { + use super::*; + use proptest::strategy::Strategy; + crate::unicast_frame_tests!(true, f32, arm64simd_unicast_mul_f32_16n, |a, b| a * b); + crate::unicast_frame_tests!(true, f32, arm64simd_unicast_add_f32_16n, |a, b| a + b); + crate::unicast_frame_tests!(true, f32, arm64simd_unicast_sub_f32_16n, |a, b| a - b); + crate::unicast_frame_tests!(true, f32, arm64simd_unicast_subf_f32_16n, |a, b| b - a); + crate::unicast_frame_tests!(true, f32, arm64simd_unicast_min_f32_16n, |a, b| a.min(b)); + crate::unicast_frame_tests!(true, f32, arm64simd_unicast_max_f32_16n, |a, b| a.max(b)); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/cortex_a53.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/cortex_a53.rs new file mode 100644 index 0000000..19b72a3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/cortex_a53.rs @@ -0,0 +1,1197 @@ +use crate::frame::mmm::CostModel; +pub fn model() -> CostModel<'static> { + CostModel { + big_product_mkn_threshold: 4193280.0, + big_product_kernel_choice: "arm64simd_mmm_f32_12x8_a53", + kernels: &[ + "arm64simd_mmm_f32_12x8_a53", + "arm64simd_mmm_f32_12x8_gen", + "arm64simd_mmm_f32_16x4_a53", + "arm64simd_mmm_f32_16x4_gen", + "arm64simd_mmm_f32_24x4_a53", + "arm64simd_mmm_f32_24x4_gen", + "arm64simd_mmm_f32_8x8_a53", + "arm64simd_mmm_f32_8x8_gen", + "generic_f32_4x4", + ], + mrs: &[4, 8, 12, 16, 24], + nrs: &[4, 8], + feat_norm_mean: &[ + 4.592185479105843, + 4.595318666792368, + 4.579484503710355, + 13.76698864960861, + 1.5094315895372235, + 0.7603118712273642, + 3.47170523138833, + 0.8752515090543259, + 5.487801810865191, + 0.9224094567404426, + 7.414361167002012, + 0.9387575452716298, + 11.415367203219317, + 0.959758551307847, + 1.5074195171026157, + 0.750125754527163, + 3.47170523138833, + 0.875125754527163, + ], + feat_norm_stddev: &[ + 1.2629893666668983, + 1.2446322895476982, + 1.258916587498509, + 1.3105293102858375, + 1.1063478713873012, + 0.4268931127321023, + 2.3025561444671223, + 0.330433510637837, + 3.431728816936762, + 0.2675261685447694, + 4.624258056138275, + 0.23977451171303063, + 6.954988241153163, + 0.19652499713600946, + 1.1207056563030822, + 0.4329400731304941, + 2.292868878895526, + 0.3305762669799629, + ], + w1: &[ + -0.6321063041687012, + 0.24184978008270264, + -0.4356610178947449, + -0.1422707587480545, + 0.10410869866609573, + 0.09415467828512192, + 0.1568029671907425, + -0.25644537806510925, + -0.37143954634666443, + 0.15696385502815247, + 0.050514884293079376, + -0.07972156256437302, + -0.253411203622818, + 0.27587205171585083, + 0.02698700875043869, + -0.07245094329118729, + -0.013899300247430801, + 0.022088056430220604, + 0.2630922496318817, + -0.06870237737894058, + 0.40947580337524414, + 0.22110328078269958, + 0.03808840364217758, + -0.008957616984844208, + -0.11127127707004547, + 0.07818343490362167, + 0.025474127382040024, + -0.09513817727565765, + 0.10613243281841278, + 0.029441041871905327, + 0.0819312185049057, + -0.03519295156002045, + -0.3130439519882202, + 0.4705337882041931, + 0.4476615786552429, + -0.616556704044342, + 0.2223544716835022, + -0.23584842681884766, + -0.3312308192253113, + 0.18874213099479675, + -0.033394988626241684, + 0.09006354957818985, + 0.014722823165357113, + 0.0877116397023201, + 0.07635975629091263, + 0.04284617677330971, + -0.029695890843868256, + -0.05645013228058815, + -0.096514992415905, + 0.16431200504302979, + 0.11922749876976013, + -0.08329842984676361, + -0.15593503415584564, + 0.33497852087020874, + 0.5143201947212219, + -0.4143742322921753, + -0.07121813297271729, + 0.032980211079120636, + -0.014759342186152935, + -0.10575086623430252, + -0.08755142986774445, + 0.053559254854917526, + 0.2959750294685364, + -0.210640087723732, + -0.09462635219097137, + 0.14600691199302673, + 0.22388464212417603, + -0.185477152466774, + -0.100673608481884, + -0.10946766287088394, + 0.03957876190543175, + -0.10485030710697174, + 0.01792730763554573, + 0.15610192716121674, + -0.14726269245147705, + 0.30900657176971436, + 0.21081387996673584, + -0.06592089682817459, + 0.03168980032205582, + 0.20096036791801453, + 0.021350117400288582, + -0.04456694424152374, + 0.35106319189071655, + 0.04561518132686615, + -0.14208926260471344, + 0.06227286159992218, + -0.20092618465423584, + 0.08163813501596451, + 0.23094142973423004, + -0.0332462415099144, + 0.26035502552986145, + 0.4639679193496704, + 0.11891252547502518, + 0.4722647964954376, + -0.025709064677357674, + 0.1651654839515686, + -0.009242026135325432, + 0.02252785675227642, + 0.13325856626033783, + -0.32073062658309937, + -0.05948975682258606, + -0.07114000618457794, + -0.04468341916799545, + -0.002579547930508852, + 0.2056179940700531, + -0.14614446461200714, + -0.11110267788171768, + 0.09043771028518677, + 0.135812908411026, + -0.3300320506095886, + 0.290109783411026, + 0.23399846255779266, + -0.04882314056158066, + -7.729629578534514e-05, + 0.04754950851202011, + 0.003435821272432804, + 0.1115187332034111, + -0.08208155632019043, + 0.018088344484567642, + -0.01600349321961403, + -0.025757616385817528, + 0.060233402997255325, + -0.08445348590612411, + 0.375010222196579, + 0.7828134298324585, + -0.836024820804596, + 0.041282471269369125, + -0.07747451961040497, + 0.31279265880584717, + -0.05552798509597778, + -0.03274049609899521, + -0.1147448793053627, + -0.1660863310098648, + 0.390122652053833, + 0.29283249378204346, + -0.0705522671341896, + -0.2927100956439972, + 0.038575850427150726, + -0.15336857736110687, + -0.028894517570734024, + -0.06372164189815521, + 0.2578844130039215, + 0.060502175241708755, + -0.14235782623291016, + 0.6358739137649536, + -0.2645033001899719, + 0.01847453974187374, + 0.3809853792190552, + 0.0059107388369739056, + -0.07365082949399948, + -0.17490413784980774, + 0.26099810004234314, + 0.38216090202331543, + -0.44192376732826233, + -0.1497800052165985, + 0.11983825266361237, + 0.05704215168952942, + -0.09331715852022171, + -0.027353238314390182, + 0.07132093608379364, + 0.013686291873455048, + -0.14973664283752441, + -0.6386663317680359, + -0.42794787883758545, + 0.43632233142852783, + -0.022474655881524086, + 0.011099671013653278, + 0.08784982562065125, + 0.046248968690633774, + 0.011553826741874218, + 0.0328642763197422, + 0.08678832650184631, + 0.3153251111507416, + -0.15444470942020416, + -0.5339609980583191, + 0.10007581859827042, + -0.02821769379079342, + -0.3091129660606384, + -0.6009559631347656, + -0.555920422077179, + 0.9594710469245911, + -0.5884919166564941, + -0.08316593617200851, + 0.07074970006942749, + 0.026868166401982307, + 0.03690064698457718, + -0.2468167096376419, + 0.20655325055122375, + 0.2654767632484436, + -0.11032287031412125, + 0.09603621065616608, + 0.12746618688106537, + 0.11097392439842224, + -0.046335164457559586, + 0.2753968834877014, + -0.4040895402431488, + -0.20803606510162354, + 0.29299837350845337, + -0.21050886809825897, + -0.02308674342930317, + 0.32019543647766113, + -0.010012545622885227, + -0.07219666987657547, + 0.03816547617316246, + -0.03670865297317505, + -0.023583250120282173, + -0.2030763179063797, + 0.4087490737438202, + 0.19682352244853973, + -0.061049312353134155, + -0.34018784761428833, + 0.4121433198451996, + -0.10742263495922089, + -0.2883375287055969, + 0.15564028918743134, + -0.014489974826574326, + -0.40427249670028687, + 0.04029366746544838, + -0.46333804726600647, + -0.5811125636100769, + 0.1686166524887085, + -0.08247993886470795, + 0.02783152647316456, + -0.07444962859153748, + -0.11033248156309128, + 0.17976728081703186, + -0.05866902321577072, + -0.037863120436668396, + 0.016240332275629044, + 0.08362828195095062, + 0.04285397008061409, + -0.2676204442977905, + -0.18113869428634644, + 0.10164932906627655, + 0.5798585414886475, + -0.2936221659183502, + -0.16815273463726044, + 0.3153108060359955, + 0.1320323497056961, + 0.29474350810050964, + -0.31565147638320923, + 0.032277628779411316, + 0.5137525796890259, + 0.13915763795375824, + -0.08313784748315811, + 0.0871160700917244, + 0.07447603344917297, + -0.4863177537918091, + 0.022499559447169304, + 0.07244526594877243, + -0.1484450399875641, + -0.08256664127111435, + 0.09993510693311691, + 0.33980417251586914, + -0.5465939044952393, + -0.18684262037277222, + 0.050183601677417755, + 0.015223318710923195, + -0.32613685727119446, + 0.2532300353050232, + 0.21044038236141205, + -0.24877160787582397, + 0.17659279704093933, + -0.14793306589126587, + 0.054353710263967514, + -0.07312241941690445, + 0.04128497466444969, + -0.0071349963545799255, + -0.17010675370693207, + 0.3045605719089508, + -0.391606867313385, + 0.19206605851650238, + 0.10403380542993546, + -0.3808597922325134, + -0.016270365566015244, + -0.09313700348138809, + 0.11184006929397583, + 0.01242944784462452, + -0.03349926695227623, + -0.1107369139790535, + 0.2315940409898758, + 0.03170541673898697, + -0.48357459902763367, + 0.21056240797042847, + -0.25072887539863586, + 0.3221265375614166, + 0.5108669400215149, + -0.6159838438034058, + -0.5540208220481873, + 0.38405123353004456, + 0.1323588639497757, + -0.11752784997224808, + 0.07821227610111237, + 0.0494898185133934, + 0.28607267141342163, + -0.45723024010658264, + -0.5914809703826904, + -0.15741930902004242, + -0.09551641345024109, + -0.769051730632782, + -0.2119017094373703, + -0.8505933284759521, + 0.025818098336458206, + 0.11196669936180115, + 0.013385393656790257, + -0.02640729956328869, + -0.061663247644901276, + -0.012524818070232868, + -0.8237857222557068, + -0.40553018450737, + -0.06807617098093033, + -0.07508324831724167, + -0.011943532153964043, + 0.07591933757066727, + 0.18625806272029877, + -0.14417743682861328, + 0.0031204342376440763, + -0.031199704855680466, + -0.037418268620967865, + -0.062444642186164856, + 0.0434197299182415, + -0.12462416291236877, + -0.256317675113678, + -0.0023087849840521812, + 0.20042477548122406, + 0.17625926434993744, + -0.21970611810684204, + 0.1626158505678177, + -0.09550918638706207, + -0.10577445477247238, + -0.17239737510681152, + 0.28190216422080994, + 0.003485368099063635, + -0.24596424400806427, + 0.5330491662025452, + -0.6179713010787964, + -0.19186368584632874, + 0.04049135372042656, + 0.005797799210995436, + 0.10468537360429764, + -0.03522713482379913, + 0.2554764151573181, + -0.6601210832595825, + 0.3554987609386444, + -0.1528356373310089, + -0.2578294575214386, + -0.01912580616772175, + 0.14837700128555298, + 0.28032413125038147, + 0.6525465250015259, + -0.16390740871429443, + -0.12456659972667694, + -0.04434182122349739, + 0.44120529294013977, + -0.06832294911146164, + 0.4077378511428833, + -0.07938709110021591, + 0.23457404971122742, + -0.05966708064079285, + 0.09640492498874664, + 0.7555295825004578, + -0.3110663592815399, + 0.035311225801706314, + 0.25391876697540283, + 0.09088675677776337, + 0.03320888802409172, + -0.1745719611644745, + 0.2270633578300476, + 0.2851920425891876, + -0.07204318791627884, + -0.05483328923583031, + 0.189837247133255, + -0.15304607152938843, + -0.08311894536018372, + -0.06649994850158691, + -0.0776129737496376, + 0.11864881962537766, + -0.06670717149972916, + -0.00406235596165061, + -0.6984686255455017, + 0.28291743993759155, + -0.04160117730498314, + -0.09169034659862518, + 0.14924104511737823, + 0.46138641238212585, + -0.29699283838272095, + -0.6411864757537842, + 0.26037612557411194, + 0.21487018465995789, + -0.20806393027305603, + -0.4174681007862091, + 0.1901395320892334, + 0.049021925777196884, + 0.2822348475456238, + -0.03862098604440689, + 0.029824024066329002, + 0.2657202184200287, + -0.43108099699020386, + 0.37041717767715454, + -0.025845345109701157, + -0.09200481325387955, + -0.017871620133519173, + 0.281535267829895, + -0.20838744938373566, + -0.400356650352478, + 0.4133286476135254, + -0.08745774626731873, + 0.02171195112168789, + 0.4766440987586975, + -0.24629971385002136, + 0.2504408657550812, + -0.5850875973701477, + -0.49699774384498596, + 0.7086884379386902, + -0.479250967502594, + 0.6140879392623901, + 0.0023341099731624126, + -0.06628652662038803, + -0.0873338133096695, + -0.2862805724143982, + 0.28077220916748047, + 0.030578527599573135, + -0.281633198261261, + -0.7042887806892395, + -0.03409203886985779, + 0.3272986114025116, + 0.3397904634475708, + -0.7069221138954163, + 0.09408266842365265, + -0.05243761092424393, + -0.20503726601600647, + 0.15679042041301727, + 0.4723545014858246, + -0.39158886671066284, + 0.17581138014793396, + 0.10779093205928802, + -0.013951681554317474, + 0.052481986582279205, + -0.36543500423431396, + 0.29497984051704407, + 0.4044850766658783, + -0.3766767382621765, + -0.07298431545495987, + 0.9660398364067078, + 0.27753373980522156, + -0.11616200953722, + 0.05277060344815254, + -0.05379771068692207, + 0.026094499975442886, + -0.011136082001030445, + -0.13593854010105133, + 0.033518679440021515, + 0.6947338581085205, + 0.6335914134979248, + -0.06526267528533936, + 0.019844267517328262, + 0.10042254626750946, + -0.16847042739391327, + -0.15717101097106934, + -0.7462965250015259, + -0.0653005987405777, + 0.057602036744356155, + 0.010834889486432076, + -0.46870648860931396, + -0.1872870922088623, + 0.3152116537094116, + 0.0731910765171051, + -0.13902369141578674, + 0.10666802525520325, + 0.3094567656517029, + -0.926356315612793, + -0.38388797640800476, + -0.02191060781478882, + -0.005548040382564068, + -0.20935170352458954, + 0.24779647588729858, + 0.12304577976465225, + -0.2883053123950958, + 0.019766222685575485, + -0.029659172520041466, + 0.06051887571811676, + -0.01741836965084076, + 0.04409812018275261, + 0.011840295046567917, + -0.14320705831050873, + 0.31673386693000793, + -0.069312185049057, + -0.00935965683311224, + 0.019028477370738983, + -0.1078404039144516, + -0.12472966313362122, + 0.10027194768190384, + 0.31244829297065735, + -0.10855710506439209, + -0.3165830969810486, + 0.4076120853424072, + 0.05742274224758148, + 0.17263729870319366, + 0.3141464293003082, + -0.13655878603458405, + 0.07613589614629745, + -0.10808823257684708, + -0.19837258756160736, + 0.16735948622226715, + 0.055960867553949356, + 0.005388774909079075, + -0.30227115750312805, + -0.009724846109747887, + -0.11610261350870132, + 0.05133519321680069, + -0.029441826045513153, + 0.06810834258794785, + -0.13311177492141724, + 0.2196519374847412, + 0.19138571619987488, + -0.2621391713619232, + 0.11996466666460037, + -0.05961257219314575, + 0.1763487011194229, + -0.10918399691581726, + -0.14629563689231873, + 0.5217060446739197, + -0.0012722538085654378, + 0.08564157783985138, + -0.6640400290489197, + -0.41702714562416077, + 0.045037489384412766, + -0.059789709746837616, + -0.05092751979827881, + 0.10446680337190628, + -0.05335049331188202, + 0.0846114456653595, + 0.04981796815991402, + -0.14310699701309204, + 0.01863306201994419, + -0.0474325567483902, + 0.23124581575393677, + -0.6166588068008423, + -0.7533295154571533, + -1.1133880615234375, + -0.1241607666015625, + -0.5540894865989685, + 0.2806711494922638, + -0.4259497821331024, + -0.07380827516317368, + 0.009988346137106419, + 0.3110937178134918, + 0.0072226757183671, + 0.2422133982181549, + -0.351376473903656, + -0.5103139877319336, + 0.5470908284187317, + -0.14952707290649414, + -0.005531645845621824, + -0.24725599586963654, + 0.1639375537633896, + 0.07172811776399612, + -0.1566568911075592, + 0.32833099365234375, + 0.06875353306531906, + -0.17773276567459106, + -0.09706790000200272, + -0.019849322736263275, + 0.1257631778717041, + 0.02103520557284355, + 0.12721672654151917, + 0.012451020069420338, + 0.039879027754068375, + 0.17779605090618134, + -0.09887054562568665, + -0.08146625012159348, + 0.05893132835626602, + 0.18479469418525696, + -0.2479601502418518, + -0.26928654313087463, + 0.3720027506351471, + -0.45930227637290955, + 0.3673400282859802, + 0.016545426100492477, + 0.13507097959518433, + -0.006458526011556387, + 0.036685895174741745, + 0.309455007314682, + -0.23917894065380096, + -0.11758854985237122, + 0.2146540731191635, + -0.11578961461782455, + 0.006646907888352871, + -0.04229713976383209, + 0.09812270104885101, + 0.06730903685092926, + 0.28935620188713074, + -0.02212020941078663, + 0.007341589778661728, + -0.1257125288248062, + -0.4639318287372589, + 0.41743314266204834, + 0.40524497628211975, + -0.20389464497566223, + 0.1286880075931549, + 0.05365758389234543, + -0.14487741887569427, + 0.1511518359184265, + 0.11219878494739532, + 0.13080842792987823, + -0.175934836268425, + -0.08939457684755325, + 0.16476190090179443, + -0.061722587794065475, + 0.15382836759090424, + 0.15293729305267334, + -0.23814627528190613, + -0.778872013092041, + 0.2813372313976288, + 0.20388194918632507, + -0.34535032510757446, + -0.014981378801167011, + 0.1560390293598175, + 0.534339189529419, + 0.7075706124305725, + -0.20866382122039795, + 0.050050001591444016, + -0.030285198241472244, + 0.430580735206604, + 0.06858251988887787, + 0.32321590185165405, + 0.006104054860770702, + 0.11919829249382019, + -0.09377042204141617, + -0.028785547241568565, + 0.489607572555542, + -0.321664422750473, + 0.020770607516169548, + 0.5259214639663696, + -0.0682888925075531, + 0.10569659620523453, + -0.18257132172584534, + 0.2565872073173523, + 0.2177353799343109, + 0.029641704633831978, + 0.0678875744342804, + 0.1679811030626297, + -0.04851052165031433, + -0.1633165180683136, + -0.007416700944304466, + -0.06638842821121216, + 0.06177712231874466, + -0.0709109827876091, + -0.11213518679141998, + -0.20582593977451324, + 0.7092531323432922, + 0.43438467383384705, + -0.0060964771546423435, + -0.12442151457071304, + -0.008676152676343918, + 0.21390584111213684, + -0.014475004747509956, + -0.7601429224014282, + 0.15622451901435852, + -0.3261253833770752, + 0.005610095337033272, + -0.5111817121505737, + -0.003055301494896412, + 0.32741662859916687, + -0.022710084915161133, + -0.24255472421646118, + -0.6487520933151245, + 0.08797790110111237, + 0.2754897177219391, + -0.2213398665189743, + -0.17206217348575592, + 0.1177680641412735, + 0.16599608957767487, + -0.19922694563865662, + -0.07098120450973511, + -0.1628963202238083, + 0.03356413170695305, + -0.24303652346134186, + -0.2067747414112091, + 0.1192406490445137, + -0.020932691171765327, + 0.07735628634691238, + 0.24762177467346191, + -0.3007707893848419, + -0.43011191487312317, + -0.07597793638706207, + 0.2528873085975647, + -0.3795652985572815, + 0.14651291072368622, + 0.07552091032266617, + 0.026706784963607788, + -0.11118876934051514, + 0.0460294634103775, + 0.4268769323825836, + 0.32645294070243835, + -0.09493713080883026, + 0.18892213702201843, + 0.17980137467384338, + 0.06521839648485184, + 0.03702569752931595, + 0.05443478748202324, + -0.030978504568338394, + -0.11806164681911469, + -0.20229215919971466, + 0.6260767579078674, + 0.6068219542503357, + -0.060956377536058426, + 0.05200914293527603, + 0.04499080404639244, + -0.09300816804170609, + 0.0501115508377552, + 0.9676806926727295, + -0.12394528090953827, + 0.17313909530639648, + -0.0274575874209404, + 1.0245190858840942, + -0.24425312876701355, + 0.3827340602874756, + 0.270155131816864, + -0.7169324159622192, + ], + b1: &[ + -0.518636167049408, + 0.7074531316757202, + -0.4965735971927643, + 0.6063699126243591, + -0.3258720934391022, + 0.4608336389064789, + 0.8324258327484131, + -0.6118353605270386, + 0.8226121664047241, + 0.3534131944179535, + -0.43312883377075195, + -0.05448569357395172, + -0.5826212167739868, + 0.8478071689605713, + 0.23062080144882202, + -0.30911386013031006, + -0.5776869058609009, + 0.5107449293136597, + 0.18762148916721344, + 0.2889731228351593, + -0.5579098463058472, + 0.7818499207496643, + 0.7910265922546387, + -0.4228874444961548, + 0.6197248697280884, + -0.4563252627849579, + 0.27223169803619385, + -0.2859383523464203, + -0.4862801730632782, + -0.7853735089302063, + -0.1534343808889389, + -0.5592636466026306, + -0.6364999413490295, + -0.5210756063461304, + 0.3506944477558136, + -0.5348182916641235, + -0.5098673105239868, + 0.45690369606018066, + -0.3907462954521179, + 0.8493368029594421, + ], + w2: &[ + -0.525189995765686, + 0.44041961431503296, + -0.4107511341571808, + 0.3741440176963806, + -0.02630656771361828, + 0.27733951807022095, + 0.3907228410243988, + -0.05409616604447365, + 0.3991526663303375, + 0.24264170229434967, + -0.657869279384613, + -0.3758363425731659, + -0.5133534669876099, + 0.3480457663536072, + 0.5088834166526794, + 0.0942729115486145, + -0.4167974889278412, + 0.4895906448364258, + 0.17553496360778809, + 0.3702719211578369, + -0.5372111201286316, + -0.1560969352722168, + -0.30670106410980225, + -0.48799967765808105, + 0.4005548357963562, + -0.3075137138366699, + 0.656658947467804, + -0.4914362132549286, + -0.36532747745513916, + -0.5505443811416626, + 0.1328023225069046, + -0.3564044237136841, + -0.467242956161499, + -0.3465808629989624, + 0.4501214027404785, + -0.4742763936519623, + -0.35285890102386475, + 0.46182748675346375, + -0.28942185640335083, + 0.2825036346912384, + -0.1725425124168396, + -0.17012473940849304, + 0.5306965708732605, + -0.34125325083732605, + 0.21301832795143127, + -0.49370092153549194, + -0.06135714799165726, + 0.5665233135223389, + -0.01510544028133154, + -0.0015591675182804465, + 0.4308379292488098, + 0.09525317698717117, + 0.06129995733499527, + -0.06124228611588478, + -0.28377535939216614, + -0.038286369293928146, + 0.19221894443035126, + -0.45041826367378235, + -0.4307488799095154, + -0.30516454577445984, + 0.3670405447483063, + -0.1779327690601349, + -0.36808863282203674, + 0.344722718000412, + -0.2691067159175873, + 0.5803861021995544, + -0.42112261056900024, + 0.1169033870100975, + 0.35742461681365967, + 0.16161565482616425, + 0.44920068979263306, + 0.2572435438632965, + 0.263318806886673, + 0.7236857414245605, + -0.2759736180305481, + 0.37376394867897034, + 0.37350600957870483, + -0.4067005515098572, + 0.18588955700397491, + -0.4281120300292969, + 0.4204690456390381, + -0.448592871427536, + 0.11808016151189804, + -0.4660882353782654, + 0.33337321877479553, + -0.11569353938102722, + -0.589764416217804, + -0.17854063212871552, + -0.44001755118370056, + 0.7101057767868042, + 0.057653751224279404, + 0.3937684893608093, + 0.257487416267395, + -0.38924211263656616, + 0.08511713892221451, + 0.10950952023267746, + 0.0917661041021347, + -0.25429144501686096, + 0.6342174410820007, + -0.15891794860363007, + -0.021509289741516113, + 0.535305380821228, + 0.28721731901168823, + -0.32432296872138977, + -0.26846611499786377, + 0.07051636278629303, + -0.12710770964622498, + 0.14568471908569336, + 0.6293584704399109, + 0.4198862612247467, + -0.8883509039878845, + 0.5271400809288025, + 0.17345309257507324, + 0.1771862506866455, + -0.214192733168602, + 0.17817191779613495, + 0.44757506251335144, + 0.04112042486667633, + 0.6819244027137756, + -0.7277362942695618, + 0.19224950671195984, + -0.2905896008014679, + 0.5791959762573242, + -0.4898945093154907, + 0.47323065996170044, + -0.40173205733299255, + -0.36294564604759216, + 0.6861273050308228, + -0.2955973744392395, + -0.19740070402622223, + 0.4044080674648285, + -0.11244003474712372, + 0.58234703540802, + -0.31175708770751953, + -0.3454722762107849, + 0.12274620682001114, + 0.29693669080734253, + -0.41234102845191956, + -0.1583351045846939, + -0.2763107419013977, + 0.34174609184265137, + -0.7301539182662964, + -0.4137580394744873, + 0.5135444402694702, + -0.19664454460144043, + 0.3913029730319977, + -0.47720086574554443, + 0.2519521415233612, + 0.3860025703907013, + 0.4073657691478729, + 0.06604084372520447, + 0.32879960536956787, + 0.4341438114643097, + 0.4072171449661255, + -0.3755425810813904, + 0.29250237345695496, + 0.4723772704601288, + -0.39177075028419495, + 0.3535446524620056, + -0.5977760553359985, + -0.11535356938838959, + -0.8606860637664795, + 0.3202466070652008, + 0.534551203250885, + -0.10786011070013046, + 0.5766461491584778, + -1.0034655332565308, + -0.08353354036808014, + 0.20165663957595825, + -0.8530645370483398, + 0.2801732122898102, + -0.2713226079940796, + 0.460101842880249, + 0.5550602078437805, + 0.11862986534833908, + -0.8431587219238281, + -0.41269758343696594, + -0.36862486600875854, + 0.08385410159826279, + 0.1634000688791275, + -0.22930988669395447, + -0.39085301756858826, + 0.8845512270927429, + 0.2522968053817749, + 0.3779301643371582, + 0.3454946279525757, + -0.14984408020973206, + 0.2937467098236084, + 0.3651972711086273, + 1.1317671537399292, + -0.4535387456417084, + 0.07272656261920929, + -0.29987066984176636, + -0.03405649587512016, + 0.1012202724814415, + -0.12492970377206802, + -0.048626113682985306, + -0.3150321841239929, + -0.4124220013618469, + -0.7775830030441284, + 0.25562793016433716, + -0.4026365876197815, + 0.27681317925453186, + -0.3169574439525604, + 0.414761483669281, + -0.37095436453819275, + -0.2815983295440674, + 0.6821384429931641, + -0.23631460964679718, + -0.391885370016098, + 0.32081300020217896, + 0.029309673234820366, + 0.3151959478855133, + -0.23872429132461548, + -0.2680605947971344, + 0.2245175689458847, + 0.28024742007255554, + -0.5187304615974426, + -0.17155316472053528, + -0.18662460148334503, + 0.44196388125419617, + -0.7731465697288513, + -0.39956656098365784, + 0.4926709830760956, + -0.2705640196800232, + 0.5851831436157227, + -0.28655296564102173, + 0.21914565563201904, + 0.42291808128356934, + 0.3754308521747589, + 0.12476411461830139, + 0.4564429223537445, + 0.41455739736557007, + 0.24721866846084595, + -0.39062193036079407, + 0.47335484623908997, + 0.4390261769294739, + -0.2776612639427185, + 0.36352279782295227, + -0.4658246338367462, + 0.5458199977874756, + 0.2368425875902176, + -0.28375834226608276, + -0.21349868178367615, + -0.12575705349445343, + -0.314109742641449, + 0.2133757472038269, + -0.4604170322418213, + -0.5457999110221863, + 0.347943514585495, + 0.3864844739437103, + 0.2128392457962036, + 0.06274894624948502, + -0.5941122174263, + -0.4954967200756073, + 0.3897503614425659, + 0.6681548953056335, + 0.011607992462813854, + -0.5754616260528564, + -0.4551040530204773, + 0.14332124590873718, + 0.5475043058395386, + 0.35485684871673584, + 0.516143798828125, + -0.43508225679397583, + -0.2927212119102478, + -0.38220953941345215, + 0.22585861384868622, + -0.49666696786880493, + -0.47814127802848816, + 0.6455125212669373, + -0.4184291362762451, + 0.5714888572692871, + -0.06349734216928482, + -0.337534636259079, + 0.08359762281179428, + -0.6663680672645569, + -0.05490731820464134, + 0.27789443731307983, + 0.44944822788238525, + -0.12919825315475464, + -0.24064187705516815, + 0.3863179683685303, + -0.21315856277942657, + -0.010893935337662697, + -0.49465489387512207, + -0.1953386515378952, + 0.4405977129936218, + -0.362499862909317, + -0.15224213898181915, + 0.503758430480957, + 0.13674911856651306, + 0.24574719369411469, + -0.2888658046722412, + -0.5966756939888, + 0.24279867112636566, + 0.43060633540153503, + -0.2950061857700348, + -0.3071616590023041, + -0.31878525018692017, + 0.5719135999679565, + -0.46542906761169434, + -0.33102989196777344, + 0.2584391236305237, + -0.3341030776500702, + 0.35185420513153076, + -0.5347702503204346, + 0.2021929919719696, + 0.3747906982898712, + 0.3017856478691101, + 0.4192887842655182, + 0.2290816456079483, + 0.26369208097457886, + 0.30613088607788086, + -0.2766033113002777, + 0.48649486899375916, + 0.28767234086990356, + -0.31826111674308777, + 0.47518086433410645, + -0.2643313407897949, + 0.38674306869506836, + -0.20252466201782227, + 0.2426745593547821, + -0.2963939607143402, + 0.35027387738227844, + -0.40756842494010925, + -0.17158618569374084, + 0.6504075527191162, + -0.23639068007469177, + -0.5520732998847961, + 0.34597641229629517, + 0.12782879173755646, + 0.46479496359825134, + -0.4128115773200989, + -0.4125882685184479, + 0.20131008327007294, + 0.4997844099998474, + -0.21766024827957153, + -0.2570849657058716, + -0.1471637338399887, + 0.5070111155509949, + -0.6722937226295471, + -0.5443961024284363, + 0.5341878533363342, + -0.29976886510849, + 0.6135430932044983, + -0.3595261573791504, + 0.49033448100090027, + 0.3653552234172821, + 0.2656362056732178, + 0.10900922119617462, + 0.4813465476036072, + 0.41922783851623535, + 0.2692069411277771, + -0.4056242108345032, + 0.33006641268730164, + 0.27100467681884766, + -0.5306692123413086, + 0.2701503336429596, + -0.6044796705245972, + ], + b2: &[ + 0.044342152774333954, + -0.28361865878105164, + -0.0350283607840538, + -0.129508376121521, + -0.006770995445549488, + -0.24053514003753662, + 0.3617520332336426, + -0.3381704092025757, + -0.24953331053256989, + ], + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/cortex_a55.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/cortex_a55.rs new file mode 100644 index 0000000..b87ac1a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/cortex_a55.rs @@ -0,0 +1,1365 @@ +use crate::frame::mmm::CostModel; +pub fn model() -> CostModel<'static> { + CostModel { + big_product_mkn_threshold: 263214080.0, + big_product_kernel_choice: "arm64simd_mmm_f32_12x8_a55", + kernels: &[ + "arm64simd_mmm_f32_12x8_a53", + "arm64simd_mmm_f32_12x8_a55", + "arm64simd_mmm_f32_12x8_gen", + "arm64simd_mmm_f32_16x4_a53", + "arm64simd_mmm_f32_16x4_a55", + "arm64simd_mmm_f32_16x4_gen", + "arm64simd_mmm_f32_24x4_a53", + "arm64simd_mmm_f32_24x4_a55", + "arm64simd_mmm_f32_24x4_gen", + "arm64simd_mmm_f32_8x8_a53", + "arm64simd_mmm_f32_8x8_a55", + "arm64simd_mmm_f32_8x8_gen", + "generic_f32_4x4", + ], + mrs: &[4, 8, 12, 16, 24], + nrs: &[4, 8], + feat_norm_mean: &[ + 5.27886946965165, + 6.250454700699139, + 5.241114620514529, + 16.770438790865423, + 1.540625, + 0.770625, + 3.518125, + 0.8775, + 5.560625, + 0.923125, + 7.453125, + 0.943125, + 11.613125, + 0.9575, + 1.509375, + 0.771875, + 3.581875, + 0.898125, + ], + feat_norm_stddev: &[ + 0.9509890252368828, + 0.6930410342704738, + 1.0261938261805659, + 1.600617293156687, + 1.0981118382819681, + 0.42043086158725473, + 2.338198341538852, + 0.3278623949159141, + 3.494112850120205, + 0.26639300736881577, + 4.56457037785321, + 0.2316036147710134, + 7.043415558830455, + 0.20172691937369452, + 1.0925484471523377, + 0.4196236222795381, + 2.273113830052299, + 0.30248385804039707, + ], + w1: &[ + -0.13682155311107635, + -0.1783919334411621, + 0.26539096236228943, + 0.19552235305309296, + -0.10618806630373001, + 0.13501706719398499, + 0.21776071190834045, + 0.08390733599662781, + -0.2215081751346588, + 0.18829140067100525, + -0.20535176992416382, + -0.0463368222117424, + 0.05815611779689789, + -0.13855215907096863, + -0.024539709091186523, + -0.4855460524559021, + -0.414151668548584, + -0.7574286460876465, + 0.8987273573875427, + 0.5316352844238281, + 0.8244147896766663, + 0.8388808369636536, + -0.02545193023979664, + 0.04357631504535675, + -0.007071307860314846, + 0.18223997950553894, + -0.04292978346347809, + 0.004330582916736603, + -0.013073648326098919, + -0.04028080403804779, + -0.09901119023561478, + 0.062175191938877106, + -0.006247916258871555, + 0.009531030431389809, + 0.09731218218803406, + 0.004297865089029074, + 0.6260067224502563, + -0.10042139887809753, + 0.807989239692688, + 0.6866835951805115, + -0.018399836495518684, + -0.07194910198450089, + -0.18889868259429932, + -0.07729395478963852, + -0.03907148540019989, + 0.017019111663103104, + 0.06159460172057152, + -0.02395886555314064, + 0.23730705678462982, + -0.15546496212482452, + -0.04492897167801857, + -0.003982013091444969, + -0.09160511195659637, + 0.03185845538973808, + -0.27577653527259827, + 0.5699247121810913, + -0.6027079820632935, + -0.4136800467967987, + -0.04364140331745148, + -0.11226192861795425, + 0.16899903118610382, + -0.11524038016796112, + 0.12308179587125778, + 0.027925828471779823, + -0.06269104778766632, + 0.11644940823316574, + -0.369202196598053, + 0.3338239789009094, + -0.06509242206811905, + 0.2303273230791092, + 0.018171854317188263, + -0.08709719777107239, + 0.18228614330291748, + -0.071574367582798, + -0.012407014146447182, + -0.284942090511322, + -0.1326635330915451, + -0.08634418249130249, + 0.11018547415733337, + -0.09423547983169556, + 0.13697068393230438, + -0.03515861555933952, + 0.014629656448960304, + -0.21159854531288147, + 0.15693463385105133, + -0.021487032994627953, + 0.032396819442510605, + 0.028369005769491196, + 0.08724819868803024, + -0.13204769790172577, + 0.4691336452960968, + 0.1237262561917305, + -0.06020978465676308, + 0.24037614464759827, + 0.05237792432308197, + -0.10641840100288391, + 0.1820996105670929, + 0.6079273819923401, + -0.4903985857963562, + 0.40744978189468384, + 0.43370547890663147, + 0.5092437863349915, + 0.0810965895652771, + 0.4670366048812866, + -0.11692337691783905, + -0.013550599105656147, + -0.364605575799942, + 0.34470170736312866, + -0.01755279302597046, + 0.30621764063835144, + 0.35784396529197693, + 0.42736300826072693, + 0.022546129301190376, + 0.08497388660907745, + 0.07601173967123032, + 0.0696730837225914, + -0.21918217837810516, + 0.6236687898635864, + -0.0793512761592865, + -0.0668395534157753, + 0.010559543035924435, + 0.46621084213256836, + -0.2632196843624115, + -0.03991322219371796, + 0.1392994076013565, + 0.003188274335116148, + -0.2655166983604431, + -0.22143644094467163, + -0.19157607853412628, + -0.39395904541015625, + -0.021266555413603783, + 0.08848410844802856, + 0.08152330666780472, + 0.013220606371760368, + -0.18424198031425476, + 0.05234640836715698, + 0.05919161066412926, + -0.16255362331867218, + -0.04549096152186394, + 0.044437166303396225, + 0.21704396605491638, + -0.5149197578430176, + -0.6047705411911011, + -0.8048356175422668, + -0.36901935935020447, + -0.19035962224006653, + 1.3252514600753784, + 0.19824109971523285, + 0.07630860805511475, + -0.011165079660713673, + 0.011559495702385902, + 0.10554458945989609, + -0.12820255756378174, + 0.29352235794067383, + -0.06662449240684509, + -0.15792854130268097, + 0.0345480814576149, + -0.04881494492292404, + -0.06912268698215485, + -0.00013739474525209516, + -0.1597173660993576, + 0.34323570132255554, + 0.34446775913238525, + 0.3795395493507385, + 0.017453864216804504, + 0.1102253794670105, + 0.04026523232460022, + 0.1107630804181099, + 0.10295291990041733, + 0.5326219201087952, + 0.1749747395515442, + -0.2661803066730499, + 0.11752097308635712, + 0.08010037988424301, + -0.5501991510391235, + -0.059987347573041916, + -0.04125819355249405, + 0.16356444358825684, + 0.020170046016573906, + -0.08306766301393509, + 0.17777052521705627, + 0.4687126874923706, + 0.7723219394683838, + 0.7309747934341431, + -0.019829215481877327, + 0.10945341736078262, + 0.06796073168516159, + -0.12042505294084549, + -0.26762208342552185, + 0.10846878588199615, + 0.013867417350411415, + 0.01077105849981308, + -0.10193657130002975, + -0.1757654845714569, + -0.245382159948349, + 0.20442898571491241, + 0.10115985572338104, + 0.2514199912548065, + -0.3793720304965973, + -0.6926521062850952, + -0.6686761975288391, + -0.607191264629364, + 0.16187654435634613, + -0.0073340232484042645, + -0.09948350489139557, + -0.21431320905685425, + -0.12334707379341125, + -0.15290899574756622, + -0.026063116267323494, + 0.26553207635879517, + 0.18921764194965363, + -0.1665697544813156, + -0.00264778733253479, + 0.20274107158184052, + 0.3660823404788971, + -0.35731416940689087, + 0.50246661901474, + 0.2781502604484558, + 0.1629776805639267, + -0.03493829071521759, + -0.16012291610240936, + -0.08139592409133911, + -0.1440155804157257, + 0.32721832394599915, + 0.1312151998281479, + 0.17874418199062347, + 0.06143738701939583, + -0.05158458650112152, + 0.4802771806716919, + -0.6857288479804993, + 0.08245638012886047, + -0.09577414393424988, + -0.12872998416423798, + 0.16155612468719482, + 0.24089869856834412, + 0.44030725955963135, + -0.30994167923927307, + 0.12139064073562622, + 0.029418930411338806, + -0.051672156900167465, + -0.10080718994140625, + -0.007311842869967222, + -0.15189751982688904, + -0.1559375822544098, + 0.2731820344924927, + -0.03627878054976463, + 0.10538394004106522, + 0.15048423409461975, + 0.12981411814689636, + 0.0002639668236952275, + 0.05666665732860565, + 0.08173252642154694, + -0.16131722927093506, + -0.043261025100946426, + -0.14845971763134003, + -0.29335740208625793, + 0.039398159831762314, + -0.02791670151054859, + 0.22897064685821533, + -0.12178067117929459, + -0.4062419831752777, + 0.3934949040412903, + -0.05093907564878464, + -0.06126153841614723, + -0.07318481802940369, + -0.08793392032384872, + -0.01818496361374855, + -0.24753189086914062, + -0.30580347776412964, + 0.44876909255981445, + 0.5379880666732788, + 0.11587893962860107, + 0.2174995243549347, + -0.035063862800598145, + -0.0010147193679586053, + -0.12281838059425354, + -0.21301835775375366, + 0.3645245432853699, + 0.39920729398727417, + -0.45564430952072144, + 0.03503882512450218, + 0.6949061155319214, + -0.5742982625961304, + 0.38680514693260193, + -0.018345845863223076, + 0.04529440030455589, + -0.04468340799212456, + -0.020917288959026337, + 0.2523670792579651, + -0.4574699103832245, + 0.17178472876548767, + -0.12147565186023712, + 0.043810319155454636, + -0.17998050153255463, + -0.09663069248199463, + -0.03498067706823349, + 0.06111514940857887, + -0.11410824209451675, + 0.18208050727844238, + -0.09109053015708923, + 0.08489643037319183, + 0.15014725923538208, + 0.18506401777267456, + -0.060843177139759064, + -0.11932594329118729, + 0.11290943622589111, + -0.23226700723171234, + -0.2114422470331192, + -0.36001038551330566, + -0.29864072799682617, + -0.05599717050790787, + -0.21294310688972473, + -0.1301364004611969, + -0.4993196725845337, + 0.097460076212883, + 0.030209479853510857, + 0.35134217143058777, + -0.9156147837638855, + 0.0173207875341177, + -0.9142565131187439, + 0.13512593507766724, + -0.1926516443490982, + -0.2812888026237488, + 0.04805266484618187, + 0.5790673494338989, + -0.28300249576568604, + -0.10372477024793625, + 0.2964925169944763, + 0.16425621509552002, + -0.25588271021842957, + 0.37744808197021484, + -0.07827199995517731, + -0.7785226702690125, + -0.4873232841491699, + -0.0240982286632061, + -0.31732890009880066, + -0.7271391749382019, + -0.40648236870765686, + -0.08706668019294739, + -0.0876365602016449, + -0.08107846975326538, + 0.049622420221567154, + 0.5049374103546143, + -0.09109669923782349, + -0.2958216369152069, + 0.23400314152240753, + 0.0727144181728363, + -0.06163109838962555, + -0.3235352635383606, + -0.08323507010936737, + 0.06926267594099045, + 0.12505480647087097, + 0.06806384027004242, + -0.1783592253923416, + -0.09036792814731598, + 0.007250780239701271, + 0.07478834688663483, + 0.37752634286880493, + 0.10522382706403732, + -0.3126020133495331, + -0.339804470539093, + -0.2922729253768921, + -0.04612985998392105, + 0.06431944668292999, + 0.08483731746673584, + 0.12883307039737701, + -0.015924949198961258, + 0.10468991845846176, + -0.3394957184791565, + 0.23376204073429108, + -0.22720825672149658, + 0.005506275221705437, + -0.22926953434944153, + -0.10148110240697861, + 0.06526672840118408, + -0.2586720287799835, + -0.32853958010673523, + 0.3440588712692261, + -0.11197478324174881, + -0.24647162854671478, + 0.32472386956214905, + 0.18955329060554504, + 0.22783295810222626, + 0.27004650235176086, + 0.06792190670967102, + -0.25404539704322815, + -0.0421239472925663, + 0.19141103327274323, + -0.1919824779033661, + 0.024490466341376305, + -0.45774775743484497, + 0.15080632269382477, + -0.21607035398483276, + -0.15506379306316376, + -0.4421549439430237, + -0.3747740089893341, + -0.40712970495224, + -0.01002188865095377, + -0.18514835834503174, + -0.052659012377262115, + -0.009491002187132835, + -0.04560127854347229, + 0.5816720724105835, + -0.8684999942779541, + -0.6074734330177307, + -0.6023196578025818, + 0.09026342630386353, + -0.8521136045455933, + -0.677777886390686, + -0.7927519083023071, + 0.05012498050928116, + 0.006620208732783794, + 0.09600439667701721, + 0.006934305187314749, + -0.41822823882102966, + 0.5416979193687439, + 1.3451576232910156, + 0.6131516098976135, + -0.1447380781173706, + 0.09429032355546951, + 0.06888633966445923, + 0.09988542646169662, + -0.09572823345661163, + 0.09141702950000763, + 0.05828794091939926, + -0.20784544944763184, + -0.14200495183467865, + 0.014049896970391273, + -0.081334687769413, + 0.15918458998203278, + 0.001768372836522758, + 0.009856577031314373, + 0.5256384611129761, + 0.49961280822753906, + 0.5969673991203308, + 0.37020817399024963, + -0.07463415712118149, + -0.0038648881018161774, + 0.014317997731268406, + 0.07256675511598587, + 0.27220791578292847, + -0.14287996292114258, + -0.18170645833015442, + -0.021593274548649788, + -0.15909305214881897, + 0.3259168863296509, + -0.11064229905605316, + 0.12034989148378372, + 0.36166661977767944, + -0.21680544316768646, + -0.14505243301391602, + -0.24518895149230957, + -0.054052721709012985, + 0.11477477848529816, + 0.10946492105722427, + -0.004644579254090786, + -0.11873581260442734, + 0.00934956781566143, + 0.026955196633934975, + -0.0947655513882637, + -0.0432097427546978, + 0.2264525443315506, + 0.4585563540458679, + -0.2117093950510025, + 0.06864829361438751, + 0.01817937195301056, + -0.09130346775054932, + -0.031736359000205994, + -0.6623827219009399, + 0.07924489676952362, + 0.30316102504730225, + 0.06474705785512924, + 0.12052184343338013, + -0.06878554821014404, + 0.048135798424482346, + 0.14442582428455353, + -0.1945008486509323, + 0.16308918595314026, + 0.13180820643901825, + -0.3005691170692444, + -0.08318639546632767, + -0.0371159091591835, + -0.036223117262125015, + 0.27411049604415894, + -0.008904200047254562, + -0.21584218740463257, + -0.22458405792713165, + -0.2840893864631653, + 0.9380438327789307, + -0.026274412870407104, + -0.03674294427037239, + -0.039288733154535294, + 0.20259428024291992, + -0.2627299726009369, + -0.03588804602622986, + -0.09061996638774872, + 0.0026293552946299314, + -1.1599351167678833, + -0.0888570249080658, + 0.3020864427089691, + 0.10419020056724548, + -0.2301473766565323, + -0.2372182309627533, + 0.255910724401474, + -0.9108321666717529, + -0.17266617715358734, + -0.21715109050273895, + -0.4768790900707245, + 0.02349638193845749, + 0.06996935606002808, + 0.2306048572063446, + -0.2647320032119751, + -0.5029106140136719, + 0.18124276399612427, + 0.05404527485370636, + -0.556660532951355, + -0.20282964408397675, + 0.1787903904914856, + -0.13809867203235626, + 0.012665750458836555, + -0.007909105159342289, + -0.11666542291641235, + 0.192016139626503, + 0.20280246436595917, + 0.04091315343976021, + 0.21129484474658966, + 0.06015581637620926, + -0.1396055370569229, + 0.11048803478479385, + -0.22130873799324036, + 0.10175041109323502, + 0.15478093922138214, + -0.06699641793966293, + 0.16655825078487396, + -0.5767931938171387, + 0.23376262187957764, + -0.06561370939016342, + 0.08572515100240707, + 0.22690269351005554, + -0.10714394599199295, + 0.2328615039587021, + 0.06609856337308884, + 0.15064586699008942, + 0.1398843675851822, + 9.159173350781202e-05, + -0.006412057671695948, + 0.1231503039598465, + 0.2868848741054535, + -0.37850138545036316, + -0.4390513002872467, + -0.10716433078050613, + -0.16492293775081635, + -0.17774488031864166, + -0.006263014394789934, + -0.15535981953144073, + -0.15121980011463165, + -0.022719506174325943, + -0.3260766863822937, + 0.1365034133195877, + 0.7772430777549744, + 0.8306354880332947, + 0.8039601445198059, + 0.16534824669361115, + -0.03939266875386238, + -0.15611104667186737, + 0.21217003464698792, + -0.022034769877791405, + -0.025939559563994408, + 0.1058378517627716, + -0.08505864441394806, + 0.08503950387239456, + -0.0037705348804593086, + -0.0026697057764977217, + 0.3492349088191986, + 0.15157155692577362, + -0.3159380555152893, + -0.10824967920780182, + -0.04872310906648636, + 0.19715555012226105, + -0.2658633291721344, + -0.06968845427036285, + 0.009916169568896294, + 0.18593478202819824, + -0.038871243596076965, + -0.3416462540626526, + 0.1855567842721939, + 0.21629339456558228, + -0.10832708328962326, + -0.04190235957503319, + 0.2388715296983719, + -0.11624565720558167, + -0.10361404716968536, + 0.0536813959479332, + 0.12528158724308014, + -0.262010782957077, + 0.05081893876194954, + 0.29551735520362854, + 0.05958620831370354, + -0.01989975944161415, + -0.19261345267295837, + 0.01736867055296898, + -0.07923264801502228, + -0.4404444694519043, + 0.3125889301300049, + 0.10095971822738647, + 0.17173698544502258, + 0.23782190680503845, + -0.07170403748750687, + 0.013639729470014572, + 0.19007621705532074, + 0.1901141107082367, + -0.052342064678668976, + -0.9643150568008423, + -0.12307217717170715, + -0.21010802686214447, + -0.5640560984611511, + 0.010125457309186459, + 0.1314179003238678, + 0.10721258819103241, + -0.24371789395809174, + -0.5925355553627014, + 0.49424877762794495, + -0.03528435528278351, + -0.21386614441871643, + 1.4134130477905273, + -0.2751445770263672, + 0.007012579124420881, + -0.023824317380785942, + 0.004113825503736734, + -0.06332013010978699, + 0.286077082157135, + 0.04896686226129532, + 0.31404414772987366, + 0.15028351545333862, + 0.003490754636004567, + 0.0802399218082428, + -0.230818971991539, + -0.022719932720065117, + 0.26083019375801086, + -0.2885863184928894, + 0.07537354528903961, + 0.12282905727624893, + -0.38638314604759216, + 0.1752759963274002, + -0.07370781153440475, + 0.13994526863098145, + 0.13313405215740204, + 0.2851952016353607, + 0.905279278755188, + 0.34521353244781494, + -0.36453402042388916, + 0.46360254287719727, + -0.002040385501459241, + -0.003476516343653202, + -0.19058215618133545, + 0.27096763253211975, + 0.08722586184740067, + 0.03202880546450615, + -0.06164764240384102, + 0.011678489856421947, + 0.21189850568771362, + -0.40100231766700745, + 0.022941868752241135, + 0.0394427627325058, + 0.0675845518708229, + -0.22503064572811127, + 0.14730903506278992, + 0.24842065572738647, + -0.34360530972480774, + 0.21811245381832123, + -0.05238509923219681, + -0.008763357996940613, + -0.1336073875427246, + 0.15671105682849884, + 0.4475333094596863, + -0.5187726616859436, + 0.005388418212532997, + -0.07889139652252197, + 0.10729073733091354, + 0.22381159663200378, + 0.07434546202421188, + -0.0843898206949234, + 0.13574494421482086, + 0.01853088103234768, + -0.41072791814804077, + 0.40448933839797974, + -0.8231801986694336, + -0.4780847728252411, + -0.11237931996583939, + 0.012673617340624332, + -0.04672158136963844, + -0.23933981359004974, + 0.01667654886841774, + -0.14681674540042877, + 0.077765092253685, + 0.15309257805347443, + 0.03254099190235138, + -0.015896232798695564, + -0.029608771204948425, + -0.288953959941864, + -0.32651081681251526, + 0.06307528167963028, + -0.09873636066913605, + 0.08938323706388474, + 0.27018269896507263, + 0.018129458650946617, + -0.050469521433115005, + -0.17951229214668274, + 0.02319747768342495, + 0.06737810373306274, + 0.2690926194190979, + -0.10778623819351196, + -0.04740763455629349, + 0.30407941341400146, + -0.08746829628944397, + -0.2184152454137802, + 0.14826175570487976, + 0.18092381954193115, + 0.07989493757486343, + -0.1297195851802826, + ], + b1: &[ + -0.5191351175308228, + 0.6662623882293701, + 0.610133707523346, + -1.1585999727249146, + 0.6903770565986633, + 0.4241520166397095, + 0.754120945930481, + -0.7599878907203674, + -0.3445088267326355, + 0.9317805767059326, + -0.2041703462600708, + 0.17219330370426178, + 1.1566059589385986, + -0.41121166944503784, + -0.6977726817131042, + 0.7911778092384338, + 0.6611397862434387, + -0.6938921213150024, + -0.03742314130067825, + -0.16022440791130066, + 0.11257349699735641, + 0.07743008434772491, + -0.6286312937736511, + 0.544836699962616, + -0.15634237229824066, + -0.5572881698608398, + 0.9681645035743713, + -0.7440500855445862, + 0.10288882255554199, + 0.9043763875961304, + 0.14654643833637238, + -0.024421239271759987, + -0.4609592854976654, + 0.917902410030365, + 0.2704138457775116, + 0.6341348886489868, + 0.034945350140333176, + 0.5565919876098633, + 0.1746397614479065, + -0.6341800093650818, + ], + w2: &[ + 0.07229708135128021, + 0.2507615387439728, + 0.16330942511558533, + 0.5204483866691589, + 0.24313874542713165, + -0.5474504232406616, + -0.28332123160362244, + -0.2225571572780609, + -0.1043124571442604, + 0.06595291197299957, + 0.21239061653614044, + -0.14725270867347717, + -0.8134568333625793, + 0.07381946593523026, + -0.24956485629081726, + 0.4919748604297638, + 0.2962062954902649, + 0.3260444402694702, + 0.07504145801067352, + -0.053836897015571594, + 0.2531750500202179, + -0.04855559393763542, + -0.5578967332839966, + -0.5225025415420532, + 0.055111128836870193, + -0.21510563790798187, + 0.5871708989143372, + -0.19132649898529053, + 0.007392226252704859, + -0.298953115940094, + 0.16707110404968262, + -0.04706822335720062, + 0.07302752882242203, + -0.08172990381717682, + 0.23955324292182922, + -0.15824700891971588, + -0.3977665305137634, + 0.5267415642738342, + -0.11258449405431747, + -0.3343915045261383, + 0.23245088756084442, + -0.7491211891174316, + -0.6333310604095459, + 0.0232061930000782, + -0.2315434217453003, + -0.3745144307613373, + -0.03209906071424484, + -0.4041699469089508, + 0.041345734149217606, + 0.19181972742080688, + -0.2760458290576935, + -0.07779327034950256, + 0.24569696187973022, + -0.18802686035633087, + -0.6544056534767151, + 0.556419849395752, + 0.11468080431222916, + -0.32528090476989746, + 0.38538315892219543, + 0.33702555298805237, + -0.442532479763031, + 0.00750756124034524, + -0.45737770199775696, + -0.06860284507274628, + -0.4411284625530243, + -0.23914210498332977, + 0.06834587454795837, + 0.14571186900138855, + 0.6887655258178711, + 0.5702284574508667, + 0.3135473430156708, + -0.3360161781311035, + -0.5353860259056091, + 0.06292688101530075, + 0.735708475112915, + 0.7143703103065491, + -0.3693147897720337, + 0.525284469127655, + 0.39448651671409607, + -0.09941494464874268, + 0.09564384818077087, + 0.5881519913673401, + 0.05619557946920395, + 0.4508857727050781, + -0.2834583520889282, + -0.16902177035808563, + 0.24799591302871704, + -0.182522252202034, + 0.0468696765601635, + 0.14808374643325806, + -0.013205822557210922, + -0.12705814838409424, + 0.0614711195230484, + 0.14103399217128754, + -0.2599405348300934, + 0.028414186090230942, + -0.2865449786186218, + -0.08163938671350479, + 0.13120926916599274, + 0.17990124225616455, + -0.16350798308849335, + -0.09809352457523346, + -0.013590727932751179, + -0.17736633121967316, + 0.05107983574271202, + 0.3411618173122406, + -0.2772451341152191, + 0.32397109270095825, + 0.046551186591386795, + 0.13246433436870575, + 0.05053735896945, + 0.24057962000370026, + -0.04693610221147537, + -0.1650579869747162, + 0.1331019252538681, + 0.09457181394100189, + -0.16547952592372894, + -0.09469929337501526, + 0.30049434304237366, + 0.12664170563220978, + -0.013082812540233135, + 0.390655517578125, + 0.6400918364524841, + -0.0010483618825674057, + -0.03533017635345459, + 0.16345657408237457, + 0.05697643384337425, + 0.1748565286397934, + 0.0036667422391474247, + -0.05557025969028473, + 0.016822226345539093, + -0.12541711330413818, + -0.4695605933666229, + 0.008447905071079731, + 0.16371716558933258, + -0.1481284201145172, + -0.10916673392057419, + 0.1754710078239441, + -0.05557332932949066, + 0.17406205832958221, + 0.03734235838055611, + -0.0014076621737331152, + 0.16409075260162354, + -0.0339696928858757, + 0.11525241285562515, + 0.11995170265436172, + -0.39020177721977234, + 0.01936984248459339, + -0.14390763640403748, + -0.18344464898109436, + -0.08675119280815125, + 0.19569827616214752, + 0.48439380526542664, + -0.232485830783844, + -0.004231136757880449, + 0.15202505886554718, + 0.01103641465306282, + -0.1192987710237503, + -0.17487019300460815, + 0.27336806058883667, + -0.5894135236740112, + -0.03331466019153595, + 0.21942859888076782, + 0.30420297384262085, + 0.2666693329811096, + 0.4481956958770752, + -0.020630693063139915, + 0.8494743704795837, + 0.5691520571708679, + 0.5711295008659363, + 0.00404204148799181, + 0.5070351958274841, + 0.09074786305427551, + 0.15874768793582916, + 0.7676622271537781, + 0.6556511521339417, + 0.1220490038394928, + 0.7263025641441345, + -0.07173441350460052, + 0.14413252472877502, + 0.49090006947517395, + -0.3324028253555298, + 0.45898303389549255, + 0.5931536555290222, + 0.19021296501159668, + -0.7473744750022888, + -0.834629476070404, + -0.1385311633348465, + -0.05174582824110985, + 0.018871335312724113, + -0.42817312479019165, + 0.20682017505168915, + 0.016382897272706032, + -0.6684255599975586, + 0.3525462746620178, + -0.42306870222091675, + -0.0817568302154541, + 0.3572525084018707, + -0.23954586684703827, + -0.4869120717048645, + 0.016070470213890076, + 0.5639761686325073, + 0.17797298729419708, + 0.2919785678386688, + -0.3837592601776123, + 0.13362792134284973, + 0.09925093501806259, + 0.12642522156238556, + 0.09690988808870316, + -0.08732952922582626, + 0.24605968594551086, + -0.3894798457622528, + -0.174991175532341, + 0.2573908269405365, + 0.22514064610004425, + -0.24535547196865082, + -0.2993263006210327, + 0.24350187182426453, + 0.03375721350312233, + 0.16244018077850342, + -0.16753582656383514, + -0.08621060848236084, + 0.1272309273481369, + 0.007472787983715534, + 0.20557984709739685, + 0.1578531116247177, + -0.5838948488235474, + 0.08410368114709854, + -0.2831973135471344, + -0.28126293420791626, + -0.08023717254400253, + 0.5180243849754333, + 0.2208152413368225, + -0.3613019585609436, + -0.06204051896929741, + -0.13526616990566254, + 0.09384715557098389, + -0.27185022830963135, + -0.05938927084207535, + 0.284194678068161, + 0.04228530079126358, + 0.5006632208824158, + 0.6578063368797302, + -0.07014274597167969, + -0.3233219087123871, + -0.01618030108511448, + 0.2888641357421875, + -0.08185673505067825, + -0.17689819633960724, + -0.2994365096092224, + 0.016244128346443176, + 0.02359011210501194, + 0.1367129534482956, + -0.01653127372264862, + -0.09157261997461319, + -0.3516620397567749, + -0.09030301123857498, + -0.07817772775888443, + 0.17603041231632233, + -0.01393663790076971, + -0.029468189924955368, + -0.0814921036362648, + -0.12077502906322479, + -0.10759524255990982, + -0.0750858411192894, + 0.2511105239391327, + -0.20753242075443268, + -0.05136517807841301, + -0.024205535650253296, + -0.3384825587272644, + 0.020664114505052567, + 0.11200296878814697, + 0.08333364874124527, + -0.24177855253219604, + -0.07010341435670853, + 0.020779477432370186, + -0.20839253067970276, + -0.0016562794335186481, + 0.023504814133048058, + 0.3570723235607147, + -0.30022287368774414, + -0.3554439842700958, + -0.027536675333976746, + -1.1282703876495361, + -0.08706718683242798, + 0.0742080882191658, + 0.18080361187458038, + -0.02274167723953724, + -0.704075813293457, + -0.9722687602043152, + 0.1188407614827156, + -0.029379399493336678, + 0.8019110560417175, + -0.34810709953308105, + 0.04902748018503189, + -0.7494327425956726, + 0.5064789056777954, + -0.11681736260652542, + 0.2257058471441269, + -0.4354608356952667, + 0.3252757489681244, + -0.1591869592666626, + -0.5933760404586792, + -0.5259361863136292, + 0.22252318263053894, + 0.30712220072746277, + 0.29186123609542847, + -0.7899709343910217, + 0.3455640971660614, + -0.8577526807785034, + 0.19282177090644836, + 0.29095181822776794, + -0.3287593424320221, + 0.0454283282160759, + -0.5983009338378906, + -0.08342050760984421, + -0.8976981043815613, + 0.10165920853614807, + 0.13396088778972626, + 0.2290259599685669, + 0.02499830722808838, + 0.7539560794830322, + 0.1477266401052475, + 0.3097168207168579, + -0.3993585705757141, + 0.0817292109131813, + 0.038499560207128525, + 0.048502497375011444, + 0.10572300106287003, + -0.17650842666625977, + 0.30300378799438477, + -0.3586488962173462, + -0.09699319303035736, + 0.28980425000190735, + 0.1152607873082161, + -0.30993735790252686, + -0.3226162791252136, + 0.2082981914281845, + 0.08206543326377869, + 0.09643732011318207, + -0.09098457545042038, + -0.09191355854272842, + 0.04240717366337776, + -0.08706614375114441, + 0.3119218051433563, + 0.24132680892944336, + -0.5137639045715332, + 0.03463784605264664, + -0.29585450887680054, + -0.3583862781524658, + -0.09919128566980362, + 0.5263358950614929, + 0.19875890016555786, + -0.4007430374622345, + -0.044145308434963226, + -0.24342355132102966, + 0.16471655666828156, + -0.25901785492897034, + 0.012997856363654137, + 0.3298455476760864, + -0.23130790889263153, + 0.4484388828277588, + 0.35633817315101624, + 0.26454973220825195, + 0.15214529633522034, + -0.12443697452545166, + -0.405061811208725, + 0.17236965894699097, + -0.36522531509399414, + -0.074102483689785, + 0.09564346820116043, + -0.26696014404296875, + -0.7053405046463013, + -0.4750596880912781, + 0.2850874066352844, + -0.42413032054901123, + 0.3273111581802368, + 0.013779409229755402, + -0.7248923182487488, + -0.49210208654403687, + 0.5041399002075195, + -0.14308881759643555, + 0.629442036151886, + -0.8470776677131653, + 0.36798736453056335, + -0.17092065513134003, + 0.5437707304954529, + -0.26034078001976013, + -0.4502609074115753, + 0.2898317873477936, + -0.3266198933124542, + 0.1681036651134491, + 0.6064534783363342, + 0.48974573612213135, + -0.3461318910121918, + -0.36192092299461365, + 0.3675844371318817, + -0.731248676776886, + -0.21227769553661346, + -0.4246974289417267, + 0.17397946119308472, + -0.3643985986709595, + 0.205714613199234, + 0.629838228225708, + 0.10543780773878098, + 0.010421440936625004, + 0.6487590670585632, + -0.685522198677063, + 0.010746597312390804, + 0.371294766664505, + -0.68584144115448, + 0.69797283411026, + -0.39890381693840027, + 0.2957388460636139, + 0.10036955028772354, + -0.31620606780052185, + -0.5876231789588928, + -0.5783882737159729, + -0.4745366871356964, + 0.20689401030540466, + -0.2748165428638458, + 0.34110450744628906, + 0.817054033279419, + 0.8686729073524475, + -0.6139298677444458, + -0.19506172835826874, + -0.03448706120252609, + 0.635860025882721, + -0.38243091106414795, + 0.8843176960945129, + 0.08922040462493896, + -0.8030375242233276, + 0.01003911904990673, + 0.49227485060691833, + 0.02043282799422741, + -0.1812848448753357, + 0.8425045609474182, + -0.18937410414218903, + 0.2360723465681076, + -0.0486280657351017, + 0.1306903064250946, + 0.44811540842056274, + -0.09772484004497528, + 0.3676001727581024, + -0.10864408314228058, + 0.10239739716053009, + 0.26535993814468384, + -0.19465096294879913, + -0.05268852412700653, + 0.013907784596085548, + 0.11859709769487381, + -0.008244873955845833, + -0.12678827345371246, + 0.16795198619365692, + 0.09826375544071198, + -0.13783332705497742, + -0.32474759221076965, + -0.018496913835406303, + -0.12179988622665405, + 0.22411927580833435, + -0.10514824092388153, + 0.038778163492679596, + 0.33486974239349365, + 0.31644245982170105, + 0.05365574359893799, + 0.24912847578525543, + -0.31889432668685913, + 0.24240325391292572, + -0.19231560826301575, + 0.18558776378631592, + -0.022984078153967857, + 0.11608095467090607, + 0.15418484807014465, + -0.14139854907989502, + 0.01758008636534214, + -0.12027571350336075, + 0.2522386610507965, + -0.2922046184539795, + 0.049236513674259186, + 0.19894357025623322, + 0.39957553148269653, + 0.3346879780292511, + 0.3187335133552551, + 0.4501717686653137, + -0.8946970701217651, + 0.18189306557178497, + -0.08766483515501022, + 0.2782788574695587, + 0.3587392270565033, + -0.33824455738067627, + 0.6033147573471069, + -0.6243746876716614, + -0.6177958250045776, + 0.6629742383956909, + 0.4856598377227783, + -0.3099081814289093, + -0.678487241268158, + 0.47894829511642456, + -0.03139176964759827, + 0.16848357021808624, + -0.5739434957504272, + -0.16708984971046448, + 0.11146949231624603, + 0.090438611805439, + 0.4812713861465454, + 0.5129365921020508, + -0.7324693202972412, + 0.26365718245506287, + -0.4824923276901245, + -0.5487518310546875, + -0.20128659904003143, + 0.5759150385856628, + 0.3504473567008972, + -0.36605504155158997, + -0.4257725477218628, + -0.25298258662223816, + 0.512897789478302, + -0.4181336462497711, + -0.516604483127594, + 0.37244912981987, + ], + b2: &[ + 0.14859354496002197, + -0.018167857080698013, + -0.3407953083515167, + -0.14991576969623566, + 0.4018653333187103, + -0.2384500652551651, + -0.4047893285751343, + 0.15702210366725922, + -0.3152092695236206, + 0.29297566413879395, + 0.26403820514678955, + -0.2573520541191101, + -0.11290331929922104, + ], + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/cortex_a72.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/cortex_a72.rs new file mode 100644 index 0000000..6819f4d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/cortex_a72.rs @@ -0,0 +1,4 @@ +use crate::frame::mmm::cost_model::CostModel; +pub fn models() -> Vec<(&'static str, CostModel<'static>)> { + vec![] +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/cortex_a73.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/cortex_a73.rs new file mode 100644 index 0000000..6819f4d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/cortex_a73.rs @@ -0,0 +1,4 @@ +use crate::frame::mmm::cost_model::CostModel; +pub fn models() -> Vec<(&'static str, CostModel<'static>)> { + vec![] +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/sme.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/sme.rs new file mode 100644 index 0000000..51b8c2b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/sme.rs @@ -0,0 +1,309 @@ +use crate::Ops; +use crate::frame::mmm::ImplementationQuality::ManuallyOptimized; +use crate::mmm::*; + +// CAN_FUSE: everything except LeakyRelu / QScale / RoundingShiftRight / +// ShiftLeft. LoadTile, AddUnicast, AddRowColProducts, per-row/col/scalar +// arithmetic, Clear, Store, AddMatMul are all in. (Matches AMX +// `apple_amx.rs` CAN_FUSE, minus the i32-only quantization ops.) +const CAN_FUSE: fn(&FusedSpec) -> bool = |f| { + !matches!( + f, + FusedSpec::LeakyRelu(_) + | FusedSpec::QScale(_, _, _) + | FusedSpec::RoundingShiftRight(_, _) + | FusedSpec::ShiftLeft(_) + ) +}; + +const SME: fn() -> bool = has_sme; +const SME2: fn() -> bool = has_sme2; +// The SMOPA i32 kernel implements the quant fuse ops (QScale / RoundingShiftRight +// / ShiftLeft) bit-exactly; only LeakyRelu is unsupported (kernel returns 1). +const CAN_FUSE_I32: fn(&FusedSpec) -> bool = |f| !matches!(f, FusedSpec::LeakyRelu(_)); + +MMMExternKernel!(sme_qmmm_i32_32x32(32,32)@(128,128) where(SME2) can_fuse(CAN_FUSE_I32) + packing[1] = i8i8 => |k| k.with_packing(crate::pack::PackedI8K4::new(32), crate::pack::PackedI8K4::new(32)); + quality(ManuallyOptimized) store(i8)); + +// Streaming vector length in bytes, read via `RDSVL x0, #1` (encoding +// 0x04bf5820). RDSVL is legal in non-streaming mode, but is UNDEFINED +// unless FEAT_SME is implemented — callers MUST confirm FEAT_SME first +// (sysctl on macOS, HWCAP2 on Linux) or this SIGILLs. +#[cfg(any(target_os = "macos", target_os = "linux"))] +unsafe fn streaming_vector_bytes() -> u64 { + let svl: u64; + unsafe { + std::arch::asm!( + ".inst 0x04bf5820", // rdsvl x0, #1 + out("x0") svl, + options(nomem, nostack, preserves_flags), + ); + } + svl +} + +// Our SME kernels hardcode a 512-bit streaming vector length (16 f32 lanes +// per ZA.S slice — the 32x32 and 64x1 tile geometries depend on it). A host +// that advertises FEAT_SME with a different SVL would run the kernels with +// mismatched geometry and produce silently-wrong results. The prime offender +// is qemu-aarch64 user-mode emulation, which sets HWCAP2_SME / HWCAP2_SME2 +// but uses a non-512 SVL — that is exactly what makes the cross-compiled +// aarch64 CI jobs (run under QEMU) fail. Reject any non-512 SVL here so we +// fall back to the portable path. MUST only be called once FEAT_SME is known +// present. +#[cfg(any(target_os = "macos", target_os = "linux"))] +fn sme_geometry_supported() -> bool { + // SVL = 512 bits = 64 bytes. + unsafe { streaming_vector_bytes() == 64 } +} + +MMMExternKernel!( + sme_mmm_f32_32x32(32, 32)@(128, 128) + where(SME) + can_fuse(CAN_FUSE) + quality(ManuallyOptimized) +); + +MMMExternKernel!( + sme_mmv_f32_64x1(64, 1)@(128, 128) + where(SME2) + can_fuse(CAN_FUSE) + quality(ManuallyOptimized) +); + +#[cfg(target_os = "macos")] +pub fn has_sme() -> bool { + // TRACT_SME_DISABLE=1 forces the SME path off so callers can A/B + // against the AMX path on the same binary. + if std::env::var_os("TRACT_SME_DISABLE").is_some() { + return false; + } + // hw.optional.arm.FEAT_SME is an INTEGER sysctl, not a string. The + // generic apple_get_syscall reads bytes-as-C-string which fails here + // (`\x01\x00\x00\x00` would compare against the ASCII "1"), so we + // read it as a u64 directly. + use std::ffi::{CString, c_char, c_int, c_void}; + use std::ptr::null_mut; + unsafe extern "C" { + fn sysctlbyname( + name: *const c_char, + oldp: *mut c_void, + oldlenp: *mut usize, + newp: *mut c_void, + newlen: usize, + ) -> c_int; + } + let Ok(name) = CString::new("hw.optional.arm.FEAT_SME") else { + return false; + }; + let mut value: u64 = 0; + let mut len: usize = std::mem::size_of::(); + unsafe { + if sysctlbyname( + name.as_ptr(), + &mut value as *mut _ as *mut c_void, + &mut len, + null_mut(), + 0, + ) != 0 + { + return false; + } + } + // FEAT_SME present AND the streaming vector length matches our kernels' + // hardcoded 512-bit geometry. + value != 0 && sme_geometry_supported() +} + +#[cfg(target_os = "linux")] +pub fn has_sme() -> bool { + // HWCAP2_SME = 1 << 23 on aarch64 (kernel ABI). + const HWCAP2_SME: u64 = 1 << 23; + unsafe extern "C" { + fn getauxval(t: u64) -> u64; + } + const AT_HWCAP2: u64 = 26; + let feat = unsafe { (getauxval(AT_HWCAP2) & HWCAP2_SME) != 0 }; + // FEAT_SME present AND the streaming vector length matches our kernels' + // hardcoded 512-bit geometry (rejects qemu-user, which advertises SME + // with a non-512 SVL — the cause of the cross-compiled CI failures). + feat && sme_geometry_supported() +} + +#[cfg(not(any(target_os = "macos", target_os = "linux")))] +pub fn has_sme() -> bool { + false +} + +#[cfg(target_os = "macos")] +pub fn has_sme2() -> bool { + // TRACT_SME_DISABLE=1 disables both SME and SME2 dispatch on the same + // binary so end users can A/B the entire SME backend. + if std::env::var_os("TRACT_SME_DISABLE").is_some() { + return false; + } + use std::ffi::{CString, c_char, c_int, c_void}; + use std::ptr::null_mut; + unsafe extern "C" { + fn sysctlbyname( + name: *const c_char, + oldp: *mut c_void, + oldlenp: *mut usize, + newp: *mut c_void, + newlen: usize, + ) -> c_int; + } + let Ok(name) = CString::new("hw.optional.arm.FEAT_SME2") else { + return false; + }; + let mut value: u64 = 0; + let mut len: usize = std::mem::size_of::(); + unsafe { + if sysctlbyname( + name.as_ptr(), + &mut value as *mut _ as *mut c_void, + &mut len, + null_mut(), + 0, + ) != 0 + { + return false; + } + } + // FEAT_SME2 present AND the streaming vector length matches our kernels' + // hardcoded 512-bit geometry. + value != 0 && sme_geometry_supported() +} + +#[cfg(target_os = "linux")] +pub fn has_sme2() -> bool { + // HWCAP2_SME2 = 1 << 37 on aarch64 (kernel ABI). + const HWCAP2_SME2: u64 = 1 << 37; + unsafe extern "C" { + fn getauxval(t: u64) -> u64; + } + const AT_HWCAP2: u64 = 26; + let feat = unsafe { (getauxval(AT_HWCAP2) & HWCAP2_SME2) != 0 }; + // FEAT_SME2 present AND the streaming vector length matches our kernels' + // hardcoded 512-bit geometry (rejects qemu-user, which advertises SME2 + // with a non-512 SVL — the cause of the cross-compiled CI failures). + feat && sme_geometry_supported() +} + +#[cfg(not(any(target_os = "macos", target_os = "linux")))] +pub fn has_sme2() -> bool { + false +} + +pub fn plug(ops: &mut Ops) { + if has_sme() { + log::info!("SME optimisation activated"); + ops.mmm_f32 = Box::new(|_, _, _| sme_mmm_f32_32x32.mmm()); + ops.mmm_impls.extend_from_slice(&[sme_mmm_f32_32x32.mmm()]); + } + if has_sme2() { + log::info!("SME2 GEMV optimisation activated"); + ops.mmv_f32 = Box::new(|_, _| sme_mmv_f32_64x1.mmm()); + ops.qmmm_i32 = Box::new(|_, _, _| sme_qmmm_i32_32x32.mmm()); + ops.mmm_impls + .extend_from_slice(&[sme_mmv_f32_64x1.mmm(), sme_qmmm_i32_32x32.mmm()]); + } + if !has_sme() && !has_sme2() { + log::info!("No SME optimisation"); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::frame::mmm::tests::packed_packed::PackedPackedProblem; + use tract_data::internal::Approximation; + + // Phase 1A correctness: AddMatMul + Clear + Store + Done on a few + // shapes. Bypasses auto-tests (SME_OFF) by calling run/reference + // directly. Skipped if hardware lacks SME. + fn check_shape(m_tile: usize, k: usize, n_tile: usize) { + const MR: usize = 32; + const NR: usize = 32; + let m = m_tile * MR; + let n = n_tile * NR; + let a: Vec = (0..m * k).map(|i| (i as f32 * 0.013) - 1.5).collect(); + let b: Vec = (0..k * n).map(|i| (i as f32 * 0.017) + 0.25).collect(); + let pb = PackedPackedProblem::kernel(&*sme_mmm_f32_32x32, 0, a, b); + let expected = pb.reference().expect("scalar reference"); + let found = pb.run().expect("SME kernel run"); + found + .close_enough(&expected, Approximation::Approximate) + .unwrap_or_else(|e| panic!("SME mmm mismatch at k={k}: {e}")); + } + + #[test] + fn sme_mmm_f32_32x32_k1() { + if !has_sme() { + eprintln!("SME not present, skipping"); + return; + } + check_shape(1, 1, 1); + } + + #[test] + fn sme_mmm_f32_32x32_k8() { + if !has_sme() { + return; + } + check_shape(1, 8, 1); + } + + #[test] + fn sme_mmm_f32_32x32_k128() { + if !has_sme() { + return; + } + check_shape(1, 128, 1); + } + + #[test] + fn sme_mmm_f32_32x32_multi_tile() { + if !has_sme() { + return; + } + // 64x64 output (2x2 tiles), K=64 — exercises the framework + // iterating across multiple kernel calls. + check_shape(2, 64, 2); + } + + // Strided store path: hand-built Clear + Store chain with non-contig C. + #[test] + fn sme_store_non_contiguous() { + if !has_sme() { + return; + } + use crate::frame::mmm::{FusedKerSpec, OutputStoreKer}; + const MR: usize = 32; + const NR: usize = 32; + let mut v: Vec = vec![f32::MAX; MR * 5 * NR * 3]; + let c = OutputStoreKer { + ptr: v.as_mut_ptr() as _, + row_byte_stride: (4 * 3 * NR * 5) as isize, + col_byte_stride: 4 * 3, + item_size: 4, + }; + let non_linear = [ + FusedKerSpec::::Clear, + FusedKerSpec::Store(c), + FusedKerSpec::Done, + ]; + let err = unsafe { (sme_mmm_f32_32x32.kernel)(&non_linear) }; + assert_eq!(err, 0, "kernel returned non-zero error code"); + let mut expected = vec![f32::MAX; v.len()]; + for col in 0..NR { + for row in 0..MR { + expected[col * 3 + row * 3 * 5 * NR] = 0.0; + } + } + for (i, (got, exp)) in v.iter().zip(expected.iter()).enumerate() { + assert_eq!(got, exp, "mismatch at idx {i}: got {got} expected {exp}"); + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/sve.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/sve.rs new file mode 100644 index 0000000..6424f1c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/arm64/sve.rs @@ -0,0 +1,230 @@ +use crate::Ops; + +// `tract_sve` is set by build.rs only on aarch64-linux when the C compiler +// supports SVE intrinsics. The kernel registration + extern live behind it so +// non-SVE builds never reference the (absent) C symbol. +#[cfg(tract_sve)] +use crate::frame::mmm::ImplementationQuality::ManuallyOptimized; +#[cfg(tract_sve)] +use crate::mmm::*; +#[cfg(tract_sve)] +use crate::pack::PackedFormat; +// Explicit import so `f16` is tract's half::f16 (LADatum), not rustc's builtin +// primitive f16 — a glob import would not shadow the primitive. +#[cfg(tract_sve)] +use tract_data::prelude::f16; + +// f32 SVE kernel can't do LeakyRelu or the i32 quantization ops (matches the +// arm64simd / SME f32 CAN_FUSE). +#[cfg(tract_sve)] +const CAN_FUSE: fn(&FusedSpec) -> bool = |f| { + !matches!( + f, + FusedSpec::LeakyRelu(_) + | FusedSpec::QScale(_, _, _) + | FusedSpec::RoundingShiftRight(_, _) + | FusedSpec::ShiftLeft(_) + ) +}; + +// The i32 quantized kernel keeps the quantization fuse ops (QScale / +// RoundingShiftRight / ShiftLeft) — they are the whole point of a quantized +// kernel — and excludes only LeakyRelu (matches arm64simd's i32 surface; i32 +// LeakyRelu has no practical use and the C kernel does not implement it). +#[cfg(tract_sve)] +const CAN_FUSE_I32: fn(&FusedSpec) -> bool = |f| !matches!(f, FusedSpec::LeakyRelu(_)); + +#[cfg(tract_sve)] +const SVE2: fn() -> bool = has_sve2; + +// The f16 kernels need FEAT_SVE2 AND FEAT_FP16 (native f16 arithmetic). +#[cfg(tract_sve)] +const SVE2_FP16: fn() -> bool = || has_sve2() && crate::arm64::has_fp16(); + +// The VLA SVE f32 GEMM kernel, implemented in C (arm64/sve/sve_mmm_f32.c) since +// Rust has no stable SVE intrinsics. Broadcast-A rank-1 update, N-tile walked in +// svcntw() chunks → correct and full-width at any VL. +#[cfg(tract_sve)] +mod sve_sys { + use crate::frame::mmm::FusedKerSpec; + use tract_data::prelude::f16; + unsafe extern "C" { + pub fn sve_mmm_f32_kernel(ops: *const FusedKerSpec) -> isize; + pub fn sve_mmv_f32_64x1_kernel(ops: *const FusedKerSpec) -> isize; + pub fn sve_mmm_i32_kernel(ops: *const FusedKerSpec) -> isize; + pub fn sve_mmm_i32_64x1_kernel(ops: *const FusedKerSpec) -> isize; + pub fn sve_mmm_f16_kernel(ops: *const FusedKerSpec) -> isize; + pub fn sve_mmv_f16_64x1_kernel(ops: *const FusedKerSpec) -> isize; + } +} + +#[cfg(tract_sve)] +MMMRustKernel!(sve_sys::sve_mmm_f32_kernel => sve_mmm_f32_8x8(8, 8) + where(SVE2) + can_fuse(CAN_FUSE) + quality(ManuallyOptimized) +); + +// The VLA SVE f32 GEMV kernel (arm64/sve/sve_mmv_f32_64x1.c), MR=64 NR=1, +// dispatched when N == 1 (matrix x f32 column vector). Wired to ops.mmv_f32. +#[cfg(tract_sve)] +MMMRustKernel!(sve_sys::sve_mmv_f32_64x1_kernel => sve_mmv_f32_64x1(64, 1) + where(SVE2) + can_fuse(CAN_FUSE) + quality(ManuallyOptimized) +); + +// The VLA SVE int8 -> int32 GEMM kernel (arm64/sve/sve_mmm_i32.c). Consumes +// tract's native i8i8 K-major packing via the widening rank-1 update (svld1sb + +// svmla), and supports the i32 quantization fuse ops. Wired to ops.qmmm_i32. +#[cfg(tract_sve)] +MMMRustKernel!(sve_sys::sve_mmm_i32_kernel => sve_mmm_i32_8x8(8, 8) + where(SVE2) + can_fuse(CAN_FUSE_I32) + packing[1] = i8i8 => |k| k.with_packing( + PackedFormat::new(DatumType::I8, 8, 16), + PackedFormat::new(DatumType::I8, 8, 16), + ); + quality(ManuallyOptimized) + store(i8) +); + +// The VLA SVE int8 -> int32 GEMV kernel (arm64/sve/sve_mmm_i32_64x1.c), MR=64 +// NR=1, dispatched when N == 1. Same widening update vectorized over M. Wired to +// ops.qmmv_i32. +#[cfg(tract_sve)] +MMMRustKernel!(sve_sys::sve_mmm_i32_64x1_kernel => sve_mmm_i32_64x1(64, 1) + where(SVE2) + can_fuse(CAN_FUSE_I32) + packing[1] = i8i8 => |k| k.with_packing( + PackedFormat::new(DatumType::I8, 64, 16), + PackedFormat::new(DatumType::I8, 1, 1), + ); + quality(ManuallyOptimized) + store(i8) +); + +// The VLA SVE f16 GEMM kernel (arm64/sve/sve_mmm_f16.c), native f16 FMA, gated on +// SVE2 + FP16. Wired to ops.mmm_f16 when has_fp16(). +#[cfg(tract_sve)] +MMMRustKernel!(sve_sys::sve_mmm_f16_kernel => sve_mmm_f16_8x8(8, 8) + where(SVE2_FP16) + can_fuse(CAN_FUSE) + quality(ManuallyOptimized) +); + +// The VLA SVE f16 GEMV kernel (arm64/sve/sve_mmv_f16_64x1.c), MR=64 NR=1, +// dispatched when N == 1. Wired to ops.mmv_f16 when has_fp16(). +#[cfg(tract_sve)] +MMMRustKernel!(sve_sys::sve_mmv_f16_64x1_kernel => sve_mmv_f16_64x1(64, 1) + where(SVE2_FP16) + can_fuse(CAN_FUSE) + quality(ManuallyOptimized) +); + +// SVE / SVE2 backend. +// +// Unlike SME (Apple M4) and AMX (Apple), SVE/SVE2 is NOT present on any Apple +// silicon — it lives on ARMv9 server/mobile cores (Neoverse V1+/N2+, Cortex-X2+ +// / A510+, Graviton 3/4). So detection is Linux-only in practice; macOS always +// returns false. +// +// The kernels are vector-length-agnostic (VLA): they read the vector width at +// runtime via `whilelt` predication and `svcntw()`, so a single kernel is +// correct at every VL (128..2048-bit). That means — unlike the SME kernels, +// which hardcoded SVL=512 and needed an RDSVL gate — the SVE kernels need NO +// vector-length gate for correctness. `rdvl_bytes()` is provided only for +// optional VL-matched dispatch (selecting a wider-tiled variant when the +// hardware VL is large), not for correctness. + +#[cfg(target_os = "linux")] +pub fn has_sve() -> bool { + if std::env::var_os("TRACT_SVE_DISABLE").is_some() { + return false; + } + // HWCAP_SVE = 1 << 22 on aarch64 (kernel ABI). + const HWCAP_SVE: u64 = 1 << 22; + unsafe extern "C" { + fn getauxval(t: u64) -> u64; + } + const AT_HWCAP: u64 = 16; + unsafe { (getauxval(AT_HWCAP) & HWCAP_SVE) != 0 } +} + +#[cfg(not(target_os = "linux"))] +pub fn has_sve() -> bool { + // No Apple silicon implements SVE; no SVE on non-Linux targets we support. + false +} + +#[cfg(target_os = "linux")] +pub fn has_sve2() -> bool { + if std::env::var_os("TRACT_SVE_DISABLE").is_some() { + return false; + } + // HWCAP2_SVE2 = 1 << 1 on aarch64 (kernel ABI). + const HWCAP2_SVE2: u64 = 1 << 1; + unsafe extern "C" { + fn getauxval(t: u64) -> u64; + } + const AT_HWCAP2: u64 = 26; + unsafe { (getauxval(AT_HWCAP2) & HWCAP2_SVE2) != 0 } +} + +#[cfg(not(target_os = "linux"))] +pub fn has_sve2() -> bool { + false +} + +/// SVE vector length in bytes, via `RDVL x0, #1` (encoding 0x04bf5020). +/// Legal whenever FEAT_SVE is implemented; callers MUST confirm `has_sve()` +/// first (RDVL is UNDEFINED without SVE and would SIGILL). Used only for +/// optional VL-matched kernel selection — VLA kernels do not need it. +#[cfg(target_os = "linux")] +#[allow(dead_code)] +pub fn rdvl_bytes() -> u64 { + let vl: u64; + unsafe { + std::arch::asm!( + ".inst 0x04bf5020", // rdvl x0, #1 + out("x0") vl, + options(nomem, nostack, preserves_flags), + ); + } + vl +} + +pub fn plug(ops: &mut Ops) { + let _ = ops; + if has_sve2() { + #[cfg(target_os = "linux")] + log::info!("SVE2 optimisation available (VL = {} bytes)", rdvl_bytes()); + #[cfg(tract_sve)] + { + // Force the SVE kernels for f32 mmm and i32 quantized mmm (mirrors the + // SME backend) and also register them as candidates. TRACT_SVE_DISABLE=1 + // already turns the whole thing off via has_sve2(). + ops.mmm_f32 = Box::new(|_, _, _| sve_mmm_f32_8x8.mmm()); + ops.mmv_f32 = Box::new(|_, _| sve_mmv_f32_64x1.mmm()); + ops.qmmm_i32 = Box::new(|_, _, _| sve_mmm_i32_8x8.mmm()); + ops.qmmv_i32 = Box::new(|_, _| sve_mmm_i32_64x1.mmm()); + ops.mmm_impls.extend_from_slice(&[ + sve_mmm_f32_8x8.mmm(), + sve_mmv_f32_64x1.mmm(), + sve_mmm_i32_8x8.mmm(), + sve_mmm_i32_64x1.mmm(), + ]); + // f16 kernels additionally require FEAT_FP16. + if crate::arm64::has_fp16() { + ops.mmm_f16 = Box::new(|_, _, _| sve_mmm_f16_8x8.mmm()); + ops.mmv_f16 = Box::new(|_, _| sve_mmv_f16_64x1.mmm()); + ops.mmm_impls + .extend_from_slice(&[sve_mmm_f16_8x8.mmm(), sve_mmv_f16_64x1.mmm()]); + } + } + } else if has_sve() { + log::info!("SVE (v1) present; SVE2 kernels not enabled"); + } else { + log::info!("No SVE optimisation"); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/block_quant/helpers.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/block_quant/helpers.rs new file mode 100644 index 0000000..152d37f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/block_quant/helpers.rs @@ -0,0 +1,81 @@ +use byteorder::{LE, ReadBytesExt, WriteBytesExt}; +use std::io::{Cursor, Read, Write}; +use tract_data::internal::*; + +pub struct NibbleReader { + second_half: Option, + reader: R, +} + +impl<'s> NibbleReader> { + pub fn for_slice(slice: &'s [u8]) -> Self { + NibbleReader::new(Cursor::new(slice)) + } +} + +impl NibbleReader { + pub fn new(reader: R) -> NibbleReader { + NibbleReader { + reader, + second_half: None, + } + } + + pub fn read_f16(&mut self) -> f16 { + assert!(self.second_half.is_none()); + f16::from_bits(self.reader.read_u16::().unwrap()) + } + + pub fn read_i4(&mut self) -> i8 { + if let Some(second) = self.second_half.take() { + second + } else { + let byte = self.reader.read_u8().unwrap(); + self.second_half = Some((byte >> 4) as i8); + (byte & 0x0F) as i8 + } + } + + pub fn read_i8(&mut self) -> i8 { + self.reader.read_i8().unwrap() + } +} + +pub struct NibbleWriter { + first_half: Option, + writer: W, +} + +impl<'s> NibbleWriter> { + pub fn for_slice(slice: &'s mut [u8]) -> Self { + NibbleWriter::new(Cursor::new(slice)) + } +} + +impl NibbleWriter { + pub fn new(writer: W) -> NibbleWriter { + NibbleWriter { + writer, + first_half: None, + } + } + + pub fn write_f16(&mut self, f: f16) { + assert!(self.first_half.is_none()); + self.writer.write_u16::(f.to_bits()).unwrap() + } + + pub fn write_i4(&mut self, q: i8) { + if let Some(first) = self.first_half.take() { + self.writer + .write_u8(first as u8 | ((q as u8) << 4)) + .unwrap() + } else { + self.first_half = Some(q); + } + } + + pub fn write_i8(&mut self, q: i8) { + self.writer.write_i8(q).unwrap() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/block_quant/mod.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/block_quant/mod.rs new file mode 100644 index 0000000..c871a39 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/block_quant/mod.rs @@ -0,0 +1,357 @@ +use downcast_rs::{Downcast, impl_downcast}; +use dyn_clone::{DynClone, clone_box}; +use dyn_eq::DynEq; +use dyn_hash::DynHash; +use num_traits::Zero; +use tract_data::internal::*; +use tract_data::itertools::Itertools; + +use std::alloc::Layout; +use std::borrow::Cow; +use std::fmt::{Debug, Display}; +use std::hash::Hash; +use std::sync::Arc; + +mod helpers; +mod q4_0; +mod q8_1; +mod storage; +mod value; + +pub use helpers::{NibbleReader, NibbleWriter}; +pub use q4_0::Q4_0; +pub use q8_1::Q8_1; +pub use storage::{BlockQuantStorage, block_quant_slice}; +pub use value::{BlockQuantFact, PackedBlockQuantFact}; + +use crate::mmm::{EagerPackedInput, MMMInputFormat}; +use crate::pack::PackedFormat; + +use crate::WeightType; + +use super::mmm::MMMInputValue; + +pub trait BlockQuant: + Debug + Display + Send + Sync + DynClone + DynHash + dyn_eq::DynEq + Downcast +{ + fn block_len(&self) -> usize; + + fn block_bytes(&self) -> usize; + + fn dequant_block_f32(&self, quant: &[u8], block: &mut [f32]); + fn dequant_block_f16(&self, quant: &[u8], block: &mut [f16]); + fn quant_block_f16(&self, block: &[f16], quant: &mut [u8]); + fn quant_block_f32(&self, block: &[f32], quant: &mut [u8]); + + fn quant_f16(&self, input: &[f16]) -> TractResult { + unsafe { + let blocks = input.len() / self.block_len(); + let mut quant = Blob::for_layout( + Layout::from_size_align(blocks * self.block_bytes(), 128).unwrap(), + ); + for b in 0..blocks { + let block = &input[b * self.block_len()..][..self.block_len()]; + let qblock = &mut quant[b * self.block_bytes()..][..self.block_bytes()]; + self.quant_block_f16(block, qblock); + } + Ok(quant) + } + } + + fn quant_f32(&self, input: &[f32]) -> TractResult { + unsafe { + let blocks = input.len() / self.block_len(); + let mut quant = Blob::for_layout( + Layout::from_size_align(blocks * self.block_bytes(), 128).unwrap(), + ); + for b in 0..blocks { + let block = &input[b * self.block_len()..][..self.block_len()]; + let qblock = &mut quant[b * self.block_bytes()..][..self.block_bytes()]; + self.quant_block_f32(block, qblock); + } + Ok(quant) + } + } + + fn dequant_f32(&self, input: &[u8]) -> TractResult { + unsafe { + let blocks = input.len() / self.block_bytes(); + let mut tensor = Tensor::uninitialized::(&[blocks * self.block_len()])?; + let mut tensor_plain = tensor.try_as_plain_mut()?; + let slice = tensor_plain.as_slice_mut::()?; + for b in 0..blocks { + let block = &mut slice[b * self.block_len()..][..self.block_len()]; + let qblock = &input[b * self.block_bytes()..][..self.block_bytes()]; + self.dequant_block_f32(qblock, block); + } + Ok(tensor) + } + } + + fn dequant_f16(&self, input: &[u8]) -> TractResult { + unsafe { + let blocks = input.len() / self.block_bytes(); + let mut tensor = Tensor::uninitialized::(&[blocks * self.block_len()])?; + let mut tensor_plain = tensor.try_as_plain_mut()?; + let slice = tensor_plain.as_slice_mut::()?; + for b in 0..blocks { + let block = &mut slice[b * self.block_len()..][..self.block_len()]; + let qblock = &input[b * self.block_bytes()..][..self.block_bytes()]; + self.dequant_block_f16(qblock, block); + } + Ok(tensor) + } + } + + fn extract_at_offset_f16(&self, input: &[u8], offset: usize) -> f16 { + let len = self.block_len(); + let block_id = offset / len; + let mut block = vec![f16::zero(); self.block_len()]; + self.dequant_block_f16( + &input[block_id * self.block_bytes()..][..self.block_bytes()], + &mut block, + ); + block[offset % len] + } + + fn extract_at_offset_f32(&self, input: &[u8], offset: usize) -> f32 { + let len = self.block_len(); + let block_id = offset / len; + let mut block = vec![f32::zero(); self.block_len()]; + self.dequant_block_f32( + &input[block_id * self.block_bytes()..][..self.block_bytes()], + &mut block, + ); + block[offset % len] + } + + fn simulate_precision_loss( + &self, + mut tensor: Tensor, + block_axis: usize, + ) -> TractResult { + ensure!(block_axis == tensor.rank() - 1); + ensure!(tensor.shape()[block_axis] % self.block_len() == 0); + let mut scratch = vec![0u8; self.block_bytes()]; + if tensor.datum_type() == f32::datum_type() { + let mut tensor_plain = tensor.try_as_plain_mut()?; + for block in tensor_plain + .as_slice_mut::()? + .chunks_mut(self.block_len()) + { + self.quant_block_f32(block, &mut scratch); + self.dequant_block_f32(&scratch, block); + } + drop(tensor_plain); + Ok(tensor) + } else if tensor.datum_type() == f16::datum_type() { + let mut tensor_plain = tensor.try_as_plain_mut()?; + for block in tensor_plain + .as_slice_mut::()? + .chunks_mut(self.block_len()) + { + self.quant_block_f16(block, &mut scratch); + self.dequant_block_f16(&scratch, block); + } + drop(tensor_plain); + Ok(tensor) + } else { + todo!() + } + } + + fn pack( + &self, + input: &[u8], + k: usize, + r: usize, + zip: usize, + scales_at_end: bool, + ) -> TractResult; + + unsafe fn extract_packed_panel( + &self, + value: &EagerPackedInput, + target: &PackedFormat, + panel: usize, + scratch: *mut u8, + ) -> TractResult<()>; + + fn extract_at_mn_f16( + &self, + value: &EagerPackedInput, + mn: usize, + target: &mut [f16], + ) -> TractResult<()>; + + fn extract_at_mn_f32( + &self, + value: &EagerPackedInput, + mn: usize, + target: &mut [f32], + ) -> TractResult<()>; +} + +dyn_clone::clone_trait_object!(BlockQuant); +dyn_hash::hash_trait_object!(BlockQuant); +dyn_eq::eq_trait_object!(BlockQuant); +impl_downcast!(BlockQuant); + +#[allow(clippy::derived_hash_with_manual_eq)] +#[derive(Clone, Hash)] +pub struct PackedBlockQuantFormat { + pub bq: Box, + pub r: usize, + pub zip: usize, + pub scales_at_end: bool, +} + +impl PartialEq for PackedBlockQuantFormat { + fn eq(&self, other: &Self) -> bool { + *self.bq == *other.bq + && self.r == other.r + && self.zip == other.zip + && self.scales_at_end == other.scales_at_end + } +} + +impl Eq for PackedBlockQuantFormat {} + +impl Display for PackedBlockQuantFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Packed{}[{}]", &*self.bq, self.r)?; + if self.zip != 0 { + write!(f, "Z{}", self.zip)?; + } + if self.scales_at_end { + write!(f, "Se")?; + } + Ok(()) + } +} + +impl Debug for PackedBlockQuantFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + ::fmt(self, f) + } +} + +impl PackedBlockQuantFormat { + pub fn new(bq: &dyn BlockQuant, r: usize, zip: usize, scales_at_end: bool) -> Self { + PackedBlockQuantFormat { + bq: clone_box(bq), + r, + zip, + scales_at_end, + } + } + + pub fn simulate_precision_loss( + &self, + tensor: Tensor, + block_axis: usize, + ) -> TractResult { + self.bq.simulate_precision_loss(tensor, block_axis) + } + + pub fn pack(&self, input: &[u8], k: usize) -> TractResult { + self.bq.pack(input, k, self.r, self.zip, self.scales_at_end) + } +} + +impl MMMInputFormat for PackedBlockQuantFormat { + fn prepare_tensor(&self, t: &Tensor, _k_axis: usize, _mn_axis: usize) -> TractResult { + let bqs = t.try_storage_as::()?; + let num_groups: usize = if t.rank() > 2 { + t.shape()[..t.rank() - 2].iter().product() + } else { + 1 + }; + let m_per_group = t.shape()[t.rank().saturating_sub(2)]; + let k = *t.shape().last().unwrap(); + let values = (0..num_groups) + .map(|g| { + let slice = block_quant_slice(bqs.value(), &*self.bq, m_per_group, k, g); + let packed = self.pack(slice, k)?; + Ok(Box::new(packed) as Box) + }) + .collect::>>()?; + let leading_shape = &t.shape()[..t.rank().saturating_sub(2)]; + Ok( + crate::mmm::PackedMatrixStorage::new_batched(leading_shape, values) + .into_tensor(t.datum_type()), + ) + } + + fn prepare_one( + &self, + t: &Tensor, + k_axis: usize, + mn_axis: usize, + ) -> TractResult> { + // this code path is essentially there for test scenarios + let t = if t.is_plain() && t.datum_type().is_number() { + let k = t.shape()[k_axis]; + let m = t.shape()[mn_axis]; + assert!(k % self.bq.block_len() == 0); + let t: Cow = if k_axis == 1 && mn_axis == 0 { + Cow::Borrowed(t) + } else { + Cow::Owned(t.clone().move_axis(1, 0)?) + }; + let quant = if t.datum_type() == f32::datum_type() { + self.bq.quant_f32(t.try_as_plain()?.as_slice()?)? + } else if t.datum_type() == f16::datum_type() { + self.bq.quant_f16(t.try_as_plain()?.as_slice()?)? + } else { + todo!() + }; + Cow::Owned( + BlockQuantStorage::new(self.bq.clone(), m, k, Arc::new(quant))? + .into_tensor_with_shape(t.datum_type(), &[1, m, k]), + ) + } else { + Cow::Borrowed(t) + }; + ensure!(mn_axis == 0); + ensure!(k_axis == 1); + let bqs = t.try_storage_as::()?; + let k = *t.shape().last().unwrap(); + let packed = self.pack(bqs.value(), k)?; + Ok(Box::new(packed)) + } + + fn precursor(&self) -> WeightType { + WeightType::BlockQuant(self.bq.clone()) + } + + fn k_alignment(&self) -> usize { + self.bq.block_len() + } + + fn r(&self) -> usize { + self.r + } + + fn mem_size(&self, k: TDim, mn: TDim) -> TDim { + k * mn * self.bq.block_bytes() / self.bq.block_len() + } + + fn extract_at_mn_f16( + &self, + data: &EagerPackedInput, + mn: usize, + slice: &mut [f16], + ) -> TractResult<()> { + self.bq.extract_at_mn_f16(data, mn, slice) + } + + fn extract_at_mn_f32( + &self, + data: &EagerPackedInput, + mn: usize, + slice: &mut [f32], + ) -> TractResult<()> { + self.bq.extract_at_mn_f32(data, mn, slice) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/block_quant/q4_0.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/block_quant/q4_0.rs new file mode 100644 index 0000000..499b9bf --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/block_quant/q4_0.rs @@ -0,0 +1,538 @@ +use crate::mmm::PackedExoticFact; + +use super::*; +use num_traits::{AsPrimitive, Float, Zero}; +use std::alloc::Layout; + +#[derive(Copy, Clone, Hash, PartialEq, Eq)] +pub struct BaseQ4_0; + +pub const Q4_0: BaseQ4_0 = BaseQ4_0::<32>; + +impl Debug for BaseQ4_0 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if QK == 32 { + write!(f, "Q4_0") + } else { + write!(f, "BaseQ4_0<{QK}>") + } + } +} + +impl BaseQ4_0 { + fn quant_block(&self, block: &[T], quant: &mut [u8]) + where + f32: AsPrimitive + From, + T: Debug + Float, + { + assert!(quant.len() == self.block_bytes()); + assert!(block.len() == self.block_len()); + let mut writer = NibbleWriter::for_slice(quant); + let mut amax = T::zero(); + let mut max = T::zero(); + for v in block { + if amax < v.abs() { + amax = v.abs(); + max = *v; + } + } + let scale = f32::from(max) / -8f32; + let r_scale = if scale.is_zero() { 0f32 } else { scale.recip() }; + writer.write_f16(f16::from_f32(scale)); + + for idx in 0..block.len() { + // Quant block in GGML nibble order + let ggml_idx = (block.len() / 2) * (idx % 2) + (idx / 2); + let i: i8 = (f32::from(block[ggml_idx]) * r_scale + 8.5f32).as_(); + writer.write_i4(i.min(15)); + } + } + + fn dequant_block(&self, quant: &[u8], block: &mut [T]) + where + f16: AsPrimitive, + i8: AsPrimitive, + { + assert!(quant.len() == self.block_bytes()); + assert!(block.len() == self.block_len()); + let mut nibbles = NibbleReader::for_slice(quant); + let d: T = nibbles.read_f16().as_(); + for idx in 0..block.len() { + let ggml_idx = (block.len() / 2) * (idx % 2) + (idx / 2); + block[ggml_idx] = (nibbles.read_i4() - 8).as_() * d; + } + } + + unsafe fn extract_panel_t( + &self, + value: &EagerPackedInput, + target: &PackedFormat, + panel: usize, + scratch: *mut u8, + ) -> TractResult<()> + where + f16: AsPrimitive, + i8: AsPrimitive, + { + let pbqf: &PackedBlockQuantFormat = + value.fact.format.downcast_ref().with_context(|| { + format!( + "Expecing PackedBlockQuantFormat, found {:?}", + value.fact.format + ) + })?; + ensure!(pbqf.r == target.r); + ensure!(value.fact.k % self.block_len() == 0); + ensure!(*pbqf.bq == *(self as &dyn BlockQuant)); + let scratch = + unsafe { std::slice::from_raw_parts_mut(scratch as *mut T, value.fact.k * target.r) }; + let blocks_for_k = value.fact.k / self.block_len(); + let row_bytes = blocks_for_k * self.block_bytes(); + let input = &value.packed[panel * target.r * row_bytes..]; + let mut scales = vec![T::zero(); target.r]; + let mut scratch = scratch.iter_mut(); + let zipped_order = zipped_order(pbqf.r, pbqf.zip); + let mut weights = vec![0i8; pbqf.r]; + let panel_block_bytes = target.r * self.block_bytes(); + let (scale_offset, weights_offset) = if pbqf.scales_at_end { + ( + panel_block_bytes - target.r * f16::datum_type().size_of(), + 0, + ) + } else { + (0, target.r * f16::datum_type().size_of()) + }; + for block in 0..blocks_for_k { + let block = &input[block * panel_block_bytes..][..panel_block_bytes]; + let mut s_reader = NibbleReader::for_slice(&block[scale_offset..]); + let mut w_reader = NibbleReader::for_slice(&block[weights_offset..]); + for s in &mut scales { + *s = s_reader.read_f16().as_(); + } + for _ in 0..self.block_len() { + for &o in &zipped_order { + weights[o] = w_reader.read_i4(); + } + for (w, s) in weights.iter().zip(scales.iter()) { + *scratch.next().unwrap() = *s * (*w - 8).as_(); + } + } + } + Ok(()) + } + + fn extract_at_mn_t( + &self, + value: &EagerPackedInput, + mn: usize, + target: &mut [T], + ) -> TractResult<()> + where + f16: AsPrimitive, + i8: AsPrimitive, + { + let pbqf: &PackedBlockQuantFormat = + value.fact.format.downcast_ref().with_context(|| { + format!( + "Expecing PackedBlockQuantFormat, found {:?}", + value.fact.format + ) + })?; + ensure!(value.fact.k % self.block_len() == 0); + ensure!(*pbqf.bq == *(self as &dyn BlockQuant)); + ensure!( + value + .fact + .mn + .to_usize() + .ok() + .map(|it| mn < it) + .unwrap_or(true) + ); + ensure!(value.fact.k == target.len()); + let blocks_for_k = value.fact.k / self.block_len(); + let row_bytes = blocks_for_k * self.block_bytes(); + let panel = mn / pbqf.r; + let value = &value.packed[panel * pbqf.r * row_bytes..]; + let mut target = target.iter_mut(); + let zipped_order = zipped_order(pbqf.r, pbqf.zip) + .iter() + .position(|x| *x == mn % pbqf.r) + .unwrap(); + + let panel_block_bytes = pbqf.r * self.block_bytes(); + let (scale_offset, weights_offset) = if pbqf.scales_at_end { + (panel_block_bytes - pbqf.r * f16::datum_type().size_of(), 0) + } else { + (0, pbqf.r * f16::datum_type().size_of()) + }; + unsafe { + for block in 0..blocks_for_k { + let block = value.as_ptr().add(block * panel_block_bytes); + let scale = *((block.add(scale_offset) as *const f16).add(mn % pbqf.r)); + let scale: T = scale.as_(); + for i in 0..self.block_len() { + let byte = *block.add(weights_offset + i * pbqf.r / 2 + zipped_order / 2); + let nib = if zipped_order % 2 == 0 { + byte & 0x0F + } else { + byte >> 4 + }; + *target.next().unwrap() = scale * ((nib as i8) - 8).as_(); + } + } + } + Ok(()) + } +} + +fn zipped_order(r: usize, zip: usize) -> Vec { + if zip == 0 { + (0..r).collect_vec() + } else { + (0..r) + .map(|i| { + let vec_pair_ix = i / (2 * zip); + let lane = (i % (2 * zip)) / 2; + let side = i % 2; + vec_pair_ix * 2 * zip + side * zip + lane + }) + .collect_vec() + } +} + +impl BlockQuant for BaseQ4_0 { + fn block_len(&self) -> usize { + QK + } + + fn block_bytes(&self) -> usize { + 2 + self.block_len() / 2 + } + + fn quant_block_f32(&self, block: &[f32], quant: &mut [u8]) { + self.quant_block(block, quant) + } + + fn quant_block_f16(&self, block: &[f16], quant: &mut [u8]) { + self.quant_block(block, quant) + } + + fn dequant_block_f32(&self, quant: &[u8], block: &mut [f32]) { + self.dequant_block(quant, block) + } + + fn dequant_block_f16(&self, quant: &[u8], block: &mut [f16]) { + self.dequant_block(quant, block) + } + + // s0_0 n0_0 n0_1 n0_2 n0_3 ... n0_30n0_31 s0_32 n0_32n0_33 ... + // s1_0 n1_0 n1_1 n1_2 n1_3 ... n1_30n1_31 s1_32 n1_32n1_33 ... + // + // becomes (with r=4) + // + // s0_0 s1_0 s2_0 s3_0 n0_0 n1_0 n2_0 n3_0 n0_1 n1_1 n2_1 n3_1 ... n0_33 n1_33 n2_33 n3_33 + // s0_32 s1_32 s2_32 s3_32 n0_0 n1_0 n2_0 n3_0 n0_1 n1_1 n2_1 n3_1 ... n0_33 n1_33 n2_33 n3_33 + // ... + fn pack( + &self, + input: &[u8], + k: usize, + r: usize, + zip: usize, + scales_at_end: bool, + ) -> TractResult { + ensure!(input.len() % self.block_bytes() == 0); + ensure!(k % self.block_len() == 0); + // ensure!(input.len() == k * r / self.block_len() * self.block_bytes()); + ensure!(zip < r); + let m = if input.len() == 0 { + 0 + } else { + input.len() / self.block_bytes() * self.block_len() / k + }; + let panels = m.divceil(r); + let blocks_for_k = k / self.block_len(); + let row_bytes = blocks_for_k * self.block_bytes(); + let panel_bytes = row_bytes * r; + let mut blob = + unsafe { Blob::for_layout(Layout::from_size_align(panel_bytes * panels, 128)?) }; + let mut writer = NibbleWriter::for_slice(&mut blob); + let order = zipped_order(r, zip); + let mut scales = vec![f16::zero(); r]; + for p in 0..panels { + let input = &input[(r * p) * row_bytes..]; + let mut readers = (0..r) + .map(|r| { + // manage partial panel + let offset = if r * row_bytes < input.len() { + r * row_bytes + } else { + 0 + }; + NibbleReader::for_slice(&input[offset..]) + }) + .collect_vec(); + let mut temp_nibbles = vec![vec![0i8; self.block_len()]; r]; + for _ in 0..blocks_for_k { + for (row, reader) in readers.iter_mut().enumerate() { + scales[row] = reader.read_f16(); + temp_nibbles[row] = (0..self.block_len()) + .map(|_| reader.read_i4()) + .collect_vec(); + } + if !scales_at_end { + scales.iter().for_each(|s| writer.write_f16(*s)) + } + for pos in 0..self.block_len() { + for &row in &order { + let ggml_idx = pos / (self.block_len() / 2) + (2 * pos) % self.block_len(); + let nib = temp_nibbles[row][ggml_idx]; + writer.write_i4(nib); + } + } + if scales_at_end { + scales.iter().for_each(|s| writer.write_f16(*s)) + } + } + } + Ok(EagerPackedInput { + fact: PackedExoticFact { + format: Box::new(PackedBlockQuantFormat { + bq: Box::new(*self), + r, + zip, + scales_at_end, + }), + mn: m.to_dim(), + k, + }, + packed: blob.into(), + panel_bytes, + mn: m, + }) + } + + unsafe fn extract_packed_panel( + &self, + value: &EagerPackedInput, + target: &PackedFormat, + panel: usize, + scratch: *mut u8, + ) -> TractResult<()> { + unsafe { + dispatch_floatlike!(Self::extract_panel_t(target.dt)( + self, value, target, panel, scratch + )) + } + } + + fn extract_at_mn_f16( + &self, + value: &EagerPackedInput, + mn: usize, + target: &mut [f16], + ) -> TractResult<()> { + self.extract_at_mn_t(value, mn, target) + } + + fn extract_at_mn_f32( + &self, + value: &EagerPackedInput, + mn: usize, + target: &mut [f32], + ) -> TractResult<()> { + self.extract_at_mn_t(value, mn, target) + } +} + +impl Display for BaseQ4_0 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Q4_0") + } +} + +#[cfg(test)] +mod tests { + use num_traits::Zero; + use tract_data::internal::tract_ndarray::Array2; + + use crate::pack::PackedFormat; + + use super::*; + + fn test_loop_f32(b: impl BlockQuant, data: &[f32]) { + let mut input = data.to_vec(); + while input.len() % b.block_len() != 0 { + input.push(0f32); + } + let quant = b.quant_f32(&input).unwrap(); + let result = b.dequant_f32(&quant).unwrap(); + let view = &result.try_as_plain().unwrap().as_slice::().unwrap()[..data.len()]; + assert_eq!(data, view); + } + + fn test_loop_f16(b: impl BlockQuant, data: &[f32]) { + let mut input = data.iter().map(|f| f16::from_f32(*f)).collect_vec(); + while input.len() % b.block_len() != 0 { + input.push(f16::zero()); + } + let quant = b.quant_f16(&input).unwrap(); + let result = b.dequant_f16(&quant).unwrap(); + let view = &result.try_as_plain().unwrap().as_slice::().unwrap(); + assert_eq!(&input, view); + } + + #[test] + fn loop_q4f32_pos() { + test_loop_f32(Q4_0, &[1.0, 2.0, 3.0, 4.0]); + } + + #[test] + fn loop_q4f16_pos() { + test_loop_f16(Q4_0, &[1.0, 2.0, 3.0, 4.0]); + } + + #[test] + fn loop_q4f32_neg() { + test_loop_f32(Q4_0, &[-1.0, -2.0, -3.0, -4.0]); + } + + #[test] + fn loop_q4f16_neg() { + test_loop_f16(Q4_0, &[-1.0, -2.0, -3.0, -4.0]); + } + + #[test] + fn loop_q4_big_pos() { + test_loop_f32(Q4_0, &[1234.0]); + test_loop_f16(Q4_0, &[1234.0]); + } + + #[test] + fn loop_q4_big_neg() { + test_loop_f32(Q4_0, &[-1234.0]); + test_loop_f16(Q4_0, &[-1234.0]); + } + + fn test_extract_f32(b: impl BlockQuant, data: &[f32]) { + let mut input = data.to_vec(); + while input.len() % b.block_len() != 0 { + input.push(0f32); + } + let quant = b.quant_f32(&input).unwrap(); + for (ix, v) in data.iter().enumerate() { + assert_eq!(b.extract_at_offset_f32(&quant, ix).round(), *v); + } + } + + #[test] + fn extract_q40f32_pos() { + let data = (1..) + .map(|i| ((i % 14) - 6) as f32) + .take(5 * Q4_0.block_len()) + .collect_vec(); + test_extract_f32(Q4_0, &data); + } + + fn test_pack_then_extract_panel( + q: impl BlockQuant, + k: usize, + m: usize, + r: usize, + zip: usize, + scales_at_end: bool, + ) -> TractResult<()> { + let weights_orig = + Array2::from_shape_fn((m, k), |(m, k)| ((m * 31 + k * 17) % 20) as f32 - 10.) + .into_tensor(); + let weights_f32 = q + .dequant_f32(&q.quant_f32(weights_orig.try_as_plain()?.as_slice::()?)?)? + .into_shape(&[m, k])?; + let packer = PackedFormat::new(f32::datum_type(), r, 128); + let packed_f32 = packer.pack_tensor(&weights_f32, 1, 0)?; + + let q4 = q.quant_f32(weights_f32.try_as_plain()?.as_slice::()?)?; + let packed_q4 = q.pack(&q4, k, r, zip, scales_at_end)?; + + for panel in 0..packed_f32.panels_count() { + unsafe { + let panel_f32 = packed_f32.panel_bytes(panel, None)?; + let panel_f32 = std::slice::from_raw_parts(panel_f32 as *const f32, k * r); + let mut panel_q4 = Tensor::zero::(&[k * r])?; + q.extract_packed_panel( + &packed_q4, + &packer, + panel, + panel_q4.as_bytes_mut().as_mut_ptr(), + )?; + assert_eq!(panel_q4.try_as_plain()?.as_slice::()?, panel_f32); + } + } + Ok(()) + } + + #[test] + fn pack_then_extract_panel() -> TractResult<()> { + test_pack_then_extract_panel(BaseQ4_0::<2>, 4, 4, 2, 0, false) + } + + #[test] + fn pack_then_extract_panel_with_zip() -> TractResult<()> { + test_pack_then_extract_panel(BaseQ4_0::<2>, 2, 8, 8, 4, false) + } + + #[test] + fn pack_then_extract_panel_with_scales_at_end() -> TractResult<()> { + test_pack_then_extract_panel(BaseQ4_0::<2>, 2, 4, 4, 0, true) + } + + fn test_pack_then_extract_row( + q: impl BlockQuant, + k: usize, + m: usize, + r: usize, + zip: usize, + scales_at_end: bool, + ) -> TractResult<()> { + let weights_orig = + Array2::from_shape_fn((m, k), |(m, k)| ((m * 31 + k * 17) % 20) as f32 - 10.) + .into_tensor(); + let weights_f32 = q + .dequant_f32(&q.quant_f32(weights_orig.try_as_plain()?.as_slice::()?)?)? + .into_shape(&[m, k])?; + let packer = PackedFormat::new(f32::datum_type(), r, 128); + let packed_f32 = packer.pack_tensor(&weights_f32, 1, 0)?; + + let q4 = q.quant_f32(weights_f32.try_as_plain()?.as_slice::()?)?; + let packed_q4 = q.pack(&q4, k, r, zip, scales_at_end)?; + + for row in 0..packed_f32.mn() { + unsafe { + let panel_f32 = packed_f32.panel_bytes(row / r, None)?; + let panel_f32 = std::slice::from_raw_parts(panel_f32 as *const f32, k * r); + let row_f32 = (0..k).map(|ix| panel_f32[row % r + r * ix]).collect_vec(); + + let mut q4 = vec![0f32; k]; + q.extract_at_mn_f32(&packed_q4, row, &mut q4)?; + assert_eq!(q4, row_f32); + } + } + Ok(()) + } + + #[test] + fn pack_then_extract_row() -> TractResult<()> { + test_pack_then_extract_row(BaseQ4_0::<2>, 4, 4, 2, 0, false) + } + + #[test] + fn pack_then_extract_row_with_zip() -> TractResult<()> { + test_pack_then_extract_row(BaseQ4_0::<2>, 2, 8, 8, 4, false) + } + + #[test] + fn pack_then_extract_row_with_scales_at_end() -> TractResult<()> { + test_pack_then_extract_row(BaseQ4_0::<2>, 2, 4, 4, 0, true) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/block_quant/q8_1.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/block_quant/q8_1.rs new file mode 100644 index 0000000..2e9c8a9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/block_quant/q8_1.rs @@ -0,0 +1,523 @@ +use crate::mmm::PackedExoticFact; + +use super::*; +use num_traits::{AsPrimitive, Float, Zero}; +use std::alloc::Layout; +use std::ops::AddAssign; + +#[derive(Copy, Clone, Hash, PartialEq, Eq)] +pub struct BaseQ8_1; + +pub const Q8_1: BaseQ8_1 = BaseQ8_1::<32>; + +impl Debug for BaseQ8_1 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + if QK == 32 { + write!(f, "Q8_1") + } else { + write!(f, "BaseQ8_1<{QK}>") + } + } +} + +impl BaseQ8_1 { + fn quant_block(&self, block: &[T], quant: &mut [u8]) + where + f32: AsPrimitive + From, + T: Debug + Float + AsPrimitive + AddAssign + 'static, + { + assert!(quant.len() == self.block_bytes()); + assert!(block.len() == self.block_len()); + let mut writer = NibbleWriter::for_slice(quant); + let mut amax = T::zero(); + let mut max = T::zero(); + let mut sum = T::zero(); + for v in block { + if amax < v.abs() { + amax = v.abs(); + max = *v; + } + sum += *v; + } + + let scale = f32::from(max) / 127f32; + let r_scale = if scale.is_zero() { 0f32 } else { scale.recip() }; + writer.write_f16(f16::from_f32(scale)); + writer.write_f16(sum.as_()); + + for val_f in block { + let i: i8 = (f32::from(*val_f) * r_scale).round().as_(); + writer.write_i8(i); + } + } + + fn dequant_block(&self, quant: &[u8], block: &mut [T]) + where + f16: AsPrimitive, + i8: AsPrimitive, + { + assert!(quant.len() == self.block_bytes()); + assert!(block.len() == self.block_len()); + let mut quants = NibbleReader::for_slice(quant); + let d: T = quants.read_f16().as_(); + let _sum: T = quants.read_f16().as_(); + for val_f in block { + *val_f = (quants.read_i8()).as_() * d; + } + } + + unsafe fn extract_panel_t( + &self, + value: &EagerPackedInput, + target: &PackedFormat, + panel: usize, + scratch: *mut u8, + ) -> TractResult<()> + where + f16: AsPrimitive, + i8: AsPrimitive, + { + let pbqf: &PackedBlockQuantFormat = + value.fact.format.downcast_ref().with_context(|| { + format!( + "Expecing PackedBlockQuantFormat, found {:?}", + value.fact.format + ) + })?; + ensure!(pbqf.r == target.r); + ensure!(value.fact.k % self.block_len() == 0); + ensure!(*pbqf.bq == *(self as &dyn BlockQuant)); + let scratch = + unsafe { std::slice::from_raw_parts_mut(scratch as *mut T, value.fact.k * target.r) }; + let blocks_for_k = value.fact.k / self.block_len(); + let row_bytes = blocks_for_k * self.block_bytes(); + let input = &value.packed[panel * target.r * row_bytes..]; + let mut scales = vec![T::zero(); target.r]; + let mut scratch = scratch.iter_mut(); + let mut weights = vec![0i8; pbqf.r]; + let panel_block_bytes = target.r * self.block_bytes(); + let (params_offset, weights_offset) = if pbqf.scales_at_end { + ( + panel_block_bytes - target.r * 2 * f16::datum_type().size_of(), + 0, + ) + } else { + (0, target.r * 2 * f16::datum_type().size_of()) + }; + for block in 0..blocks_for_k { + let block = &input[block * panel_block_bytes..][..panel_block_bytes]; + let mut s_reader = NibbleReader::for_slice(&block[params_offset..]); + let mut w_reader = NibbleReader::for_slice(&block[weights_offset..]); + // Layout: [scale_0, sum_0, scale_1, sum_1, .., weights] + for s in &mut scales { + *s = s_reader.read_f16().as_(); + // Unused sums + s_reader.read_f16(); + } + + for _ in 0..self.block_len() { + for w in &mut weights { + *w = w_reader.read_i8(); + } + for (w, s) in weights.iter().zip(scales.iter()) { + *scratch.next().unwrap() = *s * (*w).as_(); + } + } + } + Ok(()) + } + + fn extract_at_mn_t( + &self, + value: &EagerPackedInput, + mn: usize, + target: &mut [T], + ) -> TractResult<()> + where + f16: AsPrimitive, + i8: AsPrimitive, + { + let pbqf: &PackedBlockQuantFormat = + value.fact.format.downcast_ref().with_context(|| { + format!( + "Expecing PackedBlockQuantFormat, found {:?}", + value.fact.format + ) + })?; + ensure!(value.fact.k % self.block_len() == 0); + ensure!(*pbqf.bq == *(self as &dyn BlockQuant)); + ensure!( + value + .fact + .mn + .to_usize() + .ok() + .map(|it| mn < it) + .unwrap_or(true) + ); + ensure!(value.fact.k == target.len()); + let blocks_for_k = value.fact.k / self.block_len(); + let row_bytes = blocks_for_k * self.block_bytes(); + let panel = mn / pbqf.r; + let value = &value.packed[panel * pbqf.r * row_bytes..]; + let mut target = target.iter_mut(); + let panel_block_bytes = pbqf.r * self.block_bytes(); + let (scale_offset, weights_offset) = if pbqf.scales_at_end { + ( + panel_block_bytes - pbqf.r * 2 * f16::datum_type().size_of(), + 0, + ) + } else { + (0, pbqf.r * 2 * f16::datum_type().size_of()) + }; + unsafe { + for block in 0..blocks_for_k { + let block = value.as_ptr().add(block * panel_block_bytes); + let scale = *((block.add(scale_offset) as *const f16).add(2 * (mn % pbqf.r))); + let scale: T = scale.as_(); + for i in 0..self.block_len() { + let byte = *block.add(weights_offset + i * pbqf.r + mn % pbqf.r); + *target.next().unwrap() = scale * (byte as i8).as_(); + } + } + } + Ok(()) + } +} + +impl BlockQuant for BaseQ8_1 { + fn block_len(&self) -> usize { + QK + } + + fn block_bytes(&self) -> usize { + 4 + self.block_len() + } + + fn quant_block_f32(&self, block: &[f32], quant: &mut [u8]) { + self.quant_block(block, quant) + } + + fn quant_block_f16(&self, block: &[f16], quant: &mut [u8]) { + self.quant_block(block, quant) + } + + fn dequant_block_f32(&self, quant: &[u8], block: &mut [f32]) { + self.dequant_block(quant, block) + } + + fn dequant_block_f16(&self, quant: &[u8], block: &mut [f16]) { + self.dequant_block(quant, block) + } + + // s0_0 sum_0_0 n0_0 n0_1 n0_2 n0_3 ... n0_30n0_31 s0_32 sum0_32 n0_32n0_33 ... + // s1_0 sum_1_0 n1_0 n1_1 n1_2 n1_3 ... n1_30n1_31 s1_32 sum_1_32 n1_32n1_33 ... + // + // becomes (with r=4) + // + // s0_0 sum0_0 s1_0 sum 1_0 s2_0 sum2_0 s3_0 sum3_0 n0_0 n1_0 n2_0 n3_0 n0_1 n1_1 n2_1 n3_1 ... n0_33 n1_33 n2_33 n3_33 + // s0_32 sum0_32 s1_32 sum 1_32 s2_32 sum2_32 s3_32 sum3_32 n0_0 n1_0 n2_0 n3_0 n0_1 n1_1 n2_1 n3_1 ... n0_33 n1_33 n2_33 n3_33 + // ... + fn pack( + &self, + input: &[u8], + k: usize, + r: usize, + zip: usize, + scales_at_end: bool, + ) -> TractResult { + ensure!(input.len() % self.block_bytes() == 0); + ensure!(k % self.block_len() == 0); + ensure!(zip == 0, "No zipping required for Q8_1"); + let m = if input.len() == 0 { + 0 + } else { + input.len() / self.block_bytes() * self.block_len() / k + }; + let panels = m.divceil(r); + let blocks_for_k = k / self.block_len(); + let row_bytes = blocks_for_k * self.block_bytes(); + let panel_bytes = row_bytes * r; + let mut blob = + unsafe { Blob::for_layout(Layout::from_size_align(panel_bytes * panels, 128)?) }; + let mut writer = NibbleWriter::for_slice(&mut blob); + let mut scales = vec![f16::zero(); r]; + let mut sums = vec![f16::zero(); r]; + for p in 0..panels { + let input = &input[(r * p) * row_bytes..]; + let mut readers = (0..r) + .map(|r| { + // manage partial panel + let offset = if r * row_bytes < input.len() { + r * row_bytes + } else { + 0 + }; + NibbleReader::for_slice(&input[offset..]) + }) + .collect_vec(); + let mut temp_quants = vec![vec![0i8; self.block_len()]; r]; + for _ in 0..blocks_for_k { + for (row, reader) in readers.iter_mut().enumerate() { + scales[row] = reader.read_f16(); + sums[row] = reader.read_f16(); + temp_quants[row] = (0..self.block_len()) + .map(|_| reader.read_i8()) + .collect_vec(); + } + if !scales_at_end { + scales.iter().zip(&sums).for_each(|(scale, sum)| { + writer.write_f16(*scale); + writer.write_f16(*sum); + }); + } + for pos in 0..self.block_len() { + for row in &temp_quants { + let q = row[pos]; + writer.write_i8(q); + } + } + if scales_at_end { + scales.iter().zip(&sums).for_each(|(scale, sum)| { + writer.write_f16(*scale); + writer.write_f16(*sum); + }); + } + } + } + Ok(EagerPackedInput { + fact: PackedExoticFact { + format: Box::new(PackedBlockQuantFormat { + bq: Box::new(*self), + r, + zip, + scales_at_end, + }), + mn: m.to_dim(), + k, + }, + packed: blob.into(), + panel_bytes, + mn: m, + }) + } + + unsafe fn extract_packed_panel( + &self, + value: &EagerPackedInput, + target: &PackedFormat, + panel: usize, + scratch: *mut u8, + ) -> TractResult<()> { + unsafe { + dispatch_floatlike!(Self::extract_panel_t(target.dt)( + self, value, target, panel, scratch + )) + } + } + + fn extract_at_mn_f16( + &self, + value: &EagerPackedInput, + mn: usize, + target: &mut [f16], + ) -> TractResult<()> { + self.extract_at_mn_t(value, mn, target) + } + + fn extract_at_mn_f32( + &self, + value: &EagerPackedInput, + mn: usize, + target: &mut [f32], + ) -> TractResult<()> { + self.extract_at_mn_t(value, mn, target) + } +} + +impl Display for BaseQ8_1 { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Q8_1") + } +} + +#[cfg(test)] +mod tests { + use num_traits::Zero; + use tract_data::internal::tract_ndarray::Array2; + + use crate::pack::PackedFormat; + + use super::*; + + fn test_loop_f32(b: impl BlockQuant, data: &[f32]) -> TractResult<()> { + let mut input = data.to_vec(); + while input.len() % b.block_len() != 0 { + input.push(0f32); + } + let ref_tensor = unsafe { Tensor::from_slice_align(&input, vector_size())? }; + + let quant = b.quant_f32(&input).unwrap(); + let result = b.dequant_f32(&quant).unwrap(); + result.close_enough(&ref_tensor, Approximation::VeryApproximate) + } + + fn test_loop_f16(b: impl BlockQuant, data: &[f32]) -> TractResult<()> { + let mut input = data.iter().map(|f| f16::from_f32(*f)).collect_vec(); + while input.len() % b.block_len() != 0 { + input.push(f16::zero()); + } + let ref_tensor = unsafe { Tensor::from_slice_align(&input, vector_size())? }; + + let quant = b.quant_f16(&input).unwrap(); + let result = b.dequant_f16(&quant).unwrap(); + result.close_enough(&ref_tensor, Approximation::VeryApproximate) + } + + #[test] + fn loop_q81f32_pos() -> TractResult<()> { + test_loop_f32(Q8_1, &[1.0, 2.0, 3.0, 4.0])?; + Ok(()) + } + + #[test] + fn loop_q81f16_pos() -> TractResult<()> { + test_loop_f16(Q8_1, &[1.0, 2.0, 3.0, 4.0])?; + Ok(()) + } + + #[test] + fn loop_q81f32_neg() -> TractResult<()> { + test_loop_f32(Q8_1, &[-1.0, -2.0, -3.0, -4.0])?; + Ok(()) + } + + #[test] + fn loop_q81f16_neg() -> TractResult<()> { + test_loop_f16(Q8_1, &[-1.0, -2.0, -3.0, -4.0])?; + Ok(()) + } + + #[test] + fn loop_q81_big_pos() -> TractResult<()> { + test_loop_f32(Q8_1, &[1234.0])?; + test_loop_f16(Q8_1, &[1234.0])?; + Ok(()) + } + + #[test] + fn loop_q81_big_neg() -> TractResult<()> { + test_loop_f32(Q8_1, &[-1234.0])?; + test_loop_f16(Q8_1, &[-1234.0])?; + Ok(()) + } + + fn test_extract_f32(b: impl BlockQuant, data: &[f32]) { + let mut input = data.to_vec(); + while input.len() % b.block_len() != 0 { + input.push(0f32); + } + let quant = b.quant_f32(&input).unwrap(); + for (ix, v) in data.iter().enumerate() { + assert_eq!(b.extract_at_offset_f32(&quant, ix).round(), *v); + } + } + + #[test] + fn extract_q81f32_pos() { + let data = (1..) + .map(|i| ((i % 14) - 6) as f32) + .take(5 * Q8_1.block_len()) + .collect_vec(); + test_extract_f32(Q8_1, &data); + } + + fn test_pack_then_extract_panel( + q: impl BlockQuant, + k: usize, + m: usize, + r: usize, + scales_at_end: bool, + ) -> TractResult<()> { + let weights_orig = + Array2::from_shape_fn((m, k), |(m, k)| ((m * 31 + k * 17) % 20) as f32 - 10.) + .into_tensor(); + let weights_f32 = q + .dequant_f32(&q.quant_f32(weights_orig.try_as_plain()?.as_slice::()?)?)? + .into_shape(&[m, k])?; + let packer = PackedFormat::new(f32::datum_type(), r, 128); + let packed_f32 = packer.pack_tensor(&weights_f32, 1, 0)?; + + let q81 = q.quant_f32(weights_f32.try_as_plain()?.as_slice::()?)?; + let packed_q81 = q.pack(&q81, k, r, 0, scales_at_end)?; + + for panel in 0..packed_f32.panels_count() { + unsafe { + let panel_f32 = packed_f32.panel_bytes(panel, None)?; + let panel_f32 = std::slice::from_raw_parts(panel_f32 as *const f32, k * r); + let mut panel_q81 = Tensor::zero::(&[k * r])?; + q.extract_packed_panel( + &packed_q81, + &packer, + panel, + panel_q81.as_bytes_mut().as_mut_ptr(), + )?; + assert_eq!(panel_q81.try_as_plain()?.as_slice::()?, panel_f32); + } + } + Ok(()) + } + + #[test] + fn pack_then_extract_panel() -> TractResult<()> { + test_pack_then_extract_panel(BaseQ8_1::<2>, 4, 4, 2, false) + } + + #[test] + fn pack_then_extract_panel_with_scales_at_end() -> TractResult<()> { + test_pack_then_extract_panel(BaseQ8_1::<2>, 2, 4, 4, true) + } + + fn test_pack_then_extract_row( + q: impl BlockQuant, + k: usize, + m: usize, + r: usize, + scales_at_end: bool, + ) -> TractResult<()> { + let weights_orig = + Array2::from_shape_fn((m, k), |(m, k)| ((m * 31 + k * 17) % 20) as f32 - 10.) + .into_tensor(); + let weights_f32 = q + .dequant_f32(&q.quant_f32(weights_orig.try_as_plain()?.as_slice::()?)?)? + .into_shape(&[m, k])?; + let packer = PackedFormat::new(f32::datum_type(), r, 128); + let packed_f32 = packer.pack_tensor(&weights_f32, 1, 0)?; + + let q81 = q.quant_f32(weights_f32.try_as_plain()?.as_slice::()?)?; + let packed_q81 = q.pack(&q81, k, r, 0, scales_at_end)?; + + for row in 0..packed_f32.mn() { + unsafe { + let panel_f32 = packed_f32.panel_bytes(row / r, None)?; + let panel_f32 = std::slice::from_raw_parts(panel_f32 as *const f32, k * r); + let row_f32 = (0..k).map(|ix| panel_f32[row % r + r * ix]).collect_vec(); + + let mut q81 = vec![0f32; k]; + q.extract_at_mn_f32(&packed_q81, row, &mut q81)?; + assert_eq!(q81, row_f32); + } + } + Ok(()) + } + + #[test] + fn pack_then_extract_row() -> TractResult<()> { + test_pack_then_extract_row(BaseQ8_1::<2>, 4, 4, 2, false) + } + + #[test] + fn pack_then_extract_row_with_scales_at_end() -> TractResult<()> { + test_pack_then_extract_row(BaseQ8_1::<2>, 2, 4, 4, true) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/block_quant/storage.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/block_quant/storage.rs new file mode 100644 index 0000000..f70396e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/block_quant/storage.rs @@ -0,0 +1,134 @@ +use std::fmt; +use std::sync::Arc; + +use tract_data::internal::*; + +use super::BlockQuant; +use super::BlockQuantFact; + +/// Concrete tensor storage for block-quantized weights. +/// +/// Stores a single contiguous `Arc` of quantized data along with the +/// block-quant format. Shape lives on the tensor, not here. +#[derive(Clone, PartialEq, Eq)] +pub struct BlockQuantStorage { + format: Box, + data: Arc, +} + +impl BlockQuantStorage { + fn expected_bytes(format: &dyn BlockQuant, m: usize, k: usize) -> usize { + m * k / format.block_len() * format.block_bytes() + } + + pub fn new( + format: Box, + m: usize, + k: usize, + data: Arc, + ) -> TractResult { + let expected = Self::expected_bytes(&*format, m, k); + ensure!( + data.len() == expected, + "BlockQuantStorage::new: blob length {} does not match expected {} (m={}, k={}, format={})", + data.len(), + expected, + m, + k, + format, + ); + Ok(Self { format, data }) + } + + pub fn format(&self) -> &dyn BlockQuant { + &*self.format + } + + /// Returns the single contiguous blob. + pub fn value(&self) -> &Arc { + &self.data + } + + /// Converts this storage into a `Tensor` with the given shape. + /// + /// `dt` is the logical element type (e.g. f32, f16) — the type these + /// weights represent when dequantized. + pub fn into_tensor_with_shape(self, dt: DatumType, shape: &[usize]) -> Tensor { + Tensor::from_storage(dt, shape, self) + } +} + +/// Returns a byte slice for a single group within contiguous block-quant data. +pub fn block_quant_slice<'a>( + data: &'a [u8], + format: &dyn BlockQuant, + m_per_group: usize, + k: usize, + g: usize, +) -> &'a [u8] { + let row_bytes = k / format.block_len() * format.block_bytes(); + let group_bytes = m_per_group * row_bytes; + let start = g * group_bytes; + &data[start..start + group_bytes] +} + +impl fmt::Debug for BlockQuantStorage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "BlockQuantStorage({}, bytes={})", + self.format, + self.data.len() + ) + } +} + +impl fmt::Display for BlockQuantStorage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "BlockQuantStorage({}, bytes={})", + self.format, + self.data.len() + ) + } +} + +impl TensorStorage for BlockQuantStorage { + fn byte_len(&self) -> usize { + self.data.len() + } + + fn is_empty(&self) -> bool { + self.data.is_empty() + } + + fn deep_clone(&self) -> Box { + Box::new(self.clone()) + } + + fn as_plain(&self) -> Option<&PlainStorage> { + None + } + + fn as_plain_mut(&mut self) -> Option<&mut PlainStorage> { + None + } + + fn into_plain(self: Box) -> Option { + None + } + + fn dyn_hash(&self, state: &mut dyn std::hash::Hasher) { + state.write_u8(1); + self.format.dyn_hash(state); + state.write(self.data.as_bytes()); + } + + fn exotic_fact(&self, shape: &[usize]) -> TractResult>> { + Ok(Some(Box::new(BlockQuantFact::new( + dyn_clone::clone_box(&*self.format), + shape.into(), + )))) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/block_quant/value.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/block_quant/value.rs new file mode 100644 index 0000000..4a76e57 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/block_quant/value.rs @@ -0,0 +1,83 @@ +use super::{BlockQuant, PackedBlockQuantFormat}; +use tract_data::TVec; +use tract_data::internal::*; + +#[allow(clippy::derived_hash_with_manual_eq)] +#[derive(Clone, Hash)] +pub struct BlockQuantFact { + pub format: Box, + shape: TVec, +} +impl BlockQuantFact { + pub fn new(format: Box, shape: TVec) -> Self { + Self { format, shape } + } + + /// Product of all leading dims except the last two (M, K). + /// For rank <= 2, returns 1. + pub fn num_groups(&self) -> usize { + if self.shape.len() <= 2 { + 1 + } else { + self.shape[..self.shape.len() - 2].iter().product() + } + } + + /// Product of all dims except the last (K). This is the flat M + /// dimension (groups * m_per_group). + pub fn m(&self) -> usize { + self.shape[..self.shape.len() - 1].iter().product() + } + + /// Last dimension. + pub fn k(&self) -> usize { + *self.shape.last().unwrap() + } + + pub fn shape(&self) -> &[usize] { + &self.shape + } +} + +impl std::fmt::Debug for BlockQuantFact { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}({:?})", self.format, self.shape) + } +} + +impl ExoticFact for BlockQuantFact { + fn buffer_sizes(&self) -> TVec { + let total = self.m() * self.k() / self.format.block_len() * self.format.block_bytes(); + tvec!(total.to_dim()) + } +} + +impl PartialEq for BlockQuantFact { + fn eq(&self, other: &Self) -> bool { + *self.format == *other.format && self.shape == other.shape + } +} +impl Eq for BlockQuantFact {} + +#[derive(Clone, Hash, PartialEq)] +pub struct PackedBlockQuantFact { + pub format: PackedBlockQuantFormat, + pub shape: TVec, +} +impl Eq for PackedBlockQuantFact {} + +impl std::fmt::Debug for PackedBlockQuantFact { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}({:?})", self.format, self.shape) + } +} + +impl ExoticFact for PackedBlockQuantFact { + fn buffer_sizes(&self) -> TVec { + tvec!( + (self.shape.iter().product::() / self.format.bq.block_len() + * self.format.bq.block_bytes()) + .to_dim() + ) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/by_scalar.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/by_scalar.rs new file mode 100644 index 0000000..12d0cb0 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/by_scalar.rs @@ -0,0 +1,101 @@ +use std::fmt::Debug; +use std::marker::PhantomData; + +use crate::element_wise::{ElementWise, ElementWiseKer}; +use crate::element_wise_helper::map_slice_with_alignment; +use crate::{LADatum, LinalgFn}; +use tract_data::internal::*; + +/// Generic implementation struct that unify all by scalar kernels. +/// A by scalar operation is an ElementWise operation with a scalar paramerer. +#[derive(Debug, Clone, new)] +pub struct ByScalarImpl +where + T: LADatum, + K: ByScalarKer + Clone, +{ + phantom: PhantomData<(K, T)>, +} + +impl ElementWise for ByScalarImpl +where + T: LADatum, + K: ByScalarKer + Clone, +{ + fn name(&self) -> &'static str { + K::name() + } + fn run_with_params(&self, vec: &mut [T], params: T) -> TractResult<()> { + map_slice_with_alignment( + vec, + |data| K::run(data, params), + K::nr(), + K::alignment_bytes(), + ) + } +} + +pub trait ByScalarKer: ElementWiseKer +where + T: LADatum, +{ + fn bin() -> Box { + Box::new(|a: &mut TensorView, b: &TensorView| { + let a_slice = a.as_slice_mut()?; + let b = b.as_slice()?[0]; + (Self::ew()).run_with_params(a_slice, b) + }) + } +} + +macro_rules! by_scalar_impl_wrap { + ($ti: ident, $func: ident, $nr: expr, $alignment_items: expr, $params: ty, $run: item) => { + paste! { + ew_impl_wrap!($ti, $func, $nr, $alignment_items, $ti, $run); + + impl crate::frame::by_scalar::ByScalarKer<$ti> for $func {} + } + }; +} + +#[cfg(test)] +#[macro_use] +pub mod test { + use crate::LADatum; + use crate::frame::element_wise::ElementWiseKer; + use num_traits::{AsPrimitive, Float}; + use proptest::test_runner::TestCaseResult; + + #[macro_export] + macro_rules! by_scalar_frame_tests { + ($cond:expr, $t: ty, $ker:ty, $func:expr) => { + pastey::paste! { + proptest::proptest! { + #[test] + fn [](xs in proptest::collection::vec(-25f32..25.0, 0..100), scalar in -25f32..25f32) { + if $cond { + $crate::frame::by_scalar::test::test_by_scalar::<$ker, $t>(&*xs, scalar, $func).unwrap() + } + } + } + } + }; + } + + pub fn test_by_scalar, T: LADatum + Float>( + values: &[f32], + scalar: f32, + func: impl Fn(T, T) -> T, + ) -> TestCaseResult + where + f32: AsPrimitive, + { + crate::setup_test_logger(); + let values: Vec = values.iter().copied().map(|x| x.as_()).collect(); + crate::frame::element_wise::test::test_element_wise_params::( + &values, + |a| (func)(a, scalar.as_()), + scalar.as_(), + ) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/element_wise.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/element_wise.rs new file mode 100644 index 0000000..7a05c48 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/element_wise.rs @@ -0,0 +1,170 @@ +use std::fmt::Debug; +use std::marker::PhantomData; + +use tract_data::TractResult; + +use crate::LADatum; + +use super::element_wise_helper::map_slice_with_alignment; + +macro_rules! ew_impl_wrap { + ($ti: ident, $func: ident, $nr: expr, $alignment_items: expr, $params: ty, $run: item) => { + paste! { + #[derive(Copy, Clone, Debug)] + #[allow(non_camel_case_types)] + pub struct $func; + + impl crate::frame::element_wise::ElementWiseKer<$ti, $params> for $func { + #[inline(always)] + fn name() -> &'static str { + stringify!($func) + } + #[inline(always)] + fn nr() -> usize { + $nr + } + #[inline(always)] + fn alignment_items() -> usize { + $alignment_items + } + $run + } + } + }; +} + +macro_rules! ew_impl { + ($ti: ident, $func: ident, $nr: expr, $alignment_items: expr) => { + paste! { + mod [] { + #[allow(unused_imports)] + use tract_data::prelude::f16; + extern_kernel!(fn $func(ptr: *mut $ti, count: usize) -> ()); + } + ew_impl_wrap!($ti, $func, $nr, $alignment_items, (), + #[inline(never)] + fn run(buf: &mut [$ti], _params: ()) { + unsafe { []::$func(buf.as_mut_ptr(), buf.len()) } + } + ); + } + }; + ($ti: ident, $func: ident, $nr: expr, $alignment_items: expr, $params: ty) => { + paste! { + mod [] { + #[allow(unused_imports)] + use tract_data::prelude::f16; + extern_kernel!(fn $func(ptr: *mut $ti, count: usize, params: $params) -> ()); + } + ew_impl_wrap!($ti, $func, $nr, $alignment_items, $params, + #[inline(never)] + fn run(buf: &mut [$ti], params: $params) { + unsafe { []::$func(buf.as_mut_ptr(), buf.len(), params) } + } + ); + } + }; +} + +pub trait ElementWise: Send + Sync + Debug + dyn_clone::DynClone +where + Params: Copy + Send + Sync + Debug + 'static + Default, + T: Copy + Debug + PartialEq + Send + Sync, +{ + fn name(&self) -> &'static str; + fn run(&self, vec: &mut [T]) -> TractResult<()> { + self.run_with_params(vec, Params::default()) + } + fn run_with_params(&self, vec: &mut [T], params: Params) -> TractResult<()>; +} + +dyn_clone::clone_trait_object!( ElementWise where T: Copy, Params: Copy); + +#[derive(Debug, Clone, new)] +pub struct ElementWiseImpl +where + T: LADatum, + Params: Copy + Send + Sync + Debug + 'static + Default, + K: ElementWiseKer + Clone, +{ + phantom: PhantomData<(K, T, Params)>, +} + +impl ElementWise for ElementWiseImpl +where + T: LADatum, + Params: Copy + Send + Sync + Debug + 'static + Default, + K: ElementWiseKer + Clone, +{ + fn name(&self) -> &'static str { + K::name() + } + fn run_with_params(&self, vec: &mut [T], params: Params) -> TractResult<()> { + map_slice_with_alignment( + vec, + |data| K::run(data, params), + K::nr(), + K::alignment_bytes(), + ) + } +} + +pub trait ElementWiseKer: + Send + Sync + Debug + dyn_clone::DynClone + Clone + 'static +where + Params: Copy + Send + Sync + Debug + 'static + Default, + T: LADatum, +{ + fn name() -> &'static str; + fn alignment_bytes() -> usize { + Self::alignment_items() * T::datum_type().size_of() + } + fn alignment_items() -> usize; + fn nr() -> usize; + fn run(vec: &mut [T], params: Params); + fn ew() -> Box> { + Box::new(ElementWiseImpl::::new()) + } +} + +#[cfg(test)] +pub mod test { + use crate::{LADatum, frame::element_wise::*}; + use proptest::test_runner::{TestCaseError, TestCaseResult}; + use tract_data::internal::*; + + pub fn test_element_wise, T: LADatum, F: Fn(T) -> T>( + values: &[T], + reference: F, + ) -> TestCaseResult { + test_element_wise_params::(values, reference, ()) + } + + pub fn test_element_wise_params< + K: ElementWiseKer, + T: LADatum, + F: Fn(T) -> T, + Params, + >( + values: &[T], + reference: F, + params: Params, + ) -> TestCaseResult + where + Params: Copy + Send + Sync + Debug + 'static + Default, + { + crate::setup_test_logger(); + let op = ElementWiseImpl::::new(); + let mut values = values.to_vec(); + while values.len() < K::nr() { + values.push(T::zero()); + } + let expected = values.iter().copied().map(reference).collect::>(); + let mut found = values; + op.run_with_params(&mut found, params).unwrap(); + tensor1(&found) + .close_enough(&tensor1(&expected), true) + .map_err(|e| TestCaseError::fail(e.root_cause().to_string()))?; + Ok(()) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/element_wise_helper.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/element_wise_helper.rs new file mode 100644 index 0000000..f4ca79c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/element_wise_helper.rs @@ -0,0 +1,172 @@ +use crate::LADatum; +use std::alloc::*; +use tract_data::TractResult; + +pub(crate) fn map_slice_with_alignment( + vec: &mut [T], + f: impl Fn(&mut [T]), + nr: usize, + alignment_bytes: usize, +) -> TractResult<()> +where + T: LADatum, +{ + if vec.is_empty() { + return Ok(()); + } + unsafe { + TMP.with(|buffer| { + let mut buffer = buffer.borrow_mut(); + buffer.ensure(nr * T::datum_type().size_of(), alignment_bytes); + let tmp = std::slice::from_raw_parts_mut(buffer.buffer as *mut T, nr); + let mut compute_via_temp_buffer = |slice: &mut [T]| { + tmp[..slice.len()].copy_from_slice(slice); + f(tmp); + slice.copy_from_slice(&tmp[..slice.len()]) + }; + let prefix_len = vec.as_ptr().align_offset(alignment_bytes).min(vec.len()); + if prefix_len > 0 { + compute_via_temp_buffer(&mut vec[..prefix_len]); + } + let aligned_len = (vec.len() - prefix_len) / nr * nr; + if aligned_len > 0 { + f(&mut vec[prefix_len..][..aligned_len]); + } + if prefix_len + aligned_len < vec.len() { + compute_via_temp_buffer(&mut vec[prefix_len + aligned_len..]); + } + }) + } + Ok(()) +} + +pub(crate) fn reduce_slice_with_alignment( + vec: &[T], + f: impl Fn(&[T]) -> T, + nr: usize, + alignment_bytes: usize, + neutral: T, + reduce: impl Fn(T, T) -> T, +) -> TractResult +where + T: LADatum, +{ + if vec.is_empty() { + return Ok(neutral); + } + let mut red = neutral; + unsafe { + TMP.with(|buffer| { + let mut buffer = buffer.borrow_mut(); + buffer.ensure(nr * T::datum_type().size_of(), alignment_bytes); + let tmp = std::slice::from_raw_parts_mut(buffer.buffer as *mut T, nr); + let mut compute_via_temp_buffer = |slice: &[T], red: &mut T| { + tmp[..slice.len()].copy_from_slice(slice); + tmp[slice.len()..].fill(neutral); + *red = reduce(*red, f(tmp)); + }; + let prefix_len = vec.as_ptr().align_offset(alignment_bytes).min(vec.len()); + if prefix_len > 0 { + compute_via_temp_buffer(&vec[..prefix_len], &mut red); + } + let aligned_len = (vec.len() - prefix_len) / nr * nr; + if aligned_len > 0 { + let t = f(&vec[prefix_len..][..aligned_len]); + red = reduce(red, t); + } + if prefix_len + aligned_len < vec.len() { + compute_via_temp_buffer(&vec[prefix_len + aligned_len..], &mut red); + } + }) + } + Ok(red) +} + +pub(crate) fn map_reduce_slice_with_alignment( + vec: &mut [T], + f: impl Fn(&mut [T]) -> T, + nr: usize, + alignment_bytes: usize, + map_neutral: T, + neutral: T, + reduce: impl Fn(T, T) -> T, +) -> TractResult +where + T: LADatum, +{ + if vec.is_empty() { + return Ok(neutral); + } + let mut red = neutral; + unsafe { + TMP.with(|buffer| { + let mut buffer = buffer.borrow_mut(); + buffer.ensure(nr * T::datum_type().size_of(), alignment_bytes); + let tmp = std::slice::from_raw_parts_mut(buffer.buffer as *mut T, nr); + let mut compute_via_temp_buffer = |slice: &mut [T], red: &mut T| { + tmp[..slice.len()].copy_from_slice(slice); + tmp[slice.len()..].fill(map_neutral); + *red = reduce(*red, f(tmp)); + slice.copy_from_slice(&tmp[..slice.len()]); + }; + let prefix_len = vec.as_ptr().align_offset(alignment_bytes).min(vec.len()); + if prefix_len > 0 { + compute_via_temp_buffer(&mut vec[..prefix_len], &mut red); + } + let aligned_len = (vec.len() - prefix_len) / nr * nr; + if aligned_len > 0 { + let t = f(&mut vec[prefix_len..][..aligned_len]); + red = reduce(red, t); + } + if prefix_len + aligned_len < vec.len() { + compute_via_temp_buffer(&mut vec[prefix_len + aligned_len..], &mut red); + } + }) + } + Ok(red) +} + +std::thread_local! { + static TMP: std::cell::RefCell = std::cell::RefCell::new(TempBuffer::default()); +} + +pub struct TempBuffer { + pub layout: Layout, + pub buffer: *mut u8, +} + +impl Default for TempBuffer { + fn default() -> Self { + TempBuffer { + layout: Layout::new::<()>(), + buffer: std::ptr::null_mut(), + } + } +} + +impl TempBuffer { + pub fn ensure(&mut self, size: usize, alignment: usize) { + unsafe { + if size > self.layout.size() || alignment > self.layout.align() { + let size = size.max(self.layout.size()); + let alignment = alignment.max(self.layout.align()); + if !self.buffer.is_null() { + std::alloc::dealloc(self.buffer, self.layout); + } + self.layout = Layout::from_size_align_unchecked(size, alignment); + self.buffer = std::alloc::alloc(self.layout); + assert!(!self.buffer.is_null()); + } + } + } +} + +impl Drop for TempBuffer { + fn drop(&mut self) { + unsafe { + if !self.buffer.is_null() { + std::alloc::dealloc(self.buffer, self.layout); + } + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/erf.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/erf.rs new file mode 100644 index 0000000..4a49a06 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/erf.rs @@ -0,0 +1,82 @@ +#[allow(unused_macros)] +macro_rules! erf_impl { + ($ti: ident, $func: ident, $nr: expr, $alignment_items: expr, $cond: expr) => { + ew_impl!($ti, $func, $nr, $alignment_items); + #[cfg(test)] + paste! { + mod [] { + use super::*; + erf_frame_tests!($cond, $ti, $func); + } + } + }; +} + +#[cfg(test)] +#[macro_use] +pub mod test { + use crate::LADatum; + use crate::frame::element_wise::*; + use num_traits::{AsPrimitive, Float}; + use proptest::test_runner::TestCaseResult; + + #[macro_export] + macro_rules! erf_frame_tests { + ($cond:expr, $t: ty, $ker:ty) => { + proptest::proptest! { + #[test] + fn prop(xs in proptest::collection::vec(-5f32..5.0, 0..100)) { + if $cond { + $crate::frame::erf::test::test_erf::<$ker, $t>(&*xs).unwrap() + } + } + } + #[test] + fn trivial() { + if $cond { + $crate::frame::erf::test::test_erf::<$ker, $t>(&[ + -5f32, -2.0, -1.0, -0.5, 0.0, 0.5, 1.0, 2.0, 5.0, + ]) + .unwrap(); + } + } + #[test] + fn zeros() { + if $cond { + $crate::frame::erf::test::test_erf::<$ker, $t>(&[0.0; 16]).unwrap(); + } + } + }; + } + + pub fn test_erf, T: LADatum + Float>(values: &[f32]) -> TestCaseResult + where + f32: AsPrimitive, + T: AsPrimitive, + { + let data = tract_data::prelude::tensor1(values); + let data = data.cast_to::().unwrap(); + let data = data.try_as_plain().unwrap().as_slice::().unwrap(); + crate::frame::element_wise::test::test_element_wise::(data, |x: T| { + // Abramowitz & Stegun 7.1.26 six-coefficient approximation, mirroring + // generic/erf.rs::serf so the test reference matches the production scalar path. + const A1: f32 = 0.0705230784; + const A2: f32 = 0.0422820123; + const A3: f32 = 0.0092705272; + const A4: f32 = 0.0001520143; + const A5: f32 = 0.0002765672; + const A6: f32 = 0.0000430638; + let x: f32 = x.as_(); + let signum = x.signum(); + let abs = x.abs(); + let y = A6 * abs; + let y = (A5 + y) * abs; + let y = (A4 + y) * abs; + let y = (A3 + y) * abs; + let y = (A2 + y) * abs; + let y = (A1 + y) * abs; + let y = 1.0 - (y + 1.0).powi(16).recip(); + y.copysign(signum).as_() + }) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/gelu.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/gelu.rs new file mode 100644 index 0000000..364efcb --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/gelu.rs @@ -0,0 +1,61 @@ +#[allow(unused_macros)] +macro_rules! gelu_impl { + ($ti: ident, $func: ident, $nr: expr, $alignment_items: expr, $cond: expr) => { + ew_impl!($ti, $func, $nr, $alignment_items); + #[cfg(test)] + paste! { + mod [] { + use super::*; + gelu_frame_tests!($cond, $ti, $func); + } + } + }; +} + +#[cfg(test)] +#[macro_use] +pub mod test { + use crate::LADatum; + use crate::frame::element_wise::*; + use num_traits::{AsPrimitive, Float}; + use proptest::test_runner::TestCaseResult; + + #[macro_export] + macro_rules! gelu_frame_tests { + ($cond:expr, $t: ty, $ker:ty) => { + proptest::proptest! { + #[test] + fn prop(xs in proptest::collection::vec(-10f32..10.0, 0..100)) { + if $cond { + $crate::frame::gelu::test::test_gelu::<$ker, $t>(&*xs).unwrap() + } + } + } + #[test] + fn trivial() { + if $cond { + $crate::frame::gelu::test::test_gelu::<$ker, $t>(&[-5f32, -1.0, 0.0, 1.0, 5.0]) + .unwrap(); + } + } + }; + } + + pub fn test_gelu, T: LADatum + Float>(values: &[f32]) -> TestCaseResult + where + f32: AsPrimitive, + { + let data = tract_data::prelude::tensor1(values); + let data = data.cast_to::().unwrap(); + let data = data.try_as_plain().unwrap().as_slice::().unwrap(); + // Tanh-form GELU (pow=3): 0.5 * x * (1 + tanh(sqrt(2/pi) * (x + 0.044715 * x^3))) + crate::frame::element_wise::test::test_element_wise::(data, |x: T| { + let half: T = 0.5f32.as_(); + let one: T = 1f32.as_(); + let coef: T = 0.044715f32.as_(); + let sqrt_2_over_pi: T = 0.7978845608028654f32.as_(); + let inner = sqrt_2_over_pi * (x + coef * x * x * x); + half * x * (one + inner.tanh()) + }) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/hardswish.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/hardswish.rs new file mode 100644 index 0000000..f65b995 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/hardswish.rs @@ -0,0 +1,64 @@ +#[allow(unused_macros)] +macro_rules! hardswish_impl { + ($ti: ident, $func: ident, $nr: expr, $alignment_items: expr, $cond: expr) => { + ew_impl!($ti, $func, $nr, $alignment_items); + #[cfg(test)] + paste! { + mod [] { + use super::*; + hardswish_frame_tests!($cond, $ti, $func); + } + } + }; +} + +#[cfg(test)] +#[macro_use] +pub mod test { + use crate::LADatum; + use crate::frame::element_wise::*; + use num_traits::{AsPrimitive, Float, Zero}; + use proptest::test_runner::TestCaseResult; + + #[macro_export] + macro_rules! hardswish_frame_tests { + ($cond:expr, $t: ty, $ker:ty) => { + proptest::proptest! { + #[test] + fn prop(xs in proptest::collection::vec(-25f32..25.0, 0..100)) { + if $cond { + $crate::frame::hardswish::test::test_hardswish::<$ker, $t>(&*xs).unwrap() + } + } + } + #[test] + fn trivial() { + if $cond { + $crate::frame::hardswish::test::test_hardswish::<$ker, $t>(&[ + -10f32, -3.0, -1.0, 0.0, 1.0, 3.0, 6.0, 10.0, + ]) + .unwrap(); + } + } + }; + } + + pub fn test_hardswish, T: LADatum + Float>( + values: &[f32], + ) -> TestCaseResult + where + f32: AsPrimitive, + { + let data = tract_data::prelude::tensor1(values); + let data = data.cast_to::().unwrap(); + let data = data.try_as_plain().unwrap().as_slice::().unwrap(); + crate::frame::element_wise::test::test_element_wise::(data, |x: T| { + let three: T = 3f32.as_(); + let six: T = 6f32.as_(); + let zero: T = T::zero(); + let inv6: T = (1f32 / 6f32).as_(); + let relu6 = ((x + three).min(six)).max(zero); + x * relu6 * inv6 + }) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/leaky_relu.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/leaky_relu.rs new file mode 100644 index 0000000..0751877 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/leaky_relu.rs @@ -0,0 +1,63 @@ +#[allow(unused_macros)] +macro_rules! leaky_relu_impl { + ($ti: ident, $func: ident, $nr: expr, $alignment_items: expr, $cond: expr) => { + ew_impl!($ti, $func, $nr, $alignment_items, $ti); + #[cfg(test)] + paste! { + mod [] { + use super::*; + leaky_relu_frame_tests!($cond, $ti, $func); + } + } + }; +} + +#[cfg(test)] +#[macro_use] +pub mod test { + use crate::{LADatum, frame::element_wise::*}; + use num_traits::{AsPrimitive, Float}; + use proptest::test_runner::TestCaseResult; + + #[macro_export] + macro_rules! leaky_relu_frame_tests { + ($cond:expr, $t: ty, $ker:ty) => { + proptest::proptest! { + #[test] + fn prop(xs in proptest::collection::vec(-25f32..25.0, 0..100), alpha in 0f32..1f32) { + if $cond { + $crate::frame::leaky_relu::test::test_leaky_relu::<$ker, $t>(&*xs, alpha).unwrap() + } + } + } + #[test] + fn trivial() { + if $cond { + $crate::frame::leaky_relu::test::test_leaky_relu::<$ker, $t>(&[-10f32], 0.0496).unwrap(); + } + } + }; + } + + pub fn test_leaky_relu, T: LADatum + Float>( + values: &[f32], + alpha: f32, + ) -> TestCaseResult + where + f32: AsPrimitive, + { + let data = tract_data::prelude::tensor1(values); + let data = data.cast_to::().unwrap(); + let data = data.try_as_plain().unwrap().as_slice::().unwrap(); + let alpha: T = tract_data::prelude::tensor0(alpha) + .cast_to_scalar::() + .unwrap(); + crate::frame::element_wise::test::test_element_wise_params::( + data, + |x: T| { + if x > T::zero() { x } else { alpha * x } + }, + alpha, + ) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/lut.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/lut.rs new file mode 100644 index 0000000..51a703f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/lut.rs @@ -0,0 +1,151 @@ +use std::fmt; +use std::hash::Hash; +use std::marker::PhantomData; +use tract_data::internal::*; + +pub trait Lut: fmt::Debug + dyn_clone::DynClone + Send + Sync { + fn table(&self) -> &[u8]; + fn run(&self, buf: &mut [u8]); +} + +dyn_clone::clone_trait_object!(Lut); + +impl PartialEq for dyn Lut { + fn eq(&self, other: &Self) -> bool { + self.table() == other.table() + } +} +impl Eq for dyn Lut {} + +#[derive(Debug, Clone, Hash)] +pub struct LutImpl { + table: Tensor, + _boo: PhantomData, +} + +impl LutImpl { + pub fn new(table: &[u8]) -> LutImpl { + unsafe { + LutImpl { + table: Tensor::from_raw_aligned::( + &[table.len()], + table, + K::table_alignment_bytes(), + ) + .unwrap(), + _boo: PhantomData, + } + } + } +} + +impl Lut for LutImpl { + fn table(&self) -> &[u8] { + self.table.try_as_plain().unwrap().as_slice().unwrap() + } + + fn run(&self, buf: &mut [u8]) { + unsafe { + let table: *const u8 = self.table.as_ptr_unchecked(); + let align = K::input_alignment_bytes(); + let aligned_start = (buf.as_ptr() as usize).next_multiple_of(align); + let prefix = (aligned_start - buf.as_ptr() as usize).min(buf.len()); + for i in 0..(prefix as isize) { + let ptr = buf.as_mut_ptr().offset(i); + *ptr = *table.offset(*ptr as isize); + } + let remaining = buf.len() - prefix; + if remaining == 0 { + return; + } + let n = K::n(); + let aligned_len = remaining / n * n; + if aligned_len > 0 { + K::run(buf.as_mut_ptr().add(prefix), aligned_len, table); + } + let remaining = buf.len() - aligned_len - prefix; + for i in 0..remaining { + let ptr = buf.as_mut_ptr().add(i + prefix + aligned_len); + *ptr = *table.offset(*ptr as isize); + } + } + } +} + +pub trait LutKer: Clone + fmt::Debug + Send + Sync + Hash { + fn name() -> &'static str; + fn n() -> usize; + fn input_alignment_bytes() -> usize; + fn table_alignment_bytes() -> usize; + unsafe fn run(buf: *mut u8, len: usize, table: *const u8); +} + +#[cfg(test)] +#[macro_use] +pub mod test { + use super::*; + use proptest::prelude::*; + + #[derive(Debug)] + pub struct LutProblem { + pub table: Vec, + pub data: Vec, + } + + impl Arbitrary for LutProblem { + type Parameters = (); + type Strategy = BoxedStrategy; + + fn arbitrary_with(_p: ()) -> Self::Strategy { + proptest::collection::vec(any::(), 1..256) + .prop_flat_map(|table| { + let data = proptest::collection::vec(0..table.len() as u8, 0..100); + (Just(table), data) + }) + .prop_map(|(table, data)| LutProblem { table, data }) + .boxed() + } + } + + impl LutProblem { + pub fn reference(&self) -> Vec { + self.data.iter().map(|x| self.table[*x as usize]).collect() + } + + pub fn test(&self) -> Vec { + let lut = LutImpl::::new(&self.table); + let mut data = self.data.clone(); + lut.run(&mut data); + data + } + } + + #[macro_export] + macro_rules! lut_frame_tests { + ($cond:expr, $ker:ty) => { + mod lut { + use proptest::prelude::*; + #[allow(unused_imports)] + use $crate::frame::lut::test::*; + + proptest::proptest! { + #[test] + fn lut_prop(pb in any::()) { + if $cond { + prop_assert_eq!(pb.test::<$ker>(), pb.reference()) + } + } + } + + #[test] + fn test_empty() { + let pb = LutProblem { + table: vec![0], + data: vec![], + }; + assert_eq!(pb.test::<$ker>(), pb.reference()) + } + } + }; + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/cost_model.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/cost_model.rs new file mode 100644 index 0000000..37cb172 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/cost_model.rs @@ -0,0 +1,90 @@ +use tract_data::internal::*; +use tract_data::itertools::{Itertools, izip}; + +use super::MatMatMul; + +fn order_f(&a: &F, &b: &F) -> std::cmp::Ordering { + if a < b { + std::cmp::Ordering::Less + } else { + std::cmp::Ordering::Greater + } +} + +#[derive(Debug)] +pub struct CostModel<'a> { + pub big_product_mkn_threshold: f32, + pub big_product_kernel_choice: &'a str, + pub kernels: &'a [&'a str], + pub mrs: &'a [u32], + pub nrs: &'a [u32], + pub feat_norm_mean: &'a [f32], + pub feat_norm_stddev: &'a [f32], + pub w1: &'a [f32], + pub b1: &'a [f32], + pub w2: &'a [f32], + pub b2: &'a [f32], +} + +impl CostModel<'_> { + pub fn features(&self, m: usize, k: usize, n: usize) -> Vec { + let mut feat = vec![ + (m as f32).ln(), + (k as f32).ln(), + (n as f32).ln(), + (n as f32 * m as f32 * k as f32).ln(), + ]; + for &mr in self.mrs { + let mr = mr as usize; + feat.push((m % mr) as f32); + feat.push((m % mr != 0) as usize as f32); + } + for &nr in self.nrs { + let nr = nr as usize; + feat.push((n % nr) as f32); + feat.push((n % nr != 0) as usize as f32); + } + feat + } + + fn normalize(&self, feat: &mut [f32]) { + izip!(feat, self.feat_norm_mean, self.feat_norm_stddev) + .for_each(|(x, m, s)| *x = (*x - m) / s) + } + + fn dnn(x: &[f32], w: &[f32], b: &[f32]) -> Vec { + let x = tract_ndarray::Array1::from_vec(x.to_vec()); + let w = tract_ndarray::Array2::from_shape_vec([b.len(), x.len()], w.to_vec()).unwrap(); + let b = tract_ndarray::Array1::from_vec(b.to_vec()); + (w.dot(&x) + b).to_vec() + } + + pub fn predict(&self, m: usize, k: usize, n: usize) -> &str { + let mut x = self.features(m, k, n); + self.normalize(&mut x); + let mut hidden = Self::dnn(&x, self.w1, self.b1); + (crate::generic().tanh_f32)().run(&mut hidden).unwrap(); + let output = Self::dnn(&hidden, self.w2, self.b2); + let ix = output.iter().copied().position_max_by(order_f).unwrap(); + self.kernels[ix] + } + + pub fn pick( + &self, + impls: &[Box], + m: Option, + k: Option, + n: Option, + ) -> Box { + if let (Some(m), Some(k), Some(n)) = (m, k, n) { + let choice = self.predict(m, k, n); + impls.iter().find(|k| k.name() == choice).unwrap().clone() + } else { + impls + .iter() + .find(|k| k.name() == self.big_product_kernel_choice) + .unwrap() + .clone() + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/fuse.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/fuse.rs new file mode 100644 index 0000000..5605fa5 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/fuse.rs @@ -0,0 +1,135 @@ +use std::fmt::Debug; +use std::ops::Deref; + +use crate::BinOp; +use crate::pack::PackedFormat; + +use super::{MMMInputValue, OutputStore, OutputStoreKer}; +use tract_data::internal::*; + +#[repr(usize)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum RoundingPolicy { + Native, + Zero, + Away, + MinusInf, + PlusInf, + Even, + Odd, +} + +#[derive(Clone, Debug)] +pub enum AsInputValue<'t> { + Owned(Box), + Borrowed(&'t dyn MMMInputValue), +} + +impl Deref for AsInputValue<'_> { + type Target = dyn MMMInputValue; + fn deref(&self) -> &Self::Target { + match self { + AsInputValue::Owned(b) => &**b, + AsInputValue::Borrowed(r) => *r, + } + } +} + +#[derive(Clone, Debug)] +pub enum FusedSpec<'t> { + BinScalar(&'t Tensor, BinOp), + BinPerRow(TensorView<'t>, BinOp), + BinPerCol(TensorView<'t>, BinOp), + AddRowColProducts(&'t Tensor, &'t Tensor), + AddUnicast(OutputStore), + LeakyRelu(&'t Tensor), + QScale(isize, RoundingPolicy, i32), + RoundingShiftRight(usize, RoundingPolicy), + ShiftLeft(usize), + Store(OutputStore), + AddMatMul { + a: AsInputValue<'t>, + b: AsInputValue<'t>, + packing: usize, + }, +} + +impl FusedSpec<'_> { + pub fn prefer_col_outer(&self) -> Option { + if let FusedSpec::AddMatMul { a, b, .. } = self { + let a_is_eager = a.format().is::(); + let b_is_eager = b.format().is::(); + if a_is_eager == b_is_eager { + None + } else { + Some(a_is_eager) + } + } else { + None + } + } +} + +// Careful here, the jump_to comments are used by the build script. +#[repr(C, usize)] +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +#[rustfmt::skip] +pub enum FusedKerSpec { + Done, // jump_to:done + Clear, // jump_to:clear + // + LoadTile(*const TI, *const TI), // jump_to:load_tile + + ScalarMin(TI), // jump_to:scalar_min + ScalarMax(TI), // jump_to:scalar_max + ScalarAdd(TI), // jump_to:scalar_add + ScalarMul(TI), // jump_to:scalar_mul + ScalarSub(TI), // jump_to:scalar_sub + ScalarSubF(TI), // jump_to:scalar_sub_flipped + + LeakyRelu(TI), // jump_to:leaky_relu + + PerRowMin(*const TI), // jump_to:per_row_min + PerRowMax(*const TI), // jump_to:per_row_max + PerRowAdd(*const TI), // jump_to:per_row_add + PerRowMul(*const TI), // jump_to:per_row_mul + PerRowSub(*const TI), // jump_to:per_row_sub + PerRowSubF(*const TI), // jump_to:per_row_sub_flipped + + PerColMin(*const TI), // jump_to:per_col_min + PerColMax(*const TI), // jump_to:per_col_max + PerColAdd(*const TI), // jump_to:per_col_add + PerColMul(*const TI), // jump_to:per_col_mul + PerColSub(*const TI), // jump_to:per_col_sub + PerColSubF(*const TI), // jump_to:per_col_sub_flipped + + QScale(isize, RoundingPolicy, i32), // jump_to:q_scale + RoundingShiftRight(usize, RoundingPolicy), // jump_to:q_shr + ShiftLeft(usize), // jump_to:q_shl + AddUnicast(OutputStoreKer), // jump_to:add_unicast + AddRowColProducts(*const TI, *const TI), // jump_to:add_row_col_products + Store(OutputStoreKer), // jump_to:store + + // jump_to:add_mat_mul + AddMatMul { k: usize, pa: *const u8, pb: *const u8, packing: usize }, +} + +unsafe impl Send for FusedKerSpec {} +unsafe impl Sync for FusedKerSpec {} + +#[cfg(test)] +#[test] +fn check_non_linear_enum_size() { + assert_eq!( + std::mem::size_of::(), + std::mem::size_of::() + ); + assert_eq!( + std::mem::size_of::>(), + std::mem::size_of::() + std::mem::size_of::() + ); + assert_eq!( + std::mem::size_of::>(), + 5 * std::mem::size_of::() + ); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/input_store.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/input_store.rs new file mode 100644 index 0000000..b0457ad --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/input_store.rs @@ -0,0 +1,157 @@ +use downcast_rs::{Downcast, impl_downcast}; +use dyn_clone::DynClone; +use dyn_eq::DynEq; +use dyn_hash::DynHash; +use std::alloc::Layout; +use std::fmt::{Debug, Display}; +use std::hash::Hash; +use std::sync::Arc; +use tract_data::internal::*; + +use crate::WeightType; + +pub trait MMMInputFormat: + Downcast + Debug + DynHash + dyn_eq::DynEq + DynClone + Send + Sync + Display +{ + fn prepare_tensor(&self, t: &Tensor, k_axis: usize, mn_axis: usize) -> TractResult; + fn prepare_one( + &self, + t: &Tensor, + k_axis: usize, + mn_axis: usize, + ) -> TractResult>; + fn precursor(&self) -> WeightType; + fn r(&self) -> usize; + fn k_alignment(&self) -> usize; + fn merge_with<'o, 'a: 'o, 'b: 'o>( + &'a self, + other: &'b dyn MMMInputFormat, + ) -> Option<&'o dyn MMMInputFormat> { + if self.dyn_eq(other) { + Some(other) + } else { + None + } + } + fn mem_size(&self, k: TDim, mn: TDim) -> TDim; + fn extract_at_mn_f16( + &self, + data: &EagerPackedInput, + mn: usize, + slice: &mut [f16], + ) -> TractResult<()>; + fn extract_at_mn_f32( + &self, + data: &EagerPackedInput, + mn: usize, + slice: &mut [f32], + ) -> TractResult<()>; +} + +dyn_clone::clone_trait_object!(MMMInputFormat); +impl_downcast!(MMMInputFormat); +dyn_hash::hash_trait_object!(MMMInputFormat); +dyn_eq::eq_trait_object!(MMMInputFormat); + +pub trait MMMInputValue: + DynClone + Debug + DynHash + dyn_eq::DynEq + Send + Sync + Display + Downcast +{ + fn format(&self) -> &dyn MMMInputFormat; + fn scratch_panel_buffer_layout(&self) -> Option; + fn panel_bytes(&self, i: usize, buffer: Option<*mut u8>) -> TractResult<*const u8>; + fn panels_count(&self) -> usize { + self.mn().divceil(self.format().r()) + } + fn mn(&self) -> usize; + fn k(&self) -> usize; + fn exotic_fact(&self) -> &dyn ExoticFact; + + fn extract_at_mn_f16(&self, mn: usize, slice: &mut [f16]) -> TractResult<()>; + fn extract_at_mn_f32(&self, mn: usize, slice: &mut [f32]) -> TractResult<()>; +} +dyn_clone::clone_trait_object!(MMMInputValue); +impl_downcast!(MMMInputValue); +dyn_hash::hash_trait_object!(MMMInputValue); +dyn_eq::eq_trait_object!(MMMInputValue); + +#[allow(clippy::derived_hash_with_manual_eq)] +#[derive(Clone, Hash, Debug)] +pub struct PackedExoticFact { + pub format: Box, + pub mn: TDim, + pub k: usize, +} + +impl Display for PackedExoticFact { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Eager {} tensor (mn={} k={})", + self.format, self.mn, self.k + ) + } +} + +impl ExoticFact for PackedExoticFact { + fn buffer_sizes(&self) -> TVec { + tvec!(self.format.mem_size(self.k.to_dim(), self.mn.clone())) + } +} + +impl PartialEq for PackedExoticFact { + fn eq(&self, other: &Self) -> bool { + self.format == other.format && self.mn == other.mn && self.k == other.k + } +} +impl Eq for PackedExoticFact {} + +#[derive(Clone, Hash, PartialEq, Eq)] +pub struct EagerPackedInput { + pub fact: PackedExoticFact, + pub packed: Arc, + pub panel_bytes: usize, + pub mn: usize, +} + +impl MMMInputValue for EagerPackedInput { + fn scratch_panel_buffer_layout(&self) -> Option { + None + } + fn panel_bytes(&self, i: usize, _buffer: Option<*mut u8>) -> TractResult<*const u8> { + unsafe { Ok(self.packed.as_ptr().add(i * self.panel_bytes)) } + } + fn k(&self) -> usize { + self.fact.k + } + fn mn(&self) -> usize { + self.mn + } + fn format(&self) -> &dyn MMMInputFormat { + &*self.fact.format + } + fn exotic_fact(&self) -> &dyn ExoticFact { + &self.fact + } + fn extract_at_mn_f16(&self, mn: usize, slice: &mut [f16]) -> TractResult<()> { + ensure!(slice.len() == self.k()); + ensure!(mn < self.mn()); + self.fact.format.extract_at_mn_f16(self, mn, slice) + } + fn extract_at_mn_f32(&self, mn: usize, slice: &mut [f32]) -> TractResult<()> { + ensure!(slice.len() == self.k()); + ensure!(mn < self.mn()); + self.fact.format.extract_at_mn_f32(self, mn, slice) + } +} + +impl Display for EagerPackedInput { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + (&self.fact as &dyn Display).fmt(f) + } +} + +impl Debug for EagerPackedInput { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + ::fmt(self, f) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/kernel.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/kernel.rs new file mode 100644 index 0000000..184ca44 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/kernel.rs @@ -0,0 +1,167 @@ +use crate::frame::pack::PackedFormat; + +use super::*; +use std::borrow::Cow; +use std::fmt::Debug; + +use crate::LADatum; + +pub trait MatMatMulKer: Clone + Debug + Send + Sync + 'static { + type Acc: LADatum; + fn name(&self) -> &str; + fn kernel(&self, op: &[FusedKerSpec]) -> isize; + fn mr(&self) -> usize; + fn nr(&self) -> usize; + + fn quality(&self) -> ImplementationQuality; + fn dynamic_boost(&self) -> isize; + + #[allow(clippy::type_complexity)] + fn packings(&self) -> &[(Box, Box)]; + fn stores(&self) -> Cow<'_, [DatumType]>; + + #[allow(unused_variables)] + fn can_fuse(&self, spec: &FusedSpec) -> bool { + true + } + + #[allow(unused_variables)] + fn is_supported_here(&self) -> bool { + true + } +} + +type Kernel = unsafe fn(&[FusedKerSpec]) -> isize; + +#[derive(Clone)] +pub struct DynKernel { + pub name: String, + pub kernel: Kernel, + pub quality: ImplementationQuality, + pub packings: Vec<(Box, Box)>, + pub stores: Vec, + pub supported_predicate: fn() -> bool, + pub boost: fn() -> isize, + pub can_fuse: fn(&FusedSpec) -> bool, +} + +impl DynKernel { + pub fn new( + name: &str, + kernel: Kernel, + packing_a: PackedFormat, + packing_b: PackedFormat, + quality: ImplementationQuality, + ) -> Self { + let kernel = DynKernel { + name: name.to_string(), + kernel, + quality, + packings: vec![], + stores: vec![Acc::datum_type()], + supported_predicate: || true, + boost: || 0, + can_fuse: |_| true, + }; + kernel.with_packing(packing_a, packing_b) + } + + pub fn with_platform_condition(mut self, f: fn() -> bool) -> Self { + self.supported_predicate = f; + self + } + + pub fn with_boost(mut self, f: fn() -> isize) -> Self { + self.boost = f; + self + } + + pub fn with_packing(mut self, a: impl MMMInputFormat, b: impl MMMInputFormat) -> Self { + self.packings.push((Box::new(a), Box::new(b))); + self + } + + pub fn with_packing_a(self, a: impl MMMInputFormat) -> Self { + let b = self.regular_pack_b(); + self.with_packing(a, b) + } + + pub fn regular_pack_a(&self) -> PackedFormat { + *self.packings[0] + .0 + .clone() + .downcast::() + .unwrap() + } + + pub fn regular_pack_b(&self) -> PackedFormat { + *self.packings[0] + .1 + .clone() + .downcast::() + .unwrap() + } + + pub fn with_can_fuse(self, can_fuse: fn(&FusedSpec) -> bool) -> Self { + Self { can_fuse, ..self } + } + + pub fn with_store(mut self) -> Self { + self.stores.push(D::datum_type()); + self + } + + pub fn mmm(&self) -> Box { + Box::new(self.clone()) + } +} + +impl Debug for DynKernel { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name) + } +} + +impl MatMatMulKer for DynKernel { + type Acc = Acc; + fn name(&self) -> &str { + &self.name + } + + fn mr(&self) -> usize { + MR + } + + fn nr(&self) -> usize { + NR + } + + fn quality(&self) -> ImplementationQuality { + self.quality + } + + fn is_supported_here(&self) -> bool { + (self.supported_predicate)() + } + + fn can_fuse(&self, spec: &FusedSpec) -> bool { + (self.can_fuse)(spec) + } + + fn kernel(&self, op: &[FusedKerSpec]) -> isize { + unsafe { (self.kernel)(op) } + } + + #[allow(clippy::type_complexity)] + fn packings(&self) -> &[(Box, Box)] { + &self.packings + } + + fn stores(&self) -> Cow<'_, [DatumType]> { + Cow::Borrowed(&self.stores) + } + + fn dynamic_boost(&self) -> isize { + (self.boost)() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/macros.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/macros.rs new file mode 100644 index 0000000..d500c1c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/macros.rs @@ -0,0 +1,124 @@ +macro_rules! MMMExternKernel { + ( + $func:ident<$ti:ident>($mr: expr, $nr: expr) + $(@($align_a:expr, $align_b:expr))? + $(where($where:expr))? + $(can_fuse($can_fuse:expr))? + $(packing[$pnum:literal] = $pid:ident => $packing:expr;)* + $(quality($quality:expr))? + $(boost($boost:expr))? + $(store($($store:ty),*))? + ) => { + paste! { + mod [] { + #[allow(unused_imports)] + use super::*; + #[allow(unused_imports)] + use crate::frame::mmm::*; + extern_kernel!(fn $func(op: *const FusedKerSpec<$ti>) -> isize); + + #[inline] + pub unsafe fn rusty(op: &[FusedKerSpec<$ti>]) -> isize { + unsafe { $func(op.as_ptr()) } + } + } + + MMMKernel!([]::rusty as $func<$ti>($mr, $nr) + $(@($align_a, $align_b))? + $(where($where))? + $(can_fuse($can_fuse))? + $(packing[$pnum] = $pid => $packing;)* + $(quality($quality))? + $(boost($boost))? + $(store($($store),*))? + ); + } + }; +} +macro_rules! MMMRustKernel { + ( $func: path => + $id:ident<$ti:ident>($mr: expr, $nr: expr) + $(@($align_a:expr, $align_b:expr))? + $(where($where:expr))? + $(can_fuse($can_fuse:expr))? + $(packing[$pnum:literal] = $pid:ident => $packing:expr;)* + $(quality($quality:expr))? + $(store($($store:ty),*))? + ) => { + paste! { + mod [] { + #[allow(unused_imports)] + use crate::frame::mmm::*; + use super::*; + #[inline] + pub unsafe fn rusty(op: &[FusedKerSpec<$ti>]) -> isize { + unsafe { $func(op.as_ptr()) } + } + } + MMMKernel!([]::rusty as $id<$ti>($mr, $nr) + $(@($align_a, $align_b))? + generic(true) + $(where($where))? + $(can_fuse($can_fuse))? + $(packing[$pnum] = $pid => $packing;)* + $(quality($quality))? + $(store($($store),*))? + ); + } + } +} + +macro_rules! MMMKernel { + ( + $func: path as + $id:ident<$ti:ident>($mr: expr, $nr: expr) + $(@($align_a:expr, $align_b:expr))? + $(generic($generic:expr))? + $(where($where:expr))? + $(can_fuse($can_fuse:expr))? + $(packing[$pnum:literal] = $pid:ident => $packing:expr;)* + $(quality($quality:expr))? + $(boost($boost:expr))? + $(store($($store:ty),*))? + ) => { + paste! { + lazy_static::lazy_static! { + pub static ref $id: $crate::mmm::DynKernel<$mr, $nr, $ti> = { + use $crate::mmm::DynKernel; + #[allow(unused_imports)] + use tract_data::prelude::*; + use $crate::pack::Packing; + #[allow(unused_mut)] + let (mut packing_a, mut packing_b) = ($ti::packing($mr), $ti::packing($nr)); + $( + packing_a = packing_a.align($align_a); + packing_b = packing_b.align($align_b); + )? + #[allow(unused_mut)] + let mut k = DynKernel::<$mr, $nr, $ti>::new(stringify!($id), $func, packing_a, packing_b, $crate::frame::mmm::ImplementationQuality::Dreadful); + $(k = k.with_platform_condition($where);)? + $( + assert!(k.packings.len() == $pnum); + let f: fn(DynKernel<$mr, $nr, $ti>) -> DynKernel<$mr, $nr, $ti> = $packing; + k = f(k); + )* + $($( + k.stores.push(<$store>::datum_type()); + )*)? + $(k.can_fuse = $can_fuse;)? + $(k.quality = $quality;)? + $(k = k.with_boost($boost);)? + k + }; + } + + #[cfg(test)] + mod [] { + use super::$id; + test_mmm_kernel!($ti, &*super::$id); + $(mmm_packed_packed_tests!(&*super::$id, $pid : $pnum);)* + $($(mmm_store_test!(&*super::$id, $store);)*)? + } + } + }; +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/mod.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/mod.rs new file mode 100644 index 0000000..8c59e0e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/mod.rs @@ -0,0 +1,634 @@ +#[macro_use] +mod macros; + +pub mod cost_model; +#[macro_use] +pub(crate) mod fuse; +pub(crate) mod input_store; +pub(crate) mod kernel; +#[macro_use] +pub(crate) mod panel_extract; +mod scratch; +mod storage; + +#[cfg(test)] +#[macro_use] +pub mod tests; + +use crate::multithread::Executor; +use std::borrow::Cow; +use std::cmp::Ordering; +use std::fmt::Debug; +use tract_data::internal::*; + +pub use cost_model::*; +pub use fuse::*; +pub use input_store::*; +pub use kernel::*; +pub use panel_extract::*; +pub use scratch::*; +pub use storage::*; + +pub fn no_prefetch(_ptr: *const u8, _len: usize) {} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub enum ImplementationQuality { + /// Individual operations are emulated by individual conversion (f16->f32->f16) + Dreadful, + /// Rust scalar operation (with whatever optimisation the compiler manages) + Generic, + /// Implicit vectorization (e.g. Rust code, some unrolled loops, explicit template instantiations for small constant) + RustOptimized, + /// Explicit vectorization (e.g. intrinsics vector code) + TargetOptimized, + /// Hand optimized (assembly) + ManuallyOptimized, +} + +impl ImplementationQuality { + pub fn best_to_worst() -> &'static [ImplementationQuality] { + use ImplementationQuality::*; + &[ + ManuallyOptimized, + TargetOptimized, + RustOptimized, + Generic, + Dreadful, + ] + } + + pub fn cost(&self) -> usize { + ImplementationQuality::best_to_worst() + .iter() + .position(|x| x == self) + .unwrap() + } +} + +impl PartialOrd for ImplementationQuality { + fn partial_cmp(&self, other: &Self) -> Option { + Some(usize::from(*self).cmp(&usize::from(*other))) + } +} + +impl From for usize { + fn from(value: ImplementationQuality) -> Self { + value.cost() + } +} + +pub trait MatMatMul: Debug + dyn_clone::DynClone + Send + Sync + std::any::Any { + fn name(&self) -> &str; + fn mr(&self) -> usize; + fn nr(&self) -> usize; + + fn quality(&self) -> ImplementationQuality; + fn dynamic_boost(&self) -> isize; + + /// Whether this kernel is runnable on the current CPU (platform feature + /// gate, e.g. FEAT_DotProd for the SDOT i8 kernel). + fn is_supported_here(&self) -> bool; + + #[allow(clippy::type_complexity)] + fn packings(&self) -> &[(Box, Box)]; + + fn internal_type(&self) -> DatumType; + + unsafe fn c_view(&self, m_axis: Option, n_axis: Option) -> OutputStoreSpec; + unsafe fn c_from_data_and_strides( + &self, + item_size: usize, + row_stride: isize, + col_stride: isize, + ) -> OutputStoreSpec; + + fn can_fuse(&self, spec: &FusedSpec) -> bool; + + fn stores(&self) -> Cow<'_, [DatumType]>; + + unsafe fn run(&self, m: usize, n: usize, non_linear: &[FusedSpec]) -> TractResult<()> { + unsafe { + let mut scratch = self.allocate_scratch_space(); + self.run_with_scratch_space(m, n, &mut *scratch, non_linear) + } + } + + unsafe fn allocate_scratch_space(&self) -> Box; + unsafe fn can_use_scratch_space(&self, scratch: &dyn ScratchSpace) -> bool; + unsafe fn run_with_scratch_space( + &self, + m: usize, + n: usize, + scratch: &mut dyn ScratchSpace, + non_linear: &[FusedSpec], + ) -> TractResult<()>; +} + +dyn_clone::clone_trait_object!(MatMatMul); + +impl PartialEq for Box { + fn eq(&self, other: &Box) -> bool { + self.name() == other.name() + } +} +impl Eq for Box {} + +impl std::hash::Hash for Box { + fn hash(&self, state: &mut H) { + self.name().hash(state) + } +} + +impl MatMatMul for K { + fn name(&self) -> &str { + self.name() + } + fn mr(&self) -> usize { + self.mr() + } + fn nr(&self) -> usize { + self.nr() + } + + fn quality(&self) -> ImplementationQuality { + MatMatMulKer::quality(self) + } + + fn dynamic_boost(&self) -> isize { + MatMatMulKer::dynamic_boost(self) + } + + fn is_supported_here(&self) -> bool { + MatMatMulKer::is_supported_here(self) + } + + fn packings(&self) -> &[(Box, Box)] { + self.packings() + } + + fn internal_type(&self) -> DatumType { + K::Acc::datum_type() + } + + fn can_fuse(&self, spec: &FusedSpec) -> bool { + self.can_fuse(spec) + } + + unsafe fn c_view(&self, m_axis: Option, n_axis: Option) -> OutputStoreSpec { + OutputStoreSpec::View { + m_axis, + n_axis, + mr: self.mr(), + nr: self.nr(), + } + } + + unsafe fn c_from_data_and_strides( + &self, + item_size: usize, + row_stride: isize, + col_stride: isize, + ) -> OutputStoreSpec { + OutputStoreSpec::Strides { + row_byte_stride: row_stride * item_size as isize, + col_byte_stride: col_stride * item_size as isize, + mr: self.mr(), + nr: self.nr(), + } + } + + fn stores(&self) -> Cow<'_, [DatumType]> { + self.stores() + } + + unsafe fn allocate_scratch_space(&self) -> Box { + Box::>::default() + } + + unsafe fn can_use_scratch_space(&self, scratch: &dyn ScratchSpace) -> bool { + scratch.downcast_ref::>().is_some() + } + + unsafe fn run_with_scratch_space( + &self, + m: usize, + n: usize, + scratch: &mut dyn ScratchSpace, + non_linear: &[FusedSpec], + ) -> TractResult<()> { + unsafe { + let scratch = scratch + .downcast_mut::>() + .context("Wrong scratch space type")?; + scratch.prepare(self, m, n, non_linear)?; + if n == 1 && self.nr() == 1 { + run_with_scratch_space_vec(self, m, scratch, non_linear) + } else { + let (mut prefer_col, mut prefer_row) = (0, 0); + for uop in non_linear.iter() { + if let Some(col) = uop.prefer_col_outer() { + prefer_col = col as usize; + prefer_row = (!col) as usize; + } + } + // k drives the single-thread cache-block size; read it from the + // first AddMatMul's packed input (0 if none → max block). + let k = non_linear + .iter() + .find_map(|f| match f { + FusedSpec::AddMatMul { a, .. } => Some(a.k()), + _ => None, + }) + .unwrap_or(0); + if prefer_col > prefer_row { + run_with_scratch_space_col_outer(self, m, n, k, scratch, non_linear) + } else { + run_with_scratch_space_row_outer(self, m, n, k, scratch, non_linear) + } + } + } + } +} + +unsafe fn run_with_scratch_space_vec( + ker: &K, + m: usize, + scratch: &mut ScratchSpaceImpl, + non_linear: &[FusedSpec], +) -> TractResult<()> { + unsafe { + match crate::multithread::current_tract_executor() { + Executor::SingleThread => scratch.run_in_tls_scope(|scratch, tls| { + for ia in 0..m.divceil(ker.mr()) { + scratch.run_one_tile(ker, non_linear, tls, ia, 0)?; + } + TractResult::Ok(()) + }), + #[cfg(feature = "multithread-mm")] + Executor::MultiThread(pool) => chunked_dispatch_rayon( + Some(&pool), + m.divceil(ker.mr()), + 1, + |ia_start, ia_end, _, _| { + scratch.run_in_tls_scope(|scratch, tls| { + for ia in ia_start..ia_end { + scratch.run_one_tile(ker, non_linear, tls, ia, 0)?; + } + TractResult::Ok(()) + }) + }, + ), + #[cfg(feature = "multithread-mm")] + Executor::RayonGlobal => { + chunked_dispatch_rayon(None, m.divceil(ker.mr()), 1, |ia_start, ia_end, _, _| { + scratch.run_in_tls_scope(|scratch, tls| { + for ia in ia_start..ia_end { + scratch.run_one_tile(ker, non_linear, tls, ia, 0)?; + } + TractResult::Ok(()) + }) + }) + } + } + } +} + +/// Upper bound on the single-thread panel-block edge (matches the multithread +/// `chunk_grid` default). +const ST_BLK_MAX: usize = 16; + +#[cfg(target_os = "linux")] +fn parse_cache_size(s: &str) -> usize { + let s = s.trim(); + let (num, mult) = if let Some(n) = s.strip_suffix(['K', 'k']) { + (n, 1024) + } else if let Some(n) = s.strip_suffix(['M', 'm']) { + (n, 1024 * 1024) + } else { + (s, 1) + }; + num.trim().parse::().unwrap_or(0) * mult +} + +/// Best-effort L2 data-cache size in bytes (per perf-core / cluster); 0 if +/// unknown. Cached. Used to size the single-thread cache-block budget so it is +/// correct across hardware instead of a hard-coded constant. +fn detect_l2_bytes() -> usize { + static L2: std::sync::OnceLock = std::sync::OnceLock::new(); + *L2.get_or_init(|| { + #[cfg(target_os = "macos")] + { + let sysctl = |k: &str| -> Option { + let o = std::process::Command::new("sysctl") + .arg("-n") + .arg(k) + .output() + .ok()?; + if !o.status.success() { + return None; + } + String::from_utf8_lossy(&o.stdout).trim().parse().ok() + }; + // Prefer the performance-core L2 on hybrid Apple Silicon. + sysctl("hw.perflevel0.l2cachesize") + .or_else(|| sysctl("hw.l2cachesize")) + .unwrap_or(0) + } + #[cfg(target_os = "linux")] + { + // index2/index3 is typically the unified L2 (index0/1 are L1 d/i). + for idx in [2usize, 3] { + if let Ok(s) = std::fs::read_to_string(format!( + "/sys/devices/system/cpu/cpu0/cache/index{idx}/size" + )) { + let b = parse_cache_size(s.trim()); + if b > 0 { + return b; + } + } + } + 0 + } + #[cfg(not(any(target_os = "macos", target_os = "linux")))] + { + 0 + } + }) +} + +/// Working-set budget (bytes) for the single-thread cache-block: ~a third of L2 +/// (leaving room for the C accumulator tile + packing metadata). Conservative +/// 256 KiB fallback when L2 is unknown (WASM/Windows/BSD) ⇒ small blocks ≈ the +/// naive loop, so it can never over-block a cache it can't see. +fn block_budget_bytes() -> usize { + let l2 = detect_l2_bytes(); + if l2 == 0 { + 256 * 1024 + } else { + (l2 / 3).clamp(64 * 1024, 8 * 1024 * 1024) + } +} + +/// Cache-adaptive panel-block edge: large enough to amortise streaming, small +/// enough that the block's A+B sub-panels (`~blk·(mr+nr)·k·elem_bytes`) stay +/// L2-resident at the given `k`. Capped at [`ST_BLK_MAX`]; the floor of 1 +/// degrades exactly to the naive loop, so an unknown/small cache can never +/// over-block (regression-safe). The budget is **cache-size derived** (not a +/// hard-coded constant), so it is correct across hardware. +#[inline] +fn st_block_edge(mr: usize, nr: usize, k: usize, elem_bytes: usize) -> usize { + if k == 0 { + return ST_BLK_MAX; + } + let per_blk = ((mr + nr) * k * elem_bytes.max(1)).max(1); + (block_budget_bytes() / per_blk).clamp(1, ST_BLK_MAX) +} + +/// Single-thread tile walk over the `m_panels × n_panels` grid, blocked into +/// cache-sized panel blocks for locality (the naive nested loop re-streams the +/// whole inner operand per outer panel at large k; the multithread path already +/// blocks this way via `chunk_grid`). `col_outer` selects the within-block inner +/// order (B-reuse vs A-reuse). Reordering independent tiles changes no result — +/// bit-exact with the naive loop. +#[inline] +unsafe fn run_single_thread_blocked( + ker: &K, + m_panels: usize, + n_panels: usize, + k: usize, + col_outer: bool, + scratch: &mut ScratchSpaceImpl, + non_linear: &[FusedSpec], +) -> TractResult<()> { + unsafe { + let blk = st_block_edge(ker.mr(), ker.nr(), k, K::Acc::datum_type().size_of()); + scratch.run_in_tls_scope(|scratch, tls| { + let mut jb = 0; + while jb < n_panels { + let jb_end = (jb + blk).min(n_panels); + let mut ja = 0; + while ja < m_panels { + let ja_end = (ja + blk).min(m_panels); + if col_outer { + for ib in jb..jb_end { + for ia in ja..ja_end { + scratch.run_one_tile(ker, non_linear, tls, ia, ib)?; + } + } + } else { + for ia in ja..ja_end { + for ib in jb..jb_end { + scratch.run_one_tile(ker, non_linear, tls, ia, ib)?; + } + } + } + ja = ja_end; + } + jb = jb_end; + } + TractResult::Ok(()) + }) + } +} + +unsafe fn run_with_scratch_space_col_outer( + ker: &K, + m: usize, + n: usize, + k: usize, + scratch: &mut ScratchSpaceImpl, + non_linear: &[FusedSpec], +) -> TractResult<()> { + unsafe { + match crate::multithread::current_tract_executor() { + Executor::SingleThread => run_single_thread_blocked( + ker, + m.divceil(ker.mr()), + n.divceil(ker.nr()), + k, + true, + scratch, + non_linear, + ), + #[cfg(feature = "multithread-mm")] + Executor::MultiThread(pool) => chunked_dispatch_rayon( + Some(&pool), + m.divceil(ker.mr()), + n.divceil(ker.nr()), + |ia_start, ia_end, ib_start, ib_end| { + scratch.run_in_tls_scope(|scratch, tls| { + for ib in ib_start..ib_end { + for ia in ia_start..ia_end { + scratch.run_one_tile(ker, non_linear, tls, ia, ib)?; + } + } + TractResult::Ok(()) + }) + }, + ), + #[cfg(feature = "multithread-mm")] + Executor::RayonGlobal => chunked_dispatch_rayon( + None, + m.divceil(ker.mr()), + n.divceil(ker.nr()), + |ia_start, ia_end, ib_start, ib_end| { + scratch.run_in_tls_scope(|scratch, tls| { + for ib in ib_start..ib_end { + for ia in ia_start..ia_end { + scratch.run_one_tile(ker, non_linear, tls, ia, ib)?; + } + } + TractResult::Ok(()) + }) + }, + ), + } + } +} + +unsafe fn run_with_scratch_space_row_outer( + ker: &K, + m: usize, + n: usize, + k: usize, + scratch: &mut ScratchSpaceImpl, + non_linear: &[FusedSpec], +) -> TractResult<()> { + unsafe { + match crate::multithread::current_tract_executor() { + Executor::SingleThread => run_single_thread_blocked( + ker, + m.divceil(ker.mr()), + n.divceil(ker.nr()), + k, + false, + scratch, + non_linear, + ), + #[cfg(feature = "multithread-mm")] + Executor::MultiThread(pool) => chunked_dispatch_rayon( + Some(&pool), + m.divceil(ker.mr()), + n.divceil(ker.nr()), + |ia_start, ia_end, ib_start, ib_end| { + scratch.run_in_tls_scope(|scratch, tls| { + for ia in ia_start..ia_end { + for ib in ib_start..ib_end { + scratch.run_one_tile(ker, non_linear, tls, ia, ib)?; + } + } + TractResult::Ok(()) + }) + }, + ), + #[cfg(feature = "multithread-mm")] + Executor::RayonGlobal => chunked_dispatch_rayon( + None, + m.divceil(ker.mr()), + n.divceil(ker.nr()), + |ia_start, ia_end, ib_start, ib_end| { + scratch.run_in_tls_scope(|scratch, tls| { + for ia in ia_start..ia_end { + for ib in ib_start..ib_end { + scratch.run_one_tile(ker, non_linear, tls, ia, ib)?; + } + } + TractResult::Ok(()) + }) + }, + ), + } + } +} + +/// Chunk grid for the 2D dispatch. +/// +/// Mirrors ggml's `mul_mat` heuristic (`ggml/src/ggml-cpu/ggml-cpu.c:1378-1398`): +/// * 16-tile panel chunks by default; +/// * 64-tile chunks when one dimension is 1 (vec / vec-mat); +/// * fallback to "block-per-thread along the longer axis" when the natural +/// grid would have fewer than `4·nth` chunks. +/// +/// Returns `(nchunks_m, nchunks_n, dr_m, dr_n)`. +#[cfg(feature = "multithread-mm")] +fn chunk_grid(n_panels_m: usize, n_panels_n: usize, nth: usize) -> (usize, usize, usize, usize) { + let chunk_size = if n_panels_m == 1 || n_panels_n == 1 { + 64 + } else { + 16 + }; + let mut nchunks_m = n_panels_m.div_ceil(chunk_size); + let mut nchunks_n = n_panels_n.div_ceil(chunk_size); + if nchunks_m * nchunks_n < 4 * nth { + if n_panels_m > n_panels_n { + nchunks_m = nth; + nchunks_n = 1; + } else { + nchunks_m = 1; + nchunks_n = nth; + } + } + let dr_m = n_panels_m.div_ceil(nchunks_m).max(1); + let dr_n = n_panels_n.div_ceil(nchunks_n).max(1); + (nchunks_m, nchunks_n, dr_m, dr_n) +} + +/// 2D chunked dispatcher across the (m_panels × n_panels) grid for the +/// rayon path. Replaces a 1D `into_par_iter` over a single panel axis. +/// Better-utilises threads on small/skewed shapes where one dimension has +/// fewer panels than there are workers. +/// +/// The closure receives **chunk bounds** (`ia_start, ia_end, ib_start, ib_end`), +/// not per-tile indices. This lets the caller amortise per-worker setup +/// (e.g. `ScratchSpaceImpl::run_in_tls_scope`) across all tiles in the +/// chunk, mirroring #2206 for the multi-threaded path. The closure is +/// invoked exactly once per rayon work item (and once total when the +/// small-graph fallback path is taken). +/// +/// `pool`: +/// * `Some(p)` with `p.current_num_threads() > 1` → scoped via `p.install` +/// (native, custom pool path). +/// * `Some(p)` with single-thread pool, or `None` → dispatched via +/// `into_par_iter` directly, which uses rayon's GLOBAL pool. This is +/// the only working path on `wasm32-unknown-unknown` via +/// `wasm_bindgen_rayon::init_thread_pool`. +#[cfg(feature = "multithread-mm")] +unsafe fn chunked_dispatch_rayon( + pool: Option<&rayon::ThreadPool>, + n_panels_m: usize, + n_panels_n: usize, + run_chunk: F, +) -> TractResult<()> +where + F: Fn(usize, usize, usize, usize) -> TractResult<()> + Sync, +{ + use rayon::prelude::*; + if n_panels_m == 0 || n_panels_n == 0 { + return Ok(()); + } + if n_panels_m * n_panels_n < crate::multithread::current_threading_panel_threshold() { + // Below the threading threshold: run the whole grid as a single chunk + // on the calling thread. Closure handles its own TLS scope. + return run_chunk(0, n_panels_m, 0, n_panels_n); + } + let use_global = pool.is_none_or(|p| p.current_num_threads() <= 1); + let body = || { + let nth = rayon::current_num_threads(); + let (nchunks_m, nchunks_n, dr_m, dr_n) = chunk_grid(n_panels_m, n_panels_n, nth); + let total = nchunks_m * nchunks_n; + (0..total).into_par_iter().try_for_each(|idx| { + let im = idx % nchunks_m; + let in_ = idx / nchunks_m; + let ia_start = im * dr_m; + let ia_end = (ia_start + dr_m).min(n_panels_m); + let ib_start = in_ * dr_n; + let ib_end = (ib_start + dr_n).min(n_panels_n); + run_chunk(ia_start, ia_end, ib_start, ib_end) + }) + }; + if use_global { + body() + } else { + pool.unwrap().install(body) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/panel_extract.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/panel_extract.rs new file mode 100644 index 0000000..2a47c7a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/panel_extract.rs @@ -0,0 +1,332 @@ +use std::fmt::{Debug, Display}; +use tract_data::internal::*; + +use super::{EagerPackedInput, MMMInputFormat, MMMInputValue}; +use crate::pack::PackedFormat; + +type Kernel = unsafe fn(input: *const u8, output: *mut u8, k: usize); + +#[allow(clippy::derived_hash_with_manual_eq)] +#[derive(Hash, Clone)] +pub struct PanelExtractor { + pub name: String, + pub from: Box, + pub to: PackedFormat, + pub kernel: Kernel, + pub supported_predicate: fn() -> bool, +} + +impl Debug for PanelExtractor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{} ({:?} -> {:?})", self.name, self.from, self.to) + } +} + +impl Display for PanelExtractor { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.name) + } +} + +impl PartialEq for PanelExtractor { + fn eq(&self, other: &Self) -> bool { + self.name == other.name && *self.from == *other.from && self.to == other.to + } +} +impl Eq for PanelExtractor {} + +impl PanelExtractor { + #[allow(unused_variables)] + pub fn is_supported_here(&self) -> bool { + (self.supported_predicate)() + } +} + +#[derive(Clone, Hash, PartialEq, Eq)] +pub struct PanelExtractInput { + pub format: PanelExtractor, + pub data: EagerPackedInput, +} + +impl MMMInputValue for PanelExtractInput { + fn scratch_panel_buffer_layout(&self) -> Option { + Some( + self.format + .to + .single_panel_layout(self.data.k(), self.format.to.dt.size_of()), + ) + } + fn panel_bytes(&self, i: usize, buffer: Option<*mut u8>) -> TractResult<*const u8> { + let scratch = buffer.unwrap(); + unsafe { + let source = self.data.packed.as_ptr().add(self.data.panel_bytes * i); + (self.format.kernel)(source, scratch, self.data.k()); + } + Ok(scratch) + } + fn mn(&self) -> usize { + self.data.mn() + } + fn k(&self) -> usize { + self.data.k() + } + fn format(&self) -> &dyn MMMInputFormat { + &self.format.to + } + fn exotic_fact(&self) -> &dyn ExoticFact { + self.data.exotic_fact() + } + fn extract_at_mn_f16(&self, mn: usize, slice: &mut [f16]) -> TractResult<()> { + self.data.extract_at_mn_f16(mn, slice) + } + fn extract_at_mn_f32(&self, mn: usize, slice: &mut [f32]) -> TractResult<()> { + self.data.extract_at_mn_f32(mn, slice) + } +} + +impl Display for PanelExtractInput { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "PanelExtract({})", self.data) + } +} + +impl Debug for PanelExtractInput { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "PanelExtract({})", self.data) + } +} + +#[macro_export] +macro_rules! panel_extractor { + ( $func:path as $id:ident($from:expr, $to: expr) + $(where($where:expr))? + ) => { + paste! { + lazy_static::lazy_static! { + pub static ref $id: $crate::mmm::PanelExtractor = { + use $crate::mmm::MMMInputFormat; + let (from, to) = ($from, $to); + assert!(from.r() == to.r()); + #[allow(unused_mut)] + let mut it = $crate::mmm::PanelExtractor { + name: stringify!($id).to_string(), + from, + to, + kernel: $func, + supported_predicate: || true + }; + $( + it.supported_predicate = $where; + )? + it + }; + } + + #[cfg(test)] + mod [] { + use super::$id; + #[test] + fn repack_0block_1panel() { + $crate::frame::mmm::panel_extract::test::test_packing(&$id, 0, 1).unwrap(); + } + + #[test] + fn repack_1block_0panel() { + $crate::frame::mmm::panel_extract::test::test_packing(&$id, 1, 0).unwrap(); + } + + #[test] + fn repack_1block_1panel() { + $crate::frame::mmm::panel_extract::test::test_packing(&$id, 1, 1).unwrap(); + } + + #[test] + fn repack_2block_1panel() { + $crate::frame::mmm::panel_extract::test::test_packing(&$id, 2, 1).unwrap(); + } + + #[test] + fn repack_1block_2panel() { + $crate::frame::mmm::panel_extract::test::test_packing(&$id, 1, 2).unwrap(); + } + + #[test] + fn repack_2block_2panel() { + $crate::frame::mmm::panel_extract::test::test_packing(&$id, 2, 2).unwrap(); + } + } + } + }; +} + +#[cfg(test)] +pub mod test { + use crate::frame::block_quant::PackedBlockQuantFormat; + use crate::mmm::PackedMatrixStorage; + use tract_data::internal::*; + use tract_ndarray::Array2; + + use super::*; + + pub fn test_packing( + extractor: &PanelExtractor, + blocks: usize, + panels: usize, + ) -> TractResult<()> { + if !extractor.is_supported_here() { + return Ok(()); + } + assert!(extractor.from.r() == extractor.to.r()); + assert!(extractor.to.dt == f32::datum_type() || extractor.to.dt == f16::datum_type()); + if let Some(from) = extractor.from.downcast_ref::() { + test_packing_bq(extractor, from, blocks, panels) + } else if let Some(from) = extractor.from.downcast_ref() { + test_packing_plain(extractor, from, blocks, panels) + } else { + todo!() + } + } + + pub fn test_packing_plain( + extractor: &PanelExtractor, + from: &PackedFormat, + blocks: usize, + panels: usize, + ) -> TractResult<()> { + let m = from.r * panels; + let k = 8 * blocks; // 8 is arbitrary + let to = &extractor.to; + let weights_orig = + Array2::from_shape_fn((m, k), |(m, k)| ((m * 31 + k * 17) % 20) as f32 - 10.) + .into_tensor() + .cast_to_dt(from.dt)? + .into_owned(); + let packed_orig = from.prepare_tensor(&weights_orig, 1, 0)?; + let packed_orig_storage = packed_orig.try_storage_as::()?; + let packed_orig = packed_orig_storage + .value() + .downcast_ref::() + .unwrap(); + + for panel in 0..panels { + let orig_panel = &packed_orig.packed[packed_orig.panel_bytes * panel..] + [..k * from.r * from.dt.size_of()]; + let mut reference_panel = Tensor::zero_dt(from.dt, &[k, from.r])?; + reference_panel.as_bytes_mut().copy_from_slice(orig_panel); + reference_panel = reference_panel.cast_to_dt(to.dt)?.into_owned(); + + let mut tested_panel = Tensor::zero_dt(to.dt, &[k, from.r])?; + unsafe { + (extractor.kernel)( + orig_panel.as_ptr(), + tested_panel.as_bytes_mut().as_mut_ptr(), + k, + ); + } + compare_panels(&tested_panel, &reference_panel, from.r, k); + } + Ok(()) + } + + pub fn test_packing_bq( + extractor: &PanelExtractor, + from: &PackedBlockQuantFormat, + blocks: usize, + panels: usize, + ) -> TractResult<()> { + let m = from.r * panels; + let k = from.bq.block_len() * blocks; + let to = &extractor.to; + let weights_orig = + Array2::from_shape_fn((m, k), |(m, k)| ((m * 31 + k * 17) % 20) as f32 - 10.) + .into_tensor() + .cast_to_dt(to.dt)? + .into_owned(); + let weights = if to.dt == f32::datum_type() { + from.bq + .dequant_f32( + &from + .bq + .quant_f32(weights_orig.try_as_plain()?.as_slice::()?)?, + )? + .into_shape(&[m, k])? + } else { + from.bq + .dequant_f16( + &from + .bq + .quant_f16(weights_orig.try_as_plain()?.as_slice::()?)?, + )? + .into_shape(&[m, k])? + }; + let block_quant = if to.dt == f32::datum_type() { + from.bq + .quant_f32(weights.try_as_plain()?.as_slice::()?)? + } else { + from.bq + .quant_f16(weights.try_as_plain()?.as_slice::()?)? + }; + let packed_block_quant = + from.bq + .pack(&block_quant, k, from.r, from.zip, from.scales_at_end)?; + + let mut reference_panel = Tensor::zero_dt(to.dt, &[k, from.r])?; + let mut tested_panel = Tensor::zero_dt(to.dt, &[k, from.r])?; + + for panel in 0..packed_block_quant.panels_count() { + unsafe { + from.bq.extract_packed_panel( + &packed_block_quant, + to, + panel, + reference_panel.as_bytes_mut().as_mut_ptr(), + )?; + + let source = packed_block_quant + .packed + .as_ptr() + .add(packed_block_quant.panel_bytes * panel); + (extractor.kernel)(source, tested_panel.as_bytes_mut().as_mut_ptr(), k); + } + compare_panels(&tested_panel, &reference_panel, from.r, k); + } + Ok(()) + } + + fn compare_panels(tested_panel: &Tensor, reference_panel: &Tensor, r: usize, k: usize) { + if tested_panel != reference_panel { + if reference_panel.datum_type() == f32::datum_type() { + crate::frame::mmm::tests::display_error( + tested_panel + .try_as_plain() + .unwrap() + .as_slice::() + .unwrap(), + reference_panel + .try_as_plain() + .unwrap() + .as_slice::() + .unwrap(), + r, + k, + ); + } else { + crate::frame::mmm::tests::display_error( + tested_panel + .try_as_plain() + .unwrap() + .as_slice::() + .unwrap(), + reference_panel + .try_as_plain() + .unwrap() + .as_slice::() + .unwrap(), + r, + k, + ); + } + } + assert_eq!(tested_panel, reference_panel); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/scratch.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/scratch.rs new file mode 100644 index 0000000..b75ec2a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/scratch.rs @@ -0,0 +1,590 @@ +use super::{FusedKerSpec, FusedSpec, MatMatMulKer, OutputStoreKer}; +use crate::{BinOp, LADatum}; +use downcast_rs::{Downcast, impl_downcast}; +use std::cell::RefCell; +use std::fmt::Debug; +use std::sync::atomic::AtomicUsize; +use tract_data::internal::num_integer::Integer; +use tract_data::internal::*; + +static GENERATION: AtomicUsize = AtomicUsize::new(1); + +thread_local! { + static TLS: RefCell = Default::default(); +} + +#[derive(Default, Debug)] +pub(crate) struct TLSScratch { + generation: usize, + blob: Blob, + ker_specs_16: Vec>, + ker_specs_32: Vec>, + ker_specs_64: Vec>, +} + +impl TLSScratch { + #[allow(unknown_lints, clippy::missing_transmute_annotations)] + fn ker_specs(&mut self) -> &mut Vec> { + unsafe { + if TI::datum_type() == f32::datum_type() || TI::datum_type() == i32::datum_type() { + std::mem::transmute(&mut self.ker_specs_32) + } else if TI::datum_type() == f16::datum_type() { + std::mem::transmute(&mut self.ker_specs_16) + } else if TI::datum_type() == f64::datum_type() { + std::mem::transmute(&mut self.ker_specs_64) + } else { + todo!(); + } + } + } + + fn sync(&mut self, scratch: &ScratchSpaceImpl) { + if self.generation == scratch.generation { + return; + } + let ker_specs = self.ker_specs::(); + ker_specs.clear(); + ker_specs.extend_from_slice(&scratch.ker_specs); + + unsafe { + self.blob + .ensure_size_and_align(scratch.blob_size, scratch.blob_align); + + for LocDependant { loc, ker_spec, .. } in &scratch.loc_dependant { + #[allow(clippy::single_match)] + if matches!(scratch.ker_specs[*ker_spec], FusedKerSpec::AddMatMul { .. }) { + let scratch = &mut *(self.blob.as_ptr().add(*loc) as *mut AddMatMulTemp); + scratch.panel_a_id = usize::MAX; + scratch.panel_b_id = usize::MAX; + }; + } + } + self.generation = scratch.generation; + } +} + +pub trait ScratchSpace: Downcast + Send {} +impl_downcast!(ScratchSpace); + +#[derive(Debug, Default)] +pub struct ScratchSpaceImpl { + generation: usize, + blob_size: usize, + blob_align: usize, + ker_specs: Vec>, + loc_dependant: TVec, + valid_down_tiles: usize, + remnant_down: usize, + valid_right_tiles: usize, + remnant_right: usize, +} + +#[derive(Debug, new)] +struct LocDependant { + spec: usize, + ker_spec: usize, + // offset for the location dependant structure + loc: usize, + // offset of its associated dynamic-size buffers + buffer_a: Option, + buffer_b: Option, +} + +impl ScratchSpace for ScratchSpaceImpl {} +unsafe impl Send for ScratchSpaceImpl {} + +#[derive(Debug)] +struct AddMatMulTemp { + ptr_a: *const u8, + panel_a_id: usize, + ptr_b: *const u8, + panel_b_id: usize, +} + +impl ScratchSpaceImpl { + pub unsafe fn prepare( + &mut self, + ker: &impl MatMatMulKer, + m: usize, + n: usize, + specs: &[FusedSpec], + ) -> TractResult<()> { + use FusedKerSpec as FKS; + use FusedSpec as FS; + self.ker_specs.clear(); + self.loc_dependant.clear(); + self.ker_specs.reserve(specs.len() + 2); + self.ker_specs.push(FusedKerSpec::Clear); + self.valid_down_tiles = m / ker.mr(); + self.remnant_down = m % ker.mr(); + self.valid_right_tiles = n / ker.nr(); + self.remnant_right = n % ker.nr(); + let mut offset = 0; + let mut align = std::mem::size_of::<*const ()>(); + fn ld(spec: usize, uspec: usize, loc: usize) -> LocDependant { + LocDependant { + spec, + ker_spec: uspec, + loc, + buffer_a: None, + buffer_b: None, + } + } + for (ix, spec) in specs.iter().enumerate() { + offset = offset.next_multiple_of(&align); + let ker_spec = match spec { + FS::BinScalar(t, op) => match op { + BinOp::Min => FKS::ScalarMin(*t.try_as_plain()?.to_scalar()?), + BinOp::Max => FKS::ScalarMax(*t.try_as_plain()?.to_scalar()?), + BinOp::Mul => FKS::ScalarMul(*t.try_as_plain()?.to_scalar()?), + BinOp::Add => FKS::ScalarAdd(*t.try_as_plain()?.to_scalar()?), + BinOp::Sub => FKS::ScalarSub(*t.try_as_plain()?.to_scalar()?), + BinOp::SubF => FKS::ScalarSubF(*t.try_as_plain()?.to_scalar()?), + }, + FS::ShiftLeft(s) => FKS::ShiftLeft(*s), + FS::RoundingShiftRight(s, rp) => FKS::RoundingShiftRight(*s, *rp), + FS::QScale(s, rp, m) => FKS::QScale(*s, *rp, *m), + FS::BinPerRow(_, _) => { + self.loc_dependant + .push(ld(ix, self.ker_specs.len(), offset)); + offset += TI::datum_type().size_of() * ker.mr(); + FusedKerSpec::Done + } + FS::BinPerCol(_, _) => { + self.loc_dependant + .push(ld(ix, self.ker_specs.len(), offset)); + offset += TI::datum_type().size_of() * ker.nr(); + FusedKerSpec::Done + } + FS::AddRowColProducts(_, _) => { + self.loc_dependant + .push(ld(ix, self.ker_specs.len(), offset)); + offset += TI::datum_type().size_of() * (ker.mr() + ker.nr()); + FusedKerSpec::Done + } + FS::AddUnicast(_) => { + self.loc_dependant + .push(ld(ix, self.ker_specs.len(), offset)); + offset += TI::datum_type().size_of() * ker.mr() * ker.nr(); + FusedKerSpec::Done + } + FS::Store(store) => { + self.loc_dependant + .push(ld(ix, self.ker_specs.len(), offset)); + offset += store.item_size * ker.mr() * ker.nr(); + FusedKerSpec::Done + } + FS::LeakyRelu(t) => FKS::LeakyRelu(*t.try_as_plain()?.to_scalar()?), + FS::AddMatMul { a, b, packing } => { + let mut ld = ld(ix, self.ker_specs.len(), offset); + offset += std::mem::size_of::(); + if let Some(tmp) = a.scratch_panel_buffer_layout() { + align = tmp.align().lcm(&align); + offset = Integer::next_multiple_of(&offset, &tmp.align()); + ld.buffer_a = Some(offset); + offset += tmp.size(); + } + if let Some(tmp) = b.scratch_panel_buffer_layout() { + align = tmp.align().lcm(&align); + offset = Integer::next_multiple_of(&offset, &tmp.align()); + ld.buffer_b = Some(offset); + offset += tmp.size(); + } + self.loc_dependant.push(ld); + FusedKerSpec::AddMatMul { + k: 0, + pa: std::ptr::null(), + pb: std::ptr::null(), + packing: *packing, + } + } + }; + self.ker_specs.push(ker_spec); + } + self.ker_specs.push(FKS::Done); + self.blob_size = offset; + self.blob_align = align; + + self.generation = GENERATION.fetch_add(1, std::sync::atomic::Ordering::Relaxed); + Ok(()) + } + + pub unsafe fn run( + &self, + ker: &impl MatMatMulKer, + specs: &[FusedSpec], + down: usize, + right: usize, + ) -> TractResult<()> { + // Per-tile entry: enter the TLS scope (does sync once) then run a single + // tile. Single-threaded callers should prefer `run_in_tls_scope`+ + // `run_one_tile` to amortise the TLS borrow + sync over many tiles. + unsafe { + self.run_in_tls_scope(|this, tls| this.run_one_tile(ker, specs, tls, down, right)) + } + } + + /// Borrow the per-thread scratch blob for a single MMM call and `sync` it + /// once. The closure is invoked once with a mutable reference to the TLS + /// scratch and to `self`. Used by single-threaded matmul drivers to avoid + /// re-entering TLS / re-running `sync` per tile. + pub(crate) unsafe fn run_in_tls_scope(&self, f: F) -> R + where + F: FnOnce(&Self, &mut TLSScratch) -> R, + { + TLS.with_borrow_mut(|tls| { + tls.sync(self); + f(self, tls) + }) + } + + /// Run a single tile against an already-borrowed TLS scratch. Caller is + /// responsible for entering `run_in_tls_scope` first (so `sync` has run). + #[inline(always)] + pub(crate) unsafe fn run_one_tile( + &self, + ker: &impl MatMatMulKer, + specs: &[FusedSpec], + tls: &mut TLSScratch, + down: usize, + right: usize, + ) -> TractResult<()> { + unsafe { + if down < self.valid_down_tiles && right < self.valid_right_tiles { + self.for_valid_tile(ker, specs, tls, down, right)?; + let err = ker.kernel(tls.ker_specs()); + debug_assert_eq!(err, 0, "Kernel return error {err}"); + } else { + let remnant_down = if down < self.valid_down_tiles { + ker.mr() + } else { + self.remnant_down + }; + let remnant_right = if right < self.valid_right_tiles { + ker.nr() + } else { + self.remnant_right + }; + self.for_border_tile(ker, specs, tls, down, right, remnant_down, remnant_right)?; + let err = ker.kernel(tls.ker_specs()); + debug_assert_eq!(err, 0, "Kernel return error {err}"); + self.postprocess_tile(specs, tls, down, right, remnant_down, remnant_right)?; + } + Ok(()) + } + } + + #[inline(always)] + unsafe fn for_valid_tile( + &self, + ker: &impl MatMatMulKer, + specs: &[FusedSpec], + tls: &mut TLSScratch, + down: usize, + right: usize, + ) -> TractResult<()> { + unsafe { + use FusedKerSpec as FKS; + use FusedSpec as FS; + let ScratchSpaceImpl { + ker_specs, + loc_dependant, + .. + } = self; + debug_assert!(specs.len() + 2 == ker_specs.len()); + for LocDependant { + spec, + ker_spec, + loc, + buffer_a, + buffer_b, + } in loc_dependant + { + let spec = specs.get_unchecked(*spec); + let it = match spec { + FS::BinPerRow(v, op) => { + let v = v.as_ptr_unchecked::().add(down * ker.mr()); + match op { + BinOp::Min => FKS::PerRowMin(v), + BinOp::Max => FKS::PerRowMax(v), + BinOp::Add => FKS::PerRowAdd(v), + BinOp::Mul => FKS::PerRowMul(v), + BinOp::Sub => FKS::PerRowSub(v), + BinOp::SubF => FKS::PerRowSubF(v), + } + } + FS::BinPerCol(v, op) => { + let v = v.as_ptr_unchecked::().add(right * ker.nr()); + match op { + BinOp::Min => FKS::PerColMin(v), + BinOp::Max => FKS::PerColMax(v), + BinOp::Add => FKS::PerColAdd(v), + BinOp::Mul => FKS::PerColMul(v), + BinOp::Sub => FKS::PerColSub(v), + BinOp::SubF => FKS::PerColSubF(v), + } + } + FS::AddRowColProducts(rows, cols) => { + let row_ptr = rows.as_ptr_unchecked::().add(down * ker.mr()); + let col_ptr = cols.as_ptr_unchecked::().add(right * ker.nr()); + FKS::AddRowColProducts(row_ptr, col_ptr) + } + FS::AddUnicast(store) => FKS::AddUnicast(store.tile_c(down, right)), + FS::Store(c_store) => FKS::Store(c_store.tile_c(down, right)), + FS::AddMatMul { a, b, packing } => { + let scratch = (tls.blob.as_mut_ptr().add(*loc) as *mut AddMatMulTemp) + .as_mut() + .unwrap(); + if scratch.panel_a_id != down { + scratch.ptr_a = a.panel_bytes( + down, + buffer_a.map(|o| tls.blob.as_mut_ptr().add(o)), + )?; + scratch.panel_a_id = down; + } + if scratch.panel_b_id != right { + scratch.ptr_b = b.panel_bytes( + right, + buffer_b.map(|o| tls.blob.as_mut_ptr().add(o)), + )?; + scratch.panel_b_id = right; + } + FKS::AddMatMul { + k: b.k(), + pa: scratch.ptr_a, + pb: scratch.ptr_b, + packing: *packing, + } + } + _ => std::hint::unreachable_unchecked(), + }; + *tls.ker_specs().get_unchecked_mut(*ker_spec) = it; + } + Ok(()) + } + } + + #[inline(never)] + #[allow(clippy::too_many_arguments)] + unsafe fn for_border_tile( + &self, + ker: &impl MatMatMulKer, + specs: &[FusedSpec], + tls: &mut TLSScratch, + down: usize, + right: usize, + m_remnant: usize, + n_remnant: usize, + ) -> TractResult<()> { + unsafe { + use FusedKerSpec as FKS; + use FusedSpec as FS; + for LocDependant { + spec, + ker_spec: uspec, + loc, + buffer_a, + buffer_b, + } in &self.loc_dependant + { + let loc = tls.blob.as_mut_ptr().add(*loc); + let spec = specs.get_unchecked(*spec); + let it = match spec { + FS::BinPerRow(v, op) => { + let buf = std::slice::from_raw_parts_mut(loc as *mut TI, ker.mr()); + let ptr = if m_remnant < ker.mr() { + if m_remnant > 0 { + buf.get_unchecked_mut(..m_remnant).copy_from_slice( + v.as_slice_unchecked() + .get_unchecked(down * ker.mr()..) + .get_unchecked(..m_remnant), + ); + } + if cfg!(debug_assertions) { + buf.get_unchecked_mut(m_remnant..) + .iter_mut() + .for_each(|x| *x = TI::zero()); + } + buf.as_ptr() + } else { + v.as_ptr_unchecked::().add(down * ker.mr()) + }; + match op { + BinOp::Min => FKS::PerRowMin(ptr), + BinOp::Max => FKS::PerRowMax(ptr), + BinOp::Add => FKS::PerRowAdd(ptr), + BinOp::Mul => FKS::PerRowMul(ptr), + BinOp::Sub => FKS::PerRowSub(ptr), + BinOp::SubF => FKS::PerRowSubF(ptr), + } + } + FS::BinPerCol(v, op) => { + let buf = std::slice::from_raw_parts_mut(loc as *mut TI, ker.nr()); + let ptr = if n_remnant < ker.nr() { + if n_remnant > 0 { + buf.get_unchecked_mut(..n_remnant).copy_from_slice( + v.as_slice_unchecked() + .get_unchecked(right * ker.nr()..) + .get_unchecked(..n_remnant), + ); + } + if cfg!(debug_assertions) { + buf.get_unchecked_mut(n_remnant..) + .iter_mut() + .for_each(|x| *x = TI::zero()); + } + buf.as_ptr() + } else { + v.as_ptr_unchecked::().add(right * ker.nr()) + }; + match op { + BinOp::Min => FKS::PerColMin(ptr), + BinOp::Max => FKS::PerColMax(ptr), + BinOp::Add => FKS::PerColAdd(ptr), + BinOp::Mul => FKS::PerColMul(ptr), + BinOp::Sub => FKS::PerColSub(ptr), + BinOp::SubF => FKS::PerColSubF(ptr), + } + } + FS::AddRowColProducts(rows, cols) => { + let r = std::slice::from_raw_parts_mut(loc as *mut TI, ker.mr()); + let row_ptr = if m_remnant < ker.mr() { + r.get_unchecked_mut(..m_remnant).copy_from_slice( + rows.as_slice_unchecked() + .get_unchecked(down * ker.mr()..) + .get_unchecked(..m_remnant), + ); + if cfg!(debug_assertions) { + r.get_unchecked_mut(m_remnant..) + .iter_mut() + .for_each(|x| *x = TI::zero()); + } + r.as_ptr() + } else { + rows.as_ptr_unchecked::().add(down * ker.mr()) + }; + let c = std::slice::from_raw_parts_mut( + (loc as *mut TI).add(ker.mr()), + ker.nr(), + ); + let col_ptr = if n_remnant < ker.nr() { + c.get_unchecked_mut(..n_remnant).copy_from_slice( + cols.as_slice_unchecked() + .get_unchecked(right * ker.nr()..) + .get_unchecked(..n_remnant), + ); + if cfg!(debug_assertions) { + r.get_unchecked_mut(n_remnant..) + .iter_mut() + .for_each(|x| *x = TI::zero()); + } + c.as_ptr() + } else { + cols.as_ptr_unchecked::().add(right * ker.nr()) + }; + FKS::AddRowColProducts(row_ptr, col_ptr) + } + FS::AddUnicast(store) => { + let row_byte_stride = store.row_byte_stride; + let col_byte_stride = store.col_byte_stride; + let tile_offset = row_byte_stride * down as isize * ker.mr() as isize + + col_byte_stride * right as isize * ker.nr() as isize; + let tile_ptr = store.ptr.offset(tile_offset); + let tmp_d_tile = + std::slice::from_raw_parts_mut(loc as *mut TI, ker.mr() * ker.nr()); + if cfg!(debug_assertions) { + tmp_d_tile.iter_mut().for_each(|t| *t = TI::zero()); + } + for r in 0..m_remnant as isize { + for c in 0..n_remnant as isize { + let inner_offset = c * col_byte_stride + r * row_byte_stride; + if inner_offset + tile_offset + < (store.item_size * store.item_count) as isize + { + *tmp_d_tile + .get_unchecked_mut(r as usize + c as usize * ker.mr()) = + *(tile_ptr.offset(inner_offset) as *const TI); + } + } + } + FKS::AddUnicast(OutputStoreKer { + ptr: tmp_d_tile.as_ptr() as _, + row_byte_stride: std::mem::size_of::() as isize, + col_byte_stride: (std::mem::size_of::() * ker.mr()) as isize, + item_size: std::mem::size_of::(), + }) + } + FS::Store(c_store) => { + let tmpc = OutputStoreKer { + ptr: loc as _, + item_size: c_store.item_size, + row_byte_stride: c_store.item_size as isize, + col_byte_stride: (c_store.item_size * ker.mr()) as isize, + }; + FKS::Store(tmpc) + } + FS::AddMatMul { a, b, packing } => { + let scratch = (loc as *mut AddMatMulTemp).as_mut().unwrap(); + if scratch.panel_a_id != down { + scratch.ptr_a = a.panel_bytes( + down, + buffer_a.map(|o| tls.blob.as_mut_ptr().add(o)), + )?; + scratch.panel_a_id = down; + } + if scratch.panel_b_id != right { + scratch.ptr_b = b.panel_bytes( + right, + buffer_b.map(|o| tls.blob.as_mut_ptr().add(o)), + )?; + scratch.panel_b_id = right; + } + FKS::AddMatMul { + k: b.k(), + pa: scratch.ptr_a, + pb: scratch.ptr_b, + packing: *packing, + } + } + _ => std::hint::unreachable_unchecked(), + }; + *tls.ker_specs().get_unchecked_mut(*uspec) = it; + } + Ok(()) + } + } + + #[inline] + pub fn uspecs(&self) -> &[FusedKerSpec] { + &self.ker_specs + } + + unsafe fn postprocess_tile( + &self, + specs: &[FusedSpec], + tls: &mut TLSScratch, + down: usize, + right: usize, + m_remnant: usize, + n_remnant: usize, + ) -> TractResult<()> + where + TI: LADatum, + { + unsafe { + for LocDependant { + spec, + ker_spec: uspec, + .. + } in self.loc_dependant.iter() + { + let spec = specs.get_unchecked(*spec); + let ker_spec = tls.ker_specs::().get_unchecked(*uspec); + if let (FusedSpec::Store(c_store), FusedKerSpec::Store(tmp)) = (spec, ker_spec) { + c_store.set_from_tile(down, right, m_remnant, n_remnant, tmp) + } + } + Ok(()) + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/storage.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/storage.rs new file mode 100644 index 0000000..21d4cd0 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/storage.rs @@ -0,0 +1,326 @@ +use std::fmt; +use std::fmt::Debug; +use tract_data::internal::*; + +use super::MMMInputValue; + +/// Non-plain tensor storage for packed matrices. +/// +/// Holds one or more `Box` values with an optional batch +/// shape, replacing the previous `Tensor` + double-downcast pattern. +#[derive(Clone, PartialEq, Eq)] +pub struct PackedMatrixStorage { + values: Vec>, + batch_shape: TVec, + batch_strides: TVec, +} + +impl PackedMatrixStorage { + /// Scalar storage (one value, empty shape). + pub fn new(value: Box) -> Self { + PackedMatrixStorage { + values: vec![value], + batch_shape: tvec![], + batch_strides: tvec![], + } + } + + /// Batched storage (shape like `[batch, group]`). + pub fn new_batched(shape: &[usize], values: Vec>) -> Self { + let expected: usize = shape.iter().product(); + assert_eq!( + values.len(), + expected, + "values length must match shape product" + ); + let strides = Self::compute_strides(shape); + PackedMatrixStorage { + values, + batch_shape: shape.into(), + batch_strides: strides, + } + } + + fn compute_strides(shape: &[usize]) -> TVec { + let mut strides: TVec = tvec![0; shape.len()]; + if !shape.is_empty() { + strides[shape.len() - 1] = 1; + for i in (0..shape.len() - 1).rev() { + strides[i] = strides[i + 1] * shape[i + 1] as isize; + } + } + strides + } + + /// Scalar access (asserts single value). + #[inline] + pub fn value(&self) -> &dyn MMMInputValue { + debug_assert_eq!(self.values.len(), 1); + &*self.values[0] + } + + /// Batched access by coordinates. + pub fn value_at(&self, coords: &[usize]) -> &dyn MMMInputValue { + let idx = self.flat_index(coords); + &*self.values[idx] + } + + /// Batched access by flat (pre-computed) index. + #[inline] + pub fn value_at_flat(&self, idx: usize) -> &dyn MMMInputValue { + &*self.values[idx] + } + + pub fn values(&self) -> &[Box] { + &self.values + } + + pub fn batch_shape(&self) -> &[usize] { + &self.batch_shape + } + + pub fn batch_strides(&self) -> &[isize] { + &self.batch_strides + } + + /// Convert to a Tensor with the given logical datum type. + pub fn into_tensor(self, dt: DatumType) -> Tensor { + let shape: TVec = self.batch_shape.clone(); + Tensor::from_storage(dt, &shape, self) + } + + fn flat_index(&self, coords: &[usize]) -> usize { + coords + .iter() + .zip(self.batch_strides.iter()) + .map(|(c, s)| *c as isize * s) + .sum::() as usize + } +} + +impl fmt::Debug for PackedMatrixStorage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "PackedMatrixStorage({} values, shape={:?})", + self.values.len(), + self.batch_shape + ) + } +} + +impl fmt::Display for PackedMatrixStorage { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "PackedMatrixStorage({} values, shape={:?})", + self.values.len(), + self.batch_shape + ) + } +} + +impl TensorStorage for PackedMatrixStorage { + fn byte_len(&self) -> usize { + // Approximate: sum of individual value sizes isn't precise but gives a ballpark + self.values.len() * std::mem::size_of::>() + } + + fn is_empty(&self) -> bool { + self.values.is_empty() + } + + fn deep_clone(&self) -> Box { + Box::new(self.clone()) + } + + fn as_plain(&self) -> Option<&PlainStorage> { + None + } + + fn as_plain_mut(&mut self) -> Option<&mut PlainStorage> { + None + } + + fn into_plain(self: Box) -> Option { + None + } + + fn dyn_hash(&self, state: &mut dyn std::hash::Hasher) { + for v in &self.values { + v.dyn_hash(state); + } + } + + fn exotic_fact(&self, _shape: &[usize]) -> TractResult>> { + if self.values.len() == 1 { + Ok(Some(dyn_clone::clone_box(self.values[0].exotic_fact()))) + } else { + let facts: TVec> = self + .values + .iter() + .map(|v| dyn_clone::clone_box(v.exotic_fact())) + .collect(); + Ok(Some(Box::new(facts))) + } + } +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub enum OutputStoreSpec { + View { + m_axis: Option, + n_axis: Option, + mr: usize, + nr: usize, + }, + Strides { + row_byte_stride: isize, + col_byte_stride: isize, + mr: usize, + nr: usize, + }, +} + +#[derive(Clone, Copy, Debug)] +pub struct OutputStore { + pub(crate) ptr: *mut u8, + pub(crate) row_byte_stride: isize, + pub(crate) col_byte_stride: isize, + pub(crate) panel_row_byte_stride: isize, + pub(crate) panel_col_byte_stride: isize, + pub(crate) item_size: usize, + pub(crate) item_count: usize, + pub(crate) mr: usize, +} + +unsafe impl Send for OutputStore {} +unsafe impl Sync for OutputStore {} + +impl OutputStoreSpec { + #[inline] + pub unsafe fn wrap(&self, tensor: &TensorView) -> OutputStore { + let (mr, nr, row_byte_stride, col_byte_stride) = unsafe { self.compute_strides(tensor) }; + OutputStore { + ptr: unsafe { tensor.as_ptr_unchecked::() } as _, + row_byte_stride, + col_byte_stride, + panel_row_byte_stride: row_byte_stride * mr as isize, + panel_col_byte_stride: col_byte_stride * nr as isize, + item_size: tensor.datum_type().size_of(), + mr, + item_count: tensor.len(), + } + } + + #[inline] + unsafe fn compute_strides(&self, tensor: &TensorView) -> (usize, usize, isize, isize) { + let size_of = tensor.datum_type().size_of() as isize; + match self { + OutputStoreSpec::View { + m_axis, + n_axis, + mr, + nr, + .. + } => { + let tensor_strides = tensor.strides(); + let row_item_stride = m_axis + .map(|ax| *unsafe { tensor_strides.get_unchecked(ax) }) + .unwrap_or(0); + let col_item_stride = n_axis + .map(|ax| *unsafe { tensor_strides.get_unchecked(ax) }) + .unwrap_or(0); + let row_byte_stride = row_item_stride * size_of; + let col_byte_stride = col_item_stride * size_of; + (*mr, *nr, row_byte_stride, col_byte_stride) + } + OutputStoreSpec::Strides { + row_byte_stride, + col_byte_stride, + mr, + nr, + .. + } => (*mr, *nr, *row_byte_stride, *col_byte_stride), + } + } +} + +impl OutputStore { + #[inline] + pub(super) unsafe fn tile_c(&self, down: usize, right: usize) -> OutputStoreKer { + unsafe { + let (down, right) = (down as isize, right as isize); + OutputStoreKer { + ptr: self + .ptr + .offset(self.panel_row_byte_stride * down + self.panel_col_byte_stride * right) + as *mut _, + row_byte_stride: self.row_byte_stride, + col_byte_stride: self.col_byte_stride, + item_size: self.item_size, + } + } + } + + #[inline] + pub fn item_size(&self) -> usize { + self.item_size + } + + #[inline] + pub(super) unsafe fn set_from_tile( + &self, + down: usize, + right: usize, + height: usize, + width: usize, + tile: &OutputStoreKer, + ) { + unsafe { + if self.item_size() == 1 { + self.set_from_tile_t::(down, right, height, width, tile) + } else if self.item_size() == 2 { + self.set_from_tile_t::(down, right, height, width, tile) + } else if self.item_size() == 4 { + self.set_from_tile_t::(down, right, height, width, tile) + } else { + self.set_from_tile_t::(down, right, height, width, tile) + } + } + } + + #[inline] + unsafe fn set_from_tile_t( + &self, + down: usize, + right: usize, + height: usize, + width: usize, + tile: &OutputStoreKer, + ) { + unsafe { + let tile = tile.ptr as *mut T; + let dst = self.ptr.add( + self.panel_row_byte_stride as usize * down + + self.panel_col_byte_stride as usize * right, + ); + for y in 0..height as isize { + for x in 0..width as isize { + let value = tile.offset(y + x * self.mr as isize); + let dst = dst.offset(y * self.row_byte_stride + x * self.col_byte_stride); + *(dst as *mut T) = *value; + } + } + } + } +} + +#[repr(C)] +#[derive(PartialEq, Eq, Copy, Clone, Debug)] +pub struct OutputStoreKer { + pub ptr: *mut u8, + pub row_byte_stride: isize, + pub col_byte_stride: isize, + pub item_size: usize, +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/tests/frame.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/tests/frame.rs new file mode 100644 index 0000000..9bb048a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/tests/frame.rs @@ -0,0 +1,304 @@ +use crate::frame::mmm::*; +use crate::{BinOp, LADatum}; +use num_traits::AsPrimitive; +use std::ops::Neg; +use tests::display_error; +use tract_data::internal::*; + +#[macro_export] +macro_rules! mmm_frame_tests { + ($ker:expr, $ta:ty, $tb:ty, $tc:ty, $ti:ty) => { + mod frame { + use tract_data::internal::*; + #[allow(unused_imports)] + use $crate::frame::mmm::tests::frame::*; + + #[test] + fn row_mul_2_1_3() -> TractResult<()> { + unsafe { row_mul::<_, $ta, $tb, $tc, $ti>($ker, 2, 3)? } + Ok(()) + } + + #[test] + fn row_add_2_1_3() -> TractResult<()> { + unsafe { row_add::<_, $ta, $tb, $tc, $ti>($ker, 2, 3)? } + Ok(()) + } + + #[test] + fn col_mul_2_1_3() -> TractResult<()> { + unsafe { col_mul::<_, $ta, $tb, $tc, $ti>($ker, 2, 3)? } + Ok(()) + } + + #[test] + fn col_add_2_1_3() -> TractResult<()> { + unsafe { col_add::<_, $ta, $tb, $tc, $ti>($ker, 2, 3)? } + Ok(()) + } + + #[test] + fn max_2_1_3() -> TractResult<()> { + unsafe { max::<_, $ta, $tb, $tc, $ti>($ker, 2, 3)? } + Ok(()) + } + + #[test] + fn min_2_1_3() -> TractResult<()> { + unsafe { min::<_, $ta, $tb, $tc, $ti>($ker, 2, 3)? } + Ok(()) + } + + #[test] + fn add_d_2_1_3() -> TractResult<()> { + unsafe { add_d::<_, $ta, $tb, $tc, $ti>($ker, 2, 3)? } + Ok(()) + } + + #[test] + fn add_d_big() -> TractResult<()> { + unsafe { add_d::<_, $ta, $tb, $tc, $ti>($ker, 197, 1)? } + Ok(()) + } + } + }; +} + +pub unsafe fn fused_ops< + K: MatMatMulKer + 'static, + TA, + TB, + TC, + TI, + F: Fn(usize, usize) -> TC, +>( + ker: &K, + m: usize, + n: usize, + spec: &[FusedSpec], + expect: F, +) -> TractResult<()> +where + TA: LADatum + AsPrimitive + 'static, + TB: LADatum + AsPrimitive + 'static, + TC: LADatum + AsPrimitive + 'static, + TI: LADatum + AsPrimitive + 'static, + i32: AsPrimitive, + usize: AsPrimitive, +{ + if !ker.is_supported_here() { + return Ok(()); + }; + crate::setup_test_logger(); + + let mut found = Tensor::zero::(&[m, n])?; + let c_store = unsafe { + ker.c_from_data_and_strides(TC::datum_type().size_of(), n as isize, 1) + .wrap(&found.view_mut()) + }; + let mut spec: TVec = spec.into(); + spec.push(FusedSpec::Store(c_store)); + + unsafe { ker.run(m, n, &spec) }?; + let expected = + tract_ndarray::prelude::Array2::from_shape_fn((m, n), |(r, c)| expect(r, c)).into_tensor(); + let err = found.close_enough(&expected, true); + if err.is_err() { + display_error( + found.try_as_plain()?.as_slice::()?, + expected.try_as_plain()?.as_slice::()?, + m, + n, + ); + } + err +} + +pub unsafe fn row_add + 'static, TA, TB, TC, TI>( + ker: &K, + m: usize, + n: usize, +) -> TractResult<()> +where + TA: LADatum + AsPrimitive + 'static, + TB: LADatum + AsPrimitive + 'static, + TC: LADatum + AsPrimitive + 'static, + TI: LADatum + AsPrimitive + 'static + Neg, + i32: AsPrimitive, + usize: AsPrimitive, +{ + let bias = (0..m).map(|i| i.as_()).collect::>(); + unsafe { + fused_ops::( + ker, + m, + n, + &[FusedSpec::BinPerRow(tensor1(&bias).view(), BinOp::Add)], + |r, _| bias[r].as_(), + ) + } +} + +pub unsafe fn row_mul + 'static, TA, TB, TC, TI>( + ker: &K, + m: usize, + n: usize, +) -> TractResult<()> +where + TA: LADatum + AsPrimitive + 'static, + TB: LADatum + AsPrimitive + 'static, + TC: LADatum + AsPrimitive + 'static, + TI: LADatum + AsPrimitive + 'static + Neg, + i32: AsPrimitive, + usize: AsPrimitive, +{ + let bias = (0..m).map(|i| i.as_()).collect::>(); + unsafe { + fused_ops::( + ker, + m, + n, + &[ + FusedSpec::BinScalar(&tensor0(1i32.as_()), BinOp::Add), + FusedSpec::BinPerRow(tensor1(&bias).view(), BinOp::Mul), + ], + |r, _| bias[r].as_(), + ) + } +} + +pub unsafe fn col_add + 'static, TA, TB, TC, TI>( + ker: &K, + m: usize, + n: usize, +) -> TractResult<()> +where + TA: LADatum + AsPrimitive + 'static, + TB: LADatum + AsPrimitive + 'static, + TC: LADatum + AsPrimitive + 'static, + TI: LADatum + AsPrimitive + 'static + Neg, + i32: AsPrimitive, + usize: AsPrimitive, +{ + let bias = (0..n).map(|i| i.as_()).collect::>(); + unsafe { + fused_ops::( + ker, + m, + n, + &[FusedSpec::BinPerCol(tensor1(&bias).view(), BinOp::Add)], + |_, c| bias[c].as_(), + ) + } +} + +pub unsafe fn col_mul + 'static, TA, TB, TC, TI>( + ker: &K, + m: usize, + n: usize, +) -> TractResult<()> +where + TA: LADatum + AsPrimitive + 'static, + TB: LADatum + AsPrimitive + 'static, + TC: LADatum + AsPrimitive + 'static, + TI: LADatum + AsPrimitive + 'static + Neg, + i32: AsPrimitive, + usize: AsPrimitive, +{ + let bias = (0..n).map(|i| i.as_()).collect::>(); + unsafe { + fused_ops::( + ker, + m, + n, + &[ + FusedSpec::BinScalar(&tensor0(1i32.as_()), BinOp::Add), + FusedSpec::BinPerCol(tensor1(&bias).view(), BinOp::Mul), + ], + |_, c| bias[c].as_(), + ) + } +} + +pub unsafe fn add_d + 'static, TA, TB, TC, TI>( + ker: &K, + m: usize, + n: usize, +) -> TractResult<()> +where + TA: LADatum + AsPrimitive + 'static, + TB: LADatum + AsPrimitive + 'static, + TC: LADatum + AsPrimitive + 'static, + TI: LADatum + AsPrimitive + 'static + Neg, + i32: AsPrimitive, + usize: AsPrimitive, +{ + let d = (0..m * n).map(|i| i.as_()).collect::>(); + let d = tensor1(&d).into_shape(&[m, n])?; + let store_spec = OutputStoreSpec::View { + m_axis: Some(0), + n_axis: Some(1), + mr: ker.mr(), + nr: ker.nr(), + }; + let view_d = d.to_plain_array_view::()?.into_dimensionality()?; + unsafe { + fused_ops::( + ker, + m, + n, + &[FusedSpec::AddUnicast(store_spec.wrap(&d.view()))], + |r, c| view_d[(r, c)].as_(), + ) + } +} + +pub unsafe fn max, TA, TB, TC, TI>( + ker: &K, + m: usize, + n: usize, +) -> TractResult<()> +where + TA: LADatum + AsPrimitive + 'static, + TB: LADatum + AsPrimitive + 'static, + TC: LADatum + AsPrimitive + 'static, + TI: LADatum + AsPrimitive + 'static + Neg, + i32: AsPrimitive, + usize: AsPrimitive, +{ + let five: TI = 5.as_(); + unsafe { + fused_ops::( + ker, + m, + n, + &[FusedSpec::BinScalar(&tensor0(five), BinOp::Max)], + |_, _| five.as_(), + ) + } +} + +pub unsafe fn min, TA, TB, TC, TI>( + ker: &K, + m: usize, + n: usize, +) -> TractResult<()> +where + TA: LADatum + AsPrimitive + 'static, + TB: LADatum + AsPrimitive + 'static, + TC: LADatum + AsPrimitive + 'static, + TI: LADatum + AsPrimitive + 'static + Neg, + i32: AsPrimitive, + usize: AsPrimitive, +{ + let five: TI = 5.as_(); + unsafe { + fused_ops::( + ker, + m, + n, + &[FusedSpec::BinScalar(&tensor0(five), BinOp::Min)], + |_, _| TC::zero(), + ) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/tests/fuse.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/tests/fuse.rs new file mode 100644 index 0000000..a6708d3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/tests/fuse.rs @@ -0,0 +1,351 @@ +use crate::frame::mmm::fuse::FusedKerSpec; +use crate::frame::mmm::storage::*; +use crate::frame::mmm::tests::display_error; +use crate::frame::mmm::tests::store::mmm_stride_storage; +use crate::frame::mmm::*; +use num_traits::{AsPrimitive, Bounded}; +use proptest::prelude::*; +use tract_data::internal::*; + +#[macro_export] +macro_rules! mmm_kernel_fuse_tests { + ($ker:expr, $tc:ty, $ti: ty) => { + mod fuse { + use num_traits::Zero; + #[allow(unused_imports)] + use tract_data::prelude::f16; + use tract_data::prelude::tensor0; + use $crate::frame::mmm::MatMatMulKer; + use $crate::frame::mmm::tests::fuse as test; + #[allow(unused_imports)] + use $crate::frame::mmm::tests::fuse::*; + + #[test] + fn return_zeros() { + test::return_zeros::<_, $tc, $ti>($ker) + } + + #[test] + fn store_non_contiguous() { + test::store_non_contiguous::<_, $tc, $ti>($ker) + } + + #[test] + fn add_unicast_non_contiguous() { + test::add_unicast_non_contiguous::<_, $ti>($ker) + } + proptest::proptest! { + #[test] + fn return_c_prop(c in tile::<_, $ti>($ker)) { + test::return_c::<_, $ti>($ker, &c) + } + } + + fn fmin(a: T, b: T) -> T { + if a < b { a } else { b } + } + + fn fmax(a: T, b: T) -> T { + if a > b { a } else { b } + } + + macro_rules! bin { + ($FKS:ident, $geo:expr, $f:expr, $extra_cond:expr) => { + paste! { + #[test] + fn [<$FKS:snake>]() { + if ($ker).is_supported_here() && $extra_cond { + test::$geo::<_, $ti>($ker, $crate::mmm::FusedKerSpec::$FKS, $f); + } + } + } + }; + } + + bin!(PerColMin, per_col, fmin, true); + bin!(PerColMax, per_col, fmax, true); + bin!(PerColAdd, per_col, |a, b| a + b, true); + bin!(PerColMul, per_col, |a, b| a * b, true); + bin!(PerColSub, per_col, |a, b| a - b, true); + bin!(PerColSubF, per_col, |a, b| b - a, true); + + bin!(PerRowMin, per_row, fmin, true); + bin!(PerRowMax, per_row, fmax, true); + bin!(PerRowAdd, per_row, |a, b| a + b, true); + bin!(PerRowMul, per_row, |a, b| a * b, true); + bin!(PerRowSub, per_row, |a, b| a - b, true); + bin!(PerRowSubF, per_row, |a, b| b - a, true); + + bin!(ScalarMin, scalar, fmin, true); + bin!(ScalarMax, scalar, fmax, true); + bin!(ScalarAdd, scalar, |a, b| a + b, true); + bin!(ScalarMul, scalar, |a, b| a * b, true); + bin!(ScalarSub, scalar, |a, b| a - b, true); + bin!(ScalarSubF, scalar, |a, b| b - a, true); + + bin!( + LeakyRelu, + scalar, + |a, b| if b > <$ti>::zero() { b } else { a * b }, + ($ker).can_fuse(&$crate::mmm::FusedSpec::LeakyRelu(&tensor0(<$ti>::from( + 1_u8 + )))) + ); + + #[test] + fn return_c_add_row_col_product() { + test::return_c_add_row_col_product::<_, $ti>($ker) + } + + #[test] + fn return_c_plus_d() { + test::return_c_plus_d::<_, $ti, $ti>($ker) + } + + #[test] + fn return_c_clear() { + test::return_c_clear::<_, $ti>($ker) + } + } + }; +} + +use crate::LADatum; +pub fn return_zeros(ker: &K) +where + K: MatMatMulKer, + TC: LADatum, + TI: LADatum + Bounded + PartialEq, +{ + if !ker.is_supported_here() { + return; + } + let v = vec![TC::max_value(); ker.mr() * ker.nr()]; + let c = mmm_stride_storage(&v, ker.nr()); + let non_linear = tvec![ + FusedKerSpec::Clear, + FusedKerSpec::Store(c), + FusedKerSpec::Done + ]; + let err = ker.kernel(&non_linear); + assert_eq!(err, 0); + let expected = vec![TC::zero(); v.len()]; + display_error(&v, &expected, ker.mr(), ker.nr()); + assert_eq!(v, expected); +} + +pub fn store_non_contiguous(ker: &K) +where + K: MatMatMulKer, + TC: LADatum, + TI: LADatum + Bounded + PartialEq, +{ + if !ker.is_supported_here() { + return; + } + let v = vec![TC::max_value(); ker.mr() * 5 * ker.nr() * 3]; + let c = OutputStoreKer { + ptr: v.as_ptr() as _, + row_byte_stride: (std::mem::size_of::() * 3 * ker.nr() * 5) as isize, + col_byte_stride: std::mem::size_of::() as isize * 3, + item_size: std::mem::size_of::(), + }; + let non_linear = tvec![ + FusedKerSpec::Clear, + FusedKerSpec::Store(c), + FusedKerSpec::Done + ]; + let err = ker.kernel(&non_linear); + assert_eq!(err, 0); + let mut expected = vec![TC::max_value(); v.len()]; + for c in 0..ker.nr() { + for r in 0..ker.mr() { + expected[c * 3 + r * 3 * 5 * ker.nr()] = TC::zero(); + } + } + assert_eq!(v, expected); +} + +/// `Clear` + `AddUnicast(strided)` + `Store(contiguous)` and check the +/// source pattern reaches the destination. Counterpart of +/// `store_non_contiguous` on the read side; `return_c_plus_d` uses +/// `mmm_stride_storage` (tightly packed) and so doesn't exercise this. +pub fn add_unicast_non_contiguous(ker: &K) +where + K: MatMatMulKer, + TI: LADatum + AsPrimitive, + usize: AsPrimitive, +{ + if !ker.is_supported_here() { + return; + } + let item = std::mem::size_of::(); + let row_stride_items = 3 * ker.nr() * 5; + let col_stride_items = 3; + // Source: a non-contiguous buffer with distinct values at the used + // (r, c) cells and sentinel garbage everywhere else. + let mut src: Vec = vec![TI::max_value(); ker.mr() * row_stride_items]; + for r in 0..ker.mr() { + for c in 0..ker.nr() { + src[r * row_stride_items + c * col_stride_items] = (1 + c + r * ker.nr()).as_(); + } + } + let src_store = OutputStoreKer { + ptr: src.as_ptr() as _, + row_byte_stride: (item * row_stride_items) as isize, + col_byte_stride: (item * col_stride_items) as isize, + item_size: item, + }; + // Destination: tightly-packed output for easy comparison. + let mut dst: Vec = vec![TI::min_value(); ker.mr() * ker.nr()]; + let dst_store = OutputStoreKer { + ptr: dst.as_ptr() as _, + row_byte_stride: (item * ker.nr()) as isize, + col_byte_stride: item as isize, + item_size: item, + }; + let non_linear = tvec![ + FusedKerSpec::Clear, + FusedKerSpec::AddUnicast(src_store), + FusedKerSpec::Store(dst_store), + FusedKerSpec::Done, + ]; + let err = ker.kernel(&non_linear); + assert_eq!(err, 0); + let expected: Vec = (0..ker.mr() * ker.nr()).map(|i| (1 + i).as_()).collect(); + display_error(&dst, &expected, ker.mr(), ker.nr()); + assert_eq!(dst, expected); +} + +pub fn fused_ops(ker: &K, c: &[TI], ops: &[FusedKerSpec], expect: E) +where + K: MatMatMulKer, + TI: LADatum, + E: Fn(usize, usize, TI) -> TI, +{ + if !ker.is_supported_here() { + return; + } + assert!(c.len() == ker.mr() * ker.nr()); + let v = c.to_vec(); + let c = mmm_stride_storage(&v, ker.nr()); + let mut ops = ops.to_vec(); + ops.insert(0, FusedKerSpec::AddUnicast(c)); + ops.insert(0, FusedKerSpec::Clear); + ops.push(FusedKerSpec::Store(c)); + ops.push(FusedKerSpec::Done); + let expected = (0..v.len()) + .map(|ix| expect(ix / ker.nr(), ix % ker.nr(), v[ix])) + .collect::>(); + let err = ker.kernel(&ops); + assert_eq!(err, 0); + display_error(&v, &expected, ker.mr(), ker.nr()); + assert_eq!(v, expected); +} + +pub fn return_c(ker: &K, v: &[TI]) +where + K: MatMatMulKer, + TI: LADatum, + usize: AsPrimitive, +{ + fused_ops::(ker, v, &[], |_, _, c| c + 1.as_() - 1.as_()) +} + +pub fn return_c_plus_d(ker: &K) +where + K: MatMatMulKer, + TI: LADatum, + TD: LADatum + AsPrimitive, + usize: AsPrimitive + AsPrimitive, +{ + let len = ker.mr() * ker.nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + let d: Vec = (0..len).map(|f| ((3 * f) % 7).as_()).collect(); + fused_ops::( + ker, + &v, + &[FusedKerSpec::AddUnicast(mmm_stride_storage(&d, ker.nr()))], + |row, col, c| c + d[row * ker.nr() + col].as_(), + ); +} + +pub fn per_col(ker: &K, op: impl Fn(*const TI) -> FusedKerSpec, f: impl Fn(TI, TI) -> TI) +where + K: MatMatMulKer, + TI: LADatum, + usize: AsPrimitive, +{ + let len = ker.mr() * ker.nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + let bias: Vec = (0..ker.nr()).map(|f| (f + 1).as_()).collect(); + fused_ops::(ker, &v, &[op(bias.as_ptr())], |_, col, c| f(bias[col], c)) +} + +pub fn per_row(ker: &K, op: impl Fn(*const TI) -> FusedKerSpec, f: impl Fn(TI, TI) -> TI) +where + K: MatMatMulKer, + TI: LADatum, + usize: AsPrimitive, +{ + let len = ker.mr() * ker.nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + let bias: Vec = (0..ker.mr()).map(|f| (f + 1).as_()).collect(); + fused_ops::(ker, &v, &[op(bias.as_ptr())], |row, _, c| f(bias[row], c)) +} + +pub fn scalar(ker: &K, op: impl Fn(TI) -> FusedKerSpec, f: impl Fn(TI, TI) -> TI) +where + K: MatMatMulKer, + TI: LADatum, + isize: AsPrimitive, +{ + let len = ker.mr() * ker.nr(); + let v: Vec = (0..len as isize) + .map(|f| (f - len as isize / 2).as_()) + .collect(); + let five: TI = 5.as_(); + fused_ops::(ker, &v, &[op(five)], |_, _, c| f(five, c)) +} + +pub fn return_c_add_row_col_product(ker: &K) +where + K: MatMatMulKer, + TI: LADatum, + usize: AsPrimitive, +{ + let len = ker.mr() * ker.nr(); + let v: Vec = (0..len).map(|f| (f + 1).as_()).collect(); + let rows: Vec = (0..ker.mr()).map(|f| (f + 3).as_()).collect(); + let cols: Vec = (0..ker.nr()).map(|f| (f + 2).as_()).collect(); + fused_ops::( + ker, + &v, + &[FusedKerSpec::AddRowColProducts( + rows.as_ptr(), + cols.as_ptr(), + )], + |row, col, c| c + cols[col] * rows[row], + ) +} + +pub fn return_c_clear(ker: &K) +where + K: MatMatMulKer, + TI: LADatum, + usize: AsPrimitive, +{ + let len = ker.mr() * ker.nr(); + let v: Vec = (0..len).map(|f| f.as_()).collect(); + fused_ops::(ker, &v, &[FusedKerSpec::Clear], |_, _, _| 0.as_()) +} + +pub fn tile(ker: &K) -> BoxedStrategy> +where + K: MatMatMulKer, + TI: LADatum, + i8: AsPrimitive, +{ + let len = ker.mr() * ker.nr(); + proptest::collection::vec(any::().prop_map(|c| c.as_()), len..=len).boxed() +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/tests/mod.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/tests/mod.rs new file mode 100644 index 0000000..beb4fb2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/tests/mod.rs @@ -0,0 +1,89 @@ +use crate::LADatum; + +#[macro_use] +pub mod fuse; +#[macro_use] +pub mod frame; +#[macro_use] +pub mod packed_packed; +#[macro_use] +pub mod q_scale; +#[macro_use] +pub mod store; + +#[cfg(test)] +macro_rules! test_mmm_kernel { + (f16, $ker:expr) => { + test_mmm_kernel_f16!($ker); + }; + (f32, $ker:expr) => { + test_mmm_kernel_f32!($ker); + }; + (f64, $ker:expr) => { + test_mmm_kernel_f64!($ker); + }; + (i32, $ker:expr) => { + test_mmm_kernel_i32!($ker); + }; +} + +#[macro_export] +macro_rules! test_mmm_kernel_f16 { + ($ker: expr) => { + mmm_packed_packed_tests!(&*$ker, f16f16:0); + mmm_frame_tests!(&*$ker, f16, f16, f16, f16); + mmm_kernel_fuse_tests!(&*$ker, f16, f16); + mmm_store_test!(&*$ker, f16); + }; +} + +#[macro_export] +macro_rules! test_mmm_kernel_f32 { + ($ker: expr) => { + mmm_packed_packed_tests!(&*$ker, f32f32:0); + mmm_frame_tests!(&*$ker, f32, f32, f32, f32); + mmm_kernel_fuse_tests!(&*$ker, f32, f32); + mmm_store_test!(&*$ker, f32); + }; +} + +#[macro_export] +macro_rules! test_mmm_kernel_f64 { + ($ker:expr) => { + mmm_packed_packed_tests!(&*$ker, f64f64:0); + mmm_frame_tests!(&*$ker, f64, f64, f64, f64); + mmm_kernel_fuse_tests!(&*$ker, f64, f64); + mmm_store_test!(&*$ker, f64); + }; +} + +#[macro_export] +macro_rules! test_mmm_kernel_i32 { + ($ker: expr) => { + mmm_packed_packed_tests!(&*$ker, i32i32:0); + mmm_kernel_fuse_tests!(&*$ker, i32, i32); + mmm_frame_tests!(&*$ker, i32, i32, i32, i32); + mmm_q_scale_tests!(&*$ker); + mmm_store_test!(&*$ker, i32); + }; +} + +pub fn display_error(v: &[TC], expected: &[TC], m: usize, n: usize) { + if v != expected { + for ixm in 0..m { + print!("|"); + for ixn in 0..n { + use nu_ansi_term::Color::*; + let f = v[ixm * n + ixn]; + let e = expected[ixm * n + ixn]; + let color = if f != e { Red.bold() } else { Green.into() }; + print!("{}|", color.paint(format!("{f:5}"))); + } + print!(" # "); + for ixn in 0..n { + print!("{:5} ", expected[ixm * n + ixn]); + } + println!(); + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/tests/packed_packed.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/tests/packed_packed.rs new file mode 100644 index 0000000..5f4f7fb --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/tests/packed_packed.rs @@ -0,0 +1,442 @@ +use crate::WeightType; +use crate::block_quant::PackedBlockQuantFormat; +use crate::mmm::tests::display_error; +use crate::mmm::{AsInputValue, FusedKerSpec, FusedSpec, MatMatMul, MatMatMulKer, OutputStoreKer}; +use proptest::collection::vec; +use proptest::prelude::*; +use std::fmt::Debug; +use tract_data::internal::*; + +#[macro_export] +macro_rules! mmm_packed_packed_tests { + ($ker:expr, $packing_id:ident : $packing: expr) => { + mod $packing_id { + use super::*; + #[allow(unused_imports)] + use proptest::prelude::*; + #[allow(unused_imports)] + use tract_data::prelude::f16; + use tract_data::prelude::*; + use tract_itertools::Itertools; + use $crate::frame::mmm::kernel::MatMatMulKer; + #[allow(unused_imports)] + use $crate::frame::mmm::tests::packed_packed::*; + + mod fuse { + use super::*; + + proptest::proptest! { + #[test] + fn prop(pb in arbitrary_problem(false, $ker, $packing)) { + pb.check().unwrap() + } + } + + fn t(a: impl Into>, b: impl Into>) -> TractResult<()> { + PackedPackedProblem::kernel($ker, $packing, a, b).check() + } + + #[test] + fn packed_packed_1() -> TractResult<()> { + t(vec![1f32; $ker.mr()], vec![1f32; $ker.nr()]) + } + + #[test] + fn packed_packed_2() -> TractResult<()> { + t(vec![1f32; $ker.mr() * 2], vec![1f32; $ker.nr() * 2]) + } + + #[test] + fn packed_packed_13() -> TractResult<()> { + t(vec![1f32; $ker.mr() * 13], vec![1f32; $ker.nr() * 13]) + } + + #[test] + fn packed_packed_a_scale() -> TractResult<()> { + t( + (1..=$ker.mr() as i64).map(|x| x as f32).collect_vec(), + vec![1f32; $ker.nr()], + ) + } + + #[test] + fn packed_packed_a_scale_times_2() -> TractResult<()> { + t( + (1..=2 * $ker.mr() as i64).map(|x| x as f32).collect_vec(), + vec![1f32; $ker.nr() * 2], + ) + } + + #[test] + fn packed_packed_empty() -> TractResult<()> { + t(vec![0f32; 0], vec![0f32; 0]) + } + + #[test] + fn packed_packed_bug_1() -> TractResult<()> { + t(vec![0f32; $ker.mr()], vec![0f32; $ker.nr()]) + } + + #[test] + fn packed_packed_bug_2() -> TractResult<()> { + let mut a = vec![0f32; $ker.mr()]; + a[0] = 1.; + let mut b = vec![0f32; $ker.nr()]; + b[0] = 1.; + t(a, b) + } + + #[test] + fn packed_packed_bug_3() -> TractResult<()> { + if $ker.mr() >= 4 { + let mut a = vec![0f32; 2 * $ker.mr()]; + let mut b = vec![0f32; 2 * $ker.nr()]; + a[2] = -0.7548828f32; + a[3] = 0.23547363f32; + b[2 * $ker.nr() - 1] = 0.93603516; + t(a, b)?; + } + Ok(()) + } + + #[test] + fn packed_packed_bug_4() -> TractResult<()> { + if $ker.mr() > 16 { + let mut a = vec![0f32; $ker.mr()]; + let mut b = vec![0f32; $ker.nr()]; + a[16] = 1.; + b[0] = 1.; + t(a, b)?; + } + Ok(()) + } + } + + mod frame { + use super::*; + + proptest::proptest! { + #[test] + fn prop(pb in arbitrary_problem(true, $ker, $packing)) { + pb.check().unwrap() + } + } + + fn t( + m: usize, + n: usize, + a: impl Into>, + b: impl Into>, + ) -> TractResult<()> { + PackedPackedProblem::frame($ker, $packing, m, n, a, b).check() + } + + fn ti( + m: usize, + n: usize, + a: impl Into>, + b: impl Into>, + ) -> TractResult<()> { + let a = a.into().into_iter().map(|i| i as f32).collect_vec(); + let b = b.into().into_iter().map(|i| i as f32).collect_vec(); + t(m, n, a, b) + } + + #[test] + fn trivial_1x2() -> TractResult<()> { + ti(1, 2, [0], [0, 0]) + } + + #[test] + fn packed_packed_empty() -> TractResult<()> { + t($ker.mr(), $ker.nr(), [], []) + } + + #[test] + fn packed_packed_empty_2() -> TractResult<()> { + t(2 * $ker.mr(), 2 * $ker.nr(), [], []) + } + + #[test] + fn mat_mul_1() -> TractResult<()> { + ti( + 3, + 2, + [-3, 3, 5, -5, 6, 0, -6, -5, 0, 0, 9, 7], + [-8, 5, 5, -3, 5, 7, -8, -1], + ) + } + + #[test] + fn mat_mul_2() -> TractResult<()> { + ti(1, 3, [122, 82], [0, 0, 37, 0, 0, 57]) + } + } + } + }; +} + +#[derive(Debug, new)] +pub struct PackedPackedProblem +where + K: MatMatMulKer, +{ + pub frame_test: Option<(usize, usize)>, + pub ker: K, + pub packing: usize, + pub a: Vec, + pub b: Vec, +} + +pub fn arbitrary_problem( + frame_test: bool, + ker: &K, + packing: usize, +) -> BoxedStrategy> { + let (mr, nr) = (ker.mr(), ker.nr()); + let item_range = if ker.internal_type().is_integer() { + (-5f32)..5f32 + } else { + (-1f32)..1f32 + }; + let (m_range, n_range) = if frame_test { + (1usize..3 * mr, 1usize..3 * nr) + } else { + (mr..mr + 1, nr..nr + 1) + }; + let ker = ker.clone(); + (m_range, 0usize..40, n_range) + .prop_flat_map(move |(m, k, n)| { + ( + vec(item_range.clone(), k * m..=k * m), + vec(item_range.clone(), k * n..=k * n), + Just((m, n)), + ) + }) + .prop_map(move |(mut a, mut b, mn)| { + a.reverse(); + b.reverse(); + PackedPackedProblem { + frame_test: Some(mn).filter(|_| frame_test), + ker: ker.clone(), + packing, + a, + b, + } + }) + .boxed() +} + +impl PackedPackedProblem { + pub fn kernel( + ker: &K, + packing: usize, + a: impl Into>, + b: impl Into>, + ) -> PackedPackedProblem { + PackedPackedProblem { + frame_test: None, + ker: ker.clone(), + packing, + a: a.into(), + b: b.into(), + } + } + + pub fn frame( + ker: &K, + packing: usize, + m: usize, + n: usize, + a: impl Into>, + b: impl Into>, + ) -> PackedPackedProblem { + PackedPackedProblem { + frame_test: Some((m, n)), + ker: ker.clone(), + packing, + a: a.into(), + b: b.into(), + } + } + + pub fn mkn(&self) -> (usize, usize, usize) { + let (m, n) = self.frame_test.unwrap_or((self.ker.mr(), self.ker.nr())); + assert!(m != 0 && n != 0); + let k = self.a.len() / m; + assert_eq!(self.b.len() / n, k); + (m, k, n) + } + + pub fn padded_inputs(&self) -> TractResult<(Tensor, Tensor)> { + let (pack_a, pack_b) = &self.ker.packings()[self.packing]; + let (m, k, n) = self.mkn(); + let k_aligned = k.next_multiple_of(pack_a.k_alignment().max(pack_b.k_alignment())); + + let mut a = Tensor::zero::(&[m, k_aligned])?; + for row in 0..m { + for col in 0..k { + a.try_as_plain_mut()?.to_array_view_mut()?[[row, col]] = self.a[col + k * row]; + } + } + if let WeightType::Plain(dt) = pack_a.precursor() { + a = a.cast_to_dt(dt)?.into_owned(); + } + let mut b = Tensor::zero::(&[k_aligned, n])?; + for row in 0..k { + for col in 0..n { + b.try_as_plain_mut()?.to_array_view_mut()?[[row, col]] = self.b[col + n * row]; + } + } + if let WeightType::Plain(dt) = pack_b.precursor() { + b = b.cast_to_dt(dt)?.into_owned(); + } + + Ok((a, b)) + } + + pub fn reference(&self) -> TractResult { + let (m, k, n) = self.mkn(); + let (pack_a, pack_b) = &self.ker.packings()[self.packing]; + let (mut a, b) = self.padded_inputs()?; + let k_aligned = k.next_multiple_of(pack_a.k_alignment().max(pack_b.k_alignment())); + if let Some(pbqf) = pack_a.downcast_ref::() { + a = pbqf.simulate_precision_loss(a, 1)?; + }; + let mut c = Tensor::zero::(&[m, n])?; + + let a = a.cast_to::()?; + let a = a.try_as_plain()?.as_slice::()?; + let b = b.cast_to::()?; + let b = b.try_as_plain()?.as_slice::()?; + let mut c_plain = c.try_as_plain_mut()?; + let mut view = c_plain + .to_array_view_mut::()? + .into_dimensionality()?; + for ix_m in 0..m { + for ix_n in 0..n { + for ix_k in 0..k { + let a = a[ix_k + k_aligned * ix_m]; + let b = b[ix_n + n * ix_k]; + view[(ix_m, ix_n)] += a * b; + } + } + } + Ok(c) + } + + pub fn run(&self) -> TractResult { + let (m, k, n) = self.mkn(); + let (pack_a, pack_b) = &self.ker.packings()[self.packing]; + let k_aligned = k.next_multiple_of(pack_a.k_alignment().max(pack_b.k_alignment())); + + let (a, b) = self.padded_inputs()?; + let pa = pack_a.prepare_one(&a, 1, 0)?; + let pb = pack_b.prepare_one(&b, 0, 1)?; + + let mut v = unsafe { Tensor::uninitialized_dt(self.ker.internal_type(), &[m, n])? }; + let item_size = self.ker.internal_type().size_of(); + + if self.frame_test.is_some() { + unsafe { + let c = self.ker.c_view(Some(0), Some(1)).wrap(&v.view_mut()); + let ops = tvec!( + FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa), + b: AsInputValue::Borrowed(&*pb), + packing: self.packing + }, + FusedSpec::Store(c) + ); + self.ker.run(m, n, &ops)?; + } + } else { + let c = OutputStoreKer { + ptr: v.as_bytes_mut().as_mut_ptr(), + row_byte_stride: (item_size * self.ker.nr()) as isize, + col_byte_stride: item_size as isize, + item_size, + }; + + let non_linear_ops = tvec!( + FusedKerSpec::Clear, + FusedKerSpec::AddMatMul { + k: k_aligned, + pa: pa.panel_bytes(0, None)?, + pb: pb.panel_bytes(0, None)?, + packing: self.packing + }, + FusedKerSpec::Store(c), + FusedKerSpec::Done + ); + let err = self.ker.kernel(&non_linear_ops); + assert_eq!(err, 0); + } + Ok(v) + } + + pub fn check(&self) -> TractResult<()> { + if !self.ker.is_supported_here() { + return Ok(()); + } + let expected = self.reference()?; + let found = self.run()?; + let app = if K::Acc::datum_type() == f16::datum_type() { + Approximation::SuperApproximate + } else { + Approximation::Approximate + }; + let result = found.close_enough(&expected, app); + if result.is_err() { + let exp = expected.try_as_plain()?.as_slice::()?; + let found = found.try_as_plain()?.as_slice::()?; + let (m, _, n) = self.mkn(); + display_error(found, exp, m, n); + } + result + } +} + +// Large-shape frame tests that exercise the single-thread 2D-blocked tile walk +// (`run_single_thread_blocked`): the existing `arbitrary_problem` frame proptests +// only reach 3 panels per dim (m,n < 3·mr), below the ST_BLK=16 blocking +// threshold, so the blocked path was otherwise uncovered. generic_f32_4x4 has +// mr=nr=4, so m,n=80 → 20×20 panels → multiple blocks. Compares the frame +// output against the naive reference (must be bit/approx-exact). +#[cfg(test)] +mod single_thread_blocking { + use super::PackedPackedProblem; + use crate::generic::mmm::generic_f32_4x4; + use tract_data::internal::TractResult; + + fn check_large(m: usize, n: usize, k: usize) -> TractResult<()> { + let a: Vec = (0..m * k) + .map(|i| ((i * 7 + 3) % 13) as f32 - 6.0) + .collect(); + let b: Vec = (0..k * n) + .map(|i| ((i * 5 + 1) % 11) as f32 - 5.0) + .collect(); + PackedPackedProblem::frame(&*generic_f32_4x4, 0, m, n, a, b).check() + } + + #[test] + fn blocked_80x80() -> TractResult<()> { + check_large(80, 80, 24) // 20×20 panels, multiple ST_BLK blocks + } + #[test] + fn blocked_skew_200x40() -> TractResult<()> { + check_large(200, 40, 8) // 50×10 panels (m-axis chunked) + } + #[test] + fn blocked_40x200() -> TractResult<()> { + check_large(40, 200, 8) // 10×50 panels (n-axis chunked) + } + #[test] + fn blocked_64x64_exact() -> TractResult<()> { + check_large(64, 64, 16) // exactly 16×16 panels (block boundary) + } + #[test] + fn blocked_68x68_offset() -> TractResult<()> { + check_large(68, 68, 10) // 17×17 panels (one full block + a 1-panel remainder) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/tests/q_scale.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/tests/q_scale.rs new file mode 100644 index 0000000..6449a09 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/tests/q_scale.rs @@ -0,0 +1,178 @@ +use crate::Scaler; +use crate::frame::mmm::MatMatMulKer; +use crate::frame::mmm::fuse::RoundingPolicy; +use crate::generic::rounding::ScaleShiftAndRound; +use crate::mmm::{FusedKerSpec, FusedSpec}; +use proptest::prelude::*; + +use super::fuse::fused_ops; + +#[derive(Debug, new)] +pub struct QScaleProblem +where + K: MatMatMulKer, +{ + pub ker: K, + pub c: Vec, + pub scaler: Scaler, + pub boo: std::marker::PhantomData, +} + +pub fn arbitrary_qscale_problem>( + ker: &K, +) -> BoxedStrategy> { + use RoundingPolicy::*; + let ker = ker.clone(); + let len = ker.mr() * ker.nr(); + ( + proptest::collection::vec(-20i32..20, len..=len), + -5i32..5, + prop_oneof!(Just(1f32), 0f32..1f32), + proptest::prop_oneof![ + Just(Zero), + Just(Away), + Just(PlusInf), + Just(MinusInf), + Just(Odd), + Just(Even) + ], + ) + .prop_map(move |(c, scale_pot, scale_mult, policy)| QScaleProblem { + ker: ker.clone(), + c, + scaler: Scaler::new(scale_mult * 2f32.powi(scale_pot), policy), + boo: std::marker::PhantomData, + }) + .boxed() +} + +impl QScaleProblem +where + K: MatMatMulKer, +{ + pub fn run(&self) { + if !self.ker.is_supported_here() { + return; + } + if let FusedSpec::QScale(shift, policy, mult) = self.scaler.as_fused_spec() { + fused_ops::( + &self.ker, + &self.c, + &[FusedKerSpec::QScale(shift, policy, mult)], + |_, _, c| c.q_scale(self.scaler), + ) + } else if let FusedSpec::RoundingShiftRight(shift, policy) = self.scaler.as_fused_spec() { + fused_ops::( + &self.ker, + &self.c, + &[FusedKerSpec::RoundingShiftRight(shift, policy)], + |_, _, c| c.q_shr(shift, policy), + ) + } else if let FusedSpec::ShiftLeft(shift) = self.scaler.as_fused_spec() { + fused_ops::( + &self.ker, + &self.c, + &[FusedKerSpec::ShiftLeft(shift)], + |_, _, c| c.q_shl(shift), + ) + } else { + unreachable!() + } + } +} + +pub fn return_c_scale_bigpot(ker: &K) +where + K: MatMatMulKer, +{ + let ker = ker.clone(); + let len = ker.mr() * ker.nr(); + let v: Vec = (-(len as i32) / 2..).take(len).collect(); + fused_ops::(&ker, &v, &[FusedKerSpec::ShiftLeft(1)], |_, _, c| { + c.q_shl(1) + }) +} + +#[macro_export] +macro_rules! mmm_q_scale_tests { + ($ker:expr) => { + use $crate::frame::mmm::fuse::RoundingPolicy; + use $crate::frame::mmm::tests::q_scale::arbitrary_qscale_problem; + use $crate::frame::mmm::tests::q_scale::QScaleProblem; + use $crate::frame::mmm::MatMatMulKer; + use $crate::generic::Scaler; + // FIXME: Scaler should be arbitrary + macro_rules! test_q_scale { + ($policy: ident) => { + paste! { + #[test] + fn []() { + let ker = $ker; + let len = (ker.mr() * ker.nr()) as i64; + let v = (0..len).map(|i| (i - len / 2) as i32).collect(); + QScaleProblem::new(ker.clone(), v, Scaler::new(0.5f32, RoundingPolicy::$policy)).run() + } + + #[test] + fn []() { + let ker = $ker; + let len = (ker.mr() * ker.nr()) as i64; + let v = (0..len).map(|i| (i - len / 2) as i32).collect(); + QScaleProblem::new(ker.clone(), v, Scaler::new(-0.5f32, RoundingPolicy::$policy)).run() + } + + #[test] + fn []() { + let ker = $ker; + let len = (ker.mr() * ker.nr()) as i64; + let v = (0..len).map(|i| (i - len / 2) as i32).collect(); + QScaleProblem::new(ker.clone(), v, Scaler::new(0.25f32, RoundingPolicy::$policy)).run() + } + + #[test] + fn []() { + let ker = $ker; + let len = (ker.mr() * ker.nr()) as i64; + let v = (0..len).map(|i| (i - len / 2) as i32).collect(); + QScaleProblem::new(ker.clone(), v, Scaler::new(1f32 / 5., RoundingPolicy::$policy)).run() + } + + #[test] + fn []() { + let ker = $ker; + let len = (ker.mr() * ker.nr()) as i64; + let v = (0..len).map(|i| (i - len / 2) as i32).collect(); + QScaleProblem::new(ker.clone(), v, Scaler::new(4f32, RoundingPolicy::$policy)).run() + } + + #[test] + fn []() { + let ker = $ker; + let len = (ker.mr() * ker.nr()) as i64; + let v = (0..len).map(|i| (i - len / 2) as i32).collect(); + QScaleProblem::new(ker.clone(), v, Scaler::new(14., RoundingPolicy::$policy)).run() + } + } + } + } + + test_q_scale!(Zero); + test_q_scale!(Away); + test_q_scale!(MinusInf); + test_q_scale!(PlusInf); + test_q_scale!(Even); + test_q_scale!(Odd); + + proptest::proptest! { + #[test] + fn return_q_scale_prop(pb in arbitrary_qscale_problem($ker)) { + pb.run() + } + } + + #[test] + fn return_c_scale_bigpot() { + $crate::frame::mmm::tests::q_scale::return_c_scale_bigpot::<_>($ker) + } + }; +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/tests/store.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/tests/store.rs new file mode 100644 index 0000000..195475b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mmm/tests/store.rs @@ -0,0 +1,143 @@ +use crate::LADatum; +use crate::frame::mmm::fuse::FusedKerSpec; +use crate::frame::mmm::storage::*; +use crate::frame::mmm::tests::display_error; +use crate::frame::mmm::*; +use num_traits::Bounded; +use tract_data::internal::*; +use tract_itertools::Itertools; +use tract_ndarray::Axis; + +#[macro_export] +macro_rules! mmm_store_test { + ($ker:expr, $tc:ident) => { + paste! { + mod [] { + #[allow(unused_imports)] + use tract_data::prelude::f16; + use $crate::frame::mmm::tests::store::StoreLayout; + + #[test] fn store_zeros() { + $crate::frame::mmm::tests::store::store_zeros::<_,$tc,_>($ker); + } + + #[test] fn store_col_major() { + $crate::frame::mmm::tests::store::store_pattern::<_,$tc,_>($ker, StoreLayout::ColMajor); + } + + #[test] fn store_row_major() { + $crate::frame::mmm::tests::store::store_pattern::<_,$tc,_>($ker, StoreLayout::RowMajor); + } + + #[test] fn store_arbitrary() { + $crate::frame::mmm::tests::store::store_pattern::<_,$tc,_>($ker, StoreLayout::Arbitrary); + } + } + } + }; +} + +pub fn mmm_stride_storage(v: &[T], rsc: usize) -> OutputStoreKer { + OutputStoreKer { + ptr: v.as_ptr() as _, + row_byte_stride: (std::mem::size_of::() * rsc) as isize, + col_byte_stride: std::mem::size_of::() as isize, + item_size: std::mem::size_of::(), + } +} + +pub fn store_zeros(ker: &K) +where + K: MatMatMulKer, + TC: LADatum, + TI: LADatum + Bounded + PartialEq, +{ + if !ker.is_supported_here() { + return; + } + let v = vec![TC::max_value(); ker.mr() * ker.nr()]; + let c = mmm_stride_storage(&v, ker.nr()); + let non_linear = tvec![ + FusedKerSpec::Clear, + FusedKerSpec::Store(c), + FusedKerSpec::Done + ]; + let err = ker.kernel(&non_linear); + assert_eq!(err, 0); + let expected = vec![TC::zero(); v.len()]; + display_error(&v, &expected, ker.mr(), ker.nr()); + assert_eq!(v, expected); +} + +pub enum StoreLayout { + ColMajor, + RowMajor, + Arbitrary, +} + +pub fn store_pattern(ker: &K, layout: StoreLayout) +where + K: MatMatMulKer, + TC: LADatum, + TI: LADatum + Bounded + PartialEq, +{ + if !ker.is_supported_here() { + return; + } + let (mr, nr) = (ker.mr(), ker.nr()); + let pattern = tensor1(&(0..).take(mr * nr).collect_vec()) + .cast_to::() + .unwrap() + .into_owned() + .into_shape(&[mr, nr]) + .unwrap(); + let pattern_aligned = Blob::from_bytes_alignment(pattern.as_bytes(), 128).unwrap(); + let pattern_col_major = pattern.clone().permute_axes(&[1, 0]).unwrap(); + let pattern_col_major_aligned = + Blob::from_bytes_alignment(pattern_col_major.as_bytes(), 128).unwrap(); + let size_of_tc = std::mem::size_of::(); + let (row_stride, col_stride, result_size) = match layout { + StoreLayout::RowMajor => (nr, 1, mr * nr), + StoreLayout::ColMajor => (1, mr, mr * nr), + // like row major, but storing every other third column + StoreLayout::Arbitrary => (nr * 3, 3, mr * nr * 3), + }; + let mut result = tensor0(TC::max_value()) + .broadcast_to_shape(&[result_size]) + .unwrap(); + let non_linear = tvec![ + FusedKerSpec::LoadTile( + pattern_col_major_aligned.as_ptr() as *const TI, + pattern_aligned.as_ptr() as *const TI, + ), + FusedKerSpec::Store(OutputStoreKer { + ptr: result.as_bytes_mut().as_mut_ptr(), + row_byte_stride: (size_of_tc * row_stride) as isize, + col_byte_stride: (size_of_tc * col_stride) as isize, + item_size: size_of_tc, + }), + FusedKerSpec::Done + ]; + let err = ker.kernel(&non_linear); + assert_eq!(err, 0); + let expected = pattern.cast_to::().unwrap().into_owned(); + let result = match layout { + StoreLayout::RowMajor => result, + StoreLayout::ColMajor => result + .into_shape(&[ker.nr(), ker.mr()]) + .unwrap() + .permute_axes(&[1, 0]) + .unwrap(), + StoreLayout::Arbitrary => result + .into_plain_array::() + .unwrap() + .into_shape_with_order((mr, nr, 3)) + .unwrap() + .index_axis_move(Axis(2), 0) + .into_tensor(), + }; + let expected = expected.try_as_plain().unwrap().as_slice::().unwrap(); + let result = result.try_as_plain().unwrap().as_slice::().unwrap(); + display_error(result, expected, ker.mr(), ker.nr()); + assert_eq!(result, expected); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mod.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mod.rs new file mode 100644 index 0000000..6082b96 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/mod.rs @@ -0,0 +1,33 @@ +#[macro_use] +pub mod block_quant; +#[macro_use] +pub mod element_wise; +pub mod element_wise_helper; +#[macro_use] +pub mod unicast; +#[macro_use] +pub mod by_scalar; +#[macro_use] +pub mod erf; +#[macro_use] +pub mod gelu; +#[macro_use] +pub mod hardswish; +#[macro_use] +pub mod leaky_relu; +#[macro_use] +pub mod lut; +#[macro_use] +pub mod mmm; +#[macro_use] +pub mod pack; +#[macro_use] +pub mod reduce; +#[macro_use] +pub mod sigmoid; +#[macro_use] +pub mod silu; +#[macro_use] +pub mod tanh; +#[macro_use] +pub mod weights; diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/pack.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/pack.rs new file mode 100644 index 0000000..8b1f981 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/pack.rs @@ -0,0 +1,1561 @@ +use std::alloc::Layout; +use std::fmt::{Debug, Display}; +use std::marker::PhantomData; +use std::ops::Range; +use tract_data::internal::*; + +use crate::mmm::{ + EagerPackedInput, MMMInputFormat, MMMInputValue, PackedExoticFact, PackedMatrixStorage, +}; + +use crate::WeightType; + +#[derive(Clone, Eq, PartialEq, Hash)] +pub struct PackedFormat { + pub dt: DatumType, + pub r: usize, + pub alignment_bytes: usize, + pub end_padding_record: usize, +} + +impl MMMInputFormat for PackedFormat { + fn prepare_tensor(&self, t: &Tensor, k_axis: usize, mn_axis: usize) -> TractResult { + let packed = PackedFormat::pack_tensor(self, t, k_axis, mn_axis)?; + Ok(PackedMatrixStorage::new(packed).into_tensor(t.datum_type())) + } + + fn prepare_one( + &self, + t: &Tensor, + k_axis: usize, + mn_axis: usize, + ) -> TractResult> { + PackedFormat::pack_tensor(self, t, k_axis, mn_axis) + } + + fn precursor(&self) -> WeightType { + WeightType::Plain(self.dt) + } + + fn r(&self) -> usize { + self.r + } + + fn k_alignment(&self) -> usize { + 1 + } + + #[allow(clippy::collapsible_if)] + fn merge_with<'o, 'a: 'o, 'b: 'o>( + &'a self, + other: &'b dyn MMMInputFormat, + ) -> Option<&'o dyn MMMInputFormat> { + if let Some(other) = other.downcast_ref::() { + if self.r == other.r && self.dt == other.dt { + if self.alignment_bytes % other.alignment_bytes == 0 + && self.end_padding_record >= other.end_padding_record + { + return Some(self); + } + if other.alignment_bytes % self.alignment_bytes == 0 + && other.end_padding_record >= self.end_padding_record + { + return Some(other); + } + } + } + None + } + + fn mem_size(&self, k: TDim, mn: TDim) -> TDim { + self.len(k, mn) * self.dt.size_of() + } + + fn extract_at_mn_f16( + &self, + data: &EagerPackedInput, + mn: usize, + slice: &mut [f16], + ) -> TractResult<()> { + ensure!(data.format().dyn_eq(self)); + ensure!(self.len(data.k(), data.mn()) * self.dt.size_of() == data.packed.len()); + unsafe { + let ptr = data.packed.as_ptr().add( + (self.single_panel_len(data.k()) * (mn / self.r) + mn % self.r) * self.dt.size_of(), + ); + for (i, slot) in slice.iter_mut().enumerate() { + let ptr = ptr.add(i * self.dt.size_of() * self.r); + *slot = if self.dt == f16::datum_type() { + *(ptr as *const f16) + } else if self.dt == f32::datum_type() { + f16::from_f32(*(ptr as *const f32)) + } else { + bail!("Unexpected DT {:?}", self.dt) + } + } + } + Ok(()) + } + + fn extract_at_mn_f32( + &self, + data: &EagerPackedInput, + mn: usize, + slice: &mut [f32], + ) -> TractResult<()> { + ensure!(data.format().dyn_eq(self)); + ensure!(self.len(data.k(), data.mn()) * self.dt.size_of() == data.packed.len()); + unsafe { + let ptr = data.packed.as_ptr().add( + (self.single_panel_len(data.k()) * (mn / self.r) + mn % self.r) * self.dt.size_of(), + ); + for (i, slot) in slice.iter_mut().enumerate() { + let ptr = ptr.add(i * self.dt.size_of() * self.r); + *slot = if self.dt == f16::datum_type() { + (*(ptr as *const f16)).to_f32() + } else if self.dt == f32::datum_type() { + *(ptr as *const f32) + } else { + bail!("Unexpected DT {:?}", self.dt) + } + } + } + Ok(()) + } +} + +impl Display for PackedFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Packed{:?}[{}]", self.dt, self.r) + } +} + +impl Debug for PackedFormat { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!( + f, + "Packed{:?}[{}]@{}+{}", + self.dt, self.r, self.alignment_bytes, self.end_padding_record + ) + } +} + +impl PackedFormat { + pub const fn new(dt: DatumType, nr: usize, alignment_bytes: usize) -> PackedFormat { + PackedFormat { + dt, + r: nr, + alignment_bytes, + end_padding_record: 1, + } + } + + pub const fn with_end_padding_record(self, end_padding_record: usize) -> Self { + PackedFormat { + end_padding_record, + ..self + } + } + + #[inline] + pub fn align(self, alignment: usize) -> Self { + Self { + alignment_bytes: alignment, + ..self + } + } + + #[inline] + pub fn alignment(&self) -> usize { + self.alignment_bytes + } + + #[inline] + pub fn panel_width(&self) -> usize { + self.r + } + + #[inline] + pub fn len(&self, k: D, n: D) -> D { + n.divceil(self.r) * self.single_panel_len(k) + } + + #[inline] + pub fn single_panel_len(&self, k: D) -> D { + ((k + self.end_padding_record) * self.r).divceil(self.alignment()) * self.alignment() + } + + #[inline] + pub fn single_panel_layout(&self, k: usize, item_size: usize) -> Layout { + Layout::from_size_align(self.single_panel_len(k) * item_size, self.alignment()).unwrap() + } + + pub fn pack_tensor( + &self, + t: &Tensor, + k_axis: usize, + mn_axis: usize, + ) -> TractResult> { + ensure!(t.datum_type().is_copy()); + ensure!( + t.datum_type().unquantized() == self.dt.unquantized(), + "Attempting to pack for {self} tensor {t:?}" + ); + let k = t.shape()[k_axis]; + let mn = t.shape()[mn_axis]; + let packed_len = self.len(k, mn); + let panel_len = self.single_panel_len(k); + let panel_bytes = panel_len * t.datum_type().size_of(); + let strides = t.strides(); + unsafe { + let mut packed = Blob::new_for_size_and_align( + t.datum_type().size_of() * packed_len, + self.alignment_bytes, + ); + if cfg!(debug_assertions) { + packed.as_bytes_mut().fill(0u8); + } + dispatch_copy!(Self::pack_t(t.datum_type())( + self, + packed.as_mut_ptr() as _, + t.as_ptr_unchecked(), + mn, + strides[k_axis], + strides[mn_axis], + 0..k, + 0..mn + )); + Ok(Box::new(EagerPackedInput { + fact: PackedExoticFact { + format: Box::new(self.clone()), + mn: mn.to_dim(), + k, + }, + packed: packed.into(), + panel_bytes, + mn, + })) + } + } + + pub fn pack_tensor_view( + &self, + t: &TensorView, + k_axis: usize, + mn_axis: usize, + ) -> TractResult> { + ensure!( + t.datum_type().unquantized() == self.dt.unquantized(), + "Attempting to pack for {self} tensor view {t:?}" + ); + let k = t.shape()[k_axis]; + let mn = t.shape()[mn_axis]; + let packed_len = self.len(k, mn); + let panel_len = self.single_panel_len(k); + let panel_bytes = panel_len * t.datum_type().size_of(); + let strides = t.strides(); + unsafe { + let mut packed = Blob::new_for_size_and_align( + t.datum_type().size_of() * packed_len, + self.alignment_bytes, + ); + if cfg!(debug_assertions) { + packed.as_bytes_mut().fill(0u8); + } + dispatch_copy!(Self::pack_t(t.datum_type())( + self, + packed.as_mut_ptr() as _, + t.as_ptr_unchecked(), + mn, + strides[k_axis], + strides[mn_axis], + 0..k, + 0..mn + )); + Ok(Box::new(EagerPackedInput { + fact: PackedExoticFact { + format: Box::new(self.clone()), + mn: mn.to_dim(), + k, + }, + packed: packed.into(), + panel_bytes, + mn, + })) + } + } + + pub unsafe fn pack<'a, 'b>( + &self, + pb: impl std::borrow::BorrowMut>, + b: impl std::borrow::Borrow>, + k_axis: usize, + mn_axis: usize, + ) { + let k = b.borrow().shape()[k_axis]; + let mn = b.borrow().shape()[mn_axis]; + unsafe { self.pack_segment(pb, b, k_axis, mn_axis, 0..k, 0..mn) }; + } + + + #[allow(clippy::too_many_arguments)] + #[rustfmt::skip] + pub unsafe fn pack_t( + &self, + pb: *mut T, + b: *const T, + mn: usize, + k_stride: isize, + mn_stride: isize, + k_range: Range, + mn_range: Range, + ) { unsafe { + if k_range.len() == 0 || mn_range.len() == 0 { + return + } + if self.r == 1 && k_stride == 1 && mn == 1 { + pb.copy_from_nonoverlapping(b.add(k_range.start), k_range.len()) + } else if mn_stride == 1 { + let size_of = T::datum_type().size_of(); + let rbytes = self.r * size_of; + let mn_valid_end = mn_range.end.min(mn); + let mn_range_bytes = mn_range.start * size_of..mn_valid_end * size_of; + let k_stride_bytes = k_stride * size_of as isize; + let bb = b as *const u8; + let pbb = pb as *mut u8; + let panel_len = self.single_panel_len(k_range.len()) * size_of; + match rbytes { + 16 => pack_mn_major::<[u8; 16]>(bb, pbb, panel_len, k_stride_bytes, mn_range_bytes, k_range), + 24 => pack_mn_major::<[u8; 24]>(bb, pbb, panel_len, k_stride_bytes, mn_range_bytes, k_range), + 32 => pack_mn_major::<[u8; 32]>(bb, pbb, panel_len, k_stride_bytes, mn_range_bytes, k_range), + 48 => pack_mn_major::<[u8; 48]>(bb, pbb, panel_len, k_stride_bytes, mn_range_bytes, k_range), + 64 => pack_mn_major::<[u8; 64]>(bb, pbb, panel_len, k_stride_bytes, mn_range_bytes, k_range), + 96 => pack_mn_major::<[u8; 96]>(bb, pbb, panel_len, k_stride_bytes, mn_range_bytes, k_range), + 128 => pack_mn_major::<[u8; 128]>(bb, pbb, panel_len, k_stride_bytes, mn_range_bytes, k_range), + _ => { + let mut packer = self.write_with_k_outer(pb, k_range.len(), mn_range.len()); + for k in k_range { + for x in mn_range.start..mn_valid_end { + packer.write(*b.offset(x as isize + k_stride * k as isize)) + } + for _x in mn_valid_end..mn_range.end { + packer.write(T::default()) + } + } + } + } + } else if k_stride == 1 { + let mut packer = self.write_with_k_inner(pb, k_range.len(), mn); + let mn_valid_end = mn_range.end.min(mn); + for x in mn_range.start..mn_valid_end { + for k in k_range.clone() { + packer.write(*b.offset(x as isize * mn_stride + k as isize)) + } + } + // just ignore invalid mn_range + } else { + let mut packer = self.write_with_k_outer(pb, k_range.len(), mn); + let mn_valid_end = mn_range.end.min(mn); + for k in k_range { + for x in mn_range.start..mn_valid_end { + packer.write(*b.offset(x as isize * mn_stride + k_stride * k as isize)) + } + for _x in mn_valid_end..mn_range.end { + packer.write(T::default()) + } + } + } + }} + + #[inline] + pub unsafe fn pack_segment<'a, 'b>( + &self, + mut pb: impl std::borrow::BorrowMut>, + b: impl std::borrow::Borrow>, + k_axis: usize, + mn_axis: usize, + k_range: Range, + mn_range: Range, + ) { + debug_assert!(pb.borrow().len() >= self.len(k_range.len(), mn_range.len())); + let pb = pb.borrow_mut(); + let b = b.borrow(); + let dt = pb.datum_type(); + unsafe { + dispatch_copy!(Self::pack_t(dt)( + self, + pb.as_ptr_mut_unchecked(), + b.as_ptr_unchecked(), + b.shape()[mn_axis], + b.strides()[k_axis], + b.strides()[mn_axis], + k_range, + mn_range + )); + } + } + + pub fn write_with_k_outer<'p, T: Copy + Debug>( + &self, + pb: *mut T, + k: usize, + mn: usize, + ) -> KOutWriter<'p, T> { + KOutWriter::new(pb, self.r, self.single_panel_len(k), mn, k) + } + + pub fn write_single_panel_with_k_outer<'p, T: Copy + Debug>( + &self, + pb: *mut T, + ) -> KOutSinglePanelWriter<'p, T> { + KOutSinglePanelWriter::new(pb) + } + + pub fn write_with_k_inner<'p, T: Copy + Debug>( + &self, + pb: *mut T, + k: usize, + mn: usize, + ) -> KInWriter<'p, T> { + let panel_len = self.single_panel_len(k); + KInWriter::new(pb, panel_len, self.r, mn, k) + } +} + +pub trait PackingWriter { + fn write(&mut self, t: T); + + /// Write a contiguous slice of values. The default implementation falls + /// back to per-element `write`; concrete writers may override with a + /// `memcpy`-class fast path when the destination layout permits it. + /// + /// The output produced by `write_slice(s)` must be byte-identical to + /// `for &t in s { self.write(t); }` for any input. + #[inline] + fn write_slice(&mut self, ts: &[T]) { + for t in ts { + self.write(*t); + } + } +} + +#[derive(Debug)] +pub struct KOutSinglePanelWriter<'p, T> +where + T: Copy + std::fmt::Debug, +{ + ptr: *mut T, + _phantom: PhantomData<&'p T>, +} + +impl<'p, T> KOutSinglePanelWriter<'p, T> +where + T: Copy + std::fmt::Debug, +{ + pub fn new(ptr: *mut T) -> KOutSinglePanelWriter<'p, T> { + KOutSinglePanelWriter { + ptr, + _phantom: PhantomData, + } + } +} + +impl PackingWriter for KOutSinglePanelWriter<'_, T> +where + T: Copy + std::fmt::Debug, +{ + #[inline(always)] + fn write(&mut self, t: T) { + unsafe { + *self.ptr = t; + self.ptr = self.ptr.offset(1); + } + } + + #[inline] + fn write_slice(&mut self, ts: &[T]) { + // KOutSinglePanelWriter writes elements consecutively with no panel + // boundaries. A direct `copy_nonoverlapping` is byte-identical to the + // per-element loop. + unsafe { + std::ptr::copy_nonoverlapping(ts.as_ptr(), self.ptr, ts.len()); + self.ptr = self.ptr.add(ts.len()); + } + } +} + +#[derive(Debug)] +pub struct KOutWriter<'p, T> +where + T: Copy + std::fmt::Debug, +{ + ptr: *mut T, + panels: usize, + panel_width: usize, + last_panel_width: usize, + remain: usize, + current_panel: usize, + next_panel: isize, + next_lane: isize, + _phantom: PhantomData<&'p T>, +} + +impl<'p, T> KOutWriter<'p, T> +where + T: Copy + std::fmt::Debug, +{ + pub fn new( + ptr: *mut T, + panel_width: usize, + panel_len: usize, + mn: usize, + _k: usize, + ) -> KOutWriter<'p, T> { + let panels = mn.divceil(panel_width); + let last_panel_width = mn - (panels - 1) * panel_width; + KOutWriter { + ptr, + panels, + panel_width, + last_panel_width, + remain: if panels > 1 { + panel_width + } else { + last_panel_width + }, + current_panel: 0, + next_panel: (panel_len - panel_width) as isize, + next_lane: (panel_width - last_panel_width) as isize + - (panel_len * (panels - 1)) as isize, + _phantom: PhantomData, + } + } +} + +impl PackingWriter for KOutWriter<'_, T> +where + T: Copy + std::fmt::Debug, +{ + #[inline(always)] + fn write(&mut self, t: T) { + unsafe { + *self.ptr = t; + self.remain -= 1; + self.ptr = self.ptr.offset(1); + if self.remain == 0 { + self.current_panel += 1; + if self.current_panel == self.panels { + self.ptr = self.ptr.offset(self.next_lane); + self.current_panel = 0; + } else { + self.ptr = self.ptr.offset(self.next_panel); + } + if self.current_panel == self.panels - 1 { + self.remain = self.last_panel_width; + } else { + self.remain = self.panel_width; + } + } + } + } + + #[inline] + fn write_slice(&mut self, ts: &[T]) { + // Fast path: the slice fits entirely within the current panel. Writes + // are then guaranteed to be `ts.len()` consecutive memory locations + // followed by the same panel/lane bookkeeping the per-element path + // performs. This produces byte-identical output to a per-element loop. + // + // When the slice would cross a panel boundary, fall back to the + // per-element path so all transition logic stays in one place. + let n = ts.len(); + if n == 0 { + return; + } + if n < self.remain { + // Strictly inside the current panel: bulk copy, then advance. + unsafe { + std::ptr::copy_nonoverlapping(ts.as_ptr(), self.ptr, n); + self.ptr = self.ptr.add(n); + } + self.remain -= n; + } else if n == self.remain { + // Exactly fills the current panel: bulk copy, then run the same + // panel-transition bookkeeping that `write` does on its final + // element. The transition is performed unconditionally here + // (rather than calling `write` for the last element) to keep the + // semantics identical even when the trait is inlined separately. + unsafe { + std::ptr::copy_nonoverlapping(ts.as_ptr(), self.ptr, n); + self.ptr = self.ptr.add(n); + self.current_panel += 1; + if self.current_panel == self.panels { + self.ptr = self.ptr.offset(self.next_lane); + self.current_panel = 0; + } else { + self.ptr = self.ptr.offset(self.next_panel); + } + if self.current_panel == self.panels - 1 { + self.remain = self.last_panel_width; + } else { + self.remain = self.panel_width; + } + } + } else { + // Spans a panel boundary. Fall back to per-element writes so the + // panel-transition state machine handles every step. + for t in ts { + self.write(*t); + } + } + } +} + +#[derive(Debug)] +pub struct KInWriter<'p, T> +where + T: Copy + Debug, +{ + ptr: *mut T, + k: usize, + panels: usize, + panel_width: usize, + last_panel_width: usize, + remain_on_k: usize, + remain_on_mn: usize, + current_panel: usize, + next_mn_offset: isize, + next_panel_offset: isize, + _phantom: PhantomData<&'p T>, +} + +impl<'p, T> KInWriter<'p, T> +where + T: Copy + Debug, +{ + pub fn new( + ptr: *mut T, + panel_len: usize, + panel_width: usize, + mn: usize, + k: usize, + ) -> KInWriter<'p, T> { + let panels = mn.divceil(panel_width); + let last_panel_width = mn - (panels - 1) * panel_width; + KInWriter { + ptr, + k, + panels, + panel_width, + last_panel_width, + remain_on_k: k, + remain_on_mn: if panels == 1 { + last_panel_width + } else { + panel_width + }, + current_panel: 0, + next_mn_offset: 1 - (k * panel_width) as isize, + next_panel_offset: panel_len as isize - (k * panel_width + panel_width - 1) as isize, + // ^ next panel ^ ^ rewind left ^ ^ rewind up ^ + _phantom: PhantomData, + } + } +} + +impl PackingWriter for KInWriter<'_, T> +where + T: Copy + std::fmt::Debug, +{ + #[inline(always)] + fn write(&mut self, t: T) { + unsafe { + *self.ptr = t; + self.remain_on_k -= 1; + self.ptr = self.ptr.add(self.panel_width); + if self.remain_on_k == 0 { + self.remain_on_k = self.k; + self.remain_on_mn -= 1; + if self.remain_on_mn > 0 { + self.ptr = self.ptr.offset(self.next_mn_offset); + } else { + self.ptr = self.ptr.offset(self.next_panel_offset); + self.current_panel += 1; + if self.current_panel == self.panels - 1 { + self.remain_on_mn = self.last_panel_width; + } else { + self.remain_on_mn = self.panel_width; + } + } + } + } + } +} + +#[inline(never)] +unsafe fn pack_mn_major( + b: *const u8, + packed: *mut u8, + panel_len: usize, + k_stride_bytes: isize, + mn_range_bytes: Range, + k_range: Range, +) { + unsafe { + let mnr = std::mem::size_of::(); + let full_panes = mn_range_bytes.len() / mnr; + let partial_pane = mn_range_bytes.len() % mnr; + for k in 0..k_range.len() { + let mut p_row = packed.add(k * mnr); + let mut b_row = b.offset( + (k_range.start + k) as isize * k_stride_bytes + mn_range_bytes.start as isize, + ); + for _ in 0..full_panes { + p_row.copy_from_nonoverlapping(b_row, mnr); + p_row = p_row.add(panel_len); + b_row = b_row.add(mnr); + } + if partial_pane > 0 { + p_row.copy_from_nonoverlapping(b_row, partial_pane); + } + } + } +} + +// K=4-inner packing writer (PackedI8K4 layout), fed in K-OUTER order (same feed +// as KOutWriter, used by the im2col patchers): for each k, all mn. Within a panel, +// element (k, local_mn) lands at (k/4)*r*4 + local_mn*4 + (k%4), so consecutive mn +// for a fixed k are stride-4 stores. +#[derive(Debug)] +pub struct KOut4Writer<'p, T> +where + T: Copy + std::fmt::Debug, +{ + base: *mut T, + r4: usize, // r * 4 + panel_len: usize, // k_aligned * r + panels: usize, + panel_width: usize, + last_panel_width: usize, + kb: usize, // k / 4 + kr: usize, // k % 4 + panel: usize, + local_mn: usize, + _phantom: PhantomData<&'p T>, +} + +impl<'p, T> KOut4Writer<'p, T> +where + T: Copy + std::fmt::Debug, +{ + pub fn new(base: *mut T, r: usize, panel_len: usize, mn: usize) -> KOut4Writer<'p, T> { + let panels = mn.divceil(r).max(1); + let last_panel_width = mn - (panels - 1) * r; + KOut4Writer { + base, + r4: r * 4, + panel_len, + panels, + panel_width: r, + last_panel_width, + kb: 0, + kr: 0, + panel: 0, + local_mn: 0, + _phantom: PhantomData, + } + } + #[inline(always)] + fn panel_width(&self) -> usize { + if self.panel == self.panels - 1 { + self.last_panel_width + } else { + self.panel_width + } + } + #[inline(always)] + fn advance(&mut self, by: usize) { + self.local_mn += by; + if self.local_mn >= self.panel_width() { + self.local_mn = 0; + self.panel += 1; + if self.panel == self.panels { + self.panel = 0; + self.kr += 1; + if self.kr == 4 { + self.kr = 0; + self.kb += 1; + } + } + } + } +} + +impl PackingWriter for KOut4Writer<'_, T> +where + T: Copy + std::fmt::Debug, +{ + #[inline(always)] + fn write(&mut self, t: T) { + unsafe { + let off = self.panel * self.panel_len + self.kb * self.r4 + self.local_mn * 4 + self.kr; + *self.base.add(off) = t; + } + self.advance(1); + } + + #[inline] + fn write_slice(&mut self, ts: &[T]) { + let n = ts.len(); + if n == 0 { + return; + } + let pw = self.panel_width(); + if self.local_mn + n <= pw { + // Whole slice stays inside the current (panel, k): tight stride-4 store. + unsafe { + let mut d = self.base.add( + self.panel * self.panel_len + self.kb * self.r4 + self.local_mn * 4 + self.kr, + ); + for &t in ts { + *d = t; + d = d.add(4); + } + } + self.advance(n); + } else { + for &t in ts { + self.write(t); + } + } + } +} + +// K=4-inner packing for SDOT/relaxed-dot int8 matmul: 4 contiguous K per mn-lane. +// Layout: out[(k/4)*r*4 + m*4 + (k%4)] = src[m,k]. k_alignment=4. Matmul path uses +// pack_view; the conv im2col patchers feed write_with_k_outer in K-outer order. +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +pub struct PackedI8K4 { + pub r: usize, + pub align: usize, +} +impl PackedI8K4 { + pub fn new(r: usize) -> Self { + PackedI8K4 { r, align: 16 } + } + fn panel(&self, k: usize) -> usize { + (k.div_ceil(4) * 4) * self.r + } + pub fn single_panel_len(&self, k: usize) -> usize { + self.panel(k) + } + pub fn len(&self, k: usize, mn: usize) -> usize { + mn.divceil(self.r) * self.panel(k) + } + pub fn alignment(&self) -> usize { + self.align + } + // One-pass K-outer writer for the conv im2col patchers (fed: for each k, all mn). + pub fn write_with_k_outer<'p, T: Copy + std::fmt::Debug>( + &self, + pb: *mut T, + k: usize, + mn: usize, + ) -> KOut4Writer<'p, T> { + KOut4Writer::new(pb, self.r, self.panel(k), mn) + } + // K=4-inner pack from a (possibly strided) view: out[(k/4)*r*4 + m*4 + (k%4)] = src[m,k]. + pub fn pack_view( + &self, + t: &TensorView, + k_axis: usize, + mn_axis: usize, + ) -> TractResult> { + let k = t.shape()[k_axis]; + let mn = t.shape()[mn_axis]; + let kp = k.div_ceil(4) * 4; + let pl = kp * self.r; + let panels = mn.div_ceil(self.r); + let st = t.strides(); + let mut blob = unsafe { Blob::new_for_size_and_align(panels * pl, self.align) }; + blob.as_bytes_mut().fill(0); + let (ks, ms) = (st[k_axis], st[mn_axis]); + let kblocks = kp / 4; + unsafe { + let src = t.as_ptr_unchecked::(); + let dst = blob.as_mut_ptr() as *mut i8; + for p in 0..panels { + let pw = self.r.min(mn - p * self.r); + let panel = dst.add(p * pl); + let mn0 = (p * self.r) as isize; + for kb in 0..kblocks { + for kr in 0..4 { + let kk = kb * 4 + kr; + if kk >= k { + break; + } + let srow = src.offset(kk as isize * ks + mn0 * ms); + let dcol = panel.add(kb * self.r * 4 + kr); + for lm in 0..pw { + *dcol.add(lm * 4) = *srow.offset(lm as isize * ms); + } + } + } + } + } + Ok(Box::new(EagerPackedInput { + fact: PackedExoticFact { + format: Box::new(self.clone()), + mn: mn.to_dim(), + k, + }, + packed: blob.into(), + panel_bytes: pl, + mn, + })) + } +} +impl std::fmt::Display for PackedI8K4 { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "I8K4[{}]", self.r) + } +} +impl MMMInputFormat for PackedI8K4 { + fn prepare_tensor(&self, t: &Tensor, k_axis: usize, mn_axis: usize) -> TractResult { + Ok( + PackedMatrixStorage::new(self.prepare_one(t, k_axis, mn_axis)?) + .into_tensor(t.datum_type()), + ) + } + fn prepare_one( + &self, + t: &Tensor, + k_axis: usize, + mn_axis: usize, + ) -> TractResult> { + self.pack_view(&t.view(), k_axis, mn_axis) + } + fn precursor(&self) -> WeightType { + WeightType::Plain(i8::datum_type()) + } + fn r(&self) -> usize { + self.r + } + fn k_alignment(&self) -> usize { + 4 + } + fn merge_with<'o, 'a: 'o, 'b: 'o>( + &'a self, + o: &'b dyn MMMInputFormat, + ) -> Option<&'o dyn MMMInputFormat> { + o.downcast_ref::() + .filter(|x| x.r == self.r) + .map(|_| self as _) + } + fn mem_size(&self, k: TDim, mn: TDim) -> TDim { + mn.divceil(self.r) * self.panel(k.to_usize().unwrap_or(0)) + } + fn extract_at_mn_f16(&self, _: &EagerPackedInput, _: usize, _: &mut [f16]) -> TractResult<()> { + bail!("no f16 extract") + } + fn extract_at_mn_f32(&self, _: &EagerPackedInput, _: usize, _: &mut [f32]) -> TractResult<()> { + bail!("no f32 extract") + } +} + +pub trait Packing { + fn packing(r: usize) -> PackedFormat; +} + +impl Packing for D { + fn packing(r: usize) -> PackedFormat { + PackedFormat::new(Self::datum_type(), r, vector_size()) + } +} + +#[cfg(test)] +mod test { + use std::ops::Range; + + use proptest::prelude::*; + use tract_data::internal::num_integer::Integer; + use tract_data::internal::tract_ndarray::Zip; + use tract_data::internal::*; + use tract_ndarray::prelude::*; + + #[derive(Debug)] + struct PackProblem { + k: usize, + mn: usize, + is_a: bool, + r: usize, + k_range: Range, + mn_range: Range, + align_panel: usize, + } + + impl PackProblem { + fn input(&self) -> Array2 { + let shape = if self.is_a { + (self.mn, self.k) + } else { + (self.k, self.mn) + }; + let data = (0..(self.k * self.mn) as u32).collect(); + Array2::from_shape_vec(shape, data).unwrap() + } + + fn packer(&self) -> Array2 { + let panels = self.mn_range.len().divceil(self.r); + let packer = super::PackedFormat::new(u32::datum_type(), self.r, self.align_panel) + .with_end_padding_record(0); + let input = self.input().into_tensor(); + let panel_len = packer.single_panel_len(self.k_range.len()); + let mut output = + Tensor::zero::(&[packer.len(self.k_range.len(), self.mn_range.len())]) + .unwrap(); + unsafe { + packer.pack_segment( + output.view_mut(), + input.view(), + self.is_a as usize, + !self.is_a as usize, + self.k_range.clone(), + self.mn_range.clone(), + ) + }; + output + .into_plain_array::() + .unwrap() + .into_shape_with_order((panels, panel_len)) + .unwrap() + } + + fn reference(&self) -> Array2 { + let input = self.input(); + let panels = self.mn_range.len().divceil(self.r); + let len = Integer::next_multiple_of(&(self.k_range.len() * self.r), &self.align_panel); + Array2::from_shape_fn([panels, len], |(panel, z)| { + let k = z / self.r; + let x = z % self.r; + let mn = panel * self.r + x + self.mn_range.start; + let k = k + self.k_range.start; + let coords = if self.is_a { (mn, k) } else { (k, mn) }; + *input.get(coords).unwrap_or(&0) + }) + } + + fn valid(&self) -> Array2 { + let panels = self.mn_range.len().divceil(self.r); + let len = Integer::next_multiple_of(&(self.k_range.len() * self.r), &self.align_panel); + Array2::from_shape_fn([panels, len], |(panel, z)| { + let k = z / self.r; + let x = z % self.r; + let k = k + self.k_range.start; + let mn = panel * self.r + x + self.mn_range.start; + k < self.k_range.end.min(self.k) && mn < self.mn_range.end.min(self.mn) + }) + } + + fn check(&self) { + let mut packer = self.packer(); + let mut reference = self.reference(); + let valid = self.valid(); + Zip::from(&mut packer) + .and(&valid) + .for_each(|p, v| *p = if *v { *p } else { -1 as _ }); + Zip::from(&mut reference) + .and(&valid) + .for_each(|p, v| *p = if *v { *p } else { -1 as _ }); + assert_eq!(packer, reference); + } + } + + impl Arbitrary for PackProblem { + type Parameters = (); + type Strategy = BoxedStrategy; + fn arbitrary_with(_args: ()) -> Self::Strategy { + (any::(), 1usize..9, 1usize..20, 1usize..20) + .prop_flat_map(|(is_a, r, k, mn)| { + ( + Just((is_a, r, k, mn)), + sub_range_strat(0..k), + sub_range_strat(0..mn), + 1usize..5, + ) + }) + .prop_map( + |((is_a, r, k, mn), k_range, mn_range, align_panel)| PackProblem { + k, + mn, + is_a, + r, + k_range, + mn_range, + align_panel, + }, + ) + .boxed() + } + } + + fn sub_range_strat(range: Range) -> BoxedStrategy> { + (0..range.len()) + .prop_flat_map(|cropped| (Just(cropped), 0..=cropped)) + .prop_map(move |(cropped, left)| range.start + left..range.end - (cropped - left)) + .boxed() + } + + proptest::proptest! { + #[test] + fn prop(pb in any::()) { + pb.check(); + } + + #[test] + fn subrange_prop(_range in sub_range_strat(0..20)) { + } + + } + // ---- PackedI8K4 (K=4-inner SMOPA/SDOT layout) dedicated tests ---------- + // + // PackedI8K4 has two independent producers that MUST agree byte-for-byte: + // * `pack_view` — the matmul path, reads a (possibly strided) + // TensorView and packs in one shot. + // * `write_with_k_outer` — the conv/im2col path, fed element-by-element + // in K-OUTER order (for each k, all mn). + // Both must equal the canonical layout + // out[panel*pl + (k/4)*r*4 + local_mn*4 + (k%4)] = src[k, panel*r+local_mn] + // with pl = ceil(K/4)*4 * r, and every padding byte (K%4 tail, partial last + // mn panel) left at zero. + #[derive(Debug, Clone)] + struct PackI8K4Problem { + k: usize, + mn: usize, + r: usize, + // false: input tensor is [k, mn] (k_axis=0, mn_axis=1) — contiguous read. + // true : input tensor is [mn, k] (k_axis=1, mn_axis=0) — strided read, + // mirroring how the "A" operand is fed. + is_a: bool, + } + + impl PackI8K4Problem { + // Canonical logical matrix, always indexed [k, mn]. + fn logical(&self) -> Array2 { + Array2::from_shape_fn((self.k, self.mn), |(kk, m)| { + (kk.wrapping_mul(31) + .wrapping_add(m.wrapping_mul(17)) + .wrapping_add(1)) as i8 + }) + } + + fn panel_len(&self) -> usize { + (self.k.div_ceil(4) * 4) * self.r + } + + // The layout every producer must reproduce. + fn reference(&self) -> Vec { + let logical = self.logical(); + let r = self.r; + let pl = self.panel_len(); + let panels = self.mn.div_ceil(r); + let mut out = vec![0i8; panels * pl]; + for p in 0..panels { + let pw = r.min(self.mn - p * r); + for kk in 0..self.k { + for lm in 0..pw { + let m = p * r + lm; + let off = p * pl + (kk / 4) * r * 4 + lm * 4 + (kk % 4); + out[off] = logical[[kk, m]]; + } + } + } + out + } + + // The matmul path: pack a TensorView, then read it back panel by panel. + fn pack_view_bytes(&self) -> Vec { + let logical = self.logical(); + let packer = super::PackedI8K4::new(self.r); + let (tensor, k_axis, mn_axis) = if self.is_a { + // [mn, k] with entry [m, kk] == logical[kk, m]; reads are strided. + let a = Array2::from_shape_fn((self.mn, self.k), |(m, kk)| logical[[kk, m]]); + (a.into_tensor(), 1usize, 0usize) + } else { + (logical.clone().into_tensor(), 0usize, 1usize) + }; + let packed = packer.pack_view(&tensor.view(), k_axis, mn_axis).unwrap(); + let pl = self.panel_len(); + let panels = self.mn.div_ceil(self.r); + assert_eq!(packed.panels_count(), panels); + assert_eq!(packed.k(), self.k); + assert_eq!(packed.mn(), self.mn); + let mut out = vec![0i8; panels * pl]; + unsafe { + for p in 0..panels { + let ptr = packed.panel_bytes(p, None).unwrap() as *const i8; + std::ptr::copy_nonoverlapping(ptr, out.as_mut_ptr().add(p * pl), pl); + } + } + out + } + + // The conv path: feed the writer in K-outer order (for each k, all mn). + fn writer_bytes(&self) -> Vec { + let logical = self.logical(); + let packer = super::PackedI8K4::new(self.r); + let total = packer.len(self.k, self.mn); + assert_eq!(total, self.mn.div_ceil(self.r) * self.panel_len()); + let mut buf = vec![0i8; total]; + { + let mut w = packer.write_with_k_outer(buf.as_mut_ptr(), self.k, self.mn); + for kk in 0..self.k { + for m in 0..self.mn { + super::PackingWriter::write(&mut w, logical[[kk, m]]); + } + } + } + buf + } + + fn check(&self) { + let reference = self.reference(); + assert_eq!( + self.pack_view_bytes(), + reference, + "pack_view disagrees with reference for {self:?}" + ); + assert_eq!( + self.writer_bytes(), + reference, + "write_with_k_outer disagrees with reference for {self:?}" + ); + } + } + + impl Arbitrary for PackI8K4Problem { + type Parameters = (); + type Strategy = BoxedStrategy; + fn arbitrary_with(_: ()) -> Self::Strategy { + // r is the tile width used by the int8 kernels (SMOPA 32, SDOT 8, ...). + ( + any::(), + prop::sample::select(vec![4usize, 8, 16, 32]), + 1usize..40, + 1usize..40, + ) + .prop_map(|(is_a, r, k, mn)| PackI8K4Problem { k, mn, r, is_a }) + .boxed() + } + } + + proptest::proptest! { + #[test] + fn pack_i8k4_prop(pb in any::()) { + pb.check(); + } + } + + fn k4(k: usize, mn: usize, r: usize, is_a: bool) -> PackI8K4Problem { + PackI8K4Problem { k, mn, r, is_a } + } + + #[test] + fn i8k4_smallest() { + k4(1, 1, 4, false).check(); + k4(1, 1, 4, true).check(); + } + + #[test] + fn i8k4_exact_tile() { + // K and mn land exactly on the 4 / r boundaries: no padding anywhere. + k4(4, 4, 4, false).check(); + k4(8, 32, 32, false).check(); + k4(8, 32, 32, true).check(); + } + + #[test] + fn i8k4_k_not_multiple_of_4() { + // K%4 tail must be zero-padded inside each panel. + for k in [1, 2, 3, 5, 6, 7, 9] { + k4(k, 4, 4, false).check(); + k4(k, 7, 8, true).check(); + } + } + + #[test] + fn i8k4_partial_last_panel() { + // mn not a multiple of r: last panel is narrower, tail lanes are zero. + k4(5, 7, 4, false).check(); + k4(5, 7, 4, true).check(); + k4(4, 33, 32, false).check(); + k4(4, 33, 32, true).check(); + k4(3, 1, 32, false).check(); + } + + #[test] + fn i8k4_single_wide_tile() { + // One narrow panel inside a wide (r=32) tile. + k4(7, 1, 32, false).check(); + k4(7, 5, 16, true).check(); + } + + #[test] + fn i8k4_many_panels() { + k4(13, 100, 8, false).check(); + k4(13, 100, 8, true).check(); + k4(17, 65, 16, false).check(); + } + + #[test] + fn simple_b_1() { + PackProblem { + k: 2, + mn: 1, + is_a: false, + r: 1, + k_range: 0..2, + mn_range: 0..1, + align_panel: 1, + } + .check(); + } + + #[test] + fn simple_b_2() { + PackProblem { + k: 2, + mn: 2, + is_a: false, + r: 1, + k_range: 0..2, + mn_range: 0..2, + align_panel: 1, + } + .check() + } + + #[test] + fn simple_b_3() { + PackProblem { + k: 2, + mn: 1, + is_a: false, + r: 4, + k_range: 0..2, + mn_range: 0..1, + align_panel: 1, + } + .check(); + } + + #[test] + fn simple_b_4() { + PackProblem { + k: 1, + mn: 3, + is_a: false, + r: 2, + k_range: 0..1, + mn_range: 0..3, + align_panel: 1, + } + .check(); + } + + #[test] + fn simple_a_1() { + PackProblem { + k: 2, + mn: 2, + is_a: true, + r: 1, + k_range: 0..2, + mn_range: 0..2, + align_panel: 1, + } + .check(); + } + + #[test] + fn simple_a_2() { + PackProblem { + k: 2, + mn: 3, + is_a: true, + r: 2, + k_range: 0..2, + mn_range: 0..3, + align_panel: 1, + } + .check(); + } + + #[test] + fn range_k_0() { + PackProblem { + k: 2, + mn: 1, + is_a: false, + r: 1, + k_range: 1..2, + mn_range: 0..1, + align_panel: 1, + } + .check(); + } + + #[test] + fn range_k_1() { + PackProblem { + k: 2, + mn: 2, + is_a: false, + r: 1, + k_range: 0..2, + mn_range: 0..1, + align_panel: 1, + } + .check(); + } + + #[test] + fn range_k_2() { + PackProblem { + k: 2, + mn: 1, + is_a: false, + r: 6, + k_range: 1..2, + mn_range: 0..1, + align_panel: 1, + } + .check(); + } + + #[test] + fn range_mn_0() { + PackProblem { + k: 1, + mn: 2, + is_a: false, + r: 2, + k_range: 0..1, + mn_range: 0..1, + align_panel: 1, + } + .check(); + } + + #[test] + fn range_b_4() { + PackProblem { + k: 1, + mn: 2, + is_a: false, + r: 6, + k_range: 0..1, + mn_range: 1..2, + align_panel: 1, + } + .check(); + } + + #[test] + fn range_b_5() { + PackProblem { + k: 1, + mn: 7, + is_a: false, + r: 6, + k_range: 0..1, + mn_range: 1..7, + align_panel: 1, + } + .check(); + } + + #[test] + fn align_a_1() { + PackProblem { + k: 2, + mn: 2, + is_a: true, + r: 1, + k_range: 0..1, + mn_range: 0..2, + align_panel: 2, + } + .check(); + } + + #[test] + fn align_b_1() { + PackProblem { + k: 1, + mn: 1, + is_a: false, + r: 1, + k_range: 0..1, + mn_range: 0..1, + align_panel: 2, + } + .check(); + } + + #[test] + fn align_b_2() { + PackProblem { + k: 3, + mn: 1, + is_a: false, + r: 1, + k_range: 0..3, + mn_range: 0..1, + align_panel: 2, + } + .check(); + } + + #[test] + fn align_b_3() { + PackProblem { + k: 1, + mn: 1, + is_a: false, + r: 3, + k_range: 0..1, + mn_range: 0..1, + align_panel: 2, + } + .check(); + } + + #[test] + fn align_b_4() { + PackProblem { + k: 2, + mn: 1, + is_a: false, + r: 1, + k_range: 0..1, + mn_range: 0..1, + align_panel: 2, + } + .check(); + } + + #[test] + fn align_b_5() { + PackProblem { + k: 1, + mn: 5, + is_a: false, + r: 4, + k_range: 0..1, + mn_range: 0..5, + align_panel: 3, + } + .check(); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/reduce/max.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/reduce/max.rs new file mode 100644 index 0000000..4b73003 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/reduce/max.rs @@ -0,0 +1,42 @@ +#[cfg(test)] +#[macro_use] +pub mod test { + use crate::LADatum; + use crate::frame::reduce::ReduceKer; + use num_traits::{AsPrimitive, Float}; + use proptest::test_runner::TestCaseResult; + + #[macro_export] + macro_rules! max_frame_tests { + ($cond:expr, $t: ty, $ker:ty) => { + proptest::proptest! { + #[test] + fn prop(xs in proptest::collection::vec(-25f32..25.0, 0..100)) { + if $cond { + $crate::frame::reduce::max::test::test_max::<$ker, $t>(&*xs).unwrap() + } + } + } + + #[test] + fn empty() { + if $cond { + $crate::frame::reduce::max::test::test_max::<$ker, $t>(&[]).unwrap() + } + } + }; + } + + pub fn test_max, T: LADatum + Float>(values: &[f32]) -> TestCaseResult + where + f32: AsPrimitive, + { + crate::setup_test_logger(); + let values: Vec = values.iter().copied().map(|x| x.as_()).collect(); + crate::frame::reduce::test::test_reduce::( + &values, + ::min_value(), + |a, b| a.max(b), + ) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/reduce/mod.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/reduce/mod.rs new file mode 100644 index 0000000..f9f62e2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/reduce/mod.rs @@ -0,0 +1,303 @@ +pub mod max; +pub mod softmax; +pub mod sum; + +use std::fmt::Debug; +use std::marker::PhantomData; + +use tract_data::TractResult; + +use crate::LADatum; + +use super::element_wise_helper::{map_reduce_slice_with_alignment, reduce_slice_with_alignment}; + +macro_rules! reduce_impl_wrap { + ($ti: ident, $func: ident, $nr: expr, $alignment_items: expr, $params: ty, $neutral: expr, $run: item, $reduce_two: item) => { + paste! { + #[derive(Copy, Clone, Debug)] + #[allow(non_camel_case_types)] + pub struct $func; + + impl crate::frame::reduce::ReduceKer<$ti, $params> for $func { + #[inline(always)] + fn name() -> &'static str { + stringify!($func) + } + #[inline(always)] + fn nr() -> usize { + $nr + } + #[inline(always)] + fn alignment_items() -> usize { + $alignment_items + } + #[inline(always)] + fn alignment_bytes() -> usize { + $alignment_items * std::mem::size_of::<$ti>() + } + #[inline(always)] + fn neutral() -> $ti { + $neutral + } + $run + $reduce_two + } + } + }; +} + +pub trait Reduce: Send + Sync + Debug + dyn_clone::DynClone +where + Params: Copy + Send + Sync + Debug + 'static + Default, + T: Copy + Debug + PartialEq + Send + Sync, +{ + fn name(&self) -> &'static str; + fn run(&self, vec: &[T]) -> TractResult { + self.run_with_params(vec, Params::default()) + } + fn run_with_params(&self, vec: &[T], params: Params) -> TractResult; +} + +dyn_clone::clone_trait_object!( Reduce where T: Copy, Params: Copy); + +#[derive(Debug, Clone, new)] +pub struct ReduceImpl +where + T: LADatum, + Params: Copy + Send + Sync + Debug + 'static + Default, + K: ReduceKer + Clone, +{ + phantom: PhantomData<(K, T, Params)>, +} + +impl Reduce for ReduceImpl +where + T: LADatum, + Params: Copy + Send + Sync + Debug + 'static + Default, + K: ReduceKer + Clone, +{ + fn name(&self) -> &'static str { + K::name() + } + + fn run_with_params(&self, vec: &[T], params: Params) -> TractResult { + reduce_slice_with_alignment( + vec, + |data| K::run(data, params), + K::nr(), + K::alignment_bytes(), + K::neutral(), + K::reduce_two, + ) + } +} + +pub trait ReduceKer: + Send + Sync + Debug + dyn_clone::DynClone + Clone + 'static +where + Params: Copy + Send + Sync + Debug + 'static + Default, + T: LADatum, +{ + fn name() -> &'static str; + fn alignment_bytes() -> usize { + Self::alignment_items() * T::datum_type().size_of() + } + fn alignment_items() -> usize; + fn nr() -> usize; + fn neutral() -> T; + fn reduce_two(a: T, b: T) -> T; + fn run(vec: &[T], params: Params) -> T; + fn red() -> Box> { + Box::new(ReduceImpl::::new()) + } +} + +#[allow(unused_macros)] +macro_rules! map_reduce_impl_wrap { + ($ti: ident, $func: ident, $nr: expr, $alignment_items: expr, $params: ty, $map_neutral: expr, $reduce_neutral: expr, $run: item, $reduce_two: item) => { + paste! { + #[derive(Copy, Clone, Debug)] + #[allow(non_camel_case_types)] + pub struct $func; + + impl crate::frame::reduce::MapReduceKer<$ti, $params> for $func { + #[inline(always)] + fn name() -> &'static str { + stringify!($func) + } + #[inline(always)] + fn nr() -> usize { + $nr + } + #[inline(always)] + fn alignment_items() -> usize { + $alignment_items + } + #[inline(always)] + fn alignment_bytes() -> usize { + $alignment_items * std::mem::size_of::<$ti>() + } + #[inline(always)] + fn map_neutral() -> $ti { + $map_neutral + } + #[inline(always)] + fn reduce_neutral() -> $ti { + $reduce_neutral + } + $run + $reduce_two + } + } + }; +} + +pub trait MapReduce: Send + Sync + Debug + dyn_clone::DynClone +where + Params: Copy + Send + Sync + Debug + 'static + Default, + T: Copy + Debug + PartialEq + Send + Sync, +{ + fn name(&self) -> &'static str; + fn run(&self, vec: &mut [T]) -> TractResult { + self.run_with_params(vec, Params::default()) + } + fn run_with_params(&self, vec: &mut [T], params: Params) -> TractResult; +} + +dyn_clone::clone_trait_object!( MapReduce where T: Copy, Params: Copy); + +#[derive(Debug, Clone, new)] +pub struct MapReduceImpl +where + T: LADatum, + Params: Copy + Send + Sync + Debug + 'static + Default, + K: MapReduceKer + Clone, +{ + phantom: PhantomData<(K, T, Params)>, +} + +impl MapReduce for MapReduceImpl +where + T: LADatum, + Params: Copy + Send + Sync + Debug + 'static + Default, + K: MapReduceKer + Clone, +{ + fn name(&self) -> &'static str { + K::name() + } + fn run_with_params(&self, vec: &mut [T], params: Params) -> TractResult { + map_reduce_slice_with_alignment( + vec, + |data| K::run(data, params), + K::nr(), + K::alignment_bytes(), + K::map_neutral(), + K::reduce_neutral(), + K::reduce_two, + ) + } +} + +pub trait MapReduceKer: + Send + Sync + Debug + dyn_clone::DynClone + Clone + 'static +where + Params: Copy + Send + Sync + Debug + 'static + Default, + T: LADatum, +{ + fn name() -> &'static str; + fn alignment_bytes() -> usize { + Self::alignment_items() * T::datum_type().size_of() + } + fn alignment_items() -> usize; + fn nr() -> usize; + fn map_neutral() -> T; + fn reduce_neutral() -> T; + fn reduce_two(a: T, b: T) -> T; + fn run(vec: &mut [T], params: Params) -> T; + fn red() -> Box> { + Box::new(MapReduceImpl::::new()) + } +} + +#[cfg(test)] +pub mod test { + use super::*; + use proptest::test_runner::{TestCaseError, TestCaseResult}; + use tract_data::internal::*; + use tract_data::itertools::Itertools; + + pub fn test_reduce, T: LADatum>( + values: &[T], + neutral: T, + reference_reduce: impl Fn(T, T) -> T, + ) -> TestCaseResult { + test_reduce_params::(values, neutral, reference_reduce, ()) + } + + pub fn test_reduce_params, T: LADatum, Params>( + values: &[T], + neutral: T, + reference_reducer: impl Fn(T, T) -> T, + params: Params, + ) -> TestCaseResult + where + Params: Copy + Send + Sync + Debug + 'static + Default, + { + crate::setup_test_logger(); + let op = K::red(); + let expected = values + .iter() + .fold(neutral, |acc, i| reference_reducer(acc, *i)); + let found = values; + let red = op.run_with_params(found, params).unwrap(); + tensor0(red) + .close_enough(&tensor0(expected), true) + .map_err(|e| TestCaseError::fail(e.root_cause().to_string()))?; + Ok(()) + } + + pub fn test_map_reduce, T: LADatum>( + values: &[T], + map_neutral: T, + neutral: T, + reference_map: impl Fn(T) -> T, + reference_reduce: impl Fn(T, T) -> T, + ) -> TestCaseResult { + test_map_reduce_params::( + values, + map_neutral, + neutral, + reference_map, + reference_reduce, + (), + ) + } + + pub fn test_map_reduce_params, T: LADatum, Params>( + values: &[T], + _neutral: T, + map_neutral: T, + reference_map: impl Fn(T) -> T, + reference_reducer: impl Fn(T, T) -> T, + params: Params, + ) -> TestCaseResult + where + Params: Copy + Send + Sync + Debug + 'static + Default, + { + crate::setup_test_logger(); + let op = K::red(); + let mut found = values.to_vec(); + let expected_values = values.iter().copied().map(reference_map).collect_vec(); + let expected_reduced = expected_values + .iter() + .fold(map_neutral, |acc, i| reference_reducer(acc, *i)); + let red = op.run_with_params(&mut found, params).unwrap(); + tensor1(&found) + .close_enough(&tensor1(&expected_values), Approximation::SuperApproximate) + .map_err(|e| TestCaseError::fail(e.root_cause().to_string()))?; + tensor0(red) + .close_enough(&tensor0(expected_reduced), Approximation::SuperApproximate) + .map_err(|e| TestCaseError::fail(e.root_cause().to_string()))?; + Ok(()) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/reduce/softmax.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/reduce/softmax.rs new file mode 100644 index 0000000..0846f82 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/reduce/softmax.rs @@ -0,0 +1,84 @@ +#[cfg(test)] +#[macro_use] +pub mod test { + use crate::LADatum; + use crate::frame::reduce::MapReduceKer; + use num_traits::{AsPrimitive, Float}; + use proptest::test_runner::TestCaseResult; + + #[macro_export] + macro_rules! softmax_l2_frame_tests { + ($cond:expr, $t: ty, $ker:ty) => { + proptest::proptest! { + #[test] + fn prop(xs in proptest::collection::vec(-25f32..25.0, 1..100)) { + if $cond { + $crate::frame::reduce::softmax::test::test_softmax_l2::<$ker, $t>(&*xs).unwrap() + } + } + } + + #[test] + fn single() { + if $cond { + $crate::frame::reduce::softmax::test::test_softmax_l2::<$ker, $t>(&[0.0]).unwrap() + } + } + + #[test] + fn two_zeros() { + if $cond { + $crate::frame::reduce::softmax::test::test_softmax_l2::<$ker, $t>(&[0.0, 0.0]).unwrap() + } + } + + #[test] + fn two_0() { + if $cond { + $crate::frame::reduce::softmax::test::test_softmax_l2::<$ker, $t>(&[ + 16.62555, 21.950674, + ]) + .unwrap() + } + } + + #[test] + fn two_1() { + if $cond { + $crate::frame::reduce::softmax::test::test_softmax_l2::<$ker, $t>(&[0.0f32, 0.38132212]) + .unwrap() + } + } + + #[test] + fn two_missing_max() { + if $cond { + $crate::frame::reduce::softmax::test::test_softmax_l2::<$ker, $t>(&[ + -46.15512, 42.875168, + ]) + .unwrap() + } + } + }; + } + + pub fn test_softmax_l2, T>(values: &[f32]) -> TestCaseResult + where + T: LADatum + Float + AsPrimitive, + f32: AsPrimitive, + { + use crate::generic::reduce::softmax_l2::fast_compact_exp_f32; + crate::setup_test_logger(); + let max = values.iter().max_by(|a, b| a.total_cmp(b)).unwrap(); + let values: Vec = values.iter().copied().map(|x| x.as_()).collect(); + crate::frame::reduce::test::test_map_reduce_params::( + &values, + ::min_value(), + T::zero(), + // |x| (x - max.as_()).exp(), + |x| fast_compact_exp_f32(x.as_() - max).as_(), + |a, b| a + b, + max.as_(), + ) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/reduce/sum.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/reduce/sum.rs new file mode 100644 index 0000000..9d172bc --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/reduce/sum.rs @@ -0,0 +1,54 @@ +#[cfg(test)] +#[macro_use] +pub mod test { + use crate::LADatum; + use crate::frame::reduce::ReduceKer; + use num_traits::{AsPrimitive, Float, Zero}; + use proptest::test_runner::TestCaseResult; + + #[macro_export] + macro_rules! sum_frame_tests { + ($cond:expr, $t: ty, $ker:ty) => { + proptest::proptest! { + #[test] + fn prop(xs in proptest::collection::vec(-25_isize..25, 0..100)) { + if $cond { + let xs_float = xs.into_iter().map(|it| it as f32).collect::>(); + $crate::frame::reduce::sum::test::test_sum::<$ker, $t>(&*xs_float).unwrap() + } + } + } + + #[test] + fn empty() { + if $cond { + $crate::frame::reduce::sum::test::test_sum::<$ker, $t>(&[]).unwrap() + } + } + + #[test] + fn simple() { + if $cond { + $crate::frame::reduce::sum::test::test_sum::<$ker, $t>(&[1.0, 2.0]).unwrap() + } + } + #[test] + fn multiple_tile() { + if $cond { + $crate::frame::reduce::sum::test::test_sum::<$ker, $t>(&[1.0; 35]).unwrap() + } + } + }; + } + + pub fn test_sum(values: &[f32]) -> TestCaseResult + where + K: ReduceKer, + f32: AsPrimitive, + T: LADatum + Float + Zero + AsPrimitive, + { + crate::setup_test_logger(); + let values: Vec = values.iter().copied().map(|x| x.as_()).collect(); + crate::frame::reduce::test::test_reduce::(&values, ::zero(), |a, b| a + b) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/sigmoid.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/sigmoid.rs new file mode 100644 index 0000000..67f31ef --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/sigmoid.rs @@ -0,0 +1,96 @@ +macro_rules! sigmoid_impl { + ($ti: ident, $func: ident, $nr: expr, $alignment_items: expr, $cond: expr) => { + ew_impl!($ti, $func, $nr, $alignment_items); + #[cfg(test)] + paste! { + mod [] { + use super::*; + sigmoid_frame_tests!($cond, $ti, $func); + } + } + }; +} + +#[cfg(test)] +#[macro_use] +pub mod test { + use crate::{LADatum, frame::element_wise::*}; + use num_traits::{AsPrimitive, Float}; + use proptest::test_runner::TestCaseResult; + + #[macro_export] + macro_rules! sigmoid_frame_tests { + ($cond:expr, $t: ty, $ker:ty) => { + proptest::proptest! { + #[test] + fn sigmoid(xs in proptest::collection::vec(-25f32..25.0, 0..100)) { + if $cond { + $crate::frame::sigmoid::test::test_sigmoid::<$ker, $t>(&*xs).unwrap() + } + } + } + + #[test] + fn sigmoid_4_magic() { + if $cond { + $crate::frame::sigmoid::test::test_sigmoid::<$ker, $t>(&[ + 0f32, -20.0, 20.0, 0.0, + ]) + .unwrap() + } + } + + #[test] + fn sigmoid_4zeros() { + if $cond { + $crate::frame::sigmoid::test::test_sigmoid::<$ker, $t>(&[0.0; 4]).unwrap(); + } + } + + #[test] + fn sigmoid_20_ones() { + if $cond { + $crate::frame::sigmoid::test::test_sigmoid::<$ker, $t>(&[1.0; 20]).unwrap(); + } + } + + #[test] + fn sigmoid_18_zeros() { + if $cond { + $crate::frame::sigmoid::test::test_sigmoid::<$ker, $t>(&[0.0; 18]).unwrap(); + } + } + + #[test] + fn sigmoid_asymptots() { + use tract_data::internal::*; + use $crate::frame::element_wise::*; + if $cond { + let mut input: Vec<$t> = [-100f32, 100f32] + .iter() + .map(|x| >::as_(*x)) + .collect(); + let expected: Vec<$t> = [-0f32, 1f32] + .iter() + .map(|x| >::as_(*x)) + .collect(); + <$ker>::ew().run(&mut input).unwrap(); + tensor1(&input) + .close_enough(&tensor1(&expected), Approximation::Close) + .unwrap(); + } + } + }; + } + + pub fn test_sigmoid, T: LADatum + Float>(values: &[f32]) -> TestCaseResult + where + f32: AsPrimitive, + { + crate::setup_test_logger(); + let values: Vec = values.iter().copied().map(|x| x.as_()).collect(); + crate::frame::element_wise::test::test_element_wise::(&values, |x| { + (1f32).as_() / (1f32.as_() + (-x).exp()) + }) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/silu.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/silu.rs new file mode 100644 index 0000000..5d51154 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/silu.rs @@ -0,0 +1,58 @@ +#[allow(unused_macros)] +macro_rules! silu_impl { + ($ti: ident, $func: ident, $nr: expr, $alignment_items: expr, $cond: expr) => { + ew_impl!($ti, $func, $nr, $alignment_items); + #[cfg(test)] + paste! { + mod [] { + use super::*; + silu_frame_tests!($cond, $ti, $func); + } + } + }; +} + +#[cfg(test)] +#[macro_use] +pub mod test { + use crate::LADatum; + use crate::frame::element_wise::*; + use num_traits::{AsPrimitive, Float}; + use proptest::test_runner::TestCaseResult; + + #[macro_export] + macro_rules! silu_frame_tests { + ($cond:expr, $t: ty, $ker:ty) => { + proptest::proptest! { + #[test] + fn prop(xs in proptest::collection::vec(-10f32..10.0, 0..100)) { + if $cond { + $crate::frame::silu::test::test_silu::<$ker, $t>(&*xs).unwrap() + } + } + } + #[test] + fn trivial() { + if $cond { + $crate::frame::silu::test::test_silu::<$ker, $t>(&[-5f32, -1.0, 0.0, 1.0, 5.0]) + .unwrap(); + } + } + }; + } + + pub fn test_silu, T: LADatum + Float>(values: &[f32]) -> TestCaseResult + where + f32: AsPrimitive, + { + let data = tract_data::prelude::tensor1(values); + let data = data.cast_to::().unwrap(); + let data = data.try_as_plain().unwrap().as_slice::().unwrap(); + crate::frame::element_wise::test::test_element_wise::(data, |x: T| { + let one: T = 1f32.as_(); + let neg_x = T::zero() - x; + let sigmoid = one / (one + neg_x.exp()); + x * sigmoid + }) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/tanh.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/tanh.rs new file mode 100644 index 0000000..815d593 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/tanh.rs @@ -0,0 +1,101 @@ +macro_rules! tanh_impl { + ($ti: ident, $func: ident, $nr: expr, $alignment_items: expr, $cond: expr) => { + ew_impl!($ti, $func, $nr, $alignment_items); + #[cfg(test)] + paste! { + mod [] { + use super::*; + tanh_frame_tests!($cond, $ti, $func); + } + } + }; +} + +#[cfg(test)] +#[macro_use] +pub mod test { + use crate::LADatum; + use crate::frame::element_wise::*; + use num_traits::AsPrimitive; + use num_traits::float::Float; + use proptest::test_runner::TestCaseResult; + + #[macro_export] + macro_rules! tanh_frame_tests { + ($cond:expr, $t:ty, $ker:ty) => { + proptest::proptest! { + #[test] + fn tanh(xs in proptest::collection::vec(-25f32..25.0, 0..100)) { + if $cond { + $crate::frame::tanh::test::test_tanh::<$ker, $t>(&*xs).unwrap() + } + } + } + + #[test] + fn tanh_4_magic() { + if $cond { + $crate::frame::tanh::test::test_tanh::<$ker, $t>(&[0f32, -20.0, 20.0, 0.0]) + .unwrap() + } + } + + #[test] + fn tanh_4zeros() { + if $cond { + $crate::frame::tanh::test::test_tanh::<$ker, $t>(&[0.0; 4]).unwrap(); + } + } + + #[test] + fn tanh_20_ones() { + if $cond { + $crate::frame::tanh::test::test_tanh::<$ker, $t>(&[1.0; 20]).unwrap(); + } + } + + #[test] + fn tanh_18_zeros() { + if $cond { + $crate::frame::tanh::test::test_tanh::<$ker, $t>(&[0.0; 18]).unwrap(); + } + } + + #[test] + fn tanh_foo() { + if $cond { + $crate::frame::tanh::test::test_tanh::<$ker, $t>(&[0.67503357]).unwrap(); + } + } + + #[test] + fn tanh_asymptots() { + use tract_data::internal::*; + use $crate::frame::element_wise::*; + if $cond { + let mut input: Vec<$t> = [-100f32, 100f32] + .iter() + .map(|x| >::as_(*x)) + .collect(); + let expected: Vec<$t> = [-1f32, 1f32] + .iter() + .map(|x| >::as_(*x)) + .collect(); + <$ker>::ew().run(&mut input).unwrap(); + tensor1(&input) + .close_enough(&tensor1(&expected), Approximation::Close) + .unwrap(); + } + } + }; + } + + pub fn test_tanh, T: LADatum + Float>(values: &[f32]) -> TestCaseResult + where + f32: AsPrimitive, + { + crate::setup_test_logger(); + let values: Vec = values.iter().copied().map(|x| x.as_()).collect(); + crate::frame::element_wise::test::test_element_wise::(&values, |x| x.tanh()) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/unicast.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/unicast.rs new file mode 100644 index 0000000..b004561 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/unicast.rs @@ -0,0 +1,237 @@ +use std::fmt::Debug; +use std::marker::PhantomData; + +use tract_data::TractResult; +use tract_data::internal::TensorView; + +use crate::frame::element_wise_helper::TempBuffer; +use crate::{LADatum, LinalgFn}; + +macro_rules! unicast_impl_wrap { + ($ti: ident, $func: ident, $nr: expr, $alignment_items: expr, $run: item) => { + paste! { + #[derive(Copy, Clone, Debug)] + #[allow(non_camel_case_types)] + pub struct $func; + + impl crate::frame::unicast::UnicastKer<$ti> for $func { + #[inline(always)] + fn name() -> &'static str { + stringify!($func) + } + #[inline(always)] + fn nr() -> usize { + $nr + } + #[inline(always)] + fn alignment_items() -> usize { + $alignment_items + } + $run + } + } + }; +} + +pub trait Unicast: Send + Sync + Debug + dyn_clone::DynClone +where + T: Copy + Debug + PartialEq + Send + Sync, +{ + fn name(&self) -> &'static str; + fn run(&self, a: &mut [T], b: &[T]) -> TractResult<()>; +} + +dyn_clone::clone_trait_object!( Unicast where T: Copy); + +#[derive(Debug, Clone, new)] +pub struct UnicastImpl +where + T: LADatum, + K: UnicastKer + Clone, +{ + phantom: PhantomData<(K, T)>, +} + +impl UnicastImpl +where + T: LADatum, + K: UnicastKer + Clone, +{ +} +impl Unicast for UnicastImpl +where + T: LADatum, + K: UnicastKer + Clone, +{ + fn name(&self) -> &'static str { + K::name() + } + fn run(&self, a: &mut [T], b: &[T]) -> TractResult<()> { + unicast_with_alignment(a, b, |a, b| K::run(a, b), K::nr(), K::alignment_bytes()) + } +} + +pub trait UnicastKer: Send + Sync + Debug + dyn_clone::DynClone + Clone + 'static +where + T: LADatum, +{ + fn name() -> &'static str; + fn alignment_bytes() -> usize { + Self::alignment_items() * T::datum_type().size_of() + } + fn alignment_items() -> usize; + fn nr() -> usize; + fn run(a: &mut [T], b: &[T]); + fn bin() -> Box { + Box::new(|a: &mut TensorView, b: &TensorView| { + let a_slice = a.as_slice_mut()?; + let b_slice = b.as_slice()?; + UnicastImpl::::new().run(a_slice, b_slice) + }) + } +} + +std::thread_local! { + static TMP: std::cell::RefCell<(TempBuffer, TempBuffer)> = std::cell::RefCell::new((TempBuffer::default(), TempBuffer::default())); +} + +pub(crate) fn unicast_with_alignment( + a: &mut [T], + b: &[T], + f: impl Fn(&mut [T], &[T]), + nr: usize, + alignment_bytes: usize, +) -> TractResult<()> +where + T: LADatum, +{ + if a.is_empty() { + return Ok(()); + } + unsafe { + TMP.with(|buffers| { + let mut buffers = buffers.borrow_mut(); + buffers.0.ensure(nr * T::datum_type().size_of(), alignment_bytes); + buffers.1.ensure(nr * T::datum_type().size_of(), alignment_bytes); + let tmp_a = std::slice::from_raw_parts_mut(buffers.0.buffer as *mut T, nr); + let tmp_b = std::slice::from_raw_parts_mut(buffers.1.buffer as *mut T, nr); + let mut compute_via_temp_buffer = |a: &mut [T], b: &[T]| { + tmp_a[..a.len()].copy_from_slice(a); + tmp_b[..b.len()].copy_from_slice(b); + f(tmp_a, tmp_b); + a.copy_from_slice(&tmp_a[..a.len()]) + }; + + let mut num_element_processed = 0; + let a_prefix_len = a.as_ptr().align_offset(alignment_bytes).min(a.len()); + let b_prefix_len = b.as_ptr().align_offset(alignment_bytes).min(b.len()); + assert!( + a_prefix_len == b_prefix_len, + "Both inputs should be of the same alignement, got {a_prefix_len:?}, {b_prefix_len:?}" + ); + let mut applied_prefix_len = 0; + if a_prefix_len > 0 { + // Incomplete tile needs to be created to process unaligned data. + let sub_a = &mut a[..a_prefix_len]; + let sub_b = &b[..a_prefix_len]; + compute_via_temp_buffer(sub_a, sub_b); + num_element_processed += a_prefix_len; + applied_prefix_len = a_prefix_len; + } + + let num_complete_tiles = (a.len() - applied_prefix_len) / nr; + if num_complete_tiles > 0 { + // Process all tiles that are complete. + let sub_a = &mut a[applied_prefix_len..][..(num_complete_tiles * nr)]; + let sub_b = &b[applied_prefix_len..][..(num_complete_tiles * nr)]; + f(sub_a, sub_b); + num_element_processed += num_complete_tiles * nr; + } + + if num_element_processed < a.len() { + // Incomplete tile needs to be created to process remaining elements. + compute_via_temp_buffer( + &mut a[num_element_processed..], + &b[num_element_processed..], + ); + } + }) + } + Ok(()) +} + +#[cfg(test)] +#[macro_use] +pub mod test { + use super::*; + use crate::LADatum; + use proptest::test_runner::{TestCaseError, TestCaseResult}; + use tract_data::internal::*; + use tract_num_traits::{AsPrimitive, Float}; + + pub fn test_unicast, T: LADatum>( + a: &mut [T], + b: &[T], + reference: impl Fn(T, T) -> T, + ) -> TestCaseResult { + crate::setup_test_logger(); + let op = UnicastImpl::::new(); + let expected = a + .iter() + .zip(b.iter()) + .map(|(a, b)| (reference)(*a, *b)) + .collect::>(); + op.run(a, b).unwrap(); + tensor1(a) + .close_enough(&tensor1(&expected), true) + .map_err(|e| TestCaseError::fail(e.root_cause().to_string()))?; + Ok(()) + } + + pub fn test_unicast_t, T: LADatum + Float>( + a: &[f32], + b: &[f32], + func: impl Fn(T, T) -> T, + ) -> TestCaseResult + where + f32: AsPrimitive, + { + crate::setup_test_logger(); + let vec_a: Vec = a.iter().copied().map(|x| x.as_()).collect(); + // We allocate a tensor to ensure allocation is done with alignement + let mut a = unsafe { Tensor::from_slice_align(vec_a.as_slice(), vector_size()).unwrap() }; + let vec_b: Vec = b.iter().copied().map(|x| x.as_()).collect(); + // We allocate a tensor to ensure allocation is done with alignement + let b = unsafe { Tensor::from_slice_align(vec_b.as_slice(), vector_size()).unwrap() }; + crate::frame::unicast::test::test_unicast::( + a.try_as_plain_mut().unwrap().as_slice_mut::().unwrap(), + b.try_as_plain().unwrap().as_slice::().unwrap(), + func, + ) + } + + #[macro_export] + macro_rules! unicast_frame_tests { + ($cond:expr, $t: ty, $ker:ty, $func:expr) => { + pastey::paste! { + proptest::proptest! { + #[test] + fn []( + (a, b) in (0..100_usize).prop_flat_map(|len| (vec![-25f32..25.0; len], vec![-25f32..25.0; len])) + ) { + if $cond { + $crate::frame::unicast::test::test_unicast_t::<$ker, $t>(&*a, &*b, $func).unwrap() + } + } + } + + #[test] + fn []() { + if $cond { + $crate::frame::unicast::test::test_unicast_t::<$ker, $t>(&[], &[], $func).unwrap() + } + } + } + }; + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/weights.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/weights.rs new file mode 100644 index 0000000..1e2d123 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/frame/weights.rs @@ -0,0 +1,80 @@ +use std::fmt::Debug; +use tract_data::prelude::DatumType; + +use crate::block_quant::{BlockQuant, PackedBlockQuantFormat}; + +use crate::mmm::MMMInputFormat; +use crate::pack::PackedFormat; + +#[derive(Clone)] +pub enum WeightType { + Plain(DatumType), + BlockQuant(Box), +} + +impl From for WeightType { + fn from(value: DatumType) -> Self { + match value { + DatumType::F16 => WeightType::Plain(DatumType::F16), + DatumType::F32 => WeightType::Plain(DatumType::F32), + DatumType::F64 => WeightType::Plain(DatumType::F64), + DatumType::I32 => WeightType::Plain(DatumType::I32), + DatumType::I8 | DatumType::QI8(_) => WeightType::Plain(DatumType::I8), + DatumType::U8 | DatumType::QU8(_) => WeightType::Plain(DatumType::U8), + _ => panic!("Can't build a WeightType from {value:?}"), + } + } +} + +impl From> for WeightType { + fn from(value: Box) -> Self { + (&*value).into() + } +} + +impl From<&dyn MMMInputFormat> for WeightType { + fn from(value: &dyn MMMInputFormat) -> Self { + if let Some(pf) = value.downcast_ref::() { + WeightType::Plain(pf.dt) + } else if let Some(pbqf) = value.downcast_ref::() { + WeightType::BlockQuant(dyn_clone::clone_box(&*pbqf.bq)) + } else { + todo!() + } + } +} + +impl PartialEq for WeightType { + fn eq(&self, other: &Self) -> bool { + use WeightType::*; + match (self, other) { + (Plain(a), Plain(b)) => a == b, + (BlockQuant(a), BlockQuant(b)) => a == b, + _ => false, + } + } +} + +impl From for WeightType { + fn from(value: BQ) -> Self { + WeightType::BlockQuant(dyn_clone::clone_box(&value)) + } +} + +impl Debug for WeightType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::Plain(p) => write!(f, "{p:?}"), + Self::BlockQuant(bq) => write!(f, "{bq:?}"), + } + } +} + +impl WeightType { + pub fn as_dt(&self) -> Option { + match self { + WeightType::Plain(dt) => Some(*dt), + _ => None, + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic.rs new file mode 100644 index 0000000..9d6d32e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic.rs @@ -0,0 +1,134 @@ +pub mod by_scalar; +pub mod erf; +pub mod gelu; +pub mod hardswish; +pub mod leaky_relu; +pub mod lut; +pub mod mmm; +pub mod reduce; +pub mod rms_norm; +pub mod rounding; +pub mod sigmoid; +pub mod silu; +pub mod tanh; +pub mod unicast; + +use tract_data::prelude::DatumType; + +use crate::by_scalar::ByScalarKer; +use crate::unicast::UnicastKer; +use crate::{BinOp, LinalgRegistry}; + +pub use self::by_scalar::{HMulByScalar8, SMulByScalar4}; +pub use self::erf::SErf4; +pub use self::gelu::{HGelu8, SGelu4}; +pub use self::hardswish::{HHardSwish8, SHardSwish4}; +pub use self::leaky_relu::{HLeakyRelu8, SLeakyRelu4}; +pub use self::lut::GenericLut8; +pub use self::reduce::softmax_l2::SSoftMaxL2; +pub use self::rounding::{ScaleShiftAndRound, Scaler}; +pub use self::sigmoid::{HSigmoid8, SSigmoid4}; +pub use self::silu::{HSiLU8, SSiLU4}; +pub use self::tanh::{HTanh8, STanh4}; + +pub(crate) fn register_all_unicast(registry: &mut LinalgRegistry) { + registry.insert( + (BinOp::Mul, DatumType::F32), + Box::new(|| unicast::SUnicastMul4::bin()), + ); + registry.insert( + (BinOp::Mul, DatumType::F16), + Box::new(|| unicast::HUnicastMul8::bin()), + ); + registry.insert( + (BinOp::Add, DatumType::F32), + Box::new(|| unicast::SUnicastAdd4::bin()), + ); + registry.insert( + (BinOp::Add, DatumType::F16), + Box::new(|| unicast::HUnicastAdd8::bin()), + ); + registry.insert( + (BinOp::Sub, DatumType::F32), + Box::new(|| unicast::SUnicastSub4::bin()), + ); + registry.insert( + (BinOp::Sub, DatumType::F16), + Box::new(|| unicast::HUnicastSub8::bin()), + ); + registry.insert( + (BinOp::SubF, DatumType::F32), + Box::new(|| unicast::SUnicastSubF4::bin()), + ); + registry.insert( + (BinOp::SubF, DatumType::F16), + Box::new(|| unicast::HUnicastSubF8::bin()), + ); + registry.insert( + (BinOp::Min, DatumType::F32), + Box::new(|| unicast::SUnicastMin4::bin()), + ); + registry.insert( + (BinOp::Min, DatumType::F16), + Box::new(|| unicast::HUnicastMin8::bin()), + ); + registry.insert( + (BinOp::Max, DatumType::F32), + Box::new(|| unicast::SUnicastMax4::bin()), + ); + registry.insert( + (BinOp::Max, DatumType::F16), + Box::new(|| unicast::HUnicastMax8::bin()), + ); +} + +pub(crate) fn register_all_by_scalar(registry: &mut LinalgRegistry) { + registry.insert( + (BinOp::Mul, DatumType::F32), + Box::new(|| by_scalar::SMulByScalar4::bin()), + ); + registry.insert( + (BinOp::Mul, DatumType::F16), + Box::new(|| by_scalar::HMulByScalar8::bin()), + ); + registry.insert( + (BinOp::Add, DatumType::F32), + Box::new(|| by_scalar::SAddByScalar4::bin()), + ); + registry.insert( + (BinOp::Add, DatumType::F16), + Box::new(|| by_scalar::HAddByScalar8::bin()), + ); + registry.insert( + (BinOp::Sub, DatumType::F32), + Box::new(|| by_scalar::SSubByScalar4::bin()), + ); + registry.insert( + (BinOp::Sub, DatumType::F16), + Box::new(|| by_scalar::HSubByScalar8::bin()), + ); + registry.insert( + (BinOp::SubF, DatumType::F32), + Box::new(|| by_scalar::SSubFByScalar4::bin()), + ); + registry.insert( + (BinOp::SubF, DatumType::F16), + Box::new(|| by_scalar::HSubFByScalar8::bin()), + ); + registry.insert( + (BinOp::Min, DatumType::F32), + Box::new(|| by_scalar::SMinByScalar4::bin()), + ); + registry.insert( + (BinOp::Min, DatumType::F16), + Box::new(|| by_scalar::HMinByScalar8::bin()), + ); + registry.insert( + (BinOp::Max, DatumType::F32), + Box::new(|| by_scalar::SMaxByScalar4::bin()), + ); + registry.insert( + (BinOp::Max, DatumType::F16), + Box::new(|| by_scalar::HMaxByScalar8::bin()), + ); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/by_scalar.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/by_scalar.rs new file mode 100644 index 0000000..ca7ae0a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/by_scalar.rs @@ -0,0 +1,181 @@ +use tract_data::internal::f16; + +by_scalar_impl_wrap!( + f32, + SMulByScalar4, + 4, + 4, + f32, + fn run(x: &mut [f32], s: f32) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px *= s) + } +); + +by_scalar_impl_wrap!( + f32, + SAddByScalar4, + 4, + 4, + f32, + fn run(x: &mut [f32], s: f32) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px += s) + } +); + +by_scalar_impl_wrap!( + f32, + SSubByScalar4, + 4, + 4, + f32, + fn run(x: &mut [f32], s: f32) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px -= s) + } +); + +by_scalar_impl_wrap!( + f32, + SSubFByScalar4, + 4, + 4, + f32, + fn run(x: &mut [f32], s: f32) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px = s - *px) + } +); + +by_scalar_impl_wrap!( + f32, + SMinByScalar4, + 4, + 4, + f32, + fn run(x: &mut [f32], s: f32) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px = px.min(s)) + } +); + +by_scalar_impl_wrap!( + f32, + SMaxByScalar4, + 4, + 4, + f32, + fn run(x: &mut [f32], s: f32) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px = px.max(s)) + } +); + +#[cfg(test)] +#[macro_use] +pub mod mul_by_scalar_f32 { + use super::*; + by_scalar_frame_tests!(true, f32, SMulByScalar4, |a, b| a * b); + by_scalar_frame_tests!(true, f32, SAddByScalar4, |a, b| a + b); + by_scalar_frame_tests!(true, f32, SSubByScalar4, |a, b| a - b); + by_scalar_frame_tests!(true, f32, SSubFByScalar4, |a, b| b - a); + by_scalar_frame_tests!(true, f32, SMinByScalar4, |a, b| a.min(b)); + by_scalar_frame_tests!(true, f32, SMaxByScalar4, |a, b| a.max(b)); +} + +by_scalar_impl_wrap!( + f16, + HMulByScalar8, + 8, + 8, + f16, + fn run(x: &mut [f16], s: f16) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px *= s) + } +); + +by_scalar_impl_wrap!( + f16, + HAddByScalar8, + 8, + 8, + f16, + fn run(x: &mut [f16], s: f16) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px += s) + } +); + +by_scalar_impl_wrap!( + f16, + HSubByScalar8, + 8, + 8, + f16, + fn run(x: &mut [f16], s: f16) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px -= s) + } +); + +by_scalar_impl_wrap!( + f16, + HSubFByScalar8, + 8, + 8, + f16, + fn run(x: &mut [f16], s: f16) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px = s - *px) + } +); + +by_scalar_impl_wrap!( + f16, + HMinByScalar8, + 8, + 8, + f16, + fn run(x: &mut [f16], s: f16) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px = px.min(s)) + } +); + +by_scalar_impl_wrap!( + f16, + HMaxByScalar8, + 8, + 8, + f16, + fn run(x: &mut [f16], s: f16) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px = px.max(s)) + } +); + +#[cfg(test)] +#[macro_use] +pub mod mul_by_scalar_f16 { + use super::*; + by_scalar_frame_tests!(true, f16, HMulByScalar8, |a, b| a * b); + by_scalar_frame_tests!(true, f16, HAddByScalar8, |a, b| a + b); + by_scalar_frame_tests!(true, f16, HSubByScalar8, |a, b| a - b); + by_scalar_frame_tests!(true, f16, HSubFByScalar8, |a, b| b - a); + by_scalar_frame_tests!(true, f16, HMinByScalar8, |a, b| a.min(b)); + by_scalar_frame_tests!(true, f16, HMaxByScalar8, |a, b| a.max(b)); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/erf.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/erf.rs new file mode 100644 index 0000000..30358f3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/erf.rs @@ -0,0 +1,57 @@ +use crate::element_wise::ElementWiseKer; + +#[allow(non_upper_case_globals)] +#[allow(clippy::excessive_precision)] +fn serf(x: &mut f32) { + const a1: f32 = 0.0705230784; + const a2: f32 = 0.0422820123; + const a3: f32 = 0.0092705272; + const a4: f32 = 0.0001520143; + const a5: f32 = 0.0002765672; + const a6: f32 = 0.0000430638; + + let signum = x.signum(); + let abs = x.abs(); + let y = a6 * abs; + let y = (a5 + y) * abs; + let y = (a4 + y) * abs; + let y = (a3 + y) * abs; + let y = (a2 + y) * abs; + let y = (a1 + y) * abs; + let y = 1.0 - (y + 1.0).powi(16).recip(); + + *x = y.copysign(signum) +} + +#[derive(Clone, Debug)] +pub struct SErf4; + +impl ElementWiseKer for SErf4 { + fn name() -> &'static str { + "generic" + } + + fn alignment_items() -> usize { + 16 + } + + fn alignment_bytes() -> usize { + 16 + } + + fn nr() -> usize { + 4 + } + + fn run(x: &mut [f32], _: ()) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(serf) + } +} + +#[cfg(test)] +mod test_serf4 { + use super::*; + crate::erf_frame_tests!(true, f32, SErf4); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/gelu.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/gelu.rs new file mode 100644 index 0000000..f59179d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/gelu.rs @@ -0,0 +1,92 @@ +#![allow(clippy::excessive_precision)] +use crate::frame::element_wise::ElementWiseKer; +use tract_data::internal::*; + +// Tanh-form GELU approximation matching tract's GeluApproximate (pow=3, the +// canonical Hendrycks-Gimpel/Open-AI form): +// +// gelu(x) = 0.5 * x * (1 + tanh(sqrt(2/pi) * (x + 0.044715 * x^3))) +// +// The fast variant (pow=2) is not exposed here; the graph op falls back to +// scalar when fast_impl=true. + +const SQRT_2_OVER_PI: f32 = 0.7978845608028654; +const COEF: f32 = 0.044715; + +#[derive(Clone, Debug)] +pub struct SGelu4; + +impl ElementWiseKer for SGelu4 { + fn name() -> &'static str { + "generic" + } + + fn alignment_bytes() -> usize { + 16 + } + + fn alignment_items() -> usize { + 4 + } + + fn nr() -> usize { + 4 + } + + fn run(x: &mut [f32], _: ()) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| { + let v = *px; + let inner = SQRT_2_OVER_PI * (v + COEF * v * v * v); + *px = 0.5 * v * (1.0 + inner.tanh()); + }); + } +} + +#[derive(Clone, Debug)] +pub struct HGelu8; + +impl ElementWiseKer for HGelu8 { + fn name() -> &'static str { + "generic" + } + + fn alignment_bytes() -> usize { + 16 + } + + fn alignment_items() -> usize { + 4 + } + + fn nr() -> usize { + 8 + } + + fn run(x: &mut [f16], _: ()) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| { + let v = px.to_f32(); + let inner = SQRT_2_OVER_PI * (v + COEF * v * v * v); + *px = f16::from_f32(0.5 * v * (1.0 + inner.tanh())); + }); + } +} + +#[cfg(test)] +#[macro_use] +pub mod s { + gelu_frame_tests!(true, f32, crate::generic::gelu::SGelu4); +} + +#[cfg(test)] +#[macro_use] +pub mod h { + gelu_frame_tests!( + true, + tract_data::internal::f16, + crate::generic::gelu::HGelu8 + ); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/hardswish.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/hardswish.rs new file mode 100644 index 0000000..287c298 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/hardswish.rs @@ -0,0 +1,84 @@ +#![allow(clippy::excessive_precision)] +use crate::frame::element_wise::ElementWiseKer; +use tract_data::internal::*; +use tract_num_traits::Zero; + +#[derive(Clone, Debug)] +pub struct SHardSwish4; + +impl ElementWiseKer for SHardSwish4 { + fn name() -> &'static str { + "generic" + } + + fn alignment_bytes() -> usize { + 16 + } + + fn alignment_items() -> usize { + 4 + } + + fn nr() -> usize { + 4 + } + + fn run(x: &mut [f32], _: ()) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + const INV6: f32 = 1.0 / 6.0; + x.iter_mut().for_each(|px| { + let relu6 = ((*px + 3.0).min(6.0)).max(0.0); + *px = *px * relu6 * INV6; + }); + } +} + +#[derive(Clone, Debug)] +pub struct HHardSwish8; + +impl ElementWiseKer for HHardSwish8 { + fn name() -> &'static str { + "generic" + } + + fn alignment_bytes() -> usize { + 16 + } + + fn alignment_items() -> usize { + 4 + } + + fn nr() -> usize { + 8 + } + + fn run(x: &mut [f16], _: ()) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + let three = f16::from_f32(3.0); + let six = f16::from_f32(6.0); + let inv6 = f16::from_f32(1.0 / 6.0); + x.iter_mut().for_each(|px| { + let relu6 = ((*px + three).min(six)).max(f16::zero()); + *px = *px * relu6 * inv6; + }); + } +} + +#[cfg(test)] +#[macro_use] +pub mod s { + hardswish_frame_tests!(true, f32, crate::generic::hardswish::SHardSwish4); +} + +#[cfg(test)] +#[macro_use] +pub mod h { + hardswish_frame_tests!( + true, + tract_data::internal::f16, + crate::generic::hardswish::HHardSwish8 + ); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/leaky_relu.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/leaky_relu.rs new file mode 100644 index 0000000..a114dc0 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/leaky_relu.rs @@ -0,0 +1,76 @@ +#![allow(clippy::excessive_precision)] +use crate::frame::element_wise::ElementWiseKer; +use tract_data::internal::*; +use tract_num_traits::Zero; + +#[derive(Clone, Debug)] +pub struct SLeakyRelu4; + +impl ElementWiseKer for SLeakyRelu4 { + fn name() -> &'static str { + "generic" + } + + fn alignment_bytes() -> usize { + 16 + } + + fn alignment_items() -> usize { + 4 + } + + fn nr() -> usize { + 4 + } + + fn run(x: &mut [f32], alpha: f32) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut() + .for_each(|px| *px = if *px < 0. { *px * alpha } else { *px }); + } +} + +#[derive(Clone, Debug)] +pub struct HLeakyRelu8; + +impl ElementWiseKer for HLeakyRelu8 { + fn name() -> &'static str { + "generic" + } + + fn alignment_bytes() -> usize { + 16 + } + + fn alignment_items() -> usize { + 4 + } + + fn nr() -> usize { + 8 + } + + fn run(x: &mut [f16], alpha: f16) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut() + .for_each(|px| *px = if *px < f16::zero() { *px * alpha } else { *px }) + } +} + +#[cfg(test)] +#[macro_use] +pub mod s { + leaky_relu_frame_tests!(true, f32, crate::generic::leaky_relu::SLeakyRelu4); +} + +#[cfg(test)] +#[macro_use] +pub mod h { + leaky_relu_frame_tests!( + true, + tract_data::internal::f16, + crate::generic::leaky_relu::HLeakyRelu8 + ); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/lut.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/lut.rs new file mode 100644 index 0000000..b9023bd --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/lut.rs @@ -0,0 +1,47 @@ +use crate::frame::lut::LutKer; + +#[derive(Clone, Debug, Hash)] +pub struct GenericLut8; + +impl LutKer for GenericLut8 { + fn name() -> &'static str { + "generic" + } + + fn input_alignment_bytes() -> usize { + 1 + } + + fn table_alignment_bytes() -> usize { + 1 + } + + fn n() -> usize { + 8 + } + + unsafe fn run(buf: *mut u8, len: usize, table: *const u8) { + unsafe { + debug_assert!(len % Self::n() == 0); + debug_assert!(buf as usize % Self::input_alignment_bytes() == 0); + debug_assert!(table as usize % Self::table_alignment_bytes() == 0); + for i in 0..((len / 8) as isize) { + let ptr = buf.offset(8 * i); + *ptr.offset(0) = *table.offset(*ptr.offset(0) as isize); + *ptr.offset(1) = *table.offset(*ptr.offset(1) as isize); + *ptr.offset(2) = *table.offset(*ptr.offset(2) as isize); + *ptr.offset(3) = *table.offset(*ptr.offset(3) as isize); + *ptr.offset(4) = *table.offset(*ptr.offset(4) as isize); + *ptr.offset(5) = *table.offset(*ptr.offset(5) as isize); + *ptr.offset(6) = *table.offset(*ptr.offset(6) as isize); + *ptr.offset(7) = *table.offset(*ptr.offset(7) as isize); + } + } + } +} + +#[cfg(test)] +#[macro_use] +pub mod test { + lut_frame_tests!(true, crate::generic::GenericLut8); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/mmm.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/mmm.rs new file mode 100644 index 0000000..d71c348 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/mmm.rs @@ -0,0 +1,455 @@ +#![allow(clippy::needless_range_loop)] +use num_traits::AsPrimitive; + +use tract_data::prelude::f16; +use tract_data::prelude::*; + +use super::*; +use crate::frame::block_quant::{BlockQuant, NibbleReader, PackedBlockQuantFormat, Q4_0}; +use crate::frame::mmm::*; +use crate::{LADatum, Ops, has_fp16}; + +macro_rules! scalar { + ($ab: expr, $m: expr, $f: expr) => { + for i in 0..$ab.len() { + for j in 0..$ab[0].len() { + $ab[i][j] = $f($m, $ab[i][j]) + } + } + }; +} + +macro_rules! per_row { + ($ab: expr, $m: expr, $f: expr) => { + for i in 0..$ab.len() { + for j in 0..$ab[0].len() { + $ab[i][j] = $f(*$m.add(i), $ab[i][j]) + } + } + }; +} + +macro_rules! per_col { + ($ab: expr, $m: expr, $f: expr) => { + for i in 0..$ab.len() { + for j in 0..$ab[0].len() { + $ab[i][j] = $f(*$m.add(j), $ab[i][j]) + } + } + }; +} + +unsafe fn add_mat_mul( + pa: *const u8, + pb: *const u8, + k: usize, + ab: &mut [[TI; NR]; MR], +) where + TA: LADatum + AsPrimitive, + TB: LADatum + AsPrimitive, + TI: LADatum, +{ + unsafe { + let a = pa as *const TA; + let b = pb as *const TB; + for ik in 0..k { + let a = std::slice::from_raw_parts(a.add(MR * ik), MR); + let b = std::slice::from_raw_parts(b.add(NR * ik), NR); + for i in 0..MR { + for j in 0..NR { + ab[i][j] += a[i].as_() * b[j].as_(); + } + } + } + } +} + +unsafe fn add_mat_mul_pq40( + pa: *const u8, + pb: *const u8, + k: usize, + ab: &mut [[TI; NR]; MR], +) where + TI: LADatum, + f16: AsPrimitive, + TB: AsPrimitive, + i8: AsPrimitive, +{ + unsafe { + assert!(k % Q4_0.block_len() == 0); + let len = (k * MR) / Q4_0.block_len() * Q4_0.block_bytes(); + let mut pa = NibbleReader::for_slice(std::slice::from_raw_parts(pa, len)); + let b = pb as *const TB; + for bk in 0..k / 32 { + let mut scales: [TI; MR] = [TI::zero(); MR]; + scales.iter_mut().for_each(|x| *x = pa.read_f16().as_()); + for ik in 0..32 { + let mut a: [TI; MR] = [TI::zero(); MR]; + a.iter_mut() + .zip(&scales) + .for_each(|(x, s)| *x = *s * (pa.read_i4() - 8).as_()); + let b = std::slice::from_raw_parts(b.add(NR * (ik + 32 * bk)), NR); + for i in 0..MR { + for j in 0..NR { + ab[i][j] += a[i] * b[j].as_(); + } + } + } + } + } +} + +unsafe fn add_mat_mul_pq40_scales_at_end( + pa: *const u8, + pb: *const u8, + k: usize, + ab: &mut [[TI; NR]; MR], +) where + TI: LADatum, + f16: AsPrimitive, + TB: AsPrimitive, + i8: AsPrimitive, +{ + unsafe { + assert!(k % Q4_0.block_len() == 0); + let len = (k * MR) / Q4_0.block_len() * Q4_0.block_bytes(); + let mut pa = NibbleReader::for_slice(std::slice::from_raw_parts(pa, len)); + let b = pb as *const TB; + for bk in 0..k / 32 { + let mut temp = [[TI::zero(); NR]; MR]; + for ik in 0..32 { + let mut a: [TI; MR] = [TI::zero(); MR]; + a.iter_mut().for_each(|x| *x = (pa.read_i4() - 8).as_()); + let b = std::slice::from_raw_parts(b.add(NR * (ik + 32 * bk)), NR); + for i in 0..MR { + for j in 0..NR { + temp[i][j] += a[i] * b[j].as_(); + } + } + } + for i in 0..MR { + let scale = pa.read_f16().as_(); + for j in 0..NR { + ab[i][j] += temp[i][j] * scale; + } + } + } + } +} + +unsafe fn add_unicast( + ab: &mut [[TI; NR]; MR], + other: &OutputStoreKer, +) where + TI: LADatum, + TO: LADatum + AsPrimitive, +{ + unsafe { + for i in 0usize..MR { + for j in 0usize..NR { + let value: *const TO = other + .ptr + .offset(other.row_byte_stride * i as isize + other.col_byte_stride * j as isize) + as _; + ab[i].as_mut()[j] += (*value).as_(); + } + } + } +} + +unsafe fn store_t( + tile: &OutputStoreKer, + ab: &[[TI; NR]; MR], +) where + TC: Copy, +{ + unsafe { + for i in 0usize..MR { + for j in 0usize..NR { + let loc: *mut TC = tile + .ptr + .offset(tile.row_byte_stride * i as isize + tile.col_byte_stride * j as isize) + as _; + let val: *const TC = (&ab[i].as_ref()[j]) as *const TI as _; + *loc = *val + } + } + } +} + +unsafe fn store_float_t( + tile: &OutputStoreKer, + ab: &[[TI; NR]; MR], +) where + TC: Copy + 'static, + TI: Copy + 'static + AsPrimitive, +{ + unsafe { + for i in 0usize..MR { + for j in 0usize..NR { + let loc: *mut TC = tile + .ptr + .offset(tile.row_byte_stride * i as isize + tile.col_byte_stride * j as isize) + as _; + let val = ab[i].as_ref()[j].as_(); + *loc = val + } + } + } +} + +#[inline(never)] +unsafe fn kernel(mut pnl: *const FusedKerSpec) -> isize +where + TI: LADatum + ScaleShiftAndRound + AsPrimitive, + TI: AsPrimitive + AsPrimitive + AsPrimitive, + usize: AsPrimitive, + f16: AsPrimitive, + f32: AsPrimitive, + f64: AsPrimitive, + i8: AsPrimitive, + i32: AsPrimitive, +{ + unsafe { + let mut ab = [[TI::zero(); NR]; MR]; + loop { + if pnl.is_null() { + break; + } + match *pnl { + FusedKerSpec::Done => break, + FusedKerSpec::Clear => ab = std::mem::zeroed(), + FusedKerSpec::LoadTile(col_major, _row_major) => { + for row in 0..MR { + for col in 0..NR { + ab[row][col] = *col_major.add(col * MR + row); + } + } + } + FusedKerSpec::ScalarAdd(a) => scalar!(ab, a, |a, b| a + b), + FusedKerSpec::ScalarMul(a) => scalar!(ab, a, |a, b| a * b), + FusedKerSpec::ScalarMin(m) => scalar!(ab, m, |a, b| if a < b { a } else { b }), + FusedKerSpec::ScalarMax(m) => scalar!(ab, m, |a, b| if a > b { a } else { b }), + FusedKerSpec::ScalarSub(m) => scalar!(ab, m, |a, b| a - b), + FusedKerSpec::ScalarSubF(m) => scalar!(ab, m, |a, b| b - a), + FusedKerSpec::LeakyRelu(m) => { + scalar!(ab, m, |a, b| if b > TI::zero() { b } else { a * b }) + } + FusedKerSpec::PerRowMin(m) => per_row!(ab, m, |a, b| if a < b { a } else { b }), + FusedKerSpec::PerRowMax(m) => per_row!(ab, m, |a, b| if a > b { a } else { b }), + FusedKerSpec::PerRowAdd(m) => per_row!(ab, m, |a, b| a + b), + FusedKerSpec::PerRowMul(m) => per_row!(ab, m, |a, b| a * b), + FusedKerSpec::PerRowSub(m) => per_row!(ab, m, |a, b| a - b), + FusedKerSpec::PerRowSubF(m) => per_row!(ab, m, |a, b| b - a), + FusedKerSpec::PerColMin(m) => per_col!(ab, m, |a, b| if a < b { a } else { b }), + FusedKerSpec::PerColMax(m) => per_col!(ab, m, |a, b| if a > b { a } else { b }), + FusedKerSpec::PerColAdd(m) => per_col!(ab, m, |a, b| a + b), + FusedKerSpec::PerColMul(m) => per_col!(ab, m, |a, b| a * b), + FusedKerSpec::PerColSub(m) => per_col!(ab, m, |a, b| a - b), + FusedKerSpec::PerColSubF(m) => per_col!(ab, m, |a, b| b - a), + FusedKerSpec::AddRowColProducts(rows, cols) => { + for i in 0..MR { + for j in 0..NR { + ab[i][j] += *rows.add(i) * *cols.add(j); + } + } + } + FusedKerSpec::AddUnicast(other) => { + if TI::datum_type().is_float() && other.item_size == 2 { + add_unicast::(&mut ab, &other) + } else if TI::datum_type().is_float() && other.item_size == 4 { + add_unicast::(&mut ab, &other) + } else if TI::datum_type().is_float() && other.item_size == 8 { + add_unicast::(&mut ab, &other) + } else if TI::datum_type() == i32::datum_type() && other.item_size == 1 { + add_unicast::(&mut ab, &other) + } else if TI::datum_type() == i32::datum_type() && other.item_size == 4 { + add_unicast::(&mut ab, &other) + } else { + unimplemented!("Missing AddUnicast type"); + } + } + FusedKerSpec::ShiftLeft(shift) => { + for i in 0..MR { + for j in 0..NR { + ab[i][j] = ab[i][j].q_shl(shift); + } + } + } + FusedKerSpec::RoundingShiftRight(shift, rp) => { + for i in 0..MR { + for j in 0..NR { + ab[i][j] = ab[i][j].q_shr(shift, rp); + } + } + } + FusedKerSpec::QScale(shift, rp, mult) => { + for i in 0..MR { + for j in 0..NR { + ab[i][j] = ab[i][j].q_scale(Scaler::from_fuse_params(shift, rp, mult)); + } + } + } + FusedKerSpec::AddMatMul { k, pa, pb, packing } => { + use std::mem::transmute; + if TI::datum_type().is_float() { + match packing { + 0 => add_mat_mul::(pa, pb, k, &mut ab), + 1 => add_mat_mul::(pa, pb, k, &mut ab), + 2 => add_mat_mul::(pa, pb, k, &mut ab), + 3 => add_mat_mul::(pa, pb, k, &mut ab), + 4 => add_mat_mul::(pa, pb, k, &mut ab), + 5 => add_mat_mul_pq40::(pa, pb, k, &mut ab), + 6 => add_mat_mul_pq40_scales_at_end::( + pa, pb, k, &mut ab, + ), + 7 => add_mat_mul_pq40::(pa, pb, k, &mut ab), + _ => unreachable!(), + } + } else if TI::datum_type() == i32::datum_type() { + // transmute to allow using explicitly i3 in add_mat_mul generic params + let ab = transmute::<&mut [[TI; NR]; MR], &mut [[i32; NR]; MR]>(&mut ab); + if packing == 0 { + add_mat_mul::(pa, pb, k, ab) + } else if packing == 1 { + add_mat_mul::(pa, pb, k, ab) + } else { + return 1; + } + } else { + return 1; + } + } + FusedKerSpec::Store(tile) => { + if TI::datum_type().is_float() { + match tile.item_size { + 2 => store_float_t::(&tile, &ab), + 4 => store_float_t::(&tile, &ab), + 8 => store_float_t::(&tile, &ab), + _ => unimplemented!(), + } + } else { + match tile.item_size { + 1 => store_t::(&tile, &ab), + 2 => store_t::(&tile, &ab), + 4 => store_t::(&tile, &ab), + 8 => store_t::(&tile, &ab), + _ => unimplemented!(), + } + } + } + }; + pnl = pnl.add(1); + } + } + 0 +} + +fn pq40_r4() -> PackedBlockQuantFormat { + PackedBlockQuantFormat::new(&Q4_0, 4, 0, false) +} + +fn pq40_r4_se() -> PackedBlockQuantFormat { + PackedBlockQuantFormat::new(&Q4_0, 4, 0, true) +} + +// f16 kernels +MMMRustKernel!(kernel:: => generic_f16_4x4(4,4) + packing[1] = f16f16bis => |k| k.with_packing(f16::packing(4), f16::packing(4)); + packing[2] = f32f32 => |k| k.with_packing(f32::packing(4), f32::packing(4)); + packing[3] = f16f32 => |k| k.with_packing(f16::packing(4), f32::packing(4)); + packing[4] = f32f16 => |k| k.with_packing(f32::packing(4), f16::packing(4)); + packing[5] = q40f16 => |k| k.with_packing(pq40_r4(), f16::packing(4)); + packing[6] = q40f16se => |k| k.with_packing(pq40_r4_se(), f16::packing(4)); + packing[7] = q40f32 => |k| k.with_packing(pq40_r4(), f32::packing(4)); + quality(if has_fp16() { ImplementationQuality::Generic } else { ImplementationQuality::Dreadful }) + store(f32, f64) +); + +MMMRustKernel! {kernel:: => generic_f16_4x1(4,1) + packing[1] = f16f16bis => |k| k.with_packing(f16::packing(4), f16::packing(1)); + packing[2] = f32f32 => |k| k.with_packing(f32::packing(4), f32::packing(1)); + packing[3] = f16f32 => |k| k.with_packing(f16::packing(4), f32::packing(1)); + packing[4] = f32f16 => |k| k.with_packing(f32::packing(4), f16::packing(1)); + packing[5] = q40f16 => |k| k.with_packing(pq40_r4(), f16::packing(1)); + packing[6] = q40f16se => |k| k.with_packing(pq40_r4_se(), f16::packing(1)); + packing[7] = q40f32 => |k| k.with_packing(pq40_r4(), f32::packing(1)); + quality(if has_fp16() { ImplementationQuality::Generic } else { ImplementationQuality::Dreadful }) + store(f32, f64) +} + +// f32 kernels +MMMRustKernel!(kernel:: => generic_f32_4x4(4,4) + packing[1] = f16f16 => |k| k.with_packing(f16::packing(4), f16::packing(4)); + packing[2] = f32f32bis => |k| k.with_packing(f32::packing(4), f32::packing(4)); + packing[3] = f16f32 => |k| k.with_packing(f16::packing(4), f32::packing(4)); + packing[4] = f32f16 => |k| k.with_packing(f32::packing(4), f16::packing(4)); + packing[5] = q40f16 => |k| k.with_packing(pq40_r4(), f16::packing(4)); + packing[6] = q40f16se => |k| k.with_packing(pq40_r4_se(), f16::packing(4)); + packing[7] = q40f32 => |k| k.with_packing(pq40_r4(), f32::packing(4)); + quality(ImplementationQuality::Generic) + store(f16, f64) +); +MMMRustKernel! {kernel:: => generic_f32_4x1(4,1) + packing[1] = f16f16 => |k| k.with_packing(f16::packing(4), f16::packing(1)); + packing[2] = f32f32bis => |k| k.with_packing(f32::packing(4), f32::packing(1)); + packing[3] = f16f32 => |k| k.with_packing(f16::packing(4), f32::packing(1)); + packing[4] = f32f16 => |k| k.with_packing(f32::packing(4), f16::packing(1)); + packing[5] = q40f16 => |k| k.with_packing(pq40_r4(), f16::packing(1)); + packing[6] = q40f16se => |k| k.with_packing(pq40_r4_se(), f16::packing(1)); + packing[7] = q40f32 => |k| k.with_packing(pq40_r4(), f32::packing(1)); + quality(ImplementationQuality::Generic) + store(f16, f64) +} + +// f64 kernels +MMMRustKernel!(kernel:: => generic_f64_4x4(4,4) + quality(ImplementationQuality::Generic) + store(f16, f32)); +MMMRustKernel!(kernel:: => generic_f64_4x1(4,1) + quality(ImplementationQuality::Generic) + store(f16, f32)); + +// I32 kernels +MMMRustKernel! {kernel:: => generic_i32_4x4(4,4) + packing[1] = i8i8 => |k| k.with_packing(i8::packing(4), i8::packing(4)); + quality(ImplementationQuality::Generic) + store(i8) +} + +MMMRustKernel! {kernel:: => generic_i32_4x1(4,1) + packing[1] = i8i8 => |k| k.with_packing(i8::packing(4), i8::packing(1)); + quality(ImplementationQuality::Generic) + store(i8) +} + +// extra tests kernels +#[cfg(test)] +MMMRustKernel!(kernel:: => generic_f32_3x2(3,2) store(f16, f64)); + +#[cfg(test)] +MMMRustKernel! {kernel:: => generic_i32_3x2(3,2) + packing[1] = i8i8 => |k| k.with_packing(i8::packing(3), i8::packing(2)); + store(i8) +} + +pub fn plug(ops: &mut Ops) { + ops.mmm_impls.push(generic_f16_4x4.mmm()); + ops.mmm_impls.push(generic_f16_4x1.mmm()); + ops.mmm_impls.push(generic_f32_4x4.mmm()); + ops.mmm_impls.push(generic_f32_4x1.mmm()); + ops.mmm_impls.push(generic_f64_4x4.mmm()); + ops.mmm_impls.push(generic_f64_4x1.mmm()); + ops.mmm_impls.push(generic_i32_4x4.mmm()); + ops.mmm_impls.push(generic_i32_4x1.mmm()); +} + +#[cfg(test)] +mod test { + + #[test] + fn kits() { + let mut ops = crate::generic(); + super::plug(&mut ops); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/reduce.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/reduce.rs new file mode 100644 index 0000000..af38cfb --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/reduce.rs @@ -0,0 +1,187 @@ +// Reduce generic implementation +pub mod max { + pub use tract_data::internal::f16; + + reduce_impl_wrap!( + f32, + SMax4, + 4, + 4, + (), + f32::MIN, + fn run(x: &[f32], _: ()) -> f32 { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + *x.iter().max_by(|a, b| a.total_cmp(b)).unwrap() + }, + fn reduce_two(a: f32, b: f32) -> f32 { + a.max(b) + } + ); + + reduce_impl_wrap!( + f16, + HMax8, + 8, + 8, + (), + f16::MIN, + fn run(x: &[f16], _: ()) -> f16 { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + *x.iter().max_by(|a, b| a.total_cmp(b)).unwrap() + }, + fn reduce_two(a: f16, b: f16) -> f16 { + a.max(b) + } + ); + + #[cfg(test)] + #[macro_use] + pub mod s { + crate::max_frame_tests!(true, f32, crate::generic::reduce::max::SMax4); + } + + #[cfg(test)] + #[macro_use] + pub mod h { + use super::*; + crate::max_frame_tests!(true, f16, crate::generic::reduce::max::HMax8); + } +} + +// Reduce generic implementation +pub mod sum { + use crate::num_traits::Zero; + pub use tract_data::internal::f16; + + reduce_impl_wrap!( + f32, + SSum4, + 4, + 4, + (), + 0.0, + fn run(x: &[f32], _: ()) -> f32 { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter().sum::() + }, + fn reduce_two(a: f32, b: f32) -> f32 { + a + b + } + ); + + reduce_impl_wrap!( + f16, + HSum8, + 8, + 8, + (), + f16::zero(), + fn run(x: &[f16], _: ()) -> f16 { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter().sum::() + }, + fn reduce_two(a: f16, b: f16) -> f16 { + a + b + } + ); + + #[cfg(test)] + #[macro_use] + pub mod s { + crate::sum_frame_tests!(true, f32, crate::generic::reduce::sum::SSum4); + } + + #[cfg(test)] + #[macro_use] + pub mod h { + use super::*; + crate::sum_frame_tests!(true, f16, crate::generic::reduce::sum::HSum8); + } +} + +// Softmax generic implementation +pub mod softmax_l2 { + use crate::num_traits::Zero; + use tract_data::internal::f16; + + map_reduce_impl_wrap!( + f32, + SSoftMaxL2, + 4, + 4, + f32, + f32::MIN, + 0.0, + fn run(x: &mut [f32], max: f32) -> f32 { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + let mut sum = 0.; + for v in x.iter_mut() { + let y = *v - max; + let y = fast_compact_exp_f32(y); + *v = y; + sum += y; + } + sum + }, + fn reduce_two(a: f32, b: f32) -> f32 { + a + b + } + ); + + map_reduce_impl_wrap!( + f16, + HSoftMaxL2, + 8, + 8, + f16, + f16::MIN, + f16::zero(), + fn run(x: &mut [f16], max: f16) -> f16 { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + let mut sum = f16::zero(); + for v in x.iter_mut() { + let y = *v - max; + let y = f16::from_f32(fast_compact_exp_f32(y.to_f32())); + *v = y; + sum += y; + } + sum + }, + fn reduce_two(a: f16, b: f16) -> f16 { + a + b + } + ); + + // ported from https://github.com/gnuradio/volk/blob/master/kernels/volk/volk_32f_expfast_32f.h + // probably inspired from https://nic.schraudolph.org/pubs/Schraudolph99.pdf + // not that the cast to u32 deals with negative right, while implem in volk code are wrong in some + // corner cases (need a max(0,x) before the u32 conversion) + pub fn fast_compact_exp_f32(v: f32) -> f32 { + const MLN2: f32 = 0.6931471805f32; + const A: f32 = 8388608.0f32; + const B: f32 = 1065353216.0f32; + const C: f32 = 60801.0f32; + const SLOPE: f32 = A / MLN2; + const OFFSET: f32 = B - C; + f32::from_bits(((SLOPE * v) + OFFSET) as u32) + } + + #[cfg(test)] + #[macro_use] + pub mod s { + crate::softmax_l2_frame_tests!(true, f32, super::SSoftMaxL2); + } + + #[cfg(test)] + #[macro_use] + pub mod h { + use super::*; + crate::softmax_l2_frame_tests!(true, f16, HSoftMaxL2); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/rms_norm.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/rms_norm.rs new file mode 100644 index 0000000..40eed4d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/rms_norm.rs @@ -0,0 +1,67 @@ +/// Generic scalar reference implementation of fused row-wise RmsNorm. +/// out_i = x_i * rsqrt(mean(x_i²) + eps) +/// +/// Replaces tract-core's 4-call composition (`Reducer::MeanOfSquares` + `Add` + +/// `Rsqrt` + `Mul`) with a single 2-pass kernel. Overridden by AVX-512 on +/// x86_64; non-x86 / non-AVX512 hosts keep this scalar version. +pub fn rms_norm_f32(buf: &mut [f32], eps: f32) { + if buf.is_empty() { + return; + } + let n = buf.len() as f32; + let sum_sq: f32 = buf.iter().map(|x| x * x).sum(); + let mean_sq = sum_sq / n; + let inv_std = (mean_sq + eps).sqrt().recip(); + for x in buf.iter_mut() { + *x *= inv_std; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn close_enough(got: f32, want: f32) -> bool { + (got - want).abs() < 1e-5 + } + + #[test] + fn rms_norm_constant() { + // RmsNorm of all-ones with eps=0: mean(1²)=1, rsqrt=1, output=1. + let mut buf = [1.0; 16]; + rms_norm_f32(&mut buf, 0.0); + for v in buf { + assert!(close_enough(v, 1.0), "got {v}, want 1.0"); + } + } + + #[test] + fn rms_norm_one_two_three_four() { + // mean(1+4+9+16)/4 = 7.5, rsqrt(7.5) ≈ 0.3651 + let mut buf = [1.0_f32, 2.0, 3.0, 4.0]; + rms_norm_f32(&mut buf, 0.0); + let inv = (7.5_f32).sqrt().recip(); + for (i, v) in buf.iter().enumerate() { + let want = (i + 1) as f32 * inv; + assert!(close_enough(*v, want), "i={i}: got {v}, want {want}"); + } + } + + #[test] + fn rms_norm_eps_added_under_root() { + // eps inside the sqrt, not added afterward. + let mut buf = [0.0_f32; 4]; + rms_norm_f32(&mut buf, 1e-5); + for v in buf { + // 0 * anything = 0; just verify no NaN/inf. + assert!(v.is_finite()); + assert_eq!(v, 0.0); + } + } + + #[test] + fn rms_norm_empty() { + let mut buf: [f32; 0] = []; + rms_norm_f32(&mut buf, 1e-5); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/rounding.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/rounding.rs new file mode 100644 index 0000000..09525cb --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/rounding.rs @@ -0,0 +1,534 @@ +use crate::frame::mmm::*; +use std::hash::{Hash, Hasher}; +use std::ops::Mul; +use tract_data::prelude::f16; + +#[derive(Debug, Clone, Copy, PartialEq)] +pub struct Scaler { + pub scale: f32, + pub mult: Option, + pub shift: isize, + pub policy: RoundingPolicy, +} + +impl Eq for Scaler {} + +#[allow(clippy::derived_hash_with_manual_eq)] +impl Hash for Scaler { + fn hash(&self, state: &mut H) + where + H: Hasher, + { + Hash::hash(&self.scale.to_bits(), state) + } +} + +impl Scaler { + pub fn new(scale: f32, policy: RoundingPolicy) -> Self { + let (mult, shift) = Self::convert_scale_to_mult_shift(scale); + Self { + scale, + mult, + shift, + policy, + } + } + + pub fn as_fused_spec(&self) -> FusedSpec<'_> { + if let Some(multiplier) = self.mult { + FusedSpec::QScale(self.shift, self.policy, multiplier) + } else if self.shift > 0 { + FusedSpec::RoundingShiftRight(self.shift as usize, self.policy) + } else { + FusedSpec::ShiftLeft((-self.shift) as usize) + } + } + + // FIXME: Only to avoid fused op breaking + pub fn from_fuse_params(shift: isize, policy: RoundingPolicy, mult: i32) -> Self { + let scale = mult as f32 * 2f32.powi(-(31 + shift as i32)); + Self { + scale, + mult: Some(mult), + shift, + policy, + } + } + + #[inline] + // This function convert a scale (actually a fraction of two integers Q/D) + // into an integer multiplier and a shift (the multiplier being 1/2D in Q0_31). + fn convert_scale_to_mult_shift(scale: f32) -> (Option, isize) { + // Zero is a special case to handle + if scale == 0.0 { + return (None, 0); + } + + // Convert f32 to bits representation with the following pattern + // Bit | 31 | 30-23 | 22-0 | + // | Sign | Exponent | Fraction | + let scale_bits = scale.to_bits(); + + // Get actual value of the exponent + let current_exponent = (scale_bits >> 23) & 0xff; + + // Extract fractional part of the float with: + // - 0x007fffff that represents the mask of the 23 lower bits (fractional part) + // (partial because it doesn't include the hidden bit (24) of the float representation) + let partial_frac = scale_bits & 0x007fffff; + + if partial_frac == 0 { + let shift = 127 - current_exponent as isize; + (None, shift) + } else { + // We add 0x800000 that represents the hidden bit set to one. + // Here the frac is encoded as a Q8_23. + let frac = partial_frac | 0x800000; + + // We rescale the result to be in Q0_31 + // We should have shifted the result by 8 but the frac value is in [1.0, 2.0) + // so we cannot do that (we would need one bit for the integer). + // Instead we devide the frac by two to be in [0.5, 1.0) in Q0_31 + // which lead to a shift of (8-1 = 7). + let half_frac = (frac << 7) as i32; + + // Compute the actual value of the shift + // Here, we remove one as half_frac needs to be multiplied by 2. + let shift = 127 - current_exponent as isize - 1; + (Some(half_frac), shift) + } + } +} + +impl Mul for Scaler { + type Output = f16; + + #[inline] + fn mul(self, rhs: f16) -> Self::Output { + f16::from_f32(self.scale) * rhs + } +} + +impl Mul for Scaler { + type Output = f32; + + #[inline] + fn mul(self, rhs: f32) -> Self::Output { + self.scale * rhs + } +} + +impl Mul for Scaler { + type Output = f64; + + #[inline] + fn mul(self, rhs: f64) -> Self::Output { + self.scale as f64 * rhs + } +} + +impl Mul for f16 { + type Output = f16; + + #[inline] + fn mul(self, rhs: Scaler) -> Self::Output { + rhs * self + } +} + +impl Mul for f32 { + type Output = f32; + + #[inline] + fn mul(self, rhs: Scaler) -> Self::Output { + rhs * self + } +} + +impl Mul for f64 { + type Output = f64; + + #[inline] + fn mul(self, rhs: Scaler) -> Self::Output { + rhs * self + } +} + +impl Mul for Scaler { + type Output = i32; + + #[inline] + fn mul(self, rhs: i32) -> Self::Output { + let (val, shift) = if let Some(multiplier) = self.mult { + (multiplier as i64 * rhs as i64, self.shift + 31) + } else { + (rhs as i64, self.shift) + }; + + // Round according to rounding policy + use RoundingPolicy::*; + if shift > 0 { + let half: i64 = 1 << (shift - 1); + let nudge: i64 = match self.policy { + Zero => -1, + MinusInf => -((val >= 0) as i64), + PlusInf => -((val <= 0) as i64), + Away => 0, + Even => ((val.abs() >> shift) & 0x1) - 1, + Odd => -((val.abs() >> shift) & 0x1), + _ => panic!(), + }; + + (val.signum() * ((val.abs() + half + nudge) >> shift)) as i32 + } else { + (val << -shift) as i32 + } + } +} + +impl Mul for i32 { + type Output = i32; + + #[inline] + fn mul(self, rhs: Scaler) -> Self::Output { + rhs * self + } +} + +pub trait ScaleShiftAndRound { + fn q_scale(self, scaler: Scaler) -> Self; + fn q_shl(self, shift: usize) -> Self; + fn q_shr(self, shift: usize, rp: RoundingPolicy) -> Self; +} + +impl ScaleShiftAndRound for f64 { + fn q_scale(self, scaler: Scaler) -> Self { + self * scaler + } + fn q_shl(self, shift: usize) -> Self { + self * 2f64.powi(shift as i32) + } + fn q_shr(self, shift: usize, _rp: RoundingPolicy) -> Self { + self * 2f64.powi(-(shift as i32)) + } +} + +impl ScaleShiftAndRound for f32 { + fn q_scale(self, scaler: Scaler) -> Self { + self * scaler + } + fn q_shl(self, shift: usize) -> Self { + self * 2f32.powi(shift as i32) + } + fn q_shr(self, shift: usize, _rp: RoundingPolicy) -> Self { + self * 2f32.powi(-(shift as i32)) + } +} + +impl ScaleShiftAndRound for f16 { + fn q_scale(self, scaler: Scaler) -> Self { + self * scaler + } + fn q_shl(self, shift: usize) -> Self { + self * f16::from_f32(2f32.powi(shift as i32)) + } + fn q_shr(self, shift: usize, _rp: RoundingPolicy) -> Self { + self * f16::from_f32(2f32.powi(-(shift as i32))) + } +} + +impl ScaleShiftAndRound for i32 { + fn q_scale(self, scaler: Scaler) -> Self { + self * scaler + } + fn q_shr(self, shift: usize, rp: RoundingPolicy) -> Self { + use RoundingPolicy::*; + let half: i32 = 1 << (shift - 1); + let nudge: i32 = match rp { + Zero => -1, + MinusInf => -((self >= 0) as i32), + PlusInf => -((self <= 0) as i32), + Away => 0, + Even => ((self.abs() >> shift) & 0x1) - 1, + Odd => -((self.abs() >> shift) & 0x1), + _ => panic!(), + }; + self.signum() * ((self.abs() + half + nudge) >> shift) + } + fn q_shl(self, shift: usize) -> Self { + self << shift + } +} + +// 6 / 4 -> 1.5 -> arrondi: 2. rien a faire +// 2 / 4 -> 0.5 -> arrondi: 1. veut 0 -> nudge = -1 + +#[cfg(test)] +mod test { + use super::RoundingPolicy::*; + use super::*; + + #[test] + fn test_scale_rounding_f32() { + assert_eq!(0f32.q_scale(Scaler::new(0.5, Zero)), 0.0); + assert_eq!(1f32.q_scale(Scaler::new(0.5, Zero)), 0.5); + assert_eq!(2f32.q_scale(Scaler::new(0.5, Zero)), 1.0); + assert_eq!(3f32.q_scale(Scaler::new(0.5, Zero)), 1.5); + assert_eq!((-1f32).q_scale(Scaler::new(0.5, Zero)), -0.5); + assert_eq!((-2f32).q_scale(Scaler::new(0.5, Zero)), -1.0); + assert_eq!((-3f32).q_scale(Scaler::new(0.5, Zero)), -1.5); + } + + #[test] + fn test_shift_rounding_zero() { + assert_eq!(0i32.q_shr(1, Zero), 0); + assert_eq!(1i32.q_shr(1, Zero), 0); + assert_eq!(2i32.q_shr(1, Zero), 1); + assert_eq!(3i32.q_shr(1, Zero), 1); + assert_eq!(0i32.q_shr(2, Zero), 0); + assert_eq!(1i32.q_shr(2, Zero), 0); + assert_eq!(2i32.q_shr(2, Zero), 0); + assert_eq!(3i32.q_shr(2, Zero), 1); + assert_eq!(4i32.q_shr(2, Zero), 1); + assert_eq!(5i32.q_shr(2, Zero), 1); + assert_eq!(6i32.q_shr(2, Zero), 1); + assert_eq!((-1i32).q_shr(2, Zero), 0); + assert_eq!((-2i32).q_shr(2, Zero), 0); + assert_eq!((-3i32).q_shr(2, Zero), -1); + assert_eq!((-4i32).q_shr(2, Zero), -1); + assert_eq!((-5i32).q_shr(2, Zero), -1); + assert_eq!((-6i32).q_shr(2, Zero), -1); + } + + #[test] + fn test_scale_rounding_zero() { + assert_eq!(0i32.q_scale(Scaler::new(0.5, Zero)), 0); + assert_eq!(1i32.q_scale(Scaler::new(0.5, Zero)), 0); + assert_eq!(2i32.q_scale(Scaler::new(0.5, Zero)), 1); + assert_eq!(3i32.q_scale(Scaler::new(0.5, Zero)), 1); + assert_eq!((-1i32).q_scale(Scaler::new(0.5, Zero)), 0); + assert_eq!((-2i32).q_scale(Scaler::new(0.5, Zero)), -1); + assert_eq!((-3i32).q_scale(Scaler::new(0.5, Zero)), -1); + assert_eq!(2i32.q_scale(Scaler::new(0.25, Zero)), 0); + assert_eq!(3i32.q_scale(Scaler::new(0.25, Zero)), 1); + assert_eq!(4i32.q_scale(Scaler::new(0.25, Zero)), 1); + assert_eq!(5i32.q_scale(Scaler::new(0.25, Zero)), 1); + assert_eq!(6i32.q_scale(Scaler::new(0.25, Zero)), 1); + assert_eq!((-2i32).q_scale(Scaler::new(0.25, Zero)), 0); + assert_eq!((-3i32).q_scale(Scaler::new(0.25, Zero)), -1); + assert_eq!((-4i32).q_scale(Scaler::new(0.25, Zero)), -1); + assert_eq!((-5i32).q_scale(Scaler::new(0.25, Zero)), -1); + assert_eq!((-6i32).q_scale(Scaler::new(0.25, Zero)), -1); + } + + #[test] + fn test_shift_rounding_away() { + assert_eq!(0i32.q_shr(1, Away), 0); + assert_eq!(1i32.q_shr(1, Away), 1); + assert_eq!(2i32.q_shr(1, Away), 1); + assert_eq!(3i32.q_shr(1, Away), 2); + assert_eq!(0i32.q_shr(2, Away), 0); + assert_eq!(1i32.q_shr(2, Away), 0); + assert_eq!(2i32.q_shr(2, Away), 1); + assert_eq!(3i32.q_shr(2, Away), 1); + assert_eq!(4i32.q_shr(2, Away), 1); + assert_eq!(5i32.q_shr(2, Away), 1); + assert_eq!(6i32.q_shr(2, Away), 2); + assert_eq!((-1i32).q_shr(2, Away), 0); + assert_eq!((-2i32).q_shr(2, Away), -1); + assert_eq!((-3i32).q_shr(2, Away), -1); + assert_eq!((-4i32).q_shr(2, Away), -1); + assert_eq!((-5i32).q_shr(2, Away), -1); + assert_eq!((-6i32).q_shr(2, Away), -2); + } + + #[test] + fn test_scale_rounding_away() { + assert_eq!(0i32.q_scale(Scaler::new(0.5, Away)), 0); + assert_eq!(1i32.q_scale(Scaler::new(0.5, Away)), 1); + assert_eq!(2i32.q_scale(Scaler::new(0.5, Away)), 1); + assert_eq!(3i32.q_scale(Scaler::new(0.5, Away)), 2); + assert_eq!((-1i32).q_scale(Scaler::new(0.5, Away)), -1); + assert_eq!((-2i32).q_scale(Scaler::new(0.5, Away)), -1); + assert_eq!((-3i32).q_scale(Scaler::new(0.5, Away)), -2); + assert_eq!(2i32.q_scale(Scaler::new(0.25, Away)), 1); + assert_eq!(3i32.q_scale(Scaler::new(0.25, Away)), 1); + assert_eq!(4i32.q_scale(Scaler::new(0.25, Away)), 1); + assert_eq!(5i32.q_scale(Scaler::new(0.25, Away)), 1); + assert_eq!(6i32.q_scale(Scaler::new(0.25, Away)), 2); + assert_eq!((-2i32).q_scale(Scaler::new(0.25, Away)), -1); + assert_eq!((-3i32).q_scale(Scaler::new(0.25, Away)), -1); + assert_eq!((-4i32).q_scale(Scaler::new(0.25, Away)), -1); + assert_eq!((-5i32).q_scale(Scaler::new(0.25, Away)), -1); + assert_eq!((-6i32).q_scale(Scaler::new(0.25, Away)), -2); + } + + #[test] + fn test_shift_rounding_plus_inf() { + assert_eq!(0i32.q_shr(1, PlusInf), 0); + assert_eq!(1i32.q_shr(1, PlusInf), 1); + assert_eq!(2i32.q_shr(1, PlusInf), 1); + assert_eq!(3i32.q_shr(1, PlusInf), 2); + assert_eq!(0i32.q_shr(2, PlusInf), 0); + assert_eq!(1i32.q_shr(2, PlusInf), 0); + assert_eq!(2i32.q_shr(2, PlusInf), 1); + assert_eq!(3i32.q_shr(2, PlusInf), 1); + assert_eq!(4i32.q_shr(2, PlusInf), 1); + assert_eq!(5i32.q_shr(2, PlusInf), 1); + assert_eq!(6i32.q_shr(2, PlusInf), 2); + assert_eq!((-1i32).q_shr(2, PlusInf), 0); + assert_eq!((-2i32).q_shr(2, PlusInf), 0); + assert_eq!((-3i32).q_shr(2, PlusInf), -1); + assert_eq!((-4i32).q_shr(2, PlusInf), -1); + assert_eq!((-5i32).q_shr(2, PlusInf), -1); + assert_eq!((-6i32).q_shr(2, PlusInf), -1); + } + + #[test] + fn test_scale_rounding_plus_inf() { + assert_eq!(0i32.q_scale(Scaler::new(0.5, PlusInf)), 0); + assert_eq!(1i32.q_scale(Scaler::new(0.5, PlusInf)), 1); + assert_eq!(2i32.q_scale(Scaler::new(0.5, PlusInf)), 1); + assert_eq!(3i32.q_scale(Scaler::new(0.5, PlusInf)), 2); + assert_eq!((-1i32).q_scale(Scaler::new(0.5, PlusInf)), 0); + assert_eq!((-2i32).q_scale(Scaler::new(0.5, PlusInf)), -1); + assert_eq!((-3i32).q_scale(Scaler::new(0.5, PlusInf)), -1); + assert_eq!(2i32.q_scale(Scaler::new(0.25, PlusInf)), 1); + assert_eq!(3i32.q_scale(Scaler::new(0.25, PlusInf)), 1); + assert_eq!(4i32.q_scale(Scaler::new(0.25, PlusInf)), 1); + assert_eq!(5i32.q_scale(Scaler::new(0.25, PlusInf)), 1); + assert_eq!(6i32.q_scale(Scaler::new(0.25, PlusInf)), 2); + assert_eq!((-2i32).q_scale(Scaler::new(0.25, PlusInf)), 0); + assert_eq!((-3i32).q_scale(Scaler::new(0.25, PlusInf)), -1); + assert_eq!((-4i32).q_scale(Scaler::new(0.25, PlusInf)), -1); + assert_eq!((-5i32).q_scale(Scaler::new(0.25, PlusInf)), -1); + assert_eq!((-6i32).q_scale(Scaler::new(0.25, PlusInf)), -1); + } + + #[test] + fn test_shift_rounding_minus_inf() { + assert_eq!(0i32.q_shr(1, MinusInf), 0); + assert_eq!(1i32.q_shr(1, MinusInf), 0); + assert_eq!(2i32.q_shr(1, MinusInf), 1); + assert_eq!(3i32.q_shr(1, MinusInf), 1); + assert_eq!(0i32.q_shr(2, MinusInf), 0); + assert_eq!(1i32.q_shr(2, MinusInf), 0); + assert_eq!(2i32.q_shr(2, MinusInf), 0); + assert_eq!(3i32.q_shr(2, MinusInf), 1); + assert_eq!(4i32.q_shr(2, MinusInf), 1); + assert_eq!(5i32.q_shr(2, MinusInf), 1); + assert_eq!(6i32.q_shr(2, MinusInf), 1); + assert_eq!((-1i32).q_shr(2, MinusInf), 0); + assert_eq!((-2i32).q_shr(2, MinusInf), -1); + assert_eq!((-3i32).q_shr(2, MinusInf), -1); + assert_eq!((-4i32).q_shr(2, MinusInf), -1); + assert_eq!((-5i32).q_shr(2, MinusInf), -1); + assert_eq!((-6i32).q_shr(2, MinusInf), -2); + } + + #[test] + fn test_scale_rounding_minus_inf() { + assert_eq!(0i32.q_scale(Scaler::new(0.5, MinusInf)), 0); + assert_eq!(1i32.q_scale(Scaler::new(0.5, MinusInf)), 0); + assert_eq!(2i32.q_scale(Scaler::new(0.5, MinusInf)), 1); + assert_eq!(3i32.q_scale(Scaler::new(0.5, MinusInf)), 1); + assert_eq!((-1i32).q_scale(Scaler::new(0.5, MinusInf)), -1); + assert_eq!((-2i32).q_scale(Scaler::new(0.5, MinusInf)), -1); + assert_eq!((-3i32).q_scale(Scaler::new(0.5, MinusInf)), -2); + assert_eq!(2i32.q_scale(Scaler::new(0.25, MinusInf)), 0); + assert_eq!(3i32.q_scale(Scaler::new(0.25, MinusInf)), 1); + assert_eq!(4i32.q_scale(Scaler::new(0.25, MinusInf)), 1); + assert_eq!(5i32.q_scale(Scaler::new(0.25, MinusInf)), 1); + assert_eq!(6i32.q_scale(Scaler::new(0.25, MinusInf)), 1); + assert_eq!((-2i32).q_scale(Scaler::new(0.25, MinusInf)), -1); + assert_eq!((-3i32).q_scale(Scaler::new(0.25, MinusInf)), -1); + assert_eq!((-4i32).q_scale(Scaler::new(0.25, MinusInf)), -1); + assert_eq!((-5i32).q_scale(Scaler::new(0.25, MinusInf)), -1); + assert_eq!((-6i32).q_scale(Scaler::new(0.25, MinusInf)), -2); + //assert_eq!((-9i32).q_scale(ONE_OVER_TWO_IN_Q0_30, 5, MinusInf), 0); + } + + #[test] + fn test_shift_rounding_even() { + assert_eq!(0i32.q_shr(1, Even), 0); + assert_eq!(1i32.q_shr(1, Even), 0); + assert_eq!(2i32.q_shr(1, Even), 1); + assert_eq!(3i32.q_shr(1, Even), 2); + assert_eq!(0i32.q_shr(2, Even), 0); + assert_eq!(1i32.q_shr(2, Even), 0); + assert_eq!(2i32.q_shr(2, Even), 0); + assert_eq!(3i32.q_shr(2, Even), 1); + assert_eq!(4i32.q_shr(2, Even), 1); + assert_eq!(5i32.q_shr(2, Even), 1); + assert_eq!(6i32.q_shr(2, Even), 2); + assert_eq!((-1i32).q_shr(2, Even), 0); + assert_eq!((-2i32).q_shr(2, Even), 0); + assert_eq!((-3i32).q_shr(2, Even), -1); + assert_eq!((-4i32).q_shr(2, Even), -1); + assert_eq!((-5i32).q_shr(2, Even), -1); + assert_eq!((-6i32).q_shr(2, Even), -2); + } + + #[test] + fn test_scale_rounding_even() { + assert_eq!(0i32.q_scale(Scaler::new(0.5, Even)), 0); + assert_eq!(1i32.q_scale(Scaler::new(0.5, Even)), 0); + assert_eq!(2i32.q_scale(Scaler::new(0.5, Even)), 1); + assert_eq!(3i32.q_scale(Scaler::new(0.5, Even)), 2); + assert_eq!((-1i32).q_scale(Scaler::new(0.5, Even)), 0); + assert_eq!((-2i32).q_scale(Scaler::new(0.5, Even)), -1); + assert_eq!((-3i32).q_scale(Scaler::new(0.5, Even)), -2); + assert_eq!(2i32.q_scale(Scaler::new(0.25, Even)), 0); + assert_eq!(3i32.q_scale(Scaler::new(0.25, Even)), 1); + assert_eq!(4i32.q_scale(Scaler::new(0.25, Even)), 1); + assert_eq!(5i32.q_scale(Scaler::new(0.25, Even)), 1); + assert_eq!(6i32.q_scale(Scaler::new(0.25, Even)), 2); + assert_eq!((-2i32).q_scale(Scaler::new(0.25, Even)), 0); + assert_eq!((-3i32).q_scale(Scaler::new(0.25, Even)), -1); + assert_eq!((-4i32).q_scale(Scaler::new(0.25, Even)), -1); + assert_eq!((-5i32).q_scale(Scaler::new(0.25, Even)), -1); + assert_eq!((-6i32).q_scale(Scaler::new(0.25, Even)), -2); + } + + #[test] + fn test_shift_rounding_odd() { + assert_eq!(0i32.q_shr(1, Odd), 0); + assert_eq!(1i32.q_shr(1, Odd), 1); + assert_eq!(2i32.q_shr(1, Odd), 1); + assert_eq!(3i32.q_shr(1, Odd), 1); + assert_eq!(0i32.q_shr(2, Odd), 0); + assert_eq!(1i32.q_shr(2, Odd), 0); + assert_eq!(2i32.q_shr(2, Odd), 1); + assert_eq!(3i32.q_shr(2, Odd), 1); + assert_eq!(4i32.q_shr(2, Odd), 1); + assert_eq!(5i32.q_shr(2, Odd), 1); + assert_eq!(6i32.q_shr(2, Odd), 1); + assert_eq!((-1i32).q_shr(2, Odd), 0); + assert_eq!((-2i32).q_shr(2, Odd), -1); + assert_eq!((-3i32).q_shr(2, Odd), -1); + assert_eq!((-4i32).q_shr(2, Odd), -1); + assert_eq!((-5i32).q_shr(2, Odd), -1); + assert_eq!((-6i32).q_shr(2, Odd), -1); + } + + #[test] + fn test_scale_rounding_odd() { + assert_eq!(0i32.q_scale(Scaler::new(0.5, Odd)), 0); + assert_eq!(1i32.q_scale(Scaler::new(0.5, Odd)), 1); + assert_eq!(2i32.q_scale(Scaler::new(0.5, Odd)), 1); + assert_eq!(3i32.q_scale(Scaler::new(0.5, Odd)), 1); + assert_eq!((-1i32).q_scale(Scaler::new(0.5, Odd)), -1); + assert_eq!((-2i32).q_scale(Scaler::new(0.5, Odd)), -1); + assert_eq!((-3i32).q_scale(Scaler::new(0.5, Odd)), -1); + assert_eq!(2i32.q_scale(Scaler::new(0.25, Odd)), 1); + assert_eq!(3i32.q_scale(Scaler::new(0.25, Odd)), 1); + assert_eq!(4i32.q_scale(Scaler::new(0.25, Odd)), 1); + assert_eq!(5i32.q_scale(Scaler::new(0.25, Odd)), 1); + assert_eq!(6i32.q_scale(Scaler::new(0.25, Odd)), 1); + assert_eq!((-2i32).q_scale(Scaler::new(0.25, Odd)), -1); + assert_eq!((-3i32).q_scale(Scaler::new(0.25, Odd)), -1); + assert_eq!((-4i32).q_scale(Scaler::new(0.25, Odd)), -1); + assert_eq!((-5i32).q_scale(Scaler::new(0.25, Odd)), -1); + assert_eq!((-6i32).q_scale(Scaler::new(0.25, Odd)), -1); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/sigmoid.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/sigmoid.rs new file mode 100644 index 0000000..341ee94 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/sigmoid.rs @@ -0,0 +1,142 @@ +#![allow(clippy::excessive_precision)] +use crate::frame::element_wise::ElementWiseKer; +use tract_data::internal::*; + +pub fn ssigmoid(x: f32) -> f32 { + const LOW: f32 = -18.6; + const HIGH: f32 = -LOW; + + const ALPHA_13: f32 = -4.433153405e-18; + const ALPHA_11: f32 = 1.169974371e-14; + const ALPHA_9: f32 = -1.875289645e-11; + const ALPHA_7: f32 = 4.257889523e-8; + const ALPHA_5: f32 = 0.00004811817576; + const ALPHA_3: f32 = 0.008163842030; + const ALPHA_1: f32 = 0.2499999971; + const BETA_6: f32 = 3.922935744e-6; + const BETA_4: f32 = 0.001524872358; + const BETA_2: f32 = 0.1159886749; + const BETA_0: f32 = 1.0; + + let x = x.clamp(LOW, HIGH); + + let x2 = x * x; + + let p = ALPHA_13; + let p = x2 * p + ALPHA_11; + let p = x2 * p + ALPHA_9; + let p = x2 * p + ALPHA_7; + let p = x2 * p + ALPHA_5; + let p = x2 * p + ALPHA_3; + let p = x2 * p + ALPHA_1; + let p = p * x; + + let q = BETA_6; + let q = x2 * q + BETA_4; + let q = x2 * q + BETA_2; + let q = x2 * q + BETA_0; + + p / q + 0.5 +} + +pub fn hsigmoid(x: f16) -> f16 { + /* + * (x (0.249895 + x^2 (0.00400222 - 0.0000124702 x^2))) + * / + * (1. + 0.098734 x^2) + */ + + const LOW: f16 = f16::from_f32_const(-6.92); + const HIGH: f16 = f16::from_f32_const(6.92); + + const ALPHA_5: f16 = f16::from_f32_const(-0.0000124702); + const ALPHA_3: f16 = f16::from_f32_const(0.00400222); + const ALPHA_1: f16 = f16::from_f32_const(0.249895); + + const BETA_2: f16 = f16::from_f32_const(0.098734); + const BETA_0: f16 = f16::from_f32_const(1.0); + + let x = x.clamp(LOW, HIGH); + + let x2 = x * x; + + let p = ALPHA_5; + let p = x2 * p + ALPHA_3; + let p = x2 * p + ALPHA_1; + let p = p * x; + + let q = BETA_2; + let q = x2 * q + BETA_0; + + p / q + f16::from_f32_const(0.5) +} + +#[derive(Clone, Debug)] +pub struct SSigmoid4; + +impl ElementWiseKer for SSigmoid4 { + fn name() -> &'static str { + "generic" + } + + fn alignment_bytes() -> usize { + 16 + } + + fn alignment_items() -> usize { + 4 + } + + fn nr() -> usize { + 4 + } + + fn run(x: &mut [f32], _: ()) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px = ssigmoid(*px)) + } +} + +#[derive(Clone, Debug)] +pub struct HSigmoid8; + +impl ElementWiseKer for HSigmoid8 { + fn name() -> &'static str { + "generic" + } + + fn alignment_bytes() -> usize { + 16 + } + + fn alignment_items() -> usize { + 4 + } + + fn nr() -> usize { + 8 + } + + fn run(x: &mut [f16], _: ()) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px = hsigmoid(*px)) + } +} + +#[cfg(test)] +#[macro_use] +pub mod s { + sigmoid_frame_tests!(true, f32, crate::generic::sigmoid::SSigmoid4); +} + +#[cfg(test)] +#[macro_use] +pub mod h { + sigmoid_frame_tests!( + true, + tract_data::internal::f16, + crate::generic::sigmoid::HSigmoid8 + ); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/silu.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/silu.rs new file mode 100644 index 0000000..b003518 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/silu.rs @@ -0,0 +1,80 @@ +#![allow(clippy::excessive_precision)] +use crate::frame::element_wise::ElementWiseKer; +use tract_data::internal::*; + +#[derive(Clone, Debug)] +pub struct SSiLU4; + +impl ElementWiseKer for SSiLU4 { + fn name() -> &'static str { + "generic" + } + + fn alignment_bytes() -> usize { + 16 + } + + fn alignment_items() -> usize { + 4 + } + + fn nr() -> usize { + 4 + } + + fn run(x: &mut [f32], _: ()) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| { + let sigmoid = 1.0 / (1.0 + (-*px).exp()); + *px = *px * sigmoid; + }); + } +} + +#[derive(Clone, Debug)] +pub struct HSiLU8; + +impl ElementWiseKer for HSiLU8 { + fn name() -> &'static str { + "generic" + } + + fn alignment_bytes() -> usize { + 16 + } + + fn alignment_items() -> usize { + 4 + } + + fn nr() -> usize { + 8 + } + + fn run(x: &mut [f16], _: ()) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| { + let x_f32 = px.to_f32(); + let sigmoid = 1.0 / (1.0 + (-x_f32).exp()); + *px = f16::from_f32(x_f32 * sigmoid); + }); + } +} + +#[cfg(test)] +#[macro_use] +pub mod s { + silu_frame_tests!(true, f32, crate::generic::silu::SSiLU4); +} + +#[cfg(test)] +#[macro_use] +pub mod h { + silu_frame_tests!( + true, + tract_data::internal::f16, + crate::generic::silu::HSiLU8 + ); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/tanh.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/tanh.rs new file mode 100644 index 0000000..0f60dbf --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/tanh.rs @@ -0,0 +1,137 @@ +#![allow(clippy::excessive_precision)] +use crate::frame::element_wise::ElementWiseKer; +use tract_data::internal::*; + +pub fn stanh(x: f32) -> f32 { + const LOW: f32 = -8.9; + const HIGH: f32 = 8.9; + + const ALPHA_13: f32 = -8.488492677e-14; + const ALPHA_11: f32 = 5.277853000e-11; + const ALPHA_9: f32 = -2.022500419e-8; + const ALPHA_7: f32 = 0.00001115424833; + const ALPHA_5: f32 = 0.003103950131; + const ALPHA_3: f32 = 0.1308400453; + const ALPHA_1: f32 = 0.9999999934; + + const BETA_6: f32 = 0.0002546136580; + const BETA_4: f32 = 0.02449515379; + const BETA_2: f32 = 0.4641733162; + const BETA_0: f32 = 1.0; + + let x = x.clamp(LOW, HIGH); + + let x2 = x * x; + + let p = ALPHA_13; + let p = x2 * p + ALPHA_11; + let p = x2 * p + ALPHA_9; + let p = x2 * p + ALPHA_7; + let p = x2 * p + ALPHA_5; + let p = x2 * p + ALPHA_3; + let p = x2 * p + ALPHA_1; + let p = p * x; + + let q = BETA_6; + let q = x2 * q + BETA_4; + let q = x2 * q + BETA_2; + let q = x2 * q + BETA_0; + + p / q +} + +pub fn htanh(x: f16) -> f16 { + const LOW: f16 = f16::from_f32_const(-3.84); + const HIGH: f16 = f16::from_f32_const(3.84); + + const ALPHA_3: f16 = f16::from_f32_const(0.082654955); + const ALPHA_1: f16 = f16::from_f32_const(0.99963124); + + const BETA_4: f16 = f16::from_f32_const(0.0065383179); + const BETA_2: f16 = f16::from_f32_const(0.41401828); + const BETA_0: f16 = f16::from_f32_const(1.0); + + let x = x.clamp(LOW, HIGH); + + let x2 = x * x; + + let p = ALPHA_3; + let p = x2 * p + ALPHA_1; + let p = p * x; + + let q = BETA_4; + let q = x2 * q + BETA_2; + let q = x2 * q + BETA_0; + + p / q +} + +#[derive(Clone, Debug)] +pub struct STanh4; + +impl ElementWiseKer for STanh4 { + fn name() -> &'static str { + "generic" + } + + fn alignment_items() -> usize { + 16 + } + + fn alignment_bytes() -> usize { + 16 + } + + fn nr() -> usize { + 4 + } + + fn run(x: &mut [f32], _: ()) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px = stanh(*px)) + } +} + +#[cfg(test)] +#[macro_use] +pub mod s { + tanh_frame_tests!(true, f32, crate::generic::tanh::STanh4); +} + +#[derive(Clone, Debug)] +pub struct HTanh8; + +impl ElementWiseKer for HTanh8 { + fn name() -> &'static str { + "generic" + } + + fn alignment_items() -> usize { + 16 + } + + fn alignment_bytes() -> usize { + 16 + } + + fn nr() -> usize { + 8 + } + + fn run(x: &mut [f16], _: ()) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + x.iter_mut().for_each(|px| *px = htanh(*px)) + } +} + +#[cfg(test)] +#[macro_use] +pub mod h { + tanh_frame_tests!( + true, + tract_data::internal::f16, + crate::generic::tanh::HTanh8 + ); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/unicast.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/unicast.rs new file mode 100644 index 0000000..2d7d487 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/generic/unicast.rs @@ -0,0 +1,194 @@ +pub use tract_data::internal::f16; +unicast_impl_wrap!( + f32, + SUnicastMul4, + 4, + 4, + fn run(a: &mut [f32], b: &[f32]) { + debug_assert!(a.len() == b.len()); + debug_assert!(a.len() % Self::nr() == 0); + debug_assert!(a.as_ptr() as usize % Self::alignment_bytes() == 0); + debug_assert!(b.as_ptr() as usize % Self::alignment_bytes() == 0); + a.iter_mut().zip(b.iter()).for_each(|(a, b)| *a *= b) + } +); + +unicast_impl_wrap!( + f16, + HUnicastMul8, + 8, + 8, + fn run(a: &mut [f16], b: &[f16]) { + debug_assert!(a.len() == b.len()); + debug_assert!(a.len() % Self::nr() == 0); + debug_assert!(a.as_ptr() as usize % Self::alignment_bytes() == 0); + debug_assert!(b.as_ptr() as usize % Self::alignment_bytes() == 0); + a.iter_mut().zip(b.iter()).for_each(|(a, b)| *a *= b) + } +); + +unicast_impl_wrap!( + f32, + SUnicastAdd4, + 4, + 4, + fn run(a: &mut [f32], b: &[f32]) { + debug_assert!(a.len() == b.len()); + debug_assert!(a.len() % Self::nr() == 0); + debug_assert!(a.as_ptr() as usize % Self::alignment_bytes() == 0); + debug_assert!(b.as_ptr() as usize % Self::alignment_bytes() == 0); + a.iter_mut().zip(b.iter()).for_each(|(a, b)| *a += b) + } +); + +unicast_impl_wrap!( + f16, + HUnicastAdd8, + 8, + 8, + fn run(a: &mut [f16], b: &[f16]) { + debug_assert!(a.len() == b.len()); + debug_assert!(a.len() % Self::nr() == 0); + debug_assert!(a.as_ptr() as usize % Self::alignment_bytes() == 0); + debug_assert!(b.as_ptr() as usize % Self::alignment_bytes() == 0); + a.iter_mut().zip(b.iter()).for_each(|(a, b)| *a += b) + } +); + +unicast_impl_wrap!( + f32, + SUnicastSub4, + 4, + 4, + fn run(a: &mut [f32], b: &[f32]) { + debug_assert!(a.len() == b.len()); + debug_assert!(a.len() % Self::nr() == 0); + debug_assert!(a.as_ptr() as usize % Self::alignment_bytes() == 0); + debug_assert!(b.as_ptr() as usize % Self::alignment_bytes() == 0); + a.iter_mut().zip(b.iter()).for_each(|(a, b)| *a -= b) + } +); + +unicast_impl_wrap!( + f16, + HUnicastSub8, + 8, + 8, + fn run(a: &mut [f16], b: &[f16]) { + debug_assert!(a.len() == b.len()); + debug_assert!(a.len() % Self::nr() == 0); + debug_assert!(a.as_ptr() as usize % Self::alignment_bytes() == 0); + debug_assert!(b.as_ptr() as usize % Self::alignment_bytes() == 0); + a.iter_mut().zip(b.iter()).for_each(|(a, b)| *a -= b) + } +); + +unicast_impl_wrap!( + f32, + SUnicastSubF4, + 4, + 4, + fn run(a: &mut [f32], b: &[f32]) { + debug_assert!(a.len() == b.len()); + debug_assert!(a.len() % Self::nr() == 0); + debug_assert!(a.as_ptr() as usize % Self::alignment_bytes() == 0); + debug_assert!(b.as_ptr() as usize % Self::alignment_bytes() == 0); + a.iter_mut().zip(b.iter()).for_each(|(a, b)| *a = *b - *a) + } +); + +unicast_impl_wrap!( + f16, + HUnicastSubF8, + 8, + 8, + fn run(a: &mut [f16], b: &[f16]) { + debug_assert!(a.len() == b.len()); + debug_assert!(a.len() % Self::nr() == 0); + debug_assert!(a.as_ptr() as usize % Self::alignment_bytes() == 0); + debug_assert!(b.as_ptr() as usize % Self::alignment_bytes() == 0); + a.iter_mut().zip(b.iter()).for_each(|(a, b)| *a = *b - *a) + } +); + +unicast_impl_wrap!( + f32, + SUnicastMin4, + 4, + 4, + fn run(a: &mut [f32], b: &[f32]) { + debug_assert!(a.len() == b.len()); + debug_assert!(a.len() % Self::nr() == 0); + debug_assert!(a.as_ptr() as usize % Self::alignment_bytes() == 0); + debug_assert!(b.as_ptr() as usize % Self::alignment_bytes() == 0); + a.iter_mut().zip(b.iter()).for_each(|(a, b)| *a = a.min(*b)) + } +); + +unicast_impl_wrap!( + f16, + HUnicastMin8, + 8, + 8, + fn run(a: &mut [f16], b: &[f16]) { + debug_assert!(a.len() == b.len()); + debug_assert!(a.len() % Self::nr() == 0); + debug_assert!(a.as_ptr() as usize % Self::alignment_bytes() == 0); + debug_assert!(b.as_ptr() as usize % Self::alignment_bytes() == 0); + a.iter_mut().zip(b.iter()).for_each(|(a, b)| *a = a.min(*b)) + } +); + +unicast_impl_wrap!( + f32, + SUnicastMax4, + 4, + 4, + fn run(a: &mut [f32], b: &[f32]) { + debug_assert!(a.len() == b.len()); + debug_assert!(a.len() % Self::nr() == 0); + debug_assert!(a.as_ptr() as usize % Self::alignment_bytes() == 0); + debug_assert!(b.as_ptr() as usize % Self::alignment_bytes() == 0); + a.iter_mut().zip(b.iter()).for_each(|(a, b)| *a = a.max(*b)) + } +); + +unicast_impl_wrap!( + f16, + HUnicastMax8, + 8, + 8, + fn run(a: &mut [f16], b: &[f16]) { + debug_assert!(a.len() == b.len()); + debug_assert!(a.len() % Self::nr() == 0); + debug_assert!(a.as_ptr() as usize % Self::alignment_bytes() == 0); + debug_assert!(b.as_ptr() as usize % Self::alignment_bytes() == 0); + a.iter_mut().zip(b.iter()).for_each(|(a, b)| *a = a.max(*b)) + } +); + +#[cfg(test)] +#[macro_use] +pub mod s { + use super::*; + use proptest::strategy::Strategy; + crate::unicast_frame_tests!(true, f32, SUnicastMul4, |a, b| a * b); + crate::unicast_frame_tests!(true, f32, SUnicastAdd4, |a, b| a + b); + crate::unicast_frame_tests!(true, f32, SUnicastSub4, |a, b| a - b); + crate::unicast_frame_tests!(true, f32, SUnicastSubF4, |a, b| b - a); + crate::unicast_frame_tests!(true, f32, SUnicastMin4, |a, b| a.min(b)); + crate::unicast_frame_tests!(true, f32, SUnicastMax4, |a, b| a.max(b)); +} + +#[cfg(test)] +#[macro_use] +pub mod h { + use super::*; + use proptest::strategy::Strategy; + crate::unicast_frame_tests!(true, f16, HUnicastMul8, |a, b| a * b); + crate::unicast_frame_tests!(true, f16, HUnicastAdd8, |a, b| a + b); + crate::unicast_frame_tests!(true, f16, HUnicastSub8, |a, b| a - b); + crate::unicast_frame_tests!(true, f16, HUnicastSubF8, |a, b| b - a); + crate::unicast_frame_tests!(true, f16, HUnicastMin8, |a, b| a.min(b)); + crate::unicast_frame_tests!(true, f16, HUnicastMax8, |a, b| a.max(b)); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/hwbench/bandwidth.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/hwbench/bandwidth.rs new file mode 100644 index 0000000..74c6a0e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/hwbench/bandwidth.rs @@ -0,0 +1,159 @@ +use tract_data::itertools::Itertools; +use tract_data::prelude::Blob; + +use super::runner; + +#[cfg(target_arch = "x86_64")] +static mut HAS_AVX512: bool = false; + +#[cfg(target_arch = "x86_64")] +#[inline(never)] +fn load_a_slice(slice: &[u8], loops: usize) { + unsafe { + if HAS_AVX512 { + for _ in 0..loops { + let mut ptr = slice.as_ptr(); + let end = ptr.add(slice.len()); + while ptr < end { + std::arch::asm!(" + vmovaps zmm0, [rsi] + vmovaps zmm1, [rsi + 64] + vmovaps zmm2, [rsi + 128] + vmovaps zmm3, [rsi + 192] + vmovaps zmm4, [rsi + 256] + vmovaps zmm5, [rsi + 320] + vmovaps zmm6, [rsi + 384] + vmovaps zmm7, [rsi + 448] + ", inout("rsi") ptr, + out("zmm0") _, + out("zmm1") _, + ); + ptr = ptr.add(512); + } + } + } else { + let mut ptr = slice.as_ptr(); + let end = ptr.add(slice.len()); + for _ in 0..loops { + while ptr < end { + std::arch::asm!(" + vmovaps ymm0, [rsi] + vmovaps ymm1, [rsi + 32] + vmovaps ymm2, [rsi + 64] + vmovaps ymm3, [rsi + 96] + ", inout("rsi") ptr, + out("ymm0") _, + out("ymm1") _, + out("ymm2") _, + out("ymm3") _, + ); + ptr = ptr.add(128); + } + } + } + } +} + +#[cfg(target_arch = "aarch64")] +#[inline] +fn load_a_slice(slice: &[u8], loops: usize) { + unsafe { + for _ in 0..loops { + let mut ptr = slice.as_ptr(); + let end = ptr.add(slice.len()); + while ptr < end { + std::arch::asm!(" + ld1 {{v0.16b-v3.16b}}, [x0], #64 + ld1 {{v4.16b-v7.16b}}, [x0], #64 + ", inout("x0") ptr, + out("v0") _, + out("v1") _, + out("v2") _, + out("v3") _, + out("v4") _, + out("v5") _, + out("v6") _, + out("v7") _, + ); + } + } + } +} + +#[cfg(target_arch = "arm")] +#[inline(never)] +fn load_a_slice(slice: &[u8], loops: usize) { + unsafe { + for _ in 0..loops { + let mut ptr = slice.as_ptr(); + let end = ptr.add(slice.len()); + while ptr < end { + std::arch::asm!(" + vldmia r1!, {{q0-q3}} + vldmia r1!, {{q4-q7}} + ", inout("r1") ptr, + out("d0") _, out("d1") _, out("d2") _, out("d3") _, + out("d4") _, out("d5") _, out("d6") _, out("d7") _, + out("d8") _, out("d9") _, out("d10") _, out("d11") _, + out("d12") _, out("d13") _, out("d14") _, out("d15") _, + ); + } + } + } +} + +fn bandwidth_seq(slice_len: usize, threads: usize) -> f64 { + #[cfg(target_arch = "x86_64")] + unsafe { + HAS_AVX512 = std::is_x86_feature_detected!("avx512f"); + } + std::thread::scope(|s| { + let gards = (0..threads) + .map(|_| { + s.spawn(|| { + let buffer = unsafe { Blob::new_for_size_and_align(slice_len, 1024) }; + runner::run_bench(|loops| load_a_slice(&buffer, loops)) + }) + }) + .collect_vec(); + let time = gards.into_iter().map(|t| t.join().unwrap()).sum::() / threads as f64; + (slice_len * threads) as f64 / time + }) +} + +pub fn what_is_big() -> usize { + 1024 * 1024 * if cfg!(target_arch = "arm") { 64 } else { 256 } +} + +pub fn l1_bandwidth_seq(threads: usize) -> f64 { + // [1024, 2048, 4096, 8192, 16384, 32768, 65536] + [1024] + .into_iter() + .map(|slice_len| bandwidth_seq(slice_len, threads)) + .max_by_key(|x| *x as i64) + .unwrap() +} + +pub fn main_memory_bandwith_seq(threads: usize) -> f64 { + bandwidth_seq(what_is_big(), threads) +} + +#[ignore] +#[test] +fn b() { + let max = what_is_big(); + for threads in [1, 2, 3, 4] { + println!("Threads: {}", threads); + for size in (0..) + .flat_map(|po2| (0..2).map(move |f| (1024 + 512 * f) * (1 << po2))) + .take_while(|&s| s < max) + { + let bw = bandwidth_seq(size, threads); + println!( + "threads: {threads} slice: {} KiB bandwidth: {} GiB/s", + size as f64 / 1024., + (bw / (1024. * 1024. * 1024.)) as usize + ); + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/hwbench/mod.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/hwbench/mod.rs new file mode 100644 index 0000000..3732358 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/hwbench/mod.rs @@ -0,0 +1,4 @@ +pub mod runner; + +#[cfg(feature = "hwbench")] +pub mod bandwidth; diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/hwbench/runner.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/hwbench/runner.rs new file mode 100644 index 0000000..ec549e5 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/hwbench/runner.rs @@ -0,0 +1,127 @@ +#![allow(unused_macros)] + +use std::time::Duration; +use std::time::Instant; + +#[macro_export] +macro_rules! r1 { ($($stat:stmt)*) => { $( $stat )* } } +#[macro_export] +macro_rules! r2 { ($($stat:stmt)*) => { $( $stat )* $( $stat )* } } +#[macro_export] +macro_rules! r4 { ($($stat:stmt)*) => { r2!(r2!($($stat)*)) }} +#[macro_export] +macro_rules! r8 { ($($stat:stmt)*) => { r2!(r4!($($stat)*)) }} +#[macro_export] +macro_rules! r16 { ($($stat:stmt)*) => { r2!(r8!($($stat)*)) }} +#[macro_export] +macro_rules! r32 { ($($stat:stmt)*) => { r2!(r16!($($stat)*)) }} +#[macro_export] +macro_rules! r64 { ($($stat:stmt)*) => { r2!(r32!($($stat)*)) }} +#[macro_export] +macro_rules! r128 { ($($stat:stmt)*) => { r2!(r64!($($stat)*)) }} +#[macro_export] +macro_rules! r256 { ($($stat:stmt)*) => { r2!(r128!($($stat)*)) }} +#[macro_export] +macro_rules! r512 { ($($stat:stmt)*) => { r2!(r256!($($stat)*)) }} +#[macro_export] +macro_rules! r1024 { ($($stat:stmt)*) => { r2!(r512!($($stat)*)) }} +#[macro_export] +macro_rules! r2048 { ($($stat:stmt)*) => { r2!(r1024!($($stat)*)) }} +#[macro_export] +macro_rules! r4096 { ($($stat:stmt)*) => { r2!(r2048!($($stat)*)) }} +#[macro_export] +macro_rules! r8192 { ($($stat:stmt)*) => { r2!(r4096!($($stat)*)) }} + +#[macro_export] +macro_rules! b1 { ($($stat:stmt)*) => { nano::run_bench(|| { r1!($($stat)*); }) / 1.0 } } +#[macro_export] +macro_rules! b2 { ($($stat:stmt)*) => { nano::run_bench(|| { r2!($($stat)*); }) / 2.0 } } +#[macro_export] +macro_rules! b4 { ($($stat:stmt)*) => { nano::run_bench(|| { r4!($($stat)*); }) / 4.0 } } +#[macro_export] +macro_rules! b8 { ($($stat:stmt)*) => { nano::run_bench(|| { r8!($($stat)*); }) / 8.0 } } +#[macro_export] +macro_rules! b16 { ($($stat:stmt)*) => { nano::run_bench(|| { r16!($($stat)*); }) / 16.0 } } +#[macro_export] +macro_rules! b32 { ($($stat:stmt)*) => { nano::run_bench(|| { r32!($($stat)*); }) / 32.0 } } +#[macro_export] +macro_rules! b64 { ($($stat:stmt)*) => { nano::run_bench(|| { r64!($($stat)*); }) / 64.0 } } +#[macro_export] +macro_rules! b128 { ($($stat:stmt)*) => { nano::run_bench(|| { r128!($($stat)*); }) / 128.0 } } +#[macro_export] +macro_rules! b256 { ($($stat:stmt)*) => { nano::run_bench(|| { r256!($($stat)*); }) / 256.0 } } +#[macro_export] +macro_rules! b512 { ($($stat:stmt)*) => { nano::run_bench(|| { r512!($($stat)*); }) / 512.0 } } +#[macro_export] +macro_rules! b1024 { ($($stat:stmt)*) => { nano::run_bench(|| { r1024!($($stat)*); }) / 1024.0 } } +#[macro_export] +macro_rules! b2048 { ($($stat:stmt)*) => { nano::run_bench(|| { r2048!($($stat)*); }) / 2048.0 } } +#[macro_export] +macro_rules! b4096 { ($($stat:stmt)*) => { nano::run_bench(|| { r4096!($($stat)*); }) / 4096.0 } } +#[macro_export] +macro_rules! b8192 { ($($stat:stmt)*) => { nano::run_bench(|| { r8192!($($stat)*); }) / 8192.0 } } + +#[inline] +fn black_box(dummy: T) -> T { + unsafe { + let ret = std::ptr::read_volatile(&dummy); + std::mem::forget(dummy); + ret + } +} + +pub fn run_bench T + Copy>(f: F) -> f64 { + let start = Instant::now(); + let mut f = black_box(f); + black_box(f(1)); + let once = start.elapsed(); + let evaled = if once < Duration::from_millis(1) { + let start = Instant::now(); + black_box(f)(1000); + start.elapsed().as_secs_f64() / 1000. + } else { + once.as_secs_f64() + }; + // raw evaluation is over a second. stop right there + if evaled > 1.0 { + return evaled; + } + + // we want each individual sample to run for no less than + let minimum_sampling_time_s = 0.01; + let minimum_samples = 25; + let desired_bench_time = 1.0; + + let inner_loops = (minimum_sampling_time_s / evaled).max(1.0) as usize; + + let samples = + ((desired_bench_time / (inner_loops as f64 * evaled)) as usize).max(minimum_samples); + let warmup = (1.0 / evaled) as usize; + + // println!( + // "evaled: {:?} samples:{samples} inner_loops:{inner_loops} time:{}", + // Duration::from_secs_f64(evaled), + // (samples * inner_loops) as f64 * evaled + // ); + let mut measures = vec![0.0; samples]; + + black_box(f(warmup)); + for m in &mut measures { + let start = Instant::now(); + black_box(black_box(f))(inner_loops); + let time = start.elapsed().as_secs_f64(); + *m = time / inner_loops as f64 + } + measures.sort_by(|a, b| { + if a < b { + std::cmp::Ordering::Less + } else { + std::cmp::Ordering::Greater + } + }); + let q1 = measures[samples / 4]; + let q3 = measures[samples - samples / 4]; + let iq = q3 - q1; + measures.retain(|&x| x >= q1 - 3. * iq && x <= q3 + 3. * iq); + measures.iter().copied().sum::() / measures.len() as f64 +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/lib.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/lib.rs new file mode 100644 index 0000000..1db6784 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/lib.rs @@ -0,0 +1,438 @@ +#![allow(clippy::missing_safety_doc)] +#![allow(clippy::redundant_closure_call)] +#![allow(clippy::len_zero)] +#![allow(clippy::excessive_precision)] +#![allow(clippy::approx_constant)] +#![allow(clippy::manual_is_multiple_of)] +#![allow(unexpected_cfgs)] +#![allow(unused_macros)] +#[macro_use] +extern crate derive_new; +extern crate lazy_static; +extern crate log; +extern crate num_traits; +#[macro_use] +extern crate pastey; +#[cfg(test)] +extern crate proptest; + +include!(concat!(env!("OUT_DIR"), "/extern_kernel_macro.rs")); + +#[macro_use] +mod frame; +pub mod generic; +pub mod multithread; +pub use frame::weights::WeightType; +pub use generic::{ScaleShiftAndRound, Scaler}; +use lazy_static::lazy_static; +use mmm::{MMMInputFormat, MatMatMul, PanelExtractor}; +use tract_data::internal::TensorView; +#[cfg(target_arch = "x86_64")] +pub mod x86_64_fma; + +pub mod hwbench; + +#[cfg(target_arch = "aarch64")] +pub mod arm64; + +#[cfg(target_arch = "aarch64")] +pub use arm64::has_fp16; +use tract_itertools::Itertools; + +#[cfg(not(target_arch = "aarch64"))] +pub fn has_fp16() -> bool { + false +} + +#[cfg(any(target_arch = "arm", target_arch = "armv7", target_arch = "arm"))] +pub mod arm32; + +#[cfg(all(target_family = "wasm", target_feature = "simd128"))] +pub mod wasm; + +pub use self::frame::*; + +use tract_data::prelude::*; + +pub type MMMImpl = Box< + dyn Fn(Option, Option, Option) -> Box + Send + Sync, +>; + +type MMVImpl = Box, Option) -> Box + Send + Sync>; + +#[allow(clippy::type_complexity)] +pub struct Ops { + mmm_impls: Vec>, + panel_extractors: Vec, + + mmm_f64: MMMImpl, + mmv_f64: MMVImpl, + + mmm_f32: MMMImpl, + mmv_f32: MMVImpl, + + mmm_f16: MMMImpl, + mmv_f16: MMVImpl, + + qmmm_i32: MMMImpl, + qmmv_i32: MMVImpl, + + pub leaky_relu_f16: Box Box> + Send + Sync>, + pub leaky_relu_f32: Box Box> + Send + Sync>, + pub mul_by_scalar_f32: + Box Box> + Send + Sync>, + pub mul_by_scalar_f16: + Box Box> + Send + Sync>, + + pub sigmoid_f16: Box Box> + Send + Sync>, + pub sigmoid_f32: Box Box> + Send + Sync>, + pub tanh_f16: Box Box> + Send + Sync>, + pub tanh_f32: Box Box> + Send + Sync>, + pub erf_f32: Box Box> + Send + Sync>, + pub hardswish_f16: Box Box> + Send + Sync>, + pub hardswish_f32: Box Box> + Send + Sync>, + pub silu_f16: Box Box> + Send + Sync>, + pub silu_f32: Box Box> + Send + Sync>, + pub gelu_f16: Box Box> + Send + Sync>, + pub gelu_f32: Box Box> + Send + Sync>, + pub lut_u8: Box Box + Send + Sync>, + + pub max_f16: Box Box> + Send + Sync>, + pub max_f32: Box Box> + Send + Sync>, + + pub sum_f16: Box Box> + Send + Sync>, + pub sum_f32: Box Box> + Send + Sync>, + + pub softmax2_fastcompact_f16: + Box Box> + Send + Sync>, + pub softmax2_fastcompact_f32: + Box Box> + Send + Sync>, + + /// Fused row-wise RmsNorm: out_i = x_i * rsqrt(mean(x_i²) + eps). + /// Replaces a 4-call composition (MeanOfSquares + Add + Rsqrt + Mul) with + /// a single 2-pass kernel. Called once per row by `core::ops::nn::RmsNorm` + /// when the input is f32 and the axis is the last (contiguous) one. + pub rms_norm_f32: Box, +} + +impl Ops { + pub fn mmm_impls(&self) -> &[Box] { + &self.mmm_impls + } + + pub fn all_possible_packing( + &self, + weight_type: impl Into, + ) -> impl Iterator { + let weight_type = weight_type.into(); + self.mmm_impls + .iter() + .flat_map(|m| m.packings()) + .map(|p| &*p.0) + .flat_map(move |p| { + let mut packs: Vec<&dyn MMMInputFormat> = vec![]; + if p.precursor() == weight_type { + packs.push(p) + }; + for pe in &self.panel_extractors { + if pe.from.precursor() == weight_type && pe.to.dyn_eq(p) { + packs.push(&*pe.from); + } + } + packs.into_iter() + }) + .sorted_by_key(|p| p.to_string()) + .dedup() + } + + pub fn filter_impls<'o>( + &'o self, + weight: &'o dyn MMMInputFormat, + acc: &[DatumType], + act: DatumType, + store: DatumType, + ) -> impl Iterator< + Item = ( + &'o dyn MatMatMul, + usize, + &'o dyn MMMInputFormat, + Option<&'o PanelExtractor>, + &'o dyn MMMInputFormat, + ), + > { + let acc = acc.to_vec(); + self.mmm_impls + .iter() + .filter(move |mmm| acc.contains(&mmm.internal_type()) && mmm.stores().contains(&store)) + .flat_map(|mmm| { + mmm.packings() + .iter() + .enumerate() + .map(|(pack_ix, (a, b))| (&**mmm, pack_ix, &**a, &**b)) + }) + .filter_map(|(mmm, ix, a, b)| { + if a.dyn_eq(weight) { + Some((mmm, ix, a, None, b)) + } else { + self.panel_extractors + .iter() + .find(|pe| pe.from.dyn_eq(weight) && pe.to.dyn_eq(a)) + .map(|pe| (mmm, ix, a, Some(pe), b)) + } + }) + .filter(move |(_mmm, _ix, _a, _pe, b)| { + b.precursor().as_dt().is_some_and(|dt| dt == act) + }) + } + + pub fn panel_extractors(&self) -> &[mmm::panel_extract::PanelExtractor] { + &self.panel_extractors + } + + pub fn mmm( + &self, + accumulator: DatumType, + m: Option, + k: Option, + n: Option, + ) -> Option> { + use DatumType::*; + match accumulator { + F64 => Some(if n == Some(1) { + (self.mmv_f64)(m, k) + } else { + (self.mmm_f64)(m, k, n) + }), + F32 => Some(if n == Some(1) { + (self.mmv_f32)(m, k) + } else { + (self.mmm_f32)(m, k, n) + }), + F16 => Some(if n == Some(1) { + (self.mmv_f16)(m, k) + } else { + (self.mmm_f16)(m, k, n) + }), + I32 => Some(if n == Some(1) { + (self.qmmv_i32)(m, k) + } else { + (self.qmmm_i32)(m, k, n) + }), + _ => None, + } + } +} + +pub fn generic() -> Ops { + use crate::generic::mmm::*; + use element_wise::ElementWiseKer; + use reduce::{MapReduceKer, ReduceKer}; + let mut ops = Ops { + mmm_impls: vec![], + panel_extractors: vec![], + mmm_f64: Box::new(|_, _, _| generic_f64_4x4.mmm()), + mmv_f64: Box::new(|_, _| generic_f64_4x1.mmm()), + mmm_f32: Box::new(|_, _, _| generic_f32_4x4.mmm()), + mmv_f32: Box::new(|_, _| generic_f32_4x1.mmm()), + mmm_f16: Box::new(|_, _, _| generic_f16_4x4.mmm()), + mmv_f16: Box::new(|_, _| generic_f16_4x1.mmm()), + qmmm_i32: Box::new(|_, _, _| generic_i32_4x4.mmm()), + qmmv_i32: Box::new(|_, _| generic_i32_4x4.mmm()), + leaky_relu_f16: Box::new(|| generic::HLeakyRelu8::ew()), + leaky_relu_f32: Box::new(|| generic::SLeakyRelu4::ew()), + mul_by_scalar_f16: Box::new(|| generic::HMulByScalar8::ew()), + mul_by_scalar_f32: Box::new(|| generic::SMulByScalar4::ew()), + sigmoid_f16: Box::new(|| generic::HSigmoid8::ew()), + sigmoid_f32: Box::new(|| generic::SSigmoid4::ew()), + tanh_f16: Box::new(|| generic::HTanh8::ew()), + tanh_f32: Box::new(|| generic::STanh4::ew()), + erf_f32: Box::new(|| generic::SErf4::ew()), + hardswish_f16: Box::new(|| generic::HHardSwish8::ew()), + hardswish_f32: Box::new(|| generic::SHardSwish4::ew()), + silu_f16: Box::new(|| generic::HSiLU8::ew()), + silu_f32: Box::new(|| generic::SSiLU4::ew()), + gelu_f16: Box::new(|| generic::HGelu8::ew()), + gelu_f32: Box::new(|| generic::SGelu4::ew()), + lut_u8: Box::new(|table: &[u8]| Box::new(lut::LutImpl::::new(table))), + max_f16: Box::new(|| generic::reduce::max::HMax8::red()), + max_f32: Box::new(|| generic::reduce::max::SMax4::red()), + sum_f16: Box::new(|| generic::reduce::sum::HSum8::red()), + sum_f32: Box::new(|| generic::reduce::sum::SSum4::red()), + /* + activation_f32: Box::new(|microcode| generic::SActivation::new(microcode)) + */ + softmax2_fastcompact_f16: Box::new(|| generic::reduce::softmax_l2::HSoftMaxL2::red()), + softmax2_fastcompact_f32: Box::new(|| generic::reduce::softmax_l2::SSoftMaxL2::red()), + rms_norm_f32: Box::new(generic::rms_norm::rms_norm_f32), + }; + crate::generic::mmm::plug(&mut ops); + ops +} + +#[allow(unreachable_code, unused_mut, unexpected_cfgs)] +pub fn best() -> Ops { + let mut ops = generic(); + #[cfg(target_arch = "x86_64")] + x86_64_fma::plug(&mut ops); + #[cfg(any(target_arch = "arm", target_arch = "armv7"))] + arm32::plug(&mut ops); + #[cfg(target_arch = "aarch64")] + arm64::plug(&mut ops); + #[cfg(all(target_family = "wasm", target_feature = "simd128"))] + wasm::plug(&mut ops); + + ops +} + +lazy_static::lazy_static! { + static ref OPS: Ops = { + best() + }; +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum BinOp { + Min, + Max, + Add, + Mul, + Sub, + SubF, +} + +impl BinOp { + pub fn flip(&self) -> BinOp { + use BinOp::*; + match self { + Sub => SubF, + SubF => Sub, + sym => *sym, + } + } +} + +fn register_all_unicast(registry: &mut LinalgRegistry) { + generic::register_all_unicast(registry); + #[cfg(target_arch = "aarch64")] + arm64::register_all_unicast(registry); +} + +fn register_all_by_scalar(registry: &mut LinalgRegistry) { + generic::register_all_by_scalar(registry); + #[cfg(target_arch = "aarch64")] + arm64::register_all_by_scalar(registry); +} + +pub type LinalgFn = dyn Fn(&mut TensorView, &TensorView) -> TractResult<()> + Send + Sync; +type LinalgRegistry = HashMap<(BinOp, DatumType), Box Box + Send + Sync>>; +lazy_static! { + static ref BIN_UNICAST_OPS: Mutex = { + let mut registry = HashMap::default(); + register_all_unicast(&mut registry); + Mutex::new(registry) + }; + static ref BIN_BY_SCALAR_OPS: Mutex = { + let mut registry = HashMap::default(); + register_all_by_scalar(&mut registry); + Mutex::new(registry) + }; +} + +pub fn bin_by_scalar(dt: DatumType, bin: BinOp) -> Option> { + let map = BIN_BY_SCALAR_OPS.lock().unwrap(); + if (dt == DatumType::F16) && !has_fp16() { + return None; + } + map.get(&(bin, dt)).map(|it| (it)()) +} + +pub fn bin_unicast(dt: DatumType, bin: BinOp) -> Option> { + let map = BIN_UNICAST_OPS.lock().unwrap(); + if (dt == DatumType::F16) && !has_fp16() { + return None; + } + map.get(&(bin, dt)).map(|it| (it)()) +} + +pub fn ops() -> &'static Ops { + &OPS +} + +use dyn_eq::DynEq; +use num_traits::*; +use std::collections::HashMap; +use std::fmt::Debug; +use std::ops::*; +use std::sync::Mutex; + +pub trait LADatum: + Sized + + std::fmt::Display + + Debug + + Copy + + Clone + + Zero + + One + + 'static + + Add + + Sub + + Mul + + AddAssign + + PartialOrd + + Bounded + + tract_data::prelude::Datum +{ + #[cfg(test)] + fn strat() -> proptest::prelude::BoxedStrategy; +} + +#[cfg(test)] +use proptest::prelude::*; + +impl LADatum for f16 { + #[cfg(test)] + fn strat() -> BoxedStrategy { + f32::strat().prop_map(|f| f.as_()).boxed() + } +} + +impl LADatum for f32 { + #[cfg(test)] + fn strat() -> BoxedStrategy { + (-1000isize..1000).prop_map(|i| i as f32 / 1000.0).boxed() + } +} + +impl LADatum for f64 { + #[cfg(test)] + fn strat() -> BoxedStrategy { + (-1000isize..1000).prop_map(|i| i as f64 / 1000.0).boxed() + } +} + +impl LADatum for u8 { + #[cfg(test)] + fn strat() -> BoxedStrategy { + any::().boxed() + } +} + +impl LADatum for i8 { + #[cfg(test)] + fn strat() -> BoxedStrategy { + any::().boxed() + } +} + +impl LADatum for i32 { + #[cfg(test)] + fn strat() -> BoxedStrategy { + any::().boxed() + } +} + +#[cfg(test)] +#[allow(dead_code)] +fn setup_test_logger() { + let _ = env_logger::Builder::from_env("TRACT_LOG").try_init(); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/multithread.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/multithread.rs new file mode 100644 index 0000000..ecf7b56 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/multithread.rs @@ -0,0 +1,93 @@ +use std::cell::RefCell; +#[cfg(feature = "multithread-mm")] +use std::sync::atomic::{AtomicUsize, Ordering}; +#[allow(unused_imports)] +use std::sync::{Arc, Mutex}; + +#[cfg(feature = "multithread-mm")] +use rayon::{ThreadPool, ThreadPoolBuilder}; + +#[derive(Debug, Clone, Default)] +pub enum Executor { + #[default] + SingleThread, + #[cfg(feature = "multithread-mm")] + MultiThread(Arc), + /// Use rayon's GLOBAL thread pool — the one set up by + /// `wasm_bindgen_rayon::init_thread_pool` on `wasm32-unknown-unknown`, + /// or rayon's auto-initialised default on native. + /// + /// Exists because `Arc` cannot be constructed on + /// `wasm32-unknown-unknown`: rayon's default `spawn_handler` calls + /// `std::thread::spawn`, which is unsupported there. The only working + /// route is rayon's global pool, accessed via `into_par_iter` directly. + #[cfg(feature = "multithread-mm")] + RayonGlobal, +} + +impl Executor { + #[cfg(feature = "multithread-mm")] + pub fn multithread(n: usize) -> Executor { + Executor::multithread_with_name(n, "tract-default") + } + + #[cfg(feature = "multithread-mm")] + pub fn multithread_with_name(n: usize, name: &str) -> Executor { + let name = name.to_string(); + let pool = ThreadPoolBuilder::new() + .thread_name(move |n| format!("{name}-{n}")) + .num_threads(n) + .build() + .unwrap(); + Executor::MultiThread(Arc::new(pool)) + } +} + +static DEFAULT_EXECUTOR: Mutex = Mutex::new(Executor::SingleThread); + +thread_local! { + static TLS_EXECUTOR_OVERRIDE: RefCell> = Default::default(); +} + +pub fn current_tract_executor() -> Executor { + if let Some(over_ride) = TLS_EXECUTOR_OVERRIDE.with_borrow(|tls| tls.clone()) { + over_ride + } else { + DEFAULT_EXECUTOR.lock().unwrap().clone() + } +} + +pub fn set_default_executor(executor: Executor) { + *DEFAULT_EXECUTOR.lock().unwrap() = executor; +} + +pub fn multithread_tract_scope R>(pool: Executor, f: F) -> R { + let previous = TLS_EXECUTOR_OVERRIDE.replace(Some(pool)); + let result = f(); + TLS_EXECUTOR_OVERRIDE.set(previous); + result +} + +/// Threshold (in panels) below which the rayon MMM dispatcher skips +/// parallelism and runs inline single-threaded. Below this size, +/// per-call dispatch overhead (~5 µs native, ~50 µs wasm-bindgen-rayon +/// worker) exceeds the parallel speedup. +/// +/// Default `64`. Tune higher for many-small-MMM workloads (mobile vision, +/// streaming RNN) or lower for transformer-class workloads where every MMM +/// is large. `0` disables the gate entirely (always thread). +#[cfg(feature = "multithread-mm")] +static THREADING_PANEL_THRESHOLD: AtomicUsize = AtomicUsize::new(64); + +/// Read the current MMM panel-count threshold for the rayon path. +#[cfg(feature = "multithread-mm")] +pub fn current_threading_panel_threshold() -> usize { + THREADING_PANEL_THRESHOLD.load(Ordering::Relaxed) +} + +/// Set the MMM panel-count threshold for the rayon path. Default is `64`. +/// Pass `0` to thread regardless of size. +#[cfg(feature = "multithread-mm")] +pub fn set_threading_panel_threshold(panels: usize) { + THREADING_PANEL_THRESHOLD.store(panels, Ordering::Relaxed); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/wasm.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/wasm.rs new file mode 100644 index 0000000..86947ca --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/wasm.rs @@ -0,0 +1,3208 @@ +/// Wasm SIMD implementation of `MatMatMulKer` +/// +/// To run test, you need to install `wasmtime` +/// and export the following environment variables: +/// ``` +/// > export RUSTFLAGS='-C target-feature=+simd128' +/// > export CARGO_TARGET_WASM32_WASI_RUNNER=wasmtime +/// > cargo test --target=wasm32-wasi +/// ``` +use crate::mmm::FusedKerSpec; +use crate::mmm::ImplementationQuality; +use crate::{Ops, Scaler}; + +#[cfg(target_feature = "relaxed-simd")] +use crate::frame::element_wise::ElementWiseKer; + +// f32x4 mul+add → relaxed FMA when the build has +relaxed-simd, else explicit +// mul+add. Lets the MMM kernels emit f32x4.relaxed_madd without duplicating +// kernel source. Per PR #2199: LLVM does not auto-emit relaxed_madd from +// f32x4_add(f32x4_mul(...)) even with +relaxed-simd — hand emission is needed. +// +// Caller must have `use std::arch::wasm32::*;` in scope (every kernel does). +// Args are passed (acc, a, b); evaluation order differs between the two arms +// (acc-first in baseline, acc-last in FMA), so callers must pass simple +// variable names rather than expressions with side effects. +#[cfg(target_feature = "relaxed-simd")] +macro_rules! madd_f32x4 { + ($acc:expr, $a:expr, $b:expr) => { + f32x4_relaxed_madd($a, $b, $acc) + }; +} + +#[cfg(not(target_feature = "relaxed-simd"))] +macro_rules! madd_f32x4 { + ($acc:expr, $a:expr, $b:expr) => { + f32x4_add($acc, f32x4_mul($a, $b)) + }; +} + +// Always-non-fused madd. Used by kernels with ≤4 SIMD accumulators per K-step +// (wasm_f32_4x1, _8x1, _16x1, _4x4), where the destructive `fmla.4s` +// emitted by +relaxed-simd creates a 4-cycle accumulator RAW recurrence +// that throttles throughput to 1 FMA/cycle even though Apple-class ARM64 +// pipes can do 4. The separate `fmul.4s; fadd.4s` form gives each multiply +// a fresh destination register, letting the OoO renamer overlap the next +// iteration's multiply with the in-flight add. Measured: under +// +simd128,+relaxed-simd these kernels are 19-28% slower than under +// +simd128 when using the fused form on Apple M1 — both wasmtime +// (Cranelift) and Node 20 (V8) reproduce identically. Wider kernels +// (wasm_f32_32x1 with 8 accs, wasm_f32_8x8 with 16) keep the fused form +// because their pipe is saturated and FMA's 1-instruction-per-madd wins. +// +// Cross-check: XNNPACK only ships wasmrelaxedsimd-fma GEMM kernels at +// NR=8 (i.e. ≥8 accumulator-equivalents), independently arriving at the +// same threshold without writing it down. +macro_rules! madd_f32x4_nofma { + ($acc:expr, $a:expr, $b:expr) => { + f32x4_add($acc, f32x4_mul($a, $b)) + }; +} + +pub fn plug(ops: &mut Ops) { + ops.mmm_impls.push(wasm_f32_4x4.mmm()); + ops.mmm_impls.push(wasm_f32_4x1.mmm()); + ops.mmm_impls.push(wasm_f32_8x1.mmm()); + ops.mmm_impls.push(wasm_f32_16x1.mmm()); + ops.mmm_impls.push(wasm_f32_32x1.mmm()); + ops.mmm_impls.push(wasm_f32_8x8.mmm()); + // int8 -> i32 matmul: SIMD kernel (was generic scalar). ManuallyOptimized so + // strategize's retain() keeps it over generic_i32_4x4 for i8 packing. + ops.mmm_impls.push(wasm_i32_4x4.mmm()); + ops.qmmm_i32 = Box::new(|_, _, _| wasm_i32_4x4.mmm()); + // Selection paths. Both rely on kernel_selection::strategize honouring + // the mmm_f32 / mmv_f32 callback, which it only does when the callback's + // kernel is tagged ManuallyOptimized. Otherwise strategize falls through + // to list_impls, whose retain() keeps only the top ImplementationQuality + // and drops every TargetOptimized kernel. + // - N>1 (GEMM): mmm_f32 returns 8x8, so 8x8 MUST be ManuallyOptimized. + // If it were TargetOptimized it would be dropped by retain(), and the + // N>1 branch's max(nr*mr) over the surviving (ManuallyOptimized) GEMV + // kernels would pick wasm_f32_32x1 — a matrix×vector kernel — for + // every GEMM. + // - N=1 (GEMV): mmv_f32 routes by M-band to the kernel whose MR fits. + // The four GEMV kernels are ManuallyOptimized for the same reason — + // without the tag strategize discards the callback and picks + // max(mr)=32x1 for every M, leaving up to ~37% on the table for + // small-M GEMV. + ops.mmm_f32 = Box::new(|_m, _k, _n| wasm_f32_8x8.mmm()); + // Bands derived from microbench_dispatch_gemv. At each band edge, using + // the next-larger kernel beats halving outer iterations of the smaller + // one (1 outer with ILP-absorbed padding > 2 outer with kernel preamble + // doubled). M=4/8/16 are exact tile fits at the lower edges; M=17/9/5 + // are the first values where the next-larger kernel wins. + ops.mmv_f32 = Box::new(|m, _k| match m.unwrap_or(0) { + 0..=4 => wasm_f32_4x1.mmm(), + 5..=8 => wasm_f32_8x1.mmm(), + 9..=16 => wasm_f32_16x1.mmm(), + _ => wasm_f32_32x1.mmm(), + }); + // Relaxed-SIMD activation kernels (FMA path). Only installed when the + // build has `+relaxed-simd`; otherwise the slots stay at the generic + // scalar polynomial. + #[cfg(target_feature = "relaxed-simd")] + { + ops.sigmoid_f32 = Box::new(|| WasmSigmoid4Relaxed::ew()); + ops.tanh_f32 = Box::new(|| WasmTanh4Relaxed::ew()); + } +} + +unsafe fn kernel_f32_4x4(mut pnl: *const FusedKerSpec) -> isize { + use std::arch::wasm32::*; + + unsafe { + // Each of these variables stores a row of the matrix, + // consisting of four packed `f32` numbers. + let mut ab0 = f32x4_splat(0.0); + let mut ab1 = f32x4_splat(0.0); + let mut ab2 = f32x4_splat(0.0); + let mut ab3 = f32x4_splat(0.0); + + while !pnl.is_null() { + match *pnl { + FusedKerSpec::Done => break, + FusedKerSpec::Clear => { + let a = f32x4_splat(0.0); + ab0 = a; + ab1 = a; + ab2 = a; + ab3 = a; + } + FusedKerSpec::LoadTile(_cols, rows) => { + let rows = rows as *const v128; + ab0 = *rows; + ab1 = *rows.add(1); + ab2 = *rows.add(2); + ab3 = *rows.add(3); + } + FusedKerSpec::ScalarMin(a) => { + let a = f32x4_splat(a); + ab0 = f32x4_min(a, ab0); + ab1 = f32x4_min(a, ab1); + ab2 = f32x4_min(a, ab2); + ab3 = f32x4_min(a, ab3); + } + FusedKerSpec::ScalarMax(a) => { + let a = f32x4_splat(a); + ab0 = f32x4_max(a, ab0); + ab1 = f32x4_max(a, ab1); + ab2 = f32x4_max(a, ab2); + ab3 = f32x4_max(a, ab3); + } + FusedKerSpec::ScalarAdd(a) => { + let a = f32x4_splat(a); + ab0 = f32x4_add(a, ab0); + ab1 = f32x4_add(a, ab1); + ab2 = f32x4_add(a, ab2); + ab3 = f32x4_add(a, ab3); + } + FusedKerSpec::ScalarMul(a) => { + let a = f32x4_splat(a); + ab0 = f32x4_mul(a, ab0); + ab1 = f32x4_mul(a, ab1); + ab2 = f32x4_mul(a, ab2); + ab3 = f32x4_mul(a, ab3); + } + FusedKerSpec::ScalarSub(a) => { + let a = f32x4_splat(a); + ab0 = f32x4_sub(a, ab0); + ab1 = f32x4_sub(a, ab1); + ab2 = f32x4_sub(a, ab2); + ab3 = f32x4_sub(a, ab3); + } + FusedKerSpec::ScalarSubF(a) => { + let a = f32x4_splat(a); + ab0 = f32x4_sub(ab0, a); + ab1 = f32x4_sub(ab1, a); + ab2 = f32x4_sub(ab2, a); + ab3 = f32x4_sub(ab3, a); + } + FusedKerSpec::LeakyRelu(a) => { + let a = f32x4_splat(a); + let zero = f32x4_splat(0.0); + + let mask0 = f32x4_gt(ab0, zero); + ab0 = v128_bitselect(ab0, f32x4_mul(a, ab0), mask0); + + let mask1 = f32x4_gt(ab1, zero); + ab1 = v128_bitselect(ab1, f32x4_mul(a, ab1), mask1); + + let mask2 = f32x4_gt(ab2, zero); + ab2 = v128_bitselect(ab2, f32x4_mul(a, ab2), mask2); + + let mask3 = f32x4_gt(ab3, zero); + ab3 = v128_bitselect(ab3, f32x4_mul(a, ab3), mask3); + } + FusedKerSpec::PerRowMin(row) => { + let row = std::slice::from_raw_parts(row, 4); + ab0 = f32x4_min(f32x4_splat(row[0]), ab0); + ab1 = f32x4_min(f32x4_splat(row[1]), ab1); + ab2 = f32x4_min(f32x4_splat(row[2]), ab2); + ab3 = f32x4_min(f32x4_splat(row[3]), ab3); + } + FusedKerSpec::PerRowMax(row) => { + let row = std::slice::from_raw_parts(row, 4); + ab0 = f32x4_max(f32x4_splat(row[0]), ab0); + ab1 = f32x4_max(f32x4_splat(row[1]), ab1); + ab2 = f32x4_max(f32x4_splat(row[2]), ab2); + ab3 = f32x4_max(f32x4_splat(row[3]), ab3); + } + FusedKerSpec::PerRowAdd(row) => { + let row = std::slice::from_raw_parts(row, 4); + ab0 = f32x4_add(f32x4_splat(row[0]), ab0); + ab1 = f32x4_add(f32x4_splat(row[1]), ab1); + ab2 = f32x4_add(f32x4_splat(row[2]), ab2); + ab3 = f32x4_add(f32x4_splat(row[3]), ab3); + } + FusedKerSpec::PerRowMul(row) => { + let row = std::slice::from_raw_parts(row, 4); + ab0 = f32x4_mul(f32x4_splat(row[0]), ab0); + ab1 = f32x4_mul(f32x4_splat(row[1]), ab1); + ab2 = f32x4_mul(f32x4_splat(row[2]), ab2); + ab3 = f32x4_mul(f32x4_splat(row[3]), ab3); + } + FusedKerSpec::PerRowSub(row) => { + let row = std::slice::from_raw_parts(row, 4); + ab0 = f32x4_sub(f32x4_splat(row[0]), ab0); + ab1 = f32x4_sub(f32x4_splat(row[1]), ab1); + ab2 = f32x4_sub(f32x4_splat(row[2]), ab2); + ab3 = f32x4_sub(f32x4_splat(row[3]), ab3); + } + FusedKerSpec::PerRowSubF(row) => { + let row = std::slice::from_raw_parts(row, 4); + ab0 = f32x4_sub(ab0, f32x4_splat(row[0])); + ab1 = f32x4_sub(ab1, f32x4_splat(row[1])); + ab2 = f32x4_sub(ab2, f32x4_splat(row[2])); + ab3 = f32x4_sub(ab3, f32x4_splat(row[3])); + } + FusedKerSpec::PerColMin(cols) => { + let cols = v128_load(cols as *const v128); + ab0 = f32x4_min(cols, ab0); + ab1 = f32x4_min(cols, ab1); + ab2 = f32x4_min(cols, ab2); + ab3 = f32x4_min(cols, ab3); + } + FusedKerSpec::PerColMax(cols) => { + let cols = v128_load(cols as *const v128); + ab0 = f32x4_max(cols, ab0); + ab1 = f32x4_max(cols, ab1); + ab2 = f32x4_max(cols, ab2); + ab3 = f32x4_max(cols, ab3); + } + FusedKerSpec::PerColAdd(cols) => { + let cols = v128_load(cols as *const v128); + ab0 = f32x4_add(cols, ab0); + ab1 = f32x4_add(cols, ab1); + ab2 = f32x4_add(cols, ab2); + ab3 = f32x4_add(cols, ab3); + } + FusedKerSpec::PerColMul(cols) => { + let cols = v128_load(cols as *const v128); + ab0 = f32x4_mul(cols, ab0); + ab1 = f32x4_mul(cols, ab1); + ab2 = f32x4_mul(cols, ab2); + ab3 = f32x4_mul(cols, ab3); + } + FusedKerSpec::PerColSub(cols) => { + let cols = v128_load(cols as *const v128); + ab0 = f32x4_sub(cols, ab0); + ab1 = f32x4_sub(cols, ab1); + ab2 = f32x4_sub(cols, ab2); + ab3 = f32x4_sub(cols, ab3); + } + FusedKerSpec::PerColSubF(cols) => { + let cols = v128_load(cols as *const v128); + ab0 = f32x4_sub(ab0, cols); + ab1 = f32x4_sub(ab1, cols); + ab2 = f32x4_sub(ab2, cols); + ab3 = f32x4_sub(ab3, cols); + } + FusedKerSpec::QScale(shift, rp, mult) => { + let scaler = Scaler::from_fuse_params(shift, rp, mult); + let scale = f32x4_splat(scaler.scale); + ab0 = f32x4_mul(scale, ab0); + ab1 = f32x4_mul(scale, ab1); + ab2 = f32x4_mul(scale, ab2); + ab3 = f32x4_mul(scale, ab3); + } + FusedKerSpec::RoundingShiftRight(shift, _rp) => { + let shift = f32x4_splat(2f32.powi(-(shift as i32))); + ab0 = f32x4_mul(shift, ab0); + ab1 = f32x4_mul(shift, ab1); + ab2 = f32x4_mul(shift, ab2); + ab3 = f32x4_mul(shift, ab3); + } + FusedKerSpec::ShiftLeft(shift) => { + let shift = f32x4_splat(2f32.powi(shift as i32)); + ab0 = f32x4_mul(shift, ab0); + ab1 = f32x4_mul(shift, ab1); + ab2 = f32x4_mul(shift, ab2); + ab3 = f32x4_mul(shift, ab3); + } + FusedKerSpec::AddUnicast(tile) => { + let mut ptr: *const u8 = tile.ptr; + + let m0 = *(ptr as *const f32); + let m1 = *(ptr.offset(tile.col_byte_stride) as *const f32); + let m2 = *(ptr.offset(tile.col_byte_stride * 2) as *const f32); + let m3 = *(ptr.offset(tile.col_byte_stride * 3) as *const f32); + ab0 = f32x4_add(ab0, f32x4(m0, m1, m2, m3)); + ptr = ptr.add(tile.row_byte_stride as usize); + + let m0 = *(ptr as *const f32); + let m1 = *(ptr.offset(tile.col_byte_stride) as *const f32); + let m2 = *(ptr.offset(tile.col_byte_stride * 2) as *const f32); + let m3 = *(ptr.offset(tile.col_byte_stride * 3) as *const f32); + ab1 = f32x4_add(ab1, f32x4(m0, m1, m2, m3)); + ptr = ptr.add(tile.row_byte_stride as usize); + + let m0 = *(ptr as *const f32); + let m1 = *(ptr.offset(tile.col_byte_stride) as *const f32); + let m2 = *(ptr.offset(tile.col_byte_stride * 2) as *const f32); + let m3 = *(ptr.offset(tile.col_byte_stride * 3) as *const f32); + ab2 = f32x4_add(ab2, f32x4(m0, m1, m2, m3)); + ptr = ptr.add(tile.row_byte_stride as usize); + + let m0 = *(ptr as *const f32); + let m1 = *(ptr.offset(tile.col_byte_stride) as *const f32); + let m2 = *(ptr.offset(tile.col_byte_stride * 2) as *const f32); + let m3 = *(ptr.offset(tile.col_byte_stride * 3) as *const f32); + ab3 = f32x4_add(ab3, f32x4(m0, m1, m2, m3)); + } + FusedKerSpec::AddRowColProducts(rows, cols) => { + let cols = v128_load(cols as *const v128); + ab0 = madd_f32x4_nofma!(ab0, f32x4_splat(*rows.add(0)), cols); + ab1 = madd_f32x4_nofma!(ab1, f32x4_splat(*rows.add(1)), cols); + ab2 = madd_f32x4_nofma!(ab2, f32x4_splat(*rows.add(2)), cols); + ab3 = madd_f32x4_nofma!(ab3, f32x4_splat(*rows.add(3)), cols); + } + FusedKerSpec::Store(tile) => { + let mut ptr: *mut u8 = tile.ptr; + + *(ptr as *mut f32) = f32x4_extract_lane::<0>(ab0); + *(ptr.offset(tile.col_byte_stride) as *mut f32) = f32x4_extract_lane::<1>(ab0); + *(ptr.offset(tile.col_byte_stride * 2) as *mut f32) = + f32x4_extract_lane::<2>(ab0); + *(ptr.offset(tile.col_byte_stride * 3) as *mut f32) = + f32x4_extract_lane::<3>(ab0); + ptr = ptr.add(tile.row_byte_stride as usize); + + *(ptr as *mut f32) = f32x4_extract_lane::<0>(ab1); + *(ptr.offset(tile.col_byte_stride) as *mut f32) = f32x4_extract_lane::<1>(ab1); + *(ptr.offset(tile.col_byte_stride * 2) as *mut f32) = + f32x4_extract_lane::<2>(ab1); + *(ptr.offset(tile.col_byte_stride * 3) as *mut f32) = + f32x4_extract_lane::<3>(ab1); + ptr = ptr.add(tile.row_byte_stride as usize); + + *(ptr as *mut f32) = f32x4_extract_lane::<0>(ab2); + *(ptr.offset(tile.col_byte_stride) as *mut f32) = f32x4_extract_lane::<1>(ab2); + *(ptr.offset(tile.col_byte_stride * 2) as *mut f32) = + f32x4_extract_lane::<2>(ab2); + *(ptr.offset(tile.col_byte_stride * 3) as *mut f32) = + f32x4_extract_lane::<3>(ab2); + ptr = ptr.add(tile.row_byte_stride as usize); + + *(ptr as *mut f32) = f32x4_extract_lane::<0>(ab3); + *(ptr.offset(tile.col_byte_stride) as *mut f32) = f32x4_extract_lane::<1>(ab3); + *(ptr.offset(tile.col_byte_stride * 2) as *mut f32) = + f32x4_extract_lane::<2>(ab3); + *(ptr.offset(tile.col_byte_stride * 3) as *mut f32) = + f32x4_extract_lane::<3>(ab3); + } + FusedKerSpec::AddMatMul { + k, + pa, + pb, + packing: _, + } => { + let a = pa as *const f32; + let b = pb as *const v128; + for i in 0..k { + let a = std::slice::from_raw_parts(a.offset(4 * i as isize), 4); + let b = v128_load(b.offset(i as isize)); + ab0 = madd_f32x4_nofma!(ab0, f32x4_splat(a[0]), b); + ab1 = madd_f32x4_nofma!(ab1, f32x4_splat(a[1]), b); + ab2 = madd_f32x4_nofma!(ab2, f32x4_splat(a[2]), b); + ab3 = madd_f32x4_nofma!(ab3, f32x4_splat(a[3]), b); + } + } + } + pnl = pnl.add(1); + } + 0 + } +} + +MMMRustKernel!(kernel_f32_4x4 => wasm_f32_4x4(4,4)@(4,4) quality(ImplementationQuality::TargetOptimized)); + +/// WASM SIMD f32 4x1 kernel — GEMV-shaped variant for matrix-vector products +/// (single-column outputs, e.g., streaming-RNN inference where each frame's +/// activation is a single column). Mirrors the 4x4 kernel's FusedKerSpec +/// match arms but collapses the column dimension from 4 to 1: a single +/// f32x4 accumulator holds 4 output rows × 1 output column packed as +/// [ab[0], ab[1], ab[2], ab[3]]. +/// +/// Selection: tract-core's einsum kernel_selection::strategize() prefers +/// kernels with nr() == 1 when op.n.is_one(), so this kernel is +/// automatically picked for N=1 cases once registered. +unsafe fn kernel_f32_4x1(mut pnl: *const FusedKerSpec) -> isize { + use std::arch::wasm32::*; + + unsafe { + // Single accumulator: 4 rows × 1 col, packed into one f32x4. + // lane[i] holds ab[i] = the output value for row i (col 0). + let mut ab = f32x4_splat(0.0); + + while !pnl.is_null() { + match *pnl { + FusedKerSpec::Done => break, + FusedKerSpec::Clear => { + ab = f32x4_splat(0.0); + } + FusedKerSpec::LoadTile(_cols, rows) => { + // Tile is 4 rows × 1 col = 4 contiguous f32s = 1 v128 + ab = v128_load(rows as *const v128); + } + FusedKerSpec::ScalarMin(a) => { + ab = f32x4_min(f32x4_splat(a), ab); + } + FusedKerSpec::ScalarMax(a) => { + ab = f32x4_max(f32x4_splat(a), ab); + } + FusedKerSpec::ScalarAdd(a) => { + ab = f32x4_add(f32x4_splat(a), ab); + } + FusedKerSpec::ScalarMul(a) => { + ab = f32x4_mul(f32x4_splat(a), ab); + } + FusedKerSpec::ScalarSub(a) => { + ab = f32x4_sub(f32x4_splat(a), ab); + } + FusedKerSpec::ScalarSubF(a) => { + ab = f32x4_sub(ab, f32x4_splat(a)); + } + FusedKerSpec::LeakyRelu(a) => { + let zero = f32x4_splat(0.0); + let mask = f32x4_gt(ab, zero); + ab = v128_bitselect(ab, f32x4_mul(f32x4_splat(a), ab), mask); + } + FusedKerSpec::PerRowMin(row) => { + // 4 row values, applied to ab's 4 lanes in order + let r = v128_load(row as *const v128); + ab = f32x4_min(r, ab); + } + FusedKerSpec::PerRowMax(row) => { + let r = v128_load(row as *const v128); + ab = f32x4_max(r, ab); + } + FusedKerSpec::PerRowAdd(row) => { + let r = v128_load(row as *const v128); + ab = f32x4_add(r, ab); + } + FusedKerSpec::PerRowMul(row) => { + let r = v128_load(row as *const v128); + ab = f32x4_mul(r, ab); + } + FusedKerSpec::PerRowSub(row) => { + let r = v128_load(row as *const v128); + ab = f32x4_sub(r, ab); + } + FusedKerSpec::PerRowSubF(row) => { + let r = v128_load(row as *const v128); + ab = f32x4_sub(ab, r); + } + FusedKerSpec::PerColMin(cols) => { + // Single col value broadcast to all 4 rows + ab = f32x4_min(f32x4_splat(*cols), ab); + } + FusedKerSpec::PerColMax(cols) => { + ab = f32x4_max(f32x4_splat(*cols), ab); + } + FusedKerSpec::PerColAdd(cols) => { + ab = f32x4_add(f32x4_splat(*cols), ab); + } + FusedKerSpec::PerColMul(cols) => { + ab = f32x4_mul(f32x4_splat(*cols), ab); + } + FusedKerSpec::PerColSub(cols) => { + ab = f32x4_sub(f32x4_splat(*cols), ab); + } + FusedKerSpec::PerColSubF(cols) => { + ab = f32x4_sub(ab, f32x4_splat(*cols)); + } + FusedKerSpec::QScale(shift, rp, mult) => { + let scaler = Scaler::from_fuse_params(shift, rp, mult); + ab = f32x4_mul(f32x4_splat(scaler.scale), ab); + } + FusedKerSpec::RoundingShiftRight(shift, _rp) => { + let s = f32x4_splat(2f32.powi(-(shift as i32))); + ab = f32x4_mul(s, ab); + } + FusedKerSpec::ShiftLeft(shift) => { + let s = f32x4_splat(2f32.powi(shift as i32)); + ab = f32x4_mul(s, ab); + } + FusedKerSpec::AddUnicast(tile) => { + // 4 rows × 1 col, with row_byte_stride between rows (col_stride irrelevant for N=1) + let mut ptr: *const u8 = tile.ptr; + let m0 = *(ptr as *const f32); + ptr = ptr.add(tile.row_byte_stride as usize); + let m1 = *(ptr as *const f32); + ptr = ptr.add(tile.row_byte_stride as usize); + let m2 = *(ptr as *const f32); + ptr = ptr.add(tile.row_byte_stride as usize); + let m3 = *(ptr as *const f32); + ab = f32x4_add(ab, f32x4(m0, m1, m2, m3)); + } + FusedKerSpec::AddRowColProducts(rows, cols) => { + // ab[i] += rows[i] * cols[0] (cols[0] is the single col) + let r = v128_load(rows as *const v128); + let c = f32x4_splat(*cols); + ab = madd_f32x4_nofma!(ab, r, c); + } + FusedKerSpec::Store(tile) => { + // 4 rows × 1 col, write each lane to a separate row + let mut ptr: *mut u8 = tile.ptr; + *(ptr as *mut f32) = f32x4_extract_lane::<0>(ab); + ptr = ptr.add(tile.row_byte_stride as usize); + *(ptr as *mut f32) = f32x4_extract_lane::<1>(ab); + ptr = ptr.add(tile.row_byte_stride as usize); + *(ptr as *mut f32) = f32x4_extract_lane::<2>(ab); + ptr = ptr.add(tile.row_byte_stride as usize); + *(ptr as *mut f32) = f32x4_extract_lane::<3>(ab); + } + FusedKerSpec::AddMatMul { + k, + pa, + pb, + packing: _, + } => { + // A is packed [k][MR=4]: each k iter loads 4 contiguous f32s = 1 v128. + // B is packed [k][NR=1]: each k iter loads 1 scalar f32, broadcast. + // ab[i] += a[i] * b for all i in 0..4 → SIMD: ab += a_vec * b_splat + let a = pa as *const v128; + let b = pb as *const f32; + for i in 0..k { + let a_vec = v128_load(a.offset(i as isize)); + let b_splat = f32x4_splat(*b.offset(i as isize)); + ab = madd_f32x4_nofma!(ab, a_vec, b_splat); + } + } + } + pnl = pnl.add(1); + } + 0 + } +} + +// ManuallyOptimized so kernel_selection::strategize honours the M-band +// dispatch in mmv_f32 below. See module-level comment on plug(). +MMMRustKernel!(kernel_f32_4x1 => wasm_f32_4x1(4,1)@(4,1) quality(ImplementationQuality::ManuallyOptimized)); + +/// WASM SIMD f32 8x1 kernel — wider GEMV variant for matrix-vector products +/// on large M. Uses TWO independent f32x4 accumulators (rows 0-3 in ab_top, +/// rows 4-7 in ab_bot), enabling 2-way ILP within each k-iteration: +/// the inner loop issues two independent f32x4_add(f32x4_mul(...)) ops per +/// k-step, breaking the data-dependency chain depth from K to ~K/2 at the +/// hardware pipeline level. +/// +/// Compared to wasm_f32_4x1 (1 accumulator, k-serial dep chain), this is +/// targeted at GEMV ops where M is a multiple of 8 (or close to it). For +/// M=256 GRU gate matmuls (the dominant GEMV in DFN3), this should yield +/// ~2x speedup on the inner loop on hardware where SIMD FMLA throughput +/// exceeds 1 op/cycle. +/// +/// Selection: `kernel_selection::strategize()` prefers max mr() for n=1 +/// cases, so this kernel automatically wins over wasm_f32_4x1 for all N=1 +/// ops once registered (including small-M cases where it slightly wastes +/// rows — for M=1 lsnr_fc-style ops, that's 7-of-8 row waste, but those +/// ops are <1% of frame so the regression is noise). +unsafe fn kernel_f32_8x1(mut pnl: *const FusedKerSpec) -> isize { + use std::arch::wasm32::*; + + unsafe { + // Two accumulators: 8 rows × 1 col packed as [ab_top, ab_bot] + // ab_top.lane[i] holds row i (i in 0..4); ab_bot.lane[i] holds row i+4 + let mut ab_top = f32x4_splat(0.0); + let mut ab_bot = f32x4_splat(0.0); + + while !pnl.is_null() { + match *pnl { + FusedKerSpec::Done => break, + FusedKerSpec::Clear => { + ab_top = f32x4_splat(0.0); + ab_bot = f32x4_splat(0.0); + } + FusedKerSpec::LoadTile(_cols, rows) => { + // 8 rows × 1 col = 8 contiguous f32 = 2 v128 + let p = rows as *const v128; + ab_top = *p; + ab_bot = *p.add(1); + } + FusedKerSpec::ScalarMin(a) => { + let s = f32x4_splat(a); + ab_top = f32x4_min(s, ab_top); + ab_bot = f32x4_min(s, ab_bot); + } + FusedKerSpec::ScalarMax(a) => { + let s = f32x4_splat(a); + ab_top = f32x4_max(s, ab_top); + ab_bot = f32x4_max(s, ab_bot); + } + FusedKerSpec::ScalarAdd(a) => { + let s = f32x4_splat(a); + ab_top = f32x4_add(s, ab_top); + ab_bot = f32x4_add(s, ab_bot); + } + FusedKerSpec::ScalarMul(a) => { + let s = f32x4_splat(a); + ab_top = f32x4_mul(s, ab_top); + ab_bot = f32x4_mul(s, ab_bot); + } + FusedKerSpec::ScalarSub(a) => { + let s = f32x4_splat(a); + ab_top = f32x4_sub(s, ab_top); + ab_bot = f32x4_sub(s, ab_bot); + } + FusedKerSpec::ScalarSubF(a) => { + let s = f32x4_splat(a); + ab_top = f32x4_sub(ab_top, s); + ab_bot = f32x4_sub(ab_bot, s); + } + FusedKerSpec::LeakyRelu(a) => { + let s = f32x4_splat(a); + let zero = f32x4_splat(0.0); + let mask_t = f32x4_gt(ab_top, zero); + let mask_b = f32x4_gt(ab_bot, zero); + ab_top = v128_bitselect(ab_top, f32x4_mul(s, ab_top), mask_t); + ab_bot = v128_bitselect(ab_bot, f32x4_mul(s, ab_bot), mask_b); + } + FusedKerSpec::PerRowMin(row) => { + let p = row as *const v128; + let r_t = v128_load(p); + let r_b = v128_load(p.add(1)); + ab_top = f32x4_min(r_t, ab_top); + ab_bot = f32x4_min(r_b, ab_bot); + } + FusedKerSpec::PerRowMax(row) => { + let p = row as *const v128; + let r_t = v128_load(p); + let r_b = v128_load(p.add(1)); + ab_top = f32x4_max(r_t, ab_top); + ab_bot = f32x4_max(r_b, ab_bot); + } + FusedKerSpec::PerRowAdd(row) => { + let p = row as *const v128; + let r_t = v128_load(p); + let r_b = v128_load(p.add(1)); + ab_top = f32x4_add(r_t, ab_top); + ab_bot = f32x4_add(r_b, ab_bot); + } + FusedKerSpec::PerRowMul(row) => { + let p = row as *const v128; + let r_t = v128_load(p); + let r_b = v128_load(p.add(1)); + ab_top = f32x4_mul(r_t, ab_top); + ab_bot = f32x4_mul(r_b, ab_bot); + } + FusedKerSpec::PerRowSub(row) => { + let p = row as *const v128; + let r_t = v128_load(p); + let r_b = v128_load(p.add(1)); + ab_top = f32x4_sub(r_t, ab_top); + ab_bot = f32x4_sub(r_b, ab_bot); + } + FusedKerSpec::PerRowSubF(row) => { + let p = row as *const v128; + let r_t = v128_load(p); + let r_b = v128_load(p.add(1)); + ab_top = f32x4_sub(ab_top, r_t); + ab_bot = f32x4_sub(ab_bot, r_b); + } + FusedKerSpec::PerColMin(cols) => { + let c = f32x4_splat(*cols); + ab_top = f32x4_min(c, ab_top); + ab_bot = f32x4_min(c, ab_bot); + } + FusedKerSpec::PerColMax(cols) => { + let c = f32x4_splat(*cols); + ab_top = f32x4_max(c, ab_top); + ab_bot = f32x4_max(c, ab_bot); + } + FusedKerSpec::PerColAdd(cols) => { + let c = f32x4_splat(*cols); + ab_top = f32x4_add(c, ab_top); + ab_bot = f32x4_add(c, ab_bot); + } + FusedKerSpec::PerColMul(cols) => { + let c = f32x4_splat(*cols); + ab_top = f32x4_mul(c, ab_top); + ab_bot = f32x4_mul(c, ab_bot); + } + FusedKerSpec::PerColSub(cols) => { + let c = f32x4_splat(*cols); + ab_top = f32x4_sub(c, ab_top); + ab_bot = f32x4_sub(c, ab_bot); + } + FusedKerSpec::PerColSubF(cols) => { + let c = f32x4_splat(*cols); + ab_top = f32x4_sub(ab_top, c); + ab_bot = f32x4_sub(ab_bot, c); + } + FusedKerSpec::QScale(shift, rp, mult) => { + let scaler = Scaler::from_fuse_params(shift, rp, mult); + let s = f32x4_splat(scaler.scale); + ab_top = f32x4_mul(s, ab_top); + ab_bot = f32x4_mul(s, ab_bot); + } + FusedKerSpec::RoundingShiftRight(shift, _rp) => { + let s = f32x4_splat(2f32.powi(-(shift as i32))); + ab_top = f32x4_mul(s, ab_top); + ab_bot = f32x4_mul(s, ab_bot); + } + FusedKerSpec::ShiftLeft(shift) => { + let s = f32x4_splat(2f32.powi(shift as i32)); + ab_top = f32x4_mul(s, ab_top); + ab_bot = f32x4_mul(s, ab_bot); + } + FusedKerSpec::AddUnicast(tile) => { + // 8 rows × 1 col, stride is row_byte_stride between rows + let mut ptr: *const u8 = tile.ptr; + let m0 = *(ptr as *const f32); + ptr = ptr.add(tile.row_byte_stride as usize); + let m1 = *(ptr as *const f32); + ptr = ptr.add(tile.row_byte_stride as usize); + let m2 = *(ptr as *const f32); + ptr = ptr.add(tile.row_byte_stride as usize); + let m3 = *(ptr as *const f32); + ptr = ptr.add(tile.row_byte_stride as usize); + let m4 = *(ptr as *const f32); + ptr = ptr.add(tile.row_byte_stride as usize); + let m5 = *(ptr as *const f32); + ptr = ptr.add(tile.row_byte_stride as usize); + let m6 = *(ptr as *const f32); + ptr = ptr.add(tile.row_byte_stride as usize); + let m7 = *(ptr as *const f32); + ab_top = f32x4_add(ab_top, f32x4(m0, m1, m2, m3)); + ab_bot = f32x4_add(ab_bot, f32x4(m4, m5, m6, m7)); + } + FusedKerSpec::AddRowColProducts(rows, cols) => { + let p = rows as *const v128; + let r_t = v128_load(p); + let r_b = v128_load(p.add(1)); + let c = f32x4_splat(*cols); + ab_top = madd_f32x4_nofma!(ab_top, r_t, c); + ab_bot = madd_f32x4_nofma!(ab_bot, r_b, c); + } + FusedKerSpec::Store(tile) => { + // 8 rows × 1 col, write each lane to a separate row + let mut ptr: *mut u8 = tile.ptr; + *(ptr as *mut f32) = f32x4_extract_lane::<0>(ab_top); + ptr = ptr.add(tile.row_byte_stride as usize); + *(ptr as *mut f32) = f32x4_extract_lane::<1>(ab_top); + ptr = ptr.add(tile.row_byte_stride as usize); + *(ptr as *mut f32) = f32x4_extract_lane::<2>(ab_top); + ptr = ptr.add(tile.row_byte_stride as usize); + *(ptr as *mut f32) = f32x4_extract_lane::<3>(ab_top); + ptr = ptr.add(tile.row_byte_stride as usize); + *(ptr as *mut f32) = f32x4_extract_lane::<0>(ab_bot); + ptr = ptr.add(tile.row_byte_stride as usize); + *(ptr as *mut f32) = f32x4_extract_lane::<1>(ab_bot); + ptr = ptr.add(tile.row_byte_stride as usize); + *(ptr as *mut f32) = f32x4_extract_lane::<2>(ab_bot); + ptr = ptr.add(tile.row_byte_stride as usize); + *(ptr as *mut f32) = f32x4_extract_lane::<3>(ab_bot); + } + FusedKerSpec::AddMatMul { + k, + pa, + pb, + packing: _, + } => { + // A: packed [k][MR=8] = each k iter loads 8 f32 = 2 v128 + // B: packed [k][NR=1] = each k iter loads 1 scalar f32, broadcast + // The two fmadd ops on (ab_top, ab_bot) are independent — 2-way ILP per iter. + let a = pa as *const v128; + let b = pb as *const f32; + for i in 0..k { + let a_t = v128_load(a.offset((2 * i) as isize)); + let a_b = v128_load(a.offset((2 * i + 1) as isize)); + let b_splat = f32x4_splat(*b.offset(i as isize)); + ab_top = madd_f32x4_nofma!(ab_top, a_t, b_splat); + ab_bot = madd_f32x4_nofma!(ab_bot, a_b, b_splat); + } + } + } + pnl = pnl.add(1); + } + 0 + } +} + +MMMRustKernel!(kernel_f32_8x1 => wasm_f32_8x1(8,1)@(8,1) quality(ImplementationQuality::ManuallyOptimized)); + +/// WASM SIMD f32 16x1 kernel — wider GEMV variant for matrix-vector products +/// on very large M. Uses FOUR independent f32x4 accumulators (rows 0-3, +/// 4-7, 8-11, 12-15), enabling 4-way ILP within each k-iteration. +/// +/// Compared to wasm_f32_8x1 (2 accumulators, 2-way ILP), this exposes more +/// parallel work to the SIMD pipelines, beneficial on hardware with 3+ +/// SIMD execution units (most modern ARM and x86). +unsafe fn kernel_f32_16x1(mut pnl: *const FusedKerSpec) -> isize { + use std::arch::wasm32::*; + + unsafe { + // Four accumulators: 16 rows × 1 col packed as [ab_q0, ab_q1, ab_q2, ab_q3] + // ab_q0 = rows 0-3, ab_q1 = rows 4-7, ab_q2 = rows 8-11, ab_q3 = rows 12-15 + let mut ab_q0 = f32x4_splat(0.0); + let mut ab_q1 = f32x4_splat(0.0); + let mut ab_q2 = f32x4_splat(0.0); + let mut ab_q3 = f32x4_splat(0.0); + + while !pnl.is_null() { + match *pnl { + FusedKerSpec::Done => break, + FusedKerSpec::Clear => { + let z = f32x4_splat(0.0); + ab_q0 = z; + ab_q1 = z; + ab_q2 = z; + ab_q3 = z; + } + FusedKerSpec::LoadTile(_cols, rows) => { + let p = rows as *const v128; + ab_q0 = *p; + ab_q1 = *p.add(1); + ab_q2 = *p.add(2); + ab_q3 = *p.add(3); + } + FusedKerSpec::ScalarMin(a) => { + let s = f32x4_splat(a); + ab_q0 = f32x4_min(s, ab_q0); + ab_q1 = f32x4_min(s, ab_q1); + ab_q2 = f32x4_min(s, ab_q2); + ab_q3 = f32x4_min(s, ab_q3); + } + FusedKerSpec::ScalarMax(a) => { + let s = f32x4_splat(a); + ab_q0 = f32x4_max(s, ab_q0); + ab_q1 = f32x4_max(s, ab_q1); + ab_q2 = f32x4_max(s, ab_q2); + ab_q3 = f32x4_max(s, ab_q3); + } + FusedKerSpec::ScalarAdd(a) => { + let s = f32x4_splat(a); + ab_q0 = f32x4_add(s, ab_q0); + ab_q1 = f32x4_add(s, ab_q1); + ab_q2 = f32x4_add(s, ab_q2); + ab_q3 = f32x4_add(s, ab_q3); + } + FusedKerSpec::ScalarMul(a) => { + let s = f32x4_splat(a); + ab_q0 = f32x4_mul(s, ab_q0); + ab_q1 = f32x4_mul(s, ab_q1); + ab_q2 = f32x4_mul(s, ab_q2); + ab_q3 = f32x4_mul(s, ab_q3); + } + FusedKerSpec::ScalarSub(a) => { + let s = f32x4_splat(a); + ab_q0 = f32x4_sub(s, ab_q0); + ab_q1 = f32x4_sub(s, ab_q1); + ab_q2 = f32x4_sub(s, ab_q2); + ab_q3 = f32x4_sub(s, ab_q3); + } + FusedKerSpec::ScalarSubF(a) => { + let s = f32x4_splat(a); + ab_q0 = f32x4_sub(ab_q0, s); + ab_q1 = f32x4_sub(ab_q1, s); + ab_q2 = f32x4_sub(ab_q2, s); + ab_q3 = f32x4_sub(ab_q3, s); + } + FusedKerSpec::LeakyRelu(a) => { + let s = f32x4_splat(a); + let zero = f32x4_splat(0.0); + let m0 = f32x4_gt(ab_q0, zero); + ab_q0 = v128_bitselect(ab_q0, f32x4_mul(s, ab_q0), m0); + let m1 = f32x4_gt(ab_q1, zero); + ab_q1 = v128_bitselect(ab_q1, f32x4_mul(s, ab_q1), m1); + let m2 = f32x4_gt(ab_q2, zero); + ab_q2 = v128_bitselect(ab_q2, f32x4_mul(s, ab_q2), m2); + let m3 = f32x4_gt(ab_q3, zero); + ab_q3 = v128_bitselect(ab_q3, f32x4_mul(s, ab_q3), m3); + } + FusedKerSpec::PerRowMin(row) => { + let p = row as *const v128; + ab_q0 = f32x4_min(v128_load(p), ab_q0); + ab_q1 = f32x4_min(v128_load(p.add(1)), ab_q1); + ab_q2 = f32x4_min(v128_load(p.add(2)), ab_q2); + ab_q3 = f32x4_min(v128_load(p.add(3)), ab_q3); + } + FusedKerSpec::PerRowMax(row) => { + let p = row as *const v128; + ab_q0 = f32x4_max(v128_load(p), ab_q0); + ab_q1 = f32x4_max(v128_load(p.add(1)), ab_q1); + ab_q2 = f32x4_max(v128_load(p.add(2)), ab_q2); + ab_q3 = f32x4_max(v128_load(p.add(3)), ab_q3); + } + FusedKerSpec::PerRowAdd(row) => { + let p = row as *const v128; + ab_q0 = f32x4_add(v128_load(p), ab_q0); + ab_q1 = f32x4_add(v128_load(p.add(1)), ab_q1); + ab_q2 = f32x4_add(v128_load(p.add(2)), ab_q2); + ab_q3 = f32x4_add(v128_load(p.add(3)), ab_q3); + } + FusedKerSpec::PerRowMul(row) => { + let p = row as *const v128; + ab_q0 = f32x4_mul(v128_load(p), ab_q0); + ab_q1 = f32x4_mul(v128_load(p.add(1)), ab_q1); + ab_q2 = f32x4_mul(v128_load(p.add(2)), ab_q2); + ab_q3 = f32x4_mul(v128_load(p.add(3)), ab_q3); + } + FusedKerSpec::PerRowSub(row) => { + let p = row as *const v128; + ab_q0 = f32x4_sub(v128_load(p), ab_q0); + ab_q1 = f32x4_sub(v128_load(p.add(1)), ab_q1); + ab_q2 = f32x4_sub(v128_load(p.add(2)), ab_q2); + ab_q3 = f32x4_sub(v128_load(p.add(3)), ab_q3); + } + FusedKerSpec::PerRowSubF(row) => { + let p = row as *const v128; + ab_q0 = f32x4_sub(ab_q0, v128_load(p)); + ab_q1 = f32x4_sub(ab_q1, v128_load(p.add(1))); + ab_q2 = f32x4_sub(ab_q2, v128_load(p.add(2))); + ab_q3 = f32x4_sub(ab_q3, v128_load(p.add(3))); + } + FusedKerSpec::PerColMin(cols) => { + let c = f32x4_splat(*cols); + ab_q0 = f32x4_min(c, ab_q0); + ab_q1 = f32x4_min(c, ab_q1); + ab_q2 = f32x4_min(c, ab_q2); + ab_q3 = f32x4_min(c, ab_q3); + } + FusedKerSpec::PerColMax(cols) => { + let c = f32x4_splat(*cols); + ab_q0 = f32x4_max(c, ab_q0); + ab_q1 = f32x4_max(c, ab_q1); + ab_q2 = f32x4_max(c, ab_q2); + ab_q3 = f32x4_max(c, ab_q3); + } + FusedKerSpec::PerColAdd(cols) => { + let c = f32x4_splat(*cols); + ab_q0 = f32x4_add(c, ab_q0); + ab_q1 = f32x4_add(c, ab_q1); + ab_q2 = f32x4_add(c, ab_q2); + ab_q3 = f32x4_add(c, ab_q3); + } + FusedKerSpec::PerColMul(cols) => { + let c = f32x4_splat(*cols); + ab_q0 = f32x4_mul(c, ab_q0); + ab_q1 = f32x4_mul(c, ab_q1); + ab_q2 = f32x4_mul(c, ab_q2); + ab_q3 = f32x4_mul(c, ab_q3); + } + FusedKerSpec::PerColSub(cols) => { + let c = f32x4_splat(*cols); + ab_q0 = f32x4_sub(c, ab_q0); + ab_q1 = f32x4_sub(c, ab_q1); + ab_q2 = f32x4_sub(c, ab_q2); + ab_q3 = f32x4_sub(c, ab_q3); + } + FusedKerSpec::PerColSubF(cols) => { + let c = f32x4_splat(*cols); + ab_q0 = f32x4_sub(ab_q0, c); + ab_q1 = f32x4_sub(ab_q1, c); + ab_q2 = f32x4_sub(ab_q2, c); + ab_q3 = f32x4_sub(ab_q3, c); + } + FusedKerSpec::QScale(shift, rp, mult) => { + let scaler = Scaler::from_fuse_params(shift, rp, mult); + let s = f32x4_splat(scaler.scale); + ab_q0 = f32x4_mul(s, ab_q0); + ab_q1 = f32x4_mul(s, ab_q1); + ab_q2 = f32x4_mul(s, ab_q2); + ab_q3 = f32x4_mul(s, ab_q3); + } + FusedKerSpec::RoundingShiftRight(shift, _rp) => { + let s = f32x4_splat(2f32.powi(-(shift as i32))); + ab_q0 = f32x4_mul(s, ab_q0); + ab_q1 = f32x4_mul(s, ab_q1); + ab_q2 = f32x4_mul(s, ab_q2); + ab_q3 = f32x4_mul(s, ab_q3); + } + FusedKerSpec::ShiftLeft(shift) => { + let s = f32x4_splat(2f32.powi(shift as i32)); + ab_q0 = f32x4_mul(s, ab_q0); + ab_q1 = f32x4_mul(s, ab_q1); + ab_q2 = f32x4_mul(s, ab_q2); + ab_q3 = f32x4_mul(s, ab_q3); + } + FusedKerSpec::AddUnicast(tile) => { + // 16 rows × 1 col, with row_byte_stride between rows + let mut ptr: *const u8 = tile.ptr; + let mut ms = [0f32; 16]; + for i in 0..16 { + ms[i] = *(ptr as *const f32); + ptr = ptr.add(tile.row_byte_stride as usize); + } + ab_q0 = f32x4_add(ab_q0, f32x4(ms[0], ms[1], ms[2], ms[3])); + ab_q1 = f32x4_add(ab_q1, f32x4(ms[4], ms[5], ms[6], ms[7])); + ab_q2 = f32x4_add(ab_q2, f32x4(ms[8], ms[9], ms[10], ms[11])); + ab_q3 = f32x4_add(ab_q3, f32x4(ms[12], ms[13], ms[14], ms[15])); + } + FusedKerSpec::AddRowColProducts(rows, cols) => { + let p = rows as *const v128; + let c = f32x4_splat(*cols); + ab_q0 = madd_f32x4_nofma!(ab_q0, v128_load(p), c); + ab_q1 = madd_f32x4_nofma!(ab_q1, v128_load(p.add(1)), c); + ab_q2 = madd_f32x4_nofma!(ab_q2, v128_load(p.add(2)), c); + ab_q3 = madd_f32x4_nofma!(ab_q3, v128_load(p.add(3)), c); + } + FusedKerSpec::Store(tile) => { + // 16 rows × 1 col, write each lane to a separate row + let mut ptr: *mut u8 = tile.ptr; + for ab in [ab_q0, ab_q1, ab_q2, ab_q3].iter() { + *(ptr as *mut f32) = f32x4_extract_lane::<0>(*ab); + ptr = ptr.add(tile.row_byte_stride as usize); + *(ptr as *mut f32) = f32x4_extract_lane::<1>(*ab); + ptr = ptr.add(tile.row_byte_stride as usize); + *(ptr as *mut f32) = f32x4_extract_lane::<2>(*ab); + ptr = ptr.add(tile.row_byte_stride as usize); + *(ptr as *mut f32) = f32x4_extract_lane::<3>(*ab); + ptr = ptr.add(tile.row_byte_stride as usize); + } + } + FusedKerSpec::AddMatMul { + k, + pa, + pb, + packing: _, + } => { + // A: packed [k][MR=16] = each k iter loads 16 f32 = 4 v128 + // B: packed [k][NR=1] = each k iter loads 1 scalar f32, broadcast + // 4 INDEPENDENT fmadds per k-iter — 4-way ILP + let a = pa as *const v128; + let b = pb as *const f32; + for i in 0..k { + let a0 = v128_load(a.offset((4 * i) as isize)); + let a1 = v128_load(a.offset((4 * i + 1) as isize)); + let a2 = v128_load(a.offset((4 * i + 2) as isize)); + let a3 = v128_load(a.offset((4 * i + 3) as isize)); + let bs = f32x4_splat(*b.offset(i as isize)); + ab_q0 = madd_f32x4_nofma!(ab_q0, a0, bs); + ab_q1 = madd_f32x4_nofma!(ab_q1, a1, bs); + ab_q2 = madd_f32x4_nofma!(ab_q2, a2, bs); + ab_q3 = madd_f32x4_nofma!(ab_q3, a3, bs); + } + } + } + pnl = pnl.add(1); + } + 0 + } +} + +MMMRustKernel!(kernel_f32_16x1 => wasm_f32_16x1(16,1)@(16,1) quality(ImplementationQuality::ManuallyOptimized)); + +/// WASM SIMD f32 32x1 kernel — widest GEMV variant for matrix-vector products +/// on very large M. Uses EIGHT independent f32x4 accumulators (rows 0-3, 4-7, +/// 8-11, 12-15, 16-19, 20-23, 24-27, 28-31), enabling 8-way ILP within each +/// k-iteration. +/// +/// Compared to wasm_f32_16x1 (4 accumulators, 4-way ILP), this halves the +/// per-call dispatch overhead for M=256 GRU gates (8 calls instead of 16), +/// and exposes 8 independent fmadd dependency chains. On hosts with 16+ +/// physical SIMD registers (x86_64 has 16 xmm, ARM64 has 32 NEON), the 8 +/// accumulators fit without spilling. Mirrors `apple_amx_mmm_f32_32x1` MR. +/// +/// Selection: `kernel_selection::strategize()` prefers max mr() for n=1 +/// cases, so this kernel automatically wins over wasm_f32_16x1 for M >= 32. +unsafe fn kernel_f32_32x1(mut pnl: *const FusedKerSpec) -> isize { + use std::arch::wasm32::*; + + unsafe { + // Eight accumulators: 32 rows × 1 col packed as [ab_q0..ab_q7] + // ab_q0 = rows 0-3, ab_q1 = rows 4-7, ..., ab_q7 = rows 28-31 + let mut ab_q0 = f32x4_splat(0.0); + let mut ab_q1 = f32x4_splat(0.0); + let mut ab_q2 = f32x4_splat(0.0); + let mut ab_q3 = f32x4_splat(0.0); + let mut ab_q4 = f32x4_splat(0.0); + let mut ab_q5 = f32x4_splat(0.0); + let mut ab_q6 = f32x4_splat(0.0); + let mut ab_q7 = f32x4_splat(0.0); + + while !pnl.is_null() { + match *pnl { + FusedKerSpec::Done => break, + FusedKerSpec::Clear => { + let z = f32x4_splat(0.0); + ab_q0 = z; + ab_q1 = z; + ab_q2 = z; + ab_q3 = z; + ab_q4 = z; + ab_q5 = z; + ab_q6 = z; + ab_q7 = z; + } + FusedKerSpec::LoadTile(_cols, rows) => { + let p = rows as *const v128; + ab_q0 = *p; + ab_q1 = *p.add(1); + ab_q2 = *p.add(2); + ab_q3 = *p.add(3); + ab_q4 = *p.add(4); + ab_q5 = *p.add(5); + ab_q6 = *p.add(6); + ab_q7 = *p.add(7); + } + FusedKerSpec::ScalarMin(a) => { + let s = f32x4_splat(a); + ab_q0 = f32x4_min(s, ab_q0); + ab_q1 = f32x4_min(s, ab_q1); + ab_q2 = f32x4_min(s, ab_q2); + ab_q3 = f32x4_min(s, ab_q3); + ab_q4 = f32x4_min(s, ab_q4); + ab_q5 = f32x4_min(s, ab_q5); + ab_q6 = f32x4_min(s, ab_q6); + ab_q7 = f32x4_min(s, ab_q7); + } + FusedKerSpec::ScalarMax(a) => { + let s = f32x4_splat(a); + ab_q0 = f32x4_max(s, ab_q0); + ab_q1 = f32x4_max(s, ab_q1); + ab_q2 = f32x4_max(s, ab_q2); + ab_q3 = f32x4_max(s, ab_q3); + ab_q4 = f32x4_max(s, ab_q4); + ab_q5 = f32x4_max(s, ab_q5); + ab_q6 = f32x4_max(s, ab_q6); + ab_q7 = f32x4_max(s, ab_q7); + } + FusedKerSpec::ScalarAdd(a) => { + let s = f32x4_splat(a); + ab_q0 = f32x4_add(s, ab_q0); + ab_q1 = f32x4_add(s, ab_q1); + ab_q2 = f32x4_add(s, ab_q2); + ab_q3 = f32x4_add(s, ab_q3); + ab_q4 = f32x4_add(s, ab_q4); + ab_q5 = f32x4_add(s, ab_q5); + ab_q6 = f32x4_add(s, ab_q6); + ab_q7 = f32x4_add(s, ab_q7); + } + FusedKerSpec::ScalarMul(a) => { + let s = f32x4_splat(a); + ab_q0 = f32x4_mul(s, ab_q0); + ab_q1 = f32x4_mul(s, ab_q1); + ab_q2 = f32x4_mul(s, ab_q2); + ab_q3 = f32x4_mul(s, ab_q3); + ab_q4 = f32x4_mul(s, ab_q4); + ab_q5 = f32x4_mul(s, ab_q5); + ab_q6 = f32x4_mul(s, ab_q6); + ab_q7 = f32x4_mul(s, ab_q7); + } + FusedKerSpec::ScalarSub(a) => { + let s = f32x4_splat(a); + ab_q0 = f32x4_sub(s, ab_q0); + ab_q1 = f32x4_sub(s, ab_q1); + ab_q2 = f32x4_sub(s, ab_q2); + ab_q3 = f32x4_sub(s, ab_q3); + ab_q4 = f32x4_sub(s, ab_q4); + ab_q5 = f32x4_sub(s, ab_q5); + ab_q6 = f32x4_sub(s, ab_q6); + ab_q7 = f32x4_sub(s, ab_q7); + } + FusedKerSpec::ScalarSubF(a) => { + let s = f32x4_splat(a); + ab_q0 = f32x4_sub(ab_q0, s); + ab_q1 = f32x4_sub(ab_q1, s); + ab_q2 = f32x4_sub(ab_q2, s); + ab_q3 = f32x4_sub(ab_q3, s); + ab_q4 = f32x4_sub(ab_q4, s); + ab_q5 = f32x4_sub(ab_q5, s); + ab_q6 = f32x4_sub(ab_q6, s); + ab_q7 = f32x4_sub(ab_q7, s); + } + FusedKerSpec::LeakyRelu(a) => { + let s = f32x4_splat(a); + let zero = f32x4_splat(0.0); + let m0 = f32x4_gt(ab_q0, zero); + ab_q0 = v128_bitselect(ab_q0, f32x4_mul(s, ab_q0), m0); + let m1 = f32x4_gt(ab_q1, zero); + ab_q1 = v128_bitselect(ab_q1, f32x4_mul(s, ab_q1), m1); + let m2 = f32x4_gt(ab_q2, zero); + ab_q2 = v128_bitselect(ab_q2, f32x4_mul(s, ab_q2), m2); + let m3 = f32x4_gt(ab_q3, zero); + ab_q3 = v128_bitselect(ab_q3, f32x4_mul(s, ab_q3), m3); + let m4 = f32x4_gt(ab_q4, zero); + ab_q4 = v128_bitselect(ab_q4, f32x4_mul(s, ab_q4), m4); + let m5 = f32x4_gt(ab_q5, zero); + ab_q5 = v128_bitselect(ab_q5, f32x4_mul(s, ab_q5), m5); + let m6 = f32x4_gt(ab_q6, zero); + ab_q6 = v128_bitselect(ab_q6, f32x4_mul(s, ab_q6), m6); + let m7 = f32x4_gt(ab_q7, zero); + ab_q7 = v128_bitselect(ab_q7, f32x4_mul(s, ab_q7), m7); + } + FusedKerSpec::PerRowMin(row) => { + let p = row as *const v128; + ab_q0 = f32x4_min(v128_load(p), ab_q0); + ab_q1 = f32x4_min(v128_load(p.add(1)), ab_q1); + ab_q2 = f32x4_min(v128_load(p.add(2)), ab_q2); + ab_q3 = f32x4_min(v128_load(p.add(3)), ab_q3); + ab_q4 = f32x4_min(v128_load(p.add(4)), ab_q4); + ab_q5 = f32x4_min(v128_load(p.add(5)), ab_q5); + ab_q6 = f32x4_min(v128_load(p.add(6)), ab_q6); + ab_q7 = f32x4_min(v128_load(p.add(7)), ab_q7); + } + FusedKerSpec::PerRowMax(row) => { + let p = row as *const v128; + ab_q0 = f32x4_max(v128_load(p), ab_q0); + ab_q1 = f32x4_max(v128_load(p.add(1)), ab_q1); + ab_q2 = f32x4_max(v128_load(p.add(2)), ab_q2); + ab_q3 = f32x4_max(v128_load(p.add(3)), ab_q3); + ab_q4 = f32x4_max(v128_load(p.add(4)), ab_q4); + ab_q5 = f32x4_max(v128_load(p.add(5)), ab_q5); + ab_q6 = f32x4_max(v128_load(p.add(6)), ab_q6); + ab_q7 = f32x4_max(v128_load(p.add(7)), ab_q7); + } + FusedKerSpec::PerRowAdd(row) => { + let p = row as *const v128; + ab_q0 = f32x4_add(v128_load(p), ab_q0); + ab_q1 = f32x4_add(v128_load(p.add(1)), ab_q1); + ab_q2 = f32x4_add(v128_load(p.add(2)), ab_q2); + ab_q3 = f32x4_add(v128_load(p.add(3)), ab_q3); + ab_q4 = f32x4_add(v128_load(p.add(4)), ab_q4); + ab_q5 = f32x4_add(v128_load(p.add(5)), ab_q5); + ab_q6 = f32x4_add(v128_load(p.add(6)), ab_q6); + ab_q7 = f32x4_add(v128_load(p.add(7)), ab_q7); + } + FusedKerSpec::PerRowMul(row) => { + let p = row as *const v128; + ab_q0 = f32x4_mul(v128_load(p), ab_q0); + ab_q1 = f32x4_mul(v128_load(p.add(1)), ab_q1); + ab_q2 = f32x4_mul(v128_load(p.add(2)), ab_q2); + ab_q3 = f32x4_mul(v128_load(p.add(3)), ab_q3); + ab_q4 = f32x4_mul(v128_load(p.add(4)), ab_q4); + ab_q5 = f32x4_mul(v128_load(p.add(5)), ab_q5); + ab_q6 = f32x4_mul(v128_load(p.add(6)), ab_q6); + ab_q7 = f32x4_mul(v128_load(p.add(7)), ab_q7); + } + FusedKerSpec::PerRowSub(row) => { + let p = row as *const v128; + ab_q0 = f32x4_sub(v128_load(p), ab_q0); + ab_q1 = f32x4_sub(v128_load(p.add(1)), ab_q1); + ab_q2 = f32x4_sub(v128_load(p.add(2)), ab_q2); + ab_q3 = f32x4_sub(v128_load(p.add(3)), ab_q3); + ab_q4 = f32x4_sub(v128_load(p.add(4)), ab_q4); + ab_q5 = f32x4_sub(v128_load(p.add(5)), ab_q5); + ab_q6 = f32x4_sub(v128_load(p.add(6)), ab_q6); + ab_q7 = f32x4_sub(v128_load(p.add(7)), ab_q7); + } + FusedKerSpec::PerRowSubF(row) => { + let p = row as *const v128; + ab_q0 = f32x4_sub(ab_q0, v128_load(p)); + ab_q1 = f32x4_sub(ab_q1, v128_load(p.add(1))); + ab_q2 = f32x4_sub(ab_q2, v128_load(p.add(2))); + ab_q3 = f32x4_sub(ab_q3, v128_load(p.add(3))); + ab_q4 = f32x4_sub(ab_q4, v128_load(p.add(4))); + ab_q5 = f32x4_sub(ab_q5, v128_load(p.add(5))); + ab_q6 = f32x4_sub(ab_q6, v128_load(p.add(6))); + ab_q7 = f32x4_sub(ab_q7, v128_load(p.add(7))); + } + FusedKerSpec::PerColMin(cols) => { + let c = f32x4_splat(*cols); + ab_q0 = f32x4_min(c, ab_q0); + ab_q1 = f32x4_min(c, ab_q1); + ab_q2 = f32x4_min(c, ab_q2); + ab_q3 = f32x4_min(c, ab_q3); + ab_q4 = f32x4_min(c, ab_q4); + ab_q5 = f32x4_min(c, ab_q5); + ab_q6 = f32x4_min(c, ab_q6); + ab_q7 = f32x4_min(c, ab_q7); + } + FusedKerSpec::PerColMax(cols) => { + let c = f32x4_splat(*cols); + ab_q0 = f32x4_max(c, ab_q0); + ab_q1 = f32x4_max(c, ab_q1); + ab_q2 = f32x4_max(c, ab_q2); + ab_q3 = f32x4_max(c, ab_q3); + ab_q4 = f32x4_max(c, ab_q4); + ab_q5 = f32x4_max(c, ab_q5); + ab_q6 = f32x4_max(c, ab_q6); + ab_q7 = f32x4_max(c, ab_q7); + } + FusedKerSpec::PerColAdd(cols) => { + let c = f32x4_splat(*cols); + ab_q0 = f32x4_add(c, ab_q0); + ab_q1 = f32x4_add(c, ab_q1); + ab_q2 = f32x4_add(c, ab_q2); + ab_q3 = f32x4_add(c, ab_q3); + ab_q4 = f32x4_add(c, ab_q4); + ab_q5 = f32x4_add(c, ab_q5); + ab_q6 = f32x4_add(c, ab_q6); + ab_q7 = f32x4_add(c, ab_q7); + } + FusedKerSpec::PerColMul(cols) => { + let c = f32x4_splat(*cols); + ab_q0 = f32x4_mul(c, ab_q0); + ab_q1 = f32x4_mul(c, ab_q1); + ab_q2 = f32x4_mul(c, ab_q2); + ab_q3 = f32x4_mul(c, ab_q3); + ab_q4 = f32x4_mul(c, ab_q4); + ab_q5 = f32x4_mul(c, ab_q5); + ab_q6 = f32x4_mul(c, ab_q6); + ab_q7 = f32x4_mul(c, ab_q7); + } + FusedKerSpec::PerColSub(cols) => { + let c = f32x4_splat(*cols); + ab_q0 = f32x4_sub(c, ab_q0); + ab_q1 = f32x4_sub(c, ab_q1); + ab_q2 = f32x4_sub(c, ab_q2); + ab_q3 = f32x4_sub(c, ab_q3); + ab_q4 = f32x4_sub(c, ab_q4); + ab_q5 = f32x4_sub(c, ab_q5); + ab_q6 = f32x4_sub(c, ab_q6); + ab_q7 = f32x4_sub(c, ab_q7); + } + FusedKerSpec::PerColSubF(cols) => { + let c = f32x4_splat(*cols); + ab_q0 = f32x4_sub(ab_q0, c); + ab_q1 = f32x4_sub(ab_q1, c); + ab_q2 = f32x4_sub(ab_q2, c); + ab_q3 = f32x4_sub(ab_q3, c); + ab_q4 = f32x4_sub(ab_q4, c); + ab_q5 = f32x4_sub(ab_q5, c); + ab_q6 = f32x4_sub(ab_q6, c); + ab_q7 = f32x4_sub(ab_q7, c); + } + FusedKerSpec::QScale(shift, rp, mult) => { + let scaler = Scaler::from_fuse_params(shift, rp, mult); + let s = f32x4_splat(scaler.scale); + ab_q0 = f32x4_mul(s, ab_q0); + ab_q1 = f32x4_mul(s, ab_q1); + ab_q2 = f32x4_mul(s, ab_q2); + ab_q3 = f32x4_mul(s, ab_q3); + ab_q4 = f32x4_mul(s, ab_q4); + ab_q5 = f32x4_mul(s, ab_q5); + ab_q6 = f32x4_mul(s, ab_q6); + ab_q7 = f32x4_mul(s, ab_q7); + } + FusedKerSpec::RoundingShiftRight(shift, _rp) => { + let s = f32x4_splat(2f32.powi(-(shift as i32))); + ab_q0 = f32x4_mul(s, ab_q0); + ab_q1 = f32x4_mul(s, ab_q1); + ab_q2 = f32x4_mul(s, ab_q2); + ab_q3 = f32x4_mul(s, ab_q3); + ab_q4 = f32x4_mul(s, ab_q4); + ab_q5 = f32x4_mul(s, ab_q5); + ab_q6 = f32x4_mul(s, ab_q6); + ab_q7 = f32x4_mul(s, ab_q7); + } + FusedKerSpec::ShiftLeft(shift) => { + let s = f32x4_splat(2f32.powi(shift as i32)); + ab_q0 = f32x4_mul(s, ab_q0); + ab_q1 = f32x4_mul(s, ab_q1); + ab_q2 = f32x4_mul(s, ab_q2); + ab_q3 = f32x4_mul(s, ab_q3); + ab_q4 = f32x4_mul(s, ab_q4); + ab_q5 = f32x4_mul(s, ab_q5); + ab_q6 = f32x4_mul(s, ab_q6); + ab_q7 = f32x4_mul(s, ab_q7); + } + FusedKerSpec::AddUnicast(tile) => { + // 32 rows × 1 col, with row_byte_stride between rows + let mut ptr: *const u8 = tile.ptr; + let mut ms = [0f32; 32]; + for i in 0..32 { + ms[i] = *(ptr as *const f32); + ptr = ptr.add(tile.row_byte_stride as usize); + } + ab_q0 = f32x4_add(ab_q0, f32x4(ms[0], ms[1], ms[2], ms[3])); + ab_q1 = f32x4_add(ab_q1, f32x4(ms[4], ms[5], ms[6], ms[7])); + ab_q2 = f32x4_add(ab_q2, f32x4(ms[8], ms[9], ms[10], ms[11])); + ab_q3 = f32x4_add(ab_q3, f32x4(ms[12], ms[13], ms[14], ms[15])); + ab_q4 = f32x4_add(ab_q4, f32x4(ms[16], ms[17], ms[18], ms[19])); + ab_q5 = f32x4_add(ab_q5, f32x4(ms[20], ms[21], ms[22], ms[23])); + ab_q6 = f32x4_add(ab_q6, f32x4(ms[24], ms[25], ms[26], ms[27])); + ab_q7 = f32x4_add(ab_q7, f32x4(ms[28], ms[29], ms[30], ms[31])); + } + FusedKerSpec::AddRowColProducts(rows, cols) => { + let p = rows as *const v128; + let c = f32x4_splat(*cols); + ab_q0 = madd_f32x4!(ab_q0, v128_load(p), c); + ab_q1 = madd_f32x4!(ab_q1, v128_load(p.add(1)), c); + ab_q2 = madd_f32x4!(ab_q2, v128_load(p.add(2)), c); + ab_q3 = madd_f32x4!(ab_q3, v128_load(p.add(3)), c); + ab_q4 = madd_f32x4!(ab_q4, v128_load(p.add(4)), c); + ab_q5 = madd_f32x4!(ab_q5, v128_load(p.add(5)), c); + ab_q6 = madd_f32x4!(ab_q6, v128_load(p.add(6)), c); + ab_q7 = madd_f32x4!(ab_q7, v128_load(p.add(7)), c); + } + FusedKerSpec::Store(tile) => { + // 32 rows × 1 col, write each lane to a separate row + let mut ptr: *mut u8 = tile.ptr; + for ab in [ab_q0, ab_q1, ab_q2, ab_q3, ab_q4, ab_q5, ab_q6, ab_q7].iter() { + *(ptr as *mut f32) = f32x4_extract_lane::<0>(*ab); + ptr = ptr.add(tile.row_byte_stride as usize); + *(ptr as *mut f32) = f32x4_extract_lane::<1>(*ab); + ptr = ptr.add(tile.row_byte_stride as usize); + *(ptr as *mut f32) = f32x4_extract_lane::<2>(*ab); + ptr = ptr.add(tile.row_byte_stride as usize); + *(ptr as *mut f32) = f32x4_extract_lane::<3>(*ab); + ptr = ptr.add(tile.row_byte_stride as usize); + } + } + FusedKerSpec::AddMatMul { + k, + pa, + pb, + packing: _, + } => { + // A: packed [k][MR=32] = each k iter loads 32 f32 = 8 v128 + // B: packed [k][NR=1] = each k iter loads 1 scalar f32, broadcast + // 8 INDEPENDENT fmadds per k-iter — 8-way ILP + let a = pa as *const v128; + let b = pb as *const f32; + for i in 0..k { + let a0 = v128_load(a.offset((8 * i) as isize)); + let a1 = v128_load(a.offset((8 * i + 1) as isize)); + let a2 = v128_load(a.offset((8 * i + 2) as isize)); + let a3 = v128_load(a.offset((8 * i + 3) as isize)); + let a4 = v128_load(a.offset((8 * i + 4) as isize)); + let a5 = v128_load(a.offset((8 * i + 5) as isize)); + let a6 = v128_load(a.offset((8 * i + 6) as isize)); + let a7 = v128_load(a.offset((8 * i + 7) as isize)); + let bs = f32x4_splat(*b.offset(i as isize)); + ab_q0 = madd_f32x4!(ab_q0, a0, bs); + ab_q1 = madd_f32x4!(ab_q1, a1, bs); + ab_q2 = madd_f32x4!(ab_q2, a2, bs); + ab_q3 = madd_f32x4!(ab_q3, a3, bs); + ab_q4 = madd_f32x4!(ab_q4, a4, bs); + ab_q5 = madd_f32x4!(ab_q5, a5, bs); + ab_q6 = madd_f32x4!(ab_q6, a6, bs); + ab_q7 = madd_f32x4!(ab_q7, a7, bs); + } + } + } + pnl = pnl.add(1); + } + 0 + } +} + +MMMRustKernel!(kernel_f32_32x1 => wasm_f32_32x1(32,1)@(32,1) quality(ImplementationQuality::ManuallyOptimized)); + +/// WASM SIMD f32 8x8 kernel — wide MM tile (8 rows × 8 cols, 16 v128 accumulators). +/// Each row uses 2 v128: cols 0-3 in `_lo`, cols 4-7 in `_hi`. 16 accumulators +/// is at the limit of WASM's 16 logical SIMD register slots; this tests the +/// register-pressure boundary. For DFN3 ops, all M and N are multiples of 8, +/// so 8x8 fits cleanly with no padding waste. +unsafe fn kernel_f32_8x8(mut pnl: *const FusedKerSpec) -> isize { + use std::arch::wasm32::*; + + unsafe { + // 8 rows × 8 cols = 16 f32x4 accumulators (cols 0-3 in _lo, cols 4-7 in _hi) + let mut a0lo = f32x4_splat(0.0); + let mut a0hi = f32x4_splat(0.0); + let mut a1lo = f32x4_splat(0.0); + let mut a1hi = f32x4_splat(0.0); + let mut a2lo = f32x4_splat(0.0); + let mut a2hi = f32x4_splat(0.0); + let mut a3lo = f32x4_splat(0.0); + let mut a3hi = f32x4_splat(0.0); + let mut a4lo = f32x4_splat(0.0); + let mut a4hi = f32x4_splat(0.0); + let mut a5lo = f32x4_splat(0.0); + let mut a5hi = f32x4_splat(0.0); + let mut a6lo = f32x4_splat(0.0); + let mut a6hi = f32x4_splat(0.0); + let mut a7lo = f32x4_splat(0.0); + let mut a7hi = f32x4_splat(0.0); + + while !pnl.is_null() { + match *pnl { + FusedKerSpec::Done => break, + FusedKerSpec::Clear => { + let z = f32x4_splat(0.0); + a0lo = z; + a0hi = z; + a1lo = z; + a1hi = z; + a2lo = z; + a2hi = z; + a3lo = z; + a3hi = z; + a4lo = z; + a4hi = z; + a5lo = z; + a5hi = z; + a6lo = z; + a6hi = z; + a7lo = z; + a7hi = z; + } + FusedKerSpec::LoadTile(_cols, rows) => { + // 8 rows × 8 cols = 16 v128 (2 per row, contiguous lo+hi) + let p = rows as *const v128; + a0lo = *p.add(0); + a0hi = *p.add(1); + a1lo = *p.add(2); + a1hi = *p.add(3); + a2lo = *p.add(4); + a2hi = *p.add(5); + a3lo = *p.add(6); + a3hi = *p.add(7); + a4lo = *p.add(8); + a4hi = *p.add(9); + a5lo = *p.add(10); + a5hi = *p.add(11); + a6lo = *p.add(12); + a6hi = *p.add(13); + a7lo = *p.add(14); + a7hi = *p.add(15); + } + FusedKerSpec::ScalarMin(a) => { + let s = f32x4_splat(a); + a0lo = f32x4_min(s, a0lo); + a0hi = f32x4_min(s, a0hi); + a1lo = f32x4_min(s, a1lo); + a1hi = f32x4_min(s, a1hi); + a2lo = f32x4_min(s, a2lo); + a2hi = f32x4_min(s, a2hi); + a3lo = f32x4_min(s, a3lo); + a3hi = f32x4_min(s, a3hi); + a4lo = f32x4_min(s, a4lo); + a4hi = f32x4_min(s, a4hi); + a5lo = f32x4_min(s, a5lo); + a5hi = f32x4_min(s, a5hi); + a6lo = f32x4_min(s, a6lo); + a6hi = f32x4_min(s, a6hi); + a7lo = f32x4_min(s, a7lo); + a7hi = f32x4_min(s, a7hi); + } + FusedKerSpec::ScalarMax(a) => { + let s = f32x4_splat(a); + a0lo = f32x4_max(s, a0lo); + a0hi = f32x4_max(s, a0hi); + a1lo = f32x4_max(s, a1lo); + a1hi = f32x4_max(s, a1hi); + a2lo = f32x4_max(s, a2lo); + a2hi = f32x4_max(s, a2hi); + a3lo = f32x4_max(s, a3lo); + a3hi = f32x4_max(s, a3hi); + a4lo = f32x4_max(s, a4lo); + a4hi = f32x4_max(s, a4hi); + a5lo = f32x4_max(s, a5lo); + a5hi = f32x4_max(s, a5hi); + a6lo = f32x4_max(s, a6lo); + a6hi = f32x4_max(s, a6hi); + a7lo = f32x4_max(s, a7lo); + a7hi = f32x4_max(s, a7hi); + } + FusedKerSpec::ScalarAdd(a) => { + let s = f32x4_splat(a); + a0lo = f32x4_add(s, a0lo); + a0hi = f32x4_add(s, a0hi); + a1lo = f32x4_add(s, a1lo); + a1hi = f32x4_add(s, a1hi); + a2lo = f32x4_add(s, a2lo); + a2hi = f32x4_add(s, a2hi); + a3lo = f32x4_add(s, a3lo); + a3hi = f32x4_add(s, a3hi); + a4lo = f32x4_add(s, a4lo); + a4hi = f32x4_add(s, a4hi); + a5lo = f32x4_add(s, a5lo); + a5hi = f32x4_add(s, a5hi); + a6lo = f32x4_add(s, a6lo); + a6hi = f32x4_add(s, a6hi); + a7lo = f32x4_add(s, a7lo); + a7hi = f32x4_add(s, a7hi); + } + FusedKerSpec::ScalarMul(a) => { + let s = f32x4_splat(a); + a0lo = f32x4_mul(s, a0lo); + a0hi = f32x4_mul(s, a0hi); + a1lo = f32x4_mul(s, a1lo); + a1hi = f32x4_mul(s, a1hi); + a2lo = f32x4_mul(s, a2lo); + a2hi = f32x4_mul(s, a2hi); + a3lo = f32x4_mul(s, a3lo); + a3hi = f32x4_mul(s, a3hi); + a4lo = f32x4_mul(s, a4lo); + a4hi = f32x4_mul(s, a4hi); + a5lo = f32x4_mul(s, a5lo); + a5hi = f32x4_mul(s, a5hi); + a6lo = f32x4_mul(s, a6lo); + a6hi = f32x4_mul(s, a6hi); + a7lo = f32x4_mul(s, a7lo); + a7hi = f32x4_mul(s, a7hi); + } + FusedKerSpec::ScalarSub(a) => { + let s = f32x4_splat(a); + a0lo = f32x4_sub(s, a0lo); + a0hi = f32x4_sub(s, a0hi); + a1lo = f32x4_sub(s, a1lo); + a1hi = f32x4_sub(s, a1hi); + a2lo = f32x4_sub(s, a2lo); + a2hi = f32x4_sub(s, a2hi); + a3lo = f32x4_sub(s, a3lo); + a3hi = f32x4_sub(s, a3hi); + a4lo = f32x4_sub(s, a4lo); + a4hi = f32x4_sub(s, a4hi); + a5lo = f32x4_sub(s, a5lo); + a5hi = f32x4_sub(s, a5hi); + a6lo = f32x4_sub(s, a6lo); + a6hi = f32x4_sub(s, a6hi); + a7lo = f32x4_sub(s, a7lo); + a7hi = f32x4_sub(s, a7hi); + } + FusedKerSpec::ScalarSubF(a) => { + let s = f32x4_splat(a); + a0lo = f32x4_sub(a0lo, s); + a0hi = f32x4_sub(a0hi, s); + a1lo = f32x4_sub(a1lo, s); + a1hi = f32x4_sub(a1hi, s); + a2lo = f32x4_sub(a2lo, s); + a2hi = f32x4_sub(a2hi, s); + a3lo = f32x4_sub(a3lo, s); + a3hi = f32x4_sub(a3hi, s); + a4lo = f32x4_sub(a4lo, s); + a4hi = f32x4_sub(a4hi, s); + a5lo = f32x4_sub(a5lo, s); + a5hi = f32x4_sub(a5hi, s); + a6lo = f32x4_sub(a6lo, s); + a6hi = f32x4_sub(a6hi, s); + a7lo = f32x4_sub(a7lo, s); + a7hi = f32x4_sub(a7hi, s); + } + FusedKerSpec::LeakyRelu(a) => { + let s = f32x4_splat(a); + let zero = f32x4_splat(0.0); + let m0a = f32x4_gt(a0lo, zero); + a0lo = v128_bitselect(a0lo, f32x4_mul(s, a0lo), m0a); + let m0b = f32x4_gt(a0hi, zero); + a0hi = v128_bitselect(a0hi, f32x4_mul(s, a0hi), m0b); + let m1a = f32x4_gt(a1lo, zero); + a1lo = v128_bitselect(a1lo, f32x4_mul(s, a1lo), m1a); + let m1b = f32x4_gt(a1hi, zero); + a1hi = v128_bitselect(a1hi, f32x4_mul(s, a1hi), m1b); + let m2a = f32x4_gt(a2lo, zero); + a2lo = v128_bitselect(a2lo, f32x4_mul(s, a2lo), m2a); + let m2b = f32x4_gt(a2hi, zero); + a2hi = v128_bitselect(a2hi, f32x4_mul(s, a2hi), m2b); + let m3a = f32x4_gt(a3lo, zero); + a3lo = v128_bitselect(a3lo, f32x4_mul(s, a3lo), m3a); + let m3b = f32x4_gt(a3hi, zero); + a3hi = v128_bitselect(a3hi, f32x4_mul(s, a3hi), m3b); + let m4a = f32x4_gt(a4lo, zero); + a4lo = v128_bitselect(a4lo, f32x4_mul(s, a4lo), m4a); + let m4b = f32x4_gt(a4hi, zero); + a4hi = v128_bitselect(a4hi, f32x4_mul(s, a4hi), m4b); + let m5a = f32x4_gt(a5lo, zero); + a5lo = v128_bitselect(a5lo, f32x4_mul(s, a5lo), m5a); + let m5b = f32x4_gt(a5hi, zero); + a5hi = v128_bitselect(a5hi, f32x4_mul(s, a5hi), m5b); + let m6a = f32x4_gt(a6lo, zero); + a6lo = v128_bitselect(a6lo, f32x4_mul(s, a6lo), m6a); + let m6b = f32x4_gt(a6hi, zero); + a6hi = v128_bitselect(a6hi, f32x4_mul(s, a6hi), m6b); + let m7a = f32x4_gt(a7lo, zero); + a7lo = v128_bitselect(a7lo, f32x4_mul(s, a7lo), m7a); + let m7b = f32x4_gt(a7hi, zero); + a7hi = v128_bitselect(a7hi, f32x4_mul(s, a7hi), m7b); + } + FusedKerSpec::PerRowMin(row) => { + let r = std::slice::from_raw_parts(row, 8); + let r0 = f32x4_splat(r[0]); + a0lo = f32x4_min(r0, a0lo); + a0hi = f32x4_min(r0, a0hi); + let r1 = f32x4_splat(r[1]); + a1lo = f32x4_min(r1, a1lo); + a1hi = f32x4_min(r1, a1hi); + let r2 = f32x4_splat(r[2]); + a2lo = f32x4_min(r2, a2lo); + a2hi = f32x4_min(r2, a2hi); + let r3 = f32x4_splat(r[3]); + a3lo = f32x4_min(r3, a3lo); + a3hi = f32x4_min(r3, a3hi); + let r4 = f32x4_splat(r[4]); + a4lo = f32x4_min(r4, a4lo); + a4hi = f32x4_min(r4, a4hi); + let r5 = f32x4_splat(r[5]); + a5lo = f32x4_min(r5, a5lo); + a5hi = f32x4_min(r5, a5hi); + let r6 = f32x4_splat(r[6]); + a6lo = f32x4_min(r6, a6lo); + a6hi = f32x4_min(r6, a6hi); + let r7 = f32x4_splat(r[7]); + a7lo = f32x4_min(r7, a7lo); + a7hi = f32x4_min(r7, a7hi); + } + FusedKerSpec::PerRowMax(row) => { + let r = std::slice::from_raw_parts(row, 8); + let r0 = f32x4_splat(r[0]); + a0lo = f32x4_max(r0, a0lo); + a0hi = f32x4_max(r0, a0hi); + let r1 = f32x4_splat(r[1]); + a1lo = f32x4_max(r1, a1lo); + a1hi = f32x4_max(r1, a1hi); + let r2 = f32x4_splat(r[2]); + a2lo = f32x4_max(r2, a2lo); + a2hi = f32x4_max(r2, a2hi); + let r3 = f32x4_splat(r[3]); + a3lo = f32x4_max(r3, a3lo); + a3hi = f32x4_max(r3, a3hi); + let r4 = f32x4_splat(r[4]); + a4lo = f32x4_max(r4, a4lo); + a4hi = f32x4_max(r4, a4hi); + let r5 = f32x4_splat(r[5]); + a5lo = f32x4_max(r5, a5lo); + a5hi = f32x4_max(r5, a5hi); + let r6 = f32x4_splat(r[6]); + a6lo = f32x4_max(r6, a6lo); + a6hi = f32x4_max(r6, a6hi); + let r7 = f32x4_splat(r[7]); + a7lo = f32x4_max(r7, a7lo); + a7hi = f32x4_max(r7, a7hi); + } + FusedKerSpec::PerRowAdd(row) => { + let r = std::slice::from_raw_parts(row, 8); + let r0 = f32x4_splat(r[0]); + a0lo = f32x4_add(r0, a0lo); + a0hi = f32x4_add(r0, a0hi); + let r1 = f32x4_splat(r[1]); + a1lo = f32x4_add(r1, a1lo); + a1hi = f32x4_add(r1, a1hi); + let r2 = f32x4_splat(r[2]); + a2lo = f32x4_add(r2, a2lo); + a2hi = f32x4_add(r2, a2hi); + let r3 = f32x4_splat(r[3]); + a3lo = f32x4_add(r3, a3lo); + a3hi = f32x4_add(r3, a3hi); + let r4 = f32x4_splat(r[4]); + a4lo = f32x4_add(r4, a4lo); + a4hi = f32x4_add(r4, a4hi); + let r5 = f32x4_splat(r[5]); + a5lo = f32x4_add(r5, a5lo); + a5hi = f32x4_add(r5, a5hi); + let r6 = f32x4_splat(r[6]); + a6lo = f32x4_add(r6, a6lo); + a6hi = f32x4_add(r6, a6hi); + let r7 = f32x4_splat(r[7]); + a7lo = f32x4_add(r7, a7lo); + a7hi = f32x4_add(r7, a7hi); + } + FusedKerSpec::PerRowMul(row) => { + let r = std::slice::from_raw_parts(row, 8); + let r0 = f32x4_splat(r[0]); + a0lo = f32x4_mul(r0, a0lo); + a0hi = f32x4_mul(r0, a0hi); + let r1 = f32x4_splat(r[1]); + a1lo = f32x4_mul(r1, a1lo); + a1hi = f32x4_mul(r1, a1hi); + let r2 = f32x4_splat(r[2]); + a2lo = f32x4_mul(r2, a2lo); + a2hi = f32x4_mul(r2, a2hi); + let r3 = f32x4_splat(r[3]); + a3lo = f32x4_mul(r3, a3lo); + a3hi = f32x4_mul(r3, a3hi); + let r4 = f32x4_splat(r[4]); + a4lo = f32x4_mul(r4, a4lo); + a4hi = f32x4_mul(r4, a4hi); + let r5 = f32x4_splat(r[5]); + a5lo = f32x4_mul(r5, a5lo); + a5hi = f32x4_mul(r5, a5hi); + let r6 = f32x4_splat(r[6]); + a6lo = f32x4_mul(r6, a6lo); + a6hi = f32x4_mul(r6, a6hi); + let r7 = f32x4_splat(r[7]); + a7lo = f32x4_mul(r7, a7lo); + a7hi = f32x4_mul(r7, a7hi); + } + FusedKerSpec::PerRowSub(row) => { + let r = std::slice::from_raw_parts(row, 8); + let r0 = f32x4_splat(r[0]); + a0lo = f32x4_sub(r0, a0lo); + a0hi = f32x4_sub(r0, a0hi); + let r1 = f32x4_splat(r[1]); + a1lo = f32x4_sub(r1, a1lo); + a1hi = f32x4_sub(r1, a1hi); + let r2 = f32x4_splat(r[2]); + a2lo = f32x4_sub(r2, a2lo); + a2hi = f32x4_sub(r2, a2hi); + let r3 = f32x4_splat(r[3]); + a3lo = f32x4_sub(r3, a3lo); + a3hi = f32x4_sub(r3, a3hi); + let r4 = f32x4_splat(r[4]); + a4lo = f32x4_sub(r4, a4lo); + a4hi = f32x4_sub(r4, a4hi); + let r5 = f32x4_splat(r[5]); + a5lo = f32x4_sub(r5, a5lo); + a5hi = f32x4_sub(r5, a5hi); + let r6 = f32x4_splat(r[6]); + a6lo = f32x4_sub(r6, a6lo); + a6hi = f32x4_sub(r6, a6hi); + let r7 = f32x4_splat(r[7]); + a7lo = f32x4_sub(r7, a7lo); + a7hi = f32x4_sub(r7, a7hi); + } + FusedKerSpec::PerRowSubF(row) => { + let r = std::slice::from_raw_parts(row, 8); + let r0 = f32x4_splat(r[0]); + a0lo = f32x4_sub(a0lo, r0); + a0hi = f32x4_sub(a0hi, r0); + let r1 = f32x4_splat(r[1]); + a1lo = f32x4_sub(a1lo, r1); + a1hi = f32x4_sub(a1hi, r1); + let r2 = f32x4_splat(r[2]); + a2lo = f32x4_sub(a2lo, r2); + a2hi = f32x4_sub(a2hi, r2); + let r3 = f32x4_splat(r[3]); + a3lo = f32x4_sub(a3lo, r3); + a3hi = f32x4_sub(a3hi, r3); + let r4 = f32x4_splat(r[4]); + a4lo = f32x4_sub(a4lo, r4); + a4hi = f32x4_sub(a4hi, r4); + let r5 = f32x4_splat(r[5]); + a5lo = f32x4_sub(a5lo, r5); + a5hi = f32x4_sub(a5hi, r5); + let r6 = f32x4_splat(r[6]); + a6lo = f32x4_sub(a6lo, r6); + a6hi = f32x4_sub(a6hi, r6); + let r7 = f32x4_splat(r[7]); + a7lo = f32x4_sub(a7lo, r7); + a7hi = f32x4_sub(a7hi, r7); + } + FusedKerSpec::PerColMin(cols) => { + let p = cols as *const v128; + let clo = v128_load(p); + let chi = v128_load(p.add(1)); + a0lo = f32x4_min(clo, a0lo); + a0hi = f32x4_min(chi, a0hi); + a1lo = f32x4_min(clo, a1lo); + a1hi = f32x4_min(chi, a1hi); + a2lo = f32x4_min(clo, a2lo); + a2hi = f32x4_min(chi, a2hi); + a3lo = f32x4_min(clo, a3lo); + a3hi = f32x4_min(chi, a3hi); + a4lo = f32x4_min(clo, a4lo); + a4hi = f32x4_min(chi, a4hi); + a5lo = f32x4_min(clo, a5lo); + a5hi = f32x4_min(chi, a5hi); + a6lo = f32x4_min(clo, a6lo); + a6hi = f32x4_min(chi, a6hi); + a7lo = f32x4_min(clo, a7lo); + a7hi = f32x4_min(chi, a7hi); + } + FusedKerSpec::PerColMax(cols) => { + let p = cols as *const v128; + let clo = v128_load(p); + let chi = v128_load(p.add(1)); + a0lo = f32x4_max(clo, a0lo); + a0hi = f32x4_max(chi, a0hi); + a1lo = f32x4_max(clo, a1lo); + a1hi = f32x4_max(chi, a1hi); + a2lo = f32x4_max(clo, a2lo); + a2hi = f32x4_max(chi, a2hi); + a3lo = f32x4_max(clo, a3lo); + a3hi = f32x4_max(chi, a3hi); + a4lo = f32x4_max(clo, a4lo); + a4hi = f32x4_max(chi, a4hi); + a5lo = f32x4_max(clo, a5lo); + a5hi = f32x4_max(chi, a5hi); + a6lo = f32x4_max(clo, a6lo); + a6hi = f32x4_max(chi, a6hi); + a7lo = f32x4_max(clo, a7lo); + a7hi = f32x4_max(chi, a7hi); + } + FusedKerSpec::PerColAdd(cols) => { + let p = cols as *const v128; + let clo = v128_load(p); + let chi = v128_load(p.add(1)); + a0lo = f32x4_add(clo, a0lo); + a0hi = f32x4_add(chi, a0hi); + a1lo = f32x4_add(clo, a1lo); + a1hi = f32x4_add(chi, a1hi); + a2lo = f32x4_add(clo, a2lo); + a2hi = f32x4_add(chi, a2hi); + a3lo = f32x4_add(clo, a3lo); + a3hi = f32x4_add(chi, a3hi); + a4lo = f32x4_add(clo, a4lo); + a4hi = f32x4_add(chi, a4hi); + a5lo = f32x4_add(clo, a5lo); + a5hi = f32x4_add(chi, a5hi); + a6lo = f32x4_add(clo, a6lo); + a6hi = f32x4_add(chi, a6hi); + a7lo = f32x4_add(clo, a7lo); + a7hi = f32x4_add(chi, a7hi); + } + FusedKerSpec::PerColMul(cols) => { + let p = cols as *const v128; + let clo = v128_load(p); + let chi = v128_load(p.add(1)); + a0lo = f32x4_mul(clo, a0lo); + a0hi = f32x4_mul(chi, a0hi); + a1lo = f32x4_mul(clo, a1lo); + a1hi = f32x4_mul(chi, a1hi); + a2lo = f32x4_mul(clo, a2lo); + a2hi = f32x4_mul(chi, a2hi); + a3lo = f32x4_mul(clo, a3lo); + a3hi = f32x4_mul(chi, a3hi); + a4lo = f32x4_mul(clo, a4lo); + a4hi = f32x4_mul(chi, a4hi); + a5lo = f32x4_mul(clo, a5lo); + a5hi = f32x4_mul(chi, a5hi); + a6lo = f32x4_mul(clo, a6lo); + a6hi = f32x4_mul(chi, a6hi); + a7lo = f32x4_mul(clo, a7lo); + a7hi = f32x4_mul(chi, a7hi); + } + FusedKerSpec::PerColSub(cols) => { + let p = cols as *const v128; + let clo = v128_load(p); + let chi = v128_load(p.add(1)); + a0lo = f32x4_sub(clo, a0lo); + a0hi = f32x4_sub(chi, a0hi); + a1lo = f32x4_sub(clo, a1lo); + a1hi = f32x4_sub(chi, a1hi); + a2lo = f32x4_sub(clo, a2lo); + a2hi = f32x4_sub(chi, a2hi); + a3lo = f32x4_sub(clo, a3lo); + a3hi = f32x4_sub(chi, a3hi); + a4lo = f32x4_sub(clo, a4lo); + a4hi = f32x4_sub(chi, a4hi); + a5lo = f32x4_sub(clo, a5lo); + a5hi = f32x4_sub(chi, a5hi); + a6lo = f32x4_sub(clo, a6lo); + a6hi = f32x4_sub(chi, a6hi); + a7lo = f32x4_sub(clo, a7lo); + a7hi = f32x4_sub(chi, a7hi); + } + FusedKerSpec::PerColSubF(cols) => { + let p = cols as *const v128; + let clo = v128_load(p); + let chi = v128_load(p.add(1)); + a0lo = f32x4_sub(a0lo, clo); + a0hi = f32x4_sub(a0hi, chi); + a1lo = f32x4_sub(a1lo, clo); + a1hi = f32x4_sub(a1hi, chi); + a2lo = f32x4_sub(a2lo, clo); + a2hi = f32x4_sub(a2hi, chi); + a3lo = f32x4_sub(a3lo, clo); + a3hi = f32x4_sub(a3hi, chi); + a4lo = f32x4_sub(a4lo, clo); + a4hi = f32x4_sub(a4hi, chi); + a5lo = f32x4_sub(a5lo, clo); + a5hi = f32x4_sub(a5hi, chi); + a6lo = f32x4_sub(a6lo, clo); + a6hi = f32x4_sub(a6hi, chi); + a7lo = f32x4_sub(a7lo, clo); + a7hi = f32x4_sub(a7hi, chi); + } + FusedKerSpec::QScale(shift, rp, mult) => { + let scaler = Scaler::from_fuse_params(shift, rp, mult); + let s = f32x4_splat(scaler.scale); + a0lo = f32x4_mul(s, a0lo); + a0hi = f32x4_mul(s, a0hi); + a1lo = f32x4_mul(s, a1lo); + a1hi = f32x4_mul(s, a1hi); + a2lo = f32x4_mul(s, a2lo); + a2hi = f32x4_mul(s, a2hi); + a3lo = f32x4_mul(s, a3lo); + a3hi = f32x4_mul(s, a3hi); + a4lo = f32x4_mul(s, a4lo); + a4hi = f32x4_mul(s, a4hi); + a5lo = f32x4_mul(s, a5lo); + a5hi = f32x4_mul(s, a5hi); + a6lo = f32x4_mul(s, a6lo); + a6hi = f32x4_mul(s, a6hi); + a7lo = f32x4_mul(s, a7lo); + a7hi = f32x4_mul(s, a7hi); + } + FusedKerSpec::RoundingShiftRight(shift, _rp) => { + let s = f32x4_splat(2f32.powi(-(shift as i32))); + a0lo = f32x4_mul(s, a0lo); + a0hi = f32x4_mul(s, a0hi); + a1lo = f32x4_mul(s, a1lo); + a1hi = f32x4_mul(s, a1hi); + a2lo = f32x4_mul(s, a2lo); + a2hi = f32x4_mul(s, a2hi); + a3lo = f32x4_mul(s, a3lo); + a3hi = f32x4_mul(s, a3hi); + a4lo = f32x4_mul(s, a4lo); + a4hi = f32x4_mul(s, a4hi); + a5lo = f32x4_mul(s, a5lo); + a5hi = f32x4_mul(s, a5hi); + a6lo = f32x4_mul(s, a6lo); + a6hi = f32x4_mul(s, a6hi); + a7lo = f32x4_mul(s, a7lo); + a7hi = f32x4_mul(s, a7hi); + } + FusedKerSpec::ShiftLeft(shift) => { + let s = f32x4_splat(2f32.powi(shift as i32)); + a0lo = f32x4_mul(s, a0lo); + a0hi = f32x4_mul(s, a0hi); + a1lo = f32x4_mul(s, a1lo); + a1hi = f32x4_mul(s, a1hi); + a2lo = f32x4_mul(s, a2lo); + a2hi = f32x4_mul(s, a2hi); + a3lo = f32x4_mul(s, a3lo); + a3hi = f32x4_mul(s, a3hi); + a4lo = f32x4_mul(s, a4lo); + a4hi = f32x4_mul(s, a4hi); + a5lo = f32x4_mul(s, a5lo); + a5hi = f32x4_mul(s, a5hi); + a6lo = f32x4_mul(s, a6lo); + a6hi = f32x4_mul(s, a6hi); + a7lo = f32x4_mul(s, a7lo); + a7hi = f32x4_mul(s, a7hi); + } + FusedKerSpec::AddUnicast(tile) => { + // 8 rows × 8 cols, each row laid out per col_byte_stride + let mut ptr: *const u8 = tile.ptr; + for ab_pair in [ + (&mut a0lo, &mut a0hi), + (&mut a1lo, &mut a1hi), + (&mut a2lo, &mut a2hi), + (&mut a3lo, &mut a3hi), + (&mut a4lo, &mut a4hi), + (&mut a5lo, &mut a5hi), + (&mut a6lo, &mut a6hi), + (&mut a7lo, &mut a7hi), + ] + .iter_mut() + { + let m0 = *(ptr as *const f32); + let m1 = *(ptr.offset(tile.col_byte_stride) as *const f32); + let m2 = *(ptr.offset(tile.col_byte_stride * 2) as *const f32); + let m3 = *(ptr.offset(tile.col_byte_stride * 3) as *const f32); + let m4 = *(ptr.offset(tile.col_byte_stride * 4) as *const f32); + let m5 = *(ptr.offset(tile.col_byte_stride * 5) as *const f32); + let m6 = *(ptr.offset(tile.col_byte_stride * 6) as *const f32); + let m7 = *(ptr.offset(tile.col_byte_stride * 7) as *const f32); + let (lo, hi) = ab_pair; + **lo = f32x4_add(**lo, f32x4(m0, m1, m2, m3)); + **hi = f32x4_add(**hi, f32x4(m4, m5, m6, m7)); + ptr = ptr.add(tile.row_byte_stride as usize); + } + } + FusedKerSpec::AddRowColProducts(rows, cols) => { + let p = cols as *const v128; + let clo = v128_load(p); + let chi = v128_load(p.add(1)); + let r0 = f32x4_splat(*rows.add(0)); + a0lo = madd_f32x4!(a0lo, r0, clo); + a0hi = madd_f32x4!(a0hi, r0, chi); + let r1 = f32x4_splat(*rows.add(1)); + a1lo = madd_f32x4!(a1lo, r1, clo); + a1hi = madd_f32x4!(a1hi, r1, chi); + let r2 = f32x4_splat(*rows.add(2)); + a2lo = madd_f32x4!(a2lo, r2, clo); + a2hi = madd_f32x4!(a2hi, r2, chi); + let r3 = f32x4_splat(*rows.add(3)); + a3lo = madd_f32x4!(a3lo, r3, clo); + a3hi = madd_f32x4!(a3hi, r3, chi); + let r4 = f32x4_splat(*rows.add(4)); + a4lo = madd_f32x4!(a4lo, r4, clo); + a4hi = madd_f32x4!(a4hi, r4, chi); + let r5 = f32x4_splat(*rows.add(5)); + a5lo = madd_f32x4!(a5lo, r5, clo); + a5hi = madd_f32x4!(a5hi, r5, chi); + let r6 = f32x4_splat(*rows.add(6)); + a6lo = madd_f32x4!(a6lo, r6, clo); + a6hi = madd_f32x4!(a6hi, r6, chi); + let r7 = f32x4_splat(*rows.add(7)); + a7lo = madd_f32x4!(a7lo, r7, clo); + a7hi = madd_f32x4!(a7hi, r7, chi); + } + FusedKerSpec::Store(tile) => { + // 8 rows × 8 cols stores + let mut ptr: *mut u8 = tile.ptr; + for (lo, hi) in [ + (a0lo, a0hi), + (a1lo, a1hi), + (a2lo, a2hi), + (a3lo, a3hi), + (a4lo, a4hi), + (a5lo, a5hi), + (a6lo, a6hi), + (a7lo, a7hi), + ] + .iter() + { + *(ptr as *mut f32) = f32x4_extract_lane::<0>(*lo); + *(ptr.offset(tile.col_byte_stride) as *mut f32) = + f32x4_extract_lane::<1>(*lo); + *(ptr.offset(tile.col_byte_stride * 2) as *mut f32) = + f32x4_extract_lane::<2>(*lo); + *(ptr.offset(tile.col_byte_stride * 3) as *mut f32) = + f32x4_extract_lane::<3>(*lo); + *(ptr.offset(tile.col_byte_stride * 4) as *mut f32) = + f32x4_extract_lane::<0>(*hi); + *(ptr.offset(tile.col_byte_stride * 5) as *mut f32) = + f32x4_extract_lane::<1>(*hi); + *(ptr.offset(tile.col_byte_stride * 6) as *mut f32) = + f32x4_extract_lane::<2>(*hi); + *(ptr.offset(tile.col_byte_stride * 7) as *mut f32) = + f32x4_extract_lane::<3>(*hi); + ptr = ptr.add(tile.row_byte_stride as usize); + } + } + FusedKerSpec::AddMatMul { + k, + pa, + pb, + packing: _, + } => { + // A: packed [k][MR=8] = each k iter loads 8 row values + // B: packed [k][NR=8] = each k iter loads 8 col values as 2 v128 + let a = pa as *const f32; + let b = pb as *const v128; + for i in 0..k { + let arow = std::slice::from_raw_parts(a.offset(8 * i as isize), 8); + let blo = v128_load(b.offset((2 * i) as isize)); + let bhi = v128_load(b.offset((2 * i + 1) as isize)); + let s = f32x4_splat(arow[0]); + a0lo = madd_f32x4!(a0lo, s, blo); + a0hi = madd_f32x4!(a0hi, s, bhi); + let s = f32x4_splat(arow[1]); + a1lo = madd_f32x4!(a1lo, s, blo); + a1hi = madd_f32x4!(a1hi, s, bhi); + let s = f32x4_splat(arow[2]); + a2lo = madd_f32x4!(a2lo, s, blo); + a2hi = madd_f32x4!(a2hi, s, bhi); + let s = f32x4_splat(arow[3]); + a3lo = madd_f32x4!(a3lo, s, blo); + a3hi = madd_f32x4!(a3hi, s, bhi); + let s = f32x4_splat(arow[4]); + a4lo = madd_f32x4!(a4lo, s, blo); + a4hi = madd_f32x4!(a4hi, s, bhi); + let s = f32x4_splat(arow[5]); + a5lo = madd_f32x4!(a5lo, s, blo); + a5hi = madd_f32x4!(a5hi, s, bhi); + let s = f32x4_splat(arow[6]); + a6lo = madd_f32x4!(a6lo, s, blo); + a6hi = madd_f32x4!(a6hi, s, bhi); + let s = f32x4_splat(arow[7]); + a7lo = madd_f32x4!(a7lo, s, blo); + a7hi = madd_f32x4!(a7hi, s, bhi); + } + } + } + pnl = pnl.add(1); + } + 0 + } +} + +// ManuallyOptimized so kernel_selection::strategize honours the mmm_f32 +// callback that returns it for N>1 GEMM (see the `plug` comment) — otherwise +// strategize drops it and routes every GEMM onto the 32x1 GEMV kernel. +MMMRustKernel!(kernel_f32_8x8 => wasm_f32_8x8(8,8)@(8,8) quality(ImplementationQuality::ManuallyOptimized)); + +// Wasm SIMD int8 -> i32 matmul kernel (4x4). WASM's only integer dot +// (i32x4.relaxed_dot_i8x16_i7x16) is non-deterministic for full i8 (its 2nd +// operand is i7), so for a bit-exact kernel the AddMatMul K-loop uses widening +// i8->i32 + i32x4 mul/add (an extmul/SMLAL-style outer product). The quant +// epilogue + fuse ops reuse the bit-exact scalar path (q_scale/q_shr/q_shl), +// which is O(MR*NR) and negligible vs the O(MR*NR*K) inner loop. Bit-identical +// to generic_i32_4x4; selected for i8 matmul via its ManuallyOptimized quality +// (WASM had no int8 matmul kernel — int8 fell back to the generic scalar one). +#[inline(never)] +unsafe fn kernel_i32_4x4(mut pnl: *const FusedKerSpec) -> isize { + use crate::ScaleShiftAndRound; + use std::arch::wasm32::*; + unsafe { + let mut ab = [[0i32; 4]; 4]; + loop { + if pnl.is_null() { + break; + } + match *pnl { + FusedKerSpec::Done => break, + FusedKerSpec::Clear => ab = [[0i32; 4]; 4], + FusedKerSpec::LoadTile(col_major, _row_major) => { + for row in 0..4 { + for col in 0..4 { + ab[row][col] = *col_major.add(col * 4 + row); + } + } + } + FusedKerSpec::ScalarAdd(a) => { + for i in 0..4 { + for j in 0..4 { + ab[i][j] += a; + } + } + } + FusedKerSpec::ScalarMul(a) => { + for i in 0..4 { + for j in 0..4 { + ab[i][j] *= a; + } + } + } + FusedKerSpec::ScalarMin(m) => { + for i in 0..4 { + for j in 0..4 { + ab[i][j] = ab[i][j].min(m); + } + } + } + FusedKerSpec::ScalarMax(m) => { + for i in 0..4 { + for j in 0..4 { + ab[i][j] = ab[i][j].max(m); + } + } + } + FusedKerSpec::ScalarSub(m) => { + for i in 0..4 { + for j in 0..4 { + ab[i][j] = m - ab[i][j]; + } + } + } + FusedKerSpec::ScalarSubF(m) => { + for i in 0..4 { + for j in 0..4 { + ab[i][j] -= m; + } + } + } + FusedKerSpec::LeakyRelu(a) => { + for i in 0..4 { + for j in 0..4 { + ab[i][j] = if ab[i][j] > 0 { ab[i][j] } else { a * ab[i][j] }; + } + } + } + FusedKerSpec::PerRowMin(m) => { + for i in 0..4 { + let v = *m.add(i); + for j in 0..4 { + ab[i][j] = ab[i][j].min(v); + } + } + } + FusedKerSpec::PerRowMax(m) => { + for i in 0..4 { + let v = *m.add(i); + for j in 0..4 { + ab[i][j] = ab[i][j].max(v); + } + } + } + FusedKerSpec::PerRowAdd(m) => { + for i in 0..4 { + let v = *m.add(i); + for j in 0..4 { + ab[i][j] += v; + } + } + } + FusedKerSpec::PerRowMul(m) => { + for i in 0..4 { + let v = *m.add(i); + for j in 0..4 { + ab[i][j] *= v; + } + } + } + FusedKerSpec::PerRowSub(m) => { + for i in 0..4 { + let v = *m.add(i); + for j in 0..4 { + ab[i][j] = v - ab[i][j]; + } + } + } + FusedKerSpec::PerRowSubF(m) => { + for i in 0..4 { + let v = *m.add(i); + for j in 0..4 { + ab[i][j] -= v; + } + } + } + FusedKerSpec::PerColMin(m) => { + let c = std::slice::from_raw_parts(m, 4); + for i in 0..4 { + for j in 0..4 { + ab[i][j] = ab[i][j].min(c[j]); + } + } + } + FusedKerSpec::PerColMax(m) => { + let c = std::slice::from_raw_parts(m, 4); + for i in 0..4 { + for j in 0..4 { + ab[i][j] = ab[i][j].max(c[j]); + } + } + } + FusedKerSpec::PerColAdd(m) => { + let c = std::slice::from_raw_parts(m, 4); + for i in 0..4 { + for j in 0..4 { + ab[i][j] += c[j]; + } + } + } + FusedKerSpec::PerColMul(m) => { + let c = std::slice::from_raw_parts(m, 4); + for i in 0..4 { + for j in 0..4 { + ab[i][j] *= c[j]; + } + } + } + FusedKerSpec::PerColSub(m) => { + let c = std::slice::from_raw_parts(m, 4); + for i in 0..4 { + for j in 0..4 { + ab[i][j] = c[j] - ab[i][j]; + } + } + } + FusedKerSpec::PerColSubF(m) => { + let c = std::slice::from_raw_parts(m, 4); + for i in 0..4 { + for j in 0..4 { + ab[i][j] -= c[j]; + } + } + } + FusedKerSpec::AddRowColProducts(rows, cols) => { + for i in 0..4 { + let r = *rows.add(i); + for j in 0..4 { + ab[i][j] += r * *cols.add(j); + } + } + } + FusedKerSpec::AddUnicast(other) => { + for i in 0..4 { + for j in 0..4 { + let p = other.ptr.offset( + other.row_byte_stride * i as isize + + other.col_byte_stride * j as isize, + ); + let v = match other.item_size { + 1 => *(p as *const i8) as i32, + 4 => *(p as *const i32), + _ => return 1, + }; + ab[i][j] += v; + } + } + } + FusedKerSpec::ShiftLeft(shift) => { + for i in 0..4 { + for j in 0..4 { + ab[i][j] = ab[i][j].q_shl(shift); + } + } + } + FusedKerSpec::RoundingShiftRight(shift, rp) => { + for i in 0..4 { + for j in 0..4 { + ab[i][j] = ab[i][j].q_shr(shift, rp); + } + } + } + FusedKerSpec::QScale(shift, rp, mult) => { + let s = Scaler::from_fuse_params(shift, rp, mult); + for i in 0..4 { + for j in 0..4 { + ab[i][j] = ab[i][j].q_scale(s); + } + } + } + FusedKerSpec::AddMatMul { k, pa, pb, packing } => { + if packing == 1 { + let a = pa as *const i8; + let b = pb as *const i8; + let mut acc = [ + v128_load(ab[0].as_ptr() as *const v128), + v128_load(ab[1].as_ptr() as *const v128), + v128_load(ab[2].as_ptr() as *const v128), + v128_load(ab[3].as_ptr() as *const v128), + ]; + // PackedI8K4 (K=4-inner): per 4-K block, one B v128 load is + // shared across the 4 rows; each row broadcasts its 4 K bytes + // (a[kb*16 + m*4 ..]) and issues one relaxed_dot (16 MACs). + // b[kb*16 + n*4 + kr]. Tail K (k%4) is zero-padded by the packer. + #[cfg(target_feature = "relaxed-simd")] + for kb in 0..k.div_ceil(4) { + let b_all = v128_load(b.add(kb * 16) as *const v128); + for (m, acc_m) in acc.iter_mut().enumerate() { + let a4 = (a.add(kb * 16 + m * 4) as *const i32).read_unaligned(); + *acc_m = i32x4_relaxed_dot_i8x16_i7x16_add( + i32x4_splat(a4), + b_all, + *acc_m, + ); + } + } + // Deterministic fallback (no relaxed-simd): standard PackedFormat + // K-major (4 i8 per k), widening outer-product accumulate. + #[cfg(not(target_feature = "relaxed-simd"))] + for ik in 0..k { + let bw = v128_load32_zero(b.add(4 * ik) as *const u32); + let bw = i16x8_extend_low_i8x16(bw); + let bw = i32x4_extend_low_i16x8(bw); + let ar = a.add(4 * ik); + acc[0] = + i32x4_add(acc[0], i32x4_mul(i32x4_splat(*ar.add(0) as i32), bw)); + acc[1] = + i32x4_add(acc[1], i32x4_mul(i32x4_splat(*ar.add(1) as i32), bw)); + acc[2] = + i32x4_add(acc[2], i32x4_mul(i32x4_splat(*ar.add(2) as i32), bw)); + acc[3] = + i32x4_add(acc[3], i32x4_mul(i32x4_splat(*ar.add(3) as i32), bw)); + } + v128_store(ab[0].as_mut_ptr() as *mut v128, acc[0]); + v128_store(ab[1].as_mut_ptr() as *mut v128, acc[1]); + v128_store(ab[2].as_mut_ptr() as *mut v128, acc[2]); + v128_store(ab[3].as_mut_ptr() as *mut v128, acc[3]); + } else if packing == 0 { + // i32 x i32, K-major (scalar; rare path). + let a = pa as *const i32; + let b = pb as *const i32; + for ik in 0..k { + for i in 0..4 { + let av = *a.add(4 * ik + i); + for j in 0..4 { + ab[i][j] += av * *b.add(4 * ik + j); + } + } + } + } else { + return 1; + } + } + FusedKerSpec::Store(tile) => match tile.item_size { + 1 => { + for i in 0..4 { + for j in 0..4 { + let loc = tile.ptr.offset( + tile.row_byte_stride * i as isize + + tile.col_byte_stride * j as isize, + ) as *mut u8; + *loc = ab[i][j] as u8; + } + } + } + 4 => { + for i in 0..4 { + for j in 0..4 { + let loc = tile.ptr.offset( + tile.row_byte_stride * i as isize + + tile.col_byte_stride * j as isize, + ) as *mut i32; + *loc = ab[i][j]; + } + } + } + _ => return 1, + }, + }; + pnl = pnl.add(1); + } + } + 0 +} + +// i8i8 packing for wasm_i32_4x4. Under +relaxed-simd the kernel uses the +// `i32x4_relaxed_dot_i8x16_i7x16_add` SDOT-analog, which wants 4 contiguous K per +// mn-lane → PackedI8K4 (K=4-inner). Without relaxed-simd it uses the widening +// outer-product, which wants K-major → standard PackedFormat. Both are picked at +// compile time so the kernel's AddMatMul and the packer always agree. +#[cfg(target_feature = "relaxed-simd")] +fn wasm_i8_packing() -> impl crate::mmm::MMMInputFormat { + crate::pack::PackedI8K4::new(4) +} +#[cfg(not(target_feature = "relaxed-simd"))] +fn wasm_i8_packing() -> impl crate::mmm::MMMInputFormat { + use crate::pack::Packing; + i8::packing(4) +} + +MMMRustKernel!(kernel_i32_4x4 => wasm_i32_4x4(4,4) + packing[1] = i8i8 => |k| k.with_packing(wasm_i8_packing(), wasm_i8_packing()); + quality(ImplementationQuality::ManuallyOptimized) + store(i8) +); + +#[cfg(test)] +mod dispatch_trace { + fn trace_one(label: &str, m: Option, k: Option, n: Option) { + let mut ops = crate::generic(); + super::plug(&mut ops); + let mmm = ops + .mmm(tract_data::prelude::DatumType::F32, m, k, n) + .unwrap(); + eprintln!( + "DFN3 {} (m={:?} k={:?} n={:?}) => {} [mr={}, nr={}]", + label, + m, + k, + n, + mmm.name(), + mmm.mr(), + mmm.nr() + ); + } + + #[test] + fn dfn3_shapes() { + // DFN3 N=1 GEMV ops (the dominant matrix-vector cases) + trace_one("lsnr_fc-style m=1 k=512", Some(1), Some(512), Some(1)); + trace_one("small m=16 k=96", Some(16), Some(96), Some(1)); + trace_one("medium m=32 k=256", Some(32), Some(256), Some(1)); + trace_one("GRU m=256 k=256", Some(256), Some(256), Some(1)); + trace_one("post-rnn m=256 k=512", Some(256), Some(512), Some(1)); + trace_one("frame-encoder m=64 k=96", Some(64), Some(96), Some(1)); + // N>1 sanity: should hit 8x8 + trace_one("MM m=64 k=64 n=8", Some(64), Some(64), Some(8)); + } + + /// Exercise every M-band edge of mmv_f32 to lock in the dispatch. + /// Lower edge of each band = perfect-tile size; upper edge = last + /// M before crossover to the next kernel. + #[test] + fn band_edges() { + // 4x1 band: M ∈ 0..=4 + trace_one("band 4x1 lo m=1", Some(1), Some(64), Some(1)); + trace_one("band 4x1 hi m=4", Some(4), Some(64), Some(1)); + // 8x1 band: M ∈ 5..=8 + trace_one("band 8x1 lo m=5", Some(5), Some(64), Some(1)); + trace_one("band 8x1 hi m=8", Some(8), Some(64), Some(1)); + // 16x1 band: M ∈ 9..=16 + trace_one("band 16x1 lo m=9", Some(9), Some(64), Some(1)); + trace_one("band 16x1 hi m=16", Some(16), Some(64), Some(1)); + // 32x1 band: M ≥ 17 + trace_one("band 32x1 lo m=17", Some(17), Some(64), Some(1)); + trace_one("band 32x1 hi m=512", Some(512), Some(64), Some(1)); + } + + /// Regression guard for the GEMM/GEMV dispatch. + /// + /// `kernel_selection::strategize` honours the `mmm_f32` / `mmv_f32` + /// callback only when the returned kernel is `ManuallyOptimized`; + /// otherwise it falls through to `list_impls`, whose `retain()` drops + /// every `TargetOptimized` kernel, and for N>1 then picks `max(nr*mr)` + /// over the surviving `ManuallyOptimized` GEMV kernels — i.e. + /// `wasm_f32_32x1`, a matrix×vector kernel, for every GEMM. So every + /// kernel reachable through the dispatch callbacks must be + /// `ManuallyOptimized`. + #[test] + fn dispatch_kernels_are_manually_optimized() { + use crate::mmm::ImplementationQuality::ManuallyOptimized; + let mut ops = crate::generic(); + super::plug(&mut ops); + for (label, m, k, n) in [ + ("GEMM m=64 k=64 n=8", 64, 64, 8), + ("GEMM m=256 k=256 n=256", 256, 256, 256), + ("GEMM m=1024 k=576 n=10", 1024, 576, 10), + ("GEMV m=1 k=512 n=1", 1, 512, 1), + ("GEMV m=256 k=256 n=1", 256, 256, 1), + ] { + let mmm = ops + .mmm( + tract_data::prelude::DatumType::F32, + Some(m), + Some(k), + Some(n), + ) + .unwrap(); + assert_eq!( + mmm.quality(), + ManuallyOptimized, + "{label}: dispatch returned {} tagged {:?} — strategize would \ + discard it and reroute onto a GEMV kernel", + mmm.name(), + mmm.quality(), + ); + } + } +} + +#[cfg(test)] +mod microbench_32x1 { + //! Quick microbench: time per-call cost for the kernel kit's GEMV path + //! on DFN3-shaped inputs. Compares 16x1 vs 32x1 head-to-head by + //! dispatching the named kernel directly. + //! + //! Run with: + //! RUSTFLAGS='-C target-feature=+simd128' \ + //! CARGO_TARGET_WASM32_WASIP1_RUNNER='wasmtime --env RUST_TEST_NOCAPTURE=1 --' \ + //! cargo test --release --target=wasm32-wasip1 -p tract-linalg \ + //! wasm::microbench_32x1::microbench -- --nocapture --ignored + + use crate::mmm::{AsInputValue, FusedSpec}; + use std::time::Instant; + use tract_data::internal::*; + use tract_data::prelude::*; + + fn run_one(kernel: &dyn crate::mmm::MatMatMul, m: usize, k: usize, iters: usize) -> f64 { + // Pack A (m,k) and B (k,1) + let packing = &kernel.packings()[0]; + let a = Tensor::zero::(&[m, k]).unwrap(); + let pa = packing.0.prepare_one(&a, 1, 0).unwrap(); + let b = Tensor::zero::(&[k, 1]).unwrap(); + let pb = packing.1.prepare_one(&b, 0, 1).unwrap(); + let mut c = Tensor::zero::(&[m, 1]).unwrap(); + + // Warmup + for _ in 0..50 { + unsafe { + kernel + .run( + m, + 1, + &[ + FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa), + b: AsInputValue::Borrowed(&*pb), + packing: 0, + }, + FusedSpec::Store(kernel.c_view(Some(0), Some(0)).wrap(&c.view_mut())), + ], + ) + .unwrap(); + } + } + + // Timed + let t0 = Instant::now(); + for _ in 0..iters { + unsafe { + kernel + .run( + m, + 1, + &[ + FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa), + b: AsInputValue::Borrowed(&*pb), + packing: 0, + }, + FusedSpec::Store(kernel.c_view(Some(0), Some(0)).wrap(&c.view_mut())), + ], + ) + .unwrap(); + } + } + let elapsed = t0.elapsed(); + elapsed.as_secs_f64() / iters as f64 * 1e9 // ns/call + } + + fn pick(name: &str) -> Box { + let mut ops = crate::generic(); + super::plug(&mut ops); + for impl_ in ops.mmm_impls() { + if impl_.name() == name { + return impl_.clone(); + } + } + panic!("kernel {name} not registered") + } + + fn bench_shape(label: &str, m: usize, k: usize, iters: usize) { + let k16 = pick("wasm_f32_16x1"); + let k32 = pick("wasm_f32_32x1"); + let ns16 = run_one(&*k16, m, k, iters); + let ns32 = run_one(&*k32, m, k, iters); + let calls16 = m.div_ceil(16); + let calls32 = m.div_ceil(32); + let delta = (ns32 - ns16) / ns16 * 100.0; + eprintln!( + "{label} (m={m}, k={k}, iters={iters}): 16x1={ns16:.1} ns/call ({calls16} kernel calls); 32x1={ns32:.1} ns/call ({calls32} kernel calls); Δ={delta:+.2}% ; per-frame call ns: 16x1={n16:.1} 32x1={n32:.1} pf-Δ={dpf:+.2}%", + n16 = ns16 * calls16 as f64, + n32 = ns32 * calls32 as f64, + dpf = (ns32 * calls32 as f64 - ns16 * calls16 as f64) / (ns16 * calls16 as f64) * 100.0, + ); + } + + #[test] + #[ignore] + fn microbench() { + eprintln!("=== DFN3 GEMV microbench: 16x1 vs 32x1 ==="); + // DFN3 GRU gates (highest call count) + bench_shape("GRU m=256 k=256", 256, 256, 5_000); + // post-RNN + bench_shape("post-rnn m=256 k=512", 256, 512, 3_000); + // frame encoder + bench_shape("frame-encoder m=64 k=96", 64, 96, 20_000); + // perfect tile + bench_shape("perfect-tile m=32 k=256", 32, 256, 20_000); + } + + /// Numerical-equivalence sanity check between 16x1 and 32x1 kernels on a + /// real-shape matmul with non-trivial inputs. + /// + /// Under `+simd128` (no relaxed-simd): both kernels emit + /// `f32x4_add(f32x4_mul(...))` via `madd_f32x4!`, so the K-loop order is + /// identical and outputs are bit-identical. + /// + /// Under `+simd128,+relaxed-simd`: 32x1 uses `f32x4.relaxed_madd` (fused + /// FMA) via `madd_f32x4!`, while 16x1 uses separate `mul+add` via + /// `madd_f32x4_nofma!` to avoid the destructive-accumulator recurrence + /// that throttles ≤4-accumulator kernels (see header comment on + /// `madd_f32x4_nofma`). Outputs drift by ≤1 ulp per K-step from the + /// rounding difference between fused and separate ops. We accept that + /// drift with a generous relative tolerance. + #[test] + fn numerical_consistency_16x1_vs_32x1() { + let m = 256usize; + let k = 256usize; + let mut a_data = vec![0f32; m * k]; + for (i, x) in a_data.iter_mut().enumerate() { + *x = ((i % 13) as f32 - 6.0) * 0.1 + ((i / 17) % 11) as f32 * 0.07; + } + let mut b_data = vec![0f32; k]; + for (i, x) in b_data.iter_mut().enumerate() { + *x = (i as f32).sin() * 0.5; + } + let a = Tensor::from_shape(&[m, k], &a_data).unwrap(); + let b = Tensor::from_shape(&[k, 1], &b_data).unwrap(); + + let run = |name: &str| -> Vec { + let kernel = pick(name); + let packing = &kernel.packings()[0]; + let pa = packing.0.prepare_one(&a, 1, 0).unwrap(); + let pb = packing.1.prepare_one(&b, 0, 1).unwrap(); + let mut c = Tensor::zero::(&[m, 1]).unwrap(); + unsafe { + kernel + .run( + m, + 1, + &[ + FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa), + b: AsInputValue::Borrowed(&*pb), + packing: 0, + }, + FusedSpec::Store(kernel.c_view(Some(0), Some(0)).wrap(&c.view_mut())), + ], + ) + .unwrap(); + } + c.try_as_plain() + .unwrap() + .as_slice::() + .unwrap() + .to_vec() + }; + + let c16 = run("wasm_f32_16x1"); + let c32 = run("wasm_f32_32x1"); + + #[cfg(not(target_feature = "relaxed-simd"))] + { + for (i, (x16, x32)) in c16.iter().zip(c32.iter()).enumerate() { + assert!( + x16.to_bits() == x32.to_bits(), + "row {i}: 16x1={x16} (bits 0x{:x}) != 32x1={x32} (bits 0x{:x})", + x16.to_bits(), + x32.to_bits() + ); + } + eprintln!("bit-identity OK over m={m} k={k} ({} rows)", m); + } + + #[cfg(target_feature = "relaxed-simd")] + { + // K=256 accumulator drift on fp32 between FMA and separate mul+add + // can grow up to roughly K × 0.5 ulp ≈ 128 ulp in the accumulator. + // For small-magnitude outputs that translates to ~1e-4 relative. + // We use 1e-4 as the tolerance — tight enough to catch real bugs + // (typically 1e-2+ drift) but generous for legitimate FMA drift. + let mut max_abs = 0.0f32; + let mut max_rel = 0.0f32; + for (i, (x16, x32)) in c16.iter().zip(c32.iter()).enumerate() { + let abs = (x16 - x32).abs(); + let scale = x16.abs().max(x32.abs()).max(1.0e-9); + let rel = abs / scale; + assert!( + rel < 1.0e-4, + "row {i}: relative drift {rel:e} too large; 16x1={x16} 32x1={x32}" + ); + if abs > max_abs { + max_abs = abs; + } + if rel > max_rel { + max_rel = rel; + } + } + eprintln!( + "relaxed-simd consistency OK over m={m} k={k}: max abs={max_abs:.3e}, max rel={max_rel:.3e}" + ); + } + } +} + +#[cfg(test)] +mod microbench_dispatch_gemv { + //! Microbench: 4x1 vs 8x1 vs 16x1 vs 32x1 GEMV kernels across the M + //! range. Drives the dispatch-fix decision — the M-band callback in + //! plug() routes small-M to smaller kernels, but only takes effect + //! once the kernels are tagged ManuallyOptimized (otherwise + //! kernel_selection::strategize bypasses the callback and always + //! picks max(mr) = 32x1). + //! + //! Run with: + //! RUSTFLAGS='-C target-feature=+simd128' \ + //! CARGO_TARGET_WASM32_WASIP1_RUNNER='wasmtime --env RUST_TEST_NOCAPTURE=1 --' \ + //! cargo test --release --target=wasm32-wasip1 -p tract-linalg \ + //! wasm::microbench_dispatch_gemv::microbench -- --nocapture --ignored + + use crate::mmm::{AsInputValue, FusedSpec}; + use std::time::Instant; + use tract_data::internal::*; + use tract_data::prelude::*; + + fn run_one(kernel: &dyn crate::mmm::MatMatMul, m: usize, k: usize, iters: usize) -> f64 { + let packing = &kernel.packings()[0]; + let a = Tensor::zero::(&[m, k]).unwrap(); + let pa = packing.0.prepare_one(&a, 1, 0).unwrap(); + let b = Tensor::zero::(&[k, 1]).unwrap(); + let pb = packing.1.prepare_one(&b, 0, 1).unwrap(); + let mut c = Tensor::zero::(&[m, 1]).unwrap(); + + for _ in 0..50 { + unsafe { + kernel + .run( + m, + 1, + &[ + FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa), + b: AsInputValue::Borrowed(&*pb), + packing: 0, + }, + FusedSpec::Store(kernel.c_view(Some(0), Some(0)).wrap(&c.view_mut())), + ], + ) + .unwrap(); + } + } + + let t0 = Instant::now(); + for _ in 0..iters { + unsafe { + kernel + .run( + m, + 1, + &[ + FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa), + b: AsInputValue::Borrowed(&*pb), + packing: 0, + }, + FusedSpec::Store(kernel.c_view(Some(0), Some(0)).wrap(&c.view_mut())), + ], + ) + .unwrap(); + } + } + let elapsed = t0.elapsed(); + elapsed.as_secs_f64() / iters as f64 * 1e9 + } + + fn pick(name: &str) -> Box { + let mut ops = crate::generic(); + super::plug(&mut ops); + for impl_ in ops.mmm_impls() { + if impl_.name() == name { + return impl_.clone(); + } + } + panic!("kernel {name} not registered") + } + + fn bench_shape(label: &str, m: usize, k: usize, iters: usize) { + let k4 = pick("wasm_f32_4x1"); + let k8 = pick("wasm_f32_8x1"); + let k16 = pick("wasm_f32_16x1"); + let k32 = pick("wasm_f32_32x1"); + let n4 = run_one(&*k4, m, k, iters); + let n8 = run_one(&*k8, m, k, iters); + let n16 = run_one(&*k16, m, k, iters); + let n32 = run_one(&*k32, m, k, iters); + let entries = [("4x1", n4), ("8x1", n8), ("16x1", n16), ("32x1", n32)]; + let winner = entries + .iter() + .min_by(|a, b| a.1.partial_cmp(&b.1).unwrap()) + .unwrap(); + let delta_vs_32 = (winner.1 - n32) / n32 * 100.0; + eprintln!( + "{label} (m={m} k={k}): 4x1={n4:.0} 8x1={n8:.0} 16x1={n16:.0} 32x1={n32:.0} ns; \ + winner={} ({:.0} ns, Δ vs 32x1: {delta_vs_32:+.1}%)", + winner.0, winner.1 + ); + } + + #[test] + #[ignore] + fn microbench() { + eprintln!("=== WASM GEMV dispatch microbench: 4x1 vs 8x1 vs 16x1 vs 32x1 ==="); + // M ≤ 16 — small-M region; the M-band callback's choices win clearly. + bench_shape("M=1 k=512", 1, 512, 50_000); + bench_shape("M=8 k=64 ", 8, 64, 50_000); + bench_shape("M=8 k=512", 8, 512, 20_000); + bench_shape("M=12 k=256", 12, 256, 50_000); + bench_shape("M=16 k=96 ", 16, 96, 50_000); + bench_shape("M=16 k=256", 16, 256, 30_000); + // M ≥ 17 — 32x1 wins (16x1 needs 2 outer iters, 32x1 single iter + // with ILP absorbing the row padding). + bench_shape("M=24 k=256", 24, 256, 30_000); + bench_shape("M=32 k=256", 32, 256, 20_000); + bench_shape("M=64 k=96 ", 64, 96, 20_000); + bench_shape("M=100 k=256", 100, 256, 10_000); + bench_shape("M=256 k=256", 256, 256, 5_000); + } +} + +// Relaxed-SIMD activation kernels (f32, FMA path). +// +// `f32x4_relaxed_madd(a, b, c)` computes `a * b + c`. On hosts with hardware +// FMA (all ARM64, x86_64 with FMA3) it lowers to a single fused, single- +// rounded instruction. On hosts without, it falls back to mul+add — hence +// "relaxed". The result is therefore not bit-deterministic across all hosts, +// but it is at least as accurate as the separate mul+add (FMA does fewer +// roundings). +// +// For sigmoid/tanh polynomial evaluation, the 14 muladds in the Horner chain +// fuse cleanly. Measured ~1.65x over the baseline-simd128 explicit kernel and +// over LLVM auto-vec'd scalar on V8. +// +// Gated on `target_feature = "relaxed-simd"` because `f32x4_relaxed_madd` +// requires the relaxed-simd proposal to be enabled at compile time. +// --------------------------------------------------------------------------- + +#[cfg(target_feature = "relaxed-simd")] +#[derive(Clone, Debug)] +pub struct WasmSigmoid4Relaxed; + +#[cfg(target_feature = "relaxed-simd")] +impl ElementWiseKer for WasmSigmoid4Relaxed { + fn name() -> &'static str { + "wasm_relaxed_simd" + } + + fn alignment_bytes() -> usize { + 16 + } + + fn alignment_items() -> usize { + 4 + } + + fn nr() -> usize { + 4 + } + + fn run(buf: &mut [f32], _: ()) { + use std::arch::wasm32::*; + + debug_assert!(buf.len() % Self::nr() == 0); + debug_assert!(buf.as_ptr() as usize % Self::alignment_bytes() == 0); + + // Coefficients match generic/sigmoid.rs::ssigmoid bit-for-bit. + // Output may differ by ≤1 ulp from scalar on FMA hosts (more accurate). + const LOW: f32 = -18.6; + const HIGH: f32 = -LOW; + + const ALPHA_13: f32 = -4.433153405e-18; + const ALPHA_11: f32 = 1.169974371e-14; + const ALPHA_9: f32 = -1.875289645e-11; + const ALPHA_7: f32 = 4.257889523e-8; + const ALPHA_5: f32 = 0.00004811817576; + const ALPHA_3: f32 = 0.008163842030; + const ALPHA_1: f32 = 0.2499999971; + + const BETA_6: f32 = 3.922935744e-6; + const BETA_4: f32 = 0.001524872358; + const BETA_2: f32 = 0.1159886749; + const BETA_0: f32 = 1.0; + + unsafe { + let lo = f32x4_splat(LOW); + let hi = f32x4_splat(HIGH); + + let a13 = f32x4_splat(ALPHA_13); + let a11 = f32x4_splat(ALPHA_11); + let a9 = f32x4_splat(ALPHA_9); + let a7 = f32x4_splat(ALPHA_7); + let a5 = f32x4_splat(ALPHA_5); + let a3 = f32x4_splat(ALPHA_3); + let a1 = f32x4_splat(ALPHA_1); + + let b6 = f32x4_splat(BETA_6); + let b4 = f32x4_splat(BETA_4); + let b2 = f32x4_splat(BETA_2); + let b0 = f32x4_splat(BETA_0); + + let half = f32x4_splat(0.5); + + let mut p = buf.as_mut_ptr(); + let end = p.add(buf.len()); + while p < end { + let v = v128_load(p as *const v128); + let x = f32x4_min(hi, f32x4_max(lo, v)); + let x2 = f32x4_mul(x, x); + + // Horner numerator with FMA: pn = x2 * pn + a_n + let pn = a13; + let pn = f32x4_relaxed_madd(x2, pn, a11); + let pn = f32x4_relaxed_madd(x2, pn, a9); + let pn = f32x4_relaxed_madd(x2, pn, a7); + let pn = f32x4_relaxed_madd(x2, pn, a5); + let pn = f32x4_relaxed_madd(x2, pn, a3); + let pn = f32x4_relaxed_madd(x2, pn, a1); + let pn = f32x4_mul(pn, x); + + // Horner denominator with FMA + let qn = b6; + let qn = f32x4_relaxed_madd(x2, qn, b4); + let qn = f32x4_relaxed_madd(x2, qn, b2); + let qn = f32x4_relaxed_madd(x2, qn, b0); + + let r = f32x4_add(f32x4_div(pn, qn), half); + v128_store(p as *mut v128, r); + p = p.add(4); + } + } + } +} + +#[cfg(target_feature = "relaxed-simd")] +#[derive(Clone, Debug)] +pub struct WasmTanh4Relaxed; + +#[cfg(target_feature = "relaxed-simd")] +impl ElementWiseKer for WasmTanh4Relaxed { + fn name() -> &'static str { + "wasm_relaxed_simd" + } + + fn alignment_bytes() -> usize { + 16 + } + + fn alignment_items() -> usize { + 4 + } + + fn nr() -> usize { + 4 + } + + fn run(buf: &mut [f32], _: ()) { + use std::arch::wasm32::*; + + debug_assert!(buf.len() % Self::nr() == 0); + debug_assert!(buf.as_ptr() as usize % Self::alignment_bytes() == 0); + + const LOW: f32 = -8.9; + const HIGH: f32 = 8.9; + + const ALPHA_13: f32 = -8.488492677e-14; + const ALPHA_11: f32 = 5.277853000e-11; + const ALPHA_9: f32 = -2.022500419e-8; + const ALPHA_7: f32 = 0.00001115424833; + const ALPHA_5: f32 = 0.003103950131; + const ALPHA_3: f32 = 0.1308400453; + const ALPHA_1: f32 = 0.9999999934; + + const BETA_6: f32 = 0.0002546136580; + const BETA_4: f32 = 0.02449515379; + const BETA_2: f32 = 0.4641733162; + const BETA_0: f32 = 1.0; + + unsafe { + let lo = f32x4_splat(LOW); + let hi = f32x4_splat(HIGH); + + let a13 = f32x4_splat(ALPHA_13); + let a11 = f32x4_splat(ALPHA_11); + let a9 = f32x4_splat(ALPHA_9); + let a7 = f32x4_splat(ALPHA_7); + let a5 = f32x4_splat(ALPHA_5); + let a3 = f32x4_splat(ALPHA_3); + let a1 = f32x4_splat(ALPHA_1); + + let b6 = f32x4_splat(BETA_6); + let b4 = f32x4_splat(BETA_4); + let b2 = f32x4_splat(BETA_2); + let b0 = f32x4_splat(BETA_0); + + let mut p = buf.as_mut_ptr(); + let end = p.add(buf.len()); + while p < end { + let v = v128_load(p as *const v128); + let x = f32x4_min(hi, f32x4_max(lo, v)); + let x2 = f32x4_mul(x, x); + + let pn = a13; + let pn = f32x4_relaxed_madd(x2, pn, a11); + let pn = f32x4_relaxed_madd(x2, pn, a9); + let pn = f32x4_relaxed_madd(x2, pn, a7); + let pn = f32x4_relaxed_madd(x2, pn, a5); + let pn = f32x4_relaxed_madd(x2, pn, a3); + let pn = f32x4_relaxed_madd(x2, pn, a1); + let pn = f32x4_mul(pn, x); + + let qn = b6; + let qn = f32x4_relaxed_madd(x2, qn, b4); + let qn = f32x4_relaxed_madd(x2, qn, b2); + let qn = f32x4_relaxed_madd(x2, qn, b0); + + let r = f32x4_div(pn, qn); + v128_store(p as *mut v128, r); + p = p.add(4); + } + } + } +} + +#[cfg(all(test, target_feature = "relaxed-simd"))] +#[macro_use] +mod test_wasm_sigmoid_relaxed { + sigmoid_frame_tests!(true, f32, crate::wasm::WasmSigmoid4Relaxed); +} + +#[cfg(all(test, target_feature = "relaxed-simd"))] +#[macro_use] +mod test_wasm_tanh_relaxed { + tanh_frame_tests!(true, f32, crate::wasm::WasmTanh4Relaxed); +} + +#[cfg(all(test, target_feature = "relaxed-simd"))] +mod microbench_activations { + //! Microbench: WASM SIMD sigmoid/tanh vs the generic scalar fallback. + //! Sizes mirror typical RNN/transformer hidden dims (256, 512, 1024). + //! + //! Run with: + //! RUSTFLAGS='-C target-feature=+simd128' \ + //! CARGO_TARGET_WASM32_WASIP1_RUNNER='wasmtime --env RUST_TEST_NOCAPTURE=1 --' \ + //! cargo test --release --target=wasm32-wasip1 -p tract-linalg \ + //! wasm::microbench_activations::microbench -- --nocapture --ignored + use crate::frame::element_wise::ElementWiseKer; + use std::time::Instant; + + fn ns_per_call>(buf: &mut [f32], iters: usize) -> f64 { + // Warmup + for _ in 0..50 { + K::run(buf, ()); + } + let t0 = Instant::now(); + for _ in 0..iters { + K::run(buf, ()); + } + let elapsed = t0.elapsed(); + elapsed.as_secs_f64() / iters as f64 * 1e9 + } + + fn bench(label: &str, n: usize, iters: usize) { + // Same input for both kernels — rebuild between to avoid post-clamp + // saturation mucking up the measurement. + let make = || { + (0..n) + .map(|i| ((i % 37) as f32 - 18.0) * 0.5) + .collect::>() + }; + + let mut buf = make(); + let scalar_sig = ns_per_call::(&mut buf, iters); + let mut buf = make(); + let simd_sig = ns_per_call::(&mut buf, iters); + let mut buf = make(); + let scalar_tanh = ns_per_call::(&mut buf, iters); + let mut buf = make(); + let simd_tanh = ns_per_call::(&mut buf, iters); + + eprintln!( + "{label} n={n} iters={iters}: \ + sigmoid scalar={scalar_sig:.0} ns simd={simd_sig:.0} ns ({:.2}x); \ + tanh scalar={scalar_tanh:.0} ns simd={simd_tanh:.0} ns ({:.2}x)", + scalar_sig / simd_sig, + scalar_tanh / simd_tanh, + ); + } + + #[test] + #[ignore] + fn microbench() { + eprintln!("=== WASM SIMD activations: scalar vs simd ==="); + bench("hidden=256", 256, 5_000); + bench("hidden=512", 512, 3_000); + bench("hidden=1024", 1024, 2_000); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma.rs new file mode 100644 index 0000000..f521170 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma.rs @@ -0,0 +1,128 @@ +use crate::Ops; +use crate::frame::element_wise::ElementWiseKer; +use crate::frame::reduce::{MapReduceKer, ReduceKer}; +use crate::x86_64_fma::softmax::x86_64_avx512_softmax2_fastcompact_f16_64n; +use crate::x86_64_fma::softmax::x86_64_fma_softmax2_fastcompact_f32_32n; + +pub mod mmm; + +pub mod act; +pub mod act_f16; +pub mod act_f16_fp16; +pub mod by_scalar; +pub mod erf; +mod intel; +pub mod max; +pub mod panel_extract; +pub mod rms_norm; +pub mod softmax; + +const AVX2: fn() -> bool = || is_x86_feature_detected!("avx2"); +const FMA: fn() -> bool = || is_x86_feature_detected!("fma"); +const AVX512F: fn() -> bool = || is_x86_feature_detected!("avx512f"); +#[cfg(tract_avx512vnni)] +const AVX512VNNI: fn() -> bool = || is_x86_feature_detected!("avx512vnni"); + +tanh_impl!(f32, fma_tanh_f32, 8, 8, is_x86_feature_detected!("fma")); +sigmoid_impl!(f32, fma_sigmoid_f32, 8, 8, is_x86_feature_detected!("fma")); + +// AVX-512 (zmm, 16-wide) variants. The assembly lives in x86_64/avx512/; the +// main loop handles 64 lanes (4 zmm) per iteration with a 16-lane tail, so +// nr()=16 (any multiple of 16 is safe). +tanh_impl!( + f32, + avx512_tanh_f32, + 16, + 16, + is_x86_feature_detected!("avx512f") +); +sigmoid_impl!( + f32, + avx512_sigmoid_f32, + 16, + 16, + is_x86_feature_detected!("avx512f") +); + +fn plug_avx2(_ops: &mut Ops) {} + +fn plug_fma(ops: &mut Ops) { + panel_extract::plug(ops); + + ops.sigmoid_f32 = Box::new(|| fma_sigmoid_f32::ew()); + ops.tanh_f32 = Box::new(|| fma_tanh_f32::ew()); + + ops.mul_by_scalar_f32 = Box::new(|| by_scalar::x86_64_avx_f32_mul_by_scalar_32n::ew()); + ops.max_f32 = Box::new(|| max::x86_64_fma_max_f32_32n::red()); + ops.softmax2_fastcompact_f32 = Box::new(|| x86_64_fma_softmax2_fastcompact_f32_32n::red()); + + log::info!("sigmoid_f32, tanh_f32: x86_64/fma activated"); +} + +/// On hosts that also support AVX-512_FP16 (Sapphire Rapids / Granite Rapids / +/// later, and recent Xeon-D / consumer parts), upgrade the f16 element-wise +/// kernels from the f32-roundtrip implementations in `act_f16.rs` to the +/// native f16 implementations in `act_f16_fp16.rs` where the native path is +/// actually faster on this uarch. We benched each op against its f32-roundtrip +/// equivalent on Sapphire Rapids and only plug in the ones that win: +/// +/// hardswish_f16: 8.71 → 31.6 Gelem/s (3.62× native) — plug in +/// leaky_relu_f16: 9.44 → 5.85 Gelem/s (0.62× native — regression) — keep +/// the f32-roundtrip version from act_f16.rs. The native +/// kernel exists in act_f16_fp16.rs for future revisits but +/// is not wired here. +fn plug_avx512fp16(ops: &mut Ops) { + ops.hardswish_f16 = Box::new(|| act_f16_fp16::x86_64_avx512fp16_hardswish_f16_128n::ew()); + + log::info!("hardswish_f16: x86_64/avx512fp16 native activated"); +} + +fn plug_avx512f(ops: &mut Ops) { + ops.sigmoid_f32 = Box::new(|| avx512_sigmoid_f32::ew()); + ops.tanh_f32 = Box::new(|| avx512_tanh_f32::ew()); + ops.hardswish_f32 = Box::new(|| act::x86_64_avx512_hardswish_f32_64n::ew()); + ops.leaky_relu_f32 = Box::new(|| act::x86_64_avx512_leaky_relu_f32_64n::ew()); + ops.silu_f32 = Box::new(|| act::x86_64_avx512_silu_f32_16n::ew()); + ops.gelu_f32 = Box::new(|| act::x86_64_avx512_gelu_f32_16n::ew()); + + ops.sigmoid_f16 = Box::new(|| act_f16::x86_64_avx512_sigmoid_f16_16n::ew()); + ops.tanh_f16 = Box::new(|| act_f16::x86_64_avx512_tanh_f16_16n::ew()); + ops.hardswish_f16 = Box::new(|| act_f16::x86_64_avx512_hardswish_f16_64n::ew()); + ops.leaky_relu_f16 = Box::new(|| act_f16::x86_64_avx512_leaky_relu_f16_64n::ew()); + ops.silu_f16 = Box::new(|| act_f16::x86_64_avx512_silu_f16_16n::ew()); + ops.gelu_f16 = Box::new(|| act_f16::x86_64_avx512_gelu_f16_16n::ew()); + + ops.max_f32 = Box::new(|| max::x86_64_avx512_max_f32_64n::red()); + ops.softmax2_fastcompact_f32 = + Box::new(|| softmax::x86_64_avx512_softmax2_fastcompact_f32_64n::red()); + ops.softmax2_fastcompact_f16 = Box::new(|| x86_64_avx512_softmax2_fastcompact_f16_64n::red()); + + ops.erf_f32 = Box::new(|| erf::x86_64_avx512_erf_f32_64n::ew()); + + ops.rms_norm_f32 = Box::new(rms_norm::rms_norm_f32); + + log::info!( + "sigmoid_f32, tanh_f32, hardswish_f32, leaky_relu_f32, \ + silu_f32, gelu_f32, \ + sigmoid_f16, tanh_f16, hardswish_f16, leaky_relu_f16, \ + silu_f16, gelu_f16, \ + max_f32, softmax2_fastcompact_f32, softmax2_fastcompact_f16, erf_f32, \ + rms_norm_f32: x86_64/avx512f activated" + ); +} + +pub fn plug(ops: &mut Ops) { + mmm::plug(ops); + if is_x86_feature_detected!("avx2") { + plug_avx2(ops); + if is_x86_feature_detected!("fma") { + plug_fma(ops); + if is_x86_feature_detected!("avx512f") { + plug_avx512f(ops); + if is_x86_feature_detected!("avx512fp16") { + plug_avx512fp16(ops); + } + } + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/act.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/act.rs new file mode 100644 index 0000000..68b922f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/act.rs @@ -0,0 +1,266 @@ +// AVX-512 (zmm, 16-wide) element-wise activation kernels with no FMA +// predecessor on x86: hardswish and leaky_relu. They mirror the aarch64 NEON +// kernels (arm64simd_hardswish_f32_8n / arm64simd_leaky_relu_f32_8n) but use +// 512-bit zmm registers, processing 64 f32 lanes per iteration. Validated +// against the generic scalar reference via the *_frame_tests! macros. + +// hardswish(x) = x * relu6(x + 3) / 6 +// = x * max(0, min(6, x + 3)) * (1/6) +ew_impl_wrap!( + f32, + x86_64_avx512_hardswish_f32_64n, + 64, + 16, + (), + #[inline(never)] + fn run(buf: &mut [f32], _: ()) { + debug_assert!(buf.len() % Self::nr() == 0); + debug_assert!(buf.as_ptr() as usize % Self::alignment_bytes() == 0); + if buf.is_empty() { + return; + } + unsafe { x86_64_avx512_hardswish_f32_64n_run(buf) } + } +); + +#[target_feature(enable = "avx512f")] +unsafe fn x86_64_avx512_hardswish_f32_64n_run(buf: &mut [f32]) { + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + std::arch::asm!(" + vbroadcastss zmm0, xmm0 // 3.0 + vbroadcastss zmm1, xmm1 // 6.0 + vbroadcastss zmm2, xmm2 // 1/6 + vpxord zmm3, zmm3, zmm3 // 0.0 + 2: + vmovaps zmm4, [{ptr}] + vmovaps zmm5, [{ptr} + 64] + vmovaps zmm6, [{ptr} + 128] + vmovaps zmm7, [{ptr} + 192] + + vaddps zmm8, zmm4, zmm0 + vaddps zmm9, zmm5, zmm0 + vaddps zmm10, zmm6, zmm0 + vaddps zmm11, zmm7, zmm0 + + vminps zmm8, zmm8, zmm1 + vminps zmm9, zmm9, zmm1 + vminps zmm10, zmm10, zmm1 + vminps zmm11, zmm11, zmm1 + + vmaxps zmm8, zmm8, zmm3 + vmaxps zmm9, zmm9, zmm3 + vmaxps zmm10, zmm10, zmm3 + vmaxps zmm11, zmm11, zmm3 + + vmulps zmm8, zmm8, zmm4 + vmulps zmm9, zmm9, zmm5 + vmulps zmm10, zmm10, zmm6 + vmulps zmm11, zmm11, zmm7 + + vmulps zmm8, zmm8, zmm2 + vmulps zmm9, zmm9, zmm2 + vmulps zmm10, zmm10, zmm2 + vmulps zmm11, zmm11, zmm2 + + vmovaps [{ptr}], zmm8 + vmovaps [{ptr} + 64], zmm9 + vmovaps [{ptr} + 128], zmm10 + vmovaps [{ptr} + 192], zmm11 + + add {ptr}, 256 + sub {len}, 64 + jnz 2b + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + inout("xmm0") 3.0f32 => _, + inout("xmm1") 6.0f32 => _, + inout("xmm2") 1.0f32 / 6.0f32 => _, + out("zmm3") _, + out("zmm4") _, out("zmm5") _, out("zmm6") _, out("zmm7") _, + out("zmm8") _, out("zmm9") _, out("zmm10") _, out("zmm11") _, + ); + } +} + +#[cfg(test)] +pub mod test_x86_64_avx512_hardswish_f32_64n { + use super::*; + hardswish_frame_tests!( + is_x86_feature_detected!("avx512f"), + f32, + x86_64_avx512_hardswish_f32_64n + ); +} + +// leaky_relu(x) = x > 0 ? x : alpha * x +ew_impl_wrap!( + f32, + x86_64_avx512_leaky_relu_f32_64n, + 64, + 16, + f32, + #[inline(never)] + fn run(buf: &mut [f32], alpha: f32) { + debug_assert!(buf.len() % Self::nr() == 0); + debug_assert!(buf.as_ptr() as usize % Self::alignment_bytes() == 0); + if buf.is_empty() { + return; + } + unsafe { x86_64_avx512_leaky_relu_f32_64n_run(buf, alpha) } + } +); + +#[target_feature(enable = "avx512f")] +unsafe fn x86_64_avx512_leaky_relu_f32_64n_run(buf: &mut [f32], alpha: f32) { + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + std::arch::asm!(" + vbroadcastss zmm0, xmm0 // alpha + vpxord zmm1, zmm1, zmm1 // 0.0 + 2: + vmovaps zmm4, [{ptr}] + vmovaps zmm5, [{ptr} + 64] + vmovaps zmm6, [{ptr} + 128] + vmovaps zmm7, [{ptr} + 192] + + // alpha * x in zmm8..11 + vmulps zmm8, zmm4, zmm0 + vmulps zmm9, zmm5, zmm0 + vmulps zmm10, zmm6, zmm0 + vmulps zmm11, zmm7, zmm0 + + // mask = x > 0 + vcmpps k1, zmm4, zmm1, 14 + vcmpps k2, zmm5, zmm1, 14 + vcmpps k3, zmm6, zmm1, 14 + vcmpps k4, zmm7, zmm1, 14 + + // where x > 0, overwrite alpha*x with x + vmovaps zmm8{{k1}}, zmm4 + vmovaps zmm9{{k2}}, zmm5 + vmovaps zmm10{{k3}}, zmm6 + vmovaps zmm11{{k4}}, zmm7 + + vmovaps [{ptr}], zmm8 + vmovaps [{ptr} + 64], zmm9 + vmovaps [{ptr} + 128], zmm10 + vmovaps [{ptr} + 192], zmm11 + + add {ptr}, 256 + sub {len}, 64 + jnz 2b + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + inout("xmm0") alpha => _, + out("zmm1") _, + out("zmm4") _, out("zmm5") _, out("zmm6") _, out("zmm7") _, + out("zmm8") _, out("zmm9") _, out("zmm10") _, out("zmm11") _, + out("k1") _, out("k2") _, out("k3") _, out("k4") _, + ); + } +} + +#[cfg(test)] +pub mod test_x86_64_avx512_leaky_relu_f32_64n { + use super::*; + leaky_relu_frame_tests!( + is_x86_feature_detected!("avx512f"), + f32, + x86_64_avx512_leaky_relu_f32_64n + ); +} + +// SiLU(x) = x * sigmoid(x). Composed at the kernel level (mirrors arm64): save +// the input chunk, run the AVX-512 sigmoid kernel in place, then multiply back +// by the saved original. nr() and CHUNK (256) are multiples of 16 so the +// sigmoid kernel always receives a 64-byte-aligned slice whose length is a +// multiple of 16. +ew_impl_wrap!( + f32, + x86_64_avx512_silu_f32_16n, + 16, + 16, + (), + #[inline(never)] + fn run(buf: &mut [f32], _: ()) { + debug_assert!(buf.len() % Self::nr() == 0); + debug_assert!(buf.as_ptr() as usize % Self::alignment_bytes() == 0); + const CHUNK: usize = 256; + let mut scratch = [0f32; CHUNK]; + let mut start = 0; + while start < buf.len() { + let end = (start + CHUNK).min(buf.len()); + let chunk = &mut buf[start..end]; + let n = chunk.len(); + scratch[..n].copy_from_slice(chunk); + super::avx512_sigmoid_f32::run(chunk, ()); + for i in 0..n { + chunk[i] *= scratch[i]; + } + start = end; + } + } +); + +#[cfg(test)] +pub mod test_x86_64_avx512_silu_f32_16n { + use super::*; + silu_frame_tests!( + is_x86_feature_detected!("avx512f"), + f32, + x86_64_avx512_silu_f32_16n + ); +} + +// Tanh-form GELU (pow=3) matching tract's GeluApproximate: +// gelu(x) = 0.5 * x * (1 + tanh(sqrt(2/pi) * (x + 0.044715 * x^3))) +// Composed at the kernel level (mirrors arm64): save the original x, compute +// the tanh argument in place, run the AVX-512 tanh kernel, then finish with the +// 0.5 * x * (1 + tanh) combine. +ew_impl_wrap!( + f32, + x86_64_avx512_gelu_f32_16n, + 16, + 16, + (), + #[inline(never)] + fn run(buf: &mut [f32], _: ()) { + debug_assert!(buf.len() % Self::nr() == 0); + debug_assert!(buf.as_ptr() as usize % Self::alignment_bytes() == 0); + const SQRT_2_OVER_PI: f32 = 0.7978845608028654; + const COEF: f32 = 0.044715; + const CHUNK: usize = 256; + let mut scratch = [0f32; CHUNK]; + let mut start = 0; + while start < buf.len() { + let end = (start + CHUNK).min(buf.len()); + let chunk = &mut buf[start..end]; + let n = chunk.len(); + for i in 0..n { + let x = chunk[i]; + scratch[i] = x; + chunk[i] = SQRT_2_OVER_PI * (x + COEF * x * x * x); + } + super::avx512_tanh_f32::run(chunk, ()); + for i in 0..n { + chunk[i] = 0.5 * scratch[i] * (1.0 + chunk[i]); + } + start = end; + } + } +); + +#[cfg(test)] +pub mod test_x86_64_avx512_gelu_f32_16n { + use super::*; + gelu_frame_tests!( + is_x86_feature_detected!("avx512f"), + f32, + x86_64_avx512_gelu_f32_16n + ); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/act_f16.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/act_f16.rs new file mode 100644 index 0000000..3ba518a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/act_f16.rs @@ -0,0 +1,316 @@ +// AVX-512 f16 element-wise activations. Each kernel chunks f16 -> 64-byte-aligned +// f32 scratch via vcvtph2ps (cvt_f16_to_f32 below), runs the matching f32 +// AVX-512 kernel (or the avx512_sigmoid_f32 / avx512_tanh_f32 wrappers from +// x86_64_fma.rs), and converts back to f16 via vcvtps2ph (cvt_f32_to_f16). +// Conversion is driven through std::arch intrinsics directly because the +// scalar f16::to_f32 / f16::from_f32 loops are not autovectorized by +// rustc + LLVM (branches / call overhead in the half crate's methods), +// which leaves a naive port stuck around 7 Melem/s. +// +// The f32 AVX-512 activation kernels assume 64-byte aligned input (alignment +// bytes = nr * 4 for nr >= 16). The local scratch is wrapped in +// #[repr(C, align(64))] so the contained [f32; 256] sits at a 64-byte boundary. +// +// Validated against the generic f16 reference (HHardSwish8 / HLeakyRelu8 / +// HSigmoid8 / HTanh8 / HSiLU8 / HGelu8) via the existing *_frame_tests! +// macros at SuperApproximate tolerance, which covers the precision delta +// between scalar f16 arithmetic and f32-internal computation. + +use tract_data::internal::f16; + +#[repr(C, align(64))] +struct AlignedScratch([f32; 256]); + +impl AlignedScratch { + fn new() -> Self { + Self([0f32; 256]) + } +} + +const CHUNK: usize = 256; + +// Vectorized f16 <-> f32 helpers using vcvtph2ps / vcvtps2ph. Rustc + LLVM +// do NOT autovectorize the scalar `.to_f32()` loop (the half crate's method +// has branches / function-call overhead), so we drive the conversion with +// intrinsics directly. Both helpers process 16 lanes per iteration; the tail +// (which only fires for the 1-15 leftover lanes inside a CHUNK = 256 batch) +// falls back to scalar. +#[target_feature(enable = "avx512f")] +unsafe fn cvt_f16_to_f32(src: &[f16], dst: &mut [f32]) { + use core::arch::x86_64::*; + let n = src.len(); + debug_assert!(dst.len() >= n); + let chunks = n / 16; + unsafe { + for k in 0..chunks { + let m = _mm256_loadu_si256(src.as_ptr().add(k * 16) as *const __m256i); + let z = _mm512_cvtph_ps(m); + _mm512_storeu_ps(dst.as_mut_ptr().add(k * 16), z); + } + for k in (chunks * 16)..n { + *dst.get_unchecked_mut(k) = src.get_unchecked(k).to_f32(); + } + } +} + +#[target_feature(enable = "avx512f")] +unsafe fn cvt_f32_to_f16(src: &[f32], dst: &mut [f16]) { + use core::arch::x86_64::*; + let n = src.len(); + debug_assert!(dst.len() >= n); + let chunks = n / 16; + unsafe { + for k in 0..chunks { + let z = _mm512_loadu_ps(src.as_ptr().add(k * 16)); + // _MM_FROUND_TO_NEAREST_INT == 0 (round-to-nearest-even, matches f16::from_f32) + let m = _mm512_cvtps_ph::<0>(z); + _mm256_storeu_si256(dst.as_mut_ptr().add(k * 16) as *mut __m256i, m); + } + for k in (chunks * 16)..n { + *dst.get_unchecked_mut(k) = f16::from_f32(*src.get_unchecked(k)); + } + } +} + +// hardswish_f16 +ew_impl_wrap!( + f16, + x86_64_avx512_hardswish_f16_64n, + 64, + 32, + (), + #[inline(never)] + fn run(buf: &mut [f16], _: ()) { + debug_assert!(buf.len() % Self::nr() == 0); + debug_assert!(buf.as_ptr() as usize % Self::alignment_bytes() == 0); + if buf.is_empty() { + return; + } + let mut scratch = AlignedScratch::new(); + let s = &mut scratch.0; + let mut i = 0; + while i < buf.len() { + let n = (CHUNK).min(buf.len() - i); + unsafe { cvt_f16_to_f32(&buf[i..i + n], &mut s[..n]) }; + super::act::x86_64_avx512_hardswish_f32_64n::run(&mut s[..n], ()); + unsafe { cvt_f32_to_f16(&s[..n], &mut buf[i..i + n]) }; + i += n; + } + } +); + +#[cfg(test)] +pub mod test_x86_64_avx512_hardswish_f16_64n { + use super::*; + hardswish_frame_tests!( + is_x86_feature_detected!("avx512f"), + f16, + x86_64_avx512_hardswish_f16_64n + ); +} + +// leaky_relu_f16 (parameter: alpha as f16) +ew_impl_wrap!( + f16, + x86_64_avx512_leaky_relu_f16_64n, + 64, + 32, + f16, + #[inline(never)] + fn run(buf: &mut [f16], alpha: f16) { + debug_assert!(buf.len() % Self::nr() == 0); + debug_assert!(buf.as_ptr() as usize % Self::alignment_bytes() == 0); + if buf.is_empty() { + return; + } + let alpha_f32 = alpha.to_f32(); + let mut scratch = AlignedScratch::new(); + let s = &mut scratch.0; + let mut i = 0; + while i < buf.len() { + let n = (CHUNK).min(buf.len() - i); + unsafe { cvt_f16_to_f32(&buf[i..i + n], &mut s[..n]) }; + super::act::x86_64_avx512_leaky_relu_f32_64n::run(&mut s[..n], alpha_f32); + unsafe { cvt_f32_to_f16(&s[..n], &mut buf[i..i + n]) }; + i += n; + } + } +); + +#[cfg(test)] +pub mod test_x86_64_avx512_leaky_relu_f16_64n { + use super::*; + leaky_relu_frame_tests!( + is_x86_feature_detected!("avx512f"), + f16, + x86_64_avx512_leaky_relu_f16_64n + ); +} + +// sigmoid_f16 (calls the avx512_sigmoid_f32 wrapper from x86_64_fma.rs; +// its nr() is 16 so CHUNK=256 is always a clean multiple) +ew_impl_wrap!( + f16, + x86_64_avx512_sigmoid_f16_16n, + 16, + 16, + (), + #[inline(never)] + fn run(buf: &mut [f16], _: ()) { + debug_assert!(buf.len() % Self::nr() == 0); + debug_assert!(buf.as_ptr() as usize % Self::alignment_bytes() == 0); + if buf.is_empty() { + return; + } + let mut scratch = AlignedScratch::new(); + let s = &mut scratch.0; + let mut i = 0; + while i < buf.len() { + let n = (CHUNK).min(buf.len() - i); + unsafe { cvt_f16_to_f32(&buf[i..i + n], &mut s[..n]) }; + super::avx512_sigmoid_f32::run(&mut s[..n], ()); + unsafe { cvt_f32_to_f16(&s[..n], &mut buf[i..i + n]) }; + i += n; + } + } +); + +#[cfg(test)] +pub mod test_x86_64_avx512_sigmoid_f16_16n { + use super::*; + sigmoid_frame_tests!( + is_x86_feature_detected!("avx512f"), + f16, + x86_64_avx512_sigmoid_f16_16n + ); +} + +// tanh_f16 +ew_impl_wrap!( + f16, + x86_64_avx512_tanh_f16_16n, + 16, + 16, + (), + #[inline(never)] + fn run(buf: &mut [f16], _: ()) { + debug_assert!(buf.len() % Self::nr() == 0); + debug_assert!(buf.as_ptr() as usize % Self::alignment_bytes() == 0); + if buf.is_empty() { + return; + } + let mut scratch = AlignedScratch::new(); + let s = &mut scratch.0; + let mut i = 0; + while i < buf.len() { + let n = (CHUNK).min(buf.len() - i); + unsafe { cvt_f16_to_f32(&buf[i..i + n], &mut s[..n]) }; + super::avx512_tanh_f32::run(&mut s[..n], ()); + unsafe { cvt_f32_to_f16(&s[..n], &mut buf[i..i + n]) }; + i += n; + } + } +); + +#[cfg(test)] +pub mod test_x86_64_avx512_tanh_f16_16n { + use super::*; + tanh_frame_tests!( + is_x86_feature_detected!("avx512f"), + f16, + x86_64_avx512_tanh_f16_16n + ); +} + +// silu_f16: x * sigmoid(x). Mirror the f32 silu pattern: save the input +// (in f32), run sigmoid in place on the scratch, then multiply back. +ew_impl_wrap!( + f16, + x86_64_avx512_silu_f16_16n, + 16, + 16, + (), + #[inline(never)] + fn run(buf: &mut [f16], _: ()) { + debug_assert!(buf.len() % Self::nr() == 0); + debug_assert!(buf.as_ptr() as usize % Self::alignment_bytes() == 0); + if buf.is_empty() { + return; + } + let mut work = AlignedScratch::new(); + let mut save = AlignedScratch::new(); + let w = &mut work.0; + let v = &mut save.0; + let mut i = 0; + while i < buf.len() { + let n = (CHUNK).min(buf.len() - i); + unsafe { cvt_f16_to_f32(&buf[i..i + n], &mut w[..n]) }; + v[..n].copy_from_slice(&w[..n]); + super::avx512_sigmoid_f32::run(&mut w[..n], ()); + for j in 0..n { + w[j] *= v[j]; + } + unsafe { cvt_f32_to_f16(&w[..n], &mut buf[i..i + n]) }; + i += n; + } + } +); + +#[cfg(test)] +pub mod test_x86_64_avx512_silu_f16_16n { + use super::*; + silu_frame_tests!( + is_x86_feature_detected!("avx512f"), + f16, + x86_64_avx512_silu_f16_16n + ); +} + +// Tanh-form GELU (matches tract's GeluApproximate, pow=3, see act.rs gelu_f32): +// gelu(x) = 0.5 * x * (1 + tanh(sqrt(2/pi) * (x + 0.044715 * x^3))) +ew_impl_wrap!( + f16, + x86_64_avx512_gelu_f16_16n, + 16, + 16, + (), + #[inline(never)] + fn run(buf: &mut [f16], _: ()) { + debug_assert!(buf.len() % Self::nr() == 0); + debug_assert!(buf.as_ptr() as usize % Self::alignment_bytes() == 0); + if buf.is_empty() { + return; + } + const SQRT_2_OVER_PI: f32 = 0.7978845608028654; + const COEF: f32 = 0.044715; + let mut work = AlignedScratch::new(); + let mut save = AlignedScratch::new(); + let w = &mut work.0; + let v = &mut save.0; + let mut i = 0; + while i < buf.len() { + let n = (CHUNK).min(buf.len() - i); + unsafe { cvt_f16_to_f32(&buf[i..i + n], &mut v[..n]) }; + for j in 0..n { + let x = v[j]; + w[j] = SQRT_2_OVER_PI * (x + COEF * x * x * x); + } + super::avx512_tanh_f32::run(&mut w[..n], ()); + for j in 0..n { + w[j] = 0.5 * v[j] * (1.0 + w[j]); + } + unsafe { cvt_f32_to_f16(&w[..n], &mut buf[i..i + n]) }; + i += n; + } + } +); + +#[cfg(test)] +pub mod test_x86_64_avx512_gelu_f16_16n { + use super::*; + gelu_frame_tests!( + is_x86_feature_detected!("avx512f"), + f16, + x86_64_avx512_gelu_f16_16n + ); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/act_f16_fp16.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/act_f16_fp16.rs new file mode 100644 index 0000000..4a40002 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/act_f16_fp16.rs @@ -0,0 +1,203 @@ +// AVX-512_FP16 native f16 element-wise activations. +// +// Sapphire Rapids (and later Intel) added the AVX-512 FP16 ISA: zmm-wide +// arithmetic on f16 directly (`vmulph`, `vfmadd*ph`, `vmaxph`, `vminph`, +// `vaddph`, `vsubph`, etc.). 32 f16 lanes per zmm — double the parallelism of +// the f32-roundtrip kernels in `act_f16.rs`, and zero conversion at the IO +// boundary. +// +// The kernels here mirror the algorithm of the f32 versions in `act.rs` and +// the f32-roundtrip f16 versions in `act_f16.rs`. Polynomials are evaluated +// directly in f16, accepting the lower mantissa precision (11 bits vs f32's +// 24) — the resulting tolerance fits inside the f16 activation tests' +// SuperApproximate band. +// +// Gated on `is_x86_feature_detected!("avx512fp16")` (the actual gating happens +// in `plug_avx512fp16` over in `x86_64_fma.rs`). Pre-FP16 AVX-512 hosts +// (Skylake-X, Cascade Lake, Ice Lake server prior to fp16 extension) keep +// using `act_f16.rs`'s f32-roundtrip versions. + +use tract_data::internal::f16; + +const FP16_TARGETS: &str = "avx512f,avx512fp16,avx512bw"; + +// hardswish(x) = x * clamp(x + 3, 0, 6) * (1/6). +// 128 f16 per iter (4 zmm × 32 lanes), 256 bytes / iter — same memory throughput +// as the f32 kernel's 64 f32 / iter. +ew_impl_wrap!( + f16, + x86_64_avx512fp16_hardswish_f16_128n, + 128, + 32, + (), + #[inline(never)] + fn run(buf: &mut [f16], _: ()) { + debug_assert!(buf.len() % Self::nr() == 0); + debug_assert!(buf.as_ptr() as usize % Self::alignment_bytes() == 0); + if buf.is_empty() { + return; + } + unsafe { hardswish_f16_run(buf) } + } +); + +#[target_feature(enable = "avx512f,avx512fp16,avx512bw")] +unsafe fn hardswish_f16_run(buf: &mut [f16]) { + let len = buf.len(); + let ptr = buf.as_ptr() as *mut u8; + let three = f16::from_f32(3.0).to_bits(); + let six = f16::from_f32(6.0).to_bits(); + let recip6 = f16::from_f32(1.0 / 6.0).to_bits(); + unsafe { + std::arch::asm!(" + vpbroadcastw zmm0, eax // 3.0 + vpbroadcastw zmm1, ecx // 6.0 + vpbroadcastw zmm2, edx // 1/6 + vpxord zmm3, zmm3, zmm3 // 0.0 + 2: + vmovdqa64 zmm4, [{ptr}] + vmovdqa64 zmm5, [{ptr} + 64] + vmovdqa64 zmm6, [{ptr} + 128] + vmovdqa64 zmm7, [{ptr} + 192] + + vaddph zmm8, zmm4, zmm0 + vaddph zmm9, zmm5, zmm0 + vaddph zmm10, zmm6, zmm0 + vaddph zmm11, zmm7, zmm0 + + vminph zmm8, zmm8, zmm1 + vminph zmm9, zmm9, zmm1 + vminph zmm10, zmm10, zmm1 + vminph zmm11, zmm11, zmm1 + + vmaxph zmm8, zmm8, zmm3 + vmaxph zmm9, zmm9, zmm3 + vmaxph zmm10, zmm10, zmm3 + vmaxph zmm11, zmm11, zmm3 + + vmulph zmm8, zmm8, zmm4 + vmulph zmm9, zmm9, zmm5 + vmulph zmm10, zmm10, zmm6 + vmulph zmm11, zmm11, zmm7 + + vmulph zmm8, zmm8, zmm2 + vmulph zmm9, zmm9, zmm2 + vmulph zmm10, zmm10, zmm2 + vmulph zmm11, zmm11, zmm2 + + vmovdqa64 [{ptr}], zmm8 + vmovdqa64 [{ptr} + 64], zmm9 + vmovdqa64 [{ptr} + 128], zmm10 + vmovdqa64 [{ptr} + 192], zmm11 + + add {ptr}, 256 + sub {len}, 128 + jnz 2b + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + in("eax") three as u32, + in("ecx") six as u32, + in("edx") recip6 as u32, + out("zmm0") _, out("zmm1") _, out("zmm2") _, out("zmm3") _, + out("zmm4") _, out("zmm5") _, out("zmm6") _, out("zmm7") _, + out("zmm8") _, out("zmm9") _, out("zmm10") _, out("zmm11") _, + ); + } +} + +// leaky_relu(x, alpha) = x if x >= 0 else alpha*x +// For 0 <= alpha <= 1: leaky_relu(x, alpha) = max(x, alpha*x). For the typical +// alpha values used (0.01, 0.1, 0.2) this is exact. +// +// NOTE: This native fp16 version benched ~38% SLOWER than the f32-roundtrip +// version on Sapphire Rapids (9.44 Gelem/s f32-roundtrip vs 5.85 Gelem/s +// native, n=1024, single-thread). The two compute ops per element (vmulph + +// vmaxph) appear not to saturate Sapphire Rapids' FP16 execution port the +// same way f32 mul/max saturate the FP32 ports. The kernel is correct (passes +// proptest against the f16 reference) but is NOT plugged in — see the +// `plug_avx512fp16` comment in `x86_64_fma.rs`. Kept here in case a different +// AVX-512_FP16 uarch (Granite Rapids etc.) flips the comparison. +ew_impl_wrap!( + f16, + x86_64_avx512fp16_leaky_relu_f16_128n, + 128, + 32, + f16, + #[inline(never)] + fn run(buf: &mut [f16], alpha: f16) { + debug_assert!(buf.len() % Self::nr() == 0); + debug_assert!(buf.as_ptr() as usize % Self::alignment_bytes() == 0); + if buf.is_empty() { + return; + } + unsafe { leaky_relu_f16_run(buf, alpha) } + } +); + +#[target_feature(enable = "avx512f,avx512fp16,avx512bw")] +unsafe fn leaky_relu_f16_run(buf: &mut [f16], alpha: f16) { + let len = buf.len(); + let ptr = buf.as_ptr() as *mut u8; + let alpha_bits = alpha.to_bits(); + unsafe { + std::arch::asm!(" + vpbroadcastw zmm0, eax // alpha + 2: + vmovdqa64 zmm4, [{ptr}] + vmovdqa64 zmm5, [{ptr} + 64] + vmovdqa64 zmm6, [{ptr} + 128] + vmovdqa64 zmm7, [{ptr} + 192] + + vmulph zmm8, zmm4, zmm0 + vmulph zmm9, zmm5, zmm0 + vmulph zmm10, zmm6, zmm0 + vmulph zmm11, zmm7, zmm0 + + vmaxph zmm8, zmm8, zmm4 + vmaxph zmm9, zmm9, zmm5 + vmaxph zmm10, zmm10, zmm6 + vmaxph zmm11, zmm11, zmm7 + + vmovdqa64 [{ptr}], zmm8 + vmovdqa64 [{ptr} + 64], zmm9 + vmovdqa64 [{ptr} + 128], zmm10 + vmovdqa64 [{ptr} + 192], zmm11 + + add {ptr}, 256 + sub {len}, 128 + jnz 2b + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + in("eax") alpha_bits as u32, + out("zmm0") _, + out("zmm4") _, out("zmm5") _, out("zmm6") _, out("zmm7") _, + out("zmm8") _, out("zmm9") _, out("zmm10") _, out("zmm11") _, + ); + } +} + +#[cfg(test)] +pub mod test_x86_64_avx512fp16_hardswish { + use super::*; + crate::hardswish_frame_tests!( + is_x86_feature_detected!("avx512fp16"), + f16, + x86_64_avx512fp16_hardswish_f16_128n + ); +} + +#[cfg(test)] +pub mod test_x86_64_avx512fp16_leaky_relu { + use super::*; + crate::leaky_relu_frame_tests!( + is_x86_feature_detected!("avx512fp16"), + f16, + x86_64_avx512fp16_leaky_relu_f16_128n + ); +} + +// Suppress unused-const lint until we expand to more kernels. +#[allow(dead_code)] +const _UNUSED: &str = FP16_TARGETS; diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/by_scalar.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/by_scalar.rs new file mode 100644 index 0000000..dacef24 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/by_scalar.rs @@ -0,0 +1,56 @@ +ew_impl_wrap!( + f32, + x86_64_avx_f32_mul_by_scalar_32n, + 32, + 8, + f32, + fn run(x: &mut [f32], s: f32) { + debug_assert!(x.len() % Self::nr() == 0); + debug_assert!(x.as_ptr() as usize % Self::alignment_bytes() == 0); + unsafe { x86_64_avx_f32_mul_by_scalar_32n_run(x, s) } + } +); + +#[target_feature(enable = "avx")] +unsafe fn x86_64_avx_f32_mul_by_scalar_32n_run(buf: &mut [f32], scalar: f32) { + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + std::arch::asm!(" + vbroadcastss ymm0, xmm0 + 2: + vmovaps ymm4, [{ptr}] + vmovaps ymm5, [{ptr} + 32] + vmovaps ymm6, [{ptr} + 64] + vmovaps ymm7, [{ptr} + 96] + vmulps ymm4, ymm4, ymm0 + vmulps ymm5, ymm5, ymm0 + vmulps ymm6, ymm6, ymm0 + vmulps ymm7, ymm7, ymm0 + vmovaps [{ptr}], ymm4 + vmovaps [{ptr} + 32], ymm5 + vmovaps [{ptr} + 64], ymm6 + vmovaps [{ptr} + 96], ymm7 + add {ptr}, 128 + sub {len}, 32 + jnz 2b + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + in("xmm0") scalar, + out("ymm4") _, out("ymm5") _, out("ymm6") _, out("ymm7") _ + ); + } +} + +#[cfg(test)] +#[macro_use] +pub mod test_x86_64_avx_f32_mul_by_scalar_32n { + use super::*; + by_scalar_frame_tests!( + is_x86_feature_detected!("avx2"), + f32, + x86_64_avx_f32_mul_by_scalar_32n, + |a, b| a * b + ); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/erf.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/erf.rs new file mode 100644 index 0000000..1f1d2ab --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/erf.rs @@ -0,0 +1,208 @@ +// AVX-512 (zmm, 16-wide) error function kernel. Mirrors generic/erf.rs::serf +// (Abramowitz & Stegun 7.1.26 six-coefficient polynomial) but runs the +// polynomial via FMA chains over 4 zmm registers per iteration (64 lanes per +// loop step). Validated against the generic scalar reference via +// erf_frame_tests! at SuperApproximate tolerance. +// +// Algorithm (per lane): +// signum = sign(x); abs = |x| +// y = a6 +// y = y*abs + a5 (Horner FMA) +// y = y*abs + a4 +// y = y*abs + a3 +// y = y*abs + a2 +// y = y*abs + a1 +// y = y * abs (final factor of abs) +// y = y + 1 +// y = y^16 (4 sequential squares) +// y = 1 / y (vdivps, full IEEE precision) +// y = 1 - y +// result = copysign(y, x) + +ew_impl_wrap!( + f32, + x86_64_avx512_erf_f32_64n, + 64, + 16, + (), + #[inline(never)] + fn run(buf: &mut [f32], _: ()) { + debug_assert!(buf.len() % Self::nr() == 0); + debug_assert!(buf.as_ptr() as usize % Self::alignment_bytes() == 0); + if buf.is_empty() { + return; + } + unsafe { x86_64_avx512_erf_f32_64n_run(buf) } + } +); + +#[target_feature(enable = "avx512f")] +unsafe fn x86_64_avx512_erf_f32_64n_run(buf: &mut [f32]) { + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + const A1: f32 = 0.0705230784; + const A2: f32 = 0.0422820123; + const A3: f32 = 0.0092705272; + const A4: f32 = 0.0001520143; + const A5: f32 = 0.0002765672; + const A6: f32 = 0.0000430638; + // 0x7fffffff: positive-finite mask (clears sign bit). As f32 bits, this + // is NaN; we never use it as a numeric value — only as a bit mask via vandps. + const ABS_MASK: f32 = f32::from_bits(0x7fffffff); + const SIGN_MASK: f32 = f32::from_bits(0x80000000); + std::arch::asm!(" + // broadcast constants (xmmN -> zmmN, broadcast across all 16 lanes) + vbroadcastss zmm0, xmm0 // a1 + vbroadcastss zmm1, xmm1 // a2 + vbroadcastss zmm2, xmm2 // a3 + vbroadcastss zmm3, xmm3 // a4 + vbroadcastss zmm4, xmm4 // a5 + vbroadcastss zmm5, xmm5 // a6 + vbroadcastss zmm6, xmm6 // 1.0 + vbroadcastss zmm7, xmm7 // abs mask (0x7fffffff) + vbroadcastss zmm8, xmm8 // sign mask (0x80000000) + 2: + // load 4 zmm of input + vmovaps zmm9, [{ptr}] + vmovaps zmm10, [{ptr} + 64] + vmovaps zmm11, [{ptr} + 128] + vmovaps zmm12, [{ptr} + 192] + + // sign[i] = x[i] & SIGN_MASK (keeps only the sign bit) + vandps zmm13, zmm9, zmm8 + vandps zmm14, zmm10, zmm8 + vandps zmm15, zmm11, zmm8 + vandps zmm16, zmm12, zmm8 + + // abs[i] = x[i] & ABS_MASK (clears the sign bit) + vandps zmm9, zmm9, zmm7 + vandps zmm10, zmm10, zmm7 + vandps zmm11, zmm11, zmm7 + vandps zmm12, zmm12, zmm7 + + // y = a6 (in zmm17..20, 4 independent channels) + vmovaps zmm17, zmm5 + vmovaps zmm18, zmm5 + vmovaps zmm19, zmm5 + vmovaps zmm20, zmm5 + + // y = y*abs + a5 + vfmadd213ps zmm17, zmm9, zmm4 + vfmadd213ps zmm18, zmm10, zmm4 + vfmadd213ps zmm19, zmm11, zmm4 + vfmadd213ps zmm20, zmm12, zmm4 + + // y = y*abs + a4 + vfmadd213ps zmm17, zmm9, zmm3 + vfmadd213ps zmm18, zmm10, zmm3 + vfmadd213ps zmm19, zmm11, zmm3 + vfmadd213ps zmm20, zmm12, zmm3 + + // y = y*abs + a3 + vfmadd213ps zmm17, zmm9, zmm2 + vfmadd213ps zmm18, zmm10, zmm2 + vfmadd213ps zmm19, zmm11, zmm2 + vfmadd213ps zmm20, zmm12, zmm2 + + // y = y*abs + a2 + vfmadd213ps zmm17, zmm9, zmm1 + vfmadd213ps zmm18, zmm10, zmm1 + vfmadd213ps zmm19, zmm11, zmm1 + vfmadd213ps zmm20, zmm12, zmm1 + + // y = y*abs + a1 + vfmadd213ps zmm17, zmm9, zmm0 + vfmadd213ps zmm18, zmm10, zmm0 + vfmadd213ps zmm19, zmm11, zmm0 + vfmadd213ps zmm20, zmm12, zmm0 + + // y = y * abs (final factor) + vmulps zmm17, zmm17, zmm9 + vmulps zmm18, zmm18, zmm10 + vmulps zmm19, zmm19, zmm11 + vmulps zmm20, zmm20, zmm12 + + // y = y + 1 + vaddps zmm17, zmm17, zmm6 + vaddps zmm18, zmm18, zmm6 + vaddps zmm19, zmm19, zmm6 + vaddps zmm20, zmm20, zmm6 + + // y^16: square 4 times + vmulps zmm17, zmm17, zmm17 + vmulps zmm18, zmm18, zmm18 + vmulps zmm19, zmm19, zmm19 + vmulps zmm20, zmm20, zmm20 + + vmulps zmm17, zmm17, zmm17 + vmulps zmm18, zmm18, zmm18 + vmulps zmm19, zmm19, zmm19 + vmulps zmm20, zmm20, zmm20 + + vmulps zmm17, zmm17, zmm17 + vmulps zmm18, zmm18, zmm18 + vmulps zmm19, zmm19, zmm19 + vmulps zmm20, zmm20, zmm20 + + vmulps zmm17, zmm17, zmm17 + vmulps zmm18, zmm18, zmm18 + vmulps zmm19, zmm19, zmm19 + vmulps zmm20, zmm20, zmm20 + + // y = 1 / y (full-precision reciprocal, matches generic .recip()) + vdivps zmm21, zmm6, zmm17 + vdivps zmm22, zmm6, zmm18 + vdivps zmm23, zmm6, zmm19 + vdivps zmm24, zmm6, zmm20 + + // y = 1 - y + vsubps zmm21, zmm6, zmm21 + vsubps zmm22, zmm6, zmm22 + vsubps zmm23, zmm6, zmm23 + vsubps zmm24, zmm6, zmm24 + + // copysign: stamp the original sign bit onto the (positive) result + vorps zmm21, zmm21, zmm13 + vorps zmm22, zmm22, zmm14 + vorps zmm23, zmm23, zmm15 + vorps zmm24, zmm24, zmm16 + + // store + vmovaps [{ptr}], zmm21 + vmovaps [{ptr} + 64], zmm22 + vmovaps [{ptr} + 128], zmm23 + vmovaps [{ptr} + 192], zmm24 + + add {ptr}, 256 + sub {len}, 64 + jnz 2b + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + inout("xmm0") A1 => _, + inout("xmm1") A2 => _, + inout("xmm2") A3 => _, + inout("xmm3") A4 => _, + inout("xmm4") A5 => _, + inout("xmm5") A6 => _, + inout("xmm6") 1f32 => _, + inout("xmm7") ABS_MASK => _, + inout("xmm8") SIGN_MASK => _, + out("zmm9") _, out("zmm10") _, out("zmm11") _, out("zmm12") _, + out("zmm13") _, out("zmm14") _, out("zmm15") _, out("zmm16") _, + out("zmm17") _, out("zmm18") _, out("zmm19") _, out("zmm20") _, + out("zmm21") _, out("zmm22") _, out("zmm23") _, out("zmm24") _, + ); + } +} + +#[cfg(test)] +pub mod test_x86_64_avx512_erf_f32_64n { + use super::*; + crate::erf_frame_tests!( + is_x86_feature_detected!("avx512f"), + f32, + x86_64_avx512_erf_f32_64n + ); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/intel.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/intel.rs new file mode 100644 index 0000000..a713103 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/intel.rs @@ -0,0 +1,5 @@ +use crate::frame::mmm::cost_model::CostModel; +#[allow(dead_code)] +pub fn models() -> Vec<(&'static str, CostModel<'static>)> { + vec![] +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/max.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/max.rs new file mode 100644 index 0000000..e48ca35 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/max.rs @@ -0,0 +1,144 @@ +reduce_impl_wrap!( + f32, + x86_64_fma_max_f32_32n, + 32, + 8, + (), + f32::MIN, + #[inline(never)] + fn run(buf: &[f32], _: ()) -> f32 { + assert!(buf.len() % 32 == 0); + assert!(buf.len() > 0); + unsafe { x86_64_fma_max_f32_32n_run(buf) } + }, + #[inline(never)] + fn reduce_two(a: f32, b: f32) -> f32 { + a.max(b) + } +); + +#[target_feature(enable = "avx")] +unsafe fn x86_64_fma_max_f32_32n_run(buf: &[f32]) -> f32 { + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + let mut acc = f32::MIN; + std::arch::asm!(" + vbroadcastss ymm0, xmm0 + vmovaps ymm1, ymm0 + vmovaps ymm2, ymm0 + vmovaps ymm3, ymm0 + 2: + vmovaps ymm4, [{ptr}] + vmovaps ymm5, [{ptr} + 32] + vmovaps ymm6, [{ptr} + 64] + vmovaps ymm7, [{ptr} + 96] + vmaxps ymm0, ymm0, ymm4 + vmaxps ymm1, ymm1, ymm5 + vmaxps ymm2, ymm2, ymm6 + vmaxps ymm3, ymm3, ymm7 + add {ptr}, 128 + sub {len}, 32 + jnz 2b + vmaxps ymm0, ymm0, ymm1 + vmaxps ymm2, ymm2, ymm3 + vmaxps ymm0, ymm0, ymm2 + vperm2f128 ymm1, ymm0, ymm0, 1 // copy second half (4xf32) of ymm0 to ymm1 + vmaxps xmm0, xmm0, xmm1 // xmm0 contains 4 values to max + vpermilps xmm1, xmm0, 2 + (3 << 2) // second 2x32 bit half moved to top + vmaxps xmm0, xmm0, xmm1 // xmm0 containes 2 values + vpermilps xmm1, xmm0, 1 // second f32 to top + vmaxps xmm0, xmm0, xmm1 + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + inout("ymm0") acc, + out("ymm1") _, out("ymm2") _, out("ymm3") _, + out("ymm4") _, out("ymm5") _, out("ymm6") _, out("ymm7") _ + ); + acc + } +} + +#[cfg(test)] +mod test_x86_64_fma_max_f32_32n { + use super::*; + crate::max_frame_tests!( + is_x86_feature_detected!("avx2"), + f32, + x86_64_fma_max_f32_32n + ); +} + +// AVX-512 version: processes 64 f32 per loop iteration (4 zmm registers of 16 +// lanes each). Runtime-gated on avx512f (see x86_64_fma.rs::plug_avx512f); on +// non-AVX512 CPUs this kernel is never registered and the FMA path above stays +// in use. nr=64, 64-byte (16xf32) alignment. +reduce_impl_wrap!( + f32, + x86_64_avx512_max_f32_64n, + 64, + 16, + (), + f32::MIN, + #[inline(never)] + fn run(buf: &[f32], _: ()) -> f32 { + assert!(buf.len() % 64 == 0); + assert!(buf.len() > 0); + unsafe { x86_64_avx512_max_f32_64n_run(buf) } + }, + #[inline(never)] + fn reduce_two(a: f32, b: f32) -> f32 { + a.max(b) + } +); + +#[target_feature(enable = "avx512f")] +unsafe fn x86_64_avx512_max_f32_64n_run(buf: &[f32]) -> f32 { + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + let mut acc = f32::MIN; + std::arch::asm!(" + vbroadcastss zmm0, xmm0 + vmovaps zmm1, zmm0 + vmovaps zmm2, zmm0 + vmovaps zmm3, zmm0 + 2: + vmaxps zmm0, zmm0, [{ptr}] + vmaxps zmm1, zmm1, [{ptr} + 64] + vmaxps zmm2, zmm2, [{ptr} + 128] + vmaxps zmm3, zmm3, [{ptr} + 192] + add {ptr}, 256 + sub {len}, 64 + jnz 2b + vmaxps zmm0, zmm0, zmm1 + vmaxps zmm2, zmm2, zmm3 + vmaxps zmm0, zmm0, zmm2 // zmm0 holds 16 partial maxima + vextractf64x4 ymm1, zmm0, 1 // upper 256 bits (8xf32) of zmm0 -> ymm1 (avx512f) + vmaxps ymm0, ymm0, ymm1 // ymm0 holds 8 values + vextractf128 xmm1, ymm0, 1 // upper 4xf32 -> xmm1 + vmaxps xmm0, xmm0, xmm1 // xmm0 holds 4 values + vpermilps xmm1, xmm0, 2 + (3 << 2) // second 2x32 bit half moved to top + vmaxps xmm0, xmm0, xmm1 // xmm0 holds 2 values + vpermilps xmm1, xmm0, 1 // second f32 to top + vmaxps xmm0, xmm0, xmm1 + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + inout("zmm0") acc, + out("zmm1") _, out("zmm2") _, out("zmm3") _, + ); + acc + } +} + +#[cfg(test)] +mod test_x86_64_avx512_max_f32_64n { + use super::*; + crate::max_frame_tests!( + is_x86_feature_detected!("avx512f"), + f32, + x86_64_avx512_max_f32_64n + ); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/mmm.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/mmm.rs new file mode 100644 index 0000000..4d75beb --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/mmm.rs @@ -0,0 +1,398 @@ +use crate::Ops; +use crate::block_quant::*; +use crate::mmm::ImplementationQuality::ManuallyOptimized; +use crate::mmm::MatMatMul; +use crate::pack::{PackedFormat, PackedI8K4}; + +use super::*; + +/// One candidate kernel in a dispatcher's pool, with its tile geometry +/// and a relative-throughput scale (1.0 = baseline, used to break +/// near-ties between kernels with similar tile waste). +#[derive(Clone, Copy)] +struct KernelChoice { + mr: usize, + nr: usize, + scale: f32, + ctor: fn() -> Box, +} + +/// Fraction of the M-or-N axis covered by useful work after rounding up +/// to the kernel's tile size. 1.0 = exact fit; smaller is worse. +/// Empty axis (d == 0) is treated as "no waste" — no work to misallocate. +fn tile_util(d: usize, tile: usize) -> f32 { + if d == 0 { + return 1.0; + } + let batches = d.div_ceil(tile); + d as f32 / (batches * tile) as f32 +} + +/// Pick the kernel that maximises `scale * m_util * n_util`. Ties are +/// broken first in favour of fewer total tile passes (less loop +/// overhead), then in favour of larger `nr` (more K-loop amortisation +/// per inner iteration). An unknown M or N is treated as +/// "large enough" — its utilisation contribution is 1.0. +fn pick_mmm(candidates: &[KernelChoice], m: Option, n: Option) -> Box { + let key = |c: &KernelChoice| -> (f32, i32, i32) { + let m_u = m.map(|m| tile_util(m, c.mr)).unwrap_or(1.0); + let n_u = n.map(|n| tile_util(n, c.nr)).unwrap_or(1.0); + let m_b = m.map(|m| m.div_ceil(c.mr)).unwrap_or(1) as i32; + let n_b = n.map(|n| n.div_ceil(c.nr)).unwrap_or(1) as i32; + (c.scale * m_u * n_u, -(m_b * n_b), c.nr as i32) + }; + let best = candidates + .iter() + .max_by(|a, b| key(a).partial_cmp(&key(b)).unwrap()) + .expect("non-empty kernel pool"); + (best.ctor)() +} + +MMMExternKernel!(fma_mmm_f32_8x8 (8, 8)@(256,4) where(FMA) quality(ManuallyOptimized)); +MMMExternKernel!(fma_mmm_f32_16x6(16,6)@(256,4) where(FMA) quality(ManuallyOptimized)); +MMMExternKernel!(fma_mmm_f32_16x5(16,5)@(256,4) where(FMA) quality(ManuallyOptimized)); +MMMExternKernel!(fma_mmm_f32_24x4(24,4)@(256,4) where(FMA) quality(ManuallyOptimized)); +MMMExternKernel!(fma_mmm_f32_40x2(40,2)@(256,4) where(FMA) quality(ManuallyOptimized)); +MMMExternKernel!(fma_mmm_f32_64x1(64,1)@(256,4) where(FMA) quality(ManuallyOptimized)); + +pub fn pq40_r32() -> PackedBlockQuantFormat { + PackedBlockQuantFormat::new(&Q4_0, 32, 16, false) +} +MMMExternKernel! {fma_mmm_f32_32x1(32,1)@(256,4) where(FMA) + packing[1] = q40f32 => |k| k.with_packing_a(pq40_r32()); + packing[2] = q40f16 => |k| k.with_packing(pq40_r32(), f16::packing(1)); + packing[3] = f16f16 => |k| k.with_packing(f16::packing(32), f16::packing(1)); + packing[4] = f16f32 => |k| k.with_packing(f16::packing(32), f32::packing(1)); + packing[5] = f32f16 => |k| k.with_packing(f32::packing(32), f16::packing(1)); + quality(ManuallyOptimized) + store(f16) +} +MMMExternKernel!(fma_mmm_f32_32x3(32,3)@(256,4) where(FMA) + packing[1] = f32f16 => |k| k.with_packing(f32::packing(32).align(256), f16::packing(3)); + packing[2] = f16f32 => |k| k.with_packing(f16::packing(32).align(256), f32::packing(3)); + packing[3] = f16f16 => |k| k.with_packing(f16::packing(32).align(256), f16::packing(3)); + quality(ManuallyOptimized) + store(f16) +); + +MMMExternKernel!(avx512_mmm_f32_128x1(128, 1)@(512,4) where (AVX512F) quality(ManuallyOptimized)); +MMMExternKernel!(avx512_mmm_f32_16x1 ( 16, 1)@(512,4) where (AVX512F) quality(ManuallyOptimized)); +MMMExternKernel!(avx512_mmm_f32_16x12( 16,12)@(512,4) where (AVX512F) quality(ManuallyOptimized)); +MMMExternKernel!(avx512_mmm_f32_16x8 ( 16, 8)@(512,4) where (AVX512F) quality(ManuallyOptimized)); +MMMExternKernel!(avx512_mmm_f32_32x6 ( 32, 6)@(512,4) where (AVX512F) quality(ManuallyOptimized)); +MMMExternKernel!(avx512_mmm_f32_32x5 ( 32, 5)@(512,4) where (AVX512F) quality(ManuallyOptimized)); +MMMExternKernel!(avx512_mmm_f32_48x4 ( 48, 4)@(512,4) where (AVX512F) quality(ManuallyOptimized)); +MMMExternKernel!(avx512_mmm_f32_64x3 ( 64, 3)@(512,4) where (AVX512F) quality(ManuallyOptimized)); +MMMExternKernel!(avx512_mmm_f32_80x2 ( 80, 2)@(512,4) where (AVX512F) quality(ManuallyOptimized)); + +MMMExternKernel! { avx2_mmm_i32_8x8(8,8)@(256,4) where(AVX2) + packing[1] = i8i8 => |k| k.with_packing(PackedFormat::new(DatumType::I8, 8, 256), PackedFormat::new(DatumType::I8, 8, 4)); + quality(ManuallyOptimized) + store(i8) +} + +// AVX-512 VNNI int8 GEMM: same 8x8 column-accumulator tile and quantization +// epilogue as avx2_mmm_i32_8x8, but the i8i8 matmul inner loop uses VPDPBUSD +// (4-way K dot) over the K=4-inner PackedI8K4 layout. VPDPBUSD is u8*s8, so the +// kernel offsets A by +128 and removes the 128*sum_k(B) bias per column before +// the epilogue, making the i32 accumulators bit-identical to the AVX2 path. +// +// Gated on `tract_avx512vnni` (set by build.rs when the assembler can encode +// `vpdpbusd ymm`; binutils < 2.30 cannot). On old toolchains the kernel is +// omitted entirely and the AVX2 i32 path is used instead. +#[cfg(tract_avx512vnni)] +MMMExternKernel! { avx512vnni_mmm_i32_8x8(8,8)@(256,4) where(AVX512VNNI) + packing[1] = i8i8 => |k| k.with_packing(PackedI8K4::new(8), PackedI8K4::new(8)); + quality(ManuallyOptimized) + store(i8) +} + +pub fn plug(ops: &mut Ops) { + if is_x86_feature_detected!("avx2") { + plug_avx2(ops); + if is_x86_feature_detected!("fma") { + plug_fma(ops); + if is_x86_feature_detected!("avx512f") { + plug_avx512f(ops); + #[cfg(tract_avx512vnni)] + if is_x86_feature_detected!("avx512vnni") { + plug_avx512vnni(ops); + } + } + } + } +} + +#[cfg(tract_avx512vnni)] +pub fn plug_avx512vnni(ops: &mut Ops) { + ops.mmm_impls.push(avx512vnni_mmm_i32_8x8.mmm()); + ops.qmmm_i32 = Box::new(|_, _, _| avx512vnni_mmm_i32_8x8.mmm()); + log::info!("qmmm_i32: x86_64/avx512vnni activated"); +} + +pub fn plug_avx2(ops: &mut Ops) { + ops.mmm_impls.push(mmm::avx2_mmm_i32_8x8.mmm()); + ops.qmmm_i32 = Box::new(|_, _, _| mmm::avx2_mmm_i32_8x8.mmm()); + log::info!("qmmm_i32: x86_64/avx2 activated"); +} + +pub fn plug_fma(ops: &mut Ops) { + ops.mmm_impls.extend([ + fma_mmm_f32_8x8.mmm(), + fma_mmm_f32_16x5.mmm(), + fma_mmm_f32_16x6.mmm(), + fma_mmm_f32_24x4.mmm(), + fma_mmm_f32_32x3.mmm(), + fma_mmm_f32_40x2.mmm(), + fma_mmm_f32_64x1.mmm(), + ]); + + ops.mmv_f32 = Box::new(|_, _| fma_mmm_f32_64x1.mmm()); + + // Hand-tuned for low N; calibration came from past measurements. + // For other N, fall back to a generic (M, N)-aware tile-utilisation + // picker over the same kernel pool. + const FMA_CHOICES: &[KernelChoice] = &[ + KernelChoice { + mr: 8, + nr: 8, + scale: 44.0 / 60.0, + ctor: || fma_mmm_f32_8x8.mmm(), + }, + KernelChoice { + mr: 16, + nr: 6, + scale: 54.0 / 60.0, + ctor: || fma_mmm_f32_16x6.mmm(), + }, + KernelChoice { + mr: 16, + nr: 5, + scale: 54.0 / 60.0, + ctor: || fma_mmm_f32_16x5.mmm(), + }, + KernelChoice { + mr: 24, + nr: 4, + scale: 54.0 / 60.0, + ctor: || fma_mmm_f32_24x4.mmm(), + }, + KernelChoice { + mr: 32, + nr: 3, + scale: 54.0 / 60.0, + ctor: || fma_mmm_f32_32x3.mmm(), + }, + KernelChoice { + mr: 40, + nr: 2, + scale: 54.0 / 60.0, + ctor: || fma_mmm_f32_40x2.mmm(), + }, + ]; + + ops.mmm_f32 = Box::new(|m, _, n| match n { + None => fma_mmm_f32_16x6.mmm(), + Some(1) => unreachable!("should've been mmv"), + Some(2) => fma_mmm_f32_40x2.mmm(), + Some(3) => fma_mmm_f32_32x3.mmm(), + Some(4) => fma_mmm_f32_24x4.mmm(), + Some(5) => fma_mmm_f32_16x5.mmm(), + Some(6) => fma_mmm_f32_16x6.mmm(), + Some(8) => fma_mmm_f32_8x8.mmm(), + Some(_) => pick_mmm(FMA_CHOICES, m, n), + }); + log::info!("mmm_f32, mmv_f32: x86_64/fma activated"); + + if is_x86_feature_detected!("f16c") { + ops.mmm_impls.push(mmm::fma_mmm_f32_32x1.mmm()); // q40f32 requires f16c + log::info!("found f16c, added fake-f16 and q40-able kernels"); + } +} + +pub fn plug_avx512f(ops: &mut Ops) { + ops.mmm_impls.push(avx512_mmm_f32_128x1.mmm()); + ops.mmm_impls.push(avx512_mmm_f32_80x2.mmm()); + ops.mmm_impls.push(avx512_mmm_f32_48x4.mmm()); + ops.mmm_impls.push(avx512_mmm_f32_64x3.mmm()); + ops.mmm_impls.push(avx512_mmm_f32_32x6.mmm()); + ops.mmm_impls.push(avx512_mmm_f32_32x5.mmm()); + ops.mmm_impls.push(avx512_mmm_f32_16x12.mmm()); + ops.mmm_impls.push(avx512_mmm_f32_16x8.mmm()); + ops.mmv_f32 = Box::new(|m, _k| match m { + Some(m) if m < 31 => avx512_mmm_f32_16x1.mmm(), + _ => avx512_mmm_f32_128x1.mmm(), + }); + + // No measured per-kernel scaling on AVX-512 yet; all kernels start + // at 1.0 and the picker decides on (M, N) tile waste alone. + const AVX512_CHOICES: &[KernelChoice] = &[ + KernelChoice { + mr: 16, + nr: 8, + scale: 1.0, + ctor: || avx512_mmm_f32_16x8.mmm(), + }, + KernelChoice { + mr: 16, + nr: 12, + scale: 1.0, + ctor: || avx512_mmm_f32_16x12.mmm(), + }, + KernelChoice { + mr: 32, + nr: 5, + scale: 1.0, + ctor: || avx512_mmm_f32_32x5.mmm(), + }, + KernelChoice { + mr: 32, + nr: 6, + scale: 1.0, + ctor: || avx512_mmm_f32_32x6.mmm(), + }, + KernelChoice { + mr: 48, + nr: 4, + scale: 1.0, + ctor: || avx512_mmm_f32_48x4.mmm(), + }, + KernelChoice { + mr: 64, + nr: 3, + scale: 1.0, + ctor: || avx512_mmm_f32_64x3.mmm(), + }, + KernelChoice { + mr: 80, + nr: 2, + scale: 1.0, + ctor: || avx512_mmm_f32_80x2.mmm(), + }, + KernelChoice { + mr: 128, + nr: 1, + scale: 1.0, + ctor: || avx512_mmm_f32_128x1.mmm(), + }, + ]; + + ops.mmm_f32 = Box::new(|m, _, n| { + if let Some(1) = n { + unreachable!("should've been mmv"); + } + pick_mmm(AVX512_CHOICES, m, n) + }); + log::info!("mmm_f32, mmv_f32: x86_64/avx512f activated"); +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::frame::mmm::{AsInputValue, FusedSpec}; + use tract_data::internal::*; + + #[test] + fn avx512_128x1_add_unicast_with_strided_c() -> TractResult<()> { + if !is_x86_feature_detected!("avx512f") { + return Ok(()); + } + let (m, k_each, n) = (1000usize, 256usize, 13usize); + let a0: Vec = (0..m * k_each) + .map(|i| ((i % 17) as f32 - 8.0) / 16.0) + .collect(); + let a1: Vec = (0..m * k_each) + .map(|i| ((i % 19) as f32 - 9.0) / 18.0) + .collect(); + let b0: Vec = (0..k_each * n) + .map(|i| ((i % 13) as f32 - 6.0) / 13.0) + .collect(); + let b1: Vec = (0..k_each * n) + .map(|i| ((i % 11) as f32 - 5.0) / 10.0) + .collect(); + + let mut expected = vec![0.0f32; m * n]; + for r in 0..m { + for c in 0..n { + let mut acc = 0.0f32; + for kk in 0..k_each { + acc += a0[r * k_each + kk] * b0[kk * n + c]; + acc += a1[r * k_each + kk] * b1[kk * n + c]; + } + expected[r * n + c] = acc; + } + } + + let ker = avx512_mmm_f32_128x1.mmm(); + let (pack_a, pack_b) = &ker.packings()[0]; + let pack_one = + |buf: Vec, rows, cols, m_axis, k_axis, pack: &dyn crate::mmm::MMMInputFormat| { + let t = tract_ndarray::Array2::from_shape_vec((rows, cols), buf) + .unwrap() + .into_tensor(); + pack.prepare_one(&t, k_axis, m_axis).unwrap() + }; + let pa0 = pack_one(a0, m, k_each, 0, 1, &**pack_a); + let pa1 = pack_one(a1, m, k_each, 0, 1, &**pack_a); + let pb0 = pack_one(b0, k_each, n, 1, 0, &**pack_b); + let pb1 = pack_one(b1, k_each, n, 1, 0, &**pack_b); + + // C-buffer layout with row stride > nr*sizeof, matching squeezenet conv10's + // (M=1000, spatial=13, N=13) view: M-stride is 169 floats, not nr=1. + let spatial = 13usize; + let mut c_backing = Tensor::zero::(&[m, spatial, n])?; + let c_spec = unsafe { ker.c_from_data_and_strides(4, (spatial * n) as isize, 1) }; + + unsafe { + let c_view = c_backing.view_mut(); + let c = c_spec.wrap(&c_view); + let ops: TVec = tvec!( + FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa0), + b: AsInputValue::Borrowed(&*pb0), + packing: 0, + }, + FusedSpec::Store(c), + ); + ker.run(m, n, &ops)?; + } + + unsafe { + let c_view = c_backing.view_mut(); + let c_for_unicast = c_spec.wrap(&c_view); + let c_for_store = c_spec.wrap(&c_view); + let ops: TVec = tvec!( + FusedSpec::AddMatMul { + a: AsInputValue::Borrowed(&*pa1), + b: AsInputValue::Borrowed(&*pb1), + packing: 0, + }, + FusedSpec::AddUnicast(c_for_unicast), + FusedSpec::Store(c_for_store), + ); + ker.run(m, n, &ops)?; + } + + let c_slice = c_backing.to_plain_array_view::()?; + let mut max_err = 0.0f32; + let mut wrong_cells = 0; + for r in 0..m { + for cc in 0..n { + let got = c_slice[[r, 0, cc]]; + let exp = expected[r * n + cc]; + let e = (got - exp).abs(); + if e > 1e-3 { + wrong_cells += 1; + } + max_err = max_err.max(e); + } + } + assert!( + max_err < 1e-3, + "avx512_mmm_f32_128x1 wrong output at squeezenet shape: \ + max_err={max_err}, {wrong_cells}/{} cells off", + m * n, + ); + Ok(()) + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/panel_extract.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/panel_extract.rs new file mode 100644 index 0000000..a5c260e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/panel_extract.rs @@ -0,0 +1,137 @@ +use super::*; +use crate::Ops; +use crate::pack::{PackedFormat, Packing}; +use tract_data::internal::*; + +pub fn plug(ops: &mut Ops) { + ops.panel_extractors + .extend([packed_32_q40_to_f32.clone(), packed_32_f16_to_f32.clone()]); +} + +panel_extractor!(kernel_packed_32_q40_to_f32 as packed_32_q40_to_f32( + Box::new(super::mmm::pq40_r32()), + f32::packing(32).align(32) +) where(AVX2)); + +panel_extractor!(kernel_packed_32_f16_to_f32 as packed_32_f16_to_f32( + Box::new(PackedFormat::new(f16::datum_type(), 32, 32)), + f32::packing(32).align(32) +) where(AVX2)); + +#[target_feature(enable = "avx2")] +unsafe fn kernel_packed_32_q40_to_f32(input: *const u8, output: *mut u8, k: usize) { + unsafe { + if k == 0 { + return; + } + debug_assert!(k % 32 == 0); + debug_assert!(output as usize % 32 == 0); + std::arch::asm!(" + vbroadcastss ymm14, dword ptr [{mask}] + vbroadcastss ymm13, dword ptr [{eight}] + + 2: + vmovaps xmm4, [{i}] + vmovaps xmm5, [{i} + 16] + vmovaps xmm6, [{i} + 32] + vmovaps xmm7, [{i} + 48] + vcvtph2ps ymm4, xmm4 + vcvtph2ps ymm5, xmm5 + vcvtph2ps ymm6, xmm6 + vcvtph2ps ymm7, xmm7 + add {i}, 64 + + mov {k2}, 32 + 3: + vmovaps xmm8, [{i}] // 32 nibbles + vpand xmm10, xmm8, xmm14 // 16 bytes + vpmovzxbd ymm9, xmm10 // 8 u32 + + vpermilpd xmm10, xmm10, 1 // swap 64bit halves + vpmovzxbd ymm10, xmm10 // 8 u32 + + vpsrlw xmm8, xmm8, 4 + vpand xmm12, xmm8, xmm14 // 16 bytes + vpmovzxbd ymm11, xmm12 // 8 u32 + vpermilpd xmm12, xmm12, 1 // swap 64bit halves + vpmovzxbd ymm12, xmm12 // 8 u32 + + vpsubd ymm9, ymm9, ymm13 + vpsubd ymm10, ymm10, ymm13 + vpsubd ymm11, ymm11, ymm13 + vpsubd ymm12, ymm12, ymm13 + + vcvtdq2ps ymm9, ymm9 + vcvtdq2ps ymm10, ymm10 + vcvtdq2ps ymm11, ymm11 + vcvtdq2ps ymm12, ymm12 + + vmulps ymm9, ymm9, ymm4 + vmulps ymm10, ymm10, ymm5 + vmulps ymm11, ymm11, ymm6 + vmulps ymm12, ymm12, ymm7 + + vmovaps [{o}], ymm9 + vmovaps [{o}+32], ymm10 + vmovaps [{o}+64], ymm11 + vmovaps [{o}+96], ymm12 + + add {i}, 16 + add {o}, 128 + sub {k2}, 1 + jnz 3b + + sub {k}, 32 + jnz 2b; + ", + mask = in(reg) &0x0F0F0F0F, + eight = in(reg) &0x08, + k = inout(reg) k => _, + k2 = out(reg) _, + i = inout(reg) input => _, + o = inout(reg) output => _, + out("ymm0") _, out("ymm1") _, out("ymm2") _, out("ymm3") _, + out("ymm4") _, out("ymm5") _, out("ymm6") _, out("ymm7") _, + out("ymm8") _, out("ymm9") _, out("ymm10") _, out("ymm11") _, + out("ymm12") _, out("ymm13") _, out("ymm14") _, out("ymm15") _ + ); + } +} + +#[target_feature(enable = "avx2")] +unsafe fn kernel_packed_32_f16_to_f32(input: *const u8, output: *mut u8, k: usize) { + unsafe { + if k == 0 { + return; + } + debug_assert!(output as usize % 32 == 0); + std::arch::asm!(" + 2: + vmovaps xmm4, [{i}] + vmovaps xmm5, [{i} + 16] + vmovaps xmm6, [{i} + 32] + vmovaps xmm7, [{i} + 48] + + vcvtph2ps ymm4, xmm4 + vcvtph2ps ymm5, xmm5 + vcvtph2ps ymm6, xmm6 + vcvtph2ps ymm7, xmm7 + + vmovaps [{o}], ymm4 + vmovaps [{o}+32], ymm5 + vmovaps [{o}+64], ymm6 + vmovaps [{o}+96], ymm7 + + add {i}, 64 + add {o}, 128 + + sub {k}, 1 + jnz 2b; + ", + k = inout(reg) k => _, + i = inout(reg) input => _, + o = inout(reg) output => _, + out("ymm4") _, out("ymm5") _, out("ymm6") _, out("ymm7") _, + ); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/rms_norm.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/rms_norm.rs new file mode 100644 index 0000000..468aae1 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/rms_norm.rs @@ -0,0 +1,192 @@ +// Fused AVX-512 RmsNorm (single contiguous row): +// out[i] = x[i] * rsqrt(mean(x[i]²) + eps) +// +// Two passes over the row: +// Pass 1 (sum of squares): acc += x² over 4 zmm accumulators, then reduce +// to a scalar f32; scalar tail handles the +// (len % 64) remainder. +// Pass 2 (multiply-back): broadcast inv_std into zmm0, multiply each +// 4-zmm chunk in place; scalar tail. +// +// Uses unaligned loads/stores (vmovups) — the caller hands per-row slices of a +// possibly-misaligned tensor, so we can't assume 64-byte alignment. On Cascade +// Lake the unaligned penalty is negligible for cache-resident rows. + +#[target_feature(enable = "avx512f")] +unsafe fn rms_norm_f32_inner(buf: &mut [f32], eps: f32) { + let n = buf.len(); + let chunks = n / 64; + let tail_start = chunks * 64; + let ptr = buf.as_mut_ptr(); + + // --- Pass 1: sum of squares --- + let mut sum_sq: f32 = 0.0; + if chunks > 0 { + let p = ptr; + let c = chunks; + unsafe { + std::arch::asm!(" + vpxord zmm0, zmm0, zmm0 + vpxord zmm1, zmm1, zmm1 + vpxord zmm2, zmm2, zmm2 + vpxord zmm3, zmm3, zmm3 + 2: + vmovups zmm4, [{p}] + vmovups zmm5, [{p} + 64] + vmovups zmm6, [{p} + 128] + vmovups zmm7, [{p} + 192] + vfmadd231ps zmm0, zmm4, zmm4 + vfmadd231ps zmm1, zmm5, zmm5 + vfmadd231ps zmm2, zmm6, zmm6 + vfmadd231ps zmm3, zmm7, zmm7 + add {p}, 256 + sub {c}, 1 + jnz 2b + + vaddps zmm0, zmm0, zmm1 + vaddps zmm2, zmm2, zmm3 + vaddps zmm0, zmm0, zmm2 + vextractf64x4 ymm1, zmm0, 1 + vaddps ymm0, ymm0, ymm1 + vextractf128 xmm1, ymm0, 1 + vaddps xmm0, xmm0, xmm1 + vpermilps xmm1, xmm0, 2 + (3 << 2) + vaddps xmm0, xmm0, xmm1 + vpermilps xmm1, xmm0, 1 + vaddps xmm0, xmm0, xmm1 + ", + p = inout(reg) p => _, + c = inout(reg) c => _, + out("xmm0") sum_sq, + out("zmm1") _, out("zmm2") _, out("zmm3") _, + out("zmm4") _, out("zmm5") _, out("zmm6") _, out("zmm7") _, + ); + } + } + // scalar tail + for i in tail_start..n { + let x = unsafe { *buf.get_unchecked(i) }; + sum_sq += x * x; + } + + // --- Compute inv_std (scalar) --- + let mean_sq = sum_sq / (n as f32); + let inv_std = (mean_sq + eps).sqrt().recip(); + + // --- Pass 2: multiply by inv_std --- + if chunks > 0 { + let p = ptr; + let c = chunks; + let inv = inv_std; + unsafe { + std::arch::asm!(" + vbroadcastss zmm0, xmm0 + 2: + vmovups zmm1, [{p}] + vmovups zmm2, [{p} + 64] + vmovups zmm3, [{p} + 128] + vmovups zmm4, [{p} + 192] + vmulps zmm1, zmm1, zmm0 + vmulps zmm2, zmm2, zmm0 + vmulps zmm3, zmm3, zmm0 + vmulps zmm4, zmm4, zmm0 + vmovups [{p}], zmm1 + vmovups [{p} + 64], zmm2 + vmovups [{p} + 128], zmm3 + vmovups [{p} + 192], zmm4 + add {p}, 256 + sub {c}, 1 + jnz 2b + ", + p = inout(reg) p => _, + c = inout(reg) c => _, + inout("xmm0") inv => _, + out("zmm1") _, out("zmm2") _, out("zmm3") _, out("zmm4") _, + ); + } + } + for i in tail_start..n { + unsafe { + *buf.get_unchecked_mut(i) *= inv_std; + } + } +} + +pub fn rms_norm_f32(buf: &mut [f32], eps: f32) { + if buf.is_empty() { + return; + } + unsafe { rms_norm_f32_inner(buf, eps) } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn ref_rms_norm(buf: &mut [f32], eps: f32) { + let n = buf.len() as f32; + let sum_sq: f32 = buf.iter().map(|x| x * x).sum(); + let mean_sq = sum_sq / n; + let inv_std = (mean_sq + eps).sqrt().recip(); + for x in buf.iter_mut() { + *x *= inv_std; + } + } + + fn close_enough(got: &[f32], want: &[f32], tol: f32) { + assert_eq!(got.len(), want.len()); + for (i, (g, w)) in got.iter().zip(want.iter()).enumerate() { + let diff = (g - w).abs(); + assert!(diff <= tol, "lane {i}: got {g}, want {w}, diff {diff}"); + } + } + + #[test] + fn matches_reference_64() { + if !std::is_x86_feature_detected!("avx512f") { + return; + } + let mut x: Vec = (0..64).map(|i| (i as f32 * 0.13).sin() * 5.0).collect(); + let mut y = x.clone(); + rms_norm_f32(&mut x, 1e-5); + ref_rms_norm(&mut y, 1e-5); + close_enough(&x, &y, 1e-5); + } + + #[test] + fn matches_reference_1024_with_tail() { + if !std::is_x86_feature_detected!("avx512f") { + return; + } + // 1024 + 17 = a row that exercises the scalar tail loop. + let n = 1024 + 17; + let mut x: Vec = (0..n).map(|i| (i as f32 * 0.07).cos() * 3.0).collect(); + let mut y = x.clone(); + rms_norm_f32(&mut x, 1e-5); + ref_rms_norm(&mut y, 1e-5); + close_enough(&x, &y, 1e-4); + } + + #[test] + fn matches_reference_short_below_chunk() { + if !std::is_x86_feature_detected!("avx512f") { + return; + } + // Shorter than 64 -> all scalar tail. + let mut x: Vec = vec![0.5, -1.5, 2.5, -3.5, 0.0, 4.0, -4.0, 1.0]; + let mut y = x.clone(); + rms_norm_f32(&mut x, 1e-5); + ref_rms_norm(&mut y, 1e-5); + close_enough(&x, &y, 1e-5); + } + + #[test] + fn empty_is_noop() { + if !std::is_x86_feature_detected!("avx512f") { + return; + } + let mut x: Vec = vec![]; + rms_norm_f32(&mut x, 1e-5); + assert!(x.is_empty()); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/softmax.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/softmax.rs new file mode 100644 index 0000000..283dbb5 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/src/x86_64_fma/softmax.rs @@ -0,0 +1,398 @@ +use crate::num_traits::Zero; +use tract_data::internal::f16; + +map_reduce_impl_wrap!( + f32, + x86_64_fma_softmax2_fastcompact_f32_32n, + 32, + 8, + f32, + f32::MIN, + 0f32, + #[inline(never)] + fn run(buf: &mut [f32], max: f32) -> f32 { + assert!(buf.len() % 32 == 0); + assert!(buf.len() > 0); + unsafe { x86_64_fma_softmax2_fastcompact_f32_32n_run(buf, max) } + }, + #[inline(never)] + fn reduce_two(a: f32, b: f32) -> f32 { + a + b + } +); + +#[target_feature(enable = "avx,fma")] +unsafe fn x86_64_fma_softmax2_fastcompact_f32_32n_run(buf: &mut [f32], max: f32) -> f32 { + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + let mut acc = 0f32; + const MLN2: f32 = 0.6931471805f32; + const A: f32 = 8388608.0f32; + const B: f32 = 1065353216.0f32; + const C: f32 = 60801.0f32; + const SLOPE: f32 = A / MLN2; + const OFFSET: f32 = B - C; + std::arch::asm!(" + vbroadcastss ymm0, xmm0 + vmovaps ymm1, ymm0 + vmovaps ymm2, ymm0 + vmovaps ymm3, ymm0 + + vpxor ymm12, ymm12, ymm12 + vbroadcastss ymm13, xmm13 + vbroadcastss ymm14, xmm14 + vbroadcastss ymm15, xmm15 + 2: + vmovaps ymm4, [{ptr}] + vmovaps ymm5, [{ptr} + 32] + vmovaps ymm6, [{ptr} + 64] + vmovaps ymm7, [{ptr} + 96] + + vsubps ymm4, ymm4, ymm13 + vsubps ymm5, ymm5, ymm13 + vsubps ymm6, ymm6, ymm13 + vsubps ymm7, ymm7, ymm13 + + vmovaps ymm8, ymm15 + vmovaps ymm9, ymm15 + vmovaps ymm10, ymm15 + vmovaps ymm11, ymm15 + + vfmadd231ps ymm8, ymm4, ymm14 + vfmadd231ps ymm9, ymm5, ymm14 + vfmadd231ps ymm10, ymm6, ymm14 + vfmadd231ps ymm11, ymm7, ymm14 + + vmaxps ymm8, ymm8, ymm12 + vmaxps ymm9, ymm9, ymm12 + vmaxps ymm10, ymm10, ymm12 + vmaxps ymm11, ymm11, ymm12 + + vcvttps2dq ymm8, ymm8 + vcvttps2dq ymm9, ymm9 + vcvttps2dq ymm10, ymm10 + vcvttps2dq ymm11, ymm11 + + vmovaps [{ptr}] , ymm8 + vmovaps [{ptr} + 32], ymm9 + vmovaps [{ptr} + 64], ymm10 + vmovaps [{ptr} + 96], ymm11 + + vaddps ymm0, ymm0, ymm8 + vaddps ymm1, ymm1, ymm9 + vaddps ymm2, ymm2, ymm10 + vaddps ymm3, ymm3, ymm11 + + add {ptr}, 128 + sub {len}, 32 + jnz 2b + + vaddps ymm0, ymm0, ymm1 + vaddps ymm2, ymm2, ymm3 + vaddps ymm0, ymm0, ymm2 + vperm2f128 ymm1, ymm0, ymm0, 1 + vaddps xmm0, xmm0, xmm1 + vpermilps xmm1, xmm0, 2 + (3 << 2) + vaddps xmm0, xmm0, xmm1 + vpermilps xmm1, xmm0, 1 + vaddps xmm0, xmm0, xmm1 + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + inout("ymm0") acc, + out("ymm1") _, out("ymm2") _, out("ymm3") _, + out("ymm4") _, out("ymm5") _, out("ymm6") _, out("ymm7") _, + out("ymm8") _, out("ymm9") _, out("ymm10") _, out("ymm11") _, + out("ymm12") _, + inout("ymm13") max => _, + inout("ymm14") SLOPE => _, + inout("ymm15") OFFSET => _, + ); + acc + } +} + +#[cfg(test)] +mod test_x86_64_fma_softmax2_fastcompact_f32_32n { + use super::*; + crate::softmax_l2_frame_tests!( + is_x86_feature_detected!("fma"), + f32, + x86_64_fma_softmax2_fastcompact_f32_32n + ); +} + +// AVX-512 version: processes 64 f32 per loop iteration (4 zmm registers of 16 +// lanes each). Same fast-compact-exp algorithm as the FMA kernel above: +// y = bitcast_u32(max(0, SLOPE*(x-max) + OFFSET)) (via vcvttps2dq) +// then writes y back and accumulates sum(y). Runtime-gated on avx512f (see +// x86_64_fma.rs::plug_avx512f); non-AVX512 CPUs keep using the FMA kernel. +// nr=64, 64-byte (16xf32) alignment. +map_reduce_impl_wrap!( + f32, + x86_64_avx512_softmax2_fastcompact_f32_64n, + 64, + 16, + f32, + f32::MIN, + 0f32, + #[inline(never)] + fn run(buf: &mut [f32], max: f32) -> f32 { + assert!(buf.len() % 64 == 0); + assert!(buf.len() > 0); + unsafe { x86_64_avx512_softmax2_fastcompact_f32_64n_run(buf, max) } + }, + #[inline(never)] + fn reduce_two(a: f32, b: f32) -> f32 { + a + b + } +); + +#[target_feature(enable = "avx512f")] +unsafe fn x86_64_avx512_softmax2_fastcompact_f32_64n_run(buf: &mut [f32], max: f32) -> f32 { + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + let mut acc = 0f32; + const MLN2: f32 = 0.6931471805f32; + const A: f32 = 8388608.0f32; + const B: f32 = 1065353216.0f32; + const C: f32 = 60801.0f32; + const SLOPE: f32 = A / MLN2; + const OFFSET: f32 = B - C; + std::arch::asm!(" + vbroadcastss zmm0, xmm0 + vmovaps zmm1, zmm0 + vmovaps zmm2, zmm0 + vmovaps zmm3, zmm0 + + vpxord zmm28, zmm28, zmm28 // zero (clamp floor) + vbroadcastss zmm29, xmm29 // max + vbroadcastss zmm30, xmm30 // slope + vbroadcastss zmm31, xmm31 // offset + 2: + vmovaps zmm4, [{ptr}] + vmovaps zmm5, [{ptr} + 64] + vmovaps zmm6, [{ptr} + 128] + vmovaps zmm7, [{ptr} + 192] + + vsubps zmm4, zmm4, zmm29 + vsubps zmm5, zmm5, zmm29 + vsubps zmm6, zmm6, zmm29 + vsubps zmm7, zmm7, zmm29 + + vmovaps zmm8, zmm31 + vmovaps zmm9, zmm31 + vmovaps zmm10, zmm31 + vmovaps zmm11, zmm31 + + vfmadd231ps zmm8, zmm4, zmm30 + vfmadd231ps zmm9, zmm5, zmm30 + vfmadd231ps zmm10, zmm6, zmm30 + vfmadd231ps zmm11, zmm7, zmm30 + + vmaxps zmm8, zmm8, zmm28 + vmaxps zmm9, zmm9, zmm28 + vmaxps zmm10, zmm10, zmm28 + vmaxps zmm11, zmm11, zmm28 + + vcvttps2dq zmm8, zmm8 + vcvttps2dq zmm9, zmm9 + vcvttps2dq zmm10, zmm10 + vcvttps2dq zmm11, zmm11 + + vmovaps [{ptr}] , zmm8 + vmovaps [{ptr} + 64] , zmm9 + vmovaps [{ptr} + 128], zmm10 + vmovaps [{ptr} + 192], zmm11 + + vaddps zmm0, zmm0, zmm8 + vaddps zmm1, zmm1, zmm9 + vaddps zmm2, zmm2, zmm10 + vaddps zmm3, zmm3, zmm11 + + add {ptr}, 256 + sub {len}, 64 + jnz 2b + + vaddps zmm0, zmm0, zmm1 + vaddps zmm2, zmm2, zmm3 + vaddps zmm0, zmm0, zmm2 // zmm0 holds 16 partial sums + vextractf64x4 ymm1, zmm0, 1 // upper 256 bits (8xf32) -> ymm1 (avx512f) + vaddps ymm0, ymm0, ymm1 // ymm0 holds 8 values + vextractf128 xmm1, ymm0, 1 // upper 4xf32 -> xmm1 + vaddps xmm0, xmm0, xmm1 // xmm0 holds 4 values + vpermilps xmm1, xmm0, 2 + (3 << 2) + vaddps xmm0, xmm0, xmm1 // xmm0 holds 2 values + vpermilps xmm1, xmm0, 1 + vaddps xmm0, xmm0, xmm1 + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + inout("zmm0") acc, + out("zmm1") _, out("zmm2") _, out("zmm3") _, + out("zmm4") _, out("zmm5") _, out("zmm6") _, out("zmm7") _, + out("zmm8") _, out("zmm9") _, out("zmm10") _, out("zmm11") _, + out("zmm28") _, + inout("zmm29") max => _, + inout("zmm30") SLOPE => _, + inout("zmm31") OFFSET => _, + ); + acc + } +} + +#[cfg(test)] +mod test_x86_64_avx512_softmax2_fastcompact_f32_64n { + use super::*; + crate::softmax_l2_frame_tests!( + is_x86_feature_detected!("avx512f"), + f32, + x86_64_avx512_softmax2_fastcompact_f32_64n + ); +} + +// AVX-512 f16 softmax_l2: same fast-compact-exp algorithm as the FMA f32 +// kernel, with f16 <-> f32 conversion at the IO boundary. Each loop iteration +// handles 64 f16 (128 bytes) through 4× (ymm f16 load -> vcvtph2ps -> zmm f32 +// compute -> vcvttps2dq -> vcvtps2ph -> ymm f16 store). The sum is accumulated +// in f32 across the loop (higher precision than the generic HSoftMaxL2 which +// accumulates in f16) and cast to f16 at return; the SuperApproximate test +// tolerance covers the precision delta. +// nr=64 (multiple of 4 ymm f16 loads); alignment_items=32 (64-byte aligned). +map_reduce_impl_wrap!( + f16, + x86_64_avx512_softmax2_fastcompact_f16_64n, + 64, + 32, + f16, + f16::MIN, + f16::zero(), + #[inline(never)] + fn run(buf: &mut [f16], max: f16) -> f16 { + assert!(buf.len() % 64 == 0); + assert!(buf.len() > 0); + unsafe { x86_64_avx512_softmax2_fastcompact_f16_64n_run(buf, max) } + }, + #[inline(never)] + fn reduce_two(a: f16, b: f16) -> f16 { + a + b + } +); + +#[target_feature(enable = "avx512f")] +unsafe fn x86_64_avx512_softmax2_fastcompact_f16_64n_run( + buf: &mut [tract_data::internal::f16], + max: tract_data::internal::f16, +) -> tract_data::internal::f16 { + unsafe { + let len = buf.len(); + let ptr = buf.as_ptr(); + let max_f32: f32 = max.to_f32(); + let mut acc = 0f32; + const MLN2: f32 = 0.6931471805f32; + const A: f32 = 8388608.0f32; + const B: f32 = 1065353216.0f32; + const C: f32 = 60801.0f32; + const SLOPE: f32 = A / MLN2; + const OFFSET: f32 = B - C; + std::arch::asm!(" + vbroadcastss zmm0, xmm0 + vmovaps zmm1, zmm0 + vmovaps zmm2, zmm0 + vmovaps zmm3, zmm0 + + vpxord zmm28, zmm28, zmm28 // 0 (clamp floor) + vbroadcastss zmm29, xmm29 // max (f32) + vbroadcastss zmm30, xmm30 // slope + vbroadcastss zmm31, xmm31 // offset + 2: + // load 4 ymm of f16 (16 f16 per ymm = 32 bytes), convert to zmm f32 + vcvtph2ps zmm4, [{ptr}] + vcvtph2ps zmm5, [{ptr} + 32] + vcvtph2ps zmm6, [{ptr} + 64] + vcvtph2ps zmm7, [{ptr} + 96] + + // subtract max + vsubps zmm4, zmm4, zmm29 + vsubps zmm5, zmm5, zmm29 + vsubps zmm6, zmm6, zmm29 + vsubps zmm7, zmm7, zmm29 + + // OFFSET + SLOPE * (x - max) + vmovaps zmm8, zmm31 + vmovaps zmm9, zmm31 + vmovaps zmm10, zmm31 + vmovaps zmm11, zmm31 + vfmadd231ps zmm8, zmm4, zmm30 + vfmadd231ps zmm9, zmm5, zmm30 + vfmadd231ps zmm10, zmm6, zmm30 + vfmadd231ps zmm11, zmm7, zmm30 + + // max(0, ...) + vmaxps zmm8, zmm8, zmm28 + vmaxps zmm9, zmm9, zmm28 + vmaxps zmm10, zmm10, zmm28 + vmaxps zmm11, zmm11, zmm28 + + // fast-compact-exp trick: the truncated i32 has the same bit + // pattern as the f32 ~exp(x), so accumulate AS f32 + store as f16 + vcvttps2dq zmm8, zmm8 + vcvttps2dq zmm9, zmm9 + vcvttps2dq zmm10, zmm10 + vcvttps2dq zmm11, zmm11 + + vaddps zmm0, zmm0, zmm8 + vaddps zmm1, zmm1, zmm9 + vaddps zmm2, zmm2, zmm10 + vaddps zmm3, zmm3, zmm11 + + // convert back to f16 and store (4th operand 0 = round to nearest even) + vcvtps2ph [{ptr}], zmm8, 0 + vcvtps2ph [{ptr} + 32], zmm9, 0 + vcvtps2ph [{ptr} + 64], zmm10, 0 + vcvtps2ph [{ptr} + 96], zmm11, 0 + + add {ptr}, 128 + sub {len}, 64 + jnz 2b + + // reduce zmm0..3 to a scalar f32 in xmm0 + vaddps zmm0, zmm0, zmm1 + vaddps zmm2, zmm2, zmm3 + vaddps zmm0, zmm0, zmm2 + vextractf64x4 ymm1, zmm0, 1 + vaddps ymm0, ymm0, ymm1 + vextractf128 xmm1, ymm0, 1 + vaddps xmm0, xmm0, xmm1 + vpermilps xmm1, xmm0, 2 + (3 << 2) + vaddps xmm0, xmm0, xmm1 + vpermilps xmm1, xmm0, 1 + vaddps xmm0, xmm0, xmm1 + ", + len = inout(reg) len => _, + ptr = inout(reg) ptr => _, + inout("zmm0") acc, + out("zmm1") _, out("zmm2") _, out("zmm3") _, + out("zmm4") _, out("zmm5") _, out("zmm6") _, out("zmm7") _, + out("zmm8") _, out("zmm9") _, out("zmm10") _, out("zmm11") _, + out("zmm28") _, + inout("zmm29") max_f32 => _, + inout("zmm30") SLOPE => _, + inout("zmm31") OFFSET => _, + ); + f16::from_f32(acc) + } +} + +#[cfg(test)] +mod test_x86_64_avx512_softmax2_fastcompact_f16_64n { + use super::*; + use tract_data::internal::f16; + crate::softmax_l2_frame_tests!( + is_x86_feature_detected!("avx512f"), + f16, + x86_64_avx512_softmax2_fastcompact_f16_64n + ); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/tests/virtual_im2col.proptest-regressions b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/tests/virtual_im2col.proptest-regressions new file mode 100644 index 0000000..d51a333 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/tests/virtual_im2col.proptest-regressions @@ -0,0 +1,8 @@ +# Seeds for failure cases proptest has generated in the past. It is +# automatically read and these particular cases re-run before any +# novel cases are generated. +# +# It is recommended to check this file in to source control so that +# everyone who runs the test benefits from these saved cases. +cc 0976721f82a17e26a00292dfb2991c1a492affd37448969833f57e3f6ca0b838 # shrinks to pb = ConvProblem { lazy_im2col: false, input: 2,3,4,F32 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0..., filters: 1,2,2,1,F32 0, 0, 0, 0 } +cc 23f0f45fc36312ebfba6e486364031a59deae12155e7e6824ea4ac5de1860bb9 # shrinks to pb = ConvProblem { lazy_im2col: false, input: 3,3,5,F32 -57, 74, -124, -123, 104, 122, -60, -93, 73, 35, -116, -89..., filters: 2,3,3,3,F32 -34, -89, 23, 18, 86, 56, -112, 0, 57, 67, -5, -76... } diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/tests/virtual_im2col.rs b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/tests/virtual_im2col.rs new file mode 100644 index 0000000..85bec8f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/tests/virtual_im2col.rs @@ -0,0 +1,558 @@ +use std::alloc::Layout; +use std::fmt::Display; + +use DatumType::F32; +use proptest::arbitrary::Arbitrary; +use proptest::prelude::*; +use proptest::strategy::{BoxedStrategy, Strategy}; +use tract_data::internal::*; +use tract_linalg::WeightType; +use tract_linalg::mmm::FusedSpec; +use tract_linalg::mmm::{AsInputValue, EagerPackedInput, MMMInputFormat, MMMInputValue}; +use tract_linalg::pack::{PackedFormat, PackingWriter}; + +proptest::proptest! { + #[test] + fn prop(pb in any::()) { + pb.check() + } +} + +#[test] +fn test1() { + ConvProblem { + lazy_im2col: false, + input: tensor3(&[[[1f32]]]), + filters: tensor4(&[[[[-1f32]]]]), + } + .check() +} + +#[test] +fn test_axes_0() { + // CHW HWIO CHW + // 121 1112 221 + ConvProblem { + lazy_im2col: false, + input: tensor3(&[[[0f32], [-1.0]]]), + filters: tensor4(&[[[[0f32, -1f32]]]]), + } + .check() +} + +#[test] +fn test_axes_1() { + ConvProblem { + lazy_im2col: false, + input: tensor3(&[[[0f32, 1.]]]), + filters: tensor4(&[[[[1f32]]]]), + } + .check() +} + +#[test] +fn test_lazy_0() { + ConvProblem { + lazy_im2col: true, + input: tensor3(&[[[1f32]]]), + filters: tensor4(&[[[[1f32]]]]), + } + .check() +} + +#[test] +fn test_lazy_1() { + ConvProblem { + lazy_im2col: true, + input: tensor3(&[[[0f32], [0.], [0.]]]), + filters: tensor4(&[[[[0f32]]]]), + } + .check() +} + +#[test] +fn test_lazy_2() { + ConvProblem { + lazy_im2col: true, + input: tensor3(&[[[0f32, 0.], [0., 1.]]]), + filters: tensor4(&[[[[0f32]], [[1.]]]]), + } + .check() +} + +#[test] +fn test_lazy_3() { + // CHW HWIO CHW + // 212 1221 111 + // im2col: k=4, n=1, k <- kh, kw, c + // 0 X X X X kh=0, kw=0, c=0 + // 1 X X X X kh=0, kw=0, c=1 + // 0 X X X X kh=0, kw=1, c=0 + // 0 X X X X kh=0, kw=1, c=1 + ConvProblem { + lazy_im2col: true, + input: tensor3(&[[[0f32, 0.]], [[1., 0.]]]), + filters: tensor4(&[[[[0f32], [0.]], [[1.], [0.]]]]), + } + .check() +} + +#[test] +fn test_eager_asan_0() { + ConvProblem { + lazy_im2col: false, + input: tensor(vec![3, 3, 5]), + filters: tensor(vec![3, 3, 3, 1]), + } + .check() +} + +// 2D valid, no group, no dil, no stride, HWIO, CHW +#[derive(Clone, Debug)] +pub struct ConvProblem { + pub lazy_im2col: bool, + pub input: Tensor, + pub filters: Tensor, +} + +fn mknhw(filters: &[usize], input: &[usize]) -> (usize, usize, usize, usize, usize) { + let m = filters[3]; + let k = filters[0..3].iter().product::(); + let h = input[1] - filters[0] + 1; + let w = input[2] - filters[1] + 1; + let n = h * w; + (m, k, n, h, w) +} + +impl ConvProblem { + fn reference(&self) -> Tensor { + let (m, _, _, h, w) = mknhw(self.filters.shape(), self.input.shape()); + let output_shape = [m, h, w]; + let mut output = Tensor::zero::(&output_shape).unwrap(); + let mut output_plain = output.try_as_plain_mut().unwrap(); + let mut output_view = output_plain.to_array_view_mut::().unwrap(); + let input_view = self.input.to_plain_array_view::().unwrap(); + let filters_view = self.filters.to_plain_array_view::().unwrap(); + for geo_out in tract_ndarray::indices(&output_shape[1..]) { + for ker_geo in tract_ndarray::indices(&self.filters.shape()[0..2]) { + for ci in 0..self.filters.shape()[2] { + for co in 0..self.filters.shape()[3] { + let output_coord = [co, geo_out[0], geo_out[1]]; + let input_coord = [ci, geo_out[0] + ker_geo[0], geo_out[1] + ker_geo[1]]; + let ker_coord = [ker_geo[0], ker_geo[1], ci, co]; + output_view[output_coord] += + filters_view[ker_coord] * input_view[input_coord]; + } + } + } + } + output + } + + pub fn tract(&self) -> TractResult { + let (m, k, n, h, w) = mknhw(self.filters.shape(), self.input.shape()); + let output_shape = [m, h, w]; + let internal_output_shape = [m, h * w]; + let mmm = tract_linalg::ops() + .mmm(F32, Some(m), Some(k), Some(n)) + .unwrap(); + let output = Tensor::zero::(&internal_output_shape)?; + let reshaped_filters = self.filters.clone().into_shape(&[k, m])?; + let (a_pack, b_pack) = &mmm.packings()[0]; + let a = a_pack.prepare_one(&reshaped_filters, 0, 1)?; + unsafe { + let im2col: Box = if self.lazy_im2col { + LazyIm2colSpec { + full_kernel_shape: self.filters.shape().into(), + packer: b_pack.downcast_ref::().unwrap().clone(), + } + .wrap(&self.input.view()) + } else { + EagerIm2colSpec { + full_kernel_shape: self.filters.shape().into(), + packer: b_pack.downcast_ref::().unwrap().clone(), + } + .wrap(&self.input.view()) + }; + let c_store = mmm.c_view(Some(0), Some(1)).wrap(&output.view()); + mmm.run( + m, + n, + &[ + FusedSpec::AddMatMul { + a: AsInputValue::Owned(a), + b: AsInputValue::Owned(im2col), + packing: 0, + }, + FusedSpec::Store(c_store), + ], + ) + .unwrap() + } + output.into_shape(&output_shape) + } + + fn check(&self) { + let expected = self.reference(); + let found = self.tract().unwrap(); + if found.close_enough(&expected, true).is_err() { + println!("found: "); + println!("{:?}", found.to_plain_array_view::().unwrap()); + println!("expected: "); + println!("{:?}", expected.to_plain_array_view::().unwrap()); + } + found.close_enough(&expected, true).unwrap() + } +} + +impl Arbitrary for ConvProblem { + type Parameters = (); + type Strategy = BoxedStrategy; + fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy { + ( + any::(), + 1..4usize, + 1..4usize, + 1..4usize, + 1..4usize, + 0..3usize, + 0..3usize, + ) + .prop_map(|(eager_im2col, h, w, i, o, extra_h, extra_w)| { + let filters = tensor(vec![h, w, i, o]); + let input = tensor(vec![i, h + extra_h, w + extra_w]); + ConvProblem { + lazy_im2col: eager_im2col, + filters, + input, + } + }) + .boxed() + } +} + +fn tensor(shape: Vec) -> Tensor { + let mut tensor = Tensor::zero::(&shape).unwrap(); + tensor + .try_as_plain_mut() + .unwrap() + .as_slice_mut::() + .unwrap() + .iter_mut() + .enumerate() + .for_each(|(ix, x)| *x = ix as f32); + tensor +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +struct EagerIm2colSpec { + packer: PackedFormat, + full_kernel_shape: TVec, +} + +impl EagerIm2colSpec { + fn wrap(&self, input: &TensorView) -> Box { + let (_, k, n, h, w) = mknhw(&self.full_kernel_shape, input.shape()); + // let input = input.to_array_view::().unwrap(); + let ci = input.shape()[0]; + let kh = self.full_kernel_shape[0]; + let kw = self.full_kernel_shape[1]; + let im2col = tract_ndarray::Array5::::from_shape_fn( + [kh, kw, ci, h, w], + |(kh, kw, ci, h, w)| *input.at([ci, h + kh, w + kw]).unwrap(), + ) + .into_shape_with_order([k, n]) + .unwrap(); + Box::new(EagerIm2col { + im2col: im2col.into_tensor(), + packer: self.packer.clone(), + k, + }) + } +} + +impl Display for EagerIm2colSpec { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "EagerIm2colSpec") + } +} + +impl MMMInputFormat for EagerIm2colSpec { + fn prepare_tensor(&self, _t: &Tensor, _k_axis: usize, _mn_axis: usize) -> TractResult { + todo!(); + } + + fn precursor(&self) -> WeightType { + WeightType::Plain(f32::datum_type()) + } + + fn k_alignment(&self) -> usize { + 1 + } + + fn r(&self) -> usize { + self.packer.r() + } + + fn mem_size(&self, _k: TDim, _mn: TDim) -> TDim { + unimplemented!() + } + + fn extract_at_mn_f16( + &self, + _data: &EagerPackedInput, + _mn: usize, + _slice: &mut [f16], + ) -> TractResult<()> { + todo!(); + } + + fn extract_at_mn_f32( + &self, + _data: &EagerPackedInput, + _mn: usize, + _slice: &mut [f32], + ) -> TractResult<()> { + todo!(); + } + + fn prepare_one( + &self, + _t: &Tensor, + _k_axis: usize, + _mn_axis: usize, + ) -> TractResult> { + todo!() + } +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +struct EagerIm2col { + packer: PackedFormat, + im2col: Tensor, + k: usize, +} + +impl Display for EagerIm2col { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "eager") + } +} + +impl MMMInputValue for EagerIm2col { + fn scratch_panel_buffer_layout(&self) -> Option { + Some( + Layout::from_size_align( + self.packer.single_panel_len(self.k) * f32::datum_type().size_of(), + self.packer.alignment(), + ) + .unwrap(), + ) + } + + fn panel_bytes(&self, i: usize, buffer: Option<*mut u8>) -> TractResult<*const u8> { + let buffer = buffer.unwrap(); + let mn = self.im2col.shape()[1]; + unsafe { + self.packer.pack_t::( + buffer as _, + self.im2col.as_ptr().unwrap(), + mn, + mn as isize, + 1, + 0..self.k, + (i * self.packer.r)..((i + 1) * self.packer.r), + ); + } + Ok(buffer) + } + + fn k(&self) -> usize { + self.k + } + + fn mn(&self) -> usize { + self.im2col.shape()[1] + } + + fn format(&self) -> &dyn tract_linalg::mmm::MMMInputFormat { + &self.packer + } + + fn exotic_fact(&self) -> &dyn ExoticFact { + unimplemented!() + } + + fn extract_at_mn_f16(&self, _mn: usize, _slice: &mut [f16]) -> TractResult<()> { + unimplemented!() + } + + fn extract_at_mn_f32(&self, _mn: usize, _slice: &mut [f32]) -> TractResult<()> { + unimplemented!() + } +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +struct LazyIm2colSpec { + packer: PackedFormat, + full_kernel_shape: TVec, +} + +impl LazyIm2colSpec { + fn wrap(&self, input: &TensorView) -> Box { + let (_, _, _, h, w) = mknhw(&self.full_kernel_shape, input.shape()); + let kh = self.full_kernel_shape[0]; + let kw = self.full_kernel_shape[1]; + let ci = self.full_kernel_shape[2]; + let input_strides = input.strides(); + let k_offsets = (0..kh as isize) + .flat_map(|kh| { + (0..kw as isize).flat_map(move |kw| { + (0..ci as isize).map(move |ci| { + ci * input_strides[0] + kh * input_strides[1] + kw * input_strides[2] + }) + }) + }) + .collect(); + let n_offsets = (0..h as isize) + .flat_map(|h| (0..w as isize).map(move |w| h * input_strides[1] + w * input_strides[2])) + .collect(); + unsafe { + Box::new(LazyIm2col { + spec: self.clone(), + image: input.as_ptr_unchecked(), + k_offsets, + n_offsets, + packer: self.packer.clone(), + }) + } + } +} + +impl Display for LazyIm2colSpec { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "LazyIm2colSpec") + } +} + +impl MMMInputFormat for LazyIm2colSpec { + fn prepare_tensor(&self, _t: &Tensor, _k_axis: usize, _mn_axis: usize) -> TractResult { + todo!(); + } + fn prepare_one( + &self, + _t: &Tensor, + _k_axis: usize, + _mn_axis: usize, + ) -> TractResult> { + todo!(); + } + + fn precursor(&self) -> WeightType { + WeightType::Plain(f32::datum_type()) + } + + fn k_alignment(&self) -> usize { + 1 + } + + fn r(&self) -> usize { + self.packer.r() + } + + fn mem_size(&self, _k: TDim, _mn: TDim) -> TDim { + unimplemented!() + } + + fn extract_at_mn_f16( + &self, + _data: &EagerPackedInput, + _mn: usize, + _slice: &mut [f16], + ) -> TractResult<()> { + todo!(); + } + + fn extract_at_mn_f32( + &self, + _data: &EagerPackedInput, + _mn: usize, + _slice: &mut [f32], + ) -> TractResult<()> { + todo!(); + } +} + +#[derive(Clone, Debug, Hash, PartialEq, Eq)] +struct LazyIm2col { + spec: LazyIm2colSpec, + packer: PackedFormat, + image: *const f32, + n_offsets: Vec, + k_offsets: Vec, +} +unsafe impl Send for LazyIm2col {} +unsafe impl Sync for LazyIm2col {} + +impl Display for LazyIm2col { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "lazy") + } +} + +impl MMMInputValue for LazyIm2col { + fn scratch_panel_buffer_layout(&self) -> Option { + Some( + Layout::from_size_align( + self.packer + .single_panel_len(self.k_offsets.len() * f32::datum_type().size_of()), + self.packer.alignment(), + ) + .unwrap(), + ) + } + + fn panel_bytes(&self, i: usize, buffer: Option<*mut u8>) -> TractResult<*const u8> { + let buffer = buffer.unwrap() as *mut f32; + let mn_end = ((i + 1) * self.packer.r).min(self.n_offsets.len()); + let n_range = (i * self.packer.r)..mn_end; + let k = self.k_offsets.len(); + unsafe { + let mut writer = self.packer.write_with_k_outer(buffer, k, n_range.len()); + for k in 0..k { + for n in n_range.clone() { + writer.write( + *self.image.offset( + self.n_offsets.get_unchecked(n) + self.k_offsets.get_unchecked(k), + ), + ) + } + } + } + Ok(buffer as _) + } + + fn k(&self) -> usize { + self.k_offsets.len() + } + + fn mn(&self) -> usize { + self.n_offsets.len() + } + + fn format(&self) -> &dyn MMMInputFormat { + &self.spec + } + + fn exotic_fact(&self) -> &dyn ExoticFact { + unimplemented!() + } + + fn extract_at_mn_f16(&self, _mn: usize, _slice: &mut [f16]) -> TractResult<()> { + unimplemented!() + } + + fn extract_at_mn_f32(&self, _mn: usize, _slice: &mut [f32]) -> TractResult<()> { + unimplemented!() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/10x1/packed_packed_loop1/avx-512-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/10x1/packed_packed_loop1/avx-512-unroll.S.raw new file mode 100644 index 0000000..857f782 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/10x1/packed_packed_loop1/avx-512-unroll.S.raw @@ -0,0 +1,59 @@ + // Tile size: 10x1 + // Accumulators: 0-9 + // Col regs: 10-19 + // Row regs: 20, 21 + + vbroadcastss zmm20, dword ptr [rcx] + + vmovaps zmm10, [rax + 0] + vmovaps zmm11, [rax + 64] + vmovaps zmm12, [rax + 128] + vmovaps zmm13, [rax + 192] + vmovaps zmm14, [rax + 256] + + vfmadd231ps zmm0, zmm10, zmm20 + vfmadd231ps zmm1, zmm11, zmm20 + vfmadd231ps zmm2, zmm12, zmm20 + vfmadd231ps zmm3, zmm13, zmm20 + vfmadd231ps zmm4, zmm14, zmm20 + + vmovaps zmm15, [rax + 320] + vmovaps zmm16, [rax + 384] + vmovaps zmm17, [rax + 448] + vmovaps zmm18, [rax + 512] + vmovaps zmm19, [rax + 576] + + vfmadd231ps zmm5, zmm10, zmm20 + vfmadd231ps zmm6, zmm11, zmm20 + vfmadd231ps zmm7, zmm12, zmm20 + vfmadd231ps zmm8, zmm13, zmm20 + vfmadd231ps zmm9, zmm14, zmm20 + + vbroadcastss zmm21, dword ptr [rcx + 4] + + vmovaps zmm10, [rax + 640] + vmovaps zmm11, [rax + 704] + vmovaps zmm12, [rax + 768] + vmovaps zmm13, [rax + 832] + vmovaps zmm14, [rax + 896] + + vfmadd231ps zmm0, zmm10, zmm21 + vfmadd231ps zmm1, zmm11, zmm21 + vfmadd231ps zmm2, zmm12, zmm21 + vfmadd231ps zmm3, zmm13, zmm21 + vfmadd231ps zmm4, zmm14, zmm21 + + vmovaps zmm15, [rax + 960] + vmovaps zmm16, [rax + 1024] + vmovaps zmm17, [rax + 1088] + vmovaps zmm18, [rax + 1152] + vmovaps zmm19, [rax + 1216] + + vfmadd231ps zmm5, zmm10, zmm21 + vfmadd231ps zmm6, zmm11, zmm21 + vfmadd231ps zmm7, zmm12, zmm21 + vfmadd231ps zmm8, zmm13, zmm21 + vfmadd231ps zmm9, zmm14, zmm21 + + add rcx, 8 + add rax, 1280 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/10x1/packed_packed_loop1/avx-512.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/10x1/packed_packed_loop1/avx-512.S.raw new file mode 100644 index 0000000..76aaae5 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/10x1/packed_packed_loop1/avx-512.S.raw @@ -0,0 +1,33 @@ + // Tile size: 10x1 + // Accumulators: 0-9 + // Col regs: 10-19 + // Row regs: 20 + + vbroadcastss zmm20, dword ptr [rcx] + + vmovaps zmm10, [rax + 0] + vmovaps zmm11, [rax + 64] + vmovaps zmm12, [rax + 128] + vmovaps zmm13, [rax + 192] + vmovaps zmm14, [rax + 256] + + vfmadd231ps zmm0, zmm10, zmm20 + vfmadd231ps zmm1, zmm11, zmm20 + vfmadd231ps zmm2, zmm12, zmm20 + vfmadd231ps zmm3, zmm13, zmm20 + vfmadd231ps zmm4, zmm14, zmm20 + + vmovaps zmm15, [rax + 320] + vmovaps zmm16, [rax + 384] + vmovaps zmm17, [rax + 448] + vmovaps zmm18, [rax + 512] + vmovaps zmm19, [rax + 576] + + vfmadd231ps zmm5, zmm10, zmm20 + vfmadd231ps zmm6, zmm11, zmm20 + vfmadd231ps zmm7, zmm12, zmm20 + vfmadd231ps zmm8, zmm13, zmm20 + vfmadd231ps zmm9, zmm14, zmm20 + + add rcx, 4 + add rax, 320 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/1x1/packed_packed_loop1/avx-512.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/1x1/packed_packed_loop1/avx-512.S.raw new file mode 100644 index 0000000..ba4e623 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/1x1/packed_packed_loop1/avx-512.S.raw @@ -0,0 +1,7 @@ + vbroadcastss zmm15, dword ptr [rcx] + + vmovups zmm8, [rax] + vfmadd231ps zmm0, zmm15, zmm8 + + add rcx, 4 + add rax, 64 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/1x1/packed_packed_loop1/unroll-16.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/1x1/packed_packed_loop1/unroll-16.S.raw new file mode 100644 index 0000000..4a1c310 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/1x1/packed_packed_loop1/unroll-16.S.raw @@ -0,0 +1,68 @@ + vmovups zmm31, [rcx] + // vbroadcastss zmm17, [rcx + 4 * 0] + // vbroadcastss zmm18, [rcx + 4 * 1] + // vbroadcastss zmm19, [rcx + 4 * 2] + // vbroadcastss zmm20, [rcx + 4 * 3] + // vbroadcastss zmm21, [rcx + 4 * 4] + // vbroadcastss zmm22, [rcx + 4 * 5] + // vbroadcastss zmm23, [rcx + 4 * 6] + // vbroadcastss zmm24, [rcx + 4 * 7] + // vbroadcastss zmm25, [rcx + 4 * 8] + // vbroadcastss zmm26, [rcx + 4 * 9] + // vbroadcastss zmm27, [rcx + 4 * 10] + // vbroadcastss zmm28, [rcx + 4 * 11] + // vbroadcastss zmm29, [rcx + 4 * 12] + // vbroadcastss zmm30, [rcx + 4 * 13] + // vbroadcastss zmm31, [rcx + 4 * 14] + + vbroadcastss zmm16, xmm31 + valignd zmm17, zmm31, zmm31, 1 + vbroadcastss zmm17, xmm17 + valignd zmm18, zmm31, zmm31, 2 + vbroadcastss zmm18, xmm18 + valignd zmm19, zmm31, zmm31, 3 + vbroadcastss zmm19, xmm19 + valignd zmm20, zmm31, zmm31, 4 + vbroadcastss zmm20, xmm20 + valignd zmm21, zmm31, zmm31, 5 + vbroadcastss zmm21, xmm21 + valignd zmm22, zmm31, zmm31, 6 + vbroadcastss zmm22, xmm22 + valignd zmm23, zmm31, zmm31, 7 + vbroadcastss zmm23, xmm23 + valignd zmm24, zmm31, zmm31, 8 + vbroadcastss zmm24, xmm24 + valignd zmm25, zmm31, zmm31, 9 + vbroadcastss zmm25, xmm25 + valignd zmm26, zmm31, zmm31, 10 + vbroadcastss zmm26, xmm26 + valignd zmm27, zmm31, zmm31, 11 + vbroadcastss zmm27, xmm27 + valignd zmm28, zmm31, zmm31, 12 + vbroadcastss zmm28, xmm28 + valignd zmm29, zmm31, zmm31, 13 + vbroadcastss zmm29, xmm29 + valignd zmm30, zmm31, zmm31, 14 + vbroadcastss zmm30, xmm30 + valignd zmm31, zmm31, zmm31, 15 + vbroadcastss zmm31, xmm31 + + vfmadd231ps zmm0, zmm16, [rax + 0] + vfmadd231ps zmm1, zmm17, [rax + 64] + vfmadd231ps zmm2, zmm18, [rax + 128] + vfmadd231ps zmm3, zmm19, [rax + 192] + vfmadd231ps zmm4, zmm20, [rax + 256] + vfmadd231ps zmm5, zmm21, [rax + 320] + vfmadd231ps zmm6, zmm22, [rax + 384] + vfmadd231ps zmm7, zmm23, [rax + 448] + vfmadd231ps zmm8, zmm24, [rax + 512] + vfmadd231ps zmm9, zmm25, [rax + 576] + vfmadd231ps zmm10, zmm26, [rax + 640] + vfmadd231ps zmm11, zmm27, [rax + 704] + vfmadd231ps zmm12, zmm28, [rax + 768] + vfmadd231ps zmm13, zmm29, [rax + 832] + vfmadd231ps zmm14, zmm30, [rax + 896] + vfmadd231ps zmm15, zmm31, [rax + 960] + + add rcx, 64 + add rax, 1024 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/1x1/packed_packed_loop1/unroll-4.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/1x1/packed_packed_loop1/unroll-4.S.raw new file mode 100644 index 0000000..103be70 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/1x1/packed_packed_loop1/unroll-4.S.raw @@ -0,0 +1,24 @@ + // slow + vbroadcastss xmm16, dword ptr [rcx] + vbroadcastss xmm17, dword ptr [rcx + 4] + vbroadcastss xmm18, dword ptr [rcx + 8] + vbroadcastss xmm19, dword ptr [rcx + 12] + + // fast + vmovups xmm31, [rcx] + vbroadcastss zmm16, xmm31 + valignd xmm17, xmm31, xmm31, 1 + vbroadcastss zmm17, xmm17 + valignd xmm18, xmm31, xmm31, 2 + vbroadcastss zmm18, xmm18 + valignd xmm19, xmm31, xmm31, 3 + vbroadcastss zmm19, xmm19 + + // commmon + vfmadd231ps zmm0, zmm16, [rax + 0] + vfmadd231ps zmm1, zmm17, [rax + 64] + vfmadd231ps zmm2, zmm18, [rax + 128] + vfmadd231ps zmm3, zmm19, [rax + 192] + + add rcx, 16 + add rax, 256 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/1x1/packed_packed_loop1/unroll-8.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/1x1/packed_packed_loop1/unroll-8.S.raw new file mode 100644 index 0000000..d6cb277 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/1x1/packed_packed_loop1/unroll-8.S.raw @@ -0,0 +1,29 @@ + vmovups ymm31, [rcx] + + vbroadcastss zmm16, xmm31 + valignd ymm17, ymm31, ymm31, 1 + vbroadcastss zmm17, xmm17 + valignd ymm18, ymm31, ymm31, 2 + vbroadcastss zmm18, xmm18 + valignd ymm19, ymm31, ymm31, 3 + vbroadcastss zmm19, xmm19 + valignd ymm20, ymm31, ymm31, 4 + vbroadcastss zmm20, xmm20 + valignd ymm21, ymm31, ymm31, 5 + vbroadcastss zmm21, xmm21 + valignd ymm22, ymm31, ymm31, 6 + vbroadcastss zmm22, xmm22 + valignd ymm23, ymm31, ymm31, 7 + vbroadcastss zmm23, xmm23 + + vfmadd231ps zmm0, zmm16, [rax + 0] + vfmadd231ps zmm1, zmm17, [rax + 64] + vfmadd231ps zmm2, zmm18, [rax + 128] + vfmadd231ps zmm3, zmm19, [rax + 192] + vfmadd231ps zmm4, zmm20, [rax + 256] + vfmadd231ps zmm5, zmm21, [rax + 320] + vfmadd231ps zmm6, zmm22, [rax + 384] + vfmadd231ps zmm7, zmm23, [rax + 448] + + add rcx, 32 + add rax, 512 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/1x1/packed_packed_loop1/unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/1x1/packed_packed_loop1/unroll.S.raw new file mode 100644 index 0000000..8c9bf90 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/1x1/packed_packed_loop1/unroll.S.raw @@ -0,0 +1,11 @@ + vbroadcastss zmm15, dword ptr [rcx] + + vmovaps zmm8, [rax + 0] + vfmadd231ps zmm0, zmm15, zmm8 + + vbroadcastss zmm16, dword ptr [rcx + 4] + vmovaps zmm9, [rax + 64] + vfmadd231ps zmm1, zmm16, zmm9 + + add rcx, 8 + add rax, 128 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/1x12/packed_packed_loop1/avx-512.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/1x12/packed_packed_loop1/avx-512.S.raw new file mode 100644 index 0000000..4ffab3b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/1x12/packed_packed_loop1/avx-512.S.raw @@ -0,0 +1,45 @@ + // Tile size: 1x12 + // Accumulators: 0-11 + // Col regs: zmm14 + // Row regs: zmm15 + + vmovaps zmm15, [rax] + + vbroadcastss zmm14, dword ptr [rcx + 0 * 4] + vfmadd231ps zmm0, zmm15, zmm14 + + vbroadcastss zmm14, dword ptr [rcx + 1 * 4] + vfmadd231ps zmm1, zmm15, zmm14 + + vbroadcastss zmm14, dword ptr [rcx + 2 * 4] + vfmadd231ps zmm2, zmm15, zmm14 + + vbroadcastss zmm14, dword ptr [rcx + 3 * 4] + vfmadd231ps zmm3, zmm15, zmm14 + + vbroadcastss zmm14, dword ptr [rcx + 4 * 4] + vfmadd231ps zmm4, zmm15, zmm14 + + vbroadcastss zmm14, dword ptr [rcx + 5 * 4] + vfmadd231ps zmm5, zmm15, zmm14 + + vbroadcastss zmm14, dword ptr [rcx + 6 * 4] + vfmadd231ps zmm6, zmm15, zmm14 + + vbroadcastss zmm14, dword ptr [rcx + 7 * 4] + vfmadd231ps zmm7, zmm15, zmm14 + + vbroadcastss zmm14, dword ptr [rcx + 8 * 4] + vfmadd231ps zmm8, zmm15, zmm14 + + vbroadcastss zmm14, dword ptr [rcx + 9 * 4] + vfmadd231ps zmm9, zmm15, zmm14 + + vbroadcastss zmm14, dword ptr [rcx + 10 * 4] + vfmadd231ps zmm10, zmm15, zmm14 + + vbroadcastss zmm14, dword ptr [rcx + 11 * 4] + vfmadd231ps zmm11, zmm15, zmm14 + + add rcx, 48 + add rax, 64 \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/2x5/packed_packed_loop1/avx-512-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/2x5/packed_packed_loop1/avx-512-unroll.S.raw new file mode 100644 index 0000000..118d312 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/2x5/packed_packed_loop1/avx-512-unroll.S.raw @@ -0,0 +1,53 @@ + // Accumulators: 0-9 + // Columns: 15-16 + // Rows: 10-14 + vbroadcastss zmm10, dword ptr [rcx] + vbroadcastss zmm11, dword ptr [rcx + 4] + vbroadcastss zmm12, dword ptr [rcx + 8] + vbroadcastss zmm13, dword ptr [rcx + 12] + vbroadcastss zmm14, dword ptr [rcx + 16] + + vmovaps zmm15, [rax] + vmovaps zmm16, [rax + 64] + + vfmadd231ps zmm0, zmm15, zmm10 + vfmadd231ps zmm1, zmm16, zmm10 + + vfmadd231ps zmm2, zmm15, zmm11 + vfmadd231ps zmm3, zmm16, zmm11 + + vfmadd231ps zmm4, zmm15, zmm12 + vfmadd231ps zmm5, zmm16, zmm12 + + vfmadd231ps zmm6, zmm15, zmm13 + vfmadd231ps zmm7, zmm16, zmm13 + + vfmadd231ps zmm8, zmm15, zmm14 + vfmadd231ps zmm9, zmm16, zmm14 + + vbroadcastss zmm10, dword ptr [rcx + 20] + vbroadcastss zmm11, dword ptr [rcx + 24] + vbroadcastss zmm12, dword ptr [rcx + 28] + vbroadcastss zmm13, dword ptr [rcx + 32] + vbroadcastss zmm14, dword ptr [rcx + 36] + + vmovaps zmm15, [rax + 128] + vmovaps zmm16, [rax + 192] + + vfmadd231ps zmm0, zmm15, zmm10 + vfmadd231ps zmm1, zmm16, zmm10 + + vfmadd231ps zmm2, zmm15, zmm11 + vfmadd231ps zmm3, zmm16, zmm11 + + vfmadd231ps zmm4, zmm15, zmm12 + vfmadd231ps zmm5, zmm16, zmm12 + + vfmadd231ps zmm6, zmm15, zmm13 + vfmadd231ps zmm7, zmm16, zmm13 + + vfmadd231ps zmm8, zmm15, zmm14 + vfmadd231ps zmm9, zmm16, zmm14 + + add rcx, 40 + add rax, 256 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/2x5/packed_packed_loop1/avx-512.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/2x5/packed_packed_loop1/avx-512.S.raw new file mode 100644 index 0000000..e017834 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/2x5/packed_packed_loop1/avx-512.S.raw @@ -0,0 +1,30 @@ + // Accumulators: 0-9 + // Columns: 15 + // Rows: 10-14 + + vbroadcastss zmm10, dword ptr [rcx] + vbroadcastss zmm11, dword ptr [rcx + 4] + vbroadcastss zmm12, dword ptr [rcx + 8] + vbroadcastss zmm13, dword ptr [rcx + 12] + vbroadcastss zmm14, dword ptr [rcx + 16] + + vmovaps zmm15, [rax] + vmovaps zmm16, [rax + 64] + + vfmadd231ps zmm0, zmm15, zmm10 + vfmadd231ps zmm1, zmm16, zmm10 + + vfmadd231ps zmm2, zmm15, zmm11 + vfmadd231ps zmm3, zmm16, zmm11 + + vfmadd231ps zmm4, zmm15, zmm12 + vfmadd231ps zmm5, zmm16, zmm12 + + vfmadd231ps zmm6, zmm15, zmm13 + vfmadd231ps zmm7, zmm16, zmm13 + + vfmadd231ps zmm8, zmm15, zmm14 + vfmadd231ps zmm9, zmm16, zmm14 + + add rcx, 20 + add rax, 128 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/2x6/packed_packed_loop1/avx-512-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/2x6/packed_packed_loop1/avx-512-unroll.S.raw new file mode 100644 index 0000000..9d6c940 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/2x6/packed_packed_loop1/avx-512-unroll.S.raw @@ -0,0 +1,71 @@ + // Tile size: 2x6 + // Accumulators: 0-11 + // Col regs: zmm14-15 + // Row regs: zmm12-13 + + vbroadcastss zmm14, dword ptr [rcx] + vmovaps zmm12, [rax] + vmovaps zmm13, [rax + 64] + vbroadcastss zmm15, dword ptr [rcx + 4] + + vfmadd231ps zmm0, zmm12, zmm14 + vfmadd231ps zmm1, zmm13, zmm14 + + vbroadcastss zmm14, dword ptr [rcx + 8] + + vfmadd231ps zmm2, zmm12, zmm15 + vfmadd231ps zmm3, zmm13, zmm15 + + vbroadcastss zmm15, dword ptr [rcx + 12] + + vfmadd231ps zmm4, zmm12, zmm14 + vfmadd231ps zmm5, zmm13, zmm14 + + vbroadcastss zmm14, dword ptr [rcx + 16] + + vfmadd231ps zmm6, zmm12, zmm15 + vfmadd231ps zmm7, zmm13, zmm15 + + vbroadcastss zmm15, dword ptr [rcx + 20] + + vfmadd231ps zmm8, zmm12, zmm14 + vfmadd231ps zmm9, zmm13, zmm14 + + vbroadcastss zmm14, dword ptr [rcx+24] + + vfmadd231ps zmm10, zmm12, zmm15 + vfmadd231ps zmm11, zmm13, zmm15 + + // Iteration two + vmovaps zmm12, [rax + 128] + vmovaps zmm13, [rax + 192] + vbroadcastss zmm15, dword ptr [rcx + 24 + 4] + + vfmadd231ps zmm0, zmm12, zmm14 + vfmadd231ps zmm1, zmm13, zmm14 + + vbroadcastss zmm14, dword ptr [rcx + 24 + 8] + + vfmadd231ps zmm2, zmm12, zmm15 + vfmadd231ps zmm3, zmm13, zmm15 + + vbroadcastss zmm15, dword ptr [rcx + 24 + 12] + + vfmadd231ps zmm4, zmm12, zmm14 + vfmadd231ps zmm5, zmm13, zmm14 + + vbroadcastss zmm14, dword ptr [rcx + 24 + 16] + + vfmadd231ps zmm6, zmm12, zmm15 + vfmadd231ps zmm7, zmm13, zmm15 + + vbroadcastss zmm15, dword ptr [rcx + 24 + 20] + + vfmadd231ps zmm8, zmm12, zmm14 + vfmadd231ps zmm9, zmm13, zmm14 + + vfmadd231ps zmm10, zmm12, zmm15 + vfmadd231ps zmm11, zmm13, zmm15 + + add rax, 256 + add rcx, 48 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/2x6/packed_packed_loop1/avx-512.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/2x6/packed_packed_loop1/avx-512.S.raw new file mode 100644 index 0000000..31f861b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/2x6/packed_packed_loop1/avx-512.S.raw @@ -0,0 +1,39 @@ + // Tile size: 2x6 + // Accumulators: 0-11 + // Col regs: zmm14-15 + // Row regs: zmm12-13 + + // Load ordered by earliest use for first 2x2 block + vbroadcastss zmm14, dword ptr [rcx] + vmovaps zmm12, [rax] + vmovaps zmm13, [rax + 64] + vbroadcastss zmm15, dword ptr [rcx + 4] + + vfmadd231ps zmm0, zmm12, zmm14 + vfmadd231ps zmm1, zmm13, zmm14 + + vbroadcastss zmm14, dword ptr [rcx + 8] + + vfmadd231ps zmm2, zmm12, zmm15 + vfmadd231ps zmm3, zmm13, zmm15 + + vbroadcastss zmm15, dword ptr [rcx + 12] + + vfmadd231ps zmm4, zmm12, zmm14 + vfmadd231ps zmm5, zmm13, zmm14 + + vbroadcastss zmm14, dword ptr [rcx + 16] + + vfmadd231ps zmm6, zmm12, zmm15 + vfmadd231ps zmm7, zmm13, zmm15 + + vbroadcastss zmm15, dword ptr [rcx + 20] + + vfmadd231ps zmm8, zmm12, zmm14 + vfmadd231ps zmm9, zmm13, zmm14 + + vfmadd231ps zmm10, zmm12, zmm15 + vfmadd231ps zmm11, zmm13, zmm15 + + add rax, 128 + add rcx, 24 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/3x4/packed_packed_loop1/avx-512-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/3x4/packed_packed_loop1/avx-512-unroll.S.raw new file mode 100644 index 0000000..c36b7f6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/3x4/packed_packed_loop1/avx-512-unroll.S.raw @@ -0,0 +1,63 @@ + // Tile size: 3x4 + // Accumulators: 0-11 + // Col regs: zmm12-14 + // Row regs: zmm15 + + vmovaps zmm12, [rax] + vmovaps zmm13, [rax+64] + vmovaps zmm14, [rax+128] + + vbroadcastss zmm15, dword ptr [rcx + 0] + + vfmadd231ps zmm0, zmm12, zmm15 + vfmadd231ps zmm1, zmm13, zmm15 + vfmadd231ps zmm2, zmm14, zmm15 + + vbroadcastss zmm15, dword ptr [rcx + 4] + + vfmadd231ps zmm3, zmm12, zmm15 + vfmadd231ps zmm4, zmm13, zmm15 + vfmadd231ps zmm5, zmm14, zmm15 + + vbroadcastss zmm15, dword ptr [rcx + 8] + + vfmadd231ps zmm6, zmm12, zmm15 + vfmadd231ps zmm7, zmm13, zmm15 + vfmadd231ps zmm8, zmm14, zmm15 + + vbroadcastss zmm15, dword ptr [rcx + 12] + + vfmadd231ps zmm9, zmm12, zmm15 + vfmadd231ps zmm10, zmm13, zmm15 + vfmadd231ps zmm11, zmm14, zmm15 + + vmovaps zmm12, [rax + 192] + vmovaps zmm13, [rax + 256] + vmovaps zmm14, [rax + 320] + + vbroadcastss zmm15, dword ptr [rcx + 16] + + vfmadd231ps zmm0, zmm12, zmm15 + vfmadd231ps zmm1, zmm13, zmm15 + vfmadd231ps zmm2, zmm14, zmm15 + + vbroadcastss zmm15, dword ptr [rcx + 20] + + vfmadd231ps zmm3, zmm12, zmm15 + vfmadd231ps zmm4, zmm13, zmm15 + vfmadd231ps zmm5, zmm14, zmm15 + + vbroadcastss zmm15, dword ptr [rcx + 24] + + vfmadd231ps zmm6, zmm12, zmm15 + vfmadd231ps zmm7, zmm13, zmm15 + vfmadd231ps zmm8, zmm14, zmm15 + + vbroadcastss zmm15, dword ptr [rcx + 28] + + vfmadd231ps zmm9, zmm12, zmm15 + vfmadd231ps zmm10, zmm13, zmm15 + vfmadd231ps zmm11, zmm14, zmm15 + + add rax, 384 + add rcx, 32 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/3x4/packed_packed_loop1/avx-512.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/3x4/packed_packed_loop1/avx-512.S.raw new file mode 100644 index 0000000..a8b1c32 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/3x4/packed_packed_loop1/avx-512.S.raw @@ -0,0 +1,35 @@ + // Tile size: 3x4 + // Accumulators: 0-11 + // Col regs: zmm12-14 + // Row regs: zmm15 + + vmovaps zmm12, [rax] + vmovaps zmm13, [rax+64] + vmovaps zmm14, [rax+128] + + vbroadcastss zmm15, dword ptr [rcx + 0] + + vfmadd231ps zmm0, zmm12, zmm15 + vfmadd231ps zmm1, zmm13, zmm15 + vfmadd231ps zmm2, zmm14, zmm15 + + vbroadcastss zmm15, dword ptr [rcx + 4] + + vfmadd231ps zmm3, zmm12, zmm15 + vfmadd231ps zmm4, zmm13, zmm15 + vfmadd231ps zmm5, zmm14, zmm15 + + vbroadcastss zmm15, dword ptr [rcx + 8] + + vfmadd231ps zmm6, zmm12, zmm15 + vfmadd231ps zmm7, zmm13, zmm15 + vfmadd231ps zmm8, zmm14, zmm15 + + vbroadcastss zmm15, dword ptr [rcx + 12] + + vfmadd231ps zmm9, zmm12, zmm15 + vfmadd231ps zmm10, zmm13, zmm15 + vfmadd231ps zmm11, zmm14, zmm15 + + add rax, 192 + add rcx, 16 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/4x3/packed_packed_loop1/avx-512-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/4x3/packed_packed_loop1/avx-512-unroll.S.raw new file mode 100644 index 0000000..fe661b7 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/4x3/packed_packed_loop1/avx-512-unroll.S.raw @@ -0,0 +1,69 @@ + // Tile size: 4x3 + // Accumulators: 0-11 + // Col regs: zmm12 + // Row regs: zmm13-15 + + // Load col of A + vmovaps zmm12, [rax] + + // Fill 3 cols of B + vbroadcastss zmm13, dword ptr [rcx + 0] + vbroadcastss zmm14, dword ptr [rcx + 4] + vbroadcastss zmm15, dword ptr [rcx + 8] + + // N.B. Stepping cols in inner loop + vfmadd231ps zmm0, zmm12, zmm13 + vfmadd231ps zmm4, zmm12, zmm14 + vfmadd231ps zmm8, zmm12, zmm15 + + vmovaps zmm12, [rax+64] + + vfmadd231ps zmm1, zmm12, zmm13 + vfmadd231ps zmm5, zmm12, zmm14 + vfmadd231ps zmm9, zmm12, zmm15 + + vmovaps zmm12, [rax+128] + + vfmadd231ps zmm2, zmm12, zmm13 + vfmadd231ps zmm6, zmm12, zmm14 + vfmadd231ps zmm10, zmm12, zmm15 + + vmovaps zmm12, [rax+192] + + vfmadd231ps zmm3, zmm12, zmm13 + vfmadd231ps zmm7, zmm12, zmm14 + vfmadd231ps zmm11, zmm12, zmm15 + + // Load col of A, switching col! + vmovaps zmm13, [rax + 256] + + // Fill 3 cols of B + vbroadcastss zmm14, dword ptr [rcx + 12] + vbroadcastss zmm15, dword ptr [rcx + 16] + vbroadcastss zmm12, dword ptr [rcx + 20] + + // N.B. Stepping cols in inner loop + vfmadd231ps zmm0, zmm13, zmm14 + vfmadd231ps zmm4, zmm13, zmm15 + vfmadd231ps zmm8, zmm13, zmm12 + + vmovaps zmm13, [rax + 320] + + vfmadd231ps zmm1, zmm13, zmm14 + vfmadd231ps zmm5, zmm13, zmm15 + vfmadd231ps zmm9, zmm13, zmm12 + + vmovaps zmm13, [rax + 384] + + vfmadd231ps zmm2, zmm13, zmm14 + vfmadd231ps zmm6, zmm13, zmm15 + vfmadd231ps zmm10, zmm13, zmm12 + + vmovaps zmm13, [rax + 448] + + vfmadd231ps zmm3, zmm13, zmm14 + vfmadd231ps zmm7, zmm13, zmm15 + vfmadd231ps zmm11, zmm13, zmm12 + + add rcx, 24 + add rax, 512 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/4x3/packed_packed_loop1/avx-512.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/4x3/packed_packed_loop1/avx-512.S.raw new file mode 100644 index 0000000..0e71a74 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/4x3/packed_packed_loop1/avx-512.S.raw @@ -0,0 +1,38 @@ + // Tile size: 4x3 + // Accumulators: 0-11 + // Col regs: zmm12 + // Row regs: zmm13-15 + + // Load col of A + vmovaps zmm12, [rax] + + // Fill 3 cols of B + vbroadcastss zmm13, dword ptr [rcx + 0] + vbroadcastss zmm14, dword ptr [rcx + 4] + vbroadcastss zmm15, dword ptr [rcx + 8] + + // N.B. Stepping cols in inner loop + vfmadd231ps zmm0, zmm12, zmm13 + vfmadd231ps zmm4, zmm12, zmm14 + vfmadd231ps zmm8, zmm12, zmm15 + + vmovaps zmm12, [rax+64] + + vfmadd231ps zmm1, zmm12, zmm13 + vfmadd231ps zmm5, zmm12, zmm14 + vfmadd231ps zmm9, zmm12, zmm15 + + vmovaps zmm12, [rax+128] + + vfmadd231ps zmm2, zmm12, zmm13 + vfmadd231ps zmm6, zmm12, zmm14 + vfmadd231ps zmm10, zmm12, zmm15 + + vmovaps zmm12, [rax+192] + + vfmadd231ps zmm3, zmm12, zmm13 + vfmadd231ps zmm7, zmm12, zmm14 + vfmadd231ps zmm11, zmm12, zmm15 + + add rcx, 12 + add rax, 256 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/5x2/packed_packed_loop1/avx-512-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/5x2/packed_packed_loop1/avx-512-unroll.S.raw new file mode 100644 index 0000000..6a5b887 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/5x2/packed_packed_loop1/avx-512-unroll.S.raw @@ -0,0 +1,63 @@ + // Tile size: 5x2 + // Accumulators: 0-9 + // Col regs: zmm10-13 + // Row regs: zmm14-15 + + vmovaps zmm10, [rax] + vbroadcastss zmm14, dword ptr [rcx + 0] + vbroadcastss zmm15, dword ptr [rcx + 4] + vmovaps zmm11, [rax + 64] + + // NB stepping column-wise + vfmadd231ps zmm0, zmm10, zmm14 + vfmadd231ps zmm5, zmm10, zmm15 + + vmovaps zmm12, [rax + 128] + + vfmadd231ps zmm1, zmm11, zmm14 + vfmadd231ps zmm6, zmm11, zmm15 + + vmovaps zmm13, [rax + 192] + + vfmadd231ps zmm2, zmm12, zmm14 + vfmadd231ps zmm7, zmm12, zmm15 + + vmovaps zmm10, [rax + 256] + + vfmadd231ps zmm3, zmm13, zmm14 + vfmadd231ps zmm8, zmm13, zmm15 + + vmovaps zmm11, [rax + 320] + + vfmadd231ps zmm4, zmm10, zmm14 + vfmadd231ps zmm9, zmm10, zmm15 + + vbroadcastss zmm14, dword ptr [rcx + 8] + vbroadcastss zmm15, dword ptr [rcx + 12] + + vmovaps zmm12, [rax + 384] + + // NB stepping column-wise + vfmadd231ps zmm0, zmm11, zmm14 + vfmadd231ps zmm5, zmm11, zmm15 + + vmovaps zmm13, [rax + 448] + + vfmadd231ps zmm1, zmm12, zmm14 + vfmadd231ps zmm6, zmm12, zmm15 + + vmovaps zmm10, [rax + 512] + + vfmadd231ps zmm2, zmm13, zmm14 + vfmadd231ps zmm7, zmm13, zmm15 + + vmovaps zmm11, [rax + 576] + + vfmadd231ps zmm3, zmm10, zmm14 + vfmadd231ps zmm8, zmm10, zmm15 + + vfmadd231ps zmm4, zmm11, zmm14 + vfmadd231ps zmm9, zmm11, zmm15 + + add rax, 640 + add rcx, 16 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/5x2/packed_packed_loop1/avx-512.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/5x2/packed_packed_loop1/avx-512.S.raw new file mode 100644 index 0000000..73ef89b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/5x2/packed_packed_loop1/avx-512.S.raw @@ -0,0 +1,34 @@ + // Tile size: 5x2 + // Accumulators: 0-9 + // Col regs: zmm10-14 + // Row regs: zmm15-16 + + vmovaps zmm10, [rax] + vbroadcastss zmm15, dword ptr [rcx + 0] + vbroadcastss zmm16, dword ptr [rcx + 4] + vmovaps zmm11, [rax + 64] + + // NB stepping column-wise + vfmadd231ps zmm0, zmm10, zmm15 + vfmadd231ps zmm5, zmm10, zmm16 + + vmovaps zmm12, [rax + 128] + + vfmadd231ps zmm1, zmm11, zmm15 + vfmadd231ps zmm6, zmm11, zmm16 + + vmovaps zmm13, [rax + 192] + + vfmadd231ps zmm2, zmm12, zmm15 + vfmadd231ps zmm7, zmm12, zmm16 + + vmovaps zmm14, [rax + 256] + + vfmadd231ps zmm3, zmm13, zmm15 + vfmadd231ps zmm8, zmm13, zmm16 + + vfmadd231ps zmm4, zmm14, zmm15 + vfmadd231ps zmm9, zmm14, zmm16 + + add rax, 320 + add rcx, 8 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/6x1/packed_packed_loop1/avx-512-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/6x1/packed_packed_loop1/avx-512-unroll.S.raw new file mode 100644 index 0000000..8c77044 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/6x1/packed_packed_loop1/avx-512-unroll.S.raw @@ -0,0 +1,25 @@ + // Tile size: 6x1 + // Accumulators: 0-5 + // Col regs: 6-11 + // Row regs: 15 + + + vbroadcastss zmm15, dword ptr [rcx] + vfmadd231ps zmm0, zmm15, [rax] + vfmadd231ps zmm1, zmm15, [rax + 64] + vfmadd231ps zmm2, zmm15, [rax + 128] + vfmadd231ps zmm3, zmm15, [rax + 192] + vfmadd231ps zmm4, zmm15, [rax + 256] + vfmadd231ps zmm5, zmm15, [rax + 320] + + vbroadcastss zmm14, dword ptr [rcx + 4] + + vfmadd231ps zmm0, zmm14, [rax + 384] + vfmadd231ps zmm1, zmm14, [rax + 448] + vfmadd231ps zmm2, zmm14, [rax + 512] + vfmadd231ps zmm3, zmm14, [rax + 576] + vfmadd231ps zmm4, zmm14, [rax + 640] + vfmadd231ps zmm5, zmm14, [rax + 704] + + add rax, 768 + add rcx, 8 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/6x1/packed_packed_loop1/avx-512.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/6x1/packed_packed_loop1/avx-512.S.raw new file mode 100644 index 0000000..a34c40f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/6x1/packed_packed_loop1/avx-512.S.raw @@ -0,0 +1,29 @@ + // Tile size: 6x1 + // Accumulators: 0-5 + // Col regs: 6-11 + // Row regs: 15 + + vbroadcastss zmm15, dword ptr [rcx] + + vmovups zmm10, [rax] + vmulps zmm10, zmm10, zmm15 + vaddps zmm0, zmm0, zmm10 + vmovups zmm11, [rax + 64] + vmulps zmm11, zmm11, zmm15 + vaddps zmm1, zmm1, zmm11 + vmovups zmm12, [rax + 128] + vmulps zmm12, zmm12, zmm15 + vaddps zmm2, zmm2, zmm12 + vmovups zmm13, [rax + 192] + vmulps zmm13, zmm13, zmm15 + vaddps zmm3, zmm3, zmm13 + vmovups zmm14, [rax + 256] + vmulps zmm14, zmm14, zmm15 + vaddps zmm4, zmm4, zmm14 + vmovups zmm15, [rax + 320] + vmulps zmm15, zmm15, zmm15 + vaddps zmm5, zmm5, zmm15 + + + add rcx, 4 + add rax, 384 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/6x2/packed_packed_loop1/avx-512-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/6x2/packed_packed_loop1/avx-512-unroll.S.raw new file mode 100644 index 0000000..58ed8f4 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/6x2/packed_packed_loop1/avx-512-unroll.S.raw @@ -0,0 +1,70 @@ + // Tile size: 6x2 + // Accumulators: 0-9 + // Col regs: zmm10-13 + // Row regs: zmm14-15 + + vmovaps zmm12, [rax] + vbroadcastss zmm14, dword ptr [rcx + 0] + vbroadcastss zmm15, dword ptr [rcx + 4] + vmovaps zmm13, [rax + 64] + + vfmadd231ps zmm0, zmm12, zmm14 + vfmadd231ps zmm6, zmm12, zmm15 + + vmovaps zmm12, [rax + 128] + + vfmadd231ps zmm1, zmm13, zmm14 + vfmadd231ps zmm7, zmm13, zmm15 + + vmovaps zmm13, [rax + 192] + + vfmadd231ps zmm2, zmm12, zmm14 + vfmadd231ps zmm8, zmm12, zmm15 + + vmovaps zmm12, [rax + 256] + + vfmadd231ps zmm3, zmm13, zmm14 + vfmadd231ps zmm9, zmm13, zmm15 + + vmovaps zmm13, [rax + 320] + + vfmadd231ps zmm4, zmm12, zmm14 + vfmadd231ps zmm10, zmm12, zmm15 + + vmovaps zmm12, [rax + 384] + vbroadcastss zmm14, dword ptr [rcx + 8] + + vfmadd231ps zmm5, zmm13, zmm14 + vfmadd231ps zmm11, zmm13, zmm15 + + vbroadcastss zmm15, dword ptr [rcx + 12] + vmovaps zmm13, [rax + 448] + + vfmadd231ps zmm0, zmm12, zmm14 + vfmadd231ps zmm6, zmm12, zmm15 + + vmovaps zmm12, [rax + 512] + + vfmadd231ps zmm1, zmm13, zmm14 + vfmadd231ps zmm7, zmm13, zmm15 + + vmovaps zmm13, [rax + 576] + + vfmadd231ps zmm2, zmm12, zmm14 + vfmadd231ps zmm8, zmm12, zmm15 + + vmovaps zmm12, [rax + 640] + + vfmadd231ps zmm3, zmm13, zmm14 + vfmadd231ps zmm9, zmm13, zmm15 + + vmovaps zmm13, [rax + 704] + + vfmadd231ps zmm4, zmm12, zmm14 + vfmadd231ps zmm10, zmm12, zmm15 + + vfmadd231ps zmm5, zmm13, zmm14 + vfmadd231ps zmm11, zmm13, zmm15 + + add rax, 768 + add rcx, 16 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/6x2/packed_packed_loop1/avx-512.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/6x2/packed_packed_loop1/avx-512.S.raw new file mode 100644 index 0000000..0fa5fa8 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/6x2/packed_packed_loop1/avx-512.S.raw @@ -0,0 +1,38 @@ + // Tile size: 6x2 + // Accumulators: 0-11 + // Col regs: 12-13 + // Row regs: 14-15 + + vmovaps zmm12, [rax] + vbroadcastss zmm14, dword ptr [rcx + 0] + vbroadcastss zmm15, dword ptr [rcx + 4] + vmovaps zmm13, [rax + 64] + + vfmadd231ps zmm0, zmm12, zmm14 + vfmadd231ps zmm6, zmm12, zmm15 + + vmovaps zmm12, [rax + 128] + + vfmadd231ps zmm1, zmm13, zmm14 + vfmadd231ps zmm7, zmm13, zmm15 + + vmovaps zmm13, [rax + 192] + + vfmadd231ps zmm2, zmm12, zmm14 + vfmadd231ps zmm8, zmm12, zmm15 + + vmovaps zmm12, [rax + 256] + + vfmadd231ps zmm3, zmm13, zmm14 + vfmadd231ps zmm9, zmm13, zmm15 + + vmovaps zmm13, [rax + 320] + + vfmadd231ps zmm4, zmm12, zmm14 + vfmadd231ps zmm10, zmm12, zmm15 + + vfmadd231ps zmm5, zmm13, zmm14 + vfmadd231ps zmm11, zmm13, zmm15 + + add rcx, 8 + add rax, 384 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/7x1/packed_packed_loop1/avx-512-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/7x1/packed_packed_loop1/avx-512-unroll.S.raw new file mode 100644 index 0000000..e23d79d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/7x1/packed_packed_loop1/avx-512-unroll.S.raw @@ -0,0 +1,40 @@ + // Tile size: 6x1 + // Accumulators: 0-5 + // Col regs: 6-11 + // Row regs: 15 + + vbroadcastss zmm15, dword ptr [rcx] + + vmovaps zmm7, [rax + 0] + vmovaps zmm8, [rax + 64] + vmovaps zmm9, [rax + 128] + vmovaps zmm10, [rax + 192] + vmovaps zmm11, [rax + 256] + vmovaps zmm12, [rax + 320] + vmovaps zmm13, [rax + 384] + + vfmadd231ps zmm0, zmm7, zmm15 + vfmadd231ps zmm1, zmm8, zmm15 + vfmadd231ps zmm2, zmm9, zmm15 + vfmadd231ps zmm3, zmm10, zmm15 + vfmadd231ps zmm4, zmm11, zmm15 + vfmadd231ps zmm5, zmm12, zmm15 + vfmadd231ps zmm6, zmm13, zmm15 + + vbroadcastss zmm16, dword ptr [rcx + 4] + + vmovaps zmm7, [rax + 448 + 0] + vmovaps zmm8, [rax + 448 + 64] + vmovaps zmm9, [rax + 448 + 128] + vmovaps zmm10, [rax + 448 + 192] + vmovaps zmm11, [rax + 448 + 256] + vmovaps zmm12, [rax + 448 + 320] + vmovaps zmm13, [rax + 448 + 384] + + vfmadd231ps zmm0, zmm7, zmm15 + vfmadd231ps zmm1, zmm8, zmm15 + vfmadd231ps zmm2, zmm9, zmm15 + vfmadd231ps zmm3, zmm10, zmm15 + vfmadd231ps zmm4, zmm11, zmm15 + vfmadd231ps zmm5, zmm12, zmm15 + vfmadd231ps zmm6, zmm13, zmm15 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/7x1/packed_packed_loop1/avx-512.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/7x1/packed_packed_loop1/avx-512.S.raw new file mode 100644 index 0000000..889cb34 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/7x1/packed_packed_loop1/avx-512.S.raw @@ -0,0 +1,21 @@ + // Tile size: 7x1 + // Accumulators: 0-6 + // Col regs: 6-13 + // Row regs: 15 + vbroadcastss zmm15, dword ptr [rcx] + + vmovaps zmm7, [rax + 0] + vmovaps zmm8, [rax + 64] + vmovaps zmm9, [rax + 128] + vmovaps zmm10, [rax + 192] + vmovaps zmm11, [rax + 256] + vmovaps zmm12, [rax + 320] + vmovaps zmm13, [rax + 384] + + vfmadd231ps zmm0, zmm7, zmm15 + vfmadd231ps zmm1, zmm8, zmm15 + vfmadd231ps zmm2, zmm9, zmm15 + vfmadd231ps zmm3, zmm10, zmm15 + vfmadd231ps zmm4, zmm11, zmm15 + vfmadd231ps zmm5, zmm12, zmm15 + vfmadd231ps zmm6, zmm13, zmm15 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/8x1/packed_packed_loop1/avx-512-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/8x1/packed_packed_loop1/avx-512-unroll.S.raw new file mode 100644 index 0000000..96d0d98 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/8x1/packed_packed_loop1/avx-512-unroll.S.raw @@ -0,0 +1,30 @@ + // Tile size: 8x1 + // Accumulators: 0-7 + // Col regs: 8-14 + // Row regs: 15 + + vbroadcastss zmm17, dword ptr [rcx] + + + vfmadd231ps zmm0, zmm17, [rax + 0] + vfmadd231ps zmm1, zmm17, [rax + 64] + vfmadd231ps zmm2, zmm17, [rax + 128] + vfmadd231ps zmm3, zmm17, [rax + 192] + vfmadd231ps zmm4, zmm17, [rax + 256] + vfmadd231ps zmm5, zmm17, [rax + 320] + vfmadd231ps zmm6, zmm17, [rax + 384] + vfmadd231ps zmm7, zmm17, [rax + 448] + + vbroadcastss zmm16, dword ptr [rcx + 4] + + vfmadd231ps zmm0, zmm16, [rax + 0 + 512] + vfmadd231ps zmm1, zmm16, [rax + 64 + 512] + vfmadd231ps zmm2, zmm16, [rax + 128 + 512] + vfmadd231ps zmm3, zmm16, [rax + 192 + 512] + vfmadd231ps zmm4, zmm16, [rax + 256 + 512] + vfmadd231ps zmm5, zmm16, [rax + 320 + 512] + vfmadd231ps zmm6, zmm16, [rax + 384 + 512] + vfmadd231ps zmm7, zmm16, [rax + 448 + 512] + + add rcx, 8 + add rax, 1024 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/8x1/packed_packed_loop1/avx-512.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/8x1/packed_packed_loop1/avx-512.S.raw new file mode 100644 index 0000000..38d57ce --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/8x1/packed_packed_loop1/avx-512.S.raw @@ -0,0 +1,25 @@ + // Tile size: 8x1 + // Accumulators: 0-7 + // Col regs: 8-14 + // Row regs: 15 + + vbroadcastss zmm15, dword ptr [rcx] + + vmovaps zmm8, [rax + 0] + vfmadd231ps zmm0, zmm15, zmm8 + vmovaps zmm9, [rax + 64] + vfmadd231ps zmm1, zmm15, zmm9 + vmovaps zmm10, [rax + 128] + vfmadd231ps zmm2, zmm15, zmm10 + vmovaps zmm11, [rax + 192] + vfmadd231ps zmm3, zmm15, zmm11 + vmovaps zmm12, [rax + 256] + vfmadd231ps zmm4, zmm15, zmm12 + vmovaps zmm13, [rax + 320] + vfmadd231ps zmm5, zmm15, zmm13 + vmovaps zmm14, [rax + 384] + vfmadd231ps zmm6, zmm15, zmm14 + vmovaps zmm8, [rax + 448] + vfmadd231ps zmm7, zmm15, zmm8 + add rcx, 4 + add rax, 512 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/8x2/packed_packed_loop1/avx-512.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/8x2/packed_packed_loop1/avx-512.S.raw new file mode 100644 index 0000000..772651c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/8x2/packed_packed_loop1/avx-512.S.raw @@ -0,0 +1,42 @@ + // Tile size: 8x2 + // Accumulators: 0-15 + // Col regs: 16-23 + // Row regs: 24-25 + + vmovaps zmm16, [rax + 0] + vbroadcastss zmm24, dword ptr [rcx + 0] + vbroadcastss zmm25, dword ptr [rcx + 4] + + vfmadd231ps zmm0, zmm16, zmm24 + vfmadd231ps zmm8, zmm16, zmm25 + + vmovaps zmm17, [rax + 64] + vfmadd231ps zmm1, zmm17, zmm24 + vfmadd231ps zmm9, zmm17, zmm25 + + vmovaps zmm18, [rax + 128] + vfmadd231ps zmm2, zmm18, zmm24 + vfmadd231ps zmm10, zmm18, zmm25 + + vmovaps zmm19, [rax + 192] + vfmadd231ps zmm3, zmm19, zmm24 + vfmadd231ps zmm11, zmm19, zmm25 + + vmovaps zmm20, [rax + 256] + vfmadd231ps zmm4, zmm20, zmm24 + vfmadd231ps zmm12, zmm20, zmm25 + + vmovaps zmm21, [rax + 320] + vfmadd231ps zmm5, zmm21, zmm24 + vfmadd231ps zmm13, zmm21, zmm25 + + vmovaps zmm22, [rax + 384] + vfmadd231ps zmm6, zmm22, zmm24 + vfmadd231ps zmm14, zmm22, zmm25 + + vmovaps zmm23, [rax + 448] + vfmadd231ps zmm7, zmm23, zmm24 + vfmadd231ps zmm15, zmm23, zmm25 + + add rax, 512 + add rcx, 8 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/8x8/packed_packed_loop1/avx-512-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/8x8/packed_packed_loop1/avx-512-unroll.S.raw new file mode 100644 index 0000000..1400fdf --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/8x8/packed_packed_loop1/avx-512-unroll.S.raw @@ -0,0 +1,61 @@ + // Tile size: 1x8 + // Accumulators: 0-7 + // Col regs: 8-14 + // Row regs: 15 + + + vmovaps zmm15, [rax] + + vbroadcastss zmm8, dword ptr [rcx + 0 * 4] + vfmadd231ps zmm0, zmm15, zmm8 + + vbroadcastss zmm9, dword ptr [rcx + 1 * 4] + vfmadd231ps zmm1, zmm15, zmm9 + + vbroadcastss zmm10, dword ptr [rcx + 2 * 4] + vfmadd231ps zmm2, zmm15, zmm10 + + vbroadcastss zmm11, dword ptr [rcx + 3 * 4] + vfmadd231ps zmm3, zmm15, zmm11 + + vbroadcastss zmm12, dword ptr [rcx + 4 * 4] + vfmadd231ps zmm4, zmm15, zmm12 + + vbroadcastss zmm13, dword ptr [rcx + 5 * 4] + vfmadd231ps zmm5, zmm15, zmm13 + + vbroadcastss zmm10, dword ptr [rcx + 6 * 4] + vfmadd231ps zmm6, zmm15, zmm10 + + vbroadcastss zmm11, dword ptr [rcx + 7 * 4] + vfmadd231ps zmm7, zmm15, zmm11 + + + vmovaps zmm15, [rax+64] + + vbroadcastss zmm8, dword ptr [rcx + 8 * 4] + vfmadd231ps zmm0, zmm15, zmm8 + + vbroadcastss zmm9, dword ptr [rcx + 9 * 4] + vfmadd231ps zmm1, zmm15, zmm9 + + vbroadcastss zmm10, dword ptr [rcx + 10 * 4] + vfmadd231ps zmm2, zmm15, zmm10 + + vbroadcastss zmm11, dword ptr [rcx + 11 * 4] + vfmadd231ps zmm3, zmm15, zmm11 + + vbroadcastss zmm12, dword ptr [rcx + 12 * 4] + vfmadd231ps zmm4, zmm15, zmm12 + + vbroadcastss zmm13, dword ptr [rcx + 13 * 4] + vfmadd231ps zmm5, zmm15, zmm13 + + vbroadcastss zmm10, dword ptr [rcx + 14 * 4] + vfmadd231ps zmm6, zmm15, zmm10 + + vbroadcastss zmm11, dword ptr [rcx + 15 * 4] + vfmadd231ps zmm7, zmm15, zmm11 + + add rcx, 64 + add rax, 128 \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/8x8/packed_packed_loop1/avx-512.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/8x8/packed_packed_loop1/avx-512.S.raw new file mode 100644 index 0000000..c08151c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/8x8/packed_packed_loop1/avx-512.S.raw @@ -0,0 +1,33 @@ + // Tile size: 1x8 + // Accumulators: 0-7 + // Col regs: 8-14 + // Row regs: 15 + + vmovaps zmm15, [rax] + + vbroadcastss zmm8, dword ptr [rcx + 0 * 4] + vfmadd231ps zmm0, zmm15, zmm8 + + vbroadcastss zmm9, dword ptr [rcx + 1 * 4] + vfmadd231ps zmm1, zmm15, zmm9 + + vbroadcastss zmm10, dword ptr [rcx + 2 * 4] + vfmadd231ps zmm2, zmm15, zmm10 + + vbroadcastss zmm11, dword ptr [rcx + 3 * 4] + vfmadd231ps zmm3, zmm15, zmm11 + + vbroadcastss zmm12, dword ptr [rcx + 4 * 4] + vfmadd231ps zmm4, zmm15, zmm12 + + vbroadcastss zmm13, dword ptr [rcx + 5 * 4] + vfmadd231ps zmm5, zmm15, zmm13 + + vbroadcastss zmm10, dword ptr [rcx + 6 * 4] + vfmadd231ps zmm6, zmm15, zmm10 + + vbroadcastss zmm11, dword ptr [rcx + 7 * 4] + vfmadd231ps zmm7, zmm15, zmm11 + + add rcx, 32 + add rax, 64 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_128x1.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_128x1.S.j2 new file mode 100644 index 0000000..26ff030 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_128x1.S.j2 @@ -0,0 +1,151 @@ +{# +// vim: set syntax=asm : + +/* mmm 128 x 1 + + zmm0 + zmm1 + ... + zmm7 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of ZMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +#} + +{% set size = "128x1" %}{% set suffix = suffix %}{% set G = G %}{% set arch = "avx512" %}{% include "preamble.j2" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + test rbx, rbx + jz {{L}}non_linear_loop + +{{align}} 16 +{{L}}main_loop_packed_packed: + {% include "8x1/packed_packed_loop1/avx-512.S.raw" %} + + sub rbx, 1 + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +{% set from = 0 %}{% set to = 7 %}{% include "f32_scalars.j2" %} +{% set mr = 128 %}{% set from = 0 %}{% set to = 7 %}{% include "f32_per_rows.j2" %} +{% set mr = 128 %}{% set from = 0 %}{% set to = 7 %}{% include "f32_per_cols.j2" %} +{% set from = 0 %}{% set to = 7 %}{% include "avx512_mmm_load_tile.j2" %} + +{{L}}add_unicast: + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + + cmp rsi, 4 + jne {{L}}add_unicast_generic + + {% for row in range(0, 8) %} + vaddps zmm{{row}}, zmm{{row}}, [ r10 + {{ row * 64 }} ] + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_unicast_generic: + mov eax, 0 + {% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi + {% endfor %} + {% for i in range(0, 4) %} + pinsrd xmm15, eax, {{i}} + add eax, esi + {% endfor %} + {% for i in range(0, 4) %} + pinsrd xmm12, eax, {{i}} + add eax, esi + {% endfor %} + {% for i in range(0, 4) %} + pinsrd xmm13, eax, {{i}} + add eax, esi + {% endfor %} + vperm2f128 ymm14, ymm14, ymm15, 32 + vperm2f128 ymm13, ymm12, ymm13, 32 + vinsertf32x8 zmm14, zmm14, ymm13, 1 + + kxnorw k1, k1, k1 + vgatherdps zmm12{k1}, [r10 + zmm14] + vaddps zmm0, zmm0, zmm12 + + imul esi, 16 + vpbroadcastd zmm15, esi + + {% for j in range(1, 8) %} + vpaddd zmm14, zmm14, zmm15 + kxnorw k1, k1, k1 + vgatherdps zmm12{k1}, [r10 + zmm14] + vaddps zmm{{j}}, zmm{{j}}, zmm12 + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vbroadcastss zmm14, dword ptr [rbx] + +{% for i in range(0, 8) %} + vmovups zmm12, [rax + {{ i * 64 }}] + vfmadd231ps zmm{{i}}, zmm12, zmm14 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + + cmp rsi, 4 + jne {{L}}store_noncontiguous + + test r8, 63 + jnz {{L}}store_unaligned + + {% for row in range(0, 8) %} + vmovaps [r8 + {{ row * 64 }}], zmm{{row}} + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store_unaligned: + {% for row in range(0, 8) %} + vmovups [r8 + {{ row * 64 }}], zmm{{row}} + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store_noncontiguous: + {% for r in range(0, 8) %} + {% for quarter in range(0, 4) %} + vextractf32x4 xmm8, zmm{{r}}, {{quarter}} + {% for row in range(0, 4) %} + vextractps dword ptr [r8], xmm8, {{row}} + add r8, rsi + {% endfor %} + {% endfor %} + {% endfor %} + jmp {{L}}non_linear_loop + +{% set size = "128x1" %}{% set suffix = suffix %}{% set G = G %}{% set L = L %}{% set arch = "avx512" %}{% include "postamble.j2" %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_16x1.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_16x1.S.j2 new file mode 100644 index 0000000..48987c3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_16x1.S.j2 @@ -0,0 +1,147 @@ +{# +// vim: set syntax=asm : + +/* mmm 16 x 1 + + zmm0 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of ZMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +#} + + +{% set size = "16x1" %}{% set suffix = suffix %}{% set G = G %}{% set arch = "avx512" %}{% include "preamble.j2" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + test rbx, rbx + jz {{L}}non_linear_loop + + cmp rbx, 8 + jl {{L}}main_loop_packed_packed_tail + +{{align}} 16 +{{L}}main_loop_packed_packed: + {% include "1x1/packed_packed_loop1/unroll-4.S.raw" %} + + sub rbx, 4 + cmp rbx, 4 + jge {{L}}main_loop_packed_packed + + {% for r in range(1, 4) %} + vaddps zmm0, zmm0, zmm{{r}} + {% endfor %} + + test rbx, rbx + jz {{L}}non_linear_loop + +{{align}} 16 +{{L}}main_loop_packed_packed_tail: + {% include "1x1/packed_packed_loop1/avx-512.S.raw" %} + + sub rbx, 1 + jnz {{L}}main_loop_packed_packed_tail + + jmp {{L}}non_linear_loop + +{% set from = 0 %}{% set to = 0 %}{% include "f32_scalars.j2" %} +{% set mr = 16 %}{% set from = 0 %}{% set to = 0 %}{% include "f32_per_rows.j2" %} +{% set mr = 16 %}{% set from = 0 %}{% set to = 0 %}{% include "f32_per_cols.j2" %} +{% set from = 0 %}{% set to = 0 %}{% include "avx512_mmm_load_tile.j2" %} + +{{L}}add_unicast: + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + + cmp rsi, 4 + jne {{L}}add_unicast_generic + + vaddps zmm0, zmm0, [r10] + + jmp {{L}}non_linear_loop + +{{L}}add_unicast_generic: + mov eax, 0 + {% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi + {% endfor %} + {% for i in range(0, 4) %} + pinsrd xmm15, eax, {{i}} + add eax, esi + {% endfor %} + {% for i in range(0, 4) %} + pinsrd xmm12, eax, {{i}} + add eax, esi + {% endfor %} + {% for i in range(0, 4) %} + pinsrd xmm13, eax, {{i}} + add eax, esi + {% endfor %} + vperm2f128 ymm14, ymm14, ymm15, 32 + vperm2f128 ymm13, ymm12, ymm13, 32 + vinsertf32x8 zmm14, zmm14, ymm13, 1 + + kxnorw k1, k1, k1 + vgatherdps zmm12{k1}, [r10 + zmm14] + vaddps zmm0, zmm0, zmm12 + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vbroadcastss zmm14, dword ptr [rbx] + +{% for i in range(0, 1) %} + vmovups zmm12, [rax + {{ i * 64 }}] + vfmadd231ps zmm{{i}}, zmm12, zmm14 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + + cmp rsi, 4 + jne {{L}}store_noncontiguous + + test r8, 63 + jnz {{L}}store_unaligned + + vmovaps [r8], zmm0 + jmp {{L}}non_linear_loop + +{{L}}store_unaligned: + vmovups [r8], zmm0 + jmp {{L}}non_linear_loop + +{{L}}store_noncontiguous: + {% for quarter in range(0, 4) %} + vextractf32x4 xmm8, zmm0, {{quarter}} + {% for row in range(0, 4) %} + vextractps dword ptr [r8], xmm8, {{row}} + add r8, rsi + {% endfor %} + {% endfor %} + jmp {{L}}non_linear_loop + +{% set size = "16x1" %}{% set suffix = suffix %}{% set G = G %}{% set L = L %}{% set arch = "avx512" %}{% include "postamble.j2" %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_16x12.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_16x12.S.j2 new file mode 100644 index 0000000..1ee83cf --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_16x12.S.j2 @@ -0,0 +1,165 @@ +{# +// vim: set syntax=asm : + +/* mmm 16 x 12 + + zmm0 zmm1 ... zmm11 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of ZMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +#} + + +{% set size = "16x12" %}{% set suffix = suffix %}{% set G = G %}{% set arch = "avx512" %}{% include "preamble.j2" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + test rbx, rbx + jz {{L}}non_linear_loop + +{{align}} 16 +{{L}}main_loop_packed_packed_tail: + {% include "1x12/packed_packed_loop1/avx-512.S.raw" %} + + sub rbx, 1 + jnz {{L}}main_loop_packed_packed_tail + + jmp {{L}}non_linear_loop + +{% set from = 0 %}{% set to = 11 %}{% include "f32_scalars.j2" %} +{% set mr = 16 %}{% set from = 0 %}{% set to = 11 %}{% include "f32_per_rows.j2" %} +{% set mr = 16 %}{% set from = 0 %}{% set to = 11 %}{% include "f32_per_cols.j2" %} +{% set from = 0 %}{% set to = 11 %}{% include "avx512_mmm_load_tile.j2" %} + +{{L}}add_unicast: + + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + mov eax, 0 + +{% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm12, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm13, eax, {{i}} + add eax, esi +{% endfor %} + + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + vperm2f128 ymm13, ymm12, ymm13, 32 // ymm12 <- xmm12::xmm13 + vinsertf32x8 zmm14, zmm14, ymm13, 1 + +{% for i in range(0, 12) %} + kxnorw k1,k1,k1 + vgatherdps zmm12{k1}, [ r10 + zmm14 ] + add r10, rbx + vaddps zmm{{i}}, zmm{{i}}, zmm12 +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vmovups zmm12, zmmword ptr [rax] + +{% for i in range(0, 12) %} + vbroadcastss zmm14, dword ptr [rbx + {{ i * 4 }} ] + vfmadd231ps zmm{{i}}, zmm12, zmm14 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + // tops of cols + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + lea r11, [ r10 + rbx ] + + {% for quarter in range(0, 4) %} + {% for r in range(0, 4) %} + vextractf32x4 xmm{{ r + 12 }}, zmm{{r}}, {{quarter}} + {% endfor %} + {% for row in range(0, 4) %} + {% for i in range(0, 4) %} + vextractps dword ptr [r{{ i + 8 }}], xmm{{ i + 12 }}, {{row}} + add r{{ i + 8 }}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + + mov r8, [rdi + 8] // c ptr + + // tops of cols + lea r8, [ r8 + 4 * rbx ] + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + lea r11, [ r10 + rbx ] + + {% for quarter in range(0, 4) %} + {% for r in range(0, 4) %} + vextractf32x4 xmm{{ r + 12 }}, zmm{{ r + 4 }}, {{quarter}} + {% endfor %} + {% for row in range(0, 4) %} + {% for i in range(0, 4) %} + vextractps dword ptr [r{{ i + 8 }}], xmm{{ i + 12 }}, {{row}} + add r{{ i + 8 }}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + + mov r8, [rdi + 8] // c ptr + + // tops of cols + lea r8, [ r8 + 8 * rbx ] + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + lea r11, [ r10 + rbx ] + + {% for quarter in range(0, 4) %} + {% for r in range(0, 4) %} + vextractf32x4 xmm{{ r + 12 }}, zmm{{ r + 8 }}, {{quarter}} + {% endfor %} + {% for row in range(0, 4) %} + {% for i in range(0, 4) %} + vextractps dword ptr [r{{ i + 8 }}], xmm{{ i + 12 }}, {{row}} + add r{{ i + 8 }}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{% set size = "16x12" %}{% set suffix = suffix %}{% set G = G %}{% set L = L %}{% set arch = "avx512" %}{% include "postamble.j2" %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_16x8.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_16x8.S.j2 new file mode 100644 index 0000000..2a8344d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_16x8.S.j2 @@ -0,0 +1,143 @@ +{# +// vim: set syntax=asm : + +/* mmm 16 x 8 + + zmm0 zmm1 ... zmm8 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of ZMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +#} + + +{% set size = "16x8" %}{% set suffix = suffix %}{% set G = G %}{% set arch = "avx512" %}{% include "preamble.j2" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + test rbx, rbx + jz {{L}}non_linear_loop + + cmp rbx, 2 + jl {{L}}main_loop_packed_packed_tail + +{{align}} 16 +{{L}}main_loop_packed_packed: + {% include "8x8/packed_packed_loop1/avx-512-unroll.S.raw" %} + + sub rbx, 2 + cmp rbx, 2 + jge {{L}}main_loop_packed_packed + + test rbx, rbx + jz {{L}}non_linear_loop + +{{align}} 16 +{{L}}main_loop_packed_packed_tail: + {% include "8x8/packed_packed_loop1/avx-512.S.raw" %} + + sub rbx, 1 + jnz {{L}}main_loop_packed_packed_tail + + jmp {{L}}non_linear_loop + +{% set from = 0 %}{% set to = 7 %}{% include "f32_scalars.j2" %} +{% set mr = 16 %}{% set from = 0 %}{% set to = 7 %}{% include "f32_per_rows.j2" %} +{% set mr = 16 %}{% set from = 0 %}{% set to = 7 %}{% include "f32_per_cols.j2" %} +{% set from = 0 %}{% set to = 7 %}{% include "avx512_mmm_load_tile.j2" %} + +{{L}}add_unicast: + + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + mov eax, 0 + +{% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm12, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm13, eax, {{i}} + add eax, esi +{% endfor %} + + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + vperm2f128 ymm13, ymm12, ymm13, 32 // ymm12 <- xmm12::xmm13 + vinsertf32x8 zmm14, zmm14, ymm13, 1 + +{% for i in range(0, 8) %} + kxnorw k1,k1,k1 + vgatherdps zmm12{k1}, [ r10 + zmm14 ] + add r10, rbx + vaddps zmm{{i}}, zmm{{i}}, zmm12 +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vmovups zmm12, zmmword ptr [rax] + +{% for i in range(0, 8) %} + vbroadcastss zmm14, dword ptr [rbx + {{ i * 4 }} ] + vfmadd231ps zmm{{i}}, zmm12, zmm14 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + // tops of cols + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + lea r12, [ r8 + 4 * rbx ] + lea r11, [ r10 + rbx ] + lea r13, [ r12 + rbx ] + lea r14, [ r12 + 2 * rbx ] + lea r15, [ r13 + 2 * rbx ] + + {% for quarter in range(0, 4) %} + {% for r in range(0, 8) %} + vextractf32x4 xmm{{ r + 8 }}, zmm{{r}}, {{quarter}} + {% endfor %} + {% for row in range(0, 4) %} + {% for i in range(0, 8) %} + vextractps dword ptr [r{{ i + 8 }}], xmm{{ i + 8 }}, {{row}} + add r{{ i + 8 }}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{% set size = "16x8" %}{% set suffix = suffix %}{% set G = G %}{% set L = L %}{% set arch = "avx512" %}{% include "postamble.j2" %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_32x5.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_32x5.S.j2 new file mode 100644 index 0000000..179f0d0 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_32x5.S.j2 @@ -0,0 +1,144 @@ +{# +// vim: set syntax=asm : + +/* mmm 32 x 5: + + zmm0 zmm2 zmm4 zmm6 zmm8 + zmm1 zmm3 zmm5 zmm7 zmm9 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +#} + +{% set size = "32x5" %}{% set suffix = suffix %}{% set G = G %}{% set arch = "avx512" %}{% include "preamble.j2" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + test rbx, rbx + jz {{L}}non_linear_loop + +{{L}}main_loop_packed_packed: + {% include "2x5/packed_packed_loop1/avx-512.S.raw" %} + + dec rbx + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +{% set from = 0 %}{% set to = 9 %}{% include "f32_scalars.j2" %} +{% set mr = 32 %}{% set from = 0 %}{% set to = 9 %}{% include "f32_per_rows.j2" %} +{% set mr = 32 %}{% set from = 0 %}{% set to = 9 %}{% include "f32_per_cols.j2" %} +{% set from = 0 %}{% set to = 9 %}{% include "avx512_mmm_load_tile.j2" %} + +{{L}}add_unicast: + + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + mov eax, 0 + +{% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm12, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm13, eax, {{i}} + add eax, esi +{% endfor %} + + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + vperm2f128 ymm13, ymm12, ymm13, 32 // ymm12 <- xmm12::xmm13 + vinsertf32x8 zmm14, zmm14, ymm13, 1 + +{% for i in range(0, 5) %} + kxnorw k1,k1,k1 + vgatherdps zmm12{k1}, [ r10 + zmm14 ] + add r10, rbx + vaddps zmm{{ i * 2 }}, zmm{{ i * 2 }}, zmm12 +{% endfor %} + + imul esi, 16 + vpbroadcastd zmm15, esi + + mov r10, [rdi + 8] + vpaddd zmm14, zmm14, zmm15 + +{% for i in range(0, 5) %} + kxnorw k1,k1,k1 + vgatherdps zmm12{k1}, [ r10 + zmm14 ] + add r10, rbx + vaddps zmm{{ i * 2 + 1 }}, zmm{{ i * 2 + 1 }}, zmm12 +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vmovups zmm12, zmmword ptr [rax] + vmovups zmm13, zmmword ptr [rax+64] + +{% for i in range(0, 5) %} + vbroadcastss zmm14, dword ptr [rbx + {{ i * 4 }} ] + vfmadd231ps zmm{{ i * 2 }}, zmm12, zmm14 + vfmadd231ps zmm{{ i * 2 + 1 }}, zmm13, zmm14 +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + // tops of cols + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + lea r11, [ r10 + rbx ] + lea r12, [ r10 + 2 * rbx ] + + {% for word in range(0, 2) %} + {% for quarter in range(0, 4) %} + {% for r in range(0, 5) %} + vextractf32x4 xmm{{ r + 11 }}, zmm{{ r * 2 + word }}, {{quarter}} + {% endfor %} + {% for row in range(0, 4) %} + {% for i in range(0, 5) %} + vextractps dword ptr [r{{ i + 8 }}], xmm{{ i + 11 }}, {{row}} + add r{{ i + 8 }}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{% set size = "32x5" %}{% set suffix = suffix %}{% set G = G %}{% set L = L %}{% set arch = "avx512" %}{% include "postamble.j2" %} + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_32x6.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_32x6.S.j2 new file mode 100644 index 0000000..cdfcea2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_32x6.S.j2 @@ -0,0 +1,161 @@ +{# +// vim: set syntax=asm : + +/* mmm 32 x 6: + + zmm0 zmm2 zmm4 zmm6 zmm8 zmm10 + zmm1 zmm3 zmm5 zmm7 zmm9 zmm11 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +#} + +{% set size = "32x6" %}{% set suffix = suffix %}{% set G = G %}{% set arch = "avx512" %}{% include "preamble.j2" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + test rbx, rbx + jz {{L}}non_linear_loop + +{{L}}main_loop_packed_packed: + {% include "2x6/packed_packed_loop1/avx-512.S.raw" %} + + dec rbx + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +{% set from = 0 %}{% set to = 11 %}{% include "f32_scalars.j2" %} +{% set mr = 32 %}{% set from = 0 %}{% set to = 11 %}{% include "f32_per_rows.j2" %} +{% set mr = 32 %}{% set from = 0 %}{% set to = 11 %}{% include "f32_per_cols.j2" %} +{% set from = 0 %}{% set to = 11 %}{% include "avx512_mmm_load_tile.j2" %} + +{{L}}add_unicast: + + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + mov eax, 0 + +{% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm12, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm13, eax, {{i}} + add eax, esi +{% endfor %} + + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + vperm2f128 ymm13, ymm12, ymm13, 32 // ymm12 <- xmm12::xmm13 + vinsertf32x8 zmm14, zmm14, ymm13, 1 + +{% for i in range(0, 6) %} + kxnorw k1,k1,k1 + vgatherdps zmm12{k1}, [ r10 + zmm14 ] + add r10, rbx + vaddps zmm{{ i * 2 }}, zmm{{ i * 2 }}, zmm12 +{% endfor %} + + mov r10, [rdi + 8] + imul esi, 16 + vpbroadcastd zmm15, esi + vpaddd zmm14, zmm14, zmm15 + +{% for i in range(0, 6) %} + kxnorw k1,k1,k1 + vgatherdps zmm12{k1}, [ r10 + zmm14 ] + add r10, rbx + vaddps zmm{{ i * 2 + 1 }}, zmm{{ i * 2 + 1 }}, zmm12 +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vmovups zmm12, zmmword ptr [rax] + vmovups zmm13, zmmword ptr [rax+64] + +{% for i in range(0, 6) %} + vbroadcastss zmm14, dword ptr [rbx + {{ i * 4 }} ] + vfmadd231ps zmm{{ i * 2 }}, zmm12, zmm14 + vfmadd231ps zmm{{ i * 2 + 1 }}, zmm13, zmm14 +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + // tops of cols + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + lea r11, [ r10 + rbx ] + + {% for word in range(0, 2) %} + {% for quarter in range(0, 4) %} + {% for r in range(0, 3) %} + vextractf32x4 xmm{{ r + 12 }}, zmm{{ r * 2 + word }}, {{quarter}} + {% endfor %} + {% for row in range(0, 4) %} + {% for i in range(0, 3) %} + vextractps dword ptr [r{{ i + 8 }}], xmm{{ i + 12 }}, {{row}} + add r{{ i + 8 }}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + {% endfor %} + + // tops of cols + mov r8, r11 + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + + {% for word in range(0, 2) %} + {% for quarter in range(0, 4) %} + {% for r in range(0, 3) %} + vextractf32x4 xmm{{ r + 12 }}, zmm{{ (r + 3) * 2 + word }}, {{quarter}} + {% endfor %} + {% for row in range(0, 4) %} + {% for i in range(0, 3) %} + vextractps dword ptr [r{{ i + 8 }}], xmm{{ i + 12 }}, {{row}} + add r{{ i + 8 }}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{% set size = "32x6" %}{% set suffix = suffix %}{% set G = G %}{% set L = L %}{% set arch = "avx512" %}{% include "postamble.j2" %} + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_48x4.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_48x4.S.j2 new file mode 100644 index 0000000..945703c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_48x4.S.j2 @@ -0,0 +1,148 @@ +{# +// vim: set syntax=asm : + +/* mmm 48 x 4: + + zmm0 zmm3 zmm6 zmm9 + zmm1 zmm4 zmm7 zmm10 + zmm2 zmm5 zmm8 zmm11 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +#} + +{% set size = "48x4" %}{% set suffix = suffix %}{% set G = G %}{% set arch = "avx512" %}{% include "preamble.j2" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + test rbx, rbx + jz {{L}}non_linear_loop + +{{L}}main_loop_packed_packed: + {% include "3x4/packed_packed_loop1/avx-512.S.raw" %} + + dec rbx + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +{% set from = 0 %}{% set to = 11 %}{% include "f32_scalars.j2" %} +{% set mr = 48 %}{% set from = 0 %}{% set to = 11 %}{% include "f32_per_rows.j2" %} +{% set mr = 48 %}{% set from = 0 %}{% set to = 11 %}{% include "f32_per_cols.j2" %} +{% set from = 0 %}{% set to = 11 %}{% include "avx512_mmm_load_tile.j2" %} + +{{L}}add_unicast: + + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + mov eax, 0 + +{% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm12, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm13, eax, {{i}} + add eax, esi +{% endfor %} + + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + vperm2f128 ymm13, ymm12, ymm13, 32 // ymm12 <- xmm12::xmm13 + vinsertf32x8 zmm14, zmm14, ymm13, 1 + +{% for i in range(0, 4) %} + kxnorw k1,k1,k1 + vgatherdps zmm12{k1}, [ r10 + zmm14 ] + add r10, rbx + vaddps zmm{{ i * 3 }}, zmm{{ i * 3 }}, zmm12 +{% endfor %} + + imul esi, 16 + vpbroadcastd zmm15, esi + +{% for j in range(1, 3) %} + mov r10, [rdi + 8] + vpaddd zmm14, zmm14, zmm15 + + {% for i in range(0, 4) %} + kxnorw k1,k1,k1 + vgatherdps zmm12{k1}, [ r10 + zmm14 ] + add r10, rbx + vaddps zmm{{ i * 3 + j }}, zmm{{ i * 3 + j }}, zmm12 + {% endfor %} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vmovups zmm12, zmmword ptr [rax] + vmovups zmm13, zmmword ptr [rax+64] + vmovups zmm15, zmmword ptr [rax+128] + +{% for i in range(0, 4) %} + vbroadcastss zmm14, dword ptr [rbx + {{ i * 4 }} ] + vfmadd231ps zmm{{ i * 3 }}, zmm12, zmm14 + vfmadd231ps zmm{{ i * 3 + 1 }}, zmm13, zmm14 + vfmadd231ps zmm{{ i * 3 + 2 }}, zmm15, zmm14 +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + // tops of cols + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + lea r11, [ r10 + rbx ] + + {% for word in range(0, 3) %} + {% for quarter in range(0, 4) %} + {% for r in range(0, 4) %} + vextractf32x4 xmm{{ r + 12 }}, zmm{{ r * 3 + word }}, {{quarter}} + {% endfor %} + {% for row in range(0, 4) %} + {% for i in range(0, 4) %} + vextractps dword ptr [r{{ i + 8 }}], xmm{{ i + 12 }}, {{row}} + add r{{ i + 8 }}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{% set size = "48x4" %}{% set suffix = suffix %}{% set G = G %}{% set L = L %}{% set arch = "avx512" %}{% include "postamble.j2" %} + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_64x3.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_64x3.S.j2 new file mode 100644 index 0000000..16b3453 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_64x3.S.j2 @@ -0,0 +1,149 @@ +{# +// vim: set syntax=asm : + +/* mmm 64 x 3: + + zmm0 zmm4 zmm8 + zmm1 zmm5 zmm9 + zmm2 zmm6 zmm10 + zmm3 zmm7 zmm11 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +#} + +{% set size = "64x3" %}{% set suffix = suffix %}{% set G = G %}{% set arch = "avx512" %}{% include "preamble.j2" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + test rbx, rbx + jz {{L}}non_linear_loop + +{{L}}main_loop_packed_packed: + {% include "4x3/packed_packed_loop1/avx-512.S.raw" %} + + dec rbx + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +{% set from = 0 %}{% set to = 11 %}{% include "f32_scalars.j2" %} +{% set mr = 64 %}{% set from = 0 %}{% set to = 11 %}{% include "f32_per_rows.j2" %} +{% set mr = 64 %}{% set from = 0 %}{% set to = 11 %}{% include "f32_per_cols.j2" %} +{% set from = 0 %}{% set to = 11 %}{% include "avx512_mmm_load_tile.j2" %} + +{{L}}add_unicast: + + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + mov eax, 0 + +{% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm12, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm13, eax, {{i}} + add eax, esi +{% endfor %} + + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + vperm2f128 ymm13, ymm12, ymm13, 32 // ymm12 <- xmm12::xmm13 + vinsertf32x8 zmm14, zmm14, ymm13, 1 + +{% for i in range(0, 3) %} + kxnorw k1,k1,k1 + vgatherdps zmm12{k1}, [ r10 + zmm14 ] + add r10, rbx + vaddps zmm{{ i * 4 }}, zmm{{ i * 4 }}, zmm12 +{% endfor %} + + imul esi, 16 + vpbroadcastd zmm15, esi + +{% for j in range(1, 4) %} + mov r10, [rdi + 8] + vpaddd zmm14, zmm14, zmm15 + + {% for i in range(0, 3) %} + kxnorw k1,k1,k1 + vgatherdps zmm12{k1}, [ r10 + zmm14 ] + add r10, rbx + vaddps zmm{{ i * 4 + j }}, zmm{{ i * 4 + j }}, zmm12 + {% endfor %} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vbroadcastss zmm13, dword ptr [rbx] + vbroadcastss zmm14, dword ptr [rbx+4] + vbroadcastss zmm15, dword ptr [rbx+8] + +{% for i in range(0, 4) %} + vmovups zmm12, zmmword ptr [rax+{{ i * 64 }}] + vfmadd231ps zmm{{i}}, zmm12, zmm13 + vfmadd231ps zmm{{ i + 4 }}, zmm12, zmm14 + vfmadd231ps zmm{{ i + 8 }}, zmm12, zmm15 +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + // tops of cols + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + lea r11, [ r10 + rbx ] + + {% for word in range(0, 4) %} + {% for quarter in range(0, 4) %} + {% for r in range(0, 3) %} + vextractf32x4 xmm{{ r + 12 }}, zmm{{ r * 4 + word }}, {{quarter}} + {% endfor %} + {% for row in range(0, 4) %} + {% for i in range(0, 3) %} + vextractps dword ptr [r{{ i + 8 }}], xmm{{ i + 12 }}, {{row}} + add r{{ i + 8 }}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{% set size = "64x3" %}{% set suffix = suffix %}{% set G = G %}{% set L = L %}{% set arch = "avx512" %}{% include "postamble.j2" %} + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_80x2.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_80x2.S.j2 new file mode 100644 index 0000000..6048e36 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_f32_80x2.S.j2 @@ -0,0 +1,148 @@ +{# +// vim: set syntax=asm : + +/* mmm 80 x 2: + + zmm0 zmm5 + zmm1 zmm6 + zmm2 zmm7 + zmm3 zmm8 + zmm4 zmm9 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +#} + +{% set size = "80x2" %}{% set suffix = suffix %}{% set G = G %}{% set arch = "avx512" %}{% include "preamble.j2" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + test rbx, rbx + jz {{L}}non_linear_loop + +{{L}}main_loop_packed_packed: + {% include "5x2/packed_packed_loop1/avx-512.S.raw" %} + + dec rbx + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +{% set from = 0 %}{% set to = 9 %}{% include "f32_scalars.j2" %} +{% set mr = 80 %}{% set from = 0 %}{% set to = 9 %}{% include "f32_per_rows.j2" %} +{% set mr = 80 %}{% set from = 0 %}{% set to = 9 %}{% include "f32_per_cols.j2" %} +{% set from = 0 %}{% set to = 9 %}{% include "avx512_mmm_load_tile.j2" %} + +{{L}}add_unicast: + + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + mov eax, 0 + +{% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm12, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm13, eax, {{i}} + add eax, esi +{% endfor %} + + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + vperm2f128 ymm13, ymm12, ymm13, 32 // ymm12 <- xmm12::xmm13 + vinsertf32x8 zmm14, zmm14, ymm13, 1 + +{% for i in range(0, 2) %} + kxnorw k1,k1,k1 + vgatherdps zmm12{k1}, [ r10 + zmm14 ] + add r10, rbx + vaddps zmm{{ i * 5 }}, zmm{{ i * 5 }}, zmm12 +{% endfor %} + + imul esi, 16 + vpbroadcastd zmm15, esi + +{% for j in range(1, 5) %} + mov r10, [rdi + 8] + vpaddd zmm14, zmm14, zmm15 + + {% for i in range(0, 2) %} + kxnorw k1,k1,k1 + vgatherdps zmm12{k1}, [ r10 + zmm14 ] + add r10, rbx + vaddps zmm{{ i * 5 + j }}, zmm{{ i * 5 + j }}, zmm12 + {% endfor %} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vbroadcastss zmm14, dword ptr [rbx] + vbroadcastss zmm15, dword ptr [rbx+4] + +{% for i in range(0, 5) %} + vmovups zmm12, zmmword ptr [rax+{{ i * 64 }}] + vfmadd231ps zmm{{i}}, zmm12, zmm14 + vfmadd231ps zmm{{ i + 5 }}, zmm12, zmm15 +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + // tops of cols + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + lea r11, [ r10 + rbx ] + + {% for word in range(0, 5) %} + {% for quarter in range(0, 4) %} + {% for r in range(0, 2) %} + vextractf32x4 xmm{{ r + 12 }}, zmm{{ r * 5 + word }}, {{quarter}} + {% endfor %} + {% for row in range(0, 4) %} + {% for i in range(0, 2) %} + vextractps dword ptr [r{{ i + 8 }}], xmm{{ i + 12 }}, {{row}} + add r{{ i + 8 }}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{% set size = "80x2" %}{% set suffix = suffix %}{% set G = G %}{% set L = L %}{% set arch = "avx512" %}{% include "postamble.j2" %} + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_load_tile.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_load_tile.j2 new file mode 100644 index 0000000..c2c23ec --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/avx512_mmm_load_tile.j2 @@ -0,0 +1,9 @@ +// vim: set syntax=asm : + +{{L}}load_tile: + mov r8, [rdi + 8] + {% for reg in range(from, to + 1) %} + vmovups zmm{{reg}}, zmmword ptr [r8 + {{ (reg - from) * 64 }}] + {% endfor %} + + jmp {{L}}non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/dispatcher.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/dispatcher.j2 new file mode 100644 index 0000000..e60fdc6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/dispatcher.j2 @@ -0,0 +1,40 @@ +// vim: set syntax=asm : + +{{L}}non_linear: + +{{L}}non_linear_loop_enter: + sub rdi, 40 +{{L}}non_linear_loop: + add rdi, 40 + mov rax, [rdi] + + mov r8, {{ jump_table | length }} + cmp rax, 0 + cmovl rax, r8 + cmp rax, {{ jump_table | length }} + cmovg rax, r8 + +{% if msvc %} + lea r8, [ offset {{L}}jmp_table ] +{% else %} + lea r8, [ rip + {{L}}jmp_table ] +{% endif %} + movsxd r9, dword ptr [ r8 + rax * 4 ] + lea r8, [ r8 + r9 ] + jmp r8 + +{{L}}jmp_table: +{% for j in jump_table %} + {{long}} {{L}}{{j}}-{{L}}jmp_table +{% endfor %} + {{long}} {{L}}unsupported-{{L}}jmp_table + +{{L}}unsupported: + mov rax, 1 + jmp {{L}}return + + +{{L}}done: + mov rax, 0 + jmp {{L}}return + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/f32_per_cols.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/f32_per_cols.j2 new file mode 100644 index 0000000..773435c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/f32_per_cols.j2 @@ -0,0 +1,9 @@ +// vim: set syntax=asm : +{% from "zmm_ops.j2" import per_col %} + +{{ per_col("per_col_min", "vminps", mr, from, to) }} +{{ per_col("per_col_max", "vmaxps", mr, from, to) }} +{{ per_col("per_col_add", "vaddps", mr, from, to) }} +{{ per_col("per_col_mul", "vmulps", mr, from, to) }} +{{ per_col("per_col_sub", "vsubps", mr, from, to) }} +{{ per_col("per_col_sub_flipped", "vsubps", mr, from, to, flipped=true) }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/f32_per_rows.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/f32_per_rows.j2 new file mode 100644 index 0000000..4318897 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/f32_per_rows.j2 @@ -0,0 +1,9 @@ +// vim: set syntax=asm : +{% from "zmm_ops.j2" import per_row %} + +{{ per_row("per_row_min", "vminps", mr, from, to) }} +{{ per_row("per_row_max", "vmaxps", mr, from, to) }} +{{ per_row("per_row_add", "vaddps", mr, from, to) }} +{{ per_row("per_row_mul", "vmulps", mr, from, to) }} +{{ per_row("per_row_sub", "vsubps", mr, from, to) }} +{{ per_row("per_row_sub_flipped", "vsubps", mr, from, to, flipped=true) }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/f32_scalars.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/f32_scalars.j2 new file mode 100644 index 0000000..40ed655 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/f32_scalars.j2 @@ -0,0 +1,30 @@ +// vim: set syntax=asm : +{% from "zmm_ops.j2" import scalar %} + +{{ scalar("scalar_min", "vminps", from, to) }} +{{ scalar("scalar_max", "vmaxps", from, to) }} +{{ scalar("scalar_add", "vaddps", from, to) }} +{{ scalar("scalar_mul", "vmulps", from, to) }} +{{ scalar("scalar_sub", "vsubps", from, to) }} +{{ scalar("scalar_sub_flipped", "vsubps", from, to, flipped=true) }} + +{{L}}leaky_relu: + // can only use zmm12 to zmm15 + // ymm15 <- alpha + vbroadcastss zmm15, dword ptr [rdi + 8] + // ymm14 <- all zero + vpxorq zmm14, zmm14, zmm14 + + {% for reg in range(from, to + 1) %} + vcmpps k1, zmm{{reg}}, zmm14, 1 // 1 means LT + // ymm12 <- alpha * x if < 0 + vmulps zmm{{reg}} {k1}, zmm{{reg}}, zmm15 + {% endfor %} + // select muled of orginal + + jmp {{L}}non_linear_loop + +{{L}}q_scale: +{{L}}q_shl: +{{L}}q_shr: + jmp {{L}}unsupported diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/i32_per_cols.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/i32_per_cols.j2 new file mode 100644 index 0000000..1047367 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/i32_per_cols.j2 @@ -0,0 +1,9 @@ +// vim: set syntax=asm : +{% from "zmm_ops.j2" import per_col %} + +{{ per_col("per_col_min", "vpminsd", mr, from, to) }} +{{ per_col("per_col_max", "vpmaxsd", mr, from, to) }} +{{ per_col("per_col_add", "vpaddd", mr, from, to) }} +{{ per_col("per_col_mul", "vpmulld", mr, from, to) }} +{{ per_col("per_col_sub", "vpsubd", mr, from, to) }} +{{ per_col("per_col_sub_flipped", "vpsubd", mr, from, to, flipped=true) }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/i32_per_rows.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/i32_per_rows.j2 new file mode 100644 index 0000000..befb8a2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/i32_per_rows.j2 @@ -0,0 +1,9 @@ +// vim: set syntax=asm : +{% from "zmm_ops.j2" import per_row %} + +{{ per_row("per_row_min", "vpminsd", mr, from, to) }} +{{ per_row("per_row_max", "vpmaxsd", mr, from, to) }} +{{ per_row("per_row_add", "vpaddd", mr, from, to) }} +{{ per_row("per_row_mul", "vpmulld", mr, from, to) }} +{{ per_row("per_row_sub", "vpsubd", mr, from, to) }} +{{ per_row("per_row_sub_flipped", "vpsubd", mr, from, to, flipped=true) }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/i32_scalars.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/i32_scalars.j2 new file mode 100644 index 0000000..445d1f1 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/i32_scalars.j2 @@ -0,0 +1,12 @@ +// vim: set syntax=asm : +{% if not arch %} + {% set arch = "ymm" %} +{% endif %} +{% from "zmm_ops.j2" import scalar %} + +{{ scalar("scalar_min", "vpminsd", from, to) }} +{{ scalar("scalar_max", "vpmaxsd", from, to) }} +{{ scalar("scalar_mul", "vpmulld", from, to) }} +{{ scalar("scalar_add", "vpaddd", from, to) }} +{{ scalar("scalar_sub", "vpsubd", from, to) }} +{{ scalar("scalar_sub_flipped", "vpsubd", from, to, flipped=true) }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/postamble.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/postamble.j2 new file mode 100644 index 0000000..ff3071a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/postamble.j2 @@ -0,0 +1,38 @@ +{{L}}return: + ldmxcsr [rsp + 4] + add rsp, 8 + + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + +{% if family == "windows" %} + pop rsi + pop rdi + + vmovaps xmm15, [rsp+16*9] + vmovaps xmm14, [rsp+16*8] + vmovaps xmm13, [rsp+16*7] + vmovaps xmm12, [rsp+16*6] + vmovaps xmm11, [rsp+16*5] + vmovaps xmm10, [rsp+16*4] + vmovaps xmm9, [rsp+16*3] + vmovaps xmm8, [rsp+16*2] + vmovaps xmm7, [rsp+16*1] + vmovaps xmm6, [rsp] +{% endif %} + + mov rsp, rbp + pop rbp + ret + +{% if msvc %} +{{arch}}_mmm_f32_{{size}}_{{suffix}} endp +_text ends +end + +{% else %} +.cfi_endproc +{% endif %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/preamble.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/preamble.j2 new file mode 100644 index 0000000..d4f8aa7 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/preamble.j2 @@ -0,0 +1,63 @@ +{% if msvc %} + +_text segment +{{arch}}_mmm_f32_{{size}}_{{suffix}} proc + +{% else %} + +.intel_syntax noprefix +.text +.p2align 5 +.globl {{G}}{{arch}}_mmm_f32_{{size}}_{{suffix}} +{{G}}{{arch}}_mmm_f32_{{size}}_{{suffix}}: +.cfi_startproc + +{% endif %} + + push rbp + mov rbp, rsp + +{% if family == "windows" %} +// https://www.agner.org/optimize/calling_conventions.pdf xmm6-15 are not scratch +// https://stackoverflow.com/questions/43358429/save-value-of-xmm-registers + and rsp,-16 + lea rsp,[rsp-160] + vmovaps [rsp], xmm6 + vmovaps [rsp+16*1],xmm7 + vmovaps [rsp+16*2],xmm8 + vmovaps [rsp+16*3],xmm9 + vmovaps [rsp+16*4],xmm10 + vmovaps [rsp+16*5],xmm11 + vmovaps [rsp+16*6],xmm12 + vmovaps [rsp+16*7],xmm13 + vmovaps [rsp+16*8],xmm14 + vmovaps [rsp+16*9],xmm15 + + push rdi + push rsi + + mov rdi, rcx + +{% endif %} + + push rbx + push r12 + push r13 + push r14 + push r15 + + sub rsp, 8 + +{% if family == "unix" %} +.cfi_def_cfa_offset 64 +{% endif %} + stmxcsr [rsp + 4] +{% if msvc %} + mov rax, 1FC0h +{% else %} + mov rax, 0x1FC0 +{% endif %} + mov [rsp], eax + ldmxcsr [rsp] + +{% include "dispatcher.j2" %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/sigmoid_f32.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/sigmoid_f32.S.j2 new file mode 100644 index 0000000..bc57bbb --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/sigmoid_f32.S.j2 @@ -0,0 +1,325 @@ +{# +// vim: set syntax=asm : + +// AVX-512 (zmm, 16-wide) sigmoid. Uses a rational (Padé-style) approximation +// of sigmoid clamped to [-18, 18]. Validated against the generic scalar +// reference via sigmoid_frame_tests! (see x86_64_fma.rs). + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of ZMM0-15 and ZMM0-15 + return: rax (+rdx) + +#} + +{% if msvc %} + +_text segment +avx512_sigmoid_f32_{{suffix}} proc + +{% else %} + +.intel_syntax noprefix +.text +.p2align 5 +.globl {{G}}avx512_sigmoid_f32_{{suffix}} +{{G}}avx512_sigmoid_f32_{{suffix}}: +.cfi_startproc +{% endif %} + + push rbp + mov rbp, rsp + + +{% if family == "windows" %} +// https://www.agner.org/optimize/calling_conventions.pdf xmm6-15 are not scratch +// https://stackoverflow.com/questions/43358429/save-value-of-xmm-registers + and rsp,-16 + lea rsp,[rsp-160] + vmovaps [rsp], xmm6 + vmovaps [rsp+16*1],xmm7 + vmovaps [rsp+16*2],xmm8 + vmovaps [rsp+16*3],xmm9 + vmovaps [rsp+16*4],xmm10 + vmovaps [rsp+16*5],xmm11 + vmovaps [rsp+16*6],xmm12 + vmovaps [rsp+16*7],xmm13 + vmovaps [rsp+16*8],xmm14 + vmovaps [rsp+16*9],xmm15 + + // move around arguments to mimick SysV rdi,rsi passing + push rdi + push rsi + mov rdi, rcx + mov rsi, rdx + +{% endif %} + + push rbx + push r12 + push r13 + push r14 + push r15 + + sub rsp, 8 + +{% if family == "unix" %} +// FIXME +// .cfi_def_cfa_offset 64 +{% endif %} + + stmxcsr [rsp + 4] +{% if msvc %} + mov rax, 1FC0h +{% else %} + mov rax, 0x1FC0 +{% endif %} + mov [rsp], eax + ldmxcsr [rsp] +// ---------------------------------------------------------------------- + +{% set offset %}{% if msvc %} offset {%else%} rip + {%endif%} {% endset %} + + cmp rsi, 0 + je {{L}}done + + cmp rsi, 64 + jl {{L}}loop_1 + +{{L}}loop_4: + + vmovaps zmm4, [rdi] + vmovaps zmm5, [rdi + 64] + vmovaps zmm6, [rdi + 128] + vmovaps zmm7, [rdi + 192] + + vbroadcastss zmm0, dword ptr [{{offset}} {{L}}coeffs_num_low] + vbroadcastss zmm1, dword ptr [{{offset}} {{L}}coeffs_num_high] + vbroadcastss zmm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_9] + vbroadcastss zmm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_7] + + vmaxps zmm4, zmm4, zmm0 + vmaxps zmm5, zmm5, zmm0 + vmaxps zmm6, zmm6, zmm0 + vmaxps zmm7, zmm7, zmm0 + vbroadcastss zmm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_5] + + vminps zmm4, zmm4, zmm1 + vminps zmm5, zmm5, zmm1 + vminps zmm6, zmm6, zmm1 + vminps zmm7, zmm7, zmm1 // zmm4..7 <- x + vbroadcastss zmm1, dword ptr [{{offset}} {{L}}coeffs_num_alpha_3] + + vmulps zmm8, zmm4, zmm4 + vmulps zmm9, zmm5, zmm5 + vmulps zmm10, zmm6, zmm6 + vmulps zmm11, zmm7, zmm7 // zmm8..11 <- x^2 + + vmovaps zmm12, zmm2 + vmovaps zmm13, zmm2 + vmovaps zmm14, zmm2 + vmovaps zmm15, zmm2 + vbroadcastss zmm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_1] + vfmadd132ps zmm12, zmm3, zmm8 + vfmadd132ps zmm13, zmm3, zmm9 + vfmadd132ps zmm14, zmm3, zmm10 + vfmadd132ps zmm15, zmm3, zmm11 + vbroadcastss zmm3, dword ptr [{{offset}} {{L}}coeffs_num_beta_10] + vfmadd132ps zmm12, zmm0, zmm8 + vfmadd132ps zmm13, zmm0, zmm9 + vfmadd132ps zmm14, zmm0, zmm10 + vfmadd132ps zmm15, zmm0, zmm11 + vbroadcastss zmm0, dword ptr [{{offset}} {{L}}coeffs_num_beta_8] + vfmadd132ps zmm12, zmm1, zmm8 + vfmadd132ps zmm13, zmm1, zmm9 + vfmadd132ps zmm14, zmm1, zmm10 + vfmadd132ps zmm15, zmm1, zmm11 + vbroadcastss zmm1, dword ptr [{{offset}} {{L}}coeffs_num_beta_6] + vfmadd132ps zmm12, zmm2, zmm8 + vfmadd132ps zmm13, zmm2, zmm9 + vfmadd132ps zmm14, zmm2, zmm10 + vfmadd132ps zmm15, zmm2, zmm11 + vbroadcastss zmm2, dword ptr [{{offset}} {{L}}coeffs_num_beta_4] + vmulps zmm4, zmm4, zmm12 + vmulps zmm5, zmm5, zmm13 + vmulps zmm6, zmm6, zmm14 + vmulps zmm7, zmm7, zmm15 // zmm4..7 <- num + + vmovaps zmm12, zmm3 + vmovaps zmm13, zmm3 + vmovaps zmm14, zmm3 + vmovaps zmm15, zmm3 + vbroadcastss zmm3, dword ptr [{{offset}} {{L}}coeffs_num_beta_2] + vfmadd132ps zmm12, zmm0, zmm8 + vfmadd132ps zmm13, zmm0, zmm9 + vfmadd132ps zmm14, zmm0, zmm10 + vfmadd132ps zmm15, zmm0, zmm11 + vbroadcastss zmm0, dword ptr [{{offset}} {{L}}coeffs_num_beta_0] + vfmadd132ps zmm12, zmm1, zmm8 + vfmadd132ps zmm13, zmm1, zmm9 + vfmadd132ps zmm14, zmm1, zmm10 + vfmadd132ps zmm15, zmm1, zmm11 + vbroadcastss zmm1, dword ptr [{{offset}} {{L}}coeffs_num_half] + vfmadd132ps zmm12, zmm2, zmm8 + vfmadd132ps zmm13, zmm2, zmm9 + vfmadd132ps zmm14, zmm2, zmm10 + vfmadd132ps zmm15, zmm2, zmm11 + vfmadd132ps zmm12, zmm3, zmm8 + vfmadd132ps zmm13, zmm3, zmm9 + vfmadd132ps zmm14, zmm3, zmm10 + vfmadd132ps zmm15, zmm3, zmm11 + vfmadd132ps zmm12, zmm0, zmm8 + vfmadd132ps zmm13, zmm0, zmm9 + vfmadd132ps zmm14, zmm0, zmm10 + vfmadd132ps zmm15, zmm0, zmm11 // zmm12..14 <- denum + + vdivps zmm4, zmm4, zmm12 + vdivps zmm5, zmm5, zmm13 + vdivps zmm6, zmm6, zmm14 + vdivps zmm7, zmm7, zmm15 + vaddps zmm4, zmm4, zmm1 + vaddps zmm5, zmm5, zmm1 + vaddps zmm6, zmm6, zmm1 + vaddps zmm7, zmm7, zmm1 + + vmovaps [rdi], zmm4 + vmovaps [rdi + 64], zmm5 + vmovaps [rdi + 128], zmm6 + vmovaps [rdi + 192], zmm7 + + add rdi, 256 + sub rsi, 64 + cmp rsi, 64 + jg {{L}}loop_4 + + cmp rsi, 0 + je {{L}}done + +{{L}}loop_1: + vmovaps zmm4, [rdi] + + vbroadcastss zmm0, dword ptr [{{offset}} {{L}}coeffs_num_low] + vbroadcastss zmm1, dword ptr [{{offset}} {{L}}coeffs_num_high] + vbroadcastss zmm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_9] + vbroadcastss zmm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_7] + + vmaxps zmm4, zmm4, zmm0 + vbroadcastss zmm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_5] + + vminps zmm4, zmm4, zmm1 // zmm4 <- x + vbroadcastss zmm1, dword ptr [{{offset}} {{L}}coeffs_num_alpha_3] + + vmulps zmm8, zmm4, zmm4 // zmm8 <- x^2 + + vmovaps zmm12, zmm2 + vbroadcastss zmm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_1] + vfmadd132ps zmm12, zmm3, zmm8 + vbroadcastss zmm3, dword ptr [{{offset}} {{L}}coeffs_num_beta_10] + vfmadd132ps zmm12, zmm0, zmm8 + vbroadcastss zmm0, dword ptr [{{offset}} {{L}}coeffs_num_beta_8] + vfmadd132ps zmm12, zmm1, zmm8 + vbroadcastss zmm1, dword ptr [{{offset}} {{L}}coeffs_num_beta_6] + vfmadd132ps zmm12, zmm2, zmm8 + vbroadcastss zmm2, dword ptr [{{offset}} {{L}}coeffs_num_beta_4] + vmulps zmm4, zmm4, zmm12 + + vmovaps zmm12, zmm3 + vbroadcastss zmm3, dword ptr [{{offset}} {{L}}coeffs_num_beta_2] + vfmadd132ps zmm12, zmm0, zmm8 + vbroadcastss zmm0, dword ptr [{{offset}} {{L}}coeffs_num_beta_0] + vfmadd132ps zmm12, zmm1, zmm8 + vbroadcastss zmm1, dword ptr [{{offset}} {{L}}coeffs_num_half] + vfmadd132ps zmm12, zmm2, zmm8 + vfmadd132ps zmm12, zmm3, zmm8 + vfmadd132ps zmm12, zmm0, zmm8 + + vdivps zmm4, zmm4, zmm12 + vaddps zmm4, zmm4, zmm1 + + vmovaps [rdi], zmm4 + add rdi, 64 + sub rsi, 16 + jnz {{L}}loop_1 + +{{L}}done: + +// ---------------------------------------------------------------------- + + ldmxcsr [rsp + 4] + + add rsp, 8 + + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + +{% if family == "windows" %} + pop rsi + pop rdi + + vmovaps xmm15, [rsp+16*9] + vmovaps xmm14, [rsp+16*8] + vmovaps xmm13, [rsp+16*7] + vmovaps xmm12, [rsp+16*6] + vmovaps xmm11, [rsp+16*5] + vmovaps xmm10, [rsp+16*4] + vmovaps xmm9, [rsp+16*3] + vmovaps xmm8, [rsp+16*2] + vmovaps xmm7, [rsp+16*1] + vmovaps xmm6, [rsp] +{% endif %} + + mov rsp, rbp + pop rbp + ret + +{% set float %}{% if msvc %} real4 {%else%} .float {%endif%}{% endset %} + +{{L}}coeffs_num_low: + {{float}} -18.0 // low +{{L}}coeffs_num_high: + {{float}} 18.0 // high + +{{L}}coeffs_num_alpha_9: + {{float}} 4.37031012579801e-11 // alpha_9 +{{L}}coeffs_num_alpha_7: + {{float}} 1.15627324459942e-07 // alpha_7 +{{L}}coeffs_num_alpha_5: + {{float}} 6.08574864600143e-05 // alpha_5 +{{L}}coeffs_num_alpha_3: + {{float}} 8.51377133304701e-03 // alpha_3 +{{L}}coeffs_num_alpha_1: + {{float}} 2.48287947061529e-01 // alpha_1 + +{{L}}coeffs_num_beta_10: + {{float}} 6.10247389755681e-13 +{{L}}coeffs_num_beta_8: + {{float}} 5.76102136993427e-09 +{{L}}coeffs_num_beta_6: + {{float}} 6.29106785017040e-06 // beta_6 +{{L}}coeffs_num_beta_4: + {{float}} 1.70198817374094e-03 // beta_4 +{{L}}coeffs_num_beta_2: + {{float}} 1.16817656904453e-01 // beta_2 +{{L}}coeffs_num_beta_0: + {{float}} 9.93151921023180e-01 // beta_0 + +{{L}}coeffs_num_half: + {{float}} 0.5 + +{% if msvc %} +avx512_sigmoid_f32_{{suffix}} endp +_text ends +end +{% else %} +.cfi_endproc +{% endif %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/tanh_f32.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/tanh_f32.S.j2 new file mode 100644 index 0000000..dd0325b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/tanh_f32.S.j2 @@ -0,0 +1,315 @@ +{# +// vim: set syntax=asm : + +// AVX-512 (zmm, 16-wide) tanh. Polynomial-numerator / polynomial-denominator +// rational approximation of tanh clamped to [-9, 9]. Validated against the +// generic scalar reference via tanh_frame_tests! (see x86_64_fma.rs). + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of ZMM0-15 and ZMM0-15 + return: rax (+rdx) + +#} + +{% if msvc %} + +_text segment +avx512_tanh_f32_{{suffix}} proc + +{% else %} + +.intel_syntax noprefix +.text +.p2align 5 +.globl {{G}}avx512_tanh_f32_{{suffix}} +{{G}}avx512_tanh_f32_{{suffix}}: +.cfi_startproc +{% endif %} + + push rbp + mov rbp, rsp + + +{% if family == "windows" %} +// https://www.agner.org/optimize/calling_conventions.pdf xmm6-15 are not scratch +// https://stackoverflow.com/questions/43358429/save-value-of-xmm-registers + and rsp,-16 + lea rsp,[rsp-160] + vmovaps [rsp], xmm6 + vmovaps [rsp+16*1],xmm7 + vmovaps [rsp+16*2],xmm8 + vmovaps [rsp+16*3],xmm9 + vmovaps [rsp+16*4],xmm10 + vmovaps [rsp+16*5],xmm11 + vmovaps [rsp+16*6],xmm12 + vmovaps [rsp+16*7],xmm13 + vmovaps [rsp+16*8],xmm14 + vmovaps [rsp+16*9],xmm15 + + // move around arguments to mimick SysV rdi,rsi passing + push rdi + push rsi + mov rdi, rcx + mov rsi, rdx + +{% endif %} + + push rbx + push r12 + push r13 + push r14 + push r15 + + sub rsp, 8 + +{% if family == "unix" %} +// FIXME +// .cfi_def_cfa_offset 64 +{% endif %} + + stmxcsr [rsp + 4] +{% if msvc %} + mov rax, 1FC0h +{% else %} + mov rax, 0x1FC0 +{% endif %} + mov [rsp], eax + ldmxcsr [rsp] +// ---------------------------------------------------------------------- + +{% set offset %}{% if msvc %} offset {%else%} rip + {%endif%} {% endset %} + + cmp rsi, 0 + je {{L}}done + + cmp rsi, 64 + jl {{L}}loop_1 + +{{L}}loop_4: + + vmovaps zmm4, [rdi] + vmovaps zmm5, [rdi + 64] + vmovaps zmm6, [rdi + 128] + vmovaps zmm7, [rdi + 192] + + vbroadcastss zmm0, dword ptr [{{offset}} {{L}}coeffs_num_low] + vbroadcastss zmm1, dword ptr [{{offset}} {{L}}coeffs_num_high] + vbroadcastss zmm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_13] + vbroadcastss zmm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_11] + + vmaxps zmm4, zmm4, zmm0 + vmaxps zmm5, zmm5, zmm0 + vmaxps zmm6, zmm6, zmm0 + vmaxps zmm7, zmm7, zmm0 + vbroadcastss zmm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_9] + + vminps zmm4, zmm4, zmm1 + vminps zmm5, zmm5, zmm1 + vminps zmm6, zmm6, zmm1 + vminps zmm7, zmm7, zmm1 // zmm4..7 <- x + vbroadcastss zmm1, dword ptr [{{offset}} {{L}}coeffs_num_alpha_7] + + vmulps zmm8, zmm4, zmm4 + vmulps zmm9, zmm5, zmm5 + vmulps zmm10, zmm6, zmm6 + vmulps zmm11, zmm7, zmm7 // zmm8..11 <- x^2 + + vmovaps zmm12, zmm2 + vmovaps zmm13, zmm2 + vmovaps zmm14, zmm2 + vmovaps zmm15, zmm2 + vbroadcastss zmm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_5] + vfmadd132ps zmm12, zmm3, zmm8 + vfmadd132ps zmm13, zmm3, zmm9 + vfmadd132ps zmm14, zmm3, zmm10 + vfmadd132ps zmm15, zmm3, zmm11 + vbroadcastss zmm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_3] + vfmadd132ps zmm12, zmm0, zmm8 + vfmadd132ps zmm13, zmm0, zmm9 + vfmadd132ps zmm14, zmm0, zmm10 + vfmadd132ps zmm15, zmm0, zmm11 + vbroadcastss zmm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_1] + vfmadd132ps zmm12, zmm1, zmm8 + vfmadd132ps zmm13, zmm1, zmm9 + vfmadd132ps zmm14, zmm1, zmm10 + vfmadd132ps zmm15, zmm1, zmm11 + vbroadcastss zmm1, dword ptr [{{offset}} {{L}}coeffs_num_beta_6] + vfmadd132ps zmm12, zmm2, zmm8 + vfmadd132ps zmm13, zmm2, zmm9 + vfmadd132ps zmm14, zmm2, zmm10 + vfmadd132ps zmm15, zmm2, zmm11 + vbroadcastss zmm2, dword ptr [{{offset}} {{L}}coeffs_num_beta_4] + vfmadd132ps zmm12, zmm3, zmm8 + vfmadd132ps zmm13, zmm3, zmm9 + vfmadd132ps zmm14, zmm3, zmm10 + vfmadd132ps zmm15, zmm3, zmm11 + vbroadcastss zmm3, dword ptr [{{offset}} {{L}}coeffs_num_beta_2] + vfmadd132ps zmm12, zmm0, zmm8 + vfmadd132ps zmm13, zmm0, zmm9 + vfmadd132ps zmm14, zmm0, zmm10 + vfmadd132ps zmm15, zmm0, zmm11 + vbroadcastss zmm0, dword ptr [{{offset}} {{L}}coeffs_num_beta_0] + vmulps zmm4, zmm4, zmm12 + vmulps zmm5, zmm5, zmm13 + vmulps zmm6, zmm6, zmm14 + vmulps zmm7, zmm7, zmm15 // zmm4..7 <- num + + vmovaps zmm12, zmm1 + vmovaps zmm13, zmm1 + vmovaps zmm14, zmm1 + vmovaps zmm15, zmm1 + vfmadd132ps zmm12, zmm2, zmm8 + vfmadd132ps zmm13, zmm2, zmm9 + vfmadd132ps zmm14, zmm2, zmm10 + vfmadd132ps zmm15, zmm2, zmm11 + vfmadd132ps zmm12, zmm3, zmm8 + vfmadd132ps zmm13, zmm3, zmm9 + vfmadd132ps zmm14, zmm3, zmm10 + vfmadd132ps zmm15, zmm3, zmm11 + vfmadd132ps zmm12, zmm0, zmm8 + vfmadd132ps zmm13, zmm0, zmm9 + vfmadd132ps zmm14, zmm0, zmm10 + vfmadd132ps zmm15, zmm0, zmm11 // zmm12..14 <- denum + + vdivps zmm4, zmm4, zmm12 + vdivps zmm5, zmm5, zmm13 + vdivps zmm6, zmm6, zmm14 + vdivps zmm7, zmm7, zmm15 + + vmovaps [rdi], zmm4 + vmovaps [rdi + 64], zmm5 + vmovaps [rdi + 128], zmm6 + vmovaps [rdi + 192], zmm7 + + add rdi, 256 + sub rsi, 64 + cmp rsi, 64 + jg {{L}}loop_4 + + cmp rsi, 0 + je {{L}}done + +{{L}}loop_1: + vmovaps zmm4, [rdi] + + vbroadcastss zmm0, dword ptr [{{offset}} {{L}}coeffs_num_low] + vbroadcastss zmm1, dword ptr [{{offset}} {{L}}coeffs_num_high] + vbroadcastss zmm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_13] + vbroadcastss zmm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_11] + + vmaxps zmm4, zmm4, zmm0 + vbroadcastss zmm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_9] + + vminps zmm4, zmm4, zmm1 // zmm4 <- x + vbroadcastss zmm1, dword ptr [{{offset}} {{L}}coeffs_num_alpha_7] + + vmulps zmm8, zmm4, zmm4 // zmm8 <- x^2 + + vmovaps zmm12, zmm2 + vbroadcastss zmm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_5] + vfmadd132ps zmm12, zmm3, zmm8 + vbroadcastss zmm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_3] + vfmadd132ps zmm12, zmm0, zmm8 + vbroadcastss zmm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_1] + vfmadd132ps zmm12, zmm1, zmm8 + vbroadcastss zmm1, dword ptr [{{offset}} {{L}}coeffs_num_beta_6] + vfmadd132ps zmm12, zmm2, zmm8 + vbroadcastss zmm2, dword ptr [{{offset}} {{L}}coeffs_num_beta_4] + vfmadd132ps zmm12, zmm3, zmm8 + vbroadcastss zmm3, dword ptr [{{offset}} {{L}}coeffs_num_beta_2] + vfmadd132ps zmm12, zmm0, zmm8 + vbroadcastss zmm0, dword ptr [{{offset}} {{L}}coeffs_num_beta_0] + vmulps zmm4, zmm4, zmm12 + + vmovaps zmm12, zmm1 + vfmadd132ps zmm12, zmm2, zmm8 + vfmadd132ps zmm12, zmm3, zmm8 + vfmadd132ps zmm12, zmm0, zmm8 + + vdivps zmm4, zmm4, zmm12 + + vmovaps [rdi], zmm4 + add rdi, 64 + sub rsi, 16 + jnz {{L}}loop_1 + +{{L}}done: + +// ---------------------------------------------------------------------- + + ldmxcsr [rsp + 4] + + add rsp, 8 + + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + +{% if family == "windows" %} + pop rsi + pop rdi + + vmovaps xmm15, [rsp+16*9] + vmovaps xmm14, [rsp+16*8] + vmovaps xmm13, [rsp+16*7] + vmovaps xmm12, [rsp+16*6] + vmovaps xmm11, [rsp+16*5] + vmovaps xmm10, [rsp+16*4] + vmovaps xmm9, [rsp+16*3] + vmovaps xmm8, [rsp+16*2] + vmovaps xmm7, [rsp+16*1] + vmovaps xmm6, [rsp] +{% endif %} + + mov rsp, rbp + pop rbp + ret + +{% set float %}{% if msvc %} real4 {%else%} .float {%endif%}{% endset %} + +{{L}}coeffs_num_low: + {{float}} -9.0 // low +{{L}}coeffs_num_high: + {{float}} 9.0 // high + +{{L}}coeffs_num_alpha_13: + {{float}} -2.76076847742355e-16 // alpha_13 +{{L}}coeffs_num_alpha_11: + {{float}} 2.00018790482477e-13 // alpha_11 +{{L}}coeffs_num_alpha_9: + {{float}} -8.60467152213735e-11 // alpha_9 +{{L}}coeffs_num_alpha_7: + {{float}} 5.12229709037114e-08 // alpha_7 +{{L}}coeffs_num_alpha_5: + {{float}} 1.48572235717979e-05 // alpha_5 +{{L}}coeffs_num_alpha_3: + {{float}} 6.37261928875436e-04 // alpha_3 +{{L}}coeffs_num_alpha_1: + {{float}} 4.89352455891786e-03 // alpha_1 + +{{L}}coeffs_num_beta_6: + {{float}} 1.19825839466702e-06 // beta_6 +{{L}}coeffs_num_beta_4: + {{float}} 1.18534705686654e-04 // beta_4 +{{L}}coeffs_num_beta_2: + {{float}} 2.26843463243900e-03 // beta_2 +{{L}}coeffs_num_beta_0: + {{float}} 4.89352518554385e-03 // beta_0 + +{% if msvc %} +avx512_tanh_f32_{{suffix}} endp +_text ends +end +{% else %} +.cfi_endproc +{% endif %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/zmm_ops.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/zmm_ops.j2 new file mode 100644 index 0000000..d98ed81 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512/zmm_ops.j2 @@ -0,0 +1,70 @@ + +{% macro scalar(label, op, from, to, flipped=false) %} +{{L}}{{label}}: + vbroadcastss zmm12, dword ptr [rdi + 8] + {% if flipped %} + {% for reg in range(from, to + 1) %} + {{op}} zmm{{reg}}, zmm{{reg}}, zmm12 + {% endfor %} + {% else %} + {% for reg in range(from, to + 1) %} + {{op}} zmm{{reg}}, zmm12, zmm{{reg}} + {% endfor %} + {% endif %} + + jmp {{L}}non_linear_loop +{% endmacro %} + +{% macro per_row(label, op, mr, from, to, flipped=false) %} +{{L}}{{label}}: + mov rax, [ rdi + 8 ] + +{% set mr_over_16 = mr // 16 %} +{% set mr_over_16_min_1 = mr // 16 - 1 %} + +{% for ix in range(0, mr_over_16_min_1 + 1) %} + vmovups zmm{{ to + 1 + ix }}, [rax + {{ ix * 64 }}] +{% endfor %} + +{% if flipped %} + {% for acc in range(from, to + 1) %} + {{op}} zmm{{acc}}, zmm{{acc}}, zmm{{ acc % mr_over_16 + to + 1 }} + {% endfor %} +{% else %} + {% for acc in range(from, to + 1) %} + {{op}} zmm{{acc}}, zmm{{ acc % mr_over_16 + to + 1 }}, zmm{{acc}} + {% endfor %} +{% endif %} + + jmp {{L}}non_linear_loop +{% endmacro %} + +{% macro per_col(label, op, mr, from, to, flipped=false) %} +{{L}}{{label}}: + mov rax, [ rdi + 8 ] + +{% set mr_over_16 = mr // 16 %} +{% set mr_over_16_min_1 = mr // 16 - 1 %} + +{% set tmp = to + 1 %} + +{% set cols = (to + 1 - from) // mr_over_16 %} +{% set cols_min_1 = (to + 1 - from) // mr_over_16 - 1 %} +// {{ to - from + 1 }} cols:{{cols}} + +{% for right in range(0, cols_min_1 + 1) %} + vbroadcastss zmm{{tmp}}, dword ptr [ rax ] + add rax, 4 + + {% for down in range(0, mr_over_16_min_1 + 1) %} + {% set acc = mr_over_16 * right + from + down %} + {% if flipped %} + {{op}} zmm{{acc}}, zmm{{acc}}, zmm{{tmp}} + {% else %} + {{op}} zmm{{acc}}, zmm{{tmp}}, zmm{{acc}} + {% endif %} + {% endfor %} +{% endfor %} + + jmp {{L}}non_linear_loop +{% endmacro %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512vnni/dummy_vnni.S b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512vnni/dummy_vnni.S new file mode 100644 index 0000000..293f07f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/avx512vnni/dummy_vnni.S @@ -0,0 +1,13 @@ +// Build-time capability probe for the assembler, used by build.rs +// (assembler_supports_avx512vnni). Older binutils — notably the Debian stretch +// x86_64 toolchain in CI — predate AVX-512 VNNI (added in binutils ~2.30) and +// cannot assemble `vpdpbusd ymm` even when targeting a VNNI-capable CPU. If +// this file fails to assemble, build.rs skips the VNNI kernel and the +// `tract_avx512vnni` cfg, and the runtime falls back to the AVX2 i32 path. +// Not linked into anything. +.intel_syntax noprefix +.text +.globl tract_avx512vnni_probe +tract_avx512vnni_probe: + vpdpbusd ymm0, ymm1, ymm2 + ret diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/10x1/packed_packed_loop1/avx-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/10x1/packed_packed_loop1/avx-unroll.S.raw new file mode 100644 index 0000000..93e5699 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/10x1/packed_packed_loop1/avx-unroll.S.raw @@ -0,0 +1,58 @@ + // Accumulators: 0-7 + // Columns: 14-15 + // Rows: 8-13 + + vbroadcastss ymm15, dword ptr [rcx] + + vmovaps ymm10, [rax + 0] + vmovaps ymm11, [rax + 32] + vmovaps ymm12, [rax + 64] + vmovaps ymm13, [rax + 96] + vmovaps ymm14, [rax + 128] + + vfmadd231ps ymm0, ymm10, ymm15 + vfmadd231ps ymm1, ymm11, ymm15 + vfmadd231ps ymm2, ymm12, ymm15 + vfmadd231ps ymm3, ymm13, ymm15 + vfmadd231ps ymm4, ymm14, ymm15 + + vmovaps ymm10, [rax + 160] + vmovaps ymm11, [rax + 192] + vmovaps ymm12, [rax + 224] + vmovaps ymm13, [rax + 256] + vmovaps ymm14, [rax + 288] + + vfmadd231ps ymm5, ymm10, ymm15 + vfmadd231ps ymm6, ymm11, ymm15 + vfmadd231ps ymm7, ymm12, ymm15 + vfmadd231ps ymm8, ymm13, ymm15 + vfmadd231ps ymm9, ymm14, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 4] + + vmovaps ymm10, [rax + 320] + vmovaps ymm11, [rax + 352] + vmovaps ymm12, [rax + 384] + vmovaps ymm13, [rax + 416] + vmovaps ymm14, [rax + 448] + + vfmadd231ps ymm0, ymm10, ymm15 + vfmadd231ps ymm1, ymm11, ymm15 + vfmadd231ps ymm2, ymm12, ymm15 + vfmadd231ps ymm3, ymm13, ymm15 + vfmadd231ps ymm4, ymm14, ymm15 + + vmovaps ymm10, [rax + 480] + vmovaps ymm11, [rax + 512] + vmovaps ymm12, [rax + 544] + vmovaps ymm13, [rax + 576] + vmovaps ymm14, [rax + 608] + + vfmadd231ps ymm5, ymm10, ymm15 + vfmadd231ps ymm6, ymm11, ymm15 + vfmadd231ps ymm7, ymm12, ymm15 + vfmadd231ps ymm8, ymm13, ymm15 + vfmadd231ps ymm9, ymm14, ymm15 + + add rcx, 8 + add rax, 640 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/10x1/packed_packed_loop1/avx.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/10x1/packed_packed_loop1/avx.S.raw new file mode 100644 index 0000000..d29f839 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/10x1/packed_packed_loop1/avx.S.raw @@ -0,0 +1,33 @@ + // Tile size: 10x1 + // Accumulators: 0-9 + // Col regs: 10-14 + // Row regs: 15 + + vbroadcastss ymm15, dword ptr [rcx] + + vmovaps ymm10, [rax + 0] + vmovaps ymm11, [rax + 32] + vmovaps ymm12, [rax + 64] + vmovaps ymm13, [rax + 96] + vmovaps ymm14, [rax + 128] + + vfmadd231ps ymm0, ymm10, ymm15 + vfmadd231ps ymm1, ymm11, ymm15 + vfmadd231ps ymm2, ymm12, ymm15 + vfmadd231ps ymm3, ymm13, ymm15 + vfmadd231ps ymm4, ymm14, ymm15 + + vmovaps ymm10, [rax + 160] + vmovaps ymm11, [rax + 192] + vmovaps ymm12, [rax + 224] + vmovaps ymm13, [rax + 256] + vmovaps ymm14, [rax + 288] + + vfmadd231ps ymm5, ymm10, ymm15 + vfmadd231ps ymm6, ymm11, ymm15 + vfmadd231ps ymm7, ymm12, ymm15 + vfmadd231ps ymm8, ymm13, ymm15 + vfmadd231ps ymm9, ymm14, ymm15 + + add rcx, 4 + add rax, 320 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/2x5/packed_packed_loop1/avx-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/2x5/packed_packed_loop1/avx-unroll.S.raw new file mode 100644 index 0000000..6cb8246 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/2x5/packed_packed_loop1/avx-unroll.S.raw @@ -0,0 +1,52 @@ + // Accumulators: 0-9 + // Columns: 14-15 + // Rows: 10-13 + vbroadcastss ymm10, dword ptr [rcx] + vbroadcastss ymm11, dword ptr [rcx + 4] + vbroadcastss ymm12, dword ptr [rcx + 8] + vbroadcastss ymm13, dword ptr [rcx + 12] + + vmovaps ymm14, [rax] + vmovaps ymm15, [rax + 32] + + vfmadd231ps ymm0, ymm14, ymm10 + vfmadd231ps ymm1, ymm15, ymm10 + + vfmadd231ps ymm2, ymm14, ymm11 + vfmadd231ps ymm3, ymm15, ymm11 + + vbroadcastss ymm11, dword ptr [rcx + 16] + + vfmadd231ps ymm4, ymm14, ymm12 + vfmadd231ps ymm5, ymm15, ymm12 + + vfmadd231ps ymm6, ymm14, ymm13 + vfmadd231ps ymm7, ymm15, ymm13 + + vfmadd231ps ymm8, ymm14, ymm11 + vfmadd231ps ymm9, ymm15, ymm11 + + vbroadcastss ymm10, dword ptr [rcx + 20] + vbroadcastss ymm11, dword ptr [rcx + 24] + vbroadcastss ymm12, dword ptr [rcx + 28] + vbroadcastss ymm13, dword ptr [rcx + 32] + + vmovaps ymm14, [rax + 64] + vmovaps ymm15, [rax + 96] + + vfmadd231ps ymm0, ymm14, ymm10 + vfmadd231ps ymm1, ymm15, ymm10 + + vfmadd231ps ymm2, ymm14, ymm11 + vfmadd231ps ymm3, ymm15, ymm11 + + vbroadcastss ymm11, dword ptr [rcx + 36] + + vfmadd231ps ymm4, ymm14, ymm12 + vfmadd231ps ymm5, ymm15, ymm12 + + vfmadd231ps ymm6, ymm14, ymm13 + vfmadd231ps ymm7, ymm15, ymm13 + + vfmadd231ps ymm8, ymm14, ymm11 + vfmadd231ps ymm9, ymm15, ymm11 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/2x5/packed_packed_loop1/avx.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/2x5/packed_packed_loop1/avx.S.raw new file mode 100644 index 0000000..59a29b6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/2x5/packed_packed_loop1/avx.S.raw @@ -0,0 +1,30 @@ + // Accumulators: 0-9 + // Columns: 14-15 + // Rows: 10-13 + vbroadcastss ymm10, dword ptr [rcx] + vbroadcastss ymm11, dword ptr [rcx + 4] + vbroadcastss ymm12, dword ptr [rcx + 8] + vbroadcastss ymm13, dword ptr [rcx + 12] + + vmovaps ymm14, [rax] + vmovaps ymm15, [rax + 32] + + vfmadd231ps ymm0, ymm14, ymm10 + vfmadd231ps ymm1, ymm15, ymm10 + + vfmadd231ps ymm2, ymm14, ymm11 + vfmadd231ps ymm3, ymm15, ymm11 + + // Use register 11 as it's "middle" use, leading to a decent + // trade-off between required use next iteration and when it has + // to be used this iteration. + vbroadcastss ymm11, dword ptr [rcx + 16] + + vfmadd231ps ymm4, ymm14, ymm12 + vfmadd231ps ymm5, ymm15, ymm12 + + vfmadd231ps ymm6, ymm14, ymm13 + vfmadd231ps ymm7, ymm15, ymm13 + + vfmadd231ps ymm8, ymm14, ymm11 + vfmadd231ps ymm9, ymm15, ymm11 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/2x6/packed_packed_loop1/original-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/2x6/packed_packed_loop1/original-unroll.S.raw new file mode 100644 index 0000000..c41328b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/2x6/packed_packed_loop1/original-unroll.S.raw @@ -0,0 +1,71 @@ + // Tile size: 2x6 + // Accumulators: 0-11 + // Col regs: ymm14-15 + // Row regs: ymm12-13 + + vbroadcastss ymm14, dword ptr [rcx] + vmovaps ymm12, [rax] + vmovaps ymm13, [rax + 32] + vbroadcastss ymm15, dword ptr [rcx + 4] + + vfmadd231ps ymm0, ymm12, ymm14 + vfmadd231ps ymm1, ymm13, ymm14 + + vbroadcastss ymm14, dword ptr [rcx + 8] + + vfmadd231ps ymm2, ymm12, ymm15 + vfmadd231ps ymm3, ymm13, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 12] + + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm5, ymm13, ymm14 + + vbroadcastss ymm14, dword ptr [rcx + 16] + + vfmadd231ps ymm6, ymm12, ymm15 + vfmadd231ps ymm7, ymm13, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 20] + + vfmadd231ps ymm8, ymm12, ymm14 + vfmadd231ps ymm9, ymm13, ymm14 + + vbroadcastss ymm14, dword ptr [rcx+24] + + vfmadd231ps ymm10, ymm12, ymm15 + vfmadd231ps ymm11, ymm13, ymm15 + + // Iteration two + vmovaps ymm12, [rax + 64] + vmovaps ymm13, [rax + 96] + vbroadcastss ymm15, dword ptr [rcx + 24 + 4] + + vfmadd231ps ymm0, ymm12, ymm14 + vfmadd231ps ymm1, ymm13, ymm14 + + vbroadcastss ymm14, dword ptr [rcx + 24 + 8] + + vfmadd231ps ymm2, ymm12, ymm15 + vfmadd231ps ymm3, ymm13, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 24 + 12] + + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm5, ymm13, ymm14 + + vbroadcastss ymm14, dword ptr [rcx + 24 + 16] + + vfmadd231ps ymm6, ymm12, ymm15 + vfmadd231ps ymm7, ymm13, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 24 + 20] + + vfmadd231ps ymm8, ymm12, ymm14 + vfmadd231ps ymm9, ymm13, ymm14 + + vfmadd231ps ymm10, ymm12, ymm15 + vfmadd231ps ymm11, ymm13, ymm15 + + add rax, 128 + add rcx, 48 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/2x6/packed_packed_loop1/original.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/2x6/packed_packed_loop1/original.S.raw new file mode 100644 index 0000000..1c7fc27 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/2x6/packed_packed_loop1/original.S.raw @@ -0,0 +1,39 @@ + // Tile size: 2x6 + // Accumulators: 0-11 + // Col regs: ymm14-15 + // Row regs: ymm12-13 + + // Load ordered by earliest use for first 2x2 block + vbroadcastss ymm14, dword ptr [rcx] + vmovaps ymm12, [rax] + vmovaps ymm13, [rax + 32] + vbroadcastss ymm15, dword ptr [rcx + 4] + + vfmadd231ps ymm0, ymm12, ymm14 + vfmadd231ps ymm1, ymm13, ymm14 + + vbroadcastss ymm14, dword ptr [rcx + 8] + + vfmadd231ps ymm2, ymm12, ymm15 + vfmadd231ps ymm3, ymm13, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 12] + + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm5, ymm13, ymm14 + + vbroadcastss ymm14, dword ptr [rcx + 16] + + vfmadd231ps ymm6, ymm12, ymm15 + vfmadd231ps ymm7, ymm13, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 20] + + vfmadd231ps ymm8, ymm12, ymm14 + vfmadd231ps ymm9, ymm13, ymm14 + + vfmadd231ps ymm10, ymm12, ymm15 + vfmadd231ps ymm11, ymm13, ymm15 + + add rax, 64 + add rcx, 24 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/3x4/packed_packed_loop1/avx-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/3x4/packed_packed_loop1/avx-unroll.S.raw new file mode 100644 index 0000000..a9e6ea3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/3x4/packed_packed_loop1/avx-unroll.S.raw @@ -0,0 +1,60 @@ + // Tile size: 3x4 + // Accumulators: 0-11 + // Col regs: ymm12-14 + // Row regs: ymm15 + + vmovaps ymm12, [rax] + vmovaps ymm13, [rax+32] + vmovaps ymm14, [rax+64] + + vbroadcastss ymm15, dword ptr [rcx + 0] + + vfmadd231ps ymm0, ymm12, ymm15 + vfmadd231ps ymm1, ymm13, ymm15 + vfmadd231ps ymm2, ymm14, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 4] + + vfmadd231ps ymm3, ymm12, ymm15 + vfmadd231ps ymm4, ymm13, ymm15 + vfmadd231ps ymm5, ymm14, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 8] + + vfmadd231ps ymm6, ymm12, ymm15 + vfmadd231ps ymm7, ymm13, ymm15 + vfmadd231ps ymm8, ymm14, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 12] + + vfmadd231ps ymm9, ymm12, ymm15 + vfmadd231ps ymm10, ymm13, ymm15 + vfmadd231ps ymm11, ymm14, ymm15 + + vmovaps ymm12, [rax + 96] + vmovaps ymm13, [rax + 128] + vmovaps ymm14, [rax + 160] + + vbroadcastss ymm15, dword ptr [rcx + 16] + + vfmadd231ps ymm0, ymm12, ymm15 + vfmadd231ps ymm1, ymm13, ymm15 + vfmadd231ps ymm2, ymm14, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 20] + + vfmadd231ps ymm3, ymm12, ymm15 + vfmadd231ps ymm4, ymm13, ymm15 + vfmadd231ps ymm5, ymm14, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 24] + + vfmadd231ps ymm6, ymm12, ymm15 + vfmadd231ps ymm7, ymm13, ymm15 + vfmadd231ps ymm8, ymm14, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 28] + + vfmadd231ps ymm9, ymm12, ymm15 + vfmadd231ps ymm10, ymm13, ymm15 + vfmadd231ps ymm11, ymm14, ymm15 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/3x4/packed_packed_loop1/avx.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/3x4/packed_packed_loop1/avx.S.raw new file mode 100644 index 0000000..eff5cd2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/3x4/packed_packed_loop1/avx.S.raw @@ -0,0 +1,32 @@ + // Tile size: 3x4 + // Accumulators: 0-11 + // Col regs: ymm12-14 + // Row regs: ymm15 + + vmovaps ymm12, [rax] + vmovaps ymm13, [rax+32] + vmovaps ymm14, [rax+64] + + vbroadcastss ymm15, dword ptr [rcx + 0] + + vfmadd231ps ymm0, ymm12, ymm15 + vfmadd231ps ymm1, ymm13, ymm15 + vfmadd231ps ymm2, ymm14, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 4] + + vfmadd231ps ymm3, ymm12, ymm15 + vfmadd231ps ymm4, ymm13, ymm15 + vfmadd231ps ymm5, ymm14, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 8] + + vfmadd231ps ymm6, ymm12, ymm15 + vfmadd231ps ymm7, ymm13, ymm15 + vfmadd231ps ymm8, ymm14, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 12] + + vfmadd231ps ymm9, ymm12, ymm15 + vfmadd231ps ymm10, ymm13, ymm15 + vfmadd231ps ymm11, ymm14, ymm15 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/4x3/packed_packed_loop1/avx-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/4x3/packed_packed_loop1/avx-unroll.S.raw new file mode 100644 index 0000000..faaf1ba --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/4x3/packed_packed_loop1/avx-unroll.S.raw @@ -0,0 +1,69 @@ + // Tile size: 4x3 + // Accumulators: 0-11 + // Col regs: ymm12 + // Row regs: ymm13-15 + + // Load col of A + vmovaps ymm12, [rax] + + // Fill 3 cols of B + vbroadcastss ymm13, dword ptr [rcx + 0] + vbroadcastss ymm14, dword ptr [rcx + 4] + vbroadcastss ymm15, dword ptr [rcx + 8] + + // N.B. Stepping cols in inner loop + vfmadd231ps ymm0, ymm12, ymm13 + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm8, ymm12, ymm15 + + vmovaps ymm12, [rax+32] + + vfmadd231ps ymm1, ymm12, ymm13 + vfmadd231ps ymm5, ymm12, ymm14 + vfmadd231ps ymm9, ymm12, ymm15 + + vmovaps ymm12, [rax+64] + + vfmadd231ps ymm2, ymm12, ymm13 + vfmadd231ps ymm6, ymm12, ymm14 + vfmadd231ps ymm10, ymm12, ymm15 + + vmovaps ymm12, [rax+96] + + vfmadd231ps ymm3, ymm12, ymm13 + vfmadd231ps ymm7, ymm12, ymm14 + vfmadd231ps ymm11, ymm12, ymm15 + + // Load col of A, switching col! + vmovaps ymm13, [rax + 128] + + // Fill 3 cols of B + vbroadcastss ymm14, dword ptr [rcx + 12] + vbroadcastss ymm15, dword ptr [rcx + 16] + vbroadcastss ymm12, dword ptr [rcx + 20] + + // N.B. Stepping cols in inner loop + vfmadd231ps ymm0, ymm13, ymm14 + vfmadd231ps ymm4, ymm13, ymm15 + vfmadd231ps ymm8, ymm13, ymm12 + + vmovaps ymm13, [rax + 160] + + vfmadd231ps ymm1, ymm13, ymm14 + vfmadd231ps ymm5, ymm13, ymm15 + vfmadd231ps ymm9, ymm13, ymm12 + + vmovaps ymm13, [rax + 192] + + vfmadd231ps ymm2, ymm13, ymm14 + vfmadd231ps ymm6, ymm13, ymm15 + vfmadd231ps ymm10, ymm13, ymm12 + + vmovaps ymm13, [rax + 224] + + vfmadd231ps ymm3, ymm13, ymm14 + vfmadd231ps ymm7, ymm13, ymm15 + vfmadd231ps ymm11, ymm13, ymm12 + + add rcx, 24 + add rax, 256 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/4x3/packed_packed_loop1/avx.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/4x3/packed_packed_loop1/avx.S.raw new file mode 100644 index 0000000..2a6b432 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/4x3/packed_packed_loop1/avx.S.raw @@ -0,0 +1,38 @@ + // Tile size: 4x3 + // Accumulators: 0-11 + // Col regs: ymm12 + // Row regs: ymm13-15 + + // Load col of A + vmovaps ymm12, [rax] + + // Fill 3 cols of B + vbroadcastss ymm13, dword ptr [rcx + 0] + vbroadcastss ymm14, dword ptr [rcx + 4] + vbroadcastss ymm15, dword ptr [rcx + 8] + + // N.B. Stepping cols in inner loop + vfmadd231ps ymm0, ymm12, ymm13 + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm8, ymm12, ymm15 + + vmovaps ymm12, [rax+32] + + vfmadd231ps ymm1, ymm12, ymm13 + vfmadd231ps ymm5, ymm12, ymm14 + vfmadd231ps ymm9, ymm12, ymm15 + + vmovaps ymm12, [rax+64] + + vfmadd231ps ymm2, ymm12, ymm13 + vfmadd231ps ymm6, ymm12, ymm14 + vfmadd231ps ymm10, ymm12, ymm15 + + vmovaps ymm12, [rax+96] + + vfmadd231ps ymm3, ymm12, ymm13 + vfmadd231ps ymm7, ymm12, ymm14 + vfmadd231ps ymm11, ymm12, ymm15 + + add rcx, 12 + add rax, 128 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/5x2/packed_packed_loop1/avx-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/5x2/packed_packed_loop1/avx-unroll.S.raw new file mode 100644 index 0000000..9327630 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/5x2/packed_packed_loop1/avx-unroll.S.raw @@ -0,0 +1,63 @@ + // Tile size: 5x2 + // Accumulators: 0-9 + // Col regs: ymm10-13 + // Row regs: ymm14-15 + + vmovaps ymm10, [rax] + vbroadcastss ymm14, dword ptr [rcx + 0] + vbroadcastss ymm15, dword ptr [rcx + 4] + vmovaps ymm11, [rax + 32] + + // NB stepping column-wise + vfmadd231ps ymm0, ymm10, ymm14 + vfmadd231ps ymm5, ymm10, ymm15 + + vmovaps ymm12, [rax + 64] + + vfmadd231ps ymm1, ymm11, ymm14 + vfmadd231ps ymm6, ymm11, ymm15 + + vmovaps ymm13, [rax + 96] + + vfmadd231ps ymm2, ymm12, ymm14 + vfmadd231ps ymm7, ymm12, ymm15 + + vmovaps ymm10, [rax + 128] + + vfmadd231ps ymm3, ymm13, ymm14 + vfmadd231ps ymm8, ymm13, ymm15 + + vmovaps ymm11, [rax + 160] + + vfmadd231ps ymm4, ymm10, ymm14 + vfmadd231ps ymm9, ymm10, ymm15 + + vbroadcastss ymm14, dword ptr [rcx + 8] + vbroadcastss ymm15, dword ptr [rcx + 12] + + vmovaps ymm12, [rax + 192] + + // NB stepping column-wise + vfmadd231ps ymm0, ymm11, ymm14 + vfmadd231ps ymm5, ymm11, ymm15 + + vmovaps ymm13, [rax + 224] + + vfmadd231ps ymm1, ymm12, ymm14 + vfmadd231ps ymm6, ymm12, ymm15 + + vmovaps ymm10, [rax + 256] + + vfmadd231ps ymm2, ymm13, ymm14 + vfmadd231ps ymm7, ymm13, ymm15 + + vmovaps ymm11, [rax + 288] + + vfmadd231ps ymm3, ymm10, ymm14 + vfmadd231ps ymm8, ymm10, ymm15 + + vfmadd231ps ymm4, ymm11, ymm14 + vfmadd231ps ymm9, ymm11, ymm15 + + add rax, 320 + add rcx, 16 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/5x2/packed_packed_loop1/avx.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/5x2/packed_packed_loop1/avx.S.raw new file mode 100644 index 0000000..add37ce --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/5x2/packed_packed_loop1/avx.S.raw @@ -0,0 +1,34 @@ + // Tile size: 5x2 + // Accumulators: 0-9 + // Col regs: ymm10-13 + // Row regs: ymm14-15 + + vmovaps ymm10, [rax] + vbroadcastss ymm14, dword ptr [rcx + 0] + vbroadcastss ymm15, dword ptr [rcx + 4] + vmovaps ymm11, [rax + 32] + + // NB stepping column-wise + vfmadd231ps ymm0, ymm10, ymm14 + vfmadd231ps ymm5, ymm10, ymm15 + + vmovaps ymm12, [rax + 64] + + vfmadd231ps ymm1, ymm11, ymm14 + vfmadd231ps ymm6, ymm11, ymm15 + + vmovaps ymm13, [rax + 96] + + vfmadd231ps ymm2, ymm12, ymm14 + vfmadd231ps ymm7, ymm12, ymm15 + + vmovaps ymm11, [rax + 128] + + vfmadd231ps ymm3, ymm13, ymm14 + vfmadd231ps ymm8, ymm13, ymm15 + + vfmadd231ps ymm4, ymm11, ymm14 + vfmadd231ps ymm9, ymm11, ymm15 + + add rax, 160 + add rcx, 8 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/6x1/packed_packed_loop1/avx-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/6x1/packed_packed_loop1/avx-unroll.S.raw new file mode 100644 index 0000000..0d5f738 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/6x1/packed_packed_loop1/avx-unroll.S.raw @@ -0,0 +1,25 @@ + // Tile size: 6x1 + // Accumulators: 0-5 + // Col regs: 6-11 + // Row regs: 15 + + + vbroadcastss ymm15, dword ptr [rcx] + vfmadd231ps ymm0, ymm15, [rax] + vfmadd231ps ymm1, ymm15, [rax + 32] + vfmadd231ps ymm2, ymm15, [rax + 64] + vfmadd231ps ymm3, ymm15, [rax + 96] + vfmadd231ps ymm4, ymm15, [rax + 128] + vfmadd231ps ymm5, ymm15, [rax + 160] + + vbroadcastss ymm14, dword ptr [rcx + 4] + + vfmadd231ps ymm0, ymm14, [rax + 192] + vfmadd231ps ymm1, ymm14, [rax + 224] + vfmadd231ps ymm2, ymm14, [rax + 256] + vfmadd231ps ymm3, ymm14, [rax + 288] + vfmadd231ps ymm4, ymm14, [rax + 320] + vfmadd231ps ymm5, ymm14, [rax + 352] + + add rax, 384 + add rcx, 8 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/6x1/packed_packed_loop1/avx.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/6x1/packed_packed_loop1/avx.S.raw new file mode 100644 index 0000000..b9eb475 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/6x1/packed_packed_loop1/avx.S.raw @@ -0,0 +1,29 @@ + // Tile size: 6x1 + // Accumulators: 0-5 + // Col regs: 6-11 + // Row regs: 15 + + vbroadcastss ymm15, dword ptr [rcx] + + vmovups ymm10, [rax] + vmulps ymm10, ymm10, ymm15 + vaddps ymm0, ymm0, ymm10 + vmovups ymm11, [rax + 32] + vmulps ymm11, ymm11, ymm15 + vaddps ymm1, ymm1, ymm11 + vmovups ymm12, [rax + 64] + vmulps ymm12, ymm12, ymm15 + vaddps ymm2, ymm2, ymm12 + vmovups ymm13, [rax + 96] + vmulps ymm13, ymm13, ymm15 + vaddps ymm3, ymm3, ymm13 + vmovups ymm14, [rax + 128] + vmulps ymm14, ymm14, ymm15 + vaddps ymm4, ymm4, ymm14 + vmovups ymm15, [rax + 160] + vmulps ymm15, ymm15, ymm15 + vaddps ymm5, ymm5, ymm15 + + + add rcx, 4 + add rax, 192 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/6x2/packed_packed_loop1/avx-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/6x2/packed_packed_loop1/avx-unroll.S.raw new file mode 100644 index 0000000..885e84a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/6x2/packed_packed_loop1/avx-unroll.S.raw @@ -0,0 +1,70 @@ + // Tile size: 6x2 + // Accumulators: 0-9 + // Col regs: ymm10-13 + // Row regs: ymm14-15 + + vmovaps ymm12, [rax] + vbroadcastss ymm14, dword ptr [rcx + 0] + vbroadcastss ymm15, dword ptr [rcx + 4] + vmovaps ymm13, [rax + 32] + + vfmadd231ps ymm0, ymm12, ymm14 + vfmadd231ps ymm6, ymm12, ymm15 + + vmovaps ymm12, [rax + 64] + + vfmadd231ps ymm1, ymm13, ymm14 + vfmadd231ps ymm7, ymm13, ymm15 + + vmovaps ymm13, [rax + 96] + + vfmadd231ps ymm2, ymm12, ymm14 + vfmadd231ps ymm8, ymm12, ymm15 + + vmovaps ymm12, [rax + 128] + + vfmadd231ps ymm3, ymm13, ymm14 + vfmadd231ps ymm9, ymm13, ymm15 + + vmovaps ymm13, [rax + 160] + + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm10, ymm12, ymm15 + + vmovaps ymm12, [rax + 192] + vbroadcastss ymm14, dword ptr [rcx + 8] + + vfmadd231ps ymm5, ymm13, ymm14 + vfmadd231ps ymm11, ymm13, ymm15 + + vbroadcastss ymm15, dword ptr [rcx + 12] + vmovaps ymm13, [rax + 224] + + vfmadd231ps ymm0, ymm12, ymm14 + vfmadd231ps ymm6, ymm12, ymm15 + + vmovaps ymm12, [rax + 256] + + vfmadd231ps ymm1, ymm13, ymm14 + vfmadd231ps ymm7, ymm13, ymm15 + + vmovaps ymm13, [rax + 288] + + vfmadd231ps ymm2, ymm12, ymm14 + vfmadd231ps ymm8, ymm12, ymm15 + + vmovaps ymm12, [rax + 320] + + vfmadd231ps ymm3, ymm13, ymm14 + vfmadd231ps ymm9, ymm13, ymm15 + + vmovaps ymm13, [rax + 352] + + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm10, ymm12, ymm15 + + vfmadd231ps ymm5, ymm13, ymm14 + vfmadd231ps ymm11, ymm13, ymm15 + + add rax, 384 + add rcx, 16 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/6x2/packed_packed_loop1/avx.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/6x2/packed_packed_loop1/avx.S.raw new file mode 100644 index 0000000..df8d6f1 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/6x2/packed_packed_loop1/avx.S.raw @@ -0,0 +1,38 @@ + // Tile size: 6x2 + // Accumulators: 0-11 + // Col regs: 12-13 + // Row regs: 14-15 + + vmovaps ymm12, [rax] + vbroadcastss ymm14, dword ptr [rcx + 0] + vbroadcastss ymm15, dword ptr [rcx + 4] + vmovaps ymm13, [rax + 32] + + vfmadd231ps ymm0, ymm12, ymm14 + vfmadd231ps ymm6, ymm12, ymm15 + + vmovaps ymm12, [rax + 64] + + vfmadd231ps ymm1, ymm13, ymm14 + vfmadd231ps ymm7, ymm13, ymm15 + + vmovaps ymm13, [rax + 96] + + vfmadd231ps ymm2, ymm12, ymm14 + vfmadd231ps ymm8, ymm12, ymm15 + + vmovaps ymm12, [rax + 128] + + vfmadd231ps ymm3, ymm13, ymm14 + vfmadd231ps ymm9, ymm13, ymm15 + + vmovaps ymm13, [rax + 160] + + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm10, ymm12, ymm15 + + vfmadd231ps ymm5, ymm13, ymm14 + vfmadd231ps ymm11, ymm13, ymm15 + + add rcx, 8 + add rax, 192 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/7x1/packed_packed_loop1/avx-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/7x1/packed_packed_loop1/avx-unroll.S.raw new file mode 100644 index 0000000..0c52cba --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/7x1/packed_packed_loop1/avx-unroll.S.raw @@ -0,0 +1,37 @@ + // Tile size: 6x1 + // Accumulators: 0-5 + // Col regs: 6-11 + // Row regs: 15 + vbroadcastss ymm15, dword ptr [rcx] + + vmovaps ymm6, [rax + 0] + vmovaps ymm7, [rax + 32] + vmovaps ymm8, [rax + 64] + vmovaps ymm9, [rax + 96] + + vfmadd231ps ymm0, ymm6, ymm15 + vmovaps ymm10, [rax + 128] + + vfmadd231ps ymm1, ymm7, ymm15 + vmovaps ymm11, [rax + 160] + vfmadd231ps ymm2, ymm8, ymm15 + vbroadcastss ymm14, dword ptr [rcx+4] + vfmadd231ps ymm3, ymm9, ymm15 + vmovaps ymm12, [rax + 192] + vfmadd231ps ymm4, ymm10, ymm15 + vmovaps ymm13, [rax + 224] + vfmadd231ps ymm5, ymm11, ymm15 + + vmovaps ymm6, [rax + 256] + vfmadd231ps ymm0, ymm12, ymm14 + vmovaps ymm7, [rax + 288] + vfmadd231ps ymm1, ymm13, ymm14 + + vmovaps ymm8, [rax + 128] + vfmadd231ps ymm2, ymm6, ymm14 + + vmovaps ymm9, [rax + 160] + vfmadd231ps ymm3, ymm7, ymm14 + + vfmadd231ps ymm4, ymm8, ymm14 + vfmadd231ps ymm5, ymm9, ymm14 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/7x1/packed_packed_loop1/avx.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/7x1/packed_packed_loop1/avx.S.raw new file mode 100644 index 0000000..95cd323 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/7x1/packed_packed_loop1/avx.S.raw @@ -0,0 +1,22 @@ + // Tile size: 6x1 + // Accumulators: 0-5 + // Col regs: 6-11 + // Row regs: 15 + vbroadcastss ymm15, dword ptr [rcx] + + vmovaps ymm6, [rax + 0] + vmovaps ymm7, [rax + 32] + vmovaps ymm8, [rax + 64] + vmovaps ymm9, [rax + 96] + + vfmadd231ps ymm0, ymm6, ymm15 + vfmadd231ps ymm1, ymm7, ymm15 + + vmovaps ymm10, [rax + 128] + vfmadd231ps ymm2, ymm8, ymm15 + + vmovaps ymm11, [rax + 160] + vfmadd231ps ymm3, ymm9, ymm15 + + vfmadd231ps ymm4, ymm10, ymm15 + vfmadd231ps ymm5, ymm11, ymm15 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/8x1/packed_packed_loop1/avx-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/8x1/packed_packed_loop1/avx-unroll.S.raw new file mode 100644 index 0000000..2348b2f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/8x1/packed_packed_loop1/avx-unroll.S.raw @@ -0,0 +1,48 @@ + // Accumulators: 0-7 + // Columns: 14-15 + // Rows: 8-13 + + + vbroadcastss ymm15, dword ptr [rcx] + vbroadcastss ymm14, dword ptr [rcx + 4] + + vmovaps ymm8, [rax] + vmovaps ymm9, [rax + 32] + vmovaps ymm10, [rax + 64] + vmovaps ymm11, [rax + 96] + vmovaps ymm12, [rax + 128] + vmovaps ymm13, [rax + 160] + + vfmadd231ps ymm0, ymm15, ymm8 + vfmadd231ps ymm1, ymm15, ymm9 + vfmadd231ps ymm2, ymm15, ymm10 + vfmadd231ps ymm3, ymm15, ymm11 + vfmadd231ps ymm4, ymm15, ymm12 + vfmadd231ps ymm5, ymm15, ymm13 + + vmovaps ymm8, [rax + 192] + vmovaps ymm9, [rax + 224] + vmovaps ymm10, [rax + 256] + vmovaps ymm11, [rax + 288] + vmovaps ymm12, [rax + 320] + vmovaps ymm13, [rax + 352] + + vfmadd231ps ymm6, ymm15, ymm8 + vfmadd231ps ymm7, ymm15, ymm9 + vfmadd231ps ymm0, ymm14, ymm10 + vfmadd231ps ymm1, ymm14, ymm11 + vfmadd231ps ymm2, ymm14, ymm12 + vfmadd231ps ymm3, ymm14, ymm13 + + vmovaps ymm8, [rax + 384] + vmovaps ymm9, [rax + 416] + vmovaps ymm10, [rax + 448] + vmovaps ymm11, [rax + 480] + + vfmadd231ps ymm4, ymm14, ymm8 + vfmadd231ps ymm5, ymm14, ymm9 + vfmadd231ps ymm6, ymm14, ymm10 + vfmadd231ps ymm7, ymm14, ymm11 + + add rcx, 8 + add rax, 512 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/8x1/packed_packed_loop1/avx.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/8x1/packed_packed_loop1/avx.S.raw new file mode 100644 index 0000000..c170e66 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/8x1/packed_packed_loop1/avx.S.raw @@ -0,0 +1,33 @@ + // Tile size: 8x1 + // Accumulators: 0-7 + // Col regs: 8-14 + // Row regs: 15 + + vbroadcastss ymm15, dword ptr [rcx] + + vmovaps ymm8, [rax + 0] + vmovaps ymm9, [rax + 32] + vmovaps ymm10, [rax + 64] + vmovaps ymm11, [rax + 96] + + vfmadd231ps ymm0, ymm8, ymm15 + vfmadd231ps ymm1, ymm9, ymm15 + + vmovaps ymm12, [rax + 128] + vmovaps ymm13, [rax + 160] + + vfmadd231ps ymm2, ymm10, ymm15 + vfmadd231ps ymm3, ymm11, ymm15 + + vmovaps ymm14, [rax + 192] + vmovaps ymm11, [rax + 224] + + vfmadd231ps ymm4, ymm12, ymm15 + vfmadd231ps ymm5, ymm13, ymm15 + + + vfmadd231ps ymm6, ymm14, ymm15 + vfmadd231ps ymm7, ymm11, ymm15 + + add rcx, 4 + add rax, 256 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/8x8/packed_packed_loop1/avx-unroll.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/8x8/packed_packed_loop1/avx-unroll.S.raw new file mode 100644 index 0000000..f8e8193 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/8x8/packed_packed_loop1/avx-unroll.S.raw @@ -0,0 +1,58 @@ + // Tile size: 1x8 + // Accumulators: 0-7 + // Col regs: 8-14 + // Row regs: 15 + + + vmovaps ymm15, [rax] + + vbroadcastss ymm8, dword ptr [rcx + 0 * 4] + vfmadd231ps ymm0, ymm15, ymm8 + + vbroadcastss ymm9, dword ptr [rcx + 1 * 4] + vfmadd231ps ymm1, ymm15, ymm9 + + vbroadcastss ymm10, dword ptr [rcx + 2 * 4] + vfmadd231ps ymm2, ymm15, ymm10 + + vbroadcastss ymm11, dword ptr [rcx + 3 * 4] + vfmadd231ps ymm3, ymm15, ymm11 + + vbroadcastss ymm12, dword ptr [rcx + 4 * 4] + vfmadd231ps ymm4, ymm15, ymm12 + + vbroadcastss ymm13, dword ptr [rcx + 5 * 4] + vfmadd231ps ymm5, ymm15, ymm13 + + vbroadcastss ymm10, dword ptr [rcx + 6 * 4] + vfmadd231ps ymm6, ymm15, ymm10 + + vbroadcastss ymm11, dword ptr [rcx + 7 * 4] + vfmadd231ps ymm7, ymm15, ymm11 + + + vmovaps ymm15, [rax] + + vbroadcastss ymm8, dword ptr [rcx + 0 * 4] + vfmadd231ps ymm0, ymm15, ymm8 + + vbroadcastss ymm9, dword ptr [rcx + 1 * 4] + vfmadd231ps ymm1, ymm15, ymm9 + + vbroadcastss ymm10, dword ptr [rcx + 2 * 4] + vfmadd231ps ymm2, ymm15, ymm10 + + vbroadcastss ymm11, dword ptr [rcx + 3 * 4] + vfmadd231ps ymm3, ymm15, ymm11 + + vbroadcastss ymm12, dword ptr [rcx + 4 * 4] + vfmadd231ps ymm4, ymm15, ymm12 + + vbroadcastss ymm13, dword ptr [rcx + 5 * 4] + vfmadd231ps ymm5, ymm15, ymm13 + + vbroadcastss ymm10, dword ptr [rcx + 6 * 4] + vfmadd231ps ymm6, ymm15, ymm10 + + vbroadcastss ymm11, dword ptr [rcx + 7 * 4] + vfmadd231ps ymm7, ymm15, ymm11 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/8x8/packed_packed_loop1/avx.S.raw b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/8x8/packed_packed_loop1/avx.S.raw new file mode 100644 index 0000000..1af4afe --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/8x8/packed_packed_loop1/avx.S.raw @@ -0,0 +1,30 @@ + // Tile size: 1x8 + // Accumulators: 0-7 + // Col regs: 8-14 + // Row regs: 15 + + vmovaps ymm15, [rax] + + vbroadcastss ymm8, dword ptr [rcx + 0 * 4] + vfmadd231ps ymm0, ymm15, ymm8 + + vbroadcastss ymm9, dword ptr [rcx + 1 * 4] + vfmadd231ps ymm1, ymm15, ymm9 + + vbroadcastss ymm10, dword ptr [rcx + 2 * 4] + vfmadd231ps ymm2, ymm15, ymm10 + + vbroadcastss ymm11, dword ptr [rcx + 3 * 4] + vfmadd231ps ymm3, ymm15, ymm11 + + vbroadcastss ymm12, dword ptr [rcx + 4 * 4] + vfmadd231ps ymm4, ymm15, ymm12 + + vbroadcastss ymm13, dword ptr [rcx + 5 * 4] + vfmadd231ps ymm5, ymm15, ymm13 + + vbroadcastss ymm10, dword ptr [rcx + 6 * 4] + vfmadd231ps ymm6, ymm15, ymm10 + + vbroadcastss ymm11, dword ptr [rcx + 7 * 4] + vfmadd231ps ymm7, ymm15, ymm11 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/avx2_mmm_i32_8x8.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/avx2_mmm_i32_8x8.S.j2 new file mode 100644 index 0000000..5b8160b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/avx2_mmm_i32_8x8.S.j2 @@ -0,0 +1,682 @@ +{# +// vim: set syntax=asm : + +/* mmm 8x8: + + ymm0 ymm1 ymm2 ymm3 ymm4 ymm5 ymm6 ymm7 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +#} + +{% if msvc %} + +_text segment +avx2_mmm_i32_8x8_{{suffix}} proc + +{% else %} + +.intel_syntax noprefix +.text +.p2align 5 +.globl {{G}}avx2_mmm_i32_8x8_{{suffix}} +{{G}}avx2_mmm_i32_8x8_{{suffix}}: +.cfi_startproc + +{% endif %} + + push rbp + mov rbp, rsp + +{% if family == "windows" %} +// https://www.agner.org/optimize/calling_conventions.pdf xmm6-15 are not scratch +// https://stackoverflow.com/questions/43358429/save-value-of-xmm-registers + and rsp,-16 + lea rsp,[rsp-160] + vmovaps [rsp], xmm6 + vmovaps [rsp+16*1],xmm7 + vmovaps [rsp+16*2],xmm8 + vmovaps [rsp+16*3],xmm9 + vmovaps [rsp+16*4],xmm10 + vmovaps [rsp+16*5],xmm11 + vmovaps [rsp+16*6],xmm12 + vmovaps [rsp+16*7],xmm13 + vmovaps [rsp+16*8],xmm14 + vmovaps [rsp+16*9],xmm15 + + push rdi + push rsi + + mov rdi, rcx + +{% endif %} + + push rbx + push r12 + push r13 + push r14 + push r15 + + sub rsp, 8 + +{% if family == "unix" %} +.cfi_def_cfa_offset 64 +{% endif %} + + stmxcsr [rsp + 4] +{% if msvc %} + mov rax, 1FC0h +{% else %} + mov rax, 0x1FC0 +{% endif %} + mov [rsp], eax + ldmxcsr [rsp] + +{% include "dispatcher.j2" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov r12, [rdi + 32] // packing + mov rbx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rcx, [rdi + 8] // k + test rcx, rcx + jz {{L}}non_linear_loop + + cmp r12, 1 + je {{L}}main_loop_packed_packed_i8i8 + +{{L}}main_loop_packed_packed: + vmovaps ymm12, [rax] + + {% for i in range(0, 8) %} + vbroadcastss ymm14, dword ptr [rbx + {{i}} * 4] + vpmulld ymm13, ymm12, ymm14 + vpaddd ymm{{i}}, ymm{{i}}, ymm13 + {% endfor %} + + add rax, 32 + add rbx, 32 + dec rcx + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +{{L}}main_loop_packed_packed_i8i8: + movq xmm8, qword ptr [rax] // read 8 bytes + vpmovsxbw ymm8, xmm8 // promote byte to i32x8 + + vpbroadcastb ymm9, byte ptr [rbx] // broadcast 1 byte from B + vpbroadcastb ymm10, byte ptr [rbx + 1] // broadcast 1 byte from B + vpbroadcastb ymm11, byte ptr [rbx + 2] // broadcast 1 byte from B + vpbroadcastb ymm12, byte ptr [rbx + 3] // broadcast 1 byte from B + vpmovsxbw ymm9, xmm9 // promote byte to i32x8 + vpmovsxbw ymm10, xmm10 // promote byte to i32x8 + vpmovsxbw ymm11, xmm11 // promote byte to i32x8 + vpmovsxbw ymm12, xmm12 // promote byte to i32x8 + + vpmullw ymm9, ymm9, ymm8 + vpmullw ymm10, ymm10, ymm8 + vpmullw ymm11, ymm11, ymm8 + vpmullw ymm12, ymm12, ymm8 + vpmovsxwd ymm9, xmm9 // promote byte to i32x8 + vpmovsxwd ymm10, xmm10 // promote byte to i32x8 + vpmovsxwd ymm11, xmm11 // promote byte to i32x8 + vpmovsxwd ymm12, xmm12 // promote byte to i32x8 + vpaddd ymm0, ymm0, ymm9 + vpaddd ymm1, ymm1, ymm10 + vpaddd ymm2, ymm2, ymm11 + vpaddd ymm3, ymm3, ymm12 + + vpbroadcastb ymm9, byte ptr [rbx + 4] + vpbroadcastb ymm10, byte ptr [rbx + 5] + vpbroadcastb ymm11, byte ptr [rbx + 6] + vpbroadcastb ymm12, byte ptr [rbx + 7] + vpmovsxbw ymm9, xmm9 + vpmovsxbw ymm10, xmm10 + vpmovsxbw ymm11, xmm11 + vpmovsxbw ymm12, xmm12 + + vpmullw ymm9, ymm9, ymm8 + vpmullw ymm10, ymm10, ymm8 + vpmullw ymm11, ymm11, ymm8 + vpmullw ymm12, ymm12, ymm8 + vpmovsxwd ymm9, xmm9 // promote byte to i32x8 + vpmovsxwd ymm10, xmm10 // promote byte to i32x8 + vpmovsxwd ymm11, xmm11 // promote byte to i32x8 + vpmovsxwd ymm12, xmm12 // promote byte to i32x8 + vpaddd ymm4, ymm4, ymm9 + vpaddd ymm5, ymm5, ymm10 + vpaddd ymm6, ymm6, ymm11 + vpaddd ymm7, ymm7, ymm12 + + add rbx, 8 + add rax, 8 + dec rcx + jnz {{L}}main_loop_packed_packed_i8i8 + + jmp {{L}}non_linear_loop + +{% set from = 0 %}{% set to = 7 %}{% include "fma_mmm_i32_scalars.j2" %} +{% set mr = 8 %}{% set from = 0 %}{% set to = 7 %}{% include "fma_mmm_i32_per_rows.j2" %} +{% set mr = 8 %}{% set from = 0 %}{% set to = 7 %}{% include "fma_mmm_i32_per_cols.j2" %} +{% set from = 0 %}{% set to = 7 %}{% include "fma_mmm_load_tile.j2" %} + +{{L}}add_unicast: + + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + mov r8, [rdi + 32] // item size + + cmp r8, 4 + je {{L}}non_linear_addc_i32 + +{# +// This is not great as vgatherdps reads 32-bits values and goes beyond our buffer. Probably harmless though. +// Commented and replaced with the "mov al" loop beyond to pacify valgrind. +// ymm14 and ymm15 are the same as in the non_linear_addc_i32 case (compute them before the test right above here. +// {% for i in range(0, 8) %} +// vpcmpeqd ymm15, ymm15, ymm15 +// vgatherdps ymm12, [ r10 + ymm14 ], ymm15 // 0xxx 1xxx 2xxx 3xxx 4xxx 5xxx 6xxx 7xxx +// +// // we need to go through vpmovsxbd, shuffling naively erases signs +// vpshufb ymm12, ymm12, ymm10 // 0123 0123 0123 0123 4567 4567 4567 4567 +// +// vpermd ymm12, ymm11, ymm12 // 0123 4567 +// vpmovsxbd ymm12, xmm12 // sign extend +// +// vpaddd ymm{{i}}, ymm{{i}}, ymm12 +// add r10, rbx +// {% endfor %} +#} + + {% for col in range(0, 8) %} + mov r8, r10 + {% for half in range(0, 2) %} + {% for lane in range(0, 4) %} + mov al, [ r8 ] + add r8, rsi + movsx eax, al + pinsrd xmm10, eax, {{lane}} + {% endfor %} + vperm2f128 ymm10, ymm10, ymm10, 1 + {% endfor %} + vpaddd ymm{{col}}, ymm{{col}}, ymm10 + add r10, rbx + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}non_linear_addc_i32: + + mov eax, 0 +{% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} + vpermq ymm14, ymm14, 78 // 0b01001110 +{% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} + vpermq ymm14, ymm14, 78 // 0b01001110 + + +{% if msvc %} + vpbroadcastd ymm10, dword ptr [ offset byte_shuffle ] + vmovups ymm11, dword ptr [ offset i128_shuffle ] +{% else %} + vpbroadcastd ymm10, [ rip + {{L}}byte_shuffle ] + vmovups ymm11, [ rip + {{L}}i128_shuffle ] +{% endif %} + +{% for i in range(0, 8) %} + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm12, [ r10 + ymm14 ], ymm15 + vpaddd ymm{{i}}, ymm{{i}}, ymm12 + add r10, rbx +{% endfor %} + + jmp {{L}}non_linear_loop + +{% if msvc %} +.data +byte_shuffle dd 201851904 // 0x0c080400 +i128_shuffle dd 0, 4 +.code +{% else %} +{{L}}byte_shuffle: .int 201851904 // 0x0c080400 +{{L}}i128_shuffle: .int 0, 4 +{% endif %} + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vmovups ymm12, [rax] + +{% for i in range(0, 8) %} + vbroadcastss ymm14, dword ptr [rbx + {{ i * 4 }} ] + vpmulld ymm15, ymm12, ymm14 + vpaddd ymm{{i}}, ymm{{i}}, ymm15 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_scale: + mov r8, [ rdi + 16 ] // policy + vbroadcastss ymm8, dword ptr [rdi + 24] // multi + + mov rax, 1 + movq xmm9, rax + vpbroadcastq ymm9, xmm9 // ymm9 <- 1 + + mov rax, [ rdi + 8 ] // xmm10 <- shift + 31 + add rax, 31 + movq xmm10, rax + vpbroadcastq ymm10, xmm10 + + mov rax, 1 + movq xmm11, rax + vpsubq ymm12, ymm10, ymm9 // shift+31 - 1 + vpsllq ymm11, ymm9, xmm12 // ymm11 <- 1 << (shift + 31 - 1) + + cmp r8, 1 + je {{L}}q_scale_rounding_zero + cmp r8, 2 + je {{L}}q_scale_rounding_away + cmp r8, 3 + je {{L}}q_scale_rounding_minus_inf + cmp r8, 4 + je {{L}}q_scale_rounding_plus_inf + cmp r8, 5 + je {{L}}q_scale_rounding_even + cmp r8, 6 + je {{L}}q_scale_rounding_odd + + jmp {{L}}unsupported + +{{L}}q_scale_rounding_zero: // signum * ( (abs + nudge) >> shift ) +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + vpsrldq ymm15, ymm14, 4 // ymm15 <- a1, a2, a3, a4, a5, a6, a7, 0 + vpmuldq ymm14, ymm14, ymm8 // ymm14 <- a0*c, a2*c, a4*c, a6*c + vpmuldq ymm15, ymm15, ymm8 // ymm15 <- a1*c, a3*c, a5*c, a7*c + + vpaddq ymm14, ymm14, ymm11 + vpaddq ymm15, ymm15, ymm11 + + vpsubq ymm14, ymm14, ymm9 + vpsubq ymm15, ymm15, ymm9 + + vpsrlq ymm14, ymm14, xmm10 + vpsrlq ymm15, ymm15, xmm10 + + vpslldq ymm15, ymm15, 4 + vpblendd ymm14, ymm15, ymm14, 85 // 0x55 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}q_scale_rounding_away: // signum * ( (abs + nudge) >> shift ) +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + vpsrldq ymm15, ymm14, 4 // ymm15 <- a1, a2, a3, a4, a5, a6, a7, 0 + vpmuldq ymm14, ymm14, ymm8 // ymm14 <- a0*c, a2*c, a4*c, a6*c + vpmuldq ymm15, ymm15, ymm8 // ymm15 <- a1*c, a3*c, a5*c, a7*c + + vpaddq ymm14, ymm14, ymm11 + vpaddq ymm15, ymm15, ymm11 + + vpsrlq ymm14, ymm14, xmm10 + vpsrlq ymm15, ymm15, xmm10 + + vpslldq ymm15, ymm15, 4 + vpblendd ymm14, ymm15, ymm14, 85 // 0x55 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}q_scale_rounding_minus_inf: // signum * ( (abs << 32 + 1<<30+shift) >> shift ) +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + // sign extract for nudging in the right direction + vpxor ymm13, ymm13, ymm13 + vpcmpgtd ymm13, ymm{{i}}, ymm13 // ymm13 <- s0, s1, ..s8 (signums, as all ones or all zeros) + vpsrld ymm13, ymm13, 31 // then just 0 or 1 + + vpsrldq ymm15, ymm14, 4 // ymm15 <- a1, a2, a3, a4, a5, a6, a7, 0 + vpmuldq ymm14, ymm14, ymm8 // ymm14 <- a0*c, a2*c, a4*c, a6*c + vpmuldq ymm15, ymm15, ymm8 // ymm15 <- a1*c, a3*c, a5*c, a7*c + + vpaddq ymm14, ymm14, ymm11 + vpaddq ymm15, ymm15, ymm11 + + // reinterpret ymm13=s0i32..s7 as i64 and blend with zero to pick the even ones as i64 + vpxor ymm12, ymm12, ymm12 + vpblendd ymm12, ymm12, ymm13, 85 // 0x55 + vpsubq ymm14, ymm14, ymm12 + + vpsrldq ymm13, ymm13, 4 // ymm13 <- s1, s2, .., s7, 0 + vpxor ymm12, ymm12, ymm12 + vpblendd ymm12, ymm12, ymm13, 85 // 0x55 + vpsubq ymm15, ymm15, ymm12 + + vpsrlq ymm14, ymm14, xmm10 + vpsrlq ymm15, ymm15, xmm10 + + vpslldq ymm15, ymm15, 4 + vpblendd ymm14, ymm15, ymm14, 85 // 0x55 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}q_scale_rounding_plus_inf: // signum * ( (abs << 32 + 1<<30+shift) >> shift ) + + vpbroadcastd ymm9, xmm9 + +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + vpxor ymm13, ymm13, ymm13 + + // sign extract for nudging in the right direction + vpcmpgtd ymm13, ymm{{i}}, ymm13 // ymm13 <- s0, s1, ..s8 (signums, as all ones or all zeros) + vpaddd ymm13, ymm13, ymm9 // if val >= 0 { 0i32 } else { 1i32 } + + vpsrldq ymm15, ymm14, 4 // ymm15 <- a1, a2, a3, a4, a5, a6, a7, 0 + vpmuldq ymm14, ymm14, ymm8 // ymm14 <- a0*c, a2*c, a4*c, a6*c + vpmuldq ymm15, ymm15, ymm8 // ymm15 <- a1*c, a3*c, a5*c, a7*c + + vpaddq ymm14, ymm14, ymm11 + vpaddq ymm15, ymm15, ymm11 + + // reinterpret ymm13=s0i32..s7 as i64 and blend with zero to pick the even ones as i64 + vpxor ymm12, ymm12, ymm12 + vpblendd ymm12, ymm12, ymm13, 85 // 0x55 + vpsubq ymm14, ymm14, ymm12 + + vpsrldq ymm13, ymm13, 4 // ymm13 <- s1, s2, .., s7, 0 + vpxor ymm12, ymm12, ymm12 + vpblendd ymm12, ymm12, ymm13, 85 // 0x55 + vpsubq ymm15, ymm15, ymm12 + + vpsrlq ymm14, ymm14, xmm10 + vpsrlq ymm15, ymm15, xmm10 + + vpslldq ymm15, ymm15, 4 + vpblendd ymm14, ymm15, ymm14, 85 // 0x55 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}q_scale_rounding_even: // signum * ( (abs + nudge) >> shift ) +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + vpsrldq ymm15, ymm14, 4 // ymm15 <- a1, a2, a3, a4, a5, a6, a7, 0 + vpmuldq ymm14, ymm14, ymm8 // ymm14 <- a0*c, a2*c, a4*c, a6*c + vpmuldq ymm15, ymm15, ymm8 // ymm15 <- a1*c, a3*c, a5*c, a7*c + + vpsrlq ymm12, ymm14, xmm10 + vpand ymm12, ymm12, ymm9 + vpaddq ymm14, ymm14, ymm12 + vpsubq ymm14, ymm14, ymm9 + + vpsrlq ymm12, ymm15, xmm10 + vpand ymm12, ymm12, ymm9 + vpaddq ymm15, ymm15, ymm12 + vpsubq ymm15, ymm15, ymm9 + + vpaddq ymm14, ymm14, ymm11 + vpaddq ymm15, ymm15, ymm11 + + vpsrlq ymm14, ymm14, xmm10 + vpsrlq ymm15, ymm15, xmm10 + + vpslldq ymm15, ymm15, 4 + vpblendd ymm14, ymm15, ymm14, 85 // 0x55 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_scale_rounding_odd: // signum * ( (abs + nudge) >> shift ) +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + vpsrldq ymm15, ymm14, 4 // ymm15 <- a1, a2, a3, a4, a5, a6, a7, 0 + vpmuldq ymm14, ymm14, ymm8 // ymm14 <- a0*c, a2*c, a4*c, a6*c + vpmuldq ymm15, ymm15, ymm8 // ymm15 <- a1*c, a3*c, a5*c, a7*c + + vpsrlq ymm12, ymm14, xmm10 + vpand ymm12, ymm12, ymm9 + vpsubq ymm14, ymm14, ymm12 + + vpsrlq ymm12, ymm15, xmm10 + vpand ymm12, ymm12, ymm9 + vpsubq ymm15, ymm15, ymm12 + + vpaddq ymm14, ymm14, ymm11 + vpaddq ymm15, ymm15, ymm11 + + vpsrlq ymm14, ymm14, xmm10 + vpsrlq ymm15, ymm15, xmm10 + + vpslldq ymm15, ymm15, 4 + vpblendd ymm14, ymm15, ymm14, 85 // 0x55 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}q_shl: + mov eax, [ rdi + 8 ] // xmm10 <- -shift (8 times) + movd xmm10, eax + vpbroadcastd ymm10, xmm10 + +{% for i in range(0, 8) %} + vpsllvd ymm{{i}}, ymm{{i}}, ymm10 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_shr: + mov r8, [ rdi + 16 ] // policy + + mov eax, 1 + movd xmm9, eax + vpbroadcastd ymm9, xmm9 // ymm9 <- 1u32 (8 times) + + mov eax, [ rdi + 8 ] // xmm10 <- shift (8 times) + movd xmm10, eax + vpbroadcastd ymm10, xmm10 + + mov ebx, 1 + mov cl, al + sub cl, 1 // rcx <- shift -1 + sal ebx, cl // rbx <- (1 << (shift - 1)) + movd xmm11, ebx + vpbroadcastd ymm11, xmm11 // ymm11 <- "half" + + vpxor ymm12, ymm12, ymm12 // ymm12 <- zeroes + + cmp r8, 1 + je {{L}}q_shr_rounding_zero + cmp r8, 2 + je {{L}}q_shr_rounding_away + cmp r8, 3 + je {{L}}q_shr_rounding_minus_inf + cmp r8, 4 + je {{L}}q_shr_rounding_plus_inf + cmp r8, 5 + je {{L}}q_shr_rounding_even + cmp r8, 6 + je {{L}}q_shr_rounding_odd + + jmp {{L}}unsupported + +{{L}}q_shr_rounding_zero: +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + vpsubd ymm14, ymm14, ymm9 + vpaddd ymm14, ymm14, ymm11 + vpsravd ymm14, ymm14, ymm10 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_shr_rounding_away: +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + vpaddd ymm14, ymm14, ymm11 + vpsravd ymm14, ymm14, ymm10 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_shr_rounding_minus_inf: +{% for i in range(0, 8) %} + vpsubd ymm{{i}}, ymm{{i}}, ymm9 + vpaddd ymm{{i}}, ymm{{i}}, ymm11 + vpsravd ymm{{i}}, ymm{{i}}, ymm10 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_shr_rounding_plus_inf: +{% for i in range(0, 8) %} + vpaddd ymm{{i}}, ymm{{i}}, ymm11 + vpsravd ymm{{i}}, ymm{{i}}, ymm10 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_shr_rounding_even: +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + vpsravd ymm13, ymm14, ymm10 + vpand ymm13, ymm13, ymm9 + vpsubd ymm13, ymm13, ymm9 // nudge = ((abs >>l shift) & 0x01) - 1 + vpaddd ymm14, ymm14, ymm13 // add nudge + vpaddd ymm14, ymm14, ymm11 // add half + vpsravd ymm14, ymm14, ymm10 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_shr_rounding_odd: +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + vpsravd ymm13, ymm14, ymm10 + vpand ymm13, ymm13, ymm9 + vpsubd ymm13, ymm12, ymm13 // nudge = - ((abs >>l shift) & 0x01) + vpaddd ymm14, ymm14, ymm13 // add nudge + vpaddd ymm14, ymm14, ymm11 // add half + vpsravd ymm14, ymm14, ymm10 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rdx, [rdi + 24] // col stride + mov rcx, [rdi + 32] // item size + + cmp rcx, 4 + je {{L}}store_strides_i32 + + {% for col in range(0, 8) %} + mov r10, r8 + {% for row in range(0, 4) %} + extractps ebx, xmm{{col}}, {{row}} + mov byte ptr [r10], bl + add r10, rsi + {% endfor %} + vperm2f128 ymm{{col}}, ymm{{col}}, ymm{{col}}, 1 + {% for row in range(0, 4) %} + extractps ebx, xmm{{col}}, {{row}} + mov byte ptr [r10], bl + add r10, rsi + {% endfor %} + add r8, rdx + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store_strides_i32: + {% for col in range(0, 8) %} + mov r10, r8 + {% for row in range(0, 4) %} + extractps ebx, xmm{{col}}, {{row}} + mov dword ptr [r10], ebx + add r10, rsi + {% endfor %} + vperm2f128 ymm{{col}}, ymm{{col}}, ymm{{col}}, 1 + {% for row in range(0, 4) %} + extractps ebx, xmm{{col}}, {{row}} + mov dword ptr [r10], ebx + add r10, rsi + {% endfor %} + add r8, rdx + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}return: + ldmxcsr [rsp + 4] + add rsp, 8 + + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + +{% if family == "windows" %} + pop rsi + pop rdi + + vmovaps xmm15, [rsp+16*9] + vmovaps xmm14, [rsp+16*8] + vmovaps xmm13, [rsp+16*7] + vmovaps xmm12, [rsp+16*6] + vmovaps xmm11, [rsp+16*5] + vmovaps xmm10, [rsp+16*4] + vmovaps xmm9, [rsp+16*3] + vmovaps xmm8, [rsp+16*2] + vmovaps xmm7, [rsp+16*1] + vmovaps xmm6, [rsp] +{% endif %} + + mov rsp, rbp + pop rbp + ret + + +{{L}}one_32bit: +{% if msvc %} + dd 1 +{% else %} + .int 1 +{% endif %} + +{% if msvc %} +avx2_mmm_i32_8x8_{{suffix}} endp +_text ends +end +{% else %} +.cfi_endproc +{% endif %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/avx512vnni_mmm_i32_8x8.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/avx512vnni_mmm_i32_8x8.S.j2 new file mode 100644 index 0000000..ed5d454 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/avx512vnni_mmm_i32_8x8.S.j2 @@ -0,0 +1,676 @@ +{# +// vim: set syntax=asm : + +/* mmm 8x8: + + ymm0 ymm1 ymm2 ymm3 ymm4 ymm5 ymm6 ymm7 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +#} + +{% if msvc %} + +_text segment +avx512vnni_mmm_i32_8x8_{{suffix}} proc + +{% else %} + +.intel_syntax noprefix +.text +.p2align 5 +.globl {{G}}avx512vnni_mmm_i32_8x8_{{suffix}} +{{G}}avx512vnni_mmm_i32_8x8_{{suffix}}: +.cfi_startproc + +{% endif %} + + push rbp + mov rbp, rsp + +{% if family == "windows" %} +// https://www.agner.org/optimize/calling_conventions.pdf xmm6-15 are not scratch +// https://stackoverflow.com/questions/43358429/save-value-of-xmm-registers + and rsp,-16 + lea rsp,[rsp-160] + vmovaps [rsp], xmm6 + vmovaps [rsp+16*1],xmm7 + vmovaps [rsp+16*2],xmm8 + vmovaps [rsp+16*3],xmm9 + vmovaps [rsp+16*4],xmm10 + vmovaps [rsp+16*5],xmm11 + vmovaps [rsp+16*6],xmm12 + vmovaps [rsp+16*7],xmm13 + vmovaps [rsp+16*8],xmm14 + vmovaps [rsp+16*9],xmm15 + + push rdi + push rsi + + mov rdi, rcx + +{% endif %} + + push rbx + push r12 + push r13 + push r14 + push r15 + + sub rsp, 8 + +{% if family == "unix" %} +.cfi_def_cfa_offset 64 +{% endif %} + + stmxcsr [rsp + 4] +{% if msvc %} + mov rax, 1FC0h +{% else %} + mov rax, 0x1FC0 +{% endif %} + mov [rsp], eax + ldmxcsr [rsp] + +{% include "dispatcher.j2" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov r12, [rdi + 32] // packing + mov rbx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rcx, [rdi + 8] // k + test rcx, rcx + jz {{L}}non_linear_loop + + cmp r12, 1 + je {{L}}main_loop_packed_packed_i8i8 + +{{L}}main_loop_packed_packed: + vmovaps ymm12, [rax] + + {% for i in range(0, 8) %} + vbroadcastss ymm14, dword ptr [rbx + {{i}} * 4] + vpmulld ymm13, ymm12, ymm14 + vpaddd ymm{{i}}, ymm{{i}}, ymm13 + {% endfor %} + + add rax, 32 + add rbx, 32 + dec rcx + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +{{L}}main_loop_packed_packed_i8i8: + // PackedI8K4 layout: per K=4 block, the A panel is 8 rows x 4 K-bytes (32 + // bytes, lane m = A[m, 4kb..4kb+3]) and the B panel is 8 cols x 4 K-bytes + // (lane n = B[n, 4kb..4kb+3]). VPDPBUSD is u8 x s8, so A is offset by +128 + // (-> u8) and the resulting 128*sum_k(B[n]) bias is removed per column after + // the loop, leaving the i32 accumulators identical to the AVX2 path. + + add rcx, 3 + shr rcx, 2 // rcx <- ceil(k/4) K=4 blocks + + mov r8d, 0x01010101 + movd xmm11, r8d + vpbroadcastd ymm11, xmm11 // ymm11 <- u8 ones (sum of B) + + mov r8d, 0x80808080 + movd xmm12, r8d + vpbroadcastd ymm12, xmm12 // ymm12 <- byte 0x80 (A + 128) + + vpxor ymm10, ymm10, ymm10 // ymm10 <- per-col sum_k B[n] + +{{L}}loop_4k_i8i8: + vmovdqu ymm8, [rax] // A block: lane m = A[m,4kb..] + vpaddb ymm8, ymm8, ymm12 // s8 -> u8 (+128, modular) + + vmovdqu ymm9, [rbx] // B block: lane n = B[n,4kb..] + vpdpbusd ymm10, ymm11, ymm9 // sum_k B[n] += sum_t B[n,4kb+t] + + {% for n in range(0, 8) %} + vpbroadcastd ymm13, dword ptr [rbx + {{n}} * 4] + vpdpbusd ymm{{n}}, ymm8, ymm13 // acc[n][m] += sum_t (A[m]+128)*B[n] + {% endfor %} + + add rax, 32 + add rbx, 32 + dec rcx + jnz {{L}}loop_4k_i8i8 + + // remove the +128 bias added on A: acc[n] -= 128 * sum_k B[n] + vpslld ymm10, ymm10, 7 // lane n <- 128 * sum_k B[n] + {% for n in range(0, 8) %} + mov r8d, {{n}} + movd xmm14, r8d + vpbroadcastd ymm14, xmm14 // index = n in every lane + vpermd ymm15, ymm14, ymm10 // splat 128*sum_k B[n] + vpsubd ymm{{n}}, ymm{{n}}, ymm15 + {% endfor %} + + jmp {{L}}non_linear_loop + +{% set from = 0 %}{% set to = 7 %}{% include "fma_mmm_i32_scalars.j2" %} +{% set mr = 8 %}{% set from = 0 %}{% set to = 7 %}{% include "fma_mmm_i32_per_rows.j2" %} +{% set mr = 8 %}{% set from = 0 %}{% set to = 7 %}{% include "fma_mmm_i32_per_cols.j2" %} +{% set from = 0 %}{% set to = 7 %}{% include "fma_mmm_load_tile.j2" %} + +{{L}}add_unicast: + + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + mov r8, [rdi + 32] // item size + + cmp r8, 4 + je {{L}}non_linear_addc_i32 + +{# +// This is not great as vgatherdps reads 32-bits values and goes beyond our buffer. Probably harmless though. +// Commented and replaced with the "mov al" loop beyond to pacify valgrind. +// ymm14 and ymm15 are the same as in the non_linear_addc_i32 case (compute them before the test right above here. +// {% for i in range(0, 8) %} +// vpcmpeqd ymm15, ymm15, ymm15 +// vgatherdps ymm12, [ r10 + ymm14 ], ymm15 // 0xxx 1xxx 2xxx 3xxx 4xxx 5xxx 6xxx 7xxx +// +// // we need to go through vpmovsxbd, shuffling naively erases signs +// vpshufb ymm12, ymm12, ymm10 // 0123 0123 0123 0123 4567 4567 4567 4567 +// +// vpermd ymm12, ymm11, ymm12 // 0123 4567 +// vpmovsxbd ymm12, xmm12 // sign extend +// +// vpaddd ymm{{i}}, ymm{{i}}, ymm12 +// add r10, rbx +// {% endfor %} +#} + + {% for col in range(0, 8) %} + mov r8, r10 + {% for half in range(0, 2) %} + {% for lane in range(0, 4) %} + mov al, [ r8 ] + add r8, rsi + movsx eax, al + pinsrd xmm10, eax, {{lane}} + {% endfor %} + vperm2f128 ymm10, ymm10, ymm10, 1 + {% endfor %} + vpaddd ymm{{col}}, ymm{{col}}, ymm10 + add r10, rbx + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}non_linear_addc_i32: + + mov eax, 0 +{% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} + vpermq ymm14, ymm14, 78 // 0b01001110 +{% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} + vpermq ymm14, ymm14, 78 // 0b01001110 + + +{% if msvc %} + vpbroadcastd ymm10, dword ptr [ offset byte_shuffle ] + vmovups ymm11, dword ptr [ offset i128_shuffle ] +{% else %} + vpbroadcastd ymm10, [ rip + {{L}}byte_shuffle ] + vmovups ymm11, [ rip + {{L}}i128_shuffle ] +{% endif %} + +{% for i in range(0, 8) %} + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm12, [ r10 + ymm14 ], ymm15 + vpaddd ymm{{i}}, ymm{{i}}, ymm12 + add r10, rbx +{% endfor %} + + jmp {{L}}non_linear_loop + +{% if msvc %} +.data +byte_shuffle dd 201851904 // 0x0c080400 +i128_shuffle dd 0, 4 +.code +{% else %} +{{L}}byte_shuffle: .int 201851904 // 0x0c080400 +{{L}}i128_shuffle: .int 0, 4 +{% endif %} + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vmovups ymm12, [rax] + +{% for i in range(0, 8) %} + vbroadcastss ymm14, dword ptr [rbx + {{ i * 4 }} ] + vpmulld ymm15, ymm12, ymm14 + vpaddd ymm{{i}}, ymm{{i}}, ymm15 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_scale: + mov r8, [ rdi + 16 ] // policy + vbroadcastss ymm8, dword ptr [rdi + 24] // multi + + mov rax, 1 + movq xmm9, rax + vpbroadcastq ymm9, xmm9 // ymm9 <- 1 + + mov rax, [ rdi + 8 ] // xmm10 <- shift + 31 + add rax, 31 + movq xmm10, rax + vpbroadcastq ymm10, xmm10 + + mov rax, 1 + movq xmm11, rax + vpsubq ymm12, ymm10, ymm9 // shift+31 - 1 + vpsllq ymm11, ymm9, xmm12 // ymm11 <- 1 << (shift + 31 - 1) + + cmp r8, 1 + je {{L}}q_scale_rounding_zero + cmp r8, 2 + je {{L}}q_scale_rounding_away + cmp r8, 3 + je {{L}}q_scale_rounding_minus_inf + cmp r8, 4 + je {{L}}q_scale_rounding_plus_inf + cmp r8, 5 + je {{L}}q_scale_rounding_even + cmp r8, 6 + je {{L}}q_scale_rounding_odd + + jmp {{L}}unsupported + +{{L}}q_scale_rounding_zero: // signum * ( (abs + nudge) >> shift ) +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + vpsrldq ymm15, ymm14, 4 // ymm15 <- a1, a2, a3, a4, a5, a6, a7, 0 + vpmuldq ymm14, ymm14, ymm8 // ymm14 <- a0*c, a2*c, a4*c, a6*c + vpmuldq ymm15, ymm15, ymm8 // ymm15 <- a1*c, a3*c, a5*c, a7*c + + vpaddq ymm14, ymm14, ymm11 + vpaddq ymm15, ymm15, ymm11 + + vpsubq ymm14, ymm14, ymm9 + vpsubq ymm15, ymm15, ymm9 + + vpsrlq ymm14, ymm14, xmm10 + vpsrlq ymm15, ymm15, xmm10 + + vpslldq ymm15, ymm15, 4 + vpblendd ymm14, ymm15, ymm14, 85 // 0x55 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}q_scale_rounding_away: // signum * ( (abs + nudge) >> shift ) +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + vpsrldq ymm15, ymm14, 4 // ymm15 <- a1, a2, a3, a4, a5, a6, a7, 0 + vpmuldq ymm14, ymm14, ymm8 // ymm14 <- a0*c, a2*c, a4*c, a6*c + vpmuldq ymm15, ymm15, ymm8 // ymm15 <- a1*c, a3*c, a5*c, a7*c + + vpaddq ymm14, ymm14, ymm11 + vpaddq ymm15, ymm15, ymm11 + + vpsrlq ymm14, ymm14, xmm10 + vpsrlq ymm15, ymm15, xmm10 + + vpslldq ymm15, ymm15, 4 + vpblendd ymm14, ymm15, ymm14, 85 // 0x55 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}q_scale_rounding_minus_inf: // signum * ( (abs << 32 + 1<<30+shift) >> shift ) +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + // sign extract for nudging in the right direction + vpxor ymm13, ymm13, ymm13 + vpcmpgtd ymm13, ymm{{i}}, ymm13 // ymm13 <- s0, s1, ..s8 (signums, as all ones or all zeros) + vpsrld ymm13, ymm13, 31 // then just 0 or 1 + + vpsrldq ymm15, ymm14, 4 // ymm15 <- a1, a2, a3, a4, a5, a6, a7, 0 + vpmuldq ymm14, ymm14, ymm8 // ymm14 <- a0*c, a2*c, a4*c, a6*c + vpmuldq ymm15, ymm15, ymm8 // ymm15 <- a1*c, a3*c, a5*c, a7*c + + vpaddq ymm14, ymm14, ymm11 + vpaddq ymm15, ymm15, ymm11 + + // reinterpret ymm13=s0i32..s7 as i64 and blend with zero to pick the even ones as i64 + vpxor ymm12, ymm12, ymm12 + vpblendd ymm12, ymm12, ymm13, 85 // 0x55 + vpsubq ymm14, ymm14, ymm12 + + vpsrldq ymm13, ymm13, 4 // ymm13 <- s1, s2, .., s7, 0 + vpxor ymm12, ymm12, ymm12 + vpblendd ymm12, ymm12, ymm13, 85 // 0x55 + vpsubq ymm15, ymm15, ymm12 + + vpsrlq ymm14, ymm14, xmm10 + vpsrlq ymm15, ymm15, xmm10 + + vpslldq ymm15, ymm15, 4 + vpblendd ymm14, ymm15, ymm14, 85 // 0x55 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}q_scale_rounding_plus_inf: // signum * ( (abs << 32 + 1<<30+shift) >> shift ) + + vpbroadcastd ymm9, xmm9 + +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + vpxor ymm13, ymm13, ymm13 + + // sign extract for nudging in the right direction + vpcmpgtd ymm13, ymm{{i}}, ymm13 // ymm13 <- s0, s1, ..s8 (signums, as all ones or all zeros) + vpaddd ymm13, ymm13, ymm9 // if val >= 0 { 0i32 } else { 1i32 } + + vpsrldq ymm15, ymm14, 4 // ymm15 <- a1, a2, a3, a4, a5, a6, a7, 0 + vpmuldq ymm14, ymm14, ymm8 // ymm14 <- a0*c, a2*c, a4*c, a6*c + vpmuldq ymm15, ymm15, ymm8 // ymm15 <- a1*c, a3*c, a5*c, a7*c + + vpaddq ymm14, ymm14, ymm11 + vpaddq ymm15, ymm15, ymm11 + + // reinterpret ymm13=s0i32..s7 as i64 and blend with zero to pick the even ones as i64 + vpxor ymm12, ymm12, ymm12 + vpblendd ymm12, ymm12, ymm13, 85 // 0x55 + vpsubq ymm14, ymm14, ymm12 + + vpsrldq ymm13, ymm13, 4 // ymm13 <- s1, s2, .., s7, 0 + vpxor ymm12, ymm12, ymm12 + vpblendd ymm12, ymm12, ymm13, 85 // 0x55 + vpsubq ymm15, ymm15, ymm12 + + vpsrlq ymm14, ymm14, xmm10 + vpsrlq ymm15, ymm15, xmm10 + + vpslldq ymm15, ymm15, 4 + vpblendd ymm14, ymm15, ymm14, 85 // 0x55 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}q_scale_rounding_even: // signum * ( (abs + nudge) >> shift ) +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + vpsrldq ymm15, ymm14, 4 // ymm15 <- a1, a2, a3, a4, a5, a6, a7, 0 + vpmuldq ymm14, ymm14, ymm8 // ymm14 <- a0*c, a2*c, a4*c, a6*c + vpmuldq ymm15, ymm15, ymm8 // ymm15 <- a1*c, a3*c, a5*c, a7*c + + vpsrlq ymm12, ymm14, xmm10 + vpand ymm12, ymm12, ymm9 + vpaddq ymm14, ymm14, ymm12 + vpsubq ymm14, ymm14, ymm9 + + vpsrlq ymm12, ymm15, xmm10 + vpand ymm12, ymm12, ymm9 + vpaddq ymm15, ymm15, ymm12 + vpsubq ymm15, ymm15, ymm9 + + vpaddq ymm14, ymm14, ymm11 + vpaddq ymm15, ymm15, ymm11 + + vpsrlq ymm14, ymm14, xmm10 + vpsrlq ymm15, ymm15, xmm10 + + vpslldq ymm15, ymm15, 4 + vpblendd ymm14, ymm15, ymm14, 85 // 0x55 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_scale_rounding_odd: // signum * ( (abs + nudge) >> shift ) +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + vpsrldq ymm15, ymm14, 4 // ymm15 <- a1, a2, a3, a4, a5, a6, a7, 0 + vpmuldq ymm14, ymm14, ymm8 // ymm14 <- a0*c, a2*c, a4*c, a6*c + vpmuldq ymm15, ymm15, ymm8 // ymm15 <- a1*c, a3*c, a5*c, a7*c + + vpsrlq ymm12, ymm14, xmm10 + vpand ymm12, ymm12, ymm9 + vpsubq ymm14, ymm14, ymm12 + + vpsrlq ymm12, ymm15, xmm10 + vpand ymm12, ymm12, ymm9 + vpsubq ymm15, ymm15, ymm12 + + vpaddq ymm14, ymm14, ymm11 + vpaddq ymm15, ymm15, ymm11 + + vpsrlq ymm14, ymm14, xmm10 + vpsrlq ymm15, ymm15, xmm10 + + vpslldq ymm15, ymm15, 4 + vpblendd ymm14, ymm15, ymm14, 85 // 0x55 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}q_shl: + mov eax, [ rdi + 8 ] // xmm10 <- -shift (8 times) + movd xmm10, eax + vpbroadcastd ymm10, xmm10 + +{% for i in range(0, 8) %} + vpsllvd ymm{{i}}, ymm{{i}}, ymm10 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_shr: + mov r8, [ rdi + 16 ] // policy + + mov eax, 1 + movd xmm9, eax + vpbroadcastd ymm9, xmm9 // ymm9 <- 1u32 (8 times) + + mov eax, [ rdi + 8 ] // xmm10 <- shift (8 times) + movd xmm10, eax + vpbroadcastd ymm10, xmm10 + + mov ebx, 1 + mov cl, al + sub cl, 1 // rcx <- shift -1 + sal ebx, cl // rbx <- (1 << (shift - 1)) + movd xmm11, ebx + vpbroadcastd ymm11, xmm11 // ymm11 <- "half" + + vpxor ymm12, ymm12, ymm12 // ymm12 <- zeroes + + cmp r8, 1 + je {{L}}q_shr_rounding_zero + cmp r8, 2 + je {{L}}q_shr_rounding_away + cmp r8, 3 + je {{L}}q_shr_rounding_minus_inf + cmp r8, 4 + je {{L}}q_shr_rounding_plus_inf + cmp r8, 5 + je {{L}}q_shr_rounding_even + cmp r8, 6 + je {{L}}q_shr_rounding_odd + + jmp {{L}}unsupported + +{{L}}q_shr_rounding_zero: +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + vpsubd ymm14, ymm14, ymm9 + vpaddd ymm14, ymm14, ymm11 + vpsravd ymm14, ymm14, ymm10 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_shr_rounding_away: +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + vpaddd ymm14, ymm14, ymm11 + vpsravd ymm14, ymm14, ymm10 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_shr_rounding_minus_inf: +{% for i in range(0, 8) %} + vpsubd ymm{{i}}, ymm{{i}}, ymm9 + vpaddd ymm{{i}}, ymm{{i}}, ymm11 + vpsravd ymm{{i}}, ymm{{i}}, ymm10 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_shr_rounding_plus_inf: +{% for i in range(0, 8) %} + vpaddd ymm{{i}}, ymm{{i}}, ymm11 + vpsravd ymm{{i}}, ymm{{i}}, ymm10 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_shr_rounding_even: +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + vpsravd ymm13, ymm14, ymm10 + vpand ymm13, ymm13, ymm9 + vpsubd ymm13, ymm13, ymm9 // nudge = ((abs >>l shift) & 0x01) - 1 + vpaddd ymm14, ymm14, ymm13 // add nudge + vpaddd ymm14, ymm14, ymm11 // add half + vpsravd ymm14, ymm14, ymm10 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}q_shr_rounding_odd: +{% for i in range(0, 8) %} + vpabsd ymm14, ymm{{i}} + vpsravd ymm13, ymm14, ymm10 + vpand ymm13, ymm13, ymm9 + vpsubd ymm13, ymm12, ymm13 // nudge = - ((abs >>l shift) & 0x01) + vpaddd ymm14, ymm14, ymm13 // add nudge + vpaddd ymm14, ymm14, ymm11 // add half + vpsravd ymm14, ymm14, ymm10 + vpsignd ymm{{i}}, ymm14, ymm{{i}} +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rdx, [rdi + 24] // col stride + mov rcx, [rdi + 32] // item size + + cmp rcx, 4 + je {{L}}store_strides_i32 + + {% for col in range(0, 8) %} + mov r10, r8 + {% for row in range(0, 4) %} + extractps ebx, xmm{{col}}, {{row}} + mov byte ptr [r10], bl + add r10, rsi + {% endfor %} + vperm2f128 ymm{{col}}, ymm{{col}}, ymm{{col}}, 1 + {% for row in range(0, 4) %} + extractps ebx, xmm{{col}}, {{row}} + mov byte ptr [r10], bl + add r10, rsi + {% endfor %} + add r8, rdx + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store_strides_i32: + {% for col in range(0, 8) %} + mov r10, r8 + {% for row in range(0, 4) %} + extractps ebx, xmm{{col}}, {{row}} + mov dword ptr [r10], ebx + add r10, rsi + {% endfor %} + vperm2f128 ymm{{col}}, ymm{{col}}, ymm{{col}}, 1 + {% for row in range(0, 4) %} + extractps ebx, xmm{{col}}, {{row}} + mov dword ptr [r10], ebx + add r10, rsi + {% endfor %} + add r8, rdx + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}return: + ldmxcsr [rsp + 4] + add rsp, 8 + + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + +{% if family == "windows" %} + pop rsi + pop rdi + + vmovaps xmm15, [rsp+16*9] + vmovaps xmm14, [rsp+16*8] + vmovaps xmm13, [rsp+16*7] + vmovaps xmm12, [rsp+16*6] + vmovaps xmm11, [rsp+16*5] + vmovaps xmm10, [rsp+16*4] + vmovaps xmm9, [rsp+16*3] + vmovaps xmm8, [rsp+16*2] + vmovaps xmm7, [rsp+16*1] + vmovaps xmm6, [rsp] +{% endif %} + + mov rsp, rbp + pop rbp + ret + + +{{L}}one_32bit: +{% if msvc %} + dd 1 +{% else %} + .int 1 +{% endif %} + +{% if msvc %} +avx512vnni_mmm_i32_8x8_{{suffix}} endp +_text ends +end +{% else %} +.cfi_endproc +{% endif %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/dispatcher.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/dispatcher.j2 new file mode 100644 index 0000000..e60fdc6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/dispatcher.j2 @@ -0,0 +1,40 @@ +// vim: set syntax=asm : + +{{L}}non_linear: + +{{L}}non_linear_loop_enter: + sub rdi, 40 +{{L}}non_linear_loop: + add rdi, 40 + mov rax, [rdi] + + mov r8, {{ jump_table | length }} + cmp rax, 0 + cmovl rax, r8 + cmp rax, {{ jump_table | length }} + cmovg rax, r8 + +{% if msvc %} + lea r8, [ offset {{L}}jmp_table ] +{% else %} + lea r8, [ rip + {{L}}jmp_table ] +{% endif %} + movsxd r9, dword ptr [ r8 + rax * 4 ] + lea r8, [ r8 + r9 ] + jmp r8 + +{{L}}jmp_table: +{% for j in jump_table %} + {{long}} {{L}}{{j}}-{{L}}jmp_table +{% endfor %} + {{long}} {{L}}unsupported-{{L}}jmp_table + +{{L}}unsupported: + mov rax, 1 + jmp {{L}}return + + +{{L}}done: + mov rax, 0 + jmp {{L}}return + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_16x5.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_16x5.S.j2 new file mode 100644 index 0000000..5ccee35 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_16x5.S.j2 @@ -0,0 +1,143 @@ +{# +// vim: set syntax=asm : +/* mmm 16 x 5: + + ymm0 ymm2 ymm4 ymm6 ymm8 + ymm1 ymm3 ymm5 ymm7 ymm9 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +#} + +{% set type = "f32" %}{% set size = "16x5" %}{% set suffix = suffix %}{% set G = G %}{% include "preamble.j2" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + test rbx, rbx + jz {{L}}non_linear_loop + +{{L}}main_loop_packed_packed: + {% include "2x5/packed_packed_loop1/avx.S.raw" %} + + add rcx, 20 + add rax, 64 + dec rbx + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +// NON LINEAR / ADDC + +{% set from = 0 %}{% set to = 9 %}{% set type = "f32" %}{% include "fma_mmm_f32_scalars.j2" %} +{% set mr = 16 %}{% set from = 0 %}{% set to = 9 %}{% set type = "f32" %}{% include "fma_mmm_f32_per_rows.j2" %} +{% set mr = 16 %}{% set from = 0 %}{% set to = 9 %}{% set type = "f32" %}{% include "fma_mmm_f32_per_cols.j2" %} +{% set from = 0 %}{% set to = 9 %}{% include "fma_mmm_load_tile.j2" %} + +{{L}}add_unicast: + + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + mov eax, 0 +{% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} + + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + + lea r8, [ r10 + rsi * 8 ] + +{% for i in range(0, 5) %} + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm12, [ r10 + ymm14 ], ymm15 + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm13, [ r8 + ymm14 ], ymm15 + add r10, rbx + add r8, rbx + vaddps ymm{{ i * 2 }}, ymm{{ i * 2 }}, ymm12 + vaddps ymm{{ i * 2 + 1 }}, ymm{{ i * 2 + 1 }}, ymm13 +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vmovups ymm12, [rax] + vmovups ymm13, [rax + 32] + +{% for i in range(0, 5) %} + vbroadcastss ymm14, dword ptr [rbx + {{ i * 4 }} ] + vfmadd231ps ymm{{ i * 2 }}, ymm12, ymm14 + vfmadd231ps ymm{{ i * 2 + 1 }}, ymm13, ymm14 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + lea r12, [ r8 + 4 * rbx ] + lea r11, [ r10 + rbx ] + cmp rbx, 64 + jne {{L}}store_strides_generic + + {% for row in range(0, 2) %} + {% for col in range(0, 5) %} + vmovups ymmword ptr [r{{ col + 8 }}], ymm{{ col * 2 + row }} + add r{{ col + 8 }}, 32 + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store_strides_generic: + // tops of cols + + {% for quarter in range(0, 4) %} + {% if quarter != 0 %} + // move next four rows at top (xmm0,2,..10) + vperm2f128 ymm0, ymm0, ymm1, {{quarter}} + vperm2f128 ymm2, ymm2, ymm3, {{quarter}} + vperm2f128 ymm4, ymm4, ymm5, {{quarter}} + vperm2f128 ymm6, ymm6, ymm7, {{quarter}} + vperm2f128 ymm8, ymm8, ymm9, {{quarter}} + {% endif %} + {% for row in range(0, 4) %} + {% for i in range(0, 5) %} + vextractps dword ptr [r{{ i + 8 }}], xmm{{ i * 2 }}, {{row}} + add r{{ i + 8 }}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{% set type = "f32" %}{% set size = "16x5" %}{% set suffix = suffix %}{% set G = G %}{% set L = L %}{% include "postamble.j2" %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_16x6.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_16x6.S.j2 new file mode 100644 index 0000000..3355505 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_16x6.S.j2 @@ -0,0 +1,131 @@ +{# +// vim: set syntax=asm : + +/* mmm 16 x 6: + + ymm0 ymm2 ymm4 ymm6 ymm8 ymm10 + ymm1 ymm3 ymm5 ymm7 ymm9 ymm11 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +#} + +{% set type = "f32" %}{% set size = "16x6" %}{% set suffix = suffix %}{% set G = G %}{% include "preamble.j2" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + test rbx, rbx + jz {{L}}non_linear_loop + +{{L}}main_loop_packed_packed: + {% include "2x6/packed_packed_loop1/original.S.raw" %} + + dec rbx + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +// NON LINEAR / ADDC + +{% set from = 0 %}{% set to = 11 %}{% set type = "f32" %}{% include "fma_mmm_f32_scalars.j2" %} +{% set mr = 16 %}{% set from = 0 %}{% set to = 11 %}{% set type = "f32" %}{% include "fma_mmm_f32_per_rows.j2" %} +{% set mr = 16 %}{% set from = 0 %}{% set to = 11 %}{% set type = "f32" %}{% include "fma_mmm_f32_per_cols.j2" %} +{% set from = 0 %}{% set to = 11 %}{% include "fma_mmm_load_tile.j2" %} + +{{L}}add_unicast: + + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + mov eax, 0 +{% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} + + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + + lea r8, [ r10 + rsi * 8 ] + +{% for i in range(0, 6) %} + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm12, [ r10 + ymm14 ], ymm15 + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm13, [ r8 + ymm14 ], ymm15 + add r10, rbx + add r8, rbx + vaddps ymm{{ i * 2 }}, ymm{{ i * 2 }}, ymm12 + vaddps ymm{{ i * 2 + 1 }}, ymm{{ i * 2 + 1 }}, ymm13 +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vmovups ymm12, [rax] + vmovups ymm13, [rax + 32] + +{% for i in range(0, 6) %} + vbroadcastss ymm14, dword ptr [rbx + {{ i * 4 }} ] + vfmadd231ps ymm{{ i * 2 }}, ymm12, ymm14 + vfmadd231ps ymm{{ i * 2 + 1 }}, ymm13, ymm14 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + // tops of cols + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + lea r12, [ r8 + 4 * rbx ] + lea r11, [ r10 + rbx ] + lea r13, [ r12 + rbx ] + + {% for quarter in range(0, 4) %} + {% if quarter != 0 %} + // move next four rows at top (xmm0,2,..10) + vperm2f128 ymm0, ymm0, ymm1, {{quarter}} + vperm2f128 ymm2, ymm2, ymm3, {{quarter}} + vperm2f128 ymm4, ymm4, ymm5, {{quarter}} + vperm2f128 ymm6, ymm6, ymm7, {{quarter}} + vperm2f128 ymm8, ymm8, ymm9, {{quarter}} + vperm2f128 ymm10, ymm10, ymm11, {{quarter}} + {% endif %} + {% for row in range(0, 4) %} + {% for i in range(0, 6) %} + vextractps dword ptr [r{{ i + 8 }}], xmm{{ i * 2 }}, {{row}} + add r{{ i + 8 }}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{% set type = "f32" %}{% set size = "16x6" %}{% set suffix = suffix %}{% set G = G %}{% set L = L %}{% include "postamble.j2" %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_24x4.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_24x4.S.j2 new file mode 100644 index 0000000..cf5e1af --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_24x4.S.j2 @@ -0,0 +1,158 @@ +{# +// vim: set syntax=asm : +/* mmm 24 x 4: + + ymm0 ymm3 ymm6 ymm10 + ymm1 ymm4 ymm7 ymm11 + ymm2 ymm5 ymm8 ymm12 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +#} + +{% set type = "f32" %}{% set size = "24x4" %}{% set suffix = suffix %}{% set G = G %}{% include "preamble.j2" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + test rbx, rbx + jz {{L}}non_linear_loop + +{{L}}main_loop_packed_packed: + {% include "3x4/packed_packed_loop1/avx.S.raw" %} + + add rcx, 16 + add rax, 96 + dec rbx + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +// NON LINEAR / ADDC + +{% set from = 0 %}{% set to = 11 %}{% set type = "f32" %}{% include "fma_mmm_f32_scalars.j2" %} +{% set mr = 24 %}{% set from = 0 %}{% set to = 11 %}{% set type = "f32" %}{% include "fma_mmm_f32_per_rows.j2" %} +{% set mr = 24 %}{% set from = 0 %}{% set to = 11 %}{% set type = "f32" %}{% include "fma_mmm_f32_per_cols.j2" %} +{% set from = 0 %}{% set to = 11 %}{% include "fma_mmm_load_tile.j2" %} + +{{L}}add_unicast: + + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + cmp rsi, 4 + jne {{L}}unicast_generic + + lea r9, [ r8 + rbx ] + lea r10, [ r9 + rbx] + lea r11, [ r10 + rbx ] + lea r12, [ r11 + rbx ] + +{% for col in range(0, 4) %} + {% for row in range(0, 3) %} + vmovups ymm12, [ r{{ col + 8 }} ] + add r{{ col + 8 }}, 32 + vaddps ymm{{ col * 3 + row }}, ymm{{ col * 3 + row }}, ymm12 + {% endfor %} +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}unicast_generic: + mov eax, 0 +{% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} + +// mov r12, [0] + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + + lea r9, [ r8 + rsi * 8 ] + lea r10, [ r9 + rsi * 8 ] + +{% for col in range(0, 4) %} + {% for row in range(0, 3) %} + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm12, [ r{{ row + 8 }} + ymm14 ], ymm15 + add r{{ row + 8 }}, rbx + vaddps ymm{{ col * 3 + row }}, ymm{{ col * 3 + row }}, ymm12 + {% endfor %} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vmovups ymm12, [rax] + vmovups ymm13, [rax + 32] + vmovups ymm15, [rax + 64] +{% for i in range(0, 4) %} + vbroadcastss ymm14, dword ptr [rbx + {{ i * 4 }} ] + vfmadd231ps ymm{{ i * 3 }}, ymm12, ymm14 + vfmadd231ps ymm{{ i * 3 + 1 }}, ymm13, ymm14 + vfmadd231ps ymm{{ i * 3 + 2 }}, ymm15, ymm14 +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + lea r11, [ r10 + rbx ] + + cmp rsi, 4 + jne {{L}}store_strides_generic + + {% for col in range(0, 4) %} + {% for row in range(0, 3) %} + vmovups ymmword ptr [r{{ col + 8 }}], ymm{{ col * 3 + row }} + add r{{ col + 8 }}, 32 + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store_strides_generic: + {% for col in range(0, 4) %} + {% for row in range(0, 3) %} + {% for i in range(0, 4) %} + vextractps dword ptr [r{{ col + 8 }}], xmm{{ col * 3 + row }}, {{i}} + add r{{ col + 8 }}, rsi + {% endfor %} + vperm2f128 ymm{{ col * 3 + row }}, ymm{{ col * 3 + row }}, ymm{{ col * 3 + row }}, 1 + {% for i in range(0, 4) %} + vextractps dword ptr [r{{ col + 8 }}], xmm{{ col * 3 + row }}, {{i}} + add r{{ col + 8 }}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + jmp {{L}}non_linear_loop + +{% set type = "f32" %}{% set size = "24x4" %}{% set suffix = suffix %}{% set G = G %}{% set L = L %}{% include "postamble.j2" %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_32x1.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_32x1.S.j2 new file mode 100644 index 0000000..c1bd914 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_32x1.S.j2 @@ -0,0 +1,424 @@ +{# +// vim: set syntax=asm : + +/* mmm 64 x 1 + + ymm0 + ymm1 + ymm2 + ymm3 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +#} + +{% set type = "f32" %}{% set size = "32x1" %}{% set suffix = suffix %}{% set G = G %}{% include "preamble.j2" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + mov r8, [rdi + 32] // packing + test rbx, rbx + jz {{L}}non_linear_loop + + cmp r8, 1 + jz {{L}}q40f32 + + cmp r8, 2 + jz {{L}}q40f16 + + cmp r8, 3 + jz {{L}}f16f16 + + cmp r8, 4 + jz {{L}}f16f32 + + cmp r8, 5 + jz {{L}}f32f16 + +{{align}} 16 +{{L}}main_loop_packed_packed: + vbroadcastss ymm15, dword ptr [rcx] + + vmovaps ymm8, [rax] + vmovaps ymm9, [rax + 32] + vmovaps ymm10, [rax + 64] + vmovaps ymm11, [rax + 96] + + vfmadd231ps ymm0, ymm15, ymm8 + vfmadd231ps ymm1, ymm15, ymm9 + vfmadd231ps ymm2, ymm15, ymm10 + vfmadd231ps ymm3, ymm15, ymm11 + + add rcx, 4 + add rax, 128 + sub rbx, 1 + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +{% if msvc %} +{{L}}q40f32_mask: + {{long}} 0F0F0F0Fh +{{L}}q40f32_eight: + {{long}} 08h +{% else %} +{{L}}q40f32_mask: + {{long}} 0x0F0F0F0F +{{L}}q40f32_eight: + {{long}} 8 +{% endif %} + +{{L}}q40f32: + // ymm0-3: acc + // ymm4-7: scales + // ymm13: 8 + // ymm14: mask + // ymm15: b value + vbroadcastss ymm14, dword ptr [{{offset}} {{L}}q40f32_mask] + vbroadcastss ymm13, dword ptr [{{offset}} {{L}}q40f32_eight] + +{{L}}q40f32_outerloop: + // scales + vmovaps xmm4, [rax] + vmovaps xmm5, [rax + 16] + vmovaps xmm6, [rax + 32] + vmovaps xmm7, [rax + 48] + vcvtph2ps ymm4, xmm4 + vcvtph2ps ymm5, xmm5 + vcvtph2ps ymm6, xmm6 + vcvtph2ps ymm7, xmm7 + add rax, 64 + + mov rdx, 32 + +{{L}}q40f32_innerloop: + vbroadcastss ymm15, dword ptr [rcx] + vmovaps xmm8, [rax] // 32 nibbles + + vpand xmm10, xmm8, xmm14 // 16 bytes + + vpmovzxbd ymm9, xmm10 // 8 u32 + + vpermilpd xmm10, xmm10, 1 // swap 64bit halves + vpmovzxbd ymm10, xmm10 // 8 u32 + + vpsrlw xmm8, xmm8, 4 + vpand xmm12, xmm8, xmm14 // 16 bytes + vpmovzxbd ymm11, xmm12 // 8 u32 + vpermilpd xmm12, xmm12, 1 // swap 64bit halves + vpmovzxbd ymm12, xmm12 // 8 u32 + + vpsubd ymm9, ymm9, ymm13 + vpsubd ymm10, ymm10, ymm13 + vpsubd ymm11, ymm11, ymm13 + vpsubd ymm12, ymm12, ymm13 + + vcvtdq2ps ymm9, ymm9 + vcvtdq2ps ymm10, ymm10 + vcvtdq2ps ymm11, ymm11 + vcvtdq2ps ymm12, ymm12 + + vmulps ymm9, ymm9, ymm4 + vmulps ymm10, ymm10, ymm5 + vmulps ymm11, ymm11, ymm6 + vmulps ymm12, ymm12, ymm7 + + vfmadd231ps ymm0, ymm15, ymm9 + vfmadd231ps ymm1, ymm15, ymm10 + vfmadd231ps ymm2, ymm15, ymm11 + vfmadd231ps ymm3, ymm15, ymm12 + + add rax, 16 + add rcx, 4 + sub rdx, 1 + jnz {{L}}q40f32_innerloop + + sub rbx, 32 + jnz {{L}}q40f32_outerloop + + jmp {{L}}non_linear_loop + +{{L}}q40f16: + // ymm0-3: acc + // ymm4-7: scales + // ymm13: 8 + // ymm14: mask + // ymm15: b value + vbroadcastss ymm14, dword ptr [{{offset}} {{L}}q40f32_mask] + vbroadcastss ymm13, dword ptr [{{offset}} {{L}}q40f32_eight] + +{{L}}q40f16_outerloop: + // scales + vmovaps xmm4, [rax] + vmovaps xmm5, [rax + 16] + vmovaps xmm6, [rax + 32] + vmovaps xmm7, [rax + 48] + vcvtph2ps ymm4, xmm4 + vcvtph2ps ymm5, xmm5 + vcvtph2ps ymm6, xmm6 + vcvtph2ps ymm7, xmm7 + add rax, 64 + + mov rdx, 32 + +{{L}}q40f16_innerloop: + vpbroadcastw ymm15, word ptr [rcx] + vcvtph2ps ymm15, xmm15 + + vmovaps xmm8, [rax] // 32 nibbles + + vpand xmm10, xmm8, xmm14 // 16 bytes + + vpmovzxbd ymm9, xmm10 // 8 u32 + + vpermilpd xmm10, xmm10, 1 // swap 64bit halves + vpmovzxbd ymm10, xmm10 // 8 u32 + + vpsrlw xmm8, xmm8, 4 + vpand xmm12, xmm8, xmm14 // 16 bytes + vpmovzxbd ymm11, xmm12 // 8 u32 + vpermilpd xmm12, xmm12, 1 // swap 64bit halves + vpmovzxbd ymm12, xmm12 // 8 u32 + + vpsubd ymm9, ymm9, ymm13 + vpsubd ymm10, ymm10, ymm13 + vpsubd ymm11, ymm11, ymm13 + vpsubd ymm12, ymm12, ymm13 + + vcvtdq2ps ymm9, ymm9 + vcvtdq2ps ymm10, ymm10 + vcvtdq2ps ymm11, ymm11 + vcvtdq2ps ymm12, ymm12 + + vmulps ymm9, ymm9, ymm4 + vmulps ymm10, ymm10, ymm5 + vmulps ymm11, ymm11, ymm6 + vmulps ymm12, ymm12, ymm7 + + vfmadd231ps ymm0, ymm15, ymm9 + vfmadd231ps ymm1, ymm15, ymm10 + vfmadd231ps ymm2, ymm15, ymm11 + vfmadd231ps ymm3, ymm15, ymm12 + + add rax, 16 + add rcx, 2 + sub rdx, 1 + jnz {{L}}q40f16_innerloop + + sub rbx, 32 + jnz {{L}}q40f16_outerloop + + jmp {{L}}non_linear_loop + +{{L}}f16f16: +{{align}} 16 + vpbroadcastw ymm15, word ptr [rcx] + + vmovaps xmm4, [rax] + vmovaps xmm5, [rax + 16] + vmovaps xmm6, [rax + 32] + vmovaps xmm7, [rax + 48] + + vcvtph2ps ymm15, xmm15 + vcvtph2ps ymm4, xmm4 + vcvtph2ps ymm5, xmm5 + vcvtph2ps ymm6, xmm6 + vcvtph2ps ymm7, xmm7 + + vfmadd231ps ymm0, ymm15, ymm4 + vfmadd231ps ymm1, ymm15, ymm5 + vfmadd231ps ymm2, ymm15, ymm6 + vfmadd231ps ymm3, ymm15, ymm7 + + add rcx, 2 + add rax, 64 + sub rbx, 1 + jnz {{L}}f16f16 + + jmp {{L}}non_linear_loop + +{{L}}f32f16: +{{align}} 16 + vpbroadcastw ymm15, word ptr [rcx] + + vmovaps ymm4, [rax] + vmovaps ymm5, [rax + 32] + vmovaps ymm6, [rax + 64] + vmovaps ymm7, [rax + 96] + + vcvtph2ps ymm15, xmm15 + + vfmadd231ps ymm0, ymm15, ymm4 + vfmadd231ps ymm1, ymm15, ymm5 + vfmadd231ps ymm2, ymm15, ymm6 + vfmadd231ps ymm3, ymm15, ymm7 + + add rcx, 2 + add rax, 128 + sub rbx, 1 + jnz {{L}}f32f16 + + jmp {{L}}non_linear_loop + +{{L}}f16f32: +{{align}} 16 + vbroadcastss ymm15, dword ptr [rcx] + + vmovaps xmm4, [rax] + vmovaps xmm5, [rax + 16] + vmovaps xmm6, [rax + 32] + vmovaps xmm7, [rax + 48] + + vcvtph2ps ymm4, xmm4 + vcvtph2ps ymm5, xmm5 + vcvtph2ps ymm6, xmm6 + vcvtph2ps ymm7, xmm7 + + vfmadd231ps ymm0, ymm15, ymm4 + vfmadd231ps ymm1, ymm15, ymm5 + vfmadd231ps ymm2, ymm15, ymm6 + vfmadd231ps ymm3, ymm15, ymm7 + + add rcx, 4 + add rax, 64 + sub rbx, 1 + jnz {{L}}f16f32 + + jmp {{L}}non_linear_loop + + +{% set from = 0 %}{% set to = 3 %}{% set type = "f32" %}{% include "fma_mmm_f32_scalars.j2" %} +{% set mr = 32 %}{% set from = 0 %}{% set to = 3 %}{% set type = "f32" %}{% include "fma_mmm_f32_per_rows.j2" %} +{% set mr = 32 %}{% set from = 0 %}{% set to = 3 %}{% set type = "f32" %}{% include "fma_mmm_f32_per_cols.j2" %} +{% set from = 0 %}{% set to = 3 %}{% include "fma_mmm_load_tile.j2" %} + +{{L}}add_unicast: + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + + cmp rsi, 4 + jne {{L}}add_unicast_generic + + {% for row in range(0, 4) %} + vaddps ymm{{row}}, ymm{{row}}, [ r10 + {{ row * 32 }} ] + {% endfor %} + jmp {{L}}non_linear_loop + + + jmp {{L}}non_linear_loop + +{{L}}add_unicast_generic: + mov eax, 0 +{% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} + + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + +{% for i in range(0, 4) %} + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm12, [ r10 + ymm14 ], ymm15 + + vaddps ymm{{i}}, ymm{{i}}, ymm12 + lea r10, [ r10 + rsi * 8 ] +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vbroadcastss ymm14, dword ptr [rbx] + +{% for i in range(0, 4) %} + vmovups ymm12, [rax + {{ i * 32 }}] + vfmadd231ps ymm{{i}}, ymm12, ymm14 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov r11, [rdi + 32] // item size + + cmp r11, 2 + je {{L}}store_f16 + + cmp rsi, 4 + jne {{L}}store_generic + + {% for row in range(0, 4) %} + vmovups [r8 + {{ row * 32 }}], ymm{{row}} + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store_generic: + + {% for vec in range(0, 4) %} + {% for half in range(0, 2) %} + {% if half == 0 %} + movaps xmm9, xmm{{vec}} + {% else %} + vperm2f128 ymm9, ymm{{vec}}, ymm{{vec}}, 1 + {% endif %} + {% for row in range(0, 4) %} + vextractps dword ptr [r8], xmm9, {{row}} + add r8, rsi + {% endfor %} + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store_f16: + + vcvtps2ph xmm0, ymm0, 0 + vcvtps2ph xmm1, ymm1, 0 + vcvtps2ph xmm2, ymm2, 0 + vcvtps2ph xmm3, ymm3, 0 + + cmp rsi, 2 + jne {{L}}store_generic_f16 + + {% for row in range(0, 4) %} + vmovups [r8 + {{ row * 16 }}], xmm{{row}} + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store_generic_f16: + + {% for vec in range(0, 4) %} + {% for row in range(0, 8) %} + pextrw word ptr [r8], xmm{{vec}}, {{row}} + add r8, rsi + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{% set type = "f32" %}{% set size = "32x1" %}{% set suffix = suffix %}{% set G = G %}{% set L = L %}{% include "postamble.j2" %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_32x3.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_32x3.S.j2 new file mode 100644 index 0000000..be4817b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_32x3.S.j2 @@ -0,0 +1,336 @@ +{# +// vim: set syntax=asm : +/* mmm 16 x 5: + + ymm0 ymm4 ymm8 + ymm1 ymm5 ymm9 + ymm2 ymm6 ymm10 + ymm3 ymm7 ymm11 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +#} + +{% set type = "f32" %}{% set size = "32x3" %}{% set suffix = suffix %}{% set G = G %}{% include "preamble.j2" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rbx, [rdi + 8] // k + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov r8, [rdi + 32] // packing + + test rbx, rbx + jz {{L}}non_linear_loop + + cmp r8, 1 + jz {{L}}main_loop_packed_packed_f32_f16 + + cmp r8, 2 + jz {{L}}main_loop_packed_packed_f16_f32 + + cmp r8, 3 + jz {{L}}main_loop_packed_packed_f16_f16 + +{{L}}main_loop_packed_packed: + {% include "4x3/packed_packed_loop1/avx.S.raw" %} + + dec rbx + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +{{L}}main_loop_packed_packed_f32_f16: + // Load col of A + vmovaps ymm12, [rax] + + // Fill 3 cols of B + vpbroadcastw xmm13, word ptr [rcx + 0] + vpbroadcastw xmm14, word ptr [rcx + 2] + vpbroadcastw xmm15, word ptr [rcx + 4] + + vcvtph2ps ymm13, xmm13 + vcvtph2ps ymm14, xmm14 + vcvtph2ps ymm15, xmm15 + + // N.B. Stepping cols in inner loop + vfmadd231ps ymm0, ymm12, ymm13 + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm8, ymm12, ymm15 + + vmovaps ymm12, [rax+32] + + vfmadd231ps ymm1, ymm12, ymm13 + vfmadd231ps ymm5, ymm12, ymm14 + vfmadd231ps ymm9, ymm12, ymm15 + + vmovaps ymm12, [rax+64] + + vfmadd231ps ymm2, ymm12, ymm13 + vfmadd231ps ymm6, ymm12, ymm14 + vfmadd231ps ymm10, ymm12, ymm15 + + vmovaps ymm12, [rax+96] + + vfmadd231ps ymm3, ymm12, ymm13 + vfmadd231ps ymm7, ymm12, ymm14 + vfmadd231ps ymm11, ymm12, ymm15 + + add rcx, 6 + add rax, 128 + + dec rbx + jnz {{L}}main_loop_packed_packed_f32_f16 + + jmp {{L}}non_linear_loop + +{{L}}main_loop_packed_packed_f16_f32: + // Load col of A + vmovaps xmm12, [rax] + + // Fill 3 cols of B + vbroadcastss ymm13, dword ptr [rcx + 0] + vbroadcastss ymm14, dword ptr [rcx + 4] + vbroadcastss ymm15, dword ptr [rcx + 8] + + vcvtph2ps ymm12, xmm12 + + vfmadd231ps ymm0, ymm12, ymm13 + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm8, ymm12, ymm15 + + vmovaps xmm12, [rax+16] + vcvtph2ps ymm12, xmm12 + + vfmadd231ps ymm1, ymm12, ymm13 + vfmadd231ps ymm5, ymm12, ymm14 + vfmadd231ps ymm9, ymm12, ymm15 + + vmovaps xmm12, [rax+32] + vcvtph2ps ymm12, xmm12 + + vfmadd231ps ymm2, ymm12, ymm13 + vfmadd231ps ymm6, ymm12, ymm14 + vfmadd231ps ymm10, ymm12, ymm15 + + vmovaps xmm12, [rax+48] + vcvtph2ps ymm12, xmm12 + + vfmadd231ps ymm3, ymm12, ymm13 + vfmadd231ps ymm7, ymm12, ymm14 + vfmadd231ps ymm11, ymm12, ymm15 + + add rcx, 12 + add rax, 64 + + dec rbx + jnz {{L}}main_loop_packed_packed_f16_f32 + + jmp {{L}}non_linear_loop + +{{L}}main_loop_packed_packed_f16_f16: + // Load col of A + vmovaps xmm12, [rax] + + // Fill 3 cols of B + vpbroadcastw xmm13, word ptr [rcx + 0] + vpbroadcastw xmm14, word ptr [rcx + 2] + vpbroadcastw xmm15, word ptr [rcx + 4] + + vcvtph2ps ymm12, xmm12 + vcvtph2ps ymm13, xmm13 + vcvtph2ps ymm14, xmm14 + vcvtph2ps ymm15, xmm15 + + vfmadd231ps ymm0, ymm12, ymm13 + vfmadd231ps ymm4, ymm12, ymm14 + vfmadd231ps ymm8, ymm12, ymm15 + + vmovaps xmm12, [rax+16] + vcvtph2ps ymm12, xmm12 + + vfmadd231ps ymm1, ymm12, ymm13 + vfmadd231ps ymm5, ymm12, ymm14 + vfmadd231ps ymm9, ymm12, ymm15 + + vmovaps xmm12, [rax+32] + vcvtph2ps ymm12, xmm12 + + vfmadd231ps ymm2, ymm12, ymm13 + vfmadd231ps ymm6, ymm12, ymm14 + vfmadd231ps ymm10, ymm12, ymm15 + + vmovaps xmm12, [rax+48] + vcvtph2ps ymm12, xmm12 + + vfmadd231ps ymm3, ymm12, ymm13 + vfmadd231ps ymm7, ymm12, ymm14 + vfmadd231ps ymm11, ymm12, ymm15 + + add rcx, 6 + add rax, 64 + + dec rbx + jnz {{L}}main_loop_packed_packed_f16_f16 + + jmp {{L}}non_linear_loop + +// NON LINEAR / ADDC + +{% set from = 0 %}{% set to = 11 %}{% set type = "f32" %}{% include "fma_mmm_f32_scalars.j2" %} +{% set mr = 32 %}{% set from = 0 %}{% set to = 11 %}{% set type = "f32" %}{% include "fma_mmm_f32_per_rows.j2" %} +{% set mr = 32 %}{% set from = 0 %}{% set to = 11 %}{% set type = "f32" %}{% include "fma_mmm_f32_per_cols.j2" %} +{% set from = 0 %}{% set to = 11 %}{% include "fma_mmm_load_tile.j2" %} + +{{L}}add_unicast: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + cmp rsi, 4 + jne {{L}}unicast_generic + + lea r9, [ r8 + rbx ] + lea r10, [ r9 + rbx] + lea r11, [ r10 + rbx ] + +{% for col in range(0, 3) %} + {% for row in range(0, 4) %} + vmovups ymm12, [ r{{ col + 8 }} ] + add r{{ col + 8 }}, 32 + vaddps ymm{{ col * 4 + row }}, ymm{{ col * 4 + row }}, ymm12 + {% endfor %} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}unicast_generic: + mov eax, 0 +{% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} + +// mov r12, [0] + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + + lea r9, [ r8 + rsi * 8 ] + lea r10, [ r9 + rsi * 8 ] + lea r11, [ r10 + rsi * 8 ] + +{% for col in range(0, 3) %} + {% for row in range(0, 4) %} + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm12, [ r{{ row + 8 }} + ymm14 ], ymm15 + add r{{ row + 8 }}, rbx + vaddps ymm{{ col * 4 + row }}, ymm{{ col * 4 + row }}, ymm12 + {% endfor %} +{% endfor %} + + jmp {{L}}non_linear_loop + + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vbroadcastss ymm13, dword ptr [rbx] + vbroadcastss ymm14, dword ptr [rbx + 4] + vbroadcastss ymm15, dword ptr [rbx + 8] +{% for i in range(0, 4) %} + vmovups ymm12, [rax + {{ i * 32 }}] + vfmadd231ps ymm{{ 0 + i }}, ymm12, ymm13 + vfmadd231ps ymm{{ 4 + i }}, ymm12, ymm14 + vfmadd231ps ymm{{ 8 + i }}, ymm12, ymm15 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + mov r11, [rdi + 32] // item size + + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + + cmp r11, 2 + je {{L}}store_f16 + + cmp rsi, 4 + jne {{L}}store_strides_generic + + {% for col in range(0, 3) %} + {% for row in range(0, 4) %} + vmovups ymmword ptr [r{{ col + 8 }}], ymm{{ col * 4 + row }} + add r{{ col + 8 }}, 32 + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store_strides_generic: + + {% for col in range(0, 3) %} + {% for row in range(0, 4) %} + {% for i in range(0, 4) %} + vextractps dword ptr [r{{ col + 8 }}], xmm{{ col * 4 + row }}, {{i}} + add r{{ col + 8 }}, rsi + {% endfor %} + vperm2f128 ymm{{ col * 4 + row }}, ymm{{ col * 4 + row }}, ymm{{ col * 4 + row }}, 1 + {% for i in range(0, 4) %} + vextractps dword ptr [r{{ col + 8 }}], xmm{{ col * 4 + row }}, {{i}} + add r{{ col + 8 }}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + jmp {{L}}non_linear_loop + +{{L}}store_f16: + + {% for reg in range(0, 12) %} + vcvtps2ph xmm{{reg}}, ymm{{reg}}, 0 + {% endfor %} + + cmp rsi, 2 + jne {{L}}store_generic_f16 + + {% for col in range(0, 3) %} + {% for row in range(0, 4) %} + vmovups [r{{ col + 8 }} + {{ row * 16 }}], xmm{{ col * 4 + row }} + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store_generic_f16: + {% for col in range(0, 3) %} + {% for vec in range(0, 4) %} + {% for row in range(0, 8) %} + pextrw word ptr [r{{ col + 8 }}], xmm{{ col * 4 + vec }}, {{row}} + add r{{ col + 8 }}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{% set type = "f32" %}{% set size = "32x3" %}{% set suffix = suffix %}{% set G = G %}{% set L = L %}{% include "postamble.j2" %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_40x2.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_40x2.S.j2 new file mode 100644 index 0000000..9559419 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_40x2.S.j2 @@ -0,0 +1,158 @@ +{# +// vim: set syntax=asm : +/* mmm 40 x 5: + + ymm0 ymm5 + ymm1 ymm6 + ymm2 ymm7 + ymm3 ymm8 + ymm4 ymm9 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +#} + +{% set type = "f32" %}{% set size = "40x2" %}{% set suffix = suffix %}{% set G = G %}{% include "preamble.j2" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + test rbx, rbx + jz {{L}}non_linear_loop + +{{L}}main_loop_packed_packed: + {% include "5x2/packed_packed_loop1/avx.S.raw" %} + + dec rbx + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +// NON LINEAR / ADDC + +{% set from = 0 %}{% set to = 9 %}{% set type = "f32" %}{% include "fma_mmm_f32_scalars.j2" %} +{% set mr = 40 %}{% set from = 0 %}{% set to = 9 %}{% set type = "f32" %}{% include "fma_mmm_f32_per_rows.j2" %} +{% set mr = 40 %}{% set from = 0 %}{% set to = 9 %}{% set type = "f32" %}{% include "fma_mmm_f32_per_cols.j2" %} +{% set from = 0 %}{% set to = 9 %}{% include "fma_mmm_load_tile.j2" %} + +{{L}}add_unicast: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + cmp rsi, 4 + jne {{L}}unicast_generic + + lea r9, [ r8 + rbx ] + lea r10, [ r9 + rbx] + lea r11, [ r10 + rbx ] + lea r12, [ r11 + rbx ] + + +{% for col in range(0, 2) %} + {% for row in range(0, 5) %} + vmovups ymm12, [ r{{ col + 8 }} ] + add r{{ col + 8 }}, 32 + vaddps ymm{{ col * 5 + row }}, ymm{{ col * 5 + row }}, ymm12 + {% endfor %} +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}unicast_generic: + mov eax, 0 +{% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} + + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + + lea r9, [ r8 + rsi * 8] + lea r10, [ r9 + rsi * 8] + lea r11, [ r10 + rsi * 8] + lea r12, [ r11 + rsi * 8] + +{% for col in range(0, 2) %} + {% for row in range(0, 5) %} + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm12, [ r{{ row + 8 }} + ymm14 ], ymm15 + add r{{ row + 8 }}, rbx + vaddps ymm{{ col * 5 + row }}, ymm{{ col * 5 + row }}, ymm12 + {% endfor %} +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vbroadcastss ymm10, dword ptr [rbx] + vbroadcastss ymm11, dword ptr [rbx + 4] +{% for i in range(0, 5) %} + vmovups ymm12, [rax + {{ i * 32 }}] + vfmadd231ps ymm{{ 0 + i }}, ymm12, ymm10 + vfmadd231ps ymm{{ 5 + i }}, ymm12, ymm11 +{% endfor %} + jmp {{L}}non_linear_loop + + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + lea r11, [ r10 + rbx ] + lea r12, [ r10 + 2 * rbx ] + + cmp rsi, 4 + jne {{L}}store_strides_generic + + {% for col in range(0, 2) %} + {% for row in range(0, 5) %} + vmovups ymmword ptr [r{{ col + 8 }}], ymm{{ col * 5 + row }} + add r{{ col + 8 }}, 32 + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store_strides_generic: + {% for col in range(0, 2) %} + {% for row in range(0, 5) %} + {% for i in range(0, 4) %} + vextractps dword ptr [r{{ col + 8 }}], xmm{{ col * 5 + row }}, {{i}} + add r{{ col + 8 }}, rsi + {% endfor %} + vperm2f128 ymm{{ col * 5 + row }}, ymm{{ col * 5 + row }}, ymm{{ col * 5 + row }}, 1 + {% for i in range(0, 4) %} + vextractps dword ptr [r{{ col + 8 }}], xmm{{ col * 5 + row }}, {{i}} + add r{{ col + 8 }}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + jmp {{L}}non_linear_loop + +{% set type = "f32" %}{% set size = "40x2" %}{% set suffix = suffix %}{% set G = G %}{% set L = L %}{% include "postamble.j2" %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_64x1.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_64x1.S.j2 new file mode 100644 index 0000000..e2f631e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_64x1.S.j2 @@ -0,0 +1,142 @@ +{# +// vim: set syntax=asm : + +/* mmm 64 x 1 + + ymm0 + ymm1 + ... + ymm8 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +#} + +{% set type = "f32" %}{% set size = "64x1" %}{% set suffix = suffix %}{% set G = G %}{% include "preamble.j2" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rcx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rbx, [rdi + 8] // k + test rbx, rbx + jz {{L}}non_linear_loop + + test rbx, 1 + jz {{L}}main_loop_packed_packed + {% include "8x1/packed_packed_loop1/avx.S.raw" %} + + dec rbx + jz {{L}}non_linear_loop + +{{align}} 16 +{{L}}main_loop_packed_packed: + {% include "8x1/packed_packed_loop1/avx-unroll.S.raw" %} + + sub rbx, 2 + jnz {{L}}main_loop_packed_packed + + jmp {{L}}non_linear_loop + +{% set from = 0 %}{% set to = 7 %}{% set type = "f32" %}{% include "fma_mmm_f32_scalars.j2" %} +{% set mr = 64 %}{% set from = 0 %}{% set to = 7 %}{% set type = "f32" %}{% include "fma_mmm_f32_per_rows.j2" %} +{% set mr = 64 %}{% set from = 0 %}{% set to = 7 %}{% set type = "f32" %}{% include "fma_mmm_f32_per_cols.j2" %} +{% set from = 0 %}{% set to = 7 %}{% include "fma_mmm_load_tile.j2" %} + +{{L}}add_unicast: + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + + cmp rsi, 4 + jne {{L}}add_unicast_generic + + {% for row in range(0, 8) %} + vaddps ymm{{row}}, ymm{{row}}, [ r10 + {{ row * 32 }} ] + {% endfor %} + jmp {{L}}non_linear_loop + + + jmp {{L}}non_linear_loop + +{{L}}add_unicast_generic: + mov eax, 0 +{% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} + + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + +{% for i in range(0, 8) %} + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm12, [ r10 + ymm14 ], ymm15 + + vaddps ymm{{i}}, ymm{{i}}, ymm12 + lea r10, [ r10 + rsi * 8 ] +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vbroadcastss ymm14, dword ptr [rbx] + +{% for i in range(0, 8) %} + vmovups ymm12, [rax + {{ i * 32 }}] + vfmadd231ps ymm{{i}}, ymm12, ymm14 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + + cmp rsi, 4 + jne {{L}}store_generic + + {% for row in range(0, 8) %} + vmovups [r8 + {{ row * 32 }}], ymm{{row}} + {% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}store_generic: + + {% for vec in range(0, 8) %} + {% for half in range(0, 2) %} + {% if half == 0 %} + movaps xmm9, xmm{{vec}} + {% else %} + vperm2f128 ymm9, ymm{{vec}}, ymm{{vec}}, 1 + {% endif %} + {% for row in range(0, 4) %} + vextractps dword ptr [r8], xmm9, {{row}} + add r8, rsi + {% endfor %} + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + + +{% set type = "f32" %}{% set size = "64x1" %}{% set suffix = suffix %}{% set G = G %}{% set L = L %}{% include "postamble.j2" %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_8x8.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_8x8.S.j2 new file mode 100644 index 0000000..00864e7 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_8x8.S.j2 @@ -0,0 +1,129 @@ +{# +// vim: set syntax=asm : + +/* mmm 16 x 6: + + ymm0 ymm2 ymm4 ymm6 ymm8 ymm10 + ymm1 ymm3 ymm5 ymm7 ymm9 ymm11 + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) +*/ +#} + +{% set type = "f32" %}{% set size = "8x8" %}{% set suffix = suffix %}{% set G = G %}{% include "preamble.j2" %} + +{{L}}clear: + vzeroall + jmp {{L}}non_linear_loop + +{{L}}add_mat_mul: + mov rbx, [rdi + 24] // B + mov rax, [rdi + 16] // A + + mov rcx, [rdi + 8] // k + test rcx, rcx + jz {{L}}non_linear_loop + +{{L}}main_loop_packed_packed: + vmovaps ymm12, [rax] + + {% for i in range(0, 8) %} + vbroadcastss ymm14, dword ptr [rbx + {{i}} * 4] + vfmadd231ps ymm{{i}}, ymm12, ymm14 + {% endfor %} + + add rax, 32 + add rbx, 32 + dec rcx + jnz {{L}}main_loop_packed_packed + jmp {{L}}non_linear_loop + +// NON LINEAR / ADDC + +{% set from = 0 %}{% set to = 7 %}{% set type = "f32" %}{% include "fma_mmm_f32_scalars.j2" %} +{% set mr = 8 %}{% set from = 0 %}{% set to = 7 %}{% set type = "f32" %}{% include "fma_mmm_f32_per_rows.j2" %} +{% set mr = 8 %}{% set from = 0 %}{% set to = 7 %}{% set type = "f32" %}{% include "fma_mmm_f32_per_cols.j2" %} +{% set from = 0 %}{% set to = 7 %}{% include "fma_mmm_load_tile.j2" %} + +{{L}}add_unicast: + + mov r10, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + mov eax, 0 +{% for i in range(0, 4) %} + pinsrd xmm14, eax, {{i}} + add eax, esi +{% endfor %} +{% for i in range(0, 4) %} + pinsrd xmm15, eax, {{i}} + add eax, esi +{% endfor %} + + vperm2f128 ymm14, ymm14, ymm15, 32 // ymm14 <- xmm14::xmm15 + +{% for i in range(0, 8) %} + vpcmpeqd ymm15, ymm15, ymm15 + vgatherdps ymm12, [ r10 + ymm14 ], ymm15 + add r10, rbx + vaddps ymm{{i}}, ymm{{i}}, ymm12 +{% endfor %} + + jmp {{L}}non_linear_loop + +{{L}}add_row_col_products: + mov rax, [ rdi + 8 ] + mov rbx, [ rdi + 16 ] + + vmovups ymm12, [rax] + +{% for i in range(0, 8) %} + vbroadcastss ymm14, dword ptr [rbx + {{ i * 4 }} ] + vfmadd231ps ymm{{i}}, ymm12, ymm14 +{% endfor %} + jmp {{L}}non_linear_loop + +{{L}}store: + mov r8, [rdi + 8] // c ptr + mov rsi, [rdi + 16] // row stride + mov rbx, [rdi + 24] // col stride + + // tops of cols + lea r9, [ r8 + rbx ] + lea r10, [ r8 + 2 * rbx ] + lea r12, [ r8 + 4 * rbx ] + lea r11, [ r10 + rbx ] + lea r13, [ r12 + rbx ] + lea r14, [ r12 + 2 * rbx ] + lea r15, [ r13 + 2 * rbx ] + + {% for quarter in range(0, 2) %} + {% if quarter != 0 %} + // move next four rows at top (xmm0,2,..10) + {% for r in range(0, 8) %} + vperm2f128 ymm{{r}}, ymm{{r}}, ymm{{r}}, {{quarter}} + {% endfor %} + {% endif %} + {% for row in range(0, 4) %} + {% for i in range(0, 8) %} + vextractps dword ptr [r{{ i + 8 }}], xmm{{i}}, {{row}} + add r{{ i + 8 }}, rsi + {% endfor %} + {% endfor %} + {% endfor %} + + jmp {{L}}non_linear_loop + + +{% set type = "f32" %}{% set size = "8x8" %}{% set suffix = suffix %}{% set G = G %}{% set L = L %}{% include "postamble.j2" %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_per_cols.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_per_cols.j2 new file mode 100644 index 0000000..44387c3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_per_cols.j2 @@ -0,0 +1,9 @@ +// vim: set syntax=asm : +{% from "fma_mmm_ymm_ops.j2" import per_col %} + +{{ per_col("per_col_min", "vminps", mr, from, to, type=type) }} +{{ per_col("per_col_max", "vmaxps", mr, from, to, type=type) }} +{{ per_col("per_col_add", "vaddps", mr, from, to, type=type) }} +{{ per_col("per_col_mul", "vmulps", mr, from, to, type=type) }} +{{ per_col("per_col_sub", "vsubps", mr, from, to, type=type) }} +{{ per_col("per_col_sub_flipped", "vsubps", mr, from, to, type=type, flipped=true) }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_per_rows.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_per_rows.j2 new file mode 100644 index 0000000..ed3e70e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_per_rows.j2 @@ -0,0 +1,9 @@ +// vim: set syntax=asm : +{% from "fma_mmm_ymm_ops.j2" import per_row %} + +{{ per_row("per_row_min", "vminps", mr, from, to, type=type) }} +{{ per_row("per_row_max", "vmaxps", mr, from, to, type=type) }} +{{ per_row("per_row_add", "vaddps", mr, from, to, type=type) }} +{{ per_row("per_row_mul", "vmulps", mr, from, to, type=type) }} +{{ per_row("per_row_sub", "vsubps", mr, from, to, type=type) }} +{{ per_row("per_row_sub_flipped", "vsubps", mr, from, to, type=type, flipped=true) }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_scalars.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_scalars.j2 new file mode 100644 index 0000000..f01a690 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_f32_scalars.j2 @@ -0,0 +1,38 @@ +// vim: set syntax=asm : +{% from "fma_mmm_ymm_ops.j2" import scalar %} + +{{ scalar("scalar_min", "vminps", from, to, type=type) }} +{{ scalar("scalar_max", "vmaxps", from, to, type=type) }} +{{ scalar("scalar_add", "vaddps", from, to, type=type) }} +{{ scalar("scalar_mul", "vmulps", from, to, type=type) }} +{{ scalar("scalar_sub", "vsubps", from, to, type=type) }} +{{ scalar("scalar_sub_flipped", "vsubps", from, to, type=type, flipped=true) }} + +{{L}}leaky_relu: + // can only use ymm12 to ymm15 + // ymm15 <- alpha + {% if type == "f32" %} + vbroadcastss ymm15, dword ptr [rdi + 8] + {% else %} + pinsrw xmm15, word ptr [rdi + 8], 0 + vcvtph2ps ymm15, xmm15 + vbroadcastss ymm15, xmm15 + {% endif %} + + // ymm14 <- all zero + vpxor ymm14, ymm14, ymm14 + + {% for reg in range(from, to + 1) %} + // ymm12 <- alpha * x + vmulps ymm12, ymm{{reg}}, ymm15 + vcmpps ymm13, ymm14, ymm{{reg}}, 1 // 1 means LT + vblendvps ymm{{reg}}, ymm12, ymm{{reg}}, ymm13 + {% endfor %} + // select muled of orginal + + jmp {{L}}non_linear_loop + +{{L}}q_scale: +{{L}}q_shl: +{{L}}q_shr: + jmp {{L}}unsupported diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_i32_per_cols.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_i32_per_cols.j2 new file mode 100644 index 0000000..79cbb87 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_i32_per_cols.j2 @@ -0,0 +1,9 @@ +// vim: set syntax=asm : +{% from "fma_mmm_ymm_ops.j2" import per_col %} + +{{ per_col("per_col_min", "vpminsd", mr, from, to, type="i32") }} +{{ per_col("per_col_max", "vpmaxsd", mr, from, to, type="i32") }} +{{ per_col("per_col_add", "vpaddd", mr, from, to, type="i32") }} +{{ per_col("per_col_mul", "vpmulld", mr, from, to, type="i32") }} +{{ per_col("per_col_sub", "vpsubd", mr, from, to, type="i32") }} +{{ per_col("per_col_sub_flipped", "vpsubd", mr, from, to, type="i32", flipped=true) }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_i32_per_rows.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_i32_per_rows.j2 new file mode 100644 index 0000000..4d84af0 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_i32_per_rows.j2 @@ -0,0 +1,9 @@ +// vim: set syntax=asm : +{% from "fma_mmm_ymm_ops.j2" import per_row %} + +{{ per_row("per_row_min", "vpminsd", mr, from, to, type="i32") }} +{{ per_row("per_row_max", "vpmaxsd", mr, from, to, type="i32") }} +{{ per_row("per_row_add", "vpaddd", mr, from, to, type="i32") }} +{{ per_row("per_row_mul", "vpmulld", mr, from, to, type="i32") }} +{{ per_row("per_row_sub", "vpsubd", mr, from, to, type="i32") }} +{{ per_row("per_row_sub_flipped", "vpsubd", mr, from, to, type="i32", flipped=true) }} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_i32_scalars.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_i32_scalars.j2 new file mode 100644 index 0000000..23b29d3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_i32_scalars.j2 @@ -0,0 +1,24 @@ +// vim: set syntax=asm : +{% from "fma_mmm_ymm_ops.j2" import scalar %} + +{{ scalar("scalar_min", "vpminsd", from, to, type="i32") }} +{{ scalar("scalar_max", "vpmaxsd", from, to, type="i32") }} +{{ scalar("scalar_mul", "vpmulld", from, to, type="i32") }} +{{ scalar("scalar_add", "vpaddd", from, to, type="i32") }} +{{ scalar("scalar_sub", "vpsubd", from, to, type="i32") }} +{{ scalar("scalar_sub_flipped", "vpsubd", from, to, type="i32", flipped=true) }} + +{{L}}leaky_relu: + // can only use ymm12 to ymm15 + // ymm15 <- alpha + vbroadcastss ymm15, dword ptr [rdi + 8] + // ymm14 <- all zero + vpxor ymm14, ymm14, ymm14 + + {% for reg in range(from, to + 1) %} + vpmulld ymm12, ymm{{reg}}, ymm15 + vpcmpgtd ymm13, ymm14, ymm{{reg}} + vblendvps ymm{{reg}}, ymm{{reg}}, ymm12, ymm13 + {% endfor %} + + jmp {{L}}non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_load_tile.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_load_tile.j2 new file mode 100644 index 0000000..27cf0f4 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_load_tile.j2 @@ -0,0 +1,9 @@ +// vim: set syntax=asm : + +{{L}}load_tile: + mov r8, [rdi + 8] + {% for reg in range(from, to + 1) %} + vmovups ymm{{reg}}, ymmword ptr [r8 + {{ (reg - from) * 32 }}] + {% endfor %} + + jmp {{L}}non_linear_loop diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_ymm_ops.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_ymm_ops.j2 new file mode 100644 index 0000000..2c97173 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_mmm_ymm_ops.j2 @@ -0,0 +1,92 @@ + +{% macro scalar(label, op, from, to, type="f32", flipped=false) %} +{{L}}{{label}}: + {% if type == "f16" %} + pinsrw xmm12, word ptr [rdi + 8], 0 + vcvtph2ps ymm12, xmm12 + vbroadcastss ymm12, xmm12 + {% else %} + vbroadcastss ymm12, dword ptr [rdi + 8] + {% endif %} + + {% if flipped %} + {% for reg in range(from, to + 1) %} + {{op}} ymm{{reg}}, ymm{{reg}}, ymm12 + {% endfor %} + {% else %} + {% for reg in range(from, to + 1) %} + {{op}} ymm{{reg}}, ymm12, ymm{{reg}} + {% endfor %} + {% endif %} + + jmp {{L}}non_linear_loop +{% endmacro %} + +{% macro per_row(label, op, mr, from, to, type="f32", flipped=false) %} +{{L}}{{label}}: + mov rax, [ rdi + 8 ] + +{% set mr_over_8 = mr // 8 %} +{% set mr_over_8_min_1 = mr // 8 - 1 %} + +{% if type == "f16" %} + {% for ix in range(0, mr_over_8_min_1 + 1) %} + vmovups xmm{{ to + 1 + ix }}, [rax + {{ ix * 16 }}] + {% endfor %} + {% for ix in range(0, mr_over_8_min_1 + 1) %} + vcvtph2ps ymm{{ to + 1 + ix }}, xmm{{ to + 1 + ix }} + {% endfor %} +{% else %} + {% for ix in range(0, mr_over_8_min_1 + 1) %} + vmovups ymm{{ to + 1 + ix }}, [rax + {{ ix * 32 }}] + {% endfor %} +{% endif %} + +{% if flipped %} + {% for acc in range(from, to + 1) %} + {{op}} ymm{{acc}}, ymm{{acc}}, ymm{{ acc % mr_over_8 + to + 1 }} + {% endfor %} +{% else %} + {% for acc in range(from, to + 1) %} + {{op}} ymm{{acc}}, ymm{{ acc % mr_over_8 + to + 1 }}, ymm{{acc}} + {% endfor %} +{% endif %} + + jmp {{L}}non_linear_loop +{% endmacro %} + +{% macro per_col(label, op, mr, from, to, type="f32", flipped=false) %} +{{L}}{{label}}: + mov rax, [ rdi + 8 ] + +{% set mr_over_8 = mr // 8 %} +{% set mr_over_8_min_1 = mr // 8 - 1 %} + +{% set tmp = to + 1 %} + +{% set cols = (to + 1 - from) // mr_over_8 %} +{% set cols_min_1 = (to + 1 - from) // mr_over_8 - 1 %} + + +{% for right in range(0, cols_min_1 + 1) %} + {% if type == "f16" %} + pinsrw xmm{{tmp}}, word ptr [ rax ], 0 + add rax, 2 + vcvtph2ps ymm{{tmp}}, xmm{{tmp}} + vbroadcastss ymm{{tmp}}, xmm{{tmp}} + {% else %} + vbroadcastss ymm{{tmp}}, dword ptr [ rax ] + add rax, 4 + {% endif %} + {% for down in range(0, mr_over_8_min_1 + 1) %} + {% set acc = mr_over_8 * right + from + down %} + {% if flipped %} + {{op}} ymm{{acc}}, ymm{{acc}}, ymm{{tmp}} + {% else %} + {{op}} ymm{{acc}}, ymm{{tmp}}, ymm{{acc}} + {% endif %} + {% endfor %} +{% endfor %} + + jmp {{L}}non_linear_loop +{% endmacro %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_sigmoid_f32.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_sigmoid_f32.S.j2 new file mode 100644 index 0000000..742dd23 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_sigmoid_f32.S.j2 @@ -0,0 +1,319 @@ +{# +// vim: set syntax=asm : + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) + +#} + +{% if msvc %} + +_text segment +fma_sigmoid_f32_{{suffix}} proc + +{% else %} + +.intel_syntax noprefix +.text +.p2align 5 +.globl {{G}}fma_sigmoid_f32_{{suffix}} +{{G}}fma_sigmoid_f32_{{suffix}}: +.cfi_startproc +{% endif %} + + push rbp + mov rbp, rsp + + +{% if family == "windows" %} +// https://www.agner.org/optimize/calling_conventions.pdf xmm6-15 are not scratch +// https://stackoverflow.com/questions/43358429/save-value-of-xmm-registers + and rsp,-16 + lea rsp,[rsp-160] + vmovaps [rsp], xmm6 + vmovaps [rsp+16*1],xmm7 + vmovaps [rsp+16*2],xmm8 + vmovaps [rsp+16*3],xmm9 + vmovaps [rsp+16*4],xmm10 + vmovaps [rsp+16*5],xmm11 + vmovaps [rsp+16*6],xmm12 + vmovaps [rsp+16*7],xmm13 + vmovaps [rsp+16*8],xmm14 + vmovaps [rsp+16*9],xmm15 + + // move around arguments to mimick SysV rdi,rsi passing + push rdi + push rsi + mov rdi, rcx + mov rsi, rdx + +{% endif %} + + push rbx + push r12 + push r13 + push r14 + push r15 + + sub rsp, 8 + +{% if family == "unix" %} +// FIXME +// .cfi_def_cfa_offset 64 +{% endif %} + + stmxcsr [rsp + 4] +{% if msvc %} + mov rax, 1FC0h +{% else %} + mov rax, 0x1FC0 +{% endif %} + mov [rsp], eax + ldmxcsr [rsp] +// ---------------------------------------------------------------------- + + cmp rsi, 0 + je {{L}}done + + cmp rsi, 32 + jl {{L}}loop_1 + +{{L}}loop_4: + + vmovaps ymm4, [rdi] + vmovaps ymm5, [rdi + 32] + vmovaps ymm6, [rdi + 64] + vmovaps ymm7, [rdi + 96] + + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_low] + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_high] + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_13] + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_11] + + vmaxps ymm4, ymm4, ymm0 + vmaxps ymm5, ymm5, ymm0 + vmaxps ymm6, ymm6, ymm0 + vmaxps ymm7, ymm7, ymm0 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_9] + + vminps ymm4, ymm4, ymm1 + vminps ymm5, ymm5, ymm1 + vminps ymm6, ymm6, ymm1 + vminps ymm7, ymm7, ymm1 // ymm4..7 <- x + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_alpha_7] + + vmulps ymm8, ymm4, ymm4 + vmulps ymm9, ymm5, ymm5 + vmulps ymm10, ymm6, ymm6 + vmulps ymm11, ymm7, ymm7 // ymm8..11 <- x^2 + + vmovaps ymm12, ymm2 + vmovaps ymm13, ymm2 + vmovaps ymm14, ymm2 + vmovaps ymm15, ymm2 + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_5] + vfmadd132ps ymm12, ymm3, ymm8 + vfmadd132ps ymm13, ymm3, ymm9 + vfmadd132ps ymm14, ymm3, ymm10 + vfmadd132ps ymm15, ymm3, ymm11 + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_3] + vfmadd132ps ymm12, ymm0, ymm8 + vfmadd132ps ymm13, ymm0, ymm9 + vfmadd132ps ymm14, ymm0, ymm10 + vfmadd132ps ymm15, ymm0, ymm11 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_1] + vfmadd132ps ymm12, ymm1, ymm8 + vfmadd132ps ymm13, ymm1, ymm9 + vfmadd132ps ymm14, ymm1, ymm10 + vfmadd132ps ymm15, ymm1, ymm11 + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_beta_6] + vfmadd132ps ymm12, ymm2, ymm8 + vfmadd132ps ymm13, ymm2, ymm9 + vfmadd132ps ymm14, ymm2, ymm10 + vfmadd132ps ymm15, ymm2, ymm11 + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_beta_4] + vfmadd132ps ymm12, ymm3, ymm8 + vfmadd132ps ymm13, ymm3, ymm9 + vfmadd132ps ymm14, ymm3, ymm10 + vfmadd132ps ymm15, ymm3, ymm11 + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_beta_2] + vfmadd132ps ymm12, ymm0, ymm8 + vfmadd132ps ymm13, ymm0, ymm9 + vfmadd132ps ymm14, ymm0, ymm10 + vfmadd132ps ymm15, ymm0, ymm11 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_beta_0] + vmulps ymm4, ymm4, ymm12 + vmulps ymm5, ymm5, ymm13 + vmulps ymm6, ymm6, ymm14 + vmulps ymm7, ymm7, ymm15 // ymm4..7 <- num + + vmovaps ymm12, ymm1 + vmovaps ymm13, ymm1 + vmovaps ymm14, ymm1 + vmovaps ymm15, ymm1 + + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_half] + vfmadd132ps ymm12, ymm2, ymm8 + vfmadd132ps ymm13, ymm2, ymm9 + vfmadd132ps ymm14, ymm2, ymm10 + vfmadd132ps ymm15, ymm2, ymm11 + vfmadd132ps ymm12, ymm3, ymm8 + vfmadd132ps ymm13, ymm3, ymm9 + vfmadd132ps ymm14, ymm3, ymm10 + vfmadd132ps ymm15, ymm3, ymm11 + vfmadd132ps ymm12, ymm0, ymm8 + vfmadd132ps ymm13, ymm0, ymm9 + vfmadd132ps ymm14, ymm0, ymm10 + vfmadd132ps ymm15, ymm0, ymm11 // ymm12..14 <- denum + + vdivps ymm4, ymm4, ymm12 + vdivps ymm5, ymm5, ymm13 + vdivps ymm6, ymm6, ymm14 + vdivps ymm7, ymm7, ymm15 + vaddps ymm4, ymm4, ymm1 + vaddps ymm5, ymm5, ymm1 + vaddps ymm6, ymm6, ymm1 + vaddps ymm7, ymm7, ymm1 + + vmovaps [rdi], ymm4 + vmovaps [rdi + 32], ymm5 + vmovaps [rdi + 64], ymm6 + vmovaps [rdi + 96], ymm7 + + add rdi, 128 + sub rsi, 32 + cmp rsi, 32 + jg {{L}}loop_4 + + cmp rsi, 0 + je {{L}}done + +{{L}}loop_1: + vmovaps ymm4, [rdi] + + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_low] + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_high] + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_13] + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_11] + + vmaxps ymm4, ymm4, ymm0 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_9] + + vminps ymm4, ymm4, ymm1 // ymm4 <- x + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_alpha_7] + + vmulps ymm8, ymm4, ymm4 // ymm8 <- x^2 + + vmovaps ymm12, ymm2 + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_5] + vfmadd132ps ymm12, ymm3, ymm8 + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_3] + vfmadd132ps ymm12, ymm0, ymm8 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_1] + vfmadd132ps ymm12, ymm1, ymm8 + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_beta_6] + vfmadd132ps ymm12, ymm2, ymm8 + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_beta_4] + vfmadd132ps ymm12, ymm3, ymm8 + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_beta_2] + vfmadd132ps ymm12, ymm0, ymm8 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_beta_0] + vmulps ymm4, ymm4, ymm12 + + vmovaps ymm12, ymm1 + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_half] + vfmadd132ps ymm12, ymm2, ymm8 + vfmadd132ps ymm12, ymm3, ymm8 + vfmadd132ps ymm12, ymm0, ymm8 + + vdivps ymm4, ymm4, ymm12 + vaddps ymm4, ymm4, ymm1 + + vmovaps [rdi], ymm4 + add rdi, 32 + sub rsi, 8 + jnz {{L}}loop_1 +{{L}}done: + +// ---------------------------------------------------------------------- + + ldmxcsr [rsp + 4] + + add rsp, 8 + + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + +{% if family == "windows" %} + pop rsi + pop rdi + + vmovaps xmm15, [rsp+16*9] + vmovaps xmm14, [rsp+16*8] + vmovaps xmm13, [rsp+16*7] + vmovaps xmm12, [rsp+16*6] + vmovaps xmm11, [rsp+16*5] + vmovaps xmm10, [rsp+16*4] + vmovaps xmm9, [rsp+16*3] + vmovaps xmm8, [rsp+16*2] + vmovaps xmm7, [rsp+16*1] + vmovaps xmm6, [rsp] +{% endif %} + + mov rsp, rbp + pop rbp + ret + +{% set float %}{% if msvc %} real4 {%else%} .float {%endif%}{% endset %} + +{{L}}coeffs_num_low: + {{float}} -18.6 // low +{{L}}coeffs_num_high: + {{float}} 18.6 // high + +{{L}}coeffs_num_alpha_13: + {{float}} -4.433153405e-18 +{{L}}coeffs_num_alpha_11: + {{float}} 1.169974371e-14 +{{L}}coeffs_num_alpha_9: + {{float}} -1.875289645e-11 +{{L}}coeffs_num_alpha_7: + {{float}} 4.257889523e-8 +{{L}}coeffs_num_alpha_5: + {{float}} 0.00004811817576 +{{L}}coeffs_num_alpha_3: + {{float}} 0.008163842030 +{{L}}coeffs_num_alpha_1: + {{float}} 0.2499999971 + +{{L}}coeffs_num_beta_6: + {{float}} 3.922935744e-6 +{{L}}coeffs_num_beta_4: + {{float}} 0.001524872358 +{{L}}coeffs_num_beta_2: + {{float}} 0.1159886749 +{{L}}coeffs_num_beta_0: + {{float}} 1.0; + +{{L}}coeffs_num_half: + {{float}} 0.5 + +{% if msvc %} +fma_sigmoid_f32_{{suffix}} endp +_text ends +end +{% else %} +.cfi_endproc +{% endif %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_tanh_f32.S.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_tanh_f32.S.j2 new file mode 100644 index 0000000..3ff30db --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/fma_tanh_f32.S.j2 @@ -0,0 +1,313 @@ +{# +// vim: set syntax=asm : + +System V ABI: + args: rdi, rsi, rdx, rcx, r8, r9 + preserve: rbx, rsp, rbp, r12, r13, r14, r15 + scratch: rax, rdi, rsi, rdx, rcx, r8, r9, r10, r11 + return: rax (+rdx) + +Windows ABI: + args: RCX, RDX, R8, R9 + preserve: RBX, RBP, RDI, RSI, RSP, R12, R13, R14, R15, and XMM6-15 + scratch: RAX, RCX, RDX, R8, R9, R10, R11, XMM0-5, and the upper portions of YMM0-15 and ZMM0-15 + return: rax (+rdx) + +#} + +{% if msvc %} + +_text segment +fma_tanh_f32_{{suffix}} proc + +{% else %} + +.intel_syntax noprefix +.text +.p2align 5 +.globl {{G}}fma_tanh_f32_{{suffix}} +{{G}}fma_tanh_f32_{{suffix}}: +.cfi_startproc +{% endif %} + + push rbp + mov rbp, rsp + + +{% if family == "windows" %} +// https://www.agner.org/optimize/calling_conventions.pdf xmm6-15 are not scratch +// https://stackoverflow.com/questions/43358429/save-value-of-xmm-registers + and rsp,-16 + lea rsp,[rsp-160] + vmovaps [rsp], xmm6 + vmovaps [rsp+16*1],xmm7 + vmovaps [rsp+16*2],xmm8 + vmovaps [rsp+16*3],xmm9 + vmovaps [rsp+16*4],xmm10 + vmovaps [rsp+16*5],xmm11 + vmovaps [rsp+16*6],xmm12 + vmovaps [rsp+16*7],xmm13 + vmovaps [rsp+16*8],xmm14 + vmovaps [rsp+16*9],xmm15 + + // move around arguments to mimick SysV rdi,rsi passing + push rdi + push rsi + mov rdi, rcx + mov rsi, rdx + +{% endif %} + + push rbx + push r12 + push r13 + push r14 + push r15 + + sub rsp, 8 + +{% if family == "unix" %} +// FIXME +// .cfi_def_cfa_offset 64 +{% endif %} + + stmxcsr [rsp + 4] +{% if msvc %} + mov rax, 1FC0h +{% else %} + mov rax, 0x1FC0 +{% endif %} + mov [rsp], eax + ldmxcsr [rsp] +// ---------------------------------------------------------------------- + +{% set offset %}{% if msvc %} offset {%else%} rip + {%endif%} {% endset %} + + cmp rsi, 0 + je {{L}}done + + cmp rsi, 32 + jl {{L}}loop_1 + +{{L}}loop_4: + + vmovaps ymm4, [rdi] + vmovaps ymm5, [rdi + 32] + vmovaps ymm6, [rdi + 64] + vmovaps ymm7, [rdi + 96] + + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_low] + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_high] + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_13] + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_11] + + vmaxps ymm4, ymm4, ymm0 + vmaxps ymm5, ymm5, ymm0 + vmaxps ymm6, ymm6, ymm0 + vmaxps ymm7, ymm7, ymm0 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_9] + + vminps ymm4, ymm4, ymm1 + vminps ymm5, ymm5, ymm1 + vminps ymm6, ymm6, ymm1 + vminps ymm7, ymm7, ymm1 // ymm4..7 <- x + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_alpha_7] + + vmulps ymm8, ymm4, ymm4 + vmulps ymm9, ymm5, ymm5 + vmulps ymm10, ymm6, ymm6 + vmulps ymm11, ymm7, ymm7 // ymm8..11 <- x^2 + + vmovaps ymm12, ymm2 + vmovaps ymm13, ymm2 + vmovaps ymm14, ymm2 + vmovaps ymm15, ymm2 + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_5] + vfmadd132ps ymm12, ymm3, ymm8 + vfmadd132ps ymm13, ymm3, ymm9 + vfmadd132ps ymm14, ymm3, ymm10 + vfmadd132ps ymm15, ymm3, ymm11 + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_3] + vfmadd132ps ymm12, ymm0, ymm8 + vfmadd132ps ymm13, ymm0, ymm9 + vfmadd132ps ymm14, ymm0, ymm10 + vfmadd132ps ymm15, ymm0, ymm11 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_1] + vfmadd132ps ymm12, ymm1, ymm8 + vfmadd132ps ymm13, ymm1, ymm9 + vfmadd132ps ymm14, ymm1, ymm10 + vfmadd132ps ymm15, ymm1, ymm11 + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_beta_6] + vfmadd132ps ymm12, ymm2, ymm8 + vfmadd132ps ymm13, ymm2, ymm9 + vfmadd132ps ymm14, ymm2, ymm10 + vfmadd132ps ymm15, ymm2, ymm11 + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_beta_4] + vfmadd132ps ymm12, ymm3, ymm8 + vfmadd132ps ymm13, ymm3, ymm9 + vfmadd132ps ymm14, ymm3, ymm10 + vfmadd132ps ymm15, ymm3, ymm11 + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_beta_2] + vfmadd132ps ymm12, ymm0, ymm8 + vfmadd132ps ymm13, ymm0, ymm9 + vfmadd132ps ymm14, ymm0, ymm10 + vfmadd132ps ymm15, ymm0, ymm11 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_beta_0] + vmulps ymm4, ymm4, ymm12 + vmulps ymm5, ymm5, ymm13 + vmulps ymm6, ymm6, ymm14 + vmulps ymm7, ymm7, ymm15 // ymm4..7 <- num + + vmovaps ymm12, ymm1 + vmovaps ymm13, ymm1 + vmovaps ymm14, ymm1 + vmovaps ymm15, ymm1 + vfmadd132ps ymm12, ymm2, ymm8 + vfmadd132ps ymm13, ymm2, ymm9 + vfmadd132ps ymm14, ymm2, ymm10 + vfmadd132ps ymm15, ymm2, ymm11 + vfmadd132ps ymm12, ymm3, ymm8 + vfmadd132ps ymm13, ymm3, ymm9 + vfmadd132ps ymm14, ymm3, ymm10 + vfmadd132ps ymm15, ymm3, ymm11 + vfmadd132ps ymm12, ymm0, ymm8 + vfmadd132ps ymm13, ymm0, ymm9 + vfmadd132ps ymm14, ymm0, ymm10 + vfmadd132ps ymm15, ymm0, ymm11 // ymm12..14 <- denum + + vdivps ymm4, ymm4, ymm12 + vdivps ymm5, ymm5, ymm13 + vdivps ymm6, ymm6, ymm14 + vdivps ymm7, ymm7, ymm15 + + vmovaps [rdi], ymm4 + vmovaps [rdi + 32], ymm5 + vmovaps [rdi + 64], ymm6 + vmovaps [rdi + 96], ymm7 + + add rdi, 128 + sub rsi, 32 + cmp rsi, 32 + jg {{L}}loop_4 + + cmp rsi, 0 + je {{L}}done + +{{L}}loop_1: + vmovaps ymm4, [rdi] + + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_low] + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_high] + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_13] + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_11] + + vmaxps ymm4, ymm4, ymm0 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_9] + + vminps ymm4, ymm4, ymm1 // ymm4 <- x + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_alpha_7] + + vmulps ymm8, ymm4, ymm4 // ymm8 <- x^2 + + vmovaps ymm12, ymm2 + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_alpha_5] + vfmadd132ps ymm12, ymm3, ymm8 + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_alpha_3] + vfmadd132ps ymm12, ymm0, ymm8 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_alpha_1] + vfmadd132ps ymm12, ymm1, ymm8 + vbroadcastss ymm1, dword ptr [{{offset}} {{L}}coeffs_num_beta_6] + vfmadd132ps ymm12, ymm2, ymm8 + vbroadcastss ymm2, dword ptr [{{offset}} {{L}}coeffs_num_beta_4] + vfmadd132ps ymm12, ymm3, ymm8 + vbroadcastss ymm3, dword ptr [{{offset}} {{L}}coeffs_num_beta_2] + vfmadd132ps ymm12, ymm0, ymm8 + vbroadcastss ymm0, dword ptr [{{offset}} {{L}}coeffs_num_beta_0] + vmulps ymm4, ymm4, ymm12 + + vmovaps ymm12, ymm1 + vfmadd132ps ymm12, ymm2, ymm8 + vfmadd132ps ymm12, ymm3, ymm8 + vfmadd132ps ymm12, ymm0, ymm8 + + vdivps ymm4, ymm4, ymm12 + + vmovaps [rdi], ymm4 + add rdi, 32 + sub rsi, 8 + jnz {{L}}loop_1 + +{{L}}done: + +// ---------------------------------------------------------------------- + + ldmxcsr [rsp + 4] + + add rsp, 8 + + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + +{% if family == "windows" %} + pop rsi + pop rdi + + vmovaps xmm15, [rsp+16*9] + vmovaps xmm14, [rsp+16*8] + vmovaps xmm13, [rsp+16*7] + vmovaps xmm12, [rsp+16*6] + vmovaps xmm11, [rsp+16*5] + vmovaps xmm10, [rsp+16*4] + vmovaps xmm9, [rsp+16*3] + vmovaps xmm8, [rsp+16*2] + vmovaps xmm7, [rsp+16*1] + vmovaps xmm6, [rsp] +{% endif %} + + mov rsp, rbp + pop rbp + ret + +{% set float %}{% if msvc %} real4 {%else%} .float {%endif%}{% endset %} + +{{L}}coeffs_num_low: + {{float}} -8.9 +{{L}}coeffs_num_high: + {{float}} 8.9 + +{{L}}coeffs_num_alpha_13: + {{float}} -8.488492677e-14 +{{L}}coeffs_num_alpha_11: + {{float}} 5.277853000e-11 +{{L}}coeffs_num_alpha_9: + {{float}} -2.022500419e-8 +{{L}}coeffs_num_alpha_7: + {{float}} 0.00001115424833 +{{L}}coeffs_num_alpha_5: + {{float}} 0.003103950131 +{{L}}coeffs_num_alpha_3: + {{float}} 0.1308400453 +{{L}}coeffs_num_alpha_1: + {{float}} 0.9999999934 + +{{L}}coeffs_num_beta_6: + {{float}} 0.0002546136580 +{{L}}coeffs_num_beta_4: + {{float}} 0.02449515379 +{{L}}coeffs_num_beta_2: + {{float}} 0.4641733162 +{{L}}coeffs_num_beta_0: + {{float}} 1.0 + + + +{% if msvc %} +fma_tanh_f32_{{suffix}} endp +_text ends +end +{% else %} +.cfi_endproc +{% endif %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/postamble.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/postamble.j2 new file mode 100644 index 0000000..616a989 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/postamble.j2 @@ -0,0 +1,38 @@ +{{L}}return: + ldmxcsr [rsp + 4] + add rsp, 8 + + pop r15 + pop r14 + pop r13 + pop r12 + pop rbx + +{% if family == "windows" %} + pop rsi + pop rdi + + vmovaps xmm15, [rsp+16*9] + vmovaps xmm14, [rsp+16*8] + vmovaps xmm13, [rsp+16*7] + vmovaps xmm12, [rsp+16*6] + vmovaps xmm11, [rsp+16*5] + vmovaps xmm10, [rsp+16*4] + vmovaps xmm9, [rsp+16*3] + vmovaps xmm8, [rsp+16*2] + vmovaps xmm7, [rsp+16*1] + vmovaps xmm6, [rsp] +{% endif %} + + mov rsp, rbp + pop rbp + ret + +{% if msvc %} +fma_mmm_{{type}}_{{size}}_{{suffix}} endp +_text ends +end + +{% else %} +.cfi_endproc +{% endif %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/preamble.j2 b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/preamble.j2 new file mode 100644 index 0000000..9d98c1f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/tract-linalg-0.23.1/x86_64/fma/preamble.j2 @@ -0,0 +1,64 @@ + +{% if msvc %} + +_text segment +fma_mmm_{{type}}_{{size}}_{{suffix}} proc + +{% else %} + +.intel_syntax noprefix +.text +.p2align 5 +.globl {{G}}fma_mmm_{{type}}_{{size}}_{{suffix}} +{{G}}fma_mmm_{{type}}_{{size}}_{{suffix}}: +.cfi_startproc + +{% endif %} + + push rbp + mov rbp, rsp + +{% if family == "windows" %} +// https://www.agner.org/optimize/calling_conventions.pdf xmm6-15 are not scratch +// https://stackoverflow.com/questions/43358429/save-value-of-xmm-registers + and rsp,-16 + lea rsp,[rsp-160] + vmovaps [rsp], xmm6 + vmovaps [rsp+16*1],xmm7 + vmovaps [rsp+16*2],xmm8 + vmovaps [rsp+16*3],xmm9 + vmovaps [rsp+16*4],xmm10 + vmovaps [rsp+16*5],xmm11 + vmovaps [rsp+16*6],xmm12 + vmovaps [rsp+16*7],xmm13 + vmovaps [rsp+16*8],xmm14 + vmovaps [rsp+16*9],xmm15 + + push rdi + push rsi + + mov rdi, rcx + +{% endif %} + + push rbx + push r12 + push r13 + push r14 + push r15 + + sub rsp, 8 + +{% if family == "unix" %} +.cfi_def_cfa_offset 64 +{% endif %} + stmxcsr [rsp + 4] +{% if msvc %} + mov rax, 1FC0h +{% else %} + mov rax, 0x1FC0 +{% endif %} + mov [rsp], eax + ldmxcsr [rsp] + +{% include "dispatcher.j2" %} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/.cargo-ok b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/.cargo-ok new file mode 100644 index 0000000..5f8b795 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/.cargo-ok @@ -0,0 +1 @@ +{"v":1} \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/.cargo_vcs_info.json b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/.cargo_vcs_info.json new file mode 100644 index 0000000..4cf9928 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/.cargo_vcs_info.json @@ -0,0 +1,6 @@ +{ + "git": { + "sha1": "ba0c31290ad6f7836ef96efd62167c3501ed07fa" + }, + "path_in_vcs": "webrtc-sys" +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/.clang-format b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/.clang-format new file mode 100644 index 0000000..9fe4cd8 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/.clang-format @@ -0,0 +1,24 @@ +# Using the same .clang-format as libwebrtc +# https://github.com/webrtc-sdk/webrtc/blob/m104_release/.clang-format +BasedOnStyle: Chromium +--- +Language: Java +BasedOnStyle: Google +--- +Language: ObjC +BasedOnStyle: Google +BinPackParameters: false +BinPackArguments: false +ColumnLimit: 100 +ObjCBlockIndentWidth: 2 +AllowAllParametersOfDeclarationOnNextLine: true +AlignOperands: false +AlwaysBreakBeforeMultilineStrings: false +AllowShortFunctionsOnASingleLine: Inline +BreakBeforeTernaryOperators: false +IndentWrappedFunctionNames: true +ContinuationIndentWidth: 4 +ObjCSpaceBeforeProtocolList: true +--- +Language: Cpp +IncludeBlocks: Regroup \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/CHANGELOG.md b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/CHANGELOG.md new file mode 100644 index 0000000..6afaa7a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/CHANGELOG.md @@ -0,0 +1,359 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [0.3.23](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.22...rust-sdks/webrtc-sys@0.3.23) - 2026-02-16 + +### Other + +- add is_screencast to VideoSource ([#896](https://github.com/livekit/rust-sdks/pull/896)) + +## [0.3.22](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.21...rust-sdks/webrtc-sys@0.3.22) - 2026-02-09 + +### Fixed + +- fix the 440->441 samples issue and pass a noop callback for release ([#848](https://github.com/livekit/rust-sdks/pull/848)) + +### Other + +- Use workspace dependencies & settings ([#856](https://github.com/livekit/rust-sdks/pull/856)) + +## [0.3.21](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.20...rust-sdks/webrtc-sys@0.3.21) - 2026-01-15 + +### Other + +- Rename namespace for ffi bridge code. ([#836](https://github.com/livekit/rust-sdks/pull/836)) + +## [0.3.20](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.19...rust-sdks/webrtc-sys@0.3.20) - 2025-12-19 + +### Fixed + +- Exclude the desktop-capturer module link for mobile. ([#817](https://github.com/livekit/rust-sdks/pull/817)) + +## [0.3.19](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.18...rust-sdks/webrtc-sys@0.3.19) - 2025-12-17 + +### Fixed + +- lazy loading for additional dependencies. ([#814](https://github.com/livekit/rust-sdks/pull/814)) + +### Other + +- Expose WebRTC's audio_mixer ([#806](https://github.com/livekit/rust-sdks/pull/806)) + +## [0.3.18](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.17...rust-sdks/webrtc-sys@0.3.18) - 2025-12-04 + +### Other + +- initialize frame_.samples_per_channel_ ([#779](https://github.com/livekit/rust-sdks/pull/779)) +- Expose desktop capturer ([#725](https://github.com/livekit/rust-sdks/pull/725)) + +## [0.3.17](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.16...rust-sdks/webrtc-sys@0.3.17) - 2025-11-20 + +### Fixed + +- fix Android libwebrtc builds ([#791](https://github.com/livekit/rust-sdks/pull/791)) + +### Other + +- Enable H265 encoding & decoding on Nvidia GPU ([#776](https://github.com/livekit/rust-sdks/pull/776)) +- copy desktop_capture.ninja into C++ library archive ([#787](https://github.com/livekit/rust-sdks/pull/787)) +- enable Pipewire (Wayland) and X11 desktop capture ([#784](https://github.com/livekit/rust-sdks/pull/784)) + +## [0.3.16](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.15...rust-sdks/webrtc-sys@0.3.16) - 2025-10-27 + +### Fixed + +- fix unable to locate __arm_tpidr2_save for android ffi. ([#765](https://github.com/livekit/rust-sdks/pull/765)) + +### Other + +- Linux hardware acceleration build fixes ([#753](https://github.com/livekit/rust-sdks/pull/753)) + +## [0.3.15](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.14...rust-sdks/webrtc-sys@0.3.15) - 2025-10-22 + +### Other + +- License check ([#746](https://github.com/livekit/rust-sdks/pull/746)) +- put examples in root Cargo workspace ([#731](https://github.com/livekit/rust-sdks/pull/731)) + +## [0.3.14](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.13...rust-sdks/webrtc-sys@0.3.14) - 2025-10-13 + +### Added + +- *(e2ee)* add data channel encryption ([#708](https://github.com/livekit/rust-sdks/pull/708)) + +### Fixed + +- fix some potential audio issues, clean up the code a bit, and suppress some warnings ([#737](https://github.com/livekit/rust-sdks/pull/737)) +- fix linux so link issue. ([#733](https://github.com/livekit/rust-sdks/pull/733)) +- change search_dirs to use cc --print-search-dirs instead of clang --print-search-dirs ([#697](https://github.com/livekit/rust-sdks/pull/697)) + +### Other + +- bump libwebrtc libs version for webrtc-sys. ([#741](https://github.com/livekit/rust-sdks/pull/741)) +- Enable buffer scaling ([#473](https://github.com/livekit/rust-sdks/pull/473)) + +## [0.3.13](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.12...rust-sdks/webrtc-sys@0.3.13) - 2025-10-03 + +### Other + +- Fix empty audio frames after resample ([#722](https://github.com/livekit/rust-sdks/pull/722)) + +## [0.3.12](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.11...rust-sdks/webrtc-sys@0.3.12) - 2025-09-29 + +### Fixed + +- fix Builds/E2E Tests CI. ([#715](https://github.com/livekit/rust-sdks/pull/715)) + +### Other + +- nvidia codec improve ([#721](https://github.com/livekit/rust-sdks/pull/721)) +- Upgrade libwebrtc to m137. ([#696](https://github.com/livekit/rust-sdks/pull/696)) + +## [0.3.11](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.10...rust-sdks/webrtc-sys@0.3.11) - 2025-09-09 + +### Other + +- Optional flags for video hw codec. ([#701](https://github.com/livekit/rust-sdks/pull/701)) + +## [0.3.10](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.9...rust-sdks/webrtc-sys@0.3.10) - 2025-09-03 + +### Added + +- VA-API support for linux. ([#638](https://github.com/livekit/rust-sdks/pull/638)) + +### Fixed + +- hardware rendering ([#695](https://github.com/livekit/rust-sdks/pull/695)) +# Changelog + +## [0.3.9](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.8...rust-sdks/webrtc-sys@0.3.9) - 2025-06-17 + +### Other + +- updated the following local packages: webrtc-sys-build + +## [0.3.8](https://github.com/livekit/rust-sdks/compare/rust-sdks/webrtc-sys@0.3.7...rust-sdks/webrtc-sys@0.3.8) - 2025-06-11 + +### Fixed + +- fix libwebrtc.jar build issue ([#586](https://github.com/livekit/rust-sdks/pull/586)) + +### Other + +- bump version for webrtc (fix win CI) ([#650](https://github.com/livekit/rust-sdks/pull/650)) +- try to fix webrtc build for iOS/macOS. ([#646](https://github.com/livekit/rust-sdks/pull/646)) +- remove ([#633](https://github.com/livekit/rust-sdks/pull/633)) +- expose apm stream_delay ([#616](https://github.com/livekit/rust-sdks/pull/616)) +- Add i420_to_nv12 ([#605](https://github.com/livekit/rust-sdks/pull/605)) +- ffi-v0.13.0 ([#590](https://github.com/livekit/rust-sdks/pull/590)) +- add AudioProcessingModule ([#580](https://github.com/livekit/rust-sdks/pull/580)) + +## [0.3.7] - 2025-02-05 + +### Added + +- Expose DataChannel.bufferedAmount property + +## [0.3.6] - 2024-12-14 + +### Added + +- bump libwebrtc to m125 +## 0.3.33 (2026-05-29) + +### Fixes + +- Add native video pipeline timing instrumentation for local video measurements, exposing local publish and subscribe timing through async streams and subscriber overlay GPU upload and receive-to-GPU latency metrics through explicit timing observers. + +## 0.3.32 (2026-05-21) + +### Fixes + +#### feat: add Android application context initialization for PlatformAudio support. + +Android requires `ContextUtils.initialize(applicationContext)` before WebRTC audio components can be created. This change: + +- Adds `livekit_ffi_initialize_android_context()` C FFI function for Unity and other FFI consumers +- Uses `CreateAndroidAudioDeviceModule()` instead of generic `CreateAudioDeviceModule()` on Android +- Handles empty device GUIDs on Android (falls back to index 0) +- Documents Android-specific limitations: single default device, no app-level device selection + +Platform notes: +- Android device enumeration returns only one "default" device with empty name/GUID +- Audio routing (speaker/earpiece/Bluetooth) is controlled by Android's AudioManager, not WebRTC + +## 0.3.31 (2026-05-14) + +### Fixes + +- chore: bump libwebrtc version to webrtc-51ef663 +- Add `LIVEKIT_PREFERRED_HW_ENCODER` to prefer `nvenc` or `vaapi` hardware video encoding when both are available. + +#### fix: fix LICENSE.md generation in webrtc build scripts + +- Add fix_license_json_parsing.patch to handle GN warnings in JSON output +- Enable add_licenses.patch for iOS and Android builds (was commented out) +- Restore LICENSE.md copy in iOS build script (regression from #1053) + +The license generation script was failing because `gn desc --format=json` +outputs warnings before the JSON when certain build args trigger deprecation +notices. The new patch strips non-JSON content before parsing. + +#### Get WebRTC ADM into Rust - #1037 (@xianshijing-lk) + +This PR introduces platform audio device management via WebRTC's Audio Device Module (ADM). + +#### Features +- **ADM Proxy**: New `AdmProxy` class that switches between Dummy ADM (synthetic mode) and Platform ADM (real audio I/O) +- **PlatformAudio API**: High-level Rust API for microphone capture and speaker playout with AEC/AGC/NS +- **Device enumeration**: List and select recording/playout devices by index or GUID +- **Mode switching**: Seamlessly switch between synthetic mode (FFI callbacks) and platform mode (native speakers) while audio is active +- **FFI platform audio support**: Expose platform audio device enumeration and selection through `livekit-ffi` +- **Audio processing**: Configure echo cancellation, noise suppression, and auto gain control with platform-specific defaults (hardware on iOS, software elsewhere) + +#### Audio Modes +| Mode | Recording | Playout | Use Case | +|------|-----------|---------|----------| +| Synthetic | NativeAudioSource | Dummy ADM + FFI | Unity audio, agents | +| Platform | Platform ADM mic | Platform ADM speakers | VoIP with AEC | + +#### API +```rust +// Create PlatformAudio for microphone/speaker access +let audio = PlatformAudio::new()?; + +// Enumerate and select devices +for i in 0..audio.recording_devices() as u16 { + println!("Mic {}: {}", i, audio.recording_device_name(i)); +} +audio.set_recording_device(0)?; + +// Create audio track for publishing +let track = LocalAudioTrack::create_audio_track("mic", audio.rtc_source()); +``` + +## 0.3.30 (2026-05-11) + +### Fixes + +#### Fix missing RTC_OBJC_TYPE macros in webrtc-sys .mm files + +Wrap bare ObjC class references in `RTC_OBJC_TYPE()` in `objc_video_factory.mm` and `objc_video_frame_buffer.mm` to support builds with `rtc_objc_prefix` set. + +#### Fix WebRTC build scripts to properly report failures and fix C++ module compilation issues + +- Add `set -e` to all build scripts so CI properly reports build failures instead of silently creating empty/broken artifacts +- Re-add `use_clang_modules=false` to macOS, iOS, and Linux build scripts to fix C++ module compilation errors + +Without `use_clang_modules=false`, builds fail due to libc++ header incompatibilities (on macOS/iOS with Xcode 26.0) or other C++ module issues, resulting in: +- macOS/iOS: Empty `libwebrtc.a` (~13KB instead of ~700MB) +- Android: Missing `libwebrtc.jar` +- Linux: Incomplete artifacts + +The builds appeared successful because the scripts continued after ninja failures, but now with `set -e`, failures will be properly reported. + +## 0.3.29 (2026-05-10) + +### Fixes + +- Fix missing `libwebrtc.jar` for Android builds, harden build scripts +- fix race in download_webrtc to reduce flaky build - #1047 (@hechen-eng) +- Improve WebRTC build scripts and add external_audio_source patch - #1053 (@xianshijing-lk) + +## 0.3.28 (2026-04-23) + +### Features + +#### Add support for frame level packet trailer + +##890 by @chenosaurus + +- Add support to attach/parse frame level timestamps & frame ID to VideoTracks as a custom payload trailer. +- Breaking change in VideoFrame API, must include `frame_metadata` or use VideoFrame::new(). + +### Fixes + +- Add NVENC check when Nvidia GPU detected - #1015 (@chenosaurus) + +## 0.3.27 (2026-04-02) + +### Features + +#### chore: upgrade libwebrtc to m144. + +##965 by @cloudwebrtc + +### Fixes + +#### use the bounded buffer for video stream + +##956 by @xianshijing-lk + +Before this PR, it uses an unbounded buffer for video stream, that will cause multiple problems: +1, video will be lagged behind if rendering is slow or just wake up from background +2, it will be out of sync with audio + +This PRs provides options to set a bounded buffer for video stream, and use 1 buffer as the default option. + +## 0.3.26 (2026-03-31) + +### Fixes + +- fix unity android build with "livekit" prefixed jni - #983 (@xianshijing-lk) + +#### fix: fix unavailable sem symbol for Linux aarch64. + +##975 by @cloudwebrtc + +## 0.3.25 (2026-03-22) + +### Fixes + +- fix: enable AGC2 adaptive digital controller +- Fix H.264 codec matching + +#### E2EE: allow setting key_ring_size and key_derivation_algorithm, update webrtc to m144 + +##921 by @onestacked + +This PR uses [this webrtc-sdk PR](https://github.com/webrtc-sdk/webrtc/pull/224) to configure the KDF. + +I've tested this with https://codeberg.org/esoteric_programmer/matrix-jukebox and it is compatible with Element Call. + +Fixed: https://github.com/livekit/rust-sdks/issues/796 + +#### fix clang build issue from zed patches (#949) + +##950 by @cloudwebrtc + +* webrtc-sys: Use clang instead of gcc + +* Debug CI output for aarch64-linux + +* ci: Install lld for aarch64-linux FFI builders + +* webrtc-sys: Disable CREL + +## 0.3.24 (2026-03-13) + +### Fixes + +#### avoid getting webrtc into underrun + +Before this change, the Rust implementation would only start sending silence frames after missing 10 consecutive audio frames. This could cause WebRTC's audio pipeline to enter an underrun state when audio stopped temporarily. + +Once WebRTC enters underrun, resuming audio can significantly increase latency until the pipeline stabilizes again. In testing, this could add hundreds of milliseconds of additional latency when audio resumed shortly after the underrun. + +This change ensures silence frames are sent earlier to prevent the audio pipeline from entering underrun. By maintaining a continuous stream of audio (including silence), WebRTC can avoid unnecessary buffering and latency spikes when audio resumes. + +In testing, this reduces the additional latency observed after underrun recovery and results in more stable end-to-end audio latency. + +#### webrtc-sys: Handle gracefully lack of libva on linux + +##924 by @kubkon diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/Cargo.lock b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/Cargo.lock new file mode 100644 index 0000000..0199e2e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/Cargo.lock @@ -0,0 +1,1932 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" + +[[package]] +name = "anstyle-parse" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.102" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bitflags" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" + +[[package]] +name = "bumpalo" +version = "3.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" + +[[package]] +name = "cc" +version = "1.2.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "clap" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b193af5b67834b676abd72466a96c1024e6a6ad978a1f484bd90b85c94041351" +dependencies = [ + "clap_builder", +] + +[[package]] +name = "clap_builder" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +dependencies = [ + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_lex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" + +[[package]] +name = "codespan-reporting" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af491d569909a7e4dee0ad7db7f5341fef5c614d5b8ec8cf765732aba3cff681" +dependencies = [ + "serde", + "termcolor", + "unicode-width", +] + +[[package]] +name = "colorchoice" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "cxx" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "747d8437319e3a2f43d93b341c137927ca70c0f5dabeea7a005a73665e247c7e" +dependencies = [ + "cc", + "cxx-build", + "cxxbridge-cmd", + "cxxbridge-flags", + "cxxbridge-macro", + "foldhash", + "link-cplusplus", +] + +[[package]] +name = "cxx-build" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0f4697d190a142477b16aef7da8a99bfdc41e7e8b1687583c0d23a79c7afc1e" +dependencies = [ + "cc", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "scratch", + "syn", +] + +[[package]] +name = "cxxbridge-cmd" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0956799fa8678d4c50eed028f2de1c0552ae183c76e976cf7ca8c4e36a7c328" +dependencies = [ + "clap", + "codespan-reporting", + "indexmap", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "cxxbridge-flags" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23384a836ab4f0ad98ace7e3955ad2de39de42378ab487dc28d3990392cb283a" + +[[package]] +name = "cxxbridge-macro" +version = "1.0.194" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6acc6b5822b9526adfb4fc377b67128fdd60aac757cc4a741a6278603f763cf" +dependencies = [ + "indexmap", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "env_filter" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "env_logger" +version = "0.11.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" +dependencies = [ + "anstream", + "anstyle", + "env_filter", + "jiff", + "log", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "iri-string" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8e7418f59cc01c88316161279a7f665217ae316b388e58a0d10e29f54f1e5eb" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jiff" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" +dependencies = [ + "jiff-static", + "log", + "portable-atomic", + "portable-atomic-util", + "serde_core", +] + +[[package]] +name = "jiff-static" +version = "0.2.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "js-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "libc" +version = "0.2.183" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" + +[[package]] +name = "link-cplusplus" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f78c730aaa7d0b9336a299029ea49f9ee53b0ed06e9202e8cb7db9bae7b8c82" +dependencies = [ + "cc", +] + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "memchr" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "rand" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ec095654a25171c2124e9e3393a930bddbffdc939556c914957a4c3e0a87166" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" +dependencies = [ + "getrandom 0.3.4", +] + +[[package]] +name = "regex" +version = "1.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustls" +version = "0.23.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + +[[package]] +name = "rustls-pki-types" +version = "1.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "scratch" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d68f2ec51b097e4c1a75b681a8bec621909b5e91f15bb7b840c4f2f7b01148b2" + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "semver" +version = "1.0.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "socket2" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.50.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.2+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +dependencies = [ + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.114" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.91" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webrtc-sys" +version = "0.3.33" +dependencies = [ + "cc", + "cxx", + "cxx-build", + "env_logger", + "glob", + "log", + "pkg-config", + "webrtc-sys-build", +] + +[[package]] +name = "webrtc-sys-build" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d46da6b5a5cbd091fae0400f77189f4ca4807c0d9442b85838a584f28720570" +dependencies = [ + "anyhow", + "fs2", + "regex", + "reqwest", + "scratch", + "semver", + "zip", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.47" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "zip" +version = "0.6.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +dependencies = [ + "byteorder", + "crc32fast", + "crossbeam-utils", + "flate2", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/Cargo.toml b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/Cargo.toml new file mode 100644 index 0000000..f5d8881 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/Cargo.toml @@ -0,0 +1,60 @@ +# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO +# +# When uploading crates to the registry Cargo will automatically +# "normalize" Cargo.toml files for maximal compatibility +# with all versions of Cargo and also rewrite `path` dependencies +# to registry (e.g., crates.io) dependencies. +# +# If you are reading this file be aware that the original Cargo.toml +# will likely look very different (and much more reasonable). +# See Cargo.toml.orig for the original contents. + +[package] +edition = "2021" +name = "webrtc-sys" +version = "0.3.33" +build = "build.rs" +autolib = false +autobins = false +autoexamples = false +autotests = false +autobenches = false +description = "Unsafe bindings to libwebrtc" +homepage = "https://livekit.io" +readme = "README.md" +license = "Apache-2.0" +repository = "https://github.com/livekit/rust-sdks" + +[features] +default = [] + +[lib] +name = "webrtc_sys" +path = "src/lib.rs" + +[dependencies.cxx] +version = "1.0" + +[dependencies.log] +version = "0.4" + +[dev-dependencies.env_logger] +version = "0.11" + +[build-dependencies.cc] +version = "1.0" + +[build-dependencies.cxx-build] +version = "1.0" + +[build-dependencies.glob] +version = "0.3" + +[build-dependencies.pkg-config] +version = "0.3.22" + +[build-dependencies.webrtc-sys-build] +version = "0.3.18" + +[target.'cfg(target_os = "linux")'.build-dependencies.pkg-config] +version = "0.3.22" diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/Cargo.toml.orig b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/Cargo.toml.orig new file mode 100644 index 0000000..1d9a946 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/Cargo.toml.orig @@ -0,0 +1,28 @@ +[package] +name = "webrtc-sys" +version = "0.3.33" +edition.workspace = true +homepage = "https://livekit.io" +license.workspace = true +description = "Unsafe bindings to libwebrtc" +repository.workspace = true + +[features] +default = [] + +[dependencies] +cxx = "1.0" +log = { workspace = true } + +[build-dependencies] +webrtc-sys-build = { workspace = true } +cxx-build = "1.0" +glob = "0.3" +cc = "1.0" +pkg-config = "0.3.22" + +[target.'cfg(target_os = "linux")'.build-dependencies] +pkg-config = "0.3.22" + +[dev-dependencies] +env_logger = { workspace = true } diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/NOTICE.md b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/NOTICE.md new file mode 100644 index 0000000..7c6f836 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/NOTICE.md @@ -0,0 +1,426 @@ +## webrtc patches APACHE 2.0 License +License of some patches we use to build webrtc +``` +Copyright 2019-2022, Wandbox LLC (Original Author) +Copyright 2019-2022, tnoho (Original Author) +Copyright 2019-2022, Shiguredo Inc. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` +## littlebearlabs/arcas-io MIT License +The build system and the bindings are inspired by arcas-io (https://github.com/arcas-io) +``` +MIT License + +Copyright (c) 2022 arcas-io + +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. +Footer +``` +## Shiguredo Apache 2.0 License +The Android build system is inspired by Shiguredo sora-cpp-sdk. +``` +Sora C++ SDK + +Copyright 2021-2022, Wandbox LLC (Original Author) + +Copyright 2021-2022, Shiguredo Inc. + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS +``` + +## webrtc build scripts APACHE 2.0 License +The webrtc builds scripts are inspired by Unity-Technologies com.unity.webrtc +``` +com.unity.webrtc copyright © 2022 Unity Technologies ApS + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + --- + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS +``` diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/README.md b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/README.md new file mode 100644 index 0000000..f3c0446 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/README.md @@ -0,0 +1,16 @@ +# webrtc-sys + +This crate provides wrapper over the WebRTC API for use from Rust. +We use the crate [cxx.rs](https://cxx.rs/) to simplify our bindings. + +## Wrappers + +Most of our wrappers use the cxx.rs types compatible with Rust. +As most of our wrappers are stateless, we allow multiple instances of a specific wrapper to point to the same underlying webrtc pointer. (e.g: multiple livekit::MediaStreamTrack pointing to the same webrtc::MediaStreamTrackInterface). + +Threadsafe methods use the const keyword so we can easily call them from the Rust side without worrying about the mutability of the object. (This is similar on how Cell/UnsafeCell works but implemented on the C++ side: interior mutability). + +## Code + +We also use this C++ code to provide other needed utilities/features on the Rust side (e.g: tiny bindings to libyuv). + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/build.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/build.rs new file mode 100644 index 0000000..2437f8f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/build.rs @@ -0,0 +1,503 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::path::Path; +use std::path::PathBuf; +use std::{env, path, process::Command}; + +fn main() { + if env::var("DOCS_RS").is_ok() { + return; + } + + let target_os = env::var("CARGO_CFG_TARGET_OS").unwrap(); + let target_arch = env::var("CARGO_CFG_TARGET_ARCH").unwrap(); + let is_desktop = target_os == "linux" || target_os == "windows" || target_os == "macos"; + + println!("cargo:rerun-if-env-changed=LK_DEBUG_WEBRTC"); + println!("cargo:rerun-if-env-changed=LK_CUSTOM_WEBRTC"); + + let mut rust_files = vec![ + "src/peer_connection.rs", + "src/peer_connection_factory.rs", + "src/audio_device_controller.rs", + "src/media_stream.rs", + "src/media_stream_track.rs", + "src/audio_track.rs", + "src/video_track.rs", + "src/data_channel.rs", + "src/frame_cryptor.rs", + "src/jsep.rs", + "src/candidate.rs", + "src/rtp_parameters.rs", + "src/rtp_sender.rs", + "src/rtp_receiver.rs", + "src/rtp_transceiver.rs", + "src/rtc_error.rs", + "src/webrtc.rs", + "src/video_frame.rs", + "src/video_frame_buffer.rs", + "src/helper.rs", + "src/yuv_helper.rs", + "src/audio_resampler.rs", + "src/android.rs", + "src/prohibit_libsrtp_initialization.rs", + "src/recorded_audio_tap.rs", + "src/apm.rs", + "src/audio_mixer.rs", + "src/packet_trailer.rs", + ]; + + if is_desktop { + rust_files.push("src/desktop_capturer.rs"); + } + + let mut builder = cxx_build::bridges(rust_files); + + builder.files(&[ + "src/peer_connection.cpp", + "src/peer_connection_factory.cpp", + "src/audio_device_controller.cpp", + "src/media_stream.cpp", + "src/media_stream_track.cpp", + "src/audio_track.cpp", + "src/video_track.cpp", + "src/data_channel.cpp", + "src/jsep.cpp", + "src/candidate.cpp", + "src/rtp_receiver.cpp", + "src/rtp_sender.cpp", + "src/rtp_transceiver.cpp", + "src/rtp_parameters.cpp", + "src/rtc_error.cpp", + "src/webrtc.cpp", + "src/video_frame.cpp", + "src/video_frame_buffer.cpp", + "src/video_encoder_factory.cpp", + "src/video_decoder_factory.cpp", + "src/synthetic_audio_device.cpp", + "src/adm_proxy.cpp", + "src/audio_resampler.cpp", + "src/frame_cryptor.cpp", + "src/global_task_queue.cpp", + "src/prohibit_libsrtp_initialization.cpp", + "src/recorded_audio_tap.cpp", + "src/apm.cpp", + "src/audio_mixer.cpp", + "src/packet_trailer.cpp", + ]); + + if is_desktop { + builder.file("src/desktop_capturer.cpp"); + } + + let webrtc_dir = webrtc_sys_build::webrtc_dir(); + let webrtc_include = webrtc_dir.join("include"); + let webrtc_lib = webrtc_dir.join("lib"); + + if !webrtc_dir.exists() { + webrtc_sys_build::download_webrtc().unwrap(); + } + + builder.includes(&[ + path::PathBuf::from("./include"), + path::PathBuf::from("src/nvidia/NvCodec/include"), + webrtc_include.clone(), + webrtc_include.join("third_party/abseil-cpp/"), + webrtc_include.join("third_party/libyuv/include/"), + webrtc_include.join("third_party/libc++/"), + // For mac & ios + webrtc_include.join("sdk/objc"), + webrtc_include.join("sdk/objc/base"), + ]); + builder.define("WEBRTC_APM_DEBUG_DUMP", "0"); + + println!("cargo:rustc-link-search=native={}", webrtc_lib.to_str().unwrap()); + + for (key, value) in webrtc_sys_build::webrtc_defines() { + let value = value.as_deref(); + builder.define(key.as_str(), value); + } + + // Link webrtc library + println!("cargo:rustc-link-lib=static=webrtc"); + match target_os.as_str() { + "windows" => { + println!("cargo:rustc-link-lib=dylib=msdmo"); + println!("cargo:rustc-link-lib=dylib=wmcodecdspuuid"); + println!("cargo:rustc-link-lib=dylib=dmoguids"); + println!("cargo:rustc-link-lib=dylib=crypt32"); + println!("cargo:rustc-link-lib=dylib=iphlpapi"); + println!("cargo:rustc-link-lib=dylib=ole32"); + println!("cargo:rustc-link-lib=dylib=secur32"); + println!("cargo:rustc-link-lib=dylib=winmm"); + println!("cargo:rustc-link-lib=dylib=ws2_32"); + println!("cargo:rustc-link-lib=dylib=strmiids"); + println!("cargo:rustc-link-lib=dylib=d3d11"); + println!("cargo:rustc-link-lib=dylib=gdi32"); + println!("cargo:rustc-link-lib=dylib=dxgi"); + println!("cargo:rustc-link-lib=dylib=dwmapi"); + println!("cargo:rustc-link-lib=dylib=shcore"); + + //let path = env::current_dir().unwrap(); + //println!("cargo:rustc-link-search=native={}/vaapi-windows/x64/lib", path.display()); + //println!("cargo:rustc-link-lib=dylib=va"); + //println!("cargo:rustc-link-lib=dylib=va_win32"); + + builder + //.include("./vaapi-windows/DirectX-Headers-1.0/include") + //.include(path::PathBuf::from("./vaapi-windows/x64/include")) + //.file("vaapi-windows/DirectX-Headers-1.0/src/dxguids.cpp") + //.file("src/vaapi/vaapi_display_win32.cpp") + //.file("src/vaapi/vaapi_h264_encoder_wrapper.cpp") + //.file("src/vaapi/vaapi_encoder_factory.cpp") + //.file("src/vaapi/h264_encoder_impl.cpp") + .include("src/nvidia/NvCodec/include") + .flag("-Isrc/nvidia/NvCodec/include") + .flag("-Isrc/nvidia/NvCodec/NvCodec") + .file("src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoder.cpp") + .file("src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoderCuda.cpp") + .file("src/nvidia/h264_encoder_impl.cpp") + .file("src/nvidia/h265_encoder_impl.cpp") + .file("src/nvidia/nvidia_encoder_factory.cpp") + .file("src/nvidia/native_gpu_encode_bridge.cpp") + .file("src/nvidia/cuda_context.cpp") + .file("src/nvidia/cuda_driver_dynamic.cpp") + .flag("-DUSE_NVIDIA_VIDEO_ENCODER=1") + .flag("/std:c++20") + //.flag("/wd4819") + //.flag("/wd4068") + .flag("/EHsc"); + } + "linux" => { + println!("cargo:rustc-link-lib=dylib=rt"); + println!("cargo:rustc-link-lib=dylib=dl"); + println!("cargo:rustc-link-lib=dylib=pthread"); + println!("cargo:rustc-link-lib=dylib=m"); + + // In order to avoid any ABI mismatches we use the sysroot's headers. + add_gio_headers(&mut builder); + + for lib_name in ["glib-2.0", "gobject-2.0", "gio-2.0"] { + pkg_config::probe_library(lib_name).unwrap(); + } + + add_lazy_load_so( + &mut builder, + "desktop_capturer", + ["drm", "gbm", "X11", "Xfixes", "Xdamage", "Xrandr", "Xcomposite", "Xext"] + .map(String::from) + .to_vec(), + ); + + let x86 = target_arch == "x86_64" || target_arch == "i686"; + let arm = target_arch == "aarch64" || target_arch.contains("arm"); + + if x86 { + if let Some(libva_include) = pkg_config::get_variable("libva", "includedir").ok() { + // Do not use pkg_config::probe_library because libva is dlopened + // and pkg_config::probe_library would link it. + builder + .include(libva_include) + .file("src/vaapi/vaapi_display_drm.cpp") + .file("src/vaapi/vaapi_h264_encoder_wrapper.cpp") + .file("src/vaapi/vaapi_encoder_factory.cpp") + .file("src/vaapi/h264_encoder_impl.cpp") + .flag("-DUSE_VAAPI_VIDEO_CODEC=1"); + + add_lazy_load_so( + &mut builder, + "vaapi", + ["va", "va-drm"].map(String::from).to_vec(), + ); + } else { + println!("cargo:warning=libva not found; building without hardware accelerated video codecs"); + } + } + + if x86 || arm { + let cuda_include_dir = PathBuf::from("src/nvidia/NvCodec/include"); + + // libcuda and libnvcuvid are dlopened, so do not link them. + if cuda_include_dir.join("cuda.h").exists() { + match probe_egl() { + Some(egl) => { + for include_path in egl.include_paths { + builder.include(include_path); + } + builder + .include(cuda_include_dir) + .flag("-Isrc/nvidia/NvCodec/include") + .flag("-Isrc/nvidia/NvCodec/NvCodec") + .file("src/nvidia/NvCodec/NvCodec/NvDecoder/NvDecoder.cpp") + .file("src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoder.cpp") + .file("src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoderCuda.cpp") + .file("src/nvidia/h264_encoder_impl.cpp") + .file("src/nvidia/h265_encoder_impl.cpp") + .file("src/nvidia/h264_decoder_impl.cpp") + .file("src/nvidia/h265_decoder_impl.cpp") + .file("src/nvidia/nvidia_decoder_factory.cpp") + .file("src/nvidia/nvidia_encoder_factory.cpp") + .file("src/nvidia/native_gpu_encode_bridge.cpp") + .file("src/nvidia/cuda_context.cpp") + .flag("-Wno-deprecated-declarations") + .flag("-DUSE_NVIDIA_VIDEO_CODEC=1"); + println!("cargo:rustc-link-lib=dylib=EGL"); + + add_lazy_load_so( + &mut builder, + "nvidia", + ["cuda", "nvcuvid"].map(String::from).to_vec(), + ); + } + None => { + println!("cargo:warning=EGL headers not found; building without NVIDIA hardware accelerated video codec support"); + } + } + } else { + println!("cargo:warning=vendored cuda.h not found; building without hardware accelerated video codec support for NVidia GPUs"); + } + } + + builder + .flag("-Wno-changes-meaning") + .flag("-Wno-deprecated-declarations") + .flag("-std=c++20"); + } + "macos" => { + println!("cargo:rustc-link-lib=framework=Foundation"); + println!("cargo:rustc-link-lib=framework=AVFoundation"); + println!("cargo:rustc-link-lib=framework=CoreAudio"); + println!("cargo:rustc-link-lib=framework=AudioToolbox"); + println!("cargo:rustc-link-lib=framework=Appkit"); + println!("cargo:rustc-link-lib=framework=CoreMedia"); + println!("cargo:rustc-link-lib=framework=CoreGraphics"); + println!("cargo:rustc-link-lib=framework=VideoToolbox"); + println!("cargo:rustc-link-lib=framework=CoreVideo"); + println!("cargo:rustc-link-lib=framework=OpenGL"); + println!("cargo:rustc-link-lib=framework=Metal"); + println!("cargo:rustc-link-lib=framework=MetalKit"); + println!("cargo:rustc-link-lib=framework=QuartzCore"); + println!("cargo:rustc-link-lib=framework=IOKit"); + println!("cargo:rustc-link-lib=framework=IOSurface"); + println!("cargo:rustc-link-lib=framework=ScreenCaptureKit"); + + configure_darwin_sysroot(&mut builder); + + builder + .file("src/objc_video_factory.mm") + .file("src/objc_video_frame_buffer.mm") + .flag("-stdlib=libc++") + .flag("-std=c++20") + .flag("-Wno-nullability-completeness"); + } + "ios" => { + println!("cargo:rustc-link-lib=framework=Foundation"); + println!("cargo:rustc-link-lib=framework=CoreFoundation"); + println!("cargo:rustc-link-lib=framework=AVFoundation"); + println!("cargo:rustc-link-lib=framework=CoreAudio"); + println!("cargo:rustc-link-lib=framework=UIKit"); + println!("cargo:rustc-link-lib=framework=CoreVideo"); + println!("cargo:rustc-link-lib=framework=CoreGraphics"); + println!("cargo:rustc-link-lib=framework=CoreMedia"); + println!("cargo:rustc-link-lib=framework=VideoToolbox"); + println!("cargo:rustc-link-lib=framework=AudioToolbox"); + println!("cargo:rustc-link-lib=framework=OpenGLES"); + println!("cargo:rustc-link-lib=framework=GLKit"); + println!("cargo:rustc-link-lib=framework=Metal"); + println!("cargo:rustc-link-lib=framework=MetalKit"); + println!("cargo:rustc-link-lib=framework=Network"); + println!("cargo:rustc-link-lib=framework=QuartzCore"); + + configure_darwin_sysroot(&mut builder); + + builder + .file("src/objc_video_factory.mm") + .file("src/objc_video_frame_buffer.mm") + .flag("-std=c++20"); + } + "android" => { + webrtc_sys_build::configure_jni_symbols().unwrap(); + + println!("cargo:rustc-link-lib=EGL"); + println!("cargo:rustc-link-lib=OpenSLES"); + println!("cargo:rustc-link-lib=c++_static"); + println!("cargo:rustc-link-lib=c++abi"); + + configure_android_sysroot(&mut builder); + builder.file("src/android.cpp").flag("-std=c++20"); + } + _ => { + panic!("Unsupported target, {}", target_os); + } + } + + // TODO(theomonnom) Only add this define when building tests + builder.define("LIVEKIT_TEST", None); + builder.warnings(false).compile("webrtcsys-cxx"); + + for entry in glob::glob("./src/**/*.cpp").unwrap() { + println!("cargo:rerun-if-changed={}", entry.unwrap().display()); + } + + for entry in glob::glob("./src/**/*.mm").unwrap() { + println!("cargo:rerun-if-changed={}", entry.unwrap().display()); + } + + for entry in glob::glob("./include/**/*.h").unwrap() { + println!("cargo:rerun-if-changed={}", entry.unwrap().display()); + } + + for entry in glob::glob("./src/nvidia/**/*.h").unwrap() { + println!("cargo:rerun-if-changed={}", entry.unwrap().display()); + } + + if target_os.as_str() == "android" { + copy_libwebrtc_jar(&PathBuf::from(Path::new(&webrtc_dir))); + } +} + +fn copy_libwebrtc_jar(webrtc_dir: &PathBuf) { + let jar_path = webrtc_dir.join("libwebrtc.jar"); + let output_path = get_output_path(); + let output_jar_path = output_path.join("libwebrtc.jar"); + let res = std::fs::copy(jar_path, output_jar_path); + if let Err(e) = res { + println!("Failed to copy libwebrtc.jar: {}", e); + } +} + +fn get_output_path() -> PathBuf { + let manifest_dir_string = env::var("CARGO_MANIFEST_DIR").unwrap(); + let build_type = env::var("PROFILE").unwrap(); + let build_target = env::var("TARGET").unwrap(); + let path = + Path::new(&manifest_dir_string).join("../target").join(build_target).join(build_type); + return PathBuf::from(path); +} + +fn configure_darwin_sysroot(builder: &mut cc::Build) { + let target_os = webrtc_sys_build::target_os(); + + let sdk = match target_os.as_str() { + "mac" => "macosx", + "ios-device" => "iphoneos", + "ios-simulator" => "iphonesimulator", + _ => panic!("Unsupported target_os: {}", target_os), + }; + + let clang_rt = match target_os.as_str() { + "mac" => "clang_rt.osx", + "ios-device" => "clang_rt.ios", + "ios-simulator" => "clang_rt.iossim", + _ => panic!("Unsupported target_os: {}", target_os), + }; + + println!("cargo:rustc-link-lib={}", clang_rt); + println!("cargo:rustc-link-arg=-ObjC"); + + let sysroot = Command::new("xcrun").args(["--sdk", sdk, "--show-sdk-path"]).output().unwrap(); + + let sysroot = String::from_utf8_lossy(&sysroot.stdout); + let sysroot = sysroot.trim(); + + let search_dirs = Command::new("cc").arg("--print-search-dirs").output().unwrap(); + + let search_dirs = String::from_utf8_lossy(&search_dirs.stdout); + for line in search_dirs.lines() { + if line.contains("libraries: =") { + let path = line.split('=').nth(1).unwrap(); + let path = format!("{}/lib/darwin", path); + println!("cargo:rustc-link-search={}", path); + } + } + + builder.flag(format!("-isysroot{}", sysroot).as_str()); +} + +fn configure_android_sysroot(builder: &mut cc::Build) { + let toolchain = webrtc_sys_build::android_ndk_toolchain().unwrap(); + let sysroot = toolchain.join("sysroot").canonicalize().unwrap(); + builder.flag(format!("-isysroot{}", sysroot.display()).as_str()); +} + +fn add_lazy_load_so(builder: &mut cc::Build, name: &str, libraries: Vec) { + let target_arch = webrtc_sys_build::target_arch(); + for lib_name in libraries { + let mut arch_dir = "x86_64-linux-gnu"; + if target_arch.contains("arm64") { + arch_dir = "aarch64-linux-gnu"; + } + let implib_file_c_name = "src/lazy_load_deps_for/".to_owned() + + name + + "/" + + arch_dir + + "/lib" + + &lib_name + + ".so.init.c"; + let implib_file_asm_name = "src/lazy_load_deps_for/".to_owned() + + name + + "/" + + arch_dir + + "/lib" + + &lib_name + + ".so.tramp.S"; + builder.file(implib_file_c_name).file(implib_file_asm_name); + } +} + +fn add_gio_headers(builder: &mut cc::Build) { + let webrtc_dir = webrtc_sys_build::webrtc_dir(); + let target_arch = webrtc_sys_build::target_arch(); + let target_arch_sysroot = match target_arch.as_str() { + "arm64" => "arm64", + "x64" => "amd64", + _ => panic!("unsupported arch"), + }; + let sysroot_path = format!("include/build/linux/debian_bullseye_{target_arch_sysroot}-sysroot"); + let sysroot = webrtc_dir.join(sysroot_path); + let glib_path = sysroot.join("usr/include/glib-2.0"); + println!("cargo:info=add_gio_headers {}", glib_path.display()); + + builder.include(&glib_path); + let arch_specific_path = match target_arch.as_str() { + "x64" => "x86_64-linux-gnu", + "arm64" => "aarch64-linux-gnu", + _ => panic!("unsupported target"), + }; + + let glib_path_config = sysroot.join("usr/lib"); + let glib_path_config = glib_path_config.join(arch_specific_path); + let glib_path_config = glib_path_config.join("glib-2.0/include"); + builder.include(&glib_path_config); +} + +fn probe_egl() -> Option { + println!("cargo:rerun-if-env-changed=EGL_NO_PKG_CONFIG"); + let egl = pkg_config::Config::new() + .cargo_metadata(false) + .probe("egl") + .ok()?; + if !egl.include_paths.iter().any(|path| path.join("EGL/egl.h").exists()) { + return None; + } + if !egl + .include_paths + .iter() + .any(|path| path.join("EGL/eglext.h").exists()) + { + return None; + } + Some(egl) +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/compile_flags.txt b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/compile_flags.txt new file mode 100644 index 0000000..e1d524d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/compile_flags.txt @@ -0,0 +1,22 @@ +-xc++ +-xobjective-c +-xobjective-c++ +-std=c++17 +-Iinclude +-Ilibwebrtc/src +-Ilibwebrtc/src/third_party/abseil-cpp +-Ilibwebrtc/src/third_party/libc++ +-Ilibwebrtc/src/third_party/libyuv/include +-Ilibwebrtc/src/sdk/objc/base +-Ilibwebrtc/src/sdk/objc +-I../target/cxxbridge +-DWEBRTC_POSIX +-DWEBRTC_ANDROID +-DNDEBUG +-DWEBRTC_ENABLE_SYMBOL_EXPORT +-DWEBRTC_APM_DEBUG_DUMP=0 +-D__ANDROID_API__=29 +-DWEBRTC_LIBRARY_IMPL +--sysroot=/Users/theomonnom/Library/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64/sysroot +--target=aarch64-none-linux-android +--gcc-toolchain=/Users/theomonnom/Library/Android/sdk/ndk-bundle/toolchains/llvm/prebuilt/darwin-x86_64 diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/adm_proxy.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/adm_proxy.h new file mode 100644 index 0000000..79538ab --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/adm_proxy.h @@ -0,0 +1,305 @@ +/* + * Copyright 2026 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "api/environment/environment.h" +#include "api/scoped_refptr.h" +#include "livekit/recorded_audio_tap.h" +#include "livekit/synthetic_audio_device.h" +#include "modules/audio_device/include/audio_device.h" +#include "modules/audio_device/include/audio_device_defines.h" +#include "rtc_base/synchronization/mutex.h" + +namespace webrtc { +class Thread; +} // namespace webrtc + +namespace livekit_ffi { + +/// ADM Proxy that manages synthetic and platform audio modes. +/// +/// This proxy implements the AudioDeviceModule interface and switches between: +/// 1. **Synthetic mode**: Uses `SyntheticAudioDevice`, which pumps the WebRTC +/// audio pipeline without platform audio. Remote audio is delivered via FFI +/// callbacks to external audio systems (e.g., Unity AudioSource). +/// 2. **Platform mode**: Real audio I/O through the Platform ADM with microphone +/// capture and speaker playout. Used when PlatformAudio is active for VoIP +/// with AEC. +/// +/// ## Mode Selection +/// +/// - **Playout**: Uses Platform ADM when `ref_count > 0 && playout_enabled`, +/// otherwise uses synthetic mode (internal audio pumping task). +/// - **Recording**: Uses Platform ADM when `ref_count > 0 && recording_enabled`, +/// otherwise recording is unavailable (synthetic mode has no microphone). +/// +/// ## Lifecycle Management +/// +/// Platform ADM creation is attempted eagerly at construction (for iOS +/// compatibility). If that attempt fails (e.g. the OS audio stack is still +/// starting up), creation is retried on demand via EnsurePlatformAdm() and +/// AcquirePlatformAdm() instead of failing for the process lifetime. +/// Reference counting controls which mode is active: +/// - `AcquirePlatformAdm()`: Increments ref count +/// - `ReleasePlatformAdm()`: Decrements ref count +/// - When ref_count is 0, playout uses synthetic mode +/// +/// ## Audio Modes +/// +/// | Mode | Recording | Playout | Use Case | +/// |------|-----------|---------|----------| +/// | Synthetic | NativeAudioSource | Internal task + FFI | Unity audio, agents | +/// | Platform | Platform ADM mic | Platform ADM speakers | VoIP with AEC | +/// +class AdmProxy : public webrtc::AudioDeviceModule { + public: + explicit AdmProxy(const webrtc::Environment& env, + webrtc::Thread* worker_thread); + ~AdmProxy() override; + + // =========================================================================== + // Platform ADM Lifecycle Management + // =========================================================================== + + /// Acquires a reference to the Platform ADM. + /// + /// On first call, creates and initializes the Platform ADM. On subsequent + /// calls, just increments the reference count. + /// + /// @return true if Platform ADM is ready for use, false if initialization failed. + bool AcquirePlatformAdm(); + + /// Releases a reference to the Platform ADM. + /// + /// When the reference count reaches zero, the Platform ADM is terminated + /// and the proxy returns to synthetic mode. + void ReleasePlatformAdm(); + + /// Returns the current reference count for the Platform ADM. + int platform_adm_ref_count() const; + + /// Returns true if Platform ADM is currently active (ref_count > 0). + bool is_platform_adm_active() const; + + /// Ensures the Platform ADM exists, creating and initializing it if needed. + /// + /// Platform ADM creation can fail transiently when the OS audio stack is + /// not ready yet (e.g. an app launched at login racing coreaudiod or the + /// Windows audio services). A failed attempt leaves platform_adm_ null so + /// the next call retries instead of staying broken for the process + /// lifetime. + /// + /// @return true if the Platform ADM is available after the call. + bool EnsurePlatformAdm(); + + /// Returns true if the Platform ADM has been created and initialized. + /// Distinguishes "audio stack unavailable" from "zero audio devices". + bool platform_adm_available() const; + + // =========================================================================== + // Recording/Playout Control + // =========================================================================== + + /// Control whether recording (microphone) is enabled. + /// + /// When disabled (default), InitRecording/StartRecording return success but + /// do nothing. This allows NativeAudioSource to work without interference. + /// + /// @note Only effective when Platform ADM is active. + void set_recording_enabled(bool enabled); + bool recording_enabled() const; + + /// Control whether playout goes through Platform ADM speakers. + /// + /// When disabled (default), playout uses synthetic mode - remote audio is + /// delivered via FFI callbacks to the application (e.g., Unity AudioSource). + /// + /// When enabled, remote audio plays through the platform speakers with AEC. + /// + /// @note Only effective when Platform ADM is active. + void set_playout_enabled(bool enabled); + bool playout_enabled() const; + + // =========================================================================== + // AudioDeviceModule Interface + // =========================================================================== + + int32_t ActiveAudioLayer(AudioLayer* audioLayer) const override; + int32_t RegisterAudioCallback(webrtc::AudioTransport* transport) override; + + int32_t Init() override; + int32_t Terminate() override; + bool Initialized() const override; + + int16_t PlayoutDevices() override; + int16_t RecordingDevices() override; + int32_t PlayoutDeviceName(uint16_t index, + char name[webrtc::kAdmMaxDeviceNameSize], + char guid[webrtc::kAdmMaxGuidSize]) override; + int32_t RecordingDeviceName(uint16_t index, + char name[webrtc::kAdmMaxDeviceNameSize], + char guid[webrtc::kAdmMaxGuidSize]) override; + + int32_t SetPlayoutDevice(uint16_t index) override; + int32_t SetPlayoutDevice(WindowsDeviceType device) override; + int32_t SetRecordingDevice(uint16_t index) override; + int32_t SetRecordingDevice(WindowsDeviceType device) override; + + int32_t PlayoutIsAvailable(bool* available) override; + int32_t InitPlayout() override; + bool PlayoutIsInitialized() const override; + int32_t RecordingIsAvailable(bool* available) override; + int32_t InitRecording() override; + bool RecordingIsInitialized() const override; + + int32_t StartPlayout() override; + int32_t StopPlayout() override; + bool Playing() const override; + int32_t StartRecording() override; + int32_t StopRecording() override; + bool Recording() const override; + + int32_t InitSpeaker() override; + bool SpeakerIsInitialized() const override; + int32_t InitMicrophone() override; + bool MicrophoneIsInitialized() const override; + + int32_t SpeakerVolumeIsAvailable(bool* available) override; + int32_t SetSpeakerVolume(uint32_t volume) override; + int32_t SpeakerVolume(uint32_t* volume) const override; + int32_t MaxSpeakerVolume(uint32_t* maxVolume) const override; + int32_t MinSpeakerVolume(uint32_t* minVolume) const override; + + int32_t MicrophoneVolumeIsAvailable(bool* available) override; + int32_t SetMicrophoneVolume(uint32_t volume) override; + int32_t MicrophoneVolume(uint32_t* volume) const override; + int32_t MaxMicrophoneVolume(uint32_t* maxVolume) const override; + int32_t MinMicrophoneVolume(uint32_t* minVolume) const override; + + int32_t SpeakerMuteIsAvailable(bool* available) override; + int32_t SetSpeakerMute(bool enable) override; + int32_t SpeakerMute(bool* enabled) const override; + + int32_t MicrophoneMuteIsAvailable(bool* available) override; + int32_t SetMicrophoneMute(bool enable) override; + int32_t MicrophoneMute(bool* enabled) const override; + + int32_t StereoPlayoutIsAvailable(bool* available) const override; + int32_t SetStereoPlayout(bool enable) override; + int32_t StereoPlayout(bool* enabled) const override; + int32_t StereoRecordingIsAvailable(bool* available) const override; + int32_t SetStereoRecording(bool enable) override; + int32_t StereoRecording(bool* enabled) const override; + + int32_t PlayoutDelay(uint16_t* delayMS) const override; + + bool BuiltInAECIsAvailable() const override; + bool BuiltInAGCIsAvailable() const override; + bool BuiltInNSIsAvailable() const override; + + int32_t EnableBuiltInAEC(bool enable) override; + int32_t EnableBuiltInAGC(bool enable) override; + int32_t EnableBuiltInNS(bool enable) override; + +#if defined(WEBRTC_IOS) + int GetPlayoutAudioParameters(webrtc::AudioParameters* params) const override; + int GetRecordAudioParameters(webrtc::AudioParameters* params) const override; +#endif + + int32_t SetObserver(webrtc::AudioDeviceObserver* observer) override; + + private: + // Returns true if platform mode is active for playout + // (ref_count > 0 && playout_enabled) + bool is_platform_playout_active() const; + + // Returns the ADM to use for recording operations + // - Platform ADM when recording is enabled (ref_count > 0 && recording_enabled) + // - nullptr otherwise (recording not available in synthetic mode) + webrtc::AudioDeviceModule* recording_adm() const; + + // Switches playout mode based on current state. + // Called when ref_count or playout_enabled changes. + // If playout is active, stops the old mode and starts the new one. + // Must be called with mutex_ held. + void SwitchPlayoutModeIfNeeded(); + + // Switches recording to the correct ADM based on current mode. + // Called when ref_count or recording_enabled changes. + // If recording is active, stops the old ADM and starts the new one. + // Must be called with mutex_ held. + void SwitchRecordingAdmIfNeeded(); + + // Lazily creates and initializes the Platform ADM. On failure the ADM + // stays null so a later call can retry once the OS audio stack is ready. + // Must be called with mutex_ held. + // Returns true if ADM is available after the call. + bool EnsurePlatformAdmCreated(); + + // Re-applies state that predates a late Platform ADM creation: the + // registered audio transport and any previously selected devices. + // Must be called with mutex_ held and platform_adm_ non-null. + void RestorePlatformAdmStateLocked(); + + const webrtc::Environment env_; + webrtc::Thread* worker_thread_; + + // Mutex for thread-safe access to mutable state + mutable webrtc::Mutex mutex_; + + // Synthetic ADM for synthetic mode - pumps the WebRTC audio pipeline without + // platform audio via SyntheticAudioDevice's internal task. + webrtc::scoped_refptr synthetic_adm_; + + // Platform ADM for real audio I/O (microphone capture, speaker playout with AEC) + webrtc::scoped_refptr platform_adm_; + + // Reference count for Platform ADM users (PlatformAudio instances) + int platform_adm_ref_count_ = 0; + + // Audio transport callback (registered by WebRTC) + webrtc::AudioTransport* audio_transport_ = nullptr; + + // Interposed between the platform ADM and audio_transport_ so recorded + // microphone frames can be teed to the global recorded-audio sink. Forwards + // every call to audio_transport_, leaving the send and playout paths intact. + RecordingTransportProxy recording_transport_proxy_; + + // State tracking + bool playout_initialized_ = false; + bool recording_initialized_ = false; + bool playing_ = false; + bool recording_ = false; + + // Control flags + // When false (default), recording operations are no-ops (NativeAudioSource mode) + bool recording_enabled_ = false; + // When false (default), playout uses synthetic mode (internal task pumps audio) + bool playout_enabled_ = false; + + // Selected device information (for re-initialization after ADM restart) + // We store both index and GUID. GUID is preferred for restoration as it's + // stable across device hot-plug events. + uint16_t selected_playout_device_ = 0; + uint16_t selected_recording_device_ = 0; + std::string selected_playout_guid_; + std::string selected_recording_guid_; +}; + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/android.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/android.h new file mode 100644 index 0000000..f7b9f24 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/android.h @@ -0,0 +1,56 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include + +#include "api/video_codecs/video_decoder_factory.h" +#include "api/video_codecs/video_encoder_factory.h" + +namespace livekit_ffi { +typedef JavaVM JavaVM; +} // namespace livekit_ffi +#include "webrtc-sys/src/android.rs.h" + +namespace livekit_ffi { + +/// Initialize Android WebRTC with the JVM. +/// This is called automatically by init_android_context(), so you only need to +/// call this directly in JNI_OnLoad or if you don't have an Android Context. +/// This function is idempotent - safe to call multiple times. +/// +/// @param jvm The JavaVM pointer +void init_android(JavaVM* jvm); + +/// Initialize Android WebRTC with the application context. +/// This is the main initialization function - it calls init_android() internally +/// and then initializes ContextUtils for PlatformAudio support. +/// This function is idempotent - safe to call multiple times. +/// +/// @param jvm The JavaVM pointer +/// @param context The Android application context (jobject cast to uintptr_t) +/// @return true if context initialization was successful, false otherwise. +/// Note: JVM init (init_android) always happens regardless of return value. +bool init_android_context(JavaVM* jvm, uintptr_t context); + +std::unique_ptr CreateAndroidVideoEncoderFactory(); +std::unique_ptr CreateAndroidVideoDecoderFactory(); + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/apm.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/apm.h new file mode 100644 index 0000000..1390616 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/apm.h @@ -0,0 +1,76 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "api/scoped_refptr.h" +#include "api/video_codecs/video_decoder_factory.h" +#include "api/video_codecs/video_encoder_factory.h" +#include "modules/audio_processing/aec3/echo_canceller3.h" +#include "modules/audio_processing/audio_buffer.h" + +namespace livekit_ffi { + +struct AudioProcessingConfig { + bool echo_canceller_enabled; + bool gain_controller_enabled; + bool high_pass_filter_enabled; + bool noise_suppression_enabled; + + webrtc::AudioProcessing::Config ToWebrtcConfig() const { + webrtc::AudioProcessing::Config config; + config.echo_canceller.enabled = echo_canceller_enabled; + config.gain_controller2.enabled = gain_controller_enabled; + config.gain_controller2.adaptive_digital.enabled = gain_controller_enabled; + config.high_pass_filter.enabled = high_pass_filter_enabled; + config.noise_suppression.enabled = noise_suppression_enabled; + return config; + } +}; + +class AudioProcessingModule { + public: + AudioProcessingModule(const AudioProcessingConfig& config); + + int process_stream(const int16_t* src, + size_t src_len, + int16_t* dst, + size_t dst_len, + int sample_rate, + int num_channels); + + int process_reverse_stream(const int16_t* src, + size_t src_len, + int16_t* dst, + size_t dst_len, + int sample_rate, + int num_channels); + + int set_stream_delay_ms(int delay_ms); + + private: + webrtc::scoped_refptr apm_; +}; + +std::unique_ptr create_apm( + bool echo_canceller_enabled, + bool gain_controller_enabled, + bool high_pass_filter_enabled, + bool noise_suppression_enabled); + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/audio_device_controller.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/audio_device_controller.h new file mode 100644 index 0000000..83fae3a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/audio_device_controller.h @@ -0,0 +1,85 @@ +/* + * Copyright 2026 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "api/scoped_refptr.h" +#include "livekit/adm_proxy.h" +#include "rust/cxx.h" + +namespace livekit_ffi { + +class AudioDeviceController { + public: + explicit AudioDeviceController(webrtc::scoped_refptr adm_proxy); + + // Device enumeration + int16_t playout_devices() const; + int16_t recording_devices() const; + rust::String playout_device_name(uint16_t index) const; + rust::String recording_device_name(uint16_t index) const; + rust::String playout_device_guid(uint16_t index) const; + rust::String recording_device_guid(uint16_t index) const; + + // Device selection + bool set_playout_device(uint16_t index) const; + bool set_recording_device(uint16_t index) const; + bool set_playout_device_by_guid(rust::String guid) const; + bool set_recording_device_by_guid(rust::String guid) const; + + // Recording control + bool stop_recording() const; + bool init_recording() const; + bool start_recording() const; + bool recording_is_initialized() const; + + // Playout control + bool stop_playout() const; + bool init_playout() const; + bool start_playout() const; + bool playout_is_initialized() const; + + // Built-in audio processing + bool builtin_aec_is_available() const; + bool builtin_agc_is_available() const; + bool builtin_ns_is_available() const; + bool enable_builtin_aec(bool enable) const; + bool enable_builtin_agc(bool enable) const; + bool enable_builtin_ns(bool enable) const; + + // ADM recording control + void set_adm_recording_enabled(bool enabled) const; + bool adm_recording_enabled() const; + + // ADM playout control + void set_adm_playout_enabled(bool enabled) const; + bool adm_playout_enabled() const; + + // Platform ADM lifecycle management + bool acquire_platform_adm() const; + void release_platform_adm() const; + int platform_adm_ref_count() const; + bool is_platform_adm_active() const; + bool ensure_platform_adm() const; + bool platform_adm_available() const; + + private: + webrtc::scoped_refptr adm_proxy_; +}; + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/audio_mixer.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/audio_mixer.h new file mode 100644 index 0000000..395c0ac --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/audio_mixer.h @@ -0,0 +1,88 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "api/audio/audio_mixer.h" +#include "api/scoped_refptr.h" +#include "modules/audio_mixer/audio_mixer_impl.h" +#include "modules/audio_processing/audio_buffer.h" +#include "rtc_base/synchronization/mutex.h" +#include "rust/cxx.h" + +namespace livekit_ffi { +class AudioMixer; +class NativeAudioFrame; +} // namespace livekit_ffi + +#include "webrtc-sys/src/audio_mixer.rs.h" + +namespace livekit_ffi { + +class NativeAudioFrame { + public: + NativeAudioFrame(webrtc::AudioFrame* frame) : frame_(frame) {} + void update_frame(uint32_t timestamp, + const int16_t* data, + size_t samples_per_channel, + int sample_rate_hz, + size_t num_channels); + + private: + webrtc::AudioFrame* frame_; +}; + +class AudioMixerSource : public webrtc::AudioMixer::Source { + public: + AudioMixerSource(rust::Box source); + + AudioFrameInfo GetAudioFrameWithInfo( + int sample_rate_hz, + webrtc::AudioFrame* audio_frame) override; + + int Ssrc() const override; + + int PreferredSampleRate() const override; + + ~AudioMixerSource() {} + + private: + rust::Box source_; +}; + +class AudioMixer { + public: + AudioMixer(); + + void add_source(rust::Box source); + + void remove_source(int ssrc); + + size_t mix(size_t num_channels); + const int16_t* data() const; + + private: + mutable webrtc::Mutex sources_mutex_; + webrtc::AudioFrame frame_; + std::vector> sources_; + webrtc::scoped_refptr audio_mixer_; +}; + +std::unique_ptr create_audio_mixer(); + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/audio_resampler.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/audio_resampler.h new file mode 100644 index 0000000..a8a186a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/audio_resampler.h @@ -0,0 +1,47 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "api/audio/audio_frame.h" +#include "api/data_channel_interface.h" +#include "common_audio/resampler/include/push_resampler.h" +#include "livekit/webrtc.h" +#include "rust/cxx.h" + +namespace livekit_ffi { + +class AudioResampler { + public: + size_t remix_and_resample(const int16_t* src, + size_t samples_per_channel, + size_t num_channels, + int sample_rate_hz, + size_t dest_num_channels, + int dest_sample_rate_hz); + + const int16_t* data() const; + + private: + webrtc::AudioFrame frame_; + webrtc::PushResampler resampler_; +}; + +std::unique_ptr create_audio_resampler(); + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/audio_track.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/audio_track.h new file mode 100644 index 0000000..a21fc58 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/audio_track.h @@ -0,0 +1,206 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include "api/audio/audio_frame.h" +#include "api/audio_options.h" +#include "api/task_queue/task_queue_factory.h" +#include "common_audio/resampler/include/push_resampler.h" +#include "livekit/helper.h" +#include "livekit/media_stream_track.h" +#include "livekit/webrtc.h" +#include "pc/local_audio_source.h" +#include "rtc_base/synchronization/mutex.h" +#include "api/task_queue/task_queue_base.h" +#include "rtc_base/task_utils/repeating_task.h" +#include "rtc_base/thread_annotations.h" +#include "rust/cxx.h" + +namespace livekit_ffi { +class AudioTrack; +class NativeAudioSink; +class AudioTrackSource; +class SourceContext; + +using CompleteCallback = void (*)(const livekit_ffi::SourceContext*); +} // namespace livekit_ffi +#include "webrtc-sys/src/audio_track.rs.h" + +namespace livekit_ffi { + +class AudioTrack : public MediaStreamTrack { + private: + friend RtcRuntime; + AudioTrack(std::shared_ptr rtc_runtime, + webrtc::scoped_refptr track); + + public: + ~AudioTrack(); + + void add_sink(const std::shared_ptr& sink) const; + void remove_sink(const std::shared_ptr& sink) const; + + private: + webrtc::AudioTrackInterface* track() const { + return static_cast(track_.get()); + } + + mutable webrtc::Mutex mutex_; + + // Same for VideoTrack: + // Keep a strong reference to the added sinks, so we don't need to + // manage the lifetime safety on the Rust side + mutable std::vector> sinks_; +}; + +class NativeAudioSink : public webrtc::AudioTrackSinkInterface { + public: + explicit NativeAudioSink(rust::Box observer, + int sample_rate, + int num_channels); + void OnData(const void* audio_data, + int bits_per_sample, + int sample_rate, + size_t number_of_channels, + size_t number_of_frames) override; + + private: + rust::Box observer_; + + int sample_rate_; + int num_channels_; + + webrtc::AudioFrame frame_; + webrtc::PushResampler resampler_; +}; + +std::shared_ptr new_native_audio_sink( + rust::Box observer, + int sample_rate, + int num_channels); + +class AudioTrackSource { + class InternalSource : public webrtc::LocalAudioSource { + public: + InternalSource(const webrtc::AudioOptions& options, + int sample_rate, + int num_channels, + int buffer_size_ms, + webrtc::TaskQueueFactory* task_queue_factory); + + ~InternalSource() override; + + SourceState state() const override; + bool remote() const override; + + const webrtc::AudioOptions options() const override; + + void AddSink(webrtc::AudioTrackSinkInterface* sink) override; + void RemoveSink(webrtc::AudioTrackSinkInterface* sink) override; + + void set_options(const webrtc::AudioOptions& options); + + bool capture_frame(rust::Slice audio_data, + uint32_t sample_rate, + uint32_t number_of_channels, + size_t number_of_frames, + const SourceContext* ctx, + void (*on_complete)(const SourceContext*)); + + void clear_buffer(); + + // Indicate this is an external audio source (when external_audio_source.patch is applied). + // This prevents AudioState from sending device audio to streams using this source. + // Note: Omit 'override' to allow builds without the patch applied. + // When the patch is applied, this will correctly override the base class virtual method. + bool is_external_source() const { return true; } + + private: + mutable webrtc::Mutex mutex_; + std::unique_ptr audio_queue_; + webrtc::RepeatingTaskHandle audio_task_; + + std::vector sinks_ RTC_GUARDED_BY(mutex_); + std::vector buffer_ RTC_GUARDED_BY(mutex_); + + const SourceContext* capture_userdata_ RTC_GUARDED_BY(mutex_); + void (*on_complete_)(const SourceContext*) RTC_GUARDED_BY(mutex_); + + std::vector silence_buffer_; + + int sample_rate_ = 0; + int num_channels_ = 0; + int queue_size_samples_ = 0; + int notify_threshold_samples_ = 0; + + webrtc::AudioOptions options_{}; + }; + + public: + AudioTrackSource(AudioSourceOptions options, + int sample_rate, + int num_channels, + int queue_size_ms, + webrtc::TaskQueueFactory* task_queue_factory); + + AudioSourceOptions audio_options() const; + + void set_audio_options(const AudioSourceOptions& options) const; + + bool capture_frame(rust::Slice audio_data, + uint32_t sample_rate, + uint32_t number_of_channels, + size_t number_of_frames, + const SourceContext* ctx, + CompleteCallback on_complete) const; + + void clear_buffer() const; + + webrtc::scoped_refptr get() const; + + private: + webrtc::scoped_refptr source_; +}; + +std::shared_ptr new_audio_track_source( + AudioSourceOptions options, + int sample_rate, + int num_channels, + int queue_size_ms); + +static std::shared_ptr audio_to_media( + std::shared_ptr track) { + return track; +} + +static std::shared_ptr media_to_audio( + std::shared_ptr track) { + return std::static_pointer_cast(track); +} + +static std::shared_ptr _shared_audio_track() { + return nullptr; // Ignore +} + +static std::shared_ptr _shared_audio_track_source() { + return nullptr; // Ignore +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/candidate.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/candidate.h new file mode 100644 index 0000000..d3e9830 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/candidate.h @@ -0,0 +1,43 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "api/candidate.h" + +namespace livekit_ffi { +class Candidate; +} +#include "webrtc-sys/src/candidate.rs.h" + +// webrtc::Candidate +namespace livekit_ffi { + +class Candidate { + public: + explicit Candidate(const webrtc::Candidate& candidate); + + private: + webrtc::Candidate candidate_; +}; + +static std::shared_ptr _shared_candidate() { + return nullptr; +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/data_channel.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/data_channel.h new file mode 100644 index 0000000..7643c11 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/data_channel.h @@ -0,0 +1,79 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include + +#include "api/data_channel_interface.h" +#include "livekit/webrtc.h" +#include "rtc_base/synchronization/mutex.h" +#include "rust/cxx.h" + +namespace livekit_ffi { +class DataChannel; +} // namespace livekit_ffi +#include "webrtc-sys/src/data_channel.rs.h" + +namespace livekit_ffi { + +class NativeDataChannelObserver; + +webrtc::DataChannelInit to_native_data_channel_init(DataChannelInit init); + +class DataChannel { + public: + explicit DataChannel( + std::shared_ptr rtc_runtime, + webrtc::scoped_refptr data_channel); + ~DataChannel(); + + void register_observer(rust::Box observer) const; + void unregister_observer() const; + bool send(const DataBuffer& buffer) const; + int id() const; + rust::String label() const; + DataState state() const; + void close() const; + uint64_t buffered_amount() const; + + private: + mutable webrtc::Mutex mutex_; + std::shared_ptr rtc_runtime_; + webrtc::scoped_refptr data_channel_; + mutable std::unique_ptr observer_; +}; + +static std::shared_ptr _shared_data_channel() { + return nullptr; // Ignore +} + +class NativeDataChannelObserver : public webrtc::DataChannelObserver { + public: + NativeDataChannelObserver(rust::Box observer, + const DataChannel* dc); + + void OnStateChange() override; + void OnMessage(const webrtc::DataBuffer& buffer) override; + void OnBufferedAmountChange(uint64_t sent_data_size) override; + + private: + rust::Box observer_; + const DataChannel* dc_; +}; + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/desktop_capturer.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/desktop_capturer.h new file mode 100644 index 0000000..aac111c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/desktop_capturer.h @@ -0,0 +1,72 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once +#include + +#include "modules/desktop_capture/desktop_capturer.h" +#include "rust/cxx.h" + +namespace livekit_ffi { +class DesktopFrame; +class DesktopCapturer; +class DesktopCapturerOptions; +class Source; +} // namespace livekit_ffi + +#include "webrtc-sys/src/desktop_capturer.rs.h" + +namespace livekit_ffi { + +class DesktopCapturer : public webrtc::DesktopCapturer::Callback { + public: + explicit DesktopCapturer(std::unique_ptr capturer) + : capturer(std::move(capturer)), callback(std::nullopt) {} + + void OnCaptureResult(webrtc::DesktopCapturer::Result result, + std::unique_ptr frame) final; + + rust::Vec get_source_list() const; + bool select_source(uint64_t id) const { return capturer->SelectSource(id); } + void start(rust::Box callback); + void capture_frame() const { capturer->CaptureFrame(); } + + private: + std::unique_ptr capturer; + std::optional> callback; +}; + +class DesktopFrame { + public: + DesktopFrame(std::unique_ptr frame) : frame(std::move(frame)) {} + int32_t width() const { return frame->size().width(); } + + int32_t height() const { return frame->size().height(); } + + int32_t left() const { return frame->rect().left(); } + + int32_t top() const { return frame->rect().top(); } + + int32_t stride() const { return frame->stride(); } + + const uint8_t* data() const { return frame->data(); } + + private: + std::unique_ptr frame; +}; + +std::unique_ptr new_desktop_capturer(DesktopCapturerOptions options); +} // namespace livekit_ffi \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/frame_cryptor.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/frame_cryptor.h new file mode 100644 index 0000000..52b46ca --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/frame_cryptor.h @@ -0,0 +1,232 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include +#include + +#include "api/crypto/frame_crypto_transformer.h" +#include "api/scoped_refptr.h" +#include "livekit/peer_connection.h" +#include "livekit/peer_connection_factory.h" +#include "livekit/rtp_receiver.h" +#include "livekit/rtp_sender.h" +#include "livekit/webrtc.h" +#include "rtc_base/synchronization/mutex.h" +#include "rust/cxx.h" + +namespace livekit_ffi { + +struct KeyProviderOptions; +struct EncryptedPacket; +enum class Algorithm : ::std::int32_t; +class RtcFrameCryptorObserverWrapper; +class NativeFrameCryptorObserver; +class PacketTrailerHandler; + +/// Shared secret key for frame encryption. +class KeyProvider { + public: + KeyProvider(KeyProviderOptions options); + ~KeyProvider() {} + + bool set_shared_key(int32_t index, rust::Vec<::std::uint8_t> key) const { + std::vector key_vec; + std::copy(key.begin(), key.end(), std::back_inserter(key_vec)); + return impl_->SetSharedKey(index, key_vec); + } + + rust::Vec<::std::uint8_t> ratchet_shared_key(int32_t key_index) const { + rust::Vec vec; + auto data = impl_->RatchetSharedKey(key_index); + if (data.empty()) { + throw std::runtime_error("ratchet_shared_key failed"); + } + + std::move(data.begin(), data.end(), std::back_inserter(vec)); + return vec; + } + + rust::Vec<::std::uint8_t> get_shared_key(int32_t key_index) const { + rust::Vec vec; + auto data = impl_->ExportSharedKey(key_index); + if (data.empty()) { + throw std::runtime_error("get_shared_key failed"); + } + + std::move(data.begin(), data.end(), std::back_inserter(vec)); + return vec; + } + + /// Set the key at the given index. + bool set_key(const ::rust::String participant_id, + int32_t index, + rust::Vec<::std::uint8_t> key) const { + std::vector key_vec; + std::copy(key.begin(), key.end(), std::back_inserter(key_vec)); + return impl_->SetKey( + std::string(participant_id.data(), participant_id.size()), index, + key_vec); + } + + rust::Vec<::std::uint8_t> ratchet_key(const ::rust::String participant_id, + int32_t key_index) const { + rust::Vec vec; + auto data = impl_->RatchetKey( + std::string(participant_id.data(), participant_id.size()), key_index); + if (data.empty()) { + throw std::runtime_error("ratchet_key failed"); + } + + std::move(data.begin(), data.end(), std::back_inserter(vec)); + return vec; + } + + rust::Vec<::std::uint8_t> get_key(const ::rust::String participant_id, + int32_t key_index) const { + rust::Vec vec; + auto data = impl_->ExportKey( + std::string(participant_id.data(), participant_id.size()), key_index); + if (data.empty()) { + throw std::runtime_error("get_key failed"); + } + + std::move(data.begin(), data.end(), std::back_inserter(vec)); + return vec; + } + + void set_sif_trailer(rust::Vec<::std::uint8_t> trailer) const { + std::vector trailer_vec; + std::copy(trailer.begin(), trailer.end(), std::back_inserter(trailer_vec)); + impl_->SetSifTrailer(trailer_vec); + } + + webrtc::scoped_refptr rtc_key_provider() { return impl_; } + + private: + webrtc::scoped_refptr impl_; +}; + +class FrameCryptor { + public: + FrameCryptor(std::shared_ptr rtc_runtime, + const std::string participant_id, + webrtc::FrameCryptorTransformer::Algorithm algorithm, + webrtc::scoped_refptr key_provider, + webrtc::scoped_refptr sender); + + FrameCryptor(std::shared_ptr rtc_runtime, + const std::string participant_id, + webrtc::FrameCryptorTransformer::Algorithm algorithm, + webrtc::scoped_refptr key_provider, + webrtc::scoped_refptr receiver); + ~FrameCryptor(); + + /// Enable/Disable frame crypto for the sender or receiver. + void set_enabled(bool enabled) const; + + /// Get the enabled state for the sender or receiver. + bool enabled() const; + + /// Set the key index for the sender or receiver. + /// If the key index is not set, the key index will be set to 0. + void set_key_index(int32_t index) const; + + /// Get the key index for the sender or receiver. + int32_t key_index() const; + + rust::String participant_id() const { return participant_id_; } + + void register_observer( + rust::Box observer) const; + + void unregister_observer() const; + + /// Attach a packet trailer transformer for chained processing. + void set_packet_trailer_handler( + std::shared_ptr handler) const; + + private: + std::shared_ptr rtc_runtime_; + const rust::String participant_id_; + mutable webrtc::Mutex mutex_; + webrtc::scoped_refptr e2ee_transformer_; + webrtc::scoped_refptr key_provider_; + webrtc::scoped_refptr sender_; + webrtc::scoped_refptr receiver_; + mutable webrtc::scoped_refptr observer_; + mutable webrtc::scoped_refptr + chained_transformer_; +}; + +class NativeFrameCryptorObserver + : public webrtc::FrameCryptorTransformerObserver { + public: + NativeFrameCryptorObserver(rust::Box observer, + const FrameCryptor* fc); + ~NativeFrameCryptorObserver(); + + void OnFrameCryptionStateChanged(const std::string participant_id, + webrtc::FrameCryptionState error) override; + + private: + rust::Box observer_; + const FrameCryptor* fc_; +}; + +class DataPacketCryptor { + public: + DataPacketCryptor(webrtc::FrameCryptorTransformer::Algorithm algorithm, + webrtc::scoped_refptr key_provider); + + EncryptedPacket encrypt_data_packet( + const ::rust::String participant_id, + uint32_t key_index, + rust::Vec<::std::uint8_t> data) const; + + rust::Vec<::std::uint8_t> decrypt_data_packet( + const ::rust::String participant_id, + const EncryptedPacket& encrypted_packet) const; + + private: + webrtc::scoped_refptr data_packet_cryptor_; +}; + +std::shared_ptr new_frame_cryptor_for_rtp_sender( + std::shared_ptr peer_factory, + const ::rust::String participant_id, + Algorithm algorithm, + std::shared_ptr key_provider, + std::shared_ptr sender); + +std::shared_ptr new_frame_cryptor_for_rtp_receiver( + std::shared_ptr peer_factory, + const ::rust::String participant_id, + Algorithm algorithm, + std::shared_ptr key_provider, + std::shared_ptr receiver); + +std::shared_ptr new_key_provider(KeyProviderOptions options); + +std::shared_ptr new_data_packet_cryptor( + Algorithm algorithm, + std::shared_ptr key_provider); + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/global_task_queue.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/global_task_queue.h new file mode 100644 index 0000000..59c1006 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/global_task_queue.h @@ -0,0 +1,25 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "api/task_queue/task_queue_factory.h" + +namespace livekit_ffi { + +webrtc::TaskQueueFactory* GetGlobalTaskQueueFactory(); + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/helper.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/helper.h new file mode 100644 index 0000000..a50c3d4 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/helper.h @@ -0,0 +1,57 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "rust/cxx.h" + +namespace livekit_ffi { +class MediaStream; +class AudioTrack; +class VideoTrack; +class Candidate; +class RtpSender; +class RtpReceiver; +class RtpTransceiver; +} // namespace livekit_ffi +#include "webrtc-sys/src/helper.rs.h" + +namespace livekit_ffi { + +// Impl not needed +static rust::Vec _vec_media_stream_ptr() { + throw; +} +static rust::Vec _vec_candidate_ptr() { + throw; +} +static rust::Vec _vec_audio_track_ptr() { + throw; +} +static rust::Vec _vec_video_track_ptr() { + throw; +} +static rust::Vec _vec_rtp_sender_ptr() { + throw; +} +static rust::Vec _vec_rtp_receiver_ptr() { + throw; +} +static rust::Vec _vec_rtp_transceiver_ptr() { + throw; +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/jsep.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/jsep.h new file mode 100644 index 0000000..536c2e6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/jsep.h @@ -0,0 +1,155 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "api/jsep.h" +#include "api/ref_counted_base.h" +#include "api/set_local_description_observer_interface.h" +#include "api/set_remote_description_observer_interface.h" +#include "api/stats/rtc_stats_collector_callback.h" +#include "livekit/rtc_error.h" +#include "rtc_base/ref_count.h" +#include "rust/cxx.h" + +namespace livekit_ffi { +class IceCandidate; +class SessionDescription; +}; // namespace livekit_ffi +#include "webrtc-sys/src/jsep.rs.h" + +namespace livekit_ffi { + +class PeerContext; + +class IceCandidate { + public: + explicit IceCandidate( + std::unique_ptr ice_candidate); + + rust::String sdp_mid() const; + int sdp_mline_index() const; + rust::String candidate() const; // TODO(theomonnom) Return livekit_ffi::Candidate + // instead of rust::String + + rust::String stringify() const; + std::unique_ptr release(); + + private: + std::unique_ptr ice_candidate_; +}; + +std::shared_ptr create_ice_candidate(rust::String sdp_mid, + int sdp_mline_index, + rust::String sdp); + +static std::shared_ptr _shared_ice_candidate() { + return nullptr; // Ignore +} + +class SessionDescription { + public: + explicit SessionDescription( + std::unique_ptr session_description); + + SdpType sdp_type() const; + rust::String stringify() const; + std::unique_ptr clone() const; + std::unique_ptr release(); + + private: + std::unique_ptr session_description_; +}; + +std::unique_ptr create_session_description( + SdpType type, + rust::String sdp); + +static std::unique_ptr _unique_session_description() { + return nullptr; // Ignore +} + +#ifdef LIVEKIT_TEST +rust::String serialize_sdp_parse_error_for_test(); +#endif + +class NativeCreateSdpObserver + : public webrtc::CreateSessionDescriptionObserver { + public: + NativeCreateSdpObserver( + rust::Box ctx, + rust::Fn ctx, + std::unique_ptr)> on_success, + rust::Fn ctx, RtcError)> on_error); + + void OnSuccess(webrtc::SessionDescriptionInterface* desc) override; + void OnFailure(webrtc::RTCError error) override; + + private: + rust::Box ctx_; + rust::Fn, std::unique_ptr)> + on_success_; + rust::Fn, RtcError)> on_error_; +}; + +class NativeSetLocalSdpObserver + : public webrtc::SetLocalDescriptionObserverInterface { + public: + NativeSetLocalSdpObserver( + rust::Box ctx, + rust::Fn, RtcError)> on_complete); + + void OnSetLocalDescriptionComplete(webrtc::RTCError error) override; + + private: + rust::Box ctx_; + rust::Fn, RtcError)> on_complete_; +}; + +class NativeSetRemoteSdpObserver + : public webrtc::SetRemoteDescriptionObserverInterface { + public: + NativeSetRemoteSdpObserver( + rust::Box ctx, + rust::Fn, RtcError)> on_complete); + + void OnSetRemoteDescriptionComplete(webrtc::RTCError error) override; + + private: + rust::Box ctx_; + rust::Fn, RtcError)> on_complete_; +}; + +template // Context type +class NativeRtcStatsCollector : public webrtc::RTCStatsCollectorCallback { + public: + NativeRtcStatsCollector(rust::Box ctx, + rust::Fn, rust::String)> on_stats) + : ctx_(std::move(ctx)), on_stats_(on_stats) {} + + void OnStatsDelivered( + const webrtc::scoped_refptr& report) override { + on_stats_(std::move(ctx_), report->ToJson()); + } + + private: + rust::Box ctx_; + rust::Fn, rust::String)> on_stats_; +}; + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/media_stream.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/media_stream.h new file mode 100644 index 0000000..e3acea7 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/media_stream.h @@ -0,0 +1,57 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "api/media_stream_interface.h" +#include "livekit/helper.h" +#include "livekit/webrtc.h" +#include "rust/cxx.h" + +namespace livekit_ffi { +class MediaStream; +} // namespace livekit_ffi +#include "webrtc-sys/src/media_stream.rs.h" + +namespace livekit_ffi { + +class MediaStream { + public: + MediaStream(std::shared_ptr rtc_runtime, + webrtc::scoped_refptr stream); + + rust::String id() const; + rust::Vec get_video_tracks() const; + rust::Vec get_audio_tracks() const; + + std::shared_ptr find_audio_track(rust::String track_id) const; + std::shared_ptr find_video_track(rust::String track_id) const; + + bool add_track(std::shared_ptr track) const; + bool remove_track(std::shared_ptr track) const; + + private: + std::shared_ptr rtc_runtime_; + webrtc::scoped_refptr media_stream_; +}; + +static std::shared_ptr _shared_media_stream() { + return nullptr; // Ignore +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/media_stream_track.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/media_stream_track.h new file mode 100644 index 0000000..59f2606 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/media_stream_track.h @@ -0,0 +1,60 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "api/media_stream_interface.h" +#include "livekit/helper.h" +#include "livekit/webrtc.h" +#include "rust/cxx.h" + +namespace livekit_ffi { +class MediaStreamTrack; +} // namespace livekit_ffi +#include "webrtc-sys/src/media_stream_track.rs.h" + +namespace livekit_ffi { + +class MediaStreamTrack { + protected: + MediaStreamTrack(std::shared_ptr, + webrtc::scoped_refptr track); + + public: + rust::String kind() const; + rust::String id() const; + + bool enabled() const; + bool set_enabled(bool enable) const; + + TrackState state() const; + + webrtc::scoped_refptr rtc_track() const { + return track_; + } + + protected: + std::shared_ptr rtc_runtime_; + webrtc::scoped_refptr track_; +}; + +static std::shared_ptr _shared_media_stream_track() { + return nullptr; // Ignore +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/objc_video_factory.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/objc_video_factory.h new file mode 100644 index 0000000..dd6e959 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/objc_video_factory.h @@ -0,0 +1,29 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "api/video_codecs/video_decoder_factory.h" +#include "api/video_codecs/video_encoder_factory.h" + +namespace livekit_ffi { + +std::unique_ptr CreateObjCVideoEncoderFactory(); +std::unique_ptr CreateObjCVideoDecoderFactory(); + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/packet_trailer.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/packet_trailer.h new file mode 100644 index 0000000..5b30701 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/packet_trailer.h @@ -0,0 +1,290 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "absl/types/optional.h" +#include "api/frame_transformer_interface.h" +#include "api/rtp_sender_interface.h" +#include "api/rtp_receiver_interface.h" +#include "api/scoped_refptr.h" +#include "livekit/webrtc.h" +#include "rtc_base/synchronization/mutex.h" +#include "rust/cxx.h" + +// Forward declarations to avoid circular includes +// (video_track.h -> packet_trailer.h -> peer_connection.h -> media_stream.h -> video_track.h) +namespace livekit_ffi { +class PeerConnectionFactory; +class RtpSender; +class RtpReceiver; +enum class VideoPublishTimingStage : int32_t; +enum class VideoSubscribeTimingStage : int32_t; +struct VideoPublishTimingObserverWrapper; +struct VideoSubscribeTimingObserverWrapper; +} // namespace livekit_ffi + +namespace livekit_ffi { + +// Magic bytes to identify packet trailers: "LKTS" (LiveKit TimeStamp) +constexpr uint8_t kPacketTrailerMagic[4] = {'L', 'K', 'T', 'S'}; + +// Trailer envelope: [trailer_len: 1B] [magic: 4B] = 5 bytes. +// Always present at the end of every trailer. +constexpr size_t kTrailerEnvelopeSize = 5; + +// TLV element overhead: [tag: 1B] [len: 1B] = 2 bytes before value. +// All TLV bytes (tag, len, value) are XORed with 0xFF. + +// TLV tag IDs +constexpr uint8_t kTagTimestampUs = 0x01; // value: 8 bytes big-endian uint64 +constexpr uint8_t kTagFrameId = 0x02; // value: 4 bytes big-endian uint32 + +constexpr size_t kTimestampTlvSize = 10; // tag + len + 8-byte value +constexpr size_t kFrameIdTlvSize = 6; // tag + len + 4-byte value + +// Trailer size varies because frame_id is omitted when it is unset (0). +constexpr size_t kPacketTrailerMinSize = + kTimestampTlvSize + kTrailerEnvelopeSize; +constexpr size_t kPacketTrailerMaxSize = + kTimestampTlvSize + kFrameIdTlvSize + kTrailerEnvelopeSize; + +struct PacketTrailerMetadata { + uint64_t user_timestamp; + uint32_t frame_id; + uint32_t ssrc; // SSRC that produced this entry (for simulcast tracking) +}; + +/// Frame transformer that appends/extracts packet trailers. +/// This transformer can be used standalone or in conjunction with e2ee. +/// +/// On the send side, user timestamps are stored in an internal map keyed +/// by capture timestamp (microseconds). When TransformSend fires it +/// looks up the user timestamp via the frame's CaptureTime(). +/// +/// On the receive side, extracted frame metadata is stored in an +/// internal map keyed by RTP timestamp (uint32_t). Decoded frames can +/// look up their metadata via lookup_frame_metadata(rtp_ts). +class PacketTrailerTransformer : public webrtc::FrameTransformerInterface { + public: + enum class Direction { kSend, kReceive }; + + explicit PacketTrailerTransformer(Direction direction); + ~PacketTrailerTransformer() override = default; + + // FrameTransformerInterface implementation + void Transform( + std::unique_ptr frame) override; + void RegisterTransformedFrameCallback( + webrtc::scoped_refptr callback) override; + void RegisterTransformedFrameSinkCallback( + webrtc::scoped_refptr callback, + uint32_t ssrc) override; + void UnregisterTransformedFrameCallback() override; + void UnregisterTransformedFrameSinkCallback(uint32_t ssrc) override; + + /// Enable/disable timestamp embedding + void set_enabled(bool enabled); + bool enabled() const; + + /// Lookup the frame metadata associated with a given RTP timestamp. + /// Returns the metadata if found, nullopt otherwise. + /// The entry is removed from the map after lookup. + std::optional lookup_frame_metadata(uint32_t rtp_timestamp); + + /// Store frame metadata for a given capture timestamp (sender side). + /// Called from VideoTrackSource::on_captured_frame with the + /// TimestampAligner-adjusted timestamp, which matches CaptureTime() + /// in the encoder pipeline. + void store_frame_metadata(int64_t capture_timestamp_us, + uint64_t user_timestamp, + uint32_t frame_id); + + /// Set the observer receiving sender-side publish timing events. + void set_publish_timing_observer( + rust::Box observer); + + /// Clear the observer receiving sender-side publish timing events. + void clear_publish_timing_observer(); + + /// Emit a sender-side publish timing event. + void emit_publish_timing(VideoPublishTimingStage stage, + uint64_t user_timestamp, + uint32_t frame_id) const; + + /// Set the observer receiving receiver-side subscribe timing events. + void set_subscribe_timing_observer( + rust::Box observer); + + /// Clear the observer receiving receiver-side subscribe timing events. + void clear_subscribe_timing_observer(); + + /// Emit a receiver-side subscribe timing event. + void emit_subscribe_timing(VideoSubscribeTimingStage stage, + uint64_t user_timestamp, + uint32_t frame_id) const; + + private: + void TransformSend( + std::unique_ptr frame); + void TransformReceive( + std::unique_ptr frame); + void emit_subscribe_timing(VideoSubscribeTimingStage stage, + uint64_t user_timestamp, + uint32_t frame_id, + uint64_t timestamp_us) const; + bool publish_timing_enabled() const; + bool subscribe_timing_enabled() const; + + PacketTrailerMetadata LookupSendMetadata( + const webrtc::TransformableFrameInterface& frame, + uint32_t ssrc, + uint32_t rtp_timestamp) const; + + /// Append frame metadata trailer to frame data + std::vector AppendTrailer( + webrtc::ArrayView data, + uint64_t user_timestamp, + uint32_t frame_id); + + /// Extract and remove frame metadata trailer from frame data + std::optional ExtractTrailer( + webrtc::ArrayView data, + std::vector& out_data); + + const Direction direction_; + std::atomic enabled_{true}; + mutable webrtc::Mutex mutex_; + webrtc::scoped_refptr callback_; + std::unordered_map> + sink_callbacks_; + // Send-side map: capture timestamp (us) -> frame metadata. + // Populated by store_frame_metadata(), consumed by TransformSend() + // via CaptureTime() lookup. + mutable webrtc::Mutex send_map_mutex_; + mutable std::unordered_map send_map_; + mutable std::deque send_map_order_; + static constexpr size_t kMaxSendMapEntries = 300; + + // Receive-side map: RTP timestamp -> frame metadata. + // Keyed by RTP timestamp so decoded frames can look up their + // metadata regardless of frame drops or reordering. + mutable webrtc::Mutex recv_map_mutex_; + mutable std::unordered_map recv_map_; + mutable std::deque recv_map_order_; + static constexpr size_t kMaxRecvMapEntries = 300; + + // Simulcast tracking: detect layer switches and flush stale entries. + mutable uint32_t recv_active_ssrc_{0}; + + mutable webrtc::Mutex publish_timing_observer_mutex_; + std::atomic publish_timing_enabled_{false}; + mutable std::shared_ptr> + publish_timing_observer_; + mutable webrtc::Mutex subscribe_timing_observer_mutex_; + std::atomic subscribe_timing_enabled_{false}; + mutable std::shared_ptr> + subscribe_timing_observer_; +}; + +/// Wrapper class for Rust FFI that manages packet trailer transformers. +class PacketTrailerHandler { + public: + PacketTrailerHandler( + std::shared_ptr rtc_runtime, + webrtc::scoped_refptr sender); + + PacketTrailerHandler( + std::shared_ptr rtc_runtime, + webrtc::scoped_refptr receiver); + + ~PacketTrailerHandler() = default; + + /// Enable/disable timestamp embedding + void set_enabled(bool enabled) const; + bool enabled() const; + + /// Lookup the user timestamp for a given RTP timestamp (receiver side). + /// Returns UINT64_MAX if not found. The entry is removed after lookup. + /// Also caches the frame_id for retrieval via last_lookup_frame_id(). + uint64_t lookup_timestamp(uint32_t rtp_timestamp) const; + + /// Returns the frame_id from the most recent successful + /// lookup_timestamp() call. Returns 0 if no lookup succeeded. + uint32_t last_lookup_frame_id() const; + + /// Store frame metadata for a given capture timestamp (sender side). + void store_frame_metadata(int64_t capture_timestamp_us, + uint64_t user_timestamp, + uint32_t frame_id) const; + + /// Set the observer receiving sender-side publish timing events. + void set_publish_timing_observer( + rust::Box observer) const; + + /// Clear the observer receiving sender-side publish timing events. + void clear_publish_timing_observer() const; + + /// Emit a sender-side publish timing event. + void emit_publish_timing(VideoPublishTimingStage stage, + uint64_t user_timestamp, + uint32_t frame_id) const; + + /// Set the observer receiving receiver-side subscribe timing events. + void set_subscribe_timing_observer( + rust::Box observer) const; + + /// Clear the observer receiving receiver-side subscribe timing events. + void clear_subscribe_timing_observer() const; + + /// Emit a receiver-side subscribe timing event. + void emit_subscribe_timing(VideoSubscribeTimingStage stage, + uint64_t user_timestamp, + uint32_t frame_id) const; + + /// Access the underlying transformer for chaining. + webrtc::scoped_refptr transformer() const; + + private: + std::shared_ptr rtc_runtime_; + webrtc::scoped_refptr transformer_; + webrtc::scoped_refptr sender_; + webrtc::scoped_refptr receiver_; + mutable uint32_t last_frame_id_{0}; +}; + +// Factory functions for Rust FFI + +std::shared_ptr new_packet_trailer_sender( + std::shared_ptr peer_factory, + std::shared_ptr sender); + +std::shared_ptr new_packet_trailer_receiver( + std::shared_ptr peer_factory, + std::shared_ptr receiver); + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/peer_connection.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/peer_connection.h new file mode 100644 index 0000000..50c545c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/peer_connection.h @@ -0,0 +1,207 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "api/peer_connection_interface.h" +#include "api/scoped_refptr.h" +#include "livekit/data_channel.h" +#include "livekit/helper.h" +#include "livekit/jsep.h" +#include "livekit/media_stream.h" +#include "livekit/rtc_error.h" +#include "livekit/rtp_receiver.h" +#include "livekit/rtp_sender.h" +#include "livekit/rtp_transceiver.h" +#include "livekit/webrtc.h" +#include "rust/cxx.h" +#include "webrtc-sys/src/data_channel.rs.h" + +namespace livekit_ffi { +class PeerConnection; +} // namespace livekit_ffi +#include "webrtc-sys/src/peer_connection.rs.h" + +namespace livekit_ffi { + +webrtc::PeerConnectionInterface::RTCConfiguration to_native_rtc_configuration( + RtcConfiguration config); + +class PeerConnectionObserverWrapper; + +class PeerConnection : webrtc::PeerConnectionObserver { + public: + PeerConnection( + std::shared_ptr rtc_runtime, + webrtc::scoped_refptr pc_factory, + rust::Box observer); + + ~PeerConnection(); + + bool Initialize(webrtc::PeerConnectionInterface::RTCConfiguration config); + + void set_configuration(RtcConfiguration config) const; + + void create_offer( + RtcOfferAnswerOptions options, + rust::Box ctx, + rust::Fn, + std::unique_ptr)> on_success, + rust::Fn, RtcError)> on_error) const; + + void create_answer( + RtcOfferAnswerOptions options, + rust::Box ctx, + rust::Fn, + std::unique_ptr)> on_success, + rust::Fn, RtcError)> on_error) const; + + void set_local_description( + std::unique_ptr desc, + rust::Box ctx, + rust::Fn, RtcError)> on_complete) const; + + void set_remote_description( + std::unique_ptr desc, + rust::Box ctx, + rust::Fn, RtcError)> on_complete) const; + + std::shared_ptr create_data_channel(rust::String label, + DataChannelInit init) const; + + void add_ice_candidate( + std::shared_ptr candidate, + rust::Box ctx, + rust::Fn, RtcError)> on_complete) const; + + std::shared_ptr add_track( + std::shared_ptr track, + const rust::Vec& stream_ids) const; + + void remove_track(std::shared_ptr sender) const; + + void get_stats( + rust::Box ctx, + rust::Fn, rust::String)> on_stats) const; + + void restart_ice() const; + + std::shared_ptr add_transceiver( + std::shared_ptr track, + RtpTransceiverInit init) const; + + std::shared_ptr add_transceiver_for_media( + MediaType media_type, + RtpTransceiverInit init) const; + + rust::Vec get_senders() const; + + rust::Vec get_receivers() const; + + rust::Vec get_transceivers() const; + + std::unique_ptr current_local_description() const; + + std::unique_ptr current_remote_description() const; + + std::unique_ptr pending_local_description() const; + + std::unique_ptr pending_remote_description() const; + + std::unique_ptr local_description() const; + + std::unique_ptr remote_description() const; + + PeerConnectionState connection_state() const; + + SignalingState signaling_state() const; + + IceGatheringState ice_gathering_state() const; + + IceConnectionState ice_connection_state() const; + + void close() const; + + void OnSignalingChange( + webrtc::PeerConnectionInterface::SignalingState new_state) override; + + void OnAddStream( + webrtc::scoped_refptr stream) override; + + void OnRemoveStream( + webrtc::scoped_refptr stream) override; + + void OnDataChannel( + webrtc::scoped_refptr data_channel) override; + + void OnRenegotiationNeeded() override; + + void OnNegotiationNeededEvent(uint32_t event_id) override; + + void OnIceConnectionChange( + webrtc::PeerConnectionInterface::IceConnectionState new_state) override; + + void OnStandardizedIceConnectionChange( + webrtc::PeerConnectionInterface::IceConnectionState new_state) override; + + void OnConnectionChange( + webrtc::PeerConnectionInterface::PeerConnectionState new_state) override; + + void OnIceGatheringChange( + webrtc::PeerConnectionInterface::IceGatheringState new_state) override; + + void OnIceCandidate(const webrtc::IceCandidate* candidate) override; + + void OnIceCandidateError(const std::string& address, + int port, + const std::string& url, + int error_code, + const std::string& error_text) override; + + void OnIceCandidateRemoved(const webrtc::IceCandidate* candidate) override; + + void OnIceConnectionReceivingChange(bool receiving) override; + + void OnIceSelectedCandidatePairChanged( + const webrtc::CandidatePairChangeEvent& event) override; + + void OnAddTrack( + webrtc::scoped_refptr receiver, + const std::vector>& + streams) override; + + void OnTrack( + webrtc::scoped_refptr transceiver) override; + + void OnRemoveTrack( + webrtc::scoped_refptr receiver) override; + + void OnInterestingUsage(int usage_pattern) override; + + private: + std::shared_ptr rtc_runtime_; + webrtc::scoped_refptr pc_factory_; + rust::Box observer_; + webrtc::scoped_refptr peer_connection_; +}; + +static std::shared_ptr _shared_peer_connection() { + return nullptr; // Ignore +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/peer_connection_factory.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/peer_connection_factory.h new file mode 100644 index 0000000..371d531 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/peer_connection_factory.h @@ -0,0 +1,83 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "api/environment/environment_factory.h" +#include "api/peer_connection_interface.h" +#include "api/scoped_refptr.h" +#include "api/task_queue/task_queue_factory.h" +#include "livekit/adm_proxy.h" +#include "livekit/audio_device_controller.h" +#include "media_stream.h" +#include "rtp_parameters.h" +#include "rust/cxx.h" +#include "webrtc.h" + +namespace livekit_ffi { +class PeerConnectionFactory; +class AudioDeviceController; +class PeerConnectionObserverWrapper; +} // namespace livekit_ffi +#include "webrtc-sys/src/peer_connection_factory.rs.h" + +namespace livekit_ffi { + +class PeerConnection; +struct RtcConfiguration; + +webrtc::PeerConnectionInterface::RTCConfiguration to_native_rtc_configuration( + RtcConfiguration config); + +class PeerConnectionFactory { + public: + explicit PeerConnectionFactory(std::shared_ptr rtc_runtime); + ~PeerConnectionFactory(); + + std::shared_ptr create_peer_connection( + RtcConfiguration config, + rust::Box observer) const; + + std::shared_ptr create_video_track( + rust::String label, + std::shared_ptr source) const; + + std::shared_ptr create_audio_track( + rust::String label, + std::shared_ptr source) const; + + // Create an audio track that uses the ADM for capture (microphone) + // This creates a track that captures from the selected recording device + std::shared_ptr create_device_audio_track( + rust::String label) const; + + RtpCapabilities rtp_sender_capabilities(MediaType type) const; + + RtpCapabilities rtp_receiver_capabilities(MediaType type) const; + + std::shared_ptr rtc_runtime() const { return rtc_runtime_; } + std::shared_ptr audio_device() const; + + private: + std::shared_ptr rtc_runtime_; + webrtc::scoped_refptr adm_proxy_; + std::shared_ptr audio_device_; + webrtc::scoped_refptr peer_factory_; + webrtc::Environment env_; +}; + +std::shared_ptr create_peer_connection_factory(); +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/prohibit_libsrtp_initialization.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/prohibit_libsrtp_initialization.h new file mode 100644 index 0000000..510255f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/prohibit_libsrtp_initialization.h @@ -0,0 +1,21 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +namespace livekit_ffi { +void ProhibitLibsrtpInitialization(); +} \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/recorded_audio_tap.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/recorded_audio_tap.h new file mode 100644 index 0000000..59d42b9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/recorded_audio_tap.h @@ -0,0 +1,110 @@ +/* + * Copyright 2026 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include "api/audio/audio_device_defines.h" +#include "api/audio/audio_frame.h" +#include "common_audio/resampler/include/push_resampler.h" +#include "rust/cxx.h" + +namespace livekit_ffi { + +struct RecordedAudioSinkWrapper; + +// Installs the process-global recorded-audio sink. The platform ADM delivers +// 10ms PCM frames on its capture thread; while a sink is installed each frame +// is resampled to 48kHz mono and handed to Rust. Returns a generation token so +// a later clear can target exactly this installation and never clobber a sink +// that a subsequent caller installed in the meantime. +uint64_t set_recorded_audio_sink(rust::Box sink); + +// Removes the global recorded-audio sink only if `generation` matches the +// currently-installed sink. A stale token is a no-op. +void clear_recorded_audio_sink(uint64_t generation); + +// AudioTransport interposer registered with the platform ADM in place of the +// real transport. It tees recorded microphone frames to the global sink and +// forwards every call unchanged to the real transport so the normal send and +// playout pipelines are unaffected. +class RecordingTransportProxy : public webrtc::AudioTransport { + public: + RecordingTransportProxy(); + ~RecordingTransportProxy(); + + void set_real_transport(webrtc::AudioTransport* transport); + + int32_t RecordedDataIsAvailable(const void* audioSamples, + size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, + uint32_t totalDelayMS, + int32_t clockDrift, + uint32_t currentMicLevel, + bool keyPressed, + uint32_t& newMicLevel) override; + + int32_t RecordedDataIsAvailable( + const void* audioSamples, + size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, + uint32_t totalDelayMS, + int32_t clockDrift, + uint32_t currentMicLevel, + bool keyPressed, + uint32_t& newMicLevel, + std::optional estimatedCaptureTimeNS) override; + + int32_t NeedMorePlayData(size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, + void* audioSamples, + size_t& nSamplesOut, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms) override; + + void PullRenderData(int bits_per_sample, + int sample_rate, + size_t number_of_channels, + size_t number_of_frames, + void* audio_data, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms) override; + + private: + void TeeRecordedData(const void* audioSamples, + size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec); + + std::atomic real_transport_{nullptr}; + + // Touched only on the ADM capture thread inside RecordedDataIsAvailable, + // which WebRTC serializes, so neither member needs a lock. + webrtc::AudioFrame capture_frame_; + webrtc::PushResampler capture_resampler_; +}; + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/rtc_error.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/rtc_error.h new file mode 100644 index 0000000..6d71c23 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/rtc_error.h @@ -0,0 +1,33 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "api/rtc_error.h" +#include "rust/cxx.h" +#include "webrtc-sys/src/rtc_error.rs.h" + +namespace livekit_ffi { + +RtcError to_error(const webrtc::RTCError& error); +std::string serialize_error( + const RtcError& error); // to be used inside cxx::Exception msg + +#ifdef LIVEKIT_TEST +rust::String serialize_deserialize(); +#endif + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/rtp_parameters.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/rtp_parameters.h new file mode 100644 index 0000000..9661112 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/rtp_parameters.h @@ -0,0 +1,61 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "api/media_types.h" +#include "api/priority.h" +#include "api/rtp_parameters.h" +#include "api/rtp_transceiver_direction.h" +#include "webrtc-sys/src/rtp_parameters.rs.h" + +namespace livekit_ffi { + +webrtc::RtcpFeedback to_native_rtcp_feedback(RtcpFeedback feedback); +webrtc::RtpCodecCapability to_native_rtp_codec_capability( + RtpCodecCapability capability); +webrtc::RtpHeaderExtensionCapability to_native_rtp_header_extension_capability( + RtpHeaderExtensionCapability header); +webrtc::RtpExtension to_native_rtp_extension(RtpExtension ext); +webrtc::RtpFecParameters to_rtp_fec_parameters(RtpFecParameters fec); +webrtc::RtpRtxParameters to_rtp_rtx_parameters(RtpRtxParameters rtx); +webrtc::RtpEncodingParameters to_native_rtp_encoding_paramters( + RtpEncodingParameters parameters); +webrtc::RtpCodecParameters to_native_rtp_codec_parameters( + RtpCodecParameters params); +webrtc::RtpCapabilities to_rtp_capabilities(RtpCapabilities capabilities); +webrtc::RtcpParameters to_native_rtcp_paramaters(RtcpParameters params); +webrtc::RtpParameters to_native_rtp_parameters(RtpParameters params); + +RtcpFeedback to_rust_rtcp_feedback(webrtc::RtcpFeedback feedback); +RtpCodecCapability to_rust_rtp_codec_capability( + webrtc::RtpCodecCapability capability); +RtpHeaderExtensionCapability to_rust_rtp_header_extension_capability( + webrtc::RtpHeaderExtensionCapability header); +RtpExtension to_rust_rtp_extension(webrtc::RtpExtension ext); +RtpFecParameters to_rust_rtp_fec_parameters(webrtc::RtpFecParameters fec); +RtpRtxParameters to_rust_rtp_rtx_parameters(webrtc::RtpRtxParameters param); +RtpEncodingParameters to_rust_rtp_encoding_parameters( + webrtc::RtpEncodingParameters params); +RtpCodecParameters to_rust_rtp_codec_parameters( + webrtc::RtpCodecParameters params); +RtpCapabilities to_rust_rtp_capabilities(webrtc::RtpCapabilities capabilities); +RtcpParameters to_rust_rtcp_parameters(webrtc::RtcpParameters params); +RtpParameters to_rust_rtp_parameters(webrtc::RtpParameters params); + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/rtp_receiver.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/rtp_receiver.h new file mode 100644 index 0000000..de43d13 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/rtp_receiver.h @@ -0,0 +1,79 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "api/peer_connection_interface.h" +#include "api/rtp_receiver_interface.h" +#include "api/scoped_refptr.h" +#include "livekit/helper.h" +#include "livekit/media_stream.h" +#include "livekit/rtp_parameters.h" +#include "livekit/webrtc.h" +#include "rust/cxx.h" + +namespace livekit_ffi { +class RtpReceiver; +} +#include "webrtc-sys/src/rtp_receiver.rs.h" +namespace livekit_ffi { + +// TODO(theomonnom): Implement RtpReceiverObserverInterface? +// TODO(theomonnom): RtpSource +// TODO(theomonnom): FrameTransformer & FrameDecryptor interface +class RtpReceiver { + public: + RtpReceiver( + std::shared_ptr rtc_runtime, + webrtc::scoped_refptr receiver, + webrtc::scoped_refptr peer_connection); + + std::shared_ptr track() const; + + void get_stats( + rust::Box ctx, + rust::Fn, rust::String)> on_stats) const; + + rust::Vec stream_ids() const; + rust::Vec streams() const; + + MediaType media_type() const; + rust::String id() const; + + RtpParameters get_parameters() const; + + // bool set_parameters(RtpParameters parameters) const; // Seems unsupported + + void set_jitter_buffer_minimum_delay(bool is_some, + double delay_seconds) const; + + webrtc::scoped_refptr rtc_receiver() const { + return receiver_; + } + + private: + std::shared_ptr rtc_runtime_; + webrtc::scoped_refptr receiver_; + webrtc::scoped_refptr peer_connection_; +}; + +static std::shared_ptr _shared_rtp_receiver() { + return nullptr; +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/rtp_sender.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/rtp_sender.h new file mode 100644 index 0000000..2711f55 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/rtp_sender.h @@ -0,0 +1,81 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "api/peer_connection_interface.h" +#include "api/rtp_sender_interface.h" +#include "api/scoped_refptr.h" +#include "livekit/media_stream.h" +#include "livekit/rtc_error.h" +#include "livekit/rtp_parameters.h" +#include "rust/cxx.h" + +namespace livekit_ffi { +class RtpSender; +} +#include "webrtc-sys/src/rtp_sender.rs.h" + +namespace livekit_ffi { + +// TODO(theomonnom): FrameTransformer & FrameEncryptor interface +class RtpSender { + public: + RtpSender( + std::shared_ptr rtc_runtime, + webrtc::scoped_refptr sender, + webrtc::scoped_refptr peer_connection); + + bool set_track(std::shared_ptr track) const; + + std::shared_ptr track() const; + + uint32_t ssrc() const; + + void get_stats( + rust::Box ctx, + rust::Fn, rust::String)> on_stats) const; + + MediaType media_type() const; + + rust::String id() const; + + rust::Vec stream_ids() const; + + void set_streams(const rust::Vec& stream_ids) const; + + rust::Vec init_send_encodings() const; + + RtpParameters get_parameters() const; + + void set_parameters(RtpParameters params) const; + + webrtc::scoped_refptr rtc_sender() const { + return sender_; + } + + private: + std::shared_ptr rtc_runtime_; + webrtc::scoped_refptr sender_; + webrtc::scoped_refptr peer_connection_; +}; + +static std::shared_ptr _shared_rtp_sender() { + return nullptr; // Ignore +} +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/rtp_transceiver.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/rtp_transceiver.h new file mode 100644 index 0000000..ed60d06 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/rtp_transceiver.h @@ -0,0 +1,93 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "api/peer_connection_interface.h" +#include "api/rtp_parameters.h" +#include "api/rtp_transceiver_direction.h" +#include "api/rtp_transceiver_interface.h" +#include "api/scoped_refptr.h" +#include "livekit/rtc_error.h" +#include "livekit/rtp_parameters.h" +#include "livekit/rtp_receiver.h" +#include "livekit/rtp_sender.h" +#include "rust/cxx.h" + +namespace livekit_ffi { +class RtpTransceiver; +} +#include "webrtc-sys/src/rtp_transceiver.rs.h" + +namespace livekit_ffi { + +webrtc::RtpTransceiverInit to_native_rtp_transceiver_init( + RtpTransceiverInit init); + +class RtpTransceiver { + public: + RtpTransceiver( + std::shared_ptr rtc_runtime, + webrtc::scoped_refptr transceiver, + webrtc::scoped_refptr peer_connection); + + MediaType media_type() const; + + rust::String mid() const; + + std::shared_ptr sender() const; + + std::shared_ptr receiver() const; + + bool stopped() const; + + bool stopping() const; + + RtpTransceiverDirection direction() const; + + void set_direction(RtpTransceiverDirection direction) const; + + RtpTransceiverDirection current_direction() const; + + RtpTransceiverDirection fired_direction() const; + + void stop_standard() const; + + void set_codec_preferences(rust::Vec codecs) const; + + rust::Vec codec_preferences() const; + + rust::Vec header_extensions_to_negotiate() + const; + + rust::Vec negotiated_header_extensions() const; + + void set_header_extensions_to_negotiate( + rust::Vec header_extensions_to_offer) const; + + private: + std::shared_ptr rtc_runtime_; + webrtc::scoped_refptr transceiver_; + webrtc::scoped_refptr peer_connection_; +}; + +static std::shared_ptr _shared_rtp_transceiver() { + return nullptr; +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/synthetic_audio_device.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/synthetic_audio_device.h new file mode 100644 index 0000000..0d0bb8d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/synthetic_audio_device.h @@ -0,0 +1,129 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "api/environment/environment.h" +#include "api/task_queue/task_queue_base.h" +#include "modules/audio_device/include/audio_device.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/task_utils/repeating_task.h" + +namespace livekit_ffi { + +class SyntheticAudioDevice : public webrtc::AudioDeviceModule { + public: + SyntheticAudioDevice(const webrtc::Environment& env); + ~SyntheticAudioDevice() override; + + int32_t ActiveAudioLayer(AudioLayer* audioLayer) const override; + int32_t RegisterAudioCallback(webrtc::AudioTransport* transport) override; + + int32_t Init() override; + int32_t Terminate() override; + bool Initialized() const override; + + int16_t PlayoutDevices() override; + int16_t RecordingDevices() override; + int32_t PlayoutDeviceName(uint16_t index, + char name[webrtc::kAdmMaxDeviceNameSize], + char guid[webrtc::kAdmMaxGuidSize]) override; + + int32_t RecordingDeviceName(uint16_t index, + char name[webrtc::kAdmMaxDeviceNameSize], + char guid[webrtc::kAdmMaxGuidSize]) override; + + int32_t SetPlayoutDevice(uint16_t index) override; + int32_t SetPlayoutDevice(WindowsDeviceType device) override; + int32_t SetRecordingDevice(uint16_t index) override; + int32_t SetRecordingDevice(WindowsDeviceType device) override; + + int32_t PlayoutIsAvailable(bool* available) override; + int32_t InitPlayout() override; + bool PlayoutIsInitialized() const override; + int32_t RecordingIsAvailable(bool* available) override; + int32_t InitRecording() override; + bool RecordingIsInitialized() const override; + + int32_t StartPlayout() override; + int32_t StopPlayout() override; + bool Playing() const override; + int32_t StartRecording() override; + int32_t StopRecording() override; + bool Recording() const override; + + int32_t InitSpeaker() override; + bool SpeakerIsInitialized() const override; + int32_t InitMicrophone() override; + bool MicrophoneIsInitialized() const override; + + int32_t SpeakerVolumeIsAvailable(bool* available) override; + int32_t SetSpeakerVolume(uint32_t volume) override; + int32_t SpeakerVolume(uint32_t* volume) const override; + int32_t MaxSpeakerVolume(uint32_t* maxVolume) const override; + int32_t MinSpeakerVolume(uint32_t* minVolume) const override; + + int32_t MicrophoneVolumeIsAvailable(bool* available) override; + int32_t SetMicrophoneVolume(uint32_t volume) override; + int32_t MicrophoneVolume(uint32_t* volume) const override; + int32_t MaxMicrophoneVolume(uint32_t* maxVolume) const override; + int32_t MinMicrophoneVolume(uint32_t* minVolume) const override; + + int32_t SpeakerMuteIsAvailable(bool* available) override; + int32_t SetSpeakerMute(bool enable) override; + int32_t SpeakerMute(bool* enabled) const override; + + int32_t MicrophoneMuteIsAvailable(bool* available) override; + int32_t SetMicrophoneMute(bool enable) override; + int32_t MicrophoneMute(bool* enabled) const override; + + int32_t StereoPlayoutIsAvailable(bool* available) const override; + int32_t SetStereoPlayout(bool enable) override; + int32_t StereoPlayout(bool* enabled) const override; + int32_t StereoRecordingIsAvailable(bool* available) const override; + int32_t SetStereoRecording(bool enable) override; + int32_t StereoRecording(bool* enabled) const override; + + int32_t PlayoutDelay(uint16_t* delayMS) const override; + + bool BuiltInAECIsAvailable() const override; + bool BuiltInAGCIsAvailable() const override; + bool BuiltInNSIsAvailable() const override; + + int32_t EnableBuiltInAEC(bool enable) override; + int32_t EnableBuiltInAGC(bool enable) override; + int32_t EnableBuiltInNS(bool enable) override; + +#if defined(WEBRTC_IOS) + int GetPlayoutAudioParameters(webrtc::AudioParameters* params) const override; + int GetRecordAudioParameters(webrtc::AudioParameters* params) const override; +#endif // WEBRTC_IOS + + int32_t SetObserver(webrtc::AudioDeviceObserver* sink) override; + + private: + mutable webrtc::Mutex mutex_; + std::vector data_; + std::unique_ptr audio_queue_; + webrtc::RepeatingTaskHandle audio_task_; + webrtc::AudioTransport* audio_transport_; + const webrtc::Environment& env_; + bool playing_{false}; + bool initialized_{false}; +}; +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/video_decoder_factory.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/video_decoder_factory.h new file mode 100644 index 0000000..4b1b0ed --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/video_decoder_factory.h @@ -0,0 +1,39 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "api/video_codecs/video_decoder.h" +#include "api/video_codecs/video_decoder_factory.h" +#include "absl/strings/match.h" + +namespace livekit_ffi { +class VideoDecoderFactory : public webrtc::VideoDecoderFactory { + public: + VideoDecoderFactory(); + + std::vector GetSupportedFormats() const override; + + CodecSupport QueryCodecSupport(const webrtc::SdpVideoFormat& format, + bool reference_scaling) const override; + + std::unique_ptr Create( + const webrtc::Environment& env, const webrtc::SdpVideoFormat& format) override; + + private: + std::vector> factories_; +}; +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/video_encoder_factory.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/video_encoder_factory.h new file mode 100644 index 0000000..91aedcd --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/video_encoder_factory.h @@ -0,0 +1,56 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "api/video_codecs/video_encoder.h" +#include "api/video_codecs/video_encoder_factory.h" + +namespace livekit_ffi { +class VideoEncoderFactory : public webrtc::VideoEncoderFactory { + class InternalFactory : public webrtc::VideoEncoderFactory { + public: + InternalFactory(); + + std::vector GetSupportedFormats() const override; + + CodecSupport QueryCodecSupport( + const webrtc::SdpVideoFormat& format, + std::optional scalability_mode) const override; + + std::unique_ptr Create( + const webrtc::Environment& env, const webrtc::SdpVideoFormat& format) override; + + private: + std::vector> factories_; + }; + + public: + VideoEncoderFactory(); + + std::vector GetSupportedFormats() const override; + + CodecSupport QueryCodecSupport( + const webrtc::SdpVideoFormat& format, + std::optional scalability_mode) const override; + + std::unique_ptr Create( + const webrtc::Environment& env, const webrtc::SdpVideoFormat& format) override; + + private: + std::unique_ptr internal_factory_; +}; +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/video_frame.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/video_frame.h new file mode 100644 index 0000000..6d68f83 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/video_frame.h @@ -0,0 +1,71 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include "api/video/video_frame.h" +#include "livekit/video_frame_buffer.h" +#include "rtc_base/checks.h" + +namespace livekit_ffi { +class VideoFrame; +class VideoFrameBuilder; +} // namespace livekit_ffi +#include "webrtc-sys/src/video_frame.rs.h" + +namespace livekit_ffi { + +class VideoFrame { + public: + explicit VideoFrame(const webrtc::VideoFrame& frame); + + unsigned int width() const; + unsigned int height() const; + uint32_t size() const; + uint16_t id() const; + int64_t timestamp_us() const; + int64_t ntp_time_ms() const; + uint32_t timestamp() const; + + VideoRotation rotation() const; + std::unique_ptr video_frame_buffer() const; + + webrtc::VideoFrame get() const; + + private: + webrtc::VideoFrame frame_; +}; + +// Allow to create VideoFrames from Rust, +// the builder pattern will be redone in Rust +class VideoFrameBuilder { + public: + VideoFrameBuilder() = default; + + // TODO(theomonnom): other setters? + void set_video_frame_buffer(const VideoFrameBuffer& buffer); + void set_timestamp_us(int64_t timestamp_us); + void set_rotation(VideoRotation rotation); + void set_id(uint16_t id); + std::unique_ptr build(); + + private: + webrtc::VideoFrame::Builder builder_; +}; + +std::unique_ptr new_video_frame_builder(); + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/video_frame_buffer.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/video_frame_buffer.h new file mode 100644 index 0000000..f10ce0c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/video_frame_buffer.h @@ -0,0 +1,369 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include "api/video/i420_buffer.h" +#include "api/video/i422_buffer.h" +#include "api/video/i444_buffer.h" +#include "api/video/i010_buffer.h" +#include "api/video/nv12_buffer.h" +#include "api/video/video_frame_buffer.h" + +namespace livekit_ffi { +class VideoFrameBuffer; +class FluxerGpuFrameBuffer; +class PlanarYuvBuffer; +class PlanarYuv8Buffer; +class PlanarYuv16BBuffer; +class BiplanarYuvBuffer; +class BiplanarYuv8Buffer; +class I420Buffer; +class I420ABuffer; +class I422Buffer; +class I444Buffer; +class I010Buffer; +class NV12Buffer; +} // namespace livekit_ffi + +#ifdef __APPLE__ +#include +namespace livekit_ffi { +typedef __CVBuffer PlatformImageBuffer; +} // namespace livekit_ffi +#else +namespace livekit_ffi { +typedef void PlatformImageBuffer; +} // namespace livekit_ffi +#endif + +#include "webrtc-sys/src/video_frame_buffer.rs.h" + +namespace livekit_ffi { + +class VideoFrameBuffer { + public: + explicit VideoFrameBuffer( + webrtc::scoped_refptr buffer); + + VideoFrameBufferType buffer_type() const; + + unsigned int width() const; + unsigned int height() const; + + std::unique_ptr to_i420() const; + + // Requires ownership + std::unique_ptr get_i420(); + std::unique_ptr get_i420a(); + std::unique_ptr get_i422(); + std::unique_ptr get_i444(); + std::unique_ptr get_i010(); + std::unique_ptr get_nv12(); + webrtc::scoped_refptr get() const; + + protected: + webrtc::scoped_refptr buffer_; +}; + +class FluxerGpuFrameBuffer : public webrtc::VideoFrameBuffer { + public: + enum class Kind { + kD3D11Texture, + kDmaBuf, + }; + + FluxerGpuFrameBuffer(uint64_t handle, + uint32_t width, + uint32_t height, + uint32_t dxgi_format); + FluxerGpuFrameBuffer(int fd0, + int fd1, + int fd2, + int fd3, + uint32_t plane_count, + uint32_t width, + uint32_t height, + uint32_t drm_format, + uint64_t modifier, + uint32_t stride0, + uint32_t stride1, + uint32_t stride2, + uint32_t stride3, + uint32_t offset0, + uint32_t offset1, + uint32_t offset2, + uint32_t offset3, + uint64_t device_uuid_hi, + uint64_t device_uuid_lo); + ~FluxerGpuFrameBuffer() override; + + Type type() const override; + int width() const override; + int height() const override; + webrtc::scoped_refptr ToI420() override; + webrtc::scoped_refptr CropAndScale( + int offset_x, + int offset_y, + int crop_width, + int crop_height, + int scaled_width, + int scaled_height) override; + webrtc::scoped_refptr GetMappedFrameBuffer( + webrtc::ArrayView types) override; + std::string storage_representation() const override; + + Kind kind() const { return kind_; } + uint64_t d3d11_handle() const { return d3d11_handle_; } + uint32_t dxgi_format() const { return dxgi_format_; } + uint32_t drm_format() const { return drm_format_; } + uint64_t modifier() const { return modifier_; } + uint32_t plane_count() const { return plane_count_; } + int fd(uint32_t plane) const; + uint32_t stride(uint32_t plane) const; + uint32_t offset(uint32_t plane) const; + uint64_t device_uuid_hi() const { return device_uuid_hi_; } + uint64_t device_uuid_lo() const { return device_uuid_lo_; } + + private: + Kind kind_; + uint32_t width_; + uint32_t height_; + uint64_t d3d11_handle_ = 0; + uint32_t dxgi_format_ = 0; + int fds_[4] = {-1, -1, -1, -1}; + uint32_t plane_count_ = 0; + uint32_t drm_format_ = 0; + uint64_t modifier_ = 0; + uint32_t strides_[4] = {0, 0, 0, 0}; + uint32_t offsets_[4] = {0, 0, 0, 0}; + uint64_t device_uuid_hi_ = 0; + uint64_t device_uuid_lo_ = 0; +}; + +const FluxerGpuFrameBuffer* AsFluxerGpuFrameBuffer( + const webrtc::VideoFrameBuffer* buffer); + +class PlanarYuvBuffer : public VideoFrameBuffer { + public: + explicit PlanarYuvBuffer(webrtc::scoped_refptr buffer); + + unsigned int chroma_width() const; + unsigned int chroma_height() const; + + unsigned int stride_y() const; + unsigned int stride_u() const; + unsigned int stride_v() const; + + private: + webrtc::PlanarYuvBuffer* buffer() const; +}; + +class PlanarYuv8Buffer : public PlanarYuvBuffer { + public: + explicit PlanarYuv8Buffer( + webrtc::scoped_refptr buffer); + + const uint8_t* data_y() const; + const uint8_t* data_u() const; + const uint8_t* data_v() const; + + private: + webrtc::PlanarYuv8Buffer* buffer() const; +}; + +class PlanarYuv16BBuffer : public PlanarYuvBuffer { + public: + explicit PlanarYuv16BBuffer( + webrtc::scoped_refptr buffer); + + const uint16_t* data_y() const; + const uint16_t* data_u() const; + const uint16_t* data_v() const; + + private: + webrtc::PlanarYuv16BBuffer* buffer() const; +}; + +class BiplanarYuvBuffer : public VideoFrameBuffer { + public: + explicit BiplanarYuvBuffer( + webrtc::scoped_refptr buffer); + + unsigned int chroma_width() const; + unsigned int chroma_height() const; + + unsigned int stride_y() const; + unsigned int stride_uv() const; + + private: + webrtc::BiplanarYuvBuffer* buffer() const; +}; + +class BiplanarYuv8Buffer : public BiplanarYuvBuffer { + public: + explicit BiplanarYuv8Buffer( + webrtc::scoped_refptr buffer); + + const uint8_t* data_y() const; + const uint8_t* data_uv() const; + + private: + webrtc::BiplanarYuv8Buffer* buffer() const; +}; + +class I420Buffer : public PlanarYuv8Buffer { + public: + explicit I420Buffer(webrtc::scoped_refptr buffer); + + std::unique_ptr scale(int scaled_width, int scaled_height) const; + + private: + webrtc::I420BufferInterface* buffer() const; +}; + +class I420ABuffer : public I420Buffer { + public: + explicit I420ABuffer(webrtc::scoped_refptr buffer); + + unsigned int stride_a() const; + const uint8_t* data_a() const; + + std::unique_ptr scale(int scaled_width, int scaled_height) const; + + private: + webrtc::I420ABufferInterface* buffer() const; +}; + +class I422Buffer : public PlanarYuv8Buffer { + public: + explicit I422Buffer(webrtc::scoped_refptr buffer); + + std::unique_ptr scale(int scaled_width, int scaled_height) const; + + private: + webrtc::I422BufferInterface* buffer() const; +}; + +class I444Buffer : public PlanarYuv8Buffer { + public: + explicit I444Buffer(webrtc::scoped_refptr buffer); + + std::unique_ptr scale(int scaled_width, int scaled_height) const; + + private: + webrtc::I444BufferInterface* buffer() const; +}; + +class I010Buffer : public PlanarYuv16BBuffer { + public: + explicit I010Buffer(webrtc::scoped_refptr buffer); + + std::unique_ptr scale(int scaled_width, int scaled_height) const; + + private: + webrtc::I010BufferInterface* buffer() const; +}; + +class NV12Buffer : public BiplanarYuv8Buffer { + public: + explicit NV12Buffer(webrtc::scoped_refptr buffer); + + std::unique_ptr scale(int scaled_width, int scaled_height) const; + + private: + webrtc::NV12BufferInterface* buffer() const; +}; + +std::unique_ptr copy_i420_buffer( + const std::unique_ptr& i420); +std::unique_ptr new_i420_buffer(int width, int height, int stride_y, int stride_u, int stride_v); +std::unique_ptr new_i422_buffer(int width, int height, int stride_y, int stride_u, int stride_v); +std::unique_ptr new_i444_buffer(int width, int height, int stride_y, int stride_u, int stride_v); +std::unique_ptr new_i010_buffer(int width, int height, int stride_y, int stride_u, int stride_v); +std::unique_ptr new_nv12_buffer(int width, int height, int stride_y, int stride_uv); + +std::unique_ptr new_fluxer_d3d11_texture_buffer( + uint64_t handle, uint32_t width, uint32_t height, uint32_t dxgi_format); +std::unique_ptr new_fluxer_dmabuf_texture_buffer( + int fd0, int fd1, int fd2, int fd3, uint32_t plane_count, + uint32_t width, uint32_t height, uint32_t drm_format, uint64_t modifier, + uint32_t stride0, uint32_t stride1, uint32_t stride2, uint32_t stride3, + uint32_t offset0, uint32_t offset1, uint32_t offset2, uint32_t offset3, + uint64_t device_uuid_hi, uint64_t device_uuid_lo); +bool is_fluxer_gpu_buffer(const std::unique_ptr& buffer); +uint64_t fluxer_d3d11_texture_handle(const std::unique_ptr& buffer); +uint32_t fluxer_gpu_buffer_width(const std::unique_ptr& buffer); +uint32_t fluxer_gpu_buffer_height(const std::unique_ptr& buffer); +uint32_t fluxer_gpu_buffer_format(const std::unique_ptr& buffer); + +std::unique_ptr new_native_buffer_from_platform_image_buffer(PlatformImageBuffer *buffer); +PlatformImageBuffer* native_buffer_to_platform_image_buffer(const std::unique_ptr &); + +static const VideoFrameBuffer* yuv_to_vfb(const PlanarYuvBuffer* yuv) { + return yuv; +} + +static const VideoFrameBuffer* biyuv_to_vfb(const BiplanarYuvBuffer* biyuv) { + return biyuv; +} + +static const PlanarYuvBuffer* yuv8_to_yuv(const PlanarYuv8Buffer* yuv8) { + return yuv8; +} + +static const PlanarYuvBuffer* yuv16b_to_yuv(const PlanarYuv16BBuffer* yuv16) { + return yuv16; +} + +static const BiplanarYuvBuffer* biyuv8_to_biyuv( + const BiplanarYuv8Buffer* biyuv8) { + return biyuv8; +} + +static const PlanarYuv8Buffer* i420_to_yuv8(const I420Buffer* i420) { + return i420; +} + +static const PlanarYuv8Buffer* i420a_to_yuv8(const I420ABuffer* i420a) { + return i420a; +} + +static const PlanarYuv8Buffer* i422_to_yuv8(const I422Buffer* i422) { + return i422; +} + +static const PlanarYuv8Buffer* i444_to_yuv8(const I444Buffer* i444) { + return i444; +} + +static const PlanarYuv16BBuffer* i010_to_yuv16b(const I010Buffer* i010) { + return i010; +} + +static const BiplanarYuv8Buffer* nv12_to_biyuv8(const NV12Buffer* nv12) { + return nv12; +} + +static std::unique_ptr _unique_video_frame_buffer() { + return nullptr; +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/video_track.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/video_track.h new file mode 100644 index 0000000..3f4e1c1 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/video_track.h @@ -0,0 +1,151 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "api/media_stream_interface.h" +#include "api/video/video_frame.h" +#include "livekit/helper.h" +#include "livekit/media_stream_track.h" +#include "livekit/video_frame.h" +#include "livekit/webrtc.h" +#include "media/base/adapted_video_track_source.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/timestamp_aligner.h" +#include "rust/cxx.h" + +namespace livekit_ffi { +class VideoTrack; +class NativeVideoSink; +class VideoTrackSource; +class PacketTrailerHandler; // forward declaration to avoid circular include +} // namespace livekit_ffi +#include "webrtc-sys/src/video_track.rs.h" + +namespace livekit_ffi { + +class VideoTrack : public MediaStreamTrack { + private: + friend RtcRuntime; + VideoTrack(std::shared_ptr rtc_runtime, + webrtc::scoped_refptr track); + + public: + ~VideoTrack(); + + void add_sink(const std::shared_ptr& sink) const; + void remove_sink(const std::shared_ptr& sink) const; + + void set_should_receive(bool should_receive) const; + bool should_receive() const; + ContentHint content_hint() const; + void set_content_hint(ContentHint hint) const; + + private: + webrtc::VideoTrackInterface* track() const { + return static_cast(track_.get()); + } + + mutable webrtc::Mutex mutex_; + + // Same for AudioTrack: + // Keep a strong reference to the added sinks, so we don't need to + // manage the lifetime safety on the Rust side + mutable std::vector> sinks_; +}; + +class NativeVideoSink : public webrtc::VideoSinkInterface { + public: + explicit NativeVideoSink(rust::Box observer); + + void OnFrame(const webrtc::VideoFrame& frame) override; + void OnDiscardedFrame() override; + void OnConstraintsChanged( + const webrtc::VideoTrackSourceConstraints& constraints) override; + + private: + rust::Box observer_; +}; + +std::shared_ptr new_native_video_sink( + rust::Box observer); + +class VideoTrackSource { + class InternalSource : public webrtc::AdaptedVideoTrackSource { + public: + InternalSource(const VideoResolution& resolution, + bool is_screencast); // (0, 0) means no resolution/optional, the + // source will guess the resolution at the + // first captured frame + ~InternalSource() override; + + bool is_screencast() const override; + std::optional needs_denoising() const override; + SourceState state() const override; + bool remote() const override; + VideoResolution video_resolution() const; + bool on_captured_frame(const webrtc::VideoFrame& frame, + const FrameMetadata& frame_metadata); + + void set_packet_trailer_handler( + std::shared_ptr handler); + + private: + mutable webrtc::Mutex mutex_; + webrtc::TimestampAligner timestamp_aligner_; + VideoResolution resolution_; + std::shared_ptr packet_trailer_handler_; + bool is_screencast_; + }; + + public: + VideoTrackSource(const VideoResolution& resolution, bool is_screencast); + + VideoResolution video_resolution() const; + + bool on_captured_frame(const std::unique_ptr& frame, + const FrameMetadata& frame_metadata) + const; // frames pushed from Rust (+interior mutability) + + void set_packet_trailer_handler( + std::shared_ptr handler) const; + + webrtc::scoped_refptr get() const; + + private: + webrtc::scoped_refptr source_; +}; + +std::shared_ptr new_video_track_source( + const VideoResolution& resolution, bool is_screencast); + +static std::shared_ptr video_to_media( + std::shared_ptr track) { + return track; +} + +static std::shared_ptr media_to_video( + std::shared_ptr track) { + return std::static_pointer_cast(track); +} + +static std::shared_ptr _shared_video_track() { + return nullptr; // Ignore +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/webrtc.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/webrtc.h new file mode 100644 index 0000000..5c5a3e8 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/webrtc.h @@ -0,0 +1,118 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include + +#include "api/media_stream_interface.h" +#include "api/rtp_receiver_interface.h" +#include "api/rtp_sender_interface.h" +#include "livekit/helper.h" +#include "rtc_base/logging.h" +#include "rtc_base/physical_socket_server.h" +#include "rtc_base/ssl_adapter.h" +#include "rtc_base/thread.h" +#include "rust/cxx.h" + +#ifdef WEBRTC_WIN +#include "rtc_base/win32_socket_init.h" +#endif + +namespace livekit_ffi { +class RtcRuntime; +class LogSink; +} // namespace livekit_ffi +#include "webrtc-sys/src/webrtc.rs.h" + +namespace livekit_ffi { + +class MediaStreamTrack; +class RtpReceiver; +class RtpSender; + +// Using a shared_ptr in RtcRuntime allows us to keep a strong reference to it +// on resources that depend on it. (e.g: AudioTrack, VideoTrack). +class RtcRuntime : public std::enable_shared_from_this { + public: + [[nodiscard]] static std::shared_ptr create() { + return std::shared_ptr(new RtcRuntime()); + } + + RtcRuntime(const RtcRuntime&) = delete; + RtcRuntime& operator=(const RtcRuntime&) = delete; + ~RtcRuntime(); + + webrtc::Thread* network_thread() const; + webrtc::Thread* worker_thread() const; + webrtc::Thread* signaling_thread() const; + + std::shared_ptr get_or_create_media_stream_track( + webrtc::scoped_refptr track); + + std::shared_ptr get_or_create_audio_track( + webrtc::scoped_refptr track); + + std::shared_ptr get_or_create_video_track( + webrtc::scoped_refptr track); + + private: + RtcRuntime(); + + std::unique_ptr network_thread_; + std::unique_ptr worker_thread_; + std::unique_ptr signaling_thread_; + + // Lists used to make sure we don't create multiple wrappers for one + // underlying webrtc object. (e.g: webrtc::VideoTrackInterface should only + // have one livekit_ffi::VideoTrack associated with it). + // The only reason we to do that is to allow to add states inside our + // wrappers (e.g: the sinks_ member inside AudioTrack) + // DataChannel and the PeerConnectionFactory don't need to do this (There's no + // way to retrieve them after creation) + webrtc::Mutex mutex_; + std::vector> media_stream_tracks_; + // We don't have additonal state in RtpReceiver and RtpSender atm.. + // std::vector> rtp_receivers_; + // std::vector> rtp_senders_; + +#ifdef WEBRTC_WIN + // webrtc::WinsockInitializer winsock_; + // webrtc::PhysicalSocketServer ss_; + // webrtc::AutoSocketServerThread main_thread_{&ss_}; +#endif +}; + +class LogSink : public webrtc::LogSink { + public: + LogSink(rust::Fn fnc); + ~LogSink(); + + void OnLogMessage(const std::string& message, + webrtc::LoggingSeverity severity) override; + + void OnLogMessage(const std::string& message) override {} + + private: + rust::Fn fnc_; +}; + +std::unique_ptr new_log_sink( + rust::Fn fnc); + +rust::String create_random_uuid(); + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/yuv_helper.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/yuv_helper.h new file mode 100644 index 0000000..9e4d0b6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/include/livekit/yuv_helper.h @@ -0,0 +1,381 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include "api/video/yuv_helper.h" +#include "webrtc-sys/src/yuv_helper.rs.h" + +namespace livekit_ffi { + +#define THROW_ON_ERROR(ret) \ + if (ret != 0) { \ + throw std::runtime_error("libyuv error: " + std::to_string(ret)); \ + } + +static void i420_to_argb(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_u, + int src_stride_u, + const uint8_t* src_v, + int src_stride_v, + uint8_t* dst_argb, + int dst_stride_argb, + int width, + int height) { + THROW_ON_ERROR(webrtc::I420ToARGB(src_y, src_stride_y, src_u, src_stride_u, + src_v, src_stride_v, dst_argb, + dst_stride_argb, width, height)); +} + +static void i420_to_bgra(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_u, + int src_stride_u, + const uint8_t* src_v, + int src_stride_v, + uint8_t* dst_bgra, + int dst_stride_bgra, + int width, + int height) { + THROW_ON_ERROR(webrtc::I420ToBGRA(src_y, src_stride_y, src_u, src_stride_u, + src_v, src_stride_v, dst_bgra, + dst_stride_bgra, width, height)); +} + +static void i420_to_abgr(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_u, + int src_stride_u, + const uint8_t* src_v, + int src_stride_v, + uint8_t* dst_abgr, + int dst_stride_abgr, + int width, + int height) { + THROW_ON_ERROR(webrtc::I420ToABGR(src_y, src_stride_y, src_u, src_stride_u, + src_v, src_stride_v, dst_abgr, + dst_stride_abgr, width, height)); +} + +static void i420_to_rgba(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_u, + int src_stride_u, + const uint8_t* src_v, + int src_stride_v, + uint8_t* dst_rgba, + int dst_stride_rgba, + int width, + int height) { + THROW_ON_ERROR(webrtc::I420ToRGBA(src_y, src_stride_y, src_u, src_stride_u, + src_v, src_stride_v, dst_rgba, + dst_stride_rgba, width, height)); +} + +static void argb_to_i420(const uint8_t* src_argb, + int src_stride_argb, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_u, + int dst_stride_u, + uint8_t* dst_v, + int dst_stride_v, + int width, + int height) { + THROW_ON_ERROR(webrtc::ARGBToI420(src_argb, src_stride_argb, dst_y, + dst_stride_y, dst_u, dst_stride_u, dst_v, + dst_stride_v, width, height)); +} + +static void abgr_to_i420(const uint8_t* src_abgr, + int src_stride_abgr, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_u, + int dst_stride_u, + uint8_t* dst_v, + int dst_stride_v, + int width, + int height) { + THROW_ON_ERROR(webrtc::ABGRToI420(src_abgr, src_stride_abgr, dst_y, + dst_stride_y, dst_u, dst_stride_u, dst_v, + dst_stride_v, width, height)); +} + +static void argb_to_rgb24(const uint8_t* src_argb, + int src_stride_argb, + uint8_t* dst_rgb24, + int dst_stride_rgb24, + int width, + int height) { + THROW_ON_ERROR(webrtc::ARGBToRGB24(src_argb, src_stride_argb, dst_rgb24, + dst_stride_rgb24, width, height)); +} + +static void i420_to_nv12(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_u, + int src_stride_u, + const uint8_t* src_v, + int src_stride_v, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_uv, + int dst_stride_uv, + int width, + int height) { + THROW_ON_ERROR(webrtc::I420ToNV12(src_y, src_stride_y, src_u, src_stride_u, + src_v, src_stride_v, dst_y, dst_stride_y, + dst_uv, dst_stride_uv, width, height)); +} + +static void nv12_to_i420(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_uv, + int src_stride_uv, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_u, + int dst_stride_u, + uint8_t* dst_v, + int dst_stride_v, + int width, + int height) { + THROW_ON_ERROR(webrtc::NV12ToI420(src_y, src_stride_y, src_uv, src_stride_uv, + dst_y, dst_stride_y, dst_u, dst_stride_u, + dst_v, dst_stride_v, width, height)); +} + +static void i420_to_nv12(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_u, + int src_stride_u, + uint8_t* src_v, + int src_stride_v, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_uv, + int dst_stride_uv, + int width, + int height) { + THROW_ON_ERROR(webrtc::NV12ToI420(src_y, src_stride_y, src_u, src_stride_u, + src_v, src_stride_v, dst_y, dst_stride_y, + dst_uv, dst_stride_uv, width, height)); +} + +static void i444_to_i420(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_u, + int src_stride_u, + const uint8_t* src_v, + int src_stride_v, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_u, + int dst_stride_u, + uint8_t* dst_v, + int dst_stride_v, + int width, + int height) { + THROW_ON_ERROR(webrtc::I444ToI420( + src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v, dst_y, + dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v, width, height)); +} + +static void i422_to_i420(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_u, + int src_stride_u, + const uint8_t* src_v, + int src_stride_v, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_u, + int dst_stride_u, + uint8_t* dst_v, + int dst_stride_v, + int width, + int height) { + THROW_ON_ERROR(webrtc::I422ToI420( + src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v, dst_y, + dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v, width, height)); +} + +static void i010_to_i420(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_u, + int src_stride_u, + const uint16_t* src_v, + int src_stride_v, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_u, + int dst_stride_u, + uint8_t* dst_v, + int dst_stride_v, + int width, + int height) { + THROW_ON_ERROR(webrtc::I010ToI420( + src_y, src_stride_y, src_u, src_stride_u, src_v, src_stride_v, dst_y, + dst_stride_y, dst_u, dst_stride_u, dst_v, dst_stride_v, width, height)); +} + +static void nv12_to_argb(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_uv, + int src_stride_uv, + uint8_t* dst_argb, + int dst_stride_argb, + int width, + int height) { + THROW_ON_ERROR(webrtc::NV12ToARGB(src_y, src_stride_y, src_uv, src_stride_uv, + dst_argb, dst_stride_argb, width, height)); +} + +static void nv12_to_abgr(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_uv, + int src_stride_uv, + uint8_t* dst_abgr, + int dst_stride_abgr, + int width, + int height) { + THROW_ON_ERROR(webrtc::NV12ToABGR(src_y, src_stride_y, src_uv, src_stride_uv, + dst_abgr, dst_stride_abgr, width, height)); +} + +static void i444_to_argb(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_u, + int src_stride_u, + const uint8_t* src_v, + int src_stride_v, + uint8_t* dst_abgr, + int dst_stride_abgr, + int width, + int height) { + THROW_ON_ERROR(webrtc::I444ToARGB(src_y, src_stride_y, src_u, src_stride_u, + src_v, src_stride_v, dst_abgr, + dst_stride_abgr, width, height)); +} + +static void i444_to_abgr(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_u, + int src_stride_u, + const uint8_t* src_v, + int src_stride_v, + uint8_t* dst_abgr, + int dst_stride_abgr, + int width, + int height) { + THROW_ON_ERROR(webrtc::I444ToABGR(src_y, src_stride_y, src_u, src_stride_u, + src_v, src_stride_v, dst_abgr, + dst_stride_abgr, width, height)); +} + +static void i422_to_argb(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_u, + int src_stride_u, + const uint8_t* src_v, + int src_stride_v, + uint8_t* dst_argb, + int dst_stride_argb, + int width, + int height) { + THROW_ON_ERROR(webrtc::I422ToARGB(src_y, src_stride_y, src_u, src_stride_u, + src_v, src_stride_v, dst_argb, + dst_stride_argb, width, height)); +} + +static void i422_to_abgr(const uint8_t* src_y, + int src_stride_y, + const uint8_t* src_u, + int src_stride_u, + const uint8_t* src_v, + int src_stride_v, + uint8_t* dst_abgr, + int dst_stride_abgr, + int width, + int height) { + THROW_ON_ERROR(webrtc::I422ToABGR(src_y, src_stride_y, src_u, src_stride_u, + src_v, src_stride_v, dst_abgr, + dst_stride_abgr, width, height)); +} + +static void i010_to_argb(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_u, + int src_stride_u, + const uint16_t* src_v, + int src_stride_v, + uint8_t* dst_argb, + int dst_stride_argb, + int width, + int height) { + THROW_ON_ERROR(webrtc::I010ToARGB(src_y, src_stride_y, src_u, src_stride_u, + src_v, src_stride_v, dst_argb, + dst_stride_argb, width, height)); +} + +static void i010_to_abgr(const uint16_t* src_y, + int src_stride_y, + const uint16_t* src_u, + int src_stride_u, + const uint16_t* src_v, + int src_stride_v, + uint8_t* dst_abgr, + int dst_stride_abgr, + int width, + int height) { + THROW_ON_ERROR(webrtc::I010ToABGR(src_y, src_stride_y, src_u, src_stride_u, + src_v, src_stride_v, dst_abgr, + dst_stride_abgr, width, height)); +} + +static void abgr_to_nv12(const uint8_t* src_abgr, + int src_stride_abgr, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_uv, + int dst_stride_uv, + int width, + int height) { + THROW_ON_ERROR(webrtc::ABGRToNV12(src_abgr, src_stride_abgr, dst_y, + dst_stride_y, dst_uv, dst_stride_uv, width, + height)); +} + +static void argb_to_nv12(const uint8_t* src_argb, + int src_stride_argb, + uint8_t* dst_y, + int dst_stride_y, + uint8_t* dst_uv, + int dst_stride_uv, + int width, + int height) { + THROW_ON_ERROR(webrtc::ARGBToNV12(src_argb, src_stride_argb, dst_y, + dst_stride_y, dst_uv, dst_stride_uv, width, + height)); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/.gclient b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/.gclient new file mode 100644 index 0000000..6305d0c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/.gclient @@ -0,0 +1,9 @@ +solutions = [ + { + "name": 'src', + "url": 'https://github.com/webrtc-sdk/webrtc.git@m144_release', + "custom_deps": {}, + "deps_file": "DEPS", + "managed": False, + }, +] diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/.gitignore b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/.gitignore new file mode 100644 index 0000000..3821240 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/.gitignore @@ -0,0 +1,12 @@ +.cipd +src +.gclient_* +depot_tools +ninja + +# builds +win-* +mac-* +linux-* +android-* +ios-* diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/README.md b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/README.md new file mode 100644 index 0000000..222db63 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/README.md @@ -0,0 +1,28 @@ +This directory can contain a checkout of WebRTC. The build scripts +here will install dependencies, checkout the version that LiveKit +currently uses, apply some patches to it, and build it. For example to +do a Linux debug build on x64: + +```sh +$ ./build-linux.sh --arch x64 --profile release +``` + +After running this, `linux-x64-debug/lib/libwebrtc.a` should +exist. This can be rerun to rebuild it, but will complain about +patches not applying as they have already been applied. + +If something goes wrong it may be helpful to consult the [WebRTC native +development documentation](https://webrtc.googlesource.com/src/+/main/docs/native-code/development/). + +# Building LiveKit Rust SDK with custom WebRTC checkout + +Add the following environment variable to `/.config/config.toml`, to +specify use of a custom WebRTC build: + +```toml +[env] +LK_CUSTOM_WEBRTC = { value = "webrtc-sys/libwebrtc/linux-x64-release", relative = true } +``` + +Note that `linux-x64-debug` should be replaced with the artifact +directory appropriate for your configuration. diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/boringssl_prefix_symbols.txt b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/boringssl_prefix_symbols.txt new file mode 100644 index 0000000..69eeb5a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/boringssl_prefix_symbols.txt @@ -0,0 +1,2767 @@ +# this list was extracted from the version of BoringSSL used by libwebrtc +# version m114_release, by grepping for OPENSSL_EXPORT: +# +# rg -NIU "^.*OPENSSL_EXPORT .*[ \n]\*?([\d\w_]+)\(.*$" -r '$1' | sort | uniq +AES_CMAC LK_AES_CMAC +AES_cbc_encrypt LK_AES_cbc_encrypt +AES_cfb128_encrypt LK_AES_cfb128_encrypt +AES_ctr128_encrypt LK_AES_ctr128_encrypt +AES_decrypt LK_AES_decrypt +AES_ecb_encrypt LK_AES_ecb_encrypt +AES_encrypt LK_AES_encrypt +AES_ofb128_encrypt LK_AES_ofb128_encrypt +AES_set_decrypt_key LK_AES_set_decrypt_key +AES_set_encrypt_key LK_AES_set_encrypt_key +AES_unwrap_key LK_AES_unwrap_key +AES_unwrap_key_padded LK_AES_unwrap_key_padded +AES_wrap_key LK_AES_wrap_key +AES_wrap_key_padded LK_AES_wrap_key_padded +ASN1_BIT_STRING_check LK_ASN1_BIT_STRING_check +ASN1_BIT_STRING_free LK_ASN1_BIT_STRING_free +ASN1_BIT_STRING_get_bit LK_ASN1_BIT_STRING_get_bit +ASN1_BIT_STRING_new LK_ASN1_BIT_STRING_new +ASN1_BIT_STRING_num_bytes LK_ASN1_BIT_STRING_num_bytes +ASN1_BIT_STRING_set LK_ASN1_BIT_STRING_set +ASN1_BIT_STRING_set_bit LK_ASN1_BIT_STRING_set_bit +ASN1_BMPSTRING_free LK_ASN1_BMPSTRING_free +ASN1_BMPSTRING_new LK_ASN1_BMPSTRING_new +ASN1_ENUMERATED_free LK_ASN1_ENUMERATED_free +ASN1_ENUMERATED_get LK_ASN1_ENUMERATED_get +ASN1_ENUMERATED_get_int64 LK_ASN1_ENUMERATED_get_int64 +ASN1_ENUMERATED_get_uint64 LK_ASN1_ENUMERATED_get_uint64 +ASN1_ENUMERATED_new LK_ASN1_ENUMERATED_new +ASN1_ENUMERATED_set LK_ASN1_ENUMERATED_set +ASN1_ENUMERATED_set_int64 LK_ASN1_ENUMERATED_set_int64 +ASN1_ENUMERATED_set_uint64 LK_ASN1_ENUMERATED_set_uint64 +ASN1_ENUMERATED_to_BN LK_ASN1_ENUMERATED_to_BN +ASN1_GENERALIZEDTIME_adj LK_ASN1_GENERALIZEDTIME_adj +ASN1_GENERALIZEDTIME_check LK_ASN1_GENERALIZEDTIME_check +ASN1_GENERALIZEDTIME_free LK_ASN1_GENERALIZEDTIME_free +ASN1_GENERALIZEDTIME_new LK_ASN1_GENERALIZEDTIME_new +ASN1_GENERALIZEDTIME_print LK_ASN1_GENERALIZEDTIME_print +ASN1_GENERALIZEDTIME_set LK_ASN1_GENERALIZEDTIME_set +ASN1_GENERALIZEDTIME_set_string LK_ASN1_GENERALIZEDTIME_set_string +ASN1_GENERALSTRING_free LK_ASN1_GENERALSTRING_free +ASN1_GENERALSTRING_new LK_ASN1_GENERALSTRING_new +ASN1_IA5STRING_free LK_ASN1_IA5STRING_free +ASN1_IA5STRING_new LK_ASN1_IA5STRING_new +ASN1_INTEGER_cmp LK_ASN1_INTEGER_cmp +ASN1_INTEGER_dup LK_ASN1_INTEGER_dup +ASN1_INTEGER_free LK_ASN1_INTEGER_free +ASN1_INTEGER_get LK_ASN1_INTEGER_get +ASN1_INTEGER_get_int64 LK_ASN1_INTEGER_get_int64 +ASN1_INTEGER_get_uint64 LK_ASN1_INTEGER_get_uint64 +ASN1_INTEGER_new LK_ASN1_INTEGER_new +ASN1_INTEGER_set LK_ASN1_INTEGER_set +ASN1_INTEGER_set_int64 LK_ASN1_INTEGER_set_int64 +ASN1_INTEGER_set_uint64 LK_ASN1_INTEGER_set_uint64 +ASN1_INTEGER_to_BN LK_ASN1_INTEGER_to_BN +ASN1_NULL_free LK_ASN1_NULL_free +ASN1_NULL_new LK_ASN1_NULL_new +ASN1_OBJECT_create LK_ASN1_OBJECT_create +ASN1_OBJECT_free LK_ASN1_OBJECT_free +ASN1_OCTET_STRING_cmp LK_ASN1_OCTET_STRING_cmp +ASN1_OCTET_STRING_dup LK_ASN1_OCTET_STRING_dup +ASN1_OCTET_STRING_free LK_ASN1_OCTET_STRING_free +ASN1_OCTET_STRING_new LK_ASN1_OCTET_STRING_new +ASN1_OCTET_STRING_set LK_ASN1_OCTET_STRING_set +ASN1_PRINTABLESTRING_free LK_ASN1_PRINTABLESTRING_free +ASN1_PRINTABLESTRING_new LK_ASN1_PRINTABLESTRING_new +ASN1_PRINTABLE_free LK_ASN1_PRINTABLE_free +ASN1_PRINTABLE_new LK_ASN1_PRINTABLE_new +ASN1_STRING_TABLE_add LK_ASN1_STRING_TABLE_add +ASN1_STRING_TABLE_cleanup LK_ASN1_STRING_TABLE_cleanup +ASN1_STRING_cmp LK_ASN1_STRING_cmp +ASN1_STRING_copy LK_ASN1_STRING_copy +ASN1_STRING_data LK_ASN1_STRING_data +ASN1_STRING_dup LK_ASN1_STRING_dup +ASN1_STRING_free LK_ASN1_STRING_free +ASN1_STRING_get0_data LK_ASN1_STRING_get0_data +ASN1_STRING_get_default_mask LK_ASN1_STRING_get_default_mask +ASN1_STRING_length LK_ASN1_STRING_length +ASN1_STRING_new LK_ASN1_STRING_new +ASN1_STRING_print LK_ASN1_STRING_print +ASN1_STRING_print_ex LK_ASN1_STRING_print_ex +ASN1_STRING_print_ex_fp LK_ASN1_STRING_print_ex_fp +ASN1_STRING_set LK_ASN1_STRING_set +ASN1_STRING_set0 LK_ASN1_STRING_set0 +ASN1_STRING_set_by_NID LK_ASN1_STRING_set_by_NID +ASN1_STRING_set_default_mask LK_ASN1_STRING_set_default_mask +ASN1_STRING_set_default_mask_asc LK_ASN1_STRING_set_default_mask_asc +ASN1_STRING_to_UTF8 LK_ASN1_STRING_to_UTF8 +ASN1_STRING_type LK_ASN1_STRING_type +ASN1_STRING_type_new LK_ASN1_STRING_type_new +ASN1_T61STRING_free LK_ASN1_T61STRING_free +ASN1_T61STRING_new LK_ASN1_T61STRING_new +ASN1_TIME_adj LK_ASN1_TIME_adj +ASN1_TIME_check LK_ASN1_TIME_check +ASN1_TIME_diff LK_ASN1_TIME_diff +ASN1_TIME_free LK_ASN1_TIME_free +ASN1_TIME_new LK_ASN1_TIME_new +ASN1_TIME_print LK_ASN1_TIME_print +ASN1_TIME_set LK_ASN1_TIME_set +ASN1_TIME_set_posix LK_ASN1_TIME_set_posix +ASN1_TIME_set_string LK_ASN1_TIME_set_string +ASN1_TIME_to_generalizedtime LK_ASN1_TIME_to_generalizedtime +ASN1_TIME_to_posix LK_ASN1_TIME_to_posix +ASN1_TIME_to_time_t LK_ASN1_TIME_to_time_t +ASN1_TYPE_cmp LK_ASN1_TYPE_cmp +ASN1_TYPE_free LK_ASN1_TYPE_free +ASN1_TYPE_get LK_ASN1_TYPE_get +ASN1_TYPE_new LK_ASN1_TYPE_new +ASN1_TYPE_set LK_ASN1_TYPE_set +ASN1_TYPE_set1 LK_ASN1_TYPE_set1 +ASN1_UNIVERSALSTRING_free LK_ASN1_UNIVERSALSTRING_free +ASN1_UNIVERSALSTRING_new LK_ASN1_UNIVERSALSTRING_new +ASN1_UTCTIME_adj LK_ASN1_UTCTIME_adj +ASN1_UTCTIME_check LK_ASN1_UTCTIME_check +ASN1_UTCTIME_cmp_time_t LK_ASN1_UTCTIME_cmp_time_t +ASN1_UTCTIME_free LK_ASN1_UTCTIME_free +ASN1_UTCTIME_new LK_ASN1_UTCTIME_new +ASN1_UTCTIME_print LK_ASN1_UTCTIME_print +ASN1_UTCTIME_set LK_ASN1_UTCTIME_set +ASN1_UTCTIME_set_string LK_ASN1_UTCTIME_set_string +ASN1_UTF8STRING_free LK_ASN1_UTF8STRING_free +ASN1_UTF8STRING_new LK_ASN1_UTF8STRING_new +ASN1_VISIBLESTRING_free LK_ASN1_VISIBLESTRING_free +ASN1_VISIBLESTRING_new LK_ASN1_VISIBLESTRING_new +ASN1_digest LK_ASN1_digest +ASN1_get_object LK_ASN1_get_object +ASN1_item_d2i LK_ASN1_item_d2i +ASN1_item_d2i_bio LK_ASN1_item_d2i_bio +ASN1_item_d2i_fp LK_ASN1_item_d2i_fp +ASN1_item_digest LK_ASN1_item_digest +ASN1_item_dup LK_ASN1_item_dup +ASN1_item_free LK_ASN1_item_free +ASN1_item_i2d LK_ASN1_item_i2d +ASN1_item_i2d_bio LK_ASN1_item_i2d_bio +ASN1_item_i2d_fp LK_ASN1_item_i2d_fp +ASN1_item_new LK_ASN1_item_new +ASN1_item_pack LK_ASN1_item_pack +ASN1_item_sign LK_ASN1_item_sign +ASN1_item_sign_ctx LK_ASN1_item_sign_ctx +ASN1_item_unpack LK_ASN1_item_unpack +ASN1_item_verify LK_ASN1_item_verify +ASN1_mbstring_copy LK_ASN1_mbstring_copy +ASN1_mbstring_ncopy LK_ASN1_mbstring_ncopy +ASN1_object_size LK_ASN1_object_size +ASN1_put_eoc LK_ASN1_put_eoc +ASN1_put_object LK_ASN1_put_object +ASN1_tag2bit LK_ASN1_tag2bit +ASN1_tag2str LK_ASN1_tag2str +BF_cbc_encrypt LK_BF_cbc_encrypt +BF_decrypt LK_BF_decrypt +BF_ecb_encrypt LK_BF_ecb_encrypt +BF_encrypt LK_BF_encrypt +BF_set_key LK_BF_set_key +BIO_append_filename LK_BIO_append_filename +BIO_callback_ctrl LK_BIO_callback_ctrl +BIO_clear_flags LK_BIO_clear_flags +BIO_clear_retry_flags LK_BIO_clear_retry_flags +BIO_copy_next_retry LK_BIO_copy_next_retry +BIO_ctrl LK_BIO_ctrl +BIO_ctrl_get_read_request LK_BIO_ctrl_get_read_request +BIO_ctrl_get_write_guarantee LK_BIO_ctrl_get_write_guarantee +BIO_ctrl_pending LK_BIO_ctrl_pending +BIO_do_connect LK_BIO_do_connect +BIO_eof LK_BIO_eof +BIO_f_base64 LK_BIO_f_base64 +BIO_f_ssl LK_BIO_f_ssl +BIO_find_type LK_BIO_find_type +BIO_flush LK_BIO_flush +BIO_free LK_BIO_free +BIO_free_all LK_BIO_free_all +BIO_get_data LK_BIO_get_data +BIO_get_fd LK_BIO_get_fd +BIO_get_fp LK_BIO_get_fp +BIO_get_init LK_BIO_get_init +BIO_get_mem_data LK_BIO_get_mem_data +BIO_get_mem_ptr LK_BIO_get_mem_ptr +BIO_get_new_index LK_BIO_get_new_index +BIO_get_retry_flags LK_BIO_get_retry_flags +BIO_get_retry_reason LK_BIO_get_retry_reason +BIO_get_shutdown LK_BIO_get_shutdown +BIO_gets LK_BIO_gets +BIO_hexdump LK_BIO_hexdump +BIO_indent LK_BIO_indent +BIO_int_ctrl LK_BIO_int_ctrl +BIO_mem_contents LK_BIO_mem_contents +BIO_meth_free LK_BIO_meth_free +BIO_meth_new LK_BIO_meth_new +BIO_meth_set_create LK_BIO_meth_set_create +BIO_meth_set_ctrl LK_BIO_meth_set_ctrl +BIO_meth_set_destroy LK_BIO_meth_set_destroy +BIO_meth_set_gets LK_BIO_meth_set_gets +BIO_meth_set_puts LK_BIO_meth_set_puts +BIO_meth_set_read LK_BIO_meth_set_read +BIO_meth_set_write LK_BIO_meth_set_write +BIO_method_type LK_BIO_method_type +BIO_new LK_BIO_new +BIO_new_bio_pair LK_BIO_new_bio_pair +BIO_new_connect LK_BIO_new_connect +BIO_new_fd LK_BIO_new_fd +BIO_new_file LK_BIO_new_file +BIO_new_fp LK_BIO_new_fp +BIO_new_mem_buf LK_BIO_new_mem_buf +BIO_new_socket LK_BIO_new_socket +BIO_next LK_BIO_next +BIO_number_read LK_BIO_number_read +BIO_number_written LK_BIO_number_written +BIO_pending LK_BIO_pending +BIO_pop LK_BIO_pop +BIO_printf LK_BIO_printf +BIO_ptr_ctrl LK_BIO_ptr_ctrl +BIO_push LK_BIO_push +BIO_puts LK_BIO_puts +BIO_read LK_BIO_read +BIO_read_asn1 LK_BIO_read_asn1 +BIO_read_filename LK_BIO_read_filename +BIO_reset LK_BIO_reset +BIO_rw_filename LK_BIO_rw_filename +BIO_s_connect LK_BIO_s_connect +BIO_s_fd LK_BIO_s_fd +BIO_s_file LK_BIO_s_file +BIO_s_mem LK_BIO_s_mem +BIO_s_socket LK_BIO_s_socket +BIO_seek LK_BIO_seek +BIO_set_close LK_BIO_set_close +BIO_set_conn_hostname LK_BIO_set_conn_hostname +BIO_set_conn_int_port LK_BIO_set_conn_int_port +BIO_set_conn_port LK_BIO_set_conn_port +BIO_set_data LK_BIO_set_data +BIO_set_fd LK_BIO_set_fd +BIO_set_flags LK_BIO_set_flags +BIO_set_fp LK_BIO_set_fp +BIO_set_init LK_BIO_set_init +BIO_set_mem_buf LK_BIO_set_mem_buf +BIO_set_mem_eof_return LK_BIO_set_mem_eof_return +BIO_set_nbio LK_BIO_set_nbio +BIO_set_retry_read LK_BIO_set_retry_read +BIO_set_retry_reason LK_BIO_set_retry_reason +BIO_set_retry_special LK_BIO_set_retry_special +BIO_set_retry_write LK_BIO_set_retry_write +BIO_set_shutdown LK_BIO_set_shutdown +BIO_set_ssl LK_BIO_set_ssl +BIO_set_write_buffer_size LK_BIO_set_write_buffer_size +BIO_should_io_special LK_BIO_should_io_special +BIO_should_read LK_BIO_should_read +BIO_should_retry LK_BIO_should_retry +BIO_should_write LK_BIO_should_write +BIO_shutdown_wr LK_BIO_shutdown_wr +BIO_snprintf LK_BIO_snprintf +BIO_tell LK_BIO_tell +BIO_test_flags LK_BIO_test_flags +BIO_up_ref LK_BIO_up_ref +BIO_vfree LK_BIO_vfree +BIO_vsnprintf LK_BIO_vsnprintf +BIO_wpending LK_BIO_wpending +BIO_write LK_BIO_write +BIO_write_all LK_BIO_write_all +BIO_write_filename LK_BIO_write_filename +BLAKE2B256 LK_BLAKE2B256 +BLAKE2B256_Final LK_BLAKE2B256_Final +BLAKE2B256_Init LK_BLAKE2B256_Init +BLAKE2B256_Update LK_BLAKE2B256_Update +BN_CTX_end LK_BN_CTX_end +BN_CTX_free LK_BN_CTX_free +BN_CTX_get LK_BN_CTX_get +BN_CTX_new LK_BN_CTX_new +BN_CTX_start LK_BN_CTX_start +BN_GENCB_call LK_BN_GENCB_call +BN_GENCB_free LK_BN_GENCB_free +BN_GENCB_get_arg LK_BN_GENCB_get_arg +BN_GENCB_new LK_BN_GENCB_new +BN_GENCB_set LK_BN_GENCB_set +BN_MONT_CTX_copy LK_BN_MONT_CTX_copy +BN_MONT_CTX_free LK_BN_MONT_CTX_free +BN_MONT_CTX_new LK_BN_MONT_CTX_new +BN_MONT_CTX_new_consttime LK_BN_MONT_CTX_new_consttime +BN_MONT_CTX_new_for_modulus LK_BN_MONT_CTX_new_for_modulus +BN_MONT_CTX_set LK_BN_MONT_CTX_set +BN_abs_is_word LK_BN_abs_is_word +BN_add LK_BN_add +BN_add_word LK_BN_add_word +BN_asc2bn LK_BN_asc2bn +BN_bin2bn LK_BN_bin2bn +BN_bn2bin LK_BN_bn2bin +BN_bn2bin_padded LK_BN_bn2bin_padded +BN_bn2binpad LK_BN_bn2binpad +BN_bn2cbb_padded LK_BN_bn2cbb_padded +BN_bn2dec LK_BN_bn2dec +BN_bn2hex LK_BN_bn2hex +BN_bn2le_padded LK_BN_bn2le_padded +BN_bn2mpi LK_BN_bn2mpi +BN_clear LK_BN_clear +BN_clear_bit LK_BN_clear_bit +BN_clear_free LK_BN_clear_free +BN_cmp LK_BN_cmp +BN_cmp_word LK_BN_cmp_word +BN_copy LK_BN_copy +BN_count_low_zero_bits LK_BN_count_low_zero_bits +BN_dec2bn LK_BN_dec2bn +BN_div LK_BN_div +BN_div_word LK_BN_div_word +BN_dup LK_BN_dup +BN_enhanced_miller_rabin_primality_test LK_BN_enhanced_miller_rabin_primality_test +BN_equal_consttime LK_BN_equal_consttime +BN_exp LK_BN_exp +BN_free LK_BN_free +BN_from_montgomery LK_BN_from_montgomery +BN_gcd LK_BN_gcd +BN_generate_prime_ex LK_BN_generate_prime_ex +BN_get_rfc3526_prime_1536 LK_BN_get_rfc3526_prime_1536 +BN_get_rfc3526_prime_2048 LK_BN_get_rfc3526_prime_2048 +BN_get_rfc3526_prime_3072 LK_BN_get_rfc3526_prime_3072 +BN_get_rfc3526_prime_4096 LK_BN_get_rfc3526_prime_4096 +BN_get_rfc3526_prime_6144 LK_BN_get_rfc3526_prime_6144 +BN_get_rfc3526_prime_8192 LK_BN_get_rfc3526_prime_8192 +BN_get_u64 LK_BN_get_u64 +BN_get_word LK_BN_get_word +BN_hex2bn LK_BN_hex2bn +BN_init LK_BN_init +BN_is_bit_set LK_BN_is_bit_set +BN_is_negative LK_BN_is_negative +BN_is_odd LK_BN_is_odd +BN_is_one LK_BN_is_one +BN_is_pow2 LK_BN_is_pow2 +BN_is_prime_ex LK_BN_is_prime_ex +BN_is_prime_fasttest_ex LK_BN_is_prime_fasttest_ex +BN_is_word LK_BN_is_word +BN_is_zero LK_BN_is_zero +BN_le2bn LK_BN_le2bn +BN_lshift LK_BN_lshift +BN_lshift1 LK_BN_lshift1 +BN_marshal_asn1 LK_BN_marshal_asn1 +BN_mask_bits LK_BN_mask_bits +BN_mod_add LK_BN_mod_add +BN_mod_add_quick LK_BN_mod_add_quick +BN_mod_exp LK_BN_mod_exp +BN_mod_exp2_mont LK_BN_mod_exp2_mont +BN_mod_exp_mont LK_BN_mod_exp_mont +BN_mod_exp_mont_consttime LK_BN_mod_exp_mont_consttime +BN_mod_exp_mont_word LK_BN_mod_exp_mont_word +BN_mod_inverse LK_BN_mod_inverse +BN_mod_inverse_blinded LK_BN_mod_inverse_blinded +BN_mod_lshift LK_BN_mod_lshift +BN_mod_lshift1 LK_BN_mod_lshift1 +BN_mod_lshift1_quick LK_BN_mod_lshift1_quick +BN_mod_lshift_quick LK_BN_mod_lshift_quick +BN_mod_mul LK_BN_mod_mul +BN_mod_mul_montgomery LK_BN_mod_mul_montgomery +BN_mod_pow2 LK_BN_mod_pow2 +BN_mod_sqr LK_BN_mod_sqr +BN_mod_sqrt LK_BN_mod_sqrt +BN_mod_sub LK_BN_mod_sub +BN_mod_sub_quick LK_BN_mod_sub_quick +BN_mod_word LK_BN_mod_word +BN_mpi2bn LK_BN_mpi2bn +BN_mul LK_BN_mul +BN_mul_word LK_BN_mul_word +BN_new LK_BN_new +BN_nnmod LK_BN_nnmod +BN_nnmod_pow2 LK_BN_nnmod_pow2 +BN_num_bits LK_BN_num_bits +BN_num_bits_word LK_BN_num_bits_word +BN_num_bytes LK_BN_num_bytes +BN_one LK_BN_one +BN_parse_asn1_unsigned LK_BN_parse_asn1_unsigned +BN_primality_test LK_BN_primality_test +BN_print LK_BN_print +BN_print_fp LK_BN_print_fp +BN_pseudo_rand LK_BN_pseudo_rand +BN_pseudo_rand_range LK_BN_pseudo_rand_range +BN_rand LK_BN_rand +BN_rand_range LK_BN_rand_range +BN_rand_range_ex LK_BN_rand_range_ex +BN_rshift LK_BN_rshift +BN_rshift1 LK_BN_rshift1 +BN_secure_new LK_BN_secure_new +BN_set_bit LK_BN_set_bit +BN_set_negative LK_BN_set_negative +BN_set_u64 LK_BN_set_u64 +BN_set_word LK_BN_set_word +BN_sqr LK_BN_sqr +BN_sqrt LK_BN_sqrt +BN_sub LK_BN_sub +BN_sub_word LK_BN_sub_word +BN_to_ASN1_ENUMERATED LK_BN_to_ASN1_ENUMERATED +BN_to_ASN1_INTEGER LK_BN_to_ASN1_INTEGER +BN_to_montgomery LK_BN_to_montgomery +BN_uadd LK_BN_uadd +BN_ucmp LK_BN_ucmp +BN_usub LK_BN_usub +BN_value_one LK_BN_value_one +BN_zero LK_BN_zero +BORINGSSL_integrity_test LK_BORINGSSL_integrity_test +BORINGSSL_keccak LK_BORINGSSL_keccak +BORINGSSL_keccak_init LK_BORINGSSL_keccak_init +BORINGSSL_keccak_squeeze LK_BORINGSSL_keccak_squeeze +BORINGSSL_self_test LK_BORINGSSL_self_test +BUF_MEM_append LK_BUF_MEM_append +BUF_MEM_free LK_BUF_MEM_free +BUF_MEM_grow LK_BUF_MEM_grow +BUF_MEM_grow_clean LK_BUF_MEM_grow_clean +BUF_MEM_new LK_BUF_MEM_new +BUF_MEM_reserve LK_BUF_MEM_reserve +BUF_memdup LK_BUF_memdup +BUF_strdup LK_BUF_strdup +BUF_strlcat LK_BUF_strlcat +BUF_strlcpy LK_BUF_strlcpy +BUF_strndup LK_BUF_strndup +BUF_strnlen LK_BUF_strnlen +CAST_cbc_encrypt LK_CAST_cbc_encrypt +CAST_cfb64_encrypt LK_CAST_cfb64_encrypt +CAST_decrypt LK_CAST_decrypt +CAST_ecb_encrypt LK_CAST_ecb_encrypt +CAST_encrypt LK_CAST_encrypt +CAST_set_key LK_CAST_set_key +CBBFinishArray LK_CBBFinishArray +CBB_add_asn1 LK_CBB_add_asn1 +CBB_add_asn1_bool LK_CBB_add_asn1_bool +CBB_add_asn1_int64 LK_CBB_add_asn1_int64 +CBB_add_asn1_int64_with_tag LK_CBB_add_asn1_int64_with_tag +CBB_add_asn1_octet_string LK_CBB_add_asn1_octet_string +CBB_add_asn1_oid_from_text LK_CBB_add_asn1_oid_from_text +CBB_add_asn1_uint64 LK_CBB_add_asn1_uint64 +CBB_add_asn1_uint64_with_tag LK_CBB_add_asn1_uint64_with_tag +CBB_add_bytes LK_CBB_add_bytes +CBB_add_space LK_CBB_add_space +CBB_add_u16 LK_CBB_add_u16 +CBB_add_u16_length_prefixed LK_CBB_add_u16_length_prefixed +CBB_add_u16le LK_CBB_add_u16le +CBB_add_u24 LK_CBB_add_u24 +CBB_add_u24_length_prefixed LK_CBB_add_u24_length_prefixed +CBB_add_u32 LK_CBB_add_u32 +CBB_add_u32le LK_CBB_add_u32le +CBB_add_u64 LK_CBB_add_u64 +CBB_add_u64le LK_CBB_add_u64le +CBB_add_u8 LK_CBB_add_u8 +CBB_add_u8_length_prefixed LK_CBB_add_u8_length_prefixed +CBB_add_zeros LK_CBB_add_zeros +CBB_cleanup LK_CBB_cleanup +CBB_data LK_CBB_data +CBB_did_write LK_CBB_did_write +CBB_discard_child LK_CBB_discard_child +CBB_finish LK_CBB_finish +CBB_flush LK_CBB_flush +CBB_flush_asn1_set_of LK_CBB_flush_asn1_set_of +CBB_init LK_CBB_init +CBB_init_fixed LK_CBB_init_fixed +CBB_len LK_CBB_len +CBB_reserve LK_CBB_reserve +CBB_zero LK_CBB_zero +CBS_asn1_ber_to_der LK_CBS_asn1_ber_to_der +CBS_asn1_bitstring_has_bit LK_CBS_asn1_bitstring_has_bit +CBS_asn1_oid_to_text LK_CBS_asn1_oid_to_text +CBS_contains_zero_byte LK_CBS_contains_zero_byte +CBS_copy_bytes LK_CBS_copy_bytes +CBS_data LK_CBS_data +CBS_get_any_asn1 LK_CBS_get_any_asn1 +CBS_get_any_asn1_element LK_CBS_get_any_asn1_element +CBS_get_any_ber_asn1_element LK_CBS_get_any_ber_asn1_element +CBS_get_asn1 LK_CBS_get_asn1 +CBS_get_asn1_bool LK_CBS_get_asn1_bool +CBS_get_asn1_element LK_CBS_get_asn1_element +CBS_get_asn1_implicit_string LK_CBS_get_asn1_implicit_string +CBS_get_asn1_int64 LK_CBS_get_asn1_int64 +CBS_get_asn1_uint64 LK_CBS_get_asn1_uint64 +CBS_get_bytes LK_CBS_get_bytes +CBS_get_last_u8 LK_CBS_get_last_u8 +CBS_get_optional_asn1 LK_CBS_get_optional_asn1 +CBS_get_optional_asn1_bool LK_CBS_get_optional_asn1_bool +CBS_get_optional_asn1_octet_string LK_CBS_get_optional_asn1_octet_string +CBS_get_optional_asn1_uint64 LK_CBS_get_optional_asn1_uint64 +CBS_get_u16 LK_CBS_get_u16 +CBS_get_u16_length_prefixed LK_CBS_get_u16_length_prefixed +CBS_get_u16le LK_CBS_get_u16le +CBS_get_u24 LK_CBS_get_u24 +CBS_get_u24_length_prefixed LK_CBS_get_u24_length_prefixed +CBS_get_u32 LK_CBS_get_u32 +CBS_get_u32le LK_CBS_get_u32le +CBS_get_u64 LK_CBS_get_u64 +CBS_get_u64_decimal LK_CBS_get_u64_decimal +CBS_get_u64le LK_CBS_get_u64le +CBS_get_u8 LK_CBS_get_u8 +CBS_get_u8_length_prefixed LK_CBS_get_u8_length_prefixed +CBS_get_until_first LK_CBS_get_until_first +CBS_init LK_CBS_init +CBS_is_unsigned_asn1_integer LK_CBS_is_unsigned_asn1_integer +CBS_is_valid_asn1_bitstring LK_CBS_is_valid_asn1_bitstring +CBS_is_valid_asn1_integer LK_CBS_is_valid_asn1_integer +CBS_is_valid_asn1_oid LK_CBS_is_valid_asn1_oid +CBS_len LK_CBS_len +CBS_mem_equal LK_CBS_mem_equal +CBS_parse_generalized_time LK_CBS_parse_generalized_time +CBS_parse_utc_time LK_CBS_parse_utc_time +CBS_peek_asn1_tag LK_CBS_peek_asn1_tag +CBS_skip LK_CBS_skip +CBS_stow LK_CBS_stow +CBS_strdup LK_CBS_strdup +CMAC_CTX_copy LK_CMAC_CTX_copy +CMAC_CTX_free LK_CMAC_CTX_free +CMAC_CTX_new LK_CMAC_CTX_new +CMAC_Final LK_CMAC_Final +CMAC_Init LK_CMAC_Init +CMAC_Reset LK_CMAC_Reset +CMAC_Update LK_CMAC_Update +CONF_modules_free LK_CONF_modules_free +CONF_modules_load_file LK_CONF_modules_load_file +CONF_parse_list LK_CONF_parse_list +CRYPTO_BUFFER_POOL_free LK_CRYPTO_BUFFER_POOL_free +CRYPTO_BUFFER_POOL_new LK_CRYPTO_BUFFER_POOL_new +CRYPTO_BUFFER_alloc LK_CRYPTO_BUFFER_alloc +CRYPTO_BUFFER_data LK_CRYPTO_BUFFER_data +CRYPTO_BUFFER_free LK_CRYPTO_BUFFER_free +CRYPTO_BUFFER_init_CBS LK_CRYPTO_BUFFER_init_CBS +CRYPTO_BUFFER_len LK_CRYPTO_BUFFER_len +CRYPTO_BUFFER_new LK_CRYPTO_BUFFER_new +CRYPTO_BUFFER_new_from_CBS LK_CRYPTO_BUFFER_new_from_CBS +CRYPTO_BUFFER_new_from_static_data_unsafe LK_CRYPTO_BUFFER_new_from_static_data_unsafe +CRYPTO_BUFFER_up_ref LK_CRYPTO_BUFFER_up_ref +CRYPTO_MUTEX_cleanup LK_CRYPTO_MUTEX_cleanup +CRYPTO_MUTEX_init LK_CRYPTO_MUTEX_init +CRYPTO_MUTEX_lock_read LK_CRYPTO_MUTEX_lock_read +CRYPTO_MUTEX_lock_write LK_CRYPTO_MUTEX_lock_write +CRYPTO_MUTEX_unlock_read LK_CRYPTO_MUTEX_unlock_read +CRYPTO_MUTEX_unlock_write LK_CRYPTO_MUTEX_unlock_write +CRYPTO_STATIC_MUTEX_lock_read LK_CRYPTO_STATIC_MUTEX_lock_read +CRYPTO_STATIC_MUTEX_lock_write LK_CRYPTO_STATIC_MUTEX_lock_write +CRYPTO_STATIC_MUTEX_unlock_read LK_CRYPTO_STATIC_MUTEX_unlock_read +CRYPTO_STATIC_MUTEX_unlock_write LK_CRYPTO_STATIC_MUTEX_unlock_write +CRYPTO_THREADID_current LK_CRYPTO_THREADID_current +CRYPTO_THREADID_set_callback LK_CRYPTO_THREADID_set_callback +CRYPTO_THREADID_set_numeric LK_CRYPTO_THREADID_set_numeric +CRYPTO_THREADID_set_pointer LK_CRYPTO_THREADID_set_pointer +CRYPTO_chacha_20 LK_CRYPTO_chacha_20 +CRYPTO_cleanup_all_ex_data LK_CRYPTO_cleanup_all_ex_data +CRYPTO_fork_detect_force_madv_wipeonfork_for_testing LK_CRYPTO_fork_detect_force_madv_wipeonfork_for_testing +CRYPTO_free LK_CRYPTO_free +CRYPTO_free_ex_data LK_CRYPTO_free_ex_data +CRYPTO_gcm128_aad LK_CRYPTO_gcm128_aad +CRYPTO_gcm128_decrypt LK_CRYPTO_gcm128_decrypt +CRYPTO_gcm128_decrypt_ctr32 LK_CRYPTO_gcm128_decrypt_ctr32 +CRYPTO_gcm128_encrypt LK_CRYPTO_gcm128_encrypt +CRYPTO_gcm128_encrypt_ctr32 LK_CRYPTO_gcm128_encrypt_ctr32 +CRYPTO_gcm128_finish LK_CRYPTO_gcm128_finish +CRYPTO_gcm128_init_key LK_CRYPTO_gcm128_init_key +CRYPTO_gcm128_setiv LK_CRYPTO_gcm128_setiv +CRYPTO_gcm128_tag LK_CRYPTO_gcm128_tag +CRYPTO_get_ex_data LK_CRYPTO_get_ex_data +CRYPTO_get_ex_new_index LK_CRYPTO_get_ex_new_index +CRYPTO_get_fork_generation LK_CRYPTO_get_fork_generation +CRYPTO_get_lock_name LK_CRYPTO_get_lock_name +CRYPTO_get_thread_local LK_CRYPTO_get_thread_local +CRYPTO_has_asm LK_CRYPTO_has_asm +CRYPTO_has_broken_NEON LK_CRYPTO_has_broken_NEON +CRYPTO_is_NEON_capable_at_runtime LK_CRYPTO_is_NEON_capable_at_runtime +CRYPTO_is_confidential_build LK_CRYPTO_is_confidential_build +CRYPTO_library_init LK_CRYPTO_library_init +CRYPTO_malloc LK_CRYPTO_malloc +CRYPTO_malloc_init LK_CRYPTO_malloc_init +CRYPTO_memcmp LK_CRYPTO_memcmp +CRYPTO_needs_hwcap2_workaround LK_CRYPTO_needs_hwcap2_workaround +CRYPTO_new_ex_data LK_CRYPTO_new_ex_data +CRYPTO_num_locks LK_CRYPTO_num_locks +CRYPTO_once LK_CRYPTO_once +CRYPTO_poly1305_finish LK_CRYPTO_poly1305_finish +CRYPTO_poly1305_init LK_CRYPTO_poly1305_init +CRYPTO_poly1305_update LK_CRYPTO_poly1305_update +CRYPTO_pre_sandbox_init LK_CRYPTO_pre_sandbox_init +CRYPTO_realloc LK_CRYPTO_realloc +CRYPTO_refcount_dec_and_test_zero LK_CRYPTO_refcount_dec_and_test_zero +CRYPTO_refcount_inc LK_CRYPTO_refcount_inc +CRYPTO_secure_malloc_init LK_CRYPTO_secure_malloc_init +CRYPTO_secure_malloc_initialized LK_CRYPTO_secure_malloc_initialized +CRYPTO_secure_used LK_CRYPTO_secure_used +CRYPTO_set_add_lock_callback LK_CRYPTO_set_add_lock_callback +CRYPTO_set_dynlock_create_callback LK_CRYPTO_set_dynlock_create_callback +CRYPTO_set_dynlock_destroy_callback LK_CRYPTO_set_dynlock_destroy_callback +CRYPTO_set_dynlock_lock_callback LK_CRYPTO_set_dynlock_lock_callback +CRYPTO_set_ex_data LK_CRYPTO_set_ex_data +CRYPTO_set_id_callback LK_CRYPTO_set_id_callback +CRYPTO_set_locking_callback LK_CRYPTO_set_locking_callback +CRYPTO_set_thread_local LK_CRYPTO_set_thread_local +CRYPTO_tls13_hkdf_expand_label LK_CRYPTO_tls13_hkdf_expand_label +CRYPTO_tls1_prf LK_CRYPTO_tls1_prf +CTR_DRBG_clear LK_CTR_DRBG_clear +CTR_DRBG_free LK_CTR_DRBG_free +CTR_DRBG_generate LK_CTR_DRBG_generate +CTR_DRBG_init LK_CTR_DRBG_init +CTR_DRBG_new LK_CTR_DRBG_new +CTR_DRBG_reseed LK_CTR_DRBG_reseed +DES_decrypt3 LK_DES_decrypt3 +DES_ecb3_encrypt LK_DES_ecb3_encrypt +DES_ecb_encrypt LK_DES_ecb_encrypt +DES_ede2_cbc_encrypt LK_DES_ede2_cbc_encrypt +DES_ede3_cbc_encrypt LK_DES_ede3_cbc_encrypt +DES_ede3_cfb64_encrypt LK_DES_ede3_cfb64_encrypt +DES_ede3_cfb_encrypt LK_DES_ede3_cfb_encrypt +DES_encrypt3 LK_DES_encrypt3 +DES_ncbc_encrypt LK_DES_ncbc_encrypt +DES_set_key LK_DES_set_key +DES_set_key_unchecked LK_DES_set_key_unchecked +DES_set_odd_parity LK_DES_set_odd_parity +DH_bits LK_DH_bits +DH_check LK_DH_check +DH_check_pub_key LK_DH_check_pub_key +DH_compute_key LK_DH_compute_key +DH_compute_key_hashed LK_DH_compute_key_hashed +DH_compute_key_padded LK_DH_compute_key_padded +DH_free LK_DH_free +DH_generate_key LK_DH_generate_key +DH_generate_parameters LK_DH_generate_parameters +DH_generate_parameters_ex LK_DH_generate_parameters_ex +DH_get0_g LK_DH_get0_g +DH_get0_key LK_DH_get0_key +DH_get0_p LK_DH_get0_p +DH_get0_pqg LK_DH_get0_pqg +DH_get0_priv_key LK_DH_get0_priv_key +DH_get0_pub_key LK_DH_get0_pub_key +DH_get0_q LK_DH_get0_q +DH_get_rfc7919_2048 LK_DH_get_rfc7919_2048 +DH_marshal_parameters LK_DH_marshal_parameters +DH_new LK_DH_new +DH_num_bits LK_DH_num_bits +DH_parse_parameters LK_DH_parse_parameters +DH_set0_key LK_DH_set0_key +DH_set0_pqg LK_DH_set0_pqg +DH_set_length LK_DH_set_length +DH_size LK_DH_size +DH_up_ref LK_DH_up_ref +DHparams_dup LK_DHparams_dup +DIRECTORYSTRING_free LK_DIRECTORYSTRING_free +DIRECTORYSTRING_new LK_DIRECTORYSTRING_new +DISPLAYTEXT_free LK_DISPLAYTEXT_free +DISPLAYTEXT_new LK_DISPLAYTEXT_new +DIST_POINT_set_dpname LK_DIST_POINT_set_dpname +DSA_SIG_free LK_DSA_SIG_free +DSA_SIG_get0 LK_DSA_SIG_get0 +DSA_SIG_marshal LK_DSA_SIG_marshal +DSA_SIG_new LK_DSA_SIG_new +DSA_SIG_parse LK_DSA_SIG_parse +DSA_SIG_set0 LK_DSA_SIG_set0 +DSA_bits LK_DSA_bits +DSA_check_signature LK_DSA_check_signature +DSA_do_check_signature LK_DSA_do_check_signature +DSA_do_sign LK_DSA_do_sign +DSA_do_verify LK_DSA_do_verify +DSA_dup_DH LK_DSA_dup_DH +DSA_free LK_DSA_free +DSA_generate_key LK_DSA_generate_key +DSA_generate_parameters LK_DSA_generate_parameters +DSA_generate_parameters_ex LK_DSA_generate_parameters_ex +DSA_get0_g LK_DSA_get0_g +DSA_get0_key LK_DSA_get0_key +DSA_get0_p LK_DSA_get0_p +DSA_get0_pqg LK_DSA_get0_pqg +DSA_get0_priv_key LK_DSA_get0_priv_key +DSA_get0_pub_key LK_DSA_get0_pub_key +DSA_get0_q LK_DSA_get0_q +DSA_get_ex_data LK_DSA_get_ex_data +DSA_get_ex_new_index LK_DSA_get_ex_new_index +DSA_marshal_parameters LK_DSA_marshal_parameters +DSA_marshal_private_key LK_DSA_marshal_private_key +DSA_marshal_public_key LK_DSA_marshal_public_key +DSA_new LK_DSA_new +DSA_parse_parameters LK_DSA_parse_parameters +DSA_parse_private_key LK_DSA_parse_private_key +DSA_parse_public_key LK_DSA_parse_public_key +DSA_set0_key LK_DSA_set0_key +DSA_set0_pqg LK_DSA_set0_pqg +DSA_set_ex_data LK_DSA_set_ex_data +DSA_sign LK_DSA_sign +DSA_size LK_DSA_size +DSA_up_ref LK_DSA_up_ref +DSA_verify LK_DSA_verify +DSAparams_dup LK_DSAparams_dup +DTLS_client_method LK_DTLS_client_method +DTLS_method LK_DTLS_method +DTLS_server_method LK_DTLS_server_method +DTLS_with_buffers_method LK_DTLS_with_buffers_method +DTLSv1_2_client_method LK_DTLSv1_2_client_method +DTLSv1_2_method LK_DTLSv1_2_method +DTLSv1_2_server_method LK_DTLSv1_2_server_method +DTLSv1_client_method LK_DTLSv1_client_method +DTLSv1_get_timeout LK_DTLSv1_get_timeout +DTLSv1_handle_timeout LK_DTLSv1_handle_timeout +DTLSv1_method LK_DTLSv1_method +DTLSv1_server_method LK_DTLSv1_server_method +DTLSv1_set_initial_timeout_duration LK_DTLSv1_set_initial_timeout_duration +ECDH_compute_key LK_ECDH_compute_key +ECDH_compute_key_fips LK_ECDH_compute_key_fips +ECDSA_SIG_free LK_ECDSA_SIG_free +ECDSA_SIG_from_bytes LK_ECDSA_SIG_from_bytes +ECDSA_SIG_get0 LK_ECDSA_SIG_get0 +ECDSA_SIG_get0_r LK_ECDSA_SIG_get0_r +ECDSA_SIG_get0_s LK_ECDSA_SIG_get0_s +ECDSA_SIG_marshal LK_ECDSA_SIG_marshal +ECDSA_SIG_max_len LK_ECDSA_SIG_max_len +ECDSA_SIG_new LK_ECDSA_SIG_new +ECDSA_SIG_parse LK_ECDSA_SIG_parse +ECDSA_SIG_set0 LK_ECDSA_SIG_set0 +ECDSA_SIG_to_bytes LK_ECDSA_SIG_to_bytes +ECDSA_do_sign LK_ECDSA_do_sign +ECDSA_do_verify LK_ECDSA_do_verify +ECDSA_sign LK_ECDSA_sign +ECDSA_sign_with_nonce_and_leak_private_key_for_testing LK_ECDSA_sign_with_nonce_and_leak_private_key_for_testing +ECDSA_size LK_ECDSA_size +ECDSA_verify LK_ECDSA_verify +EC_GROUP_cmp LK_EC_GROUP_cmp +EC_GROUP_dup LK_EC_GROUP_dup +EC_GROUP_free LK_EC_GROUP_free +EC_GROUP_get0_generator LK_EC_GROUP_get0_generator +EC_GROUP_get0_order LK_EC_GROUP_get0_order +EC_GROUP_get_asn1_flag LK_EC_GROUP_get_asn1_flag +EC_GROUP_get_cofactor LK_EC_GROUP_get_cofactor +EC_GROUP_get_curve_GFp LK_EC_GROUP_get_curve_GFp +EC_GROUP_get_curve_name LK_EC_GROUP_get_curve_name +EC_GROUP_get_degree LK_EC_GROUP_get_degree +EC_GROUP_get_order LK_EC_GROUP_get_order +EC_GROUP_method_of LK_EC_GROUP_method_of +EC_GROUP_new_by_curve_name LK_EC_GROUP_new_by_curve_name +EC_GROUP_new_curve_GFp LK_EC_GROUP_new_curve_GFp +EC_GROUP_order_bits LK_EC_GROUP_order_bits +EC_GROUP_set_asn1_flag LK_EC_GROUP_set_asn1_flag +EC_GROUP_set_generator LK_EC_GROUP_set_generator +EC_GROUP_set_point_conversion_form LK_EC_GROUP_set_point_conversion_form +EC_KEY_check_fips LK_EC_KEY_check_fips +EC_KEY_check_key LK_EC_KEY_check_key +EC_KEY_derive_from_secret LK_EC_KEY_derive_from_secret +EC_KEY_dup LK_EC_KEY_dup +EC_KEY_free LK_EC_KEY_free +EC_KEY_generate_key LK_EC_KEY_generate_key +EC_KEY_generate_key_fips LK_EC_KEY_generate_key_fips +EC_KEY_get0_group LK_EC_KEY_get0_group +EC_KEY_get0_private_key LK_EC_KEY_get0_private_key +EC_KEY_get0_public_key LK_EC_KEY_get0_public_key +EC_KEY_get_conv_form LK_EC_KEY_get_conv_form +EC_KEY_get_enc_flags LK_EC_KEY_get_enc_flags +EC_KEY_get_ex_data LK_EC_KEY_get_ex_data +EC_KEY_get_ex_new_index LK_EC_KEY_get_ex_new_index +EC_KEY_is_opaque LK_EC_KEY_is_opaque +EC_KEY_key2buf LK_EC_KEY_key2buf +EC_KEY_marshal_curve_name LK_EC_KEY_marshal_curve_name +EC_KEY_marshal_private_key LK_EC_KEY_marshal_private_key +EC_KEY_new LK_EC_KEY_new +EC_KEY_new_by_curve_name LK_EC_KEY_new_by_curve_name +EC_KEY_new_method LK_EC_KEY_new_method +EC_KEY_oct2key LK_EC_KEY_oct2key +EC_KEY_oct2priv LK_EC_KEY_oct2priv +EC_KEY_parse_curve_name LK_EC_KEY_parse_curve_name +EC_KEY_parse_parameters LK_EC_KEY_parse_parameters +EC_KEY_parse_private_key LK_EC_KEY_parse_private_key +EC_KEY_priv2buf LK_EC_KEY_priv2buf +EC_KEY_priv2oct LK_EC_KEY_priv2oct +EC_KEY_set_asn1_flag LK_EC_KEY_set_asn1_flag +EC_KEY_set_conv_form LK_EC_KEY_set_conv_form +EC_KEY_set_enc_flags LK_EC_KEY_set_enc_flags +EC_KEY_set_ex_data LK_EC_KEY_set_ex_data +EC_KEY_set_group LK_EC_KEY_set_group +EC_KEY_set_private_key LK_EC_KEY_set_private_key +EC_KEY_set_public_key LK_EC_KEY_set_public_key +EC_KEY_set_public_key_affine_coordinates LK_EC_KEY_set_public_key_affine_coordinates +EC_KEY_up_ref LK_EC_KEY_up_ref +EC_METHOD_get_field_type LK_EC_METHOD_get_field_type +EC_POINT_add LK_EC_POINT_add +EC_POINT_clear_free LK_EC_POINT_clear_free +EC_POINT_cmp LK_EC_POINT_cmp +EC_POINT_copy LK_EC_POINT_copy +EC_POINT_dbl LK_EC_POINT_dbl +EC_POINT_dup LK_EC_POINT_dup +EC_POINT_free LK_EC_POINT_free +EC_POINT_get_affine_coordinates LK_EC_POINT_get_affine_coordinates +EC_POINT_get_affine_coordinates_GFp LK_EC_POINT_get_affine_coordinates_GFp +EC_POINT_invert LK_EC_POINT_invert +EC_POINT_is_at_infinity LK_EC_POINT_is_at_infinity +EC_POINT_is_on_curve LK_EC_POINT_is_on_curve +EC_POINT_mul LK_EC_POINT_mul +EC_POINT_new LK_EC_POINT_new +EC_POINT_oct2point LK_EC_POINT_oct2point +EC_POINT_point2buf LK_EC_POINT_point2buf +EC_POINT_point2cbb LK_EC_POINT_point2cbb +EC_POINT_point2oct LK_EC_POINT_point2oct +EC_POINT_set_affine_coordinates LK_EC_POINT_set_affine_coordinates +EC_POINT_set_affine_coordinates_GFp LK_EC_POINT_set_affine_coordinates_GFp +EC_POINT_set_compressed_coordinates_GFp LK_EC_POINT_set_compressed_coordinates_GFp +EC_POINT_set_to_infinity LK_EC_POINT_set_to_infinity +EC_curve_nid2nist LK_EC_curve_nid2nist +EC_curve_nist2nid LK_EC_curve_nist2nid +EC_get_builtin_curves LK_EC_get_builtin_curves +EC_hash_to_curve_p256_xmd_sha256_sswu LK_EC_hash_to_curve_p256_xmd_sha256_sswu +EC_hash_to_curve_p384_xmd_sha384_sswu LK_EC_hash_to_curve_p384_xmd_sha384_sswu +ED25519_keypair LK_ED25519_keypair +ED25519_keypair_from_seed LK_ED25519_keypair_from_seed +ED25519_sign LK_ED25519_sign +ED25519_verify LK_ED25519_verify +ENGINE_free LK_ENGINE_free +ENGINE_get_ECDSA_method LK_ENGINE_get_ECDSA_method +ENGINE_get_RSA_method LK_ENGINE_get_RSA_method +ENGINE_load_builtin_engines LK_ENGINE_load_builtin_engines +ENGINE_new LK_ENGINE_new +ENGINE_register_all_complete LK_ENGINE_register_all_complete +ENGINE_set_ECDSA_method LK_ENGINE_set_ECDSA_method +ENGINE_set_RSA_method LK_ENGINE_set_RSA_method +ERR_SAVE_STATE_free LK_ERR_SAVE_STATE_free +ERR_add_error_data LK_ERR_add_error_data +ERR_add_error_dataf LK_ERR_add_error_dataf +ERR_clear_error LK_ERR_clear_error +ERR_clear_system_error LK_ERR_clear_system_error +ERR_error_string LK_ERR_error_string +ERR_error_string_n LK_ERR_error_string_n +ERR_free_strings LK_ERR_free_strings +ERR_func_error_string LK_ERR_func_error_string +ERR_get_error LK_ERR_get_error +ERR_get_error_line LK_ERR_get_error_line +ERR_get_error_line_data LK_ERR_get_error_line_data +ERR_get_next_error_library LK_ERR_get_next_error_library +ERR_lib_error_string LK_ERR_lib_error_string +ERR_load_BIO_strings LK_ERR_load_BIO_strings +ERR_load_ERR_strings LK_ERR_load_ERR_strings +ERR_load_RAND_strings LK_ERR_load_RAND_strings +ERR_load_SSL_strings LK_ERR_load_SSL_strings +ERR_load_crypto_strings LK_ERR_load_crypto_strings +ERR_peek_error LK_ERR_peek_error +ERR_peek_error_line LK_ERR_peek_error_line +ERR_peek_error_line_data LK_ERR_peek_error_line_data +ERR_peek_last_error LK_ERR_peek_last_error +ERR_peek_last_error_line LK_ERR_peek_last_error_line +ERR_peek_last_error_line_data LK_ERR_peek_last_error_line_data +ERR_pop_to_mark LK_ERR_pop_to_mark +ERR_print_errors LK_ERR_print_errors +ERR_print_errors_cb LK_ERR_print_errors_cb +ERR_print_errors_fp LK_ERR_print_errors_fp +ERR_put_error LK_ERR_put_error +ERR_reason_error_string LK_ERR_reason_error_string +ERR_remove_state LK_ERR_remove_state +ERR_remove_thread_state LK_ERR_remove_thread_state +ERR_restore_state LK_ERR_restore_state +ERR_save_state LK_ERR_save_state +ERR_set_error_data LK_ERR_set_error_data +ERR_set_mark LK_ERR_set_mark +EVP_AEAD_CTX_aead LK_EVP_AEAD_CTX_aead +EVP_AEAD_CTX_cleanup LK_EVP_AEAD_CTX_cleanup +EVP_AEAD_CTX_free LK_EVP_AEAD_CTX_free +EVP_AEAD_CTX_get_iv LK_EVP_AEAD_CTX_get_iv +EVP_AEAD_CTX_init LK_EVP_AEAD_CTX_init +EVP_AEAD_CTX_init_with_direction LK_EVP_AEAD_CTX_init_with_direction +EVP_AEAD_CTX_new LK_EVP_AEAD_CTX_new +EVP_AEAD_CTX_open LK_EVP_AEAD_CTX_open +EVP_AEAD_CTX_open_gather LK_EVP_AEAD_CTX_open_gather +EVP_AEAD_CTX_seal LK_EVP_AEAD_CTX_seal +EVP_AEAD_CTX_seal_scatter LK_EVP_AEAD_CTX_seal_scatter +EVP_AEAD_CTX_tag_len LK_EVP_AEAD_CTX_tag_len +EVP_AEAD_CTX_zero LK_EVP_AEAD_CTX_zero +EVP_AEAD_key_length LK_EVP_AEAD_key_length +EVP_AEAD_max_overhead LK_EVP_AEAD_max_overhead +EVP_AEAD_max_tag_len LK_EVP_AEAD_max_tag_len +EVP_AEAD_nonce_length LK_EVP_AEAD_nonce_length +EVP_BytesToKey LK_EVP_BytesToKey +EVP_CIPHER_CTX_block_size LK_EVP_CIPHER_CTX_block_size +EVP_CIPHER_CTX_cipher LK_EVP_CIPHER_CTX_cipher +EVP_CIPHER_CTX_cleanup LK_EVP_CIPHER_CTX_cleanup +EVP_CIPHER_CTX_copy LK_EVP_CIPHER_CTX_copy +EVP_CIPHER_CTX_ctrl LK_EVP_CIPHER_CTX_ctrl +EVP_CIPHER_CTX_encrypting LK_EVP_CIPHER_CTX_encrypting +EVP_CIPHER_CTX_flags LK_EVP_CIPHER_CTX_flags +EVP_CIPHER_CTX_free LK_EVP_CIPHER_CTX_free +EVP_CIPHER_CTX_get_app_data LK_EVP_CIPHER_CTX_get_app_data +EVP_CIPHER_CTX_init LK_EVP_CIPHER_CTX_init +EVP_CIPHER_CTX_iv_length LK_EVP_CIPHER_CTX_iv_length +EVP_CIPHER_CTX_key_length LK_EVP_CIPHER_CTX_key_length +EVP_CIPHER_CTX_mode LK_EVP_CIPHER_CTX_mode +EVP_CIPHER_CTX_new LK_EVP_CIPHER_CTX_new +EVP_CIPHER_CTX_nid LK_EVP_CIPHER_CTX_nid +EVP_CIPHER_CTX_reset LK_EVP_CIPHER_CTX_reset +EVP_CIPHER_CTX_set_app_data LK_EVP_CIPHER_CTX_set_app_data +EVP_CIPHER_CTX_set_flags LK_EVP_CIPHER_CTX_set_flags +EVP_CIPHER_CTX_set_key_length LK_EVP_CIPHER_CTX_set_key_length +EVP_CIPHER_CTX_set_padding LK_EVP_CIPHER_CTX_set_padding +EVP_CIPHER_block_size LK_EVP_CIPHER_block_size +EVP_CIPHER_do_all_sorted LK_EVP_CIPHER_do_all_sorted +EVP_CIPHER_flags LK_EVP_CIPHER_flags +EVP_CIPHER_iv_length LK_EVP_CIPHER_iv_length +EVP_CIPHER_key_length LK_EVP_CIPHER_key_length +EVP_CIPHER_mode LK_EVP_CIPHER_mode +EVP_CIPHER_nid LK_EVP_CIPHER_nid +EVP_Cipher LK_EVP_Cipher +EVP_CipherFinal LK_EVP_CipherFinal +EVP_CipherFinal_ex LK_EVP_CipherFinal_ex +EVP_CipherInit LK_EVP_CipherInit +EVP_CipherInit_ex LK_EVP_CipherInit_ex +EVP_CipherUpdate LK_EVP_CipherUpdate +EVP_DecodeBase64 LK_EVP_DecodeBase64 +EVP_DecodeBlock LK_EVP_DecodeBlock +EVP_DecodeFinal LK_EVP_DecodeFinal +EVP_DecodeInit LK_EVP_DecodeInit +EVP_DecodeUpdate LK_EVP_DecodeUpdate +EVP_DecodedLength LK_EVP_DecodedLength +EVP_DecryptFinal LK_EVP_DecryptFinal +EVP_DecryptFinal_ex LK_EVP_DecryptFinal_ex +EVP_DecryptInit LK_EVP_DecryptInit +EVP_DecryptInit_ex LK_EVP_DecryptInit_ex +EVP_DecryptUpdate LK_EVP_DecryptUpdate +EVP_Digest LK_EVP_Digest +EVP_DigestFinal LK_EVP_DigestFinal +EVP_DigestFinalXOF LK_EVP_DigestFinalXOF +EVP_DigestFinal_ex LK_EVP_DigestFinal_ex +EVP_DigestInit LK_EVP_DigestInit +EVP_DigestInit_ex LK_EVP_DigestInit_ex +EVP_DigestSign LK_EVP_DigestSign +EVP_DigestSignFinal LK_EVP_DigestSignFinal +EVP_DigestSignInit LK_EVP_DigestSignInit +EVP_DigestSignUpdate LK_EVP_DigestSignUpdate +EVP_DigestUpdate LK_EVP_DigestUpdate +EVP_DigestVerify LK_EVP_DigestVerify +EVP_DigestVerifyFinal LK_EVP_DigestVerifyFinal +EVP_DigestVerifyInit LK_EVP_DigestVerifyInit +EVP_DigestVerifyUpdate LK_EVP_DigestVerifyUpdate +EVP_ENCODE_CTX_free LK_EVP_ENCODE_CTX_free +EVP_ENCODE_CTX_new LK_EVP_ENCODE_CTX_new +EVP_EncodeBlock LK_EVP_EncodeBlock +EVP_EncodeFinal LK_EVP_EncodeFinal +EVP_EncodeInit LK_EVP_EncodeInit +EVP_EncodeUpdate LK_EVP_EncodeUpdate +EVP_EncodedLength LK_EVP_EncodedLength +EVP_EncryptFinal LK_EVP_EncryptFinal +EVP_EncryptFinal_ex LK_EVP_EncryptFinal_ex +EVP_EncryptInit LK_EVP_EncryptInit +EVP_EncryptInit_ex LK_EVP_EncryptInit_ex +EVP_EncryptUpdate LK_EVP_EncryptUpdate +EVP_HPKE_AEAD_aead LK_EVP_HPKE_AEAD_aead +EVP_HPKE_AEAD_id LK_EVP_HPKE_AEAD_id +EVP_HPKE_CTX_aead LK_EVP_HPKE_CTX_aead +EVP_HPKE_CTX_cleanup LK_EVP_HPKE_CTX_cleanup +EVP_HPKE_CTX_export LK_EVP_HPKE_CTX_export +EVP_HPKE_CTX_free LK_EVP_HPKE_CTX_free +EVP_HPKE_CTX_kdf LK_EVP_HPKE_CTX_kdf +EVP_HPKE_CTX_kem LK_EVP_HPKE_CTX_kem +EVP_HPKE_CTX_max_overhead LK_EVP_HPKE_CTX_max_overhead +EVP_HPKE_CTX_new LK_EVP_HPKE_CTX_new +EVP_HPKE_CTX_open LK_EVP_HPKE_CTX_open +EVP_HPKE_CTX_seal LK_EVP_HPKE_CTX_seal +EVP_HPKE_CTX_setup_recipient LK_EVP_HPKE_CTX_setup_recipient +EVP_HPKE_CTX_setup_sender LK_EVP_HPKE_CTX_setup_sender +EVP_HPKE_CTX_setup_sender_with_seed_for_testing LK_EVP_HPKE_CTX_setup_sender_with_seed_for_testing +EVP_HPKE_CTX_zero LK_EVP_HPKE_CTX_zero +EVP_HPKE_KDF_hkdf_md LK_EVP_HPKE_KDF_hkdf_md +EVP_HPKE_KDF_id LK_EVP_HPKE_KDF_id +EVP_HPKE_KEM_enc_len LK_EVP_HPKE_KEM_enc_len +EVP_HPKE_KEM_id LK_EVP_HPKE_KEM_id +EVP_HPKE_KEM_private_key_len LK_EVP_HPKE_KEM_private_key_len +EVP_HPKE_KEM_public_key_len LK_EVP_HPKE_KEM_public_key_len +EVP_HPKE_KEY_cleanup LK_EVP_HPKE_KEY_cleanup +EVP_HPKE_KEY_copy LK_EVP_HPKE_KEY_copy +EVP_HPKE_KEY_free LK_EVP_HPKE_KEY_free +EVP_HPKE_KEY_generate LK_EVP_HPKE_KEY_generate +EVP_HPKE_KEY_init LK_EVP_HPKE_KEY_init +EVP_HPKE_KEY_kem LK_EVP_HPKE_KEY_kem +EVP_HPKE_KEY_new LK_EVP_HPKE_KEY_new +EVP_HPKE_KEY_private_key LK_EVP_HPKE_KEY_private_key +EVP_HPKE_KEY_public_key LK_EVP_HPKE_KEY_public_key +EVP_HPKE_KEY_zero LK_EVP_HPKE_KEY_zero +EVP_MD_CTX_block_size LK_EVP_MD_CTX_block_size +EVP_MD_CTX_cleanse LK_EVP_MD_CTX_cleanse +EVP_MD_CTX_cleanup LK_EVP_MD_CTX_cleanup +EVP_MD_CTX_copy LK_EVP_MD_CTX_copy +EVP_MD_CTX_copy_ex LK_EVP_MD_CTX_copy_ex +EVP_MD_CTX_create LK_EVP_MD_CTX_create +EVP_MD_CTX_destroy LK_EVP_MD_CTX_destroy +EVP_MD_CTX_free LK_EVP_MD_CTX_free +EVP_MD_CTX_init LK_EVP_MD_CTX_init +EVP_MD_CTX_md LK_EVP_MD_CTX_md +EVP_MD_CTX_move LK_EVP_MD_CTX_move +EVP_MD_CTX_new LK_EVP_MD_CTX_new +EVP_MD_CTX_reset LK_EVP_MD_CTX_reset +EVP_MD_CTX_set_flags LK_EVP_MD_CTX_set_flags +EVP_MD_CTX_size LK_EVP_MD_CTX_size +EVP_MD_CTX_type LK_EVP_MD_CTX_type +EVP_MD_block_size LK_EVP_MD_block_size +EVP_MD_do_all LK_EVP_MD_do_all +EVP_MD_do_all_sorted LK_EVP_MD_do_all_sorted +EVP_MD_flags LK_EVP_MD_flags +EVP_MD_meth_get_flags LK_EVP_MD_meth_get_flags +EVP_MD_nid LK_EVP_MD_nid +EVP_MD_size LK_EVP_MD_size +EVP_MD_type LK_EVP_MD_type +EVP_PBE_scrypt LK_EVP_PBE_scrypt +EVP_PKCS82PKEY LK_EVP_PKCS82PKEY +EVP_PKEY2PKCS8 LK_EVP_PKEY2PKCS8 +EVP_PKEY_CTX_add1_hkdf_info LK_EVP_PKEY_CTX_add1_hkdf_info +EVP_PKEY_CTX_ctrl LK_EVP_PKEY_CTX_ctrl +EVP_PKEY_CTX_dup LK_EVP_PKEY_CTX_dup +EVP_PKEY_CTX_free LK_EVP_PKEY_CTX_free +EVP_PKEY_CTX_get0_pkey LK_EVP_PKEY_CTX_get0_pkey +EVP_PKEY_CTX_get0_rsa_oaep_label LK_EVP_PKEY_CTX_get0_rsa_oaep_label +EVP_PKEY_CTX_get_rsa_mgf1_md LK_EVP_PKEY_CTX_get_rsa_mgf1_md +EVP_PKEY_CTX_get_rsa_oaep_md LK_EVP_PKEY_CTX_get_rsa_oaep_md +EVP_PKEY_CTX_get_rsa_padding LK_EVP_PKEY_CTX_get_rsa_padding +EVP_PKEY_CTX_get_rsa_pss_saltlen LK_EVP_PKEY_CTX_get_rsa_pss_saltlen +EVP_PKEY_CTX_get_signature_md LK_EVP_PKEY_CTX_get_signature_md +EVP_PKEY_CTX_hkdf_mode LK_EVP_PKEY_CTX_hkdf_mode +EVP_PKEY_CTX_new LK_EVP_PKEY_CTX_new +EVP_PKEY_CTX_new_id LK_EVP_PKEY_CTX_new_id +EVP_PKEY_CTX_set0_rsa_oaep_label LK_EVP_PKEY_CTX_set0_rsa_oaep_label +EVP_PKEY_CTX_set1_hkdf_key LK_EVP_PKEY_CTX_set1_hkdf_key +EVP_PKEY_CTX_set1_hkdf_salt LK_EVP_PKEY_CTX_set1_hkdf_salt +EVP_PKEY_CTX_set_dsa_paramgen_bits LK_EVP_PKEY_CTX_set_dsa_paramgen_bits +EVP_PKEY_CTX_set_dsa_paramgen_q_bits LK_EVP_PKEY_CTX_set_dsa_paramgen_q_bits +EVP_PKEY_CTX_set_ec_param_enc LK_EVP_PKEY_CTX_set_ec_param_enc +EVP_PKEY_CTX_set_ec_paramgen_curve_nid LK_EVP_PKEY_CTX_set_ec_paramgen_curve_nid +EVP_PKEY_CTX_set_hkdf_md LK_EVP_PKEY_CTX_set_hkdf_md +EVP_PKEY_CTX_set_rsa_keygen_bits LK_EVP_PKEY_CTX_set_rsa_keygen_bits +EVP_PKEY_CTX_set_rsa_keygen_pubexp LK_EVP_PKEY_CTX_set_rsa_keygen_pubexp +EVP_PKEY_CTX_set_rsa_mgf1_md LK_EVP_PKEY_CTX_set_rsa_mgf1_md +EVP_PKEY_CTX_set_rsa_oaep_md LK_EVP_PKEY_CTX_set_rsa_oaep_md +EVP_PKEY_CTX_set_rsa_padding LK_EVP_PKEY_CTX_set_rsa_padding +EVP_PKEY_CTX_set_rsa_pss_keygen_md LK_EVP_PKEY_CTX_set_rsa_pss_keygen_md +EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md LK_EVP_PKEY_CTX_set_rsa_pss_keygen_mgf1_md +EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen LK_EVP_PKEY_CTX_set_rsa_pss_keygen_saltlen +EVP_PKEY_CTX_set_rsa_pss_saltlen LK_EVP_PKEY_CTX_set_rsa_pss_saltlen +EVP_PKEY_CTX_set_signature_md LK_EVP_PKEY_CTX_set_signature_md +EVP_PKEY_assign LK_EVP_PKEY_assign +EVP_PKEY_assign_DSA LK_EVP_PKEY_assign_DSA +EVP_PKEY_assign_EC_KEY LK_EVP_PKEY_assign_EC_KEY +EVP_PKEY_assign_RSA LK_EVP_PKEY_assign_RSA +EVP_PKEY_base_id LK_EVP_PKEY_base_id +EVP_PKEY_bits LK_EVP_PKEY_bits +EVP_PKEY_cmp LK_EVP_PKEY_cmp +EVP_PKEY_cmp_parameters LK_EVP_PKEY_cmp_parameters +EVP_PKEY_copy_parameters LK_EVP_PKEY_copy_parameters +EVP_PKEY_decrypt LK_EVP_PKEY_decrypt +EVP_PKEY_decrypt_init LK_EVP_PKEY_decrypt_init +EVP_PKEY_derive LK_EVP_PKEY_derive +EVP_PKEY_derive_init LK_EVP_PKEY_derive_init +EVP_PKEY_derive_set_peer LK_EVP_PKEY_derive_set_peer +EVP_PKEY_encrypt LK_EVP_PKEY_encrypt +EVP_PKEY_encrypt_init LK_EVP_PKEY_encrypt_init +EVP_PKEY_free LK_EVP_PKEY_free +EVP_PKEY_get0 LK_EVP_PKEY_get0 +EVP_PKEY_get0_DH LK_EVP_PKEY_get0_DH +EVP_PKEY_get0_DSA LK_EVP_PKEY_get0_DSA +EVP_PKEY_get0_EC_KEY LK_EVP_PKEY_get0_EC_KEY +EVP_PKEY_get0_RSA LK_EVP_PKEY_get0_RSA +EVP_PKEY_get1_DH LK_EVP_PKEY_get1_DH +EVP_PKEY_get1_DSA LK_EVP_PKEY_get1_DSA +EVP_PKEY_get1_EC_KEY LK_EVP_PKEY_get1_EC_KEY +EVP_PKEY_get1_RSA LK_EVP_PKEY_get1_RSA +EVP_PKEY_get1_tls_encodedpoint LK_EVP_PKEY_get1_tls_encodedpoint +EVP_PKEY_get_raw_private_key LK_EVP_PKEY_get_raw_private_key +EVP_PKEY_get_raw_public_key LK_EVP_PKEY_get_raw_public_key +EVP_PKEY_id LK_EVP_PKEY_id +EVP_PKEY_is_opaque LK_EVP_PKEY_is_opaque +EVP_PKEY_keygen LK_EVP_PKEY_keygen +EVP_PKEY_keygen_init LK_EVP_PKEY_keygen_init +EVP_PKEY_missing_parameters LK_EVP_PKEY_missing_parameters +EVP_PKEY_new LK_EVP_PKEY_new +EVP_PKEY_new_raw_private_key LK_EVP_PKEY_new_raw_private_key +EVP_PKEY_new_raw_public_key LK_EVP_PKEY_new_raw_public_key +EVP_PKEY_paramgen LK_EVP_PKEY_paramgen +EVP_PKEY_paramgen_init LK_EVP_PKEY_paramgen_init +EVP_PKEY_print_params LK_EVP_PKEY_print_params +EVP_PKEY_print_private LK_EVP_PKEY_print_private +EVP_PKEY_print_public LK_EVP_PKEY_print_public +EVP_PKEY_set1_DSA LK_EVP_PKEY_set1_DSA +EVP_PKEY_set1_EC_KEY LK_EVP_PKEY_set1_EC_KEY +EVP_PKEY_set1_RSA LK_EVP_PKEY_set1_RSA +EVP_PKEY_set1_tls_encodedpoint LK_EVP_PKEY_set1_tls_encodedpoint +EVP_PKEY_set_type LK_EVP_PKEY_set_type +EVP_PKEY_sign LK_EVP_PKEY_sign +EVP_PKEY_sign_init LK_EVP_PKEY_sign_init +EVP_PKEY_size LK_EVP_PKEY_size +EVP_PKEY_type LK_EVP_PKEY_type +EVP_PKEY_up_ref LK_EVP_PKEY_up_ref +EVP_PKEY_verify LK_EVP_PKEY_verify +EVP_PKEY_verify_init LK_EVP_PKEY_verify_init +EVP_PKEY_verify_recover LK_EVP_PKEY_verify_recover +EVP_PKEY_verify_recover_init LK_EVP_PKEY_verify_recover_init +EVP_SignFinal LK_EVP_SignFinal +EVP_SignInit LK_EVP_SignInit +EVP_SignInit_ex LK_EVP_SignInit_ex +EVP_SignUpdate LK_EVP_SignUpdate +EVP_VerifyFinal LK_EVP_VerifyFinal +EVP_VerifyInit LK_EVP_VerifyInit +EVP_VerifyInit_ex LK_EVP_VerifyInit_ex +EVP_VerifyUpdate LK_EVP_VerifyUpdate +EVP_add_cipher_alias LK_EVP_add_cipher_alias +EVP_add_digest LK_EVP_add_digest +EVP_aead_aes_128_cbc_sha1_tls LK_EVP_aead_aes_128_cbc_sha1_tls +EVP_aead_aes_128_cbc_sha1_tls_implicit_iv LK_EVP_aead_aes_128_cbc_sha1_tls_implicit_iv +EVP_aead_aes_128_ccm_bluetooth LK_EVP_aead_aes_128_ccm_bluetooth +EVP_aead_aes_128_ccm_bluetooth_8 LK_EVP_aead_aes_128_ccm_bluetooth_8 +EVP_aead_aes_128_ccm_matter LK_EVP_aead_aes_128_ccm_matter +EVP_aead_aes_128_ctr_hmac_sha256 LK_EVP_aead_aes_128_ctr_hmac_sha256 +EVP_aead_aes_128_gcm LK_EVP_aead_aes_128_gcm +EVP_aead_aes_128_gcm_randnonce LK_EVP_aead_aes_128_gcm_randnonce +EVP_aead_aes_128_gcm_siv LK_EVP_aead_aes_128_gcm_siv +EVP_aead_aes_128_gcm_tls12 LK_EVP_aead_aes_128_gcm_tls12 +EVP_aead_aes_128_gcm_tls13 LK_EVP_aead_aes_128_gcm_tls13 +EVP_aead_aes_192_gcm LK_EVP_aead_aes_192_gcm +EVP_aead_aes_256_cbc_sha1_tls LK_EVP_aead_aes_256_cbc_sha1_tls +EVP_aead_aes_256_cbc_sha1_tls_implicit_iv LK_EVP_aead_aes_256_cbc_sha1_tls_implicit_iv +EVP_aead_aes_256_ctr_hmac_sha256 LK_EVP_aead_aes_256_ctr_hmac_sha256 +EVP_aead_aes_256_gcm LK_EVP_aead_aes_256_gcm +EVP_aead_aes_256_gcm_randnonce LK_EVP_aead_aes_256_gcm_randnonce +EVP_aead_aes_256_gcm_siv LK_EVP_aead_aes_256_gcm_siv +EVP_aead_aes_256_gcm_tls12 LK_EVP_aead_aes_256_gcm_tls12 +EVP_aead_aes_256_gcm_tls13 LK_EVP_aead_aes_256_gcm_tls13 +EVP_aead_chacha20_poly1305 LK_EVP_aead_chacha20_poly1305 +EVP_aead_des_ede3_cbc_sha1_tls LK_EVP_aead_des_ede3_cbc_sha1_tls +EVP_aead_des_ede3_cbc_sha1_tls_implicit_iv LK_EVP_aead_des_ede3_cbc_sha1_tls_implicit_iv +EVP_aead_null_sha1_tls LK_EVP_aead_null_sha1_tls +EVP_aead_xchacha20_poly1305 LK_EVP_aead_xchacha20_poly1305 +EVP_aes_128_cbc LK_EVP_aes_128_cbc +EVP_aes_128_cfb LK_EVP_aes_128_cfb +EVP_aes_128_cfb128 LK_EVP_aes_128_cfb128 +EVP_aes_128_ctr LK_EVP_aes_128_ctr +EVP_aes_128_ecb LK_EVP_aes_128_ecb +EVP_aes_128_gcm LK_EVP_aes_128_gcm +EVP_aes_128_ofb LK_EVP_aes_128_ofb +EVP_aes_192_cbc LK_EVP_aes_192_cbc +EVP_aes_192_cfb LK_EVP_aes_192_cfb +EVP_aes_192_cfb128 LK_EVP_aes_192_cfb128 +EVP_aes_192_ctr LK_EVP_aes_192_ctr +EVP_aes_192_ecb LK_EVP_aes_192_ecb +EVP_aes_192_gcm LK_EVP_aes_192_gcm +EVP_aes_192_ofb LK_EVP_aes_192_ofb +EVP_aes_256_cbc LK_EVP_aes_256_cbc +EVP_aes_256_cfb LK_EVP_aes_256_cfb +EVP_aes_256_cfb128 LK_EVP_aes_256_cfb128 +EVP_aes_256_ctr LK_EVP_aes_256_ctr +EVP_aes_256_ecb LK_EVP_aes_256_ecb +EVP_aes_256_gcm LK_EVP_aes_256_gcm +EVP_aes_256_ofb LK_EVP_aes_256_ofb +EVP_aes_256_xts LK_EVP_aes_256_xts +EVP_bf_cbc LK_EVP_bf_cbc +EVP_bf_cfb LK_EVP_bf_cfb +EVP_bf_ecb LK_EVP_bf_ecb +EVP_blake2b256 LK_EVP_blake2b256 +EVP_cast5_cbc LK_EVP_cast5_cbc +EVP_cast5_ecb LK_EVP_cast5_ecb +EVP_cleanup LK_EVP_cleanup +EVP_des_cbc LK_EVP_des_cbc +EVP_des_ecb LK_EVP_des_ecb +EVP_des_ede LK_EVP_des_ede +EVP_des_ede3 LK_EVP_des_ede3 +EVP_des_ede3_cbc LK_EVP_des_ede3_cbc +EVP_des_ede3_ecb LK_EVP_des_ede3_ecb +EVP_des_ede_cbc LK_EVP_des_ede_cbc +EVP_dss1 LK_EVP_dss1 +EVP_enc_null LK_EVP_enc_null +EVP_get_cipherbyname LK_EVP_get_cipherbyname +EVP_get_cipherbynid LK_EVP_get_cipherbynid +EVP_get_digestbyname LK_EVP_get_digestbyname +EVP_get_digestbynid LK_EVP_get_digestbynid +EVP_get_digestbyobj LK_EVP_get_digestbyobj +EVP_has_aes_hardware LK_EVP_has_aes_hardware +EVP_hpke_aes_128_gcm LK_EVP_hpke_aes_128_gcm +EVP_hpke_aes_256_gcm LK_EVP_hpke_aes_256_gcm +EVP_hpke_chacha20_poly1305 LK_EVP_hpke_chacha20_poly1305 +EVP_hpke_hkdf_sha256 LK_EVP_hpke_hkdf_sha256 +EVP_hpke_x25519_hkdf_sha256 LK_EVP_hpke_x25519_hkdf_sha256 +EVP_marshal_digest_algorithm LK_EVP_marshal_digest_algorithm +EVP_marshal_private_key LK_EVP_marshal_private_key +EVP_marshal_public_key LK_EVP_marshal_public_key +EVP_md4 LK_EVP_md4 +EVP_md5 LK_EVP_md5 +EVP_md5_sha1 LK_EVP_md5_sha1 +EVP_parse_digest_algorithm LK_EVP_parse_digest_algorithm +EVP_parse_private_key LK_EVP_parse_private_key +EVP_parse_public_key LK_EVP_parse_public_key +EVP_rc2_cbc LK_EVP_rc2_cbc +EVP_rc4 LK_EVP_rc4 +EVP_sha1 LK_EVP_sha1 +EVP_sha1_final_with_secret_suffix LK_EVP_sha1_final_with_secret_suffix +EVP_sha224 LK_EVP_sha224 +EVP_sha256 LK_EVP_sha256 +EVP_sha384 LK_EVP_sha384 +EVP_sha512 LK_EVP_sha512 +EVP_sha512_256 LK_EVP_sha512_256 +FIPS_mode LK_FIPS_mode +FIPS_mode_set LK_FIPS_mode_set +FIPS_module_name LK_FIPS_module_name +FIPS_query_algorithm_status LK_FIPS_query_algorithm_status +FIPS_read_counter LK_FIPS_read_counter +FIPS_service_indicator_after_call LK_FIPS_service_indicator_after_call +FIPS_service_indicator_before_call LK_FIPS_service_indicator_before_call +FIPS_version LK_FIPS_version +GENERAL_NAME_cmp LK_GENERAL_NAME_cmp +GENERAL_NAME_dup LK_GENERAL_NAME_dup +GENERAL_NAME_get0_otherName LK_GENERAL_NAME_get0_otherName +GENERAL_NAME_get0_value LK_GENERAL_NAME_get0_value +GENERAL_NAME_print LK_GENERAL_NAME_print +GENERAL_NAME_set0_othername LK_GENERAL_NAME_set0_othername +GENERAL_NAME_set0_value LK_GENERAL_NAME_set0_value +HKDF LK_HKDF +HKDF_expand LK_HKDF_expand +HKDF_extract LK_HKDF_extract +HMAC LK_HMAC +HMAC_CTX_cleanse LK_HMAC_CTX_cleanse +HMAC_CTX_cleanup LK_HMAC_CTX_cleanup +HMAC_CTX_copy LK_HMAC_CTX_copy +HMAC_CTX_copy_ex LK_HMAC_CTX_copy_ex +HMAC_CTX_free LK_HMAC_CTX_free +HMAC_CTX_get_md LK_HMAC_CTX_get_md +HMAC_CTX_init LK_HMAC_CTX_init +HMAC_CTX_new LK_HMAC_CTX_new +HMAC_CTX_reset LK_HMAC_CTX_reset +HMAC_Final LK_HMAC_Final +HMAC_Init LK_HMAC_Init +HMAC_Init_ex LK_HMAC_Init_ex +HMAC_Update LK_HMAC_Update +HMAC_size LK_HMAC_size +HRSS_decap LK_HRSS_decap +HRSS_encap LK_HRSS_encap +HRSS_generate_key LK_HRSS_generate_key +HRSS_marshal_public_key LK_HRSS_marshal_public_key +HRSS_parse_public_key LK_HRSS_parse_public_key +HRSS_poly3_invert LK_HRSS_poly3_invert +HRSS_poly3_mul LK_HRSS_poly3_mul +KYBER_decap LK_KYBER_decap +KYBER_encap LK_KYBER_encap +KYBER_encap_external_entropy LK_KYBER_encap_external_entropy +KYBER_generate_key LK_KYBER_generate_key +KYBER_generate_key_external_entropy LK_KYBER_generate_key_external_entropy +KYBER_marshal_private_key LK_KYBER_marshal_private_key +KYBER_marshal_public_key LK_KYBER_marshal_public_key +KYBER_parse_private_key LK_KYBER_parse_private_key +KYBER_parse_public_key LK_KYBER_parse_public_key +KYBER_public_from_private LK_KYBER_public_from_private +MD4 LK_MD4 +MD4_Final LK_MD4_Final +MD4_Init LK_MD4_Init +MD4_Transform LK_MD4_Transform +MD4_Update LK_MD4_Update +MD5 LK_MD5 +MD5_Final LK_MD5_Final +MD5_Init LK_MD5_Init +MD5_Transform LK_MD5_Transform +MD5_Update LK_MD5_Update +NAME_CONSTRAINTS_check LK_NAME_CONSTRAINTS_check +NCONF_free LK_NCONF_free +NCONF_get_section LK_NCONF_get_section +NCONF_get_string LK_NCONF_get_string +NCONF_load LK_NCONF_load +NCONF_load_bio LK_NCONF_load_bio +NCONF_new LK_NCONF_new +NETSCAPE_SPKI_b64_decode LK_NETSCAPE_SPKI_b64_decode +NETSCAPE_SPKI_b64_encode LK_NETSCAPE_SPKI_b64_encode +NETSCAPE_SPKI_get_pubkey LK_NETSCAPE_SPKI_get_pubkey +NETSCAPE_SPKI_set_pubkey LK_NETSCAPE_SPKI_set_pubkey +NETSCAPE_SPKI_sign LK_NETSCAPE_SPKI_sign +NETSCAPE_SPKI_verify LK_NETSCAPE_SPKI_verify +OBJ_NAME_do_all LK_OBJ_NAME_do_all +OBJ_NAME_do_all_sorted LK_OBJ_NAME_do_all_sorted +OBJ_cbs2nid LK_OBJ_cbs2nid +OBJ_cleanup LK_OBJ_cleanup +OBJ_cmp LK_OBJ_cmp +OBJ_create LK_OBJ_create +OBJ_dup LK_OBJ_dup +OBJ_find_sigid_algs LK_OBJ_find_sigid_algs +OBJ_find_sigid_by_algs LK_OBJ_find_sigid_by_algs +OBJ_get0_data LK_OBJ_get0_data +OBJ_length LK_OBJ_length +OBJ_ln2nid LK_OBJ_ln2nid +OBJ_nid2cbb LK_OBJ_nid2cbb +OBJ_nid2ln LK_OBJ_nid2ln +OBJ_nid2obj LK_OBJ_nid2obj +OBJ_nid2sn LK_OBJ_nid2sn +OBJ_obj2nid LK_OBJ_obj2nid +OBJ_obj2txt LK_OBJ_obj2txt +OBJ_sn2nid LK_OBJ_sn2nid +OBJ_txt2nid LK_OBJ_txt2nid +OBJ_txt2obj LK_OBJ_txt2obj +OPENSSL_add_all_algorithms_conf LK_OPENSSL_add_all_algorithms_conf +OPENSSL_asprintf LK_OPENSSL_asprintf +OPENSSL_cleanse LK_OPENSSL_cleanse +OPENSSL_cleanup LK_OPENSSL_cleanup +OPENSSL_clear_free LK_OPENSSL_clear_free +OPENSSL_config LK_OPENSSL_config +OPENSSL_free LK_OPENSSL_free +OPENSSL_fromxdigit LK_OPENSSL_fromxdigit +OPENSSL_get_armcap_pointer_for_test LK_OPENSSL_get_armcap_pointer_for_test +OPENSSL_gmtime LK_OPENSSL_gmtime +OPENSSL_gmtime_adj LK_OPENSSL_gmtime_adj +OPENSSL_gmtime_diff LK_OPENSSL_gmtime_diff +OPENSSL_hash32 LK_OPENSSL_hash32 +OPENSSL_init_crypto LK_OPENSSL_init_crypto +OPENSSL_init_ssl LK_OPENSSL_init_ssl +OPENSSL_isalnum LK_OPENSSL_isalnum +OPENSSL_isalpha LK_OPENSSL_isalpha +OPENSSL_isdigit LK_OPENSSL_isdigit +OPENSSL_isspace LK_OPENSSL_isspace +OPENSSL_isxdigit LK_OPENSSL_isxdigit +OPENSSL_lh_delete LK_OPENSSL_lh_delete +OPENSSL_lh_doall_arg LK_OPENSSL_lh_doall_arg +OPENSSL_lh_free LK_OPENSSL_lh_free +OPENSSL_lh_insert LK_OPENSSL_lh_insert +OPENSSL_lh_new LK_OPENSSL_lh_new +OPENSSL_lh_num_items LK_OPENSSL_lh_num_items +OPENSSL_lh_retrieve LK_OPENSSL_lh_retrieve +OPENSSL_lh_retrieve_key LK_OPENSSL_lh_retrieve_key +OPENSSL_load_builtin_modules LK_OPENSSL_load_builtin_modules +OPENSSL_malloc LK_OPENSSL_malloc +OPENSSL_malloc_init LK_OPENSSL_malloc_init +OPENSSL_memdup LK_OPENSSL_memdup +OPENSSL_no_config LK_OPENSSL_no_config +OPENSSL_posix_to_tm LK_OPENSSL_posix_to_tm +OPENSSL_realloc LK_OPENSSL_realloc +OPENSSL_reset_malloc_counter_for_testing LK_OPENSSL_reset_malloc_counter_for_testing +OPENSSL_secure_clear_free LK_OPENSSL_secure_clear_free +OPENSSL_secure_malloc LK_OPENSSL_secure_malloc +OPENSSL_strcasecmp LK_OPENSSL_strcasecmp +OPENSSL_strdup LK_OPENSSL_strdup +OPENSSL_strhash LK_OPENSSL_strhash +OPENSSL_strlcat LK_OPENSSL_strlcat +OPENSSL_strlcpy LK_OPENSSL_strlcpy +OPENSSL_strncasecmp LK_OPENSSL_strncasecmp +OPENSSL_strndup LK_OPENSSL_strndup +OPENSSL_strnlen LK_OPENSSL_strnlen +OPENSSL_timegm LK_OPENSSL_timegm +OPENSSL_tm_to_posix LK_OPENSSL_tm_to_posix +OPENSSL_tolower LK_OPENSSL_tolower +OPENSSL_vasprintf LK_OPENSSL_vasprintf +OPENSSL_vasprintf_internal LK_OPENSSL_vasprintf_internal +OpenSSL_add_all_algorithms LK_OpenSSL_add_all_algorithms +OpenSSL_add_all_ciphers LK_OpenSSL_add_all_ciphers +OpenSSL_add_all_digests LK_OpenSSL_add_all_digests +OpenSSL_version LK_OpenSSL_version +OpenSSL_version_num LK_OpenSSL_version_num +PEM_ASN1_read LK_PEM_ASN1_read +PEM_ASN1_read_bio LK_PEM_ASN1_read_bio +PEM_ASN1_write LK_PEM_ASN1_write +PEM_ASN1_write_bio LK_PEM_ASN1_write_bio +PEM_X509_INFO_read LK_PEM_X509_INFO_read +PEM_X509_INFO_read_bio LK_PEM_X509_INFO_read_bio +PEM_bytes_read_bio LK_PEM_bytes_read_bio +PEM_def_callback LK_PEM_def_callback +PEM_do_header LK_PEM_do_header +PEM_get_EVP_CIPHER_INFO LK_PEM_get_EVP_CIPHER_INFO +PEM_read LK_PEM_read +PEM_read_bio LK_PEM_read_bio +PEM_write LK_PEM_write +PEM_write_PKCS8PrivateKey LK_PEM_write_PKCS8PrivateKey +PEM_write_PKCS8PrivateKey_nid LK_PEM_write_PKCS8PrivateKey_nid +PEM_write_bio LK_PEM_write_bio +PEM_write_bio_PKCS8PrivateKey LK_PEM_write_bio_PKCS8PrivateKey +PEM_write_bio_PKCS8PrivateKey_nid LK_PEM_write_bio_PKCS8PrivateKey_nid +PKCS12_PBE_add LK_PKCS12_PBE_add +PKCS12_create LK_PKCS12_create +PKCS12_free LK_PKCS12_free +PKCS12_get_key_and_certs LK_PKCS12_get_key_and_certs +PKCS12_parse LK_PKCS12_parse +PKCS12_verify_mac LK_PKCS12_verify_mac +PKCS5_PBKDF2_HMAC LK_PKCS5_PBKDF2_HMAC +PKCS5_PBKDF2_HMAC_SHA1 LK_PKCS5_PBKDF2_HMAC_SHA1 +PKCS7_bundle_certificates LK_PKCS7_bundle_certificates +PKCS7_bundle_raw_certificates LK_PKCS7_bundle_raw_certificates +PKCS7_free LK_PKCS7_free +PKCS7_get_CRLs LK_PKCS7_get_CRLs +PKCS7_get_PEM_CRLs LK_PKCS7_get_PEM_CRLs +PKCS7_get_PEM_certificates LK_PKCS7_get_PEM_certificates +PKCS7_get_certificates LK_PKCS7_get_certificates +PKCS7_get_raw_certificates LK_PKCS7_get_raw_certificates +PKCS7_sign LK_PKCS7_sign +PKCS7_type_is_data LK_PKCS7_type_is_data +PKCS7_type_is_digest LK_PKCS7_type_is_digest +PKCS7_type_is_encrypted LK_PKCS7_type_is_encrypted +PKCS7_type_is_enveloped LK_PKCS7_type_is_enveloped +PKCS7_type_is_signed LK_PKCS7_type_is_signed +PKCS7_type_is_signedAndEnveloped LK_PKCS7_type_is_signedAndEnveloped +PKCS8_decrypt LK_PKCS8_decrypt +PKCS8_encrypt LK_PKCS8_encrypt +PKCS8_marshal_encrypted_private_key LK_PKCS8_marshal_encrypted_private_key +PKCS8_parse_encrypted_private_key LK_PKCS8_parse_encrypted_private_key +RAND_OpenSSL LK_RAND_OpenSSL +RAND_SSLeay LK_RAND_SSLeay +RAND_add LK_RAND_add +RAND_bytes LK_RAND_bytes +RAND_cleanup LK_RAND_cleanup +RAND_egd LK_RAND_egd +RAND_enable_fork_unsafe_buffering LK_RAND_enable_fork_unsafe_buffering +RAND_file_name LK_RAND_file_name +RAND_get_rand_method LK_RAND_get_rand_method +RAND_get_system_entropy_for_custom_prng LK_RAND_get_system_entropy_for_custom_prng +RAND_load_file LK_RAND_load_file +RAND_poll LK_RAND_poll +RAND_pseudo_bytes LK_RAND_pseudo_bytes +RAND_reset_for_fuzzing LK_RAND_reset_for_fuzzing +RAND_seed LK_RAND_seed +RAND_set_rand_method LK_RAND_set_rand_method +RAND_status LK_RAND_status +RC4 LK_RC4 +RC4_options LK_RC4_options +RC4_set_key LK_RC4_set_key +RIPEMD160 LK_RIPEMD160 +RIPEMD160_Final LK_RIPEMD160_Final +RIPEMD160_Init LK_RIPEMD160_Init +RIPEMD160_Transform LK_RIPEMD160_Transform +RIPEMD160_Update LK_RIPEMD160_Update +RSAPrivateKey_dup LK_RSAPrivateKey_dup +RSAPublicKey_dup LK_RSAPublicKey_dup +RSA_add_pkcs1_prefix LK_RSA_add_pkcs1_prefix +RSA_bits LK_RSA_bits +RSA_blinding_on LK_RSA_blinding_on +RSA_check_fips LK_RSA_check_fips +RSA_check_key LK_RSA_check_key +RSA_decrypt LK_RSA_decrypt +RSA_encrypt LK_RSA_encrypt +RSA_flags LK_RSA_flags +RSA_free LK_RSA_free +RSA_generate_key LK_RSA_generate_key +RSA_generate_key_ex LK_RSA_generate_key_ex +RSA_generate_key_fips LK_RSA_generate_key_fips +RSA_get0_crt_params LK_RSA_get0_crt_params +RSA_get0_d LK_RSA_get0_d +RSA_get0_dmp1 LK_RSA_get0_dmp1 +RSA_get0_dmq1 LK_RSA_get0_dmq1 +RSA_get0_e LK_RSA_get0_e +RSA_get0_factors LK_RSA_get0_factors +RSA_get0_iqmp LK_RSA_get0_iqmp +RSA_get0_key LK_RSA_get0_key +RSA_get0_n LK_RSA_get0_n +RSA_get0_p LK_RSA_get0_p +RSA_get0_pss_params LK_RSA_get0_pss_params +RSA_get0_q LK_RSA_get0_q +RSA_get_ex_data LK_RSA_get_ex_data +RSA_get_ex_new_index LK_RSA_get_ex_new_index +RSA_is_opaque LK_RSA_is_opaque +RSA_marshal_private_key LK_RSA_marshal_private_key +RSA_marshal_public_key LK_RSA_marshal_public_key +RSA_new LK_RSA_new +RSA_new_method LK_RSA_new_method +RSA_padding_add_PKCS1_OAEP LK_RSA_padding_add_PKCS1_OAEP +RSA_padding_add_PKCS1_OAEP_mgf1 LK_RSA_padding_add_PKCS1_OAEP_mgf1 +RSA_padding_add_PKCS1_PSS LK_RSA_padding_add_PKCS1_PSS +RSA_padding_add_PKCS1_PSS_mgf1 LK_RSA_padding_add_PKCS1_PSS_mgf1 +RSA_parse_private_key LK_RSA_parse_private_key +RSA_parse_public_key LK_RSA_parse_public_key +RSA_print LK_RSA_print +RSA_private_decrypt LK_RSA_private_decrypt +RSA_private_encrypt LK_RSA_private_encrypt +RSA_private_key_from_bytes LK_RSA_private_key_from_bytes +RSA_private_key_to_bytes LK_RSA_private_key_to_bytes +RSA_public_decrypt LK_RSA_public_decrypt +RSA_public_encrypt LK_RSA_public_encrypt +RSA_public_key_from_bytes LK_RSA_public_key_from_bytes +RSA_public_key_to_bytes LK_RSA_public_key_to_bytes +RSA_set0_crt_params LK_RSA_set0_crt_params +RSA_set0_factors LK_RSA_set0_factors +RSA_set0_key LK_RSA_set0_key +RSA_set_ex_data LK_RSA_set_ex_data +RSA_sign LK_RSA_sign +RSA_sign_pss_mgf1 LK_RSA_sign_pss_mgf1 +RSA_sign_raw LK_RSA_sign_raw +RSA_size LK_RSA_size +RSA_test_flags LK_RSA_test_flags +RSA_up_ref LK_RSA_up_ref +RSA_verify LK_RSA_verify +RSA_verify_PKCS1_PSS LK_RSA_verify_PKCS1_PSS +RSA_verify_PKCS1_PSS_mgf1 LK_RSA_verify_PKCS1_PSS_mgf1 +RSA_verify_pss_mgf1 LK_RSA_verify_pss_mgf1 +RSA_verify_raw LK_RSA_verify_raw +SHA1 LK_SHA1 +SHA1_Final LK_SHA1_Final +SHA1_Init LK_SHA1_Init +SHA1_Transform LK_SHA1_Transform +SHA1_Update LK_SHA1_Update +SHA224 LK_SHA224 +SHA224_Final LK_SHA224_Final +SHA224_Init LK_SHA224_Init +SHA224_Update LK_SHA224_Update +SHA256 LK_SHA256 +SHA256_Final LK_SHA256_Final +SHA256_Init LK_SHA256_Init +SHA256_Transform LK_SHA256_Transform +SHA256_TransformBlocks LK_SHA256_TransformBlocks +SHA256_Update LK_SHA256_Update +SHA384 LK_SHA384 +SHA384_Final LK_SHA384_Final +SHA384_Init LK_SHA384_Init +SHA384_Update LK_SHA384_Update +SHA512 LK_SHA512 +SHA512_256 LK_SHA512_256 +SHA512_256_Final LK_SHA512_256_Final +SHA512_256_Init LK_SHA512_256_Init +SHA512_256_Update LK_SHA512_256_Update +SHA512_Final LK_SHA512_Final +SHA512_Init LK_SHA512_Init +SHA512_Transform LK_SHA512_Transform +SHA512_Update LK_SHA512_Update +SIPHASH_24 LK_SIPHASH_24 +SPAKE2_CTX_free LK_SPAKE2_CTX_free +SPAKE2_CTX_new LK_SPAKE2_CTX_new +SPAKE2_generate_msg LK_SPAKE2_generate_msg +SPAKE2_process_msg LK_SPAKE2_process_msg +SSL_CIPHER_description LK_SSL_CIPHER_description +SSL_CIPHER_get_auth_nid LK_SSL_CIPHER_get_auth_nid +SSL_CIPHER_get_bits LK_SSL_CIPHER_get_bits +SSL_CIPHER_get_cipher_nid LK_SSL_CIPHER_get_cipher_nid +SSL_CIPHER_get_digest_nid LK_SSL_CIPHER_get_digest_nid +SSL_CIPHER_get_id LK_SSL_CIPHER_get_id +SSL_CIPHER_get_kx_name LK_SSL_CIPHER_get_kx_name +SSL_CIPHER_get_kx_nid LK_SSL_CIPHER_get_kx_nid +SSL_CIPHER_get_max_version LK_SSL_CIPHER_get_max_version +SSL_CIPHER_get_min_version LK_SSL_CIPHER_get_min_version +SSL_CIPHER_get_name LK_SSL_CIPHER_get_name +SSL_CIPHER_get_prf_nid LK_SSL_CIPHER_get_prf_nid +SSL_CIPHER_get_protocol_id LK_SSL_CIPHER_get_protocol_id +SSL_CIPHER_get_value LK_SSL_CIPHER_get_value +SSL_CIPHER_get_version LK_SSL_CIPHER_get_version +SSL_CIPHER_is_aead LK_SSL_CIPHER_is_aead +SSL_CIPHER_is_block_cipher LK_SSL_CIPHER_is_block_cipher +SSL_CIPHER_standard_name LK_SSL_CIPHER_standard_name +SSL_COMP_add_compression_method LK_SSL_COMP_add_compression_method +SSL_COMP_free_compression_methods LK_SSL_COMP_free_compression_methods +SSL_COMP_get0_name LK_SSL_COMP_get0_name +SSL_COMP_get_compression_methods LK_SSL_COMP_get_compression_methods +SSL_COMP_get_id LK_SSL_COMP_get_id +SSL_COMP_get_name LK_SSL_COMP_get_name +SSL_CTX_add0_chain_cert LK_SSL_CTX_add0_chain_cert +SSL_CTX_add1_chain_cert LK_SSL_CTX_add1_chain_cert +SSL_CTX_add_cert_compression_alg LK_SSL_CTX_add_cert_compression_alg +SSL_CTX_add_client_CA LK_SSL_CTX_add_client_CA +SSL_CTX_add_extra_chain_cert LK_SSL_CTX_add_extra_chain_cert +SSL_CTX_add_session LK_SSL_CTX_add_session +SSL_CTX_check_private_key LK_SSL_CTX_check_private_key +SSL_CTX_cipher_in_group LK_SSL_CTX_cipher_in_group +SSL_CTX_clear_chain_certs LK_SSL_CTX_clear_chain_certs +SSL_CTX_clear_extra_chain_certs LK_SSL_CTX_clear_extra_chain_certs +SSL_CTX_clear_mode LK_SSL_CTX_clear_mode +SSL_CTX_clear_options LK_SSL_CTX_clear_options +SSL_CTX_enable_ocsp_stapling LK_SSL_CTX_enable_ocsp_stapling +SSL_CTX_enable_signed_cert_timestamps LK_SSL_CTX_enable_signed_cert_timestamps +SSL_CTX_enable_tls_channel_id LK_SSL_CTX_enable_tls_channel_id +SSL_CTX_flush_sessions LK_SSL_CTX_flush_sessions +SSL_CTX_free LK_SSL_CTX_free +SSL_CTX_get0_certificate LK_SSL_CTX_get0_certificate +SSL_CTX_get0_chain_certs LK_SSL_CTX_get0_chain_certs +SSL_CTX_get0_param LK_SSL_CTX_get0_param +SSL_CTX_get0_privatekey LK_SSL_CTX_get0_privatekey +SSL_CTX_get_cert_store LK_SSL_CTX_get_cert_store +SSL_CTX_get_ciphers LK_SSL_CTX_get_ciphers +SSL_CTX_get_default_passwd_cb LK_SSL_CTX_get_default_passwd_cb +SSL_CTX_get_default_passwd_cb_userdata LK_SSL_CTX_get_default_passwd_cb_userdata +SSL_CTX_get_ex_data LK_SSL_CTX_get_ex_data +SSL_CTX_get_ex_new_index LK_SSL_CTX_get_ex_new_index +SSL_CTX_get_extra_chain_certs LK_SSL_CTX_get_extra_chain_certs +SSL_CTX_get_max_cert_list LK_SSL_CTX_get_max_cert_list +SSL_CTX_get_max_proto_version LK_SSL_CTX_get_max_proto_version +SSL_CTX_get_min_proto_version LK_SSL_CTX_get_min_proto_version +SSL_CTX_get_mode LK_SSL_CTX_get_mode +SSL_CTX_get_num_tickets LK_SSL_CTX_get_num_tickets +SSL_CTX_get_options LK_SSL_CTX_get_options +SSL_CTX_get_quiet_shutdown LK_SSL_CTX_get_quiet_shutdown +SSL_CTX_get_read_ahead LK_SSL_CTX_get_read_ahead +SSL_CTX_get_session_cache_mode LK_SSL_CTX_get_session_cache_mode +SSL_CTX_get_timeout LK_SSL_CTX_get_timeout +SSL_CTX_get_tlsext_ticket_keys LK_SSL_CTX_get_tlsext_ticket_keys +SSL_CTX_get_verify_depth LK_SSL_CTX_get_verify_depth +SSL_CTX_get_verify_mode LK_SSL_CTX_get_verify_mode +SSL_CTX_load_verify_locations LK_SSL_CTX_load_verify_locations +SSL_CTX_need_tmp_RSA LK_SSL_CTX_need_tmp_RSA +SSL_CTX_new LK_SSL_CTX_new +SSL_CTX_remove_session LK_SSL_CTX_remove_session +SSL_CTX_sess_accept LK_SSL_CTX_sess_accept +SSL_CTX_sess_accept_good LK_SSL_CTX_sess_accept_good +SSL_CTX_sess_accept_renegotiate LK_SSL_CTX_sess_accept_renegotiate +SSL_CTX_sess_cache_full LK_SSL_CTX_sess_cache_full +SSL_CTX_sess_cb_hits LK_SSL_CTX_sess_cb_hits +SSL_CTX_sess_connect LK_SSL_CTX_sess_connect +SSL_CTX_sess_connect_good LK_SSL_CTX_sess_connect_good +SSL_CTX_sess_connect_renegotiate LK_SSL_CTX_sess_connect_renegotiate +SSL_CTX_sess_get_cache_size LK_SSL_CTX_sess_get_cache_size +SSL_CTX_sess_hits LK_SSL_CTX_sess_hits +SSL_CTX_sess_misses LK_SSL_CTX_sess_misses +SSL_CTX_sess_number LK_SSL_CTX_sess_number +SSL_CTX_sess_set_cache_size LK_SSL_CTX_sess_set_cache_size +SSL_CTX_sess_set_get_cb LK_SSL_CTX_sess_set_get_cb +SSL_CTX_sess_set_new_cb LK_SSL_CTX_sess_set_new_cb +SSL_CTX_sess_set_remove_cb LK_SSL_CTX_sess_set_remove_cb +SSL_CTX_sess_timeouts LK_SSL_CTX_sess_timeouts +SSL_CTX_set0_buffer_pool LK_SSL_CTX_set0_buffer_pool +SSL_CTX_set0_client_CAs LK_SSL_CTX_set0_client_CAs +SSL_CTX_set0_verify_cert_store LK_SSL_CTX_set0_verify_cert_store +SSL_CTX_set1_curves LK_SSL_CTX_set1_curves +SSL_CTX_set1_curves_list LK_SSL_CTX_set1_curves_list +SSL_CTX_set1_ech_keys LK_SSL_CTX_set1_ech_keys +SSL_CTX_set1_groups LK_SSL_CTX_set1_groups +SSL_CTX_set1_groups_list LK_SSL_CTX_set1_groups_list +SSL_CTX_set1_param LK_SSL_CTX_set1_param +SSL_CTX_set1_sigalgs LK_SSL_CTX_set1_sigalgs +SSL_CTX_set1_sigalgs_list LK_SSL_CTX_set1_sigalgs_list +SSL_CTX_set1_tls_channel_id LK_SSL_CTX_set1_tls_channel_id +SSL_CTX_set1_verify_cert_store LK_SSL_CTX_set1_verify_cert_store +SSL_CTX_set_aes_hw_override_for_testing LK_SSL_CTX_set_aes_hw_override_for_testing +SSL_CTX_set_allow_unknown_alpn_protos LK_SSL_CTX_set_allow_unknown_alpn_protos +SSL_CTX_set_alpn_protos LK_SSL_CTX_set_alpn_protos +SSL_CTX_set_alpn_select_cb LK_SSL_CTX_set_alpn_select_cb +SSL_CTX_set_cert_cb LK_SSL_CTX_set_cert_cb +SSL_CTX_set_cert_store LK_SSL_CTX_set_cert_store +SSL_CTX_set_cert_verify_callback LK_SSL_CTX_set_cert_verify_callback +SSL_CTX_set_chain_and_key LK_SSL_CTX_set_chain_and_key +SSL_CTX_set_cipher_list LK_SSL_CTX_set_cipher_list +SSL_CTX_set_client_CA_list LK_SSL_CTX_set_client_CA_list +SSL_CTX_set_client_cert_cb LK_SSL_CTX_set_client_cert_cb +SSL_CTX_set_compliance_policy LK_SSL_CTX_set_compliance_policy +SSL_CTX_set_current_time_cb LK_SSL_CTX_set_current_time_cb +SSL_CTX_set_custom_verify LK_SSL_CTX_set_custom_verify +SSL_CTX_set_default_passwd_cb LK_SSL_CTX_set_default_passwd_cb +SSL_CTX_set_default_passwd_cb_userdata LK_SSL_CTX_set_default_passwd_cb_userdata +SSL_CTX_set_default_verify_paths LK_SSL_CTX_set_default_verify_paths +SSL_CTX_set_dos_protection_cb LK_SSL_CTX_set_dos_protection_cb +SSL_CTX_set_early_data_enabled LK_SSL_CTX_set_early_data_enabled +SSL_CTX_set_ex_data LK_SSL_CTX_set_ex_data +SSL_CTX_set_false_start_allowed_without_alpn LK_SSL_CTX_set_false_start_allowed_without_alpn +SSL_CTX_set_grease_enabled LK_SSL_CTX_set_grease_enabled +SSL_CTX_set_handoff_mode LK_SSL_CTX_set_handoff_mode +SSL_CTX_set_info_callback LK_SSL_CTX_set_info_callback +SSL_CTX_set_keylog_callback LK_SSL_CTX_set_keylog_callback +SSL_CTX_set_max_cert_list LK_SSL_CTX_set_max_cert_list +SSL_CTX_set_max_proto_version LK_SSL_CTX_set_max_proto_version +SSL_CTX_set_max_send_fragment LK_SSL_CTX_set_max_send_fragment +SSL_CTX_set_min_proto_version LK_SSL_CTX_set_min_proto_version +SSL_CTX_set_mode LK_SSL_CTX_set_mode +SSL_CTX_set_msg_callback LK_SSL_CTX_set_msg_callback +SSL_CTX_set_msg_callback_arg LK_SSL_CTX_set_msg_callback_arg +SSL_CTX_set_next_proto_select_cb LK_SSL_CTX_set_next_proto_select_cb +SSL_CTX_set_next_protos_advertised_cb LK_SSL_CTX_set_next_protos_advertised_cb +SSL_CTX_set_num_tickets LK_SSL_CTX_set_num_tickets +SSL_CTX_set_ocsp_response LK_SSL_CTX_set_ocsp_response +SSL_CTX_set_options LK_SSL_CTX_set_options +SSL_CTX_set_permute_extensions LK_SSL_CTX_set_permute_extensions +SSL_CTX_set_private_key_method LK_SSL_CTX_set_private_key_method +SSL_CTX_set_psk_client_callback LK_SSL_CTX_set_psk_client_callback +SSL_CTX_set_psk_server_callback LK_SSL_CTX_set_psk_server_callback +SSL_CTX_set_purpose LK_SSL_CTX_set_purpose +SSL_CTX_set_quic_method LK_SSL_CTX_set_quic_method +SSL_CTX_set_quiet_shutdown LK_SSL_CTX_set_quiet_shutdown +SSL_CTX_set_read_ahead LK_SSL_CTX_set_read_ahead +SSL_CTX_set_record_protocol_version LK_SSL_CTX_set_record_protocol_version +SSL_CTX_set_retain_only_sha256_of_client_certs LK_SSL_CTX_set_retain_only_sha256_of_client_certs +SSL_CTX_set_reverify_on_resume LK_SSL_CTX_set_reverify_on_resume +SSL_CTX_set_select_certificate_cb LK_SSL_CTX_set_select_certificate_cb +SSL_CTX_set_session_cache_mode LK_SSL_CTX_set_session_cache_mode +SSL_CTX_set_session_id_context LK_SSL_CTX_set_session_id_context +SSL_CTX_set_session_psk_dhe_timeout LK_SSL_CTX_set_session_psk_dhe_timeout +SSL_CTX_set_signed_cert_timestamp_list LK_SSL_CTX_set_signed_cert_timestamp_list +SSL_CTX_set_signing_algorithm_prefs LK_SSL_CTX_set_signing_algorithm_prefs +SSL_CTX_set_srtp_profiles LK_SSL_CTX_set_srtp_profiles +SSL_CTX_set_strict_cipher_list LK_SSL_CTX_set_strict_cipher_list +SSL_CTX_set_ticket_aead_method LK_SSL_CTX_set_ticket_aead_method +SSL_CTX_set_timeout LK_SSL_CTX_set_timeout +SSL_CTX_set_tls_channel_id_enabled LK_SSL_CTX_set_tls_channel_id_enabled +SSL_CTX_set_tlsext_servername_arg LK_SSL_CTX_set_tlsext_servername_arg +SSL_CTX_set_tlsext_servername_callback LK_SSL_CTX_set_tlsext_servername_callback +SSL_CTX_set_tlsext_status_arg LK_SSL_CTX_set_tlsext_status_arg +SSL_CTX_set_tlsext_status_cb LK_SSL_CTX_set_tlsext_status_cb +SSL_CTX_set_tlsext_ticket_key_cb LK_SSL_CTX_set_tlsext_ticket_key_cb +SSL_CTX_set_tlsext_ticket_keys LK_SSL_CTX_set_tlsext_ticket_keys +SSL_CTX_set_tlsext_use_srtp LK_SSL_CTX_set_tlsext_use_srtp +SSL_CTX_set_tmp_dh LK_SSL_CTX_set_tmp_dh +SSL_CTX_set_tmp_dh_callback LK_SSL_CTX_set_tmp_dh_callback +SSL_CTX_set_tmp_ecdh LK_SSL_CTX_set_tmp_ecdh +SSL_CTX_set_tmp_rsa LK_SSL_CTX_set_tmp_rsa +SSL_CTX_set_tmp_rsa_callback LK_SSL_CTX_set_tmp_rsa_callback +SSL_CTX_set_trust LK_SSL_CTX_set_trust +SSL_CTX_set_verify LK_SSL_CTX_set_verify +SSL_CTX_set_verify_algorithm_prefs LK_SSL_CTX_set_verify_algorithm_prefs +SSL_CTX_set_verify_depth LK_SSL_CTX_set_verify_depth +SSL_CTX_up_ref LK_SSL_CTX_up_ref +SSL_CTX_use_PrivateKey LK_SSL_CTX_use_PrivateKey +SSL_CTX_use_PrivateKey_ASN1 LK_SSL_CTX_use_PrivateKey_ASN1 +SSL_CTX_use_PrivateKey_file LK_SSL_CTX_use_PrivateKey_file +SSL_CTX_use_RSAPrivateKey LK_SSL_CTX_use_RSAPrivateKey +SSL_CTX_use_RSAPrivateKey_ASN1 LK_SSL_CTX_use_RSAPrivateKey_ASN1 +SSL_CTX_use_RSAPrivateKey_file LK_SSL_CTX_use_RSAPrivateKey_file +SSL_CTX_use_certificate LK_SSL_CTX_use_certificate +SSL_CTX_use_certificate_ASN1 LK_SSL_CTX_use_certificate_ASN1 +SSL_CTX_use_certificate_chain_file LK_SSL_CTX_use_certificate_chain_file +SSL_CTX_use_certificate_file LK_SSL_CTX_use_certificate_file +SSL_CTX_use_psk_identity_hint LK_SSL_CTX_use_psk_identity_hint +SSL_ECH_KEYS_add LK_SSL_ECH_KEYS_add +SSL_ECH_KEYS_free LK_SSL_ECH_KEYS_free +SSL_ECH_KEYS_has_duplicate_config_id LK_SSL_ECH_KEYS_has_duplicate_config_id +SSL_ECH_KEYS_marshal_retry_configs LK_SSL_ECH_KEYS_marshal_retry_configs +SSL_ECH_KEYS_new LK_SSL_ECH_KEYS_new +SSL_ECH_KEYS_up_ref LK_SSL_ECH_KEYS_up_ref +SSL_SESSION_copy_without_early_data LK_SSL_SESSION_copy_without_early_data +SSL_SESSION_dup LK_SSL_SESSION_dup +SSL_SESSION_early_data_capable LK_SSL_SESSION_early_data_capable +SSL_SESSION_free LK_SSL_SESSION_free +SSL_SESSION_from_bytes LK_SSL_SESSION_from_bytes +SSL_SESSION_get0_cipher LK_SSL_SESSION_get0_cipher +SSL_SESSION_get0_id_context LK_SSL_SESSION_get0_id_context +SSL_SESSION_get0_ocsp_response LK_SSL_SESSION_get0_ocsp_response +SSL_SESSION_get0_peer LK_SSL_SESSION_get0_peer +SSL_SESSION_get0_peer_sha256 LK_SSL_SESSION_get0_peer_sha256 +SSL_SESSION_get0_signed_cert_timestamp_list LK_SSL_SESSION_get0_signed_cert_timestamp_list +SSL_SESSION_get0_ticket LK_SSL_SESSION_get0_ticket +SSL_SESSION_get_ex_data LK_SSL_SESSION_get_ex_data +SSL_SESSION_get_ex_new_index LK_SSL_SESSION_get_ex_new_index +SSL_SESSION_get_id LK_SSL_SESSION_get_id +SSL_SESSION_get_master_key LK_SSL_SESSION_get_master_key +SSL_SESSION_get_protocol_version LK_SSL_SESSION_get_protocol_version +SSL_SESSION_get_ticket_lifetime_hint LK_SSL_SESSION_get_ticket_lifetime_hint +SSL_SESSION_get_time LK_SSL_SESSION_get_time +SSL_SESSION_get_timeout LK_SSL_SESSION_get_timeout +SSL_SESSION_get_version LK_SSL_SESSION_get_version +SSL_SESSION_has_peer_sha256 LK_SSL_SESSION_has_peer_sha256 +SSL_SESSION_has_ticket LK_SSL_SESSION_has_ticket +SSL_SESSION_is_resumable LK_SSL_SESSION_is_resumable +SSL_SESSION_new LK_SSL_SESSION_new +SSL_SESSION_parse LK_SSL_SESSION_parse +SSL_SESSION_set1_id LK_SSL_SESSION_set1_id +SSL_SESSION_set1_id_context LK_SSL_SESSION_set1_id_context +SSL_SESSION_set_ex_data LK_SSL_SESSION_set_ex_data +SSL_SESSION_set_protocol_version LK_SSL_SESSION_set_protocol_version +SSL_SESSION_set_ticket LK_SSL_SESSION_set_ticket +SSL_SESSION_set_time LK_SSL_SESSION_set_time +SSL_SESSION_set_timeout LK_SSL_SESSION_set_timeout +SSL_SESSION_should_be_single_use LK_SSL_SESSION_should_be_single_use +SSL_SESSION_to_bytes LK_SSL_SESSION_to_bytes +SSL_SESSION_to_bytes_for_ticket LK_SSL_SESSION_to_bytes_for_ticket +SSL_SESSION_up_ref LK_SSL_SESSION_up_ref +SSL_accept LK_SSL_accept +SSL_add0_chain_cert LK_SSL_add0_chain_cert +SSL_add1_chain_cert LK_SSL_add1_chain_cert +SSL_add_application_settings LK_SSL_add_application_settings +SSL_add_bio_cert_subjects_to_stack LK_SSL_add_bio_cert_subjects_to_stack +SSL_add_client_CA LK_SSL_add_client_CA +SSL_add_dir_cert_subjects_to_stack LK_SSL_add_dir_cert_subjects_to_stack +SSL_add_file_cert_subjects_to_stack LK_SSL_add_file_cert_subjects_to_stack +SSL_alert_desc_string LK_SSL_alert_desc_string +SSL_alert_desc_string_long LK_SSL_alert_desc_string_long +SSL_alert_from_verify_result LK_SSL_alert_from_verify_result +SSL_alert_type_string LK_SSL_alert_type_string +SSL_alert_type_string_long LK_SSL_alert_type_string_long +SSL_apply_handback LK_SSL_apply_handback +SSL_apply_handoff LK_SSL_apply_handoff +SSL_cache_hit LK_SSL_cache_hit +SSL_can_release_private_key LK_SSL_can_release_private_key +SSL_certs_clear LK_SSL_certs_clear +SSL_check_private_key LK_SSL_check_private_key +SSL_clear LK_SSL_clear +SSL_clear_chain_certs LK_SSL_clear_chain_certs +SSL_clear_mode LK_SSL_clear_mode +SSL_clear_options LK_SSL_clear_options +SSL_connect LK_SSL_connect +SSL_cutthrough_complete LK_SSL_cutthrough_complete +SSL_decline_handoff LK_SSL_decline_handoff +SSL_delegated_credential_used LK_SSL_delegated_credential_used +SSL_do_handshake LK_SSL_do_handshake +SSL_dup_CA_list LK_SSL_dup_CA_list +SSL_early_callback_ctx_extension_get LK_SSL_early_callback_ctx_extension_get +SSL_early_data_accepted LK_SSL_early_data_accepted +SSL_early_data_reason_string LK_SSL_early_data_reason_string +SSL_ech_accepted LK_SSL_ech_accepted +SSL_enable_ocsp_stapling LK_SSL_enable_ocsp_stapling +SSL_enable_signed_cert_timestamps LK_SSL_enable_signed_cert_timestamps +SSL_enable_tls_channel_id LK_SSL_enable_tls_channel_id +SSL_error_description LK_SSL_error_description +SSL_export_keying_material LK_SSL_export_keying_material +SSL_free LK_SSL_free +SSL_generate_key_block LK_SSL_generate_key_block +SSL_get0_alpn_selected LK_SSL_get0_alpn_selected +SSL_get0_certificate_types LK_SSL_get0_certificate_types +SSL_get0_chain_certs LK_SSL_get0_chain_certs +SSL_get0_ech_name_override LK_SSL_get0_ech_name_override +SSL_get0_ech_retry_configs LK_SSL_get0_ech_retry_configs +SSL_get0_next_proto_negotiated LK_SSL_get0_next_proto_negotiated +SSL_get0_ocsp_response LK_SSL_get0_ocsp_response +SSL_get0_param LK_SSL_get0_param +SSL_get0_peer_application_settings LK_SSL_get0_peer_application_settings +SSL_get0_peer_delegation_algorithms LK_SSL_get0_peer_delegation_algorithms +SSL_get0_peer_verify_algorithms LK_SSL_get0_peer_verify_algorithms +SSL_get0_session_id_context LK_SSL_get0_session_id_context +SSL_get0_signed_cert_timestamp_list LK_SSL_get0_signed_cert_timestamp_list +SSL_get1_session LK_SSL_get1_session +SSL_get_SSL_CTX LK_SSL_get_SSL_CTX +SSL_get_certificate LK_SSL_get_certificate +SSL_get_cipher_by_value LK_SSL_get_cipher_by_value +SSL_get_cipher_list LK_SSL_get_cipher_list +SSL_get_ciphers LK_SSL_get_ciphers +SSL_get_client_CA_list LK_SSL_get_client_CA_list +SSL_get_client_random LK_SSL_get_client_random +SSL_get_current_cipher LK_SSL_get_current_cipher +SSL_get_current_compression LK_SSL_get_current_compression +SSL_get_current_expansion LK_SSL_get_current_expansion +SSL_get_curve_id LK_SSL_get_curve_id +SSL_get_curve_name LK_SSL_get_curve_name +SSL_get_default_timeout LK_SSL_get_default_timeout +SSL_get_early_data_reason LK_SSL_get_early_data_reason +SSL_get_error LK_SSL_get_error +SSL_get_ex_data LK_SSL_get_ex_data +SSL_get_ex_data_X509_STORE_CTX_idx LK_SSL_get_ex_data_X509_STORE_CTX_idx +SSL_get_ex_new_index LK_SSL_get_ex_new_index +SSL_get_extms_support LK_SSL_get_extms_support +SSL_get_fd LK_SSL_get_fd +SSL_get_finished LK_SSL_get_finished +SSL_get_ivs LK_SSL_get_ivs +SSL_get_key_block_len LK_SSL_get_key_block_len +SSL_get_max_cert_list LK_SSL_get_max_cert_list +SSL_get_max_proto_version LK_SSL_get_max_proto_version +SSL_get_min_proto_version LK_SSL_get_min_proto_version +SSL_get_mode LK_SSL_get_mode +SSL_get_options LK_SSL_get_options +SSL_get_peer_cert_chain LK_SSL_get_peer_cert_chain +SSL_get_peer_certificate LK_SSL_get_peer_certificate +SSL_get_peer_finished LK_SSL_get_peer_finished +SSL_get_peer_full_cert_chain LK_SSL_get_peer_full_cert_chain +SSL_get_peer_quic_transport_params LK_SSL_get_peer_quic_transport_params +SSL_get_peer_signature_algorithm LK_SSL_get_peer_signature_algorithm +SSL_get_pending_cipher LK_SSL_get_pending_cipher +SSL_get_privatekey LK_SSL_get_privatekey +SSL_get_psk_identity LK_SSL_get_psk_identity +SSL_get_psk_identity_hint LK_SSL_get_psk_identity_hint +SSL_get_quiet_shutdown LK_SSL_get_quiet_shutdown +SSL_get_rbio LK_SSL_get_rbio +SSL_get_read_ahead LK_SSL_get_read_ahead +SSL_get_read_sequence LK_SSL_get_read_sequence +SSL_get_rfd LK_SSL_get_rfd +SSL_get_secure_renegotiation_support LK_SSL_get_secure_renegotiation_support +SSL_get_selected_srtp_profile LK_SSL_get_selected_srtp_profile +SSL_get_server_random LK_SSL_get_server_random +SSL_get_server_tmp_key LK_SSL_get_server_tmp_key +SSL_get_servername LK_SSL_get_servername +SSL_get_servername_type LK_SSL_get_servername_type +SSL_get_session LK_SSL_get_session +SSL_get_shared_ciphers LK_SSL_get_shared_ciphers +SSL_get_shared_sigalgs LK_SSL_get_shared_sigalgs +SSL_get_shutdown LK_SSL_get_shutdown +SSL_get_signature_algorithm_digest LK_SSL_get_signature_algorithm_digest +SSL_get_signature_algorithm_key_type LK_SSL_get_signature_algorithm_key_type +SSL_get_signature_algorithm_name LK_SSL_get_signature_algorithm_name +SSL_get_srtp_profiles LK_SSL_get_srtp_profiles +SSL_get_ticket_age_skew LK_SSL_get_ticket_age_skew +SSL_get_tls_channel_id LK_SSL_get_tls_channel_id +SSL_get_tls_unique LK_SSL_get_tls_unique +SSL_get_tlsext_status_ocsp_resp LK_SSL_get_tlsext_status_ocsp_resp +SSL_get_tlsext_status_type LK_SSL_get_tlsext_status_type +SSL_get_traffic_secrets LK_SSL_get_traffic_secrets +SSL_get_verify_depth LK_SSL_get_verify_depth +SSL_get_verify_mode LK_SSL_get_verify_mode +SSL_get_verify_result LK_SSL_get_verify_result +SSL_get_version LK_SSL_get_version +SSL_get_wbio LK_SSL_get_wbio +SSL_get_wfd LK_SSL_get_wfd +SSL_get_write_sequence LK_SSL_get_write_sequence +SSL_has_application_settings LK_SSL_has_application_settings +SSL_has_pending LK_SSL_has_pending +SSL_in_early_data LK_SSL_in_early_data +SSL_in_false_start LK_SSL_in_false_start +SSL_in_init LK_SSL_in_init +SSL_is_dtls LK_SSL_is_dtls +SSL_is_init_finished LK_SSL_is_init_finished +SSL_is_server LK_SSL_is_server +SSL_is_signature_algorithm_rsa_pss LK_SSL_is_signature_algorithm_rsa_pss +SSL_key_update LK_SSL_key_update +SSL_library_init LK_SSL_library_init +SSL_load_client_CA_file LK_SSL_load_client_CA_file +SSL_load_error_strings LK_SSL_load_error_strings +SSL_magic_pending_session_ptr LK_SSL_magic_pending_session_ptr +SSL_marshal_ech_config LK_SSL_marshal_ech_config +SSL_max_seal_overhead LK_SSL_max_seal_overhead +SSL_need_tmp_RSA LK_SSL_need_tmp_RSA +SSL_new LK_SSL_new +SSL_num_renegotiations LK_SSL_num_renegotiations +SSL_peek LK_SSL_peek +SSL_pending LK_SSL_pending +SSL_process_quic_post_handshake LK_SSL_process_quic_post_handshake +SSL_process_tls13_new_session_ticket LK_SSL_process_tls13_new_session_ticket +SSL_provide_quic_data LK_SSL_provide_quic_data +SSL_quic_max_handshake_flight_len LK_SSL_quic_max_handshake_flight_len +SSL_quic_read_level LK_SSL_quic_read_level +SSL_quic_write_level LK_SSL_quic_write_level +SSL_read LK_SSL_read +SSL_renegotiate LK_SSL_renegotiate +SSL_renegotiate_pending LK_SSL_renegotiate_pending +SSL_request_handshake_hints LK_SSL_request_handshake_hints +SSL_reset_early_data_reject LK_SSL_reset_early_data_reject +SSL_select_next_proto LK_SSL_select_next_proto +SSL_send_fatal_alert LK_SSL_send_fatal_alert +SSL_serialize_capabilities LK_SSL_serialize_capabilities +SSL_serialize_handback LK_SSL_serialize_handback +SSL_serialize_handoff LK_SSL_serialize_handoff +SSL_serialize_handshake_hints LK_SSL_serialize_handshake_hints +SSL_session_reused LK_SSL_session_reused +SSL_set0_client_CAs LK_SSL_set0_client_CAs +SSL_set0_rbio LK_SSL_set0_rbio +SSL_set0_verify_cert_store LK_SSL_set0_verify_cert_store +SSL_set0_wbio LK_SSL_set0_wbio +SSL_set1_curves LK_SSL_set1_curves +SSL_set1_curves_list LK_SSL_set1_curves_list +SSL_set1_delegated_credential LK_SSL_set1_delegated_credential +SSL_set1_ech_config_list LK_SSL_set1_ech_config_list +SSL_set1_groups LK_SSL_set1_groups +SSL_set1_groups_list LK_SSL_set1_groups_list +SSL_set1_host LK_SSL_set1_host +SSL_set1_param LK_SSL_set1_param +SSL_set1_sigalgs LK_SSL_set1_sigalgs +SSL_set1_sigalgs_list LK_SSL_set1_sigalgs_list +SSL_set1_tls_channel_id LK_SSL_set1_tls_channel_id +SSL_set1_verify_cert_store LK_SSL_set1_verify_cert_store +SSL_set_SSL_CTX LK_SSL_set_SSL_CTX +SSL_set_accept_state LK_SSL_set_accept_state +SSL_set_aes_hw_override_for_testing LK_SSL_set_aes_hw_override_for_testing +SSL_set_alpn_protos LK_SSL_set_alpn_protos +SSL_set_bio LK_SSL_set_bio +SSL_set_cert_cb LK_SSL_set_cert_cb +SSL_set_chain_and_key LK_SSL_set_chain_and_key +SSL_set_cipher_list LK_SSL_set_cipher_list +SSL_set_client_CA_list LK_SSL_set_client_CA_list +SSL_set_compliance_policy LK_SSL_set_compliance_policy +SSL_set_connect_state LK_SSL_set_connect_state +SSL_set_custom_verify LK_SSL_set_custom_verify +SSL_set_early_data_enabled LK_SSL_set_early_data_enabled +SSL_set_enable_ech_grease LK_SSL_set_enable_ech_grease +SSL_set_enforce_rsa_key_usage LK_SSL_set_enforce_rsa_key_usage +SSL_set_ex_data LK_SSL_set_ex_data +SSL_set_fd LK_SSL_set_fd +SSL_set_handoff_mode LK_SSL_set_handoff_mode +SSL_set_handshake_hints LK_SSL_set_handshake_hints +SSL_set_hostflags LK_SSL_set_hostflags +SSL_set_info_callback LK_SSL_set_info_callback +SSL_set_jdk11_workaround LK_SSL_set_jdk11_workaround +SSL_set_max_cert_list LK_SSL_set_max_cert_list +SSL_set_max_proto_version LK_SSL_set_max_proto_version +SSL_set_max_send_fragment LK_SSL_set_max_send_fragment +SSL_set_min_proto_version LK_SSL_set_min_proto_version +SSL_set_mode LK_SSL_set_mode +SSL_set_msg_callback LK_SSL_set_msg_callback +SSL_set_msg_callback_arg LK_SSL_set_msg_callback_arg +SSL_set_mtu LK_SSL_set_mtu +SSL_set_ocsp_response LK_SSL_set_ocsp_response +SSL_set_options LK_SSL_set_options +SSL_set_permute_extensions LK_SSL_set_permute_extensions +SSL_set_private_key_method LK_SSL_set_private_key_method +SSL_set_psk_client_callback LK_SSL_set_psk_client_callback +SSL_set_psk_server_callback LK_SSL_set_psk_server_callback +SSL_set_purpose LK_SSL_set_purpose +SSL_set_quic_early_data_context LK_SSL_set_quic_early_data_context +SSL_set_quic_method LK_SSL_set_quic_method +SSL_set_quic_transport_params LK_SSL_set_quic_transport_params +SSL_set_quic_use_legacy_codepoint LK_SSL_set_quic_use_legacy_codepoint +SSL_set_quiet_shutdown LK_SSL_set_quiet_shutdown +SSL_set_read_ahead LK_SSL_set_read_ahead +SSL_set_renegotiate_mode LK_SSL_set_renegotiate_mode +SSL_set_retain_only_sha256_of_client_certs LK_SSL_set_retain_only_sha256_of_client_certs +SSL_set_rfd LK_SSL_set_rfd +SSL_set_session LK_SSL_set_session +SSL_set_session_id_context LK_SSL_set_session_id_context +SSL_set_shed_handshake_config LK_SSL_set_shed_handshake_config +SSL_set_shutdown LK_SSL_set_shutdown +SSL_set_signed_cert_timestamp_list LK_SSL_set_signed_cert_timestamp_list +SSL_set_signing_algorithm_prefs LK_SSL_set_signing_algorithm_prefs +SSL_set_srtp_profiles LK_SSL_set_srtp_profiles +SSL_set_state LK_SSL_set_state +SSL_set_strict_cipher_list LK_SSL_set_strict_cipher_list +SSL_set_tls_channel_id_enabled LK_SSL_set_tls_channel_id_enabled +SSL_set_tlsext_host_name LK_SSL_set_tlsext_host_name +SSL_set_tlsext_status_ocsp_resp LK_SSL_set_tlsext_status_ocsp_resp +SSL_set_tlsext_status_type LK_SSL_set_tlsext_status_type +SSL_set_tlsext_use_srtp LK_SSL_set_tlsext_use_srtp +SSL_set_tmp_dh LK_SSL_set_tmp_dh +SSL_set_tmp_dh_callback LK_SSL_set_tmp_dh_callback +SSL_set_tmp_ecdh LK_SSL_set_tmp_ecdh +SSL_set_tmp_rsa LK_SSL_set_tmp_rsa +SSL_set_tmp_rsa_callback LK_SSL_set_tmp_rsa_callback +SSL_set_trust LK_SSL_set_trust +SSL_set_verify LK_SSL_set_verify +SSL_set_verify_algorithm_prefs LK_SSL_set_verify_algorithm_prefs +SSL_set_verify_depth LK_SSL_set_verify_depth +SSL_set_wfd LK_SSL_set_wfd +SSL_shutdown LK_SSL_shutdown +SSL_state LK_SSL_state +SSL_state_string LK_SSL_state_string +SSL_state_string_long LK_SSL_state_string_long +SSL_total_renegotiations LK_SSL_total_renegotiations +SSL_use_PrivateKey LK_SSL_use_PrivateKey +SSL_use_PrivateKey_ASN1 LK_SSL_use_PrivateKey_ASN1 +SSL_use_PrivateKey_file LK_SSL_use_PrivateKey_file +SSL_use_RSAPrivateKey LK_SSL_use_RSAPrivateKey +SSL_use_RSAPrivateKey_ASN1 LK_SSL_use_RSAPrivateKey_ASN1 +SSL_use_RSAPrivateKey_file LK_SSL_use_RSAPrivateKey_file +SSL_use_certificate LK_SSL_use_certificate +SSL_use_certificate_ASN1 LK_SSL_use_certificate_ASN1 +SSL_use_certificate_file LK_SSL_use_certificate_file +SSL_use_psk_identity_hint LK_SSL_use_psk_identity_hint +SSL_used_hello_retry_request LK_SSL_used_hello_retry_request +SSL_version LK_SSL_version +SSL_want LK_SSL_want +SSL_was_key_usage_invalid LK_SSL_was_key_usage_invalid +SSL_write LK_SSL_write +SSLeay LK_SSLeay +SSLeay_version LK_SSLeay_version +SSLv23_client_method LK_SSLv23_client_method +SSLv23_method LK_SSLv23_method +SSLv23_server_method LK_SSLv23_server_method +STACK_OF LK_STACK_OF +TLS_client_method LK_TLS_client_method +TLS_method LK_TLS_method +TLS_server_method LK_TLS_server_method +TLS_with_buffers_method LK_TLS_with_buffers_method +TLSv1_1_client_method LK_TLSv1_1_client_method +TLSv1_1_method LK_TLSv1_1_method +TLSv1_1_server_method LK_TLSv1_1_server_method +TLSv1_2_client_method LK_TLSv1_2_client_method +TLSv1_2_method LK_TLSv1_2_method +TLSv1_2_server_method LK_TLSv1_2_server_method +TLSv1_client_method LK_TLSv1_client_method +TLSv1_method LK_TLSv1_method +TLSv1_server_method LK_TLSv1_server_method +TRUST_TOKEN_CLIENT_add_key LK_TRUST_TOKEN_CLIENT_add_key +TRUST_TOKEN_CLIENT_begin_issuance LK_TRUST_TOKEN_CLIENT_begin_issuance +TRUST_TOKEN_CLIENT_begin_issuance_over_message LK_TRUST_TOKEN_CLIENT_begin_issuance_over_message +TRUST_TOKEN_CLIENT_begin_redemption LK_TRUST_TOKEN_CLIENT_begin_redemption +TRUST_TOKEN_CLIENT_finish_redemption LK_TRUST_TOKEN_CLIENT_finish_redemption +TRUST_TOKEN_CLIENT_free LK_TRUST_TOKEN_CLIENT_free +TRUST_TOKEN_CLIENT_new LK_TRUST_TOKEN_CLIENT_new +TRUST_TOKEN_CLIENT_set_srr_key LK_TRUST_TOKEN_CLIENT_set_srr_key +TRUST_TOKEN_ISSUER_add_key LK_TRUST_TOKEN_ISSUER_add_key +TRUST_TOKEN_ISSUER_free LK_TRUST_TOKEN_ISSUER_free +TRUST_TOKEN_ISSUER_issue LK_TRUST_TOKEN_ISSUER_issue +TRUST_TOKEN_ISSUER_new LK_TRUST_TOKEN_ISSUER_new +TRUST_TOKEN_ISSUER_redeem LK_TRUST_TOKEN_ISSUER_redeem +TRUST_TOKEN_ISSUER_redeem_over_message LK_TRUST_TOKEN_ISSUER_redeem_over_message +TRUST_TOKEN_ISSUER_set_metadata_key LK_TRUST_TOKEN_ISSUER_set_metadata_key +TRUST_TOKEN_ISSUER_set_srr_key LK_TRUST_TOKEN_ISSUER_set_srr_key +TRUST_TOKEN_PRETOKEN_free LK_TRUST_TOKEN_PRETOKEN_free +TRUST_TOKEN_decode_private_metadata LK_TRUST_TOKEN_decode_private_metadata +TRUST_TOKEN_derive_key_from_secret LK_TRUST_TOKEN_derive_key_from_secret +TRUST_TOKEN_experiment_v1 LK_TRUST_TOKEN_experiment_v1 +TRUST_TOKEN_experiment_v2_pmb LK_TRUST_TOKEN_experiment_v2_pmb +TRUST_TOKEN_experiment_v2_voprf LK_TRUST_TOKEN_experiment_v2_voprf +TRUST_TOKEN_free LK_TRUST_TOKEN_free +TRUST_TOKEN_generate_key LK_TRUST_TOKEN_generate_key +TRUST_TOKEN_new LK_TRUST_TOKEN_new +TRUST_TOKEN_pst_v1_pmb LK_TRUST_TOKEN_pst_v1_pmb +TRUST_TOKEN_pst_v1_voprf LK_TRUST_TOKEN_pst_v1_voprf +TYPE_get_ex_data LK_TYPE_get_ex_data +TYPE_get_ex_new_index LK_TYPE_get_ex_new_index +TYPE_set_ex_data LK_TYPE_set_ex_data +X25519 LK_X25519 +X25519_keypair LK_X25519_keypair +X25519_public_from_private LK_X25519_public_from_private +X509V3_EXT_CRL_add_nconf LK_X509V3_EXT_CRL_add_nconf +X509V3_EXT_REQ_add_nconf LK_X509V3_EXT_REQ_add_nconf +X509V3_EXT_add LK_X509V3_EXT_add +X509V3_EXT_add_alias LK_X509V3_EXT_add_alias +X509V3_EXT_add_nconf LK_X509V3_EXT_add_nconf +X509V3_EXT_add_nconf_sk LK_X509V3_EXT_add_nconf_sk +X509V3_EXT_conf_nid LK_X509V3_EXT_conf_nid +X509V3_EXT_d2i LK_X509V3_EXT_d2i +X509V3_EXT_free LK_X509V3_EXT_free +X509V3_EXT_get LK_X509V3_EXT_get +X509V3_EXT_get_nid LK_X509V3_EXT_get_nid +X509V3_EXT_i2d LK_X509V3_EXT_i2d +X509V3_EXT_nconf LK_X509V3_EXT_nconf +X509V3_EXT_nconf_nid LK_X509V3_EXT_nconf_nid +X509V3_EXT_print LK_X509V3_EXT_print +X509V3_EXT_print_fp LK_X509V3_EXT_print_fp +X509V3_EXT_val_prn LK_X509V3_EXT_val_prn +X509V3_add1_i2d LK_X509V3_add1_i2d +X509V3_add_standard_extensions LK_X509V3_add_standard_extensions +X509V3_conf_free LK_X509V3_conf_free +X509V3_extensions_print LK_X509V3_extensions_print +X509V3_set_ctx LK_X509V3_set_ctx +X509V3_set_nconf LK_X509V3_set_nconf +X509_ALGOR_cmp LK_X509_ALGOR_cmp +X509_ALGOR_dup LK_X509_ALGOR_dup +X509_ALGOR_free LK_X509_ALGOR_free +X509_ALGOR_get0 LK_X509_ALGOR_get0 +X509_ALGOR_new LK_X509_ALGOR_new +X509_ALGOR_set0 LK_X509_ALGOR_set0 +X509_ALGOR_set_md LK_X509_ALGOR_set_md +X509_ATTRIBUTE_count LK_X509_ATTRIBUTE_count +X509_ATTRIBUTE_create LK_X509_ATTRIBUTE_create +X509_ATTRIBUTE_create_by_NID LK_X509_ATTRIBUTE_create_by_NID +X509_ATTRIBUTE_create_by_OBJ LK_X509_ATTRIBUTE_create_by_OBJ +X509_ATTRIBUTE_create_by_txt LK_X509_ATTRIBUTE_create_by_txt +X509_ATTRIBUTE_dup LK_X509_ATTRIBUTE_dup +X509_ATTRIBUTE_get0_data LK_X509_ATTRIBUTE_get0_data +X509_ATTRIBUTE_get0_object LK_X509_ATTRIBUTE_get0_object +X509_ATTRIBUTE_get0_type LK_X509_ATTRIBUTE_get0_type +X509_ATTRIBUTE_set1_data LK_X509_ATTRIBUTE_set1_data +X509_ATTRIBUTE_set1_object LK_X509_ATTRIBUTE_set1_object +X509_CRL_add0_revoked LK_X509_CRL_add0_revoked +X509_CRL_add1_ext_i2d LK_X509_CRL_add1_ext_i2d +X509_CRL_add_ext LK_X509_CRL_add_ext +X509_CRL_cmp LK_X509_CRL_cmp +X509_CRL_delete_ext LK_X509_CRL_delete_ext +X509_CRL_diff LK_X509_CRL_diff +X509_CRL_digest LK_X509_CRL_digest +X509_CRL_dup LK_X509_CRL_dup +X509_CRL_free LK_X509_CRL_free +X509_CRL_get0_by_cert LK_X509_CRL_get0_by_cert +X509_CRL_get0_by_serial LK_X509_CRL_get0_by_serial +X509_CRL_get0_extensions LK_X509_CRL_get0_extensions +X509_CRL_get0_lastUpdate LK_X509_CRL_get0_lastUpdate +X509_CRL_get0_nextUpdate LK_X509_CRL_get0_nextUpdate +X509_CRL_get0_signature LK_X509_CRL_get0_signature +X509_CRL_get_REVOKED LK_X509_CRL_get_REVOKED +X509_CRL_get_ext LK_X509_CRL_get_ext +X509_CRL_get_ext_by_NID LK_X509_CRL_get_ext_by_NID +X509_CRL_get_ext_by_OBJ LK_X509_CRL_get_ext_by_OBJ +X509_CRL_get_ext_by_critical LK_X509_CRL_get_ext_by_critical +X509_CRL_get_ext_count LK_X509_CRL_get_ext_count +X509_CRL_get_ext_d2i LK_X509_CRL_get_ext_d2i +X509_CRL_get_issuer LK_X509_CRL_get_issuer +X509_CRL_get_lastUpdate LK_X509_CRL_get_lastUpdate +X509_CRL_get_nextUpdate LK_X509_CRL_get_nextUpdate +X509_CRL_get_signature_nid LK_X509_CRL_get_signature_nid +X509_CRL_get_version LK_X509_CRL_get_version +X509_CRL_match LK_X509_CRL_match +X509_CRL_new LK_X509_CRL_new +X509_CRL_print LK_X509_CRL_print +X509_CRL_print_fp LK_X509_CRL_print_fp +X509_CRL_set1_lastUpdate LK_X509_CRL_set1_lastUpdate +X509_CRL_set1_nextUpdate LK_X509_CRL_set1_nextUpdate +X509_CRL_set1_signature_algo LK_X509_CRL_set1_signature_algo +X509_CRL_set1_signature_value LK_X509_CRL_set1_signature_value +X509_CRL_set_issuer_name LK_X509_CRL_set_issuer_name +X509_CRL_set_version LK_X509_CRL_set_version +X509_CRL_sign LK_X509_CRL_sign +X509_CRL_sign_ctx LK_X509_CRL_sign_ctx +X509_CRL_sort LK_X509_CRL_sort +X509_CRL_up_ref LK_X509_CRL_up_ref +X509_CRL_verify LK_X509_CRL_verify +X509_EXTENSION_create_by_NID LK_X509_EXTENSION_create_by_NID +X509_EXTENSION_create_by_OBJ LK_X509_EXTENSION_create_by_OBJ +X509_EXTENSION_dup LK_X509_EXTENSION_dup +X509_EXTENSION_free LK_X509_EXTENSION_free +X509_EXTENSION_get_critical LK_X509_EXTENSION_get_critical +X509_EXTENSION_get_data LK_X509_EXTENSION_get_data +X509_EXTENSION_get_object LK_X509_EXTENSION_get_object +X509_EXTENSION_new LK_X509_EXTENSION_new +X509_EXTENSION_set_critical LK_X509_EXTENSION_set_critical +X509_EXTENSION_set_data LK_X509_EXTENSION_set_data +X509_EXTENSION_set_object LK_X509_EXTENSION_set_object +X509_INFO_free LK_X509_INFO_free +X509_INFO_new LK_X509_INFO_new +X509_LOOKUP_by_subject LK_X509_LOOKUP_by_subject +X509_LOOKUP_ctrl LK_X509_LOOKUP_ctrl +X509_LOOKUP_file LK_X509_LOOKUP_file +X509_LOOKUP_free LK_X509_LOOKUP_free +X509_LOOKUP_hash_dir LK_X509_LOOKUP_hash_dir +X509_LOOKUP_init LK_X509_LOOKUP_init +X509_LOOKUP_new LK_X509_LOOKUP_new +X509_LOOKUP_shutdown LK_X509_LOOKUP_shutdown +X509_NAME_ENTRY_create_by_NID LK_X509_NAME_ENTRY_create_by_NID +X509_NAME_ENTRY_create_by_OBJ LK_X509_NAME_ENTRY_create_by_OBJ +X509_NAME_ENTRY_create_by_txt LK_X509_NAME_ENTRY_create_by_txt +X509_NAME_ENTRY_dup LK_X509_NAME_ENTRY_dup +X509_NAME_ENTRY_free LK_X509_NAME_ENTRY_free +X509_NAME_ENTRY_get_data LK_X509_NAME_ENTRY_get_data +X509_NAME_ENTRY_get_object LK_X509_NAME_ENTRY_get_object +X509_NAME_ENTRY_new LK_X509_NAME_ENTRY_new +X509_NAME_ENTRY_set LK_X509_NAME_ENTRY_set +X509_NAME_ENTRY_set_data LK_X509_NAME_ENTRY_set_data +X509_NAME_ENTRY_set_object LK_X509_NAME_ENTRY_set_object +X509_NAME_add_entry LK_X509_NAME_add_entry +X509_NAME_add_entry_by_NID LK_X509_NAME_add_entry_by_NID +X509_NAME_add_entry_by_OBJ LK_X509_NAME_add_entry_by_OBJ +X509_NAME_add_entry_by_txt LK_X509_NAME_add_entry_by_txt +X509_NAME_cmp LK_X509_NAME_cmp +X509_NAME_delete_entry LK_X509_NAME_delete_entry +X509_NAME_digest LK_X509_NAME_digest +X509_NAME_dup LK_X509_NAME_dup +X509_NAME_entry_count LK_X509_NAME_entry_count +X509_NAME_free LK_X509_NAME_free +X509_NAME_get0_der LK_X509_NAME_get0_der +X509_NAME_get_entry LK_X509_NAME_get_entry +X509_NAME_get_index_by_NID LK_X509_NAME_get_index_by_NID +X509_NAME_get_index_by_OBJ LK_X509_NAME_get_index_by_OBJ +X509_NAME_get_text_by_NID LK_X509_NAME_get_text_by_NID +X509_NAME_get_text_by_OBJ LK_X509_NAME_get_text_by_OBJ +X509_NAME_hash LK_X509_NAME_hash +X509_NAME_hash_old LK_X509_NAME_hash_old +X509_NAME_new LK_X509_NAME_new +X509_NAME_oneline LK_X509_NAME_oneline +X509_NAME_print LK_X509_NAME_print +X509_NAME_print_ex LK_X509_NAME_print_ex +X509_NAME_print_ex_fp LK_X509_NAME_print_ex_fp +X509_NAME_set LK_X509_NAME_set +X509_OBJECT_free_contents LK_X509_OBJECT_free_contents +X509_OBJECT_get0_X509 LK_X509_OBJECT_get0_X509 +X509_OBJECT_get_type LK_X509_OBJECT_get_type +X509_OBJECT_idx_by_subject LK_X509_OBJECT_idx_by_subject +X509_OBJECT_retrieve_by_subject LK_X509_OBJECT_retrieve_by_subject +X509_OBJECT_retrieve_match LK_X509_OBJECT_retrieve_match +X509_OBJECT_up_ref_count LK_X509_OBJECT_up_ref_count +X509_PKEY_free LK_X509_PKEY_free +X509_PKEY_new LK_X509_PKEY_new +X509_PUBKEY_get LK_X509_PUBKEY_get +X509_PUBKEY_get0_param LK_X509_PUBKEY_get0_param +X509_PUBKEY_get0_public_key LK_X509_PUBKEY_get0_public_key +X509_PUBKEY_set LK_X509_PUBKEY_set +X509_PUBKEY_set0_param LK_X509_PUBKEY_set0_param +X509_PURPOSE_add LK_X509_PURPOSE_add +X509_PURPOSE_cleanup LK_X509_PURPOSE_cleanup +X509_PURPOSE_get0 LK_X509_PURPOSE_get0 +X509_PURPOSE_get0_name LK_X509_PURPOSE_get0_name +X509_PURPOSE_get0_sname LK_X509_PURPOSE_get0_sname +X509_PURPOSE_get_by_id LK_X509_PURPOSE_get_by_id +X509_PURPOSE_get_by_sname LK_X509_PURPOSE_get_by_sname +X509_PURPOSE_get_count LK_X509_PURPOSE_get_count +X509_PURPOSE_get_id LK_X509_PURPOSE_get_id +X509_PURPOSE_get_trust LK_X509_PURPOSE_get_trust +X509_PURPOSE_set LK_X509_PURPOSE_set +X509_REQ_add1_attr LK_X509_REQ_add1_attr +X509_REQ_add1_attr_by_NID LK_X509_REQ_add1_attr_by_NID +X509_REQ_add1_attr_by_OBJ LK_X509_REQ_add1_attr_by_OBJ +X509_REQ_add1_attr_by_txt LK_X509_REQ_add1_attr_by_txt +X509_REQ_add_extensions LK_X509_REQ_add_extensions +X509_REQ_add_extensions_nid LK_X509_REQ_add_extensions_nid +X509_REQ_check_private_key LK_X509_REQ_check_private_key +X509_REQ_delete_attr LK_X509_REQ_delete_attr +X509_REQ_digest LK_X509_REQ_digest +X509_REQ_dup LK_X509_REQ_dup +X509_REQ_extension_nid LK_X509_REQ_extension_nid +X509_REQ_free LK_X509_REQ_free +X509_REQ_get0_signature LK_X509_REQ_get0_signature +X509_REQ_get1_email LK_X509_REQ_get1_email +X509_REQ_get_attr LK_X509_REQ_get_attr +X509_REQ_get_attr_by_NID LK_X509_REQ_get_attr_by_NID +X509_REQ_get_attr_by_OBJ LK_X509_REQ_get_attr_by_OBJ +X509_REQ_get_attr_count LK_X509_REQ_get_attr_count +X509_REQ_get_extensions LK_X509_REQ_get_extensions +X509_REQ_get_pubkey LK_X509_REQ_get_pubkey +X509_REQ_get_signature_nid LK_X509_REQ_get_signature_nid +X509_REQ_get_subject_name LK_X509_REQ_get_subject_name +X509_REQ_get_version LK_X509_REQ_get_version +X509_REQ_new LK_X509_REQ_new +X509_REQ_print LK_X509_REQ_print +X509_REQ_print_ex LK_X509_REQ_print_ex +X509_REQ_print_fp LK_X509_REQ_print_fp +X509_REQ_set1_signature_algo LK_X509_REQ_set1_signature_algo +X509_REQ_set1_signature_value LK_X509_REQ_set1_signature_value +X509_REQ_set_pubkey LK_X509_REQ_set_pubkey +X509_REQ_set_subject_name LK_X509_REQ_set_subject_name +X509_REQ_set_version LK_X509_REQ_set_version +X509_REQ_sign LK_X509_REQ_sign +X509_REQ_sign_ctx LK_X509_REQ_sign_ctx +X509_REQ_verify LK_X509_REQ_verify +X509_REVOKED_add1_ext_i2d LK_X509_REVOKED_add1_ext_i2d +X509_REVOKED_add_ext LK_X509_REVOKED_add_ext +X509_REVOKED_delete_ext LK_X509_REVOKED_delete_ext +X509_REVOKED_dup LK_X509_REVOKED_dup +X509_REVOKED_get0_extensions LK_X509_REVOKED_get0_extensions +X509_REVOKED_get0_revocationDate LK_X509_REVOKED_get0_revocationDate +X509_REVOKED_get0_serialNumber LK_X509_REVOKED_get0_serialNumber +X509_REVOKED_get_ext LK_X509_REVOKED_get_ext +X509_REVOKED_get_ext_by_NID LK_X509_REVOKED_get_ext_by_NID +X509_REVOKED_get_ext_by_OBJ LK_X509_REVOKED_get_ext_by_OBJ +X509_REVOKED_get_ext_by_critical LK_X509_REVOKED_get_ext_by_critical +X509_REVOKED_get_ext_count LK_X509_REVOKED_get_ext_count +X509_REVOKED_get_ext_d2i LK_X509_REVOKED_get_ext_d2i +X509_REVOKED_set_revocationDate LK_X509_REVOKED_set_revocationDate +X509_REVOKED_set_serialNumber LK_X509_REVOKED_set_serialNumber +X509_SIG_get0 LK_X509_SIG_get0 +X509_SIG_getm LK_X509_SIG_getm +X509_STORE_CTX_cleanup LK_X509_STORE_CTX_cleanup +X509_STORE_CTX_free LK_X509_STORE_CTX_free +X509_STORE_CTX_get0_cert LK_X509_STORE_CTX_get0_cert +X509_STORE_CTX_get0_chain LK_X509_STORE_CTX_get0_chain +X509_STORE_CTX_get0_current_crl LK_X509_STORE_CTX_get0_current_crl +X509_STORE_CTX_get0_current_issuer LK_X509_STORE_CTX_get0_current_issuer +X509_STORE_CTX_get0_param LK_X509_STORE_CTX_get0_param +X509_STORE_CTX_get0_parent_ctx LK_X509_STORE_CTX_get0_parent_ctx +X509_STORE_CTX_get0_store LK_X509_STORE_CTX_get0_store +X509_STORE_CTX_get0_untrusted LK_X509_STORE_CTX_get0_untrusted +X509_STORE_CTX_get1_chain LK_X509_STORE_CTX_get1_chain +X509_STORE_CTX_get1_issuer LK_X509_STORE_CTX_get1_issuer +X509_STORE_CTX_get_chain LK_X509_STORE_CTX_get_chain +X509_STORE_CTX_get_current_cert LK_X509_STORE_CTX_get_current_cert +X509_STORE_CTX_get_error LK_X509_STORE_CTX_get_error +X509_STORE_CTX_get_error_depth LK_X509_STORE_CTX_get_error_depth +X509_STORE_CTX_get_ex_data LK_X509_STORE_CTX_get_ex_data +X509_STORE_CTX_get_ex_new_index LK_X509_STORE_CTX_get_ex_new_index +X509_STORE_CTX_init LK_X509_STORE_CTX_init +X509_STORE_CTX_new LK_X509_STORE_CTX_new +X509_STORE_CTX_purpose_inherit LK_X509_STORE_CTX_purpose_inherit +X509_STORE_CTX_set0_crls LK_X509_STORE_CTX_set0_crls +X509_STORE_CTX_set0_param LK_X509_STORE_CTX_set0_param +X509_STORE_CTX_set0_trusted_stack LK_X509_STORE_CTX_set0_trusted_stack +X509_STORE_CTX_set_cert LK_X509_STORE_CTX_set_cert +X509_STORE_CTX_set_chain LK_X509_STORE_CTX_set_chain +X509_STORE_CTX_set_default LK_X509_STORE_CTX_set_default +X509_STORE_CTX_set_depth LK_X509_STORE_CTX_set_depth +X509_STORE_CTX_set_error LK_X509_STORE_CTX_set_error +X509_STORE_CTX_set_ex_data LK_X509_STORE_CTX_set_ex_data +X509_STORE_CTX_set_flags LK_X509_STORE_CTX_set_flags +X509_STORE_CTX_set_purpose LK_X509_STORE_CTX_set_purpose +X509_STORE_CTX_set_time LK_X509_STORE_CTX_set_time +X509_STORE_CTX_set_time_posix LK_X509_STORE_CTX_set_time_posix +X509_STORE_CTX_set_trust LK_X509_STORE_CTX_set_trust +X509_STORE_CTX_set_verify LK_X509_STORE_CTX_set_verify +X509_STORE_CTX_set_verify_cb LK_X509_STORE_CTX_set_verify_cb +X509_STORE_CTX_trusted_stack LK_X509_STORE_CTX_trusted_stack +X509_STORE_CTX_zero LK_X509_STORE_CTX_zero +X509_STORE_add_cert LK_X509_STORE_add_cert +X509_STORE_add_crl LK_X509_STORE_add_crl +X509_STORE_add_lookup LK_X509_STORE_add_lookup +X509_STORE_free LK_X509_STORE_free +X509_STORE_get0_objects LK_X509_STORE_get0_objects +X509_STORE_get0_param LK_X509_STORE_get0_param +X509_STORE_get1_certs LK_X509_STORE_get1_certs +X509_STORE_get1_crls LK_X509_STORE_get1_crls +X509_STORE_get_by_subject LK_X509_STORE_get_by_subject +X509_STORE_get_cert_crl LK_X509_STORE_get_cert_crl +X509_STORE_get_check_crl LK_X509_STORE_get_check_crl +X509_STORE_get_check_issued LK_X509_STORE_get_check_issued +X509_STORE_get_check_revocation LK_X509_STORE_get_check_revocation +X509_STORE_get_cleanup LK_X509_STORE_get_cleanup +X509_STORE_get_get_crl LK_X509_STORE_get_get_crl +X509_STORE_get_get_issuer LK_X509_STORE_get_get_issuer +X509_STORE_get_lookup_certs LK_X509_STORE_get_lookup_certs +X509_STORE_get_lookup_crls LK_X509_STORE_get_lookup_crls +X509_STORE_get_verify LK_X509_STORE_get_verify +X509_STORE_get_verify_cb LK_X509_STORE_get_verify_cb +X509_STORE_load_locations LK_X509_STORE_load_locations +X509_STORE_new LK_X509_STORE_new +X509_STORE_set1_param LK_X509_STORE_set1_param +X509_STORE_set_cert_crl LK_X509_STORE_set_cert_crl +X509_STORE_set_check_crl LK_X509_STORE_set_check_crl +X509_STORE_set_check_issued LK_X509_STORE_set_check_issued +X509_STORE_set_check_revocation LK_X509_STORE_set_check_revocation +X509_STORE_set_cleanup LK_X509_STORE_set_cleanup +X509_STORE_set_default_paths LK_X509_STORE_set_default_paths +X509_STORE_set_depth LK_X509_STORE_set_depth +X509_STORE_set_flags LK_X509_STORE_set_flags +X509_STORE_set_get_crl LK_X509_STORE_set_get_crl +X509_STORE_set_get_issuer LK_X509_STORE_set_get_issuer +X509_STORE_set_lookup_certs LK_X509_STORE_set_lookup_certs +X509_STORE_set_lookup_crls LK_X509_STORE_set_lookup_crls +X509_STORE_set_purpose LK_X509_STORE_set_purpose +X509_STORE_set_trust LK_X509_STORE_set_trust +X509_STORE_set_verify LK_X509_STORE_set_verify +X509_STORE_set_verify_cb LK_X509_STORE_set_verify_cb +X509_STORE_up_ref LK_X509_STORE_up_ref +X509_TRUST_add LK_X509_TRUST_add +X509_TRUST_cleanup LK_X509_TRUST_cleanup +X509_TRUST_get0 LK_X509_TRUST_get0 +X509_TRUST_get0_name LK_X509_TRUST_get0_name +X509_TRUST_get_by_id LK_X509_TRUST_get_by_id +X509_TRUST_get_count LK_X509_TRUST_get_count +X509_TRUST_get_flags LK_X509_TRUST_get_flags +X509_TRUST_get_trust LK_X509_TRUST_get_trust +X509_TRUST_set LK_X509_TRUST_set +X509_VERIFY_PARAM_add0_policy LK_X509_VERIFY_PARAM_add0_policy +X509_VERIFY_PARAM_add1_host LK_X509_VERIFY_PARAM_add1_host +X509_VERIFY_PARAM_clear_flags LK_X509_VERIFY_PARAM_clear_flags +X509_VERIFY_PARAM_free LK_X509_VERIFY_PARAM_free +X509_VERIFY_PARAM_get0_name LK_X509_VERIFY_PARAM_get0_name +X509_VERIFY_PARAM_get0_peername LK_X509_VERIFY_PARAM_get0_peername +X509_VERIFY_PARAM_get_depth LK_X509_VERIFY_PARAM_get_depth +X509_VERIFY_PARAM_get_flags LK_X509_VERIFY_PARAM_get_flags +X509_VERIFY_PARAM_inherit LK_X509_VERIFY_PARAM_inherit +X509_VERIFY_PARAM_lookup LK_X509_VERIFY_PARAM_lookup +X509_VERIFY_PARAM_new LK_X509_VERIFY_PARAM_new +X509_VERIFY_PARAM_set1 LK_X509_VERIFY_PARAM_set1 +X509_VERIFY_PARAM_set1_email LK_X509_VERIFY_PARAM_set1_email +X509_VERIFY_PARAM_set1_host LK_X509_VERIFY_PARAM_set1_host +X509_VERIFY_PARAM_set1_ip LK_X509_VERIFY_PARAM_set1_ip +X509_VERIFY_PARAM_set1_ip_asc LK_X509_VERIFY_PARAM_set1_ip_asc +X509_VERIFY_PARAM_set1_name LK_X509_VERIFY_PARAM_set1_name +X509_VERIFY_PARAM_set1_policies LK_X509_VERIFY_PARAM_set1_policies +X509_VERIFY_PARAM_set_depth LK_X509_VERIFY_PARAM_set_depth +X509_VERIFY_PARAM_set_flags LK_X509_VERIFY_PARAM_set_flags +X509_VERIFY_PARAM_set_hostflags LK_X509_VERIFY_PARAM_set_hostflags +X509_VERIFY_PARAM_set_purpose LK_X509_VERIFY_PARAM_set_purpose +X509_VERIFY_PARAM_set_time LK_X509_VERIFY_PARAM_set_time +X509_VERIFY_PARAM_set_time_posix LK_X509_VERIFY_PARAM_set_time_posix +X509_VERIFY_PARAM_set_trust LK_X509_VERIFY_PARAM_set_trust +X509_add1_ext_i2d LK_X509_add1_ext_i2d +X509_add1_reject_object LK_X509_add1_reject_object +X509_add1_trust_object LK_X509_add1_trust_object +X509_add_ext LK_X509_add_ext +X509_alias_get0 LK_X509_alias_get0 +X509_alias_set1 LK_X509_alias_set1 +X509_chain_up_ref LK_X509_chain_up_ref +X509_check_akid LK_X509_check_akid +X509_check_ca LK_X509_check_ca +X509_check_email LK_X509_check_email +X509_check_host LK_X509_check_host +X509_check_ip LK_X509_check_ip +X509_check_ip_asc LK_X509_check_ip_asc +X509_check_issued LK_X509_check_issued +X509_check_private_key LK_X509_check_private_key +X509_check_purpose LK_X509_check_purpose +X509_check_trust LK_X509_check_trust +X509_cmp LK_X509_cmp +X509_cmp_current_time LK_X509_cmp_current_time +X509_cmp_time LK_X509_cmp_time +X509_cmp_time_posix LK_X509_cmp_time_posix +X509_delete_ext LK_X509_delete_ext +X509_digest LK_X509_digest +X509_dup LK_X509_dup +X509_email_free LK_X509_email_free +X509_free LK_X509_free +X509_get0_authority_issuer LK_X509_get0_authority_issuer +X509_get0_authority_key_id LK_X509_get0_authority_key_id +X509_get0_authority_serial LK_X509_get0_authority_serial +X509_get0_extensions LK_X509_get0_extensions +X509_get0_notAfter LK_X509_get0_notAfter +X509_get0_notBefore LK_X509_get0_notBefore +X509_get0_pubkey_bitstr LK_X509_get0_pubkey_bitstr +X509_get0_serialNumber LK_X509_get0_serialNumber +X509_get0_signature LK_X509_get0_signature +X509_get0_subject_key_id LK_X509_get0_subject_key_id +X509_get0_tbs_sigalg LK_X509_get0_tbs_sigalg +X509_get0_uids LK_X509_get0_uids +X509_get1_email LK_X509_get1_email +X509_get1_ocsp LK_X509_get1_ocsp +X509_get_X509_PUBKEY LK_X509_get_X509_PUBKEY +X509_get_default_cert_area LK_X509_get_default_cert_area +X509_get_default_cert_dir LK_X509_get_default_cert_dir +X509_get_default_cert_dir_env LK_X509_get_default_cert_dir_env +X509_get_default_cert_file LK_X509_get_default_cert_file +X509_get_default_cert_file_env LK_X509_get_default_cert_file_env +X509_get_default_private_dir LK_X509_get_default_private_dir +X509_get_ex_data LK_X509_get_ex_data +X509_get_ex_new_index LK_X509_get_ex_new_index +X509_get_ext LK_X509_get_ext +X509_get_ext_by_NID LK_X509_get_ext_by_NID +X509_get_ext_by_OBJ LK_X509_get_ext_by_OBJ +X509_get_ext_by_critical LK_X509_get_ext_by_critical +X509_get_ext_count LK_X509_get_ext_count +X509_get_ext_d2i LK_X509_get_ext_d2i +X509_get_extended_key_usage LK_X509_get_extended_key_usage +X509_get_extension_flags LK_X509_get_extension_flags +X509_get_issuer_name LK_X509_get_issuer_name +X509_get_key_usage LK_X509_get_key_usage +X509_get_notAfter LK_X509_get_notAfter +X509_get_notBefore LK_X509_get_notBefore +X509_get_pathlen LK_X509_get_pathlen +X509_get_pubkey LK_X509_get_pubkey +X509_get_serialNumber LK_X509_get_serialNumber +X509_get_signature_nid LK_X509_get_signature_nid +X509_get_subject_name LK_X509_get_subject_name +X509_get_version LK_X509_get_version +X509_getm_notAfter LK_X509_getm_notAfter +X509_getm_notBefore LK_X509_getm_notBefore +X509_gmtime_adj LK_X509_gmtime_adj +X509_issuer_name_cmp LK_X509_issuer_name_cmp +X509_issuer_name_hash LK_X509_issuer_name_hash +X509_issuer_name_hash_old LK_X509_issuer_name_hash_old +X509_keyid_get0 LK_X509_keyid_get0 +X509_keyid_set1 LK_X509_keyid_set1 +X509_load_cert_crl_file LK_X509_load_cert_crl_file +X509_load_cert_file LK_X509_load_cert_file +X509_load_crl_file LK_X509_load_crl_file +X509_new LK_X509_new +X509_parse_from_buffer LK_X509_parse_from_buffer +X509_print LK_X509_print +X509_print_ex LK_X509_print_ex +X509_print_ex_fp LK_X509_print_ex_fp +X509_print_fp LK_X509_print_fp +X509_pubkey_digest LK_X509_pubkey_digest +X509_reject_clear LK_X509_reject_clear +X509_set1_notAfter LK_X509_set1_notAfter +X509_set1_notBefore LK_X509_set1_notBefore +X509_set1_signature_algo LK_X509_set1_signature_algo +X509_set1_signature_value LK_X509_set1_signature_value +X509_set_ex_data LK_X509_set_ex_data +X509_set_issuer_name LK_X509_set_issuer_name +X509_set_notAfter LK_X509_set_notAfter +X509_set_notBefore LK_X509_set_notBefore +X509_set_pubkey LK_X509_set_pubkey +X509_set_serialNumber LK_X509_set_serialNumber +X509_set_subject_name LK_X509_set_subject_name +X509_set_version LK_X509_set_version +X509_sign LK_X509_sign +X509_sign_ctx LK_X509_sign_ctx +X509_signature_dump LK_X509_signature_dump +X509_signature_print LK_X509_signature_print +X509_subject_name_cmp LK_X509_subject_name_cmp +X509_subject_name_hash LK_X509_subject_name_hash +X509_subject_name_hash_old LK_X509_subject_name_hash_old +X509_supported_extension LK_X509_supported_extension +X509_time_adj LK_X509_time_adj +X509_time_adj_ex LK_X509_time_adj_ex +X509_trust_clear LK_X509_trust_clear +X509_up_ref LK_X509_up_ref +X509_verify LK_X509_verify +X509_verify_cert LK_X509_verify_cert +X509_verify_cert_error_string LK_X509_verify_cert_error_string +X509at_add1_attr LK_X509at_add1_attr +X509at_add1_attr_by_NID LK_X509at_add1_attr_by_NID +X509at_add1_attr_by_OBJ LK_X509at_add1_attr_by_OBJ +X509at_add1_attr_by_txt LK_X509at_add1_attr_by_txt +X509at_delete_attr LK_X509at_delete_attr +X509at_get_attr LK_X509at_get_attr +X509v3_add_ext LK_X509v3_add_ext +X509v3_delete_ext LK_X509v3_delete_ext +a2i_GENERAL_NAME LK_a2i_GENERAL_NAME +a2i_IPADDRESS LK_a2i_IPADDRESS +a2i_IPADDRESS_NC LK_a2i_IPADDRESS_NC +asn1_generalizedtime_to_tm LK_asn1_generalizedtime_to_tm +asn1_get_string_table_for_testing LK_asn1_get_string_table_for_testing +asn1_utctime_to_tm LK_asn1_utctime_to_tm +bn_abs_sub_consttime LK_bn_abs_sub_consttime +bn_div_consttime LK_bn_div_consttime +bn_is_relatively_prime LK_bn_is_relatively_prime +bn_lcm_consttime LK_bn_lcm_consttime +bn_miller_rabin_init LK_bn_miller_rabin_init +bn_miller_rabin_iteration LK_bn_miller_rabin_iteration +bn_mod_inverse_consttime LK_bn_mod_inverse_consttime +bn_mod_u16_consttime LK_bn_mod_u16_consttime +bn_resize_words LK_bn_resize_words +bn_rshift_secret_shift LK_bn_rshift_secret_shift +c2i_ASN1_BIT_STRING LK_c2i_ASN1_BIT_STRING +c2i_ASN1_INTEGER LK_c2i_ASN1_INTEGER +c2i_ASN1_OBJECT LK_c2i_ASN1_OBJECT +cbb_add_latin1 LK_cbb_add_latin1 +cbb_add_ucs2_be LK_cbb_add_ucs2_be +cbb_add_utf32_be LK_cbb_add_utf32_be +cbb_add_utf8 LK_cbb_add_utf8 +cbb_get_utf8_len LK_cbb_get_utf8_len +cbs_get_latin1 LK_cbs_get_latin1 +cbs_get_ucs2_be LK_cbs_get_ucs2_be +cbs_get_utf32_be LK_cbs_get_utf32_be +cbs_get_utf8 LK_cbs_get_utf8 +d2i_ASN1_BIT_STRING LK_d2i_ASN1_BIT_STRING +d2i_ASN1_BMPSTRING LK_d2i_ASN1_BMPSTRING +d2i_ASN1_BOOLEAN LK_d2i_ASN1_BOOLEAN +d2i_ASN1_ENUMERATED LK_d2i_ASN1_ENUMERATED +d2i_ASN1_GENERALIZEDTIME LK_d2i_ASN1_GENERALIZEDTIME +d2i_ASN1_GENERALSTRING LK_d2i_ASN1_GENERALSTRING +d2i_ASN1_IA5STRING LK_d2i_ASN1_IA5STRING +d2i_ASN1_INTEGER LK_d2i_ASN1_INTEGER +d2i_ASN1_NULL LK_d2i_ASN1_NULL +d2i_ASN1_OBJECT LK_d2i_ASN1_OBJECT +d2i_ASN1_OCTET_STRING LK_d2i_ASN1_OCTET_STRING +d2i_ASN1_PRINTABLE LK_d2i_ASN1_PRINTABLE +d2i_ASN1_PRINTABLESTRING LK_d2i_ASN1_PRINTABLESTRING +d2i_ASN1_SEQUENCE_ANY LK_d2i_ASN1_SEQUENCE_ANY +d2i_ASN1_SET_ANY LK_d2i_ASN1_SET_ANY +d2i_ASN1_T61STRING LK_d2i_ASN1_T61STRING +d2i_ASN1_TIME LK_d2i_ASN1_TIME +d2i_ASN1_TYPE LK_d2i_ASN1_TYPE +d2i_ASN1_UNIVERSALSTRING LK_d2i_ASN1_UNIVERSALSTRING +d2i_ASN1_UTCTIME LK_d2i_ASN1_UTCTIME +d2i_ASN1_UTF8STRING LK_d2i_ASN1_UTF8STRING +d2i_ASN1_VISIBLESTRING LK_d2i_ASN1_VISIBLESTRING +d2i_AutoPrivateKey LK_d2i_AutoPrivateKey +d2i_DHparams LK_d2i_DHparams +d2i_DHparams_bio LK_d2i_DHparams_bio +d2i_DIRECTORYSTRING LK_d2i_DIRECTORYSTRING +d2i_DISPLAYTEXT LK_d2i_DISPLAYTEXT +d2i_DSAPrivateKey LK_d2i_DSAPrivateKey +d2i_DSAPrivateKey_bio LK_d2i_DSAPrivateKey_bio +d2i_DSAPrivateKey_fp LK_d2i_DSAPrivateKey_fp +d2i_DSAPublicKey LK_d2i_DSAPublicKey +d2i_DSA_PUBKEY LK_d2i_DSA_PUBKEY +d2i_DSA_PUBKEY_bio LK_d2i_DSA_PUBKEY_bio +d2i_DSA_PUBKEY_fp LK_d2i_DSA_PUBKEY_fp +d2i_DSA_SIG LK_d2i_DSA_SIG +d2i_DSAparams LK_d2i_DSAparams +d2i_ECDSA_SIG LK_d2i_ECDSA_SIG +d2i_ECParameters LK_d2i_ECParameters +d2i_ECPrivateKey LK_d2i_ECPrivateKey +d2i_ECPrivateKey_bio LK_d2i_ECPrivateKey_bio +d2i_ECPrivateKey_fp LK_d2i_ECPrivateKey_fp +d2i_EC_PUBKEY LK_d2i_EC_PUBKEY +d2i_EC_PUBKEY_bio LK_d2i_EC_PUBKEY_bio +d2i_EC_PUBKEY_fp LK_d2i_EC_PUBKEY_fp +d2i_PKCS12 LK_d2i_PKCS12 +d2i_PKCS12_bio LK_d2i_PKCS12_bio +d2i_PKCS12_fp LK_d2i_PKCS12_fp +d2i_PKCS7 LK_d2i_PKCS7 +d2i_PKCS7_bio LK_d2i_PKCS7_bio +d2i_PKCS8PrivateKey_bio LK_d2i_PKCS8PrivateKey_bio +d2i_PKCS8PrivateKey_fp LK_d2i_PKCS8PrivateKey_fp +d2i_PKCS8_PRIV_KEY_INFO_bio LK_d2i_PKCS8_PRIV_KEY_INFO_bio +d2i_PKCS8_PRIV_KEY_INFO_fp LK_d2i_PKCS8_PRIV_KEY_INFO_fp +d2i_PKCS8_bio LK_d2i_PKCS8_bio +d2i_PKCS8_fp LK_d2i_PKCS8_fp +d2i_PUBKEY LK_d2i_PUBKEY +d2i_PUBKEY_bio LK_d2i_PUBKEY_bio +d2i_PUBKEY_fp LK_d2i_PUBKEY_fp +d2i_PrivateKey LK_d2i_PrivateKey +d2i_PrivateKey_bio LK_d2i_PrivateKey_bio +d2i_PrivateKey_fp LK_d2i_PrivateKey_fp +d2i_PublicKey LK_d2i_PublicKey +d2i_RSAPrivateKey LK_d2i_RSAPrivateKey +d2i_RSAPrivateKey_bio LK_d2i_RSAPrivateKey_bio +d2i_RSAPrivateKey_fp LK_d2i_RSAPrivateKey_fp +d2i_RSAPublicKey LK_d2i_RSAPublicKey +d2i_RSAPublicKey_bio LK_d2i_RSAPublicKey_bio +d2i_RSAPublicKey_fp LK_d2i_RSAPublicKey_fp +d2i_RSA_PUBKEY LK_d2i_RSA_PUBKEY +d2i_RSA_PUBKEY_bio LK_d2i_RSA_PUBKEY_bio +d2i_RSA_PUBKEY_fp LK_d2i_RSA_PUBKEY_fp +d2i_SSL_SESSION LK_d2i_SSL_SESSION +d2i_SSL_SESSION_bio LK_d2i_SSL_SESSION_bio +d2i_X509 LK_d2i_X509 +d2i_X509_ALGOR LK_d2i_X509_ALGOR +d2i_X509_AUX LK_d2i_X509_AUX +d2i_X509_CRL LK_d2i_X509_CRL +d2i_X509_CRL_bio LK_d2i_X509_CRL_bio +d2i_X509_CRL_fp LK_d2i_X509_CRL_fp +d2i_X509_EXTENSION LK_d2i_X509_EXTENSION +d2i_X509_EXTENSIONS LK_d2i_X509_EXTENSIONS +d2i_X509_NAME LK_d2i_X509_NAME +d2i_X509_NAME_ENTRY LK_d2i_X509_NAME_ENTRY +d2i_X509_REQ LK_d2i_X509_REQ +d2i_X509_REQ_bio LK_d2i_X509_REQ_bio +d2i_X509_REQ_fp LK_d2i_X509_REQ_fp +d2i_X509_bio LK_d2i_X509_bio +d2i_X509_fp LK_d2i_X509_fp +ec_bignum_to_scalar LK_ec_bignum_to_scalar +ec_hash_to_curve_p256_xmd_sha256_sswu LK_ec_hash_to_curve_p256_xmd_sha256_sswu +ec_hash_to_curve_p384_xmd_sha384_sswu LK_ec_hash_to_curve_p384_xmd_sha384_sswu +ec_hash_to_curve_p384_xmd_sha512_sswu_draft07 LK_ec_hash_to_curve_p384_xmd_sha512_sswu_draft07 +ec_hash_to_scalar_p384_xmd_sha384 LK_ec_hash_to_scalar_p384_xmd_sha384 +ec_hash_to_scalar_p384_xmd_sha512_draft07 LK_ec_hash_to_scalar_p384_xmd_sha512_draft07 +ec_point_mul_scalar_public LK_ec_point_mul_scalar_public +ec_scalar_to_bytes LK_ec_scalar_to_bytes +i2a_ACCESS_DESCRIPTION LK_i2a_ACCESS_DESCRIPTION +i2a_ASN1_ENUMERATED LK_i2a_ASN1_ENUMERATED +i2a_ASN1_INTEGER LK_i2a_ASN1_INTEGER +i2a_ASN1_OBJECT LK_i2a_ASN1_OBJECT +i2a_ASN1_STRING LK_i2a_ASN1_STRING +i2c_ASN1_BIT_STRING LK_i2c_ASN1_BIT_STRING +i2c_ASN1_INTEGER LK_i2c_ASN1_INTEGER +i2d_ASN1_BIT_STRING LK_i2d_ASN1_BIT_STRING +i2d_ASN1_BMPSTRING LK_i2d_ASN1_BMPSTRING +i2d_ASN1_BOOLEAN LK_i2d_ASN1_BOOLEAN +i2d_ASN1_ENUMERATED LK_i2d_ASN1_ENUMERATED +i2d_ASN1_GENERALIZEDTIME LK_i2d_ASN1_GENERALIZEDTIME +i2d_ASN1_GENERALSTRING LK_i2d_ASN1_GENERALSTRING +i2d_ASN1_IA5STRING LK_i2d_ASN1_IA5STRING +i2d_ASN1_INTEGER LK_i2d_ASN1_INTEGER +i2d_ASN1_NULL LK_i2d_ASN1_NULL +i2d_ASN1_OBJECT LK_i2d_ASN1_OBJECT +i2d_ASN1_OCTET_STRING LK_i2d_ASN1_OCTET_STRING +i2d_ASN1_PRINTABLE LK_i2d_ASN1_PRINTABLE +i2d_ASN1_PRINTABLESTRING LK_i2d_ASN1_PRINTABLESTRING +i2d_ASN1_SEQUENCE_ANY LK_i2d_ASN1_SEQUENCE_ANY +i2d_ASN1_SET_ANY LK_i2d_ASN1_SET_ANY +i2d_ASN1_T61STRING LK_i2d_ASN1_T61STRING +i2d_ASN1_TIME LK_i2d_ASN1_TIME +i2d_ASN1_TYPE LK_i2d_ASN1_TYPE +i2d_ASN1_UNIVERSALSTRING LK_i2d_ASN1_UNIVERSALSTRING +i2d_ASN1_UTCTIME LK_i2d_ASN1_UTCTIME +i2d_ASN1_UTF8STRING LK_i2d_ASN1_UTF8STRING +i2d_ASN1_VISIBLESTRING LK_i2d_ASN1_VISIBLESTRING +i2d_DHparams LK_i2d_DHparams +i2d_DHparams_bio LK_i2d_DHparams_bio +i2d_DIRECTORYSTRING LK_i2d_DIRECTORYSTRING +i2d_DISPLAYTEXT LK_i2d_DISPLAYTEXT +i2d_DSAPrivateKey LK_i2d_DSAPrivateKey +i2d_DSAPrivateKey_bio LK_i2d_DSAPrivateKey_bio +i2d_DSAPrivateKey_fp LK_i2d_DSAPrivateKey_fp +i2d_DSAPublicKey LK_i2d_DSAPublicKey +i2d_DSA_PUBKEY LK_i2d_DSA_PUBKEY +i2d_DSA_PUBKEY_bio LK_i2d_DSA_PUBKEY_bio +i2d_DSA_PUBKEY_fp LK_i2d_DSA_PUBKEY_fp +i2d_DSA_SIG LK_i2d_DSA_SIG +i2d_DSAparams LK_i2d_DSAparams +i2d_ECDSA_SIG LK_i2d_ECDSA_SIG +i2d_ECParameters LK_i2d_ECParameters +i2d_ECPrivateKey LK_i2d_ECPrivateKey +i2d_ECPrivateKey_bio LK_i2d_ECPrivateKey_bio +i2d_ECPrivateKey_fp LK_i2d_ECPrivateKey_fp +i2d_EC_PUBKEY LK_i2d_EC_PUBKEY +i2d_EC_PUBKEY_bio LK_i2d_EC_PUBKEY_bio +i2d_EC_PUBKEY_fp LK_i2d_EC_PUBKEY_fp +i2d_PKCS12 LK_i2d_PKCS12 +i2d_PKCS12_bio LK_i2d_PKCS12_bio +i2d_PKCS12_fp LK_i2d_PKCS12_fp +i2d_PKCS7 LK_i2d_PKCS7 +i2d_PKCS7_bio LK_i2d_PKCS7_bio +i2d_PKCS8PrivateKeyInfo_bio LK_i2d_PKCS8PrivateKeyInfo_bio +i2d_PKCS8PrivateKeyInfo_fp LK_i2d_PKCS8PrivateKeyInfo_fp +i2d_PKCS8PrivateKey_bio LK_i2d_PKCS8PrivateKey_bio +i2d_PKCS8PrivateKey_fp LK_i2d_PKCS8PrivateKey_fp +i2d_PKCS8PrivateKey_nid_bio LK_i2d_PKCS8PrivateKey_nid_bio +i2d_PKCS8PrivateKey_nid_fp LK_i2d_PKCS8PrivateKey_nid_fp +i2d_PKCS8_PRIV_KEY_INFO_bio LK_i2d_PKCS8_PRIV_KEY_INFO_bio +i2d_PKCS8_PRIV_KEY_INFO_fp LK_i2d_PKCS8_PRIV_KEY_INFO_fp +i2d_PKCS8_bio LK_i2d_PKCS8_bio +i2d_PKCS8_fp LK_i2d_PKCS8_fp +i2d_PUBKEY LK_i2d_PUBKEY +i2d_PUBKEY_bio LK_i2d_PUBKEY_bio +i2d_PUBKEY_fp LK_i2d_PUBKEY_fp +i2d_PrivateKey LK_i2d_PrivateKey +i2d_PrivateKey_bio LK_i2d_PrivateKey_bio +i2d_PrivateKey_fp LK_i2d_PrivateKey_fp +i2d_PublicKey LK_i2d_PublicKey +i2d_RSAPrivateKey LK_i2d_RSAPrivateKey +i2d_RSAPrivateKey_bio LK_i2d_RSAPrivateKey_bio +i2d_RSAPrivateKey_fp LK_i2d_RSAPrivateKey_fp +i2d_RSAPublicKey LK_i2d_RSAPublicKey +i2d_RSAPublicKey_bio LK_i2d_RSAPublicKey_bio +i2d_RSAPublicKey_fp LK_i2d_RSAPublicKey_fp +i2d_RSA_PUBKEY LK_i2d_RSA_PUBKEY +i2d_RSA_PUBKEY_bio LK_i2d_RSA_PUBKEY_bio +i2d_RSA_PUBKEY_fp LK_i2d_RSA_PUBKEY_fp +i2d_SSL_SESSION LK_i2d_SSL_SESSION +i2d_SSL_SESSION_bio LK_i2d_SSL_SESSION_bio +i2d_X509 LK_i2d_X509 +i2d_X509_ALGOR LK_i2d_X509_ALGOR +i2d_X509_AUX LK_i2d_X509_AUX +i2d_X509_CRL LK_i2d_X509_CRL +i2d_X509_CRL_bio LK_i2d_X509_CRL_bio +i2d_X509_CRL_fp LK_i2d_X509_CRL_fp +i2d_X509_CRL_tbs LK_i2d_X509_CRL_tbs +i2d_X509_EXTENSION LK_i2d_X509_EXTENSION +i2d_X509_EXTENSIONS LK_i2d_X509_EXTENSIONS +i2d_X509_NAME LK_i2d_X509_NAME +i2d_X509_NAME_ENTRY LK_i2d_X509_NAME_ENTRY +i2d_X509_REQ LK_i2d_X509_REQ +i2d_X509_REQ_bio LK_i2d_X509_REQ_bio +i2d_X509_REQ_fp LK_i2d_X509_REQ_fp +i2d_X509_bio LK_i2d_X509_bio +i2d_X509_fp LK_i2d_X509_fp +i2d_X509_tbs LK_i2d_X509_tbs +i2d_re_X509_CRL_tbs LK_i2d_re_X509_CRL_tbs +i2d_re_X509_REQ_tbs LK_i2d_re_X509_REQ_tbs +i2d_re_X509_tbs LK_i2d_re_X509_tbs +i2o_ECPublicKey LK_i2o_ECPublicKey +i2s_ASN1_ENUMERATED LK_i2s_ASN1_ENUMERATED +i2s_ASN1_INTEGER LK_i2s_ASN1_INTEGER +i2s_ASN1_OCTET_STRING LK_i2s_ASN1_OCTET_STRING +i2t_ASN1_OBJECT LK_i2t_ASN1_OBJECT +i2v_GENERAL_NAME LK_i2v_GENERAL_NAME +i2v_GENERAL_NAMES LK_i2v_GENERAL_NAMES +o2i_ECPublicKey LK_o2i_ECPublicKey +pmbtoken_exp1_get_h_for_testing LK_pmbtoken_exp1_get_h_for_testing +pmbtoken_exp2_get_h_for_testing LK_pmbtoken_exp2_get_h_for_testing +pmbtoken_pst1_get_h_for_testing LK_pmbtoken_pst1_get_h_for_testing +s2i_ASN1_INTEGER LK_s2i_ASN1_INTEGER +s2i_ASN1_OCTET_STRING LK_s2i_ASN1_OCTET_STRING +sk_deep_copy LK_sk_deep_copy +sk_delete LK_sk_delete +sk_delete_if LK_sk_delete_if +sk_delete_ptr LK_sk_delete_ptr +sk_dup LK_sk_dup +sk_find LK_sk_find +sk_free LK_sk_free +sk_insert LK_sk_insert +sk_is_sorted LK_sk_is_sorted +sk_new LK_sk_new +sk_new_null LK_sk_new_null +sk_num LK_sk_num +sk_pop LK_sk_pop +sk_pop_free LK_sk_pop_free +sk_pop_free_ex LK_sk_pop_free_ex +sk_push LK_sk_push +sk_set LK_sk_set +sk_set_cmp_func LK_sk_set_cmp_func +sk_shift LK_sk_shift +sk_sort LK_sk_sort +sk_value LK_sk_value +sk_zero LK_sk_zero +ssl_cert_check_key_usage LK_ssl_cert_check_key_usage +ssl_client_hello_init LK_ssl_client_hello_init +ssl_decode_client_hello_inner LK_ssl_decode_client_hello_inner +ssl_is_valid_ech_public_name LK_ssl_is_valid_ech_public_name +ssl_session_serialize LK_ssl_session_serialize +v2i_GENERAL_NAME LK_v2i_GENERAL_NAME +v2i_GENERAL_NAMES LK_v2i_GENERAL_NAMES +v2i_GENERAL_NAME_ex LK_v2i_GENERAL_NAME_ex +x509v3_bytes_to_hex LK_x509v3_bytes_to_hex +x509v3_cache_extensions LK_x509v3_cache_extensions +x509v3_looks_like_dns_name LK_x509v3_looks_like_dns_name diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/build_android.sh b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/build_android.sh new file mode 100755 index 0000000..d8ef313 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/build_android.sh @@ -0,0 +1,159 @@ +#!/bin/bash +# Exit immediately if any command fails. This ensures CI properly reports build +# failures instead of continuing to create empty/broken artifacts. +set -e + +# Copyright 2023 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +arch="" +profile="release" + +while [ "$#" -gt 0 ]; do + case "$1" in + --arch) + arch="$2" + if [ "$arch" != "arm" ] && [ "$arch" != "x64" ] && [ "$arch" != "arm64" ]; then + echo "Error: Invalid value for --arch. Must be 'arm', 'x64' or 'arm64'." + exit 1 + fi + shift 2 + ;; + --profile) + profile="$2" + if [ "$profile" != "debug" ] && [ "$profile" != "release" ]; then + echo "Error: Invalid value for --profile. Must be 'debug' or 'release'." + exit 1 + fi + shift 2 + ;; + *) + echo "Error: Unknown argument '$1'" + exit 1 + ;; + esac +done + +if [ -z "$arch" ]; then + echo "Error: --arch must be set." + exit 1 +fi + +echo "Building LiveKit WebRTC - Android" +echo "Arch: $arch" +echo "Profile: $profile" + +if [ ! -e "$(pwd)/depot_tools" ] +then + git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git +fi + +export COMMAND_DIR=$(cd $(dirname $0); pwd) +export PATH="$(pwd)/depot_tools:$PATH" +export OUTPUT_DIR="$(pwd)/src/out-$arch-$profile" +export ARTIFACTS_DIR="$(pwd)/android-$arch-$profile" + +if [ ! -e "$(pwd)/src" ] +then + gclient sync -D --no-history +fi + +cd src +git apply "$COMMAND_DIR/patches/add_licenses.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn +git apply "$COMMAND_DIR/patches/fix_license_json_parsing.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn +git apply "$COMMAND_DIR/patches/ssl_verify_callback_with_native_handle.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn +git apply "$COMMAND_DIR/patches/add_deps.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn +git apply "$COMMAND_DIR/patches/android_use_libunwind.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn +git apply "$COMMAND_DIR/patches/external_audio_source.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn +# livekit prefixed jni +git apply "$COMMAND_DIR/patches/jni_prefix.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn + +cd third_party/libyuv +git apply "$COMMAND_DIR/patches/disable_sme_for_libyuv.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn + +cd ../../.. + +mkdir -p "$ARTIFACTS_DIR/lib" + +debug="false" +if [ "$profile" = "debug" ]; then + debug="true" +fi + +# Note: use_clang_modules=false is required to avoid C++ module compilation issues. +# Without this flag, the build may fail partway through, resulting in missing +# artifacts like libwebrtc.jar. +args="is_debug=$debug \ + is_java_debug=$debug \ + target_os=\"android\" \ + target_cpu=\"$arch\" \ + rtc_enable_protobuf=false \ + treat_warnings_as_errors=false \ + rtc_include_tests=false \ + rtc_build_tools=false \ + rtc_build_examples=false \ + rtc_libvpx_build_vp9=false \ + is_component_build=false \ + enable_stripping=true \ + rtc_use_h264=false \ + rtc_use_h265=true \ + rtc_use_pipewire=false \ + symbol_level=0 \ + enable_iterator_debugging=false \ + android_package_prefix=\"livekit\" \ + use_custom_libcxx=false \ + use_clang_modules=false \ + use_rtti=true" + +if [ "$debug" = "true" ]; then + args="${args} is_asan=true is_lsan=true"; +fi + +# generate ninja files +gn gen "$OUTPUT_DIR" --root="src" --args="${args}" + +# build shared library +autoninja -C "$OUTPUT_DIR" :default \ + sdk/android:native_api \ + sdk/android:libwebrtc \ + sdk/android:libjingle_peerconnection_so + +# make libwebrtc.a +# don't include nasm +ar -rc "$ARTIFACTS_DIR/lib/libwebrtc.a" `find "$OUTPUT_DIR/obj" -name '*.o' -not -path "*/third_party/nasm/*"` + +# License generation is optional - may fail with some Python versions +# Use vpython3 from depot_tools for consistent Python version +vpython3 "./src/tools_webrtc/libs/generate_licenses.py" \ + --target :default "$OUTPUT_DIR" "$OUTPUT_DIR" || echo "Warning: License generation failed (non-critical)" + +cp "$OUTPUT_DIR/obj/webrtc.ninja" "$ARTIFACTS_DIR" +cp "$OUTPUT_DIR/libjingle_peerconnection_so.so" "$ARTIFACTS_DIR/lib" +cp "$OUTPUT_DIR/args.gn" "$ARTIFACTS_DIR" + +cp "$OUTPUT_DIR/LICENSE.md" "$ARTIFACTS_DIR" + +mkdir -p "$COMMAND_DIR/prefixed-jni/libs" +cp "$OUTPUT_DIR/lib.java/sdk/android/libwebrtc.jar" "$COMMAND_DIR/prefixed-jni/libs/classes.jar" +cd "$COMMAND_DIR/prefixed-jni" && ./gradlew shadowJar +cp "$COMMAND_DIR/prefixed-jni/build/libs/prefixed-jni-all.jar" "$ARTIFACTS_DIR/libwebrtc.jar" + +cd .. + +cp "src/sdk/android/AndroidManifest.xml" "$ARTIFACTS_DIR" + +cd src +find . -name "*.h" -print | cpio -pd "$ARTIFACTS_DIR/include" +find . -name "*.inc" -print | cpio -pd "$ARTIFACTS_DIR/include" diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/build_ios.sh b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/build_ios.sh new file mode 100755 index 0000000..aa651d2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/build_ios.sh @@ -0,0 +1,168 @@ +#!/bin/bash +# Exit immediately if any command fails. This ensures CI properly reports build +# failures instead of continuing to create empty/broken artifacts. +set -e + +# Copyright 2023 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +arch="" +profile="release" +environment="device" + +while [ "$#" -gt 0 ]; do + case "$1" in + --arch) + arch="$2" + if [ "$arch" != "arm64" ]; then + echo "Error: Invalid value for --arch. Must 'arm64'." + exit 1 + fi + shift 2 + ;; + --environment) + environment="$2" + if [ "$environment" != "device" ] && [ "$environment" != "simulator" ]; then + echo "Error: Invalid value for --environment. Must be 'device' or 'simulator'." + exit 1 + fi + shift 2 + ;; + --profile) + profile="$2" + if [ "$profile" != "debug" ] && [ "$profile" != "release" ]; then + echo "Error: Invalid value for --profile. Must be 'debug' or 'release'." + exit 1 + fi + shift 2 + ;; + *) + echo "Error: Unknown argument '$1'" + exit 1 + ;; + esac +done + +if [ -z "$arch" ]; then + echo "Error: --arch must be set." + exit 1 +fi + +echo "Building LiveKit WebRTC - iOS" +echo "Arch: $arch" +echo "Profile: $profile" +echo "Environment: $environment" + +if [ ! -e "$(pwd)/depot_tools" ] +then + git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git +fi + +export COMMAND_DIR=$(cd $(dirname $0); pwd) +export PATH="$(pwd)/depot_tools:$PATH" + +export OUTPUT_DIR="$(pwd)/src/out-$arch-$profile" +export ARTIFACTS_DIR="$(pwd)/ios-$environment-$arch-$profile" + +if [ ! -e "$(pwd)/src" ] +then + gclient sync -D --no-history +fi + +cd src + +# Apply patches only if not already applied (check with --reverse --check) +apply_patch_if_needed() { + local patch="$1" + if git apply --reverse --check "$patch" 2>/dev/null; then + echo "Patch already applied: $(basename "$patch")" + else + echo "Applying patch: $(basename "$patch")" + git apply "$patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn || true + fi +} + +apply_patch_if_needed "$COMMAND_DIR/patches/add_licenses.patch" +apply_patch_if_needed "$COMMAND_DIR/patches/fix_license_json_parsing.patch" +apply_patch_if_needed "$COMMAND_DIR/patches/ssl_verify_callback_with_native_handle.patch" +apply_patch_if_needed "$COMMAND_DIR/patches/add_deps.patch" +apply_patch_if_needed "$COMMAND_DIR/patches/external_audio_source.patch" + +cd .. + +mkdir -p "$ARTIFACTS_DIR/lib" + +debug="false" +if [ "$profile" = "debug" ]; then + debug="true" +fi + +# generate ninja files +# Note: use_clang_modules=false is required to avoid libc++ header incompatibility +# with Xcode 26.0. When enabled, C++ module compilation fails with errors like +# "unknown type name 'size_t'" due to conflicts between WebRTC's bundled libc++ +# headers and the iOS SDK headers. +gn gen "$OUTPUT_DIR" --root="src" \ + --args="is_debug=$debug \ + enable_dsyms=$debug \ + target_os=\"ios\" \ + target_cpu=\"$arch\" \ + target_environment=\"$environment\" \ + treat_warnings_as_errors=false \ + ios_enable_code_signing=false \ + rtc_enable_protobuf=false \ + rtc_include_tests=false \ + rtc_build_examples=false \ + rtc_build_tools=false \ + rtc_libvpx_build_vp9=false \ + is_component_build=false \ + enable_stripping=true \ + rtc_enable_symbol_export=true \ + rtc_enable_objc_symbol_export=false \ + rtc_use_h264=false \ + use_custom_libcxx=false \ + use_clang_modules=false \ + clang_use_chrome_plugins=false \ + use_rtti=true \ + use_lld=false" + +# build static library +ninja -C "$OUTPUT_DIR" :default \ + api/audio_codecs:builtin_audio_decoder_factory \ + api/task_queue:default_task_queue_factory \ + sdk:native_api \ + sdk:default_codec_factory_objc \ + pc:peer_connection \ + sdk:videocapture_objc \ + sdk:framework_objc + +# make libwebrtc.a +# don't include nasm +ar -rc "$ARTIFACTS_DIR/lib/libwebrtc.a" `find "$OUTPUT_DIR/obj" -name '*.o' -not -path "*/third_party/nasm/*"` + +# License generation - may fail locally due to GN warnings breaking JSON parsing +# Use vpython3 from depot_tools for consistent Python version +vpython3 "./src/tools_webrtc/libs/generate_licenses.py" \ + --target :webrtc "$OUTPUT_DIR" "$OUTPUT_DIR" || echo "Warning: License generation failed" + +cp "$OUTPUT_DIR/obj/webrtc.ninja" "$ARTIFACTS_DIR" +cp "$OUTPUT_DIR/obj/modules/desktop_capture/desktop_capture.ninja" "$ARTIFACTS_DIR" 2>/dev/null || true +cp "$OUTPUT_DIR/args.gn" "$ARTIFACTS_DIR" + +cp "$OUTPUT_DIR/LICENSE.md" "$ARTIFACTS_DIR" + +cd src +find . -name "*.h" -print | cpio -pd "$ARTIFACTS_DIR/include" +find . -name "*.inc" -print | cpio -pd "$ARTIFACTS_DIR/include" diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/build_linux.sh b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/build_linux.sh new file mode 100755 index 0000000..52ebbad --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/build_linux.sh @@ -0,0 +1,159 @@ +#!/bin/bash +# Exit immediately if any command fails. This ensures CI properly reports build +# failures instead of continuing to create empty/broken artifacts. +set -e + +# Copyright 2023 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +arch="" +profile="release" + +while [ "$#" -gt 0 ]; do + case "$1" in + --arch) + arch="$2" + if [ "$arch" != "x64" ] && [ "$arch" != "arm64" ]; then + echo "Error: Invalid value for --arch. Must be 'x64' or 'arm64'." + exit 1 + fi + shift 2 + ;; + --profile) + profile="$2" + if [ "$profile" != "debug" ] && [ "$profile" != "release" ]; then + echo "Error: Invalid value for --profile. Must be 'debug' or 'release'." + exit 1 + fi + shift 2 + ;; + *) + echo "Error: Unknown argument '$1'" + exit 1 + ;; + esac +done + +if [ -z "$arch" ]; then + echo "Error: --arch must be set." + exit 1 +fi + +echo "Building LiveKit WebRTC - Linux" +echo "Arch: $arch" +echo "Profile: $profile" + +if [ ! -e "$(pwd)/depot_tools" ] +then + git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git +fi + +export COMMAND_DIR=$(cd $(dirname $0); pwd) +export PATH="$(pwd)/depot_tools:$PATH" +export OUTPUT_DIR="$(pwd)/src/out-$arch-$profile" +export ARTIFACTS_DIR="$(pwd)/linux-$arch-$profile" + +if [ ! -e "$(pwd)/src" ] +then + gclient sync -D --no-history +fi + +cd src +git apply "$COMMAND_DIR/patches/add_licenses.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn +git apply "$COMMAND_DIR/patches/fix_license_json_parsing.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn +git apply "$COMMAND_DIR/patches/ssl_verify_callback_with_native_handle.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn +git apply "$COMMAND_DIR/patches/add_deps.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn +git apply "$COMMAND_DIR/patches/fix_desktop_capture_compile.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn +git apply "$COMMAND_DIR/patches/external_audio_source.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn + +# Disable CREL (compact relocations). Chromium's build enables experimental +# CREL via -Wa,--crel which causes segfaults on aarch64-linux (and is known +# broken on arm32 and s390x too). +# See: https://crbug.com/376278218 +# See: https://github.com/zed-industries/zed/pull/51433#discussion_r2944567608 +git -C build apply "$COMMAND_DIR/patches/disable_crel.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn + +cd third_party + +git apply "$COMMAND_DIR/patches/david_disable_gun_source_macro.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn + +cd libyuv + +git apply "$COMMAND_DIR/patches/disable_sme_for_libyuv.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn + +cd ../../.. + +mkdir -p "$ARTIFACTS_DIR/lib" + +python3 "./src/build/linux/sysroot_scripts/install-sysroot.py" --arch="$arch" + +debug="false" +if [ "$profile" = "debug" ]; then + debug="true" +fi + +# Note: use_clang_modules=false is required to avoid C++ module compilation issues. +# Without this flag, the build may fail partway through, resulting in missing +# or incomplete artifacts. +args="is_debug=$debug \ + target_os=\"linux\" \ + target_cpu=\"$arch\" \ + rtc_enable_protobuf=false \ + treat_warnings_as_errors=false \ + use_llvm_libatomic=false \ + use_custom_libcxx=false \ + use_custom_libcxx_for_host=false \ + use_clang_modules=false \ + rtc_include_tests=false \ + rtc_build_tools=false \ + rtc_build_examples=false \ + rtc_libvpx_build_vp9=true \ + enable_libaom=true \ + is_component_build=false \ + enable_stripping=true \ + ffmpeg_branding=\"Chrome\" \ + rtc_use_h264=true \ + rtc_use_h265=true \ + rtc_use_pipewire=true \ + symbol_level=0 \ + enable_iterator_debugging=false \ + use_rtti=true \ + rtc_use_x11=true" + +# generate ninja files +gn gen "$OUTPUT_DIR" --root="src" --args="${args}" + +# build static library +ninja -C "$OUTPUT_DIR" :default + +# make libwebrtc.a +# don't include nasm +ar -rc "$ARTIFACTS_DIR/lib/libwebrtc.a" `find "$OUTPUT_DIR/obj" -name '*.o' -not -path "*/third_party/nasm/*"` +src/third_party/llvm-build/Release+Asserts/bin/llvm-objcopy --redefine-syms="$COMMAND_DIR/boringssl_prefix_symbols.txt" "$ARTIFACTS_DIR/lib/libwebrtc.a" + +# License generation is optional - may fail with some Python versions +# Use vpython3 from depot_tools for consistent Python version +vpython3 "./src/tools_webrtc/libs/generate_licenses.py" \ + --target :default "$OUTPUT_DIR" "$OUTPUT_DIR" || echo "Warning: License generation failed (non-critical)" + +cp "$OUTPUT_DIR/obj/webrtc.ninja" "$ARTIFACTS_DIR" +cp "$OUTPUT_DIR/obj/modules/desktop_capture/desktop_capture.ninja" "$ARTIFACTS_DIR" +cp "$OUTPUT_DIR/args.gn" "$ARTIFACTS_DIR" + +cp "$OUTPUT_DIR/LICENSE.md" "$ARTIFACTS_DIR" + +cd src +find . -name "*.h" -print | cpio -pd "$ARTIFACTS_DIR/include" +find . -name "*.inc" -print | cpio -pd "$ARTIFACTS_DIR/include" diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/build_macos.sh b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/build_macos.sh new file mode 100755 index 0000000..bd52f41 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/build_macos.sh @@ -0,0 +1,163 @@ +#!/bin/bash +# Exit immediately if any command fails. This ensures CI properly reports build +# failures instead of continuing to create empty/broken artifacts. +set -e + +# Copyright 2023 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +arch="" +profile="release" + +while [ "$#" -gt 0 ]; do + case "$1" in + --arch) + arch="$2" + if [ "$arch" != "x64" ] && [ "$arch" != "arm64" ]; then + echo "Error: Invalid value for --arch. Must be 'x64' or 'arm64'." + exit 1 + fi + shift 2 + ;; + --profile) + profile="$2" + if [ "$profile" != "debug" ] && [ "$profile" != "release" ]; then + echo "Error: Invalid value for --profile. Must be 'debug' or 'release'." + exit 1 + fi + shift 2 + ;; + *) + echo "Error: Unknown argument '$1'" + exit 1 + ;; + esac +done + +if [ -z "$arch" ]; then + echo "Error: --arch must be set." + exit 1 +fi + +echo "Building LiveKit WebRTC - MacOS" +echo "Arch: $arch" +echo "Profile: $profile" + +if [ ! -e "$(pwd)/depot_tools" ] +then + git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git +fi + +export COMMAND_DIR=$(cd $(dirname $0); pwd) +export PATH="$(pwd)/depot_tools:$PATH" +export OUTPUT_DIR="$(pwd)/src/out-$arch-$profile" +export ARTIFACTS_DIR="$(pwd)/mac-$arch-$profile" + +if [ ! -e "$(pwd)/src" ] +then + gclient sync -D --no-history +fi + +cd src + +# Apply patches only if not already applied (check with --reverse --check) +apply_patch_if_needed() { + local patch="$1" + if git apply --reverse --check "$patch" 2>/dev/null; then + echo "Patch already applied: $(basename "$patch")" + else + echo "Applying patch: $(basename "$patch")" + git apply "$patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn || true + fi +} + +apply_patch_if_needed "$COMMAND_DIR/patches/add_licenses.patch" +apply_patch_if_needed "$COMMAND_DIR/patches/fix_license_json_parsing.patch" +apply_patch_if_needed "$COMMAND_DIR/patches/ssl_verify_callback_with_native_handle.patch" +apply_patch_if_needed "$COMMAND_DIR/patches/add_deps.patch" +apply_patch_if_needed "$COMMAND_DIR/patches/external_audio_source.patch" + +cd .. + +mkdir -p "$ARTIFACTS_DIR/lib" + +debug="false" +if [ "$profile" = "debug" ]; then + debug="true" +fi + +# generate ninja files +# Note: use_clang_modules=false is required to avoid libc++ header incompatibility +# with Xcode 26.0. When enabled, C++ module compilation fails with errors like +# "unknown type name 'size_t'" due to conflicts between WebRTC's bundled libc++ +# headers and the macOS SDK headers. +gn gen "$OUTPUT_DIR" --root="src" \ + --args="is_debug=$debug \ + enable_dsyms=$debug \ + target_os=\"mac\" \ + target_cpu=\"$arch\" \ + mac_deployment_target=\"10.15\" \ + mac_min_system_version=\"10.15\" \ + treat_warnings_as_errors=false \ + rtc_enable_protobuf=false \ + rtc_include_tests=false \ + rtc_build_examples=false \ + rtc_build_tools=false \ + rtc_libvpx_build_vp9=true \ + enable_libaom=true \ + is_component_build=false \ + enable_stripping=true \ + rtc_enable_symbol_export=true \ + rtc_enable_objc_symbol_export=false \ + rtc_include_dav1d_in_internal_decoder_factory = true \ + rtc_use_h264=true \ + rtc_use_h265=true \ + use_custom_libcxx=false \ + use_clang_modules=false \ + clang_use_chrome_plugins=false \ + use_rtti=true \ + use_lld=false \ + rtc_include_internal_audio_device=true" + +# build static library +ninja -C "$OUTPUT_DIR" :default \ + api/audio_codecs:builtin_audio_decoder_factory \ + api/task_queue:default_task_queue_factory \ + sdk:native_api \ + sdk:default_codec_factory_objc \ + pc:peer_connection \ + sdk:videocapture_objc \ + sdk:mac_framework_objc \ + desktop_capture_objc \ + modules/audio_device:audio_device + +# make libwebrtc.a +# don't include nasm +ar -rc "$ARTIFACTS_DIR/lib/libwebrtc.a" `find "$OUTPUT_DIR/obj" -name '*.o' -not -path "*/third_party/nasm/*"` + +# License generation - may fail locally due to GN warnings breaking JSON parsing +# Use vpython3 from depot_tools for consistent Python version +vpython3 "./src/tools_webrtc/libs/generate_licenses.py" \ + --target :webrtc "$OUTPUT_DIR" "$OUTPUT_DIR" || echo "Warning: License generation failed" + +cp "$OUTPUT_DIR/obj/webrtc.ninja" "$ARTIFACTS_DIR" +cp "$OUTPUT_DIR/obj/modules/desktop_capture/desktop_capture.ninja" "$ARTIFACTS_DIR" 2>/dev/null || true +cp "$OUTPUT_DIR/args.gn" "$ARTIFACTS_DIR" + +cp "$OUTPUT_DIR/LICENSE.md" "$ARTIFACTS_DIR" + +cd src +find . -name "*.h" -print | cpio -pd "$ARTIFACTS_DIR/include" +find . -name "*.inc" -print | cpio -pd "$ARTIFACTS_DIR/include" diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/build_windows.cmd b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/build_windows.cmd new file mode 100644 index 0000000..b4c3b5f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/build_windows.cmd @@ -0,0 +1,91 @@ +@echo off + +setlocal enabledelayedexpansion + +set arch= +set profile=release + +:arg_loop +if "%1" == "" goto end_arg_loop +if "%1" == "--arch" ( + set "arch=%2" + shift & shift & goto arg_loop +) +if "%1" == "--profile" ( + set "profile=%2" + shift & shift & goto arg_loop +) +echo Error: Unknown argument '%1' +exit /b 1 +:end_arg_loop + +if not "!arch!" == "x64" if not "!arch!" == "arm64" ( + echo Error: Invalid value for --arch. Must be 'x64' or 'arm64'. + exit /b 1 +) +if not "!profile!" == "debug" if not "!profile!" == "release" ( + echo Error: Invalid value for --profile. Must be 'debug' or 'release'. + exit /b 1 +) + +echo "Building LiveKit WebRTC - Windows" +echo "Arch: !arch!" +echo "Profile: !profile!" + +if not exist depot_tools ( + git clone --depth 1 https://chromium.googlesource.com/chromium/tools/depot_tools.git +) + +set COMMAND_DIR=%~dp0 +set PATH=%cd%\depot_tools;%PATH% +set DEPOT_TOOLS_WIN_TOOLCHAIN=0 +set GYP_GENERATORS=ninja,msvs-ninja +set GYP_MSVS_VERSION=2022 +set OUTPUT_DIR=src\out-!arch!-!profile! +set ARTIFACTS_DIR=%cd%\win-!arch!-!profile! +set vs2019_install=C:\Program Files\Microsoft Visual Studio\2022\Enterprise + +if not exist src ( + call gclient.bat sync -D --with_branch_heads --with_tags +) + +cd src +call git apply "%COMMAND_DIR%/patches/add_licenses.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn +call git apply "%COMMAND_DIR%/patches/add_deps.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn +call git apply "%COMMAND_DIR%/patches/windows_silence_warnings.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn +call git apply "%COMMAND_DIR%/patches/ssl_verify_callback_with_native_handle.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn +call git apply "%COMMAND_DIR%/patches/external_audio_source.patch" -v --ignore-space-change --ignore-whitespace --whitespace=nowarn + +copy ".vpython3" "..\" + +cd .. + +mkdir "%ARTIFACTS_DIR%\lib" + +set "debug=false" +if "!profile!" == "debug" ( + set "debug=true" +) + +rem generate ninja for release +call gn.bat gen %OUTPUT_DIR% --root="src" ^ + --args="is_debug=!debug! is_clang=true target_cpu=\"!arch!\" use_custom_libcxx=false rtc_libvpx_build_vp9=true enable_libaom=true rtc_include_tests=false rtc_build_examples=false rtc_build_tools=false is_component_build=false rtc_enable_protobuf=false rtc_use_h264=true ffmpeg_branding=\"Chrome\" symbol_level=0 enable_iterator_debugging=false" + +rem build +ninja.exe -C %OUTPUT_DIR% :default + +rem copy static library for release build +copy "%OUTPUT_DIR%\obj\webrtc.lib" "%ARTIFACTS_DIR%\lib" + +rem generate license +call python3 "%cd%\src\tools_webrtc\libs\generate_licenses.py" ^ + --target :default %OUTPUT_DIR% %OUTPUT_DIR% + +copy "%OUTPUT_DIR%\obj\webrtc.ninja" "%ARTIFACTS_DIR%" +copy "%OUTPUT_DIR%\obj\modules\desktop_capture\desktop_capture.ninja" "%ARTIFACTS_DIR%" +copy "%OUTPUT_DIR%\args.gn" "%ARTIFACTS_DIR%" +copy "%OUTPUT_DIR%\LICENSE.md" "%ARTIFACTS_DIR%" + +rem copy header +xcopy src\*.h "%ARTIFACTS_DIR%\include" /C /S /I /F /H +xcopy src\*.inc "%ARTIFACTS_DIR%\include" /C /S /I /F /H diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/add_deps.patch b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/add_deps.patch new file mode 100644 index 0000000..92ae3fa --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/add_deps.patch @@ -0,0 +1,36 @@ +diff --git a/BUILD.gn b/BUILD.gn +index ca8d8faa61..13e07a2f28 100644 +--- a/BUILD.gn ++++ b/BUILD.gn +@@ -24,6 +24,9 @@ + import("//build/config/linux/pkg_config.gni") + import("//build/config/sanitizers/sanitizers.gni") + import("webrtc.gni") ++import("//third_party/libaom/options.gni") ++ ++ + if (rtc_enable_protobuf) { + import("//third_party/protobuf/proto_library.gni") + } +@@ -331,6 +334,10 @@ config("common_config") { + defines += [ "WEBRTC_INCLUDE_INTERNAL_AUDIO_DEVICE" ] + } + ++ if (enable_libaom) { ++ defines += [ "RTC_USE_LIBAOM_AV1_ENCODER" ] ++ } ++ + if (rtc_libvpx_build_vp9) { + defines += [ "RTC_ENABLE_VP9" ] + } +@@ -565,6 +572,10 @@ if (!build_with_chromium) { + "pc:rtc_pc", + "sdk", + "video", ++ "//third_party/zlib", ++ "rtc_base:log_sinks", ++ "media:rtc_simulcast_encoder_adapter", ++ "api/crypto:frame_crypto_transformer", + ] + + if (rtc_include_builtin_audio_codecs) { diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/add_licenses.patch b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/add_licenses.patch new file mode 100644 index 0000000..865e114 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/add_licenses.patch @@ -0,0 +1,23 @@ +diff --git a/tools_webrtc/libs/generate_licenses.py b/tools_webrtc/libs/generate_licenses.py +index d945b43..5f3e457 100755 +--- a/tools_webrtc/libs/generate_licenses.py ++++ b/tools_webrtc/libs/generate_licenses.py +@@ -86,6 +86,18 @@ LIB_TO_LICENSES_DICT = { + # Compile time dependencies, no license needed: + 'ow2_asm': [], + 'jdk': [], ++ ++ 'ffmpeg':[ ++ 'third_party/ffmpeg/COPYING.GPLv2', ++ 'third_party/ffmpeg/COPYING.GPLv3', ++ 'third_party/ffmpeg/COPYING.LGPLv2.1', ++ 'third_party/ffmpeg/COPYING.LGPLv3' ++ ], ++ 'openh264': ['third_party/openh264/src/LICENSE'], ++ 'catapult': [], ++ 'google_benchmark': [], ++ 'googletest': [], ++ 'vinn': [], + } + + # Third_party library _regex_ to licences mapping. Keys are regular expression diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/android_use_libunwind.patch b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/android_use_libunwind.patch new file mode 100644 index 0000000..1339b31 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/android_use_libunwind.patch @@ -0,0 +1,22 @@ +--- src/buildtools/third_party/libunwind/BUILD.gn 2023-07-10 10:19:16 ++++ src/buildtools/third_party/libunwind/BUILD.gn 2023-07-10 10:19:23 +@@ -21,7 +21,7 @@ config("libunwind_config") { + + # TODO(crbug.com/40273848): Move this build file to third_party/libc++/BUILD.gn once submodule migration is done + source_set("libunwind") { +- visibility = [ "//buildtools/third_party/libc++abi" ] ++ visibility = [ "//buildtools/third_party/libc++abi", "//build/config:common_deps" ] + if (is_android) { + visibility += [ "//services/tracing/public/cpp" ] + } +--- src/build/config/BUILD.gn 2023-07-10 10:23:49 ++++ src/build/config/BUILD.gn 2023-07-10 10:23:54 +@@ -296,6 +296,8 @@ group("common_deps") { + + if (use_custom_libcxx) { + public_deps += [ "//buildtools/third_party/libc++" ] ++ } else { ++ public_deps += [ "//buildtools/third_party/libunwind" ] + } + + if (use_llvm_libatomic) { diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/david_disable_gun_source_macro.patch b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/david_disable_gun_source_macro.patch new file mode 100644 index 0000000..97fe870 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/david_disable_gun_source_macro.patch @@ -0,0 +1,13 @@ +diff --git a/dav1d/BUILD.gn b/dav1d/BUILD.gn +index 9348e15c3c..f34db7ff98 100644 +--- a/dav1d/BUILD.gn ++++ b/dav1d/BUILD.gn +@@ -101,7 +101,7 @@ if (is_win) { + dav1d_copts += [ "-D_DARWIN_C_SOURCE" ] + } + if (is_linux || is_chromeos || is_android || current_os == "aix") { +- if (!is_clang) { ++ if (!is_clang && (current_cpu == "x86" || current_cpu == "x64")) { + dav1d_copts += [ "-D_GNU_SOURCE" ] + } + } diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/disable_crel.patch b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/disable_crel.patch new file mode 100644 index 0000000..6e4723e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/disable_crel.patch @@ -0,0 +1,19 @@ +diff --git a/config/compiler/BUILD.gn b/config/compiler/BUILD.gn +index c09c9677b..3cfce80a1 100644 +--- a/config/compiler/BUILD.gn ++++ b/config/compiler/BUILD.gn +@@ -721,14 +721,6 @@ config("compiler") { + } else { + cflags += [ "-ffp-contract=off" ] + } +- +- # Enable ELF CREL (see crbug.com/357878242) for all platforms that use ELF. +- # TODO(crbug.com/376278218): This causes segfault on Linux ARM builds. +- # It also causes segfault on Linux s390x: +- # https://github.com/llvm/llvm-project/issues/149511 +- if (is_linux && use_lld && current_cpu != "arm" && current_cpu != "s390x") { +- cflags += [ "-Wa,--crel,--allow-experimental-crel" ] +- } + } + + # C11/C++11 compiler flags setup. diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/disable_sme_for_libyuv.patch b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/disable_sme_for_libyuv.patch new file mode 100644 index 0000000..813f697 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/disable_sme_for_libyuv.patch @@ -0,0 +1,13 @@ +diff --git a/libyuv.gni b/libyuv.gni +index 3334df70..9859a121 100644 +--- a/libyuv.gni ++++ b/libyuv.gni +@@ -24,7 +24,7 @@ declare_args() { + # errors on Fuchsia, macOS, and compilation errors on Windows. + # TODO: bug 359006069 - Remove the restriction after the linker and + # compilation errors are fixed. +- libyuv_use_sme = current_cpu == "arm64" && (is_linux || is_android) ++ libyuv_use_sme = false + libyuv_use_msa = + (current_cpu == "mips64el" || current_cpu == "mipsel") && mips_use_msa + libyuv_use_mmi = diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/external_audio_source.patch b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/external_audio_source.patch new file mode 100644 index 0000000..088be40 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/external_audio_source.patch @@ -0,0 +1,142 @@ +diff --git a/api/media_stream_interface.h b/api/media_stream_interface.h +index fb1cc4e58e..85062ba60e 100644 +--- a/api/media_stream_interface.h ++++ b/api/media_stream_interface.h +@@ -267,6 +267,11 @@ class RTC_EXPORT AudioSourceInterface : public MediaSourceInterface { + // (for some of the settings this approach is broken, e.g. setting + // audio network adaptation on the source is the wrong layer of abstraction). + virtual const AudioOptions options() const; ++ ++ // Returns true if this source delivers audio externally (via AddSink), ++ // bypassing the ADM/AudioState audio distribution path. ++ // When true, AudioSendStream should not register with AudioState. ++ virtual bool is_external_source() const { return false; } + }; + + // Interface of the audio processor used by the audio track to collect +diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc +index 76156ce830..10b59d3ff6 100644 +--- a/audio/audio_send_stream.cc ++++ b/audio/audio_send_stream.cc +@@ -373,8 +373,13 @@ void AudioSendStream::Start() { + } + channel_send_->StartSend(); + sending_ = true; +- audio_state()->AddSendingStream(this, encoder_sample_rate_hz_, +- encoder_num_channels_); ++ // Only register with AudioState if not using external source. ++ // External sources (like NativeAudioSource) deliver audio directly via AddSink, ++ // so we don't want AudioState to also send device audio to this stream. ++ if (!config_.external_source) { ++ audio_state()->AddSendingStream(this, encoder_sample_rate_hz_, ++ encoder_num_channels_); ++ } + } + + void AudioSendStream::Stop() { +@@ -386,7 +391,10 @@ void AudioSendStream::Stop() { + RemoveBitrateObserver(); + channel_send_->StopSend(); + sending_ = false; +- audio_state()->RemoveSendingStream(this); ++ // Only unregister if we registered (when not using external source). ++ if (!config_.external_source) { ++ audio_state()->RemoveSendingStream(this); ++ } + } + + void AudioSendStream::SendAudioData(std::unique_ptr audio_frame) { +diff --git a/call/audio_send_stream.h b/call/audio_send_stream.h +index 84341b5cb1..9359777bc9 100644 +--- a/call/audio_send_stream.h ++++ b/call/audio_send_stream.h +@@ -178,6 +178,12 @@ class AudioSendStream : public AudioSender { + // An optional frame transformer used by insertable streams to transform + // encoded frames. + scoped_refptr frame_transformer; ++ ++ // When true, this stream uses an external audio source (not ADM). ++ // AudioState will NOT send device-captured audio to this stream. ++ // Audio is delivered directly via the source's AddSink mechanism. ++ // This prevents mixing of device audio with externally-sourced audio. ++ bool external_source = false; + }; + + virtual ~AudioSendStream() = default; +diff --git a/media/base/audio_source.h b/media/base/audio_source.h +index 04a7d19dfa..9f513f3c75 100644 +--- a/media/base/audio_source.h ++++ b/media/base/audio_source.h +@@ -49,6 +49,10 @@ class AudioSource { + // to the source at a time. + virtual void SetSink(Sink* sink) = 0; + ++ // Returns true if this source delivers audio externally (bypassing ADM). ++ // When true, AudioSendStream should not register with AudioState. ++ virtual bool is_external_source() const { return false; } ++ + protected: + virtual ~AudioSource() {} + }; +diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc +index 762f9d584c..4ce07ddc9d 100644 +--- a/media/engine/webrtc_voice_engine.cc ++++ b/media/engine/webrtc_voice_engine.cc +@@ -1017,6 +1017,14 @@ class WebRtcVoiceSendChannel::WebRtcAudioSendStream : public AudioSource::Sink { + RTC_DCHECK(source_ == source); + return; + } ++ ++ // Check if this is an external audio source (delivers audio via AddSink). ++ // If so, mark the config so AudioState doesn't send device audio to this ++ // stream. This must be done before UpdateSendState() calls Start(). ++ if (source->is_external_source() && !config_.external_source) { ++ config_.external_source = true; ++ stream_->Reconfigure(config_, nullptr); ++ } + source->SetSink(this); + source_ = source; + UpdateSendState(); +diff --git a/pc/rtp_sender.cc b/pc/rtp_sender.cc +index d5edbbf0ed..c14ddfe868 100644 +--- a/pc/rtp_sender.cc ++++ b/pc/rtp_sender.cc +@@ -786,6 +786,13 @@ void AudioRtpSender::SetSend() { + RTC_DCHECK_RUN_ON(signaling_thread_); + RTC_DCHECK(!stopped_); + RTC_DCHECK(can_send_track()); ++ ++ // Propagate is_external_source from AudioSourceInterface to the sink adapter. ++ // This ensures the voice engine knows not to mix ADM audio with external sources. ++ if (audio_track()->GetSource() && audio_track()->GetSource()->is_external_source()) { ++ sink_adapter_->set_is_external_source(true); ++ } ++ + if (!media_channel_) { + RTC_LOG(LS_ERROR) << "SetAudioSend: No audio channel exists."; + return; +diff --git a/pc/rtp_sender.h b/pc/rtp_sender.h +index eaffd4ef0f..d0489df9e7 100644 +--- a/pc/rtp_sender.h ++++ b/pc/rtp_sender.h +@@ -307,6 +307,12 @@ class LocalAudioSinkAdapter : public AudioTrackSinkInterface, + LocalAudioSinkAdapter(); + virtual ~LocalAudioSinkAdapter(); + ++ // Set whether the original AudioSourceInterface is an external source. ++ // This propagates the is_external_source state from AudioSourceInterface ++ // to this AudioSource adapter. ++ void set_is_external_source(bool value) { is_external_source_ = value; } ++ bool is_external_source() const override { return is_external_source_; } ++ + private: + // AudioSinkInterface implementation. + void OnData(const void* audio_data, +@@ -337,6 +343,7 @@ class LocalAudioSinkAdapter : public AudioTrackSinkInterface, + // Critical section protecting `sink_`. + Mutex lock_; + int num_preferred_channels_ = -1; ++ bool is_external_source_ = false; + }; + + class AudioRtpSender : public DtmfProviderInterface, public RtpSenderBase { diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/fix_desktop_capture_compile.patch b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/fix_desktop_capture_compile.patch new file mode 100644 index 0000000..634b69c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/fix_desktop_capture_compile.patch @@ -0,0 +1,17 @@ +diff --git a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc +index 070257f072..61fd0c8f3b 100644 +--- a/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc ++++ b/modules/desktop_capture/linux/wayland/shared_screencast_stream.cc +@@ -71,10 +71,10 @@ constexpr int CursorMetaSize(int w, int h) { + w * h * kCursorBpp); + } + +-constexpr PipeWireVersion kDmaBufModifierMinVersion = {.major = 0, ++const PipeWireVersion kDmaBufModifierMinVersion = {.major = 0, + .minor = 3, + .micro = 33}; +-constexpr PipeWireVersion kDropSingleModifierMinVersion = {.major = 0, ++const PipeWireVersion kDropSingleModifierMinVersion = {.major = 0, + .minor = 3, + .micro = 40}; + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/fix_license_json_parsing.patch b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/fix_license_json_parsing.patch new file mode 100644 index 0000000..d7b9d7e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/fix_license_json_parsing.patch @@ -0,0 +1,16 @@ +diff --git a/tools_webrtc/libs/generate_licenses.py b/tools_webrtc/libs/generate_licenses.py +--- a/tools_webrtc/libs/generate_licenses.py ++++ b/tools_webrtc/libs/generate_licenses.py +@@ -211,6 +211,12 @@ class LicenseBuilder: + + def _get_third_party_libraries(self, buildfile_dir, target): + license_json = LicenseBuilder._run_gn(buildfile_dir, target) ++ # Strip any non-JSON content (e.g., GN warnings) before the actual JSON. ++ # GN may output warnings before the JSON when certain build args trigger ++ # deprecation notices. ++ json_start = license_json.find('{') ++ if json_start > 0: ++ license_json = license_json[json_start:] + try: + output = json.loads(license_json) + except: diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/jni_prefix.patch b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/jni_prefix.patch new file mode 100644 index 0000000..2cf4577 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/jni_prefix.patch @@ -0,0 +1,317 @@ +diff --git a/modules/video_coding/codecs/test/android_codec_factory_helper.cc b/modules/video_coding/codecs/test/android_codec_factory_helper.cc +index f6a5046210..4ce5a79d25 100644 +--- a/modules/video_coding/codecs/test/android_codec_factory_helper.cc ++++ b/modules/video_coding/codecs/test/android_codec_factory_helper.cc +@@ -54,9 +54,9 @@ void InitializeAndroidObjects() { + std::unique_ptr CreateAndroidEncoderFactory() { + JNIEnv* env = AttachCurrentThreadIfNeeded(); + ScopedJavaLocalRef factory_class = +- GetClass(env, "org/webrtc/HardwareVideoEncoderFactory"); ++ GetClass(env, "livekit/org/webrtc/HardwareVideoEncoderFactory"); + jmethodID factory_constructor = env->GetMethodID( +- factory_class.obj(), "", "(Lorg/webrtc/EglBase$Context;ZZ)V"); ++ factory_class.obj(), "", "(Llivekit/org/webrtc/EglBase$Context;ZZ)V"); + ScopedJavaLocalRef factory_object = + ScopedJavaLocalRef::Adopt( + env, env->NewObject(factory_class.obj(), factory_constructor, +@@ -69,9 +69,9 @@ std::unique_ptr CreateAndroidEncoderFactory() { + std::unique_ptr CreateAndroidDecoderFactory() { + JNIEnv* env = AttachCurrentThreadIfNeeded(); + ScopedJavaLocalRef factory_class = +- GetClass(env, "org/webrtc/HardwareVideoDecoderFactory"); ++ GetClass(env, "livekit/org/webrtc/HardwareVideoDecoderFactory"); + jmethodID factory_constructor = env->GetMethodID( +- factory_class.obj(), "", "(Lorg/webrtc/EglBase$Context;)V"); ++ factory_class.obj(), "", "(Llivekit/org/webrtc/EglBase$Context;)V"); + ScopedJavaLocalRef factory_object = + ScopedJavaLocalRef::Adopt( + env, env->NewObject(factory_class.obj(), factory_constructor, +diff --git a/sdk/android/BUILD.gn b/sdk/android/BUILD.gn +index 8ce74e2c8c..2e88760d0a 100644 +--- a/sdk/android/BUILD.gn ++++ b/sdk/android/BUILD.gn +@@ -1446,11 +1446,13 @@ if (current_os == "linux" || is_android) { + generate_jni("generated_environment_jni") { + sources = [ "api/org/webrtc/Environment.java" ] + namespace = "webrtc::jni" ++ package_prefix = android_package_prefix + } + + generate_jni("generated_metrics_jni") { + sources = [ "api/org/webrtc/Metrics.java" ] + namespace = "webrtc::jni" ++ package_prefix = android_package_prefix + } + + # Generated JNI for public JNI targets, matching order of targets +@@ -1464,6 +1466,7 @@ if (current_os == "linux" || is_android) { + "src/java/org/webrtc/JniCommon.java", + ] + namespace = "webrtc::jni" ++ package_prefix = android_package_prefix + } + + generate_jni("generated_video_jni") { +@@ -1493,11 +1496,13 @@ if (current_os == "linux" || is_android) { + "src/java/org/webrtc/WrappedNativeI420Buffer.java", + ] + namespace = "webrtc::jni" ++ package_prefix = android_package_prefix + } + + generate_jni("generated_video_egl_jni") { + sources = [ "src/java/org/webrtc/EglBase10Impl.java" ] + namespace = "webrtc::jni" ++ package_prefix = android_package_prefix + } + + generate_jni("generated_libvpx_vp8_jni") { +@@ -1507,6 +1512,7 @@ if (current_os == "linux" || is_android) { + ] + + namespace = "webrtc::jni" ++ package_prefix = android_package_prefix + } + + generate_jni("generated_libvpx_vp9_jni") { +@@ -1516,18 +1522,21 @@ if (current_os == "linux" || is_android) { + ] + + namespace = "webrtc::jni" ++ package_prefix = android_package_prefix + } + + generate_jni("generated_libaom_av1_encoder_jni") { + sources = [ "api/org/webrtc/LibaomAv1Encoder.java" ] + + namespace = "webrtc::jni" ++ package_prefix = android_package_prefix + } + + generate_jni("generated_dav1d_jni") { + sources = [ "api/org/webrtc/Dav1dDecoder.java" ] + + namespace = "webrtc::jni" ++ package_prefix = android_package_prefix + } + + generate_jni("generated_swcodecs_jni") { +@@ -1537,11 +1546,13 @@ if (current_os == "linux" || is_android) { + ] + + namespace = "webrtc::jni" ++ package_prefix = android_package_prefix + } + + generate_jni("generated_rtcerror_jni") { + sources = [ "src/java/org/webrtc/RtcError.java" ] + namespace = "webrtc::jni" ++ package_prefix = android_package_prefix + } + + generate_jni("generated_peerconnection_jni") { +@@ -1585,11 +1596,13 @@ if (current_os == "linux" || is_android) { + "api/org/webrtc/TurnCustomizer.java", + ] + namespace = "webrtc::jni" ++ package_prefix = android_package_prefix + } + + generate_jni("generated_java_audio_jni") { + sources = [ "api/org/webrtc/audio/JavaAudioDeviceModule.java" ] + namespace = "webrtc::jni" ++ package_prefix = android_package_prefix + } + + generate_jni("generated_builtin_audio_codecs_jni") { +@@ -1598,6 +1611,7 @@ if (current_os == "linux" || is_android) { + "api/org/webrtc/BuiltinAudioEncoderFactoryFactory.java", + ] + namespace = "webrtc::jni" ++ package_prefix = android_package_prefix + } + + # Generated JNI for native API targets +@@ -1609,17 +1623,20 @@ if (current_os == "linux" || is_android) { + "src/java/org/webrtc/WebRtcClassLoader.java", + ] + namespace = "webrtc::jni" ++ package_prefix = android_package_prefix + } + + # Generated JNI for internal targets. + + generate_jni("generated_logging_jni") { + sources = [ "src/java/org/webrtc/JNILogging.java" ] ++ package_prefix = android_package_prefix + } + + generate_jni("generated_audio_device_module_base_jni") { + sources = [ "src/java/org/webrtc/audio/WebRtcAudioManager.java" ] + namespace = "webrtc::jni" ++ package_prefix = android_package_prefix + } + + generate_jni("generated_java_audio_device_module_native_jni") { +@@ -1628,6 +1645,7 @@ if (current_os == "linux" || is_android) { + "src/java/org/webrtc/audio/WebRtcAudioTrack.java", + ] + namespace = "webrtc::jni" ++ package_prefix = android_package_prefix + } + } + +diff --git a/sdk/android/api/org/webrtc/PeerConnectionFactory.java b/sdk/android/api/org/webrtc/PeerConnectionFactory.java +index 01679ad12b..7b8545902d 100644 +--- a/sdk/android/api/org/webrtc/PeerConnectionFactory.java ++++ b/sdk/android/api/org/webrtc/PeerConnectionFactory.java +@@ -88,7 +88,7 @@ public class PeerConnectionFactory { + private String fieldTrials = ""; + private boolean enableInternalTracer; + private NativeLibraryLoader nativeLibraryLoader = new NativeLibrary.DefaultLoader(); +- private String nativeLibraryName = "jingle_peerconnection_so"; ++ private String nativeLibraryName = "lkjingle_peerconnection_so"; + @Nullable private Loggable loggable; + @Nullable private Severity loggableSeverity; + +diff --git a/sdk/android/src/jni/jni_helpers.h b/sdk/android/src/jni/jni_helpers.h +index d86c8fa4ad..30f307a2ca 100644 +--- a/sdk/android/src/jni/jni_helpers.h ++++ b/sdk/android/src/jni/jni_helpers.h +@@ -29,10 +29,10 @@ + // boundary. crbug.com/655248 + #define JNI_FUNCTION_DECLARATION(rettype, name, ...) \ + __attribute__((force_align_arg_pointer)) extern "C" JNIEXPORT rettype \ +- JNICALL Java_org_webrtc_##name(__VA_ARGS__) ++ JNICALL Java_livekit_org_webrtc_##name(__VA_ARGS__) + #else + #define JNI_FUNCTION_DECLARATION(rettype, name, ...) \ +- extern "C" JNIEXPORT rettype JNICALL Java_org_webrtc_##name(__VA_ARGS__) ++ extern "C" JNIEXPORT rettype JNICALL Java_livekit_org_webrtc_##name(__VA_ARGS__) + #endif + + namespace webrtc { +diff --git a/sdk/android/src/jni/simulcast_video_encoder.cc b/sdk/android/src/jni/simulcast_video_encoder.cc +index 6d4ba5b3b6..48d3cb3541 100644 +--- a/sdk/android/src/jni/simulcast_video_encoder.cc ++++ b/sdk/android/src/jni/simulcast_video_encoder.cc +@@ -15,7 +15,14 @@ extern "C" { + #endif + + // (VideoEncoderFactory primary, VideoEncoderFactory fallback, VideoCodecInfo info) +-JNIEXPORT jlong JNICALL Java_org_webrtc_SimulcastVideoEncoder_nativeCreateEncoder(JNIEnv *env, jclass klass, jlong webrtcEnvRef, jobject primary, jobject fallback, jobject info) { ++JNI_FUNCTION_DECLARATION(jlong, ++ SimulcastVideoEncoder_nativeCreateEncoder, ++ JNIEnv *env, ++ jclass klass, ++ jlong webrtcEnvRef, ++ jobject primary, ++ jobject fallback, ++ jobject info) { + RTC_LOG(LS_INFO) << "Create simulcast video encoder"; + auto info_ref = JavaParamRef::CreateLeaky(env, info); + SdpVideoFormat format = VideoCodecInfoToSdpVideoFormat(env, info_ref); +diff --git a/tools_webrtc/android/build_aar.py b/tools_webrtc/android/build_aar.py +index 2fbce97c60..130b3d5fa7 100755 +--- a/tools_webrtc/android/build_aar.py ++++ b/tools_webrtc/android/build_aar.py +@@ -254,7 +254,7 @@ def BuildAar(build_dir, + Collect(aar_file, build_dir, arch, unstripped) + + license_dir = os.path.dirname(os.path.realpath(output_file)) +- GenerateLicenses(license_dir, build_dir, archs) ++ #GenerateLicenses(license_dir, build_dir, archs) + + + def main(): +diff --git a/webrtc.gni b/webrtc.gni +index 468ba33b01..ca81ce298c 100644 +--- a/webrtc.gni ++++ b/webrtc.gni +@@ -241,6 +241,10 @@ declare_args() { + # hasn't been registered. + rtc_strict_field_trials = "" + ++ if (is_android) { ++ android_package_prefix = "livekit" ++ } ++ + # If different from "", symbols exported with RTC_OBJC_EXPORT will be prefixed + # with this string. + # See the definition of RTC_OBJC_TYPE_PREFIX in the code. +diff --git a/third_party/jni_zero/BUILD.gn b/third_party/jni_zero/BUILD.gn +index 7056bdd033c1..2b97de476de7 100644 +--- a/third_party/jni_zero/BUILD.gn ++++ b/third_party/jni_zero/BUILD.gn +@@ -17,6 +17,7 @@ generate_jni("generate_jni") { + ":*", + "//components/cronet/android/*", + ] ++ package_prefix = "livekit" + } + + generate_jar_jni("system_jni") { +diff --git a/third_party/jni_zero/codegen/header_common.py b/third_party/jni_zero/codegen/header_common.py +index 338cd748079f..be2577281a91 100644 +--- a/third_party/jni_zero/codegen/header_common.py ++++ b/third_party/jni_zero/codegen/header_common.py +@@ -12,7 +12,8 @@ def class_accessors(sb, java_classes, module_name): + for java_class in java_classes: + if java_class in (java_types.OBJECT_CLASS, java_types.STRING_CLASS): + continue +- escaped_name = java_class.to_cpp() ++ escaped_name = java_class.class_without_prefix.to_cpp() ++ print("header accessor: ", escaped_name) + # #ifdef needed when multple .h files are #included that common classes. + sb(f"""\ + #ifndef {escaped_name}_clazz_defined +@@ -44,7 +45,7 @@ def class_accessor_expression(java_class): + if java_class == java_types.STRING_CLASS: + return 'jni_zero::g_string_class' + +- return f'{java_class.to_cpp()}_clazz(env)' ++ return f'{java_class.class_without_prefix.to_cpp()}_clazz(env)' + + + def header_preamble(script_name, +@@ -53,7 +54,7 @@ def header_preamble(script_name, + user_includes=None, + header_guard=None): + if header_guard is None: +- header_guard = f'{java_class.to_cpp()}_JNI' ++ header_guard = f'{java_class.class_without_prefix.to_cpp()}_JNI' + sb = [] + sb.append(f"""\ + // This file was generated by +diff --git a/third_party/jni_zero/jni_zero.gni b/third_party/jni_zero/jni_zero.gni +index 71826b906d10..22d8eaf6e232 100644 +--- a/third_party/jni_zero/jni_zero.gni ++++ b/third_party/jni_zero/jni_zero.gni +@@ -123,7 +123,7 @@ template("_invoke_jni_zero") { + # namespace: Registration functions will be wrapped into this. (optional) + # priority_java_targets: List of java targets that, if using multiplexing, + # will always be placed first in the sequential switch numbers. (optional) +-# ++# package_prefix: package prefix to add to jni + # Example + # generate_jni_registration("chrome_jni_registration") { + # java_targets = [ ":chrome_public_apk" ] +@@ -327,6 +327,7 @@ template("generate_jni_impl") { + "deps", + "metadata", + "public_deps", ++ "package_prefix", + ]) + if (!defined(public_deps)) { + public_deps = [] +@@ -421,7 +422,9 @@ template("generate_jni_impl") { + if (defined(invoker.split_name)) { + args += [ "--split-name=${invoker.split_name}" ] + } +- ++ if (defined(package_prefix)) { ++ args += [ "--package-prefix=${package_prefix}" ] ++ } + foreach(_name, _input_names) { + _name = + string_replace(get_path_info(_name, "name"), "\$", "__") + "_jni.h" diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/ssl_verify_callback_with_native_handle.patch b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/ssl_verify_callback_with_native_handle.patch new file mode 100644 index 0000000..3c9ae54 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/ssl_verify_callback_with_native_handle.patch @@ -0,0 +1,62 @@ +diff --git a/rtc_base/boringssl_certificate.cc b/rtc_base/boringssl_certificate.cc +index a4f919fbbe..fe3beeaed9 100644 +--- a/rtc_base/boringssl_certificate.cc ++++ b/rtc_base/boringssl_certificate.cc +@@ -252,6 +252,12 @@ BoringSSLCertificate::BoringSSLCertificate( + RTC_DCHECK(cert_buffer_ != nullptr); + } + ++BoringSSLCertificate::BoringSSLCertificate( ++ bssl::UniquePtr cert_buffer, SSL* ssl) ++ : cert_buffer_(std::move(cert_buffer)), ssl_(ssl) { ++ RTC_DCHECK(cert_buffer_ != nullptr); ++} ++ + std::unique_ptr BoringSSLCertificate::Generate( + OpenSSLKeyPair* key_pair, + const SSLIdentityParams& params) { +diff --git a/rtc_base/boringssl_certificate.h b/rtc_base/boringssl_certificate.h +index b5a18d0843..53759fd064 100644 +--- a/rtc_base/boringssl_certificate.h ++++ b/rtc_base/boringssl_certificate.h +@@ -34,6 +34,7 @@ namespace webrtc { + class BoringSSLCertificate final : public SSLCertificate { + public: + explicit BoringSSLCertificate(bssl::UniquePtr cert_buffer); ++ BoringSSLCertificate(bssl::UniquePtr cert_buffer, SSL* ssl); + + static std::unique_ptr Generate( + OpenSSLKeyPair* key_pair, +@@ -66,6 +67,10 @@ class BoringSSLCertificate final : public SSLCertificate { + private: + // A handle to the DER encoded certificate data. + bssl::UniquePtr cert_buffer_; ++ SSL* ssl_ = nullptr; ++ ++ public: ++ SSL* ssl() const { return ssl_; } + }; + + } // namespace webrtc +diff --git a/rtc_base/openssl_adapter.cc b/rtc_base/openssl_adapter.cc +index b80e665d78..1f3dd28795 100644 +--- a/rtc_base/openssl_adapter.cc ++++ b/rtc_base/openssl_adapter.cc +@@ -846,7 +846,7 @@ enum ssl_verify_result_t OpenSSLAdapter::SSLVerifyInternal(SSL* ssl, + std::vector> certs; + for (size_t i = 0; i < sk_CRYPTO_BUFFER_num(chain); ++i) { + certs.emplace_back(new BoringSSLCertificate( +- bssl::UpRef(sk_CRYPTO_BUFFER_value(chain, i)))); ++ bssl::UpRef(sk_CRYPTO_BUFFER_value(chain, i)), ssl)); + } + + SSLCertChain cert_chain(std::move(certs)); +@@ -932,7 +932,7 @@ int OpenSSLAdapter::SSLVerifyInternal(int previous_status, + RTC_LOG(LS_ERROR) << "Failed to allocate CRYPTO_BUFFER."; + return previous_status; + } +- certs.emplace_back(new BoringSSLCertificate(std::move(crypto_buffer))); ++ certs.emplace_back(new BoringSSLCertificate(std::move(crypto_buffer), ssl)); + #else + certs.emplace_back(new OpenSSLCertificate(x509_cert)); + #endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/windows_silence_warnings.patch b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/windows_silence_warnings.patch new file mode 100644 index 0000000..b10ca99 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/patches/windows_silence_warnings.patch @@ -0,0 +1,14 @@ +diff --git a/build/config/compiler/BUILD.gn b/build/config/compiler/BUILD.gn +index 8048ec5e8..4e00f7e53 100644 +--- a/build/config/compiler/BUILD.gn ++++ b/build/config/compiler/BUILD.gn +@@ -1424,6 +1424,9 @@ config("default_warnings") { + # gethostbyname. Fires mostly in non-Chromium code. We probably + # want to remove this define eventually. + "_WINSOCK_DEPRECATED_NO_WARNINGS", ++ ++ "_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS", ++ "_SILENCE_ALL_CXX20_DEPRECATION_WARNINGS", + ] + if (!is_clang) { + # TODO(thakis): Remove this once diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/prefixed-jni/build.gradle b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/prefixed-jni/build.gradle new file mode 100644 index 0000000..9b8c584 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/prefixed-jni/build.gradle @@ -0,0 +1,24 @@ +plugins { + id 'java-library' + id 'com.github.johnrengelman.shadow' version '7.1.2' +} + +java { + sourceCompatibility = JavaVersion.VERSION_1_7 + targetCompatibility = JavaVersion.VERSION_1_7 +} + +dependencies { + api files("libs/classes.jar") +} + +shadowJar { + +} +import com.github.jengelman.gradle.plugins.shadow.tasks.ConfigureShadowRelocation + +task relocateShadowJar(type: ConfigureShadowRelocation) { + target = tasks.shadowJar + prefix = "livekit" // Default value is "shadow" +} +tasks.shadowJar.dependsOn tasks.relocateShadowJar diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/prefixed-jni/gradle/wrapper/gradle-wrapper.jar b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/prefixed-jni/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..f6b961f Binary files /dev/null and b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/prefixed-jni/gradle/wrapper/gradle-wrapper.jar differ diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/prefixed-jni/gradle/wrapper/gradle-wrapper.properties b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/prefixed-jni/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..c5c6e9b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/prefixed-jni/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Thu Apr 29 14:50:17 JST 2021 +distributionBase=GRADLE_USER_HOME +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip +distributionPath=wrapper/dists +zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/prefixed-jni/gradlew b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/prefixed-jni/gradlew new file mode 100755 index 0000000..cccdd3d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/prefixed-jni/gradlew @@ -0,0 +1,172 @@ +#!/usr/bin/env sh + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" >/dev/null +APP_HOME="`pwd -P`" +cd "$SAVED" >/dev/null + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn () { + echo "$*" +} + +die () { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; + NONSTOP* ) + nonstop=true + ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Escape application args +save () { + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done + echo " " +} +APP_ARGS=$(save "$@") + +# Collect all arguments for the java command, following the shell quoting and substitution rules +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" + +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then + cd "$(dirname "$0")" +fi + +exec "$JAVACMD" "$@" diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/prefixed-jni/gradlew.bat b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/prefixed-jni/gradlew.bat new file mode 100644 index 0000000..f955316 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/prefixed-jni/gradlew.bat @@ -0,0 +1,84 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windows variants + +if not "%OS%" == "Windows_NT" goto win9xME_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/prefixed-jni/settings.gradle b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/prefixed-jni/settings.gradle new file mode 100644 index 0000000..2a9285c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/libwebrtc/prefixed-jni/settings.gradle @@ -0,0 +1,14 @@ +pluginManagement { + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/adm_proxy.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/adm_proxy.cpp new file mode 100644 index 0000000..e435730 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/adm_proxy.cpp @@ -0,0 +1,926 @@ +/* + * Copyright 2026 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/adm_proxy.h" + +#include "api/audio/audio_device.h" +#include "api/audio/create_audio_device_module.h" +#include "api/make_ref_counted.h" +#include "rtc_base/logging.h" +#include "rtc_base/thread.h" + +#if defined(__ANDROID__) +#include +#include "sdk/android/native_api/audio_device_module/audio_device_android.h" +#include "sdk/android/native_api/base/init.h" +#endif + +namespace livekit_ffi { + +AdmProxy::AdmProxy(const webrtc::Environment& env, webrtc::Thread* worker_thread) + : env_(env), + worker_thread_(worker_thread) { + // Create the synthetic ADM for synthetic mode. SyntheticAudioDevice pumps + // the WebRTC audio pipeline without platform audio, allowing FFI callbacks + // to receive decoded remote audio. + synthetic_adm_ = webrtc::make_ref_counted(env_); + if (synthetic_adm_->Init() != 0) { + RTC_LOG(LS_ERROR) << "AdmProxy: Failed to initialize synthetic ADM"; + } + + // Attempt to create the Platform ADM for real audio I/O. + // The eager attempt exists for iOS compatibility: the iOS audio session + // requires early setup to avoid KVO race conditions. + // On Android, we defer Platform ADM creation to AcquirePlatformAdm(). + // This is because: + // 1. CreateAudioDeviceModule requires JNI to be fully initialized + // 2. The JNI initialization (via JNI_OnLoad or manual init) may not have + // completed by the time the AdmProxy constructor runs + // 3. Deferring creation ensures JNI is ready when we actually need the ADM + // On all platforms a failed attempt is retried later by + // EnsurePlatformAdmCreated(): desktop apps launched at login can race the + // OS audio stack (coreaudiod, Windows audio services), and a one-shot + // creation would leave audio permanently broken for the process lifetime. +#if defined(__ANDROID__) + // platform_adm_ stays nullptr, will be created in EnsurePlatformAdmCreated() +#else + webrtc::MutexLock lock(&mutex_); + if (!EnsurePlatformAdmCreated()) { + RTC_LOG(LS_WARNING) + << "AdmProxy: Platform ADM unavailable at construction; will retry on demand"; + } +#endif +} + +AdmProxy::~AdmProxy() { + RTC_LOG(LS_VERBOSE) << "AdmProxy::~AdmProxy()"; + + if (synthetic_adm_) { + synthetic_adm_->Terminate(); + synthetic_adm_ = nullptr; + } + + if (platform_adm_) { + platform_adm_->Terminate(); + platform_adm_ = nullptr; + } +} + +// ============================================================================= +// Helper Methods +// ============================================================================= + +bool AdmProxy::is_platform_playout_active() const { + // Platform playout is active when: ref_count > 0 AND playout explicitly enabled. + // Otherwise, synthetic mode handles playout via the internal pumping task. + return platform_adm_ && platform_adm_ref_count_ > 0 && playout_enabled_; +} + +webrtc::AudioDeviceModule* AdmProxy::recording_adm() const { + // Recording only available through platform ADM when enabled. + // Synthetic mode doesn't support recording (no microphone). + if (platform_adm_ && platform_adm_ref_count_ > 0 && recording_enabled_) { + return platform_adm_.get(); + } + return nullptr; +} + +// ============================================================================= +// Platform ADM Lifecycle Management +// ============================================================================= + +// Lazily creates the Platform ADM. Must be called with mutex held. +// Returns true if ADM is available (either already existed or successfully created). +// A failed attempt leaves platform_adm_ null so the next call retries; the OS +// audio stack may simply not be ready yet (cold boot, login launch). +bool AdmProxy::EnsurePlatformAdmCreated() { + if (platform_adm_) { + return true; // Already created + } + +#if defined(__ANDROID__) + // Use CreateAndroidAudioDeviceModule which properly uses GetAppContext() + // to get the application context set via ContextUtils.initialize(). + platform_adm_ = webrtc::CreateAndroidAudioDeviceModule( + env_, webrtc::AudioDeviceModule::kPlatformDefaultAudio); + + if (!platform_adm_) { + RTC_LOG(LS_ERROR) << "AdmProxy: CreateAndroidAudioDeviceModule returned nullptr. " + << "Ensure ContextUtils.initialize() was called."; + return false; + } +#else + platform_adm_ = webrtc::CreateAudioDeviceModule( + env_, webrtc::AudioDeviceModule::kPlatformDefaultAudio); + + if (!platform_adm_) { + RTC_LOG(LS_ERROR) << "AdmProxy: CreateAudioDeviceModule returned nullptr"; + return false; + } +#endif + + int32_t init_result = platform_adm_->Init(); + if (init_result != 0) { + RTC_LOG(LS_ERROR) << "AdmProxy: Platform ADM Init() failed with error=" << init_result; + platform_adm_ = nullptr; + return false; + } + + RestorePlatformAdmStateLocked(); + return true; +} + +// Re-applies state that may have been recorded before the Platform ADM +// existed: the audio transport registered by WebRTC and any device selection. +// Without this, an ADM created after RegisterAudioCallback() would never +// receive audio data. +void AdmProxy::RestorePlatformAdmStateLocked() { + RTC_DCHECK(platform_adm_); + if (audio_transport_) { + recording_transport_proxy_.set_real_transport(audio_transport_); + platform_adm_->RegisterAudioCallback(&recording_transport_proxy_); + } + if (!selected_playout_guid_.empty()) { + int16_t count = platform_adm_->PlayoutDevices(); + for (int16_t i = 0; i < count; i++) { + char name[webrtc::kAdmMaxDeviceNameSize] = {0}; + char guid[webrtc::kAdmMaxGuidSize] = {0}; + if (platform_adm_->PlayoutDeviceName(static_cast(i), name, guid) == 0 && + selected_playout_guid_ == guid) { + platform_adm_->SetPlayoutDevice(static_cast(i)); + break; + } + } + } + if (!selected_recording_guid_.empty()) { + int16_t count = platform_adm_->RecordingDevices(); + for (int16_t i = 0; i < count; i++) { + char name[webrtc::kAdmMaxDeviceNameSize] = {0}; + char guid[webrtc::kAdmMaxGuidSize] = {0}; + if (platform_adm_->RecordingDeviceName(static_cast(i), name, guid) == 0 && + selected_recording_guid_ == guid) { + platform_adm_->SetRecordingDevice(static_cast(i)); + break; + } + } + } +} + +bool AdmProxy::EnsurePlatformAdm() { + webrtc::MutexLock lock(&mutex_); + return EnsurePlatformAdmCreated(); +} + +bool AdmProxy::platform_adm_available() const { + webrtc::MutexLock lock(&mutex_); + return platform_adm_ != nullptr; +} + +bool AdmProxy::AcquirePlatformAdm() { + webrtc::MutexLock lock(&mutex_); + + // Lazily create the Platform ADM on first acquire (and retry after any + // earlier failed attempt, e.g. when the OS audio stack was still starting). + if (!EnsurePlatformAdmCreated()) { + RTC_LOG(LS_ERROR) << "AdmProxy::AcquirePlatformAdm() - Failed to create Platform ADM"; + return false; + } + + int old_ref_count = platform_adm_ref_count_; + platform_adm_ref_count_++; + + // If this is the first acquisition and playout/recording is enabled, + // we may need to switch from synthetic mode to platform ADM + if (old_ref_count == 0) { + SwitchPlayoutModeIfNeeded(); + SwitchRecordingAdmIfNeeded(); + } + + return true; +} + +void AdmProxy::ReleasePlatformAdm() { + webrtc::MutexLock lock(&mutex_); + + if (platform_adm_ref_count_ <= 0) { + RTC_LOG(LS_WARNING) << "AdmProxy::ReleasePlatformAdm() called with ref_count=" + << platform_adm_ref_count_; + return; + } + + platform_adm_ref_count_--; + + // If ref_count reaches 0, switch back from platform ADM to synthetic mode + // Note: We do NOT terminate the Platform ADM - it stays alive until destructor. + // This avoids iOS KVO race conditions from re-creating the ADM. + if (platform_adm_ref_count_ == 0) { + SwitchPlayoutModeIfNeeded(); + SwitchRecordingAdmIfNeeded(); + } +} + +int AdmProxy::platform_adm_ref_count() const { + webrtc::MutexLock lock(&mutex_); + return platform_adm_ref_count_; +} + +bool AdmProxy::is_platform_adm_active() const { + webrtc::MutexLock lock(&mutex_); + // Platform ADM is considered active when there are users and playout/recording is enabled + return platform_adm_ != nullptr && platform_adm_ref_count_ > 0; +} + +// ============================================================================= +// Recording/Playout Control +// ============================================================================= + +void AdmProxy::set_recording_enabled(bool enabled) { + webrtc::MutexLock lock(&mutex_); + if (recording_enabled_ == enabled) { + return; + } + recording_enabled_ = enabled; + SwitchRecordingAdmIfNeeded(); +} + +bool AdmProxy::recording_enabled() const { + webrtc::MutexLock lock(&mutex_); + return recording_enabled_; +} + +void AdmProxy::set_playout_enabled(bool enabled) { + webrtc::MutexLock lock(&mutex_); + if (playout_enabled_ == enabled) { + return; + } + playout_enabled_ = enabled; + SwitchPlayoutModeIfNeeded(); +} + +bool AdmProxy::playout_enabled() const { + webrtc::MutexLock lock(&mutex_); + return playout_enabled_; +} + +// ============================================================================= +// Mode Switching Helpers (called with mutex held) +// ============================================================================= + +void AdmProxy::SwitchPlayoutModeIfNeeded() { + if (!playing_) return; + + bool use_platform = is_platform_playout_active(); + + if (use_platform) { + // Switch to platform mode - stop synthetic, start platform ADM + if (synthetic_adm_) { + synthetic_adm_->StopPlayout(); + } + if (platform_adm_) { + platform_adm_->InitPlayout(); + platform_adm_->StartPlayout(); + } + } else { + // Switch to synthetic mode - stop platform ADM, start synthetic ADM + if (platform_adm_) { + platform_adm_->StopPlayout(); + } + if (synthetic_adm_) { + synthetic_adm_->StartPlayout(); + } + } +} + +void AdmProxy::SwitchRecordingAdmIfNeeded() { + if (!recording_) return; + + // Stop platform ADM recording (only one that supports recording) + if (platform_adm_) platform_adm_->StopRecording(); + + // Start if new ADM supports recording + auto* adm = recording_adm(); + if (adm) { + adm->InitRecording(); + adm->StartRecording(); + } else { + recording_ = false; + } +} + +// ============================================================================= +// AudioDeviceModule Interface Implementation +// ============================================================================= + +int32_t AdmProxy::ActiveAudioLayer(AudioLayer* audioLayer) const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->ActiveAudioLayer(audioLayer); + } + *audioLayer = AudioLayer::kDummyAudio; + return 0; +} + +int32_t AdmProxy::RegisterAudioCallback(webrtc::AudioTransport* transport) { + webrtc::MutexLock lock(&mutex_); + audio_transport_ = transport; + recording_transport_proxy_.set_real_transport(transport); + + // Register the interposing proxy with both ADMs so they're ready when we + // switch modes. The proxy tees recorded frames and forwards to `transport`. + webrtc::AudioTransport* proxy = &recording_transport_proxy_; + if (synthetic_adm_) { + synthetic_adm_->RegisterAudioCallback(proxy); + } + if (platform_adm_) { + platform_adm_->RegisterAudioCallback(proxy); + } + return 0; +} + +int32_t AdmProxy::Init() { + // Init is a no-op - Platform ADM is created lazily via AcquirePlatformAdm() + return 0; +} + +int32_t AdmProxy::Terminate() { + webrtc::MutexLock lock(&mutex_); + + int32_t result = 0; + if (synthetic_adm_) { + result = synthetic_adm_->Terminate(); + } + if (platform_adm_) { + int32_t platform_result = platform_adm_->Terminate(); + if (result == 0) result = platform_result; + } + return result; +} + +bool AdmProxy::Initialized() const { + webrtc::MutexLock lock(&mutex_); + // We're initialized if at least one ADM is initialized + bool synthetic_init = synthetic_adm_ && synthetic_adm_->Initialized(); + bool platform_init = platform_adm_ && platform_adm_->Initialized(); + return synthetic_init || platform_init; +} + +int16_t AdmProxy::PlayoutDevices() { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->PlayoutDevices(); + } + // In synthetic mode, return 0 devices (no platform audio) + return 0; +} + +int16_t AdmProxy::RecordingDevices() { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->RecordingDevices(); + } + // In synthetic mode, return 0 devices (no platform audio) + return 0; +} + +int32_t AdmProxy::PlayoutDeviceName(uint16_t index, + char name[webrtc::kAdmMaxDeviceNameSize], + char guid[webrtc::kAdmMaxGuidSize]) { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->PlayoutDeviceName(index, name, guid); + } + return -1; +} + +int32_t AdmProxy::RecordingDeviceName(uint16_t index, + char name[webrtc::kAdmMaxDeviceNameSize], + char guid[webrtc::kAdmMaxGuidSize]) { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->RecordingDeviceName(index, name, guid); + } + return -1; +} + +int32_t AdmProxy::SetPlayoutDevice(uint16_t index) { + webrtc::MutexLock lock(&mutex_); + selected_playout_device_ = index; + + // Also store the GUID for this device for robust restoration + if (platform_adm_) { + char name[webrtc::kAdmMaxDeviceNameSize] = {0}; + char guid[webrtc::kAdmMaxGuidSize] = {0}; + if (platform_adm_->PlayoutDeviceName(index, name, guid) == 0) { + selected_playout_guid_ = guid; + } + return platform_adm_->SetPlayoutDevice(index); + } + return 0; +} + +int32_t AdmProxy::SetPlayoutDevice(WindowsDeviceType device) { + webrtc::MutexLock lock(&mutex_); + // Note: When using WindowsDeviceType, we can't easily get the GUID + // The GUID will be populated on next CreatePlatformAdm if needed + selected_playout_guid_.clear(); + if (platform_adm_) { + return platform_adm_->SetPlayoutDevice(device); + } + return 0; +} + +int32_t AdmProxy::SetRecordingDevice(uint16_t index) { + webrtc::MutexLock lock(&mutex_); + selected_recording_device_ = index; + + // Also store the GUID for this device for robust restoration + if (platform_adm_) { + char name[webrtc::kAdmMaxDeviceNameSize] = {0}; + char guid[webrtc::kAdmMaxGuidSize] = {0}; + if (platform_adm_->RecordingDeviceName(index, name, guid) == 0) { + selected_recording_guid_ = guid; + } + return platform_adm_->SetRecordingDevice(index); + } + return 0; +} + +int32_t AdmProxy::SetRecordingDevice(WindowsDeviceType device) { + webrtc::MutexLock lock(&mutex_); + // Note: When using WindowsDeviceType, we can't easily get the GUID + // The GUID will be populated on next CreatePlatformAdm if needed + selected_recording_guid_.clear(); + if (platform_adm_) { + return platform_adm_->SetRecordingDevice(device); + } + return 0; +} + +int32_t AdmProxy::PlayoutIsAvailable(bool* available) { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->PlayoutIsAvailable(available); + } + *available = true; // Synthetic playout is always available + return 0; +} + +int32_t AdmProxy::InitPlayout() { + webrtc::MutexLock lock(&mutex_); + + if (is_platform_playout_active()) { + if (platform_adm_) { + int32_t result = platform_adm_->InitPlayout(); + if (result == 0) { + playout_initialized_ = true; + } + return result; + } + return -1; + } + + // Synthetic mode + if (synthetic_adm_) { + int32_t result = synthetic_adm_->InitPlayout(); + if (result == 0) { + playout_initialized_ = true; + } + return result; + } + return -1; +} + +bool AdmProxy::PlayoutIsInitialized() const { + webrtc::MutexLock lock(&mutex_); + if (is_platform_playout_active()) { + return platform_adm_ && platform_adm_->PlayoutIsInitialized(); + } + // Synthetic mode + return synthetic_adm_ && synthetic_adm_->PlayoutIsInitialized(); +} + +int32_t AdmProxy::RecordingIsAvailable(bool* available) { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->RecordingIsAvailable(available); + } + *available = false; // Recording not available in synthetic mode + return 0; +} + +int32_t AdmProxy::InitRecording() { + webrtc::MutexLock lock(&mutex_); + + auto* adm = recording_adm(); + if (!adm) { + // Recording not available (no platform ADM or recording disabled) + // Return success to avoid breaking WebRTC's initialization flow + return 0; + } + + int32_t result = adm->InitRecording(); + if (result == 0) { + recording_initialized_ = true; + } + return result; +} + +bool AdmProxy::RecordingIsInitialized() const { + webrtc::MutexLock lock(&mutex_); + auto* adm = recording_adm(); + if (adm) { + return adm->RecordingIsInitialized(); + } + return false; // Recording not available +} + +int32_t AdmProxy::StartPlayout() { + webrtc::MutexLock lock(&mutex_); + playing_ = true; + + if (is_platform_playout_active()) { + if (platform_adm_) { + return platform_adm_->StartPlayout(); + } + return -1; + } + + // Synthetic mode + if (synthetic_adm_) { + return synthetic_adm_->StartPlayout(); + } + return -1; +} + +int32_t AdmProxy::StopPlayout() { + webrtc::MutexLock lock(&mutex_); + playing_ = false; + + // Stop both ADMs + if (synthetic_adm_) { + synthetic_adm_->StopPlayout(); + } + if (platform_adm_) { + platform_adm_->StopPlayout(); + } + return 0; +} + +bool AdmProxy::Playing() const { + webrtc::MutexLock lock(&mutex_); + if (is_platform_playout_active()) { + return platform_adm_ && platform_adm_->Playing(); + } + return synthetic_adm_ && synthetic_adm_->Playing(); +} + +int32_t AdmProxy::StartRecording() { + webrtc::MutexLock lock(&mutex_); + + auto* adm = recording_adm(); + if (!adm) { + // Recording not available - return success to avoid breaking WebRTC + return 0; + } + + recording_ = true; + return adm->StartRecording(); +} + +int32_t AdmProxy::StopRecording() { + webrtc::MutexLock lock(&mutex_); + recording_ = false; + + auto* adm = recording_adm(); + if (adm) { + return adm->StopRecording(); + } + return 0; +} + +bool AdmProxy::Recording() const { + webrtc::MutexLock lock(&mutex_); + auto* adm = recording_adm(); + if (adm) { + return adm->Recording(); + } + return false; +} + +int32_t AdmProxy::InitSpeaker() { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->InitSpeaker(); + } + return 0; +} + +bool AdmProxy::SpeakerIsInitialized() const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->SpeakerIsInitialized(); + } + return true; +} + +int32_t AdmProxy::InitMicrophone() { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->InitMicrophone(); + } + return 0; +} + +bool AdmProxy::MicrophoneIsInitialized() const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->MicrophoneIsInitialized(); + } + return false; +} + +int32_t AdmProxy::SpeakerVolumeIsAvailable(bool* available) { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->SpeakerVolumeIsAvailable(available); + } + *available = false; + return 0; +} + +int32_t AdmProxy::SetSpeakerVolume(uint32_t volume) { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->SetSpeakerVolume(volume); + } + return -1; +} + +int32_t AdmProxy::SpeakerVolume(uint32_t* volume) const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->SpeakerVolume(volume); + } + return -1; +} + +int32_t AdmProxy::MaxSpeakerVolume(uint32_t* maxVolume) const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->MaxSpeakerVolume(maxVolume); + } + return -1; +} + +int32_t AdmProxy::MinSpeakerVolume(uint32_t* minVolume) const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->MinSpeakerVolume(minVolume); + } + return -1; +} + +int32_t AdmProxy::MicrophoneVolumeIsAvailable(bool* available) { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->MicrophoneVolumeIsAvailable(available); + } + *available = false; + return 0; +} + +int32_t AdmProxy::SetMicrophoneVolume(uint32_t volume) { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->SetMicrophoneVolume(volume); + } + return -1; +} + +int32_t AdmProxy::MicrophoneVolume(uint32_t* volume) const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->MicrophoneVolume(volume); + } + return -1; +} + +int32_t AdmProxy::MaxMicrophoneVolume(uint32_t* maxVolume) const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->MaxMicrophoneVolume(maxVolume); + } + return -1; +} + +int32_t AdmProxy::MinMicrophoneVolume(uint32_t* minVolume) const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->MinMicrophoneVolume(minVolume); + } + return -1; +} + +int32_t AdmProxy::SpeakerMuteIsAvailable(bool* available) { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->SpeakerMuteIsAvailable(available); + } + *available = false; + return 0; +} + +int32_t AdmProxy::SetSpeakerMute(bool enable) { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->SetSpeakerMute(enable); + } + return -1; +} + +int32_t AdmProxy::SpeakerMute(bool* enabled) const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->SpeakerMute(enabled); + } + return -1; +} + +int32_t AdmProxy::MicrophoneMuteIsAvailable(bool* available) { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->MicrophoneMuteIsAvailable(available); + } + *available = false; + return 0; +} + +int32_t AdmProxy::SetMicrophoneMute(bool enable) { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->SetMicrophoneMute(enable); + } + return -1; +} + +int32_t AdmProxy::MicrophoneMute(bool* enabled) const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->MicrophoneMute(enabled); + } + return -1; +} + +int32_t AdmProxy::StereoPlayoutIsAvailable(bool* available) const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->StereoPlayoutIsAvailable(available); + } + *available = true; + return 0; +} + +int32_t AdmProxy::SetStereoPlayout(bool enable) { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->SetStereoPlayout(enable); + } + return 0; +} + +int32_t AdmProxy::StereoPlayout(bool* enabled) const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->StereoPlayout(enabled); + } + *enabled = true; + return 0; +} + +int32_t AdmProxy::StereoRecordingIsAvailable(bool* available) const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->StereoRecordingIsAvailable(available); + } + *available = false; + return 0; +} + +int32_t AdmProxy::SetStereoRecording(bool enable) { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->SetStereoRecording(enable); + } + return 0; +} + +int32_t AdmProxy::StereoRecording(bool* enabled) const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->StereoRecording(enabled); + } + *enabled = false; + return 0; +} + +int32_t AdmProxy::PlayoutDelay(uint16_t* delayMS) const { + webrtc::MutexLock lock(&mutex_); + if (is_platform_playout_active()) { + if (platform_adm_) { + return platform_adm_->PlayoutDelay(delayMS); + } + } else if (synthetic_adm_) { + return synthetic_adm_->PlayoutDelay(delayMS); + } + *delayMS = 0; + return 0; +} + +bool AdmProxy::BuiltInAECIsAvailable() const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->BuiltInAECIsAvailable(); + } + return false; +} + +bool AdmProxy::BuiltInAGCIsAvailable() const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->BuiltInAGCIsAvailable(); + } + return false; +} + +bool AdmProxy::BuiltInNSIsAvailable() const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->BuiltInNSIsAvailable(); + } + return false; +} + +int32_t AdmProxy::EnableBuiltInAEC(bool enable) { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->EnableBuiltInAEC(enable); + } + return -1; +} + +int32_t AdmProxy::EnableBuiltInAGC(bool enable) { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->EnableBuiltInAGC(enable); + } + return -1; +} + +int32_t AdmProxy::EnableBuiltInNS(bool enable) { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->EnableBuiltInNS(enable); + } + return -1; +} + +#if defined(WEBRTC_IOS) +int AdmProxy::GetPlayoutAudioParameters(webrtc::AudioParameters* params) const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->GetPlayoutAudioParameters(params); + } + return -1; +} + +int AdmProxy::GetRecordAudioParameters(webrtc::AudioParameters* params) const { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->GetRecordAudioParameters(params); + } + return -1; +} +#endif + +int32_t AdmProxy::SetObserver(webrtc::AudioDeviceObserver* observer) { + webrtc::MutexLock lock(&mutex_); + if (platform_adm_) { + return platform_adm_->SetObserver(observer); + } + return 0; +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/android.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/android.cpp new file mode 100644 index 0000000..5e14db6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/android.cpp @@ -0,0 +1,150 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/android.h" + +#include +#include +#include +#include +#include + +#include "api/video_codecs/video_decoder_factory.h" +#include "rtc_base/logging.h" +#include "sdk/android/native_api/base/init.h" +#include "sdk/android/native_api/codecs/wrapper.h" +#include "sdk/android/native_api/jni/class_loader.h" +#include "sdk/android/native_api/jni/scoped_java_ref.h" +#include "sdk/android/src/jni/jni_helpers.h" + +// When compiling the examples app on Android, the linker complains that +// `stdout` and `stderr` symbols cannot be found. The previous workaround +// referenced `__sF`, but that symbol was removed in NDK 28+. +// Use POSIX `fdopen()` with the standard file descriptors instead — works +// across all NDK versions. +#undef stdout +FILE *stdout = fdopen(STDOUT_FILENO, "w"); + +#undef stderr +FILE *stderr = fdopen(STDERR_FILENO, "w"); + +namespace livekit_ffi { + +// Track whether Android WebRTC has been initialized to prevent crashes on double-init. +static std::atomic g_android_initialized{false}; + +void init_android(JavaVM* jvm) { + // Idempotent - safe to call multiple times + if (g_android_initialized.exchange(true)) { + RTC_LOG(LS_INFO) << "livekit_ffi::init_android() - already initialized, skipping"; + return; + } + + RTC_LOG(LS_INFO) << "livekit_ffi::init_android() called with jvm=" << (jvm ? "valid" : "null"); + if (!jvm) { + RTC_LOG(LS_ERROR) << "livekit_ffi::init_android() - JavaVM is null! Cannot initialize Android WebRTC."; + g_android_initialized.store(false); + return; + } + webrtc::InitAndroid(jvm); + RTC_LOG(LS_INFO) << "livekit_ffi::init_android() - webrtc::InitAndroid() completed"; +} + +bool init_android_context(JavaVM* jvm, uintptr_t context_ptr) { + RTC_LOG(LS_INFO) << "livekit_ffi::init_android_context() called"; + + if (!jvm || !context_ptr) { + RTC_LOG(LS_ERROR) << "livekit_ffi::init_android_context() - jvm or context is null"; + return false; + } + + // Initialize JVM first (idempotent - WebRTC handles double-init internally) + init_android(jvm); + + // Cast uintptr_t back to jobject + jobject context = reinterpret_cast(context_ptr); + + JNIEnv* env = webrtc::AttachCurrentThreadIfNeeded(); + if (!env) { + RTC_LOG(LS_ERROR) << "livekit_ffi::init_android_context() - Failed to attach to JNI"; + return false; + } + + // Find livekit.org.webrtc.ContextUtils class + jclass context_utils_class = env->FindClass("livekit/org/webrtc/ContextUtils"); + if (!context_utils_class) { + RTC_LOG(LS_ERROR) << "livekit_ffi::init_android_context() - Failed to find ContextUtils class"; + env->ExceptionClear(); + return false; + } + + // Get the initialize method + jmethodID initialize_method = env->GetStaticMethodID( + context_utils_class, "initialize", "(Landroid/content/Context;)V"); + if (!initialize_method) { + RTC_LOG(LS_ERROR) << "livekit_ffi::init_android_context() - Failed to find initialize method"; + env->ExceptionClear(); + env->DeleteLocalRef(context_utils_class); + return false; + } + + // Call ContextUtils.initialize(context) + env->CallStaticVoidMethod(context_utils_class, initialize_method, context); + + // Check for exceptions + if (env->ExceptionCheck()) { + RTC_LOG(LS_ERROR) << "livekit_ffi::init_android_context() - Exception during initialize"; + env->ExceptionDescribe(); + env->ExceptionClear(); + env->DeleteLocalRef(context_utils_class); + return false; + } + + env->DeleteLocalRef(context_utils_class); + RTC_LOG(LS_INFO) << "livekit_ffi::init_android_context() - ContextUtils initialized successfully"; + return true; +} + +std::unique_ptr +CreateAndroidVideoEncoderFactory() { + JNIEnv* env = webrtc::AttachCurrentThreadIfNeeded(); + webrtc::ScopedJavaLocalRef factory_class = + webrtc::GetClass(env, "livekit/org/webrtc/DefaultVideoEncoderFactory"); + + jmethodID ctor = env->GetMethodID(factory_class.obj(), "", + "(Llivekit/org/webrtc/EglBase$Context;ZZ)V"); + + jobject encoder_factory = + env->NewObject(factory_class.obj(), ctor, nullptr, true, false); + + return webrtc::JavaToNativeVideoEncoderFactory(env, encoder_factory); +} + +std::unique_ptr +CreateAndroidVideoDecoderFactory() { + JNIEnv* env = webrtc::AttachCurrentThreadIfNeeded(); + + webrtc::ScopedJavaLocalRef factory_class = + webrtc::GetClass(env, "livekit/org/webrtc/WrappedVideoDecoderFactory"); + + jmethodID ctor = env->GetMethodID(factory_class.obj(), "", + "(Llivekit/org/webrtc/EglBase$Context;)V"); + + jobject decoder_factory = env->NewObject(factory_class.obj(), ctor, nullptr); + return webrtc::JavaToNativeVideoDecoderFactory(env, decoder_factory); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/android.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/android.rs new file mode 100644 index 0000000..5552985 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/android.rs @@ -0,0 +1,43 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[cfg(target_os = "android")] +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + unsafe extern "C++" { + include!("livekit/android.h"); + + type JavaVM; + + /// Initialize Android WebRTC with the JVM. + /// Called automatically by init_android_context(), so only call directly + /// in JNI_OnLoad or when you don't have an Android Context. + /// Idempotent - safe to call multiple times. + unsafe fn init_android(vm: *mut JavaVM); + + /// Initialize Android WebRTC with the application context. + /// This is the main init function - calls init_android() internally, + /// then initializes ContextUtils for PlatformAudio. + /// Idempotent - safe to call multiple times. + /// + /// # Arguments + /// * `jvm` - The JavaVM pointer + /// * `context` - The Android application context (jobject as usize) + /// + /// # Returns + /// true if context init succeeded, false otherwise. + /// Note: JVM init always happens regardless of return value. + unsafe fn init_android_context(jvm: *mut JavaVM, context: usize) -> bool; + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/apm.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/apm.cpp new file mode 100644 index 0000000..0b885c6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/apm.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/apm.h" + +#include "api/audio/builtin_audio_processing_builder.h" +#include "api/environment/environment_factory.h" + +#include +#include + +namespace livekit_ffi { + +AudioProcessingModule::AudioProcessingModule( + const AudioProcessingConfig& config) { + apm_ = webrtc::BuiltinAudioProcessingBuilder() + .Build(webrtc::CreateEnvironment()); + + apm_->ApplyConfig(config.ToWebrtcConfig()); + apm_->Initialize(); +} + +int AudioProcessingModule::process_stream(const int16_t* src, + size_t src_len, + int16_t* dst, + size_t dst_len, + int sample_rate, + int num_channels) { + webrtc::StreamConfig stream_cfg(sample_rate, num_channels); + return apm_->ProcessStream(src, stream_cfg, stream_cfg, dst); +} + +int AudioProcessingModule::process_reverse_stream(const int16_t* src, + size_t src_len, + int16_t* dst, + size_t dst_len, + int sample_rate, + int num_channels) { + webrtc::StreamConfig stream_cfg(sample_rate, num_channels); + return apm_->ProcessReverseStream(src, stream_cfg, stream_cfg, dst); +} + +int AudioProcessingModule::set_stream_delay_ms(int delay_ms) { + return apm_->set_stream_delay_ms(delay_ms); +} + +std::unique_ptr create_apm( + bool echo_canceller_enabled, + bool gain_controller_enabled, + bool high_pass_filter_enabled, + bool noise_suppression_enabled) { + AudioProcessingConfig config; + config.echo_canceller_enabled = echo_canceller_enabled; + config.gain_controller_enabled = gain_controller_enabled; + config.high_pass_filter_enabled = high_pass_filter_enabled; + config.noise_suppression_enabled = noise_suppression_enabled; + return std::make_unique(config); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/apm.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/apm.rs new file mode 100644 index 0000000..6f073f9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/apm.rs @@ -0,0 +1,55 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::impl_thread_safety; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + unsafe extern "C++" { + include!("livekit/apm.h"); + + type AudioProcessingModule; + + unsafe fn process_stream( + self: Pin<&mut AudioProcessingModule>, + src: *const i16, + src_len: usize, + dst: *mut i16, + dst_len: usize, + sample_rate: i32, + num_channels: i32, + ) -> i32; + + unsafe fn process_reverse_stream( + self: Pin<&mut AudioProcessingModule>, + src: *const i16, + src_len: usize, + dst: *mut i16, + dst_len: usize, + sample_rate: i32, + num_channels: i32, + ) -> i32; + + fn set_stream_delay_ms(self: Pin<&mut AudioProcessingModule>, delay: i32) -> i32; + + fn create_apm( + echo_canceller_enabled: bool, + gain_controller_enabled: bool, + high_pass_filter_enabled: bool, + noise_suppression_enabled: bool, + ) -> UniquePtr; + } +} + +impl_thread_safety!(ffi::AudioProcessingModule, Send + Sync); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_device_controller.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_device_controller.cpp new file mode 100644 index 0000000..0d59672 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_device_controller.cpp @@ -0,0 +1,216 @@ +/* + * Copyright 2026 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/audio_device_controller.h" + +#include +#include + +namespace livekit_ffi { + +AudioDeviceController::AudioDeviceController( + webrtc::scoped_refptr adm_proxy) + : adm_proxy_(std::move(adm_proxy)) {} + +int16_t AudioDeviceController::playout_devices() const { + return adm_proxy_->PlayoutDevices(); +} + +int16_t AudioDeviceController::recording_devices() const { + return adm_proxy_->RecordingDevices(); +} + +rust::String AudioDeviceController::playout_device_name(uint16_t index) const { + char name[webrtc::kAdmMaxDeviceNameSize] = {0}; + char guid[webrtc::kAdmMaxGuidSize] = {0}; + adm_proxy_->PlayoutDeviceName(index, name, guid); + return rust::String(name); +} + +rust::String AudioDeviceController::recording_device_name(uint16_t index) const { + char name[webrtc::kAdmMaxDeviceNameSize] = {0}; + char guid[webrtc::kAdmMaxGuidSize] = {0}; + adm_proxy_->RecordingDeviceName(index, name, guid); + return rust::String(name); +} + +rust::String AudioDeviceController::playout_device_guid(uint16_t index) const { + char name[webrtc::kAdmMaxDeviceNameSize] = {0}; + char guid[webrtc::kAdmMaxGuidSize] = {0}; + adm_proxy_->PlayoutDeviceName(index, name, guid); + return rust::String(guid); +} + +rust::String AudioDeviceController::recording_device_guid(uint16_t index) const { + char name[webrtc::kAdmMaxDeviceNameSize] = {0}; + char guid[webrtc::kAdmMaxGuidSize] = {0}; + adm_proxy_->RecordingDeviceName(index, name, guid); + return rust::String(guid); +} + +bool AudioDeviceController::set_playout_device(uint16_t index) const { + return adm_proxy_->SetPlayoutDevice(index) == 0; +} + +bool AudioDeviceController::set_recording_device(uint16_t index) const { + return adm_proxy_->SetRecordingDevice(index) == 0; +} + +bool AudioDeviceController::set_playout_device_by_guid(rust::String guid) const { + int16_t count = adm_proxy_->PlayoutDevices(); + + // Try to find a device matching the GUID + for (int16_t i = 0; i < count; i++) { + char name[webrtc::kAdmMaxDeviceNameSize] = {0}; + char device_guid[webrtc::kAdmMaxGuidSize] = {0}; + if (adm_proxy_->PlayoutDeviceName(i, name, device_guid) == 0) { + if (std::string(guid.c_str()) == std::string(device_guid)) { + return adm_proxy_->SetPlayoutDevice(i) == 0; + } + } + } + + // No match found - fall back to default device (index 0). + // This handles mobile platforms (iOS/Android) where: + // - GUIDs may be empty or not meaningful + // - Device selection is a no-op (system handles routing) + if (count > 0) { + return adm_proxy_->SetPlayoutDevice(0) == 0; + } + return false; +} + +bool AudioDeviceController::set_recording_device_by_guid(rust::String guid) const { + int16_t count = adm_proxy_->RecordingDevices(); + + // Try to find a device matching the GUID + for (int16_t i = 0; i < count; i++) { + char name[webrtc::kAdmMaxDeviceNameSize] = {0}; + char device_guid[webrtc::kAdmMaxGuidSize] = {0}; + if (adm_proxy_->RecordingDeviceName(i, name, device_guid) == 0) { + if (std::string(guid.c_str()) == std::string(device_guid)) { + return adm_proxy_->SetRecordingDevice(i) == 0; + } + } + } + + // No match found - fall back to default device (index 0). + // This handles mobile platforms (iOS/Android) where: + // - GUIDs may be empty or not meaningful + // - Device selection is a no-op (system handles routing) + if (count > 0) { + return adm_proxy_->SetRecordingDevice(0) == 0; + } + return false; +} + +bool AudioDeviceController::stop_recording() const { + return adm_proxy_->StopRecording() == 0; +} + +bool AudioDeviceController::init_recording() const { + return adm_proxy_->InitRecording() == 0; +} + +bool AudioDeviceController::start_recording() const { + return adm_proxy_->StartRecording() == 0; +} + +bool AudioDeviceController::recording_is_initialized() const { + return adm_proxy_->RecordingIsInitialized(); +} + +bool AudioDeviceController::stop_playout() const { + return adm_proxy_->StopPlayout() == 0; +} + +bool AudioDeviceController::init_playout() const { + return adm_proxy_->InitPlayout() == 0; +} + +bool AudioDeviceController::start_playout() const { + return adm_proxy_->StartPlayout() == 0; +} + +bool AudioDeviceController::playout_is_initialized() const { + return adm_proxy_->PlayoutIsInitialized(); +} + +bool AudioDeviceController::builtin_aec_is_available() const { + return adm_proxy_->BuiltInAECIsAvailable(); +} + +bool AudioDeviceController::builtin_agc_is_available() const { + return adm_proxy_->BuiltInAGCIsAvailable(); +} + +bool AudioDeviceController::builtin_ns_is_available() const { + return adm_proxy_->BuiltInNSIsAvailable(); +} + +bool AudioDeviceController::enable_builtin_aec(bool enable) const { + return adm_proxy_->EnableBuiltInAEC(enable) == 0; +} + +bool AudioDeviceController::enable_builtin_agc(bool enable) const { + return adm_proxy_->EnableBuiltInAGC(enable) == 0; +} + +bool AudioDeviceController::enable_builtin_ns(bool enable) const { + return adm_proxy_->EnableBuiltInNS(enable) == 0; +} + +void AudioDeviceController::set_adm_recording_enabled(bool enabled) const { + adm_proxy_->set_recording_enabled(enabled); +} + +bool AudioDeviceController::adm_recording_enabled() const { + return adm_proxy_->recording_enabled(); +} + +void AudioDeviceController::set_adm_playout_enabled(bool enabled) const { + adm_proxy_->set_playout_enabled(enabled); +} + +bool AudioDeviceController::adm_playout_enabled() const { + return adm_proxy_->playout_enabled(); +} + +bool AudioDeviceController::acquire_platform_adm() const { + return adm_proxy_->AcquirePlatformAdm(); +} + +void AudioDeviceController::release_platform_adm() const { + adm_proxy_->ReleasePlatformAdm(); +} + +int AudioDeviceController::platform_adm_ref_count() const { + return adm_proxy_->platform_adm_ref_count(); +} + +bool AudioDeviceController::is_platform_adm_active() const { + return adm_proxy_->is_platform_adm_active(); +} + +bool AudioDeviceController::ensure_platform_adm() const { + return adm_proxy_->EnsurePlatformAdm(); +} + +bool AudioDeviceController::platform_adm_available() const { + return adm_proxy_->platform_adm_available(); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_device_controller.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_device_controller.rs new file mode 100644 index 0000000..0e85572 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_device_controller.rs @@ -0,0 +1,121 @@ +// Copyright 2026 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub use cxx::SharedPtr; + +use crate::impl_thread_safety; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + unsafe extern "C++" { + include!("livekit/audio_device_controller.h"); + include!("livekit/peer_connection_factory.h"); + + type AudioDeviceController; + type PeerConnectionFactory = crate::peer_connection_factory::ffi::PeerConnectionFactory; + + fn audio_device(self: &PeerConnectionFactory) -> SharedPtr; + + fn playout_devices(self: &AudioDeviceController) -> i16; + fn recording_devices(self: &AudioDeviceController) -> i16; + fn playout_device_name(self: &AudioDeviceController, index: u16) -> String; + fn recording_device_name(self: &AudioDeviceController, index: u16) -> String; + fn playout_device_guid(self: &AudioDeviceController, index: u16) -> String; + fn recording_device_guid(self: &AudioDeviceController, index: u16) -> String; + + fn set_playout_device(self: &AudioDeviceController, index: u16) -> bool; + fn set_recording_device(self: &AudioDeviceController, index: u16) -> bool; + fn set_playout_device_by_guid(self: &AudioDeviceController, guid: String) -> bool; + fn set_recording_device_by_guid(self: &AudioDeviceController, guid: String) -> bool; + + fn stop_recording(self: &AudioDeviceController) -> bool; + fn init_recording(self: &AudioDeviceController) -> bool; + fn start_recording(self: &AudioDeviceController) -> bool; + fn recording_is_initialized(self: &AudioDeviceController) -> bool; + + fn stop_playout(self: &AudioDeviceController) -> bool; + fn init_playout(self: &AudioDeviceController) -> bool; + fn start_playout(self: &AudioDeviceController) -> bool; + fn playout_is_initialized(self: &AudioDeviceController) -> bool; + + fn builtin_aec_is_available(self: &AudioDeviceController) -> bool; + fn builtin_agc_is_available(self: &AudioDeviceController) -> bool; + fn builtin_ns_is_available(self: &AudioDeviceController) -> bool; + fn enable_builtin_aec(self: &AudioDeviceController, enable: bool) -> bool; + fn enable_builtin_agc(self: &AudioDeviceController, enable: bool) -> bool; + fn enable_builtin_ns(self: &AudioDeviceController, enable: bool) -> bool; + + fn set_adm_recording_enabled(self: &AudioDeviceController, enabled: bool); + fn adm_recording_enabled(self: &AudioDeviceController) -> bool; + + fn set_adm_playout_enabled(self: &AudioDeviceController, enabled: bool); + fn adm_playout_enabled(self: &AudioDeviceController) -> bool; + + fn acquire_platform_adm(self: &AudioDeviceController) -> bool; + fn release_platform_adm(self: &AudioDeviceController); + fn platform_adm_ref_count(self: &AudioDeviceController) -> i32; + fn is_platform_adm_active(self: &AudioDeviceController) -> bool; + fn ensure_platform_adm(self: &AudioDeviceController) -> bool; + fn platform_adm_available(self: &AudioDeviceController) -> bool; + } +} + +impl_thread_safety!(ffi::AudioDeviceController, Send + Sync); + +#[cfg(test)] +mod tests { + use crate::peer_connection_factory::ffi::create_peer_connection_factory; + use std::sync::Mutex; + + static TEST_MUTEX: Mutex<()> = Mutex::new(()); + + #[test] + fn test_audio_device_controller_bridge() { + let _guard = TEST_MUTEX.lock().expect("test mutex poisoned"); + let factory = create_peer_connection_factory(); + let audio = factory.audio_device(); + + let recording_count = audio.recording_devices(); + let playout_count = audio.playout_devices(); + assert!(recording_count >= 0); + assert!(playout_count >= 0); + + if recording_count > 0 { + let _ = audio.recording_device_name(0); + let guid = audio.recording_device_guid(0); + let _ = audio.set_recording_device_by_guid(guid); + } + + if playout_count > 0 { + let _ = audio.playout_device_name(0); + let guid = audio.playout_device_guid(0); + let _ = audio.set_playout_device_by_guid(guid); + } + + let initial_recording = audio.adm_recording_enabled(); + audio.set_adm_recording_enabled(!initial_recording); + assert_eq!(audio.adm_recording_enabled(), !initial_recording); + audio.set_adm_recording_enabled(initial_recording); + assert_eq!(audio.adm_recording_enabled(), initial_recording); + + let initial_playout = audio.adm_playout_enabled(); + audio.set_adm_playout_enabled(!initial_playout); + assert_eq!(audio.adm_playout_enabled(), !initial_playout); + audio.set_adm_playout_enabled(initial_playout); + assert_eq!(audio.adm_playout_enabled(), initial_playout); + + assert!(audio.platform_adm_ref_count() >= 0); + let _ = audio.is_platform_adm_active(); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_mixer.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_mixer.cpp new file mode 100644 index 0000000..409ac9a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_mixer.cpp @@ -0,0 +1,105 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/audio_mixer.h" + +#include +#include + +#include "api/audio/audio_frame.h" +#include "api/audio/audio_mixer.h" +#include "modules/audio_mixer/audio_mixer_impl.h" +#include "webrtc-sys/src/audio_mixer.rs.h" + +namespace livekit_ffi { + +AudioMixer::AudioMixer() { + audio_mixer_ = webrtc::AudioMixerImpl::Create(); +} + +void AudioMixer::add_source(rust::Box source) { + auto native_source = std::make_shared(std::move(source)); + + webrtc::MutexLock lock(&sources_mutex_); + audio_mixer_->AddSource(native_source.get()); + sources_.push_back(native_source); +} + +void AudioMixer::remove_source(int source_ssrc) { + webrtc::MutexLock lock(&sources_mutex_); + auto it = std::find_if( + sources_.begin(), sources_.end(), + [source_ssrc](const auto& s) { return s->Ssrc() == source_ssrc; }); + + if (it != sources_.end()) { + audio_mixer_->RemoveSource(it->get()); + sources_.erase(it); + } +} + +size_t AudioMixer::mix(size_t number_of_channels) { + audio_mixer_->Mix(number_of_channels, &frame_); + return frame_.num_channels() * frame_.samples_per_channel(); +} + +const int16_t* AudioMixer::data() const { + return frame_.data(); +} + +std::unique_ptr create_audio_mixer() { + return std::make_unique(); +} + +AudioMixerSource::AudioMixerSource(rust::Box source) + : source_(std::move(source)) {} + +int AudioMixerSource::Ssrc() const { + return source_->ssrc(); +} + +int AudioMixerSource::PreferredSampleRate() const { + return source_->preferred_sample_rate(); +} + +webrtc::AudioMixer::Source::AudioFrameInfo +AudioMixerSource::GetAudioFrameWithInfo(int sample_rate, + webrtc::AudioFrame* audio_frame) { + NativeAudioFrame frame(audio_frame); + + livekit_ffi::AudioFrameInfo result = + source_->get_audio_frame_with_info(sample_rate, frame); + + if (result == livekit_ffi::AudioFrameInfo::Normal) { + return webrtc::AudioMixer::Source::AudioFrameInfo::kNormal; + } else if (result == livekit_ffi::AudioFrameInfo::Muted) { + return webrtc::AudioMixer::Source::AudioFrameInfo::kMuted; + } else { + return webrtc::AudioMixer::Source::AudioFrameInfo::kError; + } +} + +void NativeAudioFrame::update_frame(uint32_t timestamp, + const int16_t* data, + size_t samples_per_channel, + int sample_rate_hz, + size_t num_channels) { + frame_->UpdateFrame(timestamp, data, samples_per_channel, sample_rate_hz, + webrtc::AudioFrame::SpeechType::kNormalSpeech, + webrtc::AudioFrame::VADActivity::kVadUnknown, + num_channels); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_mixer.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_mixer.rs new file mode 100644 index 0000000..f90d2d7 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_mixer.rs @@ -0,0 +1,106 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{pin::Pin, sync::Arc}; + +use ffi::AudioFrameInfo; + +use crate::impl_thread_safety; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + unsafe extern "C++" { + include!("livekit/audio_mixer.h"); + + type AudioMixer; + + unsafe fn add_source(self: Pin<&mut AudioMixer>, src: Box); + + unsafe fn remove_source(self: Pin<&mut AudioMixer>, ssrc: i32); + + unsafe fn mix(self: Pin<&mut AudioMixer>, num_channels: usize) -> usize; + + unsafe fn data(self: &AudioMixer) -> *const i16; + + fn create_audio_mixer() -> UniquePtr; + + type NativeAudioFrame; + + unsafe fn update_frame( + self: Pin<&mut NativeAudioFrame>, + timestamp: u32, + data: *const i16, + samples_per_channel: usize, + sample_rate_hz: i32, + num_channels: usize, + ); + } + + pub enum AudioFrameInfo { + Normal, + Muted, + Error, + } + + extern "Rust" { + type AudioMixerSourceWrapper; + + fn ssrc(self: &AudioMixerSourceWrapper) -> i32; + fn preferred_sample_rate(self: &AudioMixerSourceWrapper) -> i32; + fn get_audio_frame_with_info( + self: &AudioMixerSourceWrapper, + target_sample_rate: i32, + frame: Pin<&mut NativeAudioFrame>, + ) -> AudioFrameInfo; + } +} + +pub trait AudioMixerSource { + fn ssrc(&self) -> i32; + fn preferred_sample_rate(&self) -> i32; + fn get_audio_frame_with_info( + &self, + target_sample_rate: i32, + frame: NativeAudioFrame, + ) -> AudioFrameInfo; +} + +pub struct AudioMixerSourceWrapper { + source: Arc, +} + +pub type NativeAudioFrame<'a> = Pin<&'a mut ffi::NativeAudioFrame>; + +impl AudioMixerSourceWrapper { + pub fn new(source: Arc) -> Self { + Self { source } + } + + pub fn ssrc(&self) -> i32 { + self.source.ssrc() + } + pub fn preferred_sample_rate(&self) -> i32 { + self.source.preferred_sample_rate() + } + + pub fn get_audio_frame_with_info( + &self, + target_sample_rate: i32, + frame: Pin<&mut ffi::NativeAudioFrame>, + ) -> AudioFrameInfo { + self.source.get_audio_frame_with_info(target_sample_rate, frame) + } +} + +impl_thread_safety!(ffi::AudioMixer, Send + Sync); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_resampler.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_resampler.cpp new file mode 100644 index 0000000..6c624a4 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_resampler.cpp @@ -0,0 +1,52 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/audio_resampler.h" + +#include + +#include "audio/remix_resample.h" +#include "api/audio/audio_view.h" +#include "api/audio/audio_frame.h" + +namespace livekit_ffi { + +size_t AudioResampler::remix_and_resample(const int16_t* src, + size_t samples_per_channel, + size_t num_channels, + int sample_rate, + size_t dest_num_channels, + int dest_sample_rate) { + frame_.num_channels_ = dest_num_channels; + frame_.sample_rate_hz_ = dest_sample_rate; + frame_.samples_per_channel_ = webrtc::SampleRateToDefaultChannelSize(dest_sample_rate); + webrtc::InterleavedView source(static_cast(src), + samples_per_channel, + num_channels); + webrtc::voe::RemixAndResample(source, sample_rate, &resampler_, &frame_); + + return frame_.num_channels() * frame_.samples_per_channel() * sizeof(int16_t); +} + +const int16_t* AudioResampler::data() const { + return frame_.data(); +} + +std::unique_ptr create_audio_resampler() { + return std::make_unique(); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_resampler.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_resampler.rs new file mode 100644 index 0000000..954d90e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_resampler.rs @@ -0,0 +1,40 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::impl_thread_safety; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + unsafe extern "C++" { + include!("livekit/audio_resampler.h"); + + type AudioResampler; + + unsafe fn remix_and_resample( + self: Pin<&mut AudioResampler>, + src: *const i16, + samples_per_channel: usize, + num_channels: usize, + sample_rate: i32, + dst_num_channels: usize, + dst_sample_rate: i32, + ) -> usize; + + unsafe fn data(self: &AudioResampler) -> *const i16; + + fn create_audio_resampler() -> UniquePtr; + } +} + +impl_thread_safety!(ffi::AudioResampler, Send + Sync); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_track.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_track.cpp new file mode 100644 index 0000000..1738515 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_track.cpp @@ -0,0 +1,319 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/audio_track.h" + +#include +#include +#include +#include + +#include "api/audio_options.h" +#include "api/audio/audio_frame.h" +#include "api/media_stream_interface.h" +#include "api/task_queue/task_queue_base.h" +#include "audio/remix_resample.h" +#include "common_audio/include/audio_util.h" +#include "livekit/global_task_queue.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/synchronization/mutex.h" +#include "rust/cxx.h" +#include "webrtc-sys/src/audio_track.rs.h" + +namespace livekit_ffi { + +inline webrtc::AudioOptions to_native_audio_options( + const AudioSourceOptions& options) { + webrtc::AudioOptions rtc_options{}; + rtc_options.echo_cancellation = options.echo_cancellation; + rtc_options.noise_suppression = options.noise_suppression; + rtc_options.auto_gain_control = options.auto_gain_control; + return rtc_options; +} + +inline AudioSourceOptions to_rust_audio_options( + const webrtc::AudioOptions& rtc_options) { + AudioSourceOptions options{}; + options.echo_cancellation = rtc_options.echo_cancellation.value_or(false); + options.noise_suppression = rtc_options.noise_suppression.value_or(false); + options.auto_gain_control = rtc_options.auto_gain_control.value_or(false); + return options; +} + +AudioTrack::AudioTrack(std::shared_ptr rtc_runtime, + webrtc::scoped_refptr track) + : MediaStreamTrack(rtc_runtime, std::move(track)) {} + +AudioTrack::~AudioTrack() { + webrtc::MutexLock lock(&mutex_); + for (auto& sink : sinks_) { + track()->RemoveSink(sink.get()); + } +} + +void AudioTrack::add_sink(const std::shared_ptr& sink) const { + webrtc::MutexLock lock(&mutex_); + track()->AddSink(sink.get()); + sinks_.push_back(sink); +} + +void AudioTrack::remove_sink( + const std::shared_ptr& sink) const { + webrtc::MutexLock lock(&mutex_); + track()->RemoveSink(sink.get()); + sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end()); +} + +NativeAudioSink::NativeAudioSink(rust::Box observer, + int sample_rate, + int num_channels) + : observer_(std::move(observer)), + sample_rate_(sample_rate), + num_channels_(num_channels) { + frame_.sample_rate_hz_ = sample_rate; + frame_.num_channels_ = num_channels; + frame_.samples_per_channel_ = webrtc::SampleRateToDefaultChannelSize(sample_rate); +} + +void NativeAudioSink::OnData(const void* audio_data, + int bits_per_sample, + int sample_rate, + size_t number_of_channels, + size_t number_of_frames) { + RTC_CHECK_EQ(16, bits_per_sample); + + const int16_t* data = static_cast(audio_data); + + if (sample_rate_ != sample_rate || num_channels_ != number_of_channels) { + webrtc::InterleavedView source(data, + number_of_frames, + number_of_channels); + // resample/remix before capturing + webrtc::voe::RemixAndResample(source, sample_rate, &resampler_, &frame_); + + rust::Slice rust_slice( + frame_.data(), frame_.num_channels() * frame_.samples_per_channel()); + + observer_->on_data(rust_slice, frame_.sample_rate_hz(), + frame_.num_channels(), frame_.samples_per_channel()); + + } else { + rust::Slice rust_slice( + data, number_of_channels * number_of_frames); + + observer_->on_data(rust_slice, sample_rate, number_of_channels, + number_of_frames); + } +} + +std::shared_ptr new_native_audio_sink( + rust::Box observer, + int sample_rate, + int num_channels) { + return std::make_shared(std::move(observer), sample_rate, + num_channels); +} + +AudioTrackSource::InternalSource::InternalSource( + const webrtc::AudioOptions& options, + int sample_rate, + int num_channels, + int queue_size_ms, // must be a multiple of 10ms + webrtc::TaskQueueFactory* task_queue_factory) + : options_(options), + sample_rate_(sample_rate), + num_channels_(num_channels), + capture_userdata_(nullptr), + on_complete_(nullptr) { + if (!queue_size_ms) { + // Set queue_size_samples_ to 0 so that capture_frame() will get to the fast path. + queue_size_samples_ = 0; + return; // no audio queue + } + + int samples10ms = sample_rate / 100 * num_channels; + + silence_buffer_.assign(samples10ms, 0); + queue_size_samples_ = queue_size_ms / 10 * samples10ms; + notify_threshold_samples_ = queue_size_samples_; // TODO: this is currently + // using x2 the queue size + buffer_.reserve(queue_size_samples_ + notify_threshold_samples_); + + audio_queue_ = + task_queue_factory->CreateTaskQueue( + "AudioSourceCapture", webrtc::TaskQueueFactory::Priority::NORMAL); + + audio_task_ = webrtc::RepeatingTaskHandle::Start( + audio_queue_.get(), + [this, samples10ms]() { + webrtc::MutexLock lock(&mutex_); + constexpr int kBitsPerSample = sizeof(int16_t) * 8; + + if (buffer_.size() >= samples10ms) { + for (auto sink : sinks_) + sink->OnData(buffer_.data(), kBitsPerSample, sample_rate_, + num_channels_, samples10ms / num_channels_); + + buffer_.erase(buffer_.begin(), buffer_.begin() + samples10ms); + } else { + // Always provide a 10ms frame to avoid playout underruns. + for (auto sink : sinks_) + sink->OnData(silence_buffer_.data(), kBitsPerSample, sample_rate_, + num_channels_, samples10ms / num_channels_); + } + + if (on_complete_ && buffer_.size() <= notify_threshold_samples_) { + on_complete_(capture_userdata_); + on_complete_ = nullptr; + capture_userdata_ = nullptr; + } + + return webrtc::TimeDelta::Millis(10); + }, + webrtc::TaskQueueBase::DelayPrecision::kHigh); +} + +AudioTrackSource::InternalSource::~InternalSource() { +} + +bool AudioTrackSource::InternalSource::capture_frame( + rust::Slice data, + uint32_t sample_rate, + uint32_t number_of_channels, + size_t number_of_frames, + const SourceContext* ctx, + void (*on_complete)(const SourceContext*)) { + webrtc::MutexLock lock(&mutex_); + + if (queue_size_samples_) { + int available = + (queue_size_samples_ + notify_threshold_samples_) - buffer_.size(); + if (available < data.size()) + return false; + + if (on_complete_ || capture_userdata_) + return false; + + buffer_.insert(buffer_.end(), data.begin(), data.end()); + + if (buffer_.size() <= notify_threshold_samples_) { + on_complete(ctx); // complete directly + } else { + on_complete_ = on_complete; + capture_userdata_ = ctx; + } + + } else { + // Fast path: capture directly when the queue buffer is 0 (frame size must be 10ms) + for (auto sink : sinks_) + sink->OnData(data.data(), sizeof(int16_t) * 8, sample_rate, + number_of_channels, number_of_frames); + } + + return true; +} + +void AudioTrackSource::InternalSource::clear_buffer() { + webrtc::MutexLock lock(&mutex_); + buffer_.clear(); +} + +webrtc::MediaSourceInterface::SourceState +AudioTrackSource::InternalSource::state() const { + return webrtc::MediaSourceInterface::SourceState::kLive; +} + +bool AudioTrackSource::InternalSource::remote() const { + return false; +} + +const webrtc::AudioOptions AudioTrackSource::InternalSource::options() const { + webrtc::MutexLock lock(&mutex_); + return options_; +} + +void AudioTrackSource::InternalSource::set_options( + const webrtc::AudioOptions& options) { + webrtc::MutexLock lock(&mutex_); + options_ = options; +} + +void AudioTrackSource::InternalSource::AddSink( + webrtc::AudioTrackSinkInterface* sink) { + webrtc::MutexLock lock(&mutex_); + sinks_.push_back(sink); +} + +void AudioTrackSource::InternalSource::RemoveSink( + webrtc::AudioTrackSinkInterface* sink) { + webrtc::MutexLock lock(&mutex_); + sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end()); +} + +AudioTrackSource::AudioTrackSource(AudioSourceOptions options, + int sample_rate, + int num_channels, + int queue_size_ms, + webrtc::TaskQueueFactory* task_queue_factory) + : source_(webrtc::make_ref_counted( + to_native_audio_options(options), + sample_rate, + num_channels, + queue_size_ms, + task_queue_factory)) {} + +AudioSourceOptions AudioTrackSource::audio_options() const { + return to_rust_audio_options(source_->options()); +} + +void AudioTrackSource::set_audio_options( + const AudioSourceOptions& options) const { + source_->set_options(to_native_audio_options(options)); +} + +bool AudioTrackSource::capture_frame( + rust::Slice audio_data, + uint32_t sample_rate, + uint32_t number_of_channels, + size_t number_of_frames, + const SourceContext* ctx, + void (*on_complete)(const SourceContext*)) const { + return source_->capture_frame(audio_data, sample_rate, number_of_channels, + number_of_frames, ctx, on_complete); +} + +void AudioTrackSource::clear_buffer() const { + source_->clear_buffer(); +} + +std::shared_ptr new_audio_track_source( + AudioSourceOptions options, + int sample_rate, + int num_channels, + int queue_size_ms) { + return std::make_shared(options, sample_rate, num_channels, + queue_size_ms, + GetGlobalTaskQueueFactory()); +} + +webrtc::scoped_refptr AudioTrackSource::get() + const { + return source_; +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_track.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_track.rs new file mode 100644 index 0000000..67ed270 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/audio_track.rs @@ -0,0 +1,124 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use cxx::type_id; +use cxx::ExternType; +use std::any::Any; +use std::sync::Arc; + +use crate::impl_thread_safety; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + + pub struct AudioSourceOptions { + pub echo_cancellation: bool, + pub noise_suppression: bool, + pub auto_gain_control: bool, + } + + extern "C++" { + include!("livekit/media_stream_track.h"); + + type MediaStreamTrack = crate::media_stream_track::ffi::MediaStreamTrack; + type CompleteCallback = crate::audio_track::CompleteCallback; + } + + unsafe extern "C++" { + include!("livekit/audio_track.h"); + + type AudioTrack; + type NativeAudioSink; + type AudioTrackSource; + + fn add_sink(self: &AudioTrack, sink: &SharedPtr); + fn remove_sink(self: &AudioTrack, sink: &SharedPtr); + fn new_native_audio_sink( + observer: Box, + sample_rate: i32, + num_channels: i32, + ) -> SharedPtr; + + unsafe fn capture_frame( + self: &AudioTrackSource, + data: &[i16], + sample_rate: u32, + nb_channels: u32, + nb_frames: usize, + userdata: *const SourceContext, + on_complete: CompleteCallback, + ) -> bool; + fn clear_buffer(self: &AudioTrackSource); + fn audio_options(self: &AudioTrackSource) -> AudioSourceOptions; + fn set_audio_options(self: &AudioTrackSource, options: &AudioSourceOptions); + + fn new_audio_track_source( + options: AudioSourceOptions, + sample_rate: i32, + num_channels: i32, + queue_size_ms: i32, + ) -> SharedPtr; + + fn audio_to_media(track: SharedPtr) -> SharedPtr; + unsafe fn media_to_audio(track: SharedPtr) -> SharedPtr; + fn _shared_audio_track() -> SharedPtr; + fn _shared_audio_track_source() -> SharedPtr; + } + + extern "Rust" { + type AudioSinkWrapper; + type SourceContext; + + fn on_data( + self: &AudioSinkWrapper, + data: &[i16], + sample_rate: i32, + nb_channels: usize, + nb_frames: usize, + ); + } +} + +impl_thread_safety!(ffi::AudioTrack, Send + Sync); +impl_thread_safety!(ffi::NativeAudioSink, Send + Sync); +impl_thread_safety!(ffi::AudioTrackSource, Send + Sync); + +#[repr(transparent)] +pub struct SourceContext(pub Box); + +#[repr(transparent)] +pub struct CompleteCallback(pub extern "C" fn(ctx: *const SourceContext)); + +unsafe impl ExternType for CompleteCallback { + type Id = type_id!("livekit_ffi::CompleteCallback"); + type Kind = cxx::kind::Trivial; +} + +pub trait AudioSink: Send { + fn on_data(&self, data: &[i16], sample_rate: i32, nb_channels: usize, nb_frames: usize); +} + +pub struct AudioSinkWrapper { + observer: Arc, +} + +impl AudioSinkWrapper { + pub fn new(observer: Arc) -> Self { + Self { observer } + } + + fn on_data(&self, data: &[i16], sample_rate: i32, nb_channels: usize, nb_frames: usize) { + self.observer.on_data(data, sample_rate, nb_channels, nb_frames); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/candidate.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/candidate.cpp new file mode 100644 index 0000000..c2e3a33 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/candidate.cpp @@ -0,0 +1,22 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/candidate.h" + +namespace livekit_ffi { +Candidate::Candidate(const webrtc::Candidate& candidate) + : candidate_(candidate) {} +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/candidate.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/candidate.rs new file mode 100644 index 0000000..7c029db --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/candidate.rs @@ -0,0 +1,24 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + unsafe extern "C++" { + include!("livekit/candidate.h"); + + type Candidate; // webrtc::Candidate + + fn _shared_candidate() -> SharedPtr; + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/data_channel.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/data_channel.cpp new file mode 100644 index 0000000..7ac068e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/data_channel.cpp @@ -0,0 +1,121 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/data_channel.h" + +#include + +#include "rtc_base/synchronization/mutex.h" +#include "webrtc-sys/src/data_channel.rs.h" + +namespace livekit_ffi { + +webrtc::DataChannelInit to_native_data_channel_init(DataChannelInit init) { + webrtc::DataChannelInit rtc_init{}; + rtc_init.id = init.id; + rtc_init.negotiated = init.negotiated; + rtc_init.ordered = init.ordered; + rtc_init.protocol = init.protocol.c_str(); + + if (init.has_max_retransmit_time) + rtc_init.maxRetransmitTime = init.max_retransmit_time; + + if (init.has_max_retransmits) + rtc_init.maxRetransmits = init.max_retransmits; + + if (init.has_priority) + rtc_init.priority = webrtc::PriorityValue(static_cast(init.priority)); + + return rtc_init; +} + +DataChannel::DataChannel( + std::shared_ptr rtc_runtime, + webrtc::scoped_refptr data_channel) + : rtc_runtime_(rtc_runtime), data_channel_(std::move(data_channel)) { + RTC_LOG(LS_VERBOSE) << "DataChannel::DataChannel()"; +} + +DataChannel::~DataChannel() { + RTC_LOG(LS_VERBOSE) << "DataChannel::~DataChannel()"; + unregister_observer(); +} + +void DataChannel::register_observer( + rust::Box observer) const { + webrtc::MutexLock lock(&mutex_); + + data_channel_->UnregisterObserver(); + + observer_ = + std::make_unique(std::move(observer), this); + data_channel_->RegisterObserver(observer_.get()); +} + +void DataChannel::unregister_observer() const { + webrtc::MutexLock lock(&mutex_); + data_channel_->UnregisterObserver(); + observer_ = nullptr; +} + +bool DataChannel::send(const DataBuffer& buffer) const { + return data_channel_->Send(webrtc::DataBuffer{ + webrtc::CopyOnWriteBuffer(buffer.ptr, buffer.len), buffer.binary}); +} + +int DataChannel::id() const { + return data_channel_->id(); +} + +rust::String DataChannel::label() const { + return data_channel_->label(); +} + +DataState DataChannel::state() const { + return static_cast(data_channel_->state()); +} + +void DataChannel::close() const { + return data_channel_->Close(); +} + +uint64_t DataChannel::buffered_amount() const { + return data_channel_->buffered_amount(); +} + +NativeDataChannelObserver::NativeDataChannelObserver( + rust::Box observer, + const DataChannel* dc) + : observer_(std::move(observer)), dc_(dc) {} + +void NativeDataChannelObserver::OnStateChange() { + observer_->on_state_change(dc_->state()); +} + +void NativeDataChannelObserver::OnMessage(const webrtc::DataBuffer& buffer) { + DataBuffer data{}; + data.ptr = buffer.data.data(); + data.len = buffer.data.size(); + data.binary = buffer.binary; + observer_->on_message(data); +} + +void NativeDataChannelObserver::OnBufferedAmountChange( + uint64_t sent_data_size) { + observer_->on_buffered_amount_change(sent_data_size); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/data_channel.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/data_channel.rs new file mode 100644 index 0000000..c2e426c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/data_channel.rs @@ -0,0 +1,118 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::sync::Arc; + +use crate::impl_thread_safety; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + #[derive(Debug)] + #[repr(i32)] + pub enum Priority { + VeryLow, + Low, + Medium, + High, + } + + #[derive(Debug)] + pub struct DataChannelInit { + pub ordered: bool, + pub has_max_retransmit_time: bool, + pub max_retransmit_time: i32, + pub has_max_retransmits: bool, + pub max_retransmits: i32, + pub protocol: String, + pub negotiated: bool, + pub id: i32, + pub has_priority: bool, + pub priority: Priority, + } + + #[derive(Debug)] + pub struct DataBuffer { + pub ptr: *const u8, + pub len: usize, + pub binary: bool, + } + + #[derive(Debug)] + #[repr(i32)] + pub enum DataState { + Connecting, + Open, + Closing, + Closed, + } + + unsafe extern "C++" { + include!("livekit/data_channel.h"); + + type DataChannel; + + fn register_observer(self: &DataChannel, observer: Box); + fn unregister_observer(self: &DataChannel); + + fn send(self: &DataChannel, data: &DataBuffer) -> bool; + fn id(self: &DataChannel) -> i32; + fn label(self: &DataChannel) -> String; + fn state(self: &DataChannel) -> DataState; + fn close(self: &DataChannel); + fn buffered_amount(self: &DataChannel) -> u64; + + fn _shared_data_channel() -> SharedPtr; // Ignore + } + + extern "Rust" { + type DataChannelObserverWrapper; + + fn on_state_change(self: &DataChannelObserverWrapper, state: DataState); + fn on_message(self: &DataChannelObserverWrapper, buffer: DataBuffer); + fn on_buffered_amount_change(self: &DataChannelObserverWrapper, sent_data_size: u64); + } +} + +impl_thread_safety!(ffi::DataChannel, Send + Sync); + +pub trait DataChannelObserver: Send + Sync { + fn on_state_change(&self, state: ffi::DataState); + fn on_message(&self, data: &[u8], is_binary: bool); + fn on_buffered_amount_change(&self, sent_data_size: u64); +} + +pub struct DataChannelObserverWrapper { + observer: Arc, +} + +impl DataChannelObserverWrapper { + pub fn new(observer: Arc) -> Self { + Self { observer } + } + + fn on_state_change(&self, state: ffi::DataState) { + self.observer.on_state_change(state); + } + + fn on_message(&self, buffer: ffi::DataBuffer) { + unsafe { + let data = std::slice::from_raw_parts(buffer.ptr, buffer.len); + self.observer.on_message(data, buffer.binary); + } + } + + fn on_buffered_amount_change(&self, sent_data_size: u64) { + self.observer.on_buffered_amount_change(sent_data_size); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/desktop_capturer.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/desktop_capturer.cpp new file mode 100644 index 0000000..8b5af3c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/desktop_capturer.cpp @@ -0,0 +1,118 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/desktop_capturer.h" + +#include "modules/desktop_capture/desktop_capture_options.h" + +using SourceList = webrtc::DesktopCapturer::SourceList; + +namespace livekit_ffi { + +std::unique_ptr new_desktop_capturer( + DesktopCapturerOptions options) { + webrtc::DesktopCaptureOptions webrtc_options = + webrtc::DesktopCaptureOptions::CreateDefault(); +#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) + webrtc_options.set_allow_sck_capturer(true); + webrtc_options.set_allow_sck_system_picker(options.allow_sck_system_picker); +#endif /* defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) */ +#ifdef _WIN64 + switch (options.source_type) { + case SourceType::Screen: + webrtc_options.set_allow_wgc_screen_capturer(true); + break; + case SourceType::Window: + webrtc_options.set_allow_wgc_window_capturer(true); + // https://github.com/webrtc-sdk/webrtc/blob/m137_release/modules/desktop_capture/desktop_capture_options.h#L133-L142 + webrtc_options.set_enumerate_current_process_windows(false); + break; + default: + break; + } + webrtc_options.set_allow_directx_capturer(true); +#endif /* _WIN64 */ +#ifdef WEBRTC_USE_PIPEWIRE + webrtc_options.set_allow_pipewire(true); +#endif /* WEBRTC_USE_PIPEWIRE */ + + // prefer_cursor_embedded indicate that the capturer should try to include the + // cursor in the frame + webrtc_options.set_prefer_cursor_embedded(options.include_cursor); + + std::unique_ptr capturer = nullptr; + switch (options.source_type) { + case SourceType::Window: + capturer = webrtc::DesktopCapturer::CreateWindowCapturer(webrtc_options); + break; + case SourceType::Screen: + capturer = webrtc::DesktopCapturer::CreateScreenCapturer(webrtc_options); + break; + case SourceType::Generic: + capturer = webrtc::DesktopCapturer::CreateGenericCapturer(webrtc_options); + break; + default: + return nullptr; + } + + if (!capturer) { + return nullptr; + } + return std::make_unique(std::move(capturer)); +} + +void DesktopCapturer::start( + rust::Box callback) { + this->callback = std::move(callback); + capturer->Start(this); +} + +void DesktopCapturer::OnCaptureResult( + webrtc::DesktopCapturer::Result result, + std::unique_ptr frame) { + CaptureResult ret_result = CaptureResult::ErrorPermanent; + switch (result) { + case webrtc::DesktopCapturer::Result::SUCCESS: + ret_result = CaptureResult::Success; + break; + case webrtc::DesktopCapturer::Result::ERROR_PERMANENT: + ret_result = CaptureResult::ErrorPermanent; + break; + case webrtc::DesktopCapturer::Result::ERROR_TEMPORARY: + ret_result = CaptureResult::ErrorTemporary; + break; + default: + break; + } + if (callback) { + (*callback)->on_capture_result( + ret_result, std::make_unique(std::move(frame))); + } +} + +rust::Vec DesktopCapturer::get_source_list() const { + SourceList list{}; + bool res = capturer->GetSourceList(&list); + rust::Vec source_list{}; + if (res) { + for (auto& source : list) { + source_list.push_back(Source{static_cast(source.id), + source.title, source.display_id}); + } + } + return source_list; +} +} // namespace livekit_ffi \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/desktop_capturer.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/desktop_capturer.rs new file mode 100644 index 0000000..65ddecc --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/desktop_capturer.rs @@ -0,0 +1,113 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use cxx::UniquePtr; +use ffi::CaptureResult; + +use crate::{desktop_capturer::ffi::DesktopFrame, impl_thread_safety}; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + #[derive(Clone)] + struct Source { + id: u64, + title: String, + display_id: i64, + } + + #[derive(Debug, PartialEq)] + enum SourceType { + Screen, + Window, + Generic, + } + + #[derive(Clone, Debug)] + struct DesktopCapturerOptions { + source_type: SourceType, + include_cursor: bool, + allow_sck_system_picker: bool, + } + + enum CaptureResult { + Success, + ErrorTemporary, + ErrorPermanent, + } + + unsafe extern "C++" { + include!("livekit/desktop_capturer.h"); + + type DesktopCapturer; + type DesktopFrame; + + fn new_desktop_capturer(options: DesktopCapturerOptions) -> UniquePtr; + fn capture_frame(self: &DesktopCapturer); + fn get_source_list(self: &DesktopCapturer) -> Vec; + fn select_source(self: &DesktopCapturer, id: u64) -> bool; + fn start(self: Pin<&mut DesktopCapturer>, callback: Box); + + fn width(self: &DesktopFrame) -> i32; + fn height(self: &DesktopFrame) -> i32; + fn stride(self: &DesktopFrame) -> i32; + fn left(self: &DesktopFrame) -> i32; + fn top(self: &DesktopFrame) -> i32; + fn data(self: &DesktopFrame) -> *const u8; + } + + extern "Rust" { + type DesktopCapturerCallbackWrapper; + + fn on_capture_result( + self: &mut DesktopCapturerCallbackWrapper, + result: CaptureResult, + frame: UniquePtr, + ); + } +} + +impl_thread_safety!(ffi::DesktopCapturer, Send + Sync); + +#[derive(Debug, PartialEq)] +pub enum CaptureError { + Temporary, + Permanent, +} + +pub trait DesktopCapturerCallback: Send { + fn on_capture_result(&mut self, result: Result, CaptureError>); +} + +pub struct DesktopCapturerCallbackWrapper { + callback: Box, +} + +impl DesktopCapturerCallbackWrapper { + pub fn new(callback: Box) -> Self { + Self { callback } + } + + fn on_capture_result(&mut self, result: CaptureResult, frame: UniquePtr) { + match result { + CaptureResult::Success => self.callback.on_capture_result(Ok(frame)), + CaptureResult::ErrorTemporary => { + self.callback.on_capture_result(Err(CaptureError::Temporary)) + } + CaptureResult::ErrorPermanent => { + self.callback.on_capture_result(Err(CaptureError::Permanent)) + } + _ => self.callback.on_capture_result(Err(CaptureError::Permanent)), + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/frame_cryptor.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/frame_cryptor.cpp new file mode 100644 index 0000000..1ff1b0b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/frame_cryptor.cpp @@ -0,0 +1,364 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/frame_cryptor.h" + +#include + +#include "absl/types/optional.h" +#include "api/make_ref_counted.h" +#include "livekit/peer_connection.h" +#include "livekit/peer_connection_factory.h" +#include "livekit/packet_trailer.h" +#include "livekit/webrtc.h" +#include "rtc_base/thread.h" +#include "webrtc-sys/src/frame_cryptor.rs.h" + +namespace livekit_ffi { + +class ChainedFrameTransformer : public webrtc::FrameTransformerInterface, + public webrtc::TransformedFrameCallback { + public: + ChainedFrameTransformer( + webrtc::scoped_refptr first, + webrtc::scoped_refptr second) + : first_(std::move(first)), second_(std::move(second)) {} + + void Transform( + std::unique_ptr frame) override { + first_->Transform(std::move(frame)); + } + + void RegisterTransformedFrameCallback( + webrtc::scoped_refptr callback) override { + second_->RegisterTransformedFrameCallback(callback); + first_->RegisterTransformedFrameCallback( + webrtc::scoped_refptr(this)); + } + + void RegisterTransformedFrameSinkCallback( + webrtc::scoped_refptr callback, + uint32_t ssrc) override { + second_->RegisterTransformedFrameSinkCallback(callback, ssrc); + first_->RegisterTransformedFrameSinkCallback( + webrtc::scoped_refptr(this), ssrc); + } + + void UnregisterTransformedFrameCallback() override { + first_->UnregisterTransformedFrameCallback(); + second_->UnregisterTransformedFrameCallback(); + } + + void UnregisterTransformedFrameSinkCallback(uint32_t ssrc) override { + first_->UnregisterTransformedFrameSinkCallback(ssrc); + second_->UnregisterTransformedFrameSinkCallback(ssrc); + } + + void OnTransformedFrame( + std::unique_ptr frame) override { + second_->Transform(std::move(frame)); + } + + private: + webrtc::scoped_refptr first_; + webrtc::scoped_refptr second_; +}; + +webrtc::FrameCryptorTransformer::Algorithm AlgorithmToFrameCryptorAlgorithm( + Algorithm algorithm) { + switch (algorithm) { + case Algorithm::AesGcm: + return webrtc::FrameCryptorTransformer::Algorithm::kAesGcm; + case Algorithm::AesCbc: + return webrtc::FrameCryptorTransformer::Algorithm::kAesCbc; + default: + return webrtc::FrameCryptorTransformer::Algorithm::kAesGcm; + } +} + +webrtc::KeyDerivationAlgorithm +KeyDerivationAlgorithmToFrameCryptorKeyDerivationAlgorithm( + KeyDerivationAlgorithm algorithm) { + switch (algorithm) { + case KeyDerivationAlgorithm::PBKDF2: + return webrtc::KeyDerivationAlgorithm::kPBKDF2; + case KeyDerivationAlgorithm::HKDF: + return webrtc::KeyDerivationAlgorithm::kHKDF; + default: + return webrtc::KeyDerivationAlgorithm::kPBKDF2; + } +} + +KeyProvider::KeyProvider(KeyProviderOptions options) { + webrtc::KeyProviderOptions rtc_options; + rtc_options.shared_key = options.shared_key; + + std::vector ratchet_salt; + std::copy(options.ratchet_salt.begin(), options.ratchet_salt.end(), + std::back_inserter(ratchet_salt)); + + rtc_options.ratchet_salt = ratchet_salt; + rtc_options.ratchet_window_size = options.ratchet_window_size; + rtc_options.failure_tolerance = options.failure_tolerance; + rtc_options.key_ring_size = options.key_ring_size; + rtc_options.key_derivation_algorithm = + KeyDerivationAlgorithmToFrameCryptorKeyDerivationAlgorithm( + options.key_derivation_algorithm); + impl_ = + new webrtc::RefCountedObject(rtc_options); +} + +FrameCryptor::FrameCryptor( + std::shared_ptr rtc_runtime, + const std::string participant_id, + webrtc::FrameCryptorTransformer::Algorithm algorithm, + webrtc::scoped_refptr key_provider, + webrtc::scoped_refptr sender) + : rtc_runtime_(rtc_runtime), + participant_id_(participant_id), + key_provider_(key_provider), + sender_(sender) { + auto mediaType = + sender->track()->kind() == "audio" + ? webrtc::FrameCryptorTransformer::MediaType::kAudioFrame + : webrtc::FrameCryptorTransformer::MediaType::kVideoFrame; + e2ee_transformer_ = webrtc::scoped_refptr( + new webrtc::FrameCryptorTransformer(rtc_runtime->signaling_thread(), + participant_id, mediaType, algorithm, + key_provider_)); + sender->SetEncoderToPacketizerFrameTransformer(e2ee_transformer_); + e2ee_transformer_->SetEnabled(false); +} + +FrameCryptor::FrameCryptor( + std::shared_ptr rtc_runtime, + const std::string participant_id, + webrtc::FrameCryptorTransformer::Algorithm algorithm, + webrtc::scoped_refptr key_provider, + webrtc::scoped_refptr receiver) + : rtc_runtime_(rtc_runtime), + participant_id_(participant_id), + key_provider_(key_provider), + receiver_(receiver) { + auto mediaType = + receiver->track()->kind() == "audio" + ? webrtc::FrameCryptorTransformer::MediaType::kAudioFrame + : webrtc::FrameCryptorTransformer::MediaType::kVideoFrame; + e2ee_transformer_ = webrtc::scoped_refptr( + new webrtc::FrameCryptorTransformer(rtc_runtime->signaling_thread(), + participant_id, mediaType, algorithm, + key_provider_)); + receiver->SetDepacketizerToDecoderFrameTransformer(e2ee_transformer_); + e2ee_transformer_->SetEnabled(false); +} + +FrameCryptor::~FrameCryptor() { + if (observer_) { + unregister_observer(); + } +} + +void FrameCryptor::register_observer( + rust::Box observer) const { + webrtc::MutexLock lock(&mutex_); + observer_ = webrtc::make_ref_counted( + std::move(observer), this); + e2ee_transformer_->RegisterFrameCryptorTransformerObserver(observer_); +} + +void FrameCryptor::unregister_observer() const { + webrtc::MutexLock lock(&mutex_); + observer_ = nullptr; + e2ee_transformer_->UnRegisterFrameCryptorTransformerObserver(); +} + +void FrameCryptor::set_packet_trailer_handler( + std::shared_ptr handler) const { + if (!handler) { + return; + } + + auto timestamp_transformer = handler->transformer(); + if (!timestamp_transformer) { + return; + } + + webrtc::scoped_refptr first; + webrtc::scoped_refptr second; + if (sender_) { + first = e2ee_transformer_; + second = timestamp_transformer; + } else if (receiver_) { + first = timestamp_transformer; + second = e2ee_transformer_; + } else { + return; + } + + chained_transformer_ = + webrtc::make_ref_counted(first, second); + + if (sender_) { + sender_->SetEncoderToPacketizerFrameTransformer(chained_transformer_); + } + if (receiver_) { + receiver_->SetDepacketizerToDecoderFrameTransformer(chained_transformer_); + } +} + +NativeFrameCryptorObserver::NativeFrameCryptorObserver( + rust::Box observer, + const FrameCryptor* fc) + : observer_(std::move(observer)), fc_(fc) {} + +NativeFrameCryptorObserver::~NativeFrameCryptorObserver() {} + +void NativeFrameCryptorObserver::OnFrameCryptionStateChanged( + const std::string participant_id, + webrtc::FrameCryptionState state) { + observer_->on_frame_cryption_state_change( + participant_id, static_cast(state)); +} + +void FrameCryptor::set_enabled(bool enabled) const { + webrtc::MutexLock lock(&mutex_); + e2ee_transformer_->SetEnabled(enabled); +} + +bool FrameCryptor::enabled() const { + webrtc::MutexLock lock(&mutex_); + return e2ee_transformer_->enabled(); +} + +void FrameCryptor::set_key_index(int32_t index) const { + webrtc::MutexLock lock(&mutex_); + e2ee_transformer_->SetKeyIndex(index); +} + +int32_t FrameCryptor::key_index() const { + webrtc::MutexLock lock(&mutex_); + return e2ee_transformer_->key_index(); +} + +DataPacketCryptor::DataPacketCryptor( + webrtc::FrameCryptorTransformer::Algorithm algorithm, + webrtc::scoped_refptr key_provider) + : data_packet_cryptor_( + webrtc::make_ref_counted(algorithm, + key_provider)) {} + +EncryptedPacket DataPacketCryptor::encrypt_data_packet( + const ::rust::String participant_id, + uint32_t key_index, + rust::Vec<::std::uint8_t> data) const { + std::vector data_vec; + std::copy(data.begin(), data.end(), std::back_inserter(data_vec)); + + auto result = data_packet_cryptor_->Encrypt( + std::string(participant_id.data(), participant_id.size()), key_index, + data_vec); + + if (!result.ok()) { + throw std::runtime_error(std::string("Failed to encrypt data packet: ") + + result.error().message()); + } + + auto& packet = result.value(); + + EncryptedPacket encrypted_packet; + encrypted_packet.data = rust::Vec(); + std::copy(packet->data.begin(), packet->data.end(), + std::back_inserter(encrypted_packet.data)); + + encrypted_packet.iv = rust::Vec(); + std::copy(packet->iv.begin(), packet->iv.end(), + std::back_inserter(encrypted_packet.iv)); + + encrypted_packet.key_index = packet->key_index; + + return encrypted_packet; +} + +rust::Vec<::std::uint8_t> DataPacketCryptor::decrypt_data_packet( + const ::rust::String participant_id, + const EncryptedPacket& encrypted_packet) const { + std::vector data_vec; + std::copy(encrypted_packet.data.begin(), encrypted_packet.data.end(), + std::back_inserter(data_vec)); + + std::vector iv_vec; + std::copy(encrypted_packet.iv.begin(), encrypted_packet.iv.end(), + std::back_inserter(iv_vec)); + + auto native_encrypted_packet = + webrtc::make_ref_counted( + std::move(data_vec), std::move(iv_vec), encrypted_packet.key_index); + + auto result = data_packet_cryptor_->Decrypt( + std::string(participant_id.data(), participant_id.size()), + native_encrypted_packet); + + if (!result.ok()) { + throw std::runtime_error(std::string("Failed to decrypt data packet: ") + + result.error().message()); + } + + rust::Vec decrypted_data; + auto& decrypted = result.value(); + std::copy(decrypted.begin(), decrypted.end(), + std::back_inserter(decrypted_data)); + return decrypted_data; +} + +std::shared_ptr new_key_provider(KeyProviderOptions options) { + return std::make_shared(options); +} + +std::shared_ptr new_frame_cryptor_for_rtp_sender( + std::shared_ptr peer_factory, + const ::rust::String participant_id, + Algorithm algorithm, + std::shared_ptr key_provider, + std::shared_ptr sender) { + return std::make_shared( + peer_factory->rtc_runtime(), + std::string(participant_id.data(), participant_id.size()), + AlgorithmToFrameCryptorAlgorithm(algorithm), + key_provider->rtc_key_provider(), sender->rtc_sender()); +} + +std::shared_ptr new_frame_cryptor_for_rtp_receiver( + std::shared_ptr peer_factory, + const ::rust::String participant_id, + Algorithm algorithm, + std::shared_ptr key_provider, + std::shared_ptr receiver) { + return std::make_shared( + peer_factory->rtc_runtime(), + std::string(participant_id.data(), participant_id.size()), + AlgorithmToFrameCryptorAlgorithm(algorithm), + key_provider->rtc_key_provider(), receiver->rtc_receiver()); +} + +std::shared_ptr new_data_packet_cryptor( + Algorithm algorithm, + std::shared_ptr key_provider) { + return std::make_shared( + AlgorithmToFrameCryptorAlgorithm(algorithm), + key_provider->rtc_key_provider()); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/frame_cryptor.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/frame_cryptor.rs new file mode 100644 index 0000000..c402ce0 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/frame_cryptor.rs @@ -0,0 +1,275 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::sync::Arc; + +use crate::impl_thread_safety; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + + #[derive(Debug)] + pub struct KeyProviderOptions { + pub shared_key: bool, + pub ratchet_window_size: i32, + pub ratchet_salt: Vec, + pub failure_tolerance: i32, + pub key_ring_size: i32, + pub key_derivation_algorithm: KeyDerivationAlgorithm, + } + + #[derive(Debug)] + #[repr(i32)] + pub enum KeyDerivationAlgorithm { + PBKDF2 = 0, + HKDF, + } + + #[derive(Debug)] + #[repr(i32)] + pub enum Algorithm { + AesGcm = 0, + AesCbc, + } + + #[derive(Debug)] + #[repr(i32)] + pub enum FrameCryptionState { + New = 0, + Ok, + EncryptionFailed, + DecryptionFailed, + MissingKey, + KeyRatcheted, + InternalError, + } + + #[derive(Debug)] + pub struct EncryptedPacket { + pub data: Vec, + pub iv: Vec, + pub key_index: u32, + } + + unsafe extern "C++" { + include!("livekit/frame_cryptor.h"); + + pub type KeyProvider; + + pub fn new_key_provider(options: KeyProviderOptions) -> SharedPtr; + + pub fn set_shared_key(self: &KeyProvider, key_index: i32, key: Vec) -> bool; + + pub fn ratchet_shared_key(self: &KeyProvider, key_index: i32) -> Result>; + + pub fn get_shared_key(self: &KeyProvider, key_index: i32) -> Result>; + + pub fn set_sif_trailer(&self, trailer: Vec); + + pub fn set_key( + self: &KeyProvider, + participant_id: String, + key_index: i32, + key: Vec, + ) -> bool; + + pub fn ratchet_key( + self: &KeyProvider, + participant_id: String, + key_index: i32, + ) -> Result>; + + pub fn get_key( + self: &KeyProvider, + participant_id: String, + key_index: i32, + ) -> Result>; + } + + unsafe extern "C++" { + include!("livekit/frame_cryptor.h"); + include!("livekit/rtp_sender.h"); + include!("livekit/rtp_receiver.h"); + include!("livekit/peer_connection_factory.h"); + include!("livekit/packet_trailer.h"); + + type RtpSender = crate::rtp_sender::ffi::RtpSender; + type RtpReceiver = crate::rtp_receiver::ffi::RtpReceiver; + type PeerConnectionFactory = crate::peer_connection_factory::ffi::PeerConnectionFactory; + type PacketTrailerHandler = crate::packet_trailer::ffi::PacketTrailerHandler; + + pub type FrameCryptor; + + pub fn new_frame_cryptor_for_rtp_sender( + peer_factory: SharedPtr, + participant_id: String, + algorithm: Algorithm, + key_provider: SharedPtr, + sender: SharedPtr, + ) -> SharedPtr; + + pub fn new_frame_cryptor_for_rtp_receiver( + peer_factory: SharedPtr, + participant_id: String, + algorithm: Algorithm, + key_provider: SharedPtr, + receiver: SharedPtr, + ) -> SharedPtr; + + pub fn set_enabled(self: &FrameCryptor, enabled: bool); + + pub fn enabled(self: &FrameCryptor) -> bool; + + pub fn set_key_index(self: &FrameCryptor, index: i32); + + pub fn key_index(self: &FrameCryptor) -> i32; + + pub fn participant_id(self: &FrameCryptor) -> String; + + pub fn register_observer( + self: &FrameCryptor, + observer: Box, + ); + + pub fn unregister_observer(self: &FrameCryptor); + + pub fn set_packet_trailer_handler( + self: &FrameCryptor, + handler: SharedPtr, + ); + } + + unsafe extern "C++" { + include!("livekit/frame_cryptor.h"); + + pub type DataPacketCryptor; + + pub fn new_data_packet_cryptor( + algorithm: Algorithm, + key_provider: SharedPtr, + ) -> SharedPtr; + + pub fn encrypt_data_packet( + self: &DataPacketCryptor, + participant_id: String, + key_index: u32, + data: Vec, + ) -> Result; + + pub fn decrypt_data_packet( + self: &DataPacketCryptor, + participant_id: String, + encrypted_packet: &EncryptedPacket, + ) -> Result>; + } + + extern "Rust" { + type RtcFrameCryptorObserverWrapper; + + fn on_frame_cryption_state_change( + self: &RtcFrameCryptorObserverWrapper, + participant_id: String, + state: FrameCryptionState, + ); + } +} // namespace livekit_ffi + +impl_thread_safety!(ffi::FrameCryptor, Send + Sync); +impl_thread_safety!(ffi::KeyProvider, Send + Sync); +impl_thread_safety!(ffi::DataPacketCryptor, Send + Sync); + +use ffi::FrameCryptionState; + +// Re-export the EncryptedPacket for convenience +pub use ffi::EncryptedPacket; + +pub trait RtcFrameCryptorObserver: Send + Sync { + fn on_frame_cryption_state_change(&self, participant_id: String, state: FrameCryptionState); +} + +pub struct RtcFrameCryptorObserverWrapper { + observer: Arc, +} + +impl RtcFrameCryptorObserverWrapper { + pub fn new(observer: Arc) -> Self { + Self { observer } + } + + fn on_frame_cryption_state_change( + self: &RtcFrameCryptorObserverWrapper, + participant_id: String, + state: FrameCryptionState, + ) { + self.observer.on_frame_cryption_state_change(participant_id, state); + } +} + +/// High-level Rust wrapper for data packet cryptor functionality +pub struct DataPacketCryptor { + inner: cxx::SharedPtr, +} + +impl DataPacketCryptor { + /// Create a new data packet cryptor with the specified algorithm and key provider + pub fn new(algorithm: ffi::Algorithm, key_provider: cxx::SharedPtr) -> Self { + Self { inner: ffi::new_data_packet_cryptor(algorithm, key_provider) } + } + + /// Encrypt data for a specific participant + pub fn encrypt( + &self, + participant_id: &str, + key_index: u32, + data: &[u8], + ) -> Result> { + let data_vec: Vec = data.to_vec(); + match self.inner.encrypt_data_packet(participant_id.to_string(), key_index, data_vec) { + Ok(packet) => Ok(packet), + Err(e) => Err(format!("Encryption failed: {}", e).into()), + } + } + + /// Decrypt an encrypted packet for a specific participant + pub fn decrypt( + &self, + participant_id: &str, + encrypted_packet: &ffi::EncryptedPacket, + ) -> Result, Box> { + match self.inner.decrypt_data_packet(participant_id.to_string(), encrypted_packet) { + Ok(data) => Ok(data.into_iter().collect()), + Err(e) => Err(format!("Decryption failed: {}", e).into()), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_data_packet_cryptor_creation() { + let options = ffi::KeyProviderOptions { + shared_key: true, + ratchet_window_size: 16, + ratchet_salt: vec![], + failure_tolerance: -1, + key_ring_size: 16, + key_derivation_algorithm: ffi::KeyDerivationAlgorithm::HKDF, + }; + + let key_provider = ffi::new_key_provider(options); + let _cryptor = DataPacketCryptor::new(ffi::Algorithm::AesGcm, key_provider); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/global_task_queue.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/global_task_queue.cpp new file mode 100644 index 0000000..d9a06eb --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/global_task_queue.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/global_task_queue.h" + +#include "api/task_queue/default_task_queue_factory.h" +#include "api/task_queue/task_queue_factory.h" + +namespace livekit_ffi { + +webrtc::TaskQueueFactory* GetGlobalTaskQueueFactory() { + static std::unique_ptr global_task_queue_factory = + webrtc::CreateDefaultTaskQueueFactory(); + return global_task_queue_factory.get(); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/helper.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/helper.rs new file mode 100644 index 0000000..8320731 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/helper.rs @@ -0,0 +1,67 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + // Wrapper to opaque C++ objects + // https://github.com/dtolnay/cxx/issues/741 + // Used to allow SharedPtr/UniquePtr type inside a rust::Vec + pub struct MediaStreamPtr { + pub ptr: SharedPtr, + } + + pub struct CandidatePtr { + pub ptr: SharedPtr, + } + + pub struct AudioTrackPtr { + pub ptr: SharedPtr, + } + + pub struct VideoTrackPtr { + pub ptr: SharedPtr, + } + + pub struct RtpSenderPtr { + pub ptr: SharedPtr, + } + + pub struct RtpReceiverPtr { + pub ptr: SharedPtr, + } + + pub struct RtpTransceiverPtr { + pub ptr: SharedPtr, + } + + unsafe extern "C++" { + include!("livekit/helper.h"); + + type MediaStream = crate::media_stream::ffi::MediaStream; + type AudioTrack = crate::media_stream::ffi::AudioTrack; + type VideoTrack = crate::media_stream::ffi::VideoTrack; + type Candidate = crate::candidate::ffi::Candidate; + type RtpSender = crate::rtp_sender::ffi::RtpSender; + type RtpReceiver = crate::rtp_receiver::ffi::RtpReceiver; + type RtpTransceiver = crate::rtp_transceiver::ffi::RtpTransceiver; + + fn _vec_media_stream_ptr() -> Vec; + fn _vec_candidate_ptr() -> Vec; + fn _vec_audio_track_ptr() -> Vec; + fn _vec_video_track_ptr() -> Vec; + fn _vec_rtp_sender_ptr() -> Vec; + fn _vec_rtp_receiver_ptr() -> Vec; + fn _vec_rtp_transceiver_ptr() -> Vec; + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/jsep.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/jsep.cpp new file mode 100644 index 0000000..b01229f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/jsep.cpp @@ -0,0 +1,183 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/jsep.h" + +#include +#include + +#include "livekit/rtc_error.h" +#include "rtc_base/ref_counted_object.h" +#include "rust/cxx.h" + +namespace livekit_ffi { + +std::string serialize_sdp_error(webrtc::SdpParseError error) { + std::stringstream ss; + ss << std::hex << std::setfill('0'); + ss << std::setw(8) << (uint32_t)error.line.length(); + ss << std::dec << std::setw(1) << error.line; + ss << std::dec << std::setw(1) << error.description; + return ss.str(); +} + +IceCandidate::IceCandidate( + std::unique_ptr ice_candidate) + : ice_candidate_(std::move(ice_candidate)) {} + +rust::String IceCandidate::sdp_mid() const { + return ice_candidate_->sdp_mid(); +} + +int IceCandidate::sdp_mline_index() const { + return ice_candidate_->sdp_mline_index(); +} + +rust::String IceCandidate::candidate() const { + return stringify(); +} + +rust::String IceCandidate::stringify() const { + std::string str; + ice_candidate_->ToString(&str); + return rust::String::lossy(str); +} + +std::unique_ptr IceCandidate::release() { + return std::move(ice_candidate_); +} + +std::shared_ptr create_ice_candidate(rust::String sdp_mid, + int sdp_mline_index, + rust::String sdp) { + webrtc::SdpParseError error; + auto ice_rtc = webrtc::CreateIceCandidate(sdp_mid.c_str(), sdp_mline_index, + sdp.c_str(), &error); + if (!ice_rtc) { + throw std::runtime_error(serialize_sdp_error(error)); + } + + return std::make_shared( + std::unique_ptr(ice_rtc)); +} + +SessionDescription::SessionDescription( + std::unique_ptr session_description) + : session_description_(std::move(session_description)) {} + +SdpType SessionDescription::sdp_type() const { + return static_cast(session_description_->GetType()); +} + +rust::String SessionDescription::stringify() const { + std::string str; + session_description_->ToString(&str); + return rust::String::lossy(str); +} + +std::unique_ptr SessionDescription::clone() const { + return std::make_unique(session_description_->Clone()); +} + +std::unique_ptr +SessionDescription::release() { + return std::move(session_description_); +} + +std::unique_ptr create_session_description( + SdpType type, + rust::String sdp) { + webrtc::SdpParseError error; + auto rtc_sdp = webrtc::CreateSessionDescription( + static_cast(type), sdp.c_str(), &error); + if (!rtc_sdp) { + throw std::runtime_error(serialize_sdp_error(error)); + } + + return std::make_unique(std::move(rtc_sdp)); +} + +#ifdef LIVEKIT_TEST +rust::String serialize_sdp_parse_error_for_test() { + webrtc::SdpParseError error; + auto rtc_sdp = webrtc::CreateSessionDescription( + webrtc::SdpType::kOffer, + "v=0\n" + "o=- 6549709950142776241 2 IN IP4 127.0.0.1\n" + "s=-\n" + "t=0 0\n" + "======================== ERROR HERE\n" + "a=group:BUNDLE 0\n" + "a=extmap-allow-mixed\n" + "a=msid-semantic: WMS\n" + "m=application 9 UDP/DTLS/SCTP webrtc-datachannel\n" + "c=IN IP4 0.0.0.0\n" + "a=ice-ufrag:Tw7h\n" + "a=ice-pwd:6XOVUD6HpcB4c1M8EB8jXJE9\n" + "a=ice-options:trickle\n" + "a=fingerprint:sha-256 " + "4F:EC:23:59:5D:A5:E6:3E:3E:5D:8A:09:B6:FA:04:AA:19:99:49:67:BD:65:93:06:BB:EE:AC:D5:21:0F:57:D6\n" + "a=setup:actpass\n" + "a=mid:0\n" + "a=sctp-port:5000\n" + "a=max-message-size:262144\n", + &error); + if (rtc_sdp) { + return rust::String(""); + } + return rust::String::lossy(serialize_sdp_error(error)); +} +#endif + +NativeCreateSdpObserver::NativeCreateSdpObserver( + rust::Box ctx, + rust::Fn, std::unique_ptr)> + on_success, + rust::Fn, RtcError)> on_error) + : ctx_(std::move(ctx)), on_success_(on_success), on_error_(on_error) {} + +void NativeCreateSdpObserver::OnSuccess( + webrtc::SessionDescriptionInterface* desc) { + // We have ownership of desc + on_success_(std::move(ctx_), + std::make_unique( + std::unique_ptr(desc))); +} + +void NativeCreateSdpObserver::OnFailure(webrtc::RTCError error) { + on_error_(std::move(ctx_), to_error(error)); +} + +NativeSetLocalSdpObserver::NativeSetLocalSdpObserver( + rust::Box ctx, + rust::Fn, RtcError)> on_complete) + : ctx_(std::move(ctx)), on_complete_(on_complete) {} + +void NativeSetLocalSdpObserver::OnSetLocalDescriptionComplete( + webrtc::RTCError error) { + on_complete_(std::move(ctx_), to_error(error)); +} + +NativeSetRemoteSdpObserver::NativeSetRemoteSdpObserver( + rust::Box ctx, + rust::Fn, RtcError)> on_complete) + : ctx_(std::move(ctx)), on_complete_(on_complete) {} + +void NativeSetRemoteSdpObserver::OnSetRemoteDescriptionComplete( + webrtc::RTCError error) { + on_complete_(std::move(ctx_), to_error(error)); +} +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/jsep.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/jsep.rs new file mode 100644 index 0000000..de82253 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/jsep.rs @@ -0,0 +1,123 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{ + error::Error, + fmt::{Display, Formatter}, +}; + +use crate::impl_thread_safety; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + #[derive(Debug)] + #[repr(i32)] + pub enum SdpType { + Offer, + PrAnswer, + Answer, + Rollback, + } + + #[derive(Debug)] + pub struct SdpParseError { + pub line: String, + pub description: String, + } + + extern "C++" { + include!("livekit/rtc_error.h"); + + type RtcError = crate::rtc_error::ffi::RtcError; + } + + unsafe extern "C++" { + include!("livekit/jsep.h"); + + type IceCandidate; + type SessionDescription; + + fn sdp_mid(self: &IceCandidate) -> String; + fn sdp_mline_index(self: &IceCandidate) -> i32; + fn candidate(self: &IceCandidate) -> String; + fn stringify(self: &IceCandidate) -> String; + + fn sdp_type(self: &SessionDescription) -> SdpType; + fn stringify(self: &SessionDescription) -> String; + fn clone(self: &SessionDescription) -> UniquePtr; + + fn create_ice_candidate( + sdp_mid: String, + sdp_mline_index: i32, + sdp: String, + ) -> Result>; + + fn create_session_description( + sdp_type: SdpType, + sdp: String, + ) -> Result>; + + fn _shared_ice_candidate() -> SharedPtr; // Ignore + fn _unique_session_description() -> UniquePtr; // Ignore + } +} + +impl Error for ffi::SdpParseError {} + +impl Display for ffi::SdpParseError { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "SdpParseError occurred {}: {}", self.line, self.description) + } +} + +impl_thread_safety!(ffi::SessionDescription, Send + Sync); +impl_thread_safety!(ffi::IceCandidate, Send + Sync); + +impl ffi::SdpParseError { + /// # Safety + /// The value must be correctly encoded + pub unsafe fn from(value: &str) -> Self { + // Parse the hex encoded error from c++ + let line_length = u32::from_str_radix(&value[0..8], 16).unwrap() as usize + 8; + let line = String::from(&value[8..line_length]); + let description = String::from(&value[line_length..]); + + Self { line, description } + } +} + +#[cfg(test)] +mod tests { + #[cxx::bridge(namespace = "livekit_ffi")] + pub mod ffi_tests { + unsafe extern "C++" { + include!("livekit/jsep.h"); + + fn serialize_sdp_parse_error_for_test() -> String; + } + } + + use crate::jsep::ffi; + + /// Tests that SdpParseError can correctly deserialize the hex-encoded + /// error format produced by C++ when SDP parsing fails. + #[test] + fn sdp_parse_error_deserialization() { + let serialized = ffi_tests::serialize_sdp_parse_error_for_test(); + let err = unsafe { ffi::SdpParseError::from(&serialized) }; + + assert!(!err.line.is_empty(), "error line should not be empty"); + assert!(!err.description.is_empty(), "error description should not be empty"); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libX11.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libX11.so.init.c new file mode 100644 index 0000000..3fb945f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libX11.so.init.c @@ -0,0 +1,1426 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libX11.so.6: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libX11.so.6"); + CHECK(lib_handle, "failed to load library 'libX11.so.6' via callback ''"); +#else + lib_handle = dlopen("libX11.so.6", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libX11.so.6' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "KeySymToUcs4", + "XActivateScreenSaver", + "XAddConnectionWatch", + "XAddExtension", + "XAddHost", + "XAddHosts", + "XAddPixel", + "XAddToExtensionList", + "XAddToSaveSet", + "XAllPlanes", + "XAllocClassHint", + "XAllocColor", + "XAllocColorCells", + "XAllocColorPlanes", + "XAllocIconSize", + "XAllocNamedColor", + "XAllocSizeHints", + "XAllocStandardColormap", + "XAllocWMHints", + "XAllowEvents", + "XAutoRepeatOff", + "XAutoRepeatOn", + "XBaseFontNameListOfFontSet", + "XBell", + "XBitmapBitOrder", + "XBitmapPad", + "XBitmapUnit", + "XBlackPixel", + "XBlackPixelOfScreen", + "XCellsOfScreen", + "XChangeActivePointerGrab", + "XChangeGC", + "XChangeKeyboardControl", + "XChangeKeyboardMapping", + "XChangePointerControl", + "XChangeProperty", + "XChangeSaveSet", + "XChangeWindowAttributes", + "XCheckIfEvent", + "XCheckMaskEvent", + "XCheckTypedEvent", + "XCheckTypedWindowEvent", + "XCheckWindowEvent", + "XCirculateSubwindows", + "XCirculateSubwindowsDown", + "XCirculateSubwindowsUp", + "XClearArea", + "XClearWindow", + "XClipBox", + "XCloseDisplay", + "XCloseIM", + "XCloseOM", + "XConfigureWindow", + "XConnectionNumber", + "XContextDependentDrawing", + "XContextualDrawing", + "XConvertCase", + "XConvertSelection", + "XCopyArea", + "XCopyColormapAndFree", + "XCopyGC", + "XCopyPlane", + "XCreateBitmapFromData", + "XCreateColormap", + "XCreateFontCursor", + "XCreateFontSet", + "XCreateGC", + "XCreateGlyphCursor", + "XCreateIC", + "XCreateImage", + "XCreateOC", + "XCreatePixmap", + "XCreatePixmapCursor", + "XCreatePixmapFromBitmapData", + "XCreateRegion", + "XCreateSimpleWindow", + "XCreateWindow", + "XDefaultColormap", + "XDefaultColormapOfScreen", + "XDefaultDepth", + "XDefaultDepthOfScreen", + "XDefaultGC", + "XDefaultGCOfScreen", + "XDefaultRootWindow", + "XDefaultScreen", + "XDefaultScreenOfDisplay", + "XDefaultString", + "XDefaultVisual", + "XDefaultVisualOfScreen", + "XDefineCursor", + "XDeleteContext", + "XDeleteModifiermapEntry", + "XDeleteProperty", + "XDestroyIC", + "XDestroyImage", + "XDestroyOC", + "XDestroyRegion", + "XDestroySubwindows", + "XDestroyWindow", + "XDirectionalDependentDrawing", + "XDisableAccessControl", + "XDisplayCells", + "XDisplayHeight", + "XDisplayHeightMM", + "XDisplayKeycodes", + "XDisplayMotionBufferSize", + "XDisplayName", + "XDisplayOfIM", + "XDisplayOfOM", + "XDisplayOfScreen", + "XDisplayPlanes", + "XDisplayString", + "XDisplayWidth", + "XDisplayWidthMM", + "XDoesBackingStore", + "XDoesSaveUnders", + "XDrawArc", + "XDrawArcs", + "XDrawImageString", + "XDrawImageString16", + "XDrawLine", + "XDrawLines", + "XDrawPoint", + "XDrawPoints", + "XDrawRectangle", + "XDrawRectangles", + "XDrawSegments", + "XDrawString", + "XDrawString16", + "XDrawText", + "XDrawText16", + "XEHeadOfExtensionList", + "XESetBeforeFlush", + "XESetCloseDisplay", + "XESetCopyEventCookie", + "XESetCopyGC", + "XESetCreateFont", + "XESetCreateGC", + "XESetError", + "XESetErrorString", + "XESetEventToWire", + "XESetFlushGC", + "XESetFreeFont", + "XESetFreeGC", + "XESetPrintErrorValues", + "XESetWireToError", + "XESetWireToEvent", + "XESetWireToEventCookie", + "XEmptyRegion", + "XEnableAccessControl", + "XEqualRegion", + "XEventMaskOfScreen", + "XEventsQueued", + "XExtendedMaxRequestSize", + "XExtentsOfFontSet", + "XFetchBuffer", + "XFetchBytes", + "XFetchName", + "XFillArc", + "XFillArcs", + "XFillPolygon", + "XFillRectangle", + "XFillRectangles", + "XFilterEvent", + "XFindContext", + "XFindOnExtensionList", + "XFlush", + "XFlushGC", + "XFontsOfFontSet", + "XForceScreenSaver", + "XFree", + "XFreeColormap", + "XFreeColors", + "XFreeCursor", + "XFreeEventData", + "XFreeExtensionList", + "XFreeFont", + "XFreeFontInfo", + "XFreeFontNames", + "XFreeFontPath", + "XFreeFontSet", + "XFreeGC", + "XFreeModifiermap", + "XFreePixmap", + "XFreeStringList", + "XFreeThreads", + "XGContextFromGC", + "XGeometry", + "XGetAtomName", + "XGetAtomNames", + "XGetClassHint", + "XGetCommand", + "XGetDefault", + "XGetErrorDatabaseText", + "XGetErrorText", + "XGetEventData", + "XGetFontPath", + "XGetFontProperty", + "XGetGCValues", + "XGetGeometry", + "XGetICValues", + "XGetIMValues", + "XGetIconName", + "XGetIconSizes", + "XGetImage", + "XGetInputFocus", + "XGetKeyboardControl", + "XGetKeyboardMapping", + "XGetModifierMapping", + "XGetMotionEvents", + "XGetNormalHints", + "XGetOCValues", + "XGetOMValues", + "XGetPixel", + "XGetPointerControl", + "XGetPointerMapping", + "XGetRGBColormaps", + "XGetScreenSaver", + "XGetSelectionOwner", + "XGetSizeHints", + "XGetStandardColormap", + "XGetSubImage", + "XGetTextProperty", + "XGetTransientForHint", + "XGetVisualInfo", + "XGetWMClientMachine", + "XGetWMColormapWindows", + "XGetWMHints", + "XGetWMIconName", + "XGetWMName", + "XGetWMNormalHints", + "XGetWMProtocols", + "XGetWMSizeHints", + "XGetWindowAttributes", + "XGetWindowProperty", + "XGetZoomHints", + "XGrabButton", + "XGrabKey", + "XGrabKeyboard", + "XGrabPointer", + "XGrabServer", + "XHeightMMOfScreen", + "XHeightOfScreen", + "XIMOfIC", + "XIconifyWindow", + "XIfEvent", + "XImageByteOrder", + "XInitExtension", + "XInitImage", + "XInitThreads", + "XInsertModifiermapEntry", + "XInstallColormap", + "XInternAtom", + "XInternAtoms", + "XInternalConnectionNumbers", + "XIntersectRegion", + "XKeycodeToKeysym", + "XKeysymToKeycode", + "XKeysymToString", + "XKillClient", + "XLastKnownRequestProcessed", + "XListDepths", + "XListExtensions", + "XListFonts", + "XListFontsWithInfo", + "XListHosts", + "XListInstalledColormaps", + "XListPixmapFormats", + "XListProperties", + "XLoadFont", + "XLoadQueryFont", + "XLocaleOfFontSet", + "XLocaleOfIM", + "XLocaleOfOM", + "XLockDisplay", + "XLookupColor", + "XLookupKeysym", + "XLookupString", + "XLowerWindow", + "XMapRaised", + "XMapSubwindows", + "XMapWindow", + "XMaskEvent", + "XMatchVisualInfo", + "XMaxCmapsOfScreen", + "XMaxRequestSize", + "XMinCmapsOfScreen", + "XMoveResizeWindow", + "XMoveWindow", + "XNewModifiermap", + "XNextEvent", + "XNextRequest", + "XNoOp", + "XOMOfOC", + "XOffsetRegion", + "XOpenDisplay", + "XOpenIM", + "XOpenOM", + "XParseColor", + "XParseGeometry", + "XPeekEvent", + "XPeekIfEvent", + "XPending", + "XPlanesOfScreen", + "XPointInRegion", + "XPolygonRegion", + "XProcessInternalConnection", + "XProtocolRevision", + "XProtocolVersion", + "XPutBackEvent", + "XPutImage", + "XPutPixel", + "XQLength", + "XQueryBestCursor", + "XQueryBestSize", + "XQueryBestStipple", + "XQueryBestTile", + "XQueryColor", + "XQueryColors", + "XQueryExtension", + "XQueryFont", + "XQueryKeymap", + "XQueryPointer", + "XQueryTextExtents", + "XQueryTextExtents16", + "XQueryTree", + "XRaiseWindow", + "XReadBitmapFile", + "XReadBitmapFileData", + "XRebindKeysym", + "XRecolorCursor", + "XReconfigureWMWindow", + "XRectInRegion", + "XRefreshKeyboardMapping", + "XRegisterIMInstantiateCallback", + "XRemoveConnectionWatch", + "XRemoveFromSaveSet", + "XRemoveHost", + "XRemoveHosts", + "XReparentWindow", + "XResetScreenSaver", + "XResizeWindow", + "XResourceManagerString", + "XRestackWindows", + "XRootWindow", + "XRootWindowOfScreen", + "XRotateBuffers", + "XRotateWindowProperties", + "XSaveContext", + "XScreenCount", + "XScreenNumberOfScreen", + "XScreenOfDisplay", + "XScreenResourceString", + "XSelectInput", + "XSendEvent", + "XServerVendor", + "XSetAccessControl", + "XSetAfterFunction", + "XSetArcMode", + "XSetAuthorization", + "XSetBackground", + "XSetClassHint", + "XSetClipMask", + "XSetClipOrigin", + "XSetClipRectangles", + "XSetCloseDownMode", + "XSetCommand", + "XSetDashes", + "XSetErrorHandler", + "XSetFillRule", + "XSetFillStyle", + "XSetFont", + "XSetFontPath", + "XSetForeground", + "XSetFunction", + "XSetGraphicsExposures", + "XSetICFocus", + "XSetICValues", + "XSetIMValues", + "XSetIOErrorExitHandler", + "XSetIOErrorHandler", + "XSetIconName", + "XSetIconSizes", + "XSetInputFocus", + "XSetLineAttributes", + "XSetLocaleModifiers", + "XSetModifierMapping", + "XSetNormalHints", + "XSetOCValues", + "XSetOMValues", + "XSetPlaneMask", + "XSetPointerMapping", + "XSetRGBColormaps", + "XSetRegion", + "XSetScreenSaver", + "XSetSelectionOwner", + "XSetSizeHints", + "XSetStandardColormap", + "XSetStandardProperties", + "XSetState", + "XSetStipple", + "XSetSubwindowMode", + "XSetTSOrigin", + "XSetTextProperty", + "XSetTile", + "XSetTransientForHint", + "XSetWMClientMachine", + "XSetWMColormapWindows", + "XSetWMHints", + "XSetWMIconName", + "XSetWMName", + "XSetWMNormalHints", + "XSetWMProperties", + "XSetWMProtocols", + "XSetWMSizeHints", + "XSetWindowBackground", + "XSetWindowBackgroundPixmap", + "XSetWindowBorder", + "XSetWindowBorderPixmap", + "XSetWindowBorderWidth", + "XSetWindowColormap", + "XSetZoomHints", + "XShrinkRegion", + "XStoreBuffer", + "XStoreBytes", + "XStoreColor", + "XStoreColors", + "XStoreName", + "XStoreNamedColor", + "XStringListToTextProperty", + "XStringToKeysym", + "XSubImage", + "XSubtractRegion", + "XSupportsLocale", + "XSync", + "XSynchronize", + "XTextExtents", + "XTextExtents16", + "XTextPropertyToStringList", + "XTextWidth", + "XTextWidth16", + "XTranslateCoordinates", + "XUndefineCursor", + "XUngrabButton", + "XUngrabKey", + "XUngrabKeyboard", + "XUngrabPointer", + "XUngrabServer", + "XUninstallColormap", + "XUnionRectWithRegion", + "XUnionRegion", + "XUnloadFont", + "XUnlockDisplay", + "XUnmapSubwindows", + "XUnmapWindow", + "XUnregisterIMInstantiateCallback", + "XUnsetICFocus", + "XVaCreateNestedList", + "XVendorRelease", + "XVisualIDFromVisual", + "XWMGeometry", + "XWarpPointer", + "XWhitePixel", + "XWhitePixelOfScreen", + "XWidthMMOfScreen", + "XWidthOfScreen", + "XWindowEvent", + "XWithdrawWindow", + "XWriteBitmapFile", + "XXorRegion", + "XcmsAddColorSpace", + "XcmsAddFunctionSet", + "XcmsAllocColor", + "XcmsAllocNamedColor", + "XcmsCCCOfColormap", + "XcmsCIELabClipL", + "XcmsCIELabClipLab", + "XcmsCIELabClipab", + "XcmsCIELabQueryMaxC", + "XcmsCIELabQueryMaxL", + "XcmsCIELabQueryMaxLC", + "XcmsCIELabQueryMinL", + "XcmsCIELabToCIEXYZ", + "XcmsCIELabWhiteShiftColors", + "XcmsCIELuvClipL", + "XcmsCIELuvClipLuv", + "XcmsCIELuvClipuv", + "XcmsCIELuvQueryMaxC", + "XcmsCIELuvQueryMaxL", + "XcmsCIELuvQueryMaxLC", + "XcmsCIELuvQueryMinL", + "XcmsCIELuvToCIEuvY", + "XcmsCIELuvWhiteShiftColors", + "XcmsCIEXYZToCIELab", + "XcmsCIEXYZToCIEuvY", + "XcmsCIEXYZToCIExyY", + "XcmsCIEXYZToRGBi", + "XcmsCIEuvYToCIELuv", + "XcmsCIEuvYToCIEXYZ", + "XcmsCIEuvYToTekHVC", + "XcmsCIExyYToCIEXYZ", + "XcmsClientWhitePointOfCCC", + "XcmsConvertColors", + "XcmsCreateCCC", + "XcmsDefaultCCC", + "XcmsDisplayOfCCC", + "XcmsFormatOfPrefix", + "XcmsFreeCCC", + "XcmsLookupColor", + "XcmsPrefixOfFormat", + "XcmsQueryBlack", + "XcmsQueryBlue", + "XcmsQueryColor", + "XcmsQueryColors", + "XcmsQueryGreen", + "XcmsQueryRed", + "XcmsQueryWhite", + "XcmsRGBToRGBi", + "XcmsRGBiToCIEXYZ", + "XcmsRGBiToRGB", + "XcmsScreenNumberOfCCC", + "XcmsScreenWhitePointOfCCC", + "XcmsSetCCCOfColormap", + "XcmsSetCompressionProc", + "XcmsSetWhiteAdjustProc", + "XcmsSetWhitePoint", + "XcmsStoreColor", + "XcmsStoreColors", + "XcmsTekHVCClipC", + "XcmsTekHVCClipV", + "XcmsTekHVCClipVC", + "XcmsTekHVCQueryMaxC", + "XcmsTekHVCQueryMaxV", + "XcmsTekHVCQueryMaxVC", + "XcmsTekHVCQueryMaxVSamples", + "XcmsTekHVCQueryMinV", + "XcmsTekHVCToCIEuvY", + "XcmsTekHVCWhiteShiftColors", + "XcmsVisualOfCCC", + "XkbAddDeviceLedInfo", + "XkbAddGeomColor", + "XkbAddGeomDoodad", + "XkbAddGeomKey", + "XkbAddGeomKeyAlias", + "XkbAddGeomOutline", + "XkbAddGeomOverlay", + "XkbAddGeomOverlayKey", + "XkbAddGeomOverlayRow", + "XkbAddGeomProperty", + "XkbAddGeomRow", + "XkbAddGeomSection", + "XkbAddGeomShape", + "XkbAddKeyType", + "XkbAllocClientMap", + "XkbAllocCompatMap", + "XkbAllocControls", + "XkbAllocDeviceInfo", + "XkbAllocGeomColors", + "XkbAllocGeomDoodads", + "XkbAllocGeomKeyAliases", + "XkbAllocGeomKeys", + "XkbAllocGeomOutlines", + "XkbAllocGeomOverlayKeys", + "XkbAllocGeomOverlayRows", + "XkbAllocGeomOverlays", + "XkbAllocGeomPoints", + "XkbAllocGeomProps", + "XkbAllocGeomRows", + "XkbAllocGeomSectionDoodads", + "XkbAllocGeomSections", + "XkbAllocGeomShapes", + "XkbAllocGeometry", + "XkbAllocIndicatorMaps", + "XkbAllocKeyboard", + "XkbAllocNames", + "XkbAllocServerMap", + "XkbApplyCompatMapToKey", + "XkbApplyVirtualModChanges", + "XkbBell", + "XkbBellEvent", + "XkbChangeDeviceInfo", + "XkbChangeEnabledControls", + "XkbChangeKeycodeRange", + "XkbChangeMap", + "XkbChangeNames", + "XkbChangeTypesOfKey", + "XkbComputeEffectiveMap", + "XkbComputeRowBounds", + "XkbComputeSectionBounds", + "XkbComputeShapeBounds", + "XkbComputeShapeTop", + "XkbCopyKeyType", + "XkbCopyKeyTypes", + "XkbDeviceBell", + "XkbDeviceBellEvent", + "XkbFindOverlayForKey", + "XkbForceBell", + "XkbForceDeviceBell", + "XkbFreeClientMap", + "XkbFreeCompatMap", + "XkbFreeComponentList", + "XkbFreeControls", + "XkbFreeDeviceInfo", + "XkbFreeGeomColors", + "XkbFreeGeomDoodads", + "XkbFreeGeomKeyAliases", + "XkbFreeGeomKeys", + "XkbFreeGeomOutlines", + "XkbFreeGeomOverlayKeys", + "XkbFreeGeomOverlayRows", + "XkbFreeGeomOverlays", + "XkbFreeGeomPoints", + "XkbFreeGeomProperties", + "XkbFreeGeomRows", + "XkbFreeGeomSections", + "XkbFreeGeomShapes", + "XkbFreeGeometry", + "XkbFreeIndicatorMaps", + "XkbFreeKeyboard", + "XkbFreeNames", + "XkbFreeServerMap", + "XkbGetAutoRepeatRate", + "XkbGetAutoResetControls", + "XkbGetCompatMap", + "XkbGetControls", + "XkbGetDetectableAutoRepeat", + "XkbGetDeviceButtonActions", + "XkbGetDeviceInfo", + "XkbGetDeviceInfoChanges", + "XkbGetDeviceLedInfo", + "XkbGetGeometry", + "XkbGetIndicatorMap", + "XkbGetIndicatorState", + "XkbGetKeyActions", + "XkbGetKeyBehaviors", + "XkbGetKeyExplicitComponents", + "XkbGetKeyModifierMap", + "XkbGetKeySyms", + "XkbGetKeyTypes", + "XkbGetKeyVirtualModMap", + "XkbGetKeyboard", + "XkbGetKeyboardByName", + "XkbGetMap", + "XkbGetMapChanges", + "XkbGetNamedDeviceIndicator", + "XkbGetNamedGeometry", + "XkbGetNamedIndicator", + "XkbGetNames", + "XkbGetPerClientControls", + "XkbGetState", + "XkbGetUpdatedMap", + "XkbGetVirtualMods", + "XkbGetXlibControls", + "XkbIgnoreExtension", + "XkbInitCanonicalKeyTypes", + "XkbKeyTypesForCoreSymbols", + "XkbKeycodeToKeysym", + "XkbKeysymToModifiers", + "XkbLatchGroup", + "XkbLatchModifiers", + "XkbLibraryVersion", + "XkbListComponents", + "XkbLockGroup", + "XkbLockModifiers", + "XkbLookupKeyBinding", + "XkbLookupKeySym", + "XkbNoteControlsChanges", + "XkbNoteDeviceChanges", + "XkbNoteMapChanges", + "XkbNoteNameChanges", + "XkbOpenDisplay", + "XkbQueryExtension", + "XkbRefreshKeyboardMapping", + "XkbResizeDeviceButtonActions", + "XkbResizeKeyActions", + "XkbResizeKeySyms", + "XkbResizeKeyType", + "XkbSelectEventDetails", + "XkbSelectEvents", + "XkbSetAtomFuncs", + "XkbSetAutoRepeatRate", + "XkbSetAutoResetControls", + "XkbSetCompatMap", + "XkbSetControls", + "XkbSetDebuggingFlags", + "XkbSetDetectableAutoRepeat", + "XkbSetDeviceButtonActions", + "XkbSetDeviceInfo", + "XkbSetDeviceLedInfo", + "XkbSetGeometry", + "XkbSetIgnoreLockMods", + "XkbSetIndicatorMap", + "XkbSetMap", + "XkbSetNamedDeviceIndicator", + "XkbSetNamedIndicator", + "XkbSetNames", + "XkbSetPerClientControls", + "XkbSetServerInternalMods", + "XkbSetXlibControls", + "XkbToControl", + "XkbTranslateKey", + "XkbTranslateKeyCode", + "XkbTranslateKeySym", + "XkbUpdateActionVirtualMods", + "XkbUpdateKeyTypeVirtualMods", + "XkbUpdateMapFromCore", + "XkbUseExtension", + "XkbVirtualModsToReal", + "XkbXlibControlsImplemented", + "XmbDrawImageString", + "XmbDrawString", + "XmbDrawText", + "XmbLookupString", + "XmbResetIC", + "XmbSetWMProperties", + "XmbTextEscapement", + "XmbTextExtents", + "XmbTextListToTextProperty", + "XmbTextPerCharExtents", + "XmbTextPropertyToTextList", + "Xpermalloc", + "XrmCombineDatabase", + "XrmCombineFileDatabase", + "XrmDestroyDatabase", + "XrmEnumerateDatabase", + "XrmGetDatabase", + "XrmGetFileDatabase", + "XrmGetResource", + "XrmGetStringDatabase", + "XrmInitialize", + "XrmLocaleOfDatabase", + "XrmMergeDatabases", + "XrmParseCommand", + "XrmPermStringToQuark", + "XrmPutFileDatabase", + "XrmPutLineResource", + "XrmPutResource", + "XrmPutStringResource", + "XrmQGetResource", + "XrmQGetSearchList", + "XrmQGetSearchResource", + "XrmQPutResource", + "XrmQPutStringResource", + "XrmQuarkToString", + "XrmSetDatabase", + "XrmStringToBindingQuarkList", + "XrmStringToQuark", + "XrmStringToQuarkList", + "XrmUniqueQuark", + "Xutf8DrawImageString", + "Xutf8DrawString", + "Xutf8DrawText", + "Xutf8LookupString", + "Xutf8ResetIC", + "Xutf8SetWMProperties", + "Xutf8TextEscapement", + "Xutf8TextExtents", + "Xutf8TextListToTextProperty", + "Xutf8TextPerCharExtents", + "Xutf8TextPropertyToTextList", + "XwcDrawImageString", + "XwcDrawString", + "XwcDrawText", + "XwcFreeStringList", + "XwcLookupString", + "XwcResetIC", + "XwcTextEscapement", + "XwcTextExtents", + "XwcTextListToTextProperty", + "XwcTextPerCharExtents", + "XwcTextPropertyToTextList", + "_Utf8GetConvByName", + "_XAllocID", + "_XAllocIDs", + "_XAllocScratch", + "_XAllocTemp", + "_XAsyncErrorHandler", + "_XCloseLC", + "_XColor_to_XcmsRGB", + "_XConnectXCB", + "_XCopyEventCookie", + "_XCopyToArg", + "_XData32", + "_XDefaultError", + "_XDefaultIOError", + "_XDefaultIOErrorExit", + "_XDefaultOpenIM", + "_XDefaultOpenOM", + "_XDefaultWireError", + "_XDeq", + "_XDeqAsyncHandler", + "_XEatData", + "_XEatDataWords", + "_XEnq", + "_XError", + "_XEventToWire", + "_XEventsQueued", + "_XF86BigfontFreeFontMetrics", + "_XF86LoadQueryLocaleFont", + "_XFetchEventCookie", + "_XFlush", + "_XFlushGCCache", + "_XFreeAtomTable", + "_XFreeDisplayStructure", + "_XFreeEventCookies", + "_XFreeExtData", + "_XFreeTemp", + "_XFreeX11XCBStructure", + "_XGetAsyncData", + "_XGetAsyncReply", + "_XGetBitsPerPixel", + "_XGetHostname", + "_XGetLCValues", + "_XGetRequest", + "_XGetScanlinePad", + "_XGetWindowAttributes", + "_XIMCompileResourceList", + "_XIOError", + "_XInitIM", + "_XInitImageFuncPtrs", + "_XInitKeysymDB", + "_XInitOM", + "_XIsEventCookie", + "_XKeyInitialize", + "_XKeycodeToKeysym", + "_XKeysymToKeycode", + "_XKeysymToModifiers", + "_XLookupKeysym", + "_XLookupString", + "_XNoticeCreateBitmap", + "_XNoticePutBitmap", + "_XOpenLC", + "_XParseBaseFontNameList", + "_XPollfdCacheAdd", + "_XPollfdCacheDel", + "_XPollfdCacheInit", + "_XProcessInternalConnection", + "_XProcessWindowAttributes", + "_XPutBackEvent", + "_XRead", + "_XRead32", + "_XReadEvents", + "_XReadPad", + "_XRefreshKeyboardMapping", + "_XRegisterFilterByMask", + "_XRegisterFilterByType", + "_XRegisterInternalConnection", + "_XReply", + "_XReverse_Bytes", + "_XScreenOfWindow", + "_XSend", + "_XSetClipRectangles", + "_XSetImage", + "_XSetLastRequestRead", + "_XStoreEventCookie", + "_XTextHeight", + "_XTextHeight16", + "_XTranslateKey", + "_XTranslateKeySym", + "_XTryShapeBitmapCursor", + "_XUnknownCopyEventCookie", + "_XUnknownNativeEvent", + "_XUnknownWireEvent", + "_XUnknownWireEventCookie", + "_XUnregisterFilter", + "_XUnregisterInternalConnection", + "_XUnresolveColor", + "_XUpdateAtomCache", + "_XUpdateGCCache", + "_XVIDtoVisual", + "_XWireToEvent", + "_XcmsAddCmapRec", + "_XcmsArcTangent", + "_XcmsCIELabQueryMaxLCRGB", + "_XcmsCIELuvQueryMaxLCRGB", + "_XcmsCIEXYZ_ValidSpec", + "_XcmsCIEuvY_ValidSpec", + "_XcmsConvertColorsWithWhitePt", + "_XcmsCopyCmapRecAndFree", + "_XcmsCopyISOLatin1Lowered", + "_XcmsCopyPointerArray", + "_XcmsCosine", + "_XcmsCubeRoot", + "_XcmsDDConvertColors", + "_XcmsDIConvertColors", + "_XcmsDeleteCmapRec", + "_XcmsEqualWhitePts", + "_XcmsFreeIntensityMaps", + "_XcmsFreePointerArray", + "_XcmsGetElement", + "_XcmsGetIntensityMap", + "_XcmsGetProperty", + "_XcmsInitDefaultCCCs", + "_XcmsInitScrnInfo", + "_XcmsLRGB_InitScrnDefault", + "_XcmsPushPointerArray", + "_XcmsRGB_to_XColor", + "_XcmsRegFormatOfPrefix", + "_XcmsResolveColor", + "_XcmsResolveColorString", + "_XcmsSetGetColor", + "_XcmsSetGetColors", + "_XcmsSine", + "_XcmsSquareRoot", + "_XcmsTekHVCQueryMaxVCRGB", + "_XcmsTekHVC_CheckModify", + "_XcmsUnresolveColor", + "_XimCbDispatch", + "_XimCheckCreateICValues", + "_XimCheckDataSize", + "_XimCheckICMode", + "_XimCheckIMMode", + "_XimCheckIfLocalProcessing", + "_XimCheckIfThaiProcessing", + "_XimCheckLocalInputStyle", + "_XimCommitCallback", + "_XimConnect", + "_XimDecodeICATTRIBUTE", + "_XimDecodeIMATTRIBUTE", + "_XimDecodeLocalICAttr", + "_XimDecodeLocalIMAttr", + "_XimDestroyIMStructureList", + "_XimDispatchInit", + "_XimEncodeICATTRIBUTE", + "_XimEncodeIMATTRIBUTE", + "_XimEncodeLocalICAttr", + "_XimEncodeLocalIMAttr", + "_XimError", + "_XimErrorCallback", + "_XimExtension", + "_XimFilterWaitEvent", + "_XimFlush", + "_XimForwardEvent", + "_XimForwardEventCallback", + "_XimFreeCommitInfo", + "_XimFreeProtoIntrCallback", + "_XimFreeTransIntrCallback", + "_XimGetAttributeID", + "_XimGetCharCode", + "_XimGetCurrentICValues", + "_XimGetCurrentIMValues", + "_XimGetICValueData", + "_XimGetIMValueData", + "_XimGetLocaleCode", + "_XimGetMyEndian", + "_XimGetResourceListRec", + "_XimGetResourceListRecByQuark", + "_XimGetWindowEventmask", + "_XimICOfXICID", + "_XimInitialResourceInfo", + "_XimLcctstombs", + "_XimLcctstoutf8", + "_XimLcctstowcs", + "_XimLocalCreateIC", + "_XimLocalFilter", + "_XimLocalGetICValues", + "_XimLocalGetIMValues", + "_XimLocalIMFree", + "_XimLocalMbLookupString", + "_XimLocalOpenIM", + "_XimLocalSetICValues", + "_XimLocalSetIMValues", + "_XimLocalUtf8LookupString", + "_XimLocalWcLookupString", + "_XimLookupMBText", + "_XimLookupUTF8Text", + "_XimLookupWCText", + "_XimMakeICAttrIDList", + "_XimMakeIMAttrIDList", + "_XimOpenIM", + "_XimParseStringFile", + "_XimProcError", + "_XimProcSyncReply", + "_XimProtoCreateIC", + "_XimProtoEventToWire", + "_XimProtoIMFree", + "_XimProtoMbLookupString", + "_XimProtoOpenIM", + "_XimProtoUtf8LookupString", + "_XimProtoWcLookupString", + "_XimProtoWireToEvent", + "_XimRead", + "_XimRegProtoIntrCallback", + "_XimRegisterDispatcher", + "_XimRegisterFilter", + "_XimRegisterIMInstantiateCallback", + "_XimRegisterServerFilter", + "_XimRegisterTriggerKeysCallback", + "_XimReregisterFilter", + "_XimResetIMInstantiateCallback", + "_XimRespSyncReply", + "_XimServerDestroy", + "_XimSetCurrentICValues", + "_XimSetCurrentIMValues", + "_XimSetEventMaskCallback", + "_XimSetHeader", + "_XimSetICDefaults", + "_XimSetICMode", + "_XimSetICResourceList", + "_XimSetICValueData", + "_XimSetIMMode", + "_XimSetIMResourceList", + "_XimSetIMValueData", + "_XimSetInnerICResourceList", + "_XimSetInnerIMResourceList", + "_XimSetLocalIMDefaults", + "_XimShutdown", + "_XimSync", + "_XimSyncCallback", + "_XimThaiCloseIM", + "_XimThaiCreateIC", + "_XimThaiFilter", + "_XimThaiIMFree", + "_XimThaiOpenIM", + "_XimTransCallDispatcher", + "_XimTransConf", + "_XimTransFilterWaitEvent", + "_XimTransFlush", + "_XimTransInternalConnection", + "_XimTransRead", + "_XimTransRegisterDispatcher", + "_XimTransWrite", + "_XimTriggerNotify", + "_XimUnRegisterIMInstantiateCallback", + "_XimUnregisterFilter", + "_XimUnregisterServerFilter", + "_XimWrite", + "_XimXConf", + "_XimXTransBytesReadable", + "_XimXTransClose", + "_XimXTransCloseForCloning", + "_XimXTransConnect", + "_XimXTransDisconnect", + "_XimXTransFreeConnInfo", + "_XimXTransGetConnectionNumber", + "_XimXTransGetHostname", + "_XimXTransGetPeerAddr", + "_XimXTransIsLocal", + "_XimXTransOpenCOTSClient", + "_XimXTransRead", + "_XimXTransReadv", + "_XimXTransSetOption", + "_XimXTransWrite", + "_XimXTransWritev", + "_Ximctstombs", + "_Ximctstoutf8", + "_Ximctstowcs", + "_XkbCopyFromReadBuffer", + "_XkbFreeReadBuffer", + "_XkbGetCharset", + "_XkbGetConverters", + "_XkbGetReadBufferCountedString", + "_XkbGetReadBufferPtr", + "_XkbInitReadBuffer", + "_XkbNoteCoreMapChanges", + "_XkbPeekAtReadBuffer", + "_XkbReadBufferCopy32", + "_XkbReadBufferCopyKeySyms", + "_XkbReadCopyData32", + "_XkbReadCopyKeySyms", + "_XkbReadGetCompatMapReply", + "_XkbReadGetGeometryReply", + "_XkbReadGetIndicatorMapReply", + "_XkbReadGetMapReply", + "_XkbReadGetNamesReply", + "_XkbReloadDpy", + "_XkbSkipReadBufferData", + "_XkbWriteCopyData32", + "_XkbWriteCopyKeySyms", + "_XlcAddCT", + "_XlcAddCharSet", + "_XlcAddGB18030LocaleConverters", + "_XlcAddLoader", + "_XlcAddUtf8Converters", + "_XlcAddUtf8LocaleConverters", + "_XlcCloseConverter", + "_XlcCompareISOLatin1", + "_XlcCompileResourceList", + "_XlcConvert", + "_XlcCopyFromArg", + "_XlcCopyToArg", + "_XlcCountVaList", + "_XlcCreateDefaultCharSet", + "_XlcCreateLC", + "_XlcCreateLocaleDataBase", + "_XlcCurrentLC", + "_XlcDbg_printValue", + "_XlcDeInitLoader", + "_XlcDefaultLoader", + "_XlcDefaultMapModifiers", + "_XlcDestroyLC", + "_XlcDestroyLocaleDataBase", + "_XlcFileName", + "_XlcGenericLoader", + "_XlcGetCSValues", + "_XlcGetCharSet", + "_XlcGetCharSetWithSide", + "_XlcGetLocaleDataBase", + "_XlcGetResource", + "_XlcGetValues", + "_XlcInitCTInfo", + "_XlcInitLoader", + "_XlcLocaleDirName", + "_XlcLocaleLibDirName", + "_XlcMapOSLocaleName", + "_XlcNCompareISOLatin1", + "_XlcOpenConverter", + "_XlcParseCharSet", + "_XlcParse_scopemaps", + "_XlcRemoveLoader", + "_XlcResetConverter", + "_XlcResolveI18NPath", + "_XlcResolveLocaleName", + "_XlcSetConverter", + "_XlcSetValues", + "_XlcUtf8Loader", + "_XlcVaToArgList", + "_XlcValidModSyntax", + "_Xlcmbstoutf8", + "_Xlcmbstowcs", + "_Xlcmbtowc", + "_Xlcwcstombs", + "_Xlcwctomb", + "_XmbDefaultDrawImageString", + "_XmbDefaultDrawString", + "_XmbDefaultTextEscapement", + "_XmbDefaultTextExtents", + "_XmbDefaultTextPerCharExtents", + "_XmbGenericDrawImageString", + "_XmbGenericDrawString", + "_XmbGenericTextEscapement", + "_XmbGenericTextExtents", + "_XmbGenericTextPerCharExtents", + "_XmbTextListToTextProperty", + "_XmbTextPropertyToTextList", + "_Xmblen", + "_Xmbstoutf8", + "_Xmbstowcs", + "_Xmbtowc", + "_XomConvert", + "_XomGenericDrawString", + "_XomGenericOpenOM", + "_XomGenericTextExtents", + "_XomGetFontDataFromFontSet", + "_XomInitConverter", + "_XrmDefaultInitParseInfo", + "_XrmInitParseInfo", + "_XrmInternalStringToQuark", + "_Xutf8DefaultDrawImageString", + "_Xutf8DefaultDrawString", + "_Xutf8DefaultTextEscapement", + "_Xutf8DefaultTextExtents", + "_Xutf8DefaultTextPerCharExtents", + "_Xutf8GenericDrawImageString", + "_Xutf8GenericDrawString", + "_Xutf8GenericTextEscapement", + "_Xutf8GenericTextExtents", + "_Xutf8GenericTextPerCharExtents", + "_Xutf8TextListToTextProperty", + "_Xutf8TextPropertyToTextList", + "_XwcDefaultDrawImageString", + "_XwcDefaultDrawString", + "_XwcDefaultTextEscapement", + "_XwcDefaultTextExtents", + "_XwcDefaultTextPerCharExtents", + "_XwcFreeStringList", + "_XwcGenericDrawImageString", + "_XwcGenericDrawString", + "_XwcGenericTextEscapement", + "_XwcGenericTextExtents", + "_XwcGenericTextPerCharExtents", + "_XwcTextListToTextProperty", + "_XwcTextPropertyToTextList", + "_Xwcscmp", + "_Xwcscpy", + "_Xwcslen", + "_Xwcsncmp", + "_Xwcsncpy", + "_Xwcstombs", + "_Xwctomb", + "read_EncodingInfo", + "xlocaledir", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libX11_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libX11_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libX11_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libX11_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libX11_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libX11_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libX11_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libX11_so_tramp_table, 0, SYM_COUNT * sizeof(_libX11_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libX11.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libX11.so.tramp.S new file mode 100644 index 0000000..06d61f8 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libX11.so.tramp.S @@ -0,0 +1,48543 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#define lr x30 +#define ip0 x16 + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libX11_so_tramp_table + .hidden _libX11_so_tramp_table + .align 8 +_libX11_so_tramp_table: + .zero 9464 + + .text + + .globl _libX11_so_tramp_resolve + .hidden _libX11_so_tramp_resolve + + .globl _libX11_so_save_regs_and_resolve + .hidden _libX11_so_save_regs_and_resolve + .type _libX11_so_save_regs_and_resolve, %function +_libX11_so_save_regs_and_resolve: + .cfi_startproc + + // Slow path which calls dlsym, taken only on first call. + // Registers are saved according to "Procedure Call Standard for the Arm® 64-bit Architecture". + // For DWARF directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + // Stack is aligned at 16 bytes + +#define PUSH_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 8 +#define POP_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore reg2; .cfi_restore reg1 + +#define PUSH_WIDE_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-32]!; .cfi_adjust_cfa_offset 32; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 16 +#define POP_WIDE_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #32; .cfi_adjust_cfa_offset -32; .cfi_restore reg2; .cfi_restore reg1 + + // Save only arguments (and lr) + PUSH_PAIR(x0, x1) + PUSH_PAIR(x2, x3) + PUSH_PAIR(x4, x5) + PUSH_PAIR(x6, x7) + PUSH_PAIR(x8, lr) + + ldr x0, [sp, #80] // 16*5 + + PUSH_WIDE_PAIR(q0, q1) + PUSH_WIDE_PAIR(q2, q3) + PUSH_WIDE_PAIR(q4, q5) + PUSH_WIDE_PAIR(q6, q7) + + // Stack is aligned at 16 bytes + + bl _libX11_so_tramp_resolve + mov ip0, x0 + + // TODO: pop pc? + + POP_WIDE_PAIR(q6, q7) + POP_WIDE_PAIR(q4, q5) + POP_WIDE_PAIR(q2, q3) + POP_WIDE_PAIR(q0, q1) + + POP_PAIR(x8, lr) + POP_PAIR(x6, x7) + POP_PAIR(x4, x5) + POP_PAIR(x2, x3) + POP_PAIR(x0, x1) + + br lr + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl KeySymToUcs4 + .p2align 4 + .type KeySymToUcs4, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden KeySymToUcs4 +#endif +KeySymToUcs4: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+0 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+0] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 0 & 0xffff +#if 0 > 0xffff + movk ip0, 0 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XActivateScreenSaver + .p2align 4 + .type XActivateScreenSaver, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XActivateScreenSaver +#endif +XActivateScreenSaver: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1 & 0xffff +#if 1 > 0xffff + movk ip0, 1 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAddConnectionWatch + .p2align 4 + .type XAddConnectionWatch, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAddConnectionWatch +#endif +XAddConnectionWatch: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+16 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+16] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 2 & 0xffff +#if 2 > 0xffff + movk ip0, 2 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAddExtension + .p2align 4 + .type XAddExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAddExtension +#endif +XAddExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+24 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+24] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 3 & 0xffff +#if 3 > 0xffff + movk ip0, 3 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAddHost + .p2align 4 + .type XAddHost, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAddHost +#endif +XAddHost: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+32 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+32] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 4 & 0xffff +#if 4 > 0xffff + movk ip0, 4 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAddHosts + .p2align 4 + .type XAddHosts, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAddHosts +#endif +XAddHosts: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+40 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+40] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 5 & 0xffff +#if 5 > 0xffff + movk ip0, 5 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAddPixel + .p2align 4 + .type XAddPixel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAddPixel +#endif +XAddPixel: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+48 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+48] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 6 & 0xffff +#if 6 > 0xffff + movk ip0, 6 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAddToExtensionList + .p2align 4 + .type XAddToExtensionList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAddToExtensionList +#endif +XAddToExtensionList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+56 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+56] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 7 & 0xffff +#if 7 > 0xffff + movk ip0, 7 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAddToSaveSet + .p2align 4 + .type XAddToSaveSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAddToSaveSet +#endif +XAddToSaveSet: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+64 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+64] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 8 & 0xffff +#if 8 > 0xffff + movk ip0, 8 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllPlanes + .p2align 4 + .type XAllPlanes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllPlanes +#endif +XAllPlanes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+72 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+72] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 9 & 0xffff +#if 9 > 0xffff + movk ip0, 9 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllocClassHint + .p2align 4 + .type XAllocClassHint, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllocClassHint +#endif +XAllocClassHint: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+80 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+80] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 10 & 0xffff +#if 10 > 0xffff + movk ip0, 10 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllocColor + .p2align 4 + .type XAllocColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllocColor +#endif +XAllocColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+88 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+88] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 11 & 0xffff +#if 11 > 0xffff + movk ip0, 11 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllocColorCells + .p2align 4 + .type XAllocColorCells, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllocColorCells +#endif +XAllocColorCells: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+96 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+96] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 12 & 0xffff +#if 12 > 0xffff + movk ip0, 12 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllocColorPlanes + .p2align 4 + .type XAllocColorPlanes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllocColorPlanes +#endif +XAllocColorPlanes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+104 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 13 & 0xffff +#if 13 > 0xffff + movk ip0, 13 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllocIconSize + .p2align 4 + .type XAllocIconSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllocIconSize +#endif +XAllocIconSize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+112 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 14 & 0xffff +#if 14 > 0xffff + movk ip0, 14 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllocNamedColor + .p2align 4 + .type XAllocNamedColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllocNamedColor +#endif +XAllocNamedColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+120 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 15 & 0xffff +#if 15 > 0xffff + movk ip0, 15 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllocSizeHints + .p2align 4 + .type XAllocSizeHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllocSizeHints +#endif +XAllocSizeHints: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+128 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 16 & 0xffff +#if 16 > 0xffff + movk ip0, 16 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllocStandardColormap + .p2align 4 + .type XAllocStandardColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllocStandardColormap +#endif +XAllocStandardColormap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+136 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 17 & 0xffff +#if 17 > 0xffff + movk ip0, 17 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllocWMHints + .p2align 4 + .type XAllocWMHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllocWMHints +#endif +XAllocWMHints: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+144 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 18 & 0xffff +#if 18 > 0xffff + movk ip0, 18 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllowEvents + .p2align 4 + .type XAllowEvents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllowEvents +#endif +XAllowEvents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+152 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 19 & 0xffff +#if 19 > 0xffff + movk ip0, 19 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAutoRepeatOff + .p2align 4 + .type XAutoRepeatOff, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAutoRepeatOff +#endif +XAutoRepeatOff: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+160 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 20 & 0xffff +#if 20 > 0xffff + movk ip0, 20 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAutoRepeatOn + .p2align 4 + .type XAutoRepeatOn, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAutoRepeatOn +#endif +XAutoRepeatOn: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+168 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 21 & 0xffff +#if 21 > 0xffff + movk ip0, 21 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XBaseFontNameListOfFontSet + .p2align 4 + .type XBaseFontNameListOfFontSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XBaseFontNameListOfFontSet +#endif +XBaseFontNameListOfFontSet: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+176 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 22 & 0xffff +#if 22 > 0xffff + movk ip0, 22 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XBell + .p2align 4 + .type XBell, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XBell +#endif +XBell: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+184 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 23 & 0xffff +#if 23 > 0xffff + movk ip0, 23 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XBitmapBitOrder + .p2align 4 + .type XBitmapBitOrder, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XBitmapBitOrder +#endif +XBitmapBitOrder: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+192 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 24 & 0xffff +#if 24 > 0xffff + movk ip0, 24 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XBitmapPad + .p2align 4 + .type XBitmapPad, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XBitmapPad +#endif +XBitmapPad: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+200 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 25 & 0xffff +#if 25 > 0xffff + movk ip0, 25 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XBitmapUnit + .p2align 4 + .type XBitmapUnit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XBitmapUnit +#endif +XBitmapUnit: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+208 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 26 & 0xffff +#if 26 > 0xffff + movk ip0, 26 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XBlackPixel + .p2align 4 + .type XBlackPixel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XBlackPixel +#endif +XBlackPixel: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+216 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 27 & 0xffff +#if 27 > 0xffff + movk ip0, 27 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XBlackPixelOfScreen + .p2align 4 + .type XBlackPixelOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XBlackPixelOfScreen +#endif +XBlackPixelOfScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+224 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 28 & 0xffff +#if 28 > 0xffff + movk ip0, 28 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCellsOfScreen + .p2align 4 + .type XCellsOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCellsOfScreen +#endif +XCellsOfScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+232 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 29 & 0xffff +#if 29 > 0xffff + movk ip0, 29 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XChangeActivePointerGrab + .p2align 4 + .type XChangeActivePointerGrab, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XChangeActivePointerGrab +#endif +XChangeActivePointerGrab: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+240 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 30 & 0xffff +#if 30 > 0xffff + movk ip0, 30 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XChangeGC + .p2align 4 + .type XChangeGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XChangeGC +#endif +XChangeGC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+248 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 31 & 0xffff +#if 31 > 0xffff + movk ip0, 31 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XChangeKeyboardControl + .p2align 4 + .type XChangeKeyboardControl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XChangeKeyboardControl +#endif +XChangeKeyboardControl: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+256 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 32 & 0xffff +#if 32 > 0xffff + movk ip0, 32 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XChangeKeyboardMapping + .p2align 4 + .type XChangeKeyboardMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XChangeKeyboardMapping +#endif +XChangeKeyboardMapping: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+264 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 33 & 0xffff +#if 33 > 0xffff + movk ip0, 33 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XChangePointerControl + .p2align 4 + .type XChangePointerControl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XChangePointerControl +#endif +XChangePointerControl: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+272 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 34 & 0xffff +#if 34 > 0xffff + movk ip0, 34 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XChangeProperty + .p2align 4 + .type XChangeProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XChangeProperty +#endif +XChangeProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+280 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 35 & 0xffff +#if 35 > 0xffff + movk ip0, 35 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XChangeSaveSet + .p2align 4 + .type XChangeSaveSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XChangeSaveSet +#endif +XChangeSaveSet: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+288 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 36 & 0xffff +#if 36 > 0xffff + movk ip0, 36 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XChangeWindowAttributes + .p2align 4 + .type XChangeWindowAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XChangeWindowAttributes +#endif +XChangeWindowAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+296 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 37 & 0xffff +#if 37 > 0xffff + movk ip0, 37 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCheckIfEvent + .p2align 4 + .type XCheckIfEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCheckIfEvent +#endif +XCheckIfEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+304 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 38 & 0xffff +#if 38 > 0xffff + movk ip0, 38 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCheckMaskEvent + .p2align 4 + .type XCheckMaskEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCheckMaskEvent +#endif +XCheckMaskEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+312 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 39 & 0xffff +#if 39 > 0xffff + movk ip0, 39 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCheckTypedEvent + .p2align 4 + .type XCheckTypedEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCheckTypedEvent +#endif +XCheckTypedEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+320 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 40 & 0xffff +#if 40 > 0xffff + movk ip0, 40 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCheckTypedWindowEvent + .p2align 4 + .type XCheckTypedWindowEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCheckTypedWindowEvent +#endif +XCheckTypedWindowEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+328 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 41 & 0xffff +#if 41 > 0xffff + movk ip0, 41 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCheckWindowEvent + .p2align 4 + .type XCheckWindowEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCheckWindowEvent +#endif +XCheckWindowEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+336 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 42 & 0xffff +#if 42 > 0xffff + movk ip0, 42 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCirculateSubwindows + .p2align 4 + .type XCirculateSubwindows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCirculateSubwindows +#endif +XCirculateSubwindows: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+344 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 43 & 0xffff +#if 43 > 0xffff + movk ip0, 43 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCirculateSubwindowsDown + .p2align 4 + .type XCirculateSubwindowsDown, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCirculateSubwindowsDown +#endif +XCirculateSubwindowsDown: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+352 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 44 & 0xffff +#if 44 > 0xffff + movk ip0, 44 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCirculateSubwindowsUp + .p2align 4 + .type XCirculateSubwindowsUp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCirculateSubwindowsUp +#endif +XCirculateSubwindowsUp: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+360 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 45 & 0xffff +#if 45 > 0xffff + movk ip0, 45 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XClearArea + .p2align 4 + .type XClearArea, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XClearArea +#endif +XClearArea: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+368 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 46 & 0xffff +#if 46 > 0xffff + movk ip0, 46 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XClearWindow + .p2align 4 + .type XClearWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XClearWindow +#endif +XClearWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+376 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 47 & 0xffff +#if 47 > 0xffff + movk ip0, 47 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XClipBox + .p2align 4 + .type XClipBox, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XClipBox +#endif +XClipBox: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+384 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 48 & 0xffff +#if 48 > 0xffff + movk ip0, 48 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCloseDisplay + .p2align 4 + .type XCloseDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCloseDisplay +#endif +XCloseDisplay: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+392 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 49 & 0xffff +#if 49 > 0xffff + movk ip0, 49 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCloseIM + .p2align 4 + .type XCloseIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCloseIM +#endif +XCloseIM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+400 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 50 & 0xffff +#if 50 > 0xffff + movk ip0, 50 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCloseOM + .p2align 4 + .type XCloseOM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCloseOM +#endif +XCloseOM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+408 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 51 & 0xffff +#if 51 > 0xffff + movk ip0, 51 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XConfigureWindow + .p2align 4 + .type XConfigureWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XConfigureWindow +#endif +XConfigureWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+416 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 52 & 0xffff +#if 52 > 0xffff + movk ip0, 52 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XConnectionNumber + .p2align 4 + .type XConnectionNumber, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XConnectionNumber +#endif +XConnectionNumber: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+424 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 53 & 0xffff +#if 53 > 0xffff + movk ip0, 53 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XContextDependentDrawing + .p2align 4 + .type XContextDependentDrawing, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XContextDependentDrawing +#endif +XContextDependentDrawing: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+432 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 54 & 0xffff +#if 54 > 0xffff + movk ip0, 54 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XContextualDrawing + .p2align 4 + .type XContextualDrawing, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XContextualDrawing +#endif +XContextualDrawing: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+440 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 55 & 0xffff +#if 55 > 0xffff + movk ip0, 55 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XConvertCase + .p2align 4 + .type XConvertCase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XConvertCase +#endif +XConvertCase: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+448 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 56 & 0xffff +#if 56 > 0xffff + movk ip0, 56 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XConvertSelection + .p2align 4 + .type XConvertSelection, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XConvertSelection +#endif +XConvertSelection: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+456 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+456] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 57 & 0xffff +#if 57 > 0xffff + movk ip0, 57 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCopyArea + .p2align 4 + .type XCopyArea, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCopyArea +#endif +XCopyArea: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+464 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+464] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 58 & 0xffff +#if 58 > 0xffff + movk ip0, 58 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCopyColormapAndFree + .p2align 4 + .type XCopyColormapAndFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCopyColormapAndFree +#endif +XCopyColormapAndFree: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+472 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+472] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 59 & 0xffff +#if 59 > 0xffff + movk ip0, 59 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCopyGC + .p2align 4 + .type XCopyGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCopyGC +#endif +XCopyGC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+480 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+480] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 60 & 0xffff +#if 60 > 0xffff + movk ip0, 60 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCopyPlane + .p2align 4 + .type XCopyPlane, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCopyPlane +#endif +XCopyPlane: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+488 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+488] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 61 & 0xffff +#if 61 > 0xffff + movk ip0, 61 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateBitmapFromData + .p2align 4 + .type XCreateBitmapFromData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateBitmapFromData +#endif +XCreateBitmapFromData: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+496 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+496] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 62 & 0xffff +#if 62 > 0xffff + movk ip0, 62 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateColormap + .p2align 4 + .type XCreateColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateColormap +#endif +XCreateColormap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+504 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+504] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 63 & 0xffff +#if 63 > 0xffff + movk ip0, 63 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateFontCursor + .p2align 4 + .type XCreateFontCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateFontCursor +#endif +XCreateFontCursor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+512 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+512] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 64 & 0xffff +#if 64 > 0xffff + movk ip0, 64 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateFontSet + .p2align 4 + .type XCreateFontSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateFontSet +#endif +XCreateFontSet: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+520 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+520] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 65 & 0xffff +#if 65 > 0xffff + movk ip0, 65 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateGC + .p2align 4 + .type XCreateGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateGC +#endif +XCreateGC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+528 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+528] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 66 & 0xffff +#if 66 > 0xffff + movk ip0, 66 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateGlyphCursor + .p2align 4 + .type XCreateGlyphCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateGlyphCursor +#endif +XCreateGlyphCursor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+536 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+536] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 67 & 0xffff +#if 67 > 0xffff + movk ip0, 67 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateIC + .p2align 4 + .type XCreateIC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateIC +#endif +XCreateIC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+544 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+544] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 68 & 0xffff +#if 68 > 0xffff + movk ip0, 68 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateImage + .p2align 4 + .type XCreateImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateImage +#endif +XCreateImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+552 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+552] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 69 & 0xffff +#if 69 > 0xffff + movk ip0, 69 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateOC + .p2align 4 + .type XCreateOC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateOC +#endif +XCreateOC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+560 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+560] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 70 & 0xffff +#if 70 > 0xffff + movk ip0, 70 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreatePixmap + .p2align 4 + .type XCreatePixmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreatePixmap +#endif +XCreatePixmap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+568 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+568] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 71 & 0xffff +#if 71 > 0xffff + movk ip0, 71 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreatePixmapCursor + .p2align 4 + .type XCreatePixmapCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreatePixmapCursor +#endif +XCreatePixmapCursor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+576 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+576] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 72 & 0xffff +#if 72 > 0xffff + movk ip0, 72 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreatePixmapFromBitmapData + .p2align 4 + .type XCreatePixmapFromBitmapData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreatePixmapFromBitmapData +#endif +XCreatePixmapFromBitmapData: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+584 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+584] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 73 & 0xffff +#if 73 > 0xffff + movk ip0, 73 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateRegion + .p2align 4 + .type XCreateRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateRegion +#endif +XCreateRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+592 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+592] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 74 & 0xffff +#if 74 > 0xffff + movk ip0, 74 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateSimpleWindow + .p2align 4 + .type XCreateSimpleWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateSimpleWindow +#endif +XCreateSimpleWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+600 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+600] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 75 & 0xffff +#if 75 > 0xffff + movk ip0, 75 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateWindow + .p2align 4 + .type XCreateWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateWindow +#endif +XCreateWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+608 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+608] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 76 & 0xffff +#if 76 > 0xffff + movk ip0, 76 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultColormap + .p2align 4 + .type XDefaultColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultColormap +#endif +XDefaultColormap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+616 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+616] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 77 & 0xffff +#if 77 > 0xffff + movk ip0, 77 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultColormapOfScreen + .p2align 4 + .type XDefaultColormapOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultColormapOfScreen +#endif +XDefaultColormapOfScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+624 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+624] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 78 & 0xffff +#if 78 > 0xffff + movk ip0, 78 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultDepth + .p2align 4 + .type XDefaultDepth, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultDepth +#endif +XDefaultDepth: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+632 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+632] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 79 & 0xffff +#if 79 > 0xffff + movk ip0, 79 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultDepthOfScreen + .p2align 4 + .type XDefaultDepthOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultDepthOfScreen +#endif +XDefaultDepthOfScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+640 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+640] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 80 & 0xffff +#if 80 > 0xffff + movk ip0, 80 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultGC + .p2align 4 + .type XDefaultGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultGC +#endif +XDefaultGC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+648 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+648] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 81 & 0xffff +#if 81 > 0xffff + movk ip0, 81 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultGCOfScreen + .p2align 4 + .type XDefaultGCOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultGCOfScreen +#endif +XDefaultGCOfScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+656 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+656] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 82 & 0xffff +#if 82 > 0xffff + movk ip0, 82 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultRootWindow + .p2align 4 + .type XDefaultRootWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultRootWindow +#endif +XDefaultRootWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+664 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+664] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 83 & 0xffff +#if 83 > 0xffff + movk ip0, 83 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultScreen + .p2align 4 + .type XDefaultScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultScreen +#endif +XDefaultScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+672 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+672] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 84 & 0xffff +#if 84 > 0xffff + movk ip0, 84 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultScreenOfDisplay + .p2align 4 + .type XDefaultScreenOfDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultScreenOfDisplay +#endif +XDefaultScreenOfDisplay: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+680 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+680] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 85 & 0xffff +#if 85 > 0xffff + movk ip0, 85 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultString + .p2align 4 + .type XDefaultString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultString +#endif +XDefaultString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+688 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+688] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 86 & 0xffff +#if 86 > 0xffff + movk ip0, 86 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultVisual + .p2align 4 + .type XDefaultVisual, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultVisual +#endif +XDefaultVisual: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+696 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+696] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 87 & 0xffff +#if 87 > 0xffff + movk ip0, 87 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultVisualOfScreen + .p2align 4 + .type XDefaultVisualOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultVisualOfScreen +#endif +XDefaultVisualOfScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+704 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+704] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 88 & 0xffff +#if 88 > 0xffff + movk ip0, 88 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefineCursor + .p2align 4 + .type XDefineCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefineCursor +#endif +XDefineCursor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+712 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+712] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 89 & 0xffff +#if 89 > 0xffff + movk ip0, 89 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDeleteContext + .p2align 4 + .type XDeleteContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDeleteContext +#endif +XDeleteContext: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+720 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+720] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 90 & 0xffff +#if 90 > 0xffff + movk ip0, 90 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDeleteModifiermapEntry + .p2align 4 + .type XDeleteModifiermapEntry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDeleteModifiermapEntry +#endif +XDeleteModifiermapEntry: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+728 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+728] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 91 & 0xffff +#if 91 > 0xffff + movk ip0, 91 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDeleteProperty + .p2align 4 + .type XDeleteProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDeleteProperty +#endif +XDeleteProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+736 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+736] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 92 & 0xffff +#if 92 > 0xffff + movk ip0, 92 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDestroyIC + .p2align 4 + .type XDestroyIC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDestroyIC +#endif +XDestroyIC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+744 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+744] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 93 & 0xffff +#if 93 > 0xffff + movk ip0, 93 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDestroyImage + .p2align 4 + .type XDestroyImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDestroyImage +#endif +XDestroyImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+752 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+752] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 94 & 0xffff +#if 94 > 0xffff + movk ip0, 94 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDestroyOC + .p2align 4 + .type XDestroyOC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDestroyOC +#endif +XDestroyOC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+760 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+760] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 95 & 0xffff +#if 95 > 0xffff + movk ip0, 95 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDestroyRegion + .p2align 4 + .type XDestroyRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDestroyRegion +#endif +XDestroyRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+768 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+768] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 96 & 0xffff +#if 96 > 0xffff + movk ip0, 96 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDestroySubwindows + .p2align 4 + .type XDestroySubwindows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDestroySubwindows +#endif +XDestroySubwindows: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+776 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+776] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 97 & 0xffff +#if 97 > 0xffff + movk ip0, 97 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDestroyWindow + .p2align 4 + .type XDestroyWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDestroyWindow +#endif +XDestroyWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+784 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+784] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 98 & 0xffff +#if 98 > 0xffff + movk ip0, 98 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDirectionalDependentDrawing + .p2align 4 + .type XDirectionalDependentDrawing, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDirectionalDependentDrawing +#endif +XDirectionalDependentDrawing: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+792 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+792] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 99 & 0xffff +#if 99 > 0xffff + movk ip0, 99 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisableAccessControl + .p2align 4 + .type XDisableAccessControl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisableAccessControl +#endif +XDisableAccessControl: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+800 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+800] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 100 & 0xffff +#if 100 > 0xffff + movk ip0, 100 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayCells + .p2align 4 + .type XDisplayCells, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayCells +#endif +XDisplayCells: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+808 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+808] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 101 & 0xffff +#if 101 > 0xffff + movk ip0, 101 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayHeight + .p2align 4 + .type XDisplayHeight, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayHeight +#endif +XDisplayHeight: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+816 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+816] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 102 & 0xffff +#if 102 > 0xffff + movk ip0, 102 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayHeightMM + .p2align 4 + .type XDisplayHeightMM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayHeightMM +#endif +XDisplayHeightMM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+824 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+824] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 103 & 0xffff +#if 103 > 0xffff + movk ip0, 103 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayKeycodes + .p2align 4 + .type XDisplayKeycodes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayKeycodes +#endif +XDisplayKeycodes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+832 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+832] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 104 & 0xffff +#if 104 > 0xffff + movk ip0, 104 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayMotionBufferSize + .p2align 4 + .type XDisplayMotionBufferSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayMotionBufferSize +#endif +XDisplayMotionBufferSize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+840 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+840] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 105 & 0xffff +#if 105 > 0xffff + movk ip0, 105 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayName + .p2align 4 + .type XDisplayName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayName +#endif +XDisplayName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+848 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+848] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 106 & 0xffff +#if 106 > 0xffff + movk ip0, 106 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayOfIM + .p2align 4 + .type XDisplayOfIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayOfIM +#endif +XDisplayOfIM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+856 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+856] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 107 & 0xffff +#if 107 > 0xffff + movk ip0, 107 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayOfOM + .p2align 4 + .type XDisplayOfOM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayOfOM +#endif +XDisplayOfOM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+864 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+864] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 108 & 0xffff +#if 108 > 0xffff + movk ip0, 108 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayOfScreen + .p2align 4 + .type XDisplayOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayOfScreen +#endif +XDisplayOfScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+872 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+872] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 109 & 0xffff +#if 109 > 0xffff + movk ip0, 109 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayPlanes + .p2align 4 + .type XDisplayPlanes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayPlanes +#endif +XDisplayPlanes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+880 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+880] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 110 & 0xffff +#if 110 > 0xffff + movk ip0, 110 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayString + .p2align 4 + .type XDisplayString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayString +#endif +XDisplayString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+888 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+888] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 111 & 0xffff +#if 111 > 0xffff + movk ip0, 111 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayWidth + .p2align 4 + .type XDisplayWidth, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayWidth +#endif +XDisplayWidth: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+896 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+896] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 112 & 0xffff +#if 112 > 0xffff + movk ip0, 112 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayWidthMM + .p2align 4 + .type XDisplayWidthMM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayWidthMM +#endif +XDisplayWidthMM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+904 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+904] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 113 & 0xffff +#if 113 > 0xffff + movk ip0, 113 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDoesBackingStore + .p2align 4 + .type XDoesBackingStore, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDoesBackingStore +#endif +XDoesBackingStore: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+912 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+912] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 114 & 0xffff +#if 114 > 0xffff + movk ip0, 114 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDoesSaveUnders + .p2align 4 + .type XDoesSaveUnders, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDoesSaveUnders +#endif +XDoesSaveUnders: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+920 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+920] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 115 & 0xffff +#if 115 > 0xffff + movk ip0, 115 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawArc + .p2align 4 + .type XDrawArc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawArc +#endif +XDrawArc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+928 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+928] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 116 & 0xffff +#if 116 > 0xffff + movk ip0, 116 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawArcs + .p2align 4 + .type XDrawArcs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawArcs +#endif +XDrawArcs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+936 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+936] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 117 & 0xffff +#if 117 > 0xffff + movk ip0, 117 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawImageString + .p2align 4 + .type XDrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawImageString +#endif +XDrawImageString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+944 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+944] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 118 & 0xffff +#if 118 > 0xffff + movk ip0, 118 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawImageString16 + .p2align 4 + .type XDrawImageString16, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawImageString16 +#endif +XDrawImageString16: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+952 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+952] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 119 & 0xffff +#if 119 > 0xffff + movk ip0, 119 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawLine + .p2align 4 + .type XDrawLine, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawLine +#endif +XDrawLine: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+960 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+960] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 120 & 0xffff +#if 120 > 0xffff + movk ip0, 120 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawLines + .p2align 4 + .type XDrawLines, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawLines +#endif +XDrawLines: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+968 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+968] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 121 & 0xffff +#if 121 > 0xffff + movk ip0, 121 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawPoint + .p2align 4 + .type XDrawPoint, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawPoint +#endif +XDrawPoint: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+976 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+976] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 122 & 0xffff +#if 122 > 0xffff + movk ip0, 122 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawPoints + .p2align 4 + .type XDrawPoints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawPoints +#endif +XDrawPoints: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+984 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+984] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 123 & 0xffff +#if 123 > 0xffff + movk ip0, 123 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawRectangle + .p2align 4 + .type XDrawRectangle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawRectangle +#endif +XDrawRectangle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+992 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+992] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 124 & 0xffff +#if 124 > 0xffff + movk ip0, 124 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawRectangles + .p2align 4 + .type XDrawRectangles, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawRectangles +#endif +XDrawRectangles: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1000 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1000] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 125 & 0xffff +#if 125 > 0xffff + movk ip0, 125 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawSegments + .p2align 4 + .type XDrawSegments, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawSegments +#endif +XDrawSegments: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1008 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1008] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 126 & 0xffff +#if 126 > 0xffff + movk ip0, 126 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawString + .p2align 4 + .type XDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawString +#endif +XDrawString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1016 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1016] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 127 & 0xffff +#if 127 > 0xffff + movk ip0, 127 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawString16 + .p2align 4 + .type XDrawString16, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawString16 +#endif +XDrawString16: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1024 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1024] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 128 & 0xffff +#if 128 > 0xffff + movk ip0, 128 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawText + .p2align 4 + .type XDrawText, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawText +#endif +XDrawText: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1032 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1032] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 129 & 0xffff +#if 129 > 0xffff + movk ip0, 129 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawText16 + .p2align 4 + .type XDrawText16, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawText16 +#endif +XDrawText16: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1040 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1040] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 130 & 0xffff +#if 130 > 0xffff + movk ip0, 130 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XEHeadOfExtensionList + .p2align 4 + .type XEHeadOfExtensionList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XEHeadOfExtensionList +#endif +XEHeadOfExtensionList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1048 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1048] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 131 & 0xffff +#if 131 > 0xffff + movk ip0, 131 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetBeforeFlush + .p2align 4 + .type XESetBeforeFlush, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetBeforeFlush +#endif +XESetBeforeFlush: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1056 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1056] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 132 & 0xffff +#if 132 > 0xffff + movk ip0, 132 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetCloseDisplay + .p2align 4 + .type XESetCloseDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetCloseDisplay +#endif +XESetCloseDisplay: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1064 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1064] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 133 & 0xffff +#if 133 > 0xffff + movk ip0, 133 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetCopyEventCookie + .p2align 4 + .type XESetCopyEventCookie, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetCopyEventCookie +#endif +XESetCopyEventCookie: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1072 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1072] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 134 & 0xffff +#if 134 > 0xffff + movk ip0, 134 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetCopyGC + .p2align 4 + .type XESetCopyGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetCopyGC +#endif +XESetCopyGC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1080 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1080] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 135 & 0xffff +#if 135 > 0xffff + movk ip0, 135 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetCreateFont + .p2align 4 + .type XESetCreateFont, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetCreateFont +#endif +XESetCreateFont: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1088 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1088] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 136 & 0xffff +#if 136 > 0xffff + movk ip0, 136 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetCreateGC + .p2align 4 + .type XESetCreateGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetCreateGC +#endif +XESetCreateGC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1096 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1096] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 137 & 0xffff +#if 137 > 0xffff + movk ip0, 137 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetError + .p2align 4 + .type XESetError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetError +#endif +XESetError: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1104 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 138 & 0xffff +#if 138 > 0xffff + movk ip0, 138 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetErrorString + .p2align 4 + .type XESetErrorString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetErrorString +#endif +XESetErrorString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1112 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 139 & 0xffff +#if 139 > 0xffff + movk ip0, 139 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetEventToWire + .p2align 4 + .type XESetEventToWire, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetEventToWire +#endif +XESetEventToWire: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1120 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 140 & 0xffff +#if 140 > 0xffff + movk ip0, 140 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetFlushGC + .p2align 4 + .type XESetFlushGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetFlushGC +#endif +XESetFlushGC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1128 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 141 & 0xffff +#if 141 > 0xffff + movk ip0, 141 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetFreeFont + .p2align 4 + .type XESetFreeFont, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetFreeFont +#endif +XESetFreeFont: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1136 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 142 & 0xffff +#if 142 > 0xffff + movk ip0, 142 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetFreeGC + .p2align 4 + .type XESetFreeGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetFreeGC +#endif +XESetFreeGC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1144 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 143 & 0xffff +#if 143 > 0xffff + movk ip0, 143 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetPrintErrorValues + .p2align 4 + .type XESetPrintErrorValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetPrintErrorValues +#endif +XESetPrintErrorValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1152 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 144 & 0xffff +#if 144 > 0xffff + movk ip0, 144 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetWireToError + .p2align 4 + .type XESetWireToError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetWireToError +#endif +XESetWireToError: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1160 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 145 & 0xffff +#if 145 > 0xffff + movk ip0, 145 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetWireToEvent + .p2align 4 + .type XESetWireToEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetWireToEvent +#endif +XESetWireToEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1168 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 146 & 0xffff +#if 146 > 0xffff + movk ip0, 146 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetWireToEventCookie + .p2align 4 + .type XESetWireToEventCookie, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetWireToEventCookie +#endif +XESetWireToEventCookie: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1176 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 147 & 0xffff +#if 147 > 0xffff + movk ip0, 147 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XEmptyRegion + .p2align 4 + .type XEmptyRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XEmptyRegion +#endif +XEmptyRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1184 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 148 & 0xffff +#if 148 > 0xffff + movk ip0, 148 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XEnableAccessControl + .p2align 4 + .type XEnableAccessControl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XEnableAccessControl +#endif +XEnableAccessControl: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1192 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 149 & 0xffff +#if 149 > 0xffff + movk ip0, 149 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XEqualRegion + .p2align 4 + .type XEqualRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XEqualRegion +#endif +XEqualRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1200 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 150 & 0xffff +#if 150 > 0xffff + movk ip0, 150 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XEventMaskOfScreen + .p2align 4 + .type XEventMaskOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XEventMaskOfScreen +#endif +XEventMaskOfScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1208 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 151 & 0xffff +#if 151 > 0xffff + movk ip0, 151 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XEventsQueued + .p2align 4 + .type XEventsQueued, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XEventsQueued +#endif +XEventsQueued: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1216 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 152 & 0xffff +#if 152 > 0xffff + movk ip0, 152 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XExtendedMaxRequestSize + .p2align 4 + .type XExtendedMaxRequestSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XExtendedMaxRequestSize +#endif +XExtendedMaxRequestSize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1224 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 153 & 0xffff +#if 153 > 0xffff + movk ip0, 153 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XExtentsOfFontSet + .p2align 4 + .type XExtentsOfFontSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XExtentsOfFontSet +#endif +XExtentsOfFontSet: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1232 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 154 & 0xffff +#if 154 > 0xffff + movk ip0, 154 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFetchBuffer + .p2align 4 + .type XFetchBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFetchBuffer +#endif +XFetchBuffer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1240 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 155 & 0xffff +#if 155 > 0xffff + movk ip0, 155 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFetchBytes + .p2align 4 + .type XFetchBytes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFetchBytes +#endif +XFetchBytes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1248 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 156 & 0xffff +#if 156 > 0xffff + movk ip0, 156 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFetchName + .p2align 4 + .type XFetchName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFetchName +#endif +XFetchName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1256 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 157 & 0xffff +#if 157 > 0xffff + movk ip0, 157 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFillArc + .p2align 4 + .type XFillArc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFillArc +#endif +XFillArc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1264 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 158 & 0xffff +#if 158 > 0xffff + movk ip0, 158 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFillArcs + .p2align 4 + .type XFillArcs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFillArcs +#endif +XFillArcs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1272 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 159 & 0xffff +#if 159 > 0xffff + movk ip0, 159 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFillPolygon + .p2align 4 + .type XFillPolygon, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFillPolygon +#endif +XFillPolygon: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1280 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 160 & 0xffff +#if 160 > 0xffff + movk ip0, 160 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFillRectangle + .p2align 4 + .type XFillRectangle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFillRectangle +#endif +XFillRectangle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1288 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 161 & 0xffff +#if 161 > 0xffff + movk ip0, 161 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFillRectangles + .p2align 4 + .type XFillRectangles, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFillRectangles +#endif +XFillRectangles: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1296 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 162 & 0xffff +#if 162 > 0xffff + movk ip0, 162 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFilterEvent + .p2align 4 + .type XFilterEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFilterEvent +#endif +XFilterEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1304 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 163 & 0xffff +#if 163 > 0xffff + movk ip0, 163 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFindContext + .p2align 4 + .type XFindContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFindContext +#endif +XFindContext: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1312 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 164 & 0xffff +#if 164 > 0xffff + movk ip0, 164 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFindOnExtensionList + .p2align 4 + .type XFindOnExtensionList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFindOnExtensionList +#endif +XFindOnExtensionList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1320 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 165 & 0xffff +#if 165 > 0xffff + movk ip0, 165 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFlush + .p2align 4 + .type XFlush, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFlush +#endif +XFlush: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1328 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 166 & 0xffff +#if 166 > 0xffff + movk ip0, 166 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFlushGC + .p2align 4 + .type XFlushGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFlushGC +#endif +XFlushGC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1336 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 167 & 0xffff +#if 167 > 0xffff + movk ip0, 167 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFontsOfFontSet + .p2align 4 + .type XFontsOfFontSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFontsOfFontSet +#endif +XFontsOfFontSet: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1344 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 168 & 0xffff +#if 168 > 0xffff + movk ip0, 168 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XForceScreenSaver + .p2align 4 + .type XForceScreenSaver, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XForceScreenSaver +#endif +XForceScreenSaver: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1352 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 169 & 0xffff +#if 169 > 0xffff + movk ip0, 169 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFree + .p2align 4 + .type XFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFree +#endif +XFree: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1360 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 170 & 0xffff +#if 170 > 0xffff + movk ip0, 170 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeColormap + .p2align 4 + .type XFreeColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeColormap +#endif +XFreeColormap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1368 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 171 & 0xffff +#if 171 > 0xffff + movk ip0, 171 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeColors + .p2align 4 + .type XFreeColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeColors +#endif +XFreeColors: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1376 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 172 & 0xffff +#if 172 > 0xffff + movk ip0, 172 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeCursor + .p2align 4 + .type XFreeCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeCursor +#endif +XFreeCursor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1384 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 173 & 0xffff +#if 173 > 0xffff + movk ip0, 173 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeEventData + .p2align 4 + .type XFreeEventData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeEventData +#endif +XFreeEventData: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1392 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 174 & 0xffff +#if 174 > 0xffff + movk ip0, 174 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeExtensionList + .p2align 4 + .type XFreeExtensionList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeExtensionList +#endif +XFreeExtensionList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1400 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 175 & 0xffff +#if 175 > 0xffff + movk ip0, 175 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeFont + .p2align 4 + .type XFreeFont, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeFont +#endif +XFreeFont: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1408 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 176 & 0xffff +#if 176 > 0xffff + movk ip0, 176 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeFontInfo + .p2align 4 + .type XFreeFontInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeFontInfo +#endif +XFreeFontInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1416 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 177 & 0xffff +#if 177 > 0xffff + movk ip0, 177 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeFontNames + .p2align 4 + .type XFreeFontNames, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeFontNames +#endif +XFreeFontNames: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1424 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 178 & 0xffff +#if 178 > 0xffff + movk ip0, 178 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeFontPath + .p2align 4 + .type XFreeFontPath, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeFontPath +#endif +XFreeFontPath: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1432 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 179 & 0xffff +#if 179 > 0xffff + movk ip0, 179 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeFontSet + .p2align 4 + .type XFreeFontSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeFontSet +#endif +XFreeFontSet: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1440 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 180 & 0xffff +#if 180 > 0xffff + movk ip0, 180 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeGC + .p2align 4 + .type XFreeGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeGC +#endif +XFreeGC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1448 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 181 & 0xffff +#if 181 > 0xffff + movk ip0, 181 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeModifiermap + .p2align 4 + .type XFreeModifiermap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeModifiermap +#endif +XFreeModifiermap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1456 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1456] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 182 & 0xffff +#if 182 > 0xffff + movk ip0, 182 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreePixmap + .p2align 4 + .type XFreePixmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreePixmap +#endif +XFreePixmap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1464 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1464] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 183 & 0xffff +#if 183 > 0xffff + movk ip0, 183 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeStringList + .p2align 4 + .type XFreeStringList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeStringList +#endif +XFreeStringList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1472 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1472] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 184 & 0xffff +#if 184 > 0xffff + movk ip0, 184 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeThreads + .p2align 4 + .type XFreeThreads, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeThreads +#endif +XFreeThreads: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1480 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1480] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 185 & 0xffff +#if 185 > 0xffff + movk ip0, 185 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGContextFromGC + .p2align 4 + .type XGContextFromGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGContextFromGC +#endif +XGContextFromGC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1488 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1488] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 186 & 0xffff +#if 186 > 0xffff + movk ip0, 186 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGeometry + .p2align 4 + .type XGeometry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGeometry +#endif +XGeometry: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1496 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1496] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 187 & 0xffff +#if 187 > 0xffff + movk ip0, 187 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetAtomName + .p2align 4 + .type XGetAtomName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetAtomName +#endif +XGetAtomName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1504 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1504] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 188 & 0xffff +#if 188 > 0xffff + movk ip0, 188 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetAtomNames + .p2align 4 + .type XGetAtomNames, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetAtomNames +#endif +XGetAtomNames: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1512 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1512] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 189 & 0xffff +#if 189 > 0xffff + movk ip0, 189 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetClassHint + .p2align 4 + .type XGetClassHint, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetClassHint +#endif +XGetClassHint: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1520 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1520] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 190 & 0xffff +#if 190 > 0xffff + movk ip0, 190 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetCommand + .p2align 4 + .type XGetCommand, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetCommand +#endif +XGetCommand: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1528 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1528] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 191 & 0xffff +#if 191 > 0xffff + movk ip0, 191 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetDefault + .p2align 4 + .type XGetDefault, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetDefault +#endif +XGetDefault: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1536 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1536] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 192 & 0xffff +#if 192 > 0xffff + movk ip0, 192 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetErrorDatabaseText + .p2align 4 + .type XGetErrorDatabaseText, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetErrorDatabaseText +#endif +XGetErrorDatabaseText: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1544 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1544] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 193 & 0xffff +#if 193 > 0xffff + movk ip0, 193 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetErrorText + .p2align 4 + .type XGetErrorText, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetErrorText +#endif +XGetErrorText: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1552 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1552] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 194 & 0xffff +#if 194 > 0xffff + movk ip0, 194 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetEventData + .p2align 4 + .type XGetEventData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetEventData +#endif +XGetEventData: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1560 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1560] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 195 & 0xffff +#if 195 > 0xffff + movk ip0, 195 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetFontPath + .p2align 4 + .type XGetFontPath, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetFontPath +#endif +XGetFontPath: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1568 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1568] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 196 & 0xffff +#if 196 > 0xffff + movk ip0, 196 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetFontProperty + .p2align 4 + .type XGetFontProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetFontProperty +#endif +XGetFontProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1576 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1576] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 197 & 0xffff +#if 197 > 0xffff + movk ip0, 197 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetGCValues + .p2align 4 + .type XGetGCValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetGCValues +#endif +XGetGCValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1584 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1584] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 198 & 0xffff +#if 198 > 0xffff + movk ip0, 198 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetGeometry + .p2align 4 + .type XGetGeometry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetGeometry +#endif +XGetGeometry: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1592 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1592] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 199 & 0xffff +#if 199 > 0xffff + movk ip0, 199 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetICValues + .p2align 4 + .type XGetICValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetICValues +#endif +XGetICValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1600 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1600] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 200 & 0xffff +#if 200 > 0xffff + movk ip0, 200 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetIMValues + .p2align 4 + .type XGetIMValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetIMValues +#endif +XGetIMValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1608 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1608] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 201 & 0xffff +#if 201 > 0xffff + movk ip0, 201 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetIconName + .p2align 4 + .type XGetIconName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetIconName +#endif +XGetIconName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1616 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1616] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 202 & 0xffff +#if 202 > 0xffff + movk ip0, 202 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetIconSizes + .p2align 4 + .type XGetIconSizes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetIconSizes +#endif +XGetIconSizes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1624 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1624] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 203 & 0xffff +#if 203 > 0xffff + movk ip0, 203 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetImage + .p2align 4 + .type XGetImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetImage +#endif +XGetImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1632 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1632] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 204 & 0xffff +#if 204 > 0xffff + movk ip0, 204 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetInputFocus + .p2align 4 + .type XGetInputFocus, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetInputFocus +#endif +XGetInputFocus: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1640 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1640] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 205 & 0xffff +#if 205 > 0xffff + movk ip0, 205 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetKeyboardControl + .p2align 4 + .type XGetKeyboardControl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetKeyboardControl +#endif +XGetKeyboardControl: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1648 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1648] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 206 & 0xffff +#if 206 > 0xffff + movk ip0, 206 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetKeyboardMapping + .p2align 4 + .type XGetKeyboardMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetKeyboardMapping +#endif +XGetKeyboardMapping: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1656 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1656] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 207 & 0xffff +#if 207 > 0xffff + movk ip0, 207 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetModifierMapping + .p2align 4 + .type XGetModifierMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetModifierMapping +#endif +XGetModifierMapping: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1664 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1664] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 208 & 0xffff +#if 208 > 0xffff + movk ip0, 208 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetMotionEvents + .p2align 4 + .type XGetMotionEvents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetMotionEvents +#endif +XGetMotionEvents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1672 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1672] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 209 & 0xffff +#if 209 > 0xffff + movk ip0, 209 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetNormalHints + .p2align 4 + .type XGetNormalHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetNormalHints +#endif +XGetNormalHints: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1680 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1680] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 210 & 0xffff +#if 210 > 0xffff + movk ip0, 210 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetOCValues + .p2align 4 + .type XGetOCValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetOCValues +#endif +XGetOCValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1688 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1688] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 211 & 0xffff +#if 211 > 0xffff + movk ip0, 211 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetOMValues + .p2align 4 + .type XGetOMValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetOMValues +#endif +XGetOMValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1696 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1696] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 212 & 0xffff +#if 212 > 0xffff + movk ip0, 212 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetPixel + .p2align 4 + .type XGetPixel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetPixel +#endif +XGetPixel: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1704 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1704] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 213 & 0xffff +#if 213 > 0xffff + movk ip0, 213 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetPointerControl + .p2align 4 + .type XGetPointerControl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetPointerControl +#endif +XGetPointerControl: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1712 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1712] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 214 & 0xffff +#if 214 > 0xffff + movk ip0, 214 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetPointerMapping + .p2align 4 + .type XGetPointerMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetPointerMapping +#endif +XGetPointerMapping: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1720 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1720] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 215 & 0xffff +#if 215 > 0xffff + movk ip0, 215 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetRGBColormaps + .p2align 4 + .type XGetRGBColormaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetRGBColormaps +#endif +XGetRGBColormaps: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1728 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1728] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 216 & 0xffff +#if 216 > 0xffff + movk ip0, 216 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetScreenSaver + .p2align 4 + .type XGetScreenSaver, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetScreenSaver +#endif +XGetScreenSaver: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1736 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1736] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 217 & 0xffff +#if 217 > 0xffff + movk ip0, 217 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetSelectionOwner + .p2align 4 + .type XGetSelectionOwner, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetSelectionOwner +#endif +XGetSelectionOwner: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1744 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1744] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 218 & 0xffff +#if 218 > 0xffff + movk ip0, 218 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetSizeHints + .p2align 4 + .type XGetSizeHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetSizeHints +#endif +XGetSizeHints: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1752 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1752] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 219 & 0xffff +#if 219 > 0xffff + movk ip0, 219 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetStandardColormap + .p2align 4 + .type XGetStandardColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetStandardColormap +#endif +XGetStandardColormap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1760 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1760] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 220 & 0xffff +#if 220 > 0xffff + movk ip0, 220 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetSubImage + .p2align 4 + .type XGetSubImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetSubImage +#endif +XGetSubImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1768 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1768] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 221 & 0xffff +#if 221 > 0xffff + movk ip0, 221 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetTextProperty + .p2align 4 + .type XGetTextProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetTextProperty +#endif +XGetTextProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1776 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1776] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 222 & 0xffff +#if 222 > 0xffff + movk ip0, 222 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetTransientForHint + .p2align 4 + .type XGetTransientForHint, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetTransientForHint +#endif +XGetTransientForHint: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1784 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1784] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 223 & 0xffff +#if 223 > 0xffff + movk ip0, 223 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetVisualInfo + .p2align 4 + .type XGetVisualInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetVisualInfo +#endif +XGetVisualInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1792 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1792] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 224 & 0xffff +#if 224 > 0xffff + movk ip0, 224 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWMClientMachine + .p2align 4 + .type XGetWMClientMachine, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWMClientMachine +#endif +XGetWMClientMachine: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1800 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1800] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 225 & 0xffff +#if 225 > 0xffff + movk ip0, 225 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWMColormapWindows + .p2align 4 + .type XGetWMColormapWindows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWMColormapWindows +#endif +XGetWMColormapWindows: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1808 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1808] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 226 & 0xffff +#if 226 > 0xffff + movk ip0, 226 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWMHints + .p2align 4 + .type XGetWMHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWMHints +#endif +XGetWMHints: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1816 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1816] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 227 & 0xffff +#if 227 > 0xffff + movk ip0, 227 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWMIconName + .p2align 4 + .type XGetWMIconName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWMIconName +#endif +XGetWMIconName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1824 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1824] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 228 & 0xffff +#if 228 > 0xffff + movk ip0, 228 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWMName + .p2align 4 + .type XGetWMName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWMName +#endif +XGetWMName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1832 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1832] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 229 & 0xffff +#if 229 > 0xffff + movk ip0, 229 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWMNormalHints + .p2align 4 + .type XGetWMNormalHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWMNormalHints +#endif +XGetWMNormalHints: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1840 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1840] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 230 & 0xffff +#if 230 > 0xffff + movk ip0, 230 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWMProtocols + .p2align 4 + .type XGetWMProtocols, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWMProtocols +#endif +XGetWMProtocols: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1848 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1848] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 231 & 0xffff +#if 231 > 0xffff + movk ip0, 231 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWMSizeHints + .p2align 4 + .type XGetWMSizeHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWMSizeHints +#endif +XGetWMSizeHints: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1856 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1856] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 232 & 0xffff +#if 232 > 0xffff + movk ip0, 232 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWindowAttributes + .p2align 4 + .type XGetWindowAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWindowAttributes +#endif +XGetWindowAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1864 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1864] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 233 & 0xffff +#if 233 > 0xffff + movk ip0, 233 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWindowProperty + .p2align 4 + .type XGetWindowProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWindowProperty +#endif +XGetWindowProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1872 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1872] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 234 & 0xffff +#if 234 > 0xffff + movk ip0, 234 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetZoomHints + .p2align 4 + .type XGetZoomHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetZoomHints +#endif +XGetZoomHints: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1880 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1880] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 235 & 0xffff +#if 235 > 0xffff + movk ip0, 235 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGrabButton + .p2align 4 + .type XGrabButton, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGrabButton +#endif +XGrabButton: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1888 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1888] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 236 & 0xffff +#if 236 > 0xffff + movk ip0, 236 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGrabKey + .p2align 4 + .type XGrabKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGrabKey +#endif +XGrabKey: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1896 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1896] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 237 & 0xffff +#if 237 > 0xffff + movk ip0, 237 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGrabKeyboard + .p2align 4 + .type XGrabKeyboard, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGrabKeyboard +#endif +XGrabKeyboard: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1904 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1904] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 238 & 0xffff +#if 238 > 0xffff + movk ip0, 238 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGrabPointer + .p2align 4 + .type XGrabPointer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGrabPointer +#endif +XGrabPointer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1912 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1912] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 239 & 0xffff +#if 239 > 0xffff + movk ip0, 239 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGrabServer + .p2align 4 + .type XGrabServer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGrabServer +#endif +XGrabServer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1920 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1920] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 240 & 0xffff +#if 240 > 0xffff + movk ip0, 240 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XHeightMMOfScreen + .p2align 4 + .type XHeightMMOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XHeightMMOfScreen +#endif +XHeightMMOfScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1928 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1928] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 241 & 0xffff +#if 241 > 0xffff + movk ip0, 241 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XHeightOfScreen + .p2align 4 + .type XHeightOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XHeightOfScreen +#endif +XHeightOfScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1936 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1936] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 242 & 0xffff +#if 242 > 0xffff + movk ip0, 242 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XIMOfIC + .p2align 4 + .type XIMOfIC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XIMOfIC +#endif +XIMOfIC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1944 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1944] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 243 & 0xffff +#if 243 > 0xffff + movk ip0, 243 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XIconifyWindow + .p2align 4 + .type XIconifyWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XIconifyWindow +#endif +XIconifyWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1952 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1952] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 244 & 0xffff +#if 244 > 0xffff + movk ip0, 244 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XIfEvent + .p2align 4 + .type XIfEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XIfEvent +#endif +XIfEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1960 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1960] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 245 & 0xffff +#if 245 > 0xffff + movk ip0, 245 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XImageByteOrder + .p2align 4 + .type XImageByteOrder, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XImageByteOrder +#endif +XImageByteOrder: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1968 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1968] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 246 & 0xffff +#if 246 > 0xffff + movk ip0, 246 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XInitExtension + .p2align 4 + .type XInitExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XInitExtension +#endif +XInitExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1976 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1976] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 247 & 0xffff +#if 247 > 0xffff + movk ip0, 247 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XInitImage + .p2align 4 + .type XInitImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XInitImage +#endif +XInitImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1984 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1984] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 248 & 0xffff +#if 248 > 0xffff + movk ip0, 248 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XInitThreads + .p2align 4 + .type XInitThreads, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XInitThreads +#endif +XInitThreads: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+1992 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+1992] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 249 & 0xffff +#if 249 > 0xffff + movk ip0, 249 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XInsertModifiermapEntry + .p2align 4 + .type XInsertModifiermapEntry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XInsertModifiermapEntry +#endif +XInsertModifiermapEntry: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2000 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2000] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 250 & 0xffff +#if 250 > 0xffff + movk ip0, 250 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XInstallColormap + .p2align 4 + .type XInstallColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XInstallColormap +#endif +XInstallColormap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2008 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2008] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 251 & 0xffff +#if 251 > 0xffff + movk ip0, 251 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XInternAtom + .p2align 4 + .type XInternAtom, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XInternAtom +#endif +XInternAtom: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2016 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2016] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 252 & 0xffff +#if 252 > 0xffff + movk ip0, 252 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XInternAtoms + .p2align 4 + .type XInternAtoms, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XInternAtoms +#endif +XInternAtoms: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2024 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2024] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 253 & 0xffff +#if 253 > 0xffff + movk ip0, 253 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XInternalConnectionNumbers + .p2align 4 + .type XInternalConnectionNumbers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XInternalConnectionNumbers +#endif +XInternalConnectionNumbers: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2032 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2032] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 254 & 0xffff +#if 254 > 0xffff + movk ip0, 254 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XIntersectRegion + .p2align 4 + .type XIntersectRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XIntersectRegion +#endif +XIntersectRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2040 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2040] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 255 & 0xffff +#if 255 > 0xffff + movk ip0, 255 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XKeycodeToKeysym + .p2align 4 + .type XKeycodeToKeysym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XKeycodeToKeysym +#endif +XKeycodeToKeysym: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2048 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2048] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 256 & 0xffff +#if 256 > 0xffff + movk ip0, 256 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XKeysymToKeycode + .p2align 4 + .type XKeysymToKeycode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XKeysymToKeycode +#endif +XKeysymToKeycode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2056 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2056] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 257 & 0xffff +#if 257 > 0xffff + movk ip0, 257 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XKeysymToString + .p2align 4 + .type XKeysymToString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XKeysymToString +#endif +XKeysymToString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2064 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2064] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 258 & 0xffff +#if 258 > 0xffff + movk ip0, 258 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XKillClient + .p2align 4 + .type XKillClient, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XKillClient +#endif +XKillClient: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2072 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2072] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 259 & 0xffff +#if 259 > 0xffff + movk ip0, 259 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLastKnownRequestProcessed + .p2align 4 + .type XLastKnownRequestProcessed, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLastKnownRequestProcessed +#endif +XLastKnownRequestProcessed: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2080 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2080] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 260 & 0xffff +#if 260 > 0xffff + movk ip0, 260 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XListDepths + .p2align 4 + .type XListDepths, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XListDepths +#endif +XListDepths: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2088 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2088] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 261 & 0xffff +#if 261 > 0xffff + movk ip0, 261 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XListExtensions + .p2align 4 + .type XListExtensions, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XListExtensions +#endif +XListExtensions: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2096 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2096] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 262 & 0xffff +#if 262 > 0xffff + movk ip0, 262 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XListFonts + .p2align 4 + .type XListFonts, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XListFonts +#endif +XListFonts: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2104 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 263 & 0xffff +#if 263 > 0xffff + movk ip0, 263 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XListFontsWithInfo + .p2align 4 + .type XListFontsWithInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XListFontsWithInfo +#endif +XListFontsWithInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2112 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 264 & 0xffff +#if 264 > 0xffff + movk ip0, 264 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XListHosts + .p2align 4 + .type XListHosts, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XListHosts +#endif +XListHosts: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2120 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 265 & 0xffff +#if 265 > 0xffff + movk ip0, 265 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XListInstalledColormaps + .p2align 4 + .type XListInstalledColormaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XListInstalledColormaps +#endif +XListInstalledColormaps: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2128 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 266 & 0xffff +#if 266 > 0xffff + movk ip0, 266 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XListPixmapFormats + .p2align 4 + .type XListPixmapFormats, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XListPixmapFormats +#endif +XListPixmapFormats: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2136 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 267 & 0xffff +#if 267 > 0xffff + movk ip0, 267 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XListProperties + .p2align 4 + .type XListProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XListProperties +#endif +XListProperties: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2144 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 268 & 0xffff +#if 268 > 0xffff + movk ip0, 268 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLoadFont + .p2align 4 + .type XLoadFont, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLoadFont +#endif +XLoadFont: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2152 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 269 & 0xffff +#if 269 > 0xffff + movk ip0, 269 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLoadQueryFont + .p2align 4 + .type XLoadQueryFont, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLoadQueryFont +#endif +XLoadQueryFont: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2160 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 270 & 0xffff +#if 270 > 0xffff + movk ip0, 270 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLocaleOfFontSet + .p2align 4 + .type XLocaleOfFontSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLocaleOfFontSet +#endif +XLocaleOfFontSet: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2168 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 271 & 0xffff +#if 271 > 0xffff + movk ip0, 271 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLocaleOfIM + .p2align 4 + .type XLocaleOfIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLocaleOfIM +#endif +XLocaleOfIM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2176 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 272 & 0xffff +#if 272 > 0xffff + movk ip0, 272 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLocaleOfOM + .p2align 4 + .type XLocaleOfOM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLocaleOfOM +#endif +XLocaleOfOM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2184 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 273 & 0xffff +#if 273 > 0xffff + movk ip0, 273 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLockDisplay + .p2align 4 + .type XLockDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLockDisplay +#endif +XLockDisplay: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2192 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 274 & 0xffff +#if 274 > 0xffff + movk ip0, 274 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLookupColor + .p2align 4 + .type XLookupColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLookupColor +#endif +XLookupColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2200 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 275 & 0xffff +#if 275 > 0xffff + movk ip0, 275 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLookupKeysym + .p2align 4 + .type XLookupKeysym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLookupKeysym +#endif +XLookupKeysym: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2208 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 276 & 0xffff +#if 276 > 0xffff + movk ip0, 276 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLookupString + .p2align 4 + .type XLookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLookupString +#endif +XLookupString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2216 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 277 & 0xffff +#if 277 > 0xffff + movk ip0, 277 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLowerWindow + .p2align 4 + .type XLowerWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLowerWindow +#endif +XLowerWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2224 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 278 & 0xffff +#if 278 > 0xffff + movk ip0, 278 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMapRaised + .p2align 4 + .type XMapRaised, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMapRaised +#endif +XMapRaised: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2232 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 279 & 0xffff +#if 279 > 0xffff + movk ip0, 279 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMapSubwindows + .p2align 4 + .type XMapSubwindows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMapSubwindows +#endif +XMapSubwindows: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2240 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 280 & 0xffff +#if 280 > 0xffff + movk ip0, 280 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMapWindow + .p2align 4 + .type XMapWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMapWindow +#endif +XMapWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2248 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 281 & 0xffff +#if 281 > 0xffff + movk ip0, 281 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMaskEvent + .p2align 4 + .type XMaskEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMaskEvent +#endif +XMaskEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2256 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 282 & 0xffff +#if 282 > 0xffff + movk ip0, 282 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMatchVisualInfo + .p2align 4 + .type XMatchVisualInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMatchVisualInfo +#endif +XMatchVisualInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2264 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 283 & 0xffff +#if 283 > 0xffff + movk ip0, 283 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMaxCmapsOfScreen + .p2align 4 + .type XMaxCmapsOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMaxCmapsOfScreen +#endif +XMaxCmapsOfScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2272 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 284 & 0xffff +#if 284 > 0xffff + movk ip0, 284 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMaxRequestSize + .p2align 4 + .type XMaxRequestSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMaxRequestSize +#endif +XMaxRequestSize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2280 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 285 & 0xffff +#if 285 > 0xffff + movk ip0, 285 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMinCmapsOfScreen + .p2align 4 + .type XMinCmapsOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMinCmapsOfScreen +#endif +XMinCmapsOfScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2288 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 286 & 0xffff +#if 286 > 0xffff + movk ip0, 286 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMoveResizeWindow + .p2align 4 + .type XMoveResizeWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMoveResizeWindow +#endif +XMoveResizeWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2296 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 287 & 0xffff +#if 287 > 0xffff + movk ip0, 287 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMoveWindow + .p2align 4 + .type XMoveWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMoveWindow +#endif +XMoveWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2304 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 288 & 0xffff +#if 288 > 0xffff + movk ip0, 288 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XNewModifiermap + .p2align 4 + .type XNewModifiermap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XNewModifiermap +#endif +XNewModifiermap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2312 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 289 & 0xffff +#if 289 > 0xffff + movk ip0, 289 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XNextEvent + .p2align 4 + .type XNextEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XNextEvent +#endif +XNextEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2320 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 290 & 0xffff +#if 290 > 0xffff + movk ip0, 290 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XNextRequest + .p2align 4 + .type XNextRequest, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XNextRequest +#endif +XNextRequest: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2328 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 291 & 0xffff +#if 291 > 0xffff + movk ip0, 291 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XNoOp + .p2align 4 + .type XNoOp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XNoOp +#endif +XNoOp: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2336 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 292 & 0xffff +#if 292 > 0xffff + movk ip0, 292 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XOMOfOC + .p2align 4 + .type XOMOfOC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XOMOfOC +#endif +XOMOfOC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2344 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 293 & 0xffff +#if 293 > 0xffff + movk ip0, 293 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XOffsetRegion + .p2align 4 + .type XOffsetRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XOffsetRegion +#endif +XOffsetRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2352 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 294 & 0xffff +#if 294 > 0xffff + movk ip0, 294 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XOpenDisplay + .p2align 4 + .type XOpenDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XOpenDisplay +#endif +XOpenDisplay: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2360 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 295 & 0xffff +#if 295 > 0xffff + movk ip0, 295 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XOpenIM + .p2align 4 + .type XOpenIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XOpenIM +#endif +XOpenIM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2368 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 296 & 0xffff +#if 296 > 0xffff + movk ip0, 296 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XOpenOM + .p2align 4 + .type XOpenOM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XOpenOM +#endif +XOpenOM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2376 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 297 & 0xffff +#if 297 > 0xffff + movk ip0, 297 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XParseColor + .p2align 4 + .type XParseColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XParseColor +#endif +XParseColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2384 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 298 & 0xffff +#if 298 > 0xffff + movk ip0, 298 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XParseGeometry + .p2align 4 + .type XParseGeometry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XParseGeometry +#endif +XParseGeometry: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2392 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 299 & 0xffff +#if 299 > 0xffff + movk ip0, 299 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XPeekEvent + .p2align 4 + .type XPeekEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XPeekEvent +#endif +XPeekEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2400 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 300 & 0xffff +#if 300 > 0xffff + movk ip0, 300 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XPeekIfEvent + .p2align 4 + .type XPeekIfEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XPeekIfEvent +#endif +XPeekIfEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2408 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 301 & 0xffff +#if 301 > 0xffff + movk ip0, 301 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XPending + .p2align 4 + .type XPending, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XPending +#endif +XPending: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2416 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 302 & 0xffff +#if 302 > 0xffff + movk ip0, 302 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XPlanesOfScreen + .p2align 4 + .type XPlanesOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XPlanesOfScreen +#endif +XPlanesOfScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2424 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 303 & 0xffff +#if 303 > 0xffff + movk ip0, 303 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XPointInRegion + .p2align 4 + .type XPointInRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XPointInRegion +#endif +XPointInRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2432 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 304 & 0xffff +#if 304 > 0xffff + movk ip0, 304 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XPolygonRegion + .p2align 4 + .type XPolygonRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XPolygonRegion +#endif +XPolygonRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2440 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 305 & 0xffff +#if 305 > 0xffff + movk ip0, 305 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XProcessInternalConnection + .p2align 4 + .type XProcessInternalConnection, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XProcessInternalConnection +#endif +XProcessInternalConnection: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2448 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 306 & 0xffff +#if 306 > 0xffff + movk ip0, 306 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XProtocolRevision + .p2align 4 + .type XProtocolRevision, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XProtocolRevision +#endif +XProtocolRevision: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2456 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2456] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 307 & 0xffff +#if 307 > 0xffff + movk ip0, 307 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XProtocolVersion + .p2align 4 + .type XProtocolVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XProtocolVersion +#endif +XProtocolVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2464 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2464] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 308 & 0xffff +#if 308 > 0xffff + movk ip0, 308 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XPutBackEvent + .p2align 4 + .type XPutBackEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XPutBackEvent +#endif +XPutBackEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2472 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2472] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 309 & 0xffff +#if 309 > 0xffff + movk ip0, 309 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XPutImage + .p2align 4 + .type XPutImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XPutImage +#endif +XPutImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2480 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2480] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 310 & 0xffff +#if 310 > 0xffff + movk ip0, 310 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XPutPixel + .p2align 4 + .type XPutPixel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XPutPixel +#endif +XPutPixel: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2488 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2488] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 311 & 0xffff +#if 311 > 0xffff + movk ip0, 311 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQLength + .p2align 4 + .type XQLength, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQLength +#endif +XQLength: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2496 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2496] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 312 & 0xffff +#if 312 > 0xffff + movk ip0, 312 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryBestCursor + .p2align 4 + .type XQueryBestCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryBestCursor +#endif +XQueryBestCursor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2504 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2504] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 313 & 0xffff +#if 313 > 0xffff + movk ip0, 313 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryBestSize + .p2align 4 + .type XQueryBestSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryBestSize +#endif +XQueryBestSize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2512 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2512] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 314 & 0xffff +#if 314 > 0xffff + movk ip0, 314 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryBestStipple + .p2align 4 + .type XQueryBestStipple, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryBestStipple +#endif +XQueryBestStipple: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2520 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2520] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 315 & 0xffff +#if 315 > 0xffff + movk ip0, 315 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryBestTile + .p2align 4 + .type XQueryBestTile, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryBestTile +#endif +XQueryBestTile: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2528 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2528] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 316 & 0xffff +#if 316 > 0xffff + movk ip0, 316 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryColor + .p2align 4 + .type XQueryColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryColor +#endif +XQueryColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2536 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2536] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 317 & 0xffff +#if 317 > 0xffff + movk ip0, 317 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryColors + .p2align 4 + .type XQueryColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryColors +#endif +XQueryColors: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2544 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2544] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 318 & 0xffff +#if 318 > 0xffff + movk ip0, 318 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryExtension + .p2align 4 + .type XQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryExtension +#endif +XQueryExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2552 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2552] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 319 & 0xffff +#if 319 > 0xffff + movk ip0, 319 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryFont + .p2align 4 + .type XQueryFont, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryFont +#endif +XQueryFont: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2560 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2560] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 320 & 0xffff +#if 320 > 0xffff + movk ip0, 320 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryKeymap + .p2align 4 + .type XQueryKeymap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryKeymap +#endif +XQueryKeymap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2568 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2568] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 321 & 0xffff +#if 321 > 0xffff + movk ip0, 321 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryPointer + .p2align 4 + .type XQueryPointer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryPointer +#endif +XQueryPointer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2576 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2576] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 322 & 0xffff +#if 322 > 0xffff + movk ip0, 322 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryTextExtents + .p2align 4 + .type XQueryTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryTextExtents +#endif +XQueryTextExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2584 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2584] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 323 & 0xffff +#if 323 > 0xffff + movk ip0, 323 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryTextExtents16 + .p2align 4 + .type XQueryTextExtents16, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryTextExtents16 +#endif +XQueryTextExtents16: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2592 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2592] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 324 & 0xffff +#if 324 > 0xffff + movk ip0, 324 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryTree + .p2align 4 + .type XQueryTree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryTree +#endif +XQueryTree: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2600 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2600] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 325 & 0xffff +#if 325 > 0xffff + movk ip0, 325 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRaiseWindow + .p2align 4 + .type XRaiseWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRaiseWindow +#endif +XRaiseWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2608 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2608] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 326 & 0xffff +#if 326 > 0xffff + movk ip0, 326 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XReadBitmapFile + .p2align 4 + .type XReadBitmapFile, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XReadBitmapFile +#endif +XReadBitmapFile: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2616 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2616] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 327 & 0xffff +#if 327 > 0xffff + movk ip0, 327 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XReadBitmapFileData + .p2align 4 + .type XReadBitmapFileData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XReadBitmapFileData +#endif +XReadBitmapFileData: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2624 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2624] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 328 & 0xffff +#if 328 > 0xffff + movk ip0, 328 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRebindKeysym + .p2align 4 + .type XRebindKeysym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRebindKeysym +#endif +XRebindKeysym: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2632 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2632] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 329 & 0xffff +#if 329 > 0xffff + movk ip0, 329 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRecolorCursor + .p2align 4 + .type XRecolorCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRecolorCursor +#endif +XRecolorCursor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2640 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2640] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 330 & 0xffff +#if 330 > 0xffff + movk ip0, 330 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XReconfigureWMWindow + .p2align 4 + .type XReconfigureWMWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XReconfigureWMWindow +#endif +XReconfigureWMWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2648 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2648] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 331 & 0xffff +#if 331 > 0xffff + movk ip0, 331 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRectInRegion + .p2align 4 + .type XRectInRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRectInRegion +#endif +XRectInRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2656 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2656] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 332 & 0xffff +#if 332 > 0xffff + movk ip0, 332 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRefreshKeyboardMapping + .p2align 4 + .type XRefreshKeyboardMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRefreshKeyboardMapping +#endif +XRefreshKeyboardMapping: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2664 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2664] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 333 & 0xffff +#if 333 > 0xffff + movk ip0, 333 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRegisterIMInstantiateCallback + .p2align 4 + .type XRegisterIMInstantiateCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRegisterIMInstantiateCallback +#endif +XRegisterIMInstantiateCallback: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2672 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2672] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 334 & 0xffff +#if 334 > 0xffff + movk ip0, 334 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRemoveConnectionWatch + .p2align 4 + .type XRemoveConnectionWatch, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRemoveConnectionWatch +#endif +XRemoveConnectionWatch: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2680 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2680] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 335 & 0xffff +#if 335 > 0xffff + movk ip0, 335 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRemoveFromSaveSet + .p2align 4 + .type XRemoveFromSaveSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRemoveFromSaveSet +#endif +XRemoveFromSaveSet: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2688 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2688] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 336 & 0xffff +#if 336 > 0xffff + movk ip0, 336 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRemoveHost + .p2align 4 + .type XRemoveHost, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRemoveHost +#endif +XRemoveHost: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2696 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2696] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 337 & 0xffff +#if 337 > 0xffff + movk ip0, 337 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRemoveHosts + .p2align 4 + .type XRemoveHosts, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRemoveHosts +#endif +XRemoveHosts: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2704 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2704] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 338 & 0xffff +#if 338 > 0xffff + movk ip0, 338 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XReparentWindow + .p2align 4 + .type XReparentWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XReparentWindow +#endif +XReparentWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2712 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2712] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 339 & 0xffff +#if 339 > 0xffff + movk ip0, 339 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XResetScreenSaver + .p2align 4 + .type XResetScreenSaver, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XResetScreenSaver +#endif +XResetScreenSaver: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2720 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2720] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 340 & 0xffff +#if 340 > 0xffff + movk ip0, 340 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XResizeWindow + .p2align 4 + .type XResizeWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XResizeWindow +#endif +XResizeWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2728 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2728] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 341 & 0xffff +#if 341 > 0xffff + movk ip0, 341 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XResourceManagerString + .p2align 4 + .type XResourceManagerString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XResourceManagerString +#endif +XResourceManagerString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2736 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2736] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 342 & 0xffff +#if 342 > 0xffff + movk ip0, 342 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRestackWindows + .p2align 4 + .type XRestackWindows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRestackWindows +#endif +XRestackWindows: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2744 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2744] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 343 & 0xffff +#if 343 > 0xffff + movk ip0, 343 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRootWindow + .p2align 4 + .type XRootWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRootWindow +#endif +XRootWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2752 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2752] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 344 & 0xffff +#if 344 > 0xffff + movk ip0, 344 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRootWindowOfScreen + .p2align 4 + .type XRootWindowOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRootWindowOfScreen +#endif +XRootWindowOfScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2760 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2760] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 345 & 0xffff +#if 345 > 0xffff + movk ip0, 345 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRotateBuffers + .p2align 4 + .type XRotateBuffers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRotateBuffers +#endif +XRotateBuffers: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2768 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2768] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 346 & 0xffff +#if 346 > 0xffff + movk ip0, 346 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRotateWindowProperties + .p2align 4 + .type XRotateWindowProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRotateWindowProperties +#endif +XRotateWindowProperties: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2776 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2776] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 347 & 0xffff +#if 347 > 0xffff + movk ip0, 347 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSaveContext + .p2align 4 + .type XSaveContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSaveContext +#endif +XSaveContext: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2784 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2784] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 348 & 0xffff +#if 348 > 0xffff + movk ip0, 348 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XScreenCount + .p2align 4 + .type XScreenCount, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XScreenCount +#endif +XScreenCount: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2792 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2792] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 349 & 0xffff +#if 349 > 0xffff + movk ip0, 349 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XScreenNumberOfScreen + .p2align 4 + .type XScreenNumberOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XScreenNumberOfScreen +#endif +XScreenNumberOfScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2800 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2800] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 350 & 0xffff +#if 350 > 0xffff + movk ip0, 350 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XScreenOfDisplay + .p2align 4 + .type XScreenOfDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XScreenOfDisplay +#endif +XScreenOfDisplay: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2808 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2808] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 351 & 0xffff +#if 351 > 0xffff + movk ip0, 351 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XScreenResourceString + .p2align 4 + .type XScreenResourceString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XScreenResourceString +#endif +XScreenResourceString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2816 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2816] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 352 & 0xffff +#if 352 > 0xffff + movk ip0, 352 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSelectInput + .p2align 4 + .type XSelectInput, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSelectInput +#endif +XSelectInput: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2824 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2824] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 353 & 0xffff +#if 353 > 0xffff + movk ip0, 353 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSendEvent + .p2align 4 + .type XSendEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSendEvent +#endif +XSendEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2832 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2832] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 354 & 0xffff +#if 354 > 0xffff + movk ip0, 354 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XServerVendor + .p2align 4 + .type XServerVendor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XServerVendor +#endif +XServerVendor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2840 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2840] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 355 & 0xffff +#if 355 > 0xffff + movk ip0, 355 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetAccessControl + .p2align 4 + .type XSetAccessControl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetAccessControl +#endif +XSetAccessControl: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2848 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2848] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 356 & 0xffff +#if 356 > 0xffff + movk ip0, 356 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetAfterFunction + .p2align 4 + .type XSetAfterFunction, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetAfterFunction +#endif +XSetAfterFunction: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2856 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2856] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 357 & 0xffff +#if 357 > 0xffff + movk ip0, 357 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetArcMode + .p2align 4 + .type XSetArcMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetArcMode +#endif +XSetArcMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2864 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2864] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 358 & 0xffff +#if 358 > 0xffff + movk ip0, 358 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetAuthorization + .p2align 4 + .type XSetAuthorization, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetAuthorization +#endif +XSetAuthorization: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2872 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2872] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 359 & 0xffff +#if 359 > 0xffff + movk ip0, 359 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetBackground + .p2align 4 + .type XSetBackground, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetBackground +#endif +XSetBackground: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2880 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2880] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 360 & 0xffff +#if 360 > 0xffff + movk ip0, 360 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetClassHint + .p2align 4 + .type XSetClassHint, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetClassHint +#endif +XSetClassHint: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2888 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2888] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 361 & 0xffff +#if 361 > 0xffff + movk ip0, 361 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetClipMask + .p2align 4 + .type XSetClipMask, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetClipMask +#endif +XSetClipMask: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2896 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2896] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 362 & 0xffff +#if 362 > 0xffff + movk ip0, 362 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetClipOrigin + .p2align 4 + .type XSetClipOrigin, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetClipOrigin +#endif +XSetClipOrigin: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2904 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2904] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 363 & 0xffff +#if 363 > 0xffff + movk ip0, 363 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetClipRectangles + .p2align 4 + .type XSetClipRectangles, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetClipRectangles +#endif +XSetClipRectangles: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2912 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2912] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 364 & 0xffff +#if 364 > 0xffff + movk ip0, 364 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetCloseDownMode + .p2align 4 + .type XSetCloseDownMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetCloseDownMode +#endif +XSetCloseDownMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2920 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2920] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 365 & 0xffff +#if 365 > 0xffff + movk ip0, 365 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetCommand + .p2align 4 + .type XSetCommand, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetCommand +#endif +XSetCommand: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2928 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2928] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 366 & 0xffff +#if 366 > 0xffff + movk ip0, 366 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetDashes + .p2align 4 + .type XSetDashes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetDashes +#endif +XSetDashes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2936 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2936] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 367 & 0xffff +#if 367 > 0xffff + movk ip0, 367 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetErrorHandler + .p2align 4 + .type XSetErrorHandler, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetErrorHandler +#endif +XSetErrorHandler: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2944 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2944] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 368 & 0xffff +#if 368 > 0xffff + movk ip0, 368 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetFillRule + .p2align 4 + .type XSetFillRule, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetFillRule +#endif +XSetFillRule: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2952 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2952] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 369 & 0xffff +#if 369 > 0xffff + movk ip0, 369 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetFillStyle + .p2align 4 + .type XSetFillStyle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetFillStyle +#endif +XSetFillStyle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2960 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2960] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 370 & 0xffff +#if 370 > 0xffff + movk ip0, 370 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetFont + .p2align 4 + .type XSetFont, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetFont +#endif +XSetFont: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2968 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2968] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 371 & 0xffff +#if 371 > 0xffff + movk ip0, 371 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetFontPath + .p2align 4 + .type XSetFontPath, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetFontPath +#endif +XSetFontPath: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2976 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2976] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 372 & 0xffff +#if 372 > 0xffff + movk ip0, 372 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetForeground + .p2align 4 + .type XSetForeground, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetForeground +#endif +XSetForeground: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2984 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2984] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 373 & 0xffff +#if 373 > 0xffff + movk ip0, 373 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetFunction + .p2align 4 + .type XSetFunction, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetFunction +#endif +XSetFunction: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+2992 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+2992] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 374 & 0xffff +#if 374 > 0xffff + movk ip0, 374 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetGraphicsExposures + .p2align 4 + .type XSetGraphicsExposures, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetGraphicsExposures +#endif +XSetGraphicsExposures: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3000 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3000] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 375 & 0xffff +#if 375 > 0xffff + movk ip0, 375 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetICFocus + .p2align 4 + .type XSetICFocus, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetICFocus +#endif +XSetICFocus: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3008 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3008] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 376 & 0xffff +#if 376 > 0xffff + movk ip0, 376 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetICValues + .p2align 4 + .type XSetICValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetICValues +#endif +XSetICValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3016 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3016] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 377 & 0xffff +#if 377 > 0xffff + movk ip0, 377 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetIMValues + .p2align 4 + .type XSetIMValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetIMValues +#endif +XSetIMValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3024 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3024] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 378 & 0xffff +#if 378 > 0xffff + movk ip0, 378 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetIOErrorExitHandler + .p2align 4 + .type XSetIOErrorExitHandler, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetIOErrorExitHandler +#endif +XSetIOErrorExitHandler: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3032 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3032] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 379 & 0xffff +#if 379 > 0xffff + movk ip0, 379 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetIOErrorHandler + .p2align 4 + .type XSetIOErrorHandler, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetIOErrorHandler +#endif +XSetIOErrorHandler: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3040 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3040] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 380 & 0xffff +#if 380 > 0xffff + movk ip0, 380 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetIconName + .p2align 4 + .type XSetIconName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetIconName +#endif +XSetIconName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3048 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3048] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 381 & 0xffff +#if 381 > 0xffff + movk ip0, 381 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetIconSizes + .p2align 4 + .type XSetIconSizes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetIconSizes +#endif +XSetIconSizes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3056 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3056] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 382 & 0xffff +#if 382 > 0xffff + movk ip0, 382 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetInputFocus + .p2align 4 + .type XSetInputFocus, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetInputFocus +#endif +XSetInputFocus: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3064 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3064] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 383 & 0xffff +#if 383 > 0xffff + movk ip0, 383 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetLineAttributes + .p2align 4 + .type XSetLineAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetLineAttributes +#endif +XSetLineAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3072 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3072] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 384 & 0xffff +#if 384 > 0xffff + movk ip0, 384 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetLocaleModifiers + .p2align 4 + .type XSetLocaleModifiers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetLocaleModifiers +#endif +XSetLocaleModifiers: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3080 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3080] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 385 & 0xffff +#if 385 > 0xffff + movk ip0, 385 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetModifierMapping + .p2align 4 + .type XSetModifierMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetModifierMapping +#endif +XSetModifierMapping: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3088 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3088] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 386 & 0xffff +#if 386 > 0xffff + movk ip0, 386 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetNormalHints + .p2align 4 + .type XSetNormalHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetNormalHints +#endif +XSetNormalHints: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3096 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3096] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 387 & 0xffff +#if 387 > 0xffff + movk ip0, 387 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetOCValues + .p2align 4 + .type XSetOCValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetOCValues +#endif +XSetOCValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3104 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 388 & 0xffff +#if 388 > 0xffff + movk ip0, 388 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetOMValues + .p2align 4 + .type XSetOMValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetOMValues +#endif +XSetOMValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3112 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 389 & 0xffff +#if 389 > 0xffff + movk ip0, 389 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetPlaneMask + .p2align 4 + .type XSetPlaneMask, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetPlaneMask +#endif +XSetPlaneMask: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3120 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 390 & 0xffff +#if 390 > 0xffff + movk ip0, 390 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetPointerMapping + .p2align 4 + .type XSetPointerMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetPointerMapping +#endif +XSetPointerMapping: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3128 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 391 & 0xffff +#if 391 > 0xffff + movk ip0, 391 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetRGBColormaps + .p2align 4 + .type XSetRGBColormaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetRGBColormaps +#endif +XSetRGBColormaps: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3136 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 392 & 0xffff +#if 392 > 0xffff + movk ip0, 392 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetRegion + .p2align 4 + .type XSetRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetRegion +#endif +XSetRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3144 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 393 & 0xffff +#if 393 > 0xffff + movk ip0, 393 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetScreenSaver + .p2align 4 + .type XSetScreenSaver, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetScreenSaver +#endif +XSetScreenSaver: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3152 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 394 & 0xffff +#if 394 > 0xffff + movk ip0, 394 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetSelectionOwner + .p2align 4 + .type XSetSelectionOwner, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetSelectionOwner +#endif +XSetSelectionOwner: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3160 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 395 & 0xffff +#if 395 > 0xffff + movk ip0, 395 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetSizeHints + .p2align 4 + .type XSetSizeHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetSizeHints +#endif +XSetSizeHints: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3168 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 396 & 0xffff +#if 396 > 0xffff + movk ip0, 396 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetStandardColormap + .p2align 4 + .type XSetStandardColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetStandardColormap +#endif +XSetStandardColormap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3176 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 397 & 0xffff +#if 397 > 0xffff + movk ip0, 397 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetStandardProperties + .p2align 4 + .type XSetStandardProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetStandardProperties +#endif +XSetStandardProperties: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3184 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 398 & 0xffff +#if 398 > 0xffff + movk ip0, 398 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetState + .p2align 4 + .type XSetState, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetState +#endif +XSetState: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3192 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 399 & 0xffff +#if 399 > 0xffff + movk ip0, 399 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetStipple + .p2align 4 + .type XSetStipple, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetStipple +#endif +XSetStipple: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3200 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 400 & 0xffff +#if 400 > 0xffff + movk ip0, 400 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetSubwindowMode + .p2align 4 + .type XSetSubwindowMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetSubwindowMode +#endif +XSetSubwindowMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3208 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 401 & 0xffff +#if 401 > 0xffff + movk ip0, 401 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetTSOrigin + .p2align 4 + .type XSetTSOrigin, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetTSOrigin +#endif +XSetTSOrigin: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3216 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 402 & 0xffff +#if 402 > 0xffff + movk ip0, 402 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetTextProperty + .p2align 4 + .type XSetTextProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetTextProperty +#endif +XSetTextProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3224 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 403 & 0xffff +#if 403 > 0xffff + movk ip0, 403 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetTile + .p2align 4 + .type XSetTile, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetTile +#endif +XSetTile: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3232 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 404 & 0xffff +#if 404 > 0xffff + movk ip0, 404 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetTransientForHint + .p2align 4 + .type XSetTransientForHint, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetTransientForHint +#endif +XSetTransientForHint: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3240 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 405 & 0xffff +#if 405 > 0xffff + movk ip0, 405 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWMClientMachine + .p2align 4 + .type XSetWMClientMachine, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWMClientMachine +#endif +XSetWMClientMachine: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3248 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 406 & 0xffff +#if 406 > 0xffff + movk ip0, 406 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWMColormapWindows + .p2align 4 + .type XSetWMColormapWindows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWMColormapWindows +#endif +XSetWMColormapWindows: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3256 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 407 & 0xffff +#if 407 > 0xffff + movk ip0, 407 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWMHints + .p2align 4 + .type XSetWMHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWMHints +#endif +XSetWMHints: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3264 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 408 & 0xffff +#if 408 > 0xffff + movk ip0, 408 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWMIconName + .p2align 4 + .type XSetWMIconName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWMIconName +#endif +XSetWMIconName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3272 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 409 & 0xffff +#if 409 > 0xffff + movk ip0, 409 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWMName + .p2align 4 + .type XSetWMName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWMName +#endif +XSetWMName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3280 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 410 & 0xffff +#if 410 > 0xffff + movk ip0, 410 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWMNormalHints + .p2align 4 + .type XSetWMNormalHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWMNormalHints +#endif +XSetWMNormalHints: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3288 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 411 & 0xffff +#if 411 > 0xffff + movk ip0, 411 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWMProperties + .p2align 4 + .type XSetWMProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWMProperties +#endif +XSetWMProperties: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3296 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 412 & 0xffff +#if 412 > 0xffff + movk ip0, 412 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWMProtocols + .p2align 4 + .type XSetWMProtocols, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWMProtocols +#endif +XSetWMProtocols: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3304 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 413 & 0xffff +#if 413 > 0xffff + movk ip0, 413 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWMSizeHints + .p2align 4 + .type XSetWMSizeHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWMSizeHints +#endif +XSetWMSizeHints: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3312 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 414 & 0xffff +#if 414 > 0xffff + movk ip0, 414 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWindowBackground + .p2align 4 + .type XSetWindowBackground, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWindowBackground +#endif +XSetWindowBackground: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3320 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 415 & 0xffff +#if 415 > 0xffff + movk ip0, 415 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWindowBackgroundPixmap + .p2align 4 + .type XSetWindowBackgroundPixmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWindowBackgroundPixmap +#endif +XSetWindowBackgroundPixmap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3328 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 416 & 0xffff +#if 416 > 0xffff + movk ip0, 416 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWindowBorder + .p2align 4 + .type XSetWindowBorder, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWindowBorder +#endif +XSetWindowBorder: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3336 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 417 & 0xffff +#if 417 > 0xffff + movk ip0, 417 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWindowBorderPixmap + .p2align 4 + .type XSetWindowBorderPixmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWindowBorderPixmap +#endif +XSetWindowBorderPixmap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3344 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 418 & 0xffff +#if 418 > 0xffff + movk ip0, 418 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWindowBorderWidth + .p2align 4 + .type XSetWindowBorderWidth, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWindowBorderWidth +#endif +XSetWindowBorderWidth: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3352 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 419 & 0xffff +#if 419 > 0xffff + movk ip0, 419 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWindowColormap + .p2align 4 + .type XSetWindowColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWindowColormap +#endif +XSetWindowColormap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3360 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 420 & 0xffff +#if 420 > 0xffff + movk ip0, 420 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetZoomHints + .p2align 4 + .type XSetZoomHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetZoomHints +#endif +XSetZoomHints: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3368 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 421 & 0xffff +#if 421 > 0xffff + movk ip0, 421 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShrinkRegion + .p2align 4 + .type XShrinkRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShrinkRegion +#endif +XShrinkRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3376 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 422 & 0xffff +#if 422 > 0xffff + movk ip0, 422 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XStoreBuffer + .p2align 4 + .type XStoreBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XStoreBuffer +#endif +XStoreBuffer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3384 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 423 & 0xffff +#if 423 > 0xffff + movk ip0, 423 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XStoreBytes + .p2align 4 + .type XStoreBytes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XStoreBytes +#endif +XStoreBytes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3392 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 424 & 0xffff +#if 424 > 0xffff + movk ip0, 424 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XStoreColor + .p2align 4 + .type XStoreColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XStoreColor +#endif +XStoreColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3400 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 425 & 0xffff +#if 425 > 0xffff + movk ip0, 425 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XStoreColors + .p2align 4 + .type XStoreColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XStoreColors +#endif +XStoreColors: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3408 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 426 & 0xffff +#if 426 > 0xffff + movk ip0, 426 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XStoreName + .p2align 4 + .type XStoreName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XStoreName +#endif +XStoreName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3416 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 427 & 0xffff +#if 427 > 0xffff + movk ip0, 427 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XStoreNamedColor + .p2align 4 + .type XStoreNamedColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XStoreNamedColor +#endif +XStoreNamedColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3424 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 428 & 0xffff +#if 428 > 0xffff + movk ip0, 428 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XStringListToTextProperty + .p2align 4 + .type XStringListToTextProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XStringListToTextProperty +#endif +XStringListToTextProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3432 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 429 & 0xffff +#if 429 > 0xffff + movk ip0, 429 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XStringToKeysym + .p2align 4 + .type XStringToKeysym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XStringToKeysym +#endif +XStringToKeysym: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3440 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 430 & 0xffff +#if 430 > 0xffff + movk ip0, 430 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSubImage + .p2align 4 + .type XSubImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSubImage +#endif +XSubImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3448 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 431 & 0xffff +#if 431 > 0xffff + movk ip0, 431 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSubtractRegion + .p2align 4 + .type XSubtractRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSubtractRegion +#endif +XSubtractRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3456 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3456] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 432 & 0xffff +#if 432 > 0xffff + movk ip0, 432 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSupportsLocale + .p2align 4 + .type XSupportsLocale, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSupportsLocale +#endif +XSupportsLocale: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3464 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3464] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 433 & 0xffff +#if 433 > 0xffff + movk ip0, 433 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSync + .p2align 4 + .type XSync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSync +#endif +XSync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3472 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3472] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 434 & 0xffff +#if 434 > 0xffff + movk ip0, 434 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSynchronize + .p2align 4 + .type XSynchronize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSynchronize +#endif +XSynchronize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3480 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3480] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 435 & 0xffff +#if 435 > 0xffff + movk ip0, 435 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTextExtents + .p2align 4 + .type XTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTextExtents +#endif +XTextExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3488 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3488] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 436 & 0xffff +#if 436 > 0xffff + movk ip0, 436 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTextExtents16 + .p2align 4 + .type XTextExtents16, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTextExtents16 +#endif +XTextExtents16: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3496 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3496] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 437 & 0xffff +#if 437 > 0xffff + movk ip0, 437 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTextPropertyToStringList + .p2align 4 + .type XTextPropertyToStringList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTextPropertyToStringList +#endif +XTextPropertyToStringList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3504 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3504] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 438 & 0xffff +#if 438 > 0xffff + movk ip0, 438 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTextWidth + .p2align 4 + .type XTextWidth, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTextWidth +#endif +XTextWidth: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3512 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3512] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 439 & 0xffff +#if 439 > 0xffff + movk ip0, 439 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTextWidth16 + .p2align 4 + .type XTextWidth16, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTextWidth16 +#endif +XTextWidth16: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3520 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3520] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 440 & 0xffff +#if 440 > 0xffff + movk ip0, 440 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTranslateCoordinates + .p2align 4 + .type XTranslateCoordinates, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTranslateCoordinates +#endif +XTranslateCoordinates: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3528 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3528] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 441 & 0xffff +#if 441 > 0xffff + movk ip0, 441 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUndefineCursor + .p2align 4 + .type XUndefineCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUndefineCursor +#endif +XUndefineCursor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3536 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3536] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 442 & 0xffff +#if 442 > 0xffff + movk ip0, 442 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUngrabButton + .p2align 4 + .type XUngrabButton, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUngrabButton +#endif +XUngrabButton: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3544 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3544] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 443 & 0xffff +#if 443 > 0xffff + movk ip0, 443 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUngrabKey + .p2align 4 + .type XUngrabKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUngrabKey +#endif +XUngrabKey: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3552 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3552] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 444 & 0xffff +#if 444 > 0xffff + movk ip0, 444 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUngrabKeyboard + .p2align 4 + .type XUngrabKeyboard, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUngrabKeyboard +#endif +XUngrabKeyboard: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3560 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3560] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 445 & 0xffff +#if 445 > 0xffff + movk ip0, 445 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUngrabPointer + .p2align 4 + .type XUngrabPointer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUngrabPointer +#endif +XUngrabPointer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3568 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3568] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 446 & 0xffff +#if 446 > 0xffff + movk ip0, 446 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUngrabServer + .p2align 4 + .type XUngrabServer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUngrabServer +#endif +XUngrabServer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3576 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3576] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 447 & 0xffff +#if 447 > 0xffff + movk ip0, 447 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUninstallColormap + .p2align 4 + .type XUninstallColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUninstallColormap +#endif +XUninstallColormap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3584 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3584] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 448 & 0xffff +#if 448 > 0xffff + movk ip0, 448 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUnionRectWithRegion + .p2align 4 + .type XUnionRectWithRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUnionRectWithRegion +#endif +XUnionRectWithRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3592 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3592] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 449 & 0xffff +#if 449 > 0xffff + movk ip0, 449 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUnionRegion + .p2align 4 + .type XUnionRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUnionRegion +#endif +XUnionRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3600 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3600] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 450 & 0xffff +#if 450 > 0xffff + movk ip0, 450 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUnloadFont + .p2align 4 + .type XUnloadFont, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUnloadFont +#endif +XUnloadFont: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3608 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3608] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 451 & 0xffff +#if 451 > 0xffff + movk ip0, 451 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUnlockDisplay + .p2align 4 + .type XUnlockDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUnlockDisplay +#endif +XUnlockDisplay: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3616 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3616] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 452 & 0xffff +#if 452 > 0xffff + movk ip0, 452 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUnmapSubwindows + .p2align 4 + .type XUnmapSubwindows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUnmapSubwindows +#endif +XUnmapSubwindows: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3624 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3624] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 453 & 0xffff +#if 453 > 0xffff + movk ip0, 453 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUnmapWindow + .p2align 4 + .type XUnmapWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUnmapWindow +#endif +XUnmapWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3632 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3632] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 454 & 0xffff +#if 454 > 0xffff + movk ip0, 454 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUnregisterIMInstantiateCallback + .p2align 4 + .type XUnregisterIMInstantiateCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUnregisterIMInstantiateCallback +#endif +XUnregisterIMInstantiateCallback: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3640 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3640] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 455 & 0xffff +#if 455 > 0xffff + movk ip0, 455 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUnsetICFocus + .p2align 4 + .type XUnsetICFocus, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUnsetICFocus +#endif +XUnsetICFocus: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3648 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3648] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 456 & 0xffff +#if 456 > 0xffff + movk ip0, 456 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XVaCreateNestedList + .p2align 4 + .type XVaCreateNestedList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XVaCreateNestedList +#endif +XVaCreateNestedList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3656 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3656] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 457 & 0xffff +#if 457 > 0xffff + movk ip0, 457 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XVendorRelease + .p2align 4 + .type XVendorRelease, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XVendorRelease +#endif +XVendorRelease: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3664 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3664] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 458 & 0xffff +#if 458 > 0xffff + movk ip0, 458 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XVisualIDFromVisual + .p2align 4 + .type XVisualIDFromVisual, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XVisualIDFromVisual +#endif +XVisualIDFromVisual: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3672 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3672] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 459 & 0xffff +#if 459 > 0xffff + movk ip0, 459 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XWMGeometry + .p2align 4 + .type XWMGeometry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XWMGeometry +#endif +XWMGeometry: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3680 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3680] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 460 & 0xffff +#if 460 > 0xffff + movk ip0, 460 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XWarpPointer + .p2align 4 + .type XWarpPointer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XWarpPointer +#endif +XWarpPointer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3688 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3688] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 461 & 0xffff +#if 461 > 0xffff + movk ip0, 461 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XWhitePixel + .p2align 4 + .type XWhitePixel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XWhitePixel +#endif +XWhitePixel: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3696 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3696] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 462 & 0xffff +#if 462 > 0xffff + movk ip0, 462 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XWhitePixelOfScreen + .p2align 4 + .type XWhitePixelOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XWhitePixelOfScreen +#endif +XWhitePixelOfScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3704 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3704] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 463 & 0xffff +#if 463 > 0xffff + movk ip0, 463 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XWidthMMOfScreen + .p2align 4 + .type XWidthMMOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XWidthMMOfScreen +#endif +XWidthMMOfScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3712 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3712] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 464 & 0xffff +#if 464 > 0xffff + movk ip0, 464 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XWidthOfScreen + .p2align 4 + .type XWidthOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XWidthOfScreen +#endif +XWidthOfScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3720 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3720] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 465 & 0xffff +#if 465 > 0xffff + movk ip0, 465 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XWindowEvent + .p2align 4 + .type XWindowEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XWindowEvent +#endif +XWindowEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3728 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3728] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 466 & 0xffff +#if 466 > 0xffff + movk ip0, 466 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XWithdrawWindow + .p2align 4 + .type XWithdrawWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XWithdrawWindow +#endif +XWithdrawWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3736 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3736] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 467 & 0xffff +#if 467 > 0xffff + movk ip0, 467 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XWriteBitmapFile + .p2align 4 + .type XWriteBitmapFile, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XWriteBitmapFile +#endif +XWriteBitmapFile: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3744 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3744] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 468 & 0xffff +#if 468 > 0xffff + movk ip0, 468 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XXorRegion + .p2align 4 + .type XXorRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XXorRegion +#endif +XXorRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3752 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3752] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 469 & 0xffff +#if 469 > 0xffff + movk ip0, 469 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsAddColorSpace + .p2align 4 + .type XcmsAddColorSpace, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsAddColorSpace +#endif +XcmsAddColorSpace: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3760 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3760] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 470 & 0xffff +#if 470 > 0xffff + movk ip0, 470 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsAddFunctionSet + .p2align 4 + .type XcmsAddFunctionSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsAddFunctionSet +#endif +XcmsAddFunctionSet: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3768 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3768] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 471 & 0xffff +#if 471 > 0xffff + movk ip0, 471 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsAllocColor + .p2align 4 + .type XcmsAllocColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsAllocColor +#endif +XcmsAllocColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3776 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3776] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 472 & 0xffff +#if 472 > 0xffff + movk ip0, 472 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsAllocNamedColor + .p2align 4 + .type XcmsAllocNamedColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsAllocNamedColor +#endif +XcmsAllocNamedColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3784 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3784] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 473 & 0xffff +#if 473 > 0xffff + movk ip0, 473 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCCCOfColormap + .p2align 4 + .type XcmsCCCOfColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCCCOfColormap +#endif +XcmsCCCOfColormap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3792 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3792] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 474 & 0xffff +#if 474 > 0xffff + movk ip0, 474 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELabClipL + .p2align 4 + .type XcmsCIELabClipL, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELabClipL +#endif +XcmsCIELabClipL: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3800 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3800] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 475 & 0xffff +#if 475 > 0xffff + movk ip0, 475 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELabClipLab + .p2align 4 + .type XcmsCIELabClipLab, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELabClipLab +#endif +XcmsCIELabClipLab: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3808 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3808] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 476 & 0xffff +#if 476 > 0xffff + movk ip0, 476 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELabClipab + .p2align 4 + .type XcmsCIELabClipab, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELabClipab +#endif +XcmsCIELabClipab: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3816 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3816] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 477 & 0xffff +#if 477 > 0xffff + movk ip0, 477 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELabQueryMaxC + .p2align 4 + .type XcmsCIELabQueryMaxC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELabQueryMaxC +#endif +XcmsCIELabQueryMaxC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3824 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3824] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 478 & 0xffff +#if 478 > 0xffff + movk ip0, 478 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELabQueryMaxL + .p2align 4 + .type XcmsCIELabQueryMaxL, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELabQueryMaxL +#endif +XcmsCIELabQueryMaxL: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3832 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3832] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 479 & 0xffff +#if 479 > 0xffff + movk ip0, 479 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELabQueryMaxLC + .p2align 4 + .type XcmsCIELabQueryMaxLC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELabQueryMaxLC +#endif +XcmsCIELabQueryMaxLC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3840 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3840] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 480 & 0xffff +#if 480 > 0xffff + movk ip0, 480 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELabQueryMinL + .p2align 4 + .type XcmsCIELabQueryMinL, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELabQueryMinL +#endif +XcmsCIELabQueryMinL: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3848 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3848] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 481 & 0xffff +#if 481 > 0xffff + movk ip0, 481 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELabToCIEXYZ + .p2align 4 + .type XcmsCIELabToCIEXYZ, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELabToCIEXYZ +#endif +XcmsCIELabToCIEXYZ: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3856 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3856] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 482 & 0xffff +#if 482 > 0xffff + movk ip0, 482 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELabWhiteShiftColors + .p2align 4 + .type XcmsCIELabWhiteShiftColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELabWhiteShiftColors +#endif +XcmsCIELabWhiteShiftColors: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3864 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3864] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 483 & 0xffff +#if 483 > 0xffff + movk ip0, 483 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELuvClipL + .p2align 4 + .type XcmsCIELuvClipL, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELuvClipL +#endif +XcmsCIELuvClipL: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3872 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3872] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 484 & 0xffff +#if 484 > 0xffff + movk ip0, 484 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELuvClipLuv + .p2align 4 + .type XcmsCIELuvClipLuv, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELuvClipLuv +#endif +XcmsCIELuvClipLuv: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3880 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3880] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 485 & 0xffff +#if 485 > 0xffff + movk ip0, 485 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELuvClipuv + .p2align 4 + .type XcmsCIELuvClipuv, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELuvClipuv +#endif +XcmsCIELuvClipuv: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3888 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3888] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 486 & 0xffff +#if 486 > 0xffff + movk ip0, 486 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELuvQueryMaxC + .p2align 4 + .type XcmsCIELuvQueryMaxC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELuvQueryMaxC +#endif +XcmsCIELuvQueryMaxC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3896 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3896] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 487 & 0xffff +#if 487 > 0xffff + movk ip0, 487 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELuvQueryMaxL + .p2align 4 + .type XcmsCIELuvQueryMaxL, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELuvQueryMaxL +#endif +XcmsCIELuvQueryMaxL: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3904 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3904] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 488 & 0xffff +#if 488 > 0xffff + movk ip0, 488 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELuvQueryMaxLC + .p2align 4 + .type XcmsCIELuvQueryMaxLC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELuvQueryMaxLC +#endif +XcmsCIELuvQueryMaxLC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3912 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3912] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 489 & 0xffff +#if 489 > 0xffff + movk ip0, 489 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELuvQueryMinL + .p2align 4 + .type XcmsCIELuvQueryMinL, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELuvQueryMinL +#endif +XcmsCIELuvQueryMinL: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3920 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3920] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 490 & 0xffff +#if 490 > 0xffff + movk ip0, 490 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELuvToCIEuvY + .p2align 4 + .type XcmsCIELuvToCIEuvY, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELuvToCIEuvY +#endif +XcmsCIELuvToCIEuvY: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3928 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3928] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 491 & 0xffff +#if 491 > 0xffff + movk ip0, 491 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELuvWhiteShiftColors + .p2align 4 + .type XcmsCIELuvWhiteShiftColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELuvWhiteShiftColors +#endif +XcmsCIELuvWhiteShiftColors: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3936 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3936] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 492 & 0xffff +#if 492 > 0xffff + movk ip0, 492 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIEXYZToCIELab + .p2align 4 + .type XcmsCIEXYZToCIELab, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIEXYZToCIELab +#endif +XcmsCIEXYZToCIELab: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3944 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3944] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 493 & 0xffff +#if 493 > 0xffff + movk ip0, 493 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIEXYZToCIEuvY + .p2align 4 + .type XcmsCIEXYZToCIEuvY, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIEXYZToCIEuvY +#endif +XcmsCIEXYZToCIEuvY: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3952 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3952] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 494 & 0xffff +#if 494 > 0xffff + movk ip0, 494 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIEXYZToCIExyY + .p2align 4 + .type XcmsCIEXYZToCIExyY, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIEXYZToCIExyY +#endif +XcmsCIEXYZToCIExyY: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3960 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3960] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 495 & 0xffff +#if 495 > 0xffff + movk ip0, 495 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIEXYZToRGBi + .p2align 4 + .type XcmsCIEXYZToRGBi, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIEXYZToRGBi +#endif +XcmsCIEXYZToRGBi: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3968 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3968] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 496 & 0xffff +#if 496 > 0xffff + movk ip0, 496 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIEuvYToCIELuv + .p2align 4 + .type XcmsCIEuvYToCIELuv, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIEuvYToCIELuv +#endif +XcmsCIEuvYToCIELuv: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3976 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3976] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 497 & 0xffff +#if 497 > 0xffff + movk ip0, 497 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIEuvYToCIEXYZ + .p2align 4 + .type XcmsCIEuvYToCIEXYZ, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIEuvYToCIEXYZ +#endif +XcmsCIEuvYToCIEXYZ: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3984 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3984] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 498 & 0xffff +#if 498 > 0xffff + movk ip0, 498 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIEuvYToTekHVC + .p2align 4 + .type XcmsCIEuvYToTekHVC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIEuvYToTekHVC +#endif +XcmsCIEuvYToTekHVC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+3992 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+3992] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 499 & 0xffff +#if 499 > 0xffff + movk ip0, 499 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIExyYToCIEXYZ + .p2align 4 + .type XcmsCIExyYToCIEXYZ, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIExyYToCIEXYZ +#endif +XcmsCIExyYToCIEXYZ: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4000 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4000] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 500 & 0xffff +#if 500 > 0xffff + movk ip0, 500 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsClientWhitePointOfCCC + .p2align 4 + .type XcmsClientWhitePointOfCCC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsClientWhitePointOfCCC +#endif +XcmsClientWhitePointOfCCC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4008 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4008] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 501 & 0xffff +#if 501 > 0xffff + movk ip0, 501 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsConvertColors + .p2align 4 + .type XcmsConvertColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsConvertColors +#endif +XcmsConvertColors: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4016 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4016] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 502 & 0xffff +#if 502 > 0xffff + movk ip0, 502 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCreateCCC + .p2align 4 + .type XcmsCreateCCC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCreateCCC +#endif +XcmsCreateCCC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4024 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4024] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 503 & 0xffff +#if 503 > 0xffff + movk ip0, 503 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsDefaultCCC + .p2align 4 + .type XcmsDefaultCCC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsDefaultCCC +#endif +XcmsDefaultCCC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4032 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4032] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 504 & 0xffff +#if 504 > 0xffff + movk ip0, 504 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsDisplayOfCCC + .p2align 4 + .type XcmsDisplayOfCCC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsDisplayOfCCC +#endif +XcmsDisplayOfCCC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4040 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4040] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 505 & 0xffff +#if 505 > 0xffff + movk ip0, 505 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsFormatOfPrefix + .p2align 4 + .type XcmsFormatOfPrefix, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsFormatOfPrefix +#endif +XcmsFormatOfPrefix: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4048 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4048] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 506 & 0xffff +#if 506 > 0xffff + movk ip0, 506 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsFreeCCC + .p2align 4 + .type XcmsFreeCCC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsFreeCCC +#endif +XcmsFreeCCC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4056 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4056] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 507 & 0xffff +#if 507 > 0xffff + movk ip0, 507 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsLookupColor + .p2align 4 + .type XcmsLookupColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsLookupColor +#endif +XcmsLookupColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4064 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4064] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 508 & 0xffff +#if 508 > 0xffff + movk ip0, 508 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsPrefixOfFormat + .p2align 4 + .type XcmsPrefixOfFormat, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsPrefixOfFormat +#endif +XcmsPrefixOfFormat: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4072 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4072] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 509 & 0xffff +#if 509 > 0xffff + movk ip0, 509 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsQueryBlack + .p2align 4 + .type XcmsQueryBlack, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsQueryBlack +#endif +XcmsQueryBlack: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4080 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4080] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 510 & 0xffff +#if 510 > 0xffff + movk ip0, 510 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsQueryBlue + .p2align 4 + .type XcmsQueryBlue, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsQueryBlue +#endif +XcmsQueryBlue: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4088 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4088] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 511 & 0xffff +#if 511 > 0xffff + movk ip0, 511 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsQueryColor + .p2align 4 + .type XcmsQueryColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsQueryColor +#endif +XcmsQueryColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4096 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4096] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 512 & 0xffff +#if 512 > 0xffff + movk ip0, 512 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsQueryColors + .p2align 4 + .type XcmsQueryColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsQueryColors +#endif +XcmsQueryColors: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4104 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 513 & 0xffff +#if 513 > 0xffff + movk ip0, 513 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsQueryGreen + .p2align 4 + .type XcmsQueryGreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsQueryGreen +#endif +XcmsQueryGreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4112 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 514 & 0xffff +#if 514 > 0xffff + movk ip0, 514 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsQueryRed + .p2align 4 + .type XcmsQueryRed, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsQueryRed +#endif +XcmsQueryRed: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4120 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 515 & 0xffff +#if 515 > 0xffff + movk ip0, 515 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsQueryWhite + .p2align 4 + .type XcmsQueryWhite, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsQueryWhite +#endif +XcmsQueryWhite: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4128 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 516 & 0xffff +#if 516 > 0xffff + movk ip0, 516 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsRGBToRGBi + .p2align 4 + .type XcmsRGBToRGBi, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsRGBToRGBi +#endif +XcmsRGBToRGBi: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4136 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 517 & 0xffff +#if 517 > 0xffff + movk ip0, 517 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsRGBiToCIEXYZ + .p2align 4 + .type XcmsRGBiToCIEXYZ, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsRGBiToCIEXYZ +#endif +XcmsRGBiToCIEXYZ: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4144 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 518 & 0xffff +#if 518 > 0xffff + movk ip0, 518 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsRGBiToRGB + .p2align 4 + .type XcmsRGBiToRGB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsRGBiToRGB +#endif +XcmsRGBiToRGB: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4152 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 519 & 0xffff +#if 519 > 0xffff + movk ip0, 519 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsScreenNumberOfCCC + .p2align 4 + .type XcmsScreenNumberOfCCC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsScreenNumberOfCCC +#endif +XcmsScreenNumberOfCCC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4160 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 520 & 0xffff +#if 520 > 0xffff + movk ip0, 520 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsScreenWhitePointOfCCC + .p2align 4 + .type XcmsScreenWhitePointOfCCC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsScreenWhitePointOfCCC +#endif +XcmsScreenWhitePointOfCCC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4168 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 521 & 0xffff +#if 521 > 0xffff + movk ip0, 521 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsSetCCCOfColormap + .p2align 4 + .type XcmsSetCCCOfColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsSetCCCOfColormap +#endif +XcmsSetCCCOfColormap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4176 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 522 & 0xffff +#if 522 > 0xffff + movk ip0, 522 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsSetCompressionProc + .p2align 4 + .type XcmsSetCompressionProc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsSetCompressionProc +#endif +XcmsSetCompressionProc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4184 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 523 & 0xffff +#if 523 > 0xffff + movk ip0, 523 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsSetWhiteAdjustProc + .p2align 4 + .type XcmsSetWhiteAdjustProc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsSetWhiteAdjustProc +#endif +XcmsSetWhiteAdjustProc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4192 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 524 & 0xffff +#if 524 > 0xffff + movk ip0, 524 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsSetWhitePoint + .p2align 4 + .type XcmsSetWhitePoint, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsSetWhitePoint +#endif +XcmsSetWhitePoint: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4200 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 525 & 0xffff +#if 525 > 0xffff + movk ip0, 525 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsStoreColor + .p2align 4 + .type XcmsStoreColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsStoreColor +#endif +XcmsStoreColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4208 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 526 & 0xffff +#if 526 > 0xffff + movk ip0, 526 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsStoreColors + .p2align 4 + .type XcmsStoreColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsStoreColors +#endif +XcmsStoreColors: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4216 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 527 & 0xffff +#if 527 > 0xffff + movk ip0, 527 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCClipC + .p2align 4 + .type XcmsTekHVCClipC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCClipC +#endif +XcmsTekHVCClipC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4224 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 528 & 0xffff +#if 528 > 0xffff + movk ip0, 528 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCClipV + .p2align 4 + .type XcmsTekHVCClipV, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCClipV +#endif +XcmsTekHVCClipV: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4232 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 529 & 0xffff +#if 529 > 0xffff + movk ip0, 529 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCClipVC + .p2align 4 + .type XcmsTekHVCClipVC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCClipVC +#endif +XcmsTekHVCClipVC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4240 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 530 & 0xffff +#if 530 > 0xffff + movk ip0, 530 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCQueryMaxC + .p2align 4 + .type XcmsTekHVCQueryMaxC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCQueryMaxC +#endif +XcmsTekHVCQueryMaxC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4248 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 531 & 0xffff +#if 531 > 0xffff + movk ip0, 531 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCQueryMaxV + .p2align 4 + .type XcmsTekHVCQueryMaxV, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCQueryMaxV +#endif +XcmsTekHVCQueryMaxV: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4256 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 532 & 0xffff +#if 532 > 0xffff + movk ip0, 532 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCQueryMaxVC + .p2align 4 + .type XcmsTekHVCQueryMaxVC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCQueryMaxVC +#endif +XcmsTekHVCQueryMaxVC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4264 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 533 & 0xffff +#if 533 > 0xffff + movk ip0, 533 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCQueryMaxVSamples + .p2align 4 + .type XcmsTekHVCQueryMaxVSamples, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCQueryMaxVSamples +#endif +XcmsTekHVCQueryMaxVSamples: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4272 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 534 & 0xffff +#if 534 > 0xffff + movk ip0, 534 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCQueryMinV + .p2align 4 + .type XcmsTekHVCQueryMinV, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCQueryMinV +#endif +XcmsTekHVCQueryMinV: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4280 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 535 & 0xffff +#if 535 > 0xffff + movk ip0, 535 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCToCIEuvY + .p2align 4 + .type XcmsTekHVCToCIEuvY, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCToCIEuvY +#endif +XcmsTekHVCToCIEuvY: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4288 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 536 & 0xffff +#if 536 > 0xffff + movk ip0, 536 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCWhiteShiftColors + .p2align 4 + .type XcmsTekHVCWhiteShiftColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCWhiteShiftColors +#endif +XcmsTekHVCWhiteShiftColors: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4296 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 537 & 0xffff +#if 537 > 0xffff + movk ip0, 537 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsVisualOfCCC + .p2align 4 + .type XcmsVisualOfCCC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsVisualOfCCC +#endif +XcmsVisualOfCCC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4304 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 538 & 0xffff +#if 538 > 0xffff + movk ip0, 538 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddDeviceLedInfo + .p2align 4 + .type XkbAddDeviceLedInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddDeviceLedInfo +#endif +XkbAddDeviceLedInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4312 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 539 & 0xffff +#if 539 > 0xffff + movk ip0, 539 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomColor + .p2align 4 + .type XkbAddGeomColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomColor +#endif +XkbAddGeomColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4320 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 540 & 0xffff +#if 540 > 0xffff + movk ip0, 540 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomDoodad + .p2align 4 + .type XkbAddGeomDoodad, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomDoodad +#endif +XkbAddGeomDoodad: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4328 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 541 & 0xffff +#if 541 > 0xffff + movk ip0, 541 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomKey + .p2align 4 + .type XkbAddGeomKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomKey +#endif +XkbAddGeomKey: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4336 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 542 & 0xffff +#if 542 > 0xffff + movk ip0, 542 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomKeyAlias + .p2align 4 + .type XkbAddGeomKeyAlias, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomKeyAlias +#endif +XkbAddGeomKeyAlias: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4344 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 543 & 0xffff +#if 543 > 0xffff + movk ip0, 543 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomOutline + .p2align 4 + .type XkbAddGeomOutline, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomOutline +#endif +XkbAddGeomOutline: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4352 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 544 & 0xffff +#if 544 > 0xffff + movk ip0, 544 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomOverlay + .p2align 4 + .type XkbAddGeomOverlay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomOverlay +#endif +XkbAddGeomOverlay: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4360 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 545 & 0xffff +#if 545 > 0xffff + movk ip0, 545 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomOverlayKey + .p2align 4 + .type XkbAddGeomOverlayKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomOverlayKey +#endif +XkbAddGeomOverlayKey: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4368 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 546 & 0xffff +#if 546 > 0xffff + movk ip0, 546 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomOverlayRow + .p2align 4 + .type XkbAddGeomOverlayRow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomOverlayRow +#endif +XkbAddGeomOverlayRow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4376 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 547 & 0xffff +#if 547 > 0xffff + movk ip0, 547 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomProperty + .p2align 4 + .type XkbAddGeomProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomProperty +#endif +XkbAddGeomProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4384 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 548 & 0xffff +#if 548 > 0xffff + movk ip0, 548 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomRow + .p2align 4 + .type XkbAddGeomRow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomRow +#endif +XkbAddGeomRow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4392 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 549 & 0xffff +#if 549 > 0xffff + movk ip0, 549 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomSection + .p2align 4 + .type XkbAddGeomSection, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomSection +#endif +XkbAddGeomSection: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4400 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 550 & 0xffff +#if 550 > 0xffff + movk ip0, 550 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomShape + .p2align 4 + .type XkbAddGeomShape, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomShape +#endif +XkbAddGeomShape: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4408 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 551 & 0xffff +#if 551 > 0xffff + movk ip0, 551 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddKeyType + .p2align 4 + .type XkbAddKeyType, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddKeyType +#endif +XkbAddKeyType: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4416 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 552 & 0xffff +#if 552 > 0xffff + movk ip0, 552 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocClientMap + .p2align 4 + .type XkbAllocClientMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocClientMap +#endif +XkbAllocClientMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4424 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 553 & 0xffff +#if 553 > 0xffff + movk ip0, 553 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocCompatMap + .p2align 4 + .type XkbAllocCompatMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocCompatMap +#endif +XkbAllocCompatMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4432 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 554 & 0xffff +#if 554 > 0xffff + movk ip0, 554 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocControls + .p2align 4 + .type XkbAllocControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocControls +#endif +XkbAllocControls: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4440 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 555 & 0xffff +#if 555 > 0xffff + movk ip0, 555 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocDeviceInfo + .p2align 4 + .type XkbAllocDeviceInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocDeviceInfo +#endif +XkbAllocDeviceInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4448 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 556 & 0xffff +#if 556 > 0xffff + movk ip0, 556 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomColors + .p2align 4 + .type XkbAllocGeomColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomColors +#endif +XkbAllocGeomColors: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4456 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4456] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 557 & 0xffff +#if 557 > 0xffff + movk ip0, 557 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomDoodads + .p2align 4 + .type XkbAllocGeomDoodads, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomDoodads +#endif +XkbAllocGeomDoodads: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4464 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4464] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 558 & 0xffff +#if 558 > 0xffff + movk ip0, 558 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomKeyAliases + .p2align 4 + .type XkbAllocGeomKeyAliases, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomKeyAliases +#endif +XkbAllocGeomKeyAliases: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4472 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4472] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 559 & 0xffff +#if 559 > 0xffff + movk ip0, 559 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomKeys + .p2align 4 + .type XkbAllocGeomKeys, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomKeys +#endif +XkbAllocGeomKeys: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4480 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4480] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 560 & 0xffff +#if 560 > 0xffff + movk ip0, 560 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomOutlines + .p2align 4 + .type XkbAllocGeomOutlines, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomOutlines +#endif +XkbAllocGeomOutlines: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4488 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4488] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 561 & 0xffff +#if 561 > 0xffff + movk ip0, 561 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomOverlayKeys + .p2align 4 + .type XkbAllocGeomOverlayKeys, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomOverlayKeys +#endif +XkbAllocGeomOverlayKeys: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4496 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4496] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 562 & 0xffff +#if 562 > 0xffff + movk ip0, 562 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomOverlayRows + .p2align 4 + .type XkbAllocGeomOverlayRows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomOverlayRows +#endif +XkbAllocGeomOverlayRows: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4504 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4504] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 563 & 0xffff +#if 563 > 0xffff + movk ip0, 563 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomOverlays + .p2align 4 + .type XkbAllocGeomOverlays, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomOverlays +#endif +XkbAllocGeomOverlays: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4512 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4512] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 564 & 0xffff +#if 564 > 0xffff + movk ip0, 564 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomPoints + .p2align 4 + .type XkbAllocGeomPoints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomPoints +#endif +XkbAllocGeomPoints: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4520 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4520] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 565 & 0xffff +#if 565 > 0xffff + movk ip0, 565 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomProps + .p2align 4 + .type XkbAllocGeomProps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomProps +#endif +XkbAllocGeomProps: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4528 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4528] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 566 & 0xffff +#if 566 > 0xffff + movk ip0, 566 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomRows + .p2align 4 + .type XkbAllocGeomRows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomRows +#endif +XkbAllocGeomRows: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4536 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4536] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 567 & 0xffff +#if 567 > 0xffff + movk ip0, 567 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomSectionDoodads + .p2align 4 + .type XkbAllocGeomSectionDoodads, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomSectionDoodads +#endif +XkbAllocGeomSectionDoodads: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4544 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4544] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 568 & 0xffff +#if 568 > 0xffff + movk ip0, 568 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomSections + .p2align 4 + .type XkbAllocGeomSections, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomSections +#endif +XkbAllocGeomSections: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4552 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4552] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 569 & 0xffff +#if 569 > 0xffff + movk ip0, 569 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomShapes + .p2align 4 + .type XkbAllocGeomShapes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomShapes +#endif +XkbAllocGeomShapes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4560 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4560] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 570 & 0xffff +#if 570 > 0xffff + movk ip0, 570 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeometry + .p2align 4 + .type XkbAllocGeometry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeometry +#endif +XkbAllocGeometry: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4568 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4568] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 571 & 0xffff +#if 571 > 0xffff + movk ip0, 571 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocIndicatorMaps + .p2align 4 + .type XkbAllocIndicatorMaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocIndicatorMaps +#endif +XkbAllocIndicatorMaps: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4576 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4576] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 572 & 0xffff +#if 572 > 0xffff + movk ip0, 572 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocKeyboard + .p2align 4 + .type XkbAllocKeyboard, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocKeyboard +#endif +XkbAllocKeyboard: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4584 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4584] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 573 & 0xffff +#if 573 > 0xffff + movk ip0, 573 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocNames + .p2align 4 + .type XkbAllocNames, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocNames +#endif +XkbAllocNames: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4592 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4592] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 574 & 0xffff +#if 574 > 0xffff + movk ip0, 574 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocServerMap + .p2align 4 + .type XkbAllocServerMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocServerMap +#endif +XkbAllocServerMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4600 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4600] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 575 & 0xffff +#if 575 > 0xffff + movk ip0, 575 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbApplyCompatMapToKey + .p2align 4 + .type XkbApplyCompatMapToKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbApplyCompatMapToKey +#endif +XkbApplyCompatMapToKey: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4608 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4608] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 576 & 0xffff +#if 576 > 0xffff + movk ip0, 576 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbApplyVirtualModChanges + .p2align 4 + .type XkbApplyVirtualModChanges, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbApplyVirtualModChanges +#endif +XkbApplyVirtualModChanges: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4616 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4616] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 577 & 0xffff +#if 577 > 0xffff + movk ip0, 577 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbBell + .p2align 4 + .type XkbBell, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbBell +#endif +XkbBell: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4624 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4624] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 578 & 0xffff +#if 578 > 0xffff + movk ip0, 578 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbBellEvent + .p2align 4 + .type XkbBellEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbBellEvent +#endif +XkbBellEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4632 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4632] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 579 & 0xffff +#if 579 > 0xffff + movk ip0, 579 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbChangeDeviceInfo + .p2align 4 + .type XkbChangeDeviceInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbChangeDeviceInfo +#endif +XkbChangeDeviceInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4640 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4640] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 580 & 0xffff +#if 580 > 0xffff + movk ip0, 580 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbChangeEnabledControls + .p2align 4 + .type XkbChangeEnabledControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbChangeEnabledControls +#endif +XkbChangeEnabledControls: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4648 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4648] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 581 & 0xffff +#if 581 > 0xffff + movk ip0, 581 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbChangeKeycodeRange + .p2align 4 + .type XkbChangeKeycodeRange, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbChangeKeycodeRange +#endif +XkbChangeKeycodeRange: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4656 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4656] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 582 & 0xffff +#if 582 > 0xffff + movk ip0, 582 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbChangeMap + .p2align 4 + .type XkbChangeMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbChangeMap +#endif +XkbChangeMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4664 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4664] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 583 & 0xffff +#if 583 > 0xffff + movk ip0, 583 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbChangeNames + .p2align 4 + .type XkbChangeNames, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbChangeNames +#endif +XkbChangeNames: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4672 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4672] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 584 & 0xffff +#if 584 > 0xffff + movk ip0, 584 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbChangeTypesOfKey + .p2align 4 + .type XkbChangeTypesOfKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbChangeTypesOfKey +#endif +XkbChangeTypesOfKey: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4680 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4680] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 585 & 0xffff +#if 585 > 0xffff + movk ip0, 585 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbComputeEffectiveMap + .p2align 4 + .type XkbComputeEffectiveMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbComputeEffectiveMap +#endif +XkbComputeEffectiveMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4688 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4688] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 586 & 0xffff +#if 586 > 0xffff + movk ip0, 586 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbComputeRowBounds + .p2align 4 + .type XkbComputeRowBounds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbComputeRowBounds +#endif +XkbComputeRowBounds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4696 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4696] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 587 & 0xffff +#if 587 > 0xffff + movk ip0, 587 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbComputeSectionBounds + .p2align 4 + .type XkbComputeSectionBounds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbComputeSectionBounds +#endif +XkbComputeSectionBounds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4704 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4704] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 588 & 0xffff +#if 588 > 0xffff + movk ip0, 588 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbComputeShapeBounds + .p2align 4 + .type XkbComputeShapeBounds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbComputeShapeBounds +#endif +XkbComputeShapeBounds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4712 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4712] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 589 & 0xffff +#if 589 > 0xffff + movk ip0, 589 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbComputeShapeTop + .p2align 4 + .type XkbComputeShapeTop, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbComputeShapeTop +#endif +XkbComputeShapeTop: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4720 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4720] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 590 & 0xffff +#if 590 > 0xffff + movk ip0, 590 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbCopyKeyType + .p2align 4 + .type XkbCopyKeyType, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbCopyKeyType +#endif +XkbCopyKeyType: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4728 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4728] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 591 & 0xffff +#if 591 > 0xffff + movk ip0, 591 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbCopyKeyTypes + .p2align 4 + .type XkbCopyKeyTypes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbCopyKeyTypes +#endif +XkbCopyKeyTypes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4736 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4736] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 592 & 0xffff +#if 592 > 0xffff + movk ip0, 592 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbDeviceBell + .p2align 4 + .type XkbDeviceBell, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbDeviceBell +#endif +XkbDeviceBell: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4744 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4744] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 593 & 0xffff +#if 593 > 0xffff + movk ip0, 593 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbDeviceBellEvent + .p2align 4 + .type XkbDeviceBellEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbDeviceBellEvent +#endif +XkbDeviceBellEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4752 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4752] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 594 & 0xffff +#if 594 > 0xffff + movk ip0, 594 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFindOverlayForKey + .p2align 4 + .type XkbFindOverlayForKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFindOverlayForKey +#endif +XkbFindOverlayForKey: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4760 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4760] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 595 & 0xffff +#if 595 > 0xffff + movk ip0, 595 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbForceBell + .p2align 4 + .type XkbForceBell, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbForceBell +#endif +XkbForceBell: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4768 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4768] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 596 & 0xffff +#if 596 > 0xffff + movk ip0, 596 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbForceDeviceBell + .p2align 4 + .type XkbForceDeviceBell, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbForceDeviceBell +#endif +XkbForceDeviceBell: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4776 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4776] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 597 & 0xffff +#if 597 > 0xffff + movk ip0, 597 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeClientMap + .p2align 4 + .type XkbFreeClientMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeClientMap +#endif +XkbFreeClientMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4784 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4784] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 598 & 0xffff +#if 598 > 0xffff + movk ip0, 598 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeCompatMap + .p2align 4 + .type XkbFreeCompatMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeCompatMap +#endif +XkbFreeCompatMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4792 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4792] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 599 & 0xffff +#if 599 > 0xffff + movk ip0, 599 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeComponentList + .p2align 4 + .type XkbFreeComponentList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeComponentList +#endif +XkbFreeComponentList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4800 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4800] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 600 & 0xffff +#if 600 > 0xffff + movk ip0, 600 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeControls + .p2align 4 + .type XkbFreeControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeControls +#endif +XkbFreeControls: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4808 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4808] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 601 & 0xffff +#if 601 > 0xffff + movk ip0, 601 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeDeviceInfo + .p2align 4 + .type XkbFreeDeviceInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeDeviceInfo +#endif +XkbFreeDeviceInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4816 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4816] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 602 & 0xffff +#if 602 > 0xffff + movk ip0, 602 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomColors + .p2align 4 + .type XkbFreeGeomColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomColors +#endif +XkbFreeGeomColors: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4824 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4824] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 603 & 0xffff +#if 603 > 0xffff + movk ip0, 603 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomDoodads + .p2align 4 + .type XkbFreeGeomDoodads, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomDoodads +#endif +XkbFreeGeomDoodads: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4832 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4832] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 604 & 0xffff +#if 604 > 0xffff + movk ip0, 604 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomKeyAliases + .p2align 4 + .type XkbFreeGeomKeyAliases, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomKeyAliases +#endif +XkbFreeGeomKeyAliases: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4840 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4840] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 605 & 0xffff +#if 605 > 0xffff + movk ip0, 605 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomKeys + .p2align 4 + .type XkbFreeGeomKeys, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomKeys +#endif +XkbFreeGeomKeys: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4848 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4848] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 606 & 0xffff +#if 606 > 0xffff + movk ip0, 606 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomOutlines + .p2align 4 + .type XkbFreeGeomOutlines, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomOutlines +#endif +XkbFreeGeomOutlines: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4856 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4856] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 607 & 0xffff +#if 607 > 0xffff + movk ip0, 607 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomOverlayKeys + .p2align 4 + .type XkbFreeGeomOverlayKeys, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomOverlayKeys +#endif +XkbFreeGeomOverlayKeys: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4864 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4864] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 608 & 0xffff +#if 608 > 0xffff + movk ip0, 608 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomOverlayRows + .p2align 4 + .type XkbFreeGeomOverlayRows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomOverlayRows +#endif +XkbFreeGeomOverlayRows: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4872 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4872] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 609 & 0xffff +#if 609 > 0xffff + movk ip0, 609 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomOverlays + .p2align 4 + .type XkbFreeGeomOverlays, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomOverlays +#endif +XkbFreeGeomOverlays: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4880 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4880] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 610 & 0xffff +#if 610 > 0xffff + movk ip0, 610 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomPoints + .p2align 4 + .type XkbFreeGeomPoints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomPoints +#endif +XkbFreeGeomPoints: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4888 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4888] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 611 & 0xffff +#if 611 > 0xffff + movk ip0, 611 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomProperties + .p2align 4 + .type XkbFreeGeomProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomProperties +#endif +XkbFreeGeomProperties: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4896 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4896] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 612 & 0xffff +#if 612 > 0xffff + movk ip0, 612 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomRows + .p2align 4 + .type XkbFreeGeomRows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomRows +#endif +XkbFreeGeomRows: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4904 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4904] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 613 & 0xffff +#if 613 > 0xffff + movk ip0, 613 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomSections + .p2align 4 + .type XkbFreeGeomSections, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomSections +#endif +XkbFreeGeomSections: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4912 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4912] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 614 & 0xffff +#if 614 > 0xffff + movk ip0, 614 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomShapes + .p2align 4 + .type XkbFreeGeomShapes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomShapes +#endif +XkbFreeGeomShapes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4920 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4920] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 615 & 0xffff +#if 615 > 0xffff + movk ip0, 615 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeometry + .p2align 4 + .type XkbFreeGeometry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeometry +#endif +XkbFreeGeometry: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4928 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4928] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 616 & 0xffff +#if 616 > 0xffff + movk ip0, 616 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeIndicatorMaps + .p2align 4 + .type XkbFreeIndicatorMaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeIndicatorMaps +#endif +XkbFreeIndicatorMaps: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4936 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4936] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 617 & 0xffff +#if 617 > 0xffff + movk ip0, 617 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeKeyboard + .p2align 4 + .type XkbFreeKeyboard, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeKeyboard +#endif +XkbFreeKeyboard: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4944 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4944] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 618 & 0xffff +#if 618 > 0xffff + movk ip0, 618 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeNames + .p2align 4 + .type XkbFreeNames, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeNames +#endif +XkbFreeNames: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4952 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4952] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 619 & 0xffff +#if 619 > 0xffff + movk ip0, 619 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeServerMap + .p2align 4 + .type XkbFreeServerMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeServerMap +#endif +XkbFreeServerMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4960 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4960] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 620 & 0xffff +#if 620 > 0xffff + movk ip0, 620 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetAutoRepeatRate + .p2align 4 + .type XkbGetAutoRepeatRate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetAutoRepeatRate +#endif +XkbGetAutoRepeatRate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4968 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4968] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 621 & 0xffff +#if 621 > 0xffff + movk ip0, 621 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetAutoResetControls + .p2align 4 + .type XkbGetAutoResetControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetAutoResetControls +#endif +XkbGetAutoResetControls: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4976 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4976] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 622 & 0xffff +#if 622 > 0xffff + movk ip0, 622 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetCompatMap + .p2align 4 + .type XkbGetCompatMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetCompatMap +#endif +XkbGetCompatMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4984 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4984] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 623 & 0xffff +#if 623 > 0xffff + movk ip0, 623 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetControls + .p2align 4 + .type XkbGetControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetControls +#endif +XkbGetControls: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+4992 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+4992] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 624 & 0xffff +#if 624 > 0xffff + movk ip0, 624 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetDetectableAutoRepeat + .p2align 4 + .type XkbGetDetectableAutoRepeat, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetDetectableAutoRepeat +#endif +XkbGetDetectableAutoRepeat: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5000 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5000] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 625 & 0xffff +#if 625 > 0xffff + movk ip0, 625 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetDeviceButtonActions + .p2align 4 + .type XkbGetDeviceButtonActions, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetDeviceButtonActions +#endif +XkbGetDeviceButtonActions: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5008 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5008] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 626 & 0xffff +#if 626 > 0xffff + movk ip0, 626 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetDeviceInfo + .p2align 4 + .type XkbGetDeviceInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetDeviceInfo +#endif +XkbGetDeviceInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5016 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5016] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 627 & 0xffff +#if 627 > 0xffff + movk ip0, 627 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetDeviceInfoChanges + .p2align 4 + .type XkbGetDeviceInfoChanges, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetDeviceInfoChanges +#endif +XkbGetDeviceInfoChanges: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5024 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5024] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 628 & 0xffff +#if 628 > 0xffff + movk ip0, 628 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetDeviceLedInfo + .p2align 4 + .type XkbGetDeviceLedInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetDeviceLedInfo +#endif +XkbGetDeviceLedInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5032 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5032] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 629 & 0xffff +#if 629 > 0xffff + movk ip0, 629 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetGeometry + .p2align 4 + .type XkbGetGeometry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetGeometry +#endif +XkbGetGeometry: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5040 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5040] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 630 & 0xffff +#if 630 > 0xffff + movk ip0, 630 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetIndicatorMap + .p2align 4 + .type XkbGetIndicatorMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetIndicatorMap +#endif +XkbGetIndicatorMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5048 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5048] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 631 & 0xffff +#if 631 > 0xffff + movk ip0, 631 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetIndicatorState + .p2align 4 + .type XkbGetIndicatorState, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetIndicatorState +#endif +XkbGetIndicatorState: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5056 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5056] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 632 & 0xffff +#if 632 > 0xffff + movk ip0, 632 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetKeyActions + .p2align 4 + .type XkbGetKeyActions, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetKeyActions +#endif +XkbGetKeyActions: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5064 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5064] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 633 & 0xffff +#if 633 > 0xffff + movk ip0, 633 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetKeyBehaviors + .p2align 4 + .type XkbGetKeyBehaviors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetKeyBehaviors +#endif +XkbGetKeyBehaviors: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5072 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5072] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 634 & 0xffff +#if 634 > 0xffff + movk ip0, 634 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetKeyExplicitComponents + .p2align 4 + .type XkbGetKeyExplicitComponents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetKeyExplicitComponents +#endif +XkbGetKeyExplicitComponents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5080 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5080] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 635 & 0xffff +#if 635 > 0xffff + movk ip0, 635 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetKeyModifierMap + .p2align 4 + .type XkbGetKeyModifierMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetKeyModifierMap +#endif +XkbGetKeyModifierMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5088 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5088] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 636 & 0xffff +#if 636 > 0xffff + movk ip0, 636 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetKeySyms + .p2align 4 + .type XkbGetKeySyms, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetKeySyms +#endif +XkbGetKeySyms: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5096 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5096] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 637 & 0xffff +#if 637 > 0xffff + movk ip0, 637 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetKeyTypes + .p2align 4 + .type XkbGetKeyTypes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetKeyTypes +#endif +XkbGetKeyTypes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5104 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 638 & 0xffff +#if 638 > 0xffff + movk ip0, 638 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetKeyVirtualModMap + .p2align 4 + .type XkbGetKeyVirtualModMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetKeyVirtualModMap +#endif +XkbGetKeyVirtualModMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5112 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 639 & 0xffff +#if 639 > 0xffff + movk ip0, 639 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetKeyboard + .p2align 4 + .type XkbGetKeyboard, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetKeyboard +#endif +XkbGetKeyboard: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5120 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 640 & 0xffff +#if 640 > 0xffff + movk ip0, 640 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetKeyboardByName + .p2align 4 + .type XkbGetKeyboardByName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetKeyboardByName +#endif +XkbGetKeyboardByName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5128 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 641 & 0xffff +#if 641 > 0xffff + movk ip0, 641 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetMap + .p2align 4 + .type XkbGetMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetMap +#endif +XkbGetMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5136 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 642 & 0xffff +#if 642 > 0xffff + movk ip0, 642 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetMapChanges + .p2align 4 + .type XkbGetMapChanges, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetMapChanges +#endif +XkbGetMapChanges: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5144 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 643 & 0xffff +#if 643 > 0xffff + movk ip0, 643 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetNamedDeviceIndicator + .p2align 4 + .type XkbGetNamedDeviceIndicator, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetNamedDeviceIndicator +#endif +XkbGetNamedDeviceIndicator: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5152 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 644 & 0xffff +#if 644 > 0xffff + movk ip0, 644 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetNamedGeometry + .p2align 4 + .type XkbGetNamedGeometry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetNamedGeometry +#endif +XkbGetNamedGeometry: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5160 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 645 & 0xffff +#if 645 > 0xffff + movk ip0, 645 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetNamedIndicator + .p2align 4 + .type XkbGetNamedIndicator, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetNamedIndicator +#endif +XkbGetNamedIndicator: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5168 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 646 & 0xffff +#if 646 > 0xffff + movk ip0, 646 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetNames + .p2align 4 + .type XkbGetNames, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetNames +#endif +XkbGetNames: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5176 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 647 & 0xffff +#if 647 > 0xffff + movk ip0, 647 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetPerClientControls + .p2align 4 + .type XkbGetPerClientControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetPerClientControls +#endif +XkbGetPerClientControls: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5184 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 648 & 0xffff +#if 648 > 0xffff + movk ip0, 648 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetState + .p2align 4 + .type XkbGetState, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetState +#endif +XkbGetState: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5192 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 649 & 0xffff +#if 649 > 0xffff + movk ip0, 649 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetUpdatedMap + .p2align 4 + .type XkbGetUpdatedMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetUpdatedMap +#endif +XkbGetUpdatedMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5200 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 650 & 0xffff +#if 650 > 0xffff + movk ip0, 650 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetVirtualMods + .p2align 4 + .type XkbGetVirtualMods, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetVirtualMods +#endif +XkbGetVirtualMods: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5208 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 651 & 0xffff +#if 651 > 0xffff + movk ip0, 651 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetXlibControls + .p2align 4 + .type XkbGetXlibControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetXlibControls +#endif +XkbGetXlibControls: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5216 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 652 & 0xffff +#if 652 > 0xffff + movk ip0, 652 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbIgnoreExtension + .p2align 4 + .type XkbIgnoreExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbIgnoreExtension +#endif +XkbIgnoreExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5224 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 653 & 0xffff +#if 653 > 0xffff + movk ip0, 653 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbInitCanonicalKeyTypes + .p2align 4 + .type XkbInitCanonicalKeyTypes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbInitCanonicalKeyTypes +#endif +XkbInitCanonicalKeyTypes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5232 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 654 & 0xffff +#if 654 > 0xffff + movk ip0, 654 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbKeyTypesForCoreSymbols + .p2align 4 + .type XkbKeyTypesForCoreSymbols, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbKeyTypesForCoreSymbols +#endif +XkbKeyTypesForCoreSymbols: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5240 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 655 & 0xffff +#if 655 > 0xffff + movk ip0, 655 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbKeycodeToKeysym + .p2align 4 + .type XkbKeycodeToKeysym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbKeycodeToKeysym +#endif +XkbKeycodeToKeysym: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5248 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 656 & 0xffff +#if 656 > 0xffff + movk ip0, 656 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbKeysymToModifiers + .p2align 4 + .type XkbKeysymToModifiers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbKeysymToModifiers +#endif +XkbKeysymToModifiers: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5256 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 657 & 0xffff +#if 657 > 0xffff + movk ip0, 657 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbLatchGroup + .p2align 4 + .type XkbLatchGroup, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbLatchGroup +#endif +XkbLatchGroup: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5264 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 658 & 0xffff +#if 658 > 0xffff + movk ip0, 658 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbLatchModifiers + .p2align 4 + .type XkbLatchModifiers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbLatchModifiers +#endif +XkbLatchModifiers: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5272 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 659 & 0xffff +#if 659 > 0xffff + movk ip0, 659 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbLibraryVersion + .p2align 4 + .type XkbLibraryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbLibraryVersion +#endif +XkbLibraryVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5280 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 660 & 0xffff +#if 660 > 0xffff + movk ip0, 660 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbListComponents + .p2align 4 + .type XkbListComponents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbListComponents +#endif +XkbListComponents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5288 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 661 & 0xffff +#if 661 > 0xffff + movk ip0, 661 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbLockGroup + .p2align 4 + .type XkbLockGroup, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbLockGroup +#endif +XkbLockGroup: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5296 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 662 & 0xffff +#if 662 > 0xffff + movk ip0, 662 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbLockModifiers + .p2align 4 + .type XkbLockModifiers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbLockModifiers +#endif +XkbLockModifiers: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5304 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 663 & 0xffff +#if 663 > 0xffff + movk ip0, 663 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbLookupKeyBinding + .p2align 4 + .type XkbLookupKeyBinding, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbLookupKeyBinding +#endif +XkbLookupKeyBinding: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5312 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 664 & 0xffff +#if 664 > 0xffff + movk ip0, 664 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbLookupKeySym + .p2align 4 + .type XkbLookupKeySym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbLookupKeySym +#endif +XkbLookupKeySym: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5320 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 665 & 0xffff +#if 665 > 0xffff + movk ip0, 665 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbNoteControlsChanges + .p2align 4 + .type XkbNoteControlsChanges, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbNoteControlsChanges +#endif +XkbNoteControlsChanges: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5328 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 666 & 0xffff +#if 666 > 0xffff + movk ip0, 666 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbNoteDeviceChanges + .p2align 4 + .type XkbNoteDeviceChanges, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbNoteDeviceChanges +#endif +XkbNoteDeviceChanges: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5336 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 667 & 0xffff +#if 667 > 0xffff + movk ip0, 667 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbNoteMapChanges + .p2align 4 + .type XkbNoteMapChanges, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbNoteMapChanges +#endif +XkbNoteMapChanges: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5344 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 668 & 0xffff +#if 668 > 0xffff + movk ip0, 668 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbNoteNameChanges + .p2align 4 + .type XkbNoteNameChanges, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbNoteNameChanges +#endif +XkbNoteNameChanges: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5352 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 669 & 0xffff +#if 669 > 0xffff + movk ip0, 669 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbOpenDisplay + .p2align 4 + .type XkbOpenDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbOpenDisplay +#endif +XkbOpenDisplay: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5360 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 670 & 0xffff +#if 670 > 0xffff + movk ip0, 670 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbQueryExtension + .p2align 4 + .type XkbQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbQueryExtension +#endif +XkbQueryExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5368 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 671 & 0xffff +#if 671 > 0xffff + movk ip0, 671 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbRefreshKeyboardMapping + .p2align 4 + .type XkbRefreshKeyboardMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbRefreshKeyboardMapping +#endif +XkbRefreshKeyboardMapping: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5376 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 672 & 0xffff +#if 672 > 0xffff + movk ip0, 672 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbResizeDeviceButtonActions + .p2align 4 + .type XkbResizeDeviceButtonActions, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbResizeDeviceButtonActions +#endif +XkbResizeDeviceButtonActions: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5384 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 673 & 0xffff +#if 673 > 0xffff + movk ip0, 673 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbResizeKeyActions + .p2align 4 + .type XkbResizeKeyActions, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbResizeKeyActions +#endif +XkbResizeKeyActions: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5392 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 674 & 0xffff +#if 674 > 0xffff + movk ip0, 674 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbResizeKeySyms + .p2align 4 + .type XkbResizeKeySyms, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbResizeKeySyms +#endif +XkbResizeKeySyms: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5400 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 675 & 0xffff +#if 675 > 0xffff + movk ip0, 675 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbResizeKeyType + .p2align 4 + .type XkbResizeKeyType, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbResizeKeyType +#endif +XkbResizeKeyType: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5408 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 676 & 0xffff +#if 676 > 0xffff + movk ip0, 676 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSelectEventDetails + .p2align 4 + .type XkbSelectEventDetails, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSelectEventDetails +#endif +XkbSelectEventDetails: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5416 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 677 & 0xffff +#if 677 > 0xffff + movk ip0, 677 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSelectEvents + .p2align 4 + .type XkbSelectEvents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSelectEvents +#endif +XkbSelectEvents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5424 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 678 & 0xffff +#if 678 > 0xffff + movk ip0, 678 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetAtomFuncs + .p2align 4 + .type XkbSetAtomFuncs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetAtomFuncs +#endif +XkbSetAtomFuncs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5432 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 679 & 0xffff +#if 679 > 0xffff + movk ip0, 679 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetAutoRepeatRate + .p2align 4 + .type XkbSetAutoRepeatRate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetAutoRepeatRate +#endif +XkbSetAutoRepeatRate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5440 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 680 & 0xffff +#if 680 > 0xffff + movk ip0, 680 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetAutoResetControls + .p2align 4 + .type XkbSetAutoResetControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetAutoResetControls +#endif +XkbSetAutoResetControls: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5448 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 681 & 0xffff +#if 681 > 0xffff + movk ip0, 681 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetCompatMap + .p2align 4 + .type XkbSetCompatMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetCompatMap +#endif +XkbSetCompatMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5456 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5456] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 682 & 0xffff +#if 682 > 0xffff + movk ip0, 682 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetControls + .p2align 4 + .type XkbSetControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetControls +#endif +XkbSetControls: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5464 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5464] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 683 & 0xffff +#if 683 > 0xffff + movk ip0, 683 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetDebuggingFlags + .p2align 4 + .type XkbSetDebuggingFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetDebuggingFlags +#endif +XkbSetDebuggingFlags: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5472 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5472] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 684 & 0xffff +#if 684 > 0xffff + movk ip0, 684 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetDetectableAutoRepeat + .p2align 4 + .type XkbSetDetectableAutoRepeat, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetDetectableAutoRepeat +#endif +XkbSetDetectableAutoRepeat: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5480 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5480] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 685 & 0xffff +#if 685 > 0xffff + movk ip0, 685 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetDeviceButtonActions + .p2align 4 + .type XkbSetDeviceButtonActions, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetDeviceButtonActions +#endif +XkbSetDeviceButtonActions: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5488 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5488] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 686 & 0xffff +#if 686 > 0xffff + movk ip0, 686 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetDeviceInfo + .p2align 4 + .type XkbSetDeviceInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetDeviceInfo +#endif +XkbSetDeviceInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5496 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5496] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 687 & 0xffff +#if 687 > 0xffff + movk ip0, 687 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetDeviceLedInfo + .p2align 4 + .type XkbSetDeviceLedInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetDeviceLedInfo +#endif +XkbSetDeviceLedInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5504 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5504] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 688 & 0xffff +#if 688 > 0xffff + movk ip0, 688 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetGeometry + .p2align 4 + .type XkbSetGeometry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetGeometry +#endif +XkbSetGeometry: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5512 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5512] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 689 & 0xffff +#if 689 > 0xffff + movk ip0, 689 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetIgnoreLockMods + .p2align 4 + .type XkbSetIgnoreLockMods, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetIgnoreLockMods +#endif +XkbSetIgnoreLockMods: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5520 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5520] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 690 & 0xffff +#if 690 > 0xffff + movk ip0, 690 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetIndicatorMap + .p2align 4 + .type XkbSetIndicatorMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetIndicatorMap +#endif +XkbSetIndicatorMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5528 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5528] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 691 & 0xffff +#if 691 > 0xffff + movk ip0, 691 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetMap + .p2align 4 + .type XkbSetMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetMap +#endif +XkbSetMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5536 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5536] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 692 & 0xffff +#if 692 > 0xffff + movk ip0, 692 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetNamedDeviceIndicator + .p2align 4 + .type XkbSetNamedDeviceIndicator, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetNamedDeviceIndicator +#endif +XkbSetNamedDeviceIndicator: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5544 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5544] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 693 & 0xffff +#if 693 > 0xffff + movk ip0, 693 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetNamedIndicator + .p2align 4 + .type XkbSetNamedIndicator, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetNamedIndicator +#endif +XkbSetNamedIndicator: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5552 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5552] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 694 & 0xffff +#if 694 > 0xffff + movk ip0, 694 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetNames + .p2align 4 + .type XkbSetNames, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetNames +#endif +XkbSetNames: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5560 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5560] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 695 & 0xffff +#if 695 > 0xffff + movk ip0, 695 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetPerClientControls + .p2align 4 + .type XkbSetPerClientControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetPerClientControls +#endif +XkbSetPerClientControls: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5568 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5568] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 696 & 0xffff +#if 696 > 0xffff + movk ip0, 696 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetServerInternalMods + .p2align 4 + .type XkbSetServerInternalMods, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetServerInternalMods +#endif +XkbSetServerInternalMods: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5576 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5576] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 697 & 0xffff +#if 697 > 0xffff + movk ip0, 697 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetXlibControls + .p2align 4 + .type XkbSetXlibControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetXlibControls +#endif +XkbSetXlibControls: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5584 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5584] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 698 & 0xffff +#if 698 > 0xffff + movk ip0, 698 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbToControl + .p2align 4 + .type XkbToControl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbToControl +#endif +XkbToControl: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5592 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5592] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 699 & 0xffff +#if 699 > 0xffff + movk ip0, 699 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbTranslateKey + .p2align 4 + .type XkbTranslateKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbTranslateKey +#endif +XkbTranslateKey: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5600 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5600] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 700 & 0xffff +#if 700 > 0xffff + movk ip0, 700 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbTranslateKeyCode + .p2align 4 + .type XkbTranslateKeyCode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbTranslateKeyCode +#endif +XkbTranslateKeyCode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5608 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5608] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 701 & 0xffff +#if 701 > 0xffff + movk ip0, 701 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbTranslateKeySym + .p2align 4 + .type XkbTranslateKeySym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbTranslateKeySym +#endif +XkbTranslateKeySym: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5616 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5616] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 702 & 0xffff +#if 702 > 0xffff + movk ip0, 702 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbUpdateActionVirtualMods + .p2align 4 + .type XkbUpdateActionVirtualMods, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbUpdateActionVirtualMods +#endif +XkbUpdateActionVirtualMods: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5624 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5624] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 703 & 0xffff +#if 703 > 0xffff + movk ip0, 703 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbUpdateKeyTypeVirtualMods + .p2align 4 + .type XkbUpdateKeyTypeVirtualMods, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbUpdateKeyTypeVirtualMods +#endif +XkbUpdateKeyTypeVirtualMods: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5632 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5632] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 704 & 0xffff +#if 704 > 0xffff + movk ip0, 704 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbUpdateMapFromCore + .p2align 4 + .type XkbUpdateMapFromCore, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbUpdateMapFromCore +#endif +XkbUpdateMapFromCore: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5640 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5640] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 705 & 0xffff +#if 705 > 0xffff + movk ip0, 705 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbUseExtension + .p2align 4 + .type XkbUseExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbUseExtension +#endif +XkbUseExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5648 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5648] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 706 & 0xffff +#if 706 > 0xffff + movk ip0, 706 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbVirtualModsToReal + .p2align 4 + .type XkbVirtualModsToReal, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbVirtualModsToReal +#endif +XkbVirtualModsToReal: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5656 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5656] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 707 & 0xffff +#if 707 > 0xffff + movk ip0, 707 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbXlibControlsImplemented + .p2align 4 + .type XkbXlibControlsImplemented, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbXlibControlsImplemented +#endif +XkbXlibControlsImplemented: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5664 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5664] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 708 & 0xffff +#if 708 > 0xffff + movk ip0, 708 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbDrawImageString + .p2align 4 + .type XmbDrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbDrawImageString +#endif +XmbDrawImageString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5672 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5672] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 709 & 0xffff +#if 709 > 0xffff + movk ip0, 709 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbDrawString + .p2align 4 + .type XmbDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbDrawString +#endif +XmbDrawString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5680 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5680] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 710 & 0xffff +#if 710 > 0xffff + movk ip0, 710 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbDrawText + .p2align 4 + .type XmbDrawText, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbDrawText +#endif +XmbDrawText: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5688 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5688] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 711 & 0xffff +#if 711 > 0xffff + movk ip0, 711 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbLookupString + .p2align 4 + .type XmbLookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbLookupString +#endif +XmbLookupString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5696 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5696] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 712 & 0xffff +#if 712 > 0xffff + movk ip0, 712 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbResetIC + .p2align 4 + .type XmbResetIC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbResetIC +#endif +XmbResetIC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5704 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5704] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 713 & 0xffff +#if 713 > 0xffff + movk ip0, 713 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbSetWMProperties + .p2align 4 + .type XmbSetWMProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbSetWMProperties +#endif +XmbSetWMProperties: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5712 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5712] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 714 & 0xffff +#if 714 > 0xffff + movk ip0, 714 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbTextEscapement + .p2align 4 + .type XmbTextEscapement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbTextEscapement +#endif +XmbTextEscapement: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5720 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5720] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 715 & 0xffff +#if 715 > 0xffff + movk ip0, 715 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbTextExtents + .p2align 4 + .type XmbTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbTextExtents +#endif +XmbTextExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5728 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5728] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 716 & 0xffff +#if 716 > 0xffff + movk ip0, 716 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbTextListToTextProperty + .p2align 4 + .type XmbTextListToTextProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbTextListToTextProperty +#endif +XmbTextListToTextProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5736 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5736] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 717 & 0xffff +#if 717 > 0xffff + movk ip0, 717 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbTextPerCharExtents + .p2align 4 + .type XmbTextPerCharExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbTextPerCharExtents +#endif +XmbTextPerCharExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5744 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5744] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 718 & 0xffff +#if 718 > 0xffff + movk ip0, 718 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbTextPropertyToTextList + .p2align 4 + .type XmbTextPropertyToTextList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbTextPropertyToTextList +#endif +XmbTextPropertyToTextList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5752 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5752] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 719 & 0xffff +#if 719 > 0xffff + movk ip0, 719 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xpermalloc + .p2align 4 + .type Xpermalloc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xpermalloc +#endif +Xpermalloc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5760 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5760] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 720 & 0xffff +#if 720 > 0xffff + movk ip0, 720 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmCombineDatabase + .p2align 4 + .type XrmCombineDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmCombineDatabase +#endif +XrmCombineDatabase: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5768 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5768] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 721 & 0xffff +#if 721 > 0xffff + movk ip0, 721 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmCombineFileDatabase + .p2align 4 + .type XrmCombineFileDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmCombineFileDatabase +#endif +XrmCombineFileDatabase: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5776 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5776] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 722 & 0xffff +#if 722 > 0xffff + movk ip0, 722 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmDestroyDatabase + .p2align 4 + .type XrmDestroyDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmDestroyDatabase +#endif +XrmDestroyDatabase: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5784 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5784] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 723 & 0xffff +#if 723 > 0xffff + movk ip0, 723 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmEnumerateDatabase + .p2align 4 + .type XrmEnumerateDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmEnumerateDatabase +#endif +XrmEnumerateDatabase: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5792 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5792] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 724 & 0xffff +#if 724 > 0xffff + movk ip0, 724 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmGetDatabase + .p2align 4 + .type XrmGetDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmGetDatabase +#endif +XrmGetDatabase: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5800 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5800] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 725 & 0xffff +#if 725 > 0xffff + movk ip0, 725 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmGetFileDatabase + .p2align 4 + .type XrmGetFileDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmGetFileDatabase +#endif +XrmGetFileDatabase: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5808 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5808] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 726 & 0xffff +#if 726 > 0xffff + movk ip0, 726 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmGetResource + .p2align 4 + .type XrmGetResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmGetResource +#endif +XrmGetResource: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5816 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5816] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 727 & 0xffff +#if 727 > 0xffff + movk ip0, 727 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmGetStringDatabase + .p2align 4 + .type XrmGetStringDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmGetStringDatabase +#endif +XrmGetStringDatabase: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5824 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5824] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 728 & 0xffff +#if 728 > 0xffff + movk ip0, 728 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmInitialize + .p2align 4 + .type XrmInitialize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmInitialize +#endif +XrmInitialize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5832 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5832] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 729 & 0xffff +#if 729 > 0xffff + movk ip0, 729 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmLocaleOfDatabase + .p2align 4 + .type XrmLocaleOfDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmLocaleOfDatabase +#endif +XrmLocaleOfDatabase: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5840 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5840] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 730 & 0xffff +#if 730 > 0xffff + movk ip0, 730 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmMergeDatabases + .p2align 4 + .type XrmMergeDatabases, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmMergeDatabases +#endif +XrmMergeDatabases: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5848 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5848] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 731 & 0xffff +#if 731 > 0xffff + movk ip0, 731 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmParseCommand + .p2align 4 + .type XrmParseCommand, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmParseCommand +#endif +XrmParseCommand: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5856 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5856] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 732 & 0xffff +#if 732 > 0xffff + movk ip0, 732 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmPermStringToQuark + .p2align 4 + .type XrmPermStringToQuark, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmPermStringToQuark +#endif +XrmPermStringToQuark: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5864 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5864] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 733 & 0xffff +#if 733 > 0xffff + movk ip0, 733 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmPutFileDatabase + .p2align 4 + .type XrmPutFileDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmPutFileDatabase +#endif +XrmPutFileDatabase: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5872 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5872] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 734 & 0xffff +#if 734 > 0xffff + movk ip0, 734 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmPutLineResource + .p2align 4 + .type XrmPutLineResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmPutLineResource +#endif +XrmPutLineResource: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5880 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5880] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 735 & 0xffff +#if 735 > 0xffff + movk ip0, 735 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmPutResource + .p2align 4 + .type XrmPutResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmPutResource +#endif +XrmPutResource: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5888 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5888] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 736 & 0xffff +#if 736 > 0xffff + movk ip0, 736 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmPutStringResource + .p2align 4 + .type XrmPutStringResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmPutStringResource +#endif +XrmPutStringResource: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5896 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5896] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 737 & 0xffff +#if 737 > 0xffff + movk ip0, 737 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmQGetResource + .p2align 4 + .type XrmQGetResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmQGetResource +#endif +XrmQGetResource: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5904 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5904] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 738 & 0xffff +#if 738 > 0xffff + movk ip0, 738 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmQGetSearchList + .p2align 4 + .type XrmQGetSearchList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmQGetSearchList +#endif +XrmQGetSearchList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5912 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5912] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 739 & 0xffff +#if 739 > 0xffff + movk ip0, 739 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmQGetSearchResource + .p2align 4 + .type XrmQGetSearchResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmQGetSearchResource +#endif +XrmQGetSearchResource: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5920 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5920] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 740 & 0xffff +#if 740 > 0xffff + movk ip0, 740 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmQPutResource + .p2align 4 + .type XrmQPutResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmQPutResource +#endif +XrmQPutResource: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5928 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5928] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 741 & 0xffff +#if 741 > 0xffff + movk ip0, 741 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmQPutStringResource + .p2align 4 + .type XrmQPutStringResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmQPutStringResource +#endif +XrmQPutStringResource: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5936 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5936] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 742 & 0xffff +#if 742 > 0xffff + movk ip0, 742 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmQuarkToString + .p2align 4 + .type XrmQuarkToString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmQuarkToString +#endif +XrmQuarkToString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5944 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5944] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 743 & 0xffff +#if 743 > 0xffff + movk ip0, 743 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmSetDatabase + .p2align 4 + .type XrmSetDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmSetDatabase +#endif +XrmSetDatabase: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5952 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5952] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 744 & 0xffff +#if 744 > 0xffff + movk ip0, 744 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmStringToBindingQuarkList + .p2align 4 + .type XrmStringToBindingQuarkList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmStringToBindingQuarkList +#endif +XrmStringToBindingQuarkList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5960 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5960] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 745 & 0xffff +#if 745 > 0xffff + movk ip0, 745 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmStringToQuark + .p2align 4 + .type XrmStringToQuark, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmStringToQuark +#endif +XrmStringToQuark: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5968 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5968] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 746 & 0xffff +#if 746 > 0xffff + movk ip0, 746 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmStringToQuarkList + .p2align 4 + .type XrmStringToQuarkList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmStringToQuarkList +#endif +XrmStringToQuarkList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5976 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5976] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 747 & 0xffff +#if 747 > 0xffff + movk ip0, 747 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmUniqueQuark + .p2align 4 + .type XrmUniqueQuark, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmUniqueQuark +#endif +XrmUniqueQuark: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5984 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5984] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 748 & 0xffff +#if 748 > 0xffff + movk ip0, 748 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8DrawImageString + .p2align 4 + .type Xutf8DrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8DrawImageString +#endif +Xutf8DrawImageString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+5992 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+5992] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 749 & 0xffff +#if 749 > 0xffff + movk ip0, 749 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8DrawString + .p2align 4 + .type Xutf8DrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8DrawString +#endif +Xutf8DrawString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6000 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6000] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 750 & 0xffff +#if 750 > 0xffff + movk ip0, 750 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8DrawText + .p2align 4 + .type Xutf8DrawText, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8DrawText +#endif +Xutf8DrawText: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6008 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6008] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 751 & 0xffff +#if 751 > 0xffff + movk ip0, 751 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8LookupString + .p2align 4 + .type Xutf8LookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8LookupString +#endif +Xutf8LookupString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6016 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6016] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 752 & 0xffff +#if 752 > 0xffff + movk ip0, 752 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8ResetIC + .p2align 4 + .type Xutf8ResetIC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8ResetIC +#endif +Xutf8ResetIC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6024 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6024] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 753 & 0xffff +#if 753 > 0xffff + movk ip0, 753 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8SetWMProperties + .p2align 4 + .type Xutf8SetWMProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8SetWMProperties +#endif +Xutf8SetWMProperties: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6032 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6032] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 754 & 0xffff +#if 754 > 0xffff + movk ip0, 754 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8TextEscapement + .p2align 4 + .type Xutf8TextEscapement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8TextEscapement +#endif +Xutf8TextEscapement: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6040 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6040] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 755 & 0xffff +#if 755 > 0xffff + movk ip0, 755 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8TextExtents + .p2align 4 + .type Xutf8TextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8TextExtents +#endif +Xutf8TextExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6048 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6048] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 756 & 0xffff +#if 756 > 0xffff + movk ip0, 756 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8TextListToTextProperty + .p2align 4 + .type Xutf8TextListToTextProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8TextListToTextProperty +#endif +Xutf8TextListToTextProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6056 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6056] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 757 & 0xffff +#if 757 > 0xffff + movk ip0, 757 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8TextPerCharExtents + .p2align 4 + .type Xutf8TextPerCharExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8TextPerCharExtents +#endif +Xutf8TextPerCharExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6064 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6064] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 758 & 0xffff +#if 758 > 0xffff + movk ip0, 758 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8TextPropertyToTextList + .p2align 4 + .type Xutf8TextPropertyToTextList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8TextPropertyToTextList +#endif +Xutf8TextPropertyToTextList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6072 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6072] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 759 & 0xffff +#if 759 > 0xffff + movk ip0, 759 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcDrawImageString + .p2align 4 + .type XwcDrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcDrawImageString +#endif +XwcDrawImageString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6080 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6080] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 760 & 0xffff +#if 760 > 0xffff + movk ip0, 760 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcDrawString + .p2align 4 + .type XwcDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcDrawString +#endif +XwcDrawString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6088 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6088] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 761 & 0xffff +#if 761 > 0xffff + movk ip0, 761 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcDrawText + .p2align 4 + .type XwcDrawText, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcDrawText +#endif +XwcDrawText: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6096 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6096] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 762 & 0xffff +#if 762 > 0xffff + movk ip0, 762 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcFreeStringList + .p2align 4 + .type XwcFreeStringList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcFreeStringList +#endif +XwcFreeStringList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6104 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 763 & 0xffff +#if 763 > 0xffff + movk ip0, 763 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcLookupString + .p2align 4 + .type XwcLookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcLookupString +#endif +XwcLookupString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6112 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 764 & 0xffff +#if 764 > 0xffff + movk ip0, 764 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcResetIC + .p2align 4 + .type XwcResetIC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcResetIC +#endif +XwcResetIC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6120 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 765 & 0xffff +#if 765 > 0xffff + movk ip0, 765 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcTextEscapement + .p2align 4 + .type XwcTextEscapement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcTextEscapement +#endif +XwcTextEscapement: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6128 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 766 & 0xffff +#if 766 > 0xffff + movk ip0, 766 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcTextExtents + .p2align 4 + .type XwcTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcTextExtents +#endif +XwcTextExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6136 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 767 & 0xffff +#if 767 > 0xffff + movk ip0, 767 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcTextListToTextProperty + .p2align 4 + .type XwcTextListToTextProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcTextListToTextProperty +#endif +XwcTextListToTextProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6144 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 768 & 0xffff +#if 768 > 0xffff + movk ip0, 768 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcTextPerCharExtents + .p2align 4 + .type XwcTextPerCharExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcTextPerCharExtents +#endif +XwcTextPerCharExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6152 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 769 & 0xffff +#if 769 > 0xffff + movk ip0, 769 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcTextPropertyToTextList + .p2align 4 + .type XwcTextPropertyToTextList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcTextPropertyToTextList +#endif +XwcTextPropertyToTextList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6160 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 770 & 0xffff +#if 770 > 0xffff + movk ip0, 770 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Utf8GetConvByName + .p2align 4 + .type _Utf8GetConvByName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Utf8GetConvByName +#endif +_Utf8GetConvByName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6168 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 771 & 0xffff +#if 771 > 0xffff + movk ip0, 771 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XAllocID + .p2align 4 + .type _XAllocID, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XAllocID +#endif +_XAllocID: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6176 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 772 & 0xffff +#if 772 > 0xffff + movk ip0, 772 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XAllocIDs + .p2align 4 + .type _XAllocIDs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XAllocIDs +#endif +_XAllocIDs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6184 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 773 & 0xffff +#if 773 > 0xffff + movk ip0, 773 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XAllocScratch + .p2align 4 + .type _XAllocScratch, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XAllocScratch +#endif +_XAllocScratch: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6192 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 774 & 0xffff +#if 774 > 0xffff + movk ip0, 774 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XAllocTemp + .p2align 4 + .type _XAllocTemp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XAllocTemp +#endif +_XAllocTemp: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6200 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 775 & 0xffff +#if 775 > 0xffff + movk ip0, 775 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XAsyncErrorHandler + .p2align 4 + .type _XAsyncErrorHandler, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XAsyncErrorHandler +#endif +_XAsyncErrorHandler: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6208 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 776 & 0xffff +#if 776 > 0xffff + movk ip0, 776 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XCloseLC + .p2align 4 + .type _XCloseLC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XCloseLC +#endif +_XCloseLC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6216 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 777 & 0xffff +#if 777 > 0xffff + movk ip0, 777 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XColor_to_XcmsRGB + .p2align 4 + .type _XColor_to_XcmsRGB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XColor_to_XcmsRGB +#endif +_XColor_to_XcmsRGB: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6224 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 778 & 0xffff +#if 778 > 0xffff + movk ip0, 778 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XConnectXCB + .p2align 4 + .type _XConnectXCB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XConnectXCB +#endif +_XConnectXCB: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6232 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 779 & 0xffff +#if 779 > 0xffff + movk ip0, 779 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XCopyEventCookie + .p2align 4 + .type _XCopyEventCookie, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XCopyEventCookie +#endif +_XCopyEventCookie: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6240 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 780 & 0xffff +#if 780 > 0xffff + movk ip0, 780 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XCopyToArg + .p2align 4 + .type _XCopyToArg, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XCopyToArg +#endif +_XCopyToArg: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6248 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 781 & 0xffff +#if 781 > 0xffff + movk ip0, 781 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XData32 + .p2align 4 + .type _XData32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XData32 +#endif +_XData32: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6256 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 782 & 0xffff +#if 782 > 0xffff + movk ip0, 782 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XDefaultError + .p2align 4 + .type _XDefaultError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XDefaultError +#endif +_XDefaultError: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6264 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 783 & 0xffff +#if 783 > 0xffff + movk ip0, 783 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XDefaultIOError + .p2align 4 + .type _XDefaultIOError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XDefaultIOError +#endif +_XDefaultIOError: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6272 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 784 & 0xffff +#if 784 > 0xffff + movk ip0, 784 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XDefaultIOErrorExit + .p2align 4 + .type _XDefaultIOErrorExit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XDefaultIOErrorExit +#endif +_XDefaultIOErrorExit: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6280 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 785 & 0xffff +#if 785 > 0xffff + movk ip0, 785 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XDefaultOpenIM + .p2align 4 + .type _XDefaultOpenIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XDefaultOpenIM +#endif +_XDefaultOpenIM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6288 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 786 & 0xffff +#if 786 > 0xffff + movk ip0, 786 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XDefaultOpenOM + .p2align 4 + .type _XDefaultOpenOM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XDefaultOpenOM +#endif +_XDefaultOpenOM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6296 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 787 & 0xffff +#if 787 > 0xffff + movk ip0, 787 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XDefaultWireError + .p2align 4 + .type _XDefaultWireError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XDefaultWireError +#endif +_XDefaultWireError: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6304 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 788 & 0xffff +#if 788 > 0xffff + movk ip0, 788 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XDeq + .p2align 4 + .type _XDeq, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XDeq +#endif +_XDeq: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6312 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 789 & 0xffff +#if 789 > 0xffff + movk ip0, 789 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XDeqAsyncHandler + .p2align 4 + .type _XDeqAsyncHandler, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XDeqAsyncHandler +#endif +_XDeqAsyncHandler: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6320 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 790 & 0xffff +#if 790 > 0xffff + movk ip0, 790 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XEatData + .p2align 4 + .type _XEatData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XEatData +#endif +_XEatData: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6328 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 791 & 0xffff +#if 791 > 0xffff + movk ip0, 791 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XEatDataWords + .p2align 4 + .type _XEatDataWords, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XEatDataWords +#endif +_XEatDataWords: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6336 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 792 & 0xffff +#if 792 > 0xffff + movk ip0, 792 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XEnq + .p2align 4 + .type _XEnq, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XEnq +#endif +_XEnq: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6344 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 793 & 0xffff +#if 793 > 0xffff + movk ip0, 793 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XError + .p2align 4 + .type _XError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XError +#endif +_XError: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6352 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 794 & 0xffff +#if 794 > 0xffff + movk ip0, 794 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XEventToWire + .p2align 4 + .type _XEventToWire, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XEventToWire +#endif +_XEventToWire: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6360 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 795 & 0xffff +#if 795 > 0xffff + movk ip0, 795 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XEventsQueued + .p2align 4 + .type _XEventsQueued, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XEventsQueued +#endif +_XEventsQueued: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6368 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 796 & 0xffff +#if 796 > 0xffff + movk ip0, 796 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XF86BigfontFreeFontMetrics + .p2align 4 + .type _XF86BigfontFreeFontMetrics, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XF86BigfontFreeFontMetrics +#endif +_XF86BigfontFreeFontMetrics: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6376 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 797 & 0xffff +#if 797 > 0xffff + movk ip0, 797 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XF86LoadQueryLocaleFont + .p2align 4 + .type _XF86LoadQueryLocaleFont, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XF86LoadQueryLocaleFont +#endif +_XF86LoadQueryLocaleFont: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6384 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 798 & 0xffff +#if 798 > 0xffff + movk ip0, 798 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XFetchEventCookie + .p2align 4 + .type _XFetchEventCookie, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XFetchEventCookie +#endif +_XFetchEventCookie: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6392 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 799 & 0xffff +#if 799 > 0xffff + movk ip0, 799 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XFlush + .p2align 4 + .type _XFlush, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XFlush +#endif +_XFlush: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6400 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 800 & 0xffff +#if 800 > 0xffff + movk ip0, 800 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XFlushGCCache + .p2align 4 + .type _XFlushGCCache, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XFlushGCCache +#endif +_XFlushGCCache: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6408 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 801 & 0xffff +#if 801 > 0xffff + movk ip0, 801 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XFreeAtomTable + .p2align 4 + .type _XFreeAtomTable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XFreeAtomTable +#endif +_XFreeAtomTable: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6416 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 802 & 0xffff +#if 802 > 0xffff + movk ip0, 802 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XFreeDisplayStructure + .p2align 4 + .type _XFreeDisplayStructure, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XFreeDisplayStructure +#endif +_XFreeDisplayStructure: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6424 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 803 & 0xffff +#if 803 > 0xffff + movk ip0, 803 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XFreeEventCookies + .p2align 4 + .type _XFreeEventCookies, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XFreeEventCookies +#endif +_XFreeEventCookies: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6432 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 804 & 0xffff +#if 804 > 0xffff + movk ip0, 804 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XFreeExtData + .p2align 4 + .type _XFreeExtData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XFreeExtData +#endif +_XFreeExtData: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6440 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 805 & 0xffff +#if 805 > 0xffff + movk ip0, 805 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XFreeTemp + .p2align 4 + .type _XFreeTemp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XFreeTemp +#endif +_XFreeTemp: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6448 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 806 & 0xffff +#if 806 > 0xffff + movk ip0, 806 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XFreeX11XCBStructure + .p2align 4 + .type _XFreeX11XCBStructure, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XFreeX11XCBStructure +#endif +_XFreeX11XCBStructure: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6456 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6456] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 807 & 0xffff +#if 807 > 0xffff + movk ip0, 807 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XGetAsyncData + .p2align 4 + .type _XGetAsyncData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XGetAsyncData +#endif +_XGetAsyncData: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6464 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6464] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 808 & 0xffff +#if 808 > 0xffff + movk ip0, 808 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XGetAsyncReply + .p2align 4 + .type _XGetAsyncReply, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XGetAsyncReply +#endif +_XGetAsyncReply: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6472 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6472] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 809 & 0xffff +#if 809 > 0xffff + movk ip0, 809 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XGetBitsPerPixel + .p2align 4 + .type _XGetBitsPerPixel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XGetBitsPerPixel +#endif +_XGetBitsPerPixel: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6480 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6480] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 810 & 0xffff +#if 810 > 0xffff + movk ip0, 810 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XGetHostname + .p2align 4 + .type _XGetHostname, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XGetHostname +#endif +_XGetHostname: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6488 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6488] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 811 & 0xffff +#if 811 > 0xffff + movk ip0, 811 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XGetLCValues + .p2align 4 + .type _XGetLCValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XGetLCValues +#endif +_XGetLCValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6496 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6496] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 812 & 0xffff +#if 812 > 0xffff + movk ip0, 812 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XGetRequest + .p2align 4 + .type _XGetRequest, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XGetRequest +#endif +_XGetRequest: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6504 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6504] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 813 & 0xffff +#if 813 > 0xffff + movk ip0, 813 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XGetScanlinePad + .p2align 4 + .type _XGetScanlinePad, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XGetScanlinePad +#endif +_XGetScanlinePad: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6512 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6512] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 814 & 0xffff +#if 814 > 0xffff + movk ip0, 814 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XGetWindowAttributes + .p2align 4 + .type _XGetWindowAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XGetWindowAttributes +#endif +_XGetWindowAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6520 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6520] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 815 & 0xffff +#if 815 > 0xffff + movk ip0, 815 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XIMCompileResourceList + .p2align 4 + .type _XIMCompileResourceList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XIMCompileResourceList +#endif +_XIMCompileResourceList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6528 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6528] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 816 & 0xffff +#if 816 > 0xffff + movk ip0, 816 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XIOError + .p2align 4 + .type _XIOError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XIOError +#endif +_XIOError: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6536 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6536] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 817 & 0xffff +#if 817 > 0xffff + movk ip0, 817 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XInitIM + .p2align 4 + .type _XInitIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XInitIM +#endif +_XInitIM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6544 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6544] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 818 & 0xffff +#if 818 > 0xffff + movk ip0, 818 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XInitImageFuncPtrs + .p2align 4 + .type _XInitImageFuncPtrs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XInitImageFuncPtrs +#endif +_XInitImageFuncPtrs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6552 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6552] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 819 & 0xffff +#if 819 > 0xffff + movk ip0, 819 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XInitKeysymDB + .p2align 4 + .type _XInitKeysymDB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XInitKeysymDB +#endif +_XInitKeysymDB: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6560 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6560] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 820 & 0xffff +#if 820 > 0xffff + movk ip0, 820 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XInitOM + .p2align 4 + .type _XInitOM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XInitOM +#endif +_XInitOM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6568 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6568] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 821 & 0xffff +#if 821 > 0xffff + movk ip0, 821 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XIsEventCookie + .p2align 4 + .type _XIsEventCookie, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XIsEventCookie +#endif +_XIsEventCookie: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6576 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6576] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 822 & 0xffff +#if 822 > 0xffff + movk ip0, 822 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XKeyInitialize + .p2align 4 + .type _XKeyInitialize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XKeyInitialize +#endif +_XKeyInitialize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6584 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6584] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 823 & 0xffff +#if 823 > 0xffff + movk ip0, 823 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XKeycodeToKeysym + .p2align 4 + .type _XKeycodeToKeysym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XKeycodeToKeysym +#endif +_XKeycodeToKeysym: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6592 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6592] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 824 & 0xffff +#if 824 > 0xffff + movk ip0, 824 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XKeysymToKeycode + .p2align 4 + .type _XKeysymToKeycode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XKeysymToKeycode +#endif +_XKeysymToKeycode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6600 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6600] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 825 & 0xffff +#if 825 > 0xffff + movk ip0, 825 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XKeysymToModifiers + .p2align 4 + .type _XKeysymToModifiers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XKeysymToModifiers +#endif +_XKeysymToModifiers: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6608 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6608] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 826 & 0xffff +#if 826 > 0xffff + movk ip0, 826 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XLookupKeysym + .p2align 4 + .type _XLookupKeysym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XLookupKeysym +#endif +_XLookupKeysym: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6616 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6616] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 827 & 0xffff +#if 827 > 0xffff + movk ip0, 827 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XLookupString + .p2align 4 + .type _XLookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XLookupString +#endif +_XLookupString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6624 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6624] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 828 & 0xffff +#if 828 > 0xffff + movk ip0, 828 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XNoticeCreateBitmap + .p2align 4 + .type _XNoticeCreateBitmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XNoticeCreateBitmap +#endif +_XNoticeCreateBitmap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6632 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6632] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 829 & 0xffff +#if 829 > 0xffff + movk ip0, 829 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XNoticePutBitmap + .p2align 4 + .type _XNoticePutBitmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XNoticePutBitmap +#endif +_XNoticePutBitmap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6640 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6640] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 830 & 0xffff +#if 830 > 0xffff + movk ip0, 830 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XOpenLC + .p2align 4 + .type _XOpenLC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XOpenLC +#endif +_XOpenLC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6648 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6648] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 831 & 0xffff +#if 831 > 0xffff + movk ip0, 831 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XParseBaseFontNameList + .p2align 4 + .type _XParseBaseFontNameList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XParseBaseFontNameList +#endif +_XParseBaseFontNameList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6656 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6656] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 832 & 0xffff +#if 832 > 0xffff + movk ip0, 832 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XPollfdCacheAdd + .p2align 4 + .type _XPollfdCacheAdd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XPollfdCacheAdd +#endif +_XPollfdCacheAdd: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6664 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6664] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 833 & 0xffff +#if 833 > 0xffff + movk ip0, 833 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XPollfdCacheDel + .p2align 4 + .type _XPollfdCacheDel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XPollfdCacheDel +#endif +_XPollfdCacheDel: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6672 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6672] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 834 & 0xffff +#if 834 > 0xffff + movk ip0, 834 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XPollfdCacheInit + .p2align 4 + .type _XPollfdCacheInit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XPollfdCacheInit +#endif +_XPollfdCacheInit: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6680 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6680] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 835 & 0xffff +#if 835 > 0xffff + movk ip0, 835 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XProcessInternalConnection + .p2align 4 + .type _XProcessInternalConnection, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XProcessInternalConnection +#endif +_XProcessInternalConnection: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6688 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6688] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 836 & 0xffff +#if 836 > 0xffff + movk ip0, 836 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XProcessWindowAttributes + .p2align 4 + .type _XProcessWindowAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XProcessWindowAttributes +#endif +_XProcessWindowAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6696 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6696] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 837 & 0xffff +#if 837 > 0xffff + movk ip0, 837 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XPutBackEvent + .p2align 4 + .type _XPutBackEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XPutBackEvent +#endif +_XPutBackEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6704 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6704] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 838 & 0xffff +#if 838 > 0xffff + movk ip0, 838 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XRead + .p2align 4 + .type _XRead, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XRead +#endif +_XRead: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6712 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6712] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 839 & 0xffff +#if 839 > 0xffff + movk ip0, 839 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XRead32 + .p2align 4 + .type _XRead32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XRead32 +#endif +_XRead32: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6720 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6720] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 840 & 0xffff +#if 840 > 0xffff + movk ip0, 840 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XReadEvents + .p2align 4 + .type _XReadEvents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XReadEvents +#endif +_XReadEvents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6728 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6728] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 841 & 0xffff +#if 841 > 0xffff + movk ip0, 841 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XReadPad + .p2align 4 + .type _XReadPad, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XReadPad +#endif +_XReadPad: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6736 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6736] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 842 & 0xffff +#if 842 > 0xffff + movk ip0, 842 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XRefreshKeyboardMapping + .p2align 4 + .type _XRefreshKeyboardMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XRefreshKeyboardMapping +#endif +_XRefreshKeyboardMapping: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6744 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6744] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 843 & 0xffff +#if 843 > 0xffff + movk ip0, 843 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XRegisterFilterByMask + .p2align 4 + .type _XRegisterFilterByMask, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XRegisterFilterByMask +#endif +_XRegisterFilterByMask: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6752 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6752] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 844 & 0xffff +#if 844 > 0xffff + movk ip0, 844 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XRegisterFilterByType + .p2align 4 + .type _XRegisterFilterByType, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XRegisterFilterByType +#endif +_XRegisterFilterByType: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6760 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6760] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 845 & 0xffff +#if 845 > 0xffff + movk ip0, 845 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XRegisterInternalConnection + .p2align 4 + .type _XRegisterInternalConnection, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XRegisterInternalConnection +#endif +_XRegisterInternalConnection: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6768 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6768] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 846 & 0xffff +#if 846 > 0xffff + movk ip0, 846 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XReply + .p2align 4 + .type _XReply, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XReply +#endif +_XReply: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6776 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6776] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 847 & 0xffff +#if 847 > 0xffff + movk ip0, 847 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XReverse_Bytes + .p2align 4 + .type _XReverse_Bytes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XReverse_Bytes +#endif +_XReverse_Bytes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6784 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6784] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 848 & 0xffff +#if 848 > 0xffff + movk ip0, 848 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XScreenOfWindow + .p2align 4 + .type _XScreenOfWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XScreenOfWindow +#endif +_XScreenOfWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6792 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6792] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 849 & 0xffff +#if 849 > 0xffff + movk ip0, 849 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XSend + .p2align 4 + .type _XSend, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XSend +#endif +_XSend: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6800 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6800] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 850 & 0xffff +#if 850 > 0xffff + movk ip0, 850 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XSetClipRectangles + .p2align 4 + .type _XSetClipRectangles, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XSetClipRectangles +#endif +_XSetClipRectangles: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6808 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6808] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 851 & 0xffff +#if 851 > 0xffff + movk ip0, 851 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XSetImage + .p2align 4 + .type _XSetImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XSetImage +#endif +_XSetImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6816 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6816] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 852 & 0xffff +#if 852 > 0xffff + movk ip0, 852 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XSetLastRequestRead + .p2align 4 + .type _XSetLastRequestRead, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XSetLastRequestRead +#endif +_XSetLastRequestRead: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6824 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6824] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 853 & 0xffff +#if 853 > 0xffff + movk ip0, 853 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XStoreEventCookie + .p2align 4 + .type _XStoreEventCookie, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XStoreEventCookie +#endif +_XStoreEventCookie: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6832 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6832] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 854 & 0xffff +#if 854 > 0xffff + movk ip0, 854 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XTextHeight + .p2align 4 + .type _XTextHeight, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XTextHeight +#endif +_XTextHeight: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6840 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6840] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 855 & 0xffff +#if 855 > 0xffff + movk ip0, 855 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XTextHeight16 + .p2align 4 + .type _XTextHeight16, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XTextHeight16 +#endif +_XTextHeight16: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6848 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6848] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 856 & 0xffff +#if 856 > 0xffff + movk ip0, 856 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XTranslateKey + .p2align 4 + .type _XTranslateKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XTranslateKey +#endif +_XTranslateKey: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6856 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6856] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 857 & 0xffff +#if 857 > 0xffff + movk ip0, 857 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XTranslateKeySym + .p2align 4 + .type _XTranslateKeySym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XTranslateKeySym +#endif +_XTranslateKeySym: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6864 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6864] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 858 & 0xffff +#if 858 > 0xffff + movk ip0, 858 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XTryShapeBitmapCursor + .p2align 4 + .type _XTryShapeBitmapCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XTryShapeBitmapCursor +#endif +_XTryShapeBitmapCursor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6872 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6872] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 859 & 0xffff +#if 859 > 0xffff + movk ip0, 859 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XUnknownCopyEventCookie + .p2align 4 + .type _XUnknownCopyEventCookie, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XUnknownCopyEventCookie +#endif +_XUnknownCopyEventCookie: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6880 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6880] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 860 & 0xffff +#if 860 > 0xffff + movk ip0, 860 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XUnknownNativeEvent + .p2align 4 + .type _XUnknownNativeEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XUnknownNativeEvent +#endif +_XUnknownNativeEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6888 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6888] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 861 & 0xffff +#if 861 > 0xffff + movk ip0, 861 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XUnknownWireEvent + .p2align 4 + .type _XUnknownWireEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XUnknownWireEvent +#endif +_XUnknownWireEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6896 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6896] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 862 & 0xffff +#if 862 > 0xffff + movk ip0, 862 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XUnknownWireEventCookie + .p2align 4 + .type _XUnknownWireEventCookie, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XUnknownWireEventCookie +#endif +_XUnknownWireEventCookie: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6904 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6904] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 863 & 0xffff +#if 863 > 0xffff + movk ip0, 863 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XUnregisterFilter + .p2align 4 + .type _XUnregisterFilter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XUnregisterFilter +#endif +_XUnregisterFilter: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6912 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6912] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 864 & 0xffff +#if 864 > 0xffff + movk ip0, 864 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XUnregisterInternalConnection + .p2align 4 + .type _XUnregisterInternalConnection, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XUnregisterInternalConnection +#endif +_XUnregisterInternalConnection: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6920 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6920] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 865 & 0xffff +#if 865 > 0xffff + movk ip0, 865 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XUnresolveColor + .p2align 4 + .type _XUnresolveColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XUnresolveColor +#endif +_XUnresolveColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6928 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6928] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 866 & 0xffff +#if 866 > 0xffff + movk ip0, 866 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XUpdateAtomCache + .p2align 4 + .type _XUpdateAtomCache, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XUpdateAtomCache +#endif +_XUpdateAtomCache: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6936 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6936] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 867 & 0xffff +#if 867 > 0xffff + movk ip0, 867 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XUpdateGCCache + .p2align 4 + .type _XUpdateGCCache, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XUpdateGCCache +#endif +_XUpdateGCCache: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6944 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6944] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 868 & 0xffff +#if 868 > 0xffff + movk ip0, 868 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XVIDtoVisual + .p2align 4 + .type _XVIDtoVisual, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XVIDtoVisual +#endif +_XVIDtoVisual: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6952 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6952] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 869 & 0xffff +#if 869 > 0xffff + movk ip0, 869 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XWireToEvent + .p2align 4 + .type _XWireToEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XWireToEvent +#endif +_XWireToEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6960 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6960] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 870 & 0xffff +#if 870 > 0xffff + movk ip0, 870 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsAddCmapRec + .p2align 4 + .type _XcmsAddCmapRec, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsAddCmapRec +#endif +_XcmsAddCmapRec: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6968 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6968] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 871 & 0xffff +#if 871 > 0xffff + movk ip0, 871 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsArcTangent + .p2align 4 + .type _XcmsArcTangent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsArcTangent +#endif +_XcmsArcTangent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6976 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6976] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 872 & 0xffff +#if 872 > 0xffff + movk ip0, 872 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsCIELabQueryMaxLCRGB + .p2align 4 + .type _XcmsCIELabQueryMaxLCRGB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsCIELabQueryMaxLCRGB +#endif +_XcmsCIELabQueryMaxLCRGB: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6984 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6984] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 873 & 0xffff +#if 873 > 0xffff + movk ip0, 873 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsCIELuvQueryMaxLCRGB + .p2align 4 + .type _XcmsCIELuvQueryMaxLCRGB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsCIELuvQueryMaxLCRGB +#endif +_XcmsCIELuvQueryMaxLCRGB: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+6992 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+6992] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 874 & 0xffff +#if 874 > 0xffff + movk ip0, 874 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsCIEXYZ_ValidSpec + .p2align 4 + .type _XcmsCIEXYZ_ValidSpec, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsCIEXYZ_ValidSpec +#endif +_XcmsCIEXYZ_ValidSpec: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7000 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7000] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 875 & 0xffff +#if 875 > 0xffff + movk ip0, 875 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsCIEuvY_ValidSpec + .p2align 4 + .type _XcmsCIEuvY_ValidSpec, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsCIEuvY_ValidSpec +#endif +_XcmsCIEuvY_ValidSpec: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7008 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7008] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 876 & 0xffff +#if 876 > 0xffff + movk ip0, 876 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsConvertColorsWithWhitePt + .p2align 4 + .type _XcmsConvertColorsWithWhitePt, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsConvertColorsWithWhitePt +#endif +_XcmsConvertColorsWithWhitePt: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7016 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7016] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 877 & 0xffff +#if 877 > 0xffff + movk ip0, 877 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsCopyCmapRecAndFree + .p2align 4 + .type _XcmsCopyCmapRecAndFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsCopyCmapRecAndFree +#endif +_XcmsCopyCmapRecAndFree: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7024 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7024] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 878 & 0xffff +#if 878 > 0xffff + movk ip0, 878 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsCopyISOLatin1Lowered + .p2align 4 + .type _XcmsCopyISOLatin1Lowered, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsCopyISOLatin1Lowered +#endif +_XcmsCopyISOLatin1Lowered: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7032 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7032] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 879 & 0xffff +#if 879 > 0xffff + movk ip0, 879 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsCopyPointerArray + .p2align 4 + .type _XcmsCopyPointerArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsCopyPointerArray +#endif +_XcmsCopyPointerArray: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7040 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7040] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 880 & 0xffff +#if 880 > 0xffff + movk ip0, 880 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsCosine + .p2align 4 + .type _XcmsCosine, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsCosine +#endif +_XcmsCosine: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7048 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7048] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 881 & 0xffff +#if 881 > 0xffff + movk ip0, 881 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsCubeRoot + .p2align 4 + .type _XcmsCubeRoot, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsCubeRoot +#endif +_XcmsCubeRoot: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7056 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7056] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 882 & 0xffff +#if 882 > 0xffff + movk ip0, 882 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsDDConvertColors + .p2align 4 + .type _XcmsDDConvertColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsDDConvertColors +#endif +_XcmsDDConvertColors: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7064 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7064] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 883 & 0xffff +#if 883 > 0xffff + movk ip0, 883 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsDIConvertColors + .p2align 4 + .type _XcmsDIConvertColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsDIConvertColors +#endif +_XcmsDIConvertColors: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7072 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7072] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 884 & 0xffff +#if 884 > 0xffff + movk ip0, 884 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsDeleteCmapRec + .p2align 4 + .type _XcmsDeleteCmapRec, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsDeleteCmapRec +#endif +_XcmsDeleteCmapRec: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7080 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7080] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 885 & 0xffff +#if 885 > 0xffff + movk ip0, 885 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsEqualWhitePts + .p2align 4 + .type _XcmsEqualWhitePts, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsEqualWhitePts +#endif +_XcmsEqualWhitePts: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7088 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7088] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 886 & 0xffff +#if 886 > 0xffff + movk ip0, 886 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsFreeIntensityMaps + .p2align 4 + .type _XcmsFreeIntensityMaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsFreeIntensityMaps +#endif +_XcmsFreeIntensityMaps: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7096 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7096] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 887 & 0xffff +#if 887 > 0xffff + movk ip0, 887 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsFreePointerArray + .p2align 4 + .type _XcmsFreePointerArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsFreePointerArray +#endif +_XcmsFreePointerArray: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7104 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 888 & 0xffff +#if 888 > 0xffff + movk ip0, 888 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsGetElement + .p2align 4 + .type _XcmsGetElement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsGetElement +#endif +_XcmsGetElement: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7112 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 889 & 0xffff +#if 889 > 0xffff + movk ip0, 889 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsGetIntensityMap + .p2align 4 + .type _XcmsGetIntensityMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsGetIntensityMap +#endif +_XcmsGetIntensityMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7120 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 890 & 0xffff +#if 890 > 0xffff + movk ip0, 890 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsGetProperty + .p2align 4 + .type _XcmsGetProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsGetProperty +#endif +_XcmsGetProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7128 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 891 & 0xffff +#if 891 > 0xffff + movk ip0, 891 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsInitDefaultCCCs + .p2align 4 + .type _XcmsInitDefaultCCCs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsInitDefaultCCCs +#endif +_XcmsInitDefaultCCCs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7136 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 892 & 0xffff +#if 892 > 0xffff + movk ip0, 892 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsInitScrnInfo + .p2align 4 + .type _XcmsInitScrnInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsInitScrnInfo +#endif +_XcmsInitScrnInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7144 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 893 & 0xffff +#if 893 > 0xffff + movk ip0, 893 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsLRGB_InitScrnDefault + .p2align 4 + .type _XcmsLRGB_InitScrnDefault, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsLRGB_InitScrnDefault +#endif +_XcmsLRGB_InitScrnDefault: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7152 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 894 & 0xffff +#if 894 > 0xffff + movk ip0, 894 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsPushPointerArray + .p2align 4 + .type _XcmsPushPointerArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsPushPointerArray +#endif +_XcmsPushPointerArray: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7160 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 895 & 0xffff +#if 895 > 0xffff + movk ip0, 895 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsRGB_to_XColor + .p2align 4 + .type _XcmsRGB_to_XColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsRGB_to_XColor +#endif +_XcmsRGB_to_XColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7168 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 896 & 0xffff +#if 896 > 0xffff + movk ip0, 896 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsRegFormatOfPrefix + .p2align 4 + .type _XcmsRegFormatOfPrefix, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsRegFormatOfPrefix +#endif +_XcmsRegFormatOfPrefix: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7176 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 897 & 0xffff +#if 897 > 0xffff + movk ip0, 897 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsResolveColor + .p2align 4 + .type _XcmsResolveColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsResolveColor +#endif +_XcmsResolveColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7184 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 898 & 0xffff +#if 898 > 0xffff + movk ip0, 898 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsResolveColorString + .p2align 4 + .type _XcmsResolveColorString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsResolveColorString +#endif +_XcmsResolveColorString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7192 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 899 & 0xffff +#if 899 > 0xffff + movk ip0, 899 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsSetGetColor + .p2align 4 + .type _XcmsSetGetColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsSetGetColor +#endif +_XcmsSetGetColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7200 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 900 & 0xffff +#if 900 > 0xffff + movk ip0, 900 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsSetGetColors + .p2align 4 + .type _XcmsSetGetColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsSetGetColors +#endif +_XcmsSetGetColors: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7208 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 901 & 0xffff +#if 901 > 0xffff + movk ip0, 901 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsSine + .p2align 4 + .type _XcmsSine, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsSine +#endif +_XcmsSine: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7216 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 902 & 0xffff +#if 902 > 0xffff + movk ip0, 902 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsSquareRoot + .p2align 4 + .type _XcmsSquareRoot, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsSquareRoot +#endif +_XcmsSquareRoot: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7224 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 903 & 0xffff +#if 903 > 0xffff + movk ip0, 903 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsTekHVCQueryMaxVCRGB + .p2align 4 + .type _XcmsTekHVCQueryMaxVCRGB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsTekHVCQueryMaxVCRGB +#endif +_XcmsTekHVCQueryMaxVCRGB: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7232 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 904 & 0xffff +#if 904 > 0xffff + movk ip0, 904 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsTekHVC_CheckModify + .p2align 4 + .type _XcmsTekHVC_CheckModify, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsTekHVC_CheckModify +#endif +_XcmsTekHVC_CheckModify: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7240 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 905 & 0xffff +#if 905 > 0xffff + movk ip0, 905 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsUnresolveColor + .p2align 4 + .type _XcmsUnresolveColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsUnresolveColor +#endif +_XcmsUnresolveColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7248 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 906 & 0xffff +#if 906 > 0xffff + movk ip0, 906 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimCbDispatch + .p2align 4 + .type _XimCbDispatch, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimCbDispatch +#endif +_XimCbDispatch: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7256 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 907 & 0xffff +#if 907 > 0xffff + movk ip0, 907 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimCheckCreateICValues + .p2align 4 + .type _XimCheckCreateICValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimCheckCreateICValues +#endif +_XimCheckCreateICValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7264 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 908 & 0xffff +#if 908 > 0xffff + movk ip0, 908 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimCheckDataSize + .p2align 4 + .type _XimCheckDataSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimCheckDataSize +#endif +_XimCheckDataSize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7272 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 909 & 0xffff +#if 909 > 0xffff + movk ip0, 909 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimCheckICMode + .p2align 4 + .type _XimCheckICMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimCheckICMode +#endif +_XimCheckICMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7280 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 910 & 0xffff +#if 910 > 0xffff + movk ip0, 910 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimCheckIMMode + .p2align 4 + .type _XimCheckIMMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimCheckIMMode +#endif +_XimCheckIMMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7288 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 911 & 0xffff +#if 911 > 0xffff + movk ip0, 911 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimCheckIfLocalProcessing + .p2align 4 + .type _XimCheckIfLocalProcessing, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimCheckIfLocalProcessing +#endif +_XimCheckIfLocalProcessing: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7296 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 912 & 0xffff +#if 912 > 0xffff + movk ip0, 912 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimCheckIfThaiProcessing + .p2align 4 + .type _XimCheckIfThaiProcessing, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimCheckIfThaiProcessing +#endif +_XimCheckIfThaiProcessing: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7304 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 913 & 0xffff +#if 913 > 0xffff + movk ip0, 913 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimCheckLocalInputStyle + .p2align 4 + .type _XimCheckLocalInputStyle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimCheckLocalInputStyle +#endif +_XimCheckLocalInputStyle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7312 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 914 & 0xffff +#if 914 > 0xffff + movk ip0, 914 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimCommitCallback + .p2align 4 + .type _XimCommitCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimCommitCallback +#endif +_XimCommitCallback: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7320 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 915 & 0xffff +#if 915 > 0xffff + movk ip0, 915 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimConnect + .p2align 4 + .type _XimConnect, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimConnect +#endif +_XimConnect: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7328 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 916 & 0xffff +#if 916 > 0xffff + movk ip0, 916 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimDecodeICATTRIBUTE + .p2align 4 + .type _XimDecodeICATTRIBUTE, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimDecodeICATTRIBUTE +#endif +_XimDecodeICATTRIBUTE: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7336 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 917 & 0xffff +#if 917 > 0xffff + movk ip0, 917 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimDecodeIMATTRIBUTE + .p2align 4 + .type _XimDecodeIMATTRIBUTE, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimDecodeIMATTRIBUTE +#endif +_XimDecodeIMATTRIBUTE: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7344 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 918 & 0xffff +#if 918 > 0xffff + movk ip0, 918 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimDecodeLocalICAttr + .p2align 4 + .type _XimDecodeLocalICAttr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimDecodeLocalICAttr +#endif +_XimDecodeLocalICAttr: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7352 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 919 & 0xffff +#if 919 > 0xffff + movk ip0, 919 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimDecodeLocalIMAttr + .p2align 4 + .type _XimDecodeLocalIMAttr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimDecodeLocalIMAttr +#endif +_XimDecodeLocalIMAttr: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7360 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 920 & 0xffff +#if 920 > 0xffff + movk ip0, 920 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimDestroyIMStructureList + .p2align 4 + .type _XimDestroyIMStructureList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimDestroyIMStructureList +#endif +_XimDestroyIMStructureList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7368 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 921 & 0xffff +#if 921 > 0xffff + movk ip0, 921 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimDispatchInit + .p2align 4 + .type _XimDispatchInit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimDispatchInit +#endif +_XimDispatchInit: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7376 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 922 & 0xffff +#if 922 > 0xffff + movk ip0, 922 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimEncodeICATTRIBUTE + .p2align 4 + .type _XimEncodeICATTRIBUTE, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimEncodeICATTRIBUTE +#endif +_XimEncodeICATTRIBUTE: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7384 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 923 & 0xffff +#if 923 > 0xffff + movk ip0, 923 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimEncodeIMATTRIBUTE + .p2align 4 + .type _XimEncodeIMATTRIBUTE, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimEncodeIMATTRIBUTE +#endif +_XimEncodeIMATTRIBUTE: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7392 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 924 & 0xffff +#if 924 > 0xffff + movk ip0, 924 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimEncodeLocalICAttr + .p2align 4 + .type _XimEncodeLocalICAttr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimEncodeLocalICAttr +#endif +_XimEncodeLocalICAttr: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7400 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 925 & 0xffff +#if 925 > 0xffff + movk ip0, 925 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimEncodeLocalIMAttr + .p2align 4 + .type _XimEncodeLocalIMAttr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimEncodeLocalIMAttr +#endif +_XimEncodeLocalIMAttr: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7408 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 926 & 0xffff +#if 926 > 0xffff + movk ip0, 926 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimError + .p2align 4 + .type _XimError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimError +#endif +_XimError: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7416 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 927 & 0xffff +#if 927 > 0xffff + movk ip0, 927 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimErrorCallback + .p2align 4 + .type _XimErrorCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimErrorCallback +#endif +_XimErrorCallback: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7424 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 928 & 0xffff +#if 928 > 0xffff + movk ip0, 928 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimExtension + .p2align 4 + .type _XimExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimExtension +#endif +_XimExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7432 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 929 & 0xffff +#if 929 > 0xffff + movk ip0, 929 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimFilterWaitEvent + .p2align 4 + .type _XimFilterWaitEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimFilterWaitEvent +#endif +_XimFilterWaitEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7440 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 930 & 0xffff +#if 930 > 0xffff + movk ip0, 930 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimFlush + .p2align 4 + .type _XimFlush, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimFlush +#endif +_XimFlush: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7448 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 931 & 0xffff +#if 931 > 0xffff + movk ip0, 931 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimForwardEvent + .p2align 4 + .type _XimForwardEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimForwardEvent +#endif +_XimForwardEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7456 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7456] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 932 & 0xffff +#if 932 > 0xffff + movk ip0, 932 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimForwardEventCallback + .p2align 4 + .type _XimForwardEventCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimForwardEventCallback +#endif +_XimForwardEventCallback: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7464 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7464] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 933 & 0xffff +#if 933 > 0xffff + movk ip0, 933 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimFreeCommitInfo + .p2align 4 + .type _XimFreeCommitInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimFreeCommitInfo +#endif +_XimFreeCommitInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7472 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7472] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 934 & 0xffff +#if 934 > 0xffff + movk ip0, 934 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimFreeProtoIntrCallback + .p2align 4 + .type _XimFreeProtoIntrCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimFreeProtoIntrCallback +#endif +_XimFreeProtoIntrCallback: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7480 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7480] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 935 & 0xffff +#if 935 > 0xffff + movk ip0, 935 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimFreeTransIntrCallback + .p2align 4 + .type _XimFreeTransIntrCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimFreeTransIntrCallback +#endif +_XimFreeTransIntrCallback: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7488 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7488] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 936 & 0xffff +#if 936 > 0xffff + movk ip0, 936 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetAttributeID + .p2align 4 + .type _XimGetAttributeID, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetAttributeID +#endif +_XimGetAttributeID: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7496 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7496] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 937 & 0xffff +#if 937 > 0xffff + movk ip0, 937 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetCharCode + .p2align 4 + .type _XimGetCharCode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetCharCode +#endif +_XimGetCharCode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7504 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7504] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 938 & 0xffff +#if 938 > 0xffff + movk ip0, 938 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetCurrentICValues + .p2align 4 + .type _XimGetCurrentICValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetCurrentICValues +#endif +_XimGetCurrentICValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7512 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7512] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 939 & 0xffff +#if 939 > 0xffff + movk ip0, 939 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetCurrentIMValues + .p2align 4 + .type _XimGetCurrentIMValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetCurrentIMValues +#endif +_XimGetCurrentIMValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7520 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7520] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 940 & 0xffff +#if 940 > 0xffff + movk ip0, 940 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetICValueData + .p2align 4 + .type _XimGetICValueData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetICValueData +#endif +_XimGetICValueData: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7528 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7528] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 941 & 0xffff +#if 941 > 0xffff + movk ip0, 941 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetIMValueData + .p2align 4 + .type _XimGetIMValueData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetIMValueData +#endif +_XimGetIMValueData: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7536 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7536] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 942 & 0xffff +#if 942 > 0xffff + movk ip0, 942 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetLocaleCode + .p2align 4 + .type _XimGetLocaleCode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetLocaleCode +#endif +_XimGetLocaleCode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7544 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7544] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 943 & 0xffff +#if 943 > 0xffff + movk ip0, 943 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetMyEndian + .p2align 4 + .type _XimGetMyEndian, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetMyEndian +#endif +_XimGetMyEndian: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7552 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7552] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 944 & 0xffff +#if 944 > 0xffff + movk ip0, 944 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetResourceListRec + .p2align 4 + .type _XimGetResourceListRec, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetResourceListRec +#endif +_XimGetResourceListRec: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7560 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7560] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 945 & 0xffff +#if 945 > 0xffff + movk ip0, 945 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetResourceListRecByQuark + .p2align 4 + .type _XimGetResourceListRecByQuark, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetResourceListRecByQuark +#endif +_XimGetResourceListRecByQuark: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7568 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7568] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 946 & 0xffff +#if 946 > 0xffff + movk ip0, 946 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetWindowEventmask + .p2align 4 + .type _XimGetWindowEventmask, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetWindowEventmask +#endif +_XimGetWindowEventmask: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7576 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7576] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 947 & 0xffff +#if 947 > 0xffff + movk ip0, 947 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimICOfXICID + .p2align 4 + .type _XimICOfXICID, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimICOfXICID +#endif +_XimICOfXICID: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7584 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7584] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 948 & 0xffff +#if 948 > 0xffff + movk ip0, 948 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimInitialResourceInfo + .p2align 4 + .type _XimInitialResourceInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimInitialResourceInfo +#endif +_XimInitialResourceInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7592 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7592] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 949 & 0xffff +#if 949 > 0xffff + movk ip0, 949 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLcctstombs + .p2align 4 + .type _XimLcctstombs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLcctstombs +#endif +_XimLcctstombs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7600 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7600] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 950 & 0xffff +#if 950 > 0xffff + movk ip0, 950 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLcctstoutf8 + .p2align 4 + .type _XimLcctstoutf8, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLcctstoutf8 +#endif +_XimLcctstoutf8: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7608 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7608] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 951 & 0xffff +#if 951 > 0xffff + movk ip0, 951 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLcctstowcs + .p2align 4 + .type _XimLcctstowcs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLcctstowcs +#endif +_XimLcctstowcs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7616 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7616] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 952 & 0xffff +#if 952 > 0xffff + movk ip0, 952 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalCreateIC + .p2align 4 + .type _XimLocalCreateIC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalCreateIC +#endif +_XimLocalCreateIC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7624 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7624] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 953 & 0xffff +#if 953 > 0xffff + movk ip0, 953 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalFilter + .p2align 4 + .type _XimLocalFilter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalFilter +#endif +_XimLocalFilter: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7632 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7632] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 954 & 0xffff +#if 954 > 0xffff + movk ip0, 954 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalGetICValues + .p2align 4 + .type _XimLocalGetICValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalGetICValues +#endif +_XimLocalGetICValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7640 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7640] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 955 & 0xffff +#if 955 > 0xffff + movk ip0, 955 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalGetIMValues + .p2align 4 + .type _XimLocalGetIMValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalGetIMValues +#endif +_XimLocalGetIMValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7648 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7648] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 956 & 0xffff +#if 956 > 0xffff + movk ip0, 956 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalIMFree + .p2align 4 + .type _XimLocalIMFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalIMFree +#endif +_XimLocalIMFree: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7656 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7656] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 957 & 0xffff +#if 957 > 0xffff + movk ip0, 957 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalMbLookupString + .p2align 4 + .type _XimLocalMbLookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalMbLookupString +#endif +_XimLocalMbLookupString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7664 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7664] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 958 & 0xffff +#if 958 > 0xffff + movk ip0, 958 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalOpenIM + .p2align 4 + .type _XimLocalOpenIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalOpenIM +#endif +_XimLocalOpenIM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7672 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7672] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 959 & 0xffff +#if 959 > 0xffff + movk ip0, 959 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalSetICValues + .p2align 4 + .type _XimLocalSetICValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalSetICValues +#endif +_XimLocalSetICValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7680 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7680] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 960 & 0xffff +#if 960 > 0xffff + movk ip0, 960 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalSetIMValues + .p2align 4 + .type _XimLocalSetIMValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalSetIMValues +#endif +_XimLocalSetIMValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7688 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7688] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 961 & 0xffff +#if 961 > 0xffff + movk ip0, 961 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalUtf8LookupString + .p2align 4 + .type _XimLocalUtf8LookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalUtf8LookupString +#endif +_XimLocalUtf8LookupString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7696 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7696] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 962 & 0xffff +#if 962 > 0xffff + movk ip0, 962 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalWcLookupString + .p2align 4 + .type _XimLocalWcLookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalWcLookupString +#endif +_XimLocalWcLookupString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7704 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7704] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 963 & 0xffff +#if 963 > 0xffff + movk ip0, 963 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLookupMBText + .p2align 4 + .type _XimLookupMBText, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLookupMBText +#endif +_XimLookupMBText: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7712 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7712] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 964 & 0xffff +#if 964 > 0xffff + movk ip0, 964 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLookupUTF8Text + .p2align 4 + .type _XimLookupUTF8Text, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLookupUTF8Text +#endif +_XimLookupUTF8Text: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7720 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7720] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 965 & 0xffff +#if 965 > 0xffff + movk ip0, 965 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLookupWCText + .p2align 4 + .type _XimLookupWCText, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLookupWCText +#endif +_XimLookupWCText: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7728 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7728] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 966 & 0xffff +#if 966 > 0xffff + movk ip0, 966 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimMakeICAttrIDList + .p2align 4 + .type _XimMakeICAttrIDList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimMakeICAttrIDList +#endif +_XimMakeICAttrIDList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7736 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7736] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 967 & 0xffff +#if 967 > 0xffff + movk ip0, 967 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimMakeIMAttrIDList + .p2align 4 + .type _XimMakeIMAttrIDList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimMakeIMAttrIDList +#endif +_XimMakeIMAttrIDList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7744 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7744] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 968 & 0xffff +#if 968 > 0xffff + movk ip0, 968 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimOpenIM + .p2align 4 + .type _XimOpenIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimOpenIM +#endif +_XimOpenIM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7752 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7752] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 969 & 0xffff +#if 969 > 0xffff + movk ip0, 969 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimParseStringFile + .p2align 4 + .type _XimParseStringFile, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimParseStringFile +#endif +_XimParseStringFile: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7760 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7760] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 970 & 0xffff +#if 970 > 0xffff + movk ip0, 970 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProcError + .p2align 4 + .type _XimProcError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProcError +#endif +_XimProcError: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7768 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7768] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 971 & 0xffff +#if 971 > 0xffff + movk ip0, 971 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProcSyncReply + .p2align 4 + .type _XimProcSyncReply, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProcSyncReply +#endif +_XimProcSyncReply: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7776 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7776] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 972 & 0xffff +#if 972 > 0xffff + movk ip0, 972 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProtoCreateIC + .p2align 4 + .type _XimProtoCreateIC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProtoCreateIC +#endif +_XimProtoCreateIC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7784 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7784] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 973 & 0xffff +#if 973 > 0xffff + movk ip0, 973 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProtoEventToWire + .p2align 4 + .type _XimProtoEventToWire, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProtoEventToWire +#endif +_XimProtoEventToWire: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7792 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7792] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 974 & 0xffff +#if 974 > 0xffff + movk ip0, 974 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProtoIMFree + .p2align 4 + .type _XimProtoIMFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProtoIMFree +#endif +_XimProtoIMFree: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7800 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7800] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 975 & 0xffff +#if 975 > 0xffff + movk ip0, 975 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProtoMbLookupString + .p2align 4 + .type _XimProtoMbLookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProtoMbLookupString +#endif +_XimProtoMbLookupString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7808 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7808] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 976 & 0xffff +#if 976 > 0xffff + movk ip0, 976 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProtoOpenIM + .p2align 4 + .type _XimProtoOpenIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProtoOpenIM +#endif +_XimProtoOpenIM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7816 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7816] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 977 & 0xffff +#if 977 > 0xffff + movk ip0, 977 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProtoUtf8LookupString + .p2align 4 + .type _XimProtoUtf8LookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProtoUtf8LookupString +#endif +_XimProtoUtf8LookupString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7824 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7824] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 978 & 0xffff +#if 978 > 0xffff + movk ip0, 978 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProtoWcLookupString + .p2align 4 + .type _XimProtoWcLookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProtoWcLookupString +#endif +_XimProtoWcLookupString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7832 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7832] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 979 & 0xffff +#if 979 > 0xffff + movk ip0, 979 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProtoWireToEvent + .p2align 4 + .type _XimProtoWireToEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProtoWireToEvent +#endif +_XimProtoWireToEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7840 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7840] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 980 & 0xffff +#if 980 > 0xffff + movk ip0, 980 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimRead + .p2align 4 + .type _XimRead, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimRead +#endif +_XimRead: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7848 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7848] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 981 & 0xffff +#if 981 > 0xffff + movk ip0, 981 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimRegProtoIntrCallback + .p2align 4 + .type _XimRegProtoIntrCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimRegProtoIntrCallback +#endif +_XimRegProtoIntrCallback: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7856 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7856] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 982 & 0xffff +#if 982 > 0xffff + movk ip0, 982 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimRegisterDispatcher + .p2align 4 + .type _XimRegisterDispatcher, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimRegisterDispatcher +#endif +_XimRegisterDispatcher: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7864 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7864] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 983 & 0xffff +#if 983 > 0xffff + movk ip0, 983 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimRegisterFilter + .p2align 4 + .type _XimRegisterFilter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimRegisterFilter +#endif +_XimRegisterFilter: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7872 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7872] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 984 & 0xffff +#if 984 > 0xffff + movk ip0, 984 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimRegisterIMInstantiateCallback + .p2align 4 + .type _XimRegisterIMInstantiateCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimRegisterIMInstantiateCallback +#endif +_XimRegisterIMInstantiateCallback: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7880 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7880] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 985 & 0xffff +#if 985 > 0xffff + movk ip0, 985 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimRegisterServerFilter + .p2align 4 + .type _XimRegisterServerFilter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimRegisterServerFilter +#endif +_XimRegisterServerFilter: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7888 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7888] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 986 & 0xffff +#if 986 > 0xffff + movk ip0, 986 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimRegisterTriggerKeysCallback + .p2align 4 + .type _XimRegisterTriggerKeysCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimRegisterTriggerKeysCallback +#endif +_XimRegisterTriggerKeysCallback: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7896 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7896] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 987 & 0xffff +#if 987 > 0xffff + movk ip0, 987 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimReregisterFilter + .p2align 4 + .type _XimReregisterFilter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimReregisterFilter +#endif +_XimReregisterFilter: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7904 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7904] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 988 & 0xffff +#if 988 > 0xffff + movk ip0, 988 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimResetIMInstantiateCallback + .p2align 4 + .type _XimResetIMInstantiateCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimResetIMInstantiateCallback +#endif +_XimResetIMInstantiateCallback: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7912 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7912] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 989 & 0xffff +#if 989 > 0xffff + movk ip0, 989 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimRespSyncReply + .p2align 4 + .type _XimRespSyncReply, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimRespSyncReply +#endif +_XimRespSyncReply: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7920 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7920] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 990 & 0xffff +#if 990 > 0xffff + movk ip0, 990 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimServerDestroy + .p2align 4 + .type _XimServerDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimServerDestroy +#endif +_XimServerDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7928 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7928] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 991 & 0xffff +#if 991 > 0xffff + movk ip0, 991 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetCurrentICValues + .p2align 4 + .type _XimSetCurrentICValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetCurrentICValues +#endif +_XimSetCurrentICValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7936 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7936] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 992 & 0xffff +#if 992 > 0xffff + movk ip0, 992 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetCurrentIMValues + .p2align 4 + .type _XimSetCurrentIMValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetCurrentIMValues +#endif +_XimSetCurrentIMValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7944 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7944] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 993 & 0xffff +#if 993 > 0xffff + movk ip0, 993 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetEventMaskCallback + .p2align 4 + .type _XimSetEventMaskCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetEventMaskCallback +#endif +_XimSetEventMaskCallback: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7952 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7952] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 994 & 0xffff +#if 994 > 0xffff + movk ip0, 994 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetHeader + .p2align 4 + .type _XimSetHeader, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetHeader +#endif +_XimSetHeader: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7960 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7960] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 995 & 0xffff +#if 995 > 0xffff + movk ip0, 995 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetICDefaults + .p2align 4 + .type _XimSetICDefaults, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetICDefaults +#endif +_XimSetICDefaults: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7968 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7968] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 996 & 0xffff +#if 996 > 0xffff + movk ip0, 996 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetICMode + .p2align 4 + .type _XimSetICMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetICMode +#endif +_XimSetICMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7976 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7976] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 997 & 0xffff +#if 997 > 0xffff + movk ip0, 997 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetICResourceList + .p2align 4 + .type _XimSetICResourceList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetICResourceList +#endif +_XimSetICResourceList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7984 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7984] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 998 & 0xffff +#if 998 > 0xffff + movk ip0, 998 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetICValueData + .p2align 4 + .type _XimSetICValueData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetICValueData +#endif +_XimSetICValueData: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+7992 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+7992] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 999 & 0xffff +#if 999 > 0xffff + movk ip0, 999 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetIMMode + .p2align 4 + .type _XimSetIMMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetIMMode +#endif +_XimSetIMMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8000 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8000] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1000 & 0xffff +#if 1000 > 0xffff + movk ip0, 1000 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetIMResourceList + .p2align 4 + .type _XimSetIMResourceList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetIMResourceList +#endif +_XimSetIMResourceList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8008 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8008] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1001 & 0xffff +#if 1001 > 0xffff + movk ip0, 1001 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetIMValueData + .p2align 4 + .type _XimSetIMValueData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetIMValueData +#endif +_XimSetIMValueData: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8016 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8016] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1002 & 0xffff +#if 1002 > 0xffff + movk ip0, 1002 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetInnerICResourceList + .p2align 4 + .type _XimSetInnerICResourceList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetInnerICResourceList +#endif +_XimSetInnerICResourceList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8024 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8024] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1003 & 0xffff +#if 1003 > 0xffff + movk ip0, 1003 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetInnerIMResourceList + .p2align 4 + .type _XimSetInnerIMResourceList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetInnerIMResourceList +#endif +_XimSetInnerIMResourceList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8032 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8032] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1004 & 0xffff +#if 1004 > 0xffff + movk ip0, 1004 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetLocalIMDefaults + .p2align 4 + .type _XimSetLocalIMDefaults, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetLocalIMDefaults +#endif +_XimSetLocalIMDefaults: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8040 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8040] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1005 & 0xffff +#if 1005 > 0xffff + movk ip0, 1005 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimShutdown + .p2align 4 + .type _XimShutdown, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimShutdown +#endif +_XimShutdown: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8048 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8048] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1006 & 0xffff +#if 1006 > 0xffff + movk ip0, 1006 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSync + .p2align 4 + .type _XimSync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSync +#endif +_XimSync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8056 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8056] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1007 & 0xffff +#if 1007 > 0xffff + movk ip0, 1007 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSyncCallback + .p2align 4 + .type _XimSyncCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSyncCallback +#endif +_XimSyncCallback: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8064 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8064] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1008 & 0xffff +#if 1008 > 0xffff + movk ip0, 1008 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimThaiCloseIM + .p2align 4 + .type _XimThaiCloseIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimThaiCloseIM +#endif +_XimThaiCloseIM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8072 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8072] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1009 & 0xffff +#if 1009 > 0xffff + movk ip0, 1009 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimThaiCreateIC + .p2align 4 + .type _XimThaiCreateIC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimThaiCreateIC +#endif +_XimThaiCreateIC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8080 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8080] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1010 & 0xffff +#if 1010 > 0xffff + movk ip0, 1010 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimThaiFilter + .p2align 4 + .type _XimThaiFilter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimThaiFilter +#endif +_XimThaiFilter: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8088 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8088] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1011 & 0xffff +#if 1011 > 0xffff + movk ip0, 1011 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimThaiIMFree + .p2align 4 + .type _XimThaiIMFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimThaiIMFree +#endif +_XimThaiIMFree: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8096 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8096] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1012 & 0xffff +#if 1012 > 0xffff + movk ip0, 1012 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimThaiOpenIM + .p2align 4 + .type _XimThaiOpenIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimThaiOpenIM +#endif +_XimThaiOpenIM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8104 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1013 & 0xffff +#if 1013 > 0xffff + movk ip0, 1013 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimTransCallDispatcher + .p2align 4 + .type _XimTransCallDispatcher, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimTransCallDispatcher +#endif +_XimTransCallDispatcher: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8112 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1014 & 0xffff +#if 1014 > 0xffff + movk ip0, 1014 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimTransConf + .p2align 4 + .type _XimTransConf, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimTransConf +#endif +_XimTransConf: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8120 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1015 & 0xffff +#if 1015 > 0xffff + movk ip0, 1015 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimTransFilterWaitEvent + .p2align 4 + .type _XimTransFilterWaitEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimTransFilterWaitEvent +#endif +_XimTransFilterWaitEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8128 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1016 & 0xffff +#if 1016 > 0xffff + movk ip0, 1016 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimTransFlush + .p2align 4 + .type _XimTransFlush, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimTransFlush +#endif +_XimTransFlush: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8136 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1017 & 0xffff +#if 1017 > 0xffff + movk ip0, 1017 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimTransInternalConnection + .p2align 4 + .type _XimTransInternalConnection, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimTransInternalConnection +#endif +_XimTransInternalConnection: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8144 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1018 & 0xffff +#if 1018 > 0xffff + movk ip0, 1018 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimTransRead + .p2align 4 + .type _XimTransRead, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimTransRead +#endif +_XimTransRead: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8152 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1019 & 0xffff +#if 1019 > 0xffff + movk ip0, 1019 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimTransRegisterDispatcher + .p2align 4 + .type _XimTransRegisterDispatcher, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimTransRegisterDispatcher +#endif +_XimTransRegisterDispatcher: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8160 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1020 & 0xffff +#if 1020 > 0xffff + movk ip0, 1020 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimTransWrite + .p2align 4 + .type _XimTransWrite, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimTransWrite +#endif +_XimTransWrite: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8168 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1021 & 0xffff +#if 1021 > 0xffff + movk ip0, 1021 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimTriggerNotify + .p2align 4 + .type _XimTriggerNotify, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimTriggerNotify +#endif +_XimTriggerNotify: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8176 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1022 & 0xffff +#if 1022 > 0xffff + movk ip0, 1022 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimUnRegisterIMInstantiateCallback + .p2align 4 + .type _XimUnRegisterIMInstantiateCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimUnRegisterIMInstantiateCallback +#endif +_XimUnRegisterIMInstantiateCallback: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8184 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1023 & 0xffff +#if 1023 > 0xffff + movk ip0, 1023 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimUnregisterFilter + .p2align 4 + .type _XimUnregisterFilter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimUnregisterFilter +#endif +_XimUnregisterFilter: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8192 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1024 & 0xffff +#if 1024 > 0xffff + movk ip0, 1024 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimUnregisterServerFilter + .p2align 4 + .type _XimUnregisterServerFilter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimUnregisterServerFilter +#endif +_XimUnregisterServerFilter: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8200 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1025 & 0xffff +#if 1025 > 0xffff + movk ip0, 1025 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimWrite + .p2align 4 + .type _XimWrite, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimWrite +#endif +_XimWrite: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8208 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1026 & 0xffff +#if 1026 > 0xffff + movk ip0, 1026 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXConf + .p2align 4 + .type _XimXConf, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXConf +#endif +_XimXConf: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8216 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1027 & 0xffff +#if 1027 > 0xffff + movk ip0, 1027 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransBytesReadable + .p2align 4 + .type _XimXTransBytesReadable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransBytesReadable +#endif +_XimXTransBytesReadable: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8224 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1028 & 0xffff +#if 1028 > 0xffff + movk ip0, 1028 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransClose + .p2align 4 + .type _XimXTransClose, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransClose +#endif +_XimXTransClose: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8232 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1029 & 0xffff +#if 1029 > 0xffff + movk ip0, 1029 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransCloseForCloning + .p2align 4 + .type _XimXTransCloseForCloning, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransCloseForCloning +#endif +_XimXTransCloseForCloning: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8240 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1030 & 0xffff +#if 1030 > 0xffff + movk ip0, 1030 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransConnect + .p2align 4 + .type _XimXTransConnect, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransConnect +#endif +_XimXTransConnect: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8248 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1031 & 0xffff +#if 1031 > 0xffff + movk ip0, 1031 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransDisconnect + .p2align 4 + .type _XimXTransDisconnect, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransDisconnect +#endif +_XimXTransDisconnect: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8256 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1032 & 0xffff +#if 1032 > 0xffff + movk ip0, 1032 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransFreeConnInfo + .p2align 4 + .type _XimXTransFreeConnInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransFreeConnInfo +#endif +_XimXTransFreeConnInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8264 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1033 & 0xffff +#if 1033 > 0xffff + movk ip0, 1033 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransGetConnectionNumber + .p2align 4 + .type _XimXTransGetConnectionNumber, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransGetConnectionNumber +#endif +_XimXTransGetConnectionNumber: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8272 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1034 & 0xffff +#if 1034 > 0xffff + movk ip0, 1034 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransGetHostname + .p2align 4 + .type _XimXTransGetHostname, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransGetHostname +#endif +_XimXTransGetHostname: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8280 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1035 & 0xffff +#if 1035 > 0xffff + movk ip0, 1035 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransGetPeerAddr + .p2align 4 + .type _XimXTransGetPeerAddr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransGetPeerAddr +#endif +_XimXTransGetPeerAddr: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8288 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1036 & 0xffff +#if 1036 > 0xffff + movk ip0, 1036 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransIsLocal + .p2align 4 + .type _XimXTransIsLocal, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransIsLocal +#endif +_XimXTransIsLocal: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8296 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1037 & 0xffff +#if 1037 > 0xffff + movk ip0, 1037 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransOpenCOTSClient + .p2align 4 + .type _XimXTransOpenCOTSClient, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransOpenCOTSClient +#endif +_XimXTransOpenCOTSClient: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8304 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1038 & 0xffff +#if 1038 > 0xffff + movk ip0, 1038 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransRead + .p2align 4 + .type _XimXTransRead, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransRead +#endif +_XimXTransRead: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8312 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1039 & 0xffff +#if 1039 > 0xffff + movk ip0, 1039 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransReadv + .p2align 4 + .type _XimXTransReadv, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransReadv +#endif +_XimXTransReadv: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8320 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1040 & 0xffff +#if 1040 > 0xffff + movk ip0, 1040 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransSetOption + .p2align 4 + .type _XimXTransSetOption, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransSetOption +#endif +_XimXTransSetOption: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8328 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1041 & 0xffff +#if 1041 > 0xffff + movk ip0, 1041 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransWrite + .p2align 4 + .type _XimXTransWrite, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransWrite +#endif +_XimXTransWrite: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8336 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1042 & 0xffff +#if 1042 > 0xffff + movk ip0, 1042 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransWritev + .p2align 4 + .type _XimXTransWritev, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransWritev +#endif +_XimXTransWritev: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8344 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1043 & 0xffff +#if 1043 > 0xffff + movk ip0, 1043 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Ximctstombs + .p2align 4 + .type _Ximctstombs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Ximctstombs +#endif +_Ximctstombs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8352 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1044 & 0xffff +#if 1044 > 0xffff + movk ip0, 1044 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Ximctstoutf8 + .p2align 4 + .type _Ximctstoutf8, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Ximctstoutf8 +#endif +_Ximctstoutf8: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8360 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1045 & 0xffff +#if 1045 > 0xffff + movk ip0, 1045 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Ximctstowcs + .p2align 4 + .type _Ximctstowcs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Ximctstowcs +#endif +_Ximctstowcs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8368 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1046 & 0xffff +#if 1046 > 0xffff + movk ip0, 1046 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbCopyFromReadBuffer + .p2align 4 + .type _XkbCopyFromReadBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbCopyFromReadBuffer +#endif +_XkbCopyFromReadBuffer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8376 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1047 & 0xffff +#if 1047 > 0xffff + movk ip0, 1047 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbFreeReadBuffer + .p2align 4 + .type _XkbFreeReadBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbFreeReadBuffer +#endif +_XkbFreeReadBuffer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8384 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1048 & 0xffff +#if 1048 > 0xffff + movk ip0, 1048 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbGetCharset + .p2align 4 + .type _XkbGetCharset, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbGetCharset +#endif +_XkbGetCharset: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8392 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1049 & 0xffff +#if 1049 > 0xffff + movk ip0, 1049 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbGetConverters + .p2align 4 + .type _XkbGetConverters, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbGetConverters +#endif +_XkbGetConverters: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8400 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1050 & 0xffff +#if 1050 > 0xffff + movk ip0, 1050 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbGetReadBufferCountedString + .p2align 4 + .type _XkbGetReadBufferCountedString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbGetReadBufferCountedString +#endif +_XkbGetReadBufferCountedString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8408 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1051 & 0xffff +#if 1051 > 0xffff + movk ip0, 1051 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbGetReadBufferPtr + .p2align 4 + .type _XkbGetReadBufferPtr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbGetReadBufferPtr +#endif +_XkbGetReadBufferPtr: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8416 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1052 & 0xffff +#if 1052 > 0xffff + movk ip0, 1052 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbInitReadBuffer + .p2align 4 + .type _XkbInitReadBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbInitReadBuffer +#endif +_XkbInitReadBuffer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8424 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1053 & 0xffff +#if 1053 > 0xffff + movk ip0, 1053 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbNoteCoreMapChanges + .p2align 4 + .type _XkbNoteCoreMapChanges, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbNoteCoreMapChanges +#endif +_XkbNoteCoreMapChanges: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8432 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1054 & 0xffff +#if 1054 > 0xffff + movk ip0, 1054 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbPeekAtReadBuffer + .p2align 4 + .type _XkbPeekAtReadBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbPeekAtReadBuffer +#endif +_XkbPeekAtReadBuffer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8440 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1055 & 0xffff +#if 1055 > 0xffff + movk ip0, 1055 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReadBufferCopy32 + .p2align 4 + .type _XkbReadBufferCopy32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReadBufferCopy32 +#endif +_XkbReadBufferCopy32: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8448 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1056 & 0xffff +#if 1056 > 0xffff + movk ip0, 1056 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReadBufferCopyKeySyms + .p2align 4 + .type _XkbReadBufferCopyKeySyms, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReadBufferCopyKeySyms +#endif +_XkbReadBufferCopyKeySyms: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8456 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8456] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1057 & 0xffff +#if 1057 > 0xffff + movk ip0, 1057 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReadCopyData32 + .p2align 4 + .type _XkbReadCopyData32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReadCopyData32 +#endif +_XkbReadCopyData32: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8464 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8464] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1058 & 0xffff +#if 1058 > 0xffff + movk ip0, 1058 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReadCopyKeySyms + .p2align 4 + .type _XkbReadCopyKeySyms, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReadCopyKeySyms +#endif +_XkbReadCopyKeySyms: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8472 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8472] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1059 & 0xffff +#if 1059 > 0xffff + movk ip0, 1059 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReadGetCompatMapReply + .p2align 4 + .type _XkbReadGetCompatMapReply, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReadGetCompatMapReply +#endif +_XkbReadGetCompatMapReply: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8480 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8480] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1060 & 0xffff +#if 1060 > 0xffff + movk ip0, 1060 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReadGetGeometryReply + .p2align 4 + .type _XkbReadGetGeometryReply, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReadGetGeometryReply +#endif +_XkbReadGetGeometryReply: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8488 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8488] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1061 & 0xffff +#if 1061 > 0xffff + movk ip0, 1061 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReadGetIndicatorMapReply + .p2align 4 + .type _XkbReadGetIndicatorMapReply, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReadGetIndicatorMapReply +#endif +_XkbReadGetIndicatorMapReply: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8496 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8496] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1062 & 0xffff +#if 1062 > 0xffff + movk ip0, 1062 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReadGetMapReply + .p2align 4 + .type _XkbReadGetMapReply, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReadGetMapReply +#endif +_XkbReadGetMapReply: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8504 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8504] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1063 & 0xffff +#if 1063 > 0xffff + movk ip0, 1063 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReadGetNamesReply + .p2align 4 + .type _XkbReadGetNamesReply, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReadGetNamesReply +#endif +_XkbReadGetNamesReply: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8512 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8512] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1064 & 0xffff +#if 1064 > 0xffff + movk ip0, 1064 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReloadDpy + .p2align 4 + .type _XkbReloadDpy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReloadDpy +#endif +_XkbReloadDpy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8520 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8520] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1065 & 0xffff +#if 1065 > 0xffff + movk ip0, 1065 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbSkipReadBufferData + .p2align 4 + .type _XkbSkipReadBufferData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbSkipReadBufferData +#endif +_XkbSkipReadBufferData: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8528 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8528] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1066 & 0xffff +#if 1066 > 0xffff + movk ip0, 1066 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbWriteCopyData32 + .p2align 4 + .type _XkbWriteCopyData32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbWriteCopyData32 +#endif +_XkbWriteCopyData32: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8536 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8536] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1067 & 0xffff +#if 1067 > 0xffff + movk ip0, 1067 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbWriteCopyKeySyms + .p2align 4 + .type _XkbWriteCopyKeySyms, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbWriteCopyKeySyms +#endif +_XkbWriteCopyKeySyms: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8544 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8544] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1068 & 0xffff +#if 1068 > 0xffff + movk ip0, 1068 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcAddCT + .p2align 4 + .type _XlcAddCT, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcAddCT +#endif +_XlcAddCT: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8552 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8552] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1069 & 0xffff +#if 1069 > 0xffff + movk ip0, 1069 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcAddCharSet + .p2align 4 + .type _XlcAddCharSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcAddCharSet +#endif +_XlcAddCharSet: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8560 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8560] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1070 & 0xffff +#if 1070 > 0xffff + movk ip0, 1070 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcAddGB18030LocaleConverters + .p2align 4 + .type _XlcAddGB18030LocaleConverters, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcAddGB18030LocaleConverters +#endif +_XlcAddGB18030LocaleConverters: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8568 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8568] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1071 & 0xffff +#if 1071 > 0xffff + movk ip0, 1071 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcAddLoader + .p2align 4 + .type _XlcAddLoader, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcAddLoader +#endif +_XlcAddLoader: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8576 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8576] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1072 & 0xffff +#if 1072 > 0xffff + movk ip0, 1072 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcAddUtf8Converters + .p2align 4 + .type _XlcAddUtf8Converters, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcAddUtf8Converters +#endif +_XlcAddUtf8Converters: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8584 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8584] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1073 & 0xffff +#if 1073 > 0xffff + movk ip0, 1073 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcAddUtf8LocaleConverters + .p2align 4 + .type _XlcAddUtf8LocaleConverters, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcAddUtf8LocaleConverters +#endif +_XlcAddUtf8LocaleConverters: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8592 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8592] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1074 & 0xffff +#if 1074 > 0xffff + movk ip0, 1074 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCloseConverter + .p2align 4 + .type _XlcCloseConverter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCloseConverter +#endif +_XlcCloseConverter: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8600 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8600] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1075 & 0xffff +#if 1075 > 0xffff + movk ip0, 1075 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCompareISOLatin1 + .p2align 4 + .type _XlcCompareISOLatin1, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCompareISOLatin1 +#endif +_XlcCompareISOLatin1: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8608 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8608] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1076 & 0xffff +#if 1076 > 0xffff + movk ip0, 1076 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCompileResourceList + .p2align 4 + .type _XlcCompileResourceList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCompileResourceList +#endif +_XlcCompileResourceList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8616 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8616] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1077 & 0xffff +#if 1077 > 0xffff + movk ip0, 1077 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcConvert + .p2align 4 + .type _XlcConvert, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcConvert +#endif +_XlcConvert: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8624 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8624] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1078 & 0xffff +#if 1078 > 0xffff + movk ip0, 1078 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCopyFromArg + .p2align 4 + .type _XlcCopyFromArg, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCopyFromArg +#endif +_XlcCopyFromArg: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8632 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8632] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1079 & 0xffff +#if 1079 > 0xffff + movk ip0, 1079 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCopyToArg + .p2align 4 + .type _XlcCopyToArg, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCopyToArg +#endif +_XlcCopyToArg: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8640 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8640] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1080 & 0xffff +#if 1080 > 0xffff + movk ip0, 1080 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCountVaList + .p2align 4 + .type _XlcCountVaList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCountVaList +#endif +_XlcCountVaList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8648 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8648] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1081 & 0xffff +#if 1081 > 0xffff + movk ip0, 1081 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCreateDefaultCharSet + .p2align 4 + .type _XlcCreateDefaultCharSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCreateDefaultCharSet +#endif +_XlcCreateDefaultCharSet: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8656 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8656] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1082 & 0xffff +#if 1082 > 0xffff + movk ip0, 1082 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCreateLC + .p2align 4 + .type _XlcCreateLC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCreateLC +#endif +_XlcCreateLC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8664 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8664] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1083 & 0xffff +#if 1083 > 0xffff + movk ip0, 1083 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCreateLocaleDataBase + .p2align 4 + .type _XlcCreateLocaleDataBase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCreateLocaleDataBase +#endif +_XlcCreateLocaleDataBase: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8672 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8672] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1084 & 0xffff +#if 1084 > 0xffff + movk ip0, 1084 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCurrentLC + .p2align 4 + .type _XlcCurrentLC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCurrentLC +#endif +_XlcCurrentLC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8680 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8680] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1085 & 0xffff +#if 1085 > 0xffff + movk ip0, 1085 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcDbg_printValue + .p2align 4 + .type _XlcDbg_printValue, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcDbg_printValue +#endif +_XlcDbg_printValue: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8688 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8688] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1086 & 0xffff +#if 1086 > 0xffff + movk ip0, 1086 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcDeInitLoader + .p2align 4 + .type _XlcDeInitLoader, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcDeInitLoader +#endif +_XlcDeInitLoader: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8696 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8696] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1087 & 0xffff +#if 1087 > 0xffff + movk ip0, 1087 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcDefaultLoader + .p2align 4 + .type _XlcDefaultLoader, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcDefaultLoader +#endif +_XlcDefaultLoader: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8704 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8704] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1088 & 0xffff +#if 1088 > 0xffff + movk ip0, 1088 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcDefaultMapModifiers + .p2align 4 + .type _XlcDefaultMapModifiers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcDefaultMapModifiers +#endif +_XlcDefaultMapModifiers: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8712 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8712] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1089 & 0xffff +#if 1089 > 0xffff + movk ip0, 1089 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcDestroyLC + .p2align 4 + .type _XlcDestroyLC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcDestroyLC +#endif +_XlcDestroyLC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8720 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8720] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1090 & 0xffff +#if 1090 > 0xffff + movk ip0, 1090 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcDestroyLocaleDataBase + .p2align 4 + .type _XlcDestroyLocaleDataBase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcDestroyLocaleDataBase +#endif +_XlcDestroyLocaleDataBase: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8728 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8728] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1091 & 0xffff +#if 1091 > 0xffff + movk ip0, 1091 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcFileName + .p2align 4 + .type _XlcFileName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcFileName +#endif +_XlcFileName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8736 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8736] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1092 & 0xffff +#if 1092 > 0xffff + movk ip0, 1092 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcGenericLoader + .p2align 4 + .type _XlcGenericLoader, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcGenericLoader +#endif +_XlcGenericLoader: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8744 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8744] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1093 & 0xffff +#if 1093 > 0xffff + movk ip0, 1093 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcGetCSValues + .p2align 4 + .type _XlcGetCSValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcGetCSValues +#endif +_XlcGetCSValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8752 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8752] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1094 & 0xffff +#if 1094 > 0xffff + movk ip0, 1094 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcGetCharSet + .p2align 4 + .type _XlcGetCharSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcGetCharSet +#endif +_XlcGetCharSet: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8760 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8760] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1095 & 0xffff +#if 1095 > 0xffff + movk ip0, 1095 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcGetCharSetWithSide + .p2align 4 + .type _XlcGetCharSetWithSide, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcGetCharSetWithSide +#endif +_XlcGetCharSetWithSide: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8768 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8768] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1096 & 0xffff +#if 1096 > 0xffff + movk ip0, 1096 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcGetLocaleDataBase + .p2align 4 + .type _XlcGetLocaleDataBase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcGetLocaleDataBase +#endif +_XlcGetLocaleDataBase: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8776 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8776] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1097 & 0xffff +#if 1097 > 0xffff + movk ip0, 1097 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcGetResource + .p2align 4 + .type _XlcGetResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcGetResource +#endif +_XlcGetResource: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8784 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8784] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1098 & 0xffff +#if 1098 > 0xffff + movk ip0, 1098 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcGetValues + .p2align 4 + .type _XlcGetValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcGetValues +#endif +_XlcGetValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8792 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8792] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1099 & 0xffff +#if 1099 > 0xffff + movk ip0, 1099 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcInitCTInfo + .p2align 4 + .type _XlcInitCTInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcInitCTInfo +#endif +_XlcInitCTInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8800 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8800] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1100 & 0xffff +#if 1100 > 0xffff + movk ip0, 1100 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcInitLoader + .p2align 4 + .type _XlcInitLoader, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcInitLoader +#endif +_XlcInitLoader: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8808 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8808] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1101 & 0xffff +#if 1101 > 0xffff + movk ip0, 1101 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcLocaleDirName + .p2align 4 + .type _XlcLocaleDirName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcLocaleDirName +#endif +_XlcLocaleDirName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8816 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8816] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1102 & 0xffff +#if 1102 > 0xffff + movk ip0, 1102 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcLocaleLibDirName + .p2align 4 + .type _XlcLocaleLibDirName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcLocaleLibDirName +#endif +_XlcLocaleLibDirName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8824 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8824] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1103 & 0xffff +#if 1103 > 0xffff + movk ip0, 1103 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcMapOSLocaleName + .p2align 4 + .type _XlcMapOSLocaleName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcMapOSLocaleName +#endif +_XlcMapOSLocaleName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8832 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8832] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1104 & 0xffff +#if 1104 > 0xffff + movk ip0, 1104 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcNCompareISOLatin1 + .p2align 4 + .type _XlcNCompareISOLatin1, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcNCompareISOLatin1 +#endif +_XlcNCompareISOLatin1: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8840 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8840] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1105 & 0xffff +#if 1105 > 0xffff + movk ip0, 1105 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcOpenConverter + .p2align 4 + .type _XlcOpenConverter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcOpenConverter +#endif +_XlcOpenConverter: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8848 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8848] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1106 & 0xffff +#if 1106 > 0xffff + movk ip0, 1106 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcParseCharSet + .p2align 4 + .type _XlcParseCharSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcParseCharSet +#endif +_XlcParseCharSet: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8856 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8856] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1107 & 0xffff +#if 1107 > 0xffff + movk ip0, 1107 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcParse_scopemaps + .p2align 4 + .type _XlcParse_scopemaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcParse_scopemaps +#endif +_XlcParse_scopemaps: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8864 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8864] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1108 & 0xffff +#if 1108 > 0xffff + movk ip0, 1108 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcRemoveLoader + .p2align 4 + .type _XlcRemoveLoader, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcRemoveLoader +#endif +_XlcRemoveLoader: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8872 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8872] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1109 & 0xffff +#if 1109 > 0xffff + movk ip0, 1109 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcResetConverter + .p2align 4 + .type _XlcResetConverter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcResetConverter +#endif +_XlcResetConverter: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8880 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8880] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1110 & 0xffff +#if 1110 > 0xffff + movk ip0, 1110 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcResolveI18NPath + .p2align 4 + .type _XlcResolveI18NPath, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcResolveI18NPath +#endif +_XlcResolveI18NPath: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8888 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8888] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1111 & 0xffff +#if 1111 > 0xffff + movk ip0, 1111 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcResolveLocaleName + .p2align 4 + .type _XlcResolveLocaleName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcResolveLocaleName +#endif +_XlcResolveLocaleName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8896 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8896] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1112 & 0xffff +#if 1112 > 0xffff + movk ip0, 1112 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcSetConverter + .p2align 4 + .type _XlcSetConverter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcSetConverter +#endif +_XlcSetConverter: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8904 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8904] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1113 & 0xffff +#if 1113 > 0xffff + movk ip0, 1113 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcSetValues + .p2align 4 + .type _XlcSetValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcSetValues +#endif +_XlcSetValues: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8912 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8912] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1114 & 0xffff +#if 1114 > 0xffff + movk ip0, 1114 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcUtf8Loader + .p2align 4 + .type _XlcUtf8Loader, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcUtf8Loader +#endif +_XlcUtf8Loader: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8920 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8920] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1115 & 0xffff +#if 1115 > 0xffff + movk ip0, 1115 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcVaToArgList + .p2align 4 + .type _XlcVaToArgList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcVaToArgList +#endif +_XlcVaToArgList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8928 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8928] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1116 & 0xffff +#if 1116 > 0xffff + movk ip0, 1116 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcValidModSyntax + .p2align 4 + .type _XlcValidModSyntax, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcValidModSyntax +#endif +_XlcValidModSyntax: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8936 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8936] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1117 & 0xffff +#if 1117 > 0xffff + movk ip0, 1117 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xlcmbstoutf8 + .p2align 4 + .type _Xlcmbstoutf8, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xlcmbstoutf8 +#endif +_Xlcmbstoutf8: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8944 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8944] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1118 & 0xffff +#if 1118 > 0xffff + movk ip0, 1118 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xlcmbstowcs + .p2align 4 + .type _Xlcmbstowcs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xlcmbstowcs +#endif +_Xlcmbstowcs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8952 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8952] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1119 & 0xffff +#if 1119 > 0xffff + movk ip0, 1119 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xlcmbtowc + .p2align 4 + .type _Xlcmbtowc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xlcmbtowc +#endif +_Xlcmbtowc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8960 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8960] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1120 & 0xffff +#if 1120 > 0xffff + movk ip0, 1120 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xlcwcstombs + .p2align 4 + .type _Xlcwcstombs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xlcwcstombs +#endif +_Xlcwcstombs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8968 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8968] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1121 & 0xffff +#if 1121 > 0xffff + movk ip0, 1121 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xlcwctomb + .p2align 4 + .type _Xlcwctomb, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xlcwctomb +#endif +_Xlcwctomb: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8976 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8976] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1122 & 0xffff +#if 1122 > 0xffff + movk ip0, 1122 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbDefaultDrawImageString + .p2align 4 + .type _XmbDefaultDrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbDefaultDrawImageString +#endif +_XmbDefaultDrawImageString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8984 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8984] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1123 & 0xffff +#if 1123 > 0xffff + movk ip0, 1123 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbDefaultDrawString + .p2align 4 + .type _XmbDefaultDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbDefaultDrawString +#endif +_XmbDefaultDrawString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+8992 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+8992] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1124 & 0xffff +#if 1124 > 0xffff + movk ip0, 1124 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbDefaultTextEscapement + .p2align 4 + .type _XmbDefaultTextEscapement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbDefaultTextEscapement +#endif +_XmbDefaultTextEscapement: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9000 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9000] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1125 & 0xffff +#if 1125 > 0xffff + movk ip0, 1125 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbDefaultTextExtents + .p2align 4 + .type _XmbDefaultTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbDefaultTextExtents +#endif +_XmbDefaultTextExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9008 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9008] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1126 & 0xffff +#if 1126 > 0xffff + movk ip0, 1126 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbDefaultTextPerCharExtents + .p2align 4 + .type _XmbDefaultTextPerCharExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbDefaultTextPerCharExtents +#endif +_XmbDefaultTextPerCharExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9016 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9016] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1127 & 0xffff +#if 1127 > 0xffff + movk ip0, 1127 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbGenericDrawImageString + .p2align 4 + .type _XmbGenericDrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbGenericDrawImageString +#endif +_XmbGenericDrawImageString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9024 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9024] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1128 & 0xffff +#if 1128 > 0xffff + movk ip0, 1128 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbGenericDrawString + .p2align 4 + .type _XmbGenericDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbGenericDrawString +#endif +_XmbGenericDrawString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9032 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9032] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1129 & 0xffff +#if 1129 > 0xffff + movk ip0, 1129 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbGenericTextEscapement + .p2align 4 + .type _XmbGenericTextEscapement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbGenericTextEscapement +#endif +_XmbGenericTextEscapement: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9040 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9040] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1130 & 0xffff +#if 1130 > 0xffff + movk ip0, 1130 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbGenericTextExtents + .p2align 4 + .type _XmbGenericTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbGenericTextExtents +#endif +_XmbGenericTextExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9048 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9048] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1131 & 0xffff +#if 1131 > 0xffff + movk ip0, 1131 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbGenericTextPerCharExtents + .p2align 4 + .type _XmbGenericTextPerCharExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbGenericTextPerCharExtents +#endif +_XmbGenericTextPerCharExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9056 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9056] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1132 & 0xffff +#if 1132 > 0xffff + movk ip0, 1132 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbTextListToTextProperty + .p2align 4 + .type _XmbTextListToTextProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbTextListToTextProperty +#endif +_XmbTextListToTextProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9064 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9064] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1133 & 0xffff +#if 1133 > 0xffff + movk ip0, 1133 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbTextPropertyToTextList + .p2align 4 + .type _XmbTextPropertyToTextList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbTextPropertyToTextList +#endif +_XmbTextPropertyToTextList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9072 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9072] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1134 & 0xffff +#if 1134 > 0xffff + movk ip0, 1134 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xmblen + .p2align 4 + .type _Xmblen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xmblen +#endif +_Xmblen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9080 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9080] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1135 & 0xffff +#if 1135 > 0xffff + movk ip0, 1135 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xmbstoutf8 + .p2align 4 + .type _Xmbstoutf8, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xmbstoutf8 +#endif +_Xmbstoutf8: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9088 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9088] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1136 & 0xffff +#if 1136 > 0xffff + movk ip0, 1136 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xmbstowcs + .p2align 4 + .type _Xmbstowcs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xmbstowcs +#endif +_Xmbstowcs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9096 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9096] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1137 & 0xffff +#if 1137 > 0xffff + movk ip0, 1137 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xmbtowc + .p2align 4 + .type _Xmbtowc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xmbtowc +#endif +_Xmbtowc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9104 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1138 & 0xffff +#if 1138 > 0xffff + movk ip0, 1138 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XomConvert + .p2align 4 + .type _XomConvert, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XomConvert +#endif +_XomConvert: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9112 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1139 & 0xffff +#if 1139 > 0xffff + movk ip0, 1139 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XomGenericDrawString + .p2align 4 + .type _XomGenericDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XomGenericDrawString +#endif +_XomGenericDrawString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9120 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1140 & 0xffff +#if 1140 > 0xffff + movk ip0, 1140 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XomGenericOpenOM + .p2align 4 + .type _XomGenericOpenOM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XomGenericOpenOM +#endif +_XomGenericOpenOM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9128 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1141 & 0xffff +#if 1141 > 0xffff + movk ip0, 1141 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XomGenericTextExtents + .p2align 4 + .type _XomGenericTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XomGenericTextExtents +#endif +_XomGenericTextExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9136 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1142 & 0xffff +#if 1142 > 0xffff + movk ip0, 1142 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XomGetFontDataFromFontSet + .p2align 4 + .type _XomGetFontDataFromFontSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XomGetFontDataFromFontSet +#endif +_XomGetFontDataFromFontSet: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9144 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1143 & 0xffff +#if 1143 > 0xffff + movk ip0, 1143 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XomInitConverter + .p2align 4 + .type _XomInitConverter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XomInitConverter +#endif +_XomInitConverter: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9152 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1144 & 0xffff +#if 1144 > 0xffff + movk ip0, 1144 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XrmDefaultInitParseInfo + .p2align 4 + .type _XrmDefaultInitParseInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XrmDefaultInitParseInfo +#endif +_XrmDefaultInitParseInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9160 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1145 & 0xffff +#if 1145 > 0xffff + movk ip0, 1145 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XrmInitParseInfo + .p2align 4 + .type _XrmInitParseInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XrmInitParseInfo +#endif +_XrmInitParseInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9168 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1146 & 0xffff +#if 1146 > 0xffff + movk ip0, 1146 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XrmInternalStringToQuark + .p2align 4 + .type _XrmInternalStringToQuark, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XrmInternalStringToQuark +#endif +_XrmInternalStringToQuark: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9176 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1147 & 0xffff +#if 1147 > 0xffff + movk ip0, 1147 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8DefaultDrawImageString + .p2align 4 + .type _Xutf8DefaultDrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8DefaultDrawImageString +#endif +_Xutf8DefaultDrawImageString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9184 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1148 & 0xffff +#if 1148 > 0xffff + movk ip0, 1148 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8DefaultDrawString + .p2align 4 + .type _Xutf8DefaultDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8DefaultDrawString +#endif +_Xutf8DefaultDrawString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9192 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1149 & 0xffff +#if 1149 > 0xffff + movk ip0, 1149 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8DefaultTextEscapement + .p2align 4 + .type _Xutf8DefaultTextEscapement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8DefaultTextEscapement +#endif +_Xutf8DefaultTextEscapement: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9200 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1150 & 0xffff +#if 1150 > 0xffff + movk ip0, 1150 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8DefaultTextExtents + .p2align 4 + .type _Xutf8DefaultTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8DefaultTextExtents +#endif +_Xutf8DefaultTextExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9208 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1151 & 0xffff +#if 1151 > 0xffff + movk ip0, 1151 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8DefaultTextPerCharExtents + .p2align 4 + .type _Xutf8DefaultTextPerCharExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8DefaultTextPerCharExtents +#endif +_Xutf8DefaultTextPerCharExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9216 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1152 & 0xffff +#if 1152 > 0xffff + movk ip0, 1152 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8GenericDrawImageString + .p2align 4 + .type _Xutf8GenericDrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8GenericDrawImageString +#endif +_Xutf8GenericDrawImageString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9224 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1153 & 0xffff +#if 1153 > 0xffff + movk ip0, 1153 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8GenericDrawString + .p2align 4 + .type _Xutf8GenericDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8GenericDrawString +#endif +_Xutf8GenericDrawString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9232 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1154 & 0xffff +#if 1154 > 0xffff + movk ip0, 1154 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8GenericTextEscapement + .p2align 4 + .type _Xutf8GenericTextEscapement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8GenericTextEscapement +#endif +_Xutf8GenericTextEscapement: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9240 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1155 & 0xffff +#if 1155 > 0xffff + movk ip0, 1155 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8GenericTextExtents + .p2align 4 + .type _Xutf8GenericTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8GenericTextExtents +#endif +_Xutf8GenericTextExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9248 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1156 & 0xffff +#if 1156 > 0xffff + movk ip0, 1156 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8GenericTextPerCharExtents + .p2align 4 + .type _Xutf8GenericTextPerCharExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8GenericTextPerCharExtents +#endif +_Xutf8GenericTextPerCharExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9256 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1157 & 0xffff +#if 1157 > 0xffff + movk ip0, 1157 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8TextListToTextProperty + .p2align 4 + .type _Xutf8TextListToTextProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8TextListToTextProperty +#endif +_Xutf8TextListToTextProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9264 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1158 & 0xffff +#if 1158 > 0xffff + movk ip0, 1158 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8TextPropertyToTextList + .p2align 4 + .type _Xutf8TextPropertyToTextList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8TextPropertyToTextList +#endif +_Xutf8TextPropertyToTextList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9272 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1159 & 0xffff +#if 1159 > 0xffff + movk ip0, 1159 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcDefaultDrawImageString + .p2align 4 + .type _XwcDefaultDrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcDefaultDrawImageString +#endif +_XwcDefaultDrawImageString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9280 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1160 & 0xffff +#if 1160 > 0xffff + movk ip0, 1160 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcDefaultDrawString + .p2align 4 + .type _XwcDefaultDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcDefaultDrawString +#endif +_XwcDefaultDrawString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9288 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1161 & 0xffff +#if 1161 > 0xffff + movk ip0, 1161 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcDefaultTextEscapement + .p2align 4 + .type _XwcDefaultTextEscapement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcDefaultTextEscapement +#endif +_XwcDefaultTextEscapement: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9296 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1162 & 0xffff +#if 1162 > 0xffff + movk ip0, 1162 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcDefaultTextExtents + .p2align 4 + .type _XwcDefaultTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcDefaultTextExtents +#endif +_XwcDefaultTextExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9304 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1163 & 0xffff +#if 1163 > 0xffff + movk ip0, 1163 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcDefaultTextPerCharExtents + .p2align 4 + .type _XwcDefaultTextPerCharExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcDefaultTextPerCharExtents +#endif +_XwcDefaultTextPerCharExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9312 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1164 & 0xffff +#if 1164 > 0xffff + movk ip0, 1164 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcFreeStringList + .p2align 4 + .type _XwcFreeStringList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcFreeStringList +#endif +_XwcFreeStringList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9320 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1165 & 0xffff +#if 1165 > 0xffff + movk ip0, 1165 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcGenericDrawImageString + .p2align 4 + .type _XwcGenericDrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcGenericDrawImageString +#endif +_XwcGenericDrawImageString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9328 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1166 & 0xffff +#if 1166 > 0xffff + movk ip0, 1166 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcGenericDrawString + .p2align 4 + .type _XwcGenericDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcGenericDrawString +#endif +_XwcGenericDrawString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9336 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1167 & 0xffff +#if 1167 > 0xffff + movk ip0, 1167 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcGenericTextEscapement + .p2align 4 + .type _XwcGenericTextEscapement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcGenericTextEscapement +#endif +_XwcGenericTextEscapement: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9344 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1168 & 0xffff +#if 1168 > 0xffff + movk ip0, 1168 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcGenericTextExtents + .p2align 4 + .type _XwcGenericTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcGenericTextExtents +#endif +_XwcGenericTextExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9352 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1169 & 0xffff +#if 1169 > 0xffff + movk ip0, 1169 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcGenericTextPerCharExtents + .p2align 4 + .type _XwcGenericTextPerCharExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcGenericTextPerCharExtents +#endif +_XwcGenericTextPerCharExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9360 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1170 & 0xffff +#if 1170 > 0xffff + movk ip0, 1170 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcTextListToTextProperty + .p2align 4 + .type _XwcTextListToTextProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcTextListToTextProperty +#endif +_XwcTextListToTextProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9368 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1171 & 0xffff +#if 1171 > 0xffff + movk ip0, 1171 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcTextPropertyToTextList + .p2align 4 + .type _XwcTextPropertyToTextList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcTextPropertyToTextList +#endif +_XwcTextPropertyToTextList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9376 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1172 & 0xffff +#if 1172 > 0xffff + movk ip0, 1172 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xwcscmp + .p2align 4 + .type _Xwcscmp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xwcscmp +#endif +_Xwcscmp: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9384 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1173 & 0xffff +#if 1173 > 0xffff + movk ip0, 1173 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xwcscpy + .p2align 4 + .type _Xwcscpy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xwcscpy +#endif +_Xwcscpy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9392 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1174 & 0xffff +#if 1174 > 0xffff + movk ip0, 1174 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xwcslen + .p2align 4 + .type _Xwcslen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xwcslen +#endif +_Xwcslen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9400 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1175 & 0xffff +#if 1175 > 0xffff + movk ip0, 1175 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xwcsncmp + .p2align 4 + .type _Xwcsncmp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xwcsncmp +#endif +_Xwcsncmp: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9408 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1176 & 0xffff +#if 1176 > 0xffff + movk ip0, 1176 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xwcsncpy + .p2align 4 + .type _Xwcsncpy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xwcsncpy +#endif +_Xwcsncpy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9416 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1177 & 0xffff +#if 1177 > 0xffff + movk ip0, 1177 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xwcstombs + .p2align 4 + .type _Xwcstombs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xwcstombs +#endif +_Xwcstombs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9424 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1178 & 0xffff +#if 1178 > 0xffff + movk ip0, 1178 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xwctomb + .p2align 4 + .type _Xwctomb, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xwctomb +#endif +_Xwctomb: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9432 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1179 & 0xffff +#if 1179 > 0xffff + movk ip0, 1179 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl read_EncodingInfo + .p2align 4 + .type read_EncodingInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden read_EncodingInfo +#endif +read_EncodingInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9440 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1180 & 0xffff +#if 1180 > 0xffff + movk ip0, 1180 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl xlocaledir + .p2align 4 + .type xlocaledir, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden xlocaledir +#endif +xlocaledir: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libX11_so_tramp_table+9448 + ldr ip0, [ip0, #:lo12:_libX11_so_tramp_table+9448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1181 & 0xffff +#if 1181 > 0xffff + movk ip0, 1181 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libX11_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXcomposite.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXcomposite.so.init.c new file mode 100644 index 0000000..bd319a5 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXcomposite.so.init.c @@ -0,0 +1,256 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libXcomposite.so.1: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libXcomposite.so.1"); + CHECK(lib_handle, "failed to load library 'libXcomposite.so.1' via callback ''"); +#else + lib_handle = dlopen("libXcomposite.so.1", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libXcomposite.so.1' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "XCompositeCreateRegionFromBorderClip", + "XCompositeFindDisplay", + "XCompositeGetOverlayWindow", + "XCompositeNameWindowPixmap", + "XCompositeQueryExtension", + "XCompositeQueryVersion", + "XCompositeRedirectSubwindows", + "XCompositeRedirectWindow", + "XCompositeReleaseOverlayWindow", + "XCompositeUnredirectSubwindows", + "XCompositeUnredirectWindow", + "XCompositeVersion", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libXcomposite_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libXcomposite_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libXcomposite_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libXcomposite_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libXcomposite_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libXcomposite_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libXcomposite_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libXcomposite_so_tramp_table, 0, SYM_COUNT * sizeof(_libXcomposite_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXcomposite.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXcomposite.so.tramp.S new file mode 100644 index 0000000..c1263f9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXcomposite.so.tramp.S @@ -0,0 +1,573 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#define lr x30 +#define ip0 x16 + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libXcomposite_so_tramp_table + .hidden _libXcomposite_so_tramp_table + .align 8 +_libXcomposite_so_tramp_table: + .zero 104 + + .text + + .globl _libXcomposite_so_tramp_resolve + .hidden _libXcomposite_so_tramp_resolve + + .globl _libXcomposite_so_save_regs_and_resolve + .hidden _libXcomposite_so_save_regs_and_resolve + .type _libXcomposite_so_save_regs_and_resolve, %function +_libXcomposite_so_save_regs_and_resolve: + .cfi_startproc + + // Slow path which calls dlsym, taken only on first call. + // Registers are saved according to "Procedure Call Standard for the Arm® 64-bit Architecture". + // For DWARF directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + // Stack is aligned at 16 bytes + +#define PUSH_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 8 +#define POP_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore reg2; .cfi_restore reg1 + +#define PUSH_WIDE_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-32]!; .cfi_adjust_cfa_offset 32; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 16 +#define POP_WIDE_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #32; .cfi_adjust_cfa_offset -32; .cfi_restore reg2; .cfi_restore reg1 + + // Save only arguments (and lr) + PUSH_PAIR(x0, x1) + PUSH_PAIR(x2, x3) + PUSH_PAIR(x4, x5) + PUSH_PAIR(x6, x7) + PUSH_PAIR(x8, lr) + + ldr x0, [sp, #80] // 16*5 + + PUSH_WIDE_PAIR(q0, q1) + PUSH_WIDE_PAIR(q2, q3) + PUSH_WIDE_PAIR(q4, q5) + PUSH_WIDE_PAIR(q6, q7) + + // Stack is aligned at 16 bytes + + bl _libXcomposite_so_tramp_resolve + mov ip0, x0 + + // TODO: pop pc? + + POP_WIDE_PAIR(q6, q7) + POP_WIDE_PAIR(q4, q5) + POP_WIDE_PAIR(q2, q3) + POP_WIDE_PAIR(q0, q1) + + POP_PAIR(x8, lr) + POP_PAIR(x6, x7) + POP_PAIR(x4, x5) + POP_PAIR(x2, x3) + POP_PAIR(x0, x1) + + br lr + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeCreateRegionFromBorderClip + .p2align 4 + .type XCompositeCreateRegionFromBorderClip, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeCreateRegionFromBorderClip +#endif +XCompositeCreateRegionFromBorderClip: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXcomposite_so_tramp_table+0 + ldr ip0, [ip0, #:lo12:_libXcomposite_so_tramp_table+0] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 0 & 0xffff +#if 0 > 0xffff + movk ip0, 0 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXcomposite_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeFindDisplay + .p2align 4 + .type XCompositeFindDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeFindDisplay +#endif +XCompositeFindDisplay: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXcomposite_so_tramp_table+8 + ldr ip0, [ip0, #:lo12:_libXcomposite_so_tramp_table+8] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1 & 0xffff +#if 1 > 0xffff + movk ip0, 1 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXcomposite_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeGetOverlayWindow + .p2align 4 + .type XCompositeGetOverlayWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeGetOverlayWindow +#endif +XCompositeGetOverlayWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXcomposite_so_tramp_table+16 + ldr ip0, [ip0, #:lo12:_libXcomposite_so_tramp_table+16] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 2 & 0xffff +#if 2 > 0xffff + movk ip0, 2 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXcomposite_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeNameWindowPixmap + .p2align 4 + .type XCompositeNameWindowPixmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeNameWindowPixmap +#endif +XCompositeNameWindowPixmap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXcomposite_so_tramp_table+24 + ldr ip0, [ip0, #:lo12:_libXcomposite_so_tramp_table+24] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 3 & 0xffff +#if 3 > 0xffff + movk ip0, 3 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXcomposite_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeQueryExtension + .p2align 4 + .type XCompositeQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeQueryExtension +#endif +XCompositeQueryExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXcomposite_so_tramp_table+32 + ldr ip0, [ip0, #:lo12:_libXcomposite_so_tramp_table+32] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 4 & 0xffff +#if 4 > 0xffff + movk ip0, 4 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXcomposite_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeQueryVersion + .p2align 4 + .type XCompositeQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeQueryVersion +#endif +XCompositeQueryVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXcomposite_so_tramp_table+40 + ldr ip0, [ip0, #:lo12:_libXcomposite_so_tramp_table+40] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 5 & 0xffff +#if 5 > 0xffff + movk ip0, 5 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXcomposite_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeRedirectSubwindows + .p2align 4 + .type XCompositeRedirectSubwindows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeRedirectSubwindows +#endif +XCompositeRedirectSubwindows: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXcomposite_so_tramp_table+48 + ldr ip0, [ip0, #:lo12:_libXcomposite_so_tramp_table+48] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 6 & 0xffff +#if 6 > 0xffff + movk ip0, 6 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXcomposite_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeRedirectWindow + .p2align 4 + .type XCompositeRedirectWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeRedirectWindow +#endif +XCompositeRedirectWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXcomposite_so_tramp_table+56 + ldr ip0, [ip0, #:lo12:_libXcomposite_so_tramp_table+56] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 7 & 0xffff +#if 7 > 0xffff + movk ip0, 7 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXcomposite_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeReleaseOverlayWindow + .p2align 4 + .type XCompositeReleaseOverlayWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeReleaseOverlayWindow +#endif +XCompositeReleaseOverlayWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXcomposite_so_tramp_table+64 + ldr ip0, [ip0, #:lo12:_libXcomposite_so_tramp_table+64] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 8 & 0xffff +#if 8 > 0xffff + movk ip0, 8 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXcomposite_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeUnredirectSubwindows + .p2align 4 + .type XCompositeUnredirectSubwindows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeUnredirectSubwindows +#endif +XCompositeUnredirectSubwindows: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXcomposite_so_tramp_table+72 + ldr ip0, [ip0, #:lo12:_libXcomposite_so_tramp_table+72] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 9 & 0xffff +#if 9 > 0xffff + movk ip0, 9 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXcomposite_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeUnredirectWindow + .p2align 4 + .type XCompositeUnredirectWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeUnredirectWindow +#endif +XCompositeUnredirectWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXcomposite_so_tramp_table+80 + ldr ip0, [ip0, #:lo12:_libXcomposite_so_tramp_table+80] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 10 & 0xffff +#if 10 > 0xffff + movk ip0, 10 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXcomposite_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeVersion + .p2align 4 + .type XCompositeVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeVersion +#endif +XCompositeVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXcomposite_so_tramp_table+88 + ldr ip0, [ip0, #:lo12:_libXcomposite_so_tramp_table+88] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 11 & 0xffff +#if 11 > 0xffff + movk ip0, 11 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXcomposite_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXdamage.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXdamage.so.init.c new file mode 100644 index 0000000..267ca69 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXdamage.so.init.c @@ -0,0 +1,251 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libXdamage.so.1: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libXdamage.so.1"); + CHECK(lib_handle, "failed to load library 'libXdamage.so.1' via callback ''"); +#else + lib_handle = dlopen("libXdamage.so.1", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libXdamage.so.1' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "XDamageAdd", + "XDamageCreate", + "XDamageDestroy", + "XDamageFindDisplay", + "XDamageQueryExtension", + "XDamageQueryVersion", + "XDamageSubtract", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libXdamage_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libXdamage_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libXdamage_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libXdamage_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libXdamage_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libXdamage_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libXdamage_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libXdamage_so_tramp_table, 0, SYM_COUNT * sizeof(_libXdamage_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXdamage.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXdamage.so.tramp.S new file mode 100644 index 0000000..6e40b41 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXdamage.so.tramp.S @@ -0,0 +1,368 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#define lr x30 +#define ip0 x16 + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libXdamage_so_tramp_table + .hidden _libXdamage_so_tramp_table + .align 8 +_libXdamage_so_tramp_table: + .zero 64 + + .text + + .globl _libXdamage_so_tramp_resolve + .hidden _libXdamage_so_tramp_resolve + + .globl _libXdamage_so_save_regs_and_resolve + .hidden _libXdamage_so_save_regs_and_resolve + .type _libXdamage_so_save_regs_and_resolve, %function +_libXdamage_so_save_regs_and_resolve: + .cfi_startproc + + // Slow path which calls dlsym, taken only on first call. + // Registers are saved according to "Procedure Call Standard for the Arm® 64-bit Architecture". + // For DWARF directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + // Stack is aligned at 16 bytes + +#define PUSH_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 8 +#define POP_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore reg2; .cfi_restore reg1 + +#define PUSH_WIDE_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-32]!; .cfi_adjust_cfa_offset 32; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 16 +#define POP_WIDE_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #32; .cfi_adjust_cfa_offset -32; .cfi_restore reg2; .cfi_restore reg1 + + // Save only arguments (and lr) + PUSH_PAIR(x0, x1) + PUSH_PAIR(x2, x3) + PUSH_PAIR(x4, x5) + PUSH_PAIR(x6, x7) + PUSH_PAIR(x8, lr) + + ldr x0, [sp, #80] // 16*5 + + PUSH_WIDE_PAIR(q0, q1) + PUSH_WIDE_PAIR(q2, q3) + PUSH_WIDE_PAIR(q4, q5) + PUSH_WIDE_PAIR(q6, q7) + + // Stack is aligned at 16 bytes + + bl _libXdamage_so_tramp_resolve + mov ip0, x0 + + // TODO: pop pc? + + POP_WIDE_PAIR(q6, q7) + POP_WIDE_PAIR(q4, q5) + POP_WIDE_PAIR(q2, q3) + POP_WIDE_PAIR(q0, q1) + + POP_PAIR(x8, lr) + POP_PAIR(x6, x7) + POP_PAIR(x4, x5) + POP_PAIR(x2, x3) + POP_PAIR(x0, x1) + + br lr + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDamageAdd + .p2align 4 + .type XDamageAdd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDamageAdd +#endif +XDamageAdd: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXdamage_so_tramp_table+0 + ldr ip0, [ip0, #:lo12:_libXdamage_so_tramp_table+0] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 0 & 0xffff +#if 0 > 0xffff + movk ip0, 0 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXdamage_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDamageCreate + .p2align 4 + .type XDamageCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDamageCreate +#endif +XDamageCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXdamage_so_tramp_table+8 + ldr ip0, [ip0, #:lo12:_libXdamage_so_tramp_table+8] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1 & 0xffff +#if 1 > 0xffff + movk ip0, 1 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXdamage_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDamageDestroy + .p2align 4 + .type XDamageDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDamageDestroy +#endif +XDamageDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXdamage_so_tramp_table+16 + ldr ip0, [ip0, #:lo12:_libXdamage_so_tramp_table+16] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 2 & 0xffff +#if 2 > 0xffff + movk ip0, 2 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXdamage_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDamageFindDisplay + .p2align 4 + .type XDamageFindDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDamageFindDisplay +#endif +XDamageFindDisplay: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXdamage_so_tramp_table+24 + ldr ip0, [ip0, #:lo12:_libXdamage_so_tramp_table+24] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 3 & 0xffff +#if 3 > 0xffff + movk ip0, 3 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXdamage_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDamageQueryExtension + .p2align 4 + .type XDamageQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDamageQueryExtension +#endif +XDamageQueryExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXdamage_so_tramp_table+32 + ldr ip0, [ip0, #:lo12:_libXdamage_so_tramp_table+32] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 4 & 0xffff +#if 4 > 0xffff + movk ip0, 4 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXdamage_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDamageQueryVersion + .p2align 4 + .type XDamageQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDamageQueryVersion +#endif +XDamageQueryVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXdamage_so_tramp_table+40 + ldr ip0, [ip0, #:lo12:_libXdamage_so_tramp_table+40] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 5 & 0xffff +#if 5 > 0xffff + movk ip0, 5 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXdamage_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDamageSubtract + .p2align 4 + .type XDamageSubtract, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDamageSubtract +#endif +XDamageSubtract: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXdamage_so_tramp_table+48 + ldr ip0, [ip0, #:lo12:_libXdamage_so_tramp_table+48] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 6 & 0xffff +#if 6 > 0xffff + movk ip0, 6 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXdamage_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXext.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXext.so.init.c new file mode 100644 index 0000000..1b1166d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXext.so.init.c @@ -0,0 +1,376 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libXext.so.6: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libXext.so.6"); + CHECK(lib_handle, "failed to load library 'libXext.so.6' via callback ''"); +#else + lib_handle = dlopen("libXext.so.6", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libXext.so.6' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "DPMSCapable", + "DPMSDisable", + "DPMSEnable", + "DPMSForceLevel", + "DPMSGetTimeouts", + "DPMSGetVersion", + "DPMSInfo", + "DPMSQueryExtension", + "DPMSSetTimeouts", + "XGEQueryExtension", + "XGEQueryVersion", + "XLbxGetEventBase", + "XLbxQueryExtension", + "XLbxQueryVersion", + "XMITMiscGetBugMode", + "XMITMiscQueryExtension", + "XMITMiscSetBugMode", + "XMissingExtension", + "XSecurityAllocXauth", + "XSecurityFreeXauth", + "XSecurityGenerateAuthorization", + "XSecurityQueryExtension", + "XSecurityRevokeAuthorization", + "XSetExtensionErrorHandler", + "XShapeCombineMask", + "XShapeCombineRectangles", + "XShapeCombineRegion", + "XShapeCombineShape", + "XShapeGetRectangles", + "XShapeInputSelected", + "XShapeOffsetShape", + "XShapeQueryExtension", + "XShapeQueryExtents", + "XShapeQueryVersion", + "XShapeSelectInput", + "XShmAttach", + "XShmCreateImage", + "XShmCreatePixmap", + "XShmDetach", + "XShmGetEventBase", + "XShmGetImage", + "XShmPixmapFormat", + "XShmPutImage", + "XShmQueryExtension", + "XShmQueryVersion", + "XSyncAwait", + "XSyncAwaitFence", + "XSyncChangeAlarm", + "XSyncChangeCounter", + "XSyncCreateAlarm", + "XSyncCreateCounter", + "XSyncCreateFence", + "XSyncDestroyAlarm", + "XSyncDestroyCounter", + "XSyncDestroyFence", + "XSyncFreeSystemCounterList", + "XSyncGetPriority", + "XSyncInitialize", + "XSyncIntToValue", + "XSyncIntsToValue", + "XSyncListSystemCounters", + "XSyncMaxValue", + "XSyncMinValue", + "XSyncQueryAlarm", + "XSyncQueryCounter", + "XSyncQueryExtension", + "XSyncQueryFence", + "XSyncResetFence", + "XSyncSetCounter", + "XSyncSetPriority", + "XSyncTriggerFence", + "XSyncValueAdd", + "XSyncValueEqual", + "XSyncValueGreaterOrEqual", + "XSyncValueGreaterThan", + "XSyncValueHigh32", + "XSyncValueIsNegative", + "XSyncValueIsPositive", + "XSyncValueIsZero", + "XSyncValueLessOrEqual", + "XSyncValueLessThan", + "XSyncValueLow32", + "XSyncValueSubtract", + "XTestFakeInput", + "XTestFlush", + "XTestGetInput", + "XTestMovePointer", + "XTestPressButton", + "XTestPressKey", + "XTestQueryInputSize", + "XTestReset", + "XTestStopInput", + "XagCreateAssociation", + "XagCreateEmbeddedApplicationGroup", + "XagCreateNonembeddedApplicationGroup", + "XagDestroyApplicationGroup", + "XagDestroyAssociation", + "XagGetApplicationGroupAttributes", + "XagQueryApplicationGroup", + "XagQueryVersion", + "XcupGetReservedColormapEntries", + "XcupQueryVersion", + "XcupStoreColors", + "XdbeAllocateBackBufferName", + "XdbeBeginIdiom", + "XdbeDeallocateBackBufferName", + "XdbeEndIdiom", + "XdbeFreeVisualInfo", + "XdbeGetBackBufferAttributes", + "XdbeGetVisualInfo", + "XdbeQueryExtension", + "XdbeSwapBuffers", + "XeviGetVisualInfo", + "XeviQueryExtension", + "XeviQueryVersion", + "XextAddDisplay", + "XextCreateExtension", + "XextDestroyExtension", + "XextFindDisplay", + "XextRemoveDisplay", + "XmbufChangeBufferAttributes", + "XmbufChangeWindowAttributes", + "XmbufClearBufferArea", + "XmbufCreateBuffers", + "XmbufCreateStereoWindow", + "XmbufDestroyBuffers", + "XmbufDisplayBuffers", + "XmbufGetBufferAttributes", + "XmbufGetScreenInfo", + "XmbufGetVersion", + "XmbufGetWindowAttributes", + "XmbufQueryExtension", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libXext_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libXext_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libXext_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libXext_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libXext_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libXext_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libXext_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libXext_so_tramp_table, 0, SYM_COUNT * sizeof(_libXext_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXext.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXext.so.tramp.S new file mode 100644 index 0000000..43fa735 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXext.so.tramp.S @@ -0,0 +1,5493 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#define lr x30 +#define ip0 x16 + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libXext_so_tramp_table + .hidden _libXext_so_tramp_table + .align 8 +_libXext_so_tramp_table: + .zero 1064 + + .text + + .globl _libXext_so_tramp_resolve + .hidden _libXext_so_tramp_resolve + + .globl _libXext_so_save_regs_and_resolve + .hidden _libXext_so_save_regs_and_resolve + .type _libXext_so_save_regs_and_resolve, %function +_libXext_so_save_regs_and_resolve: + .cfi_startproc + + // Slow path which calls dlsym, taken only on first call. + // Registers are saved according to "Procedure Call Standard for the Arm® 64-bit Architecture". + // For DWARF directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + // Stack is aligned at 16 bytes + +#define PUSH_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 8 +#define POP_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore reg2; .cfi_restore reg1 + +#define PUSH_WIDE_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-32]!; .cfi_adjust_cfa_offset 32; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 16 +#define POP_WIDE_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #32; .cfi_adjust_cfa_offset -32; .cfi_restore reg2; .cfi_restore reg1 + + // Save only arguments (and lr) + PUSH_PAIR(x0, x1) + PUSH_PAIR(x2, x3) + PUSH_PAIR(x4, x5) + PUSH_PAIR(x6, x7) + PUSH_PAIR(x8, lr) + + ldr x0, [sp, #80] // 16*5 + + PUSH_WIDE_PAIR(q0, q1) + PUSH_WIDE_PAIR(q2, q3) + PUSH_WIDE_PAIR(q4, q5) + PUSH_WIDE_PAIR(q6, q7) + + // Stack is aligned at 16 bytes + + bl _libXext_so_tramp_resolve + mov ip0, x0 + + // TODO: pop pc? + + POP_WIDE_PAIR(q6, q7) + POP_WIDE_PAIR(q4, q5) + POP_WIDE_PAIR(q2, q3) + POP_WIDE_PAIR(q0, q1) + + POP_PAIR(x8, lr) + POP_PAIR(x6, x7) + POP_PAIR(x4, x5) + POP_PAIR(x2, x3) + POP_PAIR(x0, x1) + + br lr + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl DPMSCapable + .p2align 4 + .type DPMSCapable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden DPMSCapable +#endif +DPMSCapable: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+0 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+0] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 0 & 0xffff +#if 0 > 0xffff + movk ip0, 0 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl DPMSDisable + .p2align 4 + .type DPMSDisable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden DPMSDisable +#endif +DPMSDisable: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+8 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+8] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1 & 0xffff +#if 1 > 0xffff + movk ip0, 1 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl DPMSEnable + .p2align 4 + .type DPMSEnable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden DPMSEnable +#endif +DPMSEnable: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+16 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+16] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 2 & 0xffff +#if 2 > 0xffff + movk ip0, 2 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl DPMSForceLevel + .p2align 4 + .type DPMSForceLevel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden DPMSForceLevel +#endif +DPMSForceLevel: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+24 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+24] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 3 & 0xffff +#if 3 > 0xffff + movk ip0, 3 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl DPMSGetTimeouts + .p2align 4 + .type DPMSGetTimeouts, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden DPMSGetTimeouts +#endif +DPMSGetTimeouts: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+32 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+32] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 4 & 0xffff +#if 4 > 0xffff + movk ip0, 4 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl DPMSGetVersion + .p2align 4 + .type DPMSGetVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden DPMSGetVersion +#endif +DPMSGetVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+40 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+40] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 5 & 0xffff +#if 5 > 0xffff + movk ip0, 5 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl DPMSInfo + .p2align 4 + .type DPMSInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden DPMSInfo +#endif +DPMSInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+48 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+48] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 6 & 0xffff +#if 6 > 0xffff + movk ip0, 6 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl DPMSQueryExtension + .p2align 4 + .type DPMSQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden DPMSQueryExtension +#endif +DPMSQueryExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+56 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+56] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 7 & 0xffff +#if 7 > 0xffff + movk ip0, 7 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl DPMSSetTimeouts + .p2align 4 + .type DPMSSetTimeouts, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden DPMSSetTimeouts +#endif +DPMSSetTimeouts: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+64 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+64] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 8 & 0xffff +#if 8 > 0xffff + movk ip0, 8 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGEQueryExtension + .p2align 4 + .type XGEQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGEQueryExtension +#endif +XGEQueryExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+72 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+72] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 9 & 0xffff +#if 9 > 0xffff + movk ip0, 9 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGEQueryVersion + .p2align 4 + .type XGEQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGEQueryVersion +#endif +XGEQueryVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+80 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+80] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 10 & 0xffff +#if 10 > 0xffff + movk ip0, 10 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLbxGetEventBase + .p2align 4 + .type XLbxGetEventBase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLbxGetEventBase +#endif +XLbxGetEventBase: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+88 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+88] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 11 & 0xffff +#if 11 > 0xffff + movk ip0, 11 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLbxQueryExtension + .p2align 4 + .type XLbxQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLbxQueryExtension +#endif +XLbxQueryExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+96 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+96] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 12 & 0xffff +#if 12 > 0xffff + movk ip0, 12 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLbxQueryVersion + .p2align 4 + .type XLbxQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLbxQueryVersion +#endif +XLbxQueryVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+104 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 13 & 0xffff +#if 13 > 0xffff + movk ip0, 13 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMITMiscGetBugMode + .p2align 4 + .type XMITMiscGetBugMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMITMiscGetBugMode +#endif +XMITMiscGetBugMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+112 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 14 & 0xffff +#if 14 > 0xffff + movk ip0, 14 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMITMiscQueryExtension + .p2align 4 + .type XMITMiscQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMITMiscQueryExtension +#endif +XMITMiscQueryExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+120 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 15 & 0xffff +#if 15 > 0xffff + movk ip0, 15 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMITMiscSetBugMode + .p2align 4 + .type XMITMiscSetBugMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMITMiscSetBugMode +#endif +XMITMiscSetBugMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+128 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 16 & 0xffff +#if 16 > 0xffff + movk ip0, 16 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMissingExtension + .p2align 4 + .type XMissingExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMissingExtension +#endif +XMissingExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+136 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 17 & 0xffff +#if 17 > 0xffff + movk ip0, 17 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSecurityAllocXauth + .p2align 4 + .type XSecurityAllocXauth, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSecurityAllocXauth +#endif +XSecurityAllocXauth: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+144 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 18 & 0xffff +#if 18 > 0xffff + movk ip0, 18 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSecurityFreeXauth + .p2align 4 + .type XSecurityFreeXauth, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSecurityFreeXauth +#endif +XSecurityFreeXauth: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+152 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 19 & 0xffff +#if 19 > 0xffff + movk ip0, 19 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSecurityGenerateAuthorization + .p2align 4 + .type XSecurityGenerateAuthorization, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSecurityGenerateAuthorization +#endif +XSecurityGenerateAuthorization: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+160 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 20 & 0xffff +#if 20 > 0xffff + movk ip0, 20 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSecurityQueryExtension + .p2align 4 + .type XSecurityQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSecurityQueryExtension +#endif +XSecurityQueryExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+168 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 21 & 0xffff +#if 21 > 0xffff + movk ip0, 21 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSecurityRevokeAuthorization + .p2align 4 + .type XSecurityRevokeAuthorization, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSecurityRevokeAuthorization +#endif +XSecurityRevokeAuthorization: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+176 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 22 & 0xffff +#if 22 > 0xffff + movk ip0, 22 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetExtensionErrorHandler + .p2align 4 + .type XSetExtensionErrorHandler, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetExtensionErrorHandler +#endif +XSetExtensionErrorHandler: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+184 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 23 & 0xffff +#if 23 > 0xffff + movk ip0, 23 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeCombineMask + .p2align 4 + .type XShapeCombineMask, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeCombineMask +#endif +XShapeCombineMask: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+192 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 24 & 0xffff +#if 24 > 0xffff + movk ip0, 24 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeCombineRectangles + .p2align 4 + .type XShapeCombineRectangles, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeCombineRectangles +#endif +XShapeCombineRectangles: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+200 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 25 & 0xffff +#if 25 > 0xffff + movk ip0, 25 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeCombineRegion + .p2align 4 + .type XShapeCombineRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeCombineRegion +#endif +XShapeCombineRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+208 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 26 & 0xffff +#if 26 > 0xffff + movk ip0, 26 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeCombineShape + .p2align 4 + .type XShapeCombineShape, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeCombineShape +#endif +XShapeCombineShape: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+216 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 27 & 0xffff +#if 27 > 0xffff + movk ip0, 27 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeGetRectangles + .p2align 4 + .type XShapeGetRectangles, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeGetRectangles +#endif +XShapeGetRectangles: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+224 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 28 & 0xffff +#if 28 > 0xffff + movk ip0, 28 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeInputSelected + .p2align 4 + .type XShapeInputSelected, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeInputSelected +#endif +XShapeInputSelected: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+232 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 29 & 0xffff +#if 29 > 0xffff + movk ip0, 29 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeOffsetShape + .p2align 4 + .type XShapeOffsetShape, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeOffsetShape +#endif +XShapeOffsetShape: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+240 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 30 & 0xffff +#if 30 > 0xffff + movk ip0, 30 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeQueryExtension + .p2align 4 + .type XShapeQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeQueryExtension +#endif +XShapeQueryExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+248 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 31 & 0xffff +#if 31 > 0xffff + movk ip0, 31 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeQueryExtents + .p2align 4 + .type XShapeQueryExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeQueryExtents +#endif +XShapeQueryExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+256 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 32 & 0xffff +#if 32 > 0xffff + movk ip0, 32 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeQueryVersion + .p2align 4 + .type XShapeQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeQueryVersion +#endif +XShapeQueryVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+264 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 33 & 0xffff +#if 33 > 0xffff + movk ip0, 33 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeSelectInput + .p2align 4 + .type XShapeSelectInput, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeSelectInput +#endif +XShapeSelectInput: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+272 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 34 & 0xffff +#if 34 > 0xffff + movk ip0, 34 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmAttach + .p2align 4 + .type XShmAttach, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmAttach +#endif +XShmAttach: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+280 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 35 & 0xffff +#if 35 > 0xffff + movk ip0, 35 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmCreateImage + .p2align 4 + .type XShmCreateImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmCreateImage +#endif +XShmCreateImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+288 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 36 & 0xffff +#if 36 > 0xffff + movk ip0, 36 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmCreatePixmap + .p2align 4 + .type XShmCreatePixmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmCreatePixmap +#endif +XShmCreatePixmap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+296 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 37 & 0xffff +#if 37 > 0xffff + movk ip0, 37 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmDetach + .p2align 4 + .type XShmDetach, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmDetach +#endif +XShmDetach: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+304 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 38 & 0xffff +#if 38 > 0xffff + movk ip0, 38 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmGetEventBase + .p2align 4 + .type XShmGetEventBase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmGetEventBase +#endif +XShmGetEventBase: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+312 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 39 & 0xffff +#if 39 > 0xffff + movk ip0, 39 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmGetImage + .p2align 4 + .type XShmGetImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmGetImage +#endif +XShmGetImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+320 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 40 & 0xffff +#if 40 > 0xffff + movk ip0, 40 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmPixmapFormat + .p2align 4 + .type XShmPixmapFormat, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmPixmapFormat +#endif +XShmPixmapFormat: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+328 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 41 & 0xffff +#if 41 > 0xffff + movk ip0, 41 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmPutImage + .p2align 4 + .type XShmPutImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmPutImage +#endif +XShmPutImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+336 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 42 & 0xffff +#if 42 > 0xffff + movk ip0, 42 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmQueryExtension + .p2align 4 + .type XShmQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmQueryExtension +#endif +XShmQueryExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+344 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 43 & 0xffff +#if 43 > 0xffff + movk ip0, 43 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmQueryVersion + .p2align 4 + .type XShmQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmQueryVersion +#endif +XShmQueryVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+352 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 44 & 0xffff +#if 44 > 0xffff + movk ip0, 44 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncAwait + .p2align 4 + .type XSyncAwait, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncAwait +#endif +XSyncAwait: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+360 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 45 & 0xffff +#if 45 > 0xffff + movk ip0, 45 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncAwaitFence + .p2align 4 + .type XSyncAwaitFence, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncAwaitFence +#endif +XSyncAwaitFence: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+368 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 46 & 0xffff +#if 46 > 0xffff + movk ip0, 46 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncChangeAlarm + .p2align 4 + .type XSyncChangeAlarm, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncChangeAlarm +#endif +XSyncChangeAlarm: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+376 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 47 & 0xffff +#if 47 > 0xffff + movk ip0, 47 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncChangeCounter + .p2align 4 + .type XSyncChangeCounter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncChangeCounter +#endif +XSyncChangeCounter: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+384 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 48 & 0xffff +#if 48 > 0xffff + movk ip0, 48 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncCreateAlarm + .p2align 4 + .type XSyncCreateAlarm, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncCreateAlarm +#endif +XSyncCreateAlarm: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+392 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 49 & 0xffff +#if 49 > 0xffff + movk ip0, 49 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncCreateCounter + .p2align 4 + .type XSyncCreateCounter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncCreateCounter +#endif +XSyncCreateCounter: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+400 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 50 & 0xffff +#if 50 > 0xffff + movk ip0, 50 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncCreateFence + .p2align 4 + .type XSyncCreateFence, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncCreateFence +#endif +XSyncCreateFence: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+408 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 51 & 0xffff +#if 51 > 0xffff + movk ip0, 51 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncDestroyAlarm + .p2align 4 + .type XSyncDestroyAlarm, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncDestroyAlarm +#endif +XSyncDestroyAlarm: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+416 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 52 & 0xffff +#if 52 > 0xffff + movk ip0, 52 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncDestroyCounter + .p2align 4 + .type XSyncDestroyCounter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncDestroyCounter +#endif +XSyncDestroyCounter: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+424 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 53 & 0xffff +#if 53 > 0xffff + movk ip0, 53 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncDestroyFence + .p2align 4 + .type XSyncDestroyFence, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncDestroyFence +#endif +XSyncDestroyFence: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+432 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 54 & 0xffff +#if 54 > 0xffff + movk ip0, 54 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncFreeSystemCounterList + .p2align 4 + .type XSyncFreeSystemCounterList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncFreeSystemCounterList +#endif +XSyncFreeSystemCounterList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+440 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 55 & 0xffff +#if 55 > 0xffff + movk ip0, 55 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncGetPriority + .p2align 4 + .type XSyncGetPriority, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncGetPriority +#endif +XSyncGetPriority: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+448 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 56 & 0xffff +#if 56 > 0xffff + movk ip0, 56 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncInitialize + .p2align 4 + .type XSyncInitialize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncInitialize +#endif +XSyncInitialize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+456 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+456] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 57 & 0xffff +#if 57 > 0xffff + movk ip0, 57 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncIntToValue + .p2align 4 + .type XSyncIntToValue, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncIntToValue +#endif +XSyncIntToValue: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+464 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+464] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 58 & 0xffff +#if 58 > 0xffff + movk ip0, 58 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncIntsToValue + .p2align 4 + .type XSyncIntsToValue, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncIntsToValue +#endif +XSyncIntsToValue: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+472 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+472] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 59 & 0xffff +#if 59 > 0xffff + movk ip0, 59 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncListSystemCounters + .p2align 4 + .type XSyncListSystemCounters, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncListSystemCounters +#endif +XSyncListSystemCounters: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+480 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+480] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 60 & 0xffff +#if 60 > 0xffff + movk ip0, 60 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncMaxValue + .p2align 4 + .type XSyncMaxValue, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncMaxValue +#endif +XSyncMaxValue: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+488 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+488] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 61 & 0xffff +#if 61 > 0xffff + movk ip0, 61 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncMinValue + .p2align 4 + .type XSyncMinValue, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncMinValue +#endif +XSyncMinValue: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+496 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+496] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 62 & 0xffff +#if 62 > 0xffff + movk ip0, 62 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncQueryAlarm + .p2align 4 + .type XSyncQueryAlarm, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncQueryAlarm +#endif +XSyncQueryAlarm: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+504 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+504] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 63 & 0xffff +#if 63 > 0xffff + movk ip0, 63 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncQueryCounter + .p2align 4 + .type XSyncQueryCounter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncQueryCounter +#endif +XSyncQueryCounter: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+512 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+512] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 64 & 0xffff +#if 64 > 0xffff + movk ip0, 64 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncQueryExtension + .p2align 4 + .type XSyncQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncQueryExtension +#endif +XSyncQueryExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+520 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+520] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 65 & 0xffff +#if 65 > 0xffff + movk ip0, 65 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncQueryFence + .p2align 4 + .type XSyncQueryFence, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncQueryFence +#endif +XSyncQueryFence: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+528 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+528] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 66 & 0xffff +#if 66 > 0xffff + movk ip0, 66 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncResetFence + .p2align 4 + .type XSyncResetFence, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncResetFence +#endif +XSyncResetFence: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+536 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+536] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 67 & 0xffff +#if 67 > 0xffff + movk ip0, 67 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncSetCounter + .p2align 4 + .type XSyncSetCounter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncSetCounter +#endif +XSyncSetCounter: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+544 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+544] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 68 & 0xffff +#if 68 > 0xffff + movk ip0, 68 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncSetPriority + .p2align 4 + .type XSyncSetPriority, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncSetPriority +#endif +XSyncSetPriority: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+552 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+552] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 69 & 0xffff +#if 69 > 0xffff + movk ip0, 69 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncTriggerFence + .p2align 4 + .type XSyncTriggerFence, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncTriggerFence +#endif +XSyncTriggerFence: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+560 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+560] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 70 & 0xffff +#if 70 > 0xffff + movk ip0, 70 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueAdd + .p2align 4 + .type XSyncValueAdd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueAdd +#endif +XSyncValueAdd: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+568 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+568] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 71 & 0xffff +#if 71 > 0xffff + movk ip0, 71 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueEqual + .p2align 4 + .type XSyncValueEqual, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueEqual +#endif +XSyncValueEqual: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+576 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+576] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 72 & 0xffff +#if 72 > 0xffff + movk ip0, 72 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueGreaterOrEqual + .p2align 4 + .type XSyncValueGreaterOrEqual, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueGreaterOrEqual +#endif +XSyncValueGreaterOrEqual: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+584 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+584] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 73 & 0xffff +#if 73 > 0xffff + movk ip0, 73 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueGreaterThan + .p2align 4 + .type XSyncValueGreaterThan, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueGreaterThan +#endif +XSyncValueGreaterThan: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+592 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+592] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 74 & 0xffff +#if 74 > 0xffff + movk ip0, 74 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueHigh32 + .p2align 4 + .type XSyncValueHigh32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueHigh32 +#endif +XSyncValueHigh32: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+600 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+600] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 75 & 0xffff +#if 75 > 0xffff + movk ip0, 75 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueIsNegative + .p2align 4 + .type XSyncValueIsNegative, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueIsNegative +#endif +XSyncValueIsNegative: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+608 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+608] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 76 & 0xffff +#if 76 > 0xffff + movk ip0, 76 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueIsPositive + .p2align 4 + .type XSyncValueIsPositive, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueIsPositive +#endif +XSyncValueIsPositive: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+616 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+616] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 77 & 0xffff +#if 77 > 0xffff + movk ip0, 77 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueIsZero + .p2align 4 + .type XSyncValueIsZero, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueIsZero +#endif +XSyncValueIsZero: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+624 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+624] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 78 & 0xffff +#if 78 > 0xffff + movk ip0, 78 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueLessOrEqual + .p2align 4 + .type XSyncValueLessOrEqual, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueLessOrEqual +#endif +XSyncValueLessOrEqual: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+632 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+632] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 79 & 0xffff +#if 79 > 0xffff + movk ip0, 79 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueLessThan + .p2align 4 + .type XSyncValueLessThan, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueLessThan +#endif +XSyncValueLessThan: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+640 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+640] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 80 & 0xffff +#if 80 > 0xffff + movk ip0, 80 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueLow32 + .p2align 4 + .type XSyncValueLow32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueLow32 +#endif +XSyncValueLow32: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+648 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+648] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 81 & 0xffff +#if 81 > 0xffff + movk ip0, 81 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueSubtract + .p2align 4 + .type XSyncValueSubtract, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueSubtract +#endif +XSyncValueSubtract: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+656 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+656] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 82 & 0xffff +#if 82 > 0xffff + movk ip0, 82 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTestFakeInput + .p2align 4 + .type XTestFakeInput, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTestFakeInput +#endif +XTestFakeInput: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+664 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+664] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 83 & 0xffff +#if 83 > 0xffff + movk ip0, 83 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTestFlush + .p2align 4 + .type XTestFlush, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTestFlush +#endif +XTestFlush: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+672 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+672] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 84 & 0xffff +#if 84 > 0xffff + movk ip0, 84 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTestGetInput + .p2align 4 + .type XTestGetInput, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTestGetInput +#endif +XTestGetInput: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+680 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+680] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 85 & 0xffff +#if 85 > 0xffff + movk ip0, 85 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTestMovePointer + .p2align 4 + .type XTestMovePointer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTestMovePointer +#endif +XTestMovePointer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+688 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+688] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 86 & 0xffff +#if 86 > 0xffff + movk ip0, 86 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTestPressButton + .p2align 4 + .type XTestPressButton, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTestPressButton +#endif +XTestPressButton: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+696 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+696] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 87 & 0xffff +#if 87 > 0xffff + movk ip0, 87 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTestPressKey + .p2align 4 + .type XTestPressKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTestPressKey +#endif +XTestPressKey: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+704 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+704] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 88 & 0xffff +#if 88 > 0xffff + movk ip0, 88 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTestQueryInputSize + .p2align 4 + .type XTestQueryInputSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTestQueryInputSize +#endif +XTestQueryInputSize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+712 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+712] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 89 & 0xffff +#if 89 > 0xffff + movk ip0, 89 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTestReset + .p2align 4 + .type XTestReset, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTestReset +#endif +XTestReset: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+720 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+720] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 90 & 0xffff +#if 90 > 0xffff + movk ip0, 90 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTestStopInput + .p2align 4 + .type XTestStopInput, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTestStopInput +#endif +XTestStopInput: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+728 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+728] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 91 & 0xffff +#if 91 > 0xffff + movk ip0, 91 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XagCreateAssociation + .p2align 4 + .type XagCreateAssociation, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XagCreateAssociation +#endif +XagCreateAssociation: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+736 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+736] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 92 & 0xffff +#if 92 > 0xffff + movk ip0, 92 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XagCreateEmbeddedApplicationGroup + .p2align 4 + .type XagCreateEmbeddedApplicationGroup, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XagCreateEmbeddedApplicationGroup +#endif +XagCreateEmbeddedApplicationGroup: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+744 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+744] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 93 & 0xffff +#if 93 > 0xffff + movk ip0, 93 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XagCreateNonembeddedApplicationGroup + .p2align 4 + .type XagCreateNonembeddedApplicationGroup, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XagCreateNonembeddedApplicationGroup +#endif +XagCreateNonembeddedApplicationGroup: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+752 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+752] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 94 & 0xffff +#if 94 > 0xffff + movk ip0, 94 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XagDestroyApplicationGroup + .p2align 4 + .type XagDestroyApplicationGroup, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XagDestroyApplicationGroup +#endif +XagDestroyApplicationGroup: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+760 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+760] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 95 & 0xffff +#if 95 > 0xffff + movk ip0, 95 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XagDestroyAssociation + .p2align 4 + .type XagDestroyAssociation, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XagDestroyAssociation +#endif +XagDestroyAssociation: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+768 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+768] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 96 & 0xffff +#if 96 > 0xffff + movk ip0, 96 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XagGetApplicationGroupAttributes + .p2align 4 + .type XagGetApplicationGroupAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XagGetApplicationGroupAttributes +#endif +XagGetApplicationGroupAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+776 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+776] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 97 & 0xffff +#if 97 > 0xffff + movk ip0, 97 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XagQueryApplicationGroup + .p2align 4 + .type XagQueryApplicationGroup, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XagQueryApplicationGroup +#endif +XagQueryApplicationGroup: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+784 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+784] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 98 & 0xffff +#if 98 > 0xffff + movk ip0, 98 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XagQueryVersion + .p2align 4 + .type XagQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XagQueryVersion +#endif +XagQueryVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+792 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+792] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 99 & 0xffff +#if 99 > 0xffff + movk ip0, 99 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcupGetReservedColormapEntries + .p2align 4 + .type XcupGetReservedColormapEntries, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcupGetReservedColormapEntries +#endif +XcupGetReservedColormapEntries: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+800 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+800] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 100 & 0xffff +#if 100 > 0xffff + movk ip0, 100 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcupQueryVersion + .p2align 4 + .type XcupQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcupQueryVersion +#endif +XcupQueryVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+808 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+808] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 101 & 0xffff +#if 101 > 0xffff + movk ip0, 101 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcupStoreColors + .p2align 4 + .type XcupStoreColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcupStoreColors +#endif +XcupStoreColors: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+816 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+816] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 102 & 0xffff +#if 102 > 0xffff + movk ip0, 102 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XdbeAllocateBackBufferName + .p2align 4 + .type XdbeAllocateBackBufferName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XdbeAllocateBackBufferName +#endif +XdbeAllocateBackBufferName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+824 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+824] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 103 & 0xffff +#if 103 > 0xffff + movk ip0, 103 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XdbeBeginIdiom + .p2align 4 + .type XdbeBeginIdiom, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XdbeBeginIdiom +#endif +XdbeBeginIdiom: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+832 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+832] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 104 & 0xffff +#if 104 > 0xffff + movk ip0, 104 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XdbeDeallocateBackBufferName + .p2align 4 + .type XdbeDeallocateBackBufferName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XdbeDeallocateBackBufferName +#endif +XdbeDeallocateBackBufferName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+840 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+840] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 105 & 0xffff +#if 105 > 0xffff + movk ip0, 105 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XdbeEndIdiom + .p2align 4 + .type XdbeEndIdiom, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XdbeEndIdiom +#endif +XdbeEndIdiom: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+848 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+848] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 106 & 0xffff +#if 106 > 0xffff + movk ip0, 106 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XdbeFreeVisualInfo + .p2align 4 + .type XdbeFreeVisualInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XdbeFreeVisualInfo +#endif +XdbeFreeVisualInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+856 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+856] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 107 & 0xffff +#if 107 > 0xffff + movk ip0, 107 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XdbeGetBackBufferAttributes + .p2align 4 + .type XdbeGetBackBufferAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XdbeGetBackBufferAttributes +#endif +XdbeGetBackBufferAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+864 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+864] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 108 & 0xffff +#if 108 > 0xffff + movk ip0, 108 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XdbeGetVisualInfo + .p2align 4 + .type XdbeGetVisualInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XdbeGetVisualInfo +#endif +XdbeGetVisualInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+872 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+872] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 109 & 0xffff +#if 109 > 0xffff + movk ip0, 109 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XdbeQueryExtension + .p2align 4 + .type XdbeQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XdbeQueryExtension +#endif +XdbeQueryExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+880 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+880] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 110 & 0xffff +#if 110 > 0xffff + movk ip0, 110 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XdbeSwapBuffers + .p2align 4 + .type XdbeSwapBuffers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XdbeSwapBuffers +#endif +XdbeSwapBuffers: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+888 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+888] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 111 & 0xffff +#if 111 > 0xffff + movk ip0, 111 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XeviGetVisualInfo + .p2align 4 + .type XeviGetVisualInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XeviGetVisualInfo +#endif +XeviGetVisualInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+896 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+896] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 112 & 0xffff +#if 112 > 0xffff + movk ip0, 112 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XeviQueryExtension + .p2align 4 + .type XeviQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XeviQueryExtension +#endif +XeviQueryExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+904 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+904] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 113 & 0xffff +#if 113 > 0xffff + movk ip0, 113 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XeviQueryVersion + .p2align 4 + .type XeviQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XeviQueryVersion +#endif +XeviQueryVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+912 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+912] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 114 & 0xffff +#if 114 > 0xffff + movk ip0, 114 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XextAddDisplay + .p2align 4 + .type XextAddDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XextAddDisplay +#endif +XextAddDisplay: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+920 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+920] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 115 & 0xffff +#if 115 > 0xffff + movk ip0, 115 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XextCreateExtension + .p2align 4 + .type XextCreateExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XextCreateExtension +#endif +XextCreateExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+928 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+928] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 116 & 0xffff +#if 116 > 0xffff + movk ip0, 116 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XextDestroyExtension + .p2align 4 + .type XextDestroyExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XextDestroyExtension +#endif +XextDestroyExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+936 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+936] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 117 & 0xffff +#if 117 > 0xffff + movk ip0, 117 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XextFindDisplay + .p2align 4 + .type XextFindDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XextFindDisplay +#endif +XextFindDisplay: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+944 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+944] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 118 & 0xffff +#if 118 > 0xffff + movk ip0, 118 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XextRemoveDisplay + .p2align 4 + .type XextRemoveDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XextRemoveDisplay +#endif +XextRemoveDisplay: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+952 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+952] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 119 & 0xffff +#if 119 > 0xffff + movk ip0, 119 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufChangeBufferAttributes + .p2align 4 + .type XmbufChangeBufferAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufChangeBufferAttributes +#endif +XmbufChangeBufferAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+960 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+960] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 120 & 0xffff +#if 120 > 0xffff + movk ip0, 120 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufChangeWindowAttributes + .p2align 4 + .type XmbufChangeWindowAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufChangeWindowAttributes +#endif +XmbufChangeWindowAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+968 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+968] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 121 & 0xffff +#if 121 > 0xffff + movk ip0, 121 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufClearBufferArea + .p2align 4 + .type XmbufClearBufferArea, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufClearBufferArea +#endif +XmbufClearBufferArea: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+976 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+976] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 122 & 0xffff +#if 122 > 0xffff + movk ip0, 122 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufCreateBuffers + .p2align 4 + .type XmbufCreateBuffers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufCreateBuffers +#endif +XmbufCreateBuffers: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+984 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+984] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 123 & 0xffff +#if 123 > 0xffff + movk ip0, 123 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufCreateStereoWindow + .p2align 4 + .type XmbufCreateStereoWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufCreateStereoWindow +#endif +XmbufCreateStereoWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+992 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+992] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 124 & 0xffff +#if 124 > 0xffff + movk ip0, 124 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufDestroyBuffers + .p2align 4 + .type XmbufDestroyBuffers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufDestroyBuffers +#endif +XmbufDestroyBuffers: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+1000 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+1000] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 125 & 0xffff +#if 125 > 0xffff + movk ip0, 125 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufDisplayBuffers + .p2align 4 + .type XmbufDisplayBuffers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufDisplayBuffers +#endif +XmbufDisplayBuffers: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+1008 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+1008] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 126 & 0xffff +#if 126 > 0xffff + movk ip0, 126 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufGetBufferAttributes + .p2align 4 + .type XmbufGetBufferAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufGetBufferAttributes +#endif +XmbufGetBufferAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+1016 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+1016] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 127 & 0xffff +#if 127 > 0xffff + movk ip0, 127 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufGetScreenInfo + .p2align 4 + .type XmbufGetScreenInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufGetScreenInfo +#endif +XmbufGetScreenInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+1024 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+1024] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 128 & 0xffff +#if 128 > 0xffff + movk ip0, 128 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufGetVersion + .p2align 4 + .type XmbufGetVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufGetVersion +#endif +XmbufGetVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+1032 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+1032] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 129 & 0xffff +#if 129 > 0xffff + movk ip0, 129 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufGetWindowAttributes + .p2align 4 + .type XmbufGetWindowAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufGetWindowAttributes +#endif +XmbufGetWindowAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+1040 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+1040] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 130 & 0xffff +#if 130 > 0xffff + movk ip0, 130 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufQueryExtension + .p2align 4 + .type XmbufQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufQueryExtension +#endif +XmbufQueryExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXext_so_tramp_table+1048 + ldr ip0, [ip0, #:lo12:_libXext_so_tramp_table+1048] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 131 & 0xffff +#if 131 > 0xffff + movk ip0, 131 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXext_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXfixes.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXfixes.so.init.c new file mode 100644 index 0000000..dc737ea --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXfixes.so.init.c @@ -0,0 +1,282 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libXfixes.so.3: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libXfixes.so.3"); + CHECK(lib_handle, "failed to load library 'libXfixes.so.3' via callback ''"); +#else + lib_handle = dlopen("libXfixes.so.3", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libXfixes.so.3' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "XFixesChangeCursor", + "XFixesChangeCursorByName", + "XFixesChangeSaveSet", + "XFixesCopyRegion", + "XFixesCreatePointerBarrier", + "XFixesCreateRegion", + "XFixesCreateRegionFromBitmap", + "XFixesCreateRegionFromGC", + "XFixesCreateRegionFromPicture", + "XFixesCreateRegionFromWindow", + "XFixesDestroyPointerBarrier", + "XFixesDestroyRegion", + "XFixesExpandRegion", + "XFixesFetchRegion", + "XFixesFetchRegionAndBounds", + "XFixesFindDisplay", + "XFixesGetClientDisconnectMode", + "XFixesGetCursorImage", + "XFixesGetCursorName", + "XFixesHideCursor", + "XFixesIntersectRegion", + "XFixesInvertRegion", + "XFixesQueryExtension", + "XFixesQueryVersion", + "XFixesRegionExtents", + "XFixesSelectCursorInput", + "XFixesSelectSelectionInput", + "XFixesSetClientDisconnectMode", + "XFixesSetCursorName", + "XFixesSetGCClipRegion", + "XFixesSetPictureClipRegion", + "XFixesSetRegion", + "XFixesSetWindowShapeRegion", + "XFixesShowCursor", + "XFixesSubtractRegion", + "XFixesTranslateRegion", + "XFixesUnionRegion", + "XFixesVersion", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libXfixes_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libXfixes_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libXfixes_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libXfixes_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libXfixes_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libXfixes_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libXfixes_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libXfixes_so_tramp_table, 0, SYM_COUNT * sizeof(_libXfixes_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXfixes.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXfixes.so.tramp.S new file mode 100644 index 0000000..830b346 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXfixes.so.tramp.S @@ -0,0 +1,1639 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#define lr x30 +#define ip0 x16 + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libXfixes_so_tramp_table + .hidden _libXfixes_so_tramp_table + .align 8 +_libXfixes_so_tramp_table: + .zero 312 + + .text + + .globl _libXfixes_so_tramp_resolve + .hidden _libXfixes_so_tramp_resolve + + .globl _libXfixes_so_save_regs_and_resolve + .hidden _libXfixes_so_save_regs_and_resolve + .type _libXfixes_so_save_regs_and_resolve, %function +_libXfixes_so_save_regs_and_resolve: + .cfi_startproc + + // Slow path which calls dlsym, taken only on first call. + // Registers are saved according to "Procedure Call Standard for the Arm® 64-bit Architecture". + // For DWARF directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + // Stack is aligned at 16 bytes + +#define PUSH_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 8 +#define POP_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore reg2; .cfi_restore reg1 + +#define PUSH_WIDE_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-32]!; .cfi_adjust_cfa_offset 32; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 16 +#define POP_WIDE_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #32; .cfi_adjust_cfa_offset -32; .cfi_restore reg2; .cfi_restore reg1 + + // Save only arguments (and lr) + PUSH_PAIR(x0, x1) + PUSH_PAIR(x2, x3) + PUSH_PAIR(x4, x5) + PUSH_PAIR(x6, x7) + PUSH_PAIR(x8, lr) + + ldr x0, [sp, #80] // 16*5 + + PUSH_WIDE_PAIR(q0, q1) + PUSH_WIDE_PAIR(q2, q3) + PUSH_WIDE_PAIR(q4, q5) + PUSH_WIDE_PAIR(q6, q7) + + // Stack is aligned at 16 bytes + + bl _libXfixes_so_tramp_resolve + mov ip0, x0 + + // TODO: pop pc? + + POP_WIDE_PAIR(q6, q7) + POP_WIDE_PAIR(q4, q5) + POP_WIDE_PAIR(q2, q3) + POP_WIDE_PAIR(q0, q1) + + POP_PAIR(x8, lr) + POP_PAIR(x6, x7) + POP_PAIR(x4, x5) + POP_PAIR(x2, x3) + POP_PAIR(x0, x1) + + br lr + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesChangeCursor + .p2align 4 + .type XFixesChangeCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesChangeCursor +#endif +XFixesChangeCursor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+0 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+0] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 0 & 0xffff +#if 0 > 0xffff + movk ip0, 0 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesChangeCursorByName + .p2align 4 + .type XFixesChangeCursorByName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesChangeCursorByName +#endif +XFixesChangeCursorByName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+8 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+8] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1 & 0xffff +#if 1 > 0xffff + movk ip0, 1 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesChangeSaveSet + .p2align 4 + .type XFixesChangeSaveSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesChangeSaveSet +#endif +XFixesChangeSaveSet: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+16 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+16] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 2 & 0xffff +#if 2 > 0xffff + movk ip0, 2 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesCopyRegion + .p2align 4 + .type XFixesCopyRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesCopyRegion +#endif +XFixesCopyRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+24 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+24] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 3 & 0xffff +#if 3 > 0xffff + movk ip0, 3 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesCreatePointerBarrier + .p2align 4 + .type XFixesCreatePointerBarrier, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesCreatePointerBarrier +#endif +XFixesCreatePointerBarrier: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+32 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+32] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 4 & 0xffff +#if 4 > 0xffff + movk ip0, 4 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesCreateRegion + .p2align 4 + .type XFixesCreateRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesCreateRegion +#endif +XFixesCreateRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+40 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+40] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 5 & 0xffff +#if 5 > 0xffff + movk ip0, 5 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesCreateRegionFromBitmap + .p2align 4 + .type XFixesCreateRegionFromBitmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesCreateRegionFromBitmap +#endif +XFixesCreateRegionFromBitmap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+48 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+48] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 6 & 0xffff +#if 6 > 0xffff + movk ip0, 6 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesCreateRegionFromGC + .p2align 4 + .type XFixesCreateRegionFromGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesCreateRegionFromGC +#endif +XFixesCreateRegionFromGC: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+56 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+56] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 7 & 0xffff +#if 7 > 0xffff + movk ip0, 7 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesCreateRegionFromPicture + .p2align 4 + .type XFixesCreateRegionFromPicture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesCreateRegionFromPicture +#endif +XFixesCreateRegionFromPicture: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+64 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+64] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 8 & 0xffff +#if 8 > 0xffff + movk ip0, 8 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesCreateRegionFromWindow + .p2align 4 + .type XFixesCreateRegionFromWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesCreateRegionFromWindow +#endif +XFixesCreateRegionFromWindow: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+72 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+72] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 9 & 0xffff +#if 9 > 0xffff + movk ip0, 9 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesDestroyPointerBarrier + .p2align 4 + .type XFixesDestroyPointerBarrier, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesDestroyPointerBarrier +#endif +XFixesDestroyPointerBarrier: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+80 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+80] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 10 & 0xffff +#if 10 > 0xffff + movk ip0, 10 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesDestroyRegion + .p2align 4 + .type XFixesDestroyRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesDestroyRegion +#endif +XFixesDestroyRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+88 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+88] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 11 & 0xffff +#if 11 > 0xffff + movk ip0, 11 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesExpandRegion + .p2align 4 + .type XFixesExpandRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesExpandRegion +#endif +XFixesExpandRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+96 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+96] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 12 & 0xffff +#if 12 > 0xffff + movk ip0, 12 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesFetchRegion + .p2align 4 + .type XFixesFetchRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesFetchRegion +#endif +XFixesFetchRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+104 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 13 & 0xffff +#if 13 > 0xffff + movk ip0, 13 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesFetchRegionAndBounds + .p2align 4 + .type XFixesFetchRegionAndBounds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesFetchRegionAndBounds +#endif +XFixesFetchRegionAndBounds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+112 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 14 & 0xffff +#if 14 > 0xffff + movk ip0, 14 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesFindDisplay + .p2align 4 + .type XFixesFindDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesFindDisplay +#endif +XFixesFindDisplay: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+120 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 15 & 0xffff +#if 15 > 0xffff + movk ip0, 15 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesGetClientDisconnectMode + .p2align 4 + .type XFixesGetClientDisconnectMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesGetClientDisconnectMode +#endif +XFixesGetClientDisconnectMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+128 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 16 & 0xffff +#if 16 > 0xffff + movk ip0, 16 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesGetCursorImage + .p2align 4 + .type XFixesGetCursorImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesGetCursorImage +#endif +XFixesGetCursorImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+136 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 17 & 0xffff +#if 17 > 0xffff + movk ip0, 17 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesGetCursorName + .p2align 4 + .type XFixesGetCursorName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesGetCursorName +#endif +XFixesGetCursorName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+144 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 18 & 0xffff +#if 18 > 0xffff + movk ip0, 18 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesHideCursor + .p2align 4 + .type XFixesHideCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesHideCursor +#endif +XFixesHideCursor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+152 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 19 & 0xffff +#if 19 > 0xffff + movk ip0, 19 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesIntersectRegion + .p2align 4 + .type XFixesIntersectRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesIntersectRegion +#endif +XFixesIntersectRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+160 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 20 & 0xffff +#if 20 > 0xffff + movk ip0, 20 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesInvertRegion + .p2align 4 + .type XFixesInvertRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesInvertRegion +#endif +XFixesInvertRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+168 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 21 & 0xffff +#if 21 > 0xffff + movk ip0, 21 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesQueryExtension + .p2align 4 + .type XFixesQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesQueryExtension +#endif +XFixesQueryExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+176 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 22 & 0xffff +#if 22 > 0xffff + movk ip0, 22 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesQueryVersion + .p2align 4 + .type XFixesQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesQueryVersion +#endif +XFixesQueryVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+184 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 23 & 0xffff +#if 23 > 0xffff + movk ip0, 23 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesRegionExtents + .p2align 4 + .type XFixesRegionExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesRegionExtents +#endif +XFixesRegionExtents: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+192 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 24 & 0xffff +#if 24 > 0xffff + movk ip0, 24 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesSelectCursorInput + .p2align 4 + .type XFixesSelectCursorInput, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesSelectCursorInput +#endif +XFixesSelectCursorInput: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+200 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 25 & 0xffff +#if 25 > 0xffff + movk ip0, 25 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesSelectSelectionInput + .p2align 4 + .type XFixesSelectSelectionInput, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesSelectSelectionInput +#endif +XFixesSelectSelectionInput: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+208 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 26 & 0xffff +#if 26 > 0xffff + movk ip0, 26 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesSetClientDisconnectMode + .p2align 4 + .type XFixesSetClientDisconnectMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesSetClientDisconnectMode +#endif +XFixesSetClientDisconnectMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+216 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 27 & 0xffff +#if 27 > 0xffff + movk ip0, 27 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesSetCursorName + .p2align 4 + .type XFixesSetCursorName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesSetCursorName +#endif +XFixesSetCursorName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+224 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 28 & 0xffff +#if 28 > 0xffff + movk ip0, 28 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesSetGCClipRegion + .p2align 4 + .type XFixesSetGCClipRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesSetGCClipRegion +#endif +XFixesSetGCClipRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+232 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 29 & 0xffff +#if 29 > 0xffff + movk ip0, 29 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesSetPictureClipRegion + .p2align 4 + .type XFixesSetPictureClipRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesSetPictureClipRegion +#endif +XFixesSetPictureClipRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+240 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 30 & 0xffff +#if 30 > 0xffff + movk ip0, 30 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesSetRegion + .p2align 4 + .type XFixesSetRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesSetRegion +#endif +XFixesSetRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+248 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 31 & 0xffff +#if 31 > 0xffff + movk ip0, 31 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesSetWindowShapeRegion + .p2align 4 + .type XFixesSetWindowShapeRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesSetWindowShapeRegion +#endif +XFixesSetWindowShapeRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+256 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 32 & 0xffff +#if 32 > 0xffff + movk ip0, 32 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesShowCursor + .p2align 4 + .type XFixesShowCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesShowCursor +#endif +XFixesShowCursor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+264 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 33 & 0xffff +#if 33 > 0xffff + movk ip0, 33 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesSubtractRegion + .p2align 4 + .type XFixesSubtractRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesSubtractRegion +#endif +XFixesSubtractRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+272 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 34 & 0xffff +#if 34 > 0xffff + movk ip0, 34 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesTranslateRegion + .p2align 4 + .type XFixesTranslateRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesTranslateRegion +#endif +XFixesTranslateRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+280 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 35 & 0xffff +#if 35 > 0xffff + movk ip0, 35 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesUnionRegion + .p2align 4 + .type XFixesUnionRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesUnionRegion +#endif +XFixesUnionRegion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+288 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 36 & 0xffff +#if 36 > 0xffff + movk ip0, 36 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesVersion + .p2align 4 + .type XFixesVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesVersion +#endif +XFixesVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXfixes_so_tramp_table+296 + ldr ip0, [ip0, #:lo12:_libXfixes_so_tramp_table+296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 37 & 0xffff +#if 37 > 0xffff + movk ip0, 37 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXfixes_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXrandr.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXrandr.so.init.c new file mode 100644 index 0000000..200111d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXrandr.so.init.c @@ -0,0 +1,314 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libXrandr.so.2: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libXrandr.so.2"); + CHECK(lib_handle, "failed to load library 'libXrandr.so.2' via callback ''"); +#else + lib_handle = dlopen("libXrandr.so.2", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libXrandr.so.2' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "XRRAddOutputMode", + "XRRAllocGamma", + "XRRAllocModeInfo", + "XRRAllocateMonitor", + "XRRChangeOutputProperty", + "XRRChangeProviderProperty", + "XRRConfigCurrentConfiguration", + "XRRConfigCurrentRate", + "XRRConfigRates", + "XRRConfigRotations", + "XRRConfigSizes", + "XRRConfigTimes", + "XRRConfigureOutputProperty", + "XRRConfigureProviderProperty", + "XRRCreateMode", + "XRRDeleteMonitor", + "XRRDeleteOutputMode", + "XRRDeleteOutputProperty", + "XRRDeleteProviderProperty", + "XRRDestroyMode", + "XRRFreeCrtcInfo", + "XRRFreeGamma", + "XRRFreeModeInfo", + "XRRFreeMonitors", + "XRRFreeOutputInfo", + "XRRFreePanning", + "XRRFreeProviderInfo", + "XRRFreeProviderResources", + "XRRFreeScreenConfigInfo", + "XRRFreeScreenResources", + "XRRGetCrtcGamma", + "XRRGetCrtcGammaSize", + "XRRGetCrtcInfo", + "XRRGetCrtcTransform", + "XRRGetMonitors", + "XRRGetOutputInfo", + "XRRGetOutputPrimary", + "XRRGetOutputProperty", + "XRRGetPanning", + "XRRGetProviderInfo", + "XRRGetProviderProperty", + "XRRGetProviderResources", + "XRRGetScreenInfo", + "XRRGetScreenResources", + "XRRGetScreenResourcesCurrent", + "XRRGetScreenSizeRange", + "XRRListOutputProperties", + "XRRListProviderProperties", + "XRRQueryExtension", + "XRRQueryOutputProperty", + "XRRQueryProviderProperty", + "XRRQueryVersion", + "XRRRates", + "XRRRootToScreen", + "XRRRotations", + "XRRSelectInput", + "XRRSetCrtcConfig", + "XRRSetCrtcGamma", + "XRRSetCrtcTransform", + "XRRSetMonitor", + "XRRSetOutputPrimary", + "XRRSetPanning", + "XRRSetProviderOffloadSink", + "XRRSetProviderOutputSource", + "XRRSetScreenConfig", + "XRRSetScreenConfigAndRate", + "XRRSetScreenSize", + "XRRSizes", + "XRRTimes", + "XRRUpdateConfiguration", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libXrandr_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libXrandr_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libXrandr_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libXrandr_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libXrandr_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libXrandr_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libXrandr_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libXrandr_so_tramp_table, 0, SYM_COUNT * sizeof(_libXrandr_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXrandr.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXrandr.so.tramp.S new file mode 100644 index 0000000..82cc451 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libXrandr.so.tramp.S @@ -0,0 +1,2951 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#define lr x30 +#define ip0 x16 + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libXrandr_so_tramp_table + .hidden _libXrandr_so_tramp_table + .align 8 +_libXrandr_so_tramp_table: + .zero 568 + + .text + + .globl _libXrandr_so_tramp_resolve + .hidden _libXrandr_so_tramp_resolve + + .globl _libXrandr_so_save_regs_and_resolve + .hidden _libXrandr_so_save_regs_and_resolve + .type _libXrandr_so_save_regs_and_resolve, %function +_libXrandr_so_save_regs_and_resolve: + .cfi_startproc + + // Slow path which calls dlsym, taken only on first call. + // Registers are saved according to "Procedure Call Standard for the Arm® 64-bit Architecture". + // For DWARF directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + // Stack is aligned at 16 bytes + +#define PUSH_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 8 +#define POP_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore reg2; .cfi_restore reg1 + +#define PUSH_WIDE_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-32]!; .cfi_adjust_cfa_offset 32; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 16 +#define POP_WIDE_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #32; .cfi_adjust_cfa_offset -32; .cfi_restore reg2; .cfi_restore reg1 + + // Save only arguments (and lr) + PUSH_PAIR(x0, x1) + PUSH_PAIR(x2, x3) + PUSH_PAIR(x4, x5) + PUSH_PAIR(x6, x7) + PUSH_PAIR(x8, lr) + + ldr x0, [sp, #80] // 16*5 + + PUSH_WIDE_PAIR(q0, q1) + PUSH_WIDE_PAIR(q2, q3) + PUSH_WIDE_PAIR(q4, q5) + PUSH_WIDE_PAIR(q6, q7) + + // Stack is aligned at 16 bytes + + bl _libXrandr_so_tramp_resolve + mov ip0, x0 + + // TODO: pop pc? + + POP_WIDE_PAIR(q6, q7) + POP_WIDE_PAIR(q4, q5) + POP_WIDE_PAIR(q2, q3) + POP_WIDE_PAIR(q0, q1) + + POP_PAIR(x8, lr) + POP_PAIR(x6, x7) + POP_PAIR(x4, x5) + POP_PAIR(x2, x3) + POP_PAIR(x0, x1) + + br lr + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRAddOutputMode + .p2align 4 + .type XRRAddOutputMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRAddOutputMode +#endif +XRRAddOutputMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+0 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+0] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 0 & 0xffff +#if 0 > 0xffff + movk ip0, 0 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRAllocGamma + .p2align 4 + .type XRRAllocGamma, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRAllocGamma +#endif +XRRAllocGamma: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+8 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+8] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1 & 0xffff +#if 1 > 0xffff + movk ip0, 1 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRAllocModeInfo + .p2align 4 + .type XRRAllocModeInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRAllocModeInfo +#endif +XRRAllocModeInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+16 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+16] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 2 & 0xffff +#if 2 > 0xffff + movk ip0, 2 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRAllocateMonitor + .p2align 4 + .type XRRAllocateMonitor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRAllocateMonitor +#endif +XRRAllocateMonitor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+24 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+24] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 3 & 0xffff +#if 3 > 0xffff + movk ip0, 3 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRChangeOutputProperty + .p2align 4 + .type XRRChangeOutputProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRChangeOutputProperty +#endif +XRRChangeOutputProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+32 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+32] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 4 & 0xffff +#if 4 > 0xffff + movk ip0, 4 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRChangeProviderProperty + .p2align 4 + .type XRRChangeProviderProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRChangeProviderProperty +#endif +XRRChangeProviderProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+40 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+40] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 5 & 0xffff +#if 5 > 0xffff + movk ip0, 5 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRConfigCurrentConfiguration + .p2align 4 + .type XRRConfigCurrentConfiguration, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRConfigCurrentConfiguration +#endif +XRRConfigCurrentConfiguration: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+48 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+48] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 6 & 0xffff +#if 6 > 0xffff + movk ip0, 6 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRConfigCurrentRate + .p2align 4 + .type XRRConfigCurrentRate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRConfigCurrentRate +#endif +XRRConfigCurrentRate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+56 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+56] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 7 & 0xffff +#if 7 > 0xffff + movk ip0, 7 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRConfigRates + .p2align 4 + .type XRRConfigRates, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRConfigRates +#endif +XRRConfigRates: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+64 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+64] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 8 & 0xffff +#if 8 > 0xffff + movk ip0, 8 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRConfigRotations + .p2align 4 + .type XRRConfigRotations, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRConfigRotations +#endif +XRRConfigRotations: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+72 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+72] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 9 & 0xffff +#if 9 > 0xffff + movk ip0, 9 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRConfigSizes + .p2align 4 + .type XRRConfigSizes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRConfigSizes +#endif +XRRConfigSizes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+80 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+80] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 10 & 0xffff +#if 10 > 0xffff + movk ip0, 10 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRConfigTimes + .p2align 4 + .type XRRConfigTimes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRConfigTimes +#endif +XRRConfigTimes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+88 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+88] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 11 & 0xffff +#if 11 > 0xffff + movk ip0, 11 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRConfigureOutputProperty + .p2align 4 + .type XRRConfigureOutputProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRConfigureOutputProperty +#endif +XRRConfigureOutputProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+96 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+96] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 12 & 0xffff +#if 12 > 0xffff + movk ip0, 12 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRConfigureProviderProperty + .p2align 4 + .type XRRConfigureProviderProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRConfigureProviderProperty +#endif +XRRConfigureProviderProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+104 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 13 & 0xffff +#if 13 > 0xffff + movk ip0, 13 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRCreateMode + .p2align 4 + .type XRRCreateMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRCreateMode +#endif +XRRCreateMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+112 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 14 & 0xffff +#if 14 > 0xffff + movk ip0, 14 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRDeleteMonitor + .p2align 4 + .type XRRDeleteMonitor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRDeleteMonitor +#endif +XRRDeleteMonitor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+120 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 15 & 0xffff +#if 15 > 0xffff + movk ip0, 15 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRDeleteOutputMode + .p2align 4 + .type XRRDeleteOutputMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRDeleteOutputMode +#endif +XRRDeleteOutputMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+128 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 16 & 0xffff +#if 16 > 0xffff + movk ip0, 16 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRDeleteOutputProperty + .p2align 4 + .type XRRDeleteOutputProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRDeleteOutputProperty +#endif +XRRDeleteOutputProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+136 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 17 & 0xffff +#if 17 > 0xffff + movk ip0, 17 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRDeleteProviderProperty + .p2align 4 + .type XRRDeleteProviderProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRDeleteProviderProperty +#endif +XRRDeleteProviderProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+144 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 18 & 0xffff +#if 18 > 0xffff + movk ip0, 18 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRDestroyMode + .p2align 4 + .type XRRDestroyMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRDestroyMode +#endif +XRRDestroyMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+152 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 19 & 0xffff +#if 19 > 0xffff + movk ip0, 19 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreeCrtcInfo + .p2align 4 + .type XRRFreeCrtcInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreeCrtcInfo +#endif +XRRFreeCrtcInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+160 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 20 & 0xffff +#if 20 > 0xffff + movk ip0, 20 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreeGamma + .p2align 4 + .type XRRFreeGamma, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreeGamma +#endif +XRRFreeGamma: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+168 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 21 & 0xffff +#if 21 > 0xffff + movk ip0, 21 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreeModeInfo + .p2align 4 + .type XRRFreeModeInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreeModeInfo +#endif +XRRFreeModeInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+176 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 22 & 0xffff +#if 22 > 0xffff + movk ip0, 22 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreeMonitors + .p2align 4 + .type XRRFreeMonitors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreeMonitors +#endif +XRRFreeMonitors: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+184 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 23 & 0xffff +#if 23 > 0xffff + movk ip0, 23 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreeOutputInfo + .p2align 4 + .type XRRFreeOutputInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreeOutputInfo +#endif +XRRFreeOutputInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+192 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 24 & 0xffff +#if 24 > 0xffff + movk ip0, 24 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreePanning + .p2align 4 + .type XRRFreePanning, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreePanning +#endif +XRRFreePanning: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+200 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 25 & 0xffff +#if 25 > 0xffff + movk ip0, 25 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreeProviderInfo + .p2align 4 + .type XRRFreeProviderInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreeProviderInfo +#endif +XRRFreeProviderInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+208 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 26 & 0xffff +#if 26 > 0xffff + movk ip0, 26 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreeProviderResources + .p2align 4 + .type XRRFreeProviderResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreeProviderResources +#endif +XRRFreeProviderResources: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+216 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 27 & 0xffff +#if 27 > 0xffff + movk ip0, 27 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreeScreenConfigInfo + .p2align 4 + .type XRRFreeScreenConfigInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreeScreenConfigInfo +#endif +XRRFreeScreenConfigInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+224 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 28 & 0xffff +#if 28 > 0xffff + movk ip0, 28 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreeScreenResources + .p2align 4 + .type XRRFreeScreenResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreeScreenResources +#endif +XRRFreeScreenResources: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+232 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 29 & 0xffff +#if 29 > 0xffff + movk ip0, 29 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetCrtcGamma + .p2align 4 + .type XRRGetCrtcGamma, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetCrtcGamma +#endif +XRRGetCrtcGamma: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+240 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 30 & 0xffff +#if 30 > 0xffff + movk ip0, 30 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetCrtcGammaSize + .p2align 4 + .type XRRGetCrtcGammaSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetCrtcGammaSize +#endif +XRRGetCrtcGammaSize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+248 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 31 & 0xffff +#if 31 > 0xffff + movk ip0, 31 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetCrtcInfo + .p2align 4 + .type XRRGetCrtcInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetCrtcInfo +#endif +XRRGetCrtcInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+256 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 32 & 0xffff +#if 32 > 0xffff + movk ip0, 32 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetCrtcTransform + .p2align 4 + .type XRRGetCrtcTransform, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetCrtcTransform +#endif +XRRGetCrtcTransform: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+264 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 33 & 0xffff +#if 33 > 0xffff + movk ip0, 33 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetMonitors + .p2align 4 + .type XRRGetMonitors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetMonitors +#endif +XRRGetMonitors: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+272 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 34 & 0xffff +#if 34 > 0xffff + movk ip0, 34 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetOutputInfo + .p2align 4 + .type XRRGetOutputInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetOutputInfo +#endif +XRRGetOutputInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+280 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 35 & 0xffff +#if 35 > 0xffff + movk ip0, 35 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetOutputPrimary + .p2align 4 + .type XRRGetOutputPrimary, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetOutputPrimary +#endif +XRRGetOutputPrimary: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+288 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 36 & 0xffff +#if 36 > 0xffff + movk ip0, 36 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetOutputProperty + .p2align 4 + .type XRRGetOutputProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetOutputProperty +#endif +XRRGetOutputProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+296 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 37 & 0xffff +#if 37 > 0xffff + movk ip0, 37 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetPanning + .p2align 4 + .type XRRGetPanning, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetPanning +#endif +XRRGetPanning: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+304 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 38 & 0xffff +#if 38 > 0xffff + movk ip0, 38 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetProviderInfo + .p2align 4 + .type XRRGetProviderInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetProviderInfo +#endif +XRRGetProviderInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+312 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 39 & 0xffff +#if 39 > 0xffff + movk ip0, 39 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetProviderProperty + .p2align 4 + .type XRRGetProviderProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetProviderProperty +#endif +XRRGetProviderProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+320 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 40 & 0xffff +#if 40 > 0xffff + movk ip0, 40 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetProviderResources + .p2align 4 + .type XRRGetProviderResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetProviderResources +#endif +XRRGetProviderResources: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+328 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 41 & 0xffff +#if 41 > 0xffff + movk ip0, 41 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetScreenInfo + .p2align 4 + .type XRRGetScreenInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetScreenInfo +#endif +XRRGetScreenInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+336 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 42 & 0xffff +#if 42 > 0xffff + movk ip0, 42 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetScreenResources + .p2align 4 + .type XRRGetScreenResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetScreenResources +#endif +XRRGetScreenResources: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+344 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 43 & 0xffff +#if 43 > 0xffff + movk ip0, 43 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetScreenResourcesCurrent + .p2align 4 + .type XRRGetScreenResourcesCurrent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetScreenResourcesCurrent +#endif +XRRGetScreenResourcesCurrent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+352 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 44 & 0xffff +#if 44 > 0xffff + movk ip0, 44 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetScreenSizeRange + .p2align 4 + .type XRRGetScreenSizeRange, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetScreenSizeRange +#endif +XRRGetScreenSizeRange: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+360 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 45 & 0xffff +#if 45 > 0xffff + movk ip0, 45 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRListOutputProperties + .p2align 4 + .type XRRListOutputProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRListOutputProperties +#endif +XRRListOutputProperties: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+368 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 46 & 0xffff +#if 46 > 0xffff + movk ip0, 46 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRListProviderProperties + .p2align 4 + .type XRRListProviderProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRListProviderProperties +#endif +XRRListProviderProperties: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+376 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 47 & 0xffff +#if 47 > 0xffff + movk ip0, 47 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRQueryExtension + .p2align 4 + .type XRRQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRQueryExtension +#endif +XRRQueryExtension: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+384 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 48 & 0xffff +#if 48 > 0xffff + movk ip0, 48 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRQueryOutputProperty + .p2align 4 + .type XRRQueryOutputProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRQueryOutputProperty +#endif +XRRQueryOutputProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+392 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 49 & 0xffff +#if 49 > 0xffff + movk ip0, 49 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRQueryProviderProperty + .p2align 4 + .type XRRQueryProviderProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRQueryProviderProperty +#endif +XRRQueryProviderProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+400 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 50 & 0xffff +#if 50 > 0xffff + movk ip0, 50 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRQueryVersion + .p2align 4 + .type XRRQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRQueryVersion +#endif +XRRQueryVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+408 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 51 & 0xffff +#if 51 > 0xffff + movk ip0, 51 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRRates + .p2align 4 + .type XRRRates, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRRates +#endif +XRRRates: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+416 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 52 & 0xffff +#if 52 > 0xffff + movk ip0, 52 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRRootToScreen + .p2align 4 + .type XRRRootToScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRRootToScreen +#endif +XRRRootToScreen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+424 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 53 & 0xffff +#if 53 > 0xffff + movk ip0, 53 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRRotations + .p2align 4 + .type XRRRotations, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRRotations +#endif +XRRRotations: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+432 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 54 & 0xffff +#if 54 > 0xffff + movk ip0, 54 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSelectInput + .p2align 4 + .type XRRSelectInput, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSelectInput +#endif +XRRSelectInput: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+440 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 55 & 0xffff +#if 55 > 0xffff + movk ip0, 55 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetCrtcConfig + .p2align 4 + .type XRRSetCrtcConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetCrtcConfig +#endif +XRRSetCrtcConfig: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+448 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 56 & 0xffff +#if 56 > 0xffff + movk ip0, 56 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetCrtcGamma + .p2align 4 + .type XRRSetCrtcGamma, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetCrtcGamma +#endif +XRRSetCrtcGamma: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+456 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+456] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 57 & 0xffff +#if 57 > 0xffff + movk ip0, 57 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetCrtcTransform + .p2align 4 + .type XRRSetCrtcTransform, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetCrtcTransform +#endif +XRRSetCrtcTransform: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+464 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+464] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 58 & 0xffff +#if 58 > 0xffff + movk ip0, 58 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetMonitor + .p2align 4 + .type XRRSetMonitor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetMonitor +#endif +XRRSetMonitor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+472 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+472] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 59 & 0xffff +#if 59 > 0xffff + movk ip0, 59 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetOutputPrimary + .p2align 4 + .type XRRSetOutputPrimary, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetOutputPrimary +#endif +XRRSetOutputPrimary: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+480 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+480] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 60 & 0xffff +#if 60 > 0xffff + movk ip0, 60 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetPanning + .p2align 4 + .type XRRSetPanning, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetPanning +#endif +XRRSetPanning: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+488 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+488] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 61 & 0xffff +#if 61 > 0xffff + movk ip0, 61 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetProviderOffloadSink + .p2align 4 + .type XRRSetProviderOffloadSink, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetProviderOffloadSink +#endif +XRRSetProviderOffloadSink: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+496 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+496] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 62 & 0xffff +#if 62 > 0xffff + movk ip0, 62 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetProviderOutputSource + .p2align 4 + .type XRRSetProviderOutputSource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetProviderOutputSource +#endif +XRRSetProviderOutputSource: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+504 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+504] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 63 & 0xffff +#if 63 > 0xffff + movk ip0, 63 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetScreenConfig + .p2align 4 + .type XRRSetScreenConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetScreenConfig +#endif +XRRSetScreenConfig: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+512 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+512] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 64 & 0xffff +#if 64 > 0xffff + movk ip0, 64 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetScreenConfigAndRate + .p2align 4 + .type XRRSetScreenConfigAndRate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetScreenConfigAndRate +#endif +XRRSetScreenConfigAndRate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+520 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+520] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 65 & 0xffff +#if 65 > 0xffff + movk ip0, 65 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetScreenSize + .p2align 4 + .type XRRSetScreenSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetScreenSize +#endif +XRRSetScreenSize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+528 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+528] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 66 & 0xffff +#if 66 > 0xffff + movk ip0, 66 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSizes + .p2align 4 + .type XRRSizes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSizes +#endif +XRRSizes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+536 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+536] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 67 & 0xffff +#if 67 > 0xffff + movk ip0, 67 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRTimes + .p2align 4 + .type XRRTimes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRTimes +#endif +XRRTimes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+544 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+544] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 68 & 0xffff +#if 68 > 0xffff + movk ip0, 68 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRUpdateConfiguration + .p2align 4 + .type XRRUpdateConfiguration, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRUpdateConfiguration +#endif +XRRUpdateConfiguration: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libXrandr_so_tramp_table+552 + ldr ip0, [ip0, #:lo12:_libXrandr_so_tramp_table+552] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 69 & 0xffff +#if 69 > 0xffff + movk ip0, 69 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libXrandr_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libdrm.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libdrm.so.init.c new file mode 100644 index 0000000..244159b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libdrm.so.init.c @@ -0,0 +1,456 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libdrm.so.2: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libdrm.so.2"); + CHECK(lib_handle, "failed to load library 'libdrm.so.2' via callback ''"); +#else + lib_handle = dlopen("libdrm.so.2", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libdrm.so.2' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "drmAddBufs", + "drmAddContextPrivateMapping", + "drmAddContextTag", + "drmAddMap", + "drmAgpAcquire", + "drmAgpAlloc", + "drmAgpBase", + "drmAgpBind", + "drmAgpDeviceId", + "drmAgpEnable", + "drmAgpFree", + "drmAgpGetMode", + "drmAgpMemoryAvail", + "drmAgpMemoryUsed", + "drmAgpRelease", + "drmAgpSize", + "drmAgpUnbind", + "drmAgpVendorId", + "drmAgpVersionMajor", + "drmAgpVersionMinor", + "drmAuthMagic", + "drmAvailable", + "drmCheckModesettingSupported", + "drmClose", + "drmCloseBufferHandle", + "drmCloseOnce", + "drmCommandNone", + "drmCommandRead", + "drmCommandWrite", + "drmCommandWriteRead", + "drmCreateContext", + "drmCreateDrawable", + "drmCrtcGetSequence", + "drmCrtcQueueSequence", + "drmCtlInstHandler", + "drmCtlUninstHandler", + "drmDMA", + "drmDelContextTag", + "drmDestroyContext", + "drmDestroyDrawable", + "drmDevicesEqual", + "drmDropMaster", + "drmError", + "drmFinish", + "drmFree", + "drmFreeBufs", + "drmFreeBusid", + "drmFreeDevice", + "drmFreeDevices", + "drmFreeReservedContextList", + "drmFreeVersion", + "drmGetBufInfo", + "drmGetBusid", + "drmGetCap", + "drmGetClient", + "drmGetContextFlags", + "drmGetContextPrivateMapping", + "drmGetContextTag", + "drmGetDevice", + "drmGetDevice2", + "drmGetDeviceFromDevId", + "drmGetDeviceNameFromFd", + "drmGetDeviceNameFromFd2", + "drmGetDevices", + "drmGetDevices2", + "drmGetEntry", + "drmGetFormatModifierName", + "drmGetFormatModifierVendor", + "drmGetFormatName", + "drmGetHashTable", + "drmGetInterruptFromBusID", + "drmGetLibVersion", + "drmGetLock", + "drmGetMagic", + "drmGetMap", + "drmGetNodeTypeFromDevId", + "drmGetNodeTypeFromFd", + "drmGetPrimaryDeviceNameFromFd", + "drmGetRenderDeviceNameFromFd", + "drmGetReservedContextList", + "drmGetStats", + "drmGetVersion", + "drmHandleEvent", + "drmHashCreate", + "drmHashDelete", + "drmHashDestroy", + "drmHashFirst", + "drmHashInsert", + "drmHashLookup", + "drmHashNext", + "drmIoctl", + "drmIsKMS", + "drmIsMaster", + "drmMalloc", + "drmMap", + "drmMapBufs", + "drmMarkBufs", + "drmModeAddFB", + "drmModeAddFB2", + "drmModeAddFB2WithModifiers", + "drmModeAtomicAddProperty", + "drmModeAtomicAlloc", + "drmModeAtomicCommit", + "drmModeAtomicDuplicate", + "drmModeAtomicFree", + "drmModeAtomicGetCursor", + "drmModeAtomicMerge", + "drmModeAtomicSetCursor", + "drmModeAttachMode", + "drmModeCloseFB", + "drmModeConnectorGetPossibleCrtcs", + "drmModeConnectorSetProperty", + "drmModeCreateDumbBuffer", + "drmModeCreateLease", + "drmModeCreatePropertyBlob", + "drmModeCrtcGetGamma", + "drmModeCrtcSetGamma", + "drmModeDestroyDumbBuffer", + "drmModeDestroyPropertyBlob", + "drmModeDetachMode", + "drmModeDirtyFB", + "drmModeFormatModifierBlobIterNext", + "drmModeFreeConnector", + "drmModeFreeCrtc", + "drmModeFreeEncoder", + "drmModeFreeFB", + "drmModeFreeFB2", + "drmModeFreeModeInfo", + "drmModeFreeObjectProperties", + "drmModeFreePlane", + "drmModeFreePlaneResources", + "drmModeFreeProperty", + "drmModeFreePropertyBlob", + "drmModeFreeResources", + "drmModeGetConnector", + "drmModeGetConnectorCurrent", + "drmModeGetConnectorTypeName", + "drmModeGetCrtc", + "drmModeGetEncoder", + "drmModeGetFB", + "drmModeGetFB2", + "drmModeGetLease", + "drmModeGetPlane", + "drmModeGetPlaneResources", + "drmModeGetProperty", + "drmModeGetPropertyBlob", + "drmModeGetResources", + "drmModeListLessees", + "drmModeMapDumbBuffer", + "drmModeMoveCursor", + "drmModeObjectGetProperties", + "drmModeObjectSetProperty", + "drmModePageFlip", + "drmModePageFlipTarget", + "drmModeRevokeLease", + "drmModeRmFB", + "drmModeSetCrtc", + "drmModeSetCursor", + "drmModeSetCursor2", + "drmModeSetPlane", + "drmMsg", + "drmOpen", + "drmOpenControl", + "drmOpenOnce", + "drmOpenOnceWithType", + "drmOpenRender", + "drmOpenWithType", + "drmPrimeFDToHandle", + "drmPrimeHandleToFD", + "drmRandom", + "drmRandomCreate", + "drmRandomDestroy", + "drmRandomDouble", + "drmRmMap", + "drmSLCreate", + "drmSLDelete", + "drmSLDestroy", + "drmSLDump", + "drmSLFirst", + "drmSLInsert", + "drmSLLookup", + "drmSLLookupNeighbors", + "drmSLNext", + "drmScatterGatherAlloc", + "drmScatterGatherFree", + "drmSetBusid", + "drmSetClientCap", + "drmSetContextFlags", + "drmSetInterfaceVersion", + "drmSetMaster", + "drmSetServerInfo", + "drmSwitchToContext", + "drmSyncobjCreate", + "drmSyncobjDestroy", + "drmSyncobjEventfd", + "drmSyncobjExportSyncFile", + "drmSyncobjFDToHandle", + "drmSyncobjHandleToFD", + "drmSyncobjImportSyncFile", + "drmSyncobjQuery", + "drmSyncobjQuery2", + "drmSyncobjReset", + "drmSyncobjSignal", + "drmSyncobjTimelineSignal", + "drmSyncobjTimelineWait", + "drmSyncobjTransfer", + "drmSyncobjWait", + "drmUnlock", + "drmUnmap", + "drmUnmapBufs", + "drmUpdateDrawableInfo", + "drmWaitVBlank", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libdrm_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libdrm_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libdrm_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libdrm_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libdrm_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libdrm_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libdrm_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libdrm_so_tramp_table, 0, SYM_COUNT * sizeof(_libdrm_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libdrm.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libdrm.so.tramp.S new file mode 100644 index 0000000..54ba00b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libdrm.so.tramp.S @@ -0,0 +1,8773 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#define lr x30 +#define ip0 x16 + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libdrm_so_tramp_table + .hidden _libdrm_so_tramp_table + .align 8 +_libdrm_so_tramp_table: + .zero 1704 + + .text + + .globl _libdrm_so_tramp_resolve + .hidden _libdrm_so_tramp_resolve + + .globl _libdrm_so_save_regs_and_resolve + .hidden _libdrm_so_save_regs_and_resolve + .type _libdrm_so_save_regs_and_resolve, %function +_libdrm_so_save_regs_and_resolve: + .cfi_startproc + + // Slow path which calls dlsym, taken only on first call. + // Registers are saved according to "Procedure Call Standard for the Arm® 64-bit Architecture". + // For DWARF directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + // Stack is aligned at 16 bytes + +#define PUSH_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 8 +#define POP_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore reg2; .cfi_restore reg1 + +#define PUSH_WIDE_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-32]!; .cfi_adjust_cfa_offset 32; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 16 +#define POP_WIDE_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #32; .cfi_adjust_cfa_offset -32; .cfi_restore reg2; .cfi_restore reg1 + + // Save only arguments (and lr) + PUSH_PAIR(x0, x1) + PUSH_PAIR(x2, x3) + PUSH_PAIR(x4, x5) + PUSH_PAIR(x6, x7) + PUSH_PAIR(x8, lr) + + ldr x0, [sp, #80] // 16*5 + + PUSH_WIDE_PAIR(q0, q1) + PUSH_WIDE_PAIR(q2, q3) + PUSH_WIDE_PAIR(q4, q5) + PUSH_WIDE_PAIR(q6, q7) + + // Stack is aligned at 16 bytes + + bl _libdrm_so_tramp_resolve + mov ip0, x0 + + // TODO: pop pc? + + POP_WIDE_PAIR(q6, q7) + POP_WIDE_PAIR(q4, q5) + POP_WIDE_PAIR(q2, q3) + POP_WIDE_PAIR(q0, q1) + + POP_PAIR(x8, lr) + POP_PAIR(x6, x7) + POP_PAIR(x4, x5) + POP_PAIR(x2, x3) + POP_PAIR(x0, x1) + + br lr + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAddBufs + .p2align 4 + .type drmAddBufs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAddBufs +#endif +drmAddBufs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+0 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+0] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 0 & 0xffff +#if 0 > 0xffff + movk ip0, 0 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAddContextPrivateMapping + .p2align 4 + .type drmAddContextPrivateMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAddContextPrivateMapping +#endif +drmAddContextPrivateMapping: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+8 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+8] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1 & 0xffff +#if 1 > 0xffff + movk ip0, 1 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAddContextTag + .p2align 4 + .type drmAddContextTag, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAddContextTag +#endif +drmAddContextTag: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+16 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+16] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 2 & 0xffff +#if 2 > 0xffff + movk ip0, 2 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAddMap + .p2align 4 + .type drmAddMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAddMap +#endif +drmAddMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+24 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+24] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 3 & 0xffff +#if 3 > 0xffff + movk ip0, 3 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpAcquire + .p2align 4 + .type drmAgpAcquire, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpAcquire +#endif +drmAgpAcquire: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+32 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+32] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 4 & 0xffff +#if 4 > 0xffff + movk ip0, 4 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpAlloc + .p2align 4 + .type drmAgpAlloc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpAlloc +#endif +drmAgpAlloc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+40 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+40] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 5 & 0xffff +#if 5 > 0xffff + movk ip0, 5 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpBase + .p2align 4 + .type drmAgpBase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpBase +#endif +drmAgpBase: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+48 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+48] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 6 & 0xffff +#if 6 > 0xffff + movk ip0, 6 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpBind + .p2align 4 + .type drmAgpBind, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpBind +#endif +drmAgpBind: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+56 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+56] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 7 & 0xffff +#if 7 > 0xffff + movk ip0, 7 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpDeviceId + .p2align 4 + .type drmAgpDeviceId, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpDeviceId +#endif +drmAgpDeviceId: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+64 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+64] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 8 & 0xffff +#if 8 > 0xffff + movk ip0, 8 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpEnable + .p2align 4 + .type drmAgpEnable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpEnable +#endif +drmAgpEnable: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+72 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+72] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 9 & 0xffff +#if 9 > 0xffff + movk ip0, 9 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpFree + .p2align 4 + .type drmAgpFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpFree +#endif +drmAgpFree: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+80 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+80] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 10 & 0xffff +#if 10 > 0xffff + movk ip0, 10 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpGetMode + .p2align 4 + .type drmAgpGetMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpGetMode +#endif +drmAgpGetMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+88 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+88] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 11 & 0xffff +#if 11 > 0xffff + movk ip0, 11 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpMemoryAvail + .p2align 4 + .type drmAgpMemoryAvail, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpMemoryAvail +#endif +drmAgpMemoryAvail: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+96 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+96] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 12 & 0xffff +#if 12 > 0xffff + movk ip0, 12 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpMemoryUsed + .p2align 4 + .type drmAgpMemoryUsed, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpMemoryUsed +#endif +drmAgpMemoryUsed: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+104 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 13 & 0xffff +#if 13 > 0xffff + movk ip0, 13 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpRelease + .p2align 4 + .type drmAgpRelease, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpRelease +#endif +drmAgpRelease: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+112 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 14 & 0xffff +#if 14 > 0xffff + movk ip0, 14 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpSize + .p2align 4 + .type drmAgpSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpSize +#endif +drmAgpSize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+120 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 15 & 0xffff +#if 15 > 0xffff + movk ip0, 15 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpUnbind + .p2align 4 + .type drmAgpUnbind, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpUnbind +#endif +drmAgpUnbind: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+128 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 16 & 0xffff +#if 16 > 0xffff + movk ip0, 16 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpVendorId + .p2align 4 + .type drmAgpVendorId, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpVendorId +#endif +drmAgpVendorId: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+136 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 17 & 0xffff +#if 17 > 0xffff + movk ip0, 17 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpVersionMajor + .p2align 4 + .type drmAgpVersionMajor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpVersionMajor +#endif +drmAgpVersionMajor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+144 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 18 & 0xffff +#if 18 > 0xffff + movk ip0, 18 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpVersionMinor + .p2align 4 + .type drmAgpVersionMinor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpVersionMinor +#endif +drmAgpVersionMinor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+152 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 19 & 0xffff +#if 19 > 0xffff + movk ip0, 19 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAuthMagic + .p2align 4 + .type drmAuthMagic, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAuthMagic +#endif +drmAuthMagic: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+160 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 20 & 0xffff +#if 20 > 0xffff + movk ip0, 20 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAvailable + .p2align 4 + .type drmAvailable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAvailable +#endif +drmAvailable: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+168 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 21 & 0xffff +#if 21 > 0xffff + movk ip0, 21 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCheckModesettingSupported + .p2align 4 + .type drmCheckModesettingSupported, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCheckModesettingSupported +#endif +drmCheckModesettingSupported: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+176 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 22 & 0xffff +#if 22 > 0xffff + movk ip0, 22 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmClose + .p2align 4 + .type drmClose, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmClose +#endif +drmClose: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+184 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 23 & 0xffff +#if 23 > 0xffff + movk ip0, 23 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCloseBufferHandle + .p2align 4 + .type drmCloseBufferHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCloseBufferHandle +#endif +drmCloseBufferHandle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+192 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 24 & 0xffff +#if 24 > 0xffff + movk ip0, 24 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCloseOnce + .p2align 4 + .type drmCloseOnce, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCloseOnce +#endif +drmCloseOnce: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+200 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 25 & 0xffff +#if 25 > 0xffff + movk ip0, 25 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCommandNone + .p2align 4 + .type drmCommandNone, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCommandNone +#endif +drmCommandNone: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+208 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 26 & 0xffff +#if 26 > 0xffff + movk ip0, 26 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCommandRead + .p2align 4 + .type drmCommandRead, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCommandRead +#endif +drmCommandRead: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+216 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 27 & 0xffff +#if 27 > 0xffff + movk ip0, 27 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCommandWrite + .p2align 4 + .type drmCommandWrite, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCommandWrite +#endif +drmCommandWrite: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+224 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 28 & 0xffff +#if 28 > 0xffff + movk ip0, 28 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCommandWriteRead + .p2align 4 + .type drmCommandWriteRead, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCommandWriteRead +#endif +drmCommandWriteRead: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+232 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 29 & 0xffff +#if 29 > 0xffff + movk ip0, 29 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCreateContext + .p2align 4 + .type drmCreateContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCreateContext +#endif +drmCreateContext: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+240 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 30 & 0xffff +#if 30 > 0xffff + movk ip0, 30 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCreateDrawable + .p2align 4 + .type drmCreateDrawable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCreateDrawable +#endif +drmCreateDrawable: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+248 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 31 & 0xffff +#if 31 > 0xffff + movk ip0, 31 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCrtcGetSequence + .p2align 4 + .type drmCrtcGetSequence, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCrtcGetSequence +#endif +drmCrtcGetSequence: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+256 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 32 & 0xffff +#if 32 > 0xffff + movk ip0, 32 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCrtcQueueSequence + .p2align 4 + .type drmCrtcQueueSequence, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCrtcQueueSequence +#endif +drmCrtcQueueSequence: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+264 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 33 & 0xffff +#if 33 > 0xffff + movk ip0, 33 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCtlInstHandler + .p2align 4 + .type drmCtlInstHandler, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCtlInstHandler +#endif +drmCtlInstHandler: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+272 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 34 & 0xffff +#if 34 > 0xffff + movk ip0, 34 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCtlUninstHandler + .p2align 4 + .type drmCtlUninstHandler, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCtlUninstHandler +#endif +drmCtlUninstHandler: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+280 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 35 & 0xffff +#if 35 > 0xffff + movk ip0, 35 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmDMA + .p2align 4 + .type drmDMA, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmDMA +#endif +drmDMA: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+288 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 36 & 0xffff +#if 36 > 0xffff + movk ip0, 36 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmDelContextTag + .p2align 4 + .type drmDelContextTag, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmDelContextTag +#endif +drmDelContextTag: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+296 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 37 & 0xffff +#if 37 > 0xffff + movk ip0, 37 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmDestroyContext + .p2align 4 + .type drmDestroyContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmDestroyContext +#endif +drmDestroyContext: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+304 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 38 & 0xffff +#if 38 > 0xffff + movk ip0, 38 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmDestroyDrawable + .p2align 4 + .type drmDestroyDrawable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmDestroyDrawable +#endif +drmDestroyDrawable: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+312 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 39 & 0xffff +#if 39 > 0xffff + movk ip0, 39 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmDevicesEqual + .p2align 4 + .type drmDevicesEqual, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmDevicesEqual +#endif +drmDevicesEqual: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+320 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 40 & 0xffff +#if 40 > 0xffff + movk ip0, 40 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmDropMaster + .p2align 4 + .type drmDropMaster, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmDropMaster +#endif +drmDropMaster: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+328 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 41 & 0xffff +#if 41 > 0xffff + movk ip0, 41 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmError + .p2align 4 + .type drmError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmError +#endif +drmError: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+336 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 42 & 0xffff +#if 42 > 0xffff + movk ip0, 42 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmFinish + .p2align 4 + .type drmFinish, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmFinish +#endif +drmFinish: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+344 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 43 & 0xffff +#if 43 > 0xffff + movk ip0, 43 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmFree + .p2align 4 + .type drmFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmFree +#endif +drmFree: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+352 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 44 & 0xffff +#if 44 > 0xffff + movk ip0, 44 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmFreeBufs + .p2align 4 + .type drmFreeBufs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmFreeBufs +#endif +drmFreeBufs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+360 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 45 & 0xffff +#if 45 > 0xffff + movk ip0, 45 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmFreeBusid + .p2align 4 + .type drmFreeBusid, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmFreeBusid +#endif +drmFreeBusid: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+368 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 46 & 0xffff +#if 46 > 0xffff + movk ip0, 46 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmFreeDevice + .p2align 4 + .type drmFreeDevice, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmFreeDevice +#endif +drmFreeDevice: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+376 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 47 & 0xffff +#if 47 > 0xffff + movk ip0, 47 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmFreeDevices + .p2align 4 + .type drmFreeDevices, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmFreeDevices +#endif +drmFreeDevices: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+384 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 48 & 0xffff +#if 48 > 0xffff + movk ip0, 48 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmFreeReservedContextList + .p2align 4 + .type drmFreeReservedContextList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmFreeReservedContextList +#endif +drmFreeReservedContextList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+392 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 49 & 0xffff +#if 49 > 0xffff + movk ip0, 49 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmFreeVersion + .p2align 4 + .type drmFreeVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmFreeVersion +#endif +drmFreeVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+400 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 50 & 0xffff +#if 50 > 0xffff + movk ip0, 50 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetBufInfo + .p2align 4 + .type drmGetBufInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetBufInfo +#endif +drmGetBufInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+408 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 51 & 0xffff +#if 51 > 0xffff + movk ip0, 51 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetBusid + .p2align 4 + .type drmGetBusid, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetBusid +#endif +drmGetBusid: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+416 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 52 & 0xffff +#if 52 > 0xffff + movk ip0, 52 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetCap + .p2align 4 + .type drmGetCap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetCap +#endif +drmGetCap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+424 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 53 & 0xffff +#if 53 > 0xffff + movk ip0, 53 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetClient + .p2align 4 + .type drmGetClient, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetClient +#endif +drmGetClient: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+432 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 54 & 0xffff +#if 54 > 0xffff + movk ip0, 54 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetContextFlags + .p2align 4 + .type drmGetContextFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetContextFlags +#endif +drmGetContextFlags: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+440 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 55 & 0xffff +#if 55 > 0xffff + movk ip0, 55 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetContextPrivateMapping + .p2align 4 + .type drmGetContextPrivateMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetContextPrivateMapping +#endif +drmGetContextPrivateMapping: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+448 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 56 & 0xffff +#if 56 > 0xffff + movk ip0, 56 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetContextTag + .p2align 4 + .type drmGetContextTag, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetContextTag +#endif +drmGetContextTag: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+456 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+456] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 57 & 0xffff +#if 57 > 0xffff + movk ip0, 57 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetDevice + .p2align 4 + .type drmGetDevice, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetDevice +#endif +drmGetDevice: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+464 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+464] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 58 & 0xffff +#if 58 > 0xffff + movk ip0, 58 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetDevice2 + .p2align 4 + .type drmGetDevice2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetDevice2 +#endif +drmGetDevice2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+472 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+472] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 59 & 0xffff +#if 59 > 0xffff + movk ip0, 59 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetDeviceFromDevId + .p2align 4 + .type drmGetDeviceFromDevId, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetDeviceFromDevId +#endif +drmGetDeviceFromDevId: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+480 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+480] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 60 & 0xffff +#if 60 > 0xffff + movk ip0, 60 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetDeviceNameFromFd + .p2align 4 + .type drmGetDeviceNameFromFd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetDeviceNameFromFd +#endif +drmGetDeviceNameFromFd: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+488 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+488] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 61 & 0xffff +#if 61 > 0xffff + movk ip0, 61 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetDeviceNameFromFd2 + .p2align 4 + .type drmGetDeviceNameFromFd2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetDeviceNameFromFd2 +#endif +drmGetDeviceNameFromFd2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+496 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+496] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 62 & 0xffff +#if 62 > 0xffff + movk ip0, 62 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetDevices + .p2align 4 + .type drmGetDevices, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetDevices +#endif +drmGetDevices: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+504 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+504] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 63 & 0xffff +#if 63 > 0xffff + movk ip0, 63 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetDevices2 + .p2align 4 + .type drmGetDevices2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetDevices2 +#endif +drmGetDevices2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+512 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+512] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 64 & 0xffff +#if 64 > 0xffff + movk ip0, 64 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetEntry + .p2align 4 + .type drmGetEntry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetEntry +#endif +drmGetEntry: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+520 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+520] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 65 & 0xffff +#if 65 > 0xffff + movk ip0, 65 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetFormatModifierName + .p2align 4 + .type drmGetFormatModifierName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetFormatModifierName +#endif +drmGetFormatModifierName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+528 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+528] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 66 & 0xffff +#if 66 > 0xffff + movk ip0, 66 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetFormatModifierVendor + .p2align 4 + .type drmGetFormatModifierVendor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetFormatModifierVendor +#endif +drmGetFormatModifierVendor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+536 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+536] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 67 & 0xffff +#if 67 > 0xffff + movk ip0, 67 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetFormatName + .p2align 4 + .type drmGetFormatName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetFormatName +#endif +drmGetFormatName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+544 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+544] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 68 & 0xffff +#if 68 > 0xffff + movk ip0, 68 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetHashTable + .p2align 4 + .type drmGetHashTable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetHashTable +#endif +drmGetHashTable: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+552 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+552] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 69 & 0xffff +#if 69 > 0xffff + movk ip0, 69 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetInterruptFromBusID + .p2align 4 + .type drmGetInterruptFromBusID, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetInterruptFromBusID +#endif +drmGetInterruptFromBusID: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+560 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+560] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 70 & 0xffff +#if 70 > 0xffff + movk ip0, 70 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetLibVersion + .p2align 4 + .type drmGetLibVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetLibVersion +#endif +drmGetLibVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+568 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+568] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 71 & 0xffff +#if 71 > 0xffff + movk ip0, 71 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetLock + .p2align 4 + .type drmGetLock, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetLock +#endif +drmGetLock: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+576 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+576] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 72 & 0xffff +#if 72 > 0xffff + movk ip0, 72 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetMagic + .p2align 4 + .type drmGetMagic, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetMagic +#endif +drmGetMagic: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+584 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+584] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 73 & 0xffff +#if 73 > 0xffff + movk ip0, 73 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetMap + .p2align 4 + .type drmGetMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetMap +#endif +drmGetMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+592 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+592] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 74 & 0xffff +#if 74 > 0xffff + movk ip0, 74 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetNodeTypeFromDevId + .p2align 4 + .type drmGetNodeTypeFromDevId, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetNodeTypeFromDevId +#endif +drmGetNodeTypeFromDevId: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+600 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+600] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 75 & 0xffff +#if 75 > 0xffff + movk ip0, 75 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetNodeTypeFromFd + .p2align 4 + .type drmGetNodeTypeFromFd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetNodeTypeFromFd +#endif +drmGetNodeTypeFromFd: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+608 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+608] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 76 & 0xffff +#if 76 > 0xffff + movk ip0, 76 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetPrimaryDeviceNameFromFd + .p2align 4 + .type drmGetPrimaryDeviceNameFromFd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetPrimaryDeviceNameFromFd +#endif +drmGetPrimaryDeviceNameFromFd: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+616 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+616] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 77 & 0xffff +#if 77 > 0xffff + movk ip0, 77 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetRenderDeviceNameFromFd + .p2align 4 + .type drmGetRenderDeviceNameFromFd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetRenderDeviceNameFromFd +#endif +drmGetRenderDeviceNameFromFd: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+624 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+624] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 78 & 0xffff +#if 78 > 0xffff + movk ip0, 78 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetReservedContextList + .p2align 4 + .type drmGetReservedContextList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetReservedContextList +#endif +drmGetReservedContextList: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+632 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+632] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 79 & 0xffff +#if 79 > 0xffff + movk ip0, 79 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetStats + .p2align 4 + .type drmGetStats, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetStats +#endif +drmGetStats: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+640 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+640] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 80 & 0xffff +#if 80 > 0xffff + movk ip0, 80 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetVersion + .p2align 4 + .type drmGetVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetVersion +#endif +drmGetVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+648 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+648] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 81 & 0xffff +#if 81 > 0xffff + movk ip0, 81 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmHandleEvent + .p2align 4 + .type drmHandleEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmHandleEvent +#endif +drmHandleEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+656 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+656] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 82 & 0xffff +#if 82 > 0xffff + movk ip0, 82 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmHashCreate + .p2align 4 + .type drmHashCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmHashCreate +#endif +drmHashCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+664 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+664] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 83 & 0xffff +#if 83 > 0xffff + movk ip0, 83 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmHashDelete + .p2align 4 + .type drmHashDelete, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmHashDelete +#endif +drmHashDelete: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+672 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+672] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 84 & 0xffff +#if 84 > 0xffff + movk ip0, 84 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmHashDestroy + .p2align 4 + .type drmHashDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmHashDestroy +#endif +drmHashDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+680 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+680] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 85 & 0xffff +#if 85 > 0xffff + movk ip0, 85 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmHashFirst + .p2align 4 + .type drmHashFirst, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmHashFirst +#endif +drmHashFirst: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+688 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+688] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 86 & 0xffff +#if 86 > 0xffff + movk ip0, 86 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmHashInsert + .p2align 4 + .type drmHashInsert, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmHashInsert +#endif +drmHashInsert: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+696 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+696] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 87 & 0xffff +#if 87 > 0xffff + movk ip0, 87 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmHashLookup + .p2align 4 + .type drmHashLookup, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmHashLookup +#endif +drmHashLookup: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+704 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+704] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 88 & 0xffff +#if 88 > 0xffff + movk ip0, 88 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmHashNext + .p2align 4 + .type drmHashNext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmHashNext +#endif +drmHashNext: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+712 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+712] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 89 & 0xffff +#if 89 > 0xffff + movk ip0, 89 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmIoctl + .p2align 4 + .type drmIoctl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmIoctl +#endif +drmIoctl: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+720 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+720] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 90 & 0xffff +#if 90 > 0xffff + movk ip0, 90 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmIsKMS + .p2align 4 + .type drmIsKMS, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmIsKMS +#endif +drmIsKMS: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+728 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+728] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 91 & 0xffff +#if 91 > 0xffff + movk ip0, 91 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmIsMaster + .p2align 4 + .type drmIsMaster, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmIsMaster +#endif +drmIsMaster: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+736 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+736] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 92 & 0xffff +#if 92 > 0xffff + movk ip0, 92 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmMalloc + .p2align 4 + .type drmMalloc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmMalloc +#endif +drmMalloc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+744 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+744] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 93 & 0xffff +#if 93 > 0xffff + movk ip0, 93 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmMap + .p2align 4 + .type drmMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmMap +#endif +drmMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+752 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+752] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 94 & 0xffff +#if 94 > 0xffff + movk ip0, 94 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmMapBufs + .p2align 4 + .type drmMapBufs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmMapBufs +#endif +drmMapBufs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+760 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+760] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 95 & 0xffff +#if 95 > 0xffff + movk ip0, 95 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmMarkBufs + .p2align 4 + .type drmMarkBufs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmMarkBufs +#endif +drmMarkBufs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+768 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+768] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 96 & 0xffff +#if 96 > 0xffff + movk ip0, 96 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAddFB + .p2align 4 + .type drmModeAddFB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAddFB +#endif +drmModeAddFB: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+776 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+776] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 97 & 0xffff +#if 97 > 0xffff + movk ip0, 97 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAddFB2 + .p2align 4 + .type drmModeAddFB2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAddFB2 +#endif +drmModeAddFB2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+784 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+784] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 98 & 0xffff +#if 98 > 0xffff + movk ip0, 98 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAddFB2WithModifiers + .p2align 4 + .type drmModeAddFB2WithModifiers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAddFB2WithModifiers +#endif +drmModeAddFB2WithModifiers: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+792 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+792] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 99 & 0xffff +#if 99 > 0xffff + movk ip0, 99 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAtomicAddProperty + .p2align 4 + .type drmModeAtomicAddProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAtomicAddProperty +#endif +drmModeAtomicAddProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+800 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+800] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 100 & 0xffff +#if 100 > 0xffff + movk ip0, 100 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAtomicAlloc + .p2align 4 + .type drmModeAtomicAlloc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAtomicAlloc +#endif +drmModeAtomicAlloc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+808 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+808] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 101 & 0xffff +#if 101 > 0xffff + movk ip0, 101 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAtomicCommit + .p2align 4 + .type drmModeAtomicCommit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAtomicCommit +#endif +drmModeAtomicCommit: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+816 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+816] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 102 & 0xffff +#if 102 > 0xffff + movk ip0, 102 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAtomicDuplicate + .p2align 4 + .type drmModeAtomicDuplicate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAtomicDuplicate +#endif +drmModeAtomicDuplicate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+824 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+824] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 103 & 0xffff +#if 103 > 0xffff + movk ip0, 103 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAtomicFree + .p2align 4 + .type drmModeAtomicFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAtomicFree +#endif +drmModeAtomicFree: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+832 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+832] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 104 & 0xffff +#if 104 > 0xffff + movk ip0, 104 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAtomicGetCursor + .p2align 4 + .type drmModeAtomicGetCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAtomicGetCursor +#endif +drmModeAtomicGetCursor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+840 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+840] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 105 & 0xffff +#if 105 > 0xffff + movk ip0, 105 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAtomicMerge + .p2align 4 + .type drmModeAtomicMerge, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAtomicMerge +#endif +drmModeAtomicMerge: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+848 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+848] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 106 & 0xffff +#if 106 > 0xffff + movk ip0, 106 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAtomicSetCursor + .p2align 4 + .type drmModeAtomicSetCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAtomicSetCursor +#endif +drmModeAtomicSetCursor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+856 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+856] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 107 & 0xffff +#if 107 > 0xffff + movk ip0, 107 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAttachMode + .p2align 4 + .type drmModeAttachMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAttachMode +#endif +drmModeAttachMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+864 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+864] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 108 & 0xffff +#if 108 > 0xffff + movk ip0, 108 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeCloseFB + .p2align 4 + .type drmModeCloseFB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeCloseFB +#endif +drmModeCloseFB: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+872 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+872] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 109 & 0xffff +#if 109 > 0xffff + movk ip0, 109 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeConnectorGetPossibleCrtcs + .p2align 4 + .type drmModeConnectorGetPossibleCrtcs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeConnectorGetPossibleCrtcs +#endif +drmModeConnectorGetPossibleCrtcs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+880 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+880] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 110 & 0xffff +#if 110 > 0xffff + movk ip0, 110 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeConnectorSetProperty + .p2align 4 + .type drmModeConnectorSetProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeConnectorSetProperty +#endif +drmModeConnectorSetProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+888 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+888] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 111 & 0xffff +#if 111 > 0xffff + movk ip0, 111 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeCreateDumbBuffer + .p2align 4 + .type drmModeCreateDumbBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeCreateDumbBuffer +#endif +drmModeCreateDumbBuffer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+896 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+896] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 112 & 0xffff +#if 112 > 0xffff + movk ip0, 112 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeCreateLease + .p2align 4 + .type drmModeCreateLease, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeCreateLease +#endif +drmModeCreateLease: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+904 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+904] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 113 & 0xffff +#if 113 > 0xffff + movk ip0, 113 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeCreatePropertyBlob + .p2align 4 + .type drmModeCreatePropertyBlob, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeCreatePropertyBlob +#endif +drmModeCreatePropertyBlob: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+912 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+912] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 114 & 0xffff +#if 114 > 0xffff + movk ip0, 114 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeCrtcGetGamma + .p2align 4 + .type drmModeCrtcGetGamma, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeCrtcGetGamma +#endif +drmModeCrtcGetGamma: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+920 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+920] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 115 & 0xffff +#if 115 > 0xffff + movk ip0, 115 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeCrtcSetGamma + .p2align 4 + .type drmModeCrtcSetGamma, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeCrtcSetGamma +#endif +drmModeCrtcSetGamma: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+928 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+928] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 116 & 0xffff +#if 116 > 0xffff + movk ip0, 116 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeDestroyDumbBuffer + .p2align 4 + .type drmModeDestroyDumbBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeDestroyDumbBuffer +#endif +drmModeDestroyDumbBuffer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+936 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+936] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 117 & 0xffff +#if 117 > 0xffff + movk ip0, 117 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeDestroyPropertyBlob + .p2align 4 + .type drmModeDestroyPropertyBlob, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeDestroyPropertyBlob +#endif +drmModeDestroyPropertyBlob: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+944 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+944] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 118 & 0xffff +#if 118 > 0xffff + movk ip0, 118 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeDetachMode + .p2align 4 + .type drmModeDetachMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeDetachMode +#endif +drmModeDetachMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+952 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+952] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 119 & 0xffff +#if 119 > 0xffff + movk ip0, 119 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeDirtyFB + .p2align 4 + .type drmModeDirtyFB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeDirtyFB +#endif +drmModeDirtyFB: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+960 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+960] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 120 & 0xffff +#if 120 > 0xffff + movk ip0, 120 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFormatModifierBlobIterNext + .p2align 4 + .type drmModeFormatModifierBlobIterNext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFormatModifierBlobIterNext +#endif +drmModeFormatModifierBlobIterNext: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+968 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+968] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 121 & 0xffff +#if 121 > 0xffff + movk ip0, 121 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreeConnector + .p2align 4 + .type drmModeFreeConnector, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreeConnector +#endif +drmModeFreeConnector: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+976 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+976] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 122 & 0xffff +#if 122 > 0xffff + movk ip0, 122 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreeCrtc + .p2align 4 + .type drmModeFreeCrtc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreeCrtc +#endif +drmModeFreeCrtc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+984 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+984] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 123 & 0xffff +#if 123 > 0xffff + movk ip0, 123 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreeEncoder + .p2align 4 + .type drmModeFreeEncoder, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreeEncoder +#endif +drmModeFreeEncoder: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+992 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+992] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 124 & 0xffff +#if 124 > 0xffff + movk ip0, 124 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreeFB + .p2align 4 + .type drmModeFreeFB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreeFB +#endif +drmModeFreeFB: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1000 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1000] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 125 & 0xffff +#if 125 > 0xffff + movk ip0, 125 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreeFB2 + .p2align 4 + .type drmModeFreeFB2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreeFB2 +#endif +drmModeFreeFB2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1008 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1008] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 126 & 0xffff +#if 126 > 0xffff + movk ip0, 126 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreeModeInfo + .p2align 4 + .type drmModeFreeModeInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreeModeInfo +#endif +drmModeFreeModeInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1016 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1016] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 127 & 0xffff +#if 127 > 0xffff + movk ip0, 127 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreeObjectProperties + .p2align 4 + .type drmModeFreeObjectProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreeObjectProperties +#endif +drmModeFreeObjectProperties: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1024 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1024] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 128 & 0xffff +#if 128 > 0xffff + movk ip0, 128 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreePlane + .p2align 4 + .type drmModeFreePlane, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreePlane +#endif +drmModeFreePlane: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1032 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1032] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 129 & 0xffff +#if 129 > 0xffff + movk ip0, 129 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreePlaneResources + .p2align 4 + .type drmModeFreePlaneResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreePlaneResources +#endif +drmModeFreePlaneResources: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1040 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1040] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 130 & 0xffff +#if 130 > 0xffff + movk ip0, 130 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreeProperty + .p2align 4 + .type drmModeFreeProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreeProperty +#endif +drmModeFreeProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1048 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1048] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 131 & 0xffff +#if 131 > 0xffff + movk ip0, 131 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreePropertyBlob + .p2align 4 + .type drmModeFreePropertyBlob, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreePropertyBlob +#endif +drmModeFreePropertyBlob: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1056 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1056] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 132 & 0xffff +#if 132 > 0xffff + movk ip0, 132 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreeResources + .p2align 4 + .type drmModeFreeResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreeResources +#endif +drmModeFreeResources: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1064 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1064] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 133 & 0xffff +#if 133 > 0xffff + movk ip0, 133 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetConnector + .p2align 4 + .type drmModeGetConnector, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetConnector +#endif +drmModeGetConnector: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1072 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1072] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 134 & 0xffff +#if 134 > 0xffff + movk ip0, 134 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetConnectorCurrent + .p2align 4 + .type drmModeGetConnectorCurrent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetConnectorCurrent +#endif +drmModeGetConnectorCurrent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1080 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1080] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 135 & 0xffff +#if 135 > 0xffff + movk ip0, 135 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetConnectorTypeName + .p2align 4 + .type drmModeGetConnectorTypeName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetConnectorTypeName +#endif +drmModeGetConnectorTypeName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1088 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1088] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 136 & 0xffff +#if 136 > 0xffff + movk ip0, 136 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetCrtc + .p2align 4 + .type drmModeGetCrtc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetCrtc +#endif +drmModeGetCrtc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1096 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1096] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 137 & 0xffff +#if 137 > 0xffff + movk ip0, 137 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetEncoder + .p2align 4 + .type drmModeGetEncoder, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetEncoder +#endif +drmModeGetEncoder: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1104 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 138 & 0xffff +#if 138 > 0xffff + movk ip0, 138 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetFB + .p2align 4 + .type drmModeGetFB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetFB +#endif +drmModeGetFB: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1112 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 139 & 0xffff +#if 139 > 0xffff + movk ip0, 139 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetFB2 + .p2align 4 + .type drmModeGetFB2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetFB2 +#endif +drmModeGetFB2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1120 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 140 & 0xffff +#if 140 > 0xffff + movk ip0, 140 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetLease + .p2align 4 + .type drmModeGetLease, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetLease +#endif +drmModeGetLease: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1128 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 141 & 0xffff +#if 141 > 0xffff + movk ip0, 141 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetPlane + .p2align 4 + .type drmModeGetPlane, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetPlane +#endif +drmModeGetPlane: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1136 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 142 & 0xffff +#if 142 > 0xffff + movk ip0, 142 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetPlaneResources + .p2align 4 + .type drmModeGetPlaneResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetPlaneResources +#endif +drmModeGetPlaneResources: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1144 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 143 & 0xffff +#if 143 > 0xffff + movk ip0, 143 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetProperty + .p2align 4 + .type drmModeGetProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetProperty +#endif +drmModeGetProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1152 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 144 & 0xffff +#if 144 > 0xffff + movk ip0, 144 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetPropertyBlob + .p2align 4 + .type drmModeGetPropertyBlob, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetPropertyBlob +#endif +drmModeGetPropertyBlob: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1160 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 145 & 0xffff +#if 145 > 0xffff + movk ip0, 145 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetResources + .p2align 4 + .type drmModeGetResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetResources +#endif +drmModeGetResources: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1168 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 146 & 0xffff +#if 146 > 0xffff + movk ip0, 146 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeListLessees + .p2align 4 + .type drmModeListLessees, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeListLessees +#endif +drmModeListLessees: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1176 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 147 & 0xffff +#if 147 > 0xffff + movk ip0, 147 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeMapDumbBuffer + .p2align 4 + .type drmModeMapDumbBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeMapDumbBuffer +#endif +drmModeMapDumbBuffer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1184 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 148 & 0xffff +#if 148 > 0xffff + movk ip0, 148 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeMoveCursor + .p2align 4 + .type drmModeMoveCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeMoveCursor +#endif +drmModeMoveCursor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1192 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 149 & 0xffff +#if 149 > 0xffff + movk ip0, 149 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeObjectGetProperties + .p2align 4 + .type drmModeObjectGetProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeObjectGetProperties +#endif +drmModeObjectGetProperties: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1200 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 150 & 0xffff +#if 150 > 0xffff + movk ip0, 150 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeObjectSetProperty + .p2align 4 + .type drmModeObjectSetProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeObjectSetProperty +#endif +drmModeObjectSetProperty: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1208 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 151 & 0xffff +#if 151 > 0xffff + movk ip0, 151 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModePageFlip + .p2align 4 + .type drmModePageFlip, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModePageFlip +#endif +drmModePageFlip: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1216 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 152 & 0xffff +#if 152 > 0xffff + movk ip0, 152 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModePageFlipTarget + .p2align 4 + .type drmModePageFlipTarget, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModePageFlipTarget +#endif +drmModePageFlipTarget: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1224 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 153 & 0xffff +#if 153 > 0xffff + movk ip0, 153 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeRevokeLease + .p2align 4 + .type drmModeRevokeLease, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeRevokeLease +#endif +drmModeRevokeLease: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1232 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 154 & 0xffff +#if 154 > 0xffff + movk ip0, 154 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeRmFB + .p2align 4 + .type drmModeRmFB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeRmFB +#endif +drmModeRmFB: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1240 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 155 & 0xffff +#if 155 > 0xffff + movk ip0, 155 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeSetCrtc + .p2align 4 + .type drmModeSetCrtc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeSetCrtc +#endif +drmModeSetCrtc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1248 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 156 & 0xffff +#if 156 > 0xffff + movk ip0, 156 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeSetCursor + .p2align 4 + .type drmModeSetCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeSetCursor +#endif +drmModeSetCursor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1256 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 157 & 0xffff +#if 157 > 0xffff + movk ip0, 157 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeSetCursor2 + .p2align 4 + .type drmModeSetCursor2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeSetCursor2 +#endif +drmModeSetCursor2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1264 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 158 & 0xffff +#if 158 > 0xffff + movk ip0, 158 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeSetPlane + .p2align 4 + .type drmModeSetPlane, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeSetPlane +#endif +drmModeSetPlane: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1272 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 159 & 0xffff +#if 159 > 0xffff + movk ip0, 159 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmMsg + .p2align 4 + .type drmMsg, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmMsg +#endif +drmMsg: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1280 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 160 & 0xffff +#if 160 > 0xffff + movk ip0, 160 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmOpen + .p2align 4 + .type drmOpen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmOpen +#endif +drmOpen: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1288 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 161 & 0xffff +#if 161 > 0xffff + movk ip0, 161 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmOpenControl + .p2align 4 + .type drmOpenControl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmOpenControl +#endif +drmOpenControl: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1296 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 162 & 0xffff +#if 162 > 0xffff + movk ip0, 162 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmOpenOnce + .p2align 4 + .type drmOpenOnce, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmOpenOnce +#endif +drmOpenOnce: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1304 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 163 & 0xffff +#if 163 > 0xffff + movk ip0, 163 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmOpenOnceWithType + .p2align 4 + .type drmOpenOnceWithType, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmOpenOnceWithType +#endif +drmOpenOnceWithType: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1312 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 164 & 0xffff +#if 164 > 0xffff + movk ip0, 164 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmOpenRender + .p2align 4 + .type drmOpenRender, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmOpenRender +#endif +drmOpenRender: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1320 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 165 & 0xffff +#if 165 > 0xffff + movk ip0, 165 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmOpenWithType + .p2align 4 + .type drmOpenWithType, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmOpenWithType +#endif +drmOpenWithType: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1328 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 166 & 0xffff +#if 166 > 0xffff + movk ip0, 166 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmPrimeFDToHandle + .p2align 4 + .type drmPrimeFDToHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmPrimeFDToHandle +#endif +drmPrimeFDToHandle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1336 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 167 & 0xffff +#if 167 > 0xffff + movk ip0, 167 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmPrimeHandleToFD + .p2align 4 + .type drmPrimeHandleToFD, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmPrimeHandleToFD +#endif +drmPrimeHandleToFD: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1344 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 168 & 0xffff +#if 168 > 0xffff + movk ip0, 168 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmRandom + .p2align 4 + .type drmRandom, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmRandom +#endif +drmRandom: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1352 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 169 & 0xffff +#if 169 > 0xffff + movk ip0, 169 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmRandomCreate + .p2align 4 + .type drmRandomCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmRandomCreate +#endif +drmRandomCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1360 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 170 & 0xffff +#if 170 > 0xffff + movk ip0, 170 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmRandomDestroy + .p2align 4 + .type drmRandomDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmRandomDestroy +#endif +drmRandomDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1368 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 171 & 0xffff +#if 171 > 0xffff + movk ip0, 171 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmRandomDouble + .p2align 4 + .type drmRandomDouble, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmRandomDouble +#endif +drmRandomDouble: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1376 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 172 & 0xffff +#if 172 > 0xffff + movk ip0, 172 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmRmMap + .p2align 4 + .type drmRmMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmRmMap +#endif +drmRmMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1384 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 173 & 0xffff +#if 173 > 0xffff + movk ip0, 173 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSLCreate + .p2align 4 + .type drmSLCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSLCreate +#endif +drmSLCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1392 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 174 & 0xffff +#if 174 > 0xffff + movk ip0, 174 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSLDelete + .p2align 4 + .type drmSLDelete, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSLDelete +#endif +drmSLDelete: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1400 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 175 & 0xffff +#if 175 > 0xffff + movk ip0, 175 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSLDestroy + .p2align 4 + .type drmSLDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSLDestroy +#endif +drmSLDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1408 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 176 & 0xffff +#if 176 > 0xffff + movk ip0, 176 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSLDump + .p2align 4 + .type drmSLDump, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSLDump +#endif +drmSLDump: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1416 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 177 & 0xffff +#if 177 > 0xffff + movk ip0, 177 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSLFirst + .p2align 4 + .type drmSLFirst, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSLFirst +#endif +drmSLFirst: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1424 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 178 & 0xffff +#if 178 > 0xffff + movk ip0, 178 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSLInsert + .p2align 4 + .type drmSLInsert, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSLInsert +#endif +drmSLInsert: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1432 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 179 & 0xffff +#if 179 > 0xffff + movk ip0, 179 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSLLookup + .p2align 4 + .type drmSLLookup, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSLLookup +#endif +drmSLLookup: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1440 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 180 & 0xffff +#if 180 > 0xffff + movk ip0, 180 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSLLookupNeighbors + .p2align 4 + .type drmSLLookupNeighbors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSLLookupNeighbors +#endif +drmSLLookupNeighbors: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1448 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 181 & 0xffff +#if 181 > 0xffff + movk ip0, 181 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSLNext + .p2align 4 + .type drmSLNext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSLNext +#endif +drmSLNext: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1456 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1456] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 182 & 0xffff +#if 182 > 0xffff + movk ip0, 182 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmScatterGatherAlloc + .p2align 4 + .type drmScatterGatherAlloc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmScatterGatherAlloc +#endif +drmScatterGatherAlloc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1464 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1464] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 183 & 0xffff +#if 183 > 0xffff + movk ip0, 183 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmScatterGatherFree + .p2align 4 + .type drmScatterGatherFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmScatterGatherFree +#endif +drmScatterGatherFree: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1472 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1472] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 184 & 0xffff +#if 184 > 0xffff + movk ip0, 184 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSetBusid + .p2align 4 + .type drmSetBusid, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSetBusid +#endif +drmSetBusid: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1480 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1480] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 185 & 0xffff +#if 185 > 0xffff + movk ip0, 185 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSetClientCap + .p2align 4 + .type drmSetClientCap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSetClientCap +#endif +drmSetClientCap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1488 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1488] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 186 & 0xffff +#if 186 > 0xffff + movk ip0, 186 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSetContextFlags + .p2align 4 + .type drmSetContextFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSetContextFlags +#endif +drmSetContextFlags: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1496 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1496] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 187 & 0xffff +#if 187 > 0xffff + movk ip0, 187 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSetInterfaceVersion + .p2align 4 + .type drmSetInterfaceVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSetInterfaceVersion +#endif +drmSetInterfaceVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1504 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1504] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 188 & 0xffff +#if 188 > 0xffff + movk ip0, 188 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSetMaster + .p2align 4 + .type drmSetMaster, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSetMaster +#endif +drmSetMaster: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1512 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1512] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 189 & 0xffff +#if 189 > 0xffff + movk ip0, 189 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSetServerInfo + .p2align 4 + .type drmSetServerInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSetServerInfo +#endif +drmSetServerInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1520 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1520] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 190 & 0xffff +#if 190 > 0xffff + movk ip0, 190 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSwitchToContext + .p2align 4 + .type drmSwitchToContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSwitchToContext +#endif +drmSwitchToContext: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1528 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1528] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 191 & 0xffff +#if 191 > 0xffff + movk ip0, 191 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjCreate + .p2align 4 + .type drmSyncobjCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjCreate +#endif +drmSyncobjCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1536 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1536] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 192 & 0xffff +#if 192 > 0xffff + movk ip0, 192 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjDestroy + .p2align 4 + .type drmSyncobjDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjDestroy +#endif +drmSyncobjDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1544 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1544] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 193 & 0xffff +#if 193 > 0xffff + movk ip0, 193 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjEventfd + .p2align 4 + .type drmSyncobjEventfd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjEventfd +#endif +drmSyncobjEventfd: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1552 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1552] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 194 & 0xffff +#if 194 > 0xffff + movk ip0, 194 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjExportSyncFile + .p2align 4 + .type drmSyncobjExportSyncFile, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjExportSyncFile +#endif +drmSyncobjExportSyncFile: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1560 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1560] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 195 & 0xffff +#if 195 > 0xffff + movk ip0, 195 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjFDToHandle + .p2align 4 + .type drmSyncobjFDToHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjFDToHandle +#endif +drmSyncobjFDToHandle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1568 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1568] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 196 & 0xffff +#if 196 > 0xffff + movk ip0, 196 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjHandleToFD + .p2align 4 + .type drmSyncobjHandleToFD, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjHandleToFD +#endif +drmSyncobjHandleToFD: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1576 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1576] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 197 & 0xffff +#if 197 > 0xffff + movk ip0, 197 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjImportSyncFile + .p2align 4 + .type drmSyncobjImportSyncFile, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjImportSyncFile +#endif +drmSyncobjImportSyncFile: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1584 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1584] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 198 & 0xffff +#if 198 > 0xffff + movk ip0, 198 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjQuery + .p2align 4 + .type drmSyncobjQuery, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjQuery +#endif +drmSyncobjQuery: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1592 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1592] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 199 & 0xffff +#if 199 > 0xffff + movk ip0, 199 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjQuery2 + .p2align 4 + .type drmSyncobjQuery2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjQuery2 +#endif +drmSyncobjQuery2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1600 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1600] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 200 & 0xffff +#if 200 > 0xffff + movk ip0, 200 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjReset + .p2align 4 + .type drmSyncobjReset, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjReset +#endif +drmSyncobjReset: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1608 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1608] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 201 & 0xffff +#if 201 > 0xffff + movk ip0, 201 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjSignal + .p2align 4 + .type drmSyncobjSignal, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjSignal +#endif +drmSyncobjSignal: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1616 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1616] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 202 & 0xffff +#if 202 > 0xffff + movk ip0, 202 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjTimelineSignal + .p2align 4 + .type drmSyncobjTimelineSignal, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjTimelineSignal +#endif +drmSyncobjTimelineSignal: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1624 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1624] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 203 & 0xffff +#if 203 > 0xffff + movk ip0, 203 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjTimelineWait + .p2align 4 + .type drmSyncobjTimelineWait, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjTimelineWait +#endif +drmSyncobjTimelineWait: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1632 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1632] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 204 & 0xffff +#if 204 > 0xffff + movk ip0, 204 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjTransfer + .p2align 4 + .type drmSyncobjTransfer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjTransfer +#endif +drmSyncobjTransfer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1640 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1640] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 205 & 0xffff +#if 205 > 0xffff + movk ip0, 205 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjWait + .p2align 4 + .type drmSyncobjWait, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjWait +#endif +drmSyncobjWait: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1648 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1648] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 206 & 0xffff +#if 206 > 0xffff + movk ip0, 206 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmUnlock + .p2align 4 + .type drmUnlock, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmUnlock +#endif +drmUnlock: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1656 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1656] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 207 & 0xffff +#if 207 > 0xffff + movk ip0, 207 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmUnmap + .p2align 4 + .type drmUnmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmUnmap +#endif +drmUnmap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1664 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1664] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 208 & 0xffff +#if 208 > 0xffff + movk ip0, 208 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmUnmapBufs + .p2align 4 + .type drmUnmapBufs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmUnmapBufs +#endif +drmUnmapBufs: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1672 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1672] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 209 & 0xffff +#if 209 > 0xffff + movk ip0, 209 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmUpdateDrawableInfo + .p2align 4 + .type drmUpdateDrawableInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmUpdateDrawableInfo +#endif +drmUpdateDrawableInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1680 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1680] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 210 & 0xffff +#if 210 > 0xffff + movk ip0, 210 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmWaitVBlank + .p2align 4 + .type drmWaitVBlank, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmWaitVBlank +#endif +drmWaitVBlank: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libdrm_so_tramp_table+1688 + ldr ip0, [ip0, #:lo12:_libdrm_so_tramp_table+1688] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 211 & 0xffff +#if 211 > 0xffff + movk ip0, 211 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libdrm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libgbm.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libgbm.so.init.c new file mode 100644 index 0000000..1dc5173 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libgbm.so.init.c @@ -0,0 +1,282 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libgbm.so.1: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libgbm.so.1"); + CHECK(lib_handle, "failed to load library 'libgbm.so.1' via callback ''"); +#else + lib_handle = dlopen("libgbm.so.1", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libgbm.so.1' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "gbm_bo_create", + "gbm_bo_create_with_modifiers", + "gbm_bo_create_with_modifiers2", + "gbm_bo_destroy", + "gbm_bo_get_bpp", + "gbm_bo_get_device", + "gbm_bo_get_fd", + "gbm_bo_get_fd_for_plane", + "gbm_bo_get_format", + "gbm_bo_get_handle", + "gbm_bo_get_handle_for_plane", + "gbm_bo_get_height", + "gbm_bo_get_modifier", + "gbm_bo_get_offset", + "gbm_bo_get_plane_count", + "gbm_bo_get_stride", + "gbm_bo_get_stride_for_plane", + "gbm_bo_get_user_data", + "gbm_bo_get_width", + "gbm_bo_import", + "gbm_bo_map", + "gbm_bo_set_user_data", + "gbm_bo_unmap", + "gbm_bo_write", + "gbm_create_device", + "gbm_device_destroy", + "gbm_device_get_backend_name", + "gbm_device_get_fd", + "gbm_device_get_format_modifier_plane_count", + "gbm_device_is_format_supported", + "gbm_format_get_name", + "gbm_surface_create", + "gbm_surface_create_with_modifiers", + "gbm_surface_create_with_modifiers2", + "gbm_surface_destroy", + "gbm_surface_has_free_buffers", + "gbm_surface_lock_front_buffer", + "gbm_surface_release_buffer", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libgbm_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libgbm_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libgbm_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libgbm_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libgbm_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libgbm_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libgbm_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libgbm_so_tramp_table, 0, SYM_COUNT * sizeof(_libgbm_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libgbm.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libgbm.so.tramp.S new file mode 100644 index 0000000..d48f5ed --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/aarch64-linux-gnu/libgbm.so.tramp.S @@ -0,0 +1,1639 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#define lr x30 +#define ip0 x16 + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libgbm_so_tramp_table + .hidden _libgbm_so_tramp_table + .align 8 +_libgbm_so_tramp_table: + .zero 312 + + .text + + .globl _libgbm_so_tramp_resolve + .hidden _libgbm_so_tramp_resolve + + .globl _libgbm_so_save_regs_and_resolve + .hidden _libgbm_so_save_regs_and_resolve + .type _libgbm_so_save_regs_and_resolve, %function +_libgbm_so_save_regs_and_resolve: + .cfi_startproc + + // Slow path which calls dlsym, taken only on first call. + // Registers are saved according to "Procedure Call Standard for the Arm® 64-bit Architecture". + // For DWARF directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + // Stack is aligned at 16 bytes + +#define PUSH_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 8 +#define POP_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore reg2; .cfi_restore reg1 + +#define PUSH_WIDE_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-32]!; .cfi_adjust_cfa_offset 32; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 16 +#define POP_WIDE_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #32; .cfi_adjust_cfa_offset -32; .cfi_restore reg2; .cfi_restore reg1 + + // Save only arguments (and lr) + PUSH_PAIR(x0, x1) + PUSH_PAIR(x2, x3) + PUSH_PAIR(x4, x5) + PUSH_PAIR(x6, x7) + PUSH_PAIR(x8, lr) + + ldr x0, [sp, #80] // 16*5 + + PUSH_WIDE_PAIR(q0, q1) + PUSH_WIDE_PAIR(q2, q3) + PUSH_WIDE_PAIR(q4, q5) + PUSH_WIDE_PAIR(q6, q7) + + // Stack is aligned at 16 bytes + + bl _libgbm_so_tramp_resolve + mov ip0, x0 + + // TODO: pop pc? + + POP_WIDE_PAIR(q6, q7) + POP_WIDE_PAIR(q4, q5) + POP_WIDE_PAIR(q2, q3) + POP_WIDE_PAIR(q0, q1) + + POP_PAIR(x8, lr) + POP_PAIR(x6, x7) + POP_PAIR(x4, x5) + POP_PAIR(x2, x3) + POP_PAIR(x0, x1) + + br lr + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_create + .p2align 4 + .type gbm_bo_create, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_create +#endif +gbm_bo_create: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+0 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+0] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 0 & 0xffff +#if 0 > 0xffff + movk ip0, 0 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_create_with_modifiers + .p2align 4 + .type gbm_bo_create_with_modifiers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_create_with_modifiers +#endif +gbm_bo_create_with_modifiers: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+8 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+8] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1 & 0xffff +#if 1 > 0xffff + movk ip0, 1 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_create_with_modifiers2 + .p2align 4 + .type gbm_bo_create_with_modifiers2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_create_with_modifiers2 +#endif +gbm_bo_create_with_modifiers2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+16 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+16] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 2 & 0xffff +#if 2 > 0xffff + movk ip0, 2 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_destroy + .p2align 4 + .type gbm_bo_destroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_destroy +#endif +gbm_bo_destroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+24 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+24] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 3 & 0xffff +#if 3 > 0xffff + movk ip0, 3 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_bpp + .p2align 4 + .type gbm_bo_get_bpp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_bpp +#endif +gbm_bo_get_bpp: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+32 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+32] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 4 & 0xffff +#if 4 > 0xffff + movk ip0, 4 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_device + .p2align 4 + .type gbm_bo_get_device, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_device +#endif +gbm_bo_get_device: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+40 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+40] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 5 & 0xffff +#if 5 > 0xffff + movk ip0, 5 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_fd + .p2align 4 + .type gbm_bo_get_fd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_fd +#endif +gbm_bo_get_fd: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+48 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+48] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 6 & 0xffff +#if 6 > 0xffff + movk ip0, 6 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_fd_for_plane + .p2align 4 + .type gbm_bo_get_fd_for_plane, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_fd_for_plane +#endif +gbm_bo_get_fd_for_plane: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+56 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+56] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 7 & 0xffff +#if 7 > 0xffff + movk ip0, 7 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_format + .p2align 4 + .type gbm_bo_get_format, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_format +#endif +gbm_bo_get_format: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+64 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+64] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 8 & 0xffff +#if 8 > 0xffff + movk ip0, 8 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_handle + .p2align 4 + .type gbm_bo_get_handle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_handle +#endif +gbm_bo_get_handle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+72 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+72] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 9 & 0xffff +#if 9 > 0xffff + movk ip0, 9 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_handle_for_plane + .p2align 4 + .type gbm_bo_get_handle_for_plane, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_handle_for_plane +#endif +gbm_bo_get_handle_for_plane: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+80 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+80] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 10 & 0xffff +#if 10 > 0xffff + movk ip0, 10 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_height + .p2align 4 + .type gbm_bo_get_height, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_height +#endif +gbm_bo_get_height: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+88 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+88] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 11 & 0xffff +#if 11 > 0xffff + movk ip0, 11 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_modifier + .p2align 4 + .type gbm_bo_get_modifier, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_modifier +#endif +gbm_bo_get_modifier: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+96 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+96] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 12 & 0xffff +#if 12 > 0xffff + movk ip0, 12 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_offset + .p2align 4 + .type gbm_bo_get_offset, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_offset +#endif +gbm_bo_get_offset: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+104 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 13 & 0xffff +#if 13 > 0xffff + movk ip0, 13 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_plane_count + .p2align 4 + .type gbm_bo_get_plane_count, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_plane_count +#endif +gbm_bo_get_plane_count: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+112 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 14 & 0xffff +#if 14 > 0xffff + movk ip0, 14 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_stride + .p2align 4 + .type gbm_bo_get_stride, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_stride +#endif +gbm_bo_get_stride: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+120 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 15 & 0xffff +#if 15 > 0xffff + movk ip0, 15 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_stride_for_plane + .p2align 4 + .type gbm_bo_get_stride_for_plane, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_stride_for_plane +#endif +gbm_bo_get_stride_for_plane: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+128 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 16 & 0xffff +#if 16 > 0xffff + movk ip0, 16 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_user_data + .p2align 4 + .type gbm_bo_get_user_data, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_user_data +#endif +gbm_bo_get_user_data: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+136 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 17 & 0xffff +#if 17 > 0xffff + movk ip0, 17 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_width + .p2align 4 + .type gbm_bo_get_width, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_width +#endif +gbm_bo_get_width: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+144 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 18 & 0xffff +#if 18 > 0xffff + movk ip0, 18 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_import + .p2align 4 + .type gbm_bo_import, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_import +#endif +gbm_bo_import: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+152 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 19 & 0xffff +#if 19 > 0xffff + movk ip0, 19 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_map + .p2align 4 + .type gbm_bo_map, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_map +#endif +gbm_bo_map: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+160 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 20 & 0xffff +#if 20 > 0xffff + movk ip0, 20 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_set_user_data + .p2align 4 + .type gbm_bo_set_user_data, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_set_user_data +#endif +gbm_bo_set_user_data: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+168 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 21 & 0xffff +#if 21 > 0xffff + movk ip0, 21 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_unmap + .p2align 4 + .type gbm_bo_unmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_unmap +#endif +gbm_bo_unmap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+176 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 22 & 0xffff +#if 22 > 0xffff + movk ip0, 22 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_write + .p2align 4 + .type gbm_bo_write, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_write +#endif +gbm_bo_write: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+184 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 23 & 0xffff +#if 23 > 0xffff + movk ip0, 23 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_create_device + .p2align 4 + .type gbm_create_device, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_create_device +#endif +gbm_create_device: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+192 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 24 & 0xffff +#if 24 > 0xffff + movk ip0, 24 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_device_destroy + .p2align 4 + .type gbm_device_destroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_device_destroy +#endif +gbm_device_destroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+200 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 25 & 0xffff +#if 25 > 0xffff + movk ip0, 25 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_device_get_backend_name + .p2align 4 + .type gbm_device_get_backend_name, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_device_get_backend_name +#endif +gbm_device_get_backend_name: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+208 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 26 & 0xffff +#if 26 > 0xffff + movk ip0, 26 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_device_get_fd + .p2align 4 + .type gbm_device_get_fd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_device_get_fd +#endif +gbm_device_get_fd: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+216 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 27 & 0xffff +#if 27 > 0xffff + movk ip0, 27 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_device_get_format_modifier_plane_count + .p2align 4 + .type gbm_device_get_format_modifier_plane_count, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_device_get_format_modifier_plane_count +#endif +gbm_device_get_format_modifier_plane_count: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+224 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 28 & 0xffff +#if 28 > 0xffff + movk ip0, 28 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_device_is_format_supported + .p2align 4 + .type gbm_device_is_format_supported, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_device_is_format_supported +#endif +gbm_device_is_format_supported: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+232 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 29 & 0xffff +#if 29 > 0xffff + movk ip0, 29 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_format_get_name + .p2align 4 + .type gbm_format_get_name, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_format_get_name +#endif +gbm_format_get_name: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+240 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 30 & 0xffff +#if 30 > 0xffff + movk ip0, 30 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_surface_create + .p2align 4 + .type gbm_surface_create, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_surface_create +#endif +gbm_surface_create: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+248 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 31 & 0xffff +#if 31 > 0xffff + movk ip0, 31 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_surface_create_with_modifiers + .p2align 4 + .type gbm_surface_create_with_modifiers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_surface_create_with_modifiers +#endif +gbm_surface_create_with_modifiers: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+256 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 32 & 0xffff +#if 32 > 0xffff + movk ip0, 32 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_surface_create_with_modifiers2 + .p2align 4 + .type gbm_surface_create_with_modifiers2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_surface_create_with_modifiers2 +#endif +gbm_surface_create_with_modifiers2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+264 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 33 & 0xffff +#if 33 > 0xffff + movk ip0, 33 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_surface_destroy + .p2align 4 + .type gbm_surface_destroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_surface_destroy +#endif +gbm_surface_destroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+272 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 34 & 0xffff +#if 34 > 0xffff + movk ip0, 34 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_surface_has_free_buffers + .p2align 4 + .type gbm_surface_has_free_buffers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_surface_has_free_buffers +#endif +gbm_surface_has_free_buffers: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+280 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 35 & 0xffff +#if 35 > 0xffff + movk ip0, 35 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_surface_lock_front_buffer + .p2align 4 + .type gbm_surface_lock_front_buffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_surface_lock_front_buffer +#endif +gbm_surface_lock_front_buffer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+288 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 36 & 0xffff +#if 36 > 0xffff + movk ip0, 36 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_surface_release_buffer + .p2align 4 + .type gbm_surface_release_buffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_surface_release_buffer +#endif +gbm_surface_release_buffer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libgbm_so_tramp_table+296 + ldr ip0, [ip0, #:lo12:_libgbm_so_tramp_table+296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 37 & 0xffff +#if 37 > 0xffff + movk ip0, 37 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libgbm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libX11.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libX11.so.init.c new file mode 100644 index 0000000..3fb945f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libX11.so.init.c @@ -0,0 +1,1426 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libX11.so.6: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libX11.so.6"); + CHECK(lib_handle, "failed to load library 'libX11.so.6' via callback ''"); +#else + lib_handle = dlopen("libX11.so.6", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libX11.so.6' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "KeySymToUcs4", + "XActivateScreenSaver", + "XAddConnectionWatch", + "XAddExtension", + "XAddHost", + "XAddHosts", + "XAddPixel", + "XAddToExtensionList", + "XAddToSaveSet", + "XAllPlanes", + "XAllocClassHint", + "XAllocColor", + "XAllocColorCells", + "XAllocColorPlanes", + "XAllocIconSize", + "XAllocNamedColor", + "XAllocSizeHints", + "XAllocStandardColormap", + "XAllocWMHints", + "XAllowEvents", + "XAutoRepeatOff", + "XAutoRepeatOn", + "XBaseFontNameListOfFontSet", + "XBell", + "XBitmapBitOrder", + "XBitmapPad", + "XBitmapUnit", + "XBlackPixel", + "XBlackPixelOfScreen", + "XCellsOfScreen", + "XChangeActivePointerGrab", + "XChangeGC", + "XChangeKeyboardControl", + "XChangeKeyboardMapping", + "XChangePointerControl", + "XChangeProperty", + "XChangeSaveSet", + "XChangeWindowAttributes", + "XCheckIfEvent", + "XCheckMaskEvent", + "XCheckTypedEvent", + "XCheckTypedWindowEvent", + "XCheckWindowEvent", + "XCirculateSubwindows", + "XCirculateSubwindowsDown", + "XCirculateSubwindowsUp", + "XClearArea", + "XClearWindow", + "XClipBox", + "XCloseDisplay", + "XCloseIM", + "XCloseOM", + "XConfigureWindow", + "XConnectionNumber", + "XContextDependentDrawing", + "XContextualDrawing", + "XConvertCase", + "XConvertSelection", + "XCopyArea", + "XCopyColormapAndFree", + "XCopyGC", + "XCopyPlane", + "XCreateBitmapFromData", + "XCreateColormap", + "XCreateFontCursor", + "XCreateFontSet", + "XCreateGC", + "XCreateGlyphCursor", + "XCreateIC", + "XCreateImage", + "XCreateOC", + "XCreatePixmap", + "XCreatePixmapCursor", + "XCreatePixmapFromBitmapData", + "XCreateRegion", + "XCreateSimpleWindow", + "XCreateWindow", + "XDefaultColormap", + "XDefaultColormapOfScreen", + "XDefaultDepth", + "XDefaultDepthOfScreen", + "XDefaultGC", + "XDefaultGCOfScreen", + "XDefaultRootWindow", + "XDefaultScreen", + "XDefaultScreenOfDisplay", + "XDefaultString", + "XDefaultVisual", + "XDefaultVisualOfScreen", + "XDefineCursor", + "XDeleteContext", + "XDeleteModifiermapEntry", + "XDeleteProperty", + "XDestroyIC", + "XDestroyImage", + "XDestroyOC", + "XDestroyRegion", + "XDestroySubwindows", + "XDestroyWindow", + "XDirectionalDependentDrawing", + "XDisableAccessControl", + "XDisplayCells", + "XDisplayHeight", + "XDisplayHeightMM", + "XDisplayKeycodes", + "XDisplayMotionBufferSize", + "XDisplayName", + "XDisplayOfIM", + "XDisplayOfOM", + "XDisplayOfScreen", + "XDisplayPlanes", + "XDisplayString", + "XDisplayWidth", + "XDisplayWidthMM", + "XDoesBackingStore", + "XDoesSaveUnders", + "XDrawArc", + "XDrawArcs", + "XDrawImageString", + "XDrawImageString16", + "XDrawLine", + "XDrawLines", + "XDrawPoint", + "XDrawPoints", + "XDrawRectangle", + "XDrawRectangles", + "XDrawSegments", + "XDrawString", + "XDrawString16", + "XDrawText", + "XDrawText16", + "XEHeadOfExtensionList", + "XESetBeforeFlush", + "XESetCloseDisplay", + "XESetCopyEventCookie", + "XESetCopyGC", + "XESetCreateFont", + "XESetCreateGC", + "XESetError", + "XESetErrorString", + "XESetEventToWire", + "XESetFlushGC", + "XESetFreeFont", + "XESetFreeGC", + "XESetPrintErrorValues", + "XESetWireToError", + "XESetWireToEvent", + "XESetWireToEventCookie", + "XEmptyRegion", + "XEnableAccessControl", + "XEqualRegion", + "XEventMaskOfScreen", + "XEventsQueued", + "XExtendedMaxRequestSize", + "XExtentsOfFontSet", + "XFetchBuffer", + "XFetchBytes", + "XFetchName", + "XFillArc", + "XFillArcs", + "XFillPolygon", + "XFillRectangle", + "XFillRectangles", + "XFilterEvent", + "XFindContext", + "XFindOnExtensionList", + "XFlush", + "XFlushGC", + "XFontsOfFontSet", + "XForceScreenSaver", + "XFree", + "XFreeColormap", + "XFreeColors", + "XFreeCursor", + "XFreeEventData", + "XFreeExtensionList", + "XFreeFont", + "XFreeFontInfo", + "XFreeFontNames", + "XFreeFontPath", + "XFreeFontSet", + "XFreeGC", + "XFreeModifiermap", + "XFreePixmap", + "XFreeStringList", + "XFreeThreads", + "XGContextFromGC", + "XGeometry", + "XGetAtomName", + "XGetAtomNames", + "XGetClassHint", + "XGetCommand", + "XGetDefault", + "XGetErrorDatabaseText", + "XGetErrorText", + "XGetEventData", + "XGetFontPath", + "XGetFontProperty", + "XGetGCValues", + "XGetGeometry", + "XGetICValues", + "XGetIMValues", + "XGetIconName", + "XGetIconSizes", + "XGetImage", + "XGetInputFocus", + "XGetKeyboardControl", + "XGetKeyboardMapping", + "XGetModifierMapping", + "XGetMotionEvents", + "XGetNormalHints", + "XGetOCValues", + "XGetOMValues", + "XGetPixel", + "XGetPointerControl", + "XGetPointerMapping", + "XGetRGBColormaps", + "XGetScreenSaver", + "XGetSelectionOwner", + "XGetSizeHints", + "XGetStandardColormap", + "XGetSubImage", + "XGetTextProperty", + "XGetTransientForHint", + "XGetVisualInfo", + "XGetWMClientMachine", + "XGetWMColormapWindows", + "XGetWMHints", + "XGetWMIconName", + "XGetWMName", + "XGetWMNormalHints", + "XGetWMProtocols", + "XGetWMSizeHints", + "XGetWindowAttributes", + "XGetWindowProperty", + "XGetZoomHints", + "XGrabButton", + "XGrabKey", + "XGrabKeyboard", + "XGrabPointer", + "XGrabServer", + "XHeightMMOfScreen", + "XHeightOfScreen", + "XIMOfIC", + "XIconifyWindow", + "XIfEvent", + "XImageByteOrder", + "XInitExtension", + "XInitImage", + "XInitThreads", + "XInsertModifiermapEntry", + "XInstallColormap", + "XInternAtom", + "XInternAtoms", + "XInternalConnectionNumbers", + "XIntersectRegion", + "XKeycodeToKeysym", + "XKeysymToKeycode", + "XKeysymToString", + "XKillClient", + "XLastKnownRequestProcessed", + "XListDepths", + "XListExtensions", + "XListFonts", + "XListFontsWithInfo", + "XListHosts", + "XListInstalledColormaps", + "XListPixmapFormats", + "XListProperties", + "XLoadFont", + "XLoadQueryFont", + "XLocaleOfFontSet", + "XLocaleOfIM", + "XLocaleOfOM", + "XLockDisplay", + "XLookupColor", + "XLookupKeysym", + "XLookupString", + "XLowerWindow", + "XMapRaised", + "XMapSubwindows", + "XMapWindow", + "XMaskEvent", + "XMatchVisualInfo", + "XMaxCmapsOfScreen", + "XMaxRequestSize", + "XMinCmapsOfScreen", + "XMoveResizeWindow", + "XMoveWindow", + "XNewModifiermap", + "XNextEvent", + "XNextRequest", + "XNoOp", + "XOMOfOC", + "XOffsetRegion", + "XOpenDisplay", + "XOpenIM", + "XOpenOM", + "XParseColor", + "XParseGeometry", + "XPeekEvent", + "XPeekIfEvent", + "XPending", + "XPlanesOfScreen", + "XPointInRegion", + "XPolygonRegion", + "XProcessInternalConnection", + "XProtocolRevision", + "XProtocolVersion", + "XPutBackEvent", + "XPutImage", + "XPutPixel", + "XQLength", + "XQueryBestCursor", + "XQueryBestSize", + "XQueryBestStipple", + "XQueryBestTile", + "XQueryColor", + "XQueryColors", + "XQueryExtension", + "XQueryFont", + "XQueryKeymap", + "XQueryPointer", + "XQueryTextExtents", + "XQueryTextExtents16", + "XQueryTree", + "XRaiseWindow", + "XReadBitmapFile", + "XReadBitmapFileData", + "XRebindKeysym", + "XRecolorCursor", + "XReconfigureWMWindow", + "XRectInRegion", + "XRefreshKeyboardMapping", + "XRegisterIMInstantiateCallback", + "XRemoveConnectionWatch", + "XRemoveFromSaveSet", + "XRemoveHost", + "XRemoveHosts", + "XReparentWindow", + "XResetScreenSaver", + "XResizeWindow", + "XResourceManagerString", + "XRestackWindows", + "XRootWindow", + "XRootWindowOfScreen", + "XRotateBuffers", + "XRotateWindowProperties", + "XSaveContext", + "XScreenCount", + "XScreenNumberOfScreen", + "XScreenOfDisplay", + "XScreenResourceString", + "XSelectInput", + "XSendEvent", + "XServerVendor", + "XSetAccessControl", + "XSetAfterFunction", + "XSetArcMode", + "XSetAuthorization", + "XSetBackground", + "XSetClassHint", + "XSetClipMask", + "XSetClipOrigin", + "XSetClipRectangles", + "XSetCloseDownMode", + "XSetCommand", + "XSetDashes", + "XSetErrorHandler", + "XSetFillRule", + "XSetFillStyle", + "XSetFont", + "XSetFontPath", + "XSetForeground", + "XSetFunction", + "XSetGraphicsExposures", + "XSetICFocus", + "XSetICValues", + "XSetIMValues", + "XSetIOErrorExitHandler", + "XSetIOErrorHandler", + "XSetIconName", + "XSetIconSizes", + "XSetInputFocus", + "XSetLineAttributes", + "XSetLocaleModifiers", + "XSetModifierMapping", + "XSetNormalHints", + "XSetOCValues", + "XSetOMValues", + "XSetPlaneMask", + "XSetPointerMapping", + "XSetRGBColormaps", + "XSetRegion", + "XSetScreenSaver", + "XSetSelectionOwner", + "XSetSizeHints", + "XSetStandardColormap", + "XSetStandardProperties", + "XSetState", + "XSetStipple", + "XSetSubwindowMode", + "XSetTSOrigin", + "XSetTextProperty", + "XSetTile", + "XSetTransientForHint", + "XSetWMClientMachine", + "XSetWMColormapWindows", + "XSetWMHints", + "XSetWMIconName", + "XSetWMName", + "XSetWMNormalHints", + "XSetWMProperties", + "XSetWMProtocols", + "XSetWMSizeHints", + "XSetWindowBackground", + "XSetWindowBackgroundPixmap", + "XSetWindowBorder", + "XSetWindowBorderPixmap", + "XSetWindowBorderWidth", + "XSetWindowColormap", + "XSetZoomHints", + "XShrinkRegion", + "XStoreBuffer", + "XStoreBytes", + "XStoreColor", + "XStoreColors", + "XStoreName", + "XStoreNamedColor", + "XStringListToTextProperty", + "XStringToKeysym", + "XSubImage", + "XSubtractRegion", + "XSupportsLocale", + "XSync", + "XSynchronize", + "XTextExtents", + "XTextExtents16", + "XTextPropertyToStringList", + "XTextWidth", + "XTextWidth16", + "XTranslateCoordinates", + "XUndefineCursor", + "XUngrabButton", + "XUngrabKey", + "XUngrabKeyboard", + "XUngrabPointer", + "XUngrabServer", + "XUninstallColormap", + "XUnionRectWithRegion", + "XUnionRegion", + "XUnloadFont", + "XUnlockDisplay", + "XUnmapSubwindows", + "XUnmapWindow", + "XUnregisterIMInstantiateCallback", + "XUnsetICFocus", + "XVaCreateNestedList", + "XVendorRelease", + "XVisualIDFromVisual", + "XWMGeometry", + "XWarpPointer", + "XWhitePixel", + "XWhitePixelOfScreen", + "XWidthMMOfScreen", + "XWidthOfScreen", + "XWindowEvent", + "XWithdrawWindow", + "XWriteBitmapFile", + "XXorRegion", + "XcmsAddColorSpace", + "XcmsAddFunctionSet", + "XcmsAllocColor", + "XcmsAllocNamedColor", + "XcmsCCCOfColormap", + "XcmsCIELabClipL", + "XcmsCIELabClipLab", + "XcmsCIELabClipab", + "XcmsCIELabQueryMaxC", + "XcmsCIELabQueryMaxL", + "XcmsCIELabQueryMaxLC", + "XcmsCIELabQueryMinL", + "XcmsCIELabToCIEXYZ", + "XcmsCIELabWhiteShiftColors", + "XcmsCIELuvClipL", + "XcmsCIELuvClipLuv", + "XcmsCIELuvClipuv", + "XcmsCIELuvQueryMaxC", + "XcmsCIELuvQueryMaxL", + "XcmsCIELuvQueryMaxLC", + "XcmsCIELuvQueryMinL", + "XcmsCIELuvToCIEuvY", + "XcmsCIELuvWhiteShiftColors", + "XcmsCIEXYZToCIELab", + "XcmsCIEXYZToCIEuvY", + "XcmsCIEXYZToCIExyY", + "XcmsCIEXYZToRGBi", + "XcmsCIEuvYToCIELuv", + "XcmsCIEuvYToCIEXYZ", + "XcmsCIEuvYToTekHVC", + "XcmsCIExyYToCIEXYZ", + "XcmsClientWhitePointOfCCC", + "XcmsConvertColors", + "XcmsCreateCCC", + "XcmsDefaultCCC", + "XcmsDisplayOfCCC", + "XcmsFormatOfPrefix", + "XcmsFreeCCC", + "XcmsLookupColor", + "XcmsPrefixOfFormat", + "XcmsQueryBlack", + "XcmsQueryBlue", + "XcmsQueryColor", + "XcmsQueryColors", + "XcmsQueryGreen", + "XcmsQueryRed", + "XcmsQueryWhite", + "XcmsRGBToRGBi", + "XcmsRGBiToCIEXYZ", + "XcmsRGBiToRGB", + "XcmsScreenNumberOfCCC", + "XcmsScreenWhitePointOfCCC", + "XcmsSetCCCOfColormap", + "XcmsSetCompressionProc", + "XcmsSetWhiteAdjustProc", + "XcmsSetWhitePoint", + "XcmsStoreColor", + "XcmsStoreColors", + "XcmsTekHVCClipC", + "XcmsTekHVCClipV", + "XcmsTekHVCClipVC", + "XcmsTekHVCQueryMaxC", + "XcmsTekHVCQueryMaxV", + "XcmsTekHVCQueryMaxVC", + "XcmsTekHVCQueryMaxVSamples", + "XcmsTekHVCQueryMinV", + "XcmsTekHVCToCIEuvY", + "XcmsTekHVCWhiteShiftColors", + "XcmsVisualOfCCC", + "XkbAddDeviceLedInfo", + "XkbAddGeomColor", + "XkbAddGeomDoodad", + "XkbAddGeomKey", + "XkbAddGeomKeyAlias", + "XkbAddGeomOutline", + "XkbAddGeomOverlay", + "XkbAddGeomOverlayKey", + "XkbAddGeomOverlayRow", + "XkbAddGeomProperty", + "XkbAddGeomRow", + "XkbAddGeomSection", + "XkbAddGeomShape", + "XkbAddKeyType", + "XkbAllocClientMap", + "XkbAllocCompatMap", + "XkbAllocControls", + "XkbAllocDeviceInfo", + "XkbAllocGeomColors", + "XkbAllocGeomDoodads", + "XkbAllocGeomKeyAliases", + "XkbAllocGeomKeys", + "XkbAllocGeomOutlines", + "XkbAllocGeomOverlayKeys", + "XkbAllocGeomOverlayRows", + "XkbAllocGeomOverlays", + "XkbAllocGeomPoints", + "XkbAllocGeomProps", + "XkbAllocGeomRows", + "XkbAllocGeomSectionDoodads", + "XkbAllocGeomSections", + "XkbAllocGeomShapes", + "XkbAllocGeometry", + "XkbAllocIndicatorMaps", + "XkbAllocKeyboard", + "XkbAllocNames", + "XkbAllocServerMap", + "XkbApplyCompatMapToKey", + "XkbApplyVirtualModChanges", + "XkbBell", + "XkbBellEvent", + "XkbChangeDeviceInfo", + "XkbChangeEnabledControls", + "XkbChangeKeycodeRange", + "XkbChangeMap", + "XkbChangeNames", + "XkbChangeTypesOfKey", + "XkbComputeEffectiveMap", + "XkbComputeRowBounds", + "XkbComputeSectionBounds", + "XkbComputeShapeBounds", + "XkbComputeShapeTop", + "XkbCopyKeyType", + "XkbCopyKeyTypes", + "XkbDeviceBell", + "XkbDeviceBellEvent", + "XkbFindOverlayForKey", + "XkbForceBell", + "XkbForceDeviceBell", + "XkbFreeClientMap", + "XkbFreeCompatMap", + "XkbFreeComponentList", + "XkbFreeControls", + "XkbFreeDeviceInfo", + "XkbFreeGeomColors", + "XkbFreeGeomDoodads", + "XkbFreeGeomKeyAliases", + "XkbFreeGeomKeys", + "XkbFreeGeomOutlines", + "XkbFreeGeomOverlayKeys", + "XkbFreeGeomOverlayRows", + "XkbFreeGeomOverlays", + "XkbFreeGeomPoints", + "XkbFreeGeomProperties", + "XkbFreeGeomRows", + "XkbFreeGeomSections", + "XkbFreeGeomShapes", + "XkbFreeGeometry", + "XkbFreeIndicatorMaps", + "XkbFreeKeyboard", + "XkbFreeNames", + "XkbFreeServerMap", + "XkbGetAutoRepeatRate", + "XkbGetAutoResetControls", + "XkbGetCompatMap", + "XkbGetControls", + "XkbGetDetectableAutoRepeat", + "XkbGetDeviceButtonActions", + "XkbGetDeviceInfo", + "XkbGetDeviceInfoChanges", + "XkbGetDeviceLedInfo", + "XkbGetGeometry", + "XkbGetIndicatorMap", + "XkbGetIndicatorState", + "XkbGetKeyActions", + "XkbGetKeyBehaviors", + "XkbGetKeyExplicitComponents", + "XkbGetKeyModifierMap", + "XkbGetKeySyms", + "XkbGetKeyTypes", + "XkbGetKeyVirtualModMap", + "XkbGetKeyboard", + "XkbGetKeyboardByName", + "XkbGetMap", + "XkbGetMapChanges", + "XkbGetNamedDeviceIndicator", + "XkbGetNamedGeometry", + "XkbGetNamedIndicator", + "XkbGetNames", + "XkbGetPerClientControls", + "XkbGetState", + "XkbGetUpdatedMap", + "XkbGetVirtualMods", + "XkbGetXlibControls", + "XkbIgnoreExtension", + "XkbInitCanonicalKeyTypes", + "XkbKeyTypesForCoreSymbols", + "XkbKeycodeToKeysym", + "XkbKeysymToModifiers", + "XkbLatchGroup", + "XkbLatchModifiers", + "XkbLibraryVersion", + "XkbListComponents", + "XkbLockGroup", + "XkbLockModifiers", + "XkbLookupKeyBinding", + "XkbLookupKeySym", + "XkbNoteControlsChanges", + "XkbNoteDeviceChanges", + "XkbNoteMapChanges", + "XkbNoteNameChanges", + "XkbOpenDisplay", + "XkbQueryExtension", + "XkbRefreshKeyboardMapping", + "XkbResizeDeviceButtonActions", + "XkbResizeKeyActions", + "XkbResizeKeySyms", + "XkbResizeKeyType", + "XkbSelectEventDetails", + "XkbSelectEvents", + "XkbSetAtomFuncs", + "XkbSetAutoRepeatRate", + "XkbSetAutoResetControls", + "XkbSetCompatMap", + "XkbSetControls", + "XkbSetDebuggingFlags", + "XkbSetDetectableAutoRepeat", + "XkbSetDeviceButtonActions", + "XkbSetDeviceInfo", + "XkbSetDeviceLedInfo", + "XkbSetGeometry", + "XkbSetIgnoreLockMods", + "XkbSetIndicatorMap", + "XkbSetMap", + "XkbSetNamedDeviceIndicator", + "XkbSetNamedIndicator", + "XkbSetNames", + "XkbSetPerClientControls", + "XkbSetServerInternalMods", + "XkbSetXlibControls", + "XkbToControl", + "XkbTranslateKey", + "XkbTranslateKeyCode", + "XkbTranslateKeySym", + "XkbUpdateActionVirtualMods", + "XkbUpdateKeyTypeVirtualMods", + "XkbUpdateMapFromCore", + "XkbUseExtension", + "XkbVirtualModsToReal", + "XkbXlibControlsImplemented", + "XmbDrawImageString", + "XmbDrawString", + "XmbDrawText", + "XmbLookupString", + "XmbResetIC", + "XmbSetWMProperties", + "XmbTextEscapement", + "XmbTextExtents", + "XmbTextListToTextProperty", + "XmbTextPerCharExtents", + "XmbTextPropertyToTextList", + "Xpermalloc", + "XrmCombineDatabase", + "XrmCombineFileDatabase", + "XrmDestroyDatabase", + "XrmEnumerateDatabase", + "XrmGetDatabase", + "XrmGetFileDatabase", + "XrmGetResource", + "XrmGetStringDatabase", + "XrmInitialize", + "XrmLocaleOfDatabase", + "XrmMergeDatabases", + "XrmParseCommand", + "XrmPermStringToQuark", + "XrmPutFileDatabase", + "XrmPutLineResource", + "XrmPutResource", + "XrmPutStringResource", + "XrmQGetResource", + "XrmQGetSearchList", + "XrmQGetSearchResource", + "XrmQPutResource", + "XrmQPutStringResource", + "XrmQuarkToString", + "XrmSetDatabase", + "XrmStringToBindingQuarkList", + "XrmStringToQuark", + "XrmStringToQuarkList", + "XrmUniqueQuark", + "Xutf8DrawImageString", + "Xutf8DrawString", + "Xutf8DrawText", + "Xutf8LookupString", + "Xutf8ResetIC", + "Xutf8SetWMProperties", + "Xutf8TextEscapement", + "Xutf8TextExtents", + "Xutf8TextListToTextProperty", + "Xutf8TextPerCharExtents", + "Xutf8TextPropertyToTextList", + "XwcDrawImageString", + "XwcDrawString", + "XwcDrawText", + "XwcFreeStringList", + "XwcLookupString", + "XwcResetIC", + "XwcTextEscapement", + "XwcTextExtents", + "XwcTextListToTextProperty", + "XwcTextPerCharExtents", + "XwcTextPropertyToTextList", + "_Utf8GetConvByName", + "_XAllocID", + "_XAllocIDs", + "_XAllocScratch", + "_XAllocTemp", + "_XAsyncErrorHandler", + "_XCloseLC", + "_XColor_to_XcmsRGB", + "_XConnectXCB", + "_XCopyEventCookie", + "_XCopyToArg", + "_XData32", + "_XDefaultError", + "_XDefaultIOError", + "_XDefaultIOErrorExit", + "_XDefaultOpenIM", + "_XDefaultOpenOM", + "_XDefaultWireError", + "_XDeq", + "_XDeqAsyncHandler", + "_XEatData", + "_XEatDataWords", + "_XEnq", + "_XError", + "_XEventToWire", + "_XEventsQueued", + "_XF86BigfontFreeFontMetrics", + "_XF86LoadQueryLocaleFont", + "_XFetchEventCookie", + "_XFlush", + "_XFlushGCCache", + "_XFreeAtomTable", + "_XFreeDisplayStructure", + "_XFreeEventCookies", + "_XFreeExtData", + "_XFreeTemp", + "_XFreeX11XCBStructure", + "_XGetAsyncData", + "_XGetAsyncReply", + "_XGetBitsPerPixel", + "_XGetHostname", + "_XGetLCValues", + "_XGetRequest", + "_XGetScanlinePad", + "_XGetWindowAttributes", + "_XIMCompileResourceList", + "_XIOError", + "_XInitIM", + "_XInitImageFuncPtrs", + "_XInitKeysymDB", + "_XInitOM", + "_XIsEventCookie", + "_XKeyInitialize", + "_XKeycodeToKeysym", + "_XKeysymToKeycode", + "_XKeysymToModifiers", + "_XLookupKeysym", + "_XLookupString", + "_XNoticeCreateBitmap", + "_XNoticePutBitmap", + "_XOpenLC", + "_XParseBaseFontNameList", + "_XPollfdCacheAdd", + "_XPollfdCacheDel", + "_XPollfdCacheInit", + "_XProcessInternalConnection", + "_XProcessWindowAttributes", + "_XPutBackEvent", + "_XRead", + "_XRead32", + "_XReadEvents", + "_XReadPad", + "_XRefreshKeyboardMapping", + "_XRegisterFilterByMask", + "_XRegisterFilterByType", + "_XRegisterInternalConnection", + "_XReply", + "_XReverse_Bytes", + "_XScreenOfWindow", + "_XSend", + "_XSetClipRectangles", + "_XSetImage", + "_XSetLastRequestRead", + "_XStoreEventCookie", + "_XTextHeight", + "_XTextHeight16", + "_XTranslateKey", + "_XTranslateKeySym", + "_XTryShapeBitmapCursor", + "_XUnknownCopyEventCookie", + "_XUnknownNativeEvent", + "_XUnknownWireEvent", + "_XUnknownWireEventCookie", + "_XUnregisterFilter", + "_XUnregisterInternalConnection", + "_XUnresolveColor", + "_XUpdateAtomCache", + "_XUpdateGCCache", + "_XVIDtoVisual", + "_XWireToEvent", + "_XcmsAddCmapRec", + "_XcmsArcTangent", + "_XcmsCIELabQueryMaxLCRGB", + "_XcmsCIELuvQueryMaxLCRGB", + "_XcmsCIEXYZ_ValidSpec", + "_XcmsCIEuvY_ValidSpec", + "_XcmsConvertColorsWithWhitePt", + "_XcmsCopyCmapRecAndFree", + "_XcmsCopyISOLatin1Lowered", + "_XcmsCopyPointerArray", + "_XcmsCosine", + "_XcmsCubeRoot", + "_XcmsDDConvertColors", + "_XcmsDIConvertColors", + "_XcmsDeleteCmapRec", + "_XcmsEqualWhitePts", + "_XcmsFreeIntensityMaps", + "_XcmsFreePointerArray", + "_XcmsGetElement", + "_XcmsGetIntensityMap", + "_XcmsGetProperty", + "_XcmsInitDefaultCCCs", + "_XcmsInitScrnInfo", + "_XcmsLRGB_InitScrnDefault", + "_XcmsPushPointerArray", + "_XcmsRGB_to_XColor", + "_XcmsRegFormatOfPrefix", + "_XcmsResolveColor", + "_XcmsResolveColorString", + "_XcmsSetGetColor", + "_XcmsSetGetColors", + "_XcmsSine", + "_XcmsSquareRoot", + "_XcmsTekHVCQueryMaxVCRGB", + "_XcmsTekHVC_CheckModify", + "_XcmsUnresolveColor", + "_XimCbDispatch", + "_XimCheckCreateICValues", + "_XimCheckDataSize", + "_XimCheckICMode", + "_XimCheckIMMode", + "_XimCheckIfLocalProcessing", + "_XimCheckIfThaiProcessing", + "_XimCheckLocalInputStyle", + "_XimCommitCallback", + "_XimConnect", + "_XimDecodeICATTRIBUTE", + "_XimDecodeIMATTRIBUTE", + "_XimDecodeLocalICAttr", + "_XimDecodeLocalIMAttr", + "_XimDestroyIMStructureList", + "_XimDispatchInit", + "_XimEncodeICATTRIBUTE", + "_XimEncodeIMATTRIBUTE", + "_XimEncodeLocalICAttr", + "_XimEncodeLocalIMAttr", + "_XimError", + "_XimErrorCallback", + "_XimExtension", + "_XimFilterWaitEvent", + "_XimFlush", + "_XimForwardEvent", + "_XimForwardEventCallback", + "_XimFreeCommitInfo", + "_XimFreeProtoIntrCallback", + "_XimFreeTransIntrCallback", + "_XimGetAttributeID", + "_XimGetCharCode", + "_XimGetCurrentICValues", + "_XimGetCurrentIMValues", + "_XimGetICValueData", + "_XimGetIMValueData", + "_XimGetLocaleCode", + "_XimGetMyEndian", + "_XimGetResourceListRec", + "_XimGetResourceListRecByQuark", + "_XimGetWindowEventmask", + "_XimICOfXICID", + "_XimInitialResourceInfo", + "_XimLcctstombs", + "_XimLcctstoutf8", + "_XimLcctstowcs", + "_XimLocalCreateIC", + "_XimLocalFilter", + "_XimLocalGetICValues", + "_XimLocalGetIMValues", + "_XimLocalIMFree", + "_XimLocalMbLookupString", + "_XimLocalOpenIM", + "_XimLocalSetICValues", + "_XimLocalSetIMValues", + "_XimLocalUtf8LookupString", + "_XimLocalWcLookupString", + "_XimLookupMBText", + "_XimLookupUTF8Text", + "_XimLookupWCText", + "_XimMakeICAttrIDList", + "_XimMakeIMAttrIDList", + "_XimOpenIM", + "_XimParseStringFile", + "_XimProcError", + "_XimProcSyncReply", + "_XimProtoCreateIC", + "_XimProtoEventToWire", + "_XimProtoIMFree", + "_XimProtoMbLookupString", + "_XimProtoOpenIM", + "_XimProtoUtf8LookupString", + "_XimProtoWcLookupString", + "_XimProtoWireToEvent", + "_XimRead", + "_XimRegProtoIntrCallback", + "_XimRegisterDispatcher", + "_XimRegisterFilter", + "_XimRegisterIMInstantiateCallback", + "_XimRegisterServerFilter", + "_XimRegisterTriggerKeysCallback", + "_XimReregisterFilter", + "_XimResetIMInstantiateCallback", + "_XimRespSyncReply", + "_XimServerDestroy", + "_XimSetCurrentICValues", + "_XimSetCurrentIMValues", + "_XimSetEventMaskCallback", + "_XimSetHeader", + "_XimSetICDefaults", + "_XimSetICMode", + "_XimSetICResourceList", + "_XimSetICValueData", + "_XimSetIMMode", + "_XimSetIMResourceList", + "_XimSetIMValueData", + "_XimSetInnerICResourceList", + "_XimSetInnerIMResourceList", + "_XimSetLocalIMDefaults", + "_XimShutdown", + "_XimSync", + "_XimSyncCallback", + "_XimThaiCloseIM", + "_XimThaiCreateIC", + "_XimThaiFilter", + "_XimThaiIMFree", + "_XimThaiOpenIM", + "_XimTransCallDispatcher", + "_XimTransConf", + "_XimTransFilterWaitEvent", + "_XimTransFlush", + "_XimTransInternalConnection", + "_XimTransRead", + "_XimTransRegisterDispatcher", + "_XimTransWrite", + "_XimTriggerNotify", + "_XimUnRegisterIMInstantiateCallback", + "_XimUnregisterFilter", + "_XimUnregisterServerFilter", + "_XimWrite", + "_XimXConf", + "_XimXTransBytesReadable", + "_XimXTransClose", + "_XimXTransCloseForCloning", + "_XimXTransConnect", + "_XimXTransDisconnect", + "_XimXTransFreeConnInfo", + "_XimXTransGetConnectionNumber", + "_XimXTransGetHostname", + "_XimXTransGetPeerAddr", + "_XimXTransIsLocal", + "_XimXTransOpenCOTSClient", + "_XimXTransRead", + "_XimXTransReadv", + "_XimXTransSetOption", + "_XimXTransWrite", + "_XimXTransWritev", + "_Ximctstombs", + "_Ximctstoutf8", + "_Ximctstowcs", + "_XkbCopyFromReadBuffer", + "_XkbFreeReadBuffer", + "_XkbGetCharset", + "_XkbGetConverters", + "_XkbGetReadBufferCountedString", + "_XkbGetReadBufferPtr", + "_XkbInitReadBuffer", + "_XkbNoteCoreMapChanges", + "_XkbPeekAtReadBuffer", + "_XkbReadBufferCopy32", + "_XkbReadBufferCopyKeySyms", + "_XkbReadCopyData32", + "_XkbReadCopyKeySyms", + "_XkbReadGetCompatMapReply", + "_XkbReadGetGeometryReply", + "_XkbReadGetIndicatorMapReply", + "_XkbReadGetMapReply", + "_XkbReadGetNamesReply", + "_XkbReloadDpy", + "_XkbSkipReadBufferData", + "_XkbWriteCopyData32", + "_XkbWriteCopyKeySyms", + "_XlcAddCT", + "_XlcAddCharSet", + "_XlcAddGB18030LocaleConverters", + "_XlcAddLoader", + "_XlcAddUtf8Converters", + "_XlcAddUtf8LocaleConverters", + "_XlcCloseConverter", + "_XlcCompareISOLatin1", + "_XlcCompileResourceList", + "_XlcConvert", + "_XlcCopyFromArg", + "_XlcCopyToArg", + "_XlcCountVaList", + "_XlcCreateDefaultCharSet", + "_XlcCreateLC", + "_XlcCreateLocaleDataBase", + "_XlcCurrentLC", + "_XlcDbg_printValue", + "_XlcDeInitLoader", + "_XlcDefaultLoader", + "_XlcDefaultMapModifiers", + "_XlcDestroyLC", + "_XlcDestroyLocaleDataBase", + "_XlcFileName", + "_XlcGenericLoader", + "_XlcGetCSValues", + "_XlcGetCharSet", + "_XlcGetCharSetWithSide", + "_XlcGetLocaleDataBase", + "_XlcGetResource", + "_XlcGetValues", + "_XlcInitCTInfo", + "_XlcInitLoader", + "_XlcLocaleDirName", + "_XlcLocaleLibDirName", + "_XlcMapOSLocaleName", + "_XlcNCompareISOLatin1", + "_XlcOpenConverter", + "_XlcParseCharSet", + "_XlcParse_scopemaps", + "_XlcRemoveLoader", + "_XlcResetConverter", + "_XlcResolveI18NPath", + "_XlcResolveLocaleName", + "_XlcSetConverter", + "_XlcSetValues", + "_XlcUtf8Loader", + "_XlcVaToArgList", + "_XlcValidModSyntax", + "_Xlcmbstoutf8", + "_Xlcmbstowcs", + "_Xlcmbtowc", + "_Xlcwcstombs", + "_Xlcwctomb", + "_XmbDefaultDrawImageString", + "_XmbDefaultDrawString", + "_XmbDefaultTextEscapement", + "_XmbDefaultTextExtents", + "_XmbDefaultTextPerCharExtents", + "_XmbGenericDrawImageString", + "_XmbGenericDrawString", + "_XmbGenericTextEscapement", + "_XmbGenericTextExtents", + "_XmbGenericTextPerCharExtents", + "_XmbTextListToTextProperty", + "_XmbTextPropertyToTextList", + "_Xmblen", + "_Xmbstoutf8", + "_Xmbstowcs", + "_Xmbtowc", + "_XomConvert", + "_XomGenericDrawString", + "_XomGenericOpenOM", + "_XomGenericTextExtents", + "_XomGetFontDataFromFontSet", + "_XomInitConverter", + "_XrmDefaultInitParseInfo", + "_XrmInitParseInfo", + "_XrmInternalStringToQuark", + "_Xutf8DefaultDrawImageString", + "_Xutf8DefaultDrawString", + "_Xutf8DefaultTextEscapement", + "_Xutf8DefaultTextExtents", + "_Xutf8DefaultTextPerCharExtents", + "_Xutf8GenericDrawImageString", + "_Xutf8GenericDrawString", + "_Xutf8GenericTextEscapement", + "_Xutf8GenericTextExtents", + "_Xutf8GenericTextPerCharExtents", + "_Xutf8TextListToTextProperty", + "_Xutf8TextPropertyToTextList", + "_XwcDefaultDrawImageString", + "_XwcDefaultDrawString", + "_XwcDefaultTextEscapement", + "_XwcDefaultTextExtents", + "_XwcDefaultTextPerCharExtents", + "_XwcFreeStringList", + "_XwcGenericDrawImageString", + "_XwcGenericDrawString", + "_XwcGenericTextEscapement", + "_XwcGenericTextExtents", + "_XwcGenericTextPerCharExtents", + "_XwcTextListToTextProperty", + "_XwcTextPropertyToTextList", + "_Xwcscmp", + "_Xwcscpy", + "_Xwcslen", + "_Xwcsncmp", + "_Xwcsncpy", + "_Xwcstombs", + "_Xwctomb", + "read_EncodingInfo", + "xlocaledir", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libX11_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libX11_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libX11_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libX11_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libX11_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libX11_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libX11_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libX11_so_tramp_table, 0, SYM_COUNT * sizeof(_libX11_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libX11.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libX11.so.tramp.S new file mode 100644 index 0000000..4c57adb --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libX11.so.tramp.S @@ -0,0 +1,40346 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libX11_so_tramp_table + .hidden _libX11_so_tramp_table + .align 8 +_libX11_so_tramp_table: + .zero 9464 + + .text + + .globl _libX11_so_tramp_resolve + .hidden _libX11_so_tramp_resolve + + .globl _libX11_so_save_regs_and_resolve + .hidden _libX11_so_save_regs_and_resolve + .type _libX11_so_save_regs_and_resolve, %function +_libX11_so_save_regs_and_resolve: + .cfi_startproc + +#define PUSH_REG(reg) pushq %reg ; .cfi_adjust_cfa_offset 8; .cfi_rel_offset reg, 0 +#define POP_REG(reg) popq %reg ; .cfi_adjust_cfa_offset -8; .cfi_restore reg + +#define DEC_STACK(d) subq $d, %rsp; .cfi_adjust_cfa_offset d +#define INC_STACK(d) addq $d, %rsp; .cfi_adjust_cfa_offset -d + +#define PUSH_MMX_REG(reg) DEC_STACK(8); movq %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_MMX_REG(reg) movq (%rsp), %reg; .cfi_restore reg; INC_STACK(8) + +#define PUSH_XMM_REG(reg) DEC_STACK(16); movdqa %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_XMM_REG(reg) movdqa (%rsp), %reg; .cfi_restore reg; INC_STACK(16) + +// TODO: cfi_offset/cfi_restore +#define PUSH_YMM_REG(reg) DEC_STACK(32); vmovdqu %reg, (%rsp) +#define POP_YMM_REG(reg) vmovdqu (%rsp), %reg; INC_STACK(32) + +// TODO: cfi_offset/cfi_restore +#define PUSH_ZMM_REG(reg) DEC_STACK(64); vmovdqu32 %reg, (%rsp) +#define POP_ZMM_REG(reg) vmovdqu32 (%rsp), %reg; INC_STACK(64) + + // Slow path which calls dlsym, taken only on first call. + // All registers are stored to handle arbitrary calling conventions + // (except x87 FPU registers which do not have to be preserved). + // For Dwarf directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + .cfi_def_cfa_offset 8 // Return address + + PUSH_REG(rdi) // 16 + mov 0x10(%rsp), %rdi + PUSH_REG(rbx) + PUSH_REG(rbx) // 16 + PUSH_REG(rcx) + PUSH_REG(rdx) // 16 + PUSH_REG(rbp) + PUSH_REG(rsi) // 16 + PUSH_REG(r8) + PUSH_REG(r9) // 16 + PUSH_REG(r10) + PUSH_REG(r11) // 16 + PUSH_REG(r12) + PUSH_REG(r13) // 16 + PUSH_REG(r14) + PUSH_REG(r15) // 16 + + // Maybe use cpuid instead of macro to detect current vector size... +#ifdef __AVX512F__ + PUSH_ZMM_REG(zmm0) + PUSH_ZMM_REG(zmm1) + PUSH_ZMM_REG(zmm2) + PUSH_ZMM_REG(zmm3) + PUSH_ZMM_REG(zmm4) + PUSH_ZMM_REG(zmm5) + PUSH_ZMM_REG(zmm6) + PUSH_ZMM_REG(zmm7) +#elif defined __AVX__ + PUSH_YMM_REG(ymm0) + PUSH_YMM_REG(ymm1) + PUSH_YMM_REG(ymm2) + PUSH_YMM_REG(ymm3) + PUSH_YMM_REG(ymm4) + PUSH_YMM_REG(ymm5) + PUSH_YMM_REG(ymm6) + PUSH_YMM_REG(ymm7) +#elif defined __SSE__ + PUSH_XMM_REG(xmm0) + PUSH_XMM_REG(xmm1) + PUSH_XMM_REG(xmm2) + PUSH_XMM_REG(xmm3) + PUSH_XMM_REG(xmm4) + PUSH_XMM_REG(xmm5) + PUSH_XMM_REG(xmm6) + PUSH_XMM_REG(xmm7) +#endif + + // MMX registers are not used to pass arguments so we do not save them + + // Stack is just 8-byte aligned but callee will re-align to 16 + call _libX11_so_tramp_resolve + +#ifdef __AVX512F__ + POP_ZMM_REG(zmm7) + POP_ZMM_REG(zmm6) + POP_ZMM_REG(zmm5) + POP_ZMM_REG(zmm4) + POP_ZMM_REG(zmm3) + POP_ZMM_REG(zmm2) + POP_ZMM_REG(zmm1) + POP_ZMM_REG(zmm0) // 16 +#elif defined __AVX__ + POP_YMM_REG(ymm7) + POP_YMM_REG(ymm6) + POP_YMM_REG(ymm5) + POP_YMM_REG(ymm4) + POP_YMM_REG(ymm3) + POP_YMM_REG(ymm2) + POP_YMM_REG(ymm1) + POP_YMM_REG(ymm0) // 16 +#elif defined __SSE__ + POP_XMM_REG(xmm7) + POP_XMM_REG(xmm6) + POP_XMM_REG(xmm5) + POP_XMM_REG(xmm4) + POP_XMM_REG(xmm3) + POP_XMM_REG(xmm2) + POP_XMM_REG(xmm1) + POP_XMM_REG(xmm0) // 16 +#endif + + POP_REG(r15) + POP_REG(r14) // 16 + POP_REG(r13) + POP_REG(r12) // 16 + POP_REG(r11) + POP_REG(r10) // 16 + POP_REG(r9) + POP_REG(r8) // 16 + POP_REG(rsi) + POP_REG(rbp) // 16 + POP_REG(rdx) + POP_REG(rcx) // 16 + POP_REG(rbx) + POP_REG(rbx) // 16 + POP_REG(rdi) + + ret + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl KeySymToUcs4 + .p2align 4 + .type KeySymToUcs4, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden KeySymToUcs4 +#endif +KeySymToUcs4: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+0(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+0(%rip) +2: + pushq $0 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XActivateScreenSaver + .p2align 4 + .type XActivateScreenSaver, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XActivateScreenSaver +#endif +XActivateScreenSaver: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8(%rip) +2: + pushq $1 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAddConnectionWatch + .p2align 4 + .type XAddConnectionWatch, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAddConnectionWatch +#endif +XAddConnectionWatch: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+16(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+16(%rip) +2: + pushq $2 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAddExtension + .p2align 4 + .type XAddExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAddExtension +#endif +XAddExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+24(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+24(%rip) +2: + pushq $3 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAddHost + .p2align 4 + .type XAddHost, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAddHost +#endif +XAddHost: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+32(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+32(%rip) +2: + pushq $4 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAddHosts + .p2align 4 + .type XAddHosts, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAddHosts +#endif +XAddHosts: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+40(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+40(%rip) +2: + pushq $5 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAddPixel + .p2align 4 + .type XAddPixel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAddPixel +#endif +XAddPixel: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+48(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+48(%rip) +2: + pushq $6 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAddToExtensionList + .p2align 4 + .type XAddToExtensionList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAddToExtensionList +#endif +XAddToExtensionList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+56(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+56(%rip) +2: + pushq $7 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAddToSaveSet + .p2align 4 + .type XAddToSaveSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAddToSaveSet +#endif +XAddToSaveSet: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+64(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+64(%rip) +2: + pushq $8 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllPlanes + .p2align 4 + .type XAllPlanes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllPlanes +#endif +XAllPlanes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+72(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+72(%rip) +2: + pushq $9 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllocClassHint + .p2align 4 + .type XAllocClassHint, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllocClassHint +#endif +XAllocClassHint: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+80(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+80(%rip) +2: + pushq $10 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllocColor + .p2align 4 + .type XAllocColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllocColor +#endif +XAllocColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+88(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+88(%rip) +2: + pushq $11 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllocColorCells + .p2align 4 + .type XAllocColorCells, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllocColorCells +#endif +XAllocColorCells: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+96(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+96(%rip) +2: + pushq $12 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllocColorPlanes + .p2align 4 + .type XAllocColorPlanes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllocColorPlanes +#endif +XAllocColorPlanes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+104(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+104(%rip) +2: + pushq $13 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllocIconSize + .p2align 4 + .type XAllocIconSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllocIconSize +#endif +XAllocIconSize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+112(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+112(%rip) +2: + pushq $14 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllocNamedColor + .p2align 4 + .type XAllocNamedColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllocNamedColor +#endif +XAllocNamedColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+120(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+120(%rip) +2: + pushq $15 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllocSizeHints + .p2align 4 + .type XAllocSizeHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllocSizeHints +#endif +XAllocSizeHints: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+128(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+128(%rip) +2: + pushq $16 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllocStandardColormap + .p2align 4 + .type XAllocStandardColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllocStandardColormap +#endif +XAllocStandardColormap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+136(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+136(%rip) +2: + pushq $17 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllocWMHints + .p2align 4 + .type XAllocWMHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllocWMHints +#endif +XAllocWMHints: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+144(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+144(%rip) +2: + pushq $18 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAllowEvents + .p2align 4 + .type XAllowEvents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAllowEvents +#endif +XAllowEvents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+152(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+152(%rip) +2: + pushq $19 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAutoRepeatOff + .p2align 4 + .type XAutoRepeatOff, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAutoRepeatOff +#endif +XAutoRepeatOff: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+160(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+160(%rip) +2: + pushq $20 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XAutoRepeatOn + .p2align 4 + .type XAutoRepeatOn, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XAutoRepeatOn +#endif +XAutoRepeatOn: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+168(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+168(%rip) +2: + pushq $21 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XBaseFontNameListOfFontSet + .p2align 4 + .type XBaseFontNameListOfFontSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XBaseFontNameListOfFontSet +#endif +XBaseFontNameListOfFontSet: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+176(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+176(%rip) +2: + pushq $22 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XBell + .p2align 4 + .type XBell, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XBell +#endif +XBell: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+184(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+184(%rip) +2: + pushq $23 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XBitmapBitOrder + .p2align 4 + .type XBitmapBitOrder, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XBitmapBitOrder +#endif +XBitmapBitOrder: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+192(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+192(%rip) +2: + pushq $24 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XBitmapPad + .p2align 4 + .type XBitmapPad, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XBitmapPad +#endif +XBitmapPad: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+200(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+200(%rip) +2: + pushq $25 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XBitmapUnit + .p2align 4 + .type XBitmapUnit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XBitmapUnit +#endif +XBitmapUnit: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+208(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+208(%rip) +2: + pushq $26 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XBlackPixel + .p2align 4 + .type XBlackPixel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XBlackPixel +#endif +XBlackPixel: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+216(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+216(%rip) +2: + pushq $27 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XBlackPixelOfScreen + .p2align 4 + .type XBlackPixelOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XBlackPixelOfScreen +#endif +XBlackPixelOfScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+224(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+224(%rip) +2: + pushq $28 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCellsOfScreen + .p2align 4 + .type XCellsOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCellsOfScreen +#endif +XCellsOfScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+232(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+232(%rip) +2: + pushq $29 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XChangeActivePointerGrab + .p2align 4 + .type XChangeActivePointerGrab, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XChangeActivePointerGrab +#endif +XChangeActivePointerGrab: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+240(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+240(%rip) +2: + pushq $30 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XChangeGC + .p2align 4 + .type XChangeGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XChangeGC +#endif +XChangeGC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+248(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+248(%rip) +2: + pushq $31 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XChangeKeyboardControl + .p2align 4 + .type XChangeKeyboardControl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XChangeKeyboardControl +#endif +XChangeKeyboardControl: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+256(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+256(%rip) +2: + pushq $32 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XChangeKeyboardMapping + .p2align 4 + .type XChangeKeyboardMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XChangeKeyboardMapping +#endif +XChangeKeyboardMapping: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+264(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+264(%rip) +2: + pushq $33 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XChangePointerControl + .p2align 4 + .type XChangePointerControl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XChangePointerControl +#endif +XChangePointerControl: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+272(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+272(%rip) +2: + pushq $34 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XChangeProperty + .p2align 4 + .type XChangeProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XChangeProperty +#endif +XChangeProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+280(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+280(%rip) +2: + pushq $35 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XChangeSaveSet + .p2align 4 + .type XChangeSaveSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XChangeSaveSet +#endif +XChangeSaveSet: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+288(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+288(%rip) +2: + pushq $36 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XChangeWindowAttributes + .p2align 4 + .type XChangeWindowAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XChangeWindowAttributes +#endif +XChangeWindowAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+296(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+296(%rip) +2: + pushq $37 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCheckIfEvent + .p2align 4 + .type XCheckIfEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCheckIfEvent +#endif +XCheckIfEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+304(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+304(%rip) +2: + pushq $38 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCheckMaskEvent + .p2align 4 + .type XCheckMaskEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCheckMaskEvent +#endif +XCheckMaskEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+312(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+312(%rip) +2: + pushq $39 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCheckTypedEvent + .p2align 4 + .type XCheckTypedEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCheckTypedEvent +#endif +XCheckTypedEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+320(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+320(%rip) +2: + pushq $40 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCheckTypedWindowEvent + .p2align 4 + .type XCheckTypedWindowEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCheckTypedWindowEvent +#endif +XCheckTypedWindowEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+328(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+328(%rip) +2: + pushq $41 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCheckWindowEvent + .p2align 4 + .type XCheckWindowEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCheckWindowEvent +#endif +XCheckWindowEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+336(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+336(%rip) +2: + pushq $42 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCirculateSubwindows + .p2align 4 + .type XCirculateSubwindows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCirculateSubwindows +#endif +XCirculateSubwindows: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+344(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+344(%rip) +2: + pushq $43 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCirculateSubwindowsDown + .p2align 4 + .type XCirculateSubwindowsDown, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCirculateSubwindowsDown +#endif +XCirculateSubwindowsDown: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+352(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+352(%rip) +2: + pushq $44 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCirculateSubwindowsUp + .p2align 4 + .type XCirculateSubwindowsUp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCirculateSubwindowsUp +#endif +XCirculateSubwindowsUp: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+360(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+360(%rip) +2: + pushq $45 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XClearArea + .p2align 4 + .type XClearArea, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XClearArea +#endif +XClearArea: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+368(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+368(%rip) +2: + pushq $46 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XClearWindow + .p2align 4 + .type XClearWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XClearWindow +#endif +XClearWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+376(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+376(%rip) +2: + pushq $47 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XClipBox + .p2align 4 + .type XClipBox, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XClipBox +#endif +XClipBox: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+384(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+384(%rip) +2: + pushq $48 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCloseDisplay + .p2align 4 + .type XCloseDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCloseDisplay +#endif +XCloseDisplay: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+392(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+392(%rip) +2: + pushq $49 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCloseIM + .p2align 4 + .type XCloseIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCloseIM +#endif +XCloseIM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+400(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+400(%rip) +2: + pushq $50 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCloseOM + .p2align 4 + .type XCloseOM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCloseOM +#endif +XCloseOM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+408(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+408(%rip) +2: + pushq $51 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XConfigureWindow + .p2align 4 + .type XConfigureWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XConfigureWindow +#endif +XConfigureWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+416(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+416(%rip) +2: + pushq $52 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XConnectionNumber + .p2align 4 + .type XConnectionNumber, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XConnectionNumber +#endif +XConnectionNumber: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+424(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+424(%rip) +2: + pushq $53 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XContextDependentDrawing + .p2align 4 + .type XContextDependentDrawing, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XContextDependentDrawing +#endif +XContextDependentDrawing: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+432(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+432(%rip) +2: + pushq $54 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XContextualDrawing + .p2align 4 + .type XContextualDrawing, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XContextualDrawing +#endif +XContextualDrawing: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+440(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+440(%rip) +2: + pushq $55 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XConvertCase + .p2align 4 + .type XConvertCase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XConvertCase +#endif +XConvertCase: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+448(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+448(%rip) +2: + pushq $56 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XConvertSelection + .p2align 4 + .type XConvertSelection, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XConvertSelection +#endif +XConvertSelection: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+456(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+456(%rip) +2: + pushq $57 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCopyArea + .p2align 4 + .type XCopyArea, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCopyArea +#endif +XCopyArea: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+464(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+464(%rip) +2: + pushq $58 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCopyColormapAndFree + .p2align 4 + .type XCopyColormapAndFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCopyColormapAndFree +#endif +XCopyColormapAndFree: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+472(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+472(%rip) +2: + pushq $59 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCopyGC + .p2align 4 + .type XCopyGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCopyGC +#endif +XCopyGC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+480(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+480(%rip) +2: + pushq $60 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCopyPlane + .p2align 4 + .type XCopyPlane, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCopyPlane +#endif +XCopyPlane: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+488(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+488(%rip) +2: + pushq $61 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateBitmapFromData + .p2align 4 + .type XCreateBitmapFromData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateBitmapFromData +#endif +XCreateBitmapFromData: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+496(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+496(%rip) +2: + pushq $62 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateColormap + .p2align 4 + .type XCreateColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateColormap +#endif +XCreateColormap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+504(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+504(%rip) +2: + pushq $63 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateFontCursor + .p2align 4 + .type XCreateFontCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateFontCursor +#endif +XCreateFontCursor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+512(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+512(%rip) +2: + pushq $64 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateFontSet + .p2align 4 + .type XCreateFontSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateFontSet +#endif +XCreateFontSet: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+520(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+520(%rip) +2: + pushq $65 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateGC + .p2align 4 + .type XCreateGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateGC +#endif +XCreateGC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+528(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+528(%rip) +2: + pushq $66 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateGlyphCursor + .p2align 4 + .type XCreateGlyphCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateGlyphCursor +#endif +XCreateGlyphCursor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+536(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+536(%rip) +2: + pushq $67 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateIC + .p2align 4 + .type XCreateIC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateIC +#endif +XCreateIC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+544(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+544(%rip) +2: + pushq $68 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateImage + .p2align 4 + .type XCreateImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateImage +#endif +XCreateImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+552(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+552(%rip) +2: + pushq $69 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateOC + .p2align 4 + .type XCreateOC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateOC +#endif +XCreateOC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+560(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+560(%rip) +2: + pushq $70 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreatePixmap + .p2align 4 + .type XCreatePixmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreatePixmap +#endif +XCreatePixmap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+568(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+568(%rip) +2: + pushq $71 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreatePixmapCursor + .p2align 4 + .type XCreatePixmapCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreatePixmapCursor +#endif +XCreatePixmapCursor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+576(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+576(%rip) +2: + pushq $72 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreatePixmapFromBitmapData + .p2align 4 + .type XCreatePixmapFromBitmapData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreatePixmapFromBitmapData +#endif +XCreatePixmapFromBitmapData: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+584(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+584(%rip) +2: + pushq $73 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateRegion + .p2align 4 + .type XCreateRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateRegion +#endif +XCreateRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+592(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+592(%rip) +2: + pushq $74 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateSimpleWindow + .p2align 4 + .type XCreateSimpleWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateSimpleWindow +#endif +XCreateSimpleWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+600(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+600(%rip) +2: + pushq $75 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCreateWindow + .p2align 4 + .type XCreateWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCreateWindow +#endif +XCreateWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+608(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+608(%rip) +2: + pushq $76 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultColormap + .p2align 4 + .type XDefaultColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultColormap +#endif +XDefaultColormap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+616(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+616(%rip) +2: + pushq $77 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultColormapOfScreen + .p2align 4 + .type XDefaultColormapOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultColormapOfScreen +#endif +XDefaultColormapOfScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+624(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+624(%rip) +2: + pushq $78 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultDepth + .p2align 4 + .type XDefaultDepth, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultDepth +#endif +XDefaultDepth: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+632(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+632(%rip) +2: + pushq $79 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultDepthOfScreen + .p2align 4 + .type XDefaultDepthOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultDepthOfScreen +#endif +XDefaultDepthOfScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+640(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+640(%rip) +2: + pushq $80 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultGC + .p2align 4 + .type XDefaultGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultGC +#endif +XDefaultGC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+648(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+648(%rip) +2: + pushq $81 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultGCOfScreen + .p2align 4 + .type XDefaultGCOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultGCOfScreen +#endif +XDefaultGCOfScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+656(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+656(%rip) +2: + pushq $82 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultRootWindow + .p2align 4 + .type XDefaultRootWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultRootWindow +#endif +XDefaultRootWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+664(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+664(%rip) +2: + pushq $83 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultScreen + .p2align 4 + .type XDefaultScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultScreen +#endif +XDefaultScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+672(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+672(%rip) +2: + pushq $84 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultScreenOfDisplay + .p2align 4 + .type XDefaultScreenOfDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultScreenOfDisplay +#endif +XDefaultScreenOfDisplay: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+680(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+680(%rip) +2: + pushq $85 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultString + .p2align 4 + .type XDefaultString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultString +#endif +XDefaultString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+688(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+688(%rip) +2: + pushq $86 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultVisual + .p2align 4 + .type XDefaultVisual, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultVisual +#endif +XDefaultVisual: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+696(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+696(%rip) +2: + pushq $87 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefaultVisualOfScreen + .p2align 4 + .type XDefaultVisualOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefaultVisualOfScreen +#endif +XDefaultVisualOfScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+704(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+704(%rip) +2: + pushq $88 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDefineCursor + .p2align 4 + .type XDefineCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDefineCursor +#endif +XDefineCursor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+712(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+712(%rip) +2: + pushq $89 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDeleteContext + .p2align 4 + .type XDeleteContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDeleteContext +#endif +XDeleteContext: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+720(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+720(%rip) +2: + pushq $90 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDeleteModifiermapEntry + .p2align 4 + .type XDeleteModifiermapEntry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDeleteModifiermapEntry +#endif +XDeleteModifiermapEntry: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+728(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+728(%rip) +2: + pushq $91 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDeleteProperty + .p2align 4 + .type XDeleteProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDeleteProperty +#endif +XDeleteProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+736(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+736(%rip) +2: + pushq $92 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDestroyIC + .p2align 4 + .type XDestroyIC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDestroyIC +#endif +XDestroyIC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+744(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+744(%rip) +2: + pushq $93 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDestroyImage + .p2align 4 + .type XDestroyImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDestroyImage +#endif +XDestroyImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+752(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+752(%rip) +2: + pushq $94 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDestroyOC + .p2align 4 + .type XDestroyOC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDestroyOC +#endif +XDestroyOC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+760(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+760(%rip) +2: + pushq $95 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDestroyRegion + .p2align 4 + .type XDestroyRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDestroyRegion +#endif +XDestroyRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+768(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+768(%rip) +2: + pushq $96 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDestroySubwindows + .p2align 4 + .type XDestroySubwindows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDestroySubwindows +#endif +XDestroySubwindows: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+776(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+776(%rip) +2: + pushq $97 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDestroyWindow + .p2align 4 + .type XDestroyWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDestroyWindow +#endif +XDestroyWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+784(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+784(%rip) +2: + pushq $98 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDirectionalDependentDrawing + .p2align 4 + .type XDirectionalDependentDrawing, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDirectionalDependentDrawing +#endif +XDirectionalDependentDrawing: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+792(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+792(%rip) +2: + pushq $99 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisableAccessControl + .p2align 4 + .type XDisableAccessControl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisableAccessControl +#endif +XDisableAccessControl: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+800(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+800(%rip) +2: + pushq $100 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayCells + .p2align 4 + .type XDisplayCells, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayCells +#endif +XDisplayCells: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+808(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+808(%rip) +2: + pushq $101 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayHeight + .p2align 4 + .type XDisplayHeight, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayHeight +#endif +XDisplayHeight: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+816(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+816(%rip) +2: + pushq $102 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayHeightMM + .p2align 4 + .type XDisplayHeightMM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayHeightMM +#endif +XDisplayHeightMM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+824(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+824(%rip) +2: + pushq $103 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayKeycodes + .p2align 4 + .type XDisplayKeycodes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayKeycodes +#endif +XDisplayKeycodes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+832(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+832(%rip) +2: + pushq $104 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayMotionBufferSize + .p2align 4 + .type XDisplayMotionBufferSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayMotionBufferSize +#endif +XDisplayMotionBufferSize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+840(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+840(%rip) +2: + pushq $105 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayName + .p2align 4 + .type XDisplayName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayName +#endif +XDisplayName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+848(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+848(%rip) +2: + pushq $106 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayOfIM + .p2align 4 + .type XDisplayOfIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayOfIM +#endif +XDisplayOfIM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+856(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+856(%rip) +2: + pushq $107 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayOfOM + .p2align 4 + .type XDisplayOfOM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayOfOM +#endif +XDisplayOfOM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+864(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+864(%rip) +2: + pushq $108 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayOfScreen + .p2align 4 + .type XDisplayOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayOfScreen +#endif +XDisplayOfScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+872(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+872(%rip) +2: + pushq $109 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayPlanes + .p2align 4 + .type XDisplayPlanes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayPlanes +#endif +XDisplayPlanes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+880(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+880(%rip) +2: + pushq $110 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayString + .p2align 4 + .type XDisplayString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayString +#endif +XDisplayString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+888(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+888(%rip) +2: + pushq $111 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayWidth + .p2align 4 + .type XDisplayWidth, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayWidth +#endif +XDisplayWidth: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+896(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+896(%rip) +2: + pushq $112 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDisplayWidthMM + .p2align 4 + .type XDisplayWidthMM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDisplayWidthMM +#endif +XDisplayWidthMM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+904(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+904(%rip) +2: + pushq $113 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDoesBackingStore + .p2align 4 + .type XDoesBackingStore, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDoesBackingStore +#endif +XDoesBackingStore: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+912(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+912(%rip) +2: + pushq $114 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDoesSaveUnders + .p2align 4 + .type XDoesSaveUnders, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDoesSaveUnders +#endif +XDoesSaveUnders: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+920(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+920(%rip) +2: + pushq $115 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawArc + .p2align 4 + .type XDrawArc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawArc +#endif +XDrawArc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+928(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+928(%rip) +2: + pushq $116 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawArcs + .p2align 4 + .type XDrawArcs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawArcs +#endif +XDrawArcs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+936(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+936(%rip) +2: + pushq $117 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawImageString + .p2align 4 + .type XDrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawImageString +#endif +XDrawImageString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+944(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+944(%rip) +2: + pushq $118 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawImageString16 + .p2align 4 + .type XDrawImageString16, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawImageString16 +#endif +XDrawImageString16: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+952(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+952(%rip) +2: + pushq $119 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawLine + .p2align 4 + .type XDrawLine, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawLine +#endif +XDrawLine: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+960(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+960(%rip) +2: + pushq $120 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawLines + .p2align 4 + .type XDrawLines, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawLines +#endif +XDrawLines: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+968(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+968(%rip) +2: + pushq $121 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawPoint + .p2align 4 + .type XDrawPoint, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawPoint +#endif +XDrawPoint: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+976(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+976(%rip) +2: + pushq $122 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawPoints + .p2align 4 + .type XDrawPoints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawPoints +#endif +XDrawPoints: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+984(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+984(%rip) +2: + pushq $123 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawRectangle + .p2align 4 + .type XDrawRectangle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawRectangle +#endif +XDrawRectangle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+992(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+992(%rip) +2: + pushq $124 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawRectangles + .p2align 4 + .type XDrawRectangles, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawRectangles +#endif +XDrawRectangles: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1000(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1000(%rip) +2: + pushq $125 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawSegments + .p2align 4 + .type XDrawSegments, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawSegments +#endif +XDrawSegments: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1008(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1008(%rip) +2: + pushq $126 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawString + .p2align 4 + .type XDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawString +#endif +XDrawString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1016(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1016(%rip) +2: + pushq $127 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawString16 + .p2align 4 + .type XDrawString16, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawString16 +#endif +XDrawString16: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1024(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1024(%rip) +2: + pushq $128 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawText + .p2align 4 + .type XDrawText, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawText +#endif +XDrawText: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1032(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1032(%rip) +2: + pushq $129 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDrawText16 + .p2align 4 + .type XDrawText16, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDrawText16 +#endif +XDrawText16: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1040(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1040(%rip) +2: + pushq $130 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XEHeadOfExtensionList + .p2align 4 + .type XEHeadOfExtensionList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XEHeadOfExtensionList +#endif +XEHeadOfExtensionList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1048(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1048(%rip) +2: + pushq $131 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetBeforeFlush + .p2align 4 + .type XESetBeforeFlush, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetBeforeFlush +#endif +XESetBeforeFlush: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1056(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1056(%rip) +2: + pushq $132 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetCloseDisplay + .p2align 4 + .type XESetCloseDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetCloseDisplay +#endif +XESetCloseDisplay: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1064(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1064(%rip) +2: + pushq $133 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetCopyEventCookie + .p2align 4 + .type XESetCopyEventCookie, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetCopyEventCookie +#endif +XESetCopyEventCookie: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1072(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1072(%rip) +2: + pushq $134 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetCopyGC + .p2align 4 + .type XESetCopyGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetCopyGC +#endif +XESetCopyGC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1080(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1080(%rip) +2: + pushq $135 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetCreateFont + .p2align 4 + .type XESetCreateFont, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetCreateFont +#endif +XESetCreateFont: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1088(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1088(%rip) +2: + pushq $136 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetCreateGC + .p2align 4 + .type XESetCreateGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetCreateGC +#endif +XESetCreateGC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1096(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1096(%rip) +2: + pushq $137 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetError + .p2align 4 + .type XESetError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetError +#endif +XESetError: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1104(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1104(%rip) +2: + pushq $138 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetErrorString + .p2align 4 + .type XESetErrorString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetErrorString +#endif +XESetErrorString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1112(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1112(%rip) +2: + pushq $139 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetEventToWire + .p2align 4 + .type XESetEventToWire, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetEventToWire +#endif +XESetEventToWire: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1120(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1120(%rip) +2: + pushq $140 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetFlushGC + .p2align 4 + .type XESetFlushGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetFlushGC +#endif +XESetFlushGC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1128(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1128(%rip) +2: + pushq $141 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetFreeFont + .p2align 4 + .type XESetFreeFont, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetFreeFont +#endif +XESetFreeFont: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1136(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1136(%rip) +2: + pushq $142 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetFreeGC + .p2align 4 + .type XESetFreeGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetFreeGC +#endif +XESetFreeGC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1144(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1144(%rip) +2: + pushq $143 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetPrintErrorValues + .p2align 4 + .type XESetPrintErrorValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetPrintErrorValues +#endif +XESetPrintErrorValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1152(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1152(%rip) +2: + pushq $144 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetWireToError + .p2align 4 + .type XESetWireToError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetWireToError +#endif +XESetWireToError: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1160(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1160(%rip) +2: + pushq $145 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetWireToEvent + .p2align 4 + .type XESetWireToEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetWireToEvent +#endif +XESetWireToEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1168(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1168(%rip) +2: + pushq $146 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XESetWireToEventCookie + .p2align 4 + .type XESetWireToEventCookie, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XESetWireToEventCookie +#endif +XESetWireToEventCookie: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1176(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1176(%rip) +2: + pushq $147 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XEmptyRegion + .p2align 4 + .type XEmptyRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XEmptyRegion +#endif +XEmptyRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1184(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1184(%rip) +2: + pushq $148 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XEnableAccessControl + .p2align 4 + .type XEnableAccessControl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XEnableAccessControl +#endif +XEnableAccessControl: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1192(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1192(%rip) +2: + pushq $149 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XEqualRegion + .p2align 4 + .type XEqualRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XEqualRegion +#endif +XEqualRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1200(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1200(%rip) +2: + pushq $150 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XEventMaskOfScreen + .p2align 4 + .type XEventMaskOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XEventMaskOfScreen +#endif +XEventMaskOfScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1208(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1208(%rip) +2: + pushq $151 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XEventsQueued + .p2align 4 + .type XEventsQueued, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XEventsQueued +#endif +XEventsQueued: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1216(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1216(%rip) +2: + pushq $152 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XExtendedMaxRequestSize + .p2align 4 + .type XExtendedMaxRequestSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XExtendedMaxRequestSize +#endif +XExtendedMaxRequestSize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1224(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1224(%rip) +2: + pushq $153 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XExtentsOfFontSet + .p2align 4 + .type XExtentsOfFontSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XExtentsOfFontSet +#endif +XExtentsOfFontSet: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1232(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1232(%rip) +2: + pushq $154 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFetchBuffer + .p2align 4 + .type XFetchBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFetchBuffer +#endif +XFetchBuffer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1240(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1240(%rip) +2: + pushq $155 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFetchBytes + .p2align 4 + .type XFetchBytes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFetchBytes +#endif +XFetchBytes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1248(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1248(%rip) +2: + pushq $156 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFetchName + .p2align 4 + .type XFetchName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFetchName +#endif +XFetchName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1256(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1256(%rip) +2: + pushq $157 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFillArc + .p2align 4 + .type XFillArc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFillArc +#endif +XFillArc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1264(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1264(%rip) +2: + pushq $158 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFillArcs + .p2align 4 + .type XFillArcs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFillArcs +#endif +XFillArcs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1272(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1272(%rip) +2: + pushq $159 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFillPolygon + .p2align 4 + .type XFillPolygon, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFillPolygon +#endif +XFillPolygon: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1280(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1280(%rip) +2: + pushq $160 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFillRectangle + .p2align 4 + .type XFillRectangle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFillRectangle +#endif +XFillRectangle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1288(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1288(%rip) +2: + pushq $161 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFillRectangles + .p2align 4 + .type XFillRectangles, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFillRectangles +#endif +XFillRectangles: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1296(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1296(%rip) +2: + pushq $162 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFilterEvent + .p2align 4 + .type XFilterEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFilterEvent +#endif +XFilterEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1304(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1304(%rip) +2: + pushq $163 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFindContext + .p2align 4 + .type XFindContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFindContext +#endif +XFindContext: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1312(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1312(%rip) +2: + pushq $164 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFindOnExtensionList + .p2align 4 + .type XFindOnExtensionList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFindOnExtensionList +#endif +XFindOnExtensionList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1320(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1320(%rip) +2: + pushq $165 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFlush + .p2align 4 + .type XFlush, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFlush +#endif +XFlush: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1328(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1328(%rip) +2: + pushq $166 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFlushGC + .p2align 4 + .type XFlushGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFlushGC +#endif +XFlushGC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1336(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1336(%rip) +2: + pushq $167 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFontsOfFontSet + .p2align 4 + .type XFontsOfFontSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFontsOfFontSet +#endif +XFontsOfFontSet: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1344(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1344(%rip) +2: + pushq $168 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XForceScreenSaver + .p2align 4 + .type XForceScreenSaver, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XForceScreenSaver +#endif +XForceScreenSaver: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1352(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1352(%rip) +2: + pushq $169 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFree + .p2align 4 + .type XFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFree +#endif +XFree: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1360(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1360(%rip) +2: + pushq $170 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeColormap + .p2align 4 + .type XFreeColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeColormap +#endif +XFreeColormap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1368(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1368(%rip) +2: + pushq $171 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeColors + .p2align 4 + .type XFreeColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeColors +#endif +XFreeColors: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1376(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1376(%rip) +2: + pushq $172 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeCursor + .p2align 4 + .type XFreeCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeCursor +#endif +XFreeCursor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1384(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1384(%rip) +2: + pushq $173 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeEventData + .p2align 4 + .type XFreeEventData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeEventData +#endif +XFreeEventData: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1392(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1392(%rip) +2: + pushq $174 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeExtensionList + .p2align 4 + .type XFreeExtensionList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeExtensionList +#endif +XFreeExtensionList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1400(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1400(%rip) +2: + pushq $175 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeFont + .p2align 4 + .type XFreeFont, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeFont +#endif +XFreeFont: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1408(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1408(%rip) +2: + pushq $176 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeFontInfo + .p2align 4 + .type XFreeFontInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeFontInfo +#endif +XFreeFontInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1416(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1416(%rip) +2: + pushq $177 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeFontNames + .p2align 4 + .type XFreeFontNames, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeFontNames +#endif +XFreeFontNames: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1424(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1424(%rip) +2: + pushq $178 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeFontPath + .p2align 4 + .type XFreeFontPath, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeFontPath +#endif +XFreeFontPath: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1432(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1432(%rip) +2: + pushq $179 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeFontSet + .p2align 4 + .type XFreeFontSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeFontSet +#endif +XFreeFontSet: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1440(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1440(%rip) +2: + pushq $180 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeGC + .p2align 4 + .type XFreeGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeGC +#endif +XFreeGC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1448(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1448(%rip) +2: + pushq $181 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeModifiermap + .p2align 4 + .type XFreeModifiermap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeModifiermap +#endif +XFreeModifiermap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1456(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1456(%rip) +2: + pushq $182 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreePixmap + .p2align 4 + .type XFreePixmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreePixmap +#endif +XFreePixmap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1464(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1464(%rip) +2: + pushq $183 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeStringList + .p2align 4 + .type XFreeStringList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeStringList +#endif +XFreeStringList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1472(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1472(%rip) +2: + pushq $184 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFreeThreads + .p2align 4 + .type XFreeThreads, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFreeThreads +#endif +XFreeThreads: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1480(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1480(%rip) +2: + pushq $185 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGContextFromGC + .p2align 4 + .type XGContextFromGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGContextFromGC +#endif +XGContextFromGC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1488(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1488(%rip) +2: + pushq $186 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGeometry + .p2align 4 + .type XGeometry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGeometry +#endif +XGeometry: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1496(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1496(%rip) +2: + pushq $187 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetAtomName + .p2align 4 + .type XGetAtomName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetAtomName +#endif +XGetAtomName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1504(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1504(%rip) +2: + pushq $188 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetAtomNames + .p2align 4 + .type XGetAtomNames, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetAtomNames +#endif +XGetAtomNames: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1512(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1512(%rip) +2: + pushq $189 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetClassHint + .p2align 4 + .type XGetClassHint, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetClassHint +#endif +XGetClassHint: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1520(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1520(%rip) +2: + pushq $190 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetCommand + .p2align 4 + .type XGetCommand, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetCommand +#endif +XGetCommand: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1528(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1528(%rip) +2: + pushq $191 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetDefault + .p2align 4 + .type XGetDefault, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetDefault +#endif +XGetDefault: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1536(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1536(%rip) +2: + pushq $192 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetErrorDatabaseText + .p2align 4 + .type XGetErrorDatabaseText, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetErrorDatabaseText +#endif +XGetErrorDatabaseText: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1544(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1544(%rip) +2: + pushq $193 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetErrorText + .p2align 4 + .type XGetErrorText, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetErrorText +#endif +XGetErrorText: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1552(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1552(%rip) +2: + pushq $194 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetEventData + .p2align 4 + .type XGetEventData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetEventData +#endif +XGetEventData: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1560(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1560(%rip) +2: + pushq $195 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetFontPath + .p2align 4 + .type XGetFontPath, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetFontPath +#endif +XGetFontPath: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1568(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1568(%rip) +2: + pushq $196 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetFontProperty + .p2align 4 + .type XGetFontProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetFontProperty +#endif +XGetFontProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1576(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1576(%rip) +2: + pushq $197 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetGCValues + .p2align 4 + .type XGetGCValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetGCValues +#endif +XGetGCValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1584(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1584(%rip) +2: + pushq $198 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetGeometry + .p2align 4 + .type XGetGeometry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetGeometry +#endif +XGetGeometry: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1592(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1592(%rip) +2: + pushq $199 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetICValues + .p2align 4 + .type XGetICValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetICValues +#endif +XGetICValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1600(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1600(%rip) +2: + pushq $200 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetIMValues + .p2align 4 + .type XGetIMValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetIMValues +#endif +XGetIMValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1608(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1608(%rip) +2: + pushq $201 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetIconName + .p2align 4 + .type XGetIconName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetIconName +#endif +XGetIconName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1616(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1616(%rip) +2: + pushq $202 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetIconSizes + .p2align 4 + .type XGetIconSizes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetIconSizes +#endif +XGetIconSizes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1624(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1624(%rip) +2: + pushq $203 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetImage + .p2align 4 + .type XGetImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetImage +#endif +XGetImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1632(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1632(%rip) +2: + pushq $204 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetInputFocus + .p2align 4 + .type XGetInputFocus, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetInputFocus +#endif +XGetInputFocus: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1640(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1640(%rip) +2: + pushq $205 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetKeyboardControl + .p2align 4 + .type XGetKeyboardControl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetKeyboardControl +#endif +XGetKeyboardControl: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1648(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1648(%rip) +2: + pushq $206 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetKeyboardMapping + .p2align 4 + .type XGetKeyboardMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetKeyboardMapping +#endif +XGetKeyboardMapping: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1656(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1656(%rip) +2: + pushq $207 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetModifierMapping + .p2align 4 + .type XGetModifierMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetModifierMapping +#endif +XGetModifierMapping: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1664(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1664(%rip) +2: + pushq $208 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetMotionEvents + .p2align 4 + .type XGetMotionEvents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetMotionEvents +#endif +XGetMotionEvents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1672(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1672(%rip) +2: + pushq $209 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetNormalHints + .p2align 4 + .type XGetNormalHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetNormalHints +#endif +XGetNormalHints: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1680(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1680(%rip) +2: + pushq $210 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetOCValues + .p2align 4 + .type XGetOCValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetOCValues +#endif +XGetOCValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1688(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1688(%rip) +2: + pushq $211 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetOMValues + .p2align 4 + .type XGetOMValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetOMValues +#endif +XGetOMValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1696(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1696(%rip) +2: + pushq $212 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetPixel + .p2align 4 + .type XGetPixel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetPixel +#endif +XGetPixel: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1704(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1704(%rip) +2: + pushq $213 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetPointerControl + .p2align 4 + .type XGetPointerControl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetPointerControl +#endif +XGetPointerControl: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1712(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1712(%rip) +2: + pushq $214 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetPointerMapping + .p2align 4 + .type XGetPointerMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetPointerMapping +#endif +XGetPointerMapping: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1720(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1720(%rip) +2: + pushq $215 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetRGBColormaps + .p2align 4 + .type XGetRGBColormaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetRGBColormaps +#endif +XGetRGBColormaps: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1728(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1728(%rip) +2: + pushq $216 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetScreenSaver + .p2align 4 + .type XGetScreenSaver, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetScreenSaver +#endif +XGetScreenSaver: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1736(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1736(%rip) +2: + pushq $217 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetSelectionOwner + .p2align 4 + .type XGetSelectionOwner, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetSelectionOwner +#endif +XGetSelectionOwner: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1744(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1744(%rip) +2: + pushq $218 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetSizeHints + .p2align 4 + .type XGetSizeHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetSizeHints +#endif +XGetSizeHints: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1752(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1752(%rip) +2: + pushq $219 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetStandardColormap + .p2align 4 + .type XGetStandardColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetStandardColormap +#endif +XGetStandardColormap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1760(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1760(%rip) +2: + pushq $220 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetSubImage + .p2align 4 + .type XGetSubImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetSubImage +#endif +XGetSubImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1768(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1768(%rip) +2: + pushq $221 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetTextProperty + .p2align 4 + .type XGetTextProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetTextProperty +#endif +XGetTextProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1776(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1776(%rip) +2: + pushq $222 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetTransientForHint + .p2align 4 + .type XGetTransientForHint, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetTransientForHint +#endif +XGetTransientForHint: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1784(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1784(%rip) +2: + pushq $223 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetVisualInfo + .p2align 4 + .type XGetVisualInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetVisualInfo +#endif +XGetVisualInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1792(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1792(%rip) +2: + pushq $224 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWMClientMachine + .p2align 4 + .type XGetWMClientMachine, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWMClientMachine +#endif +XGetWMClientMachine: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1800(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1800(%rip) +2: + pushq $225 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWMColormapWindows + .p2align 4 + .type XGetWMColormapWindows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWMColormapWindows +#endif +XGetWMColormapWindows: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1808(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1808(%rip) +2: + pushq $226 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWMHints + .p2align 4 + .type XGetWMHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWMHints +#endif +XGetWMHints: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1816(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1816(%rip) +2: + pushq $227 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWMIconName + .p2align 4 + .type XGetWMIconName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWMIconName +#endif +XGetWMIconName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1824(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1824(%rip) +2: + pushq $228 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWMName + .p2align 4 + .type XGetWMName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWMName +#endif +XGetWMName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1832(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1832(%rip) +2: + pushq $229 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWMNormalHints + .p2align 4 + .type XGetWMNormalHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWMNormalHints +#endif +XGetWMNormalHints: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1840(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1840(%rip) +2: + pushq $230 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWMProtocols + .p2align 4 + .type XGetWMProtocols, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWMProtocols +#endif +XGetWMProtocols: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1848(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1848(%rip) +2: + pushq $231 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWMSizeHints + .p2align 4 + .type XGetWMSizeHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWMSizeHints +#endif +XGetWMSizeHints: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1856(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1856(%rip) +2: + pushq $232 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWindowAttributes + .p2align 4 + .type XGetWindowAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWindowAttributes +#endif +XGetWindowAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1864(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1864(%rip) +2: + pushq $233 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetWindowProperty + .p2align 4 + .type XGetWindowProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetWindowProperty +#endif +XGetWindowProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1872(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1872(%rip) +2: + pushq $234 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGetZoomHints + .p2align 4 + .type XGetZoomHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGetZoomHints +#endif +XGetZoomHints: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1880(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1880(%rip) +2: + pushq $235 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGrabButton + .p2align 4 + .type XGrabButton, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGrabButton +#endif +XGrabButton: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1888(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1888(%rip) +2: + pushq $236 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGrabKey + .p2align 4 + .type XGrabKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGrabKey +#endif +XGrabKey: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1896(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1896(%rip) +2: + pushq $237 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGrabKeyboard + .p2align 4 + .type XGrabKeyboard, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGrabKeyboard +#endif +XGrabKeyboard: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1904(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1904(%rip) +2: + pushq $238 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGrabPointer + .p2align 4 + .type XGrabPointer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGrabPointer +#endif +XGrabPointer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1912(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1912(%rip) +2: + pushq $239 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGrabServer + .p2align 4 + .type XGrabServer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGrabServer +#endif +XGrabServer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1920(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1920(%rip) +2: + pushq $240 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XHeightMMOfScreen + .p2align 4 + .type XHeightMMOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XHeightMMOfScreen +#endif +XHeightMMOfScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1928(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1928(%rip) +2: + pushq $241 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XHeightOfScreen + .p2align 4 + .type XHeightOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XHeightOfScreen +#endif +XHeightOfScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1936(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1936(%rip) +2: + pushq $242 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XIMOfIC + .p2align 4 + .type XIMOfIC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XIMOfIC +#endif +XIMOfIC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1944(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1944(%rip) +2: + pushq $243 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XIconifyWindow + .p2align 4 + .type XIconifyWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XIconifyWindow +#endif +XIconifyWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1952(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1952(%rip) +2: + pushq $244 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XIfEvent + .p2align 4 + .type XIfEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XIfEvent +#endif +XIfEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1960(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1960(%rip) +2: + pushq $245 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XImageByteOrder + .p2align 4 + .type XImageByteOrder, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XImageByteOrder +#endif +XImageByteOrder: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1968(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1968(%rip) +2: + pushq $246 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XInitExtension + .p2align 4 + .type XInitExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XInitExtension +#endif +XInitExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1976(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1976(%rip) +2: + pushq $247 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XInitImage + .p2align 4 + .type XInitImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XInitImage +#endif +XInitImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1984(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1984(%rip) +2: + pushq $248 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XInitThreads + .p2align 4 + .type XInitThreads, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XInitThreads +#endif +XInitThreads: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+1992(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+1992(%rip) +2: + pushq $249 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XInsertModifiermapEntry + .p2align 4 + .type XInsertModifiermapEntry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XInsertModifiermapEntry +#endif +XInsertModifiermapEntry: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2000(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2000(%rip) +2: + pushq $250 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XInstallColormap + .p2align 4 + .type XInstallColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XInstallColormap +#endif +XInstallColormap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2008(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2008(%rip) +2: + pushq $251 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XInternAtom + .p2align 4 + .type XInternAtom, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XInternAtom +#endif +XInternAtom: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2016(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2016(%rip) +2: + pushq $252 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XInternAtoms + .p2align 4 + .type XInternAtoms, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XInternAtoms +#endif +XInternAtoms: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2024(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2024(%rip) +2: + pushq $253 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XInternalConnectionNumbers + .p2align 4 + .type XInternalConnectionNumbers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XInternalConnectionNumbers +#endif +XInternalConnectionNumbers: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2032(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2032(%rip) +2: + pushq $254 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XIntersectRegion + .p2align 4 + .type XIntersectRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XIntersectRegion +#endif +XIntersectRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2040(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2040(%rip) +2: + pushq $255 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XKeycodeToKeysym + .p2align 4 + .type XKeycodeToKeysym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XKeycodeToKeysym +#endif +XKeycodeToKeysym: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2048(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2048(%rip) +2: + pushq $256 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XKeysymToKeycode + .p2align 4 + .type XKeysymToKeycode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XKeysymToKeycode +#endif +XKeysymToKeycode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2056(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2056(%rip) +2: + pushq $257 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XKeysymToString + .p2align 4 + .type XKeysymToString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XKeysymToString +#endif +XKeysymToString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2064(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2064(%rip) +2: + pushq $258 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XKillClient + .p2align 4 + .type XKillClient, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XKillClient +#endif +XKillClient: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2072(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2072(%rip) +2: + pushq $259 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLastKnownRequestProcessed + .p2align 4 + .type XLastKnownRequestProcessed, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLastKnownRequestProcessed +#endif +XLastKnownRequestProcessed: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2080(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2080(%rip) +2: + pushq $260 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XListDepths + .p2align 4 + .type XListDepths, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XListDepths +#endif +XListDepths: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2088(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2088(%rip) +2: + pushq $261 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XListExtensions + .p2align 4 + .type XListExtensions, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XListExtensions +#endif +XListExtensions: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2096(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2096(%rip) +2: + pushq $262 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XListFonts + .p2align 4 + .type XListFonts, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XListFonts +#endif +XListFonts: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2104(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2104(%rip) +2: + pushq $263 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XListFontsWithInfo + .p2align 4 + .type XListFontsWithInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XListFontsWithInfo +#endif +XListFontsWithInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2112(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2112(%rip) +2: + pushq $264 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XListHosts + .p2align 4 + .type XListHosts, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XListHosts +#endif +XListHosts: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2120(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2120(%rip) +2: + pushq $265 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XListInstalledColormaps + .p2align 4 + .type XListInstalledColormaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XListInstalledColormaps +#endif +XListInstalledColormaps: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2128(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2128(%rip) +2: + pushq $266 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XListPixmapFormats + .p2align 4 + .type XListPixmapFormats, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XListPixmapFormats +#endif +XListPixmapFormats: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2136(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2136(%rip) +2: + pushq $267 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XListProperties + .p2align 4 + .type XListProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XListProperties +#endif +XListProperties: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2144(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2144(%rip) +2: + pushq $268 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLoadFont + .p2align 4 + .type XLoadFont, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLoadFont +#endif +XLoadFont: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2152(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2152(%rip) +2: + pushq $269 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLoadQueryFont + .p2align 4 + .type XLoadQueryFont, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLoadQueryFont +#endif +XLoadQueryFont: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2160(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2160(%rip) +2: + pushq $270 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLocaleOfFontSet + .p2align 4 + .type XLocaleOfFontSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLocaleOfFontSet +#endif +XLocaleOfFontSet: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2168(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2168(%rip) +2: + pushq $271 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLocaleOfIM + .p2align 4 + .type XLocaleOfIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLocaleOfIM +#endif +XLocaleOfIM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2176(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2176(%rip) +2: + pushq $272 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLocaleOfOM + .p2align 4 + .type XLocaleOfOM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLocaleOfOM +#endif +XLocaleOfOM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2184(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2184(%rip) +2: + pushq $273 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLockDisplay + .p2align 4 + .type XLockDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLockDisplay +#endif +XLockDisplay: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2192(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2192(%rip) +2: + pushq $274 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLookupColor + .p2align 4 + .type XLookupColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLookupColor +#endif +XLookupColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2200(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2200(%rip) +2: + pushq $275 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLookupKeysym + .p2align 4 + .type XLookupKeysym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLookupKeysym +#endif +XLookupKeysym: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2208(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2208(%rip) +2: + pushq $276 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLookupString + .p2align 4 + .type XLookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLookupString +#endif +XLookupString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2216(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2216(%rip) +2: + pushq $277 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLowerWindow + .p2align 4 + .type XLowerWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLowerWindow +#endif +XLowerWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2224(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2224(%rip) +2: + pushq $278 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMapRaised + .p2align 4 + .type XMapRaised, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMapRaised +#endif +XMapRaised: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2232(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2232(%rip) +2: + pushq $279 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMapSubwindows + .p2align 4 + .type XMapSubwindows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMapSubwindows +#endif +XMapSubwindows: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2240(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2240(%rip) +2: + pushq $280 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMapWindow + .p2align 4 + .type XMapWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMapWindow +#endif +XMapWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2248(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2248(%rip) +2: + pushq $281 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMaskEvent + .p2align 4 + .type XMaskEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMaskEvent +#endif +XMaskEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2256(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2256(%rip) +2: + pushq $282 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMatchVisualInfo + .p2align 4 + .type XMatchVisualInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMatchVisualInfo +#endif +XMatchVisualInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2264(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2264(%rip) +2: + pushq $283 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMaxCmapsOfScreen + .p2align 4 + .type XMaxCmapsOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMaxCmapsOfScreen +#endif +XMaxCmapsOfScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2272(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2272(%rip) +2: + pushq $284 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMaxRequestSize + .p2align 4 + .type XMaxRequestSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMaxRequestSize +#endif +XMaxRequestSize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2280(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2280(%rip) +2: + pushq $285 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMinCmapsOfScreen + .p2align 4 + .type XMinCmapsOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMinCmapsOfScreen +#endif +XMinCmapsOfScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2288(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2288(%rip) +2: + pushq $286 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMoveResizeWindow + .p2align 4 + .type XMoveResizeWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMoveResizeWindow +#endif +XMoveResizeWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2296(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2296(%rip) +2: + pushq $287 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMoveWindow + .p2align 4 + .type XMoveWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMoveWindow +#endif +XMoveWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2304(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2304(%rip) +2: + pushq $288 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XNewModifiermap + .p2align 4 + .type XNewModifiermap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XNewModifiermap +#endif +XNewModifiermap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2312(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2312(%rip) +2: + pushq $289 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XNextEvent + .p2align 4 + .type XNextEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XNextEvent +#endif +XNextEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2320(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2320(%rip) +2: + pushq $290 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XNextRequest + .p2align 4 + .type XNextRequest, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XNextRequest +#endif +XNextRequest: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2328(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2328(%rip) +2: + pushq $291 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XNoOp + .p2align 4 + .type XNoOp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XNoOp +#endif +XNoOp: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2336(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2336(%rip) +2: + pushq $292 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XOMOfOC + .p2align 4 + .type XOMOfOC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XOMOfOC +#endif +XOMOfOC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2344(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2344(%rip) +2: + pushq $293 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XOffsetRegion + .p2align 4 + .type XOffsetRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XOffsetRegion +#endif +XOffsetRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2352(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2352(%rip) +2: + pushq $294 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XOpenDisplay + .p2align 4 + .type XOpenDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XOpenDisplay +#endif +XOpenDisplay: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2360(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2360(%rip) +2: + pushq $295 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XOpenIM + .p2align 4 + .type XOpenIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XOpenIM +#endif +XOpenIM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2368(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2368(%rip) +2: + pushq $296 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XOpenOM + .p2align 4 + .type XOpenOM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XOpenOM +#endif +XOpenOM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2376(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2376(%rip) +2: + pushq $297 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XParseColor + .p2align 4 + .type XParseColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XParseColor +#endif +XParseColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2384(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2384(%rip) +2: + pushq $298 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XParseGeometry + .p2align 4 + .type XParseGeometry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XParseGeometry +#endif +XParseGeometry: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2392(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2392(%rip) +2: + pushq $299 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XPeekEvent + .p2align 4 + .type XPeekEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XPeekEvent +#endif +XPeekEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2400(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2400(%rip) +2: + pushq $300 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XPeekIfEvent + .p2align 4 + .type XPeekIfEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XPeekIfEvent +#endif +XPeekIfEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2408(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2408(%rip) +2: + pushq $301 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XPending + .p2align 4 + .type XPending, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XPending +#endif +XPending: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2416(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2416(%rip) +2: + pushq $302 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XPlanesOfScreen + .p2align 4 + .type XPlanesOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XPlanesOfScreen +#endif +XPlanesOfScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2424(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2424(%rip) +2: + pushq $303 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XPointInRegion + .p2align 4 + .type XPointInRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XPointInRegion +#endif +XPointInRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2432(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2432(%rip) +2: + pushq $304 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XPolygonRegion + .p2align 4 + .type XPolygonRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XPolygonRegion +#endif +XPolygonRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2440(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2440(%rip) +2: + pushq $305 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XProcessInternalConnection + .p2align 4 + .type XProcessInternalConnection, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XProcessInternalConnection +#endif +XProcessInternalConnection: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2448(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2448(%rip) +2: + pushq $306 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XProtocolRevision + .p2align 4 + .type XProtocolRevision, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XProtocolRevision +#endif +XProtocolRevision: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2456(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2456(%rip) +2: + pushq $307 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XProtocolVersion + .p2align 4 + .type XProtocolVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XProtocolVersion +#endif +XProtocolVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2464(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2464(%rip) +2: + pushq $308 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XPutBackEvent + .p2align 4 + .type XPutBackEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XPutBackEvent +#endif +XPutBackEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2472(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2472(%rip) +2: + pushq $309 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XPutImage + .p2align 4 + .type XPutImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XPutImage +#endif +XPutImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2480(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2480(%rip) +2: + pushq $310 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XPutPixel + .p2align 4 + .type XPutPixel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XPutPixel +#endif +XPutPixel: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2488(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2488(%rip) +2: + pushq $311 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQLength + .p2align 4 + .type XQLength, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQLength +#endif +XQLength: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2496(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2496(%rip) +2: + pushq $312 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryBestCursor + .p2align 4 + .type XQueryBestCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryBestCursor +#endif +XQueryBestCursor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2504(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2504(%rip) +2: + pushq $313 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryBestSize + .p2align 4 + .type XQueryBestSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryBestSize +#endif +XQueryBestSize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2512(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2512(%rip) +2: + pushq $314 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryBestStipple + .p2align 4 + .type XQueryBestStipple, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryBestStipple +#endif +XQueryBestStipple: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2520(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2520(%rip) +2: + pushq $315 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryBestTile + .p2align 4 + .type XQueryBestTile, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryBestTile +#endif +XQueryBestTile: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2528(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2528(%rip) +2: + pushq $316 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryColor + .p2align 4 + .type XQueryColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryColor +#endif +XQueryColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2536(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2536(%rip) +2: + pushq $317 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryColors + .p2align 4 + .type XQueryColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryColors +#endif +XQueryColors: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2544(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2544(%rip) +2: + pushq $318 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryExtension + .p2align 4 + .type XQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryExtension +#endif +XQueryExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2552(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2552(%rip) +2: + pushq $319 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryFont + .p2align 4 + .type XQueryFont, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryFont +#endif +XQueryFont: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2560(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2560(%rip) +2: + pushq $320 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryKeymap + .p2align 4 + .type XQueryKeymap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryKeymap +#endif +XQueryKeymap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2568(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2568(%rip) +2: + pushq $321 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryPointer + .p2align 4 + .type XQueryPointer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryPointer +#endif +XQueryPointer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2576(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2576(%rip) +2: + pushq $322 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryTextExtents + .p2align 4 + .type XQueryTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryTextExtents +#endif +XQueryTextExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2584(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2584(%rip) +2: + pushq $323 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryTextExtents16 + .p2align 4 + .type XQueryTextExtents16, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryTextExtents16 +#endif +XQueryTextExtents16: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2592(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2592(%rip) +2: + pushq $324 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XQueryTree + .p2align 4 + .type XQueryTree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XQueryTree +#endif +XQueryTree: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2600(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2600(%rip) +2: + pushq $325 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRaiseWindow + .p2align 4 + .type XRaiseWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRaiseWindow +#endif +XRaiseWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2608(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2608(%rip) +2: + pushq $326 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XReadBitmapFile + .p2align 4 + .type XReadBitmapFile, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XReadBitmapFile +#endif +XReadBitmapFile: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2616(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2616(%rip) +2: + pushq $327 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XReadBitmapFileData + .p2align 4 + .type XReadBitmapFileData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XReadBitmapFileData +#endif +XReadBitmapFileData: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2624(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2624(%rip) +2: + pushq $328 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRebindKeysym + .p2align 4 + .type XRebindKeysym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRebindKeysym +#endif +XRebindKeysym: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2632(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2632(%rip) +2: + pushq $329 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRecolorCursor + .p2align 4 + .type XRecolorCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRecolorCursor +#endif +XRecolorCursor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2640(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2640(%rip) +2: + pushq $330 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XReconfigureWMWindow + .p2align 4 + .type XReconfigureWMWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XReconfigureWMWindow +#endif +XReconfigureWMWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2648(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2648(%rip) +2: + pushq $331 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRectInRegion + .p2align 4 + .type XRectInRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRectInRegion +#endif +XRectInRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2656(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2656(%rip) +2: + pushq $332 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRefreshKeyboardMapping + .p2align 4 + .type XRefreshKeyboardMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRefreshKeyboardMapping +#endif +XRefreshKeyboardMapping: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2664(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2664(%rip) +2: + pushq $333 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRegisterIMInstantiateCallback + .p2align 4 + .type XRegisterIMInstantiateCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRegisterIMInstantiateCallback +#endif +XRegisterIMInstantiateCallback: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2672(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2672(%rip) +2: + pushq $334 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRemoveConnectionWatch + .p2align 4 + .type XRemoveConnectionWatch, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRemoveConnectionWatch +#endif +XRemoveConnectionWatch: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2680(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2680(%rip) +2: + pushq $335 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRemoveFromSaveSet + .p2align 4 + .type XRemoveFromSaveSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRemoveFromSaveSet +#endif +XRemoveFromSaveSet: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2688(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2688(%rip) +2: + pushq $336 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRemoveHost + .p2align 4 + .type XRemoveHost, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRemoveHost +#endif +XRemoveHost: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2696(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2696(%rip) +2: + pushq $337 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRemoveHosts + .p2align 4 + .type XRemoveHosts, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRemoveHosts +#endif +XRemoveHosts: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2704(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2704(%rip) +2: + pushq $338 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XReparentWindow + .p2align 4 + .type XReparentWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XReparentWindow +#endif +XReparentWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2712(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2712(%rip) +2: + pushq $339 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XResetScreenSaver + .p2align 4 + .type XResetScreenSaver, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XResetScreenSaver +#endif +XResetScreenSaver: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2720(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2720(%rip) +2: + pushq $340 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XResizeWindow + .p2align 4 + .type XResizeWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XResizeWindow +#endif +XResizeWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2728(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2728(%rip) +2: + pushq $341 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XResourceManagerString + .p2align 4 + .type XResourceManagerString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XResourceManagerString +#endif +XResourceManagerString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2736(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2736(%rip) +2: + pushq $342 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRestackWindows + .p2align 4 + .type XRestackWindows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRestackWindows +#endif +XRestackWindows: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2744(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2744(%rip) +2: + pushq $343 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRootWindow + .p2align 4 + .type XRootWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRootWindow +#endif +XRootWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2752(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2752(%rip) +2: + pushq $344 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRootWindowOfScreen + .p2align 4 + .type XRootWindowOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRootWindowOfScreen +#endif +XRootWindowOfScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2760(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2760(%rip) +2: + pushq $345 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRotateBuffers + .p2align 4 + .type XRotateBuffers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRotateBuffers +#endif +XRotateBuffers: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2768(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2768(%rip) +2: + pushq $346 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRotateWindowProperties + .p2align 4 + .type XRotateWindowProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRotateWindowProperties +#endif +XRotateWindowProperties: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2776(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2776(%rip) +2: + pushq $347 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSaveContext + .p2align 4 + .type XSaveContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSaveContext +#endif +XSaveContext: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2784(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2784(%rip) +2: + pushq $348 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XScreenCount + .p2align 4 + .type XScreenCount, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XScreenCount +#endif +XScreenCount: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2792(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2792(%rip) +2: + pushq $349 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XScreenNumberOfScreen + .p2align 4 + .type XScreenNumberOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XScreenNumberOfScreen +#endif +XScreenNumberOfScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2800(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2800(%rip) +2: + pushq $350 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XScreenOfDisplay + .p2align 4 + .type XScreenOfDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XScreenOfDisplay +#endif +XScreenOfDisplay: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2808(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2808(%rip) +2: + pushq $351 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XScreenResourceString + .p2align 4 + .type XScreenResourceString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XScreenResourceString +#endif +XScreenResourceString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2816(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2816(%rip) +2: + pushq $352 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSelectInput + .p2align 4 + .type XSelectInput, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSelectInput +#endif +XSelectInput: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2824(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2824(%rip) +2: + pushq $353 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSendEvent + .p2align 4 + .type XSendEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSendEvent +#endif +XSendEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2832(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2832(%rip) +2: + pushq $354 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XServerVendor + .p2align 4 + .type XServerVendor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XServerVendor +#endif +XServerVendor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2840(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2840(%rip) +2: + pushq $355 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetAccessControl + .p2align 4 + .type XSetAccessControl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetAccessControl +#endif +XSetAccessControl: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2848(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2848(%rip) +2: + pushq $356 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetAfterFunction + .p2align 4 + .type XSetAfterFunction, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetAfterFunction +#endif +XSetAfterFunction: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2856(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2856(%rip) +2: + pushq $357 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetArcMode + .p2align 4 + .type XSetArcMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetArcMode +#endif +XSetArcMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2864(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2864(%rip) +2: + pushq $358 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetAuthorization + .p2align 4 + .type XSetAuthorization, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetAuthorization +#endif +XSetAuthorization: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2872(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2872(%rip) +2: + pushq $359 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetBackground + .p2align 4 + .type XSetBackground, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetBackground +#endif +XSetBackground: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2880(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2880(%rip) +2: + pushq $360 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetClassHint + .p2align 4 + .type XSetClassHint, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetClassHint +#endif +XSetClassHint: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2888(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2888(%rip) +2: + pushq $361 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetClipMask + .p2align 4 + .type XSetClipMask, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetClipMask +#endif +XSetClipMask: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2896(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2896(%rip) +2: + pushq $362 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetClipOrigin + .p2align 4 + .type XSetClipOrigin, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetClipOrigin +#endif +XSetClipOrigin: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2904(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2904(%rip) +2: + pushq $363 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetClipRectangles + .p2align 4 + .type XSetClipRectangles, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetClipRectangles +#endif +XSetClipRectangles: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2912(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2912(%rip) +2: + pushq $364 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetCloseDownMode + .p2align 4 + .type XSetCloseDownMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetCloseDownMode +#endif +XSetCloseDownMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2920(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2920(%rip) +2: + pushq $365 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetCommand + .p2align 4 + .type XSetCommand, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetCommand +#endif +XSetCommand: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2928(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2928(%rip) +2: + pushq $366 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetDashes + .p2align 4 + .type XSetDashes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetDashes +#endif +XSetDashes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2936(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2936(%rip) +2: + pushq $367 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetErrorHandler + .p2align 4 + .type XSetErrorHandler, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetErrorHandler +#endif +XSetErrorHandler: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2944(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2944(%rip) +2: + pushq $368 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetFillRule + .p2align 4 + .type XSetFillRule, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetFillRule +#endif +XSetFillRule: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2952(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2952(%rip) +2: + pushq $369 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetFillStyle + .p2align 4 + .type XSetFillStyle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetFillStyle +#endif +XSetFillStyle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2960(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2960(%rip) +2: + pushq $370 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetFont + .p2align 4 + .type XSetFont, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetFont +#endif +XSetFont: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2968(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2968(%rip) +2: + pushq $371 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetFontPath + .p2align 4 + .type XSetFontPath, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetFontPath +#endif +XSetFontPath: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2976(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2976(%rip) +2: + pushq $372 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetForeground + .p2align 4 + .type XSetForeground, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetForeground +#endif +XSetForeground: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2984(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2984(%rip) +2: + pushq $373 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetFunction + .p2align 4 + .type XSetFunction, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetFunction +#endif +XSetFunction: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+2992(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+2992(%rip) +2: + pushq $374 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetGraphicsExposures + .p2align 4 + .type XSetGraphicsExposures, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetGraphicsExposures +#endif +XSetGraphicsExposures: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3000(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3000(%rip) +2: + pushq $375 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetICFocus + .p2align 4 + .type XSetICFocus, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetICFocus +#endif +XSetICFocus: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3008(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3008(%rip) +2: + pushq $376 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetICValues + .p2align 4 + .type XSetICValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetICValues +#endif +XSetICValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3016(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3016(%rip) +2: + pushq $377 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetIMValues + .p2align 4 + .type XSetIMValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetIMValues +#endif +XSetIMValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3024(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3024(%rip) +2: + pushq $378 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetIOErrorExitHandler + .p2align 4 + .type XSetIOErrorExitHandler, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetIOErrorExitHandler +#endif +XSetIOErrorExitHandler: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3032(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3032(%rip) +2: + pushq $379 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetIOErrorHandler + .p2align 4 + .type XSetIOErrorHandler, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetIOErrorHandler +#endif +XSetIOErrorHandler: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3040(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3040(%rip) +2: + pushq $380 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetIconName + .p2align 4 + .type XSetIconName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetIconName +#endif +XSetIconName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3048(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3048(%rip) +2: + pushq $381 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetIconSizes + .p2align 4 + .type XSetIconSizes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetIconSizes +#endif +XSetIconSizes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3056(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3056(%rip) +2: + pushq $382 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetInputFocus + .p2align 4 + .type XSetInputFocus, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetInputFocus +#endif +XSetInputFocus: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3064(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3064(%rip) +2: + pushq $383 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetLineAttributes + .p2align 4 + .type XSetLineAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetLineAttributes +#endif +XSetLineAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3072(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3072(%rip) +2: + pushq $384 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetLocaleModifiers + .p2align 4 + .type XSetLocaleModifiers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetLocaleModifiers +#endif +XSetLocaleModifiers: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3080(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3080(%rip) +2: + pushq $385 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetModifierMapping + .p2align 4 + .type XSetModifierMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetModifierMapping +#endif +XSetModifierMapping: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3088(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3088(%rip) +2: + pushq $386 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetNormalHints + .p2align 4 + .type XSetNormalHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetNormalHints +#endif +XSetNormalHints: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3096(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3096(%rip) +2: + pushq $387 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetOCValues + .p2align 4 + .type XSetOCValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetOCValues +#endif +XSetOCValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3104(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3104(%rip) +2: + pushq $388 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetOMValues + .p2align 4 + .type XSetOMValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetOMValues +#endif +XSetOMValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3112(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3112(%rip) +2: + pushq $389 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetPlaneMask + .p2align 4 + .type XSetPlaneMask, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetPlaneMask +#endif +XSetPlaneMask: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3120(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3120(%rip) +2: + pushq $390 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetPointerMapping + .p2align 4 + .type XSetPointerMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetPointerMapping +#endif +XSetPointerMapping: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3128(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3128(%rip) +2: + pushq $391 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetRGBColormaps + .p2align 4 + .type XSetRGBColormaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetRGBColormaps +#endif +XSetRGBColormaps: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3136(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3136(%rip) +2: + pushq $392 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetRegion + .p2align 4 + .type XSetRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetRegion +#endif +XSetRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3144(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3144(%rip) +2: + pushq $393 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetScreenSaver + .p2align 4 + .type XSetScreenSaver, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetScreenSaver +#endif +XSetScreenSaver: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3152(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3152(%rip) +2: + pushq $394 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetSelectionOwner + .p2align 4 + .type XSetSelectionOwner, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetSelectionOwner +#endif +XSetSelectionOwner: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3160(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3160(%rip) +2: + pushq $395 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetSizeHints + .p2align 4 + .type XSetSizeHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetSizeHints +#endif +XSetSizeHints: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3168(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3168(%rip) +2: + pushq $396 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetStandardColormap + .p2align 4 + .type XSetStandardColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetStandardColormap +#endif +XSetStandardColormap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3176(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3176(%rip) +2: + pushq $397 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetStandardProperties + .p2align 4 + .type XSetStandardProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetStandardProperties +#endif +XSetStandardProperties: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3184(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3184(%rip) +2: + pushq $398 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetState + .p2align 4 + .type XSetState, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetState +#endif +XSetState: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3192(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3192(%rip) +2: + pushq $399 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetStipple + .p2align 4 + .type XSetStipple, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetStipple +#endif +XSetStipple: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3200(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3200(%rip) +2: + pushq $400 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetSubwindowMode + .p2align 4 + .type XSetSubwindowMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetSubwindowMode +#endif +XSetSubwindowMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3208(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3208(%rip) +2: + pushq $401 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetTSOrigin + .p2align 4 + .type XSetTSOrigin, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetTSOrigin +#endif +XSetTSOrigin: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3216(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3216(%rip) +2: + pushq $402 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetTextProperty + .p2align 4 + .type XSetTextProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetTextProperty +#endif +XSetTextProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3224(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3224(%rip) +2: + pushq $403 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetTile + .p2align 4 + .type XSetTile, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetTile +#endif +XSetTile: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3232(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3232(%rip) +2: + pushq $404 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetTransientForHint + .p2align 4 + .type XSetTransientForHint, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetTransientForHint +#endif +XSetTransientForHint: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3240(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3240(%rip) +2: + pushq $405 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWMClientMachine + .p2align 4 + .type XSetWMClientMachine, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWMClientMachine +#endif +XSetWMClientMachine: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3248(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3248(%rip) +2: + pushq $406 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWMColormapWindows + .p2align 4 + .type XSetWMColormapWindows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWMColormapWindows +#endif +XSetWMColormapWindows: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3256(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3256(%rip) +2: + pushq $407 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWMHints + .p2align 4 + .type XSetWMHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWMHints +#endif +XSetWMHints: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3264(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3264(%rip) +2: + pushq $408 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWMIconName + .p2align 4 + .type XSetWMIconName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWMIconName +#endif +XSetWMIconName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3272(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3272(%rip) +2: + pushq $409 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWMName + .p2align 4 + .type XSetWMName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWMName +#endif +XSetWMName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3280(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3280(%rip) +2: + pushq $410 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWMNormalHints + .p2align 4 + .type XSetWMNormalHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWMNormalHints +#endif +XSetWMNormalHints: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3288(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3288(%rip) +2: + pushq $411 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWMProperties + .p2align 4 + .type XSetWMProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWMProperties +#endif +XSetWMProperties: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3296(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3296(%rip) +2: + pushq $412 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWMProtocols + .p2align 4 + .type XSetWMProtocols, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWMProtocols +#endif +XSetWMProtocols: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3304(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3304(%rip) +2: + pushq $413 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWMSizeHints + .p2align 4 + .type XSetWMSizeHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWMSizeHints +#endif +XSetWMSizeHints: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3312(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3312(%rip) +2: + pushq $414 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWindowBackground + .p2align 4 + .type XSetWindowBackground, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWindowBackground +#endif +XSetWindowBackground: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3320(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3320(%rip) +2: + pushq $415 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWindowBackgroundPixmap + .p2align 4 + .type XSetWindowBackgroundPixmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWindowBackgroundPixmap +#endif +XSetWindowBackgroundPixmap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3328(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3328(%rip) +2: + pushq $416 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWindowBorder + .p2align 4 + .type XSetWindowBorder, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWindowBorder +#endif +XSetWindowBorder: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3336(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3336(%rip) +2: + pushq $417 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWindowBorderPixmap + .p2align 4 + .type XSetWindowBorderPixmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWindowBorderPixmap +#endif +XSetWindowBorderPixmap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3344(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3344(%rip) +2: + pushq $418 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWindowBorderWidth + .p2align 4 + .type XSetWindowBorderWidth, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWindowBorderWidth +#endif +XSetWindowBorderWidth: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3352(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3352(%rip) +2: + pushq $419 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetWindowColormap + .p2align 4 + .type XSetWindowColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetWindowColormap +#endif +XSetWindowColormap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3360(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3360(%rip) +2: + pushq $420 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetZoomHints + .p2align 4 + .type XSetZoomHints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetZoomHints +#endif +XSetZoomHints: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3368(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3368(%rip) +2: + pushq $421 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShrinkRegion + .p2align 4 + .type XShrinkRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShrinkRegion +#endif +XShrinkRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3376(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3376(%rip) +2: + pushq $422 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XStoreBuffer + .p2align 4 + .type XStoreBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XStoreBuffer +#endif +XStoreBuffer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3384(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3384(%rip) +2: + pushq $423 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XStoreBytes + .p2align 4 + .type XStoreBytes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XStoreBytes +#endif +XStoreBytes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3392(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3392(%rip) +2: + pushq $424 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XStoreColor + .p2align 4 + .type XStoreColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XStoreColor +#endif +XStoreColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3400(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3400(%rip) +2: + pushq $425 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XStoreColors + .p2align 4 + .type XStoreColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XStoreColors +#endif +XStoreColors: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3408(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3408(%rip) +2: + pushq $426 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XStoreName + .p2align 4 + .type XStoreName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XStoreName +#endif +XStoreName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3416(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3416(%rip) +2: + pushq $427 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XStoreNamedColor + .p2align 4 + .type XStoreNamedColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XStoreNamedColor +#endif +XStoreNamedColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3424(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3424(%rip) +2: + pushq $428 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XStringListToTextProperty + .p2align 4 + .type XStringListToTextProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XStringListToTextProperty +#endif +XStringListToTextProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3432(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3432(%rip) +2: + pushq $429 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XStringToKeysym + .p2align 4 + .type XStringToKeysym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XStringToKeysym +#endif +XStringToKeysym: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3440(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3440(%rip) +2: + pushq $430 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSubImage + .p2align 4 + .type XSubImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSubImage +#endif +XSubImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3448(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3448(%rip) +2: + pushq $431 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSubtractRegion + .p2align 4 + .type XSubtractRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSubtractRegion +#endif +XSubtractRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3456(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3456(%rip) +2: + pushq $432 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSupportsLocale + .p2align 4 + .type XSupportsLocale, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSupportsLocale +#endif +XSupportsLocale: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3464(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3464(%rip) +2: + pushq $433 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSync + .p2align 4 + .type XSync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSync +#endif +XSync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3472(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3472(%rip) +2: + pushq $434 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSynchronize + .p2align 4 + .type XSynchronize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSynchronize +#endif +XSynchronize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3480(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3480(%rip) +2: + pushq $435 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTextExtents + .p2align 4 + .type XTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTextExtents +#endif +XTextExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3488(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3488(%rip) +2: + pushq $436 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTextExtents16 + .p2align 4 + .type XTextExtents16, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTextExtents16 +#endif +XTextExtents16: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3496(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3496(%rip) +2: + pushq $437 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTextPropertyToStringList + .p2align 4 + .type XTextPropertyToStringList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTextPropertyToStringList +#endif +XTextPropertyToStringList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3504(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3504(%rip) +2: + pushq $438 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTextWidth + .p2align 4 + .type XTextWidth, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTextWidth +#endif +XTextWidth: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3512(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3512(%rip) +2: + pushq $439 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTextWidth16 + .p2align 4 + .type XTextWidth16, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTextWidth16 +#endif +XTextWidth16: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3520(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3520(%rip) +2: + pushq $440 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTranslateCoordinates + .p2align 4 + .type XTranslateCoordinates, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTranslateCoordinates +#endif +XTranslateCoordinates: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3528(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3528(%rip) +2: + pushq $441 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUndefineCursor + .p2align 4 + .type XUndefineCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUndefineCursor +#endif +XUndefineCursor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3536(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3536(%rip) +2: + pushq $442 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUngrabButton + .p2align 4 + .type XUngrabButton, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUngrabButton +#endif +XUngrabButton: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3544(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3544(%rip) +2: + pushq $443 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUngrabKey + .p2align 4 + .type XUngrabKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUngrabKey +#endif +XUngrabKey: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3552(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3552(%rip) +2: + pushq $444 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUngrabKeyboard + .p2align 4 + .type XUngrabKeyboard, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUngrabKeyboard +#endif +XUngrabKeyboard: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3560(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3560(%rip) +2: + pushq $445 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUngrabPointer + .p2align 4 + .type XUngrabPointer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUngrabPointer +#endif +XUngrabPointer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3568(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3568(%rip) +2: + pushq $446 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUngrabServer + .p2align 4 + .type XUngrabServer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUngrabServer +#endif +XUngrabServer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3576(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3576(%rip) +2: + pushq $447 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUninstallColormap + .p2align 4 + .type XUninstallColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUninstallColormap +#endif +XUninstallColormap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3584(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3584(%rip) +2: + pushq $448 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUnionRectWithRegion + .p2align 4 + .type XUnionRectWithRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUnionRectWithRegion +#endif +XUnionRectWithRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3592(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3592(%rip) +2: + pushq $449 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUnionRegion + .p2align 4 + .type XUnionRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUnionRegion +#endif +XUnionRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3600(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3600(%rip) +2: + pushq $450 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUnloadFont + .p2align 4 + .type XUnloadFont, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUnloadFont +#endif +XUnloadFont: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3608(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3608(%rip) +2: + pushq $451 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUnlockDisplay + .p2align 4 + .type XUnlockDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUnlockDisplay +#endif +XUnlockDisplay: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3616(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3616(%rip) +2: + pushq $452 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUnmapSubwindows + .p2align 4 + .type XUnmapSubwindows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUnmapSubwindows +#endif +XUnmapSubwindows: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3624(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3624(%rip) +2: + pushq $453 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUnmapWindow + .p2align 4 + .type XUnmapWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUnmapWindow +#endif +XUnmapWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3632(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3632(%rip) +2: + pushq $454 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUnregisterIMInstantiateCallback + .p2align 4 + .type XUnregisterIMInstantiateCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUnregisterIMInstantiateCallback +#endif +XUnregisterIMInstantiateCallback: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3640(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3640(%rip) +2: + pushq $455 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XUnsetICFocus + .p2align 4 + .type XUnsetICFocus, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XUnsetICFocus +#endif +XUnsetICFocus: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3648(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3648(%rip) +2: + pushq $456 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XVaCreateNestedList + .p2align 4 + .type XVaCreateNestedList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XVaCreateNestedList +#endif +XVaCreateNestedList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3656(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3656(%rip) +2: + pushq $457 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XVendorRelease + .p2align 4 + .type XVendorRelease, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XVendorRelease +#endif +XVendorRelease: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3664(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3664(%rip) +2: + pushq $458 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XVisualIDFromVisual + .p2align 4 + .type XVisualIDFromVisual, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XVisualIDFromVisual +#endif +XVisualIDFromVisual: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3672(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3672(%rip) +2: + pushq $459 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XWMGeometry + .p2align 4 + .type XWMGeometry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XWMGeometry +#endif +XWMGeometry: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3680(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3680(%rip) +2: + pushq $460 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XWarpPointer + .p2align 4 + .type XWarpPointer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XWarpPointer +#endif +XWarpPointer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3688(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3688(%rip) +2: + pushq $461 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XWhitePixel + .p2align 4 + .type XWhitePixel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XWhitePixel +#endif +XWhitePixel: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3696(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3696(%rip) +2: + pushq $462 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XWhitePixelOfScreen + .p2align 4 + .type XWhitePixelOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XWhitePixelOfScreen +#endif +XWhitePixelOfScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3704(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3704(%rip) +2: + pushq $463 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XWidthMMOfScreen + .p2align 4 + .type XWidthMMOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XWidthMMOfScreen +#endif +XWidthMMOfScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3712(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3712(%rip) +2: + pushq $464 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XWidthOfScreen + .p2align 4 + .type XWidthOfScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XWidthOfScreen +#endif +XWidthOfScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3720(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3720(%rip) +2: + pushq $465 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XWindowEvent + .p2align 4 + .type XWindowEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XWindowEvent +#endif +XWindowEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3728(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3728(%rip) +2: + pushq $466 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XWithdrawWindow + .p2align 4 + .type XWithdrawWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XWithdrawWindow +#endif +XWithdrawWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3736(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3736(%rip) +2: + pushq $467 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XWriteBitmapFile + .p2align 4 + .type XWriteBitmapFile, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XWriteBitmapFile +#endif +XWriteBitmapFile: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3744(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3744(%rip) +2: + pushq $468 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XXorRegion + .p2align 4 + .type XXorRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XXorRegion +#endif +XXorRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3752(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3752(%rip) +2: + pushq $469 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsAddColorSpace + .p2align 4 + .type XcmsAddColorSpace, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsAddColorSpace +#endif +XcmsAddColorSpace: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3760(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3760(%rip) +2: + pushq $470 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsAddFunctionSet + .p2align 4 + .type XcmsAddFunctionSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsAddFunctionSet +#endif +XcmsAddFunctionSet: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3768(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3768(%rip) +2: + pushq $471 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsAllocColor + .p2align 4 + .type XcmsAllocColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsAllocColor +#endif +XcmsAllocColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3776(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3776(%rip) +2: + pushq $472 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsAllocNamedColor + .p2align 4 + .type XcmsAllocNamedColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsAllocNamedColor +#endif +XcmsAllocNamedColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3784(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3784(%rip) +2: + pushq $473 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCCCOfColormap + .p2align 4 + .type XcmsCCCOfColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCCCOfColormap +#endif +XcmsCCCOfColormap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3792(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3792(%rip) +2: + pushq $474 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELabClipL + .p2align 4 + .type XcmsCIELabClipL, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELabClipL +#endif +XcmsCIELabClipL: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3800(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3800(%rip) +2: + pushq $475 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELabClipLab + .p2align 4 + .type XcmsCIELabClipLab, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELabClipLab +#endif +XcmsCIELabClipLab: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3808(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3808(%rip) +2: + pushq $476 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELabClipab + .p2align 4 + .type XcmsCIELabClipab, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELabClipab +#endif +XcmsCIELabClipab: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3816(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3816(%rip) +2: + pushq $477 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELabQueryMaxC + .p2align 4 + .type XcmsCIELabQueryMaxC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELabQueryMaxC +#endif +XcmsCIELabQueryMaxC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3824(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3824(%rip) +2: + pushq $478 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELabQueryMaxL + .p2align 4 + .type XcmsCIELabQueryMaxL, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELabQueryMaxL +#endif +XcmsCIELabQueryMaxL: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3832(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3832(%rip) +2: + pushq $479 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELabQueryMaxLC + .p2align 4 + .type XcmsCIELabQueryMaxLC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELabQueryMaxLC +#endif +XcmsCIELabQueryMaxLC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3840(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3840(%rip) +2: + pushq $480 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELabQueryMinL + .p2align 4 + .type XcmsCIELabQueryMinL, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELabQueryMinL +#endif +XcmsCIELabQueryMinL: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3848(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3848(%rip) +2: + pushq $481 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELabToCIEXYZ + .p2align 4 + .type XcmsCIELabToCIEXYZ, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELabToCIEXYZ +#endif +XcmsCIELabToCIEXYZ: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3856(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3856(%rip) +2: + pushq $482 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELabWhiteShiftColors + .p2align 4 + .type XcmsCIELabWhiteShiftColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELabWhiteShiftColors +#endif +XcmsCIELabWhiteShiftColors: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3864(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3864(%rip) +2: + pushq $483 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELuvClipL + .p2align 4 + .type XcmsCIELuvClipL, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELuvClipL +#endif +XcmsCIELuvClipL: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3872(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3872(%rip) +2: + pushq $484 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELuvClipLuv + .p2align 4 + .type XcmsCIELuvClipLuv, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELuvClipLuv +#endif +XcmsCIELuvClipLuv: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3880(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3880(%rip) +2: + pushq $485 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELuvClipuv + .p2align 4 + .type XcmsCIELuvClipuv, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELuvClipuv +#endif +XcmsCIELuvClipuv: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3888(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3888(%rip) +2: + pushq $486 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELuvQueryMaxC + .p2align 4 + .type XcmsCIELuvQueryMaxC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELuvQueryMaxC +#endif +XcmsCIELuvQueryMaxC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3896(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3896(%rip) +2: + pushq $487 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELuvQueryMaxL + .p2align 4 + .type XcmsCIELuvQueryMaxL, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELuvQueryMaxL +#endif +XcmsCIELuvQueryMaxL: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3904(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3904(%rip) +2: + pushq $488 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELuvQueryMaxLC + .p2align 4 + .type XcmsCIELuvQueryMaxLC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELuvQueryMaxLC +#endif +XcmsCIELuvQueryMaxLC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3912(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3912(%rip) +2: + pushq $489 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELuvQueryMinL + .p2align 4 + .type XcmsCIELuvQueryMinL, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELuvQueryMinL +#endif +XcmsCIELuvQueryMinL: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3920(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3920(%rip) +2: + pushq $490 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELuvToCIEuvY + .p2align 4 + .type XcmsCIELuvToCIEuvY, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELuvToCIEuvY +#endif +XcmsCIELuvToCIEuvY: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3928(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3928(%rip) +2: + pushq $491 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIELuvWhiteShiftColors + .p2align 4 + .type XcmsCIELuvWhiteShiftColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIELuvWhiteShiftColors +#endif +XcmsCIELuvWhiteShiftColors: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3936(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3936(%rip) +2: + pushq $492 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIEXYZToCIELab + .p2align 4 + .type XcmsCIEXYZToCIELab, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIEXYZToCIELab +#endif +XcmsCIEXYZToCIELab: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3944(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3944(%rip) +2: + pushq $493 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIEXYZToCIEuvY + .p2align 4 + .type XcmsCIEXYZToCIEuvY, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIEXYZToCIEuvY +#endif +XcmsCIEXYZToCIEuvY: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3952(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3952(%rip) +2: + pushq $494 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIEXYZToCIExyY + .p2align 4 + .type XcmsCIEXYZToCIExyY, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIEXYZToCIExyY +#endif +XcmsCIEXYZToCIExyY: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3960(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3960(%rip) +2: + pushq $495 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIEXYZToRGBi + .p2align 4 + .type XcmsCIEXYZToRGBi, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIEXYZToRGBi +#endif +XcmsCIEXYZToRGBi: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3968(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3968(%rip) +2: + pushq $496 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIEuvYToCIELuv + .p2align 4 + .type XcmsCIEuvYToCIELuv, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIEuvYToCIELuv +#endif +XcmsCIEuvYToCIELuv: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3976(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3976(%rip) +2: + pushq $497 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIEuvYToCIEXYZ + .p2align 4 + .type XcmsCIEuvYToCIEXYZ, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIEuvYToCIEXYZ +#endif +XcmsCIEuvYToCIEXYZ: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3984(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3984(%rip) +2: + pushq $498 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIEuvYToTekHVC + .p2align 4 + .type XcmsCIEuvYToTekHVC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIEuvYToTekHVC +#endif +XcmsCIEuvYToTekHVC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+3992(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+3992(%rip) +2: + pushq $499 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCIExyYToCIEXYZ + .p2align 4 + .type XcmsCIExyYToCIEXYZ, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCIExyYToCIEXYZ +#endif +XcmsCIExyYToCIEXYZ: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4000(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4000(%rip) +2: + pushq $500 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsClientWhitePointOfCCC + .p2align 4 + .type XcmsClientWhitePointOfCCC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsClientWhitePointOfCCC +#endif +XcmsClientWhitePointOfCCC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4008(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4008(%rip) +2: + pushq $501 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsConvertColors + .p2align 4 + .type XcmsConvertColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsConvertColors +#endif +XcmsConvertColors: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4016(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4016(%rip) +2: + pushq $502 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsCreateCCC + .p2align 4 + .type XcmsCreateCCC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsCreateCCC +#endif +XcmsCreateCCC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4024(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4024(%rip) +2: + pushq $503 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsDefaultCCC + .p2align 4 + .type XcmsDefaultCCC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsDefaultCCC +#endif +XcmsDefaultCCC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4032(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4032(%rip) +2: + pushq $504 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsDisplayOfCCC + .p2align 4 + .type XcmsDisplayOfCCC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsDisplayOfCCC +#endif +XcmsDisplayOfCCC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4040(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4040(%rip) +2: + pushq $505 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsFormatOfPrefix + .p2align 4 + .type XcmsFormatOfPrefix, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsFormatOfPrefix +#endif +XcmsFormatOfPrefix: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4048(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4048(%rip) +2: + pushq $506 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsFreeCCC + .p2align 4 + .type XcmsFreeCCC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsFreeCCC +#endif +XcmsFreeCCC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4056(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4056(%rip) +2: + pushq $507 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsLookupColor + .p2align 4 + .type XcmsLookupColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsLookupColor +#endif +XcmsLookupColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4064(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4064(%rip) +2: + pushq $508 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsPrefixOfFormat + .p2align 4 + .type XcmsPrefixOfFormat, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsPrefixOfFormat +#endif +XcmsPrefixOfFormat: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4072(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4072(%rip) +2: + pushq $509 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsQueryBlack + .p2align 4 + .type XcmsQueryBlack, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsQueryBlack +#endif +XcmsQueryBlack: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4080(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4080(%rip) +2: + pushq $510 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsQueryBlue + .p2align 4 + .type XcmsQueryBlue, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsQueryBlue +#endif +XcmsQueryBlue: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4088(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4088(%rip) +2: + pushq $511 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsQueryColor + .p2align 4 + .type XcmsQueryColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsQueryColor +#endif +XcmsQueryColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4096(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4096(%rip) +2: + pushq $512 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsQueryColors + .p2align 4 + .type XcmsQueryColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsQueryColors +#endif +XcmsQueryColors: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4104(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4104(%rip) +2: + pushq $513 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsQueryGreen + .p2align 4 + .type XcmsQueryGreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsQueryGreen +#endif +XcmsQueryGreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4112(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4112(%rip) +2: + pushq $514 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsQueryRed + .p2align 4 + .type XcmsQueryRed, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsQueryRed +#endif +XcmsQueryRed: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4120(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4120(%rip) +2: + pushq $515 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsQueryWhite + .p2align 4 + .type XcmsQueryWhite, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsQueryWhite +#endif +XcmsQueryWhite: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4128(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4128(%rip) +2: + pushq $516 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsRGBToRGBi + .p2align 4 + .type XcmsRGBToRGBi, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsRGBToRGBi +#endif +XcmsRGBToRGBi: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4136(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4136(%rip) +2: + pushq $517 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsRGBiToCIEXYZ + .p2align 4 + .type XcmsRGBiToCIEXYZ, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsRGBiToCIEXYZ +#endif +XcmsRGBiToCIEXYZ: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4144(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4144(%rip) +2: + pushq $518 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsRGBiToRGB + .p2align 4 + .type XcmsRGBiToRGB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsRGBiToRGB +#endif +XcmsRGBiToRGB: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4152(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4152(%rip) +2: + pushq $519 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsScreenNumberOfCCC + .p2align 4 + .type XcmsScreenNumberOfCCC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsScreenNumberOfCCC +#endif +XcmsScreenNumberOfCCC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4160(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4160(%rip) +2: + pushq $520 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsScreenWhitePointOfCCC + .p2align 4 + .type XcmsScreenWhitePointOfCCC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsScreenWhitePointOfCCC +#endif +XcmsScreenWhitePointOfCCC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4168(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4168(%rip) +2: + pushq $521 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsSetCCCOfColormap + .p2align 4 + .type XcmsSetCCCOfColormap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsSetCCCOfColormap +#endif +XcmsSetCCCOfColormap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4176(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4176(%rip) +2: + pushq $522 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsSetCompressionProc + .p2align 4 + .type XcmsSetCompressionProc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsSetCompressionProc +#endif +XcmsSetCompressionProc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4184(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4184(%rip) +2: + pushq $523 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsSetWhiteAdjustProc + .p2align 4 + .type XcmsSetWhiteAdjustProc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsSetWhiteAdjustProc +#endif +XcmsSetWhiteAdjustProc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4192(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4192(%rip) +2: + pushq $524 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsSetWhitePoint + .p2align 4 + .type XcmsSetWhitePoint, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsSetWhitePoint +#endif +XcmsSetWhitePoint: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4200(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4200(%rip) +2: + pushq $525 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsStoreColor + .p2align 4 + .type XcmsStoreColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsStoreColor +#endif +XcmsStoreColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4208(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4208(%rip) +2: + pushq $526 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsStoreColors + .p2align 4 + .type XcmsStoreColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsStoreColors +#endif +XcmsStoreColors: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4216(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4216(%rip) +2: + pushq $527 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCClipC + .p2align 4 + .type XcmsTekHVCClipC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCClipC +#endif +XcmsTekHVCClipC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4224(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4224(%rip) +2: + pushq $528 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCClipV + .p2align 4 + .type XcmsTekHVCClipV, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCClipV +#endif +XcmsTekHVCClipV: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4232(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4232(%rip) +2: + pushq $529 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCClipVC + .p2align 4 + .type XcmsTekHVCClipVC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCClipVC +#endif +XcmsTekHVCClipVC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4240(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4240(%rip) +2: + pushq $530 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCQueryMaxC + .p2align 4 + .type XcmsTekHVCQueryMaxC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCQueryMaxC +#endif +XcmsTekHVCQueryMaxC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4248(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4248(%rip) +2: + pushq $531 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCQueryMaxV + .p2align 4 + .type XcmsTekHVCQueryMaxV, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCQueryMaxV +#endif +XcmsTekHVCQueryMaxV: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4256(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4256(%rip) +2: + pushq $532 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCQueryMaxVC + .p2align 4 + .type XcmsTekHVCQueryMaxVC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCQueryMaxVC +#endif +XcmsTekHVCQueryMaxVC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4264(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4264(%rip) +2: + pushq $533 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCQueryMaxVSamples + .p2align 4 + .type XcmsTekHVCQueryMaxVSamples, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCQueryMaxVSamples +#endif +XcmsTekHVCQueryMaxVSamples: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4272(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4272(%rip) +2: + pushq $534 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCQueryMinV + .p2align 4 + .type XcmsTekHVCQueryMinV, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCQueryMinV +#endif +XcmsTekHVCQueryMinV: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4280(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4280(%rip) +2: + pushq $535 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCToCIEuvY + .p2align 4 + .type XcmsTekHVCToCIEuvY, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCToCIEuvY +#endif +XcmsTekHVCToCIEuvY: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4288(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4288(%rip) +2: + pushq $536 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsTekHVCWhiteShiftColors + .p2align 4 + .type XcmsTekHVCWhiteShiftColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsTekHVCWhiteShiftColors +#endif +XcmsTekHVCWhiteShiftColors: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4296(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4296(%rip) +2: + pushq $537 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcmsVisualOfCCC + .p2align 4 + .type XcmsVisualOfCCC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcmsVisualOfCCC +#endif +XcmsVisualOfCCC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4304(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4304(%rip) +2: + pushq $538 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddDeviceLedInfo + .p2align 4 + .type XkbAddDeviceLedInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddDeviceLedInfo +#endif +XkbAddDeviceLedInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4312(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4312(%rip) +2: + pushq $539 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomColor + .p2align 4 + .type XkbAddGeomColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomColor +#endif +XkbAddGeomColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4320(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4320(%rip) +2: + pushq $540 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomDoodad + .p2align 4 + .type XkbAddGeomDoodad, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomDoodad +#endif +XkbAddGeomDoodad: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4328(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4328(%rip) +2: + pushq $541 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomKey + .p2align 4 + .type XkbAddGeomKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomKey +#endif +XkbAddGeomKey: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4336(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4336(%rip) +2: + pushq $542 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomKeyAlias + .p2align 4 + .type XkbAddGeomKeyAlias, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomKeyAlias +#endif +XkbAddGeomKeyAlias: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4344(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4344(%rip) +2: + pushq $543 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomOutline + .p2align 4 + .type XkbAddGeomOutline, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomOutline +#endif +XkbAddGeomOutline: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4352(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4352(%rip) +2: + pushq $544 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomOverlay + .p2align 4 + .type XkbAddGeomOverlay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomOverlay +#endif +XkbAddGeomOverlay: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4360(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4360(%rip) +2: + pushq $545 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomOverlayKey + .p2align 4 + .type XkbAddGeomOverlayKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomOverlayKey +#endif +XkbAddGeomOverlayKey: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4368(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4368(%rip) +2: + pushq $546 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomOverlayRow + .p2align 4 + .type XkbAddGeomOverlayRow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomOverlayRow +#endif +XkbAddGeomOverlayRow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4376(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4376(%rip) +2: + pushq $547 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomProperty + .p2align 4 + .type XkbAddGeomProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomProperty +#endif +XkbAddGeomProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4384(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4384(%rip) +2: + pushq $548 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomRow + .p2align 4 + .type XkbAddGeomRow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomRow +#endif +XkbAddGeomRow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4392(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4392(%rip) +2: + pushq $549 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomSection + .p2align 4 + .type XkbAddGeomSection, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomSection +#endif +XkbAddGeomSection: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4400(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4400(%rip) +2: + pushq $550 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddGeomShape + .p2align 4 + .type XkbAddGeomShape, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddGeomShape +#endif +XkbAddGeomShape: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4408(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4408(%rip) +2: + pushq $551 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAddKeyType + .p2align 4 + .type XkbAddKeyType, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAddKeyType +#endif +XkbAddKeyType: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4416(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4416(%rip) +2: + pushq $552 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocClientMap + .p2align 4 + .type XkbAllocClientMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocClientMap +#endif +XkbAllocClientMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4424(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4424(%rip) +2: + pushq $553 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocCompatMap + .p2align 4 + .type XkbAllocCompatMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocCompatMap +#endif +XkbAllocCompatMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4432(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4432(%rip) +2: + pushq $554 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocControls + .p2align 4 + .type XkbAllocControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocControls +#endif +XkbAllocControls: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4440(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4440(%rip) +2: + pushq $555 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocDeviceInfo + .p2align 4 + .type XkbAllocDeviceInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocDeviceInfo +#endif +XkbAllocDeviceInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4448(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4448(%rip) +2: + pushq $556 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomColors + .p2align 4 + .type XkbAllocGeomColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomColors +#endif +XkbAllocGeomColors: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4456(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4456(%rip) +2: + pushq $557 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomDoodads + .p2align 4 + .type XkbAllocGeomDoodads, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomDoodads +#endif +XkbAllocGeomDoodads: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4464(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4464(%rip) +2: + pushq $558 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomKeyAliases + .p2align 4 + .type XkbAllocGeomKeyAliases, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomKeyAliases +#endif +XkbAllocGeomKeyAliases: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4472(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4472(%rip) +2: + pushq $559 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomKeys + .p2align 4 + .type XkbAllocGeomKeys, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomKeys +#endif +XkbAllocGeomKeys: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4480(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4480(%rip) +2: + pushq $560 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomOutlines + .p2align 4 + .type XkbAllocGeomOutlines, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomOutlines +#endif +XkbAllocGeomOutlines: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4488(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4488(%rip) +2: + pushq $561 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomOverlayKeys + .p2align 4 + .type XkbAllocGeomOverlayKeys, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomOverlayKeys +#endif +XkbAllocGeomOverlayKeys: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4496(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4496(%rip) +2: + pushq $562 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomOverlayRows + .p2align 4 + .type XkbAllocGeomOverlayRows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomOverlayRows +#endif +XkbAllocGeomOverlayRows: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4504(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4504(%rip) +2: + pushq $563 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomOverlays + .p2align 4 + .type XkbAllocGeomOverlays, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomOverlays +#endif +XkbAllocGeomOverlays: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4512(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4512(%rip) +2: + pushq $564 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomPoints + .p2align 4 + .type XkbAllocGeomPoints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomPoints +#endif +XkbAllocGeomPoints: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4520(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4520(%rip) +2: + pushq $565 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomProps + .p2align 4 + .type XkbAllocGeomProps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomProps +#endif +XkbAllocGeomProps: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4528(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4528(%rip) +2: + pushq $566 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomRows + .p2align 4 + .type XkbAllocGeomRows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomRows +#endif +XkbAllocGeomRows: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4536(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4536(%rip) +2: + pushq $567 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomSectionDoodads + .p2align 4 + .type XkbAllocGeomSectionDoodads, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomSectionDoodads +#endif +XkbAllocGeomSectionDoodads: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4544(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4544(%rip) +2: + pushq $568 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomSections + .p2align 4 + .type XkbAllocGeomSections, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomSections +#endif +XkbAllocGeomSections: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4552(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4552(%rip) +2: + pushq $569 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeomShapes + .p2align 4 + .type XkbAllocGeomShapes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeomShapes +#endif +XkbAllocGeomShapes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4560(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4560(%rip) +2: + pushq $570 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocGeometry + .p2align 4 + .type XkbAllocGeometry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocGeometry +#endif +XkbAllocGeometry: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4568(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4568(%rip) +2: + pushq $571 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocIndicatorMaps + .p2align 4 + .type XkbAllocIndicatorMaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocIndicatorMaps +#endif +XkbAllocIndicatorMaps: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4576(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4576(%rip) +2: + pushq $572 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocKeyboard + .p2align 4 + .type XkbAllocKeyboard, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocKeyboard +#endif +XkbAllocKeyboard: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4584(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4584(%rip) +2: + pushq $573 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocNames + .p2align 4 + .type XkbAllocNames, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocNames +#endif +XkbAllocNames: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4592(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4592(%rip) +2: + pushq $574 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbAllocServerMap + .p2align 4 + .type XkbAllocServerMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbAllocServerMap +#endif +XkbAllocServerMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4600(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4600(%rip) +2: + pushq $575 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbApplyCompatMapToKey + .p2align 4 + .type XkbApplyCompatMapToKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbApplyCompatMapToKey +#endif +XkbApplyCompatMapToKey: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4608(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4608(%rip) +2: + pushq $576 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbApplyVirtualModChanges + .p2align 4 + .type XkbApplyVirtualModChanges, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbApplyVirtualModChanges +#endif +XkbApplyVirtualModChanges: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4616(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4616(%rip) +2: + pushq $577 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbBell + .p2align 4 + .type XkbBell, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbBell +#endif +XkbBell: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4624(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4624(%rip) +2: + pushq $578 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbBellEvent + .p2align 4 + .type XkbBellEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbBellEvent +#endif +XkbBellEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4632(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4632(%rip) +2: + pushq $579 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbChangeDeviceInfo + .p2align 4 + .type XkbChangeDeviceInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbChangeDeviceInfo +#endif +XkbChangeDeviceInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4640(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4640(%rip) +2: + pushq $580 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbChangeEnabledControls + .p2align 4 + .type XkbChangeEnabledControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbChangeEnabledControls +#endif +XkbChangeEnabledControls: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4648(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4648(%rip) +2: + pushq $581 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbChangeKeycodeRange + .p2align 4 + .type XkbChangeKeycodeRange, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbChangeKeycodeRange +#endif +XkbChangeKeycodeRange: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4656(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4656(%rip) +2: + pushq $582 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbChangeMap + .p2align 4 + .type XkbChangeMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbChangeMap +#endif +XkbChangeMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4664(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4664(%rip) +2: + pushq $583 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbChangeNames + .p2align 4 + .type XkbChangeNames, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbChangeNames +#endif +XkbChangeNames: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4672(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4672(%rip) +2: + pushq $584 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbChangeTypesOfKey + .p2align 4 + .type XkbChangeTypesOfKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbChangeTypesOfKey +#endif +XkbChangeTypesOfKey: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4680(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4680(%rip) +2: + pushq $585 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbComputeEffectiveMap + .p2align 4 + .type XkbComputeEffectiveMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbComputeEffectiveMap +#endif +XkbComputeEffectiveMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4688(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4688(%rip) +2: + pushq $586 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbComputeRowBounds + .p2align 4 + .type XkbComputeRowBounds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbComputeRowBounds +#endif +XkbComputeRowBounds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4696(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4696(%rip) +2: + pushq $587 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbComputeSectionBounds + .p2align 4 + .type XkbComputeSectionBounds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbComputeSectionBounds +#endif +XkbComputeSectionBounds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4704(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4704(%rip) +2: + pushq $588 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbComputeShapeBounds + .p2align 4 + .type XkbComputeShapeBounds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbComputeShapeBounds +#endif +XkbComputeShapeBounds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4712(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4712(%rip) +2: + pushq $589 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbComputeShapeTop + .p2align 4 + .type XkbComputeShapeTop, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbComputeShapeTop +#endif +XkbComputeShapeTop: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4720(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4720(%rip) +2: + pushq $590 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbCopyKeyType + .p2align 4 + .type XkbCopyKeyType, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbCopyKeyType +#endif +XkbCopyKeyType: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4728(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4728(%rip) +2: + pushq $591 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbCopyKeyTypes + .p2align 4 + .type XkbCopyKeyTypes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbCopyKeyTypes +#endif +XkbCopyKeyTypes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4736(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4736(%rip) +2: + pushq $592 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbDeviceBell + .p2align 4 + .type XkbDeviceBell, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbDeviceBell +#endif +XkbDeviceBell: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4744(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4744(%rip) +2: + pushq $593 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbDeviceBellEvent + .p2align 4 + .type XkbDeviceBellEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbDeviceBellEvent +#endif +XkbDeviceBellEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4752(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4752(%rip) +2: + pushq $594 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFindOverlayForKey + .p2align 4 + .type XkbFindOverlayForKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFindOverlayForKey +#endif +XkbFindOverlayForKey: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4760(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4760(%rip) +2: + pushq $595 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbForceBell + .p2align 4 + .type XkbForceBell, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbForceBell +#endif +XkbForceBell: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4768(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4768(%rip) +2: + pushq $596 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbForceDeviceBell + .p2align 4 + .type XkbForceDeviceBell, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbForceDeviceBell +#endif +XkbForceDeviceBell: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4776(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4776(%rip) +2: + pushq $597 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeClientMap + .p2align 4 + .type XkbFreeClientMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeClientMap +#endif +XkbFreeClientMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4784(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4784(%rip) +2: + pushq $598 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeCompatMap + .p2align 4 + .type XkbFreeCompatMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeCompatMap +#endif +XkbFreeCompatMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4792(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4792(%rip) +2: + pushq $599 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeComponentList + .p2align 4 + .type XkbFreeComponentList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeComponentList +#endif +XkbFreeComponentList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4800(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4800(%rip) +2: + pushq $600 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeControls + .p2align 4 + .type XkbFreeControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeControls +#endif +XkbFreeControls: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4808(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4808(%rip) +2: + pushq $601 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeDeviceInfo + .p2align 4 + .type XkbFreeDeviceInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeDeviceInfo +#endif +XkbFreeDeviceInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4816(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4816(%rip) +2: + pushq $602 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomColors + .p2align 4 + .type XkbFreeGeomColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomColors +#endif +XkbFreeGeomColors: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4824(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4824(%rip) +2: + pushq $603 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomDoodads + .p2align 4 + .type XkbFreeGeomDoodads, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomDoodads +#endif +XkbFreeGeomDoodads: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4832(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4832(%rip) +2: + pushq $604 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomKeyAliases + .p2align 4 + .type XkbFreeGeomKeyAliases, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomKeyAliases +#endif +XkbFreeGeomKeyAliases: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4840(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4840(%rip) +2: + pushq $605 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomKeys + .p2align 4 + .type XkbFreeGeomKeys, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomKeys +#endif +XkbFreeGeomKeys: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4848(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4848(%rip) +2: + pushq $606 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomOutlines + .p2align 4 + .type XkbFreeGeomOutlines, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomOutlines +#endif +XkbFreeGeomOutlines: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4856(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4856(%rip) +2: + pushq $607 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomOverlayKeys + .p2align 4 + .type XkbFreeGeomOverlayKeys, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomOverlayKeys +#endif +XkbFreeGeomOverlayKeys: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4864(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4864(%rip) +2: + pushq $608 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomOverlayRows + .p2align 4 + .type XkbFreeGeomOverlayRows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomOverlayRows +#endif +XkbFreeGeomOverlayRows: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4872(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4872(%rip) +2: + pushq $609 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomOverlays + .p2align 4 + .type XkbFreeGeomOverlays, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomOverlays +#endif +XkbFreeGeomOverlays: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4880(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4880(%rip) +2: + pushq $610 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomPoints + .p2align 4 + .type XkbFreeGeomPoints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomPoints +#endif +XkbFreeGeomPoints: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4888(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4888(%rip) +2: + pushq $611 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomProperties + .p2align 4 + .type XkbFreeGeomProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomProperties +#endif +XkbFreeGeomProperties: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4896(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4896(%rip) +2: + pushq $612 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomRows + .p2align 4 + .type XkbFreeGeomRows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomRows +#endif +XkbFreeGeomRows: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4904(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4904(%rip) +2: + pushq $613 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomSections + .p2align 4 + .type XkbFreeGeomSections, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomSections +#endif +XkbFreeGeomSections: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4912(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4912(%rip) +2: + pushq $614 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeomShapes + .p2align 4 + .type XkbFreeGeomShapes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeomShapes +#endif +XkbFreeGeomShapes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4920(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4920(%rip) +2: + pushq $615 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeGeometry + .p2align 4 + .type XkbFreeGeometry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeGeometry +#endif +XkbFreeGeometry: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4928(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4928(%rip) +2: + pushq $616 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeIndicatorMaps + .p2align 4 + .type XkbFreeIndicatorMaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeIndicatorMaps +#endif +XkbFreeIndicatorMaps: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4936(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4936(%rip) +2: + pushq $617 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeKeyboard + .p2align 4 + .type XkbFreeKeyboard, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeKeyboard +#endif +XkbFreeKeyboard: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4944(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4944(%rip) +2: + pushq $618 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeNames + .p2align 4 + .type XkbFreeNames, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeNames +#endif +XkbFreeNames: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4952(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4952(%rip) +2: + pushq $619 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbFreeServerMap + .p2align 4 + .type XkbFreeServerMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbFreeServerMap +#endif +XkbFreeServerMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4960(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4960(%rip) +2: + pushq $620 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetAutoRepeatRate + .p2align 4 + .type XkbGetAutoRepeatRate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetAutoRepeatRate +#endif +XkbGetAutoRepeatRate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4968(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4968(%rip) +2: + pushq $621 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetAutoResetControls + .p2align 4 + .type XkbGetAutoResetControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetAutoResetControls +#endif +XkbGetAutoResetControls: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4976(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4976(%rip) +2: + pushq $622 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetCompatMap + .p2align 4 + .type XkbGetCompatMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetCompatMap +#endif +XkbGetCompatMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4984(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4984(%rip) +2: + pushq $623 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetControls + .p2align 4 + .type XkbGetControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetControls +#endif +XkbGetControls: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+4992(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+4992(%rip) +2: + pushq $624 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetDetectableAutoRepeat + .p2align 4 + .type XkbGetDetectableAutoRepeat, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetDetectableAutoRepeat +#endif +XkbGetDetectableAutoRepeat: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5000(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5000(%rip) +2: + pushq $625 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetDeviceButtonActions + .p2align 4 + .type XkbGetDeviceButtonActions, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetDeviceButtonActions +#endif +XkbGetDeviceButtonActions: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5008(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5008(%rip) +2: + pushq $626 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetDeviceInfo + .p2align 4 + .type XkbGetDeviceInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetDeviceInfo +#endif +XkbGetDeviceInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5016(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5016(%rip) +2: + pushq $627 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetDeviceInfoChanges + .p2align 4 + .type XkbGetDeviceInfoChanges, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetDeviceInfoChanges +#endif +XkbGetDeviceInfoChanges: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5024(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5024(%rip) +2: + pushq $628 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetDeviceLedInfo + .p2align 4 + .type XkbGetDeviceLedInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetDeviceLedInfo +#endif +XkbGetDeviceLedInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5032(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5032(%rip) +2: + pushq $629 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetGeometry + .p2align 4 + .type XkbGetGeometry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetGeometry +#endif +XkbGetGeometry: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5040(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5040(%rip) +2: + pushq $630 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetIndicatorMap + .p2align 4 + .type XkbGetIndicatorMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetIndicatorMap +#endif +XkbGetIndicatorMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5048(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5048(%rip) +2: + pushq $631 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetIndicatorState + .p2align 4 + .type XkbGetIndicatorState, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetIndicatorState +#endif +XkbGetIndicatorState: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5056(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5056(%rip) +2: + pushq $632 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetKeyActions + .p2align 4 + .type XkbGetKeyActions, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetKeyActions +#endif +XkbGetKeyActions: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5064(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5064(%rip) +2: + pushq $633 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetKeyBehaviors + .p2align 4 + .type XkbGetKeyBehaviors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetKeyBehaviors +#endif +XkbGetKeyBehaviors: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5072(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5072(%rip) +2: + pushq $634 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetKeyExplicitComponents + .p2align 4 + .type XkbGetKeyExplicitComponents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetKeyExplicitComponents +#endif +XkbGetKeyExplicitComponents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5080(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5080(%rip) +2: + pushq $635 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetKeyModifierMap + .p2align 4 + .type XkbGetKeyModifierMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetKeyModifierMap +#endif +XkbGetKeyModifierMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5088(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5088(%rip) +2: + pushq $636 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetKeySyms + .p2align 4 + .type XkbGetKeySyms, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetKeySyms +#endif +XkbGetKeySyms: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5096(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5096(%rip) +2: + pushq $637 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetKeyTypes + .p2align 4 + .type XkbGetKeyTypes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetKeyTypes +#endif +XkbGetKeyTypes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5104(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5104(%rip) +2: + pushq $638 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetKeyVirtualModMap + .p2align 4 + .type XkbGetKeyVirtualModMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetKeyVirtualModMap +#endif +XkbGetKeyVirtualModMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5112(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5112(%rip) +2: + pushq $639 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetKeyboard + .p2align 4 + .type XkbGetKeyboard, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetKeyboard +#endif +XkbGetKeyboard: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5120(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5120(%rip) +2: + pushq $640 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetKeyboardByName + .p2align 4 + .type XkbGetKeyboardByName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetKeyboardByName +#endif +XkbGetKeyboardByName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5128(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5128(%rip) +2: + pushq $641 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetMap + .p2align 4 + .type XkbGetMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetMap +#endif +XkbGetMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5136(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5136(%rip) +2: + pushq $642 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetMapChanges + .p2align 4 + .type XkbGetMapChanges, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetMapChanges +#endif +XkbGetMapChanges: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5144(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5144(%rip) +2: + pushq $643 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetNamedDeviceIndicator + .p2align 4 + .type XkbGetNamedDeviceIndicator, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetNamedDeviceIndicator +#endif +XkbGetNamedDeviceIndicator: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5152(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5152(%rip) +2: + pushq $644 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetNamedGeometry + .p2align 4 + .type XkbGetNamedGeometry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetNamedGeometry +#endif +XkbGetNamedGeometry: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5160(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5160(%rip) +2: + pushq $645 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetNamedIndicator + .p2align 4 + .type XkbGetNamedIndicator, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetNamedIndicator +#endif +XkbGetNamedIndicator: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5168(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5168(%rip) +2: + pushq $646 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetNames + .p2align 4 + .type XkbGetNames, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetNames +#endif +XkbGetNames: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5176(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5176(%rip) +2: + pushq $647 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetPerClientControls + .p2align 4 + .type XkbGetPerClientControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetPerClientControls +#endif +XkbGetPerClientControls: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5184(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5184(%rip) +2: + pushq $648 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetState + .p2align 4 + .type XkbGetState, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetState +#endif +XkbGetState: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5192(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5192(%rip) +2: + pushq $649 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetUpdatedMap + .p2align 4 + .type XkbGetUpdatedMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetUpdatedMap +#endif +XkbGetUpdatedMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5200(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5200(%rip) +2: + pushq $650 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetVirtualMods + .p2align 4 + .type XkbGetVirtualMods, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetVirtualMods +#endif +XkbGetVirtualMods: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5208(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5208(%rip) +2: + pushq $651 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbGetXlibControls + .p2align 4 + .type XkbGetXlibControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbGetXlibControls +#endif +XkbGetXlibControls: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5216(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5216(%rip) +2: + pushq $652 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbIgnoreExtension + .p2align 4 + .type XkbIgnoreExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbIgnoreExtension +#endif +XkbIgnoreExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5224(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5224(%rip) +2: + pushq $653 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbInitCanonicalKeyTypes + .p2align 4 + .type XkbInitCanonicalKeyTypes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbInitCanonicalKeyTypes +#endif +XkbInitCanonicalKeyTypes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5232(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5232(%rip) +2: + pushq $654 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbKeyTypesForCoreSymbols + .p2align 4 + .type XkbKeyTypesForCoreSymbols, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbKeyTypesForCoreSymbols +#endif +XkbKeyTypesForCoreSymbols: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5240(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5240(%rip) +2: + pushq $655 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbKeycodeToKeysym + .p2align 4 + .type XkbKeycodeToKeysym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbKeycodeToKeysym +#endif +XkbKeycodeToKeysym: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5248(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5248(%rip) +2: + pushq $656 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbKeysymToModifiers + .p2align 4 + .type XkbKeysymToModifiers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbKeysymToModifiers +#endif +XkbKeysymToModifiers: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5256(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5256(%rip) +2: + pushq $657 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbLatchGroup + .p2align 4 + .type XkbLatchGroup, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbLatchGroup +#endif +XkbLatchGroup: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5264(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5264(%rip) +2: + pushq $658 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbLatchModifiers + .p2align 4 + .type XkbLatchModifiers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbLatchModifiers +#endif +XkbLatchModifiers: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5272(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5272(%rip) +2: + pushq $659 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbLibraryVersion + .p2align 4 + .type XkbLibraryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbLibraryVersion +#endif +XkbLibraryVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5280(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5280(%rip) +2: + pushq $660 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbListComponents + .p2align 4 + .type XkbListComponents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbListComponents +#endif +XkbListComponents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5288(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5288(%rip) +2: + pushq $661 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbLockGroup + .p2align 4 + .type XkbLockGroup, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbLockGroup +#endif +XkbLockGroup: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5296(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5296(%rip) +2: + pushq $662 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbLockModifiers + .p2align 4 + .type XkbLockModifiers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbLockModifiers +#endif +XkbLockModifiers: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5304(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5304(%rip) +2: + pushq $663 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbLookupKeyBinding + .p2align 4 + .type XkbLookupKeyBinding, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbLookupKeyBinding +#endif +XkbLookupKeyBinding: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5312(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5312(%rip) +2: + pushq $664 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbLookupKeySym + .p2align 4 + .type XkbLookupKeySym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbLookupKeySym +#endif +XkbLookupKeySym: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5320(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5320(%rip) +2: + pushq $665 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbNoteControlsChanges + .p2align 4 + .type XkbNoteControlsChanges, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbNoteControlsChanges +#endif +XkbNoteControlsChanges: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5328(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5328(%rip) +2: + pushq $666 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbNoteDeviceChanges + .p2align 4 + .type XkbNoteDeviceChanges, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbNoteDeviceChanges +#endif +XkbNoteDeviceChanges: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5336(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5336(%rip) +2: + pushq $667 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbNoteMapChanges + .p2align 4 + .type XkbNoteMapChanges, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbNoteMapChanges +#endif +XkbNoteMapChanges: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5344(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5344(%rip) +2: + pushq $668 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbNoteNameChanges + .p2align 4 + .type XkbNoteNameChanges, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbNoteNameChanges +#endif +XkbNoteNameChanges: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5352(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5352(%rip) +2: + pushq $669 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbOpenDisplay + .p2align 4 + .type XkbOpenDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbOpenDisplay +#endif +XkbOpenDisplay: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5360(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5360(%rip) +2: + pushq $670 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbQueryExtension + .p2align 4 + .type XkbQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbQueryExtension +#endif +XkbQueryExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5368(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5368(%rip) +2: + pushq $671 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbRefreshKeyboardMapping + .p2align 4 + .type XkbRefreshKeyboardMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbRefreshKeyboardMapping +#endif +XkbRefreshKeyboardMapping: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5376(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5376(%rip) +2: + pushq $672 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbResizeDeviceButtonActions + .p2align 4 + .type XkbResizeDeviceButtonActions, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbResizeDeviceButtonActions +#endif +XkbResizeDeviceButtonActions: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5384(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5384(%rip) +2: + pushq $673 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbResizeKeyActions + .p2align 4 + .type XkbResizeKeyActions, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbResizeKeyActions +#endif +XkbResizeKeyActions: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5392(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5392(%rip) +2: + pushq $674 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbResizeKeySyms + .p2align 4 + .type XkbResizeKeySyms, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbResizeKeySyms +#endif +XkbResizeKeySyms: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5400(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5400(%rip) +2: + pushq $675 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbResizeKeyType + .p2align 4 + .type XkbResizeKeyType, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbResizeKeyType +#endif +XkbResizeKeyType: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5408(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5408(%rip) +2: + pushq $676 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSelectEventDetails + .p2align 4 + .type XkbSelectEventDetails, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSelectEventDetails +#endif +XkbSelectEventDetails: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5416(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5416(%rip) +2: + pushq $677 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSelectEvents + .p2align 4 + .type XkbSelectEvents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSelectEvents +#endif +XkbSelectEvents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5424(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5424(%rip) +2: + pushq $678 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetAtomFuncs + .p2align 4 + .type XkbSetAtomFuncs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetAtomFuncs +#endif +XkbSetAtomFuncs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5432(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5432(%rip) +2: + pushq $679 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetAutoRepeatRate + .p2align 4 + .type XkbSetAutoRepeatRate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetAutoRepeatRate +#endif +XkbSetAutoRepeatRate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5440(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5440(%rip) +2: + pushq $680 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetAutoResetControls + .p2align 4 + .type XkbSetAutoResetControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetAutoResetControls +#endif +XkbSetAutoResetControls: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5448(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5448(%rip) +2: + pushq $681 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetCompatMap + .p2align 4 + .type XkbSetCompatMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetCompatMap +#endif +XkbSetCompatMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5456(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5456(%rip) +2: + pushq $682 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetControls + .p2align 4 + .type XkbSetControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetControls +#endif +XkbSetControls: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5464(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5464(%rip) +2: + pushq $683 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetDebuggingFlags + .p2align 4 + .type XkbSetDebuggingFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetDebuggingFlags +#endif +XkbSetDebuggingFlags: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5472(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5472(%rip) +2: + pushq $684 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetDetectableAutoRepeat + .p2align 4 + .type XkbSetDetectableAutoRepeat, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetDetectableAutoRepeat +#endif +XkbSetDetectableAutoRepeat: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5480(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5480(%rip) +2: + pushq $685 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetDeviceButtonActions + .p2align 4 + .type XkbSetDeviceButtonActions, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetDeviceButtonActions +#endif +XkbSetDeviceButtonActions: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5488(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5488(%rip) +2: + pushq $686 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetDeviceInfo + .p2align 4 + .type XkbSetDeviceInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetDeviceInfo +#endif +XkbSetDeviceInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5496(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5496(%rip) +2: + pushq $687 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetDeviceLedInfo + .p2align 4 + .type XkbSetDeviceLedInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetDeviceLedInfo +#endif +XkbSetDeviceLedInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5504(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5504(%rip) +2: + pushq $688 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetGeometry + .p2align 4 + .type XkbSetGeometry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetGeometry +#endif +XkbSetGeometry: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5512(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5512(%rip) +2: + pushq $689 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetIgnoreLockMods + .p2align 4 + .type XkbSetIgnoreLockMods, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetIgnoreLockMods +#endif +XkbSetIgnoreLockMods: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5520(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5520(%rip) +2: + pushq $690 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetIndicatorMap + .p2align 4 + .type XkbSetIndicatorMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetIndicatorMap +#endif +XkbSetIndicatorMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5528(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5528(%rip) +2: + pushq $691 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetMap + .p2align 4 + .type XkbSetMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetMap +#endif +XkbSetMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5536(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5536(%rip) +2: + pushq $692 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetNamedDeviceIndicator + .p2align 4 + .type XkbSetNamedDeviceIndicator, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetNamedDeviceIndicator +#endif +XkbSetNamedDeviceIndicator: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5544(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5544(%rip) +2: + pushq $693 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetNamedIndicator + .p2align 4 + .type XkbSetNamedIndicator, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetNamedIndicator +#endif +XkbSetNamedIndicator: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5552(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5552(%rip) +2: + pushq $694 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetNames + .p2align 4 + .type XkbSetNames, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetNames +#endif +XkbSetNames: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5560(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5560(%rip) +2: + pushq $695 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetPerClientControls + .p2align 4 + .type XkbSetPerClientControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetPerClientControls +#endif +XkbSetPerClientControls: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5568(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5568(%rip) +2: + pushq $696 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetServerInternalMods + .p2align 4 + .type XkbSetServerInternalMods, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetServerInternalMods +#endif +XkbSetServerInternalMods: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5576(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5576(%rip) +2: + pushq $697 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbSetXlibControls + .p2align 4 + .type XkbSetXlibControls, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbSetXlibControls +#endif +XkbSetXlibControls: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5584(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5584(%rip) +2: + pushq $698 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbToControl + .p2align 4 + .type XkbToControl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbToControl +#endif +XkbToControl: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5592(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5592(%rip) +2: + pushq $699 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbTranslateKey + .p2align 4 + .type XkbTranslateKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbTranslateKey +#endif +XkbTranslateKey: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5600(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5600(%rip) +2: + pushq $700 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbTranslateKeyCode + .p2align 4 + .type XkbTranslateKeyCode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbTranslateKeyCode +#endif +XkbTranslateKeyCode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5608(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5608(%rip) +2: + pushq $701 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbTranslateKeySym + .p2align 4 + .type XkbTranslateKeySym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbTranslateKeySym +#endif +XkbTranslateKeySym: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5616(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5616(%rip) +2: + pushq $702 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbUpdateActionVirtualMods + .p2align 4 + .type XkbUpdateActionVirtualMods, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbUpdateActionVirtualMods +#endif +XkbUpdateActionVirtualMods: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5624(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5624(%rip) +2: + pushq $703 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbUpdateKeyTypeVirtualMods + .p2align 4 + .type XkbUpdateKeyTypeVirtualMods, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbUpdateKeyTypeVirtualMods +#endif +XkbUpdateKeyTypeVirtualMods: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5632(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5632(%rip) +2: + pushq $704 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbUpdateMapFromCore + .p2align 4 + .type XkbUpdateMapFromCore, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbUpdateMapFromCore +#endif +XkbUpdateMapFromCore: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5640(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5640(%rip) +2: + pushq $705 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbUseExtension + .p2align 4 + .type XkbUseExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbUseExtension +#endif +XkbUseExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5648(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5648(%rip) +2: + pushq $706 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbVirtualModsToReal + .p2align 4 + .type XkbVirtualModsToReal, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbVirtualModsToReal +#endif +XkbVirtualModsToReal: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5656(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5656(%rip) +2: + pushq $707 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XkbXlibControlsImplemented + .p2align 4 + .type XkbXlibControlsImplemented, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XkbXlibControlsImplemented +#endif +XkbXlibControlsImplemented: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5664(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5664(%rip) +2: + pushq $708 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbDrawImageString + .p2align 4 + .type XmbDrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbDrawImageString +#endif +XmbDrawImageString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5672(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5672(%rip) +2: + pushq $709 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbDrawString + .p2align 4 + .type XmbDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbDrawString +#endif +XmbDrawString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5680(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5680(%rip) +2: + pushq $710 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbDrawText + .p2align 4 + .type XmbDrawText, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbDrawText +#endif +XmbDrawText: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5688(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5688(%rip) +2: + pushq $711 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbLookupString + .p2align 4 + .type XmbLookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbLookupString +#endif +XmbLookupString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5696(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5696(%rip) +2: + pushq $712 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbResetIC + .p2align 4 + .type XmbResetIC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbResetIC +#endif +XmbResetIC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5704(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5704(%rip) +2: + pushq $713 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbSetWMProperties + .p2align 4 + .type XmbSetWMProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbSetWMProperties +#endif +XmbSetWMProperties: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5712(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5712(%rip) +2: + pushq $714 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbTextEscapement + .p2align 4 + .type XmbTextEscapement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbTextEscapement +#endif +XmbTextEscapement: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5720(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5720(%rip) +2: + pushq $715 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbTextExtents + .p2align 4 + .type XmbTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbTextExtents +#endif +XmbTextExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5728(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5728(%rip) +2: + pushq $716 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbTextListToTextProperty + .p2align 4 + .type XmbTextListToTextProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbTextListToTextProperty +#endif +XmbTextListToTextProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5736(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5736(%rip) +2: + pushq $717 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbTextPerCharExtents + .p2align 4 + .type XmbTextPerCharExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbTextPerCharExtents +#endif +XmbTextPerCharExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5744(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5744(%rip) +2: + pushq $718 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbTextPropertyToTextList + .p2align 4 + .type XmbTextPropertyToTextList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbTextPropertyToTextList +#endif +XmbTextPropertyToTextList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5752(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5752(%rip) +2: + pushq $719 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xpermalloc + .p2align 4 + .type Xpermalloc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xpermalloc +#endif +Xpermalloc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5760(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5760(%rip) +2: + pushq $720 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmCombineDatabase + .p2align 4 + .type XrmCombineDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmCombineDatabase +#endif +XrmCombineDatabase: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5768(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5768(%rip) +2: + pushq $721 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmCombineFileDatabase + .p2align 4 + .type XrmCombineFileDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmCombineFileDatabase +#endif +XrmCombineFileDatabase: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5776(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5776(%rip) +2: + pushq $722 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmDestroyDatabase + .p2align 4 + .type XrmDestroyDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmDestroyDatabase +#endif +XrmDestroyDatabase: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5784(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5784(%rip) +2: + pushq $723 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmEnumerateDatabase + .p2align 4 + .type XrmEnumerateDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmEnumerateDatabase +#endif +XrmEnumerateDatabase: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5792(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5792(%rip) +2: + pushq $724 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmGetDatabase + .p2align 4 + .type XrmGetDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmGetDatabase +#endif +XrmGetDatabase: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5800(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5800(%rip) +2: + pushq $725 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmGetFileDatabase + .p2align 4 + .type XrmGetFileDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmGetFileDatabase +#endif +XrmGetFileDatabase: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5808(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5808(%rip) +2: + pushq $726 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmGetResource + .p2align 4 + .type XrmGetResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmGetResource +#endif +XrmGetResource: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5816(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5816(%rip) +2: + pushq $727 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmGetStringDatabase + .p2align 4 + .type XrmGetStringDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmGetStringDatabase +#endif +XrmGetStringDatabase: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5824(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5824(%rip) +2: + pushq $728 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmInitialize + .p2align 4 + .type XrmInitialize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmInitialize +#endif +XrmInitialize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5832(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5832(%rip) +2: + pushq $729 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmLocaleOfDatabase + .p2align 4 + .type XrmLocaleOfDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmLocaleOfDatabase +#endif +XrmLocaleOfDatabase: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5840(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5840(%rip) +2: + pushq $730 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmMergeDatabases + .p2align 4 + .type XrmMergeDatabases, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmMergeDatabases +#endif +XrmMergeDatabases: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5848(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5848(%rip) +2: + pushq $731 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmParseCommand + .p2align 4 + .type XrmParseCommand, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmParseCommand +#endif +XrmParseCommand: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5856(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5856(%rip) +2: + pushq $732 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmPermStringToQuark + .p2align 4 + .type XrmPermStringToQuark, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmPermStringToQuark +#endif +XrmPermStringToQuark: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5864(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5864(%rip) +2: + pushq $733 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmPutFileDatabase + .p2align 4 + .type XrmPutFileDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmPutFileDatabase +#endif +XrmPutFileDatabase: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5872(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5872(%rip) +2: + pushq $734 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmPutLineResource + .p2align 4 + .type XrmPutLineResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmPutLineResource +#endif +XrmPutLineResource: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5880(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5880(%rip) +2: + pushq $735 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmPutResource + .p2align 4 + .type XrmPutResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmPutResource +#endif +XrmPutResource: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5888(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5888(%rip) +2: + pushq $736 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmPutStringResource + .p2align 4 + .type XrmPutStringResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmPutStringResource +#endif +XrmPutStringResource: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5896(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5896(%rip) +2: + pushq $737 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmQGetResource + .p2align 4 + .type XrmQGetResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmQGetResource +#endif +XrmQGetResource: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5904(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5904(%rip) +2: + pushq $738 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmQGetSearchList + .p2align 4 + .type XrmQGetSearchList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmQGetSearchList +#endif +XrmQGetSearchList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5912(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5912(%rip) +2: + pushq $739 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmQGetSearchResource + .p2align 4 + .type XrmQGetSearchResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmQGetSearchResource +#endif +XrmQGetSearchResource: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5920(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5920(%rip) +2: + pushq $740 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmQPutResource + .p2align 4 + .type XrmQPutResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmQPutResource +#endif +XrmQPutResource: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5928(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5928(%rip) +2: + pushq $741 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmQPutStringResource + .p2align 4 + .type XrmQPutStringResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmQPutStringResource +#endif +XrmQPutStringResource: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5936(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5936(%rip) +2: + pushq $742 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmQuarkToString + .p2align 4 + .type XrmQuarkToString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmQuarkToString +#endif +XrmQuarkToString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5944(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5944(%rip) +2: + pushq $743 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmSetDatabase + .p2align 4 + .type XrmSetDatabase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmSetDatabase +#endif +XrmSetDatabase: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5952(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5952(%rip) +2: + pushq $744 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmStringToBindingQuarkList + .p2align 4 + .type XrmStringToBindingQuarkList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmStringToBindingQuarkList +#endif +XrmStringToBindingQuarkList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5960(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5960(%rip) +2: + pushq $745 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmStringToQuark + .p2align 4 + .type XrmStringToQuark, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmStringToQuark +#endif +XrmStringToQuark: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5968(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5968(%rip) +2: + pushq $746 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmStringToQuarkList + .p2align 4 + .type XrmStringToQuarkList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmStringToQuarkList +#endif +XrmStringToQuarkList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5976(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5976(%rip) +2: + pushq $747 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XrmUniqueQuark + .p2align 4 + .type XrmUniqueQuark, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XrmUniqueQuark +#endif +XrmUniqueQuark: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5984(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5984(%rip) +2: + pushq $748 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8DrawImageString + .p2align 4 + .type Xutf8DrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8DrawImageString +#endif +Xutf8DrawImageString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+5992(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+5992(%rip) +2: + pushq $749 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8DrawString + .p2align 4 + .type Xutf8DrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8DrawString +#endif +Xutf8DrawString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6000(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6000(%rip) +2: + pushq $750 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8DrawText + .p2align 4 + .type Xutf8DrawText, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8DrawText +#endif +Xutf8DrawText: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6008(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6008(%rip) +2: + pushq $751 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8LookupString + .p2align 4 + .type Xutf8LookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8LookupString +#endif +Xutf8LookupString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6016(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6016(%rip) +2: + pushq $752 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8ResetIC + .p2align 4 + .type Xutf8ResetIC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8ResetIC +#endif +Xutf8ResetIC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6024(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6024(%rip) +2: + pushq $753 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8SetWMProperties + .p2align 4 + .type Xutf8SetWMProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8SetWMProperties +#endif +Xutf8SetWMProperties: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6032(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6032(%rip) +2: + pushq $754 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8TextEscapement + .p2align 4 + .type Xutf8TextEscapement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8TextEscapement +#endif +Xutf8TextEscapement: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6040(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6040(%rip) +2: + pushq $755 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8TextExtents + .p2align 4 + .type Xutf8TextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8TextExtents +#endif +Xutf8TextExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6048(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6048(%rip) +2: + pushq $756 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8TextListToTextProperty + .p2align 4 + .type Xutf8TextListToTextProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8TextListToTextProperty +#endif +Xutf8TextListToTextProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6056(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6056(%rip) +2: + pushq $757 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8TextPerCharExtents + .p2align 4 + .type Xutf8TextPerCharExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8TextPerCharExtents +#endif +Xutf8TextPerCharExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6064(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6064(%rip) +2: + pushq $758 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl Xutf8TextPropertyToTextList + .p2align 4 + .type Xutf8TextPropertyToTextList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden Xutf8TextPropertyToTextList +#endif +Xutf8TextPropertyToTextList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6072(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6072(%rip) +2: + pushq $759 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcDrawImageString + .p2align 4 + .type XwcDrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcDrawImageString +#endif +XwcDrawImageString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6080(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6080(%rip) +2: + pushq $760 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcDrawString + .p2align 4 + .type XwcDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcDrawString +#endif +XwcDrawString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6088(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6088(%rip) +2: + pushq $761 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcDrawText + .p2align 4 + .type XwcDrawText, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcDrawText +#endif +XwcDrawText: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6096(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6096(%rip) +2: + pushq $762 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcFreeStringList + .p2align 4 + .type XwcFreeStringList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcFreeStringList +#endif +XwcFreeStringList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6104(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6104(%rip) +2: + pushq $763 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcLookupString + .p2align 4 + .type XwcLookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcLookupString +#endif +XwcLookupString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6112(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6112(%rip) +2: + pushq $764 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcResetIC + .p2align 4 + .type XwcResetIC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcResetIC +#endif +XwcResetIC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6120(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6120(%rip) +2: + pushq $765 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcTextEscapement + .p2align 4 + .type XwcTextEscapement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcTextEscapement +#endif +XwcTextEscapement: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6128(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6128(%rip) +2: + pushq $766 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcTextExtents + .p2align 4 + .type XwcTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcTextExtents +#endif +XwcTextExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6136(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6136(%rip) +2: + pushq $767 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcTextListToTextProperty + .p2align 4 + .type XwcTextListToTextProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcTextListToTextProperty +#endif +XwcTextListToTextProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6144(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6144(%rip) +2: + pushq $768 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcTextPerCharExtents + .p2align 4 + .type XwcTextPerCharExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcTextPerCharExtents +#endif +XwcTextPerCharExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6152(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6152(%rip) +2: + pushq $769 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XwcTextPropertyToTextList + .p2align 4 + .type XwcTextPropertyToTextList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XwcTextPropertyToTextList +#endif +XwcTextPropertyToTextList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6160(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6160(%rip) +2: + pushq $770 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Utf8GetConvByName + .p2align 4 + .type _Utf8GetConvByName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Utf8GetConvByName +#endif +_Utf8GetConvByName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6168(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6168(%rip) +2: + pushq $771 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XAllocID + .p2align 4 + .type _XAllocID, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XAllocID +#endif +_XAllocID: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6176(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6176(%rip) +2: + pushq $772 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XAllocIDs + .p2align 4 + .type _XAllocIDs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XAllocIDs +#endif +_XAllocIDs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6184(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6184(%rip) +2: + pushq $773 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XAllocScratch + .p2align 4 + .type _XAllocScratch, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XAllocScratch +#endif +_XAllocScratch: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6192(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6192(%rip) +2: + pushq $774 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XAllocTemp + .p2align 4 + .type _XAllocTemp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XAllocTemp +#endif +_XAllocTemp: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6200(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6200(%rip) +2: + pushq $775 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XAsyncErrorHandler + .p2align 4 + .type _XAsyncErrorHandler, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XAsyncErrorHandler +#endif +_XAsyncErrorHandler: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6208(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6208(%rip) +2: + pushq $776 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XCloseLC + .p2align 4 + .type _XCloseLC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XCloseLC +#endif +_XCloseLC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6216(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6216(%rip) +2: + pushq $777 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XColor_to_XcmsRGB + .p2align 4 + .type _XColor_to_XcmsRGB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XColor_to_XcmsRGB +#endif +_XColor_to_XcmsRGB: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6224(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6224(%rip) +2: + pushq $778 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XConnectXCB + .p2align 4 + .type _XConnectXCB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XConnectXCB +#endif +_XConnectXCB: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6232(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6232(%rip) +2: + pushq $779 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XCopyEventCookie + .p2align 4 + .type _XCopyEventCookie, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XCopyEventCookie +#endif +_XCopyEventCookie: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6240(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6240(%rip) +2: + pushq $780 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XCopyToArg + .p2align 4 + .type _XCopyToArg, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XCopyToArg +#endif +_XCopyToArg: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6248(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6248(%rip) +2: + pushq $781 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XData32 + .p2align 4 + .type _XData32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XData32 +#endif +_XData32: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6256(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6256(%rip) +2: + pushq $782 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XDefaultError + .p2align 4 + .type _XDefaultError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XDefaultError +#endif +_XDefaultError: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6264(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6264(%rip) +2: + pushq $783 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XDefaultIOError + .p2align 4 + .type _XDefaultIOError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XDefaultIOError +#endif +_XDefaultIOError: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6272(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6272(%rip) +2: + pushq $784 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XDefaultIOErrorExit + .p2align 4 + .type _XDefaultIOErrorExit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XDefaultIOErrorExit +#endif +_XDefaultIOErrorExit: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6280(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6280(%rip) +2: + pushq $785 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XDefaultOpenIM + .p2align 4 + .type _XDefaultOpenIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XDefaultOpenIM +#endif +_XDefaultOpenIM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6288(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6288(%rip) +2: + pushq $786 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XDefaultOpenOM + .p2align 4 + .type _XDefaultOpenOM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XDefaultOpenOM +#endif +_XDefaultOpenOM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6296(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6296(%rip) +2: + pushq $787 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XDefaultWireError + .p2align 4 + .type _XDefaultWireError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XDefaultWireError +#endif +_XDefaultWireError: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6304(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6304(%rip) +2: + pushq $788 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XDeq + .p2align 4 + .type _XDeq, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XDeq +#endif +_XDeq: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6312(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6312(%rip) +2: + pushq $789 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XDeqAsyncHandler + .p2align 4 + .type _XDeqAsyncHandler, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XDeqAsyncHandler +#endif +_XDeqAsyncHandler: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6320(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6320(%rip) +2: + pushq $790 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XEatData + .p2align 4 + .type _XEatData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XEatData +#endif +_XEatData: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6328(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6328(%rip) +2: + pushq $791 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XEatDataWords + .p2align 4 + .type _XEatDataWords, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XEatDataWords +#endif +_XEatDataWords: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6336(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6336(%rip) +2: + pushq $792 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XEnq + .p2align 4 + .type _XEnq, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XEnq +#endif +_XEnq: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6344(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6344(%rip) +2: + pushq $793 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XError + .p2align 4 + .type _XError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XError +#endif +_XError: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6352(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6352(%rip) +2: + pushq $794 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XEventToWire + .p2align 4 + .type _XEventToWire, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XEventToWire +#endif +_XEventToWire: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6360(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6360(%rip) +2: + pushq $795 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XEventsQueued + .p2align 4 + .type _XEventsQueued, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XEventsQueued +#endif +_XEventsQueued: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6368(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6368(%rip) +2: + pushq $796 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XF86BigfontFreeFontMetrics + .p2align 4 + .type _XF86BigfontFreeFontMetrics, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XF86BigfontFreeFontMetrics +#endif +_XF86BigfontFreeFontMetrics: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6376(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6376(%rip) +2: + pushq $797 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XF86LoadQueryLocaleFont + .p2align 4 + .type _XF86LoadQueryLocaleFont, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XF86LoadQueryLocaleFont +#endif +_XF86LoadQueryLocaleFont: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6384(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6384(%rip) +2: + pushq $798 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XFetchEventCookie + .p2align 4 + .type _XFetchEventCookie, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XFetchEventCookie +#endif +_XFetchEventCookie: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6392(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6392(%rip) +2: + pushq $799 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XFlush + .p2align 4 + .type _XFlush, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XFlush +#endif +_XFlush: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6400(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6400(%rip) +2: + pushq $800 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XFlushGCCache + .p2align 4 + .type _XFlushGCCache, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XFlushGCCache +#endif +_XFlushGCCache: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6408(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6408(%rip) +2: + pushq $801 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XFreeAtomTable + .p2align 4 + .type _XFreeAtomTable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XFreeAtomTable +#endif +_XFreeAtomTable: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6416(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6416(%rip) +2: + pushq $802 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XFreeDisplayStructure + .p2align 4 + .type _XFreeDisplayStructure, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XFreeDisplayStructure +#endif +_XFreeDisplayStructure: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6424(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6424(%rip) +2: + pushq $803 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XFreeEventCookies + .p2align 4 + .type _XFreeEventCookies, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XFreeEventCookies +#endif +_XFreeEventCookies: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6432(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6432(%rip) +2: + pushq $804 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XFreeExtData + .p2align 4 + .type _XFreeExtData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XFreeExtData +#endif +_XFreeExtData: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6440(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6440(%rip) +2: + pushq $805 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XFreeTemp + .p2align 4 + .type _XFreeTemp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XFreeTemp +#endif +_XFreeTemp: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6448(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6448(%rip) +2: + pushq $806 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XFreeX11XCBStructure + .p2align 4 + .type _XFreeX11XCBStructure, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XFreeX11XCBStructure +#endif +_XFreeX11XCBStructure: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6456(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6456(%rip) +2: + pushq $807 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XGetAsyncData + .p2align 4 + .type _XGetAsyncData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XGetAsyncData +#endif +_XGetAsyncData: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6464(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6464(%rip) +2: + pushq $808 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XGetAsyncReply + .p2align 4 + .type _XGetAsyncReply, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XGetAsyncReply +#endif +_XGetAsyncReply: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6472(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6472(%rip) +2: + pushq $809 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XGetBitsPerPixel + .p2align 4 + .type _XGetBitsPerPixel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XGetBitsPerPixel +#endif +_XGetBitsPerPixel: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6480(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6480(%rip) +2: + pushq $810 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XGetHostname + .p2align 4 + .type _XGetHostname, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XGetHostname +#endif +_XGetHostname: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6488(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6488(%rip) +2: + pushq $811 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XGetLCValues + .p2align 4 + .type _XGetLCValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XGetLCValues +#endif +_XGetLCValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6496(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6496(%rip) +2: + pushq $812 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XGetRequest + .p2align 4 + .type _XGetRequest, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XGetRequest +#endif +_XGetRequest: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6504(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6504(%rip) +2: + pushq $813 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XGetScanlinePad + .p2align 4 + .type _XGetScanlinePad, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XGetScanlinePad +#endif +_XGetScanlinePad: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6512(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6512(%rip) +2: + pushq $814 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XGetWindowAttributes + .p2align 4 + .type _XGetWindowAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XGetWindowAttributes +#endif +_XGetWindowAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6520(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6520(%rip) +2: + pushq $815 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XIMCompileResourceList + .p2align 4 + .type _XIMCompileResourceList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XIMCompileResourceList +#endif +_XIMCompileResourceList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6528(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6528(%rip) +2: + pushq $816 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XIOError + .p2align 4 + .type _XIOError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XIOError +#endif +_XIOError: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6536(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6536(%rip) +2: + pushq $817 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XInitIM + .p2align 4 + .type _XInitIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XInitIM +#endif +_XInitIM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6544(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6544(%rip) +2: + pushq $818 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XInitImageFuncPtrs + .p2align 4 + .type _XInitImageFuncPtrs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XInitImageFuncPtrs +#endif +_XInitImageFuncPtrs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6552(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6552(%rip) +2: + pushq $819 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XInitKeysymDB + .p2align 4 + .type _XInitKeysymDB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XInitKeysymDB +#endif +_XInitKeysymDB: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6560(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6560(%rip) +2: + pushq $820 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XInitOM + .p2align 4 + .type _XInitOM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XInitOM +#endif +_XInitOM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6568(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6568(%rip) +2: + pushq $821 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XIsEventCookie + .p2align 4 + .type _XIsEventCookie, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XIsEventCookie +#endif +_XIsEventCookie: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6576(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6576(%rip) +2: + pushq $822 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XKeyInitialize + .p2align 4 + .type _XKeyInitialize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XKeyInitialize +#endif +_XKeyInitialize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6584(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6584(%rip) +2: + pushq $823 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XKeycodeToKeysym + .p2align 4 + .type _XKeycodeToKeysym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XKeycodeToKeysym +#endif +_XKeycodeToKeysym: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6592(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6592(%rip) +2: + pushq $824 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XKeysymToKeycode + .p2align 4 + .type _XKeysymToKeycode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XKeysymToKeycode +#endif +_XKeysymToKeycode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6600(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6600(%rip) +2: + pushq $825 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XKeysymToModifiers + .p2align 4 + .type _XKeysymToModifiers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XKeysymToModifiers +#endif +_XKeysymToModifiers: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6608(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6608(%rip) +2: + pushq $826 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XLookupKeysym + .p2align 4 + .type _XLookupKeysym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XLookupKeysym +#endif +_XLookupKeysym: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6616(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6616(%rip) +2: + pushq $827 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XLookupString + .p2align 4 + .type _XLookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XLookupString +#endif +_XLookupString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6624(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6624(%rip) +2: + pushq $828 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XNoticeCreateBitmap + .p2align 4 + .type _XNoticeCreateBitmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XNoticeCreateBitmap +#endif +_XNoticeCreateBitmap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6632(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6632(%rip) +2: + pushq $829 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XNoticePutBitmap + .p2align 4 + .type _XNoticePutBitmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XNoticePutBitmap +#endif +_XNoticePutBitmap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6640(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6640(%rip) +2: + pushq $830 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XOpenLC + .p2align 4 + .type _XOpenLC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XOpenLC +#endif +_XOpenLC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6648(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6648(%rip) +2: + pushq $831 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XParseBaseFontNameList + .p2align 4 + .type _XParseBaseFontNameList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XParseBaseFontNameList +#endif +_XParseBaseFontNameList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6656(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6656(%rip) +2: + pushq $832 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XPollfdCacheAdd + .p2align 4 + .type _XPollfdCacheAdd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XPollfdCacheAdd +#endif +_XPollfdCacheAdd: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6664(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6664(%rip) +2: + pushq $833 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XPollfdCacheDel + .p2align 4 + .type _XPollfdCacheDel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XPollfdCacheDel +#endif +_XPollfdCacheDel: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6672(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6672(%rip) +2: + pushq $834 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XPollfdCacheInit + .p2align 4 + .type _XPollfdCacheInit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XPollfdCacheInit +#endif +_XPollfdCacheInit: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6680(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6680(%rip) +2: + pushq $835 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XProcessInternalConnection + .p2align 4 + .type _XProcessInternalConnection, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XProcessInternalConnection +#endif +_XProcessInternalConnection: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6688(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6688(%rip) +2: + pushq $836 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XProcessWindowAttributes + .p2align 4 + .type _XProcessWindowAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XProcessWindowAttributes +#endif +_XProcessWindowAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6696(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6696(%rip) +2: + pushq $837 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XPutBackEvent + .p2align 4 + .type _XPutBackEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XPutBackEvent +#endif +_XPutBackEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6704(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6704(%rip) +2: + pushq $838 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XRead + .p2align 4 + .type _XRead, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XRead +#endif +_XRead: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6712(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6712(%rip) +2: + pushq $839 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XRead32 + .p2align 4 + .type _XRead32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XRead32 +#endif +_XRead32: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6720(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6720(%rip) +2: + pushq $840 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XReadEvents + .p2align 4 + .type _XReadEvents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XReadEvents +#endif +_XReadEvents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6728(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6728(%rip) +2: + pushq $841 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XReadPad + .p2align 4 + .type _XReadPad, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XReadPad +#endif +_XReadPad: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6736(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6736(%rip) +2: + pushq $842 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XRefreshKeyboardMapping + .p2align 4 + .type _XRefreshKeyboardMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XRefreshKeyboardMapping +#endif +_XRefreshKeyboardMapping: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6744(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6744(%rip) +2: + pushq $843 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XRegisterFilterByMask + .p2align 4 + .type _XRegisterFilterByMask, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XRegisterFilterByMask +#endif +_XRegisterFilterByMask: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6752(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6752(%rip) +2: + pushq $844 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XRegisterFilterByType + .p2align 4 + .type _XRegisterFilterByType, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XRegisterFilterByType +#endif +_XRegisterFilterByType: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6760(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6760(%rip) +2: + pushq $845 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XRegisterInternalConnection + .p2align 4 + .type _XRegisterInternalConnection, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XRegisterInternalConnection +#endif +_XRegisterInternalConnection: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6768(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6768(%rip) +2: + pushq $846 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XReply + .p2align 4 + .type _XReply, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XReply +#endif +_XReply: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6776(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6776(%rip) +2: + pushq $847 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XReverse_Bytes + .p2align 4 + .type _XReverse_Bytes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XReverse_Bytes +#endif +_XReverse_Bytes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6784(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6784(%rip) +2: + pushq $848 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XScreenOfWindow + .p2align 4 + .type _XScreenOfWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XScreenOfWindow +#endif +_XScreenOfWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6792(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6792(%rip) +2: + pushq $849 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XSend + .p2align 4 + .type _XSend, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XSend +#endif +_XSend: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6800(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6800(%rip) +2: + pushq $850 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XSetClipRectangles + .p2align 4 + .type _XSetClipRectangles, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XSetClipRectangles +#endif +_XSetClipRectangles: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6808(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6808(%rip) +2: + pushq $851 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XSetImage + .p2align 4 + .type _XSetImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XSetImage +#endif +_XSetImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6816(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6816(%rip) +2: + pushq $852 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XSetLastRequestRead + .p2align 4 + .type _XSetLastRequestRead, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XSetLastRequestRead +#endif +_XSetLastRequestRead: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6824(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6824(%rip) +2: + pushq $853 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XStoreEventCookie + .p2align 4 + .type _XStoreEventCookie, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XStoreEventCookie +#endif +_XStoreEventCookie: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6832(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6832(%rip) +2: + pushq $854 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XTextHeight + .p2align 4 + .type _XTextHeight, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XTextHeight +#endif +_XTextHeight: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6840(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6840(%rip) +2: + pushq $855 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XTextHeight16 + .p2align 4 + .type _XTextHeight16, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XTextHeight16 +#endif +_XTextHeight16: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6848(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6848(%rip) +2: + pushq $856 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XTranslateKey + .p2align 4 + .type _XTranslateKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XTranslateKey +#endif +_XTranslateKey: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6856(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6856(%rip) +2: + pushq $857 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XTranslateKeySym + .p2align 4 + .type _XTranslateKeySym, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XTranslateKeySym +#endif +_XTranslateKeySym: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6864(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6864(%rip) +2: + pushq $858 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XTryShapeBitmapCursor + .p2align 4 + .type _XTryShapeBitmapCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XTryShapeBitmapCursor +#endif +_XTryShapeBitmapCursor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6872(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6872(%rip) +2: + pushq $859 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XUnknownCopyEventCookie + .p2align 4 + .type _XUnknownCopyEventCookie, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XUnknownCopyEventCookie +#endif +_XUnknownCopyEventCookie: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6880(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6880(%rip) +2: + pushq $860 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XUnknownNativeEvent + .p2align 4 + .type _XUnknownNativeEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XUnknownNativeEvent +#endif +_XUnknownNativeEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6888(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6888(%rip) +2: + pushq $861 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XUnknownWireEvent + .p2align 4 + .type _XUnknownWireEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XUnknownWireEvent +#endif +_XUnknownWireEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6896(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6896(%rip) +2: + pushq $862 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XUnknownWireEventCookie + .p2align 4 + .type _XUnknownWireEventCookie, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XUnknownWireEventCookie +#endif +_XUnknownWireEventCookie: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6904(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6904(%rip) +2: + pushq $863 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XUnregisterFilter + .p2align 4 + .type _XUnregisterFilter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XUnregisterFilter +#endif +_XUnregisterFilter: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6912(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6912(%rip) +2: + pushq $864 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XUnregisterInternalConnection + .p2align 4 + .type _XUnregisterInternalConnection, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XUnregisterInternalConnection +#endif +_XUnregisterInternalConnection: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6920(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6920(%rip) +2: + pushq $865 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XUnresolveColor + .p2align 4 + .type _XUnresolveColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XUnresolveColor +#endif +_XUnresolveColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6928(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6928(%rip) +2: + pushq $866 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XUpdateAtomCache + .p2align 4 + .type _XUpdateAtomCache, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XUpdateAtomCache +#endif +_XUpdateAtomCache: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6936(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6936(%rip) +2: + pushq $867 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XUpdateGCCache + .p2align 4 + .type _XUpdateGCCache, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XUpdateGCCache +#endif +_XUpdateGCCache: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6944(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6944(%rip) +2: + pushq $868 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XVIDtoVisual + .p2align 4 + .type _XVIDtoVisual, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XVIDtoVisual +#endif +_XVIDtoVisual: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6952(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6952(%rip) +2: + pushq $869 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XWireToEvent + .p2align 4 + .type _XWireToEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XWireToEvent +#endif +_XWireToEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6960(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6960(%rip) +2: + pushq $870 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsAddCmapRec + .p2align 4 + .type _XcmsAddCmapRec, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsAddCmapRec +#endif +_XcmsAddCmapRec: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6968(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6968(%rip) +2: + pushq $871 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsArcTangent + .p2align 4 + .type _XcmsArcTangent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsArcTangent +#endif +_XcmsArcTangent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6976(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6976(%rip) +2: + pushq $872 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsCIELabQueryMaxLCRGB + .p2align 4 + .type _XcmsCIELabQueryMaxLCRGB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsCIELabQueryMaxLCRGB +#endif +_XcmsCIELabQueryMaxLCRGB: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6984(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6984(%rip) +2: + pushq $873 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsCIELuvQueryMaxLCRGB + .p2align 4 + .type _XcmsCIELuvQueryMaxLCRGB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsCIELuvQueryMaxLCRGB +#endif +_XcmsCIELuvQueryMaxLCRGB: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+6992(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+6992(%rip) +2: + pushq $874 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsCIEXYZ_ValidSpec + .p2align 4 + .type _XcmsCIEXYZ_ValidSpec, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsCIEXYZ_ValidSpec +#endif +_XcmsCIEXYZ_ValidSpec: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7000(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7000(%rip) +2: + pushq $875 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsCIEuvY_ValidSpec + .p2align 4 + .type _XcmsCIEuvY_ValidSpec, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsCIEuvY_ValidSpec +#endif +_XcmsCIEuvY_ValidSpec: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7008(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7008(%rip) +2: + pushq $876 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsConvertColorsWithWhitePt + .p2align 4 + .type _XcmsConvertColorsWithWhitePt, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsConvertColorsWithWhitePt +#endif +_XcmsConvertColorsWithWhitePt: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7016(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7016(%rip) +2: + pushq $877 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsCopyCmapRecAndFree + .p2align 4 + .type _XcmsCopyCmapRecAndFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsCopyCmapRecAndFree +#endif +_XcmsCopyCmapRecAndFree: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7024(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7024(%rip) +2: + pushq $878 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsCopyISOLatin1Lowered + .p2align 4 + .type _XcmsCopyISOLatin1Lowered, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsCopyISOLatin1Lowered +#endif +_XcmsCopyISOLatin1Lowered: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7032(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7032(%rip) +2: + pushq $879 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsCopyPointerArray + .p2align 4 + .type _XcmsCopyPointerArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsCopyPointerArray +#endif +_XcmsCopyPointerArray: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7040(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7040(%rip) +2: + pushq $880 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsCosine + .p2align 4 + .type _XcmsCosine, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsCosine +#endif +_XcmsCosine: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7048(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7048(%rip) +2: + pushq $881 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsCubeRoot + .p2align 4 + .type _XcmsCubeRoot, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsCubeRoot +#endif +_XcmsCubeRoot: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7056(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7056(%rip) +2: + pushq $882 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsDDConvertColors + .p2align 4 + .type _XcmsDDConvertColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsDDConvertColors +#endif +_XcmsDDConvertColors: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7064(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7064(%rip) +2: + pushq $883 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsDIConvertColors + .p2align 4 + .type _XcmsDIConvertColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsDIConvertColors +#endif +_XcmsDIConvertColors: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7072(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7072(%rip) +2: + pushq $884 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsDeleteCmapRec + .p2align 4 + .type _XcmsDeleteCmapRec, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsDeleteCmapRec +#endif +_XcmsDeleteCmapRec: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7080(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7080(%rip) +2: + pushq $885 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsEqualWhitePts + .p2align 4 + .type _XcmsEqualWhitePts, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsEqualWhitePts +#endif +_XcmsEqualWhitePts: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7088(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7088(%rip) +2: + pushq $886 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsFreeIntensityMaps + .p2align 4 + .type _XcmsFreeIntensityMaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsFreeIntensityMaps +#endif +_XcmsFreeIntensityMaps: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7096(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7096(%rip) +2: + pushq $887 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsFreePointerArray + .p2align 4 + .type _XcmsFreePointerArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsFreePointerArray +#endif +_XcmsFreePointerArray: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7104(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7104(%rip) +2: + pushq $888 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsGetElement + .p2align 4 + .type _XcmsGetElement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsGetElement +#endif +_XcmsGetElement: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7112(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7112(%rip) +2: + pushq $889 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsGetIntensityMap + .p2align 4 + .type _XcmsGetIntensityMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsGetIntensityMap +#endif +_XcmsGetIntensityMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7120(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7120(%rip) +2: + pushq $890 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsGetProperty + .p2align 4 + .type _XcmsGetProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsGetProperty +#endif +_XcmsGetProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7128(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7128(%rip) +2: + pushq $891 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsInitDefaultCCCs + .p2align 4 + .type _XcmsInitDefaultCCCs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsInitDefaultCCCs +#endif +_XcmsInitDefaultCCCs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7136(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7136(%rip) +2: + pushq $892 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsInitScrnInfo + .p2align 4 + .type _XcmsInitScrnInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsInitScrnInfo +#endif +_XcmsInitScrnInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7144(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7144(%rip) +2: + pushq $893 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsLRGB_InitScrnDefault + .p2align 4 + .type _XcmsLRGB_InitScrnDefault, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsLRGB_InitScrnDefault +#endif +_XcmsLRGB_InitScrnDefault: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7152(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7152(%rip) +2: + pushq $894 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsPushPointerArray + .p2align 4 + .type _XcmsPushPointerArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsPushPointerArray +#endif +_XcmsPushPointerArray: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7160(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7160(%rip) +2: + pushq $895 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsRGB_to_XColor + .p2align 4 + .type _XcmsRGB_to_XColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsRGB_to_XColor +#endif +_XcmsRGB_to_XColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7168(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7168(%rip) +2: + pushq $896 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsRegFormatOfPrefix + .p2align 4 + .type _XcmsRegFormatOfPrefix, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsRegFormatOfPrefix +#endif +_XcmsRegFormatOfPrefix: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7176(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7176(%rip) +2: + pushq $897 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsResolveColor + .p2align 4 + .type _XcmsResolveColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsResolveColor +#endif +_XcmsResolveColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7184(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7184(%rip) +2: + pushq $898 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsResolveColorString + .p2align 4 + .type _XcmsResolveColorString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsResolveColorString +#endif +_XcmsResolveColorString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7192(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7192(%rip) +2: + pushq $899 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsSetGetColor + .p2align 4 + .type _XcmsSetGetColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsSetGetColor +#endif +_XcmsSetGetColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7200(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7200(%rip) +2: + pushq $900 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsSetGetColors + .p2align 4 + .type _XcmsSetGetColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsSetGetColors +#endif +_XcmsSetGetColors: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7208(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7208(%rip) +2: + pushq $901 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsSine + .p2align 4 + .type _XcmsSine, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsSine +#endif +_XcmsSine: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7216(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7216(%rip) +2: + pushq $902 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsSquareRoot + .p2align 4 + .type _XcmsSquareRoot, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsSquareRoot +#endif +_XcmsSquareRoot: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7224(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7224(%rip) +2: + pushq $903 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsTekHVCQueryMaxVCRGB + .p2align 4 + .type _XcmsTekHVCQueryMaxVCRGB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsTekHVCQueryMaxVCRGB +#endif +_XcmsTekHVCQueryMaxVCRGB: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7232(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7232(%rip) +2: + pushq $904 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsTekHVC_CheckModify + .p2align 4 + .type _XcmsTekHVC_CheckModify, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsTekHVC_CheckModify +#endif +_XcmsTekHVC_CheckModify: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7240(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7240(%rip) +2: + pushq $905 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XcmsUnresolveColor + .p2align 4 + .type _XcmsUnresolveColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XcmsUnresolveColor +#endif +_XcmsUnresolveColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7248(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7248(%rip) +2: + pushq $906 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimCbDispatch + .p2align 4 + .type _XimCbDispatch, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimCbDispatch +#endif +_XimCbDispatch: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7256(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7256(%rip) +2: + pushq $907 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimCheckCreateICValues + .p2align 4 + .type _XimCheckCreateICValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimCheckCreateICValues +#endif +_XimCheckCreateICValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7264(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7264(%rip) +2: + pushq $908 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimCheckDataSize + .p2align 4 + .type _XimCheckDataSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimCheckDataSize +#endif +_XimCheckDataSize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7272(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7272(%rip) +2: + pushq $909 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimCheckICMode + .p2align 4 + .type _XimCheckICMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimCheckICMode +#endif +_XimCheckICMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7280(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7280(%rip) +2: + pushq $910 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimCheckIMMode + .p2align 4 + .type _XimCheckIMMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimCheckIMMode +#endif +_XimCheckIMMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7288(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7288(%rip) +2: + pushq $911 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimCheckIfLocalProcessing + .p2align 4 + .type _XimCheckIfLocalProcessing, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimCheckIfLocalProcessing +#endif +_XimCheckIfLocalProcessing: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7296(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7296(%rip) +2: + pushq $912 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimCheckIfThaiProcessing + .p2align 4 + .type _XimCheckIfThaiProcessing, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimCheckIfThaiProcessing +#endif +_XimCheckIfThaiProcessing: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7304(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7304(%rip) +2: + pushq $913 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimCheckLocalInputStyle + .p2align 4 + .type _XimCheckLocalInputStyle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimCheckLocalInputStyle +#endif +_XimCheckLocalInputStyle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7312(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7312(%rip) +2: + pushq $914 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimCommitCallback + .p2align 4 + .type _XimCommitCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimCommitCallback +#endif +_XimCommitCallback: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7320(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7320(%rip) +2: + pushq $915 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimConnect + .p2align 4 + .type _XimConnect, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimConnect +#endif +_XimConnect: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7328(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7328(%rip) +2: + pushq $916 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimDecodeICATTRIBUTE + .p2align 4 + .type _XimDecodeICATTRIBUTE, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimDecodeICATTRIBUTE +#endif +_XimDecodeICATTRIBUTE: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7336(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7336(%rip) +2: + pushq $917 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimDecodeIMATTRIBUTE + .p2align 4 + .type _XimDecodeIMATTRIBUTE, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimDecodeIMATTRIBUTE +#endif +_XimDecodeIMATTRIBUTE: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7344(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7344(%rip) +2: + pushq $918 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimDecodeLocalICAttr + .p2align 4 + .type _XimDecodeLocalICAttr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimDecodeLocalICAttr +#endif +_XimDecodeLocalICAttr: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7352(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7352(%rip) +2: + pushq $919 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimDecodeLocalIMAttr + .p2align 4 + .type _XimDecodeLocalIMAttr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimDecodeLocalIMAttr +#endif +_XimDecodeLocalIMAttr: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7360(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7360(%rip) +2: + pushq $920 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimDestroyIMStructureList + .p2align 4 + .type _XimDestroyIMStructureList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimDestroyIMStructureList +#endif +_XimDestroyIMStructureList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7368(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7368(%rip) +2: + pushq $921 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimDispatchInit + .p2align 4 + .type _XimDispatchInit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimDispatchInit +#endif +_XimDispatchInit: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7376(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7376(%rip) +2: + pushq $922 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimEncodeICATTRIBUTE + .p2align 4 + .type _XimEncodeICATTRIBUTE, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimEncodeICATTRIBUTE +#endif +_XimEncodeICATTRIBUTE: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7384(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7384(%rip) +2: + pushq $923 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimEncodeIMATTRIBUTE + .p2align 4 + .type _XimEncodeIMATTRIBUTE, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimEncodeIMATTRIBUTE +#endif +_XimEncodeIMATTRIBUTE: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7392(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7392(%rip) +2: + pushq $924 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimEncodeLocalICAttr + .p2align 4 + .type _XimEncodeLocalICAttr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimEncodeLocalICAttr +#endif +_XimEncodeLocalICAttr: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7400(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7400(%rip) +2: + pushq $925 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimEncodeLocalIMAttr + .p2align 4 + .type _XimEncodeLocalIMAttr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimEncodeLocalIMAttr +#endif +_XimEncodeLocalIMAttr: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7408(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7408(%rip) +2: + pushq $926 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimError + .p2align 4 + .type _XimError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimError +#endif +_XimError: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7416(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7416(%rip) +2: + pushq $927 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimErrorCallback + .p2align 4 + .type _XimErrorCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimErrorCallback +#endif +_XimErrorCallback: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7424(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7424(%rip) +2: + pushq $928 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimExtension + .p2align 4 + .type _XimExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimExtension +#endif +_XimExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7432(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7432(%rip) +2: + pushq $929 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimFilterWaitEvent + .p2align 4 + .type _XimFilterWaitEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimFilterWaitEvent +#endif +_XimFilterWaitEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7440(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7440(%rip) +2: + pushq $930 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimFlush + .p2align 4 + .type _XimFlush, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimFlush +#endif +_XimFlush: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7448(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7448(%rip) +2: + pushq $931 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimForwardEvent + .p2align 4 + .type _XimForwardEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimForwardEvent +#endif +_XimForwardEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7456(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7456(%rip) +2: + pushq $932 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimForwardEventCallback + .p2align 4 + .type _XimForwardEventCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimForwardEventCallback +#endif +_XimForwardEventCallback: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7464(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7464(%rip) +2: + pushq $933 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimFreeCommitInfo + .p2align 4 + .type _XimFreeCommitInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimFreeCommitInfo +#endif +_XimFreeCommitInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7472(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7472(%rip) +2: + pushq $934 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimFreeProtoIntrCallback + .p2align 4 + .type _XimFreeProtoIntrCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimFreeProtoIntrCallback +#endif +_XimFreeProtoIntrCallback: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7480(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7480(%rip) +2: + pushq $935 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimFreeTransIntrCallback + .p2align 4 + .type _XimFreeTransIntrCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimFreeTransIntrCallback +#endif +_XimFreeTransIntrCallback: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7488(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7488(%rip) +2: + pushq $936 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetAttributeID + .p2align 4 + .type _XimGetAttributeID, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetAttributeID +#endif +_XimGetAttributeID: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7496(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7496(%rip) +2: + pushq $937 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetCharCode + .p2align 4 + .type _XimGetCharCode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetCharCode +#endif +_XimGetCharCode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7504(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7504(%rip) +2: + pushq $938 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetCurrentICValues + .p2align 4 + .type _XimGetCurrentICValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetCurrentICValues +#endif +_XimGetCurrentICValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7512(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7512(%rip) +2: + pushq $939 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetCurrentIMValues + .p2align 4 + .type _XimGetCurrentIMValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetCurrentIMValues +#endif +_XimGetCurrentIMValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7520(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7520(%rip) +2: + pushq $940 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetICValueData + .p2align 4 + .type _XimGetICValueData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetICValueData +#endif +_XimGetICValueData: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7528(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7528(%rip) +2: + pushq $941 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetIMValueData + .p2align 4 + .type _XimGetIMValueData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetIMValueData +#endif +_XimGetIMValueData: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7536(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7536(%rip) +2: + pushq $942 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetLocaleCode + .p2align 4 + .type _XimGetLocaleCode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetLocaleCode +#endif +_XimGetLocaleCode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7544(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7544(%rip) +2: + pushq $943 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetMyEndian + .p2align 4 + .type _XimGetMyEndian, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetMyEndian +#endif +_XimGetMyEndian: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7552(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7552(%rip) +2: + pushq $944 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetResourceListRec + .p2align 4 + .type _XimGetResourceListRec, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetResourceListRec +#endif +_XimGetResourceListRec: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7560(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7560(%rip) +2: + pushq $945 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetResourceListRecByQuark + .p2align 4 + .type _XimGetResourceListRecByQuark, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetResourceListRecByQuark +#endif +_XimGetResourceListRecByQuark: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7568(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7568(%rip) +2: + pushq $946 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimGetWindowEventmask + .p2align 4 + .type _XimGetWindowEventmask, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimGetWindowEventmask +#endif +_XimGetWindowEventmask: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7576(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7576(%rip) +2: + pushq $947 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimICOfXICID + .p2align 4 + .type _XimICOfXICID, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimICOfXICID +#endif +_XimICOfXICID: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7584(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7584(%rip) +2: + pushq $948 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimInitialResourceInfo + .p2align 4 + .type _XimInitialResourceInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimInitialResourceInfo +#endif +_XimInitialResourceInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7592(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7592(%rip) +2: + pushq $949 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLcctstombs + .p2align 4 + .type _XimLcctstombs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLcctstombs +#endif +_XimLcctstombs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7600(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7600(%rip) +2: + pushq $950 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLcctstoutf8 + .p2align 4 + .type _XimLcctstoutf8, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLcctstoutf8 +#endif +_XimLcctstoutf8: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7608(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7608(%rip) +2: + pushq $951 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLcctstowcs + .p2align 4 + .type _XimLcctstowcs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLcctstowcs +#endif +_XimLcctstowcs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7616(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7616(%rip) +2: + pushq $952 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalCreateIC + .p2align 4 + .type _XimLocalCreateIC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalCreateIC +#endif +_XimLocalCreateIC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7624(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7624(%rip) +2: + pushq $953 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalFilter + .p2align 4 + .type _XimLocalFilter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalFilter +#endif +_XimLocalFilter: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7632(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7632(%rip) +2: + pushq $954 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalGetICValues + .p2align 4 + .type _XimLocalGetICValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalGetICValues +#endif +_XimLocalGetICValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7640(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7640(%rip) +2: + pushq $955 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalGetIMValues + .p2align 4 + .type _XimLocalGetIMValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalGetIMValues +#endif +_XimLocalGetIMValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7648(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7648(%rip) +2: + pushq $956 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalIMFree + .p2align 4 + .type _XimLocalIMFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalIMFree +#endif +_XimLocalIMFree: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7656(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7656(%rip) +2: + pushq $957 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalMbLookupString + .p2align 4 + .type _XimLocalMbLookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalMbLookupString +#endif +_XimLocalMbLookupString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7664(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7664(%rip) +2: + pushq $958 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalOpenIM + .p2align 4 + .type _XimLocalOpenIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalOpenIM +#endif +_XimLocalOpenIM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7672(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7672(%rip) +2: + pushq $959 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalSetICValues + .p2align 4 + .type _XimLocalSetICValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalSetICValues +#endif +_XimLocalSetICValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7680(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7680(%rip) +2: + pushq $960 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalSetIMValues + .p2align 4 + .type _XimLocalSetIMValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalSetIMValues +#endif +_XimLocalSetIMValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7688(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7688(%rip) +2: + pushq $961 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalUtf8LookupString + .p2align 4 + .type _XimLocalUtf8LookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalUtf8LookupString +#endif +_XimLocalUtf8LookupString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7696(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7696(%rip) +2: + pushq $962 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLocalWcLookupString + .p2align 4 + .type _XimLocalWcLookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLocalWcLookupString +#endif +_XimLocalWcLookupString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7704(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7704(%rip) +2: + pushq $963 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLookupMBText + .p2align 4 + .type _XimLookupMBText, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLookupMBText +#endif +_XimLookupMBText: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7712(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7712(%rip) +2: + pushq $964 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLookupUTF8Text + .p2align 4 + .type _XimLookupUTF8Text, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLookupUTF8Text +#endif +_XimLookupUTF8Text: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7720(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7720(%rip) +2: + pushq $965 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimLookupWCText + .p2align 4 + .type _XimLookupWCText, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimLookupWCText +#endif +_XimLookupWCText: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7728(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7728(%rip) +2: + pushq $966 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimMakeICAttrIDList + .p2align 4 + .type _XimMakeICAttrIDList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimMakeICAttrIDList +#endif +_XimMakeICAttrIDList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7736(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7736(%rip) +2: + pushq $967 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimMakeIMAttrIDList + .p2align 4 + .type _XimMakeIMAttrIDList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimMakeIMAttrIDList +#endif +_XimMakeIMAttrIDList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7744(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7744(%rip) +2: + pushq $968 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimOpenIM + .p2align 4 + .type _XimOpenIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimOpenIM +#endif +_XimOpenIM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7752(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7752(%rip) +2: + pushq $969 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimParseStringFile + .p2align 4 + .type _XimParseStringFile, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimParseStringFile +#endif +_XimParseStringFile: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7760(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7760(%rip) +2: + pushq $970 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProcError + .p2align 4 + .type _XimProcError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProcError +#endif +_XimProcError: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7768(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7768(%rip) +2: + pushq $971 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProcSyncReply + .p2align 4 + .type _XimProcSyncReply, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProcSyncReply +#endif +_XimProcSyncReply: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7776(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7776(%rip) +2: + pushq $972 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProtoCreateIC + .p2align 4 + .type _XimProtoCreateIC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProtoCreateIC +#endif +_XimProtoCreateIC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7784(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7784(%rip) +2: + pushq $973 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProtoEventToWire + .p2align 4 + .type _XimProtoEventToWire, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProtoEventToWire +#endif +_XimProtoEventToWire: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7792(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7792(%rip) +2: + pushq $974 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProtoIMFree + .p2align 4 + .type _XimProtoIMFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProtoIMFree +#endif +_XimProtoIMFree: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7800(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7800(%rip) +2: + pushq $975 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProtoMbLookupString + .p2align 4 + .type _XimProtoMbLookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProtoMbLookupString +#endif +_XimProtoMbLookupString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7808(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7808(%rip) +2: + pushq $976 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProtoOpenIM + .p2align 4 + .type _XimProtoOpenIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProtoOpenIM +#endif +_XimProtoOpenIM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7816(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7816(%rip) +2: + pushq $977 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProtoUtf8LookupString + .p2align 4 + .type _XimProtoUtf8LookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProtoUtf8LookupString +#endif +_XimProtoUtf8LookupString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7824(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7824(%rip) +2: + pushq $978 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProtoWcLookupString + .p2align 4 + .type _XimProtoWcLookupString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProtoWcLookupString +#endif +_XimProtoWcLookupString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7832(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7832(%rip) +2: + pushq $979 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimProtoWireToEvent + .p2align 4 + .type _XimProtoWireToEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimProtoWireToEvent +#endif +_XimProtoWireToEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7840(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7840(%rip) +2: + pushq $980 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimRead + .p2align 4 + .type _XimRead, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimRead +#endif +_XimRead: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7848(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7848(%rip) +2: + pushq $981 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimRegProtoIntrCallback + .p2align 4 + .type _XimRegProtoIntrCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimRegProtoIntrCallback +#endif +_XimRegProtoIntrCallback: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7856(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7856(%rip) +2: + pushq $982 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimRegisterDispatcher + .p2align 4 + .type _XimRegisterDispatcher, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimRegisterDispatcher +#endif +_XimRegisterDispatcher: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7864(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7864(%rip) +2: + pushq $983 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimRegisterFilter + .p2align 4 + .type _XimRegisterFilter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimRegisterFilter +#endif +_XimRegisterFilter: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7872(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7872(%rip) +2: + pushq $984 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimRegisterIMInstantiateCallback + .p2align 4 + .type _XimRegisterIMInstantiateCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimRegisterIMInstantiateCallback +#endif +_XimRegisterIMInstantiateCallback: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7880(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7880(%rip) +2: + pushq $985 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimRegisterServerFilter + .p2align 4 + .type _XimRegisterServerFilter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimRegisterServerFilter +#endif +_XimRegisterServerFilter: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7888(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7888(%rip) +2: + pushq $986 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimRegisterTriggerKeysCallback + .p2align 4 + .type _XimRegisterTriggerKeysCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimRegisterTriggerKeysCallback +#endif +_XimRegisterTriggerKeysCallback: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7896(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7896(%rip) +2: + pushq $987 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimReregisterFilter + .p2align 4 + .type _XimReregisterFilter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimReregisterFilter +#endif +_XimReregisterFilter: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7904(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7904(%rip) +2: + pushq $988 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimResetIMInstantiateCallback + .p2align 4 + .type _XimResetIMInstantiateCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimResetIMInstantiateCallback +#endif +_XimResetIMInstantiateCallback: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7912(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7912(%rip) +2: + pushq $989 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimRespSyncReply + .p2align 4 + .type _XimRespSyncReply, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimRespSyncReply +#endif +_XimRespSyncReply: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7920(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7920(%rip) +2: + pushq $990 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimServerDestroy + .p2align 4 + .type _XimServerDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimServerDestroy +#endif +_XimServerDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7928(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7928(%rip) +2: + pushq $991 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetCurrentICValues + .p2align 4 + .type _XimSetCurrentICValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetCurrentICValues +#endif +_XimSetCurrentICValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7936(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7936(%rip) +2: + pushq $992 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetCurrentIMValues + .p2align 4 + .type _XimSetCurrentIMValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetCurrentIMValues +#endif +_XimSetCurrentIMValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7944(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7944(%rip) +2: + pushq $993 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetEventMaskCallback + .p2align 4 + .type _XimSetEventMaskCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetEventMaskCallback +#endif +_XimSetEventMaskCallback: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7952(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7952(%rip) +2: + pushq $994 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetHeader + .p2align 4 + .type _XimSetHeader, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetHeader +#endif +_XimSetHeader: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7960(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7960(%rip) +2: + pushq $995 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetICDefaults + .p2align 4 + .type _XimSetICDefaults, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetICDefaults +#endif +_XimSetICDefaults: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7968(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7968(%rip) +2: + pushq $996 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetICMode + .p2align 4 + .type _XimSetICMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetICMode +#endif +_XimSetICMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7976(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7976(%rip) +2: + pushq $997 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetICResourceList + .p2align 4 + .type _XimSetICResourceList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetICResourceList +#endif +_XimSetICResourceList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7984(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7984(%rip) +2: + pushq $998 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetICValueData + .p2align 4 + .type _XimSetICValueData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetICValueData +#endif +_XimSetICValueData: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+7992(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+7992(%rip) +2: + pushq $999 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetIMMode + .p2align 4 + .type _XimSetIMMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetIMMode +#endif +_XimSetIMMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8000(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8000(%rip) +2: + pushq $1000 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetIMResourceList + .p2align 4 + .type _XimSetIMResourceList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetIMResourceList +#endif +_XimSetIMResourceList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8008(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8008(%rip) +2: + pushq $1001 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetIMValueData + .p2align 4 + .type _XimSetIMValueData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetIMValueData +#endif +_XimSetIMValueData: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8016(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8016(%rip) +2: + pushq $1002 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetInnerICResourceList + .p2align 4 + .type _XimSetInnerICResourceList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetInnerICResourceList +#endif +_XimSetInnerICResourceList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8024(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8024(%rip) +2: + pushq $1003 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetInnerIMResourceList + .p2align 4 + .type _XimSetInnerIMResourceList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetInnerIMResourceList +#endif +_XimSetInnerIMResourceList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8032(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8032(%rip) +2: + pushq $1004 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSetLocalIMDefaults + .p2align 4 + .type _XimSetLocalIMDefaults, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSetLocalIMDefaults +#endif +_XimSetLocalIMDefaults: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8040(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8040(%rip) +2: + pushq $1005 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimShutdown + .p2align 4 + .type _XimShutdown, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimShutdown +#endif +_XimShutdown: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8048(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8048(%rip) +2: + pushq $1006 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSync + .p2align 4 + .type _XimSync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSync +#endif +_XimSync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8056(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8056(%rip) +2: + pushq $1007 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimSyncCallback + .p2align 4 + .type _XimSyncCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimSyncCallback +#endif +_XimSyncCallback: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8064(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8064(%rip) +2: + pushq $1008 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimThaiCloseIM + .p2align 4 + .type _XimThaiCloseIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimThaiCloseIM +#endif +_XimThaiCloseIM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8072(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8072(%rip) +2: + pushq $1009 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimThaiCreateIC + .p2align 4 + .type _XimThaiCreateIC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimThaiCreateIC +#endif +_XimThaiCreateIC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8080(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8080(%rip) +2: + pushq $1010 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimThaiFilter + .p2align 4 + .type _XimThaiFilter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimThaiFilter +#endif +_XimThaiFilter: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8088(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8088(%rip) +2: + pushq $1011 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimThaiIMFree + .p2align 4 + .type _XimThaiIMFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimThaiIMFree +#endif +_XimThaiIMFree: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8096(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8096(%rip) +2: + pushq $1012 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimThaiOpenIM + .p2align 4 + .type _XimThaiOpenIM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimThaiOpenIM +#endif +_XimThaiOpenIM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8104(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8104(%rip) +2: + pushq $1013 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimTransCallDispatcher + .p2align 4 + .type _XimTransCallDispatcher, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimTransCallDispatcher +#endif +_XimTransCallDispatcher: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8112(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8112(%rip) +2: + pushq $1014 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimTransConf + .p2align 4 + .type _XimTransConf, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimTransConf +#endif +_XimTransConf: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8120(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8120(%rip) +2: + pushq $1015 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimTransFilterWaitEvent + .p2align 4 + .type _XimTransFilterWaitEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimTransFilterWaitEvent +#endif +_XimTransFilterWaitEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8128(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8128(%rip) +2: + pushq $1016 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimTransFlush + .p2align 4 + .type _XimTransFlush, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimTransFlush +#endif +_XimTransFlush: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8136(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8136(%rip) +2: + pushq $1017 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimTransInternalConnection + .p2align 4 + .type _XimTransInternalConnection, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimTransInternalConnection +#endif +_XimTransInternalConnection: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8144(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8144(%rip) +2: + pushq $1018 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimTransRead + .p2align 4 + .type _XimTransRead, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimTransRead +#endif +_XimTransRead: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8152(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8152(%rip) +2: + pushq $1019 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimTransRegisterDispatcher + .p2align 4 + .type _XimTransRegisterDispatcher, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimTransRegisterDispatcher +#endif +_XimTransRegisterDispatcher: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8160(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8160(%rip) +2: + pushq $1020 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimTransWrite + .p2align 4 + .type _XimTransWrite, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimTransWrite +#endif +_XimTransWrite: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8168(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8168(%rip) +2: + pushq $1021 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimTriggerNotify + .p2align 4 + .type _XimTriggerNotify, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimTriggerNotify +#endif +_XimTriggerNotify: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8176(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8176(%rip) +2: + pushq $1022 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimUnRegisterIMInstantiateCallback + .p2align 4 + .type _XimUnRegisterIMInstantiateCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimUnRegisterIMInstantiateCallback +#endif +_XimUnRegisterIMInstantiateCallback: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8184(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8184(%rip) +2: + pushq $1023 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimUnregisterFilter + .p2align 4 + .type _XimUnregisterFilter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimUnregisterFilter +#endif +_XimUnregisterFilter: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8192(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8192(%rip) +2: + pushq $1024 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimUnregisterServerFilter + .p2align 4 + .type _XimUnregisterServerFilter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimUnregisterServerFilter +#endif +_XimUnregisterServerFilter: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8200(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8200(%rip) +2: + pushq $1025 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimWrite + .p2align 4 + .type _XimWrite, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimWrite +#endif +_XimWrite: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8208(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8208(%rip) +2: + pushq $1026 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXConf + .p2align 4 + .type _XimXConf, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXConf +#endif +_XimXConf: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8216(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8216(%rip) +2: + pushq $1027 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransBytesReadable + .p2align 4 + .type _XimXTransBytesReadable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransBytesReadable +#endif +_XimXTransBytesReadable: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8224(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8224(%rip) +2: + pushq $1028 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransClose + .p2align 4 + .type _XimXTransClose, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransClose +#endif +_XimXTransClose: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8232(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8232(%rip) +2: + pushq $1029 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransCloseForCloning + .p2align 4 + .type _XimXTransCloseForCloning, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransCloseForCloning +#endif +_XimXTransCloseForCloning: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8240(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8240(%rip) +2: + pushq $1030 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransConnect + .p2align 4 + .type _XimXTransConnect, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransConnect +#endif +_XimXTransConnect: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8248(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8248(%rip) +2: + pushq $1031 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransDisconnect + .p2align 4 + .type _XimXTransDisconnect, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransDisconnect +#endif +_XimXTransDisconnect: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8256(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8256(%rip) +2: + pushq $1032 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransFreeConnInfo + .p2align 4 + .type _XimXTransFreeConnInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransFreeConnInfo +#endif +_XimXTransFreeConnInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8264(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8264(%rip) +2: + pushq $1033 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransGetConnectionNumber + .p2align 4 + .type _XimXTransGetConnectionNumber, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransGetConnectionNumber +#endif +_XimXTransGetConnectionNumber: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8272(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8272(%rip) +2: + pushq $1034 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransGetHostname + .p2align 4 + .type _XimXTransGetHostname, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransGetHostname +#endif +_XimXTransGetHostname: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8280(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8280(%rip) +2: + pushq $1035 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransGetPeerAddr + .p2align 4 + .type _XimXTransGetPeerAddr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransGetPeerAddr +#endif +_XimXTransGetPeerAddr: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8288(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8288(%rip) +2: + pushq $1036 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransIsLocal + .p2align 4 + .type _XimXTransIsLocal, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransIsLocal +#endif +_XimXTransIsLocal: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8296(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8296(%rip) +2: + pushq $1037 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransOpenCOTSClient + .p2align 4 + .type _XimXTransOpenCOTSClient, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransOpenCOTSClient +#endif +_XimXTransOpenCOTSClient: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8304(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8304(%rip) +2: + pushq $1038 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransRead + .p2align 4 + .type _XimXTransRead, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransRead +#endif +_XimXTransRead: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8312(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8312(%rip) +2: + pushq $1039 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransReadv + .p2align 4 + .type _XimXTransReadv, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransReadv +#endif +_XimXTransReadv: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8320(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8320(%rip) +2: + pushq $1040 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransSetOption + .p2align 4 + .type _XimXTransSetOption, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransSetOption +#endif +_XimXTransSetOption: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8328(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8328(%rip) +2: + pushq $1041 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransWrite + .p2align 4 + .type _XimXTransWrite, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransWrite +#endif +_XimXTransWrite: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8336(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8336(%rip) +2: + pushq $1042 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XimXTransWritev + .p2align 4 + .type _XimXTransWritev, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XimXTransWritev +#endif +_XimXTransWritev: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8344(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8344(%rip) +2: + pushq $1043 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Ximctstombs + .p2align 4 + .type _Ximctstombs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Ximctstombs +#endif +_Ximctstombs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8352(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8352(%rip) +2: + pushq $1044 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Ximctstoutf8 + .p2align 4 + .type _Ximctstoutf8, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Ximctstoutf8 +#endif +_Ximctstoutf8: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8360(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8360(%rip) +2: + pushq $1045 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Ximctstowcs + .p2align 4 + .type _Ximctstowcs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Ximctstowcs +#endif +_Ximctstowcs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8368(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8368(%rip) +2: + pushq $1046 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbCopyFromReadBuffer + .p2align 4 + .type _XkbCopyFromReadBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbCopyFromReadBuffer +#endif +_XkbCopyFromReadBuffer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8376(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8376(%rip) +2: + pushq $1047 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbFreeReadBuffer + .p2align 4 + .type _XkbFreeReadBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbFreeReadBuffer +#endif +_XkbFreeReadBuffer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8384(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8384(%rip) +2: + pushq $1048 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbGetCharset + .p2align 4 + .type _XkbGetCharset, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbGetCharset +#endif +_XkbGetCharset: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8392(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8392(%rip) +2: + pushq $1049 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbGetConverters + .p2align 4 + .type _XkbGetConverters, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbGetConverters +#endif +_XkbGetConverters: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8400(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8400(%rip) +2: + pushq $1050 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbGetReadBufferCountedString + .p2align 4 + .type _XkbGetReadBufferCountedString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbGetReadBufferCountedString +#endif +_XkbGetReadBufferCountedString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8408(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8408(%rip) +2: + pushq $1051 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbGetReadBufferPtr + .p2align 4 + .type _XkbGetReadBufferPtr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbGetReadBufferPtr +#endif +_XkbGetReadBufferPtr: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8416(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8416(%rip) +2: + pushq $1052 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbInitReadBuffer + .p2align 4 + .type _XkbInitReadBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbInitReadBuffer +#endif +_XkbInitReadBuffer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8424(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8424(%rip) +2: + pushq $1053 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbNoteCoreMapChanges + .p2align 4 + .type _XkbNoteCoreMapChanges, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbNoteCoreMapChanges +#endif +_XkbNoteCoreMapChanges: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8432(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8432(%rip) +2: + pushq $1054 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbPeekAtReadBuffer + .p2align 4 + .type _XkbPeekAtReadBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbPeekAtReadBuffer +#endif +_XkbPeekAtReadBuffer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8440(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8440(%rip) +2: + pushq $1055 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReadBufferCopy32 + .p2align 4 + .type _XkbReadBufferCopy32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReadBufferCopy32 +#endif +_XkbReadBufferCopy32: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8448(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8448(%rip) +2: + pushq $1056 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReadBufferCopyKeySyms + .p2align 4 + .type _XkbReadBufferCopyKeySyms, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReadBufferCopyKeySyms +#endif +_XkbReadBufferCopyKeySyms: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8456(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8456(%rip) +2: + pushq $1057 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReadCopyData32 + .p2align 4 + .type _XkbReadCopyData32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReadCopyData32 +#endif +_XkbReadCopyData32: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8464(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8464(%rip) +2: + pushq $1058 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReadCopyKeySyms + .p2align 4 + .type _XkbReadCopyKeySyms, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReadCopyKeySyms +#endif +_XkbReadCopyKeySyms: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8472(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8472(%rip) +2: + pushq $1059 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReadGetCompatMapReply + .p2align 4 + .type _XkbReadGetCompatMapReply, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReadGetCompatMapReply +#endif +_XkbReadGetCompatMapReply: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8480(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8480(%rip) +2: + pushq $1060 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReadGetGeometryReply + .p2align 4 + .type _XkbReadGetGeometryReply, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReadGetGeometryReply +#endif +_XkbReadGetGeometryReply: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8488(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8488(%rip) +2: + pushq $1061 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReadGetIndicatorMapReply + .p2align 4 + .type _XkbReadGetIndicatorMapReply, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReadGetIndicatorMapReply +#endif +_XkbReadGetIndicatorMapReply: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8496(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8496(%rip) +2: + pushq $1062 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReadGetMapReply + .p2align 4 + .type _XkbReadGetMapReply, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReadGetMapReply +#endif +_XkbReadGetMapReply: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8504(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8504(%rip) +2: + pushq $1063 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReadGetNamesReply + .p2align 4 + .type _XkbReadGetNamesReply, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReadGetNamesReply +#endif +_XkbReadGetNamesReply: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8512(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8512(%rip) +2: + pushq $1064 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbReloadDpy + .p2align 4 + .type _XkbReloadDpy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbReloadDpy +#endif +_XkbReloadDpy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8520(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8520(%rip) +2: + pushq $1065 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbSkipReadBufferData + .p2align 4 + .type _XkbSkipReadBufferData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbSkipReadBufferData +#endif +_XkbSkipReadBufferData: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8528(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8528(%rip) +2: + pushq $1066 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbWriteCopyData32 + .p2align 4 + .type _XkbWriteCopyData32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbWriteCopyData32 +#endif +_XkbWriteCopyData32: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8536(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8536(%rip) +2: + pushq $1067 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XkbWriteCopyKeySyms + .p2align 4 + .type _XkbWriteCopyKeySyms, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XkbWriteCopyKeySyms +#endif +_XkbWriteCopyKeySyms: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8544(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8544(%rip) +2: + pushq $1068 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcAddCT + .p2align 4 + .type _XlcAddCT, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcAddCT +#endif +_XlcAddCT: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8552(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8552(%rip) +2: + pushq $1069 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcAddCharSet + .p2align 4 + .type _XlcAddCharSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcAddCharSet +#endif +_XlcAddCharSet: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8560(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8560(%rip) +2: + pushq $1070 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcAddGB18030LocaleConverters + .p2align 4 + .type _XlcAddGB18030LocaleConverters, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcAddGB18030LocaleConverters +#endif +_XlcAddGB18030LocaleConverters: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8568(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8568(%rip) +2: + pushq $1071 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcAddLoader + .p2align 4 + .type _XlcAddLoader, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcAddLoader +#endif +_XlcAddLoader: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8576(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8576(%rip) +2: + pushq $1072 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcAddUtf8Converters + .p2align 4 + .type _XlcAddUtf8Converters, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcAddUtf8Converters +#endif +_XlcAddUtf8Converters: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8584(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8584(%rip) +2: + pushq $1073 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcAddUtf8LocaleConverters + .p2align 4 + .type _XlcAddUtf8LocaleConverters, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcAddUtf8LocaleConverters +#endif +_XlcAddUtf8LocaleConverters: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8592(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8592(%rip) +2: + pushq $1074 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCloseConverter + .p2align 4 + .type _XlcCloseConverter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCloseConverter +#endif +_XlcCloseConverter: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8600(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8600(%rip) +2: + pushq $1075 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCompareISOLatin1 + .p2align 4 + .type _XlcCompareISOLatin1, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCompareISOLatin1 +#endif +_XlcCompareISOLatin1: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8608(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8608(%rip) +2: + pushq $1076 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCompileResourceList + .p2align 4 + .type _XlcCompileResourceList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCompileResourceList +#endif +_XlcCompileResourceList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8616(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8616(%rip) +2: + pushq $1077 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcConvert + .p2align 4 + .type _XlcConvert, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcConvert +#endif +_XlcConvert: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8624(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8624(%rip) +2: + pushq $1078 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCopyFromArg + .p2align 4 + .type _XlcCopyFromArg, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCopyFromArg +#endif +_XlcCopyFromArg: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8632(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8632(%rip) +2: + pushq $1079 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCopyToArg + .p2align 4 + .type _XlcCopyToArg, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCopyToArg +#endif +_XlcCopyToArg: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8640(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8640(%rip) +2: + pushq $1080 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCountVaList + .p2align 4 + .type _XlcCountVaList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCountVaList +#endif +_XlcCountVaList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8648(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8648(%rip) +2: + pushq $1081 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCreateDefaultCharSet + .p2align 4 + .type _XlcCreateDefaultCharSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCreateDefaultCharSet +#endif +_XlcCreateDefaultCharSet: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8656(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8656(%rip) +2: + pushq $1082 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCreateLC + .p2align 4 + .type _XlcCreateLC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCreateLC +#endif +_XlcCreateLC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8664(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8664(%rip) +2: + pushq $1083 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCreateLocaleDataBase + .p2align 4 + .type _XlcCreateLocaleDataBase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCreateLocaleDataBase +#endif +_XlcCreateLocaleDataBase: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8672(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8672(%rip) +2: + pushq $1084 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcCurrentLC + .p2align 4 + .type _XlcCurrentLC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcCurrentLC +#endif +_XlcCurrentLC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8680(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8680(%rip) +2: + pushq $1085 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcDbg_printValue + .p2align 4 + .type _XlcDbg_printValue, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcDbg_printValue +#endif +_XlcDbg_printValue: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8688(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8688(%rip) +2: + pushq $1086 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcDeInitLoader + .p2align 4 + .type _XlcDeInitLoader, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcDeInitLoader +#endif +_XlcDeInitLoader: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8696(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8696(%rip) +2: + pushq $1087 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcDefaultLoader + .p2align 4 + .type _XlcDefaultLoader, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcDefaultLoader +#endif +_XlcDefaultLoader: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8704(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8704(%rip) +2: + pushq $1088 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcDefaultMapModifiers + .p2align 4 + .type _XlcDefaultMapModifiers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcDefaultMapModifiers +#endif +_XlcDefaultMapModifiers: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8712(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8712(%rip) +2: + pushq $1089 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcDestroyLC + .p2align 4 + .type _XlcDestroyLC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcDestroyLC +#endif +_XlcDestroyLC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8720(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8720(%rip) +2: + pushq $1090 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcDestroyLocaleDataBase + .p2align 4 + .type _XlcDestroyLocaleDataBase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcDestroyLocaleDataBase +#endif +_XlcDestroyLocaleDataBase: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8728(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8728(%rip) +2: + pushq $1091 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcFileName + .p2align 4 + .type _XlcFileName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcFileName +#endif +_XlcFileName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8736(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8736(%rip) +2: + pushq $1092 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcGenericLoader + .p2align 4 + .type _XlcGenericLoader, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcGenericLoader +#endif +_XlcGenericLoader: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8744(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8744(%rip) +2: + pushq $1093 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcGetCSValues + .p2align 4 + .type _XlcGetCSValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcGetCSValues +#endif +_XlcGetCSValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8752(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8752(%rip) +2: + pushq $1094 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcGetCharSet + .p2align 4 + .type _XlcGetCharSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcGetCharSet +#endif +_XlcGetCharSet: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8760(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8760(%rip) +2: + pushq $1095 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcGetCharSetWithSide + .p2align 4 + .type _XlcGetCharSetWithSide, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcGetCharSetWithSide +#endif +_XlcGetCharSetWithSide: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8768(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8768(%rip) +2: + pushq $1096 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcGetLocaleDataBase + .p2align 4 + .type _XlcGetLocaleDataBase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcGetLocaleDataBase +#endif +_XlcGetLocaleDataBase: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8776(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8776(%rip) +2: + pushq $1097 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcGetResource + .p2align 4 + .type _XlcGetResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcGetResource +#endif +_XlcGetResource: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8784(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8784(%rip) +2: + pushq $1098 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcGetValues + .p2align 4 + .type _XlcGetValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcGetValues +#endif +_XlcGetValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8792(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8792(%rip) +2: + pushq $1099 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcInitCTInfo + .p2align 4 + .type _XlcInitCTInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcInitCTInfo +#endif +_XlcInitCTInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8800(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8800(%rip) +2: + pushq $1100 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcInitLoader + .p2align 4 + .type _XlcInitLoader, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcInitLoader +#endif +_XlcInitLoader: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8808(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8808(%rip) +2: + pushq $1101 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcLocaleDirName + .p2align 4 + .type _XlcLocaleDirName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcLocaleDirName +#endif +_XlcLocaleDirName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8816(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8816(%rip) +2: + pushq $1102 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcLocaleLibDirName + .p2align 4 + .type _XlcLocaleLibDirName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcLocaleLibDirName +#endif +_XlcLocaleLibDirName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8824(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8824(%rip) +2: + pushq $1103 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcMapOSLocaleName + .p2align 4 + .type _XlcMapOSLocaleName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcMapOSLocaleName +#endif +_XlcMapOSLocaleName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8832(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8832(%rip) +2: + pushq $1104 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcNCompareISOLatin1 + .p2align 4 + .type _XlcNCompareISOLatin1, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcNCompareISOLatin1 +#endif +_XlcNCompareISOLatin1: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8840(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8840(%rip) +2: + pushq $1105 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcOpenConverter + .p2align 4 + .type _XlcOpenConverter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcOpenConverter +#endif +_XlcOpenConverter: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8848(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8848(%rip) +2: + pushq $1106 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcParseCharSet + .p2align 4 + .type _XlcParseCharSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcParseCharSet +#endif +_XlcParseCharSet: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8856(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8856(%rip) +2: + pushq $1107 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcParse_scopemaps + .p2align 4 + .type _XlcParse_scopemaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcParse_scopemaps +#endif +_XlcParse_scopemaps: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8864(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8864(%rip) +2: + pushq $1108 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcRemoveLoader + .p2align 4 + .type _XlcRemoveLoader, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcRemoveLoader +#endif +_XlcRemoveLoader: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8872(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8872(%rip) +2: + pushq $1109 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcResetConverter + .p2align 4 + .type _XlcResetConverter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcResetConverter +#endif +_XlcResetConverter: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8880(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8880(%rip) +2: + pushq $1110 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcResolveI18NPath + .p2align 4 + .type _XlcResolveI18NPath, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcResolveI18NPath +#endif +_XlcResolveI18NPath: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8888(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8888(%rip) +2: + pushq $1111 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcResolveLocaleName + .p2align 4 + .type _XlcResolveLocaleName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcResolveLocaleName +#endif +_XlcResolveLocaleName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8896(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8896(%rip) +2: + pushq $1112 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcSetConverter + .p2align 4 + .type _XlcSetConverter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcSetConverter +#endif +_XlcSetConverter: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8904(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8904(%rip) +2: + pushq $1113 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcSetValues + .p2align 4 + .type _XlcSetValues, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcSetValues +#endif +_XlcSetValues: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8912(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8912(%rip) +2: + pushq $1114 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcUtf8Loader + .p2align 4 + .type _XlcUtf8Loader, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcUtf8Loader +#endif +_XlcUtf8Loader: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8920(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8920(%rip) +2: + pushq $1115 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcVaToArgList + .p2align 4 + .type _XlcVaToArgList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcVaToArgList +#endif +_XlcVaToArgList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8928(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8928(%rip) +2: + pushq $1116 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XlcValidModSyntax + .p2align 4 + .type _XlcValidModSyntax, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XlcValidModSyntax +#endif +_XlcValidModSyntax: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8936(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8936(%rip) +2: + pushq $1117 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xlcmbstoutf8 + .p2align 4 + .type _Xlcmbstoutf8, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xlcmbstoutf8 +#endif +_Xlcmbstoutf8: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8944(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8944(%rip) +2: + pushq $1118 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xlcmbstowcs + .p2align 4 + .type _Xlcmbstowcs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xlcmbstowcs +#endif +_Xlcmbstowcs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8952(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8952(%rip) +2: + pushq $1119 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xlcmbtowc + .p2align 4 + .type _Xlcmbtowc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xlcmbtowc +#endif +_Xlcmbtowc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8960(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8960(%rip) +2: + pushq $1120 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xlcwcstombs + .p2align 4 + .type _Xlcwcstombs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xlcwcstombs +#endif +_Xlcwcstombs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8968(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8968(%rip) +2: + pushq $1121 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xlcwctomb + .p2align 4 + .type _Xlcwctomb, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xlcwctomb +#endif +_Xlcwctomb: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8976(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8976(%rip) +2: + pushq $1122 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbDefaultDrawImageString + .p2align 4 + .type _XmbDefaultDrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbDefaultDrawImageString +#endif +_XmbDefaultDrawImageString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8984(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8984(%rip) +2: + pushq $1123 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbDefaultDrawString + .p2align 4 + .type _XmbDefaultDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbDefaultDrawString +#endif +_XmbDefaultDrawString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+8992(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+8992(%rip) +2: + pushq $1124 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbDefaultTextEscapement + .p2align 4 + .type _XmbDefaultTextEscapement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbDefaultTextEscapement +#endif +_XmbDefaultTextEscapement: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9000(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9000(%rip) +2: + pushq $1125 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbDefaultTextExtents + .p2align 4 + .type _XmbDefaultTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbDefaultTextExtents +#endif +_XmbDefaultTextExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9008(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9008(%rip) +2: + pushq $1126 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbDefaultTextPerCharExtents + .p2align 4 + .type _XmbDefaultTextPerCharExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbDefaultTextPerCharExtents +#endif +_XmbDefaultTextPerCharExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9016(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9016(%rip) +2: + pushq $1127 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbGenericDrawImageString + .p2align 4 + .type _XmbGenericDrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbGenericDrawImageString +#endif +_XmbGenericDrawImageString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9024(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9024(%rip) +2: + pushq $1128 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbGenericDrawString + .p2align 4 + .type _XmbGenericDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbGenericDrawString +#endif +_XmbGenericDrawString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9032(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9032(%rip) +2: + pushq $1129 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbGenericTextEscapement + .p2align 4 + .type _XmbGenericTextEscapement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbGenericTextEscapement +#endif +_XmbGenericTextEscapement: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9040(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9040(%rip) +2: + pushq $1130 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbGenericTextExtents + .p2align 4 + .type _XmbGenericTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbGenericTextExtents +#endif +_XmbGenericTextExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9048(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9048(%rip) +2: + pushq $1131 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbGenericTextPerCharExtents + .p2align 4 + .type _XmbGenericTextPerCharExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbGenericTextPerCharExtents +#endif +_XmbGenericTextPerCharExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9056(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9056(%rip) +2: + pushq $1132 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbTextListToTextProperty + .p2align 4 + .type _XmbTextListToTextProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbTextListToTextProperty +#endif +_XmbTextListToTextProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9064(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9064(%rip) +2: + pushq $1133 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XmbTextPropertyToTextList + .p2align 4 + .type _XmbTextPropertyToTextList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XmbTextPropertyToTextList +#endif +_XmbTextPropertyToTextList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9072(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9072(%rip) +2: + pushq $1134 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xmblen + .p2align 4 + .type _Xmblen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xmblen +#endif +_Xmblen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9080(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9080(%rip) +2: + pushq $1135 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xmbstoutf8 + .p2align 4 + .type _Xmbstoutf8, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xmbstoutf8 +#endif +_Xmbstoutf8: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9088(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9088(%rip) +2: + pushq $1136 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xmbstowcs + .p2align 4 + .type _Xmbstowcs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xmbstowcs +#endif +_Xmbstowcs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9096(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9096(%rip) +2: + pushq $1137 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xmbtowc + .p2align 4 + .type _Xmbtowc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xmbtowc +#endif +_Xmbtowc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9104(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9104(%rip) +2: + pushq $1138 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XomConvert + .p2align 4 + .type _XomConvert, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XomConvert +#endif +_XomConvert: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9112(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9112(%rip) +2: + pushq $1139 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XomGenericDrawString + .p2align 4 + .type _XomGenericDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XomGenericDrawString +#endif +_XomGenericDrawString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9120(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9120(%rip) +2: + pushq $1140 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XomGenericOpenOM + .p2align 4 + .type _XomGenericOpenOM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XomGenericOpenOM +#endif +_XomGenericOpenOM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9128(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9128(%rip) +2: + pushq $1141 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XomGenericTextExtents + .p2align 4 + .type _XomGenericTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XomGenericTextExtents +#endif +_XomGenericTextExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9136(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9136(%rip) +2: + pushq $1142 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XomGetFontDataFromFontSet + .p2align 4 + .type _XomGetFontDataFromFontSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XomGetFontDataFromFontSet +#endif +_XomGetFontDataFromFontSet: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9144(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9144(%rip) +2: + pushq $1143 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XomInitConverter + .p2align 4 + .type _XomInitConverter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XomInitConverter +#endif +_XomInitConverter: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9152(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9152(%rip) +2: + pushq $1144 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XrmDefaultInitParseInfo + .p2align 4 + .type _XrmDefaultInitParseInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XrmDefaultInitParseInfo +#endif +_XrmDefaultInitParseInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9160(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9160(%rip) +2: + pushq $1145 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XrmInitParseInfo + .p2align 4 + .type _XrmInitParseInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XrmInitParseInfo +#endif +_XrmInitParseInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9168(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9168(%rip) +2: + pushq $1146 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XrmInternalStringToQuark + .p2align 4 + .type _XrmInternalStringToQuark, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XrmInternalStringToQuark +#endif +_XrmInternalStringToQuark: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9176(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9176(%rip) +2: + pushq $1147 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8DefaultDrawImageString + .p2align 4 + .type _Xutf8DefaultDrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8DefaultDrawImageString +#endif +_Xutf8DefaultDrawImageString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9184(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9184(%rip) +2: + pushq $1148 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8DefaultDrawString + .p2align 4 + .type _Xutf8DefaultDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8DefaultDrawString +#endif +_Xutf8DefaultDrawString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9192(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9192(%rip) +2: + pushq $1149 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8DefaultTextEscapement + .p2align 4 + .type _Xutf8DefaultTextEscapement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8DefaultTextEscapement +#endif +_Xutf8DefaultTextEscapement: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9200(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9200(%rip) +2: + pushq $1150 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8DefaultTextExtents + .p2align 4 + .type _Xutf8DefaultTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8DefaultTextExtents +#endif +_Xutf8DefaultTextExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9208(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9208(%rip) +2: + pushq $1151 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8DefaultTextPerCharExtents + .p2align 4 + .type _Xutf8DefaultTextPerCharExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8DefaultTextPerCharExtents +#endif +_Xutf8DefaultTextPerCharExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9216(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9216(%rip) +2: + pushq $1152 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8GenericDrawImageString + .p2align 4 + .type _Xutf8GenericDrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8GenericDrawImageString +#endif +_Xutf8GenericDrawImageString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9224(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9224(%rip) +2: + pushq $1153 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8GenericDrawString + .p2align 4 + .type _Xutf8GenericDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8GenericDrawString +#endif +_Xutf8GenericDrawString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9232(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9232(%rip) +2: + pushq $1154 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8GenericTextEscapement + .p2align 4 + .type _Xutf8GenericTextEscapement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8GenericTextEscapement +#endif +_Xutf8GenericTextEscapement: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9240(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9240(%rip) +2: + pushq $1155 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8GenericTextExtents + .p2align 4 + .type _Xutf8GenericTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8GenericTextExtents +#endif +_Xutf8GenericTextExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9248(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9248(%rip) +2: + pushq $1156 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8GenericTextPerCharExtents + .p2align 4 + .type _Xutf8GenericTextPerCharExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8GenericTextPerCharExtents +#endif +_Xutf8GenericTextPerCharExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9256(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9256(%rip) +2: + pushq $1157 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8TextListToTextProperty + .p2align 4 + .type _Xutf8TextListToTextProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8TextListToTextProperty +#endif +_Xutf8TextListToTextProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9264(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9264(%rip) +2: + pushq $1158 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xutf8TextPropertyToTextList + .p2align 4 + .type _Xutf8TextPropertyToTextList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xutf8TextPropertyToTextList +#endif +_Xutf8TextPropertyToTextList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9272(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9272(%rip) +2: + pushq $1159 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcDefaultDrawImageString + .p2align 4 + .type _XwcDefaultDrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcDefaultDrawImageString +#endif +_XwcDefaultDrawImageString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9280(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9280(%rip) +2: + pushq $1160 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcDefaultDrawString + .p2align 4 + .type _XwcDefaultDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcDefaultDrawString +#endif +_XwcDefaultDrawString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9288(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9288(%rip) +2: + pushq $1161 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcDefaultTextEscapement + .p2align 4 + .type _XwcDefaultTextEscapement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcDefaultTextEscapement +#endif +_XwcDefaultTextEscapement: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9296(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9296(%rip) +2: + pushq $1162 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcDefaultTextExtents + .p2align 4 + .type _XwcDefaultTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcDefaultTextExtents +#endif +_XwcDefaultTextExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9304(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9304(%rip) +2: + pushq $1163 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcDefaultTextPerCharExtents + .p2align 4 + .type _XwcDefaultTextPerCharExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcDefaultTextPerCharExtents +#endif +_XwcDefaultTextPerCharExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9312(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9312(%rip) +2: + pushq $1164 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcFreeStringList + .p2align 4 + .type _XwcFreeStringList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcFreeStringList +#endif +_XwcFreeStringList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9320(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9320(%rip) +2: + pushq $1165 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcGenericDrawImageString + .p2align 4 + .type _XwcGenericDrawImageString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcGenericDrawImageString +#endif +_XwcGenericDrawImageString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9328(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9328(%rip) +2: + pushq $1166 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcGenericDrawString + .p2align 4 + .type _XwcGenericDrawString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcGenericDrawString +#endif +_XwcGenericDrawString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9336(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9336(%rip) +2: + pushq $1167 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcGenericTextEscapement + .p2align 4 + .type _XwcGenericTextEscapement, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcGenericTextEscapement +#endif +_XwcGenericTextEscapement: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9344(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9344(%rip) +2: + pushq $1168 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcGenericTextExtents + .p2align 4 + .type _XwcGenericTextExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcGenericTextExtents +#endif +_XwcGenericTextExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9352(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9352(%rip) +2: + pushq $1169 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcGenericTextPerCharExtents + .p2align 4 + .type _XwcGenericTextPerCharExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcGenericTextPerCharExtents +#endif +_XwcGenericTextPerCharExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9360(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9360(%rip) +2: + pushq $1170 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcTextListToTextProperty + .p2align 4 + .type _XwcTextListToTextProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcTextListToTextProperty +#endif +_XwcTextListToTextProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9368(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9368(%rip) +2: + pushq $1171 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _XwcTextPropertyToTextList + .p2align 4 + .type _XwcTextPropertyToTextList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _XwcTextPropertyToTextList +#endif +_XwcTextPropertyToTextList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9376(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9376(%rip) +2: + pushq $1172 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xwcscmp + .p2align 4 + .type _Xwcscmp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xwcscmp +#endif +_Xwcscmp: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9384(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9384(%rip) +2: + pushq $1173 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xwcscpy + .p2align 4 + .type _Xwcscpy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xwcscpy +#endif +_Xwcscpy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9392(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9392(%rip) +2: + pushq $1174 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xwcslen + .p2align 4 + .type _Xwcslen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xwcslen +#endif +_Xwcslen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9400(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9400(%rip) +2: + pushq $1175 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xwcsncmp + .p2align 4 + .type _Xwcsncmp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xwcsncmp +#endif +_Xwcsncmp: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9408(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9408(%rip) +2: + pushq $1176 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xwcsncpy + .p2align 4 + .type _Xwcsncpy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xwcsncpy +#endif +_Xwcsncpy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9416(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9416(%rip) +2: + pushq $1177 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xwcstombs + .p2align 4 + .type _Xwcstombs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xwcstombs +#endif +_Xwcstombs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9424(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9424(%rip) +2: + pushq $1178 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl _Xwctomb + .p2align 4 + .type _Xwctomb, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden _Xwctomb +#endif +_Xwctomb: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9432(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9432(%rip) +2: + pushq $1179 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl read_EncodingInfo + .p2align 4 + .type read_EncodingInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden read_EncodingInfo +#endif +read_EncodingInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9440(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9440(%rip) +2: + pushq $1180 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl xlocaledir + .p2align 4 + .type xlocaledir, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden xlocaledir +#endif +xlocaledir: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libX11_so_tramp_table+9448(%rip) + je 2f +1: + jmp *_libX11_so_tramp_table+9448(%rip) +2: + pushq $1181 + .cfi_adjust_cfa_offset 8 + call _libX11_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXcomposite.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXcomposite.so.init.c new file mode 100644 index 0000000..bd319a5 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXcomposite.so.init.c @@ -0,0 +1,256 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libXcomposite.so.1: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libXcomposite.so.1"); + CHECK(lib_handle, "failed to load library 'libXcomposite.so.1' via callback ''"); +#else + lib_handle = dlopen("libXcomposite.so.1", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libXcomposite.so.1' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "XCompositeCreateRegionFromBorderClip", + "XCompositeFindDisplay", + "XCompositeGetOverlayWindow", + "XCompositeNameWindowPixmap", + "XCompositeQueryExtension", + "XCompositeQueryVersion", + "XCompositeRedirectSubwindows", + "XCompositeRedirectWindow", + "XCompositeReleaseOverlayWindow", + "XCompositeUnredirectSubwindows", + "XCompositeUnredirectWindow", + "XCompositeVersion", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libXcomposite_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libXcomposite_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libXcomposite_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libXcomposite_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libXcomposite_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libXcomposite_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libXcomposite_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libXcomposite_so_tramp_table, 0, SYM_COUNT * sizeof(_libXcomposite_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXcomposite.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXcomposite.so.tramp.S new file mode 100644 index 0000000..339f9d0 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXcomposite.so.tramp.S @@ -0,0 +1,566 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libXcomposite_so_tramp_table + .hidden _libXcomposite_so_tramp_table + .align 8 +_libXcomposite_so_tramp_table: + .zero 104 + + .text + + .globl _libXcomposite_so_tramp_resolve + .hidden _libXcomposite_so_tramp_resolve + + .globl _libXcomposite_so_save_regs_and_resolve + .hidden _libXcomposite_so_save_regs_and_resolve + .type _libXcomposite_so_save_regs_and_resolve, %function +_libXcomposite_so_save_regs_and_resolve: + .cfi_startproc + +#define PUSH_REG(reg) pushq %reg ; .cfi_adjust_cfa_offset 8; .cfi_rel_offset reg, 0 +#define POP_REG(reg) popq %reg ; .cfi_adjust_cfa_offset -8; .cfi_restore reg + +#define DEC_STACK(d) subq $d, %rsp; .cfi_adjust_cfa_offset d +#define INC_STACK(d) addq $d, %rsp; .cfi_adjust_cfa_offset -d + +#define PUSH_MMX_REG(reg) DEC_STACK(8); movq %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_MMX_REG(reg) movq (%rsp), %reg; .cfi_restore reg; INC_STACK(8) + +#define PUSH_XMM_REG(reg) DEC_STACK(16); movdqa %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_XMM_REG(reg) movdqa (%rsp), %reg; .cfi_restore reg; INC_STACK(16) + +// TODO: cfi_offset/cfi_restore +#define PUSH_YMM_REG(reg) DEC_STACK(32); vmovdqu %reg, (%rsp) +#define POP_YMM_REG(reg) vmovdqu (%rsp), %reg; INC_STACK(32) + +// TODO: cfi_offset/cfi_restore +#define PUSH_ZMM_REG(reg) DEC_STACK(64); vmovdqu32 %reg, (%rsp) +#define POP_ZMM_REG(reg) vmovdqu32 (%rsp), %reg; INC_STACK(64) + + // Slow path which calls dlsym, taken only on first call. + // All registers are stored to handle arbitrary calling conventions + // (except x87 FPU registers which do not have to be preserved). + // For Dwarf directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + .cfi_def_cfa_offset 8 // Return address + + PUSH_REG(rdi) // 16 + mov 0x10(%rsp), %rdi + PUSH_REG(rbx) + PUSH_REG(rbx) // 16 + PUSH_REG(rcx) + PUSH_REG(rdx) // 16 + PUSH_REG(rbp) + PUSH_REG(rsi) // 16 + PUSH_REG(r8) + PUSH_REG(r9) // 16 + PUSH_REG(r10) + PUSH_REG(r11) // 16 + PUSH_REG(r12) + PUSH_REG(r13) // 16 + PUSH_REG(r14) + PUSH_REG(r15) // 16 + + // Maybe use cpuid instead of macro to detect current vector size... +#ifdef __AVX512F__ + PUSH_ZMM_REG(zmm0) + PUSH_ZMM_REG(zmm1) + PUSH_ZMM_REG(zmm2) + PUSH_ZMM_REG(zmm3) + PUSH_ZMM_REG(zmm4) + PUSH_ZMM_REG(zmm5) + PUSH_ZMM_REG(zmm6) + PUSH_ZMM_REG(zmm7) +#elif defined __AVX__ + PUSH_YMM_REG(ymm0) + PUSH_YMM_REG(ymm1) + PUSH_YMM_REG(ymm2) + PUSH_YMM_REG(ymm3) + PUSH_YMM_REG(ymm4) + PUSH_YMM_REG(ymm5) + PUSH_YMM_REG(ymm6) + PUSH_YMM_REG(ymm7) +#elif defined __SSE__ + PUSH_XMM_REG(xmm0) + PUSH_XMM_REG(xmm1) + PUSH_XMM_REG(xmm2) + PUSH_XMM_REG(xmm3) + PUSH_XMM_REG(xmm4) + PUSH_XMM_REG(xmm5) + PUSH_XMM_REG(xmm6) + PUSH_XMM_REG(xmm7) +#endif + + // MMX registers are not used to pass arguments so we do not save them + + // Stack is just 8-byte aligned but callee will re-align to 16 + call _libXcomposite_so_tramp_resolve + +#ifdef __AVX512F__ + POP_ZMM_REG(zmm7) + POP_ZMM_REG(zmm6) + POP_ZMM_REG(zmm5) + POP_ZMM_REG(zmm4) + POP_ZMM_REG(zmm3) + POP_ZMM_REG(zmm2) + POP_ZMM_REG(zmm1) + POP_ZMM_REG(zmm0) // 16 +#elif defined __AVX__ + POP_YMM_REG(ymm7) + POP_YMM_REG(ymm6) + POP_YMM_REG(ymm5) + POP_YMM_REG(ymm4) + POP_YMM_REG(ymm3) + POP_YMM_REG(ymm2) + POP_YMM_REG(ymm1) + POP_YMM_REG(ymm0) // 16 +#elif defined __SSE__ + POP_XMM_REG(xmm7) + POP_XMM_REG(xmm6) + POP_XMM_REG(xmm5) + POP_XMM_REG(xmm4) + POP_XMM_REG(xmm3) + POP_XMM_REG(xmm2) + POP_XMM_REG(xmm1) + POP_XMM_REG(xmm0) // 16 +#endif + + POP_REG(r15) + POP_REG(r14) // 16 + POP_REG(r13) + POP_REG(r12) // 16 + POP_REG(r11) + POP_REG(r10) // 16 + POP_REG(r9) + POP_REG(r8) // 16 + POP_REG(rsi) + POP_REG(rbp) // 16 + POP_REG(rdx) + POP_REG(rcx) // 16 + POP_REG(rbx) + POP_REG(rbx) // 16 + POP_REG(rdi) + + ret + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeCreateRegionFromBorderClip + .p2align 4 + .type XCompositeCreateRegionFromBorderClip, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeCreateRegionFromBorderClip +#endif +XCompositeCreateRegionFromBorderClip: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXcomposite_so_tramp_table+0(%rip) + je 2f +1: + jmp *_libXcomposite_so_tramp_table+0(%rip) +2: + pushq $0 + .cfi_adjust_cfa_offset 8 + call _libXcomposite_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeFindDisplay + .p2align 4 + .type XCompositeFindDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeFindDisplay +#endif +XCompositeFindDisplay: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXcomposite_so_tramp_table+8(%rip) + je 2f +1: + jmp *_libXcomposite_so_tramp_table+8(%rip) +2: + pushq $1 + .cfi_adjust_cfa_offset 8 + call _libXcomposite_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeGetOverlayWindow + .p2align 4 + .type XCompositeGetOverlayWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeGetOverlayWindow +#endif +XCompositeGetOverlayWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXcomposite_so_tramp_table+16(%rip) + je 2f +1: + jmp *_libXcomposite_so_tramp_table+16(%rip) +2: + pushq $2 + .cfi_adjust_cfa_offset 8 + call _libXcomposite_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeNameWindowPixmap + .p2align 4 + .type XCompositeNameWindowPixmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeNameWindowPixmap +#endif +XCompositeNameWindowPixmap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXcomposite_so_tramp_table+24(%rip) + je 2f +1: + jmp *_libXcomposite_so_tramp_table+24(%rip) +2: + pushq $3 + .cfi_adjust_cfa_offset 8 + call _libXcomposite_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeQueryExtension + .p2align 4 + .type XCompositeQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeQueryExtension +#endif +XCompositeQueryExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXcomposite_so_tramp_table+32(%rip) + je 2f +1: + jmp *_libXcomposite_so_tramp_table+32(%rip) +2: + pushq $4 + .cfi_adjust_cfa_offset 8 + call _libXcomposite_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeQueryVersion + .p2align 4 + .type XCompositeQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeQueryVersion +#endif +XCompositeQueryVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXcomposite_so_tramp_table+40(%rip) + je 2f +1: + jmp *_libXcomposite_so_tramp_table+40(%rip) +2: + pushq $5 + .cfi_adjust_cfa_offset 8 + call _libXcomposite_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeRedirectSubwindows + .p2align 4 + .type XCompositeRedirectSubwindows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeRedirectSubwindows +#endif +XCompositeRedirectSubwindows: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXcomposite_so_tramp_table+48(%rip) + je 2f +1: + jmp *_libXcomposite_so_tramp_table+48(%rip) +2: + pushq $6 + .cfi_adjust_cfa_offset 8 + call _libXcomposite_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeRedirectWindow + .p2align 4 + .type XCompositeRedirectWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeRedirectWindow +#endif +XCompositeRedirectWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXcomposite_so_tramp_table+56(%rip) + je 2f +1: + jmp *_libXcomposite_so_tramp_table+56(%rip) +2: + pushq $7 + .cfi_adjust_cfa_offset 8 + call _libXcomposite_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeReleaseOverlayWindow + .p2align 4 + .type XCompositeReleaseOverlayWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeReleaseOverlayWindow +#endif +XCompositeReleaseOverlayWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXcomposite_so_tramp_table+64(%rip) + je 2f +1: + jmp *_libXcomposite_so_tramp_table+64(%rip) +2: + pushq $8 + .cfi_adjust_cfa_offset 8 + call _libXcomposite_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeUnredirectSubwindows + .p2align 4 + .type XCompositeUnredirectSubwindows, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeUnredirectSubwindows +#endif +XCompositeUnredirectSubwindows: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXcomposite_so_tramp_table+72(%rip) + je 2f +1: + jmp *_libXcomposite_so_tramp_table+72(%rip) +2: + pushq $9 + .cfi_adjust_cfa_offset 8 + call _libXcomposite_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeUnredirectWindow + .p2align 4 + .type XCompositeUnredirectWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeUnredirectWindow +#endif +XCompositeUnredirectWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXcomposite_so_tramp_table+80(%rip) + je 2f +1: + jmp *_libXcomposite_so_tramp_table+80(%rip) +2: + pushq $10 + .cfi_adjust_cfa_offset 8 + call _libXcomposite_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XCompositeVersion + .p2align 4 + .type XCompositeVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XCompositeVersion +#endif +XCompositeVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXcomposite_so_tramp_table+88(%rip) + je 2f +1: + jmp *_libXcomposite_so_tramp_table+88(%rip) +2: + pushq $11 + .cfi_adjust_cfa_offset 8 + call _libXcomposite_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXdamage.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXdamage.so.init.c new file mode 100644 index 0000000..267ca69 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXdamage.so.init.c @@ -0,0 +1,251 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libXdamage.so.1: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libXdamage.so.1"); + CHECK(lib_handle, "failed to load library 'libXdamage.so.1' via callback ''"); +#else + lib_handle = dlopen("libXdamage.so.1", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libXdamage.so.1' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "XDamageAdd", + "XDamageCreate", + "XDamageDestroy", + "XDamageFindDisplay", + "XDamageQueryExtension", + "XDamageQueryVersion", + "XDamageSubtract", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libXdamage_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libXdamage_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libXdamage_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libXdamage_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libXdamage_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libXdamage_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libXdamage_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libXdamage_so_tramp_table, 0, SYM_COUNT * sizeof(_libXdamage_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXdamage.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXdamage.so.tramp.S new file mode 100644 index 0000000..0d135f6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXdamage.so.tramp.S @@ -0,0 +1,396 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libXdamage_so_tramp_table + .hidden _libXdamage_so_tramp_table + .align 8 +_libXdamage_so_tramp_table: + .zero 64 + + .text + + .globl _libXdamage_so_tramp_resolve + .hidden _libXdamage_so_tramp_resolve + + .globl _libXdamage_so_save_regs_and_resolve + .hidden _libXdamage_so_save_regs_and_resolve + .type _libXdamage_so_save_regs_and_resolve, %function +_libXdamage_so_save_regs_and_resolve: + .cfi_startproc + +#define PUSH_REG(reg) pushq %reg ; .cfi_adjust_cfa_offset 8; .cfi_rel_offset reg, 0 +#define POP_REG(reg) popq %reg ; .cfi_adjust_cfa_offset -8; .cfi_restore reg + +#define DEC_STACK(d) subq $d, %rsp; .cfi_adjust_cfa_offset d +#define INC_STACK(d) addq $d, %rsp; .cfi_adjust_cfa_offset -d + +#define PUSH_MMX_REG(reg) DEC_STACK(8); movq %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_MMX_REG(reg) movq (%rsp), %reg; .cfi_restore reg; INC_STACK(8) + +#define PUSH_XMM_REG(reg) DEC_STACK(16); movdqa %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_XMM_REG(reg) movdqa (%rsp), %reg; .cfi_restore reg; INC_STACK(16) + +// TODO: cfi_offset/cfi_restore +#define PUSH_YMM_REG(reg) DEC_STACK(32); vmovdqu %reg, (%rsp) +#define POP_YMM_REG(reg) vmovdqu (%rsp), %reg; INC_STACK(32) + +// TODO: cfi_offset/cfi_restore +#define PUSH_ZMM_REG(reg) DEC_STACK(64); vmovdqu32 %reg, (%rsp) +#define POP_ZMM_REG(reg) vmovdqu32 (%rsp), %reg; INC_STACK(64) + + // Slow path which calls dlsym, taken only on first call. + // All registers are stored to handle arbitrary calling conventions + // (except x87 FPU registers which do not have to be preserved). + // For Dwarf directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + .cfi_def_cfa_offset 8 // Return address + + PUSH_REG(rdi) // 16 + mov 0x10(%rsp), %rdi + PUSH_REG(rbx) + PUSH_REG(rbx) // 16 + PUSH_REG(rcx) + PUSH_REG(rdx) // 16 + PUSH_REG(rbp) + PUSH_REG(rsi) // 16 + PUSH_REG(r8) + PUSH_REG(r9) // 16 + PUSH_REG(r10) + PUSH_REG(r11) // 16 + PUSH_REG(r12) + PUSH_REG(r13) // 16 + PUSH_REG(r14) + PUSH_REG(r15) // 16 + + // Maybe use cpuid instead of macro to detect current vector size... +#ifdef __AVX512F__ + PUSH_ZMM_REG(zmm0) + PUSH_ZMM_REG(zmm1) + PUSH_ZMM_REG(zmm2) + PUSH_ZMM_REG(zmm3) + PUSH_ZMM_REG(zmm4) + PUSH_ZMM_REG(zmm5) + PUSH_ZMM_REG(zmm6) + PUSH_ZMM_REG(zmm7) +#elif defined __AVX__ + PUSH_YMM_REG(ymm0) + PUSH_YMM_REG(ymm1) + PUSH_YMM_REG(ymm2) + PUSH_YMM_REG(ymm3) + PUSH_YMM_REG(ymm4) + PUSH_YMM_REG(ymm5) + PUSH_YMM_REG(ymm6) + PUSH_YMM_REG(ymm7) +#elif defined __SSE__ + PUSH_XMM_REG(xmm0) + PUSH_XMM_REG(xmm1) + PUSH_XMM_REG(xmm2) + PUSH_XMM_REG(xmm3) + PUSH_XMM_REG(xmm4) + PUSH_XMM_REG(xmm5) + PUSH_XMM_REG(xmm6) + PUSH_XMM_REG(xmm7) +#endif + + // MMX registers are not used to pass arguments so we do not save them + + // Stack is just 8-byte aligned but callee will re-align to 16 + call _libXdamage_so_tramp_resolve + +#ifdef __AVX512F__ + POP_ZMM_REG(zmm7) + POP_ZMM_REG(zmm6) + POP_ZMM_REG(zmm5) + POP_ZMM_REG(zmm4) + POP_ZMM_REG(zmm3) + POP_ZMM_REG(zmm2) + POP_ZMM_REG(zmm1) + POP_ZMM_REG(zmm0) // 16 +#elif defined __AVX__ + POP_YMM_REG(ymm7) + POP_YMM_REG(ymm6) + POP_YMM_REG(ymm5) + POP_YMM_REG(ymm4) + POP_YMM_REG(ymm3) + POP_YMM_REG(ymm2) + POP_YMM_REG(ymm1) + POP_YMM_REG(ymm0) // 16 +#elif defined __SSE__ + POP_XMM_REG(xmm7) + POP_XMM_REG(xmm6) + POP_XMM_REG(xmm5) + POP_XMM_REG(xmm4) + POP_XMM_REG(xmm3) + POP_XMM_REG(xmm2) + POP_XMM_REG(xmm1) + POP_XMM_REG(xmm0) // 16 +#endif + + POP_REG(r15) + POP_REG(r14) // 16 + POP_REG(r13) + POP_REG(r12) // 16 + POP_REG(r11) + POP_REG(r10) // 16 + POP_REG(r9) + POP_REG(r8) // 16 + POP_REG(rsi) + POP_REG(rbp) // 16 + POP_REG(rdx) + POP_REG(rcx) // 16 + POP_REG(rbx) + POP_REG(rbx) // 16 + POP_REG(rdi) + + ret + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDamageAdd + .p2align 4 + .type XDamageAdd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDamageAdd +#endif +XDamageAdd: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXdamage_so_tramp_table+0(%rip) + je 2f +1: + jmp *_libXdamage_so_tramp_table+0(%rip) +2: + pushq $0 + .cfi_adjust_cfa_offset 8 + call _libXdamage_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDamageCreate + .p2align 4 + .type XDamageCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDamageCreate +#endif +XDamageCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXdamage_so_tramp_table+8(%rip) + je 2f +1: + jmp *_libXdamage_so_tramp_table+8(%rip) +2: + pushq $1 + .cfi_adjust_cfa_offset 8 + call _libXdamage_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDamageDestroy + .p2align 4 + .type XDamageDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDamageDestroy +#endif +XDamageDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXdamage_so_tramp_table+16(%rip) + je 2f +1: + jmp *_libXdamage_so_tramp_table+16(%rip) +2: + pushq $2 + .cfi_adjust_cfa_offset 8 + call _libXdamage_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDamageFindDisplay + .p2align 4 + .type XDamageFindDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDamageFindDisplay +#endif +XDamageFindDisplay: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXdamage_so_tramp_table+24(%rip) + je 2f +1: + jmp *_libXdamage_so_tramp_table+24(%rip) +2: + pushq $3 + .cfi_adjust_cfa_offset 8 + call _libXdamage_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDamageQueryExtension + .p2align 4 + .type XDamageQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDamageQueryExtension +#endif +XDamageQueryExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXdamage_so_tramp_table+32(%rip) + je 2f +1: + jmp *_libXdamage_so_tramp_table+32(%rip) +2: + pushq $4 + .cfi_adjust_cfa_offset 8 + call _libXdamage_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDamageQueryVersion + .p2align 4 + .type XDamageQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDamageQueryVersion +#endif +XDamageQueryVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXdamage_so_tramp_table+40(%rip) + je 2f +1: + jmp *_libXdamage_so_tramp_table+40(%rip) +2: + pushq $5 + .cfi_adjust_cfa_offset 8 + call _libXdamage_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XDamageSubtract + .p2align 4 + .type XDamageSubtract, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XDamageSubtract +#endif +XDamageSubtract: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXdamage_so_tramp_table+48(%rip) + je 2f +1: + jmp *_libXdamage_so_tramp_table+48(%rip) +2: + pushq $6 + .cfi_adjust_cfa_offset 8 + call _libXdamage_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXext.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXext.so.init.c new file mode 100644 index 0000000..1b1166d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXext.so.init.c @@ -0,0 +1,376 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libXext.so.6: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libXext.so.6"); + CHECK(lib_handle, "failed to load library 'libXext.so.6' via callback ''"); +#else + lib_handle = dlopen("libXext.so.6", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libXext.so.6' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "DPMSCapable", + "DPMSDisable", + "DPMSEnable", + "DPMSForceLevel", + "DPMSGetTimeouts", + "DPMSGetVersion", + "DPMSInfo", + "DPMSQueryExtension", + "DPMSSetTimeouts", + "XGEQueryExtension", + "XGEQueryVersion", + "XLbxGetEventBase", + "XLbxQueryExtension", + "XLbxQueryVersion", + "XMITMiscGetBugMode", + "XMITMiscQueryExtension", + "XMITMiscSetBugMode", + "XMissingExtension", + "XSecurityAllocXauth", + "XSecurityFreeXauth", + "XSecurityGenerateAuthorization", + "XSecurityQueryExtension", + "XSecurityRevokeAuthorization", + "XSetExtensionErrorHandler", + "XShapeCombineMask", + "XShapeCombineRectangles", + "XShapeCombineRegion", + "XShapeCombineShape", + "XShapeGetRectangles", + "XShapeInputSelected", + "XShapeOffsetShape", + "XShapeQueryExtension", + "XShapeQueryExtents", + "XShapeQueryVersion", + "XShapeSelectInput", + "XShmAttach", + "XShmCreateImage", + "XShmCreatePixmap", + "XShmDetach", + "XShmGetEventBase", + "XShmGetImage", + "XShmPixmapFormat", + "XShmPutImage", + "XShmQueryExtension", + "XShmQueryVersion", + "XSyncAwait", + "XSyncAwaitFence", + "XSyncChangeAlarm", + "XSyncChangeCounter", + "XSyncCreateAlarm", + "XSyncCreateCounter", + "XSyncCreateFence", + "XSyncDestroyAlarm", + "XSyncDestroyCounter", + "XSyncDestroyFence", + "XSyncFreeSystemCounterList", + "XSyncGetPriority", + "XSyncInitialize", + "XSyncIntToValue", + "XSyncIntsToValue", + "XSyncListSystemCounters", + "XSyncMaxValue", + "XSyncMinValue", + "XSyncQueryAlarm", + "XSyncQueryCounter", + "XSyncQueryExtension", + "XSyncQueryFence", + "XSyncResetFence", + "XSyncSetCounter", + "XSyncSetPriority", + "XSyncTriggerFence", + "XSyncValueAdd", + "XSyncValueEqual", + "XSyncValueGreaterOrEqual", + "XSyncValueGreaterThan", + "XSyncValueHigh32", + "XSyncValueIsNegative", + "XSyncValueIsPositive", + "XSyncValueIsZero", + "XSyncValueLessOrEqual", + "XSyncValueLessThan", + "XSyncValueLow32", + "XSyncValueSubtract", + "XTestFakeInput", + "XTestFlush", + "XTestGetInput", + "XTestMovePointer", + "XTestPressButton", + "XTestPressKey", + "XTestQueryInputSize", + "XTestReset", + "XTestStopInput", + "XagCreateAssociation", + "XagCreateEmbeddedApplicationGroup", + "XagCreateNonembeddedApplicationGroup", + "XagDestroyApplicationGroup", + "XagDestroyAssociation", + "XagGetApplicationGroupAttributes", + "XagQueryApplicationGroup", + "XagQueryVersion", + "XcupGetReservedColormapEntries", + "XcupQueryVersion", + "XcupStoreColors", + "XdbeAllocateBackBufferName", + "XdbeBeginIdiom", + "XdbeDeallocateBackBufferName", + "XdbeEndIdiom", + "XdbeFreeVisualInfo", + "XdbeGetBackBufferAttributes", + "XdbeGetVisualInfo", + "XdbeQueryExtension", + "XdbeSwapBuffers", + "XeviGetVisualInfo", + "XeviQueryExtension", + "XeviQueryVersion", + "XextAddDisplay", + "XextCreateExtension", + "XextDestroyExtension", + "XextFindDisplay", + "XextRemoveDisplay", + "XmbufChangeBufferAttributes", + "XmbufChangeWindowAttributes", + "XmbufClearBufferArea", + "XmbufCreateBuffers", + "XmbufCreateStereoWindow", + "XmbufDestroyBuffers", + "XmbufDisplayBuffers", + "XmbufGetBufferAttributes", + "XmbufGetScreenInfo", + "XmbufGetVersion", + "XmbufGetWindowAttributes", + "XmbufQueryExtension", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libXext_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libXext_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libXext_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libXext_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libXext_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libXext_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libXext_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libXext_so_tramp_table, 0, SYM_COUNT * sizeof(_libXext_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXext.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXext.so.tramp.S new file mode 100644 index 0000000..a0859ec --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXext.so.tramp.S @@ -0,0 +1,4646 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libXext_so_tramp_table + .hidden _libXext_so_tramp_table + .align 8 +_libXext_so_tramp_table: + .zero 1064 + + .text + + .globl _libXext_so_tramp_resolve + .hidden _libXext_so_tramp_resolve + + .globl _libXext_so_save_regs_and_resolve + .hidden _libXext_so_save_regs_and_resolve + .type _libXext_so_save_regs_and_resolve, %function +_libXext_so_save_regs_and_resolve: + .cfi_startproc + +#define PUSH_REG(reg) pushq %reg ; .cfi_adjust_cfa_offset 8; .cfi_rel_offset reg, 0 +#define POP_REG(reg) popq %reg ; .cfi_adjust_cfa_offset -8; .cfi_restore reg + +#define DEC_STACK(d) subq $d, %rsp; .cfi_adjust_cfa_offset d +#define INC_STACK(d) addq $d, %rsp; .cfi_adjust_cfa_offset -d + +#define PUSH_MMX_REG(reg) DEC_STACK(8); movq %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_MMX_REG(reg) movq (%rsp), %reg; .cfi_restore reg; INC_STACK(8) + +#define PUSH_XMM_REG(reg) DEC_STACK(16); movdqa %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_XMM_REG(reg) movdqa (%rsp), %reg; .cfi_restore reg; INC_STACK(16) + +// TODO: cfi_offset/cfi_restore +#define PUSH_YMM_REG(reg) DEC_STACK(32); vmovdqu %reg, (%rsp) +#define POP_YMM_REG(reg) vmovdqu (%rsp), %reg; INC_STACK(32) + +// TODO: cfi_offset/cfi_restore +#define PUSH_ZMM_REG(reg) DEC_STACK(64); vmovdqu32 %reg, (%rsp) +#define POP_ZMM_REG(reg) vmovdqu32 (%rsp), %reg; INC_STACK(64) + + // Slow path which calls dlsym, taken only on first call. + // All registers are stored to handle arbitrary calling conventions + // (except x87 FPU registers which do not have to be preserved). + // For Dwarf directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + .cfi_def_cfa_offset 8 // Return address + + PUSH_REG(rdi) // 16 + mov 0x10(%rsp), %rdi + PUSH_REG(rbx) + PUSH_REG(rbx) // 16 + PUSH_REG(rcx) + PUSH_REG(rdx) // 16 + PUSH_REG(rbp) + PUSH_REG(rsi) // 16 + PUSH_REG(r8) + PUSH_REG(r9) // 16 + PUSH_REG(r10) + PUSH_REG(r11) // 16 + PUSH_REG(r12) + PUSH_REG(r13) // 16 + PUSH_REG(r14) + PUSH_REG(r15) // 16 + + // Maybe use cpuid instead of macro to detect current vector size... +#ifdef __AVX512F__ + PUSH_ZMM_REG(zmm0) + PUSH_ZMM_REG(zmm1) + PUSH_ZMM_REG(zmm2) + PUSH_ZMM_REG(zmm3) + PUSH_ZMM_REG(zmm4) + PUSH_ZMM_REG(zmm5) + PUSH_ZMM_REG(zmm6) + PUSH_ZMM_REG(zmm7) +#elif defined __AVX__ + PUSH_YMM_REG(ymm0) + PUSH_YMM_REG(ymm1) + PUSH_YMM_REG(ymm2) + PUSH_YMM_REG(ymm3) + PUSH_YMM_REG(ymm4) + PUSH_YMM_REG(ymm5) + PUSH_YMM_REG(ymm6) + PUSH_YMM_REG(ymm7) +#elif defined __SSE__ + PUSH_XMM_REG(xmm0) + PUSH_XMM_REG(xmm1) + PUSH_XMM_REG(xmm2) + PUSH_XMM_REG(xmm3) + PUSH_XMM_REG(xmm4) + PUSH_XMM_REG(xmm5) + PUSH_XMM_REG(xmm6) + PUSH_XMM_REG(xmm7) +#endif + + // MMX registers are not used to pass arguments so we do not save them + + // Stack is just 8-byte aligned but callee will re-align to 16 + call _libXext_so_tramp_resolve + +#ifdef __AVX512F__ + POP_ZMM_REG(zmm7) + POP_ZMM_REG(zmm6) + POP_ZMM_REG(zmm5) + POP_ZMM_REG(zmm4) + POP_ZMM_REG(zmm3) + POP_ZMM_REG(zmm2) + POP_ZMM_REG(zmm1) + POP_ZMM_REG(zmm0) // 16 +#elif defined __AVX__ + POP_YMM_REG(ymm7) + POP_YMM_REG(ymm6) + POP_YMM_REG(ymm5) + POP_YMM_REG(ymm4) + POP_YMM_REG(ymm3) + POP_YMM_REG(ymm2) + POP_YMM_REG(ymm1) + POP_YMM_REG(ymm0) // 16 +#elif defined __SSE__ + POP_XMM_REG(xmm7) + POP_XMM_REG(xmm6) + POP_XMM_REG(xmm5) + POP_XMM_REG(xmm4) + POP_XMM_REG(xmm3) + POP_XMM_REG(xmm2) + POP_XMM_REG(xmm1) + POP_XMM_REG(xmm0) // 16 +#endif + + POP_REG(r15) + POP_REG(r14) // 16 + POP_REG(r13) + POP_REG(r12) // 16 + POP_REG(r11) + POP_REG(r10) // 16 + POP_REG(r9) + POP_REG(r8) // 16 + POP_REG(rsi) + POP_REG(rbp) // 16 + POP_REG(rdx) + POP_REG(rcx) // 16 + POP_REG(rbx) + POP_REG(rbx) // 16 + POP_REG(rdi) + + ret + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl DPMSCapable + .p2align 4 + .type DPMSCapable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden DPMSCapable +#endif +DPMSCapable: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+0(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+0(%rip) +2: + pushq $0 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl DPMSDisable + .p2align 4 + .type DPMSDisable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden DPMSDisable +#endif +DPMSDisable: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+8(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+8(%rip) +2: + pushq $1 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl DPMSEnable + .p2align 4 + .type DPMSEnable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden DPMSEnable +#endif +DPMSEnable: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+16(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+16(%rip) +2: + pushq $2 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl DPMSForceLevel + .p2align 4 + .type DPMSForceLevel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden DPMSForceLevel +#endif +DPMSForceLevel: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+24(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+24(%rip) +2: + pushq $3 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl DPMSGetTimeouts + .p2align 4 + .type DPMSGetTimeouts, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden DPMSGetTimeouts +#endif +DPMSGetTimeouts: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+32(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+32(%rip) +2: + pushq $4 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl DPMSGetVersion + .p2align 4 + .type DPMSGetVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden DPMSGetVersion +#endif +DPMSGetVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+40(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+40(%rip) +2: + pushq $5 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl DPMSInfo + .p2align 4 + .type DPMSInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden DPMSInfo +#endif +DPMSInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+48(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+48(%rip) +2: + pushq $6 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl DPMSQueryExtension + .p2align 4 + .type DPMSQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden DPMSQueryExtension +#endif +DPMSQueryExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+56(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+56(%rip) +2: + pushq $7 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl DPMSSetTimeouts + .p2align 4 + .type DPMSSetTimeouts, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden DPMSSetTimeouts +#endif +DPMSSetTimeouts: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+64(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+64(%rip) +2: + pushq $8 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGEQueryExtension + .p2align 4 + .type XGEQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGEQueryExtension +#endif +XGEQueryExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+72(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+72(%rip) +2: + pushq $9 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XGEQueryVersion + .p2align 4 + .type XGEQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XGEQueryVersion +#endif +XGEQueryVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+80(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+80(%rip) +2: + pushq $10 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLbxGetEventBase + .p2align 4 + .type XLbxGetEventBase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLbxGetEventBase +#endif +XLbxGetEventBase: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+88(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+88(%rip) +2: + pushq $11 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLbxQueryExtension + .p2align 4 + .type XLbxQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLbxQueryExtension +#endif +XLbxQueryExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+96(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+96(%rip) +2: + pushq $12 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XLbxQueryVersion + .p2align 4 + .type XLbxQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XLbxQueryVersion +#endif +XLbxQueryVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+104(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+104(%rip) +2: + pushq $13 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMITMiscGetBugMode + .p2align 4 + .type XMITMiscGetBugMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMITMiscGetBugMode +#endif +XMITMiscGetBugMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+112(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+112(%rip) +2: + pushq $14 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMITMiscQueryExtension + .p2align 4 + .type XMITMiscQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMITMiscQueryExtension +#endif +XMITMiscQueryExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+120(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+120(%rip) +2: + pushq $15 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMITMiscSetBugMode + .p2align 4 + .type XMITMiscSetBugMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMITMiscSetBugMode +#endif +XMITMiscSetBugMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+128(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+128(%rip) +2: + pushq $16 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XMissingExtension + .p2align 4 + .type XMissingExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XMissingExtension +#endif +XMissingExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+136(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+136(%rip) +2: + pushq $17 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSecurityAllocXauth + .p2align 4 + .type XSecurityAllocXauth, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSecurityAllocXauth +#endif +XSecurityAllocXauth: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+144(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+144(%rip) +2: + pushq $18 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSecurityFreeXauth + .p2align 4 + .type XSecurityFreeXauth, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSecurityFreeXauth +#endif +XSecurityFreeXauth: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+152(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+152(%rip) +2: + pushq $19 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSecurityGenerateAuthorization + .p2align 4 + .type XSecurityGenerateAuthorization, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSecurityGenerateAuthorization +#endif +XSecurityGenerateAuthorization: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+160(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+160(%rip) +2: + pushq $20 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSecurityQueryExtension + .p2align 4 + .type XSecurityQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSecurityQueryExtension +#endif +XSecurityQueryExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+168(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+168(%rip) +2: + pushq $21 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSecurityRevokeAuthorization + .p2align 4 + .type XSecurityRevokeAuthorization, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSecurityRevokeAuthorization +#endif +XSecurityRevokeAuthorization: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+176(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+176(%rip) +2: + pushq $22 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSetExtensionErrorHandler + .p2align 4 + .type XSetExtensionErrorHandler, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSetExtensionErrorHandler +#endif +XSetExtensionErrorHandler: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+184(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+184(%rip) +2: + pushq $23 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeCombineMask + .p2align 4 + .type XShapeCombineMask, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeCombineMask +#endif +XShapeCombineMask: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+192(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+192(%rip) +2: + pushq $24 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeCombineRectangles + .p2align 4 + .type XShapeCombineRectangles, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeCombineRectangles +#endif +XShapeCombineRectangles: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+200(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+200(%rip) +2: + pushq $25 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeCombineRegion + .p2align 4 + .type XShapeCombineRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeCombineRegion +#endif +XShapeCombineRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+208(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+208(%rip) +2: + pushq $26 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeCombineShape + .p2align 4 + .type XShapeCombineShape, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeCombineShape +#endif +XShapeCombineShape: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+216(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+216(%rip) +2: + pushq $27 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeGetRectangles + .p2align 4 + .type XShapeGetRectangles, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeGetRectangles +#endif +XShapeGetRectangles: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+224(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+224(%rip) +2: + pushq $28 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeInputSelected + .p2align 4 + .type XShapeInputSelected, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeInputSelected +#endif +XShapeInputSelected: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+232(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+232(%rip) +2: + pushq $29 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeOffsetShape + .p2align 4 + .type XShapeOffsetShape, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeOffsetShape +#endif +XShapeOffsetShape: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+240(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+240(%rip) +2: + pushq $30 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeQueryExtension + .p2align 4 + .type XShapeQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeQueryExtension +#endif +XShapeQueryExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+248(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+248(%rip) +2: + pushq $31 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeQueryExtents + .p2align 4 + .type XShapeQueryExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeQueryExtents +#endif +XShapeQueryExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+256(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+256(%rip) +2: + pushq $32 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeQueryVersion + .p2align 4 + .type XShapeQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeQueryVersion +#endif +XShapeQueryVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+264(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+264(%rip) +2: + pushq $33 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShapeSelectInput + .p2align 4 + .type XShapeSelectInput, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShapeSelectInput +#endif +XShapeSelectInput: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+272(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+272(%rip) +2: + pushq $34 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmAttach + .p2align 4 + .type XShmAttach, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmAttach +#endif +XShmAttach: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+280(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+280(%rip) +2: + pushq $35 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmCreateImage + .p2align 4 + .type XShmCreateImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmCreateImage +#endif +XShmCreateImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+288(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+288(%rip) +2: + pushq $36 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmCreatePixmap + .p2align 4 + .type XShmCreatePixmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmCreatePixmap +#endif +XShmCreatePixmap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+296(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+296(%rip) +2: + pushq $37 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmDetach + .p2align 4 + .type XShmDetach, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmDetach +#endif +XShmDetach: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+304(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+304(%rip) +2: + pushq $38 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmGetEventBase + .p2align 4 + .type XShmGetEventBase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmGetEventBase +#endif +XShmGetEventBase: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+312(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+312(%rip) +2: + pushq $39 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmGetImage + .p2align 4 + .type XShmGetImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmGetImage +#endif +XShmGetImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+320(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+320(%rip) +2: + pushq $40 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmPixmapFormat + .p2align 4 + .type XShmPixmapFormat, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmPixmapFormat +#endif +XShmPixmapFormat: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+328(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+328(%rip) +2: + pushq $41 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmPutImage + .p2align 4 + .type XShmPutImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmPutImage +#endif +XShmPutImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+336(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+336(%rip) +2: + pushq $42 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmQueryExtension + .p2align 4 + .type XShmQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmQueryExtension +#endif +XShmQueryExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+344(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+344(%rip) +2: + pushq $43 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XShmQueryVersion + .p2align 4 + .type XShmQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XShmQueryVersion +#endif +XShmQueryVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+352(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+352(%rip) +2: + pushq $44 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncAwait + .p2align 4 + .type XSyncAwait, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncAwait +#endif +XSyncAwait: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+360(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+360(%rip) +2: + pushq $45 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncAwaitFence + .p2align 4 + .type XSyncAwaitFence, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncAwaitFence +#endif +XSyncAwaitFence: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+368(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+368(%rip) +2: + pushq $46 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncChangeAlarm + .p2align 4 + .type XSyncChangeAlarm, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncChangeAlarm +#endif +XSyncChangeAlarm: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+376(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+376(%rip) +2: + pushq $47 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncChangeCounter + .p2align 4 + .type XSyncChangeCounter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncChangeCounter +#endif +XSyncChangeCounter: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+384(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+384(%rip) +2: + pushq $48 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncCreateAlarm + .p2align 4 + .type XSyncCreateAlarm, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncCreateAlarm +#endif +XSyncCreateAlarm: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+392(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+392(%rip) +2: + pushq $49 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncCreateCounter + .p2align 4 + .type XSyncCreateCounter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncCreateCounter +#endif +XSyncCreateCounter: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+400(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+400(%rip) +2: + pushq $50 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncCreateFence + .p2align 4 + .type XSyncCreateFence, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncCreateFence +#endif +XSyncCreateFence: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+408(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+408(%rip) +2: + pushq $51 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncDestroyAlarm + .p2align 4 + .type XSyncDestroyAlarm, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncDestroyAlarm +#endif +XSyncDestroyAlarm: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+416(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+416(%rip) +2: + pushq $52 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncDestroyCounter + .p2align 4 + .type XSyncDestroyCounter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncDestroyCounter +#endif +XSyncDestroyCounter: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+424(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+424(%rip) +2: + pushq $53 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncDestroyFence + .p2align 4 + .type XSyncDestroyFence, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncDestroyFence +#endif +XSyncDestroyFence: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+432(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+432(%rip) +2: + pushq $54 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncFreeSystemCounterList + .p2align 4 + .type XSyncFreeSystemCounterList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncFreeSystemCounterList +#endif +XSyncFreeSystemCounterList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+440(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+440(%rip) +2: + pushq $55 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncGetPriority + .p2align 4 + .type XSyncGetPriority, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncGetPriority +#endif +XSyncGetPriority: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+448(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+448(%rip) +2: + pushq $56 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncInitialize + .p2align 4 + .type XSyncInitialize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncInitialize +#endif +XSyncInitialize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+456(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+456(%rip) +2: + pushq $57 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncIntToValue + .p2align 4 + .type XSyncIntToValue, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncIntToValue +#endif +XSyncIntToValue: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+464(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+464(%rip) +2: + pushq $58 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncIntsToValue + .p2align 4 + .type XSyncIntsToValue, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncIntsToValue +#endif +XSyncIntsToValue: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+472(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+472(%rip) +2: + pushq $59 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncListSystemCounters + .p2align 4 + .type XSyncListSystemCounters, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncListSystemCounters +#endif +XSyncListSystemCounters: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+480(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+480(%rip) +2: + pushq $60 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncMaxValue + .p2align 4 + .type XSyncMaxValue, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncMaxValue +#endif +XSyncMaxValue: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+488(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+488(%rip) +2: + pushq $61 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncMinValue + .p2align 4 + .type XSyncMinValue, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncMinValue +#endif +XSyncMinValue: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+496(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+496(%rip) +2: + pushq $62 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncQueryAlarm + .p2align 4 + .type XSyncQueryAlarm, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncQueryAlarm +#endif +XSyncQueryAlarm: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+504(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+504(%rip) +2: + pushq $63 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncQueryCounter + .p2align 4 + .type XSyncQueryCounter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncQueryCounter +#endif +XSyncQueryCounter: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+512(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+512(%rip) +2: + pushq $64 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncQueryExtension + .p2align 4 + .type XSyncQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncQueryExtension +#endif +XSyncQueryExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+520(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+520(%rip) +2: + pushq $65 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncQueryFence + .p2align 4 + .type XSyncQueryFence, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncQueryFence +#endif +XSyncQueryFence: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+528(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+528(%rip) +2: + pushq $66 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncResetFence + .p2align 4 + .type XSyncResetFence, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncResetFence +#endif +XSyncResetFence: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+536(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+536(%rip) +2: + pushq $67 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncSetCounter + .p2align 4 + .type XSyncSetCounter, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncSetCounter +#endif +XSyncSetCounter: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+544(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+544(%rip) +2: + pushq $68 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncSetPriority + .p2align 4 + .type XSyncSetPriority, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncSetPriority +#endif +XSyncSetPriority: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+552(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+552(%rip) +2: + pushq $69 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncTriggerFence + .p2align 4 + .type XSyncTriggerFence, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncTriggerFence +#endif +XSyncTriggerFence: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+560(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+560(%rip) +2: + pushq $70 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueAdd + .p2align 4 + .type XSyncValueAdd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueAdd +#endif +XSyncValueAdd: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+568(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+568(%rip) +2: + pushq $71 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueEqual + .p2align 4 + .type XSyncValueEqual, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueEqual +#endif +XSyncValueEqual: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+576(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+576(%rip) +2: + pushq $72 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueGreaterOrEqual + .p2align 4 + .type XSyncValueGreaterOrEqual, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueGreaterOrEqual +#endif +XSyncValueGreaterOrEqual: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+584(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+584(%rip) +2: + pushq $73 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueGreaterThan + .p2align 4 + .type XSyncValueGreaterThan, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueGreaterThan +#endif +XSyncValueGreaterThan: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+592(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+592(%rip) +2: + pushq $74 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueHigh32 + .p2align 4 + .type XSyncValueHigh32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueHigh32 +#endif +XSyncValueHigh32: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+600(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+600(%rip) +2: + pushq $75 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueIsNegative + .p2align 4 + .type XSyncValueIsNegative, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueIsNegative +#endif +XSyncValueIsNegative: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+608(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+608(%rip) +2: + pushq $76 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueIsPositive + .p2align 4 + .type XSyncValueIsPositive, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueIsPositive +#endif +XSyncValueIsPositive: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+616(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+616(%rip) +2: + pushq $77 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueIsZero + .p2align 4 + .type XSyncValueIsZero, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueIsZero +#endif +XSyncValueIsZero: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+624(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+624(%rip) +2: + pushq $78 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueLessOrEqual + .p2align 4 + .type XSyncValueLessOrEqual, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueLessOrEqual +#endif +XSyncValueLessOrEqual: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+632(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+632(%rip) +2: + pushq $79 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueLessThan + .p2align 4 + .type XSyncValueLessThan, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueLessThan +#endif +XSyncValueLessThan: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+640(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+640(%rip) +2: + pushq $80 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueLow32 + .p2align 4 + .type XSyncValueLow32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueLow32 +#endif +XSyncValueLow32: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+648(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+648(%rip) +2: + pushq $81 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XSyncValueSubtract + .p2align 4 + .type XSyncValueSubtract, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XSyncValueSubtract +#endif +XSyncValueSubtract: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+656(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+656(%rip) +2: + pushq $82 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTestFakeInput + .p2align 4 + .type XTestFakeInput, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTestFakeInput +#endif +XTestFakeInput: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+664(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+664(%rip) +2: + pushq $83 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTestFlush + .p2align 4 + .type XTestFlush, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTestFlush +#endif +XTestFlush: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+672(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+672(%rip) +2: + pushq $84 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTestGetInput + .p2align 4 + .type XTestGetInput, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTestGetInput +#endif +XTestGetInput: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+680(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+680(%rip) +2: + pushq $85 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTestMovePointer + .p2align 4 + .type XTestMovePointer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTestMovePointer +#endif +XTestMovePointer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+688(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+688(%rip) +2: + pushq $86 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTestPressButton + .p2align 4 + .type XTestPressButton, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTestPressButton +#endif +XTestPressButton: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+696(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+696(%rip) +2: + pushq $87 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTestPressKey + .p2align 4 + .type XTestPressKey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTestPressKey +#endif +XTestPressKey: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+704(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+704(%rip) +2: + pushq $88 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTestQueryInputSize + .p2align 4 + .type XTestQueryInputSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTestQueryInputSize +#endif +XTestQueryInputSize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+712(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+712(%rip) +2: + pushq $89 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTestReset + .p2align 4 + .type XTestReset, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTestReset +#endif +XTestReset: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+720(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+720(%rip) +2: + pushq $90 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XTestStopInput + .p2align 4 + .type XTestStopInput, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XTestStopInput +#endif +XTestStopInput: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+728(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+728(%rip) +2: + pushq $91 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XagCreateAssociation + .p2align 4 + .type XagCreateAssociation, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XagCreateAssociation +#endif +XagCreateAssociation: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+736(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+736(%rip) +2: + pushq $92 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XagCreateEmbeddedApplicationGroup + .p2align 4 + .type XagCreateEmbeddedApplicationGroup, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XagCreateEmbeddedApplicationGroup +#endif +XagCreateEmbeddedApplicationGroup: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+744(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+744(%rip) +2: + pushq $93 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XagCreateNonembeddedApplicationGroup + .p2align 4 + .type XagCreateNonembeddedApplicationGroup, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XagCreateNonembeddedApplicationGroup +#endif +XagCreateNonembeddedApplicationGroup: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+752(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+752(%rip) +2: + pushq $94 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XagDestroyApplicationGroup + .p2align 4 + .type XagDestroyApplicationGroup, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XagDestroyApplicationGroup +#endif +XagDestroyApplicationGroup: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+760(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+760(%rip) +2: + pushq $95 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XagDestroyAssociation + .p2align 4 + .type XagDestroyAssociation, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XagDestroyAssociation +#endif +XagDestroyAssociation: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+768(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+768(%rip) +2: + pushq $96 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XagGetApplicationGroupAttributes + .p2align 4 + .type XagGetApplicationGroupAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XagGetApplicationGroupAttributes +#endif +XagGetApplicationGroupAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+776(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+776(%rip) +2: + pushq $97 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XagQueryApplicationGroup + .p2align 4 + .type XagQueryApplicationGroup, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XagQueryApplicationGroup +#endif +XagQueryApplicationGroup: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+784(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+784(%rip) +2: + pushq $98 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XagQueryVersion + .p2align 4 + .type XagQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XagQueryVersion +#endif +XagQueryVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+792(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+792(%rip) +2: + pushq $99 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcupGetReservedColormapEntries + .p2align 4 + .type XcupGetReservedColormapEntries, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcupGetReservedColormapEntries +#endif +XcupGetReservedColormapEntries: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+800(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+800(%rip) +2: + pushq $100 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcupQueryVersion + .p2align 4 + .type XcupQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcupQueryVersion +#endif +XcupQueryVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+808(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+808(%rip) +2: + pushq $101 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XcupStoreColors + .p2align 4 + .type XcupStoreColors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XcupStoreColors +#endif +XcupStoreColors: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+816(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+816(%rip) +2: + pushq $102 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XdbeAllocateBackBufferName + .p2align 4 + .type XdbeAllocateBackBufferName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XdbeAllocateBackBufferName +#endif +XdbeAllocateBackBufferName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+824(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+824(%rip) +2: + pushq $103 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XdbeBeginIdiom + .p2align 4 + .type XdbeBeginIdiom, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XdbeBeginIdiom +#endif +XdbeBeginIdiom: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+832(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+832(%rip) +2: + pushq $104 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XdbeDeallocateBackBufferName + .p2align 4 + .type XdbeDeallocateBackBufferName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XdbeDeallocateBackBufferName +#endif +XdbeDeallocateBackBufferName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+840(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+840(%rip) +2: + pushq $105 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XdbeEndIdiom + .p2align 4 + .type XdbeEndIdiom, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XdbeEndIdiom +#endif +XdbeEndIdiom: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+848(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+848(%rip) +2: + pushq $106 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XdbeFreeVisualInfo + .p2align 4 + .type XdbeFreeVisualInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XdbeFreeVisualInfo +#endif +XdbeFreeVisualInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+856(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+856(%rip) +2: + pushq $107 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XdbeGetBackBufferAttributes + .p2align 4 + .type XdbeGetBackBufferAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XdbeGetBackBufferAttributes +#endif +XdbeGetBackBufferAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+864(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+864(%rip) +2: + pushq $108 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XdbeGetVisualInfo + .p2align 4 + .type XdbeGetVisualInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XdbeGetVisualInfo +#endif +XdbeGetVisualInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+872(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+872(%rip) +2: + pushq $109 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XdbeQueryExtension + .p2align 4 + .type XdbeQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XdbeQueryExtension +#endif +XdbeQueryExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+880(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+880(%rip) +2: + pushq $110 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XdbeSwapBuffers + .p2align 4 + .type XdbeSwapBuffers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XdbeSwapBuffers +#endif +XdbeSwapBuffers: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+888(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+888(%rip) +2: + pushq $111 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XeviGetVisualInfo + .p2align 4 + .type XeviGetVisualInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XeviGetVisualInfo +#endif +XeviGetVisualInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+896(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+896(%rip) +2: + pushq $112 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XeviQueryExtension + .p2align 4 + .type XeviQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XeviQueryExtension +#endif +XeviQueryExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+904(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+904(%rip) +2: + pushq $113 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XeviQueryVersion + .p2align 4 + .type XeviQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XeviQueryVersion +#endif +XeviQueryVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+912(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+912(%rip) +2: + pushq $114 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XextAddDisplay + .p2align 4 + .type XextAddDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XextAddDisplay +#endif +XextAddDisplay: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+920(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+920(%rip) +2: + pushq $115 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XextCreateExtension + .p2align 4 + .type XextCreateExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XextCreateExtension +#endif +XextCreateExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+928(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+928(%rip) +2: + pushq $116 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XextDestroyExtension + .p2align 4 + .type XextDestroyExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XextDestroyExtension +#endif +XextDestroyExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+936(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+936(%rip) +2: + pushq $117 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XextFindDisplay + .p2align 4 + .type XextFindDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XextFindDisplay +#endif +XextFindDisplay: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+944(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+944(%rip) +2: + pushq $118 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XextRemoveDisplay + .p2align 4 + .type XextRemoveDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XextRemoveDisplay +#endif +XextRemoveDisplay: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+952(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+952(%rip) +2: + pushq $119 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufChangeBufferAttributes + .p2align 4 + .type XmbufChangeBufferAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufChangeBufferAttributes +#endif +XmbufChangeBufferAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+960(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+960(%rip) +2: + pushq $120 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufChangeWindowAttributes + .p2align 4 + .type XmbufChangeWindowAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufChangeWindowAttributes +#endif +XmbufChangeWindowAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+968(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+968(%rip) +2: + pushq $121 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufClearBufferArea + .p2align 4 + .type XmbufClearBufferArea, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufClearBufferArea +#endif +XmbufClearBufferArea: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+976(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+976(%rip) +2: + pushq $122 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufCreateBuffers + .p2align 4 + .type XmbufCreateBuffers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufCreateBuffers +#endif +XmbufCreateBuffers: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+984(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+984(%rip) +2: + pushq $123 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufCreateStereoWindow + .p2align 4 + .type XmbufCreateStereoWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufCreateStereoWindow +#endif +XmbufCreateStereoWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+992(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+992(%rip) +2: + pushq $124 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufDestroyBuffers + .p2align 4 + .type XmbufDestroyBuffers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufDestroyBuffers +#endif +XmbufDestroyBuffers: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+1000(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+1000(%rip) +2: + pushq $125 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufDisplayBuffers + .p2align 4 + .type XmbufDisplayBuffers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufDisplayBuffers +#endif +XmbufDisplayBuffers: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+1008(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+1008(%rip) +2: + pushq $126 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufGetBufferAttributes + .p2align 4 + .type XmbufGetBufferAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufGetBufferAttributes +#endif +XmbufGetBufferAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+1016(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+1016(%rip) +2: + pushq $127 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufGetScreenInfo + .p2align 4 + .type XmbufGetScreenInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufGetScreenInfo +#endif +XmbufGetScreenInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+1024(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+1024(%rip) +2: + pushq $128 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufGetVersion + .p2align 4 + .type XmbufGetVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufGetVersion +#endif +XmbufGetVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+1032(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+1032(%rip) +2: + pushq $129 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufGetWindowAttributes + .p2align 4 + .type XmbufGetWindowAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufGetWindowAttributes +#endif +XmbufGetWindowAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+1040(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+1040(%rip) +2: + pushq $130 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XmbufQueryExtension + .p2align 4 + .type XmbufQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XmbufQueryExtension +#endif +XmbufQueryExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXext_so_tramp_table+1048(%rip) + je 2f +1: + jmp *_libXext_so_tramp_table+1048(%rip) +2: + pushq $131 + .cfi_adjust_cfa_offset 8 + call _libXext_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXfixes.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXfixes.so.init.c new file mode 100644 index 0000000..dc737ea --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXfixes.so.init.c @@ -0,0 +1,282 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libXfixes.so.3: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libXfixes.so.3"); + CHECK(lib_handle, "failed to load library 'libXfixes.so.3' via callback ''"); +#else + lib_handle = dlopen("libXfixes.so.3", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libXfixes.so.3' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "XFixesChangeCursor", + "XFixesChangeCursorByName", + "XFixesChangeSaveSet", + "XFixesCopyRegion", + "XFixesCreatePointerBarrier", + "XFixesCreateRegion", + "XFixesCreateRegionFromBitmap", + "XFixesCreateRegionFromGC", + "XFixesCreateRegionFromPicture", + "XFixesCreateRegionFromWindow", + "XFixesDestroyPointerBarrier", + "XFixesDestroyRegion", + "XFixesExpandRegion", + "XFixesFetchRegion", + "XFixesFetchRegionAndBounds", + "XFixesFindDisplay", + "XFixesGetClientDisconnectMode", + "XFixesGetCursorImage", + "XFixesGetCursorName", + "XFixesHideCursor", + "XFixesIntersectRegion", + "XFixesInvertRegion", + "XFixesQueryExtension", + "XFixesQueryVersion", + "XFixesRegionExtents", + "XFixesSelectCursorInput", + "XFixesSelectSelectionInput", + "XFixesSetClientDisconnectMode", + "XFixesSetCursorName", + "XFixesSetGCClipRegion", + "XFixesSetPictureClipRegion", + "XFixesSetRegion", + "XFixesSetWindowShapeRegion", + "XFixesShowCursor", + "XFixesSubtractRegion", + "XFixesTranslateRegion", + "XFixesUnionRegion", + "XFixesVersion", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libXfixes_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libXfixes_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libXfixes_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libXfixes_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libXfixes_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libXfixes_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libXfixes_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libXfixes_so_tramp_table, 0, SYM_COUNT * sizeof(_libXfixes_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXfixes.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXfixes.so.tramp.S new file mode 100644 index 0000000..822b60b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXfixes.so.tramp.S @@ -0,0 +1,1450 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libXfixes_so_tramp_table + .hidden _libXfixes_so_tramp_table + .align 8 +_libXfixes_so_tramp_table: + .zero 312 + + .text + + .globl _libXfixes_so_tramp_resolve + .hidden _libXfixes_so_tramp_resolve + + .globl _libXfixes_so_save_regs_and_resolve + .hidden _libXfixes_so_save_regs_and_resolve + .type _libXfixes_so_save_regs_and_resolve, %function +_libXfixes_so_save_regs_and_resolve: + .cfi_startproc + +#define PUSH_REG(reg) pushq %reg ; .cfi_adjust_cfa_offset 8; .cfi_rel_offset reg, 0 +#define POP_REG(reg) popq %reg ; .cfi_adjust_cfa_offset -8; .cfi_restore reg + +#define DEC_STACK(d) subq $d, %rsp; .cfi_adjust_cfa_offset d +#define INC_STACK(d) addq $d, %rsp; .cfi_adjust_cfa_offset -d + +#define PUSH_MMX_REG(reg) DEC_STACK(8); movq %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_MMX_REG(reg) movq (%rsp), %reg; .cfi_restore reg; INC_STACK(8) + +#define PUSH_XMM_REG(reg) DEC_STACK(16); movdqa %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_XMM_REG(reg) movdqa (%rsp), %reg; .cfi_restore reg; INC_STACK(16) + +// TODO: cfi_offset/cfi_restore +#define PUSH_YMM_REG(reg) DEC_STACK(32); vmovdqu %reg, (%rsp) +#define POP_YMM_REG(reg) vmovdqu (%rsp), %reg; INC_STACK(32) + +// TODO: cfi_offset/cfi_restore +#define PUSH_ZMM_REG(reg) DEC_STACK(64); vmovdqu32 %reg, (%rsp) +#define POP_ZMM_REG(reg) vmovdqu32 (%rsp), %reg; INC_STACK(64) + + // Slow path which calls dlsym, taken only on first call. + // All registers are stored to handle arbitrary calling conventions + // (except x87 FPU registers which do not have to be preserved). + // For Dwarf directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + .cfi_def_cfa_offset 8 // Return address + + PUSH_REG(rdi) // 16 + mov 0x10(%rsp), %rdi + PUSH_REG(rbx) + PUSH_REG(rbx) // 16 + PUSH_REG(rcx) + PUSH_REG(rdx) // 16 + PUSH_REG(rbp) + PUSH_REG(rsi) // 16 + PUSH_REG(r8) + PUSH_REG(r9) // 16 + PUSH_REG(r10) + PUSH_REG(r11) // 16 + PUSH_REG(r12) + PUSH_REG(r13) // 16 + PUSH_REG(r14) + PUSH_REG(r15) // 16 + + // Maybe use cpuid instead of macro to detect current vector size... +#ifdef __AVX512F__ + PUSH_ZMM_REG(zmm0) + PUSH_ZMM_REG(zmm1) + PUSH_ZMM_REG(zmm2) + PUSH_ZMM_REG(zmm3) + PUSH_ZMM_REG(zmm4) + PUSH_ZMM_REG(zmm5) + PUSH_ZMM_REG(zmm6) + PUSH_ZMM_REG(zmm7) +#elif defined __AVX__ + PUSH_YMM_REG(ymm0) + PUSH_YMM_REG(ymm1) + PUSH_YMM_REG(ymm2) + PUSH_YMM_REG(ymm3) + PUSH_YMM_REG(ymm4) + PUSH_YMM_REG(ymm5) + PUSH_YMM_REG(ymm6) + PUSH_YMM_REG(ymm7) +#elif defined __SSE__ + PUSH_XMM_REG(xmm0) + PUSH_XMM_REG(xmm1) + PUSH_XMM_REG(xmm2) + PUSH_XMM_REG(xmm3) + PUSH_XMM_REG(xmm4) + PUSH_XMM_REG(xmm5) + PUSH_XMM_REG(xmm6) + PUSH_XMM_REG(xmm7) +#endif + + // MMX registers are not used to pass arguments so we do not save them + + // Stack is just 8-byte aligned but callee will re-align to 16 + call _libXfixes_so_tramp_resolve + +#ifdef __AVX512F__ + POP_ZMM_REG(zmm7) + POP_ZMM_REG(zmm6) + POP_ZMM_REG(zmm5) + POP_ZMM_REG(zmm4) + POP_ZMM_REG(zmm3) + POP_ZMM_REG(zmm2) + POP_ZMM_REG(zmm1) + POP_ZMM_REG(zmm0) // 16 +#elif defined __AVX__ + POP_YMM_REG(ymm7) + POP_YMM_REG(ymm6) + POP_YMM_REG(ymm5) + POP_YMM_REG(ymm4) + POP_YMM_REG(ymm3) + POP_YMM_REG(ymm2) + POP_YMM_REG(ymm1) + POP_YMM_REG(ymm0) // 16 +#elif defined __SSE__ + POP_XMM_REG(xmm7) + POP_XMM_REG(xmm6) + POP_XMM_REG(xmm5) + POP_XMM_REG(xmm4) + POP_XMM_REG(xmm3) + POP_XMM_REG(xmm2) + POP_XMM_REG(xmm1) + POP_XMM_REG(xmm0) // 16 +#endif + + POP_REG(r15) + POP_REG(r14) // 16 + POP_REG(r13) + POP_REG(r12) // 16 + POP_REG(r11) + POP_REG(r10) // 16 + POP_REG(r9) + POP_REG(r8) // 16 + POP_REG(rsi) + POP_REG(rbp) // 16 + POP_REG(rdx) + POP_REG(rcx) // 16 + POP_REG(rbx) + POP_REG(rbx) // 16 + POP_REG(rdi) + + ret + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesChangeCursor + .p2align 4 + .type XFixesChangeCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesChangeCursor +#endif +XFixesChangeCursor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+0(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+0(%rip) +2: + pushq $0 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesChangeCursorByName + .p2align 4 + .type XFixesChangeCursorByName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesChangeCursorByName +#endif +XFixesChangeCursorByName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+8(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+8(%rip) +2: + pushq $1 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesChangeSaveSet + .p2align 4 + .type XFixesChangeSaveSet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesChangeSaveSet +#endif +XFixesChangeSaveSet: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+16(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+16(%rip) +2: + pushq $2 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesCopyRegion + .p2align 4 + .type XFixesCopyRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesCopyRegion +#endif +XFixesCopyRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+24(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+24(%rip) +2: + pushq $3 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesCreatePointerBarrier + .p2align 4 + .type XFixesCreatePointerBarrier, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesCreatePointerBarrier +#endif +XFixesCreatePointerBarrier: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+32(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+32(%rip) +2: + pushq $4 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesCreateRegion + .p2align 4 + .type XFixesCreateRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesCreateRegion +#endif +XFixesCreateRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+40(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+40(%rip) +2: + pushq $5 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesCreateRegionFromBitmap + .p2align 4 + .type XFixesCreateRegionFromBitmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesCreateRegionFromBitmap +#endif +XFixesCreateRegionFromBitmap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+48(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+48(%rip) +2: + pushq $6 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesCreateRegionFromGC + .p2align 4 + .type XFixesCreateRegionFromGC, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesCreateRegionFromGC +#endif +XFixesCreateRegionFromGC: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+56(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+56(%rip) +2: + pushq $7 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesCreateRegionFromPicture + .p2align 4 + .type XFixesCreateRegionFromPicture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesCreateRegionFromPicture +#endif +XFixesCreateRegionFromPicture: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+64(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+64(%rip) +2: + pushq $8 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesCreateRegionFromWindow + .p2align 4 + .type XFixesCreateRegionFromWindow, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesCreateRegionFromWindow +#endif +XFixesCreateRegionFromWindow: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+72(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+72(%rip) +2: + pushq $9 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesDestroyPointerBarrier + .p2align 4 + .type XFixesDestroyPointerBarrier, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesDestroyPointerBarrier +#endif +XFixesDestroyPointerBarrier: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+80(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+80(%rip) +2: + pushq $10 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesDestroyRegion + .p2align 4 + .type XFixesDestroyRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesDestroyRegion +#endif +XFixesDestroyRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+88(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+88(%rip) +2: + pushq $11 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesExpandRegion + .p2align 4 + .type XFixesExpandRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesExpandRegion +#endif +XFixesExpandRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+96(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+96(%rip) +2: + pushq $12 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesFetchRegion + .p2align 4 + .type XFixesFetchRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesFetchRegion +#endif +XFixesFetchRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+104(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+104(%rip) +2: + pushq $13 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesFetchRegionAndBounds + .p2align 4 + .type XFixesFetchRegionAndBounds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesFetchRegionAndBounds +#endif +XFixesFetchRegionAndBounds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+112(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+112(%rip) +2: + pushq $14 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesFindDisplay + .p2align 4 + .type XFixesFindDisplay, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesFindDisplay +#endif +XFixesFindDisplay: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+120(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+120(%rip) +2: + pushq $15 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesGetClientDisconnectMode + .p2align 4 + .type XFixesGetClientDisconnectMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesGetClientDisconnectMode +#endif +XFixesGetClientDisconnectMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+128(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+128(%rip) +2: + pushq $16 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesGetCursorImage + .p2align 4 + .type XFixesGetCursorImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesGetCursorImage +#endif +XFixesGetCursorImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+136(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+136(%rip) +2: + pushq $17 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesGetCursorName + .p2align 4 + .type XFixesGetCursorName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesGetCursorName +#endif +XFixesGetCursorName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+144(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+144(%rip) +2: + pushq $18 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesHideCursor + .p2align 4 + .type XFixesHideCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesHideCursor +#endif +XFixesHideCursor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+152(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+152(%rip) +2: + pushq $19 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesIntersectRegion + .p2align 4 + .type XFixesIntersectRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesIntersectRegion +#endif +XFixesIntersectRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+160(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+160(%rip) +2: + pushq $20 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesInvertRegion + .p2align 4 + .type XFixesInvertRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesInvertRegion +#endif +XFixesInvertRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+168(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+168(%rip) +2: + pushq $21 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesQueryExtension + .p2align 4 + .type XFixesQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesQueryExtension +#endif +XFixesQueryExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+176(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+176(%rip) +2: + pushq $22 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesQueryVersion + .p2align 4 + .type XFixesQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesQueryVersion +#endif +XFixesQueryVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+184(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+184(%rip) +2: + pushq $23 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesRegionExtents + .p2align 4 + .type XFixesRegionExtents, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesRegionExtents +#endif +XFixesRegionExtents: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+192(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+192(%rip) +2: + pushq $24 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesSelectCursorInput + .p2align 4 + .type XFixesSelectCursorInput, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesSelectCursorInput +#endif +XFixesSelectCursorInput: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+200(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+200(%rip) +2: + pushq $25 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesSelectSelectionInput + .p2align 4 + .type XFixesSelectSelectionInput, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesSelectSelectionInput +#endif +XFixesSelectSelectionInput: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+208(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+208(%rip) +2: + pushq $26 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesSetClientDisconnectMode + .p2align 4 + .type XFixesSetClientDisconnectMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesSetClientDisconnectMode +#endif +XFixesSetClientDisconnectMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+216(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+216(%rip) +2: + pushq $27 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesSetCursorName + .p2align 4 + .type XFixesSetCursorName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesSetCursorName +#endif +XFixesSetCursorName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+224(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+224(%rip) +2: + pushq $28 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesSetGCClipRegion + .p2align 4 + .type XFixesSetGCClipRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesSetGCClipRegion +#endif +XFixesSetGCClipRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+232(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+232(%rip) +2: + pushq $29 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesSetPictureClipRegion + .p2align 4 + .type XFixesSetPictureClipRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesSetPictureClipRegion +#endif +XFixesSetPictureClipRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+240(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+240(%rip) +2: + pushq $30 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesSetRegion + .p2align 4 + .type XFixesSetRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesSetRegion +#endif +XFixesSetRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+248(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+248(%rip) +2: + pushq $31 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesSetWindowShapeRegion + .p2align 4 + .type XFixesSetWindowShapeRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesSetWindowShapeRegion +#endif +XFixesSetWindowShapeRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+256(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+256(%rip) +2: + pushq $32 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesShowCursor + .p2align 4 + .type XFixesShowCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesShowCursor +#endif +XFixesShowCursor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+264(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+264(%rip) +2: + pushq $33 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesSubtractRegion + .p2align 4 + .type XFixesSubtractRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesSubtractRegion +#endif +XFixesSubtractRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+272(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+272(%rip) +2: + pushq $34 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesTranslateRegion + .p2align 4 + .type XFixesTranslateRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesTranslateRegion +#endif +XFixesTranslateRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+280(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+280(%rip) +2: + pushq $35 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesUnionRegion + .p2align 4 + .type XFixesUnionRegion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesUnionRegion +#endif +XFixesUnionRegion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+288(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+288(%rip) +2: + pushq $36 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XFixesVersion + .p2align 4 + .type XFixesVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XFixesVersion +#endif +XFixesVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXfixes_so_tramp_table+296(%rip) + je 2f +1: + jmp *_libXfixes_so_tramp_table+296(%rip) +2: + pushq $37 + .cfi_adjust_cfa_offset 8 + call _libXfixes_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXrandr.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXrandr.so.init.c new file mode 100644 index 0000000..200111d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXrandr.so.init.c @@ -0,0 +1,314 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libXrandr.so.2: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libXrandr.so.2"); + CHECK(lib_handle, "failed to load library 'libXrandr.so.2' via callback ''"); +#else + lib_handle = dlopen("libXrandr.so.2", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libXrandr.so.2' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "XRRAddOutputMode", + "XRRAllocGamma", + "XRRAllocModeInfo", + "XRRAllocateMonitor", + "XRRChangeOutputProperty", + "XRRChangeProviderProperty", + "XRRConfigCurrentConfiguration", + "XRRConfigCurrentRate", + "XRRConfigRates", + "XRRConfigRotations", + "XRRConfigSizes", + "XRRConfigTimes", + "XRRConfigureOutputProperty", + "XRRConfigureProviderProperty", + "XRRCreateMode", + "XRRDeleteMonitor", + "XRRDeleteOutputMode", + "XRRDeleteOutputProperty", + "XRRDeleteProviderProperty", + "XRRDestroyMode", + "XRRFreeCrtcInfo", + "XRRFreeGamma", + "XRRFreeModeInfo", + "XRRFreeMonitors", + "XRRFreeOutputInfo", + "XRRFreePanning", + "XRRFreeProviderInfo", + "XRRFreeProviderResources", + "XRRFreeScreenConfigInfo", + "XRRFreeScreenResources", + "XRRGetCrtcGamma", + "XRRGetCrtcGammaSize", + "XRRGetCrtcInfo", + "XRRGetCrtcTransform", + "XRRGetMonitors", + "XRRGetOutputInfo", + "XRRGetOutputPrimary", + "XRRGetOutputProperty", + "XRRGetPanning", + "XRRGetProviderInfo", + "XRRGetProviderProperty", + "XRRGetProviderResources", + "XRRGetScreenInfo", + "XRRGetScreenResources", + "XRRGetScreenResourcesCurrent", + "XRRGetScreenSizeRange", + "XRRListOutputProperties", + "XRRListProviderProperties", + "XRRQueryExtension", + "XRRQueryOutputProperty", + "XRRQueryProviderProperty", + "XRRQueryVersion", + "XRRRates", + "XRRRootToScreen", + "XRRRotations", + "XRRSelectInput", + "XRRSetCrtcConfig", + "XRRSetCrtcGamma", + "XRRSetCrtcTransform", + "XRRSetMonitor", + "XRRSetOutputPrimary", + "XRRSetPanning", + "XRRSetProviderOffloadSink", + "XRRSetProviderOutputSource", + "XRRSetScreenConfig", + "XRRSetScreenConfigAndRate", + "XRRSetScreenSize", + "XRRSizes", + "XRRTimes", + "XRRUpdateConfiguration", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libXrandr_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libXrandr_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libXrandr_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libXrandr_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libXrandr_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libXrandr_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libXrandr_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libXrandr_so_tramp_table, 0, SYM_COUNT * sizeof(_libXrandr_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXrandr.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXrandr.so.tramp.S new file mode 100644 index 0000000..2c348da --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libXrandr.so.tramp.S @@ -0,0 +1,2538 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libXrandr_so_tramp_table + .hidden _libXrandr_so_tramp_table + .align 8 +_libXrandr_so_tramp_table: + .zero 568 + + .text + + .globl _libXrandr_so_tramp_resolve + .hidden _libXrandr_so_tramp_resolve + + .globl _libXrandr_so_save_regs_and_resolve + .hidden _libXrandr_so_save_regs_and_resolve + .type _libXrandr_so_save_regs_and_resolve, %function +_libXrandr_so_save_regs_and_resolve: + .cfi_startproc + +#define PUSH_REG(reg) pushq %reg ; .cfi_adjust_cfa_offset 8; .cfi_rel_offset reg, 0 +#define POP_REG(reg) popq %reg ; .cfi_adjust_cfa_offset -8; .cfi_restore reg + +#define DEC_STACK(d) subq $d, %rsp; .cfi_adjust_cfa_offset d +#define INC_STACK(d) addq $d, %rsp; .cfi_adjust_cfa_offset -d + +#define PUSH_MMX_REG(reg) DEC_STACK(8); movq %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_MMX_REG(reg) movq (%rsp), %reg; .cfi_restore reg; INC_STACK(8) + +#define PUSH_XMM_REG(reg) DEC_STACK(16); movdqa %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_XMM_REG(reg) movdqa (%rsp), %reg; .cfi_restore reg; INC_STACK(16) + +// TODO: cfi_offset/cfi_restore +#define PUSH_YMM_REG(reg) DEC_STACK(32); vmovdqu %reg, (%rsp) +#define POP_YMM_REG(reg) vmovdqu (%rsp), %reg; INC_STACK(32) + +// TODO: cfi_offset/cfi_restore +#define PUSH_ZMM_REG(reg) DEC_STACK(64); vmovdqu32 %reg, (%rsp) +#define POP_ZMM_REG(reg) vmovdqu32 (%rsp), %reg; INC_STACK(64) + + // Slow path which calls dlsym, taken only on first call. + // All registers are stored to handle arbitrary calling conventions + // (except x87 FPU registers which do not have to be preserved). + // For Dwarf directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + .cfi_def_cfa_offset 8 // Return address + + PUSH_REG(rdi) // 16 + mov 0x10(%rsp), %rdi + PUSH_REG(rbx) + PUSH_REG(rbx) // 16 + PUSH_REG(rcx) + PUSH_REG(rdx) // 16 + PUSH_REG(rbp) + PUSH_REG(rsi) // 16 + PUSH_REG(r8) + PUSH_REG(r9) // 16 + PUSH_REG(r10) + PUSH_REG(r11) // 16 + PUSH_REG(r12) + PUSH_REG(r13) // 16 + PUSH_REG(r14) + PUSH_REG(r15) // 16 + + // Maybe use cpuid instead of macro to detect current vector size... +#ifdef __AVX512F__ + PUSH_ZMM_REG(zmm0) + PUSH_ZMM_REG(zmm1) + PUSH_ZMM_REG(zmm2) + PUSH_ZMM_REG(zmm3) + PUSH_ZMM_REG(zmm4) + PUSH_ZMM_REG(zmm5) + PUSH_ZMM_REG(zmm6) + PUSH_ZMM_REG(zmm7) +#elif defined __AVX__ + PUSH_YMM_REG(ymm0) + PUSH_YMM_REG(ymm1) + PUSH_YMM_REG(ymm2) + PUSH_YMM_REG(ymm3) + PUSH_YMM_REG(ymm4) + PUSH_YMM_REG(ymm5) + PUSH_YMM_REG(ymm6) + PUSH_YMM_REG(ymm7) +#elif defined __SSE__ + PUSH_XMM_REG(xmm0) + PUSH_XMM_REG(xmm1) + PUSH_XMM_REG(xmm2) + PUSH_XMM_REG(xmm3) + PUSH_XMM_REG(xmm4) + PUSH_XMM_REG(xmm5) + PUSH_XMM_REG(xmm6) + PUSH_XMM_REG(xmm7) +#endif + + // MMX registers are not used to pass arguments so we do not save them + + // Stack is just 8-byte aligned but callee will re-align to 16 + call _libXrandr_so_tramp_resolve + +#ifdef __AVX512F__ + POP_ZMM_REG(zmm7) + POP_ZMM_REG(zmm6) + POP_ZMM_REG(zmm5) + POP_ZMM_REG(zmm4) + POP_ZMM_REG(zmm3) + POP_ZMM_REG(zmm2) + POP_ZMM_REG(zmm1) + POP_ZMM_REG(zmm0) // 16 +#elif defined __AVX__ + POP_YMM_REG(ymm7) + POP_YMM_REG(ymm6) + POP_YMM_REG(ymm5) + POP_YMM_REG(ymm4) + POP_YMM_REG(ymm3) + POP_YMM_REG(ymm2) + POP_YMM_REG(ymm1) + POP_YMM_REG(ymm0) // 16 +#elif defined __SSE__ + POP_XMM_REG(xmm7) + POP_XMM_REG(xmm6) + POP_XMM_REG(xmm5) + POP_XMM_REG(xmm4) + POP_XMM_REG(xmm3) + POP_XMM_REG(xmm2) + POP_XMM_REG(xmm1) + POP_XMM_REG(xmm0) // 16 +#endif + + POP_REG(r15) + POP_REG(r14) // 16 + POP_REG(r13) + POP_REG(r12) // 16 + POP_REG(r11) + POP_REG(r10) // 16 + POP_REG(r9) + POP_REG(r8) // 16 + POP_REG(rsi) + POP_REG(rbp) // 16 + POP_REG(rdx) + POP_REG(rcx) // 16 + POP_REG(rbx) + POP_REG(rbx) // 16 + POP_REG(rdi) + + ret + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRAddOutputMode + .p2align 4 + .type XRRAddOutputMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRAddOutputMode +#endif +XRRAddOutputMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+0(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+0(%rip) +2: + pushq $0 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRAllocGamma + .p2align 4 + .type XRRAllocGamma, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRAllocGamma +#endif +XRRAllocGamma: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+8(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+8(%rip) +2: + pushq $1 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRAllocModeInfo + .p2align 4 + .type XRRAllocModeInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRAllocModeInfo +#endif +XRRAllocModeInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+16(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+16(%rip) +2: + pushq $2 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRAllocateMonitor + .p2align 4 + .type XRRAllocateMonitor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRAllocateMonitor +#endif +XRRAllocateMonitor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+24(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+24(%rip) +2: + pushq $3 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRChangeOutputProperty + .p2align 4 + .type XRRChangeOutputProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRChangeOutputProperty +#endif +XRRChangeOutputProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+32(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+32(%rip) +2: + pushq $4 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRChangeProviderProperty + .p2align 4 + .type XRRChangeProviderProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRChangeProviderProperty +#endif +XRRChangeProviderProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+40(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+40(%rip) +2: + pushq $5 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRConfigCurrentConfiguration + .p2align 4 + .type XRRConfigCurrentConfiguration, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRConfigCurrentConfiguration +#endif +XRRConfigCurrentConfiguration: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+48(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+48(%rip) +2: + pushq $6 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRConfigCurrentRate + .p2align 4 + .type XRRConfigCurrentRate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRConfigCurrentRate +#endif +XRRConfigCurrentRate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+56(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+56(%rip) +2: + pushq $7 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRConfigRates + .p2align 4 + .type XRRConfigRates, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRConfigRates +#endif +XRRConfigRates: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+64(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+64(%rip) +2: + pushq $8 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRConfigRotations + .p2align 4 + .type XRRConfigRotations, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRConfigRotations +#endif +XRRConfigRotations: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+72(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+72(%rip) +2: + pushq $9 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRConfigSizes + .p2align 4 + .type XRRConfigSizes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRConfigSizes +#endif +XRRConfigSizes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+80(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+80(%rip) +2: + pushq $10 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRConfigTimes + .p2align 4 + .type XRRConfigTimes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRConfigTimes +#endif +XRRConfigTimes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+88(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+88(%rip) +2: + pushq $11 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRConfigureOutputProperty + .p2align 4 + .type XRRConfigureOutputProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRConfigureOutputProperty +#endif +XRRConfigureOutputProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+96(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+96(%rip) +2: + pushq $12 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRConfigureProviderProperty + .p2align 4 + .type XRRConfigureProviderProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRConfigureProviderProperty +#endif +XRRConfigureProviderProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+104(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+104(%rip) +2: + pushq $13 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRCreateMode + .p2align 4 + .type XRRCreateMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRCreateMode +#endif +XRRCreateMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+112(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+112(%rip) +2: + pushq $14 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRDeleteMonitor + .p2align 4 + .type XRRDeleteMonitor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRDeleteMonitor +#endif +XRRDeleteMonitor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+120(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+120(%rip) +2: + pushq $15 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRDeleteOutputMode + .p2align 4 + .type XRRDeleteOutputMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRDeleteOutputMode +#endif +XRRDeleteOutputMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+128(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+128(%rip) +2: + pushq $16 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRDeleteOutputProperty + .p2align 4 + .type XRRDeleteOutputProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRDeleteOutputProperty +#endif +XRRDeleteOutputProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+136(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+136(%rip) +2: + pushq $17 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRDeleteProviderProperty + .p2align 4 + .type XRRDeleteProviderProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRDeleteProviderProperty +#endif +XRRDeleteProviderProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+144(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+144(%rip) +2: + pushq $18 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRDestroyMode + .p2align 4 + .type XRRDestroyMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRDestroyMode +#endif +XRRDestroyMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+152(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+152(%rip) +2: + pushq $19 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreeCrtcInfo + .p2align 4 + .type XRRFreeCrtcInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreeCrtcInfo +#endif +XRRFreeCrtcInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+160(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+160(%rip) +2: + pushq $20 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreeGamma + .p2align 4 + .type XRRFreeGamma, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreeGamma +#endif +XRRFreeGamma: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+168(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+168(%rip) +2: + pushq $21 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreeModeInfo + .p2align 4 + .type XRRFreeModeInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreeModeInfo +#endif +XRRFreeModeInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+176(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+176(%rip) +2: + pushq $22 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreeMonitors + .p2align 4 + .type XRRFreeMonitors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreeMonitors +#endif +XRRFreeMonitors: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+184(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+184(%rip) +2: + pushq $23 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreeOutputInfo + .p2align 4 + .type XRRFreeOutputInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreeOutputInfo +#endif +XRRFreeOutputInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+192(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+192(%rip) +2: + pushq $24 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreePanning + .p2align 4 + .type XRRFreePanning, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreePanning +#endif +XRRFreePanning: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+200(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+200(%rip) +2: + pushq $25 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreeProviderInfo + .p2align 4 + .type XRRFreeProviderInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreeProviderInfo +#endif +XRRFreeProviderInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+208(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+208(%rip) +2: + pushq $26 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreeProviderResources + .p2align 4 + .type XRRFreeProviderResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreeProviderResources +#endif +XRRFreeProviderResources: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+216(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+216(%rip) +2: + pushq $27 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreeScreenConfigInfo + .p2align 4 + .type XRRFreeScreenConfigInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreeScreenConfigInfo +#endif +XRRFreeScreenConfigInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+224(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+224(%rip) +2: + pushq $28 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRFreeScreenResources + .p2align 4 + .type XRRFreeScreenResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRFreeScreenResources +#endif +XRRFreeScreenResources: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+232(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+232(%rip) +2: + pushq $29 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetCrtcGamma + .p2align 4 + .type XRRGetCrtcGamma, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetCrtcGamma +#endif +XRRGetCrtcGamma: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+240(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+240(%rip) +2: + pushq $30 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetCrtcGammaSize + .p2align 4 + .type XRRGetCrtcGammaSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetCrtcGammaSize +#endif +XRRGetCrtcGammaSize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+248(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+248(%rip) +2: + pushq $31 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetCrtcInfo + .p2align 4 + .type XRRGetCrtcInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetCrtcInfo +#endif +XRRGetCrtcInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+256(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+256(%rip) +2: + pushq $32 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetCrtcTransform + .p2align 4 + .type XRRGetCrtcTransform, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetCrtcTransform +#endif +XRRGetCrtcTransform: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+264(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+264(%rip) +2: + pushq $33 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetMonitors + .p2align 4 + .type XRRGetMonitors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetMonitors +#endif +XRRGetMonitors: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+272(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+272(%rip) +2: + pushq $34 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetOutputInfo + .p2align 4 + .type XRRGetOutputInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetOutputInfo +#endif +XRRGetOutputInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+280(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+280(%rip) +2: + pushq $35 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetOutputPrimary + .p2align 4 + .type XRRGetOutputPrimary, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetOutputPrimary +#endif +XRRGetOutputPrimary: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+288(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+288(%rip) +2: + pushq $36 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetOutputProperty + .p2align 4 + .type XRRGetOutputProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetOutputProperty +#endif +XRRGetOutputProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+296(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+296(%rip) +2: + pushq $37 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetPanning + .p2align 4 + .type XRRGetPanning, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetPanning +#endif +XRRGetPanning: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+304(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+304(%rip) +2: + pushq $38 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetProviderInfo + .p2align 4 + .type XRRGetProviderInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetProviderInfo +#endif +XRRGetProviderInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+312(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+312(%rip) +2: + pushq $39 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetProviderProperty + .p2align 4 + .type XRRGetProviderProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetProviderProperty +#endif +XRRGetProviderProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+320(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+320(%rip) +2: + pushq $40 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetProviderResources + .p2align 4 + .type XRRGetProviderResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetProviderResources +#endif +XRRGetProviderResources: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+328(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+328(%rip) +2: + pushq $41 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetScreenInfo + .p2align 4 + .type XRRGetScreenInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetScreenInfo +#endif +XRRGetScreenInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+336(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+336(%rip) +2: + pushq $42 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetScreenResources + .p2align 4 + .type XRRGetScreenResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetScreenResources +#endif +XRRGetScreenResources: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+344(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+344(%rip) +2: + pushq $43 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetScreenResourcesCurrent + .p2align 4 + .type XRRGetScreenResourcesCurrent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetScreenResourcesCurrent +#endif +XRRGetScreenResourcesCurrent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+352(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+352(%rip) +2: + pushq $44 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRGetScreenSizeRange + .p2align 4 + .type XRRGetScreenSizeRange, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRGetScreenSizeRange +#endif +XRRGetScreenSizeRange: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+360(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+360(%rip) +2: + pushq $45 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRListOutputProperties + .p2align 4 + .type XRRListOutputProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRListOutputProperties +#endif +XRRListOutputProperties: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+368(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+368(%rip) +2: + pushq $46 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRListProviderProperties + .p2align 4 + .type XRRListProviderProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRListProviderProperties +#endif +XRRListProviderProperties: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+376(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+376(%rip) +2: + pushq $47 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRQueryExtension + .p2align 4 + .type XRRQueryExtension, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRQueryExtension +#endif +XRRQueryExtension: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+384(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+384(%rip) +2: + pushq $48 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRQueryOutputProperty + .p2align 4 + .type XRRQueryOutputProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRQueryOutputProperty +#endif +XRRQueryOutputProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+392(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+392(%rip) +2: + pushq $49 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRQueryProviderProperty + .p2align 4 + .type XRRQueryProviderProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRQueryProviderProperty +#endif +XRRQueryProviderProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+400(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+400(%rip) +2: + pushq $50 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRQueryVersion + .p2align 4 + .type XRRQueryVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRQueryVersion +#endif +XRRQueryVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+408(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+408(%rip) +2: + pushq $51 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRRates + .p2align 4 + .type XRRRates, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRRates +#endif +XRRRates: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+416(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+416(%rip) +2: + pushq $52 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRRootToScreen + .p2align 4 + .type XRRRootToScreen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRRootToScreen +#endif +XRRRootToScreen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+424(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+424(%rip) +2: + pushq $53 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRRotations + .p2align 4 + .type XRRRotations, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRRotations +#endif +XRRRotations: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+432(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+432(%rip) +2: + pushq $54 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSelectInput + .p2align 4 + .type XRRSelectInput, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSelectInput +#endif +XRRSelectInput: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+440(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+440(%rip) +2: + pushq $55 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetCrtcConfig + .p2align 4 + .type XRRSetCrtcConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetCrtcConfig +#endif +XRRSetCrtcConfig: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+448(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+448(%rip) +2: + pushq $56 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetCrtcGamma + .p2align 4 + .type XRRSetCrtcGamma, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetCrtcGamma +#endif +XRRSetCrtcGamma: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+456(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+456(%rip) +2: + pushq $57 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetCrtcTransform + .p2align 4 + .type XRRSetCrtcTransform, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetCrtcTransform +#endif +XRRSetCrtcTransform: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+464(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+464(%rip) +2: + pushq $58 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetMonitor + .p2align 4 + .type XRRSetMonitor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetMonitor +#endif +XRRSetMonitor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+472(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+472(%rip) +2: + pushq $59 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetOutputPrimary + .p2align 4 + .type XRRSetOutputPrimary, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetOutputPrimary +#endif +XRRSetOutputPrimary: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+480(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+480(%rip) +2: + pushq $60 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetPanning + .p2align 4 + .type XRRSetPanning, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetPanning +#endif +XRRSetPanning: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+488(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+488(%rip) +2: + pushq $61 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetProviderOffloadSink + .p2align 4 + .type XRRSetProviderOffloadSink, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetProviderOffloadSink +#endif +XRRSetProviderOffloadSink: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+496(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+496(%rip) +2: + pushq $62 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetProviderOutputSource + .p2align 4 + .type XRRSetProviderOutputSource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetProviderOutputSource +#endif +XRRSetProviderOutputSource: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+504(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+504(%rip) +2: + pushq $63 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetScreenConfig + .p2align 4 + .type XRRSetScreenConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetScreenConfig +#endif +XRRSetScreenConfig: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+512(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+512(%rip) +2: + pushq $64 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetScreenConfigAndRate + .p2align 4 + .type XRRSetScreenConfigAndRate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetScreenConfigAndRate +#endif +XRRSetScreenConfigAndRate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+520(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+520(%rip) +2: + pushq $65 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSetScreenSize + .p2align 4 + .type XRRSetScreenSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSetScreenSize +#endif +XRRSetScreenSize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+528(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+528(%rip) +2: + pushq $66 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRSizes + .p2align 4 + .type XRRSizes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRSizes +#endif +XRRSizes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+536(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+536(%rip) +2: + pushq $67 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRTimes + .p2align 4 + .type XRRTimes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRTimes +#endif +XRRTimes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+544(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+544(%rip) +2: + pushq $68 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl XRRUpdateConfiguration + .p2align 4 + .type XRRUpdateConfiguration, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden XRRUpdateConfiguration +#endif +XRRUpdateConfiguration: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libXrandr_so_tramp_table+552(%rip) + je 2f +1: + jmp *_libXrandr_so_tramp_table+552(%rip) +2: + pushq $69 + .cfi_adjust_cfa_offset 8 + call _libXrandr_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libdrm.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libdrm.so.init.c new file mode 100644 index 0000000..244159b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libdrm.so.init.c @@ -0,0 +1,456 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libdrm.so.2: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libdrm.so.2"); + CHECK(lib_handle, "failed to load library 'libdrm.so.2' via callback ''"); +#else + lib_handle = dlopen("libdrm.so.2", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libdrm.so.2' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "drmAddBufs", + "drmAddContextPrivateMapping", + "drmAddContextTag", + "drmAddMap", + "drmAgpAcquire", + "drmAgpAlloc", + "drmAgpBase", + "drmAgpBind", + "drmAgpDeviceId", + "drmAgpEnable", + "drmAgpFree", + "drmAgpGetMode", + "drmAgpMemoryAvail", + "drmAgpMemoryUsed", + "drmAgpRelease", + "drmAgpSize", + "drmAgpUnbind", + "drmAgpVendorId", + "drmAgpVersionMajor", + "drmAgpVersionMinor", + "drmAuthMagic", + "drmAvailable", + "drmCheckModesettingSupported", + "drmClose", + "drmCloseBufferHandle", + "drmCloseOnce", + "drmCommandNone", + "drmCommandRead", + "drmCommandWrite", + "drmCommandWriteRead", + "drmCreateContext", + "drmCreateDrawable", + "drmCrtcGetSequence", + "drmCrtcQueueSequence", + "drmCtlInstHandler", + "drmCtlUninstHandler", + "drmDMA", + "drmDelContextTag", + "drmDestroyContext", + "drmDestroyDrawable", + "drmDevicesEqual", + "drmDropMaster", + "drmError", + "drmFinish", + "drmFree", + "drmFreeBufs", + "drmFreeBusid", + "drmFreeDevice", + "drmFreeDevices", + "drmFreeReservedContextList", + "drmFreeVersion", + "drmGetBufInfo", + "drmGetBusid", + "drmGetCap", + "drmGetClient", + "drmGetContextFlags", + "drmGetContextPrivateMapping", + "drmGetContextTag", + "drmGetDevice", + "drmGetDevice2", + "drmGetDeviceFromDevId", + "drmGetDeviceNameFromFd", + "drmGetDeviceNameFromFd2", + "drmGetDevices", + "drmGetDevices2", + "drmGetEntry", + "drmGetFormatModifierName", + "drmGetFormatModifierVendor", + "drmGetFormatName", + "drmGetHashTable", + "drmGetInterruptFromBusID", + "drmGetLibVersion", + "drmGetLock", + "drmGetMagic", + "drmGetMap", + "drmGetNodeTypeFromDevId", + "drmGetNodeTypeFromFd", + "drmGetPrimaryDeviceNameFromFd", + "drmGetRenderDeviceNameFromFd", + "drmGetReservedContextList", + "drmGetStats", + "drmGetVersion", + "drmHandleEvent", + "drmHashCreate", + "drmHashDelete", + "drmHashDestroy", + "drmHashFirst", + "drmHashInsert", + "drmHashLookup", + "drmHashNext", + "drmIoctl", + "drmIsKMS", + "drmIsMaster", + "drmMalloc", + "drmMap", + "drmMapBufs", + "drmMarkBufs", + "drmModeAddFB", + "drmModeAddFB2", + "drmModeAddFB2WithModifiers", + "drmModeAtomicAddProperty", + "drmModeAtomicAlloc", + "drmModeAtomicCommit", + "drmModeAtomicDuplicate", + "drmModeAtomicFree", + "drmModeAtomicGetCursor", + "drmModeAtomicMerge", + "drmModeAtomicSetCursor", + "drmModeAttachMode", + "drmModeCloseFB", + "drmModeConnectorGetPossibleCrtcs", + "drmModeConnectorSetProperty", + "drmModeCreateDumbBuffer", + "drmModeCreateLease", + "drmModeCreatePropertyBlob", + "drmModeCrtcGetGamma", + "drmModeCrtcSetGamma", + "drmModeDestroyDumbBuffer", + "drmModeDestroyPropertyBlob", + "drmModeDetachMode", + "drmModeDirtyFB", + "drmModeFormatModifierBlobIterNext", + "drmModeFreeConnector", + "drmModeFreeCrtc", + "drmModeFreeEncoder", + "drmModeFreeFB", + "drmModeFreeFB2", + "drmModeFreeModeInfo", + "drmModeFreeObjectProperties", + "drmModeFreePlane", + "drmModeFreePlaneResources", + "drmModeFreeProperty", + "drmModeFreePropertyBlob", + "drmModeFreeResources", + "drmModeGetConnector", + "drmModeGetConnectorCurrent", + "drmModeGetConnectorTypeName", + "drmModeGetCrtc", + "drmModeGetEncoder", + "drmModeGetFB", + "drmModeGetFB2", + "drmModeGetLease", + "drmModeGetPlane", + "drmModeGetPlaneResources", + "drmModeGetProperty", + "drmModeGetPropertyBlob", + "drmModeGetResources", + "drmModeListLessees", + "drmModeMapDumbBuffer", + "drmModeMoveCursor", + "drmModeObjectGetProperties", + "drmModeObjectSetProperty", + "drmModePageFlip", + "drmModePageFlipTarget", + "drmModeRevokeLease", + "drmModeRmFB", + "drmModeSetCrtc", + "drmModeSetCursor", + "drmModeSetCursor2", + "drmModeSetPlane", + "drmMsg", + "drmOpen", + "drmOpenControl", + "drmOpenOnce", + "drmOpenOnceWithType", + "drmOpenRender", + "drmOpenWithType", + "drmPrimeFDToHandle", + "drmPrimeHandleToFD", + "drmRandom", + "drmRandomCreate", + "drmRandomDestroy", + "drmRandomDouble", + "drmRmMap", + "drmSLCreate", + "drmSLDelete", + "drmSLDestroy", + "drmSLDump", + "drmSLFirst", + "drmSLInsert", + "drmSLLookup", + "drmSLLookupNeighbors", + "drmSLNext", + "drmScatterGatherAlloc", + "drmScatterGatherFree", + "drmSetBusid", + "drmSetClientCap", + "drmSetContextFlags", + "drmSetInterfaceVersion", + "drmSetMaster", + "drmSetServerInfo", + "drmSwitchToContext", + "drmSyncobjCreate", + "drmSyncobjDestroy", + "drmSyncobjEventfd", + "drmSyncobjExportSyncFile", + "drmSyncobjFDToHandle", + "drmSyncobjHandleToFD", + "drmSyncobjImportSyncFile", + "drmSyncobjQuery", + "drmSyncobjQuery2", + "drmSyncobjReset", + "drmSyncobjSignal", + "drmSyncobjTimelineSignal", + "drmSyncobjTimelineWait", + "drmSyncobjTransfer", + "drmSyncobjWait", + "drmUnlock", + "drmUnmap", + "drmUnmapBufs", + "drmUpdateDrawableInfo", + "drmWaitVBlank", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libdrm_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libdrm_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libdrm_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libdrm_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libdrm_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libdrm_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libdrm_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libdrm_so_tramp_table, 0, SYM_COUNT * sizeof(_libdrm_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libdrm.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libdrm.so.tramp.S new file mode 100644 index 0000000..29fede6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libdrm.so.tramp.S @@ -0,0 +1,7366 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libdrm_so_tramp_table + .hidden _libdrm_so_tramp_table + .align 8 +_libdrm_so_tramp_table: + .zero 1704 + + .text + + .globl _libdrm_so_tramp_resolve + .hidden _libdrm_so_tramp_resolve + + .globl _libdrm_so_save_regs_and_resolve + .hidden _libdrm_so_save_regs_and_resolve + .type _libdrm_so_save_regs_and_resolve, %function +_libdrm_so_save_regs_and_resolve: + .cfi_startproc + +#define PUSH_REG(reg) pushq %reg ; .cfi_adjust_cfa_offset 8; .cfi_rel_offset reg, 0 +#define POP_REG(reg) popq %reg ; .cfi_adjust_cfa_offset -8; .cfi_restore reg + +#define DEC_STACK(d) subq $d, %rsp; .cfi_adjust_cfa_offset d +#define INC_STACK(d) addq $d, %rsp; .cfi_adjust_cfa_offset -d + +#define PUSH_MMX_REG(reg) DEC_STACK(8); movq %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_MMX_REG(reg) movq (%rsp), %reg; .cfi_restore reg; INC_STACK(8) + +#define PUSH_XMM_REG(reg) DEC_STACK(16); movdqa %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_XMM_REG(reg) movdqa (%rsp), %reg; .cfi_restore reg; INC_STACK(16) + +// TODO: cfi_offset/cfi_restore +#define PUSH_YMM_REG(reg) DEC_STACK(32); vmovdqu %reg, (%rsp) +#define POP_YMM_REG(reg) vmovdqu (%rsp), %reg; INC_STACK(32) + +// TODO: cfi_offset/cfi_restore +#define PUSH_ZMM_REG(reg) DEC_STACK(64); vmovdqu32 %reg, (%rsp) +#define POP_ZMM_REG(reg) vmovdqu32 (%rsp), %reg; INC_STACK(64) + + // Slow path which calls dlsym, taken only on first call. + // All registers are stored to handle arbitrary calling conventions + // (except x87 FPU registers which do not have to be preserved). + // For Dwarf directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + .cfi_def_cfa_offset 8 // Return address + + PUSH_REG(rdi) // 16 + mov 0x10(%rsp), %rdi + PUSH_REG(rbx) + PUSH_REG(rbx) // 16 + PUSH_REG(rcx) + PUSH_REG(rdx) // 16 + PUSH_REG(rbp) + PUSH_REG(rsi) // 16 + PUSH_REG(r8) + PUSH_REG(r9) // 16 + PUSH_REG(r10) + PUSH_REG(r11) // 16 + PUSH_REG(r12) + PUSH_REG(r13) // 16 + PUSH_REG(r14) + PUSH_REG(r15) // 16 + + // Maybe use cpuid instead of macro to detect current vector size... +#ifdef __AVX512F__ + PUSH_ZMM_REG(zmm0) + PUSH_ZMM_REG(zmm1) + PUSH_ZMM_REG(zmm2) + PUSH_ZMM_REG(zmm3) + PUSH_ZMM_REG(zmm4) + PUSH_ZMM_REG(zmm5) + PUSH_ZMM_REG(zmm6) + PUSH_ZMM_REG(zmm7) +#elif defined __AVX__ + PUSH_YMM_REG(ymm0) + PUSH_YMM_REG(ymm1) + PUSH_YMM_REG(ymm2) + PUSH_YMM_REG(ymm3) + PUSH_YMM_REG(ymm4) + PUSH_YMM_REG(ymm5) + PUSH_YMM_REG(ymm6) + PUSH_YMM_REG(ymm7) +#elif defined __SSE__ + PUSH_XMM_REG(xmm0) + PUSH_XMM_REG(xmm1) + PUSH_XMM_REG(xmm2) + PUSH_XMM_REG(xmm3) + PUSH_XMM_REG(xmm4) + PUSH_XMM_REG(xmm5) + PUSH_XMM_REG(xmm6) + PUSH_XMM_REG(xmm7) +#endif + + // MMX registers are not used to pass arguments so we do not save them + + // Stack is just 8-byte aligned but callee will re-align to 16 + call _libdrm_so_tramp_resolve + +#ifdef __AVX512F__ + POP_ZMM_REG(zmm7) + POP_ZMM_REG(zmm6) + POP_ZMM_REG(zmm5) + POP_ZMM_REG(zmm4) + POP_ZMM_REG(zmm3) + POP_ZMM_REG(zmm2) + POP_ZMM_REG(zmm1) + POP_ZMM_REG(zmm0) // 16 +#elif defined __AVX__ + POP_YMM_REG(ymm7) + POP_YMM_REG(ymm6) + POP_YMM_REG(ymm5) + POP_YMM_REG(ymm4) + POP_YMM_REG(ymm3) + POP_YMM_REG(ymm2) + POP_YMM_REG(ymm1) + POP_YMM_REG(ymm0) // 16 +#elif defined __SSE__ + POP_XMM_REG(xmm7) + POP_XMM_REG(xmm6) + POP_XMM_REG(xmm5) + POP_XMM_REG(xmm4) + POP_XMM_REG(xmm3) + POP_XMM_REG(xmm2) + POP_XMM_REG(xmm1) + POP_XMM_REG(xmm0) // 16 +#endif + + POP_REG(r15) + POP_REG(r14) // 16 + POP_REG(r13) + POP_REG(r12) // 16 + POP_REG(r11) + POP_REG(r10) // 16 + POP_REG(r9) + POP_REG(r8) // 16 + POP_REG(rsi) + POP_REG(rbp) // 16 + POP_REG(rdx) + POP_REG(rcx) // 16 + POP_REG(rbx) + POP_REG(rbx) // 16 + POP_REG(rdi) + + ret + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAddBufs + .p2align 4 + .type drmAddBufs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAddBufs +#endif +drmAddBufs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+0(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+0(%rip) +2: + pushq $0 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAddContextPrivateMapping + .p2align 4 + .type drmAddContextPrivateMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAddContextPrivateMapping +#endif +drmAddContextPrivateMapping: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+8(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+8(%rip) +2: + pushq $1 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAddContextTag + .p2align 4 + .type drmAddContextTag, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAddContextTag +#endif +drmAddContextTag: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+16(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+16(%rip) +2: + pushq $2 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAddMap + .p2align 4 + .type drmAddMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAddMap +#endif +drmAddMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+24(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+24(%rip) +2: + pushq $3 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpAcquire + .p2align 4 + .type drmAgpAcquire, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpAcquire +#endif +drmAgpAcquire: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+32(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+32(%rip) +2: + pushq $4 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpAlloc + .p2align 4 + .type drmAgpAlloc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpAlloc +#endif +drmAgpAlloc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+40(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+40(%rip) +2: + pushq $5 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpBase + .p2align 4 + .type drmAgpBase, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpBase +#endif +drmAgpBase: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+48(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+48(%rip) +2: + pushq $6 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpBind + .p2align 4 + .type drmAgpBind, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpBind +#endif +drmAgpBind: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+56(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+56(%rip) +2: + pushq $7 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpDeviceId + .p2align 4 + .type drmAgpDeviceId, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpDeviceId +#endif +drmAgpDeviceId: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+64(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+64(%rip) +2: + pushq $8 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpEnable + .p2align 4 + .type drmAgpEnable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpEnable +#endif +drmAgpEnable: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+72(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+72(%rip) +2: + pushq $9 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpFree + .p2align 4 + .type drmAgpFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpFree +#endif +drmAgpFree: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+80(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+80(%rip) +2: + pushq $10 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpGetMode + .p2align 4 + .type drmAgpGetMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpGetMode +#endif +drmAgpGetMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+88(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+88(%rip) +2: + pushq $11 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpMemoryAvail + .p2align 4 + .type drmAgpMemoryAvail, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpMemoryAvail +#endif +drmAgpMemoryAvail: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+96(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+96(%rip) +2: + pushq $12 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpMemoryUsed + .p2align 4 + .type drmAgpMemoryUsed, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpMemoryUsed +#endif +drmAgpMemoryUsed: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+104(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+104(%rip) +2: + pushq $13 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpRelease + .p2align 4 + .type drmAgpRelease, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpRelease +#endif +drmAgpRelease: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+112(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+112(%rip) +2: + pushq $14 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpSize + .p2align 4 + .type drmAgpSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpSize +#endif +drmAgpSize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+120(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+120(%rip) +2: + pushq $15 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpUnbind + .p2align 4 + .type drmAgpUnbind, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpUnbind +#endif +drmAgpUnbind: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+128(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+128(%rip) +2: + pushq $16 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpVendorId + .p2align 4 + .type drmAgpVendorId, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpVendorId +#endif +drmAgpVendorId: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+136(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+136(%rip) +2: + pushq $17 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpVersionMajor + .p2align 4 + .type drmAgpVersionMajor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpVersionMajor +#endif +drmAgpVersionMajor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+144(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+144(%rip) +2: + pushq $18 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAgpVersionMinor + .p2align 4 + .type drmAgpVersionMinor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAgpVersionMinor +#endif +drmAgpVersionMinor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+152(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+152(%rip) +2: + pushq $19 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAuthMagic + .p2align 4 + .type drmAuthMagic, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAuthMagic +#endif +drmAuthMagic: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+160(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+160(%rip) +2: + pushq $20 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmAvailable + .p2align 4 + .type drmAvailable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmAvailable +#endif +drmAvailable: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+168(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+168(%rip) +2: + pushq $21 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCheckModesettingSupported + .p2align 4 + .type drmCheckModesettingSupported, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCheckModesettingSupported +#endif +drmCheckModesettingSupported: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+176(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+176(%rip) +2: + pushq $22 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmClose + .p2align 4 + .type drmClose, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmClose +#endif +drmClose: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+184(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+184(%rip) +2: + pushq $23 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCloseBufferHandle + .p2align 4 + .type drmCloseBufferHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCloseBufferHandle +#endif +drmCloseBufferHandle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+192(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+192(%rip) +2: + pushq $24 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCloseOnce + .p2align 4 + .type drmCloseOnce, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCloseOnce +#endif +drmCloseOnce: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+200(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+200(%rip) +2: + pushq $25 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCommandNone + .p2align 4 + .type drmCommandNone, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCommandNone +#endif +drmCommandNone: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+208(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+208(%rip) +2: + pushq $26 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCommandRead + .p2align 4 + .type drmCommandRead, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCommandRead +#endif +drmCommandRead: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+216(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+216(%rip) +2: + pushq $27 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCommandWrite + .p2align 4 + .type drmCommandWrite, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCommandWrite +#endif +drmCommandWrite: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+224(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+224(%rip) +2: + pushq $28 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCommandWriteRead + .p2align 4 + .type drmCommandWriteRead, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCommandWriteRead +#endif +drmCommandWriteRead: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+232(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+232(%rip) +2: + pushq $29 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCreateContext + .p2align 4 + .type drmCreateContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCreateContext +#endif +drmCreateContext: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+240(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+240(%rip) +2: + pushq $30 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCreateDrawable + .p2align 4 + .type drmCreateDrawable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCreateDrawable +#endif +drmCreateDrawable: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+248(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+248(%rip) +2: + pushq $31 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCrtcGetSequence + .p2align 4 + .type drmCrtcGetSequence, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCrtcGetSequence +#endif +drmCrtcGetSequence: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+256(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+256(%rip) +2: + pushq $32 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCrtcQueueSequence + .p2align 4 + .type drmCrtcQueueSequence, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCrtcQueueSequence +#endif +drmCrtcQueueSequence: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+264(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+264(%rip) +2: + pushq $33 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCtlInstHandler + .p2align 4 + .type drmCtlInstHandler, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCtlInstHandler +#endif +drmCtlInstHandler: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+272(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+272(%rip) +2: + pushq $34 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmCtlUninstHandler + .p2align 4 + .type drmCtlUninstHandler, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmCtlUninstHandler +#endif +drmCtlUninstHandler: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+280(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+280(%rip) +2: + pushq $35 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmDMA + .p2align 4 + .type drmDMA, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmDMA +#endif +drmDMA: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+288(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+288(%rip) +2: + pushq $36 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmDelContextTag + .p2align 4 + .type drmDelContextTag, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmDelContextTag +#endif +drmDelContextTag: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+296(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+296(%rip) +2: + pushq $37 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmDestroyContext + .p2align 4 + .type drmDestroyContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmDestroyContext +#endif +drmDestroyContext: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+304(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+304(%rip) +2: + pushq $38 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmDestroyDrawable + .p2align 4 + .type drmDestroyDrawable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmDestroyDrawable +#endif +drmDestroyDrawable: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+312(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+312(%rip) +2: + pushq $39 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmDevicesEqual + .p2align 4 + .type drmDevicesEqual, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmDevicesEqual +#endif +drmDevicesEqual: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+320(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+320(%rip) +2: + pushq $40 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmDropMaster + .p2align 4 + .type drmDropMaster, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmDropMaster +#endif +drmDropMaster: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+328(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+328(%rip) +2: + pushq $41 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmError + .p2align 4 + .type drmError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmError +#endif +drmError: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+336(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+336(%rip) +2: + pushq $42 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmFinish + .p2align 4 + .type drmFinish, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmFinish +#endif +drmFinish: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+344(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+344(%rip) +2: + pushq $43 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmFree + .p2align 4 + .type drmFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmFree +#endif +drmFree: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+352(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+352(%rip) +2: + pushq $44 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmFreeBufs + .p2align 4 + .type drmFreeBufs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmFreeBufs +#endif +drmFreeBufs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+360(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+360(%rip) +2: + pushq $45 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmFreeBusid + .p2align 4 + .type drmFreeBusid, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmFreeBusid +#endif +drmFreeBusid: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+368(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+368(%rip) +2: + pushq $46 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmFreeDevice + .p2align 4 + .type drmFreeDevice, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmFreeDevice +#endif +drmFreeDevice: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+376(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+376(%rip) +2: + pushq $47 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmFreeDevices + .p2align 4 + .type drmFreeDevices, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmFreeDevices +#endif +drmFreeDevices: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+384(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+384(%rip) +2: + pushq $48 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmFreeReservedContextList + .p2align 4 + .type drmFreeReservedContextList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmFreeReservedContextList +#endif +drmFreeReservedContextList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+392(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+392(%rip) +2: + pushq $49 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmFreeVersion + .p2align 4 + .type drmFreeVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmFreeVersion +#endif +drmFreeVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+400(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+400(%rip) +2: + pushq $50 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetBufInfo + .p2align 4 + .type drmGetBufInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetBufInfo +#endif +drmGetBufInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+408(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+408(%rip) +2: + pushq $51 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetBusid + .p2align 4 + .type drmGetBusid, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetBusid +#endif +drmGetBusid: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+416(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+416(%rip) +2: + pushq $52 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetCap + .p2align 4 + .type drmGetCap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetCap +#endif +drmGetCap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+424(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+424(%rip) +2: + pushq $53 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetClient + .p2align 4 + .type drmGetClient, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetClient +#endif +drmGetClient: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+432(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+432(%rip) +2: + pushq $54 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetContextFlags + .p2align 4 + .type drmGetContextFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetContextFlags +#endif +drmGetContextFlags: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+440(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+440(%rip) +2: + pushq $55 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetContextPrivateMapping + .p2align 4 + .type drmGetContextPrivateMapping, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetContextPrivateMapping +#endif +drmGetContextPrivateMapping: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+448(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+448(%rip) +2: + pushq $56 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetContextTag + .p2align 4 + .type drmGetContextTag, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetContextTag +#endif +drmGetContextTag: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+456(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+456(%rip) +2: + pushq $57 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetDevice + .p2align 4 + .type drmGetDevice, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetDevice +#endif +drmGetDevice: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+464(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+464(%rip) +2: + pushq $58 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetDevice2 + .p2align 4 + .type drmGetDevice2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetDevice2 +#endif +drmGetDevice2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+472(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+472(%rip) +2: + pushq $59 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetDeviceFromDevId + .p2align 4 + .type drmGetDeviceFromDevId, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetDeviceFromDevId +#endif +drmGetDeviceFromDevId: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+480(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+480(%rip) +2: + pushq $60 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetDeviceNameFromFd + .p2align 4 + .type drmGetDeviceNameFromFd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetDeviceNameFromFd +#endif +drmGetDeviceNameFromFd: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+488(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+488(%rip) +2: + pushq $61 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetDeviceNameFromFd2 + .p2align 4 + .type drmGetDeviceNameFromFd2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetDeviceNameFromFd2 +#endif +drmGetDeviceNameFromFd2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+496(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+496(%rip) +2: + pushq $62 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetDevices + .p2align 4 + .type drmGetDevices, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetDevices +#endif +drmGetDevices: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+504(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+504(%rip) +2: + pushq $63 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetDevices2 + .p2align 4 + .type drmGetDevices2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetDevices2 +#endif +drmGetDevices2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+512(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+512(%rip) +2: + pushq $64 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetEntry + .p2align 4 + .type drmGetEntry, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetEntry +#endif +drmGetEntry: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+520(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+520(%rip) +2: + pushq $65 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetFormatModifierName + .p2align 4 + .type drmGetFormatModifierName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetFormatModifierName +#endif +drmGetFormatModifierName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+528(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+528(%rip) +2: + pushq $66 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetFormatModifierVendor + .p2align 4 + .type drmGetFormatModifierVendor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetFormatModifierVendor +#endif +drmGetFormatModifierVendor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+536(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+536(%rip) +2: + pushq $67 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetFormatName + .p2align 4 + .type drmGetFormatName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetFormatName +#endif +drmGetFormatName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+544(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+544(%rip) +2: + pushq $68 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetHashTable + .p2align 4 + .type drmGetHashTable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetHashTable +#endif +drmGetHashTable: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+552(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+552(%rip) +2: + pushq $69 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetInterruptFromBusID + .p2align 4 + .type drmGetInterruptFromBusID, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetInterruptFromBusID +#endif +drmGetInterruptFromBusID: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+560(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+560(%rip) +2: + pushq $70 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetLibVersion + .p2align 4 + .type drmGetLibVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetLibVersion +#endif +drmGetLibVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+568(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+568(%rip) +2: + pushq $71 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetLock + .p2align 4 + .type drmGetLock, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetLock +#endif +drmGetLock: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+576(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+576(%rip) +2: + pushq $72 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetMagic + .p2align 4 + .type drmGetMagic, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetMagic +#endif +drmGetMagic: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+584(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+584(%rip) +2: + pushq $73 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetMap + .p2align 4 + .type drmGetMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetMap +#endif +drmGetMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+592(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+592(%rip) +2: + pushq $74 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetNodeTypeFromDevId + .p2align 4 + .type drmGetNodeTypeFromDevId, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetNodeTypeFromDevId +#endif +drmGetNodeTypeFromDevId: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+600(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+600(%rip) +2: + pushq $75 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetNodeTypeFromFd + .p2align 4 + .type drmGetNodeTypeFromFd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetNodeTypeFromFd +#endif +drmGetNodeTypeFromFd: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+608(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+608(%rip) +2: + pushq $76 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetPrimaryDeviceNameFromFd + .p2align 4 + .type drmGetPrimaryDeviceNameFromFd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetPrimaryDeviceNameFromFd +#endif +drmGetPrimaryDeviceNameFromFd: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+616(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+616(%rip) +2: + pushq $77 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetRenderDeviceNameFromFd + .p2align 4 + .type drmGetRenderDeviceNameFromFd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetRenderDeviceNameFromFd +#endif +drmGetRenderDeviceNameFromFd: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+624(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+624(%rip) +2: + pushq $78 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetReservedContextList + .p2align 4 + .type drmGetReservedContextList, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetReservedContextList +#endif +drmGetReservedContextList: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+632(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+632(%rip) +2: + pushq $79 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetStats + .p2align 4 + .type drmGetStats, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetStats +#endif +drmGetStats: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+640(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+640(%rip) +2: + pushq $80 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmGetVersion + .p2align 4 + .type drmGetVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmGetVersion +#endif +drmGetVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+648(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+648(%rip) +2: + pushq $81 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmHandleEvent + .p2align 4 + .type drmHandleEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmHandleEvent +#endif +drmHandleEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+656(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+656(%rip) +2: + pushq $82 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmHashCreate + .p2align 4 + .type drmHashCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmHashCreate +#endif +drmHashCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+664(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+664(%rip) +2: + pushq $83 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmHashDelete + .p2align 4 + .type drmHashDelete, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmHashDelete +#endif +drmHashDelete: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+672(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+672(%rip) +2: + pushq $84 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmHashDestroy + .p2align 4 + .type drmHashDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmHashDestroy +#endif +drmHashDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+680(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+680(%rip) +2: + pushq $85 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmHashFirst + .p2align 4 + .type drmHashFirst, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmHashFirst +#endif +drmHashFirst: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+688(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+688(%rip) +2: + pushq $86 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmHashInsert + .p2align 4 + .type drmHashInsert, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmHashInsert +#endif +drmHashInsert: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+696(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+696(%rip) +2: + pushq $87 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmHashLookup + .p2align 4 + .type drmHashLookup, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmHashLookup +#endif +drmHashLookup: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+704(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+704(%rip) +2: + pushq $88 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmHashNext + .p2align 4 + .type drmHashNext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmHashNext +#endif +drmHashNext: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+712(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+712(%rip) +2: + pushq $89 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmIoctl + .p2align 4 + .type drmIoctl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmIoctl +#endif +drmIoctl: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+720(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+720(%rip) +2: + pushq $90 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmIsKMS + .p2align 4 + .type drmIsKMS, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmIsKMS +#endif +drmIsKMS: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+728(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+728(%rip) +2: + pushq $91 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmIsMaster + .p2align 4 + .type drmIsMaster, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmIsMaster +#endif +drmIsMaster: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+736(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+736(%rip) +2: + pushq $92 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmMalloc + .p2align 4 + .type drmMalloc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmMalloc +#endif +drmMalloc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+744(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+744(%rip) +2: + pushq $93 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmMap + .p2align 4 + .type drmMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmMap +#endif +drmMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+752(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+752(%rip) +2: + pushq $94 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmMapBufs + .p2align 4 + .type drmMapBufs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmMapBufs +#endif +drmMapBufs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+760(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+760(%rip) +2: + pushq $95 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmMarkBufs + .p2align 4 + .type drmMarkBufs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmMarkBufs +#endif +drmMarkBufs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+768(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+768(%rip) +2: + pushq $96 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAddFB + .p2align 4 + .type drmModeAddFB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAddFB +#endif +drmModeAddFB: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+776(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+776(%rip) +2: + pushq $97 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAddFB2 + .p2align 4 + .type drmModeAddFB2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAddFB2 +#endif +drmModeAddFB2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+784(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+784(%rip) +2: + pushq $98 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAddFB2WithModifiers + .p2align 4 + .type drmModeAddFB2WithModifiers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAddFB2WithModifiers +#endif +drmModeAddFB2WithModifiers: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+792(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+792(%rip) +2: + pushq $99 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAtomicAddProperty + .p2align 4 + .type drmModeAtomicAddProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAtomicAddProperty +#endif +drmModeAtomicAddProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+800(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+800(%rip) +2: + pushq $100 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAtomicAlloc + .p2align 4 + .type drmModeAtomicAlloc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAtomicAlloc +#endif +drmModeAtomicAlloc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+808(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+808(%rip) +2: + pushq $101 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAtomicCommit + .p2align 4 + .type drmModeAtomicCommit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAtomicCommit +#endif +drmModeAtomicCommit: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+816(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+816(%rip) +2: + pushq $102 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAtomicDuplicate + .p2align 4 + .type drmModeAtomicDuplicate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAtomicDuplicate +#endif +drmModeAtomicDuplicate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+824(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+824(%rip) +2: + pushq $103 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAtomicFree + .p2align 4 + .type drmModeAtomicFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAtomicFree +#endif +drmModeAtomicFree: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+832(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+832(%rip) +2: + pushq $104 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAtomicGetCursor + .p2align 4 + .type drmModeAtomicGetCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAtomicGetCursor +#endif +drmModeAtomicGetCursor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+840(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+840(%rip) +2: + pushq $105 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAtomicMerge + .p2align 4 + .type drmModeAtomicMerge, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAtomicMerge +#endif +drmModeAtomicMerge: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+848(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+848(%rip) +2: + pushq $106 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAtomicSetCursor + .p2align 4 + .type drmModeAtomicSetCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAtomicSetCursor +#endif +drmModeAtomicSetCursor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+856(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+856(%rip) +2: + pushq $107 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeAttachMode + .p2align 4 + .type drmModeAttachMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeAttachMode +#endif +drmModeAttachMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+864(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+864(%rip) +2: + pushq $108 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeCloseFB + .p2align 4 + .type drmModeCloseFB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeCloseFB +#endif +drmModeCloseFB: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+872(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+872(%rip) +2: + pushq $109 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeConnectorGetPossibleCrtcs + .p2align 4 + .type drmModeConnectorGetPossibleCrtcs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeConnectorGetPossibleCrtcs +#endif +drmModeConnectorGetPossibleCrtcs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+880(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+880(%rip) +2: + pushq $110 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeConnectorSetProperty + .p2align 4 + .type drmModeConnectorSetProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeConnectorSetProperty +#endif +drmModeConnectorSetProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+888(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+888(%rip) +2: + pushq $111 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeCreateDumbBuffer + .p2align 4 + .type drmModeCreateDumbBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeCreateDumbBuffer +#endif +drmModeCreateDumbBuffer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+896(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+896(%rip) +2: + pushq $112 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeCreateLease + .p2align 4 + .type drmModeCreateLease, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeCreateLease +#endif +drmModeCreateLease: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+904(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+904(%rip) +2: + pushq $113 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeCreatePropertyBlob + .p2align 4 + .type drmModeCreatePropertyBlob, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeCreatePropertyBlob +#endif +drmModeCreatePropertyBlob: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+912(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+912(%rip) +2: + pushq $114 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeCrtcGetGamma + .p2align 4 + .type drmModeCrtcGetGamma, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeCrtcGetGamma +#endif +drmModeCrtcGetGamma: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+920(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+920(%rip) +2: + pushq $115 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeCrtcSetGamma + .p2align 4 + .type drmModeCrtcSetGamma, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeCrtcSetGamma +#endif +drmModeCrtcSetGamma: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+928(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+928(%rip) +2: + pushq $116 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeDestroyDumbBuffer + .p2align 4 + .type drmModeDestroyDumbBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeDestroyDumbBuffer +#endif +drmModeDestroyDumbBuffer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+936(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+936(%rip) +2: + pushq $117 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeDestroyPropertyBlob + .p2align 4 + .type drmModeDestroyPropertyBlob, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeDestroyPropertyBlob +#endif +drmModeDestroyPropertyBlob: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+944(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+944(%rip) +2: + pushq $118 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeDetachMode + .p2align 4 + .type drmModeDetachMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeDetachMode +#endif +drmModeDetachMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+952(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+952(%rip) +2: + pushq $119 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeDirtyFB + .p2align 4 + .type drmModeDirtyFB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeDirtyFB +#endif +drmModeDirtyFB: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+960(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+960(%rip) +2: + pushq $120 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFormatModifierBlobIterNext + .p2align 4 + .type drmModeFormatModifierBlobIterNext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFormatModifierBlobIterNext +#endif +drmModeFormatModifierBlobIterNext: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+968(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+968(%rip) +2: + pushq $121 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreeConnector + .p2align 4 + .type drmModeFreeConnector, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreeConnector +#endif +drmModeFreeConnector: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+976(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+976(%rip) +2: + pushq $122 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreeCrtc + .p2align 4 + .type drmModeFreeCrtc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreeCrtc +#endif +drmModeFreeCrtc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+984(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+984(%rip) +2: + pushq $123 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreeEncoder + .p2align 4 + .type drmModeFreeEncoder, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreeEncoder +#endif +drmModeFreeEncoder: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+992(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+992(%rip) +2: + pushq $124 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreeFB + .p2align 4 + .type drmModeFreeFB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreeFB +#endif +drmModeFreeFB: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1000(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1000(%rip) +2: + pushq $125 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreeFB2 + .p2align 4 + .type drmModeFreeFB2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreeFB2 +#endif +drmModeFreeFB2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1008(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1008(%rip) +2: + pushq $126 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreeModeInfo + .p2align 4 + .type drmModeFreeModeInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreeModeInfo +#endif +drmModeFreeModeInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1016(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1016(%rip) +2: + pushq $127 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreeObjectProperties + .p2align 4 + .type drmModeFreeObjectProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreeObjectProperties +#endif +drmModeFreeObjectProperties: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1024(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1024(%rip) +2: + pushq $128 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreePlane + .p2align 4 + .type drmModeFreePlane, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreePlane +#endif +drmModeFreePlane: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1032(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1032(%rip) +2: + pushq $129 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreePlaneResources + .p2align 4 + .type drmModeFreePlaneResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreePlaneResources +#endif +drmModeFreePlaneResources: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1040(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1040(%rip) +2: + pushq $130 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreeProperty + .p2align 4 + .type drmModeFreeProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreeProperty +#endif +drmModeFreeProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1048(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1048(%rip) +2: + pushq $131 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreePropertyBlob + .p2align 4 + .type drmModeFreePropertyBlob, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreePropertyBlob +#endif +drmModeFreePropertyBlob: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1056(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1056(%rip) +2: + pushq $132 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeFreeResources + .p2align 4 + .type drmModeFreeResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeFreeResources +#endif +drmModeFreeResources: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1064(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1064(%rip) +2: + pushq $133 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetConnector + .p2align 4 + .type drmModeGetConnector, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetConnector +#endif +drmModeGetConnector: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1072(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1072(%rip) +2: + pushq $134 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetConnectorCurrent + .p2align 4 + .type drmModeGetConnectorCurrent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetConnectorCurrent +#endif +drmModeGetConnectorCurrent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1080(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1080(%rip) +2: + pushq $135 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetConnectorTypeName + .p2align 4 + .type drmModeGetConnectorTypeName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetConnectorTypeName +#endif +drmModeGetConnectorTypeName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1088(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1088(%rip) +2: + pushq $136 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetCrtc + .p2align 4 + .type drmModeGetCrtc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetCrtc +#endif +drmModeGetCrtc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1096(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1096(%rip) +2: + pushq $137 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetEncoder + .p2align 4 + .type drmModeGetEncoder, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetEncoder +#endif +drmModeGetEncoder: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1104(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1104(%rip) +2: + pushq $138 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetFB + .p2align 4 + .type drmModeGetFB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetFB +#endif +drmModeGetFB: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1112(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1112(%rip) +2: + pushq $139 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetFB2 + .p2align 4 + .type drmModeGetFB2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetFB2 +#endif +drmModeGetFB2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1120(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1120(%rip) +2: + pushq $140 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetLease + .p2align 4 + .type drmModeGetLease, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetLease +#endif +drmModeGetLease: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1128(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1128(%rip) +2: + pushq $141 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetPlane + .p2align 4 + .type drmModeGetPlane, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetPlane +#endif +drmModeGetPlane: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1136(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1136(%rip) +2: + pushq $142 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetPlaneResources + .p2align 4 + .type drmModeGetPlaneResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetPlaneResources +#endif +drmModeGetPlaneResources: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1144(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1144(%rip) +2: + pushq $143 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetProperty + .p2align 4 + .type drmModeGetProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetProperty +#endif +drmModeGetProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1152(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1152(%rip) +2: + pushq $144 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetPropertyBlob + .p2align 4 + .type drmModeGetPropertyBlob, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetPropertyBlob +#endif +drmModeGetPropertyBlob: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1160(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1160(%rip) +2: + pushq $145 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeGetResources + .p2align 4 + .type drmModeGetResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeGetResources +#endif +drmModeGetResources: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1168(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1168(%rip) +2: + pushq $146 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeListLessees + .p2align 4 + .type drmModeListLessees, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeListLessees +#endif +drmModeListLessees: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1176(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1176(%rip) +2: + pushq $147 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeMapDumbBuffer + .p2align 4 + .type drmModeMapDumbBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeMapDumbBuffer +#endif +drmModeMapDumbBuffer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1184(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1184(%rip) +2: + pushq $148 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeMoveCursor + .p2align 4 + .type drmModeMoveCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeMoveCursor +#endif +drmModeMoveCursor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1192(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1192(%rip) +2: + pushq $149 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeObjectGetProperties + .p2align 4 + .type drmModeObjectGetProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeObjectGetProperties +#endif +drmModeObjectGetProperties: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1200(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1200(%rip) +2: + pushq $150 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeObjectSetProperty + .p2align 4 + .type drmModeObjectSetProperty, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeObjectSetProperty +#endif +drmModeObjectSetProperty: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1208(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1208(%rip) +2: + pushq $151 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModePageFlip + .p2align 4 + .type drmModePageFlip, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModePageFlip +#endif +drmModePageFlip: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1216(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1216(%rip) +2: + pushq $152 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModePageFlipTarget + .p2align 4 + .type drmModePageFlipTarget, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModePageFlipTarget +#endif +drmModePageFlipTarget: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1224(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1224(%rip) +2: + pushq $153 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeRevokeLease + .p2align 4 + .type drmModeRevokeLease, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeRevokeLease +#endif +drmModeRevokeLease: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1232(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1232(%rip) +2: + pushq $154 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeRmFB + .p2align 4 + .type drmModeRmFB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeRmFB +#endif +drmModeRmFB: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1240(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1240(%rip) +2: + pushq $155 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeSetCrtc + .p2align 4 + .type drmModeSetCrtc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeSetCrtc +#endif +drmModeSetCrtc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1248(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1248(%rip) +2: + pushq $156 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeSetCursor + .p2align 4 + .type drmModeSetCursor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeSetCursor +#endif +drmModeSetCursor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1256(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1256(%rip) +2: + pushq $157 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeSetCursor2 + .p2align 4 + .type drmModeSetCursor2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeSetCursor2 +#endif +drmModeSetCursor2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1264(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1264(%rip) +2: + pushq $158 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmModeSetPlane + .p2align 4 + .type drmModeSetPlane, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmModeSetPlane +#endif +drmModeSetPlane: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1272(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1272(%rip) +2: + pushq $159 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmMsg + .p2align 4 + .type drmMsg, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmMsg +#endif +drmMsg: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1280(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1280(%rip) +2: + pushq $160 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmOpen + .p2align 4 + .type drmOpen, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmOpen +#endif +drmOpen: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1288(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1288(%rip) +2: + pushq $161 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmOpenControl + .p2align 4 + .type drmOpenControl, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmOpenControl +#endif +drmOpenControl: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1296(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1296(%rip) +2: + pushq $162 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmOpenOnce + .p2align 4 + .type drmOpenOnce, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmOpenOnce +#endif +drmOpenOnce: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1304(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1304(%rip) +2: + pushq $163 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmOpenOnceWithType + .p2align 4 + .type drmOpenOnceWithType, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmOpenOnceWithType +#endif +drmOpenOnceWithType: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1312(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1312(%rip) +2: + pushq $164 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmOpenRender + .p2align 4 + .type drmOpenRender, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmOpenRender +#endif +drmOpenRender: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1320(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1320(%rip) +2: + pushq $165 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmOpenWithType + .p2align 4 + .type drmOpenWithType, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmOpenWithType +#endif +drmOpenWithType: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1328(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1328(%rip) +2: + pushq $166 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmPrimeFDToHandle + .p2align 4 + .type drmPrimeFDToHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmPrimeFDToHandle +#endif +drmPrimeFDToHandle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1336(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1336(%rip) +2: + pushq $167 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmPrimeHandleToFD + .p2align 4 + .type drmPrimeHandleToFD, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmPrimeHandleToFD +#endif +drmPrimeHandleToFD: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1344(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1344(%rip) +2: + pushq $168 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmRandom + .p2align 4 + .type drmRandom, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmRandom +#endif +drmRandom: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1352(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1352(%rip) +2: + pushq $169 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmRandomCreate + .p2align 4 + .type drmRandomCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmRandomCreate +#endif +drmRandomCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1360(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1360(%rip) +2: + pushq $170 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmRandomDestroy + .p2align 4 + .type drmRandomDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmRandomDestroy +#endif +drmRandomDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1368(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1368(%rip) +2: + pushq $171 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmRandomDouble + .p2align 4 + .type drmRandomDouble, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmRandomDouble +#endif +drmRandomDouble: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1376(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1376(%rip) +2: + pushq $172 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmRmMap + .p2align 4 + .type drmRmMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmRmMap +#endif +drmRmMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1384(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1384(%rip) +2: + pushq $173 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSLCreate + .p2align 4 + .type drmSLCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSLCreate +#endif +drmSLCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1392(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1392(%rip) +2: + pushq $174 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSLDelete + .p2align 4 + .type drmSLDelete, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSLDelete +#endif +drmSLDelete: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1400(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1400(%rip) +2: + pushq $175 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSLDestroy + .p2align 4 + .type drmSLDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSLDestroy +#endif +drmSLDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1408(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1408(%rip) +2: + pushq $176 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSLDump + .p2align 4 + .type drmSLDump, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSLDump +#endif +drmSLDump: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1416(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1416(%rip) +2: + pushq $177 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSLFirst + .p2align 4 + .type drmSLFirst, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSLFirst +#endif +drmSLFirst: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1424(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1424(%rip) +2: + pushq $178 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSLInsert + .p2align 4 + .type drmSLInsert, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSLInsert +#endif +drmSLInsert: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1432(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1432(%rip) +2: + pushq $179 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSLLookup + .p2align 4 + .type drmSLLookup, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSLLookup +#endif +drmSLLookup: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1440(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1440(%rip) +2: + pushq $180 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSLLookupNeighbors + .p2align 4 + .type drmSLLookupNeighbors, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSLLookupNeighbors +#endif +drmSLLookupNeighbors: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1448(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1448(%rip) +2: + pushq $181 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSLNext + .p2align 4 + .type drmSLNext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSLNext +#endif +drmSLNext: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1456(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1456(%rip) +2: + pushq $182 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmScatterGatherAlloc + .p2align 4 + .type drmScatterGatherAlloc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmScatterGatherAlloc +#endif +drmScatterGatherAlloc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1464(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1464(%rip) +2: + pushq $183 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmScatterGatherFree + .p2align 4 + .type drmScatterGatherFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmScatterGatherFree +#endif +drmScatterGatherFree: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1472(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1472(%rip) +2: + pushq $184 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSetBusid + .p2align 4 + .type drmSetBusid, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSetBusid +#endif +drmSetBusid: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1480(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1480(%rip) +2: + pushq $185 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSetClientCap + .p2align 4 + .type drmSetClientCap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSetClientCap +#endif +drmSetClientCap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1488(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1488(%rip) +2: + pushq $186 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSetContextFlags + .p2align 4 + .type drmSetContextFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSetContextFlags +#endif +drmSetContextFlags: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1496(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1496(%rip) +2: + pushq $187 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSetInterfaceVersion + .p2align 4 + .type drmSetInterfaceVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSetInterfaceVersion +#endif +drmSetInterfaceVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1504(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1504(%rip) +2: + pushq $188 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSetMaster + .p2align 4 + .type drmSetMaster, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSetMaster +#endif +drmSetMaster: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1512(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1512(%rip) +2: + pushq $189 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSetServerInfo + .p2align 4 + .type drmSetServerInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSetServerInfo +#endif +drmSetServerInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1520(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1520(%rip) +2: + pushq $190 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSwitchToContext + .p2align 4 + .type drmSwitchToContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSwitchToContext +#endif +drmSwitchToContext: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1528(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1528(%rip) +2: + pushq $191 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjCreate + .p2align 4 + .type drmSyncobjCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjCreate +#endif +drmSyncobjCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1536(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1536(%rip) +2: + pushq $192 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjDestroy + .p2align 4 + .type drmSyncobjDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjDestroy +#endif +drmSyncobjDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1544(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1544(%rip) +2: + pushq $193 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjEventfd + .p2align 4 + .type drmSyncobjEventfd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjEventfd +#endif +drmSyncobjEventfd: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1552(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1552(%rip) +2: + pushq $194 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjExportSyncFile + .p2align 4 + .type drmSyncobjExportSyncFile, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjExportSyncFile +#endif +drmSyncobjExportSyncFile: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1560(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1560(%rip) +2: + pushq $195 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjFDToHandle + .p2align 4 + .type drmSyncobjFDToHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjFDToHandle +#endif +drmSyncobjFDToHandle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1568(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1568(%rip) +2: + pushq $196 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjHandleToFD + .p2align 4 + .type drmSyncobjHandleToFD, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjHandleToFD +#endif +drmSyncobjHandleToFD: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1576(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1576(%rip) +2: + pushq $197 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjImportSyncFile + .p2align 4 + .type drmSyncobjImportSyncFile, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjImportSyncFile +#endif +drmSyncobjImportSyncFile: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1584(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1584(%rip) +2: + pushq $198 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjQuery + .p2align 4 + .type drmSyncobjQuery, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjQuery +#endif +drmSyncobjQuery: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1592(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1592(%rip) +2: + pushq $199 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjQuery2 + .p2align 4 + .type drmSyncobjQuery2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjQuery2 +#endif +drmSyncobjQuery2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1600(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1600(%rip) +2: + pushq $200 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjReset + .p2align 4 + .type drmSyncobjReset, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjReset +#endif +drmSyncobjReset: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1608(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1608(%rip) +2: + pushq $201 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjSignal + .p2align 4 + .type drmSyncobjSignal, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjSignal +#endif +drmSyncobjSignal: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1616(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1616(%rip) +2: + pushq $202 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjTimelineSignal + .p2align 4 + .type drmSyncobjTimelineSignal, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjTimelineSignal +#endif +drmSyncobjTimelineSignal: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1624(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1624(%rip) +2: + pushq $203 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjTimelineWait + .p2align 4 + .type drmSyncobjTimelineWait, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjTimelineWait +#endif +drmSyncobjTimelineWait: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1632(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1632(%rip) +2: + pushq $204 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjTransfer + .p2align 4 + .type drmSyncobjTransfer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjTransfer +#endif +drmSyncobjTransfer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1640(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1640(%rip) +2: + pushq $205 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmSyncobjWait + .p2align 4 + .type drmSyncobjWait, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmSyncobjWait +#endif +drmSyncobjWait: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1648(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1648(%rip) +2: + pushq $206 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmUnlock + .p2align 4 + .type drmUnlock, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmUnlock +#endif +drmUnlock: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1656(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1656(%rip) +2: + pushq $207 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmUnmap + .p2align 4 + .type drmUnmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmUnmap +#endif +drmUnmap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1664(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1664(%rip) +2: + pushq $208 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmUnmapBufs + .p2align 4 + .type drmUnmapBufs, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmUnmapBufs +#endif +drmUnmapBufs: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1672(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1672(%rip) +2: + pushq $209 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmUpdateDrawableInfo + .p2align 4 + .type drmUpdateDrawableInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmUpdateDrawableInfo +#endif +drmUpdateDrawableInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1680(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1680(%rip) +2: + pushq $210 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl drmWaitVBlank + .p2align 4 + .type drmWaitVBlank, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden drmWaitVBlank +#endif +drmWaitVBlank: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libdrm_so_tramp_table+1688(%rip) + je 2f +1: + jmp *_libdrm_so_tramp_table+1688(%rip) +2: + pushq $211 + .cfi_adjust_cfa_offset 8 + call _libdrm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libgbm.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libgbm.so.init.c new file mode 100644 index 0000000..1dc5173 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libgbm.so.init.c @@ -0,0 +1,282 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libgbm.so.1: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libgbm.so.1"); + CHECK(lib_handle, "failed to load library 'libgbm.so.1' via callback ''"); +#else + lib_handle = dlopen("libgbm.so.1", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libgbm.so.1' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "gbm_bo_create", + "gbm_bo_create_with_modifiers", + "gbm_bo_create_with_modifiers2", + "gbm_bo_destroy", + "gbm_bo_get_bpp", + "gbm_bo_get_device", + "gbm_bo_get_fd", + "gbm_bo_get_fd_for_plane", + "gbm_bo_get_format", + "gbm_bo_get_handle", + "gbm_bo_get_handle_for_plane", + "gbm_bo_get_height", + "gbm_bo_get_modifier", + "gbm_bo_get_offset", + "gbm_bo_get_plane_count", + "gbm_bo_get_stride", + "gbm_bo_get_stride_for_plane", + "gbm_bo_get_user_data", + "gbm_bo_get_width", + "gbm_bo_import", + "gbm_bo_map", + "gbm_bo_set_user_data", + "gbm_bo_unmap", + "gbm_bo_write", + "gbm_create_device", + "gbm_device_destroy", + "gbm_device_get_backend_name", + "gbm_device_get_fd", + "gbm_device_get_format_modifier_plane_count", + "gbm_device_is_format_supported", + "gbm_format_get_name", + "gbm_surface_create", + "gbm_surface_create_with_modifiers", + "gbm_surface_create_with_modifiers2", + "gbm_surface_destroy", + "gbm_surface_has_free_buffers", + "gbm_surface_lock_front_buffer", + "gbm_surface_release_buffer", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libgbm_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libgbm_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libgbm_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libgbm_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libgbm_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libgbm_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libgbm_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libgbm_so_tramp_table, 0, SYM_COUNT * sizeof(_libgbm_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libgbm.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libgbm.so.tramp.S new file mode 100644 index 0000000..f76024d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/desktop_capturer/x86_64-linux-gnu/libgbm.so.tramp.S @@ -0,0 +1,1450 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libgbm_so_tramp_table + .hidden _libgbm_so_tramp_table + .align 8 +_libgbm_so_tramp_table: + .zero 312 + + .text + + .globl _libgbm_so_tramp_resolve + .hidden _libgbm_so_tramp_resolve + + .globl _libgbm_so_save_regs_and_resolve + .hidden _libgbm_so_save_regs_and_resolve + .type _libgbm_so_save_regs_and_resolve, %function +_libgbm_so_save_regs_and_resolve: + .cfi_startproc + +#define PUSH_REG(reg) pushq %reg ; .cfi_adjust_cfa_offset 8; .cfi_rel_offset reg, 0 +#define POP_REG(reg) popq %reg ; .cfi_adjust_cfa_offset -8; .cfi_restore reg + +#define DEC_STACK(d) subq $d, %rsp; .cfi_adjust_cfa_offset d +#define INC_STACK(d) addq $d, %rsp; .cfi_adjust_cfa_offset -d + +#define PUSH_MMX_REG(reg) DEC_STACK(8); movq %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_MMX_REG(reg) movq (%rsp), %reg; .cfi_restore reg; INC_STACK(8) + +#define PUSH_XMM_REG(reg) DEC_STACK(16); movdqa %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_XMM_REG(reg) movdqa (%rsp), %reg; .cfi_restore reg; INC_STACK(16) + +// TODO: cfi_offset/cfi_restore +#define PUSH_YMM_REG(reg) DEC_STACK(32); vmovdqu %reg, (%rsp) +#define POP_YMM_REG(reg) vmovdqu (%rsp), %reg; INC_STACK(32) + +// TODO: cfi_offset/cfi_restore +#define PUSH_ZMM_REG(reg) DEC_STACK(64); vmovdqu32 %reg, (%rsp) +#define POP_ZMM_REG(reg) vmovdqu32 (%rsp), %reg; INC_STACK(64) + + // Slow path which calls dlsym, taken only on first call. + // All registers are stored to handle arbitrary calling conventions + // (except x87 FPU registers which do not have to be preserved). + // For Dwarf directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + .cfi_def_cfa_offset 8 // Return address + + PUSH_REG(rdi) // 16 + mov 0x10(%rsp), %rdi + PUSH_REG(rbx) + PUSH_REG(rbx) // 16 + PUSH_REG(rcx) + PUSH_REG(rdx) // 16 + PUSH_REG(rbp) + PUSH_REG(rsi) // 16 + PUSH_REG(r8) + PUSH_REG(r9) // 16 + PUSH_REG(r10) + PUSH_REG(r11) // 16 + PUSH_REG(r12) + PUSH_REG(r13) // 16 + PUSH_REG(r14) + PUSH_REG(r15) // 16 + + // Maybe use cpuid instead of macro to detect current vector size... +#ifdef __AVX512F__ + PUSH_ZMM_REG(zmm0) + PUSH_ZMM_REG(zmm1) + PUSH_ZMM_REG(zmm2) + PUSH_ZMM_REG(zmm3) + PUSH_ZMM_REG(zmm4) + PUSH_ZMM_REG(zmm5) + PUSH_ZMM_REG(zmm6) + PUSH_ZMM_REG(zmm7) +#elif defined __AVX__ + PUSH_YMM_REG(ymm0) + PUSH_YMM_REG(ymm1) + PUSH_YMM_REG(ymm2) + PUSH_YMM_REG(ymm3) + PUSH_YMM_REG(ymm4) + PUSH_YMM_REG(ymm5) + PUSH_YMM_REG(ymm6) + PUSH_YMM_REG(ymm7) +#elif defined __SSE__ + PUSH_XMM_REG(xmm0) + PUSH_XMM_REG(xmm1) + PUSH_XMM_REG(xmm2) + PUSH_XMM_REG(xmm3) + PUSH_XMM_REG(xmm4) + PUSH_XMM_REG(xmm5) + PUSH_XMM_REG(xmm6) + PUSH_XMM_REG(xmm7) +#endif + + // MMX registers are not used to pass arguments so we do not save them + + // Stack is just 8-byte aligned but callee will re-align to 16 + call _libgbm_so_tramp_resolve + +#ifdef __AVX512F__ + POP_ZMM_REG(zmm7) + POP_ZMM_REG(zmm6) + POP_ZMM_REG(zmm5) + POP_ZMM_REG(zmm4) + POP_ZMM_REG(zmm3) + POP_ZMM_REG(zmm2) + POP_ZMM_REG(zmm1) + POP_ZMM_REG(zmm0) // 16 +#elif defined __AVX__ + POP_YMM_REG(ymm7) + POP_YMM_REG(ymm6) + POP_YMM_REG(ymm5) + POP_YMM_REG(ymm4) + POP_YMM_REG(ymm3) + POP_YMM_REG(ymm2) + POP_YMM_REG(ymm1) + POP_YMM_REG(ymm0) // 16 +#elif defined __SSE__ + POP_XMM_REG(xmm7) + POP_XMM_REG(xmm6) + POP_XMM_REG(xmm5) + POP_XMM_REG(xmm4) + POP_XMM_REG(xmm3) + POP_XMM_REG(xmm2) + POP_XMM_REG(xmm1) + POP_XMM_REG(xmm0) // 16 +#endif + + POP_REG(r15) + POP_REG(r14) // 16 + POP_REG(r13) + POP_REG(r12) // 16 + POP_REG(r11) + POP_REG(r10) // 16 + POP_REG(r9) + POP_REG(r8) // 16 + POP_REG(rsi) + POP_REG(rbp) // 16 + POP_REG(rdx) + POP_REG(rcx) // 16 + POP_REG(rbx) + POP_REG(rbx) // 16 + POP_REG(rdi) + + ret + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_create + .p2align 4 + .type gbm_bo_create, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_create +#endif +gbm_bo_create: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+0(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+0(%rip) +2: + pushq $0 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_create_with_modifiers + .p2align 4 + .type gbm_bo_create_with_modifiers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_create_with_modifiers +#endif +gbm_bo_create_with_modifiers: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+8(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+8(%rip) +2: + pushq $1 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_create_with_modifiers2 + .p2align 4 + .type gbm_bo_create_with_modifiers2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_create_with_modifiers2 +#endif +gbm_bo_create_with_modifiers2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+16(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+16(%rip) +2: + pushq $2 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_destroy + .p2align 4 + .type gbm_bo_destroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_destroy +#endif +gbm_bo_destroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+24(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+24(%rip) +2: + pushq $3 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_bpp + .p2align 4 + .type gbm_bo_get_bpp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_bpp +#endif +gbm_bo_get_bpp: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+32(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+32(%rip) +2: + pushq $4 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_device + .p2align 4 + .type gbm_bo_get_device, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_device +#endif +gbm_bo_get_device: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+40(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+40(%rip) +2: + pushq $5 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_fd + .p2align 4 + .type gbm_bo_get_fd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_fd +#endif +gbm_bo_get_fd: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+48(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+48(%rip) +2: + pushq $6 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_fd_for_plane + .p2align 4 + .type gbm_bo_get_fd_for_plane, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_fd_for_plane +#endif +gbm_bo_get_fd_for_plane: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+56(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+56(%rip) +2: + pushq $7 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_format + .p2align 4 + .type gbm_bo_get_format, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_format +#endif +gbm_bo_get_format: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+64(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+64(%rip) +2: + pushq $8 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_handle + .p2align 4 + .type gbm_bo_get_handle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_handle +#endif +gbm_bo_get_handle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+72(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+72(%rip) +2: + pushq $9 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_handle_for_plane + .p2align 4 + .type gbm_bo_get_handle_for_plane, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_handle_for_plane +#endif +gbm_bo_get_handle_for_plane: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+80(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+80(%rip) +2: + pushq $10 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_height + .p2align 4 + .type gbm_bo_get_height, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_height +#endif +gbm_bo_get_height: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+88(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+88(%rip) +2: + pushq $11 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_modifier + .p2align 4 + .type gbm_bo_get_modifier, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_modifier +#endif +gbm_bo_get_modifier: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+96(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+96(%rip) +2: + pushq $12 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_offset + .p2align 4 + .type gbm_bo_get_offset, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_offset +#endif +gbm_bo_get_offset: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+104(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+104(%rip) +2: + pushq $13 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_plane_count + .p2align 4 + .type gbm_bo_get_plane_count, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_plane_count +#endif +gbm_bo_get_plane_count: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+112(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+112(%rip) +2: + pushq $14 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_stride + .p2align 4 + .type gbm_bo_get_stride, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_stride +#endif +gbm_bo_get_stride: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+120(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+120(%rip) +2: + pushq $15 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_stride_for_plane + .p2align 4 + .type gbm_bo_get_stride_for_plane, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_stride_for_plane +#endif +gbm_bo_get_stride_for_plane: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+128(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+128(%rip) +2: + pushq $16 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_user_data + .p2align 4 + .type gbm_bo_get_user_data, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_user_data +#endif +gbm_bo_get_user_data: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+136(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+136(%rip) +2: + pushq $17 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_get_width + .p2align 4 + .type gbm_bo_get_width, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_get_width +#endif +gbm_bo_get_width: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+144(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+144(%rip) +2: + pushq $18 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_import + .p2align 4 + .type gbm_bo_import, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_import +#endif +gbm_bo_import: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+152(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+152(%rip) +2: + pushq $19 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_map + .p2align 4 + .type gbm_bo_map, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_map +#endif +gbm_bo_map: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+160(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+160(%rip) +2: + pushq $20 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_set_user_data + .p2align 4 + .type gbm_bo_set_user_data, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_set_user_data +#endif +gbm_bo_set_user_data: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+168(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+168(%rip) +2: + pushq $21 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_unmap + .p2align 4 + .type gbm_bo_unmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_unmap +#endif +gbm_bo_unmap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+176(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+176(%rip) +2: + pushq $22 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_bo_write + .p2align 4 + .type gbm_bo_write, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_bo_write +#endif +gbm_bo_write: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+184(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+184(%rip) +2: + pushq $23 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_create_device + .p2align 4 + .type gbm_create_device, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_create_device +#endif +gbm_create_device: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+192(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+192(%rip) +2: + pushq $24 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_device_destroy + .p2align 4 + .type gbm_device_destroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_device_destroy +#endif +gbm_device_destroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+200(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+200(%rip) +2: + pushq $25 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_device_get_backend_name + .p2align 4 + .type gbm_device_get_backend_name, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_device_get_backend_name +#endif +gbm_device_get_backend_name: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+208(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+208(%rip) +2: + pushq $26 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_device_get_fd + .p2align 4 + .type gbm_device_get_fd, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_device_get_fd +#endif +gbm_device_get_fd: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+216(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+216(%rip) +2: + pushq $27 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_device_get_format_modifier_plane_count + .p2align 4 + .type gbm_device_get_format_modifier_plane_count, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_device_get_format_modifier_plane_count +#endif +gbm_device_get_format_modifier_plane_count: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+224(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+224(%rip) +2: + pushq $28 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_device_is_format_supported + .p2align 4 + .type gbm_device_is_format_supported, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_device_is_format_supported +#endif +gbm_device_is_format_supported: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+232(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+232(%rip) +2: + pushq $29 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_format_get_name + .p2align 4 + .type gbm_format_get_name, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_format_get_name +#endif +gbm_format_get_name: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+240(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+240(%rip) +2: + pushq $30 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_surface_create + .p2align 4 + .type gbm_surface_create, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_surface_create +#endif +gbm_surface_create: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+248(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+248(%rip) +2: + pushq $31 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_surface_create_with_modifiers + .p2align 4 + .type gbm_surface_create_with_modifiers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_surface_create_with_modifiers +#endif +gbm_surface_create_with_modifiers: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+256(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+256(%rip) +2: + pushq $32 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_surface_create_with_modifiers2 + .p2align 4 + .type gbm_surface_create_with_modifiers2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_surface_create_with_modifiers2 +#endif +gbm_surface_create_with_modifiers2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+264(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+264(%rip) +2: + pushq $33 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_surface_destroy + .p2align 4 + .type gbm_surface_destroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_surface_destroy +#endif +gbm_surface_destroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+272(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+272(%rip) +2: + pushq $34 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_surface_has_free_buffers + .p2align 4 + .type gbm_surface_has_free_buffers, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_surface_has_free_buffers +#endif +gbm_surface_has_free_buffers: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+280(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+280(%rip) +2: + pushq $35 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_surface_lock_front_buffer + .p2align 4 + .type gbm_surface_lock_front_buffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_surface_lock_front_buffer +#endif +gbm_surface_lock_front_buffer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+288(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+288(%rip) +2: + pushq $36 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl gbm_surface_release_buffer + .p2align 4 + .type gbm_surface_release_buffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden gbm_surface_release_buffer +#endif +gbm_surface_release_buffer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libgbm_so_tramp_table+296(%rip) + je 2f +1: + jmp *_libgbm_so_tramp_table+296(%rip) +2: + pushq $37 + .cfi_adjust_cfa_offset 8 + call _libgbm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/generate_implibs.sh b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/generate_implibs.sh new file mode 100755 index 0000000..6dd0920 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/generate_implibs.sh @@ -0,0 +1,53 @@ +#!/bin/bash +# Copyright 2023 LiveKit, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + + +if [ ! -e "$(pwd)/Implib.so" ] +then + git clone --depth 1 https://github.com/yugr/Implib.so.git +fi + +generate_implib() { + category=$1 + libname=$2 + arch=$3 + echo "Generating implib for category: ${category} libname: ${libname} - ${arch}, output to ${category}/${arch}/" + mkdir -p ${category}/${arch}/ + python3 $(pwd)/Implib.so/implib-gen.py /lib/x86_64-linux-gnu/${libname}.so --target ${arch} --outdir ${category}/${arch}/ +} + +desktop_capturer_deps=("libdrm" "libgbm" "libXfixes" "libXdamage" "libXcomposite" "libXrandr" "libXext" "libX11") + +for dep in "${desktop_capturer_deps[@]}" +do + generate_implib "desktop_capturer" ${dep} "x86_64-linux-gnu" + generate_implib "desktop_capturer" ${dep} "aarch64-linux-gnu" +done + +nvidia_deps=("libcuda" "libnvcuvid") + +for dep in "${nvidia_deps[@]}" +do + generate_implib "nvidia" ${dep} "x86_64-linux-gnu" + generate_implib "nvidia" ${dep} "aarch64-linux-gnu" +done + + +vaapi_deps=("libva" "libva-drm") +for dep in "${vaapi_deps[@]}" +do + generate_implib "vaapi" ${dep} "x86_64-linux-gnu" + generate_implib "vaapi" ${dep} "aarch64-linux-gnu" +done \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/aarch64-linux-gnu/libcuda.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/aarch64-linux-gnu/libcuda.so.init.c new file mode 100644 index 0000000..1e4b687 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/aarch64-linux-gnu/libcuda.so.init.c @@ -0,0 +1,903 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libcuda.so.1: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libcuda.so.1"); + CHECK(lib_handle, "failed to load library 'libcuda.so.1' via callback ''"); +#else + lib_handle = dlopen("libcuda.so.1", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libcuda.so.1' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "cuArray3DCreate", + "cuArray3DCreate_v2", + "cuArray3DGetDescriptor", + "cuArray3DGetDescriptor_v2", + "cuArrayCreate", + "cuArrayCreate_v2", + "cuArrayDestroy", + "cuArrayGetDescriptor", + "cuArrayGetDescriptor_v2", + "cuArrayGetMemoryRequirements", + "cuArrayGetPlane", + "cuArrayGetSparseProperties", + "cuCheckpointProcessCheckpoint", + "cuCheckpointProcessGetRestoreThreadId", + "cuCheckpointProcessGetState", + "cuCheckpointProcessLock", + "cuCheckpointProcessRestore", + "cuCheckpointProcessUnlock", + "cuCoredumpGetAttribute", + "cuCoredumpGetAttributeGlobal", + "cuCoredumpSetAttribute", + "cuCoredumpSetAttributeGlobal", + "cuCtxAttach", + "cuCtxCreate", + "cuCtxCreate_v2", + "cuCtxCreate_v3", + "cuCtxCreate_v4", + "cuCtxDestroy", + "cuCtxDestroy_v2", + "cuCtxDetach", + "cuCtxDisablePeerAccess", + "cuCtxEnablePeerAccess", + "cuCtxFromGreenCtx", + "cuCtxGetApiVersion", + "cuCtxGetCacheConfig", + "cuCtxGetCurrent", + "cuCtxGetDevResource", + "cuCtxGetDevice", + "cuCtxGetExecAffinity", + "cuCtxGetFlags", + "cuCtxGetId", + "cuCtxGetLimit", + "cuCtxGetSharedMemConfig", + "cuCtxGetStreamPriorityRange", + "cuCtxPopCurrent", + "cuCtxPopCurrent_v2", + "cuCtxPushCurrent", + "cuCtxPushCurrent_v2", + "cuCtxRecordEvent", + "cuCtxResetPersistingL2Cache", + "cuCtxSetCacheConfig", + "cuCtxSetCurrent", + "cuCtxSetFlags", + "cuCtxSetLimit", + "cuCtxSetSharedMemConfig", + "cuCtxSynchronize", + "cuCtxWaitEvent", + "cuDestroyExternalMemory", + "cuDestroyExternalSemaphore", + "cuDevResourceGenerateDesc", + "cuDevSmResourceSplitByCount", + "cuDeviceCanAccessPeer", + "cuDeviceComputeCapability", + "cuDeviceGet", + "cuDeviceGetAttribute", + "cuDeviceGetByPCIBusId", + "cuDeviceGetCount", + "cuDeviceGetDefaultMemPool", + "cuDeviceGetDevResource", + "cuDeviceGetExecAffinitySupport", + "cuDeviceGetGraphMemAttribute", + "cuDeviceGetLuid", + "cuDeviceGetMemPool", + "cuDeviceGetName", + "cuDeviceGetNvSciSyncAttributes", + "cuDeviceGetP2PAttribute", + "cuDeviceGetPCIBusId", + "cuDeviceGetProperties", + "cuDeviceGetTexture1DLinearMaxWidth", + "cuDeviceGetUuid", + "cuDeviceGetUuid_v2", + "cuDeviceGraphMemTrim", + "cuDevicePrimaryCtxGetState", + "cuDevicePrimaryCtxRelease", + "cuDevicePrimaryCtxRelease_v2", + "cuDevicePrimaryCtxReset", + "cuDevicePrimaryCtxReset_v2", + "cuDevicePrimaryCtxRetain", + "cuDevicePrimaryCtxSetFlags", + "cuDevicePrimaryCtxSetFlags_v2", + "cuDeviceRegisterAsyncNotification", + "cuDeviceSetGraphMemAttribute", + "cuDeviceSetMemPool", + "cuDeviceTotalMem", + "cuDeviceTotalMem_v2", + "cuDeviceUnregisterAsyncNotification", + "cuDriverGetVersion", + "cuEGLApiInit", + "cuEGLStreamConsumerAcquireFrame", + "cuEGLStreamConsumerConnect", + "cuEGLStreamConsumerConnectWithFlags", + "cuEGLStreamConsumerDisconnect", + "cuEGLStreamConsumerReleaseFrame", + "cuEGLStreamProducerConnect", + "cuEGLStreamProducerDisconnect", + "cuEGLStreamProducerPresentFrame", + "cuEGLStreamProducerReturnFrame", + "cuEventCreate", + "cuEventDestroy", + "cuEventDestroy_v2", + "cuEventElapsedTime", + "cuEventElapsedTime_v2", + "cuEventQuery", + "cuEventRecord", + "cuEventRecordWithFlags", + "cuEventRecordWithFlags_ptsz", + "cuEventRecord_ptsz", + "cuEventSynchronize", + "cuExternalMemoryGetMappedBuffer", + "cuExternalMemoryGetMappedMipmappedArray", + "cuFlushGPUDirectRDMAWrites", + "cuFuncGetAttribute", + "cuFuncGetModule", + "cuFuncGetName", + "cuFuncGetParamInfo", + "cuFuncIsLoaded", + "cuFuncLoad", + "cuFuncSetAttribute", + "cuFuncSetBlockShape", + "cuFuncSetCacheConfig", + "cuFuncSetSharedMemConfig", + "cuFuncSetSharedSize", + "cuGLCtxCreate", + "cuGLCtxCreate_v2", + "cuGLGetDevices", + "cuGLGetDevices_v2", + "cuGLInit", + "cuGLMapBufferObject", + "cuGLMapBufferObjectAsync", + "cuGLMapBufferObjectAsync_v2", + "cuGLMapBufferObjectAsync_v2_ptsz", + "cuGLMapBufferObject_v2", + "cuGLMapBufferObject_v2_ptds", + "cuGLRegisterBufferObject", + "cuGLSetBufferObjectMapFlags", + "cuGLUnmapBufferObject", + "cuGLUnmapBufferObjectAsync", + "cuGLUnregisterBufferObject", + "cuGetErrorName", + "cuGetErrorString", + "cuGetExportTable", + "cuGetProcAddress", + "cuGetProcAddress_v2", + "cuGraphAddBatchMemOpNode", + "cuGraphAddChildGraphNode", + "cuGraphAddDependencies", + "cuGraphAddDependencies_v2", + "cuGraphAddEmptyNode", + "cuGraphAddEventRecordNode", + "cuGraphAddEventWaitNode", + "cuGraphAddExternalSemaphoresSignalNode", + "cuGraphAddExternalSemaphoresWaitNode", + "cuGraphAddHostNode", + "cuGraphAddKernelNode", + "cuGraphAddKernelNode_v2", + "cuGraphAddMemAllocNode", + "cuGraphAddMemFreeNode", + "cuGraphAddMemcpyNode", + "cuGraphAddMemsetNode", + "cuGraphAddNode", + "cuGraphAddNode_v2", + "cuGraphBatchMemOpNodeGetParams", + "cuGraphBatchMemOpNodeSetParams", + "cuGraphChildGraphNodeGetGraph", + "cuGraphClone", + "cuGraphConditionalHandleCreate", + "cuGraphCreate", + "cuGraphDebugDotPrint", + "cuGraphDestroy", + "cuGraphDestroyNode", + "cuGraphEventRecordNodeGetEvent", + "cuGraphEventRecordNodeSetEvent", + "cuGraphEventWaitNodeGetEvent", + "cuGraphEventWaitNodeSetEvent", + "cuGraphExecBatchMemOpNodeSetParams", + "cuGraphExecChildGraphNodeSetParams", + "cuGraphExecDestroy", + "cuGraphExecEventRecordNodeSetEvent", + "cuGraphExecEventWaitNodeSetEvent", + "cuGraphExecExternalSemaphoresSignalNodeSetParams", + "cuGraphExecExternalSemaphoresWaitNodeSetParams", + "cuGraphExecGetFlags", + "cuGraphExecHostNodeSetParams", + "cuGraphExecKernelNodeSetParams", + "cuGraphExecKernelNodeSetParams_v2", + "cuGraphExecMemcpyNodeSetParams", + "cuGraphExecMemsetNodeSetParams", + "cuGraphExecNodeSetParams", + "cuGraphExecUpdate", + "cuGraphExecUpdate_v2", + "cuGraphExternalSemaphoresSignalNodeGetParams", + "cuGraphExternalSemaphoresSignalNodeSetParams", + "cuGraphExternalSemaphoresWaitNodeGetParams", + "cuGraphExternalSemaphoresWaitNodeSetParams", + "cuGraphGetEdges", + "cuGraphGetEdges_v2", + "cuGraphGetNodes", + "cuGraphGetRootNodes", + "cuGraphHostNodeGetParams", + "cuGraphHostNodeSetParams", + "cuGraphInstantiate", + "cuGraphInstantiateWithFlags", + "cuGraphInstantiateWithParams", + "cuGraphInstantiateWithParams_ptsz", + "cuGraphInstantiate_v2", + "cuGraphKernelNodeCopyAttributes", + "cuGraphKernelNodeGetAttribute", + "cuGraphKernelNodeGetParams", + "cuGraphKernelNodeGetParams_v2", + "cuGraphKernelNodeSetAttribute", + "cuGraphKernelNodeSetParams", + "cuGraphKernelNodeSetParams_v2", + "cuGraphLaunch", + "cuGraphLaunch_ptsz", + "cuGraphMemAllocNodeGetParams", + "cuGraphMemFreeNodeGetParams", + "cuGraphMemcpyNodeGetParams", + "cuGraphMemcpyNodeSetParams", + "cuGraphMemsetNodeGetParams", + "cuGraphMemsetNodeSetParams", + "cuGraphNodeFindInClone", + "cuGraphNodeGetDependencies", + "cuGraphNodeGetDependencies_v2", + "cuGraphNodeGetDependentNodes", + "cuGraphNodeGetDependentNodes_v2", + "cuGraphNodeGetEnabled", + "cuGraphNodeGetType", + "cuGraphNodeSetEnabled", + "cuGraphNodeSetParams", + "cuGraphReleaseUserObject", + "cuGraphRemoveDependencies", + "cuGraphRemoveDependencies_v2", + "cuGraphRetainUserObject", + "cuGraphUpload", + "cuGraphUpload_ptsz", + "cuGraphicsEGLRegisterImage", + "cuGraphicsGLRegisterBuffer", + "cuGraphicsGLRegisterImage", + "cuGraphicsMapResources", + "cuGraphicsMapResources_ptsz", + "cuGraphicsResourceGetMappedEglFrame", + "cuGraphicsResourceGetMappedMipmappedArray", + "cuGraphicsResourceGetMappedPointer", + "cuGraphicsResourceGetMappedPointer_v2", + "cuGraphicsResourceSetMapFlags", + "cuGraphicsResourceSetMapFlags_v2", + "cuGraphicsSubResourceGetMappedArray", + "cuGraphicsUnmapResources", + "cuGraphicsUnmapResources_ptsz", + "cuGraphicsUnregisterResource", + "cuGraphicsVDPAURegisterOutputSurface", + "cuGraphicsVDPAURegisterVideoSurface", + "cuGreenCtxCreate", + "cuGreenCtxDestroy", + "cuGreenCtxGetDevResource", + "cuGreenCtxRecordEvent", + "cuGreenCtxStreamCreate", + "cuGreenCtxWaitEvent", + "cuImportExternalMemory", + "cuImportExternalSemaphore", + "cuInit", + "cuIpcCloseMemHandle", + "cuIpcGetEventHandle", + "cuIpcGetMemHandle", + "cuIpcOpenEventHandle", + "cuIpcOpenMemHandle", + "cuIpcOpenMemHandle_v2", + "cuKernelGetAttribute", + "cuKernelGetFunction", + "cuKernelGetLibrary", + "cuKernelGetName", + "cuKernelGetParamInfo", + "cuKernelSetAttribute", + "cuKernelSetCacheConfig", + "cuLaunch", + "cuLaunchCooperativeKernel", + "cuLaunchCooperativeKernelMultiDevice", + "cuLaunchCooperativeKernel_ptsz", + "cuLaunchGrid", + "cuLaunchGridAsync", + "cuLaunchHostFunc", + "cuLaunchHostFunc_ptsz", + "cuLaunchKernel", + "cuLaunchKernelEx", + "cuLaunchKernelEx_ptsz", + "cuLaunchKernel_ptsz", + "cuLibraryEnumerateKernels", + "cuLibraryGetGlobal", + "cuLibraryGetKernel", + "cuLibraryGetKernelCount", + "cuLibraryGetManaged", + "cuLibraryGetModule", + "cuLibraryGetUnifiedFunction", + "cuLibraryLoadData", + "cuLibraryLoadFromFile", + "cuLibraryUnload", + "cuLinkAddData", + "cuLinkAddData_v2", + "cuLinkAddFile", + "cuLinkAddFile_v2", + "cuLinkComplete", + "cuLinkCreate", + "cuLinkCreate_v2", + "cuLinkDestroy", + "cuMemAddressFree", + "cuMemAddressReserve", + "cuMemAdvise", + "cuMemAdvise_v2", + "cuMemAlloc", + "cuMemAllocAsync", + "cuMemAllocAsync_ptsz", + "cuMemAllocFromPoolAsync", + "cuMemAllocFromPoolAsync_ptsz", + "cuMemAllocHost", + "cuMemAllocHost_v2", + "cuMemAllocManaged", + "cuMemAllocPitch", + "cuMemAllocPitch_v2", + "cuMemAlloc_v2", + "cuMemBatchDecompressAsync", + "cuMemBatchDecompressAsync_ptsz", + "cuMemCreate", + "cuMemExportToShareableHandle", + "cuMemFree", + "cuMemFreeAsync", + "cuMemFreeAsync_ptsz", + "cuMemFreeHost", + "cuMemFree_v2", + "cuMemGetAccess", + "cuMemGetAddressRange", + "cuMemGetAddressRange_v2", + "cuMemGetAllocationGranularity", + "cuMemGetAllocationPropertiesFromHandle", + "cuMemGetAttribute", + "cuMemGetAttribute_v2", + "cuMemGetHandleForAddressRange", + "cuMemGetInfo", + "cuMemGetInfo_v2", + "cuMemHostAlloc", + "cuMemHostGetDevicePointer", + "cuMemHostGetDevicePointer_v2", + "cuMemHostGetFlags", + "cuMemHostRegister", + "cuMemHostRegister_v2", + "cuMemHostUnregister", + "cuMemImportFromShareableHandle", + "cuMemMap", + "cuMemMapArrayAsync", + "cuMemMapArrayAsync_ptsz", + "cuMemPoolCreate", + "cuMemPoolDestroy", + "cuMemPoolExportPointer", + "cuMemPoolExportToShareableHandle", + "cuMemPoolGetAccess", + "cuMemPoolGetAttribute", + "cuMemPoolImportFromShareableHandle", + "cuMemPoolImportPointer", + "cuMemPoolSetAccess", + "cuMemPoolSetAttribute", + "cuMemPoolTrimTo", + "cuMemPrefetchAsync", + "cuMemPrefetchAsync_ptsz", + "cuMemPrefetchAsync_v2", + "cuMemPrefetchAsync_v2_ptsz", + "cuMemRangeGetAttribute", + "cuMemRangeGetAttributes", + "cuMemRelease", + "cuMemRetainAllocationHandle", + "cuMemSetAccess", + "cuMemUnmap", + "cuMemcpy", + "cuMemcpy2D", + "cuMemcpy2DAsync", + "cuMemcpy2DAsync_v2", + "cuMemcpy2DAsync_v2_ptsz", + "cuMemcpy2DUnaligned", + "cuMemcpy2DUnaligned_v2", + "cuMemcpy2DUnaligned_v2_ptds", + "cuMemcpy2D_v2", + "cuMemcpy2D_v2_ptds", + "cuMemcpy3D", + "cuMemcpy3DAsync", + "cuMemcpy3DAsync_v2", + "cuMemcpy3DAsync_v2_ptsz", + "cuMemcpy3DBatchAsync", + "cuMemcpy3DBatchAsync_ptsz", + "cuMemcpy3DPeer", + "cuMemcpy3DPeerAsync", + "cuMemcpy3DPeerAsync_ptsz", + "cuMemcpy3DPeer_ptds", + "cuMemcpy3D_v2", + "cuMemcpy3D_v2_ptds", + "cuMemcpyAsync", + "cuMemcpyAsync_ptsz", + "cuMemcpyAtoA", + "cuMemcpyAtoA_v2", + "cuMemcpyAtoA_v2_ptds", + "cuMemcpyAtoD", + "cuMemcpyAtoD_v2", + "cuMemcpyAtoD_v2_ptds", + "cuMemcpyAtoH", + "cuMemcpyAtoHAsync", + "cuMemcpyAtoHAsync_v2", + "cuMemcpyAtoHAsync_v2_ptsz", + "cuMemcpyAtoH_v2", + "cuMemcpyAtoH_v2_ptds", + "cuMemcpyBatchAsync", + "cuMemcpyBatchAsync_ptsz", + "cuMemcpyDtoA", + "cuMemcpyDtoA_v2", + "cuMemcpyDtoA_v2_ptds", + "cuMemcpyDtoD", + "cuMemcpyDtoDAsync", + "cuMemcpyDtoDAsync_v2", + "cuMemcpyDtoDAsync_v2_ptsz", + "cuMemcpyDtoD_v2", + "cuMemcpyDtoD_v2_ptds", + "cuMemcpyDtoH", + "cuMemcpyDtoHAsync", + "cuMemcpyDtoHAsync_v2", + "cuMemcpyDtoHAsync_v2_ptsz", + "cuMemcpyDtoH_v2", + "cuMemcpyDtoH_v2_ptds", + "cuMemcpyHtoA", + "cuMemcpyHtoAAsync", + "cuMemcpyHtoAAsync_v2", + "cuMemcpyHtoAAsync_v2_ptsz", + "cuMemcpyHtoA_v2", + "cuMemcpyHtoA_v2_ptds", + "cuMemcpyHtoD", + "cuMemcpyHtoDAsync", + "cuMemcpyHtoDAsync_v2", + "cuMemcpyHtoDAsync_v2_ptsz", + "cuMemcpyHtoD_v2", + "cuMemcpyHtoD_v2_ptds", + "cuMemcpyPeer", + "cuMemcpyPeerAsync", + "cuMemcpyPeerAsync_ptsz", + "cuMemcpyPeer_ptds", + "cuMemcpy_ptds", + "cuMemsetD16", + "cuMemsetD16Async", + "cuMemsetD16Async_ptsz", + "cuMemsetD16_v2", + "cuMemsetD16_v2_ptds", + "cuMemsetD2D16", + "cuMemsetD2D16Async", + "cuMemsetD2D16Async_ptsz", + "cuMemsetD2D16_v2", + "cuMemsetD2D16_v2_ptds", + "cuMemsetD2D32", + "cuMemsetD2D32Async", + "cuMemsetD2D32Async_ptsz", + "cuMemsetD2D32_v2", + "cuMemsetD2D32_v2_ptds", + "cuMemsetD2D8", + "cuMemsetD2D8Async", + "cuMemsetD2D8Async_ptsz", + "cuMemsetD2D8_v2", + "cuMemsetD2D8_v2_ptds", + "cuMemsetD32", + "cuMemsetD32Async", + "cuMemsetD32Async_ptsz", + "cuMemsetD32_v2", + "cuMemsetD32_v2_ptds", + "cuMemsetD8", + "cuMemsetD8Async", + "cuMemsetD8Async_ptsz", + "cuMemsetD8_v2", + "cuMemsetD8_v2_ptds", + "cuMipmappedArrayCreate", + "cuMipmappedArrayDestroy", + "cuMipmappedArrayGetLevel", + "cuMipmappedArrayGetMemoryRequirements", + "cuMipmappedArrayGetSparseProperties", + "cuModuleEnumerateFunctions", + "cuModuleGetFunction", + "cuModuleGetFunctionCount", + "cuModuleGetGlobal", + "cuModuleGetGlobal_v2", + "cuModuleGetLoadingMode", + "cuModuleGetSurfRef", + "cuModuleGetTexRef", + "cuModuleLoad", + "cuModuleLoadData", + "cuModuleLoadDataEx", + "cuModuleLoadFatBinary", + "cuModuleUnload", + "cuMulticastAddDevice", + "cuMulticastBindAddr", + "cuMulticastBindMem", + "cuMulticastCreate", + "cuMulticastGetGranularity", + "cuMulticastUnbind", + "cuOccupancyAvailableDynamicSMemPerBlock", + "cuOccupancyMaxActiveBlocksPerMultiprocessor", + "cuOccupancyMaxActiveBlocksPerMultiprocessorWithFlags", + "cuOccupancyMaxActiveClusters", + "cuOccupancyMaxPotentialBlockSize", + "cuOccupancyMaxPotentialBlockSizeWithFlags", + "cuOccupancyMaxPotentialClusterSize", + "cuParamSetSize", + "cuParamSetTexRef", + "cuParamSetf", + "cuParamSeti", + "cuParamSetv", + "cuPointerGetAttribute", + "cuPointerGetAttributes", + "cuPointerSetAttribute", + "cuProfilerInitialize", + "cuProfilerStart", + "cuProfilerStop", + "cuSignalExternalSemaphoresAsync", + "cuSignalExternalSemaphoresAsync_ptsz", + "cuStreamAddCallback", + "cuStreamAddCallback_ptsz", + "cuStreamAttachMemAsync", + "cuStreamAttachMemAsync_ptsz", + "cuStreamBatchMemOp", + "cuStreamBatchMemOp_ptsz", + "cuStreamBatchMemOp_v2", + "cuStreamBatchMemOp_v2_ptsz", + "cuStreamBeginCapture", + "cuStreamBeginCaptureToGraph", + "cuStreamBeginCaptureToGraph_ptsz", + "cuStreamBeginCapture_ptsz", + "cuStreamBeginCapture_v2", + "cuStreamBeginCapture_v2_ptsz", + "cuStreamCopyAttributes", + "cuStreamCopyAttributes_ptsz", + "cuStreamCreate", + "cuStreamCreateWithPriority", + "cuStreamDestroy", + "cuStreamDestroy_v2", + "cuStreamEndCapture", + "cuStreamEndCapture_ptsz", + "cuStreamGetAttribute", + "cuStreamGetAttribute_ptsz", + "cuStreamGetCaptureInfo", + "cuStreamGetCaptureInfo_ptsz", + "cuStreamGetCaptureInfo_v2", + "cuStreamGetCaptureInfo_v2_ptsz", + "cuStreamGetCaptureInfo_v3", + "cuStreamGetCaptureInfo_v3_ptsz", + "cuStreamGetCtx", + "cuStreamGetCtx_ptsz", + "cuStreamGetCtx_v2", + "cuStreamGetCtx_v2_ptsz", + "cuStreamGetDevice", + "cuStreamGetDevice_ptsz", + "cuStreamGetFlags", + "cuStreamGetFlags_ptsz", + "cuStreamGetGreenCtx", + "cuStreamGetId", + "cuStreamGetId_ptsz", + "cuStreamGetPriority", + "cuStreamGetPriority_ptsz", + "cuStreamIsCapturing", + "cuStreamIsCapturing_ptsz", + "cuStreamQuery", + "cuStreamQuery_ptsz", + "cuStreamSetAttribute", + "cuStreamSetAttribute_ptsz", + "cuStreamSynchronize", + "cuStreamSynchronize_ptsz", + "cuStreamUpdateCaptureDependencies", + "cuStreamUpdateCaptureDependencies_ptsz", + "cuStreamUpdateCaptureDependencies_v2", + "cuStreamUpdateCaptureDependencies_v2_ptsz", + "cuStreamWaitEvent", + "cuStreamWaitEvent_ptsz", + "cuStreamWaitValue32", + "cuStreamWaitValue32_ptsz", + "cuStreamWaitValue32_v2", + "cuStreamWaitValue32_v2_ptsz", + "cuStreamWaitValue64", + "cuStreamWaitValue64_ptsz", + "cuStreamWaitValue64_v2", + "cuStreamWaitValue64_v2_ptsz", + "cuStreamWriteValue32", + "cuStreamWriteValue32_ptsz", + "cuStreamWriteValue32_v2", + "cuStreamWriteValue32_v2_ptsz", + "cuStreamWriteValue64", + "cuStreamWriteValue64_ptsz", + "cuStreamWriteValue64_v2", + "cuStreamWriteValue64_v2_ptsz", + "cuSurfObjectCreate", + "cuSurfObjectDestroy", + "cuSurfObjectGetResourceDesc", + "cuSurfRefGetArray", + "cuSurfRefSetArray", + "cuTensorMapEncodeIm2col", + "cuTensorMapEncodeIm2colWide", + "cuTensorMapEncodeTiled", + "cuTensorMapReplaceAddress", + "cuTexObjectCreate", + "cuTexObjectDestroy", + "cuTexObjectGetResourceDesc", + "cuTexObjectGetResourceViewDesc", + "cuTexObjectGetTextureDesc", + "cuTexRefCreate", + "cuTexRefDestroy", + "cuTexRefGetAddress", + "cuTexRefGetAddressMode", + "cuTexRefGetAddress_v2", + "cuTexRefGetArray", + "cuTexRefGetBorderColor", + "cuTexRefGetFilterMode", + "cuTexRefGetFlags", + "cuTexRefGetFormat", + "cuTexRefGetMaxAnisotropy", + "cuTexRefGetMipmapFilterMode", + "cuTexRefGetMipmapLevelBias", + "cuTexRefGetMipmapLevelClamp", + "cuTexRefGetMipmappedArray", + "cuTexRefSetAddress", + "cuTexRefSetAddress2D", + "cuTexRefSetAddress2D_v2", + "cuTexRefSetAddress2D_v3", + "cuTexRefSetAddressMode", + "cuTexRefSetAddress_v2", + "cuTexRefSetArray", + "cuTexRefSetBorderColor", + "cuTexRefSetFilterMode", + "cuTexRefSetFlags", + "cuTexRefSetFormat", + "cuTexRefSetMaxAnisotropy", + "cuTexRefSetMipmapFilterMode", + "cuTexRefSetMipmapLevelBias", + "cuTexRefSetMipmapLevelClamp", + "cuTexRefSetMipmappedArray", + "cuThreadExchangeStreamCaptureMode", + "cuUserObjectCreate", + "cuUserObjectRelease", + "cuUserObjectRetain", + "cuVDPAUCtxCreate", + "cuVDPAUCtxCreate_v2", + "cuVDPAUGetDevice", + "cuWaitExternalSemaphoresAsync", + "cuWaitExternalSemaphoresAsync_ptsz", + "cudbgApiAttach", + "cudbgApiDetach", + "cudbgApiInit", + "cudbgGetAPI", + "cudbgGetAPIVersion", + "cudbgMain", + "cudbgReportDriverApiError", + "cudbgReportDriverInternalError", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libcuda_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libcuda_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libcuda_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libcuda_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libcuda_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libcuda_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libcuda_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libcuda_so_tramp_table, 0, SYM_COUNT * sizeof(_libcuda_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/aarch64-linux-gnu/libcuda.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/aarch64-linux-gnu/libcuda.so.tramp.S new file mode 100644 index 0000000..6596d74 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/aarch64-linux-gnu/libcuda.so.tramp.S @@ -0,0 +1,27100 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#define lr x30 +#define ip0 x16 + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libcuda_so_tramp_table + .hidden _libcuda_so_tramp_table + .align 8 +_libcuda_so_tramp_table: + .zero 5280 + + .text + + .globl _libcuda_so_tramp_resolve + .hidden _libcuda_so_tramp_resolve + + .globl _libcuda_so_save_regs_and_resolve + .hidden _libcuda_so_save_regs_and_resolve + .type _libcuda_so_save_regs_and_resolve, %function +_libcuda_so_save_regs_and_resolve: + .cfi_startproc + + // Slow path which calls dlsym, taken only on first call. + // Registers are saved according to "Procedure Call Standard for the Arm® 64-bit Architecture". + // For DWARF directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + // Stack is aligned at 16 bytes + +#define PUSH_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 8 +#define POP_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore reg2; .cfi_restore reg1 + +#define PUSH_WIDE_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-32]!; .cfi_adjust_cfa_offset 32; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 16 +#define POP_WIDE_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #32; .cfi_adjust_cfa_offset -32; .cfi_restore reg2; .cfi_restore reg1 + + // Save only arguments (and lr) + PUSH_PAIR(x0, x1) + PUSH_PAIR(x2, x3) + PUSH_PAIR(x4, x5) + PUSH_PAIR(x6, x7) + PUSH_PAIR(x8, lr) + + ldr x0, [sp, #80] // 16*5 + + PUSH_WIDE_PAIR(q0, q1) + PUSH_WIDE_PAIR(q2, q3) + PUSH_WIDE_PAIR(q4, q5) + PUSH_WIDE_PAIR(q6, q7) + + // Stack is aligned at 16 bytes + + bl _libcuda_so_tramp_resolve + mov ip0, x0 + + // TODO: pop pc? + + POP_WIDE_PAIR(q6, q7) + POP_WIDE_PAIR(q4, q5) + POP_WIDE_PAIR(q2, q3) + POP_WIDE_PAIR(q0, q1) + + POP_PAIR(x8, lr) + POP_PAIR(x6, x7) + POP_PAIR(x4, x5) + POP_PAIR(x2, x3) + POP_PAIR(x0, x1) + + br lr + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArray3DCreate + .p2align 4 + .type cuArray3DCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArray3DCreate +#endif +cuArray3DCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+0 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+0] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 0 & 0xffff +#if 0 > 0xffff + movk ip0, 0 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArray3DCreate_v2 + .p2align 4 + .type cuArray3DCreate_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArray3DCreate_v2 +#endif +cuArray3DCreate_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+8 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+8] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1 & 0xffff +#if 1 > 0xffff + movk ip0, 1 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArray3DGetDescriptor + .p2align 4 + .type cuArray3DGetDescriptor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArray3DGetDescriptor +#endif +cuArray3DGetDescriptor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+16 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+16] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 2 & 0xffff +#if 2 > 0xffff + movk ip0, 2 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArray3DGetDescriptor_v2 + .p2align 4 + .type cuArray3DGetDescriptor_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArray3DGetDescriptor_v2 +#endif +cuArray3DGetDescriptor_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+24 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+24] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 3 & 0xffff +#if 3 > 0xffff + movk ip0, 3 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArrayCreate + .p2align 4 + .type cuArrayCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArrayCreate +#endif +cuArrayCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+32 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+32] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 4 & 0xffff +#if 4 > 0xffff + movk ip0, 4 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArrayCreate_v2 + .p2align 4 + .type cuArrayCreate_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArrayCreate_v2 +#endif +cuArrayCreate_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+40 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+40] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 5 & 0xffff +#if 5 > 0xffff + movk ip0, 5 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArrayDestroy + .p2align 4 + .type cuArrayDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArrayDestroy +#endif +cuArrayDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+48 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+48] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 6 & 0xffff +#if 6 > 0xffff + movk ip0, 6 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArrayGetDescriptor + .p2align 4 + .type cuArrayGetDescriptor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArrayGetDescriptor +#endif +cuArrayGetDescriptor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+56 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+56] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 7 & 0xffff +#if 7 > 0xffff + movk ip0, 7 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArrayGetDescriptor_v2 + .p2align 4 + .type cuArrayGetDescriptor_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArrayGetDescriptor_v2 +#endif +cuArrayGetDescriptor_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+64 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+64] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 8 & 0xffff +#if 8 > 0xffff + movk ip0, 8 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArrayGetMemoryRequirements + .p2align 4 + .type cuArrayGetMemoryRequirements, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArrayGetMemoryRequirements +#endif +cuArrayGetMemoryRequirements: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+72 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+72] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 9 & 0xffff +#if 9 > 0xffff + movk ip0, 9 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArrayGetPlane + .p2align 4 + .type cuArrayGetPlane, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArrayGetPlane +#endif +cuArrayGetPlane: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+80 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+80] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 10 & 0xffff +#if 10 > 0xffff + movk ip0, 10 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArrayGetSparseProperties + .p2align 4 + .type cuArrayGetSparseProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArrayGetSparseProperties +#endif +cuArrayGetSparseProperties: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+88 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+88] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 11 & 0xffff +#if 11 > 0xffff + movk ip0, 11 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCheckpointProcessCheckpoint + .p2align 4 + .type cuCheckpointProcessCheckpoint, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCheckpointProcessCheckpoint +#endif +cuCheckpointProcessCheckpoint: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+96 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+96] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 12 & 0xffff +#if 12 > 0xffff + movk ip0, 12 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCheckpointProcessGetRestoreThreadId + .p2align 4 + .type cuCheckpointProcessGetRestoreThreadId, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCheckpointProcessGetRestoreThreadId +#endif +cuCheckpointProcessGetRestoreThreadId: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+104 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 13 & 0xffff +#if 13 > 0xffff + movk ip0, 13 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCheckpointProcessGetState + .p2align 4 + .type cuCheckpointProcessGetState, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCheckpointProcessGetState +#endif +cuCheckpointProcessGetState: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+112 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 14 & 0xffff +#if 14 > 0xffff + movk ip0, 14 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCheckpointProcessLock + .p2align 4 + .type cuCheckpointProcessLock, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCheckpointProcessLock +#endif +cuCheckpointProcessLock: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+120 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 15 & 0xffff +#if 15 > 0xffff + movk ip0, 15 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCheckpointProcessRestore + .p2align 4 + .type cuCheckpointProcessRestore, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCheckpointProcessRestore +#endif +cuCheckpointProcessRestore: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+128 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 16 & 0xffff +#if 16 > 0xffff + movk ip0, 16 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCheckpointProcessUnlock + .p2align 4 + .type cuCheckpointProcessUnlock, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCheckpointProcessUnlock +#endif +cuCheckpointProcessUnlock: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+136 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 17 & 0xffff +#if 17 > 0xffff + movk ip0, 17 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCoredumpGetAttribute + .p2align 4 + .type cuCoredumpGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCoredumpGetAttribute +#endif +cuCoredumpGetAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+144 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 18 & 0xffff +#if 18 > 0xffff + movk ip0, 18 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCoredumpGetAttributeGlobal + .p2align 4 + .type cuCoredumpGetAttributeGlobal, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCoredumpGetAttributeGlobal +#endif +cuCoredumpGetAttributeGlobal: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+152 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 19 & 0xffff +#if 19 > 0xffff + movk ip0, 19 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCoredumpSetAttribute + .p2align 4 + .type cuCoredumpSetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCoredumpSetAttribute +#endif +cuCoredumpSetAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+160 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 20 & 0xffff +#if 20 > 0xffff + movk ip0, 20 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCoredumpSetAttributeGlobal + .p2align 4 + .type cuCoredumpSetAttributeGlobal, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCoredumpSetAttributeGlobal +#endif +cuCoredumpSetAttributeGlobal: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+168 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 21 & 0xffff +#if 21 > 0xffff + movk ip0, 21 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxAttach + .p2align 4 + .type cuCtxAttach, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxAttach +#endif +cuCtxAttach: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+176 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 22 & 0xffff +#if 22 > 0xffff + movk ip0, 22 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxCreate + .p2align 4 + .type cuCtxCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxCreate +#endif +cuCtxCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+184 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 23 & 0xffff +#if 23 > 0xffff + movk ip0, 23 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxCreate_v2 + .p2align 4 + .type cuCtxCreate_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxCreate_v2 +#endif +cuCtxCreate_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+192 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 24 & 0xffff +#if 24 > 0xffff + movk ip0, 24 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxCreate_v3 + .p2align 4 + .type cuCtxCreate_v3, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxCreate_v3 +#endif +cuCtxCreate_v3: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+200 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 25 & 0xffff +#if 25 > 0xffff + movk ip0, 25 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxCreate_v4 + .p2align 4 + .type cuCtxCreate_v4, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxCreate_v4 +#endif +cuCtxCreate_v4: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+208 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 26 & 0xffff +#if 26 > 0xffff + movk ip0, 26 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxDestroy + .p2align 4 + .type cuCtxDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxDestroy +#endif +cuCtxDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+216 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 27 & 0xffff +#if 27 > 0xffff + movk ip0, 27 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxDestroy_v2 + .p2align 4 + .type cuCtxDestroy_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxDestroy_v2 +#endif +cuCtxDestroy_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+224 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 28 & 0xffff +#if 28 > 0xffff + movk ip0, 28 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxDetach + .p2align 4 + .type cuCtxDetach, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxDetach +#endif +cuCtxDetach: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+232 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 29 & 0xffff +#if 29 > 0xffff + movk ip0, 29 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxDisablePeerAccess + .p2align 4 + .type cuCtxDisablePeerAccess, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxDisablePeerAccess +#endif +cuCtxDisablePeerAccess: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+240 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 30 & 0xffff +#if 30 > 0xffff + movk ip0, 30 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxEnablePeerAccess + .p2align 4 + .type cuCtxEnablePeerAccess, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxEnablePeerAccess +#endif +cuCtxEnablePeerAccess: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+248 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 31 & 0xffff +#if 31 > 0xffff + movk ip0, 31 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxFromGreenCtx + .p2align 4 + .type cuCtxFromGreenCtx, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxFromGreenCtx +#endif +cuCtxFromGreenCtx: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+256 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 32 & 0xffff +#if 32 > 0xffff + movk ip0, 32 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetApiVersion + .p2align 4 + .type cuCtxGetApiVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetApiVersion +#endif +cuCtxGetApiVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+264 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 33 & 0xffff +#if 33 > 0xffff + movk ip0, 33 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetCacheConfig + .p2align 4 + .type cuCtxGetCacheConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetCacheConfig +#endif +cuCtxGetCacheConfig: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+272 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 34 & 0xffff +#if 34 > 0xffff + movk ip0, 34 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetCurrent + .p2align 4 + .type cuCtxGetCurrent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetCurrent +#endif +cuCtxGetCurrent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+280 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 35 & 0xffff +#if 35 > 0xffff + movk ip0, 35 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetDevResource + .p2align 4 + .type cuCtxGetDevResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetDevResource +#endif +cuCtxGetDevResource: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+288 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 36 & 0xffff +#if 36 > 0xffff + movk ip0, 36 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetDevice + .p2align 4 + .type cuCtxGetDevice, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetDevice +#endif +cuCtxGetDevice: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+296 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 37 & 0xffff +#if 37 > 0xffff + movk ip0, 37 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetExecAffinity + .p2align 4 + .type cuCtxGetExecAffinity, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetExecAffinity +#endif +cuCtxGetExecAffinity: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+304 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 38 & 0xffff +#if 38 > 0xffff + movk ip0, 38 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetFlags + .p2align 4 + .type cuCtxGetFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetFlags +#endif +cuCtxGetFlags: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+312 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 39 & 0xffff +#if 39 > 0xffff + movk ip0, 39 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetId + .p2align 4 + .type cuCtxGetId, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetId +#endif +cuCtxGetId: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+320 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 40 & 0xffff +#if 40 > 0xffff + movk ip0, 40 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetLimit + .p2align 4 + .type cuCtxGetLimit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetLimit +#endif +cuCtxGetLimit: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+328 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 41 & 0xffff +#if 41 > 0xffff + movk ip0, 41 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetSharedMemConfig + .p2align 4 + .type cuCtxGetSharedMemConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetSharedMemConfig +#endif +cuCtxGetSharedMemConfig: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+336 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 42 & 0xffff +#if 42 > 0xffff + movk ip0, 42 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetStreamPriorityRange + .p2align 4 + .type cuCtxGetStreamPriorityRange, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetStreamPriorityRange +#endif +cuCtxGetStreamPriorityRange: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+344 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 43 & 0xffff +#if 43 > 0xffff + movk ip0, 43 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxPopCurrent + .p2align 4 + .type cuCtxPopCurrent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxPopCurrent +#endif +cuCtxPopCurrent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+352 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 44 & 0xffff +#if 44 > 0xffff + movk ip0, 44 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxPopCurrent_v2 + .p2align 4 + .type cuCtxPopCurrent_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxPopCurrent_v2 +#endif +cuCtxPopCurrent_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+360 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 45 & 0xffff +#if 45 > 0xffff + movk ip0, 45 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxPushCurrent + .p2align 4 + .type cuCtxPushCurrent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxPushCurrent +#endif +cuCtxPushCurrent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+368 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 46 & 0xffff +#if 46 > 0xffff + movk ip0, 46 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxPushCurrent_v2 + .p2align 4 + .type cuCtxPushCurrent_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxPushCurrent_v2 +#endif +cuCtxPushCurrent_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+376 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 47 & 0xffff +#if 47 > 0xffff + movk ip0, 47 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxRecordEvent + .p2align 4 + .type cuCtxRecordEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxRecordEvent +#endif +cuCtxRecordEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+384 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 48 & 0xffff +#if 48 > 0xffff + movk ip0, 48 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxResetPersistingL2Cache + .p2align 4 + .type cuCtxResetPersistingL2Cache, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxResetPersistingL2Cache +#endif +cuCtxResetPersistingL2Cache: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+392 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 49 & 0xffff +#if 49 > 0xffff + movk ip0, 49 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxSetCacheConfig + .p2align 4 + .type cuCtxSetCacheConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxSetCacheConfig +#endif +cuCtxSetCacheConfig: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+400 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 50 & 0xffff +#if 50 > 0xffff + movk ip0, 50 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxSetCurrent + .p2align 4 + .type cuCtxSetCurrent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxSetCurrent +#endif +cuCtxSetCurrent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+408 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 51 & 0xffff +#if 51 > 0xffff + movk ip0, 51 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxSetFlags + .p2align 4 + .type cuCtxSetFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxSetFlags +#endif +cuCtxSetFlags: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+416 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 52 & 0xffff +#if 52 > 0xffff + movk ip0, 52 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxSetLimit + .p2align 4 + .type cuCtxSetLimit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxSetLimit +#endif +cuCtxSetLimit: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+424 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 53 & 0xffff +#if 53 > 0xffff + movk ip0, 53 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxSetSharedMemConfig + .p2align 4 + .type cuCtxSetSharedMemConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxSetSharedMemConfig +#endif +cuCtxSetSharedMemConfig: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+432 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 54 & 0xffff +#if 54 > 0xffff + movk ip0, 54 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxSynchronize + .p2align 4 + .type cuCtxSynchronize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxSynchronize +#endif +cuCtxSynchronize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+440 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 55 & 0xffff +#if 55 > 0xffff + movk ip0, 55 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxWaitEvent + .p2align 4 + .type cuCtxWaitEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxWaitEvent +#endif +cuCtxWaitEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+448 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 56 & 0xffff +#if 56 > 0xffff + movk ip0, 56 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDestroyExternalMemory + .p2align 4 + .type cuDestroyExternalMemory, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDestroyExternalMemory +#endif +cuDestroyExternalMemory: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+456 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+456] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 57 & 0xffff +#if 57 > 0xffff + movk ip0, 57 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDestroyExternalSemaphore + .p2align 4 + .type cuDestroyExternalSemaphore, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDestroyExternalSemaphore +#endif +cuDestroyExternalSemaphore: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+464 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+464] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 58 & 0xffff +#if 58 > 0xffff + movk ip0, 58 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevResourceGenerateDesc + .p2align 4 + .type cuDevResourceGenerateDesc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevResourceGenerateDesc +#endif +cuDevResourceGenerateDesc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+472 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+472] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 59 & 0xffff +#if 59 > 0xffff + movk ip0, 59 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevSmResourceSplitByCount + .p2align 4 + .type cuDevSmResourceSplitByCount, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevSmResourceSplitByCount +#endif +cuDevSmResourceSplitByCount: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+480 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+480] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 60 & 0xffff +#if 60 > 0xffff + movk ip0, 60 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceCanAccessPeer + .p2align 4 + .type cuDeviceCanAccessPeer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceCanAccessPeer +#endif +cuDeviceCanAccessPeer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+488 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+488] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 61 & 0xffff +#if 61 > 0xffff + movk ip0, 61 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceComputeCapability + .p2align 4 + .type cuDeviceComputeCapability, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceComputeCapability +#endif +cuDeviceComputeCapability: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+496 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+496] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 62 & 0xffff +#if 62 > 0xffff + movk ip0, 62 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGet + .p2align 4 + .type cuDeviceGet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGet +#endif +cuDeviceGet: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+504 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+504] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 63 & 0xffff +#if 63 > 0xffff + movk ip0, 63 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetAttribute + .p2align 4 + .type cuDeviceGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetAttribute +#endif +cuDeviceGetAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+512 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+512] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 64 & 0xffff +#if 64 > 0xffff + movk ip0, 64 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetByPCIBusId + .p2align 4 + .type cuDeviceGetByPCIBusId, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetByPCIBusId +#endif +cuDeviceGetByPCIBusId: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+520 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+520] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 65 & 0xffff +#if 65 > 0xffff + movk ip0, 65 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetCount + .p2align 4 + .type cuDeviceGetCount, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetCount +#endif +cuDeviceGetCount: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+528 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+528] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 66 & 0xffff +#if 66 > 0xffff + movk ip0, 66 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetDefaultMemPool + .p2align 4 + .type cuDeviceGetDefaultMemPool, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetDefaultMemPool +#endif +cuDeviceGetDefaultMemPool: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+536 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+536] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 67 & 0xffff +#if 67 > 0xffff + movk ip0, 67 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetDevResource + .p2align 4 + .type cuDeviceGetDevResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetDevResource +#endif +cuDeviceGetDevResource: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+544 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+544] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 68 & 0xffff +#if 68 > 0xffff + movk ip0, 68 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetExecAffinitySupport + .p2align 4 + .type cuDeviceGetExecAffinitySupport, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetExecAffinitySupport +#endif +cuDeviceGetExecAffinitySupport: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+552 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+552] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 69 & 0xffff +#if 69 > 0xffff + movk ip0, 69 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetGraphMemAttribute + .p2align 4 + .type cuDeviceGetGraphMemAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetGraphMemAttribute +#endif +cuDeviceGetGraphMemAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+560 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+560] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 70 & 0xffff +#if 70 > 0xffff + movk ip0, 70 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetLuid + .p2align 4 + .type cuDeviceGetLuid, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetLuid +#endif +cuDeviceGetLuid: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+568 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+568] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 71 & 0xffff +#if 71 > 0xffff + movk ip0, 71 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetMemPool + .p2align 4 + .type cuDeviceGetMemPool, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetMemPool +#endif +cuDeviceGetMemPool: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+576 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+576] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 72 & 0xffff +#if 72 > 0xffff + movk ip0, 72 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetName + .p2align 4 + .type cuDeviceGetName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetName +#endif +cuDeviceGetName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+584 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+584] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 73 & 0xffff +#if 73 > 0xffff + movk ip0, 73 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetNvSciSyncAttributes + .p2align 4 + .type cuDeviceGetNvSciSyncAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetNvSciSyncAttributes +#endif +cuDeviceGetNvSciSyncAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+592 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+592] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 74 & 0xffff +#if 74 > 0xffff + movk ip0, 74 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetP2PAttribute + .p2align 4 + .type cuDeviceGetP2PAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetP2PAttribute +#endif +cuDeviceGetP2PAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+600 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+600] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 75 & 0xffff +#if 75 > 0xffff + movk ip0, 75 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetPCIBusId + .p2align 4 + .type cuDeviceGetPCIBusId, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetPCIBusId +#endif +cuDeviceGetPCIBusId: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+608 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+608] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 76 & 0xffff +#if 76 > 0xffff + movk ip0, 76 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetProperties + .p2align 4 + .type cuDeviceGetProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetProperties +#endif +cuDeviceGetProperties: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+616 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+616] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 77 & 0xffff +#if 77 > 0xffff + movk ip0, 77 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetTexture1DLinearMaxWidth + .p2align 4 + .type cuDeviceGetTexture1DLinearMaxWidth, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetTexture1DLinearMaxWidth +#endif +cuDeviceGetTexture1DLinearMaxWidth: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+624 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+624] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 78 & 0xffff +#if 78 > 0xffff + movk ip0, 78 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetUuid + .p2align 4 + .type cuDeviceGetUuid, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetUuid +#endif +cuDeviceGetUuid: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+632 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+632] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 79 & 0xffff +#if 79 > 0xffff + movk ip0, 79 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetUuid_v2 + .p2align 4 + .type cuDeviceGetUuid_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetUuid_v2 +#endif +cuDeviceGetUuid_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+640 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+640] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 80 & 0xffff +#if 80 > 0xffff + movk ip0, 80 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGraphMemTrim + .p2align 4 + .type cuDeviceGraphMemTrim, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGraphMemTrim +#endif +cuDeviceGraphMemTrim: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+648 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+648] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 81 & 0xffff +#if 81 > 0xffff + movk ip0, 81 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevicePrimaryCtxGetState + .p2align 4 + .type cuDevicePrimaryCtxGetState, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevicePrimaryCtxGetState +#endif +cuDevicePrimaryCtxGetState: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+656 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+656] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 82 & 0xffff +#if 82 > 0xffff + movk ip0, 82 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevicePrimaryCtxRelease + .p2align 4 + .type cuDevicePrimaryCtxRelease, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevicePrimaryCtxRelease +#endif +cuDevicePrimaryCtxRelease: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+664 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+664] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 83 & 0xffff +#if 83 > 0xffff + movk ip0, 83 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevicePrimaryCtxRelease_v2 + .p2align 4 + .type cuDevicePrimaryCtxRelease_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevicePrimaryCtxRelease_v2 +#endif +cuDevicePrimaryCtxRelease_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+672 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+672] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 84 & 0xffff +#if 84 > 0xffff + movk ip0, 84 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevicePrimaryCtxReset + .p2align 4 + .type cuDevicePrimaryCtxReset, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevicePrimaryCtxReset +#endif +cuDevicePrimaryCtxReset: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+680 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+680] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 85 & 0xffff +#if 85 > 0xffff + movk ip0, 85 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevicePrimaryCtxReset_v2 + .p2align 4 + .type cuDevicePrimaryCtxReset_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevicePrimaryCtxReset_v2 +#endif +cuDevicePrimaryCtxReset_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+688 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+688] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 86 & 0xffff +#if 86 > 0xffff + movk ip0, 86 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevicePrimaryCtxRetain + .p2align 4 + .type cuDevicePrimaryCtxRetain, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevicePrimaryCtxRetain +#endif +cuDevicePrimaryCtxRetain: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+696 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+696] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 87 & 0xffff +#if 87 > 0xffff + movk ip0, 87 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevicePrimaryCtxSetFlags + .p2align 4 + .type cuDevicePrimaryCtxSetFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevicePrimaryCtxSetFlags +#endif +cuDevicePrimaryCtxSetFlags: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+704 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+704] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 88 & 0xffff +#if 88 > 0xffff + movk ip0, 88 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevicePrimaryCtxSetFlags_v2 + .p2align 4 + .type cuDevicePrimaryCtxSetFlags_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevicePrimaryCtxSetFlags_v2 +#endif +cuDevicePrimaryCtxSetFlags_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+712 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+712] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 89 & 0xffff +#if 89 > 0xffff + movk ip0, 89 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceRegisterAsyncNotification + .p2align 4 + .type cuDeviceRegisterAsyncNotification, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceRegisterAsyncNotification +#endif +cuDeviceRegisterAsyncNotification: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+720 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+720] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 90 & 0xffff +#if 90 > 0xffff + movk ip0, 90 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceSetGraphMemAttribute + .p2align 4 + .type cuDeviceSetGraphMemAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceSetGraphMemAttribute +#endif +cuDeviceSetGraphMemAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+728 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+728] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 91 & 0xffff +#if 91 > 0xffff + movk ip0, 91 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceSetMemPool + .p2align 4 + .type cuDeviceSetMemPool, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceSetMemPool +#endif +cuDeviceSetMemPool: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+736 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+736] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 92 & 0xffff +#if 92 > 0xffff + movk ip0, 92 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceTotalMem + .p2align 4 + .type cuDeviceTotalMem, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceTotalMem +#endif +cuDeviceTotalMem: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+744 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+744] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 93 & 0xffff +#if 93 > 0xffff + movk ip0, 93 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceTotalMem_v2 + .p2align 4 + .type cuDeviceTotalMem_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceTotalMem_v2 +#endif +cuDeviceTotalMem_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+752 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+752] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 94 & 0xffff +#if 94 > 0xffff + movk ip0, 94 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceUnregisterAsyncNotification + .p2align 4 + .type cuDeviceUnregisterAsyncNotification, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceUnregisterAsyncNotification +#endif +cuDeviceUnregisterAsyncNotification: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+760 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+760] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 95 & 0xffff +#if 95 > 0xffff + movk ip0, 95 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDriverGetVersion + .p2align 4 + .type cuDriverGetVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDriverGetVersion +#endif +cuDriverGetVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+768 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+768] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 96 & 0xffff +#if 96 > 0xffff + movk ip0, 96 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLApiInit + .p2align 4 + .type cuEGLApiInit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLApiInit +#endif +cuEGLApiInit: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+776 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+776] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 97 & 0xffff +#if 97 > 0xffff + movk ip0, 97 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLStreamConsumerAcquireFrame + .p2align 4 + .type cuEGLStreamConsumerAcquireFrame, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLStreamConsumerAcquireFrame +#endif +cuEGLStreamConsumerAcquireFrame: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+784 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+784] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 98 & 0xffff +#if 98 > 0xffff + movk ip0, 98 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLStreamConsumerConnect + .p2align 4 + .type cuEGLStreamConsumerConnect, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLStreamConsumerConnect +#endif +cuEGLStreamConsumerConnect: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+792 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+792] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 99 & 0xffff +#if 99 > 0xffff + movk ip0, 99 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLStreamConsumerConnectWithFlags + .p2align 4 + .type cuEGLStreamConsumerConnectWithFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLStreamConsumerConnectWithFlags +#endif +cuEGLStreamConsumerConnectWithFlags: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+800 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+800] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 100 & 0xffff +#if 100 > 0xffff + movk ip0, 100 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLStreamConsumerDisconnect + .p2align 4 + .type cuEGLStreamConsumerDisconnect, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLStreamConsumerDisconnect +#endif +cuEGLStreamConsumerDisconnect: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+808 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+808] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 101 & 0xffff +#if 101 > 0xffff + movk ip0, 101 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLStreamConsumerReleaseFrame + .p2align 4 + .type cuEGLStreamConsumerReleaseFrame, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLStreamConsumerReleaseFrame +#endif +cuEGLStreamConsumerReleaseFrame: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+816 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+816] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 102 & 0xffff +#if 102 > 0xffff + movk ip0, 102 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLStreamProducerConnect + .p2align 4 + .type cuEGLStreamProducerConnect, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLStreamProducerConnect +#endif +cuEGLStreamProducerConnect: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+824 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+824] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 103 & 0xffff +#if 103 > 0xffff + movk ip0, 103 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLStreamProducerDisconnect + .p2align 4 + .type cuEGLStreamProducerDisconnect, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLStreamProducerDisconnect +#endif +cuEGLStreamProducerDisconnect: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+832 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+832] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 104 & 0xffff +#if 104 > 0xffff + movk ip0, 104 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLStreamProducerPresentFrame + .p2align 4 + .type cuEGLStreamProducerPresentFrame, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLStreamProducerPresentFrame +#endif +cuEGLStreamProducerPresentFrame: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+840 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+840] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 105 & 0xffff +#if 105 > 0xffff + movk ip0, 105 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLStreamProducerReturnFrame + .p2align 4 + .type cuEGLStreamProducerReturnFrame, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLStreamProducerReturnFrame +#endif +cuEGLStreamProducerReturnFrame: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+848 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+848] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 106 & 0xffff +#if 106 > 0xffff + movk ip0, 106 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventCreate + .p2align 4 + .type cuEventCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventCreate +#endif +cuEventCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+856 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+856] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 107 & 0xffff +#if 107 > 0xffff + movk ip0, 107 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventDestroy + .p2align 4 + .type cuEventDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventDestroy +#endif +cuEventDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+864 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+864] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 108 & 0xffff +#if 108 > 0xffff + movk ip0, 108 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventDestroy_v2 + .p2align 4 + .type cuEventDestroy_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventDestroy_v2 +#endif +cuEventDestroy_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+872 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+872] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 109 & 0xffff +#if 109 > 0xffff + movk ip0, 109 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventElapsedTime + .p2align 4 + .type cuEventElapsedTime, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventElapsedTime +#endif +cuEventElapsedTime: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+880 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+880] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 110 & 0xffff +#if 110 > 0xffff + movk ip0, 110 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventElapsedTime_v2 + .p2align 4 + .type cuEventElapsedTime_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventElapsedTime_v2 +#endif +cuEventElapsedTime_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+888 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+888] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 111 & 0xffff +#if 111 > 0xffff + movk ip0, 111 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventQuery + .p2align 4 + .type cuEventQuery, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventQuery +#endif +cuEventQuery: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+896 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+896] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 112 & 0xffff +#if 112 > 0xffff + movk ip0, 112 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventRecord + .p2align 4 + .type cuEventRecord, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventRecord +#endif +cuEventRecord: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+904 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+904] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 113 & 0xffff +#if 113 > 0xffff + movk ip0, 113 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventRecordWithFlags + .p2align 4 + .type cuEventRecordWithFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventRecordWithFlags +#endif +cuEventRecordWithFlags: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+912 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+912] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 114 & 0xffff +#if 114 > 0xffff + movk ip0, 114 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventRecordWithFlags_ptsz + .p2align 4 + .type cuEventRecordWithFlags_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventRecordWithFlags_ptsz +#endif +cuEventRecordWithFlags_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+920 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+920] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 115 & 0xffff +#if 115 > 0xffff + movk ip0, 115 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventRecord_ptsz + .p2align 4 + .type cuEventRecord_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventRecord_ptsz +#endif +cuEventRecord_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+928 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+928] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 116 & 0xffff +#if 116 > 0xffff + movk ip0, 116 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventSynchronize + .p2align 4 + .type cuEventSynchronize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventSynchronize +#endif +cuEventSynchronize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+936 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+936] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 117 & 0xffff +#if 117 > 0xffff + movk ip0, 117 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuExternalMemoryGetMappedBuffer + .p2align 4 + .type cuExternalMemoryGetMappedBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuExternalMemoryGetMappedBuffer +#endif +cuExternalMemoryGetMappedBuffer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+944 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+944] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 118 & 0xffff +#if 118 > 0xffff + movk ip0, 118 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuExternalMemoryGetMappedMipmappedArray + .p2align 4 + .type cuExternalMemoryGetMappedMipmappedArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuExternalMemoryGetMappedMipmappedArray +#endif +cuExternalMemoryGetMappedMipmappedArray: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+952 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+952] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 119 & 0xffff +#if 119 > 0xffff + movk ip0, 119 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFlushGPUDirectRDMAWrites + .p2align 4 + .type cuFlushGPUDirectRDMAWrites, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFlushGPUDirectRDMAWrites +#endif +cuFlushGPUDirectRDMAWrites: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+960 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+960] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 120 & 0xffff +#if 120 > 0xffff + movk ip0, 120 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncGetAttribute + .p2align 4 + .type cuFuncGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncGetAttribute +#endif +cuFuncGetAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+968 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+968] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 121 & 0xffff +#if 121 > 0xffff + movk ip0, 121 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncGetModule + .p2align 4 + .type cuFuncGetModule, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncGetModule +#endif +cuFuncGetModule: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+976 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+976] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 122 & 0xffff +#if 122 > 0xffff + movk ip0, 122 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncGetName + .p2align 4 + .type cuFuncGetName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncGetName +#endif +cuFuncGetName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+984 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+984] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 123 & 0xffff +#if 123 > 0xffff + movk ip0, 123 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncGetParamInfo + .p2align 4 + .type cuFuncGetParamInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncGetParamInfo +#endif +cuFuncGetParamInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+992 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+992] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 124 & 0xffff +#if 124 > 0xffff + movk ip0, 124 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncIsLoaded + .p2align 4 + .type cuFuncIsLoaded, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncIsLoaded +#endif +cuFuncIsLoaded: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1000 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1000] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 125 & 0xffff +#if 125 > 0xffff + movk ip0, 125 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncLoad + .p2align 4 + .type cuFuncLoad, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncLoad +#endif +cuFuncLoad: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1008 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1008] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 126 & 0xffff +#if 126 > 0xffff + movk ip0, 126 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncSetAttribute + .p2align 4 + .type cuFuncSetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncSetAttribute +#endif +cuFuncSetAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1016 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1016] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 127 & 0xffff +#if 127 > 0xffff + movk ip0, 127 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncSetBlockShape + .p2align 4 + .type cuFuncSetBlockShape, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncSetBlockShape +#endif +cuFuncSetBlockShape: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1024 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1024] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 128 & 0xffff +#if 128 > 0xffff + movk ip0, 128 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncSetCacheConfig + .p2align 4 + .type cuFuncSetCacheConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncSetCacheConfig +#endif +cuFuncSetCacheConfig: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1032 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1032] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 129 & 0xffff +#if 129 > 0xffff + movk ip0, 129 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncSetSharedMemConfig + .p2align 4 + .type cuFuncSetSharedMemConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncSetSharedMemConfig +#endif +cuFuncSetSharedMemConfig: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1040 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1040] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 130 & 0xffff +#if 130 > 0xffff + movk ip0, 130 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncSetSharedSize + .p2align 4 + .type cuFuncSetSharedSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncSetSharedSize +#endif +cuFuncSetSharedSize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1048 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1048] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 131 & 0xffff +#if 131 > 0xffff + movk ip0, 131 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLCtxCreate + .p2align 4 + .type cuGLCtxCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLCtxCreate +#endif +cuGLCtxCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1056 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1056] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 132 & 0xffff +#if 132 > 0xffff + movk ip0, 132 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLCtxCreate_v2 + .p2align 4 + .type cuGLCtxCreate_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLCtxCreate_v2 +#endif +cuGLCtxCreate_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1064 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1064] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 133 & 0xffff +#if 133 > 0xffff + movk ip0, 133 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLGetDevices + .p2align 4 + .type cuGLGetDevices, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLGetDevices +#endif +cuGLGetDevices: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1072 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1072] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 134 & 0xffff +#if 134 > 0xffff + movk ip0, 134 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLGetDevices_v2 + .p2align 4 + .type cuGLGetDevices_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLGetDevices_v2 +#endif +cuGLGetDevices_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1080 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1080] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 135 & 0xffff +#if 135 > 0xffff + movk ip0, 135 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLInit + .p2align 4 + .type cuGLInit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLInit +#endif +cuGLInit: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1088 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1088] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 136 & 0xffff +#if 136 > 0xffff + movk ip0, 136 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLMapBufferObject + .p2align 4 + .type cuGLMapBufferObject, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLMapBufferObject +#endif +cuGLMapBufferObject: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1096 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1096] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 137 & 0xffff +#if 137 > 0xffff + movk ip0, 137 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLMapBufferObjectAsync + .p2align 4 + .type cuGLMapBufferObjectAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLMapBufferObjectAsync +#endif +cuGLMapBufferObjectAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1104 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 138 & 0xffff +#if 138 > 0xffff + movk ip0, 138 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLMapBufferObjectAsync_v2 + .p2align 4 + .type cuGLMapBufferObjectAsync_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLMapBufferObjectAsync_v2 +#endif +cuGLMapBufferObjectAsync_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1112 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 139 & 0xffff +#if 139 > 0xffff + movk ip0, 139 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLMapBufferObjectAsync_v2_ptsz + .p2align 4 + .type cuGLMapBufferObjectAsync_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLMapBufferObjectAsync_v2_ptsz +#endif +cuGLMapBufferObjectAsync_v2_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1120 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 140 & 0xffff +#if 140 > 0xffff + movk ip0, 140 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLMapBufferObject_v2 + .p2align 4 + .type cuGLMapBufferObject_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLMapBufferObject_v2 +#endif +cuGLMapBufferObject_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1128 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 141 & 0xffff +#if 141 > 0xffff + movk ip0, 141 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLMapBufferObject_v2_ptds + .p2align 4 + .type cuGLMapBufferObject_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLMapBufferObject_v2_ptds +#endif +cuGLMapBufferObject_v2_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1136 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 142 & 0xffff +#if 142 > 0xffff + movk ip0, 142 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLRegisterBufferObject + .p2align 4 + .type cuGLRegisterBufferObject, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLRegisterBufferObject +#endif +cuGLRegisterBufferObject: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1144 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 143 & 0xffff +#if 143 > 0xffff + movk ip0, 143 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLSetBufferObjectMapFlags + .p2align 4 + .type cuGLSetBufferObjectMapFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLSetBufferObjectMapFlags +#endif +cuGLSetBufferObjectMapFlags: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1152 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 144 & 0xffff +#if 144 > 0xffff + movk ip0, 144 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLUnmapBufferObject + .p2align 4 + .type cuGLUnmapBufferObject, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLUnmapBufferObject +#endif +cuGLUnmapBufferObject: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1160 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 145 & 0xffff +#if 145 > 0xffff + movk ip0, 145 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLUnmapBufferObjectAsync + .p2align 4 + .type cuGLUnmapBufferObjectAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLUnmapBufferObjectAsync +#endif +cuGLUnmapBufferObjectAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1168 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 146 & 0xffff +#if 146 > 0xffff + movk ip0, 146 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLUnregisterBufferObject + .p2align 4 + .type cuGLUnregisterBufferObject, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLUnregisterBufferObject +#endif +cuGLUnregisterBufferObject: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1176 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 147 & 0xffff +#if 147 > 0xffff + movk ip0, 147 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGetErrorName + .p2align 4 + .type cuGetErrorName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGetErrorName +#endif +cuGetErrorName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1184 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 148 & 0xffff +#if 148 > 0xffff + movk ip0, 148 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGetErrorString + .p2align 4 + .type cuGetErrorString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGetErrorString +#endif +cuGetErrorString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1192 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 149 & 0xffff +#if 149 > 0xffff + movk ip0, 149 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGetExportTable + .p2align 4 + .type cuGetExportTable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGetExportTable +#endif +cuGetExportTable: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1200 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 150 & 0xffff +#if 150 > 0xffff + movk ip0, 150 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGetProcAddress + .p2align 4 + .type cuGetProcAddress, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGetProcAddress +#endif +cuGetProcAddress: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1208 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 151 & 0xffff +#if 151 > 0xffff + movk ip0, 151 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGetProcAddress_v2 + .p2align 4 + .type cuGetProcAddress_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGetProcAddress_v2 +#endif +cuGetProcAddress_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1216 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 152 & 0xffff +#if 152 > 0xffff + movk ip0, 152 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddBatchMemOpNode + .p2align 4 + .type cuGraphAddBatchMemOpNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddBatchMemOpNode +#endif +cuGraphAddBatchMemOpNode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1224 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 153 & 0xffff +#if 153 > 0xffff + movk ip0, 153 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddChildGraphNode + .p2align 4 + .type cuGraphAddChildGraphNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddChildGraphNode +#endif +cuGraphAddChildGraphNode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1232 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 154 & 0xffff +#if 154 > 0xffff + movk ip0, 154 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddDependencies + .p2align 4 + .type cuGraphAddDependencies, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddDependencies +#endif +cuGraphAddDependencies: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1240 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 155 & 0xffff +#if 155 > 0xffff + movk ip0, 155 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddDependencies_v2 + .p2align 4 + .type cuGraphAddDependencies_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddDependencies_v2 +#endif +cuGraphAddDependencies_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1248 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 156 & 0xffff +#if 156 > 0xffff + movk ip0, 156 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddEmptyNode + .p2align 4 + .type cuGraphAddEmptyNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddEmptyNode +#endif +cuGraphAddEmptyNode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1256 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 157 & 0xffff +#if 157 > 0xffff + movk ip0, 157 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddEventRecordNode + .p2align 4 + .type cuGraphAddEventRecordNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddEventRecordNode +#endif +cuGraphAddEventRecordNode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1264 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 158 & 0xffff +#if 158 > 0xffff + movk ip0, 158 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddEventWaitNode + .p2align 4 + .type cuGraphAddEventWaitNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddEventWaitNode +#endif +cuGraphAddEventWaitNode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1272 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 159 & 0xffff +#if 159 > 0xffff + movk ip0, 159 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddExternalSemaphoresSignalNode + .p2align 4 + .type cuGraphAddExternalSemaphoresSignalNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddExternalSemaphoresSignalNode +#endif +cuGraphAddExternalSemaphoresSignalNode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1280 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 160 & 0xffff +#if 160 > 0xffff + movk ip0, 160 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddExternalSemaphoresWaitNode + .p2align 4 + .type cuGraphAddExternalSemaphoresWaitNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddExternalSemaphoresWaitNode +#endif +cuGraphAddExternalSemaphoresWaitNode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1288 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 161 & 0xffff +#if 161 > 0xffff + movk ip0, 161 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddHostNode + .p2align 4 + .type cuGraphAddHostNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddHostNode +#endif +cuGraphAddHostNode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1296 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 162 & 0xffff +#if 162 > 0xffff + movk ip0, 162 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddKernelNode + .p2align 4 + .type cuGraphAddKernelNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddKernelNode +#endif +cuGraphAddKernelNode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1304 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 163 & 0xffff +#if 163 > 0xffff + movk ip0, 163 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddKernelNode_v2 + .p2align 4 + .type cuGraphAddKernelNode_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddKernelNode_v2 +#endif +cuGraphAddKernelNode_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1312 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 164 & 0xffff +#if 164 > 0xffff + movk ip0, 164 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddMemAllocNode + .p2align 4 + .type cuGraphAddMemAllocNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddMemAllocNode +#endif +cuGraphAddMemAllocNode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1320 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 165 & 0xffff +#if 165 > 0xffff + movk ip0, 165 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddMemFreeNode + .p2align 4 + .type cuGraphAddMemFreeNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddMemFreeNode +#endif +cuGraphAddMemFreeNode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1328 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 166 & 0xffff +#if 166 > 0xffff + movk ip0, 166 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddMemcpyNode + .p2align 4 + .type cuGraphAddMemcpyNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddMemcpyNode +#endif +cuGraphAddMemcpyNode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1336 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 167 & 0xffff +#if 167 > 0xffff + movk ip0, 167 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddMemsetNode + .p2align 4 + .type cuGraphAddMemsetNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddMemsetNode +#endif +cuGraphAddMemsetNode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1344 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 168 & 0xffff +#if 168 > 0xffff + movk ip0, 168 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddNode + .p2align 4 + .type cuGraphAddNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddNode +#endif +cuGraphAddNode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1352 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 169 & 0xffff +#if 169 > 0xffff + movk ip0, 169 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddNode_v2 + .p2align 4 + .type cuGraphAddNode_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddNode_v2 +#endif +cuGraphAddNode_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1360 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 170 & 0xffff +#if 170 > 0xffff + movk ip0, 170 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphBatchMemOpNodeGetParams + .p2align 4 + .type cuGraphBatchMemOpNodeGetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphBatchMemOpNodeGetParams +#endif +cuGraphBatchMemOpNodeGetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1368 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 171 & 0xffff +#if 171 > 0xffff + movk ip0, 171 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphBatchMemOpNodeSetParams + .p2align 4 + .type cuGraphBatchMemOpNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphBatchMemOpNodeSetParams +#endif +cuGraphBatchMemOpNodeSetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1376 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 172 & 0xffff +#if 172 > 0xffff + movk ip0, 172 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphChildGraphNodeGetGraph + .p2align 4 + .type cuGraphChildGraphNodeGetGraph, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphChildGraphNodeGetGraph +#endif +cuGraphChildGraphNodeGetGraph: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1384 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 173 & 0xffff +#if 173 > 0xffff + movk ip0, 173 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphClone + .p2align 4 + .type cuGraphClone, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphClone +#endif +cuGraphClone: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1392 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 174 & 0xffff +#if 174 > 0xffff + movk ip0, 174 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphConditionalHandleCreate + .p2align 4 + .type cuGraphConditionalHandleCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphConditionalHandleCreate +#endif +cuGraphConditionalHandleCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1400 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 175 & 0xffff +#if 175 > 0xffff + movk ip0, 175 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphCreate + .p2align 4 + .type cuGraphCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphCreate +#endif +cuGraphCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1408 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 176 & 0xffff +#if 176 > 0xffff + movk ip0, 176 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphDebugDotPrint + .p2align 4 + .type cuGraphDebugDotPrint, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphDebugDotPrint +#endif +cuGraphDebugDotPrint: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1416 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 177 & 0xffff +#if 177 > 0xffff + movk ip0, 177 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphDestroy + .p2align 4 + .type cuGraphDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphDestroy +#endif +cuGraphDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1424 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 178 & 0xffff +#if 178 > 0xffff + movk ip0, 178 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphDestroyNode + .p2align 4 + .type cuGraphDestroyNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphDestroyNode +#endif +cuGraphDestroyNode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1432 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 179 & 0xffff +#if 179 > 0xffff + movk ip0, 179 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphEventRecordNodeGetEvent + .p2align 4 + .type cuGraphEventRecordNodeGetEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphEventRecordNodeGetEvent +#endif +cuGraphEventRecordNodeGetEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1440 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 180 & 0xffff +#if 180 > 0xffff + movk ip0, 180 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphEventRecordNodeSetEvent + .p2align 4 + .type cuGraphEventRecordNodeSetEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphEventRecordNodeSetEvent +#endif +cuGraphEventRecordNodeSetEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1448 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 181 & 0xffff +#if 181 > 0xffff + movk ip0, 181 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphEventWaitNodeGetEvent + .p2align 4 + .type cuGraphEventWaitNodeGetEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphEventWaitNodeGetEvent +#endif +cuGraphEventWaitNodeGetEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1456 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1456] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 182 & 0xffff +#if 182 > 0xffff + movk ip0, 182 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphEventWaitNodeSetEvent + .p2align 4 + .type cuGraphEventWaitNodeSetEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphEventWaitNodeSetEvent +#endif +cuGraphEventWaitNodeSetEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1464 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1464] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 183 & 0xffff +#if 183 > 0xffff + movk ip0, 183 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecBatchMemOpNodeSetParams + .p2align 4 + .type cuGraphExecBatchMemOpNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecBatchMemOpNodeSetParams +#endif +cuGraphExecBatchMemOpNodeSetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1472 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1472] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 184 & 0xffff +#if 184 > 0xffff + movk ip0, 184 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecChildGraphNodeSetParams + .p2align 4 + .type cuGraphExecChildGraphNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecChildGraphNodeSetParams +#endif +cuGraphExecChildGraphNodeSetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1480 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1480] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 185 & 0xffff +#if 185 > 0xffff + movk ip0, 185 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecDestroy + .p2align 4 + .type cuGraphExecDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecDestroy +#endif +cuGraphExecDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1488 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1488] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 186 & 0xffff +#if 186 > 0xffff + movk ip0, 186 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecEventRecordNodeSetEvent + .p2align 4 + .type cuGraphExecEventRecordNodeSetEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecEventRecordNodeSetEvent +#endif +cuGraphExecEventRecordNodeSetEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1496 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1496] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 187 & 0xffff +#if 187 > 0xffff + movk ip0, 187 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecEventWaitNodeSetEvent + .p2align 4 + .type cuGraphExecEventWaitNodeSetEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecEventWaitNodeSetEvent +#endif +cuGraphExecEventWaitNodeSetEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1504 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1504] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 188 & 0xffff +#if 188 > 0xffff + movk ip0, 188 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecExternalSemaphoresSignalNodeSetParams + .p2align 4 + .type cuGraphExecExternalSemaphoresSignalNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecExternalSemaphoresSignalNodeSetParams +#endif +cuGraphExecExternalSemaphoresSignalNodeSetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1512 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1512] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 189 & 0xffff +#if 189 > 0xffff + movk ip0, 189 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecExternalSemaphoresWaitNodeSetParams + .p2align 4 + .type cuGraphExecExternalSemaphoresWaitNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecExternalSemaphoresWaitNodeSetParams +#endif +cuGraphExecExternalSemaphoresWaitNodeSetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1520 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1520] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 190 & 0xffff +#if 190 > 0xffff + movk ip0, 190 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecGetFlags + .p2align 4 + .type cuGraphExecGetFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecGetFlags +#endif +cuGraphExecGetFlags: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1528 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1528] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 191 & 0xffff +#if 191 > 0xffff + movk ip0, 191 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecHostNodeSetParams + .p2align 4 + .type cuGraphExecHostNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecHostNodeSetParams +#endif +cuGraphExecHostNodeSetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1536 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1536] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 192 & 0xffff +#if 192 > 0xffff + movk ip0, 192 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecKernelNodeSetParams + .p2align 4 + .type cuGraphExecKernelNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecKernelNodeSetParams +#endif +cuGraphExecKernelNodeSetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1544 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1544] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 193 & 0xffff +#if 193 > 0xffff + movk ip0, 193 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecKernelNodeSetParams_v2 + .p2align 4 + .type cuGraphExecKernelNodeSetParams_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecKernelNodeSetParams_v2 +#endif +cuGraphExecKernelNodeSetParams_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1552 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1552] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 194 & 0xffff +#if 194 > 0xffff + movk ip0, 194 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecMemcpyNodeSetParams + .p2align 4 + .type cuGraphExecMemcpyNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecMemcpyNodeSetParams +#endif +cuGraphExecMemcpyNodeSetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1560 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1560] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 195 & 0xffff +#if 195 > 0xffff + movk ip0, 195 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecMemsetNodeSetParams + .p2align 4 + .type cuGraphExecMemsetNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecMemsetNodeSetParams +#endif +cuGraphExecMemsetNodeSetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1568 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1568] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 196 & 0xffff +#if 196 > 0xffff + movk ip0, 196 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecNodeSetParams + .p2align 4 + .type cuGraphExecNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecNodeSetParams +#endif +cuGraphExecNodeSetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1576 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1576] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 197 & 0xffff +#if 197 > 0xffff + movk ip0, 197 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecUpdate + .p2align 4 + .type cuGraphExecUpdate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecUpdate +#endif +cuGraphExecUpdate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1584 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1584] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 198 & 0xffff +#if 198 > 0xffff + movk ip0, 198 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecUpdate_v2 + .p2align 4 + .type cuGraphExecUpdate_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecUpdate_v2 +#endif +cuGraphExecUpdate_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1592 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1592] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 199 & 0xffff +#if 199 > 0xffff + movk ip0, 199 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExternalSemaphoresSignalNodeGetParams + .p2align 4 + .type cuGraphExternalSemaphoresSignalNodeGetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExternalSemaphoresSignalNodeGetParams +#endif +cuGraphExternalSemaphoresSignalNodeGetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1600 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1600] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 200 & 0xffff +#if 200 > 0xffff + movk ip0, 200 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExternalSemaphoresSignalNodeSetParams + .p2align 4 + .type cuGraphExternalSemaphoresSignalNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExternalSemaphoresSignalNodeSetParams +#endif +cuGraphExternalSemaphoresSignalNodeSetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1608 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1608] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 201 & 0xffff +#if 201 > 0xffff + movk ip0, 201 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExternalSemaphoresWaitNodeGetParams + .p2align 4 + .type cuGraphExternalSemaphoresWaitNodeGetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExternalSemaphoresWaitNodeGetParams +#endif +cuGraphExternalSemaphoresWaitNodeGetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1616 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1616] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 202 & 0xffff +#if 202 > 0xffff + movk ip0, 202 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExternalSemaphoresWaitNodeSetParams + .p2align 4 + .type cuGraphExternalSemaphoresWaitNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExternalSemaphoresWaitNodeSetParams +#endif +cuGraphExternalSemaphoresWaitNodeSetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1624 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1624] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 203 & 0xffff +#if 203 > 0xffff + movk ip0, 203 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphGetEdges + .p2align 4 + .type cuGraphGetEdges, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphGetEdges +#endif +cuGraphGetEdges: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1632 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1632] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 204 & 0xffff +#if 204 > 0xffff + movk ip0, 204 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphGetEdges_v2 + .p2align 4 + .type cuGraphGetEdges_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphGetEdges_v2 +#endif +cuGraphGetEdges_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1640 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1640] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 205 & 0xffff +#if 205 > 0xffff + movk ip0, 205 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphGetNodes + .p2align 4 + .type cuGraphGetNodes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphGetNodes +#endif +cuGraphGetNodes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1648 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1648] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 206 & 0xffff +#if 206 > 0xffff + movk ip0, 206 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphGetRootNodes + .p2align 4 + .type cuGraphGetRootNodes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphGetRootNodes +#endif +cuGraphGetRootNodes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1656 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1656] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 207 & 0xffff +#if 207 > 0xffff + movk ip0, 207 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphHostNodeGetParams + .p2align 4 + .type cuGraphHostNodeGetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphHostNodeGetParams +#endif +cuGraphHostNodeGetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1664 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1664] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 208 & 0xffff +#if 208 > 0xffff + movk ip0, 208 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphHostNodeSetParams + .p2align 4 + .type cuGraphHostNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphHostNodeSetParams +#endif +cuGraphHostNodeSetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1672 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1672] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 209 & 0xffff +#if 209 > 0xffff + movk ip0, 209 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphInstantiate + .p2align 4 + .type cuGraphInstantiate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphInstantiate +#endif +cuGraphInstantiate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1680 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1680] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 210 & 0xffff +#if 210 > 0xffff + movk ip0, 210 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphInstantiateWithFlags + .p2align 4 + .type cuGraphInstantiateWithFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphInstantiateWithFlags +#endif +cuGraphInstantiateWithFlags: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1688 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1688] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 211 & 0xffff +#if 211 > 0xffff + movk ip0, 211 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphInstantiateWithParams + .p2align 4 + .type cuGraphInstantiateWithParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphInstantiateWithParams +#endif +cuGraphInstantiateWithParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1696 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1696] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 212 & 0xffff +#if 212 > 0xffff + movk ip0, 212 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphInstantiateWithParams_ptsz + .p2align 4 + .type cuGraphInstantiateWithParams_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphInstantiateWithParams_ptsz +#endif +cuGraphInstantiateWithParams_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1704 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1704] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 213 & 0xffff +#if 213 > 0xffff + movk ip0, 213 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphInstantiate_v2 + .p2align 4 + .type cuGraphInstantiate_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphInstantiate_v2 +#endif +cuGraphInstantiate_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1712 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1712] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 214 & 0xffff +#if 214 > 0xffff + movk ip0, 214 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphKernelNodeCopyAttributes + .p2align 4 + .type cuGraphKernelNodeCopyAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphKernelNodeCopyAttributes +#endif +cuGraphKernelNodeCopyAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1720 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1720] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 215 & 0xffff +#if 215 > 0xffff + movk ip0, 215 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphKernelNodeGetAttribute + .p2align 4 + .type cuGraphKernelNodeGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphKernelNodeGetAttribute +#endif +cuGraphKernelNodeGetAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1728 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1728] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 216 & 0xffff +#if 216 > 0xffff + movk ip0, 216 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphKernelNodeGetParams + .p2align 4 + .type cuGraphKernelNodeGetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphKernelNodeGetParams +#endif +cuGraphKernelNodeGetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1736 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1736] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 217 & 0xffff +#if 217 > 0xffff + movk ip0, 217 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphKernelNodeGetParams_v2 + .p2align 4 + .type cuGraphKernelNodeGetParams_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphKernelNodeGetParams_v2 +#endif +cuGraphKernelNodeGetParams_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1744 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1744] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 218 & 0xffff +#if 218 > 0xffff + movk ip0, 218 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphKernelNodeSetAttribute + .p2align 4 + .type cuGraphKernelNodeSetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphKernelNodeSetAttribute +#endif +cuGraphKernelNodeSetAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1752 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1752] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 219 & 0xffff +#if 219 > 0xffff + movk ip0, 219 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphKernelNodeSetParams + .p2align 4 + .type cuGraphKernelNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphKernelNodeSetParams +#endif +cuGraphKernelNodeSetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1760 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1760] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 220 & 0xffff +#if 220 > 0xffff + movk ip0, 220 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphKernelNodeSetParams_v2 + .p2align 4 + .type cuGraphKernelNodeSetParams_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphKernelNodeSetParams_v2 +#endif +cuGraphKernelNodeSetParams_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1768 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1768] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 221 & 0xffff +#if 221 > 0xffff + movk ip0, 221 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphLaunch + .p2align 4 + .type cuGraphLaunch, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphLaunch +#endif +cuGraphLaunch: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1776 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1776] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 222 & 0xffff +#if 222 > 0xffff + movk ip0, 222 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphLaunch_ptsz + .p2align 4 + .type cuGraphLaunch_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphLaunch_ptsz +#endif +cuGraphLaunch_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1784 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1784] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 223 & 0xffff +#if 223 > 0xffff + movk ip0, 223 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphMemAllocNodeGetParams + .p2align 4 + .type cuGraphMemAllocNodeGetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphMemAllocNodeGetParams +#endif +cuGraphMemAllocNodeGetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1792 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1792] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 224 & 0xffff +#if 224 > 0xffff + movk ip0, 224 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphMemFreeNodeGetParams + .p2align 4 + .type cuGraphMemFreeNodeGetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphMemFreeNodeGetParams +#endif +cuGraphMemFreeNodeGetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1800 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1800] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 225 & 0xffff +#if 225 > 0xffff + movk ip0, 225 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphMemcpyNodeGetParams + .p2align 4 + .type cuGraphMemcpyNodeGetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphMemcpyNodeGetParams +#endif +cuGraphMemcpyNodeGetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1808 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1808] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 226 & 0xffff +#if 226 > 0xffff + movk ip0, 226 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphMemcpyNodeSetParams + .p2align 4 + .type cuGraphMemcpyNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphMemcpyNodeSetParams +#endif +cuGraphMemcpyNodeSetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1816 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1816] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 227 & 0xffff +#if 227 > 0xffff + movk ip0, 227 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphMemsetNodeGetParams + .p2align 4 + .type cuGraphMemsetNodeGetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphMemsetNodeGetParams +#endif +cuGraphMemsetNodeGetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1824 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1824] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 228 & 0xffff +#if 228 > 0xffff + movk ip0, 228 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphMemsetNodeSetParams + .p2align 4 + .type cuGraphMemsetNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphMemsetNodeSetParams +#endif +cuGraphMemsetNodeSetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1832 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1832] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 229 & 0xffff +#if 229 > 0xffff + movk ip0, 229 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphNodeFindInClone + .p2align 4 + .type cuGraphNodeFindInClone, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphNodeFindInClone +#endif +cuGraphNodeFindInClone: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1840 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1840] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 230 & 0xffff +#if 230 > 0xffff + movk ip0, 230 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphNodeGetDependencies + .p2align 4 + .type cuGraphNodeGetDependencies, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphNodeGetDependencies +#endif +cuGraphNodeGetDependencies: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1848 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1848] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 231 & 0xffff +#if 231 > 0xffff + movk ip0, 231 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphNodeGetDependencies_v2 + .p2align 4 + .type cuGraphNodeGetDependencies_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphNodeGetDependencies_v2 +#endif +cuGraphNodeGetDependencies_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1856 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1856] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 232 & 0xffff +#if 232 > 0xffff + movk ip0, 232 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphNodeGetDependentNodes + .p2align 4 + .type cuGraphNodeGetDependentNodes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphNodeGetDependentNodes +#endif +cuGraphNodeGetDependentNodes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1864 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1864] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 233 & 0xffff +#if 233 > 0xffff + movk ip0, 233 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphNodeGetDependentNodes_v2 + .p2align 4 + .type cuGraphNodeGetDependentNodes_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphNodeGetDependentNodes_v2 +#endif +cuGraphNodeGetDependentNodes_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1872 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1872] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 234 & 0xffff +#if 234 > 0xffff + movk ip0, 234 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphNodeGetEnabled + .p2align 4 + .type cuGraphNodeGetEnabled, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphNodeGetEnabled +#endif +cuGraphNodeGetEnabled: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1880 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1880] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 235 & 0xffff +#if 235 > 0xffff + movk ip0, 235 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphNodeGetType + .p2align 4 + .type cuGraphNodeGetType, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphNodeGetType +#endif +cuGraphNodeGetType: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1888 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1888] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 236 & 0xffff +#if 236 > 0xffff + movk ip0, 236 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphNodeSetEnabled + .p2align 4 + .type cuGraphNodeSetEnabled, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphNodeSetEnabled +#endif +cuGraphNodeSetEnabled: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1896 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1896] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 237 & 0xffff +#if 237 > 0xffff + movk ip0, 237 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphNodeSetParams + .p2align 4 + .type cuGraphNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphNodeSetParams +#endif +cuGraphNodeSetParams: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1904 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1904] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 238 & 0xffff +#if 238 > 0xffff + movk ip0, 238 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphReleaseUserObject + .p2align 4 + .type cuGraphReleaseUserObject, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphReleaseUserObject +#endif +cuGraphReleaseUserObject: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1912 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1912] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 239 & 0xffff +#if 239 > 0xffff + movk ip0, 239 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphRemoveDependencies + .p2align 4 + .type cuGraphRemoveDependencies, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphRemoveDependencies +#endif +cuGraphRemoveDependencies: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1920 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1920] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 240 & 0xffff +#if 240 > 0xffff + movk ip0, 240 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphRemoveDependencies_v2 + .p2align 4 + .type cuGraphRemoveDependencies_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphRemoveDependencies_v2 +#endif +cuGraphRemoveDependencies_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1928 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1928] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 241 & 0xffff +#if 241 > 0xffff + movk ip0, 241 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphRetainUserObject + .p2align 4 + .type cuGraphRetainUserObject, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphRetainUserObject +#endif +cuGraphRetainUserObject: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1936 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1936] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 242 & 0xffff +#if 242 > 0xffff + movk ip0, 242 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphUpload + .p2align 4 + .type cuGraphUpload, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphUpload +#endif +cuGraphUpload: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1944 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1944] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 243 & 0xffff +#if 243 > 0xffff + movk ip0, 243 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphUpload_ptsz + .p2align 4 + .type cuGraphUpload_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphUpload_ptsz +#endif +cuGraphUpload_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1952 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1952] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 244 & 0xffff +#if 244 > 0xffff + movk ip0, 244 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsEGLRegisterImage + .p2align 4 + .type cuGraphicsEGLRegisterImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsEGLRegisterImage +#endif +cuGraphicsEGLRegisterImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1960 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1960] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 245 & 0xffff +#if 245 > 0xffff + movk ip0, 245 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsGLRegisterBuffer + .p2align 4 + .type cuGraphicsGLRegisterBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsGLRegisterBuffer +#endif +cuGraphicsGLRegisterBuffer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1968 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1968] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 246 & 0xffff +#if 246 > 0xffff + movk ip0, 246 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsGLRegisterImage + .p2align 4 + .type cuGraphicsGLRegisterImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsGLRegisterImage +#endif +cuGraphicsGLRegisterImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1976 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1976] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 247 & 0xffff +#if 247 > 0xffff + movk ip0, 247 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsMapResources + .p2align 4 + .type cuGraphicsMapResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsMapResources +#endif +cuGraphicsMapResources: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1984 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1984] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 248 & 0xffff +#if 248 > 0xffff + movk ip0, 248 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsMapResources_ptsz + .p2align 4 + .type cuGraphicsMapResources_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsMapResources_ptsz +#endif +cuGraphicsMapResources_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+1992 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+1992] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 249 & 0xffff +#if 249 > 0xffff + movk ip0, 249 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsResourceGetMappedEglFrame + .p2align 4 + .type cuGraphicsResourceGetMappedEglFrame, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsResourceGetMappedEglFrame +#endif +cuGraphicsResourceGetMappedEglFrame: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2000 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2000] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 250 & 0xffff +#if 250 > 0xffff + movk ip0, 250 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsResourceGetMappedMipmappedArray + .p2align 4 + .type cuGraphicsResourceGetMappedMipmappedArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsResourceGetMappedMipmappedArray +#endif +cuGraphicsResourceGetMappedMipmappedArray: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2008 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2008] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 251 & 0xffff +#if 251 > 0xffff + movk ip0, 251 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsResourceGetMappedPointer + .p2align 4 + .type cuGraphicsResourceGetMappedPointer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsResourceGetMappedPointer +#endif +cuGraphicsResourceGetMappedPointer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2016 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2016] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 252 & 0xffff +#if 252 > 0xffff + movk ip0, 252 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsResourceGetMappedPointer_v2 + .p2align 4 + .type cuGraphicsResourceGetMappedPointer_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsResourceGetMappedPointer_v2 +#endif +cuGraphicsResourceGetMappedPointer_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2024 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2024] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 253 & 0xffff +#if 253 > 0xffff + movk ip0, 253 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsResourceSetMapFlags + .p2align 4 + .type cuGraphicsResourceSetMapFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsResourceSetMapFlags +#endif +cuGraphicsResourceSetMapFlags: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2032 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2032] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 254 & 0xffff +#if 254 > 0xffff + movk ip0, 254 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsResourceSetMapFlags_v2 + .p2align 4 + .type cuGraphicsResourceSetMapFlags_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsResourceSetMapFlags_v2 +#endif +cuGraphicsResourceSetMapFlags_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2040 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2040] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 255 & 0xffff +#if 255 > 0xffff + movk ip0, 255 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsSubResourceGetMappedArray + .p2align 4 + .type cuGraphicsSubResourceGetMappedArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsSubResourceGetMappedArray +#endif +cuGraphicsSubResourceGetMappedArray: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2048 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2048] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 256 & 0xffff +#if 256 > 0xffff + movk ip0, 256 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsUnmapResources + .p2align 4 + .type cuGraphicsUnmapResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsUnmapResources +#endif +cuGraphicsUnmapResources: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2056 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2056] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 257 & 0xffff +#if 257 > 0xffff + movk ip0, 257 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsUnmapResources_ptsz + .p2align 4 + .type cuGraphicsUnmapResources_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsUnmapResources_ptsz +#endif +cuGraphicsUnmapResources_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2064 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2064] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 258 & 0xffff +#if 258 > 0xffff + movk ip0, 258 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsUnregisterResource + .p2align 4 + .type cuGraphicsUnregisterResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsUnregisterResource +#endif +cuGraphicsUnregisterResource: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2072 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2072] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 259 & 0xffff +#if 259 > 0xffff + movk ip0, 259 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsVDPAURegisterOutputSurface + .p2align 4 + .type cuGraphicsVDPAURegisterOutputSurface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsVDPAURegisterOutputSurface +#endif +cuGraphicsVDPAURegisterOutputSurface: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2080 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2080] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 260 & 0xffff +#if 260 > 0xffff + movk ip0, 260 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsVDPAURegisterVideoSurface + .p2align 4 + .type cuGraphicsVDPAURegisterVideoSurface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsVDPAURegisterVideoSurface +#endif +cuGraphicsVDPAURegisterVideoSurface: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2088 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2088] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 261 & 0xffff +#if 261 > 0xffff + movk ip0, 261 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGreenCtxCreate + .p2align 4 + .type cuGreenCtxCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGreenCtxCreate +#endif +cuGreenCtxCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2096 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2096] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 262 & 0xffff +#if 262 > 0xffff + movk ip0, 262 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGreenCtxDestroy + .p2align 4 + .type cuGreenCtxDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGreenCtxDestroy +#endif +cuGreenCtxDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2104 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 263 & 0xffff +#if 263 > 0xffff + movk ip0, 263 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGreenCtxGetDevResource + .p2align 4 + .type cuGreenCtxGetDevResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGreenCtxGetDevResource +#endif +cuGreenCtxGetDevResource: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2112 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 264 & 0xffff +#if 264 > 0xffff + movk ip0, 264 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGreenCtxRecordEvent + .p2align 4 + .type cuGreenCtxRecordEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGreenCtxRecordEvent +#endif +cuGreenCtxRecordEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2120 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 265 & 0xffff +#if 265 > 0xffff + movk ip0, 265 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGreenCtxStreamCreate + .p2align 4 + .type cuGreenCtxStreamCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGreenCtxStreamCreate +#endif +cuGreenCtxStreamCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2128 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 266 & 0xffff +#if 266 > 0xffff + movk ip0, 266 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGreenCtxWaitEvent + .p2align 4 + .type cuGreenCtxWaitEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGreenCtxWaitEvent +#endif +cuGreenCtxWaitEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2136 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 267 & 0xffff +#if 267 > 0xffff + movk ip0, 267 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuImportExternalMemory + .p2align 4 + .type cuImportExternalMemory, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuImportExternalMemory +#endif +cuImportExternalMemory: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2144 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 268 & 0xffff +#if 268 > 0xffff + movk ip0, 268 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuImportExternalSemaphore + .p2align 4 + .type cuImportExternalSemaphore, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuImportExternalSemaphore +#endif +cuImportExternalSemaphore: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2152 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 269 & 0xffff +#if 269 > 0xffff + movk ip0, 269 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuInit + .p2align 4 + .type cuInit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuInit +#endif +cuInit: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2160 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 270 & 0xffff +#if 270 > 0xffff + movk ip0, 270 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuIpcCloseMemHandle + .p2align 4 + .type cuIpcCloseMemHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuIpcCloseMemHandle +#endif +cuIpcCloseMemHandle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2168 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 271 & 0xffff +#if 271 > 0xffff + movk ip0, 271 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuIpcGetEventHandle + .p2align 4 + .type cuIpcGetEventHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuIpcGetEventHandle +#endif +cuIpcGetEventHandle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2176 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 272 & 0xffff +#if 272 > 0xffff + movk ip0, 272 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuIpcGetMemHandle + .p2align 4 + .type cuIpcGetMemHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuIpcGetMemHandle +#endif +cuIpcGetMemHandle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2184 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 273 & 0xffff +#if 273 > 0xffff + movk ip0, 273 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuIpcOpenEventHandle + .p2align 4 + .type cuIpcOpenEventHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuIpcOpenEventHandle +#endif +cuIpcOpenEventHandle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2192 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 274 & 0xffff +#if 274 > 0xffff + movk ip0, 274 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuIpcOpenMemHandle + .p2align 4 + .type cuIpcOpenMemHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuIpcOpenMemHandle +#endif +cuIpcOpenMemHandle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2200 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 275 & 0xffff +#if 275 > 0xffff + movk ip0, 275 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuIpcOpenMemHandle_v2 + .p2align 4 + .type cuIpcOpenMemHandle_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuIpcOpenMemHandle_v2 +#endif +cuIpcOpenMemHandle_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2208 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 276 & 0xffff +#if 276 > 0xffff + movk ip0, 276 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuKernelGetAttribute + .p2align 4 + .type cuKernelGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuKernelGetAttribute +#endif +cuKernelGetAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2216 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 277 & 0xffff +#if 277 > 0xffff + movk ip0, 277 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuKernelGetFunction + .p2align 4 + .type cuKernelGetFunction, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuKernelGetFunction +#endif +cuKernelGetFunction: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2224 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 278 & 0xffff +#if 278 > 0xffff + movk ip0, 278 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuKernelGetLibrary + .p2align 4 + .type cuKernelGetLibrary, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuKernelGetLibrary +#endif +cuKernelGetLibrary: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2232 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 279 & 0xffff +#if 279 > 0xffff + movk ip0, 279 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuKernelGetName + .p2align 4 + .type cuKernelGetName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuKernelGetName +#endif +cuKernelGetName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2240 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 280 & 0xffff +#if 280 > 0xffff + movk ip0, 280 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuKernelGetParamInfo + .p2align 4 + .type cuKernelGetParamInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuKernelGetParamInfo +#endif +cuKernelGetParamInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2248 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 281 & 0xffff +#if 281 > 0xffff + movk ip0, 281 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuKernelSetAttribute + .p2align 4 + .type cuKernelSetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuKernelSetAttribute +#endif +cuKernelSetAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2256 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 282 & 0xffff +#if 282 > 0xffff + movk ip0, 282 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuKernelSetCacheConfig + .p2align 4 + .type cuKernelSetCacheConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuKernelSetCacheConfig +#endif +cuKernelSetCacheConfig: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2264 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 283 & 0xffff +#if 283 > 0xffff + movk ip0, 283 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunch + .p2align 4 + .type cuLaunch, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunch +#endif +cuLaunch: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2272 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 284 & 0xffff +#if 284 > 0xffff + movk ip0, 284 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchCooperativeKernel + .p2align 4 + .type cuLaunchCooperativeKernel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchCooperativeKernel +#endif +cuLaunchCooperativeKernel: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2280 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 285 & 0xffff +#if 285 > 0xffff + movk ip0, 285 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchCooperativeKernelMultiDevice + .p2align 4 + .type cuLaunchCooperativeKernelMultiDevice, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchCooperativeKernelMultiDevice +#endif +cuLaunchCooperativeKernelMultiDevice: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2288 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 286 & 0xffff +#if 286 > 0xffff + movk ip0, 286 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchCooperativeKernel_ptsz + .p2align 4 + .type cuLaunchCooperativeKernel_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchCooperativeKernel_ptsz +#endif +cuLaunchCooperativeKernel_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2296 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 287 & 0xffff +#if 287 > 0xffff + movk ip0, 287 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchGrid + .p2align 4 + .type cuLaunchGrid, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchGrid +#endif +cuLaunchGrid: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2304 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 288 & 0xffff +#if 288 > 0xffff + movk ip0, 288 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchGridAsync + .p2align 4 + .type cuLaunchGridAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchGridAsync +#endif +cuLaunchGridAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2312 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 289 & 0xffff +#if 289 > 0xffff + movk ip0, 289 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchHostFunc + .p2align 4 + .type cuLaunchHostFunc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchHostFunc +#endif +cuLaunchHostFunc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2320 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 290 & 0xffff +#if 290 > 0xffff + movk ip0, 290 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchHostFunc_ptsz + .p2align 4 + .type cuLaunchHostFunc_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchHostFunc_ptsz +#endif +cuLaunchHostFunc_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2328 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 291 & 0xffff +#if 291 > 0xffff + movk ip0, 291 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchKernel + .p2align 4 + .type cuLaunchKernel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchKernel +#endif +cuLaunchKernel: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2336 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 292 & 0xffff +#if 292 > 0xffff + movk ip0, 292 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchKernelEx + .p2align 4 + .type cuLaunchKernelEx, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchKernelEx +#endif +cuLaunchKernelEx: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2344 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 293 & 0xffff +#if 293 > 0xffff + movk ip0, 293 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchKernelEx_ptsz + .p2align 4 + .type cuLaunchKernelEx_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchKernelEx_ptsz +#endif +cuLaunchKernelEx_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2352 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 294 & 0xffff +#if 294 > 0xffff + movk ip0, 294 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchKernel_ptsz + .p2align 4 + .type cuLaunchKernel_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchKernel_ptsz +#endif +cuLaunchKernel_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2360 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 295 & 0xffff +#if 295 > 0xffff + movk ip0, 295 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryEnumerateKernels + .p2align 4 + .type cuLibraryEnumerateKernels, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryEnumerateKernels +#endif +cuLibraryEnumerateKernels: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2368 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 296 & 0xffff +#if 296 > 0xffff + movk ip0, 296 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryGetGlobal + .p2align 4 + .type cuLibraryGetGlobal, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryGetGlobal +#endif +cuLibraryGetGlobal: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2376 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 297 & 0xffff +#if 297 > 0xffff + movk ip0, 297 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryGetKernel + .p2align 4 + .type cuLibraryGetKernel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryGetKernel +#endif +cuLibraryGetKernel: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2384 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 298 & 0xffff +#if 298 > 0xffff + movk ip0, 298 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryGetKernelCount + .p2align 4 + .type cuLibraryGetKernelCount, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryGetKernelCount +#endif +cuLibraryGetKernelCount: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2392 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 299 & 0xffff +#if 299 > 0xffff + movk ip0, 299 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryGetManaged + .p2align 4 + .type cuLibraryGetManaged, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryGetManaged +#endif +cuLibraryGetManaged: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2400 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 300 & 0xffff +#if 300 > 0xffff + movk ip0, 300 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryGetModule + .p2align 4 + .type cuLibraryGetModule, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryGetModule +#endif +cuLibraryGetModule: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2408 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 301 & 0xffff +#if 301 > 0xffff + movk ip0, 301 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryGetUnifiedFunction + .p2align 4 + .type cuLibraryGetUnifiedFunction, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryGetUnifiedFunction +#endif +cuLibraryGetUnifiedFunction: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2416 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 302 & 0xffff +#if 302 > 0xffff + movk ip0, 302 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryLoadData + .p2align 4 + .type cuLibraryLoadData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryLoadData +#endif +cuLibraryLoadData: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2424 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 303 & 0xffff +#if 303 > 0xffff + movk ip0, 303 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryLoadFromFile + .p2align 4 + .type cuLibraryLoadFromFile, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryLoadFromFile +#endif +cuLibraryLoadFromFile: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2432 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 304 & 0xffff +#if 304 > 0xffff + movk ip0, 304 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryUnload + .p2align 4 + .type cuLibraryUnload, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryUnload +#endif +cuLibraryUnload: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2440 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 305 & 0xffff +#if 305 > 0xffff + movk ip0, 305 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLinkAddData + .p2align 4 + .type cuLinkAddData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLinkAddData +#endif +cuLinkAddData: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2448 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 306 & 0xffff +#if 306 > 0xffff + movk ip0, 306 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLinkAddData_v2 + .p2align 4 + .type cuLinkAddData_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLinkAddData_v2 +#endif +cuLinkAddData_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2456 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2456] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 307 & 0xffff +#if 307 > 0xffff + movk ip0, 307 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLinkAddFile + .p2align 4 + .type cuLinkAddFile, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLinkAddFile +#endif +cuLinkAddFile: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2464 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2464] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 308 & 0xffff +#if 308 > 0xffff + movk ip0, 308 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLinkAddFile_v2 + .p2align 4 + .type cuLinkAddFile_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLinkAddFile_v2 +#endif +cuLinkAddFile_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2472 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2472] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 309 & 0xffff +#if 309 > 0xffff + movk ip0, 309 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLinkComplete + .p2align 4 + .type cuLinkComplete, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLinkComplete +#endif +cuLinkComplete: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2480 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2480] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 310 & 0xffff +#if 310 > 0xffff + movk ip0, 310 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLinkCreate + .p2align 4 + .type cuLinkCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLinkCreate +#endif +cuLinkCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2488 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2488] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 311 & 0xffff +#if 311 > 0xffff + movk ip0, 311 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLinkCreate_v2 + .p2align 4 + .type cuLinkCreate_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLinkCreate_v2 +#endif +cuLinkCreate_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2496 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2496] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 312 & 0xffff +#if 312 > 0xffff + movk ip0, 312 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLinkDestroy + .p2align 4 + .type cuLinkDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLinkDestroy +#endif +cuLinkDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2504 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2504] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 313 & 0xffff +#if 313 > 0xffff + movk ip0, 313 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAddressFree + .p2align 4 + .type cuMemAddressFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAddressFree +#endif +cuMemAddressFree: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2512 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2512] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 314 & 0xffff +#if 314 > 0xffff + movk ip0, 314 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAddressReserve + .p2align 4 + .type cuMemAddressReserve, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAddressReserve +#endif +cuMemAddressReserve: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2520 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2520] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 315 & 0xffff +#if 315 > 0xffff + movk ip0, 315 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAdvise + .p2align 4 + .type cuMemAdvise, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAdvise +#endif +cuMemAdvise: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2528 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2528] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 316 & 0xffff +#if 316 > 0xffff + movk ip0, 316 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAdvise_v2 + .p2align 4 + .type cuMemAdvise_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAdvise_v2 +#endif +cuMemAdvise_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2536 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2536] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 317 & 0xffff +#if 317 > 0xffff + movk ip0, 317 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAlloc + .p2align 4 + .type cuMemAlloc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAlloc +#endif +cuMemAlloc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2544 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2544] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 318 & 0xffff +#if 318 > 0xffff + movk ip0, 318 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAllocAsync + .p2align 4 + .type cuMemAllocAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAllocAsync +#endif +cuMemAllocAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2552 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2552] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 319 & 0xffff +#if 319 > 0xffff + movk ip0, 319 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAllocAsync_ptsz + .p2align 4 + .type cuMemAllocAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAllocAsync_ptsz +#endif +cuMemAllocAsync_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2560 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2560] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 320 & 0xffff +#if 320 > 0xffff + movk ip0, 320 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAllocFromPoolAsync + .p2align 4 + .type cuMemAllocFromPoolAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAllocFromPoolAsync +#endif +cuMemAllocFromPoolAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2568 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2568] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 321 & 0xffff +#if 321 > 0xffff + movk ip0, 321 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAllocFromPoolAsync_ptsz + .p2align 4 + .type cuMemAllocFromPoolAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAllocFromPoolAsync_ptsz +#endif +cuMemAllocFromPoolAsync_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2576 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2576] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 322 & 0xffff +#if 322 > 0xffff + movk ip0, 322 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAllocHost + .p2align 4 + .type cuMemAllocHost, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAllocHost +#endif +cuMemAllocHost: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2584 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2584] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 323 & 0xffff +#if 323 > 0xffff + movk ip0, 323 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAllocHost_v2 + .p2align 4 + .type cuMemAllocHost_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAllocHost_v2 +#endif +cuMemAllocHost_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2592 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2592] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 324 & 0xffff +#if 324 > 0xffff + movk ip0, 324 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAllocManaged + .p2align 4 + .type cuMemAllocManaged, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAllocManaged +#endif +cuMemAllocManaged: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2600 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2600] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 325 & 0xffff +#if 325 > 0xffff + movk ip0, 325 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAllocPitch + .p2align 4 + .type cuMemAllocPitch, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAllocPitch +#endif +cuMemAllocPitch: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2608 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2608] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 326 & 0xffff +#if 326 > 0xffff + movk ip0, 326 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAllocPitch_v2 + .p2align 4 + .type cuMemAllocPitch_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAllocPitch_v2 +#endif +cuMemAllocPitch_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2616 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2616] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 327 & 0xffff +#if 327 > 0xffff + movk ip0, 327 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAlloc_v2 + .p2align 4 + .type cuMemAlloc_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAlloc_v2 +#endif +cuMemAlloc_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2624 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2624] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 328 & 0xffff +#if 328 > 0xffff + movk ip0, 328 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemBatchDecompressAsync + .p2align 4 + .type cuMemBatchDecompressAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemBatchDecompressAsync +#endif +cuMemBatchDecompressAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2632 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2632] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 329 & 0xffff +#if 329 > 0xffff + movk ip0, 329 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemBatchDecompressAsync_ptsz + .p2align 4 + .type cuMemBatchDecompressAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemBatchDecompressAsync_ptsz +#endif +cuMemBatchDecompressAsync_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2640 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2640] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 330 & 0xffff +#if 330 > 0xffff + movk ip0, 330 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemCreate + .p2align 4 + .type cuMemCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemCreate +#endif +cuMemCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2648 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2648] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 331 & 0xffff +#if 331 > 0xffff + movk ip0, 331 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemExportToShareableHandle + .p2align 4 + .type cuMemExportToShareableHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemExportToShareableHandle +#endif +cuMemExportToShareableHandle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2656 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2656] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 332 & 0xffff +#if 332 > 0xffff + movk ip0, 332 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemFree + .p2align 4 + .type cuMemFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemFree +#endif +cuMemFree: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2664 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2664] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 333 & 0xffff +#if 333 > 0xffff + movk ip0, 333 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemFreeAsync + .p2align 4 + .type cuMemFreeAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemFreeAsync +#endif +cuMemFreeAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2672 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2672] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 334 & 0xffff +#if 334 > 0xffff + movk ip0, 334 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemFreeAsync_ptsz + .p2align 4 + .type cuMemFreeAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemFreeAsync_ptsz +#endif +cuMemFreeAsync_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2680 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2680] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 335 & 0xffff +#if 335 > 0xffff + movk ip0, 335 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemFreeHost + .p2align 4 + .type cuMemFreeHost, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemFreeHost +#endif +cuMemFreeHost: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2688 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2688] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 336 & 0xffff +#if 336 > 0xffff + movk ip0, 336 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemFree_v2 + .p2align 4 + .type cuMemFree_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemFree_v2 +#endif +cuMemFree_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2696 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2696] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 337 & 0xffff +#if 337 > 0xffff + movk ip0, 337 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetAccess + .p2align 4 + .type cuMemGetAccess, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetAccess +#endif +cuMemGetAccess: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2704 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2704] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 338 & 0xffff +#if 338 > 0xffff + movk ip0, 338 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetAddressRange + .p2align 4 + .type cuMemGetAddressRange, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetAddressRange +#endif +cuMemGetAddressRange: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2712 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2712] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 339 & 0xffff +#if 339 > 0xffff + movk ip0, 339 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetAddressRange_v2 + .p2align 4 + .type cuMemGetAddressRange_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetAddressRange_v2 +#endif +cuMemGetAddressRange_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2720 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2720] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 340 & 0xffff +#if 340 > 0xffff + movk ip0, 340 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetAllocationGranularity + .p2align 4 + .type cuMemGetAllocationGranularity, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetAllocationGranularity +#endif +cuMemGetAllocationGranularity: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2728 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2728] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 341 & 0xffff +#if 341 > 0xffff + movk ip0, 341 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetAllocationPropertiesFromHandle + .p2align 4 + .type cuMemGetAllocationPropertiesFromHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetAllocationPropertiesFromHandle +#endif +cuMemGetAllocationPropertiesFromHandle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2736 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2736] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 342 & 0xffff +#if 342 > 0xffff + movk ip0, 342 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetAttribute + .p2align 4 + .type cuMemGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetAttribute +#endif +cuMemGetAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2744 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2744] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 343 & 0xffff +#if 343 > 0xffff + movk ip0, 343 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetAttribute_v2 + .p2align 4 + .type cuMemGetAttribute_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetAttribute_v2 +#endif +cuMemGetAttribute_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2752 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2752] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 344 & 0xffff +#if 344 > 0xffff + movk ip0, 344 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetHandleForAddressRange + .p2align 4 + .type cuMemGetHandleForAddressRange, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetHandleForAddressRange +#endif +cuMemGetHandleForAddressRange: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2760 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2760] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 345 & 0xffff +#if 345 > 0xffff + movk ip0, 345 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetInfo + .p2align 4 + .type cuMemGetInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetInfo +#endif +cuMemGetInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2768 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2768] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 346 & 0xffff +#if 346 > 0xffff + movk ip0, 346 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetInfo_v2 + .p2align 4 + .type cuMemGetInfo_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetInfo_v2 +#endif +cuMemGetInfo_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2776 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2776] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 347 & 0xffff +#if 347 > 0xffff + movk ip0, 347 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemHostAlloc + .p2align 4 + .type cuMemHostAlloc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemHostAlloc +#endif +cuMemHostAlloc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2784 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2784] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 348 & 0xffff +#if 348 > 0xffff + movk ip0, 348 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemHostGetDevicePointer + .p2align 4 + .type cuMemHostGetDevicePointer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemHostGetDevicePointer +#endif +cuMemHostGetDevicePointer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2792 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2792] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 349 & 0xffff +#if 349 > 0xffff + movk ip0, 349 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemHostGetDevicePointer_v2 + .p2align 4 + .type cuMemHostGetDevicePointer_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemHostGetDevicePointer_v2 +#endif +cuMemHostGetDevicePointer_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2800 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2800] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 350 & 0xffff +#if 350 > 0xffff + movk ip0, 350 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemHostGetFlags + .p2align 4 + .type cuMemHostGetFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemHostGetFlags +#endif +cuMemHostGetFlags: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2808 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2808] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 351 & 0xffff +#if 351 > 0xffff + movk ip0, 351 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemHostRegister + .p2align 4 + .type cuMemHostRegister, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemHostRegister +#endif +cuMemHostRegister: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2816 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2816] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 352 & 0xffff +#if 352 > 0xffff + movk ip0, 352 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemHostRegister_v2 + .p2align 4 + .type cuMemHostRegister_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemHostRegister_v2 +#endif +cuMemHostRegister_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2824 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2824] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 353 & 0xffff +#if 353 > 0xffff + movk ip0, 353 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemHostUnregister + .p2align 4 + .type cuMemHostUnregister, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemHostUnregister +#endif +cuMemHostUnregister: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2832 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2832] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 354 & 0xffff +#if 354 > 0xffff + movk ip0, 354 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemImportFromShareableHandle + .p2align 4 + .type cuMemImportFromShareableHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemImportFromShareableHandle +#endif +cuMemImportFromShareableHandle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2840 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2840] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 355 & 0xffff +#if 355 > 0xffff + movk ip0, 355 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemMap + .p2align 4 + .type cuMemMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemMap +#endif +cuMemMap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2848 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2848] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 356 & 0xffff +#if 356 > 0xffff + movk ip0, 356 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemMapArrayAsync + .p2align 4 + .type cuMemMapArrayAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemMapArrayAsync +#endif +cuMemMapArrayAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2856 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2856] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 357 & 0xffff +#if 357 > 0xffff + movk ip0, 357 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemMapArrayAsync_ptsz + .p2align 4 + .type cuMemMapArrayAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemMapArrayAsync_ptsz +#endif +cuMemMapArrayAsync_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2864 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2864] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 358 & 0xffff +#if 358 > 0xffff + movk ip0, 358 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolCreate + .p2align 4 + .type cuMemPoolCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolCreate +#endif +cuMemPoolCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2872 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2872] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 359 & 0xffff +#if 359 > 0xffff + movk ip0, 359 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolDestroy + .p2align 4 + .type cuMemPoolDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolDestroy +#endif +cuMemPoolDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2880 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2880] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 360 & 0xffff +#if 360 > 0xffff + movk ip0, 360 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolExportPointer + .p2align 4 + .type cuMemPoolExportPointer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolExportPointer +#endif +cuMemPoolExportPointer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2888 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2888] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 361 & 0xffff +#if 361 > 0xffff + movk ip0, 361 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolExportToShareableHandle + .p2align 4 + .type cuMemPoolExportToShareableHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolExportToShareableHandle +#endif +cuMemPoolExportToShareableHandle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2896 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2896] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 362 & 0xffff +#if 362 > 0xffff + movk ip0, 362 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolGetAccess + .p2align 4 + .type cuMemPoolGetAccess, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolGetAccess +#endif +cuMemPoolGetAccess: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2904 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2904] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 363 & 0xffff +#if 363 > 0xffff + movk ip0, 363 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolGetAttribute + .p2align 4 + .type cuMemPoolGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolGetAttribute +#endif +cuMemPoolGetAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2912 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2912] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 364 & 0xffff +#if 364 > 0xffff + movk ip0, 364 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolImportFromShareableHandle + .p2align 4 + .type cuMemPoolImportFromShareableHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolImportFromShareableHandle +#endif +cuMemPoolImportFromShareableHandle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2920 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2920] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 365 & 0xffff +#if 365 > 0xffff + movk ip0, 365 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolImportPointer + .p2align 4 + .type cuMemPoolImportPointer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolImportPointer +#endif +cuMemPoolImportPointer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2928 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2928] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 366 & 0xffff +#if 366 > 0xffff + movk ip0, 366 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolSetAccess + .p2align 4 + .type cuMemPoolSetAccess, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolSetAccess +#endif +cuMemPoolSetAccess: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2936 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2936] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 367 & 0xffff +#if 367 > 0xffff + movk ip0, 367 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolSetAttribute + .p2align 4 + .type cuMemPoolSetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolSetAttribute +#endif +cuMemPoolSetAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2944 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2944] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 368 & 0xffff +#if 368 > 0xffff + movk ip0, 368 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolTrimTo + .p2align 4 + .type cuMemPoolTrimTo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolTrimTo +#endif +cuMemPoolTrimTo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2952 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2952] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 369 & 0xffff +#if 369 > 0xffff + movk ip0, 369 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPrefetchAsync + .p2align 4 + .type cuMemPrefetchAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPrefetchAsync +#endif +cuMemPrefetchAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2960 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2960] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 370 & 0xffff +#if 370 > 0xffff + movk ip0, 370 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPrefetchAsync_ptsz + .p2align 4 + .type cuMemPrefetchAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPrefetchAsync_ptsz +#endif +cuMemPrefetchAsync_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2968 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2968] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 371 & 0xffff +#if 371 > 0xffff + movk ip0, 371 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPrefetchAsync_v2 + .p2align 4 + .type cuMemPrefetchAsync_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPrefetchAsync_v2 +#endif +cuMemPrefetchAsync_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2976 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2976] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 372 & 0xffff +#if 372 > 0xffff + movk ip0, 372 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPrefetchAsync_v2_ptsz + .p2align 4 + .type cuMemPrefetchAsync_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPrefetchAsync_v2_ptsz +#endif +cuMemPrefetchAsync_v2_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2984 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2984] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 373 & 0xffff +#if 373 > 0xffff + movk ip0, 373 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemRangeGetAttribute + .p2align 4 + .type cuMemRangeGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemRangeGetAttribute +#endif +cuMemRangeGetAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+2992 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+2992] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 374 & 0xffff +#if 374 > 0xffff + movk ip0, 374 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemRangeGetAttributes + .p2align 4 + .type cuMemRangeGetAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemRangeGetAttributes +#endif +cuMemRangeGetAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3000 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3000] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 375 & 0xffff +#if 375 > 0xffff + movk ip0, 375 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemRelease + .p2align 4 + .type cuMemRelease, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemRelease +#endif +cuMemRelease: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3008 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3008] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 376 & 0xffff +#if 376 > 0xffff + movk ip0, 376 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemRetainAllocationHandle + .p2align 4 + .type cuMemRetainAllocationHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemRetainAllocationHandle +#endif +cuMemRetainAllocationHandle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3016 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3016] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 377 & 0xffff +#if 377 > 0xffff + movk ip0, 377 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemSetAccess + .p2align 4 + .type cuMemSetAccess, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemSetAccess +#endif +cuMemSetAccess: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3024 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3024] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 378 & 0xffff +#if 378 > 0xffff + movk ip0, 378 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemUnmap + .p2align 4 + .type cuMemUnmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemUnmap +#endif +cuMemUnmap: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3032 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3032] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 379 & 0xffff +#if 379 > 0xffff + movk ip0, 379 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy + .p2align 4 + .type cuMemcpy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy +#endif +cuMemcpy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3040 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3040] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 380 & 0xffff +#if 380 > 0xffff + movk ip0, 380 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy2D + .p2align 4 + .type cuMemcpy2D, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy2D +#endif +cuMemcpy2D: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3048 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3048] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 381 & 0xffff +#if 381 > 0xffff + movk ip0, 381 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy2DAsync + .p2align 4 + .type cuMemcpy2DAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy2DAsync +#endif +cuMemcpy2DAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3056 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3056] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 382 & 0xffff +#if 382 > 0xffff + movk ip0, 382 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy2DAsync_v2 + .p2align 4 + .type cuMemcpy2DAsync_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy2DAsync_v2 +#endif +cuMemcpy2DAsync_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3064 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3064] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 383 & 0xffff +#if 383 > 0xffff + movk ip0, 383 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy2DAsync_v2_ptsz + .p2align 4 + .type cuMemcpy2DAsync_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy2DAsync_v2_ptsz +#endif +cuMemcpy2DAsync_v2_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3072 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3072] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 384 & 0xffff +#if 384 > 0xffff + movk ip0, 384 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy2DUnaligned + .p2align 4 + .type cuMemcpy2DUnaligned, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy2DUnaligned +#endif +cuMemcpy2DUnaligned: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3080 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3080] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 385 & 0xffff +#if 385 > 0xffff + movk ip0, 385 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy2DUnaligned_v2 + .p2align 4 + .type cuMemcpy2DUnaligned_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy2DUnaligned_v2 +#endif +cuMemcpy2DUnaligned_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3088 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3088] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 386 & 0xffff +#if 386 > 0xffff + movk ip0, 386 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy2DUnaligned_v2_ptds + .p2align 4 + .type cuMemcpy2DUnaligned_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy2DUnaligned_v2_ptds +#endif +cuMemcpy2DUnaligned_v2_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3096 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3096] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 387 & 0xffff +#if 387 > 0xffff + movk ip0, 387 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy2D_v2 + .p2align 4 + .type cuMemcpy2D_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy2D_v2 +#endif +cuMemcpy2D_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3104 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 388 & 0xffff +#if 388 > 0xffff + movk ip0, 388 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy2D_v2_ptds + .p2align 4 + .type cuMemcpy2D_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy2D_v2_ptds +#endif +cuMemcpy2D_v2_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3112 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 389 & 0xffff +#if 389 > 0xffff + movk ip0, 389 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3D + .p2align 4 + .type cuMemcpy3D, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3D +#endif +cuMemcpy3D: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3120 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 390 & 0xffff +#if 390 > 0xffff + movk ip0, 390 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3DAsync + .p2align 4 + .type cuMemcpy3DAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3DAsync +#endif +cuMemcpy3DAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3128 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 391 & 0xffff +#if 391 > 0xffff + movk ip0, 391 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3DAsync_v2 + .p2align 4 + .type cuMemcpy3DAsync_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3DAsync_v2 +#endif +cuMemcpy3DAsync_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3136 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 392 & 0xffff +#if 392 > 0xffff + movk ip0, 392 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3DAsync_v2_ptsz + .p2align 4 + .type cuMemcpy3DAsync_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3DAsync_v2_ptsz +#endif +cuMemcpy3DAsync_v2_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3144 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 393 & 0xffff +#if 393 > 0xffff + movk ip0, 393 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3DBatchAsync + .p2align 4 + .type cuMemcpy3DBatchAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3DBatchAsync +#endif +cuMemcpy3DBatchAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3152 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 394 & 0xffff +#if 394 > 0xffff + movk ip0, 394 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3DBatchAsync_ptsz + .p2align 4 + .type cuMemcpy3DBatchAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3DBatchAsync_ptsz +#endif +cuMemcpy3DBatchAsync_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3160 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 395 & 0xffff +#if 395 > 0xffff + movk ip0, 395 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3DPeer + .p2align 4 + .type cuMemcpy3DPeer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3DPeer +#endif +cuMemcpy3DPeer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3168 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 396 & 0xffff +#if 396 > 0xffff + movk ip0, 396 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3DPeerAsync + .p2align 4 + .type cuMemcpy3DPeerAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3DPeerAsync +#endif +cuMemcpy3DPeerAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3176 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 397 & 0xffff +#if 397 > 0xffff + movk ip0, 397 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3DPeerAsync_ptsz + .p2align 4 + .type cuMemcpy3DPeerAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3DPeerAsync_ptsz +#endif +cuMemcpy3DPeerAsync_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3184 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 398 & 0xffff +#if 398 > 0xffff + movk ip0, 398 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3DPeer_ptds + .p2align 4 + .type cuMemcpy3DPeer_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3DPeer_ptds +#endif +cuMemcpy3DPeer_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3192 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 399 & 0xffff +#if 399 > 0xffff + movk ip0, 399 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3D_v2 + .p2align 4 + .type cuMemcpy3D_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3D_v2 +#endif +cuMemcpy3D_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3200 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 400 & 0xffff +#if 400 > 0xffff + movk ip0, 400 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3D_v2_ptds + .p2align 4 + .type cuMemcpy3D_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3D_v2_ptds +#endif +cuMemcpy3D_v2_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3208 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 401 & 0xffff +#if 401 > 0xffff + movk ip0, 401 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAsync + .p2align 4 + .type cuMemcpyAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAsync +#endif +cuMemcpyAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3216 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 402 & 0xffff +#if 402 > 0xffff + movk ip0, 402 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAsync_ptsz + .p2align 4 + .type cuMemcpyAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAsync_ptsz +#endif +cuMemcpyAsync_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3224 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 403 & 0xffff +#if 403 > 0xffff + movk ip0, 403 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoA + .p2align 4 + .type cuMemcpyAtoA, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoA +#endif +cuMemcpyAtoA: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3232 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 404 & 0xffff +#if 404 > 0xffff + movk ip0, 404 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoA_v2 + .p2align 4 + .type cuMemcpyAtoA_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoA_v2 +#endif +cuMemcpyAtoA_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3240 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 405 & 0xffff +#if 405 > 0xffff + movk ip0, 405 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoA_v2_ptds + .p2align 4 + .type cuMemcpyAtoA_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoA_v2_ptds +#endif +cuMemcpyAtoA_v2_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3248 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 406 & 0xffff +#if 406 > 0xffff + movk ip0, 406 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoD + .p2align 4 + .type cuMemcpyAtoD, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoD +#endif +cuMemcpyAtoD: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3256 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 407 & 0xffff +#if 407 > 0xffff + movk ip0, 407 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoD_v2 + .p2align 4 + .type cuMemcpyAtoD_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoD_v2 +#endif +cuMemcpyAtoD_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3264 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 408 & 0xffff +#if 408 > 0xffff + movk ip0, 408 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoD_v2_ptds + .p2align 4 + .type cuMemcpyAtoD_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoD_v2_ptds +#endif +cuMemcpyAtoD_v2_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3272 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 409 & 0xffff +#if 409 > 0xffff + movk ip0, 409 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoH + .p2align 4 + .type cuMemcpyAtoH, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoH +#endif +cuMemcpyAtoH: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3280 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 410 & 0xffff +#if 410 > 0xffff + movk ip0, 410 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoHAsync + .p2align 4 + .type cuMemcpyAtoHAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoHAsync +#endif +cuMemcpyAtoHAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3288 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 411 & 0xffff +#if 411 > 0xffff + movk ip0, 411 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoHAsync_v2 + .p2align 4 + .type cuMemcpyAtoHAsync_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoHAsync_v2 +#endif +cuMemcpyAtoHAsync_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3296 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 412 & 0xffff +#if 412 > 0xffff + movk ip0, 412 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoHAsync_v2_ptsz + .p2align 4 + .type cuMemcpyAtoHAsync_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoHAsync_v2_ptsz +#endif +cuMemcpyAtoHAsync_v2_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3304 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 413 & 0xffff +#if 413 > 0xffff + movk ip0, 413 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoH_v2 + .p2align 4 + .type cuMemcpyAtoH_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoH_v2 +#endif +cuMemcpyAtoH_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3312 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 414 & 0xffff +#if 414 > 0xffff + movk ip0, 414 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoH_v2_ptds + .p2align 4 + .type cuMemcpyAtoH_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoH_v2_ptds +#endif +cuMemcpyAtoH_v2_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3320 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 415 & 0xffff +#if 415 > 0xffff + movk ip0, 415 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyBatchAsync + .p2align 4 + .type cuMemcpyBatchAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyBatchAsync +#endif +cuMemcpyBatchAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3328 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 416 & 0xffff +#if 416 > 0xffff + movk ip0, 416 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyBatchAsync_ptsz + .p2align 4 + .type cuMemcpyBatchAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyBatchAsync_ptsz +#endif +cuMemcpyBatchAsync_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3336 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 417 & 0xffff +#if 417 > 0xffff + movk ip0, 417 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoA + .p2align 4 + .type cuMemcpyDtoA, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoA +#endif +cuMemcpyDtoA: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3344 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 418 & 0xffff +#if 418 > 0xffff + movk ip0, 418 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoA_v2 + .p2align 4 + .type cuMemcpyDtoA_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoA_v2 +#endif +cuMemcpyDtoA_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3352 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 419 & 0xffff +#if 419 > 0xffff + movk ip0, 419 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoA_v2_ptds + .p2align 4 + .type cuMemcpyDtoA_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoA_v2_ptds +#endif +cuMemcpyDtoA_v2_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3360 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 420 & 0xffff +#if 420 > 0xffff + movk ip0, 420 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoD + .p2align 4 + .type cuMemcpyDtoD, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoD +#endif +cuMemcpyDtoD: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3368 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 421 & 0xffff +#if 421 > 0xffff + movk ip0, 421 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoDAsync + .p2align 4 + .type cuMemcpyDtoDAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoDAsync +#endif +cuMemcpyDtoDAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3376 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 422 & 0xffff +#if 422 > 0xffff + movk ip0, 422 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoDAsync_v2 + .p2align 4 + .type cuMemcpyDtoDAsync_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoDAsync_v2 +#endif +cuMemcpyDtoDAsync_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3384 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 423 & 0xffff +#if 423 > 0xffff + movk ip0, 423 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoDAsync_v2_ptsz + .p2align 4 + .type cuMemcpyDtoDAsync_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoDAsync_v2_ptsz +#endif +cuMemcpyDtoDAsync_v2_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3392 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 424 & 0xffff +#if 424 > 0xffff + movk ip0, 424 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoD_v2 + .p2align 4 + .type cuMemcpyDtoD_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoD_v2 +#endif +cuMemcpyDtoD_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3400 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 425 & 0xffff +#if 425 > 0xffff + movk ip0, 425 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoD_v2_ptds + .p2align 4 + .type cuMemcpyDtoD_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoD_v2_ptds +#endif +cuMemcpyDtoD_v2_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3408 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 426 & 0xffff +#if 426 > 0xffff + movk ip0, 426 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoH + .p2align 4 + .type cuMemcpyDtoH, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoH +#endif +cuMemcpyDtoH: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3416 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 427 & 0xffff +#if 427 > 0xffff + movk ip0, 427 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoHAsync + .p2align 4 + .type cuMemcpyDtoHAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoHAsync +#endif +cuMemcpyDtoHAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3424 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 428 & 0xffff +#if 428 > 0xffff + movk ip0, 428 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoHAsync_v2 + .p2align 4 + .type cuMemcpyDtoHAsync_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoHAsync_v2 +#endif +cuMemcpyDtoHAsync_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3432 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 429 & 0xffff +#if 429 > 0xffff + movk ip0, 429 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoHAsync_v2_ptsz + .p2align 4 + .type cuMemcpyDtoHAsync_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoHAsync_v2_ptsz +#endif +cuMemcpyDtoHAsync_v2_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3440 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 430 & 0xffff +#if 430 > 0xffff + movk ip0, 430 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoH_v2 + .p2align 4 + .type cuMemcpyDtoH_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoH_v2 +#endif +cuMemcpyDtoH_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3448 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 431 & 0xffff +#if 431 > 0xffff + movk ip0, 431 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoH_v2_ptds + .p2align 4 + .type cuMemcpyDtoH_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoH_v2_ptds +#endif +cuMemcpyDtoH_v2_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3456 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3456] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 432 & 0xffff +#if 432 > 0xffff + movk ip0, 432 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoA + .p2align 4 + .type cuMemcpyHtoA, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoA +#endif +cuMemcpyHtoA: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3464 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3464] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 433 & 0xffff +#if 433 > 0xffff + movk ip0, 433 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoAAsync + .p2align 4 + .type cuMemcpyHtoAAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoAAsync +#endif +cuMemcpyHtoAAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3472 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3472] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 434 & 0xffff +#if 434 > 0xffff + movk ip0, 434 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoAAsync_v2 + .p2align 4 + .type cuMemcpyHtoAAsync_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoAAsync_v2 +#endif +cuMemcpyHtoAAsync_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3480 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3480] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 435 & 0xffff +#if 435 > 0xffff + movk ip0, 435 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoAAsync_v2_ptsz + .p2align 4 + .type cuMemcpyHtoAAsync_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoAAsync_v2_ptsz +#endif +cuMemcpyHtoAAsync_v2_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3488 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3488] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 436 & 0xffff +#if 436 > 0xffff + movk ip0, 436 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoA_v2 + .p2align 4 + .type cuMemcpyHtoA_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoA_v2 +#endif +cuMemcpyHtoA_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3496 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3496] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 437 & 0xffff +#if 437 > 0xffff + movk ip0, 437 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoA_v2_ptds + .p2align 4 + .type cuMemcpyHtoA_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoA_v2_ptds +#endif +cuMemcpyHtoA_v2_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3504 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3504] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 438 & 0xffff +#if 438 > 0xffff + movk ip0, 438 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoD + .p2align 4 + .type cuMemcpyHtoD, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoD +#endif +cuMemcpyHtoD: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3512 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3512] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 439 & 0xffff +#if 439 > 0xffff + movk ip0, 439 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoDAsync + .p2align 4 + .type cuMemcpyHtoDAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoDAsync +#endif +cuMemcpyHtoDAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3520 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3520] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 440 & 0xffff +#if 440 > 0xffff + movk ip0, 440 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoDAsync_v2 + .p2align 4 + .type cuMemcpyHtoDAsync_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoDAsync_v2 +#endif +cuMemcpyHtoDAsync_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3528 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3528] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 441 & 0xffff +#if 441 > 0xffff + movk ip0, 441 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoDAsync_v2_ptsz + .p2align 4 + .type cuMemcpyHtoDAsync_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoDAsync_v2_ptsz +#endif +cuMemcpyHtoDAsync_v2_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3536 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3536] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 442 & 0xffff +#if 442 > 0xffff + movk ip0, 442 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoD_v2 + .p2align 4 + .type cuMemcpyHtoD_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoD_v2 +#endif +cuMemcpyHtoD_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3544 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3544] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 443 & 0xffff +#if 443 > 0xffff + movk ip0, 443 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoD_v2_ptds + .p2align 4 + .type cuMemcpyHtoD_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoD_v2_ptds +#endif +cuMemcpyHtoD_v2_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3552 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3552] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 444 & 0xffff +#if 444 > 0xffff + movk ip0, 444 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyPeer + .p2align 4 + .type cuMemcpyPeer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyPeer +#endif +cuMemcpyPeer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3560 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3560] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 445 & 0xffff +#if 445 > 0xffff + movk ip0, 445 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyPeerAsync + .p2align 4 + .type cuMemcpyPeerAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyPeerAsync +#endif +cuMemcpyPeerAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3568 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3568] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 446 & 0xffff +#if 446 > 0xffff + movk ip0, 446 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyPeerAsync_ptsz + .p2align 4 + .type cuMemcpyPeerAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyPeerAsync_ptsz +#endif +cuMemcpyPeerAsync_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3576 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3576] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 447 & 0xffff +#if 447 > 0xffff + movk ip0, 447 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyPeer_ptds + .p2align 4 + .type cuMemcpyPeer_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyPeer_ptds +#endif +cuMemcpyPeer_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3584 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3584] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 448 & 0xffff +#if 448 > 0xffff + movk ip0, 448 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy_ptds + .p2align 4 + .type cuMemcpy_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy_ptds +#endif +cuMemcpy_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3592 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3592] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 449 & 0xffff +#if 449 > 0xffff + movk ip0, 449 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD16 + .p2align 4 + .type cuMemsetD16, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD16 +#endif +cuMemsetD16: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3600 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3600] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 450 & 0xffff +#if 450 > 0xffff + movk ip0, 450 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD16Async + .p2align 4 + .type cuMemsetD16Async, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD16Async +#endif +cuMemsetD16Async: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3608 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3608] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 451 & 0xffff +#if 451 > 0xffff + movk ip0, 451 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD16Async_ptsz + .p2align 4 + .type cuMemsetD16Async_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD16Async_ptsz +#endif +cuMemsetD16Async_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3616 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3616] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 452 & 0xffff +#if 452 > 0xffff + movk ip0, 452 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD16_v2 + .p2align 4 + .type cuMemsetD16_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD16_v2 +#endif +cuMemsetD16_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3624 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3624] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 453 & 0xffff +#if 453 > 0xffff + movk ip0, 453 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD16_v2_ptds + .p2align 4 + .type cuMemsetD16_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD16_v2_ptds +#endif +cuMemsetD16_v2_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3632 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3632] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 454 & 0xffff +#if 454 > 0xffff + movk ip0, 454 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D16 + .p2align 4 + .type cuMemsetD2D16, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D16 +#endif +cuMemsetD2D16: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3640 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3640] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 455 & 0xffff +#if 455 > 0xffff + movk ip0, 455 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D16Async + .p2align 4 + .type cuMemsetD2D16Async, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D16Async +#endif +cuMemsetD2D16Async: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3648 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3648] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 456 & 0xffff +#if 456 > 0xffff + movk ip0, 456 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D16Async_ptsz + .p2align 4 + .type cuMemsetD2D16Async_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D16Async_ptsz +#endif +cuMemsetD2D16Async_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3656 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3656] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 457 & 0xffff +#if 457 > 0xffff + movk ip0, 457 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D16_v2 + .p2align 4 + .type cuMemsetD2D16_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D16_v2 +#endif +cuMemsetD2D16_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3664 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3664] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 458 & 0xffff +#if 458 > 0xffff + movk ip0, 458 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D16_v2_ptds + .p2align 4 + .type cuMemsetD2D16_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D16_v2_ptds +#endif +cuMemsetD2D16_v2_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3672 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3672] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 459 & 0xffff +#if 459 > 0xffff + movk ip0, 459 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D32 + .p2align 4 + .type cuMemsetD2D32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D32 +#endif +cuMemsetD2D32: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3680 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3680] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 460 & 0xffff +#if 460 > 0xffff + movk ip0, 460 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D32Async + .p2align 4 + .type cuMemsetD2D32Async, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D32Async +#endif +cuMemsetD2D32Async: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3688 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3688] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 461 & 0xffff +#if 461 > 0xffff + movk ip0, 461 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D32Async_ptsz + .p2align 4 + .type cuMemsetD2D32Async_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D32Async_ptsz +#endif +cuMemsetD2D32Async_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3696 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3696] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 462 & 0xffff +#if 462 > 0xffff + movk ip0, 462 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D32_v2 + .p2align 4 + .type cuMemsetD2D32_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D32_v2 +#endif +cuMemsetD2D32_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3704 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3704] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 463 & 0xffff +#if 463 > 0xffff + movk ip0, 463 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D32_v2_ptds + .p2align 4 + .type cuMemsetD2D32_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D32_v2_ptds +#endif +cuMemsetD2D32_v2_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3712 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3712] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 464 & 0xffff +#if 464 > 0xffff + movk ip0, 464 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D8 + .p2align 4 + .type cuMemsetD2D8, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D8 +#endif +cuMemsetD2D8: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3720 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3720] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 465 & 0xffff +#if 465 > 0xffff + movk ip0, 465 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D8Async + .p2align 4 + .type cuMemsetD2D8Async, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D8Async +#endif +cuMemsetD2D8Async: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3728 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3728] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 466 & 0xffff +#if 466 > 0xffff + movk ip0, 466 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D8Async_ptsz + .p2align 4 + .type cuMemsetD2D8Async_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D8Async_ptsz +#endif +cuMemsetD2D8Async_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3736 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3736] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 467 & 0xffff +#if 467 > 0xffff + movk ip0, 467 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D8_v2 + .p2align 4 + .type cuMemsetD2D8_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D8_v2 +#endif +cuMemsetD2D8_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3744 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3744] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 468 & 0xffff +#if 468 > 0xffff + movk ip0, 468 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D8_v2_ptds + .p2align 4 + .type cuMemsetD2D8_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D8_v2_ptds +#endif +cuMemsetD2D8_v2_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3752 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3752] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 469 & 0xffff +#if 469 > 0xffff + movk ip0, 469 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD32 + .p2align 4 + .type cuMemsetD32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD32 +#endif +cuMemsetD32: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3760 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3760] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 470 & 0xffff +#if 470 > 0xffff + movk ip0, 470 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD32Async + .p2align 4 + .type cuMemsetD32Async, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD32Async +#endif +cuMemsetD32Async: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3768 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3768] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 471 & 0xffff +#if 471 > 0xffff + movk ip0, 471 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD32Async_ptsz + .p2align 4 + .type cuMemsetD32Async_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD32Async_ptsz +#endif +cuMemsetD32Async_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3776 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3776] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 472 & 0xffff +#if 472 > 0xffff + movk ip0, 472 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD32_v2 + .p2align 4 + .type cuMemsetD32_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD32_v2 +#endif +cuMemsetD32_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3784 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3784] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 473 & 0xffff +#if 473 > 0xffff + movk ip0, 473 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD32_v2_ptds + .p2align 4 + .type cuMemsetD32_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD32_v2_ptds +#endif +cuMemsetD32_v2_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3792 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3792] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 474 & 0xffff +#if 474 > 0xffff + movk ip0, 474 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD8 + .p2align 4 + .type cuMemsetD8, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD8 +#endif +cuMemsetD8: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3800 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3800] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 475 & 0xffff +#if 475 > 0xffff + movk ip0, 475 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD8Async + .p2align 4 + .type cuMemsetD8Async, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD8Async +#endif +cuMemsetD8Async: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3808 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3808] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 476 & 0xffff +#if 476 > 0xffff + movk ip0, 476 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD8Async_ptsz + .p2align 4 + .type cuMemsetD8Async_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD8Async_ptsz +#endif +cuMemsetD8Async_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3816 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3816] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 477 & 0xffff +#if 477 > 0xffff + movk ip0, 477 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD8_v2 + .p2align 4 + .type cuMemsetD8_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD8_v2 +#endif +cuMemsetD8_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3824 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3824] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 478 & 0xffff +#if 478 > 0xffff + movk ip0, 478 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD8_v2_ptds + .p2align 4 + .type cuMemsetD8_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD8_v2_ptds +#endif +cuMemsetD8_v2_ptds: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3832 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3832] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 479 & 0xffff +#if 479 > 0xffff + movk ip0, 479 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMipmappedArrayCreate + .p2align 4 + .type cuMipmappedArrayCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMipmappedArrayCreate +#endif +cuMipmappedArrayCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3840 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3840] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 480 & 0xffff +#if 480 > 0xffff + movk ip0, 480 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMipmappedArrayDestroy + .p2align 4 + .type cuMipmappedArrayDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMipmappedArrayDestroy +#endif +cuMipmappedArrayDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3848 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3848] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 481 & 0xffff +#if 481 > 0xffff + movk ip0, 481 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMipmappedArrayGetLevel + .p2align 4 + .type cuMipmappedArrayGetLevel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMipmappedArrayGetLevel +#endif +cuMipmappedArrayGetLevel: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3856 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3856] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 482 & 0xffff +#if 482 > 0xffff + movk ip0, 482 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMipmappedArrayGetMemoryRequirements + .p2align 4 + .type cuMipmappedArrayGetMemoryRequirements, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMipmappedArrayGetMemoryRequirements +#endif +cuMipmappedArrayGetMemoryRequirements: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3864 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3864] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 483 & 0xffff +#if 483 > 0xffff + movk ip0, 483 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMipmappedArrayGetSparseProperties + .p2align 4 + .type cuMipmappedArrayGetSparseProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMipmappedArrayGetSparseProperties +#endif +cuMipmappedArrayGetSparseProperties: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3872 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3872] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 484 & 0xffff +#if 484 > 0xffff + movk ip0, 484 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleEnumerateFunctions + .p2align 4 + .type cuModuleEnumerateFunctions, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleEnumerateFunctions +#endif +cuModuleEnumerateFunctions: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3880 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3880] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 485 & 0xffff +#if 485 > 0xffff + movk ip0, 485 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleGetFunction + .p2align 4 + .type cuModuleGetFunction, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleGetFunction +#endif +cuModuleGetFunction: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3888 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3888] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 486 & 0xffff +#if 486 > 0xffff + movk ip0, 486 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleGetFunctionCount + .p2align 4 + .type cuModuleGetFunctionCount, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleGetFunctionCount +#endif +cuModuleGetFunctionCount: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3896 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3896] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 487 & 0xffff +#if 487 > 0xffff + movk ip0, 487 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleGetGlobal + .p2align 4 + .type cuModuleGetGlobal, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleGetGlobal +#endif +cuModuleGetGlobal: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3904 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3904] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 488 & 0xffff +#if 488 > 0xffff + movk ip0, 488 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleGetGlobal_v2 + .p2align 4 + .type cuModuleGetGlobal_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleGetGlobal_v2 +#endif +cuModuleGetGlobal_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3912 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3912] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 489 & 0xffff +#if 489 > 0xffff + movk ip0, 489 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleGetLoadingMode + .p2align 4 + .type cuModuleGetLoadingMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleGetLoadingMode +#endif +cuModuleGetLoadingMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3920 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3920] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 490 & 0xffff +#if 490 > 0xffff + movk ip0, 490 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleGetSurfRef + .p2align 4 + .type cuModuleGetSurfRef, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleGetSurfRef +#endif +cuModuleGetSurfRef: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3928 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3928] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 491 & 0xffff +#if 491 > 0xffff + movk ip0, 491 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleGetTexRef + .p2align 4 + .type cuModuleGetTexRef, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleGetTexRef +#endif +cuModuleGetTexRef: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3936 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3936] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 492 & 0xffff +#if 492 > 0xffff + movk ip0, 492 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleLoad + .p2align 4 + .type cuModuleLoad, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleLoad +#endif +cuModuleLoad: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3944 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3944] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 493 & 0xffff +#if 493 > 0xffff + movk ip0, 493 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleLoadData + .p2align 4 + .type cuModuleLoadData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleLoadData +#endif +cuModuleLoadData: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3952 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3952] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 494 & 0xffff +#if 494 > 0xffff + movk ip0, 494 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleLoadDataEx + .p2align 4 + .type cuModuleLoadDataEx, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleLoadDataEx +#endif +cuModuleLoadDataEx: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3960 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3960] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 495 & 0xffff +#if 495 > 0xffff + movk ip0, 495 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleLoadFatBinary + .p2align 4 + .type cuModuleLoadFatBinary, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleLoadFatBinary +#endif +cuModuleLoadFatBinary: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3968 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3968] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 496 & 0xffff +#if 496 > 0xffff + movk ip0, 496 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleUnload + .p2align 4 + .type cuModuleUnload, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleUnload +#endif +cuModuleUnload: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3976 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3976] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 497 & 0xffff +#if 497 > 0xffff + movk ip0, 497 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMulticastAddDevice + .p2align 4 + .type cuMulticastAddDevice, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMulticastAddDevice +#endif +cuMulticastAddDevice: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3984 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3984] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 498 & 0xffff +#if 498 > 0xffff + movk ip0, 498 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMulticastBindAddr + .p2align 4 + .type cuMulticastBindAddr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMulticastBindAddr +#endif +cuMulticastBindAddr: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+3992 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+3992] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 499 & 0xffff +#if 499 > 0xffff + movk ip0, 499 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMulticastBindMem + .p2align 4 + .type cuMulticastBindMem, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMulticastBindMem +#endif +cuMulticastBindMem: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4000 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4000] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 500 & 0xffff +#if 500 > 0xffff + movk ip0, 500 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMulticastCreate + .p2align 4 + .type cuMulticastCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMulticastCreate +#endif +cuMulticastCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4008 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4008] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 501 & 0xffff +#if 501 > 0xffff + movk ip0, 501 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMulticastGetGranularity + .p2align 4 + .type cuMulticastGetGranularity, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMulticastGetGranularity +#endif +cuMulticastGetGranularity: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4016 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4016] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 502 & 0xffff +#if 502 > 0xffff + movk ip0, 502 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMulticastUnbind + .p2align 4 + .type cuMulticastUnbind, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMulticastUnbind +#endif +cuMulticastUnbind: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4024 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4024] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 503 & 0xffff +#if 503 > 0xffff + movk ip0, 503 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuOccupancyAvailableDynamicSMemPerBlock + .p2align 4 + .type cuOccupancyAvailableDynamicSMemPerBlock, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuOccupancyAvailableDynamicSMemPerBlock +#endif +cuOccupancyAvailableDynamicSMemPerBlock: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4032 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4032] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 504 & 0xffff +#if 504 > 0xffff + movk ip0, 504 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuOccupancyMaxActiveBlocksPerMultiprocessor + .p2align 4 + .type cuOccupancyMaxActiveBlocksPerMultiprocessor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuOccupancyMaxActiveBlocksPerMultiprocessor +#endif +cuOccupancyMaxActiveBlocksPerMultiprocessor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4040 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4040] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 505 & 0xffff +#if 505 > 0xffff + movk ip0, 505 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuOccupancyMaxActiveBlocksPerMultiprocessorWithFlags + .p2align 4 + .type cuOccupancyMaxActiveBlocksPerMultiprocessorWithFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuOccupancyMaxActiveBlocksPerMultiprocessorWithFlags +#endif +cuOccupancyMaxActiveBlocksPerMultiprocessorWithFlags: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4048 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4048] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 506 & 0xffff +#if 506 > 0xffff + movk ip0, 506 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuOccupancyMaxActiveClusters + .p2align 4 + .type cuOccupancyMaxActiveClusters, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuOccupancyMaxActiveClusters +#endif +cuOccupancyMaxActiveClusters: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4056 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4056] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 507 & 0xffff +#if 507 > 0xffff + movk ip0, 507 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuOccupancyMaxPotentialBlockSize + .p2align 4 + .type cuOccupancyMaxPotentialBlockSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuOccupancyMaxPotentialBlockSize +#endif +cuOccupancyMaxPotentialBlockSize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4064 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4064] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 508 & 0xffff +#if 508 > 0xffff + movk ip0, 508 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuOccupancyMaxPotentialBlockSizeWithFlags + .p2align 4 + .type cuOccupancyMaxPotentialBlockSizeWithFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuOccupancyMaxPotentialBlockSizeWithFlags +#endif +cuOccupancyMaxPotentialBlockSizeWithFlags: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4072 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4072] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 509 & 0xffff +#if 509 > 0xffff + movk ip0, 509 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuOccupancyMaxPotentialClusterSize + .p2align 4 + .type cuOccupancyMaxPotentialClusterSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuOccupancyMaxPotentialClusterSize +#endif +cuOccupancyMaxPotentialClusterSize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4080 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4080] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 510 & 0xffff +#if 510 > 0xffff + movk ip0, 510 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuParamSetSize + .p2align 4 + .type cuParamSetSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuParamSetSize +#endif +cuParamSetSize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4088 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4088] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 511 & 0xffff +#if 511 > 0xffff + movk ip0, 511 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuParamSetTexRef + .p2align 4 + .type cuParamSetTexRef, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuParamSetTexRef +#endif +cuParamSetTexRef: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4096 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4096] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 512 & 0xffff +#if 512 > 0xffff + movk ip0, 512 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuParamSetf + .p2align 4 + .type cuParamSetf, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuParamSetf +#endif +cuParamSetf: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4104 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 513 & 0xffff +#if 513 > 0xffff + movk ip0, 513 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuParamSeti + .p2align 4 + .type cuParamSeti, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuParamSeti +#endif +cuParamSeti: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4112 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 514 & 0xffff +#if 514 > 0xffff + movk ip0, 514 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuParamSetv + .p2align 4 + .type cuParamSetv, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuParamSetv +#endif +cuParamSetv: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4120 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 515 & 0xffff +#if 515 > 0xffff + movk ip0, 515 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuPointerGetAttribute + .p2align 4 + .type cuPointerGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuPointerGetAttribute +#endif +cuPointerGetAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4128 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 516 & 0xffff +#if 516 > 0xffff + movk ip0, 516 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuPointerGetAttributes + .p2align 4 + .type cuPointerGetAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuPointerGetAttributes +#endif +cuPointerGetAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4136 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 517 & 0xffff +#if 517 > 0xffff + movk ip0, 517 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuPointerSetAttribute + .p2align 4 + .type cuPointerSetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuPointerSetAttribute +#endif +cuPointerSetAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4144 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 518 & 0xffff +#if 518 > 0xffff + movk ip0, 518 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuProfilerInitialize + .p2align 4 + .type cuProfilerInitialize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuProfilerInitialize +#endif +cuProfilerInitialize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4152 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 519 & 0xffff +#if 519 > 0xffff + movk ip0, 519 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuProfilerStart + .p2align 4 + .type cuProfilerStart, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuProfilerStart +#endif +cuProfilerStart: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4160 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 520 & 0xffff +#if 520 > 0xffff + movk ip0, 520 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuProfilerStop + .p2align 4 + .type cuProfilerStop, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuProfilerStop +#endif +cuProfilerStop: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4168 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 521 & 0xffff +#if 521 > 0xffff + movk ip0, 521 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuSignalExternalSemaphoresAsync + .p2align 4 + .type cuSignalExternalSemaphoresAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuSignalExternalSemaphoresAsync +#endif +cuSignalExternalSemaphoresAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4176 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 522 & 0xffff +#if 522 > 0xffff + movk ip0, 522 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuSignalExternalSemaphoresAsync_ptsz + .p2align 4 + .type cuSignalExternalSemaphoresAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuSignalExternalSemaphoresAsync_ptsz +#endif +cuSignalExternalSemaphoresAsync_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4184 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 523 & 0xffff +#if 523 > 0xffff + movk ip0, 523 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamAddCallback + .p2align 4 + .type cuStreamAddCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamAddCallback +#endif +cuStreamAddCallback: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4192 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 524 & 0xffff +#if 524 > 0xffff + movk ip0, 524 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamAddCallback_ptsz + .p2align 4 + .type cuStreamAddCallback_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamAddCallback_ptsz +#endif +cuStreamAddCallback_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4200 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 525 & 0xffff +#if 525 > 0xffff + movk ip0, 525 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamAttachMemAsync + .p2align 4 + .type cuStreamAttachMemAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamAttachMemAsync +#endif +cuStreamAttachMemAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4208 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 526 & 0xffff +#if 526 > 0xffff + movk ip0, 526 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamAttachMemAsync_ptsz + .p2align 4 + .type cuStreamAttachMemAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamAttachMemAsync_ptsz +#endif +cuStreamAttachMemAsync_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4216 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 527 & 0xffff +#if 527 > 0xffff + movk ip0, 527 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBatchMemOp + .p2align 4 + .type cuStreamBatchMemOp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBatchMemOp +#endif +cuStreamBatchMemOp: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4224 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 528 & 0xffff +#if 528 > 0xffff + movk ip0, 528 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBatchMemOp_ptsz + .p2align 4 + .type cuStreamBatchMemOp_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBatchMemOp_ptsz +#endif +cuStreamBatchMemOp_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4232 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 529 & 0xffff +#if 529 > 0xffff + movk ip0, 529 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBatchMemOp_v2 + .p2align 4 + .type cuStreamBatchMemOp_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBatchMemOp_v2 +#endif +cuStreamBatchMemOp_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4240 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 530 & 0xffff +#if 530 > 0xffff + movk ip0, 530 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBatchMemOp_v2_ptsz + .p2align 4 + .type cuStreamBatchMemOp_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBatchMemOp_v2_ptsz +#endif +cuStreamBatchMemOp_v2_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4248 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 531 & 0xffff +#if 531 > 0xffff + movk ip0, 531 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBeginCapture + .p2align 4 + .type cuStreamBeginCapture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBeginCapture +#endif +cuStreamBeginCapture: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4256 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 532 & 0xffff +#if 532 > 0xffff + movk ip0, 532 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBeginCaptureToGraph + .p2align 4 + .type cuStreamBeginCaptureToGraph, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBeginCaptureToGraph +#endif +cuStreamBeginCaptureToGraph: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4264 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 533 & 0xffff +#if 533 > 0xffff + movk ip0, 533 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBeginCaptureToGraph_ptsz + .p2align 4 + .type cuStreamBeginCaptureToGraph_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBeginCaptureToGraph_ptsz +#endif +cuStreamBeginCaptureToGraph_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4272 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 534 & 0xffff +#if 534 > 0xffff + movk ip0, 534 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBeginCapture_ptsz + .p2align 4 + .type cuStreamBeginCapture_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBeginCapture_ptsz +#endif +cuStreamBeginCapture_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4280 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 535 & 0xffff +#if 535 > 0xffff + movk ip0, 535 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBeginCapture_v2 + .p2align 4 + .type cuStreamBeginCapture_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBeginCapture_v2 +#endif +cuStreamBeginCapture_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4288 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 536 & 0xffff +#if 536 > 0xffff + movk ip0, 536 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBeginCapture_v2_ptsz + .p2align 4 + .type cuStreamBeginCapture_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBeginCapture_v2_ptsz +#endif +cuStreamBeginCapture_v2_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4296 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 537 & 0xffff +#if 537 > 0xffff + movk ip0, 537 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamCopyAttributes + .p2align 4 + .type cuStreamCopyAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamCopyAttributes +#endif +cuStreamCopyAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4304 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 538 & 0xffff +#if 538 > 0xffff + movk ip0, 538 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamCopyAttributes_ptsz + .p2align 4 + .type cuStreamCopyAttributes_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamCopyAttributes_ptsz +#endif +cuStreamCopyAttributes_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4312 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 539 & 0xffff +#if 539 > 0xffff + movk ip0, 539 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamCreate + .p2align 4 + .type cuStreamCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamCreate +#endif +cuStreamCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4320 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 540 & 0xffff +#if 540 > 0xffff + movk ip0, 540 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamCreateWithPriority + .p2align 4 + .type cuStreamCreateWithPriority, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamCreateWithPriority +#endif +cuStreamCreateWithPriority: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4328 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 541 & 0xffff +#if 541 > 0xffff + movk ip0, 541 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamDestroy + .p2align 4 + .type cuStreamDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamDestroy +#endif +cuStreamDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4336 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 542 & 0xffff +#if 542 > 0xffff + movk ip0, 542 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamDestroy_v2 + .p2align 4 + .type cuStreamDestroy_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamDestroy_v2 +#endif +cuStreamDestroy_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4344 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 543 & 0xffff +#if 543 > 0xffff + movk ip0, 543 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamEndCapture + .p2align 4 + .type cuStreamEndCapture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamEndCapture +#endif +cuStreamEndCapture: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4352 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 544 & 0xffff +#if 544 > 0xffff + movk ip0, 544 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamEndCapture_ptsz + .p2align 4 + .type cuStreamEndCapture_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamEndCapture_ptsz +#endif +cuStreamEndCapture_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4360 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 545 & 0xffff +#if 545 > 0xffff + movk ip0, 545 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetAttribute + .p2align 4 + .type cuStreamGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetAttribute +#endif +cuStreamGetAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4368 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 546 & 0xffff +#if 546 > 0xffff + movk ip0, 546 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetAttribute_ptsz + .p2align 4 + .type cuStreamGetAttribute_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetAttribute_ptsz +#endif +cuStreamGetAttribute_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4376 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 547 & 0xffff +#if 547 > 0xffff + movk ip0, 547 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCaptureInfo + .p2align 4 + .type cuStreamGetCaptureInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCaptureInfo +#endif +cuStreamGetCaptureInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4384 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 548 & 0xffff +#if 548 > 0xffff + movk ip0, 548 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCaptureInfo_ptsz + .p2align 4 + .type cuStreamGetCaptureInfo_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCaptureInfo_ptsz +#endif +cuStreamGetCaptureInfo_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4392 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 549 & 0xffff +#if 549 > 0xffff + movk ip0, 549 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCaptureInfo_v2 + .p2align 4 + .type cuStreamGetCaptureInfo_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCaptureInfo_v2 +#endif +cuStreamGetCaptureInfo_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4400 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 550 & 0xffff +#if 550 > 0xffff + movk ip0, 550 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCaptureInfo_v2_ptsz + .p2align 4 + .type cuStreamGetCaptureInfo_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCaptureInfo_v2_ptsz +#endif +cuStreamGetCaptureInfo_v2_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4408 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 551 & 0xffff +#if 551 > 0xffff + movk ip0, 551 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCaptureInfo_v3 + .p2align 4 + .type cuStreamGetCaptureInfo_v3, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCaptureInfo_v3 +#endif +cuStreamGetCaptureInfo_v3: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4416 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 552 & 0xffff +#if 552 > 0xffff + movk ip0, 552 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCaptureInfo_v3_ptsz + .p2align 4 + .type cuStreamGetCaptureInfo_v3_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCaptureInfo_v3_ptsz +#endif +cuStreamGetCaptureInfo_v3_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4424 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 553 & 0xffff +#if 553 > 0xffff + movk ip0, 553 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCtx + .p2align 4 + .type cuStreamGetCtx, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCtx +#endif +cuStreamGetCtx: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4432 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 554 & 0xffff +#if 554 > 0xffff + movk ip0, 554 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCtx_ptsz + .p2align 4 + .type cuStreamGetCtx_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCtx_ptsz +#endif +cuStreamGetCtx_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4440 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 555 & 0xffff +#if 555 > 0xffff + movk ip0, 555 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCtx_v2 + .p2align 4 + .type cuStreamGetCtx_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCtx_v2 +#endif +cuStreamGetCtx_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4448 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 556 & 0xffff +#if 556 > 0xffff + movk ip0, 556 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCtx_v2_ptsz + .p2align 4 + .type cuStreamGetCtx_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCtx_v2_ptsz +#endif +cuStreamGetCtx_v2_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4456 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4456] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 557 & 0xffff +#if 557 > 0xffff + movk ip0, 557 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetDevice + .p2align 4 + .type cuStreamGetDevice, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetDevice +#endif +cuStreamGetDevice: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4464 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4464] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 558 & 0xffff +#if 558 > 0xffff + movk ip0, 558 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetDevice_ptsz + .p2align 4 + .type cuStreamGetDevice_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetDevice_ptsz +#endif +cuStreamGetDevice_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4472 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4472] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 559 & 0xffff +#if 559 > 0xffff + movk ip0, 559 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetFlags + .p2align 4 + .type cuStreamGetFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetFlags +#endif +cuStreamGetFlags: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4480 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4480] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 560 & 0xffff +#if 560 > 0xffff + movk ip0, 560 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetFlags_ptsz + .p2align 4 + .type cuStreamGetFlags_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetFlags_ptsz +#endif +cuStreamGetFlags_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4488 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4488] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 561 & 0xffff +#if 561 > 0xffff + movk ip0, 561 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetGreenCtx + .p2align 4 + .type cuStreamGetGreenCtx, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetGreenCtx +#endif +cuStreamGetGreenCtx: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4496 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4496] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 562 & 0xffff +#if 562 > 0xffff + movk ip0, 562 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetId + .p2align 4 + .type cuStreamGetId, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetId +#endif +cuStreamGetId: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4504 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4504] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 563 & 0xffff +#if 563 > 0xffff + movk ip0, 563 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetId_ptsz + .p2align 4 + .type cuStreamGetId_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetId_ptsz +#endif +cuStreamGetId_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4512 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4512] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 564 & 0xffff +#if 564 > 0xffff + movk ip0, 564 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetPriority + .p2align 4 + .type cuStreamGetPriority, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetPriority +#endif +cuStreamGetPriority: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4520 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4520] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 565 & 0xffff +#if 565 > 0xffff + movk ip0, 565 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetPriority_ptsz + .p2align 4 + .type cuStreamGetPriority_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetPriority_ptsz +#endif +cuStreamGetPriority_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4528 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4528] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 566 & 0xffff +#if 566 > 0xffff + movk ip0, 566 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamIsCapturing + .p2align 4 + .type cuStreamIsCapturing, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamIsCapturing +#endif +cuStreamIsCapturing: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4536 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4536] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 567 & 0xffff +#if 567 > 0xffff + movk ip0, 567 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamIsCapturing_ptsz + .p2align 4 + .type cuStreamIsCapturing_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamIsCapturing_ptsz +#endif +cuStreamIsCapturing_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4544 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4544] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 568 & 0xffff +#if 568 > 0xffff + movk ip0, 568 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamQuery + .p2align 4 + .type cuStreamQuery, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamQuery +#endif +cuStreamQuery: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4552 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4552] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 569 & 0xffff +#if 569 > 0xffff + movk ip0, 569 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamQuery_ptsz + .p2align 4 + .type cuStreamQuery_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamQuery_ptsz +#endif +cuStreamQuery_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4560 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4560] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 570 & 0xffff +#if 570 > 0xffff + movk ip0, 570 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamSetAttribute + .p2align 4 + .type cuStreamSetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamSetAttribute +#endif +cuStreamSetAttribute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4568 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4568] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 571 & 0xffff +#if 571 > 0xffff + movk ip0, 571 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamSetAttribute_ptsz + .p2align 4 + .type cuStreamSetAttribute_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamSetAttribute_ptsz +#endif +cuStreamSetAttribute_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4576 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4576] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 572 & 0xffff +#if 572 > 0xffff + movk ip0, 572 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamSynchronize + .p2align 4 + .type cuStreamSynchronize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamSynchronize +#endif +cuStreamSynchronize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4584 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4584] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 573 & 0xffff +#if 573 > 0xffff + movk ip0, 573 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamSynchronize_ptsz + .p2align 4 + .type cuStreamSynchronize_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamSynchronize_ptsz +#endif +cuStreamSynchronize_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4592 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4592] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 574 & 0xffff +#if 574 > 0xffff + movk ip0, 574 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamUpdateCaptureDependencies + .p2align 4 + .type cuStreamUpdateCaptureDependencies, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamUpdateCaptureDependencies +#endif +cuStreamUpdateCaptureDependencies: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4600 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4600] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 575 & 0xffff +#if 575 > 0xffff + movk ip0, 575 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamUpdateCaptureDependencies_ptsz + .p2align 4 + .type cuStreamUpdateCaptureDependencies_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamUpdateCaptureDependencies_ptsz +#endif +cuStreamUpdateCaptureDependencies_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4608 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4608] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 576 & 0xffff +#if 576 > 0xffff + movk ip0, 576 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamUpdateCaptureDependencies_v2 + .p2align 4 + .type cuStreamUpdateCaptureDependencies_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamUpdateCaptureDependencies_v2 +#endif +cuStreamUpdateCaptureDependencies_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4616 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4616] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 577 & 0xffff +#if 577 > 0xffff + movk ip0, 577 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamUpdateCaptureDependencies_v2_ptsz + .p2align 4 + .type cuStreamUpdateCaptureDependencies_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamUpdateCaptureDependencies_v2_ptsz +#endif +cuStreamUpdateCaptureDependencies_v2_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4624 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4624] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 578 & 0xffff +#if 578 > 0xffff + movk ip0, 578 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitEvent + .p2align 4 + .type cuStreamWaitEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitEvent +#endif +cuStreamWaitEvent: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4632 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4632] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 579 & 0xffff +#if 579 > 0xffff + movk ip0, 579 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitEvent_ptsz + .p2align 4 + .type cuStreamWaitEvent_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitEvent_ptsz +#endif +cuStreamWaitEvent_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4640 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4640] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 580 & 0xffff +#if 580 > 0xffff + movk ip0, 580 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitValue32 + .p2align 4 + .type cuStreamWaitValue32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitValue32 +#endif +cuStreamWaitValue32: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4648 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4648] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 581 & 0xffff +#if 581 > 0xffff + movk ip0, 581 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitValue32_ptsz + .p2align 4 + .type cuStreamWaitValue32_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitValue32_ptsz +#endif +cuStreamWaitValue32_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4656 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4656] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 582 & 0xffff +#if 582 > 0xffff + movk ip0, 582 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitValue32_v2 + .p2align 4 + .type cuStreamWaitValue32_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitValue32_v2 +#endif +cuStreamWaitValue32_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4664 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4664] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 583 & 0xffff +#if 583 > 0xffff + movk ip0, 583 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitValue32_v2_ptsz + .p2align 4 + .type cuStreamWaitValue32_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitValue32_v2_ptsz +#endif +cuStreamWaitValue32_v2_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4672 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4672] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 584 & 0xffff +#if 584 > 0xffff + movk ip0, 584 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitValue64 + .p2align 4 + .type cuStreamWaitValue64, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitValue64 +#endif +cuStreamWaitValue64: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4680 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4680] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 585 & 0xffff +#if 585 > 0xffff + movk ip0, 585 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitValue64_ptsz + .p2align 4 + .type cuStreamWaitValue64_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitValue64_ptsz +#endif +cuStreamWaitValue64_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4688 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4688] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 586 & 0xffff +#if 586 > 0xffff + movk ip0, 586 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitValue64_v2 + .p2align 4 + .type cuStreamWaitValue64_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitValue64_v2 +#endif +cuStreamWaitValue64_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4696 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4696] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 587 & 0xffff +#if 587 > 0xffff + movk ip0, 587 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitValue64_v2_ptsz + .p2align 4 + .type cuStreamWaitValue64_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitValue64_v2_ptsz +#endif +cuStreamWaitValue64_v2_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4704 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4704] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 588 & 0xffff +#if 588 > 0xffff + movk ip0, 588 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWriteValue32 + .p2align 4 + .type cuStreamWriteValue32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWriteValue32 +#endif +cuStreamWriteValue32: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4712 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4712] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 589 & 0xffff +#if 589 > 0xffff + movk ip0, 589 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWriteValue32_ptsz + .p2align 4 + .type cuStreamWriteValue32_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWriteValue32_ptsz +#endif +cuStreamWriteValue32_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4720 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4720] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 590 & 0xffff +#if 590 > 0xffff + movk ip0, 590 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWriteValue32_v2 + .p2align 4 + .type cuStreamWriteValue32_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWriteValue32_v2 +#endif +cuStreamWriteValue32_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4728 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4728] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 591 & 0xffff +#if 591 > 0xffff + movk ip0, 591 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWriteValue32_v2_ptsz + .p2align 4 + .type cuStreamWriteValue32_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWriteValue32_v2_ptsz +#endif +cuStreamWriteValue32_v2_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4736 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4736] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 592 & 0xffff +#if 592 > 0xffff + movk ip0, 592 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWriteValue64 + .p2align 4 + .type cuStreamWriteValue64, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWriteValue64 +#endif +cuStreamWriteValue64: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4744 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4744] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 593 & 0xffff +#if 593 > 0xffff + movk ip0, 593 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWriteValue64_ptsz + .p2align 4 + .type cuStreamWriteValue64_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWriteValue64_ptsz +#endif +cuStreamWriteValue64_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4752 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4752] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 594 & 0xffff +#if 594 > 0xffff + movk ip0, 594 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWriteValue64_v2 + .p2align 4 + .type cuStreamWriteValue64_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWriteValue64_v2 +#endif +cuStreamWriteValue64_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4760 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4760] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 595 & 0xffff +#if 595 > 0xffff + movk ip0, 595 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWriteValue64_v2_ptsz + .p2align 4 + .type cuStreamWriteValue64_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWriteValue64_v2_ptsz +#endif +cuStreamWriteValue64_v2_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4768 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4768] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 596 & 0xffff +#if 596 > 0xffff + movk ip0, 596 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuSurfObjectCreate + .p2align 4 + .type cuSurfObjectCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuSurfObjectCreate +#endif +cuSurfObjectCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4776 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4776] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 597 & 0xffff +#if 597 > 0xffff + movk ip0, 597 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuSurfObjectDestroy + .p2align 4 + .type cuSurfObjectDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuSurfObjectDestroy +#endif +cuSurfObjectDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4784 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4784] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 598 & 0xffff +#if 598 > 0xffff + movk ip0, 598 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuSurfObjectGetResourceDesc + .p2align 4 + .type cuSurfObjectGetResourceDesc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuSurfObjectGetResourceDesc +#endif +cuSurfObjectGetResourceDesc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4792 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4792] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 599 & 0xffff +#if 599 > 0xffff + movk ip0, 599 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuSurfRefGetArray + .p2align 4 + .type cuSurfRefGetArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuSurfRefGetArray +#endif +cuSurfRefGetArray: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4800 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4800] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 600 & 0xffff +#if 600 > 0xffff + movk ip0, 600 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuSurfRefSetArray + .p2align 4 + .type cuSurfRefSetArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuSurfRefSetArray +#endif +cuSurfRefSetArray: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4808 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4808] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 601 & 0xffff +#if 601 > 0xffff + movk ip0, 601 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTensorMapEncodeIm2col + .p2align 4 + .type cuTensorMapEncodeIm2col, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTensorMapEncodeIm2col +#endif +cuTensorMapEncodeIm2col: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4816 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4816] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 602 & 0xffff +#if 602 > 0xffff + movk ip0, 602 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTensorMapEncodeIm2colWide + .p2align 4 + .type cuTensorMapEncodeIm2colWide, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTensorMapEncodeIm2colWide +#endif +cuTensorMapEncodeIm2colWide: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4824 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4824] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 603 & 0xffff +#if 603 > 0xffff + movk ip0, 603 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTensorMapEncodeTiled + .p2align 4 + .type cuTensorMapEncodeTiled, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTensorMapEncodeTiled +#endif +cuTensorMapEncodeTiled: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4832 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4832] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 604 & 0xffff +#if 604 > 0xffff + movk ip0, 604 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTensorMapReplaceAddress + .p2align 4 + .type cuTensorMapReplaceAddress, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTensorMapReplaceAddress +#endif +cuTensorMapReplaceAddress: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4840 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4840] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 605 & 0xffff +#if 605 > 0xffff + movk ip0, 605 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexObjectCreate + .p2align 4 + .type cuTexObjectCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexObjectCreate +#endif +cuTexObjectCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4848 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4848] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 606 & 0xffff +#if 606 > 0xffff + movk ip0, 606 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexObjectDestroy + .p2align 4 + .type cuTexObjectDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexObjectDestroy +#endif +cuTexObjectDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4856 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4856] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 607 & 0xffff +#if 607 > 0xffff + movk ip0, 607 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexObjectGetResourceDesc + .p2align 4 + .type cuTexObjectGetResourceDesc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexObjectGetResourceDesc +#endif +cuTexObjectGetResourceDesc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4864 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4864] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 608 & 0xffff +#if 608 > 0xffff + movk ip0, 608 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexObjectGetResourceViewDesc + .p2align 4 + .type cuTexObjectGetResourceViewDesc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexObjectGetResourceViewDesc +#endif +cuTexObjectGetResourceViewDesc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4872 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4872] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 609 & 0xffff +#if 609 > 0xffff + movk ip0, 609 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexObjectGetTextureDesc + .p2align 4 + .type cuTexObjectGetTextureDesc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexObjectGetTextureDesc +#endif +cuTexObjectGetTextureDesc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4880 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4880] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 610 & 0xffff +#if 610 > 0xffff + movk ip0, 610 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefCreate + .p2align 4 + .type cuTexRefCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefCreate +#endif +cuTexRefCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4888 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4888] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 611 & 0xffff +#if 611 > 0xffff + movk ip0, 611 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefDestroy + .p2align 4 + .type cuTexRefDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefDestroy +#endif +cuTexRefDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4896 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4896] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 612 & 0xffff +#if 612 > 0xffff + movk ip0, 612 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetAddress + .p2align 4 + .type cuTexRefGetAddress, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetAddress +#endif +cuTexRefGetAddress: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4904 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4904] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 613 & 0xffff +#if 613 > 0xffff + movk ip0, 613 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetAddressMode + .p2align 4 + .type cuTexRefGetAddressMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetAddressMode +#endif +cuTexRefGetAddressMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4912 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4912] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 614 & 0xffff +#if 614 > 0xffff + movk ip0, 614 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetAddress_v2 + .p2align 4 + .type cuTexRefGetAddress_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetAddress_v2 +#endif +cuTexRefGetAddress_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4920 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4920] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 615 & 0xffff +#if 615 > 0xffff + movk ip0, 615 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetArray + .p2align 4 + .type cuTexRefGetArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetArray +#endif +cuTexRefGetArray: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4928 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4928] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 616 & 0xffff +#if 616 > 0xffff + movk ip0, 616 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetBorderColor + .p2align 4 + .type cuTexRefGetBorderColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetBorderColor +#endif +cuTexRefGetBorderColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4936 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4936] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 617 & 0xffff +#if 617 > 0xffff + movk ip0, 617 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetFilterMode + .p2align 4 + .type cuTexRefGetFilterMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetFilterMode +#endif +cuTexRefGetFilterMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4944 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4944] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 618 & 0xffff +#if 618 > 0xffff + movk ip0, 618 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetFlags + .p2align 4 + .type cuTexRefGetFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetFlags +#endif +cuTexRefGetFlags: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4952 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4952] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 619 & 0xffff +#if 619 > 0xffff + movk ip0, 619 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetFormat + .p2align 4 + .type cuTexRefGetFormat, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetFormat +#endif +cuTexRefGetFormat: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4960 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4960] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 620 & 0xffff +#if 620 > 0xffff + movk ip0, 620 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetMaxAnisotropy + .p2align 4 + .type cuTexRefGetMaxAnisotropy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetMaxAnisotropy +#endif +cuTexRefGetMaxAnisotropy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4968 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4968] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 621 & 0xffff +#if 621 > 0xffff + movk ip0, 621 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetMipmapFilterMode + .p2align 4 + .type cuTexRefGetMipmapFilterMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetMipmapFilterMode +#endif +cuTexRefGetMipmapFilterMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4976 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4976] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 622 & 0xffff +#if 622 > 0xffff + movk ip0, 622 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetMipmapLevelBias + .p2align 4 + .type cuTexRefGetMipmapLevelBias, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetMipmapLevelBias +#endif +cuTexRefGetMipmapLevelBias: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4984 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4984] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 623 & 0xffff +#if 623 > 0xffff + movk ip0, 623 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetMipmapLevelClamp + .p2align 4 + .type cuTexRefGetMipmapLevelClamp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetMipmapLevelClamp +#endif +cuTexRefGetMipmapLevelClamp: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+4992 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+4992] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 624 & 0xffff +#if 624 > 0xffff + movk ip0, 624 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetMipmappedArray + .p2align 4 + .type cuTexRefGetMipmappedArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetMipmappedArray +#endif +cuTexRefGetMipmappedArray: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5000 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5000] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 625 & 0xffff +#if 625 > 0xffff + movk ip0, 625 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetAddress + .p2align 4 + .type cuTexRefSetAddress, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetAddress +#endif +cuTexRefSetAddress: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5008 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5008] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 626 & 0xffff +#if 626 > 0xffff + movk ip0, 626 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetAddress2D + .p2align 4 + .type cuTexRefSetAddress2D, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetAddress2D +#endif +cuTexRefSetAddress2D: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5016 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5016] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 627 & 0xffff +#if 627 > 0xffff + movk ip0, 627 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetAddress2D_v2 + .p2align 4 + .type cuTexRefSetAddress2D_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetAddress2D_v2 +#endif +cuTexRefSetAddress2D_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5024 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5024] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 628 & 0xffff +#if 628 > 0xffff + movk ip0, 628 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetAddress2D_v3 + .p2align 4 + .type cuTexRefSetAddress2D_v3, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetAddress2D_v3 +#endif +cuTexRefSetAddress2D_v3: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5032 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5032] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 629 & 0xffff +#if 629 > 0xffff + movk ip0, 629 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetAddressMode + .p2align 4 + .type cuTexRefSetAddressMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetAddressMode +#endif +cuTexRefSetAddressMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5040 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5040] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 630 & 0xffff +#if 630 > 0xffff + movk ip0, 630 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetAddress_v2 + .p2align 4 + .type cuTexRefSetAddress_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetAddress_v2 +#endif +cuTexRefSetAddress_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5048 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5048] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 631 & 0xffff +#if 631 > 0xffff + movk ip0, 631 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetArray + .p2align 4 + .type cuTexRefSetArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetArray +#endif +cuTexRefSetArray: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5056 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5056] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 632 & 0xffff +#if 632 > 0xffff + movk ip0, 632 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetBorderColor + .p2align 4 + .type cuTexRefSetBorderColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetBorderColor +#endif +cuTexRefSetBorderColor: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5064 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5064] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 633 & 0xffff +#if 633 > 0xffff + movk ip0, 633 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetFilterMode + .p2align 4 + .type cuTexRefSetFilterMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetFilterMode +#endif +cuTexRefSetFilterMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5072 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5072] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 634 & 0xffff +#if 634 > 0xffff + movk ip0, 634 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetFlags + .p2align 4 + .type cuTexRefSetFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetFlags +#endif +cuTexRefSetFlags: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5080 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5080] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 635 & 0xffff +#if 635 > 0xffff + movk ip0, 635 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetFormat + .p2align 4 + .type cuTexRefSetFormat, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetFormat +#endif +cuTexRefSetFormat: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5088 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5088] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 636 & 0xffff +#if 636 > 0xffff + movk ip0, 636 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetMaxAnisotropy + .p2align 4 + .type cuTexRefSetMaxAnisotropy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetMaxAnisotropy +#endif +cuTexRefSetMaxAnisotropy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5096 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5096] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 637 & 0xffff +#if 637 > 0xffff + movk ip0, 637 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetMipmapFilterMode + .p2align 4 + .type cuTexRefSetMipmapFilterMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetMipmapFilterMode +#endif +cuTexRefSetMipmapFilterMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5104 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 638 & 0xffff +#if 638 > 0xffff + movk ip0, 638 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetMipmapLevelBias + .p2align 4 + .type cuTexRefSetMipmapLevelBias, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetMipmapLevelBias +#endif +cuTexRefSetMipmapLevelBias: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5112 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 639 & 0xffff +#if 639 > 0xffff + movk ip0, 639 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetMipmapLevelClamp + .p2align 4 + .type cuTexRefSetMipmapLevelClamp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetMipmapLevelClamp +#endif +cuTexRefSetMipmapLevelClamp: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5120 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 640 & 0xffff +#if 640 > 0xffff + movk ip0, 640 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetMipmappedArray + .p2align 4 + .type cuTexRefSetMipmappedArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetMipmappedArray +#endif +cuTexRefSetMipmappedArray: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5128 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 641 & 0xffff +#if 641 > 0xffff + movk ip0, 641 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuThreadExchangeStreamCaptureMode + .p2align 4 + .type cuThreadExchangeStreamCaptureMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuThreadExchangeStreamCaptureMode +#endif +cuThreadExchangeStreamCaptureMode: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5136 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 642 & 0xffff +#if 642 > 0xffff + movk ip0, 642 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuUserObjectCreate + .p2align 4 + .type cuUserObjectCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuUserObjectCreate +#endif +cuUserObjectCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5144 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 643 & 0xffff +#if 643 > 0xffff + movk ip0, 643 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuUserObjectRelease + .p2align 4 + .type cuUserObjectRelease, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuUserObjectRelease +#endif +cuUserObjectRelease: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5152 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 644 & 0xffff +#if 644 > 0xffff + movk ip0, 644 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuUserObjectRetain + .p2align 4 + .type cuUserObjectRetain, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuUserObjectRetain +#endif +cuUserObjectRetain: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5160 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 645 & 0xffff +#if 645 > 0xffff + movk ip0, 645 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuVDPAUCtxCreate + .p2align 4 + .type cuVDPAUCtxCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuVDPAUCtxCreate +#endif +cuVDPAUCtxCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5168 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 646 & 0xffff +#if 646 > 0xffff + movk ip0, 646 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuVDPAUCtxCreate_v2 + .p2align 4 + .type cuVDPAUCtxCreate_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuVDPAUCtxCreate_v2 +#endif +cuVDPAUCtxCreate_v2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5176 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 647 & 0xffff +#if 647 > 0xffff + movk ip0, 647 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuVDPAUGetDevice + .p2align 4 + .type cuVDPAUGetDevice, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuVDPAUGetDevice +#endif +cuVDPAUGetDevice: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5184 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 648 & 0xffff +#if 648 > 0xffff + movk ip0, 648 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuWaitExternalSemaphoresAsync + .p2align 4 + .type cuWaitExternalSemaphoresAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuWaitExternalSemaphoresAsync +#endif +cuWaitExternalSemaphoresAsync: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5192 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 649 & 0xffff +#if 649 > 0xffff + movk ip0, 649 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuWaitExternalSemaphoresAsync_ptsz + .p2align 4 + .type cuWaitExternalSemaphoresAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuWaitExternalSemaphoresAsync_ptsz +#endif +cuWaitExternalSemaphoresAsync_ptsz: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5200 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 650 & 0xffff +#if 650 > 0xffff + movk ip0, 650 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cudbgApiAttach + .p2align 4 + .type cudbgApiAttach, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cudbgApiAttach +#endif +cudbgApiAttach: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5208 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 651 & 0xffff +#if 651 > 0xffff + movk ip0, 651 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cudbgApiDetach + .p2align 4 + .type cudbgApiDetach, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cudbgApiDetach +#endif +cudbgApiDetach: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5216 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 652 & 0xffff +#if 652 > 0xffff + movk ip0, 652 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cudbgApiInit + .p2align 4 + .type cudbgApiInit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cudbgApiInit +#endif +cudbgApiInit: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5224 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 653 & 0xffff +#if 653 > 0xffff + movk ip0, 653 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cudbgGetAPI + .p2align 4 + .type cudbgGetAPI, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cudbgGetAPI +#endif +cudbgGetAPI: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5232 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 654 & 0xffff +#if 654 > 0xffff + movk ip0, 654 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cudbgGetAPIVersion + .p2align 4 + .type cudbgGetAPIVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cudbgGetAPIVersion +#endif +cudbgGetAPIVersion: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5240 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 655 & 0xffff +#if 655 > 0xffff + movk ip0, 655 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cudbgMain + .p2align 4 + .type cudbgMain, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cudbgMain +#endif +cudbgMain: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5248 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 656 & 0xffff +#if 656 > 0xffff + movk ip0, 656 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cudbgReportDriverApiError + .p2align 4 + .type cudbgReportDriverApiError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cudbgReportDriverApiError +#endif +cudbgReportDriverApiError: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5256 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 657 & 0xffff +#if 657 > 0xffff + movk ip0, 657 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cudbgReportDriverInternalError + .p2align 4 + .type cudbgReportDriverInternalError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cudbgReportDriverInternalError +#endif +cudbgReportDriverInternalError: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libcuda_so_tramp_table+5264 + ldr ip0, [ip0, #:lo12:_libcuda_so_tramp_table+5264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 658 & 0xffff +#if 658 > 0xffff + movk ip0, 658 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libcuda_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/aarch64-linux-gnu/libnvcuvid.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/aarch64-linux-gnu/libnvcuvid.so.init.c new file mode 100644 index 0000000..472d99d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/aarch64-linux-gnu/libnvcuvid.so.init.c @@ -0,0 +1,282 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libnvcuvid.so.1: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libnvcuvid.so.1"); + CHECK(lib_handle, "failed to load library 'libnvcuvid.so.1' via callback ''"); +#else + lib_handle = dlopen("libnvcuvid.so.1", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libnvcuvid.so.1' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "NvToolCreateInterface", + "NvToolDestroyInterface", + "NvToolGetApiFunctionCount", + "NvToolGetApiID", + "NvToolGetApiNames", + "NvToolGetInterface", + "NvToolSetApiID", + "NvToolSetInterface", + "__std_1U4S4U_X02", + "__std_2U4S4U_X08", + "__std_4U4S4U_X04", + "cuvidConvertYUVToRGB", + "cuvidConvertYUVToRGBArray", + "cuvidCreateDecoder", + "cuvidCreateVideoParser", + "cuvidCreateVideoSource", + "cuvidCreateVideoSourceW", + "cuvidCtxLock", + "cuvidCtxLockCreate", + "cuvidCtxLockDestroy", + "cuvidCtxUnlock", + "cuvidDecodePicture", + "cuvidDestroyDecoder", + "cuvidDestroyVideoParser", + "cuvidDestroyVideoSource", + "cuvidGetDecodeStatus", + "cuvidGetDecoderCaps", + "cuvidGetSourceAudioFormat", + "cuvidGetSourceVideoFormat", + "cuvidGetVideoSourceState", + "cuvidMapVideoFrame", + "cuvidMapVideoFrame64", + "cuvidParseVideoData", + "cuvidPrivateOp", + "cuvidReconfigureDecoder", + "cuvidSetVideoSourceState", + "cuvidUnmapVideoFrame", + "cuvidUnmapVideoFrame64", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libnvcuvid_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libnvcuvid_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libnvcuvid_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libnvcuvid_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libnvcuvid_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libnvcuvid_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libnvcuvid_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libnvcuvid_so_tramp_table, 0, SYM_COUNT * sizeof(_libnvcuvid_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/aarch64-linux-gnu/libnvcuvid.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/aarch64-linux-gnu/libnvcuvid.so.tramp.S new file mode 100644 index 0000000..2cc9067 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/aarch64-linux-gnu/libnvcuvid.so.tramp.S @@ -0,0 +1,1639 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#define lr x30 +#define ip0 x16 + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libnvcuvid_so_tramp_table + .hidden _libnvcuvid_so_tramp_table + .align 8 +_libnvcuvid_so_tramp_table: + .zero 312 + + .text + + .globl _libnvcuvid_so_tramp_resolve + .hidden _libnvcuvid_so_tramp_resolve + + .globl _libnvcuvid_so_save_regs_and_resolve + .hidden _libnvcuvid_so_save_regs_and_resolve + .type _libnvcuvid_so_save_regs_and_resolve, %function +_libnvcuvid_so_save_regs_and_resolve: + .cfi_startproc + + // Slow path which calls dlsym, taken only on first call. + // Registers are saved according to "Procedure Call Standard for the Arm® 64-bit Architecture". + // For DWARF directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + // Stack is aligned at 16 bytes + +#define PUSH_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 8 +#define POP_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore reg2; .cfi_restore reg1 + +#define PUSH_WIDE_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-32]!; .cfi_adjust_cfa_offset 32; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 16 +#define POP_WIDE_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #32; .cfi_adjust_cfa_offset -32; .cfi_restore reg2; .cfi_restore reg1 + + // Save only arguments (and lr) + PUSH_PAIR(x0, x1) + PUSH_PAIR(x2, x3) + PUSH_PAIR(x4, x5) + PUSH_PAIR(x6, x7) + PUSH_PAIR(x8, lr) + + ldr x0, [sp, #80] // 16*5 + + PUSH_WIDE_PAIR(q0, q1) + PUSH_WIDE_PAIR(q2, q3) + PUSH_WIDE_PAIR(q4, q5) + PUSH_WIDE_PAIR(q6, q7) + + // Stack is aligned at 16 bytes + + bl _libnvcuvid_so_tramp_resolve + mov ip0, x0 + + // TODO: pop pc? + + POP_WIDE_PAIR(q6, q7) + POP_WIDE_PAIR(q4, q5) + POP_WIDE_PAIR(q2, q3) + POP_WIDE_PAIR(q0, q1) + + POP_PAIR(x8, lr) + POP_PAIR(x6, x7) + POP_PAIR(x4, x5) + POP_PAIR(x2, x3) + POP_PAIR(x0, x1) + + br lr + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl NvToolCreateInterface + .p2align 4 + .type NvToolCreateInterface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden NvToolCreateInterface +#endif +NvToolCreateInterface: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+0 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+0] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 0 & 0xffff +#if 0 > 0xffff + movk ip0, 0 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl NvToolDestroyInterface + .p2align 4 + .type NvToolDestroyInterface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden NvToolDestroyInterface +#endif +NvToolDestroyInterface: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+8 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+8] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1 & 0xffff +#if 1 > 0xffff + movk ip0, 1 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl NvToolGetApiFunctionCount + .p2align 4 + .type NvToolGetApiFunctionCount, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden NvToolGetApiFunctionCount +#endif +NvToolGetApiFunctionCount: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+16 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+16] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 2 & 0xffff +#if 2 > 0xffff + movk ip0, 2 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl NvToolGetApiID + .p2align 4 + .type NvToolGetApiID, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden NvToolGetApiID +#endif +NvToolGetApiID: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+24 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+24] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 3 & 0xffff +#if 3 > 0xffff + movk ip0, 3 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl NvToolGetApiNames + .p2align 4 + .type NvToolGetApiNames, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden NvToolGetApiNames +#endif +NvToolGetApiNames: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+32 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+32] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 4 & 0xffff +#if 4 > 0xffff + movk ip0, 4 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl NvToolGetInterface + .p2align 4 + .type NvToolGetInterface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden NvToolGetInterface +#endif +NvToolGetInterface: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+40 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+40] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 5 & 0xffff +#if 5 > 0xffff + movk ip0, 5 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl NvToolSetApiID + .p2align 4 + .type NvToolSetApiID, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden NvToolSetApiID +#endif +NvToolSetApiID: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+48 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+48] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 6 & 0xffff +#if 6 > 0xffff + movk ip0, 6 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl NvToolSetInterface + .p2align 4 + .type NvToolSetInterface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden NvToolSetInterface +#endif +NvToolSetInterface: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+56 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+56] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 7 & 0xffff +#if 7 > 0xffff + movk ip0, 7 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl __std_1U4S4U_X02 + .p2align 4 + .type __std_1U4S4U_X02, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden __std_1U4S4U_X02 +#endif +__std_1U4S4U_X02: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+64 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+64] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 8 & 0xffff +#if 8 > 0xffff + movk ip0, 8 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl __std_2U4S4U_X08 + .p2align 4 + .type __std_2U4S4U_X08, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden __std_2U4S4U_X08 +#endif +__std_2U4S4U_X08: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+72 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+72] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 9 & 0xffff +#if 9 > 0xffff + movk ip0, 9 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl __std_4U4S4U_X04 + .p2align 4 + .type __std_4U4S4U_X04, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden __std_4U4S4U_X04 +#endif +__std_4U4S4U_X04: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+80 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+80] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 10 & 0xffff +#if 10 > 0xffff + movk ip0, 10 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidConvertYUVToRGB + .p2align 4 + .type cuvidConvertYUVToRGB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidConvertYUVToRGB +#endif +cuvidConvertYUVToRGB: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+88 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+88] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 11 & 0xffff +#if 11 > 0xffff + movk ip0, 11 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidConvertYUVToRGBArray + .p2align 4 + .type cuvidConvertYUVToRGBArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidConvertYUVToRGBArray +#endif +cuvidConvertYUVToRGBArray: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+96 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+96] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 12 & 0xffff +#if 12 > 0xffff + movk ip0, 12 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidCreateDecoder + .p2align 4 + .type cuvidCreateDecoder, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidCreateDecoder +#endif +cuvidCreateDecoder: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+104 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 13 & 0xffff +#if 13 > 0xffff + movk ip0, 13 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidCreateVideoParser + .p2align 4 + .type cuvidCreateVideoParser, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidCreateVideoParser +#endif +cuvidCreateVideoParser: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+112 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 14 & 0xffff +#if 14 > 0xffff + movk ip0, 14 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidCreateVideoSource + .p2align 4 + .type cuvidCreateVideoSource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidCreateVideoSource +#endif +cuvidCreateVideoSource: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+120 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 15 & 0xffff +#if 15 > 0xffff + movk ip0, 15 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidCreateVideoSourceW + .p2align 4 + .type cuvidCreateVideoSourceW, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidCreateVideoSourceW +#endif +cuvidCreateVideoSourceW: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+128 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 16 & 0xffff +#if 16 > 0xffff + movk ip0, 16 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidCtxLock + .p2align 4 + .type cuvidCtxLock, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidCtxLock +#endif +cuvidCtxLock: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+136 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 17 & 0xffff +#if 17 > 0xffff + movk ip0, 17 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidCtxLockCreate + .p2align 4 + .type cuvidCtxLockCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidCtxLockCreate +#endif +cuvidCtxLockCreate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+144 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 18 & 0xffff +#if 18 > 0xffff + movk ip0, 18 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidCtxLockDestroy + .p2align 4 + .type cuvidCtxLockDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidCtxLockDestroy +#endif +cuvidCtxLockDestroy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+152 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 19 & 0xffff +#if 19 > 0xffff + movk ip0, 19 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidCtxUnlock + .p2align 4 + .type cuvidCtxUnlock, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidCtxUnlock +#endif +cuvidCtxUnlock: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+160 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 20 & 0xffff +#if 20 > 0xffff + movk ip0, 20 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidDecodePicture + .p2align 4 + .type cuvidDecodePicture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidDecodePicture +#endif +cuvidDecodePicture: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+168 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 21 & 0xffff +#if 21 > 0xffff + movk ip0, 21 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidDestroyDecoder + .p2align 4 + .type cuvidDestroyDecoder, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidDestroyDecoder +#endif +cuvidDestroyDecoder: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+176 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 22 & 0xffff +#if 22 > 0xffff + movk ip0, 22 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidDestroyVideoParser + .p2align 4 + .type cuvidDestroyVideoParser, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidDestroyVideoParser +#endif +cuvidDestroyVideoParser: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+184 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 23 & 0xffff +#if 23 > 0xffff + movk ip0, 23 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidDestroyVideoSource + .p2align 4 + .type cuvidDestroyVideoSource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidDestroyVideoSource +#endif +cuvidDestroyVideoSource: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+192 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 24 & 0xffff +#if 24 > 0xffff + movk ip0, 24 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidGetDecodeStatus + .p2align 4 + .type cuvidGetDecodeStatus, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidGetDecodeStatus +#endif +cuvidGetDecodeStatus: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+200 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 25 & 0xffff +#if 25 > 0xffff + movk ip0, 25 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidGetDecoderCaps + .p2align 4 + .type cuvidGetDecoderCaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidGetDecoderCaps +#endif +cuvidGetDecoderCaps: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+208 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 26 & 0xffff +#if 26 > 0xffff + movk ip0, 26 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidGetSourceAudioFormat + .p2align 4 + .type cuvidGetSourceAudioFormat, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidGetSourceAudioFormat +#endif +cuvidGetSourceAudioFormat: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+216 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 27 & 0xffff +#if 27 > 0xffff + movk ip0, 27 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidGetSourceVideoFormat + .p2align 4 + .type cuvidGetSourceVideoFormat, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidGetSourceVideoFormat +#endif +cuvidGetSourceVideoFormat: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+224 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 28 & 0xffff +#if 28 > 0xffff + movk ip0, 28 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidGetVideoSourceState + .p2align 4 + .type cuvidGetVideoSourceState, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidGetVideoSourceState +#endif +cuvidGetVideoSourceState: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+232 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 29 & 0xffff +#if 29 > 0xffff + movk ip0, 29 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidMapVideoFrame + .p2align 4 + .type cuvidMapVideoFrame, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidMapVideoFrame +#endif +cuvidMapVideoFrame: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+240 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 30 & 0xffff +#if 30 > 0xffff + movk ip0, 30 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidMapVideoFrame64 + .p2align 4 + .type cuvidMapVideoFrame64, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidMapVideoFrame64 +#endif +cuvidMapVideoFrame64: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+248 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 31 & 0xffff +#if 31 > 0xffff + movk ip0, 31 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidParseVideoData + .p2align 4 + .type cuvidParseVideoData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidParseVideoData +#endif +cuvidParseVideoData: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+256 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 32 & 0xffff +#if 32 > 0xffff + movk ip0, 32 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidPrivateOp + .p2align 4 + .type cuvidPrivateOp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidPrivateOp +#endif +cuvidPrivateOp: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+264 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 33 & 0xffff +#if 33 > 0xffff + movk ip0, 33 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidReconfigureDecoder + .p2align 4 + .type cuvidReconfigureDecoder, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidReconfigureDecoder +#endif +cuvidReconfigureDecoder: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+272 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 34 & 0xffff +#if 34 > 0xffff + movk ip0, 34 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidSetVideoSourceState + .p2align 4 + .type cuvidSetVideoSourceState, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidSetVideoSourceState +#endif +cuvidSetVideoSourceState: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+280 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 35 & 0xffff +#if 35 > 0xffff + movk ip0, 35 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidUnmapVideoFrame + .p2align 4 + .type cuvidUnmapVideoFrame, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidUnmapVideoFrame +#endif +cuvidUnmapVideoFrame: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+288 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 36 & 0xffff +#if 36 > 0xffff + movk ip0, 36 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidUnmapVideoFrame64 + .p2align 4 + .type cuvidUnmapVideoFrame64, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidUnmapVideoFrame64 +#endif +cuvidUnmapVideoFrame64: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libnvcuvid_so_tramp_table+296 + ldr ip0, [ip0, #:lo12:_libnvcuvid_so_tramp_table+296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 37 & 0xffff +#if 37 > 0xffff + movk ip0, 37 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libnvcuvid_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/x86_64-linux-gnu/libcuda.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/x86_64-linux-gnu/libcuda.so.init.c new file mode 100644 index 0000000..1e4b687 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/x86_64-linux-gnu/libcuda.so.init.c @@ -0,0 +1,903 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libcuda.so.1: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libcuda.so.1"); + CHECK(lib_handle, "failed to load library 'libcuda.so.1' via callback ''"); +#else + lib_handle = dlopen("libcuda.so.1", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libcuda.so.1' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "cuArray3DCreate", + "cuArray3DCreate_v2", + "cuArray3DGetDescriptor", + "cuArray3DGetDescriptor_v2", + "cuArrayCreate", + "cuArrayCreate_v2", + "cuArrayDestroy", + "cuArrayGetDescriptor", + "cuArrayGetDescriptor_v2", + "cuArrayGetMemoryRequirements", + "cuArrayGetPlane", + "cuArrayGetSparseProperties", + "cuCheckpointProcessCheckpoint", + "cuCheckpointProcessGetRestoreThreadId", + "cuCheckpointProcessGetState", + "cuCheckpointProcessLock", + "cuCheckpointProcessRestore", + "cuCheckpointProcessUnlock", + "cuCoredumpGetAttribute", + "cuCoredumpGetAttributeGlobal", + "cuCoredumpSetAttribute", + "cuCoredumpSetAttributeGlobal", + "cuCtxAttach", + "cuCtxCreate", + "cuCtxCreate_v2", + "cuCtxCreate_v3", + "cuCtxCreate_v4", + "cuCtxDestroy", + "cuCtxDestroy_v2", + "cuCtxDetach", + "cuCtxDisablePeerAccess", + "cuCtxEnablePeerAccess", + "cuCtxFromGreenCtx", + "cuCtxGetApiVersion", + "cuCtxGetCacheConfig", + "cuCtxGetCurrent", + "cuCtxGetDevResource", + "cuCtxGetDevice", + "cuCtxGetExecAffinity", + "cuCtxGetFlags", + "cuCtxGetId", + "cuCtxGetLimit", + "cuCtxGetSharedMemConfig", + "cuCtxGetStreamPriorityRange", + "cuCtxPopCurrent", + "cuCtxPopCurrent_v2", + "cuCtxPushCurrent", + "cuCtxPushCurrent_v2", + "cuCtxRecordEvent", + "cuCtxResetPersistingL2Cache", + "cuCtxSetCacheConfig", + "cuCtxSetCurrent", + "cuCtxSetFlags", + "cuCtxSetLimit", + "cuCtxSetSharedMemConfig", + "cuCtxSynchronize", + "cuCtxWaitEvent", + "cuDestroyExternalMemory", + "cuDestroyExternalSemaphore", + "cuDevResourceGenerateDesc", + "cuDevSmResourceSplitByCount", + "cuDeviceCanAccessPeer", + "cuDeviceComputeCapability", + "cuDeviceGet", + "cuDeviceGetAttribute", + "cuDeviceGetByPCIBusId", + "cuDeviceGetCount", + "cuDeviceGetDefaultMemPool", + "cuDeviceGetDevResource", + "cuDeviceGetExecAffinitySupport", + "cuDeviceGetGraphMemAttribute", + "cuDeviceGetLuid", + "cuDeviceGetMemPool", + "cuDeviceGetName", + "cuDeviceGetNvSciSyncAttributes", + "cuDeviceGetP2PAttribute", + "cuDeviceGetPCIBusId", + "cuDeviceGetProperties", + "cuDeviceGetTexture1DLinearMaxWidth", + "cuDeviceGetUuid", + "cuDeviceGetUuid_v2", + "cuDeviceGraphMemTrim", + "cuDevicePrimaryCtxGetState", + "cuDevicePrimaryCtxRelease", + "cuDevicePrimaryCtxRelease_v2", + "cuDevicePrimaryCtxReset", + "cuDevicePrimaryCtxReset_v2", + "cuDevicePrimaryCtxRetain", + "cuDevicePrimaryCtxSetFlags", + "cuDevicePrimaryCtxSetFlags_v2", + "cuDeviceRegisterAsyncNotification", + "cuDeviceSetGraphMemAttribute", + "cuDeviceSetMemPool", + "cuDeviceTotalMem", + "cuDeviceTotalMem_v2", + "cuDeviceUnregisterAsyncNotification", + "cuDriverGetVersion", + "cuEGLApiInit", + "cuEGLStreamConsumerAcquireFrame", + "cuEGLStreamConsumerConnect", + "cuEGLStreamConsumerConnectWithFlags", + "cuEGLStreamConsumerDisconnect", + "cuEGLStreamConsumerReleaseFrame", + "cuEGLStreamProducerConnect", + "cuEGLStreamProducerDisconnect", + "cuEGLStreamProducerPresentFrame", + "cuEGLStreamProducerReturnFrame", + "cuEventCreate", + "cuEventDestroy", + "cuEventDestroy_v2", + "cuEventElapsedTime", + "cuEventElapsedTime_v2", + "cuEventQuery", + "cuEventRecord", + "cuEventRecordWithFlags", + "cuEventRecordWithFlags_ptsz", + "cuEventRecord_ptsz", + "cuEventSynchronize", + "cuExternalMemoryGetMappedBuffer", + "cuExternalMemoryGetMappedMipmappedArray", + "cuFlushGPUDirectRDMAWrites", + "cuFuncGetAttribute", + "cuFuncGetModule", + "cuFuncGetName", + "cuFuncGetParamInfo", + "cuFuncIsLoaded", + "cuFuncLoad", + "cuFuncSetAttribute", + "cuFuncSetBlockShape", + "cuFuncSetCacheConfig", + "cuFuncSetSharedMemConfig", + "cuFuncSetSharedSize", + "cuGLCtxCreate", + "cuGLCtxCreate_v2", + "cuGLGetDevices", + "cuGLGetDevices_v2", + "cuGLInit", + "cuGLMapBufferObject", + "cuGLMapBufferObjectAsync", + "cuGLMapBufferObjectAsync_v2", + "cuGLMapBufferObjectAsync_v2_ptsz", + "cuGLMapBufferObject_v2", + "cuGLMapBufferObject_v2_ptds", + "cuGLRegisterBufferObject", + "cuGLSetBufferObjectMapFlags", + "cuGLUnmapBufferObject", + "cuGLUnmapBufferObjectAsync", + "cuGLUnregisterBufferObject", + "cuGetErrorName", + "cuGetErrorString", + "cuGetExportTable", + "cuGetProcAddress", + "cuGetProcAddress_v2", + "cuGraphAddBatchMemOpNode", + "cuGraphAddChildGraphNode", + "cuGraphAddDependencies", + "cuGraphAddDependencies_v2", + "cuGraphAddEmptyNode", + "cuGraphAddEventRecordNode", + "cuGraphAddEventWaitNode", + "cuGraphAddExternalSemaphoresSignalNode", + "cuGraphAddExternalSemaphoresWaitNode", + "cuGraphAddHostNode", + "cuGraphAddKernelNode", + "cuGraphAddKernelNode_v2", + "cuGraphAddMemAllocNode", + "cuGraphAddMemFreeNode", + "cuGraphAddMemcpyNode", + "cuGraphAddMemsetNode", + "cuGraphAddNode", + "cuGraphAddNode_v2", + "cuGraphBatchMemOpNodeGetParams", + "cuGraphBatchMemOpNodeSetParams", + "cuGraphChildGraphNodeGetGraph", + "cuGraphClone", + "cuGraphConditionalHandleCreate", + "cuGraphCreate", + "cuGraphDebugDotPrint", + "cuGraphDestroy", + "cuGraphDestroyNode", + "cuGraphEventRecordNodeGetEvent", + "cuGraphEventRecordNodeSetEvent", + "cuGraphEventWaitNodeGetEvent", + "cuGraphEventWaitNodeSetEvent", + "cuGraphExecBatchMemOpNodeSetParams", + "cuGraphExecChildGraphNodeSetParams", + "cuGraphExecDestroy", + "cuGraphExecEventRecordNodeSetEvent", + "cuGraphExecEventWaitNodeSetEvent", + "cuGraphExecExternalSemaphoresSignalNodeSetParams", + "cuGraphExecExternalSemaphoresWaitNodeSetParams", + "cuGraphExecGetFlags", + "cuGraphExecHostNodeSetParams", + "cuGraphExecKernelNodeSetParams", + "cuGraphExecKernelNodeSetParams_v2", + "cuGraphExecMemcpyNodeSetParams", + "cuGraphExecMemsetNodeSetParams", + "cuGraphExecNodeSetParams", + "cuGraphExecUpdate", + "cuGraphExecUpdate_v2", + "cuGraphExternalSemaphoresSignalNodeGetParams", + "cuGraphExternalSemaphoresSignalNodeSetParams", + "cuGraphExternalSemaphoresWaitNodeGetParams", + "cuGraphExternalSemaphoresWaitNodeSetParams", + "cuGraphGetEdges", + "cuGraphGetEdges_v2", + "cuGraphGetNodes", + "cuGraphGetRootNodes", + "cuGraphHostNodeGetParams", + "cuGraphHostNodeSetParams", + "cuGraphInstantiate", + "cuGraphInstantiateWithFlags", + "cuGraphInstantiateWithParams", + "cuGraphInstantiateWithParams_ptsz", + "cuGraphInstantiate_v2", + "cuGraphKernelNodeCopyAttributes", + "cuGraphKernelNodeGetAttribute", + "cuGraphKernelNodeGetParams", + "cuGraphKernelNodeGetParams_v2", + "cuGraphKernelNodeSetAttribute", + "cuGraphKernelNodeSetParams", + "cuGraphKernelNodeSetParams_v2", + "cuGraphLaunch", + "cuGraphLaunch_ptsz", + "cuGraphMemAllocNodeGetParams", + "cuGraphMemFreeNodeGetParams", + "cuGraphMemcpyNodeGetParams", + "cuGraphMemcpyNodeSetParams", + "cuGraphMemsetNodeGetParams", + "cuGraphMemsetNodeSetParams", + "cuGraphNodeFindInClone", + "cuGraphNodeGetDependencies", + "cuGraphNodeGetDependencies_v2", + "cuGraphNodeGetDependentNodes", + "cuGraphNodeGetDependentNodes_v2", + "cuGraphNodeGetEnabled", + "cuGraphNodeGetType", + "cuGraphNodeSetEnabled", + "cuGraphNodeSetParams", + "cuGraphReleaseUserObject", + "cuGraphRemoveDependencies", + "cuGraphRemoveDependencies_v2", + "cuGraphRetainUserObject", + "cuGraphUpload", + "cuGraphUpload_ptsz", + "cuGraphicsEGLRegisterImage", + "cuGraphicsGLRegisterBuffer", + "cuGraphicsGLRegisterImage", + "cuGraphicsMapResources", + "cuGraphicsMapResources_ptsz", + "cuGraphicsResourceGetMappedEglFrame", + "cuGraphicsResourceGetMappedMipmappedArray", + "cuGraphicsResourceGetMappedPointer", + "cuGraphicsResourceGetMappedPointer_v2", + "cuGraphicsResourceSetMapFlags", + "cuGraphicsResourceSetMapFlags_v2", + "cuGraphicsSubResourceGetMappedArray", + "cuGraphicsUnmapResources", + "cuGraphicsUnmapResources_ptsz", + "cuGraphicsUnregisterResource", + "cuGraphicsVDPAURegisterOutputSurface", + "cuGraphicsVDPAURegisterVideoSurface", + "cuGreenCtxCreate", + "cuGreenCtxDestroy", + "cuGreenCtxGetDevResource", + "cuGreenCtxRecordEvent", + "cuGreenCtxStreamCreate", + "cuGreenCtxWaitEvent", + "cuImportExternalMemory", + "cuImportExternalSemaphore", + "cuInit", + "cuIpcCloseMemHandle", + "cuIpcGetEventHandle", + "cuIpcGetMemHandle", + "cuIpcOpenEventHandle", + "cuIpcOpenMemHandle", + "cuIpcOpenMemHandle_v2", + "cuKernelGetAttribute", + "cuKernelGetFunction", + "cuKernelGetLibrary", + "cuKernelGetName", + "cuKernelGetParamInfo", + "cuKernelSetAttribute", + "cuKernelSetCacheConfig", + "cuLaunch", + "cuLaunchCooperativeKernel", + "cuLaunchCooperativeKernelMultiDevice", + "cuLaunchCooperativeKernel_ptsz", + "cuLaunchGrid", + "cuLaunchGridAsync", + "cuLaunchHostFunc", + "cuLaunchHostFunc_ptsz", + "cuLaunchKernel", + "cuLaunchKernelEx", + "cuLaunchKernelEx_ptsz", + "cuLaunchKernel_ptsz", + "cuLibraryEnumerateKernels", + "cuLibraryGetGlobal", + "cuLibraryGetKernel", + "cuLibraryGetKernelCount", + "cuLibraryGetManaged", + "cuLibraryGetModule", + "cuLibraryGetUnifiedFunction", + "cuLibraryLoadData", + "cuLibraryLoadFromFile", + "cuLibraryUnload", + "cuLinkAddData", + "cuLinkAddData_v2", + "cuLinkAddFile", + "cuLinkAddFile_v2", + "cuLinkComplete", + "cuLinkCreate", + "cuLinkCreate_v2", + "cuLinkDestroy", + "cuMemAddressFree", + "cuMemAddressReserve", + "cuMemAdvise", + "cuMemAdvise_v2", + "cuMemAlloc", + "cuMemAllocAsync", + "cuMemAllocAsync_ptsz", + "cuMemAllocFromPoolAsync", + "cuMemAllocFromPoolAsync_ptsz", + "cuMemAllocHost", + "cuMemAllocHost_v2", + "cuMemAllocManaged", + "cuMemAllocPitch", + "cuMemAllocPitch_v2", + "cuMemAlloc_v2", + "cuMemBatchDecompressAsync", + "cuMemBatchDecompressAsync_ptsz", + "cuMemCreate", + "cuMemExportToShareableHandle", + "cuMemFree", + "cuMemFreeAsync", + "cuMemFreeAsync_ptsz", + "cuMemFreeHost", + "cuMemFree_v2", + "cuMemGetAccess", + "cuMemGetAddressRange", + "cuMemGetAddressRange_v2", + "cuMemGetAllocationGranularity", + "cuMemGetAllocationPropertiesFromHandle", + "cuMemGetAttribute", + "cuMemGetAttribute_v2", + "cuMemGetHandleForAddressRange", + "cuMemGetInfo", + "cuMemGetInfo_v2", + "cuMemHostAlloc", + "cuMemHostGetDevicePointer", + "cuMemHostGetDevicePointer_v2", + "cuMemHostGetFlags", + "cuMemHostRegister", + "cuMemHostRegister_v2", + "cuMemHostUnregister", + "cuMemImportFromShareableHandle", + "cuMemMap", + "cuMemMapArrayAsync", + "cuMemMapArrayAsync_ptsz", + "cuMemPoolCreate", + "cuMemPoolDestroy", + "cuMemPoolExportPointer", + "cuMemPoolExportToShareableHandle", + "cuMemPoolGetAccess", + "cuMemPoolGetAttribute", + "cuMemPoolImportFromShareableHandle", + "cuMemPoolImportPointer", + "cuMemPoolSetAccess", + "cuMemPoolSetAttribute", + "cuMemPoolTrimTo", + "cuMemPrefetchAsync", + "cuMemPrefetchAsync_ptsz", + "cuMemPrefetchAsync_v2", + "cuMemPrefetchAsync_v2_ptsz", + "cuMemRangeGetAttribute", + "cuMemRangeGetAttributes", + "cuMemRelease", + "cuMemRetainAllocationHandle", + "cuMemSetAccess", + "cuMemUnmap", + "cuMemcpy", + "cuMemcpy2D", + "cuMemcpy2DAsync", + "cuMemcpy2DAsync_v2", + "cuMemcpy2DAsync_v2_ptsz", + "cuMemcpy2DUnaligned", + "cuMemcpy2DUnaligned_v2", + "cuMemcpy2DUnaligned_v2_ptds", + "cuMemcpy2D_v2", + "cuMemcpy2D_v2_ptds", + "cuMemcpy3D", + "cuMemcpy3DAsync", + "cuMemcpy3DAsync_v2", + "cuMemcpy3DAsync_v2_ptsz", + "cuMemcpy3DBatchAsync", + "cuMemcpy3DBatchAsync_ptsz", + "cuMemcpy3DPeer", + "cuMemcpy3DPeerAsync", + "cuMemcpy3DPeerAsync_ptsz", + "cuMemcpy3DPeer_ptds", + "cuMemcpy3D_v2", + "cuMemcpy3D_v2_ptds", + "cuMemcpyAsync", + "cuMemcpyAsync_ptsz", + "cuMemcpyAtoA", + "cuMemcpyAtoA_v2", + "cuMemcpyAtoA_v2_ptds", + "cuMemcpyAtoD", + "cuMemcpyAtoD_v2", + "cuMemcpyAtoD_v2_ptds", + "cuMemcpyAtoH", + "cuMemcpyAtoHAsync", + "cuMemcpyAtoHAsync_v2", + "cuMemcpyAtoHAsync_v2_ptsz", + "cuMemcpyAtoH_v2", + "cuMemcpyAtoH_v2_ptds", + "cuMemcpyBatchAsync", + "cuMemcpyBatchAsync_ptsz", + "cuMemcpyDtoA", + "cuMemcpyDtoA_v2", + "cuMemcpyDtoA_v2_ptds", + "cuMemcpyDtoD", + "cuMemcpyDtoDAsync", + "cuMemcpyDtoDAsync_v2", + "cuMemcpyDtoDAsync_v2_ptsz", + "cuMemcpyDtoD_v2", + "cuMemcpyDtoD_v2_ptds", + "cuMemcpyDtoH", + "cuMemcpyDtoHAsync", + "cuMemcpyDtoHAsync_v2", + "cuMemcpyDtoHAsync_v2_ptsz", + "cuMemcpyDtoH_v2", + "cuMemcpyDtoH_v2_ptds", + "cuMemcpyHtoA", + "cuMemcpyHtoAAsync", + "cuMemcpyHtoAAsync_v2", + "cuMemcpyHtoAAsync_v2_ptsz", + "cuMemcpyHtoA_v2", + "cuMemcpyHtoA_v2_ptds", + "cuMemcpyHtoD", + "cuMemcpyHtoDAsync", + "cuMemcpyHtoDAsync_v2", + "cuMemcpyHtoDAsync_v2_ptsz", + "cuMemcpyHtoD_v2", + "cuMemcpyHtoD_v2_ptds", + "cuMemcpyPeer", + "cuMemcpyPeerAsync", + "cuMemcpyPeerAsync_ptsz", + "cuMemcpyPeer_ptds", + "cuMemcpy_ptds", + "cuMemsetD16", + "cuMemsetD16Async", + "cuMemsetD16Async_ptsz", + "cuMemsetD16_v2", + "cuMemsetD16_v2_ptds", + "cuMemsetD2D16", + "cuMemsetD2D16Async", + "cuMemsetD2D16Async_ptsz", + "cuMemsetD2D16_v2", + "cuMemsetD2D16_v2_ptds", + "cuMemsetD2D32", + "cuMemsetD2D32Async", + "cuMemsetD2D32Async_ptsz", + "cuMemsetD2D32_v2", + "cuMemsetD2D32_v2_ptds", + "cuMemsetD2D8", + "cuMemsetD2D8Async", + "cuMemsetD2D8Async_ptsz", + "cuMemsetD2D8_v2", + "cuMemsetD2D8_v2_ptds", + "cuMemsetD32", + "cuMemsetD32Async", + "cuMemsetD32Async_ptsz", + "cuMemsetD32_v2", + "cuMemsetD32_v2_ptds", + "cuMemsetD8", + "cuMemsetD8Async", + "cuMemsetD8Async_ptsz", + "cuMemsetD8_v2", + "cuMemsetD8_v2_ptds", + "cuMipmappedArrayCreate", + "cuMipmappedArrayDestroy", + "cuMipmappedArrayGetLevel", + "cuMipmappedArrayGetMemoryRequirements", + "cuMipmappedArrayGetSparseProperties", + "cuModuleEnumerateFunctions", + "cuModuleGetFunction", + "cuModuleGetFunctionCount", + "cuModuleGetGlobal", + "cuModuleGetGlobal_v2", + "cuModuleGetLoadingMode", + "cuModuleGetSurfRef", + "cuModuleGetTexRef", + "cuModuleLoad", + "cuModuleLoadData", + "cuModuleLoadDataEx", + "cuModuleLoadFatBinary", + "cuModuleUnload", + "cuMulticastAddDevice", + "cuMulticastBindAddr", + "cuMulticastBindMem", + "cuMulticastCreate", + "cuMulticastGetGranularity", + "cuMulticastUnbind", + "cuOccupancyAvailableDynamicSMemPerBlock", + "cuOccupancyMaxActiveBlocksPerMultiprocessor", + "cuOccupancyMaxActiveBlocksPerMultiprocessorWithFlags", + "cuOccupancyMaxActiveClusters", + "cuOccupancyMaxPotentialBlockSize", + "cuOccupancyMaxPotentialBlockSizeWithFlags", + "cuOccupancyMaxPotentialClusterSize", + "cuParamSetSize", + "cuParamSetTexRef", + "cuParamSetf", + "cuParamSeti", + "cuParamSetv", + "cuPointerGetAttribute", + "cuPointerGetAttributes", + "cuPointerSetAttribute", + "cuProfilerInitialize", + "cuProfilerStart", + "cuProfilerStop", + "cuSignalExternalSemaphoresAsync", + "cuSignalExternalSemaphoresAsync_ptsz", + "cuStreamAddCallback", + "cuStreamAddCallback_ptsz", + "cuStreamAttachMemAsync", + "cuStreamAttachMemAsync_ptsz", + "cuStreamBatchMemOp", + "cuStreamBatchMemOp_ptsz", + "cuStreamBatchMemOp_v2", + "cuStreamBatchMemOp_v2_ptsz", + "cuStreamBeginCapture", + "cuStreamBeginCaptureToGraph", + "cuStreamBeginCaptureToGraph_ptsz", + "cuStreamBeginCapture_ptsz", + "cuStreamBeginCapture_v2", + "cuStreamBeginCapture_v2_ptsz", + "cuStreamCopyAttributes", + "cuStreamCopyAttributes_ptsz", + "cuStreamCreate", + "cuStreamCreateWithPriority", + "cuStreamDestroy", + "cuStreamDestroy_v2", + "cuStreamEndCapture", + "cuStreamEndCapture_ptsz", + "cuStreamGetAttribute", + "cuStreamGetAttribute_ptsz", + "cuStreamGetCaptureInfo", + "cuStreamGetCaptureInfo_ptsz", + "cuStreamGetCaptureInfo_v2", + "cuStreamGetCaptureInfo_v2_ptsz", + "cuStreamGetCaptureInfo_v3", + "cuStreamGetCaptureInfo_v3_ptsz", + "cuStreamGetCtx", + "cuStreamGetCtx_ptsz", + "cuStreamGetCtx_v2", + "cuStreamGetCtx_v2_ptsz", + "cuStreamGetDevice", + "cuStreamGetDevice_ptsz", + "cuStreamGetFlags", + "cuStreamGetFlags_ptsz", + "cuStreamGetGreenCtx", + "cuStreamGetId", + "cuStreamGetId_ptsz", + "cuStreamGetPriority", + "cuStreamGetPriority_ptsz", + "cuStreamIsCapturing", + "cuStreamIsCapturing_ptsz", + "cuStreamQuery", + "cuStreamQuery_ptsz", + "cuStreamSetAttribute", + "cuStreamSetAttribute_ptsz", + "cuStreamSynchronize", + "cuStreamSynchronize_ptsz", + "cuStreamUpdateCaptureDependencies", + "cuStreamUpdateCaptureDependencies_ptsz", + "cuStreamUpdateCaptureDependencies_v2", + "cuStreamUpdateCaptureDependencies_v2_ptsz", + "cuStreamWaitEvent", + "cuStreamWaitEvent_ptsz", + "cuStreamWaitValue32", + "cuStreamWaitValue32_ptsz", + "cuStreamWaitValue32_v2", + "cuStreamWaitValue32_v2_ptsz", + "cuStreamWaitValue64", + "cuStreamWaitValue64_ptsz", + "cuStreamWaitValue64_v2", + "cuStreamWaitValue64_v2_ptsz", + "cuStreamWriteValue32", + "cuStreamWriteValue32_ptsz", + "cuStreamWriteValue32_v2", + "cuStreamWriteValue32_v2_ptsz", + "cuStreamWriteValue64", + "cuStreamWriteValue64_ptsz", + "cuStreamWriteValue64_v2", + "cuStreamWriteValue64_v2_ptsz", + "cuSurfObjectCreate", + "cuSurfObjectDestroy", + "cuSurfObjectGetResourceDesc", + "cuSurfRefGetArray", + "cuSurfRefSetArray", + "cuTensorMapEncodeIm2col", + "cuTensorMapEncodeIm2colWide", + "cuTensorMapEncodeTiled", + "cuTensorMapReplaceAddress", + "cuTexObjectCreate", + "cuTexObjectDestroy", + "cuTexObjectGetResourceDesc", + "cuTexObjectGetResourceViewDesc", + "cuTexObjectGetTextureDesc", + "cuTexRefCreate", + "cuTexRefDestroy", + "cuTexRefGetAddress", + "cuTexRefGetAddressMode", + "cuTexRefGetAddress_v2", + "cuTexRefGetArray", + "cuTexRefGetBorderColor", + "cuTexRefGetFilterMode", + "cuTexRefGetFlags", + "cuTexRefGetFormat", + "cuTexRefGetMaxAnisotropy", + "cuTexRefGetMipmapFilterMode", + "cuTexRefGetMipmapLevelBias", + "cuTexRefGetMipmapLevelClamp", + "cuTexRefGetMipmappedArray", + "cuTexRefSetAddress", + "cuTexRefSetAddress2D", + "cuTexRefSetAddress2D_v2", + "cuTexRefSetAddress2D_v3", + "cuTexRefSetAddressMode", + "cuTexRefSetAddress_v2", + "cuTexRefSetArray", + "cuTexRefSetBorderColor", + "cuTexRefSetFilterMode", + "cuTexRefSetFlags", + "cuTexRefSetFormat", + "cuTexRefSetMaxAnisotropy", + "cuTexRefSetMipmapFilterMode", + "cuTexRefSetMipmapLevelBias", + "cuTexRefSetMipmapLevelClamp", + "cuTexRefSetMipmappedArray", + "cuThreadExchangeStreamCaptureMode", + "cuUserObjectCreate", + "cuUserObjectRelease", + "cuUserObjectRetain", + "cuVDPAUCtxCreate", + "cuVDPAUCtxCreate_v2", + "cuVDPAUGetDevice", + "cuWaitExternalSemaphoresAsync", + "cuWaitExternalSemaphoresAsync_ptsz", + "cudbgApiAttach", + "cudbgApiDetach", + "cudbgApiInit", + "cudbgGetAPI", + "cudbgGetAPIVersion", + "cudbgMain", + "cudbgReportDriverApiError", + "cudbgReportDriverInternalError", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libcuda_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libcuda_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libcuda_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libcuda_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libcuda_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libcuda_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libcuda_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libcuda_so_tramp_table, 0, SYM_COUNT * sizeof(_libcuda_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/x86_64-linux-gnu/libcuda.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/x86_64-linux-gnu/libcuda.so.tramp.S new file mode 100644 index 0000000..103d78f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/x86_64-linux-gnu/libcuda.so.tramp.S @@ -0,0 +1,22564 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libcuda_so_tramp_table + .hidden _libcuda_so_tramp_table + .align 8 +_libcuda_so_tramp_table: + .zero 5280 + + .text + + .globl _libcuda_so_tramp_resolve + .hidden _libcuda_so_tramp_resolve + + .globl _libcuda_so_save_regs_and_resolve + .hidden _libcuda_so_save_regs_and_resolve + .type _libcuda_so_save_regs_and_resolve, %function +_libcuda_so_save_regs_and_resolve: + .cfi_startproc + +#define PUSH_REG(reg) pushq %reg ; .cfi_adjust_cfa_offset 8; .cfi_rel_offset reg, 0 +#define POP_REG(reg) popq %reg ; .cfi_adjust_cfa_offset -8; .cfi_restore reg + +#define DEC_STACK(d) subq $d, %rsp; .cfi_adjust_cfa_offset d +#define INC_STACK(d) addq $d, %rsp; .cfi_adjust_cfa_offset -d + +#define PUSH_MMX_REG(reg) DEC_STACK(8); movq %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_MMX_REG(reg) movq (%rsp), %reg; .cfi_restore reg; INC_STACK(8) + +#define PUSH_XMM_REG(reg) DEC_STACK(16); movdqa %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_XMM_REG(reg) movdqa (%rsp), %reg; .cfi_restore reg; INC_STACK(16) + +// TODO: cfi_offset/cfi_restore +#define PUSH_YMM_REG(reg) DEC_STACK(32); vmovdqu %reg, (%rsp) +#define POP_YMM_REG(reg) vmovdqu (%rsp), %reg; INC_STACK(32) + +// TODO: cfi_offset/cfi_restore +#define PUSH_ZMM_REG(reg) DEC_STACK(64); vmovdqu32 %reg, (%rsp) +#define POP_ZMM_REG(reg) vmovdqu32 (%rsp), %reg; INC_STACK(64) + + // Slow path which calls dlsym, taken only on first call. + // All registers are stored to handle arbitrary calling conventions + // (except x87 FPU registers which do not have to be preserved). + // For Dwarf directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + .cfi_def_cfa_offset 8 // Return address + + PUSH_REG(rdi) // 16 + mov 0x10(%rsp), %rdi + PUSH_REG(rbx) + PUSH_REG(rbx) // 16 + PUSH_REG(rcx) + PUSH_REG(rdx) // 16 + PUSH_REG(rbp) + PUSH_REG(rsi) // 16 + PUSH_REG(r8) + PUSH_REG(r9) // 16 + PUSH_REG(r10) + PUSH_REG(r11) // 16 + PUSH_REG(r12) + PUSH_REG(r13) // 16 + PUSH_REG(r14) + PUSH_REG(r15) // 16 + + // Maybe use cpuid instead of macro to detect current vector size... +#ifdef __AVX512F__ + PUSH_ZMM_REG(zmm0) + PUSH_ZMM_REG(zmm1) + PUSH_ZMM_REG(zmm2) + PUSH_ZMM_REG(zmm3) + PUSH_ZMM_REG(zmm4) + PUSH_ZMM_REG(zmm5) + PUSH_ZMM_REG(zmm6) + PUSH_ZMM_REG(zmm7) +#elif defined __AVX__ + PUSH_YMM_REG(ymm0) + PUSH_YMM_REG(ymm1) + PUSH_YMM_REG(ymm2) + PUSH_YMM_REG(ymm3) + PUSH_YMM_REG(ymm4) + PUSH_YMM_REG(ymm5) + PUSH_YMM_REG(ymm6) + PUSH_YMM_REG(ymm7) +#elif defined __SSE__ + PUSH_XMM_REG(xmm0) + PUSH_XMM_REG(xmm1) + PUSH_XMM_REG(xmm2) + PUSH_XMM_REG(xmm3) + PUSH_XMM_REG(xmm4) + PUSH_XMM_REG(xmm5) + PUSH_XMM_REG(xmm6) + PUSH_XMM_REG(xmm7) +#endif + + // MMX registers are not used to pass arguments so we do not save them + + // Stack is just 8-byte aligned but callee will re-align to 16 + call _libcuda_so_tramp_resolve + +#ifdef __AVX512F__ + POP_ZMM_REG(zmm7) + POP_ZMM_REG(zmm6) + POP_ZMM_REG(zmm5) + POP_ZMM_REG(zmm4) + POP_ZMM_REG(zmm3) + POP_ZMM_REG(zmm2) + POP_ZMM_REG(zmm1) + POP_ZMM_REG(zmm0) // 16 +#elif defined __AVX__ + POP_YMM_REG(ymm7) + POP_YMM_REG(ymm6) + POP_YMM_REG(ymm5) + POP_YMM_REG(ymm4) + POP_YMM_REG(ymm3) + POP_YMM_REG(ymm2) + POP_YMM_REG(ymm1) + POP_YMM_REG(ymm0) // 16 +#elif defined __SSE__ + POP_XMM_REG(xmm7) + POP_XMM_REG(xmm6) + POP_XMM_REG(xmm5) + POP_XMM_REG(xmm4) + POP_XMM_REG(xmm3) + POP_XMM_REG(xmm2) + POP_XMM_REG(xmm1) + POP_XMM_REG(xmm0) // 16 +#endif + + POP_REG(r15) + POP_REG(r14) // 16 + POP_REG(r13) + POP_REG(r12) // 16 + POP_REG(r11) + POP_REG(r10) // 16 + POP_REG(r9) + POP_REG(r8) // 16 + POP_REG(rsi) + POP_REG(rbp) // 16 + POP_REG(rdx) + POP_REG(rcx) // 16 + POP_REG(rbx) + POP_REG(rbx) // 16 + POP_REG(rdi) + + ret + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArray3DCreate + .p2align 4 + .type cuArray3DCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArray3DCreate +#endif +cuArray3DCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+0(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+0(%rip) +2: + pushq $0 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArray3DCreate_v2 + .p2align 4 + .type cuArray3DCreate_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArray3DCreate_v2 +#endif +cuArray3DCreate_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+8(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+8(%rip) +2: + pushq $1 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArray3DGetDescriptor + .p2align 4 + .type cuArray3DGetDescriptor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArray3DGetDescriptor +#endif +cuArray3DGetDescriptor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+16(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+16(%rip) +2: + pushq $2 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArray3DGetDescriptor_v2 + .p2align 4 + .type cuArray3DGetDescriptor_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArray3DGetDescriptor_v2 +#endif +cuArray3DGetDescriptor_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+24(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+24(%rip) +2: + pushq $3 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArrayCreate + .p2align 4 + .type cuArrayCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArrayCreate +#endif +cuArrayCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+32(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+32(%rip) +2: + pushq $4 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArrayCreate_v2 + .p2align 4 + .type cuArrayCreate_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArrayCreate_v2 +#endif +cuArrayCreate_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+40(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+40(%rip) +2: + pushq $5 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArrayDestroy + .p2align 4 + .type cuArrayDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArrayDestroy +#endif +cuArrayDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+48(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+48(%rip) +2: + pushq $6 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArrayGetDescriptor + .p2align 4 + .type cuArrayGetDescriptor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArrayGetDescriptor +#endif +cuArrayGetDescriptor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+56(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+56(%rip) +2: + pushq $7 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArrayGetDescriptor_v2 + .p2align 4 + .type cuArrayGetDescriptor_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArrayGetDescriptor_v2 +#endif +cuArrayGetDescriptor_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+64(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+64(%rip) +2: + pushq $8 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArrayGetMemoryRequirements + .p2align 4 + .type cuArrayGetMemoryRequirements, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArrayGetMemoryRequirements +#endif +cuArrayGetMemoryRequirements: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+72(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+72(%rip) +2: + pushq $9 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArrayGetPlane + .p2align 4 + .type cuArrayGetPlane, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArrayGetPlane +#endif +cuArrayGetPlane: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+80(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+80(%rip) +2: + pushq $10 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuArrayGetSparseProperties + .p2align 4 + .type cuArrayGetSparseProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuArrayGetSparseProperties +#endif +cuArrayGetSparseProperties: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+88(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+88(%rip) +2: + pushq $11 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCheckpointProcessCheckpoint + .p2align 4 + .type cuCheckpointProcessCheckpoint, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCheckpointProcessCheckpoint +#endif +cuCheckpointProcessCheckpoint: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+96(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+96(%rip) +2: + pushq $12 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCheckpointProcessGetRestoreThreadId + .p2align 4 + .type cuCheckpointProcessGetRestoreThreadId, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCheckpointProcessGetRestoreThreadId +#endif +cuCheckpointProcessGetRestoreThreadId: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+104(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+104(%rip) +2: + pushq $13 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCheckpointProcessGetState + .p2align 4 + .type cuCheckpointProcessGetState, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCheckpointProcessGetState +#endif +cuCheckpointProcessGetState: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+112(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+112(%rip) +2: + pushq $14 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCheckpointProcessLock + .p2align 4 + .type cuCheckpointProcessLock, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCheckpointProcessLock +#endif +cuCheckpointProcessLock: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+120(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+120(%rip) +2: + pushq $15 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCheckpointProcessRestore + .p2align 4 + .type cuCheckpointProcessRestore, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCheckpointProcessRestore +#endif +cuCheckpointProcessRestore: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+128(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+128(%rip) +2: + pushq $16 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCheckpointProcessUnlock + .p2align 4 + .type cuCheckpointProcessUnlock, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCheckpointProcessUnlock +#endif +cuCheckpointProcessUnlock: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+136(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+136(%rip) +2: + pushq $17 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCoredumpGetAttribute + .p2align 4 + .type cuCoredumpGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCoredumpGetAttribute +#endif +cuCoredumpGetAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+144(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+144(%rip) +2: + pushq $18 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCoredumpGetAttributeGlobal + .p2align 4 + .type cuCoredumpGetAttributeGlobal, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCoredumpGetAttributeGlobal +#endif +cuCoredumpGetAttributeGlobal: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+152(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+152(%rip) +2: + pushq $19 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCoredumpSetAttribute + .p2align 4 + .type cuCoredumpSetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCoredumpSetAttribute +#endif +cuCoredumpSetAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+160(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+160(%rip) +2: + pushq $20 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCoredumpSetAttributeGlobal + .p2align 4 + .type cuCoredumpSetAttributeGlobal, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCoredumpSetAttributeGlobal +#endif +cuCoredumpSetAttributeGlobal: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+168(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+168(%rip) +2: + pushq $21 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxAttach + .p2align 4 + .type cuCtxAttach, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxAttach +#endif +cuCtxAttach: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+176(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+176(%rip) +2: + pushq $22 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxCreate + .p2align 4 + .type cuCtxCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxCreate +#endif +cuCtxCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+184(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+184(%rip) +2: + pushq $23 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxCreate_v2 + .p2align 4 + .type cuCtxCreate_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxCreate_v2 +#endif +cuCtxCreate_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+192(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+192(%rip) +2: + pushq $24 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxCreate_v3 + .p2align 4 + .type cuCtxCreate_v3, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxCreate_v3 +#endif +cuCtxCreate_v3: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+200(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+200(%rip) +2: + pushq $25 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxCreate_v4 + .p2align 4 + .type cuCtxCreate_v4, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxCreate_v4 +#endif +cuCtxCreate_v4: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+208(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+208(%rip) +2: + pushq $26 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxDestroy + .p2align 4 + .type cuCtxDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxDestroy +#endif +cuCtxDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+216(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+216(%rip) +2: + pushq $27 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxDestroy_v2 + .p2align 4 + .type cuCtxDestroy_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxDestroy_v2 +#endif +cuCtxDestroy_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+224(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+224(%rip) +2: + pushq $28 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxDetach + .p2align 4 + .type cuCtxDetach, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxDetach +#endif +cuCtxDetach: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+232(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+232(%rip) +2: + pushq $29 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxDisablePeerAccess + .p2align 4 + .type cuCtxDisablePeerAccess, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxDisablePeerAccess +#endif +cuCtxDisablePeerAccess: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+240(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+240(%rip) +2: + pushq $30 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxEnablePeerAccess + .p2align 4 + .type cuCtxEnablePeerAccess, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxEnablePeerAccess +#endif +cuCtxEnablePeerAccess: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+248(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+248(%rip) +2: + pushq $31 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxFromGreenCtx + .p2align 4 + .type cuCtxFromGreenCtx, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxFromGreenCtx +#endif +cuCtxFromGreenCtx: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+256(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+256(%rip) +2: + pushq $32 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetApiVersion + .p2align 4 + .type cuCtxGetApiVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetApiVersion +#endif +cuCtxGetApiVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+264(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+264(%rip) +2: + pushq $33 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetCacheConfig + .p2align 4 + .type cuCtxGetCacheConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetCacheConfig +#endif +cuCtxGetCacheConfig: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+272(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+272(%rip) +2: + pushq $34 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetCurrent + .p2align 4 + .type cuCtxGetCurrent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetCurrent +#endif +cuCtxGetCurrent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+280(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+280(%rip) +2: + pushq $35 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetDevResource + .p2align 4 + .type cuCtxGetDevResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetDevResource +#endif +cuCtxGetDevResource: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+288(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+288(%rip) +2: + pushq $36 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetDevice + .p2align 4 + .type cuCtxGetDevice, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetDevice +#endif +cuCtxGetDevice: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+296(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+296(%rip) +2: + pushq $37 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetExecAffinity + .p2align 4 + .type cuCtxGetExecAffinity, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetExecAffinity +#endif +cuCtxGetExecAffinity: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+304(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+304(%rip) +2: + pushq $38 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetFlags + .p2align 4 + .type cuCtxGetFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetFlags +#endif +cuCtxGetFlags: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+312(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+312(%rip) +2: + pushq $39 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetId + .p2align 4 + .type cuCtxGetId, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetId +#endif +cuCtxGetId: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+320(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+320(%rip) +2: + pushq $40 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetLimit + .p2align 4 + .type cuCtxGetLimit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetLimit +#endif +cuCtxGetLimit: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+328(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+328(%rip) +2: + pushq $41 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetSharedMemConfig + .p2align 4 + .type cuCtxGetSharedMemConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetSharedMemConfig +#endif +cuCtxGetSharedMemConfig: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+336(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+336(%rip) +2: + pushq $42 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxGetStreamPriorityRange + .p2align 4 + .type cuCtxGetStreamPriorityRange, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxGetStreamPriorityRange +#endif +cuCtxGetStreamPriorityRange: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+344(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+344(%rip) +2: + pushq $43 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxPopCurrent + .p2align 4 + .type cuCtxPopCurrent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxPopCurrent +#endif +cuCtxPopCurrent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+352(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+352(%rip) +2: + pushq $44 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxPopCurrent_v2 + .p2align 4 + .type cuCtxPopCurrent_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxPopCurrent_v2 +#endif +cuCtxPopCurrent_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+360(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+360(%rip) +2: + pushq $45 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxPushCurrent + .p2align 4 + .type cuCtxPushCurrent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxPushCurrent +#endif +cuCtxPushCurrent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+368(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+368(%rip) +2: + pushq $46 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxPushCurrent_v2 + .p2align 4 + .type cuCtxPushCurrent_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxPushCurrent_v2 +#endif +cuCtxPushCurrent_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+376(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+376(%rip) +2: + pushq $47 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxRecordEvent + .p2align 4 + .type cuCtxRecordEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxRecordEvent +#endif +cuCtxRecordEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+384(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+384(%rip) +2: + pushq $48 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxResetPersistingL2Cache + .p2align 4 + .type cuCtxResetPersistingL2Cache, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxResetPersistingL2Cache +#endif +cuCtxResetPersistingL2Cache: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+392(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+392(%rip) +2: + pushq $49 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxSetCacheConfig + .p2align 4 + .type cuCtxSetCacheConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxSetCacheConfig +#endif +cuCtxSetCacheConfig: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+400(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+400(%rip) +2: + pushq $50 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxSetCurrent + .p2align 4 + .type cuCtxSetCurrent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxSetCurrent +#endif +cuCtxSetCurrent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+408(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+408(%rip) +2: + pushq $51 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxSetFlags + .p2align 4 + .type cuCtxSetFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxSetFlags +#endif +cuCtxSetFlags: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+416(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+416(%rip) +2: + pushq $52 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxSetLimit + .p2align 4 + .type cuCtxSetLimit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxSetLimit +#endif +cuCtxSetLimit: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+424(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+424(%rip) +2: + pushq $53 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxSetSharedMemConfig + .p2align 4 + .type cuCtxSetSharedMemConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxSetSharedMemConfig +#endif +cuCtxSetSharedMemConfig: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+432(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+432(%rip) +2: + pushq $54 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxSynchronize + .p2align 4 + .type cuCtxSynchronize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxSynchronize +#endif +cuCtxSynchronize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+440(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+440(%rip) +2: + pushq $55 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuCtxWaitEvent + .p2align 4 + .type cuCtxWaitEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuCtxWaitEvent +#endif +cuCtxWaitEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+448(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+448(%rip) +2: + pushq $56 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDestroyExternalMemory + .p2align 4 + .type cuDestroyExternalMemory, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDestroyExternalMemory +#endif +cuDestroyExternalMemory: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+456(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+456(%rip) +2: + pushq $57 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDestroyExternalSemaphore + .p2align 4 + .type cuDestroyExternalSemaphore, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDestroyExternalSemaphore +#endif +cuDestroyExternalSemaphore: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+464(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+464(%rip) +2: + pushq $58 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevResourceGenerateDesc + .p2align 4 + .type cuDevResourceGenerateDesc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevResourceGenerateDesc +#endif +cuDevResourceGenerateDesc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+472(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+472(%rip) +2: + pushq $59 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevSmResourceSplitByCount + .p2align 4 + .type cuDevSmResourceSplitByCount, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevSmResourceSplitByCount +#endif +cuDevSmResourceSplitByCount: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+480(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+480(%rip) +2: + pushq $60 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceCanAccessPeer + .p2align 4 + .type cuDeviceCanAccessPeer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceCanAccessPeer +#endif +cuDeviceCanAccessPeer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+488(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+488(%rip) +2: + pushq $61 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceComputeCapability + .p2align 4 + .type cuDeviceComputeCapability, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceComputeCapability +#endif +cuDeviceComputeCapability: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+496(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+496(%rip) +2: + pushq $62 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGet + .p2align 4 + .type cuDeviceGet, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGet +#endif +cuDeviceGet: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+504(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+504(%rip) +2: + pushq $63 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetAttribute + .p2align 4 + .type cuDeviceGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetAttribute +#endif +cuDeviceGetAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+512(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+512(%rip) +2: + pushq $64 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetByPCIBusId + .p2align 4 + .type cuDeviceGetByPCIBusId, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetByPCIBusId +#endif +cuDeviceGetByPCIBusId: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+520(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+520(%rip) +2: + pushq $65 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetCount + .p2align 4 + .type cuDeviceGetCount, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetCount +#endif +cuDeviceGetCount: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+528(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+528(%rip) +2: + pushq $66 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetDefaultMemPool + .p2align 4 + .type cuDeviceGetDefaultMemPool, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetDefaultMemPool +#endif +cuDeviceGetDefaultMemPool: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+536(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+536(%rip) +2: + pushq $67 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetDevResource + .p2align 4 + .type cuDeviceGetDevResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetDevResource +#endif +cuDeviceGetDevResource: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+544(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+544(%rip) +2: + pushq $68 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetExecAffinitySupport + .p2align 4 + .type cuDeviceGetExecAffinitySupport, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetExecAffinitySupport +#endif +cuDeviceGetExecAffinitySupport: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+552(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+552(%rip) +2: + pushq $69 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetGraphMemAttribute + .p2align 4 + .type cuDeviceGetGraphMemAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetGraphMemAttribute +#endif +cuDeviceGetGraphMemAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+560(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+560(%rip) +2: + pushq $70 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetLuid + .p2align 4 + .type cuDeviceGetLuid, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetLuid +#endif +cuDeviceGetLuid: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+568(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+568(%rip) +2: + pushq $71 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetMemPool + .p2align 4 + .type cuDeviceGetMemPool, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetMemPool +#endif +cuDeviceGetMemPool: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+576(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+576(%rip) +2: + pushq $72 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetName + .p2align 4 + .type cuDeviceGetName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetName +#endif +cuDeviceGetName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+584(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+584(%rip) +2: + pushq $73 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetNvSciSyncAttributes + .p2align 4 + .type cuDeviceGetNvSciSyncAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetNvSciSyncAttributes +#endif +cuDeviceGetNvSciSyncAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+592(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+592(%rip) +2: + pushq $74 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetP2PAttribute + .p2align 4 + .type cuDeviceGetP2PAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetP2PAttribute +#endif +cuDeviceGetP2PAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+600(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+600(%rip) +2: + pushq $75 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetPCIBusId + .p2align 4 + .type cuDeviceGetPCIBusId, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetPCIBusId +#endif +cuDeviceGetPCIBusId: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+608(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+608(%rip) +2: + pushq $76 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetProperties + .p2align 4 + .type cuDeviceGetProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetProperties +#endif +cuDeviceGetProperties: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+616(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+616(%rip) +2: + pushq $77 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetTexture1DLinearMaxWidth + .p2align 4 + .type cuDeviceGetTexture1DLinearMaxWidth, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetTexture1DLinearMaxWidth +#endif +cuDeviceGetTexture1DLinearMaxWidth: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+624(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+624(%rip) +2: + pushq $78 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetUuid + .p2align 4 + .type cuDeviceGetUuid, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetUuid +#endif +cuDeviceGetUuid: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+632(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+632(%rip) +2: + pushq $79 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGetUuid_v2 + .p2align 4 + .type cuDeviceGetUuid_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGetUuid_v2 +#endif +cuDeviceGetUuid_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+640(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+640(%rip) +2: + pushq $80 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceGraphMemTrim + .p2align 4 + .type cuDeviceGraphMemTrim, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceGraphMemTrim +#endif +cuDeviceGraphMemTrim: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+648(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+648(%rip) +2: + pushq $81 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevicePrimaryCtxGetState + .p2align 4 + .type cuDevicePrimaryCtxGetState, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevicePrimaryCtxGetState +#endif +cuDevicePrimaryCtxGetState: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+656(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+656(%rip) +2: + pushq $82 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevicePrimaryCtxRelease + .p2align 4 + .type cuDevicePrimaryCtxRelease, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevicePrimaryCtxRelease +#endif +cuDevicePrimaryCtxRelease: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+664(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+664(%rip) +2: + pushq $83 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevicePrimaryCtxRelease_v2 + .p2align 4 + .type cuDevicePrimaryCtxRelease_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevicePrimaryCtxRelease_v2 +#endif +cuDevicePrimaryCtxRelease_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+672(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+672(%rip) +2: + pushq $84 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevicePrimaryCtxReset + .p2align 4 + .type cuDevicePrimaryCtxReset, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevicePrimaryCtxReset +#endif +cuDevicePrimaryCtxReset: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+680(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+680(%rip) +2: + pushq $85 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevicePrimaryCtxReset_v2 + .p2align 4 + .type cuDevicePrimaryCtxReset_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevicePrimaryCtxReset_v2 +#endif +cuDevicePrimaryCtxReset_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+688(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+688(%rip) +2: + pushq $86 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevicePrimaryCtxRetain + .p2align 4 + .type cuDevicePrimaryCtxRetain, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevicePrimaryCtxRetain +#endif +cuDevicePrimaryCtxRetain: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+696(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+696(%rip) +2: + pushq $87 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevicePrimaryCtxSetFlags + .p2align 4 + .type cuDevicePrimaryCtxSetFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevicePrimaryCtxSetFlags +#endif +cuDevicePrimaryCtxSetFlags: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+704(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+704(%rip) +2: + pushq $88 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDevicePrimaryCtxSetFlags_v2 + .p2align 4 + .type cuDevicePrimaryCtxSetFlags_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDevicePrimaryCtxSetFlags_v2 +#endif +cuDevicePrimaryCtxSetFlags_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+712(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+712(%rip) +2: + pushq $89 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceRegisterAsyncNotification + .p2align 4 + .type cuDeviceRegisterAsyncNotification, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceRegisterAsyncNotification +#endif +cuDeviceRegisterAsyncNotification: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+720(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+720(%rip) +2: + pushq $90 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceSetGraphMemAttribute + .p2align 4 + .type cuDeviceSetGraphMemAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceSetGraphMemAttribute +#endif +cuDeviceSetGraphMemAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+728(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+728(%rip) +2: + pushq $91 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceSetMemPool + .p2align 4 + .type cuDeviceSetMemPool, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceSetMemPool +#endif +cuDeviceSetMemPool: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+736(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+736(%rip) +2: + pushq $92 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceTotalMem + .p2align 4 + .type cuDeviceTotalMem, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceTotalMem +#endif +cuDeviceTotalMem: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+744(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+744(%rip) +2: + pushq $93 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceTotalMem_v2 + .p2align 4 + .type cuDeviceTotalMem_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceTotalMem_v2 +#endif +cuDeviceTotalMem_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+752(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+752(%rip) +2: + pushq $94 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDeviceUnregisterAsyncNotification + .p2align 4 + .type cuDeviceUnregisterAsyncNotification, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDeviceUnregisterAsyncNotification +#endif +cuDeviceUnregisterAsyncNotification: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+760(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+760(%rip) +2: + pushq $95 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuDriverGetVersion + .p2align 4 + .type cuDriverGetVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuDriverGetVersion +#endif +cuDriverGetVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+768(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+768(%rip) +2: + pushq $96 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLApiInit + .p2align 4 + .type cuEGLApiInit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLApiInit +#endif +cuEGLApiInit: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+776(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+776(%rip) +2: + pushq $97 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLStreamConsumerAcquireFrame + .p2align 4 + .type cuEGLStreamConsumerAcquireFrame, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLStreamConsumerAcquireFrame +#endif +cuEGLStreamConsumerAcquireFrame: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+784(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+784(%rip) +2: + pushq $98 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLStreamConsumerConnect + .p2align 4 + .type cuEGLStreamConsumerConnect, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLStreamConsumerConnect +#endif +cuEGLStreamConsumerConnect: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+792(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+792(%rip) +2: + pushq $99 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLStreamConsumerConnectWithFlags + .p2align 4 + .type cuEGLStreamConsumerConnectWithFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLStreamConsumerConnectWithFlags +#endif +cuEGLStreamConsumerConnectWithFlags: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+800(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+800(%rip) +2: + pushq $100 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLStreamConsumerDisconnect + .p2align 4 + .type cuEGLStreamConsumerDisconnect, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLStreamConsumerDisconnect +#endif +cuEGLStreamConsumerDisconnect: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+808(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+808(%rip) +2: + pushq $101 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLStreamConsumerReleaseFrame + .p2align 4 + .type cuEGLStreamConsumerReleaseFrame, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLStreamConsumerReleaseFrame +#endif +cuEGLStreamConsumerReleaseFrame: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+816(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+816(%rip) +2: + pushq $102 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLStreamProducerConnect + .p2align 4 + .type cuEGLStreamProducerConnect, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLStreamProducerConnect +#endif +cuEGLStreamProducerConnect: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+824(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+824(%rip) +2: + pushq $103 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLStreamProducerDisconnect + .p2align 4 + .type cuEGLStreamProducerDisconnect, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLStreamProducerDisconnect +#endif +cuEGLStreamProducerDisconnect: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+832(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+832(%rip) +2: + pushq $104 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLStreamProducerPresentFrame + .p2align 4 + .type cuEGLStreamProducerPresentFrame, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLStreamProducerPresentFrame +#endif +cuEGLStreamProducerPresentFrame: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+840(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+840(%rip) +2: + pushq $105 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEGLStreamProducerReturnFrame + .p2align 4 + .type cuEGLStreamProducerReturnFrame, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEGLStreamProducerReturnFrame +#endif +cuEGLStreamProducerReturnFrame: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+848(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+848(%rip) +2: + pushq $106 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventCreate + .p2align 4 + .type cuEventCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventCreate +#endif +cuEventCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+856(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+856(%rip) +2: + pushq $107 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventDestroy + .p2align 4 + .type cuEventDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventDestroy +#endif +cuEventDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+864(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+864(%rip) +2: + pushq $108 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventDestroy_v2 + .p2align 4 + .type cuEventDestroy_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventDestroy_v2 +#endif +cuEventDestroy_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+872(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+872(%rip) +2: + pushq $109 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventElapsedTime + .p2align 4 + .type cuEventElapsedTime, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventElapsedTime +#endif +cuEventElapsedTime: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+880(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+880(%rip) +2: + pushq $110 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventElapsedTime_v2 + .p2align 4 + .type cuEventElapsedTime_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventElapsedTime_v2 +#endif +cuEventElapsedTime_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+888(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+888(%rip) +2: + pushq $111 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventQuery + .p2align 4 + .type cuEventQuery, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventQuery +#endif +cuEventQuery: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+896(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+896(%rip) +2: + pushq $112 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventRecord + .p2align 4 + .type cuEventRecord, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventRecord +#endif +cuEventRecord: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+904(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+904(%rip) +2: + pushq $113 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventRecordWithFlags + .p2align 4 + .type cuEventRecordWithFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventRecordWithFlags +#endif +cuEventRecordWithFlags: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+912(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+912(%rip) +2: + pushq $114 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventRecordWithFlags_ptsz + .p2align 4 + .type cuEventRecordWithFlags_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventRecordWithFlags_ptsz +#endif +cuEventRecordWithFlags_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+920(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+920(%rip) +2: + pushq $115 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventRecord_ptsz + .p2align 4 + .type cuEventRecord_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventRecord_ptsz +#endif +cuEventRecord_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+928(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+928(%rip) +2: + pushq $116 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuEventSynchronize + .p2align 4 + .type cuEventSynchronize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuEventSynchronize +#endif +cuEventSynchronize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+936(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+936(%rip) +2: + pushq $117 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuExternalMemoryGetMappedBuffer + .p2align 4 + .type cuExternalMemoryGetMappedBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuExternalMemoryGetMappedBuffer +#endif +cuExternalMemoryGetMappedBuffer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+944(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+944(%rip) +2: + pushq $118 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuExternalMemoryGetMappedMipmappedArray + .p2align 4 + .type cuExternalMemoryGetMappedMipmappedArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuExternalMemoryGetMappedMipmappedArray +#endif +cuExternalMemoryGetMappedMipmappedArray: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+952(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+952(%rip) +2: + pushq $119 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFlushGPUDirectRDMAWrites + .p2align 4 + .type cuFlushGPUDirectRDMAWrites, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFlushGPUDirectRDMAWrites +#endif +cuFlushGPUDirectRDMAWrites: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+960(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+960(%rip) +2: + pushq $120 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncGetAttribute + .p2align 4 + .type cuFuncGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncGetAttribute +#endif +cuFuncGetAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+968(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+968(%rip) +2: + pushq $121 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncGetModule + .p2align 4 + .type cuFuncGetModule, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncGetModule +#endif +cuFuncGetModule: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+976(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+976(%rip) +2: + pushq $122 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncGetName + .p2align 4 + .type cuFuncGetName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncGetName +#endif +cuFuncGetName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+984(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+984(%rip) +2: + pushq $123 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncGetParamInfo + .p2align 4 + .type cuFuncGetParamInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncGetParamInfo +#endif +cuFuncGetParamInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+992(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+992(%rip) +2: + pushq $124 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncIsLoaded + .p2align 4 + .type cuFuncIsLoaded, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncIsLoaded +#endif +cuFuncIsLoaded: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1000(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1000(%rip) +2: + pushq $125 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncLoad + .p2align 4 + .type cuFuncLoad, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncLoad +#endif +cuFuncLoad: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1008(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1008(%rip) +2: + pushq $126 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncSetAttribute + .p2align 4 + .type cuFuncSetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncSetAttribute +#endif +cuFuncSetAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1016(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1016(%rip) +2: + pushq $127 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncSetBlockShape + .p2align 4 + .type cuFuncSetBlockShape, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncSetBlockShape +#endif +cuFuncSetBlockShape: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1024(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1024(%rip) +2: + pushq $128 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncSetCacheConfig + .p2align 4 + .type cuFuncSetCacheConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncSetCacheConfig +#endif +cuFuncSetCacheConfig: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1032(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1032(%rip) +2: + pushq $129 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncSetSharedMemConfig + .p2align 4 + .type cuFuncSetSharedMemConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncSetSharedMemConfig +#endif +cuFuncSetSharedMemConfig: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1040(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1040(%rip) +2: + pushq $130 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuFuncSetSharedSize + .p2align 4 + .type cuFuncSetSharedSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuFuncSetSharedSize +#endif +cuFuncSetSharedSize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1048(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1048(%rip) +2: + pushq $131 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLCtxCreate + .p2align 4 + .type cuGLCtxCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLCtxCreate +#endif +cuGLCtxCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1056(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1056(%rip) +2: + pushq $132 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLCtxCreate_v2 + .p2align 4 + .type cuGLCtxCreate_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLCtxCreate_v2 +#endif +cuGLCtxCreate_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1064(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1064(%rip) +2: + pushq $133 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLGetDevices + .p2align 4 + .type cuGLGetDevices, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLGetDevices +#endif +cuGLGetDevices: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1072(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1072(%rip) +2: + pushq $134 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLGetDevices_v2 + .p2align 4 + .type cuGLGetDevices_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLGetDevices_v2 +#endif +cuGLGetDevices_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1080(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1080(%rip) +2: + pushq $135 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLInit + .p2align 4 + .type cuGLInit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLInit +#endif +cuGLInit: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1088(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1088(%rip) +2: + pushq $136 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLMapBufferObject + .p2align 4 + .type cuGLMapBufferObject, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLMapBufferObject +#endif +cuGLMapBufferObject: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1096(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1096(%rip) +2: + pushq $137 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLMapBufferObjectAsync + .p2align 4 + .type cuGLMapBufferObjectAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLMapBufferObjectAsync +#endif +cuGLMapBufferObjectAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1104(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1104(%rip) +2: + pushq $138 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLMapBufferObjectAsync_v2 + .p2align 4 + .type cuGLMapBufferObjectAsync_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLMapBufferObjectAsync_v2 +#endif +cuGLMapBufferObjectAsync_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1112(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1112(%rip) +2: + pushq $139 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLMapBufferObjectAsync_v2_ptsz + .p2align 4 + .type cuGLMapBufferObjectAsync_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLMapBufferObjectAsync_v2_ptsz +#endif +cuGLMapBufferObjectAsync_v2_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1120(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1120(%rip) +2: + pushq $140 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLMapBufferObject_v2 + .p2align 4 + .type cuGLMapBufferObject_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLMapBufferObject_v2 +#endif +cuGLMapBufferObject_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1128(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1128(%rip) +2: + pushq $141 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLMapBufferObject_v2_ptds + .p2align 4 + .type cuGLMapBufferObject_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLMapBufferObject_v2_ptds +#endif +cuGLMapBufferObject_v2_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1136(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1136(%rip) +2: + pushq $142 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLRegisterBufferObject + .p2align 4 + .type cuGLRegisterBufferObject, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLRegisterBufferObject +#endif +cuGLRegisterBufferObject: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1144(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1144(%rip) +2: + pushq $143 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLSetBufferObjectMapFlags + .p2align 4 + .type cuGLSetBufferObjectMapFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLSetBufferObjectMapFlags +#endif +cuGLSetBufferObjectMapFlags: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1152(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1152(%rip) +2: + pushq $144 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLUnmapBufferObject + .p2align 4 + .type cuGLUnmapBufferObject, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLUnmapBufferObject +#endif +cuGLUnmapBufferObject: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1160(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1160(%rip) +2: + pushq $145 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLUnmapBufferObjectAsync + .p2align 4 + .type cuGLUnmapBufferObjectAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLUnmapBufferObjectAsync +#endif +cuGLUnmapBufferObjectAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1168(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1168(%rip) +2: + pushq $146 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGLUnregisterBufferObject + .p2align 4 + .type cuGLUnregisterBufferObject, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGLUnregisterBufferObject +#endif +cuGLUnregisterBufferObject: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1176(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1176(%rip) +2: + pushq $147 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGetErrorName + .p2align 4 + .type cuGetErrorName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGetErrorName +#endif +cuGetErrorName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1184(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1184(%rip) +2: + pushq $148 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGetErrorString + .p2align 4 + .type cuGetErrorString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGetErrorString +#endif +cuGetErrorString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1192(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1192(%rip) +2: + pushq $149 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGetExportTable + .p2align 4 + .type cuGetExportTable, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGetExportTable +#endif +cuGetExportTable: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1200(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1200(%rip) +2: + pushq $150 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGetProcAddress + .p2align 4 + .type cuGetProcAddress, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGetProcAddress +#endif +cuGetProcAddress: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1208(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1208(%rip) +2: + pushq $151 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGetProcAddress_v2 + .p2align 4 + .type cuGetProcAddress_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGetProcAddress_v2 +#endif +cuGetProcAddress_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1216(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1216(%rip) +2: + pushq $152 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddBatchMemOpNode + .p2align 4 + .type cuGraphAddBatchMemOpNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddBatchMemOpNode +#endif +cuGraphAddBatchMemOpNode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1224(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1224(%rip) +2: + pushq $153 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddChildGraphNode + .p2align 4 + .type cuGraphAddChildGraphNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddChildGraphNode +#endif +cuGraphAddChildGraphNode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1232(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1232(%rip) +2: + pushq $154 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddDependencies + .p2align 4 + .type cuGraphAddDependencies, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddDependencies +#endif +cuGraphAddDependencies: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1240(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1240(%rip) +2: + pushq $155 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddDependencies_v2 + .p2align 4 + .type cuGraphAddDependencies_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddDependencies_v2 +#endif +cuGraphAddDependencies_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1248(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1248(%rip) +2: + pushq $156 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddEmptyNode + .p2align 4 + .type cuGraphAddEmptyNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddEmptyNode +#endif +cuGraphAddEmptyNode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1256(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1256(%rip) +2: + pushq $157 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddEventRecordNode + .p2align 4 + .type cuGraphAddEventRecordNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddEventRecordNode +#endif +cuGraphAddEventRecordNode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1264(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1264(%rip) +2: + pushq $158 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddEventWaitNode + .p2align 4 + .type cuGraphAddEventWaitNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddEventWaitNode +#endif +cuGraphAddEventWaitNode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1272(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1272(%rip) +2: + pushq $159 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddExternalSemaphoresSignalNode + .p2align 4 + .type cuGraphAddExternalSemaphoresSignalNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddExternalSemaphoresSignalNode +#endif +cuGraphAddExternalSemaphoresSignalNode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1280(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1280(%rip) +2: + pushq $160 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddExternalSemaphoresWaitNode + .p2align 4 + .type cuGraphAddExternalSemaphoresWaitNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddExternalSemaphoresWaitNode +#endif +cuGraphAddExternalSemaphoresWaitNode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1288(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1288(%rip) +2: + pushq $161 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddHostNode + .p2align 4 + .type cuGraphAddHostNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddHostNode +#endif +cuGraphAddHostNode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1296(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1296(%rip) +2: + pushq $162 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddKernelNode + .p2align 4 + .type cuGraphAddKernelNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddKernelNode +#endif +cuGraphAddKernelNode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1304(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1304(%rip) +2: + pushq $163 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddKernelNode_v2 + .p2align 4 + .type cuGraphAddKernelNode_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddKernelNode_v2 +#endif +cuGraphAddKernelNode_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1312(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1312(%rip) +2: + pushq $164 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddMemAllocNode + .p2align 4 + .type cuGraphAddMemAllocNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddMemAllocNode +#endif +cuGraphAddMemAllocNode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1320(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1320(%rip) +2: + pushq $165 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddMemFreeNode + .p2align 4 + .type cuGraphAddMemFreeNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddMemFreeNode +#endif +cuGraphAddMemFreeNode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1328(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1328(%rip) +2: + pushq $166 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddMemcpyNode + .p2align 4 + .type cuGraphAddMemcpyNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddMemcpyNode +#endif +cuGraphAddMemcpyNode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1336(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1336(%rip) +2: + pushq $167 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddMemsetNode + .p2align 4 + .type cuGraphAddMemsetNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddMemsetNode +#endif +cuGraphAddMemsetNode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1344(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1344(%rip) +2: + pushq $168 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddNode + .p2align 4 + .type cuGraphAddNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddNode +#endif +cuGraphAddNode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1352(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1352(%rip) +2: + pushq $169 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphAddNode_v2 + .p2align 4 + .type cuGraphAddNode_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphAddNode_v2 +#endif +cuGraphAddNode_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1360(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1360(%rip) +2: + pushq $170 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphBatchMemOpNodeGetParams + .p2align 4 + .type cuGraphBatchMemOpNodeGetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphBatchMemOpNodeGetParams +#endif +cuGraphBatchMemOpNodeGetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1368(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1368(%rip) +2: + pushq $171 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphBatchMemOpNodeSetParams + .p2align 4 + .type cuGraphBatchMemOpNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphBatchMemOpNodeSetParams +#endif +cuGraphBatchMemOpNodeSetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1376(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1376(%rip) +2: + pushq $172 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphChildGraphNodeGetGraph + .p2align 4 + .type cuGraphChildGraphNodeGetGraph, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphChildGraphNodeGetGraph +#endif +cuGraphChildGraphNodeGetGraph: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1384(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1384(%rip) +2: + pushq $173 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphClone + .p2align 4 + .type cuGraphClone, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphClone +#endif +cuGraphClone: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1392(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1392(%rip) +2: + pushq $174 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphConditionalHandleCreate + .p2align 4 + .type cuGraphConditionalHandleCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphConditionalHandleCreate +#endif +cuGraphConditionalHandleCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1400(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1400(%rip) +2: + pushq $175 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphCreate + .p2align 4 + .type cuGraphCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphCreate +#endif +cuGraphCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1408(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1408(%rip) +2: + pushq $176 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphDebugDotPrint + .p2align 4 + .type cuGraphDebugDotPrint, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphDebugDotPrint +#endif +cuGraphDebugDotPrint: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1416(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1416(%rip) +2: + pushq $177 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphDestroy + .p2align 4 + .type cuGraphDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphDestroy +#endif +cuGraphDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1424(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1424(%rip) +2: + pushq $178 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphDestroyNode + .p2align 4 + .type cuGraphDestroyNode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphDestroyNode +#endif +cuGraphDestroyNode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1432(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1432(%rip) +2: + pushq $179 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphEventRecordNodeGetEvent + .p2align 4 + .type cuGraphEventRecordNodeGetEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphEventRecordNodeGetEvent +#endif +cuGraphEventRecordNodeGetEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1440(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1440(%rip) +2: + pushq $180 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphEventRecordNodeSetEvent + .p2align 4 + .type cuGraphEventRecordNodeSetEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphEventRecordNodeSetEvent +#endif +cuGraphEventRecordNodeSetEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1448(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1448(%rip) +2: + pushq $181 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphEventWaitNodeGetEvent + .p2align 4 + .type cuGraphEventWaitNodeGetEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphEventWaitNodeGetEvent +#endif +cuGraphEventWaitNodeGetEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1456(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1456(%rip) +2: + pushq $182 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphEventWaitNodeSetEvent + .p2align 4 + .type cuGraphEventWaitNodeSetEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphEventWaitNodeSetEvent +#endif +cuGraphEventWaitNodeSetEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1464(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1464(%rip) +2: + pushq $183 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecBatchMemOpNodeSetParams + .p2align 4 + .type cuGraphExecBatchMemOpNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecBatchMemOpNodeSetParams +#endif +cuGraphExecBatchMemOpNodeSetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1472(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1472(%rip) +2: + pushq $184 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecChildGraphNodeSetParams + .p2align 4 + .type cuGraphExecChildGraphNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecChildGraphNodeSetParams +#endif +cuGraphExecChildGraphNodeSetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1480(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1480(%rip) +2: + pushq $185 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecDestroy + .p2align 4 + .type cuGraphExecDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecDestroy +#endif +cuGraphExecDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1488(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1488(%rip) +2: + pushq $186 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecEventRecordNodeSetEvent + .p2align 4 + .type cuGraphExecEventRecordNodeSetEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecEventRecordNodeSetEvent +#endif +cuGraphExecEventRecordNodeSetEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1496(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1496(%rip) +2: + pushq $187 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecEventWaitNodeSetEvent + .p2align 4 + .type cuGraphExecEventWaitNodeSetEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecEventWaitNodeSetEvent +#endif +cuGraphExecEventWaitNodeSetEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1504(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1504(%rip) +2: + pushq $188 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecExternalSemaphoresSignalNodeSetParams + .p2align 4 + .type cuGraphExecExternalSemaphoresSignalNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecExternalSemaphoresSignalNodeSetParams +#endif +cuGraphExecExternalSemaphoresSignalNodeSetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1512(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1512(%rip) +2: + pushq $189 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecExternalSemaphoresWaitNodeSetParams + .p2align 4 + .type cuGraphExecExternalSemaphoresWaitNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecExternalSemaphoresWaitNodeSetParams +#endif +cuGraphExecExternalSemaphoresWaitNodeSetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1520(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1520(%rip) +2: + pushq $190 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecGetFlags + .p2align 4 + .type cuGraphExecGetFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecGetFlags +#endif +cuGraphExecGetFlags: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1528(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1528(%rip) +2: + pushq $191 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecHostNodeSetParams + .p2align 4 + .type cuGraphExecHostNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecHostNodeSetParams +#endif +cuGraphExecHostNodeSetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1536(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1536(%rip) +2: + pushq $192 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecKernelNodeSetParams + .p2align 4 + .type cuGraphExecKernelNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecKernelNodeSetParams +#endif +cuGraphExecKernelNodeSetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1544(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1544(%rip) +2: + pushq $193 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecKernelNodeSetParams_v2 + .p2align 4 + .type cuGraphExecKernelNodeSetParams_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecKernelNodeSetParams_v2 +#endif +cuGraphExecKernelNodeSetParams_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1552(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1552(%rip) +2: + pushq $194 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecMemcpyNodeSetParams + .p2align 4 + .type cuGraphExecMemcpyNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecMemcpyNodeSetParams +#endif +cuGraphExecMemcpyNodeSetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1560(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1560(%rip) +2: + pushq $195 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecMemsetNodeSetParams + .p2align 4 + .type cuGraphExecMemsetNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecMemsetNodeSetParams +#endif +cuGraphExecMemsetNodeSetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1568(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1568(%rip) +2: + pushq $196 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecNodeSetParams + .p2align 4 + .type cuGraphExecNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecNodeSetParams +#endif +cuGraphExecNodeSetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1576(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1576(%rip) +2: + pushq $197 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecUpdate + .p2align 4 + .type cuGraphExecUpdate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecUpdate +#endif +cuGraphExecUpdate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1584(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1584(%rip) +2: + pushq $198 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExecUpdate_v2 + .p2align 4 + .type cuGraphExecUpdate_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExecUpdate_v2 +#endif +cuGraphExecUpdate_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1592(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1592(%rip) +2: + pushq $199 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExternalSemaphoresSignalNodeGetParams + .p2align 4 + .type cuGraphExternalSemaphoresSignalNodeGetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExternalSemaphoresSignalNodeGetParams +#endif +cuGraphExternalSemaphoresSignalNodeGetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1600(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1600(%rip) +2: + pushq $200 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExternalSemaphoresSignalNodeSetParams + .p2align 4 + .type cuGraphExternalSemaphoresSignalNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExternalSemaphoresSignalNodeSetParams +#endif +cuGraphExternalSemaphoresSignalNodeSetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1608(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1608(%rip) +2: + pushq $201 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExternalSemaphoresWaitNodeGetParams + .p2align 4 + .type cuGraphExternalSemaphoresWaitNodeGetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExternalSemaphoresWaitNodeGetParams +#endif +cuGraphExternalSemaphoresWaitNodeGetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1616(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1616(%rip) +2: + pushq $202 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphExternalSemaphoresWaitNodeSetParams + .p2align 4 + .type cuGraphExternalSemaphoresWaitNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphExternalSemaphoresWaitNodeSetParams +#endif +cuGraphExternalSemaphoresWaitNodeSetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1624(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1624(%rip) +2: + pushq $203 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphGetEdges + .p2align 4 + .type cuGraphGetEdges, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphGetEdges +#endif +cuGraphGetEdges: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1632(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1632(%rip) +2: + pushq $204 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphGetEdges_v2 + .p2align 4 + .type cuGraphGetEdges_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphGetEdges_v2 +#endif +cuGraphGetEdges_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1640(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1640(%rip) +2: + pushq $205 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphGetNodes + .p2align 4 + .type cuGraphGetNodes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphGetNodes +#endif +cuGraphGetNodes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1648(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1648(%rip) +2: + pushq $206 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphGetRootNodes + .p2align 4 + .type cuGraphGetRootNodes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphGetRootNodes +#endif +cuGraphGetRootNodes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1656(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1656(%rip) +2: + pushq $207 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphHostNodeGetParams + .p2align 4 + .type cuGraphHostNodeGetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphHostNodeGetParams +#endif +cuGraphHostNodeGetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1664(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1664(%rip) +2: + pushq $208 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphHostNodeSetParams + .p2align 4 + .type cuGraphHostNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphHostNodeSetParams +#endif +cuGraphHostNodeSetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1672(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1672(%rip) +2: + pushq $209 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphInstantiate + .p2align 4 + .type cuGraphInstantiate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphInstantiate +#endif +cuGraphInstantiate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1680(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1680(%rip) +2: + pushq $210 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphInstantiateWithFlags + .p2align 4 + .type cuGraphInstantiateWithFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphInstantiateWithFlags +#endif +cuGraphInstantiateWithFlags: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1688(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1688(%rip) +2: + pushq $211 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphInstantiateWithParams + .p2align 4 + .type cuGraphInstantiateWithParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphInstantiateWithParams +#endif +cuGraphInstantiateWithParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1696(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1696(%rip) +2: + pushq $212 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphInstantiateWithParams_ptsz + .p2align 4 + .type cuGraphInstantiateWithParams_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphInstantiateWithParams_ptsz +#endif +cuGraphInstantiateWithParams_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1704(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1704(%rip) +2: + pushq $213 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphInstantiate_v2 + .p2align 4 + .type cuGraphInstantiate_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphInstantiate_v2 +#endif +cuGraphInstantiate_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1712(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1712(%rip) +2: + pushq $214 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphKernelNodeCopyAttributes + .p2align 4 + .type cuGraphKernelNodeCopyAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphKernelNodeCopyAttributes +#endif +cuGraphKernelNodeCopyAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1720(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1720(%rip) +2: + pushq $215 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphKernelNodeGetAttribute + .p2align 4 + .type cuGraphKernelNodeGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphKernelNodeGetAttribute +#endif +cuGraphKernelNodeGetAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1728(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1728(%rip) +2: + pushq $216 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphKernelNodeGetParams + .p2align 4 + .type cuGraphKernelNodeGetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphKernelNodeGetParams +#endif +cuGraphKernelNodeGetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1736(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1736(%rip) +2: + pushq $217 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphKernelNodeGetParams_v2 + .p2align 4 + .type cuGraphKernelNodeGetParams_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphKernelNodeGetParams_v2 +#endif +cuGraphKernelNodeGetParams_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1744(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1744(%rip) +2: + pushq $218 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphKernelNodeSetAttribute + .p2align 4 + .type cuGraphKernelNodeSetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphKernelNodeSetAttribute +#endif +cuGraphKernelNodeSetAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1752(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1752(%rip) +2: + pushq $219 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphKernelNodeSetParams + .p2align 4 + .type cuGraphKernelNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphKernelNodeSetParams +#endif +cuGraphKernelNodeSetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1760(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1760(%rip) +2: + pushq $220 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphKernelNodeSetParams_v2 + .p2align 4 + .type cuGraphKernelNodeSetParams_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphKernelNodeSetParams_v2 +#endif +cuGraphKernelNodeSetParams_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1768(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1768(%rip) +2: + pushq $221 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphLaunch + .p2align 4 + .type cuGraphLaunch, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphLaunch +#endif +cuGraphLaunch: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1776(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1776(%rip) +2: + pushq $222 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphLaunch_ptsz + .p2align 4 + .type cuGraphLaunch_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphLaunch_ptsz +#endif +cuGraphLaunch_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1784(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1784(%rip) +2: + pushq $223 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphMemAllocNodeGetParams + .p2align 4 + .type cuGraphMemAllocNodeGetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphMemAllocNodeGetParams +#endif +cuGraphMemAllocNodeGetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1792(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1792(%rip) +2: + pushq $224 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphMemFreeNodeGetParams + .p2align 4 + .type cuGraphMemFreeNodeGetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphMemFreeNodeGetParams +#endif +cuGraphMemFreeNodeGetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1800(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1800(%rip) +2: + pushq $225 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphMemcpyNodeGetParams + .p2align 4 + .type cuGraphMemcpyNodeGetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphMemcpyNodeGetParams +#endif +cuGraphMemcpyNodeGetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1808(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1808(%rip) +2: + pushq $226 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphMemcpyNodeSetParams + .p2align 4 + .type cuGraphMemcpyNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphMemcpyNodeSetParams +#endif +cuGraphMemcpyNodeSetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1816(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1816(%rip) +2: + pushq $227 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphMemsetNodeGetParams + .p2align 4 + .type cuGraphMemsetNodeGetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphMemsetNodeGetParams +#endif +cuGraphMemsetNodeGetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1824(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1824(%rip) +2: + pushq $228 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphMemsetNodeSetParams + .p2align 4 + .type cuGraphMemsetNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphMemsetNodeSetParams +#endif +cuGraphMemsetNodeSetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1832(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1832(%rip) +2: + pushq $229 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphNodeFindInClone + .p2align 4 + .type cuGraphNodeFindInClone, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphNodeFindInClone +#endif +cuGraphNodeFindInClone: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1840(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1840(%rip) +2: + pushq $230 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphNodeGetDependencies + .p2align 4 + .type cuGraphNodeGetDependencies, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphNodeGetDependencies +#endif +cuGraphNodeGetDependencies: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1848(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1848(%rip) +2: + pushq $231 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphNodeGetDependencies_v2 + .p2align 4 + .type cuGraphNodeGetDependencies_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphNodeGetDependencies_v2 +#endif +cuGraphNodeGetDependencies_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1856(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1856(%rip) +2: + pushq $232 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphNodeGetDependentNodes + .p2align 4 + .type cuGraphNodeGetDependentNodes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphNodeGetDependentNodes +#endif +cuGraphNodeGetDependentNodes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1864(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1864(%rip) +2: + pushq $233 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphNodeGetDependentNodes_v2 + .p2align 4 + .type cuGraphNodeGetDependentNodes_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphNodeGetDependentNodes_v2 +#endif +cuGraphNodeGetDependentNodes_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1872(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1872(%rip) +2: + pushq $234 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphNodeGetEnabled + .p2align 4 + .type cuGraphNodeGetEnabled, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphNodeGetEnabled +#endif +cuGraphNodeGetEnabled: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1880(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1880(%rip) +2: + pushq $235 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphNodeGetType + .p2align 4 + .type cuGraphNodeGetType, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphNodeGetType +#endif +cuGraphNodeGetType: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1888(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1888(%rip) +2: + pushq $236 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphNodeSetEnabled + .p2align 4 + .type cuGraphNodeSetEnabled, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphNodeSetEnabled +#endif +cuGraphNodeSetEnabled: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1896(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1896(%rip) +2: + pushq $237 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphNodeSetParams + .p2align 4 + .type cuGraphNodeSetParams, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphNodeSetParams +#endif +cuGraphNodeSetParams: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1904(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1904(%rip) +2: + pushq $238 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphReleaseUserObject + .p2align 4 + .type cuGraphReleaseUserObject, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphReleaseUserObject +#endif +cuGraphReleaseUserObject: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1912(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1912(%rip) +2: + pushq $239 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphRemoveDependencies + .p2align 4 + .type cuGraphRemoveDependencies, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphRemoveDependencies +#endif +cuGraphRemoveDependencies: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1920(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1920(%rip) +2: + pushq $240 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphRemoveDependencies_v2 + .p2align 4 + .type cuGraphRemoveDependencies_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphRemoveDependencies_v2 +#endif +cuGraphRemoveDependencies_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1928(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1928(%rip) +2: + pushq $241 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphRetainUserObject + .p2align 4 + .type cuGraphRetainUserObject, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphRetainUserObject +#endif +cuGraphRetainUserObject: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1936(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1936(%rip) +2: + pushq $242 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphUpload + .p2align 4 + .type cuGraphUpload, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphUpload +#endif +cuGraphUpload: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1944(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1944(%rip) +2: + pushq $243 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphUpload_ptsz + .p2align 4 + .type cuGraphUpload_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphUpload_ptsz +#endif +cuGraphUpload_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1952(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1952(%rip) +2: + pushq $244 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsEGLRegisterImage + .p2align 4 + .type cuGraphicsEGLRegisterImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsEGLRegisterImage +#endif +cuGraphicsEGLRegisterImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1960(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1960(%rip) +2: + pushq $245 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsGLRegisterBuffer + .p2align 4 + .type cuGraphicsGLRegisterBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsGLRegisterBuffer +#endif +cuGraphicsGLRegisterBuffer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1968(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1968(%rip) +2: + pushq $246 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsGLRegisterImage + .p2align 4 + .type cuGraphicsGLRegisterImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsGLRegisterImage +#endif +cuGraphicsGLRegisterImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1976(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1976(%rip) +2: + pushq $247 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsMapResources + .p2align 4 + .type cuGraphicsMapResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsMapResources +#endif +cuGraphicsMapResources: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1984(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1984(%rip) +2: + pushq $248 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsMapResources_ptsz + .p2align 4 + .type cuGraphicsMapResources_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsMapResources_ptsz +#endif +cuGraphicsMapResources_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+1992(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+1992(%rip) +2: + pushq $249 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsResourceGetMappedEglFrame + .p2align 4 + .type cuGraphicsResourceGetMappedEglFrame, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsResourceGetMappedEglFrame +#endif +cuGraphicsResourceGetMappedEglFrame: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2000(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2000(%rip) +2: + pushq $250 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsResourceGetMappedMipmappedArray + .p2align 4 + .type cuGraphicsResourceGetMappedMipmappedArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsResourceGetMappedMipmappedArray +#endif +cuGraphicsResourceGetMappedMipmappedArray: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2008(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2008(%rip) +2: + pushq $251 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsResourceGetMappedPointer + .p2align 4 + .type cuGraphicsResourceGetMappedPointer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsResourceGetMappedPointer +#endif +cuGraphicsResourceGetMappedPointer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2016(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2016(%rip) +2: + pushq $252 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsResourceGetMappedPointer_v2 + .p2align 4 + .type cuGraphicsResourceGetMappedPointer_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsResourceGetMappedPointer_v2 +#endif +cuGraphicsResourceGetMappedPointer_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2024(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2024(%rip) +2: + pushq $253 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsResourceSetMapFlags + .p2align 4 + .type cuGraphicsResourceSetMapFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsResourceSetMapFlags +#endif +cuGraphicsResourceSetMapFlags: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2032(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2032(%rip) +2: + pushq $254 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsResourceSetMapFlags_v2 + .p2align 4 + .type cuGraphicsResourceSetMapFlags_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsResourceSetMapFlags_v2 +#endif +cuGraphicsResourceSetMapFlags_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2040(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2040(%rip) +2: + pushq $255 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsSubResourceGetMappedArray + .p2align 4 + .type cuGraphicsSubResourceGetMappedArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsSubResourceGetMappedArray +#endif +cuGraphicsSubResourceGetMappedArray: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2048(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2048(%rip) +2: + pushq $256 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsUnmapResources + .p2align 4 + .type cuGraphicsUnmapResources, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsUnmapResources +#endif +cuGraphicsUnmapResources: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2056(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2056(%rip) +2: + pushq $257 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsUnmapResources_ptsz + .p2align 4 + .type cuGraphicsUnmapResources_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsUnmapResources_ptsz +#endif +cuGraphicsUnmapResources_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2064(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2064(%rip) +2: + pushq $258 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsUnregisterResource + .p2align 4 + .type cuGraphicsUnregisterResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsUnregisterResource +#endif +cuGraphicsUnregisterResource: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2072(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2072(%rip) +2: + pushq $259 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsVDPAURegisterOutputSurface + .p2align 4 + .type cuGraphicsVDPAURegisterOutputSurface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsVDPAURegisterOutputSurface +#endif +cuGraphicsVDPAURegisterOutputSurface: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2080(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2080(%rip) +2: + pushq $260 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGraphicsVDPAURegisterVideoSurface + .p2align 4 + .type cuGraphicsVDPAURegisterVideoSurface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGraphicsVDPAURegisterVideoSurface +#endif +cuGraphicsVDPAURegisterVideoSurface: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2088(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2088(%rip) +2: + pushq $261 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGreenCtxCreate + .p2align 4 + .type cuGreenCtxCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGreenCtxCreate +#endif +cuGreenCtxCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2096(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2096(%rip) +2: + pushq $262 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGreenCtxDestroy + .p2align 4 + .type cuGreenCtxDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGreenCtxDestroy +#endif +cuGreenCtxDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2104(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2104(%rip) +2: + pushq $263 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGreenCtxGetDevResource + .p2align 4 + .type cuGreenCtxGetDevResource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGreenCtxGetDevResource +#endif +cuGreenCtxGetDevResource: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2112(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2112(%rip) +2: + pushq $264 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGreenCtxRecordEvent + .p2align 4 + .type cuGreenCtxRecordEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGreenCtxRecordEvent +#endif +cuGreenCtxRecordEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2120(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2120(%rip) +2: + pushq $265 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGreenCtxStreamCreate + .p2align 4 + .type cuGreenCtxStreamCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGreenCtxStreamCreate +#endif +cuGreenCtxStreamCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2128(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2128(%rip) +2: + pushq $266 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuGreenCtxWaitEvent + .p2align 4 + .type cuGreenCtxWaitEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuGreenCtxWaitEvent +#endif +cuGreenCtxWaitEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2136(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2136(%rip) +2: + pushq $267 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuImportExternalMemory + .p2align 4 + .type cuImportExternalMemory, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuImportExternalMemory +#endif +cuImportExternalMemory: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2144(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2144(%rip) +2: + pushq $268 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuImportExternalSemaphore + .p2align 4 + .type cuImportExternalSemaphore, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuImportExternalSemaphore +#endif +cuImportExternalSemaphore: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2152(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2152(%rip) +2: + pushq $269 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuInit + .p2align 4 + .type cuInit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuInit +#endif +cuInit: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2160(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2160(%rip) +2: + pushq $270 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuIpcCloseMemHandle + .p2align 4 + .type cuIpcCloseMemHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuIpcCloseMemHandle +#endif +cuIpcCloseMemHandle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2168(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2168(%rip) +2: + pushq $271 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuIpcGetEventHandle + .p2align 4 + .type cuIpcGetEventHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuIpcGetEventHandle +#endif +cuIpcGetEventHandle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2176(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2176(%rip) +2: + pushq $272 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuIpcGetMemHandle + .p2align 4 + .type cuIpcGetMemHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuIpcGetMemHandle +#endif +cuIpcGetMemHandle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2184(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2184(%rip) +2: + pushq $273 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuIpcOpenEventHandle + .p2align 4 + .type cuIpcOpenEventHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuIpcOpenEventHandle +#endif +cuIpcOpenEventHandle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2192(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2192(%rip) +2: + pushq $274 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuIpcOpenMemHandle + .p2align 4 + .type cuIpcOpenMemHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuIpcOpenMemHandle +#endif +cuIpcOpenMemHandle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2200(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2200(%rip) +2: + pushq $275 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuIpcOpenMemHandle_v2 + .p2align 4 + .type cuIpcOpenMemHandle_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuIpcOpenMemHandle_v2 +#endif +cuIpcOpenMemHandle_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2208(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2208(%rip) +2: + pushq $276 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuKernelGetAttribute + .p2align 4 + .type cuKernelGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuKernelGetAttribute +#endif +cuKernelGetAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2216(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2216(%rip) +2: + pushq $277 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuKernelGetFunction + .p2align 4 + .type cuKernelGetFunction, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuKernelGetFunction +#endif +cuKernelGetFunction: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2224(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2224(%rip) +2: + pushq $278 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuKernelGetLibrary + .p2align 4 + .type cuKernelGetLibrary, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuKernelGetLibrary +#endif +cuKernelGetLibrary: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2232(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2232(%rip) +2: + pushq $279 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuKernelGetName + .p2align 4 + .type cuKernelGetName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuKernelGetName +#endif +cuKernelGetName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2240(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2240(%rip) +2: + pushq $280 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuKernelGetParamInfo + .p2align 4 + .type cuKernelGetParamInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuKernelGetParamInfo +#endif +cuKernelGetParamInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2248(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2248(%rip) +2: + pushq $281 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuKernelSetAttribute + .p2align 4 + .type cuKernelSetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuKernelSetAttribute +#endif +cuKernelSetAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2256(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2256(%rip) +2: + pushq $282 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuKernelSetCacheConfig + .p2align 4 + .type cuKernelSetCacheConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuKernelSetCacheConfig +#endif +cuKernelSetCacheConfig: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2264(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2264(%rip) +2: + pushq $283 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunch + .p2align 4 + .type cuLaunch, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunch +#endif +cuLaunch: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2272(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2272(%rip) +2: + pushq $284 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchCooperativeKernel + .p2align 4 + .type cuLaunchCooperativeKernel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchCooperativeKernel +#endif +cuLaunchCooperativeKernel: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2280(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2280(%rip) +2: + pushq $285 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchCooperativeKernelMultiDevice + .p2align 4 + .type cuLaunchCooperativeKernelMultiDevice, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchCooperativeKernelMultiDevice +#endif +cuLaunchCooperativeKernelMultiDevice: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2288(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2288(%rip) +2: + pushq $286 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchCooperativeKernel_ptsz + .p2align 4 + .type cuLaunchCooperativeKernel_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchCooperativeKernel_ptsz +#endif +cuLaunchCooperativeKernel_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2296(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2296(%rip) +2: + pushq $287 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchGrid + .p2align 4 + .type cuLaunchGrid, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchGrid +#endif +cuLaunchGrid: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2304(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2304(%rip) +2: + pushq $288 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchGridAsync + .p2align 4 + .type cuLaunchGridAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchGridAsync +#endif +cuLaunchGridAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2312(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2312(%rip) +2: + pushq $289 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchHostFunc + .p2align 4 + .type cuLaunchHostFunc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchHostFunc +#endif +cuLaunchHostFunc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2320(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2320(%rip) +2: + pushq $290 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchHostFunc_ptsz + .p2align 4 + .type cuLaunchHostFunc_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchHostFunc_ptsz +#endif +cuLaunchHostFunc_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2328(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2328(%rip) +2: + pushq $291 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchKernel + .p2align 4 + .type cuLaunchKernel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchKernel +#endif +cuLaunchKernel: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2336(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2336(%rip) +2: + pushq $292 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchKernelEx + .p2align 4 + .type cuLaunchKernelEx, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchKernelEx +#endif +cuLaunchKernelEx: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2344(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2344(%rip) +2: + pushq $293 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchKernelEx_ptsz + .p2align 4 + .type cuLaunchKernelEx_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchKernelEx_ptsz +#endif +cuLaunchKernelEx_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2352(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2352(%rip) +2: + pushq $294 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLaunchKernel_ptsz + .p2align 4 + .type cuLaunchKernel_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLaunchKernel_ptsz +#endif +cuLaunchKernel_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2360(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2360(%rip) +2: + pushq $295 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryEnumerateKernels + .p2align 4 + .type cuLibraryEnumerateKernels, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryEnumerateKernels +#endif +cuLibraryEnumerateKernels: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2368(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2368(%rip) +2: + pushq $296 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryGetGlobal + .p2align 4 + .type cuLibraryGetGlobal, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryGetGlobal +#endif +cuLibraryGetGlobal: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2376(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2376(%rip) +2: + pushq $297 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryGetKernel + .p2align 4 + .type cuLibraryGetKernel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryGetKernel +#endif +cuLibraryGetKernel: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2384(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2384(%rip) +2: + pushq $298 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryGetKernelCount + .p2align 4 + .type cuLibraryGetKernelCount, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryGetKernelCount +#endif +cuLibraryGetKernelCount: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2392(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2392(%rip) +2: + pushq $299 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryGetManaged + .p2align 4 + .type cuLibraryGetManaged, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryGetManaged +#endif +cuLibraryGetManaged: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2400(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2400(%rip) +2: + pushq $300 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryGetModule + .p2align 4 + .type cuLibraryGetModule, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryGetModule +#endif +cuLibraryGetModule: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2408(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2408(%rip) +2: + pushq $301 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryGetUnifiedFunction + .p2align 4 + .type cuLibraryGetUnifiedFunction, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryGetUnifiedFunction +#endif +cuLibraryGetUnifiedFunction: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2416(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2416(%rip) +2: + pushq $302 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryLoadData + .p2align 4 + .type cuLibraryLoadData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryLoadData +#endif +cuLibraryLoadData: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2424(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2424(%rip) +2: + pushq $303 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryLoadFromFile + .p2align 4 + .type cuLibraryLoadFromFile, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryLoadFromFile +#endif +cuLibraryLoadFromFile: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2432(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2432(%rip) +2: + pushq $304 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLibraryUnload + .p2align 4 + .type cuLibraryUnload, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLibraryUnload +#endif +cuLibraryUnload: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2440(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2440(%rip) +2: + pushq $305 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLinkAddData + .p2align 4 + .type cuLinkAddData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLinkAddData +#endif +cuLinkAddData: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2448(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2448(%rip) +2: + pushq $306 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLinkAddData_v2 + .p2align 4 + .type cuLinkAddData_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLinkAddData_v2 +#endif +cuLinkAddData_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2456(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2456(%rip) +2: + pushq $307 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLinkAddFile + .p2align 4 + .type cuLinkAddFile, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLinkAddFile +#endif +cuLinkAddFile: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2464(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2464(%rip) +2: + pushq $308 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLinkAddFile_v2 + .p2align 4 + .type cuLinkAddFile_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLinkAddFile_v2 +#endif +cuLinkAddFile_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2472(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2472(%rip) +2: + pushq $309 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLinkComplete + .p2align 4 + .type cuLinkComplete, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLinkComplete +#endif +cuLinkComplete: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2480(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2480(%rip) +2: + pushq $310 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLinkCreate + .p2align 4 + .type cuLinkCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLinkCreate +#endif +cuLinkCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2488(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2488(%rip) +2: + pushq $311 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLinkCreate_v2 + .p2align 4 + .type cuLinkCreate_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLinkCreate_v2 +#endif +cuLinkCreate_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2496(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2496(%rip) +2: + pushq $312 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuLinkDestroy + .p2align 4 + .type cuLinkDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuLinkDestroy +#endif +cuLinkDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2504(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2504(%rip) +2: + pushq $313 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAddressFree + .p2align 4 + .type cuMemAddressFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAddressFree +#endif +cuMemAddressFree: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2512(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2512(%rip) +2: + pushq $314 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAddressReserve + .p2align 4 + .type cuMemAddressReserve, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAddressReserve +#endif +cuMemAddressReserve: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2520(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2520(%rip) +2: + pushq $315 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAdvise + .p2align 4 + .type cuMemAdvise, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAdvise +#endif +cuMemAdvise: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2528(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2528(%rip) +2: + pushq $316 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAdvise_v2 + .p2align 4 + .type cuMemAdvise_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAdvise_v2 +#endif +cuMemAdvise_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2536(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2536(%rip) +2: + pushq $317 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAlloc + .p2align 4 + .type cuMemAlloc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAlloc +#endif +cuMemAlloc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2544(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2544(%rip) +2: + pushq $318 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAllocAsync + .p2align 4 + .type cuMemAllocAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAllocAsync +#endif +cuMemAllocAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2552(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2552(%rip) +2: + pushq $319 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAllocAsync_ptsz + .p2align 4 + .type cuMemAllocAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAllocAsync_ptsz +#endif +cuMemAllocAsync_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2560(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2560(%rip) +2: + pushq $320 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAllocFromPoolAsync + .p2align 4 + .type cuMemAllocFromPoolAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAllocFromPoolAsync +#endif +cuMemAllocFromPoolAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2568(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2568(%rip) +2: + pushq $321 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAllocFromPoolAsync_ptsz + .p2align 4 + .type cuMemAllocFromPoolAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAllocFromPoolAsync_ptsz +#endif +cuMemAllocFromPoolAsync_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2576(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2576(%rip) +2: + pushq $322 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAllocHost + .p2align 4 + .type cuMemAllocHost, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAllocHost +#endif +cuMemAllocHost: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2584(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2584(%rip) +2: + pushq $323 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAllocHost_v2 + .p2align 4 + .type cuMemAllocHost_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAllocHost_v2 +#endif +cuMemAllocHost_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2592(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2592(%rip) +2: + pushq $324 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAllocManaged + .p2align 4 + .type cuMemAllocManaged, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAllocManaged +#endif +cuMemAllocManaged: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2600(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2600(%rip) +2: + pushq $325 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAllocPitch + .p2align 4 + .type cuMemAllocPitch, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAllocPitch +#endif +cuMemAllocPitch: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2608(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2608(%rip) +2: + pushq $326 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAllocPitch_v2 + .p2align 4 + .type cuMemAllocPitch_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAllocPitch_v2 +#endif +cuMemAllocPitch_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2616(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2616(%rip) +2: + pushq $327 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemAlloc_v2 + .p2align 4 + .type cuMemAlloc_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemAlloc_v2 +#endif +cuMemAlloc_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2624(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2624(%rip) +2: + pushq $328 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemBatchDecompressAsync + .p2align 4 + .type cuMemBatchDecompressAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemBatchDecompressAsync +#endif +cuMemBatchDecompressAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2632(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2632(%rip) +2: + pushq $329 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemBatchDecompressAsync_ptsz + .p2align 4 + .type cuMemBatchDecompressAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemBatchDecompressAsync_ptsz +#endif +cuMemBatchDecompressAsync_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2640(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2640(%rip) +2: + pushq $330 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemCreate + .p2align 4 + .type cuMemCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemCreate +#endif +cuMemCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2648(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2648(%rip) +2: + pushq $331 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemExportToShareableHandle + .p2align 4 + .type cuMemExportToShareableHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemExportToShareableHandle +#endif +cuMemExportToShareableHandle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2656(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2656(%rip) +2: + pushq $332 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemFree + .p2align 4 + .type cuMemFree, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemFree +#endif +cuMemFree: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2664(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2664(%rip) +2: + pushq $333 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemFreeAsync + .p2align 4 + .type cuMemFreeAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemFreeAsync +#endif +cuMemFreeAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2672(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2672(%rip) +2: + pushq $334 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemFreeAsync_ptsz + .p2align 4 + .type cuMemFreeAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemFreeAsync_ptsz +#endif +cuMemFreeAsync_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2680(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2680(%rip) +2: + pushq $335 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemFreeHost + .p2align 4 + .type cuMemFreeHost, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemFreeHost +#endif +cuMemFreeHost: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2688(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2688(%rip) +2: + pushq $336 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemFree_v2 + .p2align 4 + .type cuMemFree_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemFree_v2 +#endif +cuMemFree_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2696(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2696(%rip) +2: + pushq $337 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetAccess + .p2align 4 + .type cuMemGetAccess, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetAccess +#endif +cuMemGetAccess: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2704(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2704(%rip) +2: + pushq $338 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetAddressRange + .p2align 4 + .type cuMemGetAddressRange, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetAddressRange +#endif +cuMemGetAddressRange: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2712(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2712(%rip) +2: + pushq $339 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetAddressRange_v2 + .p2align 4 + .type cuMemGetAddressRange_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetAddressRange_v2 +#endif +cuMemGetAddressRange_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2720(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2720(%rip) +2: + pushq $340 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetAllocationGranularity + .p2align 4 + .type cuMemGetAllocationGranularity, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetAllocationGranularity +#endif +cuMemGetAllocationGranularity: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2728(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2728(%rip) +2: + pushq $341 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetAllocationPropertiesFromHandle + .p2align 4 + .type cuMemGetAllocationPropertiesFromHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetAllocationPropertiesFromHandle +#endif +cuMemGetAllocationPropertiesFromHandle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2736(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2736(%rip) +2: + pushq $342 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetAttribute + .p2align 4 + .type cuMemGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetAttribute +#endif +cuMemGetAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2744(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2744(%rip) +2: + pushq $343 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetAttribute_v2 + .p2align 4 + .type cuMemGetAttribute_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetAttribute_v2 +#endif +cuMemGetAttribute_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2752(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2752(%rip) +2: + pushq $344 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetHandleForAddressRange + .p2align 4 + .type cuMemGetHandleForAddressRange, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetHandleForAddressRange +#endif +cuMemGetHandleForAddressRange: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2760(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2760(%rip) +2: + pushq $345 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetInfo + .p2align 4 + .type cuMemGetInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetInfo +#endif +cuMemGetInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2768(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2768(%rip) +2: + pushq $346 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemGetInfo_v2 + .p2align 4 + .type cuMemGetInfo_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemGetInfo_v2 +#endif +cuMemGetInfo_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2776(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2776(%rip) +2: + pushq $347 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemHostAlloc + .p2align 4 + .type cuMemHostAlloc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemHostAlloc +#endif +cuMemHostAlloc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2784(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2784(%rip) +2: + pushq $348 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemHostGetDevicePointer + .p2align 4 + .type cuMemHostGetDevicePointer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemHostGetDevicePointer +#endif +cuMemHostGetDevicePointer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2792(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2792(%rip) +2: + pushq $349 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemHostGetDevicePointer_v2 + .p2align 4 + .type cuMemHostGetDevicePointer_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemHostGetDevicePointer_v2 +#endif +cuMemHostGetDevicePointer_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2800(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2800(%rip) +2: + pushq $350 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemHostGetFlags + .p2align 4 + .type cuMemHostGetFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemHostGetFlags +#endif +cuMemHostGetFlags: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2808(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2808(%rip) +2: + pushq $351 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemHostRegister + .p2align 4 + .type cuMemHostRegister, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemHostRegister +#endif +cuMemHostRegister: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2816(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2816(%rip) +2: + pushq $352 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemHostRegister_v2 + .p2align 4 + .type cuMemHostRegister_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemHostRegister_v2 +#endif +cuMemHostRegister_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2824(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2824(%rip) +2: + pushq $353 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemHostUnregister + .p2align 4 + .type cuMemHostUnregister, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemHostUnregister +#endif +cuMemHostUnregister: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2832(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2832(%rip) +2: + pushq $354 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemImportFromShareableHandle + .p2align 4 + .type cuMemImportFromShareableHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemImportFromShareableHandle +#endif +cuMemImportFromShareableHandle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2840(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2840(%rip) +2: + pushq $355 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemMap + .p2align 4 + .type cuMemMap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemMap +#endif +cuMemMap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2848(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2848(%rip) +2: + pushq $356 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemMapArrayAsync + .p2align 4 + .type cuMemMapArrayAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemMapArrayAsync +#endif +cuMemMapArrayAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2856(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2856(%rip) +2: + pushq $357 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemMapArrayAsync_ptsz + .p2align 4 + .type cuMemMapArrayAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemMapArrayAsync_ptsz +#endif +cuMemMapArrayAsync_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2864(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2864(%rip) +2: + pushq $358 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolCreate + .p2align 4 + .type cuMemPoolCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolCreate +#endif +cuMemPoolCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2872(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2872(%rip) +2: + pushq $359 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolDestroy + .p2align 4 + .type cuMemPoolDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolDestroy +#endif +cuMemPoolDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2880(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2880(%rip) +2: + pushq $360 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolExportPointer + .p2align 4 + .type cuMemPoolExportPointer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolExportPointer +#endif +cuMemPoolExportPointer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2888(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2888(%rip) +2: + pushq $361 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolExportToShareableHandle + .p2align 4 + .type cuMemPoolExportToShareableHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolExportToShareableHandle +#endif +cuMemPoolExportToShareableHandle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2896(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2896(%rip) +2: + pushq $362 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolGetAccess + .p2align 4 + .type cuMemPoolGetAccess, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolGetAccess +#endif +cuMemPoolGetAccess: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2904(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2904(%rip) +2: + pushq $363 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolGetAttribute + .p2align 4 + .type cuMemPoolGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolGetAttribute +#endif +cuMemPoolGetAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2912(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2912(%rip) +2: + pushq $364 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolImportFromShareableHandle + .p2align 4 + .type cuMemPoolImportFromShareableHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolImportFromShareableHandle +#endif +cuMemPoolImportFromShareableHandle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2920(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2920(%rip) +2: + pushq $365 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolImportPointer + .p2align 4 + .type cuMemPoolImportPointer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolImportPointer +#endif +cuMemPoolImportPointer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2928(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2928(%rip) +2: + pushq $366 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolSetAccess + .p2align 4 + .type cuMemPoolSetAccess, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolSetAccess +#endif +cuMemPoolSetAccess: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2936(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2936(%rip) +2: + pushq $367 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolSetAttribute + .p2align 4 + .type cuMemPoolSetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolSetAttribute +#endif +cuMemPoolSetAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2944(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2944(%rip) +2: + pushq $368 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPoolTrimTo + .p2align 4 + .type cuMemPoolTrimTo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPoolTrimTo +#endif +cuMemPoolTrimTo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2952(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2952(%rip) +2: + pushq $369 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPrefetchAsync + .p2align 4 + .type cuMemPrefetchAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPrefetchAsync +#endif +cuMemPrefetchAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2960(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2960(%rip) +2: + pushq $370 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPrefetchAsync_ptsz + .p2align 4 + .type cuMemPrefetchAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPrefetchAsync_ptsz +#endif +cuMemPrefetchAsync_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2968(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2968(%rip) +2: + pushq $371 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPrefetchAsync_v2 + .p2align 4 + .type cuMemPrefetchAsync_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPrefetchAsync_v2 +#endif +cuMemPrefetchAsync_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2976(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2976(%rip) +2: + pushq $372 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemPrefetchAsync_v2_ptsz + .p2align 4 + .type cuMemPrefetchAsync_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemPrefetchAsync_v2_ptsz +#endif +cuMemPrefetchAsync_v2_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2984(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2984(%rip) +2: + pushq $373 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemRangeGetAttribute + .p2align 4 + .type cuMemRangeGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemRangeGetAttribute +#endif +cuMemRangeGetAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+2992(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+2992(%rip) +2: + pushq $374 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemRangeGetAttributes + .p2align 4 + .type cuMemRangeGetAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemRangeGetAttributes +#endif +cuMemRangeGetAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3000(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3000(%rip) +2: + pushq $375 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemRelease + .p2align 4 + .type cuMemRelease, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemRelease +#endif +cuMemRelease: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3008(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3008(%rip) +2: + pushq $376 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemRetainAllocationHandle + .p2align 4 + .type cuMemRetainAllocationHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemRetainAllocationHandle +#endif +cuMemRetainAllocationHandle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3016(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3016(%rip) +2: + pushq $377 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemSetAccess + .p2align 4 + .type cuMemSetAccess, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemSetAccess +#endif +cuMemSetAccess: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3024(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3024(%rip) +2: + pushq $378 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemUnmap + .p2align 4 + .type cuMemUnmap, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemUnmap +#endif +cuMemUnmap: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3032(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3032(%rip) +2: + pushq $379 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy + .p2align 4 + .type cuMemcpy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy +#endif +cuMemcpy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3040(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3040(%rip) +2: + pushq $380 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy2D + .p2align 4 + .type cuMemcpy2D, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy2D +#endif +cuMemcpy2D: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3048(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3048(%rip) +2: + pushq $381 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy2DAsync + .p2align 4 + .type cuMemcpy2DAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy2DAsync +#endif +cuMemcpy2DAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3056(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3056(%rip) +2: + pushq $382 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy2DAsync_v2 + .p2align 4 + .type cuMemcpy2DAsync_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy2DAsync_v2 +#endif +cuMemcpy2DAsync_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3064(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3064(%rip) +2: + pushq $383 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy2DAsync_v2_ptsz + .p2align 4 + .type cuMemcpy2DAsync_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy2DAsync_v2_ptsz +#endif +cuMemcpy2DAsync_v2_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3072(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3072(%rip) +2: + pushq $384 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy2DUnaligned + .p2align 4 + .type cuMemcpy2DUnaligned, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy2DUnaligned +#endif +cuMemcpy2DUnaligned: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3080(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3080(%rip) +2: + pushq $385 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy2DUnaligned_v2 + .p2align 4 + .type cuMemcpy2DUnaligned_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy2DUnaligned_v2 +#endif +cuMemcpy2DUnaligned_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3088(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3088(%rip) +2: + pushq $386 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy2DUnaligned_v2_ptds + .p2align 4 + .type cuMemcpy2DUnaligned_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy2DUnaligned_v2_ptds +#endif +cuMemcpy2DUnaligned_v2_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3096(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3096(%rip) +2: + pushq $387 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy2D_v2 + .p2align 4 + .type cuMemcpy2D_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy2D_v2 +#endif +cuMemcpy2D_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3104(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3104(%rip) +2: + pushq $388 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy2D_v2_ptds + .p2align 4 + .type cuMemcpy2D_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy2D_v2_ptds +#endif +cuMemcpy2D_v2_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3112(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3112(%rip) +2: + pushq $389 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3D + .p2align 4 + .type cuMemcpy3D, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3D +#endif +cuMemcpy3D: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3120(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3120(%rip) +2: + pushq $390 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3DAsync + .p2align 4 + .type cuMemcpy3DAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3DAsync +#endif +cuMemcpy3DAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3128(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3128(%rip) +2: + pushq $391 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3DAsync_v2 + .p2align 4 + .type cuMemcpy3DAsync_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3DAsync_v2 +#endif +cuMemcpy3DAsync_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3136(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3136(%rip) +2: + pushq $392 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3DAsync_v2_ptsz + .p2align 4 + .type cuMemcpy3DAsync_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3DAsync_v2_ptsz +#endif +cuMemcpy3DAsync_v2_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3144(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3144(%rip) +2: + pushq $393 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3DBatchAsync + .p2align 4 + .type cuMemcpy3DBatchAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3DBatchAsync +#endif +cuMemcpy3DBatchAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3152(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3152(%rip) +2: + pushq $394 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3DBatchAsync_ptsz + .p2align 4 + .type cuMemcpy3DBatchAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3DBatchAsync_ptsz +#endif +cuMemcpy3DBatchAsync_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3160(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3160(%rip) +2: + pushq $395 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3DPeer + .p2align 4 + .type cuMemcpy3DPeer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3DPeer +#endif +cuMemcpy3DPeer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3168(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3168(%rip) +2: + pushq $396 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3DPeerAsync + .p2align 4 + .type cuMemcpy3DPeerAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3DPeerAsync +#endif +cuMemcpy3DPeerAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3176(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3176(%rip) +2: + pushq $397 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3DPeerAsync_ptsz + .p2align 4 + .type cuMemcpy3DPeerAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3DPeerAsync_ptsz +#endif +cuMemcpy3DPeerAsync_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3184(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3184(%rip) +2: + pushq $398 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3DPeer_ptds + .p2align 4 + .type cuMemcpy3DPeer_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3DPeer_ptds +#endif +cuMemcpy3DPeer_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3192(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3192(%rip) +2: + pushq $399 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3D_v2 + .p2align 4 + .type cuMemcpy3D_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3D_v2 +#endif +cuMemcpy3D_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3200(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3200(%rip) +2: + pushq $400 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy3D_v2_ptds + .p2align 4 + .type cuMemcpy3D_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy3D_v2_ptds +#endif +cuMemcpy3D_v2_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3208(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3208(%rip) +2: + pushq $401 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAsync + .p2align 4 + .type cuMemcpyAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAsync +#endif +cuMemcpyAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3216(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3216(%rip) +2: + pushq $402 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAsync_ptsz + .p2align 4 + .type cuMemcpyAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAsync_ptsz +#endif +cuMemcpyAsync_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3224(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3224(%rip) +2: + pushq $403 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoA + .p2align 4 + .type cuMemcpyAtoA, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoA +#endif +cuMemcpyAtoA: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3232(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3232(%rip) +2: + pushq $404 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoA_v2 + .p2align 4 + .type cuMemcpyAtoA_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoA_v2 +#endif +cuMemcpyAtoA_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3240(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3240(%rip) +2: + pushq $405 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoA_v2_ptds + .p2align 4 + .type cuMemcpyAtoA_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoA_v2_ptds +#endif +cuMemcpyAtoA_v2_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3248(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3248(%rip) +2: + pushq $406 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoD + .p2align 4 + .type cuMemcpyAtoD, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoD +#endif +cuMemcpyAtoD: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3256(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3256(%rip) +2: + pushq $407 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoD_v2 + .p2align 4 + .type cuMemcpyAtoD_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoD_v2 +#endif +cuMemcpyAtoD_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3264(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3264(%rip) +2: + pushq $408 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoD_v2_ptds + .p2align 4 + .type cuMemcpyAtoD_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoD_v2_ptds +#endif +cuMemcpyAtoD_v2_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3272(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3272(%rip) +2: + pushq $409 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoH + .p2align 4 + .type cuMemcpyAtoH, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoH +#endif +cuMemcpyAtoH: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3280(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3280(%rip) +2: + pushq $410 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoHAsync + .p2align 4 + .type cuMemcpyAtoHAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoHAsync +#endif +cuMemcpyAtoHAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3288(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3288(%rip) +2: + pushq $411 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoHAsync_v2 + .p2align 4 + .type cuMemcpyAtoHAsync_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoHAsync_v2 +#endif +cuMemcpyAtoHAsync_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3296(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3296(%rip) +2: + pushq $412 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoHAsync_v2_ptsz + .p2align 4 + .type cuMemcpyAtoHAsync_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoHAsync_v2_ptsz +#endif +cuMemcpyAtoHAsync_v2_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3304(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3304(%rip) +2: + pushq $413 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoH_v2 + .p2align 4 + .type cuMemcpyAtoH_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoH_v2 +#endif +cuMemcpyAtoH_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3312(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3312(%rip) +2: + pushq $414 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyAtoH_v2_ptds + .p2align 4 + .type cuMemcpyAtoH_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyAtoH_v2_ptds +#endif +cuMemcpyAtoH_v2_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3320(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3320(%rip) +2: + pushq $415 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyBatchAsync + .p2align 4 + .type cuMemcpyBatchAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyBatchAsync +#endif +cuMemcpyBatchAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3328(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3328(%rip) +2: + pushq $416 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyBatchAsync_ptsz + .p2align 4 + .type cuMemcpyBatchAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyBatchAsync_ptsz +#endif +cuMemcpyBatchAsync_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3336(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3336(%rip) +2: + pushq $417 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoA + .p2align 4 + .type cuMemcpyDtoA, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoA +#endif +cuMemcpyDtoA: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3344(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3344(%rip) +2: + pushq $418 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoA_v2 + .p2align 4 + .type cuMemcpyDtoA_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoA_v2 +#endif +cuMemcpyDtoA_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3352(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3352(%rip) +2: + pushq $419 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoA_v2_ptds + .p2align 4 + .type cuMemcpyDtoA_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoA_v2_ptds +#endif +cuMemcpyDtoA_v2_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3360(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3360(%rip) +2: + pushq $420 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoD + .p2align 4 + .type cuMemcpyDtoD, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoD +#endif +cuMemcpyDtoD: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3368(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3368(%rip) +2: + pushq $421 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoDAsync + .p2align 4 + .type cuMemcpyDtoDAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoDAsync +#endif +cuMemcpyDtoDAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3376(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3376(%rip) +2: + pushq $422 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoDAsync_v2 + .p2align 4 + .type cuMemcpyDtoDAsync_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoDAsync_v2 +#endif +cuMemcpyDtoDAsync_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3384(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3384(%rip) +2: + pushq $423 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoDAsync_v2_ptsz + .p2align 4 + .type cuMemcpyDtoDAsync_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoDAsync_v2_ptsz +#endif +cuMemcpyDtoDAsync_v2_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3392(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3392(%rip) +2: + pushq $424 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoD_v2 + .p2align 4 + .type cuMemcpyDtoD_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoD_v2 +#endif +cuMemcpyDtoD_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3400(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3400(%rip) +2: + pushq $425 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoD_v2_ptds + .p2align 4 + .type cuMemcpyDtoD_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoD_v2_ptds +#endif +cuMemcpyDtoD_v2_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3408(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3408(%rip) +2: + pushq $426 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoH + .p2align 4 + .type cuMemcpyDtoH, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoH +#endif +cuMemcpyDtoH: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3416(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3416(%rip) +2: + pushq $427 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoHAsync + .p2align 4 + .type cuMemcpyDtoHAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoHAsync +#endif +cuMemcpyDtoHAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3424(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3424(%rip) +2: + pushq $428 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoHAsync_v2 + .p2align 4 + .type cuMemcpyDtoHAsync_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoHAsync_v2 +#endif +cuMemcpyDtoHAsync_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3432(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3432(%rip) +2: + pushq $429 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoHAsync_v2_ptsz + .p2align 4 + .type cuMemcpyDtoHAsync_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoHAsync_v2_ptsz +#endif +cuMemcpyDtoHAsync_v2_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3440(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3440(%rip) +2: + pushq $430 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoH_v2 + .p2align 4 + .type cuMemcpyDtoH_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoH_v2 +#endif +cuMemcpyDtoH_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3448(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3448(%rip) +2: + pushq $431 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyDtoH_v2_ptds + .p2align 4 + .type cuMemcpyDtoH_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyDtoH_v2_ptds +#endif +cuMemcpyDtoH_v2_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3456(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3456(%rip) +2: + pushq $432 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoA + .p2align 4 + .type cuMemcpyHtoA, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoA +#endif +cuMemcpyHtoA: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3464(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3464(%rip) +2: + pushq $433 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoAAsync + .p2align 4 + .type cuMemcpyHtoAAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoAAsync +#endif +cuMemcpyHtoAAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3472(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3472(%rip) +2: + pushq $434 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoAAsync_v2 + .p2align 4 + .type cuMemcpyHtoAAsync_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoAAsync_v2 +#endif +cuMemcpyHtoAAsync_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3480(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3480(%rip) +2: + pushq $435 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoAAsync_v2_ptsz + .p2align 4 + .type cuMemcpyHtoAAsync_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoAAsync_v2_ptsz +#endif +cuMemcpyHtoAAsync_v2_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3488(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3488(%rip) +2: + pushq $436 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoA_v2 + .p2align 4 + .type cuMemcpyHtoA_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoA_v2 +#endif +cuMemcpyHtoA_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3496(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3496(%rip) +2: + pushq $437 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoA_v2_ptds + .p2align 4 + .type cuMemcpyHtoA_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoA_v2_ptds +#endif +cuMemcpyHtoA_v2_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3504(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3504(%rip) +2: + pushq $438 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoD + .p2align 4 + .type cuMemcpyHtoD, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoD +#endif +cuMemcpyHtoD: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3512(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3512(%rip) +2: + pushq $439 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoDAsync + .p2align 4 + .type cuMemcpyHtoDAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoDAsync +#endif +cuMemcpyHtoDAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3520(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3520(%rip) +2: + pushq $440 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoDAsync_v2 + .p2align 4 + .type cuMemcpyHtoDAsync_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoDAsync_v2 +#endif +cuMemcpyHtoDAsync_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3528(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3528(%rip) +2: + pushq $441 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoDAsync_v2_ptsz + .p2align 4 + .type cuMemcpyHtoDAsync_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoDAsync_v2_ptsz +#endif +cuMemcpyHtoDAsync_v2_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3536(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3536(%rip) +2: + pushq $442 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoD_v2 + .p2align 4 + .type cuMemcpyHtoD_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoD_v2 +#endif +cuMemcpyHtoD_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3544(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3544(%rip) +2: + pushq $443 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyHtoD_v2_ptds + .p2align 4 + .type cuMemcpyHtoD_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyHtoD_v2_ptds +#endif +cuMemcpyHtoD_v2_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3552(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3552(%rip) +2: + pushq $444 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyPeer + .p2align 4 + .type cuMemcpyPeer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyPeer +#endif +cuMemcpyPeer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3560(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3560(%rip) +2: + pushq $445 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyPeerAsync + .p2align 4 + .type cuMemcpyPeerAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyPeerAsync +#endif +cuMemcpyPeerAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3568(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3568(%rip) +2: + pushq $446 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyPeerAsync_ptsz + .p2align 4 + .type cuMemcpyPeerAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyPeerAsync_ptsz +#endif +cuMemcpyPeerAsync_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3576(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3576(%rip) +2: + pushq $447 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpyPeer_ptds + .p2align 4 + .type cuMemcpyPeer_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpyPeer_ptds +#endif +cuMemcpyPeer_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3584(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3584(%rip) +2: + pushq $448 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemcpy_ptds + .p2align 4 + .type cuMemcpy_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemcpy_ptds +#endif +cuMemcpy_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3592(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3592(%rip) +2: + pushq $449 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD16 + .p2align 4 + .type cuMemsetD16, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD16 +#endif +cuMemsetD16: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3600(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3600(%rip) +2: + pushq $450 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD16Async + .p2align 4 + .type cuMemsetD16Async, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD16Async +#endif +cuMemsetD16Async: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3608(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3608(%rip) +2: + pushq $451 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD16Async_ptsz + .p2align 4 + .type cuMemsetD16Async_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD16Async_ptsz +#endif +cuMemsetD16Async_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3616(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3616(%rip) +2: + pushq $452 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD16_v2 + .p2align 4 + .type cuMemsetD16_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD16_v2 +#endif +cuMemsetD16_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3624(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3624(%rip) +2: + pushq $453 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD16_v2_ptds + .p2align 4 + .type cuMemsetD16_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD16_v2_ptds +#endif +cuMemsetD16_v2_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3632(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3632(%rip) +2: + pushq $454 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D16 + .p2align 4 + .type cuMemsetD2D16, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D16 +#endif +cuMemsetD2D16: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3640(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3640(%rip) +2: + pushq $455 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D16Async + .p2align 4 + .type cuMemsetD2D16Async, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D16Async +#endif +cuMemsetD2D16Async: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3648(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3648(%rip) +2: + pushq $456 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D16Async_ptsz + .p2align 4 + .type cuMemsetD2D16Async_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D16Async_ptsz +#endif +cuMemsetD2D16Async_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3656(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3656(%rip) +2: + pushq $457 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D16_v2 + .p2align 4 + .type cuMemsetD2D16_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D16_v2 +#endif +cuMemsetD2D16_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3664(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3664(%rip) +2: + pushq $458 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D16_v2_ptds + .p2align 4 + .type cuMemsetD2D16_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D16_v2_ptds +#endif +cuMemsetD2D16_v2_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3672(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3672(%rip) +2: + pushq $459 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D32 + .p2align 4 + .type cuMemsetD2D32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D32 +#endif +cuMemsetD2D32: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3680(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3680(%rip) +2: + pushq $460 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D32Async + .p2align 4 + .type cuMemsetD2D32Async, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D32Async +#endif +cuMemsetD2D32Async: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3688(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3688(%rip) +2: + pushq $461 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D32Async_ptsz + .p2align 4 + .type cuMemsetD2D32Async_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D32Async_ptsz +#endif +cuMemsetD2D32Async_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3696(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3696(%rip) +2: + pushq $462 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D32_v2 + .p2align 4 + .type cuMemsetD2D32_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D32_v2 +#endif +cuMemsetD2D32_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3704(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3704(%rip) +2: + pushq $463 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D32_v2_ptds + .p2align 4 + .type cuMemsetD2D32_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D32_v2_ptds +#endif +cuMemsetD2D32_v2_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3712(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3712(%rip) +2: + pushq $464 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D8 + .p2align 4 + .type cuMemsetD2D8, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D8 +#endif +cuMemsetD2D8: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3720(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3720(%rip) +2: + pushq $465 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D8Async + .p2align 4 + .type cuMemsetD2D8Async, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D8Async +#endif +cuMemsetD2D8Async: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3728(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3728(%rip) +2: + pushq $466 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D8Async_ptsz + .p2align 4 + .type cuMemsetD2D8Async_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D8Async_ptsz +#endif +cuMemsetD2D8Async_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3736(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3736(%rip) +2: + pushq $467 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D8_v2 + .p2align 4 + .type cuMemsetD2D8_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D8_v2 +#endif +cuMemsetD2D8_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3744(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3744(%rip) +2: + pushq $468 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD2D8_v2_ptds + .p2align 4 + .type cuMemsetD2D8_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD2D8_v2_ptds +#endif +cuMemsetD2D8_v2_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3752(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3752(%rip) +2: + pushq $469 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD32 + .p2align 4 + .type cuMemsetD32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD32 +#endif +cuMemsetD32: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3760(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3760(%rip) +2: + pushq $470 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD32Async + .p2align 4 + .type cuMemsetD32Async, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD32Async +#endif +cuMemsetD32Async: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3768(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3768(%rip) +2: + pushq $471 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD32Async_ptsz + .p2align 4 + .type cuMemsetD32Async_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD32Async_ptsz +#endif +cuMemsetD32Async_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3776(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3776(%rip) +2: + pushq $472 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD32_v2 + .p2align 4 + .type cuMemsetD32_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD32_v2 +#endif +cuMemsetD32_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3784(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3784(%rip) +2: + pushq $473 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD32_v2_ptds + .p2align 4 + .type cuMemsetD32_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD32_v2_ptds +#endif +cuMemsetD32_v2_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3792(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3792(%rip) +2: + pushq $474 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD8 + .p2align 4 + .type cuMemsetD8, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD8 +#endif +cuMemsetD8: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3800(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3800(%rip) +2: + pushq $475 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD8Async + .p2align 4 + .type cuMemsetD8Async, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD8Async +#endif +cuMemsetD8Async: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3808(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3808(%rip) +2: + pushq $476 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD8Async_ptsz + .p2align 4 + .type cuMemsetD8Async_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD8Async_ptsz +#endif +cuMemsetD8Async_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3816(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3816(%rip) +2: + pushq $477 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD8_v2 + .p2align 4 + .type cuMemsetD8_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD8_v2 +#endif +cuMemsetD8_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3824(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3824(%rip) +2: + pushq $478 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMemsetD8_v2_ptds + .p2align 4 + .type cuMemsetD8_v2_ptds, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMemsetD8_v2_ptds +#endif +cuMemsetD8_v2_ptds: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3832(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3832(%rip) +2: + pushq $479 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMipmappedArrayCreate + .p2align 4 + .type cuMipmappedArrayCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMipmappedArrayCreate +#endif +cuMipmappedArrayCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3840(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3840(%rip) +2: + pushq $480 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMipmappedArrayDestroy + .p2align 4 + .type cuMipmappedArrayDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMipmappedArrayDestroy +#endif +cuMipmappedArrayDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3848(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3848(%rip) +2: + pushq $481 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMipmappedArrayGetLevel + .p2align 4 + .type cuMipmappedArrayGetLevel, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMipmappedArrayGetLevel +#endif +cuMipmappedArrayGetLevel: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3856(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3856(%rip) +2: + pushq $482 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMipmappedArrayGetMemoryRequirements + .p2align 4 + .type cuMipmappedArrayGetMemoryRequirements, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMipmappedArrayGetMemoryRequirements +#endif +cuMipmappedArrayGetMemoryRequirements: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3864(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3864(%rip) +2: + pushq $483 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMipmappedArrayGetSparseProperties + .p2align 4 + .type cuMipmappedArrayGetSparseProperties, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMipmappedArrayGetSparseProperties +#endif +cuMipmappedArrayGetSparseProperties: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3872(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3872(%rip) +2: + pushq $484 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleEnumerateFunctions + .p2align 4 + .type cuModuleEnumerateFunctions, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleEnumerateFunctions +#endif +cuModuleEnumerateFunctions: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3880(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3880(%rip) +2: + pushq $485 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleGetFunction + .p2align 4 + .type cuModuleGetFunction, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleGetFunction +#endif +cuModuleGetFunction: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3888(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3888(%rip) +2: + pushq $486 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleGetFunctionCount + .p2align 4 + .type cuModuleGetFunctionCount, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleGetFunctionCount +#endif +cuModuleGetFunctionCount: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3896(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3896(%rip) +2: + pushq $487 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleGetGlobal + .p2align 4 + .type cuModuleGetGlobal, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleGetGlobal +#endif +cuModuleGetGlobal: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3904(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3904(%rip) +2: + pushq $488 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleGetGlobal_v2 + .p2align 4 + .type cuModuleGetGlobal_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleGetGlobal_v2 +#endif +cuModuleGetGlobal_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3912(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3912(%rip) +2: + pushq $489 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleGetLoadingMode + .p2align 4 + .type cuModuleGetLoadingMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleGetLoadingMode +#endif +cuModuleGetLoadingMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3920(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3920(%rip) +2: + pushq $490 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleGetSurfRef + .p2align 4 + .type cuModuleGetSurfRef, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleGetSurfRef +#endif +cuModuleGetSurfRef: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3928(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3928(%rip) +2: + pushq $491 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleGetTexRef + .p2align 4 + .type cuModuleGetTexRef, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleGetTexRef +#endif +cuModuleGetTexRef: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3936(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3936(%rip) +2: + pushq $492 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleLoad + .p2align 4 + .type cuModuleLoad, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleLoad +#endif +cuModuleLoad: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3944(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3944(%rip) +2: + pushq $493 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleLoadData + .p2align 4 + .type cuModuleLoadData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleLoadData +#endif +cuModuleLoadData: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3952(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3952(%rip) +2: + pushq $494 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleLoadDataEx + .p2align 4 + .type cuModuleLoadDataEx, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleLoadDataEx +#endif +cuModuleLoadDataEx: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3960(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3960(%rip) +2: + pushq $495 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleLoadFatBinary + .p2align 4 + .type cuModuleLoadFatBinary, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleLoadFatBinary +#endif +cuModuleLoadFatBinary: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3968(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3968(%rip) +2: + pushq $496 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuModuleUnload + .p2align 4 + .type cuModuleUnload, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuModuleUnload +#endif +cuModuleUnload: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3976(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3976(%rip) +2: + pushq $497 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMulticastAddDevice + .p2align 4 + .type cuMulticastAddDevice, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMulticastAddDevice +#endif +cuMulticastAddDevice: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3984(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3984(%rip) +2: + pushq $498 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMulticastBindAddr + .p2align 4 + .type cuMulticastBindAddr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMulticastBindAddr +#endif +cuMulticastBindAddr: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+3992(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+3992(%rip) +2: + pushq $499 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMulticastBindMem + .p2align 4 + .type cuMulticastBindMem, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMulticastBindMem +#endif +cuMulticastBindMem: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4000(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4000(%rip) +2: + pushq $500 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMulticastCreate + .p2align 4 + .type cuMulticastCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMulticastCreate +#endif +cuMulticastCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4008(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4008(%rip) +2: + pushq $501 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMulticastGetGranularity + .p2align 4 + .type cuMulticastGetGranularity, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMulticastGetGranularity +#endif +cuMulticastGetGranularity: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4016(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4016(%rip) +2: + pushq $502 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuMulticastUnbind + .p2align 4 + .type cuMulticastUnbind, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuMulticastUnbind +#endif +cuMulticastUnbind: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4024(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4024(%rip) +2: + pushq $503 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuOccupancyAvailableDynamicSMemPerBlock + .p2align 4 + .type cuOccupancyAvailableDynamicSMemPerBlock, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuOccupancyAvailableDynamicSMemPerBlock +#endif +cuOccupancyAvailableDynamicSMemPerBlock: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4032(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4032(%rip) +2: + pushq $504 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuOccupancyMaxActiveBlocksPerMultiprocessor + .p2align 4 + .type cuOccupancyMaxActiveBlocksPerMultiprocessor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuOccupancyMaxActiveBlocksPerMultiprocessor +#endif +cuOccupancyMaxActiveBlocksPerMultiprocessor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4040(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4040(%rip) +2: + pushq $505 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuOccupancyMaxActiveBlocksPerMultiprocessorWithFlags + .p2align 4 + .type cuOccupancyMaxActiveBlocksPerMultiprocessorWithFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuOccupancyMaxActiveBlocksPerMultiprocessorWithFlags +#endif +cuOccupancyMaxActiveBlocksPerMultiprocessorWithFlags: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4048(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4048(%rip) +2: + pushq $506 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuOccupancyMaxActiveClusters + .p2align 4 + .type cuOccupancyMaxActiveClusters, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuOccupancyMaxActiveClusters +#endif +cuOccupancyMaxActiveClusters: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4056(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4056(%rip) +2: + pushq $507 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuOccupancyMaxPotentialBlockSize + .p2align 4 + .type cuOccupancyMaxPotentialBlockSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuOccupancyMaxPotentialBlockSize +#endif +cuOccupancyMaxPotentialBlockSize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4064(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4064(%rip) +2: + pushq $508 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuOccupancyMaxPotentialBlockSizeWithFlags + .p2align 4 + .type cuOccupancyMaxPotentialBlockSizeWithFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuOccupancyMaxPotentialBlockSizeWithFlags +#endif +cuOccupancyMaxPotentialBlockSizeWithFlags: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4072(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4072(%rip) +2: + pushq $509 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuOccupancyMaxPotentialClusterSize + .p2align 4 + .type cuOccupancyMaxPotentialClusterSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuOccupancyMaxPotentialClusterSize +#endif +cuOccupancyMaxPotentialClusterSize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4080(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4080(%rip) +2: + pushq $510 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuParamSetSize + .p2align 4 + .type cuParamSetSize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuParamSetSize +#endif +cuParamSetSize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4088(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4088(%rip) +2: + pushq $511 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuParamSetTexRef + .p2align 4 + .type cuParamSetTexRef, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuParamSetTexRef +#endif +cuParamSetTexRef: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4096(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4096(%rip) +2: + pushq $512 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuParamSetf + .p2align 4 + .type cuParamSetf, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuParamSetf +#endif +cuParamSetf: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4104(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4104(%rip) +2: + pushq $513 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuParamSeti + .p2align 4 + .type cuParamSeti, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuParamSeti +#endif +cuParamSeti: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4112(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4112(%rip) +2: + pushq $514 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuParamSetv + .p2align 4 + .type cuParamSetv, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuParamSetv +#endif +cuParamSetv: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4120(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4120(%rip) +2: + pushq $515 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuPointerGetAttribute + .p2align 4 + .type cuPointerGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuPointerGetAttribute +#endif +cuPointerGetAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4128(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4128(%rip) +2: + pushq $516 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuPointerGetAttributes + .p2align 4 + .type cuPointerGetAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuPointerGetAttributes +#endif +cuPointerGetAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4136(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4136(%rip) +2: + pushq $517 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuPointerSetAttribute + .p2align 4 + .type cuPointerSetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuPointerSetAttribute +#endif +cuPointerSetAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4144(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4144(%rip) +2: + pushq $518 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuProfilerInitialize + .p2align 4 + .type cuProfilerInitialize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuProfilerInitialize +#endif +cuProfilerInitialize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4152(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4152(%rip) +2: + pushq $519 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuProfilerStart + .p2align 4 + .type cuProfilerStart, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuProfilerStart +#endif +cuProfilerStart: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4160(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4160(%rip) +2: + pushq $520 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuProfilerStop + .p2align 4 + .type cuProfilerStop, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuProfilerStop +#endif +cuProfilerStop: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4168(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4168(%rip) +2: + pushq $521 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuSignalExternalSemaphoresAsync + .p2align 4 + .type cuSignalExternalSemaphoresAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuSignalExternalSemaphoresAsync +#endif +cuSignalExternalSemaphoresAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4176(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4176(%rip) +2: + pushq $522 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuSignalExternalSemaphoresAsync_ptsz + .p2align 4 + .type cuSignalExternalSemaphoresAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuSignalExternalSemaphoresAsync_ptsz +#endif +cuSignalExternalSemaphoresAsync_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4184(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4184(%rip) +2: + pushq $523 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamAddCallback + .p2align 4 + .type cuStreamAddCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamAddCallback +#endif +cuStreamAddCallback: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4192(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4192(%rip) +2: + pushq $524 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamAddCallback_ptsz + .p2align 4 + .type cuStreamAddCallback_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamAddCallback_ptsz +#endif +cuStreamAddCallback_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4200(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4200(%rip) +2: + pushq $525 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamAttachMemAsync + .p2align 4 + .type cuStreamAttachMemAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamAttachMemAsync +#endif +cuStreamAttachMemAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4208(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4208(%rip) +2: + pushq $526 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamAttachMemAsync_ptsz + .p2align 4 + .type cuStreamAttachMemAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamAttachMemAsync_ptsz +#endif +cuStreamAttachMemAsync_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4216(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4216(%rip) +2: + pushq $527 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBatchMemOp + .p2align 4 + .type cuStreamBatchMemOp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBatchMemOp +#endif +cuStreamBatchMemOp: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4224(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4224(%rip) +2: + pushq $528 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBatchMemOp_ptsz + .p2align 4 + .type cuStreamBatchMemOp_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBatchMemOp_ptsz +#endif +cuStreamBatchMemOp_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4232(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4232(%rip) +2: + pushq $529 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBatchMemOp_v2 + .p2align 4 + .type cuStreamBatchMemOp_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBatchMemOp_v2 +#endif +cuStreamBatchMemOp_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4240(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4240(%rip) +2: + pushq $530 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBatchMemOp_v2_ptsz + .p2align 4 + .type cuStreamBatchMemOp_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBatchMemOp_v2_ptsz +#endif +cuStreamBatchMemOp_v2_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4248(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4248(%rip) +2: + pushq $531 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBeginCapture + .p2align 4 + .type cuStreamBeginCapture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBeginCapture +#endif +cuStreamBeginCapture: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4256(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4256(%rip) +2: + pushq $532 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBeginCaptureToGraph + .p2align 4 + .type cuStreamBeginCaptureToGraph, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBeginCaptureToGraph +#endif +cuStreamBeginCaptureToGraph: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4264(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4264(%rip) +2: + pushq $533 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBeginCaptureToGraph_ptsz + .p2align 4 + .type cuStreamBeginCaptureToGraph_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBeginCaptureToGraph_ptsz +#endif +cuStreamBeginCaptureToGraph_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4272(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4272(%rip) +2: + pushq $534 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBeginCapture_ptsz + .p2align 4 + .type cuStreamBeginCapture_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBeginCapture_ptsz +#endif +cuStreamBeginCapture_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4280(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4280(%rip) +2: + pushq $535 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBeginCapture_v2 + .p2align 4 + .type cuStreamBeginCapture_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBeginCapture_v2 +#endif +cuStreamBeginCapture_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4288(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4288(%rip) +2: + pushq $536 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamBeginCapture_v2_ptsz + .p2align 4 + .type cuStreamBeginCapture_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamBeginCapture_v2_ptsz +#endif +cuStreamBeginCapture_v2_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4296(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4296(%rip) +2: + pushq $537 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamCopyAttributes + .p2align 4 + .type cuStreamCopyAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamCopyAttributes +#endif +cuStreamCopyAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4304(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4304(%rip) +2: + pushq $538 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamCopyAttributes_ptsz + .p2align 4 + .type cuStreamCopyAttributes_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamCopyAttributes_ptsz +#endif +cuStreamCopyAttributes_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4312(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4312(%rip) +2: + pushq $539 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamCreate + .p2align 4 + .type cuStreamCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamCreate +#endif +cuStreamCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4320(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4320(%rip) +2: + pushq $540 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamCreateWithPriority + .p2align 4 + .type cuStreamCreateWithPriority, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamCreateWithPriority +#endif +cuStreamCreateWithPriority: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4328(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4328(%rip) +2: + pushq $541 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamDestroy + .p2align 4 + .type cuStreamDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamDestroy +#endif +cuStreamDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4336(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4336(%rip) +2: + pushq $542 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamDestroy_v2 + .p2align 4 + .type cuStreamDestroy_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamDestroy_v2 +#endif +cuStreamDestroy_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4344(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4344(%rip) +2: + pushq $543 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamEndCapture + .p2align 4 + .type cuStreamEndCapture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamEndCapture +#endif +cuStreamEndCapture: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4352(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4352(%rip) +2: + pushq $544 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamEndCapture_ptsz + .p2align 4 + .type cuStreamEndCapture_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamEndCapture_ptsz +#endif +cuStreamEndCapture_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4360(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4360(%rip) +2: + pushq $545 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetAttribute + .p2align 4 + .type cuStreamGetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetAttribute +#endif +cuStreamGetAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4368(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4368(%rip) +2: + pushq $546 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetAttribute_ptsz + .p2align 4 + .type cuStreamGetAttribute_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetAttribute_ptsz +#endif +cuStreamGetAttribute_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4376(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4376(%rip) +2: + pushq $547 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCaptureInfo + .p2align 4 + .type cuStreamGetCaptureInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCaptureInfo +#endif +cuStreamGetCaptureInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4384(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4384(%rip) +2: + pushq $548 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCaptureInfo_ptsz + .p2align 4 + .type cuStreamGetCaptureInfo_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCaptureInfo_ptsz +#endif +cuStreamGetCaptureInfo_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4392(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4392(%rip) +2: + pushq $549 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCaptureInfo_v2 + .p2align 4 + .type cuStreamGetCaptureInfo_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCaptureInfo_v2 +#endif +cuStreamGetCaptureInfo_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4400(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4400(%rip) +2: + pushq $550 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCaptureInfo_v2_ptsz + .p2align 4 + .type cuStreamGetCaptureInfo_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCaptureInfo_v2_ptsz +#endif +cuStreamGetCaptureInfo_v2_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4408(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4408(%rip) +2: + pushq $551 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCaptureInfo_v3 + .p2align 4 + .type cuStreamGetCaptureInfo_v3, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCaptureInfo_v3 +#endif +cuStreamGetCaptureInfo_v3: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4416(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4416(%rip) +2: + pushq $552 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCaptureInfo_v3_ptsz + .p2align 4 + .type cuStreamGetCaptureInfo_v3_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCaptureInfo_v3_ptsz +#endif +cuStreamGetCaptureInfo_v3_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4424(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4424(%rip) +2: + pushq $553 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCtx + .p2align 4 + .type cuStreamGetCtx, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCtx +#endif +cuStreamGetCtx: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4432(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4432(%rip) +2: + pushq $554 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCtx_ptsz + .p2align 4 + .type cuStreamGetCtx_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCtx_ptsz +#endif +cuStreamGetCtx_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4440(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4440(%rip) +2: + pushq $555 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCtx_v2 + .p2align 4 + .type cuStreamGetCtx_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCtx_v2 +#endif +cuStreamGetCtx_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4448(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4448(%rip) +2: + pushq $556 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetCtx_v2_ptsz + .p2align 4 + .type cuStreamGetCtx_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetCtx_v2_ptsz +#endif +cuStreamGetCtx_v2_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4456(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4456(%rip) +2: + pushq $557 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetDevice + .p2align 4 + .type cuStreamGetDevice, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetDevice +#endif +cuStreamGetDevice: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4464(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4464(%rip) +2: + pushq $558 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetDevice_ptsz + .p2align 4 + .type cuStreamGetDevice_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetDevice_ptsz +#endif +cuStreamGetDevice_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4472(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4472(%rip) +2: + pushq $559 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetFlags + .p2align 4 + .type cuStreamGetFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetFlags +#endif +cuStreamGetFlags: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4480(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4480(%rip) +2: + pushq $560 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetFlags_ptsz + .p2align 4 + .type cuStreamGetFlags_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetFlags_ptsz +#endif +cuStreamGetFlags_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4488(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4488(%rip) +2: + pushq $561 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetGreenCtx + .p2align 4 + .type cuStreamGetGreenCtx, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetGreenCtx +#endif +cuStreamGetGreenCtx: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4496(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4496(%rip) +2: + pushq $562 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetId + .p2align 4 + .type cuStreamGetId, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetId +#endif +cuStreamGetId: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4504(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4504(%rip) +2: + pushq $563 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetId_ptsz + .p2align 4 + .type cuStreamGetId_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetId_ptsz +#endif +cuStreamGetId_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4512(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4512(%rip) +2: + pushq $564 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetPriority + .p2align 4 + .type cuStreamGetPriority, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetPriority +#endif +cuStreamGetPriority: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4520(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4520(%rip) +2: + pushq $565 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamGetPriority_ptsz + .p2align 4 + .type cuStreamGetPriority_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamGetPriority_ptsz +#endif +cuStreamGetPriority_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4528(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4528(%rip) +2: + pushq $566 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamIsCapturing + .p2align 4 + .type cuStreamIsCapturing, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamIsCapturing +#endif +cuStreamIsCapturing: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4536(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4536(%rip) +2: + pushq $567 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamIsCapturing_ptsz + .p2align 4 + .type cuStreamIsCapturing_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamIsCapturing_ptsz +#endif +cuStreamIsCapturing_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4544(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4544(%rip) +2: + pushq $568 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamQuery + .p2align 4 + .type cuStreamQuery, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamQuery +#endif +cuStreamQuery: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4552(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4552(%rip) +2: + pushq $569 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamQuery_ptsz + .p2align 4 + .type cuStreamQuery_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamQuery_ptsz +#endif +cuStreamQuery_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4560(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4560(%rip) +2: + pushq $570 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamSetAttribute + .p2align 4 + .type cuStreamSetAttribute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamSetAttribute +#endif +cuStreamSetAttribute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4568(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4568(%rip) +2: + pushq $571 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamSetAttribute_ptsz + .p2align 4 + .type cuStreamSetAttribute_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamSetAttribute_ptsz +#endif +cuStreamSetAttribute_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4576(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4576(%rip) +2: + pushq $572 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamSynchronize + .p2align 4 + .type cuStreamSynchronize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamSynchronize +#endif +cuStreamSynchronize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4584(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4584(%rip) +2: + pushq $573 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamSynchronize_ptsz + .p2align 4 + .type cuStreamSynchronize_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamSynchronize_ptsz +#endif +cuStreamSynchronize_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4592(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4592(%rip) +2: + pushq $574 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamUpdateCaptureDependencies + .p2align 4 + .type cuStreamUpdateCaptureDependencies, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamUpdateCaptureDependencies +#endif +cuStreamUpdateCaptureDependencies: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4600(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4600(%rip) +2: + pushq $575 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamUpdateCaptureDependencies_ptsz + .p2align 4 + .type cuStreamUpdateCaptureDependencies_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamUpdateCaptureDependencies_ptsz +#endif +cuStreamUpdateCaptureDependencies_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4608(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4608(%rip) +2: + pushq $576 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamUpdateCaptureDependencies_v2 + .p2align 4 + .type cuStreamUpdateCaptureDependencies_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamUpdateCaptureDependencies_v2 +#endif +cuStreamUpdateCaptureDependencies_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4616(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4616(%rip) +2: + pushq $577 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamUpdateCaptureDependencies_v2_ptsz + .p2align 4 + .type cuStreamUpdateCaptureDependencies_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamUpdateCaptureDependencies_v2_ptsz +#endif +cuStreamUpdateCaptureDependencies_v2_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4624(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4624(%rip) +2: + pushq $578 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitEvent + .p2align 4 + .type cuStreamWaitEvent, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitEvent +#endif +cuStreamWaitEvent: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4632(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4632(%rip) +2: + pushq $579 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitEvent_ptsz + .p2align 4 + .type cuStreamWaitEvent_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitEvent_ptsz +#endif +cuStreamWaitEvent_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4640(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4640(%rip) +2: + pushq $580 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitValue32 + .p2align 4 + .type cuStreamWaitValue32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitValue32 +#endif +cuStreamWaitValue32: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4648(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4648(%rip) +2: + pushq $581 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitValue32_ptsz + .p2align 4 + .type cuStreamWaitValue32_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitValue32_ptsz +#endif +cuStreamWaitValue32_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4656(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4656(%rip) +2: + pushq $582 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitValue32_v2 + .p2align 4 + .type cuStreamWaitValue32_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitValue32_v2 +#endif +cuStreamWaitValue32_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4664(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4664(%rip) +2: + pushq $583 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitValue32_v2_ptsz + .p2align 4 + .type cuStreamWaitValue32_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitValue32_v2_ptsz +#endif +cuStreamWaitValue32_v2_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4672(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4672(%rip) +2: + pushq $584 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitValue64 + .p2align 4 + .type cuStreamWaitValue64, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitValue64 +#endif +cuStreamWaitValue64: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4680(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4680(%rip) +2: + pushq $585 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitValue64_ptsz + .p2align 4 + .type cuStreamWaitValue64_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitValue64_ptsz +#endif +cuStreamWaitValue64_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4688(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4688(%rip) +2: + pushq $586 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitValue64_v2 + .p2align 4 + .type cuStreamWaitValue64_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitValue64_v2 +#endif +cuStreamWaitValue64_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4696(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4696(%rip) +2: + pushq $587 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWaitValue64_v2_ptsz + .p2align 4 + .type cuStreamWaitValue64_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWaitValue64_v2_ptsz +#endif +cuStreamWaitValue64_v2_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4704(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4704(%rip) +2: + pushq $588 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWriteValue32 + .p2align 4 + .type cuStreamWriteValue32, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWriteValue32 +#endif +cuStreamWriteValue32: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4712(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4712(%rip) +2: + pushq $589 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWriteValue32_ptsz + .p2align 4 + .type cuStreamWriteValue32_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWriteValue32_ptsz +#endif +cuStreamWriteValue32_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4720(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4720(%rip) +2: + pushq $590 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWriteValue32_v2 + .p2align 4 + .type cuStreamWriteValue32_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWriteValue32_v2 +#endif +cuStreamWriteValue32_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4728(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4728(%rip) +2: + pushq $591 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWriteValue32_v2_ptsz + .p2align 4 + .type cuStreamWriteValue32_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWriteValue32_v2_ptsz +#endif +cuStreamWriteValue32_v2_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4736(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4736(%rip) +2: + pushq $592 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWriteValue64 + .p2align 4 + .type cuStreamWriteValue64, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWriteValue64 +#endif +cuStreamWriteValue64: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4744(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4744(%rip) +2: + pushq $593 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWriteValue64_ptsz + .p2align 4 + .type cuStreamWriteValue64_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWriteValue64_ptsz +#endif +cuStreamWriteValue64_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4752(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4752(%rip) +2: + pushq $594 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWriteValue64_v2 + .p2align 4 + .type cuStreamWriteValue64_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWriteValue64_v2 +#endif +cuStreamWriteValue64_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4760(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4760(%rip) +2: + pushq $595 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuStreamWriteValue64_v2_ptsz + .p2align 4 + .type cuStreamWriteValue64_v2_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuStreamWriteValue64_v2_ptsz +#endif +cuStreamWriteValue64_v2_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4768(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4768(%rip) +2: + pushq $596 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuSurfObjectCreate + .p2align 4 + .type cuSurfObjectCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuSurfObjectCreate +#endif +cuSurfObjectCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4776(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4776(%rip) +2: + pushq $597 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuSurfObjectDestroy + .p2align 4 + .type cuSurfObjectDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuSurfObjectDestroy +#endif +cuSurfObjectDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4784(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4784(%rip) +2: + pushq $598 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuSurfObjectGetResourceDesc + .p2align 4 + .type cuSurfObjectGetResourceDesc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuSurfObjectGetResourceDesc +#endif +cuSurfObjectGetResourceDesc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4792(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4792(%rip) +2: + pushq $599 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuSurfRefGetArray + .p2align 4 + .type cuSurfRefGetArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuSurfRefGetArray +#endif +cuSurfRefGetArray: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4800(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4800(%rip) +2: + pushq $600 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuSurfRefSetArray + .p2align 4 + .type cuSurfRefSetArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuSurfRefSetArray +#endif +cuSurfRefSetArray: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4808(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4808(%rip) +2: + pushq $601 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTensorMapEncodeIm2col + .p2align 4 + .type cuTensorMapEncodeIm2col, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTensorMapEncodeIm2col +#endif +cuTensorMapEncodeIm2col: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4816(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4816(%rip) +2: + pushq $602 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTensorMapEncodeIm2colWide + .p2align 4 + .type cuTensorMapEncodeIm2colWide, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTensorMapEncodeIm2colWide +#endif +cuTensorMapEncodeIm2colWide: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4824(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4824(%rip) +2: + pushq $603 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTensorMapEncodeTiled + .p2align 4 + .type cuTensorMapEncodeTiled, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTensorMapEncodeTiled +#endif +cuTensorMapEncodeTiled: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4832(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4832(%rip) +2: + pushq $604 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTensorMapReplaceAddress + .p2align 4 + .type cuTensorMapReplaceAddress, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTensorMapReplaceAddress +#endif +cuTensorMapReplaceAddress: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4840(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4840(%rip) +2: + pushq $605 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexObjectCreate + .p2align 4 + .type cuTexObjectCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexObjectCreate +#endif +cuTexObjectCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4848(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4848(%rip) +2: + pushq $606 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexObjectDestroy + .p2align 4 + .type cuTexObjectDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexObjectDestroy +#endif +cuTexObjectDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4856(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4856(%rip) +2: + pushq $607 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexObjectGetResourceDesc + .p2align 4 + .type cuTexObjectGetResourceDesc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexObjectGetResourceDesc +#endif +cuTexObjectGetResourceDesc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4864(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4864(%rip) +2: + pushq $608 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexObjectGetResourceViewDesc + .p2align 4 + .type cuTexObjectGetResourceViewDesc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexObjectGetResourceViewDesc +#endif +cuTexObjectGetResourceViewDesc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4872(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4872(%rip) +2: + pushq $609 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexObjectGetTextureDesc + .p2align 4 + .type cuTexObjectGetTextureDesc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexObjectGetTextureDesc +#endif +cuTexObjectGetTextureDesc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4880(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4880(%rip) +2: + pushq $610 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefCreate + .p2align 4 + .type cuTexRefCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefCreate +#endif +cuTexRefCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4888(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4888(%rip) +2: + pushq $611 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefDestroy + .p2align 4 + .type cuTexRefDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefDestroy +#endif +cuTexRefDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4896(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4896(%rip) +2: + pushq $612 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetAddress + .p2align 4 + .type cuTexRefGetAddress, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetAddress +#endif +cuTexRefGetAddress: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4904(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4904(%rip) +2: + pushq $613 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetAddressMode + .p2align 4 + .type cuTexRefGetAddressMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetAddressMode +#endif +cuTexRefGetAddressMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4912(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4912(%rip) +2: + pushq $614 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetAddress_v2 + .p2align 4 + .type cuTexRefGetAddress_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetAddress_v2 +#endif +cuTexRefGetAddress_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4920(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4920(%rip) +2: + pushq $615 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetArray + .p2align 4 + .type cuTexRefGetArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetArray +#endif +cuTexRefGetArray: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4928(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4928(%rip) +2: + pushq $616 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetBorderColor + .p2align 4 + .type cuTexRefGetBorderColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetBorderColor +#endif +cuTexRefGetBorderColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4936(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4936(%rip) +2: + pushq $617 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetFilterMode + .p2align 4 + .type cuTexRefGetFilterMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetFilterMode +#endif +cuTexRefGetFilterMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4944(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4944(%rip) +2: + pushq $618 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetFlags + .p2align 4 + .type cuTexRefGetFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetFlags +#endif +cuTexRefGetFlags: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4952(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4952(%rip) +2: + pushq $619 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetFormat + .p2align 4 + .type cuTexRefGetFormat, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetFormat +#endif +cuTexRefGetFormat: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4960(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4960(%rip) +2: + pushq $620 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetMaxAnisotropy + .p2align 4 + .type cuTexRefGetMaxAnisotropy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetMaxAnisotropy +#endif +cuTexRefGetMaxAnisotropy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4968(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4968(%rip) +2: + pushq $621 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetMipmapFilterMode + .p2align 4 + .type cuTexRefGetMipmapFilterMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetMipmapFilterMode +#endif +cuTexRefGetMipmapFilterMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4976(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4976(%rip) +2: + pushq $622 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetMipmapLevelBias + .p2align 4 + .type cuTexRefGetMipmapLevelBias, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetMipmapLevelBias +#endif +cuTexRefGetMipmapLevelBias: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4984(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4984(%rip) +2: + pushq $623 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetMipmapLevelClamp + .p2align 4 + .type cuTexRefGetMipmapLevelClamp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetMipmapLevelClamp +#endif +cuTexRefGetMipmapLevelClamp: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+4992(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+4992(%rip) +2: + pushq $624 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefGetMipmappedArray + .p2align 4 + .type cuTexRefGetMipmappedArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefGetMipmappedArray +#endif +cuTexRefGetMipmappedArray: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5000(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5000(%rip) +2: + pushq $625 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetAddress + .p2align 4 + .type cuTexRefSetAddress, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetAddress +#endif +cuTexRefSetAddress: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5008(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5008(%rip) +2: + pushq $626 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetAddress2D + .p2align 4 + .type cuTexRefSetAddress2D, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetAddress2D +#endif +cuTexRefSetAddress2D: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5016(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5016(%rip) +2: + pushq $627 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetAddress2D_v2 + .p2align 4 + .type cuTexRefSetAddress2D_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetAddress2D_v2 +#endif +cuTexRefSetAddress2D_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5024(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5024(%rip) +2: + pushq $628 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetAddress2D_v3 + .p2align 4 + .type cuTexRefSetAddress2D_v3, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetAddress2D_v3 +#endif +cuTexRefSetAddress2D_v3: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5032(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5032(%rip) +2: + pushq $629 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetAddressMode + .p2align 4 + .type cuTexRefSetAddressMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetAddressMode +#endif +cuTexRefSetAddressMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5040(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5040(%rip) +2: + pushq $630 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetAddress_v2 + .p2align 4 + .type cuTexRefSetAddress_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetAddress_v2 +#endif +cuTexRefSetAddress_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5048(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5048(%rip) +2: + pushq $631 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetArray + .p2align 4 + .type cuTexRefSetArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetArray +#endif +cuTexRefSetArray: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5056(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5056(%rip) +2: + pushq $632 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetBorderColor + .p2align 4 + .type cuTexRefSetBorderColor, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetBorderColor +#endif +cuTexRefSetBorderColor: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5064(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5064(%rip) +2: + pushq $633 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetFilterMode + .p2align 4 + .type cuTexRefSetFilterMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetFilterMode +#endif +cuTexRefSetFilterMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5072(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5072(%rip) +2: + pushq $634 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetFlags + .p2align 4 + .type cuTexRefSetFlags, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetFlags +#endif +cuTexRefSetFlags: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5080(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5080(%rip) +2: + pushq $635 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetFormat + .p2align 4 + .type cuTexRefSetFormat, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetFormat +#endif +cuTexRefSetFormat: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5088(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5088(%rip) +2: + pushq $636 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetMaxAnisotropy + .p2align 4 + .type cuTexRefSetMaxAnisotropy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetMaxAnisotropy +#endif +cuTexRefSetMaxAnisotropy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5096(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5096(%rip) +2: + pushq $637 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetMipmapFilterMode + .p2align 4 + .type cuTexRefSetMipmapFilterMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetMipmapFilterMode +#endif +cuTexRefSetMipmapFilterMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5104(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5104(%rip) +2: + pushq $638 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetMipmapLevelBias + .p2align 4 + .type cuTexRefSetMipmapLevelBias, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetMipmapLevelBias +#endif +cuTexRefSetMipmapLevelBias: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5112(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5112(%rip) +2: + pushq $639 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetMipmapLevelClamp + .p2align 4 + .type cuTexRefSetMipmapLevelClamp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetMipmapLevelClamp +#endif +cuTexRefSetMipmapLevelClamp: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5120(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5120(%rip) +2: + pushq $640 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuTexRefSetMipmappedArray + .p2align 4 + .type cuTexRefSetMipmappedArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuTexRefSetMipmappedArray +#endif +cuTexRefSetMipmappedArray: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5128(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5128(%rip) +2: + pushq $641 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuThreadExchangeStreamCaptureMode + .p2align 4 + .type cuThreadExchangeStreamCaptureMode, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuThreadExchangeStreamCaptureMode +#endif +cuThreadExchangeStreamCaptureMode: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5136(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5136(%rip) +2: + pushq $642 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuUserObjectCreate + .p2align 4 + .type cuUserObjectCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuUserObjectCreate +#endif +cuUserObjectCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5144(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5144(%rip) +2: + pushq $643 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuUserObjectRelease + .p2align 4 + .type cuUserObjectRelease, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuUserObjectRelease +#endif +cuUserObjectRelease: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5152(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5152(%rip) +2: + pushq $644 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuUserObjectRetain + .p2align 4 + .type cuUserObjectRetain, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuUserObjectRetain +#endif +cuUserObjectRetain: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5160(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5160(%rip) +2: + pushq $645 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuVDPAUCtxCreate + .p2align 4 + .type cuVDPAUCtxCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuVDPAUCtxCreate +#endif +cuVDPAUCtxCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5168(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5168(%rip) +2: + pushq $646 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuVDPAUCtxCreate_v2 + .p2align 4 + .type cuVDPAUCtxCreate_v2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuVDPAUCtxCreate_v2 +#endif +cuVDPAUCtxCreate_v2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5176(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5176(%rip) +2: + pushq $647 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuVDPAUGetDevice + .p2align 4 + .type cuVDPAUGetDevice, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuVDPAUGetDevice +#endif +cuVDPAUGetDevice: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5184(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5184(%rip) +2: + pushq $648 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuWaitExternalSemaphoresAsync + .p2align 4 + .type cuWaitExternalSemaphoresAsync, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuWaitExternalSemaphoresAsync +#endif +cuWaitExternalSemaphoresAsync: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5192(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5192(%rip) +2: + pushq $649 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuWaitExternalSemaphoresAsync_ptsz + .p2align 4 + .type cuWaitExternalSemaphoresAsync_ptsz, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuWaitExternalSemaphoresAsync_ptsz +#endif +cuWaitExternalSemaphoresAsync_ptsz: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5200(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5200(%rip) +2: + pushq $650 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cudbgApiAttach + .p2align 4 + .type cudbgApiAttach, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cudbgApiAttach +#endif +cudbgApiAttach: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5208(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5208(%rip) +2: + pushq $651 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cudbgApiDetach + .p2align 4 + .type cudbgApiDetach, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cudbgApiDetach +#endif +cudbgApiDetach: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5216(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5216(%rip) +2: + pushq $652 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cudbgApiInit + .p2align 4 + .type cudbgApiInit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cudbgApiInit +#endif +cudbgApiInit: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5224(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5224(%rip) +2: + pushq $653 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cudbgGetAPI + .p2align 4 + .type cudbgGetAPI, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cudbgGetAPI +#endif +cudbgGetAPI: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5232(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5232(%rip) +2: + pushq $654 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cudbgGetAPIVersion + .p2align 4 + .type cudbgGetAPIVersion, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cudbgGetAPIVersion +#endif +cudbgGetAPIVersion: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5240(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5240(%rip) +2: + pushq $655 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cudbgMain + .p2align 4 + .type cudbgMain, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cudbgMain +#endif +cudbgMain: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5248(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5248(%rip) +2: + pushq $656 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cudbgReportDriverApiError + .p2align 4 + .type cudbgReportDriverApiError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cudbgReportDriverApiError +#endif +cudbgReportDriverApiError: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5256(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5256(%rip) +2: + pushq $657 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cudbgReportDriverInternalError + .p2align 4 + .type cudbgReportDriverInternalError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cudbgReportDriverInternalError +#endif +cudbgReportDriverInternalError: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libcuda_so_tramp_table+5264(%rip) + je 2f +1: + jmp *_libcuda_so_tramp_table+5264(%rip) +2: + pushq $658 + .cfi_adjust_cfa_offset 8 + call _libcuda_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/x86_64-linux-gnu/libnvcuvid.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/x86_64-linux-gnu/libnvcuvid.so.init.c new file mode 100644 index 0000000..472d99d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/x86_64-linux-gnu/libnvcuvid.so.init.c @@ -0,0 +1,282 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libnvcuvid.so.1: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libnvcuvid.so.1"); + CHECK(lib_handle, "failed to load library 'libnvcuvid.so.1' via callback ''"); +#else + lib_handle = dlopen("libnvcuvid.so.1", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libnvcuvid.so.1' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "NvToolCreateInterface", + "NvToolDestroyInterface", + "NvToolGetApiFunctionCount", + "NvToolGetApiID", + "NvToolGetApiNames", + "NvToolGetInterface", + "NvToolSetApiID", + "NvToolSetInterface", + "__std_1U4S4U_X02", + "__std_2U4S4U_X08", + "__std_4U4S4U_X04", + "cuvidConvertYUVToRGB", + "cuvidConvertYUVToRGBArray", + "cuvidCreateDecoder", + "cuvidCreateVideoParser", + "cuvidCreateVideoSource", + "cuvidCreateVideoSourceW", + "cuvidCtxLock", + "cuvidCtxLockCreate", + "cuvidCtxLockDestroy", + "cuvidCtxUnlock", + "cuvidDecodePicture", + "cuvidDestroyDecoder", + "cuvidDestroyVideoParser", + "cuvidDestroyVideoSource", + "cuvidGetDecodeStatus", + "cuvidGetDecoderCaps", + "cuvidGetSourceAudioFormat", + "cuvidGetSourceVideoFormat", + "cuvidGetVideoSourceState", + "cuvidMapVideoFrame", + "cuvidMapVideoFrame64", + "cuvidParseVideoData", + "cuvidPrivateOp", + "cuvidReconfigureDecoder", + "cuvidSetVideoSourceState", + "cuvidUnmapVideoFrame", + "cuvidUnmapVideoFrame64", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libnvcuvid_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libnvcuvid_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libnvcuvid_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libnvcuvid_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libnvcuvid_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libnvcuvid_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libnvcuvid_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libnvcuvid_so_tramp_table, 0, SYM_COUNT * sizeof(_libnvcuvid_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/x86_64-linux-gnu/libnvcuvid.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/x86_64-linux-gnu/libnvcuvid.so.tramp.S new file mode 100644 index 0000000..53fd9d2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/nvidia/x86_64-linux-gnu/libnvcuvid.so.tramp.S @@ -0,0 +1,1450 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libnvcuvid_so_tramp_table + .hidden _libnvcuvid_so_tramp_table + .align 8 +_libnvcuvid_so_tramp_table: + .zero 312 + + .text + + .globl _libnvcuvid_so_tramp_resolve + .hidden _libnvcuvid_so_tramp_resolve + + .globl _libnvcuvid_so_save_regs_and_resolve + .hidden _libnvcuvid_so_save_regs_and_resolve + .type _libnvcuvid_so_save_regs_and_resolve, %function +_libnvcuvid_so_save_regs_and_resolve: + .cfi_startproc + +#define PUSH_REG(reg) pushq %reg ; .cfi_adjust_cfa_offset 8; .cfi_rel_offset reg, 0 +#define POP_REG(reg) popq %reg ; .cfi_adjust_cfa_offset -8; .cfi_restore reg + +#define DEC_STACK(d) subq $d, %rsp; .cfi_adjust_cfa_offset d +#define INC_STACK(d) addq $d, %rsp; .cfi_adjust_cfa_offset -d + +#define PUSH_MMX_REG(reg) DEC_STACK(8); movq %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_MMX_REG(reg) movq (%rsp), %reg; .cfi_restore reg; INC_STACK(8) + +#define PUSH_XMM_REG(reg) DEC_STACK(16); movdqa %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_XMM_REG(reg) movdqa (%rsp), %reg; .cfi_restore reg; INC_STACK(16) + +// TODO: cfi_offset/cfi_restore +#define PUSH_YMM_REG(reg) DEC_STACK(32); vmovdqu %reg, (%rsp) +#define POP_YMM_REG(reg) vmovdqu (%rsp), %reg; INC_STACK(32) + +// TODO: cfi_offset/cfi_restore +#define PUSH_ZMM_REG(reg) DEC_STACK(64); vmovdqu32 %reg, (%rsp) +#define POP_ZMM_REG(reg) vmovdqu32 (%rsp), %reg; INC_STACK(64) + + // Slow path which calls dlsym, taken only on first call. + // All registers are stored to handle arbitrary calling conventions + // (except x87 FPU registers which do not have to be preserved). + // For Dwarf directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + .cfi_def_cfa_offset 8 // Return address + + PUSH_REG(rdi) // 16 + mov 0x10(%rsp), %rdi + PUSH_REG(rbx) + PUSH_REG(rbx) // 16 + PUSH_REG(rcx) + PUSH_REG(rdx) // 16 + PUSH_REG(rbp) + PUSH_REG(rsi) // 16 + PUSH_REG(r8) + PUSH_REG(r9) // 16 + PUSH_REG(r10) + PUSH_REG(r11) // 16 + PUSH_REG(r12) + PUSH_REG(r13) // 16 + PUSH_REG(r14) + PUSH_REG(r15) // 16 + + // Maybe use cpuid instead of macro to detect current vector size... +#ifdef __AVX512F__ + PUSH_ZMM_REG(zmm0) + PUSH_ZMM_REG(zmm1) + PUSH_ZMM_REG(zmm2) + PUSH_ZMM_REG(zmm3) + PUSH_ZMM_REG(zmm4) + PUSH_ZMM_REG(zmm5) + PUSH_ZMM_REG(zmm6) + PUSH_ZMM_REG(zmm7) +#elif defined __AVX__ + PUSH_YMM_REG(ymm0) + PUSH_YMM_REG(ymm1) + PUSH_YMM_REG(ymm2) + PUSH_YMM_REG(ymm3) + PUSH_YMM_REG(ymm4) + PUSH_YMM_REG(ymm5) + PUSH_YMM_REG(ymm6) + PUSH_YMM_REG(ymm7) +#elif defined __SSE__ + PUSH_XMM_REG(xmm0) + PUSH_XMM_REG(xmm1) + PUSH_XMM_REG(xmm2) + PUSH_XMM_REG(xmm3) + PUSH_XMM_REG(xmm4) + PUSH_XMM_REG(xmm5) + PUSH_XMM_REG(xmm6) + PUSH_XMM_REG(xmm7) +#endif + + // MMX registers are not used to pass arguments so we do not save them + + // Stack is just 8-byte aligned but callee will re-align to 16 + call _libnvcuvid_so_tramp_resolve + +#ifdef __AVX512F__ + POP_ZMM_REG(zmm7) + POP_ZMM_REG(zmm6) + POP_ZMM_REG(zmm5) + POP_ZMM_REG(zmm4) + POP_ZMM_REG(zmm3) + POP_ZMM_REG(zmm2) + POP_ZMM_REG(zmm1) + POP_ZMM_REG(zmm0) // 16 +#elif defined __AVX__ + POP_YMM_REG(ymm7) + POP_YMM_REG(ymm6) + POP_YMM_REG(ymm5) + POP_YMM_REG(ymm4) + POP_YMM_REG(ymm3) + POP_YMM_REG(ymm2) + POP_YMM_REG(ymm1) + POP_YMM_REG(ymm0) // 16 +#elif defined __SSE__ + POP_XMM_REG(xmm7) + POP_XMM_REG(xmm6) + POP_XMM_REG(xmm5) + POP_XMM_REG(xmm4) + POP_XMM_REG(xmm3) + POP_XMM_REG(xmm2) + POP_XMM_REG(xmm1) + POP_XMM_REG(xmm0) // 16 +#endif + + POP_REG(r15) + POP_REG(r14) // 16 + POP_REG(r13) + POP_REG(r12) // 16 + POP_REG(r11) + POP_REG(r10) // 16 + POP_REG(r9) + POP_REG(r8) // 16 + POP_REG(rsi) + POP_REG(rbp) // 16 + POP_REG(rdx) + POP_REG(rcx) // 16 + POP_REG(rbx) + POP_REG(rbx) // 16 + POP_REG(rdi) + + ret + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl NvToolCreateInterface + .p2align 4 + .type NvToolCreateInterface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden NvToolCreateInterface +#endif +NvToolCreateInterface: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+0(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+0(%rip) +2: + pushq $0 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl NvToolDestroyInterface + .p2align 4 + .type NvToolDestroyInterface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden NvToolDestroyInterface +#endif +NvToolDestroyInterface: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+8(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+8(%rip) +2: + pushq $1 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl NvToolGetApiFunctionCount + .p2align 4 + .type NvToolGetApiFunctionCount, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden NvToolGetApiFunctionCount +#endif +NvToolGetApiFunctionCount: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+16(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+16(%rip) +2: + pushq $2 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl NvToolGetApiID + .p2align 4 + .type NvToolGetApiID, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden NvToolGetApiID +#endif +NvToolGetApiID: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+24(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+24(%rip) +2: + pushq $3 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl NvToolGetApiNames + .p2align 4 + .type NvToolGetApiNames, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden NvToolGetApiNames +#endif +NvToolGetApiNames: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+32(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+32(%rip) +2: + pushq $4 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl NvToolGetInterface + .p2align 4 + .type NvToolGetInterface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden NvToolGetInterface +#endif +NvToolGetInterface: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+40(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+40(%rip) +2: + pushq $5 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl NvToolSetApiID + .p2align 4 + .type NvToolSetApiID, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden NvToolSetApiID +#endif +NvToolSetApiID: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+48(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+48(%rip) +2: + pushq $6 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl NvToolSetInterface + .p2align 4 + .type NvToolSetInterface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden NvToolSetInterface +#endif +NvToolSetInterface: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+56(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+56(%rip) +2: + pushq $7 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl __std_1U4S4U_X02 + .p2align 4 + .type __std_1U4S4U_X02, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden __std_1U4S4U_X02 +#endif +__std_1U4S4U_X02: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+64(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+64(%rip) +2: + pushq $8 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl __std_2U4S4U_X08 + .p2align 4 + .type __std_2U4S4U_X08, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden __std_2U4S4U_X08 +#endif +__std_2U4S4U_X08: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+72(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+72(%rip) +2: + pushq $9 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl __std_4U4S4U_X04 + .p2align 4 + .type __std_4U4S4U_X04, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden __std_4U4S4U_X04 +#endif +__std_4U4S4U_X04: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+80(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+80(%rip) +2: + pushq $10 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidConvertYUVToRGB + .p2align 4 + .type cuvidConvertYUVToRGB, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidConvertYUVToRGB +#endif +cuvidConvertYUVToRGB: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+88(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+88(%rip) +2: + pushq $11 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidConvertYUVToRGBArray + .p2align 4 + .type cuvidConvertYUVToRGBArray, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidConvertYUVToRGBArray +#endif +cuvidConvertYUVToRGBArray: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+96(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+96(%rip) +2: + pushq $12 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidCreateDecoder + .p2align 4 + .type cuvidCreateDecoder, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidCreateDecoder +#endif +cuvidCreateDecoder: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+104(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+104(%rip) +2: + pushq $13 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidCreateVideoParser + .p2align 4 + .type cuvidCreateVideoParser, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidCreateVideoParser +#endif +cuvidCreateVideoParser: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+112(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+112(%rip) +2: + pushq $14 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidCreateVideoSource + .p2align 4 + .type cuvidCreateVideoSource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidCreateVideoSource +#endif +cuvidCreateVideoSource: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+120(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+120(%rip) +2: + pushq $15 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidCreateVideoSourceW + .p2align 4 + .type cuvidCreateVideoSourceW, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidCreateVideoSourceW +#endif +cuvidCreateVideoSourceW: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+128(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+128(%rip) +2: + pushq $16 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidCtxLock + .p2align 4 + .type cuvidCtxLock, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidCtxLock +#endif +cuvidCtxLock: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+136(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+136(%rip) +2: + pushq $17 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidCtxLockCreate + .p2align 4 + .type cuvidCtxLockCreate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidCtxLockCreate +#endif +cuvidCtxLockCreate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+144(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+144(%rip) +2: + pushq $18 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidCtxLockDestroy + .p2align 4 + .type cuvidCtxLockDestroy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidCtxLockDestroy +#endif +cuvidCtxLockDestroy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+152(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+152(%rip) +2: + pushq $19 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidCtxUnlock + .p2align 4 + .type cuvidCtxUnlock, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidCtxUnlock +#endif +cuvidCtxUnlock: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+160(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+160(%rip) +2: + pushq $20 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidDecodePicture + .p2align 4 + .type cuvidDecodePicture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidDecodePicture +#endif +cuvidDecodePicture: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+168(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+168(%rip) +2: + pushq $21 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidDestroyDecoder + .p2align 4 + .type cuvidDestroyDecoder, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidDestroyDecoder +#endif +cuvidDestroyDecoder: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+176(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+176(%rip) +2: + pushq $22 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidDestroyVideoParser + .p2align 4 + .type cuvidDestroyVideoParser, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidDestroyVideoParser +#endif +cuvidDestroyVideoParser: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+184(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+184(%rip) +2: + pushq $23 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidDestroyVideoSource + .p2align 4 + .type cuvidDestroyVideoSource, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidDestroyVideoSource +#endif +cuvidDestroyVideoSource: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+192(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+192(%rip) +2: + pushq $24 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidGetDecodeStatus + .p2align 4 + .type cuvidGetDecodeStatus, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidGetDecodeStatus +#endif +cuvidGetDecodeStatus: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+200(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+200(%rip) +2: + pushq $25 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidGetDecoderCaps + .p2align 4 + .type cuvidGetDecoderCaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidGetDecoderCaps +#endif +cuvidGetDecoderCaps: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+208(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+208(%rip) +2: + pushq $26 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidGetSourceAudioFormat + .p2align 4 + .type cuvidGetSourceAudioFormat, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidGetSourceAudioFormat +#endif +cuvidGetSourceAudioFormat: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+216(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+216(%rip) +2: + pushq $27 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidGetSourceVideoFormat + .p2align 4 + .type cuvidGetSourceVideoFormat, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidGetSourceVideoFormat +#endif +cuvidGetSourceVideoFormat: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+224(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+224(%rip) +2: + pushq $28 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidGetVideoSourceState + .p2align 4 + .type cuvidGetVideoSourceState, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidGetVideoSourceState +#endif +cuvidGetVideoSourceState: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+232(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+232(%rip) +2: + pushq $29 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidMapVideoFrame + .p2align 4 + .type cuvidMapVideoFrame, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidMapVideoFrame +#endif +cuvidMapVideoFrame: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+240(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+240(%rip) +2: + pushq $30 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidMapVideoFrame64 + .p2align 4 + .type cuvidMapVideoFrame64, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidMapVideoFrame64 +#endif +cuvidMapVideoFrame64: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+248(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+248(%rip) +2: + pushq $31 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidParseVideoData + .p2align 4 + .type cuvidParseVideoData, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidParseVideoData +#endif +cuvidParseVideoData: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+256(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+256(%rip) +2: + pushq $32 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidPrivateOp + .p2align 4 + .type cuvidPrivateOp, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidPrivateOp +#endif +cuvidPrivateOp: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+264(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+264(%rip) +2: + pushq $33 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidReconfigureDecoder + .p2align 4 + .type cuvidReconfigureDecoder, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidReconfigureDecoder +#endif +cuvidReconfigureDecoder: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+272(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+272(%rip) +2: + pushq $34 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidSetVideoSourceState + .p2align 4 + .type cuvidSetVideoSourceState, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidSetVideoSourceState +#endif +cuvidSetVideoSourceState: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+280(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+280(%rip) +2: + pushq $35 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidUnmapVideoFrame + .p2align 4 + .type cuvidUnmapVideoFrame, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidUnmapVideoFrame +#endif +cuvidUnmapVideoFrame: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+288(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+288(%rip) +2: + pushq $36 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl cuvidUnmapVideoFrame64 + .p2align 4 + .type cuvidUnmapVideoFrame64, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden cuvidUnmapVideoFrame64 +#endif +cuvidUnmapVideoFrame64: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libnvcuvid_so_tramp_table+296(%rip) + je 2f +1: + jmp *_libnvcuvid_so_tramp_table+296(%rip) +2: + pushq $37 + .cfi_adjust_cfa_offset 8 + call _libnvcuvid_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/aarch64-linux-gnu/libva-drm.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/aarch64-linux-gnu/libva-drm.so.init.c new file mode 100644 index 0000000..b9df56f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/aarch64-linux-gnu/libva-drm.so.init.c @@ -0,0 +1,245 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libva-drm.so.2: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libva-drm.so.2"); + CHECK(lib_handle, "failed to load library 'libva-drm.so.2' via callback ''"); +#else + lib_handle = dlopen("libva-drm.so.2", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libva-drm.so.2' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "vaGetDisplayDRM", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libva_drm_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libva_drm_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libva_drm_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libva_drm_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libva_drm_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libva_drm_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libva_drm_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libva_drm_so_tramp_table, 0, SYM_COUNT * sizeof(_libva_drm_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/aarch64-linux-gnu/libva-drm.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/aarch64-linux-gnu/libva-drm.so.tramp.S new file mode 100644 index 0000000..62d624d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/aarch64-linux-gnu/libva-drm.so.tramp.S @@ -0,0 +1,122 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#define lr x30 +#define ip0 x16 + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libva_drm_so_tramp_table + .hidden _libva_drm_so_tramp_table + .align 8 +_libva_drm_so_tramp_table: + .zero 16 + + .text + + .globl _libva_drm_so_tramp_resolve + .hidden _libva_drm_so_tramp_resolve + + .globl _libva_drm_so_save_regs_and_resolve + .hidden _libva_drm_so_save_regs_and_resolve + .type _libva_drm_so_save_regs_and_resolve, %function +_libva_drm_so_save_regs_and_resolve: + .cfi_startproc + + // Slow path which calls dlsym, taken only on first call. + // Registers are saved according to "Procedure Call Standard for the Arm® 64-bit Architecture". + // For DWARF directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + // Stack is aligned at 16 bytes + +#define PUSH_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 8 +#define POP_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore reg2; .cfi_restore reg1 + +#define PUSH_WIDE_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-32]!; .cfi_adjust_cfa_offset 32; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 16 +#define POP_WIDE_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #32; .cfi_adjust_cfa_offset -32; .cfi_restore reg2; .cfi_restore reg1 + + // Save only arguments (and lr) + PUSH_PAIR(x0, x1) + PUSH_PAIR(x2, x3) + PUSH_PAIR(x4, x5) + PUSH_PAIR(x6, x7) + PUSH_PAIR(x8, lr) + + ldr x0, [sp, #80] // 16*5 + + PUSH_WIDE_PAIR(q0, q1) + PUSH_WIDE_PAIR(q2, q3) + PUSH_WIDE_PAIR(q4, q5) + PUSH_WIDE_PAIR(q6, q7) + + // Stack is aligned at 16 bytes + + bl _libva_drm_so_tramp_resolve + mov ip0, x0 + + // TODO: pop pc? + + POP_WIDE_PAIR(q6, q7) + POP_WIDE_PAIR(q4, q5) + POP_WIDE_PAIR(q2, q3) + POP_WIDE_PAIR(q0, q1) + + POP_PAIR(x8, lr) + POP_PAIR(x6, x7) + POP_PAIR(x4, x5) + POP_PAIR(x2, x3) + POP_PAIR(x0, x1) + + br lr + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaGetDisplayDRM + .p2align 4 + .type vaGetDisplayDRM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaGetDisplayDRM +#endif +vaGetDisplayDRM: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_drm_so_tramp_table+0 + ldr ip0, [ip0, #:lo12:_libva_drm_so_tramp_table+0] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 0 & 0xffff +#if 0 > 0xffff + movk ip0, 0 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_drm_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/aarch64-linux-gnu/libva.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/aarch64-linux-gnu/libva.so.init.c new file mode 100644 index 0000000..d2ff7f2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/aarch64-linux-gnu/libva.so.init.c @@ -0,0 +1,332 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libva.so.2: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libva.so.2"); + CHECK(lib_handle, "failed to load library 'libva.so.2' via callback ''"); +#else + lib_handle = dlopen("libva.so.2", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libva.so.2' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "vaAcquireBufferHandle", + "vaAssociateSubpicture", + "vaAttachProtectedSession", + "vaBeginPicture", + "vaBufferInfo", + "vaBufferSetNumElements", + "vaBufferTypeStr", + "vaConfigAttribTypeStr", + "vaCopy", + "vaCreateBuffer", + "vaCreateBuffer2", + "vaCreateConfig", + "vaCreateContext", + "vaCreateImage", + "vaCreateMFContext", + "vaCreateProtectedSession", + "vaCreateSubpicture", + "vaCreateSurfaces", + "vaDeassociateSubpicture", + "vaDeriveImage", + "vaDestroyBuffer", + "vaDestroyConfig", + "vaDestroyContext", + "vaDestroyImage", + "vaDestroyProtectedSession", + "vaDestroySubpicture", + "vaDestroySurfaces", + "vaDetachProtectedSession", + "vaDisplayIsValid", + "vaEndPicture", + "vaEntrypointStr", + "vaErrorStr", + "vaExportSurfaceHandle", + "vaGetConfigAttributes", + "vaGetDisplayAttributes", + "vaGetImage", + "vaGetLibFunc", + "vaInitialize", + "vaLockSurface", + "vaMFAddContext", + "vaMFReleaseContext", + "vaMFSubmit", + "vaMapBuffer", + "vaMapBuffer2", + "vaMaxNumConfigAttributes", + "vaMaxNumDisplayAttributes", + "vaMaxNumEntrypoints", + "vaMaxNumImageFormats", + "vaMaxNumProfiles", + "vaMaxNumSubpictureFormats", + "vaProfileStr", + "vaProtectedSessionExecute", + "vaPutImage", + "vaQueryConfigAttributes", + "vaQueryConfigEntrypoints", + "vaQueryConfigProfiles", + "vaQueryDisplayAttributes", + "vaQueryImageFormats", + "vaQueryProcessingRate", + "vaQuerySubpictureFormats", + "vaQuerySurfaceAttributes", + "vaQuerySurfaceError", + "vaQuerySurfaceStatus", + "vaQueryVendorString", + "vaQueryVideoProcFilterCaps", + "vaQueryVideoProcFilters", + "vaQueryVideoProcPipelineCaps", + "vaReleaseBufferHandle", + "vaRenderPicture", + "vaSetDisplayAttributes", + "vaSetDriverName", + "vaSetErrorCallback", + "vaSetImagePalette", + "vaSetInfoCallback", + "vaSetSubpictureChromakey", + "vaSetSubpictureGlobalAlpha", + "vaSetSubpictureImage", + "vaStatusStr", + "vaSyncBuffer", + "vaSyncSurface", + "vaSyncSurface2", + "vaTerminate", + "vaUnlockSurface", + "vaUnmapBuffer", + "va_TracePutSurface", + "va_TraceStatus", + "va_newDisplayContext", + "va_newDriverContext", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libva_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libva_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libva_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libva_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libva_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libva_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libva_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libva_so_tramp_table, 0, SYM_COUNT * sizeof(_libva_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/aarch64-linux-gnu/libva.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/aarch64-linux-gnu/libva.so.tramp.S new file mode 100644 index 0000000..447f40d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/aarch64-linux-gnu/libva.so.tramp.S @@ -0,0 +1,3689 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#define lr x30 +#define ip0 x16 + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libva_so_tramp_table + .hidden _libva_so_tramp_table + .align 8 +_libva_so_tramp_table: + .zero 712 + + .text + + .globl _libva_so_tramp_resolve + .hidden _libva_so_tramp_resolve + + .globl _libva_so_save_regs_and_resolve + .hidden _libva_so_save_regs_and_resolve + .type _libva_so_save_regs_and_resolve, %function +_libva_so_save_regs_and_resolve: + .cfi_startproc + + // Slow path which calls dlsym, taken only on first call. + // Registers are saved according to "Procedure Call Standard for the Arm® 64-bit Architecture". + // For DWARF directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + // Stack is aligned at 16 bytes + +#define PUSH_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 8 +#define POP_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore reg2; .cfi_restore reg1 + +#define PUSH_WIDE_PAIR(reg1, reg2) stp reg1, reg2, [sp, #-32]!; .cfi_adjust_cfa_offset 32; .cfi_rel_offset reg1, 0; .cfi_rel_offset reg2, 16 +#define POP_WIDE_PAIR(reg1, reg2) ldp reg1, reg2, [sp], #32; .cfi_adjust_cfa_offset -32; .cfi_restore reg2; .cfi_restore reg1 + + // Save only arguments (and lr) + PUSH_PAIR(x0, x1) + PUSH_PAIR(x2, x3) + PUSH_PAIR(x4, x5) + PUSH_PAIR(x6, x7) + PUSH_PAIR(x8, lr) + + ldr x0, [sp, #80] // 16*5 + + PUSH_WIDE_PAIR(q0, q1) + PUSH_WIDE_PAIR(q2, q3) + PUSH_WIDE_PAIR(q4, q5) + PUSH_WIDE_PAIR(q6, q7) + + // Stack is aligned at 16 bytes + + bl _libva_so_tramp_resolve + mov ip0, x0 + + // TODO: pop pc? + + POP_WIDE_PAIR(q6, q7) + POP_WIDE_PAIR(q4, q5) + POP_WIDE_PAIR(q2, q3) + POP_WIDE_PAIR(q0, q1) + + POP_PAIR(x8, lr) + POP_PAIR(x6, x7) + POP_PAIR(x4, x5) + POP_PAIR(x2, x3) + POP_PAIR(x0, x1) + + br lr + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaAcquireBufferHandle + .p2align 4 + .type vaAcquireBufferHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaAcquireBufferHandle +#endif +vaAcquireBufferHandle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+0 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+0] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 0 & 0xffff +#if 0 > 0xffff + movk ip0, 0 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaAssociateSubpicture + .p2align 4 + .type vaAssociateSubpicture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaAssociateSubpicture +#endif +vaAssociateSubpicture: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+8 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+8] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 1 & 0xffff +#if 1 > 0xffff + movk ip0, 1 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaAttachProtectedSession + .p2align 4 + .type vaAttachProtectedSession, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaAttachProtectedSession +#endif +vaAttachProtectedSession: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+16 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+16] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 2 & 0xffff +#if 2 > 0xffff + movk ip0, 2 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaBeginPicture + .p2align 4 + .type vaBeginPicture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaBeginPicture +#endif +vaBeginPicture: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+24 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+24] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 3 & 0xffff +#if 3 > 0xffff + movk ip0, 3 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaBufferInfo + .p2align 4 + .type vaBufferInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaBufferInfo +#endif +vaBufferInfo: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+32 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+32] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 4 & 0xffff +#if 4 > 0xffff + movk ip0, 4 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaBufferSetNumElements + .p2align 4 + .type vaBufferSetNumElements, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaBufferSetNumElements +#endif +vaBufferSetNumElements: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+40 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+40] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 5 & 0xffff +#if 5 > 0xffff + movk ip0, 5 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaBufferTypeStr + .p2align 4 + .type vaBufferTypeStr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaBufferTypeStr +#endif +vaBufferTypeStr: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+48 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+48] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 6 & 0xffff +#if 6 > 0xffff + movk ip0, 6 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaConfigAttribTypeStr + .p2align 4 + .type vaConfigAttribTypeStr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaConfigAttribTypeStr +#endif +vaConfigAttribTypeStr: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+56 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+56] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 7 & 0xffff +#if 7 > 0xffff + movk ip0, 7 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCopy + .p2align 4 + .type vaCopy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCopy +#endif +vaCopy: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+64 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+64] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 8 & 0xffff +#if 8 > 0xffff + movk ip0, 8 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCreateBuffer + .p2align 4 + .type vaCreateBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCreateBuffer +#endif +vaCreateBuffer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+72 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+72] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 9 & 0xffff +#if 9 > 0xffff + movk ip0, 9 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCreateBuffer2 + .p2align 4 + .type vaCreateBuffer2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCreateBuffer2 +#endif +vaCreateBuffer2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+80 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+80] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 10 & 0xffff +#if 10 > 0xffff + movk ip0, 10 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCreateConfig + .p2align 4 + .type vaCreateConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCreateConfig +#endif +vaCreateConfig: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+88 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+88] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 11 & 0xffff +#if 11 > 0xffff + movk ip0, 11 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCreateContext + .p2align 4 + .type vaCreateContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCreateContext +#endif +vaCreateContext: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+96 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+96] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 12 & 0xffff +#if 12 > 0xffff + movk ip0, 12 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCreateImage + .p2align 4 + .type vaCreateImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCreateImage +#endif +vaCreateImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+104 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+104] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 13 & 0xffff +#if 13 > 0xffff + movk ip0, 13 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCreateMFContext + .p2align 4 + .type vaCreateMFContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCreateMFContext +#endif +vaCreateMFContext: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+112 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+112] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 14 & 0xffff +#if 14 > 0xffff + movk ip0, 14 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCreateProtectedSession + .p2align 4 + .type vaCreateProtectedSession, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCreateProtectedSession +#endif +vaCreateProtectedSession: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+120 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+120] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 15 & 0xffff +#if 15 > 0xffff + movk ip0, 15 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCreateSubpicture + .p2align 4 + .type vaCreateSubpicture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCreateSubpicture +#endif +vaCreateSubpicture: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+128 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+128] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 16 & 0xffff +#if 16 > 0xffff + movk ip0, 16 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCreateSurfaces + .p2align 4 + .type vaCreateSurfaces, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCreateSurfaces +#endif +vaCreateSurfaces: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+136 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+136] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 17 & 0xffff +#if 17 > 0xffff + movk ip0, 17 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDeassociateSubpicture + .p2align 4 + .type vaDeassociateSubpicture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDeassociateSubpicture +#endif +vaDeassociateSubpicture: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+144 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+144] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 18 & 0xffff +#if 18 > 0xffff + movk ip0, 18 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDeriveImage + .p2align 4 + .type vaDeriveImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDeriveImage +#endif +vaDeriveImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+152 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+152] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 19 & 0xffff +#if 19 > 0xffff + movk ip0, 19 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDestroyBuffer + .p2align 4 + .type vaDestroyBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDestroyBuffer +#endif +vaDestroyBuffer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+160 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+160] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 20 & 0xffff +#if 20 > 0xffff + movk ip0, 20 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDestroyConfig + .p2align 4 + .type vaDestroyConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDestroyConfig +#endif +vaDestroyConfig: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+168 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+168] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 21 & 0xffff +#if 21 > 0xffff + movk ip0, 21 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDestroyContext + .p2align 4 + .type vaDestroyContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDestroyContext +#endif +vaDestroyContext: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+176 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+176] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 22 & 0xffff +#if 22 > 0xffff + movk ip0, 22 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDestroyImage + .p2align 4 + .type vaDestroyImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDestroyImage +#endif +vaDestroyImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+184 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+184] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 23 & 0xffff +#if 23 > 0xffff + movk ip0, 23 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDestroyProtectedSession + .p2align 4 + .type vaDestroyProtectedSession, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDestroyProtectedSession +#endif +vaDestroyProtectedSession: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+192 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+192] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 24 & 0xffff +#if 24 > 0xffff + movk ip0, 24 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDestroySubpicture + .p2align 4 + .type vaDestroySubpicture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDestroySubpicture +#endif +vaDestroySubpicture: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+200 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+200] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 25 & 0xffff +#if 25 > 0xffff + movk ip0, 25 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDestroySurfaces + .p2align 4 + .type vaDestroySurfaces, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDestroySurfaces +#endif +vaDestroySurfaces: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+208 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+208] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 26 & 0xffff +#if 26 > 0xffff + movk ip0, 26 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDetachProtectedSession + .p2align 4 + .type vaDetachProtectedSession, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDetachProtectedSession +#endif +vaDetachProtectedSession: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+216 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+216] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 27 & 0xffff +#if 27 > 0xffff + movk ip0, 27 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDisplayIsValid + .p2align 4 + .type vaDisplayIsValid, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDisplayIsValid +#endif +vaDisplayIsValid: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+224 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+224] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 28 & 0xffff +#if 28 > 0xffff + movk ip0, 28 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaEndPicture + .p2align 4 + .type vaEndPicture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaEndPicture +#endif +vaEndPicture: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+232 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+232] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 29 & 0xffff +#if 29 > 0xffff + movk ip0, 29 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaEntrypointStr + .p2align 4 + .type vaEntrypointStr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaEntrypointStr +#endif +vaEntrypointStr: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+240 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+240] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 30 & 0xffff +#if 30 > 0xffff + movk ip0, 30 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaErrorStr + .p2align 4 + .type vaErrorStr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaErrorStr +#endif +vaErrorStr: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+248 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+248] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 31 & 0xffff +#if 31 > 0xffff + movk ip0, 31 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaExportSurfaceHandle + .p2align 4 + .type vaExportSurfaceHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaExportSurfaceHandle +#endif +vaExportSurfaceHandle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+256 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+256] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 32 & 0xffff +#if 32 > 0xffff + movk ip0, 32 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaGetConfigAttributes + .p2align 4 + .type vaGetConfigAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaGetConfigAttributes +#endif +vaGetConfigAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+264 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+264] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 33 & 0xffff +#if 33 > 0xffff + movk ip0, 33 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaGetDisplayAttributes + .p2align 4 + .type vaGetDisplayAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaGetDisplayAttributes +#endif +vaGetDisplayAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+272 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+272] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 34 & 0xffff +#if 34 > 0xffff + movk ip0, 34 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaGetImage + .p2align 4 + .type vaGetImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaGetImage +#endif +vaGetImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+280 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+280] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 35 & 0xffff +#if 35 > 0xffff + movk ip0, 35 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaGetLibFunc + .p2align 4 + .type vaGetLibFunc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaGetLibFunc +#endif +vaGetLibFunc: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+288 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+288] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 36 & 0xffff +#if 36 > 0xffff + movk ip0, 36 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaInitialize + .p2align 4 + .type vaInitialize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaInitialize +#endif +vaInitialize: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+296 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+296] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 37 & 0xffff +#if 37 > 0xffff + movk ip0, 37 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaLockSurface + .p2align 4 + .type vaLockSurface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaLockSurface +#endif +vaLockSurface: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+304 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+304] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 38 & 0xffff +#if 38 > 0xffff + movk ip0, 38 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMFAddContext + .p2align 4 + .type vaMFAddContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMFAddContext +#endif +vaMFAddContext: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+312 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+312] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 39 & 0xffff +#if 39 > 0xffff + movk ip0, 39 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMFReleaseContext + .p2align 4 + .type vaMFReleaseContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMFReleaseContext +#endif +vaMFReleaseContext: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+320 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+320] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 40 & 0xffff +#if 40 > 0xffff + movk ip0, 40 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMFSubmit + .p2align 4 + .type vaMFSubmit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMFSubmit +#endif +vaMFSubmit: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+328 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+328] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 41 & 0xffff +#if 41 > 0xffff + movk ip0, 41 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMapBuffer + .p2align 4 + .type vaMapBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMapBuffer +#endif +vaMapBuffer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+336 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+336] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 42 & 0xffff +#if 42 > 0xffff + movk ip0, 42 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMapBuffer2 + .p2align 4 + .type vaMapBuffer2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMapBuffer2 +#endif +vaMapBuffer2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+344 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+344] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 43 & 0xffff +#if 43 > 0xffff + movk ip0, 43 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMaxNumConfigAttributes + .p2align 4 + .type vaMaxNumConfigAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMaxNumConfigAttributes +#endif +vaMaxNumConfigAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+352 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+352] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 44 & 0xffff +#if 44 > 0xffff + movk ip0, 44 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMaxNumDisplayAttributes + .p2align 4 + .type vaMaxNumDisplayAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMaxNumDisplayAttributes +#endif +vaMaxNumDisplayAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+360 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+360] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 45 & 0xffff +#if 45 > 0xffff + movk ip0, 45 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMaxNumEntrypoints + .p2align 4 + .type vaMaxNumEntrypoints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMaxNumEntrypoints +#endif +vaMaxNumEntrypoints: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+368 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+368] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 46 & 0xffff +#if 46 > 0xffff + movk ip0, 46 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMaxNumImageFormats + .p2align 4 + .type vaMaxNumImageFormats, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMaxNumImageFormats +#endif +vaMaxNumImageFormats: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+376 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+376] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 47 & 0xffff +#if 47 > 0xffff + movk ip0, 47 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMaxNumProfiles + .p2align 4 + .type vaMaxNumProfiles, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMaxNumProfiles +#endif +vaMaxNumProfiles: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+384 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+384] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 48 & 0xffff +#if 48 > 0xffff + movk ip0, 48 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMaxNumSubpictureFormats + .p2align 4 + .type vaMaxNumSubpictureFormats, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMaxNumSubpictureFormats +#endif +vaMaxNumSubpictureFormats: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+392 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+392] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 49 & 0xffff +#if 49 > 0xffff + movk ip0, 49 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaProfileStr + .p2align 4 + .type vaProfileStr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaProfileStr +#endif +vaProfileStr: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+400 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+400] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 50 & 0xffff +#if 50 > 0xffff + movk ip0, 50 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaProtectedSessionExecute + .p2align 4 + .type vaProtectedSessionExecute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaProtectedSessionExecute +#endif +vaProtectedSessionExecute: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+408 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+408] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 51 & 0xffff +#if 51 > 0xffff + movk ip0, 51 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaPutImage + .p2align 4 + .type vaPutImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaPutImage +#endif +vaPutImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+416 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+416] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 52 & 0xffff +#if 52 > 0xffff + movk ip0, 52 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryConfigAttributes + .p2align 4 + .type vaQueryConfigAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryConfigAttributes +#endif +vaQueryConfigAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+424 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+424] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 53 & 0xffff +#if 53 > 0xffff + movk ip0, 53 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryConfigEntrypoints + .p2align 4 + .type vaQueryConfigEntrypoints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryConfigEntrypoints +#endif +vaQueryConfigEntrypoints: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+432 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+432] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 54 & 0xffff +#if 54 > 0xffff + movk ip0, 54 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryConfigProfiles + .p2align 4 + .type vaQueryConfigProfiles, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryConfigProfiles +#endif +vaQueryConfigProfiles: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+440 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+440] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 55 & 0xffff +#if 55 > 0xffff + movk ip0, 55 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryDisplayAttributes + .p2align 4 + .type vaQueryDisplayAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryDisplayAttributes +#endif +vaQueryDisplayAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+448 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+448] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 56 & 0xffff +#if 56 > 0xffff + movk ip0, 56 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryImageFormats + .p2align 4 + .type vaQueryImageFormats, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryImageFormats +#endif +vaQueryImageFormats: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+456 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+456] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 57 & 0xffff +#if 57 > 0xffff + movk ip0, 57 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryProcessingRate + .p2align 4 + .type vaQueryProcessingRate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryProcessingRate +#endif +vaQueryProcessingRate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+464 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+464] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 58 & 0xffff +#if 58 > 0xffff + movk ip0, 58 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQuerySubpictureFormats + .p2align 4 + .type vaQuerySubpictureFormats, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQuerySubpictureFormats +#endif +vaQuerySubpictureFormats: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+472 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+472] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 59 & 0xffff +#if 59 > 0xffff + movk ip0, 59 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQuerySurfaceAttributes + .p2align 4 + .type vaQuerySurfaceAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQuerySurfaceAttributes +#endif +vaQuerySurfaceAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+480 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+480] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 60 & 0xffff +#if 60 > 0xffff + movk ip0, 60 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQuerySurfaceError + .p2align 4 + .type vaQuerySurfaceError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQuerySurfaceError +#endif +vaQuerySurfaceError: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+488 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+488] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 61 & 0xffff +#if 61 > 0xffff + movk ip0, 61 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQuerySurfaceStatus + .p2align 4 + .type vaQuerySurfaceStatus, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQuerySurfaceStatus +#endif +vaQuerySurfaceStatus: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+496 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+496] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 62 & 0xffff +#if 62 > 0xffff + movk ip0, 62 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryVendorString + .p2align 4 + .type vaQueryVendorString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryVendorString +#endif +vaQueryVendorString: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+504 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+504] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 63 & 0xffff +#if 63 > 0xffff + movk ip0, 63 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryVideoProcFilterCaps + .p2align 4 + .type vaQueryVideoProcFilterCaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryVideoProcFilterCaps +#endif +vaQueryVideoProcFilterCaps: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+512 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+512] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 64 & 0xffff +#if 64 > 0xffff + movk ip0, 64 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryVideoProcFilters + .p2align 4 + .type vaQueryVideoProcFilters, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryVideoProcFilters +#endif +vaQueryVideoProcFilters: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+520 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+520] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 65 & 0xffff +#if 65 > 0xffff + movk ip0, 65 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryVideoProcPipelineCaps + .p2align 4 + .type vaQueryVideoProcPipelineCaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryVideoProcPipelineCaps +#endif +vaQueryVideoProcPipelineCaps: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+528 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+528] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 66 & 0xffff +#if 66 > 0xffff + movk ip0, 66 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaReleaseBufferHandle + .p2align 4 + .type vaReleaseBufferHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaReleaseBufferHandle +#endif +vaReleaseBufferHandle: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+536 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+536] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 67 & 0xffff +#if 67 > 0xffff + movk ip0, 67 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaRenderPicture + .p2align 4 + .type vaRenderPicture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaRenderPicture +#endif +vaRenderPicture: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+544 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+544] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 68 & 0xffff +#if 68 > 0xffff + movk ip0, 68 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSetDisplayAttributes + .p2align 4 + .type vaSetDisplayAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSetDisplayAttributes +#endif +vaSetDisplayAttributes: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+552 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+552] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 69 & 0xffff +#if 69 > 0xffff + movk ip0, 69 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSetDriverName + .p2align 4 + .type vaSetDriverName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSetDriverName +#endif +vaSetDriverName: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+560 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+560] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 70 & 0xffff +#if 70 > 0xffff + movk ip0, 70 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSetErrorCallback + .p2align 4 + .type vaSetErrorCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSetErrorCallback +#endif +vaSetErrorCallback: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+568 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+568] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 71 & 0xffff +#if 71 > 0xffff + movk ip0, 71 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSetImagePalette + .p2align 4 + .type vaSetImagePalette, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSetImagePalette +#endif +vaSetImagePalette: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+576 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+576] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 72 & 0xffff +#if 72 > 0xffff + movk ip0, 72 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSetInfoCallback + .p2align 4 + .type vaSetInfoCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSetInfoCallback +#endif +vaSetInfoCallback: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+584 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+584] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 73 & 0xffff +#if 73 > 0xffff + movk ip0, 73 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSetSubpictureChromakey + .p2align 4 + .type vaSetSubpictureChromakey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSetSubpictureChromakey +#endif +vaSetSubpictureChromakey: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+592 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+592] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 74 & 0xffff +#if 74 > 0xffff + movk ip0, 74 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSetSubpictureGlobalAlpha + .p2align 4 + .type vaSetSubpictureGlobalAlpha, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSetSubpictureGlobalAlpha +#endif +vaSetSubpictureGlobalAlpha: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+600 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+600] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 75 & 0xffff +#if 75 > 0xffff + movk ip0, 75 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSetSubpictureImage + .p2align 4 + .type vaSetSubpictureImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSetSubpictureImage +#endif +vaSetSubpictureImage: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+608 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+608] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 76 & 0xffff +#if 76 > 0xffff + movk ip0, 76 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaStatusStr + .p2align 4 + .type vaStatusStr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaStatusStr +#endif +vaStatusStr: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+616 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+616] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 77 & 0xffff +#if 77 > 0xffff + movk ip0, 77 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSyncBuffer + .p2align 4 + .type vaSyncBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSyncBuffer +#endif +vaSyncBuffer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+624 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+624] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 78 & 0xffff +#if 78 > 0xffff + movk ip0, 78 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSyncSurface + .p2align 4 + .type vaSyncSurface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSyncSurface +#endif +vaSyncSurface: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+632 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+632] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 79 & 0xffff +#if 79 > 0xffff + movk ip0, 79 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSyncSurface2 + .p2align 4 + .type vaSyncSurface2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSyncSurface2 +#endif +vaSyncSurface2: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+640 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+640] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 80 & 0xffff +#if 80 > 0xffff + movk ip0, 80 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaTerminate + .p2align 4 + .type vaTerminate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaTerminate +#endif +vaTerminate: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+648 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+648] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 81 & 0xffff +#if 81 > 0xffff + movk ip0, 81 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaUnlockSurface + .p2align 4 + .type vaUnlockSurface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaUnlockSurface +#endif +vaUnlockSurface: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+656 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+656] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 82 & 0xffff +#if 82 > 0xffff + movk ip0, 82 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaUnmapBuffer + .p2align 4 + .type vaUnmapBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaUnmapBuffer +#endif +vaUnmapBuffer: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+664 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+664] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 83 & 0xffff +#if 83 > 0xffff + movk ip0, 83 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl va_TracePutSurface + .p2align 4 + .type va_TracePutSurface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden va_TracePutSurface +#endif +va_TracePutSurface: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+672 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+672] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 84 & 0xffff +#if 84 > 0xffff + movk ip0, 84 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl va_TraceStatus + .p2align 4 + .type va_TraceStatus, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden va_TraceStatus +#endif +va_TraceStatus: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+680 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+680] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 85 & 0xffff +#if 85 > 0xffff + movk ip0, 85 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl va_newDisplayContext + .p2align 4 + .type va_newDisplayContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden va_newDisplayContext +#endif +va_newDisplayContext: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+688 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+688] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 86 & 0xffff +#if 86 > 0xffff + movk ip0, 86 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl va_newDriverContext + .p2align 4 + .type va_newDriverContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden va_newDriverContext +#endif +va_newDriverContext: + .cfi_startproc + +1: + // Load address + // TODO: can we do this faster on newer ARMs? + adrp ip0, _libva_so_tramp_table+696 + ldr ip0, [ip0, #:lo12:_libva_so_tramp_table+696] + + cbz ip0, 2f + + // Fast path + br ip0 + +2: + // Slow path + mov ip0, 87 & 0xffff +#if 87 > 0xffff + movk ip0, 87 >> 16, lsl #16 +#endif + stp ip0, lr, [sp, #-16]!; .cfi_adjust_cfa_offset 16; .cfi_rel_offset lr, 8 + bl _libva_so_save_regs_and_resolve + ldp xzr, lr, [sp], #16; .cfi_adjust_cfa_offset -16; .cfi_restore lr + br ip0 + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/x86_64-linux-gnu/libva-drm.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/x86_64-linux-gnu/libva-drm.so.init.c new file mode 100644 index 0000000..b9df56f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/x86_64-linux-gnu/libva-drm.so.init.c @@ -0,0 +1,245 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libva-drm.so.2: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libva-drm.so.2"); + CHECK(lib_handle, "failed to load library 'libva-drm.so.2' via callback ''"); +#else + lib_handle = dlopen("libva-drm.so.2", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libva-drm.so.2' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "vaGetDisplayDRM", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libva_drm_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libva_drm_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libva_drm_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libva_drm_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libva_drm_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libva_drm_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libva_drm_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libva_drm_so_tramp_table, 0, SYM_COUNT * sizeof(_libva_drm_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/x86_64-linux-gnu/libva-drm.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/x86_64-linux-gnu/libva-drm.so.tramp.S new file mode 100644 index 0000000..ba19e20 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/x86_64-linux-gnu/libva-drm.so.tramp.S @@ -0,0 +1,192 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libva_drm_so_tramp_table + .hidden _libva_drm_so_tramp_table + .align 8 +_libva_drm_so_tramp_table: + .zero 16 + + .text + + .globl _libva_drm_so_tramp_resolve + .hidden _libva_drm_so_tramp_resolve + + .globl _libva_drm_so_save_regs_and_resolve + .hidden _libva_drm_so_save_regs_and_resolve + .type _libva_drm_so_save_regs_and_resolve, %function +_libva_drm_so_save_regs_and_resolve: + .cfi_startproc + +#define PUSH_REG(reg) pushq %reg ; .cfi_adjust_cfa_offset 8; .cfi_rel_offset reg, 0 +#define POP_REG(reg) popq %reg ; .cfi_adjust_cfa_offset -8; .cfi_restore reg + +#define DEC_STACK(d) subq $d, %rsp; .cfi_adjust_cfa_offset d +#define INC_STACK(d) addq $d, %rsp; .cfi_adjust_cfa_offset -d + +#define PUSH_MMX_REG(reg) DEC_STACK(8); movq %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_MMX_REG(reg) movq (%rsp), %reg; .cfi_restore reg; INC_STACK(8) + +#define PUSH_XMM_REG(reg) DEC_STACK(16); movdqa %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_XMM_REG(reg) movdqa (%rsp), %reg; .cfi_restore reg; INC_STACK(16) + +// TODO: cfi_offset/cfi_restore +#define PUSH_YMM_REG(reg) DEC_STACK(32); vmovdqu %reg, (%rsp) +#define POP_YMM_REG(reg) vmovdqu (%rsp), %reg; INC_STACK(32) + +// TODO: cfi_offset/cfi_restore +#define PUSH_ZMM_REG(reg) DEC_STACK(64); vmovdqu32 %reg, (%rsp) +#define POP_ZMM_REG(reg) vmovdqu32 (%rsp), %reg; INC_STACK(64) + + // Slow path which calls dlsym, taken only on first call. + // All registers are stored to handle arbitrary calling conventions + // (except x87 FPU registers which do not have to be preserved). + // For Dwarf directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + .cfi_def_cfa_offset 8 // Return address + + PUSH_REG(rdi) // 16 + mov 0x10(%rsp), %rdi + PUSH_REG(rbx) + PUSH_REG(rbx) // 16 + PUSH_REG(rcx) + PUSH_REG(rdx) // 16 + PUSH_REG(rbp) + PUSH_REG(rsi) // 16 + PUSH_REG(r8) + PUSH_REG(r9) // 16 + PUSH_REG(r10) + PUSH_REG(r11) // 16 + PUSH_REG(r12) + PUSH_REG(r13) // 16 + PUSH_REG(r14) + PUSH_REG(r15) // 16 + + // Maybe use cpuid instead of macro to detect current vector size... +#ifdef __AVX512F__ + PUSH_ZMM_REG(zmm0) + PUSH_ZMM_REG(zmm1) + PUSH_ZMM_REG(zmm2) + PUSH_ZMM_REG(zmm3) + PUSH_ZMM_REG(zmm4) + PUSH_ZMM_REG(zmm5) + PUSH_ZMM_REG(zmm6) + PUSH_ZMM_REG(zmm7) +#elif defined __AVX__ + PUSH_YMM_REG(ymm0) + PUSH_YMM_REG(ymm1) + PUSH_YMM_REG(ymm2) + PUSH_YMM_REG(ymm3) + PUSH_YMM_REG(ymm4) + PUSH_YMM_REG(ymm5) + PUSH_YMM_REG(ymm6) + PUSH_YMM_REG(ymm7) +#elif defined __SSE__ + PUSH_XMM_REG(xmm0) + PUSH_XMM_REG(xmm1) + PUSH_XMM_REG(xmm2) + PUSH_XMM_REG(xmm3) + PUSH_XMM_REG(xmm4) + PUSH_XMM_REG(xmm5) + PUSH_XMM_REG(xmm6) + PUSH_XMM_REG(xmm7) +#endif + + // MMX registers are not used to pass arguments so we do not save them + + // Stack is just 8-byte aligned but callee will re-align to 16 + call _libva_drm_so_tramp_resolve + +#ifdef __AVX512F__ + POP_ZMM_REG(zmm7) + POP_ZMM_REG(zmm6) + POP_ZMM_REG(zmm5) + POP_ZMM_REG(zmm4) + POP_ZMM_REG(zmm3) + POP_ZMM_REG(zmm2) + POP_ZMM_REG(zmm1) + POP_ZMM_REG(zmm0) // 16 +#elif defined __AVX__ + POP_YMM_REG(ymm7) + POP_YMM_REG(ymm6) + POP_YMM_REG(ymm5) + POP_YMM_REG(ymm4) + POP_YMM_REG(ymm3) + POP_YMM_REG(ymm2) + POP_YMM_REG(ymm1) + POP_YMM_REG(ymm0) // 16 +#elif defined __SSE__ + POP_XMM_REG(xmm7) + POP_XMM_REG(xmm6) + POP_XMM_REG(xmm5) + POP_XMM_REG(xmm4) + POP_XMM_REG(xmm3) + POP_XMM_REG(xmm2) + POP_XMM_REG(xmm1) + POP_XMM_REG(xmm0) // 16 +#endif + + POP_REG(r15) + POP_REG(r14) // 16 + POP_REG(r13) + POP_REG(r12) // 16 + POP_REG(r11) + POP_REG(r10) // 16 + POP_REG(r9) + POP_REG(r8) // 16 + POP_REG(rsi) + POP_REG(rbp) // 16 + POP_REG(rdx) + POP_REG(rcx) // 16 + POP_REG(rbx) + POP_REG(rbx) // 16 + POP_REG(rdi) + + ret + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaGetDisplayDRM + .p2align 4 + .type vaGetDisplayDRM, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaGetDisplayDRM +#endif +vaGetDisplayDRM: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_drm_so_tramp_table+0(%rip) + je 2f +1: + jmp *_libva_drm_so_tramp_table+0(%rip) +2: + pushq $0 + .cfi_adjust_cfa_offset 8 + call _libva_drm_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/x86_64-linux-gnu/libva.so.init.c b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/x86_64-linux-gnu/libva.so.init.c new file mode 100644 index 0000000..6cd09a9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/x86_64-linux-gnu/libva.so.init.c @@ -0,0 +1,333 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE // For RTLD_DEFAULT +#endif + +#define HAS_DLOPEN_CALLBACK 0 +#define HAS_DLSYM_CALLBACK 0 +#define NO_DLOPEN 0 +#define LAZY_LOAD 1 +#define THREAD_SAFE 1 + +#include +#include +#include +#include +#include + +#if THREAD_SAFE +#include +#endif + +// Sanity check for ARM to avoid puzzling runtime crashes +#ifdef __arm__ +# if defined __thumb__ && ! defined __THUMB_INTERWORK__ +# error "ARM trampolines need -mthumb-interwork to work in Thumb mode" +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#define CHECK(cond, fmt, ...) do { \ + if(!(cond)) { \ + fprintf(stderr, "implib-gen: libva.so.2: " fmt "\n", ##__VA_ARGS__); \ + assert(0 && "Assertion in generated code"); \ + abort(); \ + } \ + } while(0) + +static void *lib_handle; +static int dlopened; + +#if ! NO_DLOPEN + +#if THREAD_SAFE + +// We need to consider two cases: +// - different threads calling intercepted APIs in parallel +// - same thread calling 2 intercepted APIs recursively +// due to dlopen calling library constructors +// (usually happens only under IMPLIB_EXPORT_SHIMS) + +// Current recursive mutex approach will deadlock +// if library constructor starts and joins a new thread +// which (directly or indirectly) calls another library function. +// Such situations should be very rare (although chances +// are higher when -DIMLIB_EXPORT_SHIMS are enabled). +// +// Similar issue is present in Glibc so hopefully it's +// not a big deal: // http://sourceware.org/bugzilla/show_bug.cgi?id=15686 +// (also google for "dlopen deadlock). + +static pthread_mutex_t mtx; +static int rec_count; + +static void init_lock(void) { + // We need recursive lock because dlopen will call library constructors + // which may call other intercepted APIs that will call load_library again. + // PTHREAD_RECURSIVE_MUTEX_INITIALIZER is not portable + // so we do it hard way. + + pthread_mutexattr_t attr; + CHECK(0 == pthread_mutexattr_init(&attr), "failed to init mutex"); + CHECK(0 == pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE), "failed to init mutex"); + + CHECK(0 == pthread_mutex_init(&mtx, &attr), "failed to init mutex"); +} + +static int lock(void) { + static pthread_once_t once = PTHREAD_ONCE_INIT; + CHECK(0 == pthread_once(&once, init_lock), "failed to init lock"); + + CHECK(0 == pthread_mutex_lock(&mtx), "failed to lock mutex"); + + return 0 == __sync_fetch_and_add(&rec_count, 1); +} + +static void unlock(void) { + __sync_fetch_and_add(&rec_count, -1); + CHECK(0 == pthread_mutex_unlock(&mtx), "failed to unlock mutex"); +} +#else +static int lock(void) { + return 1; +} +static void unlock(void) {} +#endif + +static int load_library(void) { + int publish = lock(); + + if (lib_handle) { + unlock(); + return publish; + } + +#if HAS_DLOPEN_CALLBACK + extern void *(const char *lib_name); + lib_handle = ("libva.so.2"); + CHECK(lib_handle, "failed to load library 'libva.so.2' via callback ''"); +#else + lib_handle = dlopen("libva.so.2", RTLD_LAZY | RTLD_GLOBAL); + CHECK(lib_handle, "failed to load library 'libva.so.2' via dlopen: %s", dlerror()); +#endif + + // With (non-default) IMPLIB_EXPORT_SHIMS we may call dlopen more than once + // so dlclose it if we are not the first ones + if (__sync_val_compare_and_swap(&dlopened, 0, 1)) { + dlclose(lib_handle); + } + + unlock(); + + return publish; +} + +// Run dtor as late as possible in case library functions are +// called in other global dtors +// FIXME: this may crash if one thread is calling into library +// while some other thread executes exit(). It's no clear +// how to fix this besides simply NOT dlclosing library at all. +static void __attribute__((destructor(101))) unload_lib(void) { + if (dlopened) { + dlclose(lib_handle); + lib_handle = 0; + dlopened = 0; + } +} +#endif + +#if ! NO_DLOPEN && ! LAZY_LOAD +static void __attribute__((constructor(101))) load_lib(void) { + load_library(); +} +#endif + +// TODO: convert to single 0-separated string +static const char *const sym_names[] = { + "disabled_va_TraceInit", + "vaAcquireBufferHandle", + "vaAssociateSubpicture", + "vaAttachProtectedSession", + "vaBeginPicture", + "vaBufferInfo", + "vaBufferSetNumElements", + "vaBufferTypeStr", + "vaConfigAttribTypeStr", + "vaCopy", + "vaCreateBuffer", + "vaCreateBuffer2", + "vaCreateConfig", + "vaCreateContext", + "vaCreateImage", + "vaCreateMFContext", + "vaCreateProtectedSession", + "vaCreateSubpicture", + "vaCreateSurfaces", + "vaDeassociateSubpicture", + "vaDeriveImage", + "vaDestroyBuffer", + "vaDestroyConfig", + "vaDestroyContext", + "vaDestroyImage", + "vaDestroyProtectedSession", + "vaDestroySubpicture", + "vaDestroySurfaces", + "vaDetachProtectedSession", + "vaDisplayIsValid", + "vaEndPicture", + "vaEntrypointStr", + "vaErrorStr", + "vaExportSurfaceHandle", + "vaGetConfigAttributes", + "vaGetDisplayAttributes", + "vaGetImage", + "vaGetLibFunc", + "vaInitialize", + "vaLockSurface", + "vaMFAddContext", + "vaMFReleaseContext", + "vaMFSubmit", + "vaMapBuffer", + "vaMapBuffer2", + "vaMaxNumConfigAttributes", + "vaMaxNumDisplayAttributes", + "vaMaxNumEntrypoints", + "vaMaxNumImageFormats", + "vaMaxNumProfiles", + "vaMaxNumSubpictureFormats", + "vaProfileStr", + "vaProtectedSessionExecute", + "vaPutImage", + "vaQueryConfigAttributes", + "vaQueryConfigEntrypoints", + "vaQueryConfigProfiles", + "vaQueryDisplayAttributes", + "vaQueryImageFormats", + "vaQueryProcessingRate", + "vaQuerySubpictureFormats", + "vaQuerySurfaceAttributes", + "vaQuerySurfaceError", + "vaQuerySurfaceStatus", + "vaQueryVendorString", + "vaQueryVideoProcFilterCaps", + "vaQueryVideoProcFilters", + "vaQueryVideoProcPipelineCaps", + "vaReleaseBufferHandle", + "vaRenderPicture", + "vaSetDisplayAttributes", + "vaSetDriverName", + "vaSetErrorCallback", + "vaSetImagePalette", + "vaSetInfoCallback", + "vaSetSubpictureChromakey", + "vaSetSubpictureGlobalAlpha", + "vaSetSubpictureImage", + "vaStatusStr", + "vaSyncBuffer", + "vaSyncSurface", + "vaSyncSurface2", + "vaTerminate", + "vaUnlockSurface", + "vaUnmapBuffer", + "va_TracePutSurface", + "va_TraceStatus", + "va_newDisplayContext", + "va_newDriverContext", + 0 +}; + +#define SYM_COUNT (sizeof(sym_names)/sizeof(sym_names[0]) - 1) + +extern void *_libva_so_tramp_table[]; + +// Can be sped up by manually parsing library symtab... +void *_libva_so_tramp_resolve(size_t i) { + assert(i < SYM_COUNT); + + int publish = 1; + + void *h = 0; +#if NO_DLOPEN + // Library with implementations must have already been loaded. + if (lib_handle) { + // User has specified loaded library + h = lib_handle; + } else { + // User hasn't provided us the loaded library so search the global namespace. +# ifndef IMPLIB_EXPORT_SHIMS + // If shim symbols are hidden we should search + // for first available definition of symbol in library list + h = RTLD_DEFAULT; +# else + // Otherwise look for next available definition + h = RTLD_NEXT; +# endif + } +#else + publish = load_library(); + h = lib_handle; + CHECK(h, "failed to resolve symbol '%s', library failed to load", sym_names[i]); +#endif + + void *addr; +#if HAS_DLSYM_CALLBACK + extern void *(void *handle, const char *sym_name); + addr = (h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via callback ", sym_names[i]); +#else + // Dlsym is thread-safe so don't need to protect it. + addr = dlsym(h, sym_names[i]); + CHECK(addr, "failed to resolve symbol '%s' via dlsym: %s", sym_names[i], dlerror()); +#endif + + if (publish) { + // Use atomic to please Tsan and ensure that preceeding writes + // in library ctors have been delivered before publishing address + (void)__sync_val_compare_and_swap(&_libva_so_tramp_table[i], 0, addr); + } + + return addr; +} + +// Below APIs are not thread-safe +// and it's not clear how make them such +// (we can not know if some other thread is +// currently executing library code). + +// Helper for user to resolve all symbols +void _libva_so_tramp_resolve_all(void) { + size_t i; + for(i = 0; i < SYM_COUNT; ++i) + _libva_so_tramp_resolve(i); +} + +// Allows user to specify manually loaded implementation library. +void _libva_so_tramp_set_handle(void *handle) { + // TODO: call unload_lib ? + lib_handle = handle; + dlopened = 0; +} + +// Resets all resolved symbols. This is needed in case +// client code wants to reload interposed library multiple times. +void _libva_so_tramp_reset(void) { + // TODO: call unload_lib ? + memset(_libva_so_tramp_table, 0, SYM_COUNT * sizeof(_libva_so_tramp_table[0])); + lib_handle = 0; + dlopened = 0; +} + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/x86_64-linux-gnu/libva.so.tramp.S b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/x86_64-linux-gnu/libva.so.tramp.S new file mode 100644 index 0000000..4c7f154 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lazy_load_deps_for/vaapi/x86_64-linux-gnu/libva.so.tramp.S @@ -0,0 +1,3184 @@ +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .section .note.GNU-stack,"",@progbits + + .data + + .globl _libva_so_tramp_table + .hidden _libva_so_tramp_table + .align 8 +_libva_so_tramp_table: + .zero 720 + + .text + + .globl _libva_so_tramp_resolve + .hidden _libva_so_tramp_resolve + + .globl _libva_so_save_regs_and_resolve + .hidden _libva_so_save_regs_and_resolve + .type _libva_so_save_regs_and_resolve, %function +_libva_so_save_regs_and_resolve: + .cfi_startproc + +#define PUSH_REG(reg) pushq %reg ; .cfi_adjust_cfa_offset 8; .cfi_rel_offset reg, 0 +#define POP_REG(reg) popq %reg ; .cfi_adjust_cfa_offset -8; .cfi_restore reg + +#define DEC_STACK(d) subq $d, %rsp; .cfi_adjust_cfa_offset d +#define INC_STACK(d) addq $d, %rsp; .cfi_adjust_cfa_offset -d + +#define PUSH_MMX_REG(reg) DEC_STACK(8); movq %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_MMX_REG(reg) movq (%rsp), %reg; .cfi_restore reg; INC_STACK(8) + +#define PUSH_XMM_REG(reg) DEC_STACK(16); movdqa %reg, (%rsp); .cfi_rel_offset reg, 0 +#define POP_XMM_REG(reg) movdqa (%rsp), %reg; .cfi_restore reg; INC_STACK(16) + +// TODO: cfi_offset/cfi_restore +#define PUSH_YMM_REG(reg) DEC_STACK(32); vmovdqu %reg, (%rsp) +#define POP_YMM_REG(reg) vmovdqu (%rsp), %reg; INC_STACK(32) + +// TODO: cfi_offset/cfi_restore +#define PUSH_ZMM_REG(reg) DEC_STACK(64); vmovdqu32 %reg, (%rsp) +#define POP_ZMM_REG(reg) vmovdqu32 (%rsp), %reg; INC_STACK(64) + + // Slow path which calls dlsym, taken only on first call. + // All registers are stored to handle arbitrary calling conventions + // (except x87 FPU registers which do not have to be preserved). + // For Dwarf directives, read https://www.imperialviolet.org/2017/01/18/cfi.html. + + .cfi_def_cfa_offset 8 // Return address + + PUSH_REG(rdi) // 16 + mov 0x10(%rsp), %rdi + PUSH_REG(rbx) + PUSH_REG(rbx) // 16 + PUSH_REG(rcx) + PUSH_REG(rdx) // 16 + PUSH_REG(rbp) + PUSH_REG(rsi) // 16 + PUSH_REG(r8) + PUSH_REG(r9) // 16 + PUSH_REG(r10) + PUSH_REG(r11) // 16 + PUSH_REG(r12) + PUSH_REG(r13) // 16 + PUSH_REG(r14) + PUSH_REG(r15) // 16 + + // Maybe use cpuid instead of macro to detect current vector size... +#ifdef __AVX512F__ + PUSH_ZMM_REG(zmm0) + PUSH_ZMM_REG(zmm1) + PUSH_ZMM_REG(zmm2) + PUSH_ZMM_REG(zmm3) + PUSH_ZMM_REG(zmm4) + PUSH_ZMM_REG(zmm5) + PUSH_ZMM_REG(zmm6) + PUSH_ZMM_REG(zmm7) +#elif defined __AVX__ + PUSH_YMM_REG(ymm0) + PUSH_YMM_REG(ymm1) + PUSH_YMM_REG(ymm2) + PUSH_YMM_REG(ymm3) + PUSH_YMM_REG(ymm4) + PUSH_YMM_REG(ymm5) + PUSH_YMM_REG(ymm6) + PUSH_YMM_REG(ymm7) +#elif defined __SSE__ + PUSH_XMM_REG(xmm0) + PUSH_XMM_REG(xmm1) + PUSH_XMM_REG(xmm2) + PUSH_XMM_REG(xmm3) + PUSH_XMM_REG(xmm4) + PUSH_XMM_REG(xmm5) + PUSH_XMM_REG(xmm6) + PUSH_XMM_REG(xmm7) +#endif + + // MMX registers are not used to pass arguments so we do not save them + + // Stack is just 8-byte aligned but callee will re-align to 16 + call _libva_so_tramp_resolve + +#ifdef __AVX512F__ + POP_ZMM_REG(zmm7) + POP_ZMM_REG(zmm6) + POP_ZMM_REG(zmm5) + POP_ZMM_REG(zmm4) + POP_ZMM_REG(zmm3) + POP_ZMM_REG(zmm2) + POP_ZMM_REG(zmm1) + POP_ZMM_REG(zmm0) // 16 +#elif defined __AVX__ + POP_YMM_REG(ymm7) + POP_YMM_REG(ymm6) + POP_YMM_REG(ymm5) + POP_YMM_REG(ymm4) + POP_YMM_REG(ymm3) + POP_YMM_REG(ymm2) + POP_YMM_REG(ymm1) + POP_YMM_REG(ymm0) // 16 +#elif defined __SSE__ + POP_XMM_REG(xmm7) + POP_XMM_REG(xmm6) + POP_XMM_REG(xmm5) + POP_XMM_REG(xmm4) + POP_XMM_REG(xmm3) + POP_XMM_REG(xmm2) + POP_XMM_REG(xmm1) + POP_XMM_REG(xmm0) // 16 +#endif + + POP_REG(r15) + POP_REG(r14) // 16 + POP_REG(r13) + POP_REG(r12) // 16 + POP_REG(r11) + POP_REG(r10) // 16 + POP_REG(r9) + POP_REG(r8) // 16 + POP_REG(rsi) + POP_REG(rbp) // 16 + POP_REG(rdx) + POP_REG(rcx) // 16 + POP_REG(rbx) + POP_REG(rbx) // 16 + POP_REG(rdi) + + ret + + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl disabled_va_TraceInit + .p2align 4 + .type disabled_va_TraceInit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden disabled_va_TraceInit +#endif +disabled_va_TraceInit: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+0(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+0(%rip) +2: + pushq $0 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaAcquireBufferHandle + .p2align 4 + .type vaAcquireBufferHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaAcquireBufferHandle +#endif +vaAcquireBufferHandle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+8(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+8(%rip) +2: + pushq $1 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaAssociateSubpicture + .p2align 4 + .type vaAssociateSubpicture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaAssociateSubpicture +#endif +vaAssociateSubpicture: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+16(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+16(%rip) +2: + pushq $2 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaAttachProtectedSession + .p2align 4 + .type vaAttachProtectedSession, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaAttachProtectedSession +#endif +vaAttachProtectedSession: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+24(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+24(%rip) +2: + pushq $3 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaBeginPicture + .p2align 4 + .type vaBeginPicture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaBeginPicture +#endif +vaBeginPicture: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+32(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+32(%rip) +2: + pushq $4 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaBufferInfo + .p2align 4 + .type vaBufferInfo, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaBufferInfo +#endif +vaBufferInfo: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+40(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+40(%rip) +2: + pushq $5 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaBufferSetNumElements + .p2align 4 + .type vaBufferSetNumElements, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaBufferSetNumElements +#endif +vaBufferSetNumElements: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+48(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+48(%rip) +2: + pushq $6 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaBufferTypeStr + .p2align 4 + .type vaBufferTypeStr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaBufferTypeStr +#endif +vaBufferTypeStr: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+56(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+56(%rip) +2: + pushq $7 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaConfigAttribTypeStr + .p2align 4 + .type vaConfigAttribTypeStr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaConfigAttribTypeStr +#endif +vaConfigAttribTypeStr: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+64(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+64(%rip) +2: + pushq $8 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCopy + .p2align 4 + .type vaCopy, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCopy +#endif +vaCopy: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+72(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+72(%rip) +2: + pushq $9 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCreateBuffer + .p2align 4 + .type vaCreateBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCreateBuffer +#endif +vaCreateBuffer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+80(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+80(%rip) +2: + pushq $10 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCreateBuffer2 + .p2align 4 + .type vaCreateBuffer2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCreateBuffer2 +#endif +vaCreateBuffer2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+88(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+88(%rip) +2: + pushq $11 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCreateConfig + .p2align 4 + .type vaCreateConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCreateConfig +#endif +vaCreateConfig: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+96(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+96(%rip) +2: + pushq $12 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCreateContext + .p2align 4 + .type vaCreateContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCreateContext +#endif +vaCreateContext: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+104(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+104(%rip) +2: + pushq $13 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCreateImage + .p2align 4 + .type vaCreateImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCreateImage +#endif +vaCreateImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+112(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+112(%rip) +2: + pushq $14 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCreateMFContext + .p2align 4 + .type vaCreateMFContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCreateMFContext +#endif +vaCreateMFContext: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+120(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+120(%rip) +2: + pushq $15 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCreateProtectedSession + .p2align 4 + .type vaCreateProtectedSession, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCreateProtectedSession +#endif +vaCreateProtectedSession: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+128(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+128(%rip) +2: + pushq $16 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCreateSubpicture + .p2align 4 + .type vaCreateSubpicture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCreateSubpicture +#endif +vaCreateSubpicture: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+136(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+136(%rip) +2: + pushq $17 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaCreateSurfaces + .p2align 4 + .type vaCreateSurfaces, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaCreateSurfaces +#endif +vaCreateSurfaces: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+144(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+144(%rip) +2: + pushq $18 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDeassociateSubpicture + .p2align 4 + .type vaDeassociateSubpicture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDeassociateSubpicture +#endif +vaDeassociateSubpicture: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+152(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+152(%rip) +2: + pushq $19 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDeriveImage + .p2align 4 + .type vaDeriveImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDeriveImage +#endif +vaDeriveImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+160(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+160(%rip) +2: + pushq $20 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDestroyBuffer + .p2align 4 + .type vaDestroyBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDestroyBuffer +#endif +vaDestroyBuffer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+168(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+168(%rip) +2: + pushq $21 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDestroyConfig + .p2align 4 + .type vaDestroyConfig, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDestroyConfig +#endif +vaDestroyConfig: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+176(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+176(%rip) +2: + pushq $22 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDestroyContext + .p2align 4 + .type vaDestroyContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDestroyContext +#endif +vaDestroyContext: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+184(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+184(%rip) +2: + pushq $23 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDestroyImage + .p2align 4 + .type vaDestroyImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDestroyImage +#endif +vaDestroyImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+192(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+192(%rip) +2: + pushq $24 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDestroyProtectedSession + .p2align 4 + .type vaDestroyProtectedSession, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDestroyProtectedSession +#endif +vaDestroyProtectedSession: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+200(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+200(%rip) +2: + pushq $25 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDestroySubpicture + .p2align 4 + .type vaDestroySubpicture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDestroySubpicture +#endif +vaDestroySubpicture: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+208(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+208(%rip) +2: + pushq $26 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDestroySurfaces + .p2align 4 + .type vaDestroySurfaces, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDestroySurfaces +#endif +vaDestroySurfaces: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+216(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+216(%rip) +2: + pushq $27 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDetachProtectedSession + .p2align 4 + .type vaDetachProtectedSession, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDetachProtectedSession +#endif +vaDetachProtectedSession: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+224(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+224(%rip) +2: + pushq $28 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaDisplayIsValid + .p2align 4 + .type vaDisplayIsValid, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaDisplayIsValid +#endif +vaDisplayIsValid: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+232(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+232(%rip) +2: + pushq $29 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaEndPicture + .p2align 4 + .type vaEndPicture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaEndPicture +#endif +vaEndPicture: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+240(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+240(%rip) +2: + pushq $30 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaEntrypointStr + .p2align 4 + .type vaEntrypointStr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaEntrypointStr +#endif +vaEntrypointStr: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+248(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+248(%rip) +2: + pushq $31 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaErrorStr + .p2align 4 + .type vaErrorStr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaErrorStr +#endif +vaErrorStr: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+256(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+256(%rip) +2: + pushq $32 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaExportSurfaceHandle + .p2align 4 + .type vaExportSurfaceHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaExportSurfaceHandle +#endif +vaExportSurfaceHandle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+264(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+264(%rip) +2: + pushq $33 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaGetConfigAttributes + .p2align 4 + .type vaGetConfigAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaGetConfigAttributes +#endif +vaGetConfigAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+272(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+272(%rip) +2: + pushq $34 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaGetDisplayAttributes + .p2align 4 + .type vaGetDisplayAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaGetDisplayAttributes +#endif +vaGetDisplayAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+280(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+280(%rip) +2: + pushq $35 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaGetImage + .p2align 4 + .type vaGetImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaGetImage +#endif +vaGetImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+288(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+288(%rip) +2: + pushq $36 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaGetLibFunc + .p2align 4 + .type vaGetLibFunc, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaGetLibFunc +#endif +vaGetLibFunc: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+296(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+296(%rip) +2: + pushq $37 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaInitialize + .p2align 4 + .type vaInitialize, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaInitialize +#endif +vaInitialize: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+304(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+304(%rip) +2: + pushq $38 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaLockSurface + .p2align 4 + .type vaLockSurface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaLockSurface +#endif +vaLockSurface: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+312(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+312(%rip) +2: + pushq $39 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMFAddContext + .p2align 4 + .type vaMFAddContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMFAddContext +#endif +vaMFAddContext: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+320(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+320(%rip) +2: + pushq $40 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMFReleaseContext + .p2align 4 + .type vaMFReleaseContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMFReleaseContext +#endif +vaMFReleaseContext: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+328(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+328(%rip) +2: + pushq $41 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMFSubmit + .p2align 4 + .type vaMFSubmit, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMFSubmit +#endif +vaMFSubmit: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+336(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+336(%rip) +2: + pushq $42 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMapBuffer + .p2align 4 + .type vaMapBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMapBuffer +#endif +vaMapBuffer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+344(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+344(%rip) +2: + pushq $43 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMapBuffer2 + .p2align 4 + .type vaMapBuffer2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMapBuffer2 +#endif +vaMapBuffer2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+352(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+352(%rip) +2: + pushq $44 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMaxNumConfigAttributes + .p2align 4 + .type vaMaxNumConfigAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMaxNumConfigAttributes +#endif +vaMaxNumConfigAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+360(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+360(%rip) +2: + pushq $45 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMaxNumDisplayAttributes + .p2align 4 + .type vaMaxNumDisplayAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMaxNumDisplayAttributes +#endif +vaMaxNumDisplayAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+368(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+368(%rip) +2: + pushq $46 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMaxNumEntrypoints + .p2align 4 + .type vaMaxNumEntrypoints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMaxNumEntrypoints +#endif +vaMaxNumEntrypoints: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+376(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+376(%rip) +2: + pushq $47 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMaxNumImageFormats + .p2align 4 + .type vaMaxNumImageFormats, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMaxNumImageFormats +#endif +vaMaxNumImageFormats: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+384(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+384(%rip) +2: + pushq $48 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMaxNumProfiles + .p2align 4 + .type vaMaxNumProfiles, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMaxNumProfiles +#endif +vaMaxNumProfiles: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+392(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+392(%rip) +2: + pushq $49 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaMaxNumSubpictureFormats + .p2align 4 + .type vaMaxNumSubpictureFormats, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaMaxNumSubpictureFormats +#endif +vaMaxNumSubpictureFormats: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+400(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+400(%rip) +2: + pushq $50 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaProfileStr + .p2align 4 + .type vaProfileStr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaProfileStr +#endif +vaProfileStr: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+408(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+408(%rip) +2: + pushq $51 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaProtectedSessionExecute + .p2align 4 + .type vaProtectedSessionExecute, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaProtectedSessionExecute +#endif +vaProtectedSessionExecute: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+416(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+416(%rip) +2: + pushq $52 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaPutImage + .p2align 4 + .type vaPutImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaPutImage +#endif +vaPutImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+424(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+424(%rip) +2: + pushq $53 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryConfigAttributes + .p2align 4 + .type vaQueryConfigAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryConfigAttributes +#endif +vaQueryConfigAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+432(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+432(%rip) +2: + pushq $54 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryConfigEntrypoints + .p2align 4 + .type vaQueryConfigEntrypoints, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryConfigEntrypoints +#endif +vaQueryConfigEntrypoints: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+440(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+440(%rip) +2: + pushq $55 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryConfigProfiles + .p2align 4 + .type vaQueryConfigProfiles, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryConfigProfiles +#endif +vaQueryConfigProfiles: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+448(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+448(%rip) +2: + pushq $56 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryDisplayAttributes + .p2align 4 + .type vaQueryDisplayAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryDisplayAttributes +#endif +vaQueryDisplayAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+456(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+456(%rip) +2: + pushq $57 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryImageFormats + .p2align 4 + .type vaQueryImageFormats, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryImageFormats +#endif +vaQueryImageFormats: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+464(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+464(%rip) +2: + pushq $58 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryProcessingRate + .p2align 4 + .type vaQueryProcessingRate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryProcessingRate +#endif +vaQueryProcessingRate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+472(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+472(%rip) +2: + pushq $59 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQuerySubpictureFormats + .p2align 4 + .type vaQuerySubpictureFormats, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQuerySubpictureFormats +#endif +vaQuerySubpictureFormats: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+480(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+480(%rip) +2: + pushq $60 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQuerySurfaceAttributes + .p2align 4 + .type vaQuerySurfaceAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQuerySurfaceAttributes +#endif +vaQuerySurfaceAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+488(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+488(%rip) +2: + pushq $61 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQuerySurfaceError + .p2align 4 + .type vaQuerySurfaceError, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQuerySurfaceError +#endif +vaQuerySurfaceError: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+496(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+496(%rip) +2: + pushq $62 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQuerySurfaceStatus + .p2align 4 + .type vaQuerySurfaceStatus, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQuerySurfaceStatus +#endif +vaQuerySurfaceStatus: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+504(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+504(%rip) +2: + pushq $63 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryVendorString + .p2align 4 + .type vaQueryVendorString, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryVendorString +#endif +vaQueryVendorString: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+512(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+512(%rip) +2: + pushq $64 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryVideoProcFilterCaps + .p2align 4 + .type vaQueryVideoProcFilterCaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryVideoProcFilterCaps +#endif +vaQueryVideoProcFilterCaps: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+520(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+520(%rip) +2: + pushq $65 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryVideoProcFilters + .p2align 4 + .type vaQueryVideoProcFilters, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryVideoProcFilters +#endif +vaQueryVideoProcFilters: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+528(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+528(%rip) +2: + pushq $66 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaQueryVideoProcPipelineCaps + .p2align 4 + .type vaQueryVideoProcPipelineCaps, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaQueryVideoProcPipelineCaps +#endif +vaQueryVideoProcPipelineCaps: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+536(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+536(%rip) +2: + pushq $67 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaReleaseBufferHandle + .p2align 4 + .type vaReleaseBufferHandle, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaReleaseBufferHandle +#endif +vaReleaseBufferHandle: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+544(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+544(%rip) +2: + pushq $68 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaRenderPicture + .p2align 4 + .type vaRenderPicture, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaRenderPicture +#endif +vaRenderPicture: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+552(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+552(%rip) +2: + pushq $69 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSetDisplayAttributes + .p2align 4 + .type vaSetDisplayAttributes, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSetDisplayAttributes +#endif +vaSetDisplayAttributes: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+560(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+560(%rip) +2: + pushq $70 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSetDriverName + .p2align 4 + .type vaSetDriverName, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSetDriverName +#endif +vaSetDriverName: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+568(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+568(%rip) +2: + pushq $71 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSetErrorCallback + .p2align 4 + .type vaSetErrorCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSetErrorCallback +#endif +vaSetErrorCallback: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+576(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+576(%rip) +2: + pushq $72 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSetImagePalette + .p2align 4 + .type vaSetImagePalette, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSetImagePalette +#endif +vaSetImagePalette: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+584(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+584(%rip) +2: + pushq $73 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSetInfoCallback + .p2align 4 + .type vaSetInfoCallback, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSetInfoCallback +#endif +vaSetInfoCallback: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+592(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+592(%rip) +2: + pushq $74 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSetSubpictureChromakey + .p2align 4 + .type vaSetSubpictureChromakey, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSetSubpictureChromakey +#endif +vaSetSubpictureChromakey: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+600(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+600(%rip) +2: + pushq $75 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSetSubpictureGlobalAlpha + .p2align 4 + .type vaSetSubpictureGlobalAlpha, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSetSubpictureGlobalAlpha +#endif +vaSetSubpictureGlobalAlpha: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+608(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+608(%rip) +2: + pushq $76 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSetSubpictureImage + .p2align 4 + .type vaSetSubpictureImage, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSetSubpictureImage +#endif +vaSetSubpictureImage: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+616(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+616(%rip) +2: + pushq $77 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaStatusStr + .p2align 4 + .type vaStatusStr, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaStatusStr +#endif +vaStatusStr: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+624(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+624(%rip) +2: + pushq $78 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSyncBuffer + .p2align 4 + .type vaSyncBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSyncBuffer +#endif +vaSyncBuffer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+632(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+632(%rip) +2: + pushq $79 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSyncSurface + .p2align 4 + .type vaSyncSurface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSyncSurface +#endif +vaSyncSurface: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+640(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+640(%rip) +2: + pushq $80 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaSyncSurface2 + .p2align 4 + .type vaSyncSurface2, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaSyncSurface2 +#endif +vaSyncSurface2: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+648(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+648(%rip) +2: + pushq $81 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaTerminate + .p2align 4 + .type vaTerminate, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaTerminate +#endif +vaTerminate: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+656(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+656(%rip) +2: + pushq $82 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaUnlockSurface + .p2align 4 + .type vaUnlockSurface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaUnlockSurface +#endif +vaUnlockSurface: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+664(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+664(%rip) +2: + pushq $83 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl vaUnmapBuffer + .p2align 4 + .type vaUnmapBuffer, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden vaUnmapBuffer +#endif +vaUnmapBuffer: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+672(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+672(%rip) +2: + pushq $84 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl va_TracePutSurface + .p2align 4 + .type va_TracePutSurface, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden va_TracePutSurface +#endif +va_TracePutSurface: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+680(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+680(%rip) +2: + pushq $85 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl va_TraceStatus + .p2align 4 + .type va_TraceStatus, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden va_TraceStatus +#endif +va_TraceStatus: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+688(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+688(%rip) +2: + pushq $86 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl va_newDisplayContext + .p2align 4 + .type va_newDisplayContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden va_newDisplayContext +#endif +va_newDisplayContext: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+696(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+696(%rip) +2: + pushq $87 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + +/* + * Copyright 2018-2025 Yury Gribov + * + * The MIT License (MIT) + * + * Use of this source code is governed by MIT license that can be + * found in the LICENSE.txt file. + */ + + .globl va_newDriverContext + .p2align 4 + .type va_newDriverContext, %function +#ifndef IMPLIB_EXPORT_SHIMS + .hidden va_newDriverContext +#endif +va_newDriverContext: + .cfi_startproc + .cfi_def_cfa_offset 8 // Return address + // Intel opt. manual says to + // "make the fall-through code following a conditional branch be the likely target for a branch with a forward target" + // to hint static predictor. + cmpq $0, _libva_so_tramp_table+704(%rip) + je 2f +1: + jmp *_libva_so_tramp_table+704(%rip) +2: + pushq $88 + .cfi_adjust_cfa_offset 8 + call _libva_so_save_regs_and_resolve + addq $8, %rsp + .cfi_adjust_cfa_offset -8 + jmp *%rax + .cfi_endproc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lib.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lib.rs new file mode 100644 index 0000000..29f67b5 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/lib.rs @@ -0,0 +1,62 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[cfg(target_os = "android")] +pub mod android; +pub mod apm; +pub mod audio_device_controller; +pub mod audio_mixer; +pub mod audio_resampler; +pub mod audio_track; +pub mod candidate; +pub mod data_channel; +#[cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))] +pub mod desktop_capturer; +pub mod frame_cryptor; +pub mod helper; +pub mod jsep; +pub mod media_stream; +pub mod media_stream_track; +pub mod packet_trailer; +pub mod peer_connection; +pub mod peer_connection_factory; +pub mod prohibit_libsrtp_initialization; +pub mod recorded_audio_tap; +pub mod rtc_error; +pub mod rtp_parameters; +pub mod rtp_receiver; +pub mod rtp_sender; +pub mod rtp_transceiver; +pub mod video_frame; +pub mod video_frame_buffer; +pub mod video_track; +pub mod webrtc; +pub mod yuv_helper; + +pub const MEDIA_TYPE_VIDEO: &str = "video"; +pub const MEDIA_TYPE_AUDIO: &str = "audio"; +pub const MEDIA_TYPE_DATA: &str = "data"; + +macro_rules! impl_thread_safety { + ($obj:ty, Send) => { + unsafe impl Send for $obj {} + }; + + ($obj:ty, Send + Sync) => { + unsafe impl Send for $obj {} + unsafe impl Sync for $obj {} + }; +} + +pub(crate) use impl_thread_safety; diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/media_stream.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/media_stream.cpp new file mode 100644 index 0000000..665b2d1 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/media_stream.cpp @@ -0,0 +1,101 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/media_stream.h" + +#include +#include +#include + +#include "api/media_stream_interface.h" +#include "api/video/video_frame.h" +#include "api/video/video_rotation.h" +#include "audio/remix_resample.h" +#include "common_audio/include/audio_util.h" +#include "rtc_base/logging.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/time_utils.h" + +namespace livekit_ffi { + +MediaStream::MediaStream( + std::shared_ptr rtc_runtime, + webrtc::scoped_refptr stream) + : rtc_runtime_(rtc_runtime), media_stream_(std::move(stream)) {} + +rust::String MediaStream::id() const { + return media_stream_->id(); +} + +rust::Vec MediaStream::get_video_tracks() const { + rust::Vec rust; + for (auto video : media_stream_->GetVideoTracks()) + rust.push_back( + VideoTrackPtr{rtc_runtime_->get_or_create_video_track(video)}); + + return rust; +} + +rust::Vec MediaStream::get_audio_tracks() const { + rust::Vec rust; + for (auto audio : media_stream_->GetAudioTracks()) + rust.push_back( + AudioTrackPtr{rtc_runtime_->get_or_create_audio_track(audio)}); + + return rust; +} + +std::shared_ptr MediaStream::find_audio_track( + rust::String track_id) const { + return rtc_runtime_->get_or_create_audio_track( + media_stream_->FindAudioTrack(track_id.c_str())); +} + +std::shared_ptr MediaStream::find_video_track( + rust::String track_id) const { + return rtc_runtime_->get_or_create_video_track( + media_stream_->FindVideoTrack(track_id.c_str())); +} + +bool MediaStream::add_track(std::shared_ptr track) const { + if (track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) { + return media_stream_->AddTrack( + webrtc::scoped_refptr( + static_cast( + track->rtc_track().get()))); + } else { + return media_stream_->AddTrack( + webrtc::scoped_refptr( + static_cast( + track->rtc_track().get()))); + } +} + +bool MediaStream::remove_track(std::shared_ptr track) const { + if (track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) { + return media_stream_->RemoveTrack( + webrtc::scoped_refptr( + static_cast( + track->rtc_track().get()))); + } else { + return media_stream_->RemoveTrack( + webrtc::scoped_refptr( + static_cast( + track->rtc_track().get()))); + } +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/media_stream.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/media_stream.rs new file mode 100644 index 0000000..db7ffa7 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/media_stream.rs @@ -0,0 +1,49 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::impl_thread_safety; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + extern "C++" { + include!("livekit/helper.h"); + include!("livekit/media_stream_track.h"); + include!("livekit/audio_track.h"); + include!("livekit/video_track.h"); + + type MediaStreamTrack = crate::media_stream_track::ffi::MediaStreamTrack; + type AudioTrack = crate::audio_track::ffi::AudioTrack; + type VideoTrack = crate::video_track::ffi::VideoTrack; + type VideoTrackPtr = crate::helper::ffi::VideoTrackPtr; + type AudioTrackPtr = crate::helper::ffi::AudioTrackPtr; + } + + unsafe extern "C++" { + include!("livekit/media_stream.h"); + + type MediaStream; + + fn id(self: &MediaStream) -> String; + fn get_audio_tracks(self: &MediaStream) -> Vec; + fn get_video_tracks(self: &MediaStream) -> Vec; + fn find_audio_track(self: &MediaStream, track_id: String) -> SharedPtr; + fn find_video_track(self: &MediaStream, track_id: String) -> SharedPtr; + fn add_track(self: &MediaStream, audio_track: SharedPtr) -> bool; + fn remove_track(self: &MediaStream, audio_track: SharedPtr) -> bool; + + fn _shared_media_stream() -> SharedPtr; + } +} + +impl_thread_safety!(ffi::MediaStream, Send + Sync); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/media_stream_track.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/media_stream_track.cpp new file mode 100644 index 0000000..d2197e3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/media_stream_track.cpp @@ -0,0 +1,58 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "api/media_stream_interface.h" +#include "api/video/video_frame.h" +#include "api/video/video_rotation.h" +#include "audio/remix_resample.h" +#include "common_audio/include/audio_util.h" +#include "livekit/media_stream.h" +#include "rtc_base/logging.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/time_utils.h" + +namespace livekit_ffi { + +MediaStreamTrack::MediaStreamTrack( + std::shared_ptr rtc_runtime, + webrtc::scoped_refptr track) + : rtc_runtime_(rtc_runtime), track_(std::move(track)) {} + +rust::String MediaStreamTrack::kind() const { + return track_->kind(); +} + +rust::String MediaStreamTrack::id() const { + return track_->id(); +} + +bool MediaStreamTrack::enabled() const { + return track_->enabled(); +} + +bool MediaStreamTrack::set_enabled(bool enable) const { + return track_->set_enabled(enable); +} + +TrackState MediaStreamTrack::state() const { + return static_cast(track_->state()); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/media_stream_track.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/media_stream_track.rs new file mode 100644 index 0000000..1e7d7d0 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/media_stream_track.rs @@ -0,0 +1,40 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::impl_thread_safety; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + #[repr(i32)] + pub enum TrackState { + Live, + Ended, + } + + unsafe extern "C++" { + include!("livekit/media_stream_track.h"); + + type MediaStreamTrack; + + fn kind(self: &MediaStreamTrack) -> String; + fn id(self: &MediaStreamTrack) -> String; + fn enabled(self: &MediaStreamTrack) -> bool; + fn set_enabled(self: &MediaStreamTrack, enable: bool) -> bool; + fn state(self: &MediaStreamTrack) -> TrackState; + + fn _shared_media_stream_track() -> SharedPtr; + } +} + +impl_thread_safety!(ffi::MediaStreamTrack, Send + Sync); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/NvCodec/NvDecoder/NvDecoder.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/NvCodec/NvDecoder/NvDecoder.cpp new file mode 100644 index 0000000..d0ebde5 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/NvCodec/NvDecoder/NvDecoder.cpp @@ -0,0 +1,850 @@ +/* +* Copyright 2017-2022 NVIDIA Corporation. All rights reserved. +* +* Please refer to the NVIDIA end user license agreement (EULA) associated +* with this source code for terms and conditions that govern your use of +* this software. Any use, reproduction, disclosure, or distribution of +* this software and related documentation outside the terms of the EULA +* is strictly prohibited. +* +*/ + +#include +#include +#include +#include + +#include "nvcuvid.h" +#include "NvDecoder.h" + +#include "Utils/Logger.h" +simplelogger::Logger* logger = simplelogger::LoggerFactory::CreateConsoleLogger(); + +#define START_TIMER auto start = std::chrono::high_resolution_clock::now(); + +#define STOP_TIMER(print_message) int64_t elapsedTime = std::chrono::duration_cast( \ + std::chrono::high_resolution_clock::now() - start).count(); \ + std::cout << print_message << \ + elapsedTime \ + << " ms " << std::endl; + +#define CUDA_DRVAPI_CALL( call ) \ + do \ + { \ + CUresult err__ = call; \ + if (err__ != CUDA_SUCCESS) \ + { \ + const char *szErrName = NULL; \ + cuGetErrorName(err__, &szErrName); \ + std::ostringstream errorLog; \ + errorLog << "CUDA driver API error " << szErrName ; \ + throw NVDECException::makeNVDECException(errorLog.str(), err__, __FUNCTION__, __FILE__, __LINE__); \ + } \ + } \ + while (0) + +static const char * GetVideoCodecString(cudaVideoCodec eCodec) { + static struct { + cudaVideoCodec eCodec; + const char *name; + } aCodecName [] = { + { cudaVideoCodec_MPEG1, "MPEG-1" }, + { cudaVideoCodec_MPEG2, "MPEG-2" }, + { cudaVideoCodec_MPEG4, "MPEG-4 (ASP)" }, + { cudaVideoCodec_VC1, "VC-1/WMV" }, + { cudaVideoCodec_H264, "AVC/H.264" }, + { cudaVideoCodec_JPEG, "M-JPEG" }, + { cudaVideoCodec_H264_SVC, "H.264/SVC" }, + { cudaVideoCodec_H264_MVC, "H.264/MVC" }, + { cudaVideoCodec_HEVC, "H.265/HEVC" }, + { cudaVideoCodec_VP8, "VP8" }, + { cudaVideoCodec_VP9, "VP9" }, + { cudaVideoCodec_AV1, "AV1" }, + { cudaVideoCodec_NumCodecs, "Invalid" }, + { cudaVideoCodec_YUV420, "YUV 4:2:0" }, + { cudaVideoCodec_YV12, "YV12 4:2:0" }, + { cudaVideoCodec_NV12, "NV12 4:2:0" }, + { cudaVideoCodec_YUYV, "YUYV 4:2:2" }, + { cudaVideoCodec_UYVY, "UYVY 4:2:2" }, + }; + + if (eCodec >= 0 && eCodec <= cudaVideoCodec_NumCodecs) { + return aCodecName[eCodec].name; + } + for (int i = cudaVideoCodec_NumCodecs + 1; i < sizeof(aCodecName) / sizeof(aCodecName[0]); i++) { + if (eCodec == aCodecName[i].eCodec) { + return aCodecName[eCodec].name; + } + } + return "Unknown"; +} + +static const char * GetVideoChromaFormatString(cudaVideoChromaFormat eChromaFormat) { + static struct { + cudaVideoChromaFormat eChromaFormat; + const char *name; + } aChromaFormatName[] = { + { cudaVideoChromaFormat_Monochrome, "YUV 400 (Monochrome)" }, + { cudaVideoChromaFormat_420, "YUV 420" }, + { cudaVideoChromaFormat_422, "YUV 422" }, + { cudaVideoChromaFormat_444, "YUV 444" }, + }; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare" + if (eChromaFormat >= 0 && eChromaFormat < sizeof(aChromaFormatName) / sizeof(aChromaFormatName[0])) { + return aChromaFormatName[eChromaFormat].name; + } +#pragma clang diagnostic pop + return "Unknown"; +} + +static float GetChromaHeightFactor(cudaVideoSurfaceFormat eSurfaceFormat) +{ + float factor = 0.5; + switch (eSurfaceFormat) + { + case cudaVideoSurfaceFormat_NV12: + case cudaVideoSurfaceFormat_P016: + factor = 0.5; + break; + case cudaVideoSurfaceFormat_YUV444: + case cudaVideoSurfaceFormat_YUV444_16Bit: + factor = 1.0; + break; + } + + return factor; +} + +static int GetChromaPlaneCount(cudaVideoSurfaceFormat eSurfaceFormat) +{ + int numPlane = 1; + switch (eSurfaceFormat) + { + case cudaVideoSurfaceFormat_NV12: + case cudaVideoSurfaceFormat_P016: + numPlane = 1; + break; + case cudaVideoSurfaceFormat_YUV444: + case cudaVideoSurfaceFormat_YUV444_16Bit: + numPlane = 2; + break; + } + + return numPlane; +} + +std::map NvDecoder::sessionOverHead = { {0,0}, {1,0} }; + +/** +* @brief This function is used to get codec string from codec id +*/ +const char *NvDecoder::GetCodecString(cudaVideoCodec eCodec) +{ + return GetVideoCodecString(eCodec); +} + +/* Called when the parser encounters sequence header for AV1 SVC content +* return value interpretation: +* < 0 : fail, >=0: succeeded (bit 0-9: currOperatingPoint, bit 10-10: bDispAllLayer, bit 11-30: reserved, must be set 0) +*/ +int NvDecoder::GetOperatingPoint(CUVIDOPERATINGPOINTINFO *pOPInfo) +{ + if (pOPInfo->codec == cudaVideoCodec_AV1) + { + if (pOPInfo->av1.operating_points_cnt > 1) + { + // clip has SVC enabled + if (m_nOperatingPoint >= pOPInfo->av1.operating_points_cnt) + m_nOperatingPoint = 0; + + printf("AV1 SVC clip: operating point count %d ", pOPInfo->av1.operating_points_cnt); + printf("Selected operating point: %d, IDC 0x%x bOutputAllLayers %d\n", m_nOperatingPoint, pOPInfo->av1.operating_points_idc[m_nOperatingPoint], m_bDispAllLayers); + return (m_nOperatingPoint | (m_bDispAllLayers << 10)); + } + } + return -1; +} + +/* Return value from HandleVideoSequence() are interpreted as : +* 0: fail, 1: succeeded, > 1: override dpb size of parser (set by CUVIDPARSERPARAMS::ulMaxNumDecodeSurfaces while creating parser) +*/ +int NvDecoder::HandleVideoSequence(CUVIDEOFORMAT *pVideoFormat) +{ + START_TIMER + m_videoInfo.str(""); + m_videoInfo.clear(); + m_videoInfo << "Video Input Information" << std::endl + << "\tCodec : " << GetVideoCodecString(pVideoFormat->codec) << std::endl + << "\tFrame rate : " << pVideoFormat->frame_rate.numerator << "/" << pVideoFormat->frame_rate.denominator + << " = " << 1.0 * pVideoFormat->frame_rate.numerator / pVideoFormat->frame_rate.denominator << " fps" << std::endl + << "\tSequence : " << (pVideoFormat->progressive_sequence ? "Progressive" : "Interlaced") << std::endl + << "\tCoded size : [" << pVideoFormat->coded_width << ", " << pVideoFormat->coded_height << "]" << std::endl + << "\tDisplay area : [" << pVideoFormat->display_area.left << ", " << pVideoFormat->display_area.top << ", " + << pVideoFormat->display_area.right << ", " << pVideoFormat->display_area.bottom << "]" << std::endl + << "\tChroma : " << GetVideoChromaFormatString(pVideoFormat->chroma_format) << std::endl + << "\tBit depth : " << pVideoFormat->bit_depth_luma_minus8 + 8 + ; + m_videoInfo << std::endl; + + int nDecodeSurface = pVideoFormat->min_num_decode_surfaces; + + CUVIDDECODECAPS decodecaps; + memset(&decodecaps, 0, sizeof(decodecaps)); + + decodecaps.eCodecType = pVideoFormat->codec; + decodecaps.eChromaFormat = pVideoFormat->chroma_format; + decodecaps.nBitDepthMinus8 = pVideoFormat->bit_depth_luma_minus8; + + CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext)); + NVDEC_API_CALL(cuvidGetDecoderCaps(&decodecaps)); + CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL)); + + if(!decodecaps.bIsSupported){ + NVDEC_THROW_ERROR("Codec not supported on this GPU", CUDA_ERROR_NOT_SUPPORTED); + return nDecodeSurface; + } + + if ((pVideoFormat->coded_width > decodecaps.nMaxWidth) || + (pVideoFormat->coded_height > decodecaps.nMaxHeight)){ + + std::ostringstream errorString; + errorString << std::endl + << "Resolution : " << pVideoFormat->coded_width << "x" << pVideoFormat->coded_height << std::endl + << "Max Supported (wxh) : " << decodecaps.nMaxWidth << "x" << decodecaps.nMaxHeight << std::endl + << "Resolution not supported on this GPU"; + + const std::string cErr = errorString.str(); + NVDEC_THROW_ERROR(cErr, CUDA_ERROR_NOT_SUPPORTED); + return nDecodeSurface; + } + + if ((pVideoFormat->coded_width>>4)*(pVideoFormat->coded_height>>4) > decodecaps.nMaxMBCount){ + + std::ostringstream errorString; + errorString << std::endl + << "MBCount : " << (pVideoFormat->coded_width >> 4)*(pVideoFormat->coded_height >> 4) << std::endl + << "Max Supported mbcnt : " << decodecaps.nMaxMBCount << std::endl + << "MBCount not supported on this GPU"; + + const std::string cErr = errorString.str(); + NVDEC_THROW_ERROR(cErr, CUDA_ERROR_NOT_SUPPORTED); + return nDecodeSurface; + } + + if (m_nWidth && m_nLumaHeight && m_nChromaHeight) { + + // cuvidCreateDecoder() has been called before, and now there's possible config change + return ReconfigureDecoder(pVideoFormat); + } + + // eCodec has been set in the constructor (for parser). Here it's set again for potential correction + m_eCodec = pVideoFormat->codec; + m_eChromaFormat = pVideoFormat->chroma_format; + m_nBitDepthMinus8 = pVideoFormat->bit_depth_luma_minus8; + m_nBPP = m_nBitDepthMinus8 > 0 ? 2 : 1; + + // Set the output surface format same as chroma format + if (m_eChromaFormat == cudaVideoChromaFormat_420 || cudaVideoChromaFormat_Monochrome) + m_eOutputFormat = pVideoFormat->bit_depth_luma_minus8 ? cudaVideoSurfaceFormat_P016 : cudaVideoSurfaceFormat_NV12; + else if (m_eChromaFormat == cudaVideoChromaFormat_444) + m_eOutputFormat = pVideoFormat->bit_depth_luma_minus8 ? cudaVideoSurfaceFormat_YUV444_16Bit : cudaVideoSurfaceFormat_YUV444; + else if (m_eChromaFormat == cudaVideoChromaFormat_422) + m_eOutputFormat = cudaVideoSurfaceFormat_NV12; // no 4:2:2 output format supported yet so make 420 default + + // Check if output format supported. If not, check falback options + if (!(decodecaps.nOutputFormatMask & (1 << m_eOutputFormat))) + { + if (decodecaps.nOutputFormatMask & (1 << cudaVideoSurfaceFormat_NV12)) + m_eOutputFormat = cudaVideoSurfaceFormat_NV12; + else if (decodecaps.nOutputFormatMask & (1 << cudaVideoSurfaceFormat_P016)) + m_eOutputFormat = cudaVideoSurfaceFormat_P016; + else if (decodecaps.nOutputFormatMask & (1 << cudaVideoSurfaceFormat_YUV444)) + m_eOutputFormat = cudaVideoSurfaceFormat_YUV444; + else if (decodecaps.nOutputFormatMask & (1 << cudaVideoSurfaceFormat_YUV444_16Bit)) + m_eOutputFormat = cudaVideoSurfaceFormat_YUV444_16Bit; + else + NVDEC_THROW_ERROR("No supported output format found", CUDA_ERROR_NOT_SUPPORTED); + } + m_videoFormat = *pVideoFormat; + + CUVIDDECODECREATEINFO videoDecodeCreateInfo = { 0 }; + videoDecodeCreateInfo.CodecType = pVideoFormat->codec; + videoDecodeCreateInfo.ChromaFormat = pVideoFormat->chroma_format; + videoDecodeCreateInfo.OutputFormat = m_eOutputFormat; + videoDecodeCreateInfo.bitDepthMinus8 = pVideoFormat->bit_depth_luma_minus8; + if (pVideoFormat->progressive_sequence) + videoDecodeCreateInfo.DeinterlaceMode = cudaVideoDeinterlaceMode_Weave; + else + videoDecodeCreateInfo.DeinterlaceMode = cudaVideoDeinterlaceMode_Adaptive; + videoDecodeCreateInfo.ulNumOutputSurfaces = 2; + // With PreferCUVID, JPEG is still decoded by CUDA while video is decoded by NVDEC hardware + videoDecodeCreateInfo.ulCreationFlags = cudaVideoCreate_PreferCUVID; + videoDecodeCreateInfo.ulNumDecodeSurfaces = nDecodeSurface; + videoDecodeCreateInfo.vidLock = m_ctxLock; + videoDecodeCreateInfo.ulWidth = pVideoFormat->coded_width; + videoDecodeCreateInfo.ulHeight = pVideoFormat->coded_height; + // AV1 has max width/height of sequence in sequence header + if (pVideoFormat->codec == cudaVideoCodec_AV1 && pVideoFormat->seqhdr_data_length > 0) + { + // dont overwrite if it is already set from cmdline or reconfig.txt + if (!(m_nMaxWidth > pVideoFormat->coded_width || m_nMaxHeight > pVideoFormat->coded_height)) + { + CUVIDEOFORMATEX *vidFormatEx = (CUVIDEOFORMATEX *)pVideoFormat; + m_nMaxWidth = vidFormatEx->av1.max_width; + m_nMaxHeight = vidFormatEx->av1.max_height; + } + } + if (m_nMaxWidth < (int)pVideoFormat->coded_width) + m_nMaxWidth = pVideoFormat->coded_width; + if (m_nMaxHeight < (int)pVideoFormat->coded_height) + m_nMaxHeight = pVideoFormat->coded_height; + videoDecodeCreateInfo.ulMaxWidth = m_nMaxWidth; + videoDecodeCreateInfo.ulMaxHeight = m_nMaxHeight; + + if (!(m_cropRect.r && m_cropRect.b) && !(m_resizeDim.w && m_resizeDim.h)) { + m_nWidth = pVideoFormat->display_area.right - pVideoFormat->display_area.left; + m_nLumaHeight = pVideoFormat->display_area.bottom - pVideoFormat->display_area.top; + videoDecodeCreateInfo.ulTargetWidth = pVideoFormat->coded_width; + videoDecodeCreateInfo.ulTargetHeight = pVideoFormat->coded_height; + } else { + if (m_resizeDim.w && m_resizeDim.h) { + videoDecodeCreateInfo.display_area.left = pVideoFormat->display_area.left; + videoDecodeCreateInfo.display_area.top = pVideoFormat->display_area.top; + videoDecodeCreateInfo.display_area.right = pVideoFormat->display_area.right; + videoDecodeCreateInfo.display_area.bottom = pVideoFormat->display_area.bottom; + m_nWidth = m_resizeDim.w; + m_nLumaHeight = m_resizeDim.h; + } + + if (m_cropRect.r && m_cropRect.b) { + videoDecodeCreateInfo.display_area.left = m_cropRect.l; + videoDecodeCreateInfo.display_area.top = m_cropRect.t; + videoDecodeCreateInfo.display_area.right = m_cropRect.r; + videoDecodeCreateInfo.display_area.bottom = m_cropRect.b; + m_nWidth = m_cropRect.r - m_cropRect.l; + m_nLumaHeight = m_cropRect.b - m_cropRect.t; + } + videoDecodeCreateInfo.ulTargetWidth = m_nWidth; + videoDecodeCreateInfo.ulTargetHeight = m_nLumaHeight; + } + + m_nChromaHeight = (int)(ceil(m_nLumaHeight * GetChromaHeightFactor(m_eOutputFormat))); + m_nNumChromaPlanes = GetChromaPlaneCount(m_eOutputFormat); + m_nSurfaceHeight = videoDecodeCreateInfo.ulTargetHeight; + m_nSurfaceWidth = videoDecodeCreateInfo.ulTargetWidth; + m_displayRect.b = videoDecodeCreateInfo.display_area.bottom; + m_displayRect.t = videoDecodeCreateInfo.display_area.top; + m_displayRect.l = videoDecodeCreateInfo.display_area.left; + m_displayRect.r = videoDecodeCreateInfo.display_area.right; + + m_videoInfo << "Video Decoding Params:" << std::endl + << "\tNum Surfaces : " << videoDecodeCreateInfo.ulNumDecodeSurfaces << std::endl + << "\tCrop : [" << videoDecodeCreateInfo.display_area.left << ", " << videoDecodeCreateInfo.display_area.top << ", " + << videoDecodeCreateInfo.display_area.right << ", " << videoDecodeCreateInfo.display_area.bottom << "]" << std::endl + << "\tResize : " << videoDecodeCreateInfo.ulTargetWidth << "x" << videoDecodeCreateInfo.ulTargetHeight << std::endl + << "\tDeinterlace : " << std::vector{"Weave", "Bob", "Adaptive"}[videoDecodeCreateInfo.DeinterlaceMode] + ; + m_videoInfo << std::endl; + + CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext)); + NVDEC_API_CALL(cuvidCreateDecoder(&m_hDecoder, &videoDecodeCreateInfo)); + CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL)); + STOP_TIMER("Session Initialization Time: "); + NvDecoder::addDecoderSessionOverHead(getDecoderSessionID(), elapsedTime); + return nDecodeSurface; +} + +int NvDecoder::ReconfigureDecoder(CUVIDEOFORMAT *pVideoFormat) +{ + if (pVideoFormat->bit_depth_luma_minus8 != m_videoFormat.bit_depth_luma_minus8 || pVideoFormat->bit_depth_chroma_minus8 != m_videoFormat.bit_depth_chroma_minus8){ + + NVDEC_THROW_ERROR("Reconfigure Not supported for bit depth change", CUDA_ERROR_NOT_SUPPORTED); + } + + if (pVideoFormat->chroma_format != m_videoFormat.chroma_format) { + + NVDEC_THROW_ERROR("Reconfigure Not supported for chroma format change", CUDA_ERROR_NOT_SUPPORTED); + } + + bool bDecodeResChange = !(pVideoFormat->coded_width == m_videoFormat.coded_width && pVideoFormat->coded_height == m_videoFormat.coded_height); + bool bDisplayRectChange = !(pVideoFormat->display_area.bottom == m_videoFormat.display_area.bottom && pVideoFormat->display_area.top == m_videoFormat.display_area.top \ + && pVideoFormat->display_area.left == m_videoFormat.display_area.left && pVideoFormat->display_area.right == m_videoFormat.display_area.right); + + int nDecodeSurface = pVideoFormat->min_num_decode_surfaces; + + if ((pVideoFormat->coded_width > m_nMaxWidth) || (pVideoFormat->coded_height > m_nMaxHeight)) { + // For VP9, let driver handle the change if new width/height > maxwidth/maxheight + if ((m_eCodec != cudaVideoCodec_VP9) || m_bReconfigExternal) + { + NVDEC_THROW_ERROR("Reconfigure Not supported when width/height > maxwidth/maxheight", CUDA_ERROR_NOT_SUPPORTED); + } + return 1; + } + + if (!bDecodeResChange && !m_bReconfigExtPPChange) { + // if the coded_width/coded_height hasn't changed but display resolution has changed, then need to update width/height for + // correct output without cropping. Example : 1920x1080 vs 1920x1088 + if (bDisplayRectChange) + { + m_nWidth = pVideoFormat->display_area.right - pVideoFormat->display_area.left; + m_nLumaHeight = pVideoFormat->display_area.bottom - pVideoFormat->display_area.top; + m_nChromaHeight = (int)ceil(m_nLumaHeight * GetChromaHeightFactor(m_eOutputFormat)); + m_nNumChromaPlanes = GetChromaPlaneCount(m_eOutputFormat); + } + + // no need for reconfigureDecoder(). Just return + return 1; + } + + CUVIDRECONFIGUREDECODERINFO reconfigParams = { 0 }; + + reconfigParams.ulWidth = m_videoFormat.coded_width = pVideoFormat->coded_width; + reconfigParams.ulHeight = m_videoFormat.coded_height = pVideoFormat->coded_height; + + // Dont change display rect and get scaled output from decoder. This will help display app to present apps smoothly + reconfigParams.display_area.bottom = m_displayRect.b; + reconfigParams.display_area.top = m_displayRect.t; + reconfigParams.display_area.left = m_displayRect.l; + reconfigParams.display_area.right = m_displayRect.r; + reconfigParams.ulTargetWidth = m_nSurfaceWidth; + reconfigParams.ulTargetHeight = m_nSurfaceHeight; + + // If external reconfigure is called along with resolution change even if post processing params is not changed, + // do full reconfigure params update + if ((m_bReconfigExternal && bDecodeResChange) || m_bReconfigExtPPChange) { + // update display rect and target resolution if requested explicitely + m_bReconfigExternal = false; + m_bReconfigExtPPChange = false; + m_videoFormat = *pVideoFormat; + if (!(m_cropRect.r && m_cropRect.b) && !(m_resizeDim.w && m_resizeDim.h)) { + m_nWidth = pVideoFormat->display_area.right - pVideoFormat->display_area.left; + m_nLumaHeight = pVideoFormat->display_area.bottom - pVideoFormat->display_area.top; + reconfigParams.ulTargetWidth = pVideoFormat->coded_width; + reconfigParams.ulTargetHeight = pVideoFormat->coded_height; + } + else { + if (m_resizeDim.w && m_resizeDim.h) { + reconfigParams.display_area.left = pVideoFormat->display_area.left; + reconfigParams.display_area.top = pVideoFormat->display_area.top; + reconfigParams.display_area.right = pVideoFormat->display_area.right; + reconfigParams.display_area.bottom = pVideoFormat->display_area.bottom; + m_nWidth = m_resizeDim.w; + m_nLumaHeight = m_resizeDim.h; + } + + if (m_cropRect.r && m_cropRect.b) { + reconfigParams.display_area.left = m_cropRect.l; + reconfigParams.display_area.top = m_cropRect.t; + reconfigParams.display_area.right = m_cropRect.r; + reconfigParams.display_area.bottom = m_cropRect.b; + m_nWidth = m_cropRect.r - m_cropRect.l; + m_nLumaHeight = m_cropRect.b - m_cropRect.t; + } + reconfigParams.ulTargetWidth = m_nWidth; + reconfigParams.ulTargetHeight = m_nLumaHeight; + } + + m_nChromaHeight = (int)ceil(m_nLumaHeight * GetChromaHeightFactor(m_eOutputFormat)); + m_nNumChromaPlanes = GetChromaPlaneCount(m_eOutputFormat); + m_nSurfaceHeight = reconfigParams.ulTargetHeight; + m_nSurfaceWidth = reconfigParams.ulTargetWidth; + m_displayRect.b = reconfigParams.display_area.bottom; + m_displayRect.t = reconfigParams.display_area.top; + m_displayRect.l = reconfigParams.display_area.left; + m_displayRect.r = reconfigParams.display_area.right; + } + + reconfigParams.ulNumDecodeSurfaces = nDecodeSurface; + + START_TIMER + CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext)); + NVDEC_API_CALL(cuvidReconfigureDecoder(m_hDecoder, &reconfigParams)); + CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL)); + STOP_TIMER("Session Reconfigure Time: "); + + return nDecodeSurface; +} + +int NvDecoder::setReconfigParams(const Rect *pCropRect, const Dim *pResizeDim) +{ + m_bReconfigExternal = true; + m_bReconfigExtPPChange = false; + if (pCropRect) + { + if (!((pCropRect->t == m_cropRect.t) && (pCropRect->l == m_cropRect.l) && + (pCropRect->b == m_cropRect.b) && (pCropRect->r == m_cropRect.r))) + { + m_bReconfigExtPPChange = true; + m_cropRect = *pCropRect; + } + } + if (pResizeDim) + { + if (!((pResizeDim->w == m_resizeDim.w) && (pResizeDim->h == m_resizeDim.h))) + { + m_bReconfigExtPPChange = true; + m_resizeDim = *pResizeDim; + } + } + + // Clear existing output buffers of different size + uint8_t *pFrame = NULL; + while (!m_vpFrame.empty()) + { + pFrame = m_vpFrame.back(); + m_vpFrame.pop_back(); + if (m_bUseDeviceFrame) + { + CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext)); + CUDA_DRVAPI_CALL(cuMemFree((CUdeviceptr)pFrame)); + CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL)); + } + else + { + delete pFrame; + } + } + + return 1; +} + +/* Return value from HandlePictureDecode() are interpreted as: +* 0: fail, >=1: succeeded +*/ +int NvDecoder::HandlePictureDecode(CUVIDPICPARAMS *pPicParams) { + if (!m_hDecoder) + { + NVDEC_THROW_ERROR("Decoder not initialized.", CUDA_ERROR_NOT_INITIALIZED); + return false; + } + m_nPicNumInDecodeOrder[pPicParams->CurrPicIdx] = m_nDecodePicCnt++; + CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext)); + cuvidDecodePicture(m_hDecoder, pPicParams); + if (m_bForce_zero_latency && ((!pPicParams->field_pic_flag) || (pPicParams->second_field))) + { + CUVIDPARSERDISPINFO dispInfo; + memset(&dispInfo, 0, sizeof(dispInfo)); + dispInfo.picture_index = pPicParams->CurrPicIdx; + dispInfo.progressive_frame = !pPicParams->field_pic_flag; + dispInfo.top_field_first = pPicParams->bottom_field_flag ^ 1; + HandlePictureDisplay(&dispInfo); + } + CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL)); + return 1; +} + +/* Return value from HandlePictureDisplay() are interpreted as: +* 0: fail, >=1: succeeded +*/ +int NvDecoder::HandlePictureDisplay(CUVIDPARSERDISPINFO *pDispInfo) { + CUVIDPROCPARAMS videoProcessingParameters = {}; + videoProcessingParameters.progressive_frame = pDispInfo->progressive_frame; + videoProcessingParameters.second_field = pDispInfo->repeat_first_field + 1; + videoProcessingParameters.top_field_first = pDispInfo->top_field_first; + videoProcessingParameters.unpaired_field = pDispInfo->repeat_first_field < 0; + videoProcessingParameters.output_stream = m_cuvidStream; + + if (m_bExtractSEIMessage) + { + if (m_SEIMessagesDisplayOrder[pDispInfo->picture_index].pSEIData) + { + // Write SEI Message + uint8_t *seiBuffer = (uint8_t *)(m_SEIMessagesDisplayOrder[pDispInfo->picture_index].pSEIData); + uint32_t seiNumMessages = m_SEIMessagesDisplayOrder[pDispInfo->picture_index].sei_message_count; + CUSEIMESSAGE *seiMessagesInfo = m_SEIMessagesDisplayOrder[pDispInfo->picture_index].pSEIMessage; + if (m_fpSEI) + { + for (uint32_t i = 0; i < seiNumMessages; i++) + { + if (m_eCodec == cudaVideoCodec_H264 || cudaVideoCodec_H264_SVC || cudaVideoCodec_H264_MVC || cudaVideoCodec_HEVC) + { + switch (seiMessagesInfo[i].sei_message_type) + { + case SEI_TYPE_TIME_CODE: + { + HEVCSEITIMECODE *timecode = (HEVCSEITIMECODE *)seiBuffer; + fwrite(timecode, sizeof(HEVCSEITIMECODE), 1, m_fpSEI); + } + break; + case SEI_TYPE_USER_DATA_UNREGISTERED: + { + fwrite(seiBuffer, seiMessagesInfo[i].sei_message_size, 1, m_fpSEI); + } + break; + } + } + if (m_eCodec == cudaVideoCodec_AV1) + { + fwrite(seiBuffer, seiMessagesInfo[i].sei_message_size, 1, m_fpSEI); + } + seiBuffer += seiMessagesInfo[i].sei_message_size; + } + } + free(m_SEIMessagesDisplayOrder[pDispInfo->picture_index].pSEIData); + free(m_SEIMessagesDisplayOrder[pDispInfo->picture_index].pSEIMessage); + } + } + + CUdeviceptr dpSrcFrame = 0; + unsigned int nSrcPitch = 0; + CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext)); + NVDEC_API_CALL(cuvidMapVideoFrame(m_hDecoder, pDispInfo->picture_index, &dpSrcFrame, + &nSrcPitch, &videoProcessingParameters)); + + CUVIDGETDECODESTATUS DecodeStatus; + memset(&DecodeStatus, 0, sizeof(DecodeStatus)); + CUresult result = cuvidGetDecodeStatus(m_hDecoder, pDispInfo->picture_index, &DecodeStatus); + if (result == CUDA_SUCCESS && (DecodeStatus.decodeStatus == cuvidDecodeStatus_Error || DecodeStatus.decodeStatus == cuvidDecodeStatus_Error_Concealed)) + { + printf("Decode Error occurred for picture %d\n", m_nPicNumInDecodeOrder[pDispInfo->picture_index]); + } + + uint8_t *pDecodedFrame = nullptr; + { + std::lock_guard lock(m_mtxVPFrame); + if ((unsigned)++m_nDecodedFrame > m_vpFrame.size()) + { + // Not enough frames in stock + m_nFrameAlloc++; + uint8_t *pFrame = NULL; + if (m_bUseDeviceFrame) + { + if (m_bDeviceFramePitched) + { + CUDA_DRVAPI_CALL(cuMemAllocPitch((CUdeviceptr *)&pFrame, &m_nDeviceFramePitch, GetWidth() * m_nBPP, m_nLumaHeight + (m_nChromaHeight * m_nNumChromaPlanes), 16)); + } + else + { + CUDA_DRVAPI_CALL(cuMemAlloc((CUdeviceptr *)&pFrame, GetFrameSize())); + } + } + else + { + pFrame = new uint8_t[GetFrameSize()]; + } + m_vpFrame.push_back(pFrame); + } + pDecodedFrame = m_vpFrame[m_nDecodedFrame - 1]; + } + + // Copy luma plane + CUDA_MEMCPY2D m = { 0 }; + m.srcMemoryType = CU_MEMORYTYPE_DEVICE; + m.srcDevice = dpSrcFrame; + m.srcPitch = nSrcPitch; + m.dstMemoryType = m_bUseDeviceFrame ? CU_MEMORYTYPE_DEVICE : CU_MEMORYTYPE_HOST; + m.dstDevice = (CUdeviceptr)(m.dstHost = pDecodedFrame); + m.dstPitch = m_nDeviceFramePitch ? m_nDeviceFramePitch : GetWidth() * m_nBPP; + m.WidthInBytes = GetWidth() * m_nBPP; + m.Height = m_nLumaHeight; + CUDA_DRVAPI_CALL(cuMemcpy2DAsync(&m, m_cuvidStream)); + + // Copy chroma plane + // NVDEC output has luma height aligned by 2. Adjust chroma offset by aligning height + m.srcDevice = (CUdeviceptr)((uint8_t *)dpSrcFrame + m.srcPitch * ((m_nSurfaceHeight + 1) & ~1)); + m.dstDevice = (CUdeviceptr)(m.dstHost = pDecodedFrame + m.dstPitch * m_nLumaHeight); + m.Height = m_nChromaHeight; + CUDA_DRVAPI_CALL(cuMemcpy2DAsync(&m, m_cuvidStream)); + + if (m_nNumChromaPlanes == 2) + { + m.srcDevice = (CUdeviceptr)((uint8_t *)dpSrcFrame + m.srcPitch * ((m_nSurfaceHeight + 1) & ~1) * 2); + m.dstDevice = (CUdeviceptr)(m.dstHost = pDecodedFrame + m.dstPitch * m_nLumaHeight * 2); + m.Height = m_nChromaHeight; + CUDA_DRVAPI_CALL(cuMemcpy2DAsync(&m, m_cuvidStream)); + } + CUDA_DRVAPI_CALL(cuStreamSynchronize(m_cuvidStream)); + CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL)); + + if ((int)m_vTimestamp.size() < m_nDecodedFrame) { + m_vTimestamp.resize(m_vpFrame.size()); + } + m_vTimestamp[m_nDecodedFrame - 1] = pDispInfo->timestamp; + + NVDEC_API_CALL(cuvidUnmapVideoFrame(m_hDecoder, dpSrcFrame)); + return 1; +} + +int NvDecoder::GetSEIMessage(CUVIDSEIMESSAGEINFO *pSEIMessageInfo) +{ + uint32_t seiNumMessages = pSEIMessageInfo->sei_message_count; + CUSEIMESSAGE *seiMessagesInfo = pSEIMessageInfo->pSEIMessage; + size_t totalSEIBufferSize = 0; + if ((pSEIMessageInfo->picIdx < 0) || (pSEIMessageInfo->picIdx >= MAX_FRM_CNT)) + { + printf("Invalid picture index (%d)\n", pSEIMessageInfo->picIdx); + return 0; + } + for (uint32_t i = 0; i < seiNumMessages; i++) + { + totalSEIBufferSize += seiMessagesInfo[i].sei_message_size; + } + if (!m_pCurrSEIMessage) + { + printf("Out of Memory, Allocation failed for m_pCurrSEIMessage\n"); + return 0; + } + m_pCurrSEIMessage->pSEIData = malloc(totalSEIBufferSize); + if (!m_pCurrSEIMessage->pSEIData) + { + printf("Out of Memory, Allocation failed for SEI Buffer\n"); + return 0; + } + memcpy(m_pCurrSEIMessage->pSEIData, pSEIMessageInfo->pSEIData, totalSEIBufferSize); + m_pCurrSEIMessage->pSEIMessage = (CUSEIMESSAGE *)malloc(sizeof(CUSEIMESSAGE) * seiNumMessages); + if (!m_pCurrSEIMessage->pSEIMessage) + { + free(m_pCurrSEIMessage->pSEIData); + m_pCurrSEIMessage->pSEIData = NULL; + return 0; + } + memcpy(m_pCurrSEIMessage->pSEIMessage, pSEIMessageInfo->pSEIMessage, sizeof(CUSEIMESSAGE) * seiNumMessages); + m_pCurrSEIMessage->sei_message_count = pSEIMessageInfo->sei_message_count; + m_SEIMessagesDisplayOrder[pSEIMessageInfo->picIdx] = *m_pCurrSEIMessage; + return 1; +} + +NvDecoder::NvDecoder(CUcontext cuContext, bool bUseDeviceFrame, cudaVideoCodec eCodec, bool bLowLatency, + bool bDeviceFramePitched, const Rect *pCropRect, const Dim *pResizeDim, bool extract_user_SEI_Message, + int maxWidth, int maxHeight, unsigned int clkRate, bool force_zero_latency) : + m_cuContext(cuContext), m_bUseDeviceFrame(bUseDeviceFrame), m_eCodec(eCodec), m_bDeviceFramePitched(bDeviceFramePitched), + m_bExtractSEIMessage(extract_user_SEI_Message), m_nMaxWidth (maxWidth), m_nMaxHeight(maxHeight), + m_bForce_zero_latency(force_zero_latency) +{ + if (pCropRect) m_cropRect = *pCropRect; + if (pResizeDim) m_resizeDim = *pResizeDim; + + NVDEC_API_CALL(cuvidCtxLockCreate(&m_ctxLock, cuContext)); + + ck(cuStreamCreate(&m_cuvidStream, CU_STREAM_DEFAULT)); + + decoderSessionID = 0; + + if (m_bExtractSEIMessage) + { + m_fpSEI = fopen("sei_message.txt", "wb"); + m_pCurrSEIMessage = new CUVIDSEIMESSAGEINFO; + memset(&m_SEIMessagesDisplayOrder, 0, sizeof(m_SEIMessagesDisplayOrder)); + } + CUVIDPARSERPARAMS videoParserParameters = {}; + videoParserParameters.CodecType = eCodec; + videoParserParameters.ulMaxNumDecodeSurfaces = 1; + videoParserParameters.ulClockRate = clkRate; + videoParserParameters.ulMaxDisplayDelay = bLowLatency ? 0 : 1; + videoParserParameters.pUserData = this; + videoParserParameters.pfnSequenceCallback = HandleVideoSequenceProc; + videoParserParameters.pfnDecodePicture = HandlePictureDecodeProc; + videoParserParameters.pfnDisplayPicture = m_bForce_zero_latency ? NULL : HandlePictureDisplayProc; + videoParserParameters.pfnGetOperatingPoint = HandleOperatingPointProc; + videoParserParameters.pfnGetSEIMsg = m_bExtractSEIMessage ? HandleSEIMessagesProc : NULL; + NVDEC_API_CALL(cuvidCreateVideoParser(&m_hParser, &videoParserParameters)); +} + +NvDecoder::~NvDecoder() { + + START_TIMER + + if (m_pCurrSEIMessage) { + delete m_pCurrSEIMessage; + m_pCurrSEIMessage = NULL; + } + + if (m_fpSEI) { + fclose(m_fpSEI); + m_fpSEI = NULL; + } + + if (m_hParser) { + cuvidDestroyVideoParser(m_hParser); + } + cuCtxPushCurrent(m_cuContext); + if (m_hDecoder) { + cuvidDestroyDecoder(m_hDecoder); + } + + std::lock_guard lock(m_mtxVPFrame); + + for (uint8_t *pFrame : m_vpFrame) + { + if (m_bUseDeviceFrame) + { + cuMemFree((CUdeviceptr)pFrame); + } + else + { + delete[] pFrame; + } + } + cuCtxPopCurrent(NULL); + + cuvidCtxLockDestroy(m_ctxLock); + + STOP_TIMER("Session Deinitialization Time: "); + + NvDecoder::addDecoderSessionOverHead(getDecoderSessionID(), elapsedTime); +} + +int NvDecoder::Decode(const uint8_t *pData, int nSize, int nFlags, int64_t nTimestamp) +{ + m_nDecodedFrame = 0; + m_nDecodedFrameReturned = 0; + CUVIDSOURCEDATAPACKET packet = { 0 }; + packet.payload = pData; + packet.payload_size = nSize; + packet.flags = nFlags | CUVID_PKT_TIMESTAMP; + packet.timestamp = nTimestamp; + if (!pData || nSize == 0) { + packet.flags |= CUVID_PKT_ENDOFSTREAM; + } + NVDEC_API_CALL(cuvidParseVideoData(m_hParser, &packet)); + + return m_nDecodedFrame; +} + +uint8_t* NvDecoder::GetFrame(int64_t* pTimestamp) +{ + if (m_nDecodedFrame > 0) + { + std::lock_guard lock(m_mtxVPFrame); + m_nDecodedFrame--; + if (pTimestamp) + *pTimestamp = m_vTimestamp[m_nDecodedFrameReturned]; + return m_vpFrame[m_nDecodedFrameReturned++]; + } + + return NULL; +} + +uint8_t* NvDecoder::GetLockedFrame(int64_t* pTimestamp) +{ + uint8_t *pFrame; + uint64_t timestamp; + if (m_nDecodedFrame > 0) { + std::lock_guard lock(m_mtxVPFrame); + m_nDecodedFrame--; + pFrame = m_vpFrame[0]; + m_vpFrame.erase(m_vpFrame.begin(), m_vpFrame.begin() + 1); + + timestamp = m_vTimestamp[0]; + m_vTimestamp.erase(m_vTimestamp.begin(), m_vTimestamp.begin() + 1); + + if (pTimestamp) + *pTimestamp = timestamp; + + return pFrame; + } + + return NULL; +} + +void NvDecoder::UnlockFrame(uint8_t **pFrame) +{ + std::lock_guard lock(m_mtxVPFrame); + m_vpFrame.insert(m_vpFrame.end(), &pFrame[0], &pFrame[1]); + + // add a dummy entry for timestamp + uint64_t timestamp[2] = {0}; + m_vTimestamp.insert(m_vTimestamp.end(), ×tamp[0], ×tamp[1]); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/NvCodec/NvDecoder/NvDecoder.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/NvCodec/NvDecoder/NvDecoder.h new file mode 100644 index 0000000..34fd042 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/NvCodec/NvDecoder/NvDecoder.h @@ -0,0 +1,362 @@ +/* +* Copyright 2017-2022 NVIDIA Corporation. All rights reserved. +* +* Please refer to the NVIDIA end user license agreement (EULA) associated +* with this source code for terms and conditions that govern your use of +* this software. Any use, reproduction, disclosure, or distribution of +* this software and related documentation outside the terms of the EULA +* is strictly prohibited. +* +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nvcuvid.h" +#include "Utils/NvCodecUtils.h" + +#define MAX_FRM_CNT 32 + +typedef enum{ + SEI_TYPE_TIME_CODE = 136, + SEI_TYPE_USER_DATA_UNREGISTERED = 5 +}SEI_H264_HEVC_PAYLOAD_TYPE; + +/** +* @brief Exception class for error reporting from the decode API. +*/ +class NVDECException : public std::exception +{ +public: + NVDECException(const std::string& errorStr, const CUresult errorCode) + : m_errorString(errorStr), m_errorCode(errorCode) {} + + virtual ~NVDECException() throw() {} + virtual const char* what() const throw() { return m_errorString.c_str(); } + CUresult getErrorCode() const { return m_errorCode; } + const std::string& getErrorString() const { return m_errorString; } + static NVDECException makeNVDECException(const std::string& errorStr, const CUresult errorCode, + const std::string& functionName, const std::string& fileName, int lineNo); +private: + std::string m_errorString; + CUresult m_errorCode; +}; + +inline NVDECException NVDECException::makeNVDECException(const std::string& errorStr, const CUresult errorCode, const std::string& functionName, + const std::string& fileName, int lineNo) +{ + std::ostringstream errorLog; + errorLog << functionName << " : " << errorStr << " at " << fileName << ":" << lineNo << std::endl; + NVDECException exception(errorLog.str(), errorCode); + return exception; +} + +#define NVDEC_THROW_ERROR( errorStr, errorCode ) \ + do \ + { \ + throw NVDECException::makeNVDECException(errorStr, errorCode, __FUNCTION__, __FILE__, __LINE__); \ + } while (0) + + +#define NVDEC_API_CALL( cuvidAPI ) \ + do \ + { \ + CUresult errorCode = cuvidAPI; \ + if( errorCode != CUDA_SUCCESS) \ + { \ + std::ostringstream errorLog; \ + errorLog << #cuvidAPI << " returned error " << errorCode; \ + throw NVDECException::makeNVDECException(errorLog.str(), errorCode, __FUNCTION__, __FILE__, __LINE__); \ + } \ + } while (0) + +struct Rect { + int l, t, r, b; +}; + +struct Dim { + int w, h; +}; + +/** +* @brief Base class for decoder interface. +*/ +class NvDecoder { + +public: + /** + * @brief This function is used to initialize the decoder session. + * Application must call this function to initialize the decoder, before + * starting to decode any frames. + */ + NvDecoder(CUcontext cuContext, bool bUseDeviceFrame, cudaVideoCodec eCodec, bool bLowLatency = false, + bool bDeviceFramePitched = false, const Rect *pCropRect = NULL, const Dim *pResizeDim = NULL, + bool extract_user_SEI_Message = false, int maxWidth = 0, int maxHeight = 0, unsigned int clkRate = 1000, + bool force_zero_latency = false); + ~NvDecoder(); + + /** + * @brief This function is used to get the current CUDA context. + */ + CUcontext GetContext() { return m_cuContext; } + + /** + * @brief This function is used to get the output frame width. + * NV12/P016 output format width is 2 byte aligned because of U and V interleave + */ + int GetWidth() { assert(m_nWidth); return (m_eOutputFormat == cudaVideoSurfaceFormat_NV12 || m_eOutputFormat == cudaVideoSurfaceFormat_P016) + ? (m_nWidth + 1) & ~1 : m_nWidth; } + + /** + * @brief This function is used to get the actual decode width + */ + int GetDecodeWidth() { assert(m_nWidth); return m_nWidth; } + + /** + * @brief This function is used to get the output frame height (Luma height). + */ + int GetHeight() { assert(m_nLumaHeight); return m_nLumaHeight; } + + /** + * @brief This function is used to get the current chroma height. + */ + int GetChromaHeight() { assert(m_nChromaHeight); return m_nChromaHeight; } + + /** + * @brief This function is used to get the number of chroma planes. + */ + int GetNumChromaPlanes() { assert(m_nNumChromaPlanes); return m_nNumChromaPlanes; } + + /** + * @brief This function is used to get the current frame size based on pixel format. + */ + int GetFrameSize() { assert(m_nWidth); return GetWidth() * (m_nLumaHeight + (m_nChromaHeight * m_nNumChromaPlanes)) * m_nBPP; } + + /** + * @brief This function is used to get the current frame Luma plane size. + */ + int GetLumaPlaneSize() { assert(m_nWidth); return GetWidth() * m_nLumaHeight * m_nBPP; } + + /** + * @brief This function is used to get the current frame chroma plane size. + */ + int GetChromaPlaneSize() { assert(m_nWidth); return GetWidth() * (m_nChromaHeight * m_nNumChromaPlanes) * m_nBPP; } + + /** + * @brief This function is used to get the pitch of the device buffer holding the decoded frame. + */ + int GetDeviceFramePitch() { assert(m_nWidth); return m_nDeviceFramePitch ? (int)m_nDeviceFramePitch : GetWidth() * m_nBPP; } + + /** + * @brief This function is used to get the bit depth associated with the pixel format. + */ + int GetBitDepth() { assert(m_nWidth); return m_nBitDepthMinus8 + 8; } + + /** + * @brief This function is used to get the bytes used per pixel. + */ + int GetBPP() { assert(m_nWidth); return m_nBPP; } + + /** + * @brief This function is used to get the YUV chroma format + */ + cudaVideoSurfaceFormat GetOutputFormat() { return m_eOutputFormat; } + + /** + * @brief This function is used to get information about the video stream (codec, display parameters etc) + */ + CUVIDEOFORMAT GetVideoFormatInfo() { assert(m_nWidth); return m_videoFormat; } + + /** + * @brief This function is used to get codec string from codec id + */ + const char *GetCodecString(cudaVideoCodec eCodec); + + /** + * @brief This function is used to print information about the video stream + */ + std::string GetVideoInfo() const { return m_videoInfo.str(); } + + /** + * @brief This function decodes a frame and returns the number of frames that are available for + * display. All frames that are available for display should be read before making a subsequent decode call. + * @param pData - pointer to the data buffer that is to be decoded + * @param nSize - size of the data buffer in bytes + * @param nFlags - CUvideopacketflags for setting decode options + * @param nTimestamp - presentation timestamp + */ + int Decode(const uint8_t *pData, int nSize, int nFlags = 0, int64_t nTimestamp = 0); + + /** + * @brief This function returns a decoded frame and timestamp. This function should be called in a loop for + * fetching all the frames that are available for display. + */ + uint8_t* GetFrame(int64_t* pTimestamp = nullptr); + + + /** + * @brief This function decodes a frame and returns the locked frame buffers + * This makes the buffers available for use by the application without the buffers + * getting overwritten, even if subsequent decode calls are made. The frame buffers + * remain locked, until UnlockFrame() is called + */ + uint8_t* GetLockedFrame(int64_t* pTimestamp = nullptr); + + /** + * @brief This function unlocks the frame buffer and makes the frame buffers available for write again + * @param ppFrame - pointer to array of frames that are to be unlocked + * @param nFrame - number of frames to be unlocked + */ + void UnlockFrame(uint8_t **pFrame); + + /** + * @brief This function allows app to set decoder reconfig params + * @param pCropRect - cropping rectangle coordinates + * @param pResizeDim - width and height of resized output + */ + int setReconfigParams(const Rect * pCropRect, const Dim * pResizeDim); + + /** + * @brief This function allows app to set operating point for AV1 SVC clips + * @param opPoint - operating point of an AV1 scalable bitstream + * @param bDispAllLayers - Output all decoded frames of an AV1 scalable bitstream + */ + void SetOperatingPoint(const uint32_t opPoint, const bool bDispAllLayers) { m_nOperatingPoint = opPoint; m_bDispAllLayers = bDispAllLayers; } + + // start a timer + void startTimer() { m_stDecode_time.Start(); } + + // stop the timer + double stopTimer() { return m_stDecode_time.Stop(); } + + void setDecoderSessionID(int sessionID) { decoderSessionID = sessionID; } + int getDecoderSessionID() { return decoderSessionID; } + + // Session overhead refers to decoder initialization and deinitialization time + static void addDecoderSessionOverHead(int sessionID, int64_t duration) { sessionOverHead[sessionID] += duration; } + static int64_t getDecoderSessionOverHead(int sessionID) { return sessionOverHead[sessionID]; } + +private: + int decoderSessionID; // Decoder session identifier. Used to gather session level stats. + static std::map sessionOverHead; // Records session overhead of initialization+deinitialization time. Format is (thread id, duration) + + /** + * @brief Callback function to be registered for getting a callback when decoding of sequence starts + */ + static int CUDAAPI HandleVideoSequenceProc(void *pUserData, CUVIDEOFORMAT *pVideoFormat) { return ((NvDecoder *)pUserData)->HandleVideoSequence(pVideoFormat); } + + /** + * @brief Callback function to be registered for getting a callback when a decoded frame is ready to be decoded + */ + static int CUDAAPI HandlePictureDecodeProc(void *pUserData, CUVIDPICPARAMS *pPicParams) { return ((NvDecoder *)pUserData)->HandlePictureDecode(pPicParams); } + + /** + * @brief Callback function to be registered for getting a callback when a decoded frame is available for display + */ + static int CUDAAPI HandlePictureDisplayProc(void *pUserData, CUVIDPARSERDISPINFO *pDispInfo) { return ((NvDecoder *)pUserData)->HandlePictureDisplay(pDispInfo); } + + /** + * @brief Callback function to be registered for getting a callback to get operating point when AV1 SVC sequence header start. + */ + static int CUDAAPI HandleOperatingPointProc(void *pUserData, CUVIDOPERATINGPOINTINFO *pOPInfo) { return ((NvDecoder *)pUserData)->GetOperatingPoint(pOPInfo); } + + /** + * @brief Callback function to be registered for getting a callback when all the unregistered user SEI Messages are parsed for a frame. + */ + static int CUDAAPI HandleSEIMessagesProc(void *pUserData, CUVIDSEIMESSAGEINFO *pSEIMessageInfo) { return ((NvDecoder *)pUserData)->GetSEIMessage(pSEIMessageInfo); } + + /** + * @brief This function gets called when a sequence is ready to be decoded. The function also gets called + when there is format change + */ + int HandleVideoSequence(CUVIDEOFORMAT *pVideoFormat); + + /** + * @brief This function gets called when a picture is ready to be decoded. cuvidDecodePicture is called from this function + * to decode the picture + */ + int HandlePictureDecode(CUVIDPICPARAMS *pPicParams); + + /** + * @brief This function gets called after a picture is decoded and available for display. Frames are fetched and stored in + internal buffer + */ + int HandlePictureDisplay(CUVIDPARSERDISPINFO *pDispInfo); + + /** + * @brief This function gets called when AV1 sequence encounter more than one operating points + */ + int GetOperatingPoint(CUVIDOPERATINGPOINTINFO *pOPInfo); + + /** + * @brief This function gets called when all unregistered user SEI messages are parsed for a frame + */ + int GetSEIMessage(CUVIDSEIMESSAGEINFO *pSEIMessageInfo); + + /** + * @brief This function reconfigure decoder if there is a change in sequence params. + */ + int ReconfigureDecoder(CUVIDEOFORMAT *pVideoFormat); + +private: + CUcontext m_cuContext = NULL; + CUvideoctxlock m_ctxLock; + CUvideoparser m_hParser = NULL; + CUvideodecoder m_hDecoder = NULL; + bool m_bUseDeviceFrame; + // dimension of the output + unsigned int m_nWidth = 0, m_nLumaHeight = 0, m_nChromaHeight = 0; + unsigned int m_nNumChromaPlanes = 0; + // height of the mapped surface + int m_nSurfaceHeight = 0; + int m_nSurfaceWidth = 0; + cudaVideoCodec m_eCodec = cudaVideoCodec_NumCodecs; + cudaVideoChromaFormat m_eChromaFormat = cudaVideoChromaFormat_420; + cudaVideoSurfaceFormat m_eOutputFormat = cudaVideoSurfaceFormat_NV12; + int m_nBitDepthMinus8 = 0; + int m_nBPP = 1; + CUVIDEOFORMAT m_videoFormat = {}; + Rect m_displayRect = {}; + // stock of frames + std::vector m_vpFrame; + // timestamps of decoded frames + std::vector m_vTimestamp; + int m_nDecodedFrame = 0, m_nDecodedFrameReturned = 0; + int m_nDecodePicCnt = 0, m_nPicNumInDecodeOrder[MAX_FRM_CNT]; + CUVIDSEIMESSAGEINFO *m_pCurrSEIMessage = NULL; + CUVIDSEIMESSAGEINFO m_SEIMessagesDisplayOrder[MAX_FRM_CNT]; + FILE *m_fpSEI = NULL; + bool m_bEndDecodeDone = false; + std::mutex m_mtxVPFrame; + int m_nFrameAlloc = 0; + CUstream m_cuvidStream = 0; + bool m_bDeviceFramePitched = false; + size_t m_nDeviceFramePitch = 0; + Rect m_cropRect = {}; + Dim m_resizeDim = {}; + + std::ostringstream m_videoInfo; + unsigned int m_nMaxWidth = 0, m_nMaxHeight = 0; + bool m_bReconfigExternal = false; + bool m_bReconfigExtPPChange = false; + StopWatch m_stDecode_time; + + unsigned int m_nOperatingPoint = 0; + bool m_bDispAllLayers = false; + // In H.264, there is an inherent display latency for video contents + // which do not have num_reorder_frames=0 in the VUI. This applies to + // All-Intra and IPPP sequences as well. If the user wants zero display + // latency for All-Intra and IPPP sequences, the below flag will enable + // the display callback immediately after the decode callback. + bool m_bForce_zero_latency = false; + bool m_bExtractSEIMessage = false; +}; diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoder.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoder.cpp new file mode 100644 index 0000000..6c22164 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoder.cpp @@ -0,0 +1,1156 @@ +/* + * Copyright 2017-2022 NVIDIA Corporation. All rights reserved. + * + * Please refer to the NVIDIA end user license agreement (EULA) associated + * with this source code for terms and conditions that govern your use of + * this software. Any use, reproduction, disclosure, or distribution of + * this software and related documentation outside the terms of the EULA + * is strictly prohibited. + * + */ + +#include "NvEncoder.h" + +#if defined(WIN32) +#include +#else +#include +#endif + +#ifndef _WIN32 +#include +static inline bool operator==(const GUID& guid1, const GUID& guid2) { + return !memcmp(&guid1, &guid2, sizeof(GUID)); +} + +static inline bool operator!=(const GUID& guid1, const GUID& guid2) { + return !(guid1 == guid2); +} +#endif + +NvEncoder::NvEncoder(NV_ENC_DEVICE_TYPE eDeviceType, + void* pDevice, + uint32_t nWidth, + uint32_t nHeight, + NV_ENC_BUFFER_FORMAT eBufferFormat, + uint32_t nExtraOutputDelay, + bool bMotionEstimationOnly, + bool bOutputInVideoMemory, + bool bDX12Encode, + bool bUseIVFContainer) + : m_pDevice(pDevice), + m_eDeviceType(eDeviceType), + m_nWidth(nWidth), + m_nHeight(nHeight), + m_nMaxEncodeWidth(nWidth), + m_nMaxEncodeHeight(nHeight), + m_eBufferFormat(eBufferFormat), + m_bMotionEstimationOnly(bMotionEstimationOnly), + m_bOutputInVideoMemory(bOutputInVideoMemory), + m_bIsDX12Encode(bDX12Encode), + m_bUseIVFContainer(bUseIVFContainer), + m_nExtraOutputDelay(nExtraOutputDelay), + m_hEncoder(nullptr) { + LoadNvEncApi(); + + if (!m_nvenc.nvEncOpenEncodeSession) { + m_nEncoderBuffer = 0; + NVENC_THROW_ERROR("EncodeAPI not found", NV_ENC_ERR_NO_ENCODE_DEVICE); + } + + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS encodeSessionExParams = { + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER}; + encodeSessionExParams.device = m_pDevice; + encodeSessionExParams.deviceType = m_eDeviceType; + encodeSessionExParams.apiVersion = NVENCAPI_VERSION; + void* hEncoder = NULL; + NVENC_API_CALL( + m_nvenc.nvEncOpenEncodeSessionEx(&encodeSessionExParams, &hEncoder)); + m_hEncoder = hEncoder; +} + +void NvEncoder::LoadNvEncApi() { +#if defined(_WIN32) +#if defined(_WIN64) + HMODULE hModule = LoadLibrary(TEXT("nvEncodeAPI64.dll")); +#else + HMODULE hModule = LoadLibrary(TEXT("nvEncodeAPI.dll")); +#endif +#else + void* hModule = dlopen("libnvidia-encode.so.1", RTLD_LAZY); +#endif + + if (hModule == NULL) { + NVENC_THROW_ERROR( + "NVENC library file is not found. Please ensure NV driver is installed", + NV_ENC_ERR_NO_ENCODE_DEVICE); + } + + m_hModule = hModule; + + typedef NVENCSTATUS(NVENCAPI * + NvEncodeAPIGetMaxSupportedVersion_Type)(uint32_t*); +#if defined(_WIN32) + NvEncodeAPIGetMaxSupportedVersion_Type NvEncodeAPIGetMaxSupportedVersion = + (NvEncodeAPIGetMaxSupportedVersion_Type)GetProcAddress( + hModule, "NvEncodeAPIGetMaxSupportedVersion"); +#else + NvEncodeAPIGetMaxSupportedVersion_Type NvEncodeAPIGetMaxSupportedVersion = + (NvEncodeAPIGetMaxSupportedVersion_Type)dlsym( + hModule, "NvEncodeAPIGetMaxSupportedVersion"); +#endif + + uint32_t version = 0; + uint32_t currentVersion = + (NVENCAPI_MAJOR_VERSION << 4) | NVENCAPI_MINOR_VERSION; + NVENC_API_CALL(NvEncodeAPIGetMaxSupportedVersion(&version)); + if (currentVersion > version) { + NVENC_THROW_ERROR( + "Current Driver Version does not support this NvEncodeAPI version, " + "please upgrade driver", + NV_ENC_ERR_INVALID_VERSION); + } + + typedef NVENCSTATUS(NVENCAPI * NvEncodeAPICreateInstance_Type)( + NV_ENCODE_API_FUNCTION_LIST*); +#if defined(_WIN32) + NvEncodeAPICreateInstance_Type NvEncodeAPICreateInstance = + (NvEncodeAPICreateInstance_Type)GetProcAddress( + hModule, "NvEncodeAPICreateInstance"); +#else + NvEncodeAPICreateInstance_Type NvEncodeAPICreateInstance = + (NvEncodeAPICreateInstance_Type)dlsym(hModule, + "NvEncodeAPICreateInstance"); +#endif + + if (!NvEncodeAPICreateInstance) { + NVENC_THROW_ERROR( + "Cannot find NvEncodeAPICreateInstance() entry in NVENC library", + NV_ENC_ERR_NO_ENCODE_DEVICE); + } + + m_nvenc = {NV_ENCODE_API_FUNCTION_LIST_VER}; + NVENC_API_CALL(NvEncodeAPICreateInstance(&m_nvenc)); +} + +NvEncoder::~NvEncoder() { + DestroyHWEncoder(); + if (m_hModule) { +#if defined(_WIN32) + FreeLibrary((HMODULE)m_hModule); +#else + dlclose(m_hModule); +#endif + m_hModule = nullptr; + } +} + +void NvEncoder::CreateDefaultEncoderParams( + NV_ENC_INITIALIZE_PARAMS* pIntializeParams, + GUID codecGuid, + GUID presetGuid, + NV_ENC_TUNING_INFO tuningInfo) { + if (!m_hEncoder) { + NVENC_THROW_ERROR("Encoder Initialization failed", + NV_ENC_ERR_NO_ENCODE_DEVICE); + return; + } + + if (pIntializeParams == nullptr || + pIntializeParams->encodeConfig == nullptr) { + NVENC_THROW_ERROR( + "pInitializeParams and pInitializeParams->encodeConfig can't be NULL", + NV_ENC_ERR_INVALID_PTR); + } + + memset(pIntializeParams->encodeConfig, 0, sizeof(NV_ENC_CONFIG)); + auto pEncodeConfig = pIntializeParams->encodeConfig; + memset(pIntializeParams, 0, sizeof(NV_ENC_INITIALIZE_PARAMS)); + pIntializeParams->encodeConfig = pEncodeConfig; + + pIntializeParams->encodeConfig->version = NV_ENC_CONFIG_VER; + pIntializeParams->version = NV_ENC_INITIALIZE_PARAMS_VER; + + pIntializeParams->encodeGUID = codecGuid; + pIntializeParams->presetGUID = presetGuid; + pIntializeParams->encodeWidth = m_nWidth; + pIntializeParams->encodeHeight = m_nHeight; + pIntializeParams->darWidth = m_nWidth; + pIntializeParams->darHeight = m_nHeight; + pIntializeParams->frameRateNum = 30; + pIntializeParams->frameRateDen = 1; + pIntializeParams->enablePTD = 1; + pIntializeParams->reportSliceOffsets = 0; + pIntializeParams->enableSubFrameWrite = 0; + pIntializeParams->maxEncodeWidth = m_nWidth; + pIntializeParams->maxEncodeHeight = m_nHeight; + pIntializeParams->enableMEOnlyMode = m_bMotionEstimationOnly; + pIntializeParams->enableOutputInVidmem = m_bOutputInVideoMemory; +#if defined(_WIN32) + if (!m_bOutputInVideoMemory) { + pIntializeParams->enableEncodeAsync = + GetCapabilityValue(codecGuid, NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT); + } +#endif + + NV_ENC_PRESET_CONFIG presetConfig = {NV_ENC_PRESET_CONFIG_VER, + {NV_ENC_CONFIG_VER}}; + m_nvenc.nvEncGetEncodePresetConfig(m_hEncoder, codecGuid, presetGuid, + &presetConfig); + memcpy(pIntializeParams->encodeConfig, &presetConfig.presetCfg, + sizeof(NV_ENC_CONFIG)); + pIntializeParams->encodeConfig->frameIntervalP = 1; + pIntializeParams->encodeConfig->gopLength = NVENC_INFINITE_GOPLENGTH; + + pIntializeParams->encodeConfig->rcParams.rateControlMode = + NV_ENC_PARAMS_RC_CONSTQP; + + if (!m_bMotionEstimationOnly) { + pIntializeParams->tuningInfo = tuningInfo; + NV_ENC_PRESET_CONFIG presetConfig = {NV_ENC_PRESET_CONFIG_VER, + {NV_ENC_CONFIG_VER}}; + m_nvenc.nvEncGetEncodePresetConfigEx(m_hEncoder, codecGuid, presetGuid, + tuningInfo, &presetConfig); + memcpy(pIntializeParams->encodeConfig, &presetConfig.presetCfg, + sizeof(NV_ENC_CONFIG)); + } else { + m_encodeConfig.version = NV_ENC_CONFIG_VER; + m_encodeConfig.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP; + m_encodeConfig.rcParams.constQP = {28, 31, 25}; + } + + if (pIntializeParams->encodeGUID == NV_ENC_CODEC_H264_GUID) { + if (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444 || + m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) { + pIntializeParams->encodeConfig->encodeCodecConfig.h264Config + .chromaFormatIDC = 3; + } + pIntializeParams->encodeConfig->encodeCodecConfig.h264Config.idrPeriod = + pIntializeParams->encodeConfig->gopLength; + } else if (pIntializeParams->encodeGUID == NV_ENC_CODEC_HEVC_GUID) { + pIntializeParams->encodeConfig->encodeCodecConfig.hevcConfig + .pixelBitDepthMinus8 = + (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || + m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) + ? 2 + : 0; + if (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444 || + m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) { + pIntializeParams->encodeConfig->encodeCodecConfig.hevcConfig + .chromaFormatIDC = 3; + } + pIntializeParams->encodeConfig->encodeCodecConfig.hevcConfig.idrPeriod = + pIntializeParams->encodeConfig->gopLength; + } else if (pIntializeParams->encodeGUID == NV_ENC_CODEC_AV1_GUID) { + pIntializeParams->encodeConfig->encodeCodecConfig.av1Config + .pixelBitDepthMinus8 = + (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT) ? 2 : 0; + pIntializeParams->encodeConfig->encodeCodecConfig.av1Config + .inputPixelBitDepthMinus8 = + (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT) ? 2 : 0; + pIntializeParams->encodeConfig->encodeCodecConfig.av1Config + .chromaFormatIDC = 1; + pIntializeParams->encodeConfig->encodeCodecConfig.av1Config.idrPeriod = + pIntializeParams->encodeConfig->gopLength; + if (m_bOutputInVideoMemory) { + pIntializeParams->encodeConfig->frameIntervalP = 1; + } + } + + if (m_bIsDX12Encode) { + pIntializeParams->bufferFormat = m_eBufferFormat; + } + + return; +} + +void NvEncoder::CreateEncoder(const NV_ENC_INITIALIZE_PARAMS* pEncoderParams) { + if (!m_hEncoder) { + NVENC_THROW_ERROR("Encoder Initialization failed", + NV_ENC_ERR_NO_ENCODE_DEVICE); + } + + if (!pEncoderParams) { + NVENC_THROW_ERROR("Invalid NV_ENC_INITIALIZE_PARAMS ptr", + NV_ENC_ERR_INVALID_PTR); + } + + if (pEncoderParams->encodeWidth == 0 || pEncoderParams->encodeHeight == 0) { + NVENC_THROW_ERROR("Invalid encoder width and height", + NV_ENC_ERR_INVALID_PARAM); + } + + if (pEncoderParams->encodeGUID != NV_ENC_CODEC_H264_GUID && + pEncoderParams->encodeGUID != NV_ENC_CODEC_HEVC_GUID && + pEncoderParams->encodeGUID != NV_ENC_CODEC_AV1_GUID) { + NVENC_THROW_ERROR("Invalid codec guid", NV_ENC_ERR_INVALID_PARAM); + } + + if (pEncoderParams->encodeGUID == NV_ENC_CODEC_H264_GUID) { + if (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || + m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) { + NVENC_THROW_ERROR("10-bit format isn't supported by H264 encoder", + NV_ENC_ERR_INVALID_PARAM); + } + } + + if (pEncoderParams->encodeGUID == NV_ENC_CODEC_AV1_GUID) { + if (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444 || + m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) { + NVENC_THROW_ERROR("YUV444 format isn't supported by AV1 encoder", + NV_ENC_ERR_INVALID_PARAM); + } + } + + // set other necessary params if not set yet + if (pEncoderParams->encodeGUID == NV_ENC_CODEC_H264_GUID) { + if ((m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444) && + (pEncoderParams->encodeConfig->encodeCodecConfig.h264Config + .chromaFormatIDC != 3)) { + NVENC_THROW_ERROR("Invalid ChromaFormatIDC", NV_ENC_ERR_INVALID_PARAM); + } + } + + if (pEncoderParams->encodeGUID == NV_ENC_CODEC_HEVC_GUID) { + bool yuv10BitFormat = + (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT || + m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) + ? true + : false; + if (yuv10BitFormat && pEncoderParams->encodeConfig->encodeCodecConfig + .hevcConfig.pixelBitDepthMinus8 != 2) { + NVENC_THROW_ERROR("Invalid PixelBitdepth", NV_ENC_ERR_INVALID_PARAM); + } + + if ((m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444 || + m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV444_10BIT) && + (pEncoderParams->encodeConfig->encodeCodecConfig.hevcConfig + .chromaFormatIDC != 3)) { + NVENC_THROW_ERROR("Invalid ChromaFormatIDC", NV_ENC_ERR_INVALID_PARAM); + } + } + + if (pEncoderParams->encodeGUID == NV_ENC_CODEC_AV1_GUID) { + bool yuv10BitFormat = + (m_eBufferFormat == NV_ENC_BUFFER_FORMAT_YUV420_10BIT) ? true : false; + if (yuv10BitFormat && pEncoderParams->encodeConfig->encodeCodecConfig + .av1Config.pixelBitDepthMinus8 != 2) { + NVENC_THROW_ERROR("Invalid PixelBitdepth", NV_ENC_ERR_INVALID_PARAM); + } + + if (pEncoderParams->encodeConfig->encodeCodecConfig.av1Config + .chromaFormatIDC != 1) { + NVENC_THROW_ERROR("Invalid ChromaFormatIDC", NV_ENC_ERR_INVALID_PARAM); + } + + if (m_bOutputInVideoMemory && + pEncoderParams->encodeConfig->frameIntervalP > 1) { + NVENC_THROW_ERROR( + "Alt Ref frames not supported for AV1 in case of OutputInVideoMemory", + NV_ENC_ERR_INVALID_PARAM); + } + } + + memcpy(&m_initializeParams, pEncoderParams, sizeof(m_initializeParams)); + m_initializeParams.version = NV_ENC_INITIALIZE_PARAMS_VER; + + if (pEncoderParams->encodeConfig) { + memcpy(&m_encodeConfig, pEncoderParams->encodeConfig, + sizeof(m_encodeConfig)); + m_encodeConfig.version = NV_ENC_CONFIG_VER; + } else { + NV_ENC_PRESET_CONFIG presetConfig = {NV_ENC_PRESET_CONFIG_VER, + {NV_ENC_CONFIG_VER}}; + if (!m_bMotionEstimationOnly) { + m_nvenc.nvEncGetEncodePresetConfigEx( + m_hEncoder, pEncoderParams->encodeGUID, pEncoderParams->presetGUID, + pEncoderParams->tuningInfo, &presetConfig); + memcpy(&m_encodeConfig, &presetConfig.presetCfg, sizeof(NV_ENC_CONFIG)); + if (m_bOutputInVideoMemory && + pEncoderParams->encodeGUID == NV_ENC_CODEC_AV1_GUID) { + m_encodeConfig.frameIntervalP = 1; + } + } else { + m_encodeConfig.version = NV_ENC_CONFIG_VER; + m_encodeConfig.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CONSTQP; + m_encodeConfig.rcParams.constQP = {28, 31, 25}; + } + } + m_initializeParams.encodeConfig = &m_encodeConfig; + m_initializeParams.bufferFormat = m_eBufferFormat; + + try { + NVENC_API_CALL( + m_nvenc.nvEncInitializeEncoder(m_hEncoder, &m_initializeParams)); + } catch (const NVENCException& e) { + DestroyHWEncoder(); + std::cout << "nvEncInitializeEncoder API failed" << e.getErrorCode() + << " - " << e.getErrorString() << std::endl; + } + + m_bEncoderInitialized = true; + m_nWidth = m_initializeParams.encodeWidth; + m_nHeight = m_initializeParams.encodeHeight; + m_nMaxEncodeWidth = m_initializeParams.maxEncodeWidth; + m_nMaxEncodeHeight = m_initializeParams.maxEncodeHeight; + + m_nEncoderBuffer = m_encodeConfig.frameIntervalP + + m_encodeConfig.rcParams.lookaheadDepth + + m_nExtraOutputDelay; + m_nOutputDelay = m_nEncoderBuffer - 1; + + if (!m_bOutputInVideoMemory) { + m_vpCompletionEvent.resize(m_nEncoderBuffer, nullptr); + } + +#if defined(_WIN32) + for (uint32_t i = 0; i < m_vpCompletionEvent.size(); i++) { + m_vpCompletionEvent[i] = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!m_bIsDX12Encode) { + NV_ENC_EVENT_PARAMS eventParams = {NV_ENC_EVENT_PARAMS_VER}; + eventParams.completionEvent = m_vpCompletionEvent[i]; + m_nvenc.nvEncRegisterAsyncEvent(m_hEncoder, &eventParams); + } + } +#endif + + m_vMappedInputBuffers.resize(m_nEncoderBuffer, nullptr); + + if (m_bMotionEstimationOnly) { + m_vMappedRefBuffers.resize(m_nEncoderBuffer, nullptr); + + if (!m_bOutputInVideoMemory) { + InitializeMVOutputBuffer(); + } + } else { + if (!m_bOutputInVideoMemory && !m_bIsDX12Encode) { + m_vBitstreamOutputBuffer.resize(m_nEncoderBuffer, nullptr); + InitializeBitstreamBuffer(); + } + } + + AllocateInputBuffers(m_nEncoderBuffer); +} + +void NvEncoder::DestroyEncoder() { + if (!m_hEncoder) { + return; + } + + ReleaseInputBuffers(); + + DestroyHWEncoder(); +} + +void NvEncoder::DestroyHWEncoder() { + if (!m_hEncoder) { + return; + } + +#if defined(_WIN32) + for (uint32_t i = 0; i < m_vpCompletionEvent.size(); i++) { + if (m_vpCompletionEvent[i]) { + if (!m_bIsDX12Encode) { + NV_ENC_EVENT_PARAMS eventParams = {NV_ENC_EVENT_PARAMS_VER}; + eventParams.completionEvent = m_vpCompletionEvent[i]; + m_nvenc.nvEncUnregisterAsyncEvent(m_hEncoder, &eventParams); + } + CloseHandle(m_vpCompletionEvent[i]); + } + } + m_vpCompletionEvent.clear(); +#endif + + if (m_bMotionEstimationOnly) { + DestroyMVOutputBuffer(); + } else { + if (!m_bIsDX12Encode) + DestroyBitstreamBuffer(); + } + + m_nvenc.nvEncDestroyEncoder(m_hEncoder); + + m_hEncoder = nullptr; + + m_bEncoderInitialized = false; +} + +const NvEncInputFrame* NvEncoder::GetNextInputFrame() { + int i = m_iToSend % m_nEncoderBuffer; + return &m_vInputFrames[i]; +} + +const NvEncInputFrame* NvEncoder::GetNextReferenceFrame() { + int i = m_iToSend % m_nEncoderBuffer; + return &m_vReferenceFrames[i]; +} + +void NvEncoder::MapResources(uint32_t bfrIdx) { + NV_ENC_MAP_INPUT_RESOURCE mapInputResource = {NV_ENC_MAP_INPUT_RESOURCE_VER}; + + mapInputResource.registeredResource = m_vRegisteredResources[bfrIdx]; + NVENC_API_CALL(m_nvenc.nvEncMapInputResource(m_hEncoder, &mapInputResource)); + m_vMappedInputBuffers[bfrIdx] = mapInputResource.mappedResource; + + if (m_bMotionEstimationOnly) { + mapInputResource.registeredResource = + m_vRegisteredResourcesForReference[bfrIdx]; + NVENC_API_CALL( + m_nvenc.nvEncMapInputResource(m_hEncoder, &mapInputResource)); + m_vMappedRefBuffers[bfrIdx] = mapInputResource.mappedResource; + } +} + +void NvEncoder::EncodeFrame(std::vector>& vPacket, + NV_ENC_PIC_PARAMS* pPicParams) { + vPacket.clear(); + if (!IsHWEncoderInitialized()) { + NVENC_THROW_ERROR("Encoder device not found", NV_ENC_ERR_NO_ENCODE_DEVICE); + } + + int bfrIdx = m_iToSend % m_nEncoderBuffer; + + MapResources(bfrIdx); + + NVENCSTATUS nvStatus = DoEncode(m_vMappedInputBuffers[bfrIdx], + m_vBitstreamOutputBuffer[bfrIdx], pPicParams); + + if (nvStatus == NV_ENC_SUCCESS || nvStatus == NV_ENC_ERR_NEED_MORE_INPUT) { + m_iToSend++; + GetEncodedPacket(m_vBitstreamOutputBuffer, vPacket, true); + } else { + NVENC_THROW_ERROR("nvEncEncodePicture API failed", nvStatus); + } +} + +void NvEncoder::EncodeExternalFrame( + const NvEncExternalInputFrame& inputFrame, + std::vector>& vPacket, + NV_ENC_PIC_PARAMS* pPicParams) { + vPacket.clear(); + if (!IsHWEncoderInitialized()) { + NVENC_THROW_ERROR("Encoder device not found", NV_ENC_ERR_NO_ENCODE_DEVICE); + } + if (!inputFrame.resource || inputFrame.width <= 0 || inputFrame.height <= 0) { + NVENC_THROW_ERROR("Invalid external input frame", NV_ENC_ERR_INVALID_PARAM); + } + if (m_nOutputDelay != 0) { + NVENC_THROW_ERROR("External input frames require zero output delay", + NV_ENC_ERR_UNSUPPORTED_PARAM); + } + + const int bfrIdx = m_iToSend % m_nEncoderBuffer; + NV_ENC_REGISTERED_PTR registeredResource = nullptr; + + try { + registeredResource = RegisterResource( + inputFrame.resource, inputFrame.resourceType, inputFrame.width, + inputFrame.height, inputFrame.pitch, inputFrame.bufferFormat, + NV_ENC_INPUT_IMAGE, inputFrame.inputFencePoint, + inputFrame.subResourceIndex); + + NV_ENC_MAP_INPUT_RESOURCE mapInputResource = { + NV_ENC_MAP_INPUT_RESOURCE_VER}; + mapInputResource.registeredResource = registeredResource; + NVENC_API_CALL( + m_nvenc.nvEncMapInputResource(m_hEncoder, &mapInputResource)); + m_vMappedInputBuffers[bfrIdx] = mapInputResource.mappedResource; + + NV_ENC_PIC_PARAMS picParams = {}; + if (pPicParams) { + picParams = *pPicParams; + } + picParams.version = NV_ENC_PIC_PARAMS_VER; + picParams.pictureStruct = NV_ENC_PIC_STRUCT_FRAME; + picParams.inputBuffer = m_vMappedInputBuffers[bfrIdx]; + picParams.bufferFmt = inputFrame.bufferFormat; + picParams.inputWidth = inputFrame.width; + picParams.inputHeight = inputFrame.height; + picParams.outputBitstream = m_vBitstreamOutputBuffer[bfrIdx]; + picParams.completionEvent = GetCompletionEvent(bfrIdx); + + NVENCSTATUS nvStatus = + m_nvenc.nvEncEncodePicture(m_hEncoder, &picParams); + + if (nvStatus == NV_ENC_SUCCESS || + nvStatus == NV_ENC_ERR_NEED_MORE_INPUT) { + m_iToSend++; + GetEncodedPacket(m_vBitstreamOutputBuffer, vPacket, true); + } else { + NVENC_THROW_ERROR("nvEncEncodePicture API failed", nvStatus); + } + + if (m_vMappedInputBuffers[bfrIdx]) { + NVENC_API_CALL(m_nvenc.nvEncUnmapInputResource( + m_hEncoder, m_vMappedInputBuffers[bfrIdx])); + m_vMappedInputBuffers[bfrIdx] = nullptr; + } + NVENC_API_CALL( + m_nvenc.nvEncUnregisterResource(m_hEncoder, registeredResource)); + } catch (...) { + if (bfrIdx < static_cast(m_vMappedInputBuffers.size()) && + m_vMappedInputBuffers[bfrIdx]) { + m_nvenc.nvEncUnmapInputResource(m_hEncoder, + m_vMappedInputBuffers[bfrIdx]); + m_vMappedInputBuffers[bfrIdx] = nullptr; + } + if (registeredResource) { + m_nvenc.nvEncUnregisterResource(m_hEncoder, registeredResource); + } + throw; + } +} + +void NvEncoder::RunMotionEstimation(std::vector& mvData) { + if (!m_hEncoder) { + NVENC_THROW_ERROR("Encoder Initialization failed", + NV_ENC_ERR_NO_ENCODE_DEVICE); + return; + } + + const uint32_t bfrIdx = m_iToSend % m_nEncoderBuffer; + + MapResources(bfrIdx); + + NVENCSTATUS nvStatus = DoMotionEstimation(m_vMappedInputBuffers[bfrIdx], + m_vMappedRefBuffers[bfrIdx], + m_vMVDataOutputBuffer[bfrIdx]); + + if (nvStatus == NV_ENC_SUCCESS) { + m_iToSend++; + std::vector> vPacket; + GetEncodedPacket(m_vMVDataOutputBuffer, vPacket, true); + if (vPacket.size() != 1) { + NVENC_THROW_ERROR( + "GetEncodedPacket() doesn't return one (and only one) MVData", + NV_ENC_ERR_GENERIC); + } + mvData = vPacket[0]; + } else { + NVENC_THROW_ERROR("nvEncEncodePicture API failed", nvStatus); + } +} + +void NvEncoder::GetSequenceParams(std::vector& seqParams) { + uint8_t spsppsData[1024]; // Assume maximum spspps data is 1KB or less + memset(spsppsData, 0, sizeof(spsppsData)); + NV_ENC_SEQUENCE_PARAM_PAYLOAD payload = {NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER}; + uint32_t spsppsSize = 0; + + payload.spsppsBuffer = spsppsData; + payload.inBufferSize = sizeof(spsppsData); + payload.outSPSPPSPayloadSize = &spsppsSize; + NVENC_API_CALL(m_nvenc.nvEncGetSequenceParams(m_hEncoder, &payload)); + seqParams.clear(); + seqParams.insert(seqParams.end(), &spsppsData[0], &spsppsData[spsppsSize]); +} + +NVENCSTATUS NvEncoder::DoEncode(NV_ENC_INPUT_PTR inputBuffer, + NV_ENC_OUTPUT_PTR outputBuffer, + NV_ENC_PIC_PARAMS* pPicParams) { + NV_ENC_PIC_PARAMS picParams = {}; + if (pPicParams) { + picParams = *pPicParams; + } + picParams.version = NV_ENC_PIC_PARAMS_VER; + picParams.pictureStruct = NV_ENC_PIC_STRUCT_FRAME; + picParams.inputBuffer = inputBuffer; + picParams.bufferFmt = GetPixelFormat(); + picParams.inputWidth = GetEncodeWidth(); + picParams.inputHeight = GetEncodeHeight(); + picParams.outputBitstream = outputBuffer; + picParams.completionEvent = GetCompletionEvent(m_iToSend % m_nEncoderBuffer); + NVENCSTATUS nvStatus = m_nvenc.nvEncEncodePicture(m_hEncoder, &picParams); + + return nvStatus; +} + +void NvEncoder::SendEOS() { + NV_ENC_PIC_PARAMS picParams = {NV_ENC_PIC_PARAMS_VER}; + picParams.encodePicFlags = NV_ENC_PIC_FLAG_EOS; + picParams.completionEvent = GetCompletionEvent(m_iToSend % m_nEncoderBuffer); + NVENC_API_CALL(m_nvenc.nvEncEncodePicture(m_hEncoder, &picParams)); +} + +void NvEncoder::EndEncode(std::vector>& vPacket) { + vPacket.clear(); + if (!IsHWEncoderInitialized()) { + NVENC_THROW_ERROR("Encoder device not initialized", + NV_ENC_ERR_ENCODER_NOT_INITIALIZED); + } + + SendEOS(); + + GetEncodedPacket(m_vBitstreamOutputBuffer, vPacket, false); +} + +void NvEncoder::GetEncodedPacket(std::vector& vOutputBuffer, + std::vector>& vPacket, + bool bOutputDelay) { + unsigned i = 0; + int iEnd = bOutputDelay ? m_iToSend - m_nOutputDelay : m_iToSend; + for (; m_iGot < iEnd; m_iGot++) { + WaitForCompletionEvent(m_iGot % m_nEncoderBuffer); + NV_ENC_LOCK_BITSTREAM lockBitstreamData = {NV_ENC_LOCK_BITSTREAM_VER}; + lockBitstreamData.outputBitstream = + vOutputBuffer[m_iGot % m_nEncoderBuffer]; + lockBitstreamData.doNotWait = false; + NVENC_API_CALL(m_nvenc.nvEncLockBitstream(m_hEncoder, &lockBitstreamData)); + + uint8_t* pData = (uint8_t*)lockBitstreamData.bitstreamBufferPtr; + if (vPacket.size() < i + 1) { + vPacket.push_back(std::vector()); + } + vPacket[i].clear(); + + if ((m_initializeParams.encodeGUID == NV_ENC_CODEC_AV1_GUID) && + (m_bUseIVFContainer)) { + if (m_bWriteIVFFileHeader) { + m_IVFUtils.WriteFileHeader(vPacket[i], MAKE_FOURCC('A', 'V', '0', '1'), + m_initializeParams.encodeWidth, + m_initializeParams.encodeHeight, + m_initializeParams.frameRateNum, + m_initializeParams.frameRateDen, 0xFFFF); + m_bWriteIVFFileHeader = false; + } + + m_IVFUtils.WriteFrameHeader(vPacket[i], + lockBitstreamData.bitstreamSizeInBytes, + lockBitstreamData.outputTimeStamp); + } + vPacket[i].insert(vPacket[i].end(), &pData[0], + &pData[lockBitstreamData.bitstreamSizeInBytes]); + + i++; + + NVENC_API_CALL(m_nvenc.nvEncUnlockBitstream( + m_hEncoder, lockBitstreamData.outputBitstream)); + + if (m_vMappedInputBuffers[m_iGot % m_nEncoderBuffer]) { + NVENC_API_CALL(m_nvenc.nvEncUnmapInputResource( + m_hEncoder, m_vMappedInputBuffers[m_iGot % m_nEncoderBuffer])); + m_vMappedInputBuffers[m_iGot % m_nEncoderBuffer] = nullptr; + } + + if (m_bMotionEstimationOnly && + m_vMappedRefBuffers[m_iGot % m_nEncoderBuffer]) { + NVENC_API_CALL(m_nvenc.nvEncUnmapInputResource( + m_hEncoder, m_vMappedRefBuffers[m_iGot % m_nEncoderBuffer])); + m_vMappedRefBuffers[m_iGot % m_nEncoderBuffer] = nullptr; + } + } +} + +bool NvEncoder::Reconfigure( + const NV_ENC_RECONFIGURE_PARAMS* pReconfigureParams) { + NVENC_API_CALL(m_nvenc.nvEncReconfigureEncoder( + m_hEncoder, const_cast(pReconfigureParams))); + + memcpy(&m_initializeParams, &(pReconfigureParams->reInitEncodeParams), + sizeof(m_initializeParams)); + if (pReconfigureParams->reInitEncodeParams.encodeConfig) { + memcpy(&m_encodeConfig, pReconfigureParams->reInitEncodeParams.encodeConfig, + sizeof(m_encodeConfig)); + } + + m_nWidth = m_initializeParams.encodeWidth; + m_nHeight = m_initializeParams.encodeHeight; + m_nMaxEncodeWidth = m_initializeParams.maxEncodeWidth; + m_nMaxEncodeHeight = m_initializeParams.maxEncodeHeight; + + return true; +} + +NV_ENC_REGISTERED_PTR NvEncoder::RegisterResource( + void* pBuffer, + NV_ENC_INPUT_RESOURCE_TYPE eResourceType, + int width, + int height, + int pitch, + NV_ENC_BUFFER_FORMAT bufferFormat, + NV_ENC_BUFFER_USAGE bufferUsage, + NV_ENC_FENCE_POINT_D3D12* pInputFencePoint, + uint32_t subResourceIndex) { + NV_ENC_REGISTER_RESOURCE registerResource = {NV_ENC_REGISTER_RESOURCE_VER}; + registerResource.resourceType = eResourceType; + registerResource.resourceToRegister = pBuffer; + registerResource.width = width; + registerResource.height = height; + registerResource.pitch = pitch; + registerResource.subResourceIndex = subResourceIndex; + registerResource.bufferFormat = bufferFormat; + registerResource.bufferUsage = bufferUsage; + registerResource.pInputFencePoint = pInputFencePoint; + NVENC_API_CALL(m_nvenc.nvEncRegisterResource(m_hEncoder, ®isterResource)); + + return registerResource.registeredResource; +} + +void NvEncoder::RegisterInputResources(std::vector inputframes, + NV_ENC_INPUT_RESOURCE_TYPE eResourceType, + int width, + int height, + int pitch, + NV_ENC_BUFFER_FORMAT bufferFormat, + bool bReferenceFrame) { + for (uint32_t i = 0; i < inputframes.size(); ++i) { + NV_ENC_REGISTERED_PTR registeredPtr = + RegisterResource(inputframes[i], eResourceType, width, height, pitch, + bufferFormat, NV_ENC_INPUT_IMAGE); + + std::vector _chromaOffsets; + NvEncoder::GetChromaSubPlaneOffsets(bufferFormat, pitch, height, + _chromaOffsets); + NvEncInputFrame inputframe = {}; + inputframe.inputPtr = (void*)inputframes[i]; + inputframe.chromaOffsets[0] = 0; + inputframe.chromaOffsets[1] = 0; + for (uint32_t ch = 0; ch < _chromaOffsets.size(); ch++) { + inputframe.chromaOffsets[ch] = _chromaOffsets[ch]; + } + inputframe.numChromaPlanes = NvEncoder::GetNumChromaPlanes(bufferFormat); + inputframe.pitch = pitch; + inputframe.chromaPitch = NvEncoder::GetChromaPitch(bufferFormat, pitch); + inputframe.bufferFormat = bufferFormat; + inputframe.resourceType = eResourceType; + + if (bReferenceFrame) { + m_vRegisteredResourcesForReference.push_back(registeredPtr); + m_vReferenceFrames.push_back(inputframe); + } else { + m_vRegisteredResources.push_back(registeredPtr); + m_vInputFrames.push_back(inputframe); + } + } +} + +void NvEncoder::FlushEncoder() { + if (!m_bMotionEstimationOnly && !m_bOutputInVideoMemory) { + // Incase of error it is possible for buffers still mapped to encoder. + // flush the encoder queue and then unmapped it if any surface is still + // mapped + try { + std::vector> vPacket; + EndEncode(vPacket); + } catch (...) { + } + } +} + +void NvEncoder::UnregisterInputResources() { + FlushEncoder(); + + if (m_bMotionEstimationOnly) { + for (uint32_t i = 0; i < m_vMappedRefBuffers.size(); ++i) { + if (m_vMappedRefBuffers[i]) { + m_nvenc.nvEncUnmapInputResource(m_hEncoder, m_vMappedRefBuffers[i]); + } + } + } + m_vMappedRefBuffers.clear(); + + for (uint32_t i = 0; i < m_vMappedInputBuffers.size(); ++i) { + if (m_vMappedInputBuffers[i]) { + m_nvenc.nvEncUnmapInputResource(m_hEncoder, m_vMappedInputBuffers[i]); + } + } + m_vMappedInputBuffers.clear(); + + for (uint32_t i = 0; i < m_vRegisteredResources.size(); ++i) { + if (m_vRegisteredResources[i]) { + m_nvenc.nvEncUnregisterResource(m_hEncoder, m_vRegisteredResources[i]); + } + } + m_vRegisteredResources.clear(); + + for (uint32_t i = 0; i < m_vRegisteredResourcesForReference.size(); ++i) { + if (m_vRegisteredResourcesForReference[i]) { + m_nvenc.nvEncUnregisterResource(m_hEncoder, + m_vRegisteredResourcesForReference[i]); + } + } + m_vRegisteredResourcesForReference.clear(); +} + +void NvEncoder::WaitForCompletionEvent(int iEvent) { +#if defined(_WIN32) + // Check if we are in async mode. If not, don't wait for event; + NV_ENC_CONFIG sEncodeConfig = {0}; + NV_ENC_INITIALIZE_PARAMS sInitializeParams = {0}; + sInitializeParams.encodeConfig = &sEncodeConfig; + GetInitializeParams(&sInitializeParams); + + if (0U == sInitializeParams.enableEncodeAsync) { + return; + } +#ifdef DEBUG + WaitForSingleObject(m_vpCompletionEvent[iEvent], INFINITE); +#else + // wait for 20s which is infinite on terms of gpu time + if (WaitForSingleObject(m_vpCompletionEvent[iEvent], 20000) == WAIT_FAILED) { + NVENC_THROW_ERROR("Failed to encode frame", NV_ENC_ERR_GENERIC); + } +#endif +#endif +} + +uint32_t NvEncoder::GetWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferFormat, + const uint32_t width) { + switch (bufferFormat) { + case NV_ENC_BUFFER_FORMAT_NV12: + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + case NV_ENC_BUFFER_FORMAT_YUV444: + return width; + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + return width * 2; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return width * 4; + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + return 0; + } +} + +uint32_t NvEncoder::GetNumChromaPlanes( + const NV_ENC_BUFFER_FORMAT bufferFormat) { + switch (bufferFormat) { + case NV_ENC_BUFFER_FORMAT_NV12: + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + return 1; + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + case NV_ENC_BUFFER_FORMAT_YUV444: + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + return 2; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return 0; + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + return -1; + } +} + +uint32_t NvEncoder::GetChromaPitch(const NV_ENC_BUFFER_FORMAT bufferFormat, + const uint32_t lumaPitch) { + switch (bufferFormat) { + case NV_ENC_BUFFER_FORMAT_NV12: + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + case NV_ENC_BUFFER_FORMAT_YUV444: + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + return lumaPitch; + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + return (lumaPitch + 1) / 2; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return 0; + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + return -1; + } +} + +void NvEncoder::GetChromaSubPlaneOffsets( + const NV_ENC_BUFFER_FORMAT bufferFormat, + const uint32_t pitch, + const uint32_t height, + std::vector& chromaOffsets) { + chromaOffsets.clear(); + switch (bufferFormat) { + case NV_ENC_BUFFER_FORMAT_NV12: + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + chromaOffsets.push_back(pitch * height); + return; + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + chromaOffsets.push_back(pitch * height); + chromaOffsets.push_back(chromaOffsets[0] + + (NvEncoder::GetChromaPitch(bufferFormat, pitch) * + GetChromaHeight(bufferFormat, height))); + return; + case NV_ENC_BUFFER_FORMAT_YUV444: + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + chromaOffsets.push_back(pitch * height); + chromaOffsets.push_back(chromaOffsets[0] + (pitch * height)); + return; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return; + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + return; + } +} + +uint32_t NvEncoder::GetChromaHeight(const NV_ENC_BUFFER_FORMAT bufferFormat, + const uint32_t lumaHeight) { + switch (bufferFormat) { + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + case NV_ENC_BUFFER_FORMAT_NV12: + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + return (lumaHeight + 1) / 2; + case NV_ENC_BUFFER_FORMAT_YUV444: + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + return lumaHeight; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return 0; + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + return 0; + } +} + +uint32_t NvEncoder::GetChromaWidthInBytes( + const NV_ENC_BUFFER_FORMAT bufferFormat, + const uint32_t lumaWidth) { + switch (bufferFormat) { + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + return (lumaWidth + 1) / 2; + case NV_ENC_BUFFER_FORMAT_NV12: + return lumaWidth; + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + return 2 * lumaWidth; + case NV_ENC_BUFFER_FORMAT_YUV444: + return lumaWidth; + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + return 2 * lumaWidth; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return 0; + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + return 0; + } +} + +int NvEncoder::GetCapabilityValue(GUID guidCodec, NV_ENC_CAPS capsToQuery) { + if (!m_hEncoder) { + return 0; + } + NV_ENC_CAPS_PARAM capsParam = {NV_ENC_CAPS_PARAM_VER}; + capsParam.capsToQuery = capsToQuery; + int v; + m_nvenc.nvEncGetEncodeCaps(m_hEncoder, guidCodec, &capsParam, &v); + return v; +} + +int NvEncoder::GetFrameSize() const { + switch (GetPixelFormat()) { + case NV_ENC_BUFFER_FORMAT_YV12: + case NV_ENC_BUFFER_FORMAT_IYUV: + case NV_ENC_BUFFER_FORMAT_NV12: + return GetEncodeWidth() * + (GetEncodeHeight() + (GetEncodeHeight() + 1) / 2); + case NV_ENC_BUFFER_FORMAT_YUV420_10BIT: + return 2 * GetEncodeWidth() * + (GetEncodeHeight() + (GetEncodeHeight() + 1) / 2); + case NV_ENC_BUFFER_FORMAT_YUV444: + return GetEncodeWidth() * GetEncodeHeight() * 3; + case NV_ENC_BUFFER_FORMAT_YUV444_10BIT: + return 2 * GetEncodeWidth() * GetEncodeHeight() * 3; + case NV_ENC_BUFFER_FORMAT_ARGB: + case NV_ENC_BUFFER_FORMAT_ARGB10: + case NV_ENC_BUFFER_FORMAT_AYUV: + case NV_ENC_BUFFER_FORMAT_ABGR: + case NV_ENC_BUFFER_FORMAT_ABGR10: + return 4 * GetEncodeWidth() * GetEncodeHeight(); + default: + NVENC_THROW_ERROR("Invalid Buffer format", NV_ENC_ERR_INVALID_PARAM); + return 0; + } +} + +void NvEncoder::GetInitializeParams( + NV_ENC_INITIALIZE_PARAMS* pInitializeParams) { + if (!pInitializeParams || !pInitializeParams->encodeConfig) { + NVENC_THROW_ERROR( + "Both pInitializeParams and pInitializeParams->encodeConfig can't be " + "NULL", + NV_ENC_ERR_INVALID_PTR); + } + NV_ENC_CONFIG* pEncodeConfig = pInitializeParams->encodeConfig; + *pEncodeConfig = m_encodeConfig; + *pInitializeParams = m_initializeParams; + pInitializeParams->encodeConfig = pEncodeConfig; +} + +void NvEncoder::InitializeBitstreamBuffer() { + for (int i = 0; i < m_nEncoderBuffer; i++) { + NV_ENC_CREATE_BITSTREAM_BUFFER createBitstreamBuffer = { + NV_ENC_CREATE_BITSTREAM_BUFFER_VER}; + NVENC_API_CALL( + m_nvenc.nvEncCreateBitstreamBuffer(m_hEncoder, &createBitstreamBuffer)); + m_vBitstreamOutputBuffer[i] = createBitstreamBuffer.bitstreamBuffer; + } +} + +void NvEncoder::DestroyBitstreamBuffer() { + for (uint32_t i = 0; i < m_vBitstreamOutputBuffer.size(); i++) { + if (m_vBitstreamOutputBuffer[i]) { + m_nvenc.nvEncDestroyBitstreamBuffer(m_hEncoder, + m_vBitstreamOutputBuffer[i]); + } + } + + m_vBitstreamOutputBuffer.clear(); +} + +void NvEncoder::InitializeMVOutputBuffer() { + for (int i = 0; i < m_nEncoderBuffer; i++) { + NV_ENC_CREATE_MV_BUFFER createMVBuffer = {NV_ENC_CREATE_MV_BUFFER_VER}; + NVENC_API_CALL(m_nvenc.nvEncCreateMVBuffer(m_hEncoder, &createMVBuffer)); + m_vMVDataOutputBuffer.push_back(createMVBuffer.mvBuffer); + } +} + +void NvEncoder::DestroyMVOutputBuffer() { + for (uint32_t i = 0; i < m_vMVDataOutputBuffer.size(); i++) { + if (m_vMVDataOutputBuffer[i]) { + m_nvenc.nvEncDestroyMVBuffer(m_hEncoder, m_vMVDataOutputBuffer[i]); + } + } + + m_vMVDataOutputBuffer.clear(); +} + +NVENCSTATUS NvEncoder::DoMotionEstimation( + NV_ENC_INPUT_PTR inputBuffer, + NV_ENC_INPUT_PTR inputBufferForReference, + NV_ENC_OUTPUT_PTR outputBuffer) { + NV_ENC_MEONLY_PARAMS meParams = {NV_ENC_MEONLY_PARAMS_VER}; + meParams.inputBuffer = inputBuffer; + meParams.referenceFrame = inputBufferForReference; + meParams.inputWidth = GetEncodeWidth(); + meParams.inputHeight = GetEncodeHeight(); + meParams.mvBuffer = outputBuffer; + meParams.completionEvent = GetCompletionEvent(m_iToSend % m_nEncoderBuffer); + NVENCSTATUS nvStatus = + m_nvenc.nvEncRunMotionEstimationOnly(m_hEncoder, &meParams); + + return nvStatus; +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoder.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoder.h new file mode 100644 index 0000000..7f3d5a1 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoder.h @@ -0,0 +1,530 @@ +/* + * Copyright 2017-2022 NVIDIA Corporation. All rights reserved. + * + * Please refer to the NVIDIA end user license agreement (EULA) associated + * with this source code for terms and conditions that govern your use of + * this software. Any use, reproduction, disclosure, or distribution of + * this software and related documentation outside the terms of the EULA + * is strictly prohibited. + * + */ + +#pragma once + +#include +#include + +#include +#include +#include +#include +#include + +#include "Utils/NvCodecUtils.h" +#include "nvEncodeAPI.h" + +/** + * @brief Exception class for error reporting from NvEncodeAPI calls. + */ +class NVENCException : public std::exception { + public: + NVENCException(const std::string& errorStr, const NVENCSTATUS errorCode) + : m_errorString(errorStr), m_errorCode(errorCode) {} + + virtual ~NVENCException() throw() {} + virtual const char* what() const throw() { return m_errorString.c_str(); } + NVENCSTATUS getErrorCode() const { return m_errorCode; } + const std::string& getErrorString() const { return m_errorString; } + static NVENCException makeNVENCException(const std::string& errorStr, + const NVENCSTATUS errorCode, + const std::string& functionName, + const std::string& fileName, + int lineNo); + + private: + std::string m_errorString; + NVENCSTATUS m_errorCode; +}; + +inline NVENCException NVENCException::makeNVENCException( + const std::string& errorStr, + const NVENCSTATUS errorCode, + const std::string& functionName, + const std::string& fileName, + int lineNo) { + std::ostringstream errorLog; + errorLog << functionName << " : " << errorStr << " at " << fileName << ":" + << lineNo << std::endl; + NVENCException exception(errorLog.str(), errorCode); + return exception; +} + +#define NVENC_THROW_ERROR(errorStr, errorCode) \ + do { \ + throw NVENCException::makeNVENCException( \ + errorStr, errorCode, __FUNCTION__, __FILE__, __LINE__); \ + } while (0) + +#define NVENC_API_CALL(nvencAPI) \ + do { \ + NVENCSTATUS errorCode = nvencAPI; \ + if (errorCode != NV_ENC_SUCCESS) { \ + std::ostringstream errorLog; \ + errorLog << #nvencAPI << " returned error " << errorCode; \ + throw NVENCException::makeNVENCException( \ + errorLog.str(), errorCode, __FUNCTION__, __FILE__, __LINE__); \ + } \ + } while (0) + +struct NvEncInputFrame { + void* inputPtr = nullptr; + uint32_t chromaOffsets[2]; + uint32_t numChromaPlanes; + uint32_t pitch; + uint32_t chromaPitch; + NV_ENC_BUFFER_FORMAT bufferFormat; + NV_ENC_INPUT_RESOURCE_TYPE resourceType; +}; + +struct NvEncExternalInputFrame { + void* resource = nullptr; + NV_ENC_INPUT_RESOURCE_TYPE resourceType = NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR; + int width = 0; + int height = 0; + int pitch = 0; + uint32_t subResourceIndex = 0; + NV_ENC_BUFFER_FORMAT bufferFormat = NV_ENC_BUFFER_FORMAT_NV12; + NV_ENC_FENCE_POINT_D3D12* inputFencePoint = nullptr; +}; + +/** + * @brief Shared base class for different encoder interfaces. + */ +class NvEncoder { + public: + /** + * @brief This function is used to initialize the encoder session. + * Application must call this function to initialize the encoder, before + * starting to encode any frames. + */ + virtual void CreateEncoder(const NV_ENC_INITIALIZE_PARAMS* pEncodeParams); + + /** + * @brief This function is used to destroy the encoder session. + * Application must call this function to destroy the encoder session and + * clean up any allocated resources. The application must call EndEncode() + * function to get any queued encoded frames before calling DestroyEncoder(). + */ + virtual void DestroyEncoder(); + + /** + * @brief This function is used to reconfigure an existing encoder session. + * Application can use this function to dynamically change the bitrate, + * resolution and other QOS parameters. If the application changes the + * resolution, it must set NV_ENC_RECONFIGURE_PARAMS::forceIDR. + */ + bool Reconfigure(const NV_ENC_RECONFIGURE_PARAMS* pReconfigureParams); + + /** + * @brief This function is used to get the next available input buffer. + * Applications must call this function to obtain a pointer to the next + * input buffer. The application must copy the uncompressed data to the + * input buffer and then call EncodeFrame() function to encode it. + */ + const NvEncInputFrame* GetNextInputFrame(); + + /** + * @brief This function is used to encode a frame. + * Applications must call EncodeFrame() function to encode the uncompressed + * data, which has been copied to an input buffer obtained from the + * GetNextInputFrame() function. + */ + virtual void EncodeFrame(std::vector>& vPacket, + NV_ENC_PIC_PARAMS* pPicParams = nullptr); + + /** + * @brief Encode one externally-owned CUDA, D3D, or OpenGL resource. + * + * The caller retains ownership of the resource. This method temporarily + * registers and maps it with NVENC, submits the frame, drains the matching + * bitstream packet, then unregisters it. It is intended for zero-copy + * capture paths where the frame already lives in GPU memory. + */ + virtual void EncodeExternalFrame( + const NvEncExternalInputFrame& inputFrame, + std::vector>& vPacket, + NV_ENC_PIC_PARAMS* pPicParams = nullptr); + + /** + * @brief This function to flush the encoder queue. + * The encoder might be queuing frames for B picture encoding or lookahead; + * the application must call EndEncode() to get all the queued encoded frames + * from the encoder. The application must call this function before + * destroying an encoder session. + */ + virtual void EndEncode(std::vector>& vPacket); + + /** + * @brief This function is used to query hardware encoder capabilities. + * Applications can call this function to query capabilities like maximum + * encode dimensions, support for lookahead or the ME-only mode etc. + */ + int GetCapabilityValue(GUID guidCodec, NV_ENC_CAPS capsToQuery); + + /** + * @brief This function is used to get the current device on which encoder + * is running. + */ + void* GetDevice() const { return m_pDevice; } + + /** + * @brief This function is used to get the current device type which encoder + * is running. + */ + NV_ENC_DEVICE_TYPE GetDeviceType() const { return m_eDeviceType; } + + /** + * @brief This function is used to get the current encode width. + * The encode width can be modified by Reconfigure() function. + */ + int GetEncodeWidth() const { return m_nWidth; } + + /** + * @brief This function is used to get the current encode height. + * The encode height can be modified by Reconfigure() function. + */ + int GetEncodeHeight() const { return m_nHeight; } + + /** + * @brief This function is used to get the current frame size based on + * pixel format. + */ + int GetFrameSize() const; + + /** + * @brief This function is used to initialize config parameters based on + * given codec and preset guids. + * The application can call this function to get the default configuration + * for a certain preset. The application can either use these parameters + * directly or override them with application-specific settings before + * using them in CreateEncoder() function. + */ + void CreateDefaultEncoderParams( + NV_ENC_INITIALIZE_PARAMS* pIntializeParams, + GUID codecGuid, + GUID presetGuid, + NV_ENC_TUNING_INFO tuningInfo = NV_ENC_TUNING_INFO_UNDEFINED); + + /** + * @brief This function is used to get the current initialization + * parameters, which had been used to configure the encoder session. The + * initialization parameters are modified if the application calls + * Reconfigure() function. + */ + void GetInitializeParams(NV_ENC_INITIALIZE_PARAMS* pInitializeParams); + + /** + * @brief This function is used to run motion estimation + * This is used to run motion estimation on a a pair of frames. The + * application must copy the reference frame data to the buffer obtained + * by calling GetNextReferenceFrame(), and copy the input frame data to + * the buffer obtained by calling GetNextInputFrame() before calling the + * RunMotionEstimation() function. + */ + void RunMotionEstimation(std::vector& mvData); + + /** + * @brief This function is used to get an available reference frame. + * Application must call this function to get a pointer to reference buffer, + * to be used in the subsequent RunMotionEstimation() function. + */ + const NvEncInputFrame* GetNextReferenceFrame(); + + /** + * @brief This function is used to get sequence and picture parameter + * headers. Application can call this function after encoder is initialized to + * get SPS and PPS nalus for the current encoder instance. The sequence header + * data might change when application calls Reconfigure() function. + */ + void GetSequenceParams(std::vector& seqParams); + + /** + * @brief NvEncoder class virtual destructor. + */ + virtual ~NvEncoder(); + + public: + /** + * @brief This a static function to get chroma offsets for YUV planar + * formats. + */ + static void GetChromaSubPlaneOffsets(const NV_ENC_BUFFER_FORMAT bufferFormat, + const uint32_t pitch, + const uint32_t height, + std::vector& chromaOffsets); + /** + * @brief This a static function to get the chroma plane pitch for YUV planar + * formats. + */ + static uint32_t GetChromaPitch(const NV_ENC_BUFFER_FORMAT bufferFormat, + const uint32_t lumaPitch); + + /** + * @brief This a static function to get the number of chroma planes for YUV + * planar formats. + */ + static uint32_t GetNumChromaPlanes(const NV_ENC_BUFFER_FORMAT bufferFormat); + + /** + * @brief This a static function to get the chroma plane width in bytes for + * YUV planar formats. + */ + static uint32_t GetChromaWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferFormat, + const uint32_t lumaWidth); + + /** + * @brief This a static function to get the chroma planes height in bytes for + * YUV planar formats. + */ + static uint32_t GetChromaHeight(const NV_ENC_BUFFER_FORMAT bufferFormat, + const uint32_t lumaHeight); + + /** + * @brief This a static function to get the width in bytes for the frame. + * For YUV planar format this is the width in bytes of the luma plane. + */ + static uint32_t GetWidthInBytes(const NV_ENC_BUFFER_FORMAT bufferFormat, + const uint32_t width); + + /** + * @brief This function returns the number of allocated buffers. + */ + uint32_t GetEncoderBufferCount() const { return m_nEncoderBuffer; } + + protected: + /** + * @brief NvEncoder class constructor. + * NvEncoder class constructor cannot be called directly by the application. + */ + NvEncoder(NV_ENC_DEVICE_TYPE eDeviceType, + void* pDevice, + uint32_t nWidth, + uint32_t nHeight, + NV_ENC_BUFFER_FORMAT eBufferFormat, + uint32_t nOutputDelay, + bool bMotionEstimationOnly, + bool bOutputInVideoMemory = false, + bool bDX12Encode = false, + bool bUseIVFContainer = true); + + /** + * @brief This function is used to check if hardware encoder is properly + * initialized. + */ + bool IsHWEncoderInitialized() const { + return m_hEncoder != NULL && m_bEncoderInitialized; + } + + /** + * @brief This function is used to register CUDA, D3D or OpenGL input buffers + * with NvEncodeAPI. This is non public function and is called by derived + * class for allocating and registering input buffers. + */ + void RegisterInputResources(std::vector inputframes, + NV_ENC_INPUT_RESOURCE_TYPE eResourceType, + int width, + int height, + int pitch, + NV_ENC_BUFFER_FORMAT bufferFormat, + bool bReferenceFrame = false); + + /** + * @brief This function is used to unregister resources which had been + * previously registered for encoding using RegisterInputResources() function. + */ + void UnregisterInputResources(); + + /** + * @brief This function is used to register CUDA, D3D or OpenGL input or + * output buffers with NvEncodeAPI. + */ + NV_ENC_REGISTERED_PTR RegisterResource( + void* pBuffer, + NV_ENC_INPUT_RESOURCE_TYPE eResourceType, + int width, + int height, + int pitch, + NV_ENC_BUFFER_FORMAT bufferFormat, + NV_ENC_BUFFER_USAGE bufferUsage = NV_ENC_INPUT_IMAGE, + NV_ENC_FENCE_POINT_D3D12* pInputFencePoint = NULL, + uint32_t subResourceIndex = 0); + + /** + * @brief This function returns maximum width used to open the encoder + * session. All encode input buffers are allocated using maximum dimensions. + */ + uint32_t GetMaxEncodeWidth() const { return m_nMaxEncodeWidth; } + + /** + * @brief This function returns maximum height used to open the encoder + * session. All encode input buffers are allocated using maximum dimensions. + */ + uint32_t GetMaxEncodeHeight() const { return m_nMaxEncodeHeight; } + + /** + * @brief This function returns the completion event. + */ + void* GetCompletionEvent(uint32_t eventIdx) { + return (m_vpCompletionEvent.size() == m_nEncoderBuffer) + ? m_vpCompletionEvent[eventIdx] + : nullptr; + } + + /** + * @brief This function returns the current pixel format. + */ + NV_ENC_BUFFER_FORMAT GetPixelFormat() const { return m_eBufferFormat; } + + /** + * @brief This function is used to submit the encode commands to the + * NVENC hardware. + */ + NVENCSTATUS DoEncode(NV_ENC_INPUT_PTR inputBuffer, + NV_ENC_OUTPUT_PTR outputBuffer, + NV_ENC_PIC_PARAMS* pPicParams); + + /** + * @brief This function is used to submit the encode commands to the + * NVENC hardware for ME only mode. + */ + NVENCSTATUS DoMotionEstimation(NV_ENC_INPUT_PTR inputBuffer, + NV_ENC_INPUT_PTR inputBufferForReference, + NV_ENC_OUTPUT_PTR outputBuffer); + + /** + * @brief This function is used to map the input buffers to NvEncodeAPI. + */ + void MapResources(uint32_t bfrIdx); + + /** + * @brief This function is used to wait for completion of encode command. + */ + void WaitForCompletionEvent(int iEvent); + + /** + * @brief This function is used to send EOS to HW encoder. + */ + void SendEOS(); + + private: + /** + * @brief This is a private function which is used to check if there is any + buffering done by encoder. + * The encoder generally buffers data to encode B frames or for lookahead + * or pipelining. + */ + bool IsZeroDelay() { return m_nOutputDelay == 0; } + + /** + * @brief This is a private function which is used to load the encode api + * shared library. + */ + void LoadNvEncApi(); + + /** + * @brief This is a private function which is used to get the output packets + * from the encoder HW. + * This is called by DoEncode() function. If there is buffering enabled, + * this may return without any output data. + */ + void GetEncodedPacket(std::vector& vOutputBuffer, + std::vector>& vPacket, + bool bOutputDelay); + + /** + * @brief This is a private function which is used to initialize the + * bitstream buffers. This is only used in the encoding mode. + */ + void InitializeBitstreamBuffer(); + + /** + * @brief This is a private function which is used to destroy the bitstream + * buffers. This is only used in the encoding mode. + */ + void DestroyBitstreamBuffer(); + + /** + * @brief This is a private function which is used to initialize MV output + * buffers. This is only used in ME-only Mode. + */ + void InitializeMVOutputBuffer(); + + /** + * @brief This is a private function which is used to destroy MV output + * buffers. This is only used in ME-only Mode. + */ + void DestroyMVOutputBuffer(); + + /** + * @brief This is a private function which is used to destroy HW encoder. + */ + void DestroyHWEncoder(); + + /** + * @brief This function is used to flush the encoder queue. + */ + void FlushEncoder(); + + private: + /** + * @brief This is a pure virtual function which is used to allocate input + * buffers. The derived classes must implement this function. + */ + virtual void AllocateInputBuffers(int32_t numInputBuffers) = 0; + + /** + * @brief This is a pure virtual function which is used to destroy input + * buffers. The derived classes must implement this function. + */ + virtual void ReleaseInputBuffers() = 0; + + protected: + bool m_bMotionEstimationOnly = false; + bool m_bOutputInVideoMemory = false; + bool m_bIsDX12Encode = false; + void* m_hEncoder = nullptr; + NV_ENCODE_API_FUNCTION_LIST m_nvenc; + NV_ENC_INITIALIZE_PARAMS m_initializeParams = {}; + std::vector m_vInputFrames; + std::vector m_vRegisteredResources; + std::vector m_vReferenceFrames; + std::vector m_vRegisteredResourcesForReference; + std::vector m_vMappedInputBuffers; + std::vector m_vMappedRefBuffers; + std::vector m_vpCompletionEvent; + + int32_t m_iToSend = 0; + int32_t m_iGot = 0; + int32_t m_nEncoderBuffer = 0; + int32_t m_nOutputDelay = 0; + IVFUtils m_IVFUtils; + bool m_bWriteIVFFileHeader = true; + bool m_bUseIVFContainer = true; + + private: + uint32_t m_nWidth; + uint32_t m_nHeight; + NV_ENC_BUFFER_FORMAT m_eBufferFormat; + void* m_pDevice; + NV_ENC_DEVICE_TYPE m_eDeviceType; + NV_ENC_CONFIG m_encodeConfig = {}; + bool m_bEncoderInitialized = false; + uint32_t m_nExtraOutputDelay = + 3; // To ensure encode and graphics can work in parallel, + // m_nExtraOutputDelay should be set to at least 1 + std::vector m_vBitstreamOutputBuffer; + std::vector m_vMVDataOutputBuffer; + uint32_t m_nMaxEncodeWidth = 0; + uint32_t m_nMaxEncodeHeight = 0; + void* m_hModule = nullptr; +}; diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoderCuda.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoderCuda.cpp new file mode 100644 index 0000000..5a3523c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoderCuda.cpp @@ -0,0 +1,271 @@ +/* + * Copyright 2017-2022 NVIDIA Corporation. All rights reserved. + * + * Please refer to the NVIDIA end user license agreement (EULA) associated + * with this source code for terms and conditions that govern your use of + * this software. Any use, reproduction, disclosure, or distribution of + * this software and related documentation outside the terms of the EULA + * is strictly prohibited. + * + */ + +#include "NvEncoderCuda.h" + +NvEncoderCuda::NvEncoderCuda(CUcontext cuContext, + uint32_t nWidth, + uint32_t nHeight, + NV_ENC_BUFFER_FORMAT eBufferFormat, + uint32_t nExtraOutputDelay, + bool bMotionEstimationOnly, + bool bOutputInVideoMemory, + bool bUseIVFContainer) + : NvEncoder(NV_ENC_DEVICE_TYPE_CUDA, + cuContext, + nWidth, + nHeight, + eBufferFormat, + nExtraOutputDelay, + bMotionEstimationOnly, + bOutputInVideoMemory, + false, + bUseIVFContainer), + m_cuContext(cuContext) { + if (!m_hEncoder) { + NVENC_THROW_ERROR("Encoder Initialization failed", + NV_ENC_ERR_INVALID_DEVICE); + } + + if (!m_cuContext) { + NVENC_THROW_ERROR("Invalid Cuda Context", NV_ENC_ERR_INVALID_DEVICE); + } +} + +NvEncoderCuda::~NvEncoderCuda() { + ReleaseCudaResources(); +} + +void NvEncoderCuda::AllocateInputBuffers(int32_t numInputBuffers) { + if (!IsHWEncoderInitialized()) { + NVENC_THROW_ERROR("Encoder intialization failed", + NV_ENC_ERR_ENCODER_NOT_INITIALIZED); + } + + // for MEOnly mode we need to allocate seperate set of buffers for reference + // frame + int numCount = m_bMotionEstimationOnly ? 2 : 1; + + for (int count = 0; count < numCount; count++) { + CUDA_DRVAPI_CALL(cuCtxPushCurrent(m_cuContext)); + std::vector inputFrames; + for (int i = 0; i < numInputBuffers; i++) { + CUdeviceptr pDeviceFrame; + uint32_t chromaHeight = + GetNumChromaPlanes(GetPixelFormat()) * + GetChromaHeight(GetPixelFormat(), GetMaxEncodeHeight()); + if (GetPixelFormat() == NV_ENC_BUFFER_FORMAT_YV12 || + GetPixelFormat() == NV_ENC_BUFFER_FORMAT_IYUV) + chromaHeight = GetChromaHeight(GetPixelFormat(), GetMaxEncodeHeight()); + CUDA_DRVAPI_CALL(cuMemAllocPitch( + (CUdeviceptr*)&pDeviceFrame, &m_cudaPitch, + GetWidthInBytes(GetPixelFormat(), GetMaxEncodeWidth()), + GetMaxEncodeHeight() + chromaHeight, 16)); + inputFrames.push_back((void*)pDeviceFrame); + } + CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL)); + + RegisterInputResources( + inputFrames, NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR, + GetMaxEncodeWidth(), GetMaxEncodeHeight(), (int)m_cudaPitch, + GetPixelFormat(), (count == 1) ? true : false); + } +} + +void NvEncoderCuda::SetIOCudaStreams(NV_ENC_CUSTREAM_PTR inputStream, + NV_ENC_CUSTREAM_PTR outputStream) { + NVENC_API_CALL( + m_nvenc.nvEncSetIOCudaStreams(m_hEncoder, inputStream, outputStream)); +} + +void NvEncoderCuda::ReleaseInputBuffers() { + ReleaseCudaResources(); +} + +void NvEncoderCuda::ReleaseCudaResources() { + if (!m_hEncoder) { + return; + } + + if (!m_cuContext) { + return; + } + + UnregisterInputResources(); + + cuCtxPushCurrent(m_cuContext); + + for (uint32_t i = 0; i < m_vInputFrames.size(); ++i) { + if (m_vInputFrames[i].inputPtr) { + cuMemFree(reinterpret_cast(m_vInputFrames[i].inputPtr)); + } + } + m_vInputFrames.clear(); + + for (uint32_t i = 0; i < m_vReferenceFrames.size(); ++i) { + if (m_vReferenceFrames[i].inputPtr) { + cuMemFree(reinterpret_cast(m_vReferenceFrames[i].inputPtr)); + } + } + m_vReferenceFrames.clear(); + + cuCtxPopCurrent(NULL); + m_cuContext = nullptr; +} + +void NvEncoderCuda::CopyToDeviceFrame(CUcontext device, + void* pSrcFrame, + uint32_t nSrcPitch, + CUdeviceptr pDstFrame, + uint32_t dstPitch, + int width, + int height, + CUmemorytype srcMemoryType, + NV_ENC_BUFFER_FORMAT pixelFormat, + const uint32_t dstChromaOffsets[], + uint32_t numChromaPlanes, + bool bUnAlignedDeviceCopy, + CUstream stream) { + if (srcMemoryType != CU_MEMORYTYPE_HOST && + srcMemoryType != CU_MEMORYTYPE_DEVICE) { + NVENC_THROW_ERROR("Invalid source memory type for copy", + NV_ENC_ERR_INVALID_PARAM); + } + + CUDA_DRVAPI_CALL(cuCtxPushCurrent(device)); + + uint32_t srcPitch = + nSrcPitch ? nSrcPitch : NvEncoder::GetWidthInBytes(pixelFormat, width); + CUDA_MEMCPY2D m = {0}; + m.srcMemoryType = srcMemoryType; + if (srcMemoryType == CU_MEMORYTYPE_HOST) { + m.srcHost = pSrcFrame; + } else { + m.srcDevice = (CUdeviceptr)pSrcFrame; + } + m.srcPitch = srcPitch; + m.dstMemoryType = CU_MEMORYTYPE_DEVICE; + m.dstDevice = pDstFrame; + m.dstPitch = dstPitch; + m.WidthInBytes = NvEncoder::GetWidthInBytes(pixelFormat, width); + m.Height = height; + if (bUnAlignedDeviceCopy && srcMemoryType == CU_MEMORYTYPE_DEVICE) { + CUDA_DRVAPI_CALL(cuMemcpy2DUnaligned(&m)); + } else { + CUDA_DRVAPI_CALL(stream == NULL ? cuMemcpy2D(&m) + : cuMemcpy2DAsync(&m, stream)); + } + + std::vector srcChromaOffsets; + NvEncoder::GetChromaSubPlaneOffsets(pixelFormat, srcPitch, height, + srcChromaOffsets); + uint32_t chromaHeight = NvEncoder::GetChromaHeight(pixelFormat, height); + uint32_t destChromaPitch = NvEncoder::GetChromaPitch(pixelFormat, dstPitch); + uint32_t srcChromaPitch = NvEncoder::GetChromaPitch(pixelFormat, srcPitch); + uint32_t chromaWidthInBytes = + NvEncoder::GetChromaWidthInBytes(pixelFormat, width); + + for (uint32_t i = 0; i < numChromaPlanes; ++i) { + if (chromaHeight) { + if (srcMemoryType == CU_MEMORYTYPE_HOST) { + m.srcHost = ((uint8_t*)pSrcFrame + srcChromaOffsets[i]); + } else { + m.srcDevice = (CUdeviceptr)((uint8_t*)pSrcFrame + srcChromaOffsets[i]); + } + m.srcPitch = srcChromaPitch; + + m.dstDevice = (CUdeviceptr)((uint8_t*)pDstFrame + dstChromaOffsets[i]); + m.dstPitch = destChromaPitch; + m.WidthInBytes = chromaWidthInBytes; + m.Height = chromaHeight; + if (bUnAlignedDeviceCopy && srcMemoryType == CU_MEMORYTYPE_DEVICE) { + CUDA_DRVAPI_CALL(cuMemcpy2DUnaligned(&m)); + } else { + CUDA_DRVAPI_CALL(stream == NULL ? cuMemcpy2D(&m) + : cuMemcpy2DAsync(&m, stream)); + } + } + } + CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL)); +} + +void NvEncoderCuda::CopyToDeviceFrame(CUcontext device, + void* pSrcFrame, + uint32_t nSrcPitch, + CUdeviceptr pDstFrame, + uint32_t dstPitch, + int width, + int height, + CUmemorytype srcMemoryType, + NV_ENC_BUFFER_FORMAT pixelFormat, + CUdeviceptr dstChromaDevicePtrs[], + uint32_t dstChromaPitch, + uint32_t numChromaPlanes, + bool bUnAlignedDeviceCopy) { + if (srcMemoryType != CU_MEMORYTYPE_HOST && + srcMemoryType != CU_MEMORYTYPE_DEVICE) { + NVENC_THROW_ERROR("Invalid source memory type for copy", + NV_ENC_ERR_INVALID_PARAM); + } + + CUDA_DRVAPI_CALL(cuCtxPushCurrent(device)); + + uint32_t srcPitch = + nSrcPitch ? nSrcPitch : NvEncoder::GetWidthInBytes(pixelFormat, width); + CUDA_MEMCPY2D m = {0}; + m.srcMemoryType = srcMemoryType; + if (srcMemoryType == CU_MEMORYTYPE_HOST) { + m.srcHost = pSrcFrame; + } else { + m.srcDevice = (CUdeviceptr)pSrcFrame; + } + m.srcPitch = srcPitch; + m.dstMemoryType = CU_MEMORYTYPE_DEVICE; + m.dstDevice = pDstFrame; + m.dstPitch = dstPitch; + m.WidthInBytes = NvEncoder::GetWidthInBytes(pixelFormat, width); + m.Height = height; + if (bUnAlignedDeviceCopy && srcMemoryType == CU_MEMORYTYPE_DEVICE) { + CUDA_DRVAPI_CALL(cuMemcpy2DUnaligned(&m)); + } else { + CUDA_DRVAPI_CALL(cuMemcpy2D(&m)); + } + + std::vector srcChromaOffsets; + NvEncoder::GetChromaSubPlaneOffsets(pixelFormat, srcPitch, height, + srcChromaOffsets); + uint32_t chromaHeight = NvEncoder::GetChromaHeight(pixelFormat, height); + uint32_t srcChromaPitch = NvEncoder::GetChromaPitch(pixelFormat, srcPitch); + uint32_t chromaWidthInBytes = + NvEncoder::GetChromaWidthInBytes(pixelFormat, width); + + for (uint32_t i = 0; i < numChromaPlanes; ++i) { + if (chromaHeight) { + if (srcMemoryType == CU_MEMORYTYPE_HOST) { + m.srcHost = ((uint8_t*)pSrcFrame + srcChromaOffsets[i]); + } else { + m.srcDevice = (CUdeviceptr)((uint8_t*)pSrcFrame + srcChromaOffsets[i]); + } + m.srcPitch = srcChromaPitch; + + m.dstDevice = dstChromaDevicePtrs[i]; + m.dstPitch = dstChromaPitch; + m.WidthInBytes = chromaWidthInBytes; + m.Height = chromaHeight; + if (bUnAlignedDeviceCopy && srcMemoryType == CU_MEMORYTYPE_DEVICE) { + CUDA_DRVAPI_CALL(cuMemcpy2DUnaligned(&m)); + } else { + CUDA_DRVAPI_CALL(cuMemcpy2D(&m)); + } + } + } + CUDA_DRVAPI_CALL(cuCtxPopCurrent(NULL)); +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoderCuda.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoderCuda.h new file mode 100644 index 0000000..bd05866 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoderCuda.h @@ -0,0 +1,123 @@ +/* + * Copyright 2017-2022 NVIDIA Corporation. All rights reserved. + * + * Please refer to the NVIDIA end user license agreement (EULA) associated + * with this source code for terms and conditions that govern your use of + * this software. Any use, reproduction, disclosure, or distribution of + * this software and related documentation outside the terms of the EULA + * is strictly prohibited. + * + */ + +#pragma once + +#include +#include + +#include +#include + +#include "NvEncoder.h" + +#define CUDA_DRVAPI_CALL(call) \ + do { \ + CUresult err__ = call; \ + if (err__ != CUDA_SUCCESS) { \ + const char* szErrName = NULL; \ + cuGetErrorName(err__, &szErrName); \ + std::ostringstream errorLog; \ + errorLog << "CUDA driver API error " << szErrName; \ + throw NVENCException::makeNVENCException( \ + errorLog.str(), NV_ENC_ERR_GENERIC, __FUNCTION__, __FILE__, \ + __LINE__); \ + } \ + } while (0) + +/** + * @brief Encoder for CUDA device memory. + */ +class NvEncoderCuda : public NvEncoder { + public: + NvEncoderCuda(CUcontext cuContext, + uint32_t nWidth, + uint32_t nHeight, + NV_ENC_BUFFER_FORMAT eBufferFormat, + uint32_t nExtraOutputDelay = 3, + bool bMotionEstimationOnly = false, + bool bOPInVideoMemory = false, + bool bUseIVFContainer = true); + virtual ~NvEncoderCuda(); + + /** + * @brief This is a static function to copy input data from host memory to + * device memory. This function assumes YUV plane is a single contiguous + * memory segment. + */ + static void CopyToDeviceFrame(CUcontext device, + void* pSrcFrame, + uint32_t nSrcPitch, + CUdeviceptr pDstFrame, + uint32_t dstPitch, + int width, + int height, + CUmemorytype srcMemoryType, + NV_ENC_BUFFER_FORMAT pixelFormat, + const uint32_t dstChromaOffsets[], + uint32_t numChromaPlanes, + bool bUnAlignedDeviceCopy = false, + CUstream stream = NULL); + + /** + * @brief This is a static function to copy input data from host memory to + * device memory. Application must pass a seperate device pointer for each YUV + * plane. + */ + static void CopyToDeviceFrame(CUcontext device, + void* pSrcFrame, + uint32_t nSrcPitch, + CUdeviceptr pDstFrame, + uint32_t dstPitch, + int width, + int height, + CUmemorytype srcMemoryType, + NV_ENC_BUFFER_FORMAT pixelFormat, + CUdeviceptr dstChromaPtr[], + uint32_t dstChromaPitch, + uint32_t numChromaPlanes, + bool bUnAlignedDeviceCopy = false); + + /** + * @brief This function sets input and output CUDA streams + */ + void SetIOCudaStreams(NV_ENC_CUSTREAM_PTR inputStream, + NV_ENC_CUSTREAM_PTR outputStream); + + protected: + /** + * @brief This function is used to release the input buffers allocated for + * encoding. This function is an override of virtual function + * NvEncoder::ReleaseInputBuffers(). + */ + virtual void ReleaseInputBuffers() override; + + private: + /** + * @brief This function is used to allocate input buffers for encoding. + * This function is an override of virtual function + * NvEncoder::AllocateInputBuffers(). + */ + virtual void AllocateInputBuffers(int32_t numInputBuffers) override; + + private: + /** + * @brief This is a private function to release CUDA device memory used for + * encoding. + */ + void ReleaseCudaResources(); + + protected: + CUcontext m_cuContext; + + private: + size_t m_cudaPitch = 0; +}; diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/README.txt b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/README.txt new file mode 100644 index 0000000..b0ea97f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/README.txt @@ -0,0 +1,2 @@ +Source code under the directory are copied from this repository. +https://github.com/NVIDIA/video-sdk-samples/tree/master/Samples \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/include/Utils/Logger.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/include/Utils/Logger.h new file mode 100644 index 0000000..a0ae5a0 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/include/Utils/Logger.h @@ -0,0 +1,240 @@ +/* +* Copyright 2017-2022 NVIDIA Corporation. All rights reserved. +* +* Please refer to the NVIDIA end user license agreement (EULA) associated +* with this source code for terms and conditions that govern your use of +* this software. Any use, reproduction, disclosure, or distribution of +* this software and related documentation outside the terms of the EULA +* is strictly prohibited. +* +*/ + +#pragma once + +#include +#include +#include +#include +#include +#include + +#ifdef _WIN32 +#include +#include + +#pragma comment(lib, "ws2_32.lib") +#undef ERROR +#else +#include +#include +#include +#include +#define SOCKET int +#define INVALID_SOCKET -1 +#endif + +enum LogLevel { + TRACE, + INFO, + WARNING, + ERROR, + FATAL +}; + +namespace simplelogger{ +class Logger { +public: + Logger(LogLevel level, bool bPrintTimeStamp) : level(level), bPrintTimeStamp(bPrintTimeStamp) {} + virtual ~Logger() {} + virtual std::ostream& GetStream() = 0; + virtual void FlushStream() {} + bool ShouldLogFor(LogLevel l) { + return l >= level; + } + char* GetLead(LogLevel l, const char *szFile, int nLine, const char *szFunc) { + if (l < TRACE || l > FATAL) { + sprintf(szLead, "[?????] "); + return szLead; + } + const char *szLevels[] = {"TRACE", "INFO", "WARN", "ERROR", "FATAL"}; + if (bPrintTimeStamp) { + time_t t = time(NULL); + struct tm *ptm = localtime(&t); + sprintf(szLead, "[%-5s][%02d:%02d:%02d] ", + szLevels[l], ptm->tm_hour, ptm->tm_min, ptm->tm_sec); + } else { + sprintf(szLead, "[%-5s] ", szLevels[l]); + } + return szLead; + } + void EnterCriticalSection() { + mtx.lock(); + } + void LeaveCriticalSection() { + mtx.unlock(); + } +private: + LogLevel level; + char szLead[80]; + bool bPrintTimeStamp; + std::mutex mtx; +}; + +class LoggerFactory { +public: + static Logger* CreateFileLogger(std::string strFilePath, + LogLevel level = INFO, bool bPrintTimeStamp = true) { + return new FileLogger(strFilePath, level, bPrintTimeStamp); + } + static Logger* CreateConsoleLogger(LogLevel level = INFO, + bool bPrintTimeStamp = true) { + return new ConsoleLogger(level, bPrintTimeStamp); + } + static Logger* CreateUdpLogger(char *szHost, unsigned uPort, LogLevel level = INFO, + bool bPrintTimeStamp = true) { + return new UdpLogger(szHost, uPort, level, bPrintTimeStamp); + } +private: + LoggerFactory() {} + + class FileLogger : public Logger { + public: + FileLogger(std::string strFilePath, LogLevel level, bool bPrintTimeStamp) + : Logger(level, bPrintTimeStamp) { + pFileOut = new std::ofstream(); + pFileOut->open(strFilePath.c_str()); + } + ~FileLogger() { + pFileOut->close(); + } + std::ostream& GetStream() { + return *pFileOut; + } + private: + std::ofstream *pFileOut; + }; + + class ConsoleLogger : public Logger { + public: + ConsoleLogger(LogLevel level, bool bPrintTimeStamp) + : Logger(level, bPrintTimeStamp) {} + std::ostream& GetStream() { + return std::cout; + } + }; + + class UdpLogger : public Logger { + private: + class UdpOstream : public std::ostream { + public: + UdpOstream(char *szHost, unsigned short uPort) : std::ostream(&sb), socket(INVALID_SOCKET){ +#ifdef _WIN32 + WSADATA w; + if (WSAStartup(0x0101, &w) != 0) { + fprintf(stderr, "WSAStartup() failed.\n"); + return; + } +#endif + socket = ::socket(AF_INET, SOCK_DGRAM, 0); + if (socket == INVALID_SOCKET) { +#ifdef _WIN32 + WSACleanup(); +#endif + fprintf(stderr, "socket() failed.\n"); + return; + } +#ifdef _WIN32 + unsigned int b1, b2, b3, b4; + sscanf(szHost, "%u.%u.%u.%u", &b1, &b2, &b3, &b4); + struct in_addr addr = {(unsigned char)b1, (unsigned char)b2, (unsigned char)b3, (unsigned char)b4}; +#else + struct in_addr addr = {inet_addr(szHost)}; +#endif + struct sockaddr_in s = {AF_INET, htons(uPort), addr}; + server = s; + } + ~UdpOstream() throw() { + if (socket == INVALID_SOCKET) { + return; + } +#ifdef _WIN32 + closesocket(socket); + WSACleanup(); +#else + close(socket); +#endif + } + void Flush() { + if (sendto(socket, sb.str().c_str(), (int)sb.str().length() + 1, + 0, (struct sockaddr *)&server, (int)sizeof(sockaddr_in)) == -1) { + fprintf(stderr, "sendto() failed.\n"); + } + sb.str(""); + } + + private: + std::stringbuf sb; + SOCKET socket; + struct sockaddr_in server; + }; + public: + UdpLogger(char *szHost, unsigned uPort, LogLevel level, bool bPrintTimeStamp) + : Logger(level, bPrintTimeStamp), udpOut(szHost, (unsigned short)uPort) {} + UdpOstream& GetStream() { + return udpOut; + } + virtual void FlushStream() { + udpOut.Flush(); + } + private: + UdpOstream udpOut; + }; +}; + +class LogTransaction { +public: + LogTransaction(Logger *pLogger, LogLevel level, const char *szFile, const int nLine, const char *szFunc) : pLogger(pLogger), level(level) { + if (!pLogger) { + std::cout << "[-----] "; + return; + } + if (!pLogger->ShouldLogFor(level)) { + return; + } + pLogger->EnterCriticalSection(); + pLogger->GetStream() << pLogger->GetLead(level, szFile, nLine, szFunc); + } + ~LogTransaction() { + if (!pLogger) { + std::cout << std::endl; + return; + } + if (!pLogger->ShouldLogFor(level)) { + return; + } + pLogger->GetStream() << std::endl; + pLogger->FlushStream(); + pLogger->LeaveCriticalSection(); + if (level == FATAL) { + exit(1); + } + } + std::ostream& GetStream() { + if (!pLogger) { + return std::cout; + } + if (!pLogger->ShouldLogFor(level)) { + return ossNull; + } + return pLogger->GetStream(); + } +private: + Logger *pLogger; + LogLevel level; + std::ostringstream ossNull; +}; + +} + +extern simplelogger::Logger *logger; +#define LOG(level) simplelogger::LogTransaction(logger, level, __FILE__, __LINE__, __FUNCTION__).GetStream() diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/include/Utils/NvCodecUtils.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/include/Utils/NvCodecUtils.h new file mode 100644 index 0000000..47bb940 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/include/Utils/NvCodecUtils.h @@ -0,0 +1,537 @@ +/* +* Copyright 2017-2022 NVIDIA Corporation. All rights reserved. +* +* Please refer to the NVIDIA end user license agreement (EULA) associated +* with this source code for terms and conditions that govern your use of +* this software. Any use, reproduction, disclosure, or distribution of +* this software and related documentation outside the terms of the EULA +* is strictly prohibited. +* +*/ + +//--------------------------------------------------------------------------- +//! \file NvCodecUtils.h +//! \brief Miscellaneous classes and error checking functions. +//! +//! Used by Transcode/Encode samples apps for reading input files, mutithreading, performance measurement or colorspace conversion while decoding. +//--------------------------------------------------------------------------- + +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Logger.h" + +extern simplelogger::Logger *logger; + +#ifdef __cuda_cuda_h__ +inline bool check(CUresult e, int iLine, const char *szFile) { + if (e != CUDA_SUCCESS) { + const char *szErrName = NULL; + cuGetErrorName(e, &szErrName); + LOG(FATAL) << "CUDA driver API error " << szErrName << " at line " << iLine << " in file " << szFile; + return false; + } + return true; +} +#endif + +#ifdef __CUDA_RUNTIME_H__ +inline bool check(cudaError_t e, int iLine, const char *szFile) { + if (e != cudaSuccess) { + LOG(FATAL) << "CUDA runtime API error " << cudaGetErrorName(e) << " at line " << iLine << " in file " << szFile; + return false; + } + return true; +} +#endif + +#ifdef _NV_ENCODEAPI_H_ +inline bool check(NVENCSTATUS e, int iLine, const char *szFile) { + const char *aszErrName[] = { + "NV_ENC_SUCCESS", + "NV_ENC_ERR_NO_ENCODE_DEVICE", + "NV_ENC_ERR_UNSUPPORTED_DEVICE", + "NV_ENC_ERR_INVALID_ENCODERDEVICE", + "NV_ENC_ERR_INVALID_DEVICE", + "NV_ENC_ERR_DEVICE_NOT_EXIST", + "NV_ENC_ERR_INVALID_PTR", + "NV_ENC_ERR_INVALID_EVENT", + "NV_ENC_ERR_INVALID_PARAM", + "NV_ENC_ERR_INVALID_CALL", + "NV_ENC_ERR_OUT_OF_MEMORY", + "NV_ENC_ERR_ENCODER_NOT_INITIALIZED", + "NV_ENC_ERR_UNSUPPORTED_PARAM", + "NV_ENC_ERR_LOCK_BUSY", + "NV_ENC_ERR_NOT_ENOUGH_BUFFER", + "NV_ENC_ERR_INVALID_VERSION", + "NV_ENC_ERR_MAP_FAILED", + "NV_ENC_ERR_NEED_MORE_INPUT", + "NV_ENC_ERR_ENCODER_BUSY", + "NV_ENC_ERR_EVENT_NOT_REGISTERD", + "NV_ENC_ERR_GENERIC", + "NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY", + "NV_ENC_ERR_UNIMPLEMENTED", + "NV_ENC_ERR_RESOURCE_REGISTER_FAILED", + "NV_ENC_ERR_RESOURCE_NOT_REGISTERED", + "NV_ENC_ERR_RESOURCE_NOT_MAPPED", + }; + if (e != NV_ENC_SUCCESS) { + LOG(FATAL) << "NVENC error " << aszErrName[e] << " at line " << iLine << " in file " << szFile; + return false; + } + return true; +} +#endif + +#ifdef _WINERROR_ +inline bool check(HRESULT e, int iLine, const char *szFile) { + if (e != S_OK) { + std::stringstream stream; + stream << std::hex << std::uppercase << e; + LOG(FATAL) << "HRESULT error 0x" << stream.str() << " at line " << iLine << " in file " << szFile; + return false; + } + return true; +} +#endif + +#if defined(__gl_h_) || defined(__GL_H__) +inline bool check(GLenum e, int iLine, const char *szFile) { + if (e != 0) { + LOG(ERROR) << "GLenum error " << e << " at line " << iLine << " in file " << szFile; + return false; + } + return true; +} +#endif + +inline bool check(int e, int iLine, const char *szFile) { + if (e < 0) { + LOG(ERROR) << "General error " << e << " at line " << iLine << " in file " << szFile; + return false; + } + return true; +} + +#define ck(call) check(call, __LINE__, __FILE__) +#define MAKE_FOURCC( ch0, ch1, ch2, ch3 ) \ + ( (uint32_t)(uint8_t)(ch0) | ( (uint32_t)(uint8_t)(ch1) << 8 ) | \ + ( (uint32_t)(uint8_t)(ch2) << 16 ) | ( (uint32_t)(uint8_t)(ch3) << 24 ) ) + +/** +* @brief Wrapper class around std::thread +*/ +class NvThread +{ +public: + NvThread() = default; + NvThread(const NvThread&) = delete; + NvThread& operator=(const NvThread& other) = delete; + + NvThread(std::thread&& thread) : t(std::move(thread)) + { + + } + + NvThread(NvThread&& thread) : t(std::move(thread.t)) + { + + } + + NvThread& operator=(NvThread&& other) + { + t = std::move(other.t); + return *this; + } + + ~NvThread() + { + join(); + } + + void join() + { + if (t.joinable()) + { + t.join(); + } + } +private: + std::thread t; +}; + +#ifndef _WIN32 +#define _stricmp strcasecmp +#define _stat64 stat64 +#endif + +/** +* @brief Utility class to allocate buffer memory. Helps avoid I/O during the encode/decode loop in case of performance tests. +*/ +class BufferedFileReader { +public: + /** + * @brief Constructor function to allocate appropriate memory and copy file contents into it + */ + BufferedFileReader(const char *szFileName, bool bPartial = false) { + struct _stat64 st; + + if (_stat64(szFileName, &st) != 0) { + return; + } + + nSize = st.st_size; + while (nSize) { + try { + pBuf = new uint8_t[(size_t)nSize]; + if (nSize != st.st_size) { + LOG(WARNING) << "File is too large - only " << std::setprecision(4) << 100.0 * nSize / st.st_size << "% is loaded"; + } + break; + } catch(std::bad_alloc) { + if (!bPartial) { + LOG(ERROR) << "Failed to allocate memory in BufferedReader"; + return; + } + nSize = (uint32_t)(nSize * 0.9); + } + } + + std::ifstream fpIn(szFileName, std::ifstream::in | std::ifstream::binary); + if (!fpIn) + { + LOG(ERROR) << "Unable to open input file: " << szFileName; + return; + } + + std::streamsize nRead = fpIn.read(reinterpret_cast(pBuf), nSize).gcount(); + fpIn.close(); + + assert(nRead == nSize); + } + ~BufferedFileReader() { + if (pBuf) { + delete[] pBuf; + } + } + bool GetBuffer(uint8_t **ppBuf, uint64_t *pnSize) { + if (!pBuf) { + return false; + } + + *ppBuf = pBuf; + *pnSize = nSize; + return true; + } + +private: + uint8_t *pBuf = NULL; + uint64_t nSize = 0; +}; + +/** +* @brief Template class to facilitate color space conversion +*/ +template +class YuvConverter { +public: + YuvConverter(int nWidth, int nHeight) : nWidth(nWidth), nHeight(nHeight) { + pQuad = new T[((nWidth + 1) / 2) * ((nHeight + 1) / 2)]; + } + ~YuvConverter() { + delete[] pQuad; + } + void PlanarToUVInterleaved(T *pFrame, int nPitch = 0) { + if (nPitch == 0) { + nPitch = nWidth; + } + + // sizes of source surface plane + int nSizePlaneY = nPitch * nHeight; + int nSizePlaneU = ((nPitch + 1) / 2) * ((nHeight + 1) / 2); + int nSizePlaneV = nSizePlaneU; + + T *puv = pFrame + nSizePlaneY; + if (nPitch == nWidth) { + memcpy(pQuad, puv, nSizePlaneU * sizeof(T)); + } else { + for (int i = 0; i < (nHeight + 1) / 2; i++) { + memcpy(pQuad + ((nWidth + 1) / 2) * i, puv + ((nPitch + 1) / 2) * i, ((nWidth + 1) / 2) * sizeof(T)); + } + } + T *pv = puv + nSizePlaneU; + for (int y = 0; y < (nHeight + 1) / 2; y++) { + for (int x = 0; x < (nWidth + 1) / 2; x++) { + puv[y * nPitch + x * 2] = pQuad[y * ((nWidth + 1) / 2) + x]; + puv[y * nPitch + x * 2 + 1] = pv[y * ((nPitch + 1) / 2) + x]; + } + } + } + void UVInterleavedToPlanar(T *pFrame, int nPitch = 0) { + if (nPitch == 0) { + nPitch = nWidth; + } + + // sizes of source surface plane + int nSizePlaneY = nPitch * nHeight; + int nSizePlaneU = ((nPitch + 1) / 2) * ((nHeight + 1) / 2); + int nSizePlaneV = nSizePlaneU; + + T *puv = pFrame + nSizePlaneY, + *pu = puv, + *pv = puv + nSizePlaneU; + + // split chroma from interleave to planar + for (int y = 0; y < (nHeight + 1) / 2; y++) { + for (int x = 0; x < (nWidth + 1) / 2; x++) { + pu[y * ((nPitch + 1) / 2) + x] = puv[y * nPitch + x * 2]; + pQuad[y * ((nWidth + 1) / 2) + x] = puv[y * nPitch + x * 2 + 1]; + } + } + if (nPitch == nWidth) { + memcpy(pv, pQuad, nSizePlaneV * sizeof(T)); + } else { + for (int i = 0; i < (nHeight + 1) / 2; i++) { + memcpy(pv + ((nPitch + 1) / 2) * i, pQuad + ((nWidth + 1) / 2) * i, ((nWidth + 1) / 2) * sizeof(T)); + } + } + } + +private: + T *pQuad; + int nWidth, nHeight; +}; + +/** +* @brief Class for writing IVF format header for AV1 codec +*/ +class IVFUtils { +public: + void WriteFileHeader(std::vector &vPacket, uint32_t nFourCC, uint32_t nWidth, uint32_t nHeight, uint32_t nFrameRateNum, uint32_t nFrameRateDen, uint32_t nFrameCnt) + { + char header[32]; + + header[0] = 'D'; + header[1] = 'K'; + header[2] = 'I'; + header[3] = 'F'; + mem_put_le16(header + 4, 0); // version + mem_put_le16(header + 6, 32); // header size + mem_put_le32(header + 8, nFourCC); // fourcc + mem_put_le16(header + 12, nWidth); // width + mem_put_le16(header + 14, nHeight); // height + mem_put_le32(header + 16, nFrameRateNum); // rate + mem_put_le32(header + 20, nFrameRateDen); // scale + mem_put_le32(header + 24, nFrameCnt); // length + mem_put_le32(header + 28, 0); // unused + + vPacket.insert(vPacket.end(), &header[0], &header[32]); + } + + void WriteFrameHeader(std::vector &vPacket, size_t nFrameSize, int64_t pts) + { + char header[12]; + mem_put_le32(header, (int)nFrameSize); + mem_put_le32(header + 4, (int)(pts & 0xFFFFFFFF)); + mem_put_le32(header + 8, (int)(pts >> 32)); + + vPacket.insert(vPacket.end(), &header[0], &header[12]); + } + +private: + static inline void mem_put_le32(void *vmem, int val) + { + unsigned char *mem = (unsigned char *)vmem; + mem[0] = (unsigned char)((val >> 0) & 0xff); + mem[1] = (unsigned char)((val >> 8) & 0xff); + mem[2] = (unsigned char)((val >> 16) & 0xff); + mem[3] = (unsigned char)((val >> 24) & 0xff); + } + + static inline void mem_put_le16(void *vmem, int val) + { + unsigned char *mem = (unsigned char *)vmem; + mem[0] = (unsigned char)((val >> 0) & 0xff); + mem[1] = (unsigned char)((val >> 8) & 0xff); + } + +}; + +/** +* @brief Utility class to measure elapsed time in seconds between the block of executed code +*/ +class StopWatch { +public: + void Start() { + t0 = std::chrono::high_resolution_clock::now(); + } + double Stop() { + return std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch() - t0.time_since_epoch()).count() / 1.0e9; + } + +private: + std::chrono::high_resolution_clock::time_point t0; +}; + +template +class ConcurrentQueue +{ + public: + + ConcurrentQueue() {} + ConcurrentQueue(size_t size) : maxSize(size) {} + ConcurrentQueue(const ConcurrentQueue&) = delete; + ConcurrentQueue& operator=(const ConcurrentQueue&) = delete; + + void setSize(size_t s) { + maxSize = s; + } + + void push_back(const T& value) { + // Do not use a std::lock_guard here. We will need to explicitly + // unlock before notify_one as the other waiting thread will + // automatically try to acquire mutex once it wakes up + // (which will happen on notify_one) + std::unique_lock lock(m_mutex); + auto wasEmpty = m_List.empty(); + + while (full()) { + m_cond.wait(lock); + } + + m_List.push_back(value); + if (wasEmpty && !m_List.empty()) { + lock.unlock(); + m_cond.notify_one(); + } + } + + T pop_front() { + std::unique_lock lock(m_mutex); + + while (m_List.empty()) { + m_cond.wait(lock); + } + auto wasFull = full(); + T data = std::move(m_List.front()); + m_List.pop_front(); + + if (wasFull && !full()) { + lock.unlock(); + m_cond.notify_one(); + } + + return data; + } + + T front() { + std::unique_lock lock(m_mutex); + + while (m_List.empty()) { + m_cond.wait(lock); + } + + return m_List.front(); + } + + size_t size() { + std::unique_lock lock(m_mutex); + return m_List.size(); + } + + bool empty() { + std::unique_lock lock(m_mutex); + return m_List.empty(); + } + void clear() { + std::unique_lock lock(m_mutex); + m_List.clear(); + } + +private: + bool full() { + if (m_List.size() == maxSize) + return true; + return false; + } + +private: + std::list m_List; + std::mutex m_mutex; + std::condition_variable m_cond; + size_t maxSize; +}; + +inline void CheckInputFile(const char *szInFilePath) { + std::ifstream fpIn(szInFilePath, std::ios::in | std::ios::binary); + if (fpIn.fail()) { + std::ostringstream err; + err << "Unable to open input file: " << szInFilePath << std::endl; + throw std::invalid_argument(err.str()); + } +} + +inline void ValidateResolution(int nWidth, int nHeight) { + + if (nWidth <= 0 || nHeight <= 0) { + std::ostringstream err; + err << "Please specify positive non zero resolution as -s WxH. Current resolution is " << nWidth << "x" << nHeight << std::endl; + throw std::invalid_argument(err.str()); + } +} + +template +void Nv12ToColor32(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 0); +template +void Nv12ToColor64(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 0); + +template +void P016ToColor32(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 4); +template +void P016ToColor64(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 4); + +template +void YUV444ToColor32(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 0); +template +void YUV444ToColor64(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 0); + +template +void YUV444P16ToColor32(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 4); +template +void YUV444P16ToColor64(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgra, int nBgraPitch, int nWidth, int nHeight, int iMatrix = 4); + +template +void Nv12ToColorPlanar(uint8_t *dpNv12, int nNv12Pitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix = 0); +template +void P016ToColorPlanar(uint8_t *dpP016, int nP016Pitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix = 4); + +template +void YUV444ToColorPlanar(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix = 0); +template +void YUV444P16ToColorPlanar(uint8_t *dpYUV444, int nPitch, uint8_t *dpBgrp, int nBgrpPitch, int nWidth, int nHeight, int iMatrix = 4); + +void Bgra64ToP016(uint8_t *dpBgra, int nBgraPitch, uint8_t *dpP016, int nP016Pitch, int nWidth, int nHeight, int iMatrix = 4); + +void ConvertUInt8ToUInt16(uint8_t *dpUInt8, uint16_t *dpUInt16, int nSrcPitch, int nDestPitch, int nWidth, int nHeight); +void ConvertUInt16ToUInt8(uint16_t *dpUInt16, uint8_t *dpUInt8, int nSrcPitch, int nDestPitch, int nWidth, int nHeight); + +void ResizeNv12(unsigned char *dpDstNv12, int nDstPitch, int nDstWidth, int nDstHeight, unsigned char *dpSrcNv12, int nSrcPitch, int nSrcWidth, int nSrcHeight, unsigned char *dpDstNv12UV = nullptr); +void ResizeP016(unsigned char *dpDstP016, int nDstPitch, int nDstWidth, int nDstHeight, unsigned char *dpSrcP016, int nSrcPitch, int nSrcWidth, int nSrcHeight, unsigned char *dpDstP016UV = nullptr); + +void ScaleYUV420(unsigned char *dpDstY, unsigned char* dpDstU, unsigned char* dpDstV, int nDstPitch, int nDstChromaPitch, int nDstWidth, int nDstHeight, + unsigned char *dpSrcY, unsigned char* dpSrcU, unsigned char* dpSrcV, int nSrcPitch, int nSrcChromaPitch, int nSrcWidth, int nSrcHeight, bool bSemiplanar); + +#ifdef __cuda_cuda_h__ +void ComputeCRC(uint8_t *pBuffer, uint32_t *crcValue, CUstream_st *outputCUStream); +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/include/cuda.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/include/cuda.h new file mode 100644 index 0000000..fa73eb1 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/include/cuda.h @@ -0,0 +1,230 @@ +/* + * SPDX-License-Identifier: MIT + * + * Minimal vendored CUDA Driver API declarations used by Fluxer's native + * LiveKit/WebRTC NVENC bridge. This is intentionally not the CUDA Toolkit + * header; it keeps the build independent of a system CUDA SDK while still + * compiling against the NVIDIA driver API that is loaded dynamically at + * runtime. + */ + +#ifndef __cuda_cuda_h__ +#define __cuda_cuda_h__ + +#include + +#ifdef _WIN32 +#define CUDAAPI __stdcall +#else +#define CUDAAPI +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef CUDA_VERSION +#define CUDA_VERSION 12000 +#endif + +typedef enum CUresult_enum { + CUDA_SUCCESS = 0, + CUDA_ERROR_INVALID_VALUE = 1, + CUDA_ERROR_OUT_OF_MEMORY = 2, + CUDA_ERROR_NOT_INITIALIZED = 3, + CUDA_ERROR_DEINITIALIZED = 4, + CUDA_ERROR_NOT_SUPPORTED = 801 +} CUresult; + +typedef int CUdevice; +typedef struct CUctx_st* CUcontext; +typedef struct CUstream_st* CUstream; +typedef struct CUarray_st* CUarray; +typedef struct CUgraphicsResource_st* CUgraphicsResource; +typedef unsigned long long CUdeviceptr; + +typedef enum CUmemorytype_enum { + CU_MEMORYTYPE_HOST = 0x01, + CU_MEMORYTYPE_DEVICE = 0x02, + CU_MEMORYTYPE_ARRAY = 0x03, + CU_MEMORYTYPE_UNIFIED = 0x04 +} CUmemorytype; + +typedef enum CUdevice_attribute_enum { + CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR = 75 +} CUdevice_attribute; + +typedef enum CUeglFrameType_enum { + CU_EGL_FRAME_TYPE_ARRAY = 0, + CU_EGL_FRAME_TYPE_PITCH = 1 +} CUeglFrameType; + +typedef enum CUeglColorFormat_enum { + CU_EGL_COLOR_FORMAT_YUV420_PLANAR = 0x00, + CU_EGL_COLOR_FORMAT_YUV420_SEMIPLANAR = 0x01, + CU_EGL_COLOR_FORMAT_YUV420_SEMIPLANAR_ER = 0x02, + CU_EGL_COLOR_FORMAT_YVU420_SEMIPLANAR = 0x03, + CU_EGL_COLOR_FORMAT_YVU420_SEMIPLANAR_ER = 0x04, + CU_EGL_COLOR_FORMAT_ARGB = 0x05, + CU_EGL_COLOR_FORMAT_RGBA = 0x06, + CU_EGL_COLOR_FORMAT_L = 0x07, + CU_EGL_COLOR_FORMAT_R = 0x08, + CU_EGL_COLOR_FORMAT_YUV444_PLANAR = 0x09, + CU_EGL_COLOR_FORMAT_YUV444_SEMIPLANAR = 0x0a, + CU_EGL_COLOR_FORMAT_YVU444_SEMIPLANAR = 0x0b, + CU_EGL_COLOR_FORMAT_Y = 0x0c, + CU_EGL_COLOR_FORMAT_YUVY = 0x0d, + CU_EGL_COLOR_FORMAT_UYVY = 0x0e, + CU_EGL_COLOR_FORMAT_ABGR = 0x0f, + CU_EGL_COLOR_FORMAT_BGRA = 0x10, + CU_EGL_COLOR_FORMAT_A = 0x11, + CU_EGL_COLOR_FORMAT_RG = 0x12, + CU_EGL_COLOR_FORMAT_AYUV = 0x13, + CU_EGL_COLOR_FORMAT_YVU444_PLANAR = 0x14, + CU_EGL_COLOR_FORMAT_YVU422_PLANAR = 0x15, + CU_EGL_COLOR_FORMAT_YUV422_PLANAR = 0x16, + CU_EGL_COLOR_FORMAT_YVU422_SEMIPLANAR = 0x17, + CU_EGL_COLOR_FORMAT_YUV422_SEMIPLANAR = 0x18, + CU_EGL_COLOR_FORMAT_YUYV = 0x19, + CU_EGL_COLOR_FORMAT_UYVY_ER = 0x1a, + CU_EGL_COLOR_FORMAT_YUYV_ER = 0x1b, + CU_EGL_COLOR_FORMAT_YUVA = 0x1c, + CU_EGL_COLOR_FORMAT_AYUV_ER = 0x1d, + CU_EGL_COLOR_FORMAT_YUVA_ER = 0x1e, + CU_EGL_COLOR_FORMAT_LAST = 0x1f +} CUeglColorFormat; + +typedef enum CUarray_format_enum { + CU_AD_FORMAT_UNSIGNED_INT8 = 0x01, + CU_AD_FORMAT_UNSIGNED_INT16 = 0x02, + CU_AD_FORMAT_UNSIGNED_INT32 = 0x03, + CU_AD_FORMAT_SIGNED_INT8 = 0x08, + CU_AD_FORMAT_SIGNED_INT16 = 0x09, + CU_AD_FORMAT_SIGNED_INT32 = 0x0a, + CU_AD_FORMAT_HALF = 0x10, + CU_AD_FORMAT_FLOAT = 0x20 +} CUarray_format; + +typedef struct CUDA_MEMCPY2D_st { + size_t srcXInBytes; + size_t srcY; + CUmemorytype srcMemoryType; + const void* srcHost; + CUdeviceptr srcDevice; + CUarray srcArray; + size_t srcPitch; + size_t dstXInBytes; + size_t dstY; + CUmemorytype dstMemoryType; + void* dstHost; + CUdeviceptr dstDevice; + CUarray dstArray; + size_t dstPitch; + size_t WidthInBytes; + size_t Height; +} CUDA_MEMCPY2D; + +typedef struct CUDA_ARRAY3D_DESCRIPTOR_st { + size_t Width; + size_t Height; + size_t Depth; + CUarray_format Format; + unsigned int NumChannels; + unsigned int Flags; +} CUDA_ARRAY3D_DESCRIPTOR; + +typedef struct CUDA_RESOURCE_DESC_st { + int resType; + union { + struct { + CUarray hArray; + } array; + struct { + CUdeviceptr devPtr; + CUarray_format format; + unsigned int numChannels; + size_t sizeInBytes; + } linear; + struct { + CUdeviceptr devPtr; + CUarray_format format; + unsigned int numChannels; + size_t width; + size_t height; + size_t pitchInBytes; + } pitch2D; + struct { + unsigned int reserved[32]; + } reserved; + } res; + unsigned int flags; +} CUDA_RESOURCE_DESC; + +typedef struct CUeglFrame_st { + union { + CUarray pArray[3]; + void* pPitch[3]; + } frame; + unsigned int width; + unsigned int height; + unsigned int depth; + unsigned int pitch; + unsigned int planeCount; + unsigned int numChannels; + CUeglFrameType frameType; + CUeglColorFormat eglColorFormat; + CUarray_format cuFormat; +} CUeglFrame; + +#define CU_STREAM_DEFAULT 0 +#define CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE 0x00 + +CUresult CUDAAPI cuInit(unsigned int flags); +CUresult CUDAAPI cuDriverGetVersion(int* driverVersion); +CUresult CUDAAPI cuGetErrorName(CUresult error, const char** pStr); +CUresult CUDAAPI cuDeviceGetCount(int* count); +CUresult CUDAAPI cuDeviceGet(CUdevice* device, int ordinal); +CUresult CUDAAPI cuDeviceGetName(char* name, int len, CUdevice dev); +CUresult CUDAAPI cuDeviceGetAttribute(int* pi, CUdevice_attribute attrib, CUdevice dev); +CUresult CUDAAPI cuCtxCreate(CUcontext* pctx, unsigned int flags, CUdevice dev); +CUresult CUDAAPI cuCtxCreate_v2(CUcontext* pctx, unsigned int flags, CUdevice dev); +CUresult CUDAAPI cuCtxDestroy(CUcontext ctx); +CUresult CUDAAPI cuCtxDestroy_v2(CUcontext ctx); +CUresult CUDAAPI cuCtxGetCurrent(CUcontext* pctx); +CUresult CUDAAPI cuCtxSetCurrent(CUcontext ctx); +CUresult CUDAAPI cuCtxGetDevice(CUdevice* device); +CUresult CUDAAPI cuCtxPushCurrent(CUcontext ctx); +CUresult CUDAAPI cuCtxPushCurrent_v2(CUcontext ctx); +CUresult CUDAAPI cuCtxPopCurrent(CUcontext* pctx); +CUresult CUDAAPI cuCtxPopCurrent_v2(CUcontext* pctx); +CUresult CUDAAPI cuMemAlloc(CUdeviceptr* dptr, size_t bytesize); +CUresult CUDAAPI cuMemAllocPitch(CUdeviceptr* dptr, + size_t* pPitch, + size_t WidthInBytes, + size_t Height, + unsigned int ElementSizeBytes); +CUresult CUDAAPI cuMemFree(CUdeviceptr dptr); +CUresult CUDAAPI cuMemFree_v2(CUdeviceptr dptr); +CUresult CUDAAPI cuMemcpy2D(const CUDA_MEMCPY2D* pCopy); +CUresult CUDAAPI cuMemcpy2D_v2(const CUDA_MEMCPY2D* pCopy); +CUresult CUDAAPI cuMemcpy2DUnaligned(const CUDA_MEMCPY2D* pCopy); +CUresult CUDAAPI cuMemcpy2DUnaligned_v2(const CUDA_MEMCPY2D* pCopy); +CUresult CUDAAPI cuMemcpy2DAsync(const CUDA_MEMCPY2D* pCopy, CUstream hStream); +CUresult CUDAAPI cuMemcpy2DAsync_v2(const CUDA_MEMCPY2D* pCopy, CUstream hStream); +CUresult CUDAAPI cuStreamCreate(CUstream* phStream, unsigned int Flags); +CUresult CUDAAPI cuStreamSynchronize(CUstream hStream); +CUresult CUDAAPI cuArrayDestroy(CUarray hArray); +CUresult CUDAAPI cuGraphicsEGLRegisterImage(CUgraphicsResource* pCudaResource, + void* image, + unsigned int flags); +CUresult CUDAAPI cuGraphicsResourceGetMappedEglFrame(CUeglFrame* eglFrame, + CUgraphicsResource resource, + unsigned int index, + unsigned int mipLevel); +CUresult CUDAAPI cuGraphicsUnregisterResource(CUgraphicsResource resource); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/include/cuviddec.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/include/cuviddec.h new file mode 100644 index 0000000..d1bbe0a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/include/cuviddec.h @@ -0,0 +1,1188 @@ +/* + * This copyright notice applies to this header file only: + * + * Copyright (c) 2010-2022 NVIDIA Corporation + * + * 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. + */ + +/*****************************************************************************************************/ +//! \file cuviddec.h +//! NVDECODE API provides video decoding interface to NVIDIA GPU devices. +//! This file contains constants, structure definitions and function prototypes used for decoding. +/*****************************************************************************************************/ + +#if !defined(__CUDA_VIDEO_H__) +#define __CUDA_VIDEO_H__ + +#ifndef __cuda_cuda_h__ +#include +#endif // __cuda_cuda_h__ + +#if defined(_WIN64) || defined(__LP64__) || defined(__x86_64) || defined(AMD64) || defined(_M_AMD64) +#if (CUDA_VERSION >= 3020) && (!defined(CUDA_FORCE_API_VERSION) || (CUDA_FORCE_API_VERSION >= 3020)) +#define __CUVID_DEVPTR64 +#endif +#endif + +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus */ + +typedef void *CUvideodecoder; +typedef struct _CUcontextlock_st *CUvideoctxlock; + +/*********************************************************************************/ +//! \enum cudaVideoCodec +//! Video codec enums +//! These enums are used in CUVIDDECODECREATEINFO and CUVIDDECODECAPS structures +/*********************************************************************************/ +typedef enum cudaVideoCodec_enum { + cudaVideoCodec_MPEG1=0, /**< MPEG1 */ + cudaVideoCodec_MPEG2, /**< MPEG2 */ + cudaVideoCodec_MPEG4, /**< MPEG4 */ + cudaVideoCodec_VC1, /**< VC1 */ + cudaVideoCodec_H264, /**< H264 */ + cudaVideoCodec_JPEG, /**< JPEG */ + cudaVideoCodec_H264_SVC, /**< H264-SVC */ + cudaVideoCodec_H264_MVC, /**< H264-MVC */ + cudaVideoCodec_HEVC, /**< HEVC */ + cudaVideoCodec_VP8, /**< VP8 */ + cudaVideoCodec_VP9, /**< VP9 */ + cudaVideoCodec_AV1, /**< AV1 */ + cudaVideoCodec_NumCodecs, /**< Max codecs */ + // Uncompressed YUV + cudaVideoCodec_YUV420 = (('I'<<24)|('Y'<<16)|('U'<<8)|('V')), /**< Y,U,V (4:2:0) */ + cudaVideoCodec_YV12 = (('Y'<<24)|('V'<<16)|('1'<<8)|('2')), /**< Y,V,U (4:2:0) */ + cudaVideoCodec_NV12 = (('N'<<24)|('V'<<16)|('1'<<8)|('2')), /**< Y,UV (4:2:0) */ + cudaVideoCodec_YUYV = (('Y'<<24)|('U'<<16)|('Y'<<8)|('V')), /**< YUYV/YUY2 (4:2:2) */ + cudaVideoCodec_UYVY = (('U'<<24)|('Y'<<16)|('V'<<8)|('Y')) /**< UYVY (4:2:2) */ +} cudaVideoCodec; + +/*********************************************************************************/ +//! \enum cudaVideoSurfaceFormat +//! Video surface format enums used for output format of decoded output +//! These enums are used in CUVIDDECODECREATEINFO structure +/*********************************************************************************/ +typedef enum cudaVideoSurfaceFormat_enum { + cudaVideoSurfaceFormat_NV12=0, /**< Semi-Planar YUV [Y plane followed by interleaved UV plane] */ + cudaVideoSurfaceFormat_P016=1, /**< 16 bit Semi-Planar YUV [Y plane followed by interleaved UV plane]. + Can be used for 10 bit(6LSB bits 0), 12 bit (4LSB bits 0) */ + cudaVideoSurfaceFormat_YUV444=2, /**< Planar YUV [Y plane followed by U and V planes] */ + cudaVideoSurfaceFormat_YUV444_16Bit=3, /**< 16 bit Planar YUV [Y plane followed by U and V planes]. + Can be used for 10 bit(6LSB bits 0), 12 bit (4LSB bits 0) */ +} cudaVideoSurfaceFormat; + +/******************************************************************************************************************/ +//! \enum cudaVideoDeinterlaceMode +//! Deinterlacing mode enums +//! These enums are used in CUVIDDECODECREATEINFO structure +//! Use cudaVideoDeinterlaceMode_Weave for progressive content and for content that doesn't need deinterlacing +//! cudaVideoDeinterlaceMode_Adaptive needs more video memory than other DImodes +/******************************************************************************************************************/ +typedef enum cudaVideoDeinterlaceMode_enum { + cudaVideoDeinterlaceMode_Weave=0, /**< Weave both fields (no deinterlacing) */ + cudaVideoDeinterlaceMode_Bob, /**< Drop one field */ + cudaVideoDeinterlaceMode_Adaptive /**< Adaptive deinterlacing */ +} cudaVideoDeinterlaceMode; + +/**************************************************************************************************************/ +//! \enum cudaVideoChromaFormat +//! Chroma format enums +//! These enums are used in CUVIDDECODECREATEINFO and CUVIDDECODECAPS structures +/**************************************************************************************************************/ +typedef enum cudaVideoChromaFormat_enum { + cudaVideoChromaFormat_Monochrome=0, /**< MonoChrome */ + cudaVideoChromaFormat_420, /**< YUV 4:2:0 */ + cudaVideoChromaFormat_422, /**< YUV 4:2:2 */ + cudaVideoChromaFormat_444 /**< YUV 4:4:4 */ +} cudaVideoChromaFormat; + +/*************************************************************************************************************/ +//! \enum cudaVideoCreateFlags +//! Decoder flag enums to select preferred decode path +//! cudaVideoCreate_Default and cudaVideoCreate_PreferCUVID are most optimized, use these whenever possible +/*************************************************************************************************************/ +typedef enum cudaVideoCreateFlags_enum { + cudaVideoCreate_Default = 0x00, /**< Default operation mode: use dedicated video engines */ + cudaVideoCreate_PreferCUDA = 0x01, /**< Use CUDA-based decoder (requires valid vidLock object for multi-threading) */ + cudaVideoCreate_PreferDXVA = 0x02, /**< Go through DXVA internally if possible (requires D3D9 interop) */ + cudaVideoCreate_PreferCUVID = 0x04 /**< Use dedicated video engines directly */ +} cudaVideoCreateFlags; + + +/*************************************************************************/ +//! \enum cuvidDecodeStatus +//! Decode status enums +//! These enums are used in CUVIDGETDECODESTATUS structure +/*************************************************************************/ +typedef enum cuvidDecodeStatus_enum +{ + cuvidDecodeStatus_Invalid = 0, // Decode status is not valid + cuvidDecodeStatus_InProgress = 1, // Decode is in progress + cuvidDecodeStatus_Success = 2, // Decode is completed without any errors + // 3 to 7 enums are reserved for future use + cuvidDecodeStatus_Error = 8, // Decode is completed with an error (error is not concealed) + cuvidDecodeStatus_Error_Concealed = 9, // Decode is completed with an error and error is concealed +} cuvidDecodeStatus; + +/**************************************************************************************************************/ +//! \struct CUVIDDECODECAPS; +//! This structure is used in cuvidGetDecoderCaps API +/**************************************************************************************************************/ +typedef struct _CUVIDDECODECAPS +{ + cudaVideoCodec eCodecType; /**< IN: cudaVideoCodec_XXX */ + cudaVideoChromaFormat eChromaFormat; /**< IN: cudaVideoChromaFormat_XXX */ + unsigned int nBitDepthMinus8; /**< IN: The Value "BitDepth minus 8" */ + unsigned int reserved1[3]; /**< Reserved for future use - set to zero */ + + unsigned char bIsSupported; /**< OUT: 1 if codec supported, 0 if not supported */ + unsigned char nNumNVDECs; /**< OUT: Number of NVDECs that can support IN params */ + unsigned short nOutputFormatMask; /**< OUT: each bit represents corresponding cudaVideoSurfaceFormat enum */ + unsigned int nMaxWidth; /**< OUT: Max supported coded width in pixels */ + unsigned int nMaxHeight; /**< OUT: Max supported coded height in pixels */ + unsigned int nMaxMBCount; /**< OUT: Max supported macroblock count + CodedWidth*CodedHeight/256 must be <= nMaxMBCount */ + unsigned short nMinWidth; /**< OUT: Min supported coded width in pixels */ + unsigned short nMinHeight; /**< OUT: Min supported coded height in pixels */ + unsigned char bIsHistogramSupported; /**< OUT: 1 if Y component histogram output is supported, 0 if not + Note: histogram is computed on original picture data before + any post-processing like scaling, cropping, etc. is applied */ + unsigned char nCounterBitDepth; /**< OUT: histogram counter bit depth */ + unsigned short nMaxHistogramBins; /**< OUT: Max number of histogram bins */ + unsigned int reserved3[10]; /**< Reserved for future use - set to zero */ +} CUVIDDECODECAPS; + +/**************************************************************************************************************/ +//! \struct CUVIDDECODECREATEINFO +//! This structure is used in cuvidCreateDecoder API +/**************************************************************************************************************/ +typedef struct _CUVIDDECODECREATEINFO +{ + unsigned long ulWidth; /**< IN: Coded sequence width in pixels */ + unsigned long ulHeight; /**< IN: Coded sequence height in pixels */ + unsigned long ulNumDecodeSurfaces; /**< IN: Maximum number of internal decode surfaces */ + cudaVideoCodec CodecType; /**< IN: cudaVideoCodec_XXX */ + cudaVideoChromaFormat ChromaFormat; /**< IN: cudaVideoChromaFormat_XXX */ + unsigned long ulCreationFlags; /**< IN: Decoder creation flags (cudaVideoCreateFlags_XXX) */ + unsigned long bitDepthMinus8; /**< IN: The value "BitDepth minus 8" */ + unsigned long ulIntraDecodeOnly; /**< IN: Set 1 only if video has all intra frames (default value is 0). This will + optimize video memory for Intra frames only decoding. The support is limited + to specific codecs - H264, HEVC, VP9, the flag will be ignored for codecs which + are not supported. However decoding might fail if the flag is enabled in case + of supported codecs for regular bit streams having P and/or B frames. */ + unsigned long ulMaxWidth; /**< IN: Coded sequence max width in pixels used with reconfigure Decoder */ + unsigned long ulMaxHeight; /**< IN: Coded sequence max height in pixels used with reconfigure Decoder */ + unsigned long Reserved1; /**< Reserved for future use - set to zero */ + /** + * IN: area of the frame that should be displayed + */ + struct { + short left; + short top; + short right; + short bottom; + } display_area; + + cudaVideoSurfaceFormat OutputFormat; /**< IN: cudaVideoSurfaceFormat_XXX */ + cudaVideoDeinterlaceMode DeinterlaceMode; /**< IN: cudaVideoDeinterlaceMode_XXX */ + unsigned long ulTargetWidth; /**< IN: Post-processed output width (Should be aligned to 2) */ + unsigned long ulTargetHeight; /**< IN: Post-processed output height (Should be aligned to 2) */ + unsigned long ulNumOutputSurfaces; /**< IN: Maximum number of output surfaces simultaneously mapped */ + CUvideoctxlock vidLock; /**< IN: If non-NULL, context lock used for synchronizing ownership of + the cuda context. Needed for cudaVideoCreate_PreferCUDA decode */ + /** + * IN: target rectangle in the output frame (for aspect ratio conversion) + * if a null rectangle is specified, {0,0,ulTargetWidth,ulTargetHeight} will be used + */ + struct { + short left; + short top; + short right; + short bottom; + } target_rect; + + unsigned long enableHistogram; /**< IN: enable histogram output, if supported */ + unsigned long Reserved2[4]; /**< Reserved for future use - set to zero */ +} CUVIDDECODECREATEINFO; + +/*********************************************************/ +//! \struct CUVIDH264DPBENTRY +//! H.264 DPB entry +//! This structure is used in CUVIDH264PICPARAMS structure +/*********************************************************/ +typedef struct _CUVIDH264DPBENTRY +{ + int PicIdx; /**< picture index of reference frame */ + int FrameIdx; /**< frame_num(short-term) or LongTermFrameIdx(long-term) */ + int is_long_term; /**< 0=short term reference, 1=long term reference */ + int not_existing; /**< non-existing reference frame (corresponding PicIdx should be set to -1) */ + int used_for_reference; /**< 0=unused, 1=top_field, 2=bottom_field, 3=both_fields */ + int FieldOrderCnt[2]; /**< field order count of top and bottom fields */ +} CUVIDH264DPBENTRY; + +/************************************************************/ +//! \struct CUVIDH264MVCEXT +//! H.264 MVC picture parameters ext +//! This structure is used in CUVIDH264PICPARAMS structure +/************************************************************/ +typedef struct _CUVIDH264MVCEXT +{ + int num_views_minus1; /**< Max number of coded views minus 1 in video : Range - 0 to 1023 */ + int view_id; /**< view identifier */ + unsigned char inter_view_flag; /**< 1 if used for inter-view prediction, 0 if not */ + unsigned char num_inter_view_refs_l0; /**< number of inter-view ref pics in RefPicList0 */ + unsigned char num_inter_view_refs_l1; /**< number of inter-view ref pics in RefPicList1 */ + unsigned char MVCReserved8Bits; /**< Reserved bits */ + int InterViewRefsL0[16]; /**< view id of the i-th view component for inter-view prediction in RefPicList0 */ + int InterViewRefsL1[16]; /**< view id of the i-th view component for inter-view prediction in RefPicList1 */ +} CUVIDH264MVCEXT; + +/*********************************************************/ +//! \struct CUVIDH264SVCEXT +//! H.264 SVC picture parameters ext +//! This structure is used in CUVIDH264PICPARAMS structure +/*********************************************************/ +typedef struct _CUVIDH264SVCEXT +{ + unsigned char profile_idc; + unsigned char level_idc; + unsigned char DQId; + unsigned char DQIdMax; + unsigned char disable_inter_layer_deblocking_filter_idc; + unsigned char ref_layer_chroma_phase_y_plus1; + signed char inter_layer_slice_alpha_c0_offset_div2; + signed char inter_layer_slice_beta_offset_div2; + + unsigned short DPBEntryValidFlag; + unsigned char inter_layer_deblocking_filter_control_present_flag; + unsigned char extended_spatial_scalability_idc; + unsigned char adaptive_tcoeff_level_prediction_flag; + unsigned char slice_header_restriction_flag; + unsigned char chroma_phase_x_plus1_flag; + unsigned char chroma_phase_y_plus1; + + unsigned char tcoeff_level_prediction_flag; + unsigned char constrained_intra_resampling_flag; + unsigned char ref_layer_chroma_phase_x_plus1_flag; + unsigned char store_ref_base_pic_flag; + unsigned char Reserved8BitsA; + unsigned char Reserved8BitsB; + + short scaled_ref_layer_left_offset; + short scaled_ref_layer_top_offset; + short scaled_ref_layer_right_offset; + short scaled_ref_layer_bottom_offset; + unsigned short Reserved16Bits; + struct _CUVIDPICPARAMS *pNextLayer; /**< Points to the picparams for the next layer to be decoded. + Linked list ends at the target layer. */ + int bRefBaseLayer; /**< whether to store ref base pic */ +} CUVIDH264SVCEXT; + +/******************************************************/ +//! \struct CUVIDH264PICPARAMS +//! H.264 picture parameters +//! This structure is used in CUVIDPICPARAMS structure +/******************************************************/ +typedef struct _CUVIDH264PICPARAMS +{ + // SPS + int log2_max_frame_num_minus4; + int pic_order_cnt_type; + int log2_max_pic_order_cnt_lsb_minus4; + int delta_pic_order_always_zero_flag; + int frame_mbs_only_flag; + int direct_8x8_inference_flag; + int num_ref_frames; // NOTE: shall meet level 4.1 restrictions + unsigned char residual_colour_transform_flag; + unsigned char bit_depth_luma_minus8; // Must be 0 (only 8-bit supported) + unsigned char bit_depth_chroma_minus8; // Must be 0 (only 8-bit supported) + unsigned char qpprime_y_zero_transform_bypass_flag; + // PPS + int entropy_coding_mode_flag; + int pic_order_present_flag; + int num_ref_idx_l0_active_minus1; + int num_ref_idx_l1_active_minus1; + int weighted_pred_flag; + int weighted_bipred_idc; + int pic_init_qp_minus26; + int deblocking_filter_control_present_flag; + int redundant_pic_cnt_present_flag; + int transform_8x8_mode_flag; + int MbaffFrameFlag; + int constrained_intra_pred_flag; + int chroma_qp_index_offset; + int second_chroma_qp_index_offset; + int ref_pic_flag; + int frame_num; + int CurrFieldOrderCnt[2]; + // DPB + CUVIDH264DPBENTRY dpb[16]; // List of reference frames within the DPB + // Quantization Matrices (raster-order) + unsigned char WeightScale4x4[6][16]; + unsigned char WeightScale8x8[2][64]; + // FMO/ASO + unsigned char fmo_aso_enable; + unsigned char num_slice_groups_minus1; + unsigned char slice_group_map_type; + signed char pic_init_qs_minus26; + unsigned int slice_group_change_rate_minus1; + union + { + unsigned long long slice_group_map_addr; + const unsigned char *pMb2SliceGroupMap; + } fmo; + unsigned int Reserved[12]; + // SVC/MVC + union + { + CUVIDH264MVCEXT mvcext; + CUVIDH264SVCEXT svcext; + }; +} CUVIDH264PICPARAMS; + + +/********************************************************/ +//! \struct CUVIDMPEG2PICPARAMS +//! MPEG-2 picture parameters +//! This structure is used in CUVIDPICPARAMS structure +/********************************************************/ +typedef struct _CUVIDMPEG2PICPARAMS +{ + int ForwardRefIdx; // Picture index of forward reference (P/B-frames) + int BackwardRefIdx; // Picture index of backward reference (B-frames) + int picture_coding_type; + int full_pel_forward_vector; + int full_pel_backward_vector; + int f_code[2][2]; + int intra_dc_precision; + int frame_pred_frame_dct; + int concealment_motion_vectors; + int q_scale_type; + int intra_vlc_format; + int alternate_scan; + int top_field_first; + // Quantization matrices (raster order) + unsigned char QuantMatrixIntra[64]; + unsigned char QuantMatrixInter[64]; +} CUVIDMPEG2PICPARAMS; + +// MPEG-4 has VOP types instead of Picture types +#define I_VOP 0 +#define P_VOP 1 +#define B_VOP 2 +#define S_VOP 3 + +/*******************************************************/ +//! \struct CUVIDMPEG4PICPARAMS +//! MPEG-4 picture parameters +//! This structure is used in CUVIDPICPARAMS structure +/*******************************************************/ +typedef struct _CUVIDMPEG4PICPARAMS +{ + int ForwardRefIdx; // Picture index of forward reference (P/B-frames) + int BackwardRefIdx; // Picture index of backward reference (B-frames) + // VOL + int video_object_layer_width; + int video_object_layer_height; + int vop_time_increment_bitcount; + int top_field_first; + int resync_marker_disable; + int quant_type; + int quarter_sample; + int short_video_header; + int divx_flags; + // VOP + int vop_coding_type; + int vop_coded; + int vop_rounding_type; + int alternate_vertical_scan_flag; + int interlaced; + int vop_fcode_forward; + int vop_fcode_backward; + int trd[2]; + int trb[2]; + // Quantization matrices (raster order) + unsigned char QuantMatrixIntra[64]; + unsigned char QuantMatrixInter[64]; + int gmc_enabled; +} CUVIDMPEG4PICPARAMS; + +/********************************************************/ +//! \struct CUVIDVC1PICPARAMS +//! VC1 picture parameters +//! This structure is used in CUVIDPICPARAMS structure +/********************************************************/ +typedef struct _CUVIDVC1PICPARAMS +{ + int ForwardRefIdx; /**< Picture index of forward reference (P/B-frames) */ + int BackwardRefIdx; /**< Picture index of backward reference (B-frames) */ + int FrameWidth; /**< Actual frame width */ + int FrameHeight; /**< Actual frame height */ + // PICTURE + int intra_pic_flag; /**< Set to 1 for I,BI frames */ + int ref_pic_flag; /**< Set to 1 for I,P frames */ + int progressive_fcm; /**< Progressive frame */ + // SEQUENCE + int profile; + int postprocflag; + int pulldown; + int interlace; + int tfcntrflag; + int finterpflag; + int psf; + int multires; + int syncmarker; + int rangered; + int maxbframes; + // ENTRYPOINT + int panscan_flag; + int refdist_flag; + int extended_mv; + int dquant; + int vstransform; + int loopfilter; + int fastuvmc; + int overlap; + int quantizer; + int extended_dmv; + int range_mapy_flag; + int range_mapy; + int range_mapuv_flag; + int range_mapuv; + int rangeredfrm; // range reduction state +} CUVIDVC1PICPARAMS; + +/***********************************************************/ +//! \struct CUVIDJPEGPICPARAMS +//! JPEG picture parameters +//! This structure is used in CUVIDPICPARAMS structure +/***********************************************************/ +typedef struct _CUVIDJPEGPICPARAMS +{ + int Reserved; +} CUVIDJPEGPICPARAMS; + + +/*******************************************************/ +//! \struct CUVIDHEVCPICPARAMS +//! HEVC picture parameters +//! This structure is used in CUVIDPICPARAMS structure +/*******************************************************/ +typedef struct _CUVIDHEVCPICPARAMS +{ + // sps + int pic_width_in_luma_samples; + int pic_height_in_luma_samples; + unsigned char log2_min_luma_coding_block_size_minus3; + unsigned char log2_diff_max_min_luma_coding_block_size; + unsigned char log2_min_transform_block_size_minus2; + unsigned char log2_diff_max_min_transform_block_size; + unsigned char pcm_enabled_flag; + unsigned char log2_min_pcm_luma_coding_block_size_minus3; + unsigned char log2_diff_max_min_pcm_luma_coding_block_size; + unsigned char pcm_sample_bit_depth_luma_minus1; + + unsigned char pcm_sample_bit_depth_chroma_minus1; + unsigned char pcm_loop_filter_disabled_flag; + unsigned char strong_intra_smoothing_enabled_flag; + unsigned char max_transform_hierarchy_depth_intra; + unsigned char max_transform_hierarchy_depth_inter; + unsigned char amp_enabled_flag; + unsigned char separate_colour_plane_flag; + unsigned char log2_max_pic_order_cnt_lsb_minus4; + + unsigned char num_short_term_ref_pic_sets; + unsigned char long_term_ref_pics_present_flag; + unsigned char num_long_term_ref_pics_sps; + unsigned char sps_temporal_mvp_enabled_flag; + unsigned char sample_adaptive_offset_enabled_flag; + unsigned char scaling_list_enable_flag; + unsigned char IrapPicFlag; + unsigned char IdrPicFlag; + + unsigned char bit_depth_luma_minus8; + unsigned char bit_depth_chroma_minus8; + //sps/pps extension fields + unsigned char log2_max_transform_skip_block_size_minus2; + unsigned char log2_sao_offset_scale_luma; + unsigned char log2_sao_offset_scale_chroma; + unsigned char high_precision_offsets_enabled_flag; + unsigned char reserved1[10]; + + // pps + unsigned char dependent_slice_segments_enabled_flag; + unsigned char slice_segment_header_extension_present_flag; + unsigned char sign_data_hiding_enabled_flag; + unsigned char cu_qp_delta_enabled_flag; + unsigned char diff_cu_qp_delta_depth; + signed char init_qp_minus26; + signed char pps_cb_qp_offset; + signed char pps_cr_qp_offset; + + unsigned char constrained_intra_pred_flag; + unsigned char weighted_pred_flag; + unsigned char weighted_bipred_flag; + unsigned char transform_skip_enabled_flag; + unsigned char transquant_bypass_enabled_flag; + unsigned char entropy_coding_sync_enabled_flag; + unsigned char log2_parallel_merge_level_minus2; + unsigned char num_extra_slice_header_bits; + + unsigned char loop_filter_across_tiles_enabled_flag; + unsigned char loop_filter_across_slices_enabled_flag; + unsigned char output_flag_present_flag; + unsigned char num_ref_idx_l0_default_active_minus1; + unsigned char num_ref_idx_l1_default_active_minus1; + unsigned char lists_modification_present_flag; + unsigned char cabac_init_present_flag; + unsigned char pps_slice_chroma_qp_offsets_present_flag; + + unsigned char deblocking_filter_override_enabled_flag; + unsigned char pps_deblocking_filter_disabled_flag; + signed char pps_beta_offset_div2; + signed char pps_tc_offset_div2; + unsigned char tiles_enabled_flag; + unsigned char uniform_spacing_flag; + unsigned char num_tile_columns_minus1; + unsigned char num_tile_rows_minus1; + + unsigned short column_width_minus1[21]; + unsigned short row_height_minus1[21]; + + // sps and pps extension HEVC-main 444 + unsigned char sps_range_extension_flag; + unsigned char transform_skip_rotation_enabled_flag; + unsigned char transform_skip_context_enabled_flag; + unsigned char implicit_rdpcm_enabled_flag; + + unsigned char explicit_rdpcm_enabled_flag; + unsigned char extended_precision_processing_flag; + unsigned char intra_smoothing_disabled_flag; + unsigned char persistent_rice_adaptation_enabled_flag; + + unsigned char cabac_bypass_alignment_enabled_flag; + unsigned char pps_range_extension_flag; + unsigned char cross_component_prediction_enabled_flag; + unsigned char chroma_qp_offset_list_enabled_flag; + + unsigned char diff_cu_chroma_qp_offset_depth; + unsigned char chroma_qp_offset_list_len_minus1; + signed char cb_qp_offset_list[6]; + + signed char cr_qp_offset_list[6]; + unsigned char reserved2[2]; + + unsigned int reserved3[8]; + + // RefPicSets + int NumBitsForShortTermRPSInSlice; + int NumDeltaPocsOfRefRpsIdx; + int NumPocTotalCurr; + int NumPocStCurrBefore; + int NumPocStCurrAfter; + int NumPocLtCurr; + int CurrPicOrderCntVal; + int RefPicIdx[16]; // [refpic] Indices of valid reference pictures (-1 if unused for reference) + int PicOrderCntVal[16]; // [refpic] + unsigned char IsLongTerm[16]; // [refpic] 0=not a long-term reference, 1=long-term reference + unsigned char RefPicSetStCurrBefore[8]; // [0..NumPocStCurrBefore-1] -> refpic (0..15) + unsigned char RefPicSetStCurrAfter[8]; // [0..NumPocStCurrAfter-1] -> refpic (0..15) + unsigned char RefPicSetLtCurr[8]; // [0..NumPocLtCurr-1] -> refpic (0..15) + unsigned char RefPicSetInterLayer0[8]; + unsigned char RefPicSetInterLayer1[8]; + unsigned int reserved4[12]; + + // scaling lists (diag order) + unsigned char ScalingList4x4[6][16]; // [matrixId][i] + unsigned char ScalingList8x8[6][64]; // [matrixId][i] + unsigned char ScalingList16x16[6][64]; // [matrixId][i] + unsigned char ScalingList32x32[2][64]; // [matrixId][i] + unsigned char ScalingListDCCoeff16x16[6]; // [matrixId] + unsigned char ScalingListDCCoeff32x32[2]; // [matrixId] +} CUVIDHEVCPICPARAMS; + + +/***********************************************************/ +//! \struct CUVIDVP8PICPARAMS +//! VP8 picture parameters +//! This structure is used in CUVIDPICPARAMS structure +/***********************************************************/ +typedef struct _CUVIDVP8PICPARAMS +{ + int width; + int height; + unsigned int first_partition_size; + //Frame Indexes + unsigned char LastRefIdx; + unsigned char GoldenRefIdx; + unsigned char AltRefIdx; + union { + struct { + unsigned char frame_type : 1; /**< 0 = KEYFRAME, 1 = INTERFRAME */ + unsigned char version : 3; + unsigned char show_frame : 1; + unsigned char update_mb_segmentation_data : 1; /**< Must be 0 if segmentation is not enabled */ + unsigned char Reserved2Bits : 2; + }vp8_frame_tag; + unsigned char wFrameTagFlags; + }; + unsigned char Reserved1[4]; + unsigned int Reserved2[3]; +} CUVIDVP8PICPARAMS; + +/***********************************************************/ +//! \struct CUVIDVP9PICPARAMS +//! VP9 picture parameters +//! This structure is used in CUVIDPICPARAMS structure +/***********************************************************/ +typedef struct _CUVIDVP9PICPARAMS +{ + unsigned int width; + unsigned int height; + + //Frame Indices + unsigned char LastRefIdx; + unsigned char GoldenRefIdx; + unsigned char AltRefIdx; + unsigned char colorSpace; + + unsigned short profile : 3; + unsigned short frameContextIdx : 2; + unsigned short frameType : 1; + unsigned short showFrame : 1; + unsigned short errorResilient : 1; + unsigned short frameParallelDecoding : 1; + unsigned short subSamplingX : 1; + unsigned short subSamplingY : 1; + unsigned short intraOnly : 1; + unsigned short allow_high_precision_mv : 1; + unsigned short refreshEntropyProbs : 1; + unsigned short reserved2Bits : 2; + + unsigned short reserved16Bits; + + unsigned char refFrameSignBias[4]; + + unsigned char bitDepthMinus8Luma; + unsigned char bitDepthMinus8Chroma; + unsigned char loopFilterLevel; + unsigned char loopFilterSharpness; + + unsigned char modeRefLfEnabled; + unsigned char log2_tile_columns; + unsigned char log2_tile_rows; + + unsigned char segmentEnabled : 1; + unsigned char segmentMapUpdate : 1; + unsigned char segmentMapTemporalUpdate : 1; + unsigned char segmentFeatureMode : 1; + unsigned char reserved4Bits : 4; + + + unsigned char segmentFeatureEnable[8][4]; + short segmentFeatureData[8][4]; + unsigned char mb_segment_tree_probs[7]; + unsigned char segment_pred_probs[3]; + unsigned char reservedSegment16Bits[2]; + + int qpYAc; + int qpYDc; + int qpChDc; + int qpChAc; + + unsigned int activeRefIdx[3]; + unsigned int resetFrameContext; + unsigned int mcomp_filter_type; + unsigned int mbRefLfDelta[4]; + unsigned int mbModeLfDelta[2]; + unsigned int frameTagSize; + unsigned int offsetToDctParts; + unsigned int reserved128Bits[4]; + +} CUVIDVP9PICPARAMS; + +/***********************************************************/ +//! \struct CUVIDAV1PICPARAMS +//! AV1 picture parameters +//! This structure is used in CUVIDPICPARAMS structure +/***********************************************************/ +typedef struct _CUVIDAV1PICPARAMS +{ + unsigned int width; // coded width, if superres enabled then it is upscaled width + unsigned int height; // coded height + unsigned int frame_offset; // defined as order_hint in AV1 specification + int decodePicIdx; // decoded output pic index, if film grain enabled, it will keep decoded (without film grain) output + // It can be used as reference frame for future frames + + // sequence header + unsigned int profile : 3; // 0 = profile0, 1 = profile1, 2 = profile2 + unsigned int use_128x128_superblock : 1; // superblock size 0:64x64, 1: 128x128 + unsigned int subsampling_x : 1; // (subsampling_x, _y) 1,1 = 420, 1,0 = 422, 0,0 = 444 + unsigned int subsampling_y : 1; + unsigned int mono_chrome : 1; // for monochrome content, mono_chrome = 1 and (subsampling_x, _y) should be 1,1 + unsigned int bit_depth_minus8 : 4; // bit depth minus 8 + unsigned int enable_filter_intra : 1; // tool enable in seq level, 0 : disable 1: frame header control + unsigned int enable_intra_edge_filter : 1; // intra edge filtering process, 0 : disable 1: enabled + unsigned int enable_interintra_compound : 1; // interintra, 0 : not present 1: present + unsigned int enable_masked_compound : 1; // 1: mode info for inter blocks may contain the syntax element compound_type. + // 0: syntax element compound_type will not be present + unsigned int enable_dual_filter : 1; // vertical and horiz filter selection, 1: enable and 0: disable + unsigned int enable_order_hint : 1; // order hint, and related tools, 1: enable and 0: disable + unsigned int order_hint_bits_minus1 : 3; // is used to compute OrderHintBits + unsigned int enable_jnt_comp : 1; // joint compound modes, 1: enable and 0: disable + unsigned int enable_superres : 1; // superres in seq level, 0 : disable 1: frame level control + unsigned int enable_cdef : 1; // cdef filtering in seq level, 0 : disable 1: frame level control + unsigned int enable_restoration : 1; // loop restoration filtering in seq level, 0 : disable 1: frame level control + unsigned int enable_fgs : 1; // defined as film_grain_params_present in AV1 specification + unsigned int reserved0_7bits : 7; // reserved bits; must be set to 0 + + // frame header + unsigned int frame_type : 2 ; // 0:Key frame, 1:Inter frame, 2:intra only, 3:s-frame + unsigned int show_frame : 1 ; // show_frame = 1 implies that frame should be immediately output once decoded + unsigned int disable_cdf_update : 1; // CDF update during symbol decoding, 1: disabled, 0: enabled + unsigned int allow_screen_content_tools : 1; // 1: intra blocks may use palette encoding, 0: palette encoding is never used + unsigned int force_integer_mv : 1; // 1: motion vectors will always be integers, 0: can contain fractional bits + unsigned int coded_denom : 3; // coded_denom of the superres scale as specified in AV1 specification + unsigned int allow_intrabc : 1; // 1: intra block copy may be used, 0: intra block copy is not allowed + unsigned int allow_high_precision_mv : 1; // 1/8 precision mv enable + unsigned int interp_filter : 3; // interpolation filter. Refer to section 6.8.9 of the AV1 specification Version 1.0.0 with Errata 1 + unsigned int switchable_motion_mode : 1; // defined as is_motion_mode_switchable in AV1 specification + unsigned int use_ref_frame_mvs : 1; // 1: current frame can use the previous frame mv information, 0: will not use. + unsigned int disable_frame_end_update_cdf : 1; // 1: indicates that the end of frame CDF update is disabled + unsigned int delta_q_present : 1; // quantizer index delta values are present in the block level + unsigned int delta_q_res : 2; // left shift which should be applied to decoded quantizer index delta values + unsigned int using_qmatrix : 1; // 1: quantizer matrix will be used to compute quantizers + unsigned int coded_lossless : 1; // 1: all segments use lossless coding + unsigned int use_superres : 1; // 1: superres enabled for frame + unsigned int tx_mode : 2; // 0: ONLY4x4,1:LARGEST,2:SELECT + unsigned int reference_mode : 1; // 0: SINGLE, 1: SELECT + unsigned int allow_warped_motion : 1; // 1: allow_warped_motion may be present, 0: allow_warped_motion will not be present + unsigned int reduced_tx_set : 1; // 1: frame is restricted to subset of the full set of transform types, 0: no such restriction + unsigned int skip_mode : 1; // 1: most of the mode info is skipped, 0: mode info is not skipped + unsigned int reserved1_3bits : 3; // reserved bits; must be set to 0 + + // tiling info + unsigned int num_tile_cols : 8; // number of tiles across the frame., max is 64 + unsigned int num_tile_rows : 8; // number of tiles down the frame., max is 64 + unsigned int context_update_tile_id : 16; // specifies which tile to use for the CDF update + unsigned short tile_widths[64]; // Width of each column in superblocks + unsigned short tile_heights[64]; // height of each row in superblocks + + // CDEF - refer to section 6.10.14 of the AV1 specification Version 1.0.0 with Errata 1 + unsigned char cdef_damping_minus_3 : 2; // controls the amount of damping in the deringing filter + unsigned char cdef_bits : 2; // the number of bits needed to specify which CDEF filter to apply + unsigned char reserved2_4bits : 4; // reserved bits; must be set to 0 + unsigned char cdef_y_strength[8]; // 0-3 bits: y_pri_strength, 4-7 bits y_sec_strength + unsigned char cdef_uv_strength[8]; // 0-3 bits: uv_pri_strength, 4-7 bits uv_sec_strength + + // SkipModeFrames + unsigned char SkipModeFrame0 : 4; // specifies the frames to use for compound prediction when skip_mode is equal to 1. + unsigned char SkipModeFrame1 : 4; + + // qp information - refer to section 6.8.11 of the AV1 specification Version 1.0.0 with Errata 1 + unsigned char base_qindex; // indicates the base frame qindex. Defined as base_q_idx in AV1 specification + char qp_y_dc_delta_q; // indicates the Y DC quantizer relative to base_q_idx. Defined as DeltaQYDc in AV1 specification + char qp_u_dc_delta_q; // indicates the U DC quantizer relative to base_q_idx. Defined as DeltaQUDc in AV1 specification + char qp_v_dc_delta_q; // indicates the V DC quantizer relative to base_q_idx. Defined as DeltaQVDc in AV1 specification + char qp_u_ac_delta_q; // indicates the U AC quantizer relative to base_q_idx. Defined as DeltaQUAc in AV1 specification + char qp_v_ac_delta_q; // indicates the V AC quantizer relative to base_q_idx. Defined as DeltaQVAc in AV1 specification + unsigned char qm_y; // specifies the level in the quantizer matrix that should be used for luma plane decoding + unsigned char qm_u; // specifies the level in the quantizer matrix that should be used for chroma U plane decoding + unsigned char qm_v; // specifies the level in the quantizer matrix that should be used for chroma V plane decoding + + // segmentation - refer to section 6.8.13 of the AV1 specification Version 1.0.0 with Errata 1 + unsigned char segmentation_enabled : 1; // 1 indicates that this frame makes use of the segmentation tool + unsigned char segmentation_update_map : 1; // 1 indicates that the segmentation map are updated during the decoding of this frame + unsigned char segmentation_update_data : 1; // 1 indicates that new parameters are about to be specified for each segment + unsigned char segmentation_temporal_update : 1; // 1 indicates that the updates to the segmentation map are coded relative to the existing segmentation map + unsigned char reserved3_4bits : 4; // reserved bits; must be set to 0 + short segmentation_feature_data[8][8]; // specifies the feature data for a segment feature + unsigned char segmentation_feature_mask[8]; // indicates that the corresponding feature is unused or feature value is coded + + // loopfilter - refer to section 6.8.10 of the AV1 specification Version 1.0.0 with Errata 1 + unsigned char loop_filter_level[2]; // contains loop filter strength values + unsigned char loop_filter_level_u; // loop filter strength value of U plane + unsigned char loop_filter_level_v; // loop filter strength value of V plane + unsigned char loop_filter_sharpness; // indicates the sharpness level + char loop_filter_ref_deltas[8]; // contains the adjustment needed for the filter level based on the chosen reference frame + char loop_filter_mode_deltas[2]; // contains the adjustment needed for the filter level based on the chosen mode + unsigned char loop_filter_delta_enabled : 1; // indicates that the filter level depends on the mode and reference frame used to predict a block + unsigned char loop_filter_delta_update : 1; // indicates that additional syntax elements are present that specify which mode and + // reference frame deltas are to be updated + unsigned char delta_lf_present : 1; // specifies whether loop filter delta values are present in the block level + unsigned char delta_lf_res : 2; // specifies the left shift to apply to the decoded loop filter values + unsigned char delta_lf_multi : 1; // separate loop filter deltas for Hy,Vy,U,V edges + unsigned char reserved4_2bits : 2; // reserved bits; must be set to 0 + + // restoration - refer to section 6.10.15 of the AV1 specification Version 1.0.0 with Errata 1 + unsigned char lr_unit_size[3]; // specifies the size of loop restoration units: 0: 32, 1: 64, 2: 128, 3: 256 + unsigned char lr_type[3] ; // used to compute FrameRestorationType + + // reference frames + unsigned char primary_ref_frame; // specifies which reference frame contains the CDF values and other state that should be + // loaded at the start of the frame + unsigned char ref_frame_map[8]; // frames in dpb that can be used as reference for current or future frames + + unsigned char temporal_layer_id : 4; // temporal layer id + unsigned char spatial_layer_id : 4; // spatial layer id + + unsigned char reserved5_32bits[4]; // reserved bits; must be set to 0 + + // ref frame list + struct + { + unsigned int width; + unsigned int height; + unsigned char index; + unsigned char reserved24Bits[3]; // reserved bits; must be set to 0 + } ref_frame[7]; // frames used as reference frame for current frame. + + // global motion + struct { + unsigned char invalid : 1; + unsigned char wmtype : 2; // defined as GmType in AV1 specification + unsigned char reserved5Bits : 5; // reserved bits; must be set to 0 + char reserved24Bits[3]; // reserved bits; must be set to 0 + int wmmat[6]; // defined as gm_params[] in AV1 specification + } global_motion[7]; // global motion params for reference frames + + // film grain params - refer to section 6.8.20 of the AV1 specification Version 1.0.0 with Errata 1 + unsigned short apply_grain : 1; + unsigned short overlap_flag : 1; + unsigned short scaling_shift_minus8 : 2; + unsigned short chroma_scaling_from_luma : 1; + unsigned short ar_coeff_lag : 2; + unsigned short ar_coeff_shift_minus6 : 2; + unsigned short grain_scale_shift : 2; + unsigned short clip_to_restricted_range : 1; + unsigned short reserved6_4bits : 4; // reserved bits; must be set to 0 + unsigned char num_y_points; + unsigned char scaling_points_y[14][2]; + unsigned char num_cb_points; + unsigned char scaling_points_cb[10][2]; + unsigned char num_cr_points; + unsigned char scaling_points_cr[10][2]; + unsigned char reserved7_8bits; // reserved bits; must be set to 0 + unsigned short random_seed; + short ar_coeffs_y[24]; + short ar_coeffs_cb[25]; + short ar_coeffs_cr[25]; + unsigned char cb_mult; + unsigned char cb_luma_mult; + short cb_offset; + unsigned char cr_mult; + unsigned char cr_luma_mult; + short cr_offset; + + int reserved[7]; // reserved bits; must be set to 0 +} CUVIDAV1PICPARAMS; + +/******************************************************************************************/ +//! \struct CUVIDPICPARAMS +//! Picture parameters for decoding +//! This structure is used in cuvidDecodePicture API +//! IN for cuvidDecodePicture +/******************************************************************************************/ +typedef struct _CUVIDPICPARAMS +{ + int PicWidthInMbs; /**< IN: Coded frame size in macroblocks */ + int FrameHeightInMbs; /**< IN: Coded frame height in macroblocks */ + int CurrPicIdx; /**< IN: Output index of the current picture */ + int field_pic_flag; /**< IN: 0=frame picture, 1=field picture */ + int bottom_field_flag; /**< IN: 0=top field, 1=bottom field (ignored if field_pic_flag=0) */ + int second_field; /**< IN: Second field of a complementary field pair */ + // Bitstream data + unsigned int nBitstreamDataLen; /**< IN: Number of bytes in bitstream data buffer */ + const unsigned char *pBitstreamData; /**< IN: Ptr to bitstream data for this picture (slice-layer) */ + unsigned int nNumSlices; /**< IN: Number of slices in this picture */ + const unsigned int *pSliceDataOffsets; /**< IN: nNumSlices entries, contains offset of each slice within + the bitstream data buffer */ + int ref_pic_flag; /**< IN: This picture is a reference picture */ + int intra_pic_flag; /**< IN: This picture is entirely intra coded */ + unsigned int Reserved[30]; /**< Reserved for future use */ + // IN: Codec-specific data + union { + CUVIDMPEG2PICPARAMS mpeg2; /**< Also used for MPEG-1 */ + CUVIDH264PICPARAMS h264; + CUVIDVC1PICPARAMS vc1; + CUVIDMPEG4PICPARAMS mpeg4; + CUVIDJPEGPICPARAMS jpeg; + CUVIDHEVCPICPARAMS hevc; + CUVIDVP8PICPARAMS vp8; + CUVIDVP9PICPARAMS vp9; + CUVIDAV1PICPARAMS av1; + unsigned int CodecReserved[1024]; + } CodecSpecific; +} CUVIDPICPARAMS; + + +/******************************************************/ +//! \struct CUVIDPROCPARAMS +//! Picture parameters for postprocessing +//! This structure is used in cuvidMapVideoFrame API +/******************************************************/ +typedef struct _CUVIDPROCPARAMS +{ + int progressive_frame; /**< IN: Input is progressive (deinterlace_mode will be ignored) */ + int second_field; /**< IN: Output the second field (ignored if deinterlace mode is Weave) */ + int top_field_first; /**< IN: Input frame is top field first (1st field is top, 2nd field is bottom) */ + int unpaired_field; /**< IN: Input only contains one field (2nd field is invalid) */ + // The fields below are used for raw YUV input + unsigned int reserved_flags; /**< Reserved for future use (set to zero) */ + unsigned int reserved_zero; /**< Reserved (set to zero) */ + unsigned long long raw_input_dptr; /**< IN: Input CUdeviceptr for raw YUV extensions */ + unsigned int raw_input_pitch; /**< IN: pitch in bytes of raw YUV input (should be aligned appropriately) */ + unsigned int raw_input_format; /**< IN: Input YUV format (cudaVideoCodec_enum) */ + unsigned long long raw_output_dptr; /**< IN: Output CUdeviceptr for raw YUV extensions */ + unsigned int raw_output_pitch; /**< IN: pitch in bytes of raw YUV output (should be aligned appropriately) */ + unsigned int Reserved1; /**< Reserved for future use (set to zero) */ + CUstream output_stream; /**< IN: stream object used by cuvidMapVideoFrame */ + unsigned int Reserved[46]; /**< Reserved for future use (set to zero) */ + unsigned long long *histogram_dptr; /**< OUT: Output CUdeviceptr for histogram extensions */ + void *Reserved2[1]; /**< Reserved for future use (set to zero) */ +} CUVIDPROCPARAMS; + +/*********************************************************************************************************/ +//! \struct CUVIDGETDECODESTATUS +//! Struct for reporting decode status. +//! This structure is used in cuvidGetDecodeStatus API. +/*********************************************************************************************************/ +typedef struct _CUVIDGETDECODESTATUS +{ + cuvidDecodeStatus decodeStatus; + unsigned int reserved[31]; + void *pReserved[8]; +} CUVIDGETDECODESTATUS; + +/****************************************************/ +//! \struct CUVIDRECONFIGUREDECODERINFO +//! Struct for decoder reset +//! This structure is used in cuvidReconfigureDecoder() API +/****************************************************/ +typedef struct _CUVIDRECONFIGUREDECODERINFO +{ + unsigned int ulWidth; /**< IN: Coded sequence width in pixels, MUST be < = ulMaxWidth defined at CUVIDDECODECREATEINFO */ + unsigned int ulHeight; /**< IN: Coded sequence height in pixels, MUST be < = ulMaxHeight defined at CUVIDDECODECREATEINFO */ + unsigned int ulTargetWidth; /**< IN: Post processed output width */ + unsigned int ulTargetHeight; /**< IN: Post Processed output height */ + unsigned int ulNumDecodeSurfaces; /**< IN: Maximum number of internal decode surfaces */ + unsigned int reserved1[12]; /**< Reserved for future use. Set to Zero */ + /** + * IN: Area of frame to be displayed. Use-case : Source Cropping + */ + struct { + short left; + short top; + short right; + short bottom; + } display_area; + /** + * IN: Target Rectangle in the OutputFrame. Use-case : Aspect ratio Conversion + */ + struct { + short left; + short top; + short right; + short bottom; + } target_rect; + unsigned int reserved2[11]; /**< Reserved for future use. Set to Zero */ +} CUVIDRECONFIGUREDECODERINFO; + + +/***********************************************************************************************************/ +//! VIDEO_DECODER +//! +//! In order to minimize decode latencies, there should be always at least 2 pictures in the decode +//! queue at any time, in order to make sure that all decode engines are always busy. +//! +//! Overall data flow: +//! - cuvidGetDecoderCaps(...) +//! - cuvidCreateDecoder(...) +//! - For each picture: +//! + cuvidDecodePicture(N) +//! + cuvidMapVideoFrame(N-4) +//! + do some processing in cuda +//! + cuvidUnmapVideoFrame(N-4) +//! + cuvidDecodePicture(N+1) +//! + cuvidMapVideoFrame(N-3) +//! + ... +//! - cuvidDestroyDecoder(...) +//! +//! NOTE: +//! - When the cuda context is created from a D3D device, the D3D device must also be created +//! with the D3DCREATE_MULTITHREADED flag. +//! - There is a limit to how many pictures can be mapped simultaneously (ulNumOutputSurfaces) +//! - cuvidDecodePicture may block the calling thread if there are too many pictures pending +//! in the decode queue +/***********************************************************************************************************/ + + +/**********************************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidGetDecoderCaps(CUVIDDECODECAPS *pdc) +//! Queries decode capabilities of NVDEC-HW based on CodecType, ChromaFormat and BitDepthMinus8 parameters. +//! 1. Application fills IN parameters CodecType, ChromaFormat and BitDepthMinus8 of CUVIDDECODECAPS structure +//! 2. On calling cuvidGetDecoderCaps, driver fills OUT parameters if the IN parameters are supported +//! If IN parameters passed to the driver are not supported by NVDEC-HW, then all OUT params are set to 0. +//! E.g. on Geforce GTX 960: +//! App fills - eCodecType = cudaVideoCodec_H264; eChromaFormat = cudaVideoChromaFormat_420; nBitDepthMinus8 = 0; +//! Given IN parameters are supported, hence driver fills: bIsSupported = 1; nMinWidth = 48; nMinHeight = 16; +//! nMaxWidth = 4096; nMaxHeight = 4096; nMaxMBCount = 65536; +//! CodedWidth*CodedHeight/256 must be less than or equal to nMaxMBCount +/**********************************************************************************************************************/ +extern CUresult CUDAAPI cuvidGetDecoderCaps(CUVIDDECODECAPS *pdc); + +/*****************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidCreateDecoder(CUvideodecoder *phDecoder, CUVIDDECODECREATEINFO *pdci) +//! Create the decoder object based on pdci. A handle to the created decoder is returned +/*****************************************************************************************************/ +extern CUresult CUDAAPI cuvidCreateDecoder(CUvideodecoder *phDecoder, CUVIDDECODECREATEINFO *pdci); + +/*****************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidDestroyDecoder(CUvideodecoder hDecoder) +//! Destroy the decoder object +/*****************************************************************************************************/ +extern CUresult CUDAAPI cuvidDestroyDecoder(CUvideodecoder hDecoder); + +/*****************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidDecodePicture(CUvideodecoder hDecoder, CUVIDPICPARAMS *pPicParams) +//! Decode a single picture (field or frame) +//! Kicks off HW decoding +/*****************************************************************************************************/ +extern CUresult CUDAAPI cuvidDecodePicture(CUvideodecoder hDecoder, CUVIDPICPARAMS *pPicParams); + +/************************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidGetDecodeStatus(CUvideodecoder hDecoder, int nPicIdx); +//! Get the decode status for frame corresponding to nPicIdx +//! API is supported for Maxwell and above generation GPUs. +//! API is currently supported for HEVC, H264 and JPEG codecs. +//! API returns CUDA_ERROR_NOT_SUPPORTED error code for unsupported GPU or codec. +/************************************************************************************************************/ +extern CUresult CUDAAPI cuvidGetDecodeStatus(CUvideodecoder hDecoder, int nPicIdx, CUVIDGETDECODESTATUS* pDecodeStatus); + +/*********************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidReconfigureDecoder(CUvideodecoder hDecoder, CUVIDRECONFIGUREDECODERINFO *pDecReconfigParams) +//! Used to reuse single decoder for multiple clips. Currently supports resolution change, resize params, display area +//! params, target area params change for same codec. Must be called during CUVIDPARSERPARAMS::pfnSequenceCallback +/*********************************************************************************************************/ +extern CUresult CUDAAPI cuvidReconfigureDecoder(CUvideodecoder hDecoder, CUVIDRECONFIGUREDECODERINFO *pDecReconfigParams); + + +#if !defined(__CUVID_DEVPTR64) || defined(__CUVID_INTERNAL) +/************************************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidMapVideoFrame(CUvideodecoder hDecoder, int nPicIdx, unsigned int *pDevPtr, +//! unsigned int *pPitch, CUVIDPROCPARAMS *pVPP); +//! Post-process and map video frame corresponding to nPicIdx for use in cuda. Returns cuda device pointer and associated +//! pitch of the video frame +/************************************************************************************************************************/ +extern CUresult CUDAAPI cuvidMapVideoFrame(CUvideodecoder hDecoder, int nPicIdx, + unsigned int *pDevPtr, unsigned int *pPitch, + CUVIDPROCPARAMS *pVPP); + +/*****************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidUnmapVideoFrame(CUvideodecoder hDecoder, unsigned int DevPtr) +//! Unmap a previously mapped video frame +/*****************************************************************************************************/ +extern CUresult CUDAAPI cuvidUnmapVideoFrame(CUvideodecoder hDecoder, unsigned int DevPtr); +#endif + +/****************************************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidMapVideoFrame64(CUvideodecoder hDecoder, int nPicIdx, unsigned long long *pDevPtr, +//! unsigned int * pPitch, CUVIDPROCPARAMS *pVPP); +//! Post-process and map video frame corresponding to nPicIdx for use in cuda. Returns cuda device pointer and associated +//! pitch of the video frame +/****************************************************************************************************************************/ +extern CUresult CUDAAPI cuvidMapVideoFrame64(CUvideodecoder hDecoder, int nPicIdx, unsigned long long *pDevPtr, + unsigned int *pPitch, CUVIDPROCPARAMS *pVPP); + +/**************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidUnmapVideoFrame64(CUvideodecoder hDecoder, unsigned long long DevPtr); +//! Unmap a previously mapped video frame +/**************************************************************************************************/ +extern CUresult CUDAAPI cuvidUnmapVideoFrame64(CUvideodecoder hDecoder, unsigned long long DevPtr); + +#if defined(__CUVID_DEVPTR64) && !defined(__CUVID_INTERNAL) +#define cuvidMapVideoFrame cuvidMapVideoFrame64 +#define cuvidUnmapVideoFrame cuvidUnmapVideoFrame64 +#endif + + + +/********************************************************************************************************************/ +//! +//! Context-locking: to facilitate multi-threaded implementations, the following 4 functions +//! provide a simple mutex-style host synchronization. If a non-NULL context is specified +//! in CUVIDDECODECREATEINFO, the codec library will acquire the mutex associated with the given +//! context before making any cuda calls. +//! A multi-threaded application could create a lock associated with a context handle so that +//! multiple threads can safely share the same cuda context: +//! - use cuCtxPopCurrent immediately after context creation in order to create a 'floating' context +//! that can be passed to cuvidCtxLockCreate. +//! - When using a floating context, all cuda calls should only be made within a cuvidCtxLock/cuvidCtxUnlock section. +//! +//! NOTE: This is a safer alternative to cuCtxPushCurrent and cuCtxPopCurrent, and is not related to video +//! decoder in any way (implemented as a critical section associated with cuCtx{Push|Pop}Current calls). +/********************************************************************************************************************/ + +/********************************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidCtxLockCreate(CUvideoctxlock *pLock, CUcontext ctx) +//! This API is used to create CtxLock object +/********************************************************************************************************************/ +extern CUresult CUDAAPI cuvidCtxLockCreate(CUvideoctxlock *pLock, CUcontext ctx); + +/********************************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidCtxLockDestroy(CUvideoctxlock lck) +//! This API is used to free CtxLock object +/********************************************************************************************************************/ +extern CUresult CUDAAPI cuvidCtxLockDestroy(CUvideoctxlock lck); + +/********************************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidCtxLock(CUvideoctxlock lck, unsigned int reserved_flags) +//! This API is used to acquire ctxlock +/********************************************************************************************************************/ +extern CUresult CUDAAPI cuvidCtxLock(CUvideoctxlock lck, unsigned int reserved_flags); + +/********************************************************************************************************************/ +//! \fn CUresult CUDAAPI cuvidCtxUnlock(CUvideoctxlock lck, unsigned int reserved_flags) +//! This API is used to release ctxlock +/********************************************************************************************************************/ +extern CUresult CUDAAPI cuvidCtxUnlock(CUvideoctxlock lck, unsigned int reserved_flags); + +/**********************************************************************************************/ + + +#if defined(__cplusplus) +} +// Auto-lock helper for C++ applications +class CCtxAutoLock +{ +private: + CUvideoctxlock m_ctx; +public: + CCtxAutoLock(CUvideoctxlock ctx):m_ctx(ctx) { cuvidCtxLock(m_ctx,0); } + ~CCtxAutoLock() { cuvidCtxUnlock(m_ctx,0); } +}; +#endif /* __cplusplus */ + +#endif // __CUDA_VIDEO_H__ + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/include/nvEncodeAPI.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/include/nvEncodeAPI.h new file mode 100644 index 0000000..8a56e84 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/include/nvEncodeAPI.h @@ -0,0 +1,4280 @@ +/* + * This copyright notice applies to this header file only: + * + * Copyright (c) 2010-2022 NVIDIA Corporation + * + * 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. + */ + +/** + * \file nvEncodeAPI.h + * NVIDIA GPUs - beginning with the Kepler generation - contain a hardware-based encoder + * (referred to as NVENC) which provides fully-accelerated hardware-based video encoding. + * NvEncodeAPI provides the interface for NVIDIA video encoder (NVENC). + * \date 2011-2022 + * This file contains the interface constants, structure definitions and function prototypes. + */ + +#ifndef _NV_ENCODEAPI_H_ +#define _NV_ENCODEAPI_H_ + +#include + +#ifdef _WIN32 +#include +#endif + +#ifdef _MSC_VER +#ifndef _STDINT +typedef __int32 int32_t; +typedef unsigned __int32 uint32_t; +typedef __int64 int64_t; +typedef unsigned __int64 uint64_t; +typedef signed char int8_t; +typedef unsigned char uint8_t; +typedef short int16_t; +typedef unsigned short uint16_t; +#endif +#else +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \addtogroup ENCODER_STRUCTURE NvEncodeAPI Data structures + * @{ + */ + +#ifdef _WIN32 +#define NVENCAPI __stdcall +typedef RECT NVENC_RECT; +#else +#define NVENCAPI +// ========================================================================================= +#ifndef GUID_DEFINED +#define GUID_DEFINED +/*! + * \struct GUID + * Abstracts the GUID structure for non-windows platforms. + */ +// ========================================================================================= +typedef struct _GUID +{ + uint32_t Data1; /**< [in]: Specifies the first 8 hexadecimal digits of the GUID. */ + uint16_t Data2; /**< [in]: Specifies the first group of 4 hexadecimal digits. */ + uint16_t Data3; /**< [in]: Specifies the second group of 4 hexadecimal digits. */ + uint8_t Data4[8]; /**< [in]: Array of 8 bytes. The first 2 bytes contain the third group of 4 hexadecimal digits. + The remaining 6 bytes contain the final 12 hexadecimal digits. */ +} GUID, *LPGUID; +#endif // GUID + +/** + * \struct _NVENC_RECT + * Defines a Rectangle. Used in ::NV_ENC_PREPROCESS_FRAME. + */ +typedef struct _NVENC_RECT +{ + uint32_t left; /**< [in]: X coordinate of the upper left corner of rectangular area to be specified. */ + uint32_t top; /**< [in]: Y coordinate of the upper left corner of the rectangular area to be specified. */ + uint32_t right; /**< [in]: X coordinate of the bottom right corner of the rectangular area to be specified. */ + uint32_t bottom; /**< [in]: Y coordinate of the bottom right corner of the rectangular area to be specified. */ +} NVENC_RECT; + +#endif // _WIN32 + +/** @} */ /* End of GUID and NVENC_RECT structure grouping*/ + +typedef void* NV_ENC_INPUT_PTR; /**< NVENCODE API input buffer */ +typedef void* NV_ENC_OUTPUT_PTR; /**< NVENCODE API output buffer*/ +typedef void* NV_ENC_REGISTERED_PTR; /**< A Resource that has been registered with NVENCODE API*/ +typedef void* NV_ENC_CUSTREAM_PTR; /**< Pointer to CUstream*/ + +#define NVENCAPI_MAJOR_VERSION 12 +#define NVENCAPI_MINOR_VERSION 0 + +#define NVENCAPI_VERSION (NVENCAPI_MAJOR_VERSION | (NVENCAPI_MINOR_VERSION << 24)) + +/** + * Macro to generate per-structure version for use with API. + */ +#define NVENCAPI_STRUCT_VERSION(ver) ((uint32_t)NVENCAPI_VERSION | ((ver)<<16) | (0x7 << 28)) + + +#define NVENC_INFINITE_GOPLENGTH 0xffffffff + +#define NV_MAX_SEQ_HDR_LEN (512) + +#ifdef __GNUC__ +#define NV_ENC_DEPRECATED __attribute__ ((deprecated("WILL BE REMOVED IN A FUTURE VIDEO CODEC SDK VERSION"))) +#elif defined(_MSC_VER) +#define NV_ENC_DEPRECATED __declspec(deprecated("WILL BE REMOVED IN A FUTURE VIDEO CODEC SDK VERSION")) +#endif + +// ========================================================================================= +// Encode Codec GUIDS supported by the NvEncodeAPI interface. +// ========================================================================================= + +// {6BC82762-4E63-4ca4-AA85-1E50F321F6BF} +static const GUID NV_ENC_CODEC_H264_GUID = +{ 0x6bc82762, 0x4e63, 0x4ca4, { 0xaa, 0x85, 0x1e, 0x50, 0xf3, 0x21, 0xf6, 0xbf } }; + +// {790CDC88-4522-4d7b-9425-BDA9975F7603} +static const GUID NV_ENC_CODEC_HEVC_GUID = +{ 0x790cdc88, 0x4522, 0x4d7b, { 0x94, 0x25, 0xbd, 0xa9, 0x97, 0x5f, 0x76, 0x3 } }; + +// {0A352289-0AA7-4759-862D-5D15CD16D254} +static const GUID NV_ENC_CODEC_AV1_GUID = +{ 0x0a352289, 0x0aa7, 0x4759, { 0x86, 0x2d, 0x5d, 0x15, 0xcd, 0x16, 0xd2, 0x54 } }; + + + +// ========================================================================================= +// * Encode Profile GUIDS supported by the NvEncodeAPI interface. +// ========================================================================================= + +// {BFD6F8E7-233C-4341-8B3E-4818523803F4} +static const GUID NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID = +{ 0xbfd6f8e7, 0x233c, 0x4341, { 0x8b, 0x3e, 0x48, 0x18, 0x52, 0x38, 0x3, 0xf4 } }; + +// {0727BCAA-78C4-4c83-8C2F-EF3DFF267C6A} +static const GUID NV_ENC_H264_PROFILE_BASELINE_GUID = +{ 0x727bcaa, 0x78c4, 0x4c83, { 0x8c, 0x2f, 0xef, 0x3d, 0xff, 0x26, 0x7c, 0x6a } }; + +// {60B5C1D4-67FE-4790-94D5-C4726D7B6E6D} +static const GUID NV_ENC_H264_PROFILE_MAIN_GUID = +{ 0x60b5c1d4, 0x67fe, 0x4790, { 0x94, 0xd5, 0xc4, 0x72, 0x6d, 0x7b, 0x6e, 0x6d } }; + +// {E7CBC309-4F7A-4b89-AF2A-D537C92BE310} +static const GUID NV_ENC_H264_PROFILE_HIGH_GUID = +{ 0xe7cbc309, 0x4f7a, 0x4b89, { 0xaf, 0x2a, 0xd5, 0x37, 0xc9, 0x2b, 0xe3, 0x10 } }; + +// {7AC663CB-A598-4960-B844-339B261A7D52} +static const GUID NV_ENC_H264_PROFILE_HIGH_444_GUID = +{ 0x7ac663cb, 0xa598, 0x4960, { 0xb8, 0x44, 0x33, 0x9b, 0x26, 0x1a, 0x7d, 0x52 } }; + +// {40847BF5-33F7-4601-9084-E8FE3C1DB8B7} +static const GUID NV_ENC_H264_PROFILE_STEREO_GUID = +{ 0x40847bf5, 0x33f7, 0x4601, { 0x90, 0x84, 0xe8, 0xfe, 0x3c, 0x1d, 0xb8, 0xb7 } }; + +// {B405AFAC-F32B-417B-89C4-9ABEED3E5978} +static const GUID NV_ENC_H264_PROFILE_PROGRESSIVE_HIGH_GUID = +{ 0xb405afac, 0xf32b, 0x417b, { 0x89, 0xc4, 0x9a, 0xbe, 0xed, 0x3e, 0x59, 0x78 } }; + +// {AEC1BD87-E85B-48f2-84C3-98BCA6285072} +static const GUID NV_ENC_H264_PROFILE_CONSTRAINED_HIGH_GUID = +{ 0xaec1bd87, 0xe85b, 0x48f2, { 0x84, 0xc3, 0x98, 0xbc, 0xa6, 0x28, 0x50, 0x72 } }; + +// {B514C39A-B55B-40fa-878F-F1253B4DFDEC} +static const GUID NV_ENC_HEVC_PROFILE_MAIN_GUID = +{ 0xb514c39a, 0xb55b, 0x40fa, { 0x87, 0x8f, 0xf1, 0x25, 0x3b, 0x4d, 0xfd, 0xec } }; + +// {fa4d2b6c-3a5b-411a-8018-0a3f5e3c9be5} +static const GUID NV_ENC_HEVC_PROFILE_MAIN10_GUID = +{ 0xfa4d2b6c, 0x3a5b, 0x411a, { 0x80, 0x18, 0x0a, 0x3f, 0x5e, 0x3c, 0x9b, 0xe5 } }; + +// For HEVC Main 444 8 bit and HEVC Main 444 10 bit profiles only +// {51ec32b5-1b4c-453c-9cbd-b616bd621341} +static const GUID NV_ENC_HEVC_PROFILE_FREXT_GUID = +{ 0x51ec32b5, 0x1b4c, 0x453c, { 0x9c, 0xbd, 0xb6, 0x16, 0xbd, 0x62, 0x13, 0x41 } }; + +// {5f2a39f5-f14e-4f95-9a9e-b76d568fcf97} +static const GUID NV_ENC_AV1_PROFILE_MAIN_GUID = +{ 0x5f2a39f5, 0xf14e, 0x4f95, { 0x9a, 0x9e, 0xb7, 0x6d, 0x56, 0x8f, 0xcf, 0x97 } }; + +// ========================================================================================= +// * Preset GUIDS supported by the NvEncodeAPI interface. +// ========================================================================================= +// {B2DFB705-4EBD-4C49-9B5F-24A777D3E587} +NV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_DEFAULT_GUID = +{ 0xb2dfb705, 0x4ebd, 0x4c49, { 0x9b, 0x5f, 0x24, 0xa7, 0x77, 0xd3, 0xe5, 0x87 } }; + +// {60E4C59F-E846-4484-A56D-CD45BE9FDDF6} +NV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_HP_GUID = +{ 0x60e4c59f, 0xe846, 0x4484, { 0xa5, 0x6d, 0xcd, 0x45, 0xbe, 0x9f, 0xdd, 0xf6 } }; + +// {34DBA71D-A77B-4B8F-9C3E-B6D5DA24C012} +NV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_HQ_GUID = +{ 0x34dba71d, 0xa77b, 0x4b8f, { 0x9c, 0x3e, 0xb6, 0xd5, 0xda, 0x24, 0xc0, 0x12 } }; + +// {82E3E450-BDBB-4e40-989C-82A90DF9EF32} +NV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_BD_GUID = +{ 0x82e3e450, 0xbdbb, 0x4e40, { 0x98, 0x9c, 0x82, 0xa9, 0xd, 0xf9, 0xef, 0x32 } }; + +// {49DF21C5-6DFA-4feb-9787-6ACC9EFFB726} +NV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_LOW_LATENCY_DEFAULT_GUID = +{ 0x49df21c5, 0x6dfa, 0x4feb, { 0x97, 0x87, 0x6a, 0xcc, 0x9e, 0xff, 0xb7, 0x26 } }; + +// {C5F733B9-EA97-4cf9-BEC2-BF78A74FD105} +NV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_LOW_LATENCY_HQ_GUID = +{ 0xc5f733b9, 0xea97, 0x4cf9, { 0xbe, 0xc2, 0xbf, 0x78, 0xa7, 0x4f, 0xd1, 0x5 } }; + +// {67082A44-4BAD-48FA-98EA-93056D150A58} +NV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_LOW_LATENCY_HP_GUID = +{ 0x67082a44, 0x4bad, 0x48fa, { 0x98, 0xea, 0x93, 0x5, 0x6d, 0x15, 0xa, 0x58 } }; + +// {D5BFB716-C604-44e7-9BB8-DEA5510FC3AC} +NV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_LOSSLESS_DEFAULT_GUID = +{ 0xd5bfb716, 0xc604, 0x44e7, { 0x9b, 0xb8, 0xde, 0xa5, 0x51, 0xf, 0xc3, 0xac } }; + +// {149998E7-2364-411d-82EF-179888093409} +NV_ENC_DEPRECATED static const GUID NV_ENC_PRESET_LOSSLESS_HP_GUID = +{ 0x149998e7, 0x2364, 0x411d, { 0x82, 0xef, 0x17, 0x98, 0x88, 0x9, 0x34, 0x9 } }; + +// Performance degrades and quality improves as we move from P1 to P7. Presets P3 to P7 for H264 and Presets P2 to P7 for HEVC have B frames enabled by default +// for HIGH_QUALITY and LOSSLESS tuning info, and will not work with Weighted Prediction enabled. In case Weighted Prediction is required, disable B frames by +// setting frameIntervalP = 1 +// {FC0A8D3E-45F8-4CF8-80C7-298871590EBF} +static const GUID NV_ENC_PRESET_P1_GUID = +{ 0xfc0a8d3e, 0x45f8, 0x4cf8, { 0x80, 0xc7, 0x29, 0x88, 0x71, 0x59, 0xe, 0xbf } }; + +// {F581CFB8-88D6-4381-93F0-DF13F9C27DAB} +static const GUID NV_ENC_PRESET_P2_GUID = +{ 0xf581cfb8, 0x88d6, 0x4381, { 0x93, 0xf0, 0xdf, 0x13, 0xf9, 0xc2, 0x7d, 0xab } }; + +// {36850110-3A07-441F-94D5-3670631F91F6} +static const GUID NV_ENC_PRESET_P3_GUID = +{ 0x36850110, 0x3a07, 0x441f, { 0x94, 0xd5, 0x36, 0x70, 0x63, 0x1f, 0x91, 0xf6 } }; + +// {90A7B826-DF06-4862-B9D2-CD6D73A08681} +static const GUID NV_ENC_PRESET_P4_GUID = +{ 0x90a7b826, 0xdf06, 0x4862, { 0xb9, 0xd2, 0xcd, 0x6d, 0x73, 0xa0, 0x86, 0x81 } }; + +// {21C6E6B4-297A-4CBA-998F-B6CBDE72ADE3} +static const GUID NV_ENC_PRESET_P5_GUID = +{ 0x21c6e6b4, 0x297a, 0x4cba, { 0x99, 0x8f, 0xb6, 0xcb, 0xde, 0x72, 0xad, 0xe3 } }; + +// {8E75C279-6299-4AB6-8302-0B215A335CF5} +static const GUID NV_ENC_PRESET_P6_GUID = +{ 0x8e75c279, 0x6299, 0x4ab6, { 0x83, 0x2, 0xb, 0x21, 0x5a, 0x33, 0x5c, 0xf5 } }; + +// {84848C12-6F71-4C13-931B-53E283F57974} +static const GUID NV_ENC_PRESET_P7_GUID = +{ 0x84848c12, 0x6f71, 0x4c13, { 0x93, 0x1b, 0x53, 0xe2, 0x83, 0xf5, 0x79, 0x74 } }; + +/** + * \addtogroup ENCODER_STRUCTURE NvEncodeAPI Data structures + * @{ + */ + +/** + * Input frame encode modes + */ +typedef enum _NV_ENC_PARAMS_FRAME_FIELD_MODE +{ + NV_ENC_PARAMS_FRAME_FIELD_MODE_FRAME = 0x01, /**< Frame mode */ + NV_ENC_PARAMS_FRAME_FIELD_MODE_FIELD = 0x02, /**< Field mode */ + NV_ENC_PARAMS_FRAME_FIELD_MODE_MBAFF = 0x03 /**< MB adaptive frame/field */ +} NV_ENC_PARAMS_FRAME_FIELD_MODE; + +/** + * Rate Control Modes + */ +typedef enum _NV_ENC_PARAMS_RC_MODE +{ + NV_ENC_PARAMS_RC_CONSTQP = 0x0, /**< Constant QP mode */ + NV_ENC_PARAMS_RC_VBR = 0x1, /**< Variable bitrate mode */ + NV_ENC_PARAMS_RC_CBR = 0x2, /**< Constant bitrate mode */ + NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ = 0x8, /**< Deprecated, use NV_ENC_PARAMS_RC_CBR + NV_ENC_TWO_PASS_QUARTER_RESOLUTION / NV_ENC_TWO_PASS_FULL_RESOLUTION + + lowDelayKeyFrameScale=1 */ + NV_ENC_PARAMS_RC_CBR_HQ = 0x10, /**< Deprecated, use NV_ENC_PARAMS_RC_CBR + NV_ENC_TWO_PASS_QUARTER_RESOLUTION / NV_ENC_TWO_PASS_FULL_RESOLUTION */ + NV_ENC_PARAMS_RC_VBR_HQ = 0x20 /**< Deprecated, use NV_ENC_PARAMS_RC_VBR + NV_ENC_TWO_PASS_QUARTER_RESOLUTION / NV_ENC_TWO_PASS_FULL_RESOLUTION */ +} NV_ENC_PARAMS_RC_MODE; + +/** + * Multi Pass encoding + */ +typedef enum _NV_ENC_MULTI_PASS +{ + NV_ENC_MULTI_PASS_DISABLED = 0x0, /**< Single Pass */ + NV_ENC_TWO_PASS_QUARTER_RESOLUTION = 0x1, /**< Two Pass encoding is enabled where first Pass is quarter resolution */ + NV_ENC_TWO_PASS_FULL_RESOLUTION = 0x2, /**< Two Pass encoding is enabled where first Pass is full resolution */ +} NV_ENC_MULTI_PASS; + +/** + * Emphasis Levels + */ +typedef enum _NV_ENC_EMPHASIS_MAP_LEVEL +{ + NV_ENC_EMPHASIS_MAP_LEVEL_0 = 0x0, /**< Emphasis Map Level 0, for zero Delta QP value */ + NV_ENC_EMPHASIS_MAP_LEVEL_1 = 0x1, /**< Emphasis Map Level 1, for very low Delta QP value */ + NV_ENC_EMPHASIS_MAP_LEVEL_2 = 0x2, /**< Emphasis Map Level 2, for low Delta QP value */ + NV_ENC_EMPHASIS_MAP_LEVEL_3 = 0x3, /**< Emphasis Map Level 3, for medium Delta QP value */ + NV_ENC_EMPHASIS_MAP_LEVEL_4 = 0x4, /**< Emphasis Map Level 4, for high Delta QP value */ + NV_ENC_EMPHASIS_MAP_LEVEL_5 = 0x5 /**< Emphasis Map Level 5, for very high Delta QP value */ +} NV_ENC_EMPHASIS_MAP_LEVEL; + +/** + * QP MAP MODE + */ +typedef enum _NV_ENC_QP_MAP_MODE +{ + NV_ENC_QP_MAP_DISABLED = 0x0, /**< Value in NV_ENC_PIC_PARAMS::qpDeltaMap have no effect. */ + NV_ENC_QP_MAP_EMPHASIS = 0x1, /**< Value in NV_ENC_PIC_PARAMS::qpDeltaMap will be treated as Emphasis level. Currently this is only supported for H264 */ + NV_ENC_QP_MAP_DELTA = 0x2, /**< Value in NV_ENC_PIC_PARAMS::qpDeltaMap will be treated as QP delta map. */ + NV_ENC_QP_MAP = 0x3, /**< Currently This is not supported. Value in NV_ENC_PIC_PARAMS::qpDeltaMap will be treated as QP value. */ +} NV_ENC_QP_MAP_MODE; + +#define NV_ENC_PARAMS_RC_VBR_MINQP (NV_ENC_PARAMS_RC_MODE)0x4 /**< Deprecated */ +#define NV_ENC_PARAMS_RC_2_PASS_QUALITY NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ /**< Deprecated */ +#define NV_ENC_PARAMS_RC_2_PASS_FRAMESIZE_CAP NV_ENC_PARAMS_RC_CBR_HQ /**< Deprecated */ +#define NV_ENC_PARAMS_RC_2_PASS_VBR NV_ENC_PARAMS_RC_VBR_HQ /**< Deprecated */ +#define NV_ENC_PARAMS_RC_CBR2 NV_ENC_PARAMS_RC_CBR /**< Deprecated */ + +/** + * Input picture structure + */ +typedef enum _NV_ENC_PIC_STRUCT +{ + NV_ENC_PIC_STRUCT_FRAME = 0x01, /**< Progressive frame */ + NV_ENC_PIC_STRUCT_FIELD_TOP_BOTTOM = 0x02, /**< Field encoding top field first */ + NV_ENC_PIC_STRUCT_FIELD_BOTTOM_TOP = 0x03 /**< Field encoding bottom field first */ +} NV_ENC_PIC_STRUCT; + +/** + * Display picture structure + * Currently, this enum is only used for deciding the number of clock timestamp sets in Picture Timing SEI / Time Code SEI + * Otherwise, this has no impact on encoder behavior + */ +typedef enum _NV_ENC_DISPLAY_PIC_STRUCT +{ + NV_ENC_PIC_STRUCT_DISPLAY_FRAME = 0x00, /**< Field encoding top field first */ + NV_ENC_PIC_STRUCT_DISPLAY_FIELD_TOP_BOTTOM = 0x01, /**< Field encoding top field first */ + NV_ENC_PIC_STRUCT_DISPLAY_FIELD_BOTTOM_TOP = 0x02, /**< Field encoding bottom field first */ + NV_ENC_PIC_STRUCT_DISPLAY_FRAME_DOUBLING = 0x03, /**< Frame doubling */ + NV_ENC_PIC_STRUCT_DISPLAY_FRAME_TRIPLING = 0x04 /**< Field tripling */ +} NV_ENC_DISPLAY_PIC_STRUCT; + +/** + * Input picture type + */ +typedef enum _NV_ENC_PIC_TYPE +{ + NV_ENC_PIC_TYPE_P = 0x0, /**< Forward predicted */ + NV_ENC_PIC_TYPE_B = 0x01, /**< Bi-directionally predicted picture */ + NV_ENC_PIC_TYPE_I = 0x02, /**< Intra predicted picture */ + NV_ENC_PIC_TYPE_IDR = 0x03, /**< IDR picture */ + NV_ENC_PIC_TYPE_BI = 0x04, /**< Bi-directionally predicted with only Intra MBs */ + NV_ENC_PIC_TYPE_SKIPPED = 0x05, /**< Picture is skipped */ + NV_ENC_PIC_TYPE_INTRA_REFRESH = 0x06, /**< First picture in intra refresh cycle */ + NV_ENC_PIC_TYPE_NONREF_P = 0x07, /**< Non reference P picture */ + NV_ENC_PIC_TYPE_UNKNOWN = 0xFF /**< Picture type unknown */ +} NV_ENC_PIC_TYPE; + +/** + * Motion vector precisions + */ +typedef enum _NV_ENC_MV_PRECISION +{ + NV_ENC_MV_PRECISION_DEFAULT = 0x0, /**< Driver selects Quarter-Pel motion vector precision by default */ + NV_ENC_MV_PRECISION_FULL_PEL = 0x01, /**< Full-Pel motion vector precision */ + NV_ENC_MV_PRECISION_HALF_PEL = 0x02, /**< Half-Pel motion vector precision */ + NV_ENC_MV_PRECISION_QUARTER_PEL = 0x03 /**< Quarter-Pel motion vector precision */ +} NV_ENC_MV_PRECISION; + + +/** + * Input buffer formats + */ +typedef enum _NV_ENC_BUFFER_FORMAT +{ + NV_ENC_BUFFER_FORMAT_UNDEFINED = 0x00000000, /**< Undefined buffer format */ + + NV_ENC_BUFFER_FORMAT_NV12 = 0x00000001, /**< Semi-Planar YUV [Y plane followed by interleaved UV plane] */ + NV_ENC_BUFFER_FORMAT_YV12 = 0x00000010, /**< Planar YUV [Y plane followed by V and U planes] */ + NV_ENC_BUFFER_FORMAT_IYUV = 0x00000100, /**< Planar YUV [Y plane followed by U and V planes] */ + NV_ENC_BUFFER_FORMAT_YUV444 = 0x00001000, /**< Planar YUV [Y plane followed by U and V planes] */ + NV_ENC_BUFFER_FORMAT_YUV420_10BIT = 0x00010000, /**< 10 bit Semi-Planar YUV [Y plane followed by interleaved UV plane]. Each pixel of size 2 bytes. Most Significant 10 bits contain pixel data. */ + NV_ENC_BUFFER_FORMAT_YUV444_10BIT = 0x00100000, /**< 10 bit Planar YUV444 [Y plane followed by U and V planes]. Each pixel of size 2 bytes. Most Significant 10 bits contain pixel data. */ + NV_ENC_BUFFER_FORMAT_ARGB = 0x01000000, /**< 8 bit Packed A8R8G8B8. This is a word-ordered format + where a pixel is represented by a 32-bit word with B + in the lowest 8 bits, G in the next 8 bits, R in the + 8 bits after that and A in the highest 8 bits. */ + NV_ENC_BUFFER_FORMAT_ARGB10 = 0x02000000, /**< 10 bit Packed A2R10G10B10. This is a word-ordered format + where a pixel is represented by a 32-bit word with B + in the lowest 10 bits, G in the next 10 bits, R in the + 10 bits after that and A in the highest 2 bits. */ + NV_ENC_BUFFER_FORMAT_AYUV = 0x04000000, /**< 8 bit Packed A8Y8U8V8. This is a word-ordered format + where a pixel is represented by a 32-bit word with V + in the lowest 8 bits, U in the next 8 bits, Y in the + 8 bits after that and A in the highest 8 bits. */ + NV_ENC_BUFFER_FORMAT_ABGR = 0x10000000, /**< 8 bit Packed A8B8G8R8. This is a word-ordered format + where a pixel is represented by a 32-bit word with R + in the lowest 8 bits, G in the next 8 bits, B in the + 8 bits after that and A in the highest 8 bits. */ + NV_ENC_BUFFER_FORMAT_ABGR10 = 0x20000000, /**< 10 bit Packed A2B10G10R10. This is a word-ordered format + where a pixel is represented by a 32-bit word with R + in the lowest 10 bits, G in the next 10 bits, B in the + 10 bits after that and A in the highest 2 bits. */ + NV_ENC_BUFFER_FORMAT_U8 = 0x40000000, /**< Buffer format representing one-dimensional buffer. + This format should be used only when registering the + resource as output buffer, which will be used to write + the encoded bit stream or H.264 ME only mode output. */ +} NV_ENC_BUFFER_FORMAT; + +#define NV_ENC_BUFFER_FORMAT_NV12_PL NV_ENC_BUFFER_FORMAT_NV12 +#define NV_ENC_BUFFER_FORMAT_YV12_PL NV_ENC_BUFFER_FORMAT_YV12 +#define NV_ENC_BUFFER_FORMAT_IYUV_PL NV_ENC_BUFFER_FORMAT_IYUV +#define NV_ENC_BUFFER_FORMAT_YUV444_PL NV_ENC_BUFFER_FORMAT_YUV444 + +/** + * Encoding levels + */ +typedef enum _NV_ENC_LEVEL +{ + NV_ENC_LEVEL_AUTOSELECT = 0, + + NV_ENC_LEVEL_H264_1 = 10, + NV_ENC_LEVEL_H264_1b = 9, + NV_ENC_LEVEL_H264_11 = 11, + NV_ENC_LEVEL_H264_12 = 12, + NV_ENC_LEVEL_H264_13 = 13, + NV_ENC_LEVEL_H264_2 = 20, + NV_ENC_LEVEL_H264_21 = 21, + NV_ENC_LEVEL_H264_22 = 22, + NV_ENC_LEVEL_H264_3 = 30, + NV_ENC_LEVEL_H264_31 = 31, + NV_ENC_LEVEL_H264_32 = 32, + NV_ENC_LEVEL_H264_4 = 40, + NV_ENC_LEVEL_H264_41 = 41, + NV_ENC_LEVEL_H264_42 = 42, + NV_ENC_LEVEL_H264_5 = 50, + NV_ENC_LEVEL_H264_51 = 51, + NV_ENC_LEVEL_H264_52 = 52, + NV_ENC_LEVEL_H264_60 = 60, + NV_ENC_LEVEL_H264_61 = 61, + NV_ENC_LEVEL_H264_62 = 62, + + NV_ENC_LEVEL_HEVC_1 = 30, + NV_ENC_LEVEL_HEVC_2 = 60, + NV_ENC_LEVEL_HEVC_21 = 63, + NV_ENC_LEVEL_HEVC_3 = 90, + NV_ENC_LEVEL_HEVC_31 = 93, + NV_ENC_LEVEL_HEVC_4 = 120, + NV_ENC_LEVEL_HEVC_41 = 123, + NV_ENC_LEVEL_HEVC_5 = 150, + NV_ENC_LEVEL_HEVC_51 = 153, + NV_ENC_LEVEL_HEVC_52 = 156, + NV_ENC_LEVEL_HEVC_6 = 180, + NV_ENC_LEVEL_HEVC_61 = 183, + NV_ENC_LEVEL_HEVC_62 = 186, + + NV_ENC_TIER_HEVC_MAIN = 0, + NV_ENC_TIER_HEVC_HIGH = 1, + + NV_ENC_LEVEL_AV1_2 = 0, + NV_ENC_LEVEL_AV1_21 = 1, + NV_ENC_LEVEL_AV1_22 = 2, + NV_ENC_LEVEL_AV1_23 = 3, + NV_ENC_LEVEL_AV1_3 = 4, + NV_ENC_LEVEL_AV1_31 = 5, + NV_ENC_LEVEL_AV1_32 = 6, + NV_ENC_LEVEL_AV1_33 = 7, + NV_ENC_LEVEL_AV1_4 = 8, + NV_ENC_LEVEL_AV1_41 = 9, + NV_ENC_LEVEL_AV1_42 = 10, + NV_ENC_LEVEL_AV1_43 = 11, + NV_ENC_LEVEL_AV1_5 = 12, + NV_ENC_LEVEL_AV1_51 = 13, + NV_ENC_LEVEL_AV1_52 = 14, + NV_ENC_LEVEL_AV1_53 = 15, + NV_ENC_LEVEL_AV1_6 = 16, + NV_ENC_LEVEL_AV1_61 = 17, + NV_ENC_LEVEL_AV1_62 = 18, + NV_ENC_LEVEL_AV1_63 = 19, + NV_ENC_LEVEL_AV1_7 = 20, + NV_ENC_LEVEL_AV1_71 = 21, + NV_ENC_LEVEL_AV1_72 = 22, + NV_ENC_LEVEL_AV1_73 = 23, + NV_ENC_LEVEL_AV1_AUTOSELECT , + + NV_ENC_TIER_AV1_0 = 0, + NV_ENC_TIER_AV1_1 = 1 +} NV_ENC_LEVEL; + +/** + * Error Codes + */ +typedef enum _NVENCSTATUS +{ + /** + * This indicates that API call returned with no errors. + */ + NV_ENC_SUCCESS, + + /** + * This indicates that no encode capable devices were detected. + */ + NV_ENC_ERR_NO_ENCODE_DEVICE, + + /** + * This indicates that devices pass by the client is not supported. + */ + NV_ENC_ERR_UNSUPPORTED_DEVICE, + + /** + * This indicates that the encoder device supplied by the client is not + * valid. + */ + NV_ENC_ERR_INVALID_ENCODERDEVICE, + + /** + * This indicates that device passed to the API call is invalid. + */ + NV_ENC_ERR_INVALID_DEVICE, + + /** + * This indicates that device passed to the API call is no longer available and + * needs to be reinitialized. The clients need to destroy the current encoder + * session by freeing the allocated input output buffers and destroying the device + * and create a new encoding session. + */ + NV_ENC_ERR_DEVICE_NOT_EXIST, + + /** + * This indicates that one or more of the pointers passed to the API call + * is invalid. + */ + NV_ENC_ERR_INVALID_PTR, + + /** + * This indicates that completion event passed in ::NvEncEncodePicture() call + * is invalid. + */ + NV_ENC_ERR_INVALID_EVENT, + + /** + * This indicates that one or more of the parameter passed to the API call + * is invalid. + */ + NV_ENC_ERR_INVALID_PARAM, + + /** + * This indicates that an API call was made in wrong sequence/order. + */ + NV_ENC_ERR_INVALID_CALL, + + /** + * This indicates that the API call failed because it was unable to allocate + * enough memory to perform the requested operation. + */ + NV_ENC_ERR_OUT_OF_MEMORY, + + /** + * This indicates that the encoder has not been initialized with + * ::NvEncInitializeEncoder() or that initialization has failed. + * The client cannot allocate input or output buffers or do any encoding + * related operation before successfully initializing the encoder. + */ + NV_ENC_ERR_ENCODER_NOT_INITIALIZED, + + /** + * This indicates that an unsupported parameter was passed by the client. + */ + NV_ENC_ERR_UNSUPPORTED_PARAM, + + /** + * This indicates that the ::NvEncLockBitstream() failed to lock the output + * buffer. This happens when the client makes a non blocking lock call to + * access the output bitstream by passing NV_ENC_LOCK_BITSTREAM::doNotWait flag. + * This is not a fatal error and client should retry the same operation after + * few milliseconds. + */ + NV_ENC_ERR_LOCK_BUSY, + + /** + * This indicates that the size of the user buffer passed by the client is + * insufficient for the requested operation. + */ + NV_ENC_ERR_NOT_ENOUGH_BUFFER, + + /** + * This indicates that an invalid struct version was used by the client. + */ + NV_ENC_ERR_INVALID_VERSION, + + /** + * This indicates that ::NvEncMapInputResource() API failed to map the client + * provided input resource. + */ + NV_ENC_ERR_MAP_FAILED, + + /** + * This indicates encode driver requires more input buffers to produce an output + * bitstream. If this error is returned from ::NvEncEncodePicture() API, this + * is not a fatal error. If the client is encoding with B frames then, + * ::NvEncEncodePicture() API might be buffering the input frame for re-ordering. + * + * A client operating in synchronous mode cannot call ::NvEncLockBitstream() + * API on the output bitstream buffer if ::NvEncEncodePicture() returned the + * ::NV_ENC_ERR_NEED_MORE_INPUT error code. + * The client must continue providing input frames until encode driver returns + * ::NV_ENC_SUCCESS. After receiving ::NV_ENC_SUCCESS status the client can call + * ::NvEncLockBitstream() API on the output buffers in the same order in which + * it has called ::NvEncEncodePicture(). + */ + NV_ENC_ERR_NEED_MORE_INPUT, + + /** + * This indicates that the HW encoder is busy encoding and is unable to encode + * the input. The client should call ::NvEncEncodePicture() again after few + * milliseconds. + */ + NV_ENC_ERR_ENCODER_BUSY, + + /** + * This indicates that the completion event passed in ::NvEncEncodePicture() + * API has not been registered with encoder driver using ::NvEncRegisterAsyncEvent(). + */ + NV_ENC_ERR_EVENT_NOT_REGISTERD, + + /** + * This indicates that an unknown internal error has occurred. + */ + NV_ENC_ERR_GENERIC, + + /** + * This indicates that the client is attempting to use a feature + * that is not available for the license type for the current system. + */ + NV_ENC_ERR_INCOMPATIBLE_CLIENT_KEY, + + /** + * This indicates that the client is attempting to use a feature + * that is not implemented for the current version. + */ + NV_ENC_ERR_UNIMPLEMENTED, + + /** + * This indicates that the ::NvEncRegisterResource API failed to register the resource. + */ + NV_ENC_ERR_RESOURCE_REGISTER_FAILED, + + /** + * This indicates that the client is attempting to unregister a resource + * that has not been successfully registered. + */ + NV_ENC_ERR_RESOURCE_NOT_REGISTERED, + + /** + * This indicates that the client is attempting to unmap a resource + * that has not been successfully mapped. + */ + NV_ENC_ERR_RESOURCE_NOT_MAPPED, + +} NVENCSTATUS; + +/** + * Encode Picture encode flags. + */ +typedef enum _NV_ENC_PIC_FLAGS +{ + NV_ENC_PIC_FLAG_FORCEINTRA = 0x1, /**< Encode the current picture as an Intra picture */ + NV_ENC_PIC_FLAG_FORCEIDR = 0x2, /**< Encode the current picture as an IDR picture. + This flag is only valid when Picture type decision is taken by the Encoder + [_NV_ENC_INITIALIZE_PARAMS::enablePTD == 1]. */ + NV_ENC_PIC_FLAG_OUTPUT_SPSPPS = 0x4, /**< Write the sequence and picture header in encoded bitstream of the current picture */ + NV_ENC_PIC_FLAG_EOS = 0x8, /**< Indicates end of the input stream */ +} NV_ENC_PIC_FLAGS; + +/** + * Memory heap to allocate input and output buffers. + */ +typedef enum _NV_ENC_MEMORY_HEAP +{ + NV_ENC_MEMORY_HEAP_AUTOSELECT = 0, /**< Memory heap to be decided by the encoder driver based on the usage */ + NV_ENC_MEMORY_HEAP_VID = 1, /**< Memory heap is in local video memory */ + NV_ENC_MEMORY_HEAP_SYSMEM_CACHED = 2, /**< Memory heap is in cached system memory */ + NV_ENC_MEMORY_HEAP_SYSMEM_UNCACHED = 3 /**< Memory heap is in uncached system memory */ +} NV_ENC_MEMORY_HEAP; + +/** + * B-frame used as reference modes + */ +typedef enum _NV_ENC_BFRAME_REF_MODE +{ + NV_ENC_BFRAME_REF_MODE_DISABLED = 0x0, /**< B frame is not used for reference */ + NV_ENC_BFRAME_REF_MODE_EACH = 0x1, /**< Each B-frame will be used for reference */ + NV_ENC_BFRAME_REF_MODE_MIDDLE = 0x2, /**< Only(Number of B-frame)/2 th B-frame will be used for reference */ +} NV_ENC_BFRAME_REF_MODE; + +/** + * H.264 entropy coding modes. + */ +typedef enum _NV_ENC_H264_ENTROPY_CODING_MODE +{ + NV_ENC_H264_ENTROPY_CODING_MODE_AUTOSELECT = 0x0, /**< Entropy coding mode is auto selected by the encoder driver */ + NV_ENC_H264_ENTROPY_CODING_MODE_CABAC = 0x1, /**< Entropy coding mode is CABAC */ + NV_ENC_H264_ENTROPY_CODING_MODE_CAVLC = 0x2 /**< Entropy coding mode is CAVLC */ +} NV_ENC_H264_ENTROPY_CODING_MODE; + +/** + * H.264 specific BDirect modes + */ +typedef enum _NV_ENC_H264_BDIRECT_MODE +{ + NV_ENC_H264_BDIRECT_MODE_AUTOSELECT = 0x0, /**< BDirect mode is auto selected by the encoder driver */ + NV_ENC_H264_BDIRECT_MODE_DISABLE = 0x1, /**< Disable BDirect mode */ + NV_ENC_H264_BDIRECT_MODE_TEMPORAL = 0x2, /**< Temporal BDirect mode */ + NV_ENC_H264_BDIRECT_MODE_SPATIAL = 0x3 /**< Spatial BDirect mode */ +} NV_ENC_H264_BDIRECT_MODE; + +/** + * H.264 specific FMO usage + */ +typedef enum _NV_ENC_H264_FMO_MODE +{ + NV_ENC_H264_FMO_AUTOSELECT = 0x0, /**< FMO usage is auto selected by the encoder driver */ + NV_ENC_H264_FMO_ENABLE = 0x1, /**< Enable FMO */ + NV_ENC_H264_FMO_DISABLE = 0x2, /**< Disable FMO */ +} NV_ENC_H264_FMO_MODE; + +/** + * H.264 specific Adaptive Transform modes + */ +typedef enum _NV_ENC_H264_ADAPTIVE_TRANSFORM_MODE +{ + NV_ENC_H264_ADAPTIVE_TRANSFORM_AUTOSELECT = 0x0, /**< Adaptive Transform 8x8 mode is auto selected by the encoder driver*/ + NV_ENC_H264_ADAPTIVE_TRANSFORM_DISABLE = 0x1, /**< Adaptive Transform 8x8 mode disabled */ + NV_ENC_H264_ADAPTIVE_TRANSFORM_ENABLE = 0x2, /**< Adaptive Transform 8x8 mode should be used */ +} NV_ENC_H264_ADAPTIVE_TRANSFORM_MODE; + +/** + * Stereo frame packing modes. + */ +typedef enum _NV_ENC_STEREO_PACKING_MODE +{ + NV_ENC_STEREO_PACKING_MODE_NONE = 0x0, /**< No Stereo packing required */ + NV_ENC_STEREO_PACKING_MODE_CHECKERBOARD = 0x1, /**< Checkerboard mode for packing stereo frames */ + NV_ENC_STEREO_PACKING_MODE_COLINTERLEAVE = 0x2, /**< Column Interleave mode for packing stereo frames */ + NV_ENC_STEREO_PACKING_MODE_ROWINTERLEAVE = 0x3, /**< Row Interleave mode for packing stereo frames */ + NV_ENC_STEREO_PACKING_MODE_SIDEBYSIDE = 0x4, /**< Side-by-side mode for packing stereo frames */ + NV_ENC_STEREO_PACKING_MODE_TOPBOTTOM = 0x5, /**< Top-Bottom mode for packing stereo frames */ + NV_ENC_STEREO_PACKING_MODE_FRAMESEQ = 0x6 /**< Frame Sequential mode for packing stereo frames */ +} NV_ENC_STEREO_PACKING_MODE; + +/** + * Input Resource type + */ +typedef enum _NV_ENC_INPUT_RESOURCE_TYPE +{ + NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX = 0x0, /**< input resource type is a directx9 surface*/ + NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR = 0x1, /**< input resource type is a cuda device pointer surface*/ + NV_ENC_INPUT_RESOURCE_TYPE_CUDAARRAY = 0x2, /**< input resource type is a cuda array surface. + This array must be a 2D array and the CUDA_ARRAY3D_SURFACE_LDST + flag must have been specified when creating it. */ + NV_ENC_INPUT_RESOURCE_TYPE_OPENGL_TEX = 0x3 /**< input resource type is an OpenGL texture */ +} NV_ENC_INPUT_RESOURCE_TYPE; + +/** + * Buffer usage + */ +typedef enum _NV_ENC_BUFFER_USAGE +{ + NV_ENC_INPUT_IMAGE = 0x0, /**< Registered surface will be used for input image */ + NV_ENC_OUTPUT_MOTION_VECTOR = 0x1, /**< Registered surface will be used for output of H.264 ME only mode. + This buffer usage type is not supported for HEVC ME only mode. */ + NV_ENC_OUTPUT_BITSTREAM = 0x2, /**< Registered surface will be used for output bitstream in encoding */ +} NV_ENC_BUFFER_USAGE; + +/** + * Encoder Device type + */ +typedef enum _NV_ENC_DEVICE_TYPE +{ + NV_ENC_DEVICE_TYPE_DIRECTX = 0x0, /**< encode device type is a directx9 device */ + NV_ENC_DEVICE_TYPE_CUDA = 0x1, /**< encode device type is a cuda device */ + NV_ENC_DEVICE_TYPE_OPENGL = 0x2 /**< encode device type is an OpenGL device. + Use of this device type is supported only on Linux */ +} NV_ENC_DEVICE_TYPE; + +/** + * Number of reference frames + */ +typedef enum _NV_ENC_NUM_REF_FRAMES +{ + NV_ENC_NUM_REF_FRAMES_AUTOSELECT = 0x0, /**< Number of reference frames is auto selected by the encoder driver */ + NV_ENC_NUM_REF_FRAMES_1 = 0x1, /**< Number of reference frames equal to 1 */ + NV_ENC_NUM_REF_FRAMES_2 = 0x2, /**< Number of reference frames equal to 2 */ + NV_ENC_NUM_REF_FRAMES_3 = 0x3, /**< Number of reference frames equal to 3 */ + NV_ENC_NUM_REF_FRAMES_4 = 0x4, /**< Number of reference frames equal to 4 */ + NV_ENC_NUM_REF_FRAMES_5 = 0x5, /**< Number of reference frames equal to 5 */ + NV_ENC_NUM_REF_FRAMES_6 = 0x6, /**< Number of reference frames equal to 6 */ + NV_ENC_NUM_REF_FRAMES_7 = 0x7 /**< Number of reference frames equal to 7 */ +} NV_ENC_NUM_REF_FRAMES; + +/** + * Encoder capabilities enumeration. + */ +typedef enum _NV_ENC_CAPS +{ + /** + * Maximum number of B-Frames supported. + */ + NV_ENC_CAPS_NUM_MAX_BFRAMES, + + /** + * Rate control modes supported. + * \n The API return value is a bitmask of the values in NV_ENC_PARAMS_RC_MODE. + */ + NV_ENC_CAPS_SUPPORTED_RATECONTROL_MODES, + + /** + * Indicates HW support for field mode encoding. + * \n 0 : Interlaced mode encoding is not supported. + * \n 1 : Interlaced field mode encoding is supported. + * \n 2 : Interlaced frame encoding and field mode encoding are both supported. + */ + NV_ENC_CAPS_SUPPORT_FIELD_ENCODING, + + /** + * Indicates HW support for monochrome mode encoding. + * \n 0 : Monochrome mode not supported. + * \n 1 : Monochrome mode supported. + */ + NV_ENC_CAPS_SUPPORT_MONOCHROME, + + /** + * Indicates HW support for FMO. + * \n 0 : FMO not supported. + * \n 1 : FMO supported. + */ + NV_ENC_CAPS_SUPPORT_FMO, + + /** + * Indicates HW capability for Quarter pel motion estimation. + * \n 0 : Quarter-Pel Motion Estimation not supported. + * \n 1 : Quarter-Pel Motion Estimation supported. + */ + NV_ENC_CAPS_SUPPORT_QPELMV, + + /** + * H.264 specific. Indicates HW support for BDirect modes. + * \n 0 : BDirect mode encoding not supported. + * \n 1 : BDirect mode encoding supported. + */ + NV_ENC_CAPS_SUPPORT_BDIRECT_MODE, + + /** + * H264 specific. Indicates HW support for CABAC entropy coding mode. + * \n 0 : CABAC entropy coding not supported. + * \n 1 : CABAC entropy coding supported. + */ + NV_ENC_CAPS_SUPPORT_CABAC, + + /** + * Indicates HW support for Adaptive Transform. + * \n 0 : Adaptive Transform not supported. + * \n 1 : Adaptive Transform supported. + */ + NV_ENC_CAPS_SUPPORT_ADAPTIVE_TRANSFORM, + + /** + * Indicates HW support for Multi View Coding. + * \n 0 : Multi View Coding not supported. + * \n 1 : Multi View Coding supported. + */ + NV_ENC_CAPS_SUPPORT_STEREO_MVC, + + /** + * Indicates HW support for encoding Temporal layers. + * \n 0 : Encoding Temporal layers not supported. + * \n 1 : Encoding Temporal layers supported. + */ + NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS, + + /** + * Indicates HW support for Hierarchical P frames. + * \n 0 : Hierarchical P frames not supported. + * \n 1 : Hierarchical P frames supported. + */ + NV_ENC_CAPS_SUPPORT_HIERARCHICAL_PFRAMES, + + /** + * Indicates HW support for Hierarchical B frames. + * \n 0 : Hierarchical B frames not supported. + * \n 1 : Hierarchical B frames supported. + */ + NV_ENC_CAPS_SUPPORT_HIERARCHICAL_BFRAMES, + + /** + * Maximum Encoding level supported (See ::NV_ENC_LEVEL for details). + */ + NV_ENC_CAPS_LEVEL_MAX, + + /** + * Minimum Encoding level supported (See ::NV_ENC_LEVEL for details). + */ + NV_ENC_CAPS_LEVEL_MIN, + + /** + * Indicates HW support for separate colour plane encoding. + * \n 0 : Separate colour plane encoding not supported. + * \n 1 : Separate colour plane encoding supported. + */ + NV_ENC_CAPS_SEPARATE_COLOUR_PLANE, + + /** + * Maximum output width supported. + */ + NV_ENC_CAPS_WIDTH_MAX, + + /** + * Maximum output height supported. + */ + NV_ENC_CAPS_HEIGHT_MAX, + + /** + * Indicates Temporal Scalability Support. + * \n 0 : Temporal SVC encoding not supported. + * \n 1 : Temporal SVC encoding supported. + */ + NV_ENC_CAPS_SUPPORT_TEMPORAL_SVC, + + /** + * Indicates Dynamic Encode Resolution Change Support. + * Support added from NvEncodeAPI version 2.0. + * \n 0 : Dynamic Encode Resolution Change not supported. + * \n 1 : Dynamic Encode Resolution Change supported. + */ + NV_ENC_CAPS_SUPPORT_DYN_RES_CHANGE, + + /** + * Indicates Dynamic Encode Bitrate Change Support. + * Support added from NvEncodeAPI version 2.0. + * \n 0 : Dynamic Encode bitrate change not supported. + * \n 1 : Dynamic Encode bitrate change supported. + */ + NV_ENC_CAPS_SUPPORT_DYN_BITRATE_CHANGE, + + /** + * Indicates Forcing Constant QP On The Fly Support. + * Support added from NvEncodeAPI version 2.0. + * \n 0 : Forcing constant QP on the fly not supported. + * \n 1 : Forcing constant QP on the fly supported. + */ + NV_ENC_CAPS_SUPPORT_DYN_FORCE_CONSTQP, + + /** + * Indicates Dynamic rate control mode Change Support. + * \n 0 : Dynamic rate control mode change not supported. + * \n 1 : Dynamic rate control mode change supported. + */ + NV_ENC_CAPS_SUPPORT_DYN_RCMODE_CHANGE, + + /** + * Indicates Subframe readback support for slice-based encoding. If this feature is supported, it can be enabled by setting enableSubFrameWrite = 1. + * \n 0 : Subframe readback not supported. + * \n 1 : Subframe readback supported. + */ + NV_ENC_CAPS_SUPPORT_SUBFRAME_READBACK, + + /** + * Indicates Constrained Encoding mode support. + * Support added from NvEncodeAPI version 2.0. + * \n 0 : Constrained encoding mode not supported. + * \n 1 : Constrained encoding mode supported. + * If this mode is supported client can enable this during initialization. + * Client can then force a picture to be coded as constrained picture where + * in-loop filtering is disabled across slice boundaries and prediction vectors for inter + * macroblocks in each slice will be restricted to the slice region. + */ + NV_ENC_CAPS_SUPPORT_CONSTRAINED_ENCODING, + + /** + * Indicates Intra Refresh Mode Support. + * Support added from NvEncodeAPI version 2.0. + * \n 0 : Intra Refresh Mode not supported. + * \n 1 : Intra Refresh Mode supported. + */ + NV_ENC_CAPS_SUPPORT_INTRA_REFRESH, + + /** + * Indicates Custom VBV Buffer Size support. It can be used for capping frame size. + * Support added from NvEncodeAPI version 2.0. + * \n 0 : Custom VBV buffer size specification from client, not supported. + * \n 1 : Custom VBV buffer size specification from client, supported. + */ + NV_ENC_CAPS_SUPPORT_CUSTOM_VBV_BUF_SIZE, + + /** + * Indicates Dynamic Slice Mode Support. + * Support added from NvEncodeAPI version 2.0. + * \n 0 : Dynamic Slice Mode not supported. + * \n 1 : Dynamic Slice Mode supported. + */ + NV_ENC_CAPS_SUPPORT_DYNAMIC_SLICE_MODE, + + /** + * Indicates Reference Picture Invalidation Support. + * Support added from NvEncodeAPI version 2.0. + * \n 0 : Reference Picture Invalidation not supported. + * \n 1 : Reference Picture Invalidation supported. + */ + NV_ENC_CAPS_SUPPORT_REF_PIC_INVALIDATION, + + /** + * Indicates support for Pre-Processing. + * The API return value is a bitmask of the values defined in ::NV_ENC_PREPROC_FLAGS + */ + NV_ENC_CAPS_PREPROC_SUPPORT, + + /** + * Indicates support Async mode. + * \n 0 : Async Encode mode not supported. + * \n 1 : Async Encode mode supported. + */ + NV_ENC_CAPS_ASYNC_ENCODE_SUPPORT, + + /** + * Maximum MBs per frame supported. + */ + NV_ENC_CAPS_MB_NUM_MAX, + + /** + * Maximum aggregate throughput in MBs per sec. + */ + NV_ENC_CAPS_MB_PER_SEC_MAX, + + /** + * Indicates HW support for YUV444 mode encoding. + * \n 0 : YUV444 mode encoding not supported. + * \n 1 : YUV444 mode encoding supported. + */ + NV_ENC_CAPS_SUPPORT_YUV444_ENCODE, + + /** + * Indicates HW support for lossless encoding. + * \n 0 : lossless encoding not supported. + * \n 1 : lossless encoding supported. + */ + NV_ENC_CAPS_SUPPORT_LOSSLESS_ENCODE, + + /** + * Indicates HW support for Sample Adaptive Offset. + * \n 0 : SAO not supported. + * \n 1 : SAO encoding supported. + */ + NV_ENC_CAPS_SUPPORT_SAO, + + /** + * Indicates HW support for Motion Estimation Only Mode. + * \n 0 : MEOnly Mode not supported. + * \n 1 : MEOnly Mode supported for I and P frames. + * \n 2 : MEOnly Mode supported for I, P and B frames. + */ + NV_ENC_CAPS_SUPPORT_MEONLY_MODE, + + /** + * Indicates HW support for lookahead encoding (enableLookahead=1). + * \n 0 : Lookahead not supported. + * \n 1 : Lookahead supported. + */ + NV_ENC_CAPS_SUPPORT_LOOKAHEAD, + + /** + * Indicates HW support for temporal AQ encoding (enableTemporalAQ=1). + * \n 0 : Temporal AQ not supported. + * \n 1 : Temporal AQ supported. + */ + NV_ENC_CAPS_SUPPORT_TEMPORAL_AQ, + /** + * Indicates HW support for 10 bit encoding. + * \n 0 : 10 bit encoding not supported. + * \n 1 : 10 bit encoding supported. + */ + NV_ENC_CAPS_SUPPORT_10BIT_ENCODE, + /** + * Maximum number of Long Term Reference frames supported + */ + NV_ENC_CAPS_NUM_MAX_LTR_FRAMES, + + /** + * Indicates HW support for Weighted Prediction. + * \n 0 : Weighted Prediction not supported. + * \n 1 : Weighted Prediction supported. + */ + NV_ENC_CAPS_SUPPORT_WEIGHTED_PREDICTION, + + + /** + * On managed (vGPU) platforms (Windows only), this API, in conjunction with other GRID Management APIs, can be used + * to estimate the residual capacity of the hardware encoder on the GPU as a percentage of the total available encoder capacity. + * This API can be called at any time; i.e. during the encode session or before opening the encode session. + * If the available encoder capacity is returned as zero, applications may choose to switch to software encoding + * and continue to call this API (e.g. polling once per second) until capacity becomes available. + * + * On bare metal (non-virtualized GPU) and linux platforms, this API always returns 100. + */ + NV_ENC_CAPS_DYNAMIC_QUERY_ENCODER_CAPACITY, + + /** + * Indicates B as reference support. + * \n 0 : B as reference is not supported. + * \n 1 : each B-Frame as reference is supported. + * \n 2 : only Middle B-frame as reference is supported. + */ + NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE, + + /** + * Indicates HW support for Emphasis Level Map based delta QP computation. + * \n 0 : Emphasis Level Map based delta QP not supported. + * \n 1 : Emphasis Level Map based delta QP is supported. + */ + NV_ENC_CAPS_SUPPORT_EMPHASIS_LEVEL_MAP, + + /** + * Minimum input width supported. + */ + NV_ENC_CAPS_WIDTH_MIN, + + /** + * Minimum input height supported. + */ + NV_ENC_CAPS_HEIGHT_MIN, + + /** + * Indicates HW support for multiple reference frames. + */ + NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES, + + /** + * Indicates HW support for HEVC with alpha encoding. + * \n 0 : HEVC with alpha encoding not supported. + * \n 1 : HEVC with alpha encoding is supported. + */ + NV_ENC_CAPS_SUPPORT_ALPHA_LAYER_ENCODING, + + /** + * Indicates number of Encoding engines present on GPU. + */ + NV_ENC_CAPS_NUM_ENCODER_ENGINES, + + /** + * Indicates single slice intra refresh support. + */ + NV_ENC_CAPS_SINGLE_SLICE_INTRA_REFRESH, + + /** + * Reserved - Not to be used by clients. + */ + NV_ENC_CAPS_EXPOSED_COUNT + +} NV_ENC_CAPS; + +/** + * HEVC CU SIZE + */ +typedef enum _NV_ENC_HEVC_CUSIZE +{ + NV_ENC_HEVC_CUSIZE_AUTOSELECT = 0, + NV_ENC_HEVC_CUSIZE_8x8 = 1, + NV_ENC_HEVC_CUSIZE_16x16 = 2, + NV_ENC_HEVC_CUSIZE_32x32 = 3, + NV_ENC_HEVC_CUSIZE_64x64 = 4, +}NV_ENC_HEVC_CUSIZE; + +/** +* AV1 PART SIZE +*/ +typedef enum _NV_ENC_AV1_PART_SIZE +{ + NV_ENC_AV1_PART_SIZE_AUTOSELECT = 0, + NV_ENC_AV1_PART_SIZE_4x4 = 1, + NV_ENC_AV1_PART_SIZE_8x8 = 2, + NV_ENC_AV1_PART_SIZE_16x16 = 3, + NV_ENC_AV1_PART_SIZE_32x32 = 4, + NV_ENC_AV1_PART_SIZE_64x64 = 5, +}NV_ENC_AV1_PART_SIZE; + +/** +* Enums related to fields in VUI parameters. +*/ +typedef enum _NV_ENC_VUI_VIDEO_FORMAT +{ + NV_ENC_VUI_VIDEO_FORMAT_COMPONENT = 0, + NV_ENC_VUI_VIDEO_FORMAT_PAL = 1, + NV_ENC_VUI_VIDEO_FORMAT_NTSC = 2, + NV_ENC_VUI_VIDEO_FORMAT_SECAM = 3, + NV_ENC_VUI_VIDEO_FORMAT_MAC = 4, + NV_ENC_VUI_VIDEO_FORMAT_UNSPECIFIED = 5, +}NV_ENC_VUI_VIDEO_FORMAT; + +typedef enum _NV_ENC_VUI_COLOR_PRIMARIES +{ + NV_ENC_VUI_COLOR_PRIMARIES_UNDEFINED = 0, + NV_ENC_VUI_COLOR_PRIMARIES_BT709 = 1, + NV_ENC_VUI_COLOR_PRIMARIES_UNSPECIFIED = 2, + NV_ENC_VUI_COLOR_PRIMARIES_RESERVED = 3, + NV_ENC_VUI_COLOR_PRIMARIES_BT470M = 4, + NV_ENC_VUI_COLOR_PRIMARIES_BT470BG = 5, + NV_ENC_VUI_COLOR_PRIMARIES_SMPTE170M = 6, + NV_ENC_VUI_COLOR_PRIMARIES_SMPTE240M = 7, + NV_ENC_VUI_COLOR_PRIMARIES_FILM = 8, + NV_ENC_VUI_COLOR_PRIMARIES_BT2020 = 9, + NV_ENC_VUI_COLOR_PRIMARIES_SMPTE428 = 10, + NV_ENC_VUI_COLOR_PRIMARIES_SMPTE431 = 11, + NV_ENC_VUI_COLOR_PRIMARIES_SMPTE432 = 12, + NV_ENC_VUI_COLOR_PRIMARIES_JEDEC_P22 = 22, +}NV_ENC_VUI_COLOR_PRIMARIES; + +typedef enum _NV_ENC_VUI_TRANSFER_CHARACTERISTIC +{ + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_UNDEFINED = 0, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT709 = 1, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_UNSPECIFIED = 2, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_RESERVED = 3, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT470M = 4, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT470BG = 5, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE170M = 6, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE240M = 7, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_LINEAR = 8, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_LOG = 9, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_LOG_SQRT = 10, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_IEC61966_2_4 = 11, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT1361_ECG = 12, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SRGB = 13, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT2020_10 = 14, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_BT2020_12 = 15, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE2084 = 16, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_SMPTE428 = 17, + NV_ENC_VUI_TRANSFER_CHARACTERISTIC_ARIB_STD_B67 = 18, +}NV_ENC_VUI_TRANSFER_CHARACTERISTIC; + +typedef enum _NV_ENC_VUI_MATRIX_COEFFS +{ + NV_ENC_VUI_MATRIX_COEFFS_RGB = 0, + NV_ENC_VUI_MATRIX_COEFFS_BT709 = 1, + NV_ENC_VUI_MATRIX_COEFFS_UNSPECIFIED = 2, + NV_ENC_VUI_MATRIX_COEFFS_RESERVED = 3, + NV_ENC_VUI_MATRIX_COEFFS_FCC = 4, + NV_ENC_VUI_MATRIX_COEFFS_BT470BG = 5, + NV_ENC_VUI_MATRIX_COEFFS_SMPTE170M = 6, + NV_ENC_VUI_MATRIX_COEFFS_SMPTE240M = 7, + NV_ENC_VUI_MATRIX_COEFFS_YCGCO = 8, + NV_ENC_VUI_MATRIX_COEFFS_BT2020_NCL = 9, + NV_ENC_VUI_MATRIX_COEFFS_BT2020_CL = 10, + NV_ENC_VUI_MATRIX_COEFFS_SMPTE2085 = 11, +}NV_ENC_VUI_MATRIX_COEFFS; + +/** + * Input struct for querying Encoding capabilities. + */ +typedef struct _NV_ENC_CAPS_PARAM +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_CAPS_PARAM_VER */ + NV_ENC_CAPS capsToQuery; /**< [in]: Specifies the encode capability to be queried. Client should pass a member for ::NV_ENC_CAPS enum. */ + uint32_t reserved[62]; /**< [in]: Reserved and must be set to 0 */ +} NV_ENC_CAPS_PARAM; + +/** NV_ENC_CAPS_PARAM struct version. */ +#define NV_ENC_CAPS_PARAM_VER NVENCAPI_STRUCT_VERSION(1) + + +/** + * Encoder Output parameters + */ +typedef struct _NV_ENC_ENCODE_OUT_PARAMS +{ + uint32_t version; /**< [out]: Struct version. */ + uint32_t bitstreamSizeInBytes; /**< [out]: Encoded bitstream size in bytes */ + uint32_t reserved[62]; /**< [out]: Reserved and must be set to 0 */ +} NV_ENC_ENCODE_OUT_PARAMS; + +/** NV_ENC_ENCODE_OUT_PARAMS struct version. */ +#define NV_ENC_ENCODE_OUT_PARAMS_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * Creation parameters for input buffer. + */ +typedef struct _NV_ENC_CREATE_INPUT_BUFFER +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_CREATE_INPUT_BUFFER_VER */ + uint32_t width; /**< [in]: Input frame width */ + uint32_t height; /**< [in]: Input frame height */ + NV_ENC_MEMORY_HEAP memoryHeap; /**< [in]: Deprecated. Do not use */ + NV_ENC_BUFFER_FORMAT bufferFmt; /**< [in]: Input buffer format */ + uint32_t reserved; /**< [in]: Reserved and must be set to 0 */ + NV_ENC_INPUT_PTR inputBuffer; /**< [out]: Pointer to input buffer */ + void* pSysMemBuffer; /**< [in]: Pointer to existing system memory buffer */ + uint32_t reserved1[57]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[63]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_CREATE_INPUT_BUFFER; + +/** NV_ENC_CREATE_INPUT_BUFFER struct version. */ +#define NV_ENC_CREATE_INPUT_BUFFER_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * Creation parameters for output bitstream buffer. + */ +typedef struct _NV_ENC_CREATE_BITSTREAM_BUFFER +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_CREATE_BITSTREAM_BUFFER_VER */ + uint32_t size; /**< [in]: Deprecated. Do not use */ + NV_ENC_MEMORY_HEAP memoryHeap; /**< [in]: Deprecated. Do not use */ + uint32_t reserved; /**< [in]: Reserved and must be set to 0 */ + NV_ENC_OUTPUT_PTR bitstreamBuffer; /**< [out]: Pointer to the output bitstream buffer */ + void* bitstreamBufferPtr; /**< [out]: Reserved and should not be used */ + uint32_t reserved1[58]; /**< [in]: Reserved and should be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and should be set to NULL */ +} NV_ENC_CREATE_BITSTREAM_BUFFER; + +/** NV_ENC_CREATE_BITSTREAM_BUFFER struct version. */ +#define NV_ENC_CREATE_BITSTREAM_BUFFER_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * Structs needed for ME only mode. + */ +typedef struct _NV_ENC_MVECTOR +{ + int16_t mvx; /**< the x component of MV in quarter-pel units */ + int16_t mvy; /**< the y component of MV in quarter-pel units */ +} NV_ENC_MVECTOR; + +/** + * Motion vector structure per macroblock for H264 motion estimation. + */ +typedef struct _NV_ENC_H264_MV_DATA +{ + NV_ENC_MVECTOR mv[4]; /**< up to 4 vectors for 8x8 partition */ + uint8_t mbType; /**< 0 (I), 1 (P), 2 (IPCM), 3 (B) */ + uint8_t partitionType; /**< Specifies the block partition type. 0:16x16, 1:8x8, 2:16x8, 3:8x16 */ + uint16_t reserved; /**< reserved padding for alignment */ + uint32_t mbCost; +} NV_ENC_H264_MV_DATA; + +/** + * Motion vector structure per CU for HEVC motion estimation. + */ +typedef struct _NV_ENC_HEVC_MV_DATA +{ + NV_ENC_MVECTOR mv[4]; /**< up to 4 vectors within a CU */ + uint8_t cuType; /**< 0 (I), 1(P) */ + uint8_t cuSize; /**< 0: 8x8, 1: 16x16, 2: 32x32, 3: 64x64 */ + uint8_t partitionMode; /**< The CU partition mode + 0 (2Nx2N), 1 (2NxN), 2(Nx2N), 3 (NxN), + 4 (2NxnU), 5 (2NxnD), 6(nLx2N), 7 (nRx2N) */ + uint8_t lastCUInCTB; /**< Marker to separate CUs in the current CTB from CUs in the next CTB */ +} NV_ENC_HEVC_MV_DATA; + +/** + * Creation parameters for output motion vector buffer for ME only mode. + */ +typedef struct _NV_ENC_CREATE_MV_BUFFER +{ + uint32_t version; /**< [in]: Struct version. Must be set to NV_ENC_CREATE_MV_BUFFER_VER */ + NV_ENC_OUTPUT_PTR mvBuffer; /**< [out]: Pointer to the output motion vector buffer */ + uint32_t reserved1[255]; /**< [in]: Reserved and should be set to 0 */ + void* reserved2[63]; /**< [in]: Reserved and should be set to NULL */ +} NV_ENC_CREATE_MV_BUFFER; + +/** NV_ENC_CREATE_MV_BUFFER struct version*/ +#define NV_ENC_CREATE_MV_BUFFER_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * QP value for frames + */ +typedef struct _NV_ENC_QP +{ + uint32_t qpInterP; /**< [in]: Specifies QP value for P-frame. Even though this field is uint32_t for legacy reasons, the client should treat this as a signed parameter(int32_t) for cases in which negative QP values are to be specified. */ + uint32_t qpInterB; /**< [in]: Specifies QP value for B-frame. Even though this field is uint32_t for legacy reasons, the client should treat this as a signed parameter(int32_t) for cases in which negative QP values are to be specified. */ + uint32_t qpIntra; /**< [in]: Specifies QP value for Intra Frame. Even though this field is uint32_t for legacy reasons, the client should treat this as a signed parameter(int32_t) for cases in which negative QP values are to be specified. */ +} NV_ENC_QP; + +/** + * Rate Control Configuration Parameters + */ + typedef struct _NV_ENC_RC_PARAMS + { + uint32_t version; + NV_ENC_PARAMS_RC_MODE rateControlMode; /**< [in]: Specifies the rate control mode. Check support for various rate control modes using ::NV_ENC_CAPS_SUPPORTED_RATECONTROL_MODES caps. */ + NV_ENC_QP constQP; /**< [in]: Specifies the initial QP to be used for encoding, these values would be used for all frames if in Constant QP mode. */ + uint32_t averageBitRate; /**< [in]: Specifies the average bitrate(in bits/sec) used for encoding. */ + uint32_t maxBitRate; /**< [in]: Specifies the maximum bitrate for the encoded output. This is used for VBR and ignored for CBR mode. */ + uint32_t vbvBufferSize; /**< [in]: Specifies the VBV(HRD) buffer size. in bits. Set 0 to use the default VBV buffer size. */ + uint32_t vbvInitialDelay; /**< [in]: Specifies the VBV(HRD) initial delay in bits. Set 0 to use the default VBV initial delay .*/ + uint32_t enableMinQP :1; /**< [in]: Set this to 1 if minimum QP used for rate control. */ + uint32_t enableMaxQP :1; /**< [in]: Set this to 1 if maximum QP used for rate control. */ + uint32_t enableInitialRCQP :1; /**< [in]: Set this to 1 if user supplied initial QP is used for rate control. */ + uint32_t enableAQ :1; /**< [in]: Set this to 1 to enable adaptive quantization (Spatial). */ + uint32_t reservedBitField1 :1; /**< [in]: Reserved bitfields and must be set to 0. */ + uint32_t enableLookahead :1; /**< [in]: Set this to 1 to enable lookahead with depth (if lookahead is enabled, input frames must remain available to the encoder until encode completion) */ + uint32_t disableIadapt :1; /**< [in]: Set this to 1 to disable adaptive I-frame insertion at scene cuts (only has an effect when lookahead is enabled) */ + uint32_t disableBadapt :1; /**< [in]: Set this to 1 to disable adaptive B-frame decision (only has an effect when lookahead is enabled) */ + uint32_t enableTemporalAQ :1; /**< [in]: Set this to 1 to enable temporal AQ */ + uint32_t zeroReorderDelay :1; /**< [in]: Set this to 1 to indicate zero latency operation (no reordering delay, num_reorder_frames=0) */ + uint32_t enableNonRefP :1; /**< [in]: Set this to 1 to enable automatic insertion of non-reference P-frames (no effect if enablePTD=0) */ + uint32_t strictGOPTarget :1; /**< [in]: Set this to 1 to minimize GOP-to-GOP rate fluctuations */ + uint32_t aqStrength :4; /**< [in]: When AQ (Spatial) is enabled (i.e. NV_ENC_RC_PARAMS::enableAQ is set), this field is used to specify AQ strength. AQ strength scale is from 1 (low) - 15 (aggressive). + If not set, strength is auto selected by driver. */ + uint32_t reservedBitFields :16; /**< [in]: Reserved bitfields and must be set to 0 */ + NV_ENC_QP minQP; /**< [in]: Specifies the minimum QP used for rate control. Client must set NV_ENC_CONFIG::enableMinQP to 1. */ + NV_ENC_QP maxQP; /**< [in]: Specifies the maximum QP used for rate control. Client must set NV_ENC_CONFIG::enableMaxQP to 1. */ + NV_ENC_QP initialRCQP; /**< [in]: Specifies the initial QP used for rate control. Client must set NV_ENC_CONFIG::enableInitialRCQP to 1. */ + uint32_t temporallayerIdxMask; /**< [in]: Specifies the temporal layers (as a bitmask) whose QPs have changed. Valid max bitmask is [2^NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS - 1]. + Applicable only for constant QP mode (NV_ENC_RC_PARAMS::rateControlMode = NV_ENC_PARAMS_RC_CONSTQP). */ + uint8_t temporalLayerQP[8]; /**< [in]: Specifies the temporal layer QPs used for rate control. Temporal layer index is used as the array index. + Applicable only for constant QP mode (NV_ENC_RC_PARAMS::rateControlMode = NV_ENC_PARAMS_RC_CONSTQP). */ + uint8_t targetQuality; /**< [in]: Target CQ (Constant Quality) level for VBR mode (range 0-51 with 0-automatic) */ + uint8_t targetQualityLSB; /**< [in]: Fractional part of target quality (as 8.8 fixed point format) */ + uint16_t lookaheadDepth; /**< [in]: Maximum depth of lookahead with range 0-(31 - number of B frames). + lookaheadDepth is only used if enableLookahead=1.*/ + uint8_t lowDelayKeyFrameScale; /**< [in]: Specifies the ratio of I frame bits to P frame bits in case of single frame VBV and CBR rate control mode, + is set to 2 by default for low latency tuning info and 1 by default for ultra low latency tuning info */ + int8_t yDcQPIndexOffset; /**< [in]: Specifies the value of 'deltaQ_y_dc' in AV1.*/ + int8_t uDcQPIndexOffset; /**< [in]: Specifies the value of 'deltaQ_u_dc' in AV1.*/ + int8_t vDcQPIndexOffset; /**< [in]: Specifies the value of 'deltaQ_v_dc' in AV1 (for future use only - deltaQ_v_dc is currently always internally set to same value as deltaQ_u_dc). */ + NV_ENC_QP_MAP_MODE qpMapMode; /**< [in]: This flag is used to interpret values in array specified by NV_ENC_PIC_PARAMS::qpDeltaMap. + Set this to NV_ENC_QP_MAP_EMPHASIS to treat values specified by NV_ENC_PIC_PARAMS::qpDeltaMap as Emphasis Level Map. + Emphasis Level can be assigned any value specified in enum NV_ENC_EMPHASIS_MAP_LEVEL. + Emphasis Level Map is used to specify regions to be encoded at varying levels of quality. + The hardware encoder adjusts the quantization within the image as per the provided emphasis map, + by adjusting the quantization parameter (QP) assigned to each macroblock. This adjustment is commonly called "Delta QP". + The adjustment depends on the absolute QP decided by the rate control algorithm, and is applied after the rate control has decided each macroblock's QP. + Since the Delta QP overrides rate control, enabling Emphasis Level Map may violate bitrate and VBV buffer size constraints. + Emphasis Level Map is useful in situations where client has a priori knowledge of the image complexity (e.g. via use of NVFBC's Classification feature) and encoding those high-complexity areas at higher quality (lower QP) is important, even at the possible cost of violating bitrate/VBV buffer size constraints + This feature is not supported when AQ( Spatial/Temporal) is enabled. + This feature is only supported for H264 codec currently. + + Set this to NV_ENC_QP_MAP_DELTA to treat values specified by NV_ENC_PIC_PARAMS::qpDeltaMap as QP Delta. This specifies QP modifier to be applied on top of the QP chosen by rate control + + Set this to NV_ENC_QP_MAP_DISABLED to ignore NV_ENC_PIC_PARAMS::qpDeltaMap values. In this case, qpDeltaMap should be set to NULL. + + Other values are reserved for future use.*/ + NV_ENC_MULTI_PASS multiPass; /**< [in]: This flag is used to enable multi-pass encoding for a given ::NV_ENC_PARAMS_RC_MODE. This flag is not valid for H264 and HEVC MEOnly mode */ + uint32_t alphaLayerBitrateRatio; /**< [in]: Specifies the ratio in which bitrate should be split between base and alpha layer. A value 'x' for this field will split the target bitrate in a ratio of x : 1 between base and alpha layer. + The default split ratio is 15.*/ + int8_t cbQPIndexOffset; /**< [in]: Specifies the value of 'chroma_qp_index_offset' in H264 / 'pps_cb_qp_offset' in HEVC / 'deltaQ_u_ac' in AV1.*/ + int8_t crQPIndexOffset; /**< [in]: Specifies the value of 'second_chroma_qp_index_offset' in H264 / 'pps_cr_qp_offset' in HEVC / 'deltaQ_v_ac' in AV1 (for future use only - deltaQ_v_ac is currently always internally set to same value as deltaQ_u_ac). */ + uint16_t reserved2; + uint32_t reserved[4]; + } NV_ENC_RC_PARAMS; + +/** macro for constructing the version field of ::_NV_ENC_RC_PARAMS */ +#define NV_ENC_RC_PARAMS_VER NVENCAPI_STRUCT_VERSION(1) + +#define MAX_NUM_CLOCK_TS 3 + +/** +* Clock Timestamp set parameters +* For H264, this structure is used to populate Picture Timing SEI when NV_ENC_CONFIG_H264::enableTimeCode is set to 1. +* For HEVC, this structure is used to populate Time Code SEI when NV_ENC_CONFIG_HEVC::enableTimeCodeSEI is set to 1. +* For more details, refer to Annex D of ITU-T Specification. +*/ + +typedef struct _NV_ENC_CLOCK_TIMESTAMP_SET +{ + uint32_t countingType : 1; /**< [in] Specifies the 'counting_type' */ + uint32_t discontinuityFlag : 1; /**< [in] Specifies the 'discontinuity_flag' */ + uint32_t cntDroppedFrames : 1; /**< [in] Specifies the 'cnt_dropped_flag' */ + uint32_t nFrames : 8; /**< [in] Specifies the value of 'n_frames' */ + uint32_t secondsValue : 6; /**< [in] Specifies the 'seconds_value' */ + uint32_t minutesValue : 6; /**< [in] Specifies the 'minutes_value' */ + uint32_t hoursValue : 5; /**< [in] Specifies the 'hours_value' */ + uint32_t reserved2 : 4; /**< [in] Reserved and must be set to 0 */ + uint32_t timeOffset; /**< [in] Specifies the 'time_offset_value' */ +} NV_ENC_CLOCK_TIMESTAMP_SET; + +typedef struct _NV_ENC_TIME_CODE +{ + NV_ENC_DISPLAY_PIC_STRUCT displayPicStruct; /**< [in] Display picStruct */ + NV_ENC_CLOCK_TIMESTAMP_SET clockTimestamp[MAX_NUM_CLOCK_TS]; /**< [in] Clock Timestamp set */ +} NV_ENC_TIME_CODE; + + +/** + * \struct _NV_ENC_CONFIG_H264_VUI_PARAMETERS + * H264 Video Usability Info parameters + */ +typedef struct _NV_ENC_CONFIG_H264_VUI_PARAMETERS +{ + uint32_t overscanInfoPresentFlag; /**< [in]: If set to 1 , it specifies that the overscanInfo is present */ + uint32_t overscanInfo; /**< [in]: Specifies the overscan info(as defined in Annex E of the ITU-T Specification). */ + uint32_t videoSignalTypePresentFlag; /**< [in]: If set to 1, it specifies that the videoFormat, videoFullRangeFlag and colourDescriptionPresentFlag are present. */ + NV_ENC_VUI_VIDEO_FORMAT videoFormat; /**< [in]: Specifies the source video format(as defined in Annex E of the ITU-T Specification).*/ + uint32_t videoFullRangeFlag; /**< [in]: Specifies the output range of the luma and chroma samples(as defined in Annex E of the ITU-T Specification). */ + uint32_t colourDescriptionPresentFlag; /**< [in]: If set to 1, it specifies that the colourPrimaries, transferCharacteristics and colourMatrix are present. */ + NV_ENC_VUI_COLOR_PRIMARIES colourPrimaries; /**< [in]: Specifies color primaries for converting to RGB(as defined in Annex E of the ITU-T Specification) */ + NV_ENC_VUI_TRANSFER_CHARACTERISTIC transferCharacteristics; /**< [in]: Specifies the opto-electronic transfer characteristics to use (as defined in Annex E of the ITU-T Specification) */ + NV_ENC_VUI_MATRIX_COEFFS colourMatrix; /**< [in]: Specifies the matrix coefficients used in deriving the luma and chroma from the RGB primaries (as defined in Annex E of the ITU-T Specification). */ + uint32_t chromaSampleLocationFlag; /**< [in]: If set to 1 , it specifies that the chromaSampleLocationTop and chromaSampleLocationBot are present.*/ + uint32_t chromaSampleLocationTop; /**< [in]: Specifies the chroma sample location for top field(as defined in Annex E of the ITU-T Specification) */ + uint32_t chromaSampleLocationBot; /**< [in]: Specifies the chroma sample location for bottom field(as defined in Annex E of the ITU-T Specification) */ + uint32_t bitstreamRestrictionFlag; /**< [in]: If set to 1, it specifies the bitstream restriction parameters are present in the bitstream.*/ + uint32_t timingInfoPresentFlag; /**< [in]: If set to 1, it specifies that the timingInfo is present and the 'numUnitInTicks' and 'timeScale' fields are specified by the application. */ + /**< [in]: If not set, the timingInfo may still be present with timing related fields calculated internally basedon the frame rate specified by the application. */ + uint32_t numUnitInTicks; /**< [in]: Specifies the number of time units of the clock(as defined in Annex E of the ITU-T Specification). */ + uint32_t timeScale; /**< [in]: Specifies the frquency of the clock(as defined in Annex E of the ITU-T Specification). */ + uint32_t reserved[12]; /**< [in]: Reserved and must be set to 0 */ +}NV_ENC_CONFIG_H264_VUI_PARAMETERS; + +typedef NV_ENC_CONFIG_H264_VUI_PARAMETERS NV_ENC_CONFIG_HEVC_VUI_PARAMETERS; + +/** + * \struct _NVENC_EXTERNAL_ME_HINT_COUNTS_PER_BLOCKTYPE + * External motion vector hint counts per block type. + * H264 and AV1 support multiple hint while HEVC supports one hint for each valid candidate. + */ +typedef struct _NVENC_EXTERNAL_ME_HINT_COUNTS_PER_BLOCKTYPE +{ + uint32_t numCandsPerBlk16x16 : 4; /**< [in]: Supported for H264, HEVC. It Specifies the number of candidates per 16x16 block. */ + uint32_t numCandsPerBlk16x8 : 4; /**< [in]: Supported for H264 only. Specifies the number of candidates per 16x8 block. */ + uint32_t numCandsPerBlk8x16 : 4; /**< [in]: Supported for H264 only. Specifies the number of candidates per 8x16 block. */ + uint32_t numCandsPerBlk8x8 : 4; /**< [in]: Supported for H264, HEVC. Specifies the number of candidates per 8x8 block. */ + uint32_t numCandsPerSb : 8; /**< [in]: Supported for AV1 only. Specifies the number of candidates per SB. */ + uint32_t reserved : 8; /**< [in]: Reserved for padding. */ + uint32_t reserved1[3]; /**< [in]: Reserved for future use. */ +} NVENC_EXTERNAL_ME_HINT_COUNTS_PER_BLOCKTYPE; + + +/** + * \struct _NVENC_EXTERNAL_ME_HINT + * External Motion Vector hint structure for H264 and HEVC. + */ +typedef struct _NVENC_EXTERNAL_ME_HINT +{ + int32_t mvx : 12; /**< [in]: Specifies the x component of integer pixel MV (relative to current MB) S12.0. */ + int32_t mvy : 10; /**< [in]: Specifies the y component of integer pixel MV (relative to current MB) S10.0 .*/ + int32_t refidx : 5; /**< [in]: Specifies the reference index (31=invalid). Current we support only 1 reference frame per direction for external hints, so \p refidx must be 0. */ + int32_t dir : 1; /**< [in]: Specifies the direction of motion estimation . 0=L0 1=L1.*/ + int32_t partType : 2; /**< [in]: Specifies the block partition type.0=16x16 1=16x8 2=8x16 3=8x8 (blocks in partition must be consecutive).*/ + int32_t lastofPart : 1; /**< [in]: Set to 1 for the last MV of (sub) partition */ + int32_t lastOfMB : 1; /**< [in]: Set to 1 for the last MV of macroblock. */ +} NVENC_EXTERNAL_ME_HINT; + +/** + * \struct _NVENC_EXTERNAL_ME_SB_HINT + * External Motion Vector SB hint structure for AV1 + */ +typedef struct _NVENC_EXTERNAL_ME_SB_HINT +{ + int16_t refidx : 5; /**< [in]: Specifies the reference index (31=invalid) */ + int16_t direction : 1; /**< [in]: Specifies the direction of motion estimation . 0=L0 1=L1.*/ + int16_t bi : 1; /**< [in]: Specifies reference mode 0=single mv, 1=compound mv */ + int16_t partition_type : 3; /**< [in]: Specifies the partition type: 0: 2NX2N, 1:2NxN, 2:Nx2N. reserved 3bits for future modes */ + int16_t x8 : 3; /**< [in]: Specifies the current partition's top left x position in 8 pixel unit */ + int16_t last_of_cu : 1; /**< [in]: Set to 1 for the last MV current CU */ + int16_t last_of_sb : 1; /**< [in]: Set to 1 for the last MV of current SB */ + int16_t reserved0 : 1; /**< [in]: Reserved and must be set to 0 */ + int16_t mvx : 14; /**< [in]: Specifies the x component of integer pixel MV (relative to current MB) S12.2. */ + int16_t cu_size : 2; /**< [in]: Specifies the CU size: 0: 8x8, 1: 16x16, 2:32x32, 3:64x64 */ + int16_t mvy : 12; /**< [in]: Specifies the y component of integer pixel MV (relative to current MB) S10.2 .*/ + int16_t y8 : 3; /**< [in]: Specifies the current partition's top left y position in 8 pixel unit */ + int16_t reserved1 : 1; /**< [in]: Reserved and must be set to 0 */ +} NVENC_EXTERNAL_ME_SB_HINT; + +/** + * \struct _NV_ENC_CONFIG_H264 + * H264 encoder configuration parameters + */ +typedef struct _NV_ENC_CONFIG_H264 +{ + uint32_t enableTemporalSVC :1; /**< [in]: Set to 1 to enable SVC temporal*/ + uint32_t enableStereoMVC :1; /**< [in]: Set to 1 to enable stereo MVC*/ + uint32_t hierarchicalPFrames :1; /**< [in]: Set to 1 to enable hierarchical P Frames */ + uint32_t hierarchicalBFrames :1; /**< [in]: Set to 1 to enable hierarchical B Frames */ + uint32_t outputBufferingPeriodSEI :1; /**< [in]: Set to 1 to write SEI buffering period syntax in the bitstream */ + uint32_t outputPictureTimingSEI :1; /**< [in]: Set to 1 to write SEI picture timing syntax in the bitstream. */ + uint32_t outputAUD :1; /**< [in]: Set to 1 to write access unit delimiter syntax in bitstream */ + uint32_t disableSPSPPS :1; /**< [in]: Set to 1 to disable writing of Sequence and Picture parameter info in bitstream */ + uint32_t outputFramePackingSEI :1; /**< [in]: Set to 1 to enable writing of frame packing arrangement SEI messages to bitstream */ + uint32_t outputRecoveryPointSEI :1; /**< [in]: Set to 1 to enable writing of recovery point SEI message */ + uint32_t enableIntraRefresh :1; /**< [in]: Set to 1 to enable gradual decoder refresh or intra refresh. If the GOP structure uses B frames this will be ignored */ + uint32_t enableConstrainedEncoding :1; /**< [in]: Set this to 1 to enable constrainedFrame encoding where each slice in the constrained picture is independent of other slices. + Constrained encoding works only with rectangular slices. + Check support for constrained encoding using ::NV_ENC_CAPS_SUPPORT_CONSTRAINED_ENCODING caps. */ + uint32_t repeatSPSPPS :1; /**< [in]: Set to 1 to enable writing of Sequence and Picture parameter for every IDR frame */ + uint32_t enableVFR :1; /**< [in]: Setting enableVFR=1 currently only sets the fixed_frame_rate_flag=0 in the VUI but otherwise + has no impact on the encoder behavior. For more details please refer to E.1 VUI syntax of H.264 standard. Note, however, that NVENC does not support VFR encoding and rate control. */ + uint32_t enableLTR :1; /**< [in]: Set to 1 to enable LTR (Long Term Reference) frame support. LTR can be used in two modes: "LTR Trust" mode and "LTR Per Picture" mode. + LTR Trust mode: In this mode, ltrNumFrames pictures after IDR are automatically marked as LTR. This mode is enabled by setting ltrTrustMode = 1. + Use of LTR Trust mode is strongly discouraged as this mode may be deprecated in future. + LTR Per Picture mode: In this mode, client can control whether the current picture should be marked as LTR. Enable this mode by setting + ltrTrustMode = 0 and ltrMarkFrame = 1 for the picture to be marked as LTR. This is the preferred mode + for using LTR. + Note that LTRs are not supported if encoding session is configured with B-frames */ + uint32_t qpPrimeYZeroTransformBypassFlag :1; /**< [in]: To enable lossless encode set this to 1, set QP to 0 and RC_mode to NV_ENC_PARAMS_RC_CONSTQP and profile to HIGH_444_PREDICTIVE_PROFILE. + Check support for lossless encoding using ::NV_ENC_CAPS_SUPPORT_LOSSLESS_ENCODE caps. */ + uint32_t useConstrainedIntraPred :1; /**< [in]: Set 1 to enable constrained intra prediction. */ + uint32_t enableFillerDataInsertion :1; /**< [in]: Set to 1 to enable insertion of filler data in the bitstream. + This flag will take effect only when one of the CBR rate + control modes (NV_ENC_PARAMS_RC_CBR, NV_ENC_PARAMS_RC_CBR_HQ, + NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ) is in use and both + NV_ENC_INITIALIZE_PARAMS::frameRateNum and + NV_ENC_INITIALIZE_PARAMS::frameRateDen are set to non-zero + values. Setting this field when + NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem is also set + is currently not supported and will make ::NvEncInitializeEncoder() + return an error. */ + uint32_t disableSVCPrefixNalu :1; /**< [in]: Set to 1 to disable writing of SVC Prefix NALU preceding each slice in bitstream. + Applicable only when temporal SVC is enabled (NV_ENC_CONFIG_H264::enableTemporalSVC = 1). */ + uint32_t enableScalabilityInfoSEI :1; /**< [in]: Set to 1 to enable writing of Scalability Information SEI message preceding each IDR picture in bitstream + Applicable only when temporal SVC is enabled (NV_ENC_CONFIG_H264::enableTemporalSVC = 1). */ + uint32_t singleSliceIntraRefresh :1; /**< [in]: Set to 1 to maintain single slice in frames during intra refresh. + Check support for single slice intra refresh using ::NV_ENC_CAPS_SINGLE_SLICE_INTRA_REFRESH caps. + This flag will be ignored if the value returned for ::NV_ENC_CAPS_SINGLE_SLICE_INTRA_REFRESH caps is false. */ + uint32_t enableTimeCode :1; /**< [in]: Set to 1 to enable writing of clock timestamp sets in picture timing SEI. Note that this flag will be ignored for D3D12 interface. */ + uint32_t reservedBitFields :10; /**< [in]: Reserved bitfields and must be set to 0 */ + uint32_t level; /**< [in]: Specifies the encoding level. Client is recommended to set this to NV_ENC_LEVEL_AUTOSELECT in order to enable the NvEncodeAPI interface to select the correct level. */ + uint32_t idrPeriod; /**< [in]: Specifies the IDR interval. If not set, this is made equal to gopLength in NV_ENC_CONFIG.Low latency application client can set IDR interval to NVENC_INFINITE_GOPLENGTH so that IDR frames are not inserted automatically. */ + uint32_t separateColourPlaneFlag; /**< [in]: Set to 1 to enable 4:4:4 separate colour planes */ + uint32_t disableDeblockingFilterIDC; /**< [in]: Specifies the deblocking filter mode. Permissible value range: [0,2]. This flag corresponds + to the flag disable_deblocking_filter_idc specified in section 7.4.3 of H.264 specification, + which specifies whether the operation of the deblocking filter shall be disabled across some + block edges of the slice and specifies for which edges the filtering is disabled. See section + 7.4.3 of H.264 specification for more details.*/ + uint32_t numTemporalLayers; /**< [in]: Specifies number of temporal layers to be used for hierarchical coding / temporal SVC. Valid value range is [1,::NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS] */ + uint32_t spsId; /**< [in]: Specifies the SPS id of the sequence header */ + uint32_t ppsId; /**< [in]: Specifies the PPS id of the picture header */ + NV_ENC_H264_ADAPTIVE_TRANSFORM_MODE adaptiveTransformMode; /**< [in]: Specifies the AdaptiveTransform Mode. Check support for AdaptiveTransform mode using ::NV_ENC_CAPS_SUPPORT_ADAPTIVE_TRANSFORM caps. */ + NV_ENC_H264_FMO_MODE fmoMode; /**< [in]: Specified the FMO Mode. Check support for FMO using ::NV_ENC_CAPS_SUPPORT_FMO caps. */ + NV_ENC_H264_BDIRECT_MODE bdirectMode; /**< [in]: Specifies the BDirect mode. Check support for BDirect mode using ::NV_ENC_CAPS_SUPPORT_BDIRECT_MODE caps.*/ + NV_ENC_H264_ENTROPY_CODING_MODE entropyCodingMode; /**< [in]: Specifies the entropy coding mode. Check support for CABAC mode using ::NV_ENC_CAPS_SUPPORT_CABAC caps. */ + NV_ENC_STEREO_PACKING_MODE stereoMode; /**< [in]: Specifies the stereo frame packing mode which is to be signaled in frame packing arrangement SEI */ + uint32_t intraRefreshPeriod; /**< [in]: Specifies the interval between successive intra refresh if enableIntrarefresh is set. Requires enableIntraRefresh to be set. + Will be disabled if NV_ENC_CONFIG::gopLength is not set to NVENC_INFINITE_GOPLENGTH. */ + uint32_t intraRefreshCnt; /**< [in]: Specifies the length of intra refresh in number of frames for periodic intra refresh. This value should be smaller than intraRefreshPeriod */ + uint32_t maxNumRefFrames; /**< [in]: Specifies the DPB size used for encoding. Setting it to 0 will let driver use the default DPB size. + The low latency application which wants to invalidate reference frame as an error resilience tool + is recommended to use a large DPB size so that the encoder can keep old reference frames which can be used if recent + frames are invalidated. */ + uint32_t sliceMode; /**< [in]: This parameter in conjunction with sliceModeData specifies the way in which the picture is divided into slices + sliceMode = 0 MB based slices, sliceMode = 1 Byte based slices, sliceMode = 2 MB row based slices, sliceMode = 3 numSlices in Picture. + When forceIntraRefreshWithFrameCnt is set it will have priority over sliceMode setting + When sliceMode == 0 and sliceModeData == 0 whole picture will be coded with one slice */ + uint32_t sliceModeData; /**< [in]: Specifies the parameter needed for sliceMode. For: + sliceMode = 0, sliceModeData specifies # of MBs in each slice (except last slice) + sliceMode = 1, sliceModeData specifies maximum # of bytes in each slice (except last slice) + sliceMode = 2, sliceModeData specifies # of MB rows in each slice (except last slice) + sliceMode = 3, sliceModeData specifies number of slices in the picture. Driver will divide picture into slices optimally */ + NV_ENC_CONFIG_H264_VUI_PARAMETERS h264VUIParameters; /**< [in]: Specifies the H264 video usability info parameters */ + uint32_t ltrNumFrames; /**< [in]: Specifies the number of LTR frames. This parameter has different meaning in two LTR modes. + In "LTR Trust" mode (ltrTrustMode = 1), encoder will mark the first ltrNumFrames base layer reference frames within each IDR interval as LTR. + In "LTR Per Picture" mode (ltrTrustMode = 0 and ltrMarkFrame = 1), ltrNumFrames specifies maximum number of LTR frames in DPB. */ + uint32_t ltrTrustMode; /**< [in]: Specifies the LTR operating mode. See comments near NV_ENC_CONFIG_H264::enableLTR for description of the two modes. + Set to 1 to use "LTR Trust" mode of LTR operation. Clients are discouraged to use "LTR Trust" mode as this mode may + be deprecated in future releases. + Set to 0 when using "LTR Per Picture" mode of LTR operation. */ + uint32_t chromaFormatIDC; /**< [in]: Specifies the chroma format. Should be set to 1 for yuv420 input, 3 for yuv444 input. + Check support for YUV444 encoding using ::NV_ENC_CAPS_SUPPORT_YUV444_ENCODE caps.*/ + uint32_t maxTemporalLayers; /**< [in]: Specifies the max temporal layer used for temporal SVC / hierarchical coding. + Defaut value of this field is NV_ENC_CAPS::NV_ENC_CAPS_NUM_MAX_TEMPORAL_LAYERS. Note that the value NV_ENC_CONFIG_H264::maxNumRefFrames should + be greater than or equal to (NV_ENC_CONFIG_H264::maxTemporalLayers - 2) * 2, for NV_ENC_CONFIG_H264::maxTemporalLayers >= 2.*/ + NV_ENC_BFRAME_REF_MODE useBFramesAsRef; /**< [in]: Specifies the B-Frame as reference mode. Check support for useBFramesAsRef mode using ::NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE caps.*/ + NV_ENC_NUM_REF_FRAMES numRefL0; /**< [in]: Specifies max number of reference frames in reference picture list L0, that can be used by hardware for prediction of a frame. + Check support for numRefL0 using ::NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES caps. */ + NV_ENC_NUM_REF_FRAMES numRefL1; /**< [in]: Specifies max number of reference frames in reference picture list L1, that can be used by hardware for prediction of a frame. + Check support for numRefL1 using ::NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES caps. */ + + uint32_t reserved1[267]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_CONFIG_H264; + +/** + * \struct _NV_ENC_CONFIG_HEVC + * HEVC encoder configuration parameters to be set during initialization. + */ +typedef struct _NV_ENC_CONFIG_HEVC +{ + uint32_t level; /**< [in]: Specifies the level of the encoded bitstream.*/ + uint32_t tier; /**< [in]: Specifies the level tier of the encoded bitstream.*/ + NV_ENC_HEVC_CUSIZE minCUSize; /**< [in]: Specifies the minimum size of luma coding unit.*/ + NV_ENC_HEVC_CUSIZE maxCUSize; /**< [in]: Specifies the maximum size of luma coding unit. Currently NVENC SDK only supports maxCUSize equal to NV_ENC_HEVC_CUSIZE_32x32.*/ + uint32_t useConstrainedIntraPred :1; /**< [in]: Set 1 to enable constrained intra prediction. */ + uint32_t disableDeblockAcrossSliceBoundary :1; /**< [in]: Set 1 to disable in loop filtering across slice boundary.*/ + uint32_t outputBufferingPeriodSEI :1; /**< [in]: Set 1 to write SEI buffering period syntax in the bitstream */ + uint32_t outputPictureTimingSEI :1; /**< [in]: Set 1 to write SEI picture timing syntax in the bitstream */ + uint32_t outputAUD :1; /**< [in]: Set 1 to write Access Unit Delimiter syntax. */ + uint32_t enableLTR :1; /**< [in]: Set to 1 to enable LTR (Long Term Reference) frame support. LTR can be used in two modes: "LTR Trust" mode and "LTR Per Picture" mode. + LTR Trust mode: In this mode, ltrNumFrames pictures after IDR are automatically marked as LTR. This mode is enabled by setting ltrTrustMode = 1. + Use of LTR Trust mode is strongly discouraged as this mode may be deprecated in future releases. + LTR Per Picture mode: In this mode, client can control whether the current picture should be marked as LTR. Enable this mode by setting + ltrTrustMode = 0 and ltrMarkFrame = 1 for the picture to be marked as LTR. This is the preferred mode + for using LTR. + Note that LTRs are not supported if encoding session is configured with B-frames */ + uint32_t disableSPSPPS :1; /**< [in]: Set 1 to disable VPS, SPS and PPS signaling in the bitstream. */ + uint32_t repeatSPSPPS :1; /**< [in]: Set 1 to output VPS,SPS and PPS for every IDR frame.*/ + uint32_t enableIntraRefresh :1; /**< [in]: Set 1 to enable gradual decoder refresh or intra refresh. If the GOP structure uses B frames this will be ignored */ + uint32_t chromaFormatIDC :2; /**< [in]: Specifies the chroma format. Should be set to 1 for yuv420 input, 3 for yuv444 input.*/ + uint32_t pixelBitDepthMinus8 :3; /**< [in]: Specifies pixel bit depth minus 8. Should be set to 0 for 8 bit input, 2 for 10 bit input.*/ + uint32_t enableFillerDataInsertion :1; /**< [in]: Set to 1 to enable insertion of filler data in the bitstream. + This flag will take effect only when one of the CBR rate + control modes (NV_ENC_PARAMS_RC_CBR, NV_ENC_PARAMS_RC_CBR_HQ, + NV_ENC_PARAMS_RC_CBR_LOWDELAY_HQ) is in use and both + NV_ENC_INITIALIZE_PARAMS::frameRateNum and + NV_ENC_INITIALIZE_PARAMS::frameRateDen are set to non-zero + values. Setting this field when + NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem is also set + is currently not supported and will make ::NvEncInitializeEncoder() + return an error. */ + uint32_t enableConstrainedEncoding :1; /**< [in]: Set this to 1 to enable constrainedFrame encoding where each slice in the constrained picture is independent of other slices. + Constrained encoding works only with rectangular slices. + Check support for constrained encoding using ::NV_ENC_CAPS_SUPPORT_CONSTRAINED_ENCODING caps. */ + uint32_t enableAlphaLayerEncoding :1; /**< [in]: Set this to 1 to enable HEVC encode with alpha layer. */ + uint32_t singleSliceIntraRefresh :1; /**< [in]: Set this to 1 to maintain single slice frames during intra refresh. + Check support for single slice intra refresh using ::NV_ENC_CAPS_SINGLE_SLICE_INTRA_REFRESH caps. + This flag will be ignored if the value returned for ::NV_ENC_CAPS_SINGLE_SLICE_INTRA_REFRESH caps is false. */ + uint32_t outputRecoveryPointSEI :1; /**< [in]: Set to 1 to enable writing of recovery point SEI message */ + uint32_t outputTimeCodeSEI :1; /**< [in]: Set 1 to write SEI time code syntax in the bitstream. Note that this flag will be ignored for D3D12 interface.*/ + uint32_t reserved :12; /**< [in]: Reserved bitfields.*/ + uint32_t idrPeriod; /**< [in]: Specifies the IDR interval. If not set, this is made equal to gopLength in NV_ENC_CONFIG. Low latency application client can set IDR interval to NVENC_INFINITE_GOPLENGTH so that IDR frames are not inserted automatically. */ + uint32_t intraRefreshPeriod; /**< [in]: Specifies the interval between successive intra refresh if enableIntrarefresh is set. Requires enableIntraRefresh to be set. + Will be disabled if NV_ENC_CONFIG::gopLength is not set to NVENC_INFINITE_GOPLENGTH. */ + uint32_t intraRefreshCnt; /**< [in]: Specifies the length of intra refresh in number of frames for periodic intra refresh. This value should be smaller than intraRefreshPeriod */ + uint32_t maxNumRefFramesInDPB; /**< [in]: Specifies the maximum number of references frames in the DPB.*/ + uint32_t ltrNumFrames; /**< [in]: This parameter has different meaning in two LTR modes. + In "LTR Trust" mode (ltrTrustMode = 1), encoder will mark the first ltrNumFrames base layer reference frames within each IDR interval as LTR. + In "LTR Per Picture" mode (ltrTrustMode = 0 and ltrMarkFrame = 1), ltrNumFrames specifies maximum number of LTR frames in DPB. + These ltrNumFrames acts as a guidance to the encoder and are not necessarily honored. To achieve a right balance between the encoding + quality and keeping LTR frames in the DPB queue, the encoder can internally limit the number of LTR frames. + The number of LTR frames actually used depends upon the encoding preset being used; Faster encoding presets will use fewer LTR frames.*/ + uint32_t vpsId; /**< [in]: Specifies the VPS id of the video parameter set */ + uint32_t spsId; /**< [in]: Specifies the SPS id of the sequence header */ + uint32_t ppsId; /**< [in]: Specifies the PPS id of the picture header */ + uint32_t sliceMode; /**< [in]: This parameter in conjunction with sliceModeData specifies the way in which the picture is divided into slices + sliceMode = 0 CTU based slices, sliceMode = 1 Byte based slices, sliceMode = 2 CTU row based slices, sliceMode = 3, numSlices in Picture + When sliceMode == 0 and sliceModeData == 0 whole picture will be coded with one slice */ + uint32_t sliceModeData; /**< [in]: Specifies the parameter needed for sliceMode. For: + sliceMode = 0, sliceModeData specifies # of CTUs in each slice (except last slice) + sliceMode = 1, sliceModeData specifies maximum # of bytes in each slice (except last slice) + sliceMode = 2, sliceModeData specifies # of CTU rows in each slice (except last slice) + sliceMode = 3, sliceModeData specifies number of slices in the picture. Driver will divide picture into slices optimally */ + uint32_t maxTemporalLayersMinus1; /**< [in]: Specifies the max temporal layer used for hierarchical coding. */ + NV_ENC_CONFIG_HEVC_VUI_PARAMETERS hevcVUIParameters; /**< [in]: Specifies the HEVC video usability info parameters */ + uint32_t ltrTrustMode; /**< [in]: Specifies the LTR operating mode. See comments near NV_ENC_CONFIG_HEVC::enableLTR for description of the two modes. + Set to 1 to use "LTR Trust" mode of LTR operation. Clients are discouraged to use "LTR Trust" mode as this mode may + be deprecated in future releases. + Set to 0 when using "LTR Per Picture" mode of LTR operation. */ + NV_ENC_BFRAME_REF_MODE useBFramesAsRef; /**< [in]: Specifies the B-Frame as reference mode. Check support for useBFramesAsRef mode using ::NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE caps.*/ + NV_ENC_NUM_REF_FRAMES numRefL0; /**< [in]: Specifies max number of reference frames in reference picture list L0, that can be used by hardware for prediction of a frame. + Check support for numRefL0 using ::NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES caps. */ + NV_ENC_NUM_REF_FRAMES numRefL1; /**< [in]: Specifies max number of reference frames in reference picture list L1, that can be used by hardware for prediction of a frame. + Check support for numRefL1 using ::NV_ENC_CAPS_SUPPORT_MULTIPLE_REF_FRAMES caps. */ + uint32_t reserved1[214]; /**< [in]: Reserved and must be set to 0.*/ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_CONFIG_HEVC; + +#define NV_MAX_TILE_COLS_AV1 64 +#define NV_MAX_TILE_ROWS_AV1 64 + +/** + * \struct _NV_ENC_FILM_GRAIN_PARAMS_AV1 + * AV1 Film Grain Parameters structure + */ + +typedef struct _NV_ENC_FILM_GRAIN_PARAMS_AV1 +{ + uint32_t applyGrain :1; /**< [in]: Set to 1 to specify film grain should be added to frame */ + uint32_t chromaScalingFromLuma :1; /**< [in]: Set to 1 to specify the chroma scaling is inferred from luma scaling */ + uint32_t overlapFlag :1; /**< [in]: Set to 1 to indicate that overlap between film grain blocks should be applied*/ + uint32_t clipToRestrictedRange :1; /**< [in]: Set to 1 to clip values to restricted (studio) range after adding film grain */ + uint32_t grainScalingMinus8 :2; /**< [in]: Represents the shift - 8 applied to the values of the chroma component */ + uint32_t arCoeffLag :2; /**< [in]: Specifies the number of auto-regressive coefficients for luma and chroma */ + uint32_t numYPoints :4; /**< [in]: Specifies the number of points for the piecewise linear scaling function of the luma component */ + uint32_t numCbPoints :4; /**< [in]: Specifies the number of points for the piecewise linear scaling function of the cb component */ + uint32_t numCrPoints :4; /**< [in]: Specifies the number of points for the piecewise linear scaling function of the cr component */ + uint32_t arCoeffShiftMinus6 :2; /**< [in]: specifies the range of the auto-regressive coefficients */ + uint32_t grainScaleShift :2; /**< [in]: Specifies how much the Gaussian random numbers should be scaled down during the grain synthesi process */ + uint32_t reserved1 :8; /**< [in]: Reserved bits field - should be set to 0 */ + uint8_t pointYValue[14]; /**< [in]: pointYValue[i]: x coordinate for i-th point of luma piecewise linear scaling function. Values on a scale of 0...255 */ + uint8_t pointYScaling[14]; /**< [in]: pointYScaling[i]: i-th point output value of luma piecewise linear scaling function */ + uint8_t pointCbValue[10]; /**< [in]: pointCbValue[i]: x coordinate for i-th point of cb piecewise linear scaling function. Values on a scale of 0...255 */ + uint8_t pointCbScaling[10]; /**< [in]: pointCbScaling[i]: i-th point output value of cb piecewise linear scaling function */ + uint8_t pointCrValue[10]; /**< [in]: pointCrValue[i]: x coordinate for i-th point of cr piecewise linear scaling function. Values on a scale of 0...255 */ + uint8_t pointCrScaling[10]; /**< [in]: pointCrScaling[i]: i-th point output value of cr piecewise linear scaling function */ + uint8_t arCoeffsYPlus128[24]; /**< [in]: Specifies auto-regressive coefficients used for the Y plane */ + uint8_t arCoeffsCbPlus128[25]; /**< [in]: Specifies auto-regressive coefficients used for the U plane */ + uint8_t arCoeffsCrPlus128[25]; /**< [in]: Specifies auto-regressive coefficients used for the V plane */ + uint8_t reserved2[2]; /**< [in]: Reserved bytes - should be set to 0 */ + uint8_t cbMult; /**< [in]: Represents a multiplier for the cb component used in derivation of the input index to the cb component scaling function */ + uint8_t cbLumaMult; /**< [in]: represents a multiplier for the average luma component used in derivation of the input index to the cb component scaling function. */ + uint16_t cbOffset; /**< [in]: Represents an offset used in derivation of the input index to the cb component scaling function */ + uint8_t crMult; /**< [in]: Represents a multiplier for the cr component used in derivation of the input index to the cr component scaling function */ + uint8_t crLumaMult; /**< [in]: represents a multiplier for the average luma component used in derivation of the input index to the cr component scaling function. */ + uint16_t crOffset; /**< [in]: Represents an offset used in derivation of the input index to the cr component scaling function */ +} NV_ENC_FILM_GRAIN_PARAMS_AV1; + +/** +* \struct _NV_ENC_CONFIG_AV1 +* AV1 encoder configuration parameters to be set during initialization. +*/ +typedef struct _NV_ENC_CONFIG_AV1 +{ + uint32_t level; /**< [in]: Specifies the level of the encoded bitstream.*/ + uint32_t tier; /**< [in]: Specifies the level tier of the encoded bitstream.*/ + NV_ENC_AV1_PART_SIZE minPartSize; /**< [in]: Specifies the minimum size of luma coding block partition.*/ + NV_ENC_AV1_PART_SIZE maxPartSize; /**< [in]: Specifies the maximum size of luma coding block partition.*/ + uint32_t outputAnnexBFormat : 1; /**< [in]: Set 1 to use Annex B format for bitstream output.*/ + uint32_t enableTimingInfo : 1; /**< [in]: Set 1 to write Timing Info into sequence/frame headers */ + uint32_t enableDecoderModelInfo : 1; /**< [in]: Set 1 to write Decoder Model Info into sequence/frame headers */ + uint32_t enableFrameIdNumbers : 1; /**< [in]: Set 1 to write Frame id numbers in bitstream */ + uint32_t disableSeqHdr : 1; /**< [in]: Set 1 to disable Sequence Header signaling in the bitstream. */ + uint32_t repeatSeqHdr : 1; /**< [in]: Set 1 to output Sequence Header for every Key frame.*/ + uint32_t enableIntraRefresh : 1; /**< [in]: Set 1 to enable gradual decoder refresh or intra refresh. If the GOP structure uses B frames this will be ignored */ + uint32_t chromaFormatIDC : 2; /**< [in]: Specifies the chroma format. Should be set to 1 for yuv420 input (yuv444 input currently not supported).*/ + uint32_t enableBitstreamPadding : 1; /**< [in]: Set 1 to enable bitstream padding. */ + uint32_t enableCustomTileConfig : 1; /**< [in]: Set 1 to enable custom tile configuration: numTileColumns and numTileRows must have non zero values and tileWidths and tileHeights must point to a valid address */ + uint32_t enableFilmGrainParams : 1; /**< [in]: Set 1 to enable custom film grain parameters: filmGrainParams must point to a valid address */ + uint32_t inputPixelBitDepthMinus8 : 3; /**< [in]: Specifies pixel bit depth minus 8 of video input. Should be set to 0 for 8 bit input, 2 for 10 bit input.*/ + uint32_t pixelBitDepthMinus8 : 3; /**< [in]: Specifies pixel bit depth minus 8 of encoded video. Should be set to 0 for 8 bit, 2 for 10 bit. + HW will do the bitdepth conversion internally from inputPixelBitDepthMinus8 -> pixelBitDepthMinus8 if bit dpeths differ + Support for 8 bit input to 10 bit encode conversion only */ + uint32_t reserved : 14; /**< [in]: Reserved bitfields.*/ + uint32_t idrPeriod; /**< [in]: Specifies the IDR/Key frame interval. If not set, this is made equal to gopLength in NV_ENC_CONFIG.Low latency application client can set IDR interval to NVENC_INFINITE_GOPLENGTH so that IDR frames are not inserted automatically. */ + uint32_t intraRefreshPeriod; /**< [in]: Specifies the interval between successive intra refresh if enableIntrarefresh is set. Requires enableIntraRefresh to be set. + Will be disabled if NV_ENC_CONFIG::gopLength is not set to NVENC_INFINITE_GOPLENGTH. */ + uint32_t intraRefreshCnt; /**< [in]: Specifies the length of intra refresh in number of frames for periodic intra refresh. This value should be smaller than intraRefreshPeriod */ + uint32_t maxNumRefFramesInDPB; /**< [in]: Specifies the maximum number of references frames in the DPB.*/ + uint32_t numTileColumns; /**< [in]: This parameter in conjunction with the flag enableCustomTileConfig and the array tileWidths[] specifies the way in which the picture is divided into tile columns. + When enableCustomTileConfig == 0, the picture will be uniformly divided into numTileColumns tile columns. If numTileColumns is not a power of 2, + it will be rounded down to the next power of 2 value. If numTileColumns == 0, the picture will be coded with the smallest number of vertical tiles as allowed by standard. + When enableCustomTileConfig == 1, numTileColumns must be > 0 and <= NV_MAX_TILE_COLS_AV1 and tileWidths must point to a valid array of numTileColumns entries. + Entry i specifies the width in 64x64 CTU unit of tile colum i. The sum of all the entries should be equal to the picture width in 64x64 CTU units. */ + uint32_t numTileRows; /**< [in]: This parameter in conjunction with the flag enableCustomTileConfig and the array tileHeights[] specifies the way in which the picture is divided into tiles rows + When enableCustomTileConfig == 0, the picture will be uniformly divided into numTileRows tile rows. If numTileRows is not a power of 2, + it will be rounded down to the next power of 2 value. If numTileRows == 0, the picture will be coded with the smallest number of horizontal tiles as allowed by standard. + When enableCustomTileConfig == 1, numTileRows must be > 0 and <= NV_MAX_TILE_ROWS_AV1 and tileHeights must point to a valid array of numTileRows entries. + Entry i specifies the height in 64x64 CTU unit of tile row i. The sum of all the entries should be equal to the picture hieght in 64x64 CTU units. */ + uint32_t *tileWidths; /**< [in]: If enableCustomTileConfig == 1, tileWidths[i] specifies the width of tile column i in 64x64 CTU unit, with 0 <= i <= numTileColumns -1. */ + uint32_t *tileHeights; /**< [in]: If enableCustomTileConfig == 1, tileHeights[i] specifies the height of tile row i in 64x64 CTU unit, with 0 <= i <= numTileRows -1. */ + uint32_t maxTemporalLayersMinus1; /**< [in]: Specifies the max temporal layer used for hierarchical coding. */ + NV_ENC_VUI_COLOR_PRIMARIES colorPrimaries; /**< [in]: as defined in section of ISO/IEC 23091-4/ITU-T H.273 */ + NV_ENC_VUI_TRANSFER_CHARACTERISTIC transferCharacteristics; /**< [in]: as defined in section of ISO/IEC 23091-4/ITU-T H.273 */ + NV_ENC_VUI_MATRIX_COEFFS matrixCoefficients; /**< [in]: as defined in section of ISO/IEC 23091-4/ITU-T H.273 */ + uint32_t colorRange; /**< [in]: 0: studio swing representation - 1: full swing representation */ + uint32_t chromaSamplePosition; /**< [in]: 0: unknown + 1: Horizontally collocated with luma (0,0) sample, between two vertical samples + 2: Co-located with luma (0,0) sample */ + NV_ENC_BFRAME_REF_MODE useBFramesAsRef; /**< [in]: Specifies the B-Frame as reference mode. Check support for useBFramesAsRef mode using ::NV_ENC_CAPS_SUPPORT_BFRAME_REF_MODE caps.*/ + NV_ENC_FILM_GRAIN_PARAMS_AV1 *filmGrainParams; /**< [in]: If enableFilmGrainParams == 1, filmGrainParams must point to a valid NV_ENC_FILM_GRAIN_PARAMS_AV1 structure */ + NV_ENC_NUM_REF_FRAMES numFwdRefs; /**< [in]: Specifies max number of forward reference frame used for prediction of a frame. It must be in range 1-4 (Last, Last2, last3 and Golden). It's a suggestive value not necessarily be honored always. */ + NV_ENC_NUM_REF_FRAMES numBwdRefs; /**< [in]: Specifies max number of L1 list reference frame used for prediction of a frame. It must be in range 1-3 (Backward, Altref2, Altref). It's a suggestive value not necessarily be honored always. */ + uint32_t reserved1[235]; /**< [in]: Reserved and must be set to 0.*/ + void* reserved2[62]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_CONFIG_AV1; + +/** + * \struct _NV_ENC_CONFIG_H264_MEONLY + * H264 encoder configuration parameters for ME only Mode + * + */ +typedef struct _NV_ENC_CONFIG_H264_MEONLY +{ + uint32_t disablePartition16x16 :1; /**< [in]: Disable Motion Estimation on 16x16 blocks*/ + uint32_t disablePartition8x16 :1; /**< [in]: Disable Motion Estimation on 8x16 blocks*/ + uint32_t disablePartition16x8 :1; /**< [in]: Disable Motion Estimation on 16x8 blocks*/ + uint32_t disablePartition8x8 :1; /**< [in]: Disable Motion Estimation on 8x8 blocks*/ + uint32_t disableIntraSearch :1; /**< [in]: Disable Intra search during Motion Estimation*/ + uint32_t bStereoEnable :1; /**< [in]: Enable Stereo Mode for Motion Estimation where each view is independently executed*/ + uint32_t reserved :26; /**< [in]: Reserved and must be set to 0 */ + uint32_t reserved1 [255]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_CONFIG_H264_MEONLY; + + +/** + * \struct _NV_ENC_CONFIG_HEVC_MEONLY + * HEVC encoder configuration parameters for ME only Mode + * + */ +typedef struct _NV_ENC_CONFIG_HEVC_MEONLY +{ + uint32_t reserved [256]; /**< [in]: Reserved and must be set to 0 */ + void* reserved1[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_CONFIG_HEVC_MEONLY; + +/** + * \struct _NV_ENC_CODEC_CONFIG + * Codec-specific encoder configuration parameters to be set during initialization. + */ +typedef union _NV_ENC_CODEC_CONFIG +{ + NV_ENC_CONFIG_H264 h264Config; /**< [in]: Specifies the H.264-specific encoder configuration. */ + NV_ENC_CONFIG_HEVC hevcConfig; /**< [in]: Specifies the HEVC-specific encoder configuration. */ + NV_ENC_CONFIG_AV1 av1Config; /**< [in]: Specifies the AV1-specific encoder configuration. */ + NV_ENC_CONFIG_H264_MEONLY h264MeOnlyConfig; /**< [in]: Specifies the H.264-specific ME only encoder configuration. */ + NV_ENC_CONFIG_HEVC_MEONLY hevcMeOnlyConfig; /**< [in]: Specifies the HEVC-specific ME only encoder configuration. */ + uint32_t reserved[320]; /**< [in]: Reserved and must be set to 0 */ +} NV_ENC_CODEC_CONFIG; + + +/** + * \struct _NV_ENC_CONFIG + * Encoder configuration parameters to be set during initialization. + */ +typedef struct _NV_ENC_CONFIG +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_CONFIG_VER. */ + GUID profileGUID; /**< [in]: Specifies the codec profile GUID. If client specifies \p NV_ENC_CODEC_PROFILE_AUTOSELECT_GUID the NvEncodeAPI interface will select the appropriate codec profile. */ + uint32_t gopLength; /**< [in]: Specifies the number of pictures in one GOP. Low latency application client can set goplength to NVENC_INFINITE_GOPLENGTH so that keyframes are not inserted automatically. */ + int32_t frameIntervalP; /**< [in]: Specifies the GOP pattern as follows: \p frameIntervalP = 0: I, 1: IPP, 2: IBP, 3: IBBP If goplength is set to NVENC_INFINITE_GOPLENGTH \p frameIntervalP should be set to 1. */ + uint32_t monoChromeEncoding; /**< [in]: Set this to 1 to enable monochrome encoding for this session. */ + NV_ENC_PARAMS_FRAME_FIELD_MODE frameFieldMode; /**< [in]: Specifies the frame/field mode. + Check support for field encoding using ::NV_ENC_CAPS_SUPPORT_FIELD_ENCODING caps. + Using a frameFieldMode other than NV_ENC_PARAMS_FRAME_FIELD_MODE_FRAME for RGB input is not supported. */ + NV_ENC_MV_PRECISION mvPrecision; /**< [in]: Specifies the desired motion vector prediction precision. */ + NV_ENC_RC_PARAMS rcParams; /**< [in]: Specifies the rate control parameters for the current encoding session. */ + NV_ENC_CODEC_CONFIG encodeCodecConfig; /**< [in]: Specifies the codec specific config parameters through this union. */ + uint32_t reserved [278]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_CONFIG; + +/** macro for constructing the version field of ::_NV_ENC_CONFIG */ +#define NV_ENC_CONFIG_VER (NVENCAPI_STRUCT_VERSION(8) | ( 1<<31 )) + +/** + * Tuning information of NVENC encoding (TuningInfo is not applicable to H264 and HEVC MEOnly mode). + */ +typedef enum NV_ENC_TUNING_INFO +{ + NV_ENC_TUNING_INFO_UNDEFINED = 0, /**< Undefined tuningInfo. Invalid value for encoding. */ + NV_ENC_TUNING_INFO_HIGH_QUALITY = 1, /**< Tune presets for latency tolerant encoding.*/ + NV_ENC_TUNING_INFO_LOW_LATENCY = 2, /**< Tune presets for low latency streaming.*/ + NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY = 3, /**< Tune presets for ultra low latency streaming.*/ + NV_ENC_TUNING_INFO_LOSSLESS = 4, /**< Tune presets for lossless encoding.*/ + NV_ENC_TUNING_INFO_COUNT /**< Count number of tuningInfos. Invalid value. */ +}NV_ENC_TUNING_INFO; + +/** + * \struct _NV_ENC_INITIALIZE_PARAMS + * Encode Session Initialization parameters. + */ +typedef struct _NV_ENC_INITIALIZE_PARAMS +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_INITIALIZE_PARAMS_VER. */ + GUID encodeGUID; /**< [in]: Specifies the Encode GUID for which the encoder is being created. ::NvEncInitializeEncoder() API will fail if this is not set, or set to unsupported value. */ + GUID presetGUID; /**< [in]: Specifies the preset for encoding. If the preset GUID is set then , the preset configuration will be applied before any other parameter. */ + uint32_t encodeWidth; /**< [in]: Specifies the encode width. If not set ::NvEncInitializeEncoder() API will fail. */ + uint32_t encodeHeight; /**< [in]: Specifies the encode height. If not set ::NvEncInitializeEncoder() API will fail. */ + uint32_t darWidth; /**< [in]: Specifies the display aspect ratio width (H264/HEVC) or the render width (AV1). */ + uint32_t darHeight; /**< [in]: Specifies the display aspect ratio height (H264/HEVC) or the render height (AV1). */ + uint32_t frameRateNum; /**< [in]: Specifies the numerator for frame rate used for encoding in frames per second ( Frame rate = frameRateNum / frameRateDen ). */ + uint32_t frameRateDen; /**< [in]: Specifies the denominator for frame rate used for encoding in frames per second ( Frame rate = frameRateNum / frameRateDen ). */ + uint32_t enableEncodeAsync; /**< [in]: Set this to 1 to enable asynchronous mode and is expected to use events to get picture completion notification. */ + uint32_t enablePTD; /**< [in]: Set this to 1 to enable the Picture Type Decision is be taken by the NvEncodeAPI interface. */ + uint32_t reportSliceOffsets :1; /**< [in]: Set this to 1 to enable reporting slice offsets in ::_NV_ENC_LOCK_BITSTREAM. NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync must be set to 0 to use this feature. Client must set this to 0 if NV_ENC_CONFIG_H264::sliceMode is 1 on Kepler GPUs */ + uint32_t enableSubFrameWrite :1; /**< [in]: Set this to 1 to write out available bitstream to memory at subframe intervals. + If enableSubFrameWrite = 1, then the hardware encoder returns data as soon as a slice (H264/HEVC) or tile (AV1) has completed encoding. + This results in better encoding latency, but the downside is that the application has to keep polling via a call to nvEncLockBitstream API continuously to see if any encoded slice/tile data is available. + Use this mode if you feel that the marginal reduction in latency from sub-frame encoding is worth the increase in complexity due to CPU-based polling. */ + uint32_t enableExternalMEHints :1; /**< [in]: Set to 1 to enable external ME hints for the current frame. For NV_ENC_INITIALIZE_PARAMS::enablePTD=1 with B frames, programming L1 hints is optional for B frames since Client doesn't know internal GOP structure. + NV_ENC_PIC_PARAMS::meHintRefPicDist should preferably be set with enablePTD=1. */ + uint32_t enableMEOnlyMode :1; /**< [in]: Set to 1 to enable ME Only Mode .*/ + uint32_t enableWeightedPrediction :1; /**< [in]: Set this to 1 to enable weighted prediction. Not supported if encode session is configured for B-Frames (i.e. NV_ENC_CONFIG::frameIntervalP > 1 or preset >=P3 when tuningInfo = ::NV_ENC_TUNING_INFO_HIGH_QUALITY or + tuningInfo = ::NV_ENC_TUNING_INFO_LOSSLESS. This is because preset >=p3 internally enables B frames when tuningInfo = ::NV_ENC_TUNING_INFO_HIGH_QUALITY or ::NV_ENC_TUNING_INFO_LOSSLESS). */ + uint32_t enableOutputInVidmem :1; /**< [in]: Set this to 1 to enable output of NVENC in video memory buffer created by application. This feature is not supported for HEVC ME only mode. */ + uint32_t reservedBitFields :26; /**< [in]: Reserved bitfields and must be set to 0 */ + uint32_t privDataSize; /**< [in]: Reserved private data buffer size and must be set to 0 */ + void* privData; /**< [in]: Reserved private data buffer and must be set to NULL */ + NV_ENC_CONFIG* encodeConfig; /**< [in]: Specifies the advanced codec specific structure. If client has sent a valid codec config structure, it will override parameters set by the NV_ENC_INITIALIZE_PARAMS::presetGUID parameter. If set to NULL the NvEncodeAPI interface will use the NV_ENC_INITIALIZE_PARAMS::presetGUID to set the codec specific parameters. + Client can also optionally query the NvEncodeAPI interface to get codec specific parameters for a presetGUID using ::NvEncGetEncodePresetConfig() API. It can then modify (if required) some of the codec config parameters and send down a custom config structure as part of ::_NV_ENC_INITIALIZE_PARAMS. + Even in this case client is recommended to pass the same preset guid it has used in ::NvEncGetEncodePresetConfig() API to query the config structure; as NV_ENC_INITIALIZE_PARAMS::presetGUID. This will not override the custom config structure but will be used to determine other Encoder HW specific parameters not exposed in the API. */ + uint32_t maxEncodeWidth; /**< [in]: Maximum encode width to be used for current Encode session. + Client should allocate output buffers according to this dimension for dynamic resolution change. If set to 0, Encoder will not allow dynamic resolution change. */ + uint32_t maxEncodeHeight; /**< [in]: Maximum encode height to be allowed for current Encode session. + Client should allocate output buffers according to this dimension for dynamic resolution change. If set to 0, Encode will not allow dynamic resolution change. */ + NVENC_EXTERNAL_ME_HINT_COUNTS_PER_BLOCKTYPE maxMEHintCountsPerBlock[2]; /**< [in]: If Client wants to pass external motion vectors in NV_ENC_PIC_PARAMS::meExternalHints buffer it must specify the maximum number of hint candidates per block per direction for the encode session. + The NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock[0] is for L0 predictors and NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock[1] is for L1 predictors. + This client must also set NV_ENC_INITIALIZE_PARAMS::enableExternalMEHints to 1. */ + NV_ENC_TUNING_INFO tuningInfo; /**< [in]: Tuning Info of NVENC encoding(TuningInfo is not applicable to H264 and HEVC meonly mode). */ + NV_ENC_BUFFER_FORMAT bufferFormat; /**< [in]: Input buffer format. Used only when DX12 interface type is used */ + uint32_t reserved [287]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_INITIALIZE_PARAMS; + +/** macro for constructing the version field of ::_NV_ENC_INITIALIZE_PARAMS */ +#define NV_ENC_INITIALIZE_PARAMS_VER (NVENCAPI_STRUCT_VERSION(5) | ( 1<<31 )) + + +/** + * \struct _NV_ENC_RECONFIGURE_PARAMS + * Encode Session Reconfigured parameters. + */ +typedef struct _NV_ENC_RECONFIGURE_PARAMS +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_RECONFIGURE_PARAMS_VER. */ + NV_ENC_INITIALIZE_PARAMS reInitEncodeParams; /**< [in]: Encoder session re-initialization parameters. + If reInitEncodeParams.encodeConfig is NULL and + reInitEncodeParams.presetGUID is the same as the preset + GUID specified on the call to NvEncInitializeEncoder(), + EncodeAPI will continue to use the existing encode + configuration. + If reInitEncodeParams.encodeConfig is NULL and + reInitEncodeParams.presetGUID is different from the preset + GUID specified on the call to NvEncInitializeEncoder(), + EncodeAPI will try to use the default configuration for + the preset specified by reInitEncodeParams.presetGUID. + In this case, reconfiguration may fail if the new + configuration is incompatible with the existing + configuration (e.g. the new configuration results in + a change in the GOP structure). */ + uint32_t resetEncoder :1; /**< [in]: This resets the rate control states and other internal encoder states. This should be used only with an IDR frame. + If NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1, encoder will force the frame type to IDR */ + uint32_t forceIDR :1; /**< [in]: Encode the current picture as an IDR picture. This flag is only valid when Picture type decision is taken by the Encoder + [_NV_ENC_INITIALIZE_PARAMS::enablePTD == 1]. */ + uint32_t reserved :30; + +}NV_ENC_RECONFIGURE_PARAMS; + +/** macro for constructing the version field of ::_NV_ENC_RECONFIGURE_PARAMS */ +#define NV_ENC_RECONFIGURE_PARAMS_VER (NVENCAPI_STRUCT_VERSION(1) | ( 1<<31 )) + +/** + * \struct _NV_ENC_PRESET_CONFIG + * Encoder preset config + */ +typedef struct _NV_ENC_PRESET_CONFIG +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_PRESET_CONFIG_VER. */ + NV_ENC_CONFIG presetCfg; /**< [out]: preset config returned by the Nvidia Video Encoder interface. */ + uint32_t reserved1[255]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +}NV_ENC_PRESET_CONFIG; + +/** macro for constructing the version field of ::_NV_ENC_PRESET_CONFIG */ +#define NV_ENC_PRESET_CONFIG_VER (NVENCAPI_STRUCT_VERSION(4) | ( 1<<31 )) + + +/** + * \struct _NV_ENC_PIC_PARAMS_MVC + * MVC-specific parameters to be sent on a per-frame basis. + */ +typedef struct _NV_ENC_PIC_PARAMS_MVC +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_PIC_PARAMS_MVC_VER. */ + uint32_t viewID; /**< [in]: Specifies the view ID associated with the current input view. */ + uint32_t temporalID; /**< [in]: Specifies the temporal ID associated with the current input view. */ + uint32_t priorityID; /**< [in]: Specifies the priority ID associated with the current input view. Reserved and ignored by the NvEncodeAPI interface. */ + uint32_t reserved1[12]; /**< [in]: Reserved and must be set to 0. */ + void* reserved2[8]; /**< [in]: Reserved and must be set to NULL. */ +}NV_ENC_PIC_PARAMS_MVC; + +/** macro for constructing the version field of ::_NV_ENC_PIC_PARAMS_MVC */ +#define NV_ENC_PIC_PARAMS_MVC_VER NVENCAPI_STRUCT_VERSION(1) + + +/** + * \union _NV_ENC_PIC_PARAMS_H264_EXT + * H264 extension picture parameters + */ +typedef union _NV_ENC_PIC_PARAMS_H264_EXT +{ + NV_ENC_PIC_PARAMS_MVC mvcPicParams; /**< [in]: Specifies the MVC picture parameters. */ + uint32_t reserved1[32]; /**< [in]: Reserved and must be set to 0. */ +}NV_ENC_PIC_PARAMS_H264_EXT; + +/** + * \struct _NV_ENC_SEI_PAYLOAD + * User SEI message + */ +typedef struct _NV_ENC_SEI_PAYLOAD +{ + uint32_t payloadSize; /**< [in] SEI payload size in bytes. SEI payload must be byte aligned, as described in Annex D */ + uint32_t payloadType; /**< [in] SEI payload types and syntax can be found in Annex D of the H.264 Specification. */ + uint8_t *payload; /**< [in] pointer to user data */ +} NV_ENC_SEI_PAYLOAD; + +#define NV_ENC_H264_SEI_PAYLOAD NV_ENC_SEI_PAYLOAD + +/** + * \struct _NV_ENC_PIC_PARAMS_H264 + * H264 specific enc pic params. sent on a per frame basis. + */ +typedef struct _NV_ENC_PIC_PARAMS_H264 +{ + uint32_t displayPOCSyntax; /**< [in]: Specifies the display POC syntax This is required to be set if client is handling the picture type decision. */ + uint32_t reserved3; /**< [in]: Reserved and must be set to 0 */ + uint32_t refPicFlag; /**< [in]: Set to 1 for a reference picture. This is ignored if NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */ + uint32_t colourPlaneId; /**< [in]: Specifies the colour plane ID associated with the current input. */ + uint32_t forceIntraRefreshWithFrameCnt; /**< [in]: Forces an intra refresh with duration equal to intraRefreshFrameCnt. + When outputRecoveryPointSEI is set this is value is used for recovery_frame_cnt in recovery point SEI message + forceIntraRefreshWithFrameCnt cannot be used if B frames are used in the GOP structure specified */ + uint32_t constrainedFrame :1; /**< [in]: Set to 1 if client wants to encode this frame with each slice completely independent of other slices in the frame. + NV_ENC_INITIALIZE_PARAMS::enableConstrainedEncoding should be set to 1 */ + uint32_t sliceModeDataUpdate :1; /**< [in]: Set to 1 if client wants to change the sliceModeData field to specify new sliceSize Parameter + When forceIntraRefreshWithFrameCnt is set it will have priority over sliceMode setting */ + uint32_t ltrMarkFrame :1; /**< [in]: Set to 1 if client wants to mark this frame as LTR */ + uint32_t ltrUseFrames :1; /**< [in]: Set to 1 if client allows encoding this frame using the LTR frames specified in ltrFrameBitmap */ + uint32_t reservedBitFields :28; /**< [in]: Reserved bit fields and must be set to 0 */ + uint8_t* sliceTypeData; /**< [in]: Deprecated. */ + uint32_t sliceTypeArrayCnt; /**< [in]: Deprecated. */ + uint32_t seiPayloadArrayCnt; /**< [in]: Specifies the number of elements allocated in seiPayloadArray array. */ + NV_ENC_SEI_PAYLOAD* seiPayloadArray; /**< [in]: Array of SEI payloads which will be inserted for this frame. */ + uint32_t sliceMode; /**< [in]: This parameter in conjunction with sliceModeData specifies the way in which the picture is divided into slices + sliceMode = 0 MB based slices, sliceMode = 1 Byte based slices, sliceMode = 2 MB row based slices, sliceMode = 3, numSlices in Picture + When forceIntraRefreshWithFrameCnt is set it will have priority over sliceMode setting + When sliceMode == 0 and sliceModeData == 0 whole picture will be coded with one slice */ + uint32_t sliceModeData; /**< [in]: Specifies the parameter needed for sliceMode. For: + sliceMode = 0, sliceModeData specifies # of MBs in each slice (except last slice) + sliceMode = 1, sliceModeData specifies maximum # of bytes in each slice (except last slice) + sliceMode = 2, sliceModeData specifies # of MB rows in each slice (except last slice) + sliceMode = 3, sliceModeData specifies number of slices in the picture. Driver will divide picture into slices optimally */ + uint32_t ltrMarkFrameIdx; /**< [in]: Specifies the long term referenceframe index to use for marking this frame as LTR.*/ + uint32_t ltrUseFrameBitmap; /**< [in]: Specifies the associated bitmap of LTR frame indices to use when encoding this frame. */ + uint32_t ltrUsageMode; /**< [in]: Not supported. Reserved for future use and must be set to 0. */ + uint32_t forceIntraSliceCount; /**< [in]: Specifies the number of slices to be forced to Intra in the current picture. + This option along with forceIntraSliceIdx[] array needs to be used with sliceMode = 3 only */ + uint32_t *forceIntraSliceIdx; /**< [in]: Slice indices to be forced to intra in the current picture. Each slice index should be <= num_slices_in_picture -1. Index starts from 0 for first slice. + The number of entries in this array should be equal to forceIntraSliceCount */ + NV_ENC_PIC_PARAMS_H264_EXT h264ExtPicParams; /**< [in]: Specifies the H264 extension config parameters using this config. */ + NV_ENC_TIME_CODE timeCode; /**< [in]: Specifies the clock timestamp sets used in picture timing SEI. Applicable only when NV_ENC_CONFIG_H264::enableTimeCode is set to 1. */ + uint32_t reserved [203]; /**< [in]: Reserved and must be set to 0. */ + void* reserved2[61]; /**< [in]: Reserved and must be set to NULL. */ +} NV_ENC_PIC_PARAMS_H264; + +/** + * \struct _NV_ENC_PIC_PARAMS_HEVC + * HEVC specific enc pic params. sent on a per frame basis. + */ +typedef struct _NV_ENC_PIC_PARAMS_HEVC +{ + uint32_t displayPOCSyntax; /**< [in]: Specifies the display POC syntax This is required to be set if client is handling the picture type decision. */ + uint32_t refPicFlag; /**< [in]: Set to 1 for a reference picture. This is ignored if NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */ + uint32_t temporalId; /**< [in]: Specifies the temporal id of the picture */ + uint32_t forceIntraRefreshWithFrameCnt; /**< [in]: Forces an intra refresh with duration equal to intraRefreshFrameCnt. + When outputRecoveryPointSEI is set this is value is used for recovery_frame_cnt in recovery point SEI message + forceIntraRefreshWithFrameCnt cannot be used if B frames are used in the GOP structure specified */ + uint32_t constrainedFrame :1; /**< [in]: Set to 1 if client wants to encode this frame with each slice completely independent of other slices in the frame. + NV_ENC_INITIALIZE_PARAMS::enableConstrainedEncoding should be set to 1 */ + uint32_t sliceModeDataUpdate :1; /**< [in]: Set to 1 if client wants to change the sliceModeData field to specify new sliceSize Parameter + When forceIntraRefreshWithFrameCnt is set it will have priority over sliceMode setting */ + uint32_t ltrMarkFrame :1; /**< [in]: Set to 1 if client wants to mark this frame as LTR */ + uint32_t ltrUseFrames :1; /**< [in]: Set to 1 if client allows encoding this frame using the LTR frames specified in ltrFrameBitmap */ + uint32_t reservedBitFields :28; /**< [in]: Reserved bit fields and must be set to 0 */ + uint8_t* sliceTypeData; /**< [in]: Array which specifies the slice type used to force intra slice for a particular slice. Currently supported only for NV_ENC_CONFIG_H264::sliceMode == 3. + Client should allocate array of size sliceModeData where sliceModeData is specified in field of ::_NV_ENC_CONFIG_H264 + Array element with index n corresponds to nth slice. To force a particular slice to intra client should set corresponding array element to NV_ENC_SLICE_TYPE_I + all other array elements should be set to NV_ENC_SLICE_TYPE_DEFAULT */ + uint32_t sliceTypeArrayCnt; /**< [in]: Client should set this to the number of elements allocated in sliceTypeData array. If sliceTypeData is NULL then this should be set to 0 */ + uint32_t sliceMode; /**< [in]: This parameter in conjunction with sliceModeData specifies the way in which the picture is divided into slices + sliceMode = 0 CTU based slices, sliceMode = 1 Byte based slices, sliceMode = 2 CTU row based slices, sliceMode = 3, numSlices in Picture + When forceIntraRefreshWithFrameCnt is set it will have priority over sliceMode setting + When sliceMode == 0 and sliceModeData == 0 whole picture will be coded with one slice */ + uint32_t sliceModeData; /**< [in]: Specifies the parameter needed for sliceMode. For: + sliceMode = 0, sliceModeData specifies # of CTUs in each slice (except last slice) + sliceMode = 1, sliceModeData specifies maximum # of bytes in each slice (except last slice) + sliceMode = 2, sliceModeData specifies # of CTU rows in each slice (except last slice) + sliceMode = 3, sliceModeData specifies number of slices in the picture. Driver will divide picture into slices optimally */ + uint32_t ltrMarkFrameIdx; /**< [in]: Specifies the long term reference frame index to use for marking this frame as LTR.*/ + uint32_t ltrUseFrameBitmap; /**< [in]: Specifies the associated bitmap of LTR frame indices to use when encoding this frame. */ + uint32_t ltrUsageMode; /**< [in]: Not supported. Reserved for future use and must be set to 0. */ + uint32_t seiPayloadArrayCnt; /**< [in]: Specifies the number of elements allocated in seiPayloadArray array. */ + uint32_t reserved; /**< [in]: Reserved and must be set to 0. */ + NV_ENC_SEI_PAYLOAD* seiPayloadArray; /**< [in]: Array of SEI payloads which will be inserted for this frame. */ + NV_ENC_TIME_CODE timeCode; /**< [in]: Specifies the clock timestamp sets used in time code SEI. Applicable only when NV_ENC_CONFIG_HEVC::enableTimeCodeSEI is set to 1. */ + uint32_t reserved2 [237]; /**< [in]: Reserved and must be set to 0. */ + void* reserved3[61]; /**< [in]: Reserved and must be set to NULL. */ +} NV_ENC_PIC_PARAMS_HEVC; + +#define NV_ENC_AV1_OBU_PAYLOAD NV_ENC_SEI_PAYLOAD + +/** +* \struct _NV_ENC_PIC_PARAMS_AV1 +* AV1 specific enc pic params. sent on a per frame basis. +*/ +typedef struct _NV_ENC_PIC_PARAMS_AV1 +{ + uint32_t displayPOCSyntax; /**< [in]: Specifies the display POC syntax This is required to be set if client is handling the picture type decision. */ + uint32_t refPicFlag; /**< [in]: Set to 1 for a reference picture. This is ignored if NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */ + uint32_t temporalId; /**< [in]: Specifies the temporal id of the picture */ + uint32_t forceIntraRefreshWithFrameCnt; /**< [in]: Forces an intra refresh with duration equal to intraRefreshFrameCnt. + forceIntraRefreshWithFrameCnt cannot be used if B frames are used in the GOP structure specified */ + uint32_t goldenFrameFlag : 1; /**< [in]: Encode frame as Golden Frame. This is ignored if NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */ + uint32_t arfFrameFlag : 1; /**< [in]: Encode frame as Alternate Reference Frame. This is ignored if NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */ + uint32_t arf2FrameFlag : 1; /**< [in]: Encode frame as Alternate Reference 2 Frame. This is ignored if NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */ + uint32_t bwdFrameFlag : 1; /**< [in]: Encode frame as Backward Reference Frame. This is ignored if NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */ + uint32_t overlayFrameFlag : 1; /**< [in]: Encode frame as overlay frame. A previously encoded frame with the same displayPOCSyntax value should be present in reference frame buffer. + This is ignored if NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */ + uint32_t showExistingFrameFlag : 1; /**< [in]: When ovelayFrameFlag is set to 1, this flag controls the value of the show_existing_frame syntax element associated with the overlay frame. + This flag is added to the interface as a placeholder. Its value is ignored for now and always assumed to be set to 1. + This is ignored if NV_ENC_INITIALIZE_PARAMS::enablePTD is set to 1. */ + uint32_t errorResilientModeFlag : 1; /**< [in]: encode frame independently from previously encoded frames */ + + uint32_t tileConfigUpdate : 1; /**< [in]: Set to 1 if client wants to overwrite the default tile configuration with the tile parameters specified below + When forceIntraRefreshWithFrameCnt is set it will have priority over tileConfigUpdate setting */ + uint32_t enableCustomTileConfig : 1; /**< [in]: Set 1 to enable custom tile configuration: numTileColumns and numTileRows must have non zero values and tileWidths and tileHeights must point to a valid address */ + uint32_t filmGrainParamsUpdate : 1; /**< [in]: Set to 1 if client wants to update previous film grain parameters: filmGrainParams must point to a valid address and encoder must have been configured with film grain enabled */ + uint32_t reservedBitFields : 22; /**< [in]: Reserved bitfields and must be set to 0 */ + uint32_t numTileColumns; /**< [in]: This parameter in conjunction with the flag enableCustomTileConfig and the array tileWidths[] specifies the way in which the picture is divided into tile columns. + When enableCustomTileConfig == 0, the picture will be uniformly divided into numTileColumns tile columns. If numTileColumns is not a power of 2, + it will be rounded down to the next power of 2 value. If numTileColumns == 0, the picture will be coded with the smallest number of vertical tiles as allowed by standard. + When enableCustomTileConfig == 1, numTileColumns must be > 0 and <= NV_MAX_TILE_COLS_AV1 and tileWidths must point to a valid array of numTileColumns entries. + Entry i specifies the width in 64x64 CTU unit of tile colum i. The sum of all the entries should be equal to the picture width in 64x64 CTU units. */ + uint32_t numTileRows; /**< [in]: This parameter in conjunction with the flag enableCustomTileConfig and the array tileHeights[] specifies the way in which the picture is divided into tiles rows + When enableCustomTileConfig == 0, the picture will be uniformly divided into numTileRows tile rows. If numTileRows is not a power of 2, + it will be rounded down to the next power of 2 value. If numTileRows == 0, the picture will be coded with the smallest number of horizontal tiles as allowed by standard. + When enableCustomTileConfig == 1, numTileRows must be > 0 and <= NV_MAX_TILE_ROWS_AV1 and tileHeights must point to a valid array of numTileRows entries. + Entry i specifies the height in 64x64 CTU unit of tile row i. The sum of all the entries should be equal to the picture hieght in 64x64 CTU units. */ + uint32_t *tileWidths; /**< [in]: If enableCustomTileConfig == 1, tileWidths[i] specifies the width of tile column i in 64x64 CTU unit, with 0 <= i <= numTileColumns -1. */ + uint32_t *tileHeights; /**< [in]: If enableCustomTileConfig == 1, tileHeights[i] specifies the height of tile row i in 64x64 CTU unit, with 0 <= i <= numTileRows -1. */ + uint32_t obuPayloadArrayCnt; /**< [in]: Specifies the number of elements allocated in obuPayloadArray array. */ + uint32_t reserved; /**< [in]: Reserved and must be set to 0. */ + NV_ENC_AV1_OBU_PAYLOAD* obuPayloadArray; /**< [in]: Array of OBU payloads which will be inserted for this frame. */ + NV_ENC_FILM_GRAIN_PARAMS_AV1 *filmGrainParams; /**< [in]: If filmGrainParamsUpdate == 1, filmGrainParams must point to a valid NV_ENC_FILM_GRAIN_PARAMS_AV1 structure */ + uint32_t reserved2[247]; /**< [in]: Reserved and must be set to 0. */ + void* reserved3[61]; /**< [in]: Reserved and must be set to NULL. */ +} NV_ENC_PIC_PARAMS_AV1; + +/** + * Codec specific per-picture encoding parameters. + */ +typedef union _NV_ENC_CODEC_PIC_PARAMS +{ + NV_ENC_PIC_PARAMS_H264 h264PicParams; /**< [in]: H264 encode picture params. */ + NV_ENC_PIC_PARAMS_HEVC hevcPicParams; /**< [in]: HEVC encode picture params. */ + NV_ENC_PIC_PARAMS_AV1 av1PicParams; /**< [in]: AV1 encode picture params. */ + uint32_t reserved[256]; /**< [in]: Reserved and must be set to 0. */ +} NV_ENC_CODEC_PIC_PARAMS; + + +/** + * \struct _NV_ENC_PIC_PARAMS + * Encoding parameters that need to be sent on a per frame basis. + */ +typedef struct _NV_ENC_PIC_PARAMS +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_PIC_PARAMS_VER. */ + uint32_t inputWidth; /**< [in]: Specifies the input frame width */ + uint32_t inputHeight; /**< [in]: Specifies the input frame height */ + uint32_t inputPitch; /**< [in]: Specifies the input buffer pitch. If pitch value is not known, set this to inputWidth. */ + uint32_t encodePicFlags; /**< [in]: Specifies bit-wise OR of encode picture flags. See ::NV_ENC_PIC_FLAGS enum. */ + uint32_t frameIdx; /**< [in]: Specifies the frame index associated with the input frame [optional]. */ + uint64_t inputTimeStamp; /**< [in]: Specifies opaque data which is associated with the encoded frame, but not actually encoded in the output bitstream. + This opaque data can be used later to uniquely refer to the corresponding encoded frame. For example, it can be used + for identifying the frame to be invalidated in the reference picture buffer, if lost at the client. */ + uint64_t inputDuration; /**< [in]: Specifies duration of the input picture */ + NV_ENC_INPUT_PTR inputBuffer; /**< [in]: Specifies the input buffer pointer. Client must use a pointer obtained from ::NvEncCreateInputBuffer() or ::NvEncMapInputResource() APIs.*/ + NV_ENC_OUTPUT_PTR outputBitstream; /**< [in]: Specifies the output buffer pointer. + If NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem is set to 0, specifies the pointer to output buffer. Client should use a pointer obtained from ::NvEncCreateBitstreamBuffer() API. + If NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem is set to 1, client should allocate buffer in video memory for NV_ENC_ENCODE_OUT_PARAMS struct and encoded bitstream data. Client + should use a pointer obtained from ::NvEncMapInputResource() API, when mapping this output buffer and assign it to NV_ENC_PIC_PARAMS::outputBitstream. + First 256 bytes of this buffer should be interpreted as NV_ENC_ENCODE_OUT_PARAMS struct followed by encoded bitstream data. Recommended size for output buffer is sum of size of + NV_ENC_ENCODE_OUT_PARAMS struct and twice the input frame size for lower resolution eg. CIF and 1.5 times the input frame size for higher resolutions. If encoded bitstream size is + greater than the allocated buffer size for encoded bitstream, then the output buffer will have encoded bitstream data equal to buffer size. All CUDA operations on this buffer must use + the default stream. */ + void* completionEvent; /**< [in]: Specifies an event to be signaled on completion of encoding of this Frame [only if operating in Asynchronous mode]. Each output buffer should be associated with a distinct event pointer. */ + NV_ENC_BUFFER_FORMAT bufferFmt; /**< [in]: Specifies the input buffer format. */ + NV_ENC_PIC_STRUCT pictureStruct; /**< [in]: Specifies structure of the input picture. */ + NV_ENC_PIC_TYPE pictureType; /**< [in]: Specifies input picture type. Client required to be set explicitly by the client if the client has not set NV_ENC_INITALIZE_PARAMS::enablePTD to 1 while calling NvInitializeEncoder. */ + NV_ENC_CODEC_PIC_PARAMS codecPicParams; /**< [in]: Specifies the codec specific per-picture encoding parameters. */ + NVENC_EXTERNAL_ME_HINT_COUNTS_PER_BLOCKTYPE meHintCountsPerBlock[2]; /**< [in]: For H264 and Hevc, specifies the number of hint candidates per block per direction for the current frame. meHintCountsPerBlock[0] is for L0 predictors and meHintCountsPerBlock[1] is for L1 predictors. + The candidate count in NV_ENC_PIC_PARAMS::meHintCountsPerBlock[lx] must never exceed NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock[lx] provided during encoder initialization. */ + NVENC_EXTERNAL_ME_HINT *meExternalHints; /**< [in]: For H264 and Hevc, Specifies the pointer to ME external hints for the current frame. The size of ME hint buffer should be equal to number of macroblocks * the total number of candidates per macroblock. + The total number of candidates per MB per direction = 1*meHintCountsPerBlock[Lx].numCandsPerBlk16x16 + 2*meHintCountsPerBlock[Lx].numCandsPerBlk16x8 + 2*meHintCountsPerBlock[Lx].numCandsPerBlk8x8 + + 4*meHintCountsPerBlock[Lx].numCandsPerBlk8x8. For frames using bidirectional ME , the total number of candidates for single macroblock is sum of total number of candidates per MB for each direction (L0 and L1) */ + uint32_t reserved1[6]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[2]; /**< [in]: Reserved and must be set to NULL */ + int8_t *qpDeltaMap; /**< [in]: Specifies the pointer to signed byte array containing value per MB for H264, per CTB for HEVC and per SB for AV1 in raster scan order for the current picture, which will be interpreted depending on NV_ENC_RC_PARAMS::qpMapMode. + If NV_ENC_RC_PARAMS::qpMapMode is NV_ENC_QP_MAP_DELTA, qpDeltaMap specifies QP modifier per MB for H264, per CTB for HEVC and per SB for AV1. This QP modifier will be applied on top of the QP chosen by rate control. + If NV_ENC_RC_PARAMS::qpMapMode is NV_ENC_QP_MAP_EMPHASIS, qpDeltaMap specifies Emphasis Level Map per MB for H264. This level value along with QP chosen by rate control is used to + compute the QP modifier, which in turn is applied on top of QP chosen by rate control. + If NV_ENC_RC_PARAMS::qpMapMode is NV_ENC_QP_MAP_DISABLED, value in qpDeltaMap will be ignored.*/ + uint32_t qpDeltaMapSize; /**< [in]: Specifies the size in bytes of qpDeltaMap surface allocated by client and pointed to by NV_ENC_PIC_PARAMS::qpDeltaMap. Surface (array) should be picWidthInMbs * picHeightInMbs for H264, picWidthInCtbs * picHeightInCtbs for HEVC and + picWidthInSbs * picHeightInSbs for AV1 */ + uint32_t reservedBitFields; /**< [in]: Reserved bitfields and must be set to 0 */ + uint16_t meHintRefPicDist[2]; /**< [in]: Specifies temporal distance for reference picture (NVENC_EXTERNAL_ME_HINT::refidx = 0) used during external ME with NV_ENC_INITALIZE_PARAMS::enablePTD = 1 . meHintRefPicDist[0] is for L0 hints and meHintRefPicDist[1] is for L1 hints. + If not set, will internally infer distance of 1. Ignored for NV_ENC_INITALIZE_PARAMS::enablePTD = 0 */ + NV_ENC_INPUT_PTR alphaBuffer; /**< [in]: Specifies the input alpha buffer pointer. Client must use a pointer obtained from ::NvEncCreateInputBuffer() or ::NvEncMapInputResource() APIs. + Applicable only when encoding hevc with alpha layer is enabled. */ + NVENC_EXTERNAL_ME_SB_HINT *meExternalSbHints; /**< [in]: For AV1,Specifies the pointer to ME external SB hints for the current frame. The size of ME hint buffer should be equal to meSbHintsCount. */ + uint32_t meSbHintsCount; /**< [in]: For AV1, specifies the total number of external ME SB hint candidates for the frame + NV_ENC_PIC_PARAMS::meSbHintsCount must never exceed the total number of SBs in frame * the max number of candidates per SB provided during encoder initialization. + The max number of candidates per SB is maxMeHintCountsPerBlock[0].numCandsPerSb + maxMeHintCountsPerBlock[1].numCandsPerSb */ + uint32_t reserved3[285]; /**< [in]: Reserved and must be set to 0 */ + void* reserved4[58]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_PIC_PARAMS; + +/** Macro for constructing the version field of ::_NV_ENC_PIC_PARAMS */ +#define NV_ENC_PIC_PARAMS_VER (NVENCAPI_STRUCT_VERSION(6) | ( 1<<31 )) + + +/** + * \struct _NV_ENC_MEONLY_PARAMS + * MEOnly parameters that need to be sent on a per motion estimation basis. + * NV_ENC_MEONLY_PARAMS::meExternalHints is supported for H264 only. + */ +typedef struct _NV_ENC_MEONLY_PARAMS +{ + uint32_t version; /**< [in]: Struct version. Must be set to NV_ENC_MEONLY_PARAMS_VER.*/ + uint32_t inputWidth; /**< [in]: Specifies the input frame width */ + uint32_t inputHeight; /**< [in]: Specifies the input frame height */ + NV_ENC_INPUT_PTR inputBuffer; /**< [in]: Specifies the input buffer pointer. Client must use a pointer obtained from NvEncCreateInputBuffer() or NvEncMapInputResource() APIs. */ + NV_ENC_INPUT_PTR referenceFrame; /**< [in]: Specifies the reference frame pointer */ + NV_ENC_OUTPUT_PTR mvBuffer; /**< [in]: Specifies the output buffer pointer. + If NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem is set to 0, specifies the pointer to motion vector data buffer allocated by NvEncCreateMVBuffer. + Client must lock mvBuffer using ::NvEncLockBitstream() API to get the motion vector data. + If NV_ENC_INITIALIZE_PARAMS::enableOutputInVidmem is set to 1, client should allocate buffer in video memory for storing the motion vector data. The size of this buffer must + be equal to total number of macroblocks multiplied by size of NV_ENC_H264_MV_DATA struct. Client should use a pointer obtained from ::NvEncMapInputResource() API, when mapping this + output buffer and assign it to NV_ENC_MEONLY_PARAMS::mvBuffer. All CUDA operations on this buffer must use the default stream. */ + NV_ENC_BUFFER_FORMAT bufferFmt; /**< [in]: Specifies the input buffer format. */ + void* completionEvent; /**< [in]: Specifies an event to be signaled on completion of motion estimation + of this Frame [only if operating in Asynchronous mode]. + Each output buffer should be associated with a distinct event pointer. */ + uint32_t viewID; /**< [in]: Specifies left or right viewID if NV_ENC_CONFIG_H264_MEONLY::bStereoEnable is set. + viewID can be 0,1 if bStereoEnable is set, 0 otherwise. */ + NVENC_EXTERNAL_ME_HINT_COUNTS_PER_BLOCKTYPE + meHintCountsPerBlock[2]; /**< [in]: Specifies the number of hint candidates per block for the current frame. meHintCountsPerBlock[0] is for L0 predictors. + The candidate count in NV_ENC_PIC_PARAMS::meHintCountsPerBlock[lx] must never exceed NV_ENC_INITIALIZE_PARAMS::maxMEHintCountsPerBlock[lx] provided during encoder initialization. */ + NVENC_EXTERNAL_ME_HINT *meExternalHints; /**< [in]: Specifies the pointer to ME external hints for the current frame. The size of ME hint buffer should be equal to number of macroblocks * the total number of candidates per macroblock. + The total number of candidates per MB per direction = 1*meHintCountsPerBlock[Lx].numCandsPerBlk16x16 + 2*meHintCountsPerBlock[Lx].numCandsPerBlk16x8 + 2*meHintCountsPerBlock[Lx].numCandsPerBlk8x8 + + 4*meHintCountsPerBlock[Lx].numCandsPerBlk8x8. For frames using bidirectional ME , the total number of candidates for single macroblock is sum of total number of candidates per MB for each direction (L0 and L1) */ + uint32_t reserved1[243]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[59]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_MEONLY_PARAMS; + +/** NV_ENC_MEONLY_PARAMS struct version*/ +#define NV_ENC_MEONLY_PARAMS_VER NVENCAPI_STRUCT_VERSION(3) + + +/** + * \struct _NV_ENC_LOCK_BITSTREAM + * Bitstream buffer lock parameters. + */ +typedef struct _NV_ENC_LOCK_BITSTREAM +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_LOCK_BITSTREAM_VER. */ + uint32_t doNotWait :1; /**< [in]: If this flag is set, the NvEncodeAPI interface will return buffer pointer even if operation is not completed. If not set, the call will block until operation completes. */ + uint32_t ltrFrame :1; /**< [out]: Flag indicating this frame is marked as LTR frame */ + uint32_t getRCStats :1; /**< [in]: If this flag is set then lockBitstream call will add additional intra-inter MB count and average MVX, MVY */ + uint32_t reservedBitFields :29; /**< [in]: Reserved bit fields and must be set to 0 */ + void* outputBitstream; /**< [in]: Pointer to the bitstream buffer being locked. */ + uint32_t* sliceOffsets; /**< [in, out]: Array which receives the slice (H264/HEVC) or tile (AV1) offsets. This is not supported if NV_ENC_CONFIG_H264::sliceMode is 1 on Kepler GPUs. Array size must be equal to size of frame in MBs. */ + uint32_t frameIdx; /**< [out]: Frame no. for which the bitstream is being retrieved. */ + uint32_t hwEncodeStatus; /**< [out]: The NvEncodeAPI interface status for the locked picture. */ + uint32_t numSlices; /**< [out]: Number of slices (H264/HEVC) or tiles (AV1) in the encoded picture. Will be reported only if NV_ENC_INITIALIZE_PARAMS::reportSliceOffsets set to 1. */ + uint32_t bitstreamSizeInBytes; /**< [out]: Actual number of bytes generated and copied to the memory pointed by bitstreamBufferPtr. + When HEVC alpha layer encoding is enabled, this field reports the total encoded size in bytes i.e it is the encoded size of the base plus the alpha layer. + For AV1 when enablePTD is set, this field reports the total encoded size in bytes of all the encoded frames packed into the current output surface i.e. show frame plus all preceding no-show frames */ + uint64_t outputTimeStamp; /**< [out]: Presentation timestamp associated with the encoded output. */ + uint64_t outputDuration; /**< [out]: Presentation duration associates with the encoded output. */ + void* bitstreamBufferPtr; /**< [out]: Pointer to the generated output bitstream. + For MEOnly mode _NV_ENC_LOCK_BITSTREAM::bitstreamBufferPtr should be typecast to + NV_ENC_H264_MV_DATA/NV_ENC_HEVC_MV_DATA pointer respectively for H264/HEVC */ + NV_ENC_PIC_TYPE pictureType; /**< [out]: Picture type of the encoded picture. */ + NV_ENC_PIC_STRUCT pictureStruct; /**< [out]: Structure of the generated output picture. */ + uint32_t frameAvgQP; /**< [out]: Average QP of the frame. */ + uint32_t frameSatd; /**< [out]: Total SATD cost for whole frame. */ + uint32_t ltrFrameIdx; /**< [out]: Frame index associated with this LTR frame. */ + uint32_t ltrFrameBitmap; /**< [out]: Bitmap of LTR frames indices which were used for encoding this frame. Value of 0 if no LTR frames were used. */ + uint32_t temporalId; /**< [out]: TemporalId value of the frame when using temporalSVC encoding */ + uint32_t reserved[12]; /**< [in]: Reserved and must be set to 0 */ + uint32_t intraMBCount; /**< [out]: For H264, Number of Intra MBs in the encoded frame. For HEVC, Number of Intra CTBs in the encoded frame. For AV1, Number of Intra SBs in the encoded show frame. Supported only if _NV_ENC_LOCK_BITSTREAM::getRCStats set to 1. */ + uint32_t interMBCount; /**< [out]: For H264, Number of Inter MBs in the encoded frame, includes skip MBs. For HEVC, Number of Inter CTBs in the encoded frame. For AV1, Number of Inter SBs in the encoded show frame. Supported only if _NV_ENC_LOCK_BITSTREAM::getRCStats set to 1. */ + int32_t averageMVX; /**< [out]: Average Motion Vector in X direction for the encoded frame. Supported only if _NV_ENC_LOCK_BITSTREAM::getRCStats set to 1. */ + int32_t averageMVY; /**< [out]: Average Motion Vector in y direction for the encoded frame. Supported only if _NV_ENC_LOCK_BITSTREAM::getRCStats set to 1. */ + uint32_t alphaLayerSizeInBytes; /**< [out]: Number of bytes generated for the alpha layer in the encoded output. Applicable only when HEVC with alpha encoding is enabled. */ + + uint32_t reserved1[218]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_LOCK_BITSTREAM; + +/** Macro for constructing the version field of ::_NV_ENC_LOCK_BITSTREAM */ +#define NV_ENC_LOCK_BITSTREAM_VER NVENCAPI_STRUCT_VERSION(2) + + +/** + * \struct _NV_ENC_LOCK_INPUT_BUFFER + * Uncompressed Input Buffer lock parameters. + */ +typedef struct _NV_ENC_LOCK_INPUT_BUFFER +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_LOCK_INPUT_BUFFER_VER. */ + uint32_t doNotWait :1; /**< [in]: Set to 1 to make ::NvEncLockInputBuffer() a unblocking call. If the encoding is not completed, driver will return ::NV_ENC_ERR_ENCODER_BUSY error code. */ + uint32_t reservedBitFields :31; /**< [in]: Reserved bitfields and must be set to 0 */ + NV_ENC_INPUT_PTR inputBuffer; /**< [in]: Pointer to the input buffer to be locked, client should pass the pointer obtained from ::NvEncCreateInputBuffer() or ::NvEncMapInputResource API. */ + void* bufferDataPtr; /**< [out]: Pointed to the locked input buffer data. Client can only access input buffer using the \p bufferDataPtr. */ + uint32_t pitch; /**< [out]: Pitch of the locked input buffer. */ + uint32_t reserved1[251]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_LOCK_INPUT_BUFFER; + +/** Macro for constructing the version field of ::_NV_ENC_LOCK_INPUT_BUFFER */ +#define NV_ENC_LOCK_INPUT_BUFFER_VER NVENCAPI_STRUCT_VERSION(1) + + +/** + * \struct _NV_ENC_MAP_INPUT_RESOURCE + * Map an input resource to a Nvidia Encoder Input Buffer + */ +typedef struct _NV_ENC_MAP_INPUT_RESOURCE +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_MAP_INPUT_RESOURCE_VER. */ + uint32_t subResourceIndex; /**< [in]: Deprecated. Do not use. */ + void* inputResource; /**< [in]: Deprecated. Do not use. */ + NV_ENC_REGISTERED_PTR registeredResource; /**< [in]: The Registered resource handle obtained by calling NvEncRegisterInputResource. */ + NV_ENC_INPUT_PTR mappedResource; /**< [out]: Mapped pointer corresponding to the registeredResource. This pointer must be used in NV_ENC_PIC_PARAMS::inputBuffer parameter in ::NvEncEncodePicture() API. */ + NV_ENC_BUFFER_FORMAT mappedBufferFmt; /**< [out]: Buffer format of the outputResource. This buffer format must be used in NV_ENC_PIC_PARAMS::bufferFmt if client using the above mapped resource pointer. */ + uint32_t reserved1[251]; /**< [in]: Reserved and must be set to 0. */ + void* reserved2[63]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_MAP_INPUT_RESOURCE; + +/** Macro for constructing the version field of ::_NV_ENC_MAP_INPUT_RESOURCE */ +#define NV_ENC_MAP_INPUT_RESOURCE_VER NVENCAPI_STRUCT_VERSION(4) + +/** + * \struct _NV_ENC_INPUT_RESOURCE_OPENGL_TEX + * NV_ENC_REGISTER_RESOURCE::resourceToRegister must be a pointer to a variable of this type, + * when NV_ENC_REGISTER_RESOURCE::resourceType is NV_ENC_INPUT_RESOURCE_TYPE_OPENGL_TEX + */ +typedef struct _NV_ENC_INPUT_RESOURCE_OPENGL_TEX +{ + uint32_t texture; /**< [in]: The name of the texture to be used. */ + uint32_t target; /**< [in]: Accepted values are GL_TEXTURE_RECTANGLE and GL_TEXTURE_2D. */ +} NV_ENC_INPUT_RESOURCE_OPENGL_TEX; + +/** \struct NV_ENC_FENCE_POINT_D3D12 +* Fence and fence value for synchronization. +*/ +typedef struct _NV_ENC_FENCE_POINT_D3D12 +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_FENCE_POINT_D3D12_VER. */ + uint32_t reserved; /**< [in]: Reserved and must be set to 0. */ + void* pFence; /**< [in]: Pointer to ID3D12Fence. This fence object is used for synchronization. */ + uint64_t waitValue; /**< [in]: Fence value to reach or exceed before the GPU operation. */ + uint64_t signalValue; /**< [in]: Fence value to set the fence to, after the GPU operation. */ + uint32_t bWait:1; /**< [in]: Wait on 'waitValue' if bWait is set to 1, before starting GPU operation. */ + uint32_t bSignal:1; /**< [in]: Signal on 'signalValue' if bSignal is set to 1, after GPU operation is complete. */ + uint32_t reservedBitField:30; /**< [in]: Reserved and must be set to 0. */ + uint32_t reserved1[7]; /**< [in]: Reserved and must be set to 0. */ +} NV_ENC_FENCE_POINT_D3D12; + +#define NV_ENC_FENCE_POINT_D3D12_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * \struct _NV_ENC_INPUT_RESOURCE_D3D12 + * NV_ENC_PIC_PARAMS::inputBuffer and NV_ENC_PIC_PARAMS::alphaBuffer must be a pointer to a struct of this type, + * when D3D12 interface is used + */ +typedef struct _NV_ENC_INPUT_RESOURCE_D3D12 +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_INPUT_RESOURCE_D3D12_VER. */ + uint32_t reserved; /**< [in]: Reserved and must be set to 0. */ + NV_ENC_INPUT_PTR pInputBuffer; /**< [in]: Specifies the input surface pointer. Client must use a pointer obtained from NvEncMapInputResource() in NV_ENC_MAP_INPUT_RESOURCE::mappedResource + when mapping the input surface. */ + NV_ENC_FENCE_POINT_D3D12 inputFencePoint; /**< [in]: Specifies the fence and corresponding fence values to do GPU wait and signal. */ + uint32_t reserved1[16]; /**< [in]: Reserved and must be set to 0. */ + void* reserved2[16]; /**< [in]: Reserved and must be set to NULL. */ +} NV_ENC_INPUT_RESOURCE_D3D12; + +#define NV_ENC_INPUT_RESOURCE_D3D12_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * \struct _NV_ENC_OUTPUT_RESOURCE_D3D12 + * NV_ENC_PIC_PARAMS::outputBitstream and NV_ENC_LOCK_BITSTREAM::outputBitstream must be a pointer to a struct of this type, + * when D3D12 interface is used + */ +typedef struct _NV_ENC_OUTPUT_RESOURCE_D3D12 +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_OUTPUT_RESOURCE_D3D12_VER. */ + uint32_t reserved; /**< [in]: Reserved and must be set to 0. */ + NV_ENC_INPUT_PTR pOutputBuffer; /**< [in]: Specifies the output buffer pointer. Client must use a pointer obtained from NvEncMapInputResource() in NV_ENC_MAP_INPUT_RESOURCE::mappedResource + when mapping output bitstream buffer */ + NV_ENC_FENCE_POINT_D3D12 outputFencePoint; /**< [in]: Specifies the fence and corresponding fence values to do GPU wait and signal.*/ + uint32_t reserved1[16]; /**< [in]: Reserved and must be set to 0. */ + void* reserved2[16]; /**< [in]: Reserved and must be set to NULL. */ +} NV_ENC_OUTPUT_RESOURCE_D3D12; + +#define NV_ENC_OUTPUT_RESOURCE_D3D12_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * \struct _NV_ENC_REGISTER_RESOURCE + * Register a resource for future use with the Nvidia Video Encoder Interface. + */ +typedef struct _NV_ENC_REGISTER_RESOURCE +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_REGISTER_RESOURCE_VER. */ + NV_ENC_INPUT_RESOURCE_TYPE resourceType; /**< [in]: Specifies the type of resource to be registered. + Supported values are + ::NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX, + ::NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR, + ::NV_ENC_INPUT_RESOURCE_TYPE_OPENGL_TEX */ + uint32_t width; /**< [in]: Input frame width. */ + uint32_t height; /**< [in]: Input frame height. */ + uint32_t pitch; /**< [in]: Input buffer pitch. + For ::NV_ENC_INPUT_RESOURCE_TYPE_DIRECTX resources, set this to 0. + For ::NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR resources, set this to + the pitch as obtained from cuMemAllocPitch(), or to the width in + bytes (if this resource was created by using cuMemAlloc()). This + value must be a multiple of 4. + For ::NV_ENC_INPUT_RESOURCE_TYPE_CUDAARRAY resources, set this to the + width of the allocation in bytes (i.e. + CUDA_ARRAY3D_DESCRIPTOR::Width * CUDA_ARRAY3D_DESCRIPTOR::NumChannels). + For ::NV_ENC_INPUT_RESOURCE_TYPE_OPENGL_TEX resources, set this to the + texture width multiplied by the number of components in the texture + format. */ + uint32_t subResourceIndex; /**< [in]: Subresource Index of the DirectX resource to be registered. Should be set to 0 for other interfaces. */ + void* resourceToRegister; /**< [in]: Handle to the resource that is being registered. */ + NV_ENC_REGISTERED_PTR registeredResource; /**< [out]: Registered resource handle. This should be used in future interactions with the Nvidia Video Encoder Interface. */ + NV_ENC_BUFFER_FORMAT bufferFormat; /**< [in]: Buffer format of resource to be registered. */ + NV_ENC_BUFFER_USAGE bufferUsage; /**< [in]: Usage of resource to be registered. */ + NV_ENC_FENCE_POINT_D3D12* pInputFencePoint; /**< [in]: Specifies the input fence and corresponding fence values to do GPU wait and signal. + To be used only when NV_ENC_REGISTER_RESOURCE::resourceToRegister represents D3D12 surface and + NV_ENC_BUFFER_USAGE::bufferUsage is NV_ENC_INPUT_IMAGE. + The fence NV_ENC_FENCE_POINT_D3D12::pFence and NV_ENC_FENCE_POINT_D3D12::waitValue will be used to do GPU wait + before starting GPU operation, if NV_ENC_FENCE_POINT_D3D12::bWait is set. + The fence NV_ENC_FENCE_POINT_D3D12::pFence and NV_ENC_FENCE_POINT_D3D12::signalValue will be used to do GPU signal + when GPU operation finishes, if NV_ENC_FENCE_POINT_D3D12::bSignal is set. */ + uint32_t reserved1[247]; /**< [in]: Reserved and must be set to 0. */ + void* reserved2[61]; /**< [in]: Reserved and must be set to NULL. */ +} NV_ENC_REGISTER_RESOURCE; + +/** Macro for constructing the version field of ::_NV_ENC_REGISTER_RESOURCE */ +#define NV_ENC_REGISTER_RESOURCE_VER NVENCAPI_STRUCT_VERSION(4) + +/** + * \struct _NV_ENC_STAT + * Encode Stats structure. + */ +typedef struct _NV_ENC_STAT +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_STAT_VER. */ + uint32_t reserved; /**< [in]: Reserved and must be set to 0 */ + NV_ENC_OUTPUT_PTR outputBitStream; /**< [out]: Specifies the pointer to output bitstream. */ + uint32_t bitStreamSize; /**< [out]: Size of generated bitstream in bytes. */ + uint32_t picType; /**< [out]: Picture type of encoded picture. See ::NV_ENC_PIC_TYPE. */ + uint32_t lastValidByteOffset; /**< [out]: Offset of last valid bytes of completed bitstream */ + uint32_t sliceOffsets[16]; /**< [out]: Offsets of each slice */ + uint32_t picIdx; /**< [out]: Picture number */ + uint32_t frameAvgQP; /**< [out]: Average QP of the frame. */ + uint32_t ltrFrame :1; /**< [out]: Flag indicating this frame is marked as LTR frame */ + uint32_t reservedBitFields :31; /**< [in]: Reserved bit fields and must be set to 0 */ + uint32_t ltrFrameIdx; /**< [out]: Frame index associated with this LTR frame. */ + uint32_t intraMBCount; /**< [out]: For H264, Number of Intra MBs in the encoded frame. For HEVC, Number of Intra CTBs in the encoded frame. */ + uint32_t interMBCount; /**< [out]: For H264, Number of Inter MBs in the encoded frame, includes skip MBs. For HEVC, Number of Inter CTBs in the encoded frame. */ + int32_t averageMVX; /**< [out]: Average Motion Vector in X direction for the encoded frame. */ + int32_t averageMVY; /**< [out]: Average Motion Vector in y direction for the encoded frame. */ + uint32_t reserved1[226]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_STAT; + +/** Macro for constructing the version field of ::_NV_ENC_STAT */ +#define NV_ENC_STAT_VER NVENCAPI_STRUCT_VERSION(1) + + +/** + * \struct _NV_ENC_SEQUENCE_PARAM_PAYLOAD + * Sequence and picture paramaters payload. + */ +typedef struct _NV_ENC_SEQUENCE_PARAM_PAYLOAD +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_INITIALIZE_PARAMS_VER. */ + uint32_t inBufferSize; /**< [in]: Specifies the size of the spsppsBuffer provided by the client */ + uint32_t spsId; /**< [in]: Specifies the SPS id to be used in sequence header. Default value is 0. */ + uint32_t ppsId; /**< [in]: Specifies the PPS id to be used in picture header. Default value is 0. */ + void* spsppsBuffer; /**< [in]: Specifies bitstream header pointer of size NV_ENC_SEQUENCE_PARAM_PAYLOAD::inBufferSize. + It is the client's responsibility to manage this memory. */ + uint32_t* outSPSPPSPayloadSize; /**< [out]: Size of the sequence and picture header in bytes. */ + uint32_t reserved [250]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_SEQUENCE_PARAM_PAYLOAD; + +/** Macro for constructing the version field of ::_NV_ENC_SEQUENCE_PARAM_PAYLOAD */ +#define NV_ENC_SEQUENCE_PARAM_PAYLOAD_VER NVENCAPI_STRUCT_VERSION(1) + + +/** + * Event registration/unregistration parameters. + */ +typedef struct _NV_ENC_EVENT_PARAMS +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_EVENT_PARAMS_VER. */ + uint32_t reserved; /**< [in]: Reserved and must be set to 0 */ + void* completionEvent; /**< [in]: Handle to event to be registered/unregistered with the NvEncodeAPI interface. */ + uint32_t reserved1[253]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_EVENT_PARAMS; + +/** Macro for constructing the version field of ::_NV_ENC_EVENT_PARAMS */ +#define NV_ENC_EVENT_PARAMS_VER NVENCAPI_STRUCT_VERSION(1) + +/** + * Encoder Session Creation parameters + */ +typedef struct _NV_ENC_OPEN_ENCODE_SESSIONEX_PARAMS +{ + uint32_t version; /**< [in]: Struct version. Must be set to ::NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER. */ + NV_ENC_DEVICE_TYPE deviceType; /**< [in]: Specified the device Type */ + void* device; /**< [in]: Pointer to client device. */ + void* reserved; /**< [in]: Reserved and must be set to 0. */ + uint32_t apiVersion; /**< [in]: API version. Should be set to NVENCAPI_VERSION. */ + uint32_t reserved1[253]; /**< [in]: Reserved and must be set to 0 */ + void* reserved2[64]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS; +/** Macro for constructing the version field of ::_NV_ENC_OPEN_ENCODE_SESSIONEX_PARAMS */ +#define NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER NVENCAPI_STRUCT_VERSION(1) + +/** @} */ /* END ENCODER_STRUCTURE */ + + +/** + * \addtogroup ENCODE_FUNC NvEncodeAPI Functions + * @{ + */ + +// NvEncOpenEncodeSession +/** + * \brief Opens an encoding session. + * + * Deprecated. + * + * \return + * ::NV_ENC_ERR_INVALID_CALL\n + * + */ +NVENCSTATUS NVENCAPI NvEncOpenEncodeSession (void* device, uint32_t deviceType, void** encoder); + +// NvEncGetEncodeGuidCount +/** + * \brief Retrieves the number of supported encode GUIDs. + * + * The function returns the number of codec GUIDs supported by the NvEncodeAPI + * interface. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [out] encodeGUIDCount + * Number of supported encode GUIDs. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetEncodeGUIDCount (void* encoder, uint32_t* encodeGUIDCount); + + +// NvEncGetEncodeGUIDs +/** + * \brief Retrieves an array of supported encoder codec GUIDs. + * + * The function returns an array of codec GUIDs supported by the NvEncodeAPI interface. + * The client must allocate an array where the NvEncodeAPI interface can + * fill the supported GUIDs and pass the pointer in \p *GUIDs parameter. + * The size of the array can be determined by using ::NvEncGetEncodeGUIDCount() API. + * The Nvidia Encoding interface returns the number of codec GUIDs it has actually + * filled in the GUID array in the \p GUIDCount parameter. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] guidArraySize + * Number of GUIDs to retrieved. Should be set to the number retrieved using + * ::NvEncGetEncodeGUIDCount. + * \param [out] GUIDs + * Array of supported Encode GUIDs. + * \param [out] GUIDCount + * Number of supported Encode GUIDs. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetEncodeGUIDs (void* encoder, GUID* GUIDs, uint32_t guidArraySize, uint32_t* GUIDCount); + + +// NvEncGetEncodeProfileGuidCount +/** + * \brief Retrieves the number of supported profile GUIDs. + * + * The function returns the number of profile GUIDs supported for a given codec. + * The client must first enumerate the codec GUIDs supported by the NvEncodeAPI + * interface. After determining the codec GUID, it can query the NvEncodeAPI + * interface to determine the number of profile GUIDs supported for a particular + * codec GUID. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encodeGUID + * The codec GUID for which the profile GUIDs are being enumerated. + * \param [out] encodeProfileGUIDCount + * Number of encode profiles supported for the given encodeGUID. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetEncodeProfileGUIDCount (void* encoder, GUID encodeGUID, uint32_t* encodeProfileGUIDCount); + + +// NvEncGetEncodeProfileGUIDs +/** + * \brief Retrieves an array of supported encode profile GUIDs. + * + * The function returns an array of supported profile GUIDs for a particular + * codec GUID. The client must allocate an array where the NvEncodeAPI interface + * can populate the profile GUIDs. The client can determine the array size using + * ::NvEncGetEncodeProfileGUIDCount() API. The client must also validiate that the + * NvEncodeAPI interface supports the GUID the client wants to pass as \p encodeGUID + * parameter. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encodeGUID + * The encode GUID whose profile GUIDs are being enumerated. + * \param [in] guidArraySize + * Number of GUIDs to be retrieved. Should be set to the number retrieved using + * ::NvEncGetEncodeProfileGUIDCount. + * \param [out] profileGUIDs + * Array of supported Encode Profile GUIDs + * \param [out] GUIDCount + * Number of valid encode profile GUIDs in \p profileGUIDs array. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetEncodeProfileGUIDs (void* encoder, GUID encodeGUID, GUID* profileGUIDs, uint32_t guidArraySize, uint32_t* GUIDCount); + +// NvEncGetInputFormatCount +/** + * \brief Retrieve the number of supported Input formats. + * + * The function returns the number of supported input formats. The client must + * query the NvEncodeAPI interface to determine the supported input formats + * before creating the input surfaces. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encodeGUID + * Encode GUID, corresponding to which the number of supported input formats + * is to be retrieved. + * \param [out] inputFmtCount + * Number of input formats supported for specified Encode GUID. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + */ +NVENCSTATUS NVENCAPI NvEncGetInputFormatCount (void* encoder, GUID encodeGUID, uint32_t* inputFmtCount); + + +// NvEncGetInputFormats +/** + * \brief Retrieves an array of supported Input formats + * + * Returns an array of supported input formats The client must use the input + * format to create input surface using ::NvEncCreateInputBuffer() API. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encodeGUID + * Encode GUID, corresponding to which the number of supported input formats + * is to be retrieved. + *\param [in] inputFmtArraySize + * Size input format count array passed in \p inputFmts. + *\param [out] inputFmts + * Array of input formats supported for this Encode GUID. + *\param [out] inputFmtCount + * The number of valid input format types returned by the NvEncodeAPI + * interface in \p inputFmts array. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetInputFormats (void* encoder, GUID encodeGUID, NV_ENC_BUFFER_FORMAT* inputFmts, uint32_t inputFmtArraySize, uint32_t* inputFmtCount); + + +// NvEncGetEncodeCaps +/** + * \brief Retrieves the capability value for a specified encoder attribute. + * + * The function returns the capability value for a given encoder attribute. The + * client must validate the encodeGUID using ::NvEncGetEncodeGUIDs() API before + * calling this function. The encoder attribute being queried are enumerated in + * ::NV_ENC_CAPS_PARAM enum. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encodeGUID + * Encode GUID, corresponding to which the capability attribute is to be retrieved. + * \param [in] capsParam + * Used to specify attribute being queried. Refer ::NV_ENC_CAPS_PARAM for more + * details. + * \param [out] capsVal + * The value corresponding to the capability attribute being queried. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + */ +NVENCSTATUS NVENCAPI NvEncGetEncodeCaps (void* encoder, GUID encodeGUID, NV_ENC_CAPS_PARAM* capsParam, int* capsVal); + + +// NvEncGetEncodePresetCount +/** + * \brief Retrieves the number of supported preset GUIDs. + * + * The function returns the number of preset GUIDs available for a given codec. + * The client must validate the codec GUID using ::NvEncGetEncodeGUIDs() API + * before calling this function. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encodeGUID + * Encode GUID, corresponding to which the number of supported presets is to + * be retrieved. + * \param [out] encodePresetGUIDCount + * Receives the number of supported preset GUIDs. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetEncodePresetCount (void* encoder, GUID encodeGUID, uint32_t* encodePresetGUIDCount); + + +// NvEncGetEncodePresetGUIDs +/** + * \brief Receives an array of supported encoder preset GUIDs. + * + * The function returns an array of encode preset GUIDs available for a given codec. + * The client can directly use one of the preset GUIDs based upon the use case + * or target device. The preset GUID chosen can be directly used in + * NV_ENC_INITIALIZE_PARAMS::presetGUID parameter to ::NvEncEncodePicture() API. + * Alternately client can also use the preset GUID to retrieve the encoding config + * parameters being used by NvEncodeAPI interface for that given preset, using + * ::NvEncGetEncodePresetConfig() API. It can then modify preset config parameters + * as per its use case and send it to NvEncodeAPI interface as part of + * NV_ENC_INITIALIZE_PARAMS::encodeConfig parameter for NvEncInitializeEncoder() + * API. + * + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encodeGUID + * Encode GUID, corresponding to which the list of supported presets is to be + * retrieved. + * \param [in] guidArraySize + * Size of array of preset GUIDs passed in \p preset GUIDs + * \param [out] presetGUIDs + * Array of supported Encode preset GUIDs from the NvEncodeAPI interface + * to client. + * \param [out] encodePresetGUIDCount + * Receives the number of preset GUIDs returned by the NvEncodeAPI + * interface. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetEncodePresetGUIDs (void* encoder, GUID encodeGUID, GUID* presetGUIDs, uint32_t guidArraySize, uint32_t* encodePresetGUIDCount); + + +// NvEncGetEncodePresetConfig +/** + * \brief Returns a preset config structure supported for given preset GUID. + * + * The function returns a preset config structure for a given preset GUID. + * NvEncGetEncodePresetConfig() API is not applicable to AV1. + * Before using this function the client must enumerate the preset GUIDs available for + * a given codec. The preset config structure can be modified by the client depending + * upon its use case and can be then used to initialize the encoder using + * ::NvEncInitializeEncoder() API. The client can use this function only if it + * wants to modify the NvEncodeAPI preset configuration, otherwise it can + * directly use the preset GUID. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encodeGUID + * Encode GUID, corresponding to which the list of supported presets is to be + * retrieved. + * \param [in] presetGUID + * Preset GUID, corresponding to which the Encoding configurations is to be + * retrieved. + * \param [out] presetConfig + * The requested Preset Encoder Attribute set. Refer ::_NV_ENC_CONFIG for +* more details. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetEncodePresetConfig (void* encoder, GUID encodeGUID, GUID presetGUID, NV_ENC_PRESET_CONFIG* presetConfig); + +// NvEncGetEncodePresetConfigEx +/** + * \brief Returns a preset config structure supported for given preset GUID. + * + * The function returns a preset config structure for a given preset GUID and tuning info. + * NvEncGetEncodePresetConfigEx() API is not applicable to H264 and HEVC meonly mode. + * Before using this function the client must enumerate the preset GUIDs available for + * a given codec. The preset config structure can be modified by the client depending + * upon its use case and can be then used to initialize the encoder using + * ::NvEncInitializeEncoder() API. The client can use this function only if it + * wants to modify the NvEncodeAPI preset configuration, otherwise it can + * directly use the preset GUID. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encodeGUID + * Encode GUID, corresponding to which the list of supported presets is to be + * retrieved. + * \param [in] presetGUID + * Preset GUID, corresponding to which the Encoding configurations is to be + * retrieved. + * \param [in] tuningInfo + * tuning info, corresponding to which the Encoding configurations is to be + * retrieved. + * \param [out] presetConfig + * The requested Preset Encoder Attribute set. Refer ::_NV_ENC_CONFIG for + * more details. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetEncodePresetConfigEx (void* encoder, GUID encodeGUID, GUID presetGUID, NV_ENC_TUNING_INFO tuningInfo, NV_ENC_PRESET_CONFIG* presetConfig); + +// NvEncInitializeEncoder +/** + * \brief Initialize the encoder. + * + * This API must be used to initialize the encoder. The initialization parameter + * is passed using \p *createEncodeParams The client must send the following + * fields of the _NV_ENC_INITIALIZE_PARAMS structure with a valid value. + * - NV_ENC_INITIALIZE_PARAMS::encodeGUID + * - NV_ENC_INITIALIZE_PARAMS::encodeWidth + * - NV_ENC_INITIALIZE_PARAMS::encodeHeight + * + * The client can pass a preset GUID directly to the NvEncodeAPI interface using + * NV_ENC_INITIALIZE_PARAMS::presetGUID field. If the client doesn't pass + * NV_ENC_INITIALIZE_PARAMS::encodeConfig structure, the codec specific parameters + * will be selected based on the preset GUID. The preset GUID must have been + * validated by the client using ::NvEncGetEncodePresetGUIDs() API. + * If the client passes a custom ::_NV_ENC_CONFIG structure through + * NV_ENC_INITIALIZE_PARAMS::encodeConfig , it will override the codec specific parameters + * based on the preset GUID. It is recommended that even if the client passes a custom config, + * it should also send a preset GUID. In this case, the preset GUID passed by the client + * will not override any of the custom config parameters programmed by the client, + * it is only used as a hint by the NvEncodeAPI interface to determine certain encoder parameters + * which are not exposed to the client. + * + * There are two modes of operation for the encoder namely: + * - Asynchronous mode + * - Synchronous mode + * + * The client can select asynchronous or synchronous mode by setting the \p + * enableEncodeAsync field in ::_NV_ENC_INITIALIZE_PARAMS to 1 or 0 respectively. + *\par Asynchronous mode of operation: + * The Asynchronous mode can be enabled by setting NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync to 1. + * The client operating in asynchronous mode must allocate completion event object + * for each output buffer and pass the completion event object in the + * ::NvEncEncodePicture() API. The client can create another thread and wait on + * the event object to be signaled by NvEncodeAPI interface on completion of the + * encoding process for the output frame. This should unblock the main thread from + * submitting work to the encoder. When the event is signaled the client can call + * NvEncodeAPI interfaces to copy the bitstream data using ::NvEncLockBitstream() + * API. This is the preferred mode of operation. + * + * NOTE: Asynchronous mode is not supported on Linux. + * + *\par Synchronous mode of operation: + * The client can select synchronous mode by setting NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync to 0. + * The client working in synchronous mode can work in a single threaded or multi + * threaded mode. The client need not allocate any event objects. The client can + * only lock the bitstream data after NvEncodeAPI interface has returned + * ::NV_ENC_SUCCESS from encode picture. The NvEncodeAPI interface can return + * ::NV_ENC_ERR_NEED_MORE_INPUT error code from ::NvEncEncodePicture() API. The + * client must not lock the output buffer in such case but should send the next + * frame for encoding. The client must keep on calling ::NvEncEncodePicture() API + * until it returns ::NV_ENC_SUCCESS. \n + * The client must always lock the bitstream data in order in which it has submitted. + * This is true for both asynchronous and synchronous mode. + * + *\par Picture type decision: + * If the client is taking the picture type decision and it must disable the picture + * type decision module in NvEncodeAPI by setting NV_ENC_INITIALIZE_PARAMS::enablePTD + * to 0. In this case the client is required to send the picture in encoding + * order to NvEncodeAPI by doing the re-ordering for B frames. \n + * If the client doesn't want to take the picture type decision it can enable + * picture type decision module in the NvEncodeAPI interface by setting + * NV_ENC_INITIALIZE_PARAMS::enablePTD to 1 and send the input pictures in display + * order. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] createEncodeParams + * Refer ::_NV_ENC_INITIALIZE_PARAMS for details. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncInitializeEncoder (void* encoder, NV_ENC_INITIALIZE_PARAMS* createEncodeParams); + + +// NvEncCreateInputBuffer +/** + * \brief Allocates Input buffer. + * + * This function is used to allocate an input buffer. The client must enumerate + * the input buffer format before allocating the input buffer resources. The + * NV_ENC_INPUT_PTR returned by the NvEncodeAPI interface in the + * NV_ENC_CREATE_INPUT_BUFFER::inputBuffer field can be directly used in + * ::NvEncEncodePicture() API. The number of input buffers to be allocated by the + * client must be at least 4 more than the number of B frames being used for encoding. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] createInputBufferParams + * Pointer to the ::NV_ENC_CREATE_INPUT_BUFFER structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncCreateInputBuffer (void* encoder, NV_ENC_CREATE_INPUT_BUFFER* createInputBufferParams); + + +// NvEncDestroyInputBuffer +/** + * \brief Release an input buffers. + * + * This function is used to free an input buffer. If the client has allocated + * any input buffer using ::NvEncCreateInputBuffer() API, it must free those + * input buffers by calling this function. The client must release the input + * buffers before destroying the encoder using ::NvEncDestroyEncoder() API. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] inputBuffer + * Pointer to the input buffer to be released. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncDestroyInputBuffer (void* encoder, NV_ENC_INPUT_PTR inputBuffer); + +// NvEncSetIOCudaStreams +/** + * \brief Set input and output CUDA stream for specified encoder attribute. + * + * Encoding may involve CUDA pre-processing on the input and post-processing on encoded output. + * This function is used to set input and output CUDA streams to pipeline the CUDA pre-processing + * and post-processing tasks. Clients should call this function before the call to + * NvEncUnlockInputBuffer(). If this function is not called, the default CUDA stream is used for + * input and output processing. After a successful call to this function, the streams specified + * in that call will replace the previously-used streams. + * This API is supported for NVCUVID interface only. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] inputStream + * Pointer to CUstream which is used to process ::NV_ENC_PIC_PARAMS::inputFrame for encode. + * In case of ME-only mode, inputStream is used to process ::NV_ENC_MEONLY_PARAMS::inputBuffer and + * ::NV_ENC_MEONLY_PARAMS::referenceFrame + * \param [in] outputStream + * Pointer to CUstream which is used to process ::NV_ENC_PIC_PARAMS::outputBuffer for encode. + * In case of ME-only mode, outputStream is used to process ::NV_ENC_MEONLY_PARAMS::mvBuffer + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_GENERIC \n + */ +NVENCSTATUS NVENCAPI NvEncSetIOCudaStreams (void* encoder, NV_ENC_CUSTREAM_PTR inputStream, NV_ENC_CUSTREAM_PTR outputStream); + + +// NvEncCreateBitstreamBuffer +/** + * \brief Allocates an output bitstream buffer + * + * This function is used to allocate an output bitstream buffer and returns a + * NV_ENC_OUTPUT_PTR to bitstream buffer to the client in the + * NV_ENC_CREATE_BITSTREAM_BUFFER::bitstreamBuffer field. + * The client can only call this function after the encoder session has been + * initialized using ::NvEncInitializeEncoder() API. The minimum number of output + * buffers allocated by the client must be at least 4 more than the number of B + * B frames being used for encoding. The client can only access the output + * bitstream data by locking the \p bitstreamBuffer using the ::NvEncLockBitstream() + * function. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] createBitstreamBufferParams + * Pointer ::NV_ENC_CREATE_BITSTREAM_BUFFER for details. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncCreateBitstreamBuffer (void* encoder, NV_ENC_CREATE_BITSTREAM_BUFFER* createBitstreamBufferParams); + + +// NvEncDestroyBitstreamBuffer +/** + * \brief Release a bitstream buffer. + * + * This function is used to release the output bitstream buffer allocated using + * the ::NvEncCreateBitstreamBuffer() function. The client must release the output + * bitstreamBuffer using this function before destroying the encoder session. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] bitstreamBuffer + * Pointer to the bitstream buffer being released. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncDestroyBitstreamBuffer (void* encoder, NV_ENC_OUTPUT_PTR bitstreamBuffer); + +// NvEncEncodePicture +/** + * \brief Submit an input picture for encoding. + * + * This function is used to submit an input picture buffer for encoding. The + * encoding parameters are passed using \p *encodePicParams which is a pointer + * to the ::_NV_ENC_PIC_PARAMS structure. + * + * If the client has set NV_ENC_INITIALIZE_PARAMS::enablePTD to 0, then it must + * send a valid value for the following fields. + * - NV_ENC_PIC_PARAMS::pictureType + * - NV_ENC_PIC_PARAMS_H264::displayPOCSyntax (H264 only) + * - NV_ENC_PIC_PARAMS_H264::frameNumSyntax(H264 only) + * - NV_ENC_PIC_PARAMS_H264::refPicFlag(H264 only) + * + *\par MVC Encoding: + * For MVC encoding the client must call encode picture API for each view separately + * and must pass valid view id in NV_ENC_PIC_PARAMS_MVC::viewID field. Currently + * NvEncodeAPI only support stereo MVC so client must send viewID as 0 for base + * view and view ID as 1 for dependent view. + * + *\par Asynchronous Encoding + * If the client has enabled asynchronous mode of encoding by setting + * NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync to 1 in the ::NvEncInitializeEncoder() + * API ,then the client must send a valid NV_ENC_PIC_PARAMS::completionEvent. + * Incase of asynchronous mode of operation, client can queue the ::NvEncEncodePicture() + * API commands from the main thread and then queue output buffers to be processed + * to a secondary worker thread. Before the locking the output buffers in the + * secondary thread , the client must wait on NV_ENC_PIC_PARAMS::completionEvent + * it has queued in ::NvEncEncodePicture() API call. The client must always process + * completion event and the output buffer in the same order in which they have been + * submitted for encoding. The NvEncodeAPI interface is responsible for any + * re-ordering required for B frames and will always ensure that encoded bitstream + * data is written in the same order in which output buffer is submitted. + * The NvEncodeAPI interface may return ::NV_ENC_ERR_NEED_MORE_INPUT error code for + * some ::NvEncEncodePicture() API calls but the client must not treat it as a fatal error. + * The NvEncodeAPI interface might not be able to submit an input picture buffer for encoding + * immediately due to re-ordering for B frames. + *\code + The below example shows how asynchronous encoding in case of 1 B frames + ------------------------------------------------------------------------ + Suppose the client allocated 4 input buffers(I1,I2..), 4 output buffers(O1,O2..) + and 4 completion events(E1, E2, ...). The NvEncodeAPI interface will need to + keep a copy of the input buffers for re-ordering and it allocates following + internal buffers (NvI1, NvI2...). These internal buffers are managed by NvEncodeAPI + and the client is not responsible for the allocating or freeing the memory of + the internal buffers. + + a) The client main thread will queue the following encode frame calls. + Note the picture type is unknown to the client, the decision is being taken by + NvEncodeAPI interface. The client should pass ::_NV_ENC_PIC_PARAMS parameter + consisting of allocated input buffer, output buffer and output events in successive + ::NvEncEncodePicture() API calls along with other required encode picture params. + For example: + 1st EncodePicture parameters - (I1, O1, E1) + 2nd EncodePicture parameters - (I2, O2, E2) + 3rd EncodePicture parameters - (I3, O3, E3) + + b) NvEncodeAPI SW will receive the following encode Commands from the client. + The left side shows input from client in the form (Input buffer, Output Buffer, + Output Event). The right hand side shows a possible picture type decision take by + the NvEncodeAPI interface. + (I1, O1, E1) ---P1 Frame + (I2, O2, E2) ---B2 Frame + (I3, O3, E3) ---P3 Frame + + c) NvEncodeAPI interface will make a copy of the input buffers to its internal + buffers for re-ordering. These copies are done as part of nvEncEncodePicture + function call from the client and NvEncodeAPI interface is responsible for + synchronization of copy operation with the actual encoding operation. + I1 --> NvI1 + I2 --> NvI2 + I3 --> NvI3 + + d) The NvEncodeAPI encodes I1 as P frame and submits I1 to encoder HW and returns ::NV_ENC_SUCCESS. + The NvEncodeAPI tries to encode I2 as B frame and fails with ::NV_ENC_ERR_NEED_MORE_INPUT error code. + The error is not fatal and it notifies client that I2 is not submitted to encoder immediately. + The NvEncodeAPI encodes I3 as P frame and submits I3 for encoding which will be used as backward + reference frame for I2. The NvEncodeAPI then submits I2 for encoding and returns ::NV_ENC_SUCESS. + Both the submission are part of the same ::NvEncEncodePicture() function call. + + e) After returning from ::NvEncEncodePicture() call , the client must queue the output + bitstream processing work to the secondary thread. The output bitstream processing + for asynchronous mode consist of first waiting on completion event(E1, E2..) + and then locking the output bitstream buffer(O1, O2..) for reading the encoded + data. The work queued to the secondary thread by the client is in the following order + (I1, O1, E1) + (I2, O2, E2) + (I3, O3, E3) + Note they are in the same order in which client calls ::NvEncEncodePicture() API + in \p step a). + + f) NvEncodeAPI interface will do the re-ordering such that Encoder HW will receive + the following encode commands: + (NvI1, O1, E1) ---P1 Frame + (NvI3, O2, E2) ---P3 Frame + (NvI2, O3, E3) ---B2 frame + + g) After the encoding operations are completed, the events will be signaled + by NvEncodeAPI interface in the following order : + (O1, E1) ---P1 Frame ,output bitstream copied to O1 and event E1 signaled. + (O2, E2) ---P3 Frame ,output bitstream copied to O2 and event E2 signaled. + (O3, E3) ---B2 Frame ,output bitstream copied to O3 and event E3 signaled. + + h) The client must lock the bitstream data using ::NvEncLockBitstream() API in + the order O1,O2,O3 to read the encoded data, after waiting for the events + to be signaled in the same order i.e E1, E2 and E3.The output processing is + done in the secondary thread in the following order: + Waits on E1, copies encoded bitstream from O1 + Waits on E2, copies encoded bitstream from O2 + Waits on E3, copies encoded bitstream from O3 + + -Note the client will receive the events signaling and output buffer in the + same order in which they have submitted for encoding. + -Note the LockBitstream will have picture type field which will notify the + output picture type to the clients. + -Note the input, output buffer and the output completion event are free to be + reused once NvEncodeAPI interfaced has signaled the event and the client has + copied the data from the output buffer. + + * \endcode + * + *\par Synchronous Encoding + * The client can enable synchronous mode of encoding by setting + * NV_ENC_INITIALIZE_PARAMS::enableEncodeAsync to 0 in ::NvEncInitializeEncoder() API. + * The NvEncodeAPI interface may return ::NV_ENC_ERR_NEED_MORE_INPUT error code for + * some ::NvEncEncodePicture() API calls when NV_ENC_INITIALIZE_PARAMS::enablePTD + * is set to 1, but the client must not treat it as a fatal error. The NvEncodeAPI + * interface might not be able to submit an input picture buffer for encoding + * immediately due to re-ordering for B frames. The NvEncodeAPI interface cannot + * submit the input picture which is decided to be encoded as B frame as it waits + * for backward reference from temporally subsequent frames. This input picture + * is buffered internally and waits for more input picture to arrive. The client + * must not call ::NvEncLockBitstream() API on the output buffers whose + * ::NvEncEncodePicture() API returns ::NV_ENC_ERR_NEED_MORE_INPUT. The client must + * wait for the NvEncodeAPI interface to return ::NV_ENC_SUCCESS before locking the + * output bitstreams to read the encoded bitstream data. The following example + * explains the scenario with synchronous encoding with 2 B frames. + *\code + The below example shows how synchronous encoding works in case of 1 B frames + ----------------------------------------------------------------------------- + Suppose the client allocated 4 input buffers(I1,I2..), 4 output buffers(O1,O2..) + and 4 completion events(E1, E2, ...). The NvEncodeAPI interface will need to + keep a copy of the input buffers for re-ordering and it allocates following + internal buffers (NvI1, NvI2...). These internal buffers are managed by NvEncodeAPI + and the client is not responsible for the allocating or freeing the memory of + the internal buffers. + + The client calls ::NvEncEncodePicture() API with input buffer I1 and output buffer O1. + The NvEncodeAPI decides to encode I1 as P frame and submits it to encoder + HW and returns ::NV_ENC_SUCCESS. + The client can now read the encoded data by locking the output O1 by calling + NvEncLockBitstream API. + + The client calls ::NvEncEncodePicture() API with input buffer I2 and output buffer O2. + The NvEncodeAPI decides to encode I2 as B frame and buffers I2 by copying it + to internal buffer and returns ::NV_ENC_ERR_NEED_MORE_INPUT. + The error is not fatal and it notifies client that it cannot read the encoded + data by locking the output O2 by calling ::NvEncLockBitstream() API without submitting + more work to the NvEncodeAPI interface. + + The client calls ::NvEncEncodePicture() with input buffer I3 and output buffer O3. + The NvEncodeAPI decides to encode I3 as P frame and it first submits I3 for + encoding which will be used as backward reference frame for I2. + The NvEncodeAPI then submits I2 for encoding and returns ::NV_ENC_SUCESS. Both + the submission are part of the same ::NvEncEncodePicture() function call. + The client can now read the encoded data for both the frames by locking the output + O2 followed by O3 ,by calling ::NvEncLockBitstream() API. + + The client must always lock the output in the same order in which it has submitted + to receive the encoded bitstream in correct encoding order. + + * \endcode + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] encodePicParams + * Pointer to the ::_NV_ENC_PIC_PARAMS structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_ENCODER_BUSY \n + * ::NV_ENC_ERR_NEED_MORE_INPUT \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncEncodePicture (void* encoder, NV_ENC_PIC_PARAMS* encodePicParams); + + +// NvEncLockBitstream +/** + * \brief Lock output bitstream buffer + * + * This function is used to lock the bitstream buffer to read the encoded data. + * The client can only access the encoded data by calling this function. + * The pointer to client accessible encoded data is returned in the + * NV_ENC_LOCK_BITSTREAM::bitstreamBufferPtr field. The size of the encoded data + * in the output buffer is returned in the NV_ENC_LOCK_BITSTREAM::bitstreamSizeInBytes + * The NvEncodeAPI interface also returns the output picture type and picture structure + * of the encoded frame in NV_ENC_LOCK_BITSTREAM::pictureType and + * NV_ENC_LOCK_BITSTREAM::pictureStruct fields respectively. If the client has + * set NV_ENC_LOCK_BITSTREAM::doNotWait to 1, the function might return + * ::NV_ENC_ERR_LOCK_BUSY if client is operating in synchronous mode. This is not + * a fatal failure if NV_ENC_LOCK_BITSTREAM::doNotWait is set to 1. In the above case the client can + * retry the function after few milliseconds. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] lockBitstreamBufferParams + * Pointer to the ::_NV_ENC_LOCK_BITSTREAM structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_LOCK_BUSY \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncLockBitstream (void* encoder, NV_ENC_LOCK_BITSTREAM* lockBitstreamBufferParams); + + +// NvEncUnlockBitstream +/** + * \brief Unlock the output bitstream buffer + * + * This function is used to unlock the output bitstream buffer after the client + * has read the encoded data from output buffer. The client must call this function + * to unlock the output buffer which it has previously locked using ::NvEncLockBitstream() + * function. Using a locked bitstream buffer in ::NvEncEncodePicture() API will cause + * the function to fail. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] bitstreamBuffer + * bitstream buffer pointer being unlocked + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncUnlockBitstream (void* encoder, NV_ENC_OUTPUT_PTR bitstreamBuffer); + + +// NvLockInputBuffer +/** + * \brief Locks an input buffer + * + * This function is used to lock the input buffer to load the uncompressed YUV + * pixel data into input buffer memory. The client must pass the NV_ENC_INPUT_PTR + * it had previously allocated using ::NvEncCreateInputBuffer()in the + * NV_ENC_LOCK_INPUT_BUFFER::inputBuffer field. + * The NvEncodeAPI interface returns pointer to client accessible input buffer + * memory in NV_ENC_LOCK_INPUT_BUFFER::bufferDataPtr field. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] lockInputBufferParams + * Pointer to the ::_NV_ENC_LOCK_INPUT_BUFFER structure + * + * \return + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_LOCK_BUSY \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncLockInputBuffer (void* encoder, NV_ENC_LOCK_INPUT_BUFFER* lockInputBufferParams); + + +// NvUnlockInputBuffer +/** + * \brief Unlocks the input buffer + * + * This function is used to unlock the input buffer memory previously locked for + * uploading YUV pixel data. The input buffer must be unlocked before being used + * again for encoding, otherwise NvEncodeAPI will fail the ::NvEncEncodePicture() + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] inputBuffer + * Pointer to the input buffer that is being unlocked. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + * + */ +NVENCSTATUS NVENCAPI NvEncUnlockInputBuffer (void* encoder, NV_ENC_INPUT_PTR inputBuffer); + + +// NvEncGetEncodeStats +/** + * \brief Get encoding statistics. + * + * This function is used to retrieve the encoding statistics. + * This API is not supported when encode device type is CUDA. + * Note that this API will be removed in future Video Codec SDK release. + * Clients should use NvEncLockBitstream() API to retrieve the encoding statistics. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] encodeStats + * Pointer to the ::_NV_ENC_STAT structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetEncodeStats (void* encoder, NV_ENC_STAT* encodeStats); + + +// NvEncGetSequenceParams +/** + * \brief Get encoded sequence and picture header. + * + * This function can be used to retrieve the sequence and picture header out of + * band. The client must call this function only after the encoder has been + * initialized using ::NvEncInitializeEncoder() function. The client must + * allocate the memory where the NvEncodeAPI interface can copy the bitstream + * header and pass the pointer to the memory in NV_ENC_SEQUENCE_PARAM_PAYLOAD::spsppsBuffer. + * The size of buffer is passed in the field NV_ENC_SEQUENCE_PARAM_PAYLOAD::inBufferSize. + * The NvEncodeAPI interface will copy the bitstream header payload and returns + * the actual size of the bitstream header in the field + * NV_ENC_SEQUENCE_PARAM_PAYLOAD::outSPSPPSPayloadSize. + * The client must call ::NvEncGetSequenceParams() function from the same thread which is + * being used to call ::NvEncEncodePicture() function. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] sequenceParamPayload + * Pointer to the ::_NV_ENC_SEQUENCE_PARAM_PAYLOAD structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetSequenceParams (void* encoder, NV_ENC_SEQUENCE_PARAM_PAYLOAD* sequenceParamPayload); + +// NvEncGetSequenceParamEx +/** + * \brief Get sequence and picture header. + * + * This function can be used to retrieve the sequence and picture header out of band, even when + * encoder has not been initialized using ::NvEncInitializeEncoder() function. + * The client must allocate the memory where the NvEncodeAPI interface can copy the bitstream + * header and pass the pointer to the memory in NV_ENC_SEQUENCE_PARAM_PAYLOAD::spsppsBuffer. + * The size of buffer is passed in the field NV_ENC_SEQUENCE_PARAM_PAYLOAD::inBufferSize. + * If encoder has not been initialized using ::NvEncInitializeEncoder() function, client must + * send NV_ENC_INITIALIZE_PARAMS as input. The NV_ENC_INITIALIZE_PARAMS passed must be same as the + * one which will be used for initializing encoder using ::NvEncInitializeEncoder() function later. + * If encoder is already initialized using ::NvEncInitializeEncoder() function, the provided + * NV_ENC_INITIALIZE_PARAMS structure is ignored. The NvEncodeAPI interface will copy the bitstream + * header payload and returns the actual size of the bitstream header in the field + * NV_ENC_SEQUENCE_PARAM_PAYLOAD::outSPSPPSPayloadSize. The client must call ::NvEncGetSequenceParamsEx() + * function from the same thread which is being used to call ::NvEncEncodePicture() function. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] encInitParams + * Pointer to the _NV_ENC_INITIALIZE_PARAMS structure. + * \param [in,out] sequenceParamPayload + * Pointer to the ::_NV_ENC_SEQUENCE_PARAM_PAYLOAD structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncGetSequenceParamEx (void* encoder, NV_ENC_INITIALIZE_PARAMS* encInitParams, NV_ENC_SEQUENCE_PARAM_PAYLOAD* sequenceParamPayload); + +// NvEncRegisterAsyncEvent +/** + * \brief Register event for notification to encoding completion. + * + * This function is used to register the completion event with NvEncodeAPI + * interface. The event is required when the client has configured the encoder to + * work in asynchronous mode. In this mode the client needs to send a completion + * event with every output buffer. The NvEncodeAPI interface will signal the + * completion of the encoding process using this event. Only after the event is + * signaled the client can get the encoded data using ::NvEncLockBitstream() function. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] eventParams + * Pointer to the ::_NV_ENC_EVENT_PARAMS structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncRegisterAsyncEvent (void* encoder, NV_ENC_EVENT_PARAMS* eventParams); + + +// NvEncUnregisterAsyncEvent +/** + * \brief Unregister completion event. + * + * This function is used to unregister completion event which has been previously + * registered using ::NvEncRegisterAsyncEvent() function. The client must unregister + * all events before destroying the encoder using ::NvEncDestroyEncoder() function. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] eventParams + * Pointer to the ::_NV_ENC_EVENT_PARAMS structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncUnregisterAsyncEvent (void* encoder, NV_ENC_EVENT_PARAMS* eventParams); + + +// NvEncMapInputResource +/** + * \brief Map an externally created input resource pointer for encoding. + * + * Maps an externally allocated input resource [using and returns a NV_ENC_INPUT_PTR + * which can be used for encoding in the ::NvEncEncodePicture() function. The + * mapped resource is returned in the field NV_ENC_MAP_INPUT_RESOURCE::outputResourcePtr. + * The NvEncodeAPI interface also returns the buffer format of the mapped resource + * in the field NV_ENC_MAP_INPUT_RESOURCE::outbufferFmt. + * This function provides synchronization guarantee that any graphics work submitted + * on the input buffer is completed before the buffer is used for encoding. This is + * also true for compute (i.e. CUDA) work, provided that the previous workload using + * the input resource was submitted to the default stream. + * The client should not access any input buffer while they are mapped by the encoder. + * For D3D12 interface type, this function does not provide synchronization guarantee. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] mapInputResParams + * Pointer to the ::_NV_ENC_MAP_INPUT_RESOURCE structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_RESOURCE_NOT_REGISTERED \n + * ::NV_ENC_ERR_MAP_FAILED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncMapInputResource (void* encoder, NV_ENC_MAP_INPUT_RESOURCE* mapInputResParams); + + +// NvEncUnmapInputResource +/** + * \brief UnMaps a NV_ENC_INPUT_PTR which was mapped for encoding + * + * + * UnMaps an input buffer which was previously mapped using ::NvEncMapInputResource() + * API. The mapping created using ::NvEncMapInputResource() should be invalidated + * using this API before the external resource is destroyed by the client. The client + * must unmap the buffer after ::NvEncLockBitstream() API returns successfully for encode + * work submitted using the mapped input buffer. + * + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] mappedInputBuffer + * Pointer to the NV_ENC_INPUT_PTR + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_RESOURCE_NOT_REGISTERED \n + * ::NV_ENC_ERR_RESOURCE_NOT_MAPPED \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncUnmapInputResource (void* encoder, NV_ENC_INPUT_PTR mappedInputBuffer); + +// NvEncDestroyEncoder +/** + * \brief Destroy Encoding Session + * + * Destroys the encoder session previously created using ::NvEncOpenEncodeSession() + * function. The client must flush the encoder before freeing any resources. In order + * to flush the encoder the client must pass a NULL encode picture packet and either + * wait for the ::NvEncEncodePicture() function to return in synchronous mode or wait + * for the flush event to be signaled by the encoder in asynchronous mode. + * The client must free all the input and output resources created using the + * NvEncodeAPI interface before destroying the encoder. If the client is operating + * in asynchronous mode, it must also unregister the completion events previously + * registered. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncDestroyEncoder (void* encoder); + +// NvEncInvalidateRefFrames +/** + * \brief Invalidate reference frames + * + * Invalidates reference frame based on the time stamp provided by the client. + * The encoder marks any reference frames or any frames which have been reconstructed + * using the corrupt frame as invalid for motion estimation and uses older reference + * frames for motion estimation. The encoder forces the current frame to be encoded + * as an intra frame if no reference frames are left after invalidation process. + * This is useful for low latency application for error resiliency. The client + * is recommended to set NV_ENC_CONFIG_H264::maxNumRefFrames to a large value so + * that encoder can keep a backup of older reference frames in the DPB and can use them + * for motion estimation when the newer reference frames have been invalidated. + * This API can be called multiple times. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] invalidRefFrameTimeStamp + * Timestamp of the invalid reference frames which needs to be invalidated. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncInvalidateRefFrames(void* encoder, uint64_t invalidRefFrameTimeStamp); + +// NvEncOpenEncodeSessionEx +/** + * \brief Opens an encoding session. + * + * Opens an encoding session and returns a pointer to the encoder interface in + * the \p **encoder parameter. The client should start encoding process by calling + * this API first. + * The client must pass a pointer to IDirect3DDevice9 device or CUDA context in the \p *device parameter. + * For the OpenGL interface, \p device must be NULL. An OpenGL context must be current when + * calling all NvEncodeAPI functions. + * If the creation of encoder session fails, the client must call ::NvEncDestroyEncoder API + * before exiting. + * + * \param [in] openSessionExParams + * Pointer to a ::NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS structure. + * \param [out] encoder + * Encode Session pointer to the NvEncodeAPI interface. + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_NO_ENCODE_DEVICE \n + * ::NV_ENC_ERR_UNSUPPORTED_DEVICE \n + * ::NV_ENC_ERR_INVALID_DEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncOpenEncodeSessionEx (NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS *openSessionExParams, void** encoder); + +// NvEncRegisterResource +/** + * \brief Registers a resource with the Nvidia Video Encoder Interface. + * + * Registers a resource with the Nvidia Video Encoder Interface for book keeping. + * The client is expected to pass the registered resource handle as well, while calling ::NvEncMapInputResource API. + * + * \param [in] encoder + * Pointer to the NVEncodeAPI interface. + * + * \param [in] registerResParams + * Pointer to a ::_NV_ENC_REGISTER_RESOURCE structure + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_RESOURCE_REGISTER_FAILED \n + * ::NV_ENC_ERR_GENERIC \n + * ::NV_ENC_ERR_UNIMPLEMENTED \n + * + */ +NVENCSTATUS NVENCAPI NvEncRegisterResource (void* encoder, NV_ENC_REGISTER_RESOURCE* registerResParams); + +// NvEncUnregisterResource +/** + * \brief Unregisters a resource previously registered with the Nvidia Video Encoder Interface. + * + * Unregisters a resource previously registered with the Nvidia Video Encoder Interface. + * The client is expected to unregister any resource that it has registered with the + * Nvidia Video Encoder Interface before destroying the resource. + * + * \param [in] encoder + * Pointer to the NVEncodeAPI interface. + * + * \param [in] registeredResource + * The registered resource pointer that was returned in ::NvEncRegisterResource. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_RESOURCE_NOT_REGISTERED \n + * ::NV_ENC_ERR_GENERIC \n + * ::NV_ENC_ERR_UNIMPLEMENTED \n + * + */ +NVENCSTATUS NVENCAPI NvEncUnregisterResource (void* encoder, NV_ENC_REGISTERED_PTR registeredResource); + +// NvEncReconfigureEncoder +/** + * \brief Reconfigure an existing encoding session. + * + * Reconfigure an existing encoding session. + * The client should call this API to change/reconfigure the parameter passed during + * NvEncInitializeEncoder API call. + * Currently Reconfiguration of following are not supported. + * Change in GOP structure. + * Change in sync-Async mode. + * Change in MaxWidth & MaxHeight. + * Change in PTD mode. + * + * Resolution change is possible only if maxEncodeWidth & maxEncodeHeight of NV_ENC_INITIALIZE_PARAMS + * is set while creating encoder session. + * + * \param [in] encoder + * Pointer to the NVEncodeAPI interface. + * + * \param [in] reInitEncodeParams + * Pointer to a ::NV_ENC_RECONFIGURE_PARAMS structure. + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_NO_ENCODE_DEVICE \n + * ::NV_ENC_ERR_UNSUPPORTED_DEVICE \n + * ::NV_ENC_ERR_INVALID_DEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_GENERIC \n + * + */ +NVENCSTATUS NVENCAPI NvEncReconfigureEncoder (void *encoder, NV_ENC_RECONFIGURE_PARAMS* reInitEncodeParams); + + + +// NvEncCreateMVBuffer +/** + * \brief Allocates output MV buffer for ME only mode. + * + * This function is used to allocate an output MV buffer. The size of the mvBuffer is + * dependent on the frame height and width of the last ::NvEncCreateInputBuffer() call. + * The NV_ENC_OUTPUT_PTR returned by the NvEncodeAPI interface in the + * ::NV_ENC_CREATE_MV_BUFFER::mvBuffer field should be used in + * ::NvEncRunMotionEstimationOnly() API. + * Client must lock ::NV_ENC_CREATE_MV_BUFFER::mvBuffer using ::NvEncLockBitstream() API to get the motion vector data. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in,out] createMVBufferParams + * Pointer to the ::NV_ENC_CREATE_MV_BUFFER structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_GENERIC \n + */ +NVENCSTATUS NVENCAPI NvEncCreateMVBuffer (void* encoder, NV_ENC_CREATE_MV_BUFFER* createMVBufferParams); + + +// NvEncDestroyMVBuffer +/** + * \brief Release an output MV buffer for ME only mode. + * + * This function is used to release the output MV buffer allocated using + * the ::NvEncCreateMVBuffer() function. The client must release the output + * mvBuffer using this function before destroying the encoder session. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] mvBuffer + * Pointer to the mvBuffer being released. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + */ +NVENCSTATUS NVENCAPI NvEncDestroyMVBuffer (void* encoder, NV_ENC_OUTPUT_PTR mvBuffer); + + +// NvEncRunMotionEstimationOnly +/** + * \brief Submit an input picture and reference frame for motion estimation in ME only mode. + * + * This function is used to submit the input frame and reference frame for motion + * estimation. The ME parameters are passed using *meOnlyParams which is a pointer + * to ::_NV_ENC_MEONLY_PARAMS structure. + * Client must lock ::NV_ENC_CREATE_MV_BUFFER::mvBuffer using ::NvEncLockBitstream() API to get the motion vector data. + * to get motion vector data. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * \param [in] meOnlyParams + * Pointer to the ::_NV_ENC_MEONLY_PARAMS structure. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + * ::NV_ENC_ERR_INVALID_ENCODERDEVICE \n + * ::NV_ENC_ERR_DEVICE_NOT_EXIST \n + * ::NV_ENC_ERR_UNSUPPORTED_PARAM \n + * ::NV_ENC_ERR_OUT_OF_MEMORY \n + * ::NV_ENC_ERR_INVALID_PARAM \n + * ::NV_ENC_ERR_INVALID_VERSION \n + * ::NV_ENC_ERR_NEED_MORE_INPUT \n + * ::NV_ENC_ERR_ENCODER_NOT_INITIALIZED \n + * ::NV_ENC_ERR_GENERIC \n + */ +NVENCSTATUS NVENCAPI NvEncRunMotionEstimationOnly (void* encoder, NV_ENC_MEONLY_PARAMS* meOnlyParams); + +// NvEncodeAPIGetMaxSupportedVersion +/** + * \brief Get the largest NvEncodeAPI version supported by the driver. + * + * This function can be used by clients to determine if the driver supports + * the NvEncodeAPI header the application was compiled with. + * + * \param [out] version + * Pointer to the requested value. The 4 least significant bits in the returned + * indicate the minor version and the rest of the bits indicate the major + * version of the largest supported version. + * + * \return + * ::NV_ENC_SUCCESS \n + * ::NV_ENC_ERR_INVALID_PTR \n + */ +NVENCSTATUS NVENCAPI NvEncodeAPIGetMaxSupportedVersion (uint32_t* version); + + +// NvEncGetLastErrorString +/** + * \brief Get the description of the last error reported by the API. + * + * This function returns a null-terminated string that can be used by clients to better understand the reason + * for failure of a previous API call. + * + * \param [in] encoder + * Pointer to the NvEncodeAPI interface. + * + * \return + * Pointer to buffer containing the details of the last error encountered by the API. + */ +const char * NVENCAPI NvEncGetLastErrorString (void* encoder); + + +/// \cond API PFN +/* + * Defines API function pointers + */ +typedef NVENCSTATUS (NVENCAPI* PNVENCOPENENCODESESSION) (void* device, uint32_t deviceType, void** encoder); +typedef NVENCSTATUS (NVENCAPI* PNVENCGETENCODEGUIDCOUNT) (void* encoder, uint32_t* encodeGUIDCount); +typedef NVENCSTATUS (NVENCAPI* PNVENCGETENCODEGUIDS) (void* encoder, GUID* GUIDs, uint32_t guidArraySize, uint32_t* GUIDCount); +typedef NVENCSTATUS (NVENCAPI* PNVENCGETENCODEPROFILEGUIDCOUNT) (void* encoder, GUID encodeGUID, uint32_t* encodeProfileGUIDCount); +typedef NVENCSTATUS (NVENCAPI* PNVENCGETENCODEPROFILEGUIDS) (void* encoder, GUID encodeGUID, GUID* profileGUIDs, uint32_t guidArraySize, uint32_t* GUIDCount); +typedef NVENCSTATUS (NVENCAPI* PNVENCGETINPUTFORMATCOUNT) (void* encoder, GUID encodeGUID, uint32_t* inputFmtCount); +typedef NVENCSTATUS (NVENCAPI* PNVENCGETINPUTFORMATS) (void* encoder, GUID encodeGUID, NV_ENC_BUFFER_FORMAT* inputFmts, uint32_t inputFmtArraySize, uint32_t* inputFmtCount); +typedef NVENCSTATUS (NVENCAPI* PNVENCGETENCODECAPS) (void* encoder, GUID encodeGUID, NV_ENC_CAPS_PARAM* capsParam, int* capsVal); +typedef NVENCSTATUS (NVENCAPI* PNVENCGETENCODEPRESETCOUNT) (void* encoder, GUID encodeGUID, uint32_t* encodePresetGUIDCount); +typedef NVENCSTATUS (NVENCAPI* PNVENCGETENCODEPRESETGUIDS) (void* encoder, GUID encodeGUID, GUID* presetGUIDs, uint32_t guidArraySize, uint32_t* encodePresetGUIDCount); +typedef NVENCSTATUS (NVENCAPI* PNVENCGETENCODEPRESETCONFIG) (void* encoder, GUID encodeGUID, GUID presetGUID, NV_ENC_PRESET_CONFIG* presetConfig); +typedef NVENCSTATUS (NVENCAPI* PNVENCGETENCODEPRESETCONFIGEX) (void* encoder, GUID encodeGUID, GUID presetGUID, NV_ENC_TUNING_INFO tuningInfo, NV_ENC_PRESET_CONFIG* presetConfig); +typedef NVENCSTATUS (NVENCAPI* PNVENCINITIALIZEENCODER) (void* encoder, NV_ENC_INITIALIZE_PARAMS* createEncodeParams); +typedef NVENCSTATUS (NVENCAPI* PNVENCCREATEINPUTBUFFER) (void* encoder, NV_ENC_CREATE_INPUT_BUFFER* createInputBufferParams); +typedef NVENCSTATUS (NVENCAPI* PNVENCDESTROYINPUTBUFFER) (void* encoder, NV_ENC_INPUT_PTR inputBuffer); +typedef NVENCSTATUS (NVENCAPI* PNVENCCREATEBITSTREAMBUFFER) (void* encoder, NV_ENC_CREATE_BITSTREAM_BUFFER* createBitstreamBufferParams); +typedef NVENCSTATUS (NVENCAPI* PNVENCDESTROYBITSTREAMBUFFER) (void* encoder, NV_ENC_OUTPUT_PTR bitstreamBuffer); +typedef NVENCSTATUS (NVENCAPI* PNVENCENCODEPICTURE) (void* encoder, NV_ENC_PIC_PARAMS* encodePicParams); +typedef NVENCSTATUS (NVENCAPI* PNVENCLOCKBITSTREAM) (void* encoder, NV_ENC_LOCK_BITSTREAM* lockBitstreamBufferParams); +typedef NVENCSTATUS (NVENCAPI* PNVENCUNLOCKBITSTREAM) (void* encoder, NV_ENC_OUTPUT_PTR bitstreamBuffer); +typedef NVENCSTATUS (NVENCAPI* PNVENCLOCKINPUTBUFFER) (void* encoder, NV_ENC_LOCK_INPUT_BUFFER* lockInputBufferParams); +typedef NVENCSTATUS (NVENCAPI* PNVENCUNLOCKINPUTBUFFER) (void* encoder, NV_ENC_INPUT_PTR inputBuffer); +typedef NVENCSTATUS (NVENCAPI* PNVENCGETENCODESTATS) (void* encoder, NV_ENC_STAT* encodeStats); +typedef NVENCSTATUS (NVENCAPI* PNVENCGETSEQUENCEPARAMS) (void* encoder, NV_ENC_SEQUENCE_PARAM_PAYLOAD* sequenceParamPayload); +typedef NVENCSTATUS (NVENCAPI* PNVENCREGISTERASYNCEVENT) (void* encoder, NV_ENC_EVENT_PARAMS* eventParams); +typedef NVENCSTATUS (NVENCAPI* PNVENCUNREGISTERASYNCEVENT) (void* encoder, NV_ENC_EVENT_PARAMS* eventParams); +typedef NVENCSTATUS (NVENCAPI* PNVENCMAPINPUTRESOURCE) (void* encoder, NV_ENC_MAP_INPUT_RESOURCE* mapInputResParams); +typedef NVENCSTATUS (NVENCAPI* PNVENCUNMAPINPUTRESOURCE) (void* encoder, NV_ENC_INPUT_PTR mappedInputBuffer); +typedef NVENCSTATUS (NVENCAPI* PNVENCDESTROYENCODER) (void* encoder); +typedef NVENCSTATUS (NVENCAPI* PNVENCINVALIDATEREFFRAMES) (void* encoder, uint64_t invalidRefFrameTimeStamp); +typedef NVENCSTATUS (NVENCAPI* PNVENCOPENENCODESESSIONEX) (NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS *openSessionExParams, void** encoder); +typedef NVENCSTATUS (NVENCAPI* PNVENCREGISTERRESOURCE) (void* encoder, NV_ENC_REGISTER_RESOURCE* registerResParams); +typedef NVENCSTATUS (NVENCAPI* PNVENCUNREGISTERRESOURCE) (void* encoder, NV_ENC_REGISTERED_PTR registeredRes); +typedef NVENCSTATUS (NVENCAPI* PNVENCRECONFIGUREENCODER) (void* encoder, NV_ENC_RECONFIGURE_PARAMS* reInitEncodeParams); + +typedef NVENCSTATUS (NVENCAPI* PNVENCCREATEMVBUFFER) (void* encoder, NV_ENC_CREATE_MV_BUFFER* createMVBufferParams); +typedef NVENCSTATUS (NVENCAPI* PNVENCDESTROYMVBUFFER) (void* encoder, NV_ENC_OUTPUT_PTR mvBuffer); +typedef NVENCSTATUS (NVENCAPI* PNVENCRUNMOTIONESTIMATIONONLY) (void* encoder, NV_ENC_MEONLY_PARAMS* meOnlyParams); +typedef const char * (NVENCAPI* PNVENCGETLASTERROR) (void* encoder); +typedef NVENCSTATUS (NVENCAPI* PNVENCSETIOCUDASTREAMS) (void* encoder, NV_ENC_CUSTREAM_PTR inputStream, NV_ENC_CUSTREAM_PTR outputStream); +typedef NVENCSTATUS (NVENCAPI* PNVENCGETSEQUENCEPARAMEX) (void* encoder, NV_ENC_INITIALIZE_PARAMS* encInitParams, NV_ENC_SEQUENCE_PARAM_PAYLOAD* sequenceParamPayload); + + +/// \endcond + + +/** @} */ /* END ENCODE_FUNC */ + +/** + * \ingroup ENCODER_STRUCTURE + * NV_ENCODE_API_FUNCTION_LIST + */ +typedef struct _NV_ENCODE_API_FUNCTION_LIST +{ + uint32_t version; /**< [in]: Client should pass NV_ENCODE_API_FUNCTION_LIST_VER. */ + uint32_t reserved; /**< [in]: Reserved and should be set to 0. */ + PNVENCOPENENCODESESSION nvEncOpenEncodeSession; /**< [out]: Client should access ::NvEncOpenEncodeSession() API through this pointer. */ + PNVENCGETENCODEGUIDCOUNT nvEncGetEncodeGUIDCount; /**< [out]: Client should access ::NvEncGetEncodeGUIDCount() API through this pointer. */ + PNVENCGETENCODEPRESETCOUNT nvEncGetEncodeProfileGUIDCount; /**< [out]: Client should access ::NvEncGetEncodeProfileGUIDCount() API through this pointer.*/ + PNVENCGETENCODEPRESETGUIDS nvEncGetEncodeProfileGUIDs; /**< [out]: Client should access ::NvEncGetEncodeProfileGUIDs() API through this pointer. */ + PNVENCGETENCODEGUIDS nvEncGetEncodeGUIDs; /**< [out]: Client should access ::NvEncGetEncodeGUIDs() API through this pointer. */ + PNVENCGETINPUTFORMATCOUNT nvEncGetInputFormatCount; /**< [out]: Client should access ::NvEncGetInputFormatCount() API through this pointer. */ + PNVENCGETINPUTFORMATS nvEncGetInputFormats; /**< [out]: Client should access ::NvEncGetInputFormats() API through this pointer. */ + PNVENCGETENCODECAPS nvEncGetEncodeCaps; /**< [out]: Client should access ::NvEncGetEncodeCaps() API through this pointer. */ + PNVENCGETENCODEPRESETCOUNT nvEncGetEncodePresetCount; /**< [out]: Client should access ::NvEncGetEncodePresetCount() API through this pointer. */ + PNVENCGETENCODEPRESETGUIDS nvEncGetEncodePresetGUIDs; /**< [out]: Client should access ::NvEncGetEncodePresetGUIDs() API through this pointer. */ + PNVENCGETENCODEPRESETCONFIG nvEncGetEncodePresetConfig; /**< [out]: Client should access ::NvEncGetEncodePresetConfig() API through this pointer. */ + PNVENCINITIALIZEENCODER nvEncInitializeEncoder; /**< [out]: Client should access ::NvEncInitializeEncoder() API through this pointer. */ + PNVENCCREATEINPUTBUFFER nvEncCreateInputBuffer; /**< [out]: Client should access ::NvEncCreateInputBuffer() API through this pointer. */ + PNVENCDESTROYINPUTBUFFER nvEncDestroyInputBuffer; /**< [out]: Client should access ::NvEncDestroyInputBuffer() API through this pointer. */ + PNVENCCREATEBITSTREAMBUFFER nvEncCreateBitstreamBuffer; /**< [out]: Client should access ::NvEncCreateBitstreamBuffer() API through this pointer. */ + PNVENCDESTROYBITSTREAMBUFFER nvEncDestroyBitstreamBuffer; /**< [out]: Client should access ::NvEncDestroyBitstreamBuffer() API through this pointer. */ + PNVENCENCODEPICTURE nvEncEncodePicture; /**< [out]: Client should access ::NvEncEncodePicture() API through this pointer. */ + PNVENCLOCKBITSTREAM nvEncLockBitstream; /**< [out]: Client should access ::NvEncLockBitstream() API through this pointer. */ + PNVENCUNLOCKBITSTREAM nvEncUnlockBitstream; /**< [out]: Client should access ::NvEncUnlockBitstream() API through this pointer. */ + PNVENCLOCKINPUTBUFFER nvEncLockInputBuffer; /**< [out]: Client should access ::NvEncLockInputBuffer() API through this pointer. */ + PNVENCUNLOCKINPUTBUFFER nvEncUnlockInputBuffer; /**< [out]: Client should access ::NvEncUnlockInputBuffer() API through this pointer. */ + PNVENCGETENCODESTATS nvEncGetEncodeStats; /**< [out]: Client should access ::NvEncGetEncodeStats() API through this pointer. */ + PNVENCGETSEQUENCEPARAMS nvEncGetSequenceParams; /**< [out]: Client should access ::NvEncGetSequenceParams() API through this pointer. */ + PNVENCREGISTERASYNCEVENT nvEncRegisterAsyncEvent; /**< [out]: Client should access ::NvEncRegisterAsyncEvent() API through this pointer. */ + PNVENCUNREGISTERASYNCEVENT nvEncUnregisterAsyncEvent; /**< [out]: Client should access ::NvEncUnregisterAsyncEvent() API through this pointer. */ + PNVENCMAPINPUTRESOURCE nvEncMapInputResource; /**< [out]: Client should access ::NvEncMapInputResource() API through this pointer. */ + PNVENCUNMAPINPUTRESOURCE nvEncUnmapInputResource; /**< [out]: Client should access ::NvEncUnmapInputResource() API through this pointer. */ + PNVENCDESTROYENCODER nvEncDestroyEncoder; /**< [out]: Client should access ::NvEncDestroyEncoder() API through this pointer. */ + PNVENCINVALIDATEREFFRAMES nvEncInvalidateRefFrames; /**< [out]: Client should access ::NvEncInvalidateRefFrames() API through this pointer. */ + PNVENCOPENENCODESESSIONEX nvEncOpenEncodeSessionEx; /**< [out]: Client should access ::NvEncOpenEncodeSession() API through this pointer. */ + PNVENCREGISTERRESOURCE nvEncRegisterResource; /**< [out]: Client should access ::NvEncRegisterResource() API through this pointer. */ + PNVENCUNREGISTERRESOURCE nvEncUnregisterResource; /**< [out]: Client should access ::NvEncUnregisterResource() API through this pointer. */ + PNVENCRECONFIGUREENCODER nvEncReconfigureEncoder; /**< [out]: Client should access ::NvEncReconfigureEncoder() API through this pointer. */ + void* reserved1; + PNVENCCREATEMVBUFFER nvEncCreateMVBuffer; /**< [out]: Client should access ::NvEncCreateMVBuffer API through this pointer. */ + PNVENCDESTROYMVBUFFER nvEncDestroyMVBuffer; /**< [out]: Client should access ::NvEncDestroyMVBuffer API through this pointer. */ + PNVENCRUNMOTIONESTIMATIONONLY nvEncRunMotionEstimationOnly; /**< [out]: Client should access ::NvEncRunMotionEstimationOnly API through this pointer. */ + PNVENCGETLASTERROR nvEncGetLastErrorString; /**< [out]: Client should access ::nvEncGetLastErrorString API through this pointer. */ + PNVENCSETIOCUDASTREAMS nvEncSetIOCudaStreams; /**< [out]: Client should access ::nvEncSetIOCudaStreams API through this pointer. */ + PNVENCGETENCODEPRESETCONFIGEX nvEncGetEncodePresetConfigEx; /**< [out]: Client should access ::NvEncGetEncodePresetConfigEx() API through this pointer. */ + PNVENCGETSEQUENCEPARAMEX nvEncGetSequenceParamEx; /**< [out]: Client should access ::NvEncGetSequenceParamEx() API through this pointer. */ + void* reserved2[277]; /**< [in]: Reserved and must be set to NULL */ +} NV_ENCODE_API_FUNCTION_LIST; + +/** Macro for constructing the version field of ::_NV_ENCODEAPI_FUNCTION_LIST. */ +#define NV_ENCODE_API_FUNCTION_LIST_VER NVENCAPI_STRUCT_VERSION(2) + +// NvEncodeAPICreateInstance +/** + * \ingroup ENCODE_FUNC + * Entry Point to the NvEncodeAPI interface. + * + * Creates an instance of the NvEncodeAPI interface, and populates the + * pFunctionList with function pointers to the API routines implemented by the + * NvEncodeAPI interface. + * + * \param [out] functionList + * + * \return + * ::NV_ENC_SUCCESS + * ::NV_ENC_ERR_INVALID_PTR + */ +NVENCSTATUS NVENCAPI NvEncodeAPICreateInstance(NV_ENCODE_API_FUNCTION_LIST *functionList); + +#ifdef __cplusplus +} +#endif + + +#endif + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/include/nvcuvid.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/include/nvcuvid.h new file mode 100644 index 0000000..7e78535 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/NvCodec/include/nvcuvid.h @@ -0,0 +1,501 @@ +/* + * This copyright notice applies to this header file only: + * + * Copyright (c) 2010-2022 NVIDIA Corporation + * + * 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. + */ + +/********************************************************************************************************************/ +//! \file nvcuvid.h +//! NVDECODE API provides video decoding interface to NVIDIA GPU devices. +//! \date 2015-2022 +//! This file contains the interface constants, structure definitions and function prototypes. +/********************************************************************************************************************/ + +#if !defined(__NVCUVID_H__) +#define __NVCUVID_H__ + +#include "cuviddec.h" + +#if defined(__cplusplus) +extern "C" { +#endif /* __cplusplus */ + +#define MAX_CLOCK_TS 3 + +/***********************************************/ +//! +//! High-level helper APIs for video sources +//! +/***********************************************/ + +typedef void *CUvideosource; +typedef void *CUvideoparser; +typedef long long CUvideotimestamp; + + +/************************************************************************/ +//! \enum cudaVideoState +//! Video source state enums +//! Used in cuvidSetVideoSourceState and cuvidGetVideoSourceState APIs +/************************************************************************/ +typedef enum { + cudaVideoState_Error = -1, /**< Error state (invalid source) */ + cudaVideoState_Stopped = 0, /**< Source is stopped (or reached end-of-stream) */ + cudaVideoState_Started = 1 /**< Source is running and delivering data */ +} cudaVideoState; + +/************************************************************************/ +//! \enum cudaAudioCodec +//! Audio compression enums +//! Used in CUAUDIOFORMAT structure +/************************************************************************/ +typedef enum { + cudaAudioCodec_MPEG1=0, /**< MPEG-1 Audio */ + cudaAudioCodec_MPEG2, /**< MPEG-2 Audio */ + cudaAudioCodec_MP3, /**< MPEG-1 Layer III Audio */ + cudaAudioCodec_AC3, /**< Dolby Digital (AC3) Audio */ + cudaAudioCodec_LPCM, /**< PCM Audio */ + cudaAudioCodec_AAC, /**< AAC Audio */ +} cudaAudioCodec; + +/************************************************************************/ +//! \ingroup STRUCTS +//! \struct HEVCTIMECODESET +//! Used to store Time code extracted from Time code SEI in HEVC codec +/************************************************************************/ +typedef struct _HEVCTIMECODESET +{ + unsigned int time_offset_value; + unsigned short n_frames; + unsigned char clock_timestamp_flag; + unsigned char units_field_based_flag; + unsigned char counting_type; + unsigned char full_timestamp_flag; + unsigned char discontinuity_flag; + unsigned char cnt_dropped_flag; + unsigned char seconds_value; + unsigned char minutes_value; + unsigned char hours_value; + unsigned char seconds_flag; + unsigned char minutes_flag; + unsigned char hours_flag; + unsigned char time_offset_length; + unsigned char reserved; +} HEVCTIMECODESET; + +/************************************************************************/ +//! \ingroup STRUCTS +//! \struct HEVCSEITIMECODE +//! Used to extract Time code SEI in HEVC codec +/************************************************************************/ +typedef struct _HEVCSEITIMECODE +{ + HEVCTIMECODESET time_code_set[MAX_CLOCK_TS]; + unsigned char num_clock_ts; +} HEVCSEITIMECODE; + +/**********************************************************************************/ +//! \ingroup STRUCTS +//! \struct CUSEIMESSAGE; +//! Used in CUVIDSEIMESSAGEINFO structure +/**********************************************************************************/ +typedef struct _CUSEIMESSAGE +{ + unsigned char sei_message_type; /**< OUT: SEI Message Type */ + unsigned char reserved[3]; + unsigned int sei_message_size; /**< OUT: SEI Message Size */ +} CUSEIMESSAGE; + +/************************************************************************************************/ +//! \ingroup STRUCTS +//! \struct CUVIDEOFORMAT +//! Video format +//! Used in cuvidGetSourceVideoFormat API +/************************************************************************************************/ +typedef struct +{ + cudaVideoCodec codec; /**< OUT: Compression format */ + /** + * OUT: frame rate = numerator / denominator (for example: 30000/1001) + */ + struct { + /**< OUT: frame rate numerator (0 = unspecified or variable frame rate) */ + unsigned int numerator; + /**< OUT: frame rate denominator (0 = unspecified or variable frame rate) */ + unsigned int denominator; + } frame_rate; + unsigned char progressive_sequence; /**< OUT: 0=interlaced, 1=progressive */ + unsigned char bit_depth_luma_minus8; /**< OUT: high bit depth luma. E.g, 2 for 10-bitdepth, 4 for 12-bitdepth */ + unsigned char bit_depth_chroma_minus8; /**< OUT: high bit depth chroma. E.g, 2 for 10-bitdepth, 4 for 12-bitdepth */ + unsigned char min_num_decode_surfaces; /**< OUT: Minimum number of decode surfaces to be allocated for correct + decoding. The client can send this value in ulNumDecodeSurfaces + (in CUVIDDECODECREATEINFO structure). + This guarantees correct functionality and optimal video memory + usage but not necessarily the best performance, which depends on + the design of the overall application. The optimal number of + decode surfaces (in terms of performance and memory utilization) + should be decided by experimentation for each application, but it + cannot go below min_num_decode_surfaces. + If this value is used for ulNumDecodeSurfaces then it must be + returned to parser during sequence callback. */ + unsigned int coded_width; /**< OUT: coded frame width in pixels */ + unsigned int coded_height; /**< OUT: coded frame height in pixels */ + /** + * area of the frame that should be displayed + * typical example: + * coded_width = 1920, coded_height = 1088 + * display_area = { 0,0,1920,1080 } + */ + struct { + int left; /**< OUT: left position of display rect */ + int top; /**< OUT: top position of display rect */ + int right; /**< OUT: right position of display rect */ + int bottom; /**< OUT: bottom position of display rect */ + } display_area; + cudaVideoChromaFormat chroma_format; /**< OUT: Chroma format */ + unsigned int bitrate; /**< OUT: video bitrate (bps, 0=unknown) */ + /** + * OUT: Display Aspect Ratio = x:y (4:3, 16:9, etc) + */ + struct { + int x; + int y; + } display_aspect_ratio; + /** + * Video Signal Description + * Refer section E.2.1 (VUI parameters semantics) of H264 spec file + */ + struct { + unsigned char video_format : 3; /**< OUT: 0-Component, 1-PAL, 2-NTSC, 3-SECAM, 4-MAC, 5-Unspecified */ + unsigned char video_full_range_flag : 1; /**< OUT: indicates the black level and luma and chroma range */ + unsigned char reserved_zero_bits : 4; /**< Reserved bits */ + unsigned char color_primaries; /**< OUT: chromaticity coordinates of source primaries */ + unsigned char transfer_characteristics; /**< OUT: opto-electronic transfer characteristic of the source picture */ + unsigned char matrix_coefficients; /**< OUT: used in deriving luma and chroma signals from RGB primaries */ + } video_signal_description; + unsigned int seqhdr_data_length; /**< OUT: Additional bytes following (CUVIDEOFORMATEX) */ +} CUVIDEOFORMAT; + +/****************************************************************/ +//! \ingroup STRUCTS +//! \struct CUVIDOPERATINGPOINTINFO +//! Operating point information of scalable bitstream +/****************************************************************/ +typedef struct +{ + cudaVideoCodec codec; + union + { + struct + { + unsigned char operating_points_cnt; + unsigned char reserved24_bits[3]; + unsigned short operating_points_idc[32]; + } av1; + unsigned char CodecReserved[1024]; + }; +} CUVIDOPERATINGPOINTINFO; + +/**********************************************************************************/ +//! \ingroup STRUCTS +//! \struct CUVIDSEIMESSAGEINFO +//! Used in cuvidParseVideoData API with PFNVIDSEIMSGCALLBACK pfnGetSEIMsg +/**********************************************************************************/ +typedef struct _CUVIDSEIMESSAGEINFO +{ + void *pSEIData; /**< OUT: SEI Message Data */ + CUSEIMESSAGE *pSEIMessage; /**< OUT: SEI Message Info */ + unsigned int sei_message_count; /**< OUT: SEI Message Count */ + unsigned int picIdx; /**< OUT: SEI Message Pic Index */ +} CUVIDSEIMESSAGEINFO; + +/****************************************************************/ +//! \ingroup STRUCTS +//! \struct CUVIDAV1SEQHDR +//! AV1 specific sequence header information +/****************************************************************/ +typedef struct { + unsigned int max_width; + unsigned int max_height; + unsigned char reserved[1016]; +} CUVIDAV1SEQHDR; + +/****************************************************************/ +//! \ingroup STRUCTS +//! \struct CUVIDEOFORMATEX +//! Video format including raw sequence header information +//! Used in cuvidGetSourceVideoFormat API +/****************************************************************/ +typedef struct +{ + CUVIDEOFORMAT format; /**< OUT: CUVIDEOFORMAT structure */ + union { + CUVIDAV1SEQHDR av1; + unsigned char raw_seqhdr_data[1024]; /**< OUT: Sequence header data */ + }; +} CUVIDEOFORMATEX; + +/****************************************************************/ +//! \ingroup STRUCTS +//! \struct CUAUDIOFORMAT +//! Audio formats +//! Used in cuvidGetSourceAudioFormat API +/****************************************************************/ +typedef struct +{ + cudaAudioCodec codec; /**< OUT: Compression format */ + unsigned int channels; /**< OUT: number of audio channels */ + unsigned int samplespersec; /**< OUT: sampling frequency */ + unsigned int bitrate; /**< OUT: For uncompressed, can also be used to determine bits per sample */ + unsigned int reserved1; /**< Reserved for future use */ + unsigned int reserved2; /**< Reserved for future use */ +} CUAUDIOFORMAT; + + +/***************************************************************/ +//! \enum CUvideopacketflags +//! Data packet flags +//! Used in CUVIDSOURCEDATAPACKET structure +/***************************************************************/ +typedef enum { + CUVID_PKT_ENDOFSTREAM = 0x01, /**< Set when this is the last packet for this stream */ + CUVID_PKT_TIMESTAMP = 0x02, /**< Timestamp is valid */ + CUVID_PKT_DISCONTINUITY = 0x04, /**< Set when a discontinuity has to be signalled */ + CUVID_PKT_ENDOFPICTURE = 0x08, /**< Set when the packet contains exactly one frame or one field */ + CUVID_PKT_NOTIFY_EOS = 0x10, /**< If this flag is set along with CUVID_PKT_ENDOFSTREAM, an additional (dummy) + display callback will be invoked with null value of CUVIDPARSERDISPINFO which + should be interpreted as end of the stream. */ +} CUvideopacketflags; + +/*****************************************************************************/ +//! \ingroup STRUCTS +//! \struct CUVIDSOURCEDATAPACKET +//! Data Packet +//! Used in cuvidParseVideoData API +//! IN for cuvidParseVideoData +/*****************************************************************************/ +typedef struct _CUVIDSOURCEDATAPACKET +{ + unsigned long flags; /**< IN: Combination of CUVID_PKT_XXX flags */ + unsigned long payload_size; /**< IN: number of bytes in the payload (may be zero if EOS flag is set) */ + const unsigned char *payload; /**< IN: Pointer to packet payload data (may be NULL if EOS flag is set) */ + CUvideotimestamp timestamp; /**< IN: Presentation time stamp (10MHz clock), only valid if + CUVID_PKT_TIMESTAMP flag is set */ +} CUVIDSOURCEDATAPACKET; + +// Callback for packet delivery +typedef int (CUDAAPI *PFNVIDSOURCECALLBACK)(void *, CUVIDSOURCEDATAPACKET *); + +/**************************************************************************************************************************/ +//! \ingroup STRUCTS +//! \struct CUVIDSOURCEPARAMS +//! Describes parameters needed in cuvidCreateVideoSource API +//! NVDECODE API is intended for HW accelerated video decoding so CUvideosource doesn't have audio demuxer for all supported +//! containers. It's recommended to clients to use their own or third party demuxer if audio support is needed. +/**************************************************************************************************************************/ +typedef struct _CUVIDSOURCEPARAMS +{ + unsigned int ulClockRate; /**< IN: Time stamp units in Hz (0=default=10000000Hz) */ + unsigned int bAnnexb : 1; /**< IN: AV1 annexB stream */ + unsigned int uReserved : 31; /**< Reserved for future use - set to zero */ + unsigned int uReserved1[6]; /**< Reserved for future use - set to zero */ + void *pUserData; /**< IN: User private data passed in to the data handlers */ + PFNVIDSOURCECALLBACK pfnVideoDataHandler; /**< IN: Called to deliver video packets */ + PFNVIDSOURCECALLBACK pfnAudioDataHandler; /**< IN: Called to deliver audio packets. */ + void *pvReserved2[8]; /**< Reserved for future use - set to NULL */ +} CUVIDSOURCEPARAMS; + + +/**********************************************/ +//! \ingroup ENUMS +//! \enum CUvideosourceformat_flags +//! CUvideosourceformat_flags +//! Used in cuvidGetSourceVideoFormat API +/**********************************************/ +typedef enum { + CUVID_FMT_EXTFORMATINFO = 0x100 /**< Return extended format structure (CUVIDEOFORMATEX) */ +} CUvideosourceformat_flags; + +#if !defined(__APPLE__) +/***************************************************************************************************************************/ +//! \ingroup FUNCTS +//! \fn CUresult CUDAAPI cuvidCreateVideoSource(CUvideosource *pObj, const char *pszFileName, CUVIDSOURCEPARAMS *pParams) +//! Create CUvideosource object. CUvideosource spawns demultiplexer thread that provides two callbacks: +//! pfnVideoDataHandler() and pfnAudioDataHandler() +//! NVDECODE API is intended for HW accelerated video decoding so CUvideosource doesn't have audio demuxer for all supported +//! containers. It's recommended to clients to use their own or third party demuxer if audio support is needed. +/***************************************************************************************************************************/ +CUresult CUDAAPI cuvidCreateVideoSource(CUvideosource *pObj, const char *pszFileName, CUVIDSOURCEPARAMS *pParams); + +/***************************************************************************************************************************/ +//! \ingroup FUNCTS +//! \fn CUresult CUDAAPI cuvidCreateVideoSourceW(CUvideosource *pObj, const wchar_t *pwszFileName, CUVIDSOURCEPARAMS *pParams) +//! Create video source +/***************************************************************************************************************************/ +CUresult CUDAAPI cuvidCreateVideoSourceW(CUvideosource *pObj, const wchar_t *pwszFileName, CUVIDSOURCEPARAMS *pParams); + +/********************************************************************/ +//! \ingroup FUNCTS +//! \fn CUresult CUDAAPI cuvidDestroyVideoSource(CUvideosource obj) +//! Destroy video source +/********************************************************************/ +CUresult CUDAAPI cuvidDestroyVideoSource(CUvideosource obj); + +/******************************************************************************************/ +//! \ingroup FUNCTS +//! \fn CUresult CUDAAPI cuvidSetVideoSourceState(CUvideosource obj, cudaVideoState state) +//! Set video source state to: +//! cudaVideoState_Started - to signal the source to run and deliver data +//! cudaVideoState_Stopped - to stop the source from delivering the data +//! cudaVideoState_Error - invalid source +/******************************************************************************************/ +CUresult CUDAAPI cuvidSetVideoSourceState(CUvideosource obj, cudaVideoState state); + +/******************************************************************************************/ +//! \ingroup FUNCTS +//! \fn cudaVideoState CUDAAPI cuvidGetVideoSourceState(CUvideosource obj) +//! Get video source state +//! Returns: +//! cudaVideoState_Started - if Source is running and delivering data +//! cudaVideoState_Stopped - if Source is stopped or reached end-of-stream +//! cudaVideoState_Error - if Source is in error state +/******************************************************************************************/ +cudaVideoState CUDAAPI cuvidGetVideoSourceState(CUvideosource obj); + +/******************************************************************************************************************/ +//! \ingroup FUNCTS +//! \fn CUresult CUDAAPI cuvidGetSourceVideoFormat(CUvideosource obj, CUVIDEOFORMAT *pvidfmt, unsigned int flags) +//! Gets video source format in pvidfmt, flags is set to combination of CUvideosourceformat_flags as per requirement +/******************************************************************************************************************/ +CUresult CUDAAPI cuvidGetSourceVideoFormat(CUvideosource obj, CUVIDEOFORMAT *pvidfmt, unsigned int flags); + +/**************************************************************************************************************************/ +//! \ingroup FUNCTS +//! \fn CUresult CUDAAPI cuvidGetSourceAudioFormat(CUvideosource obj, CUAUDIOFORMAT *paudfmt, unsigned int flags) +//! Get audio source format +//! NVDECODE API is intended for HW accelerated video decoding so CUvideosource doesn't have audio demuxer for all supported +//! containers. It's recommended to clients to use their own or third party demuxer if audio support is needed. +/**************************************************************************************************************************/ +CUresult CUDAAPI cuvidGetSourceAudioFormat(CUvideosource obj, CUAUDIOFORMAT *paudfmt, unsigned int flags); + +#endif +/**********************************************************************************/ +//! \ingroup STRUCTS +//! \struct CUVIDPARSERDISPINFO +//! Used in cuvidParseVideoData API with PFNVIDDISPLAYCALLBACK pfnDisplayPicture +/**********************************************************************************/ +typedef struct _CUVIDPARSERDISPINFO +{ + int picture_index; /**< OUT: Index of the current picture */ + int progressive_frame; /**< OUT: 1 if progressive frame; 0 otherwise */ + int top_field_first; /**< OUT: 1 if top field is displayed first; 0 otherwise */ + int repeat_first_field; /**< OUT: Number of additional fields (1=ivtc, 2=frame doubling, 4=frame tripling, + -1=unpaired field) */ + CUvideotimestamp timestamp; /**< OUT: Presentation time stamp */ +} CUVIDPARSERDISPINFO; + +/***********************************************************************************************************************/ +//! Parser callbacks +//! The parser will call these synchronously from within cuvidParseVideoData(), whenever there is sequence change or a picture +//! is ready to be decoded and/or displayed. First argument in functions is "void *pUserData" member of structure CUVIDSOURCEPARAMS +//! Return values from these callbacks are interpreted as below. If the callbacks return failure, it will be propagated by +//! cuvidParseVideoData() to the application. +//! Parser picks default operating point as 0 and outputAllLayers flag as 0 if PFNVIDOPPOINTCALLBACK is not set or return value is +//! -1 or invalid operating point. +//! PFNVIDSEQUENCECALLBACK : 0: fail, 1: succeeded, > 1: override dpb size of parser (set by CUVIDPARSERPARAMS::ulMaxNumDecodeSurfaces +//! while creating parser) +//! PFNVIDDECODECALLBACK : 0: fail, >=1: succeeded +//! PFNVIDDISPLAYCALLBACK : 0: fail, >=1: succeeded +//! PFNVIDOPPOINTCALLBACK : <0: fail, >=0: succeeded (bit 0-9: OperatingPoint, bit 10-10: outputAllLayers, bit 11-30: reserved) +//! PFNVIDSEIMSGCALLBACK : 0: fail, >=1: succeeded +/***********************************************************************************************************************/ +typedef int (CUDAAPI *PFNVIDSEQUENCECALLBACK)(void *, CUVIDEOFORMAT *); +typedef int (CUDAAPI *PFNVIDDECODECALLBACK)(void *, CUVIDPICPARAMS *); +typedef int (CUDAAPI *PFNVIDDISPLAYCALLBACK)(void *, CUVIDPARSERDISPINFO *); +typedef int (CUDAAPI *PFNVIDOPPOINTCALLBACK)(void *, CUVIDOPERATINGPOINTINFO*); +typedef int (CUDAAPI *PFNVIDSEIMSGCALLBACK) (void *, CUVIDSEIMESSAGEINFO *); + +/**************************************/ +//! \ingroup STRUCTS +//! \struct CUVIDPARSERPARAMS +//! Used in cuvidCreateVideoParser API +/**************************************/ +typedef struct _CUVIDPARSERPARAMS +{ + cudaVideoCodec CodecType; /**< IN: cudaVideoCodec_XXX */ + unsigned int ulMaxNumDecodeSurfaces; /**< IN: Max # of decode surfaces (parser will cycle through these) */ + unsigned int ulClockRate; /**< IN: Timestamp units in Hz (0=default=10000000Hz) */ + unsigned int ulErrorThreshold; /**< IN: % Error threshold (0-100) for calling pfnDecodePicture (100=always + IN: call pfnDecodePicture even if picture bitstream is fully corrupted) */ + unsigned int ulMaxDisplayDelay; /**< IN: Max display queue delay (improves pipelining of decode with display) + 0=no delay (recommended values: 2..4) */ + unsigned int bAnnexb : 1; /**< IN: AV1 annexB stream */ + unsigned int uReserved : 31; /**< Reserved for future use - set to zero */ + unsigned int uReserved1[4]; /**< IN: Reserved for future use - set to 0 */ + void *pUserData; /**< IN: User data for callbacks */ + PFNVIDSEQUENCECALLBACK pfnSequenceCallback; /**< IN: Called before decoding frames and/or whenever there is a fmt change */ + PFNVIDDECODECALLBACK pfnDecodePicture; /**< IN: Called when a picture is ready to be decoded (decode order) */ + PFNVIDDISPLAYCALLBACK pfnDisplayPicture; /**< IN: Called whenever a picture is ready to be displayed (display order) */ + PFNVIDOPPOINTCALLBACK pfnGetOperatingPoint; /**< IN: Called from AV1 sequence header to get operating point of a AV1 + scalable bitstream */ + PFNVIDSEIMSGCALLBACK pfnGetSEIMsg; /**< IN: Called when all SEI messages are parsed for particular frame */ + void *pvReserved2[5]; /**< Reserved for future use - set to NULL */ + CUVIDEOFORMATEX *pExtVideoInfo; /**< IN: [Optional] sequence header data from system layer */ +} CUVIDPARSERPARAMS; + +/************************************************************************************************/ +//! \ingroup FUNCTS +//! \fn CUresult CUDAAPI cuvidCreateVideoParser(CUvideoparser *pObj, CUVIDPARSERPARAMS *pParams) +//! Create video parser object and initialize +/************************************************************************************************/ +CUresult CUDAAPI cuvidCreateVideoParser(CUvideoparser *pObj, CUVIDPARSERPARAMS *pParams); + +/************************************************************************************************/ +//! \ingroup FUNCTS +//! \fn CUresult CUDAAPI cuvidParseVideoData(CUvideoparser obj, CUVIDSOURCEDATAPACKET *pPacket) +//! Parse the video data from source data packet in pPacket +//! Extracts parameter sets like SPS, PPS, bitstream etc. from pPacket and +//! calls back pfnDecodePicture with CUVIDPICPARAMS data for kicking of HW decoding +//! calls back pfnSequenceCallback with CUVIDEOFORMAT data for initial sequence header or when +//! the decoder encounters a video format change +//! calls back pfnDisplayPicture with CUVIDPARSERDISPINFO data to display a video frame +/************************************************************************************************/ +CUresult CUDAAPI cuvidParseVideoData(CUvideoparser obj, CUVIDSOURCEDATAPACKET *pPacket); + +/************************************************************************************************/ +//! \ingroup FUNCTS +//! \fn CUresult CUDAAPI cuvidDestroyVideoParser(CUvideoparser obj) +//! Destroy the video parser +/************************************************************************************************/ +CUresult CUDAAPI cuvidDestroyVideoParser(CUvideoparser obj); + +/**********************************************************************************************/ + +#if defined(__cplusplus) +} +#endif /* __cplusplus */ + +#endif // __NVCUVID_H__ + + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/cuda_context.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/cuda_context.cpp new file mode 100644 index 0000000..94273ab --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/cuda_context.cpp @@ -0,0 +1,201 @@ +#include "cuda_context.h" + +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +#if defined(WIN32) +#include +#else +#include +#endif + +#include + +#if defined(WIN32) +static const char CUDA_DYNAMIC_LIBRARY[] = "nvcuda.dll"; +#else +static const char CUDA_DYNAMIC_LIBRARY[] = "libcuda.so.1"; +#endif + +namespace livekit_ffi { + +#define __CUCTX_CUDA_CALL(call, ret) \ + CUresult err__ = call; \ + if (err__ != CUDA_SUCCESS) { \ + const char* szErrName = NULL; \ + cuGetErrorName(err__, &szErrName); \ + RTC_LOG(LS_ERROR) << "CudaContext error " << szErrName; \ + return ret; \ + } + +#define CUCTX_CUDA_CALL_ERROR(call) \ + do { \ + __CUCTX_CUDA_CALL(call, err__); \ + } while (0) + +static void* s_module_ptr = nullptr; +static const int kRequiredDriverVersion = 11000; + +static bool load_cuda_modules() { + if (s_module_ptr) + return true; + +#if defined(WIN32) + // dll delay load + HMODULE module = LoadLibrary(TEXT("nvcuda.dll")); + if (!module) { + RTC_LOG(LS_INFO) << "nvcuda.dll is not found."; + return false; + } + s_module_ptr = module; +#elif defined(__linux__) + s_module_ptr = dlopen("libcuda.so.1", RTLD_LAZY | RTLD_GLOBAL); + if (!s_module_ptr) + return false; + + // Close handle immediately because going to call `dlopen` again + // in the implib module when cuda api called on Linux. + dlclose(s_module_ptr); + s_module_ptr = nullptr; +#endif + return true; +} + +static bool check_cuda_device() { + int device_count = 0; + int driver_version = 0; + + CUCTX_CUDA_CALL_ERROR(cuDriverGetVersion(&driver_version)); + if (kRequiredDriverVersion > driver_version) { + RTC_LOG(LS_ERROR) + << "CUDA driver version is not higher than the required version. " + << driver_version; + return false; + } + + CUresult result = cuInit(0); + if (result != CUDA_SUCCESS) { + RTC_LOG(LS_ERROR) << "Failed to initialize CUDA."; + return false; + } + + result = cuDeviceGetCount(&device_count); + if (result != CUDA_SUCCESS) { + RTC_LOG(LS_ERROR) << "Failed to get CUDA device count."; + return false; + } + + if (device_count == 0) { + RTC_LOG(LS_ERROR) << "No CUDA devices found."; + return false; + } + + return true; +} + +CudaContext* CudaContext::GetInstance() { + static CudaContext instance; + return &instance; +} + +bool CudaContext::IsAvailable() { + return load_cuda_modules() && check_cuda_device(); +} + +bool CudaContext::Initialize() { + // Initialize CUDA context + + bool success = load_cuda_modules(); + if (!success) { + RTC_LOG(LS_ERROR) << "Failed to load CUDA modules. maybe the NVIDIA driver " + "is not installed?"; + return false; + } + + int num_devices = 0; + CUdevice cu_device = 0; + CUcontext context = nullptr; + + int driverVersion = 0; + + CUCTX_CUDA_CALL_ERROR(cuDriverGetVersion(&driverVersion)); + if (kRequiredDriverVersion > driverVersion) { + RTC_LOG(LS_ERROR) + << "CUDA driver version is not higher than the required version. " + << driverVersion; + return false; + } + + CUresult result = cuInit(0); + if (result != CUDA_SUCCESS) { + RTC_LOG(LS_ERROR) << "Failed to initialize CUDA."; + return false; + } + + result = cuDeviceGetCount(&num_devices); + if (result != CUDA_SUCCESS) { + RTC_LOG(LS_ERROR) << "Failed to get CUDA device count."; + return false; + } + + if (num_devices == 0) { + RTC_LOG(LS_ERROR) << "No CUDA devices found."; + return false; + } + + CUCTX_CUDA_CALL_ERROR(cuDeviceGet(&cu_device, 0)); + + char device_name[80]; + CUCTX_CUDA_CALL_ERROR( + cuDeviceGetName(device_name, sizeof(device_name), cu_device)); + RTC_LOG(LS_INFO) << "CUDA device name: " << device_name; + +#if CUDA_VERSION >= 13000 + CUCTX_CUDA_CALL_ERROR(cuCtxCreate(&context, nullptr, 0, cu_device)); +#else + CUCTX_CUDA_CALL_ERROR(cuCtxCreate(&context, 0, cu_device)); +#endif + if (context == nullptr) { + RTC_LOG(LS_ERROR) << "Failed to create CUDA context."; + return false; + } + + cu_device_ = cu_device; + cu_context_ = context; + + return true; +} + +CUcontext CudaContext::GetContext() const { + RTC_DCHECK(cu_context_ != nullptr); + // Ensure the context is current + CUcontext current; + if (cuCtxGetCurrent(¤t) != CUDA_SUCCESS) { + throw; + } + if (cu_context_ == current) { + return cu_context_; + } + if (cuCtxSetCurrent(cu_context_) != CUDA_SUCCESS) { + throw; + } + return cu_context_; +} + +void CudaContext::Shutdown() { + // Shutdown CUDA context + if (cu_context_) { + cuCtxDestroy(cu_context_); + cu_context_ = nullptr; + } + if (s_module_ptr) { +#if defined(WIN32) + FreeLibrary((HMODULE)s_module_ptr); +#elif defined(__linux__) + dlclose(s_module_ptr); +#endif + s_module_ptr = nullptr; + } +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/cuda_context.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/cuda_context.h new file mode 100644 index 0000000..d38b66c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/cuda_context.h @@ -0,0 +1,29 @@ +#ifndef WEBRTC_SYS_NVIDIA_CUDA_CONTEXT_H +#define WEBRTC_SYS_NVIDIA_CUDA_CONTEXT_H + +#include + +namespace livekit_ffi { + +class CudaContext { + public: + CudaContext() = default; + ~CudaContext() = default; + + static bool IsAvailable(); + + static CudaContext* GetInstance(); + bool Initialize(); + bool IsInitialized() const { return cu_context_ != nullptr; } + CUcontext GetContext() const; + + void Shutdown(); + + private: + CUdevice cu_device_ = 0; + CUcontext cu_context_ = nullptr; +}; + +} // namespace livekit_ffi + +#endif // WEBRTC_SYS_NVIDIA_CUDA_CONTEXT_H diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/cuda_driver_dynamic.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/cuda_driver_dynamic.cpp new file mode 100644 index 0000000..ea6cb0e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/cuda_driver_dynamic.cpp @@ -0,0 +1,240 @@ +#if defined(_WIN32) + +#include "cuda.h" + +#include + +namespace { + +HMODULE CudaModule() { + static HMODULE module = LoadLibraryA("nvcuda.dll"); + return module; +} + +FARPROC CudaProc(const char* name) { + HMODULE module = CudaModule(); + if (!module) { + return nullptr; + } + return GetProcAddress(module, name); +} + +template +Fn Resolve(const char* primary, const char* fallback = nullptr) { + FARPROC proc = CudaProc(primary); + if (!proc && fallback) { + proc = CudaProc(fallback); + } + return reinterpret_cast(proc); +} + +template +CUresult Call(Fn fn, Args... args) { + if (!fn) { + return CUDA_ERROR_NOT_INITIALIZED; + } + return fn(args...); +} + +} // namespace + +extern "C" { + +CUresult CUDAAPI cuInit(unsigned int flags) { + using Fn = CUresult(CUDAAPI*)(unsigned int); + return Call(Resolve("cuInit"), flags); +} + +CUresult CUDAAPI cuDriverGetVersion(int* driverVersion) { + using Fn = CUresult(CUDAAPI*)(int*); + return Call(Resolve("cuDriverGetVersion"), driverVersion); +} + +CUresult CUDAAPI cuGetErrorName(CUresult error, const char** pStr) { + using Fn = CUresult(CUDAAPI*)(CUresult, const char**); + Fn fn = Resolve("cuGetErrorName"); + if (fn) { + return fn(error, pStr); + } + if (pStr) { + *pStr = "CUDA driver API unavailable"; + } + return CUDA_ERROR_NOT_INITIALIZED; +} + +CUresult CUDAAPI cuDeviceGetCount(int* count) { + using Fn = CUresult(CUDAAPI*)(int*); + return Call(Resolve("cuDeviceGetCount"), count); +} + +CUresult CUDAAPI cuDeviceGet(CUdevice* device, int ordinal) { + using Fn = CUresult(CUDAAPI*)(CUdevice*, int); + return Call(Resolve("cuDeviceGet"), device, ordinal); +} + +CUresult CUDAAPI cuDeviceGetName(char* name, int len, CUdevice dev) { + using Fn = CUresult(CUDAAPI*)(char*, int, CUdevice); + return Call(Resolve("cuDeviceGetName"), name, len, dev); +} + +CUresult CUDAAPI cuDeviceGetAttribute(int* pi, + CUdevice_attribute attrib, + CUdevice dev) { + using Fn = CUresult(CUDAAPI*)(int*, CUdevice_attribute, CUdevice); + return Call(Resolve("cuDeviceGetAttribute"), pi, attrib, dev); +} + +CUresult CUDAAPI cuCtxCreate(CUcontext* pctx, + unsigned int flags, + CUdevice dev) { + using Fn = CUresult(CUDAAPI*)(CUcontext*, unsigned int, CUdevice); + return Call(Resolve("cuCtxCreate_v2", "cuCtxCreate"), pctx, flags, dev); +} + +CUresult CUDAAPI cuCtxCreate_v2(CUcontext* pctx, + unsigned int flags, + CUdevice dev) { + return cuCtxCreate(pctx, flags, dev); +} + +CUresult CUDAAPI cuCtxDestroy(CUcontext ctx) { + using Fn = CUresult(CUDAAPI*)(CUcontext); + return Call(Resolve("cuCtxDestroy_v2", "cuCtxDestroy"), ctx); +} + +CUresult CUDAAPI cuCtxDestroy_v2(CUcontext ctx) { + return cuCtxDestroy(ctx); +} + +CUresult CUDAAPI cuCtxGetCurrent(CUcontext* pctx) { + using Fn = CUresult(CUDAAPI*)(CUcontext*); + return Call(Resolve("cuCtxGetCurrent"), pctx); +} + +CUresult CUDAAPI cuCtxSetCurrent(CUcontext ctx) { + using Fn = CUresult(CUDAAPI*)(CUcontext); + return Call(Resolve("cuCtxSetCurrent"), ctx); +} + +CUresult CUDAAPI cuCtxGetDevice(CUdevice* device) { + using Fn = CUresult(CUDAAPI*)(CUdevice*); + return Call(Resolve("cuCtxGetDevice"), device); +} + +CUresult CUDAAPI cuCtxPushCurrent(CUcontext ctx) { + using Fn = CUresult(CUDAAPI*)(CUcontext); + return Call(Resolve("cuCtxPushCurrent_v2", "cuCtxPushCurrent"), ctx); +} + +CUresult CUDAAPI cuCtxPushCurrent_v2(CUcontext ctx) { + return cuCtxPushCurrent(ctx); +} + +CUresult CUDAAPI cuCtxPopCurrent(CUcontext* pctx) { + using Fn = CUresult(CUDAAPI*)(CUcontext*); + return Call(Resolve("cuCtxPopCurrent_v2", "cuCtxPopCurrent"), pctx); +} + +CUresult CUDAAPI cuCtxPopCurrent_v2(CUcontext* pctx) { + return cuCtxPopCurrent(pctx); +} + +CUresult CUDAAPI cuMemAlloc(CUdeviceptr* dptr, size_t bytesize) { + using Fn = CUresult(CUDAAPI*)(CUdeviceptr*, size_t); + return Call(Resolve("cuMemAlloc_v2", "cuMemAlloc"), dptr, bytesize); +} + +CUresult CUDAAPI cuMemAllocPitch(CUdeviceptr* dptr, + size_t* pPitch, + size_t WidthInBytes, + size_t Height, + unsigned int ElementSizeBytes) { + using Fn = CUresult(CUDAAPI*)(CUdeviceptr*, size_t*, size_t, size_t, + unsigned int); + return Call(Resolve("cuMemAllocPitch_v2", "cuMemAllocPitch"), dptr, + pPitch, WidthInBytes, Height, ElementSizeBytes); +} + +CUresult CUDAAPI cuMemFree(CUdeviceptr dptr) { + using Fn = CUresult(CUDAAPI*)(CUdeviceptr); + return Call(Resolve("cuMemFree_v2", "cuMemFree"), dptr); +} + +CUresult CUDAAPI cuMemFree_v2(CUdeviceptr dptr) { + return cuMemFree(dptr); +} + +CUresult CUDAAPI cuMemcpy2D(const CUDA_MEMCPY2D* pCopy) { + using Fn = CUresult(CUDAAPI*)(const CUDA_MEMCPY2D*); + return Call(Resolve("cuMemcpy2D_v2", "cuMemcpy2D"), pCopy); +} + +CUresult CUDAAPI cuMemcpy2D_v2(const CUDA_MEMCPY2D* pCopy) { + return cuMemcpy2D(pCopy); +} + +CUresult CUDAAPI cuMemcpy2DUnaligned(const CUDA_MEMCPY2D* pCopy) { + using Fn = CUresult(CUDAAPI*)(const CUDA_MEMCPY2D*); + return Call(Resolve("cuMemcpy2DUnaligned_v2", + "cuMemcpy2DUnaligned"), + pCopy); +} + +CUresult CUDAAPI cuMemcpy2DUnaligned_v2(const CUDA_MEMCPY2D* pCopy) { + return cuMemcpy2DUnaligned(pCopy); +} + +CUresult CUDAAPI cuMemcpy2DAsync(const CUDA_MEMCPY2D* pCopy, + CUstream hStream) { + using Fn = CUresult(CUDAAPI*)(const CUDA_MEMCPY2D*, CUstream); + return Call(Resolve("cuMemcpy2DAsync_v2", "cuMemcpy2DAsync"), pCopy, + hStream); +} + +CUresult CUDAAPI cuMemcpy2DAsync_v2(const CUDA_MEMCPY2D* pCopy, + CUstream hStream) { + return cuMemcpy2DAsync(pCopy, hStream); +} + +CUresult CUDAAPI cuStreamCreate(CUstream* phStream, unsigned int Flags) { + using Fn = CUresult(CUDAAPI*)(CUstream*, unsigned int); + return Call(Resolve("cuStreamCreate"), phStream, Flags); +} + +CUresult CUDAAPI cuStreamSynchronize(CUstream hStream) { + using Fn = CUresult(CUDAAPI*)(CUstream); + return Call(Resolve("cuStreamSynchronize"), hStream); +} + +CUresult CUDAAPI cuArrayDestroy(CUarray hArray) { + using Fn = CUresult(CUDAAPI*)(CUarray); + return Call(Resolve("cuArrayDestroy_v2", "cuArrayDestroy"), hArray); +} + +CUresult CUDAAPI cuGraphicsEGLRegisterImage(CUgraphicsResource* pCudaResource, + void* image, + unsigned int flags) { + using Fn = CUresult(CUDAAPI*)(CUgraphicsResource*, void*, unsigned int); + return Call(Resolve("cuGraphicsEGLRegisterImage"), pCudaResource, image, + flags); +} + +CUresult CUDAAPI cuGraphicsResourceGetMappedEglFrame( + CUeglFrame* eglFrame, + CUgraphicsResource resource, + unsigned int index, + unsigned int mipLevel) { + using Fn = CUresult(CUDAAPI*)(CUeglFrame*, CUgraphicsResource, unsigned int, + unsigned int); + return Call(Resolve("cuGraphicsResourceGetMappedEglFrame"), eglFrame, + resource, index, mipLevel); +} + +CUresult CUDAAPI cuGraphicsUnregisterResource(CUgraphicsResource resource) { + using Fn = CUresult(CUDAAPI*)(CUgraphicsResource); + return Call(Resolve("cuGraphicsUnregisterResource"), resource); +} + +} // extern "C" + +#endif // defined(_WIN32) diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h264_decoder_impl.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h264_decoder_impl.cpp new file mode 100644 index 0000000..4d0d0a1 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h264_decoder_impl.cpp @@ -0,0 +1,193 @@ +#include "h264_decoder_impl.h" + +#include +#include +#include +#include + +#include "NvDecoder/NvDecoder.h" +#include "Utils/NvCodecUtils.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +ColorSpace ExtractH264ColorSpace(const CUVIDEOFORMAT& format) { + return ColorSpace( + static_cast( + format.video_signal_description.color_primaries), + static_cast( + format.video_signal_description.transfer_characteristics), + static_cast( + format.video_signal_description.matrix_coefficients), + static_cast( + format.video_signal_description.video_full_range_flag)); +} + +NvidiaH264DecoderImpl::NvidiaH264DecoderImpl(CUcontext context) + : cu_context_(context), + decoder_(nullptr), + is_configured_decoder_(false), + decoded_complete_callback_(nullptr), + buffer_pool_(false) {} + +NvidiaH264DecoderImpl::~NvidiaH264DecoderImpl() { + Release(); +} + +VideoDecoder::DecoderInfo NvidiaH264DecoderImpl::GetDecoderInfo() const { + VideoDecoder::DecoderInfo info; + info.implementation_name = "NVIDIA H264 Decoder"; + info.is_hardware_accelerated = true; + return info; +} + +bool NvidiaH264DecoderImpl::Configure(const Settings& settings) { + if (settings.codec_type() != kVideoCodecH264) { + RTC_LOG(LS_ERROR) + << "initialization failed on codectype is not kVideoCodecH264"; + return false; + } + if (!settings.max_render_resolution().Valid()) { + RTC_LOG(LS_ERROR) + << "initialization failed on codec_settings width < 0 or height < 0"; + return false; + } + + settings_ = settings; + + const CUresult result = cuCtxSetCurrent(cu_context_); + if (!ck(result)) { + RTC_LOG(LS_ERROR) << "initialization failed on cuCtxSetCurrent result" + << result; + return false; + } + + // todo(kazuki): Max resolution is differred each architecture. + // Refer to the table in Video Decoder Capabilities. + // https://docs.nvidia.com/video-technologies/video-codec-sdk/nvdec-video-decoder-api-prog-guide + int maxWidth = 4096; + int maxHeight = 4096; + + // bUseDeviceFrame: allocate in memory or cuda device memory + decoder_ = std::make_unique( + cu_context_, false, cudaVideoCodec_H264, true, false, nullptr, nullptr, + false, maxWidth, maxHeight); + return true; +} + +int32_t NvidiaH264DecoderImpl::RegisterDecodeCompleteCallback( + DecodedImageCallback* callback) { + this->decoded_complete_callback_ = callback; + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t NvidiaH264DecoderImpl::Release() { + buffer_pool_.Release(); + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t NvidiaH264DecoderImpl::Decode(const EncodedImage& input_image, + bool missing_frames, + int64_t render_time_ms) { + CUcontext current; + if (!ck(cuCtxGetCurrent(¤t))) { + RTC_LOG(LS_ERROR) << "decode failed on cuCtxGetCurrent is failed"; + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + if (current != cu_context_) { + RTC_LOG(LS_ERROR) + << "decode failed on not match current context and hold context"; + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + if (decoded_complete_callback_ == nullptr) { + RTC_LOG(LS_ERROR) << "decode failed on not set m_decodedCompleteCallback"; + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + if (!input_image.data() || !input_image.size()) { + RTC_LOG(LS_ERROR) << "decode failed on input image is null"; + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + + h264_bitstream_parser_.ParseBitstream(input_image); + std::optional qp = h264_bitstream_parser_.GetLastSliceQp(); + std::optional sps = h264_bitstream_parser_.sps(); + + if (is_configured_decoder_) { + if (!sps || + sps.value().width != static_cast(decoder_->GetWidth()) || + sps.value().height != static_cast(decoder_->GetHeight())) { + decoder_->setReconfigParams(nullptr, nullptr); + } + } + + int nFrameReturnd = 0; + try { + do { + nFrameReturnd = decoder_->Decode( + input_image.data(), static_cast(input_image.size()), + CUVID_PKT_TIMESTAMP, input_image.RtpTimestamp()); + } while (nFrameReturnd == 0); + } catch (const NVDECException& e) { + RTC_LOG(LS_ERROR) << "NVDEC H264 decode failed: " << e.what(); + decoder_.reset(); + is_configured_decoder_ = false; + return WEBRTC_VIDEO_CODEC_ERROR; + } + + is_configured_decoder_ = true; + + // todo: support other output format + // Chromium's H264 Encoder is output on NV12, so currently only NV12 is + // supported. + if (decoder_->GetOutputFormat() != cudaVideoSurfaceFormat_NV12) { + RTC_LOG(LS_ERROR) << "not supported this format: " + << decoder_->GetOutputFormat(); + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + + // Pass on color space from input frame if explicitly specified. + const ColorSpace& color_space = + input_image.ColorSpace() + ? *input_image.ColorSpace() + : ExtractH264ColorSpace(decoder_->GetVideoFormatInfo()); + + for (int i = 0; i < nFrameReturnd; i++) { + int64_t timeStamp; + uint8_t* pFrame = decoder_->GetFrame(&timeStamp); + + webrtc::scoped_refptr i420_buffer = + buffer_pool_.CreateI420Buffer(decoder_->GetWidth(), + decoder_->GetHeight()); + + int result; + { + result = libyuv::NV12ToI420( + pFrame, decoder_->GetDeviceFramePitch(), + pFrame + decoder_->GetHeight() * decoder_->GetDeviceFramePitch(), + decoder_->GetDeviceFramePitch(), i420_buffer->MutableDataY(), + i420_buffer->StrideY(), i420_buffer->MutableDataU(), + i420_buffer->StrideU(), i420_buffer->MutableDataV(), + i420_buffer->StrideV(), decoder_->GetWidth(), decoder_->GetHeight()); + } + + if (result) { + RTC_LOG(LS_INFO) << "libyuv::NV12ToI420 failed. error:" << result; + } + + VideoFrame decoded_frame = + VideoFrame::Builder() + .set_video_frame_buffer(i420_buffer) + .set_timestamp_rtp(static_cast(timeStamp)) + .set_color_space(color_space) + .build(); + + // todo: measurement decoding time + std::optional decodetime; + decoded_complete_callback_->Decoded(decoded_frame, decodetime, qp); + } + + return WEBRTC_VIDEO_CODEC_OK; +} + +} // end namespace webrtc diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h264_decoder_impl.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h264_decoder_impl.h new file mode 100644 index 0000000..4674a3b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h264_decoder_impl.h @@ -0,0 +1,57 @@ +#ifndef WEBRTC_NVIDIA_H264_DECODER_IMPL_H_ +#define WEBRTC_NVIDIA_H264_DECODER_IMPL_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "NvDecoder/NvDecoder.h" + +namespace webrtc { + +class H264BitstreamParserEx : public ::webrtc::H264BitstreamParser { + public: + std::optional sps() { return sps_; } + std::optional pps() { return pps_; } +}; + +class NvidiaH264DecoderImpl : public VideoDecoder { + public: + NvidiaH264DecoderImpl(CUcontext context); + NvidiaH264DecoderImpl(const NvidiaH264DecoderImpl&) = delete; + NvidiaH264DecoderImpl& operator=(const NvidiaH264DecoderImpl&) = delete; + ~NvidiaH264DecoderImpl() override; + + bool Configure(const Settings& settings) override; + int32_t Decode(const EncodedImage& input_image, + bool missing_frames, + int64_t render_time_ms) override; + int32_t RegisterDecodeCompleteCallback( + DecodedImageCallback* callback) override; + int32_t Release() override; + DecoderInfo GetDecoderInfo() const override; + + private: + CUcontext cu_context_; + std::unique_ptr decoder_; + bool is_configured_decoder_; + + Settings settings_; + + DecodedImageCallback* decoded_complete_callback_ = nullptr; + webrtc::VideoFrameBufferPool buffer_pool_; + H264BitstreamParserEx h264_bitstream_parser_; +}; + +} // end namespace webrtc + +#endif // WEBRTC_NVIDIA_H264_DECODER_IMPL_H_ \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h264_encoder_impl.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h264_encoder_impl.cpp new file mode 100644 index 0000000..90525c8 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h264_encoder_impl.cpp @@ -0,0 +1,550 @@ +#include "h264_encoder_impl.h" + + +#include +#include +#include +#include +#include + +#include "absl/strings/match.h" +#include "absl/types/optional.h" +#include "api/video/i420_buffer.h" +#include "api/video/nv12_buffer.h" +#include "api/video/video_codec_constants.h" +#include "api/video_codecs/scalability_mode.h" +#include +#include "common_video/libyuv/include/webrtc_libyuv.h" +#include "modules/video_coding/include/video_codec_interface.h" +#include "modules/video_coding/include/video_error_codes.h" +#include "modules/video_coding/svc/create_scalability_structure.h" +#include "modules/video_coding/utility/simulcast_rate_allocator.h" +#include "modules/video_coding/utility/simulcast_utility.h" +#include "native_gpu_encode_bridge.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/time_utils.h" +#include "system_wrappers/include/metrics.h" +#include "third_party/libyuv/include/libyuv/convert.h" +#include "third_party/libyuv/include/libyuv/scale.h" + +namespace webrtc { + +// Used by histograms. Values of entries should not be changed. +enum H264EncoderImplEvent { + kH264EncoderEventInit = 0, + kH264EncoderEventError = 1, + kH264EncoderEventMax = 16, +}; + +namespace { + +struct Nv12HostFrame { + const uint8_t* data = nullptr; + uint32_t stride = 0; + std::vector owned; +}; + +bool PrepareNv12HostFrame(const VideoFrame& input_frame, + Nv12HostFrame* out) { + const auto input_buffer = input_frame.video_frame_buffer(); + if (!input_buffer) { + return false; + } + + const int width = input_frame.width(); + const int height = input_frame.height(); + if (width < 2 || height < 2 || (width % 2) != 0 || (height % 2) != 0) { + return false; + } + if (input_buffer->type() == VideoFrameBuffer::Type::kNV12) { + const NV12BufferInterface* nv12 = input_buffer->GetNV12(); + if (!nv12 || nv12->width() != width || nv12->height() != height) { + return false; + } + out->data = nv12->DataY(); + out->stride = nv12->StrideY(); + return out->data != nullptr && nv12->DataUV() != nullptr && + nv12->StrideY() == nv12->StrideUV() && + nv12->DataUV() == nv12->DataY() + nv12->StrideY() * height; + } + + webrtc::scoped_refptr i420 = input_buffer->ToI420(); + if (!i420 || i420->width() != width || i420->height() != height) { + return false; + } + + const int chroma_width = (width + 1) / 2; + const int chroma_height = (height + 1) / 2; + out->stride = width; + out->owned.assign(width * height + width * chroma_height, 0); + uint8_t* y = out->owned.data(); + uint8_t* uv = y + width * height; + for (int row = 0; row < height; ++row) { + memcpy(y + row * width, i420->DataY() + row * i420->StrideY(), width); + } + for (int row = 0; row < chroma_height; ++row) { + const uint8_t* src_u = i420->DataU() + row * i420->StrideU(); + const uint8_t* src_v = i420->DataV() + row * i420->StrideV(); + uint8_t* dst_uv = uv + row * width; + for (int col = 0; col < chroma_width; ++col) { + dst_uv[col * 2] = src_u[col]; + dst_uv[col * 2 + 1] = src_v[col]; + } + } + out->data = out->owned.data(); + return true; +} + +} // namespace + + +NV_ENC_LEVEL H264LevelToNvEncLevel(webrtc::H264Level level) { + switch (level) { + case H264Level::kLevel1_b: + return NV_ENC_LEVEL_H264_1b; + case H264Level::kLevel1: + return NV_ENC_LEVEL_H264_1; + case H264Level::kLevel1_1: + return NV_ENC_LEVEL_H264_11; + case H264Level::kLevel1_2: + return NV_ENC_LEVEL_H264_12; + case H264Level::kLevel1_3: + return NV_ENC_LEVEL_H264_13; + case H264Level::kLevel2: + return NV_ENC_LEVEL_H264_2; + case H264Level::kLevel2_1: + return NV_ENC_LEVEL_H264_21; + case H264Level::kLevel2_2: + return NV_ENC_LEVEL_H264_22; + case H264Level::kLevel3: + return NV_ENC_LEVEL_H264_3; + case H264Level::kLevel3_1: + return NV_ENC_LEVEL_H264_31; + case H264Level::kLevel3_2: + return NV_ENC_LEVEL_H264_32; + case H264Level::kLevel4: + return NV_ENC_LEVEL_H264_4; + case H264Level::kLevel4_1: + return NV_ENC_LEVEL_H264_41; + case H264Level::kLevel4_2: + return NV_ENC_LEVEL_H264_42; + case H264Level::kLevel5: + return NV_ENC_LEVEL_H264_5; + case H264Level::kLevel5_1: + return NV_ENC_LEVEL_H264_51; + case H264Level::kLevel5_2: + return NV_ENC_LEVEL_H264_52; + } + return NV_ENC_LEVEL_AUTOSELECT; // Default value. +} + + +NvidiaH264EncoderImpl::NvidiaH264EncoderImpl( + const webrtc::Environment& env, + CUcontext context, + CUmemorytype memory_type, + NV_ENC_BUFFER_FORMAT nv_format, + const SdpVideoFormat& format) + : env_(env), + encoder_(nullptr), + cu_context_(context), + cu_memory_type_(memory_type), + cu_scaled_array_(nullptr), + nv_format_(nv_format), + packetization_mode_( + H264EncoderSettings::Parse(format).packetization_mode), + format_(format) { + std::string hexString = format_.parameters.at("profile-level-id"); + std::optional profile_level_id = + webrtc::ParseH264ProfileLevelId(hexString.c_str()); + if (profile_level_id.has_value()) { + profile_ = profile_level_id->profile; + level_ = profile_level_id->level; + } + + nv_enc_level_ = NV_ENC_LEVEL_AUTOSELECT; + if (level_ != H264Level::kLevel1_b) { + // Convert H264Level to NV_ENC_LEVEL. + nv_enc_level_ = webrtc::H264LevelToNvEncLevel(level_); + } + + RTC_CHECK_NE(cu_memory_type_, CU_MEMORYTYPE_HOST); +} + +NvidiaH264EncoderImpl::~NvidiaH264EncoderImpl() { + Release(); +} + +void NvidiaH264EncoderImpl::ReportInit() { + if (has_reported_init_) + return; + RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.H264EncoderImpl.Event", + kH264EncoderEventInit, kH264EncoderEventMax); + has_reported_init_ = true; +} + +void NvidiaH264EncoderImpl::ReportError() { + if (has_reported_error_) + return; + RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.H264EncoderImpl.Event", + kH264EncoderEventError, kH264EncoderEventMax); + has_reported_error_ = true; +} + +int32_t NvidiaH264EncoderImpl::InitEncode( + const VideoCodec* inst, + const VideoEncoder::Settings& settings) { + if (!inst || inst->codecType != kVideoCodecH264) { + ReportError(); + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + if (inst->maxFramerate == 0) { + ReportError(); + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + if (inst->width < 1 || inst->height < 1) { + ReportError(); + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + + int32_t release_ret = Release(); + if (release_ret != WEBRTC_VIDEO_CODEC_OK) { + ReportError(); + return release_ret; + } + + codec_ = *inst; + + // Code expects simulcastStream resolutions to be correct, make sure they are + // filled even when there are no simulcast layers. + if (codec_.numberOfSimulcastStreams == 0) { + codec_.simulcastStream[0].width = codec_.width; + codec_.simulcastStream[0].height = codec_.height; + } + + // Initialize encoded image. Default buffer size: size of unencoded data. + const size_t new_capacity = + CalcBufferSize(VideoType::kI420, codec_.width, codec_.height); + encoded_image_.SetEncodedData(EncodedImageBuffer::Create(new_capacity)); + encoded_image_._encodedWidth = codec_.width; + encoded_image_._encodedHeight = codec_.height; + encoded_image_.set_size(0); + + configuration_.sending = false; + configuration_.frame_dropping_on = codec_.GetFrameDropEnabled(); + configuration_.key_frame_interval = codec_.H264()->keyFrameInterval; + + configuration_.width = codec_.width; + configuration_.height = codec_.height; + + configuration_.max_frame_rate = codec_.maxFramerate; + configuration_.target_bps = codec_.startBitrate * 1000; + configuration_.max_bps = codec_.maxBitrate * 1000; + + const CUresult result = cuCtxSetCurrent(cu_context_); + if (result != CUDA_SUCCESS) { + return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; + } + + // Some NVIDIA GPUs have a limited Encode Session count. + // We can't get the Session count, so catching NvEncThrow to avoid the crash. + // refer: + // https://developer.nvidia.com/video-encode-and-decode-gpu-support-matrix-new + try { + if (cu_memory_type_ == CU_MEMORYTYPE_DEVICE) { + encoder_ = std::make_unique(cu_context_, codec_.width, + codec_.height, nv_format_, 0); + } else { + RTC_DCHECK_NOTREACHED(); + } + } catch (const NVENCException& e) { + // Surface initialization failure to WebRTC through the codec error return. + RTC_LOG(LS_ERROR) << "Failed Initialize NvEncoder " << e.what(); + return WEBRTC_VIDEO_CODEC_ERROR; + } + + nv_initialize_params_.version = NV_ENC_INITIALIZE_PARAMS_VER; + nv_encode_config_.version = NV_ENC_CONFIG_VER; + nv_initialize_params_.encodeConfig = &nv_encode_config_; + + GUID encodeGuid = NV_ENC_CODEC_H264_GUID; + GUID presetGuid = NV_ENC_PRESET_P4_GUID; + + encoder_->CreateDefaultEncoderParams(&nv_initialize_params_, encodeGuid, + presetGuid, + NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY); + + nv_initialize_params_.frameRateNum = + static_cast(configuration_.max_frame_rate); + nv_initialize_params_.frameRateDen = 1; + nv_initialize_params_.bufferFormat = nv_format_; + + nv_encode_config_.profileGUID = nv_profile_guid_; + nv_encode_config_.gopLength = NVENC_INFINITE_GOPLENGTH; + nv_encode_config_.frameIntervalP = 1; + nv_encode_config_.encodeCodecConfig.h264Config.level = nv_enc_level_; + nv_encode_config_.encodeCodecConfig.h264Config.idrPeriod = + NVENC_INFINITE_GOPLENGTH; + nv_encode_config_.rcParams.version = NV_ENC_RC_PARAMS_VER; + nv_encode_config_.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CBR; + nv_encode_config_.rcParams.averageBitRate = configuration_.target_bps; + nv_encode_config_.rcParams.vbvBufferSize = + (nv_encode_config_.rcParams.averageBitRate * + nv_initialize_params_.frameRateDen / + nv_initialize_params_.frameRateNum) * + 5; + nv_encode_config_.rcParams.vbvInitialDelay = + nv_encode_config_.rcParams.vbvBufferSize; + + try { + encoder_->CreateEncoder(&nv_initialize_params_); + } catch (const NVENCException& e) { + RTC_LOG(LS_ERROR) << "Failed Initialize NvEncoder " << e.what(); + return WEBRTC_VIDEO_CODEC_ERROR; + } + + RTC_LOG(LS_INFO) << "NVIDIA H264 NVENC initialized: " + << codec_.width << "x" << codec_.height + << " @ " << codec_.maxFramerate << "fps, target_bps=" + << configuration_.target_bps; + + SimulcastRateAllocator init_allocator(env_, codec_); + VideoBitrateAllocation allocation = + init_allocator.Allocate(VideoBitrateAllocationParameters( + DataRate::KilobitsPerSec(codec_.startBitrate), codec_.maxFramerate)); + SetRates(RateControlParameters(allocation, codec_.maxFramerate)); + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t NvidiaH264EncoderImpl::RegisterEncodeCompleteCallback( + EncodedImageCallback* callback) { + encoded_image_callback_ = callback; + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t NvidiaH264EncoderImpl::Release() { + if (encoder_) { + encoder_->DestroyEncoder(); + encoder_ = nullptr; + } + if (cu_scaled_array_) { + cuArrayDestroy(cu_scaled_array_); + cu_scaled_array_ = nullptr; + } + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t NvidiaH264EncoderImpl::Encode( + const VideoFrame& input_frame, + const std::vector* frame_types) { + if (!encoder_) { + ReportError(); + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + if (!encoded_image_callback_) { + RTC_LOG(LS_WARNING) + << "InitEncode() has been called, but a callback function " + "has not been set with RegisterEncodeCompleteCallback()"; + ReportError(); + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + + bool is_keyframe_needed = false; + if (configuration_.key_frame_request && configuration_.sending) { + is_keyframe_needed = true; + } + + bool send_key_frame = + is_keyframe_needed || + (frame_types && (*frame_types)[0] == VideoFrameType::kVideoFrameKey); + if (send_key_frame) { + is_keyframe_needed = true; + configuration_.key_frame_request = false; + } + + RTC_DCHECK_EQ(configuration_.width, input_frame.width()); + RTC_DCHECK_EQ(configuration_.height, input_frame.height()); + + if (!configuration_.sending) { + return WEBRTC_VIDEO_CODEC_NO_OUTPUT; + } + + if (frame_types != nullptr) { + // Skip frame? + if ((*frame_types)[0] == VideoFrameType::kEmptyFrame) { + return WEBRTC_VIDEO_CODEC_NO_OUTPUT; + } + } + + NV_ENC_PIC_PARAMS pic_params = NV_ENC_PIC_PARAMS(); + pic_params.version = NV_ENC_PIC_PARAMS_VER; + pic_params.encodePicFlags = 0; + if (is_keyframe_needed) { + pic_params.encodePicFlags = NV_ENC_PIC_FLAG_FORCEINTRA | + NV_ENC_PIC_FLAG_FORCEIDR | + NV_ENC_PIC_FLAG_OUTPUT_SPSPPS; + configuration_.key_frame_request = false; + } + + try { + std::vector> bit_stream; + if (TryEncodeNativeGpuFrame(encoder_.get(), cu_context_, input_frame, + &pic_params, &bit_stream)) { + for (std::vector& packet : bit_stream) { + int32_t result = ProcessEncodedFrame(packet, input_frame); + if (result != WEBRTC_VIDEO_CODEC_OK) { + return result; + } + } + return WEBRTC_VIDEO_CODEC_OK; + } + } catch (const NVENCException& e) { + RTC_LOG(LS_ERROR) << "Failed native GPU EncodeFrame NvEncoder " + << e.what(); + return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; + } + + Nv12HostFrame frame_buffer; + if (!PrepareNv12HostFrame(input_frame, &frame_buffer)) { + const auto failed_buffer = input_frame.video_frame_buffer(); + RTC_LOG(LS_ERROR) << "Failed to convert " + << (failed_buffer + ? VideoFrameBufferTypeToString( + failed_buffer->type()) + : "null") + << " image to NV12. Can't encode frame."; + return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; + } + + try { + const NvEncInputFrame* nv_enc_input_frame = encoder_->GetNextInputFrame(); + + if (cu_memory_type_ == CU_MEMORYTYPE_DEVICE) { + if (nv_enc_input_frame->bufferFormat != NV_ENC_BUFFER_FORMAT_NV12) { + RTC_LOG(LS_ERROR) << "NVIDIA encoder expected NV12 input"; + return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; + } + NvEncoderCuda::CopyToDeviceFrame( + cu_context_, (void*)frame_buffer.data, frame_buffer.stride, + reinterpret_cast(nv_enc_input_frame->inputPtr), + nv_enc_input_frame->pitch, input_frame.width(), input_frame.height(), + CU_MEMORYTYPE_HOST, nv_enc_input_frame->bufferFormat, + nv_enc_input_frame->chromaOffsets, nv_enc_input_frame->numChromaPlanes); + } + + std::vector> bit_stream; + encoder_->EncodeFrame(bit_stream, &pic_params); + + for (std::vector& packet : bit_stream) { + int32_t result = ProcessEncodedFrame(packet, input_frame); + if (result != WEBRTC_VIDEO_CODEC_OK) { + return result; + } + } + } catch (const NVENCException& e) { + RTC_LOG(LS_ERROR) << "Failed EncodeFrame NvEncoder " << e.what(); + return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; + } + + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t NvidiaH264EncoderImpl::ProcessEncodedFrame( + std::vector& packet, + const ::webrtc::VideoFrame& inputFrame) { + encoded_image_._encodedWidth = encoder_->GetEncodeWidth(); + encoded_image_._encodedHeight = encoder_->GetEncodeHeight(); + encoded_image_.SetRtpTimestamp(inputFrame.rtp_timestamp()); + encoded_image_.SetSimulcastIndex(0); + encoded_image_.ntp_time_ms_ = inputFrame.ntp_time_ms(); + encoded_image_.capture_time_ms_ = inputFrame.render_time_ms(); + encoded_image_.rotation_ = inputFrame.rotation(); + encoded_image_.content_type_ = VideoContentType::UNSPECIFIED; + encoded_image_.timing_.flags = VideoSendTiming::kInvalid; + encoded_image_._frameType = VideoFrameType::kVideoFrameDelta; + encoded_image_.SetColorSpace(inputFrame.color_space()); + std::vector naluIndices = + H264::FindNaluIndices(MakeArrayView(packet.data(), packet.size())); + for (uint32_t i = 0; i < naluIndices.size(); i++) { + const H264::NaluType naluType = + H264::ParseNaluType(packet[naluIndices[i].payload_start_offset]); + if (naluType == H264::kIdr) { + encoded_image_._frameType = VideoFrameType::kVideoFrameKey; + break; + } + } + + encoded_image_.SetEncodedData( + EncodedImageBuffer::Create(packet.data(), packet.size())); + encoded_image_.set_size(packet.size()); + + h264_bitstream_parser_.ParseBitstream(encoded_image_); + encoded_image_.qp_ = h264_bitstream_parser_.GetLastSliceQp().value_or(-1); + + CodecSpecificInfo codecInfo; + codecInfo.codecType = kVideoCodecH264; + codecInfo.codecSpecific.H264.packetization_mode = + H264PacketizationMode::NonInterleaved; + + const auto result = + encoded_image_callback_->OnEncodedImage(encoded_image_, &codecInfo); + if (result.error != EncodedImageCallback::Result::OK) { + RTC_LOG(LS_ERROR) << "Encode m_encodedCompleteCallback failed " + << result.error; + return WEBRTC_VIDEO_CODEC_ERROR; + } + return WEBRTC_VIDEO_CODEC_OK; +} + +VideoEncoder::EncoderInfo NvidiaH264EncoderImpl::GetEncoderInfo() const { + EncoderInfo info; + info.supports_native_handle = false; + info.implementation_name = "NVIDIA H264 Encoder"; + info.scaling_settings = VideoEncoder::ScalingSettings::kOff; + info.is_hardware_accelerated = true; + info.supports_simulcast = false; + info.preferred_pixel_formats = {VideoFrameBuffer::Type::kNV12, + VideoFrameBuffer::Type::kI420}; + return info; +} + +void NvidiaH264EncoderImpl::SetRates( + const RateControlParameters& parameters) { + if (!encoder_) { + RTC_LOG(LS_WARNING) << "SetRates() while uninitialized."; + return; + } + + if (parameters.framerate_fps < 1.0) { + RTC_LOG(LS_WARNING) << "Invalid frame rate: " << parameters.framerate_fps; + return; + } + + if (parameters.bitrate.get_sum_bps() == 0) { + configuration_.SetStreamState(false); + return; + } + + codec_.maxFramerate = static_cast(parameters.framerate_fps); + codec_.maxBitrate = parameters.bitrate.GetSpatialLayerSum(0); + + configuration_.target_bps = parameters.bitrate.GetSpatialLayerSum(0); + configuration_.max_frame_rate = parameters.framerate_fps; + + if (configuration_.target_bps) { + configuration_.SetStreamState(true); + } else { + configuration_.SetStreamState(false); + } +} + +void NvidiaH264EncoderImpl::LayerConfig::SetStreamState(bool send_stream) { + if (send_stream && !sending) { + // Need a key frame if we have not sent this stream before. + key_frame_request = true; + } + sending = send_stream; +} + +} // namespace webrtc diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h264_encoder_impl.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h264_encoder_impl.h new file mode 100644 index 0000000..b50a7e3 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h264_encoder_impl.h @@ -0,0 +1,99 @@ +#ifndef WEBRTC_NVIDIA_H264_ENCODER_IMPL_H_ +#define WEBRTC_NVIDIA_H264_ENCODER_IMPL_H_ + +#include + +#include +#include + +#include "NvEncoder/NvEncoder.h" +#include "NvEncoder/NvEncoderCuda.h" + +#include "absl/container/inlined_vector.h" +#include "api/transport/rtp/dependency_descriptor.h" +#include "api/video/i420_buffer.h" +#include "api/video/video_codec_constants.h" +#include "api/video_codecs/scalability_mode.h" +#include "api/video_codecs/video_encoder.h" +#include "common_video/h264/h264_bitstream_parser.h" +#include "modules/video_coding/codecs/h264/include/h264.h" +#include "modules/video_coding/svc/scalable_video_controller.h" +#include "modules/video_coding/utility/quality_scaler.h" + +namespace webrtc { + +class NvidiaH264EncoderImpl : public VideoEncoder { + public: + struct LayerConfig { + int simulcast_idx = 0; + int width = -1; + int height = -1; + bool sending = true; + bool key_frame_request = false; + float max_frame_rate = 0; + uint32_t target_bps = 0; + uint32_t max_bps = 0; + bool frame_dropping_on = false; + int key_frame_interval = 0; + int num_temporal_layers = 1; + + void SetStreamState(bool send_stream); + }; + + public: + NvidiaH264EncoderImpl(const webrtc::Environment& env, + CUcontext context, + CUmemorytype memory_type, + NV_ENC_BUFFER_FORMAT nv_format, + const SdpVideoFormat& format); + ~NvidiaH264EncoderImpl() override; + + int32_t InitEncode(const VideoCodec* codec_settings, + const Settings& settings) override; + + int32_t RegisterEncodeCompleteCallback( + EncodedImageCallback* callback) override; + + int32_t Release() override; + + int32_t Encode(const VideoFrame& frame, + const std::vector* frame_types) override; + + void SetRates(const RateControlParameters& rc_parameters) override; + + EncoderInfo GetEncoderInfo() const override; + + private: + int32_t ProcessEncodedFrame(std::vector& packet, + const ::webrtc::VideoFrame& inputFrame); + private: + const webrtc::Environment& env_; + EncodedImageCallback* encoded_image_callback_ = nullptr; + + std::unique_ptr encoder_; + CUcontext cu_context_; + CUmemorytype cu_memory_type_; + CUarray cu_scaled_array_; + NV_ENC_BUFFER_FORMAT nv_format_; + NV_ENC_INITIALIZE_PARAMS nv_initialize_params_; + NV_ENC_CONFIG nv_encode_config_; + GUID nv_profile_guid_; + NV_ENC_LEVEL nv_enc_level_; + + LayerConfig configuration_; + EncodedImage encoded_image_; + H264PacketizationMode packetization_mode_; + VideoCodec codec_; + void ReportInit(); + void ReportError(); + bool has_reported_init_ = false; + bool has_reported_error_ = false; + webrtc::H264BitstreamParser h264_bitstream_parser_; + const SdpVideoFormat format_; + H264Profile profile_ = H264Profile::kProfileConstrainedBaseline; + H264Level level_ = H264Level::kLevel1_b; +}; + +} // namespace webrtc + +#endif // WEBRTC_NVIDIA_H264_ENCODER_IMPL_H_ diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h265_decoder_impl.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h265_decoder_impl.cpp new file mode 100644 index 0000000..c42bcd9 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h265_decoder_impl.cpp @@ -0,0 +1,168 @@ +#include "h265_decoder_impl.h" + +#include +#include +#include +#include + +#include "NvDecoder/NvDecoder.h" +#include "Utils/NvCodecUtils.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +static ColorSpace ExtractColorSpaceFromFormat(const CUVIDEOFORMAT& format) { + return ColorSpace( + static_cast( + format.video_signal_description.color_primaries), + static_cast( + format.video_signal_description.transfer_characteristics), + static_cast( + format.video_signal_description.matrix_coefficients), + static_cast( + format.video_signal_description.video_full_range_flag)); +} + +NvidiaH265DecoderImpl::NvidiaH265DecoderImpl(CUcontext context) + : cu_context_(context), + decoder_(nullptr), + is_configured_decoder_(false), + decoded_complete_callback_(nullptr), + buffer_pool_(false) {} + +NvidiaH265DecoderImpl::~NvidiaH265DecoderImpl() { Release(); } + +VideoDecoder::DecoderInfo NvidiaH265DecoderImpl::GetDecoderInfo() const { + VideoDecoder::DecoderInfo info; + info.implementation_name = "NVIDIA H265 Decoder"; + info.is_hardware_accelerated = true; + return info; +} + +bool NvidiaH265DecoderImpl::Configure(const Settings& settings) { + if (settings.codec_type() != kVideoCodecH265) { + RTC_LOG(LS_ERROR) << "initialization failed: codec type is not H265"; + return false; + } + if (!settings.max_render_resolution().Valid()) { + RTC_LOG(LS_ERROR) + << "initialization failed on codec_settings width < 0 or height < 0"; + return false; + } + + settings_ = settings; + + const CUresult result = cuCtxSetCurrent(cu_context_); + if (!ck(result)) { + RTC_LOG(LS_ERROR) << "initialization failed on cuCtxSetCurrent result" + << result; + return false; + } + + int maxWidth = 4096; + int maxHeight = 4096; + + decoder_ = std::make_unique( + cu_context_, false, cudaVideoCodec_HEVC, true, false, nullptr, nullptr, + false, maxWidth, maxHeight); + return true; +} + +int32_t NvidiaH265DecoderImpl::RegisterDecodeCompleteCallback( + DecodedImageCallback* callback) { + decoded_complete_callback_ = callback; + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t NvidiaH265DecoderImpl::Release() { + buffer_pool_.Release(); + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t NvidiaH265DecoderImpl::Decode(const EncodedImage& input_image, + bool /*missing_frames*/, + int64_t /*render_time_ms*/) { + CUcontext current; + if (!ck(cuCtxGetCurrent(¤t))) { + RTC_LOG(LS_ERROR) << "decode failed on cuCtxGetCurrent"; + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + if (current != cu_context_) { + RTC_LOG(LS_ERROR) + << "decode failed: current context does not match held context"; + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + if (decoded_complete_callback_ == nullptr) { + RTC_LOG(LS_ERROR) << "decode failed: decoded_complete_callback_ not set"; + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + if (!input_image.data() || !input_image.size()) { + RTC_LOG(LS_ERROR) << "decode failed: input image is null"; + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + + int nFrameReturned = 0; + try { + do { + nFrameReturned = decoder_->Decode( + input_image.data(), static_cast(input_image.size()), + CUVID_PKT_TIMESTAMP, input_image.RtpTimestamp()); + } while (nFrameReturned == 0); + } catch (const NVDECException& e) { + RTC_LOG(LS_ERROR) << "NVDEC H265 decode failed: " << e.what(); + decoder_.reset(); + is_configured_decoder_ = false; + return WEBRTC_VIDEO_CODEC_ERROR; + } + + is_configured_decoder_ = true; + + if (decoder_->GetOutputFormat() != cudaVideoSurfaceFormat_NV12) { + RTC_LOG(LS_ERROR) << "not supported output format: " + << decoder_->GetOutputFormat(); + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + + const ColorSpace& color_space = + input_image.ColorSpace() ? *input_image.ColorSpace() + : ExtractColorSpaceFromFormat( + decoder_->GetVideoFormatInfo()); + + for (int i = 0; i < nFrameReturned; i++) { + int64_t timeStamp; + uint8_t* pFrame = decoder_->GetFrame(&timeStamp); + + webrtc::scoped_refptr i420_buffer = + buffer_pool_.CreateI420Buffer(decoder_->GetWidth(), + decoder_->GetHeight()); + + int result = libyuv::NV12ToI420( + pFrame, decoder_->GetDeviceFramePitch(), + pFrame + decoder_->GetHeight() * decoder_->GetDeviceFramePitch(), + decoder_->GetDeviceFramePitch(), i420_buffer->MutableDataY(), + i420_buffer->StrideY(), i420_buffer->MutableDataU(), + i420_buffer->StrideU(), i420_buffer->MutableDataV(), + i420_buffer->StrideV(), decoder_->GetWidth(), decoder_->GetHeight()); + + if (result) { + RTC_LOG(LS_INFO) << "libyuv::NV12ToI420 failed. error:" << result; + } + + VideoFrame decoded_frame = VideoFrame::Builder() + .set_video_frame_buffer(i420_buffer) + .set_timestamp_rtp(static_cast( + timeStamp)) + .set_color_space(color_space) + .build(); + + std::optional decodetime; + std::optional qp; // Not parsed for H265 currently + decoded_complete_callback_->Decoded(decoded_frame, decodetime, qp); + } + + return WEBRTC_VIDEO_CODEC_OK; +} + +} // end namespace webrtc + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h265_decoder_impl.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h265_decoder_impl.h new file mode 100644 index 0000000..54dcbfc --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h265_decoder_impl.h @@ -0,0 +1,47 @@ +#ifndef WEBRTC_NVIDIA_H265_DECODER_IMPL_H_ +#define WEBRTC_NVIDIA_H265_DECODER_IMPL_H_ + +#include +#include +#include +#include +#include +#include +#include + +#include "NvDecoder/NvDecoder.h" + +namespace webrtc { + +class NvidiaH265DecoderImpl : public VideoDecoder { + public: + explicit NvidiaH265DecoderImpl(CUcontext context); + NvidiaH265DecoderImpl(const NvidiaH265DecoderImpl&) = delete; + NvidiaH265DecoderImpl& operator=(const NvidiaH265DecoderImpl&) = delete; + ~NvidiaH265DecoderImpl() override; + + bool Configure(const Settings& settings) override; + int32_t Decode(const EncodedImage& input_image, + bool missing_frames, + int64_t render_time_ms) override; + int32_t RegisterDecodeCompleteCallback( + DecodedImageCallback* callback) override; + int32_t Release() override; + DecoderInfo GetDecoderInfo() const override; + + private: + CUcontext cu_context_; + std::unique_ptr decoder_; + bool is_configured_decoder_; + + Settings settings_; + + DecodedImageCallback* decoded_complete_callback_ = nullptr; + webrtc::VideoFrameBufferPool buffer_pool_; +}; + +} // end namespace webrtc + +#endif // WEBRTC_NVIDIA_H265_DECODER_IMPL_H_ + + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h265_encoder_impl.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h265_encoder_impl.cpp new file mode 100644 index 0000000..d913758 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h265_encoder_impl.cpp @@ -0,0 +1,474 @@ +#include "h265_encoder_impl.h" + +#include +#include +#include +#include +#include + +#include "absl/strings/match.h" +#include "absl/types/optional.h" +#include "api/video/i420_buffer.h" +#include "api/video/nv12_buffer.h" +#include "api/video/video_codec_constants.h" +#include "api/video_codecs/scalability_mode.h" +#include "common_video/libyuv/include/webrtc_libyuv.h" +#include "modules/video_coding/include/video_codec_interface.h" +#include "modules/video_coding/include/video_error_codes.h" +#include "modules/video_coding/svc/create_scalability_structure.h" +#include "modules/video_coding/utility/simulcast_rate_allocator.h" +#include "modules/video_coding/utility/simulcast_utility.h" +#include "native_gpu_encode_bridge.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/time_utils.h" +#include "system_wrappers/include/metrics.h" +#include "third_party/libyuv/include/libyuv/convert.h" +#include "third_party/libyuv/include/libyuv/scale.h" + +namespace webrtc { + +// Used by histograms. Values of entries should not be changed. +enum H265EncoderImplEvent { + kH265EncoderEventInit = 0, + kH265EncoderEventError = 1, + kH265EncoderEventMax = 16, +}; + +namespace { + +struct Nv12HostFrame { + const uint8_t* data = nullptr; + uint32_t stride = 0; + std::vector owned; +}; + +bool PrepareNv12HostFrame(const VideoFrame& input_frame, + Nv12HostFrame* out) { + const auto input_buffer = input_frame.video_frame_buffer(); + if (!input_buffer) { + return false; + } + + const int width = input_frame.width(); + const int height = input_frame.height(); + if (width < 2 || height < 2 || (width % 2) != 0 || (height % 2) != 0) { + return false; + } + if (input_buffer->type() == VideoFrameBuffer::Type::kNV12) { + const NV12BufferInterface* nv12 = input_buffer->GetNV12(); + if (!nv12 || nv12->width() != width || nv12->height() != height) { + return false; + } + out->data = nv12->DataY(); + out->stride = nv12->StrideY(); + return out->data != nullptr && nv12->DataUV() != nullptr && + nv12->StrideY() == nv12->StrideUV() && + nv12->DataUV() == nv12->DataY() + nv12->StrideY() * height; + } + + webrtc::scoped_refptr i420 = input_buffer->ToI420(); + if (!i420 || i420->width() != width || i420->height() != height) { + return false; + } + + const int chroma_width = (width + 1) / 2; + const int chroma_height = (height + 1) / 2; + out->stride = width; + out->owned.assign(width * height + width * chroma_height, 0); + uint8_t* y = out->owned.data(); + uint8_t* uv = y + width * height; + for (int row = 0; row < height; ++row) { + memcpy(y + row * width, i420->DataY() + row * i420->StrideY(), width); + } + for (int row = 0; row < chroma_height; ++row) { + const uint8_t* src_u = i420->DataU() + row * i420->StrideU(); + const uint8_t* src_v = i420->DataV() + row * i420->StrideV(); + uint8_t* dst_uv = uv + row * width; + for (int col = 0; col < chroma_width; ++col) { + dst_uv[col * 2] = src_u[col]; + dst_uv[col * 2 + 1] = src_v[col]; + } + } + out->data = out->owned.data(); + return true; +} + +} // namespace + +NvidiaH265EncoderImpl::NvidiaH265EncoderImpl( + const webrtc::Environment& env, + CUcontext context, + CUmemorytype memory_type, + NV_ENC_BUFFER_FORMAT nv_format, + const SdpVideoFormat& format) + : env_(env), + encoder_(nullptr), + cu_context_(context), + cu_memory_type_(memory_type), + cu_scaled_array_(nullptr), + nv_format_(nv_format), + format_(format) { + RTC_CHECK_NE(cu_memory_type_, CU_MEMORYTYPE_HOST); +} + +NvidiaH265EncoderImpl::~NvidiaH265EncoderImpl() { + Release(); +} + +void NvidiaH265EncoderImpl::ReportInit() { + if (has_reported_init_) + return; + RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.H265EncoderImpl.Event", + kH265EncoderEventInit, kH265EncoderEventMax); + has_reported_init_ = true; +} + +void NvidiaH265EncoderImpl::ReportError() { + if (has_reported_error_) + return; + RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.H265EncoderImpl.Event", + kH265EncoderEventError, kH265EncoderEventMax); + has_reported_error_ = true; +} + +int32_t NvidiaH265EncoderImpl::InitEncode( + const VideoCodec* inst, + const VideoEncoder::Settings& settings) { + if (!inst || inst->codecType != kVideoCodecH265) { + ReportError(); + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + if (inst->maxFramerate == 0) { + ReportError(); + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + if (inst->width < 1 || inst->height < 1) { + ReportError(); + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + + int32_t release_ret = Release(); + if (release_ret != WEBRTC_VIDEO_CODEC_OK) { + ReportError(); + return release_ret; + } + + codec_ = *inst; + + if (codec_.numberOfSimulcastStreams == 0) { + codec_.simulcastStream[0].width = codec_.width; + codec_.simulcastStream[0].height = codec_.height; + } + + const size_t new_capacity = + CalcBufferSize(VideoType::kI420, codec_.width, codec_.height); + encoded_image_.SetEncodedData(EncodedImageBuffer::Create(new_capacity)); + encoded_image_._encodedWidth = codec_.width; + encoded_image_._encodedHeight = codec_.height; + encoded_image_.set_size(0); + + configuration_.sending = false; + configuration_.frame_dropping_on = codec_.GetFrameDropEnabled(); + configuration_.key_frame_interval = 0; + + configuration_.width = codec_.width; + configuration_.height = codec_.height; + + configuration_.max_frame_rate = codec_.maxFramerate; + configuration_.target_bps = codec_.startBitrate * 1000; + configuration_.max_bps = codec_.maxBitrate * 1000; + + const CUresult result = cuCtxSetCurrent(cu_context_); + if (result != CUDA_SUCCESS) { + return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; + } + + try { + if (cu_memory_type_ == CU_MEMORYTYPE_DEVICE) { + encoder_ = std::make_unique(cu_context_, codec_.width, + codec_.height, nv_format_, 0); + } else { + RTC_DCHECK_NOTREACHED(); + } + } catch (const NVENCException& e) { + RTC_LOG(LS_ERROR) << "Failed Initialize NvEncoder " << e.what(); + return WEBRTC_VIDEO_CODEC_ERROR; + } + + nv_initialize_params_.version = NV_ENC_INITIALIZE_PARAMS_VER; + nv_encode_config_.version = NV_ENC_CONFIG_VER; + nv_initialize_params_.encodeConfig = &nv_encode_config_; + + GUID encodeGuid = NV_ENC_CODEC_HEVC_GUID; + GUID presetGuid = NV_ENC_PRESET_P4_GUID; + + encoder_->CreateDefaultEncoderParams(&nv_initialize_params_, encodeGuid, + presetGuid, + NV_ENC_TUNING_INFO_ULTRA_LOW_LATENCY); + + nv_initialize_params_.frameRateNum = + static_cast(configuration_.max_frame_rate); + nv_initialize_params_.frameRateDen = 1; + nv_initialize_params_.bufferFormat = nv_format_; + + nv_encode_config_.profileGUID = NV_ENC_HEVC_PROFILE_MAIN_GUID; + nv_encode_config_.gopLength = NVENC_INFINITE_GOPLENGTH; + nv_encode_config_.frameIntervalP = 1; + nv_encode_config_.rcParams.version = NV_ENC_RC_PARAMS_VER; + nv_encode_config_.rcParams.rateControlMode = NV_ENC_PARAMS_RC_CBR; + nv_encode_config_.rcParams.averageBitRate = configuration_.target_bps; + nv_encode_config_.rcParams.vbvBufferSize = + (nv_encode_config_.rcParams.averageBitRate * + nv_initialize_params_.frameRateDen / + nv_initialize_params_.frameRateNum) * + 5; + nv_encode_config_.rcParams.vbvInitialDelay = + nv_encode_config_.rcParams.vbvBufferSize; + + try { + encoder_->CreateEncoder(&nv_initialize_params_); + } catch (const NVENCException& e) { + RTC_LOG(LS_ERROR) << "Failed Initialize NvEncoder " << e.what(); + return WEBRTC_VIDEO_CODEC_ERROR; + } + + RTC_LOG(LS_INFO) << "NVIDIA H265/HEVC NVENC initialized: " + << codec_.width << "x" << codec_.height + << " @ " << codec_.maxFramerate << "fps, target_bps=" + << configuration_.target_bps; + + SimulcastRateAllocator init_allocator(env_, codec_); + VideoBitrateAllocation allocation = + init_allocator.Allocate(VideoBitrateAllocationParameters( + DataRate::KilobitsPerSec(codec_.startBitrate), codec_.maxFramerate)); + SetRates(RateControlParameters(allocation, codec_.maxFramerate)); + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t NvidiaH265EncoderImpl::RegisterEncodeCompleteCallback( + EncodedImageCallback* callback) { + encoded_image_callback_ = callback; + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t NvidiaH265EncoderImpl::Release() { + if (encoder_) { + encoder_->DestroyEncoder(); + encoder_ = nullptr; + } + if (cu_scaled_array_) { + cuArrayDestroy(cu_scaled_array_); + cu_scaled_array_ = nullptr; + } + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t NvidiaH265EncoderImpl::Encode( + const VideoFrame& input_frame, + const std::vector* frame_types) { + if (!encoder_) { + ReportError(); + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + if (!encoded_image_callback_) { + RTC_LOG(LS_WARNING) + << "InitEncode() has been called, but a callback function " + "has not been set with RegisterEncodeCompleteCallback()"; + ReportError(); + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + + bool is_keyframe_needed = false; + if (configuration_.key_frame_request && configuration_.sending) { + is_keyframe_needed = true; + } + + bool send_key_frame = + is_keyframe_needed || + (frame_types && (*frame_types)[0] == VideoFrameType::kVideoFrameKey); + if (send_key_frame) { + is_keyframe_needed = true; + configuration_.key_frame_request = false; + } + + RTC_DCHECK_EQ(configuration_.width, input_frame.width()); + RTC_DCHECK_EQ(configuration_.height, input_frame.height()); + + if (!configuration_.sending) { + return WEBRTC_VIDEO_CODEC_NO_OUTPUT; + } + + if (frame_types != nullptr) { + if ((*frame_types)[0] == VideoFrameType::kEmptyFrame) { + return WEBRTC_VIDEO_CODEC_NO_OUTPUT; + } + } + + NV_ENC_PIC_PARAMS pic_params = NV_ENC_PIC_PARAMS(); + pic_params.version = NV_ENC_PIC_PARAMS_VER; + pic_params.encodePicFlags = 0; + if (is_keyframe_needed) { + pic_params.encodePicFlags = NV_ENC_PIC_FLAG_FORCEINTRA | + NV_ENC_PIC_FLAG_FORCEIDR | + NV_ENC_PIC_FLAG_OUTPUT_SPSPPS; + configuration_.key_frame_request = false; + } + + try { + current_encoding_is_keyframe_ = is_keyframe_needed; + std::vector> bit_stream; + if (TryEncodeNativeGpuFrame(encoder_.get(), cu_context_, input_frame, + &pic_params, &bit_stream)) { + for (std::vector& packet : bit_stream) { + int32_t result = ProcessEncodedFrame(packet, input_frame); + if (result != WEBRTC_VIDEO_CODEC_OK) { + current_encoding_is_keyframe_ = false; + return result; + } + } + current_encoding_is_keyframe_ = false; + return WEBRTC_VIDEO_CODEC_OK; + } + current_encoding_is_keyframe_ = false; + } catch (const NVENCException& e) { + current_encoding_is_keyframe_ = false; + RTC_LOG(LS_ERROR) << "Failed native GPU EncodeFrame NvEncoder " + << e.what(); + return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; + } + + Nv12HostFrame frame_buffer; + if (!PrepareNv12HostFrame(input_frame, &frame_buffer)) { + const auto failed_buffer = input_frame.video_frame_buffer(); + RTC_LOG(LS_ERROR) << "Failed to convert " + << (failed_buffer + ? VideoFrameBufferTypeToString( + failed_buffer->type()) + : "null") + << " image to NV12. Can't encode frame."; + return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; + } + + try { + const NvEncInputFrame* nv_enc_input_frame = encoder_->GetNextInputFrame(); + + if (cu_memory_type_ == CU_MEMORYTYPE_DEVICE) { + if (nv_enc_input_frame->bufferFormat != NV_ENC_BUFFER_FORMAT_NV12) { + RTC_LOG(LS_ERROR) << "NVIDIA encoder expected NV12 input"; + return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; + } + NvEncoderCuda::CopyToDeviceFrame( + cu_context_, (void*)frame_buffer.data, frame_buffer.stride, + reinterpret_cast(nv_enc_input_frame->inputPtr), + nv_enc_input_frame->pitch, input_frame.width(), input_frame.height(), + CU_MEMORYTYPE_HOST, nv_enc_input_frame->bufferFormat, + nv_enc_input_frame->chromaOffsets, nv_enc_input_frame->numChromaPlanes); + } + + current_encoding_is_keyframe_ = is_keyframe_needed; + + std::vector> bit_stream; + encoder_->EncodeFrame(bit_stream, &pic_params); + + for (std::vector& packet : bit_stream) { + int32_t result = ProcessEncodedFrame(packet, input_frame); + if (result != WEBRTC_VIDEO_CODEC_OK) { + return result; + } + } + current_encoding_is_keyframe_ = false; + } catch (const NVENCException& e) { + RTC_LOG(LS_ERROR) << "Failed EncodeFrame NvEncoder " << e.what(); + return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; + } + + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t NvidiaH265EncoderImpl::ProcessEncodedFrame( + std::vector& packet, + const ::webrtc::VideoFrame& inputFrame) { + encoded_image_._encodedWidth = encoder_->GetEncodeWidth(); + encoded_image_._encodedHeight = encoder_->GetEncodeHeight(); + encoded_image_.SetRtpTimestamp(inputFrame.rtp_timestamp()); + encoded_image_.SetSimulcastIndex(0); + encoded_image_.ntp_time_ms_ = inputFrame.ntp_time_ms(); + encoded_image_.capture_time_ms_ = inputFrame.render_time_ms(); + encoded_image_.rotation_ = inputFrame.rotation(); + encoded_image_.content_type_ = VideoContentType::UNSPECIFIED; + encoded_image_.timing_.flags = VideoSendTiming::kInvalid; + encoded_image_._frameType = + current_encoding_is_keyframe_ ? VideoFrameType::kVideoFrameKey + : VideoFrameType::kVideoFrameDelta; + encoded_image_.SetColorSpace(inputFrame.color_space()); + + encoded_image_.SetEncodedData( + EncodedImageBuffer::Create(packet.data(), packet.size())); + encoded_image_.set_size(packet.size()); + + encoded_image_.qp_ = -1; + + CodecSpecificInfo codecInfo; + codecInfo.codecType = kVideoCodecH265; + + const auto result = + encoded_image_callback_->OnEncodedImage(encoded_image_, &codecInfo); + if (result.error != EncodedImageCallback::Result::OK) { + RTC_LOG(LS_ERROR) << "Encode m_encodedCompleteCallback failed " + << result.error; + return WEBRTC_VIDEO_CODEC_ERROR; + } + return WEBRTC_VIDEO_CODEC_OK; +} + +VideoEncoder::EncoderInfo NvidiaH265EncoderImpl::GetEncoderInfo() const { + EncoderInfo info; + info.supports_native_handle = false; + info.implementation_name = "NVIDIA H265 Encoder"; + info.scaling_settings = VideoEncoder::ScalingSettings::kOff; + info.is_hardware_accelerated = true; + info.supports_simulcast = false; + info.preferred_pixel_formats = {VideoFrameBuffer::Type::kNV12, + VideoFrameBuffer::Type::kI420}; + return info; +} + +void NvidiaH265EncoderImpl::SetRates( + const RateControlParameters& parameters) { + if (!encoder_) { + RTC_LOG(LS_WARNING) << "SetRates() while uninitialized."; + return; + } + + if (parameters.framerate_fps < 1.0) { + RTC_LOG(LS_WARNING) << "Invalid frame rate: " << parameters.framerate_fps; + return; + } + + if (parameters.bitrate.get_sum_bps() == 0) { + configuration_.SetStreamState(false); + return; + } + + codec_.maxFramerate = static_cast(parameters.framerate_fps); + codec_.maxBitrate = parameters.bitrate.GetSpatialLayerSum(0); + + configuration_.target_bps = parameters.bitrate.GetSpatialLayerSum(0); + configuration_.max_frame_rate = parameters.framerate_fps; + + if (configuration_.target_bps) { + configuration_.SetStreamState(true); + } else { + configuration_.SetStreamState(false); + } +} + +void NvidiaH265EncoderImpl::LayerConfig::SetStreamState(bool send_stream) { + if (send_stream && !sending) { + key_frame_request = true; + } + sending = send_stream; +} + +} // namespace webrtc diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h265_encoder_impl.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h265_encoder_impl.h new file mode 100644 index 0000000..63abe97 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/h265_encoder_impl.h @@ -0,0 +1,94 @@ +#ifndef WEBRTC_NVIDIA_H265_ENCODER_IMPL_H_ +#define WEBRTC_NVIDIA_H265_ENCODER_IMPL_H_ + +#include + +#include +#include + +#include "NvEncoder/NvEncoder.h" +#include "NvEncoder/NvEncoderCuda.h" + +#include "absl/container/inlined_vector.h" +#include "api/environment/environment.h" +#include "api/video_codecs/sdp_video_format.h" +#include "api/transport/rtp/dependency_descriptor.h" +#include "api/video/i420_buffer.h" +#include "api/video/video_codec_constants.h" +#include "api/video_codecs/scalability_mode.h" +#include "api/video_codecs/video_encoder.h" + +namespace webrtc { + +class NvidiaH265EncoderImpl : public VideoEncoder { + public: + struct LayerConfig { + int simulcast_idx = 0; + int width = -1; + int height = -1; + bool sending = true; + bool key_frame_request = false; + float max_frame_rate = 0; + uint32_t target_bps = 0; + uint32_t max_bps = 0; + bool frame_dropping_on = false; + int key_frame_interval = 0; + int num_temporal_layers = 1; + + void SetStreamState(bool send_stream); + }; + + public: + NvidiaH265EncoderImpl(const webrtc::Environment& env, + CUcontext context, + CUmemorytype memory_type, + NV_ENC_BUFFER_FORMAT nv_format, + const SdpVideoFormat& format); + ~NvidiaH265EncoderImpl() override; + + int32_t InitEncode(const VideoCodec* codec_settings, + const Settings& settings) override; + + int32_t RegisterEncodeCompleteCallback( + EncodedImageCallback* callback) override; + + int32_t Release() override; + + int32_t Encode(const VideoFrame& frame, + const std::vector* frame_types) override; + + void SetRates(const RateControlParameters& rc_parameters) override; + + EncoderInfo GetEncoderInfo() const override; + + private: + int32_t ProcessEncodedFrame(std::vector& packet, + const ::webrtc::VideoFrame& inputFrame); + private: + const webrtc::Environment& env_; + EncodedImageCallback* encoded_image_callback_ = nullptr; + + std::unique_ptr encoder_; + CUcontext cu_context_; + CUmemorytype cu_memory_type_; + CUarray cu_scaled_array_; + NV_ENC_BUFFER_FORMAT nv_format_; + NV_ENC_INITIALIZE_PARAMS nv_initialize_params_; + NV_ENC_CONFIG nv_encode_config_; + + LayerConfig configuration_; + EncodedImage encoded_image_; + VideoCodec codec_; + void ReportInit(); + void ReportError(); + bool has_reported_init_ = false; + bool has_reported_error_ = false; + const SdpVideoFormat format_; + bool current_encoding_is_keyframe_ = false; +}; + +} // namespace webrtc + +#endif // WEBRTC_NVIDIA_H265_ENCODER_IMPL_H_ + + diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/native_gpu_encode_bridge.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/native_gpu_encode_bridge.cpp new file mode 100644 index 0000000..d0d8e4a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/native_gpu_encode_bridge.cpp @@ -0,0 +1,338 @@ +#include "native_gpu_encode_bridge.h" + +#include "livekit/video_frame_buffer.h" +#include "rtc_base/logging.h" + +#if defined(__linux__) +#include +#include +#include + +#include +#include +#endif + +namespace webrtc { +namespace { + +constexpr uint32_t FourCc(char a, char b, char c, char d) { + return static_cast(a) | (static_cast(b) << 8) | + (static_cast(c) << 16) | + (static_cast(d) << 24); +} + +constexpr uint32_t kDrmFormatNv12 = FourCc('N', 'V', '1', '2'); +constexpr uint32_t kDrmFormatXrgb8888 = FourCc('X', 'R', '2', '4'); +constexpr uint32_t kDrmFormatArgb8888 = FourCc('A', 'R', '2', '4'); +constexpr uint32_t kDrmFormatXbgr8888 = FourCc('X', 'B', '2', '4'); +constexpr uint32_t kDrmFormatAbgr8888 = FourCc('A', 'B', '2', '4'); +constexpr uint32_t kDrmFormatXrgb2101010 = FourCc('X', 'R', '3', '0'); +constexpr uint32_t kDrmFormatArgb2101010 = FourCc('A', 'R', '3', '0'); +constexpr uint32_t kDrmFormatXbgr2101010 = FourCc('X', 'B', '3', '0'); +constexpr uint32_t kDrmFormatAbgr2101010 = FourCc('A', 'B', '3', '0'); + +#if defined(__linux__) + +class ScopedCudaContext { + public: + explicit ScopedCudaContext(CUcontext context) : pushed_(false) { + if (context && cuCtxPushCurrent(context) == CUDA_SUCCESS) { + pushed_ = true; + } + } + + ~ScopedCudaContext() { + if (pushed_) { + CUcontext popped = nullptr; + cuCtxPopCurrent(&popped); + } + } + + bool ok() const { return pushed_; } + + private: + bool pushed_; +}; + +class ScopedCudaGraphicsResource { + public: + ~ScopedCudaGraphicsResource() { + if (resource_) { + cuGraphicsUnregisterResource(resource_); + } + } + + CUgraphicsResource* receive() { return &resource_; } + CUgraphicsResource get() const { return resource_; } + + private: + CUgraphicsResource resource_ = nullptr; +}; + +class ScopedEglImage { + public: + ScopedEglImage(EGLDisplay display, EGLImageKHR image) + : display_(display), image_(image) {} + + ~ScopedEglImage() { + if (display_ != EGL_NO_DISPLAY && image_ != EGL_NO_IMAGE_KHR) { + auto destroy_image = reinterpret_cast( + eglGetProcAddress("eglDestroyImageKHR")); + if (destroy_image) { + destroy_image(display_, image_); + } + } + } + + EGLImageKHR get() const { return image_; } + + private: + EGLDisplay display_ = EGL_NO_DISPLAY; + EGLImageKHR image_ = EGL_NO_IMAGE_KHR; +}; + +bool EnsureEglInitialized(EGLDisplay* out_display) { + EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); + if (display == EGL_NO_DISPLAY) { + RTC_LOG(LS_WARNING) << "Fluxer native GPU encode: no EGL display"; + return false; + } + EGLint major = 0; + EGLint minor = 0; + if (!eglInitialize(display, &major, &minor)) { + RTC_LOG(LS_WARNING) << "Fluxer native GPU encode: eglInitialize failed"; + return false; + } + *out_display = display; + return true; +} + +void AppendPlaneAttributes(std::vector* attrs, + int plane, + int fd, + uint32_t offset, + uint32_t stride, + uint64_t modifier) { + const EGLint fd_attrs[4] = {EGL_DMA_BUF_PLANE0_FD_EXT, + EGL_DMA_BUF_PLANE1_FD_EXT, + EGL_DMA_BUF_PLANE2_FD_EXT, + EGL_DMA_BUF_PLANE3_FD_EXT}; + const EGLint offset_attrs[4] = {EGL_DMA_BUF_PLANE0_OFFSET_EXT, + EGL_DMA_BUF_PLANE1_OFFSET_EXT, + EGL_DMA_BUF_PLANE2_OFFSET_EXT, + EGL_DMA_BUF_PLANE3_OFFSET_EXT}; + const EGLint pitch_attrs[4] = {EGL_DMA_BUF_PLANE0_PITCH_EXT, + EGL_DMA_BUF_PLANE1_PITCH_EXT, + EGL_DMA_BUF_PLANE2_PITCH_EXT, + EGL_DMA_BUF_PLANE3_PITCH_EXT}; + const EGLint mod_lo_attrs[4] = {EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT, + EGL_DMA_BUF_PLANE3_MODIFIER_LO_EXT}; + const EGLint mod_hi_attrs[4] = {EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT, + EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT, + EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT, + EGL_DMA_BUF_PLANE3_MODIFIER_HI_EXT}; + + attrs->push_back(fd_attrs[plane]); + attrs->push_back(fd); + attrs->push_back(offset_attrs[plane]); + attrs->push_back(static_cast(offset)); + attrs->push_back(pitch_attrs[plane]); + attrs->push_back(static_cast(stride)); + if (modifier != 0) { + attrs->push_back(mod_lo_attrs[plane]); + attrs->push_back(static_cast(modifier & 0xffffffffu)); + attrs->push_back(mod_hi_attrs[plane]); + attrs->push_back(static_cast((modifier >> 32) & 0xffffffffu)); + } +} + +EGLImageKHR CreateDmabufEglImage( + EGLDisplay display, + const livekit_ffi::FluxerGpuFrameBuffer& native) { + auto create_image = reinterpret_cast( + eglGetProcAddress("eglCreateImageKHR")); + if (!create_image) { + RTC_LOG(LS_WARNING) + << "Fluxer native GPU encode: eglCreateImageKHR unavailable"; + return EGL_NO_IMAGE_KHR; + } + + std::vector attrs = { + EGL_WIDTH, + native.width(), + EGL_HEIGHT, + native.height(), + EGL_LINUX_DRM_FOURCC_EXT, + static_cast(native.drm_format()), + }; + const uint32_t plane_count = native.plane_count(); + if (plane_count == 0 || plane_count > 4) { + return EGL_NO_IMAGE_KHR; + } + for (uint32_t plane = 0; plane < plane_count; ++plane) { + if (native.fd(plane) < 0 || native.stride(plane) == 0) { + return EGL_NO_IMAGE_KHR; + } + AppendPlaneAttributes(&attrs, static_cast(plane), native.fd(plane), + native.offset(plane), native.stride(plane), + native.modifier()); + } + attrs.push_back(EGL_NONE); + + return create_image(display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, nullptr, + attrs.data()); +} + +bool DrmFormatToNvencFormat(uint32_t drm_format, + NV_ENC_BUFFER_FORMAT* nvenc_format) { + switch (drm_format) { + case kDrmFormatNv12: + *nvenc_format = NV_ENC_BUFFER_FORMAT_NV12; + return true; + case kDrmFormatXrgb8888: + case kDrmFormatArgb8888: + *nvenc_format = NV_ENC_BUFFER_FORMAT_ARGB; + return true; + case kDrmFormatXbgr8888: + case kDrmFormatAbgr8888: + *nvenc_format = NV_ENC_BUFFER_FORMAT_ABGR; + return true; + case kDrmFormatXrgb2101010: + case kDrmFormatArgb2101010: + *nvenc_format = NV_ENC_BUFFER_FORMAT_ARGB10; + return true; + case kDrmFormatXbgr2101010: + case kDrmFormatAbgr2101010: + *nvenc_format = NV_ENC_BUFFER_FORMAT_ABGR10; + return true; + default: + return false; + } +} + +bool EglFrameLooksLikeSupportedInput( + const livekit_ffi::FluxerGpuFrameBuffer& native, + const CUeglFrame& egl_frame) { + if (egl_frame.frameType != CU_EGL_FRAME_TYPE_PITCH || + egl_frame.width != static_cast(native.width()) || + egl_frame.height != static_cast(native.height()) || + egl_frame.pitch == 0 || !egl_frame.frame.pPitch[0]) { + return false; + } + if (native.drm_format() != kDrmFormatNv12) { + return egl_frame.planeCount == 1; + } + if (egl_frame.planeCount <= 1) { + return true; + } + const uintptr_t y = reinterpret_cast(egl_frame.frame.pPitch[0]); + const uintptr_t uv = reinterpret_cast(egl_frame.frame.pPitch[1]); + return uv == y + static_cast(egl_frame.pitch) * + static_cast(native.height()); +} + +bool TryEncodeDmabuf(NvEncoder* encoder, + CUcontext cu_context, + const livekit_ffi::FluxerGpuFrameBuffer& native, + NV_ENC_PIC_PARAMS* pic_params, + std::vector>* bit_stream) { + NV_ENC_BUFFER_FORMAT nvenc_format = NV_ENC_BUFFER_FORMAT_UNDEFINED; + if (encoder->GetDeviceType() != NV_ENC_DEVICE_TYPE_CUDA || + !DrmFormatToNvencFormat(native.drm_format(), &nvenc_format)) { + return false; + } + + EGLDisplay display = EGL_NO_DISPLAY; + if (!EnsureEglInitialized(&display)) { + return false; + } + ScopedEglImage image(display, CreateDmabufEglImage(display, native)); + if (image.get() == EGL_NO_IMAGE_KHR) { + RTC_LOG(LS_WARNING) + << "Fluxer native GPU encode: failed to import DMA-BUF as EGLImage"; + return false; + } + + ScopedCudaContext current(cu_context); + if (!current.ok()) { + RTC_LOG(LS_WARNING) + << "Fluxer native GPU encode: failed to make CUDA context current"; + return false; + } + + ScopedCudaGraphicsResource resource; + CUresult cu_result = cuGraphicsEGLRegisterImage( + resource.receive(), image.get(), CU_GRAPHICS_MAP_RESOURCE_FLAGS_NONE); + if (cu_result != CUDA_SUCCESS) { + const char* name = nullptr; + cuGetErrorName(cu_result, &name); + RTC_LOG(LS_WARNING) + << "Fluxer native GPU encode: cuGraphicsEGLRegisterImage failed: " + << (name ? name : "unknown"); + return false; + } + + CUeglFrame egl_frame = {}; + cu_result = cuGraphicsResourceGetMappedEglFrame(&egl_frame, resource.get(), + 0, 0); + if (cu_result != CUDA_SUCCESS) { + const char* name = nullptr; + cuGetErrorName(cu_result, &name); + RTC_LOG(LS_WARNING) + << "Fluxer native GPU encode: cuGraphicsResourceGetMappedEglFrame " + "failed: " + << (name ? name : "unknown"); + return false; + } + + if (!EglFrameLooksLikeSupportedInput(native, egl_frame)) { + RTC_LOG(LS_WARNING) + << "Fluxer native GPU encode: imported DMA-BUF has unsupported layout"; + return false; + } + + NvEncExternalInputFrame external = {}; + external.resource = egl_frame.frame.pPitch[0]; + external.resourceType = NV_ENC_INPUT_RESOURCE_TYPE_CUDADEVICEPTR; + external.width = native.width(); + external.height = native.height(); + external.pitch = static_cast(egl_frame.pitch); + external.bufferFormat = nvenc_format; + + encoder->EncodeExternalFrame(external, *bit_stream, pic_params); + return true; +} + +#endif // defined(__linux__) + +} // namespace + +bool TryEncodeNativeGpuFrame(NvEncoder* encoder, + CUcontext cu_context, + const VideoFrame& input_frame, + NV_ENC_PIC_PARAMS* pic_params, + std::vector>* bit_stream) { + if (!encoder || !bit_stream) { + return false; + } + const auto input_buffer = input_frame.video_frame_buffer(); + const auto* native = + livekit_ffi::AsFluxerGpuFrameBuffer(input_buffer ? input_buffer.get() + : nullptr); + if (!native) { + return false; + } + +#if defined(__linux__) + if (native->kind() == livekit_ffi::FluxerGpuFrameBuffer::Kind::kDmaBuf) { + return TryEncodeDmabuf(encoder, cu_context, *native, pic_params, + bit_stream); + } +#endif + + return false; +} + +} // namespace webrtc diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/native_gpu_encode_bridge.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/native_gpu_encode_bridge.h new file mode 100644 index 0000000..47cf7b4 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/native_gpu_encode_bridge.h @@ -0,0 +1,22 @@ +#ifndef WEBRTC_NVIDIA_NATIVE_GPU_ENCODE_BRIDGE_H_ +#define WEBRTC_NVIDIA_NATIVE_GPU_ENCODE_BRIDGE_H_ + +#include + +#include +#include + +#include "NvEncoder/NvEncoder.h" +#include "api/video/video_frame.h" + +namespace webrtc { + +bool TryEncodeNativeGpuFrame(NvEncoder* encoder, + CUcontext cu_context, + const VideoFrame& input_frame, + NV_ENC_PIC_PARAMS* pic_params, + std::vector>* bit_stream); + +} // namespace webrtc + +#endif // WEBRTC_NVIDIA_NATIVE_GPU_ENCODE_BRIDGE_H_ diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/nvidia_decoder_factory.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/nvidia_decoder_factory.cpp new file mode 100644 index 0000000..27d4290 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/nvidia_decoder_factory.cpp @@ -0,0 +1,186 @@ +#include "nvidia_decoder_factory.h" + +#include + +#include + +#include "cuda_context.h" +#include "h264_decoder_impl.h" +#include "h265_decoder_impl.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +constexpr char kSdpKeyNameCodecImpl[] = "implementation_name"; +constexpr char kCodecName[] = "NvCodec"; + +static int GetCudaDeviceCapabilityMajorVersion(CUcontext context) { + cuCtxSetCurrent(context); + + CUdevice device; + cuCtxGetDevice(&device); + + int major; + cuDeviceGetAttribute(&major, CU_DEVICE_ATTRIBUTE_COMPUTE_CAPABILITY_MAJOR, + device); + + return major; +} + +static bool CanDecodeWithNvdec(CUcontext context, + cudaVideoCodec codec, + const char* codec_name) { + CUVIDDECODECAPS caps = {}; + caps.eCodecType = codec; + caps.eChromaFormat = cudaVideoChromaFormat_420; + caps.nBitDepthMinus8 = 0; + + CUresult result = cuCtxPushCurrent(context); + if (result != CUDA_SUCCESS) { + RTC_LOG(LS_WARNING) << "NVDEC " << codec_name + << " capability probe failed on cuCtxPushCurrent: " + << result; + return false; + } + + result = cuvidGetDecoderCaps(&caps); + CUresult pop_result = cuCtxPopCurrent(nullptr); + if (pop_result != CUDA_SUCCESS) { + RTC_LOG(LS_WARNING) << "NVDEC " << codec_name + << " capability probe failed on cuCtxPopCurrent: " + << pop_result; + return false; + } + if (result != CUDA_SUCCESS) { + RTC_LOG(LS_WARNING) << "NVDEC " << codec_name + << " capability probe failed on cuvidGetDecoderCaps: " + << result; + return false; + } + + const bool supports_nv12 = + (caps.nOutputFormatMask & (1 << cudaVideoSurfaceFormat_NV12)) != 0; + if (!caps.bIsSupported || !supports_nv12) { + RTC_LOG(LS_WARNING) << "NVDEC " << codec_name + << " is unavailable on this GPU."; + return false; + } + + return true; +} + +std::vector SupportedNvDecoderCodecs(CUcontext context) { + std::vector supportedFormats; + const bool h264_supported = + CanDecodeWithNvdec(context, cudaVideoCodec_H264, "H264"); + const bool hevc_supported = + CanDecodeWithNvdec(context, cudaVideoCodec_HEVC, "HEVC"); + + // HardwareGeneration Kepler is 3.x + // https://docs.nvidia.com/deploy/cuda-compatibility/index.html#faq + // Kepler support h264 profile Main, Highprofile up to Level4.1 + // https://docs.nvidia.com/video-technologies/video-codec-sdk/nvdec-video-decoder-api-prog-guide/index.html#video-decoder-capabilities__table_o3x_fms_3lb + if (h264_supported && GetCudaDeviceCapabilityMajorVersion(context) <= 3) { + supportedFormats = { + CreateH264Format(webrtc::H264Profile::kProfileHigh, + webrtc::H264Level::kLevel4_1, "1"), + CreateH264Format(webrtc::H264Profile::kProfileMain, + webrtc::H264Level::kLevel4_1, "1"), + }; + } else if (h264_supported) { + supportedFormats = { + // Constrained Baseline Profile does not support NvDecoder, but WebRTC + // uses this profile by default, + // so it must be returned in this method. + CreateH264Format(webrtc::H264Profile::kProfileConstrainedBaseline, + webrtc::H264Level::kLevel5_1, "1"), + CreateH264Format(webrtc::H264Profile::kProfileBaseline, + webrtc::H264Level::kLevel5_1, "1"), + CreateH264Format(webrtc::H264Profile::kProfileHigh, + webrtc::H264Level::kLevel5_1, "1"), + CreateH264Format(webrtc::H264Profile::kProfileMain, + webrtc::H264Level::kLevel5_1, "1"), + }; + } + + if (hevc_supported) { + supportedFormats.push_back(SdpVideoFormat("H265")); + supportedFormats.push_back(SdpVideoFormat("HEVC")); + } + + for (auto& format : supportedFormats) { + format.parameters.emplace(kSdpKeyNameCodecImpl, kCodecName); + } + + return supportedFormats; +} + +NvidiaVideoDecoderFactory::NvidiaVideoDecoderFactory() + : cu_context_(livekit_ffi::CudaContext::GetInstance()) { + if (cu_context_->IsInitialized() || cu_context_->Initialize()) { + supported_formats_ = SupportedNvDecoderCodecs(cu_context_->GetContext()); + } else { + RTC_LOG(LS_ERROR) << "Failed to initialize CUDA context."; + } + RTC_LOG(LS_INFO) << "NvidiaVideoDecoderFactory created with " + << supported_formats_.size() << " supported formats."; +} + +NvidiaVideoDecoderFactory::~NvidiaVideoDecoderFactory() {} + +bool NvidiaVideoDecoderFactory::IsSupported() { + if (!livekit_ffi::CudaContext::IsAvailable()) { + RTC_LOG(LS_WARNING) << "Cuda Context is not available."; + return false; + } + + livekit_ffi::CudaContext* context = livekit_ffi::CudaContext::GetInstance(); + if (!context->IsInitialized() && !context->Initialize()) { + RTC_LOG(LS_WARNING) << "CUDA context initialization failed, NVDEC disabled."; + return false; + } + + const bool supported = + !SupportedNvDecoderCodecs(context->GetContext()).empty(); + if (supported) { + std::cout << "Nvidia Decoder is supported." << std::endl; + } else { + RTC_LOG(LS_WARNING) << "No supported NVDEC codecs found, NVDEC disabled."; + context->Shutdown(); + } + return supported; +} + +std::unique_ptr NvidiaVideoDecoderFactory::Create( + const Environment& env, + const SdpVideoFormat& format) { + // Check if the requested format is supported. + for (const auto& supported_format : supported_formats_) { + if (format.IsSameCodec(supported_format)) { + // If the format is supported, create and return the decoder. + if (!cu_context_) { + cu_context_ = livekit_ffi::CudaContext::GetInstance(); + if (!cu_context_->Initialize()) { + RTC_LOG(LS_ERROR) << "Failed to initialize CUDA context."; + return nullptr; + } + } + if (format.name == "H264") { + RTC_LOG(LS_INFO) << "Using NVIDIA HW decoder (NVDEC) for H264"; + return std::make_unique(cu_context_->GetContext()); + } + if (format.name == "H265" || format.name == "HEVC") { + RTC_LOG(LS_INFO) << "Using NVIDIA HW decoder (NVDEC) for H265/HEVC"; + return std::make_unique(cu_context_->GetContext()); + } + } + } + return nullptr; +} + +std::vector NvidiaVideoDecoderFactory::GetSupportedFormats() + const { + return supported_formats_; +} + +} // namespace webrtc diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/nvidia_decoder_factory.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/nvidia_decoder_factory.h new file mode 100644 index 0000000..0b3707a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/nvidia_decoder_factory.h @@ -0,0 +1,34 @@ + + +#ifndef NVIDIA_VIDEO_DECODER_FACTORY_H_ +#define NVIDIA_VIDEO_DECODER_FACTORY_H_ + +#include + +#include "api/environment/environment.h" +#include "api/video_codecs/sdp_video_format.h" +#include "api/video_codecs/video_decoder_factory.h" +#include "cuda_context.h" + +namespace webrtc { + +class NvidiaVideoDecoderFactory : public VideoDecoderFactory { + public: + NvidiaVideoDecoderFactory(); + ~NvidiaVideoDecoderFactory() override; + + static bool IsSupported(); + + std::vector GetSupportedFormats() const override; + std::unique_ptr Create( + const Environment& env, + const SdpVideoFormat& format) override; + + private: + std::vector supported_formats_; + livekit_ffi::CudaContext* cu_context_; +}; + +} // namespace webrtc + +#endif // NVIDIA_VIDEO_DECODER_FACTORY_H_ diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/nvidia_encoder_factory.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/nvidia_encoder_factory.cpp new file mode 100644 index 0000000..66e505e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/nvidia_encoder_factory.cpp @@ -0,0 +1,220 @@ +#include "nvidia_encoder_factory.h" + +#include + +#include "cuda_context.h" +#include "h264_encoder_impl.h" +#include "h265_encoder_impl.h" +#include "nvEncodeAPI.h" +#include "rtc_base/logging.h" + +#if defined(_WIN32) +#include +#else +#include +#endif + +namespace webrtc { + +NvidiaVideoEncoderFactory::NvidiaVideoEncoderFactory() { + std::map baselineParameters = { + {"profile-level-id", "42e01f"}, + {"level-asymmetry-allowed", "1"}, + {"packetization-mode", "1"}, + }; + supported_formats_.push_back(SdpVideoFormat("H264", baselineParameters)); + + // Advertise HEVC/H265 with default parameters. + supported_formats_.push_back(SdpVideoFormat("H265")); + // Some stacks use 'HEVC' name. + supported_formats_.push_back(SdpVideoFormat("HEVC")); + + /*std::map highParameters = { + {"profile-level-id", "4d0032"}, + {"level-asymmetry-allowed", "1"}, + {"packetization-mode", "1"}, + }; + + supported_formats_.push_back(SdpVideoFormat("H264", highParameters)); + */ +} + +NvidiaVideoEncoderFactory::~NvidiaVideoEncoderFactory() {} + +bool NvidiaVideoEncoderFactory::IsSupported() { + if (!livekit_ffi::CudaContext::IsAvailable()) { + RTC_LOG(LS_WARNING) << "CUDA is not available, NVENC disabled."; + return false; + } + + // CUDA being available does NOT imply NVENC is present. Compute-only GPUs + // (H100, A100, etc.) have full CUDA support but no encode hardware. + // Probe the NVENC library and try to open a session to be sure. +#if defined(_WIN32) +#if defined(_WIN64) + HMODULE hModule = LoadLibrary(TEXT("nvEncodeAPI64.dll")); +#else + HMODULE hModule = LoadLibrary(TEXT("nvEncodeAPI.dll")); +#endif +#else + void* hModule = dlopen("libnvidia-encode.so.1", RTLD_LAZY); +#endif + if (!hModule) { +#if defined(_WIN32) + RTC_LOG(LS_WARNING) << "NVENC library not found, hardware encoding " + "unavailable."; +#else + RTC_LOG(LS_WARNING) << "NVENC library (libnvidia-encode.so.1) not found, " + "hardware encoding unavailable."; +#endif + return false; + } + auto NvEncodeAPIGetMaxSupportedVersion = +#if defined(_WIN32) + (NVENCSTATUS(NVENCAPI*)(uint32_t*))GetProcAddress( + hModule, "NvEncodeAPIGetMaxSupportedVersion"); +#else + (NVENCSTATUS(NVENCAPI*)(uint32_t*))dlsym( + hModule, "NvEncodeAPIGetMaxSupportedVersion"); +#endif + auto NvEncodeAPICreateInstance = +#if defined(_WIN32) + (NVENCSTATUS(NVENCAPI*)(NV_ENCODE_API_FUNCTION_LIST*))GetProcAddress( + hModule, "NvEncodeAPICreateInstance"); +#else + (NVENCSTATUS(NVENCAPI*)(NV_ENCODE_API_FUNCTION_LIST*))dlsym( + hModule, "NvEncodeAPICreateInstance"); +#endif + + bool supported = false; + + do { + if (!NvEncodeAPIGetMaxSupportedVersion || !NvEncodeAPICreateInstance) { + RTC_LOG(LS_WARNING) << "NVENC API entry points not found in library."; + break; + } + + uint32_t maxVersion = 0; + if (NvEncodeAPIGetMaxSupportedVersion(&maxVersion) != NV_ENC_SUCCESS) { + RTC_LOG(LS_WARNING) << "NvEncodeAPIGetMaxSupportedVersion failed."; + break; + } + + uint32_t currentVersion = + (NVENCAPI_MAJOR_VERSION << 4) | NVENCAPI_MINOR_VERSION; + if (currentVersion > maxVersion) { + RTC_LOG(LS_WARNING) << "NVENC driver version too old: driver supports " + << maxVersion << ", SDK requires " << currentVersion; + break; + } + + NV_ENCODE_API_FUNCTION_LIST fnList = {NV_ENCODE_API_FUNCTION_LIST_VER}; + if (NvEncodeAPICreateInstance(&fnList) != NV_ENC_SUCCESS) { + RTC_LOG(LS_WARNING) << "NvEncodeAPICreateInstance failed."; + break; + } + + // Try opening an encode session with CUDA device 0 to confirm the GPU + // actually has NVENC hardware. + CUresult cuRes = cuInit(0); + if (cuRes != CUDA_SUCCESS) { + RTC_LOG(LS_WARNING) << "cuInit failed during NVENC probe."; + break; + } + + CUdevice cuDevice; + cuRes = cuDeviceGet(&cuDevice, 0); + if (cuRes != CUDA_SUCCESS) { + RTC_LOG(LS_WARNING) << "cuDeviceGet failed during NVENC probe."; + break; + } + + CUcontext cuCtx = nullptr; +#if CUDA_VERSION >= 13000 + cuRes = cuCtxCreate(&cuCtx, nullptr, 0, cuDevice); +#else + cuRes = cuCtxCreate(&cuCtx, 0, cuDevice); +#endif + if (cuRes != CUDA_SUCCESS || !cuCtx) { + RTC_LOG(LS_WARNING) << "cuCtxCreate failed during NVENC probe."; + break; + } + + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS sessionParams = { + NV_ENC_OPEN_ENCODE_SESSION_EX_PARAMS_VER}; + sessionParams.device = cuCtx; + sessionParams.deviceType = NV_ENC_DEVICE_TYPE_CUDA; + sessionParams.apiVersion = NVENCAPI_VERSION; + + void* hEncoder = nullptr; + NVENCSTATUS nvStatus = + fnList.nvEncOpenEncodeSessionEx(&sessionParams, &hEncoder); + + if (nvStatus == NV_ENC_SUCCESS && hEncoder) { + fnList.nvEncDestroyEncoder(hEncoder); + supported = true; + } else { + char deviceName[80] = {}; + cuDeviceGetName(deviceName, sizeof(deviceName), cuDevice); + RTC_LOG(LS_WARNING) << "NVENC not available on GPU \"" << deviceName + << "\" (status=" << nvStatus + << "). This GPU likely lacks encode hardware."; + } + + cuCtxDestroy(cuCtx); + } while (false); + +#if defined(_WIN32) + FreeLibrary(hModule); +#else + dlclose(hModule); +#endif + + if (supported) { + RTC_LOG(LS_INFO) << "NVIDIA NVENC hardware encoder is available."; + } + return supported; +} + +std::unique_ptr NvidiaVideoEncoderFactory::Create( + const Environment& env, + const SdpVideoFormat& format) { + // Check if the requested format is supported. + for (const auto& supported_format : supported_formats_) { + if (format.IsSameCodec(supported_format)) { + if (!cu_context_) { + cu_context_ = livekit_ffi::CudaContext::GetInstance(); + if (!cu_context_->Initialize()) { + RTC_LOG(LS_ERROR) << "Failed to initialize CUDA context."; + return nullptr; + } + } + + if (format.name == "H264") { + RTC_LOG(LS_INFO) << "Using NVIDIA HW encoder (NVENC) for H264"; + return std::make_unique( + env, cu_context_->GetContext(), CU_MEMORYTYPE_DEVICE, + NV_ENC_BUFFER_FORMAT_NV12, format); + } + + if (format.name == "H265" || format.name == "HEVC") { + RTC_LOG(LS_INFO) << "Using NVIDIA HW encoder (NVENC) for H265/HEVC"; + return std::make_unique( + env, cu_context_->GetContext(), CU_MEMORYTYPE_DEVICE, + NV_ENC_BUFFER_FORMAT_NV12, format); + } + } + } + return nullptr; +} +std::vector NvidiaVideoEncoderFactory::GetSupportedFormats() + const { + return supported_formats_; +} + +std::vector NvidiaVideoEncoderFactory::GetImplementations() + const { + return supported_formats_; +} + +} // namespace webrtc diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/nvidia_encoder_factory.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/nvidia_encoder_factory.h new file mode 100644 index 0000000..fd03910 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/nvidia/nvidia_encoder_factory.h @@ -0,0 +1,42 @@ + + +#ifndef NVIDIA_VIDEO_ENCODER_FACTORY_H_ +#define NVIDIA_VIDEO_ENCODER_FACTORY_H_ + +#include + +#include "api/environment/environment.h" +#include "api/video_codecs/sdp_video_format.h" +#include "api/video_codecs/video_encoder_factory.h" +#include "cuda_context.h" + +namespace webrtc { + +class NvidiaVideoEncoderFactory : public VideoEncoderFactory { + public: + NvidiaVideoEncoderFactory(); + ~NvidiaVideoEncoderFactory() override; + + static bool IsSupported(); + + std::unique_ptr Create(const Environment& env, + const SdpVideoFormat& format) override; + + // Returns a list of supported codecs in order of preference. + std::vector GetSupportedFormats() const override; + + std::vector GetImplementations() const override; + + std::unique_ptr GetEncoderSelector() + const override { + return nullptr; + } + + private: + std::vector supported_formats_; + livekit_ffi::CudaContext* cu_context_ = nullptr; +}; + +} // namespace webrtc + +#endif // NVIDIA_VIDEO_ENCODER_FACTORY_H_ diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/objc_video_factory.mm b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/objc_video_factory.mm new file mode 100644 index 0000000..9c803af --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/objc_video_factory.mm @@ -0,0 +1,38 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/objc_video_factory.h" + +#import +#import +#import +#include "sdk/objc/native/api/video_decoder_factory.h" +#include "sdk/objc/native/api/video_encoder_factory.h" + +namespace livekit_ffi { + +std::unique_ptr CreateObjCVideoEncoderFactory() { + RTC_OBJC_TYPE(RTCDefaultVideoEncoderFactory)* encoderFactory = [[RTC_OBJC_TYPE(RTCDefaultVideoEncoderFactory) alloc] init]; + RTC_OBJC_TYPE(RTCVideoEncoderFactorySimulcast)* simulcastFactory = + [[RTC_OBJC_TYPE(RTCVideoEncoderFactorySimulcast) alloc] initWithPrimary:encoderFactory fallback:encoderFactory]; + return webrtc::ObjCToNativeVideoEncoderFactory(simulcastFactory); +} + +std::unique_ptr CreateObjCVideoDecoderFactory() { + return webrtc::ObjCToNativeVideoDecoderFactory([[RTC_OBJC_TYPE(RTCDefaultVideoDecoderFactory) alloc] init]); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/objc_video_frame_buffer.mm b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/objc_video_frame_buffer.mm new file mode 100644 index 0000000..ce21f64 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/objc_video_frame_buffer.mm @@ -0,0 +1,48 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/video_frame_buffer.h" + +#import +#import +#include "sdk/objc/native/api/video_frame_buffer.h" + +namespace livekit_ffi { + +std::unique_ptr new_native_buffer_from_platform_image_buffer( + CVPixelBufferRef pixelBuffer +) { + RTC_OBJC_TYPE(RTCCVPixelBuffer) *buffer = [[RTC_OBJC_TYPE(RTCCVPixelBuffer) alloc] initWithPixelBuffer:pixelBuffer]; + webrtc::scoped_refptr frame_buffer = webrtc::ObjCToNativeVideoFrameBuffer(buffer); + [buffer release]; + CVPixelBufferRelease(pixelBuffer); + return std::make_unique(frame_buffer); +} + +CVPixelBufferRef native_buffer_to_platform_image_buffer( + const std::unique_ptr &buffer +) { + id rtc_pixel_buffer = webrtc::NativeToObjCVideoFrameBuffer(buffer->get()); + + if ([rtc_pixel_buffer isKindOfClass:[RTC_OBJC_TYPE(RTCCVPixelBuffer) class]]) { + RTC_OBJC_TYPE(RTCCVPixelBuffer) *cv_pixel_buffer = (RTC_OBJC_TYPE(RTCCVPixelBuffer) *)rtc_pixel_buffer; + return [cv_pixel_buffer pixelBuffer]; + } else { + return nullptr; + } +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/packet_trailer.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/packet_trailer.cpp new file mode 100644 index 0000000..96ff641 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/packet_trailer.cpp @@ -0,0 +1,625 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/packet_trailer.h" + +#include +#include +#include + +#include "api/make_ref_counted.h" +#include "livekit/peer_connection_factory.h" +#include "livekit/rtp_receiver.h" +#include "livekit/rtp_sender.h" +#include "rtc_base/logging.h" +#include "webrtc-sys/src/packet_trailer.rs.h" + +namespace livekit_ffi { + +namespace { + +uint64_t CurrentUnixTimeMicros() { + auto now = std::chrono::system_clock::now().time_since_epoch(); + return static_cast( + std::chrono::duration_cast(now).count()); +} + +} // namespace + +// PacketTrailerTransformer implementation + +PacketTrailerTransformer::PacketTrailerTransformer(Direction direction) + : direction_(direction) {} + +void PacketTrailerTransformer::Transform( + std::unique_ptr frame) { + uint32_t ssrc = frame->GetSsrc(); + uint32_t rtp_timestamp = frame->GetTimestamp(); + + if (direction_ == Direction::kSend) { + TransformSend(std::move(frame)); + return; + } + + if (!enabled_.load()) { + webrtc::scoped_refptr cb; + { + webrtc::MutexLock lock(&mutex_); + auto it = sink_callbacks_.find(ssrc); + if (it != sink_callbacks_.end()) { + cb = it->second; + } else { + cb = callback_; + } + } + + if (cb) { + cb->OnTransformedFrame(std::move(frame)); + } else { + RTC_LOG(LS_WARNING) + << "PacketTrailerTransformer::Transform (disabled) has no callback" + << " direction=" + << (direction_ == Direction::kSend ? "send" : "recv") + << " ssrc=" << ssrc << " rtp_ts=" << rtp_timestamp; + } + return; + } + + TransformReceive(std::move(frame)); +} + +void PacketTrailerTransformer::TransformSend( + std::unique_ptr frame) { + uint32_t rtp_timestamp = frame->GetTimestamp(); + uint32_t ssrc = frame->GetSsrc(); + + auto data = frame->GetData(); + PacketTrailerMetadata meta_to_embed = + LookupSendMetadata(*frame, ssrc, rtp_timestamp); + emit_publish_timing(VideoPublishTimingStage::EncoderOutput, + meta_to_embed.user_timestamp, meta_to_embed.frame_id); + + // Always append trailer when enabled (even if timestamp is 0, + // which indicates no metadata was set for this frame) + std::vector new_data; + if (enabled_.load()) { + new_data = AppendTrailer(data, meta_to_embed.user_timestamp, + meta_to_embed.frame_id); + frame->SetData(webrtc::ArrayView(new_data)); + } + + // Forward to the appropriate callback (either global or per-SSRC sink). + webrtc::scoped_refptr cb; + { + webrtc::MutexLock lock(&mutex_); + auto it = sink_callbacks_.find(ssrc); + if (it != sink_callbacks_.end()) { + cb = it->second; + } else { + cb = callback_; + } + } + + if (cb) { + emit_publish_timing(VideoPublishTimingStage::WebrtcPacketize, + meta_to_embed.user_timestamp, meta_to_embed.frame_id); + cb->OnTransformedFrame(std::move(frame)); + } else { + RTC_LOG(LS_WARNING) + << "PacketTrailerTransformer::TransformSend has no callback" + << " ssrc=" << ssrc << " rtp_ts=" << rtp_timestamp; + } +} + +PacketTrailerMetadata PacketTrailerTransformer::LookupSendMetadata( + const webrtc::TransformableFrameInterface& frame, + uint32_t ssrc, + uint32_t rtp_timestamp) const { + // Look up the frame metadata by the frame's capture time. + // CaptureTime() returns Timestamp::Millis(capture_time_ms_) where + // capture_time_ms_ = timestamp_us / 1000. So capture_time->us() + // has millisecond precision (bottom 3 digits always zero). + // store_frame_metadata() truncates its key the same way. + PacketTrailerMetadata meta_to_embed{0, 0, 0}; + auto capture_time = frame.CaptureTime(); + if (capture_time.has_value()) { + int64_t capture_us = capture_time->us(); + + webrtc::MutexLock lock(&send_map_mutex_); + auto it = send_map_.find(capture_us); + if (it != send_map_.end()) { + meta_to_embed = it->second; + // Don't erase — simulcast layers share the same capture time. + // Entries are pruned by capacity in store_frame_metadata(). + } + } else { + RTC_LOG(LS_WARNING) + << "PacketTrailerTransformer::TransformSend CaptureTime() not available" + << " ssrc=" << ssrc << " rtp_ts=" << rtp_timestamp; + } + return meta_to_embed; +} + +void PacketTrailerTransformer::TransformReceive( + std::unique_ptr frame) { + uint32_t ssrc = frame->GetSsrc(); + uint32_t rtp_timestamp = frame->GetTimestamp(); + auto data = frame->GetData(); + std::vector stripped_data; + + auto meta = ExtractTrailer(data, stripped_data); + PacketTrailerMetadata timing_meta{0, 0, ssrc}; + + if (meta.has_value()) { + meta->ssrc = ssrc; + timing_meta = meta.value(); + + { + webrtc::MutexLock lock(&recv_map_mutex_); + + // Detect simulcast layer switch (SSRC change). + // When the SFU switches us to a different layer, the old layer's + // entries are stale and can cause RTP timestamp collisions or + // return wrong user timestamps on lookup. Flush them. + if (recv_active_ssrc_ != 0 && recv_active_ssrc_ != ssrc) { + auto oit = recv_map_order_.begin(); + while (oit != recv_map_order_.end()) { + auto mit = recv_map_.find(*oit); + if (mit != recv_map_.end() && mit->second.ssrc != ssrc) { + recv_map_.erase(mit); + oit = recv_map_order_.erase(oit); + } else { + ++oit; + } + } + } + recv_active_ssrc_ = ssrc; + + bool collision = recv_map_.find(rtp_timestamp) != recv_map_.end(); + + // Evict oldest entry if at capacity + while (recv_map_.size() >= kMaxRecvMapEntries && + !recv_map_order_.empty()) { + auto evicted_rtp = recv_map_order_.front(); + recv_map_.erase(evicted_rtp); + recv_map_order_.pop_front(); + } + if (!collision) { + recv_map_order_.push_back(rtp_timestamp); + } + recv_map_[rtp_timestamp] = meta.value(); + } + + // Update frame with stripped data + frame->SetData(webrtc::ArrayView(stripped_data)); + } + uint64_t receive_timestamp_us = + subscribe_timing_enabled() ? CurrentUnixTimeMicros() : 0; + emit_subscribe_timing(VideoSubscribeTimingStage::WebrtcReceive, + timing_meta.user_timestamp, timing_meta.frame_id, + receive_timestamp_us); + + // Forward to the appropriate callback (either global or per-SSRC sink). + webrtc::scoped_refptr cb; + { + webrtc::MutexLock lock(&mutex_); + auto it = sink_callbacks_.find(ssrc); + if (it != sink_callbacks_.end()) { + cb = it->second; + } else { + cb = callback_; + } + } + + if (cb) { + emit_subscribe_timing(VideoSubscribeTimingStage::DecoderUpload, + timing_meta.user_timestamp, timing_meta.frame_id); + cb->OnTransformedFrame(std::move(frame)); + } else { + RTC_LOG(LS_WARNING) + << "PacketTrailerTransformer::TransformReceive has no callback" + << " ssrc=" << ssrc << " rtp_ts=" << rtp_timestamp; + } +} + +std::vector PacketTrailerTransformer::AppendTrailer( + webrtc::ArrayView data, + uint64_t user_timestamp, + uint32_t frame_id) { + const bool has_frame_id = frame_id != 0; + const size_t trailer_len = kTimestampTlvSize + + (has_frame_id ? kFrameIdTlvSize : 0) + + kTrailerEnvelopeSize; + std::vector result; + result.reserve(data.size() + trailer_len); + + // Copy original data + result.insert(result.end(), data.begin(), data.end()); + + // All TLV bytes are XORed with 0xFF to prevent H.264 NAL start code + // sequences (0x000001 / 0x00000001) from appearing inside the trailer. + + // TLV: timestamp_us (tag=0x01, len=8, 8 bytes big-endian) + result.push_back(kTagTimestampUs ^ 0xFF); + result.push_back(8 ^ 0xFF); + for (int i = 7; i >= 0; --i) { + result.push_back( + static_cast(((user_timestamp >> (i * 8)) & 0xFF) ^ 0xFF)); + } + + if (has_frame_id) { + // TLV: frame_id (tag=0x02, len=4, 4 bytes big-endian) + result.push_back(kTagFrameId ^ 0xFF); + result.push_back(4 ^ 0xFF); + for (int i = 3; i >= 0; --i) { + result.push_back( + static_cast(((frame_id >> (i * 8)) & 0xFF) ^ 0xFF)); + } + } + + // Envelope: trailer_len (1B, XORed) + magic (4B, NOT XORed) + result.push_back(static_cast(trailer_len ^ 0xFF)); + result.insert(result.end(), std::begin(kPacketTrailerMagic), + std::end(kPacketTrailerMagic)); + + return result; +} + +std::optional PacketTrailerTransformer::ExtractTrailer( + webrtc::ArrayView data, + std::vector& out_data) { + if (data.size() < kTrailerEnvelopeSize) { + out_data.assign(data.begin(), data.end()); + return std::nullopt; + } + + // Check for magic bytes at the end + const uint8_t* magic_start = data.data() + data.size() - 4; + if (std::memcmp(magic_start, kPacketTrailerMagic, 4) != 0) { + out_data.assign(data.begin(), data.end()); + return std::nullopt; + } + + uint8_t trailer_len = data[data.size() - 5] ^ 0xFF; + + if (trailer_len < kTrailerEnvelopeSize || trailer_len > data.size()) { + out_data.assign(data.begin(), data.end()); + return std::nullopt; + } + + // Walk the TLV region: everything from trailer_start up to the envelope. + const uint8_t* trailer_start = data.data() + data.size() - trailer_len; + size_t tlv_region_len = trailer_len - kTrailerEnvelopeSize; + + PacketTrailerMetadata meta{0, 0, 0}; + bool found_any = false; + size_t pos = 0; + + while (pos + 2 <= tlv_region_len) { + uint8_t tag = trailer_start[pos] ^ 0xFF; + uint8_t len = trailer_start[pos + 1] ^ 0xFF; + pos += 2; + + if (pos + len > tlv_region_len) { + break; + } + + const uint8_t* val = trailer_start + pos; + + if (tag == kTagTimestampUs && len == 8) { + uint64_t ts = 0; + for (int i = 0; i < 8; ++i) { + ts = (ts << 8) | (val[i] ^ 0xFF); + } + meta.user_timestamp = ts; + found_any = true; + } else if (tag == kTagFrameId && len == 4) { + uint32_t fid = 0; + for (int i = 0; i < 4; ++i) { + fid = (fid << 8) | (val[i] ^ 0xFF); + } + meta.frame_id = fid; + found_any = true; + } + // Unknown tags are silently skipped. + + pos += len; + } + + out_data.assign(data.begin(), data.end() - trailer_len); + + if (!found_any) { + return std::nullopt; + } + return meta; +} + +void PacketTrailerTransformer::RegisterTransformedFrameCallback( + webrtc::scoped_refptr callback) { + webrtc::MutexLock lock(&mutex_); + callback_ = callback; +} + +void PacketTrailerTransformer::RegisterTransformedFrameSinkCallback( + webrtc::scoped_refptr callback, + uint32_t ssrc) { + webrtc::MutexLock lock(&mutex_); + sink_callbacks_[ssrc] = callback; +} + +void PacketTrailerTransformer::UnregisterTransformedFrameCallback() { + webrtc::MutexLock lock(&mutex_); + callback_ = nullptr; +} + +void PacketTrailerTransformer::UnregisterTransformedFrameSinkCallback( + uint32_t ssrc) { + webrtc::MutexLock lock(&mutex_); + sink_callbacks_.erase(ssrc); +} + +void PacketTrailerTransformer::set_enabled(bool enabled) { + enabled_.store(enabled); +} + +bool PacketTrailerTransformer::enabled() const { + return enabled_.load(); +} + +std::optional PacketTrailerTransformer::lookup_frame_metadata( + uint32_t rtp_timestamp) { + webrtc::MutexLock lock(&recv_map_mutex_); + auto it = recv_map_.find(rtp_timestamp); + if (it == recv_map_.end()) { + return std::nullopt; + } + PacketTrailerMetadata meta = it->second; + recv_map_.erase(it); + for (auto oit = recv_map_order_.begin(); oit != recv_map_order_.end(); + ++oit) { + if (*oit == rtp_timestamp) { + recv_map_order_.erase(oit); + break; + } + } + return meta; +} + +void PacketTrailerTransformer::store_frame_metadata( + int64_t capture_timestamp_us, + uint64_t user_timestamp, + uint32_t frame_id) { + // Truncate to millisecond precision to match what WebRTC stores + // internally. The encoder pipeline converts the VideoFrame's + // timestamp_us to capture_time_ms_ = timestamp_us / 1000, and + // CaptureTime() returns Timestamp::Millis(capture_time_ms_). + // When we call capture_time->us() in TransformSend we get a value + // with the bottom 3 digits zeroed, so we must store with the same + // truncation to ensure the lookup succeeds. + // + // The caller (VideoTrackSource::on_captured_frame) passes the + // TimestampAligner-adjusted timestamp here, which is the same + // value that becomes CaptureTime() in the encoder pipeline. + int64_t key = (capture_timestamp_us / 1000) * 1000; + + webrtc::MutexLock lock(&send_map_mutex_); + + // Evict oldest entries if at capacity + while (send_map_.size() >= kMaxSendMapEntries && !send_map_order_.empty()) { + send_map_.erase(send_map_order_.front()); + send_map_order_.pop_front(); + } + + if (send_map_.find(key) == send_map_.end()) { + send_map_order_.push_back(key); + } + send_map_[key] = PacketTrailerMetadata{user_timestamp, frame_id, 0}; +} + +void PacketTrailerTransformer::set_publish_timing_observer( + rust::Box observer) { + webrtc::MutexLock lock(&publish_timing_observer_mutex_); + publish_timing_observer_ = + std::make_shared>( + std::move(observer)); + publish_timing_enabled_.store(true); +} + +void PacketTrailerTransformer::clear_publish_timing_observer() { + webrtc::MutexLock lock(&publish_timing_observer_mutex_); + publish_timing_observer_.reset(); + publish_timing_enabled_.store(false); +} + +void PacketTrailerTransformer::emit_publish_timing( + VideoPublishTimingStage stage, + uint64_t user_timestamp, + uint32_t frame_id) const { + if (!publish_timing_enabled()) { + return; + } + + std::shared_ptr> observer; + { + webrtc::MutexLock lock(&publish_timing_observer_mutex_); + observer = publish_timing_observer_; + } + if (!observer) { + return; + } + + (*observer)->on_publish_timing(VideoPublishTimingEvent{ + stage, CurrentUnixTimeMicros(), user_timestamp, frame_id}); +} + +void PacketTrailerTransformer::set_subscribe_timing_observer( + rust::Box observer) { + webrtc::MutexLock lock(&subscribe_timing_observer_mutex_); + subscribe_timing_observer_ = + std::make_shared>( + std::move(observer)); + subscribe_timing_enabled_.store(true); +} + +void PacketTrailerTransformer::clear_subscribe_timing_observer() { + webrtc::MutexLock lock(&subscribe_timing_observer_mutex_); + subscribe_timing_observer_.reset(); + subscribe_timing_enabled_.store(false); +} + +void PacketTrailerTransformer::emit_subscribe_timing( + VideoSubscribeTimingStage stage, + uint64_t user_timestamp, + uint32_t frame_id) const { + if (!subscribe_timing_enabled()) { + return; + } + + emit_subscribe_timing(stage, user_timestamp, frame_id, + CurrentUnixTimeMicros()); +} + +void PacketTrailerTransformer::emit_subscribe_timing( + VideoSubscribeTimingStage stage, + uint64_t user_timestamp, + uint32_t frame_id, + uint64_t timestamp_us) const { + if (!subscribe_timing_enabled()) { + return; + } + + std::shared_ptr> observer; + { + webrtc::MutexLock lock(&subscribe_timing_observer_mutex_); + observer = subscribe_timing_observer_; + } + if (!observer) { + return; + } + + (*observer)->on_subscribe_timing(VideoSubscribeTimingEvent{ + stage, timestamp_us, user_timestamp, frame_id}); +} + +bool PacketTrailerTransformer::publish_timing_enabled() const { + return publish_timing_enabled_.load(); +} + +bool PacketTrailerTransformer::subscribe_timing_enabled() const { + return subscribe_timing_enabled_.load(); +} + +// PacketTrailerHandler implementation + +PacketTrailerHandler::PacketTrailerHandler( + std::shared_ptr rtc_runtime, + webrtc::scoped_refptr sender) + : rtc_runtime_(rtc_runtime), sender_(sender) { + transformer_ = webrtc::make_ref_counted( + PacketTrailerTransformer::Direction::kSend); + sender->SetEncoderToPacketizerFrameTransformer(transformer_); +} + +PacketTrailerHandler::PacketTrailerHandler( + std::shared_ptr rtc_runtime, + webrtc::scoped_refptr receiver) + : rtc_runtime_(rtc_runtime), receiver_(receiver) { + transformer_ = webrtc::make_ref_counted( + PacketTrailerTransformer::Direction::kReceive); + receiver->SetDepacketizerToDecoderFrameTransformer(transformer_); +} + +void PacketTrailerHandler::set_enabled(bool enabled) const { + transformer_->set_enabled(enabled); +} + +bool PacketTrailerHandler::enabled() const { + return transformer_->enabled(); +} + +uint64_t PacketTrailerHandler::lookup_timestamp(uint32_t rtp_timestamp) const { + auto meta = transformer_->lookup_frame_metadata(rtp_timestamp); + if (meta.has_value()) { + last_frame_id_ = meta->frame_id; + return meta->user_timestamp; + } + return UINT64_MAX; +} + +uint32_t PacketTrailerHandler::last_lookup_frame_id() const { + return last_frame_id_; +} + +void PacketTrailerHandler::store_frame_metadata( + int64_t capture_timestamp_us, + uint64_t user_timestamp, + uint32_t frame_id) const { + transformer_->store_frame_metadata(capture_timestamp_us, user_timestamp, frame_id); +} + +void PacketTrailerHandler::set_publish_timing_observer( + rust::Box observer) const { + transformer_->set_publish_timing_observer(std::move(observer)); +} + +void PacketTrailerHandler::clear_publish_timing_observer() const { + transformer_->clear_publish_timing_observer(); +} + +void PacketTrailerHandler::emit_publish_timing( + VideoPublishTimingStage stage, + uint64_t user_timestamp, + uint32_t frame_id) const { + transformer_->emit_publish_timing(stage, user_timestamp, frame_id); +} + +void PacketTrailerHandler::set_subscribe_timing_observer( + rust::Box observer) const { + transformer_->set_subscribe_timing_observer(std::move(observer)); +} + +void PacketTrailerHandler::clear_subscribe_timing_observer() const { + transformer_->clear_subscribe_timing_observer(); +} + +void PacketTrailerHandler::emit_subscribe_timing( + VideoSubscribeTimingStage stage, + uint64_t user_timestamp, + uint32_t frame_id) const { + transformer_->emit_subscribe_timing(stage, user_timestamp, frame_id); +} + +webrtc::scoped_refptr PacketTrailerHandler::transformer() const { + return transformer_; +} + +// Factory functions + +std::shared_ptr new_packet_trailer_sender( + std::shared_ptr peer_factory, + std::shared_ptr sender) { + return std::make_shared( + peer_factory->rtc_runtime(), sender->rtc_sender()); +} + +std::shared_ptr new_packet_trailer_receiver( + std::shared_ptr peer_factory, + std::shared_ptr receiver) { + return std::make_shared( + peer_factory->rtc_runtime(), receiver->rtc_receiver()); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/packet_trailer.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/packet_trailer.rs new file mode 100644 index 0000000..3a33944 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/packet_trailer.rs @@ -0,0 +1,176 @@ +// Copyright 2026 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::impl_thread_safety; + +/// Callback invoked for native video publish pipeline timing events. +pub type OnVideoPublishTiming = Box; +/// Callback invoked for native video subscribe pipeline timing events. +pub type OnVideoSubscribeTiming = + Box; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + #[repr(i32)] + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum VideoPublishTimingStage { + EncoderUpload, + EncoderOutput, + WebrtcPacketize, + } + + #[repr(i32)] + #[derive(Debug, Clone, Copy, PartialEq, Eq)] + pub enum VideoSubscribeTimingStage { + WebrtcReceive, + DecoderUpload, + DecoderOutput, + } + + #[derive(Debug, Clone, Copy)] + pub struct VideoPublishTimingEvent { + pub stage: VideoPublishTimingStage, + pub timestamp_us: u64, + pub capture_timestamp_us: u64, + pub frame_id: u32, + } + + #[derive(Debug, Clone, Copy)] + pub struct VideoSubscribeTimingEvent { + pub stage: VideoSubscribeTimingStage, + pub timestamp_us: u64, + pub capture_timestamp_us: u64, + pub frame_id: u32, + } + + unsafe extern "C++" { + include!("livekit/packet_trailer.h"); + include!("livekit/rtp_sender.h"); + include!("livekit/rtp_receiver.h"); + include!("livekit/peer_connection_factory.h"); + + type RtpSender = crate::rtp_sender::ffi::RtpSender; + type RtpReceiver = crate::rtp_receiver::ffi::RtpReceiver; + type PeerConnectionFactory = crate::peer_connection_factory::ffi::PeerConnectionFactory; + + /// Handler for packet trailer embedding/extraction on RTP streams. + pub type PacketTrailerHandler; + + /// Enable/disable timestamp embedding. + fn set_enabled(self: &PacketTrailerHandler, enabled: bool); + + /// Check if timestamp embedding is enabled. + fn enabled(self: &PacketTrailerHandler) -> bool; + + /// Lookup the user timestamp for a given RTP timestamp (receiver side). + /// Returns -1 if not found. The entry is removed after lookup. + /// Also caches the frame_id for retrieval via last_lookup_frame_id(). + fn lookup_timestamp(self: &PacketTrailerHandler, rtp_timestamp: u32) -> u64; + + /// Returns the frame_id from the most recent successful + /// lookup_timestamp() call. + fn last_lookup_frame_id(self: &PacketTrailerHandler) -> u32; + + /// Store frame metadata for a given capture timestamp (sender side). + fn store_frame_metadata( + self: &PacketTrailerHandler, + capture_timestamp_us: i64, + user_timestamp: u64, + frame_id: u32, + ); + + /// Set a callback for sender-side publish timing events. + fn set_publish_timing_observer( + self: &PacketTrailerHandler, + observer: Box, + ); + + /// Clear the sender-side publish timing callback. + fn clear_publish_timing_observer(self: &PacketTrailerHandler); + + /// Set a callback for receiver-side subscribe timing events. + fn set_subscribe_timing_observer( + self: &PacketTrailerHandler, + observer: Box, + ); + + /// Clear the receiver-side subscribe timing callback. + fn clear_subscribe_timing_observer(self: &PacketTrailerHandler); + + /// Emit a receiver-side subscribe timing event. + fn emit_subscribe_timing( + self: &PacketTrailerHandler, + stage: VideoSubscribeTimingStage, + user_timestamp: u64, + frame_id: u32, + ); + + /// Create a new packet trailer handler for a sender. + fn new_packet_trailer_sender( + peer_factory: SharedPtr, + sender: SharedPtr, + ) -> SharedPtr; + + /// Create a new packet trailer handler for a receiver. + fn new_packet_trailer_receiver( + peer_factory: SharedPtr, + receiver: SharedPtr, + ) -> SharedPtr; + } + + extern "Rust" { + type VideoPublishTimingObserverWrapper; + type VideoSubscribeTimingObserverWrapper; + + fn on_publish_timing( + self: &VideoPublishTimingObserverWrapper, + event: VideoPublishTimingEvent, + ); + + fn on_subscribe_timing( + self: &VideoSubscribeTimingObserverWrapper, + event: VideoSubscribeTimingEvent, + ); + } +} + +impl_thread_safety!(ffi::PacketTrailerHandler, Send + Sync); + +pub struct VideoPublishTimingObserverWrapper { + observer: OnVideoPublishTiming, +} + +impl VideoPublishTimingObserverWrapper { + pub fn new(observer: OnVideoPublishTiming) -> Self { + Self { observer } + } + + fn on_publish_timing(&self, event: ffi::VideoPublishTimingEvent) { + (self.observer)(event); + } +} + +pub struct VideoSubscribeTimingObserverWrapper { + observer: OnVideoSubscribeTiming, +} + +impl VideoSubscribeTimingObserverWrapper { + pub fn new(observer: OnVideoSubscribeTiming) -> Self { + Self { observer } + } + + fn on_subscribe_timing(&self, event: ffi::VideoSubscribeTimingEvent) { + (self.observer)(event); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/peer_connection.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/peer_connection.cpp new file mode 100644 index 0000000..6b6f602 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/peer_connection.cpp @@ -0,0 +1,476 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/peer_connection.h" +#include "livekit/peer_connection_factory.h" + +#include + +#include "api/data_channel_interface.h" +#include "api/peer_connection_interface.h" +#include "api/scoped_refptr.h" +#include "livekit/candidate.h" +#include "livekit/data_channel.h" +#include "livekit/jsep.h" +#include "livekit/media_stream.h" +#include "livekit/rtc_error.h" +#include "livekit/rtp_transceiver.h" +#include "rtc_base/logging.h" + +namespace livekit_ffi { + +webrtc::PeerConnectionInterface::RTCConfiguration to_native_rtc_configuration( + RtcConfiguration config) { + webrtc::PeerConnectionInterface::RTCConfiguration rtc_config{}; + + for (auto item : config.ice_servers) { + webrtc::PeerConnectionInterface::IceServer ice_server; + ice_server.username = item.username.c_str(); + ice_server.password = item.password.c_str(); + + for (auto url : item.urls) + ice_server.urls.emplace_back(url.c_str()); + + rtc_config.servers.push_back(ice_server); + } + + rtc_config.continual_gathering_policy = + static_cast( + config.continual_gathering_policy); + + rtc_config.type = + static_cast( + config.ice_transport_type); + + return rtc_config; +} + +inline webrtc::PeerConnectionInterface::RTCOfferAnswerOptions +to_native_offer_answer_options(const RtcOfferAnswerOptions& options) { + webrtc::PeerConnectionInterface::RTCOfferAnswerOptions rtc_options; + rtc_options.offer_to_receive_video = options.offer_to_receive_video; + rtc_options.offer_to_receive_audio = options.offer_to_receive_audio; + rtc_options.voice_activity_detection = options.voice_activity_detection; + rtc_options.ice_restart = options.ice_restart; + rtc_options.use_rtp_mux = options.use_rtp_mux; + rtc_options.raw_packetization_for_video = options.raw_packetization_for_video; + rtc_options.num_simulcast_layers = options.num_simulcast_layers; + rtc_options.use_obsolete_sctp_sdp = options.use_obsolete_sctp_sdp; + return rtc_options; +} + +PeerConnection::PeerConnection( + std::shared_ptr rtc_runtime, + webrtc::scoped_refptr pc_factory, + rust::Box observer) + : rtc_runtime_(std::move(rtc_runtime)), + pc_factory_(std::move(pc_factory)), + observer_(std::move(observer)) { + RTC_LOG(LS_VERBOSE) << "PeerConnection::PeerConnection()"; +} + +PeerConnection::~PeerConnection() { + RTC_LOG(LS_VERBOSE) << "PeerConnection::~PeerConnection()"; +} + +bool PeerConnection::Initialize( + webrtc::PeerConnectionInterface::RTCConfiguration config) { + webrtc::PeerConnectionDependencies deps{this}; + auto result = + pc_factory_->CreatePeerConnectionOrError(config, std::move(deps)); + + if (!result.ok()) { + RTC_LOG(LS_ERROR) << "Failed to create peer connection: " + << result.error().message(); + return false; + } + peer_connection_ = std::move(result.value()); + return true; +} + +void PeerConnection::set_configuration(RtcConfiguration config) const { + auto result = + peer_connection_->SetConfiguration(to_native_rtc_configuration(config)); + + if (!result.ok()) { + throw std::runtime_error(serialize_error(to_error(result))); + } +} + +void PeerConnection::create_offer( + RtcOfferAnswerOptions options, + rust::Box ctx, + rust::Fn, std::unique_ptr)> + on_success, + rust::Fn, RtcError)> on_error) const { + webrtc::scoped_refptr observer = + webrtc::make_ref_counted(std::move(ctx), on_success, + on_error); + + peer_connection_->CreateOffer(observer.get(), + to_native_offer_answer_options(options)); +} + +void PeerConnection::create_answer( + RtcOfferAnswerOptions options, + rust::Box ctx, + rust::Fn, std::unique_ptr)> + on_success, + rust::Fn, RtcError)> on_error) const { + webrtc::scoped_refptr observer = + webrtc::make_ref_counted(std::move(ctx), on_success, + on_error); + + peer_connection_->CreateAnswer(observer.get(), + to_native_offer_answer_options(options)); +} + +void PeerConnection::set_local_description( + std::unique_ptr desc, + rust::Box ctx, + rust::Fn, RtcError)> on_complete) const { + webrtc::scoped_refptr observer = + webrtc::make_ref_counted(std::move(ctx), + on_complete); + + peer_connection_->SetLocalDescription(desc->clone()->release(), observer); +} + +void PeerConnection::set_remote_description( + std::unique_ptr desc, + rust::Box ctx, + rust::Fn, RtcError)> on_complete) const { + webrtc::scoped_refptr observer = + webrtc::make_ref_counted(std::move(ctx), + on_complete); + + peer_connection_->SetRemoteDescription(desc->clone()->release(), observer); +} + +void PeerConnection::restart_ice() const { + peer_connection_->RestartIce(); +} + +void PeerConnection::add_ice_candidate( + std::shared_ptr candidate, + rust::Box ctx, + rust::Fn, RtcError)> on_complete) const { + peer_connection_->AddIceCandidate( + candidate->release(), [&](const webrtc::RTCError& err) { + on_complete(std::move(ctx), to_error(err)); + }); +} + +std::shared_ptr PeerConnection::create_data_channel( + rust::String label, + DataChannelInit init) const { + webrtc::DataChannelInit rtc_init = to_native_data_channel_init(init); + auto result = + peer_connection_->CreateDataChannelOrError(label.c_str(), &rtc_init); + + if (!result.ok()) { + throw std::runtime_error(serialize_error(to_error(result.error()))); + } + + return std::make_shared(rtc_runtime_, result.value()); +} + +std::shared_ptr PeerConnection::add_track( + std::shared_ptr track, + const rust::Vec& stream_ids) const { + std::vector std_stream_ids(stream_ids.begin(), stream_ids.end()); + auto result = peer_connection_->AddTrack(track->rtc_track(), std_stream_ids); + if (!result.ok()) { + throw std::runtime_error(serialize_error(to_error(result.error()))); + } + + return std::make_shared(rtc_runtime_, result.value(), + peer_connection_); +} + +void PeerConnection::remove_track(std::shared_ptr sender) const { + auto error = peer_connection_->RemoveTrackOrError(sender->rtc_sender()); + if (!error.ok()) + throw std::runtime_error(serialize_error(to_error(error))); +} + +void PeerConnection::get_stats( + rust::Box ctx, + rust::Fn, rust::String)> on_stats) const { + auto observer = webrtc::make_ref_counted>( + std::move(ctx), on_stats); + peer_connection_->GetStats(observer.get()); +} + +std::shared_ptr PeerConnection::add_transceiver( + std::shared_ptr track, + RtpTransceiverInit init) const { + auto result = peer_connection_->AddTransceiver( + track->rtc_track(), to_native_rtp_transceiver_init(init)); + if (!result.ok()) + throw std::runtime_error(serialize_error(to_error(result.error()))); + + return std::make_shared(rtc_runtime_, result.value(), + peer_connection_); +} + +std::shared_ptr PeerConnection::add_transceiver_for_media( + MediaType media_type, + RtpTransceiverInit init) const { + auto result = peer_connection_->AddTransceiver( + static_cast(media_type), + to_native_rtp_transceiver_init(init)); + + if (!result.ok()) + throw std::runtime_error(serialize_error(to_error(result.error()))); + + return std::make_shared(rtc_runtime_, result.value(), + peer_connection_); +} + +rust::Vec PeerConnection::get_senders() const { + rust::Vec vec; + for (auto sender : peer_connection_->GetSenders()) + vec.push_back(RtpSenderPtr{ + std::make_shared(rtc_runtime_, sender, peer_connection_)}); + + return vec; +} + +rust::Vec PeerConnection::get_receivers() const { + rust::Vec vec; + for (auto receiver : peer_connection_->GetReceivers()) + vec.push_back(RtpReceiverPtr{std::make_shared( + rtc_runtime_, receiver, peer_connection_)}); + + return vec; +} + +rust::Vec PeerConnection::get_transceivers() const { + rust::Vec vec; + for (auto transceiver : peer_connection_->GetTransceivers()) + vec.push_back(RtpTransceiverPtr{std::make_shared( + rtc_runtime_, transceiver, peer_connection_)}); + + return vec; +} + +std::unique_ptr PeerConnection::current_local_description() + const { + auto local_description = peer_connection_->current_local_description(); + if (local_description) + return std::make_unique(local_description->Clone()); + + return nullptr; +} + +std::unique_ptr PeerConnection::current_remote_description() + const { + auto remote_description = peer_connection_->current_remote_description(); + if (remote_description) + return std::make_unique(remote_description->Clone()); + + return nullptr; +} + +std::unique_ptr PeerConnection::pending_local_description() + const { + auto local_description = peer_connection_->pending_local_description(); + if (local_description) + return std::make_unique(local_description->Clone()); + + return nullptr; +} + +std::unique_ptr PeerConnection::pending_remote_description() + const { + auto remote_description = peer_connection_->pending_remote_description(); + if (remote_description) + return std::make_unique(remote_description->Clone()); + + return nullptr; +} + +std::unique_ptr PeerConnection::local_description() const { + auto local_description = peer_connection_->local_description(); + if (local_description) + return std::make_unique(local_description->Clone()); + + return nullptr; +} + +std::unique_ptr PeerConnection::remote_description() const { + auto remote_description = peer_connection_->remote_description(); + if (remote_description) + return std::make_unique(remote_description->Clone()); + + return nullptr; +} + +PeerConnectionState PeerConnection::connection_state() const { + return static_cast( + peer_connection_->peer_connection_state()); +} + +SignalingState PeerConnection::signaling_state() const { + return static_cast(peer_connection_->signaling_state()); +} + +IceGatheringState PeerConnection::ice_gathering_state() const { + return static_cast( + peer_connection_->ice_gathering_state()); +} + +IceConnectionState PeerConnection::ice_connection_state() const { + return static_cast( + peer_connection_->ice_connection_state()); +} + +void PeerConnection::close() const { + peer_connection_->Close(); +} + +// PeerConnectionObserver + +void PeerConnection::OnSignalingChange( + webrtc::PeerConnectionInterface::SignalingState new_state) { + observer_->on_signaling_change(static_cast(new_state)); +} + +void PeerConnection::OnAddStream( + webrtc::scoped_refptr stream) { + observer_->on_add_stream(std::make_unique(rtc_runtime_, stream)); +} + +void PeerConnection::OnRemoveStream( + webrtc::scoped_refptr stream) { + // Find current MediaStream + // observer_->on_remove_stream(std::make_unique(rtc_runtime_, + // stream)); +} + +void PeerConnection::OnDataChannel( + webrtc::scoped_refptr data_channel) { + observer_->on_data_channel( + std::make_shared(rtc_runtime_, data_channel)); +} + +void PeerConnection::OnRenegotiationNeeded() { + observer_->on_renegotiation_needed(); +} + +void PeerConnection::OnNegotiationNeededEvent(uint32_t event_id) { + observer_->on_negotiation_needed_event(event_id); +} + +void PeerConnection::OnIceConnectionChange( + webrtc::PeerConnectionInterface::IceConnectionState new_state) { + observer_->on_ice_connection_change( + static_cast(new_state)); +} + +void PeerConnection::OnStandardizedIceConnectionChange( + webrtc::PeerConnectionInterface::IceConnectionState new_state) { + observer_->on_standardized_ice_connection_change( + static_cast(new_state)); +} + +void PeerConnection::OnConnectionChange( + webrtc::PeerConnectionInterface::PeerConnectionState new_state) { + observer_->on_connection_change(static_cast(new_state)); +} + +void PeerConnection::OnIceGatheringChange( + webrtc::PeerConnectionInterface::IceGatheringState new_state) { + observer_->on_ice_gathering_change(static_cast(new_state)); +} + +void PeerConnection::OnIceCandidate( + const webrtc::IceCandidate* candidate) { + auto new_candidate = webrtc::CreateIceCandidate(candidate->sdp_mid(), + candidate->sdp_mline_index(), + candidate->candidate()); + observer_->on_ice_candidate( + std::make_unique(std::move(new_candidate))); +} + +void PeerConnection::OnIceCandidateError(const std::string& address, + int port, + const std::string& url, + int error_code, + const std::string& error_text) { + observer_->on_ice_candidate_error(address, port, url, error_code, error_text); +} + +void PeerConnection::OnIceCandidateRemoved(const webrtc::IceCandidate* ice_candidate) { + rust::Vec vec; + if(ice_candidate != nullptr) { + vec.push_back(CandidatePtr{std::make_unique(ice_candidate->candidate())}); + } + observer_->on_ice_candidates_removed(std::move(vec)); +} + +void PeerConnection::OnIceConnectionReceivingChange(bool receiving) { + observer_->on_ice_connection_receiving_change(receiving); +} + +void PeerConnection::OnIceSelectedCandidatePairChanged( + const webrtc::CandidatePairChangeEvent& event) { + CandidatePairChangeEvent e{}; + e.selected_candidate_pair.local = + std::make_unique(event.selected_candidate_pair.local); + e.selected_candidate_pair.remote = + std::make_unique(event.selected_candidate_pair.remote); + e.last_data_received_ms = event.last_data_received_ms; + e.reason = event.reason; + e.estimated_disconnected_time_ms = event.estimated_disconnected_time_ms; + + observer_->on_ice_selected_candidate_pair_changed(std::move(e)); +} + +void PeerConnection::OnAddTrack( + webrtc::scoped_refptr receiver, + const std::vector>& + streams) { + rust::Vec vec; + + for (const auto& item : streams) { + vec.push_back( + MediaStreamPtr{std::make_unique(rtc_runtime_, item)}); + } + + observer_->on_add_track( + std::make_unique(rtc_runtime_, receiver, peer_connection_), + std::move(vec)); +} + +void PeerConnection::OnTrack( + webrtc::scoped_refptr transceiver) { + observer_->on_track(std::make_unique( + rtc_runtime_, transceiver, peer_connection_)); +} + +void PeerConnection::OnRemoveTrack( + webrtc::scoped_refptr receiver) { + observer_->on_remove_track( + std::make_unique(rtc_runtime_, receiver, peer_connection_)); +} + +void PeerConnection::OnInterestingUsage(int usage_pattern) { + observer_->on_interesting_usage(usage_pattern); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/peer_connection.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/peer_connection.rs new file mode 100644 index 0000000..0b21cf6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/peer_connection.rs @@ -0,0 +1,235 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::any::Any; + +use crate::impl_thread_safety; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + #[repr(i32)] + pub enum PeerConnectionState { + New, + Connecting, + Connected, + Disconnected, + Failed, + Closed, + } + + #[repr(i32)] + pub enum SignalingState { + Stable, + HaveLocalOffer, + HaveLocalPrAnswer, + HaveRemoteOffer, + HaveRemotePrAnswer, + Closed, + } + + #[repr(i32)] + pub enum IceConnectionState { + IceConnectionNew, + IceConnectionChecking, + IceConnectionConnected, + IceConnectionCompleted, + IceConnectionFailed, + IceConnectionDisconnected, + IceConnectionClosed, + IceConnectionMax, + } + + #[repr(i32)] + pub enum IceGatheringState { + IceGatheringNew, + IceGatheringGathering, + IceGatheringComplete, + } + + #[repr(i32)] + pub enum ContinualGatheringPolicy { + GatherOnce, + GatherContinually, + } + + #[repr(i32)] + pub enum IceTransportsType { + None, + Relay, + NoHost, + All, + } + + pub struct RtcOfferAnswerOptions { + offer_to_receive_video: i32, + offer_to_receive_audio: i32, + voice_activity_detection: bool, + ice_restart: bool, + use_rtp_mux: bool, + raw_packetization_for_video: bool, + num_simulcast_layers: i32, + use_obsolete_sctp_sdp: bool, + } + + pub struct IceServer { + pub urls: Vec, + pub username: String, + pub password: String, + } + + pub struct RtcConfiguration { + pub ice_servers: Vec, + pub continual_gathering_policy: ContinualGatheringPolicy, + pub ice_transport_type: IceTransportsType, + } + + extern "C++" { + include!("livekit/rtc_error.h"); + include!("livekit/helper.h"); + include!("livekit/candidate.h"); + include!("livekit/media_stream.h"); + include!("livekit/rtp_transceiver.h"); + include!("livekit/rtp_sender.h"); + include!("livekit/rtp_receiver.h"); + include!("livekit/data_channel.h"); + include!("livekit/jsep.h"); + include!("livekit/webrtc.h"); + + type RtpSenderPtr = crate::helper::ffi::RtpSenderPtr; + type RtpReceiverPtr = crate::helper::ffi::RtpReceiverPtr; + type RtpTransceiverPtr = crate::helper::ffi::RtpTransceiverPtr; + type RtcError = crate::rtc_error::ffi::RtcError; + type Candidate = crate::candidate::ffi::Candidate; + type IceCandidate = crate::jsep::ffi::IceCandidate; + type DataChannel = crate::data_channel::ffi::DataChannel; + type DataChannelInit = crate::data_channel::ffi::DataChannelInit; + type RtpSender = crate::rtp_sender::ffi::RtpSender; + type RtpReceiver = crate::rtp_receiver::ffi::RtpReceiver; + type RtpTransceiver = crate::rtp_transceiver::ffi::RtpTransceiver; + type RtpTransceiverInit = crate::rtp_transceiver::ffi::RtpTransceiverInit; + type MediaStream = crate::media_stream::ffi::MediaStream; + type MediaStreamTrack = crate::media_stream::ffi::MediaStreamTrack; + type SessionDescription = crate::jsep::ffi::SessionDescription; + type MediaType = crate::webrtc::ffi::MediaType; + } + + unsafe extern "C++" { + include!("livekit/peer_connection.h"); + + type PeerConnection; + + fn set_configuration(self: &PeerConnection, config: RtcConfiguration) -> Result<()>; + + fn create_offer( + self: &PeerConnection, + options: RtcOfferAnswerOptions, + ctx: Box, + on_success: fn(ctx: Box, sdp: UniquePtr), + on_error: fn(ctx: Box, error: RtcError), + ); + fn create_answer( + self: &PeerConnection, + options: RtcOfferAnswerOptions, + ctx: Box, + on_success: fn(ctx: Box, sdp: UniquePtr), + on_error: fn(ctx: Box, error: RtcError), + ); + fn set_local_description( + self: &PeerConnection, + desc: UniquePtr, + ctx: Box, + on_complete: fn(ctx: Box, error: RtcError), + ); + fn set_remote_description( + self: &PeerConnection, + desc: UniquePtr, + ctx: Box, + on_complete: fn(ctx: Box, error: RtcError), + ); + fn add_track( + self: &PeerConnection, + track: SharedPtr, + stream_ids: &Vec, + ) -> Result>; + fn remove_track(self: &PeerConnection, sender: SharedPtr) -> Result<()>; + fn get_stats( + self: &PeerConnection, + ctx: Box, + on_stats: fn(ctx: Box, json: String), + ); + fn add_transceiver( + self: &PeerConnection, + track: SharedPtr, + init: RtpTransceiverInit, + ) -> Result>; + fn add_transceiver_for_media( + self: &PeerConnection, + media_type: MediaType, + init: RtpTransceiverInit, + ) -> Result>; + fn get_senders(self: &PeerConnection) -> Vec; + fn get_receivers(self: &PeerConnection) -> Vec; + fn get_transceivers(self: &PeerConnection) -> Vec; + fn create_data_channel( + self: &PeerConnection, + label: String, + init: DataChannelInit, + ) -> Result>; + fn add_ice_candidate( + self: &PeerConnection, + candidate: SharedPtr, + ctx: Box, + on_complete: fn(ctx: Box, error: RtcError), + ); + fn restart_ice(self: &PeerConnection); + fn current_local_description(self: &PeerConnection) -> UniquePtr; + fn current_remote_description(self: &PeerConnection) -> UniquePtr; + fn connection_state(self: &PeerConnection) -> PeerConnectionState; + fn signaling_state(self: &PeerConnection) -> SignalingState; + fn ice_gathering_state(self: &PeerConnection) -> IceGatheringState; + fn ice_connection_state(self: &PeerConnection) -> IceConnectionState; + fn close(self: &PeerConnection); + + fn _shared_peer_connection() -> SharedPtr; // Ignore + } + + extern "Rust" { + type PeerContext; + } +} + +#[repr(transparent)] +pub struct PeerContext(pub Box); + +// https://webrtc.github.io/webrtc-org/native-code/native-apis/ +impl_thread_safety!(ffi::PeerConnection, Send + Sync); + +impl Default for ffi::RtcOfferAnswerOptions { + // static const int kUndefined = -1; + // static const int kMaxOfferToReceiveMedia = 1; + // static const int kOfferToReceiveMediaTrue = 1; + + fn default() -> Self { + Self { + offer_to_receive_video: -1, + offer_to_receive_audio: -1, + voice_activity_detection: true, + ice_restart: false, + use_rtp_mux: true, + raw_packetization_for_video: false, + num_simulcast_layers: 1, + use_obsolete_sctp_sdp: false, + } + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/peer_connection_factory.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/peer_connection_factory.cpp new file mode 100644 index 0000000..cfb2d0c --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/peer_connection_factory.cpp @@ -0,0 +1,170 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/peer_connection_factory.h" + +#include +#include + +#include "api/audio_codecs/builtin_audio_decoder_factory.h" +#include "api/audio_codecs/builtin_audio_encoder_factory.h" +#include "api/audio/builtin_audio_processing_builder.h" +#include "api/create_modular_peer_connection_factory.h" +#include "api/environment/environment_factory.h" +#include "api/peer_connection_interface.h" +#include "api/rtc_error.h" +#include "api/enable_media.h" +#include "api/rtc_event_log/rtc_event_log_factory.h" +#include "api/task_queue/default_task_queue_factory.h" +#include "api/video_codecs/builtin_video_decoder_factory.h" +#include "api/video_codecs/builtin_video_encoder_factory.h" +#include "api/audio/audio_device.h" +#include "api/audio_options.h" +#include "livekit/adm_proxy.h" +#include "livekit/audio_track.h" +#include "livekit/peer_connection.h" +#include "livekit/rtc_error.h" +#include "livekit/rtp_parameters.h" +#include "livekit/video_decoder_factory.h" +#include "livekit/video_encoder_factory.h" +#include "livekit/webrtc.h" +#include "rtc_base/thread.h" +#include "webrtc-sys/src/peer_connection.rs.h" +#include "webrtc-sys/src/peer_connection_factory.rs.h" + +namespace livekit_ffi { + +class PeerConnectionObserver; + +PeerConnectionFactory::PeerConnectionFactory( + std::shared_ptr rtc_runtime) + : rtc_runtime_(rtc_runtime), + env_(webrtc::EnvironmentFactory().Create()) { + webrtc::PeerConnectionFactoryDependencies dependencies; + dependencies.network_thread = rtc_runtime_->network_thread(); + dependencies.worker_thread = rtc_runtime_->worker_thread(); + dependencies.signaling_thread = rtc_runtime_->signaling_thread(); + dependencies.socket_factory = rtc_runtime_->network_thread()->socketserver(); + dependencies.event_log_factory = std::make_unique(); + + // Create AdmProxy - it creates and initializes Platform ADM internally + adm_proxy_ = rtc_runtime_->worker_thread()->BlockingCall([&] { + return webrtc::make_ref_counted( + env_, rtc_runtime_->worker_thread()); + }); + audio_device_ = std::make_shared(adm_proxy_); + + dependencies.adm = adm_proxy_; + + dependencies.video_encoder_factory = + std::move(std::make_unique()); + dependencies.video_decoder_factory = + std::move(std::make_unique()); + dependencies.audio_encoder_factory = webrtc::CreateBuiltinAudioEncoderFactory(); + dependencies.audio_decoder_factory = webrtc::CreateBuiltinAudioDecoderFactory(); + dependencies.audio_processing_builder = std::make_unique(); + + webrtc::EnableMedia(dependencies); + peer_factory_ = + webrtc::CreateModularPeerConnectionFactory(std::move(dependencies)); + + if (peer_factory_.get() == nullptr) { + RTC_LOG_ERR(LS_ERROR) << "Failed to create PeerConnectionFactory"; + return; + } +} + +PeerConnectionFactory::~PeerConnectionFactory() { + RTC_LOG(LS_VERBOSE) << "PeerConnectionFactory::~PeerConnectionFactory()"; + + peer_factory_ = nullptr; + audio_device_ = nullptr; + rtc_runtime_->worker_thread()->BlockingCall( + [this] { adm_proxy_ = nullptr; }); +} + +std::shared_ptr PeerConnectionFactory::create_peer_connection( + RtcConfiguration config, + rust::Box observer) const { + std::shared_ptr pc = std::make_shared( + rtc_runtime_, peer_factory_, std::move(observer)); + + if (!pc->Initialize(to_native_rtc_configuration(config))) { + throw std::runtime_error(serialize_error(to_error(webrtc::RTCError( + webrtc::RTCErrorType::INTERNAL_ERROR, "failed to initialize pc")))); + } + + return pc; +} + +std::shared_ptr PeerConnectionFactory::create_video_track( + rust::String label, + std::shared_ptr source) const { + return std::static_pointer_cast( + rtc_runtime_->get_or_create_media_stream_track( + peer_factory_->CreateVideoTrack(source->get(), label.c_str()))); +} + +std::shared_ptr PeerConnectionFactory::create_audio_track( + rust::String label, + std::shared_ptr source) const { + return std::static_pointer_cast( + rtc_runtime_->get_or_create_media_stream_track( + peer_factory_->CreateAudioTrack(label.c_str(), source->get().get()))); +} + +std::shared_ptr PeerConnectionFactory::create_device_audio_track( + rust::String label) const { + // Create an audio source that uses the ADM for capture + webrtc::AudioOptions audio_options; + audio_options.echo_cancellation = true; + audio_options.auto_gain_control = true; + audio_options.noise_suppression = true; + + webrtc::scoped_refptr audio_source = + peer_factory_->CreateAudioSource(audio_options); + + if (!audio_source) { + RTC_LOG(LS_ERROR) << "Failed to create device audio source"; + return nullptr; + } + + return std::static_pointer_cast( + rtc_runtime_->get_or_create_media_stream_track( + peer_factory_->CreateAudioTrack(label.c_str(), audio_source.get()))); +} + +RtpCapabilities PeerConnectionFactory::rtp_sender_capabilities( + MediaType type) const { + return to_rust_rtp_capabilities(peer_factory_->GetRtpSenderCapabilities( + static_cast(type))); +} + +RtpCapabilities PeerConnectionFactory::rtp_receiver_capabilities( + MediaType type) const { + return to_rust_rtp_capabilities(peer_factory_->GetRtpReceiverCapabilities( + static_cast(type))); +} + +std::shared_ptr PeerConnectionFactory::audio_device() const { + return audio_device_; +} + +std::shared_ptr create_peer_connection_factory() { + return std::make_shared(RtcRuntime::create()); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/peer_connection_factory.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/peer_connection_factory.rs new file mode 100644 index 0000000..16794f6 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/peer_connection_factory.rs @@ -0,0 +1,314 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub use cxx::SharedPtr; + +use std::sync::Arc; + +use crate::{ + candidate::ffi::Candidate, data_channel::ffi::DataChannel, impl_thread_safety, + jsep::ffi::IceCandidate, media_stream::ffi::MediaStream, rtp_receiver::ffi::RtpReceiver, + rtp_transceiver::ffi::RtpTransceiver, +}; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + pub struct CandidatePair { + local: SharedPtr, + remote: SharedPtr, + } + + pub struct CandidatePairChangeEvent { + selected_candidate_pair: CandidatePair, + last_data_received_ms: i64, + reason: String, + estimated_disconnected_time_ms: i64, + } + + extern "C++" { + include!("livekit/rtp_parameters.h"); + include!("livekit/rtc_error.h"); + include!("livekit/helper.h"); + include!("livekit/candidate.h"); + include!("livekit/media_stream.h"); + include!("livekit/rtp_transceiver.h"); + include!("livekit/rtp_sender.h"); + include!("livekit/rtp_receiver.h"); + include!("livekit/data_channel.h"); + include!("livekit/jsep.h"); + include!("livekit/webrtc.h"); + include!("livekit/peer_connection.h"); + include!("livekit/audio_track.h"); + + type RtcConfiguration = crate::peer_connection::ffi::RtcConfiguration; + type PeerConnectionState = crate::peer_connection::ffi::PeerConnectionState; + type SignalingState = crate::peer_connection::ffi::SignalingState; + type IceConnectionState = crate::peer_connection::ffi::IceConnectionState; + type IceGatheringState = crate::peer_connection::ffi::IceGatheringState; + type AudioTrackSource = crate::audio_track::ffi::AudioTrackSource; + type VideoTrackSource = crate::video_track::ffi::VideoTrackSource; + type RtpCapabilities = crate::rtp_parameters::ffi::RtpCapabilities; + type AudioTrack = crate::audio_track::ffi::AudioTrack; + type VideoTrack = crate::video_track::ffi::VideoTrack; + type MediaStreamPtr = crate::helper::ffi::MediaStreamPtr; + type CandidatePtr = crate::helper::ffi::CandidatePtr; + type RtpSenderPtr = crate::helper::ffi::RtpSenderPtr; + type RtpReceiverPtr = crate::helper::ffi::RtpReceiverPtr; + type RtpTransceiverPtr = crate::helper::ffi::RtpTransceiverPtr; + type RtcError = crate::rtc_error::ffi::RtcError; + type Candidate = crate::candidate::ffi::Candidate; + type IceCandidate = crate::jsep::ffi::IceCandidate; + type DataChannel = crate::data_channel::ffi::DataChannel; + type DataChannelInit = crate::data_channel::ffi::DataChannelInit; + type RtpSender = crate::rtp_sender::ffi::RtpSender; + type RtpReceiver = crate::rtp_receiver::ffi::RtpReceiver; + type RtpTransceiver = crate::rtp_transceiver::ffi::RtpTransceiver; + type RtpTransceiverInit = crate::rtp_transceiver::ffi::RtpTransceiverInit; + type MediaStream = crate::media_stream::ffi::MediaStream; + type MediaStreamTrack = crate::media_stream::ffi::MediaStreamTrack; + type SessionDescription = crate::jsep::ffi::SessionDescription; + type MediaType = crate::webrtc::ffi::MediaType; + } + + unsafe extern "C++" { + include!("livekit/peer_connection_factory.h"); + + type PeerConnection = crate::peer_connection::ffi::PeerConnection; + type PeerConnectionFactory; + + fn create_peer_connection_factory() -> SharedPtr; + + fn create_peer_connection( + self: &PeerConnectionFactory, + config: RtcConfiguration, + observer: Box, + ) -> Result>; + + fn create_video_track( + self: &PeerConnectionFactory, + label: String, + source: SharedPtr, + ) -> SharedPtr; + + fn create_audio_track( + self: &PeerConnectionFactory, + label: String, + source: SharedPtr, + ) -> SharedPtr; + + // Create an audio track that uses the ADM for capture (Platform ADM mode) + fn create_device_audio_track( + self: &PeerConnectionFactory, + label: String, + ) -> SharedPtr; + + fn rtp_sender_capabilities( + self: &PeerConnectionFactory, + kind: MediaType, + ) -> RtpCapabilities; + + fn rtp_receiver_capabilities( + self: &PeerConnectionFactory, + kind: MediaType, + ) -> RtpCapabilities; + } + + extern "Rust" { + type PeerConnectionObserverWrapper; + + fn on_signaling_change(self: &PeerConnectionObserverWrapper, new_state: SignalingState); + fn on_add_stream(self: &PeerConnectionObserverWrapper, stream: SharedPtr); + fn on_remove_stream(self: &PeerConnectionObserverWrapper, stream: SharedPtr); + fn on_data_channel( + self: &PeerConnectionObserverWrapper, + data_channel: SharedPtr, + ); + fn on_renegotiation_needed(self: &PeerConnectionObserverWrapper); + fn on_negotiation_needed_event(self: &PeerConnectionObserverWrapper, event: u32); + fn on_ice_connection_change( + self: &PeerConnectionObserverWrapper, + new_state: IceConnectionState, + ); + fn on_standardized_ice_connection_change( + self: &PeerConnectionObserverWrapper, + new_state: IceConnectionState, + ); + fn on_connection_change( + self: &PeerConnectionObserverWrapper, + new_state: PeerConnectionState, + ); + fn on_ice_gathering_change( + self: &PeerConnectionObserverWrapper, + new_state: IceGatheringState, + ); + fn on_ice_candidate( + self: &PeerConnectionObserverWrapper, + candidate: SharedPtr, + ); + fn on_ice_candidate_error( + self: &PeerConnectionObserverWrapper, + address: String, + port: i32, + url: String, + error_code: i32, + error_text: String, + ); + fn on_ice_candidates_removed( + self: &PeerConnectionObserverWrapper, + removed: Vec, + ); + fn on_ice_connection_receiving_change( + self: &PeerConnectionObserverWrapper, + receiving: bool, + ); + fn on_ice_selected_candidate_pair_changed( + self: &PeerConnectionObserverWrapper, + event: CandidatePairChangeEvent, + ); + fn on_add_track( + self: &PeerConnectionObserverWrapper, + receiver: SharedPtr, + streams: Vec, + ); + fn on_track(self: &PeerConnectionObserverWrapper, transceiver: SharedPtr); + fn on_remove_track(self: &PeerConnectionObserverWrapper, receiver: SharedPtr); + fn on_interesting_usage(self: &PeerConnectionObserverWrapper, usage_pattern: i32); + } +} + +impl_thread_safety!(ffi::PeerConnectionFactory, Send + Sync); + +pub trait PeerConnectionObserver: Send + Sync { + fn on_signaling_change(&self, new_state: ffi::SignalingState); + fn on_add_stream(&self, stream: SharedPtr); + fn on_remove_stream(&self, stream: SharedPtr); + fn on_data_channel(&self, data_channel: SharedPtr); + fn on_renegotiation_needed(&self); + fn on_negotiation_needed_event(&self, event: u32); + fn on_ice_connection_change(&self, new_state: ffi::IceConnectionState); + fn on_standardized_ice_connection_change(&self, new_state: ffi::IceConnectionState); + fn on_connection_change(&self, new_state: ffi::PeerConnectionState); + fn on_ice_gathering_change(&self, new_state: ffi::IceGatheringState); + fn on_ice_candidate(&self, candidate: SharedPtr); + fn on_ice_candidate_error( + &self, + address: String, + port: i32, + url: String, + error_code: i32, + error_text: String, + ); + fn on_ice_candidates_removed(&self, removed: Vec>); + fn on_ice_connection_receiving_change(&self, receiving: bool); + fn on_ice_selected_candidate_pair_changed(&self, event: ffi::CandidatePairChangeEvent); + fn on_add_track(&self, receiver: SharedPtr, streams: Vec>); + fn on_track(&self, transceiver: SharedPtr); + fn on_remove_track(&self, receiver: SharedPtr); + fn on_interesting_usage(&self, usage_pattern: i32); +} + +// Wrapper for PeerConnectionObserver because cxx doesn't support dyn Trait on c++ +// https://github.com/dtolnay/cxx/issues/665 +pub struct PeerConnectionObserverWrapper { + observer: Arc, +} + +impl PeerConnectionObserverWrapper { + pub fn new(observer: Arc) -> Self { + Self { observer } + } + + fn on_signaling_change(&self, new_state: ffi::SignalingState) { + self.observer.on_signaling_change(new_state); + } + + fn on_add_stream(&self, stream: SharedPtr) { + self.observer.on_add_stream(stream); + } + + fn on_remove_stream(&self, stream: SharedPtr) { + self.observer.on_remove_stream(stream); + } + + fn on_data_channel(&self, data_channel: SharedPtr) { + self.observer.on_data_channel(data_channel); + } + + fn on_renegotiation_needed(&self) { + self.observer.on_renegotiation_needed(); + } + + fn on_negotiation_needed_event(&self, event: u32) { + self.observer.on_negotiation_needed_event(event); + } + + fn on_ice_connection_change(&self, new_state: ffi::IceConnectionState) { + self.observer.on_ice_connection_change(new_state); + } + + fn on_standardized_ice_connection_change(&self, new_state: ffi::IceConnectionState) { + self.observer.on_standardized_ice_connection_change(new_state); + } + + fn on_connection_change(&self, new_state: ffi::PeerConnectionState) { + self.observer.on_connection_change(new_state); + } + + fn on_ice_gathering_change(&self, new_state: ffi::IceGatheringState) { + self.observer.on_ice_gathering_change(new_state); + } + + fn on_ice_candidate(&self, candidate: SharedPtr) { + self.observer.on_ice_candidate(candidate); + } + + fn on_ice_candidate_error( + &self, + address: String, + port: i32, + url: String, + error_code: i32, + error_text: String, + ) { + self.observer.on_ice_candidate_error(address, port, url, error_code, error_text); + } + + fn on_ice_candidates_removed(&self, candidates: Vec) { + self.observer.on_ice_candidates_removed(candidates.into_iter().map(|v| v.ptr).collect()); + } + + fn on_ice_connection_receiving_change(&self, receiving: bool) { + self.observer.on_ice_connection_receiving_change(receiving); + } + + fn on_ice_selected_candidate_pair_changed(&self, event: ffi::CandidatePairChangeEvent) { + self.observer.on_ice_selected_candidate_pair_changed(event); + } + + fn on_add_track(&self, receiver: SharedPtr, streams: Vec) { + self.observer.on_add_track(receiver, streams.into_iter().map(|v| v.ptr).collect()); + } + + fn on_track(&self, transceiver: SharedPtr) { + self.observer.on_track(transceiver); + } + + fn on_remove_track(&self, receiver: SharedPtr) { + self.observer.on_remove_track(receiver); + } + + fn on_interesting_usage(&self, usage_pattern: i32) { + self.observer.on_interesting_usage(usage_pattern); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/prohibit_libsrtp_initialization.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/prohibit_libsrtp_initialization.cpp new file mode 100644 index 0000000..7efce17 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/prohibit_libsrtp_initialization.cpp @@ -0,0 +1,23 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "pc/srtp_session.h" + +namespace livekit_ffi { +void ProhibitLibsrtpInitialization() { + webrtc::ProhibitLibsrtpInitialization(); +} +} \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/prohibit_libsrtp_initialization.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/prohibit_libsrtp_initialization.rs new file mode 100644 index 0000000..0ec42b5 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/prohibit_libsrtp_initialization.rs @@ -0,0 +1,22 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + unsafe extern "C++" { + include!("livekit/prohibit_libsrtp_initialization.h"); + + fn ProhibitLibsrtpInitialization(); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/recorded_audio_tap.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/recorded_audio_tap.cpp new file mode 100644 index 0000000..c75d972 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/recorded_audio_tap.cpp @@ -0,0 +1,218 @@ +/* + * Copyright 2026 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/recorded_audio_tap.h" + +#include +#include + +#include "audio/remix_resample.h" +#include "common_audio/include/audio_util.h" +#include "rtc_base/checks.h" +#include "rtc_base/synchronization/mutex.h" +#include "webrtc-sys/src/recorded_audio_tap.rs.h" + +namespace livekit_ffi { + +namespace { + +constexpr int kRecordedSinkSampleRateHz = 48000; +constexpr size_t kRecordedSinkNumChannels = 1; + +// Process-global recorded-audio sink. The platform ADM is a process singleton +// owned by the livekit runtime, so the tap has to live here rather than on any +// one engine handle. +struct GlobalRecordedAudioSink { + webrtc::Mutex lock; + uint64_t generation = 0; + std::optional> sink; +}; + +// Leaked on purpose: a process-global with a mutex must not be torn down during +// static destruction while the ADM capture thread may still touch it. +GlobalRecordedAudioSink& global_recorded_audio_sink() { + static GlobalRecordedAudioSink* instance = new GlobalRecordedAudioSink(); + return *instance; +} + +} // namespace + +uint64_t set_recorded_audio_sink(rust::Box sink) { + GlobalRecordedAudioSink& global = global_recorded_audio_sink(); + webrtc::MutexLock lock(&global.lock); + global.generation += 1; + global.sink.emplace(std::move(sink)); + RTC_DCHECK(global.sink.has_value()); + return global.generation; +} + +void clear_recorded_audio_sink(uint64_t generation) { + GlobalRecordedAudioSink& global = global_recorded_audio_sink(); + webrtc::MutexLock lock(&global.lock); + if (global.generation != generation) { + return; + } + global.sink.reset(); +} + +RecordingTransportProxy::RecordingTransportProxy() { + capture_frame_.sample_rate_hz_ = kRecordedSinkSampleRateHz; + capture_frame_.num_channels_ = kRecordedSinkNumChannels; + capture_frame_.samples_per_channel_ = + webrtc::SampleRateToDefaultChannelSize(kRecordedSinkSampleRateHz); +} + +RecordingTransportProxy::~RecordingTransportProxy() = default; + +void RecordingTransportProxy::set_real_transport( + webrtc::AudioTransport* transport) { + real_transport_.store(transport, std::memory_order_release); +} + +void RecordingTransportProxy::TeeRecordedData(const void* audioSamples, + size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec) { + if (audioSamples == nullptr) { + return; + } + if (nBytesPerSample != sizeof(int16_t)) { + return; + } + if (nChannels == 0) { + return; + } + if (samplesPerSec == 0) { + return; + } + + GlobalRecordedAudioSink& global = global_recorded_audio_sink(); + // TryLock keeps the ADM capture thread wait-free: a frame dropped during an + // install/clear is acceptable, blocking the capture thread is not. + if (!global.lock.TryLock()) { + return; + } + if (global.sink.has_value()) { + webrtc::InterleavedView source( + static_cast(audioSamples), nSamples, nChannels); + webrtc::voe::RemixAndResample(source, static_cast(samplesPerSec), + &capture_resampler_, &capture_frame_); + rust::Slice samples( + capture_frame_.data(), + capture_frame_.num_channels() * capture_frame_.samples_per_channel()); + (*global.sink) + ->on_recorded_data(samples, capture_frame_.sample_rate_hz(), + capture_frame_.num_channels(), + capture_frame_.samples_per_channel()); + } + global.lock.Unlock(); +} + +int32_t RecordingTransportProxy::RecordedDataIsAvailable( + const void* audioSamples, + size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, + uint32_t totalDelayMS, + int32_t clockDrift, + uint32_t currentMicLevel, + bool keyPressed, + uint32_t& newMicLevel) { + TeeRecordedData(audioSamples, nSamples, nBytesPerSample, nChannels, + samplesPerSec); + webrtc::AudioTransport* real = + real_transport_.load(std::memory_order_acquire); + if (real == nullptr) { + newMicLevel = currentMicLevel; + return 0; + } + return real->RecordedDataIsAvailable(audioSamples, nSamples, nBytesPerSample, + nChannels, samplesPerSec, totalDelayMS, + clockDrift, currentMicLevel, keyPressed, + newMicLevel); +} + +int32_t RecordingTransportProxy::RecordedDataIsAvailable( + const void* audioSamples, + size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, + uint32_t totalDelayMS, + int32_t clockDrift, + uint32_t currentMicLevel, + bool keyPressed, + uint32_t& newMicLevel, + std::optional estimatedCaptureTimeNS) { + TeeRecordedData(audioSamples, nSamples, nBytesPerSample, nChannels, + samplesPerSec); + webrtc::AudioTransport* real = + real_transport_.load(std::memory_order_acquire); + if (real == nullptr) { + newMicLevel = currentMicLevel; + return 0; + } + return real->RecordedDataIsAvailable( + audioSamples, nSamples, nBytesPerSample, nChannels, samplesPerSec, + totalDelayMS, clockDrift, currentMicLevel, keyPressed, newMicLevel, + estimatedCaptureTimeNS); +} + +int32_t RecordingTransportProxy::NeedMorePlayData(size_t nSamples, + size_t nBytesPerSample, + size_t nChannels, + uint32_t samplesPerSec, + void* audioSamples, + size_t& nSamplesOut, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms) { + webrtc::AudioTransport* real = + real_transport_.load(std::memory_order_acquire); + if (real == nullptr) { + nSamplesOut = 0; + if (elapsed_time_ms != nullptr) { + *elapsed_time_ms = -1; + } + if (ntp_time_ms != nullptr) { + *ntp_time_ms = -1; + } + return 0; + } + return real->NeedMorePlayData(nSamples, nBytesPerSample, nChannels, + samplesPerSec, audioSamples, nSamplesOut, + elapsed_time_ms, ntp_time_ms); +} + +void RecordingTransportProxy::PullRenderData(int bits_per_sample, + int sample_rate, + size_t number_of_channels, + size_t number_of_frames, + void* audio_data, + int64_t* elapsed_time_ms, + int64_t* ntp_time_ms) { + webrtc::AudioTransport* real = + real_transport_.load(std::memory_order_acquire); + if (real == nullptr) { + return; + } + real->PullRenderData(bits_per_sample, sample_rate, number_of_channels, + number_of_frames, audio_data, elapsed_time_ms, + ntp_time_ms); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/recorded_audio_tap.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/recorded_audio_tap.rs new file mode 100644 index 0000000..9dae459 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/recorded_audio_tap.rs @@ -0,0 +1,57 @@ +// Copyright 2026 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + unsafe extern "C++" { + include!("livekit/recorded_audio_tap.h"); + + fn set_recorded_audio_sink(sink: Box) -> u64; + fn clear_recorded_audio_sink(generation: u64); + } + + extern "Rust" { + type RecordedAudioSinkWrapper; + + fn on_recorded_data( + self: &RecordedAudioSinkWrapper, + data: &[i16], + sample_rate: i32, + nb_channels: usize, + nb_frames: usize, + ); + } +} + +type RecordedAudioCallback = Box; + +pub struct RecordedAudioSinkWrapper { + callback: RecordedAudioCallback, +} + +impl RecordedAudioSinkWrapper { + pub fn new(callback: RecordedAudioCallback) -> RecordedAudioSinkWrapper { + RecordedAudioSinkWrapper { callback } + } + + fn on_recorded_data( + &self, + data: &[i16], + sample_rate: i32, + nb_channels: usize, + nb_frames: usize, + ) { + (self.callback)(data, sample_rate, nb_channels, nb_frames); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtc_error.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtc_error.cpp new file mode 100644 index 0000000..65da79a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtc_error.cpp @@ -0,0 +1,58 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/rtc_error.h" + +#include +#include +#include + +namespace livekit_ffi { + +RtcError to_error(const webrtc::RTCError& error) { + RtcError lk_error; + lk_error.error_detail = static_cast(error.error_detail()); + lk_error.error_type = static_cast(error.type()); + lk_error.has_sctp_cause_code = error.sctp_cause_code().has_value(); + lk_error.sctp_cause_code = error.sctp_cause_code().value_or(0); + lk_error.message = error.message(); + return lk_error; +} + +std::string serialize_error(const RtcError& error) { + std::stringstream ss; + ss << std::hex << std::setfill('0'); + ss << std::setw(8) << (uint32_t)error.error_type; + ss << std::setw(8) << (uint32_t)error.error_detail; + ss << std::setw(2) << (uint16_t)error.has_sctp_cause_code; + ss << std::setw(4) << (uint16_t)error.sctp_cause_code; + ss << std::dec << std::setw(1) << std::string(error.message); + return ss.str(); +} + +#ifdef LIVEKIT_TEST +rust::String serialize_deserialize() { + RtcError lk_error; + lk_error.error_type = RtcErrorType::InternalError; + lk_error.error_detail = RtcErrorDetailType::DataChannelFailure; + lk_error.has_sctp_cause_code = true; + lk_error.sctp_cause_code = 24; + lk_error.message = "this is not a test, I repeat, this is not a test"; + return serialize_error(lk_error); +} +#endif + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtc_error.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtc_error.rs new file mode 100644 index 0000000..c5e5c0a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtc_error.rs @@ -0,0 +1,185 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::{ + error::Error, + fmt::{Display, Formatter}, +}; + +// cxx doesn't support custom Exception type, so we serialize RtcError inside the cxx::Exception +// "what" string + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + #[derive(Debug)] + #[repr(i32)] + pub enum RtcErrorType { + None, + UnsupportedOperation, + UnsupportedParameter, + InvalidParameter, + InvalidRange, + SyntaxError, + InvalidState, + InvalidModification, + NetworkError, + ResourceExhausted, + InternalError, + OperationErrorWithData, + } + + #[derive(Debug)] + #[repr(i32)] + pub enum RtcErrorDetailType { + None, + DataChannelFailure, + DtlsFailure, + FingerprintFailure, + SctpFailure, + SdpSyntaxError, + HardwareEncoderNotAvailable, + HardwareEncoderError, + } + + #[derive(Debug)] + pub struct RtcError { + pub error_type: RtcErrorType, + pub message: String, + pub error_detail: RtcErrorDetailType, + // cxx doesn't support the Option trait + pub has_sctp_cause_code: bool, + pub sctp_cause_code: u16, + } +} + +impl ffi::RtcError { + /// Parse the hex-encoded error string the C++ side stuffs into the + /// `cxx::Exception` "what" message (see `webrtc-sys/src/rtc_error.cpp` + /// `serialize_error`). The format is fixed-width: + /// + /// ```text + /// bytes 0..8 error_type (u32 hex) + /// bytes 8..16 error_detail (u32 hex) + /// bytes 16..18 has_sctp_cause_code (u8 hex, 0 or 1) + /// bytes 18..22 sctp_cause_code (u16 hex) + /// bytes 22.. message (raw, not encoded) + /// ``` + /// + /// Returns `None` if the input is shorter than the fixed header or the + /// header bytes aren't valid hex. Discriminants outside the known + /// variants for `RtcErrorType` / `RtcErrorDetailType` fall back to + /// `None` for the affected field instead of being `transmute`d into + /// the enum (which is instant UB and what nightly's + /// `ptr::copy_nonoverlapping` precondition check was firing on). + pub fn parse(value: &str) -> Option { + if value.len() < 22 { + return None; + } + let error_type = u32::from_str_radix(&value[0..8], 16).ok()?; + let error_detail = u32::from_str_radix(&value[8..16], 16).ok()?; + let has_scp_cause_code = u8::from_str_radix(&value[16..18], 16).ok()?; + let sctp_cause_code = u16::from_str_radix(&value[18..22], 16).ok()?; + let message = String::from(&value[22..]); + + Some(Self { + error_type: rtc_error_type_from_u32(error_type), + error_detail: rtc_error_detail_type_from_u32(error_detail), + sctp_cause_code, + has_sctp_cause_code: has_scp_cause_code == 1, + message, + }) + } + + /// Backwards-compatible wrapper for callers that already trust the input + /// is well-formed. + /// + /// # Safety + /// Marked `unsafe` purely for source-compat with prior callers; the body + /// no longer relies on caller-upheld invariants. + pub unsafe fn from(value: &str) -> Self { + Self::parse(value).expect("malformed serialized RtcError") + } + + pub fn ok(&self) -> bool { + self.error_type == ffi::RtcErrorType::None + } +} + +fn rtc_error_type_from_u32(value: u32) -> ffi::RtcErrorType { + match value { + 0 => ffi::RtcErrorType::None, + 1 => ffi::RtcErrorType::UnsupportedOperation, + 2 => ffi::RtcErrorType::UnsupportedParameter, + 3 => ffi::RtcErrorType::InvalidParameter, + 4 => ffi::RtcErrorType::InvalidRange, + 5 => ffi::RtcErrorType::SyntaxError, + 6 => ffi::RtcErrorType::InvalidState, + 7 => ffi::RtcErrorType::InvalidModification, + 8 => ffi::RtcErrorType::NetworkError, + 9 => ffi::RtcErrorType::ResourceExhausted, + 10 => ffi::RtcErrorType::InternalError, + 11 => ffi::RtcErrorType::OperationErrorWithData, + _ => ffi::RtcErrorType::None, + } +} + +fn rtc_error_detail_type_from_u32(value: u32) -> ffi::RtcErrorDetailType { + match value { + 0 => ffi::RtcErrorDetailType::None, + 1 => ffi::RtcErrorDetailType::DataChannelFailure, + 2 => ffi::RtcErrorDetailType::DtlsFailure, + 3 => ffi::RtcErrorDetailType::FingerprintFailure, + 4 => ffi::RtcErrorDetailType::SctpFailure, + 5 => ffi::RtcErrorDetailType::SdpSyntaxError, + 6 => ffi::RtcErrorDetailType::HardwareEncoderNotAvailable, + 7 => ffi::RtcErrorDetailType::HardwareEncoderError, + _ => ffi::RtcErrorDetailType::None, + } +} + +impl Error for ffi::RtcError {} + +impl Display for ffi::RtcError { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "RtcError occurred {:?}: {}", self.error_type, self.message) + } +} + +#[cfg(test)] +mod tests { + use crate::rtc_error::ffi::{RtcError, RtcErrorDetailType, RtcErrorType}; + + #[cxx::bridge(namespace = "livekit_ffi")] + pub mod ffi { + unsafe extern "C++" { + include!("livekit/rtc_error.h"); + + fn serialize_deserialize() -> String; + } + } + + /// Tests that RtcError can correctly deserialize the hex-encoded + /// error format produced by C++ (see serialize_error in rtc_error.cpp). + #[test] + fn serialize_deserialize() { + let str = ffi::serialize_deserialize(); + let error = unsafe { RtcError::from(&str) }; + + assert_eq!(error.error_type, RtcErrorType::InternalError); + assert_eq!(error.error_detail, RtcErrorDetailType::DataChannelFailure); + assert!(error.has_sctp_cause_code); + assert_eq!(error.sctp_cause_code, 24); + assert_eq!(error.message, "this is not a test, I repeat, this is not a test"); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_parameters.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_parameters.cpp new file mode 100644 index 0000000..c4b982f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_parameters.cpp @@ -0,0 +1,414 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/rtp_parameters.h" + +namespace livekit_ffi { + +webrtc::RtcpFeedback to_native_rtcp_feedback(RtcpFeedback feedback) { + webrtc::RtcpFeedback native{}; + native.type = static_cast(feedback.feedback_type); + if (feedback.has_message_type) + native.message_type = + static_cast(feedback.message_type); + + return native; +} + +webrtc::RtpCodecCapability to_native_rtp_codec_capability( + RtpCodecCapability capability) { + webrtc::RtpCodecCapability native{}; + // native.mime_type(); IGNORED + + native.name = capability.name.c_str(); + native.kind = static_cast(capability.kind); + + if (capability.has_clock_rate) + native.clock_rate = capability.clock_rate; + + if (capability.has_preferred_payload_type) + native.preferred_payload_type = capability.preferred_payload_type; + + if (capability.has_num_channels) + native.num_channels = capability.num_channels; + + for (auto feedback : capability.rtcp_feedback) + native.rtcp_feedback.push_back(to_native_rtcp_feedback(feedback)); + + for (auto pair : capability.parameters) + native.parameters.insert(std::pair(pair.key, pair.value)); + + return native; +} + +webrtc::RtpHeaderExtensionCapability to_native_rtp_header_extension_capability( + RtpHeaderExtensionCapability header) { + webrtc::RtpHeaderExtensionCapability native{}; + native.uri = header.uri.c_str(); + + if (header.has_preferred_id) + native.preferred_id = header.preferred_id; + + native.preferred_encrypt = header.preferred_encrypt; + native.direction = + static_cast(header.direction); + + return native; +} + +webrtc::RtpExtension to_native_rtp_extension(RtpExtension ext) { + webrtc::RtpExtension native{}; + native.uri = ext.uri.c_str(); + native.id = ext.id; + native.encrypt = ext.encrypt; + return native; +} + +webrtc::RtpFecParameters to_rtp_fec_parameters(RtpFecParameters fec) { + webrtc::RtpFecParameters native{}; + + if (fec.has_ssrc) + native.ssrc = fec.ssrc; + + native.mechanism = static_cast(fec.mechanism); + return native; +} + +webrtc::RtpRtxParameters to_rtp_rtx_parameters(RtpRtxParameters rtx) { + webrtc::RtpRtxParameters native{}; + + if (rtx.has_ssrc) + native.ssrc = rtx.ssrc; + return native; +} + +webrtc::RtpEncodingParameters to_native_rtp_encoding_paramters( + RtpEncodingParameters parameters) { + webrtc::RtpEncodingParameters native{}; + native.rid = parameters.rid.c_str(); + + if (parameters.has_ssrc) + native.ssrc = parameters.ssrc; + + native.active = parameters.active; + if (parameters.has_max_framerate) + native.max_framerate = parameters.max_framerate; + + native.adaptive_ptime = parameters.adaptive_ptime; + if (parameters.has_max_bitrate_bps) + native.max_bitrate_bps = parameters.max_bitrate_bps; + + if (parameters.has_min_bitrate_bps) + native.min_bitrate_bps = parameters.min_bitrate_bps; + + native.bitrate_priority = parameters.bitrate_priority; + native.network_priority = + static_cast(parameters.network_priority); + + if (parameters.has_scalability_mode) + native.scalability_mode = parameters.scalability_mode.c_str(); + + if (parameters.has_num_temporal_layers) + native.num_temporal_layers = parameters.num_temporal_layers; + + if (parameters.has_scale_resolution_down_by) + native.scale_resolution_down_by = parameters.scale_resolution_down_by; + return native; +} + +webrtc::RtpCodecParameters to_native_rtp_codec_parameters( + RtpCodecParameters params) { + webrtc::RtpCodecParameters native{}; + native.name = params.name.c_str(); + native.kind = static_cast(params.kind); + native.payload_type = params.payload_type; + + for (auto pair : params.parameters) + native.parameters.insert(std::pair(pair.key, pair.value)); + + for (auto feedback : params.rtcp_feedback) + native.rtcp_feedback.push_back(to_native_rtcp_feedback(feedback)); + + if (params.has_num_channels) + native.num_channels = params.num_channels; + + if (params.has_clock_rate) + native.clock_rate = params.clock_rate; + + return native; +} + +webrtc::RtpCapabilities to_rtp_capabilities(RtpCapabilities capabilities) { + webrtc::RtpCapabilities native{}; + for (auto codec : capabilities.codecs) + native.codecs.push_back(to_native_rtp_codec_capability(codec)); + + for (auto header : capabilities.header_extensions) + native.header_extensions.push_back( + to_native_rtp_header_extension_capability(header)); + + for (auto fec : capabilities.fec) + native.fec.push_back(static_cast(fec)); + + return native; +} + +webrtc::RtcpParameters to_native_rtcp_paramaters(RtcpParameters params) { + webrtc::RtcpParameters native{}; + if (params.has_ssrc) + native.ssrc = params.ssrc; + + native.mux = params.mux; + native.cname = params.cname.c_str(); + native.reduced_size = params.reduced_size; + return native; +} + +webrtc::RtpParameters to_native_rtp_parameters(RtpParameters params) { + webrtc::RtpParameters native{}; + native.transaction_id = params.transaction_id.c_str(); + native.mid = params.mid.c_str(); + + for (auto codec : params.codecs) + native.codecs.push_back(to_native_rtp_codec_parameters(codec)); + + for (auto header : params.header_extensions) + native.header_extensions.push_back(to_native_rtp_extension(header)); + + for (auto encoding : params.encodings) + native.encodings.push_back(to_native_rtp_encoding_paramters(encoding)); + + native.rtcp = to_native_rtcp_paramaters(params.rtcp); + + if (params.has_degradation_preference) + native.degradation_preference = static_cast( + params.degradation_preference); + + return native; +} + +RtcpFeedback to_rust_rtcp_feedback(webrtc::RtcpFeedback feedback) { + RtcpFeedback rust{}; + rust.feedback_type = static_cast(feedback.type); + + if (feedback.message_type.has_value()) { + rust.has_message_type = true; + rust.message_type = + static_cast(feedback.message_type.value()); + } + + return rust; +} + +RtpCodecCapability to_rust_rtp_codec_capability( + webrtc::RtpCodecCapability capability) { + RtpCodecCapability rust{}; + rust.mime_type = capability.mime_type(); + rust.name = capability.name; + rust.kind = static_cast(capability.kind); + + if (capability.clock_rate.has_value()) { + rust.has_clock_rate = true; + rust.clock_rate = capability.clock_rate.value(); + } + + if (capability.preferred_payload_type.has_value()) { + rust.has_preferred_payload_type = true; + rust.preferred_payload_type = capability.preferred_payload_type.value(); + } + + if (capability.num_channels) { + rust.has_num_channels = true; + rust.num_channels = capability.num_channels.value(); + } + + for (auto feedback : capability.rtcp_feedback) + rust.rtcp_feedback.push_back(to_rust_rtcp_feedback(feedback)); + + for (auto param : capability.parameters) + rust.parameters.push_back(StringKeyValue{param.first, param.second}); + + return rust; +} + +RtpHeaderExtensionCapability to_rust_rtp_header_extension_capability( + webrtc::RtpHeaderExtensionCapability header) { + RtpHeaderExtensionCapability rust{}; + rust.uri = header.uri; + if (header.preferred_id.has_value()) { + rust.has_preferred_id = true; + rust.preferred_id = header.preferred_id.value(); + } + + rust.preferred_encrypt = header.preferred_encrypt; + rust.direction = static_cast(header.direction); + return rust; +} + +RtpExtension to_rust_rtp_extension(webrtc::RtpExtension ext) { + RtpExtension rust{}; + rust.uri = ext.uri; + rust.id = ext.id; + rust.encrypt = ext.encrypt; + return rust; +} + +RtpFecParameters to_rust_rtp_fec_parameters(webrtc::RtpFecParameters fec) { + RtpFecParameters rust{}; + if (fec.ssrc.has_value()) { + rust.has_ssrc = true; + rust.ssrc = fec.ssrc.value(); + } + + rust.mechanism = static_cast(rust.mechanism); + return rust; +} + +RtpRtxParameters to_rust_rtp_rtx_parameters(webrtc::RtpRtxParameters param) { + RtpRtxParameters rust{}; + if (param.ssrc.has_value()) { + rust.has_ssrc = param.ssrc.has_value(); + rust.ssrc = param.ssrc.value(); + } + return rust; +} + +RtpEncodingParameters to_rust_rtp_encoding_parameters( + webrtc::RtpEncodingParameters params) { + RtpEncodingParameters rust{}; + if (params.ssrc.has_value()) { + rust.has_ssrc = params.ssrc.has_value(); + rust.ssrc = params.ssrc.value(); + } + + rust.bitrate_priority = params.bitrate_priority; + rust.network_priority = static_cast(params.network_priority); + if (params.max_bitrate_bps.has_value()) { + rust.has_max_bitrate_bps = true; + rust.max_bitrate_bps = params.max_bitrate_bps.value(); + } + + if (params.min_bitrate_bps.has_value()) { + rust.has_min_bitrate_bps = true; + rust.min_bitrate_bps = params.min_bitrate_bps.value(); + } + + if (params.max_framerate.has_value()) { + rust.has_max_framerate = true; + rust.max_framerate = params.max_framerate.value(); + } + + if (params.num_temporal_layers.has_value()) { + rust.has_num_temporal_layers = true; + rust.num_temporal_layers = params.num_temporal_layers.value(); + } + + if (params.scale_resolution_down_by.has_value()) { + rust.has_scale_resolution_down_by = true; + rust.scale_resolution_down_by = params.scale_resolution_down_by.value(); + } + + if (params.scalability_mode.has_value()) { + rust.has_scalability_mode = true; + rust.scalability_mode = params.scalability_mode.value(); + } + + rust.active = params.active; + rust.rid = params.rid; + rust.adaptive_ptime = params.adaptive_ptime; + return rust; +} + +RtpCodecParameters to_rust_rtp_codec_parameters( + webrtc::RtpCodecParameters params) { + RtpCodecParameters rust{}; + rust.mime_type = params.mime_type(); + rust.name = params.name; + rust.kind = static_cast(params.kind); + rust.payload_type = params.payload_type; + if (params.clock_rate.has_value()) { + rust.has_clock_rate = true; + rust.clock_rate = params.clock_rate.value(); + } + + if (params.num_channels.has_value()) { + rust.has_num_channels = true; + rust.num_channels = params.num_channels.value(); + } + + for (auto feedback : params.rtcp_feedback) + rust.rtcp_feedback.push_back(to_rust_rtcp_feedback(feedback)); + + for (auto pair : params.parameters) + rust.parameters.push_back(StringKeyValue{pair.first, pair.second}); + + return rust; +} + +RtpCapabilities to_rust_rtp_capabilities(webrtc::RtpCapabilities capabilities) { + RtpCapabilities rust{}; + for (auto codec : capabilities.codecs) + rust.codecs.push_back(to_rust_rtp_codec_capability(codec)); + + for (auto header : capabilities.header_extensions) + rust.header_extensions.push_back( + to_rust_rtp_header_extension_capability(header)); + + for (auto fec : capabilities.fec) + rust.fec.push_back(static_cast(fec)); + + return rust; +} + +RtcpParameters to_rust_rtcp_parameters(webrtc::RtcpParameters params) { + RtcpParameters rust{}; + if (params.ssrc.has_value()) { + rust.has_ssrc = true; + rust.ssrc = params.ssrc.value(); + } + + rust.cname = params.cname; + rust.reduced_size = params.reduced_size; + rust.mux = params.mux; + return rust; +} + +RtpParameters to_rust_rtp_parameters(webrtc::RtpParameters params) { + RtpParameters rust{}; + rust.transaction_id = params.transaction_id; + rust.mid = params.mid; + + for (auto codec : params.codecs) + rust.codecs.push_back(to_rust_rtp_codec_parameters(codec)); + + for (auto header : params.header_extensions) + rust.header_extensions.push_back(to_rust_rtp_extension(header)); + + for (auto encoding : params.encodings) + rust.encodings.push_back(to_rust_rtp_encoding_parameters(encoding)); + + rust.rtcp = to_rust_rtcp_parameters(params.rtcp); + + if (params.degradation_preference.has_value()) { + rust.has_degradation_preference = true; + rust.degradation_preference = static_cast( + params.degradation_preference.value()); + } + + return rust; +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_parameters.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_parameters.rs new file mode 100644 index 0000000..2c6ab05 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_parameters.rs @@ -0,0 +1,198 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pub const DEFAULT_BITRATE_PRIORITY: f64 = 1.0; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + + // Used to replace std::map + #[derive(Debug)] + pub struct StringKeyValue { + pub key: String, + pub value: String, + } + + #[derive(Debug)] + #[repr(i32)] + pub enum FecMechanism { + Red, + RedAndUlpfec, + FlexFec, + } + + #[derive(Debug)] + #[repr(i32)] + pub enum RtcpFeedbackType { + Ccm, + Lntf, + Nack, + Remb, + TransportCC, + } + + #[derive(Debug)] + #[repr(i32)] + pub enum RtcpFeedbackMessageType { + GenericNack, + Pli, + Fir, + } + + #[derive(Debug)] + #[repr(i32)] + pub enum DegradationPreference { + Disabled, + MaintainFramerate, + MaintainResolution, + Balanced, + } + + #[derive(Debug)] + pub struct RtcpFeedback { + pub feedback_type: RtcpFeedbackType, + pub has_message_type: bool, + pub message_type: RtcpFeedbackMessageType, + } + + #[derive(Debug)] + pub struct RtpCodecCapability { + pub mime_type: String, // filled with mime_type fnc + pub name: String, + pub kind: MediaType, + pub has_clock_rate: bool, + pub clock_rate: i32, + pub has_preferred_payload_type: bool, + pub preferred_payload_type: i32, + pub has_num_channels: bool, + pub num_channels: i32, + pub rtcp_feedback: Vec, + pub parameters: Vec, + } + + #[derive(Debug)] + pub struct RtpHeaderExtensionCapability { + pub uri: String, + pub has_preferred_id: bool, + pub preferred_id: i32, + pub preferred_encrypt: bool, + pub direction: RtpTransceiverDirection, + } + + #[derive(Debug)] + #[repr(i32)] + pub enum RtpExtensionFilter { + DiscardEncryptedExtension, + PreferEncryptedExtension, + RequireEncryptedExtension, + } + + #[derive(Debug)] + pub struct RtpExtension { + // TODO(theomonnom): export available URI inside api/rtp_parameters.h + pub uri: String, + pub id: i32, + pub encrypt: bool, + } + + #[derive(Debug)] + pub struct RtpFecParameters { + pub has_ssrc: bool, + pub ssrc: u32, + pub mechanism: FecMechanism, + } + + #[derive(Debug)] + pub struct RtpRtxParameters { + pub has_ssrc: bool, + pub ssrc: u32, + } + + #[derive(Debug)] + pub struct RtpEncodingParameters { + pub has_ssrc: bool, + pub ssrc: u32, + pub bitrate_priority: f64, + pub network_priority: Priority, // Todo link type + pub has_max_bitrate_bps: bool, + pub max_bitrate_bps: i32, + pub has_min_bitrate_bps: bool, + pub min_bitrate_bps: i32, + pub has_max_framerate: bool, + pub max_framerate: f64, + pub has_num_temporal_layers: bool, + pub num_temporal_layers: i32, + pub has_scale_resolution_down_by: bool, + pub scale_resolution_down_by: f64, + pub has_scalability_mode: bool, + pub scalability_mode: String, + pub active: bool, + pub rid: String, + pub adaptive_ptime: bool, + } + + #[derive(Debug)] + pub struct RtpCodecParameters { + pub mime_type: String, // filled with mime_type fnc + pub name: String, + pub kind: MediaType, + pub payload_type: i32, + pub has_clock_rate: bool, + pub clock_rate: i32, + pub has_num_channels: bool, + pub num_channels: i32, + pub has_max_ptime: bool, + pub max_ptime: i32, + pub has_ptime: bool, + pub ptime: i32, + pub rtcp_feedback: Vec, + pub parameters: Vec, + } + + #[derive(Debug)] + pub struct RtpCapabilities { + pub codecs: Vec, + pub header_extensions: Vec, + pub fec: Vec, + } + + #[derive(Debug)] + pub struct RtcpParameters { + pub has_ssrc: bool, + pub ssrc: u32, + pub cname: String, + pub reduced_size: bool, + pub mux: bool, + } + + #[derive(Debug)] + pub struct RtpParameters { + pub transaction_id: String, + pub mid: String, + pub codecs: Vec, + pub header_extensions: Vec, + pub encodings: Vec, + pub rtcp: RtcpParameters, + pub has_degradation_preference: bool, + pub degradation_preference: DegradationPreference, + } + + extern "C++" { + include!("livekit/webrtc.h"); + + type Priority = crate::webrtc::ffi::Priority; + type MediaType = crate::webrtc::ffi::MediaType; + type RtpTransceiverDirection = crate::webrtc::ffi::RtpTransceiverDirection; + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_receiver.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_receiver.cpp new file mode 100644 index 0000000..0e25897 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_receiver.cpp @@ -0,0 +1,81 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/rtp_receiver.h" +#include "livekit/jsep.h" + +#include + +#include "absl/types/optional.h" +#include "api/peer_connection_interface.h" +#include "api/scoped_refptr.h" + +namespace livekit_ffi { + +RtpReceiver::RtpReceiver( + std::shared_ptr rtc_runtime, + webrtc::scoped_refptr receiver, + webrtc::scoped_refptr peer_connection) + : rtc_runtime_(rtc_runtime), + receiver_(std::move(receiver)), + peer_connection_(std::move(peer_connection)) {} + +std::shared_ptr RtpReceiver::track() const { + return rtc_runtime_->get_or_create_media_stream_track(receiver_->track()); +} + +rust::Vec RtpReceiver::stream_ids() const { + rust::Vec rust; + for (auto id : receiver_->stream_ids()) + rust.push_back(id); + return rust; +} + +void RtpReceiver::get_stats( + rust::Box ctx, + rust::Fn, rust::String)> on_stats) const { + auto observer = + webrtc::make_ref_counted>(std::move(ctx), on_stats); + peer_connection_->GetStats(receiver_, observer); +} + +rust::Vec RtpReceiver::streams() const { + rust::Vec rust; + for (auto stream : receiver_->streams()) + rust.push_back( + MediaStreamPtr{std::make_shared(rtc_runtime_, stream)}); + return rust; +} + +MediaType RtpReceiver::media_type() const { + return static_cast(receiver_->media_type()); +} + +rust::String RtpReceiver::id() const { + return receiver_->id(); +} + +RtpParameters RtpReceiver::get_parameters() const { + return to_rust_rtp_parameters(receiver_->GetParameters()); +} + +void RtpReceiver::set_jitter_buffer_minimum_delay(bool is_some, + double delay_seconds) const { + receiver_->SetJitterBufferMinimumDelay( + is_some ? absl::make_optional(delay_seconds) : absl::nullopt); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_receiver.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_receiver.rs new file mode 100644 index 0000000..7285f83 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_receiver.rs @@ -0,0 +1,63 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::any::Any; + +use crate::impl_thread_safety; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + + extern "C++" { + include!("livekit/webrtc.h"); + include!("livekit/rtp_parameters.h"); + include!("livekit/helper.h"); + include!("livekit/media_stream.h"); + + type MediaType = crate::webrtc::ffi::MediaType; + type RtpParameters = crate::rtp_parameters::ffi::RtpParameters; + type MediaStreamPtr = crate::helper::ffi::MediaStreamPtr; + type MediaStreamTrack = crate::media_stream::ffi::MediaStreamTrack; + type MediaStream = crate::media_stream::ffi::MediaStream; + } + + unsafe extern "C++" { + include!("livekit/rtp_receiver.h"); + + type RtpReceiver; + + fn track(self: &RtpReceiver) -> SharedPtr; + fn get_stats( + self: &RtpReceiver, + ctx: Box, + on_stats: fn(ctx: Box, json: String), + ); + fn stream_ids(self: &RtpReceiver) -> Vec; + fn streams(self: &RtpReceiver) -> Vec; + fn media_type(self: &RtpReceiver) -> MediaType; + fn id(self: &RtpReceiver) -> String; + fn get_parameters(self: &RtpReceiver) -> RtpParameters; + fn set_jitter_buffer_minimum_delay(self: &RtpReceiver, is_some: bool, delay_seconds: f64); + + fn _shared_rtp_receiver() -> SharedPtr; + } + + extern "Rust" { + type ReceiverContext; + } +} + +pub struct ReceiverContext(pub Box); + +impl_thread_safety!(ffi::RtpReceiver, Send + Sync); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_sender.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_sender.cpp new file mode 100644 index 0000000..54d3230 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_sender.cpp @@ -0,0 +1,93 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/rtp_sender.h" +#include "livekit/jsep.h" + +#include "rust/cxx.h" +#include "webrtc-sys/src/rtp_sender.rs.h" + +namespace livekit_ffi { + + + +RtpSender::RtpSender( + std::shared_ptr rtc_runtime, + webrtc::scoped_refptr sender, + webrtc::scoped_refptr peer_connection) + : rtc_runtime_(rtc_runtime), + sender_(std::move(sender)), + peer_connection_(std::move(peer_connection)) {} + +bool RtpSender::set_track(std::shared_ptr track) const { + return sender_->SetTrack(track->rtc_track().get()); +} + +std::shared_ptr RtpSender::track() const { + return rtc_runtime_->get_or_create_media_stream_track(sender_->track()); +} + +uint32_t RtpSender::ssrc() const { + return sender_->ssrc(); +} + +void RtpSender::get_stats( + rust::Box ctx, + rust::Fn, rust::String)> on_stats) const { + auto observer = + webrtc::make_ref_counted>(std::move(ctx), on_stats); + peer_connection_->GetStats(sender_, observer); +} + +MediaType RtpSender::media_type() const { + return static_cast(sender_->media_type()); +} + +rust::String RtpSender::id() const { + return sender_->id(); +} + +rust::Vec RtpSender::stream_ids() const { + rust::Vec vec; + for (auto str : sender_->stream_ids()) + vec.push_back(str); + + return vec; +} + +void RtpSender::set_streams(const rust::Vec& stream_ids) const { + std::vector std_stream_ids(stream_ids.begin(), stream_ids.end()); + sender_->SetStreams(std_stream_ids); +} + +rust::Vec RtpSender::init_send_encodings() const { + rust::Vec encodings; + for (auto encoding : sender_->init_send_encodings()) + encodings.push_back(to_rust_rtp_encoding_parameters(encoding)); + return encodings; +} + +RtpParameters RtpSender::get_parameters() const { + return to_rust_rtp_parameters(sender_->GetParameters()); +} + +void RtpSender::set_parameters(RtpParameters params) const { + auto error = sender_->SetParameters(to_native_rtp_parameters(params)); + if (!error.ok()) + throw std::runtime_error(serialize_error(to_error(error))); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_sender.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_sender.rs new file mode 100644 index 0000000..f47b0fa --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_sender.rs @@ -0,0 +1,65 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::any::Any; + +use crate::impl_thread_safety; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + + extern "C++" { + include!("livekit/webrtc.h"); + include!("livekit/rtp_parameters.h"); + include!("livekit/media_stream.h"); + + type MediaType = crate::webrtc::ffi::MediaType; + type RtpEncodingParameters = crate::rtp_parameters::ffi::RtpEncodingParameters; + type RtpParameters = crate::rtp_parameters::ffi::RtpParameters; + type MediaStreamTrack = crate::media_stream::ffi::MediaStreamTrack; + } + + unsafe extern "C++" { + include!("livekit/rtp_sender.h"); + + type RtpSender; + + fn set_track(self: &RtpSender, track: SharedPtr) -> bool; + fn track(self: &RtpSender) -> SharedPtr; + fn get_stats( + self: &RtpSender, + ctx: Box, + on_stats: fn(ctx: Box, json: String), + ); + fn ssrc(self: &RtpSender) -> u32; + fn media_type(self: &RtpSender) -> MediaType; + fn id(self: &RtpSender) -> String; + fn stream_ids(self: &RtpSender) -> Vec; + fn set_streams(self: &RtpSender, stream_ids: &Vec); + fn init_send_encodings(self: &RtpSender) -> Vec; + fn get_parameters(self: &RtpSender) -> RtpParameters; + fn set_parameters(self: &RtpSender, parameters: RtpParameters) -> Result<()>; + + fn _shared_rtp_sender() -> SharedPtr; + } + + extern "Rust" { + type SenderContext; + } +} + +#[repr(transparent)] +pub struct SenderContext(pub Box); + +impl_thread_safety!(ffi::RtpSender, Send + Sync); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_transceiver.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_transceiver.cpp new file mode 100644 index 0000000..3e3116b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_transceiver.cpp @@ -0,0 +1,154 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/rtp_transceiver.h" + +#include "api/peer_connection_interface.h" +#include "api/scoped_refptr.h" + +namespace livekit_ffi { + +webrtc::RtpTransceiverInit to_native_rtp_transceiver_init( + RtpTransceiverInit init) { + { + webrtc::RtpTransceiverInit native{}; + native.direction = + static_cast(init.direction); + native.stream_ids = std::vector(init.stream_ids.begin(), + init.stream_ids.end()); + for (auto encoding : init.send_encodings) + native.send_encodings.push_back( + to_native_rtp_encoding_paramters(encoding)); + return native; + } +} + +RtpTransceiver::RtpTransceiver( + std::shared_ptr rtc_runtime, + webrtc::scoped_refptr transceiver, + webrtc::scoped_refptr peer_connection) + : rtc_runtime_(rtc_runtime), + transceiver_(std::move(transceiver)), + peer_connection_(std::move(peer_connection)) {} + +MediaType RtpTransceiver::media_type() const { + return static_cast(transceiver_->media_type()); +} + +rust::String RtpTransceiver::mid() const { + // The error/Result is converted into an Option in Rust (Wait for Option + // suport in cxx.rs) (value throws an error if there's no value) + return transceiver_->mid().value(); +} + +std::shared_ptr RtpTransceiver::sender() const { + return std::make_shared(rtc_runtime_, transceiver_->sender(), + peer_connection_); +} + +std::shared_ptr RtpTransceiver::receiver() const { + return std::make_shared(rtc_runtime_, transceiver_->receiver(), + peer_connection_); +} + +bool RtpTransceiver::stopped() const { + return transceiver_->stopped(); +} + +bool RtpTransceiver::stopping() const { + return transceiver_->stopping(); +} + +RtpTransceiverDirection RtpTransceiver::direction() const { + return static_cast(transceiver_->direction()); +} + +void RtpTransceiver::set_direction(RtpTransceiverDirection direction) const { + auto error = transceiver_->SetDirectionWithError( + static_cast(direction)); + + if (!error.ok()) { + throw std::runtime_error(serialize_error(to_error(error))); + } +} + +RtpTransceiverDirection RtpTransceiver::current_direction() const { + return static_cast( + transceiver_->current_direction().value()); +} + +RtpTransceiverDirection RtpTransceiver::fired_direction() const { + return static_cast( + transceiver_->fired_direction().value()); +} + +void RtpTransceiver::stop_standard() const { + auto error = transceiver_->StopStandard(); + if (!error.ok()) + throw std::runtime_error(serialize_error(to_error(error))); +} + +void RtpTransceiver::set_codec_preferences( + rust::Vec codecs) const { + std::vector std_codecs; + + for (auto codec : codecs) + std_codecs.push_back(to_native_rtp_codec_capability(codec)); + + auto error = transceiver_->SetCodecPreferences(std_codecs); + if (!error.ok()) + throw std::runtime_error(serialize_error(to_error(error))); +} + +rust::Vec RtpTransceiver::codec_preferences() const { + rust::Vec rust; + for (auto codec : transceiver_->codec_preferences()) + rust.push_back(to_rust_rtp_codec_capability(codec)); + + return rust; +} + +rust::Vec +RtpTransceiver::header_extensions_to_negotiate() const { + rust::Vec rust; + for (auto header : transceiver_->GetHeaderExtensionsToNegotiate()) + rust.push_back(to_rust_rtp_header_extension_capability(header)); + + return rust; +} + +rust::Vec +RtpTransceiver::negotiated_header_extensions() const { + rust::Vec rust; + for (auto header : transceiver_->GetNegotiatedHeaderExtensions()) + rust.push_back(to_rust_rtp_header_extension_capability(header)); + + return rust; +} + +void RtpTransceiver::set_header_extensions_to_negotiate( + rust::Vec header_extensions_to_offer) const { + std::vector headers; + + for (auto header : header_extensions_to_offer) + headers.push_back(to_native_rtp_header_extension_capability(header)); + + auto error = transceiver_->SetHeaderExtensionsToNegotiate(headers); + if (!error.ok()) + throw std::runtime_error(serialize_error(to_error(error))); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_transceiver.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_transceiver.rs new file mode 100644 index 0000000..6f0642d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/rtp_transceiver.rs @@ -0,0 +1,79 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::impl_thread_safety; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + + #[derive(Debug)] + pub struct RtpTransceiverInit { + pub direction: RtpTransceiverDirection, + pub stream_ids: Vec, + pub send_encodings: Vec, + } + + extern "C++" { + include!("livekit/webrtc.h"); + include!("livekit/rtp_parameters.h"); + include!("livekit/rtp_sender.h"); + include!("livekit/rtp_receiver.h"); + + type MediaType = crate::webrtc::ffi::MediaType; + type RtpTransceiverDirection = crate::webrtc::ffi::RtpTransceiverDirection; + type RtpEncodingParameters = crate::rtp_parameters::ffi::RtpEncodingParameters; + type RtpCodecCapability = crate::rtp_parameters::ffi::RtpCodecCapability; + type RtpHeaderExtensionCapability = + crate::rtp_parameters::ffi::RtpHeaderExtensionCapability; + type RtpSender = crate::rtp_sender::ffi::RtpSender; + type RtpReceiver = crate::rtp_receiver::ffi::RtpReceiver; + type RtcError = crate::rtc_error::ffi::RtcError; + } + + unsafe extern "C++" { + include!("livekit/rtp_transceiver.h"); + + type RtpTransceiver; + + fn media_type(self: &RtpTransceiver) -> MediaType; + fn mid(self: &RtpTransceiver) -> Result; + fn sender(self: &RtpTransceiver) -> SharedPtr; + fn receiver(self: &RtpTransceiver) -> SharedPtr; + fn stopped(self: &RtpTransceiver) -> bool; + fn stopping(self: &RtpTransceiver) -> bool; + fn direction(self: &RtpTransceiver) -> RtpTransceiverDirection; + fn set_direction(self: &RtpTransceiver, direction: RtpTransceiverDirection) -> Result<()>; + fn current_direction(self: &RtpTransceiver) -> Result; + fn fired_direction(self: &RtpTransceiver) -> Result; + fn stop_standard(self: &RtpTransceiver) -> Result<()>; + fn set_codec_preferences( + self: &RtpTransceiver, + codecs: Vec, + ) -> Result<()>; + fn codec_preferences(self: &RtpTransceiver) -> Vec; + fn header_extensions_to_negotiate( + self: &RtpTransceiver, + ) -> Vec; + fn negotiated_header_extensions(self: &RtpTransceiver) + -> Vec; + fn set_header_extensions_to_negotiate( + self: &RtpTransceiver, + headers: Vec, + ) -> Result<()>; + + fn _shared_rtp_transceiver() -> SharedPtr; + } +} + +impl_thread_safety!(ffi::RtpTransceiver, Send + Sync); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/synthetic_audio_device.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/synthetic_audio_device.cpp new file mode 100644 index 0000000..8074b9d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/synthetic_audio_device.cpp @@ -0,0 +1,335 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/synthetic_audio_device.h" + +const int kSampleRate = 48000; +const int kChannels = 2; +const int kBytesPerSample = kChannels * sizeof(int16_t); +const int kSamplesPer10Ms = kSampleRate / 100; + +namespace livekit_ffi { + +SyntheticAudioDevice::SyntheticAudioDevice(const webrtc::Environment& env) + : env_(env), + data_(kSamplesPer10Ms * kChannels) {} + +SyntheticAudioDevice::~SyntheticAudioDevice() { + Terminate(); +} + +int32_t SyntheticAudioDevice::ActiveAudioLayer(AudioLayer* audioLayer) const { + *audioLayer = AudioLayer::kDummyAudio; + return 0; +} + +int32_t SyntheticAudioDevice::RegisterAudioCallback(webrtc::AudioTransport* transport) { + webrtc::MutexLock lock(&mutex_); + audio_transport_ = transport; + return 0; +} + +int32_t SyntheticAudioDevice::Init() { + webrtc::MutexLock lock(&mutex_); + if (initialized_) + return 0; + + audio_queue_ = + env_.task_queue_factory().CreateTaskQueue( + "SyntheticAudioDevice", webrtc::TaskQueueFactory::Priority::NORMAL); + + audio_task_ = + webrtc::RepeatingTaskHandle::Start(audio_queue_.get(), [this]() { + webrtc::MutexLock lock(&mutex_); + + if (playing_) { + int64_t elapsed_time_ms = -1; + int64_t ntp_time_ms = -1; + size_t n_samples_out = 0; + void* data = data_.data(); + + // Request the AudioData, otherwise WebRTC will ignore the packets. + // 10ms of audio data. + audio_transport_->NeedMorePlayData( + kSamplesPer10Ms, kBytesPerSample, kChannels, kSampleRate, data, + n_samples_out, &elapsed_time_ms, &ntp_time_ms); + } + + return webrtc::TimeDelta::Millis(10); + }); + + initialized_ = true; + return 0; +} + +int32_t SyntheticAudioDevice::Terminate() { + { + webrtc::MutexLock lock(&mutex_); + if (!initialized_) + return 0; + + initialized_ = false; + } + audio_queue_ = nullptr; + return 0; +} + +bool SyntheticAudioDevice::Initialized() const { + webrtc::MutexLock lock(&mutex_); + return initialized_; +} + +int16_t SyntheticAudioDevice::PlayoutDevices() { + return 0; +} + +int16_t SyntheticAudioDevice::RecordingDevices() { + return 0; +} + +int32_t SyntheticAudioDevice::PlayoutDeviceName(uint16_t index, + char name[webrtc::kAdmMaxDeviceNameSize], + char guid[webrtc::kAdmMaxGuidSize]) { + return 0; +} + +int32_t SyntheticAudioDevice::RecordingDeviceName( + uint16_t index, + char name[webrtc::kAdmMaxDeviceNameSize], + char guid[webrtc::kAdmMaxGuidSize]) { + return 0; +} + +int32_t SyntheticAudioDevice::SetPlayoutDevice(uint16_t index) { + return 0; +} + +int32_t SyntheticAudioDevice::SetPlayoutDevice(WindowsDeviceType device) { + return 0; +} + +int32_t SyntheticAudioDevice::SetRecordingDevice(uint16_t index) { + return 0; +} + +int32_t SyntheticAudioDevice::SetRecordingDevice(WindowsDeviceType device) { + return 0; +} + +int32_t SyntheticAudioDevice::PlayoutIsAvailable(bool* available) { + return 0; +} + +int32_t SyntheticAudioDevice::InitPlayout() { + return 0; +} + +bool SyntheticAudioDevice::PlayoutIsInitialized() const { + return false; +} + +int32_t SyntheticAudioDevice::RecordingIsAvailable(bool* available) { + return 0; +} + +int32_t SyntheticAudioDevice::InitRecording() { + return 0; +} + +bool SyntheticAudioDevice::RecordingIsInitialized() const { + return false; +} + +int32_t SyntheticAudioDevice::StartPlayout() { + webrtc::MutexLock lock(&mutex_); + playing_ = true; + return 0; +} + +int32_t SyntheticAudioDevice::StopPlayout() { + webrtc::MutexLock lock(&mutex_); + playing_ = false; + return 0; +} + +bool SyntheticAudioDevice::Playing() const { + webrtc::MutexLock lock(&mutex_); + return playing_; +} + +int32_t SyntheticAudioDevice::StartRecording() { + return 0; +} + +int32_t SyntheticAudioDevice::StopRecording() { + return 0; +} + +bool SyntheticAudioDevice::Recording() const { + return false; +} + +int32_t SyntheticAudioDevice::InitSpeaker() { + return 0; +} + +bool SyntheticAudioDevice::SpeakerIsInitialized() const { + return false; +} + +int32_t SyntheticAudioDevice::InitMicrophone() { + return 0; +} + +bool SyntheticAudioDevice::MicrophoneIsInitialized() const { + return false; +} + +int32_t SyntheticAudioDevice::SpeakerVolumeIsAvailable(bool* available) { + return 0; +} + +int32_t SyntheticAudioDevice::SetSpeakerVolume(uint32_t volume) { + return 0; +} + +int32_t SyntheticAudioDevice::SpeakerVolume(uint32_t* volume) const { + return 0; +} + +int32_t SyntheticAudioDevice::MaxSpeakerVolume(uint32_t* maxVolume) const { + return 0; +} + +int32_t SyntheticAudioDevice::MinSpeakerVolume(uint32_t* minVolume) const { + return 0; +} + +int32_t SyntheticAudioDevice::MicrophoneVolumeIsAvailable(bool* available) { + return 0; +} + +int32_t SyntheticAudioDevice::SetMicrophoneVolume(uint32_t volume) { + return 0; +} + +int32_t SyntheticAudioDevice::MicrophoneVolume(uint32_t* volume) const { + return 0; +} + +int32_t SyntheticAudioDevice::MaxMicrophoneVolume(uint32_t* maxVolume) const { + return 0; +} + +int32_t SyntheticAudioDevice::MinMicrophoneVolume(uint32_t* minVolume) const { + return 0; +} + +int32_t SyntheticAudioDevice::SpeakerMuteIsAvailable(bool* available) { + return 0; +} + +int32_t SyntheticAudioDevice::SetSpeakerMute(bool enable) { + return 0; +} + +int32_t SyntheticAudioDevice::SpeakerMute(bool* enabled) const { + return 0; +} + +int32_t SyntheticAudioDevice::MicrophoneMuteIsAvailable(bool* available) { + return 0; +} + +int32_t SyntheticAudioDevice::SetMicrophoneMute(bool enable) { + return 0; +} + +int32_t SyntheticAudioDevice::MicrophoneMute(bool* enabled) const { + return 0; +} + +int32_t SyntheticAudioDevice::StereoPlayoutIsAvailable(bool* available) const { + *available = true; + return 0; +} + +int32_t SyntheticAudioDevice::SetStereoPlayout(bool enable) { + return 0; +} + +int32_t SyntheticAudioDevice::StereoPlayout(bool* enabled) const { + return 0; +} + +int32_t SyntheticAudioDevice::StereoRecordingIsAvailable(bool* available) const { + return 0; +} + +int32_t SyntheticAudioDevice::SetStereoRecording(bool enable) { + return 0; +} + +int32_t SyntheticAudioDevice::StereoRecording(bool* enabled) const { + *enabled = true; + return 0; +} + +int32_t SyntheticAudioDevice::PlayoutDelay(uint16_t* delayMS) const { + return 0; +} + +bool SyntheticAudioDevice::BuiltInAECIsAvailable() const { + return false; +} + +bool SyntheticAudioDevice::BuiltInAGCIsAvailable() const { + return false; +} + +bool SyntheticAudioDevice::BuiltInNSIsAvailable() const { + return false; +} + +int32_t SyntheticAudioDevice::EnableBuiltInAEC(bool enable) { + return 0; +} + +int32_t SyntheticAudioDevice::EnableBuiltInAGC(bool enable) { + return 0; +} + +int32_t SyntheticAudioDevice::EnableBuiltInNS(bool enable) { + return 0; +} + +#if defined(WEBRTC_IOS) +int SyntheticAudioDevice::GetPlayoutAudioParameters( + webrtc::AudioParameters* params) const { + return 0; +} + +int SyntheticAudioDevice::GetRecordAudioParameters( + webrtc::AudioParameters* params) const { + return 0; +} +#endif // WEBRTC_IOS + +int32_t SyntheticAudioDevice::SetObserver(webrtc::AudioDeviceObserver* observer) { + return 0; +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/h264_encoder_impl.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/h264_encoder_impl.cpp new file mode 100644 index 0000000..5a3b76e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/h264_encoder_impl.cpp @@ -0,0 +1,315 @@ +#include "h264_encoder_impl.h" + +#include +#include +#include + +#include "absl/strings/match.h" +#include "absl/types/optional.h" +#include "api/video/video_codec_constants.h" +#include "api/video_codecs/scalability_mode.h" +#include "common_video/libyuv/include/webrtc_libyuv.h" +#include "modules/video_coding/include/video_codec_interface.h" +#include "modules/video_coding/include/video_error_codes.h" +#include "modules/video_coding/svc/create_scalability_structure.h" +#include "modules/video_coding/utility/simulcast_rate_allocator.h" +#include "modules/video_coding/utility/simulcast_utility.h" +#include "rtc_base/checks.h" +#include "rtc_base/logging.h" +#include "rtc_base/time_utils.h" +#include "system_wrappers/include/metrics.h" +#include "third_party/libyuv/include/libyuv/convert.h" +#include "third_party/libyuv/include/libyuv/scale.h" + +#define VA_FOURCC_I420 0x30323449 // I420 + +namespace webrtc { + +// Used by histograms. Values of entries should not be changed. +enum H264EncoderImplEvent { + kH264EncoderEventInit = 0, + kH264EncoderEventError = 1, + kH264EncoderEventMax = 16, +}; + +VAAPIH264EncoderWrapper::VAAPIH264EncoderWrapper(const webrtc::Environment& env, + const SdpVideoFormat& format) + : env_(env), + encoder_(new livekit_ffi::VaapiH264EncoderWrapper()), + packetization_mode_( + H264EncoderSettings::Parse(format).packetization_mode), + format_(format) { + std::string hexString = format_.parameters.at("profile-level-id"); + std::optional profile_level_id = + webrtc::ParseH264ProfileLevelId(hexString.c_str()); + if (profile_level_id.has_value()) { + profile_ = profile_level_id->profile; + level_ = profile_level_id->level; + } +} + +VAProfile VAAPIH264EncoderWrapper::GetVAProfile() const { + switch (profile_) { + case H264Profile::kProfileConstrainedBaseline: + case H264Profile::kProfileBaseline: + return VAProfileH264ConstrainedBaseline; + + case H264Profile::kProfileMain: + return VAProfileH264Main; + + case H264Profile::kProfileConstrainedHigh: + case H264Profile::kProfileHigh: + return VAProfileH264High; + } + return VAProfileNone; +} + +VAAPIH264EncoderWrapper::~VAAPIH264EncoderWrapper() { + Release(); +} + +void VAAPIH264EncoderWrapper::ReportInit() { + if (has_reported_init_) + return; + RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.H264EncoderImpl.Event", + kH264EncoderEventInit, kH264EncoderEventMax); + has_reported_init_ = true; +} + +void VAAPIH264EncoderWrapper::ReportError() { + if (has_reported_error_) + return; + RTC_HISTOGRAM_ENUMERATION("WebRTC.Video.H264EncoderImpl.Event", + kH264EncoderEventError, kH264EncoderEventMax); + has_reported_error_ = true; +} + +int32_t VAAPIH264EncoderWrapper::InitEncode( + const VideoCodec* inst, + const VideoEncoder::Settings& settings) { + if (!inst || inst->codecType != kVideoCodecH264) { + ReportError(); + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + if (inst->maxFramerate == 0) { + ReportError(); + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + if (inst->width < 1 || inst->height < 1) { + ReportError(); + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + + int32_t release_ret = Release(); + if (release_ret != WEBRTC_VIDEO_CODEC_OK) { + ReportError(); + return release_ret; + } + + codec_ = *inst; + + // Code expects simulcastStream resolutions to be correct, make sure they are + // filled even when there are no simulcast layers. + if (codec_.numberOfSimulcastStreams == 0) { + codec_.simulcastStream[0].width = codec_.width; + codec_.simulcastStream[0].height = codec_.height; + } + + // Initialize encoded image. Default buffer size: size of unencoded data. + const size_t new_capacity = + CalcBufferSize(VideoType::kI420, codec_.width, codec_.height); + encoded_image_.SetEncodedData(EncodedImageBuffer::Create(new_capacity)); + encoded_image_._encodedWidth = codec_.width; + encoded_image_._encodedHeight = codec_.height; + encoded_image_.set_size(0); + + configuration_.sending = false; + configuration_.frame_dropping_on = codec_.GetFrameDropEnabled(); + configuration_.key_frame_interval = codec_.H264()->keyFrameInterval; + + configuration_.width = codec_.width; + configuration_.height = codec_.height; + + configuration_.max_frame_rate = codec_.maxFramerate; + configuration_.target_bps = codec_.startBitrate * 1000; + configuration_.max_bps = codec_.maxBitrate * 1000; + + if (!encoder_->IsInitialized()) { + // Initialize encoder. + int keyFrameInterval = 60; + if (codec_.maxFramerate > 0) { + keyFrameInterval = codec_.maxFramerate * 5; + } + auto va_profile = GetVAProfile(); + if (va_profile == VAProfileNone) { + RTC_LOG(LS_ERROR) << "Unsupported H264 profile: " + << static_cast(profile_); + ReportError(); + return WEBRTC_VIDEO_CODEC_ERR_PARAMETER; + } + encoder_->Initialize(codec_.width, codec_.height, + codec_.startBitrate * 1000, keyFrameInterval, + keyFrameInterval, 1, codec_.maxFramerate, + va_profile, VA_RC_CBR); + } + + SimulcastRateAllocator init_allocator(env_, codec_); + VideoBitrateAllocation allocation = + init_allocator.Allocate(VideoBitrateAllocationParameters( + DataRate::KilobitsPerSec(codec_.startBitrate), codec_.maxFramerate)); + SetRates(RateControlParameters(allocation, codec_.maxFramerate)); + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t VAAPIH264EncoderWrapper::RegisterEncodeCompleteCallback( + EncodedImageCallback* callback) { + encoded_image_callback_ = callback; + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t VAAPIH264EncoderWrapper::Release() { + if (encoder_->IsInitialized()) { + encoder_->Destroy(); + } + return WEBRTC_VIDEO_CODEC_OK; +} + +int32_t VAAPIH264EncoderWrapper::Encode( + const VideoFrame& input_frame, + const std::vector* frame_types) { + if (!encoder_) { + ReportError(); + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + if (!encoded_image_callback_) { + RTC_LOG(LS_WARNING) + << "InitEncode() has been called, but a callback function " + "has not been set with RegisterEncodeCompleteCallback()"; + ReportError(); + return WEBRTC_VIDEO_CODEC_UNINITIALIZED; + } + + webrtc::scoped_refptr frame_buffer = + input_frame.video_frame_buffer()->ToI420(); + if (!frame_buffer) { + RTC_LOG(LS_ERROR) << "Failed to convert " + << VideoFrameBufferTypeToString( + input_frame.video_frame_buffer()->type()) + << " image to I420. Can't encode frame."; + return WEBRTC_VIDEO_CODEC_ENCODER_FAILURE; + } + RTC_CHECK(frame_buffer->type() == VideoFrameBuffer::Type::kI420 || + frame_buffer->type() == VideoFrameBuffer::Type::kI420A); + + bool is_keyframe_needed = false; + if (configuration_.key_frame_request && configuration_.sending) { + is_keyframe_needed = true; + } + + bool send_key_frame = + is_keyframe_needed || + (frame_types && (*frame_types)[0] == VideoFrameType::kVideoFrameKey); + if (send_key_frame) { + is_keyframe_needed = true; + configuration_.key_frame_request = false; + } + + RTC_DCHECK_EQ(configuration_.width, frame_buffer->width()); + RTC_DCHECK_EQ(configuration_.height, frame_buffer->height()); + + if (!configuration_.sending) { + return WEBRTC_VIDEO_CODEC_NO_OUTPUT; + } + + if (frame_types != nullptr) { + // Skip frame? + if ((*frame_types)[0] == VideoFrameType::kEmptyFrame) { + return WEBRTC_VIDEO_CODEC_NO_OUTPUT; + } + } + + std::vector output; + encoder_->Encode(VA_FOURCC_I420, frame_buffer->DataY(), frame_buffer->DataU(), + frame_buffer->DataV(), send_key_frame, output); + + if (output.empty()) { + RTC_LOG(LS_ERROR) << "Failed to encode frame."; + return WEBRTC_VIDEO_CODEC_ERROR; + } + + encoded_image_.SetEncodedData( + EncodedImageBuffer::Create(output.data(), output.size())); + + h264_bitstream_parser_.ParseBitstream(encoded_image_); + + encoded_image_.qp_ = h264_bitstream_parser_.GetLastSliceQp().value_or(-1); + + encoded_image_._encodedWidth = configuration_.width; + encoded_image_._encodedHeight = configuration_.height; + encoded_image_.SetRtpTimestamp(input_frame.rtp_timestamp()); + encoded_image_.SetColorSpace(input_frame.color_space()); + encoded_image_._frameType = send_key_frame ? VideoFrameType::kVideoFrameKey + : VideoFrameType::kVideoFrameDelta; + CodecSpecificInfo codec_specific; + codec_specific.codecType = kVideoCodecH264; + codec_specific.codecSpecific.H264.packetization_mode = packetization_mode_; + codec_specific.codecSpecific.H264.temporal_idx = kNoTemporalIdx; + codec_specific.codecSpecific.H264.base_layer_sync = false; + codec_specific.codecSpecific.H264.idr_frame = send_key_frame; + encoded_image_callback_->OnEncodedImage(encoded_image_, &codec_specific); + + return WEBRTC_VIDEO_CODEC_OK; +} + +VideoEncoder::EncoderInfo VAAPIH264EncoderWrapper::GetEncoderInfo() const { + EncoderInfo info; + info.supports_native_handle = false; + info.implementation_name = "VAAPI H264 Encoder"; + info.scaling_settings = VideoEncoder::ScalingSettings::kOff; + info.is_hardware_accelerated = true; + info.supports_simulcast = false; + info.preferred_pixel_formats = {VideoFrameBuffer::Type::kI420}; + return info; +} + +void VAAPIH264EncoderWrapper::SetRates( + const RateControlParameters& parameters) { + if (!encoder_) { + RTC_LOG(LS_WARNING) << "SetRates() while uninitialized."; + return; + } + + if (parameters.framerate_fps < 1.0) { + RTC_LOG(LS_WARNING) << "Invalid frame rate: " << parameters.framerate_fps; + return; + } + + if (parameters.bitrate.get_sum_bps() == 0) { + configuration_.SetStreamState(false); + return; + } + + codec_.maxFramerate = static_cast(parameters.framerate_fps); + + configuration_.target_bps = parameters.bitrate.GetSpatialLayerSum(0); + configuration_.max_frame_rate = parameters.framerate_fps; + + if (configuration_.target_bps) { + configuration_.SetStreamState(true); + // Update max_frame_rate/target_bitrate for vaapi encoder. + encoder_->UpdateRates(configuration_.max_frame_rate, + configuration_.target_bps); + } else { + configuration_.SetStreamState(false); + } +} + +void VAAPIH264EncoderWrapper::LayerConfig::SetStreamState(bool send_stream) { + if (send_stream && !sending) { + // Need a key frame if we have not sent this stream before. + key_frame_request = true; + } + sending = send_stream; +} + +} // namespace webrtc diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/h264_encoder_impl.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/h264_encoder_impl.h new file mode 100644 index 0000000..dc26355 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/h264_encoder_impl.h @@ -0,0 +1,83 @@ +#ifndef VAAPI_H264_ENCODER_IMPL_H_ +#define VAAPI_H264_ENCODER_IMPL_H_ + +#include +#include + +#include "absl/container/inlined_vector.h" +#include "api/transport/rtp/dependency_descriptor.h" +#include "api/video/i420_buffer.h" +#include "api/video/video_codec_constants.h" +#include "api/video_codecs/scalability_mode.h" +#include "api/video_codecs/video_encoder.h" +#include "common_video/h264/h264_bitstream_parser.h" +#include "modules/video_coding/codecs/h264/include/h264.h" +#include "modules/video_coding/svc/scalable_video_controller.h" +#include "modules/video_coding/utility/quality_scaler.h" + +#include "vaapi_h264_encoder_wrapper.h" + +namespace webrtc { + +class VAAPIH264EncoderWrapper : public VideoEncoder { + public: + struct LayerConfig { + int simulcast_idx = 0; + int width = -1; + int height = -1; + bool sending = true; + bool key_frame_request = false; + float max_frame_rate = 0; + uint32_t target_bps = 0; + uint32_t max_bps = 0; + bool frame_dropping_on = false; + int key_frame_interval = 0; + int num_temporal_layers = 1; + + void SetStreamState(bool send_stream); + }; + + public: + VAAPIH264EncoderWrapper(const webrtc::Environment& env, + const SdpVideoFormat& format); + ~VAAPIH264EncoderWrapper() override; + + int32_t InitEncode(const VideoCodec* codec_settings, + const Settings& settings) override; + + int32_t RegisterEncodeCompleteCallback( + EncodedImageCallback* callback) override; + + int32_t Release() override; + + int32_t Encode(const VideoFrame& frame, + const std::vector* frame_types) override; + + void SetRates(const RateControlParameters& rc_parameters) override; + + EncoderInfo GetEncoderInfo() const override; + + private: + VAProfile GetVAProfile() const; + + private: + const webrtc::Environment& env_; + EncodedImageCallback* encoded_image_callback_ = nullptr; + std::unique_ptr encoder_; + LayerConfig configuration_; + EncodedImage encoded_image_; + H264PacketizationMode packetization_mode_; + VideoCodec codec_; + void ReportInit(); + void ReportError(); + bool has_reported_init_ = false; + bool has_reported_error_ = false; + webrtc::H264BitstreamParser h264_bitstream_parser_; + const SdpVideoFormat format_; + H264Profile profile_ = H264Profile::kProfileConstrainedBaseline; + H264Level level_ = H264Level::kLevel1_b; +}; + +} // namespace webrtc + +#endif // VAAPI_H264_ENCODER_IMPL_H_ diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_display_drm.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_display_drm.cpp new file mode 100644 index 0000000..be22597 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_display_drm.cpp @@ -0,0 +1,135 @@ +#include "vaapi_display_drm.h" + +#include +#include +#include +#include + +#ifdef IN_LIBVA +#include "va/drm/va_drm.h" +#else +#include +#endif + +#include "rtc_base/logging.h" + +static bool check_h264_encoding_support(VADisplay va_display) { + VAProfile profile_list[] = {VAProfileH264High, VAProfileH264Main, + VAProfileH264ConstrainedBaseline}; + + VAProfile h264_profile = VAProfileH264ConstrainedBaseline; + VAEntrypoint* entrypoints; + int num_entrypoints, slice_entrypoint; + bool support_encode = false; + int selected_entrypoint = -1; + int major_ver, minor_ver; + VAStatus va_status; + uint32_t i; + + if (!va_display) { + return false; + } + + va_status = vaInitialize(va_display, &major_ver, &minor_ver); + + if (major_ver < 0 || minor_ver < 0 || va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaInitialize failed"; + return false; + } + + num_entrypoints = vaMaxNumEntrypoints(va_display); + entrypoints = new VAEntrypoint[num_entrypoints * sizeof(*entrypoints)]; + if (!entrypoints) { + RTC_LOG(LS_ERROR) << "failed to allocate VA entrypoints"; + return false; + } + + /* use the highest profile */ + for (i = 0; i < sizeof(profile_list) / sizeof(profile_list[0]); i++) { + if ((h264_profile != ~0) && h264_profile != profile_list[i]) + continue; + + h264_profile = profile_list[i]; + vaQueryConfigEntrypoints(va_display, h264_profile, entrypoints, + &num_entrypoints); + for (slice_entrypoint = 0; slice_entrypoint < num_entrypoints; + slice_entrypoint++) { + if ((entrypoints[slice_entrypoint] == VAEntrypointEncSlice) || + (entrypoints[slice_entrypoint] == VAEntrypointEncSliceLP)) { + support_encode = true; + selected_entrypoint = entrypoints[slice_entrypoint]; + break; + } + } + if (support_encode) { + RTC_LOG(LS_INFO) << "Using EntryPoint - " << selected_entrypoint; + break; + } + } + + if (support_encode) { + RTC_LOG(LS_INFO) << "Supported H264 Encoder, Using EntryPoint - " + << selected_entrypoint; + } else { + RTC_LOG(LS_ERROR) + << "Can't find VAEntrypointEncSlice or VAEntrypointEncSliceLP for " + "H264 profiles"; + delete[] entrypoints; + return false; + } + + delete[] entrypoints; + return true; +} + +static VADisplay va_open_display_drm(int* drm_fd) { + VADisplay va_dpy; + int i; + + static const char* drm_device_paths[] = {"/dev/dri/renderD128", + "/dev/dri/renderD129", NULL}; + for (i = 0; drm_device_paths[i]; i++) { + *drm_fd = open(drm_device_paths[i], O_RDWR); + if (*drm_fd < 0) + continue; + + va_dpy = vaGetDisplayDRM(*drm_fd); + vaSetErrorCallback(va_dpy, NULL, NULL); + vaSetInfoCallback(va_dpy, NULL, NULL); + if (va_dpy && check_h264_encoding_support(va_dpy)) + return va_dpy; + + close(*drm_fd); + *drm_fd = -1; + } + return NULL; +} + +namespace livekit_ffi { + +bool VaapiDisplayDrm::Open() { + va_display_ = va_open_display_drm(&drm_fd_); + if (!va_display_) { + RTC_LOG(LS_ERROR) << "Failed to open VA drm display. Maybe the AMD video " + "driver or libva-dev/libdrm-dev is not installed?"; + return false; + } + return true; +} + +bool VaapiDisplayDrm::isOpen() const { + return va_display_ != nullptr; +} + +void VaapiDisplayDrm::Close() { + if (va_display_) { + if (drm_fd_ < 0) + return; + + close(drm_fd_); + drm_fd_ = -1; + va_display_ = nullptr; + } +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_display_drm.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_display_drm.h new file mode 100644 index 0000000..cee81fa --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_display_drm.h @@ -0,0 +1,35 @@ +#ifndef VAAPI_DISPLAY_DRM_H_ +#define VAAPI_DISPLAY_DRM_H_ + +#include +#include + +namespace livekit_ffi { + +// VAAPI drm display wrapper class +class VaapiDisplayDrm { + public: + VaapiDisplayDrm() = default; + VaapiDisplayDrm(const VaapiDisplayDrm&) = delete; + ~VaapiDisplayDrm() = default; + + // Initialize the VAAPI display + bool Open(); + + // Check if the VAAPI display is open + bool isOpen() const; + + // Close the VAAPI display + void Close(); + + // Get the VAAPI display handle + VADisplay display() const { return va_display_; } + + private: + VADisplay va_display_; + int drm_fd_; +}; + +} // namespace livekit_ffi + +#endif // VAAPI_DISPLAY_DRM_H_ diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_display_win32.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_display_win32.cpp new file mode 100644 index 0000000..9b047c2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_display_win32.cpp @@ -0,0 +1,192 @@ +/* + * Copyright © Microsoft Corporation + * + * 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 (including the next + * paragraph) 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. + */ + + #include "vaapi_display_win32.h" + +#include +#include +#include +#include + +static const char* g_device_name; + +void dxcore_resolve_adapter(const char* adapter_string, + /*out*/ bool* ptr_device_found, + /*out*/ LUID* ptr_adapter_luid) { + int selected_adapter_index = -1; + IDXCoreAdapterFactory* factory = nullptr; + IDXCoreAdapterList* adapter_list = nullptr; + IDXCoreAdapter* adapter = nullptr; + typedef HRESULT(WINAPI * PFN_CREATE_DXCORE_ADAPTER_FACTORY)(REFIID riid, + void** ppFactory); + PFN_CREATE_DXCORE_ADAPTER_FACTORY DXCoreCreateAdapterFactory; + HRESULT hr = S_OK; + + memset(ptr_adapter_luid, 0, sizeof(LUID)); + *ptr_device_found = false; + + HMODULE dxcore_mod = LoadLibraryA("DXCore.DLL"); + if (!dxcore_mod) { + fprintf(stderr, "Failed to load DXCore.DLL to enumerate adapters.\n"); + goto fail; + } + + DXCoreCreateAdapterFactory = + (PFN_CREATE_DXCORE_ADAPTER_FACTORY)GetProcAddress( + dxcore_mod, "DXCoreCreateAdapterFactory"); + if (!DXCoreCreateAdapterFactory) { + fprintf(stderr, + "Failed to load DXCoreCreateAdapterFactory from DXCore.DLL.\n"); + goto fail; + } + + hr = DXCoreCreateAdapterFactory(IID_IDXCoreAdapterFactory, (void**)&factory); + if (FAILED(hr)) { + fprintf(stderr, "DXCoreCreateAdapterFactory failed: %lx\n", hr); + goto fail; + } + + hr = + factory->CreateAdapterList(1, &DXCORE_ADAPTER_ATTRIBUTE_D3D12_GRAPHICS, + IID_IDXCoreAdapterList, (void**)&adapter_list); + if (FAILED(hr)) { + fprintf(stderr, "CreateAdapterList failed: %lx\n", hr); + goto fail; + } + + if (adapter_string && + (sscanf_s(adapter_string, "%d", &selected_adapter_index) != 1)) { + fprintf(stderr, "Invalid device index received for -hwaccel_device %s\n", + adapter_string ? adapter_string : ""); + } + + if (!adapter_string) + fprintf(stdout, "Available devices for --display win32:\n"); + for (int i = 0; i < adapter_list->GetAdapterCount(); i++) { + if (SUCCEEDED(adapter_list->GetAdapter(i, IID_IDXCoreAdapter, + (void**)&adapter))) { + size_t desc_size = 0; + if (FAILED(adapter->GetPropertySize( + DXCoreAdapterProperty::DriverDescription, &desc_size))) { + adapter->Release(); + continue; + } + + char* adapter_name = (char*)malloc(desc_size); + if (!adapter_name) { + adapter->Release(); + continue; + } + + if (FAILED(adapter->GetProperty(DXCoreAdapterProperty::DriverDescription, + desc_size, adapter_name))) { + free(adapter_name); + adapter->Release(); + continue; + } + + LUID cur_adapter_luid = {0, 0}; + if (FAILED(adapter->GetProperty(DXCoreAdapterProperty::InstanceLuid, + &cur_adapter_luid))) { + free(adapter_name); + adapter->Release(); + continue; + } + + if (selected_adapter_index == i) { + *ptr_adapter_luid = cur_adapter_luid; + *ptr_device_found = true; + } + + if (!adapter_string) + fprintf(stdout, + "\tDevice Index: %d Device LUID: %lu %ld - Device Name: %s\n", + i, cur_adapter_luid.LowPart, cur_adapter_luid.HighPart, + adapter_name); + free(adapter_name); + adapter->Release(); + } + } + +fail: + if (adapter_list) + adapter_list->Release(); + if (factory) + factory->Release(); + if (dxcore_mod) + FreeLibrary(dxcore_mod); +} + +static VADisplay va_open_display_win32(void) { + LUID adapter_luid = {0, 0}; + bool device_found = false; + if (g_device_name) { + bool print_devices = (0 == strcmp(g_device_name, "help")); + dxcore_resolve_adapter(print_devices ? NULL : g_device_name, &device_found, + &adapter_luid); + if (print_devices) { + exit(0); + } else if (g_device_name && !device_found) { + fprintf(stderr, + "Could not find device %s for --display win32. Please try " + "--device help for a list of available devices.\n", + g_device_name); + exit(0); + } + } + + // Adapter automatic selection supported by sending NULL adapter to + // vaGetDisplayWin32 + return vaGetDisplayWin32(device_found ? &adapter_luid : NULL); +} + +static void va_close_display_win32(VADisplay va_dpy) {} + +namespace livekit_ffi { + +VaapiDisplayWin32::VaapiDisplayWin32() : va_display_(nullptr) { + putenv("LIBVA_DRIVER_NAME=vaon12"); + putenv("LIBVA_DRIVERS_PATH=."); +} + +bool VaapiDisplayWin32::Open() { + va_display_ = va_open_display_win32(); + if (!va_display_) { + fprintf(stderr, "Failed to open VA display\n"); + return false; + } + return true; +} + +bool VaapiDisplayWin32::isOpen() const { + return va_display_ != nullptr; +} + +void VaapiDisplayWin32::Close() { + if (va_display_) { + va_close_display_win32(va_display_); + va_display_ = nullptr; + } +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_display_win32.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_display_win32.h new file mode 100644 index 0000000..cd7ed7a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_display_win32.h @@ -0,0 +1,33 @@ +#ifndef VAAPI_DISPLAY_WIN32_H_ +#define VAAPI_DISPLAY_WIN32_H_ + +#include +#include + +namespace livekit_ffi { + +// VAAPI win32 display wrapper class +class VaapiDisplayWin32 { + public: + VaapiDisplayWin32(); + ~VaapiDisplayWin32() {} + + // Initialize the VAAPI display + bool Open(); + + // Check if the VAAPI display is open + bool isOpen() const; + + // Close the VAAPI display + void Close(); + + // Get the VAAPI display handle + VADisplay display() const { return va_display_; } + + private: + VADisplay va_display_ = nullptr; +}; + +} // namespace livekit_ffi + +#endif // VAAPI_DISPLAY_WIN32_H_ diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_encoder_factory.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_encoder_factory.cpp new file mode 100644 index 0000000..d418bbc --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_encoder_factory.cpp @@ -0,0 +1,93 @@ +#include "vaapi_encoder_factory.h" + +#include +#include +#include + +#include "h264_encoder_impl.h" +#include "rtc_base/logging.h" + +#if defined(WIN32) +#include "vaapi_display_win32.h" +using VaapiDisplay = livekit_ffi::VaapiDisplayWin32; +#elif defined(__linux__) +#include "vaapi_display_drm.h" +using VaapiDisplay = livekit_ffi::VaapiDisplayDrm; +#endif + +namespace webrtc { + +VAAPIVideoEncoderFactory::VAAPIVideoEncoderFactory() { + std::map baselineParameters = { + {"profile-level-id", "42e01f"}, + {"level-asymmetry-allowed", "1"}, + {"packetization-mode", "1"}, + }; + supported_formats_.push_back(SdpVideoFormat("H264", baselineParameters)); + /* + std::map highParameters = { + {"profile-level-id", "4d0032"}, + {"level-asymmetry-allowed", "1"}, + {"packetization-mode", "1"}, + }; + + supported_formats_.push_back(SdpVideoFormat("H264", highParameters)); + */ +} + +VAAPIVideoEncoderFactory::~VAAPIVideoEncoderFactory() {} + +bool VAAPIVideoEncoderFactory::IsSupported() { + // Ensure that libva and libva-drm are actually available for loading. + // Otherwise, we will immediately abort. + void* libva_ptr = dlopen("libva.so.2", RTLD_LAZY); + if (!libva_ptr) { + RTC_LOG(LS_INFO) << "libva.so.2 is not found"; + return false; + } + dlclose(libva_ptr); + + void* libvadrm_ptr = dlopen("libva-drm.so.2", RTLD_LAZY); + if (!libvadrm_ptr) { + RTC_LOG(LS_INFO) << "libva-drm.so.2 is not found"; + return false; + } + dlclose(libvadrm_ptr); + + // Check if VAAPI is supported by the environment. + // This could involve checking if the VAAPI display can be opened. + VaapiDisplay vaapi_display; + if (!vaapi_display.Open()) { + RTC_LOG(LS_WARNING) << "Failed to open VAAPI display."; + return false; + } + + vaapi_display.Close(); + // If we can open the VAAPI display, we consider it supported. + std::cout << "VAAPI is supported." << std::endl; + return true; +} + +std::unique_ptr VAAPIVideoEncoderFactory::Create( + const Environment& env, + const SdpVideoFormat& format) { + // Check if the requested format is supported. + for (const auto& supported_format : supported_formats_) { + if (format.IsSameCodec(supported_format)) { + // If the format is supported, create and return the encoder. + return std::make_unique(env, format); + } + } + return nullptr; +} +std::vector VAAPIVideoEncoderFactory::GetSupportedFormats() + const { + return supported_formats_; +} + +std::vector VAAPIVideoEncoderFactory::GetImplementations() + const { + return supported_formats_; +} + +} // namespace webrtc diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_encoder_factory.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_encoder_factory.h new file mode 100644 index 0000000..c1e5766 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_encoder_factory.h @@ -0,0 +1,40 @@ + + + #ifndef VAAPI_VIDEO_ENCODER_FACTORY_H_ + #define VAAPI_VIDEO_ENCODER_FACTORY_H_ + + #include + + #include "api/environment/environment.h" + #include "api/video_codecs/sdp_video_format.h" + #include "api/video_codecs/video_encoder_factory.h" + + namespace webrtc { + + class VAAPIVideoEncoderFactory : public VideoEncoderFactory { + public: + VAAPIVideoEncoderFactory(); + ~VAAPIVideoEncoderFactory() override; + + static bool IsSupported(); + + std::unique_ptr Create(const Environment& env, + const SdpVideoFormat& format) override; + + // Returns a list of supported codecs in order of preference. + std::vector GetSupportedFormats() const override; + + std::vector GetImplementations() const override; + + std::unique_ptr GetEncoderSelector() const override { + return nullptr; + } + + private: + std::vector supported_formats_; + }; + + } // namespace webrtc + + #endif // VAAPI_VIDEO_ENCODER_FACTORY_H_ + \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_h264_encoder_wrapper.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_h264_encoder_wrapper.cpp new file mode 100644 index 0000000..f255bb2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_h264_encoder_wrapper.cpp @@ -0,0 +1,1880 @@ +#include "vaapi_h264_encoder_wrapper.h" + +#include +#include +#include +#include + +#include + +#include "rtc_base/logging.h" + +#define NAL_REF_IDC_NONE 0 +#define NAL_REF_IDC_LOW 1 +#define NAL_REF_IDC_MEDIUM 2 +#define NAL_REF_IDC_HIGH 3 + +#define NAL_NON_IDR 1 +#define NAL_IDR 5 +#define NAL_SPS 7 +#define NAL_PPS 8 +#define NAL_SEI 6 + +#define SLICE_TYPE_P 0 +#define SLICE_TYPE_B 1 +#define SLICE_TYPE_I 2 +#define IS_P_SLICE(type) (SLICE_TYPE_P == (type)) +#define IS_B_SLICE(type) (SLICE_TYPE_B == (type)) +#define IS_I_SLICE(type) (SLICE_TYPE_I == (type)) + +#define ENTROPY_MODE_CAVLC 0 +#define ENTROPY_MODE_CABAC 1 + +#define PROFILE_IDC_BASELINE 66 +#define PROFILE_IDC_MAIN 77 +#define PROFILE_IDC_HIGH 100 + +#define BITSTREAM_ALLOCATE_STEPPING 4096 + +static const uint32_t MaxFrameNum = (2 << 16); +static const uint32_t MaxPicOrderCntLsb = (2 << 8); +static const uint32_t Log2MaxFrameNum = 16; +static const uint32_t Log2MaxPicOrderCntLsb = 8; +static const uint32_t num_ref_frames = 2; +static const int srcyuv_fourcc = VA_FOURCC_NV12; +static const uint32_t frame_slices = 1; + +static const int rc_default_modes[] = { + VA_RC_VBR, VA_RC_CQP, VA_RC_VBR_CONSTRAINED, + VA_RC_CBR, VA_RC_VCM, VA_RC_NONE, +}; + +VAImageFormat kImageFormatI420 = { + .fourcc = VA_FOURCC_I420, + .byte_order = VA_LSB_FIRST, + .bits_per_pixel = 12, +}; + +static int upload_surface_yuv(VADisplay va_dpy, + VASurfaceID surface_id, + int src_fourcc, + int src_width, + int src_height, + uint8_t* src_Y, + uint8_t* src_U, + uint8_t* src_V) { + VAImage surface_image; + uint8_t *surface_p = NULL, *Y_start = NULL, *U_start = NULL; + int Y_pitch = 0, U_pitch = 0, row; + VAStatus va_status; + + va_status = vaDeriveImage(va_dpy, surface_id, &surface_image); + if (va_status != VA_STATUS_SUCCESS) { + // If the driver does not support vaDeriveImage, create a new image. + va_status = vaCreateImage(va_dpy, &kImageFormatI420, src_width, src_height, + &surface_image); + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaCreateImage failed with status " << va_status; + return -1; + } + } + + vaMapBuffer(va_dpy, surface_image.buf, (void**)&surface_p); + assert(VA_STATUS_SUCCESS == va_status); + + Y_start = surface_p; + Y_pitch = surface_image.pitches[0]; + switch (surface_image.format.fourcc) { + case VA_FOURCC_NV12: + U_start = (unsigned char*)surface_p + surface_image.offsets[1]; + U_pitch = surface_image.pitches[1]; + break; + case VA_FOURCC_I420: + U_start = (unsigned char*)surface_p + surface_image.offsets[1]; + U_pitch = surface_image.pitches[1]; + break; + case VA_FOURCC_YV12: + U_start = (unsigned char*)surface_p + surface_image.offsets[2]; + U_pitch = surface_image.pitches[2]; + break; + case VA_FOURCC_YUY2: + U_start = surface_p + 1; + U_pitch = surface_image.pitches[0]; + break; + default: + assert(0); + } + + /* copy Y plane */ + for (row = 0; row < src_height; row++) { + uint8_t* Y_row = Y_start + row * Y_pitch; + memcpy(Y_row, src_Y + row * src_width, src_width); + } + for (row = 0; row < src_height / 2; row++) { + uint8_t* U_row = U_start + row * U_pitch; + uint8_t *u_ptr = NULL, *v_ptr = NULL; + int j; + if (src_fourcc == VA_FOURCC_NV12) { + memcpy(U_row, src_U + row * src_width, src_width); + break; + } else if (src_fourcc == VA_FOURCC_I420) { + u_ptr = src_U + row * (src_width / 2); + v_ptr = src_V + row * (src_width / 2); + } else if (src_fourcc == VA_FOURCC_YV12) { + v_ptr = src_U + row * (src_width / 2); + u_ptr = src_V + row * (src_width / 2); + } + if ((src_fourcc == VA_FOURCC_I420) || (src_fourcc == VA_FOURCC_YV12)) { + for (j = 0; j < src_width / 2; j++) { + U_row[2 * j] = u_ptr[j]; + U_row[2 * j + 1] = v_ptr[j]; + } + } + } + vaUnmapBuffer(va_dpy, surface_image.buf); + vaDestroyImage(va_dpy, surface_image.image_id); + + return 0; +} + +#define MIN(a, b) ((a) > (b) ? (b) : (a)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +struct __bitstream { + uint32_t* buffer; + int bit_offset; + int max_size_in_dword; +}; +typedef struct __bitstream bitstream; + +static uint32_t va_swap32(uint32_t val) { + unsigned char* pval = (unsigned char*)&val; + + return ((pval[0] << 24) | (pval[1] << 16) | (pval[2] << 8) | (pval[3] << 0)); +} + +static void bitstream_start(bitstream* bs) { + bs->max_size_in_dword = BITSTREAM_ALLOCATE_STEPPING; + bs->buffer = (uint32_t*)calloc(bs->max_size_in_dword * sizeof(int), 1); + assert(bs->buffer); + bs->bit_offset = 0; +} + +static void bitstream_end(bitstream* bs) { + int pos = (bs->bit_offset >> 5); + int bit_offset = (bs->bit_offset & 0x1f); + int bit_left = 32 - bit_offset; + + if (bit_offset) { + bs->buffer[pos] = va_swap32((bs->buffer[pos] << bit_left)); + } +} + +static void bitstream_put_ui(bitstream* bs, uint32_t val, int size_in_bits) { + int pos = (bs->bit_offset >> 5); + int bit_offset = (bs->bit_offset & 0x1f); + int bit_left = 32 - bit_offset; + + if (!size_in_bits) + return; + + bs->bit_offset += size_in_bits; + + if (bit_left > size_in_bits) { + bs->buffer[pos] = (bs->buffer[pos] << size_in_bits | val); + } else { + size_in_bits -= bit_left; + bs->buffer[pos] = (bs->buffer[pos] << bit_left) | (val >> size_in_bits); + bs->buffer[pos] = va_swap32(bs->buffer[pos]); + + if (pos + 1 == bs->max_size_in_dword) { + bs->max_size_in_dword += BITSTREAM_ALLOCATE_STEPPING; + bs->buffer = bs->buffer = (uint32_t*)realloc( + bs->buffer, bs->max_size_in_dword * sizeof(uint32_t)); + assert(bs->buffer); + } + + bs->buffer[pos + 1] = val; + } +} + +static void bitstream_put_ue(bitstream* bs, uint32_t val) { + int size_in_bits = 0; + int tmp_val = ++val; + + while (tmp_val) { + tmp_val >>= 1; + size_in_bits++; + } + + bitstream_put_ui(bs, 0, size_in_bits - 1); // leading zero + bitstream_put_ui(bs, val, size_in_bits); +} + +static void bitstream_put_se(bitstream* bs, int val) { + uint32_t new_val; + + if (val <= 0) + new_val = -2 * val; + else + new_val = 2 * val - 1; + + bitstream_put_ue(bs, new_val); +} + +static void bitstream_byte_aligning(bitstream* bs, int bit) { + int bit_offset = (bs->bit_offset & 0x7); + int bit_left = 8 - bit_offset; + int new_val; + + if (!bit_offset) + return; + + assert(bit == 0 || bit == 1); + + if (bit) + new_val = (1 << bit_left) - 1; + else + new_val = 0; + + bitstream_put_ui(bs, new_val, bit_left); +} + +static void rbsp_trailing_bits(bitstream* bs) { + bitstream_put_ui(bs, 1, 1); + bitstream_byte_aligning(bs, 0); +} + +static void nal_start_code_prefix(bitstream* bs) { + bitstream_put_ui(bs, 0x00000001, 32); +} + +static void nal_header(bitstream* bs, int nal_ref_idc, int nal_unit_type) { + bitstream_put_ui(bs, 0, 1); /* forbidden_zero_bit: 0 */ + bitstream_put_ui(bs, nal_ref_idc, 2); + bitstream_put_ui(bs, nal_unit_type, 5); +} + +static void sps_rbsp(VA264Context* context, bitstream* bs) { + int profile_idc = PROFILE_IDC_BASELINE; + + if (context->config.h264_profile == VAProfileH264High) + profile_idc = PROFILE_IDC_HIGH; + else if (context->config.h264_profile == VAProfileH264Main) + profile_idc = PROFILE_IDC_MAIN; + + bitstream_put_ui(bs, profile_idc, 8); /* profile_idc */ + bitstream_put_ui(bs, !!(context->constraint_set_flag & 1), + 1); /* constraint_set0_flag */ + bitstream_put_ui(bs, !!(context->constraint_set_flag & 2), + 1); /* constraint_set1_flag */ + bitstream_put_ui(bs, !!(context->constraint_set_flag & 4), + 1); /* constraint_set2_flag */ + bitstream_put_ui(bs, !!(context->constraint_set_flag & 8), + 1); /* constraint_set3_flag */ + bitstream_put_ui(bs, 0, 4); /* reserved_zero_4bits */ + bitstream_put_ui(bs, context->seq_param.level_idc, 8); /* level_idc */ + bitstream_put_ue( + bs, context->seq_param.seq_parameter_set_id); /* seq_parameter_set_id */ + + if (profile_idc == PROFILE_IDC_HIGH) { + bitstream_put_ue(bs, 1); /* chroma_format_idc = 1, 4:2:0 */ + bitstream_put_ue(bs, 0); /* bit_depth_luma_minus8 */ + bitstream_put_ue(bs, 0); /* bit_depth_chroma_minus8 */ + bitstream_put_ui(bs, 0, 1); /* qpprime_y_zero_transform_bypass_flag */ + bitstream_put_ui(bs, 0, 1); /* seq_scaling_matrix_present_flag */ + } + + bitstream_put_ue( + bs, context->seq_param.seq_fields.bits + .log2_max_frame_num_minus4); /* log2_max_frame_num_minus4 */ + bitstream_put_ue(bs, context->seq_param.seq_fields.bits + .pic_order_cnt_type); /* pic_order_cnt_type */ + + if (context->seq_param.seq_fields.bits.pic_order_cnt_type == 0) + bitstream_put_ue( + bs, + context->seq_param.seq_fields.bits + .log2_max_pic_order_cnt_lsb_minus4); /* log2_max_pic_order_cnt_lsb_minus4 + */ + else { + assert(0); + } + + bitstream_put_ue(bs, + context->seq_param.max_num_ref_frames); /* num_ref_frames */ + bitstream_put_ui(bs, 0, 1); /* gaps_in_frame_num_value_allowed_flag */ + + bitstream_put_ue(bs, context->seq_param.picture_width_in_mbs - + 1); /* pic_width_in_mbs_minus1 */ + bitstream_put_ue(bs, context->seq_param.picture_height_in_mbs - + 1); /* pic_height_in_map_units_minus1 */ + bitstream_put_ui(bs, context->seq_param.seq_fields.bits.frame_mbs_only_flag, + 1); /* frame_mbs_only_flag */ + + if (!context->seq_param.seq_fields.bits.frame_mbs_only_flag) { + assert(0); + } + + bitstream_put_ui(bs, + context->seq_param.seq_fields.bits.direct_8x8_inference_flag, + 1); /* direct_8x8_inference_flag */ + bitstream_put_ui(bs, context->seq_param.frame_cropping_flag, + 1); /* frame_cropping_flag */ + + if (context->seq_param.frame_cropping_flag) { + bitstream_put_ue( + bs, + context->seq_param.frame_crop_left_offset); /* frame_crop_left_offset */ + bitstream_put_ue( + bs, context->seq_param + .frame_crop_right_offset); /* frame_crop_right_offset */ + bitstream_put_ue( + bs, + context->seq_param.frame_crop_top_offset); /* frame_crop_top_offset */ + bitstream_put_ue( + bs, context->seq_param + .frame_crop_bottom_offset); /* frame_crop_bottom_offset */ + } + + // if ( frame_bit_rate < 0 ) { //TODO EW: the vui header isn't correct + if (1) { + bitstream_put_ui(bs, 0, 1); /* vui_parameters_present_flag */ + } else { + bitstream_put_ui(bs, 1, 1); /* vui_parameters_present_flag */ + bitstream_put_ui(bs, 0, 1); /* aspect_ratio_info_present_flag */ + bitstream_put_ui(bs, 0, 1); /* overscan_info_present_flag */ + bitstream_put_ui(bs, 0, 1); /* video_signal_type_present_flag */ + bitstream_put_ui(bs, 0, 1); /* chroma_loc_info_present_flag */ + bitstream_put_ui(bs, 1, 1); /* timing_info_present_flag */ + { + bitstream_put_ui(bs, 15, 32); + bitstream_put_ui(bs, 900, 32); + bitstream_put_ui(bs, 1, 1); + } + bitstream_put_ui(bs, 1, 1); /* nal_hrd_parameters_present_flag */ + { + // hrd_parameters + bitstream_put_ue(bs, 0); /* cpb_cnt_minus1 */ + bitstream_put_ui(bs, 4, 4); /* bit_rate_scale */ + bitstream_put_ui(bs, 6, 4); /* cpb_size_scale */ + + bitstream_put_ue( + bs, context->config.bitrate - 1); /* bit_rate_value_minus1[0] */ + bitstream_put_ue( + bs, context->config.bitrate * 8 - 1); /* cpb_size_value_minus1[0] */ + bitstream_put_ui(bs, 1, 1); /* cbr_flag[0] */ + + bitstream_put_ui(bs, 23, 5); /* initial_cpb_removal_delay_length_minus1 */ + bitstream_put_ui(bs, 23, 5); /* cpb_removal_delay_length_minus1 */ + bitstream_put_ui(bs, 23, 5); /* dpb_output_delay_length_minus1 */ + bitstream_put_ui(bs, 23, 5); /* time_offset_length */ + } + bitstream_put_ui(bs, 0, 1); /* vcl_hrd_parameters_present_flag */ + bitstream_put_ui(bs, 0, 1); /* low_delay_hrd_flag */ + + bitstream_put_ui(bs, 0, 1); /* pic_struct_present_flag */ + bitstream_put_ui(bs, 0, 1); /* bitstream_restriction_flag */ + } + + rbsp_trailing_bits(bs); /* rbsp_trailing_bits */ +} + +static void pps_rbsp(VA264Context* context, bitstream* bs) { + bitstream_put_ue( + bs, context->pic_param.pic_parameter_set_id); /* pic_parameter_set_id */ + bitstream_put_ue( + bs, context->pic_param.seq_parameter_set_id); /* seq_parameter_set_id */ + + bitstream_put_ui(bs, + context->pic_param.pic_fields.bits.entropy_coding_mode_flag, + 1); /* entropy_coding_mode_flag */ + + bitstream_put_ui(bs, 0, 1); /* pic_order_present_flag: 0 */ + + bitstream_put_ue(bs, 0); /* num_slice_groups_minus1 */ + + bitstream_put_ue( + bs, context->pic_param + .num_ref_idx_l0_active_minus1); /* num_ref_idx_l0_active_minus1 */ + bitstream_put_ue( + bs, context->pic_param + .num_ref_idx_l1_active_minus1); /* num_ref_idx_l1_active_minus1 + 1 */ + + bitstream_put_ui(bs, context->pic_param.pic_fields.bits.weighted_pred_flag, + 1); /* weighted_pred_flag: 0 */ + bitstream_put_ui(bs, context->pic_param.pic_fields.bits.weighted_bipred_idc, + 2); /* weighted_bipred_idc: 0 */ + + bitstream_put_se( + bs, context->pic_param.pic_init_qp - 26); /* pic_init_qp_minus26 */ + bitstream_put_se(bs, 0); /* pic_init_qs_minus26 */ + bitstream_put_se(bs, 0); /* chroma_qp_index_offset */ + + bitstream_put_ui( + bs, + context->pic_param.pic_fields.bits.deblocking_filter_control_present_flag, + 1); /* deblocking_filter_control_present_flag */ + bitstream_put_ui(bs, 0, 1); /* constrained_intra_pred_flag */ + bitstream_put_ui(bs, 0, 1); /* redundant_pic_cnt_present_flag */ + + /* more_rbsp_data */ + bitstream_put_ui(bs, + context->pic_param.pic_fields.bits.transform_8x8_mode_flag, + 1); /*transform_8x8_mode_flag */ + bitstream_put_ui(bs, 0, 1); /* pic_scaling_matrix_present_flag */ + bitstream_put_se( + bs, context->pic_param + .second_chroma_qp_index_offset); /*second_chroma_qp_index_offset + */ + + rbsp_trailing_bits(bs); +} + +static void slice_header(VA264Context* context, bitstream* bs) { + int first_mb_in_slice = context->slice_param.macroblock_address; + + bitstream_put_ue(bs, first_mb_in_slice); /* first_mb_in_slice: 0 */ + bitstream_put_ue(bs, context->slice_param.slice_type); /* slice_type */ + bitstream_put_ue( + bs, + context->slice_param.pic_parameter_set_id); /* pic_parameter_set_id: 0 */ + bitstream_put_ui( + bs, context->pic_param.frame_num, + context->seq_param.seq_fields.bits.log2_max_frame_num_minus4 + + 4); /* frame_num */ + + if (context->pic_param.pic_fields.bits.idr_pic_flag) + bitstream_put_ue(bs, context->slice_param.idr_pic_id); /* idr_pic_id: 0 */ + + if (context->seq_param.seq_fields.bits.pic_order_cnt_type == 0) { + bitstream_put_ui( + bs, context->pic_param.CurrPic.TopFieldOrderCnt, + context->seq_param.seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 + + 4); + /* pic_order_present_flag == 0 */ + } + + /* redundant_pic_cnt_present_flag == 0 */ + /* slice type */ + if (IS_P_SLICE(context->slice_param.slice_type)) { + bitstream_put_ui(bs, context->slice_param.num_ref_idx_active_override_flag, + 1); /* num_ref_idx_active_override_flag: */ + + if (context->slice_param.num_ref_idx_active_override_flag) + bitstream_put_ue(bs, context->slice_param.num_ref_idx_l0_active_minus1); + + /* ref_pic_list_reordering */ + bitstream_put_ui(bs, 0, 1); /* ref_pic_list_reordering_flag_l0: 0 */ + } else if (IS_B_SLICE(context->slice_param.slice_type)) { + bitstream_put_ui(bs, context->slice_param.direct_spatial_mv_pred_flag, + 1); /* direct_spatial_mv_pred: 1 */ + + bitstream_put_ui(bs, context->slice_param.num_ref_idx_active_override_flag, + 1); /* num_ref_idx_active_override_flag: */ + + if (context->slice_param.num_ref_idx_active_override_flag) { + bitstream_put_ue(bs, context->slice_param.num_ref_idx_l0_active_minus1); + bitstream_put_ue(bs, context->slice_param.num_ref_idx_l1_active_minus1); + } + + /* ref_pic_list_reordering */ + bitstream_put_ui(bs, 0, 1); /* ref_pic_list_reordering_flag_l0: 0 */ + bitstream_put_ui(bs, 0, 1); /* ref_pic_list_reordering_flag_l1: 0 */ + } + + if ((context->pic_param.pic_fields.bits.weighted_pred_flag && + IS_P_SLICE(context->slice_param.slice_type)) || + ((context->pic_param.pic_fields.bits.weighted_bipred_idc == 1) && + IS_B_SLICE(context->slice_param.slice_type))) { + } + + /* dec_ref_pic_marking */ + if (context->pic_param.pic_fields.bits + .reference_pic_flag) { /* nal_ref_idc != 0 */ + unsigned char no_output_of_prior_pics_flag = 0; + unsigned char long_term_reference_flag = 0; + unsigned char adaptive_ref_pic_marking_mode_flag = 0; + + if (context->pic_param.pic_fields.bits.idr_pic_flag) { + bitstream_put_ui(bs, no_output_of_prior_pics_flag, + 1); /* no_output_of_prior_pics_flag: 0 */ + bitstream_put_ui(bs, long_term_reference_flag, + 1); /* long_term_reference_flag: 0 */ + } else { + bitstream_put_ui(bs, adaptive_ref_pic_marking_mode_flag, + 1); /* adaptive_ref_pic_marking_mode_flag: 0 */ + } + } + + if (context->pic_param.pic_fields.bits.entropy_coding_mode_flag && + !IS_I_SLICE(context->slice_param.slice_type)) + bitstream_put_ue( + bs, context->slice_param.cabac_init_idc); /* cabac_init_idc: 0 */ + + bitstream_put_se(bs, + context->slice_param.slice_qp_delta); /* slice_qp_delta: 0 */ + + /* ignore for SP/SI */ + + if (context->pic_param.pic_fields.bits + .deblocking_filter_control_present_flag) { + bitstream_put_ue( + bs, + context->slice_param + .disable_deblocking_filter_idc); /* disable_deblocking_filter_idc: + 0 */ + + if (context->slice_param.disable_deblocking_filter_idc != 1) { + bitstream_put_se( + bs, + context->slice_param + .slice_alpha_c0_offset_div2); /* slice_alpha_c0_offset_div2: 2 */ + bitstream_put_se( + bs, context->slice_param + .slice_beta_offset_div2); /* slice_beta_offset_div2: 2 */ + } + } + + if (context->pic_param.pic_fields.bits.entropy_coding_mode_flag) { + bitstream_byte_aligning(bs, 1); + } +} + +static int build_packed_pic_buffer(VA264Context* context, + unsigned char** header_buffer) { + bitstream bs; + + bitstream_start(&bs); + nal_start_code_prefix(&bs); + nal_header(&bs, NAL_REF_IDC_HIGH, NAL_PPS); + pps_rbsp(context, &bs); + bitstream_end(&bs); + + *header_buffer = (unsigned char*)bs.buffer; + return bs.bit_offset; +} + +static int build_packed_seq_buffer(VA264Context* context, + unsigned char** header_buffer) { + bitstream bs; + + bitstream_start(&bs); + nal_start_code_prefix(&bs); + nal_header(&bs, NAL_REF_IDC_HIGH, NAL_SPS); + sps_rbsp(context, &bs); + bitstream_end(&bs); + + *header_buffer = (unsigned char*)bs.buffer; + return bs.bit_offset; +} + +static int build_packed_slice_buffer(VA264Context* context, + unsigned char** header_buffer) { + bitstream bs; + int is_idr = !!context->pic_param.pic_fields.bits.idr_pic_flag; + int is_ref = !!context->pic_param.pic_fields.bits.reference_pic_flag; + + bitstream_start(&bs); + nal_start_code_prefix(&bs); + + if (IS_I_SLICE(context->slice_param.slice_type)) { + nal_header(&bs, NAL_REF_IDC_HIGH, is_idr ? NAL_IDR : NAL_NON_IDR); + } else if (IS_P_SLICE(context->slice_param.slice_type)) { + nal_header(&bs, NAL_REF_IDC_MEDIUM, NAL_NON_IDR); + } else { + assert(IS_B_SLICE(context->slice_param.slice_type)); + nal_header(&bs, is_ref ? NAL_REF_IDC_LOW : NAL_REF_IDC_NONE, NAL_NON_IDR); + } + + slice_header(context, &bs); + bitstream_end(&bs); + + *header_buffer = (unsigned char*)bs.buffer; + return bs.bit_offset; +} + +/* + Assume frame sequence is: Frame#0,#1,#2,...,#M,...,#X,... (encoding order) + 1) period between Frame #X and Frame #N = #X - #N + 2) 0 means infinite for intra_period/intra_idr_period, and 0 is invalid for + ip_period 3) intra_idr_period % intra_period (intra_period > 0) and + intra_period % ip_period must be 0 4) intra_period and intra_idr_period take + precedence over ip_period 5) if ip_period > 1, intra_period and + intra_idr_period are not the strict periods of I/IDR frames, see bellow + examples + ------------------------------------------------------------------- + intra_period intra_idr_period ip_period frame sequence + (intra_period/intra_idr_period/ip_period) 0 ignored 1 + IDRPPPPPPP ... (No IDR/I any more) 0 ignored >=2 + IDR(PBB)(PBB)... (No IDR/I any more) 1 0 ignored + IDRIIIIIII... (No IDR any more) 1 1 ignored IDR + IDR IDR IDR... 1 >=2 ignored IDRII IDRII IDR... + (1/3/ignore) + >=2 0 1 IDRPPP IPPP I... (3/0/1) + >=2 0 >=2 IDR(PBB)(PBB)(IBB) (6/0/3) + (PBB)(IBB)(PBB)(IBB)... + >=2 >=2 1 IDRPPPPP IPPPPP IPPPPP (6/18/1) + IDRPPPPP IPPPPP IPPPPP... + >=2 >=2 >=2 {IDR(PBB)(PBB)(IBB)(PBB)(IBB)(PBB)} + (6/18/3) {IDR(PBB)(PBB)(IBB)(PBB)(IBB)(PBB)}... {IDR(PBB)(PBB)(IBB)(PBB)} + (6/12/3) {IDR(PBB)(PBB)(IBB)(PBB)}... {IDR(PBB)(PBB)} (6/6/3) {IDR(PBB)(PBB)}. +*/ + +/* + * Return displaying order with specified periods and encoding order + * displaying_order: displaying order + * frame_type: frame type + */ +#define FRAME_P 0 +#define FRAME_B 1 +#define FRAME_I 2 +#define FRAME_IDR 7 +void encoding2display_order(uint64_t encoding_order, + int intra_period, + int intra_idr_period, + int ip_period, + uint64_t* displaying_order, + int* frame_type) { + int encoding_order_gop = 0; + + if (intra_period == 1) { /* all are I/IDR frames */ + *displaying_order = encoding_order; + if (intra_idr_period == 0) + *frame_type = (encoding_order == 0) ? FRAME_IDR : FRAME_I; + else + *frame_type = + (encoding_order % intra_idr_period == 0) ? FRAME_IDR : FRAME_I; + return; + } + + if (intra_period == 0) + intra_idr_period = 0; + + /* new sequence like + * IDR PPPPP IPPPPP + * IDR (PBB)(PBB)(IBB)(PBB) + */ + encoding_order_gop = + (intra_idr_period == 0) + ? encoding_order + : (encoding_order % (intra_idr_period + ((ip_period == 1) ? 0 : 1))); + + if (encoding_order_gop == 0) { /* the first frame */ + *frame_type = FRAME_IDR; + *displaying_order = encoding_order; + } else if (((encoding_order_gop - 1) % ip_period) != 0) { /* B frames */ + *frame_type = FRAME_B; + *displaying_order = encoding_order - 1; + } else if ((intra_period != 0) && /* have I frames */ + (encoding_order_gop >= 2) && + ((ip_period == 1 && encoding_order_gop % intra_period == + 0) || /* for IDR PPPPP IPPPP */ + /* for IDR (PBB)(PBB)(IBB) */ + (ip_period >= 2 && ((encoding_order_gop - 1) / ip_period % + (intra_period / ip_period)) == 0))) { + *frame_type = FRAME_I; + *displaying_order = encoding_order + ip_period - 1; + } else { + *frame_type = FRAME_P; + *displaying_order = encoding_order + ip_period - 1; + } +} + +std::map fourcc_map = {{VA_FOURCC_NV12, "NV12"}, + {VA_FOURCC_I420, "I420"}, + {VA_FOURCC_YV12, "YV12"}, + {VA_FOURCC_UYVY, "UYVY"}}; + +static std::string fourcc_to_string(int fourcc) { + auto it = fourcc_map.find(fourcc); + if (it != fourcc_map.end()) { + return it->second; + } + RTC_LOG(LS_ERROR) << "Unknow FOURCC"; + return "Unknown"; +} + +std::map rc_mode_map = { + {VA_RC_NONE, "NONE"}, {VA_RC_CBR, "CBR"}, + {VA_RC_VBR, "VBR"}, {VA_RC_VCM, "VCM"}, + {VA_RC_CQP, "CQP"}, {VA_RC_VBR_CONSTRAINED, "VBR_CONSTRAINED"}}; + +static std::string rc_to_string(int rcmode) { + auto it = rc_mode_map.find(rcmode); + if (it != rc_mode_map.end()) { + return it->second; + } + return "Unknown"; +} + +static int init_va(VA264Context* context, VADisplay va_dpy) { + VAProfile profile_list[] = {VAProfileH264High, VAProfileH264Main, + VAProfileH264ConstrainedBaseline}; + VAEntrypoint* entrypoints; + int num_entrypoints, slice_entrypoint; + int support_encode = 0; + int major_ver, minor_ver; + VAStatus va_status; + uint32_t i; + + context->va_dpy = va_dpy; + if (!context->va_dpy) { + return VA_STATUS_ERROR_INVALID_DISPLAY; + } + + va_status = vaInitialize(context->va_dpy, &major_ver, &minor_ver); + + if (major_ver < 0 || minor_ver < 0 || va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaInitialize failed"; + return VA_STATUS_ERROR_INVALID_DISPLAY; + } + + num_entrypoints = vaMaxNumEntrypoints(context->va_dpy); + entrypoints = new VAEntrypoint[num_entrypoints * sizeof(*entrypoints)]; + if (!entrypoints) { + RTC_LOG(LS_ERROR) << "failed to allocate VA entrypoints"; + return VA_STATUS_ERROR_INVALID_DISPLAY; + } + + /* use the highest profile */ + for (i = 0; i < sizeof(profile_list) / sizeof(profile_list[0]); i++) { + if ((context->config.h264_profile != ~0) && + context->config.h264_profile != profile_list[i]) + continue; + + context->config.h264_profile = profile_list[i]; + vaQueryConfigEntrypoints(context->va_dpy, context->config.h264_profile, + entrypoints, &num_entrypoints); + for (slice_entrypoint = 0; slice_entrypoint < num_entrypoints; + slice_entrypoint++) { + if (context->requested_entrypoint == -1) { + // Select the entry point based on what is avaiable + if ((entrypoints[slice_entrypoint] == VAEntrypointEncSlice) || + (entrypoints[slice_entrypoint] == VAEntrypointEncSliceLP)) { + support_encode = 1; + context->selected_entrypoint = entrypoints[slice_entrypoint]; + break; + } + } else if ((entrypoints[slice_entrypoint] == + context->requested_entrypoint)) { + // Select the entry point based on what was requested in cmd line option + support_encode = 1; + context->selected_entrypoint = entrypoints[slice_entrypoint]; + break; + } + } + if (support_encode == 1) { + RTC_LOG(LS_INFO) << "Using EntryPoint - " << context->selected_entrypoint; + break; + } + } + + if (support_encode == 0) { + RTC_LOG(LS_ERROR) + << "Can't find VAEntrypointEncSlice or VAEntrypointEncSliceLP for " + "H264 profiles"; + return VA_STATUS_ERROR_UNSUPPORTED_ENTRYPOINT; + } else { + switch (context->config.h264_profile) { + case VAProfileH264ConstrainedBaseline: + RTC_LOG(LS_INFO) << "Use profile VAProfileH264ConstrainedBaseline"; + context->constraint_set_flag |= (1 << 0 | 1 << 1); /* Annex A.2.2 */ + context->config.ip_period = 1; + break; + + case VAProfileH264Main: + RTC_LOG(LS_INFO) << "Use profile VAProfileH264Main"; + context->constraint_set_flag |= (1 << 1); /* Annex A.2.2 */ + break; + + case VAProfileH264High: + context->constraint_set_flag |= (1 << 3); /* Annex A.2.4 */ + RTC_LOG(LS_INFO) << "Use profile VAProfileH264High"; + break; + default: + RTC_LOG(LS_INFO) << "unknow profile. Set to Constrained Baseline"; + context->config.h264_profile = VAProfileH264ConstrainedBaseline; + context->constraint_set_flag |= + (1 << 0 | 1 << 1); /* Annex A.2.1 & A.2.2 */ + context->config.ip_period = 1; + break; + } + } + + /* find out the format for the render target, and rate control mode */ + for (i = 0; i < VAConfigAttribTypeMax; i++) + context->attrib[i].type = (VAConfigAttribType)i; + + va_status = + vaGetConfigAttributes(context->va_dpy, context->config.h264_profile, + (VAEntrypoint)context->selected_entrypoint, + &context->attrib[0], VAConfigAttribTypeMax); + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaGetConfigAttributes failed"; + delete[] entrypoints; + return va_status; + } + + /* check the interested configattrib */ + if ((context->attrib[VAConfigAttribRTFormat].value & VA_RT_FORMAT_YUV420) == + 0) { + RTC_LOG(LS_ERROR) << "Not find desired YUV420 RT format"; + return VA_STATUS_ERROR_INVALID_CONFIG; + } else { + context->config_attrib[context->config_attrib_num].type = + VAConfigAttribRTFormat; + context->config_attrib[context->config_attrib_num].value = + VA_RT_FORMAT_YUV420; + context->config_attrib_num++; + } + + if (context->attrib[VAConfigAttribRateControl].value != + VA_ATTRIB_NOT_SUPPORTED) { + int tmp = context->attrib[VAConfigAttribRateControl].value; + + // context->attrib[VAConfigAttribRateControl].value = + // context->config.rc_mode; + + std::string rc_modes; + if (tmp & VA_RC_NONE) + rc_modes += "NONE "; + if (tmp & VA_RC_VBR) + rc_modes += "VBR "; + if (tmp & VA_RC_CBR) + rc_modes += "CBR "; + if (tmp & VA_RC_VCM) + rc_modes += "VCM "; + if (tmp & VA_RC_CQP) + rc_modes += "CQP "; + if (tmp & VA_RC_VBR_CONSTRAINED) + rc_modes += "VBR_CONSTRAINED "; + + RTC_LOG(LS_INFO) << "Support rate control mode: " << rc_modes; + + if (context->config.rc_mode == -1 || !(context->config.rc_mode & tmp)) { + if (context->config.rc_mode != -1) { + RTC_LOG(LS_WARNING) + << "Warning: Don't support the specified RateControl mode: " + << rc_to_string(context->config.rc_mode) << "!!!, switch to "; + } + + for (i = 0; i < sizeof(rc_default_modes) / sizeof(rc_default_modes[0]); + i++) { + if (rc_default_modes[i] & tmp) { + context->config.rc_mode = rc_default_modes[i]; + break; + } + } + + RTC_LOG(LS_INFO) << "RateControl mode: " + << rc_to_string(context->config.rc_mode); + } + + context->config_attrib[context->config_attrib_num].type = + VAConfigAttribRateControl; + context->config_attrib[context->config_attrib_num].value = + context->config.rc_mode; + context->config_attrib_num++; + } + + if (context->attrib[VAConfigAttribEncPackedHeaders].value != + VA_ATTRIB_NOT_SUPPORTED) { + int tmp = context->attrib[VAConfigAttribEncPackedHeaders].value; + + RTC_LOG(LS_INFO) << "Support VAConfigAttribEncPackedHeaders: "; + + context->h264_packedheader = 1; + context->config_attrib[context->config_attrib_num].type = + VAConfigAttribEncPackedHeaders; + context->config_attrib[context->config_attrib_num].value = + VA_ENC_PACKED_HEADER_NONE; + + if (tmp & VA_ENC_PACKED_HEADER_SEQUENCE) { + RTC_LOG(LS_INFO) << "Support packed sequence headers"; + context->config_attrib[context->config_attrib_num].value |= + VA_ENC_PACKED_HEADER_SEQUENCE; + } + + if (tmp & VA_ENC_PACKED_HEADER_PICTURE) { + RTC_LOG(LS_INFO) << "Support packed picture headers"; + context->config_attrib[context->config_attrib_num].value |= + VA_ENC_PACKED_HEADER_PICTURE; + } + + if (tmp & VA_ENC_PACKED_HEADER_SLICE) { + RTC_LOG(LS_INFO) << "Support packed slice headers"; + context->config_attrib[context->config_attrib_num].value |= + VA_ENC_PACKED_HEADER_SLICE; + } + + if (tmp & VA_ENC_PACKED_HEADER_MISC) { + RTC_LOG(LS_INFO) << "Support packed misc headers"; + context->config_attrib[context->config_attrib_num].value |= + VA_ENC_PACKED_HEADER_MISC; + } + + context->enc_packed_header_idx = context->config_attrib_num; + context->config_attrib_num++; + } + + if (context->attrib[VAConfigAttribEncInterlaced].value != + VA_ATTRIB_NOT_SUPPORTED) { + int tmp = context->attrib[VAConfigAttribEncInterlaced].value; + + RTC_LOG(LS_INFO) << "Support VAConfigAttribEncInterlaced: "; + + if (tmp & VA_ENC_INTERLACED_FRAME) + RTC_LOG(LS_INFO) << "Support VA_ENC_INTERLACED_FRAME"; + if (tmp & VA_ENC_INTERLACED_FIELD) + RTC_LOG(LS_INFO) << "Support VA_ENC_INTERLACED_FIELD"; + if (tmp & VA_ENC_INTERLACED_MBAFF) + RTC_LOG(LS_INFO) << "Support VA_ENC_INTERLACED_MBAFF"; + if (tmp & VA_ENC_INTERLACED_PAFF) + RTC_LOG(LS_INFO) << "Support VA_ENC_INTERLACED_PAFF"; + + context->config_attrib[context->config_attrib_num].type = + VAConfigAttribEncInterlaced; + context->config_attrib[context->config_attrib_num].value = + VA_ENC_PACKED_HEADER_NONE; + context->config_attrib_num++; + } + + if (context->attrib[VAConfigAttribEncMaxRefFrames].value != + VA_ATTRIB_NOT_SUPPORTED) { + context->h264_maxref = context->attrib[VAConfigAttribEncMaxRefFrames].value; + + RTC_LOG(LS_INFO) << "Support " << (context->h264_maxref & 0xffff) + << " RefPicList0 and " + << ((context->h264_maxref >> 16) & 0xffff) + << " RefPicList1"; + } + + if (context->attrib[VAConfigAttribEncMaxSlices].value != + VA_ATTRIB_NOT_SUPPORTED) + + RTC_LOG(LS_INFO) << "Support " + << context->attrib[VAConfigAttribEncMaxSlices].value + << " slices"; + + if (context->attrib[VAConfigAttribEncSliceStructure].value != + VA_ATTRIB_NOT_SUPPORTED) { + int tmp = context->attrib[VAConfigAttribEncSliceStructure].value; + + RTC_LOG(LS_INFO) << "Support VAConfigAttribEncSliceStructure: "; + + RTC_LOG(LS_INFO) << "Support VAConfigAttribEncSliceStructure"; + + if (tmp & VA_ENC_SLICE_STRUCTURE_ARBITRARY_ROWS) + RTC_LOG(LS_INFO) << "Support VA_ENC_SLICE_STRUCTURE_ARBITRARY_ROWS"; + if (tmp & VA_ENC_SLICE_STRUCTURE_POWER_OF_TWO_ROWS) + RTC_LOG(LS_INFO) << "Support VA_ENC_SLICE_STRUCTURE_POWER_OF_TWO_ROWS"; + if (tmp & VA_ENC_SLICE_STRUCTURE_ARBITRARY_MACROBLOCKS) + RTC_LOG(LS_INFO) + << "Support VA_ENC_SLICE_STRUCTURE_ARBITRARY_MACROBLOCKS"; + } + if (context->attrib[VAConfigAttribEncMacroblockInfo].value != + VA_ATTRIB_NOT_SUPPORTED) { + RTC_LOG(LS_INFO) << "Support VAConfigAttribEncMacroblockInfo"; + } + + delete[] entrypoints; + + return 0; +} + +static int setup_encode(VA264Context* context) { + VAStatus va_status; + VASurfaceID* tmp_surfaceid; + int codedbuf_size, i; + + va_status = vaCreateConfig(context->va_dpy, context->config.h264_profile, + (VAEntrypoint)context->selected_entrypoint, + &context->config_attrib[0], + context->config_attrib_num, &context->config_id); + + if (context->config_id == VA_INVALID_ID) { + RTC_LOG(LS_ERROR) << "vaCreateConfig failed va_status = " << va_status; + return -1; + } + + /* create source surfaces */ + va_status = vaCreateSurfaces(context->va_dpy, VA_RT_FORMAT_YUV420, + context->frame_width_mbaligned, + context->frame_height_mbaligned, + &context->src_surface[0], SURFACE_NUM, NULL, 0); + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaCreateSurfaces failed va_status = " << va_status; + return -1; + } + + /* create reference surfaces */ + va_status = vaCreateSurfaces(context->va_dpy, VA_RT_FORMAT_YUV420, + context->frame_width_mbaligned, + context->frame_height_mbaligned, + &context->ref_surface[0], SURFACE_NUM, NULL, 0); + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaCreateSurfaces failed va_status = " << va_status; + return -1; + } + + tmp_surfaceid = new VASurfaceID[2 * SURFACE_NUM]; + assert(tmp_surfaceid); + memcpy(tmp_surfaceid, context->src_surface, + SURFACE_NUM * sizeof(VASurfaceID)); + memcpy(tmp_surfaceid + SURFACE_NUM, context->ref_surface, + SURFACE_NUM * sizeof(VASurfaceID)); + + /* Create a context for this encode pipe */ + va_status = vaCreateContext( + context->va_dpy, context->config_id, context->frame_width_mbaligned, + context->frame_height_mbaligned, VA_PROGRESSIVE, tmp_surfaceid, + 2 * SURFACE_NUM, &context->context_id); + + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaCreateContext failed va_status = " << va_status; + delete[] tmp_surfaceid; + return -1; + } + + delete[] tmp_surfaceid; + + codedbuf_size = + (context->frame_width_mbaligned * context->frame_height_mbaligned * 400) / + (16 * 16); + + for (i = 0; i < SURFACE_NUM; i++) { + /* create coded buffer once for all + * other VA buffers which won't be used again after vaRenderPicture. + * so APP can always vaCreateBuffer for every frame + * but coded buffer need to be mapped and accessed after + * vaRenderPicture/vaEndPicture so VA won't maintain the coded buffer + */ + va_status = vaCreateBuffer(context->va_dpy, context->context_id, + VAEncCodedBufferType, codedbuf_size, 1, NULL, + &context->coded_buf[i]); + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaCreateBuffer failed va_status = " << va_status; + return -1; + } + } + + return 0; +} + +#define partition(ref, field, key, ascending) \ + while (i <= j) { \ + if (ascending) { \ + while (ref[i].field < key) \ + i++; \ + while (ref[j].field > key) \ + j--; \ + } else { \ + while (ref[i].field > key) \ + i++; \ + while (ref[j].field < key) \ + j--; \ + } \ + if (i <= j) { \ + tmp = ref[i]; \ + ref[i] = ref[j]; \ + ref[j] = tmp; \ + i++; \ + j--; \ + } \ + } + +static void sort_one(VAPictureH264 ref[], + int left, + int right, + int ascending, + int frame_idx) { + int i = left, j = right; + uint32_t key; + VAPictureH264 tmp; + + if (frame_idx) { + key = ref[(left + right) / 2].frame_idx; + partition(ref, frame_idx, key, ascending); + } else { + key = ref[(left + right) / 2].TopFieldOrderCnt; + partition(ref, TopFieldOrderCnt, (signed int)key, ascending); + } + + /* recursion */ + if (left < j) + sort_one(ref, left, j, ascending, frame_idx); + + if (i < right) + sort_one(ref, i, right, ascending, frame_idx); +} + +static void sort_two(VAPictureH264 ref[], + int left, + int right, + uint32_t key, + uint32_t frame_idx, + int partition_ascending, + int list0_ascending, + int list1_ascending) { + int i = left, j = right; + VAPictureH264 tmp; + + if (frame_idx) { + partition(ref, frame_idx, key, partition_ascending); + } else { + partition(ref, TopFieldOrderCnt, (signed int)key, partition_ascending); + } + + sort_one(ref, left, i - 1, list0_ascending, frame_idx); + sort_one(ref, j + 1, right, list1_ascending, frame_idx); +} + +static int update_ReferenceFrames(VA264Context* context) { + int i; + + if (context->current_frame_type == FRAME_B) + return 0; + + context->current_curr_pic.flags = VA_PICTURE_H264_SHORT_TERM_REFERENCE; + context->num_short_term++; + if (context->num_short_term > num_ref_frames) + context->num_short_term = num_ref_frames; + for (i = context->num_short_term - 1; i > 0; i--) + context->reference_frames[i] = context->reference_frames[i - 1]; + context->reference_frames[0] = context->current_curr_pic; + + if (context->current_frame_type != FRAME_B) + context->current_frame_num++; + if (context->current_frame_num > MaxFrameNum) + context->current_frame_num = 0; + + return 0; +} + +static int update_RefPicList(VA264Context* context) { + uint32_t current_poc = context->current_curr_pic.TopFieldOrderCnt; + + if (context->current_frame_type == FRAME_IDR) { + // per issue 1189 in Intel Media Driver: + // https://github.com/intel/media-driver/issues/1189 + // For the start of each IDR, reset ALL the reference pic lists to invalid + uint32_t flags = VA_PICTURE_H264_INVALID; + for (int i = 0; i < SURFACE_NUM * 2; i++) { + context->slice_param.RefPicList0[i].flags = flags; + context->slice_param.RefPicList1[i].flags = flags; + context->ref_pic_list0_p[i].flags = flags; + context->ref_pic_list0_b[i].flags = flags; + context->ref_pic_list1_b[i].flags = flags; + context->slice_param.RefPicList1[i].picture_id = VA_INVALID_SURFACE; + context->slice_param.RefPicList0[i].picture_id = VA_INVALID_SURFACE; + context->ref_pic_list0_p[i].picture_id = VA_INVALID_SURFACE; + context->ref_pic_list0_b[i].picture_id = VA_INVALID_SURFACE; + context->ref_pic_list1_b[i].picture_id = VA_INVALID_SURFACE; + } + + for (int i = 0; i < SURFACE_NUM; i++) { + context->reference_frames[i].picture_id = VA_INVALID_SURFACE; + context->reference_frames[i].flags = flags; + } + } + + if (context->current_frame_type == FRAME_P) { + memcpy(context->ref_pic_list0_p, context->reference_frames, + context->num_short_term * sizeof(VAPictureH264)); + sort_one(context->ref_pic_list0_p, 0, context->num_short_term - 1, 0, 1); + } + + if (context->current_frame_type == FRAME_B) { + memcpy(context->ref_pic_list0_b, context->reference_frames, + context->num_short_term * sizeof(VAPictureH264)); + sort_two(context->ref_pic_list0_b, 0, context->num_short_term - 1, + current_poc, 0, 1, 0, 1); + + memcpy(context->ref_pic_list1_b, context->reference_frames, + context->num_short_term * sizeof(VAPictureH264)); + sort_two(context->ref_pic_list1_b, 0, context->num_short_term - 1, + current_poc, 0, 0, 1, 0); + } + + return 0; +} + +template +VAEncMiscParam& AllocateMiscParameterBuffer( + std::vector& misc_buffer, + VAEncMiscParameterType misc_param_type) { + constexpr size_t buffer_size = + sizeof(VAEncMiscParameterBuffer) + sizeof(VAEncMiscParam); + misc_buffer.resize(buffer_size); + auto* va_buffer = + reinterpret_cast(misc_buffer.data()); + va_buffer->type = misc_param_type; + return *reinterpret_cast(va_buffer->data); +} + +void CreateVAEncRateControlParams(uint32_t bps, + uint32_t target_percentage, + uint32_t window_size, + uint32_t initial_qp, + uint32_t min_qp, + uint32_t max_qp, + uint32_t framerate, + uint32_t buffer_size, + std::vector misc_buffers[3]) { + auto& rate_control_param = + AllocateMiscParameterBuffer( + misc_buffers[0], VAEncMiscParameterTypeRateControl); + rate_control_param.bits_per_second = bps; + rate_control_param.target_percentage = target_percentage; + rate_control_param.window_size = window_size; + rate_control_param.initial_qp = initial_qp; + rate_control_param.min_qp = min_qp; + rate_control_param.max_qp = max_qp; + rate_control_param.rc_flags.bits.disable_frame_skip = true; + + auto& framerate_param = + AllocateMiscParameterBuffer( + misc_buffers[1], VAEncMiscParameterTypeFrameRate); + framerate_param.framerate = framerate; + + auto& hrd_param = AllocateMiscParameterBuffer( + misc_buffers[2], VAEncMiscParameterTypeHRD); + hrd_param.buffer_size = buffer_size; + hrd_param.initial_buffer_fullness = buffer_size / 2; +} + +static int render_sequence(VA264Context* context) { + VABufferID seq_param_buf, rc_param_buf, misc_param_tmpbuf, render_id[2]; + VAStatus va_status; + VAEncMiscParameterBuffer *misc_param, *misc_param_tmp; + VAEncMiscParameterRateControl* misc_rate_ctrl; + + context->seq_param.level_idc = 41 /*SH_LEVEL_3*/; + context->seq_param.picture_width_in_mbs = context->frame_width_mbaligned / 16; + context->seq_param.picture_height_in_mbs = + context->frame_height_mbaligned / 16; + context->seq_param.bits_per_second = context->config.bitrate; + + context->seq_param.intra_period = context->config.intra_period; + context->seq_param.intra_idr_period = context->config.intra_idr_period; + context->seq_param.ip_period = context->config.ip_period; + + context->seq_param.max_num_ref_frames = num_ref_frames; + context->seq_param.seq_fields.bits.frame_mbs_only_flag = 1; + context->seq_param.time_scale = 900; + context->seq_param.num_units_in_tick = + 15; /* Tc = num_units_in_tick / time_sacle */ + context->seq_param.seq_fields.bits.log2_max_pic_order_cnt_lsb_minus4 = + Log2MaxPicOrderCntLsb - 4; + context->seq_param.seq_fields.bits.log2_max_frame_num_minus4 = + Log2MaxFrameNum - 4; + context->seq_param.seq_fields.bits.frame_mbs_only_flag = 1; + context->seq_param.seq_fields.bits.chroma_format_idc = 1; + context->seq_param.seq_fields.bits.direct_8x8_inference_flag = 1; + + if (context->config.frame_width != context->frame_width_mbaligned || + context->config.frame_height != context->frame_height_mbaligned) { + context->seq_param.frame_cropping_flag = 1; + context->seq_param.frame_crop_left_offset = 0; + context->seq_param.frame_crop_right_offset = + (context->frame_width_mbaligned - context->config.frame_width) / 2; + context->seq_param.frame_crop_top_offset = 0; + context->seq_param.frame_crop_bottom_offset = + (context->frame_height_mbaligned - context->config.frame_height) / 2; + } + + va_status = vaCreateBuffer( + context->va_dpy, context->context_id, VAEncSequenceParameterBufferType, + sizeof(context->seq_param), 1, &context->seq_param, &seq_param_buf); + + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaCreateBuffer failed va_status = " << va_status; + return -1; + } + + va_status = vaCreateBuffer( + context->va_dpy, context->context_id, VAEncMiscParameterBufferType, + sizeof(VAEncMiscParameterBuffer) + sizeof(VAEncMiscParameterRateControl), + 1, NULL, &rc_param_buf); + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaCreateBuffer failed va_status = " << va_status; + return -1; + } + + vaMapBuffer(context->va_dpy, rc_param_buf, (void**)&misc_param); + misc_param->type = VAEncMiscParameterTypeRateControl; + misc_rate_ctrl = (VAEncMiscParameterRateControl*)misc_param->data; + memset(misc_rate_ctrl, 0, sizeof(*misc_rate_ctrl)); + misc_rate_ctrl->bits_per_second = context->config.bitrate; + misc_rate_ctrl->target_percentage = 66; + misc_rate_ctrl->window_size = 1000; + misc_rate_ctrl->initial_qp = context->config.initial_qp; + misc_rate_ctrl->min_qp = context->config.minimal_qp; + misc_rate_ctrl->basic_unit_size = 0; + vaUnmapBuffer(context->va_dpy, rc_param_buf); + + render_id[0] = seq_param_buf; + render_id[1] = rc_param_buf; + + va_status = + vaRenderPicture(context->va_dpy, context->context_id, &render_id[0], 2); + + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaRenderPicture failed va_status = " << va_status; + return -1; + } + + return 0; +} + +std::map frame_type_map = {{FRAME_P, "P"}, + {FRAME_B, "B"}, + {FRAME_I, "I"}, + {FRAME_IDR, "IDR"}}; + +static std::string frametype_to_string(int ftype) { + auto it = frame_type_map.find(ftype); + if (it != frame_type_map.end()) { + return it->second; + } + return "Unknown"; +} + +static int calc_poc(VA264Context* context, int pic_order_cnt_lsb) { + static int PicOrderCntMsb_ref = 0, pic_order_cnt_lsb_ref = 0; + int prevPicOrderCntMsb, prevPicOrderCntLsb; + int PicOrderCntMsb, TopFieldOrderCnt; + + if (context->current_frame_type == FRAME_IDR) + prevPicOrderCntMsb = prevPicOrderCntLsb = 0; + else { + prevPicOrderCntMsb = PicOrderCntMsb_ref; + prevPicOrderCntLsb = pic_order_cnt_lsb_ref; + } + + if ((pic_order_cnt_lsb < prevPicOrderCntLsb) && + ((prevPicOrderCntLsb - pic_order_cnt_lsb) >= + (int)(MaxPicOrderCntLsb / 2))) + PicOrderCntMsb = prevPicOrderCntMsb + MaxPicOrderCntLsb; + else if ((pic_order_cnt_lsb > prevPicOrderCntLsb) && + ((pic_order_cnt_lsb - prevPicOrderCntLsb) > + (int)(MaxPicOrderCntLsb / 2))) + PicOrderCntMsb = prevPicOrderCntMsb - MaxPicOrderCntLsb; + else + PicOrderCntMsb = prevPicOrderCntMsb; + + TopFieldOrderCnt = PicOrderCntMsb + pic_order_cnt_lsb; + + if (context->current_frame_type != FRAME_B) { + PicOrderCntMsb_ref = PicOrderCntMsb; + pic_order_cnt_lsb_ref = pic_order_cnt_lsb; + } + + return TopFieldOrderCnt; +} + +static int render_picture(VA264Context* context) { + VABufferID pic_param_buf; + VAStatus va_status; + int i = 0; + + context->pic_param.CurrPic.picture_id = + context->ref_surface[(context->current_frame_display % SURFACE_NUM)]; + context->pic_param.CurrPic.frame_idx = context->current_frame_num; + context->pic_param.CurrPic.flags = 0; + context->pic_param.CurrPic.TopFieldOrderCnt = calc_poc( + context, (context->current_frame_display - context->current_idr_display) % + MaxPicOrderCntLsb); + context->pic_param.CurrPic.BottomFieldOrderCnt = + context->pic_param.CurrPic.TopFieldOrderCnt; + context->current_curr_pic = context->pic_param.CurrPic; + + memcpy(context->pic_param.ReferenceFrames, context->reference_frames, + context->num_short_term * sizeof(VAPictureH264)); + for (i = context->num_short_term; i < SURFACE_NUM; i++) { + context->pic_param.ReferenceFrames[i].picture_id = VA_INVALID_SURFACE; + context->pic_param.ReferenceFrames[i].flags = VA_PICTURE_H264_INVALID; + } + + context->pic_param.pic_fields.bits.idr_pic_flag = + (context->current_frame_type == FRAME_IDR); + context->pic_param.pic_fields.bits.reference_pic_flag = + (context->current_frame_type != FRAME_B); + context->pic_param.pic_fields.bits.entropy_coding_mode_flag = + context->config.h264_entropy_mode; + context->pic_param.pic_fields.bits.deblocking_filter_control_present_flag = 1; + context->pic_param.frame_num = context->current_frame_num; + context->pic_param.coded_buf = + context->coded_buf[(context->current_frame_display % SURFACE_NUM)]; + context->pic_param.last_picture = + 0; // (context->current_frame_encoding == frame_count); + context->pic_param.pic_init_qp = context->config.initial_qp; + + va_status = vaCreateBuffer( + context->va_dpy, context->context_id, VAEncPictureParameterBufferType, + sizeof(context->pic_param), 1, &context->pic_param, &pic_param_buf); + + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaCreateBuffer failed va_status = " << va_status; + return -1; + } + + va_status = + vaRenderPicture(context->va_dpy, context->context_id, &pic_param_buf, 1); + + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaRenderPicture failed va_status = " << va_status; + return -1; + } + + return 0; +} + +static int render_packedsequence(VA264Context* context) { + VAEncPackedHeaderParameterBuffer packedheader_param_buffer; + VABufferID packedseq_para_bufid, packedseq_data_bufid, render_id[2]; + uint32_t length_in_bits; + unsigned char* packedseq_buffer = NULL; + VAStatus va_status; + + length_in_bits = build_packed_seq_buffer(context, &packedseq_buffer); + + packedheader_param_buffer.type = VAEncPackedHeaderSequence; + + packedheader_param_buffer.bit_length = length_in_bits; /*length_in_bits*/ + packedheader_param_buffer.has_emulation_bytes = 0; + va_status = vaCreateBuffer(context->va_dpy, context->context_id, + VAEncPackedHeaderParameterBufferType, + sizeof(packedheader_param_buffer), 1, + &packedheader_param_buffer, &packedseq_para_bufid); + + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaCreateBuffer failed va_status = " << va_status; + return -1; + } + + va_status = vaCreateBuffer( + context->va_dpy, context->context_id, VAEncPackedHeaderDataBufferType, + (length_in_bits + 7) / 8, 1, packedseq_buffer, &packedseq_data_bufid); + + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaCreateBuffer failed va_status = " << va_status; + return -1; + } + + render_id[0] = packedseq_para_bufid; + render_id[1] = packedseq_data_bufid; + va_status = + vaRenderPicture(context->va_dpy, context->context_id, render_id, 2); + + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaRenderPicture failed va_status = " << va_status; + return -1; + } + + free(packedseq_buffer); + + return 0; +} + +static int render_packedpicture(VA264Context* context) { + VAEncPackedHeaderParameterBuffer packedheader_param_buffer; + VABufferID packedpic_para_bufid, packedpic_data_bufid, render_id[2]; + uint32_t length_in_bits; + unsigned char* packedpic_buffer = NULL; + VAStatus va_status; + + length_in_bits = build_packed_pic_buffer(context, &packedpic_buffer); + packedheader_param_buffer.type = VAEncPackedHeaderPicture; + packedheader_param_buffer.bit_length = length_in_bits; + packedheader_param_buffer.has_emulation_bytes = 0; + + va_status = vaCreateBuffer(context->va_dpy, context->context_id, + VAEncPackedHeaderParameterBufferType, + sizeof(packedheader_param_buffer), 1, + &packedheader_param_buffer, &packedpic_para_bufid); + + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaCreateBuffer failed va_status = " << va_status; + return -1; + } + + va_status = vaCreateBuffer( + context->va_dpy, context->context_id, VAEncPackedHeaderDataBufferType, + (length_in_bits + 7) / 8, 1, packedpic_buffer, &packedpic_data_bufid); + + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaCreateBuffer failed va_status = " << va_status; + return -1; + } + render_id[0] = packedpic_para_bufid; + render_id[1] = packedpic_data_bufid; + va_status = + vaRenderPicture(context->va_dpy, context->context_id, render_id, 2); + + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaRenderPicture failed va_status = " << va_status; + return -1; + } + free(packedpic_buffer); + + return 0; +} + +static void render_packedslice(VA264Context* context) { + VAEncPackedHeaderParameterBuffer packedheader_param_buffer; + VABufferID packedslice_para_bufid, packedslice_data_bufid, render_id[2]; + uint32_t length_in_bits; + unsigned char* packedslice_buffer = NULL; + VAStatus va_status; + + length_in_bits = build_packed_slice_buffer(context, &packedslice_buffer); + packedheader_param_buffer.type = VAEncPackedHeaderSlice; + packedheader_param_buffer.bit_length = length_in_bits; + packedheader_param_buffer.has_emulation_bytes = 0; + + va_status = vaCreateBuffer( + context->va_dpy, context->context_id, + VAEncPackedHeaderParameterBufferType, sizeof(packedheader_param_buffer), + 1, &packedheader_param_buffer, &packedslice_para_bufid); + + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaCreateBuffer failed va_status = " << va_status; + return; + } + + va_status = vaCreateBuffer( + context->va_dpy, context->context_id, VAEncPackedHeaderDataBufferType, + (length_in_bits + 7) / 8, 1, packedslice_buffer, &packedslice_data_bufid); + + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaCreateBuffer failed va_status = " << va_status; + return; + } + render_id[0] = packedslice_para_bufid; + render_id[1] = packedslice_data_bufid; + va_status = + vaRenderPicture(context->va_dpy, context->context_id, render_id, 2); + + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaRenderPicture failed va_status = " << va_status; + return; + } + free(packedslice_buffer); +} + +static int render_slice(VA264Context* context) { + VABufferID slice_param_buf; + VAStatus va_status; + int i; + + update_RefPicList(context); + + /* one frame, one slice */ + context->slice_param.macroblock_address = 0; + context->slice_param.num_macroblocks = context->frame_width_mbaligned * + context->frame_height_mbaligned / + (16 * 16); /* Measured by MB */ + context->slice_param.slice_type = (context->current_frame_type == FRAME_IDR) + ? 2 + : context->current_frame_type; + if (context->current_frame_type == FRAME_IDR) { + if (context->current_frame_encoding != 0) + ++context->slice_param.idr_pic_id; + } else if (context->current_frame_type == FRAME_P) { + int refpiclist0_max = context->h264_maxref & 0xffff; + memcpy(context->slice_param.RefPicList0, context->ref_pic_list0_p, + ((refpiclist0_max > 32) ? 32 : refpiclist0_max) * + sizeof(VAPictureH264)); + + for (i = refpiclist0_max; i < 32; i++) { + context->slice_param.RefPicList0[i].picture_id = VA_INVALID_SURFACE; + context->slice_param.RefPicList0[i].flags = VA_PICTURE_H264_INVALID; + } + } else if (context->current_frame_type == FRAME_B) { + int refpiclist0_max = context->h264_maxref & 0xffff; + int refpiclist1_max = (context->h264_maxref >> 16) & 0xffff; + + memcpy(context->slice_param.RefPicList0, context->ref_pic_list0_b, + ((refpiclist0_max > 32) ? 32 : refpiclist0_max) * + sizeof(VAPictureH264)); + for (i = refpiclist0_max; i < 32; i++) { + context->slice_param.RefPicList0[i].picture_id = VA_INVALID_SURFACE; + context->slice_param.RefPicList0[i].flags = VA_PICTURE_H264_INVALID; + } + + memcpy(context->slice_param.RefPicList1, context->ref_pic_list1_b, + ((refpiclist1_max > 32) ? 32 : refpiclist1_max) * + sizeof(VAPictureH264)); + for (i = refpiclist1_max; i < 32; i++) { + context->slice_param.RefPicList1[i].picture_id = VA_INVALID_SURFACE; + context->slice_param.RefPicList1[i].flags = VA_PICTURE_H264_INVALID; + } + } + + context->slice_param.slice_alpha_c0_offset_div2 = 0; + context->slice_param.slice_beta_offset_div2 = 0; + context->slice_param.direct_spatial_mv_pred_flag = 1; + context->slice_param.pic_order_cnt_lsb = + (context->current_frame_display - context->current_idr_display) % + MaxPicOrderCntLsb; + + if (context->h264_packedheader && + context->config_attrib[context->enc_packed_header_idx].value & + VA_ENC_PACKED_HEADER_SLICE) + render_packedslice(context); + + va_status = vaCreateBuffer( + context->va_dpy, context->context_id, VAEncSliceParameterBufferType, + sizeof(context->slice_param), 1, &context->slice_param, &slice_param_buf); + + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaCreateBuffer failed va_status = " << va_status; + return -1; + } + + va_status = vaRenderPicture(context->va_dpy, context->context_id, + &slice_param_buf, 1); + + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaRenderPicture failed va_status = " << va_status; + return -1; + } + + return 0; +} + +namespace livekit_ffi { + +VaapiH264EncoderWrapper::VaapiH264EncoderWrapper() + : va_display_(std::make_unique()) { + context_ = std::make_unique(); + memset((void*)context_.get(), 0, sizeof(VA264Context)); +} + +VaapiH264EncoderWrapper::~VaapiH264EncoderWrapper() {} + +void VaapiH264EncoderWrapper::Destroy() { + if (context_->va_dpy) { + vaDestroySurfaces(context_->va_dpy, &context_->src_surface[0], SURFACE_NUM); + vaDestroySurfaces(context_->va_dpy, &context_->ref_surface[0], SURFACE_NUM); + } + + if (context_->encoded_buffer) { + free(context_->encoded_buffer); + context_->encoded_buffer = nullptr; + } + + for (int i = 0; i < SURFACE_NUM; i++) { + vaDestroyBuffer(context_->va_dpy, context_->coded_buf[i]); + } + + vaDestroyContext(context_->va_dpy, context_->context_id); + vaDestroyConfig(context_->va_dpy, context_->config_id); + + if (va_display_->isOpen()) { + vaTerminate(va_display_->display()); + va_display_->Close(); + } + + context_->va_dpy = nullptr; + context_->context_id = VA_INVALID_ID; + memset((void*)context_.get(), 0, sizeof(VA264Context)); + initialized_ = false; +} + +bool VaapiH264EncoderWrapper::Initialize(int width, + int height, + int bitrate, + int intra_period, + int idr_period, + int ip_period, + int frame_rate, + VAProfile profile, + int rc_mode) { + context_->config.h264_entropy_mode = 1; // cabac + context_->config.frame_width = width; + context_->config.frame_height = height; + context_->config.frame_rate = frame_rate; + context_->config.bitrate = bitrate; + context_->config.initial_qp = 26; + context_->config.minimal_qp = 0; + context_->config.intra_period = intra_period; + context_->config.intra_idr_period = idr_period; + context_->config.ip_period = ip_period; + context_->config.rc_mode = rc_mode; + context_->h264_maxref = (1 << 16 | 1); + context_->requested_entrypoint = context_->selected_entrypoint = -1; + + if (context_->config.ip_period < 1) { + RTC_LOG(LS_WARNING) << "ip_period must be greater than 0"; + return false; + } + if (context_->config.intra_period != 1 && + context_->config.intra_period % context_->config.ip_period != 0) { + RTC_LOG(LS_WARNING) << "intra_period must be a multiplier of ip_period"; + return false; + } + if (context_->config.intra_period != 0 && + context_->config.intra_idr_period % context_->config.intra_period != 0) { + RTC_LOG(LS_WARNING) + << "intra_idr_period must be a multiplier of intra_period"; + return false; + } + + if (context_->config.bitrate == 0) { + context_->config.bitrate = context_->config.frame_width * + context_->config.frame_height * 12 * + context_->config.frame_rate / 50; + } + + context_->config.h264_profile = profile; + + context_->frame_width_mbaligned = (context_->config.frame_width + 15) & (~15); + context_->frame_height_mbaligned = + (context_->config.frame_height + 15) & (~15); + if (context_->config.frame_width != context_->frame_width_mbaligned || + context_->config.frame_height != context_->frame_height_mbaligned) { + RTC_LOG(LS_INFO) << "Source frame is " << context_->config.frame_width + << "x" << context_->config.frame_height + << " and will code clip to " + << context_->frame_width_mbaligned << "x" + << context_->frame_height_mbaligned << " with crop"; + } + + // the buffer to receive the encoded frames from encodeImage + context_->encoded_buffer = (uint8_t*)malloc( + context_->frame_width_mbaligned * context_->frame_height_mbaligned * 3); + + if (!va_display_->isOpen()) { + if (!va_display_->Open()) { + free(context_->encoded_buffer); + context_->encoded_buffer = nullptr; + return false; + } + } + + if (init_va(context_.get(), va_display_->display()) != VA_STATUS_SUCCESS) { + free(context_->encoded_buffer); + context_->encoded_buffer = nullptr; + return false; + } + + if (setup_encode(context_.get()) != VA_STATUS_SUCCESS) { + free(context_->encoded_buffer); + context_->encoded_buffer = nullptr; + return false; + } + + // reset sps/pps/slice params + memset(&context_->seq_param, 0, sizeof(context_->seq_param)); + memset(&context_->pic_param, 0, sizeof(context_->pic_param)); + memset(&context_->slice_param, 0, sizeof(context_->slice_param)); + + initialized_ = true; + return true; +} + +bool VaapiH264EncoderWrapper::Encode(int fourcc, + const uint8_t* y, + const uint8_t* u, + const uint8_t* v, + bool forceIDR, + std::vector& encoded) { + if (forceIDR) { + // reset the sequence to start with a new IDR regardless of layout + context_->current_frame_num = context_->current_frame_display = + context_->current_frame_encoding = 0; + } + + uint8_t* output = context_->encoded_buffer; + VASurfaceID surface = + context_->src_surface[context_->current_frame_encoding % SURFACE_NUM]; + int retv = upload_surface_yuv( + context_->va_dpy, surface, fourcc, context_->config.frame_width, + context_->config.frame_height, (uint8_t*)y, (uint8_t*)u, (uint8_t*)v); + + if (retv != 0) { + RTC_LOG(LS_ERROR) << "Failed to upload surface"; + return false; + } + + encoding2display_order( + context_->current_frame_encoding, context_->config.intra_period, + context_->config.intra_idr_period, context_->config.ip_period, + &context_->current_frame_display, &context_->current_frame_type); + + if (context_->current_frame_type == FRAME_IDR) { + context_->num_short_term = 0; + context_->current_frame_num = 0; + context_->current_idr_display = context_->current_frame_display; + } + + VAStatus va_status = vaBeginPicture( + context_->va_dpy, context_->context_id, + context_->src_surface[(context_->current_frame_display % SURFACE_NUM)]); + + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaBeginPicture failed va_status = " << va_status; + return false; + } + + // render sequence and picture parameters + if (context_->current_frame_type == FRAME_IDR) { + render_sequence(context_.get()); + render_picture(context_.get()); + if (context_->h264_packedheader) { + render_packedsequence(context_.get()); + render_packedpicture(context_.get()); + } + } else { + render_picture(context_.get()); + } + render_slice(context_.get()); + + va_status = vaEndPicture(context_->va_dpy, context_->context_id); + + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaEndPicture failed va_status = " << va_status; + return false; + } + va_status = vaSyncSurface( + context_->va_dpy, + context_->src_surface[context_->current_frame_display % SURFACE_NUM]); + + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaSyncSurface failed va_status = " << va_status; + return false; + } + VACodedBufferSegment* buf_list = NULL; + uint32_t coded_size = 0; + + va_status = vaMapBuffer( + context_->va_dpy, + context_->coded_buf[context_->current_frame_display % SURFACE_NUM], + (void**)(&buf_list)); + if (va_status != VA_STATUS_SUCCESS) { + RTC_LOG(LS_ERROR) << "vaMapBuffer failed va_status = " << va_status; + return false; + } + while (buf_list != NULL) { + memcpy(&output[coded_size], buf_list->buf, buf_list->size); + coded_size += buf_list->size; + buf_list = (VACodedBufferSegment*)buf_list->next; + } + + vaUnmapBuffer( + context_->va_dpy, + context_->coded_buf[context_->current_frame_display % SURFACE_NUM]); + + update_ReferenceFrames(context_.get()); + + context_->current_frame_encoding++; + + encoded = std::vector(output, output + coded_size); + return true; +} + +} // namespace livekit_ffi \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_h264_encoder_wrapper.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_h264_encoder_wrapper.h new file mode 100644 index 0000000..69b787f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/vaapi/vaapi_h264_encoder_wrapper.h @@ -0,0 +1,127 @@ +#ifndef VAAPI_H264_ENCODER_WRAPPER_H_ +#define VAAPI_H264_ENCODER_WRAPPER_H_ + +#include +#include +#include +#include + +#include +#include + +#if defined(WIN32) +#include "vaapi_display_win32.h" +using VaapiDisplay = livekit_ffi::VaapiDisplayWin32; +#elif defined(__linux__) +#include "vaapi_display_drm.h" +using VaapiDisplay = livekit_ffi::VaapiDisplayDrm ; +#endif +#define SURFACE_NUM 16 /* 16 surfaces for reference */ + +typedef struct { + // one of: VAProfileH264ConstrainedBaseline, VAProfileH264Main, + // VAProfileH264High + VAProfile h264_profile; + int h264_entropy_mode; + int frame_width; + int frame_height; + int frame_rate; + uint32_t bitrate; + int initial_qp; + int minimal_qp; + int intra_period; + int intra_idr_period; + int ip_period; + int rc_mode; +} VA264Config; + +typedef struct { + VADisplay va_dpy; + + VAConfigAttrib attrib[VAConfigAttribTypeMax]; + VAConfigAttrib config_attrib[VAConfigAttribTypeMax]; + int config_attrib_num; + int enc_packed_header_idx; + VASurfaceID src_surface[SURFACE_NUM]; + VABufferID coded_buf[SURFACE_NUM]; + VASurfaceID ref_surface[SURFACE_NUM]; + VAConfigID config_id; + VAContextID context_id; + VAEncSequenceParameterBufferH264 seq_param; + VAEncPictureParameterBufferH264 pic_param; + VAEncSliceParameterBufferH264 slice_param; + VAPictureH264 current_curr_pic; + VAPictureH264 reference_frames[SURFACE_NUM]; + VAPictureH264 ref_pic_list0_p[SURFACE_NUM * 2]; + VAPictureH264 ref_pic_list0_b[SURFACE_NUM * 2]; + VAPictureH264 ref_pic_list1_b[SURFACE_NUM * 2]; + + // Default entrypoint for Encode + int requested_entrypoint; + int selected_entrypoint; + + uint32_t num_short_term; + int constraint_set_flag; + int h264_packedheader; /* support pack header? */ + int h264_maxref; + int frame_width_mbaligned; + int frame_height_mbaligned; + uint32_t current_frame_num; + int current_frame_type; + uint64_t current_frame_encoding; + uint64_t current_frame_display; + uint64_t current_idr_display; + + uint8_t* encoded_buffer; + VA264Config config; +} VA264Context; + +namespace livekit_ffi { + +class VaapiH264EncoderWrapper { + public: + VaapiH264EncoderWrapper(); + ~VaapiH264EncoderWrapper(); + + // Initialize the encoder with the given parameters. + bool Initialize(int width, + int height, + int bitrate, + int intra_period, + int idr_period, + int ip_period, + int frame_rate, + VAProfile profile, + int rc_mode); + + // Encode a frame and return the encoded data. + bool Encode(int fourcc, + const uint8_t* y, + const uint8_t* u, + const uint8_t* v, + bool forceIDR, + std::vector& output); + + void UpdateRates(int frame_rate, int bitrate) { + if (context_) { + context_->config.frame_rate = frame_rate; + context_->config.bitrate = bitrate; + } + } + + bool IsInitialized() const { + return initialized_; + } + + // Release resources. + void Destroy(); + + private: + std::unique_ptr context_; + std::unique_ptr va_display_; + bool initialized_ = false; +}; + +} // namespace livekit_ffi + +#endif // VAAPI_H264_ENCODER_WRAPPER_H_ diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_decoder_factory.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_decoder_factory.cpp new file mode 100644 index 0000000..7dbb80a --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_decoder_factory.cpp @@ -0,0 +1,149 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/video_decoder_factory.h" + +#include +#include "api/environment/environment.h" +#include "api/video_codecs/av1_profile.h" +#include "api/video_codecs/sdp_video_format.h" +#include "livekit/objc_video_factory.h" +#include "media/base/media_constants.h" +#include "modules/video_coding/codecs/h264/include/h264.h" +#include "modules/video_coding/codecs/vp8/include/vp8.h" +#include "modules/video_coding/codecs/vp9/include/vp9.h" +#include "rtc_base/logging.h" + +#if defined(RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY) +#include "modules/video_coding/codecs/av1/dav1d_decoder.h" // nogncheck +#endif + +#ifdef WEBRTC_ANDROID +#include "livekit/android.h" +#endif + +#if defined(USE_NVIDIA_VIDEO_CODEC) || defined(USE_NVIDIA_VIDEO_DECODER) +#include "nvidia/nvidia_decoder_factory.h" +#endif + +namespace livekit_ffi { + +VideoDecoderFactory::VideoDecoderFactory() { +#ifdef __APPLE__ + factories_.push_back(livekit_ffi::CreateObjCVideoDecoderFactory()); +#endif + +#ifdef WEBRTC_ANDROID + factories_.push_back(CreateAndroidVideoDecoderFactory()); +#endif + +#if defined(USE_NVIDIA_VIDEO_CODEC) || defined(USE_NVIDIA_VIDEO_DECODER) + if (webrtc::NvidiaVideoDecoderFactory::IsSupported()) { + factories_.push_back(std::make_unique()); + } +#endif +} + +std::vector VideoDecoderFactory::GetSupportedFormats() + const { + std::vector formats; + + for (const auto& factory : factories_) { + auto supported_formats = factory->GetSupportedFormats(); + formats.insert(formats.end(), supported_formats.begin(), + supported_formats.end()); + } + + formats.push_back(webrtc::SdpVideoFormat(webrtc::kVp8CodecName)); + for (const webrtc::SdpVideoFormat& format : + webrtc::SupportedVP9DecoderCodecs()) + formats.push_back(format); + for (const webrtc::SdpVideoFormat& h264_format : + webrtc::SupportedH264DecoderCodecs()) + formats.push_back(h264_format); + + formats.push_back(webrtc::SdpVideoFormat( + webrtc::SdpVideoFormat::AV1Profile0(), + webrtc::LibaomAv1EncoderSupportedScalabilityModes())); + return formats; +} + +VideoDecoderFactory::CodecSupport VideoDecoderFactory::QueryCodecSupport( + const webrtc::SdpVideoFormat& format, + bool reference_scaling) const { + if (reference_scaling) { + webrtc::VideoCodecType codec = + webrtc::PayloadStringToCodecType(format.name); + if (codec != webrtc::kVideoCodecVP9 && codec != webrtc::kVideoCodecAV1) { + return {/*is_supported=*/false, /*is_power_efficient=*/false}; + } + } + + CodecSupport codec_support; + codec_support.is_supported = format.IsCodecInList(GetSupportedFormats()); + return codec_support; +} + +std::unique_ptr VideoDecoderFactory::Create( + const webrtc::Environment& env, const webrtc::SdpVideoFormat& format) { + for (const auto& factory : factories_) { + for (const auto& supported_format : factory->GetSupportedFormats()) { + if (supported_format.IsSameCodec(format)) + return factory->Create(env, format); + } + } + + // IsSameCodec treats H.264 packetization-modes as distinct codecs, so when + // the SFU sends mode=0 but the platform factory only advertises mode=1 the + // strict match above fails. Retry with the factory's packetization-mode so + // only that parameter is relaxed while the profile-level-id check is kept. + if (absl::EqualsIgnoreCase(format.name, webrtc::kH264CodecName)) { + for (const auto& factory : factories_) { + for (const auto& sf : factory->GetSupportedFormats()) { + if (!absl::EqualsIgnoreCase(sf.name, webrtc::kH264CodecName)) + continue; + auto adjusted = format; + auto it = sf.parameters.find("packetization-mode"); + if (it != sf.parameters.end()) + adjusted.parameters["packetization-mode"] = it->second; + else + adjusted.parameters.erase("packetization-mode"); + if (sf.IsSameCodec(adjusted)) + return factory->Create(env, adjusted); + } + } + } + + if (absl::EqualsIgnoreCase(format.name, webrtc::kVp8CodecName)) + return webrtc::CreateVp8Decoder(env); + if (absl::EqualsIgnoreCase(format.name, webrtc::kVp9CodecName)) + return webrtc::VP9Decoder::Create(); + if (absl::EqualsIgnoreCase(format.name, webrtc::kH264CodecName)) + return webrtc::H264Decoder::Create(); + + +#if defined(RTC_DAV1D_IN_INTERNAL_DECODER_FACTORY) + if (absl::EqualsIgnoreCase(format.name, webrtc::kAv1CodecName)) { + return webrtc::CreateDav1dDecoder(); + } +#endif + + + RTC_LOG(LS_ERROR) << "No VideoDecoder found for " << format.name; + return nullptr; +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_encoder_factory.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_encoder_factory.cpp new file mode 100644 index 0000000..31f6023 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_encoder_factory.cpp @@ -0,0 +1,237 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/video_encoder_factory.h" + +#include +#include + +#include "api/environment/environment_factory.h" +#include "api/video_codecs/sdp_video_format.h" +#include "api/video_codecs/video_encoder.h" +#include "api/video_codecs/video_encoder_factory_template.h" +#include "livekit/objc_video_factory.h" +#include "media/base/media_constants.h" +#include "media/engine/simulcast_encoder_adapter.h" +#include "rtc_base/logging.h" +#if defined(RTC_USE_LIBAOM_AV1_ENCODER) +#include "api/video_codecs/video_encoder_factory_template_libaom_av1_adapter.h" +#endif +#if defined(WEBRTC_USE_H264) +#include "api/video_codecs/video_encoder_factory_template_open_h264_adapter.h" +#endif +#include "api/video_codecs/video_encoder_factory_template_libvpx_vp8_adapter.h" +#include "api/video_codecs/video_encoder_factory_template_libvpx_vp9_adapter.h" + +#ifdef WEBRTC_ANDROID +#include "livekit/android.h" +#endif + +#if defined(USE_NVIDIA_VIDEO_CODEC) || defined(USE_NVIDIA_VIDEO_ENCODER) +#include "nvidia/nvidia_encoder_factory.h" +#endif + +#if defined(USE_VAAPI_VIDEO_CODEC) +#include "vaapi/vaapi_encoder_factory.h" +#endif + +namespace livekit_ffi { + +namespace { + +constexpr char kPreferredHwEncoderEnv[] = "LIVEKIT_PREFERRED_HW_ENCODER"; + +enum class PreferredHwEncoder { + kNvenc, + kVaapi, +}; + +struct PreferredHwEncoderConfig { + PreferredHwEncoder encoder = PreferredHwEncoder::kNvenc; + bool explicitly_set = false; +}; + +PreferredHwEncoderConfig GetPreferredHwEncoderConfig() { + const char* preferred_encoder = std::getenv(kPreferredHwEncoderEnv); + if (!preferred_encoder) { + return {}; + } + + std::string_view preferred_encoder_view(preferred_encoder); + if (preferred_encoder_view == "nvenc") { + return {PreferredHwEncoder::kNvenc, true}; + } + if (preferred_encoder_view == "vaapi") { + return {PreferredHwEncoder::kVaapi, true}; + } + + RTC_LOG(LS_WARNING) << "Ignoring invalid LIVEKIT_PREFERRED_HW_ENCODER=\"" + << preferred_encoder + << "\"; expected \"nvenc\" or \"vaapi\"."; + return {}; +} + +void AddNvencFactory( + std::vector>& factories, + bool preferred) { +#if defined(USE_NVIDIA_VIDEO_CODEC) || defined(USE_NVIDIA_VIDEO_ENCODER) + if (webrtc::NvidiaVideoEncoderFactory::IsSupported()) { + factories.push_back(std::make_unique()); + return; + } + + if (preferred) { + RTC_LOG(LS_WARNING) + << "LIVEKIT_PREFERRED_HW_ENCODER=nvenc requested, but NVENC " + "is unavailable; falling back to other encoders."; + } +#else + if (preferred) { + RTC_LOG(LS_WARNING) + << "LIVEKIT_PREFERRED_HW_ENCODER=nvenc requested, but NVENC support " + "is not compiled in; falling back to other encoders."; + } +#endif +} + +void AddVaapiFactory( + std::vector>& factories, + bool preferred) { +#if defined(USE_VAAPI_VIDEO_CODEC) + if (webrtc::VAAPIVideoEncoderFactory::IsSupported()) { + factories.push_back(std::make_unique()); + return; + } + + if (preferred) { + RTC_LOG(LS_WARNING) + << "LIVEKIT_PREFERRED_HW_ENCODER=vaapi requested, but VAAPI " + "is unavailable; falling back to other encoders."; + } +#else + if (preferred) { + RTC_LOG(LS_WARNING) + << "LIVEKIT_PREFERRED_HW_ENCODER=vaapi requested, but VAAPI support " + "is not compiled in; falling back to other encoders."; + } +#endif +} + +} // namespace + +using Factory = webrtc::VideoEncoderFactoryTemplate< + webrtc::LibvpxVp8EncoderTemplateAdapter, +#if defined(WEBRTC_USE_H264) + webrtc::OpenH264EncoderTemplateAdapter, +#endif +#if defined(RTC_USE_LIBAOM_AV1_ENCODER) + webrtc::LibaomAv1EncoderTemplateAdapter, +#endif + webrtc::LibvpxVp9EncoderTemplateAdapter>; + +VideoEncoderFactory::InternalFactory::InternalFactory() { +#ifdef __APPLE__ + factories_.push_back(livekit_ffi::CreateObjCVideoEncoderFactory()); +#endif + +#ifdef WEBRTC_ANDROID + factories_.push_back(CreateAndroidVideoEncoderFactory()); +#endif + + const PreferredHwEncoderConfig preferred_hw_encoder = + GetPreferredHwEncoderConfig(); + if (preferred_hw_encoder.encoder == PreferredHwEncoder::kVaapi) { + AddVaapiFactory(factories_, preferred_hw_encoder.explicitly_set); + AddNvencFactory(factories_, false); + } else { + AddNvencFactory(factories_, preferred_hw_encoder.explicitly_set); + AddVaapiFactory(factories_, false); + } +} + +std::vector +VideoEncoderFactory::InternalFactory::GetSupportedFormats() const { + std::vector formats = Factory().GetSupportedFormats(); + + for (const auto& factory : factories_) { + auto supported_formats = factory->GetSupportedFormats(); + formats.insert(formats.end(), supported_formats.begin(), + supported_formats.end()); + } + return formats; +} + +VideoEncoderFactory::CodecSupport +VideoEncoderFactory::InternalFactory::QueryCodecSupport( + const webrtc::SdpVideoFormat& format, + std::optional scalability_mode) const { + auto original_format = + webrtc::FuzzyMatchSdpVideoFormat(Factory().GetSupportedFormats(), format); + return original_format + ? Factory().QueryCodecSupport(*original_format, scalability_mode) + : webrtc::VideoEncoderFactory::CodecSupport{.is_supported = false}; +} + +std::unique_ptr +VideoEncoderFactory::InternalFactory::Create( + const webrtc::Environment& env, + const webrtc::SdpVideoFormat& format) { + for (const auto& factory : factories_) { + for (const auto& supported_format : factory->GetSupportedFormats()) { + if (supported_format.IsSameCodec(format)) + return factory->Create(env, format); + } + } + + auto original_format = + webrtc::FuzzyMatchSdpVideoFormat(Factory().GetSupportedFormats(), format); + + if (original_format) { + return Factory().Create(env, *original_format); + } + + RTC_LOG(LS_ERROR) << "No VideoEncoder found for " << format.name; + return nullptr; +} + +VideoEncoderFactory::VideoEncoderFactory() { + internal_factory_ = std::make_unique(); +} + +std::vector VideoEncoderFactory::GetSupportedFormats() + const { + return internal_factory_->GetSupportedFormats(); +} + +VideoEncoderFactory::CodecSupport VideoEncoderFactory::QueryCodecSupport( + const webrtc::SdpVideoFormat& format, + std::optional scalability_mode) const { + return internal_factory_->QueryCodecSupport(format, scalability_mode); +} + +std::unique_ptr VideoEncoderFactory::Create( + const webrtc::Environment& env, + const webrtc::SdpVideoFormat& format) { + std::unique_ptr encoder; + if (format.IsCodecInList(internal_factory_->GetSupportedFormats())) { + encoder = std::make_unique( + env, internal_factory_.get(), nullptr, format); + } + + return encoder; +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_frame.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_frame.cpp new file mode 100644 index 0000000..dc6d25e --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_frame.cpp @@ -0,0 +1,86 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/video_frame.h" + +#include + +#include "api/video/video_frame.h" + +namespace livekit_ffi { +VideoFrame::VideoFrame(const webrtc::VideoFrame& frame) + : frame_(std::move(frame)) {} + +unsigned int VideoFrame::width() const { + return frame_.width(); +} +unsigned int VideoFrame::height() const { + return frame_.height(); +} +uint32_t VideoFrame::size() const { + return frame_.size(); +} +uint16_t VideoFrame::id() const { + return frame_.id(); +} +int64_t VideoFrame::timestamp_us() const { + return frame_.timestamp_us(); +} +int64_t VideoFrame::ntp_time_ms() const { + return frame_.ntp_time_ms(); +} +uint32_t VideoFrame::timestamp() const { + return frame_.rtp_timestamp(); +} + +VideoRotation VideoFrame::rotation() const { + return static_cast(frame_.rotation()); +} + +// TODO(theomonnom) This shouldn't create a new shared_ptr at each call +std::unique_ptr VideoFrame::video_frame_buffer() const { + return std::make_unique(frame_.video_frame_buffer()); +} + +webrtc::VideoFrame VideoFrame::get() const { + return frame_; +} + +void VideoFrameBuilder::set_video_frame_buffer(const VideoFrameBuffer& buffer) { + builder_.set_video_frame_buffer(buffer.get()); // const & ref_counted +} + +void VideoFrameBuilder::set_timestamp_us(int64_t timestamp_us) { + builder_.set_timestamp_us(timestamp_us); +} + +void VideoFrameBuilder::set_rotation(VideoRotation rotation) { + builder_.set_rotation(static_cast(rotation)); +} + +void VideoFrameBuilder::set_id(uint16_t id) { + builder_.set_id(id); +} + +std::unique_ptr VideoFrameBuilder::build() { + return std::make_unique(builder_.build()); +} + +std::unique_ptr new_video_frame_builder() { + return std::make_unique(); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_frame.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_frame.rs new file mode 100644 index 0000000..afd0382 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_frame.rs @@ -0,0 +1,62 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::impl_thread_safety; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + #[derive(Debug)] + #[repr(i32)] + pub enum VideoRotation { + VideoRotation0 = 0, + VideoRotation90 = 90, + VideoRotation180 = 180, + VideoRotation270 = 270, + } + + extern "C++" { + include!("livekit/video_frame_buffer.h"); + + type VideoFrameBuffer = crate::video_frame_buffer::ffi::VideoFrameBuffer; + } + + unsafe extern "C++" { + include!("livekit/video_frame.h"); + + type VideoFrame; + + fn width(self: &VideoFrame) -> u32; + fn height(self: &VideoFrame) -> u32; + fn size(self: &VideoFrame) -> u32; + fn id(self: &VideoFrame) -> u16; + fn timestamp_us(self: &VideoFrame) -> i64; + fn ntp_time_ms(self: &VideoFrame) -> i64; + fn timestamp(self: &VideoFrame) -> u32; + fn rotation(self: &VideoFrame) -> VideoRotation; + unsafe fn video_frame_buffer(self: &VideoFrame) -> UniquePtr; + + // VideoFrameBuilder + type VideoFrameBuilder; + fn new_video_frame_builder() -> UniquePtr; + fn set_timestamp_us(self: Pin<&mut VideoFrameBuilder>, timestamp_us: i64); + fn set_rotation(self: Pin<&mut VideoFrameBuilder>, rotation: VideoRotation); + fn set_id(self: Pin<&mut VideoFrameBuilder>, id: u16); + fn set_video_frame_buffer(self: Pin<&mut VideoFrameBuilder>, buffer: &VideoFrameBuffer); + + fn build(self: Pin<&mut VideoFrameBuilder>) -> UniquePtr; + + } +} + +impl_thread_safety!(ffi::VideoFrame, Send + Sync); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_frame_buffer.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_frame_buffer.cpp new file mode 100644 index 0000000..86dbedc --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_frame_buffer.cpp @@ -0,0 +1,577 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/video_frame_buffer.h" + +#include "api/make_ref_counted.h" +#include "rtc_base/logging.h" + +#if defined(__linux__) +#include +#endif + +namespace livekit_ffi { + +VideoFrameBuffer::VideoFrameBuffer( + webrtc::scoped_refptr buffer) + : buffer_(std::move(buffer)) {} + +VideoFrameBufferType VideoFrameBuffer::buffer_type() const { + return static_cast(buffer_->type()); +} + +unsigned int VideoFrameBuffer::width() const { + return buffer_->width(); +} + +unsigned int VideoFrameBuffer::height() const { + return buffer_->height(); +} + +std::unique_ptr VideoFrameBuffer::to_i420() const { + return std::make_unique(buffer_->ToI420()); +} + +// const_cast is valid here because we take the ownership on the rust side +std::unique_ptr VideoFrameBuffer::get_i420() { + return std::make_unique( + webrtc::scoped_refptr( + const_cast(buffer_->GetI420()))); +} + +std::unique_ptr VideoFrameBuffer::get_i420a() { + return std::make_unique( + webrtc::scoped_refptr( + const_cast(buffer_->GetI420A()))); +} + +std::unique_ptr VideoFrameBuffer::get_i422() { + return std::make_unique( + webrtc::scoped_refptr( + const_cast(buffer_->GetI422()))); +} + +std::unique_ptr VideoFrameBuffer::get_i444() { + return std::make_unique( + webrtc::scoped_refptr( + const_cast(buffer_->GetI444()))); +} + +std::unique_ptr VideoFrameBuffer::get_i010() { + return std::make_unique( + webrtc::scoped_refptr( + const_cast(buffer_->GetI010()))); +} + +std::unique_ptr VideoFrameBuffer::get_nv12() { + return std::make_unique( + webrtc::scoped_refptr( + const_cast(buffer_->GetNV12()))); +} + +webrtc::scoped_refptr VideoFrameBuffer::get() const { + return buffer_; +} + +FluxerGpuFrameBuffer::FluxerGpuFrameBuffer(uint64_t handle, + uint32_t width, + uint32_t height, + uint32_t dxgi_format) + : kind_(Kind::kD3D11Texture), + width_(width), + height_(height), + d3d11_handle_(handle), + dxgi_format_(dxgi_format) {} + +FluxerGpuFrameBuffer::FluxerGpuFrameBuffer(int fd0, + int fd1, + int fd2, + int fd3, + uint32_t plane_count, + uint32_t width, + uint32_t height, + uint32_t drm_format, + uint64_t modifier, + uint32_t stride0, + uint32_t stride1, + uint32_t stride2, + uint32_t stride3, + uint32_t offset0, + uint32_t offset1, + uint32_t offset2, + uint32_t offset3, + uint64_t device_uuid_hi, + uint64_t device_uuid_lo) + : kind_(Kind::kDmaBuf), + width_(width), + height_(height), + plane_count_(plane_count), + drm_format_(drm_format), + modifier_(modifier), + device_uuid_hi_(device_uuid_hi), + device_uuid_lo_(device_uuid_lo) { + const int input_fds[4] = {fd0, fd1, fd2, fd3}; + const uint32_t input_strides[4] = {stride0, stride1, stride2, stride3}; + const uint32_t input_offsets[4] = {offset0, offset1, offset2, offset3}; + for (uint32_t plane = 0; plane < plane_count_ && plane < 4; ++plane) { +#if defined(__linux__) + fds_[plane] = input_fds[plane] >= 0 ? dup(input_fds[plane]) : -1; +#else + fds_[plane] = -1; +#endif + strides_[plane] = input_strides[plane]; + offsets_[plane] = input_offsets[plane]; + } +} + +FluxerGpuFrameBuffer::~FluxerGpuFrameBuffer() { +#if defined(__linux__) + for (int& fd : fds_) { + if (fd >= 0) { + close(fd); + fd = -1; + } + } +#endif +} + +webrtc::VideoFrameBuffer::Type FluxerGpuFrameBuffer::type() const { + return Type::kNative; +} + +int FluxerGpuFrameBuffer::width() const { + return static_cast(width_); +} + +int FluxerGpuFrameBuffer::height() const { + return static_cast(height_); +} + +webrtc::scoped_refptr +FluxerGpuFrameBuffer::ToI420() { + RTC_LOG(LS_WARNING) + << "Fluxer GPU frame cannot be CPU-mapped by the WebRTC fallback path"; + return nullptr; +} + +webrtc::scoped_refptr +FluxerGpuFrameBuffer::CropAndScale(int offset_x, + int offset_y, + int crop_width, + int crop_height, + int scaled_width, + int scaled_height) { + RTC_LOG(LS_WARNING) + << "Fluxer GPU frame cannot be cropped or scaled by WebRTC fallback"; + return nullptr; +} + +webrtc::scoped_refptr +FluxerGpuFrameBuffer::GetMappedFrameBuffer(webrtc::ArrayView types) { + return nullptr; +} + +std::string FluxerGpuFrameBuffer::storage_representation() const { + return kind_ == Kind::kD3D11Texture ? "FluxerD3D11Texture" + : "FluxerDmaBufTexture"; +} + +int FluxerGpuFrameBuffer::fd(uint32_t plane) const { + return plane < 4 ? fds_[plane] : -1; +} + +uint32_t FluxerGpuFrameBuffer::stride(uint32_t plane) const { + return plane < 4 ? strides_[plane] : 0; +} + +uint32_t FluxerGpuFrameBuffer::offset(uint32_t plane) const { + return plane < 4 ? offsets_[plane] : 0; +} + +const FluxerGpuFrameBuffer* AsFluxerGpuFrameBuffer( + const webrtc::VideoFrameBuffer* buffer) { + if (!buffer || buffer->type() != webrtc::VideoFrameBuffer::Type::kNative) { + return nullptr; + } + const std::string storage = buffer->storage_representation(); + if (storage != "FluxerD3D11Texture" && storage != "FluxerDmaBufTexture") { + return nullptr; + } + return static_cast(buffer); +} + +PlanarYuvBuffer::PlanarYuvBuffer( + webrtc::scoped_refptr buffer) + : VideoFrameBuffer(buffer) {} + +unsigned int PlanarYuvBuffer::chroma_width() const { + return buffer()->ChromaWidth(); +} + +unsigned int PlanarYuvBuffer::chroma_height() const { + return buffer()->ChromaHeight(); +} + +unsigned int PlanarYuvBuffer::stride_y() const { + return buffer()->StrideY(); +} + +unsigned int PlanarYuvBuffer::stride_u() const { + return buffer()->StrideU(); +} + +unsigned int PlanarYuvBuffer::stride_v() const { + return buffer()->StrideV(); +} + +webrtc::PlanarYuvBuffer* PlanarYuvBuffer::buffer() const { + return static_cast(buffer_.get()); +} + +PlanarYuv8Buffer::PlanarYuv8Buffer( + webrtc::scoped_refptr buffer) + : PlanarYuvBuffer(buffer) {} + +const uint8_t* PlanarYuv8Buffer::data_y() const { + return buffer()->DataY(); +} + +const uint8_t* PlanarYuv8Buffer::data_u() const { + return buffer()->DataU(); +} + +const uint8_t* PlanarYuv8Buffer::data_v() const { + return buffer()->DataV(); +} + +webrtc::PlanarYuv8Buffer* PlanarYuv8Buffer::buffer() const { + return static_cast(buffer_.get()); +} + +PlanarYuv16BBuffer::PlanarYuv16BBuffer( + webrtc::scoped_refptr buffer) + : PlanarYuvBuffer(buffer) {} + +const uint16_t* PlanarYuv16BBuffer::data_y() const { + return buffer()->DataY(); +} + +const uint16_t* PlanarYuv16BBuffer::data_u() const { + return buffer()->DataU(); +} + +const uint16_t* PlanarYuv16BBuffer::data_v() const { + return buffer()->DataV(); +} + +webrtc::PlanarYuv16BBuffer* PlanarYuv16BBuffer::buffer() const { + return static_cast(buffer_.get()); +} + +BiplanarYuvBuffer::BiplanarYuvBuffer( + webrtc::scoped_refptr buffer) + : VideoFrameBuffer(buffer) {} + +unsigned int BiplanarYuvBuffer::chroma_width() const { + return buffer()->ChromaWidth(); +} + +unsigned int BiplanarYuvBuffer::chroma_height() const { + return buffer()->ChromaHeight(); +} + +unsigned int BiplanarYuvBuffer::stride_y() const { + return buffer()->StrideY(); +} + +unsigned int BiplanarYuvBuffer::stride_uv() const { + return buffer()->StrideUV(); +} + +webrtc::BiplanarYuvBuffer* BiplanarYuvBuffer::buffer() const { + return static_cast(buffer_.get()); +} + +BiplanarYuv8Buffer::BiplanarYuv8Buffer( + webrtc::scoped_refptr buffer) + : BiplanarYuvBuffer(buffer) {} + +const uint8_t* BiplanarYuv8Buffer::data_y() const { + return buffer()->DataY(); +} + +const uint8_t* BiplanarYuv8Buffer::data_uv() const { + return buffer()->DataUV(); +} + +webrtc::BiplanarYuv8Buffer* BiplanarYuv8Buffer::buffer() const { + return static_cast(buffer_.get()); +} + +I420Buffer::I420Buffer(webrtc::scoped_refptr buffer) + : PlanarYuv8Buffer(buffer) {} + +webrtc::I420BufferInterface* I420Buffer::buffer() const { + return static_cast(buffer_.get()); +} + +std::unique_ptr I420Buffer::scale(int scaled_width, + int scaled_height) const { + webrtc::scoped_refptr result = + buffer()->Scale(scaled_width, scaled_height); + return std::make_unique( + webrtc::scoped_refptr( + const_cast(result->GetI420()))); +} + +I420ABuffer::I420ABuffer( + webrtc::scoped_refptr buffer) + : I420Buffer(buffer) {} + +unsigned int I420ABuffer::stride_a() const { + return buffer()->StrideA(); +} + +const uint8_t* I420ABuffer::data_a() const { + return buffer()->DataA(); +} + +webrtc::I420ABufferInterface* I420ABuffer::buffer() const { + return static_cast(buffer_.get()); +} + +std::unique_ptr I420ABuffer::scale(int scaled_width, + int scaled_height) const { + webrtc::scoped_refptr result = + buffer()->Scale(scaled_width, scaled_height); + return std::make_unique( + webrtc::scoped_refptr( + const_cast(result->GetI420A()))); +} + +I422Buffer::I422Buffer(webrtc::scoped_refptr buffer) + : PlanarYuv8Buffer(buffer) {} + +webrtc::I422BufferInterface* I422Buffer::buffer() const { + return static_cast(buffer_.get()); +} + +std::unique_ptr I422Buffer::scale(int scaled_width, + int scaled_height) const { + webrtc::scoped_refptr result = + buffer()->Scale(scaled_width, scaled_height); + return std::make_unique( + webrtc::scoped_refptr( + const_cast(result->GetI422()))); +} + +I444Buffer::I444Buffer(webrtc::scoped_refptr buffer) + : PlanarYuv8Buffer(buffer) {} + +webrtc::I444BufferInterface* I444Buffer::buffer() const { + return static_cast(buffer_.get()); +} + +std::unique_ptr I444Buffer::scale(int scaled_width, + int scaled_height) const { + webrtc::scoped_refptr result = + buffer()->Scale(scaled_width, scaled_height); + return std::make_unique( + webrtc::scoped_refptr( + const_cast(result->GetI444()))); +} + +I010Buffer::I010Buffer(webrtc::scoped_refptr buffer) + : PlanarYuv16BBuffer(buffer) {} + +webrtc::I010BufferInterface* I010Buffer::buffer() const { + return static_cast(buffer_.get()); +} + +std::unique_ptr I010Buffer::scale(int scaled_width, + int scaled_height) const { + webrtc::scoped_refptr result = + buffer()->Scale(scaled_width, scaled_height); + return std::make_unique( + webrtc::scoped_refptr( + const_cast(result->GetI010()))); +} + +NV12Buffer::NV12Buffer(webrtc::scoped_refptr buffer) + : BiplanarYuv8Buffer(buffer) {} + +webrtc::NV12BufferInterface* NV12Buffer::buffer() const { + return static_cast(buffer_.get()); +} + +std::unique_ptr NV12Buffer::scale(int scaled_width, + int scaled_height) const { + webrtc::scoped_refptr result = + buffer()->Scale(scaled_width, scaled_height); + return std::make_unique( + webrtc::scoped_refptr( + const_cast(result->GetNV12()))); +} + +std::unique_ptr copy_i420_buffer( + const std::unique_ptr& i420) { + return std::make_unique(webrtc::I420Buffer::Copy(*i420->get())); +} + +std::unique_ptr new_i420_buffer(int width, + int height, + int stride_y, + int stride_u, + int stride_v) { + return std::make_unique( + webrtc::I420Buffer::Create(width, height, stride_y, stride_u, stride_v)); +} + +std::unique_ptr new_i422_buffer(int width, + int height, + int stride_y, + int stride_u, + int stride_v) { + return std::make_unique( + webrtc::I422Buffer::Create(width, height, stride_y, stride_u, stride_v)); +} + +std::unique_ptr new_i444_buffer(int width, + int height, + int stride_y, + int stride_u, + int stride_v) { + return std::make_unique( + webrtc::I444Buffer::Create(width, height, stride_y, stride_u, stride_v)); +} + +std::unique_ptr new_i010_buffer(int width, + int height, + int stride_y, + int stride_u, + int stride_v) { + return std::make_unique(webrtc::make_ref_counted( + width, height, stride_y, stride_u, stride_v)); +} + +std::unique_ptr new_nv12_buffer(int width, + int height, + int stride_y, + int stride_uv) { + return std::make_unique( + webrtc::NV12Buffer::Create(width, height, stride_y, stride_uv)); +} + +std::unique_ptr new_fluxer_d3d11_texture_buffer( + uint64_t handle, + uint32_t width, + uint32_t height, + uint32_t dxgi_format) { + if (handle == 0 || width == 0 || height == 0) { + return nullptr; + } + return std::make_unique( + webrtc::make_ref_counted(handle, width, height, + dxgi_format)); +} + +std::unique_ptr new_fluxer_dmabuf_texture_buffer( + int fd0, + int fd1, + int fd2, + int fd3, + uint32_t plane_count, + uint32_t width, + uint32_t height, + uint32_t drm_format, + uint64_t modifier, + uint32_t stride0, + uint32_t stride1, + uint32_t stride2, + uint32_t stride3, + uint32_t offset0, + uint32_t offset1, + uint32_t offset2, + uint32_t offset3, + uint64_t device_uuid_hi, + uint64_t device_uuid_lo) { + if (plane_count == 0 || plane_count > 4 || width == 0 || height == 0) { + return nullptr; + } + return std::make_unique( + webrtc::make_ref_counted( + fd0, fd1, fd2, fd3, plane_count, width, height, drm_format, + modifier, stride0, stride1, stride2, stride3, offset0, offset1, + offset2, offset3, device_uuid_hi, device_uuid_lo)); +} + +bool is_fluxer_gpu_buffer(const std::unique_ptr& buffer) { + return buffer && AsFluxerGpuFrameBuffer(buffer->get().get()) != nullptr; +} + +uint64_t fluxer_d3d11_texture_handle( + const std::unique_ptr& buffer) { + const FluxerGpuFrameBuffer* gpu = + buffer ? AsFluxerGpuFrameBuffer(buffer->get().get()) : nullptr; + if (!gpu || gpu->kind() != FluxerGpuFrameBuffer::Kind::kD3D11Texture) { + return 0; + } + return gpu->d3d11_handle(); +} + +uint32_t fluxer_gpu_buffer_width( + const std::unique_ptr& buffer) { + const FluxerGpuFrameBuffer* gpu = + buffer ? AsFluxerGpuFrameBuffer(buffer->get().get()) : nullptr; + return gpu ? static_cast(gpu->width()) : 0; +} + +uint32_t fluxer_gpu_buffer_height( + const std::unique_ptr& buffer) { + const FluxerGpuFrameBuffer* gpu = + buffer ? AsFluxerGpuFrameBuffer(buffer->get().get()) : nullptr; + return gpu ? static_cast(gpu->height()) : 0; +} + +uint32_t fluxer_gpu_buffer_format( + const std::unique_ptr& buffer) { + const FluxerGpuFrameBuffer* gpu = + buffer ? AsFluxerGpuFrameBuffer(buffer->get().get()) : nullptr; + if (!gpu) { + return 0; + } + return gpu->kind() == FluxerGpuFrameBuffer::Kind::kD3D11Texture + ? gpu->dxgi_format() + : gpu->drm_format(); +} + +#ifndef __APPLE__ + +std::unique_ptr new_native_buffer_from_platform_image_buffer( + PlatformImageBuffer *buffer +) { + return nullptr; +} + +PlatformImageBuffer* native_buffer_to_platform_image_buffer( + const std::unique_ptr &buffer +) { + return nullptr; +} + +#endif + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_frame_buffer.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_frame_buffer.rs new file mode 100644 index 0000000..7035330 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_frame_buffer.rs @@ -0,0 +1,215 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::impl_thread_safety; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + #[derive(Debug)] + #[repr(i32)] + pub enum VideoFrameBufferType { + Native, + I420, + I420A, + I422, + I444, + I010, + NV12, + } + + unsafe extern "C++" { + include!("livekit/video_frame_buffer.h"); + + type VideoFrameBuffer; + type PlanarYuvBuffer; + type PlanarYuv8Buffer; + type PlanarYuv16BBuffer; + type BiplanarYuvBuffer; + type BiplanarYuv8Buffer; + type I420Buffer; + type I420ABuffer; + type I422Buffer; + type I444Buffer; + type I010Buffer; + type NV12Buffer; + type PlatformImageBuffer; + + fn buffer_type(self: &VideoFrameBuffer) -> VideoFrameBufferType; + fn width(self: &VideoFrameBuffer) -> u32; + fn height(self: &VideoFrameBuffer) -> u32; + + /// # SAFETY + /// If the buffer type is I420, the buffer must be cloned before + unsafe fn to_i420(self: &VideoFrameBuffer) -> UniquePtr; + + /// # SAFETY + /// The functions require ownership + unsafe fn get_i420(self: Pin<&mut VideoFrameBuffer>) -> UniquePtr; + unsafe fn get_i420a(self: Pin<&mut VideoFrameBuffer>) -> UniquePtr; + unsafe fn get_i422(self: Pin<&mut VideoFrameBuffer>) -> UniquePtr; + unsafe fn get_i444(self: Pin<&mut VideoFrameBuffer>) -> UniquePtr; + unsafe fn get_i010(self: Pin<&mut VideoFrameBuffer>) -> UniquePtr; + unsafe fn get_nv12(self: Pin<&mut VideoFrameBuffer>) -> UniquePtr; + + fn chroma_width(self: &PlanarYuvBuffer) -> u32; + fn chroma_height(self: &PlanarYuvBuffer) -> u32; + fn stride_y(self: &PlanarYuvBuffer) -> u32; + fn stride_u(self: &PlanarYuvBuffer) -> u32; + fn stride_v(self: &PlanarYuvBuffer) -> u32; + + fn data_y(self: &PlanarYuv8Buffer) -> *const u8; + fn data_u(self: &PlanarYuv8Buffer) -> *const u8; + fn data_v(self: &PlanarYuv8Buffer) -> *const u8; + + fn data_y(self: &PlanarYuv16BBuffer) -> *const u16; + fn data_u(self: &PlanarYuv16BBuffer) -> *const u16; + fn data_v(self: &PlanarYuv16BBuffer) -> *const u16; + + fn chroma_width(self: &BiplanarYuvBuffer) -> u32; + fn chroma_height(self: &BiplanarYuvBuffer) -> u32; + fn stride_y(self: &BiplanarYuvBuffer) -> u32; + fn stride_uv(self: &BiplanarYuvBuffer) -> u32; + + fn data_y(self: &BiplanarYuv8Buffer) -> *const u8; + fn data_uv(self: &BiplanarYuv8Buffer) -> *const u8; + + fn stride_a(self: &I420ABuffer) -> u32; + fn data_a(self: &I420ABuffer) -> *const u8; + + fn scale(self: &I420Buffer, scaled_width: i32, scaled_height: i32) + -> UniquePtr; + fn scale( + self: &I420ABuffer, + scaled_width: i32, + scaled_height: i32, + ) -> UniquePtr; + fn scale(self: &I422Buffer, scaled_width: i32, scaled_height: i32) + -> UniquePtr; + fn scale(self: &I444Buffer, scaled_width: i32, scaled_height: i32) + -> UniquePtr; + fn scale(self: &I010Buffer, scaled_width: i32, scaled_height: i32) + -> UniquePtr; + fn scale(self: &NV12Buffer, scaled_width: i32, scaled_height: i32) + -> UniquePtr; + + fn copy_i420_buffer(i420: &UniquePtr) -> UniquePtr; + fn new_i420_buffer( + width: i32, + height: i32, + stride_y: i32, + stride_u: i32, + stride_v: i32, + ) -> UniquePtr; + + fn new_i422_buffer( + width: i32, + height: i32, + stride_y: i32, + stride_u: i32, + stride_v: i32, + ) -> UniquePtr; + + fn new_i444_buffer( + width: i32, + height: i32, + stride_y: i32, + stride_u: i32, + stride_v: i32, + ) -> UniquePtr; + + fn new_i010_buffer( + width: i32, + height: i32, + stride_y: i32, + stride_u: i32, + stride_v: i32, + ) -> UniquePtr; + + fn new_nv12_buffer( + width: i32, + height: i32, + stride_y: i32, + stride_uv: i32, + ) -> UniquePtr; + + fn new_fluxer_d3d11_texture_buffer( + handle: u64, + width: u32, + height: u32, + dxgi_format: u32, + ) -> UniquePtr; + + fn new_fluxer_dmabuf_texture_buffer( + fd0: i32, + fd1: i32, + fd2: i32, + fd3: i32, + plane_count: u32, + width: u32, + height: u32, + drm_format: u32, + modifier: u64, + stride0: u32, + stride1: u32, + stride2: u32, + stride3: u32, + offset0: u32, + offset1: u32, + offset2: u32, + offset3: u32, + device_uuid_hi: u64, + device_uuid_lo: u64, + ) -> UniquePtr; + + fn is_fluxer_gpu_buffer(buffer: &UniquePtr) -> bool; + fn fluxer_d3d11_texture_handle(buffer: &UniquePtr) -> u64; + fn fluxer_gpu_buffer_width(buffer: &UniquePtr) -> u32; + fn fluxer_gpu_buffer_height(buffer: &UniquePtr) -> u32; + fn fluxer_gpu_buffer_format(buffer: &UniquePtr) -> u32; + + unsafe fn new_native_buffer_from_platform_image_buffer( + platform_native_buffer: *mut PlatformImageBuffer, + ) -> UniquePtr; + unsafe fn native_buffer_to_platform_image_buffer( + buffer: &UniquePtr, + ) -> *mut PlatformImageBuffer; + + unsafe fn yuv_to_vfb(yuv: *const PlanarYuvBuffer) -> *const VideoFrameBuffer; + unsafe fn biyuv_to_vfb(yuv: *const BiplanarYuvBuffer) -> *const VideoFrameBuffer; + unsafe fn yuv8_to_yuv(yuv8: *const PlanarYuv8Buffer) -> *const PlanarYuvBuffer; + unsafe fn yuv16b_to_yuv(yuv16b: *const PlanarYuv16BBuffer) -> *const PlanarYuvBuffer; + unsafe fn biyuv8_to_biyuv(biyuv8: *const BiplanarYuv8Buffer) -> *const BiplanarYuvBuffer; + unsafe fn i420_to_yuv8(i420: *const I420Buffer) -> *const PlanarYuv8Buffer; + unsafe fn i420a_to_yuv8(i420a: *const I420ABuffer) -> *const PlanarYuv8Buffer; + unsafe fn i422_to_yuv8(i422: *const I422Buffer) -> *const PlanarYuv8Buffer; + unsafe fn i444_to_yuv8(i444: *const I444Buffer) -> *const PlanarYuv8Buffer; + unsafe fn i010_to_yuv16b(i010: *const I010Buffer) -> *const PlanarYuv16BBuffer; + unsafe fn nv12_to_biyuv8(nv12: *const NV12Buffer) -> *const BiplanarYuv8Buffer; + + fn _unique_video_frame_buffer() -> UniquePtr; + } +} + +impl_thread_safety!(ffi::VideoFrameBuffer, Send + Sync); +impl_thread_safety!(ffi::PlanarYuvBuffer, Send + Sync); +impl_thread_safety!(ffi::PlanarYuv8Buffer, Send + Sync); +impl_thread_safety!(ffi::PlanarYuv16BBuffer, Send + Sync); +impl_thread_safety!(ffi::BiplanarYuvBuffer, Send + Sync); +impl_thread_safety!(ffi::BiplanarYuv8Buffer, Send + Sync); +impl_thread_safety!(ffi::I420Buffer, Send + Sync); +impl_thread_safety!(ffi::I420ABuffer, Send + Sync); +impl_thread_safety!(ffi::I422Buffer, Send + Sync); +impl_thread_safety!(ffi::I444Buffer, Send + Sync); +impl_thread_safety!(ffi::I010Buffer, Send + Sync); +impl_thread_safety!(ffi::NV12Buffer, Send + Sync); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_track.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_track.cpp new file mode 100644 index 0000000..3ae36f2 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_track.cpp @@ -0,0 +1,234 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/video_track.h" + +#include +#include +#include + +#include "api/media_stream_interface.h" +#include "api/video/video_frame.h" +#include "api/video/video_rotation.h" +#include "audio/remix_resample.h" +#include "common_audio/include/audio_util.h" +#include "livekit/media_stream.h" +#include "livekit/packet_trailer.h" +#include "livekit/video_track.h" +#include "rtc_base/logging.h" +#include "rtc_base/ref_counted_object.h" +#include "rtc_base/synchronization/mutex.h" +#include "rtc_base/time_utils.h" +#include "webrtc-sys/src/packet_trailer.rs.h" +#include "webrtc-sys/src/video_track.rs.h" + +namespace livekit_ffi { + +VideoTrack::VideoTrack(std::shared_ptr rtc_runtime, + webrtc::scoped_refptr track) + : MediaStreamTrack(rtc_runtime, std::move(track)) {} + +VideoTrack::~VideoTrack() { + webrtc::MutexLock lock(&mutex_); + for (auto& sink : sinks_) { + track()->RemoveSink(sink.get()); + } +} + +void VideoTrack::add_sink(const std::shared_ptr& sink) const { + webrtc::MutexLock lock(&mutex_); + track()->AddOrUpdateSink(sink.get(), + webrtc::VideoSinkWants()); // TODO(theomonnom): Expose + // VideoSinkWants to Rust? + sinks_.push_back(sink); +} + +void VideoTrack::remove_sink( + const std::shared_ptr& sink) const { + webrtc::MutexLock lock(&mutex_); + track()->RemoveSink(sink.get()); + sinks_.erase(std::remove(sinks_.begin(), sinks_.end(), sink), sinks_.end()); +} + +void VideoTrack::set_should_receive(bool should_receive) const { + track()->set_should_receive(should_receive); +} + +bool VideoTrack::should_receive() const { + return track()->should_receive(); +} + +ContentHint VideoTrack::content_hint() const { + return static_cast(track()->content_hint()); +} + +void VideoTrack::set_content_hint(ContentHint hint) const { + track()->set_content_hint( + static_cast(hint)); +} + +NativeVideoSink::NativeVideoSink(rust::Box observer) + : observer_(std::move(observer)) {} + +void NativeVideoSink::OnFrame(const webrtc::VideoFrame& frame) { + observer_->on_frame(std::make_unique(frame)); +} + +void NativeVideoSink::OnDiscardedFrame() { + observer_->on_discarded_frame(); +} + +void NativeVideoSink::OnConstraintsChanged( + const webrtc::VideoTrackSourceConstraints& constraints) { + VideoTrackSourceConstraints cst; + cst.has_min_fps = constraints.min_fps.has_value(); + cst.min_fps = constraints.min_fps.value_or(0); + cst.has_max_fps = constraints.max_fps.has_value(); + cst.max_fps = constraints.max_fps.value_or(0); + observer_->on_constraints_changed(cst); +} + +std::shared_ptr new_native_video_sink( + rust::Box observer) { + return std::make_shared(std::move(observer)); +} + +VideoTrackSource::InternalSource::InternalSource( + const VideoResolution& resolution, bool is_screencast) + : webrtc::AdaptedVideoTrackSource(4), resolution_(resolution), is_screencast_(is_screencast) {} + +VideoTrackSource::InternalSource::~InternalSource() {} + +bool VideoTrackSource::InternalSource::is_screencast() const { + return is_screencast_; +} + +std::optional VideoTrackSource::InternalSource::needs_denoising() const { + return false; +} + +webrtc::MediaSourceInterface::SourceState +VideoTrackSource::InternalSource::state() const { + return SourceState::kLive; +} + +bool VideoTrackSource::InternalSource::remote() const { + return false; +} + +VideoResolution VideoTrackSource::InternalSource::video_resolution() const { + webrtc::MutexLock lock(&mutex_); + return resolution_; +} + +bool VideoTrackSource::InternalSource::on_captured_frame( + const webrtc::VideoFrame& frame, + const FrameMetadata& frame_metadata) { + webrtc::MutexLock lock(&mutex_); + + int64_t aligned_timestamp_us = timestamp_aligner_.TranslateTimestamp( + frame.timestamp_us(), webrtc::TimeMicros()); + + // If a packet trailer was provided on this frame and we have a handler, + // store the mapping keyed by the aligned timestamp. This is the value + // that CaptureTime() will return in TransformSend, so the lookup will + // succeed. + if (frame_metadata.has_packet_trailer && packet_trailer_handler_) { + packet_trailer_handler_->store_frame_metadata( + aligned_timestamp_us, frame_metadata.user_timestamp, + frame_metadata.frame_id); + } + + webrtc::scoped_refptr buffer = + frame.video_frame_buffer(); + + if (resolution_.height == 0 || resolution_.width == 0) { + resolution_ = VideoResolution{static_cast(buffer->width()), + static_cast(buffer->height())}; + } + + int adapted_width, adapted_height, crop_width, crop_height, crop_x, crop_y; + if (!AdaptFrame(buffer->width(), buffer->height(), aligned_timestamp_us, + &adapted_width, &adapted_height, &crop_width, &crop_height, + &crop_x, &crop_y)) { + return false; + } + + if (adapted_width != frame.width() || adapted_height != frame.height()) { + buffer = buffer->CropAndScale(crop_x, crop_y, crop_width, crop_height, + adapted_width, adapted_height); + } + + webrtc::VideoRotation rotation = frame.rotation(); + if (apply_rotation() && rotation != webrtc::kVideoRotation_0) { + // If the buffer is I420, webrtc::AdaptedVideoTrackSource will handle the + // rotation for us. + buffer = buffer->ToI420(); + } + + if (packet_trailer_handler_) { + packet_trailer_handler_->emit_publish_timing( + VideoPublishTimingStage::EncoderUpload, + frame_metadata.has_packet_trailer ? frame_metadata.user_timestamp : 0, + frame_metadata.has_packet_trailer ? frame_metadata.frame_id : 0); + } + + OnFrame(webrtc::VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rotation(rotation) + .set_timestamp_us(aligned_timestamp_us) + .build()); + + return true; +} + +void VideoTrackSource::InternalSource::set_packet_trailer_handler( + std::shared_ptr handler) { + webrtc::MutexLock lock(&mutex_); + packet_trailer_handler_ = std::move(handler); +} + +VideoTrackSource::VideoTrackSource(const VideoResolution& resolution, bool is_screencast) { + source_ = webrtc::make_ref_counted(resolution, is_screencast); +} + +VideoResolution VideoTrackSource::video_resolution() const { + return source_->video_resolution(); +} + +bool VideoTrackSource::on_captured_frame( + const std::unique_ptr& frame, + const FrameMetadata& frame_metadata) const { + auto rtc_frame = frame->get(); + return source_->on_captured_frame(rtc_frame, frame_metadata); +} + +void VideoTrackSource::set_packet_trailer_handler( + std::shared_ptr handler) const { + source_->set_packet_trailer_handler(std::move(handler)); +} + +webrtc::scoped_refptr VideoTrackSource::get() + const { + return source_; +} + +std::shared_ptr new_video_track_source( + const VideoResolution& resolution, bool is_screencast) { + return std::make_shared(resolution, is_screencast); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_track.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_track.rs new file mode 100644 index 0000000..71bc5a4 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/video_track.rs @@ -0,0 +1,142 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use std::sync::Arc; + +use cxx::UniquePtr; + +use crate::{impl_thread_safety, video_frame::ffi::VideoFrame}; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + #[repr(i32)] + pub enum ContentHint { + None, + Fluid, + Detailed, + Text, + } + + #[derive(Debug)] + pub struct VideoTrackSourceConstraints { + pub has_min_fps: bool, + pub min_fps: f64, + pub has_max_fps: bool, + pub max_fps: f64, + } + + #[derive(Debug)] + pub struct VideoResolution { + pub width: u32, + pub height: u32, + } + + #[derive(Debug)] + pub struct FrameMetadata { + pub has_packet_trailer: bool, + pub user_timestamp: u64, + pub frame_id: u32, + } + + extern "C++" { + include!("livekit/video_frame.h"); + include!("livekit/media_stream_track.h"); + + type VideoFrame = crate::video_frame::ffi::VideoFrame; + type MediaStreamTrack = crate::media_stream_track::ffi::MediaStreamTrack; + } + + extern "C++" { + include!("livekit/packet_trailer.h"); + include!("livekit/video_track.h"); + + type PacketTrailerHandler = crate::packet_trailer::ffi::PacketTrailerHandler; + } + + unsafe extern "C++" { + + type VideoTrack; + type NativeVideoSink; + type VideoTrackSource; + + fn add_sink(self: &VideoTrack, sink: &SharedPtr); + fn remove_sink(self: &VideoTrack, sink: &SharedPtr); + fn set_should_receive(self: &VideoTrack, should_receive: bool); + fn should_receive(self: &VideoTrack) -> bool; + fn content_hint(self: &VideoTrack) -> ContentHint; + fn set_content_hint(self: &VideoTrack, hint: ContentHint); + fn new_native_video_sink(observer: Box) -> SharedPtr; + + fn video_resolution(self: &VideoTrackSource) -> VideoResolution; + fn on_captured_frame( + self: &VideoTrackSource, + frame: &UniquePtr, + frame_metadata: &FrameMetadata, + ) -> bool; + fn set_packet_trailer_handler( + self: &VideoTrackSource, + handler: SharedPtr, + ); + fn new_video_track_source( + resolution: &VideoResolution, + is_screencast: bool, + ) -> SharedPtr; + fn video_to_media(track: SharedPtr) -> SharedPtr; + unsafe fn media_to_video(track: SharedPtr) -> SharedPtr; + fn _shared_video_track() -> SharedPtr; + } + + extern "Rust" { + type VideoSinkWrapper; + + fn on_frame(self: &VideoSinkWrapper, frame: UniquePtr); + fn on_discarded_frame(self: &VideoSinkWrapper); + fn on_constraints_changed( + self: &VideoSinkWrapper, + constraints: VideoTrackSourceConstraints, + ); + } +} + +impl_thread_safety!(ffi::VideoTrack, Send + Sync); +impl_thread_safety!(ffi::NativeVideoSink, Send + Sync); +impl_thread_safety!(ffi::VideoTrackSource, Send + Sync); + +pub trait VideoSink: Send { + fn on_frame(&self, frame: UniquePtr); + fn on_discarded_frame(&self); + fn on_constraints_changed(&self, constraints: ffi::VideoTrackSourceConstraints); +} + +pub struct VideoSinkWrapper { + observer: Arc, +} + +impl VideoSinkWrapper { + pub fn new(observer: Arc) -> Self { + Self { observer } + } + + fn on_frame(&self, frame: UniquePtr) { + self.observer.on_frame(frame); + } + + fn on_discarded_frame(&self) { + self.observer.on_discarded_frame(); + } + + fn on_constraints_changed(&self, constraints: ffi::VideoTrackSourceConstraints) { + self.observer.on_constraints_changed(constraints); + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/webrtc.cpp b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/webrtc.cpp new file mode 100644 index 0000000..2a2c257 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/webrtc.cpp @@ -0,0 +1,176 @@ +/* + * Copyright 2025 LiveKit, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "livekit/webrtc.h" + +#include +#include +#include +#include + +#include "livekit/audio_track.h" +#include "livekit/media_stream_track.h" +#include "livekit/rtp_receiver.h" +#include "livekit/rtp_sender.h" +#include "livekit/video_track.h" +#include "rtc_base/logging.h" +#include "rtc_base/crypto_random.h" +#include "rtc_base/synchronization/mutex.h" + +#ifdef WEBRTC_WIN +#include "rtc_base/win32.h" +#endif + +namespace livekit_ffi { + +static webrtc::Mutex g_mutex{}; +// Can't be atomic, we're using a Mutex because we need to wait for the +// execution of the first init +static uint32_t g_release_counter(0); + +RtcRuntime::RtcRuntime() { + RTC_LOG(LS_VERBOSE) << "RtcRuntime()"; + + { + // Not the best way to do it... + webrtc::MutexLock lock(&g_mutex); + if (g_release_counter == 0) { + RTC_CHECK(webrtc::InitializeSSL()) << "Failed to InitializeSSL()"; + +#ifdef WEBRTC_WIN + WSADATA data; + WSAStartup(MAKEWORD(1, 0), &data); +#endif + } + g_release_counter++; + } + + network_thread_ = webrtc::Thread::CreateWithSocketServer(); + network_thread_->SetName("network_thread", &network_thread_); + network_thread_->Start(); + worker_thread_ = webrtc::Thread::Create(); + worker_thread_->SetName("worker_thread", &worker_thread_); + worker_thread_->Start(); + signaling_thread_ = webrtc::Thread::Create(); + signaling_thread_->SetName("signaling_thread", &signaling_thread_); + signaling_thread_->Start(); +} + +RtcRuntime::~RtcRuntime() { + RTC_LOG(LS_VERBOSE) << "~RtcRuntime()"; + + worker_thread_->Stop(); + signaling_thread_->Stop(); + network_thread_->Stop(); + + { + webrtc::MutexLock lock(&g_mutex); + g_release_counter--; + if (g_release_counter == 0) { + RTC_CHECK(webrtc::CleanupSSL()) << "Failed to CleanupSSL()"; + +#ifdef WEBRTC_WIN + WSACleanup(); +#endif + } + } +} + +webrtc::Thread* RtcRuntime::network_thread() const { + return network_thread_.get(); +} + +webrtc::Thread* RtcRuntime::worker_thread() const { + return worker_thread_.get(); +} + +webrtc::Thread* RtcRuntime::signaling_thread() const { + return signaling_thread_.get(); +} + +std::shared_ptr RtcRuntime::get_or_create_media_stream_track( + webrtc::scoped_refptr rtc_track) { + webrtc::MutexLock lock(&mutex_); + for (std::weak_ptr weak_existing_track : + media_stream_tracks_) { + if (std::shared_ptr existing_track = + weak_existing_track.lock()) { + if (existing_track->rtc_track() == rtc_track) { + return existing_track; + } + } + } + + if (rtc_track->kind() == webrtc::MediaStreamTrackInterface::kVideoKind) { + std::shared_ptr video_track = + std::shared_ptr(new VideoTrack( + shared_from_this(), + webrtc::scoped_refptr( + static_cast(rtc_track.get())))); + + media_stream_tracks_.push_back( + std::static_pointer_cast(video_track)); + return video_track; + } else { + std::shared_ptr audio_track = + std::shared_ptr(new AudioTrack( + shared_from_this(), + webrtc::scoped_refptr( + static_cast(rtc_track.get())))); + + media_stream_tracks_.push_back( + std::static_pointer_cast(audio_track)); + return audio_track; + } +} + +std::shared_ptr RtcRuntime::get_or_create_audio_track( + webrtc::scoped_refptr track) { + return std::static_pointer_cast( + get_or_create_media_stream_track(track)); +} + +std::shared_ptr RtcRuntime::get_or_create_video_track( + webrtc::scoped_refptr track) { + return std::static_pointer_cast( + get_or_create_media_stream_track(track)); +} + +LogSink::LogSink( + rust::Fn fnc) + : fnc_(fnc) { + webrtc::LogMessage::AddLogToStream(this, webrtc::LoggingSeverity::LS_VERBOSE); +} + +LogSink::~LogSink() { + webrtc::LogMessage::RemoveLogToStream(this); +} + +void LogSink::OnLogMessage(const std::string& message, + webrtc::LoggingSeverity severity) { + fnc_(rust::String::lossy(message), static_cast(severity)); +} + +std::unique_ptr new_log_sink( + rust::Fn fnc) { + return std::make_unique(fnc); +} + +rust::String create_random_uuid() { + return webrtc::CreateRandomUuid(); +} + +} // namespace livekit_ffi diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/webrtc.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/webrtc.rs new file mode 100644 index 0000000..a0ee7c0 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/webrtc.rs @@ -0,0 +1,67 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::impl_thread_safety; + +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + #[derive(Debug)] + #[repr(i32)] + pub enum MediaType { + Audio, + Video, + Data, + Unsupported, + } + + #[derive(Debug)] + #[repr(i32)] + pub enum Priority { + VeryLow, + Low, + Medium, + High, + } + + #[derive(Debug)] + #[repr(i32)] + pub enum RtpTransceiverDirection { + SendRecv, + SendOnly, + RecvOnly, + Inactive, + Stopped, + } + + #[derive(Debug)] + #[repr(i32)] + pub enum LoggingSeverity { + Verbose, + Info, + Warning, + Error, + None, + } + + unsafe extern "C++" { + include!("livekit/webrtc.h"); + + type LogSink; + + fn create_random_uuid() -> String; + fn new_log_sink(fnc: fn(String, LoggingSeverity)) -> UniquePtr; + } +} + +impl_thread_safety!(ffi::LogSink, Send + Sync); diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/yuv_helper.rs b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/yuv_helper.rs new file mode 100644 index 0000000..78f9487 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/src/yuv_helper.rs @@ -0,0 +1,312 @@ +// Copyright 2025 LiveKit, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[allow(clippy::too_many_arguments)] +#[allow(clippy::missing_safety_doc)] +#[cxx::bridge(namespace = "livekit_ffi")] +pub mod ffi { + unsafe extern "C++" { + include!("livekit/yuv_helper.h"); + + unsafe fn i420_to_argb( + src_y: *const u8, + src_stride_y: i32, + src_u: *const u8, + src_stride_u: i32, + src_v: *const u8, + src_stride_v: i32, + dst_argb: *mut u8, + dst_stride_argb: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn i420_to_bgra( + src_y: *const u8, + src_stride_y: i32, + src_u: *const u8, + src_stride_u: i32, + src_v: *const u8, + src_stride_v: i32, + dst_bgra: *mut u8, + dst_stride_bgra: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn i420_to_abgr( + src_y: *const u8, + src_stride_y: i32, + src_u: *const u8, + src_stride_u: i32, + src_v: *const u8, + src_stride_v: i32, + dst_abgr: *mut u8, + dst_stride_abgr: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn i420_to_rgba( + src_y: *const u8, + src_stride_y: i32, + src_u: *const u8, + src_stride_u: i32, + src_v: *const u8, + src_stride_v: i32, + dst_rgba: *mut u8, + dst_stride_rgba: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn argb_to_i420( + src_argb: *const u8, + src_stride_argb: i32, + dst_y: *mut u8, + dst_stride_y: i32, + dst_u: *mut u8, + dst_stride_u: i32, + dst_v: *mut u8, + dst_stride_v: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn abgr_to_i420( + src_abgr: *const u8, + src_stride_abgr: i32, + dst_y: *mut u8, + dst_stride_y: i32, + dst_u: *mut u8, + dst_stride_u: i32, + dst_v: *mut u8, + dst_stride_v: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn argb_to_rgb24( + src_argb: *const u8, + src_stride_argb: i32, + dst_rgb24: *mut u8, + dst_stride_rgb24: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn nv12_to_i420( + src_y: *const u8, + src_stride_y: i32, + src_uv: *const u8, + src_stride_uv: i32, + dst_y: *mut u8, + dst_stride_y: i32, + dst_u: *mut u8, + dst_stride_u: i32, + dst_v: *mut u8, + dst_stride_v: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn i420_to_nv12( + src_y: *const u8, + src_stride_y: i32, + src_u: *const u8, + src_stride_u: i32, + src_v: *const u8, + src_stride_v: i32, + dst_y: *mut u8, + dst_stride_y: i32, + dst_uv: *mut u8, + dst_stride_uv: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn i444_to_i420( + src_y: *const u8, + src_stride_y: i32, + src_u: *const u8, + src_stride_u: i32, + src_v: *const u8, + src_stride_v: i32, + dst_y: *mut u8, + dst_stride_y: i32, + dst_u: *mut u8, + dst_stride_u: i32, + dst_v: *mut u8, + dst_stride_v: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn i422_to_i420( + src_y: *const u8, + src_stride_y: i32, + src_u: *const u8, + src_stride_u: i32, + src_v: *const u8, + src_stride_v: i32, + dst_y: *mut u8, + dst_stride_y: i32, + dst_u: *mut u8, + dst_stride_u: i32, + dst_v: *mut u8, + dst_stride_v: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn i010_to_i420( + src_y: *const u16, + src_stride_y: i32, + src_u: *const u16, + src_stride_u: i32, + src_v: *const u16, + src_stride_v: i32, + dst_y: *mut u8, + dst_stride_y: i32, + dst_u: *mut u8, + dst_stride_u: i32, + dst_v: *mut u8, + dst_stride_v: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn nv12_to_argb( + src_y: *const u8, + src_stride_y: i32, + src_uv: *const u8, + src_stride_uv: i32, + dst_argb: *mut u8, + dst_stride_argb: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn nv12_to_abgr( + src_y: *const u8, + src_stride_y: i32, + src_uv: *const u8, + src_stride_uv: i32, + dst_abgr: *mut u8, + dst_stride_abgr: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn i444_to_argb( + src_y: *const u8, + src_stride_y: i32, + src_u: *const u8, + src_stride_u: i32, + src_v: *const u8, + src_stride_v: i32, + dst_abgr: *mut u8, + dst_stride_abgr: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn i444_to_abgr( + src_y: *const u8, + src_stride_y: i32, + src_u: *const u8, + src_stride_u: i32, + src_v: *const u8, + src_stride_v: i32, + dst_abgr: *mut u8, + dst_stride_abgr: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn i422_to_argb( + src_y: *const u8, + src_stride_y: i32, + src_u: *const u8, + src_stride_u: i32, + src_v: *const u8, + src_stride_v: i32, + dst_argb: *mut u8, + dst_stride_argb: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn i422_to_abgr( + src_y: *const u8, + src_stride_y: i32, + src_u: *const u8, + src_stride_u: i32, + src_v: *const u8, + src_stride_v: i32, + dst_abgr: *mut u8, + dst_stride_abgr: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn i010_to_argb( + src_y: *const u16, + src_stride_y: i32, + src_u: *const u16, + src_stride_u: i32, + src_v: *const u16, + src_stride_v: i32, + dst_argb: *mut u8, + dst_stride_argb: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn i010_to_abgr( + src_y: *const u16, + src_stride_y: i32, + src_u: *const u16, + src_stride_u: i32, + src_v: *const u16, + src_stride_v: i32, + dst_abgr: *mut u8, + dst_stride_abgr: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn abgr_to_nv12( + src_abgr: *const u8, + src_stride_abgr: i32, + dst_y: *mut u8, + dst_stride_y: i32, + dst_uv: *mut u8, + dst_stride_uv: i32, + width: i32, + height: i32, + ) -> Result<()>; + + unsafe fn argb_to_nv12( + src_argb: *const u8, + src_stride_argb: i32, + dst_y: *mut u8, + dst_stride_y: i32, + dst_uv: *mut u8, + dst_stride_uv: i32, + width: i32, + height: i32, + ) -> Result<()>; + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/CMakeLists.txt b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/CMakeLists.txt new file mode 100644 index 0000000..ed47a64 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/CMakeLists.txt @@ -0,0 +1,92 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.10) +project(runner LANGUAGES CXX C) +enable_language(ASM) + +set(BINARY_NAME "h264_benchmark") + +# Set the C++ standard to C++23. +set(CMAKE_CXX_STANDARD 20) + +set(CMAKE_BUILD_TYPE Debug) + + +add_definitions(-DWEBRTC_POSIX) +add_definitions(-DWEBRTC_LINUX) +add_definitions(-DUSE_UDEV) +add_definitions(-DUSE_AURA=1) +add_definitions(-DUSE_GLIB=1) +add_definitions(-DUSE_OZONE=1) +add_definitions(-D__STDC_CONSTANT_MACROS) +add_definitions(-D__STDC_FORMAT_MACROS) +add_definitions(-D_FILE_OFFSET_BITS=64) +add_definitions(-D_LARGEFILE_SOURCE) +add_definitions(-D_LARGEFILE64_SOURCE) +add_definitions(-D_GNU_SOURCE) +add_definitions(-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_NONE) +add_definitions(-D_GLIBCXX_ASSERTIONS=1) +add_definitions(-DCR_CLANG_REVISION=\"llvmorg-19-init-8091-gab037c4f-1\") +add_definitions(-DCR_SYSROOT_KEY=20230611T210420Z-2) +add_definitions(-DDYNAMIC_ANNOTATIONS_ENABLED=1) +add_definitions(-DWEBRTC_ENABLE_PROTOBUF=0) +add_definitions(-DWEBRTC_STRICT_FIELD_TRIALS=0) +add_definitions(-DWEBRTC_INCLUDE_INTERNAL_AUDIO_DEVICE) +add_definitions(-DRTC_USE_LIBAOM_AV1_ENCODER) +add_definitions(-DRTC_ENABLE_VP9) +add_definitions(-DRTC_DAV1D_IN_INTERNAL_DECODER_FACTORY) +add_definitions(-DWEBRTC_HAVE_SCTP) +add_definitions(-DWEBRTC_USE_H264) +add_definitions(-DWEBRTC_ENABLE_LIBEVENT) +add_definitions(-DWEBRTC_LIBRARY_IMPL) +add_definitions(-DWEBRTC_ENABLE_AVX2) + +include_directories( + "${CMAKE_CURRENT_SOURCE_DIR}/../libwebrtc/linux-x64-debug/include" + "${CMAKE_CURRENT_SOURCE_DIR}/../libwebrtc/linux-x64-release/include/third_party/abseil-cpp" + "${CMAKE_CURRENT_SOURCE_DIR}/../libwebrtc/linux-x64-release/include/third_party/libyuv/include" + "${CMAKE_CURRENT_SOURCE_DIR}/../src/nvidia/NvCodec/include" + "${CMAKE_CURRENT_SOURCE_DIR}/../src/nvidia/NvCodec/NvCodec" + "${CMAKE_CURRENT_SOURCE_DIR}/../src" +) + +link_libraries( + "${CMAKE_CURRENT_SOURCE_DIR}/../libwebrtc/linux-x64-release/lib/libwebrtc.a" +) + +find_package (Threads) + +add_executable(${BINARY_NAME} + "test_main.cc" + "benchmark.cc" + "benchmark_openh264.cc" + "video_source.cc" + "fileutils.cc" + "cpu/cpu_linux.cc" + + "benchmark_nvidia.cc" + "../src/nvidia/NvCodec/NvCodec/NvDecoder/NvDecoder.cpp" + "../src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoder.cpp" + "../src/nvidia/NvCodec/NvCodec/NvEncoder/NvEncoderCuda.cpp" + "../src/nvidia/h264_encoder_impl.cpp" + "../src/nvidia/h264_decoder_impl.cpp" + "../src/nvidia/nvidia_decoder_factory.cpp" + "../src/nvidia/nvidia_encoder_factory.cpp" + "../src/nvidia/cuda_context.cpp" + "../src/nvidia/implib/libcuda.so.init.c" + "../src/nvidia/implib/libcuda.so.tramp.S" + "../src/nvidia/implib/libnvcuvid.so.init.c" + "../src/nvidia/implib/libnvcuvid.so.tramp.S" + + "benchmark_vaapi.cc" + "../src/vaapi/vaapi_display_drm.cpp" + "../src/vaapi/vaapi_h264_encoder_wrapper.cpp" + "../src/vaapi/vaapi_encoder_factory.cpp" + "../src/vaapi/h264_encoder_impl.cpp" + "../src/vaapi/implib/libva-drm.so.init.c" + "../src/vaapi/implib/libva-drm.so.tramp.S" + "../src/vaapi/implib/libva.so.init.c" + "../src/vaapi/implib/libva.so.tramp.S" +) + +target_link_libraries(${BINARY_NAME} ${CMAKE_THREAD_LIBS_INIT}) +target_link_libraries(${BINARY_NAME} dl) diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark.cc b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark.cc new file mode 100644 index 0000000..b66e2ec --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark.cc @@ -0,0 +1,451 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "benchmark.h" + +#include +#include +#include +#include +#if defined(_WIN32) +#include +#endif + +#include "api/video/i420_buffer.h" +#include "common_video/libyuv/include/webrtc_libyuv.h" +#include "fileutils.h" +#include "modules/video_coding/utility/simulcast_rate_allocator.h" +#include "rtc_base/event.h" +#include "video_source.h" + +#define SSIM_CALC 0 // by default, don't compute SSIM + +using namespace webrtc; + +#define EXPECT_EQ (a, b) + +FrameQueueTuple::~FrameQueueTuple() { + if (_codecSpecificInfo != NULL) { + delete _codecSpecificInfo; + } + if (_frame != NULL) { + delete _frame; + } +} + +void FrameQueue::PushFrame(VideoFrame* frame, + webrtc::CodecSpecificInfo* codecSpecificInfo) { + webrtc::MutexLock cs(&_queueRWLock); + _frameBufferQueue.push(new FrameQueueTuple(frame, codecSpecificInfo)); +} + +FrameQueueTuple* FrameQueue::PopFrame() { + webrtc::MutexLock cs(&_queueRWLock); + if (_frameBufferQueue.empty()) { + return NULL; + } + FrameQueueTuple* tuple = _frameBufferQueue.front(); + _frameBufferQueue.pop(); + return tuple; +} + +bool FrameQueue::Empty() { + webrtc::MutexLock cs(&_queueRWLock); + return _frameBufferQueue.empty(); +} + +uint32_t VideoEncodeCompleteCallback::EncodedBytes() { + return _encodedBytes; +} + +webrtc::EncodedImageCallback::Result +VideoEncodeCompleteCallback::OnEncodedImage( + const webrtc::EncodedImage& encodedImage, + const webrtc::CodecSpecificInfo* codecSpecificInfo) { + _test.UpdateEncodedBytes(encodedImage.GetEncodedData()->size()); + _encodedBytes += encodedImage.GetEncodedData()->size(); + + if (_encodedFile != NULL) { + if (fwrite(encodedImage.GetEncodedData()->data(), 1, + encodedImage.GetEncodedData()->size(), + _encodedFile) != encodedImage.GetEncodedData()->size()) { + fprintf(stderr, "Error writing to encoded file %s\n", + _test._outname.c_str()); + } + } + + return webrtc::EncodedImageCallback::Result( + webrtc::EncodedImageCallback::Result::OK); +} + +Benchmark::Benchmark() + : _resultsFileName(webrtc::test::OutputPath() + "benchmark.txt"), + _codecName("Default"), + _env(webrtc::CreateEnvironment()) {} + +Benchmark::Benchmark(std::string name, std::string description) + : _name(name), + _description(description), + _resultsFileName(webrtc::test::OutputPath() + "benchmark.txt"), + _codecName("Default"), + _env(webrtc::CreateEnvironment()) {} + +Benchmark::Benchmark(std::string name, + std::string description, + std::string resultsFileName, + std::string codecName) + : _name(name), + _description(description), + _resultsFileName(resultsFileName), + _codecName(codecName), + _cpu(webrtc::CpuWrapper::CreateCpu()), + _env(webrtc::CreateEnvironment()) {} + +void Benchmark::Perform() { + std::vector sources; + std::vector::iterator it; + + // Configuration -------------------------- + sources.push_back(new const VideoSource( + webrtc::test::ProjectRootPath() + "resources/FourPeople_1280x720_30.yuv", + kWHD)); + //sources.push_back( + // new const VideoSource(webrtc::test::ProjectRootPath() + + // "resources/Big_Buck_Bunny_1920x1080_30.yuv", + // kWFullHD)); + + const VideoSize size[] = {kWHD}; + const int frameRate[] = {30}; + // Specifies the framerates for which to perform a speed test. + const bool speedTestMask[] = {true}; + const int bitRate[] = {500, 1000, 2000, 3000, 4000}; + // Determines the number of iterations to perform to arrive at the speed + // result. + enum { kSpeedTestIterations = 8 }; + // ---------------------------------------- + + const int nFrameRates = sizeof(frameRate) / sizeof(*frameRate); + assert(sizeof(speedTestMask) / sizeof(*speedTestMask) == nFrameRates); + const int nBitrates = sizeof(bitRate) / sizeof(*bitRate); + int testIterations = 1; + + double fps[nBitrates]; + uint32_t cpuUsage[nBitrates]; + double totalEncodeTime[nBitrates]; + double totalDecodeTime[nBitrates]; + + _results.open(_resultsFileName.c_str(), std::fstream::out); + _results << GetMagicStr() << std::endl; + _results << _codecName << std::endl; + + for (it = sources.begin(); it < sources.end(); it++) { + int i = 0; + for (int j = 0; j < nFrameRates; j++) { + _target = *it; + _inname = (*it)->GetFileName(); + std::cout << (*it)->GetName() << ", " + << VideoSource::GetSizeString(size[i]) << ", " << frameRate[j] + << " fps" << ", " << _name << std::endl; + _results << (*it)->GetName() << "," << VideoSource::GetSizeString(size[i]) + << "," << frameRate[j] << " fps" << ", " << _name << std::endl + << "Bitrate [kbps]"; + + if (speedTestMask[j]) { + testIterations = kSpeedTestIterations; + } else { + testIterations = 1; + } + + for (int k = 0; k < nBitrates; k++) { + _bitRate = (bitRate[k]); + double avgFps = 0.0; + uint32_t currCpuUsage = 0; + totalEncodeTime[k] = 0; + + std::cout << "TargetBitrate [kbps]:" << " " << _bitRate << std::endl; + + for (int l = 0; l < testIterations; l++) { + PerformNormalTest(); + uint32_t cpuUsage = _cpu->CpuUsage(); + if (cpuUsage > 0) { + currCpuUsage += cpuUsage; + int coreCount = _cpu->GetNumCores(); + std::string str = "CPU Usage[%]: cores "; + str += std::to_string(coreCount); + str += ", usage " + std::to_string(cpuUsage) + "%" + + ", Test Iteration: " + std::to_string(l + 1) + "/" + + std::to_string(testIterations); + std::cout << str << std::flush; + for (int i = 0; i < str.length(); ++i) { + std::cout << "\b"; + } + } + _appendNext = false; + avgFps += _framecnt / (_totalEncodeTime); + totalEncodeTime[k] += _totalEncodeTime; + } + avgFps /= testIterations; + totalEncodeTime[k] /= testIterations; + currCpuUsage /= testIterations; + + double actualBitRate = ActualBitRate(_framecnt) / 1000.0; + std::cout << "ActualBitRate [kbps]:" << " " << actualBitRate + << std::endl; + _results << "," << actualBitRate; + fps[k] = avgFps; + cpuUsage[k] = currCpuUsage; + } + + std::cout << std::endl << "CpuUsage [%]:"; + _results << std::endl << "CpuUsage [%]"; + for (int k = 0; k < nBitrates; k++) { + std::cout << " " << cpuUsage[k] << "%"; + _results << "," << cpuUsage[k] << "%"; + } + std::cout << std::endl << "Encode Time[ms]:"; + _results << std::endl << "Encode Time[ms]"; + for (int k = 0; k < nBitrates; k++) { + std::cout << " " << totalEncodeTime[k]; + _results << "," << totalEncodeTime[k]; + } + + if (speedTestMask[j]) { + std::cout << std::endl << "Speed [fps]:"; + _results << std::endl << "Speed [fps]"; + for (int k = 0; k < nBitrates; k++) { + std::cout << " " << static_cast(fps[k] + 0.5); + _results << "," << static_cast(fps[k] + 0.5); + } + } + std::cout << std::endl << std::endl; + _results << std::endl << std::endl; + } + i++; + delete *it; + } + _results.close(); +} + +void Benchmark::PerformNormalTest() { + _encoder = GetNewEncoder(_env); + _lengthSourceFrame = _target->GetFrameLength(); + CodecSettings(_target->GetWidth(), _target->GetHeight(), + _target->GetFrameRate(), _bitRate); + Setup(); + std::unique_ptr waitEvent = std::make_unique(); + //_inputVideoBuffer.VerifyAndAllocate(_lengthSourceFrame); + _encoder->InitEncode(&_inst, 4, 1440); + CodecSpecific_InitBitrate(); + //_decoder->InitDecode(&_inst,1); + + FrameQueue frameQueue; + VideoEncodeCompleteCallback encCallback(_encodedFile, &frameQueue, *this); + + _encoder->RegisterEncodeCompleteCallback(&encCallback); + + _totalEncodeTime = _totalDecodeTime = 0; + _totalEncodePipeTime = _totalDecodePipeTime = 0; + bool complete = false; + _framecnt = 0; + _encFrameCnt = 0; + _sumEncBytes = 0; + _lengthEncFrame = 0; + while (!complete) { + complete = Encode(); + _framecnt++; + _encFrameCnt++; + /* + if (!frameQueue.Empty() || complete) { + while (!frameQueue.Empty()) { + _frameToDecode = static_cast(frameQueue.PopFrame()); + int ret = Decode(); + delete _frameToDecode; + _frameToDecode = NULL; + if (ret < 0) { + fprintf(stderr, "\n\nError in decoder: %d\n\n", ret); + exit(EXIT_FAILURE); + } else if (ret == 0) { + _framecnt++; + } else { + fprintf(stderr, "\n\nPositive return value from decode!\n\n"); + } + } + }*/ + // waitEvent->Wait(webrtc::TimeDelta::Seconds(5)); + } + + //_inputVideoBuffer.Free(); + //_encodedVideoBuffer.Free(); + //_decodedVideoBuffer.Free(); + + Teardown(); +} + +void Benchmark::Teardown() { + // Use _sourceFile as a check to prevent multiple Teardown() calls. + if (_sourceFile == NULL) { + return; + } + + _encoder->Release(); + + fclose(_sourceFile); + _sourceFile = NULL; + + delete[] _sourceBuffer; + _sourceBuffer = NULL; +} + +void Benchmark::CodecSpecific_InitBitrate() { + webrtc::SimulcastRateAllocator init_allocator(_env,_inst); + + if (_bitRate == 0) { + VideoBitrateAllocation allocation = + init_allocator.Allocate(VideoBitrateAllocationParameters( + DataRate::KilobitsPerSec(600), _inst.maxFramerate)); + _encoder->SetRates(webrtc::VideoEncoder::RateControlParameters( + allocation, _inst.maxFramerate)); + } else { + VideoBitrateAllocation allocation = + init_allocator.Allocate(VideoBitrateAllocationParameters( + DataRate::BitsPerSec(_bitRate), _inst.maxFramerate)); + _encoder->SetRates(webrtc::VideoEncoder::RateControlParameters( + allocation, _inst.maxFramerate)); + } +} + +bool Benchmark::Encode() { + _lengthEncFrame = 0; + if (_sourceBuffer == NULL) { + _sourceBuffer = new unsigned char[_lengthSourceFrame]; + } + auto size = fread(_sourceBuffer, 1, _lengthSourceFrame, _sourceFile); + if (size <= 0) { + return true; + } + // TODO: build video frame from buffer ptr. + webrtc::scoped_refptr buffer( + webrtc::I420Buffer::Create(_inst.width, _inst.height)); + + buffer->InitializeData(); + + memcpy(buffer->MutableDataY(), _sourceBuffer, _lengthSourceFrame); + + webrtc::VideoFrame inputVideoBuffer = + webrtc::VideoFrame::Builder() + .set_video_frame_buffer(buffer) + .set_rtp_timestamp( + (unsigned int)(_encFrameCnt * 9e4 / _inst.maxFramerate)) + .build(); + + if (feof(_sourceFile) != 0) { + return true; + } + _encodeCompleteTime = 0; + _encodeTimes[inputVideoBuffer.rtp_timestamp()] = tGetTime(); + std::vector frame_types(1, VideoFrameType::kVideoFrameDelta); + + // check SLI queue + _hasReceivedSLI = false; + while (!_signalSLI.empty() && _signalSLI.front().delay == 0) { + // SLI message has arrived at sender side + _hasReceivedSLI = true; + _pictureIdSLI = _signalSLI.front().id; + _signalSLI.pop_front(); + } + // decrement SLI queue times + for (std::list::iterator it = _signalSLI.begin(); + it != _signalSLI.end(); it++) { + (*it).delay--; + } + + // check PLI queue + _hasReceivedPLI = false; + while (!_signalPLI.empty() && _signalPLI.front().delay == 0) { + // PLI message has arrived at sender side + _hasReceivedPLI = true; + _signalPLI.pop_front(); + } + // decrement PLI queue times + for (std::list::iterator it = _signalPLI.begin(); + it != _signalPLI.end(); it++) { + (*it).delay--; + } + + if (_hasReceivedPLI) { + // respond to PLI by encoding a key frame + frame_types[0] = VideoFrameType::kVideoFrameKey; + _hasReceivedPLI = false; + _hasReceivedSLI = false; // don't trigger both at once + } + + int ret = _encoder->Encode(inputVideoBuffer, &frame_types); + + if (_encodeCompleteTime > 0) { + _totalEncodeTime += + _encodeCompleteTime - _encodeTimes[inputVideoBuffer.rtp_timestamp()]; + } else { + _totalEncodeTime += tGetTime() - _encodeTimes[inputVideoBuffer.rtp_timestamp()]; + } + assert(ret >= 0); + return false; +} + +webrtc::CodecSpecificInfo* Benchmark::CopyCodecSpecificInfo( + const webrtc::CodecSpecificInfo* codecSpecificInfo) const { + webrtc::CodecSpecificInfo* info = new webrtc::CodecSpecificInfo; + *info = *codecSpecificInfo; + return info; +} + +void Benchmark::Setup() { + // Use _sourceFile as a check to prevent multiple Setup() calls. + if (_sourceFile != NULL) { + return; + } + + std::stringstream ss; + std::string strTestNo; + ss << "0"; + ss >> strTestNo; + + // Check if settings exist. Otherwise use defaults. + if (_outname == "") { + _outname = + webrtc::test::OutputPath() + "out_normaltest" + strTestNo + ".yuv"; + } + + if (_codecName == "") { + _codecName = + webrtc::test::OutputPath() + "encoded_normaltest" + strTestNo + ".yuv"; + } + + if ((_sourceFile = fopen(_inname.c_str(), "rb")) == NULL) { + printf("Cannot read file %s.\n", _inname.c_str()); + exit(1); + } + + if ((_encodedFile = fopen(_codecName.c_str(), "wb")) == NULL) { + printf("Cannot write encoded file.\n"); + exit(1); + } + + char mode[3] = "wb"; + if (_appendNext) { + strncpy(mode, "ab", 3); + } + + // if ((_decodedFile = fopen(_outname.c_str(), mode)) == NULL) { + // printf("Cannot write file %s.\n", _outname.c_str()); + // exit(1); + // } + + _appendNext = true; +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark.h new file mode 100644 index 0000000..2ae2900 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark.h @@ -0,0 +1,214 @@ +; /* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAWEWORK_BENCHMARK_H_ +#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAWEWORK_BENCHMARK_H_ + +#include +#include +#include +#include +#include + +#include "cpu/cpu_linux.h" +#include "api/environment/environment_factory.h" +#include "modules/include/module_common_types.h" +#include "modules/video_coding/include/video_codec_interface.h" +#include "rtc_base/synchronization/mutex.h" +#include "system_wrappers/include/clock.h" + + +class VideoSource; +class Benchmark; + +// feedback signal to encoder +struct fbSignal { + fbSignal(int d, uint8_t pid) : delay(d), id(pid) {}; + int delay; + uint8_t id; +}; + +class FrameQueueTuple { + public: + FrameQueueTuple(webrtc::VideoFrame* frame, + const webrtc::CodecSpecificInfo* codecSpecificInfo = NULL) + : _frame(frame), _codecSpecificInfo(codecSpecificInfo) {}; + ~FrameQueueTuple(); + webrtc::VideoFrame* _frame; + const webrtc::CodecSpecificInfo* _codecSpecificInfo; +}; + +class FrameQueue { + public: + FrameQueue() {} + + ~FrameQueue() {} + + void PushFrame(webrtc::VideoFrame* frame, + webrtc::CodecSpecificInfo* codecSpecificInfo = NULL); + FrameQueueTuple* PopFrame(); + bool Empty(); + + private: + webrtc::Mutex _queueRWLock; + std::queue _frameBufferQueue; +}; + +class VideoEncodeCompleteCallback : public webrtc::EncodedImageCallback { + public: + VideoEncodeCompleteCallback(FILE* encodedFile, + FrameQueue* frameQueue, + Benchmark& test) + : _encodedFile(encodedFile), + _frameQueue(frameQueue), + _test(test), + _encodedBytes(0) {} + + webrtc::EncodedImageCallback::Result OnEncodedImage( + const webrtc::EncodedImage& encoded_image, + const webrtc::CodecSpecificInfo* codec_specific_info) override; + + uint32_t EncodedBytes(); + + private: + FILE* _encodedFile; + FrameQueue* _frameQueue; + Benchmark& _test; + uint32_t _encodedBytes; +}; + +class Benchmark { + public: + friend class VideoEncodeCompleteCallback; + + public: + Benchmark(); + virtual void Perform(); + virtual bool IsSupported() = 0; + + protected: + Benchmark(std::string name, std::string description); + Benchmark(std::string name, + std::string description, + std::string resultsFileName, + std::string codecName); + virtual webrtc::VideoEncoder* GetNewEncoder(webrtc::Environment &env) = 0; + virtual void PerformNormalTest(); + virtual void CodecSpecific_InitBitrate(); + static const char* GetMagicStr() { return "#!benchmark1.0"; } + + double ActualBitRate(int nFrames) { + return 8.0 * _sumEncBytes / (nFrames / _inst.maxFramerate); + } + + webrtc::CodecSpecificInfo* CopyCodecSpecificInfo( + const webrtc::CodecSpecificInfo* codecSpecificInfo) const; + + bool Encode(); + + void Setup(); + + void Teardown(); + + void CodecSettings(int width, + int height, + uint32_t frameRate /*=30*/, + uint32_t bitRate /*=0*/) { + if (bitRate > 0) { + _bitRate = bitRate; + } else if (_bitRate == 0) { + _bitRate = 600; + } + _inst.codecType = webrtc::kVideoCodecH264; + _inst.maxFramerate = (unsigned char)frameRate; + _inst.minBitrate = (unsigned char)frameRate; + _inst.startBitrate = (int)_bitRate; + _inst.maxBitrate = 8000; + _inst.width = width; + _inst.height = height; + _inst.numberOfSimulcastStreams = 1; + _inst.simulcastStream[0].width = width; + _inst.simulcastStream[0].height = height; + _inst.simulcastStream[0].maxBitrate = 8000; + _inst.simulcastStream[0].minBitrate = _bitRate; + _inst.simulcastStream[0].targetBitrate = _bitRate; + _inst.simulcastStream[0].maxFramerate = frameRate; + _inst.simulcastStream[0].active = true; + _inst.SetScalabilityMode(webrtc::ScalabilityMode::kL1T1); + _inst.mode = webrtc::VideoCodecMode::kRealtimeVideo; + _inst.qpMax = 56; + _inst.SetFrameDropEnabled(true); + } + + double tGetTime() { + // return time in sec + return ((double)(webrtc::Clock::GetRealTimeClock()->TimeInMilliseconds()) / + 1000); + } + + virtual webrtc::CodecSpecificInfo* CreateEncoderSpecificInfo() const { + return NULL; + }; + + void UpdateEncodedBytes(int encodedBytes) { _sumEncBytes += encodedBytes; } + + const VideoSource* _target; + std::string _resultsFileName; + std::ofstream _results; + std::string _name; + std::string _description; + std::string _codecName; + std::string _inname; + std::string _outname; + webrtc::VideoEncoder* _encoder; + //webrtc::VideoDecoder* _decoder; + uint32_t _bitRate; + bool _appendNext = false; + int _framecnt; + int _encFrameCnt; + double _totalEncodeTime; + double _totalDecodeTime; + double _decodeCompleteTime; + double _encodeCompleteTime; + double _totalEncodePipeTime; + double _totalDecodePipeTime; + webrtc::VideoCodec _inst; + int _sumEncBytes; + + unsigned int _lengthSourceFrame = 0; + unsigned char* _sourceBuffer = nullptr; + + FILE* _encodedFile = nullptr; + unsigned int _lengthEncFrame = 0; + FrameQueueTuple* _frameToDecode = nullptr; + + FILE* _sourceFile = nullptr; + FILE* _decodedFile = nullptr; + + bool _hasReceivedPLI = false; + bool _waitForKey = false; + std::map _encodeTimes; + std::map _decodeTimes; + + bool _missingFrames = false; + std::list _signalSLI; + int _rttFrames = 0; + mutable bool _hasReceivedSLI = false; + mutable bool _hasReceivedRPSI = false; + uint8_t _pictureIdSLI = 0; + uint16_t _pictureIdRPSI = 0; + uint64_t _lastDecRefPictureId = 0; + uint64_t _lastDecPictureId = 0; + std::list _signalPLI; + webrtc::CpuWrapper* _cpu; + webrtc::Environment _env; +}; + +#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAWEWORK_BENCHMARK_H_ diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark_nvidia.cc b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark_nvidia.cc new file mode 100644 index 0000000..168988b --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark_nvidia.cc @@ -0,0 +1,51 @@ +#include "benchmark_nvidia.h" + +#include "api/environment/environment_factory.h" +#include "modules/video_coding/codecs/h264/include/h264.h" +#include "fileutils.h" + +using namespace webrtc; + +NvidiaBenchmark::NvidiaBenchmark() + : Benchmark("NvidiaBenchmark", + "Nvidia benchmark over a range of test cases", + webrtc::test::OutputPath() + "NvidiaBenchmark.txt", + "nvidia_bitstream_output.h264") {} + +NvidiaBenchmark::NvidiaBenchmark(std::string name, std::string description) + : Benchmark(name, + description, + webrtc::test::OutputPath() + "NvidiaBenchmark.txt", + "nvidia_bitstream_output.h264") {} + +NvidiaBenchmark::NvidiaBenchmark(std::string name, + std::string description, + std::string resultsFileName) + : Benchmark(name, description, resultsFileName, "nvidia_bitstream_output.h264") {} + + +VideoEncoder* NvidiaBenchmark::GetNewEncoder(webrtc::Environment &env) { + if (!NvidiaVideoEncoderFactory::IsSupported()) { + fprintf(stderr, "NVIDIA is not supported on this system.\n"); + return nullptr; + } + + if (!_factory) { + _factory = std::make_unique(); + } + std::map baselineParameters = { + {"profile-level-id", "42e01f"}, + {"level-asymmetry-allowed", "1"}, + {"packetization-mode", "1"}, + }; + auto format = SdpVideoFormat("H264", baselineParameters); + + auto enc = _factory->Create(env, format); + if (!enc) { + fprintf(stderr, "Failed to create H264 encoder.\n"); + return nullptr; + } + _encoder = std::move(enc); + + return _encoder.get(); +} \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark_nvidia.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark_nvidia.h new file mode 100644 index 0000000..3c92717 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark_nvidia.h @@ -0,0 +1,24 @@ +#include "benchmark.h" +#include "nvidia/nvidia_encoder_factory.h" + +class NvidiaBenchmark : public Benchmark { + public: + NvidiaBenchmark(); + NvidiaBenchmark(std::string name, std::string description); + NvidiaBenchmark(std::string name, + std::string description, + std::string resultsFileName); + + ~NvidiaBenchmark() {} + + bool IsSupported() override { + return webrtc::NvidiaVideoEncoderFactory::IsSupported(); + } + + protected: + webrtc::VideoEncoder* GetNewEncoder(webrtc::Environment &env) override; + + private: + std::unique_ptr _encoder; + std::unique_ptr _factory; +}; \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark_openh264.cc b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark_openh264.cc new file mode 100644 index 0000000..383f969 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark_openh264.cc @@ -0,0 +1,35 @@ +#include "benchmark_openh264.h" + +#include "api/environment/environment_factory.h" +#include "modules/video_coding/codecs/h264/include/h264.h" +#include "fileutils.h" + +using namespace webrtc; + +OpenH264Benchmark::OpenH264Benchmark() + : Benchmark("OpenH264Benchmark", + "OpenH264 benchmark over a range of test cases", + webrtc::test::OutputPath() + "OpenH264Benchmark.txt", + "openh264_bitstream_output.h264") {} + +OpenH264Benchmark::OpenH264Benchmark(std::string name, std::string description) + : Benchmark(name, + description, + webrtc::test::OutputPath() + "OpenH264Benchmark.txt", + "openh264_bitstream_output.h264") {} + +OpenH264Benchmark::OpenH264Benchmark(std::string name, + std::string description, + std::string resultsFileName) + : Benchmark(name, description, resultsFileName, "openh264_bitstream_output.h264") {} + +VideoEncoder* OpenH264Benchmark::GetNewEncoder(webrtc::Environment &env) { + auto enc = CreateH264Encoder(env); + if (!enc) { + fprintf(stderr, "Failed to create H264 encoder.\n"); + return nullptr; + } + _encoder = std::move(enc); + + return _encoder.get(); +} \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark_openh264.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark_openh264.h new file mode 100644 index 0000000..f1eb1ab --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark_openh264.h @@ -0,0 +1,22 @@ +#include "benchmark.h" + +class OpenH264Benchmark : public Benchmark { + public: + OpenH264Benchmark(); + OpenH264Benchmark(std::string name, std::string description); + OpenH264Benchmark(std::string name, + std::string description, + std::string resultsFileName); + + ~OpenH264Benchmark() {} + + bool IsSupported() override { + return true; + } + + protected: + webrtc::VideoEncoder* GetNewEncoder(webrtc::Environment &env) override; + + private: + std::unique_ptr _encoder; +}; \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark_vaapi.cc b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark_vaapi.cc new file mode 100644 index 0000000..7c3f097 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark_vaapi.cc @@ -0,0 +1,49 @@ +#include "benchmark_vaapi.h" + +#include "api/environment/environment_factory.h" +#include "modules/video_coding/codecs/h264/include/h264.h" +#include "fileutils.h" + +using namespace webrtc; + +VaapiBenchmark::VaapiBenchmark() + : Benchmark("VaapiBenchmark", + "VAAPI benchmark over a range of test cases", + webrtc::test::OutputPath() + "VaapiBenchmark.txt", + "vaapi_bitstream_output.h264") {} + +VaapiBenchmark::VaapiBenchmark(std::string name, std::string description) + : Benchmark(name, + description, + webrtc::test::OutputPath() + "VaapiBenchmark.txt", + "vaapi_bitstream_output.h264") {} + +VaapiBenchmark::VaapiBenchmark(std::string name, + std::string description, + std::string resultsFileName) + : Benchmark(name, description, resultsFileName, "vaapi_bitstream_output.h264") {} + +VideoEncoder* VaapiBenchmark::GetNewEncoder(webrtc::Environment &env) { + if (!VAAPIVideoEncoderFactory::IsSupported()) { + fprintf(stderr, "VAAPI is not supported on this system.\n"); + return nullptr; + } + if (!_factory) { + _factory = std::make_unique(); + } + std::map baselineParameters = { + {"profile-level-id", "4d0032"}, + {"level-asymmetry-allowed", "1"}, + {"packetization-mode", "1"}, + }; + auto format = SdpVideoFormat("H264", baselineParameters); + + auto enc = _factory->Create(env, format); + if (!enc) { + fprintf(stderr, "Failed to create H264 encoder.\n"); + return nullptr; + } + _encoder = std::move(enc); + + return _encoder.get(); +} \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark_vaapi.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark_vaapi.h new file mode 100644 index 0000000..95ec057 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/benchmark_vaapi.h @@ -0,0 +1,24 @@ +#include "benchmark.h" +#include "vaapi/vaapi_encoder_factory.h" + +class VaapiBenchmark : public Benchmark { + public: + VaapiBenchmark(); + VaapiBenchmark(std::string name, std::string description); + VaapiBenchmark(std::string name, + std::string description, + std::string resultsFileName); + + ~VaapiBenchmark() {} + + bool IsSupported() override { + return webrtc::VAAPIVideoEncoderFactory::IsSupported(); + } + + protected: + webrtc::VideoEncoder* GetNewEncoder(webrtc::Environment &env) override; + + private: + std::unique_ptr _encoder; + std::unique_ptr _factory; +}; \ No newline at end of file diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/cpu/cpu_linux.cc b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/cpu/cpu_linux.cc new file mode 100644 index 0000000..6d25b3d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/cpu/cpu_linux.cc @@ -0,0 +1,208 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "cpu_linux.h" + +#include +#include +#include +#include + +namespace webrtc { +CpuLinux::CpuLinux() + : m_oldBusyTime(0), + m_oldIdleTime(0), + m_oldBusyTimeMulti(NULL), + m_oldIdleTimeMulti(NULL), + m_idleArray(NULL), + m_busyArray(NULL), + m_resultArray(NULL), + m_numCores(0) { + const int result = GetNumCores(); + if (result != -1) { + m_numCores = result; + m_oldBusyTimeMulti = new long long[m_numCores]; + memset(m_oldBusyTimeMulti, 0, sizeof(long long) * m_numCores); + m_oldIdleTimeMulti = new long long[m_numCores]; + memset(m_oldIdleTimeMulti, 0, sizeof(long long) * m_numCores); + m_idleArray = new long long[m_numCores]; + memset(m_idleArray, 0, sizeof(long long) * m_numCores); + m_busyArray = new long long[m_numCores]; + memset(m_busyArray, 0, sizeof(long long) * m_numCores); + m_resultArray = new uint32_t[m_numCores]; + + GetData(m_oldBusyTime, m_oldIdleTime, m_busyArray, m_idleArray); + } +} + +CpuLinux::~CpuLinux() +{ + delete [] m_oldBusyTimeMulti; + delete [] m_oldIdleTimeMulti; + delete [] m_idleArray; + delete [] m_busyArray; + delete [] m_resultArray; +} + +int32_t CpuLinux::CpuUsage() +{ + uint32_t dummy = 0; + uint32_t* dummyArray = NULL; + return CpuUsageMultiCore(dummy, dummyArray); +} + +int32_t CpuLinux::CpuUsageMultiCore(uint32_t& numCores, + uint32_t*& coreArray) +{ + coreArray = m_resultArray; + numCores = m_numCores; + long long busy = 0; + long long idle = 0; + if (GetData(busy, idle, m_busyArray, m_idleArray) != 0) + return -1; + + long long deltaBusy = busy - m_oldBusyTime; + long long deltaIdle = idle - m_oldIdleTime; + m_oldBusyTime = busy; + m_oldIdleTime = idle; + + int retVal = -1; + if (deltaBusy + deltaIdle == 0) + { + retVal = 0; + } + else + { + retVal = (int)(100 * (deltaBusy) / (deltaBusy + deltaIdle)); + } + + if (coreArray == NULL) + { + return retVal; + } + + for (int32_t i = 0; i < m_numCores; i++) + { + deltaBusy = m_busyArray[i] - m_oldBusyTimeMulti[i]; + deltaIdle = m_idleArray[i] - m_oldIdleTimeMulti[i]; + m_oldBusyTimeMulti[i] = m_busyArray[i]; + m_oldIdleTimeMulti[i] = m_idleArray[i]; + if(deltaBusy + deltaIdle == 0) + { + coreArray[i] = 0; + } + else + { + coreArray[i] = (int)(100 * (deltaBusy) / (deltaBusy+deltaIdle)); + } + } + return retVal; +} + + +int CpuLinux::GetData(long long& busy, long long& idle, long long*& busyArray, + long long*& idleArray) +{ + FILE* fp = fopen("/proc/stat", "r"); + if (!fp) + { + return -1; + } + + char line[100]; + if (fgets(line, 100, fp) == NULL) { + fclose(fp); + return -1; + } + char firstWord[100]; + if (sscanf(line, "%s ", firstWord) != 1) { + fclose(fp); + return -1; + } + if (strncmp(firstWord, "cpu", 3) != 0) { + fclose(fp); + return -1; + } + char sUser[100]; + char sNice[100]; + char sSystem[100]; + char sIdle[100]; + if (sscanf(line, "%s %s %s %s %s ", + firstWord, sUser, sNice, sSystem, sIdle) != 5) { + fclose(fp); + return -1; + } + long long luser = atoll(sUser); + long long lnice = atoll(sNice); + long long lsystem = atoll(sSystem); + long long lidle = atoll (sIdle); + + busy = luser + lnice + lsystem; + idle = lidle; + for (int32_t i = 0; i < m_numCores; i++) + { + if (fgets(line, 100, fp) == NULL) { + fclose(fp); + return -1; + } + if (sscanf(line, "%s %s %s %s %s ", firstWord, sUser, sNice, sSystem, + sIdle) != 5) { + fclose(fp); + return -1; + } + luser = atoll(sUser); + lnice = atoll(sNice); + lsystem = atoll(sSystem); + lidle = atoll (sIdle); + busyArray[i] = luser + lnice + lsystem; + idleArray[i] = lidle; + } + fclose(fp); + return 0; +} + +int CpuLinux::GetNumCores() +{ + FILE* fp = fopen("/proc/stat", "r"); + if (!fp) + { + return -1; + } + // Skip first line + char line[100]; + if (!fgets(line, 100, fp)) + { + fclose(fp); + return -1; + } + int numCores = -1; + char firstWord[100]; + do + { + numCores++; + if (fgets(line, 100, fp)) + { + if (sscanf(line, "%s ", firstWord) != 1) { + firstWord[0] = '\0'; + } + } else { + break; + } + } while (strncmp(firstWord, "cpu", 3) == 0); + fclose(fp); + return numCores; +} + +CpuWrapper* CpuWrapper::CreateCpu() +{ + return new CpuLinux(); +} + +} // namespace webrtc diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/cpu/cpu_linux.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/cpu/cpu_linux.h new file mode 100644 index 0000000..369cc31 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/cpu/cpu_linux.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_SYSTEM_WRAPPERS_SOURCE_CPU_LINUX_H_ +#define WEBRTC_SYSTEM_WRAPPERS_SOURCE_CPU_LINUX_H_ + +#include "cpu_wrapper.h" + +namespace webrtc { +class CpuLinux : public CpuWrapper { + public: + CpuLinux(); + virtual ~CpuLinux(); + + int32_t CpuUsage() override; + int32_t CpuUsage(int8_t* pProcessName, uint32_t length) override { return 0; } + int32_t CpuUsage(uint32_t dwProcessID) override { return 0; } + + int32_t CpuUsageMultiCore(uint32_t& numCores, uint32_t*& array) override; + + void Reset() override { return; } + void Stop() override { return; } + + int GetNumCores() override; + + private: + int GetData(long long& busy, + long long& idle, + long long*& busyArray, + long long*& idleArray); + + + long long m_oldBusyTime; + long long m_oldIdleTime; + + long long* m_oldBusyTimeMulti; + long long* m_oldIdleTimeMulti; + + long long* m_idleArray; + long long* m_busyArray; + uint32_t* m_resultArray; + uint32_t m_numCores; +}; +} // namespace webrtc + +#endif // WEBRTC_SYSTEM_WRAPPERS_SOURCE_CPU_LINUX_H_ diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/cpu/cpu_wrapper.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/cpu/cpu_wrapper.h new file mode 100644 index 0000000..df3c209 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/cpu/cpu_wrapper.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_SYSTEM_WRAPPERS_INTERFACE_CPU_WRAPPER_H_ +#define WEBRTC_SYSTEM_WRAPPERS_INTERFACE_CPU_WRAPPER_H_ + +#include + +namespace webrtc { +class CpuWrapper +{ +public: + static CpuWrapper* CreateCpu(); + virtual ~CpuWrapper() {} + + // Returns the average CPU usage for all processors. The CPU usage can be + // between and including 0 to 100 (%) + virtual int32_t CpuUsage() = 0; + virtual int32_t CpuUsage(int8_t* processName, + uint32_t length) = 0; + virtual int32_t CpuUsage(uint32_t dwProcessID) = 0; + + // The CPU usage per core is returned in cpu_usage. The CPU can be between + // and including 0 to 100 (%) + // Note that the pointer passed as cpu_usage is redirected to a local member + // of the CPU wrapper. + // numCores is the number of cores in the cpu_usage array. + // The return value is -1 for failure or 0-100, indicating the average + // CPU usage across all cores. + // Note: on some OSs this class is initialized lazy. This means that it + // might not yet be possible to retrieve any CPU metrics. When this happens + // the return value will be zero (indicating that there is not a failure), + // numCores will be 0 and cpu_usage will be set to NULL (indicating that + // no metrics are available yet). Once the initialization is completed, + // which can take in the order of seconds, CPU metrics can be retrieved. + virtual int32_t CpuUsageMultiCore(uint32_t& numCores, + uint32_t*& cpu_usage) = 0; + + virtual void Reset() = 0; + virtual void Stop() = 0; + + virtual int GetNumCores() = 0; + +protected: + CpuWrapper() {} +}; +} // namespace webrtc +#endif // WEBRTC_SYSTEM_WRAPPERS_INTERFACE_CPU_WRAPPER_H_ diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/fileutils.cc b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/fileutils.cc new file mode 100644 index 0000000..4e49980 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/fileutils.cc @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "fileutils.h" + +#ifdef WIN32 +#include +#define GET_CURRENT_DIR _getcwd +#else +#include +#define GET_CURRENT_DIR getcwd +#endif + +#include // To check for directory existence. +#ifndef S_ISDIR // Not defined in stat.h on Windows. +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif + +#include +#include + + +namespace webrtc { +namespace test { + +#ifdef WIN32 +static const char* kPathDelimiter = "\\"; +#else +static const char* kPathDelimiter = "/"; +#endif + +#ifdef WEBRTC_ANDROID +static const char* kRootDirName = "/sdcard/"; +static const char* kResourcesDirName = "resources"; +#else +// The file we're looking for to identify the project root dir. +static const char* kProjectRootFileName = "h264_benchmark"; +static const char* kOutputDirName = "out"; +static const char* kFallbackPath = "./"; +static const char* kResourcesDirName = "resources"; +#endif +const char* kCannotFindProjectRootDir = "ERROR_CANNOT_FIND_PROJECT_ROOT_DIR"; + +namespace { +char relative_dir_path[FILENAME_MAX]; +bool relative_dir_path_set = false; +} + +void SetExecutablePath(const std::string& path) { + std::string working_dir = WorkingDir(); + std::string temp_path = path; + + // Handle absolute paths; convert them to relative paths to the working dir. + if (path.find(working_dir) != std::string::npos) { + temp_path = path.substr(working_dir.length() + 1); + } + // Trim away the executable name; only store the relative dir path. + temp_path = temp_path.substr(0, temp_path.find_last_of(kPathDelimiter)); + strncpy(relative_dir_path, temp_path.c_str(), FILENAME_MAX); + relative_dir_path_set = true; +} + +bool FileExists(std::string& file_name) { + struct stat file_info = {0}; + return stat(file_name.c_str(), &file_info) == 0; +} + +#ifdef WEBRTC_ANDROID + +std::string ProjectRootPath() { + return kRootDirName; +} + +std::string OutputPath() { + return kRootDirName; +} + +std::string WorkingDir() { + return kRootDirName; +} + +#else // WEBRTC_ANDROID + +std::string ProjectRootPath() { + std::string path = WorkingDir(); + if (path == kFallbackPath) { + return kCannotFindProjectRootDir; + } + if (relative_dir_path_set) { + path = path + kPathDelimiter + relative_dir_path; + } + // Check for our file that verifies the root dir. + size_t path_delimiter_index = path.find_last_of(kPathDelimiter); + while (path_delimiter_index != std::string::npos) { + std::string root_filename = path + kPathDelimiter + kProjectRootFileName; + if (FileExists(root_filename)) { + return path + kPathDelimiter; + } + // Move up one directory in the directory tree. + path = path.substr(0, path_delimiter_index); + path_delimiter_index = path.find_last_of(kPathDelimiter); + } + // Reached the root directory. + fprintf(stderr, "Cannot find project root directory!\n"); + return kCannotFindProjectRootDir; +} + +std::string OutputPath() { + std::string path = ProjectRootPath(); + if (path == kCannotFindProjectRootDir) { + return kFallbackPath; + } + path += kOutputDirName; + if (!CreateDirectory(path)) { + return kFallbackPath; + } + return path + kPathDelimiter; +} + +std::string WorkingDir() { + char path_buffer[FILENAME_MAX]; + if (!GET_CURRENT_DIR(path_buffer, sizeof(path_buffer))) { + fprintf(stderr, "Cannot get current directory!\n"); + return kFallbackPath; + } else { + return std::string(path_buffer); + } +} + +#endif // !WEBRTC_ANDROID + +bool CreateDirectory(std::string directory_name) { + struct stat path_info = {0}; + // Check if the path exists already: + if (stat(directory_name.c_str(), &path_info) == 0) { + if (!S_ISDIR(path_info.st_mode)) { + fprintf(stderr, "Path %s exists but is not a directory! Remove this " + "file and re-run to create the directory.\n", + directory_name.c_str()); + return false; + } + } else { +#ifdef WIN32 + return _mkdir(directory_name.c_str()) == 0; +#else + return mkdir(directory_name.c_str(), S_IRWXU | S_IRWXG | S_IRWXO) == 0; +#endif + } + return true; +} + +std::string ResourcePath(std::string name, std::string extension) { + std::string platform = "win"; +#ifdef WEBRTC_LINUX + platform = "linux"; +#endif // WEBRTC_LINUX +#ifdef WEBRTC_MAC + platform = "mac"; +#endif // WEBRTC_MAC + +#ifdef WEBRTC_ARCH_64_BITS + std::string architecture = "64"; +#else + std::string architecture = "32"; +#endif // WEBRTC_ARCH_64_BITS + + std::string resources_path = ProjectRootPath() + kResourcesDirName + + kPathDelimiter; + std::string resource_file = resources_path + name + "_" + platform + "_" + + architecture + "." + extension; + if (FileExists(resource_file)) { + return resource_file; + } + // Try without architecture. + resource_file = resources_path + name + "_" + platform + "." + extension; + if (FileExists(resource_file)) { + return resource_file; + } + // Try without platform. + resource_file = resources_path + name + "_" + architecture + "." + extension; + if (FileExists(resource_file)) { + return resource_file; + } + + // Fall back on name without architecture or platform. + return resources_path + name + "." + extension; +} + +size_t GetFileSize(std::string filename) { + FILE* f = fopen(filename.c_str(), "rb"); + size_t size = 0; + if (f != NULL) { + if (fseek(f, 0, SEEK_END) == 0) { + size = ftell(f); + } + fclose(f); + } + return size; +} + +} // namespace test +} // namespace webrtc diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/fileutils.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/fileutils.h new file mode 100644 index 0000000..e642a5f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/fileutils.h @@ -0,0 +1,152 @@ +/* + * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include + +// File utilities for testing purposes. +// +// The ProjectRootPath() method is a convenient way of getting an absolute +// path to the project source tree root directory. Using this, it is easy to +// refer to test resource files in a portable way. +// +// Notice that even if Windows platforms use backslash as path delimiter, it is +// also supported to use slash, so there's no need for #ifdef checks in test +// code for setting up the paths to the resource files. +// +// Example use: +// Assume we have the following code being used in a test source file: +// const std::string kInputFile = webrtc::test::ProjectRootPath() + +// "test/data/voice_engine/audio_long16.wav"; +// // Use the kInputFile for the tests... +// +// Then here's some example outputs for different platforms: +// Linux: +// * Source tree located in /home/user/webrtc/trunk +// * Test project located in /home/user/webrtc/trunk/src/testproject +// * Test binary compiled as: +// /home/user/webrtc/trunk/out/Debug/testproject_unittests +// Then ProjectRootPath() will return /home/user/webrtc/trunk/ no matter if +// the test binary is executed from standing in either of: +// /home/user/webrtc/trunk +// or +// /home/user/webrtc/trunk/out/Debug +// (or any other directory below the trunk for that matter). +// +// Windows: +// * Source tree located in C:\Users\user\webrtc\trunk +// * Test project located in C:\Users\user\webrtc\trunk\src\testproject +// * Test binary compiled as: +// C:\Users\user\webrtc\trunk\src\testproject\Debug\testproject_unittests.exe +// Then ProjectRootPath() will return C:\Users\user\webrtc\trunk\ when the +// test binary is executed from inside Visual Studio. +// It will also return the same path if the test is executed from a command +// prompt standing in C:\Users\user\webrtc\trunk\src\testproject\Debug +// +// Mac: +// * Source tree located in /Users/user/webrtc/trunk +// * Test project located in /Users/user/webrtc/trunk/src/testproject +// * Test binary compiled as: +// /Users/user/webrtc/trunk/xcodebuild/Debug/testproject_unittests +// Then ProjectRootPath() will return /Users/user/webrtc/trunk/ no matter if +// the test binary is executed from standing in either of: +// /Users/user/webrtc/trunk +// or +// /Users/user/webrtc/trunk/out/Debug +// (or any other directory below the trunk for that matter). + +#ifndef WEBRTC_TEST_TESTSUPPORT_FILEUTILS_H_ +#define WEBRTC_TEST_TESTSUPPORT_FILEUTILS_H_ + +#include + +namespace webrtc { +namespace test { + +// This is the "directory" returned if the ProjectPath() function fails +// to find the project root. +extern const char* kCannotFindProjectRootDir; + +// Finds the root dir of the project, to be able to set correct paths to +// resource files used by tests. +// The implementation is simple: it just looks for the file defined by +// kProjectRootFileName, starting in the current directory (the working +// directory) and then steps upward until it is found (or it is at the root of +// the file system). +// If the current working directory is above the project root dir, it will not +// be found. +// +// If symbolic links occur in the path they will be resolved and the actual +// directory will be returned. +// +// Returns the absolute path to the project root dir (usually the trunk dir) +// WITH a trailing path delimiter. +// If the project root is not found, the string specified by +// kCannotFindProjectRootDir is returned. +std::string ProjectRootPath(); + +// Creates and returns the absolute path to the output directory where log files +// and other test artifacts should be put. The output directory is generally a +// directory named "out" at the top-level of the project, i.e. a subfolder to +// the path returned by ProjectRootPath(). The exception is Android where we use +// /sdcard/ instead. +// +// Details described for ProjectRootPath() apply here too. +// +// Returns the path WITH a trailing path delimiter. If the project root is not +// found, the current working directory ("./") is returned as a fallback. +std::string OutputPath(); + +// Returns a path to a resource file for the currently executing platform. +// Adapts to what filenames are currently present in the +// [project-root]/resources/ dir. +// Returns an absolute path according to this priority list (the directory +// part of the path is left out for readability): +// 1. [name]_[platform]_[architecture].[extension] +// 2. [name]_[platform].[extension] +// 3. [name]_[architecture].[extension] +// 4. [name].[extension] +// Where +// * platform is either of "win", "mac" or "linux". +// * architecture is either of "32" or "64". +// +// Arguments: +// name - Name of the resource file. If a plain filename (no directory path) +// is supplied, the file is assumed to be located in resources/ +// If a directory path is prepended to the filename, a subdirectory +// hierarchy reflecting that path is assumed to be present. +// extension - File extension, without the dot, i.e. "bmp" or "yuv". +std::string ResourcePath(std::string name, std::string extension); + +// Gets the current working directory for the executing program. +// Returns "./" if for some reason it is not possible to find the working +// directory. +std::string WorkingDir(); + +// Creates a directory if it not already exists. +// Returns true if successful. Will print an error message to stderr and return +// false if a file with the same name already exists. +bool CreateDirectory(std::string directory_name); + +// File size of the supplied file in bytes. Will return 0 if the file is +// empty or if the file does not exist/is readable. +size_t GetFileSize(std::string filename); + +// Sets the executable path, i.e. the path to the executable that is being used +// when launching it. This is usually the path relative to the working directory +// but can also be an absolute path. The intention with this function is to pass +// the argv[0] being sent into the main function to make it possible for +// fileutils.h to find the correct project paths even when the working directory +// is outside the project tree (which happens in some cases). +void SetExecutablePath(const std::string& path_to_executable); + +} // namespace test +} // namespace webrtc + +#endif // WEBRTC_TEST_TESTSUPPORT_FILEUTILS_H_ diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/test_main.cc b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/test_main.cc new file mode 100644 index 0000000..6b8209f --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/test_main.cc @@ -0,0 +1,20 @@ +#include "benchmark_nvidia.h" +#include "benchmark_openh264.h" +#include "benchmark_vaapi.h" +#include "stdio.h" + +int main(int argc, char** argv) { + + std::vector benchmarks; + benchmarks.push_back(new NvidiaBenchmark()); + //benchmarks.push_back(new VaapiBenchmark()); + benchmarks.push_back(new OpenH264Benchmark()); + + for (auto benchmark : benchmarks) { + if (benchmark->IsSupported()) { + benchmark->Perform(); + } + } + + return 0; +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/video_source.cc b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/video_source.cc new file mode 100644 index 0000000..8273c4d --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/video_source.cc @@ -0,0 +1,432 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "video_source.h" + +#include + +#include "fileutils.h" + +#define ASSERT_TRUE(condition) \ + do { \ + if (!(condition)) { \ + fprintf(stderr, "Assertion failed: %s\n", #condition); \ + abort(); \ + } \ + } while (0) + +VideoSource::VideoSource() +: +_fileName(webrtc::test::ProjectRootPath() + "resources/foreman_cif.yuv"), +_width(352), +_height(288), +_type(webrtc::VideoType::kI420), +_frameRate(30) +{ +} + +VideoSource::VideoSource(std::string fileName, VideoSize size, + int frameRate /*= 30*/, webrtc::VideoType type /*= webrtc::kI420*/) +: +_fileName(fileName), +_type(type), +_frameRate(frameRate) +{ + assert(size != kUndefined && size != kNumberOfVideoSizes); + assert(type != webrtc::VideoType::kUnknown); + assert(frameRate > 0); + if (GetWidthHeight(size, _width, _height) != 0) { + assert(false); + } +} + +VideoSource::VideoSource(std::string fileName, int width, int height, + int frameRate /*= 30*/, webrtc::VideoType type /*= webrtc::kI420*/) +: +_fileName(fileName), +_width(width), +_height(height), +_type(type), +_frameRate(frameRate) +{ + assert(width > 0); + assert(height > 0); + assert(type != webrtc::VideoType::kUnknown); + assert(frameRate > 0); +} + +VideoSize +VideoSource::GetSize() const +{ + return GetSize(_width, _height); +} + +VideoSize +VideoSource::GetSize(uint16_t width, uint16_t height) +{ + if(width == 128 && height == 96) + { + return kSQCIF; + }else if(width == 160 && height == 120) + { + return kQQVGA; + }else if(width == 176 && height == 144) + { + return kQCIF; + }else if(width == 320 && height == 240) + { + return kQVGA; + }else if(width == 352 && height == 288) + { + return kCIF; + }else if(width == 640 && height == 480) + { + return kVGA; + }else if(width == 720 && height == 480) + { + return kNTSC; + }else if(width == 704 && height == 576) + { + return k4CIF; + }else if(width == 800 && height == 600) + { + return kSVGA; + }else if(width == 960 && height == 720) + { + return kHD; + }else if(width == 1024 && height == 768) + { + return kXGA; + }else if(width == 1440 && height == 1080) + { + return kFullHD; + }else if(width == 400 && height == 240) + { + return kWQVGA; + }else if(width == 800 && height == 480) + { + return kWVGA; + }else if(width == 1280 && height == 720) + { + return kWHD; + }else if(width == 1920 && height == 1080) + { + return kWFullHD; + } + return kUndefined; +} + +unsigned int +VideoSource::GetFrameLength() const +{ + return webrtc::CalcBufferSize(_type, _width, _height); +} + +const char* +VideoSource::GetMySizeString() const +{ + return VideoSource::GetSizeString(GetSize()); +} + +const char* +VideoSource::GetSizeString(VideoSize size) +{ + switch (size) + { + case kSQCIF: + return "SQCIF"; + case kQQVGA: + return "QQVGA"; + case kQCIF: + return "QCIF"; + case kQVGA: + return "QVGA"; + case kCIF: + return "CIF"; + case kVGA: + return "VGA"; + case kNTSC: + return "NTSC"; + case k4CIF: + return "4CIF"; + case kSVGA: + return "SVGA"; + case kHD: + return "HD"; + case kXGA: + return "XGA"; + case kFullHD: + return "Full_HD"; + case kWQVGA: + return "WQVGA"; + case kWHD: + return "WHD"; + case kWFullHD: + return "WFull_HD"; + default: + return "Undefined"; + } +} + +std::string +VideoSource::GetFilePath() const +{ + size_t slashPos = _fileName.find_last_of("/\\"); + if (slashPos == std::string::npos) + { + return "."; + } + + return _fileName.substr(0, slashPos); +} + +std::string +VideoSource::GetName() const +{ + // Remove path. + size_t slashPos = _fileName.find_last_of("/\\"); + if (slashPos == std::string::npos) + { + slashPos = 0; + } + else + { + slashPos++; + } + + // Remove extension and underscored suffix if it exists. + return _fileName.substr(slashPos, std::min(_fileName.find_last_of("_"), + _fileName.find_last_of(".")) - slashPos); +} + +void +VideoSource::Convert(const VideoSource &target, bool force /* = false */) const +{ + // Ensure target rate is less than or equal to source + // (i.e. we are only temporally downsampling). + ASSERT_TRUE(target.GetFrameRate() <= _frameRate); + // Only supports YUV420 currently. + ASSERT_TRUE(_type == webrtc::VideoType::kI420 && target.GetType() == webrtc::VideoType::kI420); + if (!force && (FileExists(target.GetFileName().c_str()) || + (target.GetWidth() == _width && target.GetHeight() == _height && target.GetFrameRate() == _frameRate))) + { + // Assume that the filename uniquely defines the content. + // If the file already exists, it is the correct file. + return; + } + FILE *inFile = NULL; + FILE *outFile = NULL; + + inFile = fopen(_fileName.c_str(), "rb"); + ASSERT_TRUE(inFile != NULL); + + outFile = fopen(target.GetFileName().c_str(), "wb"); + ASSERT_TRUE(outFile != NULL); + + FrameDropper fd; + fd.SetFrameRate(target.GetFrameRate(), _frameRate); + + const size_t lengthOutFrame = webrtc::CalcBufferSize(target.GetType(), + target.GetWidth(), target.GetHeight()); + ASSERT_TRUE(lengthOutFrame > 0); + unsigned char *outFrame = new unsigned char[lengthOutFrame]; + + const size_t lengthInFrame = webrtc::CalcBufferSize(_type, _width, _height); + ASSERT_TRUE(lengthInFrame > 0); + unsigned char *inFrame = new unsigned char[lengthInFrame]; + + while (fread(inFrame, 1, lengthInFrame, inFile) == lengthInFrame) + { + if (!fd.DropFrame()) + { + ASSERT_TRUE(target.GetWidth() == _width && + target.GetHeight() == _height); + // Add video interpolator here! + if (fwrite(outFrame, 1, lengthOutFrame, + outFile) != lengthOutFrame) { + return; + } + } + } + + delete inFrame; + delete outFrame; + fclose(inFile); + fclose(outFile); +} + +bool VideoSource::FileExists(const char* fileName) +{ + FILE* fp = NULL; + fp = fopen(fileName, "rb"); + if(fp != NULL) + { + fclose(fp); + return true; + } + return false; +} + + +int +VideoSource::GetWidthHeight( VideoSize size, int & width, int& height) +{ + switch(size) + { + case kSQCIF: + width = 128; + height = 96; + return 0; + case kQQVGA: + width = 160; + height = 120; + return 0; + case kQCIF: + width = 176; + height = 144; + return 0; + case kCGA: + width = 320; + height = 200; + return 0; + case kQVGA: + width = 320; + height = 240; + return 0; + case kSIF: + width = 352; + height = 240; + return 0; + case kWQVGA: + width = 400; + height = 240; + return 0; + case kCIF: + width = 352; + height = 288; + return 0; + case kW288p: + width = 512; + height = 288; + return 0; + case k448p: + width = 576; + height = 448; + return 0; + case kVGA: + width = 640; + height = 480; + return 0; + case k432p: + width = 720; + height = 432; + return 0; + case kW432p: + width = 768; + height = 432; + return 0; + case k4SIF: + width = 704; + height = 480; + return 0; + case kW448p: + width = 768; + height = 448; + return 0; + case kNTSC: + width = 720; + height = 480; + return 0; + case kFW448p: + width = 800; + height = 448; + return 0; + case kWVGA: + width = 800; + height = 480; + return 0; + case k4CIF: + width = 704; + height = 576; + return 0; + case kSVGA: + width = 800; + height = 600; + return 0; + case kW544p: + width = 960; + height = 544; + return 0; + case kW576p: + width = 1024; + height = 576; + return 0; + case kHD: + width = 960; + height = 720; + return 0; + case kXGA: + width = 1024; + height = 768; + return 0; + case kFullHD: + width = 1440; + height = 1080; + return 0; + case kWHD: + width = 1280; + height = 720; + return 0; + case kWFullHD: + width = 1920; + height = 1080; + return 0; + default: + return -1; + } +} + +FrameDropper::FrameDropper() +: +_dropsBetweenRenders(0), +_frameCounter(0) +{ +} + +bool +FrameDropper::DropFrame() +{ + _frameCounter++; + if (_frameCounter > _dropsBetweenRenders) + { + _frameCounter = 0; + return false; + } + return true; +} + +unsigned int +FrameDropper::DropsBetweenRenders() +{ + return _dropsBetweenRenders; +} + +void +FrameDropper::SetFrameRate(double frameRate, double maxFrameRate) +{ + if (frameRate >= 1.0) + { + _dropsBetweenRenders = static_cast(maxFrameRate / frameRate + 0.5) - 1; + } + else + { + _dropsBetweenRenders = 0; + } +} diff --git a/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/video_source.h b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/video_source.h new file mode 100644 index 0000000..c9da346 --- /dev/null +++ b/fluxer_desktop/native/webrtc-sender/vendor/webrtc-sys/test/video_source.h @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#ifndef WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_VIDEO_SOURCE_H_ +#define WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_VIDEO_SOURCE_H_ + +#include +#include "common_video/libyuv/include/webrtc_libyuv.h" + +enum VideoSize + { + kUndefined, + kSQCIF, // 128*96 = 12 288 + kQQVGA, // 160*120 = 19 200 + kQCIF, // 176*144 = 25 344 + kCGA, // 320*200 = 64 000 + kQVGA, // 320*240 = 76 800 + kSIF, // 352*240 = 84 480 + kWQVGA, // 400*240 = 96 000 + kCIF, // 352*288 = 101 376 + kW288p, // 512*288 = 147 456 (WCIF) + k448p, // 576*448 = 281 088 + kVGA, // 640*480 = 307 200 + k432p, // 720*432 = 311 040 + kW432p, // 768*432 = 331 776 + k4SIF, // 704*480 = 337 920 + kW448p, // 768*448 = 344 064 + kNTSC, // 720*480 = 345 600 + kFW448p, // 800*448 = 358 400 + kWVGA, // 800*480 = 384 000 + k4CIF, // 704�576 = 405 504 + kSVGA, // 800*600 = 480 000 + kW544p, // 960*544 = 522 240 + kW576p, // 1024*576 = 589 824 (W4CIF) + kHD, // 960*720 = 691 200 + kXGA, // 1024*768 = 786 432 + kWHD, // 1280*720 = 921 600 + kFullHD, // 1440*1080 = 1 555 200 + kWFullHD, // 1920*1080 = 2 073 600 + + kNumberOfVideoSizes + }; + +class VideoSource +{ +public: + VideoSource(); + VideoSource(std::string fileName, VideoSize size, int frameRate = 30, + webrtc::VideoType type = webrtc::VideoType::kI420); + VideoSource(std::string fileName, int width, int height, int frameRate = 30, + webrtc::VideoType type = webrtc::VideoType::kI420); + + std::string GetFileName() const { return _fileName; } + int GetWidth() const { return _width; } + int GetHeight() const { return _height; } + webrtc::VideoType GetType() const { return _type; } + int GetFrameRate() const { return _frameRate; } + + // Returns the file path without a trailing slash. + std::string GetFilePath() const; + + // Returns the filename with the path (including the leading slash) removed. + std::string GetName() const; + + VideoSize GetSize() const; + static VideoSize GetSize(uint16_t width, uint16_t height); + unsigned int GetFrameLength() const; + + // Returns a human-readable size string. + static const char* GetSizeString(VideoSize size); + const char* GetMySizeString() const; + + // Opens the video source, converting and writing to the specified target. + // If force is true, the conversion will be done even if the target file + // already exists. + void Convert(const VideoSource& target, bool force = false) const; + static bool FileExists(const char* fileName); +private: + static int GetWidthHeight( VideoSize size, int& width, int& height); + std::string _fileName; + int _width; + int _height; + webrtc::VideoType _type; + int _frameRate; +}; + +class FrameDropper +{ +public: + FrameDropper(); + bool DropFrame(); + unsigned int DropsBetweenRenders(); + void SetFrameRate(double frameRate, double maxFrameRate); + +private: + unsigned int _dropsBetweenRenders; + unsigned int _frameCounter; +}; + + +#endif // WEBRTC_MODULES_VIDEO_CODING_CODECS_TEST_FRAMEWORK_VIDEO_SOURCE_H_ + diff --git a/fluxer_desktop/native/win-clipboard/Cargo.lock b/fluxer_desktop/native/win-clipboard/Cargo.lock new file mode 100644 index 0000000..48a305e --- /dev/null +++ b/fluxer_desktop/native/win-clipboard/Cargo.lock @@ -0,0 +1,287 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "fluxer_win_clipboard" +version = "0.0.0" +dependencies = [ + "napi", + "napi-build", + "napi-derive", + "windows-sys", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] diff --git a/fluxer_desktop/native/win-clipboard/Cargo.toml b/fluxer_desktop/native/win-clipboard/Cargo.toml new file mode 100644 index 0000000..2d28501 --- /dev/null +++ b/fluxer_desktop/native/win-clipboard/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "fluxer_win_clipboard" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8"]} +napi-derive = "3.5.6" + +[target.'cfg(target_os = "windows")'.dependencies] +windows-sys = {version = "0.61.2", features = [ + "Win32_Foundation", + "Win32_System_DataExchange", + "Win32_System_Memory", + "Win32_System_Ole", + "Win32_UI_WindowsAndMessaging", +]} + +[build-dependencies] +napi-build = "2.3.2" diff --git a/fluxer_desktop/native/win-clipboard/build.rs b/fluxer_desktop/native/win-clipboard/build.rs new file mode 100644 index 0000000..d45de40 --- /dev/null +++ b/fluxer_desktop/native/win-clipboard/build.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + napi_build::setup(); +} diff --git a/fluxer_desktop/native/win-clipboard/index.d.ts b/fluxer_desktop/native/win-clipboard/index.d.ts new file mode 100644 index 0000000..e0a3723 --- /dev/null +++ b/fluxer_desktop/native/win-clipboard/index.d.ts @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +export declare function writeFileReferenceToClipboard(filePath: string): Promise; + +export declare const loadError: Error | null; diff --git a/fluxer_desktop/native/win-clipboard/index.js b/fluxer_desktop/native/win-clipboard/index.js new file mode 100644 index 0000000..6bb1422 --- /dev/null +++ b/fluxer_desktop/native/win-clipboard/index.js @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync} = require('node:fs'); +const {join, sep} = require('node:path'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); +const MODULE_NAME = '@fluxer/win-clipboard'; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName() { + if (process.platform !== 'win32') { + throw new Error(`@fluxer/win-clipboard is only supported on Windows, got ${process.platform}`); + } + switch (process.arch) { + case 'x64': + return 'win-clipboard.win32-x64-msvc.node'; + case 'arm64': + return 'win-clipboard.win32-arm64-msvc.node'; + default: + throw new Error(`Unsupported Windows architecture: ${process.arch}`); + } +} + +let binding = null; +let loadError = null; + +if (process.platform === 'win32') { + try { + const nativeRoot = resolveNativeRoot(); + const nativePath = join(nativeRoot, nativeFileName()); + const loaded = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + probe: false, + }); + binding = loaded.binding; + loadError = loaded.loadError; + if (loadError) throw loadError; + } catch (error) { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: 'native loader threw before binding load completed', + cause: error, + }); + throw loadError; + } +} + +module.exports = { + writeFileReferenceToClipboard: binding ? binding.writeFileReferenceToClipboard : null, + loadError, +}; diff --git a/fluxer_desktop/native/win-clipboard/loader-diagnostics.cjs b/fluxer_desktop/native/win-clipboard/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/win-clipboard/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/win-clipboard/package.json b/fluxer_desktop/native/win-clipboard/package.json new file mode 100644 index 0000000..7122be2 --- /dev/null +++ b/fluxer_desktop/native/win-clipboard/package.json @@ -0,0 +1,27 @@ +{ + "name": "@fluxer/win-clipboard", + "version": "0.0.0", + "description": "", + "private": true, + "license": "AGPL-3.0-or-later", + "os": [ + "win32" + ], + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "win-clipboard.win32-x64-msvc.node", + "win-clipboard.win32-arm64-msvc.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "cargo test --manifest-path Cargo.toml" + } +} diff --git a/fluxer_desktop/native/win-clipboard/src/lib.rs b/fluxer_desktop/native/win-clipboard/src/lib.rs new file mode 100644 index 0000000..134e906 --- /dev/null +++ b/fluxer_desktop/native/win-clipboard/src/lib.rs @@ -0,0 +1,302 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use napi::Task; +use napi::bindgen_prelude::{AsyncTask, Env, Error, Result, Status}; +use napi_derive::napi; + +#[cfg(any(target_os = "windows", test))] +const DROPFILES_HEADER_SIZE: usize = 20; +#[cfg(any(target_os = "windows", test))] +const DROPFILES_PFILES_OFFSET: usize = 0; +#[cfg(any(target_os = "windows", test))] +const DROPFILES_FWIDE_OFFSET: usize = 16; + +pub struct WriteFileReferenceTask { + path: String, +} + +#[napi(js_name = "writeFileReferenceToClipboard")] +pub fn write_file_reference_to_clipboard( + file_path: String, +) -> Result> { + validate_file_path(&file_path)?; + Ok(AsyncTask::new(WriteFileReferenceTask { path: file_path })) +} + +impl Task for WriteFileReferenceTask { + type Output = (); + type JsValue = (); + + fn compute(&mut self) -> Result { + platform::write_file_reference(&self.path) + } + + fn resolve(&mut self, _env: Env, _output: Self::Output) -> Result { + Ok(()) + } +} + +fn validate_file_path(path: &str) -> Result<()> { + if path.is_empty() { + return Err(Error::new(Status::InvalidArg, "path must be non-empty")); + } + if path.as_bytes().contains(&0) { + return Err(Error::new( + Status::InvalidArg, + "path must not contain NUL bytes", + )); + } + Ok(()) +} + +#[cfg(any(target_os = "windows", test))] +fn build_hdrop_payload(path: &str) -> Vec { + let utf16: Vec = path.encode_utf16().collect(); + let mut payload = vec![0; DROPFILES_HEADER_SIZE + ((utf16.len() + 2) * size_of::())]; + + payload[DROPFILES_PFILES_OFFSET..DROPFILES_PFILES_OFFSET + size_of::()] + .copy_from_slice(&(DROPFILES_HEADER_SIZE as u32).to_le_bytes()); + payload[DROPFILES_FWIDE_OFFSET..DROPFILES_FWIDE_OFFSET + size_of::()] + .copy_from_slice(&1_i32.to_le_bytes()); + + let mut offset = DROPFILES_HEADER_SIZE; + for code_unit in utf16 { + payload[offset..offset + size_of::()].copy_from_slice(&code_unit.to_le_bytes()); + offset += size_of::(); + } + payload +} + +#[cfg(target_os = "windows")] +mod platform { + use super::build_hdrop_payload; + use napi::bindgen_prelude::{Error, Result, Status}; + use std::ffi::c_void; + use std::ptr::{copy_nonoverlapping, null_mut}; + use windows_sys::Win32::Foundation::{GetLastError, GlobalFree, HGLOBAL, HWND}; + use windows_sys::Win32::System::DataExchange::{ + CloseClipboard, EmptyClipboard, OpenClipboard, SetClipboardData, + }; + use windows_sys::Win32::System::Memory::{ + GMEM_MOVEABLE, GMEM_ZEROINIT, GlobalAlloc, GlobalLock, GlobalUnlock, + }; + use windows_sys::Win32::System::Ole::CF_HDROP; + use windows_sys::Win32::UI::WindowsAndMessaging::{CreateWindowExW, DestroyWindow}; + + const STATIC_WINDOW_CLASS: [u16; 7] = [ + 'S' as u16, 'T' as u16, 'A' as u16, 'T' as u16, 'I' as u16, 'C' as u16, 0, + ]; + + struct OwnedGlobal { + handle: HGLOBAL, + } + + impl OwnedGlobal { + fn new(handle: HGLOBAL) -> Self { + Self { handle } + } + + fn handle(&self) -> HGLOBAL { + self.handle + } + + fn into_clipboard(mut self) -> HGLOBAL { + let handle = self.handle; + self.handle = null_mut(); + handle + } + } + + impl Drop for OwnedGlobal { + fn drop(&mut self) { + if !self.handle.is_null() { + unsafe { + let _ = GlobalFree(self.handle); + } + } + } + } + + struct LockedGlobal { + handle: HGLOBAL, + ptr: *mut c_void, + } + + impl LockedGlobal { + fn new(handle: HGLOBAL) -> Result { + let ptr = unsafe { GlobalLock(handle) }; + if ptr.is_null() { + return Err(last_error("GlobalLock failed")); + } + Ok(Self { handle, ptr }) + } + + fn as_mut_ptr(&self) -> *mut u8 { + self.ptr.cast() + } + } + + impl Drop for LockedGlobal { + fn drop(&mut self) { + unsafe { + let _ = GlobalUnlock(self.handle); + } + } + } + + struct ClipboardOwnerWindow { + hwnd: HWND, + } + + impl ClipboardOwnerWindow { + fn create() -> Result { + let hwnd = unsafe { + CreateWindowExW( + 0, + STATIC_WINDOW_CLASS.as_ptr(), + null_mut(), + 0, + 0, + 0, + 0, + 0, + null_mut(), + null_mut(), + null_mut(), + null_mut(), + ) + }; + if hwnd.is_null() { + return Err(last_error("CreateWindowExW failed")); + } + Ok(Self { hwnd }) + } + + fn hwnd(&self) -> HWND { + self.hwnd + } + } + + impl Drop for ClipboardOwnerWindow { + fn drop(&mut self) { + unsafe { + let _ = DestroyWindow(self.hwnd); + } + } + } + + struct ClipboardGuard; + + impl ClipboardGuard { + fn open(hwnd: HWND) -> Result { + if unsafe { OpenClipboard(hwnd) } == 0 { + return Err(last_error("OpenClipboard failed")); + } + Ok(Self) + } + } + + impl Drop for ClipboardGuard { + fn drop(&mut self) { + unsafe { + let _ = CloseClipboard(); + } + } + } + + pub(super) fn write_file_reference(path: &str) -> Result<()> { + let payload = build_hdrop_payload(path); + let handle = unsafe { GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, payload.len()) }; + if handle.is_null() { + return Err(last_error("GlobalAlloc failed")); + } + let memory = OwnedGlobal::new(handle); + + { + let locked = LockedGlobal::new(memory.handle())?; + unsafe { + copy_nonoverlapping(payload.as_ptr(), locked.as_mut_ptr(), payload.len()); + } + } + + let owner = ClipboardOwnerWindow::create()?; + let _clipboard = ClipboardGuard::open(owner.hwnd())?; + if unsafe { EmptyClipboard() } == 0 { + return Err(last_error("EmptyClipboard failed")); + } + if unsafe { SetClipboardData(CF_HDROP as u32, memory.handle()) }.is_null() { + return Err(last_error("SetClipboardData failed")); + } + let _ = memory.into_clipboard(); + Ok(()) + } + + fn last_error(message: &str) -> Error { + Error::new( + Status::GenericFailure, + format!("{message} (err {})", unsafe { GetLastError() }), + ) + } +} + +#[cfg(not(target_os = "windows"))] +mod platform { + use napi::bindgen_prelude::{Error, Result, Status}; + + pub(super) fn write_file_reference(_path: &str) -> Result<()> { + Err(Error::new( + Status::GenericFailure, + "win-clipboard not supported on this platform", + )) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn validation_rejects_empty_paths() { + let err = validate_file_path("").expect_err("empty path should be rejected"); + assert_eq!(err.status, Status::InvalidArg); + assert_eq!(err.reason, "path must be non-empty"); + } + + #[test] + fn validation_rejects_nul_bytes() { + let err = validate_file_path("before\0after").expect_err("NUL path should be rejected"); + assert_eq!(err.status, Status::InvalidArg); + assert_eq!(err.reason, "path must not contain NUL bytes"); + } + + #[test] + fn hdrop_payload_uses_unicode_dropfiles_layout() { + let payload = build_hdrop_payload("C:\\Temp\\a.txt"); + + assert_eq!(payload.len(), DROPFILES_HEADER_SIZE + ((13 + 2) * 2)); + assert_eq!( + u32::from_le_bytes(payload[0..4].try_into().unwrap()), + DROPFILES_HEADER_SIZE as u32 + ); + assert_eq!(i32::from_le_bytes(payload[4..8].try_into().unwrap()), 0); + assert_eq!(i32::from_le_bytes(payload[8..12].try_into().unwrap()), 0); + assert_eq!(i32::from_le_bytes(payload[12..16].try_into().unwrap()), 0); + assert_eq!(i32::from_le_bytes(payload[16..20].try_into().unwrap()), 1); + + let path_units: Vec = payload[DROPFILES_HEADER_SIZE..payload.len() - 4] + .chunks_exact(2) + .map(|chunk| u16::from_le_bytes(chunk.try_into().unwrap())) + .collect(); + assert_eq!(String::from_utf16(&path_units).unwrap(), "C:\\Temp\\a.txt"); + assert_eq!(&payload[payload.len() - 4..], &[0, 0, 0, 0]); + } + + #[cfg(not(target_os = "windows"))] + #[test] + fn non_windows_worker_preserves_stub_error_contract() { + let err = + platform::write_file_reference("/tmp/file.txt").expect_err("non-Windows should fail"); + assert_eq!(err.status, Status::GenericFailure); + assert_eq!(err.reason, "win-clipboard not supported on this platform"); + } +} diff --git a/fluxer_desktop/native/win-game-capture/Cargo.lock b/fluxer_desktop/native/win-game-capture/Cargo.lock new file mode 100644 index 0000000..4cbc582 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/Cargo.lock @@ -0,0 +1,478 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "fluxer_encoder_ring" +version = "0.0.0" +dependencies = [ + "libloading", + "windows", +] + +[[package]] +name = "fluxer_gpu_rebuild" +version = "0.0.0" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "fluxer_screen_frame_bus" +version = "0.0.0" +dependencies = [ + "fluxer_gpu_rebuild", + "parking_lot", +] + +[[package]] +name = "fluxer_win_game_capture" +version = "0.0.0" +dependencies = [ + "fluxer_encoder_ring", + "fluxer_screen_frame_bus", + "napi", + "napi-build", + "napi-derive", + "parking_lot", + "windows", + "windows-core", + "windows-sys", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] diff --git a/fluxer_desktop/native/win-game-capture/Cargo.toml b/fluxer_desktop/native/win-game-capture/Cargo.toml new file mode 100644 index 0000000..82a7de5 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "fluxer_win_game_capture" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8"]} +napi-derive = "3.5.6" +fluxer_screen_frame_bus = {path = "../screen-frame-bus"} +fluxer_encoder_ring = {path = "../encoder-ring"} +parking_lot = "0.12" + +[target.'cfg(target_os = "windows")'.dependencies] +windows = {version = "0.62.2", features = [ + "Graphics_Capture", + "Graphics_DirectX_Direct3D11", + "Win32_Foundation", + "Win32_Graphics_Direct3D", + "Win32_Graphics_Direct3D11", + "Win32_Graphics_Dxgi", + "Win32_Graphics_Dxgi_Common", + "Win32_System_LibraryLoader", + "Win32_System_Threading", + "Win32_System_WinRT", + "Win32_System_WinRT_Direct3D11", + "Win32_System_WinRT_Graphics_Capture", + "Win32_UI_WindowsAndMessaging", + "Win32_Graphics_Gdi", + "Win32_System_Performance", +]} +windows-core = "0.62.2" +windows-sys = {version = "0.61.2", features = [ + "Win32_Foundation", + "Win32_Graphics_Gdi", + "Win32_Security", + "Win32_System_Diagnostics_Debug", + "Win32_System_LibraryLoader", + "Win32_System_Memory", + "Win32_System_Performance", + "Win32_System_ProcessStatus", + "Win32_System_Registry", + "Win32_System_SystemInformation", + "Win32_System_Threading", + "Win32_UI_WindowsAndMessaging", +]} + +[build-dependencies] +napi-build = "2.3.2" diff --git a/fluxer_desktop/native/win-game-capture/build.rs b/fluxer_desktop/native/win-game-capture/build.rs new file mode 100644 index 0000000..2a69c54 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/build.rs @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +extern crate napi_build; + +fn main() { + napi_build::setup(); +} diff --git a/fluxer_desktop/native/win-game-capture/compatibility.json b/fluxer_desktop/native/win-game-capture/compatibility.json new file mode 100644 index 0000000..1fcd8ff --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/compatibility.json @@ -0,0 +1,6 @@ +{ + "$comment": "Optional per-process game-capture injection policy override. Placed next to the native addon, it augments the deny/force-cpu lists compiled into the addon. Entries are matched case-insensitively by executable file name (e.g. game.exe); paths are reduced to their final component. 'deny' refuses injection; 'allow' opts a process back in past the built-in deny list; 'forceCpu' keeps injection but prefers CPU readback over the shared-texture fast path. A missing or malformed file falls back to the embedded defaults.", + "deny": [], + "allow": [], + "forceCpu": [] +} diff --git a/fluxer_desktop/native/win-game-capture/hook/Cargo.lock b/fluxer_desktop/native/win-game-capture/hook/Cargo.lock new file mode 100644 index 0000000..e66158b --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/hook/Cargo.lock @@ -0,0 +1,375 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "fluxer_game_hook" +version = "0.0.0" +dependencies = [ + "retour", + "windows", + "windows-sys 0.61.2", +] + +[[package]] +name = "generic-array" +version = "0.14.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "iced-x86" +version = "1.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c447cff8c7f384a7d4f741cfcff32f75f3ad02b406432e8d6c878d56b1edf6b" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "mach2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d640282b302c0bb0a2a8e0233ead9035e3bed871f0b7e81fe4a1ec829765db44" +dependencies = [ + "libc", +] + +[[package]] +name = "mmap-fixed-fixed" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0681853891801e4763dc252e843672faf32bcfee27a0aa3b19733902af450acc" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "region" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6b6ebd13bc009aef9cd476c1310d49ac354d36e240cf1bd753290f3dc7199a7" +dependencies = [ + "bitflags", + "libc", + "mach2", + "windows-sys 0.52.0", +] + +[[package]] +name = "retour" +version = "0.4.0-alpha.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ead4bc8e12d553ff70769c5f5c21f5f4f0e73c0018068a6bb5a3d7d3b9e57ec7" +dependencies = [ + "cfg-if", + "generic-array", + "iced-x86", + "libc", + "mmap-fixed-fixed", + "once_cell", + "region", + "slice-pool2", +] + +[[package]] +name = "slice-pool2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a3d689654af89bdfeba29a914ab6ac0236d382eb3b764f7454dde052f2821f8" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "typenum" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" diff --git a/fluxer_desktop/native/win-game-capture/hook/Cargo.toml b/fluxer_desktop/native/win-game-capture/hook/Cargo.toml new file mode 100644 index 0000000..868ac04 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/hook/Cargo.toml @@ -0,0 +1,46 @@ +[package] +name = "fluxer_game_hook" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib"] + +# `retour` only ships x86/x86_64 trampoline + patcher backends (its `arch` +# module has no aarch64 variant), so it does not even compile for +# aarch64-pc-windows-msvc. Gate it to the architectures it supports; the +# aarch64 hook uses the in-crate `inline_hook::aarch64` backend instead. +[target.'cfg(all(target_os = "windows", any(target_arch = "x86", target_arch = "x86_64")))'.dependencies] +retour = "0.4.0-alpha.4" + +[target.'cfg(target_os = "windows")'.dependencies] +windows = {version = "0.62.2", features = [ + "Win32_Foundation", + "Win32_Graphics_Direct3D", + "Win32_Graphics_Direct3D9", + "Win32_Graphics_Direct3D10", + "Win32_Graphics_Direct3D11", + "Win32_Graphics_Direct3D11on12", + "Win32_Graphics_Direct3D12", + "Win32_Graphics_Dxgi", + "Win32_Graphics_Dxgi_Common", + "Win32_Graphics_Gdi", +]} +windows-sys = {version = "0.61.2", features = [ + "Win32_Foundation", + "Win32_Graphics_Gdi", + "Win32_Graphics_OpenGL", + "Win32_Security", + "Win32_System_Diagnostics_Debug", + "Win32_System_LibraryLoader", + "Win32_System_Memory", + "Win32_System_Performance", + "Win32_System_SystemServices", + "Win32_System_Threading", + "Win32_UI_WindowsAndMessaging", +]} diff --git a/fluxer_desktop/native/win-game-capture/hook/src/arm64_reloc.rs b/fluxer_desktop/native/win-game-capture/hook/src/arm64_reloc.rs new file mode 100644 index 0000000..f416abd --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/hook/src/arm64_reloc.rs @@ -0,0 +1,248 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![allow(dead_code)] + +pub const STOLEN_BYTES: usize = 16; + +pub const NOP: u32 = 0xD503_201F; + +pub const LDR_X16_PC8: u32 = 0x5800_0050; +pub const BR_X16: u32 = 0xD61F_0200; +pub const BLR_X16: u32 = 0xD63F_0200; + +pub fn is_b(insn: u32) -> bool { + (insn & 0xFC00_0000) == 0x1400_0000 +} + +pub fn is_bl(insn: u32) -> bool { + (insn & 0xFC00_0000) == 0x9400_0000 +} + +pub fn needs_absolute_island(insn: u32) -> bool { + is_b(insn) || is_bl(insn) +} + +pub fn branch_target(insn: u32, src_pc: u64) -> Option { + if !is_b(insn) && !is_bl(insn) { + return None; + } + let imm26 = (insn & 0x03FF_FFFF) as i32; + let off = ((imm26 << 6) >> 6) as i64 * 4; + Some((src_pc as i64 + off) as u64) +} + +pub fn encode_imm26(byte_off: i64) -> Option { + if byte_off & 0b11 != 0 { + return None; + } + let words = byte_off >> 2; + if !(-(1 << 25)..(1 << 25)).contains(&words) { + return None; + } + Some((words as u32) & 0x03FF_FFFF) +} + +pub fn append_abs_branch(out: &mut Vec, addr: u64, link: bool) { + let branch = if link { BLR_X16 } else { BR_X16 }; + out.extend_from_slice(&LDR_X16_PC8.to_le_bytes()); + out.extend_from_slice(&branch.to_le_bytes()); + out.extend_from_slice(&addr.to_le_bytes()); +} + +pub fn adrp_target(insn: u32, src_pc: u64) -> Option { + if (insn & 0x9F00_0000) != 0x9000_0000 { + return None; + } + let immlo = ((insn >> 29) & 0x3) as i64; + let immhi = ((insn >> 5) & 0x7FFFF) as i64; + let raw = (immhi << 2) | immlo; + let imm21 = (raw << 43) >> 43; + let page = (src_pc & !0xFFF) as i64 + imm21 * 4096; + Some(page as u64) +} + +fn ldr_unsigned_64(insn: u32) -> Option<(u32, u32, u64)> { + if (insn & 0xFFC0_0000) != 0xF940_0000 { + return None; + } + let imm12 = ((insn >> 10) & 0xFFF) as u64; + let rn = (insn >> 5) & 0x1F; + let rt = insn & 0x1F; + Some((rt, rn, imm12 * 8)) +} + +fn br_register(insn: u32) -> Option { + if (insn & 0xFFFF_FC1F) != 0xD61F_0000 { + return None; + } + Some((insn >> 5) & 0x1F) +} + +pub unsafe fn import_thunk_target(prologue: &[u8], src_base: u64) -> Option { + if prologue.len() < 12 { + return None; + } + let adrp = u32::from_le_bytes(prologue[0..4].try_into().ok()?); + let ldr = u32::from_le_bytes(prologue[4..8].try_into().ok()?); + let br = u32::from_le_bytes(prologue[8..12].try_into().ok()?); + + let adrp_reg = adrp & 0x1F; + let page = adrp_target(adrp, src_base)?; + let (ldr_rt, ldr_rn, offset) = ldr_unsigned_64(ldr)?; + let br_rn = br_register(br)?; + if adrp_reg != ldr_rn || ldr_rt != br_rn { + return None; + } + let pointer_addr = page.checked_add(offset)?; + let target = unsafe { core::ptr::read_unaligned(pointer_addr as *const u64) }; + (target != 0).then_some(target) +} + +pub fn emit_branch_to_island(insn: u32, dst_pc: u64, island_addr: u64) -> Option { + let link = is_bl(insn); + let off = island_addr as i64 - dst_pc as i64; + let imm = encode_imm26(off)?; + let opc = if link { 0x9400_0000 } else { 0x1400_0000 }; + Some(opc | imm) +} + +pub fn island_for_branch(insn: u32, src_pc: u64) -> Option> { + let target = branch_target(insn, src_pc)?; + let mut bytes = Vec::new(); + append_abs_branch(&mut bytes, target, is_bl(insn)); + Some(bytes) +} + +pub fn relocate_instruction(insn: u32, src_pc: u64, dst_pc: u64) -> Option { + if (insn & 0x9F00_0000) == 0x9000_0000 { + return relocate_adr(insn, src_pc, dst_pc, true); + } + if (insn & 0x9F00_0000) == 0x1000_0000 { + return relocate_adr(insn, src_pc, dst_pc, false); + } + if is_b(insn) || is_bl(insn) { + let target = branch_target(insn, src_pc)?; + let off = target as i64 - dst_pc as i64; + let imm = encode_imm26(off)?; + return Some((insn & 0xFC00_0000) | imm); + } + if (insn & 0xFF00_0010) == 0x5400_0000 { + return relocate_imm19_at5(insn, src_pc, dst_pc); + } + if (insn & 0x7F00_0000) == 0x3400_0000 { + return relocate_imm19_at5(insn, src_pc, dst_pc); + } + if (insn & 0x7F00_0000) == 0x3600_0000 { + return relocate_tbz(insn, src_pc, dst_pc); + } + if (insn & 0x3B00_0000) == 0x1800_0000 { + return relocate_imm19_at5(insn, src_pc, dst_pc); + } + Some(insn) +} + +fn relocate_adr(insn: u32, src_pc: u64, dst_pc: u64, page: bool) -> Option { + let immlo = ((insn >> 29) & 0x3) as i64; + let immhi = ((insn >> 5) & 0x7FFFF) as i64; + let raw = (immhi << 2) | immlo; + let imm21 = (raw << 43) >> 43; + let (src_ref, dst_ref, scale) = if page { + (src_pc & !0xFFF, dst_pc & !0xFFF, 4096i64) + } else { + (src_pc, dst_pc, 1i64) + }; + let target = src_ref as i64 + imm21 * scale; + let new_off = target - dst_ref as i64; + if scale != 1 && new_off & 0xFFF != 0 { + return None; + } + let scaled = new_off / scale; + if !(-(1 << 20)..(1 << 20)).contains(&scaled) { + return None; + } + let new_raw = (scaled as u32) & 0x1F_FFFF; + let new_immlo = (new_raw & 0x3) << 29; + let new_immhi = ((new_raw >> 2) & 0x7FFFF) << 5; + Some((insn & 0x9F00_001F) | new_immlo | new_immhi) +} + +fn relocate_imm19_at5(insn: u32, src_pc: u64, dst_pc: u64) -> Option { + let imm19 = ((insn >> 5) & 0x7FFFF) as i64; + let off = ((imm19 << 45) >> 45) * 4; + let target = src_pc as i64 + off; + let new_off = target - dst_pc as i64; + if new_off & 0b11 != 0 { + return None; + } + let words = new_off >> 2; + if !(-(1 << 18)..(1 << 18)).contains(&words) { + return None; + } + let new_imm19 = ((words as u32) & 0x7FFFF) << 5; + Some((insn & !(0x7FFFF << 5)) | new_imm19) +} + +fn relocate_tbz(insn: u32, src_pc: u64, dst_pc: u64) -> Option { + let imm14 = ((insn >> 5) & 0x3FFF) as i64; + let off = ((imm14 << 50) >> 50) * 4; + let target = src_pc as i64 + off; + let new_off = target - dst_pc as i64; + if new_off & 0b11 != 0 { + return None; + } + let words = new_off >> 2; + if !(-(1 << 13)..(1 << 13)).contains(&words) { + return None; + } + let new_imm14 = ((words as u32) & 0x3FFF) << 5; + Some((insn & !(0x3FFF << 5)) | new_imm14) +} + +pub fn assemble_trampoline( + prologue: &[u8], + src_base: u64, + dst_base: u64, + resume: u64, +) -> Option> { + if !prologue.len().is_multiple_of(4) { + return None; + } + let count = prologue.len() / 4; + const RETURN_BRANCH_BYTES: usize = 16; + const ISLAND_BYTES: usize = 16; + + let islands_base = dst_base + (count * 4) as u64 + RETURN_BRANCH_BYTES as u64; + + let mut prologue_out: Vec = Vec::with_capacity(count * 4); + let mut islands_out: Vec = Vec::new(); + let mut next_island = islands_base; + + for i in 0..count { + let insn = u32::from_le_bytes(prologue[i * 4..i * 4 + 4].try_into().ok()?); + let src_pc = src_base + (i * 4) as u64; + let dst_pc = dst_base + (i * 4) as u64; + if needs_absolute_island(insn) { + let island_addr = next_island; + next_island += ISLAND_BYTES as u64; + let relocated = emit_branch_to_island(insn, dst_pc, island_addr)?; + prologue_out.extend_from_slice(&relocated.to_le_bytes()); + let island = island_for_branch(insn, src_pc)?; + debug_assert_eq!(island.len(), ISLAND_BYTES); + islands_out.extend_from_slice(&island); + } else { + let relocated = relocate_instruction(insn, src_pc, dst_pc)?; + prologue_out.extend_from_slice(&relocated.to_le_bytes()); + } + } + + let mut out = prologue_out; + append_abs_branch(&mut out, resume, false); + out.extend_from_slice(&islands_out); + Some(out) +} + +pub fn relocated_prologue(prologue: &[u8], src_base: u64, dst_base: u64) -> Option> { + let count = prologue.len() / 4; + let body = assemble_trampoline(prologue, src_base, dst_base, src_base + STOLEN_BYTES as u64)?; + Some(body[..count * 4].to_vec()) +} diff --git a/fluxer_desktop/native/win-game-capture/hook/src/d3d9.rs b/fluxer_desktop/native/win-game-capture/hook/src/d3d9.rs new file mode 100644 index 0000000..3cb1391 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/hook/src/d3d9.rs @@ -0,0 +1,1377 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::{ + D3d11FormatMode, Detour, ENV_FORCE_CPU, GAME_CAPTURE_API_D3D9, + GAME_CAPTURE_FALLBACK_DEVICE_LOST, GAME_CAPTURE_FALLBACK_FORCED_CPU, + GAME_CAPTURE_FALLBACK_FORMAT_UNSUPPORTED, GAME_CAPTURE_FALLBACK_NONE, + GAME_CAPTURE_FALLBACK_SHARED_TEXTURE_UNSUPPORTED, HOOKS_READY, HookState, capture_should_run, + clear_state, ensure_state, env_flag_enabled, frame_due, mark_present, + publish_shared_texture_frame, record_dropped_frame, set_capture_flags, set_fallback_reason, + verbose_log, wide, write_bgra_rows_to_shared_memory, +}; +use std::{ + ffi::c_void, + mem, + ptr::null_mut, + sync::atomic::{AtomicBool, Ordering}, +}; +use windows::{ + Win32::{ + Foundation::HMODULE as WinHmodule, + Foundation::{HWND as WinHwnd, RECT}, + Graphics::Direct3D::{ + D3D_DRIVER_TYPE_UNKNOWN, D3D_FEATURE_LEVEL_10_0, D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_11_0, + }, + Graphics::{ + Direct3D9::{ + D3D_SDK_VERSION, D3DADAPTER_DEFAULT, D3DBACKBUFFER_TYPE_MONO, + D3DCREATE_FPU_PRESERVE, D3DCREATE_HARDWARE_VERTEXPROCESSING, + D3DCREATE_MULTITHREADED, D3DCREATE_SOFTWARE_VERTEXPROCESSING, D3DDEVTYPE_HAL, + D3DDISPLAYMODEEX, D3DFMT_A8B8G8R8, D3DFMT_A8R8G8B8, D3DFMT_X8B8G8R8, + D3DFMT_X8R8G8B8, D3DFORMAT, D3DLOCK_READONLY, D3DLOCKED_RECT, D3DMULTISAMPLE_NONE, + D3DPOOL_DEFAULT, D3DPOOL_SYSTEMMEM, D3DPRESENT_PARAMETERS, D3DSURFACE_DESC, + D3DSWAPEFFECT_DISCARD, D3DTEXF_NONE, D3DUSAGE_RENDERTARGET, Direct3DCreate9, + IDirect3DDevice9, IDirect3DSurface9, IDirect3DSwapChain9, IDirect3DTexture9, + }, + Direct3D11::{ + D3D11_BIND_RENDER_TARGET, D3D11_BIND_SHADER_RESOURCE, D3D11_RESOURCE_MISC_SHARED, + D3D11_SDK_VERSION, D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, D3D11CreateDevice, + ID3D11Device, ID3D11Texture2D, + }, + Dxgi::{ + Common::{ + DXGI_FORMAT, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8X8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_SAMPLE_DESC, + }, + CreateDXGIFactory1, IDXGIFactory1, IDXGIResource, + }, + Gdi::RGNDATA, + }, + }, + core::Interface, +}; +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +use windows_sys::Win32::System::Memory::{ + MEM_COMMIT, MEMORY_BASIC_INFORMATION, PAGE_GUARD, PAGE_NOACCESS, VirtualQuery, +}; +use windows_sys::Win32::{ + Foundation::HWND, + UI::WindowsAndMessaging::{ + CW_USEDEFAULT, CreateWindowExW, DestroyWindow, GetClientRect, IsWindow, WS_OVERLAPPEDWINDOW, + }, +}; + +const VT_DEVICE9_RESET: usize = 16; +const VT_DEVICE9_PRESENT: usize = 17; +const VT_DEVICE9EX_PRESENTEX: usize = 121; +const VT_DEVICE9EX_CHECK_RESOURCE_RESIDENCY: usize = 125; +const VT_DEVICE9EX_RESETEX: usize = 132; +const VT_SWAPCHAIN9_PRESENT: usize = 3; + +type D3d9PresentFn = unsafe extern "system" fn( + *mut c_void, + *const RECT, + *const RECT, + WinHwnd, + *const RGNDATA, +) -> windows::core::HRESULT; +type D3d9PresentExFn = unsafe extern "system" fn( + *mut c_void, + *const RECT, + *const RECT, + WinHwnd, + *const RGNDATA, + u32, +) -> windows::core::HRESULT; +type D3d9ResetFn = + unsafe extern "system" fn(*mut c_void, *mut D3DPRESENT_PARAMETERS) -> windows::core::HRESULT; +type D3d9ResetExFn = unsafe extern "system" fn( + *mut c_void, + *mut D3DPRESENT_PARAMETERS, + *mut D3DDISPLAYMODEEX, +) -> windows::core::HRESULT; +type D3d9SwapChainPresentFn = unsafe extern "system" fn( + *mut c_void, + *const RECT, + *const RECT, + WinHwnd, + *const RGNDATA, + u32, +) -> windows::core::HRESULT; + +static mut D3D9_PRESENT_HOOK: Option> = None; +static mut D3D9_PRESENT_EX_HOOK: Option> = None; +static mut D3D9_RESET_HOOK: Option> = None; +static mut D3D9_RESET_EX_HOOK: Option> = None; +static mut D3D9_SWAPCHAIN_PRESENT_HOOK: Option> = None; +static D3D9_FIRED: AtomicBool = AtomicBool::new(false); +static D3D9_PRESENT_RECURSE: std::sync::atomic::AtomicUsize = + std::sync::atomic::AtomicUsize::new(0); + +pub(crate) struct D3d9CaptureState { + device_ptr: usize, + width: u32, + height: u32, + format: D3DFORMAT, + multisample_type: windows::Win32::Graphics::Direct3D9::D3DMULTISAMPLE_TYPE, + path: D3d9CapturePath, +} + +enum D3d9CapturePath { + SharedTexture { + copy_surface: IDirect3DSurface9, + _copy_texture: IDirect3DTexture9, + _d3d11_device: ID3D11Device, + _d3d11_texture: ID3D11Texture2D, + shared_handle: u64, + dxgi_format: DXGI_FORMAT, + }, + Memory { + surface: IDirect3DSurface9, + resolve_surface: Option, + format_mode: D3d11FormatMode, + }, +} + +pub(crate) unsafe fn hooks_installed() -> bool { + D3D9_PRESENT_HOOK.is_some() +} + +fn d3d9_format_mode(format: D3DFORMAT) -> Option { + if format == D3DFMT_X8R8G8B8 || format == D3DFMT_A8R8G8B8 { + Some(D3d11FormatMode::Bgra) + } else if format == D3DFMT_X8B8G8R8 || format == D3DFMT_A8B8G8R8 { + Some(D3d11FormatMode::Rgba) + } else { + None + } +} + +fn d3d9_shared_dxgi_format(format: D3DFORMAT) -> Option { + use windows::Win32::Graphics::Direct3D9::D3DFMT_A2B10G10R10; + if format == D3DFMT_A8R8G8B8 { + Some(DXGI_FORMAT_B8G8R8A8_UNORM) + } else if format == D3DFMT_X8R8G8B8 { + Some(DXGI_FORMAT_B8G8R8X8_UNORM) + } else if format == D3DFMT_A8B8G8R8 { + Some(DXGI_FORMAT_R8G8B8A8_UNORM) + } else if format == D3DFMT_A2B10G10R10 { + Some(DXGI_FORMAT_R10G10B10A2_UNORM) + } else { + None + } +} + +fn d3d9_hdr_flags(format: D3DFORMAT) -> u32 { + use crate::{GAME_CAPTURE_FLAG_HDR, GAME_CAPTURE_FLAG_TEN_BIT}; + use windows::Win32::Graphics::Direct3D9::{ + D3DFMT_A2B10G10R10, D3DFMT_A2B10G10R10_XR_BIAS, D3DFMT_A2R10G10B10, D3DFMT_A16B16G16R16, + D3DFMT_A16B16G16R16F, + }; + if format == D3DFMT_A2R10G10B10 + || format == D3DFMT_A2B10G10R10 + || format == D3DFMT_A2B10G10R10_XR_BIAS + { + GAME_CAPTURE_FLAG_TEN_BIT | GAME_CAPTURE_FLAG_HDR + } else if format == D3DFMT_A16B16G16R16F || format == D3DFMT_A16B16G16R16 { + GAME_CAPTURE_FLAG_HDR + } else { + 0 + } +} + +#[derive(Clone, Copy, Debug)] +struct D3d9ExFlagOffsets { + d3d9_object_offset: usize, + is_d3d9ex_offset: usize, +} + +struct D3d9ExFlagGuard { + flag: *mut i32, + previous: i32, +} + +impl Drop for D3d9ExFlagGuard { + fn drop(&mut self) { + unsafe { + if !self.flag.is_null() { + *self.flag = self.previous; + } + } + } +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +static D3D9EX_FLAG_OFFSETS: std::sync::OnceLock> = + std::sync::OnceLock::new(); + +#[cfg(target_arch = "x86_64")] +const D3D9EX_PATTERN_LEN: usize = 22; +#[cfg(target_arch = "x86_64")] +const D3D9EX_PATTERN_MASKS: [[u8; D3D9EX_PATTERN_LEN]; 4] = [ + [ + 0xF8, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0xF8, 0xF8, 0x00, 0x00, 0x00, 0x00, + ], + [ + 0xF8, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, + 0xF8, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, + ], + [ + 0xF8, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, + 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, + ], + [ + 0xF8, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF, + 0x00, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, + ], +]; +#[cfg(target_arch = "x86_64")] +const D3D9EX_PATTERN_VALUES: [[u8; D3D9EX_PATTERN_LEN]; 4] = [ + [ + 0x48, 0x8B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x44, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, + 0x00, 0x40, 0xB8, 0x00, 0x00, 0x00, 0x00, + ], + [ + 0x48, 0x8B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x39, 0x80, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, + 0x40, 0xB8, 0x00, 0x00, 0x00, 0x00, 0x00, + ], + [ + 0x48, 0x8B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x39, 0x80, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, + 0x48, 0x8D, 0x00, 0x00, 0x00, 0x00, 0x00, + ], + [ + 0x48, 0x8B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x83, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, + 0x00, 0x48, 0x8D, 0x00, 0x00, 0x00, 0x00, + ], +]; +#[cfg(target_arch = "x86_64")] +const D3D9EX_OFFSET_FIELDS: [(usize, usize); 4] = [(3, 10), (3, 9), (3, 9), (3, 9)]; + +#[cfg(target_arch = "x86")] +const D3D9EX_PATTERN_LEN: usize = 20; +#[cfg(target_arch = "x86")] +const D3D9EX_PATTERN_MASKS: [[u8; D3D9EX_PATTERN_LEN]; 4] = [ + [ + 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, + ], + [ + 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, + ], + [ + 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, + 0xFF, 0x00, 0x00, 0x00, 0x00, + ], + [ + 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xC0, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, + 0x00, 0x00, 0x00, 0x00, 0x00, + ], +]; +#[cfg(target_arch = "x86")] +const D3D9EX_PATTERN_VALUES: [[u8; D3D9EX_PATTERN_LEN]; 4] = [ + [ + 0x8B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x39, 0x80, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, 0x68, + 0x00, 0x00, 0x00, 0x00, 0x00, + ], + [ + 0x8B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x83, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, + 0x68, 0x00, 0x00, 0x00, 0x00, + ], + [ + 0x8B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x83, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, + 0xBA, 0x00, 0x00, 0x00, 0x00, + ], + [ + 0x8B, 0x80, 0x00, 0x00, 0x00, 0x00, 0x39, 0x80, 0x00, 0x00, 0x00, 0x00, 0x75, 0x00, 0xBA, + 0x00, 0x00, 0x00, 0x00, 0x00, + ], +]; +#[cfg(target_arch = "x86")] +const D3D9EX_OFFSET_FIELDS: [(usize, usize); 4] = [(2, 8), (2, 8), (2, 8), (2, 8)]; + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +fn d3d9ex_pattern_offsets(bytes: &[u8]) -> Option<(usize, usize)> { + if bytes.len() < D3D9EX_PATTERN_LEN { + return None; + } + for (index, (mask, value)) in D3D9EX_PATTERN_MASKS + .iter() + .zip(D3D9EX_PATTERN_VALUES.iter()) + .enumerate() + { + if bytes[..D3D9EX_PATTERN_LEN] + .iter() + .zip(mask.iter()) + .zip(value.iter()) + .all(|((byte, mask), value)| (*byte & *mask) == *value) + { + return Some(D3D9EX_OFFSET_FIELDS[index]); + } + } + None +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +unsafe fn memory_range_readable(ptr: *const u8, len: usize) -> bool { + if ptr.is_null() || len == 0 { + return false; + } + let mut info: MEMORY_BASIC_INFORMATION = mem::zeroed(); + let queried = VirtualQuery( + ptr.cast(), + &mut info, + mem::size_of::(), + ); + if queried == 0 || info.State != MEM_COMMIT { + return false; + } + if (info.Protect & PAGE_NOACCESS) != 0 || (info.Protect & PAGE_GUARD) != 0 { + return false; + } + let start = ptr as usize; + let region_start = info.BaseAddress as usize; + let Some(end) = start.checked_add(len) else { + return false; + }; + let region_end = region_start.saturating_add(info.RegionSize); + start >= region_start && end <= region_end +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +unsafe fn read_u32_field(base: *const u8, offset: usize) -> u32 { + std::ptr::read_unaligned(base.add(offset).cast::()) +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +unsafe fn validate_d3d9ex_offsets( + device_raw: *mut c_void, + d3d9ex_raw: *mut c_void, + offsets: D3d9ExFlagOffsets, +) -> bool { + if offsets.d3d9_object_offset > 0xFFFF || offsets.is_d3d9ex_offset > 0xFFFF { + return false; + } + let device = device_raw.cast::(); + let d3d9_object_slot = device.add(offsets.d3d9_object_offset).cast::<*mut u8>(); + if !memory_range_readable(d3d9_object_slot.cast(), mem::size_of::<*mut u8>()) { + return false; + } + let d3d9_object = *d3d9_object_slot; + if d3d9_object != d3d9ex_raw.cast::() { + return false; + } + let flag = d3d9_object.add(offsets.is_d3d9ex_offset).cast::(); + if !memory_range_readable(flag.cast(), mem::size_of::()) { + return false; + } + *flag == 1 +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +unsafe fn discover_d3d9ex_flag_offsets() -> Option { + use windows::Win32::Graphics::Direct3D9::{Direct3DCreate9Ex, IDirect3DDevice9Ex}; + + let Ok(d3d9ex) = Direct3DCreate9Ex(D3D_SDK_VERSION) else { + verbose_log("d3d9 shared texture: Direct3DCreate9Ex unavailable for offset discovery"); + return None; + }; + let hwnd = create_dummy_window_d3d9(); + if hwnd.is_null() { + verbose_log("d3d9 shared texture: dummy window creation failed for offset discovery"); + return None; + } + let mut params = D3DPRESENT_PARAMETERS { + Windowed: windows::core::BOOL(1), + SwapEffect: D3DSWAPEFFECT_DISCARD, + hDeviceWindow: WinHwnd(hwnd), + BackBufferFormat: D3DFMT_X8R8G8B8, + BackBufferWidth: 2, + BackBufferHeight: 2, + BackBufferCount: 1, + MultiSampleType: D3DMULTISAMPLE_NONE, + ..Default::default() + }; + let mut device: Option = None; + let base_flags = D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE; + let mut created = d3d9ex + .CreateDeviceEx( + D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + WinHwnd(hwnd), + (base_flags | D3DCREATE_HARDWARE_VERTEXPROCESSING) as u32, + &mut params, + null_mut(), + &mut device, + ) + .is_ok(); + if !created { + created = d3d9ex + .CreateDeviceEx( + D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + WinHwnd(hwnd), + (base_flags | D3DCREATE_SOFTWARE_VERTEXPROCESSING) as u32, + &mut params, + null_mut(), + &mut device, + ) + .is_ok(); + } + let Some(device) = (if created { device } else { None }) else { + let _ = DestroyWindow(hwnd); + verbose_log("d3d9 shared texture: D3D9Ex probe device creation failed"); + return None; + }; + + let raw = device.as_raw(); + let vtable = *(raw as *mut *const *const ()); + let check_resource_residency = *vtable.add(VT_DEVICE9EX_CHECK_RESOURCE_RESIDENCY) as *const u8; + let mut result = None; + const MAX_FUNC_SCAN_BYTES: usize = 200; + for offset in 0..MAX_FUNC_SCAN_BYTES { + let candidate = check_resource_residency.add(offset); + if !memory_range_readable(candidate, D3D9EX_PATTERN_LEN) { + break; + } + let bytes = std::slice::from_raw_parts(candidate, D3D9EX_PATTERN_LEN); + let Some((d3d9_object_field, is_d3d9ex_field)) = d3d9ex_pattern_offsets(bytes) else { + continue; + }; + let offsets = D3d9ExFlagOffsets { + d3d9_object_offset: read_u32_field(candidate, d3d9_object_field) as usize, + is_d3d9ex_offset: read_u32_field(candidate, is_d3d9ex_field) as usize, + }; + if validate_d3d9ex_offsets(raw, d3d9ex.as_raw(), offsets) { + verbose_log(&format!( + "d3d9 shared texture: discovered D3D9Ex flag offsets object=0x{:x} flag=0x{:x}", + offsets.d3d9_object_offset, offsets.is_d3d9ex_offset + )); + result = Some(offsets); + break; + } + } + + let _ = DestroyWindow(hwnd); + if result.is_none() { + verbose_log("d3d9 shared texture: D3D9Ex flag offset discovery failed"); + } + result +} + +#[cfg(any(target_arch = "x86", target_arch = "x86_64"))] +unsafe fn d3d9ex_flag_offsets() -> Option { + *D3D9EX_FLAG_OFFSETS.get_or_init(|| discover_d3d9ex_flag_offsets()) +} + +#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] +unsafe fn d3d9ex_flag_offsets() -> Option { + None +} + +unsafe fn d3d9ex_flag_for_device(device: &IDirect3DDevice9) -> Option<*mut i32> { + let offsets = d3d9ex_flag_offsets()?; + let device_raw = device.as_raw().cast::(); + let d3d9_object_slot = device_raw.add(offsets.d3d9_object_offset).cast::<*mut u8>(); + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + if !memory_range_readable(d3d9_object_slot.cast(), mem::size_of::<*mut u8>()) { + return None; + } + let d3d9_object = *d3d9_object_slot; + if d3d9_object.is_null() { + return None; + } + let flag = d3d9_object.add(offsets.is_d3d9ex_offset).cast::(); + #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] + if !memory_range_readable(flag.cast(), mem::size_of::()) { + return None; + } + Some(flag) +} + +unsafe fn force_d3d9ex_flag(device: &IDirect3DDevice9) -> Option { + let flag = d3d9ex_flag_for_device(device)?; + let previous = *flag; + *flag = 1; + Some(D3d9ExFlagGuard { flag, previous }) +} + +unsafe fn client_size_from_hwnd(hwnd: HWND) -> Option<(HWND, u32, u32)> { + if hwnd.is_null() || IsWindow(hwnd) == 0 { + return None; + } + let mut rect = mem::zeroed(); + if GetClientRect(hwnd, &mut rect) == 0 { + return None; + } + let width = (rect.right - rect.left).max(1) as u32; + let height = (rect.bottom - rect.top).max(1) as u32; + Some((hwnd, width, height)) +} + +unsafe fn create_d3d9_interop_d3d11_device() -> Option { + const FEATURE_LEVELS: &[windows::Win32::Graphics::Direct3D::D3D_FEATURE_LEVEL] = &[ + D3D_FEATURE_LEVEL_11_0, + D3D_FEATURE_LEVEL_10_1, + D3D_FEATURE_LEVEL_10_0, + ]; + let factory = CreateDXGIFactory1::() + .map_err(|error| { + verbose_log(&format!( + "d3d9 shared texture: CreateDXGIFactory1 failed: {error}" + )); + error + }) + .ok()?; + let adapter = factory + .EnumAdapters(0) + .map_err(|error| { + verbose_log(&format!( + "d3d9 shared texture: EnumAdapters(0) failed: {error}" + )); + error + }) + .ok()?; + let mut d3d11_device = None; + if let Err(error) = D3D11CreateDevice( + &adapter, + D3D_DRIVER_TYPE_UNKNOWN, + WinHmodule::default(), + Default::default(), + Some(FEATURE_LEVELS), + D3D11_SDK_VERSION, + Some(&mut d3d11_device), + None, + None, + ) { + verbose_log(&format!( + "d3d9 shared texture: D3D11CreateDevice failed: {error}" + )); + return None; + } + d3d11_device +} + +unsafe fn create_d3d9_shared_texture_path( + device: &IDirect3DDevice9, + desc: &D3DSURFACE_DESC, +) -> Option { + let Some(dxgi_format) = d3d9_shared_dxgi_format(desc.Format) else { + verbose_log(&format!( + "d3d9 shared texture: unsupported D3D9 format {}", + desc.Format.0 + )); + return None; + }; + let d3d11_device = create_d3d9_interop_d3d11_device()?; + let texture_desc = D3D11_TEXTURE2D_DESC { + Width: desc.Width, + Height: desc.Height, + MipLevels: 1, + ArraySize: 1, + Format: dxgi_format, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Usage: D3D11_USAGE_DEFAULT, + BindFlags: (D3D11_BIND_RENDER_TARGET.0 | D3D11_BIND_SHADER_RESOURCE.0) as u32, + CPUAccessFlags: 0, + MiscFlags: D3D11_RESOURCE_MISC_SHARED.0 as u32, + }; + let mut d3d11_texture = None; + if let Err(error) = d3d11_device.CreateTexture2D(&texture_desc, None, Some(&mut d3d11_texture)) + { + verbose_log(&format!( + "d3d9 shared texture: CreateTexture2D failed: {error}" + )); + return None; + } + let Some(d3d11_texture) = d3d11_texture else { + verbose_log("d3d9 shared texture: CreateTexture2D returned no texture"); + return None; + }; + let mut shared_handle = d3d11_texture + .cast::() + .map_err(|error| { + verbose_log(&format!( + "d3d9 shared texture: QueryInterface IDXGIResource failed: {error}" + )); + error + }) + .and_then(|resource| { + resource.GetSharedHandle().map_err(|error| { + verbose_log(&format!( + "d3d9 shared texture: GetSharedHandle failed: {error}" + )); + error + }) + }) + .ok()?; + if shared_handle.is_invalid() { + verbose_log("d3d9 shared texture: GetSharedHandle returned an invalid handle"); + return None; + } + let mut d3d9_texture = None; + let _d3d9ex_flag = force_d3d9ex_flag(device).or_else(|| { + verbose_log( + "d3d9 shared texture: D3D9Ex compatibility flag unavailable; trying direct CreateTexture", + ); + None + }); + if let Err(error) = device.CreateTexture( + desc.Width, + desc.Height, + 1, + D3DUSAGE_RENDERTARGET as u32, + desc.Format, + D3DPOOL_DEFAULT, + &mut d3d9_texture, + &mut shared_handle, + ) { + verbose_log(&format!( + "d3d9 shared texture: CreateTexture shared handle failed: {error}" + )); + return None; + } + let Some(d3d9_texture) = d3d9_texture else { + verbose_log("d3d9 shared texture: CreateTexture returned no D3D9 texture"); + return None; + }; + let copy_surface = d3d9_texture + .GetSurfaceLevel(0) + .map_err(|error| { + verbose_log(&format!( + "d3d9 shared texture: GetSurfaceLevel failed: {error}" + )); + error + }) + .ok()?; + Some(D3d9CapturePath::SharedTexture { + copy_surface, + _copy_texture: d3d9_texture, + _d3d11_device: d3d11_device, + _d3d11_texture: d3d11_texture, + shared_handle: shared_handle.0 as usize as u64, + dxgi_format, + }) +} + +unsafe fn create_d3d9_memory_path( + state: &mut HookState, + device: &IDirect3DDevice9, + desc: &D3DSURFACE_DESC, + format_mode: D3d11FormatMode, +) -> Option { + let mut surface: Option = None; + if device + .CreateOffscreenPlainSurface( + desc.Width, + desc.Height, + desc.Format, + D3DPOOL_SYSTEMMEM, + &mut surface, + null_mut(), + ) + .is_err() + { + (*state.info).last_error = 60; + set_fallback_reason(state, GAME_CAPTURE_FALLBACK_DEVICE_LOST); + return None; + } + let Some(surface) = surface else { + (*state.info).last_error = 60; + return None; + }; + let mut resolve_surface = None; + if desc.MultiSampleType != D3DMULTISAMPLE_NONE { + if device + .CreateRenderTarget( + desc.Width, + desc.Height, + desc.Format, + D3DMULTISAMPLE_NONE, + 0, + false, + &mut resolve_surface, + null_mut(), + ) + .is_err() + || resolve_surface.is_none() + { + (*state.info).last_error = 61; + set_fallback_reason(state, crate::GAME_CAPTURE_FALLBACK_MULTISAMPLED); + set_capture_flags(state, crate::GAME_CAPTURE_FLAG_MULTISAMPLED); + return None; + } + } + Some(D3d9CapturePath::Memory { + surface, + resolve_surface, + format_mode, + }) +} + +unsafe fn d3d9_state_for_frame<'a>( + state: &'a mut HookState, + device: &IDirect3DDevice9, + device_ptr: usize, + desc: &D3DSURFACE_DESC, + format_mode: Option, + prefer_shared_texture: bool, +) -> Option<&'a mut D3d9CaptureState> { + let recreate = state + .d3d9 + .as_ref() + .map(|capture| { + capture.device_ptr != device_ptr + || capture.width != desc.Width + || capture.height != desc.Height + || capture.format != desc.Format + || capture.multisample_type != desc.MultiSampleType + }) + .unwrap_or(true); + if recreate { + let path = if prefer_shared_texture { + match create_d3d9_shared_texture_path(device, desc) { + Some(path) => { + verbose_log("d3d9 shared texture capture initialized"); + Some(path) + } + None => { + verbose_log("d3d9 shared texture init failed; falling back to memory readback"); + set_fallback_reason(state, GAME_CAPTURE_FALLBACK_SHARED_TEXTURE_UNSUPPORTED); + None + } + } + } else { + None + } + .or_else(|| { + let format_mode = format_mode?; + create_d3d9_memory_path(state, device, desc, format_mode) + }); + let Some(path) = path else { + state.d3d9 = None; + return None; + }; + state.d3d9 = Some(D3d9CaptureState { + device_ptr, + width: desc.Width, + height: desc.Height, + format: desc.Format, + multisample_type: desc.MultiSampleType, + path, + }); + } + state.d3d9.as_mut() +} + +unsafe fn capture_d3d9_backbuffer( + device: &IDirect3DDevice9, + back_buffer: IDirect3DSurface9, + window_override: WinHwnd, +) { + if !HOOKS_READY.load(Ordering::Acquire) { + return; + } + let should_continue = { + let Some(state) = ensure_state() else { + return; + }; + capture_should_run(state) + }; + if !should_continue { + clear_state(); + return; + } + let Some(state) = ensure_state() else { + return; + }; + + if !D3D9_FIRED.swap(true, Ordering::AcqRel) { + verbose_log("d3d9 present detour fired (first frame, valid device)"); + } + mark_present(state, GAME_CAPTURE_API_D3D9); + set_capture_flags(state, 0); + set_fallback_reason(state, GAME_CAPTURE_FALLBACK_NONE); + + let mut desc = D3DSURFACE_DESC::default(); + if back_buffer.GetDesc(&mut desc).is_err() || desc.Width == 0 || desc.Height == 0 { + record_dropped_frame(state); + return; + } + + let format_mode = d3d9_format_mode(desc.Format); + if format_mode.is_none() && d3d9_shared_dxgi_format(desc.Format).is_none() { + set_capture_flags(state, d3d9_hdr_flags(desc.Format)); + set_fallback_reason(state, GAME_CAPTURE_FALLBACK_FORMAT_UNSUPPORTED); + record_dropped_frame(state); + return; + }; + + if !frame_due(state) { + record_dropped_frame(state); + return; + } + + if desc.MultiSampleType != D3DMULTISAMPLE_NONE { + set_capture_flags(state, crate::GAME_CAPTURE_FLAG_MULTISAMPLED); + } + set_capture_flags( + state, + (*state.info).capture_flags | d3d9_hdr_flags(desc.Format), + ); + + let device_ptr = device.as_raw() as usize; + let force_cpu = env_flag_enabled(ENV_FORCE_CPU); + if force_cpu { + set_fallback_reason(state, GAME_CAPTURE_FALLBACK_FORCED_CPU); + } + + let Some(capture_state) = + d3d9_state_for_frame(state, device, device_ptr, &desc, format_mode, !force_cpu) + else { + record_dropped_frame(state); + return; + }; + + if let D3d9CapturePath::SharedTexture { + copy_surface, + shared_handle, + dxgi_format, + .. + } = &capture_state.path + { + let copy_surface = copy_surface.clone(); + let shared_handle = *shared_handle; + let dxgi_format = *dxgi_format; + if device + .StretchRect( + &back_buffer, + null_mut(), + ©_surface, + null_mut(), + D3DTEXF_NONE, + ) + .is_ok() + { + let hwnd = desc_window(state, window_override); + let _ = publish_shared_texture_frame( + state, + hwnd, + desc.Width, + desc.Height, + dxgi_format, + shared_handle, + ); + return; + } + verbose_log("d3d9 shared texture StretchRect failed; falling back to memory readback"); + set_fallback_reason(state, GAME_CAPTURE_FALLBACK_SHARED_TEXTURE_UNSUPPORTED); + state.d3d9 = None; + } + + let (surface, source_surface, format_mode) = { + let Some(capture_state) = + d3d9_state_for_frame(state, device, device_ptr, &desc, format_mode, false) + else { + record_dropped_frame(state); + return; + }; + let D3d9CapturePath::Memory { + surface, + resolve_surface, + format_mode, + } = &capture_state.path + else { + record_dropped_frame(state); + return; + }; + let source_surface = if desc.MultiSampleType != D3DMULTISAMPLE_NONE { + let Some(resolve_surface) = resolve_surface.clone() else { + record_dropped_frame(state); + return; + }; + if device + .StretchRect( + &back_buffer, + null_mut(), + &resolve_surface, + null_mut(), + D3DTEXF_NONE, + ) + .is_err() + { + verbose_log("d3d9 StretchRect resolve FAILED (multisample backbuffer)"); + set_fallback_reason(state, crate::GAME_CAPTURE_FALLBACK_MULTISAMPLED); + record_dropped_frame(state); + return; + } + resolve_surface + } else { + back_buffer.clone() + }; + (surface.clone(), source_surface, *format_mode) + }; + + if device + .GetRenderTargetData(&source_surface, &surface) + .is_err() + { + verbose_log("d3d9 GetRenderTargetData FAILED (readback surface or lost device)"); + if desc.MultiSampleType != D3DMULTISAMPLE_NONE { + set_fallback_reason(state, crate::GAME_CAPTURE_FALLBACK_MULTISAMPLED); + set_capture_flags(state, crate::GAME_CAPTURE_FLAG_MULTISAMPLED); + } else { + set_fallback_reason(state, GAME_CAPTURE_FALLBACK_DEVICE_LOST); + state.d3d9 = None; + } + record_dropped_frame(state); + return; + } + + let mut locked = D3DLOCKED_RECT::default(); + if surface + .LockRect(&mut locked, null_mut(), D3DLOCK_READONLY as u32) + .is_err() + || locked.pBits.is_null() + { + state.d3d9 = None; + record_dropped_frame(state); + return; + } + + let hwnd = desc_window(state, window_override); + let _ = write_bgra_rows_to_shared_memory( + state, + hwnd, + desc.Width, + desc.Height, + locked.pBits as *const u8, + locked.Pitch.max(0) as usize, + false, + format_mode, + ); + let _ = surface.UnlockRect(); +} + +unsafe fn capture_d3d9_device_frame(device_raw: *mut c_void, window_override: WinHwnd) { + let Some(device) = IDirect3DDevice9::from_raw_borrowed(&device_raw) else { + return; + }; + let Ok(back_buffer) = device.GetBackBuffer(0, 0, D3DBACKBUFFER_TYPE_MONO) else { + if let Some(state) = ensure_state() { + state.d3d9 = None; + set_fallback_reason(state, GAME_CAPTURE_FALLBACK_DEVICE_LOST); + record_dropped_frame(state); + } + return; + }; + capture_d3d9_backbuffer(device, back_buffer, window_override); +} + +unsafe fn capture_d3d9_swapchain_frame(swap_chain_raw: *mut c_void, window_override: WinHwnd) { + let Some(swap_chain) = IDirect3DSwapChain9::from_raw_borrowed(&swap_chain_raw) else { + return; + }; + let Ok(device) = swap_chain.GetDevice() else { + return; + }; + let Ok(back_buffer) = swap_chain.GetBackBuffer(0, D3DBACKBUFFER_TYPE_MONO) else { + if let Some(state) = ensure_state() { + state.d3d9 = None; + set_fallback_reason(state, GAME_CAPTURE_FALLBACK_DEVICE_LOST); + record_dropped_frame(state); + } + return; + }; + capture_d3d9_backbuffer(&device, back_buffer, window_override); +} + +unsafe fn present_enter() -> bool { + D3D9_PRESENT_RECURSE.fetch_add(1, Ordering::AcqRel) == 0 +} + +unsafe fn present_leave() { + D3D9_PRESENT_RECURSE.fetch_sub(1, Ordering::AcqRel); +} + +unsafe fn desc_window(state: &mut HookState, window_override: WinHwnd) -> HWND { + let override_hwnd = window_override.0 as HWND; + if let Some((hwnd, _, _)) = client_size_from_hwnd(override_hwnd) { + return hwnd; + } + let info_hwnd = (*state.info).hwnd as usize as HWND; + if let Some((hwnd, _, _)) = client_size_from_hwnd(info_hwnd) { + return hwnd; + } + info_hwnd +} + +unsafe extern "system" fn d3d9_present_detour( + device: *mut c_void, + source_rect: *const RECT, + dest_rect: *const RECT, + dest_window_override: WinHwnd, + dirty_region: *const RGNDATA, +) -> windows::core::HRESULT { + let outermost = present_enter(); + if outermost { + capture_d3d9_device_frame(device, dest_window_override); + } + if let Some(hook) = D3D9_PRESENT_HOOK.as_ref() { + let result = (hook.trampoline_fn())( + device, + source_rect, + dest_rect, + dest_window_override, + dirty_region, + ); + present_leave(); + return result; + } + present_leave(); + windows::core::HRESULT(0) +} + +unsafe extern "system" fn d3d9_present_ex_detour( + device: *mut c_void, + source_rect: *const RECT, + dest_rect: *const RECT, + dest_window_override: WinHwnd, + dirty_region: *const RGNDATA, + flags: u32, +) -> windows::core::HRESULT { + let outermost = present_enter(); + if outermost { + capture_d3d9_device_frame(device, dest_window_override); + } + if let Some(hook) = D3D9_PRESENT_EX_HOOK.as_ref() { + let result = (hook.trampoline_fn())( + device, + source_rect, + dest_rect, + dest_window_override, + dirty_region, + flags, + ); + present_leave(); + return result; + } + present_leave(); + windows::core::HRESULT(0) +} + +unsafe extern "system" fn d3d9_reset_detour( + device: *mut c_void, + present_parameters: *mut D3DPRESENT_PARAMETERS, +) -> windows::core::HRESULT { + if let Some(state) = ensure_state() { + state.d3d9 = None; + verbose_log("d3d9 Reset: invalidated cached readback surface"); + } + if let Some(hook) = D3D9_RESET_HOOK.as_ref() { + return (hook.trampoline_fn())(device, present_parameters); + } + windows::core::HRESULT(0) +} + +unsafe extern "system" fn d3d9_reset_ex_detour( + device: *mut c_void, + present_parameters: *mut D3DPRESENT_PARAMETERS, + fullscreen_display_mode: *mut D3DDISPLAYMODEEX, +) -> windows::core::HRESULT { + if let Some(state) = ensure_state() { + state.d3d9 = None; + verbose_log("d3d9 ResetEx: invalidated cached readback surface"); + } + if let Some(hook) = D3D9_RESET_EX_HOOK.as_ref() { + return (hook.trampoline_fn())(device, present_parameters, fullscreen_display_mode); + } + windows::core::HRESULT(0) +} + +unsafe extern "system" fn d3d9_swapchain_present_detour( + swap_chain: *mut c_void, + source_rect: *const RECT, + dest_rect: *const RECT, + dest_window_override: WinHwnd, + dirty_region: *const RGNDATA, + flags: u32, +) -> windows::core::HRESULT { + let outermost = present_enter(); + if outermost { + capture_d3d9_swapchain_frame(swap_chain, dest_window_override); + } + if let Some(hook) = D3D9_SWAPCHAIN_PRESENT_HOOK.as_ref() { + let result = (hook.trampoline_fn())( + swap_chain, + source_rect, + dest_rect, + dest_window_override, + dirty_region, + flags, + ); + present_leave(); + return result; + } + present_leave(); + windows::core::HRESULT(0) +} + +unsafe fn create_dummy_window_d3d9() -> HWND { + let class_name = wide("FluxerGameCaptureDummyWindow"); + CreateWindowExW( + 0, + class_name.as_ptr(), + class_name.as_ptr(), + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + 2, + 2, + null_mut(), + null_mut(), + null_mut(), + null_mut(), + ) +} + +unsafe fn d3d9_method_pointers() -> Option<( + *const (), + *const (), + Option<*const ()>, + Option<*const ()>, + Option<*const ()>, +)> { + let d3d9 = Direct3DCreate9(D3D_SDK_VERSION)?; + let hwnd = create_dummy_window_d3d9(); + if hwnd.is_null() { + return None; + } + let mut params = D3DPRESENT_PARAMETERS { + Windowed: windows::core::BOOL(1), + SwapEffect: D3DSWAPEFFECT_DISCARD, + hDeviceWindow: WinHwnd(hwnd), + BackBufferFormat: D3DFMT_X8R8G8B8, + BackBufferWidth: 2, + BackBufferHeight: 2, + BackBufferCount: 1, + MultiSampleType: D3DMULTISAMPLE_NONE, + ..Default::default() + }; + + let mut device: Option = None; + let base_flags = D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE; + let mut created = d3d9 + .CreateDevice( + D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + WinHwnd(hwnd), + (base_flags | D3DCREATE_HARDWARE_VERTEXPROCESSING) as u32, + &mut params, + &mut device, + ) + .is_ok(); + if !created { + created = d3d9 + .CreateDevice( + D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + WinHwnd(hwnd), + (base_flags | D3DCREATE_SOFTWARE_VERTEXPROCESSING) as u32, + &mut params, + &mut device, + ) + .is_ok(); + } + let device = if created { device } else { None }; + let Some(device) = device else { + let _ = DestroyWindow(hwnd); + return None; + }; + + let raw = device.as_raw(); + let vtable = *(raw as *mut *const *const ()); + let reset = *vtable.add(VT_DEVICE9_RESET); + let present = *vtable.add(VT_DEVICE9_PRESENT); + + let swapchain_present = device.GetSwapChain(0).ok().map(|swap_chain| { + let raw = swap_chain.as_raw(); + let vtable = *(raw as *mut *const *const ()); + *vtable.add(VT_SWAPCHAIN9_PRESENT) + }); + + let (present_ex, reset_ex) = d3d9ex_method_pointers(); + + drop(device); + let _ = DestroyWindow(hwnd); + Some((present, reset, present_ex, reset_ex, swapchain_present)) +} + +unsafe fn d3d9ex_method_pointers() -> (Option<*const ()>, Option<*const ()>) { + use windows::Win32::Graphics::Direct3D9::{Direct3DCreate9Ex, IDirect3DDevice9Ex}; + let Ok(d3d9ex) = Direct3DCreate9Ex(D3D_SDK_VERSION) else { + return (None, None); + }; + let hwnd = create_dummy_window_d3d9(); + if hwnd.is_null() { + return (None, None); + } + let mut params = D3DPRESENT_PARAMETERS { + Windowed: windows::core::BOOL(1), + SwapEffect: D3DSWAPEFFECT_DISCARD, + hDeviceWindow: WinHwnd(hwnd), + BackBufferFormat: D3DFMT_X8R8G8B8, + BackBufferWidth: 2, + BackBufferHeight: 2, + BackBufferCount: 1, + MultiSampleType: D3DMULTISAMPLE_NONE, + ..Default::default() + }; + let mut device: Option = None; + let base_flags = D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE; + let mut created = d3d9ex + .CreateDeviceEx( + D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + WinHwnd(hwnd), + (base_flags | D3DCREATE_HARDWARE_VERTEXPROCESSING) as u32, + &mut params, + null_mut(), + &mut device, + ) + .is_ok(); + if !created { + created = d3d9ex + .CreateDeviceEx( + D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + WinHwnd(hwnd), + (base_flags | D3DCREATE_SOFTWARE_VERTEXPROCESSING) as u32, + &mut params, + null_mut(), + &mut device, + ) + .is_ok(); + } + let device = if created { device } else { None }; + let result = device.map(|device| { + let raw = device.as_raw(); + let vtable = *(raw as *mut *const *const ()); + ( + *vtable.add(VT_DEVICE9EX_PRESENTEX), + *vtable.add(VT_DEVICE9EX_RESETEX), + ) + }); + let _ = DestroyWindow(hwnd); + result + .map(|(present_ex, reset_ex)| (Some(present_ex), Some(reset_ex))) + .unwrap_or((None, None)) +} + +pub(crate) unsafe fn install_d3d9_hooks() { + if D3D9_PRESENT_HOOK.is_some() { + return; + } + let Some((present, reset, present_ex, reset_ex, swapchain_present)) = d3d9_method_pointers() + else { + verbose_log("d3d9 install: method_pointers FAILED (throwaway D3D9 device/vtable probe)"); + return; + }; + verbose_log("d3d9 install: method_pointers resolved, enabling detours"); + + let target: D3d9PresentFn = mem::transmute(present); + let detour: D3d9PresentFn = d3d9_present_detour; + if let Ok(hook) = Detour::::new(target, detour) { + if hook.enable().is_ok() { + D3D9_PRESENT_HOOK = Some(hook); + verbose_log("d3d9 Present hooked"); + } + } + + if D3D9_RESET_HOOK.is_none() { + let target: D3d9ResetFn = mem::transmute(reset); + let detour: D3d9ResetFn = d3d9_reset_detour; + if let Ok(hook) = Detour::::new(target, detour) { + if hook.enable().is_ok() { + D3D9_RESET_HOOK = Some(hook); + } + } + } + + if let Some(reset_ex) = reset_ex { + if D3D9_RESET_EX_HOOK.is_none() { + let target: D3d9ResetExFn = mem::transmute(reset_ex); + let detour: D3d9ResetExFn = d3d9_reset_ex_detour; + if let Ok(hook) = Detour::::new(target, detour) { + if hook.enable().is_ok() { + D3D9_RESET_EX_HOOK = Some(hook); + verbose_log("d3d9 ResetEx hooked"); + } + } + } + } + + if let Some(present_ex) = present_ex { + if D3D9_PRESENT_EX_HOOK.is_none() { + let target: D3d9PresentExFn = mem::transmute(present_ex); + let detour: D3d9PresentExFn = d3d9_present_ex_detour; + if let Ok(hook) = Detour::::new(target, detour) { + if hook.enable().is_ok() { + D3D9_PRESENT_EX_HOOK = Some(hook); + verbose_log("d3d9 PresentEx hooked"); + } + } + } + } + + if let Some(swapchain_present) = swapchain_present { + if D3D9_SWAPCHAIN_PRESENT_HOOK.is_none() { + let target: D3d9SwapChainPresentFn = mem::transmute(swapchain_present); + let detour: D3d9SwapChainPresentFn = d3d9_swapchain_present_detour; + if let Ok(hook) = Detour::::new(target, detour) { + if hook.enable().is_ok() { + D3D9_SWAPCHAIN_PRESENT_HOOK = Some(hook); + verbose_log("d3d9 SwapChain Present hooked"); + } + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[cfg(target_arch = "x86_64")] + #[test] + fn d3d9ex_pattern_matches_win11_x64_signature() { + let bytes = [ + 0x49, 0x8B, 0x86, 0x30, 0x40, 0x00, 0x00, 0x83, 0xB8, 0xA0, 0x55, 0x00, 0x00, 0x00, + 0x75, 0x12, 0x48, 0x8D, 0x15, 0xB9, 0x24, 0x0A, + ]; + assert_eq!(d3d9ex_pattern_offsets(&bytes), Some((3, 9))); + unsafe { + assert_eq!(read_u32_field(bytes.as_ptr(), 3), 0x4030); + assert_eq!(read_u32_field(bytes.as_ptr(), 9), 0x55A0); + } + } + + #[cfg(target_arch = "x86")] + #[test] + fn d3d9ex_pattern_matches_win11_x86_signature() { + let bytes = [ + 0x8B, 0x83, 0x3C, 0x2B, 0x00, 0x00, 0x39, 0xB8, 0x44, 0x4F, 0x00, 0x00, 0x75, 0x0F, + 0xBA, 0xD0, 0xC6, 0x00, 0x10, 0x90, + ]; + assert_eq!(d3d9ex_pattern_offsets(&bytes), Some((2, 8))); + unsafe { + assert_eq!(read_u32_field(bytes.as_ptr(), 2), 0x2B3C); + assert_eq!(read_u32_field(bytes.as_ptr(), 8), 0x4F44); + } + } +} diff --git a/fluxer_desktop/native/win-game-capture/hook/src/gl_interop.rs b/fluxer_desktop/native/win-game-capture/hook/src/gl_interop.rs new file mode 100644 index 0000000..c691ce7 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/hook/src/gl_interop.rs @@ -0,0 +1,584 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::{ + GAME_CAPTURE_API_OPENGL, GAME_CAPTURE_FALLBACK_NONE, + GAME_CAPTURE_FALLBACK_SHARED_TEXTURE_UNSUPPORTED, HookState, mark_present, + publish_shared_texture_frame, set_capture_flags, set_fallback_reason, verbose_log, +}; +use std::{ + ffi::c_void, + ptr::null_mut, + sync::atomic::{AtomicBool, Ordering}, +}; +use windows::{ + Win32::{ + Foundation::{HMODULE as WinHmodule, HWND as WinHwnd}, + Graphics::{ + Direct3D::D3D_DRIVER_TYPE_HARDWARE, + Direct3D11::{ + D3D11_BIND_RENDER_TARGET, D3D11_BIND_SHADER_RESOURCE, + D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_RESOURCE_MISC_SHARED, D3D11_SDK_VERSION, + D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, D3D11CreateDeviceAndSwapChain, + ID3D11Device, ID3D11DeviceContext, ID3D11Texture2D, + }, + Dxgi::{ + Common::{ + DXGI_FORMAT, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_MODE_DESC, + DXGI_MODE_SCALING_UNSPECIFIED, DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, + DXGI_RATIONAL, DXGI_SAMPLE_DESC, + }, + DXGI_PRESENT, DXGI_SWAP_CHAIN_DESC, DXGI_SWAP_EFFECT_DISCARD, + DXGI_USAGE_RENDER_TARGET_OUTPUT, IDXGIResource, IDXGISwapChain, + }, + }, + }, + core::{BOOL as WinBool, Interface}, +}; +use windows_sys::Win32::{ + Foundation::HWND as SysHwnd, + Graphics::OpenGL::{ + GL_COLOR_BUFFER_BIT, GL_LINEAR, GL_NEAREST, GL_NO_ERROR, GL_TEXTURE_2D, + GL_TEXTURE_BINDING_2D, glBindTexture, glDeleteTextures, glFinish, glGenTextures, + glGetError, glGetIntegerv, wglGetCurrentContext, wglGetProcAddress, + }, + UI::WindowsAndMessaging::DestroyWindow, +}; + +const WGL_ACCESS_READ_ONLY_NV: u32 = 0x0000; +const WGL_ACCESS_READ_WRITE_NV: u32 = 0x0001; +const WGL_ACCESS_WRITE_DISCARD_NV: u32 = 0x0002; + +const GL_READ_FRAMEBUFFER: u32 = 0x8CA8; +const GL_DRAW_FRAMEBUFFER: u32 = 0x8CA9; +const GL_FRAMEBUFFER: u32 = 0x8D40; +const GL_COLOR_ATTACHMENT0: u32 = 0x8CE0; +const GL_FRAMEBUFFER_COMPLETE: u32 = 0x8CD5; +const GL_READ_FRAMEBUFFER_BINDING: u32 = 0x8CAA; +const GL_DRAW_FRAMEBUFFER_BINDING: u32 = 0x8CA6; + +type DxOpenDeviceNvFn = unsafe extern "system" fn(dx_device: *mut c_void) -> *mut c_void; +type DxCloseDeviceNvFn = unsafe extern "system" fn(device: *mut c_void) -> i32; +type DxRegisterObjectNvFn = unsafe extern "system" fn( + device: *mut c_void, + dx_object: *mut c_void, + name: u32, + object_type: u32, + access: u32, +) -> *mut c_void; +type DxUnregisterObjectNvFn = + unsafe extern "system" fn(device: *mut c_void, object: *mut c_void) -> i32; +type DxLockObjectsNvFn = + unsafe extern "system" fn(device: *mut c_void, count: i32, objects: *const *mut c_void) -> i32; +type DxUnlockObjectsNvFn = + unsafe extern "system" fn(device: *mut c_void, count: i32, objects: *const *mut c_void) -> i32; + +type GlGenFramebuffersFn = unsafe extern "system" fn(n: i32, framebuffers: *mut u32); +type GlDeleteFramebuffersFn = unsafe extern "system" fn(n: i32, framebuffers: *const u32); +type GlBindFramebufferFn = unsafe extern "system" fn(target: u32, framebuffer: u32); +type GlFramebufferTexture2DFn = unsafe extern "system" fn( + target: u32, + attachment: u32, + textarget: u32, + texture: u32, + level: i32, +); +type GlCheckFramebufferStatusFn = unsafe extern "system" fn(target: u32) -> u32; +type GlBlitFramebufferFn = unsafe extern "system" fn( + src_x0: i32, + src_y0: i32, + src_x1: i32, + src_y1: i32, + dst_x0: i32, + dst_y0: i32, + dst_x1: i32, + dst_y1: i32, + mask: u32, + filter: u32, +); + +struct InteropProcs { + open_device: DxOpenDeviceNvFn, + close_device: DxCloseDeviceNvFn, + register_object: DxRegisterObjectNvFn, + unregister_object: DxUnregisterObjectNvFn, + lock_objects: DxLockObjectsNvFn, + unlock_objects: DxUnlockObjectsNvFn, + gen_framebuffers: GlGenFramebuffersFn, + delete_framebuffers: GlDeleteFramebuffersFn, + bind_framebuffer: GlBindFramebufferFn, + framebuffer_texture_2d: GlFramebufferTexture2DFn, + check_framebuffer_status: GlCheckFramebufferStatusFn, + blit_framebuffer: GlBlitFramebufferFn, +} + +pub(crate) struct GlInteropState { + procs: InteropProcs, + _device: ID3D11Device, + _context: ID3D11DeviceContext, + swap_chain: IDXGISwapChain, + _texture: ID3D11Texture2D, + dummy_hwnd: SysHwnd, + shared_handle: u64, + dx_device: *mut c_void, + dx_object: *mut c_void, + gl_texture: u32, + draw_fbo: u32, + width: u32, + height: u32, +} + +unsafe impl Send for GlInteropState {} + +static GL_GPU_DISABLED: AtomicBool = AtomicBool::new(false); +static GL_GPU_UNAVAILABLE_LOGGED: AtomicBool = AtomicBool::new(false); +static GL_DUMMY_PRESENT_ACTIVE: AtomicBool = AtomicBool::new(false); + +struct DummyPresentGuard; + +impl DummyPresentGuard { + fn enter() -> Self { + GL_DUMMY_PRESENT_ACTIVE.store(true, Ordering::Release); + Self + } +} + +impl Drop for DummyPresentGuard { + fn drop(&mut self) { + GL_DUMMY_PRESENT_ACTIVE.store(false, Ordering::Release); + } +} + +fn latch_disable(reason: &str) { + if !GL_GPU_DISABLED.swap(true, Ordering::AcqRel) { + verbose_log(&format!( + "opengl interop: latch-disabling GPU path, falling back to glReadPixels CPU path ({reason})" + )); + } +} + +pub(crate) fn gpu_path_disabled() -> bool { + GL_GPU_DISABLED.load(Ordering::Acquire) +} + +pub(crate) fn dummy_present_active() -> bool { + GL_DUMMY_PRESENT_ACTIVE.load(Ordering::Acquire) +} + +unsafe fn load_proc(name: &[u8]) -> Option { + debug_assert_eq!( + name.last(), + Some(&0), + "wglGetProcAddress name must be NUL-terminated" + ); + let proc = wglGetProcAddress(name.as_ptr()); + match proc { + Some(proc) => Some(std::mem::transmute_copy::<_, T>(&proc)), + None => None, + } +} + +impl InteropProcs { + unsafe fn load() -> Option { + if wglGetCurrentContext().is_null() { + return None; + } + let open_device = load_proc::(b"wglDXOpenDeviceNV\0")?; + let close_device = load_proc::(b"wglDXCloseDeviceNV\0")?; + let register_object = load_proc::(b"wglDXRegisterObjectNV\0")?; + let unregister_object = load_proc::(b"wglDXUnregisterObjectNV\0")?; + let lock_objects = load_proc::(b"wglDXLockObjectsNV\0")?; + let unlock_objects = load_proc::(b"wglDXUnlockObjectsNV\0")?; + let gen_framebuffers = load_proc::(b"glGenFramebuffers\0")?; + let delete_framebuffers = load_proc::(b"glDeleteFramebuffers\0")?; + let bind_framebuffer = load_proc::(b"glBindFramebuffer\0")?; + let framebuffer_texture_2d = + load_proc::(b"glFramebufferTexture2D\0")?; + let check_framebuffer_status = + load_proc::(b"glCheckFramebufferStatus\0")?; + let blit_framebuffer = load_proc::(b"glBlitFramebuffer\0")?; + Some(Self { + open_device, + close_device, + register_object, + unregister_object, + lock_objects, + unlock_objects, + gen_framebuffers, + delete_framebuffers, + bind_framebuffer, + framebuffer_texture_2d, + check_framebuffer_status, + blit_framebuffer, + }) + } +} + +unsafe fn create_interop_d3d11_device() +-> Option<(ID3D11Device, ID3D11DeviceContext, IDXGISwapChain, SysHwnd)> { + let dummy_hwnd = crate::create_dummy_window(); + if dummy_hwnd.is_null() { + verbose_log("opengl interop: failed to create dummy D3D11 flush window"); + return None; + } + + let desc = DXGI_SWAP_CHAIN_DESC { + BufferDesc: DXGI_MODE_DESC { + Width: 2, + Height: 2, + RefreshRate: DXGI_RATIONAL { + Numerator: 60, + Denominator: 1, + }, + Format: DXGI_FORMAT_B8G8R8A8_UNORM, + ScanlineOrdering: DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, + Scaling: DXGI_MODE_SCALING_UNSPECIFIED, + }, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT, + BufferCount: 2, + OutputWindow: WinHwnd(dummy_hwnd), + Windowed: WinBool(1), + SwapEffect: DXGI_SWAP_EFFECT_DISCARD, + Flags: 0, + }; + let mut swap_chain = None; + let mut device = None; + let mut context = None; + let result = D3D11CreateDeviceAndSwapChain( + None, + D3D_DRIVER_TYPE_HARDWARE, + WinHmodule(null_mut()), + D3D11_CREATE_DEVICE_BGRA_SUPPORT, + None, + D3D11_SDK_VERSION, + Some(&desc), + Some(&mut swap_chain), + Some(&mut device), + None, + Some(&mut context), + ); + if result.is_err() { + let _ = DestroyWindow(dummy_hwnd); + return None; + } + match (device, context, swap_chain) { + (Some(device), Some(context), Some(swap_chain)) => { + Some((device, context, swap_chain, dummy_hwnd)) + } + _ => { + let _ = DestroyWindow(dummy_hwnd); + None + } + } +} + +unsafe fn create_shared_texture( + device: &ID3D11Device, + width: u32, + height: u32, +) -> Option<(ID3D11Texture2D, u64)> { + let desc = D3D11_TEXTURE2D_DESC { + Width: width, + Height: height, + MipLevels: 1, + ArraySize: 1, + Format: DXGI_FORMAT_B8G8R8A8_UNORM, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Usage: D3D11_USAGE_DEFAULT, + BindFlags: (D3D11_BIND_RENDER_TARGET.0 | D3D11_BIND_SHADER_RESOURCE.0) as u32, + CPUAccessFlags: 0, + MiscFlags: D3D11_RESOURCE_MISC_SHARED.0 as u32, + }; + let mut texture = None; + if device + .CreateTexture2D(&desc, None, Some(&mut texture)) + .is_err() + { + return None; + } + let texture = texture?; + let handle = texture + .cast::() + .and_then(|resource| resource.GetSharedHandle()) + .ok()?; + Some((texture, handle.0 as usize as u64)) +} + +impl GlInteropState { + unsafe fn create(width: u32, height: u32) -> Option { + let procs = InteropProcs::load()?; + let (device, context, swap_chain, dummy_hwnd) = create_interop_d3d11_device()?; + let (texture, shared_handle) = match create_shared_texture(&device, width, height) { + Some(texture) => texture, + None => { + let _ = DestroyWindow(dummy_hwnd); + return None; + } + }; + if shared_handle == 0 { + let _ = DestroyWindow(dummy_hwnd); + return None; + } + + let dx_device = (procs.open_device)(device.as_raw()); + if dx_device.is_null() { + verbose_log("opengl interop: wglDXOpenDeviceNV returned NULL"); + let _ = DestroyWindow(dummy_hwnd); + return None; + } + verbose_log("opengl interop: wglDXOpenDeviceNV opened private D3D11 device"); + + let mut gl_texture = 0u32; + glGenTextures(1, &mut gl_texture); + if gl_texture == 0 { + (procs.close_device)(dx_device); + let _ = DestroyWindow(dummy_hwnd); + return None; + } + + let dx_object = (procs.register_object)( + dx_device, + texture.as_raw(), + gl_texture, + GL_TEXTURE_2D, + WGL_ACCESS_WRITE_DISCARD_NV, + ); + if dx_object.is_null() { + verbose_log("opengl interop: wglDXRegisterObjectNV returned NULL"); + glDeleteTextures(1, &gl_texture); + (procs.close_device)(dx_device); + let _ = DestroyWindow(dummy_hwnd); + return None; + } + verbose_log(&format!( + "opengl interop: registered D3D11 texture <-> GL texture {gl_texture} ({width}x{height} BGRA)" + )); + + let mut draw_fbo = 0u32; + (procs.gen_framebuffers)(1, &mut draw_fbo); + if draw_fbo == 0 { + (procs.unregister_object)(dx_device, dx_object); + glDeleteTextures(1, &gl_texture); + (procs.close_device)(dx_device); + let _ = DestroyWindow(dummy_hwnd); + return None; + } + + Some(Self { + procs, + _device: device, + _context: context, + swap_chain, + _texture: texture, + dummy_hwnd, + shared_handle, + dx_device, + dx_object, + gl_texture, + draw_fbo, + width, + height, + }) + } + + fn matches(&self, width: u32, height: u32) -> bool { + self.width == width && self.height == height + } + + unsafe fn blit_default_framebuffer(&self) -> bool { + let objects = [self.dx_object]; + + let mut prev_read_fbo = 0i32; + let mut prev_draw_fbo = 0i32; + glGetIntegerv(GL_READ_FRAMEBUFFER_BINDING, &mut prev_read_fbo); + glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &mut prev_draw_fbo); + let mut prev_tex = 0i32; + glGetIntegerv(GL_TEXTURE_BINDING_2D, &mut prev_tex); + + if (self.procs.lock_objects)(self.dx_device, 1, objects.as_ptr()) == 0 { + verbose_log("opengl interop: wglDXLockObjectsNV FAILED"); + return false; + } + + (self.procs.bind_framebuffer)(GL_DRAW_FRAMEBUFFER, self.draw_fbo); + (self.procs.framebuffer_texture_2d)( + GL_DRAW_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + self.gl_texture, + 0, + ); + let status = (self.procs.check_framebuffer_status)(GL_DRAW_FRAMEBUFFER); + if status != GL_FRAMEBUFFER_COMPLETE { + verbose_log(&format!( + "opengl interop: draw FBO incomplete (status 0x{status:04X}); unlocking and falling back" + )); + (self.procs.framebuffer_texture_2d)( + GL_DRAW_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + 0, + 0, + ); + (self.procs.bind_framebuffer)(GL_DRAW_FRAMEBUFFER, prev_draw_fbo as u32); + (self.procs.bind_framebuffer)(GL_READ_FRAMEBUFFER, prev_read_fbo as u32); + let _ = (self.procs.unlock_objects)(self.dx_device, 1, objects.as_ptr()); + return false; + } + + (self.procs.bind_framebuffer)(GL_READ_FRAMEBUFFER, 0); + let w = self.width as i32; + let h = self.height as i32; + (self.procs.blit_framebuffer)( + 0, + 0, + w, + h, + 0, + h, + w, + 0, + GL_COLOR_BUFFER_BIT, + if w == self.width as i32 && h == self.height as i32 { + GL_NEAREST + } else { + GL_LINEAR + }, + ); + let blit_err = glGetError(); + + (self.procs.framebuffer_texture_2d)( + GL_DRAW_FRAMEBUFFER, + GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, + 0, + 0, + ); + (self.procs.bind_framebuffer)(GL_DRAW_FRAMEBUFFER, prev_draw_fbo as u32); + (self.procs.bind_framebuffer)(GL_READ_FRAMEBUFFER, prev_read_fbo as u32); + glBindTexture(GL_TEXTURE_2D, prev_tex as u32); + + glFinish(); + + if (self.procs.unlock_objects)(self.dx_device, 1, objects.as_ptr()) == 0 { + verbose_log("opengl interop: wglDXUnlockObjectsNV FAILED"); + return false; + } + self._context.Flush(); + let present_result = { + let _guard = DummyPresentGuard::enter(); + self.swap_chain.Present(0, DXGI_PRESENT(0)) + }; + if present_result.is_err() { + verbose_log(&format!( + "opengl interop: dummy D3D11 Present flush failed hr={:#010x}", + present_result.0 as u32 + )); + return false; + } + + if blit_err != GL_NO_ERROR { + verbose_log(&format!( + "opengl interop: glBlitFramebuffer raised GL error 0x{blit_err:04X}" + )); + return false; + } + true + } +} + +impl Drop for GlInteropState { + fn drop(&mut self) { + unsafe { + if !self.dx_object.is_null() { + let _ = (self.procs.unregister_object)(self.dx_device, self.dx_object); + } + if self.draw_fbo != 0 { + (self.procs.delete_framebuffers)(1, &self.draw_fbo); + } + if self.gl_texture != 0 { + glDeleteTextures(1, &self.gl_texture); + } + if !self.dx_device.is_null() { + let _ = (self.procs.close_device)(self.dx_device); + } + if !self.dummy_hwnd.is_null() { + let _ = DestroyWindow(self.dummy_hwnd); + } + } + } +} + +unsafe fn interop_state_for_frame( + state: &mut HookState, + width: u32, + height: u32, +) -> Option<&mut GlInteropState> { + let recreate = state + .gl_interop + .as_ref() + .map(|interop| !interop.matches(width, height)) + .unwrap_or(true); + if recreate { + state.gl_interop = None; + match GlInteropState::create(width, height) { + Some(interop) => state.gl_interop = Some(interop), + None => return None, + } + } + state.gl_interop.as_mut() +} + +pub(crate) unsafe fn capture_opengl_frame_gpu( + state: &mut HookState, + hwnd: SysHwnd, + width: u32, + height: u32, +) -> bool { + if gpu_path_disabled() { + return false; + } + + let Some(interop) = interop_state_for_frame(state, width, height) else { + if !GL_GPU_UNAVAILABLE_LOGGED.swap(true, Ordering::AcqRel) { + verbose_log( + "opengl interop: WGL_NV_DX_interop2 unavailable or pipeline creation failed", + ); + } + latch_disable("interop pipeline creation failed"); + set_fallback_reason(state, GAME_CAPTURE_FALLBACK_SHARED_TEXTURE_UNSUPPORTED); + return false; + }; + + let shared_handle = interop.shared_handle; + let blitted = interop.blit_default_framebuffer(); + if !blitted { + latch_disable("lock/blit failed after successful registration"); + set_fallback_reason(state, GAME_CAPTURE_FALLBACK_SHARED_TEXTURE_UNSUPPORTED); + return false; + } + + mark_present(state, GAME_CAPTURE_API_OPENGL); + set_capture_flags(state, 0); + set_fallback_reason(state, GAME_CAPTURE_FALLBACK_NONE); + let published = publish_shared_texture_frame( + state, + hwnd, + width, + height, + DXGI_FORMAT(DXGI_FORMAT_B8G8R8A8_UNORM.0), + shared_handle, + ); + if published { + verbose_log(&format!( + "opengl interop: published shared-texture frame {width}x{height} (handle 0x{shared_handle:X})" + )); + } + published +} diff --git a/fluxer_desktop/native/win-game-capture/hook/src/inline_hook.rs b/fluxer_desktop/native/win-game-capture/hook/src/inline_hook.rs new file mode 100644 index 0000000..20ce315 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/hook/src/inline_hook.rs @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![allow(dead_code)] + +#[cfg(not(target_arch = "aarch64"))] +use retour::Function; + +#[cfg(target_arch = "aarch64")] +pub(crate) use aarch64_function::Function; + +#[cfg(target_arch = "aarch64")] +mod aarch64_function { + pub(crate) trait Function: Copy + Sync + 'static { + unsafe fn from_ptr(ptr: *const ()) -> Self; + fn to_ptr(&self) -> *const (); + } + + macro_rules! impl_function { + ($($arg:ident),*) => { + impl Function + for unsafe extern "system" fn($($arg),*) -> Ret + { + unsafe fn from_ptr(ptr: *const ()) -> Self { + core::mem::transmute(ptr) + } + fn to_ptr(&self) -> *const () { + *self as *const () + } + } + }; + } + + impl_function!(); + impl_function!(A); + impl_function!(A, B); + impl_function!(A, B, C); + impl_function!(A, B, C, D); + impl_function!(A, B, C, D, E); + impl_function!(A, B, C, D, E, F); +} + +pub(crate) struct Detour { + inner: Inner, +} + +#[cfg(not(target_arch = "aarch64"))] +enum Inner { + Retour(retour::GenericDetour), +} + +#[cfg(target_arch = "aarch64")] +enum Inner { + Aarch64(aarch64::Aarch64Detour), +} + +impl Detour { + pub(crate) unsafe fn new(target: T, detour: T) -> Result { + #[cfg(not(target_arch = "aarch64"))] + { + match retour::GenericDetour::::new(target, detour) { + Ok(detour) => Ok(Self { + inner: Inner::Retour(detour), + }), + Err(_) => Err(()), + } + } + #[cfg(target_arch = "aarch64")] + { + aarch64::Aarch64Detour::::new(target, detour).map(|detour| Self { + inner: Inner::Aarch64(detour), + }) + } + } + + pub(crate) unsafe fn enable(&self) -> Result<(), ()> { + match &self.inner { + #[cfg(not(target_arch = "aarch64"))] + Inner::Retour(detour) => detour.enable().map_err(|_| ()), + #[cfg(target_arch = "aarch64")] + Inner::Aarch64(detour) => detour.enable(), + } + } + + pub(crate) fn trampoline_fn(&self) -> T { + match &self.inner { + #[cfg(not(target_arch = "aarch64"))] + Inner::Retour(detour) => unsafe { + T::from_ptr(detour.trampoline() as *const () as *const ()) + }, + #[cfg(target_arch = "aarch64")] + Inner::Aarch64(detour) => detour.trampoline_fn(), + } + } +} + +#[cfg(target_arch = "aarch64")] +mod aarch64 { + + use super::Function; + use crate::arm64_reloc::{ + NOP, STOLEN_BYTES, append_abs_branch, assemble_trampoline, import_thunk_target, + }; + use core::marker::PhantomData; + use std::ptr; + use windows_sys::Win32::System::{ + Diagnostics::Debug::FlushInstructionCache, + Memory::{ + MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_EXECUTE_READ, PAGE_EXECUTE_READWRITE, + PAGE_PROTECTION_FLAGS, VirtualAlloc, VirtualFree, VirtualProtect, + }, + Threading::GetCurrentProcess, + }; + + const TRAMPOLINE_CAP: usize = 256; + + pub(super) struct Aarch64Detour { + target: *mut u8, + detour: *const u8, + trampoline: *mut u8, + original_prologue: [u8; STOLEN_BYTES], + enabled: std::cell::Cell, + _marker: PhantomData, + } + + unsafe impl Send for Aarch64Detour {} + unsafe impl Sync for Aarch64Detour {} + + impl Aarch64Detour { + pub(super) unsafe fn new(target: T, detour: T) -> Result { + let target_ptr = target.to_ptr() as *mut u8; + let detour_ptr = detour.to_ptr() as *const u8; + if target_ptr.is_null() || detour_ptr.is_null() { + return Err(()); + } + + let mut original = [0u8; STOLEN_BYTES]; + ptr::copy_nonoverlapping(target_ptr, original.as_mut_ptr(), STOLEN_BYTES); + + let trampoline = VirtualAlloc( + ptr::null(), + TRAMPOLINE_CAP, + MEM_COMMIT | MEM_RESERVE, + PAGE_EXECUTE_READWRITE, + ) as *mut u8; + if trampoline.is_null() { + return Err(()); + } + + let trampoline_addr = trampoline as u64; + let resume = target_ptr as u64 + STOLEN_BYTES as u64; + let body = + match assemble_trampoline(&original, target_ptr as u64, trampoline_addr, resume) { + Some(body) => body, + None => match import_thunk_target(&original, target_ptr as u64) { + Some(target) => { + let mut body = Vec::new(); + append_abs_branch(&mut body, target, false); + body + } + None => { + VirtualFree(trampoline.cast(), 0, MEM_RELEASE); + return Err(()); + } + }, + }; + if body.len() > TRAMPOLINE_CAP { + VirtualFree(trampoline.cast(), 0, MEM_RELEASE); + return Err(()); + } + ptr::copy_nonoverlapping(body.as_ptr(), trampoline, body.len()); + + let mut old = 0 as PAGE_PROTECTION_FLAGS; + VirtualProtect( + trampoline.cast(), + TRAMPOLINE_CAP, + PAGE_EXECUTE_READ, + &mut old, + ); + FlushInstructionCache(GetCurrentProcess(), trampoline.cast(), TRAMPOLINE_CAP); + + Ok(Self { + target: target_ptr, + detour: detour_ptr, + trampoline, + original_prologue: original, + enabled: std::cell::Cell::new(false), + _marker: PhantomData, + }) + } + + pub(super) unsafe fn enable(&self) -> Result<(), ()> { + if self.enabled.get() { + return Ok(()); + } + let mut patch = Vec::new(); + append_abs_branch(&mut patch, self.detour as u64, false); + if patch.len() > STOLEN_BYTES { + return Err(()); + } + while patch.len() < STOLEN_BYTES { + patch.extend_from_slice(&NOP.to_le_bytes()); + } + + let mut old = 0 as PAGE_PROTECTION_FLAGS; + if VirtualProtect( + self.target.cast(), + STOLEN_BYTES, + PAGE_EXECUTE_READWRITE, + &mut old, + ) == 0 + { + return Err(()); + } + ptr::copy_nonoverlapping(patch.as_ptr(), self.target, STOLEN_BYTES); + let mut restore = 0 as PAGE_PROTECTION_FLAGS; + VirtualProtect(self.target.cast(), STOLEN_BYTES, old, &mut restore); + FlushInstructionCache(GetCurrentProcess(), self.target.cast(), STOLEN_BYTES); + self.enabled.set(true); + Ok(()) + } + + unsafe fn disable(&self) { + if !self.enabled.get() { + return; + } + let mut old = 0 as PAGE_PROTECTION_FLAGS; + if VirtualProtect( + self.target.cast(), + STOLEN_BYTES, + PAGE_EXECUTE_READWRITE, + &mut old, + ) != 0 + { + ptr::copy_nonoverlapping( + self.original_prologue.as_ptr(), + self.target, + STOLEN_BYTES, + ); + let mut restore = 0 as PAGE_PROTECTION_FLAGS; + VirtualProtect(self.target.cast(), STOLEN_BYTES, old, &mut restore); + FlushInstructionCache(GetCurrentProcess(), self.target.cast(), STOLEN_BYTES); + } + self.enabled.set(false); + } + + pub(super) fn trampoline_fn(&self) -> T { + unsafe { T::from_ptr(self.trampoline as *const ()) } + } + } + + impl Drop for Aarch64Detour { + fn drop(&mut self) { + unsafe { + self.disable(); + if !self.trampoline.is_null() { + VirtualFree(self.trampoline.cast(), 0, MEM_RELEASE); + self.trampoline = ptr::null_mut(); + } + } + } + } +} diff --git a/fluxer_desktop/native/win-game-capture/hook/src/lib.rs b/fluxer_desktop/native/win-game-capture/hook/src/lib.rs new file mode 100644 index 0000000..1610dc5 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/hook/src/lib.rs @@ -0,0 +1,2168 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![cfg(target_os = "windows")] +#![allow(dead_code)] +#![allow(static_mut_refs)] +#![allow(unsafe_op_in_unsafe_fn)] + +#[path = "../../src/game_capture_abi.rs"] +mod game_capture_abi; + +mod arm64_reloc; +mod d3d9; +mod gl_interop; +mod inline_hook; + +pub(crate) use game_capture_abi::{ + ENV_DISABLE_D3D12, ENV_DISABLE_OPENGL_SHARED_TEXTURE, ENV_ENABLE_OPENGL_SHARED_TEXTURE, + ENV_FORCE_CPU, ENV_FORCE_SHARED_TEXTURE, ENV_VERBOSE, GAME_CAPTURE_API_D3D9, + GAME_CAPTURE_API_D3D10, GAME_CAPTURE_API_D3D11, GAME_CAPTURE_API_D3D12, + GAME_CAPTURE_API_OPENGL, GAME_CAPTURE_BUFFER_COUNT, + GAME_CAPTURE_CONTROL_DISABLE_SHARED_TEXTURE, GAME_CAPTURE_FALLBACK_DEVICE_LOST, + GAME_CAPTURE_FALLBACK_FORCED_CPU, GAME_CAPTURE_FALLBACK_FORMAT_UNSUPPORTED, + GAME_CAPTURE_FALLBACK_MULTISAMPLED, GAME_CAPTURE_FALLBACK_NONE, + GAME_CAPTURE_FALLBACK_SHARED_TEXTURE_UNSUPPORTED, GAME_CAPTURE_FLAG_FLIP_VERTICAL, + GAME_CAPTURE_FLAG_HDR, GAME_CAPTURE_FLAG_MULTISAMPLED, GAME_CAPTURE_FLAG_TEN_BIT, + GAME_CAPTURE_FRAME_PREFIX, GAME_CAPTURE_INFO_PREFIX, GAME_CAPTURE_KEEPALIVE_PREFIX, + GAME_CAPTURE_MAGIC, GAME_CAPTURE_PRESENT_CLOCK_QPC, GAME_CAPTURE_READY_PREFIX, + GAME_CAPTURE_STATE_ACTIVE, GAME_CAPTURE_STATE_ERROR, GAME_CAPTURE_STATE_RESIZE_REQUIRED, + GAME_CAPTURE_STATE_STOPPED, GAME_CAPTURE_STOP_PREFIX, GAME_CAPTURE_TRANSPORT_MEMORY, + GAME_CAPTURE_TRANSPORT_SHARED_TEXTURE, GameCaptureSharedInfo, env_flag_enabled, + frame_buffer_size, host_supports_present_clock, mutex_name, object_name, qpc_now_us, +}; +pub(crate) use inline_hook::Detour; +use std::{ + ffi::c_void, + mem, + ptr::{null, null_mut}, + sync::{ + Mutex, OnceLock, + atomic::{AtomicBool, AtomicU32, Ordering}, + }, +}; +use windows::{ + Win32::{ + Foundation::{HMODULE as WinHmodule, HWND as WinHwnd}, + Graphics::{ + Direct3D::{D3D_DRIVER_TYPE_HARDWARE, D3D_FEATURE_LEVEL_11_0}, + Direct3D10::{ + D3D10_BIND_RENDER_TARGET, D3D10_BIND_SHADER_RESOURCE, D3D10_RESOURCE_MISC_SHARED, + D3D10_TEXTURE2D_DESC, D3D10_USAGE_DEFAULT, ID3D10Device, ID3D10Texture2D, + }, + Direct3D11::{ + D3D11_BIND_RENDER_TARGET, D3D11_BIND_SHADER_RESOURCE, D3D11_CPU_ACCESS_READ, + D3D11_CREATE_DEVICE_FLAG, D3D11_MAP_READ, D3D11_RESOURCE_MISC_SHARED, + D3D11_SDK_VERSION, D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, D3D11_USAGE_STAGING, + D3D11CreateDeviceAndSwapChain, ID3D11Device, ID3D11DeviceContext, ID3D11Resource, + ID3D11Texture2D, + }, + Direct3D11on12::{D3D11_RESOURCE_FLAGS, D3D11On12CreateDevice, ID3D11On12Device}, + Direct3D12::{ + D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_DESC, + D3D12_RESOURCE_STATE_PRESENT, ID3D12CommandQueue, ID3D12Device, ID3D12Resource, + }, + Dxgi::{ + Common::{ + DXGI_FORMAT, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, + DXGI_FORMAT_B8G8R8X8_UNORM, DXGI_FORMAT_B8G8R8X8_UNORM_SRGB, + DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM, + DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_FORMAT_R16G16B16A16_UNORM, DXGI_MODE_DESC, + DXGI_MODE_SCALING_UNSPECIFIED, DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, + DXGI_RATIONAL, DXGI_SAMPLE_DESC, + }, + DXGI_PRESENT_PARAMETERS, DXGI_SWAP_CHAIN_DESC, DXGI_SWAP_EFFECT_DISCARD, + DXGI_USAGE_RENDER_TARGET_OUTPUT, IDXGIResource, IDXGISwapChain, IDXGISwapChain1, + IDXGISwapChain3, + }, + }, + }, + core::{BOOL as WinBool, GUID, HRESULT, IUnknown, Interface}, +}; +use windows_sys::Win32::Foundation::{LPARAM as WinLparam, LRESULT, WPARAM}; +use windows_sys::{ + Win32::{ + Foundation::{CloseHandle, HANDLE, HINSTANCE, HWND, WAIT_ABANDONED, WAIT_OBJECT_0}, + Graphics::{ + Gdi::{HDC, WindowFromDC}, + OpenGL::{ + GL_BACK, GL_BGRA_EXT, GL_PACK_ALIGNMENT, GL_PACK_LSB_FIRST, GL_PACK_ROW_LENGTH, + GL_PACK_SKIP_PIXELS, GL_PACK_SKIP_ROWS, GL_PACK_SWAP_BYTES, GL_READ_BUFFER, + GL_UNSIGNED_BYTE, glGetIntegerv, glPixelStorei, glReadBuffer, glReadPixels, + wglGetProcAddress, + }, + }, + System::{ + LibraryLoader::{DisableThreadLibraryCalls, GetModuleHandleW, GetProcAddress}, + Memory::{ + FILE_MAP_ALL_ACCESS, MEMORY_MAPPED_VIEW_ADDRESS, MapViewOfFile, OpenFileMappingW, + UnmapViewOfFile, + }, + SystemServices::DLL_PROCESS_ATTACH, + Threading::{ + CreateThread, EVENT_ALL_ACCESS, MUTEX_ALL_ACCESS, OpenEventW, OpenMutexW, + ReleaseMutex, SYNCHRONIZATION_SYNCHRONIZE, SetEvent, Sleep, WaitForSingleObject, + }, + }, + UI::WindowsAndMessaging::{ + CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, CallNextHookEx, CreateWindowExW, DefWindowProcW, + DestroyWindow, GetClientRect, IsWindow, RegisterClassW, WNDCLASSW, WS_OVERLAPPEDWINDOW, + }, + }, + core::BOOL, +}; + +type SwapBuffersFn = unsafe extern "system" fn(HDC) -> BOOL; +type WglSwapLayerBuffersFn = unsafe extern "system" fn(HDC, u32) -> BOOL; +type GlBindBufferFn = unsafe extern "system" fn(u32, u32); +type DxgiPresentFn = unsafe extern "system" fn(*mut c_void, u32, u32) -> HRESULT; +type DxgiPresent1Fn = + unsafe extern "system" fn(*mut c_void, u32, u32, *const DXGI_PRESENT_PARAMETERS) -> HRESULT; +type D3d12ExecuteCommandListsFn = unsafe extern "system" fn(*mut c_void, u32, *const *mut c_void); +type D3d12CreateDeviceFn = unsafe extern "system" fn( + *mut c_void, + windows::Win32::Graphics::Direct3D::D3D_FEATURE_LEVEL, + *const GUID, + *mut *mut c_void, +) -> HRESULT; + +static mut SWAP_BUFFERS_HOOK: Option> = None; +static mut WGL_SWAP_LAYER_BUFFERS_HOOK: Option> = None; +static mut DXGI_PRESENT_HOOK: Option> = None; +static mut DXGI_PRESENT1_HOOK: Option> = None; +static mut D3D12_EXECUTE_COMMAND_LISTS_HOOK: Option> = None; +pub(crate) static HOOKS_READY: AtomicBool = AtomicBool::new(false); +static DXGI_PRESENT_DEPTH: AtomicU32 = AtomicU32::new(0); +static DXGI_PRESENT_ATTEMPTED: AtomicBool = AtomicBool::new(false); +static DXGI_PRESENT_FIRED: AtomicBool = AtomicBool::new(false); +static DXGI_PRESENT1_FIRED: AtomicBool = AtomicBool::new(false); +static OPENGL_PRESENT_FIRED: AtomicBool = AtomicBool::new(false); +static D3D12_EXECUTE_FIRED: AtomicBool = AtomicBool::new(false); + +const GL_PIXEL_PACK_BUFFER: u32 = 0x88EB; +const GL_PIXEL_PACK_BUFFER_BINDING: u32 = 0x88ED; + +const MAX_D3D12_QUEUE_CANDIDATES: usize = 8; +static D3D12_QUEUE_CANDIDATES: Mutex> = Mutex::new(Vec::new()); + +#[derive(Clone, Copy, PartialEq, Eq)] +pub(crate) enum D3d11FormatMode { + Bgra, + Rgba, +} + +struct D3d11CaptureState { + device_ptr: usize, + context: ID3D11DeviceContext, + texture: ID3D11Texture2D, + staging: Option, + shared_handle: u64, + width: u32, + height: u32, + format: DXGI_FORMAT, + format_mode: D3d11FormatMode, + sample_count: u32, + force_cpu: bool, +} + +struct D3d12CaptureState { + device_ptr: usize, + device11: ID3D11Device, + context11: ID3D11DeviceContext, + device11on12: ID3D11On12Device, + copy_tex: ID3D11Texture2D, + staging: Option, + shared_handle: u64, + width: u32, + height: u32, + format: DXGI_FORMAT, + format_mode: D3d11FormatMode, + backbuffer_count: u32, + current_backbuffer: u32, + dxgi14: bool, + force_cpu: bool, +} + +struct D3d10CaptureState { + device_ptr: usize, + device: ID3D10Device, + texture: ID3D10Texture2D, + staging: Option, + shared_handle: u64, + width: u32, + height: u32, + format: DXGI_FORMAT, + format_mode: D3d11FormatMode, + sample_count: u32, + force_cpu: bool, +} + +pub(crate) struct HookState { + info_map: HANDLE, + frame_map: HANDLE, + ready_event: HANDLE, + stop_event: HANDLE, + mutexes: [HANDLE; GAME_CAPTURE_BUFFER_COUNT], + pub(crate) info: *mut GameCaptureSharedInfo, + frame_base: *mut u8, + frame_buffer_capacity: usize, + next_frame_index: usize, + scratch: Vec, + last_frame_ns: u64, + keepalive_name: Vec, + d3d11: Option, + d3d12: Option, + d3d10: Option, + pub(crate) d3d9: Option, + pub(crate) gl_interop: Option, +} + +unsafe impl Send for HookState {} + +static mut STATE: Option = None; + +pub(crate) fn wide(value: &str) -> Vec { + value.encode_utf16().chain(std::iter::once(0)).collect() +} + +fn current_pid() -> u32 { + std::process::id() +} + +unsafe fn close_handle(handle: &mut HANDLE) { + if !handle.is_null() { + CloseHandle(*handle); + *handle = null_mut(); + } +} + +unsafe fn open_event(prefix: &str, pid: u32) -> HANDLE { + let name = wide(&object_name(prefix, pid)); + OpenEventW(EVENT_ALL_ACCESS, 0, name.as_ptr()) +} + +unsafe fn open_mutex(name: &str) -> HANDLE { + let name = wide(name); + OpenMutexW(MUTEX_ALL_ACCESS, 0, name.as_ptr()) +} + +unsafe fn parent_alive(keepalive_name: &[u16]) -> bool { + let handle = OpenMutexW(SYNCHRONIZATION_SYNCHRONIZE, 0, keepalive_name.as_ptr()); + if handle.is_null() { + return false; + } + CloseHandle(handle); + true +} + +unsafe fn map_view(handle: HANDLE, size: usize) -> *mut T { + let view = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, size); + view.Value.cast() +} + +unsafe fn open_ipc() -> Option { + let pid = current_pid(); + let info_name = wide(&object_name(GAME_CAPTURE_INFO_PREFIX, pid)); + let frame_name = wide(&object_name(GAME_CAPTURE_FRAME_PREFIX, pid)); + let info_map = OpenFileMappingW(FILE_MAP_ALL_ACCESS, 0, info_name.as_ptr()); + if info_map.is_null() { + return None; + } + let frame_map = OpenFileMappingW(FILE_MAP_ALL_ACCESS, 0, frame_name.as_ptr()); + if frame_map.is_null() { + let mut handle = info_map; + close_handle(&mut handle); + return None; + } + let info = map_view::(info_map, mem::size_of::()); + if info.is_null() || (*info).magic != GAME_CAPTURE_MAGIC { + let mut info_map = info_map; + let mut frame_map = frame_map; + close_handle(&mut frame_map); + close_handle(&mut info_map); + return None; + } + let max_width = (*info).max_width; + let max_height = (*info).max_height; + let frame_buffer_capacity = frame_buffer_size(max_width, max_height).unwrap_or(0); + if frame_buffer_capacity == 0 { + let mut info_map = info_map; + let mut frame_map = frame_map; + close_handle(&mut frame_map); + close_handle(&mut info_map); + return None; + } + let frame_map_size = frame_buffer_capacity * GAME_CAPTURE_BUFFER_COUNT; + let frame_base = map_view::(frame_map, frame_map_size); + if frame_base.is_null() { + let mut info_map = info_map; + let mut frame_map = frame_map; + close_handle(&mut frame_map); + close_handle(&mut info_map); + return None; + } + + let ready_event = open_event(GAME_CAPTURE_READY_PREFIX, pid); + let stop_event = open_event(GAME_CAPTURE_STOP_PREFIX, pid); + let mutex_names = [mutex_name(pid, 0), mutex_name(pid, 1)]; + let mutexes = [open_mutex(&mutex_names[0]), open_mutex(&mutex_names[1])]; + if ready_event.is_null() + || stop_event.is_null() + || mutexes.iter().any(|handle| handle.is_null()) + { + let mut state = HookState { + info_map, + frame_map, + ready_event, + stop_event, + mutexes, + info, + frame_base, + frame_buffer_capacity, + next_frame_index: 0, + scratch: Vec::new(), + last_frame_ns: 0, + keepalive_name: wide(&object_name(GAME_CAPTURE_KEEPALIVE_PREFIX, pid)), + d3d11: None, + d3d12: None, + d3d10: None, + d3d9: None, + gl_interop: None, + }; + free_state(&mut state); + return None; + } + + Some(HookState { + info_map, + frame_map, + ready_event, + stop_event, + mutexes, + info, + frame_base, + frame_buffer_capacity, + next_frame_index: 0, + scratch: Vec::new(), + last_frame_ns: 0, + keepalive_name: wide(&object_name(GAME_CAPTURE_KEEPALIVE_PREFIX, pid)), + d3d11: None, + d3d12: None, + d3d10: None, + d3d9: None, + gl_interop: None, + }) +} + +unsafe fn free_state(state: &mut HookState) { + if !state.info.is_null() { + UnmapViewOfFile(MEMORY_MAPPED_VIEW_ADDRESS { + Value: state.info.cast(), + }); + state.info = null_mut(); + } + if !state.frame_base.is_null() { + UnmapViewOfFile(MEMORY_MAPPED_VIEW_ADDRESS { + Value: state.frame_base.cast(), + }); + state.frame_base = null_mut(); + } + close_handle(&mut state.mutexes[0]); + close_handle(&mut state.mutexes[1]); + close_handle(&mut state.stop_event); + close_handle(&mut state.ready_event); + close_handle(&mut state.frame_map); + close_handle(&mut state.info_map); +} + +pub(crate) unsafe fn ensure_state() -> Option<&'static mut HookState> { + if STATE.is_none() { + STATE = open_ipc(); + } + STATE.as_mut() +} + +pub(crate) unsafe fn clear_state() { + if let Some(mut state) = STATE.take() { + free_state(&mut state); + } + reset_d3d12_queue_candidates(); +} + +pub(crate) fn now_ns() -> u64 { + use std::time::{SystemTime, UNIX_EPOCH}; + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_nanos() as u64) + .unwrap_or(0) +} + +pub(crate) fn now_us() -> i64 { + (now_ns() / 1_000) as i64 +} + +pub(crate) unsafe fn mark_present(state: &mut HookState, api: u32) { + let info = &mut *state.info; + info.api_type = api; + if host_supports_present_clock(info.version) { + info.present_clock = GAME_CAPTURE_PRESENT_CLOCK_QPC; + info.last_present_timestamp_us = qpc_now_us(); + } else { + info.last_present_timestamp_us = now_us(); + } +} + +pub(crate) unsafe fn record_dropped_frame(state: &mut HookState) { + let info = &mut *state.info; + info.dropped_frame_counter = info.dropped_frame_counter.wrapping_add(1); +} + +pub(crate) unsafe fn set_fallback_reason(state: &mut HookState, reason: u32) { + (*state.info).fallback_reason = reason; +} + +pub(crate) unsafe fn set_capture_flags(state: &mut HookState, flags: u32) { + (*state.info).capture_flags = flags; +} + +static VERBOSE_LOG_PATH: std::sync::OnceLock> = + std::sync::OnceLock::new(); + +fn verbose_log_path() -> &'static Option { + VERBOSE_LOG_PATH.get_or_init(|| { + if !env_flag_enabled(ENV_VERBOSE) { + return None; + } + let mut path = std::env::temp_dir(); + path.push(format!("fluxer-game-hook-{}.log", current_pid())); + Some(path) + }) +} + +pub(crate) fn verbose_log(message: &str) { + let Some(path) = verbose_log_path() else { + return; + }; + let text = format!("[fluxer-game-hook] {message}"); + + { + use std::io::Write; + if let Ok(mut file) = std::fs::OpenOptions::new() + .create(true) + .append(true) + .open(path) + { + let _ = writeln!( + file, + "[{} pid={}] {text}", + now_ns() / 1_000_000, + current_pid() + ); + } + } + + let line = wide(&text); + unsafe { + windows_sys::Win32::System::Diagnostics::Debug::OutputDebugStringW(line.as_ptr()); + } + use std::io::Write; + let _ = writeln!(std::io::stderr(), "{text}"); +} + +pub(crate) unsafe fn capture_should_run(state: &mut HookState) -> bool { + if WaitForSingleObject(state.stop_event, 0) == WAIT_OBJECT_0 { + (*state.info).state = GAME_CAPTURE_STATE_STOPPED; + return false; + } + if !parent_alive(&state.keepalive_name) { + (*state.info).state = GAME_CAPTURE_STATE_STOPPED; + return false; + } + true +} + +unsafe fn client_size_from_hdc(hdc: HDC) -> Option<(HWND, u32, u32)> { + let hwnd = WindowFromDC(hdc); + if hwnd.is_null() || IsWindow(hwnd) == 0 { + return None; + } + let mut rect = mem::zeroed(); + if GetClientRect(hwnd, &mut rect) == 0 { + return None; + } + let width = (rect.right - rect.left).max(1) as u32; + let height = (rect.bottom - rect.top).max(1) as u32; + Some((hwnd, width, height)) +} + +pub(crate) unsafe fn frame_due(state: &mut HookState) -> bool { + let info = &*state.info; + let interval = info.target_frame_interval_ns; + if interval == 0 { + return true; + } + let now = now_ns(); + if now.saturating_sub(state.last_frame_ns) < interval { + return false; + } + state.last_frame_ns = now; + true +} + +pub(crate) unsafe fn write_bgra_rows_to_shared_memory( + state: &mut HookState, + hwnd: HWND, + width: u32, + height: u32, + src_base: *const u8, + src_row_pitch: usize, + flip_vertical: bool, + format_mode: D3d11FormatMode, +) -> bool { + if src_base.is_null() { + (*state.info).state = GAME_CAPTURE_STATE_ERROR; + (*state.info).last_error = 2; + return false; + } + let max_width = (*state.info).max_width; + let max_height = (*state.info).max_height; + if width == 0 || height == 0 || width > max_width || height > max_height { + (*state.info).state = GAME_CAPTURE_STATE_RESIZE_REQUIRED; + (*state.info).width = width; + (*state.info).height = height; + return false; + } + let Some(bytes) = frame_buffer_size(width, height) else { + (*state.info).state = GAME_CAPTURE_STATE_ERROR; + (*state.info).last_error = 1; + return false; + }; + if bytes > state.frame_buffer_capacity { + (*state.info).state = GAME_CAPTURE_STATE_RESIZE_REQUIRED; + (*state.info).width = width; + (*state.info).height = height; + return false; + } + + let mut index = state.next_frame_index; + let mut mutex = state.mutexes[index]; + let mut wait = WaitForSingleObject(mutex, 0); + if wait != WAIT_OBJECT_0 && wait != WAIT_ABANDONED { + index = (index + 1) % GAME_CAPTURE_BUFFER_COUNT; + mutex = state.mutexes[index]; + wait = WaitForSingleObject(mutex, 0); + } + if wait != WAIT_OBJECT_0 && wait != WAIT_ABANDONED { + record_dropped_frame(state); + return false; + } + + let row_bytes = (width * 4) as usize; + let dst_base = state.frame_base.add(state.frame_buffer_capacity * index); + for y in 0..height as usize { + let src_y = if flip_vertical { + height as usize - 1 - y + } else { + y + }; + let src = src_base.add(src_y * src_row_pitch); + let dst = dst_base.add(y * row_bytes); + match format_mode { + D3d11FormatMode::Bgra => std::ptr::copy_nonoverlapping(src, dst, row_bytes), + D3d11FormatMode::Rgba => { + for x in 0..width as usize { + let src_px = src.add(x * 4); + let dst_px = dst.add(x * 4); + *dst_px.add(0) = *src_px.add(2); + *dst_px.add(1) = *src_px.add(1); + *dst_px.add(2) = *src_px.add(0); + *dst_px.add(3) = *src_px.add(3); + } + } + } + } + + let info = &mut *state.info; + info.hwnd = hwnd as usize as u64; + info.width = width; + info.height = height; + info.pitch = width * 4; + info.frame_index = index as u32; + info.frame_counter = info.frame_counter.wrapping_add(1); + info.timestamp_us = now_us(); + info.transport = GAME_CAPTURE_TRANSPORT_MEMORY; + info.dxgi_format = 0; + info.texture_handle = 0; + info.state = GAME_CAPTURE_STATE_ACTIVE; + state.next_frame_index = (index + 1) % GAME_CAPTURE_BUFFER_COUNT; + + ReleaseMutex(mutex); + SetEvent(state.ready_event); + true +} + +pub(crate) unsafe fn publish_shared_texture_frame( + state: &mut HookState, + hwnd: HWND, + width: u32, + height: u32, + dxgi_format: DXGI_FORMAT, + shared_handle: u64, +) -> bool { + let max_width = (*state.info).max_width; + let max_height = (*state.info).max_height; + if width == 0 || height == 0 || width > max_width || height > max_height { + (*state.info).state = GAME_CAPTURE_STATE_RESIZE_REQUIRED; + (*state.info).width = width; + (*state.info).height = height; + return false; + } + if shared_handle == 0 { + (*state.info).state = GAME_CAPTURE_STATE_ERROR; + (*state.info).last_error = 40; + return false; + } + + let info = &mut *state.info; + info.hwnd = hwnd as usize as u64; + info.width = width; + info.height = height; + info.pitch = width * 4; + info.frame_index = 0; + info.frame_counter = info.frame_counter.wrapping_add(1); + info.timestamp_us = now_us(); + info.transport = GAME_CAPTURE_TRANSPORT_SHARED_TEXTURE; + info.dxgi_format = dxgi_format.0 as u32; + info.texture_handle = shared_handle; + info.state = GAME_CAPTURE_STATE_ACTIVE; + SetEvent(state.ready_event); + true +} + +unsafe fn gl_bind_buffer_proc() -> Option { + static GL_BIND_BUFFER: OnceLock> = OnceLock::new(); + *GL_BIND_BUFFER.get_or_init(|| { + let Some(proc) = wglGetProcAddress(c"glBindBuffer".as_ptr().cast()) else { + return None; + }; + let address = proc as usize; + if address <= 3 || address == usize::MAX { + return None; + } + Some(mem::transmute::< + unsafe extern "system" fn() -> isize, + GlBindBufferFn, + >(proc)) + }) +} + +unsafe fn write_frame_to_shared_memory( + state: &mut HookState, + hwnd: HWND, + width: u32, + height: u32, +) -> bool { + let Some(bytes) = frame_buffer_size(width, height) else { + (*state.info).state = GAME_CAPTURE_STATE_ERROR; + (*state.info).last_error = 1; + return false; + }; + if state.scratch.len() != bytes { + state.scratch.resize(bytes, 0); + } + + let mut previous_read_buffer = 0i32; + let mut prev_alignment = 4i32; + let mut prev_row_length = 0i32; + let mut prev_skip_pixels = 0i32; + let mut prev_skip_rows = 0i32; + let mut prev_swap_bytes = 0i32; + let mut prev_lsb_first = 0i32; + let mut prev_pack_buffer = 0i32; + glGetIntegerv(GL_READ_BUFFER, &mut previous_read_buffer); + glGetIntegerv(GL_PACK_ALIGNMENT, &mut prev_alignment); + glGetIntegerv(GL_PACK_ROW_LENGTH, &mut prev_row_length); + glGetIntegerv(GL_PACK_SKIP_PIXELS, &mut prev_skip_pixels); + glGetIntegerv(GL_PACK_SKIP_ROWS, &mut prev_skip_rows); + glGetIntegerv(GL_PACK_SWAP_BYTES, &mut prev_swap_bytes); + glGetIntegerv(GL_PACK_LSB_FIRST, &mut prev_lsb_first); + glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &mut prev_pack_buffer); + + glReadBuffer(GL_BACK); + let bind_buffer = if prev_pack_buffer != 0 { + match gl_bind_buffer_proc() { + Some(bind_buffer) => { + bind_buffer(GL_PIXEL_PACK_BUFFER, 0); + Some(bind_buffer) + } + None => { + record_dropped_frame(state); + return false; + } + } + } else { + None + }; + glPixelStorei(GL_PACK_ALIGNMENT, 4); + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + glPixelStorei(GL_PACK_SKIP_PIXELS, 0); + glPixelStorei(GL_PACK_SKIP_ROWS, 0); + glPixelStorei(GL_PACK_SWAP_BYTES, 0); + glPixelStorei(GL_PACK_LSB_FIRST, 0); + glReadPixels( + 0, + 0, + width as i32, + height as i32, + GL_BGRA_EXT, + GL_UNSIGNED_BYTE, + state.scratch.as_mut_ptr().cast(), + ); + + glReadBuffer(previous_read_buffer as u32); + glPixelStorei(GL_PACK_ALIGNMENT, prev_alignment); + glPixelStorei(GL_PACK_ROW_LENGTH, prev_row_length); + glPixelStorei(GL_PACK_SKIP_PIXELS, prev_skip_pixels); + glPixelStorei(GL_PACK_SKIP_ROWS, prev_skip_rows); + glPixelStorei(GL_PACK_SWAP_BYTES, prev_swap_bytes); + glPixelStorei(GL_PACK_LSB_FIRST, prev_lsb_first); + if let Some(bind_buffer) = bind_buffer { + bind_buffer(GL_PIXEL_PACK_BUFFER, prev_pack_buffer as u32); + } + + write_bgra_rows_to_shared_memory( + state, + hwnd, + width, + height, + state.scratch.as_ptr(), + (width * 4) as usize, + true, + D3d11FormatMode::Bgra, + ) +} + +unsafe fn capture_opengl_frame(hdc: HDC) { + if !HOOKS_READY.load(Ordering::Acquire) { + return; + } + let should_continue = { + let Some(state) = ensure_state() else { + return; + }; + capture_should_run(state) + }; + if !should_continue { + clear_state(); + return; + } + let Some(state) = ensure_state() else { + return; + }; + mark_present(state, GAME_CAPTURE_API_OPENGL); + set_fallback_reason(state, GAME_CAPTURE_FALLBACK_NONE); + if !frame_due(state) { + record_dropped_frame(state); + return; + } + let Some((hwnd, width, height)) = client_size_from_hdc(hdc) else { + return; + }; + + let force_cpu = env_flag_enabled(ENV_FORCE_CPU); + let enable_shared_texture = env_flag_enabled(ENV_ENABLE_OPENGL_SHARED_TEXTURE) + || env_flag_enabled(ENV_FORCE_SHARED_TEXTURE); + let disable_shared_texture = !enable_shared_texture + || env_flag_enabled(ENV_DISABLE_OPENGL_SHARED_TEXTURE) + || ((*state.info).control & GAME_CAPTURE_CONTROL_DISABLE_SHARED_TEXTURE) != 0; + if disable_shared_texture { + if state.gl_interop.is_some() { + verbose_log("opengl interop: shared texture disabled; using memory fallback"); + } + state.gl_interop = None; + } + + if !force_cpu + && !disable_shared_texture + && !gl_interop::gpu_path_disabled() + && gl_interop::capture_opengl_frame_gpu(state, hwnd, width, height) + { + return; + } + + set_capture_flags(state, GAME_CAPTURE_FLAG_FLIP_VERTICAL); + if force_cpu { + set_fallback_reason(state, GAME_CAPTURE_FALLBACK_FORCED_CPU); + } else if disable_shared_texture || gl_interop::gpu_path_disabled() { + set_fallback_reason(state, GAME_CAPTURE_FALLBACK_SHARED_TEXTURE_UNSUPPORTED); + } + let _ = write_frame_to_shared_memory(state, hwnd, width, height); +} + +fn d3d11_format_mode(format: DXGI_FORMAT) -> Option { + if format == DXGI_FORMAT_B8G8R8A8_UNORM + || format == DXGI_FORMAT_B8G8R8A8_UNORM_SRGB + || format == DXGI_FORMAT_B8G8R8X8_UNORM + || format == DXGI_FORMAT_B8G8R8X8_UNORM_SRGB + { + Some(D3d11FormatMode::Bgra) + } else if format == DXGI_FORMAT_R8G8B8A8_UNORM || format == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB { + Some(D3d11FormatMode::Rgba) + } else { + None + } +} + +fn dxgi_hdr_flags(format: DXGI_FORMAT) -> u32 { + if format == DXGI_FORMAT_R10G10B10A2_UNORM || format == DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM { + GAME_CAPTURE_FLAG_TEN_BIT | GAME_CAPTURE_FLAG_HDR + } else if format == DXGI_FORMAT_R16G16B16A16_FLOAT || format == DXGI_FORMAT_R16G16B16A16_UNORM { + GAME_CAPTURE_FLAG_HDR + } else { + 0 + } +} + +#[derive(Clone, Copy, PartialEq, Eq)] +pub(crate) enum DxgiCapturePlan { + Sdr8 { format_mode: D3d11FormatMode }, + HdrShared { flags: u32 }, + Unsupported, +} + +fn dxgi_capture_plan(format: DXGI_FORMAT, force_cpu: bool) -> DxgiCapturePlan { + if let Some(format_mode) = d3d11_format_mode(format) { + return DxgiCapturePlan::Sdr8 { format_mode }; + } + let hdr_flags = dxgi_hdr_flags(format); + if hdr_flags != 0 { + if force_cpu { + return DxgiCapturePlan::Unsupported; + } + return DxgiCapturePlan::HdrShared { flags: hdr_flags }; + } + DxgiCapturePlan::Unsupported +} + +unsafe fn publish_d3d11_staging_cpu( + state: &mut HookState, + context: &ID3D11DeviceContext, + staging: &ID3D11Texture2D, + hwnd: HWND, + width: u32, + height: u32, + format_mode: D3d11FormatMode, +) -> bool { + use windows::Win32::Graphics::Direct3D11::D3D11_MAPPED_SUBRESOURCE; + let resource: ID3D11Resource = match staging.cast() { + Ok(resource) => resource, + Err(_) => { + record_dropped_frame(state); + return false; + } + }; + let mut mapped = D3D11_MAPPED_SUBRESOURCE::default(); + if context + .Map(&resource, 0, D3D11_MAP_READ, 0, Some(&mut mapped)) + .is_err() + { + record_dropped_frame(state); + return false; + } + let published = write_bgra_rows_to_shared_memory( + state, + hwnd, + width, + height, + mapped.pData as *const u8, + mapped.RowPitch as usize, + false, + format_mode, + ); + context.Unmap(&resource, 0); + published +} + +unsafe fn d3d11_state_for_frame<'a>( + state: &'a mut HookState, + device_ptr: usize, + context: ID3D11DeviceContext, + device: &windows::Win32::Graphics::Direct3D11::ID3D11Device, + desc: &D3D11_TEXTURE2D_DESC, + format_mode: D3d11FormatMode, + force_cpu: bool, +) -> Option<&'a mut D3d11CaptureState> { + let sample_count = desc.SampleDesc.Count.max(1); + let recreate = state + .d3d11 + .as_ref() + .map(|capture| { + capture.device_ptr != device_ptr + || capture.width != desc.Width + || capture.height != desc.Height + || capture.format != desc.Format + || capture.sample_count != sample_count + || capture.force_cpu != force_cpu + }) + .unwrap_or(true); + if recreate { + let mut copy_desc = *desc; + copy_desc.MipLevels = 1; + copy_desc.ArraySize = 1; + copy_desc.SampleDesc.Count = 1; + copy_desc.SampleDesc.Quality = 0; + copy_desc.Usage = D3D11_USAGE_DEFAULT; + copy_desc.BindFlags = (D3D11_BIND_SHADER_RESOURCE.0 | D3D11_BIND_RENDER_TARGET.0) as u32; + copy_desc.CPUAccessFlags = 0; + copy_desc.MiscFlags = if force_cpu { + 0 + } else { + D3D11_RESOURCE_MISC_SHARED.0 as u32 + }; + let mut texture = None; + if device + .CreateTexture2D(©_desc, None, Some(&mut texture)) + .is_err() + { + (*state.info).state = GAME_CAPTURE_STATE_ERROR; + (*state.info).last_error = 20; + return None; + } + let Some(texture) = texture else { + (*state.info).state = GAME_CAPTURE_STATE_ERROR; + (*state.info).last_error = 20; + return None; + }; + + let (staging, shared_handle) = if force_cpu { + let mut staging_desc = copy_desc; + staging_desc.Usage = D3D11_USAGE_STAGING; + staging_desc.BindFlags = 0; + staging_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ.0 as u32; + staging_desc.MiscFlags = 0; + let mut staging = None; + if device + .CreateTexture2D(&staging_desc, None, Some(&mut staging)) + .is_err() + { + (*state.info).state = GAME_CAPTURE_STATE_ERROR; + (*state.info).last_error = 22; + return None; + } + (staging, 0u64) + } else { + let handle = match texture + .cast::() + .and_then(|resource| resource.GetSharedHandle()) + { + Ok(handle) => handle.0 as usize as u64, + Err(_) => { + (*state.info).state = GAME_CAPTURE_STATE_ERROR; + (*state.info).last_error = 21; + return None; + } + }; + (None, handle) + }; + + state.d3d11 = Some(D3d11CaptureState { + device_ptr, + context, + texture, + staging, + shared_handle, + width: desc.Width, + height: desc.Height, + format: desc.Format, + format_mode, + sample_count, + force_cpu, + }); + } + state.d3d11.as_mut() +} + +unsafe fn capture_d3d11_frame(swap_chain: *mut c_void) { + if !HOOKS_READY.load(Ordering::Acquire) { + return; + } + let should_continue = { + let Some(state) = ensure_state() else { + return; + }; + capture_should_run(state) + }; + if !should_continue { + clear_state(); + return; + } + let Some(state) = ensure_state() else { + return; + }; + + let Some(swap_chain) = IDXGISwapChain::from_raw_borrowed(&swap_chain) else { + return; + }; + let swap_desc = swap_chain.GetDesc().ok(); + let hwnd = swap_desc + .as_ref() + .map(|desc| desc.OutputWindow.0 as HWND) + .filter(|hwnd| !hwnd.is_null()) + .unwrap_or((*state.info).hwnd as usize as HWND); + + if swap_chain.GetDevice::().is_ok() { + capture_d3d10_frame_impl(state, swap_chain, hwnd); + return; + } + + let Ok(back_buffer) = swap_chain.GetBuffer::(0) else { + return; + }; + let mut desc = D3D11_TEXTURE2D_DESC::default(); + back_buffer.GetDesc(&mut desc); + if desc.Width == 0 || desc.Height == 0 { + return; + } + let multisampled = desc.SampleDesc.Count > 1; + let force_cpu = env_flag_enabled(ENV_FORCE_CPU); + + let (format_mode, hdr_flags) = match dxgi_capture_plan(desc.Format, force_cpu) { + DxgiCapturePlan::Sdr8 { format_mode } => (format_mode, 0u32), + DxgiCapturePlan::HdrShared { flags } => (D3d11FormatMode::Bgra, flags), + DxgiCapturePlan::Unsupported => { + mark_present(state, GAME_CAPTURE_API_D3D11); + set_capture_flags(state, dxgi_hdr_flags(desc.Format)); + set_fallback_reason(state, GAME_CAPTURE_FALLBACK_FORMAT_UNSUPPORTED); + record_dropped_frame(state); + return; + } + }; + + mark_present(state, GAME_CAPTURE_API_D3D11); + let mut flags = hdr_flags; + if multisampled { + flags |= GAME_CAPTURE_FLAG_MULTISAMPLED; + } + set_capture_flags(state, flags); + set_fallback_reason( + state, + if force_cpu { + GAME_CAPTURE_FALLBACK_FORCED_CPU + } else { + GAME_CAPTURE_FALLBACK_NONE + }, + ); + + if !frame_due(state) { + record_dropped_frame(state); + return; + } + let Ok(device) = back_buffer.GetDevice() else { + record_dropped_frame(state); + return; + }; + let Ok(context) = device.GetImmediateContext() else { + record_dropped_frame(state); + return; + }; + let device_ptr = device.as_raw() as usize; + let (context, texture, staging, shared_handle) = { + let Some(capture_state) = d3d11_state_for_frame( + state, + device_ptr, + context, + &device, + &desc, + format_mode, + force_cpu, + ) else { + record_dropped_frame(state); + return; + }; + ( + capture_state.context.clone(), + capture_state.texture.clone(), + capture_state.staging.clone(), + capture_state.shared_handle, + ) + }; + + if multisampled { + context.ResolveSubresource(&texture, 0, &back_buffer, 0, desc.Format); + } else { + context.CopyResource(&texture, &back_buffer); + } + + if force_cpu { + let Some(staging) = staging else { + record_dropped_frame(state); + return; + }; + context.CopyResource(&staging, &texture); + context.Flush(); + let _ = publish_d3d11_staging_cpu( + state, + &context, + &staging, + hwnd, + desc.Width, + desc.Height, + format_mode, + ); + return; + } + + context.Flush(); + let _ = publish_shared_texture_frame( + state, + hwnd, + desc.Width, + desc.Height, + desc.Format, + shared_handle, + ); +} + +unsafe fn d3d10_state_for_frame<'a>( + state: &'a mut HookState, + device_ptr: usize, + device: &ID3D10Device, + desc: &D3D10_TEXTURE2D_DESC, + format_mode: D3d11FormatMode, + force_cpu: bool, +) -> Option<&'a mut D3d10CaptureState> { + let sample_count = desc.SampleDesc.Count.max(1); + let recreate = state + .d3d10 + .as_ref() + .map(|capture| { + capture.device_ptr != device_ptr + || capture.width != desc.Width + || capture.height != desc.Height + || capture.format != desc.Format + || capture.sample_count != sample_count + || capture.force_cpu != force_cpu + }) + .unwrap_or(true); + if recreate { + let mut copy_desc = *desc; + copy_desc.MipLevels = 1; + copy_desc.ArraySize = 1; + copy_desc.SampleDesc.Count = 1; + copy_desc.SampleDesc.Quality = 0; + copy_desc.Usage = D3D10_USAGE_DEFAULT; + copy_desc.BindFlags = (D3D10_BIND_SHADER_RESOURCE.0 | D3D10_BIND_RENDER_TARGET.0) as u32; + copy_desc.CPUAccessFlags = 0; + copy_desc.MiscFlags = if force_cpu { + 0 + } else { + D3D10_RESOURCE_MISC_SHARED.0 as u32 + }; + let texture = match device.CreateTexture2D(©_desc, None) { + Ok(texture) => texture, + Err(_) => { + (*state.info).state = GAME_CAPTURE_STATE_ERROR; + (*state.info).last_error = 50; + return None; + } + }; + + let (staging, shared_handle) = if force_cpu { + use windows::Win32::Graphics::Direct3D10::{ + D3D10_CPU_ACCESS_READ, D3D10_USAGE_STAGING, + }; + let mut staging_desc = copy_desc; + staging_desc.Usage = D3D10_USAGE_STAGING; + staging_desc.BindFlags = 0; + staging_desc.CPUAccessFlags = D3D10_CPU_ACCESS_READ.0 as u32; + staging_desc.MiscFlags = 0; + let staging = match device.CreateTexture2D(&staging_desc, None) { + Ok(staging) => staging, + Err(_) => { + (*state.info).state = GAME_CAPTURE_STATE_ERROR; + (*state.info).last_error = 52; + return None; + } + }; + (Some(staging), 0u64) + } else { + let handle = match texture + .cast::() + .and_then(|resource| resource.GetSharedHandle()) + { + Ok(handle) => handle.0 as usize as u64, + Err(_) => { + (*state.info).state = GAME_CAPTURE_STATE_ERROR; + (*state.info).last_error = 51; + return None; + } + }; + (None, handle) + }; + + state.d3d10 = Some(D3d10CaptureState { + device_ptr, + device: device.clone(), + texture, + staging, + shared_handle, + width: desc.Width, + height: desc.Height, + format: desc.Format, + format_mode, + sample_count, + force_cpu, + }); + } + state.d3d10.as_mut() +} + +unsafe fn publish_d3d10_staging_cpu( + state: &mut HookState, + staging: &ID3D10Texture2D, + hwnd: HWND, + width: u32, + height: u32, + format_mode: D3d11FormatMode, +) -> bool { + use windows::Win32::Graphics::Direct3D10::D3D10_MAP_READ; + let mapped = match staging.Map(0, D3D10_MAP_READ, 0) { + Ok(mapped) => mapped, + Err(_) => { + record_dropped_frame(state); + return false; + } + }; + let published = write_bgra_rows_to_shared_memory( + state, + hwnd, + width, + height, + mapped.pData as *const u8, + mapped.RowPitch as usize, + false, + format_mode, + ); + staging.Unmap(0); + published +} + +unsafe fn capture_d3d10_frame_impl(state: &mut HookState, swap_chain: &IDXGISwapChain, hwnd: HWND) { + let Ok(back_buffer) = swap_chain.GetBuffer::(0) else { + return; + }; + let mut desc = D3D10_TEXTURE2D_DESC::default(); + back_buffer.GetDesc(&mut desc); + if desc.Width == 0 || desc.Height == 0 { + return; + } + let multisampled = desc.SampleDesc.Count > 1; + let force_cpu = env_flag_enabled(ENV_FORCE_CPU); + + let (format_mode, hdr_flags) = match dxgi_capture_plan(desc.Format, force_cpu) { + DxgiCapturePlan::Sdr8 { format_mode } => (format_mode, 0u32), + DxgiCapturePlan::HdrShared { flags } => (D3d11FormatMode::Bgra, flags), + DxgiCapturePlan::Unsupported => { + mark_present(state, GAME_CAPTURE_API_D3D10); + set_capture_flags(state, dxgi_hdr_flags(desc.Format)); + set_fallback_reason(state, GAME_CAPTURE_FALLBACK_FORMAT_UNSUPPORTED); + record_dropped_frame(state); + return; + } + }; + + mark_present(state, GAME_CAPTURE_API_D3D10); + let mut flags = hdr_flags; + if multisampled { + flags |= GAME_CAPTURE_FLAG_MULTISAMPLED; + } + set_capture_flags(state, flags); + set_fallback_reason( + state, + if force_cpu { + GAME_CAPTURE_FALLBACK_FORCED_CPU + } else { + GAME_CAPTURE_FALLBACK_NONE + }, + ); + + if !frame_due(state) { + record_dropped_frame(state); + return; + } + + let Ok(device) = swap_chain.GetDevice::() else { + record_dropped_frame(state); + return; + }; + let device_ptr = device.as_raw() as usize; + let (device, texture, staging, shared_handle) = { + let Some(capture_state) = + d3d10_state_for_frame(state, device_ptr, &device, &desc, format_mode, force_cpu) + else { + record_dropped_frame(state); + return; + }; + ( + capture_state.device.clone(), + capture_state.texture.clone(), + capture_state.staging.clone(), + capture_state.shared_handle, + ) + }; + + if multisampled { + device.ResolveSubresource(&texture, 0, &back_buffer, 0, desc.Format); + } else { + device.CopyResource(&texture, &back_buffer); + } + + if force_cpu { + let Some(staging) = staging else { + record_dropped_frame(state); + return; + }; + device.CopyResource(&staging, &texture); + device.Flush(); + let _ = + publish_d3d10_staging_cpu(state, &staging, hwnd, desc.Width, desc.Height, format_mode); + return; + } + + device.Flush(); + let _ = publish_shared_texture_frame( + state, + hwnd, + desc.Width, + desc.Height, + desc.Format, + shared_handle, + ); +} + +unsafe fn reset_d3d12_queue_candidates() { + if let Ok(mut candidates) = D3D12_QUEUE_CANDIDATES.lock() { + candidates.clear(); + } + DXGI_PRESENT_ATTEMPTED.store(false, Ordering::Release); +} + +unsafe fn remember_d3d12_queue_candidate(queue: *mut c_void) { + if queue.is_null() { + return; + } + let Some(queue_ref) = ID3D12CommandQueue::from_raw_borrowed(&queue) else { + return; + }; + if queue_ref.GetDesc().Type != D3D12_COMMAND_LIST_TYPE_DIRECT { + return; + } + let Ok(mut candidates) = D3D12_QUEUE_CANDIDATES.lock() else { + return; + }; + if candidates + .iter() + .any(|candidate| candidate.as_raw() == queue) + { + return; + } + if candidates.len() < MAX_D3D12_QUEUE_CANDIDATES { + candidates.push(queue_ref.clone()); + } +} + +unsafe fn create_d3d11_on_d3d12( + device12: &ID3D12Device, +) -> Option<(ID3D11Device, ID3D11DeviceContext, ID3D11On12Device)> { + let Ok(device_unknown) = device12.cast::() else { + return None; + }; + let candidates = D3D12_QUEUE_CANDIDATES.lock().ok()?.clone(); + for queue in candidates { + if queue.GetDesc().Type != D3D12_COMMAND_LIST_TYPE_DIRECT { + continue; + } + let Ok(queue_unknown) = queue.cast::() else { + continue; + }; + let queues = [Some(queue_unknown)]; + let mut device11 = None; + let mut context11 = None; + if D3D11On12CreateDevice( + &device_unknown, + 0, + None, + Some(&queues), + 0, + Some(&mut device11), + Some(&mut context11), + None, + ) + .is_err() + { + continue; + } + let Some(device11) = device11 else { + continue; + }; + let Some(context11) = context11 else { + continue; + }; + let Ok(device11on12) = device11.cast::() else { + continue; + }; + return Some((device11, context11, device11on12)); + } + None +} + +unsafe fn d3d12_backbuffer_count(desc: &DXGI_SWAP_CHAIN_DESC) -> u32 { + if desc.SwapEffect == DXGI_SWAP_EFFECT_DISCARD { + 1 + } else { + desc.BufferCount.max(1) + } +} + +unsafe fn d3d12_state_for_frame<'a>( + state: &'a mut HookState, + swap_chain: &IDXGISwapChain, + device_ptr: usize, + device12: &ID3D12Device, + desc: &DXGI_SWAP_CHAIN_DESC, + format_mode: D3d11FormatMode, + force_cpu: bool, +) -> Option<&'a mut D3d12CaptureState> { + let backbuffer_count = d3d12_backbuffer_count(desc); + let recreate = state + .d3d12 + .as_ref() + .map(|capture| { + capture.device_ptr != device_ptr + || capture.width != desc.BufferDesc.Width + || capture.height != desc.BufferDesc.Height + || capture.format != desc.BufferDesc.Format + || capture.backbuffer_count != backbuffer_count + || capture.force_cpu != force_cpu + }) + .unwrap_or(true); + if recreate { + let Some((device11, context11, device11on12)) = create_d3d11_on_d3d12(device12) else { + return None; + }; + let copy_desc = D3D11_TEXTURE2D_DESC { + Width: desc.BufferDesc.Width, + Height: desc.BufferDesc.Height, + MipLevels: 1, + ArraySize: 1, + Format: desc.BufferDesc.Format, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Usage: D3D11_USAGE_DEFAULT, + BindFlags: (D3D11_BIND_SHADER_RESOURCE.0 | D3D11_BIND_RENDER_TARGET.0) as u32, + CPUAccessFlags: 0, + MiscFlags: if force_cpu { + 0 + } else { + D3D11_RESOURCE_MISC_SHARED.0 as u32 + }, + }; + let mut copy_tex = None; + if device11 + .CreateTexture2D(©_desc, None, Some(&mut copy_tex)) + .is_err() + { + (*state.info).state = GAME_CAPTURE_STATE_ERROR; + (*state.info).last_error = 30; + return None; + } + state.d3d12 = match copy_tex { + Some(copy_tex) => { + let (staging, shared_handle) = if force_cpu { + let mut staging_desc = copy_desc; + staging_desc.Usage = D3D11_USAGE_STAGING; + staging_desc.BindFlags = 0; + staging_desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ.0 as u32; + staging_desc.MiscFlags = 0; + let mut staging = None; + if device11 + .CreateTexture2D(&staging_desc, None, Some(&mut staging)) + .is_err() + { + (*state.info).state = GAME_CAPTURE_STATE_ERROR; + (*state.info).last_error = 32; + return None; + } + (staging, 0u64) + } else { + let handle = match copy_tex + .cast::() + .and_then(|resource| resource.GetSharedHandle()) + { + Ok(handle) => handle.0 as usize as u64, + Err(_) => { + (*state.info).state = GAME_CAPTURE_STATE_ERROR; + (*state.info).last_error = 31; + return None; + } + }; + (None, handle) + }; + Some(D3d12CaptureState { + device_ptr, + device11, + context11, + device11on12, + copy_tex, + staging, + shared_handle, + width: desc.BufferDesc.Width, + height: desc.BufferDesc.Height, + format: desc.BufferDesc.Format, + format_mode, + backbuffer_count, + current_backbuffer: 0, + dxgi14: backbuffer_count > 1 && swap_chain.cast::().is_ok(), + force_cpu, + }) + } + _ => None, + }; + } + state.d3d12.as_mut() +} + +unsafe fn capture_d3d12_frame(swap_chain: *mut c_void) { + if !HOOKS_READY.load(Ordering::Acquire) { + return; + } + if env_flag_enabled(ENV_DISABLE_D3D12) { + return; + } + let should_continue = { + let Some(state) = ensure_state() else { + return; + }; + capture_should_run(state) + }; + if !should_continue { + clear_state(); + return; + } + let Some(state) = ensure_state() else { + return; + }; + + let Some(swap_chain) = IDXGISwapChain::from_raw_borrowed(&swap_chain) else { + return; + }; + let Ok(desc) = swap_chain.GetDesc() else { + return; + }; + let Ok(device12) = swap_chain.GetDevice::() else { + return; + }; + if desc.BufferDesc.Width == 0 || desc.BufferDesc.Height == 0 { + return; + } + if desc.SampleDesc.Count != 1 { + mark_present(state, GAME_CAPTURE_API_D3D12); + set_capture_flags(state, GAME_CAPTURE_FLAG_MULTISAMPLED); + set_fallback_reason(state, GAME_CAPTURE_FALLBACK_MULTISAMPLED); + record_dropped_frame(state); + return; + } + let force_cpu = env_flag_enabled(ENV_FORCE_CPU); + let (format_mode, hdr_flags) = match dxgi_capture_plan(desc.BufferDesc.Format, force_cpu) { + DxgiCapturePlan::Sdr8 { format_mode } => (format_mode, 0u32), + DxgiCapturePlan::HdrShared { flags } => (D3d11FormatMode::Bgra, flags), + DxgiCapturePlan::Unsupported => { + mark_present(state, GAME_CAPTURE_API_D3D12); + set_capture_flags(state, dxgi_hdr_flags(desc.BufferDesc.Format)); + set_fallback_reason(state, GAME_CAPTURE_FALLBACK_FORMAT_UNSUPPORTED); + record_dropped_frame(state); + return; + } + }; + let device_ptr = device12.as_raw() as usize; + + mark_present(state, GAME_CAPTURE_API_D3D12); + set_capture_flags(state, hdr_flags); + set_fallback_reason( + state, + if force_cpu { + GAME_CAPTURE_FALLBACK_FORCED_CPU + } else { + GAME_CAPTURE_FALLBACK_NONE + }, + ); + + if !frame_due(state) { + record_dropped_frame(state); + return; + } + let (context11, device11on12, copy_tex, staging, shared_handle, backbuffer_index) = { + let Some(capture_state) = d3d12_state_for_frame( + state, + swap_chain, + device_ptr, + &device12, + &desc, + format_mode, + force_cpu, + ) else { + record_dropped_frame(state); + return; + }; + let index = if capture_state.dxgi14 { + swap_chain + .cast::() + .ok() + .map(|swap_chain3| swap_chain3.GetCurrentBackBufferIndex()) + .unwrap_or(0) + } else { + capture_state.current_backbuffer + }; + if !capture_state.dxgi14 { + capture_state.current_backbuffer = + (capture_state.current_backbuffer + 1) % capture_state.backbuffer_count.max(1); + } + ( + capture_state.context11.clone(), + capture_state.device11on12.clone(), + capture_state.copy_tex.clone(), + capture_state.staging.clone(), + capture_state.shared_handle, + index, + ) + }; + + let Ok(backbuffer12) = swap_chain.GetBuffer::(backbuffer_index) else { + record_dropped_frame(state); + return; + }; + let mut wrapped: Option = None; + let resource_flags = D3D11_RESOURCE_FLAGS::default(); + if device11on12 + .CreateWrappedResource( + &backbuffer12, + &resource_flags, + D3D12_RESOURCE_STATE_PRESENT, + D3D12_RESOURCE_STATE_PRESENT, + &mut wrapped, + ) + .is_err() + { + record_dropped_frame(state); + return; + } + let Some(wrapped) = wrapped else { + record_dropped_frame(state); + return; + }; + let wrapped_resources = [Some(wrapped.clone())]; + device11on12.AcquireWrappedResources(&wrapped_resources); + context11.CopyResource(©_tex, &wrapped); + device11on12.ReleaseWrappedResources(&wrapped_resources); + + let hwnd = desc.OutputWindow.0 as HWND; + if force_cpu { + let Some(staging) = staging else { + record_dropped_frame(state); + return; + }; + context11.CopyResource(&staging, ©_tex); + context11.Flush(); + let _ = publish_d3d11_staging_cpu( + state, + &context11, + &staging, + hwnd, + desc.BufferDesc.Width, + desc.BufferDesc.Height, + format_mode, + ); + return; + } + + context11.Flush(); + let _ = publish_shared_texture_frame( + state, + hwnd, + desc.BufferDesc.Width, + desc.BufferDesc.Height, + desc.BufferDesc.Format, + shared_handle, + ); +} + +unsafe extern "system" fn swap_buffers_detour(hdc: HDC) -> BOOL { + if !OPENGL_PRESENT_FIRED.swap(true, Ordering::AcqRel) { + verbose_log("opengl SwapBuffers detour fired (first present)"); + } + capture_opengl_frame(hdc); + if let Some(hook) = SWAP_BUFFERS_HOOK.as_ref() { + return (hook.trampoline_fn())(hdc); + } + 0 +} + +unsafe extern "system" fn wgl_swap_layer_buffers_detour(hdc: HDC, flags: u32) -> BOOL { + capture_opengl_frame(hdc); + if let Some(hook) = WGL_SWAP_LAYER_BUFFERS_HOOK.as_ref() { + return (hook.trampoline_fn())(hdc, flags); + } + 0 +} + +unsafe extern "system" fn dxgi_present_detour( + swap_chain: *mut c_void, + sync_interval: u32, + flags: u32, +) -> HRESULT { + let suppress_capture = gl_interop::dummy_present_active(); + if !suppress_capture { + if !DXGI_PRESENT_FIRED.swap(true, Ordering::AcqRel) { + verbose_log("dxgi Present detour fired (first present)"); + } + capture_d3d11_frame(swap_chain); + capture_d3d12_frame(swap_chain); + } + if let Some(hook) = DXGI_PRESENT_HOOK.as_ref() { + if !suppress_capture { + DXGI_PRESENT_DEPTH.fetch_add(1, Ordering::AcqRel); + } + let result = (hook.trampoline_fn())(swap_chain, sync_interval, flags); + if !suppress_capture { + DXGI_PRESENT_DEPTH.fetch_sub(1, Ordering::AcqRel); + DXGI_PRESENT_ATTEMPTED.store(true, Ordering::Release); + } + return result; + } + HRESULT(0) +} + +unsafe extern "system" fn dxgi_present1_detour( + swap_chain: *mut c_void, + sync_interval: u32, + flags: u32, + present_parameters: *const DXGI_PRESENT_PARAMETERS, +) -> HRESULT { + let suppress_capture = gl_interop::dummy_present_active(); + if !suppress_capture { + if !DXGI_PRESENT1_FIRED.swap(true, Ordering::AcqRel) { + verbose_log("dxgi Present1 detour fired (first present)"); + } + capture_d3d11_frame(swap_chain); + capture_d3d12_frame(swap_chain); + } + if let Some(hook) = DXGI_PRESENT1_HOOK.as_ref() { + if !suppress_capture { + DXGI_PRESENT_DEPTH.fetch_add(1, Ordering::AcqRel); + } + let result = (hook.trampoline_fn())(swap_chain, sync_interval, flags, present_parameters); + if !suppress_capture { + DXGI_PRESENT_DEPTH.fetch_sub(1, Ordering::AcqRel); + DXGI_PRESENT_ATTEMPTED.store(true, Ordering::Release); + } + return result; + } + HRESULT(0) +} + +unsafe extern "system" fn d3d12_execute_command_lists_detour( + queue: *mut c_void, + num_command_lists: u32, + command_lists: *const *mut c_void, +) { + if !D3D12_EXECUTE_FIRED.swap(true, Ordering::AcqRel) { + verbose_log("d3d12 ExecuteCommandLists detour fired (first call)"); + } + if DXGI_PRESENT_DEPTH.load(Ordering::Acquire) > 0 + || DXGI_PRESENT_ATTEMPTED.load(Ordering::Acquire) + { + remember_d3d12_queue_candidate(queue); + } + if let Some(hook) = D3D12_EXECUTE_COMMAND_LISTS_HOOK.as_ref() { + (hook.trampoline_fn())(queue, num_command_lists, command_lists); + } +} + +unsafe extern "system" fn dummy_window_proc( + hwnd: HWND, + msg: u32, + wparam: usize, + lparam: isize, +) -> isize { + DefWindowProcW(hwnd, msg, wparam, lparam) +} + +unsafe fn module_loaded(module: &str) -> bool { + let name = wide(module); + !GetModuleHandleW(name.as_ptr()).is_null() +} + +unsafe fn log_loaded_graphics_modules(tag: &str) { + if !env_flag_enabled(ENV_VERBOSE) { + return; + } + verbose_log(&format!( + "{tag} loaded modules: d3d11={} dxgi={} d3d10={} d3d12={} opengl32={} d3d9={}", + module_loaded("d3d11.dll"), + module_loaded("dxgi.dll"), + module_loaded("d3d10.dll"), + module_loaded("d3d12.dll"), + module_loaded("opengl32.dll"), + module_loaded("d3d9.dll"), + )); +} + +unsafe fn create_dummy_window() -> HWND { + let class_name = wide("FluxerGameCaptureDummyWindow"); + let instance = GetModuleHandleW(null()); + let window_class = WNDCLASSW { + style: CS_HREDRAW | CS_VREDRAW, + lpfnWndProc: Some(dummy_window_proc), + hInstance: instance, + lpszClassName: class_name.as_ptr(), + ..Default::default() + }; + let _ = RegisterClassW(&window_class); + CreateWindowExW( + 0, + class_name.as_ptr(), + class_name.as_ptr(), + WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, + CW_USEDEFAULT, + 2, + 2, + null_mut(), + null_mut(), + instance, + null(), + ) +} + +unsafe fn swap_chain_method_pointers() -> Option<(*const (), Option<*const ()>)> { + let hwnd = create_dummy_window(); + if hwnd.is_null() { + verbose_log("d3d11 install: create_dummy_window FAILED"); + return None; + } + let desc = DXGI_SWAP_CHAIN_DESC { + BufferDesc: DXGI_MODE_DESC { + Width: 2, + Height: 2, + RefreshRate: DXGI_RATIONAL { + Numerator: 60, + Denominator: 1, + }, + Format: DXGI_FORMAT_B8G8R8A8_UNORM, + ScanlineOrdering: DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED, + Scaling: DXGI_MODE_SCALING_UNSPECIFIED, + }, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT, + BufferCount: 1, + OutputWindow: WinHwnd(hwnd), + Windowed: WinBool(1), + SwapEffect: DXGI_SWAP_EFFECT_DISCARD, + Flags: 0, + }; + let mut swap_chain = None; + let result = D3D11CreateDeviceAndSwapChain( + None, + D3D_DRIVER_TYPE_HARDWARE, + WinHmodule(null_mut()), + D3D11_CREATE_DEVICE_FLAG(0), + None, + D3D11_SDK_VERSION, + Some(&desc), + Some(&mut swap_chain), + None, + None, + None, + ); + let _ = DestroyWindow(hwnd); + if let Err(error) = result { + verbose_log(&format!( + "d3d11 install: D3D11CreateDeviceAndSwapChain dummy probe FAILED hr={:#010x}", + error.code().0 as u32 + )); + return None; + } + let Some(swap_chain) = swap_chain else { + verbose_log("d3d11 install: dummy swapchain was null despite Ok HRESULT"); + return None; + }; + let raw = swap_chain.as_raw(); + let vtable = *(raw as *mut *const *const ()); + let present = *vtable.add(8); + let present1 = swap_chain + .cast::() + .ok() + .map(|swap_chain1| { + let raw = swap_chain1.as_raw(); + let vtable = *(raw as *mut *const *const ()); + *vtable.add(22) + }); + verbose_log(&format!( + "d3d11 install: dummy swapchain created, Present vtable[8]={present:p} present1_present={}", + present1.is_some() + )); + Some((present, present1)) +} + +unsafe fn d3d12_execute_command_lists_pointer() -> Option<*const ()> { + let module_name = wide("d3d12.dll"); + let d3d12 = GetModuleHandleW(module_name.as_ptr()); + if d3d12.is_null() { + return None; + } + let create = GetProcAddress(d3d12, c"D3D12CreateDevice".as_ptr().cast())?; + let create: D3d12CreateDeviceFn = mem::transmute(create); + let mut device = null_mut(); + if create( + null_mut(), + D3D_FEATURE_LEVEL_11_0, + &ID3D12Device::IID, + &mut device, + ) + .is_err() + { + return None; + } + let device = ID3D12Device::from_raw(device); + let queue_desc = D3D12_COMMAND_QUEUE_DESC { + Type: D3D12_COMMAND_LIST_TYPE_DIRECT, + ..Default::default() + }; + let queue: ID3D12CommandQueue = device.CreateCommandQueue(&queue_desc).ok()?; + let raw = queue.as_raw(); + let vtable = *(raw as *mut *const *const ()); + Some(*vtable.add(10)) +} + +unsafe fn d3d11_hooks_installed() -> bool { + DXGI_PRESENT_HOOK.is_some() +} + +unsafe fn install_d3d11_hooks() { + if DXGI_PRESENT_HOOK.is_some() { + return; + } + let Some((present, present1)) = swap_chain_method_pointers() else { + return; + }; + if DXGI_PRESENT_HOOK.is_none() { + let target: DxgiPresentFn = mem::transmute(present); + let detour: DxgiPresentFn = dxgi_present_detour; + match Detour::::new(target, detour) { + Ok(hook) => match hook.enable() { + Ok(()) => { + DXGI_PRESENT_HOOK = Some(hook); + verbose_log("d3d11 install: IDXGISwapChain::Present detour ENABLED"); + } + Err(()) => verbose_log("d3d11 install: Present Detour.enable() FAILED"), + }, + Err(()) => verbose_log("d3d11 install: Present Detour::new() FAILED"), + } + } + if DXGI_PRESENT1_HOOK.is_none() { + if let Some(present1) = present1 { + let target: DxgiPresent1Fn = mem::transmute(present1); + let detour: DxgiPresent1Fn = dxgi_present1_detour; + match Detour::::new(target, detour) { + Ok(hook) => match hook.enable() { + Ok(()) => { + DXGI_PRESENT1_HOOK = Some(hook); + verbose_log("d3d11 install: IDXGISwapChain1::Present1 detour ENABLED"); + } + Err(()) => verbose_log("d3d11 install: Present1 Detour.enable() FAILED"), + }, + Err(()) => verbose_log("d3d11 install: Present1 Detour::new() FAILED"), + } + } + } +} + +unsafe fn install_d3d12_hooks() { + if env_flag_enabled(ENV_DISABLE_D3D12) { + return; + } + let Some(execute_command_lists) = d3d12_execute_command_lists_pointer() else { + return; + }; + if D3D12_EXECUTE_COMMAND_LISTS_HOOK.is_none() { + let target: D3d12ExecuteCommandListsFn = mem::transmute(execute_command_lists); + let detour: D3d12ExecuteCommandListsFn = d3d12_execute_command_lists_detour; + match Detour::::new(target, detour) { + Ok(hook) => match hook.enable() { + Ok(()) => { + D3D12_EXECUTE_COMMAND_LISTS_HOOK = Some(hook); + verbose_log("d3d12 install: ExecuteCommandLists detour ENABLED"); + } + Err(()) => verbose_log("d3d12 install: ExecuteCommandLists Detour.enable() FAILED"), + }, + Err(()) => verbose_log("d3d12 install: ExecuteCommandLists Detour::new() FAILED"), + } + } +} + +unsafe fn gl_hooks_installed() -> bool { + SWAP_BUFFERS_HOOK.is_some() && WGL_SWAP_LAYER_BUFFERS_HOOK.is_some() +} + +unsafe fn exported_swap_buffers() -> Option { + let module_name = wide("gdi32.dll"); + let module = GetModuleHandleW(module_name.as_ptr()); + if module.is_null() { + verbose_log("opengl install: gdi32.dll not loaded; cannot resolve SwapBuffers"); + return None; + } + let proc = GetProcAddress(module, c"SwapBuffers".as_ptr().cast())?; + Some(mem::transmute(proc)) +} + +unsafe fn exported_wgl_swap_layer_buffers() -> Option { + let module_name = wide("opengl32.dll"); + let module = GetModuleHandleW(module_name.as_ptr()); + if module.is_null() { + verbose_log("opengl install: opengl32.dll not loaded; cannot resolve wglSwapLayerBuffers"); + return None; + } + let proc = GetProcAddress(module, c"wglSwapLayerBuffers".as_ptr().cast())?; + Some(mem::transmute(proc)) +} + +unsafe fn prologue_hex(function: *const ()) -> String { + let ptr = function as *const u8; + if ptr.is_null() { + return "".to_string(); + } + let bytes = std::slice::from_raw_parts(ptr, arm64_reloc::STOLEN_BYTES); + bytes + .iter() + .map(|byte| format!("{byte:02X}")) + .collect::>() + .join(" ") +} + +unsafe fn install_opengl_hooks() { + if SWAP_BUFFERS_HOOK.is_none() { + if let Some(target) = exported_swap_buffers() { + verbose_log(&format!( + "opengl install: SwapBuffers target prologue {}", + prologue_hex(target as *const ()) + )); + let detour: SwapBuffersFn = swap_buffers_detour; + match Detour::::new(target, detour) { + Ok(hook) => match hook.enable() { + Ok(()) => { + SWAP_BUFFERS_HOOK = Some(hook); + verbose_log("opengl install: SwapBuffers detour ENABLED"); + } + Err(()) => verbose_log("opengl install: SwapBuffers Detour.enable() FAILED"), + }, + Err(()) => verbose_log("opengl install: SwapBuffers Detour::new() FAILED"), + } + } + } + if WGL_SWAP_LAYER_BUFFERS_HOOK.is_none() { + if let Some(target) = exported_wgl_swap_layer_buffers() { + let detour: WglSwapLayerBuffersFn = wgl_swap_layer_buffers_detour; + match Detour::::new(target, detour) { + Ok(hook) => match hook.enable() { + Ok(()) => { + WGL_SWAP_LAYER_BUFFERS_HOOK = Some(hook); + verbose_log("opengl install: wglSwapLayerBuffers detour ENABLED"); + } + Err(()) => { + verbose_log("opengl install: wglSwapLayerBuffers Detour.enable() FAILED") + } + }, + Err(()) => verbose_log("opengl install: wglSwapLayerBuffers Detour::new() FAILED"), + } + } + } +} + +unsafe fn install_hooks() { + install_opengl_hooks(); + install_d3d11_hooks(); + install_d3d12_hooks(); + d3d9::install_d3d9_hooks(); + HOOKS_READY.store(true, Ordering::Release); +} + +unsafe extern "system" fn hook_thread(_param: *mut c_void) -> u32 { + verbose_log("hook_thread started -- installing graphics detours"); + log_loaded_graphics_modules("hook_thread initial"); + install_hooks(); + let want_d3d12 = !env_flag_enabled(ENV_DISABLE_D3D12); + for tick in 0..50 { + let d3d11_ok = d3d11_hooks_installed(); + let gl_ok = gl_hooks_installed() || module_loaded("opengl32.dll"); + let d3d12_ok = !want_d3d12 || D3D12_EXECUTE_COMMAND_LISTS_HOOK.is_some(); + if d3d11_ok && d3d12_ok && gl_ok && d3d9::hooks_installed() { + verbose_log(&format!( + "hook_thread: all targeted detours installed after {tick} retries" + )); + break; + } + Sleep(200); + log_loaded_graphics_modules("hook_thread retry"); + install_opengl_hooks(); + install_d3d11_hooks(); + if want_d3d12 { + install_d3d12_hooks(); + } + d3d9::install_d3d9_hooks(); + } + verbose_log(&format!( + "hook_thread: install poll finished (dxgi_present={} dxgi_present1={} d3d12={} opengl={} d3d9={})", + DXGI_PRESENT_HOOK.is_some(), + DXGI_PRESENT1_HOOK.is_some(), + D3D12_EXECUTE_COMMAND_LISTS_HOOK.is_some(), + gl_hooks_installed(), + d3d9::hooks_installed(), + )); + 0 +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn FluxerGetMsgProc( + code: i32, + wparam: WPARAM, + lparam: WinLparam, +) -> LRESULT { + CallNextHookEx(null_mut(), code, wparam, lparam) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn DllMain( + hinst: HINSTANCE, + reason: u32, + _reserved: *mut c_void, +) -> BOOL { + if reason == DLL_PROCESS_ATTACH { + verbose_log(&format!( + "DllMain DLL_PROCESS_ATTACH reached (pointer_width={} bits)", + mem::size_of::() * 8 + )); + DisableThreadLibraryCalls(hinst); + let thread = CreateThread(null(), 0, Some(hook_thread), null(), 0, null_mut()); + if thread.is_null() { + verbose_log("DllMain CreateThread(hook_thread) FAILED -- no hooks will install"); + } else { + CloseHandle(thread); + } + } + 1 +} diff --git a/fluxer_desktop/native/win-game-capture/hook/tests/arm64_reloc.rs b/fluxer_desktop/native/win-game-capture/hook/tests/arm64_reloc.rs new file mode 100644 index 0000000..de69ae4 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/hook/tests/arm64_reloc.rs @@ -0,0 +1,412 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[path = "../src/arm64_reloc.rs"] +mod arm64_reloc; + +use arm64_reloc::*; + +fn adr(rd: u32, imm21: i32) -> u32 { + let raw = (imm21 as u32) & 0x1F_FFFF; + let immlo = (raw & 0x3) << 29; + let immhi = ((raw >> 2) & 0x7FFFF) << 5; + 0x1000_0000 | immlo | immhi | (rd & 0x1F) +} +fn adrp(rd: u32, imm21: i32) -> u32 { + let raw = (imm21 as u32) & 0x1F_FFFF; + let immlo = (raw & 0x3) << 29; + let immhi = ((raw >> 2) & 0x7FFFF) << 5; + 0x9000_0000 | immlo | immhi | (rd & 0x1F) +} +fn b(off_words: i32) -> u32 { + 0x1400_0000 | ((off_words as u32) & 0x03FF_FFFF) +} +fn bl(off_words: i32) -> u32 { + 0x9400_0000 | ((off_words as u32) & 0x03FF_FFFF) +} +fn bcond(cond: u32, off_words: i32) -> u32 { + 0x5400_0000 | (((off_words as u32) & 0x7FFFF) << 5) | (cond & 0xF) +} +fn cbz(rt: u32, off_words: i32) -> u32 { + 0xB400_0000 | (((off_words as u32) & 0x7FFFF) << 5) | (rt & 0x1F) +} +fn tbz(rt: u32, bit: u32, off_words: i32) -> u32 { + let b5 = (bit & 0x20) << (31 - 5); + let b40 = (bit & 0x1F) << 19; + 0x3600_0000 | b5 | b40 | (((off_words as u32) & 0x3FFF) << 5) | (rt & 0x1F) +} +fn ldr_lit(rt: u32, off_words: i32) -> u32 { + 0x5800_0000 | (((off_words as u32) & 0x7FFFF) << 5) | (rt & 0x1F) +} + +fn adr_target(insn: u32, pc: u64, page: bool) -> u64 { + let immlo = ((insn >> 29) & 0x3) as i64; + let immhi = ((insn >> 5) & 0x7FFFF) as i64; + let raw = (immhi << 2) | immlo; + let imm21 = (raw << 43) >> 43; + if page { + ((pc & !0xFFF) as i64 + imm21 * 4096) as u64 + } else { + (pc as i64 + imm21) as u64 + } +} +fn imm19_target(insn: u32, pc: u64) -> u64 { + let imm19 = ((insn >> 5) & 0x7FFFF) as i64; + let off = ((imm19 << 45) >> 45) * 4; + (pc as i64 + off) as u64 +} +fn imm14_target(insn: u32, pc: u64) -> u64 { + let imm14 = ((insn >> 5) & 0x3FFF) as i64; + let off = ((imm14 << 50) >> 50) * 4; + (pc as i64 + off) as u64 +} + +#[test] +fn non_pc_relative_copied_verbatim() { + let stp = 0xA9BF_7BFD; + assert_eq!(relocate_instruction(stp, 0x1000, 0x9000), Some(stp)); + let mov = 0xAA01_03E0; + assert_eq!(relocate_instruction(mov, 0x1000, 0x9000), Some(mov)); + let sub = 0xD100_83FF; + assert_eq!(relocate_instruction(sub, 0x1000, 0x9000), Some(sub)); + let mov_fp_sp = 0x9100_03FD; + assert_eq!( + relocate_instruction(mov_fp_sp, 0x1000, 0x9000), + Some(mov_fp_sp) + ); +} + +#[test] +fn adr_relocates_to_same_target() { + let src_pc = 0x140_0010_0000u64; + let dst_pc = 0x140_0010_8000u64; + let insn = adr(0, 0x4000); + let original = adr_target(insn, src_pc, false); + let reloc = relocate_instruction(insn, src_pc, dst_pc).expect("in range"); + assert_eq!(adr_target(reloc, dst_pc, false), original); + assert_eq!(reloc & 0x1F, 0); +} + +#[test] +fn adr_negative_offset() { + let src_pc = 0x140_0010_0000u64; + let dst_pc = 0x140_0010_0010u64; + let insn = adr(5, -0x100); + let original = adr_target(insn, src_pc, false); + let reloc = relocate_instruction(insn, src_pc, dst_pc).expect("in range"); + assert_eq!(adr_target(reloc, dst_pc, false), original); + assert_eq!(reloc & 0x1F, 5); +} + +#[test] +fn adr_out_of_range_refused() { + let src_pc = 0x0000_0000_0000u64; + let dst_pc = 0x0000_0080_0000u64; + let insn = adr(0, 0x1000); + assert_eq!(relocate_instruction(insn, src_pc, dst_pc), None); +} + +#[test] +fn adrp_relocates_to_same_page() { + let src_pc = 0x140_0010_0000u64; + let dst_pc = 0x140_0030_0000u64; + let insn = adrp(9, 0x10); + let original = adr_target(insn, src_pc, true); + let reloc = relocate_instruction(insn, src_pc, dst_pc).expect("in range"); + assert_eq!(adr_target(reloc, dst_pc, true), original); + assert_eq!(reloc & 0x1F, 9); + assert_eq!(reloc & 0x9F00_0000, 0x9000_0000); +} + +#[test] +fn adrp_negative() { + let src_pc = 0x140_0090_0000u64; + let dst_pc = 0x140_0050_0000u64; + let insn = adrp(1, -0x20); + let original = adr_target(insn, src_pc, true); + let reloc = relocate_instruction(insn, src_pc, dst_pc).expect("in range"); + assert_eq!(adr_target(reloc, dst_pc, true), original); +} + +#[test] +fn bcond_relocates() { + let src_pc = 0x10_0000u64; + let dst_pc = 0x12_0000u64; + let insn = bcond(0x0, 0x40); + let original = imm19_target(insn, src_pc); + let reloc = relocate_instruction(insn, src_pc, dst_pc).expect("in range"); + assert_eq!(imm19_target(reloc, dst_pc), original); + assert_eq!(reloc & 0xF, 0x0); +} + +#[test] +fn bcond_out_of_range_refused() { + let src_pc = 0x0u64; + let dst_pc = 0x20_0000u64; + let insn = bcond(0x1, 0x10); + assert_eq!(relocate_instruction(insn, src_pc, dst_pc), None); +} + +#[test] +fn cbz_relocates() { + let src_pc = 0x10_0000u64; + let dst_pc = 0x10_8000u64; + let insn = cbz(3, -0x20); + let original = imm19_target(insn, src_pc); + let reloc = relocate_instruction(insn, src_pc, dst_pc).expect("in range"); + assert_eq!(imm19_target(reloc, dst_pc), original); + assert_eq!(reloc & 0x1F, 3); + assert_eq!(reloc & 0x8000_0000, 0x8000_0000); +} + +#[test] +fn tbz_relocates() { + let src_pc = 0x10_0000u64; + let dst_pc = 0x10_1000u64; + let insn = tbz(7, 5, 0x10); + let original = imm14_target(insn, src_pc); + let reloc = relocate_instruction(insn, src_pc, dst_pc).expect("in range"); + assert_eq!(imm14_target(reloc, dst_pc), original); + assert_eq!(reloc & 0x1F, 7); +} + +#[test] +fn tbz_out_of_range_refused() { + let src_pc = 0x0u64; + let dst_pc = 0x1_0000u64; + let insn = tbz(0, 1, 0x8); + assert_eq!(relocate_instruction(insn, src_pc, dst_pc), None); +} + +#[test] +fn ldr_literal_relocates() { + let src_pc = 0x20_0000u64; + let dst_pc = 0x20_4000u64; + let insn = ldr_lit(2, 0x100); + let original = imm19_target(insn, src_pc); + let reloc = relocate_instruction(insn, src_pc, dst_pc).expect("in range"); + assert_eq!(imm19_target(reloc, dst_pc), original); + assert_eq!(reloc & 0x1F, 2); +} + +#[test] +fn direct_b_relocation_in_range() { + let src_pc = 0x10_0000u64; + let dst_pc = 0x14_0000u64; + let insn = b(0x100); + let target = branch_target(insn, src_pc).unwrap(); + let reloc = relocate_instruction(insn, src_pc, dst_pc).expect("in range"); + assert_eq!(branch_target(reloc, dst_pc).unwrap(), target); +} + +#[test] +fn branch_target_decode() { + let pc = 0x10_0000u64; + assert_eq!(branch_target(b(4), pc), Some(pc + 16)); + assert_eq!(branch_target(b(-4), pc), Some(pc - 16)); + assert_eq!(branch_target(bl(1), pc), Some(pc + 4)); +} + +#[test] +fn abs_branch_encoding() { + let mut bytes = Vec::new(); + append_abs_branch(&mut bytes, 0x1234_5678_9ABC_DEF0, false); + assert_eq!(bytes.len(), 16); + assert_eq!( + u32::from_le_bytes(bytes[0..4].try_into().unwrap()), + LDR_X16_PC8 + ); + assert_eq!(u32::from_le_bytes(bytes[4..8].try_into().unwrap()), BR_X16); + assert_eq!( + u64::from_le_bytes(bytes[8..16].try_into().unwrap()), + 0x1234_5678_9ABC_DEF0 + ); + let mut linked = Vec::new(); + append_abs_branch(&mut linked, 0xDEAD_BEEF, true); + assert_eq!( + u32::from_le_bytes(linked[4..8].try_into().unwrap()), + BLR_X16 + ); +} + +#[test] +fn classify_branches() { + assert!(is_b(b(1))); + assert!(!is_bl(b(1))); + assert!(is_bl(bl(1))); + assert!(!is_b(bl(1))); + assert!(needs_absolute_island(b(1))); + assert!(needs_absolute_island(bl(1))); + assert!(!needs_absolute_island(adr(0, 1))); + assert!(!needs_absolute_island(NOP)); +} + +#[test] +fn assemble_trampoline_relocates_prologue() { + let stp = 0xA9BF_7BFDu32; + let mov = 0x9100_03FDu32; + let adrp_insn = adrp(8, 0x20); + let mut prologue = Vec::new(); + for insn in [stp, mov, adrp_insn, NOP] { + prologue.extend_from_slice(&insn.to_le_bytes()); + } + let src_base = 0x140_0010_0000u64; + let dst_base = 0x140_0030_0000u64; + let resume = src_base + STOLEN_BYTES as u64; + let body = assemble_trampoline(&prologue, src_base, dst_base, resume).expect("relocatable"); + assert_eq!(body.len(), 32); + assert_eq!(u32::from_le_bytes(body[0..4].try_into().unwrap()), stp); + assert_eq!(u32::from_le_bytes(body[4..8].try_into().unwrap()), mov); + assert_eq!(u32::from_le_bytes(body[12..16].try_into().unwrap()), NOP); + let orig_target = adr_target(adrp_insn, src_base + 8, true); + let reloc_adrp = u32::from_le_bytes(body[8..12].try_into().unwrap()); + assert_eq!(adr_target(reloc_adrp, dst_base + 8, true), orig_target); + assert_eq!( + u32::from_le_bytes(body[16..20].try_into().unwrap()), + LDR_X16_PC8 + ); + assert_eq!(u32::from_le_bytes(body[20..24].try_into().unwrap()), BR_X16); + assert_eq!(u64::from_le_bytes(body[24..32].try_into().unwrap()), resume); +} + +#[test] +fn assemble_trampoline_promotes_leading_branch() { + let lead_b = b(0x4000); + let mut prologue = Vec::new(); + for insn in [lead_b, NOP, NOP, NOP] { + prologue.extend_from_slice(&insn.to_le_bytes()); + } + let src_base = 0x140_0010_0000u64; + let dst_base = 0x0000_7000_0000u64; + let resume = src_base + STOLEN_BYTES as u64; + let body = assemble_trampoline(&prologue, src_base, dst_base, resume).expect("island path"); + assert_eq!(body.len(), 48); + let first = u32::from_le_bytes(body[0..4].try_into().unwrap()); + assert!(is_b(first)); + let island_addr = branch_target(first, dst_base).unwrap(); + assert_eq!(island_addr, dst_base + 16 + 16); + let original_b_target = branch_target(lead_b, src_base).unwrap(); + assert_eq!( + u32::from_le_bytes(body[32..36].try_into().unwrap()), + LDR_X16_PC8 + ); + assert_eq!(u32::from_le_bytes(body[36..40].try_into().unwrap()), BR_X16); + assert_eq!( + u64::from_le_bytes(body[40..48].try_into().unwrap()), + original_b_target + ); +} + +#[test] +fn assemble_trampoline_promotes_leading_bl_with_blr() { + let lead_bl = bl(0x100); + let mut prologue = Vec::new(); + for insn in [lead_bl, NOP, NOP, NOP] { + prologue.extend_from_slice(&insn.to_le_bytes()); + } + let src_base = 0x140_0010_0000u64; + let dst_base = 0x0000_7000_0000u64; + let resume = src_base + STOLEN_BYTES as u64; + let body = assemble_trampoline(&prologue, src_base, dst_base, resume).expect("island path"); + assert_eq!(body.len(), 48); + let first = u32::from_le_bytes(body[0..4].try_into().unwrap()); + assert!(is_bl(first)); + let original = branch_target(lead_bl, src_base).unwrap(); + assert_eq!( + u32::from_le_bytes(body[36..40].try_into().unwrap()), + BLR_X16 + ); + assert_eq!( + u64::from_le_bytes(body[40..48].try_into().unwrap()), + original + ); +} + +#[test] +fn assemble_trampoline_two_islands() { + let b0 = b(0x10); + let bl1 = bl(0x20); + let mut prologue = Vec::new(); + for insn in [b0, NOP, bl1, NOP] { + prologue.extend_from_slice(&insn.to_le_bytes()); + } + let src_base = 0x140_0010_0000u64; + let dst_base = 0x0000_7000_0000u64; + let resume = src_base + STOLEN_BYTES as u64; + let body = assemble_trampoline(&prologue, src_base, dst_base, resume).expect("islands"); + assert_eq!(body.len(), 64); + let first = u32::from_le_bytes(body[0..4].try_into().unwrap()); + let third = u32::from_le_bytes(body[8..12].try_into().unwrap()); + let island0 = branch_target(first, dst_base).unwrap(); + let island1 = branch_target(third, dst_base + 8).unwrap(); + assert_eq!(island0, dst_base + 32); + assert_eq!(island1, dst_base + 48); + assert_eq!( + u64::from_le_bytes(body[40..48].try_into().unwrap()), + branch_target(b0, src_base).unwrap() + ); + assert_eq!( + u64::from_le_bytes(body[56..64].try_into().unwrap()), + branch_target(bl1, src_base + 8).unwrap() + ); +} + +#[test] +fn assemble_trampoline_refuses_unrelocatable_narrow_branch() { + let cond = bcond(0x2, 0x10); + let mut prologue = Vec::new(); + for insn in [NOP, cond, NOP, NOP] { + prologue.extend_from_slice(&insn.to_le_bytes()); + } + let src_base = 0x0u64; + let dst_base = 0x0000_0080_0000u64; + let resume = src_base + STOLEN_BYTES as u64; + assert_eq!( + assemble_trampoline(&prologue, src_base, dst_base, resume), + None + ); +} + +#[test] +fn relocated_prologue_matches_assemble_prefix() { + let stp = 0xA9BF_7BFDu32; + let mut prologue = Vec::new(); + for insn in [stp, NOP, NOP, NOP] { + prologue.extend_from_slice(&insn.to_le_bytes()); + } + let src_base = 0x140_0010_0000u64; + let dst_base = 0x140_0030_0000u64; + let pro = relocated_prologue(&prologue, src_base, dst_base).expect("ok"); + assert_eq!(pro.len(), 16); + assert_eq!(u32::from_le_bytes(pro[0..4].try_into().unwrap()), stp); +} + +#[test] +fn island_holds_absolute_branch_to_original_target() { + let lead_bl = bl(0x100); + let src_base = 0x140_0010_0000u64; + let original_target = branch_target(lead_bl, src_base).unwrap(); + let island = island_for_branch(lead_bl, src_base).expect("island"); + assert_eq!(island.len(), 16); + assert_eq!( + u32::from_le_bytes(island[4..8].try_into().unwrap()), + BLR_X16 + ); + assert_eq!( + u64::from_le_bytes(island[8..16].try_into().unwrap()), + original_target + ); +} + +#[test] +fn stolen_bytes_is_four_instructions() { + assert_eq!(STOLEN_BYTES, 16); + assert_eq!(STOLEN_BYTES % 4, 0); +} + +#[test] +fn encode_imm26_rejects_unaligned_and_overflow() { + assert_eq!(encode_imm26(3), None); + assert_eq!(encode_imm26(4), Some(1)); + assert_eq!(encode_imm26(-4), Some((-1i32 as u32) & 0x03FF_FFFF)); + assert_eq!(encode_imm26(1 << 27), None); +} diff --git a/fluxer_desktop/native/win-game-capture/index.d.ts b/fluxer_desktop/native/win-game-capture/index.d.ts new file mode 100644 index 0000000..262531f --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/index.d.ts @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import {EventEmitter} from 'node:events'; + +export type GameCaptureInjectionMethod = 'auto' | 'remote-thread' | 'set-windows-hook'; + +export type CaptureStrategyName = 'game-hook' | 'wgc' | 'dxgi-duplication' | 'window-gdi'; + +export interface ScreenCaptureRect { + x: number; + y: number; + width: number; + height: number; +} + +export interface ScreenCaptureOptions { + sourceId: string; + sourceKind: 'screen' | 'window' | 'game'; + width?: number; + height?: number; + frameRate?: number; + hookDllPath?: string; + hookDllPathX86?: string; + injectionMethod?: GameCaptureInjectionMethod; + captureId?: string; + colorRange?: 'full' | 'limited'; + colorSpace?: 'rec709' | 'srgb'; + showCursorClicks?: boolean; + captureRect?: ScreenCaptureRect; + frameSinkHandle?: unknown; + nativeFrameSinkRequired?: boolean; +} + +export interface ScreenCaptureStartResult { + width: number; + height: number; + frameRate: number; + pixelFormat: 'nv12' | 'bgra'; +} + +export interface ScreenCaptureSourceDescriptor { + kind: 'screen' | 'window' | 'game'; + id: string; + name: string; + width: number; + height: number; + targetPid?: number; +} + +export interface AvailabilityInfo { + available: boolean; + backend: string; + reason?: string; +} + +export interface CaptureDiagnostics { + state: number; + apiType: number; + transport: number; + fallbackReason: number; + captureFlags: number; + width: number; + height: number; + dxgiFormat: number; + frameCounter: number; + droppedFrameCounter: number; + lastPresentTimestampUs: number; + lastError: number; + requestedInjectionMethod: GameCaptureInjectionMethod; + injectionMethod: 'remote-thread' | 'set-windows-hook'; + activeStrategy: CaptureStrategyName; + lastFallbackReason: string; + startOptions: ScreenCaptureStartOptionsDiagnostics; + frameSinkAccepted: number; + frameSinkCoalesced: number; + frameSinkRejected: number; + mediaFramesDroppedWithoutSink: number; + cpuFallbackFramesDropped: number; +} + +export interface ScreenCaptureStartOptionsDiagnostics { + colorRange?: 'full' | 'limited'; + colorSpace?: 'rec709' | 'srgb'; + showCursorClicks?: boolean; + captureRect?: ScreenCaptureRect; + unsupportedOptions: Array<'showCursorClicks' | 'captureRect' | 'colorRange' | 'colorSpace'>; +} + +export interface SharedTextureHandleInfo { + handle: bigint; + width: number; + height: number; + dxgiFormat: number; + timestampUs: number; +} + +export interface EncoderAttachDiagnostics { + attached: boolean; + width: number; + height: number; + capacity: number; + framesSubmitted: number; + framesDropped: number; + ringFullEvents: number; + failedBlits: number; +} + +export interface FrameSinkDiagnostics { + accepted: number; + coalesced: number; + rejected: number; + mediaFramesDroppedWithoutSink: number; + cpuFallbackFramesDropped: number; +} + +export interface VulkanLayerRegistrationState { + registered: boolean; + manifestExists: boolean; + dllExists: boolean; + manifestPath: string | null; +} + +export declare interface ScreenCapture { + on(event: 'error', listener: (err: Error) => void): this; + on(event: 'closed', listener: () => void): this; + on(event: 'stalled', listener: (message?: string) => void): this; + on(event: 'diagnostic', listener: (message?: string) => void): this; + on(event: string | symbol, listener: (...args: Array) => void): this; + + off(event: 'error', listener: (err: Error) => void): this; + off(event: 'closed', listener: () => void): this; + off(event: 'stalled', listener: (message?: string) => void): this; + off(event: 'diagnostic', listener: (message?: string) => void): this; + off(event: string | symbol, listener: (...args: Array) => void): this; + + emit(event: 'error', err: Error): boolean; + emit(event: 'closed'): boolean; + emit(event: 'stalled', message?: string): boolean; + emit(event: 'diagnostic', message?: string): boolean; +} + +export declare class ScreenCapture extends EventEmitter { + constructor(options?: ScreenCaptureOptions); + start(): Promise; + stop(): Promise; + getDiagnostics(): CaptureDiagnostics | null; + getSharedTextureHandle(): SharedTextureHandleInfo | null; + attachEncoder(width: number, height: number, frameRate?: number): void; + detachEncoder(): void; + isEncoderAttached(): boolean; + encoderRingFullCount(): number; + getEncoderAttachDiagnostics(): EncoderAttachDiagnostics | null; + getFrameSinkDiagnostics(): FrameSinkDiagnostics; +} + +export declare function isSupported(): boolean; +export declare function getAvailability(): AvailabilityInfo; +export declare function listSources(): Promise>; +export declare function resolveGameHookPath(): string | null; +export declare function resolveGameHookPathX86(): string | null; +export declare function resolveVulkanLayerManifestPath(): string | null; +export declare function registerVulkanLayerManifest(): boolean; +export declare function unregisterVulkanLayerManifest(): boolean; +export declare function getVulkanLayerRegistrationState(): VulkanLayerRegistrationState; +export declare function parseFallbackRecommendation(message: string | undefined): CaptureStrategyName | 'none' | null; +export declare function elevateGpuSchedulingPriority(processId?: number, priorityClass?: 'high' | 'realtime'): boolean; +export declare function restoreGpuSchedulingPriority(processId?: number): boolean; +export declare function __setBindingForTests(binding: unknown): void; +export declare const loadError: Error | null; diff --git a/fluxer_desktop/native/win-game-capture/index.js b/fluxer_desktop/native/win-game-capture/index.js new file mode 100644 index 0000000..afb2de7 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/index.js @@ -0,0 +1,469 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {EventEmitter} = require('node:events'); +const {existsSync} = require('node:fs'); +const {join, sep} = require('node:path'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); + +const MODULE_NAME = '@fluxer/win-game-capture'; +const WINDOWS_GAME_CAPTURE_MODULE_ENV = 'FLUXER_WINDOWS_GAME_CAPTURE_MODULE_ENABLED'; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName(arch) { + switch (arch) { + case 'x64': + return 'win-game-capture.win32-x64-msvc.node'; + case 'arm64': + return 'win-game-capture.win32-arm64-msvc.node'; + default: + return null; + } +} + +let binding = null; +let loadError = null; +const nativeRoot = resolveNativeRoot(); + +if (process.platform === 'win32' && process.env[WINDOWS_GAME_CAPTURE_MODULE_ENV] !== 'true') { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot, + packageDir: __dirname, + reason: 'Windows game capture is disabled in this build', + }); +} else if (process.platform === 'win32') { + const fileName = nativeFileName(process.arch); + if (!fileName) { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot, + packageDir: __dirname, + reason: `unsupported Windows architecture: ${process.arch}`, + }); + } else { + const nativePath = join(nativeRoot, fileName); + const loaded = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + probe: false, + }); + binding = loaded.binding; + loadError = loaded.loadError; + } +} else { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot, + packageDir: __dirname, + reason: `not supported on platform ${process.platform}`, + }); +} + +function gameHookFileName(arch) { + switch (arch) { + case 'x64': + return 'fluxer-game-hook.win32-x64-msvc.dll'; + case 'ia32': + return 'fluxer-game-hook.win32-ia32-msvc.dll'; + case 'arm64': + return 'fluxer-game-hook.win32-arm64-msvc.dll'; + default: + return null; + } +} + +function resolveGameHookPathForArch(arch, root = nativeRoot) { + if (process.platform !== 'win32') return null; + const fileName = gameHookFileName(arch); + if (!fileName) return null; + const hookPath = join(root, fileName); + return existsSync(hookPath) ? hookPath : null; +} + +function resolveGameHookPath(root = nativeRoot) { + return resolveGameHookPathForArch(process.arch, root); +} + +function resolveGameHookPathX86(root = nativeRoot) { + return resolveGameHookPathForArch('ia32', root); +} + +function vulkanLayerManifestFileName(arch) { + switch (arch) { + case 'x64': + return 'fluxer-vulkan-layer.win32-x64-msvc.json'; + case 'ia32': + return 'fluxer-vulkan-layer.win32-ia32-msvc.json'; + case 'arm64': + return 'fluxer-vulkan-layer.win32-arm64-msvc.json'; + default: + return null; + } +} + +function resolveVulkanLayerManifestPath(root = nativeRoot) { + if (process.platform !== 'win32') return null; + const fileName = vulkanLayerManifestFileName(process.arch); + if (!fileName) return null; + const manifestPath = join(root, fileName); + return existsSync(manifestPath) ? manifestPath : null; +} + +function registerVulkanLayerManifest(root = nativeRoot) { + if (!binding?.registerVulkanLayerManifest) return false; + const manifestPath = resolveVulkanLayerManifestPath(root); + if (!manifestPath) return false; + binding.registerVulkanLayerManifest(manifestPath); + return true; +} + +function unregisterVulkanLayerManifest(root = nativeRoot) { + if (!binding?.unregisterVulkanLayerManifest) return false; + const manifestPath = resolveVulkanLayerManifestPath(root); + if (!manifestPath) return false; + try { + binding.unregisterVulkanLayerManifest(manifestPath); + return true; + } catch (error) { + console.warn('[win-game-capture] unregisterVulkanLayerManifest failed:', error?.message || error); + return false; + } +} + +function getVulkanLayerRegistrationState(root = nativeRoot) { + const manifestPath = resolveVulkanLayerManifestPath(root); + if (!binding?.getVulkanLayerRegistrationState) { + return {registered: false, manifestExists: Boolean(manifestPath), dllExists: false, manifestPath}; + } + try { + return binding.getVulkanLayerRegistrationState(manifestPath ?? ''); + } catch (error) { + console.warn('[win-game-capture] getVulkanLayerRegistrationState failed:', error?.message || error); + return {registered: false, manifestExists: Boolean(manifestPath), dllExists: false, manifestPath}; + } +} + +if (process.platform === 'win32' && binding) { + try { + registerVulkanLayerManifest(); + } catch (error) { + console.warn('[win-game-capture] registerVulkanLayerManifest failed:', error?.message || error); + } +} + +class ScreenCapture extends EventEmitter { + constructor(options = {}) { + super(); + if (!binding) { + throw loadError || new Error(`${MODULE_NAME} binding unavailable`); + } + this.sourceId = options.sourceId; + this.sourceKind = options.sourceKind ?? 'window'; + this.width = options.width ?? 0; + this.height = options.height ?? 0; + this.frameRate = options.frameRate ?? 30; + this.hookDllPath = options.hookDllPath ?? resolveGameHookPath(); + this.hookDllPathX86 = options.hookDllPathX86 ?? resolveGameHookPathX86(); + this.injectionMethod = options.injectionMethod ?? undefined; + this.captureId = typeof options.captureId === 'string' ? options.captureId : undefined; + this.colorRange = options.colorRange; + this.colorSpace = options.colorSpace; + this.showCursorClicks = options.showCursorClicks === true; + this.captureRect = options.captureRect; + this.frameSinkHandle = options.frameSinkHandle; + this.nativeFrameSinkRequired = options.nativeFrameSinkRequired === true; + this.started = false; + this.stopped = false; + this.closedEmitted = false; + + this.native = new binding.ScreenCapture(); + + this.native.setLifecycleCallback((...lifecycleArgs) => { + const [type, message] = + lifecycleArgs.length === 1 && Array.isArray(lifecycleArgs[0]) ? lifecycleArgs[0] : lifecycleArgs; + if (type === 'stalled') { + if (this.stopped) return; + this.emit('stalled', message); + return; + } + if (type === 'diagnostic') { + if (this.stopped) return; + this.emit('diagnostic', message); + return; + } + if (type === 'error') { + this.emit('error', new Error(message || 'DXGI capture error')); + return; + } + if (type === 'closed') { + if (this.stopped) { + this._emitClosedOnce(); + return; + } + this.stopped = true; + try { + this.native.stop(); + } catch {} + this._emitClosedOnce(); + } + }); + } + + async start() { + if (this.started || this.stopped) return undefined; + if (this.sourceKind === 'game' && !this.hookDllPath) { + throw new Error(`Game capture hook unavailable for ${process.platform}-${process.arch}`); + } + this.started = true; + try { + if (this.frameSinkHandle != null) { + if (typeof this.native.setFrameSinkHandle !== 'function') { + throw new Error(`${MODULE_NAME} native binding does not support native frame sink handles`); + } + this.native.setFrameSinkHandle(this.frameSinkHandle); + } else if (this.nativeFrameSinkRequired) { + throw new Error('Native frame sink handle is required for Windows screen capture'); + } + const result = this.native.start( + this.sourceId, + this.sourceKind, + this.width || undefined, + this.height || undefined, + this.frameRate || undefined, + this.sourceKind === 'game' ? this.hookDllPath : undefined, + this.sourceKind === 'game' ? (this.hookDllPathX86 ?? undefined) : undefined, + this.sourceKind === 'game' ? (this.injectionMethod ?? undefined) : undefined, + this.captureId, + { + colorRange: this.colorRange, + colorSpace: this.colorSpace, + showCursorClicks: this.showCursorClicks, + captureRect: this.captureRect, + }, + ); + return result + ? { + width: result.width, + height: result.height, + frameRate: result.frameRate, + pixelFormat: result.pixelFormat, + } + : undefined; + } catch (error) { + this.stopped = true; + this.emit('error', error instanceof Error ? error : new Error(String(error))); + throw error; + } + } + + async stop() { + if (this.stopped) return; + this.stopped = true; + try { + this.native.stop(); + } finally { + this._emitClosedOnce(); + } + } + + getDiagnostics() { + if (!this.native || typeof this.native.getDiagnostics !== 'function') return null; + try { + return this.native.getDiagnostics() ?? null; + } catch (error) { + console.warn('[win-game-capture] getDiagnostics failed:', error?.message || error); + return null; + } + } + + getSharedTextureHandle() { + if (!this.native || typeof this.native.getSharedTextureHandle !== 'function') return null; + try { + return this.native.getSharedTextureHandle() ?? null; + } catch (error) { + console.warn('[win-game-capture] getSharedTextureHandle failed:', error?.message || error); + return null; + } + } + + attachEncoder(width, height) { + if (!this.native || typeof this.native.attachEncoder !== 'function') { + throw new Error(`${MODULE_NAME} native binding does not support encoder attachment`); + } + this.native.attachEncoder(width, height); + } + + detachEncoder() { + if (!this.native || typeof this.native.detachEncoder !== 'function') return; + this.native.detachEncoder(); + } + + isEncoderAttached() { + if (!this.native || typeof this.native.isEncoderAttached !== 'function') return false; + return Boolean(this.native.isEncoderAttached()); + } + + encoderRingFullCount() { + if (!this.native || typeof this.native.encoderRingFullCount !== 'function') return 0; + const count = this.native.encoderRingFullCount(); + return Number.isFinite(count) && count > 0 ? Math.floor(count) : 0; + } + + getEncoderAttachDiagnostics() { + if (!this.native || typeof this.native.getEncoderAttachDiagnostics !== 'function') return null; + try { + return this.native.getEncoderAttachDiagnostics() ?? null; + } catch (error) { + console.warn('[win-game-capture] getEncoderAttachDiagnostics failed:', error?.message || error); + return null; + } + } + + getFrameSinkDiagnostics() { + if (!this.native || typeof this.native.getFrameSinkDiagnostics !== 'function') { + return { + accepted: 0, + coalesced: 0, + rejected: 0, + mediaFramesDroppedWithoutSink: 0, + cpuFallbackFramesDropped: 0, + }; + } + try { + return this.native.getFrameSinkDiagnostics(); + } catch (error) { + console.warn('[win-game-capture] getFrameSinkDiagnostics failed:', error?.message || error); + return { + accepted: 0, + coalesced: 0, + rejected: 0, + mediaFramesDroppedWithoutSink: 0, + cpuFallbackFramesDropped: 0, + }; + } + } + + _emitClosedOnce() { + if (this.closedEmitted) return; + this.closedEmitted = true; + queueMicrotask(() => this.emit('closed')); + } +} + +const FALLBACK_STRATEGY_NAMES = new Set(['game-hook', 'wgc', 'dxgi-duplication', 'window-gdi', 'none']); +function parseFallbackRecommendation(message) { + if (typeof message !== 'string') return null; + const match = message.match(/\[next-strategy=([a-z-]+)\]/); + if (!match) return null; + const name = match[1]; + return FALLBACK_STRATEGY_NAMES.has(name) ? name : null; +} + +async function listSources() { + if (!binding || typeof binding.listSources !== 'function') return []; + try { + const sources = await binding.listSources(); + if (!Array.isArray(sources)) return []; + return sources + .filter((source) => { + return ( + source && + (source.kind === 'screen' || source.kind === 'window' || source.kind === 'game') && + typeof source.id === 'string' && + source.id.length > 0 + ); + }) + .map((source) => ({ + kind: source.kind, + id: source.id, + name: typeof source.name === 'string' && source.name.length > 0 ? source.name : source.id, + width: Number.isFinite(source.width) ? Math.max(0, Math.floor(source.width)) : 0, + height: Number.isFinite(source.height) ? Math.max(0, Math.floor(source.height)) : 0, + targetPid: Number.isFinite(source.targetPid) && source.targetPid > 0 ? Math.floor(source.targetPid) : undefined, + })); + } catch (error) { + console.warn('[win-game-capture] listSources failed:', error?.message || error); + return []; + } +} + +function isSupported() { + if (!binding) return false; + return binding.isSupported(); +} + +function getAvailability() { + if (!binding) { + return {available: false, backend: 'windows-game-capture', reason: 'load-failed'}; + } + return binding.getAvailability(); +} + +function normalizeProcessId(processId) { + if (processId === undefined || processId === null) return undefined; + if (Number.isInteger(processId) && processId > 0) return processId; + throw new TypeError(`Invalid process id: ${processId}`); +} + +function normalizePriorityClass(priorityClass) { + if (priorityClass === undefined || priorityClass === null) return undefined; + const normalized = String(priorityClass).trim().toLowerCase(); + if (normalized === 'real-time') return 'realtime'; + if (normalized === 'high' || normalized === 'realtime') return normalized; + throw new TypeError(`Invalid GPU scheduling priority class: ${priorityClass}`); +} + +function elevateGpuSchedulingPriority(processId, priorityClass) { + if (!binding) return false; + try { + binding.elevateGpuSchedulingPriority(normalizeProcessId(processId), normalizePriorityClass(priorityClass)); + return true; + } catch (error) { + console.warn('[win-game-capture] elevateGpuSchedulingPriority failed:', error?.message || error); + return false; + } +} + +function restoreGpuSchedulingPriority(processId) { + if (!binding) return false; + try { + binding.restoreGpuSchedulingPriority(normalizeProcessId(processId)); + return true; + } catch (error) { + console.warn('[win-game-capture] restoreGpuSchedulingPriority failed:', error?.message || error); + return false; + } +} + +function __setBindingForTests(nextBinding) { + binding = nextBinding; + loadError = null; +} + +module.exports = { + isSupported, + getAvailability, + resolveGameHookPath, + resolveGameHookPathX86, + resolveVulkanLayerManifestPath, + registerVulkanLayerManifest, + unregisterVulkanLayerManifest, + getVulkanLayerRegistrationState, + listSources, + ScreenCapture, + parseFallbackRecommendation, + elevateGpuSchedulingPriority, + restoreGpuSchedulingPriority, + __setBindingForTests, + get loadError() { + return loadError; + }, +}; diff --git a/fluxer_desktop/native/win-game-capture/index.test.mjs b/fluxer_desktop/native/win-game-capture/index.test.mjs new file mode 100644 index 0000000..1c401f9 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/index.test.mjs @@ -0,0 +1,839 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import assert from 'node:assert/strict'; +import {existsSync} from 'node:fs'; +import {createRequire} from 'node:module'; +import {afterEach, describe, test} from 'node:test'; + +const require = createRequire(import.meta.url); +const winGameCapture = require('./index.js'); + +const startupSupported = winGameCapture.isSupported(); +const startupAvailability = winGameCapture.getAvailability(); +const realBindingSkip = startupSupported + ? false + : 'no native binding loaded at startup (binding-less platform / unbuilt addon)'; + +const hasBindingHook = typeof winGameCapture.__setBindingForTests === 'function'; +const injectionSkip = hasBindingHook ? false : 'no __setBindingForTests hook exported'; + +if (!hasBindingHook) { + console.warn( + '[index.test.mjs] __setBindingForTests not present on index.js; ' + + 'skipping binding-injection tests and running only the no-binding tests. ' + + 'This should only happen when testing an older or stripped loader.', + ); +} + +function makeFakeBinding() { + const calls = []; + const frameSinkHandleCalls = []; + const priorityCalls = []; + const natives = []; + const diagnostics = { + state: 1, + apiType: 5, + transport: 0, + fallbackReason: 0, + captureFlags: 0, + width: 1920, + height: 1080, + dxgiFormat: 87, + frameCounter: 42, + droppedFrameCounter: 0, + lastPresentTimestampUs: 123456, + lastError: 0, + requestedInjectionMethod: 'auto', + injectionMethod: 'remote-thread', + activeStrategy: 'game-hook', + lastFallbackReason: '', + startOptions: { + colorRange: 'full', + colorSpace: 'rec709', + showCursorClicks: true, + captureRect: {x: 10, y: 20, width: 300, height: 200}, + unsupportedOptions: ['showCursorClicks', 'captureRect', 'colorRange', 'colorSpace'], + }, + frameSinkAccepted: 0, + frameSinkCoalesced: 0, + frameSinkRejected: 0, + mediaFramesDroppedWithoutSink: 0, + cpuFallbackFramesDropped: 0, + }; + const encoderDiagnostics = { + attached: false, + width: 0, + height: 0, + capacity: 0, + framesSubmitted: 0, + framesDropped: 0, + ringFullEvents: 0, + failedBlits: 0, + }; + const frameSinkDiagnostics = { + accepted: 0, + coalesced: 0, + rejected: 0, + mediaFramesDroppedWithoutSink: 0, + cpuFallbackFramesDropped: 0, + }; + + class FakeNative { + constructor() { + this.lifecycleCallback = undefined; + this.stopCount = 0; + natives.push(this); + } + + setLifecycleCallback(callback) { + this.lifecycleCallback = callback; + } + + setFrameSinkHandle(handle) { + frameSinkHandleCalls.push(handle); + } + + start( + sourceId, + sourceKind, + width, + height, + frameRate, + hookDllPath, + hookDllPathX86, + injectionMethod, + captureId, + captureOptions, + ) { + calls.push({ + sourceId, + sourceKind, + width, + height, + frameRate, + hookDllPath, + hookDllPathX86, + injectionMethod, + captureId, + captureOptions, + }); + return { + width: width || 1920, + height: height || 1080, + frameRate: frameRate || 30, + pixelFormat: 'bgra', + }; + } + + stop() { + this.stopCount += 1; + } + + getDiagnostics() { + return diagnostics; + } + + attachEncoder(width, height) { + encoderDiagnostics.attached = true; + encoderDiagnostics.width = width; + encoderDiagnostics.height = height; + encoderDiagnostics.capacity = 8; + } + + detachEncoder() { + encoderDiagnostics.attached = false; + } + + isEncoderAttached() { + return encoderDiagnostics.attached; + } + + encoderRingFullCount() { + return encoderDiagnostics.ringFullEvents; + } + + getEncoderAttachDiagnostics() { + if (!encoderDiagnostics.attached) return null; + return {...encoderDiagnostics}; + } + + getFrameSinkDiagnostics() { + return {...frameSinkDiagnostics}; + } + } + + return { + binding: { + ScreenCapture: FakeNative, + isSupported: () => true, + getAvailability: () => ({available: true, backend: 'windows-game-capture'}), + listSources: () => [ + { + kind: 'screen', + id: 'screen:0:0', + name: 'Display 1', + width: 2560, + height: 1440, + }, + { + kind: 'window', + id: 'window:5050:0', + name: 'Fixture', + width: 1280, + height: 720, + targetPid: 4242, + }, + { + kind: 'browser', + id: 'screen:1:0', + name: 'unsupported shape', + }, + ], + elevateGpuSchedulingPriority: (processId, priorityClass) => { + priorityCalls.push({type: 'elevate', processId, priorityClass}); + }, + restoreGpuSchedulingPriority: (processId) => { + priorityCalls.push({type: 'restore', processId}); + }, + }, + calls, + frameSinkHandleCalls, + priorityCalls, + natives, + diagnostics, + encoderDiagnostics, + frameSinkDiagnostics, + }; +} + +afterEach(() => { + if (hasBindingHook) { + winGameCapture.__setBindingForTests(null); + } +}); + +describe('win-game-capture loader wrapper -- binding-absent fallback path', () => { + function forceNoBinding() { + if (hasBindingHook) winGameCapture.__setBindingForTests(null); + } + + test( + 'isSupported() is false when no binding is loaded', + {skip: hasBindingHook ? false : 'no __setBindingForTests hook'}, + () => { + forceNoBinding(); + assert.equal(winGameCapture.isSupported(), false); + }, + ); + + test( + 'getAvailability() returns the {available:false, ...} shape when no binding is loaded', + {skip: hasBindingHook ? false : 'no __setBindingForTests hook'}, + () => { + forceNoBinding(); + const availability = winGameCapture.getAvailability(); + assert.equal(availability.available, false); + assert.equal(availability.backend, 'windows-game-capture'); + assert.equal(typeof availability.reason, 'string'); + }, + ); + + test( + 'constructing ScreenCapture without a binding throws', + {skip: hasBindingHook ? false : 'no __setBindingForTests hook'}, + () => { + forceNoBinding(); + assert.throws(() => new winGameCapture.ScreenCapture({sourceId: '1'})); + try { + new winGameCapture.ScreenCapture({sourceId: '1'}); + assert.fail('expected ScreenCapture constructor to throw without a binding'); + } catch (error) { + assert.ok(error instanceof Error); + } + }, + ); +}); + +describe('win-game-capture loader wrapper -- arch path resolvers (platform-portable)', () => { + test('resolveGameHookPath() is null or the host-arch hook path', () => { + const r = winGameCapture.resolveGameHookPath(); + assert.ok( + r === null || (typeof r === 'string' && /fluxer-game-hook\.win32-(x64|ia32|arm64)-msvc\.dll$/.test(r)), + `unexpected resolveGameHookPath(): ${r}`, + ); + }); + + test('resolveGameHookPathX86() is null or the ia32 hook path', () => { + const r = winGameCapture.resolveGameHookPathX86(); + assert.ok( + r === null || (typeof r === 'string' && r.endsWith('fluxer-game-hook.win32-ia32-msvc.dll')), + `unexpected resolveGameHookPathX86(): ${r}`, + ); + }); + + test('resolveVulkanLayerManifestPath() is null or the host-arch layer manifest path', () => { + const r = winGameCapture.resolveVulkanLayerManifestPath(); + assert.ok( + r === null || (typeof r === 'string' && /fluxer-vulkan-layer\.win32-(x64|ia32|arm64)-msvc\.json$/.test(r)), + `unexpected resolveVulkanLayerManifestPath(): ${r}`, + ); + }); +}); + +describe('win-game-capture loader wrapper -- real native binding (built Windows box)', () => { + test('getAvailability() reports available with the windows-game-capture backend', {skip: realBindingSkip}, () => { + assert.equal(startupAvailability.available, true); + assert.equal(startupAvailability.backend, 'windows-game-capture'); + }); + + test('resolveGameHookPath() points at an existing host-arch hook DLL', {skip: realBindingSkip}, () => { + const r = winGameCapture.resolveGameHookPath(); + assert.equal(typeof r, 'string', 'expected a hook path on a built Windows box'); + assert.ok(existsSync(r), `hook DLL should exist on disk: ${r}`); + }); + + test('resolveVulkanLayerManifestPath() points at an existing layer manifest', {skip: realBindingSkip}, () => { + const r = winGameCapture.resolveVulkanLayerManifestPath(); + assert.equal(typeof r, 'string', 'expected a Vulkan layer manifest path on a built Windows box'); + assert.ok(existsSync(r), `Vulkan layer manifest should exist on disk: ${r}`); + }); +}); + +describe('win-game-capture loader wrapper -- injected fake binding', () => { + test( + 'listSources() forwards sanitized screen/window sources from the native binding', + {skip: injectionSkip}, + async () => { + const {binding} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const sources = await winGameCapture.listSources(); + assert.deepEqual(sources, [ + { + kind: 'screen', + id: 'screen:0:0', + name: 'Display 1', + width: 2560, + height: 1440, + targetPid: undefined, + }, + { + kind: 'window', + id: 'window:5050:0', + name: 'Fixture', + width: 1280, + height: 720, + targetPid: 4242, + }, + ]); + }, + ); + + test( + 'start() forwards sourceId/kind/dims/frameRate and BOTH hook paths (6th + 7th args)', + {skip: injectionSkip}, + async () => { + const {binding, calls} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({ + sourceId: '987654', + sourceKind: 'game', + width: 1600, + height: 900, + frameRate: 60, + hookDllPath: 'C:/hooks/fluxer-game-hook.win32-x64-msvc.dll', + hookDllPathX86: 'C:/hooks/fluxer-game-hook.win32-ia32-msvc.dll', + captureId: 'capture-1', + colorRange: 'full', + colorSpace: 'rec709', + showCursorClicks: true, + captureRect: {x: 10, y: 20, width: 300, height: 200}, + }); + capture.on('error', () => {}); + const result = await capture.start(); + assert.equal(calls.length, 1); + assert.deepEqual(calls[0], { + sourceId: '987654', + sourceKind: 'game', + width: 1600, + height: 900, + frameRate: 60, + hookDllPath: 'C:/hooks/fluxer-game-hook.win32-x64-msvc.dll', + hookDllPathX86: 'C:/hooks/fluxer-game-hook.win32-ia32-msvc.dll', + injectionMethod: undefined, + captureId: 'capture-1', + captureOptions: { + colorRange: 'full', + colorSpace: 'rec709', + showCursorClicks: true, + captureRect: {x: 10, y: 20, width: 300, height: 200}, + }, + }); + assert.equal(result.pixelFormat, 'bgra'); + assert.equal(result.width, 1600); + assert.equal(result.height, 900); + assert.equal(result.frameRate, 60); + }, + ); + + test( + 'start() forwards the injectionMethod option as the 8th arg for game capture', + {skip: injectionSkip}, + async () => { + const {binding, calls} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({ + sourceId: '555', + sourceKind: 'game', + hookDllPath: 'C:/hooks/fluxer-game-hook.win32-x64-msvc.dll', + hookDllPathX86: 'C:/hooks/fluxer-game-hook.win32-ia32-msvc.dll', + injectionMethod: 'set-windows-hook', + }); + capture.on('error', () => {}); + await capture.start(); + assert.equal(calls.length, 1); + assert.equal(calls[0].injectionMethod, 'set-windows-hook'); + }, + ); + + test('window sourceKind does not forward the injectionMethod', {skip: injectionSkip}, async () => { + const {binding, calls} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({ + sourceId: '42', + sourceKind: 'window', + injectionMethod: 'set-windows-hook', + }); + capture.on('error', () => {}); + await capture.start(); + assert.equal(calls[0].injectionMethod, undefined); + }); + + test('window sourceKind does not forward hook paths', {skip: injectionSkip}, async () => { + const {binding, calls} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({ + sourceId: '42', + sourceKind: 'window', + hookDllPath: 'C:/hooks/primary.dll', + hookDllPathX86: 'C:/hooks/x86.dll', + }); + capture.on('error', () => {}); + await capture.start(); + assert.equal(calls[0].sourceKind, 'window'); + assert.equal(calls[0].hookDllPath, undefined); + assert.equal(calls[0].hookDllPathX86, undefined); + }); + + test('screen sourceKind does not forward hook paths or the injectionMethod', {skip: injectionSkip}, async () => { + const {binding, calls} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({ + sourceId: 'screen:0:0', + sourceKind: 'screen', + hookDllPath: 'C:/hooks/primary.dll', + hookDllPathX86: 'C:/hooks/x86.dll', + injectionMethod: 'set-windows-hook', + }); + capture.on('error', () => {}); + await capture.start(); + assert.equal(calls[0].sourceKind, 'screen'); + assert.equal(calls[0].hookDllPath, undefined); + assert.equal(calls[0].hookDllPathX86, undefined); + assert.equal(calls[0].injectionMethod, undefined); + }); + + test('getDiagnostics() exposes native start option state', {skip: injectionSkip}, () => { + const {binding} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({sourceId: '42', sourceKind: 'window'}); + const diagnostics = capture.getDiagnostics(); + assert.deepEqual(diagnostics.startOptions, { + colorRange: 'full', + colorSpace: 'rec709', + showCursorClicks: true, + captureRect: {x: 10, y: 20, width: 300, height: 200}, + unsupportedOptions: ['showCursorClicks', 'captureRect', 'colorRange', 'colorSpace'], + }); + }); + + test('encoder attachment diagnostics are forwarded by the wrapper', {skip: injectionSkip}, () => { + const {binding, encoderDiagnostics} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({sourceId: '42', sourceKind: 'window'}); + + assert.equal(capture.isEncoderAttached(), false); + assert.equal(capture.encoderRingFullCount(), 0); + assert.equal(capture.getEncoderAttachDiagnostics(), null); + + capture.attachEncoder(1280, 720); + encoderDiagnostics.framesSubmitted = 3; + encoderDiagnostics.framesDropped = 1; + encoderDiagnostics.ringFullEvents = 1; + encoderDiagnostics.failedBlits = 0; + + assert.equal(capture.isEncoderAttached(), true); + assert.equal(capture.encoderRingFullCount(), 1); + assert.deepEqual(capture.getEncoderAttachDiagnostics(), { + attached: true, + width: 1280, + height: 720, + capacity: 8, + framesSubmitted: 3, + framesDropped: 1, + ringFullEvents: 1, + failedBlits: 0, + }); + + capture.detachEncoder(); + assert.equal(capture.isEncoderAttached(), false); + }); + + test('frame sink diagnostics are forwarded by the wrapper', {skip: injectionSkip}, () => { + const {binding, frameSinkDiagnostics} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({sourceId: '42', sourceKind: 'window'}); + + frameSinkDiagnostics.accepted = 7; + frameSinkDiagnostics.coalesced = 2; + frameSinkDiagnostics.rejected = 1; + frameSinkDiagnostics.mediaFramesDroppedWithoutSink = 3; + frameSinkDiagnostics.cpuFallbackFramesDropped = 4; + + assert.deepEqual(capture.getFrameSinkDiagnostics(), { + accepted: 7, + coalesced: 2, + rejected: 1, + mediaFramesDroppedWithoutSink: 3, + cpuFallbackFramesDropped: 4, + }); + }); + + test('installs a native frame sink handle once before start', {skip: injectionSkip}, async () => { + const {binding, calls, frameSinkHandleCalls} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const frameSinkHandle = {native: true}; + const capture = new winGameCapture.ScreenCapture({ + sourceId: '42', + sourceKind: 'window', + frameSinkHandle, + nativeFrameSinkRequired: true, + }); + capture.on('error', () => {}); + + await capture.start(); + + assert.deepEqual(frameSinkHandleCalls, [frameSinkHandle]); + assert.equal(calls.length, 1); + }); + + test( + 'fails before native start when a native frame sink is required but missing', + {skip: injectionSkip}, + async () => { + const {binding, calls, frameSinkHandleCalls} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({ + sourceId: '42', + sourceKind: 'window', + nativeFrameSinkRequired: true, + }); + capture.on('error', () => {}); + + await assert.rejects(() => capture.start(), /native frame sink handle is required/i); + assert.deepEqual(frameSinkHandleCalls, []); + assert.deepEqual(calls, []); + }, + ); + + test( + 'game sourceKind without a hook path fails closed without WGC/browser fallback', + {skip: injectionSkip}, + async () => { + const {binding, calls} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({ + sourceId: '7', + sourceKind: 'game', + hookDllPath: '', + hookDllPathX86: '', + }); + capture.on('error', () => {}); + await assert.rejects(() => capture.start(), /Game capture hook unavailable/); + assert.equal(calls.length, 0, 'native start must not be called when the game hook is unavailable'); + }, + ); + + test('lifecycle "error" emits an Error event', {skip: injectionSkip}, () => { + const {binding, natives} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({sourceId: '1'}); + const errors = []; + capture.on('error', (err) => errors.push(err)); + natives[0].lifecycleCallback('error', 'DXGI device removed'); + assert.equal(errors.length, 1); + assert.ok(errors[0] instanceof Error); + assert.equal(errors[0].message, 'DXGI device removed'); + }); + + test('lifecycle "closed" stops the native capture and emits closed once', {skip: injectionSkip}, async () => { + const {binding, natives} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({sourceId: '1'}); + let closed = 0; + capture.on('closed', () => { + closed += 1; + }); + natives[0].lifecycleCallback('closed', ''); + await Promise.resolve(); + await capture.stop(); + assert.equal(closed, 1); + assert.equal(natives[0].stopCount, 1); + }); + + test('non-fatal "stalled" lifecycle re-emits as a stalled event WITHOUT stopping', {skip: injectionSkip}, () => { + const {binding, natives} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({sourceId: '1'}); + const stalls = []; + let closed = 0; + capture.on('stalled', (message) => stalls.push(message)); + capture.on('closed', () => { + closed += 1; + }); + natives[0].lifecycleCallback('stalled', 'frame counter frozen while focused'); + assert.deepEqual(stalls, ['frame counter frozen while focused']); + assert.equal(natives[0].stopCount, 0); + assert.equal(closed, 0); + }); + + test( + 'non-fatal "diagnostic" lifecycle re-emits as a diagnostic event WITHOUT stopping', + {skip: injectionSkip}, + () => { + const {binding, natives} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({sourceId: '1'}); + const diags = []; + let closed = 0; + capture.on('diagnostic', (message) => diags.push(message)); + capture.on('closed', () => { + closed += 1; + }); + natives[0].lifecycleCallback('diagnostic', 'fell back to CPU readback'); + assert.deepEqual(diags, ['fell back to CPU readback']); + assert.equal(natives[0].stopCount, 0); + assert.equal(closed, 0); + }, + ); + + test( + 'lifecycle "error" in the real napi [type, message] array shape emits an Error event', + {skip: injectionSkip}, + () => { + const {binding, natives} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({sourceId: '1'}); + const errors = []; + capture.on('error', (err) => errors.push(err)); + natives[0].lifecycleCallback(['error', 'DXGI device removed']); + assert.equal(errors.length, 1); + assert.ok(errors[0] instanceof Error); + assert.equal(errors[0].message, 'DXGI device removed'); + }, + ); + + test( + 'lifecycle "closed" in the napi array shape stops the native capture and emits closed once', + {skip: injectionSkip}, + async () => { + const {binding, natives} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({sourceId: '1'}); + let closed = 0; + capture.on('closed', () => { + closed += 1; + }); + natives[0].lifecycleCallback(['closed', '']); + await Promise.resolve(); + await capture.stop(); + assert.equal(closed, 1); + assert.equal(natives[0].stopCount, 1); + }, + ); + + test( + 'lifecycle "stalled" in the napi array shape re-emits as a stalled event WITHOUT stopping', + {skip: injectionSkip}, + () => { + const {binding, natives} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({sourceId: '1'}); + const stalls = []; + let closed = 0; + capture.on('stalled', (message) => stalls.push(message)); + capture.on('closed', () => { + closed += 1; + }); + natives[0].lifecycleCallback(['stalled', 'frame counter frozen while focused']); + assert.deepEqual(stalls, ['frame counter frozen while focused']); + assert.equal(natives[0].stopCount, 0); + assert.equal(closed, 0); + }, + ); + + test( + 'lifecycle "diagnostic" (injected via ) in the napi array shape re-emits WITHOUT stopping', + {skip: injectionSkip}, + () => { + const {binding, natives} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({sourceId: '1'}); + const diags = []; + let closed = 0; + capture.on('diagnostic', (message) => diags.push(message)); + capture.on('closed', () => { + closed += 1; + }); + natives[0].lifecycleCallback(['diagnostic', 'game capture injected via set-windows-hook']); + assert.deepEqual(diags, ['game capture injected via set-windows-hook']); + assert.equal(natives[0].stopCount, 0); + assert.equal(closed, 0); + }, + ); + + test('getDiagnostics() passes the native snapshot through', {skip: injectionSkip}, () => { + const {binding, diagnostics} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({sourceId: '1'}); + const snapshot = capture.getDiagnostics(); + assert.deepEqual(snapshot, diagnostics); + }); + + test('getDiagnostics() surfaces the activeStrategy + lastFallbackReason fields', {skip: injectionSkip}, () => { + const {binding, diagnostics} = makeFakeBinding(); + diagnostics.activeStrategy = 'dxgi-duplication'; + diagnostics.lastFallbackReason = + 'game-hook capture could not inject its capture hook; switching to dxgi-duplication capture'; + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({sourceId: '1'}); + const snapshot = capture.getDiagnostics(); + assert.equal(snapshot.activeStrategy, 'dxgi-duplication'); + assert.match(snapshot.lastFallbackReason, /switching to dxgi-duplication/); + }); + + test('elevateGpuSchedulingPriority forwards the optional realtime priority class', {skip: injectionSkip}, () => { + const {binding, priorityCalls} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + assert.equal(winGameCapture.elevateGpuSchedulingPriority(1234, 'realtime'), true); + assert.deepEqual(priorityCalls, [{type: 'elevate', processId: 1234, priorityClass: 'realtime'}]); + }); + + test('elevateGpuSchedulingPriority canonicalizes the real-time priority alias', {skip: injectionSkip}, () => { + const {binding, priorityCalls} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + assert.equal(winGameCapture.elevateGpuSchedulingPriority(1234, 'real-time'), true); + assert.deepEqual(priorityCalls, [{type: 'elevate', processId: 1234, priorityClass: 'realtime'}]); + }); + + test('elevateGpuSchedulingPriority preserves the existing default priority class', {skip: injectionSkip}, () => { + const {binding, priorityCalls} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + assert.equal(winGameCapture.elevateGpuSchedulingPriority(1234), true); + assert.deepEqual(priorityCalls, [{type: 'elevate', processId: 1234, priorityClass: undefined}]); + }); + + test( + 'elevateGpuSchedulingPriority rejects unsupported priority classes before binding calls', + {skip: injectionSkip}, + () => { + const {binding, priorityCalls} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + assert.equal(winGameCapture.elevateGpuSchedulingPriority(1234, 'normal'), false); + assert.deepEqual(priorityCalls, []); + }, + ); + + test('lifecycle fallback "error" (next-strategy=...) re-emits as a fatal error event', {skip: injectionSkip}, () => { + const {binding, natives} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({sourceId: '1'}); + const errors = []; + capture.on('error', (err) => errors.push(err)); + natives[0].lifecycleCallback([ + 'error', + 'fallback: game-hook -> dxgi-duplication (game-hook capture could not inject its capture hook) [next-strategy=dxgi-duplication]', + ]); + assert.equal(errors.length, 1); + assert.ok(errors[0] instanceof Error); + assert.equal(winGameCapture.parseFallbackRecommendation(errors[0].message), 'dxgi-duplication'); + }); + + test( + 'lifecycle fallback "diagnostic" (upgrade, next-strategy=...) re-emits WITHOUT stopping', + {skip: injectionSkip}, + () => { + const {binding, natives} = makeFakeBinding(); + winGameCapture.__setBindingForTests(binding); + const capture = new winGameCapture.ScreenCapture({sourceId: '1'}); + const diags = []; + let closed = 0; + capture.on('diagnostic', (message) => diags.push(message)); + capture.on('closed', () => { + closed += 1; + }); + natives[0].lifecycleCallback([ + 'diagnostic', + 'upgrade: window-gdi -> dxgi-duplication (window-gdi has been stable) [next-strategy=dxgi-duplication]', + ]); + assert.equal(diags.length, 1); + assert.equal(winGameCapture.parseFallbackRecommendation(diags[0]), 'dxgi-duplication'); + assert.equal(natives[0].stopCount, 0); + assert.equal(closed, 0); + }, + ); +}); + +describe('win-game-capture loader wrapper -- parseFallbackRecommendation', () => { + test('extracts the recommended strategy from a transition error message', () => { + assert.equal( + winGameCapture.parseFallbackRecommendation( + 'fallback: game-hook -> window-gdi (reason) [next-strategy=window-gdi]', + ), + 'window-gdi', + ); + }); + + test('returns "none" for an exhausted give-up message', () => { + assert.equal( + winGameCapture.parseFallbackRecommendation( + 'fallback exhausted: window-gdi was the last resort [next-strategy=none]', + ), + 'none', + ); + }); + + test('returns null for an ordinary error with no recommendation', () => { + assert.equal(winGameCapture.parseFallbackRecommendation('DXGI device removed'), null); + assert.equal(winGameCapture.parseFallbackRecommendation(undefined), null); + assert.equal(winGameCapture.parseFallbackRecommendation(''), null); + }); + + test('returns null for an unrecognised strategy token', () => { + assert.equal(winGameCapture.parseFallbackRecommendation('[next-strategy=teleporter]'), null); + }); + + test('accepts the WGC fallback strategy token', () => { + assert.equal(winGameCapture.parseFallbackRecommendation('[next-strategy=wgc]'), 'wgc'); + assert.equal( + winGameCapture.parseFallbackRecommendation( + 'fallback: game-hook -> wgc (game-hook capture could not inject its capture hook) [next-strategy=wgc]', + ), + 'wgc', + ); + }); + + test('rejects browser fallback strategy tokens', () => { + assert.equal(winGameCapture.parseFallbackRecommendation('[next-strategy=browser-display]'), null); + assert.equal(winGameCapture.parseFallbackRecommendation('[next-strategy=desktop-capturer]'), null); + }); +}); diff --git a/fluxer_desktop/native/win-game-capture/inject-helper/Cargo.lock b/fluxer_desktop/native/win-game-capture/inject-helper/Cargo.lock new file mode 100644 index 0000000..3b8e817 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/inject-helper/Cargo.lock @@ -0,0 +1,25 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "fluxer_inject_helper" +version = "0.0.0" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] diff --git a/fluxer_desktop/native/win-game-capture/inject-helper/Cargo.toml b/fluxer_desktop/native/win-game-capture/inject-helper/Cargo.toml new file mode 100644 index 0000000..7e53060 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/inject-helper/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "fluxer_inject_helper" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[[bin]] +name = "fluxer-inject-helper" +path = "src/main.rs" + +[profile.release] +# Keep the helper tiny: it is a one-shot injector exe shipped alongside the +# hook DLLs, so optimise hard for size and strip symbols. +opt-level = "z" +lto = true +codegen-units = 1 +panic = "abort" +strip = true + +[target.'cfg(target_os = "windows")'.dependencies] +windows-sys = {version = "0.61.2", features = [ + "Win32_Foundation", + "Win32_Security", + "Win32_System_Diagnostics_Debug", + "Win32_System_LibraryLoader", + "Win32_System_Memory", + "Win32_System_Threading", +]} diff --git a/fluxer_desktop/native/win-game-capture/inject-helper/src/main.rs b/fluxer_desktop/native/win-game-capture/inject-helper/src/main.rs new file mode 100644 index 0000000..c23442e --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/inject-helper/src/main.rs @@ -0,0 +1,242 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![deny(clippy::all)] + +#[cfg(not(target_os = "windows"))] +fn main() { + eprintln!("fluxer-inject-helper is only supported on Windows"); + std::process::exit(Stage::Unsupported as i32); +} + +#[repr(i32)] +#[derive(Clone, Copy)] +#[cfg_attr(not(target_os = "windows"), allow(dead_code))] +enum Stage { + Success = 0, + BadArgs = 2, + HookMissing = 3, + OpenProcess = 4, + Alloc = 5, + Write = 6, + Kernel32 = 7, + LoadLibraryAddr = 8, + CreateThread = 9, + WaitTimeout = 10, + LoadLibraryFailed = 11, + #[cfg_attr(target_os = "windows", allow(dead_code))] + Unsupported = 64, +} + +#[cfg(target_os = "windows")] +fn main() { + let code = win::run(); + std::process::exit(code as i32); +} + +#[cfg(target_os = "windows")] +mod win { + use super::Stage; + use core::ffi::c_void; + use std::ptr::{null, null_mut}; + use windows_sys::Win32::Foundation::{ + CloseHandle, GetLastError, HANDLE, INVALID_HANDLE_VALUE, WAIT_ABANDONED, WAIT_OBJECT_0, + }; + use windows_sys::Win32::System::Diagnostics::Debug::{OutputDebugStringW, WriteProcessMemory}; + use windows_sys::Win32::System::LibraryLoader::{GetModuleHandleW, GetProcAddress}; + use windows_sys::Win32::System::Memory::{ + MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, PAGE_READWRITE, VirtualAllocEx, VirtualFreeEx, + }; + use windows_sys::Win32::System::Threading::{ + CreateRemoteThread, GetExitCodeThread, INFINITE, OpenProcess, PROCESS_CREATE_THREAD, + PROCESS_QUERY_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_READ, PROCESS_VM_WRITE, + WaitForSingleObject, + }; + + const DEFAULT_TIMEOUT_MS: u32 = 10_000; + + struct OwnedHandle(HANDLE); + + impl OwnedHandle { + fn raw(&self) -> HANDLE { + self.0 + } + } + + impl Drop for OwnedHandle { + fn drop(&mut self) { + if !self.0.is_null() && self.0 != INVALID_HANDLE_VALUE { + unsafe { + CloseHandle(self.0); + } + } + } + } + + fn to_wide(value: &str) -> Vec { + value.encode_utf16().chain(std::iter::once(0)).collect() + } + + fn log(message: &str) { + let text = format!("[fluxer-inject-helper] {message}"); + let wide = to_wide(&text); + unsafe { + OutputDebugStringW(wide.as_ptr()); + } + eprintln!("{text}"); + } + + fn fail(stage: Stage, context: &str) -> Stage { + let err = unsafe { GetLastError() }; + log(&format!( + "FAILED stage={} ({context}); GetLastError={err}", + stage as i32 + )); + stage + } + + pub(super) fn run() -> Stage { + let args: Vec = std::env::args().skip(1).collect(); + if args.len() < 2 || args.len() > 3 { + log(&format!( + "bad args: expected [timeout-ms], got {} arg(s)", + args.len() + )); + return Stage::BadArgs; + } + let Ok(target_pid) = args[0].parse::() else { + log(&format!("bad args: unparseable pid {:?}", args[0])); + return Stage::BadArgs; + }; + if target_pid == 0 { + log("bad args: pid must be non-zero"); + return Stage::BadArgs; + } + let hook_path = args[1].as_str(); + let timeout_ms = match args.get(2) { + None => DEFAULT_TIMEOUT_MS, + Some(raw) => match raw.parse::() { + Ok(0) => INFINITE, + Ok(value) => value, + Err(_) => { + log(&format!("bad args: unparseable timeout {raw:?}")); + return Stage::BadArgs; + } + }, + }; + + if !std::path::Path::new(hook_path).exists() { + log(&format!("hook DLL missing: {hook_path}")); + return Stage::HookMissing; + } + + log(&format!( + "injecting (pid={target_pid}, hook={hook_path}, timeout_ms={timeout_ms}, \ + helper_bits={})", + usize::BITS + )); + + inject(target_pid, hook_path, timeout_ms) + } + + fn inject(target_pid: u32, hook_path: &str, timeout_ms: u32) -> Stage { + let wide_path = to_wide(hook_path); + let path_bytes = wide_path.len() * std::mem::size_of::(); + + unsafe { + let process = OpenProcess( + PROCESS_CREATE_THREAD + | PROCESS_VM_OPERATION + | PROCESS_VM_WRITE + | PROCESS_VM_READ + | PROCESS_QUERY_INFORMATION, + 0, + target_pid, + ); + if process.is_null() { + return fail(Stage::OpenProcess, "OpenProcess returned null"); + } + let process = OwnedHandle(process); + + let remote_path = VirtualAllocEx( + process.raw(), + null(), + path_bytes, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE, + ); + if remote_path.is_null() { + return fail(Stage::Alloc, "VirtualAllocEx returned null"); + } + + let mut written: usize = 0; + let write_ok = WriteProcessMemory( + process.raw(), + remote_path, + wide_path.as_ptr().cast(), + path_bytes, + &mut written, + ) != 0; + if !write_ok || written != path_bytes { + let stage = fail(Stage::Write, "WriteProcessMemory failed/short"); + VirtualFreeEx(process.raw(), remote_path, 0, MEM_RELEASE); + return stage; + } + + let kernel32_name = to_wide("kernel32.dll"); + let kernel32 = GetModuleHandleW(kernel32_name.as_ptr()); + if kernel32.is_null() { + let stage = fail(Stage::Kernel32, "GetModuleHandleW(kernel32.dll)"); + VirtualFreeEx(process.raw(), remote_path, 0, MEM_RELEASE); + return stage; + } + let load_library = GetProcAddress(kernel32, c"LoadLibraryW".as_ptr().cast()); + let Some(load_library) = load_library else { + let stage = fail(Stage::LoadLibraryAddr, "GetProcAddress(LoadLibraryW)"); + VirtualFreeEx(process.raw(), remote_path, 0, MEM_RELEASE); + return stage; + }; + let start_routine: unsafe extern "system" fn(*mut c_void) -> u32 = + std::mem::transmute(load_library); + + let thread = CreateRemoteThread( + process.raw(), + null(), + 0, + Some(start_routine), + remote_path, + 0, + null_mut(), + ); + if thread.is_null() { + let stage = fail(Stage::CreateThread, "CreateRemoteThread returned null"); + VirtualFreeEx(process.raw(), remote_path, 0, MEM_RELEASE); + return stage; + } + let thread = OwnedHandle(thread); + + let wait = WaitForSingleObject(thread.raw(), timeout_ms); + if wait != WAIT_OBJECT_0 && wait != WAIT_ABANDONED { + let stage = fail(Stage::WaitTimeout, "WaitForSingleObject did not signal"); + VirtualFreeEx(process.raw(), remote_path, 0, MEM_RELEASE); + return stage; + } + + let mut exit_code: u32 = 0; + let got_exit = GetExitCodeThread(thread.raw(), &mut exit_code) != 0; + VirtualFreeEx(process.raw(), remote_path, 0, MEM_RELEASE); + + if !got_exit { + return fail(Stage::LoadLibraryFailed, "GetExitCodeThread failed"); + } + if exit_code == 0 { + log("remote LoadLibraryW returned NULL -- DLL failed to load in target"); + return Stage::LoadLibraryFailed; + } + + log(&format!( + "injection succeeded (remote LoadLibraryW HMODULE low bits={exit_code:#010x})" + )); + Stage::Success + } + } +} diff --git a/fluxer_desktop/native/win-game-capture/loader-diagnostics.cjs b/fluxer_desktop/native/win-game-capture/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/win-game-capture/package.json b/fluxer_desktop/native/win-game-capture/package.json new file mode 100644 index 0000000..d6c8b54 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/package.json @@ -0,0 +1,41 @@ +{ + "name": "@fluxer/win-game-capture", + "version": "0.0.0", + "description": "Native Windows game capture fallback for fullscreen games that display capture cannot capture", + "private": true, + "license": "AGPL-3.0-or-later", + "os": [ + "win32" + ], + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "win-game-capture.win32-x64-msvc.node", + "win-game-capture.win32-arm64-msvc.node", + "fluxer-game-hook.win32-x64-msvc.dll", + "fluxer-game-hook.win32-ia32-msvc.dll", + "fluxer-game-hook.win32-arm64-msvc.dll", + "fluxer-inject-helper.win32-x64-msvc.exe", + "fluxer-inject-helper.win32-ia32-msvc.exe", + "fluxer-inject-helper.win32-arm64-msvc.exe", + "fluxer-vulkan-layer.win32-x64-msvc.dll", + "fluxer-vulkan-layer.win32-x64-msvc.json", + "fluxer-vulkan-layer.win32-ia32-msvc.dll", + "fluxer-vulkan-layer.win32-ia32-msvc.json", + "fluxer-vulkan-layer.win32-arm64-msvc.dll", + "fluxer-vulkan-layer.win32-arm64-msvc.json" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "cargo test --manifest-path Cargo.toml && node --test index.test.mjs", + "test:fixtures": "node scripts/fixture-smoke.mjs", + "test:loader": "node --test index.test.mjs" + } +} diff --git a/fluxer_desktop/native/win-game-capture/scripts/fixture-smoke.mjs b/fluxer_desktop/native/win-game-capture/scripts/fixture-smoke.mjs new file mode 100644 index 0000000..edf2ea5 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/scripts/fixture-smoke.mjs @@ -0,0 +1,377 @@ +#!/usr/bin/env node +// SPDX-License-Identifier: AGPL-3.0-or-later + +import {spawn} from 'node:child_process'; +import {existsSync} from 'node:fs'; +import {createRequire} from 'node:module'; +import {dirname, join} from 'node:path'; +import {setTimeout as delay} from 'node:timers/promises'; +import {fileURLToPath} from 'node:url'; + +const require = createRequire(import.meta.url); +const capture = require('../index.js'); + +const ROOT = dirname(dirname(fileURLToPath(import.meta.url))); +const DEFAULT_FIXTURES = ['d3d11-present-fixture']; +const ALL_FIXTURES = [ + 'd3d9-present-fixture', + 'd3d10-present-fixture', + 'd3d11-present-fixture', + 'd3d12-present-fixture', + 'opengl-swapbuffers-fixture', + 'vulkan-present-fixture', +]; +const WIDTH = Number.parseInt(process.env.FLUXER_WIN_GAME_CAPTURE_FIXTURE_WIDTH ?? '640', 10); +const HEIGHT = Number.parseInt(process.env.FLUXER_WIN_GAME_CAPTURE_FIXTURE_HEIGHT ?? '360', 10); +const FRAME_RATE = Number.parseInt(process.env.FLUXER_WIN_GAME_CAPTURE_FIXTURE_FPS ?? '30', 10); +const START_TIMEOUT_MS = Number.parseInt(process.env.FLUXER_WIN_GAME_CAPTURE_FIXTURE_START_TIMEOUT_MS ?? '15000', 10); +const FRAME_TIMEOUT_MS = Number.parseInt(process.env.FLUXER_WIN_GAME_CAPTURE_FIXTURE_FRAME_TIMEOUT_MS ?? '15000', 10); + +const TRANSPORT_MEMORY = 0; +const TRANSPORT_SHARED_TEXTURE = 1; + +const API_OPENGL = 1; +const API_D3D9 = 3; +const API_D3D10 = 4; +const API_D3D11 = 5; +const API_D3D12 = 6; +const API_VULKAN = 7; + +const FALLBACK_NONE = 0; +const FALLBACK_SHARED_TEXTURE_UNSUPPORTED = 1; + +const EXPECTED_DIAGNOSTICS = { + 'd3d9-present-fixture': { + apiType: API_D3D9, + transport: TRANSPORT_SHARED_TEXTURE, + fallbackReason: FALLBACK_NONE, + requiresDxgiFormat: true, + }, + 'd3d10-present-fixture': { + apiType: API_D3D10, + transport: TRANSPORT_SHARED_TEXTURE, + fallbackReason: FALLBACK_NONE, + requiresDxgiFormat: true, + }, + 'd3d11-present-fixture': { + apiType: API_D3D11, + transport: TRANSPORT_SHARED_TEXTURE, + fallbackReason: FALLBACK_NONE, + requiresDxgiFormat: true, + }, + 'd3d12-present-fixture': { + apiType: API_D3D12, + transport: TRANSPORT_SHARED_TEXTURE, + fallbackReason: FALLBACK_NONE, + requiresDxgiFormat: true, + }, + 'opengl-swapbuffers-fixture': { + apiType: API_OPENGL, + transportOneOf: [TRANSPORT_SHARED_TEXTURE, TRANSPORT_MEMORY], + fallbackReasonOneOf: [FALLBACK_NONE, FALLBACK_SHARED_TEXTURE_UNSUPPORTED], + requiresDxgiFormatWhenShared: true, + }, + 'vulkan-present-fixture': { + apiType: API_VULKAN, + transport: TRANSPORT_SHARED_TEXTURE, + fallbackReason: FALLBACK_NONE, + requiresDxgiFormat: true, + }, + 'i686-present-fixture': { + apiType: API_D3D11, + transport: TRANSPORT_SHARED_TEXTURE, + fallbackReason: FALLBACK_NONE, + requiresDxgiFormat: true, + }, +}; + +function envFlag(name) { + return /^(1|true|yes|on)$/i.test(process.env[name] ?? ''); +} + +function selectedFixtures() { + const raw = process.argv.slice(2).join(',') || process.env.FLUXER_WIN_GAME_CAPTURE_FIXTURES || ''; + if (!raw.trim()) return DEFAULT_FIXTURES; + const names = raw + .split(',') + .map((name) => name.trim()) + .filter(Boolean); + return names.flatMap((name) => (name === 'all' ? ALL_FIXTURES : [name])); +} + +function fixtureTarget(fixture) { + return fixture === 'i686-present-fixture' ? 'i686-pc-windows-msvc' : null; +} + +function fixtureExePath(fixture) { + const target = fixtureTarget(fixture); + const targetDir = target + ? join(ROOT, 'test-apps', fixture, 'target', target, 'release') + : join(ROOT, 'test-apps', fixture, 'target', 'release'); + return join(targetDir, `${fixture}.exe`); +} + +function envKeyForFixture(fixture) { + return `FLUXER_WIN_GAME_CAPTURE_FIXTURE_ARGS_${fixture.toUpperCase().replaceAll(/[^A-Z0-9]+/g, '_')}`; +} + +function fixtureEnvValue(baseName, fixture) { + const fixtureKey = `${baseName}_${fixture.toUpperCase().replaceAll(/[^A-Z0-9]+/g, '_')}`; + return process.env[fixtureKey] ?? process.env[baseName]; +} + +function splitExtraArgs(raw) { + return (raw ?? '') + .split(/\s+/) + .map((arg) => arg.trim()) + .filter(Boolean); +} + +function extraFixtureArgs(fixture) { + return [ + ...splitExtraArgs(process.env.FLUXER_WIN_GAME_CAPTURE_FIXTURE_ARGS), + ...splitExtraArgs(process.env[envKeyForFixture(fixture)]), + ]; +} + +function diagnosticOverride(baseName, fixture) { + const raw = fixtureEnvValue(baseName, fixture); + if (raw === undefined || raw === '') return undefined; + const value = Number.parseInt(raw, 10); + if (!Number.isFinite(value)) throw new Error(`Invalid ${baseName} override: ${raw}`); + return value; +} + +function runCommand(command, args, options = {}) { + return new Promise((resolve, reject) => { + const child = spawn(command, args, { + cwd: options.cwd ?? ROOT, + env: process.env, + stdio: options.stdio ?? ['ignore', 'pipe', 'pipe'], + windowsHide: false, + }); + let stdout = ''; + let stderr = ''; + child.stdout?.on('data', (chunk) => { + stdout += chunk.toString(); + if (options.echo) process.stdout.write(chunk); + }); + child.stderr?.on('data', (chunk) => { + stderr += chunk.toString(); + if (options.echo) process.stderr.write(chunk); + }); + child.once('error', reject); + child.once('exit', (code, signal) => { + if (code === 0) { + resolve({stdout, stderr}); + return; + } + reject(new Error(`${command} ${args.join(' ')} failed with ${signal ?? code}\n${stderr || stdout}`)); + }); + }); +} + +async function buildFixture(fixture) { + const manifest = join(ROOT, 'test-apps', fixture, 'Cargo.toml'); + if (!existsSync(manifest)) throw new Error(`unknown fixture: ${fixture}`); + const exe = fixtureExePath(fixture); + if (envFlag('FLUXER_WIN_GAME_CAPTURE_FIXTURE_SKIP_BUILD') && existsSync(exe)) { + console.log(`[fixture-smoke] using existing ${fixture}`); + return exe; + } + const args = ['build', '--release', '--manifest-path', manifest]; + const target = fixtureTarget(fixture); + if (target) args.push('--target', target); + console.log(`[fixture-smoke] building ${fixture}`); + await runCommand('cargo', args, {echo: envFlag('FLUXER_WIN_GAME_CAPTURE_FIXTURE_VERBOSE')}); + if (!existsSync(exe)) throw new Error(`fixture build did not produce ${exe}`); + return exe; +} + +function waitForHwnd(child, fixture) { + return new Promise((resolve, reject) => { + let stdout = ''; + let stderr = ''; + const timeout = setTimeout(() => { + reject(new Error(`${fixture} did not print HWND within ${START_TIMEOUT_MS}ms\n${stderr || stdout}`)); + }, START_TIMEOUT_MS); + const finish = (hwnd) => { + clearTimeout(timeout); + resolve(hwnd); + }; + child.stdout.on('data', (chunk) => { + const text = chunk.toString(); + stdout += text; + const match = stdout.match(/HWND=(\d+)/); + if (match) finish(match[1]); + }); + child.stderr.on('data', (chunk) => { + stderr += chunk.toString(); + if (envFlag('FLUXER_WIN_GAME_CAPTURE_FIXTURE_VERBOSE')) process.stderr.write(chunk); + }); + child.once('exit', (code, signal) => { + clearTimeout(timeout); + reject(new Error(`${fixture} exited before capture started (${signal ?? code})\n${stderr || stdout}`)); + }); + }); +} + +function frameSignature(frame) { + const bytes = frame.data; + const width = Math.max(1, frame.width); + const height = Math.max(1, frame.height); + const stride = Math.max(1, frame.strideY || (frame.format === 'bgra' ? width * 4 : width)); + const rows = frame.format === 'bgra' ? Math.min(height, 12) : Math.min(height, 32); + let hash = 2166136261; + for (let y = 0; y < rows; y += 1) { + const row = y * stride; + const rowBytes = frame.format === 'bgra' ? Math.min(stride, width * 4, 256) : Math.min(stride, width, 256); + for (let x = 0; x < rowBytes; x += 4) { + hash ^= bytes[row + x] ?? 0; + hash = Math.imul(hash, 16777619) >>> 0; + } + } + return hash >>> 0; +} + +async function waitForAdvancingFrames(screenCapture, fixture) { + const signatures = new Set(); + let frameCount = 0; + let lastDiagnostics = null; + const onFrame = (frame) => { + frameCount += 1; + signatures.add(frameSignature(frame)); + lastDiagnostics = screenCapture.getDiagnostics?.() ?? lastDiagnostics; + }; + screenCapture.on('frame', onFrame); + const start = Date.now(); + try { + while (Date.now() - start < FRAME_TIMEOUT_MS) { + lastDiagnostics = screenCapture.getDiagnostics?.() ?? lastDiagnostics; + const nativeFrames = Number(lastDiagnostics?.frameCounter ?? 0); + if (frameCount >= 3 && (signatures.size >= 2 || nativeFrames >= 3)) { + return {frameCount, signatures: signatures.size, diagnostics: lastDiagnostics}; + } + await delay(100); + } + throw new Error( + `${fixture} capture did not deliver advancing frames within ${FRAME_TIMEOUT_MS}ms (frames=${frameCount}, signatures=${signatures.size}, diagnostics=${JSON.stringify(lastDiagnostics)})`, + ); + } finally { + screenCapture.off('frame', onFrame); + } +} + +function assertEqualDiagnostic(fixture, diagnostics, key, expected) { + if (diagnostics?.[key] !== expected) { + throw new Error(`${fixture} expected diagnostics.${key}=${expected}, got ${JSON.stringify(diagnostics)}`); + } +} + +function assertOneOfDiagnostic(fixture, diagnostics, key, expected) { + if (!expected.includes(diagnostics?.[key])) { + throw new Error( + `${fixture} expected diagnostics.${key} in [${expected.join(', ')}], got ${JSON.stringify(diagnostics)}`, + ); + } +} + +function assertFixtureDiagnostics(fixture, diagnostics) { + const expectedBase = EXPECTED_DIAGNOSTICS[fixture]; + if (!expectedBase) return; + const expected = {...expectedBase}; + const apiOverride = diagnosticOverride('FLUXER_WIN_GAME_CAPTURE_EXPECT_API_TYPE', fixture); + const transportOverride = diagnosticOverride('FLUXER_WIN_GAME_CAPTURE_EXPECT_TRANSPORT', fixture); + const fallbackOverride = diagnosticOverride('FLUXER_WIN_GAME_CAPTURE_EXPECT_FALLBACK_REASON', fixture); + if (apiOverride !== undefined) expected.apiType = apiOverride; + if (transportOverride !== undefined) { + expected.transport = transportOverride; + delete expected.transportOneOf; + expected.requiresDxgiFormat = expected.transport === TRANSPORT_SHARED_TEXTURE; + } + if (fallbackOverride !== undefined) { + expected.fallbackReason = fallbackOverride; + delete expected.fallbackReasonOneOf; + } + + assertEqualDiagnostic(fixture, diagnostics, 'apiType', expected.apiType); + if (expected.transportOneOf) { + assertOneOfDiagnostic(fixture, diagnostics, 'transport', expected.transportOneOf); + } else { + assertEqualDiagnostic(fixture, diagnostics, 'transport', expected.transport); + } + if (expected.fallbackReason !== undefined) { + assertEqualDiagnostic(fixture, diagnostics, 'fallbackReason', expected.fallbackReason); + } else if (expected.fallbackReasonOneOf) { + assertOneOfDiagnostic(fixture, diagnostics, 'fallbackReason', expected.fallbackReasonOneOf); + } + if (diagnostics?.activeStrategy !== 'game-hook') { + throw new Error(`${fixture} expected activeStrategy=game-hook, got ${JSON.stringify(diagnostics)}`); + } + if ( + (expected.requiresDxgiFormat || + (expected.requiresDxgiFormatWhenShared && diagnostics?.transport === TRANSPORT_SHARED_TEXTURE)) && + Number(diagnostics?.dxgiFormat ?? 0) === 0 + ) { + throw new Error(`${fixture} expected a non-zero shared texture DXGI format, got ${JSON.stringify(diagnostics)}`); + } +} + +async function runFixture(fixture) { + const exe = await buildFixture(fixture); + const args = ['--frames', '900', '--width', String(WIDTH), '--height', String(HEIGHT), '--windowed']; + if (fixture === 'vulkan-present-fixture') args.push('--resize-at', '120'); + args.push(...extraFixtureArgs(fixture)); + const child = spawn(exe, args, { + cwd: dirname(exe), + stdio: ['ignore', 'pipe', 'pipe'], + windowsHide: false, + }); + let started = false; + try { + const hwnd = await waitForHwnd(child, fixture); + const screenCapture = new capture.ScreenCapture({ + sourceId: `window:${hwnd}:0`, + sourceKind: 'game', + width: WIDTH, + height: HEIGHT, + frameRate: FRAME_RATE, + injectionMethod: process.env.FLUXER_WIN_GAME_CAPTURE_INJECTION_METHOD || 'auto', + }); + let result; + try { + result = await screenCapture.start(); + started = true; + const observed = await waitForAdvancingFrames(screenCapture, fixture); + assertFixtureDiagnostics(fixture, observed.diagnostics); + console.log( + `[fixture-smoke] PASS ${fixture}: start=${JSON.stringify(result)} frames=${observed.frameCount} signatures=${observed.signatures} diagnostics=${JSON.stringify(observed.diagnostics)}`, + ); + } finally { + if (started) await screenCapture.stop().catch(() => {}); + } + } finally { + if (!child.killed) child.kill(); + } +} + +async function main() { + if (process.platform !== 'win32') { + console.log('[fixture-smoke] SKIP: Windows game-capture fixtures only run on Windows'); + return 0; + } + if (!capture.isSupported()) { + throw new Error(`win-game-capture binding unavailable: ${capture.loadError?.message ?? 'unknown error'}`); + } + for (const fixture of selectedFixtures()) { + await runFixture(fixture); + } + return 0; +} + +try { + process.exitCode = await main(); +} catch (error) { + console.error(`[fixture-smoke] FAIL: ${error.stack ?? error.message}`); + process.exitCode = 1; +} diff --git a/fluxer_desktop/native/win-game-capture/src/compatibility.rs b/fluxer_desktop/native/win-game-capture/src/compatibility.rs new file mode 100644 index 0000000..a4bcf98 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/src/compatibility.rs @@ -0,0 +1,610 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![allow(dead_code)] + +#[cfg(target_os = "windows")] +use std::path::Path; +use std::path::PathBuf; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum InjectionPolicy { + Allow, + ForceCpuReadback, + Deny(String), +} + +const HARD_DENY_PROCESS_NAMES: &[(&str, &str)] = &[ + ("easyanticheat.exe", "EasyAntiCheat"), + ("easyanticheat_eos.exe", "EasyAntiCheat"), + ("easyanticheat_launcher.exe", "EasyAntiCheat"), + ("eac.exe", "EasyAntiCheat"), + ("eac_launcher.exe", "EasyAntiCheat"), + ("beservice.exe", "BattlEye"), + ("beservice_x64.exe", "BattlEye"), + ("bedaisy.exe", "BattlEye"), + ("be_service.exe", "BattlEye"), + ("vgc.exe", "Riot Vanguard"), + ("vgk.exe", "Riot Vanguard"), + ("vgtray.exe", "Riot Vanguard"), + ("vanguard.exe", "Riot Vanguard"), + ("destiny2.exe", "Destiny 2 anti-cheat policy"), + ("equ8.exe", "EQU8 anti-cheat"), + ("equ8_service.exe", "EQU8 anti-cheat"), + ("gameguard.des", "nProtect GameGuard"), + ("gamemon.des", "nProtect GameGuard"), + ("gamemon64.des", "nProtect GameGuard"), + ("npggnt.des", "nProtect GameGuard"), + ("xigncode.exe", "XIGNCODE"), + ("xigncode3.exe", "XIGNCODE3"), + ("mhyprot.exe", "mhyprot anti-cheat"), + ("mhyprot2.exe", "mhyprot anti-cheat"), + ("anticheatexpert.exe", "Anti-Cheat Expert"), + ("ace-base.exe", "Anti-Cheat Expert"), + ("faceit.exe", "FACEIT Anti-cheat"), + ("faceitclient.exe", "FACEIT Anti-cheat"), + ("faceitservice.exe", "FACEIT Anti-cheat"), + ("esea.exe", "ESEA Anti-cheat"), + ("eseaclient.exe", "ESEA Anti-cheat"), + ("eseaservice.exe", "ESEA Anti-cheat"), + ("punkbuster.exe", "PunkBuster"), + ("pnkbstra.exe", "PunkBuster"), + ("pnkbstrb.exe", "PunkBuster"), + ("system", "Windows kernel process"), + ("csrss.exe", "Windows system process"), + ("smss.exe", "Windows system process"), + ("wininit.exe", "Windows system process"), + ("winlogon.exe", "Windows system process"), + ("services.exe", "Windows system process"), + ("svchost.exe", "Windows service host"), + ("dwm.exe", "Windows compositor"), + ("fontdrvhost.exe", "Windows font driver host"), + ("logonui.exe", "Windows secure desktop"), + ("consent.exe", "Windows secure desktop"), + ("secureuxhost.exe", "Windows secure desktop"), + ("lsass.exe", "Windows security process"), + ("lsaiso.exe", "Windows security process"), + ("msmpeng.exe", "Microsoft Defender"), + ("securityhealthservice.exe", "Windows Security"), + ("securityhealthsystray.exe", "Windows Security"), + ("audiodg.exe", "Windows protected audio graph"), + ("wudfhost.exe", "Windows driver host"), + ("taskhostw.exe", "Windows task host"), + ("dllhost.exe", "Windows COM surrogate"), + ("runtimebroker.exe", "Windows runtime broker"), + ("applicationframehost.exe", "Windows application frame host"), + ("lockapp.exe", "Windows lock screen"), + ("sihost.exe", "Windows shell infrastructure"), + ("startmenuexperiencehost.exe", "Windows shell"), + ("searchhost.exe", "Windows shell"), + ("searchapp.exe", "Windows shell"), + ("textinputhost.exe", "Windows shell"), + ("explorer.exe", "Windows shell"), + ("taskmgr.exe", "Windows administrative tool"), + ("regedit.exe", "Windows administrative tool"), + ("mmc.exe", "Windows administrative tool"), + ("obs32.exe", "capture application"), + ("obs64.exe", "capture application"), + ("fluxer.exe", "Fluxer application"), + ("fluxer-desktop.exe", "Fluxer application"), + ("fluxer_desktop.exe", "Fluxer application"), +]; + +const COMPATIBILITY_DENY_PROCESS_NAMES: &[(&str, &str)] = &[ + ("gta-sa.exe", "legacy D3D8/RenderWare compatibility"), + ("samp.exe", "legacy D3D8/RenderWare compatibility"), + ("leagueclientux.exe", "League of Legends launcher"), + ("steamwebhelper.exe", "Chromium-based launcher"), + ("epicgameslauncher.exe", "Chromium-based launcher"), + ("riotclientux.exe", "Riot client"), + ("riotclientservices.exe", "Riot client"), + ("battle.net.exe", "Chromium-based launcher"), + ("gamingservices.exe", "Xbox Gaming Services"), + ("gamingservicesnet.exe", "Xbox Gaming Services"), +]; + +const COMPATIBILITY_DENY_WINDOW_CLASSES: &[(&str, &str)] = &[ + ("chrome_widgetwin_0", "Chromium-based game window"), + ("chrome_widgetwin_1", "Chromium-based game window"), + ( + "gamingservicesui_hosting_window_class", + "Xbox Gaming Services", + ), +]; + +const FORCE_CPU_PROCESS_NAMES: &[&str] = &["terraria.exe"]; + +const OVERRIDE_FILE_NAME: &str = "compatibility.json"; + +pub fn injection_policy(target_pid: u32) -> InjectionPolicy { + let exe_name = match target_process_exe_name(target_pid) { + Some(name) => name, + None => { + return InjectionPolicy::Allow; + } + }; + evaluate_policy(&exe_name, None, load_override()) +} + +#[cfg(target_os = "windows")] +pub fn injection_policy_for_window( + target_pid: u32, + hwnd: windows_sys::Win32::Foundation::HWND, +) -> InjectionPolicy { + let exe_name = match target_process_exe_name(target_pid) { + Some(name) => name, + None => { + return InjectionPolicy::Allow; + } + }; + evaluate_policy( + &exe_name, + target_window_class_name(hwnd).as_deref(), + load_override(), + ) +} + +fn evaluate_policy( + exe_name: &str, + window_class: Option<&str>, + override_lists: Option, +) -> InjectionPolicy { + let exe_name_lower = file_name_lower(exe_name); + let exe_name_lower = exe_name_lower.as_str(); + let window_class_lower = window_class.map(|name| name.trim().to_ascii_lowercase()); + let window_class_lower = window_class_lower.as_deref(); + + if let Some(lists) = override_lists.as_ref() { + if let Some(reason) = embedded_hard_deny_reason(exe_name_lower) { + return reason; + } + if lists.deny.iter().any(|name| name == exe_name_lower) { + return InjectionPolicy::Deny(format!( + "{exe_name_lower} is on the local compatibility deny list; Fluxer will not inject \ + its game-capture hook" + )); + } + let allowed_by_override = lists.allow.iter().any(|name| name == exe_name_lower); + if !allowed_by_override + && let Some(reason) = embedded_compatibility_deny_reason(exe_name_lower) + { + return reason; + } + if !allowed_by_override + && let Some(reason) = embedded_window_class_deny_reason(window_class_lower) + { + return reason; + } + if lists.force_cpu.iter().any(|name| name == exe_name_lower) { + return InjectionPolicy::ForceCpuReadback; + } + if allowed_by_override { + return InjectionPolicy::Allow; + } + } else { + if let Some(reason) = embedded_hard_deny_reason(exe_name_lower) { + return reason; + } + if let Some(reason) = embedded_compatibility_deny_reason(exe_name_lower) { + return reason; + } + if let Some(reason) = embedded_window_class_deny_reason(window_class_lower) { + return reason; + } + } + + if FORCE_CPU_PROCESS_NAMES.contains(&exe_name_lower) { + return InjectionPolicy::ForceCpuReadback; + } + InjectionPolicy::Allow +} + +fn embedded_hard_deny_reason(exe_name_lower: &str) -> Option { + HARD_DENY_PROCESS_NAMES + .iter() + .find(|(name, _)| *name == exe_name_lower) + .map(|(_, label)| { + InjectionPolicy::Deny(format!( + "{exe_name_lower} is protected by {label}; Fluxer will not inject its game-capture \ + hook into anti-cheat or security-sensitive processes" + )) + }) +} + +fn embedded_compatibility_deny_reason(exe_name_lower: &str) -> Option { + COMPATIBILITY_DENY_PROCESS_NAMES + .iter() + .find(|(name, _)| *name == exe_name_lower) + .map(|(_, label)| { + InjectionPolicy::Deny(format!( + "{exe_name_lower} has known game-capture compatibility issues ({label}); Fluxer \ + will not inject its game-capture hook by default" + )) + }) +} + +fn embedded_window_class_deny_reason(window_class_lower: Option<&str>) -> Option { + let window_class_lower = window_class_lower?; + COMPATIBILITY_DENY_WINDOW_CLASSES + .iter() + .find(|(name, _)| *name == window_class_lower) + .map(|(_, label)| { + InjectionPolicy::Deny(format!( + "window class {window_class_lower} has known OBS game-capture compatibility issues \ + ({label}); Fluxer will not inject its game-capture hook by default" + )) + }) +} + +#[derive(Debug, Default, Clone, PartialEq, Eq)] +struct OverrideLists { + deny: Vec, + allow: Vec, + force_cpu: Vec, +} + +impl OverrideLists { + fn is_empty(&self) -> bool { + self.deny.is_empty() && self.allow.is_empty() && self.force_cpu.is_empty() + } +} + +fn load_override() -> Option { + let path = override_file_path()?; + let contents = std::fs::read_to_string(&path).ok()?; + let lists = parse_override_json(&contents); + if lists.is_empty() { None } else { Some(lists) } +} + +fn override_file_path() -> Option { + if let Some(dir) = addon_directory() { + let candidate = dir.join(OVERRIDE_FILE_NAME); + if candidate.is_file() { + return Some(candidate); + } + } + let exe_dir = std::env::current_exe().ok()?.parent()?.to_path_buf(); + let candidate = exe_dir.join(OVERRIDE_FILE_NAME); + if candidate.is_file() { + Some(candidate) + } else { + None + } +} + +#[cfg(target_os = "windows")] +fn addon_directory() -> Option { + use std::os::windows::ffi::OsStringExt; + use windows_sys::Win32::Foundation::HMODULE; + use windows_sys::Win32::System::LibraryLoader::{ + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + GetModuleFileNameW, GetModuleHandleExW, + }; + + let mut module: HMODULE = std::ptr::null_mut(); + let ok = unsafe { + GetModuleHandleExW( + GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + addon_directory as *const u16, + &mut module, + ) + }; + if ok == 0 || module.is_null() { + return None; + } + let mut buffer = vec![0u16; 1024]; + let len = unsafe { GetModuleFileNameW(module, buffer.as_mut_ptr(), buffer.len() as u32) }; + if len == 0 || len as usize >= buffer.len() { + return None; + } + buffer.truncate(len as usize); + let module_path = PathBuf::from(std::ffi::OsString::from_wide(&buffer)); + module_path.parent().map(Path::to_path_buf) +} + +#[cfg(not(target_os = "windows"))] +fn addon_directory() -> Option { + None +} + +#[cfg(target_os = "windows")] +fn target_process_exe_name(pid: u32) -> Option { + use windows_sys::Win32::Foundation::CloseHandle; + use windows_sys::Win32::System::Threading::{ + OpenProcess, PROCESS_NAME_WIN32, PROCESS_QUERY_LIMITED_INFORMATION, + QueryFullProcessImageNameW, + }; + + if pid == 0 { + return None; + } + let handle = unsafe { OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid) }; + if handle.is_null() { + return None; + } + let mut buffer = vec![0u16; 1024]; + let mut size = buffer.len() as u32; + let ok = unsafe { + QueryFullProcessImageNameW(handle, PROCESS_NAME_WIN32, buffer.as_mut_ptr(), &mut size) + }; + unsafe { + CloseHandle(handle); + } + if ok == 0 || size == 0 || size as usize > buffer.len() { + return None; + } + let full_path: String = String::from_utf16_lossy(&buffer[..size as usize]); + Some(file_name_lower(&full_path)) +} + +#[cfg(target_os = "windows")] +fn target_window_class_name(hwnd: windows_sys::Win32::Foundation::HWND) -> Option { + use windows_sys::Win32::UI::WindowsAndMessaging::GetClassNameW; + + if hwnd.is_null() { + return None; + } + let mut buffer = vec![0u16; 256]; + let len = unsafe { GetClassNameW(hwnd, buffer.as_mut_ptr(), buffer.len() as i32) }; + if len <= 0 { + return None; + } + buffer.truncate(len as usize); + Some( + String::from_utf16_lossy(&buffer) + .trim() + .to_ascii_lowercase(), + ) +} + +#[cfg(not(target_os = "windows"))] +fn target_process_exe_name(_pid: u32) -> Option { + None +} + +fn file_name_lower(path: &str) -> String { + path.rsplit(['\\', '/']) + .next() + .unwrap_or(path) + .trim() + .to_ascii_lowercase() +} + +fn parse_override_json(text: &str) -> OverrideLists { + let mut force_cpu = extract_string_array(text, "forceCpu"); + force_cpu.extend(extract_string_array(text, "force_cpu")); + OverrideLists { + deny: extract_string_array(text, "deny"), + allow: extract_string_array(text, "allow"), + force_cpu, + } +} + +fn extract_string_array(text: &str, key: &str) -> Vec { + let needle = format!("\"{key}\""); + let mut search_from = 0usize; + while let Some(rel) = text[search_from..].find(&needle) { + let key_pos = search_from + rel; + let after_key = key_pos + needle.len(); + let rest = text[after_key..].trim_start(); + if let Some(rest) = rest.strip_prefix(':') { + let rest = rest.trim_start(); + if let Some(array_body) = rest.strip_prefix('[') + && let Some(end) = array_body.find(']') + { + return parse_json_string_list(&array_body[..end]); + } + } + search_from = after_key; + } + Vec::new() +} + +fn parse_json_string_list(body: &str) -> Vec { + let mut out = Vec::new(); + let mut chars = body.char_indices().peekable(); + while let Some((_, ch)) = chars.next() { + if ch != '"' { + continue; + } + let mut value = String::new(); + let mut closed = false; + while let Some((_, c)) = chars.next() { + match c { + '\\' => { + if let Some((_, escaped)) = chars.next() { + match escaped { + 'n' => value.push('\n'), + 't' => value.push('\t'), + 'r' => value.push('\r'), + other => value.push(other), + } + } + } + '"' => { + closed = true; + break; + } + other => value.push(other), + } + } + if closed { + let normalised = file_name_lower(&value); + if !normalised.is_empty() { + out.push(normalised); + } + } + } + out +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn embedded_deny_matches_anticheat() { + match evaluate_policy("easyanticheat.exe", None, None) { + InjectionPolicy::Deny(reason) => assert!(reason.contains("EasyAntiCheat")), + other => panic!("expected Deny, got {other:?}"), + } + match evaluate_policy("beservice.exe", None, None) { + InjectionPolicy::Deny(reason) => assert!(reason.contains("BattlEye")), + other => panic!("expected Deny, got {other:?}"), + } + match evaluate_policy("vgc.exe", None, None) { + InjectionPolicy::Deny(reason) => assert!(reason.contains("Vanguard")), + other => panic!("expected Deny, got {other:?}"), + } + match evaluate_policy("destiny2.exe", None, None) { + InjectionPolicy::Deny(reason) => assert!(reason.contains("Destiny 2")), + other => panic!("expected Deny, got {other:?}"), + } + } + + #[test] + fn embedded_deny_matches_security_sensitive_windows_processes() { + for name in [ + "lsass.exe", + "dwm.exe", + "explorer.exe", + "applicationframehost.exe", + "obs64.exe", + "fluxer-desktop.exe", + ] { + match evaluate_policy(name, None, None) { + InjectionPolicy::Deny(reason) => { + assert!(reason.contains("security-sensitive") || reason.contains("protected")) + } + other => panic!("expected Deny for {name}, got {other:?}"), + } + } + } + + #[test] + fn unknown_process_is_allowed() { + assert_eq!( + evaluate_policy("mygame.exe", None, None), + InjectionPolicy::Allow + ); + } + + #[test] + fn override_deny_wins() { + let lists = parse_override_json(r#"{ "deny": ["MyGame.exe"] }"#); + match evaluate_policy("mygame.exe", None, Some(lists)) { + InjectionPolicy::Deny(_) => {} + other => panic!("expected Deny, got {other:?}"), + } + } + + #[test] + fn override_allow_cannot_unblock_hard_deny() { + let lists = parse_override_json(r#"{ "allow": ["easyanticheat.exe"] }"#); + match evaluate_policy("easyanticheat.exe", None, Some(lists)) { + InjectionPolicy::Deny(reason) => assert!(reason.contains("EasyAntiCheat")), + other => panic!("expected hard Deny, got {other:?}"), + } + } + + #[test] + fn override_allow_unblocks_soft_compatibility_deny() { + let lists = parse_override_json(r#"{ "allow": ["LeagueClientUx.exe"] }"#); + assert_eq!( + evaluate_policy("LeagueClientUx.exe", None, Some(lists)), + InjectionPolicy::Allow + ); + } + + #[test] + fn override_allow_plus_force_cpu_unblocks_soft_deny_with_cpu_readback() { + let lists = parse_override_json( + r#"{ "allow": ["LeagueClientUx.exe"], "forceCpu": ["LeagueClientUx.exe"] }"#, + ); + assert_eq!( + evaluate_policy("LeagueClientUx.exe", None, Some(lists)), + InjectionPolicy::ForceCpuReadback + ); + } + + #[test] + fn override_force_cpu_applies() { + let lists = parse_override_json(r#"{ "forceCpu": ["weird.exe"] }"#); + assert_eq!( + evaluate_policy("weird.exe", None, Some(lists)), + InjectionPolicy::ForceCpuReadback + ); + } + + #[test] + fn embedded_force_cpu_applies_for_known_cross_adapter_case() { + assert_eq!( + evaluate_policy("Terraria.exe", None, None), + InjectionPolicy::ForceCpuReadback + ); + } + + #[test] + fn local_force_cpu_cannot_override_hard_deny() { + let lists = parse_override_json(r#"{ "forceCpu": ["lsass.exe"] }"#); + match evaluate_policy("lsass.exe", None, Some(lists)) { + InjectionPolicy::Deny(reason) => assert!(reason.contains("security-sensitive")), + other => panic!("expected hard Deny, got {other:?}"), + } + } + + #[test] + fn force_cpu_snake_case_alias_parses() { + let lists = parse_override_json(r#"{ "force_cpu": ["weird.exe"] }"#); + assert!(lists.force_cpu.contains(&"weird.exe".to_string())); + } + + #[test] + fn embedded_window_class_deny_matches_obs_chromium_game_windows() { + match evaluate_policy("game.exe", Some("Chrome_WidgetWin_1"), None) { + InjectionPolicy::Deny(reason) => assert!(reason.contains("Chromium")), + other => panic!("expected Deny, got {other:?}"), + } + } + + #[test] + fn embedded_window_class_deny_matches_xbox_gaming_services() { + match evaluate_policy( + "game.exe", + Some("GamingServicesUI_Hosting_Window_Class"), + None, + ) { + InjectionPolicy::Deny(reason) => assert!(reason.contains("Xbox Gaming Services")), + other => panic!("expected Deny, got {other:?}"), + } + } + + #[test] + fn override_allow_unblocks_soft_window_class_deny() { + let lists = parse_override_json(r#"{ "allow": ["game.exe"] }"#); + assert_eq!( + evaluate_policy("game.exe", Some("Chrome_WidgetWin_0"), Some(lists)), + InjectionPolicy::Allow + ); + } + + #[test] + fn parser_normalises_paths_and_ignores_garbage() { + let lists = parse_override_json( + r#"{ "deny": ["C:\\Games\\Foo\\Foo.exe", "/opt/bar/Bar.EXE", 123, null] }"#, + ); + assert_eq!(lists.deny, vec!["foo.exe", "bar.exe"]); + } + + #[test] + fn malformed_json_yields_empty() { + let lists = parse_override_json("not json at all"); + assert!(lists.is_empty()); + } + + #[test] + fn file_name_lower_handles_both_separators() { + assert_eq!(file_name_lower("C:\\A\\B\\Game.EXE"), "game.exe"); + assert_eq!(file_name_lower("/a/b/Game.EXE"), "game.exe"); + assert_eq!(file_name_lower("bare.exe"), "bare.exe"); + } +} diff --git a/fluxer_desktop/native/win-game-capture/src/dxgi_capture.rs b/fluxer_desktop/native/win-game-capture/src/dxgi_capture.rs new file mode 100644 index 0000000..6a48350 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/src/dxgi_capture.rs @@ -0,0 +1,1105 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[cfg(target_os = "windows")] +use windows::{ + Win32::{ + Foundation::{HMODULE, HWND, RECT}, + Graphics::{ + Direct3D::D3D_DRIVER_TYPE_UNKNOWN, + Direct3D11::{ + D3D11_BOX, D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_RESOURCE_MISC_SHARED, + D3D11_SDK_VERSION, D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, D3D11CreateDevice, + ID3D11Device, ID3D11DeviceContext, ID3D11Resource, ID3D11Texture2D, + }, + Dxgi::{ + Common::{ + DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_MODE_ROTATION, DXGI_MODE_ROTATION_IDENTITY, + DXGI_MODE_ROTATION_UNSPECIFIED, DXGI_SAMPLE_DESC, + }, + CreateDXGIFactory1, DXGI_ERROR_ACCESS_LOST, DXGI_ERROR_MORE_DATA, + DXGI_ERROR_WAIT_TIMEOUT, DXGI_OUTDUPL_FRAME_INFO, DXGI_OUTDUPL_POINTER_SHAPE_INFO, + DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR, + DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR, + DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME, IDXGIAdapter, IDXGIAdapter1, + IDXGIDevice, IDXGIFactory1, IDXGIOutput, IDXGIOutput1, IDXGIOutputDuplication, + IDXGIResource, + }, + Gdi::{HMONITOR, MONITOR_DEFAULTTONULL, MonitorFromWindow}, + }, + UI::WindowsAndMessaging::{GetWindowRect, IsWindow}, + }, + core::Interface, +}; + +#[cfg(target_os = "windows")] +use crate::{ + CaptureInner, emit_lifecycle, emit_shared_texture_frame, note_media_frame_without_sink, + resolve_frame_sink, +}; + +#[cfg(target_os = "windows")] +use std::sync::{Arc, atomic::Ordering}; + +#[cfg(target_os = "windows")] +pub fn parse_window_source_id(source_id: &str, source_kind: &str) -> Option { + if source_kind != "window" { + return None; + } + let hwnd_str = source_id.strip_prefix("window:")?; + let hwnd_token = hwnd_str.split(':').next()?; + let hwnd_num = parse_hwnd_token(hwnd_token)?; + let hwnd = HWND(hwnd_num as *mut _); + if unsafe { IsWindow(Some(hwnd)) }.as_bool() { + Some(hwnd) + } else { + None + } +} + +#[cfg(target_os = "windows")] +fn parse_hwnd_token(token: &str) -> Option { + if let Some(hex) = token + .strip_prefix("0x") + .or_else(|| token.strip_prefix("0X")) + { + return isize::from_str_radix(hex, 16).ok(); + } + token.parse().ok() +} + +#[cfg(target_os = "windows")] +fn validate_output_rotation(rotation: DXGI_MODE_ROTATION) -> Result<(), String> { + if rotation.0 == DXGI_MODE_ROTATION_UNSPECIFIED.0 || rotation.0 == DXGI_MODE_ROTATION_IDENTITY.0 + { + return Ok(()); + } + Err(format!( + "Rotated DXGI outputs are not supported by this fallback (rotation={})", + rotation.0 + )) +} + +#[cfg(target_os = "windows")] +pub struct DxgiCaptureSession { + device: ID3D11Device, + context: ID3D11DeviceContext, + hwnd: HWND, + capture_width: u32, + capture_height: u32, + requested_width: Option, + requested_height: Option, +} + +#[cfg(target_os = "windows")] +unsafe impl Send for DxgiCaptureSession {} + +#[cfg(target_os = "windows")] +#[derive(Default)] +struct PointerShapeCache { + buffer: Vec, + info: Option, +} + +#[cfg(target_os = "windows")] +enum PointerShapeUpdate { + Ok, + AccessLost, + Error(String), +} + +#[cfg(target_os = "windows")] +struct DuplicationState { + duplication: IDXGIOutputDuplication, + monitor: HMONITOR, + monitor_rect: RECT, + shared_output: Option, + cap_w: u32, + cap_h: u32, + out_w: u32, + out_h: u32, +} + +#[cfg(target_os = "windows")] +pub(crate) const SHARED_OUTPUT_SLOT_COUNT: usize = 3; + +#[cfg(target_os = "windows")] +pub(crate) struct SharedOutputSlot { + pub(crate) texture: ID3D11Texture2D, + pub(crate) handle: u64, +} + +#[cfg(target_os = "windows")] +pub(crate) struct SharedTextureOutput { + pub(crate) slots: [SharedOutputSlot; SHARED_OUTPUT_SLOT_COUNT], + pub(crate) slot_cursor: usize, + pub(crate) width: u32, + pub(crate) height: u32, + pub(crate) dxgi_format: u32, +} + +#[cfg(target_os = "windows")] +impl SharedTextureOutput { + pub(crate) fn next_slot_index(&mut self) -> usize { + assert!( + self.slot_cursor < SHARED_OUTPUT_SLOT_COUNT, + "slot cursor in range" + ); + assert!(self.width > 0, "shared output width positive"); + let slot_index = self.slot_cursor; + self.slot_cursor = (slot_index + 1) % SHARED_OUTPUT_SLOT_COUNT; + slot_index + } +} + +#[cfg(target_os = "windows")] +impl DxgiCaptureSession { + pub fn new( + hwnd: HWND, + requested_width: Option, + requested_height: Option, + ) -> Result { + let adapter = find_dxgi_adapter_for_window(hwnd)?; + let (device, context) = create_d3d11_device(&adapter)?; + + let mut window_rect = RECT::default(); + unsafe { + GetWindowRect(hwnd, &mut window_rect).map_err(|e| format!("GetWindowRect: {e}"))?; + } + let width = (window_rect.right - window_rect.left).max(1) as u32; + let height = (window_rect.bottom - window_rect.top).max(1) as u32; + let (capture_width, capture_height) = + resolve_output_size(width, height, requested_width, requested_height); + + Ok(Self { + device, + context, + hwnd, + capture_width, + capture_height, + requested_width, + requested_height, + }) + } + + pub fn capture_width(&self) -> u32 { + self.capture_width + } + + pub fn capture_height(&self) -> u32 { + self.capture_height + } +} + +#[cfg(target_os = "windows")] +fn find_dxgi_adapter_for_window(hwnd: HWND) -> Result { + let target_monitor = unsafe { MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL) }; + if target_monitor.is_invalid() { + return Err("MonitorFromWindow returned null".into()); + } + let factory = unsafe { CreateDXGIFactory1::() } + .map_err(|e| format!("CreateDXGIFactory1: {e}"))?; + let mut adapter_index = 0u32; + loop { + let adapter = match unsafe { factory.EnumAdapters1(adapter_index) } { + Ok(adapter) => adapter, + Err(_) => break, + }; + adapter_index += 1; + let mut output_index = 0u32; + loop { + let output: IDXGIOutput = match unsafe { adapter.EnumOutputs(output_index) } { + Ok(output) => output, + Err(_) => break, + }; + output_index += 1; + let desc = unsafe { output.GetDesc().map_err(|e| format!("GetDesc: {e}"))? }; + if desc.Monitor == target_monitor { + validate_output_rotation(desc.Rotation)?; + return Ok(adapter); + } + } + } + Err("Could not find DXGI adapter for the target window's monitor".into()) +} + +#[cfg(target_os = "windows")] +fn create_d3d11_device( + adapter: &IDXGIAdapter1, +) -> Result<(ID3D11Device, ID3D11DeviceContext), String> { + let adapter: IDXGIAdapter = adapter + .cast() + .map_err(|e| format!("IDXGIAdapter cast: {e}"))?; + let mut device = None; + let mut context = None; + unsafe { + D3D11CreateDevice( + Some(&adapter), + D3D_DRIVER_TYPE_UNKNOWN, + HMODULE::default(), + D3D11_CREATE_DEVICE_BGRA_SUPPORT, + None, + D3D11_SDK_VERSION, + Some(&mut device), + None, + Some(&mut context), + ) + .map_err(|e| format!("D3D11CreateDevice: {e}"))?; + } + let device = device.ok_or("D3D11 device was None")?; + let context = context.ok_or("D3D11 context was None")?; + + if let Ok(dxgi_device) = device.cast::() { + let _ = unsafe { dxgi_device.SetGPUThreadPriority(7) }; + } + + Ok((device, context)) +} + +#[cfg(target_os = "windows")] +fn create_output_duplication( + device: &ID3D11Device, + hwnd: HWND, +) -> Result<(IDXGIOutputDuplication, RECT, HMONITOR), String> { + let dxgi_device: IDXGIDevice = device + .cast() + .map_err(|e| format!("IDXGIDevice cast: {e}"))?; + let adapter: IDXGIAdapter1 = unsafe { dxgi_device.GetAdapter() } + .map_err(|e| format!("GetAdapter: {e}"))? + .cast() + .map_err(|e| format!("IDXGIAdapter1 cast: {e}"))?; + + let target_monitor = unsafe { MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL) }; + if target_monitor.is_invalid() { + return Err("MonitorFromWindow returned null".into()); + } + + let mut output_index = 0u32; + loop { + let output: IDXGIOutput = match unsafe { adapter.EnumOutputs(output_index) } { + Ok(o) => o, + Err(_) => break, + }; + output_index += 1; + + let desc = unsafe { output.GetDesc().map_err(|e| format!("GetDesc: {e}"))? }; + + if desc.Monitor == target_monitor { + validate_output_rotation(desc.Rotation)?; + let output1: IDXGIOutput1 = output + .cast() + .map_err(|e| format!("IDXGIOutput1 cast: {e}"))?; + let duplication = unsafe { + output1 + .DuplicateOutput(device) + .map_err(|e| format!("DuplicateOutput: {e}"))? + }; + return Ok((duplication, desc.DesktopCoordinates, target_monitor)); + } + } + + Err("Could not find DXGI output for the target window's monitor".into()) +} + +#[cfg(target_os = "windows")] +pub(crate) fn create_shared_output_texture( + device: &ID3D11Device, + width: u32, + height: u32, +) -> Result { + assert!(width > 0, "shared output width positive"); + assert!(height > 0, "shared output height positive"); + let mut slots = Vec::with_capacity(SHARED_OUTPUT_SLOT_COUNT); + for _ in 0..SHARED_OUTPUT_SLOT_COUNT { + slots.push(create_shared_output_slot(device, width, height)?); + } + assert_eq!( + slots.len(), + SHARED_OUTPUT_SLOT_COUNT, + "all shared output slots created" + ); + let slots = <[SharedOutputSlot; SHARED_OUTPUT_SLOT_COUNT]>::try_from(slots) + .map_err(|_| "shared output slot count mismatch".to_string())?; + Ok(SharedTextureOutput { + slots, + slot_cursor: 0, + width, + height, + dxgi_format: DXGI_FORMAT_B8G8R8A8_UNORM.0 as u32, + }) +} + +#[cfg(target_os = "windows")] +fn create_shared_output_slot( + device: &ID3D11Device, + width: u32, + height: u32, +) -> Result { + assert!(width > 0, "shared output slot width positive"); + assert!(height > 0, "shared output slot height positive"); + let desc = D3D11_TEXTURE2D_DESC { + Width: width, + Height: height, + MipLevels: 1, + ArraySize: 1, + Format: DXGI_FORMAT_B8G8R8A8_UNORM, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Usage: D3D11_USAGE_DEFAULT, + BindFlags: Default::default(), + CPUAccessFlags: 0, + MiscFlags: D3D11_RESOURCE_MISC_SHARED.0 as u32, + }; + let mut texture = None; + let texture = unsafe { + device + .CreateTexture2D(&desc, None, Some(&mut texture)) + .map_err(|e| format!("CreateTexture2D shared output: {e}"))?; + texture.ok_or("D3D11 shared output texture was None")? + }; + let resource: IDXGIResource = texture + .cast() + .map_err(|e| format!("IDXGIResource shared output cast: {e}"))?; + let handle = unsafe { resource.GetSharedHandle() } + .map_err(|e| format!("GetSharedHandle shared output: {e}"))?; + if handle.is_invalid() { + return Err("GetSharedHandle shared output returned an invalid handle".into()); + } + Ok(SharedOutputSlot { + texture, + handle: handle.0 as usize as u64, + }) +} + +#[cfg(target_os = "windows")] +pub(crate) fn resolve_output_size( + src_width: u32, + src_height: u32, + requested_width: Option, + requested_height: Option, +) -> (u32, u32) { + let max_width = requested_width.unwrap_or(src_width).max(1); + let max_height = requested_height.unwrap_or(src_height).max(1); + let scale = (max_width as f64 / src_width.max(1) as f64) + .min(max_height as f64 / src_height.max(1) as f64) + .min(1.0); + if scale >= 1.0 { + return (src_width.max(1), src_height.max(1)); + } + ( + ((src_width as f64 * scale).floor() as u32).max(1), + ((src_height as f64 * scale).floor() as u32).max(1), + ) +} + +#[cfg(target_os = "windows")] +pub(crate) fn wall_clock_us() -> i64 { + use std::time::{SystemTime, UNIX_EPOCH}; + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|d| d.as_micros() as i64) + .unwrap_or(0) +} + +#[cfg(target_os = "windows")] +pub(crate) fn capture_timestamp_us(capture_start: std::time::Instant) -> i64 { + let elapsed_us = capture_start.elapsed().as_micros(); + assert!(elapsed_us < i64::MAX as u128); + elapsed_us as i64 +} + +pub(crate) fn pacing_sleep_and_next_deadline( + now: std::time::Instant, + deadline: std::time::Instant, + frame_interval: std::time::Duration, +) -> (std::time::Duration, std::time::Instant) { + assert!(frame_interval > std::time::Duration::ZERO); + if now < deadline { + let sleep_duration = deadline - now; + assert!(sleep_duration <= frame_interval); + (sleep_duration, deadline + frame_interval) + } else { + (std::time::Duration::ZERO, now + frame_interval) + } +} + +#[cfg(target_os = "windows")] +pub fn capture_loop(inner: &Arc, frame_interval: std::time::Duration) { + let hwnd; + let device; + let context; + let requested_width; + let requested_height; + { + let guard = inner.session.lock(); + let session = match guard.as_ref() { + Some(s) => s, + None => { + emit_lifecycle(inner, "closed-clean", "no session"); + return; + } + }; + hwnd = session.hwnd; + device = session.device.clone(); + context = session.context.clone(); + requested_width = session.requested_width; + requested_height = session.requested_height; + } + + let capture_id = inner.capture_id.lock().clone(); + let mut duplication_state: Option = None; + let mut pointer_shape = PointerShapeCache::default(); + let mut recreate_backoff = std::time::Duration::from_millis(100); + let capture_start = std::time::Instant::now(); + let mut next_frame_deadline = capture_start + frame_interval; + + while inner.running.load(Ordering::Acquire) { + if !unsafe { IsWindow(Some(hwnd)) }.as_bool() { + emit_lifecycle(inner, "closed", "window closed"); + break; + } + + if duplication_state.is_none() { + match setup_duplication(&device, hwnd, requested_width, requested_height) { + Ok(state) => { + duplication_state = Some(state); + pointer_shape = PointerShapeCache::default(); + } + Err(e) => { + if !inner.running.load(Ordering::Acquire) { + break; + } + emit_lifecycle( + inner, + "error", + &format!("Failed to create DXGI output duplication: {e}"), + ); + std::thread::sleep(recreate_backoff); + recreate_backoff = + (recreate_backoff * 2).min(std::time::Duration::from_secs(2)); + continue; + } + } + } + + let Some(state) = duplication_state.as_mut() else { + continue; + }; + + match acquire_and_emit_frame( + inner, + &context, + state, + hwnd, + capture_id.as_deref(), + &mut pointer_shape, + capture_start, + ) { + FrameResult::Ok => { + recreate_backoff = std::time::Duration::from_millis(100); + } + FrameResult::Timeout => {} + FrameResult::Resized { width, height } => { + state.cap_w = width; + state.cap_h = height; + let (out_w, out_h) = + resolve_output_size(width, height, requested_width, requested_height); + state.out_w = out_w; + state.out_h = out_h; + state.shared_output = create_shared_output_texture(&device, width, height).ok(); + } + FrameResult::AccessLost => { + duplication_state = None; + pointer_shape = PointerShapeCache::default(); + std::thread::sleep(recreate_backoff); + recreate_backoff = (recreate_backoff * 2).min(std::time::Duration::from_secs(2)); + continue; + } + FrameResult::WindowGone => { + emit_lifecycle(inner, "closed", "window closed during capture"); + break; + } + FrameResult::Error(e) => { + emit_lifecycle(inner, "error", &e); + { + let mut guard = inner.fallback.lock(); + if let Some(tracker) = guard.as_mut() { + let _ = tracker.observe(crate::fallback::FailureSignature::DeviceLost); + } + } + duplication_state = None; + pointer_shape = PointerShapeCache::default(); + continue; + } + } + + let now = std::time::Instant::now(); + let (sleep_duration, deadline) = + pacing_sleep_and_next_deadline(now, next_frame_deadline, frame_interval); + next_frame_deadline = deadline; + if sleep_duration > std::time::Duration::ZERO { + std::thread::sleep(sleep_duration); + } + } + + inner.running.store(false, Ordering::Release); + emit_lifecycle(inner, "closed-clean", "capture stopped"); +} + +#[cfg(target_os = "windows")] +fn setup_duplication( + device: &ID3D11Device, + hwnd: HWND, + requested_width: Option, + requested_height: Option, +) -> Result { + let (duplication, monitor_rect, monitor) = create_output_duplication(device, hwnd)?; + + let mut window_rect = RECT::default(); + unsafe { + GetWindowRect(hwnd, &mut window_rect).map_err(|e| format!("GetWindowRect: {e}"))?; + } + + let crop_left = (window_rect.left - monitor_rect.left).max(0) as u32; + let crop_top = (window_rect.top - monitor_rect.top).max(0) as u32; + let monitor_width = (monitor_rect.right - monitor_rect.left) as u32; + let monitor_height = (monitor_rect.bottom - monitor_rect.top) as u32; + let crop_right = ((window_rect.right - monitor_rect.left) as u32).min(monitor_width); + let crop_bottom = ((window_rect.bottom - monitor_rect.top) as u32).min(monitor_height); + + let cap_w = crop_right.saturating_sub(crop_left).max(1); + let cap_h = crop_bottom.saturating_sub(crop_top).max(1); + let (out_w, out_h) = resolve_output_size(cap_w, cap_h, requested_width, requested_height); + + let shared_output = create_shared_output_texture(device, cap_w, cap_h).ok(); + + Ok(DuplicationState { + duplication, + monitor, + monitor_rect, + shared_output, + cap_w, + cap_h, + out_w, + out_h, + }) +} + +#[cfg(target_os = "windows")] +enum FrameResult { + Ok, + Timeout, + Resized { width: u32, height: u32 }, + AccessLost, + WindowGone, + Error(String), +} + +#[cfg(target_os = "windows")] +fn acquire_and_emit_frame( + inner: &Arc, + context: &ID3D11DeviceContext, + state: &mut DuplicationState, + hwnd: HWND, + capture_id: Option<&str>, + _pointer_shape: &mut PointerShapeCache, + capture_start: std::time::Instant, +) -> FrameResult { + let mut frame_info = DXGI_OUTDUPL_FRAME_INFO::default(); + let mut resource = None; + match unsafe { + state + .duplication + .AcquireNextFrame(50, &mut frame_info, &mut resource) + } { + Ok(()) => {} + Err(e) if e.code() == DXGI_ERROR_WAIT_TIMEOUT => return FrameResult::Timeout, + Err(e) if e.code() == DXGI_ERROR_ACCESS_LOST => return FrameResult::AccessLost, + Err(e) => return FrameResult::Error(format!("AcquireNextFrame: {e}")), + } + + let desktop_texture: ID3D11Texture2D = match resource + .as_ref() + .and_then(|r| r.cast::().ok()) + { + Some(t) => t, + None => { + let _ = unsafe { state.duplication.ReleaseFrame() }; + return FrameResult::Error("Failed to cast desktop resource to ID3D11Texture2D".into()); + } + }; + + if !unsafe { IsWindow(Some(hwnd)) }.as_bool() { + let _ = unsafe { state.duplication.ReleaseFrame() }; + return FrameResult::WindowGone; + } + + let mut window_rect = RECT::default(); + if unsafe { GetWindowRect(hwnd, &mut window_rect) }.is_err() { + let _ = unsafe { state.duplication.ReleaseFrame() }; + return FrameResult::WindowGone; + } + + let monitor_rect = &state.monitor_rect; + let crop_left = (window_rect.left - monitor_rect.left).max(0) as u32; + let crop_top = (window_rect.top - monitor_rect.top).max(0) as u32; + let monitor_width = (monitor_rect.right - monitor_rect.left) as u32; + let monitor_height = (monitor_rect.bottom - monitor_rect.top) as u32; + let crop_right = ((window_rect.right - monitor_rect.left) as u32).min(monitor_width); + let crop_bottom = ((window_rect.bottom - monitor_rect.top) as u32).min(monitor_height); + + let cur_w = crop_right.saturating_sub(crop_left).max(1); + let cur_h = crop_bottom.saturating_sub(crop_top).max(1); + + if cur_w != state.cap_w || cur_h != state.cap_h { + let current_monitor = unsafe { MonitorFromWindow(hwnd, MONITOR_DEFAULTTONULL) }; + let _ = unsafe { state.duplication.ReleaseFrame() }; + if current_monitor != state.monitor { + return FrameResult::AccessLost; + } + return FrameResult::Resized { + width: cur_w, + height: cur_h, + }; + } + + let src_box = D3D11_BOX { + left: crop_left, + top: crop_top, + front: 0, + right: crop_left + state.cap_w, + bottom: crop_top + state.cap_h, + back: 1, + }; + + let desktop_resource: ID3D11Resource = match desktop_texture.cast() { + Ok(resource) => resource, + Err(e) => { + let _ = unsafe { state.duplication.ReleaseFrame() }; + return FrameResult::Error(format!("ID3D11Resource desktop cast: {e}")); + } + }; + + let result = emit_acquired_frame( + inner, + context, + state, + capture_id, + &desktop_resource, + &src_box, + capture_start, + ); + let _ = unsafe { state.duplication.ReleaseFrame() }; + result +} + +#[cfg(target_os = "windows")] +fn emit_acquired_frame( + inner: &Arc, + context: &ID3D11DeviceContext, + state: &mut DuplicationState, + capture_id: Option<&str>, + desktop_resource: &ID3D11Resource, + src_box: &D3D11_BOX, + capture_start: std::time::Instant, +) -> FrameResult { + assert!(src_box.right > src_box.left, "source box width positive"); + assert!(src_box.bottom > src_box.top, "source box height positive"); + let Some(frame_sink) = resolve_frame_sink(inner, capture_id) else { + note_media_frame_without_sink( + inner, + "DXGI frame dropped because no native frame sink is registered", + ); + return FrameResult::Ok; + }; + if state.out_w != state.cap_w || state.out_h != state.cap_h { + return FrameResult::Error( + "DXGI native bus scaling requires a GPU scaler; refusing CPU readback fallback".into(), + ); + } + let Some(shared_output) = state.shared_output.as_mut() else { + return FrameResult::Error("DXGI shared texture output unavailable".into()); + }; + let slot_index = shared_output.next_slot_index(); + let slot = &shared_output.slots[slot_index]; + let output_resource: ID3D11Resource = match slot.texture.cast() { + Ok(resource) => resource, + Err(e) => { + return FrameResult::Error(format!("ID3D11Resource shared output cast: {e}")); + } + }; + unsafe { + context.CopySubresourceRegion( + &output_resource, + 0, + 0, + 0, + 0, + desktop_resource, + 0, + Some(src_box), + ); + context.Flush(); + } + let _ = emit_shared_texture_frame( + inner, + &frame_sink, + slot.handle, + shared_output.width, + shared_output.height, + shared_output.dxgi_format, + capture_timestamp_us(capture_start), + ); + FrameResult::Ok +} + +#[cfg(target_os = "windows")] +fn update_pointer_shape( + duplication: &IDXGIOutputDuplication, + frame_info: &DXGI_OUTDUPL_FRAME_INFO, + cache: &mut PointerShapeCache, +) -> PointerShapeUpdate { + if frame_info.PointerShapeBufferSize == 0 { + return PointerShapeUpdate::Ok; + } + + let buffer_size = frame_info.PointerShapeBufferSize as usize; + if cache.buffer.len() < buffer_size { + cache.buffer.resize(buffer_size, 0); + } + + let mut required_size = 0u32; + let mut shape_info = DXGI_OUTDUPL_POINTER_SHAPE_INFO::default(); + let result = unsafe { + duplication.GetFramePointerShape( + cache.buffer.len() as u32, + cache.buffer.as_mut_ptr().cast(), + &mut required_size, + &mut shape_info, + ) + }; + + match result { + Ok(()) => { + cache.buffer.truncate(required_size as usize); + cache.info = Some(shape_info); + PointerShapeUpdate::Ok + } + Err(e) if e.code() == DXGI_ERROR_MORE_DATA && required_size > cache.buffer.len() as u32 => { + cache.buffer.resize(required_size as usize, 0); + let result = unsafe { + duplication.GetFramePointerShape( + cache.buffer.len() as u32, + cache.buffer.as_mut_ptr().cast(), + &mut required_size, + &mut shape_info, + ) + }; + match result { + Ok(()) => { + cache.buffer.truncate(required_size as usize); + cache.info = Some(shape_info); + PointerShapeUpdate::Ok + } + Err(e) if e.code() == DXGI_ERROR_ACCESS_LOST => PointerShapeUpdate::AccessLost, + Err(e) => PointerShapeUpdate::Error(format!("GetFramePointerShape: {e}")), + } + } + Err(e) if e.code() == DXGI_ERROR_ACCESS_LOST => PointerShapeUpdate::AccessLost, + Err(e) => PointerShapeUpdate::Error(format!("GetFramePointerShape: {e}")), + } +} + +#[cfg(target_os = "windows")] +#[allow(clippy::too_many_arguments)] +fn composite_pointer_shape( + frame: &mut [u8], + width: u32, + height: u32, + stride: u32, + frame_info: &DXGI_OUTDUPL_FRAME_INFO, + crop_left: u32, + crop_top: u32, + cache: &PointerShapeCache, +) { + if !frame_info.PointerPosition.Visible.as_bool() { + return; + } + let Some(shape_info) = cache.info else { + return; + }; + if shape_info.Width == 0 || shape_info.Height == 0 || shape_info.Pitch == 0 { + return; + } + + let pointer_x = frame_info.PointerPosition.Position.x - crop_left as i32; + let pointer_y = frame_info.PointerPosition.Position.y - crop_top as i32; + if pointer_x >= width as i32 || pointer_y >= height as i32 { + return; + } + + if shape_info.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_COLOR.0 as u32 { + composite_color_pointer( + frame, + width, + height, + stride, + pointer_x, + pointer_y, + &shape_info, + &cache.buffer, + ); + } else if shape_info.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MASKED_COLOR.0 as u32 { + composite_masked_color_pointer( + frame, + width, + height, + stride, + pointer_x, + pointer_y, + &shape_info, + &cache.buffer, + ); + } else if shape_info.Type == DXGI_OUTDUPL_POINTER_SHAPE_TYPE_MONOCHROME.0 as u32 { + composite_monochrome_pointer( + frame, + width, + height, + stride, + pointer_x, + pointer_y, + &shape_info, + &cache.buffer, + ); + } +} + +#[cfg(target_os = "windows")] +#[allow(clippy::too_many_arguments)] +fn composite_color_pointer( + frame: &mut [u8], + width: u32, + height: u32, + stride: u32, + pointer_x: i32, + pointer_y: i32, + shape_info: &DXGI_OUTDUPL_POINTER_SHAPE_INFO, + shape: &[u8], +) { + let pitch = shape_info.Pitch as usize; + for y in 0..shape_info.Height as i32 { + let dst_y = pointer_y + y; + if dst_y < 0 || dst_y >= height as i32 { + continue; + } + for x in 0..shape_info.Width as i32 { + let dst_x = pointer_x + x; + if dst_x < 0 || dst_x >= width as i32 { + continue; + } + let src_offset = y as usize * pitch + x as usize * 4; + let dst_offset = dst_y as usize * stride as usize + dst_x as usize * 4; + if src_offset + 4 > shape.len() || dst_offset + 4 > frame.len() { + continue; + } + blend_bgra_pixel(frame, dst_offset, shape, src_offset); + } + } +} + +#[cfg(target_os = "windows")] +#[allow(clippy::too_many_arguments)] +fn composite_masked_color_pointer( + frame: &mut [u8], + width: u32, + height: u32, + stride: u32, + pointer_x: i32, + pointer_y: i32, + shape_info: &DXGI_OUTDUPL_POINTER_SHAPE_INFO, + shape: &[u8], +) { + let pitch = shape_info.Pitch as usize; + for y in 0..shape_info.Height as i32 { + let dst_y = pointer_y + y; + if dst_y < 0 || dst_y >= height as i32 { + continue; + } + for x in 0..shape_info.Width as i32 { + let dst_x = pointer_x + x; + if dst_x < 0 || dst_x >= width as i32 { + continue; + } + let src_offset = y as usize * pitch + x as usize * 4; + let dst_offset = dst_y as usize * stride as usize + dst_x as usize * 4; + if src_offset + 4 > shape.len() || dst_offset + 4 > frame.len() { + continue; + } + let mask = shape[src_offset + 3]; + if mask == 0 { + frame[dst_offset..dst_offset + 3] + .copy_from_slice(&shape[src_offset..src_offset + 3]); + frame[dst_offset + 3] = 255; + } else if mask == 0xff { + frame[dst_offset] ^= shape[src_offset]; + frame[dst_offset + 1] ^= shape[src_offset + 1]; + frame[dst_offset + 2] ^= shape[src_offset + 2]; + frame[dst_offset + 3] = 255; + } else { + blend_bgra_pixel(frame, dst_offset, shape, src_offset); + } + } + } +} + +#[cfg(target_os = "windows")] +#[allow(clippy::too_many_arguments)] +fn composite_monochrome_pointer( + frame: &mut [u8], + width: u32, + height: u32, + stride: u32, + pointer_x: i32, + pointer_y: i32, + shape_info: &DXGI_OUTDUPL_POINTER_SHAPE_INFO, + shape: &[u8], +) { + let pitch = shape_info.Pitch as usize; + let full_height = shape_info.Height as usize; + if pitch == 0 || full_height == 0 { + return; + } + + let visible_height = if shape.len() >= pitch * full_height * 2 { + full_height + } else { + full_height / 2 + }; + if visible_height == 0 || shape.len() < pitch * visible_height * 2 { + return; + } + + let xor_mask_offset = pitch * visible_height; + for y in 0..visible_height as i32 { + let dst_y = pointer_y + y; + if dst_y < 0 || dst_y >= height as i32 { + continue; + } + for x in 0..shape_info.Width as i32 { + let dst_x = pointer_x + x; + if dst_x < 0 || dst_x >= width as i32 { + continue; + } + let byte_index = y as usize * pitch + x as usize / 8; + let mask = 0x80 >> (x as usize % 8); + if byte_index >= xor_mask_offset || xor_mask_offset + byte_index >= shape.len() { + continue; + } + + let and_set = shape[byte_index] & mask != 0; + let xor_set = shape[xor_mask_offset + byte_index] & mask != 0; + let dst_offset = dst_y as usize * stride as usize + dst_x as usize * 4; + if dst_offset + 4 > frame.len() { + continue; + } + + match (and_set, xor_set) { + (true, false) => {} + (true, true) => { + frame[dst_offset] ^= 0xff; + frame[dst_offset + 1] ^= 0xff; + frame[dst_offset + 2] ^= 0xff; + frame[dst_offset + 3] = 255; + } + (false, false) => { + frame[dst_offset] = 0; + frame[dst_offset + 1] = 0; + frame[dst_offset + 2] = 0; + frame[dst_offset + 3] = 255; + } + (false, true) => { + frame[dst_offset] = 255; + frame[dst_offset + 1] = 255; + frame[dst_offset + 2] = 255; + frame[dst_offset + 3] = 255; + } + } + } + } +} + +#[cfg(test)] +mod tests { + use super::pacing_sleep_and_next_deadline; + use std::time::{Duration, Instant}; + + const TEST_FRAME_INTERVAL: Duration = Duration::from_millis(33); + + #[test] + fn pacing_sleeps_remaining_time_and_advances_deadline_by_interval() { + let start = Instant::now(); + let deadline = start + TEST_FRAME_INTERVAL; + let now = start + Duration::from_millis(10); + let (sleep_duration, next_deadline) = + pacing_sleep_and_next_deadline(now, deadline, TEST_FRAME_INTERVAL); + assert_eq!(sleep_duration, Duration::from_millis(23)); + assert_eq!(next_deadline, deadline + TEST_FRAME_INTERVAL); + } + + #[test] + fn pacing_does_not_drift_across_iterations_with_varying_work() { + let start = Instant::now(); + let mut deadline = start + TEST_FRAME_INTERVAL; + let mut now = start; + for iteration in 1..=100u32 { + now += Duration::from_millis(7); + let (sleep_duration, next_deadline) = + pacing_sleep_and_next_deadline(now, deadline, TEST_FRAME_INTERVAL); + assert!(sleep_duration <= TEST_FRAME_INTERVAL); + now += sleep_duration; + deadline = next_deadline; + assert_eq!(now, start + TEST_FRAME_INTERVAL * iteration); + } + } + + #[test] + fn pacing_resets_deadline_without_sleeping_when_behind() { + let start = Instant::now(); + let deadline = start + TEST_FRAME_INTERVAL; + let now = deadline + Duration::from_millis(50); + let (sleep_duration, next_deadline) = + pacing_sleep_and_next_deadline(now, deadline, TEST_FRAME_INTERVAL); + assert_eq!(sleep_duration, Duration::ZERO); + assert_eq!(next_deadline, now + TEST_FRAME_INTERVAL); + } + + #[test] + fn pacing_at_exact_deadline_resets_without_sleeping() { + let start = Instant::now(); + let deadline = start + TEST_FRAME_INTERVAL; + let (sleep_duration, next_deadline) = + pacing_sleep_and_next_deadline(deadline, deadline, TEST_FRAME_INTERVAL); + assert_eq!(sleep_duration, Duration::ZERO); + assert_eq!(next_deadline, deadline + TEST_FRAME_INTERVAL); + } +} + +#[cfg(target_os = "windows")] +fn blend_bgra_pixel(frame: &mut [u8], dst_offset: usize, shape: &[u8], src_offset: usize) { + let alpha = shape[src_offset + 3] as u32; + if alpha == 0 { + return; + } + if alpha == 255 { + frame[dst_offset..dst_offset + 4].copy_from_slice(&shape[src_offset..src_offset + 4]); + return; + } + let inv_alpha = 255 - alpha; + for channel in 0..3 { + let src = shape[src_offset + channel] as u32; + let dst = frame[dst_offset + channel] as u32; + frame[dst_offset + channel] = ((src * alpha + dst * inv_alpha + 127) / 255) as u8; + } + frame[dst_offset + 3] = 255; +} diff --git a/fluxer_desktop/native/win-game-capture/src/encoder_attach.rs b/fluxer_desktop/native/win-game-capture/src/encoder_attach.rs new file mode 100644 index 0000000..22a5f0b --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/src/encoder_attach.rs @@ -0,0 +1,523 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +use std::time::Duration; + +use parking_lot::{Condvar, Mutex}; + +use fluxer_encoder_ring::d3d11::D3D11SharedHandle; +use fluxer_encoder_ring::{ + D3D11KeyedMutexBackend, EncoderFrameRate, EncoderInputRing, EncoderReady, RING_SIZE, RingError, + TextureFormat, +}; + +#[cfg(target_os = "windows")] +use fluxer_encoder_ring::{EncoderDims, FillReservation, NvencD3D11Handoff}; + +#[cfg(target_os = "windows")] +use windows::Win32::Graphics::Direct3D11::{ID3D11DeviceContext, ID3D11Texture2D}; + +pub const READY_WAIT_TIMEOUT_CAP: Duration = Duration::from_millis(17); + +const EVICTIONS_PER_RESERVE_MAX: u64 = 2; + +#[cfg(target_os = "windows")] +const NVENC_BITRATE_BPS_DEFAULT: u32 = 8_000_000; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EncoderAttachError { + AlreadyAttached, + InvalidDimensions { width: u32, height: u32 }, + RingInitFailed, + NotAttached, + BlitFailed, + SlotUnavailable, + DeviceUnavailable, +} + +impl std::fmt::Display for EncoderAttachError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::AlreadyAttached => write!(f, "encoder ring already attached"), + Self::InvalidDimensions { width, height } => { + write!(f, "encoder ring invalid dimensions {width}x{height}") + } + Self::RingInitFailed => write!(f, "encoder ring initialise failed"), + Self::NotAttached => write!(f, "encoder ring not attached"), + Self::BlitFailed => write!(f, "blit into ring slot failed"), + Self::SlotUnavailable => write!(f, "no ring slot texture available"), + Self::DeviceUnavailable => write!(f, "ring backend device unavailable"), + } + } +} + +impl std::error::Error for EncoderAttachError {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)] +pub struct EncoderAttachStats { + pub frames_submitted: u64, + pub frames_dropped: u64, + pub ring_full_events: u64, + pub failed_blits: u64, +} + +pub struct EncoderAttachment { + ring: Mutex>, + ready_condvar: Condvar, + attached: AtomicBool, + frames_submitted: AtomicU64, + frames_dropped: AtomicU64, + ring_full_events: AtomicU64, + failed_blits: AtomicU64, + width: u32, + height: u32, + frame_rate: EncoderFrameRate, + #[cfg(target_os = "windows")] + context: ID3D11DeviceContext, + #[cfg(target_os = "windows")] + nvenc: Mutex>, +} + +impl EncoderAttachment { + pub fn try_new(width: u32, height: u32) -> Result, EncoderAttachError> { + Self::try_new_with_frame_rate(width, height, EncoderFrameRate::default()) + } + + pub fn try_new_with_frame_rate( + width: u32, + height: u32, + frame_rate: EncoderFrameRate, + ) -> Result, EncoderAttachError> { + if width == 0 || height == 0 { + return Err(EncoderAttachError::InvalidDimensions { width, height }); + } + assert!(frame_rate.numerator > 0, "frame rate numerator positive"); + assert!( + frame_rate.denominator > 0, + "frame rate denominator positive" + ); + let backend = + D3D11KeyedMutexBackend::new().map_err(|_| EncoderAttachError::RingInitFailed)?; + let mut ring = EncoderInputRing::new(backend); + ring.initialise(width, height, TextureFormat::Nv12) + .map_err(|_| EncoderAttachError::RingInitFailed)?; + #[cfg(target_os = "windows")] + let context = ring + .backend_mut() + .context() + .ok_or(EncoderAttachError::DeviceUnavailable)?; + #[cfg(target_os = "windows")] + let nvenc = init_nvenc_pre_registered(&mut ring, width, height, frame_rate); + let attachment = Self { + ring: Mutex::new(ring), + ready_condvar: Condvar::new(), + attached: AtomicBool::new(true), + frames_submitted: AtomicU64::new(0), + frames_dropped: AtomicU64::new(0), + ring_full_events: AtomicU64::new(0), + failed_blits: AtomicU64::new(0), + width, + height, + frame_rate, + #[cfg(target_os = "windows")] + context, + #[cfg(target_os = "windows")] + nvenc: Mutex::new(nvenc), + }; + assert!( + attachment.attached.load(Ordering::Acquire), + "attachment is attached" + ); + assert!(attachment.width > 0, "attachment width positive"); + assert!( + attachment.frame_rate.numerator > 0, + "attachment fps positive" + ); + Ok(Arc::new(attachment)) + } + + pub fn width(&self) -> u32 { + let w = self.width; + assert!(w > 0, "attachment width positive"); + assert!(self.height > 0, "attachment height positive"); + w + } + + pub fn height(&self) -> u32 { + let h = self.height; + assert!(h > 0, "attachment height positive"); + assert!(self.width > 0, "attachment width positive"); + h + } + + pub fn frame_rate(&self) -> EncoderFrameRate { + let rate = self.frame_rate; + assert!(rate.numerator > 0, "attachment fps numerator positive"); + assert!(rate.denominator > 0, "attachment fps denominator positive"); + rate + } + + pub fn is_attached(&self) -> bool { + let a = self.attached.load(Ordering::Acquire); + assert!(self.width > 0, "width intact while reading attached"); + assert!(self.height > 0, "height intact while reading attached"); + a + } + + pub fn detach(&self) { + self.attached.store(false, Ordering::Release); + self.ready_condvar.notify_all(); + assert!( + !self.attached.load(Ordering::Acquire), + "detach flag cleared" + ); + assert!(self.width > 0, "attachment intact across detach"); + } + + pub fn capacity(&self) -> usize { + let cap = RING_SIZE; + assert!(cap > 0, "ring capacity positive"); + assert_eq!(cap, 8, "ring capacity matches RING_SIZE"); + cap + } + + pub fn note_ring_full(&self) { + self.ring_full_events.fetch_add(1, Ordering::Relaxed); + } + + #[cfg(target_os = "windows")] + pub fn nvenc_pre_registered(&self) -> bool { + let registered = self.nvenc.lock().is_some(); + assert!(self.width > 0, "width intact while reading nvenc state"); + assert!(self.height > 0, "height intact while reading nvenc state"); + registered + } + + pub fn stats(&self) -> EncoderAttachStats { + let stats = EncoderAttachStats { + frames_submitted: self.frames_submitted.load(Ordering::Relaxed), + frames_dropped: self.frames_dropped.load(Ordering::Relaxed), + ring_full_events: self.ring_full_events.load(Ordering::Relaxed), + failed_blits: self.failed_blits.load(Ordering::Relaxed), + }; + assert!( + stats.frames_submitted <= u64::MAX / 2, + "submitted plausible" + ); + assert!(stats.frames_dropped <= u64::MAX / 2, "dropped plausible"); + stats + } + + fn account_evictions(&self, evicted_delta: u64) { + assert!( + evicted_delta <= EVICTIONS_PER_RESERVE_MAX, + "evictions bounded per reserve" + ); + assert!(self.width > 0, "attachment intact while accounting"); + if evicted_delta == 0 { + return; + } + self.frames_dropped + .fetch_add(evicted_delta, Ordering::Relaxed); + self.ring_full_events + .fetch_add(evicted_delta, Ordering::Relaxed); + } + + pub fn submit_notify(&self) -> Result<(), EncoderAttachError> { + if !self.attached.load(Ordering::Acquire) { + return Err(EncoderAttachError::NotAttached); + } + let mut ring = self.ring.lock(); + let pre_dropped = ring.dropped_count(); + let result: Result<(), RingError> = ring.submit_skip_oldest(|_handle| {}); + let evicted_delta = ring.dropped_count().saturating_sub(pre_dropped); + drop(ring); + self.account_evictions(evicted_delta); + match result { + Ok(()) => { + self.frames_submitted.fetch_add(1, Ordering::Relaxed); + self.ready_condvar.notify_one(); + Ok(()) + } + Err(RingError::FullDropped { .. }) => Ok(()), + Err(_) => Err(EncoderAttachError::RingInitFailed), + } + } + + pub fn wait_next_ready(&self, timeout: Duration) -> Option> { + assert!(self.width > 0, "attachment width positive"); + assert!(self.height > 0, "attachment height positive"); + if !self.attached.load(Ordering::Acquire) { + return None; + } + let capped_timeout = timeout.min(READY_WAIT_TIMEOUT_CAP); + let mut ring = self.ring.lock(); + if let Some(ready) = ring.poll_next_ready() { + return Some(ready); + } + let _ = self.ready_condvar.wait_for(&mut ring, capped_timeout); + if !self.attached.load(Ordering::Acquire) { + return None; + } + ring.poll_next_ready() + } + + #[cfg(target_os = "windows")] + fn cancel_reservation(&self, reservation: FillReservation) { + assert!( + (reservation.slot_index() as usize) < RING_SIZE, + "cancelled slot within ring" + ); + assert!(self.width > 0, "attachment intact while cancelling"); + let mut ring = self.ring.lock(); + let _ = ring.cancel(reservation); + } + + #[cfg(target_os = "windows")] + pub fn submit_capture_frame_with_blit( + &self, + capture_texture: &ID3D11Texture2D, + capture_width: u32, + capture_height: u32, + ) -> Result<(), EncoderAttachError> { + assert!(capture_width > 0, "capture width positive"); + assert!(capture_height > 0, "capture height positive"); + assert!(self.width > 0, "attachment width positive"); + assert!(self.height > 0, "attachment height positive"); + if !self.attached.load(Ordering::Acquire) { + return Err(EncoderAttachError::NotAttached); + } + let mut ring = self.ring.lock(); + let pre_dropped = ring.dropped_count(); + let reserved = ring.reserve_skip_oldest(); + let evicted_delta = ring.dropped_count().saturating_sub(pre_dropped); + let dest_texture = match &reserved { + Ok(reservation) => ring + .backend_mut() + .texture_for_slot(reservation.slot_index()), + Err(_) => None, + }; + drop(ring); + self.account_evictions(evicted_delta); + let reservation = match reserved { + Ok(reservation) => reservation, + Err(RingError::FullDropped { .. }) => return Ok(()), + Err(_) => return Err(EncoderAttachError::RingInitFailed), + }; + let Some(dest_texture) = dest_texture else { + self.cancel_reservation(reservation); + self.failed_blits.fetch_add(1, Ordering::Relaxed); + return Err(EncoderAttachError::BlitFailed); + }; + unsafe { + self.context.CopyResource(&dest_texture, capture_texture); + } + let mut ring = self.ring.lock(); + match ring.commit(reservation) { + Ok(sequence) => { + drop(ring); + assert!(sequence > 0, "committed sequence positive"); + self.frames_submitted.fetch_add(1, Ordering::Relaxed); + self.ready_condvar.notify_one(); + Ok(()) + } + Err(_) => Err(EncoderAttachError::RingInitFailed), + } + } +} + +#[cfg(target_os = "windows")] +fn collect_ring_slot_handles( + ring: &mut EncoderInputRing, +) -> Option> { + assert_eq!( + ring.free_count(), + RING_SIZE, + "collect requires a fresh ring" + ); + let mut reservations: Vec> = Vec::with_capacity(RING_SIZE); + let mut handles: Vec = Vec::with_capacity(RING_SIZE); + for _ in 0..RING_SIZE { + let Ok(reservation) = ring.reserve() else { + break; + }; + handles.push(reservation.handle.clone()); + reservations.push(reservation); + } + let mut all_cancelled = true; + for reservation in reservations.drain(..) { + if ring.cancel(reservation).is_err() { + all_cancelled = false; + } + } + if !all_cancelled { + return None; + } + assert_eq!(ring.free_count(), RING_SIZE, "all reservations returned"); + assert!(handles.len() <= RING_SIZE, "collected handles bounded"); + if handles.len() == RING_SIZE { + Some(handles) + } else { + None + } +} + +#[cfg(target_os = "windows")] +fn init_nvenc_pre_registered( + ring: &mut EncoderInputRing, + width: u32, + height: u32, + frame_rate: EncoderFrameRate, +) -> Option { + assert!(width > 0, "nvenc init width positive"); + assert!(height > 0, "nvenc init height positive"); + assert!(frame_rate.numerator > 0, "nvenc frame rate positive"); + let device = ring.backend_mut().device()?; + let handles = collect_ring_slot_handles(ring)?; + assert_eq!(handles.len(), RING_SIZE, "pre-register covers whole ring"); + let dims = EncoderDims::new(width, height); + let mut handoff = + NvencD3D11Handoff::new_with_frame_rate(device, dims, NVENC_BITRATE_BPS_DEFAULT, frame_rate) + .ok()?; + handoff.pre_register_slots(&handles, dims).ok()?; + Some(handoff) +} + +impl Drop for EncoderAttachment { + fn drop(&mut self) { + self.attached.store(false, Ordering::Release); + self.ready_condvar.notify_all(); + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn rejects_zero_dimensions() { + let err = EncoderAttachment::try_new(0, 720).err(); + assert!(matches!( + err, + Some(EncoderAttachError::InvalidDimensions { .. }) + )); + let err2 = EncoderAttachment::try_new(640, 0).err(); + assert!(matches!( + err2, + Some(EncoderAttachError::InvalidDimensions { .. }) + )); + } + + #[test] + fn ready_wait_timeout_cap_is_one_frame_interval() { + assert!(READY_WAIT_TIMEOUT_CAP >= Duration::from_millis(1)); + assert!(READY_WAIT_TIMEOUT_CAP <= Duration::from_millis(33)); + } + + #[cfg(not(target_os = "windows"))] + #[test] + fn off_windows_init_fails() { + let err = EncoderAttachment::try_new(640, 480).err(); + assert!(matches!(err, Some(EncoderAttachError::RingInitFailed))); + } + + #[cfg(target_os = "windows")] + #[test] + fn windows_attach_then_detach_smoke() { + let attach = match EncoderAttachment::try_new(640, 480) { + Ok(a) => a, + Err(_) => return, + }; + assert!(attach.is_attached()); + assert_eq!(attach.width(), 640); + assert_eq!(attach.height(), 480); + assert_eq!(attach.capacity(), 8); + let stats = attach.stats(); + assert_eq!(stats.frames_submitted, 0, "fresh attach has 0 submissions"); + assert_eq!(stats.failed_blits, 0, "fresh attach has 0 failed blits"); + attach.detach(); + assert!(!attach.is_attached()); + } + + #[cfg(target_os = "windows")] + #[test] + fn windows_submit_notify_records_stats() { + let attach = match EncoderAttachment::try_new(64, 64) { + Ok(a) => a, + Err(_) => return, + }; + for _ in 0..3 { + attach.submit_notify().expect("notify ok"); + } + let stats = attach.stats(); + assert_eq!(stats.frames_submitted, 3); + assert_eq!(stats.frames_dropped, 0); + assert_eq!(stats.ring_full_events, 0); + } + + #[cfg(target_os = "windows")] + #[test] + fn windows_submit_rejected_when_detached() { + let attach = match EncoderAttachment::try_new(64, 64) { + Ok(a) => a, + Err(_) => return, + }; + attach.detach(); + let err = attach.submit_notify().err(); + assert!(matches!(err, Some(EncoderAttachError::NotAttached))); + } + + #[cfg(target_os = "windows")] + #[test] + fn windows_wait_next_ready_returns_committed_frame() { + let attach = match EncoderAttachment::try_new(64, 64) { + Ok(a) => a, + Err(_) => return, + }; + attach.submit_notify().expect("notify ok"); + let ready = attach.wait_next_ready(Duration::from_millis(5)); + let ready = ready.expect("committed frame becomes ready"); + assert_eq!(ready.sequence, 1); + assert_eq!(ready.duplicate_count, 0); + } + + #[cfg(target_os = "windows")] + #[test] + fn windows_wait_next_ready_empty_ring_times_out() { + let attach = match EncoderAttachment::try_new(64, 64) { + Ok(a) => a, + Err(_) => return, + }; + let ready = attach.wait_next_ready(Duration::from_millis(2)); + assert!(ready.is_none()); + assert!(attach.is_attached()); + } + + #[cfg(target_os = "windows")] + #[test] + fn windows_wait_next_ready_after_detach_returns_none() { + let attach = match EncoderAttachment::try_new(64, 64) { + Ok(a) => a, + Err(_) => return, + }; + attach.detach(); + let ready = attach.wait_next_ready(Duration::from_millis(2)); + assert!(ready.is_none()); + assert!(!attach.is_attached()); + } + + #[cfg(target_os = "windows")] + #[test] + fn windows_ring_usable_after_nvenc_pre_register_attempt() { + let attach = match EncoderAttachment::try_new(64, 64) { + Ok(a) => a, + Err(_) => return, + }; + let _ = attach.nvenc_pre_registered(); + attach.submit_notify().expect("notify ok"); + let stats = attach.stats(); + assert_eq!(stats.frames_submitted, 1); + assert_eq!(stats.frames_dropped, 0); + } +} diff --git a/fluxer_desktop/native/win-game-capture/src/fallback.rs b/fluxer_desktop/native/win-game-capture/src/fallback.rs new file mode 100644 index 0000000..68647be --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/src/fallback.rs @@ -0,0 +1,710 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![allow(dead_code)] + +use crate::game_capture_abi::{ + GAME_CAPTURE_FALLBACK_DEVICE_LOST, GAME_CAPTURE_FALLBACK_EXTERNAL_MEMORY_UNSUPPORTED, + GAME_CAPTURE_FALLBACK_FORCED_CPU, GAME_CAPTURE_FALLBACK_FORMAT_UNSUPPORTED, + GAME_CAPTURE_FALLBACK_MULTISAMPLED, GAME_CAPTURE_FALLBACK_NONE, + GAME_CAPTURE_FALLBACK_SHARED_TEXTURE_UNSUPPORTED, +}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum CaptureStrategy { + GameHook, + Wgc, + DxgiDuplication, + WindowGdi, +} + +impl CaptureStrategy { + pub const RANKED: [CaptureStrategy; 4] = [ + CaptureStrategy::GameHook, + CaptureStrategy::Wgc, + CaptureStrategy::DxgiDuplication, + CaptureStrategy::WindowGdi, + ]; + + pub fn rank(self) -> u8 { + match self { + CaptureStrategy::GameHook => 0, + CaptureStrategy::Wgc => 1, + CaptureStrategy::DxgiDuplication => 2, + CaptureStrategy::WindowGdi => 3, + } + } + + pub fn next_worse(self) -> Option { + match self { + CaptureStrategy::GameHook => Some(CaptureStrategy::Wgc), + CaptureStrategy::Wgc => Some(CaptureStrategy::DxgiDuplication), + CaptureStrategy::DxgiDuplication => Some(CaptureStrategy::WindowGdi), + CaptureStrategy::WindowGdi => None, + } + } + + pub fn as_str(self) -> &'static str { + match self { + CaptureStrategy::GameHook => "game-hook", + CaptureStrategy::Wgc => "wgc", + CaptureStrategy::DxgiDuplication => "dxgi-duplication", + CaptureStrategy::WindowGdi => "window-gdi", + } + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub enum FailureSignature { + InjectionFailed, + AntiCheatDenied, + NoFramesWithinTimeout, + DeviceLost, + UnsupportedTransport, + UnsupportedFormat, + WindowGone, + FramesRecovered, +} + +impl FailureSignature { + pub fn from_fallback_reason(reason: u32) -> Option { + match reason { + GAME_CAPTURE_FALLBACK_NONE => None, + GAME_CAPTURE_FALLBACK_SHARED_TEXTURE_UNSUPPORTED + | GAME_CAPTURE_FALLBACK_EXTERNAL_MEMORY_UNSUPPORTED => { + Some(FailureSignature::UnsupportedTransport) + } + GAME_CAPTURE_FALLBACK_FORMAT_UNSUPPORTED | GAME_CAPTURE_FALLBACK_MULTISAMPLED => { + Some(FailureSignature::UnsupportedFormat) + } + GAME_CAPTURE_FALLBACK_DEVICE_LOST => Some(FailureSignature::DeviceLost), + GAME_CAPTURE_FALLBACK_FORCED_CPU => None, + _ => None, + } + } + + fn is_terminal_for_all(self) -> bool { + matches!(self, FailureSignature::WindowGone) + } + + fn permanently_disqualifies_current(self) -> bool { + matches!( + self, + FailureSignature::AntiCheatDenied + | FailureSignature::InjectionFailed + | FailureSignature::UnsupportedTransport + | FailureSignature::UnsupportedFormat + ) + } +} + +pub const MAX_DEVICE_LOST_RETRIES: u32 = 3; + +pub const STABLE_FRAMES_BEFORE_UPGRADE: u32 = 2; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum FallbackDecision { + Stay { + retry_in_place: bool, + }, + Transition { + from: CaptureStrategy, + to: CaptureStrategy, + reason: String, + }, + Upgrade { + from: CaptureStrategy, + to: CaptureStrategy, + reason: String, + }, + GiveUp { + reason: String, + }, +} + +#[derive(Debug, Clone)] +pub struct FallbackRanker { + active: CaptureStrategy, + disqualified: Vec, + device_lost_streak: u32, + stable_frame_streak: u32, + last_fallback_reason: Option, +} + +impl FallbackRanker { + pub fn new(initial: CaptureStrategy) -> Self { + Self { + active: initial, + disqualified: Vec::new(), + device_lost_streak: 0, + stable_frame_streak: 0, + last_fallback_reason: None, + } + } + + pub fn active(&self) -> CaptureStrategy { + self.active + } + + pub fn last_fallback_reason(&self) -> Option<&str> { + self.last_fallback_reason.as_deref() + } + + pub fn is_disqualified(&self, strategy: CaptureStrategy) -> bool { + self.disqualified.contains(&strategy) + } + + fn best_available_above(&self) -> Option { + CaptureStrategy::RANKED.iter().copied().find(|candidate| { + candidate.rank() < self.active.rank() && !self.is_disqualified(*candidate) + }) + } + + fn next_available_below(&self) -> Option { + let mut candidate = self.active.next_worse(); + while let Some(strategy) = candidate { + if !self.is_disqualified(strategy) { + return Some(strategy); + } + candidate = strategy.next_worse(); + } + None + } + + fn disqualify(&mut self, strategy: CaptureStrategy) { + if !self.disqualified.contains(&strategy) { + self.disqualified.push(strategy); + } + } + + pub fn observe(&mut self, signature: FailureSignature) -> FallbackDecision { + if signature.is_terminal_for_all() { + return FallbackDecision::GiveUp { + reason: format!( + "the capture target is gone; no capture strategy can continue (was using \ + {})", + self.active.as_str() + ), + }; + } + + if signature == FailureSignature::FramesRecovered { + self.device_lost_streak = 0; + self.stable_frame_streak = self.stable_frame_streak.saturating_add(1); + if self.stable_frame_streak >= STABLE_FRAMES_BEFORE_UPGRADE + && let Some(target) = self.best_available_above() + { + let from = self.active; + let reason = format!( + "{} has been stable for {} frames; attempting to upgrade back to the \ + preferred {} strategy", + from.as_str(), + self.stable_frame_streak, + target.as_str() + ); + self.active = target; + self.stable_frame_streak = 0; + self.last_fallback_reason = Some(reason.clone()); + return FallbackDecision::Upgrade { + from, + to: target, + reason, + }; + } + return FallbackDecision::Stay { + retry_in_place: false, + }; + } + + self.stable_frame_streak = 0; + + if signature == FailureSignature::DeviceLost { + self.device_lost_streak = self.device_lost_streak.saturating_add(1); + if self.device_lost_streak <= MAX_DEVICE_LOST_RETRIES { + return FallbackDecision::Stay { + retry_in_place: true, + }; + } + return self.fall_back( + signature, + &format!( + "{} kept losing its capture device ({} consecutive recoveries failed); falling \ + back", + self.active.as_str(), + self.device_lost_streak + ), + ); + } + + self.device_lost_streak = 0; + + if signature.permanently_disqualifies_current() { + self.disqualify(self.active); + } + + let reason = describe_failure(self.active, signature); + self.fall_back(signature, &reason) + } + + fn fall_back(&mut self, signature: FailureSignature, reason: &str) -> FallbackDecision { + self.device_lost_streak = 0; + match self.next_available_below() { + Some(target) => { + let from = self.active; + let full = format!("{reason}; switching to {} capture", target.as_str()); + self.active = target; + self.last_fallback_reason = Some(full.clone()); + FallbackDecision::Transition { + from, + to: target, + reason: full, + } + } + None => { + let give_up = format!( + "{reason}, and no lower-ranked capture strategy is available ({} was the last \ + resort, signature={:?})", + self.active.as_str(), + signature + ); + self.last_fallback_reason = Some(give_up.clone()); + FallbackDecision::GiveUp { reason: give_up } + } + } + } +} + +#[derive(Debug, Clone, Default)] +pub struct FallbackSnapshot { + pub active_strategy: String, + pub last_fallback_reason: String, +} + +#[derive(Debug)] +pub struct FallbackTracker { + ranker: FallbackRanker, +} + +impl FallbackTracker { + pub fn new(initial: CaptureStrategy) -> Self { + Self { + ranker: FallbackRanker::new(initial), + } + } + + pub fn observe(&mut self, signature: FailureSignature) -> FallbackDecision { + self.ranker.observe(signature) + } + + pub fn active(&self) -> CaptureStrategy { + self.ranker.active() + } + + pub fn snapshot(&self) -> FallbackSnapshot { + FallbackSnapshot { + active_strategy: self.ranker.active().as_str().to_string(), + last_fallback_reason: self.ranker.last_fallback_reason().unwrap_or("").to_string(), + } + } +} + +pub fn decision_lifecycle(decision: &FallbackDecision) -> (&'static str, String) { + match decision { + FallbackDecision::Stay { retry_in_place } => ( + "diagnostic", + if *retry_in_place { + "capture hit a transient device loss; retrying the current strategy in place" + .to_string() + } else { + "capture is healthy on the current strategy".to_string() + }, + ), + FallbackDecision::Transition { from, to, reason } => ( + "error", + format!( + "fallback: {} -> {} ({reason}) [next-strategy={}]", + from.as_str(), + to.as_str(), + to.as_str() + ), + ), + FallbackDecision::Upgrade { from, to, reason } => ( + "diagnostic", + format!( + "upgrade: {} -> {} ({reason}) [next-strategy={}]", + from.as_str(), + to.as_str(), + to.as_str() + ), + ), + FallbackDecision::GiveUp { reason } => ( + "error", + format!("fallback exhausted: {reason} [next-strategy=none]"), + ), + } +} + +fn describe_failure(strategy: CaptureStrategy, signature: FailureSignature) -> String { + let what = match signature { + FailureSignature::InjectionFailed => "could not inject its capture hook", + FailureSignature::AntiCheatDenied => { + "is protected by anti-cheat, so the capture hook must not be injected" + } + FailureSignature::NoFramesWithinTimeout => "produced no frames within the start timeout", + FailureSignature::DeviceLost => "lost its capture device", + FailureSignature::UnsupportedTransport => "used a frame transport this build cannot read", + FailureSignature::UnsupportedFormat => "used a swapchain format the fast path cannot read", + FailureSignature::WindowGone => "lost its capture target", + FailureSignature::FramesRecovered => "recovered", + }; + format!("{} capture {what}", strategy.as_str()) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::game_capture_abi::{ + GAME_CAPTURE_FALLBACK_DEVICE_LOST, GAME_CAPTURE_FALLBACK_EXTERNAL_MEMORY_UNSUPPORTED, + GAME_CAPTURE_FALLBACK_FORCED_CPU, GAME_CAPTURE_FALLBACK_FORMAT_UNSUPPORTED, + GAME_CAPTURE_FALLBACK_MULTISAMPLED, GAME_CAPTURE_FALLBACK_NONE, + GAME_CAPTURE_FALLBACK_SHARED_TEXTURE_UNSUPPORTED, + }; + + #[test] + fn ranking_order_is_best_to_worst() { + assert!(CaptureStrategy::GameHook.rank() < CaptureStrategy::Wgc.rank()); + assert!(CaptureStrategy::Wgc.rank() < CaptureStrategy::DxgiDuplication.rank()); + assert!(CaptureStrategy::DxgiDuplication.rank() < CaptureStrategy::WindowGdi.rank()); + assert_eq!( + CaptureStrategy::GameHook.next_worse(), + Some(CaptureStrategy::Wgc) + ); + assert_eq!( + CaptureStrategy::Wgc.next_worse(), + Some(CaptureStrategy::DxgiDuplication) + ); + assert_eq!( + CaptureStrategy::DxgiDuplication.next_worse(), + Some(CaptureStrategy::WindowGdi) + ); + assert_eq!(CaptureStrategy::WindowGdi.next_worse(), None); + } + + #[test] + fn injection_failure_falls_back_to_wgc() { + let mut ranker = FallbackRanker::new(CaptureStrategy::GameHook); + match ranker.observe(FailureSignature::InjectionFailed) { + FallbackDecision::Transition { from, to, .. } => { + assert_eq!(from, CaptureStrategy::GameHook); + assert_eq!(to, CaptureStrategy::Wgc); + } + other => panic!("expected Transition, got {other:?}"), + } + assert_eq!(ranker.active(), CaptureStrategy::Wgc); + assert!(ranker.is_disqualified(CaptureStrategy::GameHook)); + assert!(ranker.last_fallback_reason().is_some()); + } + + #[test] + fn no_frames_timeout_falls_back_but_does_not_disqualify() { + let mut ranker = FallbackRanker::new(CaptureStrategy::GameHook); + match ranker.observe(FailureSignature::NoFramesWithinTimeout) { + FallbackDecision::Transition { to, .. } => { + assert_eq!(to, CaptureStrategy::Wgc) + } + other => panic!("expected Transition, got {other:?}"), + } + assert!(!ranker.is_disqualified(CaptureStrategy::GameHook)); + } + + #[test] + fn wgc_failure_falls_back_to_dxgi() { + let mut ranker = FallbackRanker::new(CaptureStrategy::Wgc); + match ranker.observe(FailureSignature::NoFramesWithinTimeout) { + FallbackDecision::Transition { from, to, .. } => { + assert_eq!(from, CaptureStrategy::Wgc); + assert_eq!(to, CaptureStrategy::DxgiDuplication); + } + other => panic!("expected Transition, got {other:?}"), + } + assert!(!ranker.is_disqualified(CaptureStrategy::Wgc)); + } + + #[test] + fn anti_cheat_denied_disqualifies_game_hook() { + let mut ranker = FallbackRanker::new(CaptureStrategy::GameHook); + let decision = ranker.observe(FailureSignature::AntiCheatDenied); + assert!(matches!( + decision, + FallbackDecision::Transition { + to: CaptureStrategy::Wgc, + .. + } + )); + assert!(ranker.is_disqualified(CaptureStrategy::GameHook)); + } + + #[test] + fn device_lost_retries_in_place_then_falls_back() { + let mut ranker = FallbackRanker::new(CaptureStrategy::DxgiDuplication); + for _ in 0..MAX_DEVICE_LOST_RETRIES { + assert_eq!( + ranker.observe(FailureSignature::DeviceLost), + FallbackDecision::Stay { + retry_in_place: true + } + ); + assert_eq!(ranker.active(), CaptureStrategy::DxgiDuplication); + } + match ranker.observe(FailureSignature::DeviceLost) { + FallbackDecision::Transition { to, .. } => assert_eq!(to, CaptureStrategy::WindowGdi), + other => panic!("expected Transition after retry budget, got {other:?}"), + } + assert!(!ranker.is_disqualified(CaptureStrategy::DxgiDuplication)); + } + + #[test] + fn frames_recovered_resets_device_lost_streak() { + let mut ranker = FallbackRanker::new(CaptureStrategy::DxgiDuplication); + ranker.observe(FailureSignature::DeviceLost); + ranker.observe(FailureSignature::DeviceLost); + assert_eq!( + ranker.observe(FailureSignature::FramesRecovered), + FallbackDecision::Stay { + retry_in_place: false + } + ); + for _ in 0..MAX_DEVICE_LOST_RETRIES { + assert_eq!( + ranker.observe(FailureSignature::DeviceLost), + FallbackDecision::Stay { + retry_in_place: true + } + ); + } + } + + #[test] + fn window_gone_gives_up_from_any_strategy() { + for start in CaptureStrategy::RANKED { + let mut ranker = FallbackRanker::new(start); + match ranker.observe(FailureSignature::WindowGone) { + FallbackDecision::GiveUp { .. } => {} + other => panic!("expected GiveUp from {start:?}, got {other:?}"), + } + } + } + + #[test] + fn last_resort_failure_gives_up() { + let mut ranker = FallbackRanker::new(CaptureStrategy::WindowGdi); + match ranker.observe(FailureSignature::NoFramesWithinTimeout) { + FallbackDecision::GiveUp { reason } => assert!(reason.contains("window-gdi")), + other => panic!("expected GiveUp at last resort, got {other:?}"), + } + } + + #[test] + fn full_descent_game_to_wgc_to_dxgi_to_window_then_give_up() { + let mut ranker = FallbackRanker::new(CaptureStrategy::GameHook); + assert!(matches!( + ranker.observe(FailureSignature::InjectionFailed), + FallbackDecision::Transition { + to: CaptureStrategy::Wgc, + .. + } + )); + assert!(matches!( + ranker.observe(FailureSignature::UnsupportedTransport), + FallbackDecision::Transition { + to: CaptureStrategy::DxgiDuplication, + .. + } + )); + assert!(matches!( + ranker.observe(FailureSignature::UnsupportedTransport), + FallbackDecision::Transition { + to: CaptureStrategy::WindowGdi, + .. + } + )); + assert!(matches!( + ranker.observe(FailureSignature::UnsupportedFormat), + FallbackDecision::GiveUp { .. } + )); + } + + #[test] + fn stable_recovery_climbs_back_up_skipping_disqualified() { + let mut ranker = FallbackRanker::new(CaptureStrategy::GameHook); + ranker.observe(FailureSignature::InjectionFailed); + for _ in 0..=MAX_DEVICE_LOST_RETRIES { + ranker.observe(FailureSignature::DeviceLost); + } + assert_eq!(ranker.active(), CaptureStrategy::DxgiDuplication); + + assert_eq!( + ranker.observe(FailureSignature::FramesRecovered), + FallbackDecision::Stay { + retry_in_place: false + } + ); + match ranker.observe(FailureSignature::FramesRecovered) { + FallbackDecision::Upgrade { from, to, .. } => { + assert_eq!(from, CaptureStrategy::DxgiDuplication); + assert_eq!(to, CaptureStrategy::Wgc); + } + other => panic!("expected Upgrade, got {other:?}"), + } + assert_eq!(ranker.active(), CaptureStrategy::Wgc); + } + + #[test] + fn no_upgrade_when_already_best() { + let mut ranker = FallbackRanker::new(CaptureStrategy::GameHook); + for _ in 0..(STABLE_FRAMES_BEFORE_UPGRADE + 2) { + assert_eq!( + ranker.observe(FailureSignature::FramesRecovered), + FallbackDecision::Stay { + retry_in_place: false + } + ); + } + assert_eq!(ranker.active(), CaptureStrategy::GameHook); + } + + #[test] + fn upgrade_does_not_revive_disqualified_better_strategy() { + let mut ranker = FallbackRanker::new(CaptureStrategy::GameHook); + ranker.observe(FailureSignature::AntiCheatDenied); + assert_eq!(ranker.active(), CaptureStrategy::Wgc); + for _ in 0..(STABLE_FRAMES_BEFORE_UPGRADE + 2) { + assert_eq!( + ranker.observe(FailureSignature::FramesRecovered), + FallbackDecision::Stay { + retry_in_place: false + } + ); + } + assert_eq!(ranker.active(), CaptureStrategy::Wgc); + } + + #[test] + fn fallback_reason_maps_from_abi_reason() { + assert_eq!( + FailureSignature::from_fallback_reason(GAME_CAPTURE_FALLBACK_NONE), + None + ); + assert_eq!( + FailureSignature::from_fallback_reason( + GAME_CAPTURE_FALLBACK_SHARED_TEXTURE_UNSUPPORTED + ), + Some(FailureSignature::UnsupportedTransport) + ); + assert_eq!( + FailureSignature::from_fallback_reason( + GAME_CAPTURE_FALLBACK_EXTERNAL_MEMORY_UNSUPPORTED + ), + Some(FailureSignature::UnsupportedTransport) + ); + assert_eq!( + FailureSignature::from_fallback_reason(GAME_CAPTURE_FALLBACK_FORMAT_UNSUPPORTED), + Some(FailureSignature::UnsupportedFormat) + ); + assert_eq!( + FailureSignature::from_fallback_reason(GAME_CAPTURE_FALLBACK_MULTISAMPLED), + Some(FailureSignature::UnsupportedFormat) + ); + assert_eq!( + FailureSignature::from_fallback_reason(GAME_CAPTURE_FALLBACK_DEVICE_LOST), + Some(FailureSignature::DeviceLost) + ); + assert_eq!( + FailureSignature::from_fallback_reason(GAME_CAPTURE_FALLBACK_FORCED_CPU), + None + ); + assert_eq!(FailureSignature::from_fallback_reason(u32::MAX), None); + } + + #[test] + fn unsupported_transport_disqualifies_current_strategy() { + let mut ranker = FallbackRanker::new(CaptureStrategy::GameHook); + match ranker.observe(FailureSignature::UnsupportedTransport) { + FallbackDecision::Transition { from, to, reason } => { + assert_eq!(from, CaptureStrategy::GameHook); + assert_eq!(to, CaptureStrategy::Wgc); + assert!(reason.contains("transport")); + } + other => panic!("expected Transition, got {other:?}"), + } + assert!(ranker.is_disqualified(CaptureStrategy::GameHook)); + } + + #[test] + fn forced_cpu_reason_is_not_a_failure_signature() { + let mut ranker = FallbackRanker::new(CaptureStrategy::GameHook); + if let Some(signature) = + FailureSignature::from_fallback_reason(GAME_CAPTURE_FALLBACK_FORCED_CPU) + { + ranker.observe(signature); + } + assert_eq!(ranker.active(), CaptureStrategy::GameHook); + assert!(!ranker.is_disqualified(CaptureStrategy::GameHook)); + } + + #[test] + fn strategy_names_are_stable_for_the_js_seam() { + assert_eq!(CaptureStrategy::GameHook.as_str(), "game-hook"); + assert_eq!(CaptureStrategy::Wgc.as_str(), "wgc"); + assert_eq!( + CaptureStrategy::DxgiDuplication.as_str(), + "dxgi-duplication" + ); + assert_eq!(CaptureStrategy::WindowGdi.as_str(), "window-gdi"); + } + + #[test] + fn tracker_snapshot_tracks_active_and_reason() { + let mut tracker = FallbackTracker::new(CaptureStrategy::GameHook); + let snap = tracker.snapshot(); + assert_eq!(snap.active_strategy, "game-hook"); + assert_eq!(snap.last_fallback_reason, "", "no reason before a fallback"); + + tracker.observe(FailureSignature::InjectionFailed); + let snap = tracker.snapshot(); + assert_eq!(snap.active_strategy, "wgc"); + assert!(snap.last_fallback_reason.contains("wgc")); + } + + #[test] + fn decision_lifecycle_maps_to_event_pairs() { + let transition = FallbackDecision::Transition { + from: CaptureStrategy::GameHook, + to: CaptureStrategy::DxgiDuplication, + reason: "game-hook capture could not inject its capture hook".to_string(), + }; + let (kind, msg) = decision_lifecycle(&transition); + assert_eq!(kind, "error"); + assert!(msg.contains("next-strategy=dxgi-duplication")); + + let upgrade = FallbackDecision::Upgrade { + from: CaptureStrategy::WindowGdi, + to: CaptureStrategy::DxgiDuplication, + reason: "stable".to_string(), + }; + let (kind, msg) = decision_lifecycle(&upgrade); + assert_eq!(kind, "diagnostic"); + assert!(msg.contains("next-strategy=dxgi-duplication")); + + let give_up = FallbackDecision::GiveUp { + reason: "window-gdi was the last resort".to_string(), + }; + let (kind, msg) = decision_lifecycle(&give_up); + assert_eq!(kind, "error"); + assert!(msg.contains("next-strategy=none")); + + let (kind, _) = decision_lifecycle(&FallbackDecision::Stay { + retry_in_place: true, + }); + assert_eq!(kind, "diagnostic"); + } +} diff --git a/fluxer_desktop/native/win-game-capture/src/game_capture.rs b/fluxer_desktop/native/win-game-capture/src/game_capture.rs new file mode 100644 index 0000000..316c636 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/src/game_capture.rs @@ -0,0 +1,1700 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::fallback; +use crate::game_capture_abi::{ENV_FORCE_CPU, ENV_VERBOSE, env_flag_enabled}; +use crate::nv12_gpu::Nv12GpuConverter; +use crate::{ + CaptureInner, + compatibility::{InjectionPolicy, injection_policy_for_window}, + dxgi_capture::{resolve_output_size, wall_clock_us}, + emit_lifecycle, emit_shared_texture_frame, + game_capture_abi::{ + GAME_CAPTURE_API_OPENGL, GAME_CAPTURE_BUFFER_COUNT, + GAME_CAPTURE_CONTROL_DISABLE_SHARED_TEXTURE, GAME_CAPTURE_FRAME_PREFIX, + GAME_CAPTURE_INFO_PREFIX, GAME_CAPTURE_KEEPALIVE_PREFIX, GAME_CAPTURE_READY_PREFIX, + GAME_CAPTURE_STATE_ERROR, GAME_CAPTURE_STATE_RESIZE_REQUIRED, GAME_CAPTURE_STATE_STOPPED, + GAME_CAPTURE_STOP_PREFIX, GAME_CAPTURE_TRANSPORT_MEMORY, + GAME_CAPTURE_TRANSPORT_SHARED_TEXTURE, GameCaptureSharedInfo, mutex_name, object_name, + presented_recently, qpc_now_us, shared_frame_mapping_size, + }, + note_cpu_fallback_frame_dropped, note_media_frame_without_sink, +}; +use std::{ + ffi::c_void, + path::Path, + ptr::{null, null_mut}, + sync::{Arc, Mutex, atomic::Ordering}, +}; +use windows::Win32::{ + Foundation::{HANDLE as WinHandle, HMODULE}, + Graphics::{ + Direct3D::{D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_UNKNOWN}, + Direct3D11::{ + D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_SDK_VERSION, D3D11_TEXTURE2D_DESC, + D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext, ID3D11Texture2D, + }, + Dxgi::{CreateDXGIFactory1, IDXGIAdapter, IDXGIDevice, IDXGIFactory1}, + }, +}; +use windows::core::Interface; +use windows_sys::Win32::{ + Foundation::{ + CloseHandle, FreeLibrary, HANDLE, HINSTANCE, HMODULE as WinSysHmodule, HWND, + INVALID_HANDLE_VALUE, LPARAM, RECT, WAIT_ABANDONED, WAIT_OBJECT_0, WAIT_TIMEOUT, + }, + Graphics::Gdi::{ + EnumDisplayMonitors, GetMonitorInfoW, HMONITOR, MONITOR_DEFAULTTONEAREST, MONITORINFO, + MonitorFromRect, + }, + System::{ + Diagnostics::Debug::WriteProcessMemory, + LibraryLoader::{GetModuleHandleW, GetProcAddress, LoadLibraryW}, + Memory::{ + CreateFileMappingW, FILE_MAP_ALL_ACCESS, MEM_COMMIT, MEM_RELEASE, MEM_RESERVE, + MEMORY_MAPPED_VIEW_ADDRESS, MapViewOfFile, PAGE_READWRITE, UnmapViewOfFile, + VirtualAllocEx, VirtualFreeEx, + }, + SystemInformation::{IMAGE_FILE_MACHINE, IMAGE_FILE_MACHINE_UNKNOWN}, + Threading::{ + CreateEventW, CreateMutexW, CreateRemoteThread, GetCurrentProcessId, IsWow64Process, + IsWow64Process2, OpenProcess, PROCESS_CREATE_THREAD, PROCESS_QUERY_INFORMATION, + PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_VM_OPERATION, PROCESS_VM_WRITE, ResetEvent, + SetEvent, WaitForSingleObject, + }, + }, + UI::WindowsAndMessaging::{ + EnumWindows, GW_OWNER, GWL_STYLE, GetClientRect, GetForegroundWindow, GetWindow, + GetWindowLongPtrW, GetWindowRect, GetWindowThreadProcessId, HHOOK, IsWindow, + IsWindowVisible, PostThreadMessageW, SetWindowsHookExW, UnhookWindowsHookEx, WH_GETMESSAGE, + WM_NULL, WS_BORDER, WS_MAXIMIZE, + }, +}; + +const MAX_GAME_CAPTURE_DIMENSION: u32 = 8192; +const MAX_GAME_CAPTURE_FRAME_BYTES: usize = 384 * 1024 * 1024; +const FRAME_EVENT_WAIT_MS: u32 = 100; + +const HOOK_PROC_EXPORT: &[u8] = b"FluxerGetMsgProc\0"; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum InjectionMethod { + Auto, + RemoteThread, + SetWindowsHook, +} + +impl InjectionMethod { + fn resolve(explicit: Option<&str>) -> Self { + if let Some(explicit) = explicit { + return Self::parse(explicit).unwrap_or(Self::Auto); + } + if let Some(value) = std::env::var_os(crate::game_capture_abi::ENV_INJECT_METHOD) { + return Self::parse(&value.to_string_lossy()).unwrap_or(Self::Auto); + } + Self::Auto + } + + fn parse(value: &str) -> Option { + match value.trim().to_ascii_lowercase().as_str() { + "auto" => Some(Self::Auto), + "remote-thread" => Some(Self::RemoteThread), + "set-windows-hook" => Some(Self::SetWindowsHook), + _ => None, + } + } + + fn as_str(self) -> &'static str { + match self { + Self::Auto => "auto", + Self::RemoteThread => "remote-thread", + Self::SetWindowsHook => "set-windows-hook", + } + } +} + +struct InstalledWindowsHook { + hook: HHOOK, + module: WinSysHmodule, +} + +unsafe impl Send for InstalledWindowsHook {} +unsafe impl Sync for InstalledWindowsHook {} + +impl Drop for InstalledWindowsHook { + fn drop(&mut self) { + unsafe { + if !self.hook.is_null() { + UnhookWindowsHookEx(self.hook); + self.hook = null_mut(); + } + if !self.module.is_null() { + FreeLibrary(self.module); + self.module = null_mut(); + } + } + } +} + +const STALL_THRESHOLD: std::time::Duration = std::time::Duration::from_millis(1000); +const RECENT_PRESENT_WINDOW_US: i64 = 1_000_000; + +#[derive(Debug)] +struct OwnedHandle(HANDLE); + +impl OwnedHandle { + fn new(handle: HANDLE, context: &str) -> Result { + if handle.is_null() || handle == INVALID_HANDLE_VALUE { + return Err(format!("{context}: failed to create/open handle")); + } + Ok(Self(handle)) + } + + fn raw(&self) -> HANDLE { + self.0 + } +} + +unsafe impl Send for OwnedHandle {} +unsafe impl Sync for OwnedHandle {} + +impl Drop for OwnedHandle { + fn drop(&mut self) { + if !self.0.is_null() && self.0 != INVALID_HANDLE_VALUE { + unsafe { + CloseHandle(self.0); + } + } + self.0 = null_mut(); + } +} + +#[derive(Debug)] +struct MappedView { + _mapping: OwnedHandle, + ptr: *mut T, +} + +unsafe impl Send for MappedView {} +unsafe impl Sync for MappedView {} + +impl MappedView { + fn create(name: &str, size: usize) -> Result { + if size == 0 || size > u32::MAX as usize { + return Err(format!("invalid mapping size for {name}: {size}")); + } + let wide_name = to_wide(name); + let mapping = unsafe { + CreateFileMappingW( + INVALID_HANDLE_VALUE, + null(), + PAGE_READWRITE, + 0, + size as u32, + wide_name.as_ptr(), + ) + }; + let mapping = OwnedHandle::new(mapping, "CreateFileMappingW")?; + let view = unsafe { MapViewOfFile(mapping.raw(), FILE_MAP_ALL_ACCESS, 0, 0, size) }; + if view.Value.is_null() { + return Err(format!("MapViewOfFile failed for {name}")); + } + Ok(Self { + _mapping: mapping, + ptr: view.Value.cast(), + }) + } +} + +impl Drop for MappedView { + fn drop(&mut self) { + if !self.ptr.is_null() { + unsafe { + UnmapViewOfFile(MEMORY_MAPPED_VIEW_ADDRESS { + Value: self.ptr.cast(), + }); + } + } + self.ptr = null_mut(); + } +} + +pub struct GameCaptureSession { + target_hwnd: HWND, + capture_width: u32, + capture_height: u32, + output_width: u32, + output_height: u32, + info: MappedView, + frames: MappedView, + frame_buffer_capacity: usize, + ready_event: OwnedHandle, + stop_event: OwnedHandle, + _keepalive_mutex: OwnedHandle, + frame_mutexes: [OwnedHandle; GAME_CAPTURE_BUFFER_COUNT], + force_cpu_readback: bool, + requested_injection_method: InjectionMethod, + used_injection_method: InjectionMethod, + native_texture: Mutex>, + _windows_hook: Option, +} + +unsafe impl Send for GameCaptureSession {} +unsafe impl Sync for GameCaptureSession {} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub struct NativeTextureHandleInfo { + pub handle: u64, + pub width: u32, + pub height: u32, + pub dxgi_format: u32, + pub timestamp_us: i64, +} + +struct SharedTextureReader { + device: ID3D11Device, + context: ID3D11DeviceContext, + handle: u64, + width: u32, + height: u32, + format: u32, + source_format: crate::hdr::SourceFormat, + nv12: Option, +} + +impl Drop for GameCaptureSession { + fn drop(&mut self) { + unsafe { + SetEvent(self.stop_event.raw()); + } + } +} + +impl GameCaptureSession { + #[allow(clippy::too_many_arguments)] + pub fn new( + source_id: &str, + source_kind: &str, + requested_width: Option, + requested_height: Option, + frame_rate: u32, + hook_path: &str, + hook_path_x86: Option<&str>, + injection_method: Option<&str>, + ) -> Result { + let requested_injection_method = InjectionMethod::resolve(injection_method); + let target_hwnd = resolve_game_capture_target(source_id, source_kind)?; + let target_pid = target_process_id(target_hwnd)?; + if target_pid == unsafe { GetCurrentProcessId() } { + return Err("refusing to inject game capture hook into Fluxer".into()); + } + + let force_cpu_readback = match injection_policy_for_window(target_pid, target_hwnd) { + InjectionPolicy::Deny(reason) => return Err(reason), + InjectionPolicy::ForceCpuReadback => true, + InjectionPolicy::Allow => false, + }; + + let target_is_32_bit = target_process_is_32_bit(target_pid)?; + let selected_hook_path = if target_is_32_bit { + hook_path_x86.ok_or( + "target game process is 32-bit but no 32-bit game capture hook DLL was provided", + )? + } else { + hook_path + }; + + let (capture_width, capture_height) = window_capture_size(target_hwnd)?; + validate_capture_size(capture_width, capture_height)?; + let (output_width, output_height) = resolve_output_size( + capture_width, + capture_height, + requested_width, + requested_height, + ); + let (buffer_width, buffer_height) = + shared_buffer_dimensions_for_window(target_hwnd, capture_width, capture_height); + let frame_map_size = shared_frame_mapping_size(buffer_width, buffer_height) + .ok_or("game capture frame mapping size overflow")?; + if frame_map_size > MAX_GAME_CAPTURE_FRAME_BYTES { + return Err(format!( + "game capture frame mapping too large: {frame_map_size} bytes" + )); + } + + let info_name = object_name(GAME_CAPTURE_INFO_PREFIX, target_pid); + let frame_name = object_name(GAME_CAPTURE_FRAME_PREFIX, target_pid); + let ready_name = object_name(GAME_CAPTURE_READY_PREFIX, target_pid); + let stop_name = object_name(GAME_CAPTURE_STOP_PREFIX, target_pid); + let keepalive_name = object_name(GAME_CAPTURE_KEEPALIVE_PREFIX, target_pid); + let mutex_names = [mutex_name(target_pid, 0), mutex_name(target_pid, 1)]; + + let info = MappedView::::create( + &info_name, + std::mem::size_of::(), + )?; + unsafe { + std::ptr::write_volatile( + info.ptr, + GameCaptureSharedInfo::new( + target_hwnd as usize as u64, + buffer_width, + buffer_height, + frame_rate, + ), + ); + } + let frames = MappedView::::create(&frame_name, frame_map_size)?; + let ready_event = create_event(&ready_name, false, false)?; + let stop_event = create_event(&stop_name, true, false)?; + let keepalive_mutex = create_mutex(&keepalive_name)?; + let frame_mutexes = [ + create_mutex(&mutex_names[0])?, + create_mutex(&mutex_names[1])?, + ]; + unsafe { + ResetEvent(ready_event.raw()); + ResetEvent(stop_event.raw()); + } + let Injected { + method: used_injection_method, + windows_hook, + } = inject( + requested_injection_method, + target_hwnd, + target_pid, + target_is_32_bit, + selected_hook_path, + )?; + + Ok(Self { + target_hwnd, + capture_width: buffer_width, + capture_height: buffer_height, + output_width, + output_height, + info, + frames, + frame_buffer_capacity: frame_map_size / GAME_CAPTURE_BUFFER_COUNT, + ready_event, + stop_event, + _keepalive_mutex: keepalive_mutex, + frame_mutexes, + force_cpu_readback, + requested_injection_method, + used_injection_method, + native_texture: Mutex::new(None), + _windows_hook: windows_hook, + }) + } + + #[allow(clippy::misnamed_getters)] + pub fn capture_width(&self) -> u32 { + self.output_width + } + + #[allow(clippy::misnamed_getters)] + pub fn capture_height(&self) -> u32 { + self.output_height + } + + pub fn force_cpu_readback(&self) -> bool { + self.force_cpu_readback + } + + pub fn requested_injection_method(&self) -> &'static str { + self.requested_injection_method.as_str() + } + + pub fn used_injection_method(&self) -> &'static str { + self.used_injection_method.as_str() + } + + pub fn read_shared_info(&self) -> Option { + let info = unsafe { std::ptr::read_volatile(self.info.ptr) }; + if info.magic == crate::game_capture_abi::GAME_CAPTURE_MAGIC { + Some(info) + } else { + None + } + } + + pub fn read_native_texture_info(&self) -> Option { + self.native_texture.lock().ok().and_then(|guard| *guard) + } + + fn set_native_texture_info(&self, info: Option) { + if let Ok(mut guard) = self.native_texture.lock() { + *guard = info; + } + } + + fn request_hook_disable_shared_texture(&self) { + unsafe { + let control = &mut (*self.info.ptr).control; + std::ptr::write_volatile( + control, + std::ptr::read_volatile(control) | GAME_CAPTURE_CONTROL_DISABLE_SHARED_TEXTURE, + ); + } + } +} + +fn to_wide(value: &str) -> Vec { + value.encode_utf16().chain(std::iter::once(0)).collect() +} + +fn create_event( + name: &str, + manual_reset: bool, + initial_state: bool, +) -> Result { + let wide_name = to_wide(name); + let handle = unsafe { + CreateEventW( + null(), + i32::from(manual_reset), + i32::from(initial_state), + wide_name.as_ptr(), + ) + }; + OwnedHandle::new(handle, "CreateEventW") +} + +fn create_mutex(name: &str) -> Result { + let wide_name = to_wide(name); + let handle = unsafe { CreateMutexW(null(), 0, wide_name.as_ptr()) }; + OwnedHandle::new(handle, "CreateMutexW") +} + +fn verbose_log(message: &str) { + if !env_flag_enabled(ENV_VERBOSE) { + return; + } + let text = format!("[fluxer-game-capture] {message}"); + let line = to_wide(&text); + unsafe { + windows_sys::Win32::System::Diagnostics::Debug::OutputDebugStringW(line.as_ptr()); + } + use std::io::Write; + let _ = writeln!(std::io::stderr(), "{text}"); +} + +struct Injected { + method: InjectionMethod, + windows_hook: Option, +} + +const HOST_IS_32_BIT: bool = cfg!(target_pointer_width = "32"); + +fn inject( + method: InjectionMethod, + target_hwnd: HWND, + target_pid: u32, + target_is_32_bit: bool, + hook_path: &str, +) -> Result { + let set_windows_hook_possible = target_is_32_bit == HOST_IS_32_BIT; + verbose_log(&format!( + "injecting hook (method={}, target_pid={target_pid}, target_32bit={target_is_32_bit}, \ + set_windows_hook_possible={set_windows_hook_possible})", + method.as_str() + )); + + match method { + InjectionMethod::RemoteThread => { + inject_via_remote_thread(target_pid, hook_path, target_is_32_bit)?; + Ok(Injected { + method: InjectionMethod::RemoteThread, + windows_hook: None, + }) + } + InjectionMethod::SetWindowsHook => { + if !set_windows_hook_possible { + return Err( + "the SetWindowsHookEx injection method requires the target game to match \ + Fluxer's bitness; this target is a different bitness, so pick the \ + remote-thread or automatic method instead" + .into(), + ); + } + let windows_hook = inject_via_set_windows_hook(target_hwnd, target_pid, hook_path)?; + Ok(Injected { + method: InjectionMethod::SetWindowsHook, + windows_hook: Some(windows_hook), + }) + } + InjectionMethod::Auto => { + match inject_via_remote_thread(target_pid, hook_path, target_is_32_bit) { + Ok(()) => Ok(Injected { + method: InjectionMethod::RemoteThread, + windows_hook: None, + }), + Err(remote_thread_error) => { + if !set_windows_hook_possible { + return Err(remote_thread_error); + } + verbose_log(&format!( + "remote-thread injection failed ({remote_thread_error}); falling back to \ + SetWindowsHookEx" + )); + match inject_via_set_windows_hook(target_hwnd, target_pid, hook_path) { + Ok(windows_hook) => Ok(Injected { + method: InjectionMethod::SetWindowsHook, + windows_hook: Some(windows_hook), + }), + Err(set_hook_error) => Err(format!( + "game capture injection failed: remote-thread method failed \ + ({remote_thread_error}); SetWindowsHookEx fallback also failed \ + ({set_hook_error})" + )), + } + } + } + } + } +} + +fn inject_via_remote_thread( + target_pid: u32, + hook_path: &str, + target_is_32_bit: bool, +) -> Result<(), String> { + let hook_path_buf = Path::new(hook_path); + if !hook_path_buf.exists() { + return Err(format!( + "game capture hook DLL missing: {}", + hook_path_buf.display() + )); + } + + if target_is_32_bit != HOST_IS_32_BIT { + return inject_via_helper(target_pid, hook_path); + } + + let hook_path = to_wide(&hook_path_buf.to_string_lossy()); + let hook_path_bytes = hook_path + .len() + .checked_mul(std::mem::size_of::()) + .ok_or("hook path size overflow")?; + + unsafe { + let process = OpenProcess( + PROCESS_CREATE_THREAD + | PROCESS_QUERY_INFORMATION + | PROCESS_VM_OPERATION + | PROCESS_VM_WRITE, + 0, + target_pid, + ); + let process = OwnedHandle::new(process, "OpenProcess")?; + let remote_path = VirtualAllocEx( + process.raw(), + null(), + hook_path_bytes, + MEM_COMMIT | MEM_RESERVE, + PAGE_READWRITE, + ); + if remote_path.is_null() { + return Err("VirtualAllocEx failed while injecting game capture hook".into()); + } + + let mut written = 0usize; + let write_ok = WriteProcessMemory( + process.raw(), + remote_path, + hook_path.as_ptr().cast(), + hook_path_bytes, + &mut written, + ) != 0; + if !write_ok || written != hook_path_bytes { + VirtualFreeEx(process.raw(), remote_path, 0, MEM_RELEASE); + return Err("WriteProcessMemory failed while injecting game capture hook".into()); + } + + let kernel32_name = to_wide("kernel32.dll"); + let kernel32 = GetModuleHandleW(kernel32_name.as_ptr()); + if kernel32.is_null() { + VirtualFreeEx(process.raw(), remote_path, 0, MEM_RELEASE); + return Err( + "GetModuleHandleW(kernel32.dll) failed while injecting game capture hook".into(), + ); + } + let load_library = GetProcAddress(kernel32, c"LoadLibraryW".as_ptr().cast()); + let Some(load_library) = load_library else { + VirtualFreeEx(process.raw(), remote_path, 0, MEM_RELEASE); + return Err( + "GetProcAddress(LoadLibraryW) failed while injecting game capture hook".into(), + ); + }; + let start_routine: unsafe extern "system" fn(*mut c_void) -> u32 = + std::mem::transmute(load_library); + let thread = CreateRemoteThread( + process.raw(), + null(), + 0, + Some(start_routine), + remote_path, + 0, + null_mut(), + ); + let thread = match OwnedHandle::new(thread, "CreateRemoteThread") { + Ok(thread) => thread, + Err(error) => { + VirtualFreeEx(process.raw(), remote_path, 0, MEM_RELEASE); + return Err(error); + } + }; + let wait = WaitForSingleObject(thread.raw(), 5000); + VirtualFreeEx(process.raw(), remote_path, 0, MEM_RELEASE); + if wait != WAIT_OBJECT_0 && wait != WAIT_ABANDONED { + return Err("game capture hook injection timed out".into()); + } + } + Ok(()) +} + +fn helper_stage_reason(code: i32) -> &'static str { + match code { + 2 => "bad helper arguments", + 3 => "hook DLL missing (as seen by the helper)", + 4 => "OpenProcess failed in the helper (target gone or insufficient rights)", + 5 => "VirtualAllocEx failed in the helper", + 6 => "WriteProcessMemory failed in the helper", + 7 => "GetModuleHandleW(kernel32) failed in the helper", + 8 => "GetProcAddress(LoadLibraryW) failed in the helper", + 9 => "CreateRemoteThread failed in the helper", + 10 => "the remote LoadLibraryW thread timed out", + 11 => "LoadLibraryW returned NULL in the target (the hook DLL failed to load)", + 64 => "the inject-helper exe is not a Windows build", + _ => "unknown helper failure", + } +} + +fn helper_path_for_hook(hook_path: &str) -> Result { + let hook = Path::new(hook_path); + let dir = hook + .parent() + .ok_or("could not resolve the hook DLL directory for the inject-helper")?; + let file_name = hook + .file_name() + .and_then(|name| name.to_str()) + .ok_or("could not resolve the hook DLL file name for the inject-helper")?; + let tag = file_name + .strip_prefix("fluxer-game-hook.") + .and_then(|rest| rest.strip_suffix(".dll")) + .ok_or_else(|| { + format!("unexpected hook DLL file name (cannot derive inject-helper): {file_name}") + })?; + Ok(dir.join(format!("fluxer-inject-helper.{tag}.exe"))) +} + +fn inject_via_helper(target_pid: u32, hook_path: &str) -> Result<(), String> { + let helper = helper_path_for_hook(hook_path)?; + if !helper.exists() { + return Err(format!( + "cross-bitness game capture needs the matching-bitness inject-helper, but it is \ + missing: {}", + helper.display() + )); + } + + verbose_log(&format!( + "spawning cross-bitness inject-helper {} (target_pid={target_pid})", + helper.display() + )); + + let status = std::process::Command::new(&helper) + .arg(target_pid.to_string()) + .arg(hook_path) + .status() + .map_err(|error| { + format!( + "failed to launch the cross-bitness inject-helper ({}): {error}", + helper.display() + ) + })?; + + match status.code() { + Some(0) => Ok(()), + Some(code) => Err(format!( + "cross-bitness inject-helper failed (exit {code}: {})", + helper_stage_reason(code) + )), + None => Err(format!( + "cross-bitness inject-helper terminated without an exit code ({})", + helper.display() + )), + } +} + +fn inject_via_set_windows_hook( + target_hwnd: HWND, + target_pid: u32, + hook_path: &str, +) -> Result { + let hook_path = Path::new(hook_path); + if !hook_path.exists() { + return Err(format!( + "game capture hook DLL missing: {}", + hook_path.display() + )); + } + + let mut resolved_pid = 0u32; + let thread_id = unsafe { GetWindowThreadProcessId(target_hwnd, &mut resolved_pid) }; + if thread_id == 0 { + return Err("failed to resolve target window thread for SetWindowsHookEx injection".into()); + } + if resolved_pid != target_pid { + return Err( + "target window thread no longer belongs to the expected process; aborting \ + SetWindowsHookEx injection" + .into(), + ); + } + + let wide_path = to_wide(&hook_path.to_string_lossy()); + let module = unsafe { LoadLibraryW(wide_path.as_ptr()) }; + if module.is_null() { + return Err( + "LoadLibraryW failed to load the game capture hook DLL for SetWindowsHookEx injection" + .into(), + ); + } + + let proc = unsafe { GetProcAddress(module, HOOK_PROC_EXPORT.as_ptr()) }; + let Some(proc) = proc else { + unsafe { + FreeLibrary(module); + } + return Err( + "GetProcAddress(FluxerGetMsgProc) failed; the hook DLL is missing the SetWindowsHookEx \ + entry point" + .into(), + ); + }; + + type HookFn = + unsafe extern "system" fn(i32, windows_sys::Win32::Foundation::WPARAM, LPARAM) -> isize; + let hook_fn: HookFn = unsafe { std::mem::transmute(proc) }; + let hook = + unsafe { SetWindowsHookExW(WH_GETMESSAGE, Some(hook_fn), module as HINSTANCE, thread_id) }; + if hook.is_null() { + unsafe { + FreeLibrary(module); + } + return Err("SetWindowsHookExW failed while injecting the game capture hook".into()); + } + + unsafe { + PostThreadMessageW(thread_id, WM_NULL, 0, 0); + } + + Ok(InstalledWindowsHook { hook, module }) +} + +fn parse_hwnd_source_id(source_id: &str) -> Option { + let token = source_id.strip_prefix("window:")?.split(':').next()?; + let value = if let Some(hex) = token + .strip_prefix("0x") + .or_else(|| token.strip_prefix("0X")) + { + isize::from_str_radix(hex, 16).ok()? + } else { + token.parse::().ok()? + }; + let hwnd = value as HWND; + if unsafe { IsWindow(hwnd) } != 0 { + Some(hwnd) + } else { + None + } +} + +fn parse_screen_ordinal(source_id: &str) -> Option { + let token = source_id.strip_prefix("screen:")?.split(':').next()?; + token.parse::().ok() +} + +fn resolve_game_capture_target(source_id: &str, source_kind: &str) -> Result { + if let Some(hwnd) = parse_hwnd_source_id(source_id) { + return Ok(hwnd); + } + if source_kind == "game" || source_kind == "screen" { + let monitor = monitor_for_screen_source(source_id)?; + if let Some(hwnd) = find_fullscreen_window_on_monitor(monitor) { + return Ok(hwnd); + } + if let Some(hwnd) = find_foreground_fullscreen_window() { + return Ok(hwnd); + } + if let Some(hwnd) = find_fullscreen_window_on_any_monitor() { + return Ok(hwnd); + } + return Err("no fullscreen game window found on selected display".into()); + } + Err(format!( + "invalid game capture source: {source_kind}:{source_id}" + )) +} + +fn monitor_for_screen_source(source_id: &str) -> Result { + let ordinal = parse_screen_ordinal(source_id).unwrap_or(0); + let monitors = enumerate_monitors(); + monitors + .get(ordinal) + .copied() + .or_else(|| monitors.first().copied()) + .ok_or_else(|| "no monitors available for game capture".to_string()) +} + +fn enumerate_monitors() -> Vec { + unsafe extern "system" fn enum_monitor( + monitor: HMONITOR, + _hdc: windows_sys::Win32::Graphics::Gdi::HDC, + _rect: *mut RECT, + param: LPARAM, + ) -> i32 { + let monitors = &mut *(param as *mut Vec); + monitors.push(monitor); + 1 + } + let mut monitors = Vec::new(); + unsafe { + EnumDisplayMonitors( + null_mut(), + null(), + Some(enum_monitor), + &mut monitors as *mut _ as LPARAM, + ); + } + monitors +} + +fn target_process_id(hwnd: HWND) -> Result { + let mut pid = 0u32; + unsafe { + GetWindowThreadProcessId(hwnd, &mut pid); + } + if pid == 0 { + Err("failed to resolve target process id".into()) + } else { + Ok(pid) + } +} + +fn target_process_is_32_bit(target_pid: u32) -> Result { + let process = unsafe { + OpenProcess( + PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_QUERY_INFORMATION, + 0, + target_pid, + ) + }; + let process = OwnedHandle::new(process, "OpenProcess(query bitness)")?; + + let mut process_machine: IMAGE_FILE_MACHINE = IMAGE_FILE_MACHINE_UNKNOWN; + let mut native_machine: IMAGE_FILE_MACHINE = IMAGE_FILE_MACHINE_UNKNOWN; + let ok = unsafe { IsWow64Process2(process.raw(), &mut process_machine, &mut native_machine) }; + if ok != 0 { + return Ok(process_machine != IMAGE_FILE_MACHINE_UNKNOWN); + } + + let mut is_wow64: windows_sys::core::BOOL = 0; + let ok = unsafe { IsWow64Process(process.raw(), &mut is_wow64) }; + if ok == 0 { + return Err("failed to query target process bitness".into()); + } + Ok(is_wow64 != 0) +} + +fn window_capture_size(hwnd: HWND) -> Result<(u32, u32), String> { + let mut rect = RECT::default(); + if unsafe { GetClientRect(hwnd, &mut rect) } != 0 { + let width = (rect.right - rect.left).max(0) as u32; + let height = (rect.bottom - rect.top).max(0) as u32; + if width > 0 && height > 0 { + return Ok((width, height)); + } + } + if unsafe { GetWindowRect(hwnd, &mut rect) } == 0 { + return Err("failed to resolve target window size".into()); + } + let width = (rect.right - rect.left).max(1) as u32; + let height = (rect.bottom - rect.top).max(1) as u32; + Ok((width, height)) +} + +fn validate_capture_size(width: u32, height: u32) -> Result<(), String> { + if width == 0 || height == 0 { + return Err("game capture target has zero size".into()); + } + if width > MAX_GAME_CAPTURE_DIMENSION || height > MAX_GAME_CAPTURE_DIMENSION { + return Err(format!("game capture target too large: {width}x{height}")); + } + Ok(()) +} + +fn rect_size(rect: &RECT) -> Option<(u32, u32)> { + let width = (rect.right as i64 - rect.left as i64).abs(); + let height = (rect.bottom as i64 - rect.top as i64).abs(); + if width <= 0 || height <= 0 { + return None; + } + Some((width as u32, height as u32)) +} + +fn shared_buffer_dimensions_for_window( + hwnd: HWND, + capture_width: u32, + capture_height: u32, +) -> (u32, u32) { + let mut window_rect = RECT::default(); + let monitor = if unsafe { GetWindowRect(hwnd, &mut window_rect) } != 0 { + unsafe { MonitorFromRect(&window_rect, MONITOR_DEFAULTTONEAREST) } + } else { + null_mut() + }; + let Some(monitor_rect) = (!monitor.is_null()) + .then(|| monitor_rect(monitor)) + .flatten() + else { + return (capture_width, capture_height); + }; + let Some((monitor_width, monitor_height)) = rect_size(&monitor_rect) else { + return (capture_width, capture_height); + }; + let Some((buffer_width, buffer_height)) = choose_shared_buffer_dimensions( + capture_width, + capture_height, + monitor_width, + monitor_height, + ) else { + verbose_log(&format!( + "nearest monitor buffer {monitor_width}x{monitor_height} exceeds capture mapping limit; \ + using initial window buffer {capture_width}x{capture_height}" + )); + return (capture_width, capture_height); + }; + (buffer_width, buffer_height) +} + +fn choose_shared_buffer_dimensions( + capture_width: u32, + capture_height: u32, + monitor_width: u32, + monitor_height: u32, +) -> Option<(u32, u32)> { + let buffer_width = capture_width + .max(monitor_width) + .min(MAX_GAME_CAPTURE_DIMENSION); + let buffer_height = capture_height + .max(monitor_height) + .min(MAX_GAME_CAPTURE_DIMENSION); + let frame_map_size = shared_frame_mapping_size(buffer_width, buffer_height)?; + if frame_map_size > MAX_GAME_CAPTURE_FRAME_BYTES { + return None; + } + Some((buffer_width, buffer_height)) +} + +fn monitor_rect(monitor: HMONITOR) -> Option { + let mut info = MONITORINFO { + cbSize: std::mem::size_of::() as u32, + rcMonitor: RECT::default(), + rcWork: RECT::default(), + dwFlags: 0, + }; + if unsafe { GetMonitorInfoW(monitor, &mut info) } == 0 { + return None; + } + Some(info.rcMonitor) +} + +fn rect_matches_monitor(window_rect: &RECT, monitor_rect: &RECT) -> bool { + let tolerance = 2; + (window_rect.left - monitor_rect.left).abs() <= tolerance + && (window_rect.top - monitor_rect.top).abs() <= tolerance + && (window_rect.right - monitor_rect.right).abs() <= tolerance + && (window_rect.bottom - monitor_rect.bottom).abs() <= tolerance +} + +fn is_regular_maximized_window(hwnd: HWND) -> bool { + let style = unsafe { GetWindowLongPtrW(hwnd, GWL_STYLE) } as u32; + (style & WS_MAXIMIZE) != 0 && (style & WS_BORDER) != 0 +} + +fn is_fullscreen_window_on_monitor(hwnd: HWND, monitor: HMONITOR, monitor_rect: &RECT) -> bool { + if unsafe { IsWindowVisible(hwnd) } == 0 || !unsafe { GetWindow(hwnd, GW_OWNER) }.is_null() { + return false; + } + let mut pid = 0u32; + unsafe { + GetWindowThreadProcessId(hwnd, &mut pid); + } + if pid == 0 || pid == unsafe { GetCurrentProcessId() } { + return false; + } + if is_regular_maximized_window(hwnd) { + return false; + } + let mut rect = RECT::default(); + if unsafe { GetWindowRect(hwnd, &mut rect) } == 0 { + return false; + } + let window_monitor = unsafe { MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST) }; + window_monitor == monitor && rect_matches_monitor(&rect, monitor_rect) +} + +fn find_foreground_fullscreen_window() -> Option { + let hwnd = unsafe { GetForegroundWindow() }; + if hwnd.is_null() { + return None; + } + let mut rect = RECT::default(); + if unsafe { GetWindowRect(hwnd, &mut rect) } == 0 { + return None; + } + let monitor = unsafe { MonitorFromRect(&rect, MONITOR_DEFAULTTONEAREST) }; + let monitor_rect = monitor_rect(monitor)?; + if is_fullscreen_window_on_monitor(hwnd, monitor, &monitor_rect) { + Some(hwnd) + } else { + None + } +} + +fn find_fullscreen_window_on_any_monitor() -> Option { + for monitor in enumerate_monitors() { + if let Some(hwnd) = find_fullscreen_window_on_monitor(monitor) { + return Some(hwnd); + } + } + None +} + +fn find_fullscreen_window_on_monitor(monitor: HMONITOR) -> Option { + struct Search { + monitor: HMONITOR, + monitor_rect: RECT, + result: HWND, + own_pid: u32, + } + unsafe extern "system" fn enum_window(hwnd: HWND, param: LPARAM) -> i32 { + let search = &mut *(param as *mut Search); + let mut pid = 0u32; + GetWindowThreadProcessId(hwnd, &mut pid); + if pid != 0 + && pid != search.own_pid + && is_fullscreen_window_on_monitor(hwnd, search.monitor, &search.monitor_rect) + { + search.result = hwnd; + return 0; + } + 1 + } + + let monitor_rect = monitor_rect(monitor)?; + let mut search = Search { + monitor, + monitor_rect, + result: null_mut(), + own_pid: unsafe { GetCurrentProcessId() }, + }; + unsafe { + let _ = EnumWindows(Some(enum_window), &mut search as *mut _ as LPARAM); + } + if search.result.is_null() { + None + } else { + Some(search.result) + } +} + +pub(crate) fn create_shared_texture_device( + adapter: Option<&IDXGIAdapter>, +) -> Result<(ID3D11Device, ID3D11DeviceContext), String> { + let mut device = None; + let mut context = None; + let driver_type = if adapter.is_some() { + D3D_DRIVER_TYPE_UNKNOWN + } else { + D3D_DRIVER_TYPE_HARDWARE + }; + unsafe { + D3D11CreateDevice( + adapter, + driver_type, + HMODULE::default(), + D3D11_CREATE_DEVICE_BGRA_SUPPORT, + None, + D3D11_SDK_VERSION, + Some(&mut device), + None, + Some(&mut context), + ) + .map_err(|e| format!("D3D11CreateDevice for shared game texture: {e}"))?; + } + let device = device.ok_or("D3D11 shared texture device was None")?; + let context = context.ok_or("D3D11 shared texture context was None")?; + set_gpu_thread_priority(&device); + Ok((device, context)) +} + +fn set_gpu_thread_priority(device: &ID3D11Device) { + if let Ok(dxgi_device) = device.cast::() { + let _ = unsafe { dxgi_device.SetGPUThreadPriority(7) }; + } +} + +const SHARED_TEXTURE_ADAPTER_LIMIT: u32 = 16; + +fn shared_texture_adapter_candidates() -> Vec> { + let mut candidates: Vec> = + Vec::with_capacity(1 + SHARED_TEXTURE_ADAPTER_LIMIT as usize); + candidates.push(None); + if let Ok(factory) = unsafe { CreateDXGIFactory1::() } { + let mut index = 0u32; + while index < SHARED_TEXTURE_ADAPTER_LIMIT { + match unsafe { factory.EnumAdapters(index) } { + Ok(adapter) => candidates.push(Some(adapter)), + Err(_) => break, + } + index += 1; + } + } + assert!(!candidates.is_empty(), "default device candidate present"); + assert!( + candidates.len() <= 1 + SHARED_TEXTURE_ADAPTER_LIMIT as usize, + "adapter candidates bounded" + ); + candidates +} + +impl SharedTextureReader { + fn new( + handle: u64, + width: u32, + height: u32, + format: u32, + capture_flags: u32, + output_width: u32, + output_height: u32, + ) -> Result { + assert!(handle != 0, "shared texture handle is non-zero"); + let source_format = crate::hdr::SourceFormat::classify(format, capture_flags) + .ok_or_else(|| format!("unsupported game shared texture DXGI format {format}"))?; + let mut last_error = None; + for adapter in shared_texture_adapter_candidates() { + let (device, context) = match create_shared_texture_device(adapter.as_ref()) { + Ok(created) => created, + Err(error) => { + last_error = Some(error); + continue; + } + }; + match Self::new_with_device( + device, + context, + handle, + width, + height, + format, + source_format, + output_width, + output_height, + ) { + Ok(reader) => return Ok(reader), + Err(error) => last_error = Some(error), + } + } + Err(last_error.unwrap_or_else(|| "no D3D11 devices available for shared texture".into())) + } + + #[allow(clippy::too_many_arguments)] + fn new_with_device( + device: ID3D11Device, + context: ID3D11DeviceContext, + handle: u64, + width: u32, + height: u32, + format: u32, + source_format: crate::hdr::SourceFormat, + output_width: u32, + output_height: u32, + ) -> Result { + assert!(handle != 0, "shared texture handle is non-zero"); + assert!(width > 0, "shared texture width is positive"); + assert!(height > 0, "shared texture height is positive"); + let mut texture = None; + unsafe { + device + .OpenSharedResource::( + WinHandle(handle as usize as *mut c_void), + &mut texture, + ) + .map_err(|e| format!("OpenSharedResource game texture: {e}"))?; + } + let texture = texture.ok_or("OpenSharedResource returned no game texture")?; + let mut desc = D3D11_TEXTURE2D_DESC::default(); + unsafe { + texture.GetDesc(&mut desc); + } + if desc.Width != width || desc.Height != height || desc.Format.0 as u32 != format { + return Err(format!( + "game shared texture metadata mismatch: info={width}x{height}/{format}, texture={}x{}/{}", + desc.Width, desc.Height, desc.Format.0 + )); + } + let nv12 = if env_flag_enabled(ENV_FORCE_CPU) { + None + } else { + Nv12GpuConverter::new( + &device, + &context, + &texture, + width, + height, + output_width, + output_height, + source_format, + ) + }; + Ok(Self { + device, + context, + handle, + width, + height, + format, + source_format, + nv12, + }) + } + + fn rebuild_on_existing_device( + existing: SharedTextureReader, + info: &GameCaptureSharedInfo, + output_width: u32, + output_height: u32, + ) -> Result { + assert!( + info.texture_handle != 0, + "shared texture handle is non-zero" + ); + assert!(output_width > 0, "output width is positive"); + let source_format = + crate::hdr::SourceFormat::classify(info.dxgi_format, info.capture_flags).ok_or_else( + || { + format!( + "unsupported game shared texture DXGI format {}", + info.dxgi_format + ) + }, + )?; + Self::new_with_device( + existing.device, + existing.context, + info.texture_handle, + info.width, + info.height, + info.dxgi_format, + source_format, + output_width, + output_height, + ) + } + + fn matches( + &self, + handle: u64, + width: u32, + height: u32, + format: u32, + capture_flags: u32, + ) -> bool { + self.handle == handle + && self.width == width + && self.height == height + && self.format == format + && Some(self.source_format) == crate::hdr::SourceFormat::classify(format, capture_flags) + } +} + +fn emit_shared_texture_to_bus( + reader: &mut Option, + session: &GameCaptureSession, + info: &GameCaptureSharedInfo, + inner: &CaptureInner, + frame_sink: &crate::FrameSinkRef, +) -> Result { + let reader = shared_texture_reader(reader, session, info)?; + let timestamp_us = shared_texture_timestamp(info); + + if let Some(nv12) = reader.nv12.as_mut() { + match nv12.convert_shared_texture() { + Ok(frame) => { + session.set_native_texture_info(Some(NativeTextureHandleInfo { + handle: frame.handle, + width: frame.width, + height: frame.height, + dxgi_format: frame.dxgi_format, + timestamp_us, + })); + return Ok(emit_shared_texture_frame( + inner, + frame_sink, + frame.handle, + frame.width, + frame.height, + frame.dxgi_format, + timestamp_us, + )); + } + Err(error) => { + verbose_log(&format!( + "disabling GPU NV12 conversion for this shared texture after failure: {error}" + )); + reader.nv12 = None; + session.set_native_texture_info(None); + } + } + } + + session.set_native_texture_info(Some(NativeTextureHandleInfo { + handle: reader.handle, + width: reader.width, + height: reader.height, + dxgi_format: reader.format, + timestamp_us, + })); + Ok(emit_shared_texture_frame( + inner, + frame_sink, + reader.handle, + reader.width, + reader.height, + reader.format, + timestamp_us, + )) +} + +fn shared_texture_reader<'a>( + reader: &'a mut Option, + session: &GameCaptureSession, + info: &GameCaptureSharedInfo, +) -> Result<&'a mut SharedTextureReader, String> { + let handle = info.texture_handle; + if handle == 0 { + return Err("game shared texture handle was empty".into()); + } + if reader.as_ref().map(|reader| { + reader.matches( + handle, + info.width, + info.height, + info.dxgi_format, + info.capture_flags, + ) + }) != Some(true) + { + let reused = reader.take().and_then(|existing| { + SharedTextureReader::rebuild_on_existing_device( + existing, + info, + session.output_width, + session.output_height, + ) + .ok() + }); + *reader = Some(match reused { + Some(rebuilt) => rebuilt, + None => SharedTextureReader::new( + handle, + info.width, + info.height, + info.dxgi_format, + info.capture_flags, + session.output_width, + session.output_height, + )?, + }); + } + let reader = reader + .as_mut() + .ok_or_else(|| "game shared texture reader was not initialized".to_string())?; + Ok(reader) +} + +fn shared_texture_timestamp(info: &GameCaptureSharedInfo) -> i64 { + if info.timestamp_us > 0 { + info.timestamp_us + } else { + wall_clock_us() + } +} + +struct StallTracker { + last_frame_counter: Option, + last_change_at: std::time::Instant, + stall_emitted: bool, +} + +impl StallTracker { + fn new() -> Self { + Self { + last_frame_counter: None, + last_change_at: std::time::Instant::now(), + stall_emitted: false, + } + } + + fn observe( + &mut self, + inner: &Arc, + session: &GameCaptureSession, + info: &GameCaptureSharedInfo, + ) { + if info.magic != crate::game_capture_abi::GAME_CAPTURE_MAGIC { + self.last_change_at = std::time::Instant::now(); + return; + } + + let counter = info.frame_counter; + let advanced = match self.last_frame_counter { + Some(previous) => counter != previous, + None => { + self.last_frame_counter = Some(counter); + self.last_change_at = std::time::Instant::now(); + return; + } + }; + + if advanced { + self.last_frame_counter = Some(counter); + self.last_change_at = std::time::Instant::now(); + if self.stall_emitted { + self.stall_emitted = false; + emit_lifecycle( + inner, + "diagnostic", + "game capture frames resumed after a stall", + ); + } + return; + } + + if self.stall_emitted { + return; + } + if self.last_change_at.elapsed() < STALL_THRESHOLD { + return; + } + if unsafe { IsWindow(session.target_hwnd) } == 0 { + return; + } + if unsafe { IsWindowVisible(session.target_hwnd) } == 0 { + return; + } + + let presenting_recently = presented_recently( + info.present_clock, + info.last_present_timestamp_us, + wall_clock_us(), + qpc_now_us(), + RECENT_PRESENT_WINDOW_US, + ); + let stalled_ms = self.last_change_at.elapsed().as_millis(); + let detail = if presenting_recently { + format!( + "game capture stalled: producer is still presenting (api={}, transport={}) but \ + has published no new frame for {stalled_ms}ms; the fast path appears to be \ + failing (fallback_reason={}, dropped={})", + info.api_type, info.transport, info.fallback_reason, info.dropped_frame_counter + ) + } else { + format!( + "game capture stalled: no presents observed for {stalled_ms}ms (api={}, \ + transport={}, last_present_us={}); the game may be paused, occluded, or no \ + longer rendering", + info.api_type, info.transport, info.last_present_timestamp_us + ) + }; + emit_lifecycle(inner, "stalled", &detail); + self.stall_emitted = true; + } +} + +pub fn capture_loop(inner: &Arc, _frame_interval: std::time::Duration) { + let session = { + let guard = inner.game_session.lock(); + match guard.as_ref() { + Some(session) => Arc::clone(session), + None => { + emit_lifecycle(inner, "closed-clean", "no game capture session"); + return; + } + } + }; + + { + let requested = session.requested_injection_method(); + let used = session.used_injection_method(); + let detail = if requested == used { + format!("game capture injected via {used}") + } else { + format!("game capture injected via {used} (requested {requested})") + }; + emit_lifecycle(inner, "diagnostic", &detail); + } + + let capture_id = inner.capture_id.lock().clone(); + let mut shared_texture_reader: Option = None; + let mut stall_tracker = StallTracker::new(); + while inner.running.load(Ordering::Acquire) { + if unsafe { IsWindow(session.target_hwnd) } == 0 { + emit_lifecycle(inner, "closed", "game window closed"); + break; + } + let wait = unsafe { WaitForSingleObject(session.ready_event.raw(), FRAME_EVENT_WAIT_MS) }; + if wait == WAIT_TIMEOUT { + let info = unsafe { std::ptr::read_volatile(session.info.ptr) }; + stall_tracker.observe(inner, &session, &info); + continue; + } + if wait != WAIT_OBJECT_0 && wait != WAIT_ABANDONED { + emit_lifecycle(inner, "error", "game capture frame event wait failed"); + break; + } + let info = unsafe { std::ptr::read_volatile(session.info.ptr) }; + stall_tracker.observe(inner, &session, &info); + if info.state == GAME_CAPTURE_STATE_ERROR { + emit_lifecycle( + inner, + "diagnostic", + &format!("game capture hook reported error {}", info.last_error), + ); + if !handle_fallback_signature(inner, fallback::FailureSignature::DeviceLost) { + break; + } + continue; + } + if info.state == GAME_CAPTURE_STATE_RESIZE_REQUIRED { + handle_fallback_signature(inner, fallback::FailureSignature::UnsupportedFormat); + break; + } + if info.state == GAME_CAPTURE_STATE_STOPPED { + emit_lifecycle(inner, "closed", "game capture hook stopped"); + break; + } + if info.width == 0 || info.height == 0 || info.pitch == 0 { + continue; + } + if info.width > session.capture_width || info.height > session.capture_height { + emit_lifecycle(inner, "error", "game capture frame exceeded shared buffer"); + handle_fallback_signature(inner, fallback::FailureSignature::UnsupportedFormat); + break; + } + let Some(frame_sink) = crate::resolve_frame_sink(inner, capture_id.as_deref()) else { + note_media_frame_without_sink( + inner, + "Windows game capture frame dropped because no native frame sink is registered", + ); + continue; + }; + if info.transport == GAME_CAPTURE_TRANSPORT_SHARED_TEXTURE { + match emit_shared_texture_to_bus( + &mut shared_texture_reader, + &session, + &info, + inner, + &frame_sink, + ) { + Ok(_) => continue, + Err(error) => { + if info.api_type == GAME_CAPTURE_API_OPENGL { + emit_lifecycle( + inner, + "diagnostic", + &format!( + "OpenGL shared texture was rejected by the parent reader; \ + requesting hook CPU fallback: {error}" + ), + ); + session.request_hook_disable_shared_texture(); + shared_texture_reader = None; + continue; + } + emit_lifecycle(inner, "error", &error); + handle_fallback_signature( + inner, + fallback::FailureSignature::UnsupportedTransport, + ); + break; + } + } + } + if info.transport != GAME_CAPTURE_TRANSPORT_MEMORY { + emit_lifecycle( + inner, + "error", + "game capture hook used unknown frame transport", + ); + handle_fallback_signature(inner, fallback::FailureSignature::UnsupportedTransport); + break; + } + note_cpu_fallback_frame_dropped( + inner, + "Windows game capture CPU-memory transport dropped; native sender requires shared textures", + ); + } + + inner.running.store(false, Ordering::Release); + emit_lifecycle(inner, "closed-clean", "game capture stopped"); +} + +fn handle_fallback_signature( + inner: &Arc, + signature: fallback::FailureSignature, +) -> bool { + matches!( + crate::observe_fallback(inner, signature), + Some(fallback::FallbackDecision::Stay { + retry_in_place: true + }) + ) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn shared_buffer_grows_to_nearest_monitor_for_window_resize_recovery() { + assert_eq!( + choose_shared_buffer_dimensions(640, 360, 1920, 1080), + Some((1920, 1080)) + ); + } + + #[test] + fn shared_buffer_keeps_initial_size_when_window_is_larger_than_monitor() { + assert_eq!( + choose_shared_buffer_dimensions(2560, 1440, 1920, 1080), + Some((2560, 1440)) + ); + } + + #[test] + fn shared_buffer_rejects_unreasonably_large_monitor_mapping() { + assert_eq!( + choose_shared_buffer_dimensions( + 640, + 360, + MAX_GAME_CAPTURE_DIMENSION, + MAX_GAME_CAPTURE_DIMENSION + ), + None + ); + } +} diff --git a/fluxer_desktop/native/win-game-capture/src/game_capture_abi.rs b/fluxer_desktop/native/win-game-capture/src/game_capture_abi.rs new file mode 100644 index 0000000..595fdd2 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/src/game_capture_abi.rs @@ -0,0 +1,307 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![allow(dead_code)] + +pub const GAME_CAPTURE_MAGIC: u32 = 0x4658_4743; +pub const GAME_CAPTURE_ABI_VERSION: u32 = 4; +pub const GAME_CAPTURE_ABI_VERSION_PRESENT_CLOCK: u32 = 4; + +pub const GAME_CAPTURE_PRESENT_CLOCK_WALL: u32 = 0; +pub const GAME_CAPTURE_PRESENT_CLOCK_QPC: u32 = 1; + +pub const GAME_CAPTURE_STATE_INIT: u32 = 0; +pub const GAME_CAPTURE_STATE_ACTIVE: u32 = 1; +pub const GAME_CAPTURE_STATE_STOPPED: u32 = 2; +pub const GAME_CAPTURE_STATE_ERROR: u32 = 3; +pub const GAME_CAPTURE_STATE_RESIZE_REQUIRED: u32 = 4; + +pub const GAME_CAPTURE_BUFFER_COUNT: usize = 2; +pub const GAME_CAPTURE_BYTES_PER_PIXEL: u32 = 4; +pub const GAME_CAPTURE_TRANSPORT_MEMORY: u32 = 0; +pub const GAME_CAPTURE_TRANSPORT_SHARED_TEXTURE: u32 = 1; + +pub const GAME_CAPTURE_API_UNKNOWN: u32 = 0; +pub const GAME_CAPTURE_API_OPENGL: u32 = 1; +pub const GAME_CAPTURE_API_D3D8: u32 = 2; +pub const GAME_CAPTURE_API_D3D9: u32 = 3; +pub const GAME_CAPTURE_API_D3D10: u32 = 4; +pub const GAME_CAPTURE_API_D3D11: u32 = 5; +pub const GAME_CAPTURE_API_D3D12: u32 = 6; +pub const GAME_CAPTURE_API_VULKAN: u32 = 7; + +pub const GAME_CAPTURE_FALLBACK_NONE: u32 = 0; +pub const GAME_CAPTURE_FALLBACK_SHARED_TEXTURE_UNSUPPORTED: u32 = 1; +pub const GAME_CAPTURE_FALLBACK_FORMAT_UNSUPPORTED: u32 = 2; +pub const GAME_CAPTURE_FALLBACK_FORCED_CPU: u32 = 3; +pub const GAME_CAPTURE_FALLBACK_EXTERNAL_MEMORY_UNSUPPORTED: u32 = 4; +pub const GAME_CAPTURE_FALLBACK_MULTISAMPLED: u32 = 5; +pub const GAME_CAPTURE_FALLBACK_DEVICE_LOST: u32 = 6; + +pub const GAME_CAPTURE_FLAG_HDR: u32 = 1 << 0; +pub const GAME_CAPTURE_FLAG_MULTISAMPLED: u32 = 1 << 1; +pub const GAME_CAPTURE_FLAG_FLIP_VERTICAL: u32 = 1 << 2; +pub const GAME_CAPTURE_FLAG_PROTECTED: u32 = 1 << 3; +pub const GAME_CAPTURE_FLAG_TEN_BIT: u32 = 1 << 4; + +pub const ENV_DISABLE_HOOK: &str = "FLUXER_GAME_CAPTURE_DISABLE_HOOK"; +pub const ENV_DISABLE_D3D12: &str = "FLUXER_GAME_CAPTURE_DISABLE_D3D12"; +pub const ENV_DISABLE_VULKAN: &str = "DISABLE_FLUXER_VULKAN_CAPTURE"; +pub const ENV_FORCE_CPU: &str = "FLUXER_GAME_CAPTURE_FORCE_CPU"; +pub const ENV_FORCE_SHARED_TEXTURE: &str = "FLUXER_GAME_CAPTURE_FORCE_SHARED_TEXTURE"; +pub const ENV_ENABLE_OPENGL_SHARED_TEXTURE: &str = + "FLUXER_GAME_CAPTURE_ENABLE_OPENGL_SHARED_TEXTURE"; +pub const ENV_DISABLE_OPENGL_SHARED_TEXTURE: &str = + "FLUXER_GAME_CAPTURE_DISABLE_OPENGL_SHARED_TEXTURE"; +pub const ENV_VERBOSE: &str = "FLUXER_GAME_CAPTURE_VERBOSE"; +pub const ENV_INJECT_METHOD: &str = "FLUXER_GAME_CAPTURE_INJECT_METHOD"; + +pub const GAME_CAPTURE_CONTROL_DISABLE_SHARED_TEXTURE: u32 = 1 << 0; + +pub fn env_flag_enabled(name: &str) -> bool { + std::env::var_os(name) + .map(|value| !value.is_empty()) + .unwrap_or(false) +} + +pub const GAME_CAPTURE_INFO_PREFIX: &str = "FluxerGameCapture_Info_"; +pub const GAME_CAPTURE_FRAME_PREFIX: &str = "FluxerGameCapture_Frame_"; +pub const GAME_CAPTURE_READY_PREFIX: &str = "FluxerGameCapture_Ready_"; +pub const GAME_CAPTURE_STOP_PREFIX: &str = "FluxerGameCapture_Stop_"; +pub const GAME_CAPTURE_KEEPALIVE_PREFIX: &str = "FluxerGameCapture_KeepAlive_"; +pub const GAME_CAPTURE_MUTEX_PREFIX: &str = "FluxerGameCapture_FrameMutex_"; + +#[repr(C)] +#[derive(Clone, Copy)] +pub struct GameCaptureSharedInfo { + pub magic: u32, + pub version: u32, + pub state: u32, + pub last_error: u32, + pub hwnd: u64, + pub max_width: u32, + pub max_height: u32, + pub width: u32, + pub height: u32, + pub pitch: u32, + pub frame_index: u32, + pub frame_counter: u64, + pub timestamp_us: i64, + pub target_frame_interval_ns: u64, + pub transport: u32, + pub dxgi_format: u32, + pub texture_handle: u64, + pub api_type: u32, + pub fallback_reason: u32, + pub capture_flags: u32, + pub dropped_frame_counter: u64, + pub last_present_timestamp_us: i64, + pub control: u32, + pub present_clock: u32, + pub reserved: [u32; 23], +} + +const _: () = assert!(std::mem::size_of::() == 224); + +impl GameCaptureSharedInfo { + pub fn new(hwnd: u64, max_width: u32, max_height: u32, frame_rate: u32) -> Self { + Self { + magic: GAME_CAPTURE_MAGIC, + version: GAME_CAPTURE_ABI_VERSION, + state: GAME_CAPTURE_STATE_INIT, + last_error: 0, + hwnd, + max_width, + max_height, + width: 0, + height: 0, + pitch: max_width.saturating_mul(GAME_CAPTURE_BYTES_PER_PIXEL), + frame_index: 0, + frame_counter: 0, + timestamp_us: 0, + target_frame_interval_ns: frame_interval_ns(frame_rate), + transport: GAME_CAPTURE_TRANSPORT_MEMORY, + dxgi_format: 0, + texture_handle: 0, + api_type: GAME_CAPTURE_API_UNKNOWN, + fallback_reason: GAME_CAPTURE_FALLBACK_NONE, + capture_flags: 0, + dropped_frame_counter: 0, + last_present_timestamp_us: 0, + control: 0, + present_clock: GAME_CAPTURE_PRESENT_CLOCK_WALL, + reserved: [0; 23], + } + } +} + +pub fn host_supports_present_clock(version: u32) -> bool { + version >= GAME_CAPTURE_ABI_VERSION_PRESENT_CLOCK +} + +pub fn qpc_ticks_to_us(ticks: i64, frequency: i64) -> i64 { + if frequency <= 0 { + return 0; + } + if ticks <= 0 { + return 0; + } + let whole_second_us = (ticks / frequency).saturating_mul(1_000_000); + let fractional_us = (ticks % frequency).saturating_mul(1_000_000) / frequency; + whole_second_us.saturating_add(fractional_us) +} + +pub fn presented_recently( + present_clock: u32, + last_present_us: i64, + wall_now_us: i64, + qpc_now_us: i64, + window_us: i64, +) -> bool { + assert!(window_us > 0); + if last_present_us <= 0 { + return false; + } + let now_us = if present_clock == GAME_CAPTURE_PRESENT_CLOCK_QPC { + qpc_now_us + } else { + wall_now_us + }; + now_us.saturating_sub(last_present_us) < window_us +} + +#[cfg(target_os = "windows")] +pub fn qpc_now_us() -> i64 { + use windows_sys::Win32::System::Performance::{ + QueryPerformanceCounter, QueryPerformanceFrequency, + }; + let mut ticks = 0i64; + let mut frequency = 0i64; + let counter_ok = unsafe { QueryPerformanceCounter(&mut ticks) }; + let frequency_ok = unsafe { QueryPerformanceFrequency(&mut frequency) }; + if counter_ok == 0 || frequency_ok == 0 { + return 0; + } + qpc_ticks_to_us(ticks, frequency) +} + +pub fn frame_interval_ns(frame_rate: u32) -> u64 { + let rate = frame_rate.clamp(1, 144) as u64; + 1_000_000_000 / rate +} + +pub fn frame_buffer_stride(width: u32) -> u32 { + width.saturating_mul(GAME_CAPTURE_BYTES_PER_PIXEL) +} + +pub fn frame_buffer_size(width: u32, height: u32) -> Option { + frame_buffer_stride(width) + .checked_mul(height)? + .try_into() + .ok() +} + +pub fn shared_frame_mapping_size(width: u32, height: u32) -> Option { + frame_buffer_size(width, height)?.checked_mul(GAME_CAPTURE_BUFFER_COUNT) +} + +pub fn object_name(prefix: &str, pid: u32) -> String { + format!("{prefix}{pid}") +} + +pub fn mutex_name(pid: u32, index: usize) -> String { + format!("{GAME_CAPTURE_MUTEX_PREFIX}{index}_{pid}") +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn qpc_ticks_to_us_converts_whole_and_fractional_seconds() { + assert_eq!(qpc_ticks_to_us(10_000_000, 10_000_000), 1_000_000); + assert_eq!(qpc_ticks_to_us(15_000_000, 10_000_000), 1_500_000); + assert_eq!(qpc_ticks_to_us(1, 10_000_000), 0); + assert_eq!(qpc_ticks_to_us(10, 10_000_000), 1); + } + + #[test] + fn qpc_ticks_to_us_does_not_overflow_for_long_uptimes() { + let frequency = 3_800_000_000i64; + let uptime_seconds = 400 * 24 * 60 * 60i64; + let ticks = frequency * uptime_seconds; + assert_eq!( + qpc_ticks_to_us(ticks, frequency), + uptime_seconds * 1_000_000 + ); + } + + #[test] + fn qpc_ticks_to_us_rejects_invalid_inputs() { + assert_eq!(qpc_ticks_to_us(123, 0), 0); + assert_eq!(qpc_ticks_to_us(123, -1), 0); + assert_eq!(qpc_ticks_to_us(-123, 10_000_000), 0); + assert_eq!(qpc_ticks_to_us(0, 10_000_000), 0); + } + + #[test] + fn host_supports_present_clock_gates_on_abi_version() { + assert!(!host_supports_present_clock(0)); + assert!(!host_supports_present_clock(3)); + assert!(host_supports_present_clock(4)); + assert!(host_supports_present_clock(5)); + } + + #[test] + fn presented_recently_uses_wall_clock_for_legacy_hooks() { + assert!(presented_recently( + GAME_CAPTURE_PRESENT_CLOCK_WALL, + 1_000_000, + 1_500_000, + i64::MAX, + 1_000_000 + )); + assert!(!presented_recently( + GAME_CAPTURE_PRESENT_CLOCK_WALL, + 1_000_000, + 2_000_000, + 0, + 1_000_000 + )); + } + + #[test] + fn presented_recently_uses_qpc_for_present_clock_hooks() { + assert!(presented_recently( + GAME_CAPTURE_PRESENT_CLOCK_QPC, + 1_000_000, + i64::MAX, + 1_500_000, + 1_000_000 + )); + assert!(!presented_recently( + GAME_CAPTURE_PRESENT_CLOCK_QPC, + 1_000_000, + 0, + 2_000_000, + 1_000_000 + )); + } + + #[test] + fn presented_recently_requires_an_observed_present() { + assert!(!presented_recently( + GAME_CAPTURE_PRESENT_CLOCK_WALL, + 0, + 1, + 1, + 1_000_000 + )); + assert!(!presented_recently( + GAME_CAPTURE_PRESENT_CLOCK_QPC, + -1, + 1, + 1, + 1_000_000 + )); + } +} diff --git a/fluxer_desktop/native/win-game-capture/src/gpu_priority.rs b/fluxer_desktop/native/win-game-capture/src/gpu_priority.rs new file mode 100644 index 0000000..7231a3b --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/src/gpu_priority.rs @@ -0,0 +1,174 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[cfg(target_os = "windows")] +use std::{ + collections::HashMap, + sync::{LazyLock, Mutex}, +}; + +#[cfg(target_os = "windows")] +use windows::{ + Win32::{ + Foundation::{CloseHandle, HANDLE}, + System::{ + LibraryLoader::{GetProcAddress, LoadLibraryW}, + Threading::{ + GetCurrentProcess, OpenProcess, PROCESS_QUERY_INFORMATION, + PROCESS_QUERY_LIMITED_INFORMATION, PROCESS_SET_INFORMATION, + }, + }, + }, + core::{s, w}, +}; + +#[cfg(target_os = "windows")] +const D3DKMT_SCHEDULINGPRIORITYCLASS_NORMAL: u32 = 2; +#[cfg(target_os = "windows")] +const D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH: u32 = 4; +#[cfg(target_os = "windows")] +const D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME: u32 = 5; + +#[cfg(target_os = "windows")] +static SAVED_PRIORITIES: LazyLock>> = + LazyLock::new(|| Mutex::new(HashMap::new())); + +#[cfg(target_os = "windows")] +type SetSchedulingPriorityClassFn = unsafe extern "system" fn(HANDLE, u32) -> i32; +#[cfg(target_os = "windows")] +type GetSchedulingPriorityClassFn = unsafe extern "system" fn(HANDLE, *mut u32) -> i32; + +#[cfg(target_os = "windows")] +struct D3dkmtFns { + set: SetSchedulingPriorityClassFn, + get: GetSchedulingPriorityClassFn, +} + +#[cfg(target_os = "windows")] +fn load_d3dkmt_fns() -> Result { + unsafe { + let gdi32 = + LoadLibraryW(w!("gdi32.dll")).map_err(|e| format!("LoadLibraryW(gdi32.dll): {e}"))?; + let set_ptr = GetProcAddress(gdi32, s!("D3DKMTSetProcessSchedulingPriorityClass")) + .ok_or("D3DKMTSetProcessSchedulingPriorityClass not exported by gdi32.dll")?; + let get_ptr = GetProcAddress(gdi32, s!("D3DKMTGetProcessSchedulingPriorityClass")) + .ok_or("D3DKMTGetProcessSchedulingPriorityClass not exported by gdi32.dll")?; + Ok(D3dkmtFns { + set: std::mem::transmute::< + unsafe extern "system" fn() -> isize, + SetSchedulingPriorityClassFn, + >(set_ptr), + get: std::mem::transmute::< + unsafe extern "system" fn() -> isize, + GetSchedulingPriorityClassFn, + >(get_ptr), + }) + } +} + +#[cfg(target_os = "windows")] +fn process_id_or_current(process_id: Option) -> Result { + let pid = process_id.unwrap_or_else(std::process::id); + if pid == 0 { + return Err("Invalid process id 0".into()); + } + Ok(pid) +} + +#[cfg(target_os = "windows")] +fn with_process_handle( + process_id: u32, + operation: impl FnOnce(HANDLE) -> Result, +) -> Result { + if process_id == std::process::id() { + return operation(unsafe { GetCurrentProcess() }); + } + + let access = + PROCESS_SET_INFORMATION | PROCESS_QUERY_INFORMATION | PROCESS_QUERY_LIMITED_INFORMATION; + let handle = unsafe { OpenProcess(access, false, process_id) } + .map_err(|e| format!("OpenProcess({process_id}): {e}"))?; + let result = operation(handle); + let _ = unsafe { CloseHandle(handle) }; + result +} + +#[cfg(target_os = "windows")] +fn scheduling_priority_class( + priority_class: Option, +) -> Result<(u32, &'static str), String> { + match priority_class + .as_deref() + .unwrap_or("high") + .trim() + .to_ascii_lowercase() + .as_str() + { + "" | "high" => Ok((D3DKMT_SCHEDULINGPRIORITYCLASS_HIGH, "HIGH")), + "realtime" | "real-time" => Ok((D3DKMT_SCHEDULINGPRIORITYCLASS_REALTIME, "REALTIME")), + other => Err(format!( + "Unsupported GPU scheduling priority class '{other}'; expected 'high' or 'realtime'" + )), + } +} + +#[cfg(target_os = "windows")] +pub fn elevate(process_id: Option, priority_class: Option) -> Result<(), String> { + let fns = load_d3dkmt_fns()?; + let process_id = process_id_or_current(process_id)?; + let (priority_class, priority_label) = scheduling_priority_class(priority_class)?; + + with_process_handle(process_id, |process| { + let mut current: u32 = D3DKMT_SCHEDULINGPRIORITYCLASS_NORMAL; + let status = unsafe { (fns.get)(process, &mut current) }; + if status == 0 { + let mut saved = SAVED_PRIORITIES + .lock() + .map_err(|_| "GPU priority saved-state lock poisoned".to_string())?; + saved.entry(process_id).or_insert(current); + } + + let status = unsafe { (fns.set)(process, priority_class) }; + if status != 0 { + return Err(format!( + "D3DKMTSetProcessSchedulingPriorityClass(pid={process_id}, {priority_label}) returned NTSTATUS 0x{:08X}", + status as u32 + )); + } + Ok(()) + }) +} + +#[cfg(target_os = "windows")] +pub fn restore(process_id: Option) -> Result<(), String> { + let fns = load_d3dkmt_fns()?; + let process_id = process_id_or_current(process_id)?; + let target = { + let mut saved = SAVED_PRIORITIES + .lock() + .map_err(|_| "GPU priority saved-state lock poisoned".to_string())?; + saved + .remove(&process_id) + .unwrap_or(D3DKMT_SCHEDULINGPRIORITYCLASS_NORMAL) + }; + + with_process_handle(process_id, |process| { + let status = unsafe { (fns.set)(process, target) }; + if status != 0 { + return Err(format!( + "D3DKMTSetProcessSchedulingPriorityClass(pid={process_id}, restore={target}) returned NTSTATUS 0x{:08X}", + status as u32 + )); + } + Ok(()) + }) +} + +#[cfg(not(target_os = "windows"))] +pub fn elevate(_process_id: Option, _priority_class: Option) -> Result<(), String> { + Ok(()) +} + +#[cfg(not(target_os = "windows"))] +pub fn restore(_process_id: Option) -> Result<(), String> { + Ok(()) +} diff --git a/fluxer_desktop/native/win-game-capture/src/hdr.rs b/fluxer_desktop/native/win-game-capture/src/hdr.rs new file mode 100644 index 0000000..171c96b --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/src/hdr.rs @@ -0,0 +1,465 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![allow(dead_code)] + +pub const DXGI_FORMAT_R16G16B16A16_FLOAT: u32 = 10; +pub const DXGI_FORMAT_R16G16B16A16_UNORM: u32 = 11; +pub const DXGI_FORMAT_R10G10B10A2_UNORM: u32 = 24; +pub const DXGI_FORMAT_R8G8B8A8_UNORM: u32 = 28; +pub const DXGI_FORMAT_R8G8B8A8_UNORM_SRGB: u32 = 29; +pub const DXGI_FORMAT_B8G8R8A8_UNORM: u32 = 87; +pub const DXGI_FORMAT_B8G8R8X8_UNORM: u32 = 88; +pub const DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: u32 = 89; +pub const DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: u32 = 91; +pub const DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: u32 = 93; +pub const DXGI_FORMAT_P010: u32 = 104; +pub const DXGI_FORMAT_P016: u32 = 105; +pub const DXGI_FORMAT_420_OPAQUE: u32 = 106; +pub const DXGI_FORMAT_YUY2: u32 = 107; +pub const DXGI_FORMAT_R32G32B32A32_FLOAT: u32 = 2; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SourceFormat { + Bgra8, + Rgba8, + R10G10B10A2 { hdr: bool }, + Rgba16Float { hdr: bool }, +} + +impl SourceFormat { + pub fn classify(dxgi_format: u32, capture_flags: u32) -> Option { + let hdr = capture_flags & crate::game_capture_abi::GAME_CAPTURE_FLAG_HDR != 0; + match dxgi_format { + DXGI_FORMAT_B8G8R8A8_UNORM + | DXGI_FORMAT_B8G8R8A8_UNORM_SRGB + | DXGI_FORMAT_B8G8R8X8_UNORM + | DXGI_FORMAT_B8G8R8X8_UNORM_SRGB => Some(SourceFormat::Bgra8), + DXGI_FORMAT_R8G8B8A8_UNORM | DXGI_FORMAT_R8G8B8A8_UNORM_SRGB => { + Some(SourceFormat::Rgba8) + } + DXGI_FORMAT_R10G10B10A2_UNORM => Some(SourceFormat::R10G10B10A2 { hdr }), + DXGI_FORMAT_R16G16B16A16_FLOAT => Some(SourceFormat::Rgba16Float { hdr }), + _ => None, + } + } + + pub fn is_8bit(self) -> bool { + matches!(self, SourceFormat::Bgra8 | SourceFormat::Rgba8) + } + + pub fn is_hdr(self) -> bool { + matches!( + self, + SourceFormat::R10G10B10A2 { hdr: true } | SourceFormat::Rgba16Float { hdr: true } + ) + } + + pub fn bytes_per_pixel(self) -> usize { + match self { + SourceFormat::Bgra8 | SourceFormat::Rgba8 | SourceFormat::R10G10B10A2 { .. } => 4, + SourceFormat::Rgba16Float { .. } => 8, + } + } +} + +const SDR_WHITE_NITS: f32 = 80.0; + +pub fn r10g10b10a2_row_to_bgra(src_row: &[u8], width: usize, dst_row: &mut [u8], hdr: bool) { + for x in 0..width { + let so = x * 4; + let dofs = x * 4; + if so + 4 > src_row.len() || dofs + 4 > dst_row.len() { + break; + } + let packed = u32::from_le_bytes([ + src_row[so], + src_row[so + 1], + src_row[so + 2], + src_row[so + 3], + ]); + let r10 = (packed & 0x3FF) as u16; + let g10 = ((packed >> 10) & 0x3FF) as u16; + let b10 = ((packed >> 20) & 0x3FF) as u16; + let a2 = ((packed >> 30) & 0x3) as u8; + let (r, g, b) = if hdr { + tonemap_rec2020_pq_to_srgb8(r10, g10, b10) + } else { + (scale10_to_8(r10), scale10_to_8(g10), scale10_to_8(b10)) + }; + dst_row[dofs] = b; + dst_row[dofs + 1] = g; + dst_row[dofs + 2] = r; + dst_row[dofs + 3] = (a2 as u16 * 255 / 3) as u8; + } +} + +pub fn rgba16f_row_to_bgra(src_row: &[u8], width: usize, dst_row: &mut [u8], hdr: bool) { + for x in 0..width { + let so = x * 8; + let dofs = x * 4; + if so + 8 > src_row.len() || dofs + 4 > dst_row.len() { + break; + } + let r = f16_to_f32(u16::from_le_bytes([src_row[so], src_row[so + 1]])); + let g = f16_to_f32(u16::from_le_bytes([src_row[so + 2], src_row[so + 3]])); + let b = f16_to_f32(u16::from_le_bytes([src_row[so + 4], src_row[so + 5]])); + let a = f16_to_f32(u16::from_le_bytes([src_row[so + 6], src_row[so + 7]])); + let (lr, lg, lb) = if hdr { + ( + reinhard(r.max(0.0)), + reinhard(g.max(0.0)), + reinhard(b.max(0.0)), + ) + } else { + (r.clamp(0.0, 1.0), g.clamp(0.0, 1.0), b.clamp(0.0, 1.0)) + }; + dst_row[dofs] = linear_to_srgb8(lb); + dst_row[dofs + 1] = linear_to_srgb8(lg); + dst_row[dofs + 2] = linear_to_srgb8(lr); + dst_row[dofs + 3] = (a.clamp(0.0, 1.0) * 255.0 + 0.5) as u8; + } +} + +fn scale10_to_8(v10: u16) -> u8 { + ((v10 as u32 * 255 + 511) / 1023) as u8 +} + +fn reinhard(linear: f32) -> f32 { + let v = linear.max(0.0); + (v / (1.0 + v)).clamp(0.0, 1.0) +} + +fn tonemap_rec2020_pq_to_srgb8(r10: u16, g10: u16, b10: u16) -> (u8, u8, u8) { + let lr = pq_eotf(r10 as f32 / 1023.0); + let lg = pq_eotf(g10 as f32 / 1023.0); + let lb = pq_eotf(b10 as f32 / 1023.0); + let scale = 10000.0 / SDR_WHITE_NITS; + let map = |v: f32| reinhard((v * scale).max(0.0)); + ( + linear_to_srgb8(map(lr)), + linear_to_srgb8(map(lg)), + linear_to_srgb8(map(lb)), + ) +} + +fn pq_eotf(e: f32) -> f32 { + const M1: f64 = 0.1593017578125; + const M2: f64 = 78.84375; + const C1: f64 = 0.8359375; + const C2: f64 = 18.8515625; + const C3: f64 = 18.6875; + let e = (e.clamp(0.0, 1.0)) as f64; + let ep = e.powf(1.0 / M2); + let num = (ep - C1).max(0.0); + let den = C2 - C3 * ep; + if den <= 0.0 { + return 0.0; + } + (num / den).powf(1.0 / M1) as f32 +} + +fn linear_to_srgb8(linear: f32) -> u8 { + let l = linear.clamp(0.0, 1.0); + let srgb = if l <= 0.0031308 { + l * 12.92 + } else { + 1.055 * l.powf(1.0 / 2.4) - 0.055 + }; + (srgb.clamp(0.0, 1.0) * 255.0 + 0.5) as u8 +} + +pub fn f16_to_f32(h: u16) -> f32 { + let sign = (h >> 15) & 0x1; + let exp = (h >> 10) & 0x1F; + let mant = h & 0x3FF; + let sign_f = if sign == 1 { -1.0f32 } else { 1.0f32 }; + if exp == 0 { + sign_f * (mant as f32) * 2f32.powi(-24) + } else if exp == 0x1F { + if mant == 0 { sign_f * 65504.0 } else { 0.0 } + } else { + sign_f * (1.0 + (mant as f32) / 1024.0) * 2f32.powi(exp as i32 - 15) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::game_capture_abi::{GAME_CAPTURE_FLAG_HDR, GAME_CAPTURE_FLAG_TEN_BIT}; + + #[test] + fn classifies_8bit_formats() { + assert_eq!( + SourceFormat::classify(DXGI_FORMAT_B8G8R8A8_UNORM, 0), + Some(SourceFormat::Bgra8) + ); + assert_eq!( + SourceFormat::classify(DXGI_FORMAT_R8G8B8A8_UNORM, 0), + Some(SourceFormat::Rgba8) + ); + assert!( + SourceFormat::classify(DXGI_FORMAT_B8G8R8A8_UNORM, 0) + .unwrap() + .is_8bit() + ); + } + + #[test] + fn classifies_obs_8bit_alias_set() { + for format in [ + DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, + DXGI_FORMAT_B8G8R8X8_UNORM, + DXGI_FORMAT_B8G8R8X8_UNORM_SRGB, + ] { + assert_eq!(SourceFormat::classify(format, 0), Some(SourceFormat::Bgra8)); + assert_eq!( + SourceFormat::classify(format, GAME_CAPTURE_FLAG_HDR), + Some(SourceFormat::Bgra8), + "8-bit format {format} must not become HDR just because the flag is set" + ); + } + + for format in [DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB] { + assert_eq!(SourceFormat::classify(format, 0), Some(SourceFormat::Rgba8)); + assert_eq!( + SourceFormat::classify(format, GAME_CAPTURE_FLAG_HDR), + Some(SourceFormat::Rgba8), + "8-bit format {format} must not become HDR just because the flag is set" + ); + } + } + + #[test] + fn classifies_10bit_and_hdr_flag() { + assert_eq!( + SourceFormat::classify(DXGI_FORMAT_R10G10B10A2_UNORM, GAME_CAPTURE_FLAG_TEN_BIT), + Some(SourceFormat::R10G10B10A2 { hdr: false }) + ); + assert_eq!( + SourceFormat::classify( + DXGI_FORMAT_R10G10B10A2_UNORM, + GAME_CAPTURE_FLAG_TEN_BIT | GAME_CAPTURE_FLAG_HDR + ), + Some(SourceFormat::R10G10B10A2 { hdr: true }) + ); + assert!( + SourceFormat::classify(DXGI_FORMAT_R10G10B10A2_UNORM, GAME_CAPTURE_FLAG_HDR) + .unwrap() + .is_hdr() + ); + } + + #[test] + fn high_precision_formats_have_expected_byte_widths() { + assert_eq!(SourceFormat::Bgra8.bytes_per_pixel(), 4); + assert_eq!(SourceFormat::Rgba8.bytes_per_pixel(), 4); + assert_eq!( + SourceFormat::R10G10B10A2 { hdr: false }.bytes_per_pixel(), + 4 + ); + assert_eq!(SourceFormat::R10G10B10A2 { hdr: true }.bytes_per_pixel(), 4); + assert_eq!( + SourceFormat::Rgba16Float { hdr: false }.bytes_per_pixel(), + 8 + ); + assert_eq!(SourceFormat::Rgba16Float { hdr: true }.bytes_per_pixel(), 8); + } + + #[test] + fn classifies_fp16_scrgb() { + assert_eq!( + SourceFormat::classify(DXGI_FORMAT_R16G16B16A16_FLOAT, GAME_CAPTURE_FLAG_HDR), + Some(SourceFormat::Rgba16Float { hdr: true }) + ); + assert_eq!( + SourceFormat::classify(DXGI_FORMAT_R16G16B16A16_FLOAT, 0), + Some(SourceFormat::Rgba16Float { hdr: false }) + ); + assert_eq!( + SourceFormat::Rgba16Float { hdr: false }.bytes_per_pixel(), + 8 + ); + } + + #[test] + fn unsupported_format_is_none() { + assert_eq!(SourceFormat::classify(104, 0), None); + assert_eq!(SourceFormat::classify(0, 0), None); + } + + #[test] + fn video_and_opaque_formats_stay_unsupported_even_with_hdr_flags() { + for format in [ + DXGI_FORMAT_P010, + DXGI_FORMAT_P016, + DXGI_FORMAT_420_OPAQUE, + DXGI_FORMAT_YUY2, + ] { + assert_eq!( + SourceFormat::classify(format, GAME_CAPTURE_FLAG_TEN_BIT | GAME_CAPTURE_FLAG_HDR), + None, + "DXGI video/opaque format {format} is not a directly readable game backbuffer" + ); + } + } + + #[test] + fn obs_high_precision_formats_without_reader_conversion_stay_unsupported() { + assert_eq!( + SourceFormat::classify(DXGI_FORMAT_R16G16B16A16_UNORM, GAME_CAPTURE_FLAG_HDR), + None, + "OBS maps RGBA16 UNORM, but Fluxer needs a row converter before claiming support" + ); + assert_eq!( + SourceFormat::classify(DXGI_FORMAT_R32G32B32A32_FLOAT, GAME_CAPTURE_FLAG_HDR), + None, + "OBS maps RGBA32F, but Fluxer has no reader-side conversion for it yet" + ); + assert_eq!( + SourceFormat::classify( + DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM, + GAME_CAPTURE_FLAG_TEN_BIT | GAME_CAPTURE_FLAG_HDR + ), + None, + "XR-bias 10A2 is not equivalent to regular R10G10B10A2 UNORM" + ); + } + + #[test] + fn f16_round_trips_known_values() { + assert_eq!(f16_to_f32(0x0000), 0.0); + assert_eq!(f16_to_f32(0x3C00), 1.0); + assert_eq!(f16_to_f32(0x4000), 2.0); + assert_eq!(f16_to_f32(0x3800), 0.5); + assert!((f16_to_f32(0xC000) + 2.0).abs() < 1e-6); + } + + #[test] + fn f16_special_values_are_bounded_for_sdr_fallback() { + assert_eq!(f16_to_f32(0x7C00), 65504.0); + assert_eq!(f16_to_f32(0xFC00), -65504.0); + assert_eq!(f16_to_f32(0x7E00), 0.0); + } + + #[test] + fn r10_sdr_unpack_scales_extremes() { + let packed: u32 = 0x3FF | (0x3FF << 10) | (0x3FF << 20) | (0x3 << 30); + let src = packed.to_le_bytes(); + let mut dst = [0u8; 4]; + r10g10b10a2_row_to_bgra(&src, 1, &mut dst, false); + assert_eq!(dst, [255, 255, 255, 255]); + + let src0 = 0u32.to_le_bytes(); + let mut dst0 = [0u8; 4]; + r10g10b10a2_row_to_bgra(&src0, 1, &mut dst0, false); + assert_eq!(dst0, [0, 0, 0, 0]); + } + + #[test] + fn r10_sdr_unpack_scales_midpoints_and_alpha() { + let packed: u32 = 0x200 | (0x100 << 10) | (0x080 << 20) | (0x2 << 30); + let src = packed.to_le_bytes(); + let mut dst = [0u8; 4]; + r10g10b10a2_row_to_bgra(&src, 1, &mut dst, false); + assert!((31..=33).contains(&dst[0]), "B was {}", dst[0]); + assert!((63..=65).contains(&dst[1]), "G was {}", dst[1]); + assert!((127..=129).contains(&dst[2]), "R was {}", dst[2]); + assert_eq!(dst[3], 170); + } + + #[test] + fn r10_unpack_respects_short_rows() { + let src = [0xFF, 0xFF, 0xFF]; + let mut dst = [9u8; 8]; + r10g10b10a2_row_to_bgra(&src, 2, &mut dst, false); + assert_eq!(dst, [9u8; 8]); + } + + #[test] + fn r10_unpack_channel_order_is_bgra() { + let packed: u32 = 0x3FF; + let src = packed.to_le_bytes(); + let mut dst = [0u8; 4]; + r10g10b10a2_row_to_bgra(&src, 1, &mut dst, false); + assert_eq!(dst[0], 0, "B should be 0"); + assert_eq!(dst[1], 0, "G should be 0"); + assert_eq!(dst[2], 255, "R should be 255"); + } + + #[test] + fn r10_hdr_unpack_is_bounded_and_nonzero() { + let packed: u32 = 0x3FF | (0x200 << 10) | (0x100 << 20) | (0x3 << 30); + let src = packed.to_le_bytes(); + let mut dst = [0u8; 4]; + r10g10b10a2_row_to_bgra(&src, 1, &mut dst, true); + assert!(dst[2] > 0, "bright red PQ should map to a visible value"); + } + + #[test] + fn r10_hdr_unpack_is_monotonic_after_tonemap() { + let pack = |v: u32| -> [u8; 4] { (v | (v << 10) | (v << 20) | (0x3 << 30)).to_le_bytes() }; + let mut low = [0u8; 4]; + let mut mid = [0u8; 4]; + let mut high = [0u8; 4]; + r10g10b10a2_row_to_bgra(&pack(0x100), 1, &mut low, true); + r10g10b10a2_row_to_bgra(&pack(0x200), 1, &mut mid, true); + r10g10b10a2_row_to_bgra(&pack(0x3FF), 1, &mut high, true); + assert!(low[2] <= mid[2] && mid[2] <= high[2]); + assert_eq!(high[3], 255); + } + + #[test] + fn fp16_sdr_clamps_and_encodes_srgb() { + let mut src = [0u8; 8]; + src[0..2].copy_from_slice(&0x3C00u16.to_le_bytes()); + src[6..8].copy_from_slice(&0x3C00u16.to_le_bytes()); + let mut dst = [0u8; 4]; + rgba16f_row_to_bgra(&src, 1, &mut dst, false); + assert_eq!(dst[0], 0, "B"); + assert_eq!(dst[1], 0, "G"); + assert_eq!(dst[2], 255, "R linear 1.0 -> sRGB 255"); + assert_eq!(dst[3], 255, "A"); + } + + #[test] + fn fp16_sdr_clamps_negative_and_above_one_channels() { + let mut src = [0u8; 8]; + src[0..2].copy_from_slice(&0xBC00u16.to_le_bytes()); + src[2..4].copy_from_slice(&0x4000u16.to_le_bytes()); + src[4..6].copy_from_slice(&0x3800u16.to_le_bytes()); + src[6..8].copy_from_slice(&0x4000u16.to_le_bytes()); + let mut dst = [0u8; 4]; + rgba16f_row_to_bgra(&src, 1, &mut dst, false); + assert!((180..=196).contains(&dst[0]), "B linear 0.5 was {}", dst[0]); + assert_eq!(dst[1], 255, "G linear 2.0 clamps to white"); + assert_eq!(dst[2], 0, "R negative clamps to black"); + assert_eq!(dst[3], 255, "A above 1.0 clamps opaque"); + } + + #[test] + fn fp16_unpack_respects_short_rows() { + let src = [0x00, 0x3C, 0x00, 0x3C]; + let mut dst = [7u8; 8]; + rgba16f_row_to_bgra(&src, 2, &mut dst, false); + assert_eq!(dst, [7u8; 8]); + } + + #[test] + fn fp16_hdr_above_one_is_tonemapped_not_clipped_to_garbage() { + let mut src = [0u8; 8]; + src[0..2].copy_from_slice(&0x4400u16.to_le_bytes()); + src[6..8].copy_from_slice(&0x3C00u16.to_le_bytes()); + let mut dst = [0u8; 4]; + rgba16f_row_to_bgra(&src, 1, &mut dst, true); + assert!(dst[2] > 100, "HDR highlight should be bright after tonemap"); + } + + #[test] + fn srgb_encode_endpoints() { + assert_eq!(linear_to_srgb8(0.0), 0); + assert_eq!(linear_to_srgb8(1.0), 255); + let mid = linear_to_srgb8(0.5); + assert!((180..=196).contains(&mid), "sRGB(0.5) was {mid}"); + } +} diff --git a/fluxer_desktop/native/win-game-capture/src/lib.rs b/fluxer_desktop/native/win-game-capture/src/lib.rs new file mode 100644 index 0000000..ab40ee9 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/src/lib.rs @@ -0,0 +1,1350 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![deny(clippy::all)] +#![allow(unsafe_op_in_unsafe_fn)] + +#[cfg(any(target_os = "windows", test))] +mod compatibility; +#[cfg(any(target_os = "windows", test))] +mod dxgi_capture; +pub mod encoder_attach; +mod fallback; +#[cfg(target_os = "windows")] +mod game_capture; +mod game_capture_abi; +mod gpu_priority; +mod hdr; +#[cfg(target_os = "windows")] +mod nv12_gpu; +mod sources; +#[cfg(target_os = "windows")] +mod vulkan_layer_registry; +#[cfg(target_os = "windows")] +mod wgc_capture; + +pub use encoder_attach::{EncoderAttachError, EncoderAttachStats, EncoderAttachment}; + +use napi::bindgen_prelude::*; +use napi::threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode}; +use napi::{JsValue, Status, ValueType}; +use napi_derive::napi; +use parking_lot::{Mutex, RwLock}; +use std::ffi::c_void; +use std::sync::Arc; + +#[cfg(target_os = "windows")] +use dxgi_capture::DxgiCaptureSession; +use fluxer_encoder_ring::EncoderFrameRate; +#[cfg(target_os = "windows")] +use fluxer_screen_frame_bus::EnqueueOutcome; +#[cfg(target_os = "windows")] +use game_capture::GameCaptureSession; +#[cfg(target_os = "windows")] +use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; +#[cfg(target_os = "windows")] +use wgc_capture::WgcCaptureSession; + +const LIFECYCLE_QUEUE_LIMIT: usize = 8; +const START_OPTION_UNSUPPORTED_LIMIT: usize = 4; + +type LifecycleTsfn = Arc< + ThreadsafeFunction< + (String, String), + (), + (String, String), + napi::Status, + false, + true, + LIFECYCLE_QUEUE_LIMIT, + >, +>; + +#[napi(object)] +#[derive(Clone, Debug)] +pub struct ScreenCaptureRect { + pub x: f64, + pub y: f64, + pub width: f64, + pub height: f64, +} + +#[napi(object)] +#[derive(Clone, Debug, Default)] +pub struct ScreenCaptureStartOptions { + #[napi(js_name = "showCursorClicks")] + pub show_cursor_clicks: Option, + #[napi(js_name = "captureRect")] + pub capture_rect: Option, + #[napi(js_name = "colorRange")] + pub color_range: Option, + #[napi(js_name = "colorSpace")] + pub color_space: Option, +} + +#[napi(object)] +#[derive(Clone, Debug, Default)] +pub struct CaptureStartOptionsDiagnostics { + #[napi(js_name = "showCursorClicks")] + pub show_cursor_clicks: Option, + #[napi(js_name = "captureRect")] + pub capture_rect: Option, + #[napi(js_name = "colorRange")] + pub color_range: Option, + #[napi(js_name = "colorSpace")] + pub color_space: Option, + #[napi(js_name = "unsupportedOptions")] + pub unsupported_options: Vec, +} + +#[napi(object)] +pub struct CaptureStartResult { + pub width: u32, + pub height: u32, + #[napi(js_name = "frameRate")] + pub frame_rate: u32, + #[napi(js_name = "pixelFormat")] + pub pixel_format: String, +} + +#[napi(object)] +pub struct ScreenCaptureSourceDescriptor { + pub kind: String, + pub id: String, + pub name: String, + pub width: u32, + pub height: u32, + #[napi(js_name = "targetPid")] + pub target_pid: Option, +} + +#[napi(object)] +pub struct AvailabilityInfo { + pub available: bool, + pub backend: String, + pub reason: Option, +} + +#[napi(object)] +pub struct CaptureDiagnostics { + pub state: u32, + #[napi(js_name = "apiType")] + pub api_type: u32, + pub transport: u32, + #[napi(js_name = "fallbackReason")] + pub fallback_reason: u32, + #[napi(js_name = "captureFlags")] + pub capture_flags: u32, + pub width: u32, + pub height: u32, + #[napi(js_name = "dxgiFormat")] + pub dxgi_format: u32, + #[napi(js_name = "frameCounter")] + pub frame_counter: f64, + #[napi(js_name = "droppedFrameCounter")] + pub dropped_frame_counter: f64, + #[napi(js_name = "lastPresentTimestampUs")] + pub last_present_timestamp_us: f64, + #[napi(js_name = "lastError")] + pub last_error: u32, + #[napi(js_name = "requestedInjectionMethod")] + pub requested_injection_method: String, + #[napi(js_name = "injectionMethod")] + pub injection_method: String, + #[napi(js_name = "activeStrategy")] + pub active_strategy: String, + #[napi(js_name = "lastFallbackReason")] + pub last_fallback_reason: String, + #[napi(js_name = "startOptions")] + pub start_options: CaptureStartOptionsDiagnostics, + #[napi(js_name = "frameSinkAccepted")] + pub frame_sink_accepted: f64, + #[napi(js_name = "frameSinkCoalesced")] + pub frame_sink_coalesced: f64, + #[napi(js_name = "frameSinkRejected")] + pub frame_sink_rejected: f64, + #[napi(js_name = "mediaFramesDroppedWithoutSink")] + pub media_frames_dropped_without_sink: f64, + #[napi(js_name = "cpuFallbackFramesDropped")] + pub cpu_fallback_frames_dropped: f64, +} + +#[napi(object)] +pub struct EncoderAttachDiagnostics { + pub attached: bool, + pub width: u32, + pub height: u32, + pub capacity: u32, + #[napi(js_name = "framesSubmitted")] + pub frames_submitted: f64, + #[napi(js_name = "framesDropped")] + pub frames_dropped: f64, + #[napi(js_name = "ringFullEvents")] + pub ring_full_events: f64, + #[napi(js_name = "failedBlits")] + pub failed_blits: f64, +} + +#[napi(object)] +pub struct FrameSinkDiagnostics { + pub accepted: f64, + pub coalesced: f64, + pub rejected: f64, + #[napi(js_name = "mediaFramesDroppedWithoutSink")] + pub media_frames_dropped_without_sink: f64, + #[napi(js_name = "cpuFallbackFramesDropped")] + pub cpu_fallback_frames_dropped: f64, +} + +#[napi(object)] +pub struct SharedTextureHandleInfo { + pub handle: BigInt, + pub width: u32, + pub height: u32, + #[napi(js_name = "dxgiFormat")] + pub dxgi_format: u32, + #[napi(js_name = "timestampUs")] + pub timestamp_us: f64, +} + +#[napi(object)] +pub struct VulkanLayerRegistrationState { + pub registered: bool, + #[napi(js_name = "manifestExists")] + pub manifest_exists: bool, + #[napi(js_name = "dllExists")] + pub dll_exists: bool, + #[napi(js_name = "manifestPath")] + pub manifest_path: String, +} + +pub struct CaptureInner { + pub lifecycle_tsfn: Mutex>, + #[cfg(target_os = "windows")] + pub session: Mutex>, + #[cfg(target_os = "windows")] + pub(crate) wgc_session: Mutex>, + #[cfg(target_os = "windows")] + pub game_session: Mutex>>, + pub running: std::sync::atomic::AtomicBool, + pub fallback: Mutex>, + pub capture_id: Mutex>, + pub start_options: Mutex, + pub encoder_attachment: RwLock>>, + pub native_frame_sink: + Mutex>>, + #[cfg(target_os = "windows")] + pub frame_sink_accepted: AtomicU64, + #[cfg(target_os = "windows")] + pub frame_sink_coalesced: AtomicU64, + #[cfg(target_os = "windows")] + pub frame_sink_rejected: AtomicU64, + #[cfg(target_os = "windows")] + pub media_frames_dropped_without_sink: AtomicU64, + #[cfg(target_os = "windows")] + pub cpu_fallback_frames_dropped: AtomicU64, + #[cfg(target_os = "windows")] + pub frame_sink_backpressure_emitted: AtomicBool, + #[cfg(target_os = "windows")] + pub frame_sink_missing_emitted: AtomicBool, + #[cfg(target_os = "windows")] + pub cpu_fallback_emitted: AtomicBool, +} + +pub fn emit_lifecycle(inner: &CaptureInner, event_type: &str, message: &str) { + let guard = inner.lifecycle_tsfn.lock(); + if let Some(tsfn) = guard.as_ref() { + let _ = tsfn.call( + (event_type.to_string(), message.to_string()), + ThreadsafeFunctionCallMode::NonBlocking, + ); + } +} + +#[cfg(target_os = "windows")] +struct BusSharedTexture { + handle: u64, + width: u32, + height: u32, + dxgi_format: u32, + timestamp_us: i64, +} + +#[cfg(target_os = "windows")] +impl BusSharedTexture { + fn into_bus_desc(self) -> fluxer_screen_frame_bus::SharedTextureDesc { + fluxer_screen_frame_bus::SharedTextureDesc { + handle: self.handle, + width: self.width, + height: self.height, + dxgi_format: self.dxgi_format, + timestamp_us: self.timestamp_us, + } + } +} + +#[derive(Clone, Copy)] +struct FrameSinkCounterSnapshot { + accepted: u64, + coalesced: u64, + rejected: u64, + dropped_without_sink: u64, + cpu_fallback_dropped: u64, +} + +#[cfg(target_os = "windows")] +fn frame_sink_counter_snapshot(inner: &CaptureInner) -> FrameSinkCounterSnapshot { + FrameSinkCounterSnapshot { + accepted: inner.frame_sink_accepted.load(Ordering::Acquire), + coalesced: inner.frame_sink_coalesced.load(Ordering::Acquire), + rejected: inner.frame_sink_rejected.load(Ordering::Acquire), + dropped_without_sink: inner + .media_frames_dropped_without_sink + .load(Ordering::Acquire), + cpu_fallback_dropped: inner.cpu_fallback_frames_dropped.load(Ordering::Acquire), + } +} + +#[cfg(target_os = "windows")] +fn frame_sink_diagnostics_from(snapshot: FrameSinkCounterSnapshot) -> FrameSinkDiagnostics { + FrameSinkDiagnostics { + accepted: snapshot.accepted as f64, + coalesced: snapshot.coalesced as f64, + rejected: snapshot.rejected as f64, + media_frames_dropped_without_sink: snapshot.dropped_without_sink as f64, + cpu_fallback_frames_dropped: snapshot.cpu_fallback_dropped as f64, + } +} + +#[cfg(target_os = "windows")] +pub(crate) enum FrameSinkRef { + Native(Arc), + Bus(Arc), +} + +#[cfg(target_os = "windows")] +pub(crate) fn resolve_frame_sink( + inner: &CaptureInner, + capture_id: Option<&str>, +) -> Option { + if let Some(sink) = native_frame_sink_for(inner) { + return Some(FrameSinkRef::Native(sink)); + } + let capture_id = capture_id?; + fluxer_screen_frame_bus::get_sink(capture_id).map(FrameSinkRef::Bus) +} + +#[cfg(target_os = "windows")] +pub(crate) fn emit_shared_texture_frame( + inner: &CaptureInner, + sink: &FrameSinkRef, + handle: u64, + width: u32, + height: u32, + dxgi_format: u32, + timestamp_us: i64, +) -> bool { + assert!(handle != 0, "shared texture handle is non-zero"); + assert!(width > 0, "shared texture width is positive"); + assert!(height > 0, "shared texture height is positive"); + let desc = BusSharedTexture { + handle, + width, + height, + dxgi_format, + timestamp_us, + } + .into_bus_desc(); + let outcome = match sink { + FrameSinkRef::Native(sink) => sink.enqueue_shared_texture(desc), + FrameSinkRef::Bus(sink) => { + sink.enqueue(fluxer_screen_frame_bus::ScreenFrame::SharedTexture(desc)) + } + }; + record_frame_sink_outcome(inner, outcome); + frame_sink_outcome_delivered(outcome) +} + +#[cfg(target_os = "windows")] +fn frame_sink_outcome_delivered(outcome: EnqueueOutcome) -> bool { + !matches!(outcome, EnqueueOutcome::Rejected) +} + +#[cfg(target_os = "windows")] +fn record_frame_sink_outcome(inner: &CaptureInner, outcome: EnqueueOutcome) { + match outcome { + EnqueueOutcome::Accepted => { + inner.frame_sink_accepted.fetch_add(1, Ordering::AcqRel); + } + EnqueueOutcome::Coalesced => { + inner.frame_sink_coalesced.fetch_add(1, Ordering::AcqRel); + emit_frame_sink_backpressure_once( + inner, + "Windows shared texture frame coalesced by native frame sink", + ); + } + EnqueueOutcome::Rejected => { + inner.frame_sink_rejected.fetch_add(1, Ordering::AcqRel); + emit_frame_sink_backpressure_once( + inner, + "Windows shared texture frame rejected by native frame sink", + ); + } + } +} + +#[cfg(target_os = "windows")] +fn emit_frame_sink_backpressure_once(inner: &CaptureInner, message: &'static str) { + if inner + .frame_sink_backpressure_emitted + .swap(true, Ordering::AcqRel) + { + return; + } + emit_lifecycle(inner, "diagnostic", message); +} + +#[cfg(target_os = "windows")] +pub(crate) fn note_media_frame_without_sink(inner: &CaptureInner, message: &'static str) { + inner + .media_frames_dropped_without_sink + .fetch_add(1, Ordering::AcqRel); + if inner + .frame_sink_missing_emitted + .swap(true, Ordering::AcqRel) + { + return; + } + emit_lifecycle(inner, "diagnostic", message); +} + +#[cfg(target_os = "windows")] +pub(crate) fn note_cpu_fallback_frame_dropped(inner: &CaptureInner, message: &'static str) { + inner + .cpu_fallback_frames_dropped + .fetch_add(1, Ordering::AcqRel); + if inner.cpu_fallback_emitted.swap(true, Ordering::AcqRel) { + return; + } + emit_lifecycle(inner, "diagnostic", message); +} + +#[cfg(target_os = "windows")] +fn native_frame_sink_for( + inner: &CaptureInner, +) -> Option> { + inner.native_frame_sink.lock().as_ref().cloned() +} + +pub fn observe_fallback( + inner: &CaptureInner, + signature: fallback::FailureSignature, +) -> Option { + let decision = { + let mut guard = inner.fallback.lock(); + guard.as_mut().map(|tracker| tracker.observe(signature)) + }; + if let Some(decision) = decision.as_ref() { + let (kind, message) = fallback::decision_lifecycle(decision); + emit_lifecycle(inner, kind, &message); + } + decision +} + +#[napi] +pub struct ScreenCapture { + inner: Arc, +} + +#[napi] +impl ScreenCapture { + #[allow(clippy::new_without_default)] + #[napi(constructor)] + pub fn new() -> Self { + Self { + inner: Arc::new(CaptureInner { + lifecycle_tsfn: Mutex::new(None), + #[cfg(target_os = "windows")] + session: Mutex::new(None), + #[cfg(target_os = "windows")] + wgc_session: Mutex::new(None), + #[cfg(target_os = "windows")] + game_session: Mutex::new(None), + running: std::sync::atomic::AtomicBool::new(false), + fallback: Mutex::new(None), + capture_id: Mutex::new(None), + start_options: Mutex::new(CaptureStartOptionsDiagnostics::default()), + encoder_attachment: RwLock::new(None), + native_frame_sink: Mutex::new(None), + #[cfg(target_os = "windows")] + frame_sink_accepted: AtomicU64::new(0), + #[cfg(target_os = "windows")] + frame_sink_coalesced: AtomicU64::new(0), + #[cfg(target_os = "windows")] + frame_sink_rejected: AtomicU64::new(0), + #[cfg(target_os = "windows")] + media_frames_dropped_without_sink: AtomicU64::new(0), + #[cfg(target_os = "windows")] + cpu_fallback_frames_dropped: AtomicU64::new(0), + #[cfg(target_os = "windows")] + frame_sink_backpressure_emitted: AtomicBool::new(false), + #[cfg(target_os = "windows")] + frame_sink_missing_emitted: AtomicBool::new(false), + #[cfg(target_os = "windows")] + cpu_fallback_emitted: AtomicBool::new(false), + }), + } + } + + #[napi(js_name = "setLifecycleCallback")] + pub fn set_lifecycle_callback(&self, callback: Function<(String, String), ()>) -> Result<()> { + let tsfn: LifecycleTsfn = callback + .build_threadsafe_function::<(String, String)>() + .weak::() + .callee_handled::() + .max_queue_size::() + .build() + .map(Arc::new)?; + let mut guard = self.inner.lifecycle_tsfn.lock(); + *guard = Some(tsfn); + Ok(()) + } + + #[napi(js_name = "setFrameSinkHandle")] + pub fn set_frame_sink_handle(&self, frame_sink_handle: Unknown<'_>) -> Result<()> { + let sink = retain_native_frame_sink_handle(frame_sink_handle)?; + let mut guard = self.inner.native_frame_sink.lock(); + *guard = Some(sink); + Ok(()) + } + + #[napi] + #[allow(clippy::too_many_arguments)] + pub fn start( + &self, + source_id: String, + source_kind: String, + width: Option, + height: Option, + frame_rate: Option, + hook_path: Option, + hook_path_x86: Option, + injection_method: Option, + capture_id: Option, + start_options: Option, + ) -> Result { + let start_options = record_start_options(&self.inner, start_options)?; + let normalized_capture_id = capture_id + .map(|raw| raw.trim().to_string()) + .filter(|trimmed| !trimmed.is_empty()); + { + let mut guard = self.inner.capture_id.lock(); + *guard = normalized_capture_id; + } + #[cfg(target_os = "windows")] + { + self.start_windows( + source_id, + source_kind, + width, + height, + frame_rate, + hook_path, + hook_path_x86, + injection_method, + start_options, + ) + } + #[cfg(not(target_os = "windows"))] + { + let _ = ( + source_id, + source_kind, + width, + height, + frame_rate, + hook_path, + hook_path_x86, + injection_method, + start_options, + ); + Err(napi::Error::from_reason( + "native game capture only supported on Windows", + )) + } + } + + #[napi(js_name = "getDiagnostics")] + pub fn get_diagnostics(&self) -> Option { + let snapshot = { + let guard = self.inner.fallback.lock(); + guard.as_ref().map(|tracker| tracker.snapshot()) + }?; + + #[cfg(target_os = "windows")] + { + let frame_sink = frame_sink_counter_snapshot(&self.inner); + let guard = self.inner.game_session.lock(); + if let Some(session) = guard.as_ref() { + let requested_injection_method = session.requested_injection_method().to_string(); + let injection_method = session.used_injection_method().to_string(); + if let Some(info) = session.read_shared_info() { + return Some(CaptureDiagnostics { + state: info.state, + api_type: info.api_type, + transport: info.transport, + fallback_reason: info.fallback_reason, + capture_flags: info.capture_flags, + width: info.width, + height: info.height, + dxgi_format: info.dxgi_format, + frame_counter: info.frame_counter as f64, + dropped_frame_counter: info.dropped_frame_counter as f64, + last_present_timestamp_us: info.last_present_timestamp_us as f64, + last_error: info.last_error, + requested_injection_method, + injection_method, + active_strategy: snapshot.active_strategy, + last_fallback_reason: snapshot.last_fallback_reason, + start_options: current_start_options(&self.inner), + frame_sink_accepted: frame_sink.accepted as f64, + frame_sink_coalesced: frame_sink.coalesced as f64, + frame_sink_rejected: frame_sink.rejected as f64, + media_frames_dropped_without_sink: frame_sink.dropped_without_sink as f64, + cpu_fallback_frames_dropped: frame_sink.cpu_fallback_dropped as f64, + }); + } + return Some(strategy_only_diagnostics( + &snapshot, + requested_injection_method, + injection_method, + current_start_options(&self.inner), + frame_sink, + )); + } + Some(strategy_only_diagnostics( + &snapshot, + String::new(), + String::new(), + current_start_options(&self.inner), + frame_sink, + )) + } + + #[cfg(not(target_os = "windows"))] + Some(strategy_only_diagnostics( + &snapshot, + String::new(), + String::new(), + current_start_options(&self.inner), + FrameSinkCounterSnapshot { + accepted: 0, + coalesced: 0, + rejected: 0, + dropped_without_sink: 0, + cpu_fallback_dropped: 0, + }, + )) + } + + #[napi(js_name = "getFrameSinkDiagnostics")] + pub fn get_frame_sink_diagnostics(&self) -> FrameSinkDiagnostics { + #[cfg(target_os = "windows")] + { + frame_sink_diagnostics_from(frame_sink_counter_snapshot(&self.inner)) + } + #[cfg(not(target_os = "windows"))] + { + FrameSinkDiagnostics { + accepted: 0.0, + coalesced: 0.0, + rejected: 0.0, + media_frames_dropped_without_sink: 0.0, + cpu_fallback_frames_dropped: 0.0, + } + } + } + + #[napi(js_name = "getSharedTextureHandle")] + pub fn get_shared_texture_handle(&self) -> Option { + #[cfg(target_os = "windows")] + { + let guard = self.inner.game_session.lock(); + let session = guard.as_ref()?; + if let Some(native_texture) = session.read_native_texture_info() { + return Some(SharedTextureHandleInfo { + handle: BigInt::from(native_texture.handle), + width: native_texture.width, + height: native_texture.height, + dxgi_format: native_texture.dxgi_format, + timestamp_us: native_texture.timestamp_us as f64, + }); + } + None + } + #[cfg(not(target_os = "windows"))] + { + None + } + } + + #[napi] + pub fn stop(&self) -> Result<()> { + self.inner + .running + .store(false, std::sync::atomic::Ordering::Release); + self.inner.capture_id.lock().take(); + self.inner.native_frame_sink.lock().take(); + if let Some(attachment) = self.inner.encoder_attachment.write().take() { + attachment.detach(); + } + #[cfg(target_os = "windows")] + { + let mut guard = self.inner.session.lock(); + *guard = None; + let mut wgc_guard = self.inner.wgc_session.lock(); + *wgc_guard = None; + let mut game_guard = self.inner.game_session.lock(); + *game_guard = None; + } + { + let mut fallback_guard = self.inner.fallback.lock(); + *fallback_guard = None; + } + Ok(()) + } + + #[napi(js_name = "attachEncoder")] + pub fn attach_encoder(&self, width: u32, height: u32, frame_rate: Option) -> Result<()> { + if width == 0 || height == 0 { + return Err(napi::Error::new( + Status::InvalidArg, + "ScreenCapture.attachEncoder requires positive dimensions", + )); + } + let frame_rate = EncoderFrameRate::from_fps(frame_rate.unwrap_or(30)); + let attachment = EncoderAttachment::try_new_with_frame_rate(width, height, frame_rate) + .map_err(|e| { + napi::Error::new(Status::GenericFailure, format!("attachEncoder failed: {e}")) + })?; + *self.inner.encoder_attachment.write() = Some(attachment); + emit_lifecycle( + &self.inner, + "diagnostic", + &format!( + "encoder ring attached: {width}x{height}@{}fps, capacity=8", + frame_rate.numerator + ), + ); + Ok(()) + } + + #[napi(js_name = "detachEncoder")] + pub fn detach_encoder(&self) -> Result<()> { + if let Some(attachment) = self.inner.encoder_attachment.write().take() { + attachment.detach(); + } + emit_lifecycle(&self.inner, "diagnostic", "encoder ring detached"); + Ok(()) + } + + #[napi(js_name = "isEncoderAttached")] + pub fn is_encoder_attached(&self) -> bool { + self.inner + .encoder_attachment + .read() + .as_ref() + .map(|attachment| attachment.is_attached()) + .unwrap_or(false) + } + + #[napi(js_name = "encoderRingFullCount")] + pub fn encoder_ring_full_count(&self) -> u32 { + self.inner + .encoder_attachment + .read() + .as_ref() + .map(|attachment| attachment.stats().ring_full_events.min(u32::MAX as u64) as u32) + .unwrap_or(0) + } + + #[napi(js_name = "getEncoderAttachDiagnostics")] + pub fn get_encoder_attach_diagnostics(&self) -> Option { + let guard = self.inner.encoder_attachment.read(); + let attachment = guard.as_ref()?; + let stats = attachment.stats(); + Some(EncoderAttachDiagnostics { + attached: attachment.is_attached(), + width: attachment.width(), + height: attachment.height(), + capacity: attachment.capacity().min(u32::MAX as usize) as u32, + frames_submitted: stats.frames_submitted as f64, + frames_dropped: stats.frames_dropped as f64, + ring_full_events: stats.ring_full_events as f64, + failed_blits: stats.failed_blits as f64, + }) + } +} + +fn record_start_options( + inner: &CaptureInner, + options: Option, +) -> Result { + let state = build_start_option_diagnostics(options.unwrap_or_default())?; + if !state.unsupported_options.is_empty() { + emit_lifecycle( + inner, + "diagnostic", + &format!( + "Windows capture start options currently unsupported: {}", + state.unsupported_options.join(", ") + ), + ); + } + let mut guard = inner.start_options.lock(); + *guard = state.clone(); + Ok(state) +} + +fn current_start_options(inner: &CaptureInner) -> CaptureStartOptionsDiagnostics { + inner.start_options.lock().clone() +} + +fn build_start_option_diagnostics( + options: ScreenCaptureStartOptions, +) -> Result { + validate_capture_rect(options.capture_rect.as_ref())?; + validate_enum_option( + options.color_range.as_deref(), + "colorRange", + &["full", "limited"], + )?; + validate_enum_option( + options.color_space.as_deref(), + "colorSpace", + &["rec709", "srgb"], + )?; + + let mut unsupported_options = Vec::with_capacity(START_OPTION_UNSUPPORTED_LIMIT); + if options.show_cursor_clicks.is_some() { + unsupported_options.push("showCursorClicks".to_string()); + } + if options.capture_rect.is_some() { + unsupported_options.push("captureRect".to_string()); + } + if options.color_range.is_some() { + unsupported_options.push("colorRange".to_string()); + } + if options.color_space.is_some() { + unsupported_options.push("colorSpace".to_string()); + } + assert!( + unsupported_options.len() <= START_OPTION_UNSUPPORTED_LIMIT, + "unsupported start-option list bounded" + ); + + Ok(CaptureStartOptionsDiagnostics { + show_cursor_clicks: options.show_cursor_clicks, + capture_rect: options.capture_rect, + color_range: options.color_range, + color_space: options.color_space, + unsupported_options, + }) +} + +fn validate_capture_rect(rect: Option<&ScreenCaptureRect>) -> Result<()> { + let Some(rect) = rect else { + return Ok(()); + }; + if !rect.x.is_finite() || !rect.y.is_finite() { + return Err(napi::Error::new( + Status::InvalidArg, + "captureRect x/y must be finite numbers", + )); + } + if !rect.width.is_finite() || !rect.height.is_finite() { + return Err(napi::Error::new( + Status::InvalidArg, + "captureRect width/height must be finite numbers", + )); + } + if rect.width <= 0.0 || rect.height <= 0.0 { + return Err(napi::Error::new( + Status::InvalidArg, + "captureRect requires positive width and height", + )); + } + Ok(()) +} + +fn validate_enum_option(value: Option<&str>, name: &str, allowed: &[&str]) -> Result<()> { + let Some(value) = value else { + return Ok(()); + }; + if allowed.contains(&value) { + return Ok(()); + } + Err(napi::Error::new( + Status::InvalidArg, + format!("invalid {name}: {value}"), + )) +} + +fn retain_native_frame_sink_handle( + value: Unknown<'_>, +) -> Result> { + if value.get_type()? != ValueType::External { + return Err(napi::Error::new( + Status::InvalidArg, + "ScreenCapture.setFrameSinkHandle expects a native external frame sink handle", + )); + } + + let raw_value = value.value(); + let mut data: *mut c_void = std::ptr::null_mut(); + let status = + unsafe { napi::sys::napi_get_value_external(raw_value.env, raw_value.value, &mut data) }; + if status != napi::sys::Status::napi_ok || data.is_null() { + return Err(napi::Error::new( + Status::InvalidArg, + "ScreenCapture.setFrameSinkHandle received an empty native external frame sink handle", + )); + } + + let handle = unsafe { + fluxer_screen_frame_bus::NativeScreenFrameSinkHandle::retain_from_raw( + data.cast::(), + ) + } + .ok_or_else(|| { + napi::Error::new( + Status::InvalidArg, + "ScreenCapture.setFrameSinkHandle received an invalid native frame sink handle", + ) + })?; + + Ok(Arc::new(handle)) +} + +impl Drop for ScreenCapture { + fn drop(&mut self) { + self.inner.native_frame_sink.lock().take(); + } +} + +#[cfg(target_os = "windows")] +impl ScreenCapture { + #[allow(clippy::too_many_arguments)] + fn start_windows( + &self, + source_id: String, + source_kind: String, + width: Option, + height: Option, + frame_rate: Option, + hook_path: Option, + hook_path_x86: Option, + injection_method: Option, + _start_options: CaptureStartOptionsDiagnostics, + ) -> Result { + use std::sync::atomic::Ordering; + + if self.inner.running.load(Ordering::Acquire) { + return Err(napi::Error::from_reason("Capture already running")); + } + + if source_kind == "game" { + return self.start_windows_game( + source_id, + source_kind, + width, + height, + frame_rate, + hook_path, + hook_path_x86, + injection_method, + _start_options, + ); + } + let _ = (hook_path, hook_path_x86, injection_method); + let target_frame_rate = frame_rate.unwrap_or(30).clamp(1, 144); + let frame_interval = + std::time::Duration::from_nanos(1_000_000_000 / target_frame_rate as u64); + + if source_kind == "screen" { + let monitor = wgc_capture::parse_monitor_source_id(&source_id, &source_kind) + .ok_or_else(|| { + napi::Error::from_reason(format!("Invalid source: {source_kind}:{source_id}")) + })?; + if !wgc_capture::wgc_capture_supported() { + return Err(napi::Error::from_reason( + "Windows Graphics Capture is unavailable for screen capture", + )); + } + let session = WgcCaptureSession::new_monitor(monitor, width, height).map_err(|e| { + napi::Error::from_reason(format!("Failed to create WGC screen capture: {e}")) + })?; + return self.start_windows_wgc_session(session, target_frame_rate); + } + + let hwnd = + dxgi_capture::parse_window_source_id(&source_id, &source_kind).ok_or_else(|| { + napi::Error::from_reason(format!("Invalid source: {source_kind}:{source_id}")) + })?; + + if let Some(result) = self.try_start_windows_wgc(hwnd, width, height, target_frame_rate)? { + return Ok(result); + } + + let session = DxgiCaptureSession::new(hwnd, width, height) + .map_err(|e| napi::Error::from_reason(format!("Failed to create DXGI capture: {e}")))?; + + let capture_width = session.capture_width(); + let capture_height = session.capture_height(); + + { + let mut guard = self.inner.session.lock(); + *guard = Some(session); + } + { + let mut guard = self.inner.fallback.lock(); + *guard = Some(fallback::FallbackTracker::new( + fallback::CaptureStrategy::DxgiDuplication, + )); + } + + self.inner.running.store(true, Ordering::Release); + + let inner = Arc::clone(&self.inner); + + std::thread::Builder::new() + .name("dxgi-capture".into()) + .spawn(move || { + dxgi_capture::capture_loop(&inner, frame_interval); + }) + .map_err(|e| { + napi::Error::from_reason(format!("Failed to spawn capture thread: {e}")) + })?; + + Ok(CaptureStartResult { + width: capture_width, + height: capture_height, + frame_rate: target_frame_rate, + pixel_format: "bgra".to_string(), + }) + } + + fn try_start_windows_wgc( + &self, + hwnd: windows::Win32::Foundation::HWND, + width: Option, + height: Option, + target_frame_rate: u32, + ) -> Result> { + assert!(target_frame_rate >= 1, "frame rate at least 1"); + assert!(target_frame_rate <= 144, "frame rate at most 144"); + if !wgc_capture::wgc_capture_supported() { + return Ok(None); + } + let session = match WgcCaptureSession::new(hwnd, width, height) { + Ok(session) => session, + Err(e) => { + emit_lifecycle( + &self.inner, + "diagnostic", + &format!("WGC window capture unavailable; using DXGI duplication: {e}"), + ); + return Ok(None); + } + }; + self.start_windows_wgc_session(session, target_frame_rate) + .map(Some) + } + + fn start_windows_wgc_session( + &self, + session: WgcCaptureSession, + target_frame_rate: u32, + ) -> Result { + assert!(target_frame_rate >= 1, "frame rate at least 1"); + assert!(target_frame_rate <= 144, "frame rate at most 144"); + let capture_width = session.capture_width(); + let capture_height = session.capture_height(); + + { + let mut guard = self.inner.wgc_session.lock(); + *guard = Some(session); + } + { + let mut guard = self.inner.fallback.lock(); + *guard = Some(fallback::FallbackTracker::new( + fallback::CaptureStrategy::Wgc, + )); + } + + self.inner.running.store(true, Ordering::Release); + + let inner = Arc::clone(&self.inner); + let frame_interval = + std::time::Duration::from_nanos(1_000_000_000 / target_frame_rate as u64); + + std::thread::Builder::new() + .name("wgc-capture".into()) + .spawn(move || { + wgc_capture::capture_loop(&inner, frame_interval); + }) + .map_err(|e| { + napi::Error::from_reason(format!("Failed to spawn WGC capture thread: {e}")) + })?; + + Ok(CaptureStartResult { + width: capture_width, + height: capture_height, + frame_rate: target_frame_rate, + pixel_format: "bgra".to_string(), + }) + } + + #[allow(clippy::too_many_arguments)] + fn start_windows_game( + &self, + source_id: String, + source_kind: String, + width: Option, + height: Option, + frame_rate: Option, + hook_path: Option, + hook_path_x86: Option, + injection_method: Option, + _start_options: CaptureStartOptionsDiagnostics, + ) -> Result { + use std::sync::atomic::Ordering; + + if game_capture_abi::env_flag_enabled(game_capture_abi::ENV_DISABLE_HOOK) { + return Err(napi::Error::from_reason( + "game capture hook disabled via FLUXER_GAME_CAPTURE_DISABLE_HOOK", + )); + } + + let hook_path = hook_path + .ok_or_else(|| napi::Error::from_reason("missing game capture hook DLL path"))?; + let target_frame_rate = frame_rate.unwrap_or(30).clamp(1, 144); + let session = GameCaptureSession::new( + &source_id, + &source_kind, + width, + height, + target_frame_rate, + &hook_path, + hook_path_x86.as_deref(), + injection_method.as_deref(), + ) + .map_err(|e| napi::Error::from_reason(format!("Failed to create game capture: {e}")))?; + let capture_width = session.capture_width(); + let capture_height = session.capture_height(); + let session = Arc::new(session); + + { + let mut guard = self.inner.game_session.lock(); + *guard = Some(session); + } + { + let mut guard = self.inner.fallback.lock(); + *guard = Some(fallback::FallbackTracker::new( + fallback::CaptureStrategy::GameHook, + )); + } + + self.inner.running.store(true, Ordering::Release); + + let inner = Arc::clone(&self.inner); + let frame_interval = + std::time::Duration::from_nanos(1_000_000_000 / target_frame_rate as u64); + + std::thread::Builder::new() + .name("game-capture".into()) + .spawn(move || { + game_capture::capture_loop(&inner, frame_interval); + }) + .map_err(|e| { + napi::Error::from_reason(format!("Failed to spawn game capture thread: {e}")) + })?; + + Ok(CaptureStartResult { + width: capture_width, + height: capture_height, + frame_rate: target_frame_rate, + pixel_format: "bgra".to_string(), + }) + } +} + +fn strategy_only_diagnostics( + snapshot: &fallback::FallbackSnapshot, + requested_injection_method: String, + injection_method: String, + start_options: CaptureStartOptionsDiagnostics, + frame_sink: FrameSinkCounterSnapshot, +) -> CaptureDiagnostics { + CaptureDiagnostics { + state: 0, + api_type: 0, + transport: 0, + fallback_reason: 0, + capture_flags: 0, + width: 0, + height: 0, + dxgi_format: 0, + frame_counter: 0.0, + dropped_frame_counter: 0.0, + last_present_timestamp_us: 0.0, + last_error: 0, + requested_injection_method, + injection_method, + active_strategy: snapshot.active_strategy.clone(), + last_fallback_reason: snapshot.last_fallback_reason.clone(), + start_options, + frame_sink_accepted: frame_sink.accepted as f64, + frame_sink_coalesced: frame_sink.coalesced as f64, + frame_sink_rejected: frame_sink.rejected as f64, + media_frames_dropped_without_sink: frame_sink.dropped_without_sink as f64, + cpu_fallback_frames_dropped: frame_sink.cpu_fallback_dropped as f64, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn start_options_are_kept_as_explicit_unsupported_state() { + let state = build_start_option_diagnostics(ScreenCaptureStartOptions { + show_cursor_clicks: Some(true), + capture_rect: Some(ScreenCaptureRect { + x: 10.0, + y: 20.0, + width: 300.0, + height: 200.0, + }), + color_range: Some("full".to_string()), + color_space: Some("rec709".to_string()), + }) + .expect("valid options"); + + assert_eq!(state.show_cursor_clicks, Some(true)); + assert_eq!(state.color_range.as_deref(), Some("full")); + assert_eq!(state.color_space.as_deref(), Some("rec709")); + assert_eq!( + state.unsupported_options, + vec![ + "showCursorClicks".to_string(), + "captureRect".to_string(), + "colorRange".to_string(), + "colorSpace".to_string(), + ] + ); + } + + #[test] + fn capture_rect_requires_positive_dimensions() { + let err = build_start_option_diagnostics(ScreenCaptureStartOptions { + capture_rect: Some(ScreenCaptureRect { + x: 0.0, + y: 0.0, + width: 0.0, + height: 10.0, + }), + ..ScreenCaptureStartOptions::default() + }) + .err(); + assert!(err.is_some(), "invalid captureRect is rejected"); + } +} + +#[napi(js_name = "isSupported")] +pub fn is_supported() -> bool { + cfg!(target_os = "windows") +} + +#[napi(js_name = "getAvailability")] +pub fn get_availability() -> AvailabilityInfo { + AvailabilityInfo { + available: cfg!(target_os = "windows"), + backend: "windows-game-capture".to_string(), + reason: if cfg!(target_os = "windows") { + None + } else { + Some("unsupported-platform".to_string()) + }, + } +} + +#[napi(js_name = "listSources")] +pub fn list_sources() -> Result> { + Ok(sources::list_sources()) +} + +#[napi(js_name = "elevateGpuSchedulingPriority")] +pub fn elevate_gpu_scheduling_priority( + process_id: Option, + priority_class: Option, +) -> Result<()> { + gpu_priority::elevate(process_id, priority_class).map_err(napi::Error::from_reason) +} + +#[napi(js_name = "restoreGpuSchedulingPriority")] +pub fn restore_gpu_scheduling_priority(process_id: Option) -> Result<()> { + gpu_priority::restore(process_id).map_err(napi::Error::from_reason) +} + +#[napi(js_name = "registerVulkanLayerManifest")] +pub fn register_vulkan_layer_manifest(manifest_path: String) -> Result<()> { + #[cfg(target_os = "windows")] + { + vulkan_layer_registry::register_manifest(&manifest_path).map_err(napi::Error::from_reason) + } + #[cfg(not(target_os = "windows"))] + { + let _ = manifest_path; + Err(napi::Error::from_reason( + "Vulkan game capture layer only supported on Windows", + )) + } +} + +#[napi(js_name = "unregisterVulkanLayerManifest")] +pub fn unregister_vulkan_layer_manifest(manifest_path: String) -> Result<()> { + #[cfg(target_os = "windows")] + { + vulkan_layer_registry::unregister_manifest(&manifest_path).map_err(napi::Error::from_reason) + } + #[cfg(not(target_os = "windows"))] + { + let _ = manifest_path; + Err(napi::Error::from_reason( + "Vulkan game capture layer only supported on Windows", + )) + } +} + +#[napi(js_name = "getVulkanLayerRegistrationState")] +pub fn get_vulkan_layer_registration_state(manifest_path: String) -> VulkanLayerRegistrationState { + #[cfg(target_os = "windows")] + { + let state = vulkan_layer_registry::registration_state(&manifest_path); + VulkanLayerRegistrationState { + registered: state.registered, + manifest_exists: state.manifest_exists, + dll_exists: state.dll_exists, + manifest_path: state.manifest_path, + } + } + #[cfg(not(target_os = "windows"))] + { + VulkanLayerRegistrationState { + registered: false, + manifest_exists: false, + dll_exists: false, + manifest_path, + } + } +} diff --git a/fluxer_desktop/native/win-game-capture/src/nv12_gpu.rs b/fluxer_desktop/native/win-game-capture/src/nv12_gpu.rs new file mode 100644 index 0000000..bfebe1b --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/src/nv12_gpu.rs @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use windows::Win32::Graphics::Direct3D11::{ + D3D11_BIND_RENDER_TARGET, D3D11_RESOURCE_MISC_SHARED, D3D11_TEXTURE2D_DESC, + D3D11_USAGE_DEFAULT, D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE, D3D11_VIDEO_PROCESSOR_CONTENT_DESC, + D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC, D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC_0, + D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC, D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC_0, + D3D11_VIDEO_PROCESSOR_STREAM, D3D11_VIDEO_USAGE_PLAYBACK_NORMAL, + D3D11_VPIV_DIMENSION_TEXTURE2D, D3D11_VPOV_DIMENSION_TEXTURE2D, ID3D11Device, + ID3D11DeviceContext, ID3D11Texture2D, ID3D11VideoContext, ID3D11VideoContext1, + ID3D11VideoDevice, ID3D11VideoProcessor, ID3D11VideoProcessorEnumerator, + ID3D11VideoProcessorInputView, ID3D11VideoProcessorOutputView, +}; +use windows::Win32::Graphics::Dxgi::Common::{ + DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709, DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, + DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020, DXGI_COLOR_SPACE_TYPE, + DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709, DXGI_FORMAT_NV12, DXGI_RATIONAL, DXGI_SAMPLE_DESC, +}; +use windows::Win32::Graphics::Dxgi::IDXGIResource; +use windows::core::Interface; + +use crate::hdr; + +fn vlog(msg: &str) { + if crate::game_capture_abi::env_flag_enabled(crate::game_capture_abi::ENV_VERBOSE) { + use std::io::Write; + let _ = writeln!(std::io::stderr(), "[fluxer-nv12] {msg}"); + } +} + +pub const NV12_OUTPUT_SLOT_COUNT: usize = 3; + +struct Nv12OutputSlot { + _texture: ID3D11Texture2D, + view: ID3D11VideoProcessorOutputView, + handle: u64, +} + +pub struct Nv12GpuConverter { + _video_device: ID3D11VideoDevice, + video_context: ID3D11VideoContext, + processor: ID3D11VideoProcessor, + _enumerator: ID3D11VideoProcessorEnumerator, + input_view: ID3D11VideoProcessorInputView, + output_slots: [Nv12OutputSlot; NV12_OUTPUT_SLOT_COUNT], + slot_cursor: usize, + context: ID3D11DeviceContext, + out_width: u32, + out_height: u32, +} + +pub struct Nv12SharedTextureFrame { + pub handle: u64, + pub width: u32, + pub height: u32, + pub dxgi_format: u32, +} + +impl Nv12GpuConverter { + #[allow(clippy::too_many_arguments)] + pub fn new( + device: &ID3D11Device, + context: &ID3D11DeviceContext, + input: &ID3D11Texture2D, + in_width: u32, + in_height: u32, + out_width: u32, + out_height: u32, + source_format: hdr::SourceFormat, + ) -> Option { + let out_width = (out_width & !1).max(2); + let out_height = (out_height & !1).max(2); + let video_device = device + .cast::() + .inspect_err(|e| vlog(&format!("cast ID3D11VideoDevice failed: {e:?}"))) + .ok()?; + let video_context = context + .cast::() + .inspect_err(|e| vlog(&format!("cast ID3D11VideoContext failed: {e:?}"))) + .ok()?; + + let content_desc = D3D11_VIDEO_PROCESSOR_CONTENT_DESC { + InputFrameFormat: D3D11_VIDEO_FRAME_FORMAT_PROGRESSIVE, + InputFrameRate: DXGI_RATIONAL { + Numerator: 60, + Denominator: 1, + }, + InputWidth: in_width, + InputHeight: in_height, + OutputFrameRate: DXGI_RATIONAL { + Numerator: 60, + Denominator: 1, + }, + OutputWidth: out_width, + OutputHeight: out_height, + Usage: D3D11_VIDEO_USAGE_PLAYBACK_NORMAL, + }; + let enumerator = unsafe { video_device.CreateVideoProcessorEnumerator(&content_desc) } + .inspect_err(|e| vlog(&format!("CreateVideoProcessorEnumerator: {e:?}"))) + .ok()?; + let processor = unsafe { video_device.CreateVideoProcessor(&enumerator, 0) } + .inspect_err(|e| vlog(&format!("CreateVideoProcessor: {e:?}"))) + .ok()?; + + if let Ok(vctx1) = video_context.cast::() { + let input_cs = input_colour_space(source_format); + unsafe { + vctx1.VideoProcessorSetStreamColorSpace1(&processor, 0, input_cs); + vctx1.VideoProcessorSetOutputColorSpace1( + &processor, + DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709, + ); + } + vlog(&format!( + "video processor colour space set: input={} -> output=YCbCr studio Rec.709", + input_cs.0 + )); + } else { + vlog("ID3D11VideoContext1 unavailable; using default SDR Rec.709 colour space"); + } + + let output_desc = D3D11_TEXTURE2D_DESC { + Width: out_width, + Height: out_height, + MipLevels: 1, + ArraySize: 1, + Format: DXGI_FORMAT_NV12, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Usage: D3D11_USAGE_DEFAULT, + BindFlags: D3D11_BIND_RENDER_TARGET.0 as u32, + CPUAccessFlags: 0, + MiscFlags: D3D11_RESOURCE_MISC_SHARED.0 as u32, + }; + let mut output_slots = Vec::with_capacity(NV12_OUTPUT_SLOT_COUNT); + for _ in 0..NV12_OUTPUT_SLOT_COUNT { + output_slots.push(create_output_slot( + device, + &video_device, + &enumerator, + &output_desc, + )?); + } + assert_eq!( + output_slots.len(), + NV12_OUTPUT_SLOT_COUNT, + "all NV12 output slots created" + ); + let Ok(output_slots) = <[Nv12OutputSlot; NV12_OUTPUT_SLOT_COUNT]>::try_from(output_slots) + else { + vlog("NV12 output slot count mismatch"); + return None; + }; + + let input_view_desc = D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC { + FourCC: 0, + ViewDimension: D3D11_VPIV_DIMENSION_TEXTURE2D, + Anonymous: D3D11_VIDEO_PROCESSOR_INPUT_VIEW_DESC_0 { + Texture2D: windows::Win32::Graphics::Direct3D11::D3D11_TEX2D_VPIV { + MipSlice: 0, + ArraySlice: 0, + }, + }, + }; + let mut input_view = None; + unsafe { + video_device.CreateVideoProcessorInputView( + input, + &enumerator, + &input_view_desc, + Some(&mut input_view), + ) + } + .inspect_err(|e| vlog(&format!("CreateVideoProcessorInputView: {e:?}"))) + .ok()?; + let input_view = input_view?; + vlog(&format!( + "NV12 converter built OK ({in_width}x{in_height} -> {out_width}x{out_height})" + )); + + Some(Self { + _video_device: video_device, + video_context, + processor, + _enumerator: enumerator, + input_view, + output_slots, + slot_cursor: 0, + context: context.clone(), + out_width, + out_height, + }) + } + + pub fn dxgi_format(&self) -> u32 { + DXGI_FORMAT_NV12.0 as u32 + } + + pub fn convert_shared_texture(&mut self) -> Result { + assert!( + self.slot_cursor < NV12_OUTPUT_SLOT_COUNT, + "slot cursor in range" + ); + assert!(self.out_width >= 2, "output width at least 2"); + let slot_index = self.slot_cursor; + self.slot_cursor = (slot_index + 1) % NV12_OUTPUT_SLOT_COUNT; + self.run_video_processor(slot_index)?; + unsafe { + self.context.Flush(); + } + Ok(Nv12SharedTextureFrame { + handle: self.output_slots[slot_index].handle, + width: self.out_width, + height: self.out_height, + dxgi_format: self.dxgi_format(), + }) + } + + fn run_video_processor(&self, slot_index: usize) -> Result<(), String> { + assert!(slot_index < NV12_OUTPUT_SLOT_COUNT, "slot index in range"); + let mut stream = D3D11_VIDEO_PROCESSOR_STREAM { + Enable: windows::core::BOOL(1), + OutputIndex: 0, + InputFrameOrField: 0, + PastFrames: 0, + FutureFrames: 0, + ppPastSurfaces: std::ptr::null_mut(), + pInputSurface: std::mem::ManuallyDrop::new(Some(self.input_view.clone())), + ppFutureSurfaces: std::ptr::null_mut(), + ppPastSurfacesRight: std::ptr::null_mut(), + pInputSurfaceRight: std::mem::ManuallyDrop::new(None), + ppFutureSurfacesRight: std::ptr::null_mut(), + }; + let blt = unsafe { + self.video_context.VideoProcessorBlt( + &self.processor, + &self.output_slots[slot_index].view, + 0, + std::slice::from_ref(&stream), + ) + }; + unsafe { + std::mem::ManuallyDrop::drop(&mut stream.pInputSurface); + } + blt.inspect_err(|e| vlog(&format!("VideoProcessorBlt RGB->NV12: {e:?}"))) + .map_err(|e| format!("VideoProcessorBlt RGB->NV12: {e}")) + } +} + +fn create_output_slot( + device: &ID3D11Device, + video_device: &ID3D11VideoDevice, + enumerator: &ID3D11VideoProcessorEnumerator, + output_desc: &D3D11_TEXTURE2D_DESC, +) -> Option { + assert!(output_desc.Width >= 2, "output width at least 2"); + assert!(output_desc.Height >= 2, "output height at least 2"); + let mut output_texture = None; + unsafe { device.CreateTexture2D(output_desc, None, Some(&mut output_texture)) } + .inspect_err(|e| vlog(&format!("CreateTexture2D NV12 output: {e:?}"))) + .ok()?; + let output_texture = output_texture?; + let resource: IDXGIResource = output_texture + .cast() + .inspect_err(|e| { + vlog(&format!( + "QueryInterface IDXGIResource for NV12 output: {e:?}" + )) + }) + .ok()?; + let shared_handle = unsafe { resource.GetSharedHandle() } + .inspect_err(|e| vlog(&format!("GetSharedHandle NV12 output: {e:?}"))) + .ok()?; + if shared_handle.is_invalid() { + vlog("GetSharedHandle NV12 output returned an invalid handle"); + return None; + } + let shared_handle = shared_handle.0 as usize as u64; + + let output_view_desc = D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC { + ViewDimension: D3D11_VPOV_DIMENSION_TEXTURE2D, + Anonymous: D3D11_VIDEO_PROCESSOR_OUTPUT_VIEW_DESC_0 { + Texture2D: windows::Win32::Graphics::Direct3D11::D3D11_TEX2D_VPOV { MipSlice: 0 }, + }, + }; + let mut output_view = None; + unsafe { + video_device.CreateVideoProcessorOutputView( + &output_texture, + enumerator, + &output_view_desc, + Some(&mut output_view), + ) + } + .inspect_err(|e| vlog(&format!("CreateVideoProcessorOutputView: {e:?}"))) + .ok()?; + let output_view = output_view?; + + Some(Nv12OutputSlot { + _texture: output_texture, + view: output_view, + handle: shared_handle, + }) +} + +fn input_colour_space(source_format: hdr::SourceFormat) -> DXGI_COLOR_SPACE_TYPE { + match source_format { + hdr::SourceFormat::R10G10B10A2 { hdr: true } => DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020, + hdr::SourceFormat::Rgba16Float { hdr: true } => DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709, + _ => DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709, + } +} + +unsafe impl Send for Nv12GpuConverter {} + +#[cfg(test)] +mod tests { + use super::*; + + fn cs_value(source_format: hdr::SourceFormat) -> i32 { + input_colour_space(source_format).0 + } + + #[test] + fn eight_bit_sources_use_sdr_rec709_colour_space() { + assert_eq!( + cs_value(hdr::SourceFormat::Bgra8), + DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709.0 + ); + assert_eq!( + cs_value(hdr::SourceFormat::Rgba8), + DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709.0 + ); + } + + #[test] + fn unflagged_high_precision_sources_stay_sdr_rec709() { + assert_eq!( + cs_value(hdr::SourceFormat::R10G10B10A2 { hdr: false }), + DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709.0 + ); + assert_eq!( + cs_value(hdr::SourceFormat::Rgba16Float { hdr: false }), + DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709.0 + ); + } + + #[test] + fn ten_bit_hdr_uses_pq_rec2020_input_space() { + assert_eq!( + cs_value(hdr::SourceFormat::R10G10B10A2 { hdr: true }), + DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020.0 + ); + } + + #[test] + fn fp16_hdr_uses_linear_extended_rec709_input_space() { + assert_eq!( + cs_value(hdr::SourceFormat::Rgba16Float { hdr: true }), + DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709.0 + ); + } +} diff --git a/fluxer_desktop/native/win-game-capture/src/sources.rs b/fluxer_desktop/native/win-game-capture/src/sources.rs new file mode 100644 index 0000000..69f8816 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/src/sources.rs @@ -0,0 +1,168 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use crate::ScreenCaptureSourceDescriptor; + +#[cfg(target_os = "windows")] +use std::ptr::null_mut; + +#[cfg(target_os = "windows")] +use windows_sys::Win32::{ + Foundation::{HWND, LPARAM, RECT}, + Graphics::Gdi::{EnumDisplayMonitors, HMONITOR}, + System::Threading::GetCurrentProcessId, + UI::WindowsAndMessaging::{ + EnumWindows, GW_OWNER, GWL_STYLE, GetWindow, GetWindowLongPtrW, GetWindowRect, + GetWindowTextLengthW, GetWindowTextW, GetWindowThreadProcessId, IsWindowVisible, WS_CHILD, + }, +}; + +#[cfg(target_os = "windows")] +const MONITOR_SOURCE_LIMIT: usize = 16; + +pub fn list_sources() -> Vec { + #[cfg(target_os = "windows")] + { + let mut sources = enumerate_monitor_sources(); + sources.extend(enumerate_window_sources()); + sources + } + + #[cfg(not(target_os = "windows"))] + { + Vec::new() + } +} + +#[cfg(target_os = "windows")] +fn enumerate_monitor_sources() -> Vec { + unsafe extern "system" fn enum_monitor( + _monitor: HMONITOR, + _hdc: windows_sys::Win32::Graphics::Gdi::HDC, + rect: *mut RECT, + param: LPARAM, + ) -> i32 { + let monitors = &mut *(param as *mut Vec); + if monitors.len() >= MONITOR_SOURCE_LIMIT { + return 0; + } + if !rect.is_null() { + monitors.push(*rect); + } + 1 + } + + let mut monitors: Vec = Vec::new(); + unsafe { + EnumDisplayMonitors( + null_mut(), + null_mut(), + Some(enum_monitor), + &mut monitors as *mut _ as LPARAM, + ); + } + assert!( + monitors.len() <= MONITOR_SOURCE_LIMIT, + "monitor source enumeration bounded" + ); + + monitors + .into_iter() + .enumerate() + .map(|(index, rect)| { + let width = (rect.right - rect.left).max(0) as u32; + let height = (rect.bottom - rect.top).max(0) as u32; + ScreenCaptureSourceDescriptor { + kind: "screen".to_string(), + id: format!("screen:{index}:0"), + name: format!("Display {}", index + 1), + width, + height, + target_pid: None, + } + }) + .collect() +} + +#[cfg(target_os = "windows")] +fn enumerate_window_sources() -> Vec { + struct EnumState { + own_pid: u32, + sources: Vec, + } + + unsafe extern "system" fn enum_window(hwnd: HWND, param: LPARAM) -> i32 { + let state = &mut *(param as *mut EnumState); + if let Some(source) = describe_window_source(hwnd, state.own_pid) { + state.sources.push(source); + } + 1 + } + + let mut state = EnumState { + own_pid: unsafe { GetCurrentProcessId() }, + sources: Vec::new(), + }; + unsafe { + EnumWindows(Some(enum_window), &mut state as *mut _ as LPARAM); + } + state.sources +} + +#[cfg(target_os = "windows")] +fn describe_window_source(hwnd: HWND, own_pid: u32) -> Option { + if hwnd.is_null() || unsafe { IsWindowVisible(hwnd) } == 0 { + return None; + } + if !unsafe { GetWindow(hwnd, GW_OWNER) }.is_null() { + return None; + } + let style = unsafe { GetWindowLongPtrW(hwnd, GWL_STYLE) } as u32; + if style & WS_CHILD != 0 { + return None; + } + + let mut pid = 0u32; + unsafe { + GetWindowThreadProcessId(hwnd, &mut pid); + } + if pid == 0 || pid == own_pid { + return None; + } + + let mut rect = RECT::default(); + if unsafe { GetWindowRect(hwnd, &mut rect) } == 0 { + return None; + } + let width = (rect.right - rect.left).max(0) as u32; + let height = (rect.bottom - rect.top).max(0) as u32; + if width == 0 || height == 0 { + return None; + } + + let title = window_title(hwnd).unwrap_or_else(|| format!("Window {pid}")); + Some(ScreenCaptureSourceDescriptor { + kind: "window".to_string(), + id: format!("window:{}:0", hwnd as usize), + name: title, + width, + height, + target_pid: Some(pid), + }) +} + +#[cfg(target_os = "windows")] +fn window_title(hwnd: HWND) -> Option { + let len = unsafe { GetWindowTextLengthW(hwnd) }; + if len <= 0 { + return None; + } + let mut buf = vec![0u16; (len + 1) as usize]; + let copied = unsafe { GetWindowTextW(hwnd, buf.as_mut_ptr(), buf.len() as i32) }; + if copied <= 0 { + return None; + } + let title = String::from_utf16_lossy(&buf[..copied as usize]) + .trim() + .to_string(); + if title.is_empty() { None } else { Some(title) } +} diff --git a/fluxer_desktop/native/win-game-capture/src/vulkan_layer_registry.rs b/fluxer_desktop/native/win-game-capture/src/vulkan_layer_registry.rs new file mode 100644 index 0000000..5a40575 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/src/vulkan_layer_registry.rs @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::path::{Path, PathBuf}; +use std::ptr::{null, null_mut}; +use windows_sys::Win32::{ + Foundation::{ERROR_FILE_NOT_FOUND, ERROR_SUCCESS}, + System::Registry::{ + HKEY, HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE, KEY_QUERY_VALUE, KEY_SET_VALUE, REG_DWORD, + REG_OPTION_NON_VOLATILE, REG_VALUE_TYPE, RegCloseKey, RegCreateKeyExW, RegDeleteValueW, + RegOpenKeyExW, RegQueryValueExW, RegSetValueExW, + }, +}; + +const VULKAN_IMPLICIT_LAYERS_KEY: &str = "Software\\Khronos\\Vulkan\\ImplicitLayers"; + +const HKCU: HKEY = HKEY_CURRENT_USER; +const HKLM: HKEY = HKEY_LOCAL_MACHINE; + +pub struct RegistrationState { + pub registered: bool, + pub manifest_exists: bool, + pub dll_exists: bool, + pub manifest_path: String, +} + +fn wide(value: &str) -> Vec { + value.encode_utf16().chain(std::iter::once(0)).collect() +} + +fn manifest_dll_path(manifest_path: &Path) -> Option { + let dir = manifest_path.parent()?; + if let Ok(contents) = std::fs::read_to_string(manifest_path) + && let Some(library) = extract_library_path(&contents) + { + let candidate = Path::new(&library); + if candidate.is_absolute() { + return Some(candidate.to_path_buf()); + } + return Some(dir.join(candidate)); + } + None +} + +fn extract_library_path(contents: &str) -> Option { + let key = "\"library_path\""; + let key_pos = contents.find(key)?; + let rest = contents[key_pos + key.len()..].trim_start(); + let rest = rest.strip_prefix(':')?.trim_start(); + let rest = rest.strip_prefix('"')?; + let end = rest.find('"')?; + let raw = &rest[..end]; + Some(raw.replace("\\\\", "\\")) +} + +pub fn register_manifest(manifest_path: &str) -> Result<(), String> { + if manifest_path.trim().is_empty() { + return Err("Vulkan layer manifest path is empty".into()); + } + let manifest = Path::new(manifest_path); + if !manifest.is_file() { + return Err(format!( + "Vulkan layer manifest does not exist: {}", + manifest.display() + )); + } + if let Some(dll) = manifest_dll_path(manifest) + && !dll.is_file() + { + return Err(format!( + "Vulkan layer DLL referenced by manifest does not exist: {}", + dll.display() + )); + } + + set_value_under(HKCU, manifest_path)?; + let _ = set_value_under(HKLM, manifest_path); + Ok(()) +} + +fn set_value_under(root: HKEY, manifest_path: &str) -> Result<(), String> { + let subkey = wide(VULKAN_IMPLICIT_LAYERS_KEY); + let value_name = wide(manifest_path); + let enabled: u32 = 0; + let mut key: HKEY = null_mut(); + let create_status = unsafe { + RegCreateKeyExW( + root, + subkey.as_ptr(), + 0, + null(), + REG_OPTION_NON_VOLATILE, + KEY_SET_VALUE, + null(), + &mut key, + null_mut(), + ) + }; + if create_status != ERROR_SUCCESS { + return Err(format!( + "RegCreateKeyExW Vulkan implicit layers failed: {create_status}" + )); + } + let set_status = unsafe { + RegSetValueExW( + key, + value_name.as_ptr(), + 0, + REG_DWORD, + (&enabled as *const u32).cast(), + std::mem::size_of::() as u32, + ) + }; + unsafe { + RegCloseKey(key); + } + if set_status != ERROR_SUCCESS { + return Err(format!( + "RegSetValueExW Vulkan implicit layer manifest failed: {set_status}" + )); + } + Ok(()) +} + +pub fn unregister_manifest(manifest_path: &str) -> Result<(), String> { + if manifest_path.trim().is_empty() { + return Err("Vulkan layer manifest path is empty".into()); + } + let hkcu = delete_value_under(HKCU, manifest_path); + let _ = delete_value_under(HKLM, manifest_path); + hkcu +} + +fn delete_value_under(root: HKEY, manifest_path: &str) -> Result<(), String> { + let subkey = wide(VULKAN_IMPLICIT_LAYERS_KEY); + let value_name = wide(manifest_path); + let mut key: HKEY = null_mut(); + let open_status = unsafe { RegOpenKeyExW(root, subkey.as_ptr(), 0, KEY_SET_VALUE, &mut key) }; + if open_status == ERROR_FILE_NOT_FOUND { + return Ok(()); + } + if open_status != ERROR_SUCCESS { + return Err(format!( + "RegOpenKeyExW Vulkan implicit layers failed: {open_status}" + )); + } + let delete_status = unsafe { RegDeleteValueW(key, value_name.as_ptr()) }; + unsafe { + RegCloseKey(key); + } + if delete_status == ERROR_SUCCESS || delete_status == ERROR_FILE_NOT_FOUND { + Ok(()) + } else { + Err(format!( + "RegDeleteValueW Vulkan implicit layer manifest failed: {delete_status}" + )) + } +} + +pub fn registration_state(manifest_path: &str) -> RegistrationState { + let manifest = Path::new(manifest_path); + let manifest_exists = manifest.is_file(); + let dll_exists = manifest_dll_path(manifest) + .map(|dll| dll.is_file()) + .unwrap_or(false); + let registered = registry_value_present(manifest_path); + RegistrationState { + registered, + manifest_exists, + dll_exists, + manifest_path: manifest_path.to_string(), + } +} + +fn registry_value_present(manifest_path: &str) -> bool { + if manifest_path.trim().is_empty() { + return false; + } + registry_value_present_under(HKCU, manifest_path) + || registry_value_present_under(HKLM, manifest_path) +} + +fn registry_value_present_under(root: HKEY, manifest_path: &str) -> bool { + let subkey = wide(VULKAN_IMPLICIT_LAYERS_KEY); + let value_name = wide(manifest_path); + let mut key: HKEY = null_mut(); + let open_status = unsafe { RegOpenKeyExW(root, subkey.as_ptr(), 0, KEY_QUERY_VALUE, &mut key) }; + if open_status != ERROR_SUCCESS { + return false; + } + let mut value_type: REG_VALUE_TYPE = 0; + let query_status = unsafe { + RegQueryValueExW( + key, + value_name.as_ptr(), + null(), + &mut value_type, + null_mut(), + null_mut(), + ) + }; + unsafe { + RegCloseKey(key); + } + query_status == ERROR_SUCCESS +} diff --git a/fluxer_desktop/native/win-game-capture/src/wgc_capture.rs b/fluxer_desktop/native/win-game-capture/src/wgc_capture.rs new file mode 100644 index 0000000..fc27ef8 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/src/wgc_capture.rs @@ -0,0 +1,885 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::sync::{Arc, atomic::Ordering}; + +use windows::Graphics::Capture::{ + Direct3D11CaptureFrame, Direct3D11CaptureFramePool, GraphicsCaptureItem, GraphicsCaptureSession, +}; +use windows::Graphics::DirectX::Direct3D11::IDirect3DDevice; +use windows::Graphics::DirectX::DirectXPixelFormat; +use windows::Graphics::SizeInt32; +use windows::Win32::Foundation::{HWND, LPARAM, RECT, RPC_E_CHANGED_MODE}; +use windows::Win32::Graphics::Direct3D11::{ + D3D11_BIND_SHADER_RESOURCE, D3D11_BOX, D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, ID3D11Device, + ID3D11DeviceContext, ID3D11Resource, ID3D11Texture2D, +}; +use windows::Win32::Graphics::Dxgi::Common::{ + DXGI_FORMAT, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_SAMPLE_DESC, +}; +use windows::Win32::Graphics::Dxgi::IDXGIDevice; +use windows::Win32::Graphics::Gdi::{EnumDisplayMonitors, HDC, HMONITOR}; +use windows::Win32::System::WinRT::Direct3D11::{ + CreateDirect3D11DeviceFromDXGIDevice, IDirect3DDxgiInterfaceAccess, +}; +use windows::Win32::System::WinRT::Graphics::Capture::IGraphicsCaptureItemInterop; +use windows::Win32::System::WinRT::{RO_INIT_MULTITHREADED, RoInitialize}; +use windows::Win32::UI::WindowsAndMessaging::IsWindow; +use windows::core::{BOOL, Interface}; + +use crate::dxgi_capture::{ + SharedTextureOutput, capture_timestamp_us, create_shared_output_texture, + pacing_sleep_and_next_deadline, resolve_output_size, +}; +use crate::nv12_gpu::Nv12GpuConverter; +use crate::{ + CaptureInner, emit_lifecycle, emit_shared_texture_frame, note_media_frame_without_sink, + resolve_frame_sink, +}; + +const WGC_FRAME_POOL_BUFFERS: i32 = 2; +const WGC_FRAME_DRAIN_LIMIT: u32 = 4; +const WGC_MONITOR_ENUM_LIMIT: usize = 16; + +fn ensure_winrt_initialized() { + let result = unsafe { RoInitialize(RO_INIT_MULTITHREADED) }; + if let Err(error) = result { + assert!( + error.code() == RPC_E_CHANGED_MODE, + "RoInitialize failed unexpectedly: {error:?}" + ); + } +} + +pub fn wgc_capture_supported() -> bool { + ensure_winrt_initialized(); + GraphicsCaptureSession::IsSupported().unwrap_or(false) +} + +#[derive(Clone, Copy)] +pub enum WgcCaptureTarget { + Window(HWND), + Monitor(HMONITOR), +} + +impl WgcCaptureTarget { + fn create_item( + self, + interop: &IGraphicsCaptureItemInterop, + ) -> Result { + match self { + Self::Window(hwnd) => unsafe { interop.CreateForWindow(hwnd) } + .map_err(|e| format!("IGraphicsCaptureItemInterop::CreateForWindow: {e}")), + Self::Monitor(monitor) => unsafe { interop.CreateForMonitor(monitor) } + .map_err(|e| format!("IGraphicsCaptureItemInterop::CreateForMonitor: {e}")), + } + } + + fn is_alive(self) -> bool { + match self { + Self::Window(hwnd) => unsafe { IsWindow(Some(hwnd)) }.as_bool(), + Self::Monitor(monitor) => !monitor.is_invalid(), + } + } + + fn closed_message(self) -> &'static str { + match self { + Self::Window(_) => "window closed", + Self::Monitor(_) => "monitor capture target unavailable", + } + } +} + +pub fn parse_monitor_source_id(source_id: &str, source_kind: &str) -> Option { + if source_kind != "screen" { + return None; + } + let mut parts = source_id.split(':'); + if parts.next()? != "screen" { + return None; + } + let index = parts.next()?.parse::().ok()?; + let sub_id = parts.next()?; + if sub_id != "0" && sub_id != "1" { + return None; + } + if parts.next().is_some() || index >= WGC_MONITOR_ENUM_LIMIT { + return None; + } + enumerate_monitors().get(index).copied() +} + +fn enumerate_monitors() -> Vec { + struct MonitorEnumState { + monitors: Vec, + } + + unsafe extern "system" fn enum_monitor( + monitor: HMONITOR, + _hdc: HDC, + _rect: *mut RECT, + param: LPARAM, + ) -> BOOL { + let state = unsafe { &mut *(param.0 as *mut MonitorEnumState) }; + if state.monitors.len() >= WGC_MONITOR_ENUM_LIMIT { + return BOOL(0); + } + if !monitor.is_invalid() { + state.monitors.push(monitor); + } + BOOL(1) + } + + let mut state = MonitorEnumState { + monitors: Vec::with_capacity(WGC_MONITOR_ENUM_LIMIT), + }; + unsafe { + let _ = EnumDisplayMonitors( + None, + None, + Some(enum_monitor), + LPARAM((&mut state as *mut MonitorEnumState) as isize), + ); + } + assert!( + state.monitors.len() <= WGC_MONITOR_ENUM_LIMIT, + "monitor enumeration bounded" + ); + state.monitors +} + +pub struct WgcCaptureSession { + device: ID3D11Device, + context: ID3D11DeviceContext, + d3d_device: IDirect3DDevice, + item: GraphicsCaptureItem, + target: WgcCaptureTarget, + output_width: u32, + output_height: u32, + requested_width: Option, + requested_height: Option, +} + +unsafe impl Send for WgcCaptureSession {} + +impl WgcCaptureSession { + pub fn new( + hwnd: HWND, + requested_width: Option, + requested_height: Option, + ) -> Result { + Self::new_for_target( + WgcCaptureTarget::Window(hwnd), + requested_width, + requested_height, + ) + } + + pub fn new_monitor( + monitor: HMONITOR, + requested_width: Option, + requested_height: Option, + ) -> Result { + Self::new_for_target( + WgcCaptureTarget::Monitor(monitor), + requested_width, + requested_height, + ) + } + + fn new_for_target( + target: WgcCaptureTarget, + requested_width: Option, + requested_height: Option, + ) -> Result { + ensure_winrt_initialized(); + let (device, context) = crate::game_capture::create_shared_texture_device(None)?; + let dxgi_device: IDXGIDevice = device + .cast() + .map_err(|e| format!("IDXGIDevice cast: {e}"))?; + let inspectable = unsafe { CreateDirect3D11DeviceFromDXGIDevice(&dxgi_device) } + .map_err(|e| format!("CreateDirect3D11DeviceFromDXGIDevice: {e}"))?; + let d3d_device: IDirect3DDevice = inspectable + .cast() + .map_err(|e| format!("IDirect3DDevice cast: {e}"))?; + let interop = windows::core::factory::() + .map_err(|e| format!("GraphicsCaptureItem interop factory: {e}"))?; + let item = target.create_item(&interop)?; + let size = item + .Size() + .map_err(|e| format!("GraphicsCaptureItem.Size: {e}"))?; + let content_width = size.Width.max(1) as u32; + let content_height = size.Height.max(1) as u32; + let (output_width, output_height) = resolve_output_size( + content_width, + content_height, + requested_width, + requested_height, + ); + assert!(output_width > 0, "WGC output width positive"); + assert!(output_height > 0, "WGC output height positive"); + Ok(Self { + device, + context, + d3d_device, + item, + target, + output_width, + output_height, + requested_width, + requested_height, + }) + } + + pub fn capture_width(&self) -> u32 { + assert!(self.output_width > 0, "WGC output width positive"); + assert!(self.output_height > 0, "WGC output height positive"); + self.output_width + } + + pub fn capture_height(&self) -> u32 { + assert!(self.output_height > 0, "WGC output height positive"); + assert!(self.output_width > 0, "WGC output width positive"); + self.output_height + } +} + +struct WgcState { + frame_pool: Direct3D11CaptureFramePool, + session: GraphicsCaptureSession, + output_pipeline: Option, + pixel_format: DirectXPixelFormat, + content_width: u32, + content_height: u32, + out_w: u32, + out_h: u32, +} + +impl WgcState { + fn close(self) { + let _ = self.session.Close(); + let _ = self.frame_pool.Close(); + } +} + +struct WgcNv12Pipeline { + input_resource: ID3D11Resource, + converter: Nv12GpuConverter, +} + +enum WgcOutputPipeline { + Bgra(SharedTextureOutput), + Nv12(WgcNv12Pipeline), +} + +enum WgcFrameResult { + Ok, + NoFrame, + Resized { width: u32, height: u32 }, + Error(String), +} + +enum LoopStep { + Paced, + Restart, +} + +struct WgcLoopContext { + device: ID3D11Device, + context: ID3D11DeviceContext, + d3d_device: IDirect3DDevice, + item: GraphicsCaptureItem, + target: WgcCaptureTarget, + requested_width: Option, + requested_height: Option, +} + +fn wgc_loop_context(inner: &Arc) -> Option { + let guard = inner.wgc_session.lock(); + let session = guard.as_ref()?; + Some(WgcLoopContext { + device: session.device.clone(), + context: session.context.clone(), + d3d_device: session.d3d_device.clone(), + item: session.item.clone(), + target: session.target, + requested_width: session.requested_width, + requested_height: session.requested_height, + }) +} + +fn teardown_wgc_state(wgc_state: &mut Option) { + if let Some(state) = wgc_state.take() { + state.close(); + } +} + +fn sleep_with_backoff(backoff: &mut std::time::Duration) { + assert!( + *backoff >= std::time::Duration::from_millis(100), + "backoff at least base interval" + ); + assert!( + *backoff <= std::time::Duration::from_secs(2), + "backoff bounded" + ); + std::thread::sleep(*backoff); + *backoff = (*backoff * 2).min(std::time::Duration::from_secs(2)); +} + +pub fn capture_loop(inner: &Arc, frame_interval: std::time::Duration) { + ensure_winrt_initialized(); + let Some(ctx) = wgc_loop_context(inner) else { + emit_lifecycle(inner, "closed-clean", "no WGC session"); + return; + }; + + let capture_id = inner.capture_id.lock().clone(); + let mut wgc_state: Option = None; + let mut recreate_backoff = std::time::Duration::from_millis(100); + let capture_start = std::time::Instant::now(); + let mut next_frame_deadline = capture_start + frame_interval; + let mut frames_dropped_coalesced: u64 = 0; + + while inner.running.load(Ordering::Acquire) { + if !ctx.target.is_alive() { + emit_lifecycle(inner, "closed", ctx.target.closed_message()); + break; + } + + if wgc_state.is_none() { + match setup_wgc_state(&ctx) { + Ok(state) => { + wgc_state = Some(state); + } + Err(e) => { + if !inner.running.load(Ordering::Acquire) { + break; + } + emit_lifecycle( + inner, + "error", + &format!("Failed to create WGC capture session: {e}"), + ); + sleep_with_backoff(&mut recreate_backoff); + continue; + } + } + } + + let Some(state) = wgc_state.as_mut() else { + continue; + }; + let result = poll_and_emit_frame( + inner, + &ctx.context, + state, + capture_id.as_deref(), + capture_start, + &mut frames_dropped_coalesced, + ); + match handle_frame_result(inner, &ctx, &mut wgc_state, result, &mut recreate_backoff) { + LoopStep::Paced => {} + LoopStep::Restart => continue, + } + + let now = std::time::Instant::now(); + let (sleep_duration, deadline) = + pacing_sleep_and_next_deadline(now, next_frame_deadline, frame_interval); + next_frame_deadline = deadline; + if sleep_duration > std::time::Duration::ZERO { + std::thread::sleep(sleep_duration); + } + } + + teardown_wgc_state(&mut wgc_state); + inner.running.store(false, Ordering::Release); + emit_lifecycle(inner, "closed-clean", "capture stopped"); +} + +fn handle_frame_result( + inner: &Arc, + ctx: &WgcLoopContext, + wgc_state: &mut Option, + result: WgcFrameResult, + recreate_backoff: &mut std::time::Duration, +) -> LoopStep { + match result { + WgcFrameResult::Ok => { + *recreate_backoff = std::time::Duration::from_millis(100); + LoopStep::Paced + } + WgcFrameResult::NoFrame => LoopStep::Paced, + WgcFrameResult::Resized { width, height } => { + let Some(state) = wgc_state.as_mut() else { + return LoopStep::Restart; + }; + match resize_wgc_state(ctx, state, width, height) { + Ok(()) => LoopStep::Paced, + Err(e) => { + emit_lifecycle( + inner, + "error", + &format!("WGC frame pool resize failed: {e}"), + ); + teardown_wgc_state(wgc_state); + sleep_with_backoff(recreate_backoff); + LoopStep::Restart + } + } + } + WgcFrameResult::Error(e) => { + emit_lifecycle(inner, "error", &e); + { + let mut guard = inner.fallback.lock(); + if let Some(tracker) = guard.as_mut() { + let _ = tracker.observe(crate::fallback::FailureSignature::DeviceLost); + } + } + teardown_wgc_state(wgc_state); + sleep_with_backoff(recreate_backoff); + LoopStep::Restart + } + } +} + +fn setup_wgc_state(ctx: &WgcLoopContext) -> Result { + let size = ctx + .item + .Size() + .map_err(|e| format!("GraphicsCaptureItem.Size: {e}"))?; + let content_width = size.Width.max(1) as u32; + let content_height = size.Height.max(1) as u32; + let (out_w, out_h) = resolve_output_size( + content_width, + content_height, + ctx.requested_width, + ctx.requested_height, + ); + create_wgc_state_for_format( + ctx, + DirectXPixelFormat::R16G16B16A16Float, + content_width, + content_height, + out_w, + out_h, + ) + .or_else(|_| { + create_wgc_state_for_format( + ctx, + DirectXPixelFormat::B8G8R8A8UIntNormalized, + content_width, + content_height, + out_w, + out_h, + ) + }) +} + +fn create_wgc_state_for_format( + ctx: &WgcLoopContext, + pixel_format: DirectXPixelFormat, + content_width: u32, + content_height: u32, + out_w: u32, + out_h: u32, +) -> Result { + let output_pipeline = create_wgc_output_pipeline( + ctx, + pixel_format, + content_width, + content_height, + out_w, + out_h, + )?; + let frame_pool = Direct3D11CaptureFramePool::CreateFreeThreaded( + &ctx.d3d_device, + pixel_format, + WGC_FRAME_POOL_BUFFERS, + SizeInt32 { + Width: content_width as i32, + Height: content_height as i32, + }, + ) + .map_err(|e| format!("Direct3D11CaptureFramePool::CreateFreeThreaded: {e}"))?; + let session = frame_pool + .CreateCaptureSession(&ctx.item) + .map_err(|e| format!("Direct3D11CaptureFramePool.CreateCaptureSession: {e}"))?; + session + .StartCapture() + .map_err(|e| format!("GraphicsCaptureSession.StartCapture: {e}"))?; + Ok(WgcState { + frame_pool, + session, + output_pipeline, + pixel_format, + content_width, + content_height, + out_w, + out_h, + }) +} + +fn resize_wgc_state( + ctx: &WgcLoopContext, + state: &mut WgcState, + width: u32, + height: u32, +) -> Result<(), String> { + assert!(width > 0, "WGC resize width positive"); + assert!(height > 0, "WGC resize height positive"); + let (out_w, out_h) = + resolve_output_size(width, height, ctx.requested_width, ctx.requested_height); + match resize_wgc_state_for_format(ctx, state, state.pixel_format, width, height, out_w, out_h) { + Ok(()) => Ok(()), + Err(first_error) if state.pixel_format == DirectXPixelFormat::R16G16B16A16Float => { + resize_wgc_state_for_format( + ctx, + state, + DirectXPixelFormat::B8G8R8A8UIntNormalized, + width, + height, + out_w, + out_h, + ) + .map_err(|fallback_error| { + format!( + "HDR resize failed ({first_error}); BGRA fallback failed ({fallback_error})" + ) + }) + } + Err(first_error) => Err(first_error), + } +} + +fn resize_wgc_state_for_format( + ctx: &WgcLoopContext, + state: &mut WgcState, + pixel_format: DirectXPixelFormat, + width: u32, + height: u32, + out_w: u32, + out_h: u32, +) -> Result<(), String> { + let output_pipeline = + create_wgc_output_pipeline(ctx, pixel_format, width, height, out_w, out_h)?; + state + .frame_pool + .Recreate( + &ctx.d3d_device, + pixel_format, + WGC_FRAME_POOL_BUFFERS, + SizeInt32 { + Width: width as i32, + Height: height as i32, + }, + ) + .map_err(|e| format!("Direct3D11CaptureFramePool.Recreate: {e}"))?; + state.content_width = width; + state.content_height = height; + state.out_w = out_w; + state.out_h = out_h; + state.pixel_format = pixel_format; + state.output_pipeline = output_pipeline; + Ok(()) +} + +fn create_wgc_output_pipeline( + ctx: &WgcLoopContext, + pixel_format: DirectXPixelFormat, + content_width: u32, + content_height: u32, + out_w: u32, + out_h: u32, +) -> Result, String> { + if pixel_format == DirectXPixelFormat::R16G16B16A16Float { + return create_wgc_nv12_pipeline( + ctx, + DXGI_FORMAT_R16G16B16A16_FLOAT, + content_width, + content_height, + out_w, + out_h, + crate::hdr::SourceFormat::Rgba16Float { hdr: true }, + ) + .map(WgcOutputPipeline::Nv12) + .map(Some); + } + if out_w != content_width || out_h != content_height { + return create_wgc_nv12_pipeline( + ctx, + DXGI_FORMAT_B8G8R8A8_UNORM, + content_width, + content_height, + out_w, + out_h, + crate::hdr::SourceFormat::Bgra8, + ) + .map(WgcOutputPipeline::Nv12) + .map(Some); + } + Ok( + create_shared_output_texture(&ctx.device, content_width, content_height) + .ok() + .map(WgcOutputPipeline::Bgra), + ) +} + +fn create_wgc_nv12_pipeline( + ctx: &WgcLoopContext, + input_format: DXGI_FORMAT, + content_width: u32, + content_height: u32, + out_w: u32, + out_h: u32, + source_format: crate::hdr::SourceFormat, +) -> Result { + assert!(content_width > 0, "WGC NV12 input width positive"); + assert!(content_height > 0, "WGC NV12 input height positive"); + let input_desc = D3D11_TEXTURE2D_DESC { + Width: content_width, + Height: content_height, + MipLevels: 1, + ArraySize: 1, + Format: input_format, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Usage: D3D11_USAGE_DEFAULT, + BindFlags: D3D11_BIND_SHADER_RESOURCE.0 as u32, + CPUAccessFlags: 0, + MiscFlags: 0, + }; + let mut input_texture = None; + unsafe { + ctx.device + .CreateTexture2D(&input_desc, None, Some(&mut input_texture)) + } + .map_err(|e| format!("CreateTexture2D WGC NV12 input: {e}"))?; + let input_texture = + input_texture.ok_or_else(|| "CreateTexture2D WGC NV12 input returned null".to_string())?; + let input_resource: ID3D11Resource = input_texture + .cast() + .map_err(|e| format!("ID3D11Resource WGC NV12 input cast: {e}"))?; + let converter = Nv12GpuConverter::new( + &ctx.device, + &ctx.context, + &input_texture, + content_width, + content_height, + out_w, + out_h, + source_format, + ) + .ok_or_else(|| "WGC NV12 converter unavailable".to_string())?; + Ok(WgcNv12Pipeline { + input_resource, + converter, + }) +} + +fn poll_and_emit_frame( + inner: &Arc, + context: &ID3D11DeviceContext, + state: &mut WgcState, + capture_id: Option<&str>, + capture_start: std::time::Instant, + frames_dropped_coalesced: &mut u64, +) -> WgcFrameResult { + let mut newest: Option = None; + let mut drained: u32 = 0; + while drained < WGC_FRAME_DRAIN_LIMIT { + let Ok(frame) = state.frame_pool.TryGetNextFrame() else { + break; + }; + if let Some(previous) = newest.replace(frame) { + *frames_dropped_coalesced += 1; + let _ = previous.Close(); + } + drained += 1; + } + assert!(drained <= WGC_FRAME_DRAIN_LIMIT, "frame drain bounded"); + let Some(frame) = newest else { + return WgcFrameResult::NoFrame; + }; + let result = emit_wgc_frame(inner, context, state, capture_id, &frame, capture_start); + let _ = frame.Close(); + result +} + +fn wgc_frame_source_resource(frame: &Direct3D11CaptureFrame) -> Result { + let surface = frame + .Surface() + .map_err(|e| format!("Direct3D11CaptureFrame.Surface: {e}"))?; + let access: IDirect3DDxgiInterfaceAccess = surface + .cast() + .map_err(|e| format!("IDirect3DDxgiInterfaceAccess cast: {e}"))?; + let source_texture: ID3D11Texture2D = + unsafe { access.GetInterface() }.map_err(|e| format!("WGC surface GetInterface: {e}"))?; + source_texture + .cast() + .map_err(|e| format!("ID3D11Resource WGC surface cast: {e}")) +} + +fn emit_wgc_frame( + inner: &Arc, + context: &ID3D11DeviceContext, + state: &mut WgcState, + capture_id: Option<&str>, + frame: &Direct3D11CaptureFrame, + capture_start: std::time::Instant, +) -> WgcFrameResult { + let content = match frame.ContentSize() { + Ok(size) => size, + Err(e) => { + return WgcFrameResult::Error(format!("Direct3D11CaptureFrame.ContentSize: {e}")); + } + }; + let content_width = content.Width.max(1) as u32; + let content_height = content.Height.max(1) as u32; + if content_width != state.content_width || content_height != state.content_height { + return WgcFrameResult::Resized { + width: content_width, + height: content_height, + }; + } + let Some(frame_sink) = resolve_frame_sink(inner, capture_id) else { + note_media_frame_without_sink( + inner, + "WGC frame dropped because no native frame sink is registered", + ); + return WgcFrameResult::Ok; + }; + let source_resource = match wgc_frame_source_resource(frame) { + Ok(resource) => resource, + Err(e) => return WgcFrameResult::Error(e), + }; + let timestamp_us = capture_timestamp_us(capture_start); + let Some(output_pipeline) = state.output_pipeline.as_mut() else { + return WgcFrameResult::Error("WGC shared texture output unavailable".into()); + }; + match output_pipeline { + WgcOutputPipeline::Bgra(shared_output) => emit_wgc_bgra_frame( + inner, + context, + &frame_sink, + shared_output, + &source_resource, + content_width, + content_height, + timestamp_us, + ), + WgcOutputPipeline::Nv12(pipeline) => emit_wgc_nv12_frame( + inner, + context, + &frame_sink, + pipeline, + &source_resource, + content_width, + content_height, + timestamp_us, + ), + } +} + +#[allow(clippy::too_many_arguments)] +fn emit_wgc_bgra_frame( + inner: &Arc, + context: &ID3D11DeviceContext, + frame_sink: &crate::FrameSinkRef, + shared_output: &mut SharedTextureOutput, + source_resource: &ID3D11Resource, + content_width: u32, + content_height: u32, + timestamp_us: i64, +) -> WgcFrameResult { + if shared_output.width != content_width || shared_output.height != content_height { + return WgcFrameResult::Error( + "WGC BGRA shared output size does not match content size".into(), + ); + } + let slot_index = shared_output.next_slot_index(); + let slot = &shared_output.slots[slot_index]; + let output_resource: ID3D11Resource = match slot.texture.cast() { + Ok(resource) => resource, + Err(e) => return WgcFrameResult::Error(format!("ID3D11Resource shared output cast: {e}")), + }; + let src_box = D3D11_BOX { + left: 0, + top: 0, + front: 0, + right: content_width, + bottom: content_height, + back: 1, + }; + unsafe { + context.CopySubresourceRegion( + &output_resource, + 0, + 0, + 0, + 0, + source_resource, + 0, + Some(&src_box), + ); + context.Flush(); + } + let _ = emit_shared_texture_frame( + inner, + frame_sink, + slot.handle, + shared_output.width, + shared_output.height, + shared_output.dxgi_format, + timestamp_us, + ); + WgcFrameResult::Ok +} + +#[allow(clippy::too_many_arguments)] +fn emit_wgc_nv12_frame( + inner: &Arc, + context: &ID3D11DeviceContext, + frame_sink: &crate::FrameSinkRef, + pipeline: &mut WgcNv12Pipeline, + source_resource: &ID3D11Resource, + content_width: u32, + content_height: u32, + timestamp_us: i64, +) -> WgcFrameResult { + let src_box = D3D11_BOX { + left: 0, + top: 0, + front: 0, + right: content_width, + bottom: content_height, + back: 1, + }; + unsafe { + context.CopySubresourceRegion( + &pipeline.input_resource, + 0, + 0, + 0, + 0, + source_resource, + 0, + Some(&src_box), + ); + } + let frame = match pipeline.converter.convert_shared_texture() { + Ok(frame) => frame, + Err(error) => return WgcFrameResult::Error(error), + }; + let _ = emit_shared_texture_frame( + inner, + frame_sink, + frame.handle, + frame.width, + frame.height, + frame.dxgi_format, + timestamp_us, + ); + WgcFrameResult::Ok +} diff --git a/fluxer_desktop/native/win-game-capture/test-apps/d3d10-present-fixture/Cargo.lock b/fluxer_desktop/native/win-game-capture/test-apps/d3d10-present-fixture/Cargo.lock new file mode 100644 index 0000000..9a46411 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/d3d10-present-fixture/Cargo.lock @@ -0,0 +1,156 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "d3d10-present-fixture" +version = "0.0.0" +dependencies = [ + "windows", + "windows-core", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] diff --git a/fluxer_desktop/native/win-game-capture/test-apps/d3d10-present-fixture/Cargo.toml b/fluxer_desktop/native/win-game-capture/test-apps/d3d10-present-fixture/Cargo.toml new file mode 100644 index 0000000..69d699f --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/d3d10-present-fixture/Cargo.toml @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +[package] +name = "d3d10-present-fixture" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +# Empty workspace table keeps this fixture out of any parent Cargo workspace, +# matching the sibling crates (hook/, vulkan-layer/) in this package. +[workspace] +resolver = "2" + +[[bin]] +name = "d3d10-present-fixture" +path = "src/main.rs" + +[target.'cfg(target_os = "windows")'.dependencies] +windows = {version = "0.62.2", features = [ + "Win32_Foundation", + "Win32_Graphics_Direct3D", + "Win32_Graphics_Direct3D10", + "Win32_Graphics_Dxgi", + "Win32_Graphics_Dxgi_Common", + "Win32_Graphics_Gdi", + "Win32_System_LibraryLoader", + "Win32_UI_WindowsAndMessaging", +]} +windows-core = "0.62.2" diff --git a/fluxer_desktop/native/win-game-capture/test-apps/d3d10-present-fixture/src/main.rs b/fluxer_desktop/native/win-game-capture/test-apps/d3d10-present-fixture/src/main.rs new file mode 100644 index 0000000..b4d7ec9 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/d3d10-present-fixture/src/main.rs @@ -0,0 +1,389 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[cfg(not(windows))] +fn main() { + eprintln!("d3d10-present-fixture is only functional on Windows"); +} + +#[cfg(windows)] +fn main() -> windows_core::Result<()> { + windows_impl::run() +} + +#[cfg_attr(not(windows), allow(dead_code))] +mod pattern { + pub const BARS: [(u8, u8, u8); 8] = [ + (255, 255, 255), + (255, 255, 0), + (0, 255, 255), + (0, 255, 0), + (255, 0, 255), + (255, 0, 0), + (0, 0, 255), + (0, 0, 0), + ]; + + pub const COUNTER_BLOCK: u32 = 16; + + pub fn bar_colour(x: u32, width: u32) -> (u8, u8, u8) { + let width = width.max(1); + let index = ((x as u64 * BARS.len() as u64) / width as u64) as usize; + BARS[index.min(BARS.len() - 1)] + } + + pub fn counter_colour(frame_index: u64) -> (u8, u8, u8) { + let low = (frame_index & 0x00FF_FFFF) as u32; + ( + (low & 0xFF) as u8, + ((low >> 8) & 0xFF) as u8, + ((low >> 16) & 0xFF) as u8, + ) + } +} + +#[cfg(windows)] +mod cli { + pub struct Options { + pub frames: Option, + pub width: u32, + pub height: u32, + pub borderless: bool, + } + + impl Default for Options { + fn default() -> Self { + Self { + frames: None, + width: 1280, + height: 720, + borderless: false, + } + } + } + + impl Options { + pub fn from_env() -> Self { + let mut options = Options::default(); + let mut args = std::env::args().skip(1); + while let Some(arg) = args.next() { + match arg.as_str() { + "--frames" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.frames = Some(value); + } + } + "--width" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.width = value; + } + } + "--height" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.height = value; + } + } + "--borderless" => options.borderless = true, + "--windowed" => options.borderless = false, + _ => {} + } + } + options + } + } +} + +#[cfg(windows)] +mod windows_impl { + use super::{cli::Options, pattern}; + use windows::Win32::Foundation::{HMODULE, HWND, LPARAM, LRESULT, WPARAM}; + use windows::Win32::Graphics::Direct3D10::{ + D3D10_DRIVER_TYPE_HARDWARE, D3D10_MAPPED_TEXTURE2D, D3D10_SDK_VERSION, D3D10CreateDevice, + ID3D10Device, ID3D10RenderTargetView, ID3D10Resource, ID3D10Texture2D, + }; + use windows::Win32::Graphics::Dxgi::Common::{ + DXGI_FORMAT, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_MODE_DESC, + DXGI_RATIONAL, DXGI_SAMPLE_DESC, + }; + use windows::Win32::Graphics::Dxgi::{ + DXGI_PRESENT, DXGI_SWAP_CHAIN_DESC, DXGI_SWAP_EFFECT_DISCARD, + DXGI_USAGE_RENDER_TARGET_OUTPUT, IDXGIDevice, IDXGIFactory, IDXGISwapChain, + }; + use windows::Win32::System::LibraryLoader::GetModuleHandleW; + use windows::Win32::UI::WindowsAndMessaging::{ + CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, CreateWindowExW, DefWindowProcW, DispatchMessageW, + GetClientRect, MSG, PM_REMOVE, PeekMessageW, PostQuitMessage, RegisterClassExW, SW_SHOW, + ShowWindow, TranslateMessage, WM_DESTROY, WM_QUIT, WNDCLASSEXW, WS_OVERLAPPEDWINDOW, + WS_POPUP, WS_VISIBLE, + }; + use windows_core::{BOOL, Interface, Result, w}; + + extern "system" fn wnd_proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT { + unsafe { + match msg { + WM_DESTROY => { + PostQuitMessage(0); + LRESULT(0) + } + _ => DefWindowProcW(hwnd, msg, wparam, lparam), + } + } + } + + unsafe fn create_window(width: u32, height: u32, borderless: bool) -> Result { + unsafe { + let instance = GetModuleHandleW(None)?; + let class_name = w!("FluxerD3D10Fixture"); + let wc = WNDCLASSEXW { + cbSize: size_of::() as u32, + style: CS_HREDRAW | CS_VREDRAW, + lpfnWndProc: Some(wnd_proc), + hInstance: instance.into(), + lpszClassName: class_name, + ..Default::default() + }; + RegisterClassExW(&wc); + let style = if borderless { + WS_POPUP | WS_VISIBLE + } else { + WS_OVERLAPPEDWINDOW | WS_VISIBLE + }; + let hwnd = CreateWindowExW( + Default::default(), + class_name, + w!("Fluxer D3D10 Present Fixture"), + style, + CW_USEDEFAULT, + CW_USEDEFAULT, + width as i32, + height as i32, + None, + None, + Some(instance.into()), + None, + )?; + let _ = ShowWindow(hwnd, SW_SHOW); + Ok(hwnd) + } + } + + struct Renderer { + device: ID3D10Device, + swap_chain: IDXGISwapChain, + rtv: ID3D10RenderTargetView, + width: u32, + height: u32, + staging: ID3D10Texture2D, + format: DXGI_FORMAT, + } + + impl Renderer { + unsafe fn new(hwnd: HWND, width: u32, height: u32) -> Result { + unsafe { + let mut device: Option = None; + D3D10CreateDevice( + None, + D3D10_DRIVER_TYPE_HARDWARE, + HMODULE::default(), + 0, + D3D10_SDK_VERSION, + Some(&mut device), + )?; + let device = device.expect("D3D10CreateDeviceAndSwapChain yielded no device"); + + let dxgi_device: IDXGIDevice = device.cast()?; + let adapter = dxgi_device.GetAdapter()?; + let factory: IDXGIFactory = adapter.GetParent()?; + let (swap_chain, format) = + Self::make_swap_chain(&factory, &device, hwnd, width, height)?; + + let rtv = Self::make_rtv(&device, &swap_chain)?; + let staging = Self::make_staging(&device, width, height, format)?; + + Ok(Self { + device, + swap_chain, + rtv, + width, + height, + staging, + format, + }) + } + } + + unsafe fn make_swap_chain( + factory: &IDXGIFactory, + device: &ID3D10Device, + hwnd: HWND, + width: u32, + height: u32, + ) -> Result<(IDXGISwapChain, DXGI_FORMAT)> { + unsafe { + let mut last = windows_core::HRESULT(0); + for format in [DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM] { + let desc = DXGI_SWAP_CHAIN_DESC { + BufferDesc: DXGI_MODE_DESC { + Width: width, + Height: height, + RefreshRate: DXGI_RATIONAL { + Numerator: 0, + Denominator: 0, + }, + Format: format, + ..Default::default() + }, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT, + BufferCount: 1, + OutputWindow: hwnd, + Windowed: BOOL(1), + SwapEffect: DXGI_SWAP_EFFECT_DISCARD, + Flags: 0, + }; + let mut swap_chain: Option = None; + let hr = factory.CreateSwapChain(device, &desc, &mut swap_chain); + if hr.is_ok() { + return Ok(( + swap_chain.expect("CreateSwapChain yielded no swapchain"), + format, + )); + } + last = hr; + } + last.ok()?; + unreachable!() + } + } + + unsafe fn make_rtv( + device: &ID3D10Device, + swap_chain: &IDXGISwapChain, + ) -> Result { + unsafe { + let back_buffer: ID3D10Texture2D = swap_chain.GetBuffer(0)?; + let resource: ID3D10Resource = back_buffer.cast()?; + let mut rtv: Option = None; + device.CreateRenderTargetView(&resource, None, Some(&mut rtv))?; + Ok(rtv.expect("CreateRenderTargetView yielded no view")) + } + } + + unsafe fn make_staging( + device: &ID3D10Device, + width: u32, + height: u32, + format: DXGI_FORMAT, + ) -> Result { + use windows::Win32::Graphics::Direct3D10::{ + D3D10_CPU_ACCESS_WRITE, D3D10_TEXTURE2D_DESC, D3D10_USAGE_STAGING, + }; + unsafe { + let desc = D3D10_TEXTURE2D_DESC { + Width: width, + Height: height, + MipLevels: 1, + ArraySize: 1, + Format: format, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Usage: D3D10_USAGE_STAGING, + BindFlags: 0, + CPUAccessFlags: D3D10_CPU_ACCESS_WRITE.0 as u32, + MiscFlags: 0, + }; + device.CreateTexture2D(&desc, None) + } + } + + unsafe fn render_frame(&self, frame_index: u64) -> Result<()> { + use windows::Win32::Graphics::Direct3D10::D3D10_MAP_WRITE; + unsafe { + let (cr, cg, cb) = + pattern::BARS[(frame_index % pattern::BARS.len() as u64) as usize]; + let clear = [cr as f32 / 255.0, cg as f32 / 255.0, cb as f32 / 255.0, 1.0]; + self.device.ClearRenderTargetView(&self.rtv, &clear); + + let mapped: D3D10_MAPPED_TEXTURE2D = self.staging.Map(0, D3D10_MAP_WRITE, 0)?; + let row_pitch = mapped.RowPitch as usize; + let base = mapped.pData as *mut u8; + let counter = pattern::counter_colour(frame_index); + let luma = (frame_index & 0xFF) as u8; + for y in 0..self.height { + let row = base.add(y as usize * row_pitch); + for x in 0..self.width { + let (r, g, b) = if x < pattern::COUNTER_BLOCK && y < pattern::COUNTER_BLOCK + { + counter + } else { + pattern::bar_colour(x, self.width) + }; + let px = row.add(x as usize * 4); + if self.format == DXGI_FORMAT_R8G8B8A8_UNORM { + *px = r; + *px.add(1) = g; + *px.add(2) = b.saturating_add(luma / 4); + } else { + *px = b.saturating_add(luma / 4); + *px.add(1) = g; + *px.add(2) = r; + } + *px.add(3) = 255; + } + } + self.staging.Unmap(0); + + let back_buffer: ID3D10Texture2D = self.swap_chain.GetBuffer(0)?; + let dst: ID3D10Resource = back_buffer.cast()?; + let src: ID3D10Resource = self.staging.cast()?; + self.device.CopyResource(&dst, &src); + self.swap_chain.Present(1, DXGI_PRESENT(0)).ok()?; + let _ = &self.device; + Ok(()) + } + } + } + + pub fn run() -> Result<()> { + let options = Options::from_env(); + unsafe { + let hwnd = create_window(options.width, options.height, options.borderless)?; + + let mut rect = Default::default(); + let _ = GetClientRect(hwnd, &mut rect); + let width = ((rect.right - rect.left).max(1)) as u32; + let height = ((rect.bottom - rect.top).max(1)) as u32; + + let renderer = Renderer::new(hwnd, width, height)?; + + println!("HWND={}", hwnd.0 as isize); + use std::io::Write; + let _ = std::io::stdout().flush(); + + let mut frame_index: u64 = 0; + let mut msg = MSG::default(); + loop { + while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() { + if msg.message == WM_QUIT { + return Ok(()); + } + let _ = TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + renderer.render_frame(frame_index)?; + frame_index += 1; + + if let Some(limit) = options.frames { + if frame_index >= limit { + return Ok(()); + } + } + } + } + } +} diff --git a/fluxer_desktop/native/win-game-capture/test-apps/d3d11-present-fixture/Cargo.lock b/fluxer_desktop/native/win-game-capture/test-apps/d3d11-present-fixture/Cargo.lock new file mode 100644 index 0000000..5c3a988 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/d3d11-present-fixture/Cargo.lock @@ -0,0 +1,156 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "d3d11-present-fixture" +version = "0.0.0" +dependencies = [ + "windows", + "windows-core", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] diff --git a/fluxer_desktop/native/win-game-capture/test-apps/d3d11-present-fixture/Cargo.toml b/fluxer_desktop/native/win-game-capture/test-apps/d3d11-present-fixture/Cargo.toml new file mode 100644 index 0000000..36b168d --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/d3d11-present-fixture/Cargo.toml @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +[package] +name = "d3d11-present-fixture" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +# Empty workspace table keeps this fixture out of any parent Cargo workspace, +# matching the sibling crates (hook/, vulkan-layer/) in this package. +[workspace] +resolver = "2" + +[[bin]] +name = "d3d11-present-fixture" +path = "src/main.rs" + +[target.'cfg(target_os = "windows")'.dependencies] +windows = {version = "0.62.2", features = [ + "Win32_Foundation", + "Win32_Graphics_Direct3D", + "Win32_Graphics_Direct3D11", + "Win32_Graphics_Dxgi", + "Win32_Graphics_Dxgi_Common", + "Win32_Graphics_Gdi", + "Win32_System_LibraryLoader", + "Win32_UI_WindowsAndMessaging", +]} +windows-core = "0.62.2" diff --git a/fluxer_desktop/native/win-game-capture/test-apps/d3d11-present-fixture/src/main.rs b/fluxer_desktop/native/win-game-capture/test-apps/d3d11-present-fixture/src/main.rs new file mode 100644 index 0000000..7b41da2 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/d3d11-present-fixture/src/main.rs @@ -0,0 +1,496 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[cfg(not(windows))] +fn main() { + eprintln!("d3d11-present-fixture is only functional on Windows"); +} + +#[cfg(windows)] +fn main() -> windows_core::Result<()> { + windows_impl::run() +} + +#[cfg_attr(not(windows), allow(dead_code))] +mod pattern { + pub const BARS: [(u8, u8, u8); 8] = [ + (255, 255, 255), + (255, 255, 0), + (0, 255, 255), + (0, 255, 0), + (255, 0, 255), + (255, 0, 0), + (0, 0, 255), + (0, 0, 0), + ]; + + pub const COUNTER_BLOCK: u32 = 16; + + pub fn bar_colour(x: u32, width: u32) -> (u8, u8, u8) { + let width = width.max(1); + let index = ((x as u64 * BARS.len() as u64) / width as u64) as usize; + BARS[index.min(BARS.len() - 1)] + } + + pub fn counter_colour(frame_index: u64) -> (u8, u8, u8) { + let low = (frame_index & 0x00FF_FFFF) as u32; + ( + (low & 0xFF) as u8, + ((low >> 8) & 0xFF) as u8, + ((low >> 16) & 0xFF) as u8, + ) + } +} + +#[cfg_attr(not(windows), allow(dead_code))] +mod cli { + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum PresentFormat { + Bgra8, + Rgba8, + R10G10B10A2, + Rgba16Float, + } + + impl PresentFormat { + fn parse(value: &str) -> Option { + match value { + "bgra8" | "b8g8r8a8" => Some(Self::Bgra8), + "rgba8" | "r8g8b8a8" => Some(Self::Rgba8), + "r10g10b10a2" | "rgb10a2" | "10bit" => Some(Self::R10G10B10A2), + "rgba16f" | "r16g16b16a16f" | "fp16" => Some(Self::Rgba16Float), + _ => None, + } + } + } + + pub struct Options { + pub frames: Option, + pub width: u32, + pub height: u32, + pub borderless: bool, + pub format: PresentFormat, + } + + impl Default for Options { + fn default() -> Self { + Self { + frames: None, + width: 1280, + height: 720, + borderless: false, + format: PresentFormat::Bgra8, + } + } + } + + impl Options { + pub fn from_env() -> Self { + let mut options = Options::default(); + let mut args = std::env::args().skip(1); + while let Some(arg) = args.next() { + match arg.as_str() { + "--frames" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.frames = Some(value); + } + } + "--width" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.width = value; + } + } + "--height" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.height = value; + } + } + "--borderless" => options.borderless = true, + "--windowed" => options.borderless = false, + "--format" => { + if let Some(format) = args.next().and_then(|v| PresentFormat::parse(&v)) { + options.format = format; + } + } + _ => {} + } + } + options + } + } + + #[cfg(test)] + mod tests { + use super::PresentFormat; + + #[test] + fn parses_explicit_format_modes() { + assert_eq!(PresentFormat::parse("bgra8"), Some(PresentFormat::Bgra8)); + assert_eq!(PresentFormat::parse("rgba8"), Some(PresentFormat::Rgba8)); + assert_eq!( + PresentFormat::parse("r10g10b10a2"), + Some(PresentFormat::R10G10B10A2) + ); + assert_eq!( + PresentFormat::parse("fp16"), + Some(PresentFormat::Rgba16Float) + ); + assert_eq!(PresentFormat::parse("unknown"), None); + } + } +} + +#[cfg(windows)] +mod windows_impl { + use super::{ + cli::{Options, PresentFormat}, + pattern, + }; + use windows::Win32::Foundation::{HMODULE, HWND, LPARAM, LRESULT, WPARAM}; + use windows::Win32::Graphics::Direct3D::D3D_DRIVER_TYPE_HARDWARE; + use windows::Win32::Graphics::Direct3D11::{ + D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_MAPPED_SUBRESOURCE, D3D11_SDK_VERSION, + D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext, ID3D11RenderTargetView, + ID3D11Resource, ID3D11Texture2D, + }; + use windows::Win32::Graphics::Dxgi::Common::{ + DXGI_ALPHA_MODE_IGNORE, DXGI_FORMAT, DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_SAMPLE_DESC, + }; + use windows::Win32::Graphics::Dxgi::{ + DXGI_SCALING_STRETCH, DXGI_SWAP_CHAIN_DESC1, DXGI_SWAP_EFFECT_FLIP_DISCARD, + DXGI_USAGE_RENDER_TARGET_OUTPUT, IDXGIDevice, IDXGIFactory2, IDXGISwapChain1, + }; + use windows::Win32::System::LibraryLoader::GetModuleHandleW; + use windows::Win32::UI::WindowsAndMessaging::{ + CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, CreateWindowExW, DefWindowProcW, DispatchMessageW, + GetClientRect, MSG, PM_REMOVE, PeekMessageW, PostQuitMessage, RegisterClassExW, SW_SHOW, + ShowWindow, TranslateMessage, WM_DESTROY, WM_QUIT, WNDCLASSEXW, WS_OVERLAPPEDWINDOW, + WS_POPUP, WS_VISIBLE, + }; + use windows_core::{Interface, Result, w}; + + impl PresentFormat { + fn dxgi(self) -> DXGI_FORMAT { + match self { + PresentFormat::Bgra8 => DXGI_FORMAT_B8G8R8A8_UNORM, + PresentFormat::Rgba8 => DXGI_FORMAT_R8G8B8A8_UNORM, + PresentFormat::R10G10B10A2 => DXGI_FORMAT_R10G10B10A2_UNORM, + PresentFormat::Rgba16Float => DXGI_FORMAT_R16G16B16A16_FLOAT, + } + } + + fn bytes_per_pixel(self) -> usize { + match self { + PresentFormat::Bgra8 | PresentFormat::Rgba8 | PresentFormat::R10G10B10A2 => 4, + PresentFormat::Rgba16Float => 8, + } + } + } + + extern "system" fn wnd_proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT { + unsafe { + match msg { + WM_DESTROY => { + PostQuitMessage(0); + LRESULT(0) + } + _ => DefWindowProcW(hwnd, msg, wparam, lparam), + } + } + } + + unsafe fn create_window(width: u32, height: u32, borderless: bool) -> Result { + unsafe { + let instance = GetModuleHandleW(None)?; + let class_name = w!("FluxerD3D11Fixture"); + let wc = WNDCLASSEXW { + cbSize: size_of::() as u32, + style: CS_HREDRAW | CS_VREDRAW, + lpfnWndProc: Some(wnd_proc), + hInstance: instance.into(), + lpszClassName: class_name, + ..Default::default() + }; + RegisterClassExW(&wc); + let style = if borderless { + WS_POPUP | WS_VISIBLE + } else { + WS_OVERLAPPEDWINDOW | WS_VISIBLE + }; + let hwnd = CreateWindowExW( + Default::default(), + class_name, + w!("Fluxer D3D11 Present Fixture"), + style, + CW_USEDEFAULT, + CW_USEDEFAULT, + width as i32, + height as i32, + None, + None, + Some(instance.into()), + None, + )?; + let _ = ShowWindow(hwnd, SW_SHOW); + Ok(hwnd) + } + } + + struct Renderer { + device: ID3D11Device, + context: ID3D11DeviceContext, + swap_chain: IDXGISwapChain1, + rtv: ID3D11RenderTargetView, + width: u32, + height: u32, + staging: ID3D11Texture2D, + format: PresentFormat, + } + + impl Renderer { + unsafe fn new(hwnd: HWND, width: u32, height: u32, format: PresentFormat) -> Result { + unsafe { + let mut device: Option = None; + let mut context: Option = None; + D3D11CreateDevice( + None, + D3D_DRIVER_TYPE_HARDWARE, + HMODULE::default(), + D3D11_CREATE_DEVICE_BGRA_SUPPORT, + None, + D3D11_SDK_VERSION, + Some(&mut device), + None, + Some(&mut context), + )?; + let device = device.expect("D3D11CreateDevice yielded no device"); + let context = context.expect("D3D11CreateDevice yielded no context"); + + let dxgi_device: IDXGIDevice = device.cast()?; + let adapter = dxgi_device.GetAdapter()?; + let factory: IDXGIFactory2 = adapter.GetParent()?; + + let desc = DXGI_SWAP_CHAIN_DESC1 { + Width: width, + Height: height, + Format: format.dxgi(), + Stereo: false.into(), + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT, + BufferCount: 2, + Scaling: DXGI_SCALING_STRETCH, + SwapEffect: DXGI_SWAP_EFFECT_FLIP_DISCARD, + AlphaMode: DXGI_ALPHA_MODE_IGNORE, + Flags: 0, + }; + let swap_chain = + factory.CreateSwapChainForHwnd(&device, hwnd, &desc, None, None)?; + + let rtv = Self::make_rtv(&device, &swap_chain)?; + let staging = Self::make_staging(&device, width, height, format)?; + + Ok(Self { + device, + context, + swap_chain, + rtv, + width, + height, + staging, + format, + }) + } + } + + unsafe fn make_rtv( + device: &ID3D11Device, + swap_chain: &IDXGISwapChain1, + ) -> Result { + unsafe { + let back_buffer: ID3D11Texture2D = swap_chain.GetBuffer(0)?; + let resource: ID3D11Resource = back_buffer.cast()?; + let mut rtv: Option = None; + device.CreateRenderTargetView(&resource, None, Some(&mut rtv))?; + Ok(rtv.expect("CreateRenderTargetView yielded no view")) + } + } + + unsafe fn make_staging( + device: &ID3D11Device, + width: u32, + height: u32, + format: PresentFormat, + ) -> Result { + use windows::Win32::Graphics::Direct3D11::{ + D3D11_CPU_ACCESS_WRITE, D3D11_TEXTURE2D_DESC, D3D11_USAGE_STAGING, + }; + unsafe { + let desc = D3D11_TEXTURE2D_DESC { + Width: width, + Height: height, + MipLevels: 1, + ArraySize: 1, + Format: format.dxgi(), + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Usage: D3D11_USAGE_STAGING, + BindFlags: 0, + CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32, + MiscFlags: 0, + }; + let mut staging: Option = None; + device.CreateTexture2D(&desc, None, Some(&mut staging))?; + Ok(staging.expect("CreateTexture2D yielded no staging texture")) + } + } + + unsafe fn render_frame(&self, frame_index: u64) -> Result<()> { + use windows::Win32::Graphics::Direct3D11::D3D11_MAP_WRITE; + unsafe { + let mut mapped = D3D11_MAPPED_SUBRESOURCE::default(); + self.context + .Map(&self.staging, 0, D3D11_MAP_WRITE, 0, Some(&mut mapped))?; + let row_pitch = mapped.RowPitch as usize; + let base = mapped.pData as *mut u8; + let counter = pattern::counter_colour(frame_index); + let luma = (frame_index & 0xFF) as u8; + let bpp = self.format.bytes_per_pixel(); + for y in 0..self.height { + let row = base.add(y as usize * row_pitch); + for x in 0..self.width { + let (r, g, b) = if x < pattern::COUNTER_BLOCK && y < pattern::COUNTER_BLOCK + { + counter + } else { + pattern::bar_colour(x, self.width) + }; + let px = row.add(x as usize * bpp); + write_pixel(self.format, px, r, g, b.saturating_add(luma / 4)); + } + } + self.context.Unmap(&self.staging, 0); + + let back_buffer: ID3D11Texture2D = self.swap_chain.GetBuffer(0)?; + let dst: ID3D11Resource = back_buffer.cast()?; + let src: ID3D11Resource = self.staging.cast()?; + self.context.CopyResource(&dst, &src); + let _ = &self.rtv; + self.swap_chain.Present(1, Default::default()).ok()?; + let _ = &self.device; + Ok(()) + } + } + } + + unsafe fn write_pixel(format: PresentFormat, px: *mut u8, r: u8, g: u8, b: u8) { + unsafe { + match format { + PresentFormat::Bgra8 => { + *px = b; + *px.add(1) = g; + *px.add(2) = r; + *px.add(3) = 255; + } + PresentFormat::Rgba8 => { + *px = r; + *px.add(1) = g; + *px.add(2) = b; + *px.add(3) = 255; + } + PresentFormat::R10G10B10A2 => { + let packed = scale8_to_10(r) + | (scale8_to_10(g) << 10) + | (scale8_to_10(b) << 20) + | (0x3 << 30); + std::ptr::copy_nonoverlapping(packed.to_le_bytes().as_ptr(), px, 4); + } + PresentFormat::Rgba16Float => { + let highlight = if r == 255 && g == 255 && b > 240 { + 1.25 + } else { + 1.0 + }; + let values = [ + f32_to_f16((r as f32 / 255.0) * highlight), + f32_to_f16((g as f32 / 255.0) * highlight), + f32_to_f16((b as f32 / 255.0) * highlight), + f32_to_f16(1.0), + ]; + for (index, value) in values.iter().enumerate() { + std::ptr::copy_nonoverlapping( + value.to_le_bytes().as_ptr(), + px.add(index * 2), + 2, + ); + } + } + } + } + } + + fn scale8_to_10(value: u8) -> u32 { + (value as u32 * 1023 + 127) / 255 + } + + fn f32_to_f16(value: f32) -> u16 { + let value = value.clamp(0.0, 65504.0); + if value == 0.0 { + return 0; + } + let bits = value.to_bits(); + let exp = ((bits >> 23) & 0xFF) as i32 - 127 + 15; + let mant = bits & 0x7F_FFFF; + if exp <= 0 { + return 0; + } + if exp >= 31 { + return 0x7BFF; + } + ((exp as u16) << 10) | (((mant + 0x1000) >> 13) as u16) + } + + pub fn run() -> Result<()> { + let options = Options::from_env(); + unsafe { + let hwnd = create_window(options.width, options.height, options.borderless)?; + + let mut rect = Default::default(); + let _ = GetClientRect(hwnd, &mut rect); + let width = ((rect.right - rect.left).max(1)) as u32; + let height = ((rect.bottom - rect.top).max(1)) as u32; + + let renderer = Renderer::new(hwnd, width, height, options.format)?; + + println!("HWND={}", hwnd.0 as isize); + use std::io::Write; + let _ = std::io::stdout().flush(); + + let mut frame_index: u64 = 0; + let mut msg = MSG::default(); + loop { + while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() { + if msg.message == WM_QUIT { + return Ok(()); + } + let _ = TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + renderer.render_frame(frame_index)?; + frame_index += 1; + + if let Some(limit) = options.frames { + if frame_index >= limit { + return Ok(()); + } + } + } + } + } +} diff --git a/fluxer_desktop/native/win-game-capture/test-apps/d3d12-present-fixture/Cargo.lock b/fluxer_desktop/native/win-game-capture/test-apps/d3d12-present-fixture/Cargo.lock new file mode 100644 index 0000000..92080e6 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/d3d12-present-fixture/Cargo.lock @@ -0,0 +1,156 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "d3d12-present-fixture" +version = "0.0.0" +dependencies = [ + "windows", + "windows-core", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] diff --git a/fluxer_desktop/native/win-game-capture/test-apps/d3d12-present-fixture/Cargo.toml b/fluxer_desktop/native/win-game-capture/test-apps/d3d12-present-fixture/Cargo.toml new file mode 100644 index 0000000..2a6cc62 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/d3d12-present-fixture/Cargo.toml @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +[package] +name = "d3d12-present-fixture" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +# Empty workspace table keeps this fixture out of any parent Cargo workspace, +# matching the sibling crates (hook/, vulkan-layer/) in this package. +[workspace] +resolver = "2" + +[[bin]] +name = "d3d12-present-fixture" +path = "src/main.rs" + +[target.'cfg(target_os = "windows")'.dependencies] +windows = {version = "0.62.2", features = [ + "Win32_Foundation", + "Win32_Graphics_Direct3D", + "Win32_Graphics_Direct3D12", + "Win32_Graphics_Dxgi", + "Win32_Graphics_Dxgi_Common", + "Win32_Graphics_Gdi", + "Win32_Security", + "Win32_System_LibraryLoader", + "Win32_System_Threading", + "Win32_UI_WindowsAndMessaging", +]} +windows-core = "0.62.2" diff --git a/fluxer_desktop/native/win-game-capture/test-apps/d3d12-present-fixture/src/main.rs b/fluxer_desktop/native/win-game-capture/test-apps/d3d12-present-fixture/src/main.rs new file mode 100644 index 0000000..4b7afa3 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/d3d12-present-fixture/src/main.rs @@ -0,0 +1,582 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[cfg(not(windows))] +fn main() { + eprintln!("d3d12-present-fixture is only functional on Windows"); +} + +#[cfg(windows)] +fn main() -> windows_core::Result<()> { + windows_impl::run() +} + +#[cfg_attr(not(windows), allow(dead_code))] +mod pattern { + pub const BARS: [(u8, u8, u8); 8] = [ + (255, 255, 255), + (255, 255, 0), + (0, 255, 255), + (0, 255, 0), + (255, 0, 255), + (255, 0, 0), + (0, 0, 255), + (0, 0, 0), + ]; + + pub const COUNTER_BLOCK: u32 = 16; + + pub fn bar_colour(x: u32, width: u32) -> (u8, u8, u8) { + let width = width.max(1); + let index = ((x as u64 * BARS.len() as u64) / width as u64) as usize; + BARS[index.min(BARS.len() - 1)] + } + + pub fn counter_colour(frame_index: u64) -> (u8, u8, u8) { + let low = (frame_index & 0x00FF_FFFF) as u32; + ( + (low & 0xFF) as u8, + ((low >> 8) & 0xFF) as u8, + ((low >> 16) & 0xFF) as u8, + ) + } +} + +#[cfg_attr(not(windows), allow(dead_code))] +mod cli { + #[derive(Clone, Copy, Debug, PartialEq, Eq)] + pub enum PresentFormat { + Bgra8, + Rgba8, + R10G10B10A2, + Rgba16Float, + } + + impl PresentFormat { + fn parse(value: &str) -> Option { + match value { + "bgra8" | "b8g8r8a8" => Some(Self::Bgra8), + "rgba8" | "r8g8b8a8" => Some(Self::Rgba8), + "r10g10b10a2" | "rgb10a2" | "10bit" => Some(Self::R10G10B10A2), + "rgba16f" | "r16g16b16a16f" | "fp16" => Some(Self::Rgba16Float), + _ => None, + } + } + } + + pub struct Options { + pub frames: Option, + pub width: u32, + pub height: u32, + pub borderless: bool, + pub backbuffers: u32, + pub format: PresentFormat, + } + + impl Default for Options { + fn default() -> Self { + Self { + frames: None, + width: 1280, + height: 720, + borderless: false, + backbuffers: 2, + format: PresentFormat::Bgra8, + } + } + } + + impl Options { + pub fn from_env() -> Self { + let mut options = Options::default(); + let mut args = std::env::args().skip(1); + while let Some(arg) = args.next() { + match arg.as_str() { + "--frames" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.frames = Some(value); + } + } + "--width" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.width = value; + } + } + "--height" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.height = value; + } + } + "--backbuffers" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.backbuffers = value; + } + } + "--borderless" => options.borderless = true, + "--windowed" => options.borderless = false, + "--format" => { + if let Some(format) = args.next().and_then(|v| PresentFormat::parse(&v)) { + options.format = format; + } + } + _ => {} + } + } + options.backbuffers = options.backbuffers.max(2); + options + } + } + + #[cfg(test)] + mod tests { + use super::PresentFormat; + + #[test] + fn parses_explicit_format_modes() { + assert_eq!(PresentFormat::parse("bgra8"), Some(PresentFormat::Bgra8)); + assert_eq!(PresentFormat::parse("rgba8"), Some(PresentFormat::Rgba8)); + assert_eq!( + PresentFormat::parse("r10g10b10a2"), + Some(PresentFormat::R10G10B10A2) + ); + assert_eq!( + PresentFormat::parse("fp16"), + Some(PresentFormat::Rgba16Float) + ); + assert_eq!(PresentFormat::parse("unknown"), None); + } + } +} + +#[cfg(windows)] +mod windows_impl { + use super::cli::{Options, PresentFormat}; + use super::pattern; + use windows::Win32::Foundation::RECT; + use windows::Win32::Foundation::{CloseHandle, HANDLE, HWND, LPARAM, LRESULT, WPARAM}; + use windows::Win32::Graphics::Direct3D::{D3D_DRIVER_TYPE_HARDWARE, D3D_FEATURE_LEVEL_11_0}; + use windows::Win32::Graphics::Direct3D12::{ + D3D12_COMMAND_LIST_TYPE_DIRECT, D3D12_COMMAND_QUEUE_DESC, D3D12_COMMAND_QUEUE_FLAG_NONE, + D3D12_CPU_DESCRIPTOR_HANDLE, D3D12_DESCRIPTOR_HEAP_DESC, D3D12_DESCRIPTOR_HEAP_FLAG_NONE, + D3D12_DESCRIPTOR_HEAP_TYPE_RTV, D3D12_FENCE_FLAG_NONE, D3D12_RESOURCE_BARRIER, + D3D12_RESOURCE_BARRIER_0, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + D3D12_RESOURCE_BARRIER_FLAG_NONE, D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, + D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_RENDER_TARGET, + D3D12_RESOURCE_TRANSITION_BARRIER, D3D12CreateDevice, ID3D12CommandAllocator, + ID3D12CommandQueue, ID3D12DescriptorHeap, ID3D12Device, ID3D12Fence, + ID3D12GraphicsCommandList, ID3D12PipelineState, ID3D12Resource, + }; + use windows::Win32::Graphics::Dxgi::Common::{ + DXGI_ALPHA_MODE_IGNORE, DXGI_FORMAT, DXGI_FORMAT_B8G8R8A8_UNORM, + DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R16G16B16A16_FLOAT, + DXGI_SAMPLE_DESC, + }; + use windows::Win32::Graphics::Dxgi::{ + CreateDXGIFactory2, DXGI_ADAPTER_FLAG, DXGI_ADAPTER_FLAG_SOFTWARE, + DXGI_CREATE_FACTORY_FLAGS, DXGI_SCALING_STRETCH, DXGI_SWAP_CHAIN_DESC1, + DXGI_SWAP_EFFECT_FLIP_DISCARD, DXGI_USAGE_RENDER_TARGET_OUTPUT, IDXGIAdapter1, + IDXGIFactory4, IDXGISwapChain3, + }; + use windows::Win32::System::LibraryLoader::GetModuleHandleW; + use windows::Win32::System::Threading::{CreateEventW, INFINITE, WaitForSingleObject}; + use windows::Win32::UI::WindowsAndMessaging::{ + CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, CreateWindowExW, DefWindowProcW, DispatchMessageW, + GetClientRect, MSG, PM_REMOVE, PeekMessageW, PostQuitMessage, RegisterClassExW, SW_SHOW, + ShowWindow, TranslateMessage, WM_DESTROY, WM_QUIT, WNDCLASSEXW, WS_OVERLAPPEDWINDOW, + WS_POPUP, WS_VISIBLE, + }; + use windows_core::{Interface, Result, w}; + + impl PresentFormat { + fn dxgi(self) -> DXGI_FORMAT { + match self { + PresentFormat::Bgra8 => DXGI_FORMAT_B8G8R8A8_UNORM, + PresentFormat::Rgba8 => DXGI_FORMAT_R8G8B8A8_UNORM, + PresentFormat::R10G10B10A2 => DXGI_FORMAT_R10G10B10A2_UNORM, + PresentFormat::Rgba16Float => DXGI_FORMAT_R16G16B16A16_FLOAT, + } + } + } + + extern "system" fn wnd_proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT { + unsafe { + match msg { + WM_DESTROY => { + PostQuitMessage(0); + LRESULT(0) + } + _ => DefWindowProcW(hwnd, msg, wparam, lparam), + } + } + } + + unsafe fn create_window(width: u32, height: u32, borderless: bool) -> Result { + unsafe { + let instance = GetModuleHandleW(None)?; + let class_name = w!("FluxerD3D12Fixture"); + let wc = WNDCLASSEXW { + cbSize: size_of::() as u32, + style: CS_HREDRAW | CS_VREDRAW, + lpfnWndProc: Some(wnd_proc), + hInstance: instance.into(), + lpszClassName: class_name, + ..Default::default() + }; + RegisterClassExW(&wc); + let style = if borderless { + WS_POPUP | WS_VISIBLE + } else { + WS_OVERLAPPEDWINDOW | WS_VISIBLE + }; + let hwnd = CreateWindowExW( + Default::default(), + class_name, + w!("Fluxer D3D12 Present Fixture"), + style, + CW_USEDEFAULT, + CW_USEDEFAULT, + width as i32, + height as i32, + None, + None, + Some(instance.into()), + None, + )?; + let _ = ShowWindow(hwnd, SW_SHOW); + Ok(hwnd) + } + } + + unsafe fn create_device(factory: &IDXGIFactory4) -> Result { + unsafe { + let mut index = 0u32; + while let Ok(adapter) = factory.EnumAdapters1(index) { + let adapter: IDXGIAdapter1 = adapter; + let desc = adapter.GetDesc1()?; + let is_software = + (DXGI_ADAPTER_FLAG(desc.Flags as i32).0 & DXGI_ADAPTER_FLAG_SOFTWARE.0) != 0; + if !is_software { + let mut device: Option = None; + if D3D12CreateDevice(&adapter, D3D_FEATURE_LEVEL_11_0, &mut device).is_ok() { + if let Some(device) = device { + return Ok(device); + } + } + } + index += 1; + } + let mut device: Option = None; + let _ = D3D_DRIVER_TYPE_HARDWARE; + D3D12CreateDevice(None, D3D_FEATURE_LEVEL_11_0, &mut device)?; + device.ok_or_else(windows_core::Error::from_thread) + } + } + + struct Renderer { + device: ID3D12Device, + queue: ID3D12CommandQueue, + swap_chain: IDXGISwapChain3, + rtv_heap: ID3D12DescriptorHeap, + rtv_descriptor_size: usize, + render_targets: Vec, + allocators: Vec, + list: ID3D12GraphicsCommandList, + fence: ID3D12Fence, + fence_event: HANDLE, + fence_value: u64, + frame_fence_values: Vec, + width: u32, + height: u32, + format: PresentFormat, + } + + impl Renderer { + unsafe fn new( + hwnd: HWND, + width: u32, + height: u32, + backbuffers: u32, + format: PresentFormat, + ) -> Result { + unsafe { + let factory: IDXGIFactory4 = CreateDXGIFactory2(DXGI_CREATE_FACTORY_FLAGS(0))?; + let device = create_device(&factory)?; + + let queue_desc = D3D12_COMMAND_QUEUE_DESC { + Type: D3D12_COMMAND_LIST_TYPE_DIRECT, + Priority: 0, + Flags: D3D12_COMMAND_QUEUE_FLAG_NONE, + NodeMask: 0, + }; + let queue: ID3D12CommandQueue = device.CreateCommandQueue(&queue_desc)?; + + let desc = DXGI_SWAP_CHAIN_DESC1 { + Width: width, + Height: height, + Format: format.dxgi(), + Stereo: false.into(), + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT, + BufferCount: backbuffers, + Scaling: DXGI_SCALING_STRETCH, + SwapEffect: DXGI_SWAP_EFFECT_FLIP_DISCARD, + AlphaMode: DXGI_ALPHA_MODE_IGNORE, + Flags: 0, + }; + let swap_chain1 = + factory.CreateSwapChainForHwnd(&queue, hwnd, &desc, None, None)?; + let swap_chain: IDXGISwapChain3 = swap_chain1.cast()?; + + let rtv_heap_desc = D3D12_DESCRIPTOR_HEAP_DESC { + Type: D3D12_DESCRIPTOR_HEAP_TYPE_RTV, + NumDescriptors: backbuffers, + Flags: D3D12_DESCRIPTOR_HEAP_FLAG_NONE, + NodeMask: 0, + }; + let rtv_heap: ID3D12DescriptorHeap = device.CreateDescriptorHeap(&rtv_heap_desc)?; + let rtv_descriptor_size = device + .GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_RTV) + as usize; + + let heap_start = rtv_heap.GetCPUDescriptorHandleForHeapStart(); + let mut render_targets = Vec::with_capacity(backbuffers as usize); + let mut allocators = Vec::with_capacity(backbuffers as usize); + for i in 0..backbuffers { + let back_buffer: ID3D12Resource = swap_chain.GetBuffer(i)?; + let handle = D3D12_CPU_DESCRIPTOR_HANDLE { + ptr: heap_start.ptr + i as usize * rtv_descriptor_size, + }; + device.CreateRenderTargetView(&back_buffer, None, handle); + render_targets.push(back_buffer); + + let allocator: ID3D12CommandAllocator = + device.CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT)?; + allocators.push(allocator); + } + + let list: ID3D12GraphicsCommandList = device.CreateCommandList( + 0, + D3D12_COMMAND_LIST_TYPE_DIRECT, + &allocators[0], + None::<&ID3D12PipelineState>, + )?; + list.Close()?; + + let fence: ID3D12Fence = device.CreateFence(0, D3D12_FENCE_FLAG_NONE)?; + let fence_event = CreateEventW(None, false, false, None)?; + + Ok(Self { + device, + queue, + swap_chain, + rtv_heap, + rtv_descriptor_size, + render_targets, + frame_fence_values: vec![0; backbuffers as usize], + allocators, + list, + fence, + fence_event, + fence_value: 0, + width, + height, + format, + }) + } + } + + unsafe fn transition( + resource: &ID3D12Resource, + before: windows::Win32::Graphics::Direct3D12::D3D12_RESOURCE_STATES, + after: windows::Win32::Graphics::Direct3D12::D3D12_RESOURCE_STATES, + ) -> D3D12_RESOURCE_BARRIER { + D3D12_RESOURCE_BARRIER { + Type: D3D12_RESOURCE_BARRIER_TYPE_TRANSITION, + Flags: D3D12_RESOURCE_BARRIER_FLAG_NONE, + Anonymous: D3D12_RESOURCE_BARRIER_0 { + Transition: std::mem::ManuallyDrop::new(D3D12_RESOURCE_TRANSITION_BARRIER { + pResource: std::mem::ManuallyDrop::new(Some(resource.clone())), + Subresource: D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, + StateBefore: before, + StateAfter: after, + }), + }, + } + } + + unsafe fn render_frame(&mut self, frame_index: u64) -> Result<()> { + unsafe { + let buffer_index = self.swap_chain.GetCurrentBackBufferIndex() as usize; + + self.wait_for_fence_value(self.frame_fence_values[buffer_index])?; + + let allocator = &self.allocators[buffer_index]; + let back_buffer = &self.render_targets[buffer_index]; + allocator.Reset()?; + self.list.Reset(allocator, None::<&ID3D12PipelineState>)?; + + let to_rt = Self::transition( + back_buffer, + D3D12_RESOURCE_STATE_PRESENT, + D3D12_RESOURCE_STATE_RENDER_TARGET, + ); + self.list.ResourceBarrier(&[to_rt]); + + let rtv = D3D12_CPU_DESCRIPTOR_HANDLE { + ptr: self.rtv_heap.GetCPUDescriptorHandleForHeapStart().ptr + + buffer_index * self.rtv_descriptor_size, + }; + self.list.OMSetRenderTargets(1, Some(&rtv), false, None); + + self.paint_pattern(rtv, frame_index); + + let to_present = Self::transition( + back_buffer, + D3D12_RESOURCE_STATE_RENDER_TARGET, + D3D12_RESOURCE_STATE_PRESENT, + ); + self.list.ResourceBarrier(&[to_present]); + + self.list.Close()?; + + let command_list: windows::Win32::Graphics::Direct3D12::ID3D12CommandList = + self.list.cast()?; + self.queue.ExecuteCommandLists(&[Some(command_list)]); + + self.swap_chain.Present(1, Default::default()).ok()?; + + self.fence_value += 1; + let signal = self.fence_value; + self.queue.Signal(&self.fence, signal)?; + self.frame_fence_values[buffer_index] = signal; + + Ok(()) + } + } + + unsafe fn paint_pattern(&self, rtv: D3D12_CPU_DESCRIPTOR_HANDLE, frame_index: u64) { + unsafe { + let width = self.width.max(1); + let height = self.height as i32; + let luma = (frame_index & 0xFF) as u8; + + for bar in 0..pattern::BARS.len() as u32 { + let x0 = ((bar as u64 * width as u64) / pattern::BARS.len() as u64) as i32; + let x1 = + (((bar + 1) as u64 * width as u64) / pattern::BARS.len() as u64) as i32; + if x1 <= x0 { + continue; + } + let (r, g, b) = pattern::bar_colour(x0 as u32, width); + let colour = rgba(self.format, r, g, b.saturating_add(luma / 4)); + let rect = RECT { + left: x0, + top: 0, + right: x1, + bottom: height, + }; + self.list.ClearRenderTargetView(rtv, &colour, Some(&[rect])); + } + + let (cr, cg, cb) = pattern::counter_colour(frame_index); + let block = pattern::COUNTER_BLOCK as i32; + let counter_rect = RECT { + left: 0, + top: 0, + right: block.min(self.width as i32), + bottom: block.min(height), + }; + let counter_colour = rgba(self.format, cr, cg, cb); + self.list + .ClearRenderTargetView(rtv, &counter_colour, Some(&[counter_rect])); + } + } + + unsafe fn wait_for_fence_value(&self, value: u64) -> Result<()> { + unsafe { + if value == 0 || self.fence.GetCompletedValue() >= value { + return Ok(()); + } + self.fence.SetEventOnCompletion(value, self.fence_event)?; + WaitForSingleObject(self.fence_event, INFINITE); + Ok(()) + } + } + + unsafe fn flush(&mut self) -> Result<()> { + unsafe { + self.fence_value += 1; + let signal = self.fence_value; + self.queue.Signal(&self.fence, signal)?; + self.wait_for_fence_value(signal) + } + } + } + + impl Drop for Renderer { + fn drop(&mut self) { + unsafe { + let _ = self.flush(); + let _ = CloseHandle(self.fence_event); + let _ = &self.device; + } + } + } + + fn rgba(format: PresentFormat, r: u8, g: u8, b: u8) -> [f32; 4] { + let highlight = + if matches!(format, PresentFormat::Rgba16Float) && r == 255 && g == 255 && b > 240 { + 1.25 + } else { + 1.0 + }; + [ + (r as f32 / 255.0) * highlight, + (g as f32 / 255.0) * highlight, + (b as f32 / 255.0) * highlight, + 1.0, + ] + } + + pub fn run() -> Result<()> { + let options = Options::from_env(); + unsafe { + let hwnd = create_window(options.width, options.height, options.borderless)?; + + let mut rect = Default::default(); + let _ = GetClientRect(hwnd, &mut rect); + let width = ((rect.right - rect.left).max(1)) as u32; + let height = ((rect.bottom - rect.top).max(1)) as u32; + + let mut renderer = + Renderer::new(hwnd, width, height, options.backbuffers, options.format)?; + + println!("HWND={}", hwnd.0 as isize); + use std::io::Write; + let _ = std::io::stdout().flush(); + + let mut frame_index: u64 = 0; + let mut msg = MSG::default(); + loop { + while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() { + if msg.message == WM_QUIT { + return Ok(()); + } + let _ = TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + renderer.render_frame(frame_index)?; + frame_index += 1; + + if let Some(limit) = options.frames { + if frame_index >= limit { + return Ok(()); + } + } + } + } + } +} diff --git a/fluxer_desktop/native/win-game-capture/test-apps/d3d9-present-fixture/Cargo.lock b/fluxer_desktop/native/win-game-capture/test-apps/d3d9-present-fixture/Cargo.lock new file mode 100644 index 0000000..c72119c --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/d3d9-present-fixture/Cargo.lock @@ -0,0 +1,156 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "d3d9-present-fixture" +version = "0.0.0" +dependencies = [ + "windows", + "windows-core", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] diff --git a/fluxer_desktop/native/win-game-capture/test-apps/d3d9-present-fixture/Cargo.toml b/fluxer_desktop/native/win-game-capture/test-apps/d3d9-present-fixture/Cargo.toml new file mode 100644 index 0000000..a49444f --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/d3d9-present-fixture/Cargo.toml @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +[package] +name = "d3d9-present-fixture" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +# Empty workspace table keeps this fixture out of any parent Cargo workspace, +# matching the sibling crates (hook/, vulkan-layer/) in this package. +[workspace] +resolver = "2" + +[[bin]] +name = "d3d9-present-fixture" +path = "src/main.rs" + +[target.'cfg(target_os = "windows")'.dependencies] +windows = {version = "0.62.2", features = [ + "Win32_Foundation", + "Win32_Graphics_Direct3D9", + "Win32_Graphics_Gdi", + "Win32_System_LibraryLoader", + "Win32_UI_WindowsAndMessaging", +]} +windows-core = "0.62.2" diff --git a/fluxer_desktop/native/win-game-capture/test-apps/d3d9-present-fixture/src/main.rs b/fluxer_desktop/native/win-game-capture/test-apps/d3d9-present-fixture/src/main.rs new file mode 100644 index 0000000..d7def54 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/d3d9-present-fixture/src/main.rs @@ -0,0 +1,417 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[cfg(not(windows))] +fn main() { + eprintln!("d3d9-present-fixture is only functional on Windows"); +} + +#[cfg(windows)] +fn main() -> windows_core::Result<()> { + windows_impl::run() +} + +#[cfg_attr(not(windows), allow(dead_code))] +mod pattern { + pub const BARS: [(u8, u8, u8); 8] = [ + (255, 255, 255), + (255, 255, 0), + (0, 255, 255), + (0, 255, 0), + (255, 0, 255), + (255, 0, 0), + (0, 0, 255), + (0, 0, 0), + ]; + + pub const COUNTER_BLOCK: u32 = 16; + + pub fn bar_colour(x: u32, width: u32) -> (u8, u8, u8) { + let width = width.max(1); + let index = ((x as u64 * BARS.len() as u64) / width as u64) as usize; + BARS[index.min(BARS.len() - 1)] + } + + pub fn counter_colour(frame_index: u64) -> (u8, u8, u8) { + let low = (frame_index & 0x00FF_FFFF) as u32; + ( + (low & 0xFF) as u8, + ((low >> 8) & 0xFF) as u8, + ((low >> 16) & 0xFF) as u8, + ) + } +} + +#[cfg(windows)] +mod cli { + #[derive(Clone, Copy, Debug, Eq, PartialEq)] + pub enum PresentMode { + Device, + SwapChain, + } + + pub struct Options { + pub frames: Option, + pub width: u32, + pub height: u32, + pub borderless: bool, + pub multisample: bool, + pub present_mode: PresentMode, + } + + impl Default for Options { + fn default() -> Self { + Self { + frames: None, + width: 1280, + height: 720, + borderless: false, + multisample: false, + present_mode: PresentMode::Device, + } + } + } + + impl Options { + pub fn from_env() -> Self { + let mut options = Options::default(); + let mut args = std::env::args().skip(1); + while let Some(arg) = args.next() { + match arg.as_str() { + "--frames" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.frames = Some(value); + } + } + "--width" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.width = value; + } + } + "--height" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.height = value; + } + } + "--borderless" => options.borderless = true, + "--windowed" => options.borderless = false, + "--multisample" => options.multisample = true, + "--no-multisample" => options.multisample = false, + "--swapchain-present" => options.present_mode = PresentMode::SwapChain, + "--device-present" => options.present_mode = PresentMode::Device, + _ => {} + } + } + options + } + } +} + +#[cfg(windows)] +mod windows_impl { + use super::{ + cli::{Options, PresentMode}, + pattern, + }; + use std::ptr::null; + use windows::Win32::Foundation::{HWND, LPARAM, LRESULT, WPARAM}; + use windows::Win32::Graphics::Direct3D9::{ + D3D_SDK_VERSION, D3DADAPTER_DEFAULT, D3DCLEAR_TARGET, D3DCREATE_HARDWARE_VERTEXPROCESSING, + D3DCREATE_SOFTWARE_VERTEXPROCESSING, D3DDEVTYPE_HAL, D3DFMT_X8R8G8B8, + D3DMULTISAMPLE_2_SAMPLES, D3DMULTISAMPLE_NONE, D3DPRESENT_PARAMETERS, D3DRECT, + D3DSWAPEFFECT_DISCARD, Direct3DCreate9, IDirect3D9, IDirect3DDevice9, + }; + use windows::Win32::System::LibraryLoader::GetModuleHandleW; + use windows::Win32::UI::WindowsAndMessaging::{ + CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, CreateWindowExW, DefWindowProcW, DispatchMessageW, + GetClientRect, MSG, PM_REMOVE, PeekMessageW, PostQuitMessage, RegisterClassExW, SW_SHOW, + ShowWindow, TranslateMessage, WM_DESTROY, WM_QUIT, WNDCLASSEXW, WS_OVERLAPPEDWINDOW, + WS_POPUP, WS_VISIBLE, + }; + use windows_core::{BOOL, Result, w}; + + const D3DERR_DEVICELOST: i32 = 0x8876_0868u32 as i32; + + fn d3dcolor_xrgb(r: u8, g: u8, b: u8) -> u32 { + 0xFF00_0000 | ((r as u32) << 16) | ((g as u32) << 8) | (b as u32) + } + + extern "system" fn wnd_proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT { + unsafe { + match msg { + WM_DESTROY => { + PostQuitMessage(0); + LRESULT(0) + } + _ => DefWindowProcW(hwnd, msg, wparam, lparam), + } + } + } + + unsafe fn create_window(width: u32, height: u32, borderless: bool) -> Result { + unsafe { + let instance = GetModuleHandleW(None)?; + let class_name = w!("FluxerD3D9Fixture"); + let wc = WNDCLASSEXW { + cbSize: size_of::() as u32, + style: CS_HREDRAW | CS_VREDRAW, + lpfnWndProc: Some(wnd_proc), + hInstance: instance.into(), + lpszClassName: class_name, + ..Default::default() + }; + RegisterClassExW(&wc); + let style = if borderless { + WS_POPUP | WS_VISIBLE + } else { + WS_OVERLAPPEDWINDOW | WS_VISIBLE + }; + let hwnd = CreateWindowExW( + Default::default(), + class_name, + w!("Fluxer D3D9 Present Fixture"), + style, + CW_USEDEFAULT, + CW_USEDEFAULT, + width as i32, + height as i32, + None, + None, + Some(instance.into()), + None, + )?; + let _ = ShowWindow(hwnd, SW_SHOW); + Ok(hwnd) + } + } + + fn present_params( + hwnd: HWND, + width: u32, + height: u32, + multisample: bool, + ) -> D3DPRESENT_PARAMETERS { + D3DPRESENT_PARAMETERS { + BackBufferWidth: width, + BackBufferHeight: height, + BackBufferFormat: D3DFMT_X8R8G8B8, + BackBufferCount: 1, + MultiSampleType: if multisample { + D3DMULTISAMPLE_2_SAMPLES + } else { + D3DMULTISAMPLE_NONE + }, + MultiSampleQuality: 0, + SwapEffect: D3DSWAPEFFECT_DISCARD, + hDeviceWindow: hwnd, + Windowed: BOOL(1), + EnableAutoDepthStencil: BOOL(0), + ..Default::default() + } + } + + struct Renderer { + _d3d9: IDirect3D9, + device: IDirect3DDevice9, + hwnd: HWND, + width: u32, + height: u32, + device_lost: bool, + multisample: bool, + present_mode: PresentMode, + } + + impl Renderer { + unsafe fn new( + hwnd: HWND, + width: u32, + height: u32, + multisample: bool, + present_mode: PresentMode, + ) -> Result { + unsafe { + let d3d9 = Direct3DCreate9(D3D_SDK_VERSION) + .ok_or_else(|| windows_core::Error::from_thread())?; + + let mut params = present_params(hwnd, width, height, multisample); + + let mut device: Option = None; + let mut created = d3d9 + .CreateDevice( + D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + hwnd, + D3DCREATE_HARDWARE_VERTEXPROCESSING as u32, + &mut params, + &mut device, + ) + .is_ok(); + if !created { + created = d3d9 + .CreateDevice( + D3DADAPTER_DEFAULT, + D3DDEVTYPE_HAL, + hwnd, + D3DCREATE_SOFTWARE_VERTEXPROCESSING as u32, + &mut params, + &mut device, + ) + .is_ok(); + } + let device = if created { + device.ok_or_else(|| windows_core::Error::from_thread())? + } else { + return Err(windows_core::Error::from_thread()); + }; + + Ok(Self { + _d3d9: d3d9, + device, + hwnd, + width, + height, + device_lost: false, + multisample, + present_mode, + }) + } + } + + unsafe fn try_reset(&mut self) { + unsafe { + let mut params = + present_params(self.hwnd, self.width, self.height, self.multisample); + if self.device.Reset(&mut params).is_ok() { + self.device_lost = false; + } + } + } + + unsafe fn render_frame(&mut self, frame_index: u64) -> Result<()> { + unsafe { + if self.device_lost { + self.try_reset(); + if self.device_lost { + return self.present(); + } + } + + let width = self.width.max(1); + let height = self.height as i32; + + let bars = pattern::BARS.len() as u32; + for bar in 0..bars { + let x1 = ((bar as u64 * width as u64) / bars as u64) as i32; + let x2 = (((bar + 1) as u64 * width as u64) / bars as u64) as i32; + if x2 <= x1 { + continue; + } + let mid = ((x1 + x2) / 2).max(0) as u32; + let (r, g, b) = pattern::bar_colour(mid, width); + let rect = D3DRECT { + x1, + y1: 0, + x2, + y2: height, + }; + self.device.Clear( + 1, + &rect, + D3DCLEAR_TARGET as u32, + d3dcolor_xrgb(r, g, b), + 1.0, + 0, + )?; + } + + let block = pattern::COUNTER_BLOCK as i32; + let block_x2 = block.min(width as i32); + let block_y2 = block.min(height); + if block_x2 > 0 && block_y2 > 0 { + let (r, g, b) = pattern::counter_colour(frame_index); + let rect = D3DRECT { + x1: 0, + y1: 0, + x2: block_x2, + y2: block_y2, + }; + self.device.Clear( + 1, + &rect, + D3DCLEAR_TARGET as u32, + d3dcolor_xrgb(r, g, b), + 1.0, + 0, + )?; + } + + self.present() + } + } + + unsafe fn present(&mut self) -> Result<()> { + unsafe { + let result = match self.present_mode { + PresentMode::Device => { + self.device.Present(null(), null(), HWND::default(), null()) + } + PresentMode::SwapChain => self.device.GetSwapChain(0).and_then(|swap_chain| { + swap_chain.Present(null(), null(), HWND::default(), null(), 0) + }), + }; + match result { + Ok(()) => Ok(()), + Err(err) if err.code().0 == D3DERR_DEVICELOST => { + self.device_lost = true; + Ok(()) + } + Err(err) => Err(err), + } + } + } + } + + pub fn run() -> Result<()> { + let options = Options::from_env(); + unsafe { + let hwnd = create_window(options.width, options.height, options.borderless)?; + + let mut rect = Default::default(); + let _ = GetClientRect(hwnd, &mut rect); + let width = ((rect.right - rect.left).max(1)) as u32; + let height = ((rect.bottom - rect.top).max(1)) as u32; + + let mut renderer = Renderer::new( + hwnd, + width, + height, + options.multisample, + options.present_mode, + )?; + + println!("HWND={}", hwnd.0 as isize); + use std::io::Write; + let _ = std::io::stdout().flush(); + + let mut frame_index: u64 = 0; + let mut msg = MSG::default(); + loop { + while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() { + if msg.message == WM_QUIT { + return Ok(()); + } + let _ = TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + renderer.render_frame(frame_index)?; + frame_index += 1; + + if let Some(limit) = options.frames { + if frame_index >= limit { + return Ok(()); + } + } + } + } + } +} diff --git a/fluxer_desktop/native/win-game-capture/test-apps/i686-present-fixture/Cargo.lock b/fluxer_desktop/native/win-game-capture/test-apps/i686-present-fixture/Cargo.lock new file mode 100644 index 0000000..4640106 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/i686-present-fixture/Cargo.lock @@ -0,0 +1,156 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "i686-present-fixture" +version = "0.0.0" +dependencies = [ + "windows", + "windows-core", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] diff --git a/fluxer_desktop/native/win-game-capture/test-apps/i686-present-fixture/Cargo.toml b/fluxer_desktop/native/win-game-capture/test-apps/i686-present-fixture/Cargo.toml new file mode 100644 index 0000000..1f93462 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/i686-present-fixture/Cargo.toml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +[package] +name = "i686-present-fixture" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +# Empty workspace table keeps this fixture out of any parent Cargo workspace, +# matching the sibling crates (hook/, vulkan-layer/) in this package. +[workspace] +resolver = "2" + +[[bin]] +name = "i686-present-fixture" +path = "src/main.rs" + +# This fixture is a deterministic Direct3D 11 present fixture whose ONLY purpose +# is to be built for the 32-bit (i686 / WOW64) target so the harness can drive +# the cross-bitness injection paths (an x64 host injecting the 32-bit hook into a +# 32-bit game). The source is target-arch-agnostic -- what makes it "i686" is +# building it for `i686-pc-windows-msvc`: +# +# cargo build --release --manifest-path test-apps/i686-present-fixture/Cargo.toml \ +# --target i686-pc-windows-msvc +# +# It also compile-checks for x86_64-pc-windows-msvc so the standard cargo-check +# matrix stays uniform across fixtures. +[target.'cfg(target_os = "windows")'.dependencies] +windows = {version = "0.62.2", features = [ + "Win32_Foundation", + "Win32_Graphics_Direct3D", + "Win32_Graphics_Direct3D11", + "Win32_Graphics_Dxgi", + "Win32_Graphics_Dxgi_Common", + "Win32_Graphics_Gdi", + "Win32_System_LibraryLoader", + "Win32_UI_WindowsAndMessaging", +]} +windows-core = "0.62.2" diff --git a/fluxer_desktop/native/win-game-capture/test-apps/i686-present-fixture/src/main.rs b/fluxer_desktop/native/win-game-capture/test-apps/i686-present-fixture/src/main.rs new file mode 100644 index 0000000..7b4f6f5 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/i686-present-fixture/src/main.rs @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[cfg(not(windows))] +fn main() { + eprintln!("i686-present-fixture is only functional on Windows"); +} + +#[cfg(windows)] +fn main() -> windows_core::Result<()> { + windows_impl::run() +} + +#[cfg_attr(not(windows), allow(dead_code))] +mod pattern { + pub const BARS: [(u8, u8, u8); 8] = [ + (255, 255, 255), + (255, 255, 0), + (0, 255, 255), + (0, 255, 0), + (255, 0, 255), + (255, 0, 0), + (0, 0, 255), + (0, 0, 0), + ]; + + pub const COUNTER_BLOCK: u32 = 16; + + pub fn bar_colour(x: u32, width: u32) -> (u8, u8, u8) { + let width = width.max(1); + let index = ((x as u64 * BARS.len() as u64) / width as u64) as usize; + BARS[index.min(BARS.len() - 1)] + } + + pub fn counter_colour(frame_index: u64) -> (u8, u8, u8) { + let low = (frame_index & 0x00FF_FFFF) as u32; + ( + (low & 0xFF) as u8, + ((low >> 8) & 0xFF) as u8, + ((low >> 16) & 0xFF) as u8, + ) + } +} + +#[cfg(windows)] +mod cli { + pub struct Options { + pub frames: Option, + pub width: u32, + pub height: u32, + pub borderless: bool, + } + + impl Default for Options { + fn default() -> Self { + Self { + frames: None, + width: 1280, + height: 720, + borderless: false, + } + } + } + + impl Options { + pub fn from_env() -> Self { + let mut options = Options::default(); + let mut args = std::env::args().skip(1); + while let Some(arg) = args.next() { + match arg.as_str() { + "--frames" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.frames = Some(value); + } + } + "--width" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.width = value; + } + } + "--height" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.height = value; + } + } + "--borderless" => options.borderless = true, + "--windowed" => options.borderless = false, + _ => {} + } + } + options + } + } +} + +#[cfg(windows)] +mod windows_impl { + use super::{cli::Options, pattern}; + use windows::Win32::Foundation::{HMODULE, HWND, LPARAM, LRESULT, WPARAM}; + use windows::Win32::Graphics::Direct3D::D3D_DRIVER_TYPE_HARDWARE; + use windows::Win32::Graphics::Direct3D11::{ + D3D11_CREATE_DEVICE_BGRA_SUPPORT, D3D11_MAPPED_SUBRESOURCE, D3D11_SDK_VERSION, + D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext, ID3D11RenderTargetView, + ID3D11Resource, ID3D11Texture2D, + }; + use windows::Win32::Graphics::Dxgi::Common::{ + DXGI_ALPHA_MODE_IGNORE, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SAMPLE_DESC, + }; + use windows::Win32::Graphics::Dxgi::{ + DXGI_SCALING_STRETCH, DXGI_SWAP_CHAIN_DESC1, DXGI_SWAP_EFFECT_FLIP_DISCARD, + DXGI_USAGE_RENDER_TARGET_OUTPUT, IDXGIDevice, IDXGIFactory2, IDXGISwapChain1, + }; + use windows::Win32::System::LibraryLoader::GetModuleHandleW; + use windows::Win32::UI::WindowsAndMessaging::{ + CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, CreateWindowExW, DefWindowProcW, DispatchMessageW, + GetClientRect, MSG, PM_REMOVE, PeekMessageW, PostQuitMessage, RegisterClassExW, SW_SHOW, + ShowWindow, TranslateMessage, WM_DESTROY, WM_QUIT, WNDCLASSEXW, WS_OVERLAPPEDWINDOW, + WS_POPUP, WS_VISIBLE, + }; + use windows_core::{Interface, Result, w}; + + extern "system" fn wnd_proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT { + unsafe { + match msg { + WM_DESTROY => { + PostQuitMessage(0); + LRESULT(0) + } + _ => DefWindowProcW(hwnd, msg, wparam, lparam), + } + } + } + + unsafe fn create_window(width: u32, height: u32, borderless: bool) -> Result { + unsafe { + let instance = GetModuleHandleW(None)?; + let class_name = w!("FluxerI686Fixture"); + let wc = WNDCLASSEXW { + cbSize: size_of::() as u32, + style: CS_HREDRAW | CS_VREDRAW, + lpfnWndProc: Some(wnd_proc), + hInstance: instance.into(), + lpszClassName: class_name, + ..Default::default() + }; + RegisterClassExW(&wc); + let style = if borderless { + WS_POPUP | WS_VISIBLE + } else { + WS_OVERLAPPEDWINDOW | WS_VISIBLE + }; + let hwnd = CreateWindowExW( + Default::default(), + class_name, + w!("Fluxer i686 (WOW64) Present Fixture"), + style, + CW_USEDEFAULT, + CW_USEDEFAULT, + width as i32, + height as i32, + None, + None, + Some(instance.into()), + None, + )?; + let _ = ShowWindow(hwnd, SW_SHOW); + Ok(hwnd) + } + } + + struct Renderer { + device: ID3D11Device, + context: ID3D11DeviceContext, + swap_chain: IDXGISwapChain1, + rtv: ID3D11RenderTargetView, + width: u32, + height: u32, + staging: ID3D11Texture2D, + } + + impl Renderer { + unsafe fn new(hwnd: HWND, width: u32, height: u32) -> Result { + unsafe { + let mut device: Option = None; + let mut context: Option = None; + D3D11CreateDevice( + None, + D3D_DRIVER_TYPE_HARDWARE, + HMODULE::default(), + D3D11_CREATE_DEVICE_BGRA_SUPPORT, + None, + D3D11_SDK_VERSION, + Some(&mut device), + None, + Some(&mut context), + )?; + let device = device.expect("D3D11CreateDevice yielded no device"); + let context = context.expect("D3D11CreateDevice yielded no context"); + + let dxgi_device: IDXGIDevice = device.cast()?; + let adapter = dxgi_device.GetAdapter()?; + let factory: IDXGIFactory2 = adapter.GetParent()?; + + let desc = DXGI_SWAP_CHAIN_DESC1 { + Width: width, + Height: height, + Format: DXGI_FORMAT_B8G8R8A8_UNORM, + Stereo: false.into(), + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + BufferUsage: DXGI_USAGE_RENDER_TARGET_OUTPUT, + BufferCount: 2, + Scaling: DXGI_SCALING_STRETCH, + SwapEffect: DXGI_SWAP_EFFECT_FLIP_DISCARD, + AlphaMode: DXGI_ALPHA_MODE_IGNORE, + Flags: 0, + }; + let swap_chain = + factory.CreateSwapChainForHwnd(&device, hwnd, &desc, None, None)?; + + let rtv = Self::make_rtv(&device, &swap_chain)?; + let staging = Self::make_staging(&device, width, height)?; + + Ok(Self { + device, + context, + swap_chain, + rtv, + width, + height, + staging, + }) + } + } + + unsafe fn make_rtv( + device: &ID3D11Device, + swap_chain: &IDXGISwapChain1, + ) -> Result { + unsafe { + let back_buffer: ID3D11Texture2D = swap_chain.GetBuffer(0)?; + let resource: ID3D11Resource = back_buffer.cast()?; + let mut rtv: Option = None; + device.CreateRenderTargetView(&resource, None, Some(&mut rtv))?; + Ok(rtv.expect("CreateRenderTargetView yielded no view")) + } + } + + unsafe fn make_staging( + device: &ID3D11Device, + width: u32, + height: u32, + ) -> Result { + use windows::Win32::Graphics::Direct3D11::{ + D3D11_CPU_ACCESS_WRITE, D3D11_TEXTURE2D_DESC, D3D11_USAGE_STAGING, + }; + unsafe { + let desc = D3D11_TEXTURE2D_DESC { + Width: width, + Height: height, + MipLevels: 1, + ArraySize: 1, + Format: DXGI_FORMAT_B8G8R8A8_UNORM, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Usage: D3D11_USAGE_STAGING, + BindFlags: 0, + CPUAccessFlags: D3D11_CPU_ACCESS_WRITE.0 as u32, + MiscFlags: 0, + }; + let mut staging: Option = None; + device.CreateTexture2D(&desc, None, Some(&mut staging))?; + Ok(staging.expect("CreateTexture2D yielded no staging texture")) + } + } + + unsafe fn render_frame(&self, frame_index: u64) -> Result<()> { + use windows::Win32::Graphics::Direct3D11::D3D11_MAP_WRITE; + unsafe { + let mut mapped = D3D11_MAPPED_SUBRESOURCE::default(); + self.context + .Map(&self.staging, 0, D3D11_MAP_WRITE, 0, Some(&mut mapped))?; + let row_pitch = mapped.RowPitch as usize; + let base = mapped.pData as *mut u8; + let counter = pattern::counter_colour(frame_index); + let luma = (frame_index & 0xFF) as u8; + for y in 0..self.height { + let row = base.add(y as usize * row_pitch); + for x in 0..self.width { + let (r, g, b) = if x < pattern::COUNTER_BLOCK && y < pattern::COUNTER_BLOCK + { + counter + } else { + pattern::bar_colour(x, self.width) + }; + let px = row.add(x as usize * 4); + *px = b.saturating_add(luma / 4); + *px.add(1) = g; + *px.add(2) = r; + *px.add(3) = 255; + } + } + self.context.Unmap(&self.staging, 0); + + let back_buffer: ID3D11Texture2D = self.swap_chain.GetBuffer(0)?; + let dst: ID3D11Resource = back_buffer.cast()?; + let src: ID3D11Resource = self.staging.cast()?; + self.context.CopyResource(&dst, &src); + let _ = &self.rtv; + self.swap_chain.Present(1, Default::default()).ok()?; + let _ = &self.device; + Ok(()) + } + } + } + + pub fn run() -> Result<()> { + let options = Options::from_env(); + unsafe { + let hwnd = create_window(options.width, options.height, options.borderless)?; + + let mut rect = Default::default(); + let _ = GetClientRect(hwnd, &mut rect); + let width = ((rect.right - rect.left).max(1)) as u32; + let height = ((rect.bottom - rect.top).max(1)) as u32; + + let renderer = Renderer::new(hwnd, width, height)?; + + println!("HWND={}", hwnd.0 as isize); + use std::io::Write; + let _ = std::io::stdout().flush(); + + let mut frame_index: u64 = 0; + let mut msg = MSG::default(); + loop { + while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() { + if msg.message == WM_QUIT { + return Ok(()); + } + let _ = TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + renderer.render_frame(frame_index)?; + frame_index += 1; + + if let Some(limit) = options.frames { + if frame_index >= limit { + return Ok(()); + } + } + } + } + } +} diff --git a/fluxer_desktop/native/win-game-capture/test-apps/opengl-swapbuffers-fixture/Cargo.lock b/fluxer_desktop/native/win-game-capture/test-apps/opengl-swapbuffers-fixture/Cargo.lock new file mode 100644 index 0000000..6ff717c --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/opengl-swapbuffers-fixture/Cargo.lock @@ -0,0 +1,156 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "opengl-swapbuffers-fixture" +version = "0.0.0" +dependencies = [ + "windows", + "windows-core", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] diff --git a/fluxer_desktop/native/win-game-capture/test-apps/opengl-swapbuffers-fixture/Cargo.toml b/fluxer_desktop/native/win-game-capture/test-apps/opengl-swapbuffers-fixture/Cargo.toml new file mode 100644 index 0000000..db94dd7 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/opengl-swapbuffers-fixture/Cargo.toml @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +[package] +name = "opengl-swapbuffers-fixture" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +# Empty workspace table keeps this fixture out of any parent Cargo workspace, +# matching the sibling crates (hook/, vulkan-layer/) in this package. +[workspace] +resolver = "2" + +[[bin]] +name = "opengl-swapbuffers-fixture" +path = "src/main.rs" + +[target.'cfg(target_os = "windows")'.dependencies] +windows = {version = "0.62.2", features = [ + "Win32_Foundation", + "Win32_Graphics_Gdi", + "Win32_Graphics_OpenGL", + "Win32_System_LibraryLoader", + "Win32_UI_WindowsAndMessaging", +]} +windows-core = "0.62.2" diff --git a/fluxer_desktop/native/win-game-capture/test-apps/opengl-swapbuffers-fixture/src/main.rs b/fluxer_desktop/native/win-game-capture/test-apps/opengl-swapbuffers-fixture/src/main.rs new file mode 100644 index 0000000..eed6c35 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/opengl-swapbuffers-fixture/src/main.rs @@ -0,0 +1,484 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[cfg(not(windows))] +fn main() { + eprintln!("opengl-swapbuffers-fixture is only functional on Windows"); +} + +#[cfg(windows)] +fn main() -> windows_core::Result<()> { + windows_impl::run() +} + +#[allow(dead_code)] +mod pattern { + pub const BARS: [(u8, u8, u8); 8] = [ + (255, 255, 255), + (255, 255, 0), + (0, 255, 255), + (0, 255, 0), + (255, 0, 255), + (255, 0, 0), + (0, 0, 255), + (0, 0, 0), + ]; + + pub const COUNTER_BLOCK: u32 = 16; + + pub fn bar_colour(x: u32, width: u32) -> (u8, u8, u8) { + let width = width.max(1); + let index = ((x as u64 * BARS.len() as u64) / width as u64) as usize; + BARS[index.min(BARS.len() - 1)] + } + + pub fn bar_x_range(index: usize, width: u32) -> (u32, u32) { + let width = width.max(1) as u64; + let len = BARS.len() as u64; + let start = (index as u64 * width) / len; + let end = ((index as u64 + 1) * width) / len; + (start as u32, end.min(width) as u32) + } + + pub fn counter_colour(frame_index: u64) -> (u8, u8, u8) { + let low = (frame_index & 0x00FF_FFFF) as u32; + ( + (low & 0xFF) as u8, + ((low >> 8) & 0xFF) as u8, + ((low >> 16) & 0xFF) as u8, + ) + } + + pub fn clear_colour(frame_index: u64) -> [f32; 3] { + let (r, g, b) = BARS[(frame_index % BARS.len() as u64) as usize]; + [r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0] + } +} + +#[cfg(windows)] +mod cli { + pub struct Options { + pub frames: Option, + pub width: u32, + pub height: u32, + pub borderless: bool, + pub layer: bool, + pub stress_pack_state: bool, + pub resize_at: Option, + pub frame_delay_ms: u64, + } + + impl Default for Options { + fn default() -> Self { + Self { + frames: None, + width: 1280, + height: 720, + borderless: false, + layer: false, + stress_pack_state: false, + resize_at: None, + frame_delay_ms: 0, + } + } + } + + impl Options { + pub fn from_env() -> Self { + let mut options = Options::default(); + let mut args = std::env::args().skip(1); + while let Some(arg) = args.next() { + match arg.as_str() { + "--frames" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.frames = Some(value); + } + } + "--width" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.width = value; + } + } + "--height" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.height = value; + } + } + "--borderless" => options.borderless = true, + "--windowed" => options.borderless = false, + "--layer" => options.layer = true, + "--stress-pack-state" => options.stress_pack_state = true, + "--resize-at" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.resize_at = Some(value); + } + } + "--frame-delay-ms" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.frame_delay_ms = value; + } + } + _ => {} + } + } + options + } + } +} + +#[cfg(windows)] +mod windows_impl { + use super::cli::Options; + use super::pattern; + use windows::Win32::Foundation::{HWND, LPARAM, LRESULT, WPARAM}; + use windows::Win32::Graphics::Gdi::{GetDC, HDC, ReleaseDC, WGL_SWAP_MAIN_PLANE}; + use windows::Win32::Graphics::OpenGL::{ + ChoosePixelFormat, GL_COLOR_BUFFER_BIT, GL_FRONT, GL_PACK_ALIGNMENT, GL_PACK_LSB_FIRST, + GL_PACK_ROW_LENGTH, GL_PACK_SKIP_PIXELS, GL_PACK_SKIP_ROWS, GL_PACK_SWAP_BYTES, + GL_READ_BUFFER, GL_SCISSOR_TEST, PFD_DOUBLEBUFFER, PFD_DRAW_TO_WINDOW, PFD_MAIN_PLANE, + PFD_SUPPORT_OPENGL, PFD_TYPE_RGBA, PIXELFORMATDESCRIPTOR, SetPixelFormat, SwapBuffers, + glClear, glClearColor, glDisable, glEnable, glGetIntegerv, glPixelStorei, glReadBuffer, + glScissor, glViewport, wglCreateContext, wglDeleteContext, wglGetProcAddress, + wglMakeCurrent, wglSwapLayerBuffers, + }; + use windows::Win32::System::LibraryLoader::GetModuleHandleW; + use windows::Win32::UI::WindowsAndMessaging::{ + CS_HREDRAW, CS_OWNDC, CS_VREDRAW, CW_USEDEFAULT, CreateWindowExW, DefWindowProcW, + DispatchMessageW, GetClientRect, MSG, MoveWindow, PM_REMOVE, PeekMessageW, PostQuitMessage, + RegisterClassExW, SW_SHOW, ShowWindow, TranslateMessage, WM_DESTROY, WM_QUIT, WNDCLASSEXW, + WS_OVERLAPPEDWINDOW, WS_POPUP, WS_VISIBLE, + }; + use windows_core::{PCSTR, Result, w}; + + const GL_PIXEL_PACK_BUFFER: u32 = 0x88EB; + const GL_PIXEL_PACK_BUFFER_BINDING: u32 = 0x88ED; + const GL_STREAM_READ: u32 = 0x88E1; + + type GlGenBuffersFn = unsafe extern "system" fn(i32, *mut u32); + type GlBindBufferFn = unsafe extern "system" fn(u32, u32); + type GlBufferDataFn = unsafe extern "system" fn(u32, isize, *const core::ffi::c_void, u32); + type GlDeleteBuffersFn = unsafe extern "system" fn(i32, *const u32); + + #[derive(Clone, Copy)] + struct BufferProcs { + gen_buffers: GlGenBuffersFn, + bind_buffer: GlBindBufferFn, + buffer_data: GlBufferDataFn, + delete_buffers: GlDeleteBuffersFn, + } + + struct PackStateStress { + procs: Option, + pbo: u32, + } + + impl PackStateStress { + unsafe fn new() -> Self { + let Some(procs) = (unsafe { BufferProcs::load() }) else { + return Self { + procs: None, + pbo: 0, + }; + }; + let mut pbo = 0u32; + unsafe { + (procs.gen_buffers)(1, &mut pbo); + } + if pbo != 0 { + let storage = [0u8; 16]; + unsafe { + (procs.bind_buffer)(GL_PIXEL_PACK_BUFFER, pbo); + (procs.buffer_data)( + GL_PIXEL_PACK_BUFFER, + storage.len() as isize, + storage.as_ptr().cast(), + GL_STREAM_READ, + ); + (procs.bind_buffer)(GL_PIXEL_PACK_BUFFER, 0); + } + } + Self { + procs: Some(procs), + pbo, + } + } + + fn pbo(&self) -> Option { + (self.pbo != 0).then_some(self.pbo) + } + } + + impl Drop for PackStateStress { + fn drop(&mut self) { + unsafe { + if let Some(procs) = self.procs { + if self.pbo != 0 { + (procs.bind_buffer)(GL_PIXEL_PACK_BUFFER, 0); + (procs.delete_buffers)(1, &self.pbo); + } + } + } + } + } + + impl BufferProcs { + unsafe fn load() -> Option { + Some(Self { + gen_buffers: unsafe { load_gl_proc(b"glGenBuffers\0")? }, + bind_buffer: unsafe { load_gl_proc(b"glBindBuffer\0")? }, + buffer_data: unsafe { load_gl_proc(b"glBufferData\0")? }, + delete_buffers: unsafe { load_gl_proc(b"glDeleteBuffers\0")? }, + }) + } + } + + unsafe fn load_gl_proc(name: &'static [u8]) -> Option { + let proc = unsafe { wglGetProcAddress(PCSTR(name.as_ptr()))? }; + let address = proc as usize; + if address <= 3 || address == usize::MAX { + return None; + } + Some(unsafe { std::mem::transmute_copy::<_, T>(&proc) }) + } + + extern "system" fn wnd_proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT { + unsafe { + match msg { + WM_DESTROY => { + PostQuitMessage(0); + LRESULT(0) + } + _ => DefWindowProcW(hwnd, msg, wparam, lparam), + } + } + } + + unsafe fn create_window(width: u32, height: u32, borderless: bool) -> Result { + unsafe { + let instance = GetModuleHandleW(None)?; + let class_name = w!("FluxerOpenGLFixture"); + let wc = WNDCLASSEXW { + cbSize: size_of::() as u32, + style: CS_HREDRAW | CS_VREDRAW | CS_OWNDC, + lpfnWndProc: Some(wnd_proc), + hInstance: instance.into(), + lpszClassName: class_name, + ..Default::default() + }; + RegisterClassExW(&wc); + let style = if borderless { + WS_POPUP | WS_VISIBLE + } else { + WS_OVERLAPPEDWINDOW | WS_VISIBLE + }; + let hwnd = CreateWindowExW( + Default::default(), + class_name, + w!("Fluxer OpenGL SwapBuffers Fixture"), + style, + CW_USEDEFAULT, + CW_USEDEFAULT, + width as i32, + height as i32, + None, + None, + Some(instance.into()), + None, + )?; + let _ = ShowWindow(hwnd, SW_SHOW); + Ok(hwnd) + } + } + + unsafe fn set_pixel_format(hdc: HDC) -> Result<()> { + unsafe { + let mut pfd = PIXELFORMATDESCRIPTOR { + nSize: size_of::() as u16, + nVersion: 1, + dwFlags: PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, + iPixelType: PFD_TYPE_RGBA, + cColorBits: 32, + cDepthBits: 24, + cStencilBits: 8, + iLayerType: PFD_MAIN_PLANE.0 as u8, + ..Default::default() + }; + let format = ChoosePixelFormat(hdc, &pfd); + if format == 0 { + return Err(windows_core::Error::from_thread()); + } + SetPixelFormat(hdc, format, &mut pfd)?; + Ok(()) + } + } + + unsafe fn clear_rect(colour: (u8, u8, u8)) { + unsafe { + let (r, g, b) = colour; + glClearColor(r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + } + } + + unsafe fn render_pattern(frame_index: u64, width: u32, height: u32) { + unsafe { + glViewport(0, 0, width as i32, height as i32); + + glEnable(GL_SCISSOR_TEST); + for index in 0..pattern::BARS.len() { + let (start, end) = pattern::bar_x_range(index, width); + if end <= start { + continue; + } + glScissor(start as i32, 0, (end - start) as i32, height as i32); + clear_rect(pattern::BARS[index]); + } + + let block = pattern::COUNTER_BLOCK.min(width).min(height); + if block > 0 { + let y = height - block; + glScissor(0, y as i32, block as i32, block as i32); + clear_rect(pattern::counter_colour(frame_index)); + } + + glDisable(GL_SCISSOR_TEST); + } + } + + unsafe fn client_size(hwnd: HWND) -> (u32, u32) { + unsafe { + let mut rect = Default::default(); + let _ = GetClientRect(hwnd, &mut rect); + ( + ((rect.right - rect.left).max(1)) as u32, + ((rect.bottom - rect.top).max(1)) as u32, + ) + } + } + + unsafe fn stress_pack_state(stress: &PackStateStress) { + unsafe { + glReadBuffer(GL_FRONT); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_ROW_LENGTH, 0); + glPixelStorei(GL_PACK_SKIP_PIXELS, 0); + glPixelStorei(GL_PACK_SKIP_ROWS, 0); + glPixelStorei(GL_PACK_SWAP_BYTES, 1); + glPixelStorei(GL_PACK_LSB_FIRST, 1); + if let (Some(procs), Some(pbo)) = (stress.procs, stress.pbo()) { + (procs.bind_buffer)(GL_PIXEL_PACK_BUFFER, pbo); + } + } + } + + unsafe fn assert_stress_pack_state(stress: &PackStateStress) { + unsafe { + let mut read_buffer = 0i32; + let mut pack_alignment = 0i32; + let mut pack_row_length = 0i32; + let mut pack_skip_pixels = 0i32; + let mut pack_skip_rows = 0i32; + let mut pack_swap_bytes = 0i32; + let mut pack_lsb_first = 0i32; + let mut pack_buffer = 0i32; + + glGetIntegerv(GL_READ_BUFFER, &mut read_buffer); + glGetIntegerv(GL_PACK_ALIGNMENT, &mut pack_alignment); + glGetIntegerv(GL_PACK_ROW_LENGTH, &mut pack_row_length); + glGetIntegerv(GL_PACK_SKIP_PIXELS, &mut pack_skip_pixels); + glGetIntegerv(GL_PACK_SKIP_ROWS, &mut pack_skip_rows); + glGetIntegerv(GL_PACK_SWAP_BYTES, &mut pack_swap_bytes); + glGetIntegerv(GL_PACK_LSB_FIRST, &mut pack_lsb_first); + glGetIntegerv(GL_PIXEL_PACK_BUFFER_BINDING, &mut pack_buffer); + + assert_eq!( + read_buffer as u32, GL_FRONT, + "GL_READ_BUFFER was not restored" + ); + assert_eq!(pack_alignment, 1, "GL_PACK_ALIGNMENT was not restored"); + assert_eq!(pack_row_length, 0, "GL_PACK_ROW_LENGTH was not restored"); + assert_eq!(pack_skip_pixels, 0, "GL_PACK_SKIP_PIXELS was not restored"); + assert_eq!(pack_skip_rows, 0, "GL_PACK_SKIP_ROWS was not restored"); + assert_eq!(pack_swap_bytes, 1, "GL_PACK_SWAP_BYTES was not restored"); + assert_eq!(pack_lsb_first, 1, "GL_PACK_LSB_FIRST was not restored"); + if let Some(pbo) = stress.pbo() { + assert_eq!( + pack_buffer as u32, pbo, + "GL_PIXEL_PACK_BUFFER_BINDING was not restored" + ); + } + } + } + + pub fn run() -> Result<()> { + let options = Options::from_env(); + unsafe { + let hwnd = create_window(options.width, options.height, options.borderless)?; + + println!("HWND={}", hwnd.0 as isize); + use std::io::Write; + let _ = std::io::stdout().flush(); + + let hdc = GetDC(Some(hwnd)); + set_pixel_format(hdc)?; + let context = wglCreateContext(hdc)?; + wglMakeCurrent(hdc, context)?; + let pack_state_stress = PackStateStress::new(); + + let mut frame_index: u64 = 0; + let mut msg = MSG::default(); + let mut resized = false; + 'render: loop { + while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() { + if msg.message == WM_QUIT { + break 'render; + } + let _ = TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + if let Some(resize_at) = options.resize_at { + if !resized && frame_index >= resize_at { + let width = options.width.saturating_add(160).max(1) as i32; + let height = options.height.saturating_add(96).max(1) as i32; + let _ = MoveWindow(hwnd, 0, 0, width, height, true); + resized = true; + } + } + + let (width, height) = client_size(hwnd); + render_pattern(frame_index, width, height); + if options.stress_pack_state { + stress_pack_state(&pack_state_stress); + } + + if options.layer { + wglSwapLayerBuffers(hdc, WGL_SWAP_MAIN_PLANE)?; + } else { + SwapBuffers(hdc)?; + } + + if options.stress_pack_state { + assert_stress_pack_state(&pack_state_stress); + } + + frame_index += 1; + if options.frame_delay_ms > 0 { + std::thread::sleep(std::time::Duration::from_millis(options.frame_delay_ms)); + } + if let Some(limit) = options.frames { + if frame_index >= limit { + break 'render; + } + } + } + + let _ = wglMakeCurrent( + HDC::default(), + windows::Win32::Graphics::OpenGL::HGLRC::default(), + ); + let _ = wglDeleteContext(context); + ReleaseDC(Some(hwnd), hdc); + Ok(()) + } + } +} diff --git a/fluxer_desktop/native/win-game-capture/test-apps/vulkan-present-fixture/Cargo.lock b/fluxer_desktop/native/win-game-capture/test-apps/vulkan-present-fixture/Cargo.lock new file mode 100644 index 0000000..132bf54 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/vulkan-present-fixture/Cargo.lock @@ -0,0 +1,182 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +dependencies = [ + "libloading", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "vulkan-present-fixture" +version = "0.0.0" +dependencies = [ + "ash", + "windows", + "windows-core", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] diff --git a/fluxer_desktop/native/win-game-capture/test-apps/vulkan-present-fixture/Cargo.toml b/fluxer_desktop/native/win-game-capture/test-apps/vulkan-present-fixture/Cargo.toml new file mode 100644 index 0000000..28654e4 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/vulkan-present-fixture/Cargo.toml @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: AGPL-3.0-or-later +[package] +name = "vulkan-present-fixture" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +# Empty workspace table keeps this fixture out of any parent Cargo workspace, +# matching the sibling crates (hook/, vulkan-layer/) in this package. +[workspace] +resolver = "2" + +[[bin]] +name = "vulkan-present-fixture" +path = "src/main.rs" + +[target.'cfg(target_os = "windows")'.dependencies] +ash = {version = "0.38.0", default-features = false, features = ["std", "loaded"]} +windows = {version = "0.62.2", features = [ + "Win32_Foundation", + "Win32_Graphics_Gdi", + "Win32_System_LibraryLoader", + "Win32_UI_WindowsAndMessaging", +]} +windows-core = "0.62.2" diff --git a/fluxer_desktop/native/win-game-capture/test-apps/vulkan-present-fixture/src/main.rs b/fluxer_desktop/native/win-game-capture/test-apps/vulkan-present-fixture/src/main.rs new file mode 100644 index 0000000..186ea76 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/test-apps/vulkan-present-fixture/src/main.rs @@ -0,0 +1,681 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#[cfg(not(windows))] +fn main() { + eprintln!("vulkan-present-fixture is only functional on Windows"); +} + +#[cfg(windows)] +fn main() { + if let Err(error) = windows_impl::run() { + eprintln!("vulkan-present-fixture failed: {error}"); + std::process::exit(1); + } +} + +#[cfg_attr(not(windows), allow(dead_code))] +mod pattern { + pub const BARS: [(u8, u8, u8); 8] = [ + (255, 255, 255), + (255, 255, 0), + (0, 255, 255), + (0, 255, 0), + (255, 0, 255), + (255, 0, 0), + (0, 0, 255), + (0, 0, 0), + ]; + + pub fn clear_colour(frame_index: u64) -> [f32; 4] { + let (r, g, b) = BARS[(frame_index % BARS.len() as u64) as usize]; + [r as f32 / 255.0, g as f32 / 255.0, b as f32 / 255.0, 1.0] + } +} + +#[cfg(windows)] +mod cli { + #[derive(Clone, Copy, PartialEq, Eq)] + pub enum FormatChoice { + Bgra, + Rgba, + } + + #[derive(Clone, Copy, PartialEq, Eq)] + pub enum PresentModeChoice { + Fifo, + Mailbox, + Immediate, + } + + #[derive(Clone, Copy, PartialEq, Eq)] + pub enum PresentWaitChoice { + Semaphore, + None, + } + + pub struct Options { + pub frames: Option, + pub width: u32, + pub height: u32, + pub borderless: bool, + pub format: FormatChoice, + pub present_mode: PresentModeChoice, + pub present_wait: PresentWaitChoice, + pub resize_at: Option, + pub resize_width: Option, + pub resize_height: Option, + } + + impl Default for Options { + fn default() -> Self { + Self { + frames: None, + width: 1280, + height: 720, + borderless: false, + format: FormatChoice::Bgra, + present_mode: PresentModeChoice::Fifo, + present_wait: PresentWaitChoice::Semaphore, + resize_at: None, + resize_width: None, + resize_height: None, + } + } + } + + impl Options { + pub fn from_env() -> Self { + let mut options = Options::default(); + let mut args = std::env::args().skip(1); + while let Some(arg) = args.next() { + match arg.as_str() { + "--frames" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.frames = Some(value); + } + } + "--width" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.width = value; + } + } + "--height" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.height = value; + } + } + "--borderless" => options.borderless = true, + "--windowed" => options.borderless = false, + "--format" => { + if let Some(value) = args.next() { + options.format = match value.to_ascii_lowercase().as_str() { + "rgba" => FormatChoice::Rgba, + _ => FormatChoice::Bgra, + }; + } + } + "--present-mode" => { + if let Some(value) = args.next() { + options.present_mode = match value.to_ascii_lowercase().as_str() { + "mailbox" => PresentModeChoice::Mailbox, + "immediate" => PresentModeChoice::Immediate, + _ => PresentModeChoice::Fifo, + }; + } + } + "--present-wait" => { + if let Some(value) = args.next() { + options.present_wait = match value.to_ascii_lowercase().as_str() { + "none" => PresentWaitChoice::None, + _ => PresentWaitChoice::Semaphore, + }; + } + } + "--resize-at" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.resize_at = Some(value); + } + } + "--resize-width" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.resize_width = Some(value); + } + } + "--resize-height" => { + if let Some(value) = args.next().and_then(|v| v.parse().ok()) { + options.resize_height = Some(value); + } + } + _ => {} + } + } + options + } + } +} + +#[cfg(windows)] +mod windows_impl { + use super::cli::{FormatChoice, Options, PresentModeChoice, PresentWaitChoice}; + use super::pattern; + use ash::{khr, vk}; + use std::error::Error; + use windows::Win32::Foundation::{HWND, LPARAM, LRESULT, RECT, WPARAM}; + use windows::Win32::System::LibraryLoader::GetModuleHandleW; + use windows::Win32::UI::WindowsAndMessaging::{ + CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, CreateWindowExW, DefWindowProcW, DispatchMessageW, + GetClientRect, MSG, MoveWindow, PM_REMOVE, PeekMessageW, PostQuitMessage, RegisterClassExW, + SW_SHOW, ShowWindow, TranslateMessage, WM_DESTROY, WM_QUIT, WNDCLASSEXW, + WS_OVERLAPPEDWINDOW, WS_POPUP, WS_VISIBLE, + }; + use windows::core::w; + + type DynError = Box; + + extern "system" fn wnd_proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT { + unsafe { + match msg { + WM_DESTROY => { + PostQuitMessage(0); + LRESULT(0) + } + _ => DefWindowProcW(hwnd, msg, wparam, lparam), + } + } + } + + unsafe fn create_window( + width: u32, + height: u32, + borderless: bool, + ) -> Result<(HWND, isize), DynError> { + unsafe { + let instance = GetModuleHandleW(None)?; + let class_name = w!("FluxerVulkanFixture"); + let wc = WNDCLASSEXW { + cbSize: size_of::() as u32, + style: CS_HREDRAW | CS_VREDRAW, + lpfnWndProc: Some(wnd_proc), + hInstance: instance.into(), + lpszClassName: class_name, + ..Default::default() + }; + RegisterClassExW(&wc); + let style = if borderless { + WS_POPUP | WS_VISIBLE + } else { + WS_OVERLAPPEDWINDOW | WS_VISIBLE + }; + let hwnd = CreateWindowExW( + Default::default(), + class_name, + w!("Fluxer Vulkan Present Fixture"), + style, + CW_USEDEFAULT, + CW_USEDEFAULT, + width as i32, + height as i32, + None, + None, + Some(instance.into()), + None, + )?; + let _ = ShowWindow(hwnd, SW_SHOW); + Ok((hwnd, instance.0 as isize)) + } + } + + unsafe fn client_extent(hwnd: HWND) -> vk::Extent2D { + unsafe { + let mut rect = RECT::default(); + let _ = GetClientRect(hwnd, &mut rect); + vk::Extent2D { + width: (rect.right - rect.left).max(1) as u32, + height: (rect.bottom - rect.top).max(1) as u32, + } + } + } + + unsafe fn resize_window(hwnd: HWND, width: u32, height: u32) { + unsafe { + let _ = MoveWindow(hwnd, 0, 0, width as i32, height as i32, true); + } + } + + fn pick_surface_format( + formats: &[vk::SurfaceFormatKHR], + choice: FormatChoice, + ) -> Result { + let preferred: [vk::Format; 2] = match choice { + FormatChoice::Bgra => [vk::Format::B8G8R8A8_UNORM, vk::Format::B8G8R8A8_SRGB], + FormatChoice::Rgba => [vk::Format::R8G8B8A8_UNORM, vk::Format::R8G8B8A8_SRGB], + }; + let any_8bit = [ + vk::Format::B8G8R8A8_UNORM, + vk::Format::R8G8B8A8_UNORM, + vk::Format::B8G8R8A8_SRGB, + vk::Format::R8G8B8A8_SRGB, + ]; + formats + .iter() + .copied() + .find(|f| preferred.contains(&f.format)) + .or_else(|| { + formats + .iter() + .copied() + .find(|f| any_8bit.contains(&f.format)) + }) + .or_else(|| formats.first().copied()) + .ok_or_else(|| "surface advertised no formats".into()) + } + + fn pick_present_mode( + available: &[vk::PresentModeKHR], + choice: PresentModeChoice, + ) -> vk::PresentModeKHR { + let wanted = match choice { + PresentModeChoice::Fifo => vk::PresentModeKHR::FIFO, + PresentModeChoice::Mailbox => vk::PresentModeKHR::MAILBOX, + PresentModeChoice::Immediate => vk::PresentModeKHR::IMMEDIATE, + }; + if available.contains(&wanted) { + wanted + } else { + vk::PresentModeKHR::FIFO + } + } + + struct Swapchain { + handle: vk::SwapchainKHR, + images: Vec, + command_buffers: Vec, + extent: vk::Extent2D, + format: vk::Format, + } + + impl Swapchain { + #[allow(clippy::too_many_arguments)] + unsafe fn create( + device: &ash::Device, + swapchain_device: &khr::swapchain::Device, + surface_instance: &khr::surface::Instance, + physical_device: vk::PhysicalDevice, + surface: vk::SurfaceKHR, + command_pool: vk::CommandPool, + surface_format: vk::SurfaceFormatKHR, + present_mode: vk::PresentModeKHR, + requested_extent: vk::Extent2D, + old: vk::SwapchainKHR, + ) -> Result { + unsafe { + let surface_caps = surface_instance + .get_physical_device_surface_capabilities(physical_device, surface)?; + + let extent = if surface_caps.current_extent.width != u32::MAX { + surface_caps.current_extent + } else { + vk::Extent2D { + width: requested_extent.width.clamp( + surface_caps.min_image_extent.width, + surface_caps.max_image_extent.width, + ), + height: requested_extent.height.clamp( + surface_caps.min_image_extent.height, + surface_caps.max_image_extent.height, + ), + } + }; + + let image_count = + surface_caps + .min_image_count + .max(2) + .min(if surface_caps.max_image_count == 0 { + u32::MAX + } else { + surface_caps.max_image_count + }); + + let swapchain_info = vk::SwapchainCreateInfoKHR::default() + .surface(surface) + .min_image_count(image_count) + .image_format(surface_format.format) + .image_color_space(surface_format.color_space) + .image_extent(extent) + .image_array_layers(1) + .image_usage( + vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::TRANSFER_DST, + ) + .image_sharing_mode(vk::SharingMode::EXCLUSIVE) + .pre_transform(surface_caps.current_transform) + .composite_alpha(vk::CompositeAlphaFlagsKHR::OPAQUE) + .present_mode(present_mode) + .clipped(true) + .old_swapchain(old); + let handle = swapchain_device.create_swapchain(&swapchain_info, None)?; + let images = swapchain_device.get_swapchain_images(handle)?; + + let alloc_info = vk::CommandBufferAllocateInfo::default() + .command_pool(command_pool) + .level(vk::CommandBufferLevel::PRIMARY) + .command_buffer_count(images.len() as u32); + let command_buffers = device.allocate_command_buffers(&alloc_info)?; + + Ok(Self { + handle, + images, + command_buffers, + extent, + format: surface_format.format, + }) + } + } + + unsafe fn destroy( + self, + device: &ash::Device, + swapchain_device: &khr::swapchain::Device, + command_pool: vk::CommandPool, + ) { + unsafe { + if !self.command_buffers.is_empty() { + device.free_command_buffers(command_pool, &self.command_buffers); + } + swapchain_device.destroy_swapchain(self.handle, None); + } + } + } + + pub fn run() -> Result<(), DynError> { + let options = Options::from_env(); + unsafe { + let (hwnd, hinstance) = + create_window(options.width, options.height, options.borderless)?; + + println!("HWND={}", hwnd.0 as isize); + use std::io::Write; + let _ = std::io::stdout().flush(); + + let entry = ash::Entry::load()?; + + let app_name = c"fluxer-vulkan-present-fixture"; + let app_info = vk::ApplicationInfo::default() + .application_name(app_name) + .api_version(vk::API_VERSION_1_1); + let instance_extensions = [ + khr::surface::NAME.as_ptr(), + khr::win32_surface::NAME.as_ptr(), + ]; + let instance_info = vk::InstanceCreateInfo::default() + .application_info(&app_info) + .enabled_extension_names(&instance_extensions); + let instance = entry.create_instance(&instance_info, None)?; + + let surface_instance = khr::surface::Instance::new(&entry, &instance); + let win32_surface = khr::win32_surface::Instance::new(&entry, &instance); + + let surface_info = vk::Win32SurfaceCreateInfoKHR::default() + .hinstance(hinstance) + .hwnd(hwnd.0 as isize); + let surface = win32_surface.create_win32_surface(&surface_info, None)?; + + let physical_devices = instance.enumerate_physical_devices()?; + let (physical_device, queue_family_index) = physical_devices + .iter() + .find_map(|&physical_device| { + let families = + instance.get_physical_device_queue_family_properties(physical_device); + families.iter().enumerate().find_map(|(index, family)| { + let index = index as u32; + let graphics = family.queue_flags.contains(vk::QueueFlags::GRAPHICS); + let present = surface_instance + .get_physical_device_surface_support(physical_device, index, surface) + .unwrap_or(false); + (graphics && present).then_some((physical_device, index)) + }) + }) + .ok_or("no Vulkan device with a graphics+present queue family")?; + + let queue_priorities = [1.0_f32]; + let queue_info = vk::DeviceQueueCreateInfo::default() + .queue_family_index(queue_family_index) + .queue_priorities(&queue_priorities); + let device_extensions = [khr::swapchain::NAME.as_ptr()]; + let queue_infos = [queue_info]; + let device_info = vk::DeviceCreateInfo::default() + .queue_create_infos(&queue_infos) + .enabled_extension_names(&device_extensions); + let device = instance.create_device(physical_device, &device_info, None)?; + let queue = device.get_device_queue(queue_family_index, 0); + let swapchain_device = khr::swapchain::Device::new(&instance, &device); + + let formats = + surface_instance.get_physical_device_surface_formats(physical_device, surface)?; + let surface_format = pick_surface_format(&formats, options.format)?; + let available_present_modes = surface_instance + .get_physical_device_surface_present_modes(physical_device, surface)?; + let present_mode = pick_present_mode(&available_present_modes, options.present_mode); + eprintln!( + "vulkan-present-fixture: vkFormat={} vkColorSpace={} vkPresentMode={}", + surface_format.format.as_raw(), + surface_format.color_space.as_raw(), + present_mode.as_raw() + ); + + let pool_info = vk::CommandPoolCreateInfo::default() + .queue_family_index(queue_family_index) + .flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER); + let command_pool = device.create_command_pool(&pool_info, None)?; + + let mut swapchain = Swapchain::create( + &device, + &swapchain_device, + &surface_instance, + physical_device, + surface, + command_pool, + surface_format, + present_mode, + client_extent(hwnd), + vk::SwapchainKHR::null(), + )?; + + let image_available = + device.create_semaphore(&vk::SemaphoreCreateInfo::default(), None)?; + let render_finished = + device.create_semaphore(&vk::SemaphoreCreateInfo::default(), None)?; + let in_flight = device.create_fence( + &vk::FenceCreateInfo::default().flags(vk::FenceCreateFlags::SIGNALED), + None, + )?; + + let subresource_range = vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, + level_count: 1, + base_array_layer: 0, + layer_count: 1, + }; + + let recreate_swapchain = |swapchain: &mut Swapchain| -> Result<(), DynError> { + device.device_wait_idle()?; + let new = Swapchain::create( + &device, + &swapchain_device, + &surface_instance, + physical_device, + surface, + command_pool, + surface_format, + present_mode, + client_extent(hwnd), + swapchain.handle, + )?; + eprintln!( + "vulkan-present-fixture: swapchain recreated extent={}x{} vkFormat={}", + new.extent.width, + new.extent.height, + new.format.as_raw() + ); + let old = std::mem::replace(swapchain, new); + old.destroy(&device, &swapchain_device, command_pool); + Ok(()) + }; + + let mut frame_index: u64 = 0; + let mut resized = false; + let mut msg = MSG::default(); + 'render: loop { + while PeekMessageW(&mut msg, None, 0, 0, PM_REMOVE).as_bool() { + if msg.message == WM_QUIT { + break 'render; + } + let _ = TranslateMessage(&msg); + DispatchMessageW(&msg); + } + + if let Some(at) = options.resize_at { + if !resized && frame_index >= at { + if let (Some(width), Some(height)) = + (options.resize_width, options.resize_height) + { + resize_window(hwnd, width, height); + } + recreate_swapchain(&mut swapchain)?; + resized = true; + } + } + + device.wait_for_fences(&[in_flight], true, u64::MAX)?; + + let (image_index, suboptimal) = match swapchain_device.acquire_next_image( + swapchain.handle, + u64::MAX, + image_available, + vk::Fence::null(), + ) { + Ok(value) => value, + Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => { + recreate_swapchain(&mut swapchain)?; + continue; + } + Err(error) => return Err(error.into()), + }; + + device.reset_fences(&[in_flight])?; + + let command_buffer = swapchain.command_buffers[image_index as usize]; + let image = swapchain.images[image_index as usize]; + device + .reset_command_buffer(command_buffer, vk::CommandBufferResetFlags::empty())?; + device.begin_command_buffer( + command_buffer, + &vk::CommandBufferBeginInfo::default() + .flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT), + )?; + + let to_transfer = vk::ImageMemoryBarrier::default() + .src_access_mask(vk::AccessFlags::empty()) + .dst_access_mask(vk::AccessFlags::TRANSFER_WRITE) + .old_layout(vk::ImageLayout::UNDEFINED) + .new_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL) + .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED) + .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED) + .image(image) + .subresource_range(subresource_range); + device.cmd_pipeline_barrier( + command_buffer, + vk::PipelineStageFlags::TOP_OF_PIPE, + vk::PipelineStageFlags::TRANSFER, + vk::DependencyFlags::empty(), + &[], + &[], + &[to_transfer], + ); + + let clear = pattern::clear_colour(frame_index); + let clear_value = vk::ClearColorValue { float32: clear }; + device.cmd_clear_color_image( + command_buffer, + image, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + &clear_value, + &[subresource_range], + ); + + let to_present = vk::ImageMemoryBarrier::default() + .src_access_mask(vk::AccessFlags::TRANSFER_WRITE) + .dst_access_mask(vk::AccessFlags::empty()) + .old_layout(vk::ImageLayout::TRANSFER_DST_OPTIMAL) + .new_layout(vk::ImageLayout::PRESENT_SRC_KHR) + .src_queue_family_index(vk::QUEUE_FAMILY_IGNORED) + .dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED) + .image(image) + .subresource_range(subresource_range); + device.cmd_pipeline_barrier( + command_buffer, + vk::PipelineStageFlags::TRANSFER, + vk::PipelineStageFlags::BOTTOM_OF_PIPE, + vk::DependencyFlags::empty(), + &[], + &[], + &[to_present], + ); + device.end_command_buffer(command_buffer)?; + + let wait_semaphores = [image_available]; + let wait_stages = [vk::PipelineStageFlags::TRANSFER]; + let command_buffers = [command_buffer]; + let mut submit = vk::SubmitInfo::default() + .wait_semaphores(&wait_semaphores) + .wait_dst_stage_mask(&wait_stages) + .command_buffers(&command_buffers); + let signal_semaphores = [render_finished]; + if options.present_wait == PresentWaitChoice::Semaphore { + submit = submit.signal_semaphores(&signal_semaphores); + } + device.queue_submit(queue, &[submit], in_flight)?; + + let swapchains = [swapchain.handle]; + let image_indices = [image_index]; + if options.present_wait == PresentWaitChoice::None { + device.wait_for_fences(&[in_flight], true, u64::MAX)?; + } + let mut present_info = vk::PresentInfoKHR::default() + .swapchains(&swapchains) + .image_indices(&image_indices); + if options.present_wait == PresentWaitChoice::Semaphore { + present_info = present_info.wait_semaphores(&signal_semaphores); + } + let present_result = swapchain_device.queue_present(queue, &present_info); + let needs_recreate = match present_result { + Ok(present_suboptimal) => present_suboptimal || suboptimal, + Err(vk::Result::ERROR_OUT_OF_DATE_KHR) => true, + Err(error) => return Err(error.into()), + }; + if needs_recreate { + recreate_swapchain(&mut swapchain)?; + } + + frame_index += 1; + if let Some(limit) = options.frames { + if frame_index >= limit { + break 'render; + } + } + } + + let _ = device.device_wait_idle(); + device.destroy_fence(in_flight, None); + device.destroy_semaphore(render_finished, None); + device.destroy_semaphore(image_available, None); + swapchain.destroy(&device, &swapchain_device, command_pool); + device.destroy_command_pool(command_pool, None); + surface_instance.destroy_surface(surface, None); + device.destroy_device(None); + instance.destroy_instance(None); + Ok(()) + } + } +} diff --git a/fluxer_desktop/native/win-game-capture/tests/windows_encoder_attach.rs b/fluxer_desktop/native/win-game-capture/tests/windows_encoder_attach.rs new file mode 100644 index 0000000..dd79983 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/tests/windows_encoder_attach.rs @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![cfg(target_os = "windows")] +use fluxer_win_game_capture::{EncoderAttachError, EncoderAttachment}; +use windows::Win32::Graphics::Direct3D::{D3D_DRIVER_TYPE_HARDWARE, D3D_FEATURE_LEVEL_11_0}; +use windows::Win32::Graphics::Direct3D11::{ + D3D11_BIND_RENDER_TARGET, D3D11_BIND_SHADER_RESOURCE, D3D11_CREATE_DEVICE_BGRA_SUPPORT, + D3D11_CREATE_DEVICE_VIDEO_SUPPORT, D3D11_RESOURCE_MISC_SHARED, D3D11_SDK_VERSION, + D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, D3D11CreateDevice, ID3D11Device, + ID3D11DeviceContext, ID3D11Multithread, ID3D11Texture2D, +}; +use windows::Win32::Graphics::Dxgi::Common::{DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_SAMPLE_DESC}; +use windows::core::Interface; + +fn try_create_device() -> Option<(ID3D11Device, ID3D11DeviceContext)> { + let feature_levels = [D3D_FEATURE_LEVEL_11_0]; + let flags = D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_VIDEO_SUPPORT; + let mut device: Option = None; + let mut context: Option = None; + let result = unsafe { + D3D11CreateDevice( + None, + D3D_DRIVER_TYPE_HARDWARE, + Default::default(), + flags, + Some(&feature_levels), + D3D11_SDK_VERSION, + Some(&mut device), + None, + Some(&mut context), + ) + }; + if result.is_err() { + return None; + } + let dev = device?; + let ctx = context?; + if let Ok(mt) = dev.cast::() { + let _ = unsafe { mt.SetMultithreadProtected(true) }; + } + Some((dev, ctx)) +} + +fn create_bgra_capture_texture( + device: &ID3D11Device, + width: u32, + height: u32, +) -> Option { + let desc = D3D11_TEXTURE2D_DESC { + Width: width, + Height: height, + MipLevels: 1, + ArraySize: 1, + Format: DXGI_FORMAT_B8G8R8A8_UNORM, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Usage: D3D11_USAGE_DEFAULT, + BindFlags: (D3D11_BIND_RENDER_TARGET.0 | D3D11_BIND_SHADER_RESOURCE.0) as u32, + CPUAccessFlags: 0, + MiscFlags: D3D11_RESOURCE_MISC_SHARED.0 as u32, + }; + let mut tex = None; + unsafe { device.CreateTexture2D(&desc, None, Some(&mut tex)) }.ok()?; + tex +} + +#[test] +fn windows_attach_construct_and_detach() { + let _device = match try_create_device() { + Some(d) => d, + None => { + eprintln!("skip: no D3D11 device available"); + return; + } + }; + let attach = match EncoderAttachment::try_new(640, 480) { + Ok(a) => a, + Err(e) => { + eprintln!("skip: EncoderAttachment init failed: {e:?}"); + return; + } + }; + assert!(attach.is_attached(), "attached after construction"); + assert_eq!(attach.width(), 640, "width preserved"); + assert_eq!(attach.height(), 480, "height preserved"); + assert_eq!(attach.capacity(), 8, "capacity matches RING_SIZE"); + let stats = attach.stats(); + assert_eq!(stats.frames_submitted, 0, "fresh attach: no submissions"); + assert_eq!(stats.failed_blits, 0, "fresh attach: no failed blits"); + attach.detach(); + assert!(!attach.is_attached(), "post-detach not attached"); +} + +#[test] +fn submit_capture_frame_with_blit_records_stats() { + let (device, _context) = match try_create_device() { + Some(d) => d, + None => { + eprintln!("skip: no D3D11 device available"); + return; + } + }; + let attach = match EncoderAttachment::try_new(640, 480) { + Ok(a) => a, + Err(e) => { + eprintln!("skip: attach init failed: {e:?}"); + return; + } + }; + let capture = match create_bgra_capture_texture(&device, 640, 480) { + Some(t) => t, + None => { + eprintln!("skip: capture texture create failed"); + return; + } + }; + let result = attach.submit_capture_frame_with_blit(&capture, 640, 480); + let stats = attach.stats(); + match result { + Ok(()) => { + assert!( + stats.frames_submitted == 1, + "expected 1 submission, got {}", + stats.frames_submitted + ); + assert!( + stats.failed_blits == 0, + "expected 0 failed blits, got {}", + stats.failed_blits + ); + } + Err(EncoderAttachError::BlitFailed) => { + assert!( + stats.failed_blits >= 1, + "expected >=1 failed blits, got {}", + stats.failed_blits + ); + } + Err(EncoderAttachError::DeviceUnavailable) => { + eprintln!("device unavailable from ring backend (driver constraint)"); + } + Err(other) => { + eprintln!("submit_capture_frame_with_blit unexpected error: {other:?}"); + } + } +} + +#[test] +fn submit_notify_back_pressure_increments_drops_after_capacity() { + let _device = match try_create_device() { + Some(d) => d, + None => { + eprintln!("skip: no D3D11 device available"); + return; + } + }; + let attach = match EncoderAttachment::try_new(64, 64) { + Ok(a) => a, + Err(e) => { + eprintln!("skip: attach init failed: {e:?}"); + return; + } + }; + let total = (attach.capacity() as u64) * 2; + for _ in 0..total { + attach.submit_notify().expect("notify ok"); + } + let stats = attach.stats(); + assert!( + stats.frames_submitted >= attach.capacity() as u64, + "many submissions accepted" + ); + assert!(stats.frames_dropped > 0, "back pressure drops oldest"); + assert_eq!( + stats.ring_full_events, stats.frames_dropped, + "drops==ring_full_events" + ); +} diff --git a/fluxer_desktop/native/win-game-capture/vulkan-layer/Cargo.lock b/fluxer_desktop/native/win-game-capture/vulkan-layer/Cargo.lock new file mode 100644 index 0000000..3cee8be --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/vulkan-layer/Cargo.lock @@ -0,0 +1,277 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "ash" +version = "0.38.0+1.3.281" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" + +[[package]] +name = "ash-layer" +version = "0.0.2+v0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aedca11308198ffa300ad1a981c10ee176e44c8ff6ee7c6883a062608b9779a5" +dependencies = [ + "ash", +] + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "dashmap" +version = "6.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6361d5c062261c78a176addb82d4c821ae42bed6089de0e12603cd25de2059c" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + +[[package]] +name = "fluxer_vulkan_layer" +version = "0.0.0" +dependencies = [ + "ash", + "ash-layer", + "dashmap", + "once_cell", + "windows", + "windows-sys", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] diff --git a/fluxer_desktop/native/win-game-capture/vulkan-layer/Cargo.toml b/fluxer_desktop/native/win-game-capture/vulkan-layer/Cargo.toml new file mode 100644 index 0000000..1fda90b --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/vulkan-layer/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "fluxer_vulkan_layer" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib"] + +[target.'cfg(target_os = "windows")'.dependencies] +ash = {version = "0.38.0", default-features = false, features = ["std", "debug"]} +ash-layer = "0.0.2" +dashmap = "6.2.1" +once_cell = "1.21.4" +windows = {version = "0.62.2", features = [ + "Win32_Foundation", + "Win32_Graphics_Direct3D", + "Win32_Graphics_Direct3D11", + "Win32_Graphics_Dxgi", + "Win32_Graphics_Dxgi_Common", +]} +windows-sys = {version = "0.61.2", features = [ + "Win32_Foundation", + "Win32_System_Memory", + "Win32_System_Performance", + "Win32_System_Threading", +]} diff --git a/fluxer_desktop/native/win-game-capture/vulkan-layer/src/d3d11_interop.rs b/fluxer_desktop/native/win-game-capture/vulkan-layer/src/d3d11_interop.rs new file mode 100644 index 0000000..d187361 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/vulkan-layer/src/d3d11_interop.rs @@ -0,0 +1,138 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use ash::vk; +use windows::Win32::Graphics::{ + Direct3D::{D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_UNKNOWN, D3D_DRIVER_TYPE_WARP}, + Direct3D11::{ + D3D11_BIND_RENDER_TARGET, D3D11_BIND_SHADER_RESOURCE, D3D11_CREATE_DEVICE_BGRA_SUPPORT, + D3D11_RESOURCE_MISC_SHARED, D3D11_SDK_VERSION, D3D11_TEXTURE2D_DESC, D3D11_USAGE_DEFAULT, + D3D11CreateDevice, ID3D11Device, ID3D11DeviceContext, ID3D11Texture2D, + }, + Dxgi::{ + Common::{ + DXGI_FORMAT, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, + DXGI_FORMAT_R8G8B8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, + DXGI_FORMAT_R10G10B10A2_UNORM, DXGI_FORMAT_R16G16B16A16_FLOAT, DXGI_SAMPLE_DESC, + }, + IDXGIResource, + }, +}; +use windows::core::Interface; + +use crate::game_capture_abi::{GAME_CAPTURE_FLAG_HDR, GAME_CAPTURE_FLAG_TEN_BIT}; + +#[derive(Clone, Copy)] +pub struct InteropFormat { + pub vk_format: vk::Format, + pub dxgi_format: DXGI_FORMAT, + pub capture_flags: u32, +} + +pub fn interop_format(format: vk::Format) -> Option { + let (dxgi_format, capture_flags) = match format { + vk::Format::B8G8R8A8_UNORM => (DXGI_FORMAT_B8G8R8A8_UNORM, 0), + vk::Format::B8G8R8A8_SRGB => (DXGI_FORMAT_B8G8R8A8_UNORM_SRGB, 0), + vk::Format::R8G8B8A8_UNORM => (DXGI_FORMAT_R8G8B8A8_UNORM, 0), + vk::Format::R8G8B8A8_SRGB => (DXGI_FORMAT_R8G8B8A8_UNORM_SRGB, 0), + vk::Format::A2B10G10R10_UNORM_PACK32 => ( + DXGI_FORMAT_R10G10B10A2_UNORM, + GAME_CAPTURE_FLAG_TEN_BIT | GAME_CAPTURE_FLAG_HDR, + ), + vk::Format::R16G16B16A16_SFLOAT => (DXGI_FORMAT_R16G16B16A16_FLOAT, GAME_CAPTURE_FLAG_HDR), + _ => return None, + }; + Some(InteropFormat { + vk_format: format, + dxgi_format, + capture_flags, + }) +} + +pub struct D3d11Device { + device: ID3D11Device, + _context: ID3D11DeviceContext, +} + +unsafe impl Send for D3d11Device {} +unsafe impl Sync for D3d11Device {} + +pub struct SharedTexture { + _texture: ID3D11Texture2D, + pub handle: u64, +} + +unsafe impl Send for SharedTexture {} +unsafe impl Sync for SharedTexture {} + +impl D3d11Device { + pub fn create() -> Option { + for driver in [D3D_DRIVER_TYPE_HARDWARE, D3D_DRIVER_TYPE_WARP] { + let mut device: Option = None; + let mut context: Option = None; + let result = unsafe { + D3D11CreateDevice( + None, + driver, + Default::default(), + D3D11_CREATE_DEVICE_BGRA_SUPPORT, + None, + D3D11_SDK_VERSION, + Some(&mut device), + None, + Some(&mut context), + ) + }; + if result.is_ok() + && let (Some(device), Some(context)) = (device, context) + { + return Some(Self { + device, + _context: context, + }); + } + } + let _ = D3D_DRIVER_TYPE_UNKNOWN; + None + } + + pub fn create_shared_texture( + &self, + width: u32, + height: u32, + format: InteropFormat, + ) -> Option { + if width == 0 || height == 0 { + return None; + } + let desc = D3D11_TEXTURE2D_DESC { + Width: width, + Height: height, + MipLevels: 1, + ArraySize: 1, + Format: format.dxgi_format, + SampleDesc: DXGI_SAMPLE_DESC { + Count: 1, + Quality: 0, + }, + Usage: D3D11_USAGE_DEFAULT, + BindFlags: (D3D11_BIND_SHADER_RESOURCE.0 | D3D11_BIND_RENDER_TARGET.0) as u32, + CPUAccessFlags: 0, + MiscFlags: D3D11_RESOURCE_MISC_SHARED.0 as u32, + }; + let mut texture: Option = None; + let result = unsafe { self.device.CreateTexture2D(&desc, None, Some(&mut texture)) }; + if result.is_err() { + return None; + } + let texture = texture?; + let resource: IDXGIResource = texture.cast().ok()?; + let handle = unsafe { resource.GetSharedHandle() }.ok()?; + if handle.is_invalid() { + return None; + } + Some(SharedTexture { + _texture: texture, + handle: handle.0 as usize as u64, + }) + } +} diff --git a/fluxer_desktop/native/win-game-capture/vulkan-layer/src/lib.rs b/fluxer_desktop/native/win-game-capture/vulkan-layer/src/lib.rs new file mode 100644 index 0000000..cbfcb00 --- /dev/null +++ b/fluxer_desktop/native/win-game-capture/vulkan-layer/src/lib.rs @@ -0,0 +1,2631 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +#![cfg(target_os = "windows")] +#![allow(non_snake_case)] +#![allow(unsafe_op_in_unsafe_fn)] + +#[path = "../../src/game_capture_abi.rs"] +mod game_capture_abi; + +mod d3d11_interop; + +use ash::{khr, vk}; +use ash_layer::{ + LayerFunction, NegotiateLayerInterface, PFN_vk_layerGetPhysicalDeviceProcAddr, + PFN_vkNegotiateLoaderLayerInterfaceVersion, get_device_chain_info, get_instance_chain_info, +}; +use d3d11_interop::{D3d11Device, InteropFormat, SharedTexture, interop_format}; +use dashmap::DashMap; +use game_capture_abi::{ + ENV_FORCE_CPU, ENV_FORCE_SHARED_TEXTURE, GAME_CAPTURE_API_VULKAN, GAME_CAPTURE_BUFFER_COUNT, + GAME_CAPTURE_FALLBACK_EXTERNAL_MEMORY_UNSUPPORTED, GAME_CAPTURE_FALLBACK_FORCED_CPU, + GAME_CAPTURE_FALLBACK_FORMAT_UNSUPPORTED, GAME_CAPTURE_FALLBACK_NONE, GAME_CAPTURE_FLAG_HDR, + GAME_CAPTURE_FLAG_TEN_BIT, GAME_CAPTURE_FRAME_PREFIX, GAME_CAPTURE_INFO_PREFIX, + GAME_CAPTURE_KEEPALIVE_PREFIX, GAME_CAPTURE_MAGIC, GAME_CAPTURE_PRESENT_CLOCK_QPC, + GAME_CAPTURE_READY_PREFIX, GAME_CAPTURE_STATE_ACTIVE, GAME_CAPTURE_STATE_ERROR, + GAME_CAPTURE_STATE_RESIZE_REQUIRED, GAME_CAPTURE_STATE_STOPPED, GAME_CAPTURE_STOP_PREFIX, + GAME_CAPTURE_TRANSPORT_MEMORY, GAME_CAPTURE_TRANSPORT_SHARED_TEXTURE, GameCaptureSharedInfo, + env_flag_enabled, frame_buffer_size, host_supports_present_clock, mutex_name, object_name, + qpc_now_us, +}; +use once_cell::sync::{Lazy, OnceCell}; +use std::{ + ffi::{CStr, c_char}, + mem, + ptr::{null, null_mut}, + sync::Mutex, +}; +use windows_sys::Win32::{ + Foundation::{CloseHandle, HANDLE, INVALID_HANDLE_VALUE, WAIT_ABANDONED, WAIT_OBJECT_0}, + System::{ + Memory::{ + FILE_MAP_ALL_ACCESS, MEMORY_MAPPED_VIEW_ADDRESS, MapViewOfFile, OpenFileMappingW, + UnmapViewOfFile, + }, + Threading::{ + EVENT_ALL_ACCESS, MUTEX_ALL_ACCESS, OpenEventW, OpenMutexW, ReleaseMutex, + SYNCHRONIZATION_SYNCHRONIZE, SetEvent, WaitForSingleObject, + }, + }, +}; + +const LAYER_NAME: &[u8] = b"VK_LAYER_FLUXER_game_capture\0"; +const FRAME_MUTEX_WAIT_MS: u32 = 0; +const CAPTURE_FENCE_TIMEOUT_NS: u64 = 250_000_000; + +const DEVICE_EXT_EXTERNAL_MEMORY: &[u8] = b"VK_KHR_external_memory\0"; +const DEVICE_EXT_EXTERNAL_MEMORY_WIN32: &[u8] = b"VK_KHR_external_memory_win32\0"; +const DEVICE_EXT_DEDICATED_ALLOCATION: &[u8] = b"VK_KHR_dedicated_allocation\0"; +const DEVICE_EXT_GET_MEMORY_REQUIREMENTS2: &[u8] = b"VK_KHR_get_memory_requirements2\0"; + +const INSTANCE_EXT_PHYSICAL_DEVICE_PROPERTIES2: &[u8] = b"VK_KHR_get_physical_device_properties2\0"; +const INSTANCE_EXT_EXTERNAL_MEMORY_CAPABILITIES: &[u8] = b"VK_KHR_external_memory_capabilities\0"; + +const SHARED_HANDLE_TYPE: vk::ExternalMemoryHandleTypeFlags = + vk::ExternalMemoryHandleTypeFlags::D3D11_TEXTURE_KMT; + +static VERBOSE: Lazy = + Lazy::new(|| game_capture_abi::env_flag_enabled(game_capture_abi::ENV_VERBOSE)); + +macro_rules! vlog { + ($($arg:tt)*) => { + if *VERBOSE { + eprintln!("[fluxer-vk-layer] {}", format!($($arg)*)); + } + }; +} + +static GIPA: OnceCell = OnceCell::new(); +static GPHYPA: OnceCell = OnceCell::new(); +static ENTRY: OnceCell = OnceCell::new(); + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +enum CaptureMode { + Auto, + ForceCpu, + ForceSharedTexture, +} + +fn capture_mode() -> CaptureMode { + static MODE: OnceCell = OnceCell::new(); + let value = *MODE.get_or_init(|| { + if env_flag_enabled(ENV_FORCE_CPU) { + 0 + } else if env_flag_enabled(ENV_FORCE_SHARED_TEXTURE) { + 2 + } else { + 1 + } + }); + match value { + 0 => CaptureMode::ForceCpu, + 2 => CaptureMode::ForceSharedTexture, + _ => CaptureMode::Auto, + } +} + +static INSTANCE_MAP: Lazy> = Lazy::new(DashMap::new); +static PHY_TO_INSTANCE_MAP: Lazy> = + Lazy::new(DashMap::new); +static GDPA_MAP: Lazy> = Lazy::new(DashMap::new); +static DEVICE_MAP: Lazy> = Lazy::new(DashMap::new); +static SURFACE_MAP: Lazy> = Lazy::new(DashMap::new); +static SWAPCHAIN_MAP: Lazy> = Lazy::new(DashMap::new); +static QUEUE_MAP: Lazy> = Lazy::new(DashMap::new); +static IPC_STATE: Lazy>> = Lazy::new(|| Mutex::new(None)); +static D3D11_DEVICE: Lazy>> = Lazy::new(|| Mutex::new(None)); +static FAST_PATH_DISABLED: Lazy> = Lazy::new(|| Mutex::new(false)); + +#[derive(Clone)] +struct LayerInstance { + ash_instance: ash::Instance, + khr_surface: khr::surface::Instance, + khr_win32_surface: khr::win32_surface::Instance, +} + +#[derive(Clone)] +struct LayerDevice { + instance: vk::Instance, + physical_device: vk::PhysicalDevice, + ash_device: ash::Device, + khr_swapchain: khr::swapchain::Device, + queue_families: Vec, + khr_external_memory_win32: Option, + external_memory_enabled: bool, + dedicated_allocation_enabled: bool, +} + +#[derive(Clone, Copy)] +struct SurfaceState { + instance: vk::Instance, + hwnd: vk::HWND, +} + +#[derive(Clone)] +struct SwapchainState { + device: vk::Device, + hwnd: vk::HWND, + extent: vk::Extent2D, + format: vk::Format, + images: Vec, + can_capture: bool, + present_layout: vk::ImageLayout, + shared_texture_allowed: bool, +} + +struct QueueState { + device: vk::Device, + family_index: u32, + supports_transfer: bool, + resources: Mutex>, + shared: Mutex>, +} + +struct ReadbackResources { + width: u32, + height: u32, + format_mode: FormatMode, + buffer: vk::Buffer, + memory: vk::DeviceMemory, + command_pool: vk::CommandPool, + command_buffer: vk::CommandBuffer, + fence: vk::Fence, + present_wait_semaphore: vk::Semaphore, + size: vk::DeviceSize, +} + +struct SharedTextureResources { + width: u32, + height: u32, + format: InteropFormat, + shared_texture: SharedTexture, + image: vk::Image, + memory: vk::DeviceMemory, + command_pool: vk::CommandPool, + command_buffer: vk::CommandBuffer, + fence: vk::Fence, + present_wait_semaphore: vk::Semaphore, + initialised: bool, +} + +#[derive(Clone, Copy, PartialEq, Eq, Debug)] +enum FormatMode { + Bgra, + Rgba, +} + +struct CaptureSubmit { + chained_wait: Option, +} + +impl CaptureSubmit { + fn none() -> Self { + Self { chained_wait: None } + } +} + +struct IpcState { + info_map: HANDLE, + frame_map: HANDLE, + ready_event: HANDLE, + stop_event: HANDLE, + mutexes: [HANDLE; GAME_CAPTURE_BUFFER_COUNT], + info: *mut GameCaptureSharedInfo, + frame_base: *mut u8, + frame_buffer_capacity: usize, + next_frame_index: usize, + last_frame_ns: u64, + keepalive_name: Vec, +} + +unsafe impl Send for IpcState {} + +fn wide(value: &str) -> Vec { + value.encode_utf16().chain(std::iter::once(0)).collect() +} + +struct AugmentedExtensions { + pointers: Vec<*const c_char>, +} + +impl AugmentedExtensions { + unsafe fn build( + existing: *const *const c_char, + existing_count: u32, + extra: &[&'static [u8]], + ) -> Self { + let mut pointers: Vec<*const c_char> = Vec::new(); + let mut present: Vec<&CStr> = Vec::new(); + if !existing.is_null() { + let slice = std::slice::from_raw_parts(existing, existing_count as usize); + for &ptr in slice { + pointers.push(ptr); + if !ptr.is_null() { + present.push(CStr::from_ptr(ptr)); + } + } + } + for &name in extra { + let cstr = CStr::from_bytes_with_nul_unchecked(name); + if !present.contains(&cstr) { + pointers.push(cstr.as_ptr()); + } + } + Self { pointers } + } + + fn count(&self) -> u32 { + self.pointers.len() as u32 + } + + fn as_ptr(&self) -> *const *const c_char { + self.pointers.as_ptr() + } +} + +unsafe fn extension_in_list(list: *const *const c_char, count: u32, name: &'static [u8]) -> bool { + if list.is_null() { + return false; + } + let target = CStr::from_bytes_with_nul_unchecked(name); + std::slice::from_raw_parts(list, count as usize) + .iter() + .any(|&ptr| !ptr.is_null() && CStr::from_ptr(ptr) == target) +} + +fn current_pid() -> u32 { + std::process::id() +} + +fn now_ns() -> u64 { + use std::time::{SystemTime, UNIX_EPOCH}; + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_nanos() as u64) + .unwrap_or(0) +} + +fn now_us() -> i64 { + (now_ns() / 1_000) as i64 +} + +unsafe fn close_handle(handle: &mut HANDLE) { + if !handle.is_null() && *handle != INVALID_HANDLE_VALUE { + CloseHandle(*handle); + *handle = null_mut(); + } +} + +unsafe fn open_event(prefix: &str, pid: u32) -> HANDLE { + let name = wide(&object_name(prefix, pid)); + OpenEventW(EVENT_ALL_ACCESS, 0, name.as_ptr()) +} + +unsafe fn open_mutex(name: &str) -> HANDLE { + let name = wide(name); + OpenMutexW(MUTEX_ALL_ACCESS, 0, name.as_ptr()) +} + +unsafe fn parent_alive(keepalive_name: &[u16]) -> bool { + let handle = OpenMutexW(SYNCHRONIZATION_SYNCHRONIZE, 0, keepalive_name.as_ptr()); + if handle.is_null() { + return false; + } + CloseHandle(handle); + true +} + +unsafe fn map_view(handle: HANDLE, size: usize) -> *mut T { + let view = MapViewOfFile(handle, FILE_MAP_ALL_ACCESS, 0, 0, size); + view.Value.cast() +} + +unsafe fn free_ipc_state(state: &mut IpcState) { + if !state.info.is_null() { + UnmapViewOfFile(MEMORY_MAPPED_VIEW_ADDRESS { + Value: state.info.cast(), + }); + state.info = null_mut(); + } + if !state.frame_base.is_null() { + UnmapViewOfFile(MEMORY_MAPPED_VIEW_ADDRESS { + Value: state.frame_base.cast(), + }); + state.frame_base = null_mut(); + } + close_handle(&mut state.mutexes[0]); + close_handle(&mut state.mutexes[1]); + close_handle(&mut state.stop_event); + close_handle(&mut state.ready_event); + close_handle(&mut state.frame_map); + close_handle(&mut state.info_map); +} + +unsafe fn open_ipc() -> Option { + let pid = current_pid(); + let info_name = wide(&object_name(GAME_CAPTURE_INFO_PREFIX, pid)); + let frame_name = wide(&object_name(GAME_CAPTURE_FRAME_PREFIX, pid)); + let info_map = OpenFileMappingW(FILE_MAP_ALL_ACCESS, 0, info_name.as_ptr()); + if info_map.is_null() { + return None; + } + let frame_map = OpenFileMappingW(FILE_MAP_ALL_ACCESS, 0, frame_name.as_ptr()); + if frame_map.is_null() { + let mut handle = info_map; + close_handle(&mut handle); + return None; + } + let info = map_view::(info_map, mem::size_of::()); + if info.is_null() || (*info).magic != GAME_CAPTURE_MAGIC { + let mut info_map = info_map; + let mut frame_map = frame_map; + close_handle(&mut frame_map); + close_handle(&mut info_map); + return None; + } + let frame_buffer_capacity = frame_buffer_size((*info).max_width, (*info).max_height)?; + let frame_map_size = frame_buffer_capacity.checked_mul(GAME_CAPTURE_BUFFER_COUNT)?; + let frame_base = map_view::(frame_map, frame_map_size); + if frame_base.is_null() { + let mut info_map = info_map; + let mut frame_map = frame_map; + close_handle(&mut frame_map); + close_handle(&mut info_map); + return None; + } + + let ready_event = open_event(GAME_CAPTURE_READY_PREFIX, pid); + let stop_event = open_event(GAME_CAPTURE_STOP_PREFIX, pid); + let mutex_names = [mutex_name(pid, 0), mutex_name(pid, 1)]; + let mutexes = [open_mutex(&mutex_names[0]), open_mutex(&mutex_names[1])]; + if ready_event.is_null() + || stop_event.is_null() + || mutexes.iter().any(|handle| handle.is_null()) + { + let mut state = IpcState { + info_map, + frame_map, + ready_event, + stop_event, + mutexes, + info, + frame_base, + frame_buffer_capacity, + next_frame_index: 0, + last_frame_ns: 0, + keepalive_name: wide(&object_name(GAME_CAPTURE_KEEPALIVE_PREFIX, pid)), + }; + free_ipc_state(&mut state); + return None; + } + + Some(IpcState { + info_map, + frame_map, + ready_event, + stop_event, + mutexes, + info, + frame_base, + frame_buffer_capacity, + next_frame_index: 0, + last_frame_ns: 0, + keepalive_name: wide(&object_name(GAME_CAPTURE_KEEPALIVE_PREFIX, pid)), + }) +} + +unsafe fn capture_should_run(state: &mut IpcState) -> bool { + if WaitForSingleObject(state.stop_event, 0) == WAIT_OBJECT_0 { + (*state.info).state = GAME_CAPTURE_STATE_STOPPED; + return false; + } + if !parent_alive(&state.keepalive_name) { + (*state.info).state = GAME_CAPTURE_STATE_STOPPED; + return false; + } + true +} + +unsafe fn frame_due(state: &mut IpcState) -> bool { + let interval = (*state.info).target_frame_interval_ns; + if interval == 0 { + return true; + } + let now = now_ns(); + if now.saturating_sub(state.last_frame_ns) < interval { + return false; + } + state.last_frame_ns = now; + true +} + +unsafe fn target_matches(state: &IpcState, hwnd: vk::HWND) -> bool { + let target = (*state.info).hwnd; + target == 0 || hwnd == 0 || target == hwnd as u64 +} + +unsafe fn write_rows_to_shared_memory( + state: &mut IpcState, + hwnd: vk::HWND, + width: u32, + height: u32, + src_base: *const u8, + src_row_pitch: usize, + format_mode: FormatMode, + fallback_reason: u32, +) -> bool { + if src_base.is_null() { + (*state.info).state = GAME_CAPTURE_STATE_ERROR; + (*state.info).last_error = 60; + return false; + } + let max_width = (*state.info).max_width; + let max_height = (*state.info).max_height; + if width == 0 || height == 0 || width > max_width || height > max_height { + (*state.info).state = GAME_CAPTURE_STATE_RESIZE_REQUIRED; + (*state.info).width = width; + (*state.info).height = height; + return false; + } + let Some(bytes) = frame_buffer_size(width, height) else { + (*state.info).state = GAME_CAPTURE_STATE_ERROR; + (*state.info).last_error = 61; + return false; + }; + if bytes > state.frame_buffer_capacity { + (*state.info).state = GAME_CAPTURE_STATE_RESIZE_REQUIRED; + (*state.info).width = width; + (*state.info).height = height; + return false; + } + + let mut index = state.next_frame_index; + let mut mutex = state.mutexes[index]; + let mut wait = WaitForSingleObject(mutex, FRAME_MUTEX_WAIT_MS); + if wait != WAIT_OBJECT_0 && wait != WAIT_ABANDONED { + index = (index + 1) % GAME_CAPTURE_BUFFER_COUNT; + mutex = state.mutexes[index]; + wait = WaitForSingleObject(mutex, FRAME_MUTEX_WAIT_MS); + } + if wait != WAIT_OBJECT_0 && wait != WAIT_ABANDONED { + return false; + } + + let row_bytes = (width * 4) as usize; + let dst_base = state.frame_base.add(state.frame_buffer_capacity * index); + for y in 0..height as usize { + let src = src_base.add(y * src_row_pitch); + let dst = dst_base.add(y * row_bytes); + match format_mode { + FormatMode::Bgra => std::ptr::copy_nonoverlapping(src, dst, row_bytes), + FormatMode::Rgba => { + for x in 0..width as usize { + let src_px = src.add(x * 4); + let dst_px = dst.add(x * 4); + *dst_px.add(0) = *src_px.add(2); + *dst_px.add(1) = *src_px.add(1); + *dst_px.add(2) = *src_px.add(0); + *dst_px.add(3) = *src_px.add(3); + } + } + } + } + + let info = &mut *state.info; + info.hwnd = hwnd as u64; + info.width = width; + info.height = height; + info.pitch = width * 4; + info.frame_index = index as u32; + info.frame_counter = info.frame_counter.wrapping_add(1); + info.timestamp_us = now_us(); + info.transport = GAME_CAPTURE_TRANSPORT_MEMORY; + info.dxgi_format = 0; + info.texture_handle = 0; + info.api_type = GAME_CAPTURE_API_VULKAN; + info.fallback_reason = fallback_reason; + info.capture_flags = 0; + info.state = GAME_CAPTURE_STATE_ACTIVE; + state.next_frame_index = (index + 1) % GAME_CAPTURE_BUFFER_COUNT; + + ReleaseMutex(mutex); + SetEvent(state.ready_event); + true +} + +fn format_mode(format: vk::Format) -> Option { + match format { + vk::Format::B8G8R8A8_UNORM | vk::Format::B8G8R8A8_SRGB => Some(FormatMode::Bgra), + vk::Format::R8G8B8A8_UNORM | vk::Format::R8G8B8A8_SRGB => Some(FormatMode::Rgba), + _ => None, + } +} + +fn hdr_format_flags(format: vk::Format) -> u32 { + match format { + vk::Format::A2B10G10R10_UNORM_PACK32 + | vk::Format::A2R10G10B10_UNORM_PACK32 + | vk::Format::A2B10G10R10_SNORM_PACK32 + | vk::Format::A2R10G10B10_SNORM_PACK32 => GAME_CAPTURE_FLAG_TEN_BIT | GAME_CAPTURE_FLAG_HDR, + vk::Format::R16G16B16A16_SFLOAT | vk::Format::R16G16B16A16_UNORM => GAME_CAPTURE_FLAG_HDR, + _ => 0, + } +} + +unsafe fn destroy_shared_texture_resources( + device: &ash::Device, + resources: &mut SharedTextureResources, +) { + let _ = device.wait_for_fences(&[resources.fence], true, CAPTURE_FENCE_TIMEOUT_NS); + device.destroy_fence(resources.fence, None); + if resources.present_wait_semaphore != vk::Semaphore::null() { + device.destroy_semaphore(resources.present_wait_semaphore, None); + resources.present_wait_semaphore = vk::Semaphore::null(); + } + device.destroy_command_pool(resources.command_pool, None); + device.destroy_image(resources.image, None); + device.free_memory(resources.memory, None); + resources.fence = vk::Fence::null(); + resources.command_pool = vk::CommandPool::null(); + resources.image = vk::Image::null(); + resources.memory = vk::DeviceMemory::null(); +} + +unsafe fn query_external_image_support( + instance: &ash::Instance, + physical_device: vk::PhysicalDevice, + format: vk::Format, +) -> Result { + let mut external_info = + vk::PhysicalDeviceExternalImageFormatInfo::default().handle_type(SHARED_HANDLE_TYPE); + let format_info = vk::PhysicalDeviceImageFormatInfo2::default() + .format(format) + .ty(vk::ImageType::TYPE_2D) + .tiling(vk::ImageTiling::OPTIMAL) + .usage(vk::ImageUsageFlags::TRANSFER_DST) + .flags(vk::ImageCreateFlags::empty()) + .push_next(&mut external_info); + let mut external_props = vk::ExternalImageFormatProperties::default(); + let mut props = vk::ImageFormatProperties2::default().push_next(&mut external_props); + instance.get_physical_device_image_format_properties2( + physical_device, + &format_info, + &mut props, + )?; + + let features = external_props + .external_memory_properties + .external_memory_features; + if !features.contains(vk::ExternalMemoryFeatureFlags::IMPORTABLE) { + return Err(vk::Result::ERROR_FORMAT_NOT_SUPPORTED); + } + Ok(features.contains(vk::ExternalMemoryFeatureFlags::DEDICATED_ONLY)) +} + +unsafe fn create_shared_texture_resources( + device_state: &LayerDevice, + queue_family_index: u32, + width: u32, + height: u32, + format: InteropFormat, +) -> Result { + if !device_state.external_memory_enabled || device_state.khr_external_memory_win32.is_none() { + return Err(vk::Result::ERROR_EXTENSION_NOT_PRESENT); + } + let device = &device_state.ash_device; + let Some(instance_state) = INSTANCE_MAP.get(&device_state.instance) else { + return Err(vk::Result::ERROR_INITIALIZATION_FAILED); + }; + let instance = instance_state.ash_instance.clone(); + drop(instance_state); + + let dedicated_required = match query_external_image_support( + &instance, + device_state.physical_device, + format.vk_format, + ) { + Ok(v) => { + vlog!("step query_external_image_support OK (dedicated_required={v})"); + v + } + Err(e) => { + vlog!( + "step query_external_image_support advisory-fail {e:?} (handle_type=D3D11_TEXTURE_KMT, format={:?}); attempting import anyway", + format.vk_format + ); + true + } + }; + + let shared_texture = { + let mut guard = D3D11_DEVICE + .lock() + .map_err(|_| vk::Result::ERROR_INITIALIZATION_FAILED)?; + if guard.is_none() { + *guard = D3d11Device::create(); + } + let d3d11 = guard + .as_ref() + .ok_or(vk::Result::ERROR_INITIALIZATION_FAILED)?; + match d3d11.create_shared_texture(width, height, format) { + Some(tex) => { + vlog!("step create_shared_texture OK (handle={:#x})", tex.handle); + tex + } + None => { + vlog!("step create_shared_texture FAILED (D3D11 CreateTexture2D/GetSharedHandle)"); + return Err(vk::Result::ERROR_OUT_OF_DEVICE_MEMORY); + } + } + }; + + let mut external_image_info = + vk::ExternalMemoryImageCreateInfo::default().handle_types(SHARED_HANDLE_TYPE); + let image_info = vk::ImageCreateInfo { + image_type: vk::ImageType::TYPE_2D, + format: format.vk_format, + extent: vk::Extent3D { + width, + height, + depth: 1, + }, + mip_levels: 1, + array_layers: 1, + samples: vk::SampleCountFlags::TYPE_1, + tiling: vk::ImageTiling::OPTIMAL, + usage: vk::ImageUsageFlags::TRANSFER_DST, + sharing_mode: vk::SharingMode::EXCLUSIVE, + initial_layout: vk::ImageLayout::UNDEFINED, + ..Default::default() + } + .push_next(&mut external_image_info); + let image = match device.create_image(&image_info, None) { + Ok(image) => image, + Err(e) => { + vlog!("step create_image (external) FAILED {e:?}"); + return Err(e); + } + }; + + let cleanup_image = |device: &ash::Device, image: vk::Image, shared: SharedTexture| { + device.destroy_image(image, None); + drop(shared); + }; + + let requirements = device.get_image_memory_requirements(image); + let Some(instance_state) = INSTANCE_MAP.get(&device_state.instance) else { + cleanup_image(device, image, shared_texture); + return Err(vk::Result::ERROR_INITIALIZATION_FAILED); + }; + let memory_properties = instance_state + .ash_instance + .get_physical_device_memory_properties(device_state.physical_device); + drop(instance_state); + let Some(memory_type_index) = find_memory_type( + &memory_properties, + requirements.memory_type_bits, + vk::MemoryPropertyFlags::DEVICE_LOCAL, + ) + .or_else(|| { + find_memory_type( + &memory_properties, + requirements.memory_type_bits, + vk::MemoryPropertyFlags::empty(), + ) + }) else { + cleanup_image(device, image, shared_texture); + return Err(vk::Result::ERROR_FEATURE_NOT_PRESENT); + }; + + let want_dedicated = dedicated_required || device_state.dedicated_allocation_enabled; + let mut dedicated_info = vk::MemoryDedicatedAllocateInfo::default().image(image); + let mut import_info = vk::ImportMemoryWin32HandleInfoKHR { + handle_type: SHARED_HANDLE_TYPE, + handle: shared_texture.handle as usize as vk::HANDLE, + ..Default::default() + }; + let mut allocate_info = vk::MemoryAllocateInfo { + allocation_size: requirements.size, + memory_type_index, + ..Default::default() + } + .push_next(&mut import_info); + if want_dedicated { + allocate_info = allocate_info.push_next(&mut dedicated_info); + } + let memory = match device.allocate_memory(&allocate_info, None) { + Ok(memory) => memory, + Err(error) => { + vlog!( + "step allocate_memory (import KMT handle) FAILED {error:?} (want_dedicated={want_dedicated}, mem_type={memory_type_index})" + ); + cleanup_image(device, image, shared_texture); + return Err(error); + } + }; + if let Err(error) = device.bind_image_memory(image, memory, 0) { + vlog!("step bind_image_memory FAILED {error:?}"); + device.free_memory(memory, None); + cleanup_image(device, image, shared_texture); + return Err(error); + } + vlog!("shared-texture resources created OK (fast path live)"); + + let pool_info = vk::CommandPoolCreateInfo { + flags: vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER, + queue_family_index, + ..Default::default() + }; + let command_pool = match device.create_command_pool(&pool_info, None) { + Ok(pool) => pool, + Err(error) => { + device.free_memory(memory, None); + cleanup_image(device, image, shared_texture); + return Err(error); + } + }; + let alloc_info = vk::CommandBufferAllocateInfo { + command_pool, + level: vk::CommandBufferLevel::PRIMARY, + command_buffer_count: 1, + ..Default::default() + }; + let command_buffer = match device.allocate_command_buffers(&alloc_info) { + Ok(mut buffers) => buffers.remove(0), + Err(error) => { + device.destroy_command_pool(command_pool, None); + device.free_memory(memory, None); + cleanup_image(device, image, shared_texture); + return Err(error); + } + }; + let fence = match device.create_fence(&vk::FenceCreateInfo::default(), None) { + Ok(fence) => fence, + Err(error) => { + device.destroy_command_pool(command_pool, None); + device.free_memory(memory, None); + cleanup_image(device, image, shared_texture); + return Err(error); + } + }; + let present_wait_semaphore = + match device.create_semaphore(&vk::SemaphoreCreateInfo::default(), None) { + Ok(semaphore) => semaphore, + Err(error) => { + device.destroy_fence(fence, None); + device.destroy_command_pool(command_pool, None); + device.free_memory(memory, None); + cleanup_image(device, image, shared_texture); + return Err(error); + } + }; + + Ok(SharedTextureResources { + width, + height, + format, + shared_texture, + image, + memory, + command_pool, + command_buffer, + fence, + present_wait_semaphore, + initialised: false, + }) +} + +unsafe fn ensure_shared_texture_resources<'a>( + device_state: &LayerDevice, + queue_state: &'a QueueState, + width: u32, + height: u32, + format: InteropFormat, +) -> Result>, vk::Result> { + let mut guard = queue_state + .shared + .lock() + .map_err(|_| vk::Result::ERROR_INITIALIZATION_FAILED)?; + let recreate = guard + .as_ref() + .map(|resources| { + resources.width != width + || resources.height != height + || resources.format.vk_format != format.vk_format + }) + .unwrap_or(true); + if recreate { + if let Some(mut old) = guard.take() { + destroy_shared_texture_resources(&device_state.ash_device, &mut old); + } + *guard = Some(create_shared_texture_resources( + device_state, + queue_state.family_index, + width, + height, + format, + )?); + } + Ok(guard) +} + +unsafe fn capture_swapchain_image_shared( + device_state: &LayerDevice, + queue_state: &QueueState, + queue: vk::Queue, + swapchain: &SwapchainState, + image_index: u32, + present_info: &vk::PresentInfoKHR, + ipc: &mut IpcState, +) -> Result { + if !swapchain.can_capture || !queue_state.supports_transfer { + return Err(vk::Result::ERROR_FEATURE_NOT_PRESENT); + } + if !device_state.external_memory_enabled { + return Err(vk::Result::ERROR_EXTENSION_NOT_PRESENT); + } + let Some(format) = interop_format(swapchain.format) else { + return Err(vk::Result::ERROR_FORMAT_NOT_SUPPORTED); + }; + let Some(&image) = swapchain.images.get(image_index as usize) else { + return Err(vk::Result::ERROR_OUT_OF_DATE_KHR); + }; + let width = swapchain.extent.width; + let height = swapchain.extent.height; + if width == 0 || height == 0 { + return Err(vk::Result::ERROR_OUT_OF_DATE_KHR); + } + + let mut resources_guard = + ensure_shared_texture_resources(device_state, queue_state, width, height, format)?; + let resources = resources_guard + .as_mut() + .ok_or(vk::Result::ERROR_INITIALIZATION_FAILED)?; + let device = &device_state.ash_device; + let dst_image = resources.image; + let shared_handle = resources.shared_texture.handle; + + device.reset_fences(&[resources.fence])?; + device.reset_command_pool(resources.command_pool, vk::CommandPoolResetFlags::empty())?; + let begin_info = vk::CommandBufferBeginInfo { + flags: vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT, + ..Default::default() + }; + device.begin_command_buffer(resources.command_buffer, &begin_info)?; + + let range = vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, + level_count: 1, + base_array_layer: 0, + layer_count: 1, + }; + + let present_layout = swapchain.present_layout; + let shared_present = present_layout == vk::ImageLayout::SHARED_PRESENT_KHR; + let src_copy_layout = if shared_present { + vk::ImageLayout::SHARED_PRESENT_KHR + } else { + vk::ImageLayout::TRANSFER_SRC_OPTIMAL + }; + let mut pre_barriers: Vec = Vec::with_capacity(2); + if !shared_present { + pre_barriers.push(vk::ImageMemoryBarrier { + src_access_mask: vk::AccessFlags::MEMORY_READ, + dst_access_mask: vk::AccessFlags::TRANSFER_READ, + old_layout: present_layout, + new_layout: vk::ImageLayout::TRANSFER_SRC_OPTIMAL, + src_queue_family_index: vk::QUEUE_FAMILY_IGNORED, + dst_queue_family_index: vk::QUEUE_FAMILY_IGNORED, + image, + subresource_range: range, + ..Default::default() + }); + } + let dst_old_layout = if resources.initialised { + vk::ImageLayout::GENERAL + } else { + vk::ImageLayout::UNDEFINED + }; + let (dst_pre_src_queue_family, dst_pre_dst_queue_family) = + shared_texture_dst_pre_queue_families(resources.initialised, queue_state.family_index); + pre_barriers.push(vk::ImageMemoryBarrier { + src_access_mask: vk::AccessFlags::empty(), + dst_access_mask: vk::AccessFlags::TRANSFER_WRITE, + old_layout: dst_old_layout, + new_layout: vk::ImageLayout::TRANSFER_DST_OPTIMAL, + src_queue_family_index: dst_pre_src_queue_family, + dst_queue_family_index: dst_pre_dst_queue_family, + image: dst_image, + subresource_range: range, + ..Default::default() + }); + device.cmd_pipeline_barrier( + resources.command_buffer, + vk::PipelineStageFlags::BOTTOM_OF_PIPE, + vk::PipelineStageFlags::TRANSFER, + vk::DependencyFlags::empty(), + &[], + &[], + &pre_barriers, + ); + + let copy = vk::ImageCopy { + src_subresource: vk::ImageSubresourceLayers { + aspect_mask: vk::ImageAspectFlags::COLOR, + mip_level: 0, + base_array_layer: 0, + layer_count: 1, + }, + src_offset: vk::Offset3D { x: 0, y: 0, z: 0 }, + dst_subresource: vk::ImageSubresourceLayers { + aspect_mask: vk::ImageAspectFlags::COLOR, + mip_level: 0, + base_array_layer: 0, + layer_count: 1, + }, + dst_offset: vk::Offset3D { x: 0, y: 0, z: 0 }, + extent: vk::Extent3D { + width, + height, + depth: 1, + }, + }; + device.cmd_copy_image( + resources.command_buffer, + image, + src_copy_layout, + dst_image, + vk::ImageLayout::TRANSFER_DST_OPTIMAL, + &[copy], + ); + + let mut post_barriers: Vec = Vec::with_capacity(2); + if !shared_present { + post_barriers.push(vk::ImageMemoryBarrier { + src_access_mask: vk::AccessFlags::TRANSFER_READ, + dst_access_mask: vk::AccessFlags::MEMORY_READ, + old_layout: vk::ImageLayout::TRANSFER_SRC_OPTIMAL, + new_layout: present_layout, + src_queue_family_index: vk::QUEUE_FAMILY_IGNORED, + dst_queue_family_index: vk::QUEUE_FAMILY_IGNORED, + image, + subresource_range: range, + ..Default::default() + }); + } + post_barriers.push(vk::ImageMemoryBarrier { + src_access_mask: vk::AccessFlags::TRANSFER_WRITE, + dst_access_mask: vk::AccessFlags::empty(), + old_layout: vk::ImageLayout::TRANSFER_DST_OPTIMAL, + new_layout: vk::ImageLayout::GENERAL, + src_queue_family_index: queue_state.family_index, + dst_queue_family_index: vk::QUEUE_FAMILY_EXTERNAL, + image: dst_image, + subresource_range: range, + ..Default::default() + }); + device.cmd_pipeline_barrier( + resources.command_buffer, + vk::PipelineStageFlags::TRANSFER, + vk::PipelineStageFlags::BOTTOM_OF_PIPE, + vk::DependencyFlags::empty(), + &[], + &[], + &post_barriers, + ); + device.end_command_buffer(resources.command_buffer)?; + + let chained_wait = submit_capture( + device, + queue, + resources.command_buffer, + resources.fence, + resources.present_wait_semaphore, + present_info, + )?; + resources.initialised = true; + + if device + .wait_for_fences(&[resources.fence], true, CAPTURE_FENCE_TIMEOUT_NS) + .is_err() + { + (*ipc.info).dropped_frame_counter = (*ipc.info).dropped_frame_counter.wrapping_add(1); + return Ok(CaptureSubmit { chained_wait }); + } + + publish_shared_texture(ipc, swapchain.hwnd, width, height, format, shared_handle); + Ok(CaptureSubmit { chained_wait }) +} + +fn shared_texture_dst_pre_queue_families(initialised: bool, queue_family_index: u32) -> (u32, u32) { + if initialised { + (vk::QUEUE_FAMILY_EXTERNAL, queue_family_index) + } else { + (vk::QUEUE_FAMILY_IGNORED, vk::QUEUE_FAMILY_IGNORED) + } +} + +unsafe fn submit_capture( + device: &ash::Device, + queue: vk::Queue, + command_buffer: vk::CommandBuffer, + fence: vk::Fence, + signal_semaphore: vk::Semaphore, + present_info: &vk::PresentInfoKHR, +) -> Result, vk::Result> { + let wait_semaphores = present_wait_semaphores(present_info); + let wait_stages = vec![vk::PipelineStageFlags::TRANSFER; wait_semaphores.len()]; + let chain = !wait_semaphores.is_empty(); + let signal = if chain { + [signal_semaphore] + } else { + [vk::Semaphore::null(); 1] + }; + let submit = vk::SubmitInfo { + wait_semaphore_count: wait_semaphores.len() as u32, + p_wait_semaphores: wait_semaphores.as_ptr(), + p_wait_dst_stage_mask: wait_stages.as_ptr(), + command_buffer_count: 1, + p_command_buffers: &command_buffer, + signal_semaphore_count: if chain { 1 } else { 0 }, + p_signal_semaphores: if chain { signal.as_ptr() } else { null() }, + ..Default::default() + }; + device.queue_submit(queue, &[submit], fence)?; + Ok(chain.then_some(signal_semaphore)) +} + +unsafe fn present_wait_semaphores<'a>( + present_info: &vk::PresentInfoKHR<'a>, +) -> &'a [vk::Semaphore] { + if present_info.wait_semaphore_count > 0 && !present_info.p_wait_semaphores.is_null() { + std::slice::from_raw_parts( + present_info.p_wait_semaphores, + present_info.wait_semaphore_count as usize, + ) + } else { + &[] + } +} + +unsafe fn publish_shared_texture( + state: &mut IpcState, + hwnd: vk::HWND, + width: u32, + height: u32, + format: InteropFormat, + shared_handle: u64, +) { + let info = &mut *state.info; + info.hwnd = hwnd as u64; + info.width = width; + info.height = height; + info.pitch = width * 4; + info.frame_index = 0; + info.frame_counter = info.frame_counter.wrapping_add(1); + info.timestamp_us = now_us(); + info.transport = GAME_CAPTURE_TRANSPORT_SHARED_TEXTURE; + info.dxgi_format = format.dxgi_format.0 as u32; + info.texture_handle = shared_handle; + info.api_type = GAME_CAPTURE_API_VULKAN; + info.fallback_reason = GAME_CAPTURE_FALLBACK_NONE; + info.capture_flags = format.capture_flags; + info.state = GAME_CAPTURE_STATE_ACTIVE; + SetEvent(state.ready_event); +} + +unsafe fn destroy_readback_resources(device: &ash::Device, resources: &mut ReadbackResources) { + let _ = device.wait_for_fences(&[resources.fence], true, CAPTURE_FENCE_TIMEOUT_NS); + device.destroy_fence(resources.fence, None); + if resources.present_wait_semaphore != vk::Semaphore::null() { + device.destroy_semaphore(resources.present_wait_semaphore, None); + resources.present_wait_semaphore = vk::Semaphore::null(); + } + device.destroy_command_pool(resources.command_pool, None); + device.destroy_buffer(resources.buffer, None); + device.free_memory(resources.memory, None); + resources.fence = vk::Fence::null(); + resources.command_pool = vk::CommandPool::null(); + resources.buffer = vk::Buffer::null(); + resources.memory = vk::DeviceMemory::null(); +} + +fn find_memory_type( + properties: &vk::PhysicalDeviceMemoryProperties, + type_bits: u32, + required: vk::MemoryPropertyFlags, +) -> Option { + for index in 0..properties.memory_type_count { + let supported = (type_bits & (1 << index)) != 0; + let flags = properties.memory_types[index as usize].property_flags; + if supported && flags.contains(required) { + return Some(index); + } + } + None +} + +unsafe fn create_readback_resources( + device_state: &LayerDevice, + queue_family_index: u32, + width: u32, + height: u32, + format_mode: FormatMode, +) -> Result { + let device = &device_state.ash_device; + let size = (width as vk::DeviceSize) + .checked_mul(height as vk::DeviceSize) + .and_then(|value| value.checked_mul(4)) + .ok_or(vk::Result::ERROR_OUT_OF_HOST_MEMORY)?; + let buffer_info = vk::BufferCreateInfo { + size, + usage: vk::BufferUsageFlags::TRANSFER_DST, + sharing_mode: vk::SharingMode::EXCLUSIVE, + ..Default::default() + }; + let buffer = device.create_buffer(&buffer_info, None)?; + let requirements = device.get_buffer_memory_requirements(buffer); + let Some(instance_state) = INSTANCE_MAP.get(&device_state.instance) else { + device.destroy_buffer(buffer, None); + return Err(vk::Result::ERROR_INITIALIZATION_FAILED); + }; + let memory_properties = instance_state + .ash_instance + .get_physical_device_memory_properties(device_state.physical_device); + let memory_type_index = match find_memory_type( + &memory_properties, + requirements.memory_type_bits, + vk::MemoryPropertyFlags::HOST_VISIBLE | vk::MemoryPropertyFlags::HOST_COHERENT, + ) { + Some(index) => index, + None => { + device.destroy_buffer(buffer, None); + return Err(vk::Result::ERROR_FEATURE_NOT_PRESENT); + } + }; + let allocate_info = vk::MemoryAllocateInfo { + allocation_size: requirements.size, + memory_type_index, + ..Default::default() + }; + let memory = match device.allocate_memory(&allocate_info, None) { + Ok(memory) => memory, + Err(error) => { + device.destroy_buffer(buffer, None); + return Err(error); + } + }; + if let Err(error) = device.bind_buffer_memory(buffer, memory, 0) { + device.free_memory(memory, None); + device.destroy_buffer(buffer, None); + return Err(error); + } + + let pool_info = vk::CommandPoolCreateInfo { + flags: vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER, + queue_family_index, + ..Default::default() + }; + let command_pool = match device.create_command_pool(&pool_info, None) { + Ok(pool) => pool, + Err(error) => { + device.free_memory(memory, None); + device.destroy_buffer(buffer, None); + return Err(error); + } + }; + let alloc_info = vk::CommandBufferAllocateInfo { + command_pool, + level: vk::CommandBufferLevel::PRIMARY, + command_buffer_count: 1, + ..Default::default() + }; + let command_buffer = match device.allocate_command_buffers(&alloc_info) { + Ok(mut buffers) => buffers.remove(0), + Err(error) => { + device.destroy_command_pool(command_pool, None); + device.free_memory(memory, None); + device.destroy_buffer(buffer, None); + return Err(error); + } + }; + let fence = match device.create_fence(&vk::FenceCreateInfo::default(), None) { + Ok(fence) => fence, + Err(error) => { + device.destroy_command_pool(command_pool, None); + device.free_memory(memory, None); + device.destroy_buffer(buffer, None); + return Err(error); + } + }; + let present_wait_semaphore = + match device.create_semaphore(&vk::SemaphoreCreateInfo::default(), None) { + Ok(semaphore) => semaphore, + Err(error) => { + device.destroy_fence(fence, None); + device.destroy_command_pool(command_pool, None); + device.free_memory(memory, None); + device.destroy_buffer(buffer, None); + return Err(error); + } + }; + + Ok(ReadbackResources { + width, + height, + format_mode, + buffer, + memory, + command_pool, + command_buffer, + fence, + present_wait_semaphore, + size, + }) +} + +unsafe fn ensure_readback_resources<'a>( + device_state: &LayerDevice, + queue_state: &'a QueueState, + width: u32, + height: u32, + format_mode: FormatMode, +) -> Result>, vk::Result> { + let mut guard = queue_state + .resources + .lock() + .map_err(|_| vk::Result::ERROR_INITIALIZATION_FAILED)?; + let recreate = guard + .as_ref() + .map(|resources| { + resources.width != width + || resources.height != height + || resources.format_mode != format_mode + }) + .unwrap_or(true); + if recreate { + if let Some(mut old) = guard.take() { + destroy_readback_resources(&device_state.ash_device, &mut old); + } + *guard = Some(create_readback_resources( + device_state, + queue_state.family_index, + width, + height, + format_mode, + )?); + } + Ok(guard) +} + +unsafe fn capture_swapchain_image( + device_state: &LayerDevice, + queue_state: &QueueState, + queue: vk::Queue, + swapchain: &SwapchainState, + image_index: u32, + present_info: &vk::PresentInfoKHR, + ipc: &mut IpcState, + fallback_reason: u32, +) -> Result { + if !swapchain.can_capture || !queue_state.supports_transfer { + return Err(vk::Result::ERROR_FEATURE_NOT_PRESENT); + } + let Some(format_mode) = format_mode(swapchain.format) else { + return Err(vk::Result::ERROR_FORMAT_NOT_SUPPORTED); + }; + let Some(&image) = swapchain.images.get(image_index as usize) else { + return Err(vk::Result::ERROR_OUT_OF_DATE_KHR); + }; + let width = swapchain.extent.width; + let height = swapchain.extent.height; + if width == 0 || height == 0 { + return Err(vk::Result::ERROR_OUT_OF_DATE_KHR); + } + + let mut resources_guard = + ensure_readback_resources(device_state, queue_state, width, height, format_mode)?; + let resources = resources_guard + .as_mut() + .ok_or(vk::Result::ERROR_INITIALIZATION_FAILED)?; + let device = &device_state.ash_device; + + device.reset_fences(&[resources.fence])?; + device.reset_command_pool(resources.command_pool, vk::CommandPoolResetFlags::empty())?; + let begin_info = vk::CommandBufferBeginInfo { + flags: vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT, + ..Default::default() + }; + device.begin_command_buffer(resources.command_buffer, &begin_info)?; + + let range = vk::ImageSubresourceRange { + aspect_mask: vk::ImageAspectFlags::COLOR, + base_mip_level: 0, + level_count: 1, + base_array_layer: 0, + layer_count: 1, + }; + let present_layout = swapchain.present_layout; + let shared_present = present_layout == vk::ImageLayout::SHARED_PRESENT_KHR; + let src_copy_layout = if shared_present { + vk::ImageLayout::SHARED_PRESENT_KHR + } else { + vk::ImageLayout::TRANSFER_SRC_OPTIMAL + }; + if !shared_present { + let to_transfer = vk::ImageMemoryBarrier { + src_access_mask: vk::AccessFlags::MEMORY_READ, + dst_access_mask: vk::AccessFlags::TRANSFER_READ, + old_layout: present_layout, + new_layout: vk::ImageLayout::TRANSFER_SRC_OPTIMAL, + src_queue_family_index: vk::QUEUE_FAMILY_IGNORED, + dst_queue_family_index: vk::QUEUE_FAMILY_IGNORED, + image, + subresource_range: range, + ..Default::default() + }; + device.cmd_pipeline_barrier( + resources.command_buffer, + vk::PipelineStageFlags::BOTTOM_OF_PIPE, + vk::PipelineStageFlags::TRANSFER, + vk::DependencyFlags::empty(), + &[], + &[], + &[to_transfer], + ); + } + let copy = vk::BufferImageCopy { + buffer_offset: 0, + buffer_row_length: 0, + buffer_image_height: 0, + image_subresource: vk::ImageSubresourceLayers { + aspect_mask: vk::ImageAspectFlags::COLOR, + mip_level: 0, + base_array_layer: 0, + layer_count: 1, + }, + image_offset: vk::Offset3D { x: 0, y: 0, z: 0 }, + image_extent: vk::Extent3D { + width, + height, + depth: 1, + }, + }; + device.cmd_copy_image_to_buffer( + resources.command_buffer, + image, + src_copy_layout, + resources.buffer, + &[copy], + ); + if !shared_present { + let to_present = vk::ImageMemoryBarrier { + src_access_mask: vk::AccessFlags::TRANSFER_READ, + dst_access_mask: vk::AccessFlags::MEMORY_READ, + old_layout: vk::ImageLayout::TRANSFER_SRC_OPTIMAL, + new_layout: present_layout, + src_queue_family_index: vk::QUEUE_FAMILY_IGNORED, + dst_queue_family_index: vk::QUEUE_FAMILY_IGNORED, + image, + subresource_range: range, + ..Default::default() + }; + device.cmd_pipeline_barrier( + resources.command_buffer, + vk::PipelineStageFlags::TRANSFER, + vk::PipelineStageFlags::BOTTOM_OF_PIPE, + vk::DependencyFlags::empty(), + &[], + &[], + &[to_present], + ); + } + device.end_command_buffer(resources.command_buffer)?; + + let chained_wait = submit_capture( + device, + queue, + resources.command_buffer, + resources.fence, + resources.present_wait_semaphore, + present_info, + )?; + + if device + .wait_for_fences(&[resources.fence], true, CAPTURE_FENCE_TIMEOUT_NS) + .is_err() + { + return Ok(CaptureSubmit { chained_wait }); + } + + let mapped = match device.map_memory( + resources.memory, + 0, + resources.size, + vk::MemoryMapFlags::empty(), + ) { + Ok(mapped) => mapped, + Err(_) => return Ok(CaptureSubmit { chained_wait }), + }; + let _ = write_rows_to_shared_memory( + ipc, + swapchain.hwnd, + width, + height, + mapped.cast(), + (width * 4) as usize, + resources.format_mode, + fallback_reason, + ); + device.unmap_memory(resources.memory); + Ok(CaptureSubmit { chained_wait }) +} + +unsafe fn capture_swapchain_for_present( + device_state: &LayerDevice, + queue_state: &QueueState, + queue: vk::Queue, + swapchain: &SwapchainState, + image_index: u32, + present_info: &vk::PresentInfoKHR, + ipc: &mut IpcState, +) -> Result { + if !target_matches(ipc, swapchain.hwnd) || !frame_due(ipc) { + return Err(vk::Result::NOT_READY); + } + + let hdr_flags = hdr_format_flags(swapchain.format); + let fast_capturable = interop_format(swapchain.format).is_some(); + let cpu_capturable = format_mode(swapchain.format).is_some(); + + if !fast_capturable && !cpu_capturable { + let info = &mut *ipc.info; + info.api_type = GAME_CAPTURE_API_VULKAN; + info.capture_flags = hdr_flags; + info.fallback_reason = GAME_CAPTURE_FALLBACK_FORMAT_UNSUPPORTED; + info.dropped_frame_counter = info.dropped_frame_counter.wrapping_add(1); + return Err(vk::Result::ERROR_FORMAT_NOT_SUPPORTED); + } + + let mode = capture_mode(); + let fast_path_possible = device_state.external_memory_enabled + && fast_capturable + && swapchain.shared_texture_allowed + && !fast_path_permanently_disabled(); + vlog!( + "present: mode={:?} fast_path_possible={} (ext_enabled={}, interop_format={:?}, shared_allowed={}, perm_disabled={}) swapchain_format={:?}", + mode, + fast_path_possible, + device_state.external_memory_enabled, + interop_format(swapchain.format).map(|f| f.dxgi_format.0), + swapchain.shared_texture_allowed, + fast_path_permanently_disabled(), + swapchain.format + ); + + if mode != CaptureMode::ForceCpu && fast_path_possible { + match capture_swapchain_image_shared( + device_state, + queue_state, + queue, + swapchain, + image_index, + present_info, + ipc, + ) { + Ok(result) => return Ok(result), + Err(error) => { + vlog!("fast-path shared-texture FAILED: {:?}", error); + if matches!( + error, + vk::Result::ERROR_INITIALIZATION_FAILED + | vk::Result::ERROR_EXTENSION_NOT_PRESENT + | vk::Result::ERROR_OUT_OF_DEVICE_MEMORY + | vk::Result::ERROR_FORMAT_NOT_SUPPORTED + | vk::Result::ERROR_FEATURE_NOT_PRESENT + | vk::Result::ERROR_INVALID_EXTERNAL_HANDLE + ) { + disable_fast_path(); + } + if mode == CaptureMode::ForceSharedTexture { + let info = &mut *ipc.info; + info.api_type = GAME_CAPTURE_API_VULKAN; + info.fallback_reason = GAME_CAPTURE_FALLBACK_EXTERNAL_MEMORY_UNSUPPORTED; + info.state = GAME_CAPTURE_STATE_ERROR; + info.last_error = 70; + info.dropped_frame_counter = info.dropped_frame_counter.wrapping_add(1); + return Err(error); + } + } + } + } else if mode == CaptureMode::ForceSharedTexture { + let info = &mut *ipc.info; + info.api_type = GAME_CAPTURE_API_VULKAN; + info.fallback_reason = if interop_format(swapchain.format).is_none() { + GAME_CAPTURE_FALLBACK_FORMAT_UNSUPPORTED + } else { + GAME_CAPTURE_FALLBACK_EXTERNAL_MEMORY_UNSUPPORTED + }; + info.state = GAME_CAPTURE_STATE_ERROR; + info.last_error = 70; + info.dropped_frame_counter = info.dropped_frame_counter.wrapping_add(1); + return Err(vk::Result::ERROR_FEATURE_NOT_PRESENT); + } + + if !cpu_capturable { + let info = &mut *ipc.info; + info.api_type = GAME_CAPTURE_API_VULKAN; + info.capture_flags = hdr_flags; + info.fallback_reason = if mode == CaptureMode::ForceCpu { + GAME_CAPTURE_FALLBACK_FORCED_CPU + } else { + GAME_CAPTURE_FALLBACK_FORMAT_UNSUPPORTED + }; + info.dropped_frame_counter = info.dropped_frame_counter.wrapping_add(1); + return Err(vk::Result::ERROR_FORMAT_NOT_SUPPORTED); + } + + let fallback_reason = if mode == CaptureMode::ForceCpu { + GAME_CAPTURE_FALLBACK_FORCED_CPU + } else { + GAME_CAPTURE_FALLBACK_EXTERNAL_MEMORY_UNSUPPORTED + }; + capture_swapchain_image( + device_state, + queue_state, + queue, + swapchain, + image_index, + present_info, + ipc, + fallback_reason, + ) +} + +fn fast_path_permanently_disabled() -> bool { + FAST_PATH_DISABLED + .lock() + .map(|guard| *guard) + .unwrap_or(false) +} + +fn disable_fast_path() { + if let Ok(mut guard) = FAST_PATH_DISABLED.lock() { + *guard = true; + } +} + +unsafe fn try_capture_present( + queue: vk::Queue, + present_info: &vk::PresentInfoKHR, +) -> CaptureSubmit { + if present_info.swapchain_count == 0 + || present_info.p_swapchains.is_null() + || present_info.p_image_indices.is_null() + { + return CaptureSubmit::none(); + } + let Some(queue_state_ref) = QUEUE_MAP.get(&queue) else { + return CaptureSubmit::none(); + }; + let Some(device_state_ref) = DEVICE_MAP.get(&queue_state_ref.device) else { + return CaptureSubmit::none(); + }; + + let mut ipc_guard = match IPC_STATE.lock() { + Ok(guard) => guard, + Err(_) => { + return CaptureSubmit::none(); + } + }; + if ipc_guard.is_none() { + *ipc_guard = open_ipc(); + } + let Some(ipc) = ipc_guard.as_mut() else { + return CaptureSubmit::none(); + }; + if !capture_should_run(ipc) { + if let Some(mut state) = ipc_guard.take() { + free_ipc_state(&mut state); + } + return CaptureSubmit::none(); + } + + if host_supports_present_clock((*ipc.info).version) { + (*ipc.info).present_clock = GAME_CAPTURE_PRESENT_CLOCK_QPC; + (*ipc.info).last_present_timestamp_us = qpc_now_us(); + } else { + (*ipc.info).last_present_timestamp_us = now_us(); + } + + let swapchains = std::slice::from_raw_parts( + present_info.p_swapchains, + present_info.swapchain_count as usize, + ); + let indices = std::slice::from_raw_parts( + present_info.p_image_indices, + present_info.swapchain_count as usize, + ); + for (swapchain_handle, image_index) in swapchains.iter().zip(indices.iter()) { + let Some(swapchain_ref) = SWAPCHAIN_MAP.get(swapchain_handle) else { + continue; + }; + if swapchain_ref.device != queue_state_ref.device { + continue; + } + if let Ok(result) = capture_swapchain_for_present( + &device_state_ref, + &queue_state_ref, + queue, + &swapchain_ref, + *image_index, + present_info, + ipc, + ) { + return result; + } + } + CaptureSubmit::none() +} + +unsafe fn record_queue(device: vk::Device, queue: vk::Queue, family_index: u32) { + if queue == vk::Queue::null() { + return; + } + let supports_transfer = DEVICE_MAP + .get(&device) + .and_then(|device_state| { + device_state + .queue_families + .get(family_index as usize) + .copied() + }) + .map(|properties| { + properties.queue_flags.intersects( + vk::QueueFlags::GRAPHICS | vk::QueueFlags::COMPUTE | vk::QueueFlags::TRANSFER, + ) + }) + .unwrap_or(true); + QUEUE_MAP.insert( + queue, + QueueState { + device, + family_index, + supports_transfer, + resources: Mutex::new(None), + shared: Mutex::new(None), + }, + ); +} + +unsafe fn destroy_device_state(device: vk::Device) -> Option { + let device_state = DEVICE_MAP.remove(&device).map(|(_, value)| value)?; + let queue_keys: Vec = QUEUE_MAP + .iter() + .filter_map(|entry| (entry.value().device == device).then_some(*entry.key())) + .collect(); + for queue in queue_keys { + if let Some((_, queue_state)) = QUEUE_MAP.remove(&queue) { + if let Ok(mut resources) = queue_state.resources.lock() { + if let Some(mut resources) = resources.take() { + destroy_readback_resources(&device_state.ash_device, &mut resources); + } + } + if let Ok(mut shared) = queue_state.shared.lock() { + if let Some(mut shared) = shared.take() { + destroy_shared_texture_resources(&device_state.ash_device, &mut shared); + } + } + } + } + let swapchain_keys: Vec = SWAPCHAIN_MAP + .iter() + .filter_map(|entry| (entry.value().device == device).then_some(*entry.key())) + .collect(); + for swapchain in swapchain_keys { + SWAPCHAIN_MAP.remove(&swapchain); + } + GDPA_MAP.remove(&device); + Some(device_state) +} + +unsafe extern "system" fn dispatch_next_vkGetInstanceProcAddr( + instance: vk::Instance, + p_name: *const c_char, +) -> vk::PFN_vkVoidFunction { + if p_name.is_null() { + return None; + } + let name = CStr::from_ptr(p_name).to_bytes(); + let pfn: *const () = match name { + b"vkGetInstanceProcAddr" => dispatch_next_vkGetInstanceProcAddr as _, + b"vkGetDeviceProcAddr" => dispatch_next_vkGetDeviceProcAddr as _, + b"vkEnumerateInstanceExtensionProperties" + | b"vkEnumerateInstanceLayerProperties" + | b"vkEnumerateInstanceVersion" => return None, + _ => { + let gipa = GIPA.get()?; + return gipa(instance, p_name); + } + }; + Some(mem::transmute(pfn)) +} + +unsafe extern "system" fn dispatch_next_vkGetDeviceProcAddr( + device: vk::Device, + p_name: *const c_char, +) -> vk::PFN_vkVoidFunction { + if p_name.is_null() { + return None; + } + let name = CStr::from_ptr(p_name).to_bytes(); + if name == b"vkGetDeviceProcAddr" { + return Some(mem::transmute( + dispatch_next_vkGetDeviceProcAddr as *const (), + )); + } + let gdpa = GDPA_MAP.get(&device)?; + gdpa(device, p_name) +} + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn vkNegotiateLoaderLayerInterfaceVersion( + p_version_struct: *mut NegotiateLayerInterface, +) -> vk::Result { + if p_version_struct.is_null() { + return vk::Result::ERROR_INITIALIZATION_FAILED; + } + let version_struct = &mut *p_version_struct; + version_struct.loader_layer_interface_version = + version_struct.loader_layer_interface_version.min(2); + version_struct.pfn_get_instance_proc_addr = Some(flux_vkGetInstanceProcAddr); + version_struct.pfn_get_device_proc_addr = Some(flux_vkGetDeviceProcAddr); + version_struct.pfn_get_physical_device_proc_addr = Some(flux_vk_layerGetPhysicalDeviceProcAddr); + vk::Result::SUCCESS +} +const _: PFN_vkNegotiateLoaderLayerInterfaceVersion = vkNegotiateLoaderLayerInterfaceVersion; + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn vkGetInstanceProcAddr( + instance: vk::Instance, + p_name: *const c_char, +) -> vk::PFN_vkVoidFunction { + flux_vkGetInstanceProcAddr(instance, p_name) +} + +#[unsafe(no_mangle)] +unsafe extern "system" fn flux_vkGetInstanceProcAddr( + instance: vk::Instance, + p_name: *const c_char, +) -> vk::PFN_vkVoidFunction { + if p_name.is_null() { + return None; + } + let name = CStr::from_ptr(p_name).to_bytes(); + let pfn: *const () = match name { + b"vkGetInstanceProcAddr" => flux_vkGetInstanceProcAddr as _, + b"vkGetDeviceProcAddr" => flux_vkGetDeviceProcAddr as _, + b"vkCreateInstance" => flux_vkCreateInstance as _, + b"vkDestroyInstance" => flux_vkDestroyInstance as _, + b"vkCreateDevice" => flux_vkCreateDevice as _, + b"vkDestroyDevice" => flux_vkDestroyDevice as _, + b"vkCreateWin32SurfaceKHR" => flux_vkCreateWin32SurfaceKHR as _, + b"vkDestroySurfaceKHR" => flux_vkDestroySurfaceKHR as _, + b"vk_layerGetPhysicalDeviceProcAddr" => flux_vk_layerGetPhysicalDeviceProcAddr as _, + _ => { + let gipa = GIPA.get()?; + return gipa(instance, p_name); + } + }; + Some(mem::transmute(pfn)) +} +const _: vk::PFN_vkGetInstanceProcAddr = flux_vkGetInstanceProcAddr; + +#[unsafe(no_mangle)] +pub unsafe extern "system" fn vkGetDeviceProcAddr( + device: vk::Device, + p_name: *const c_char, +) -> vk::PFN_vkVoidFunction { + flux_vkGetDeviceProcAddr(device, p_name) +} + +#[unsafe(no_mangle)] +unsafe extern "system" fn flux_vkGetDeviceProcAddr( + device: vk::Device, + p_name: *const c_char, +) -> vk::PFN_vkVoidFunction { + if p_name.is_null() { + return None; + } + let name = CStr::from_ptr(p_name).to_bytes(); + let pfn: *const () = match name { + b"vkGetDeviceProcAddr" => flux_vkGetDeviceProcAddr as _, + b"vkCreateDevice" => flux_vkCreateDevice as _, + b"vkDestroyDevice" => flux_vkDestroyDevice as _, + b"vkCreateSwapchainKHR" => flux_vkCreateSwapchainKHR as _, + b"vkDestroySwapchainKHR" => flux_vkDestroySwapchainKHR as _, + b"vkGetSwapchainImagesKHR" => flux_vkGetSwapchainImagesKHR as _, + b"vkQueuePresentKHR" => flux_vkQueuePresentKHR as _, + b"vkGetDeviceQueue" => flux_vkGetDeviceQueue as _, + b"vkGetDeviceQueue2" => flux_vkGetDeviceQueue2 as _, + _ => { + let gdpa = GDPA_MAP.get(&device)?; + return gdpa(device, p_name); + } + }; + Some(mem::transmute(pfn)) +} +const _: vk::PFN_vkGetDeviceProcAddr = flux_vkGetDeviceProcAddr; + +#[unsafe(no_mangle)] +unsafe extern "system" fn flux_vk_layerGetPhysicalDeviceProcAddr( + instance: vk::Instance, + p_name: *const c_char, +) -> vk::PFN_vkVoidFunction { + if p_name.is_null() { + return None; + } + let name = CStr::from_ptr(p_name).to_bytes(); + if name == b"vkCreateDevice" { + return Some(mem::transmute(flux_vkCreateDevice as *const ())); + } + let gphypa = GPHYPA.get()?; + gphypa(instance, p_name) +} +const _: PFN_vk_layerGetPhysicalDeviceProcAddr = flux_vk_layerGetPhysicalDeviceProcAddr; + +#[unsafe(no_mangle)] +unsafe extern "system" fn flux_vkCreateInstance( + p_create_info: *const vk::InstanceCreateInfo, + p_allocator: *const vk::AllocationCallbacks, + p_instance: *mut vk::Instance, +) -> vk::Result { + if p_create_info.is_null() || p_instance.is_null() { + return vk::Result::ERROR_INITIALIZATION_FAILED; + } + let create_info = p_create_info.read(); + let Some(mut chain_info) = + get_instance_chain_info(&create_info, LayerFunction::LAYER_LINK_INFO) + else { + return vk::Result::ERROR_INITIALIZATION_FAILED; + }; + let chain_info = chain_info.as_mut(); + let layer_info = chain_info.u.p_layer_info.read(); + chain_info.u.p_layer_info = layer_info.p_next; + + let Some(gipa) = layer_info.pfn_next_get_instance_proc_addr else { + return vk::Result::ERROR_INITIALIZATION_FAILED; + }; + if let Some(gphypa) = layer_info.pfn_next_get_physical_device_proc_addr { + let _ = GPHYPA.set(gphypa); + } + let create_name = CStr::from_bytes_with_nul_unchecked(b"vkCreateInstance\0"); + let Some(create_instance_ptr) = gipa(vk::Instance::null(), create_name.as_ptr()) else { + return vk::Result::ERROR_INITIALIZATION_FAILED; + }; + let create_instance: vk::PFN_vkCreateInstance = mem::transmute(create_instance_ptr); + + let augmented = AugmentedExtensions::build( + create_info.pp_enabled_extension_names, + create_info.enabled_extension_count, + &[ + INSTANCE_EXT_PHYSICAL_DEVICE_PROPERTIES2, + INSTANCE_EXT_EXTERNAL_MEMORY_CAPABILITIES, + ], + ); + let mut augmented_info = create_info; + augmented_info.enabled_extension_count = augmented.count(); + augmented_info.pp_enabled_extension_names = augmented.as_ptr(); + + let result = if capture_mode() != CaptureMode::ForceCpu { + let augmented_result = create_instance(&augmented_info, p_allocator, p_instance); + if augmented_result == vk::Result::SUCCESS { + augmented_result + } else { + create_instance(p_create_info, p_allocator, p_instance) + } + } else { + create_instance(p_create_info, p_allocator, p_instance) + }; + if result != vk::Result::SUCCESS { + return result; + } + + let instance = *p_instance; + let _ = GIPA.set(gipa); + let entry = ash::Entry::from_static_fn(ash::StaticFn { + get_instance_proc_addr: dispatch_next_vkGetInstanceProcAddr, + }); + let _ = ENTRY.set(entry.clone()); + let ash_instance = ash::Instance::load(entry.static_fn(), instance); + let khr_surface = khr::surface::Instance::new(&entry, &ash_instance); + let khr_win32_surface = khr::win32_surface::Instance::new(&entry, &ash_instance); + if let Ok(physical_devices) = ash_instance.enumerate_physical_devices() { + for physical_device in physical_devices { + PHY_TO_INSTANCE_MAP.insert(physical_device, instance); + } + } + INSTANCE_MAP.insert( + instance, + LayerInstance { + ash_instance, + khr_surface, + khr_win32_surface, + }, + ); + vk::Result::SUCCESS +} +const _: vk::PFN_vkCreateInstance = flux_vkCreateInstance; + +#[unsafe(no_mangle)] +unsafe extern "system" fn flux_vkDestroyInstance( + instance: vk::Instance, + p_allocator: *const vk::AllocationCallbacks, +) { + let Some((_, instance_state)) = INSTANCE_MAP.remove(&instance) else { + return; + }; + if let Ok(physical_devices) = instance_state.ash_instance.enumerate_physical_devices() { + for physical_device in physical_devices { + PHY_TO_INSTANCE_MAP.remove(&physical_device); + } + } + let surfaces: Vec = SURFACE_MAP + .iter() + .filter_map(|entry| (entry.value().instance == instance).then_some(*entry.key())) + .collect(); + for surface in surfaces { + SURFACE_MAP.remove(&surface); + } + (instance_state.ash_instance.fp_v1_0().destroy_instance)(instance, p_allocator); +} +const _: vk::PFN_vkDestroyInstance = flux_vkDestroyInstance; + +#[unsafe(no_mangle)] +unsafe extern "system" fn flux_vkCreateDevice( + physical_device: vk::PhysicalDevice, + p_create_info: *const vk::DeviceCreateInfo, + p_allocator: *const vk::AllocationCallbacks, + p_device: *mut vk::Device, +) -> vk::Result { + if p_create_info.is_null() || p_device.is_null() { + return vk::Result::ERROR_INITIALIZATION_FAILED; + } + let instance = PHY_TO_INSTANCE_MAP + .get(&physical_device) + .map(|entry| *entry) + .or_else(|| INSTANCE_MAP.iter().next().map(|entry| *entry.key())); + let Some(instance) = instance else { + return vk::Result::ERROR_INITIALIZATION_FAILED; + }; + let Some(instance_state) = INSTANCE_MAP.get(&instance) else { + return vk::Result::ERROR_INITIALIZATION_FAILED; + }; + let instance_fn = instance_state.ash_instance.fp_v1_0(); + + let create_info = p_create_info.read(); + let Some(mut chain_info) = get_device_chain_info(&create_info, LayerFunction::LAYER_LINK_INFO) + else { + return vk::Result::ERROR_INITIALIZATION_FAILED; + }; + let chain_info = chain_info.as_mut(); + let layer_info = chain_info.u.p_layer_info.read(); + chain_info.u.p_layer_info = layer_info.p_next; + + let Some(gdpa) = layer_info.pfn_next_get_device_proc_addr else { + return vk::Result::ERROR_INITIALIZATION_FAILED; + }; + + let want_external_memory = capture_mode() != CaptureMode::ForceCpu; + let augmented = AugmentedExtensions::build( + create_info.pp_enabled_extension_names, + create_info.enabled_extension_count, + &[ + DEVICE_EXT_EXTERNAL_MEMORY, + DEVICE_EXT_EXTERNAL_MEMORY_WIN32, + DEVICE_EXT_DEDICATED_ALLOCATION, + DEVICE_EXT_GET_MEMORY_REQUIREMENTS2, + ], + ); + let mut augmented_info = create_info; + augmented_info.enabled_extension_count = augmented.count(); + augmented_info.pp_enabled_extension_names = augmented.as_ptr(); + + let mut external_memory_enabled = false; + let result = if want_external_memory { + let augmented_result = + (instance_fn.create_device)(physical_device, &augmented_info, p_allocator, p_device); + if augmented_result == vk::Result::SUCCESS { + external_memory_enabled = true; + augmented_result + } else { + (instance_fn.create_device)(physical_device, p_create_info, p_allocator, p_device) + } + } else { + (instance_fn.create_device)(physical_device, p_create_info, p_allocator, p_device) + }; + if result != vk::Result::SUCCESS { + return result; + } + let app_external_memory_win32 = extension_in_list( + create_info.pp_enabled_extension_names, + create_info.enabled_extension_count, + DEVICE_EXT_EXTERNAL_MEMORY_WIN32, + ); + let external_memory_enabled = external_memory_enabled || app_external_memory_win32; + vlog!( + "vkCreateDevice: external_memory_enabled={external_memory_enabled} (want={want_external_memory}, app_enabled={app_external_memory_win32})" + ); + let dedicated_allocation_enabled = external_memory_enabled + || extension_in_list( + create_info.pp_enabled_extension_names, + create_info.enabled_extension_count, + DEVICE_EXT_DEDICATED_ALLOCATION, + ); + + let device = *p_device; + GDPA_MAP.insert(device, gdpa); + let ash_device = ash::Device::load(instance_fn, device); + let khr_swapchain = khr::swapchain::Device::new(&instance_state.ash_instance, &ash_device); + let khr_external_memory_win32 = external_memory_enabled.then(|| { + khr::external_memory_win32::Device::new(&instance_state.ash_instance, &ash_device) + }); + let queue_families = instance_state + .ash_instance + .get_physical_device_queue_family_properties(physical_device); + DEVICE_MAP.insert( + device, + LayerDevice { + instance, + physical_device, + ash_device: ash_device.clone(), + khr_swapchain, + queue_families, + khr_external_memory_win32, + external_memory_enabled, + dedicated_allocation_enabled, + }, + ); + + if !create_info.p_queue_create_infos.is_null() { + let queue_infos = std::slice::from_raw_parts( + create_info.p_queue_create_infos, + create_info.queue_create_info_count as usize, + ); + for queue_info in queue_infos { + for queue_index in 0..queue_info.queue_count { + let mut queue = vk::Queue::null(); + (ash_device.fp_v1_0().get_device_queue)( + device, + queue_info.queue_family_index, + queue_index, + &mut queue, + ); + record_queue(device, queue, queue_info.queue_family_index); + } + } + } + + vk::Result::SUCCESS +} +const _: vk::PFN_vkCreateDevice = flux_vkCreateDevice; + +#[unsafe(no_mangle)] +unsafe extern "system" fn flux_vkDestroyDevice( + device: vk::Device, + p_allocator: *const vk::AllocationCallbacks, +) { + let Some(device_state) = destroy_device_state(device) else { + return; + }; + (device_state.ash_device.fp_v1_0().destroy_device)(device, p_allocator); +} +const _: vk::PFN_vkDestroyDevice = flux_vkDestroyDevice; + +#[unsafe(no_mangle)] +unsafe extern "system" fn flux_vkCreateWin32SurfaceKHR( + instance: vk::Instance, + p_create_info: *const vk::Win32SurfaceCreateInfoKHR, + p_allocator: *const vk::AllocationCallbacks, + p_surface: *mut vk::SurfaceKHR, +) -> vk::Result { + let Some(instance_state) = INSTANCE_MAP.get(&instance) else { + return vk::Result::ERROR_INITIALIZATION_FAILED; + }; + let result = (instance_state + .khr_win32_surface + .fp() + .create_win32_surface_khr)(instance, p_create_info, p_allocator, p_surface); + if result == vk::Result::SUCCESS && !p_surface.is_null() && !p_create_info.is_null() { + let surface = *p_surface; + SURFACE_MAP.insert( + surface, + SurfaceState { + instance, + hwnd: (*p_create_info).hwnd, + }, + ); + } + result +} +const _: vk::PFN_vkCreateWin32SurfaceKHR = flux_vkCreateWin32SurfaceKHR; + +#[unsafe(no_mangle)] +unsafe extern "system" fn flux_vkDestroySurfaceKHR( + instance: vk::Instance, + surface: vk::SurfaceKHR, + p_allocator: *const vk::AllocationCallbacks, +) { + SURFACE_MAP.remove(&surface); + if let Some(instance_state) = INSTANCE_MAP.get(&instance) { + (instance_state.khr_surface.fp().destroy_surface_khr)(instance, surface, p_allocator); + } +} +const _: vk::PFN_vkDestroySurfaceKHR = flux_vkDestroySurfaceKHR; + +#[unsafe(no_mangle)] +unsafe extern "system" fn flux_vkCreateSwapchainKHR( + device: vk::Device, + p_create_info: *const vk::SwapchainCreateInfoKHR, + p_allocator: *const vk::AllocationCallbacks, + p_swapchain: *mut vk::SwapchainKHR, +) -> vk::Result { + if p_create_info.is_null() || p_swapchain.is_null() { + return vk::Result::ERROR_INITIALIZATION_FAILED; + } + let Some(device_state) = DEVICE_MAP.get(&device) else { + return vk::Result::ERROR_INITIALIZATION_FAILED; + }; + let original_info = p_create_info.read(); + let original_can_capture = original_info + .image_usage + .contains(vk::ImageUsageFlags::TRANSFER_SRC); + let create_info = swapchain_create_info_with_transfer_src(original_info); + let mut used_transfer_src = true; + let mut result = (device_state.khr_swapchain.fp().create_swapchain_khr)( + device, + &create_info, + p_allocator, + p_swapchain, + ); + if result != vk::Result::SUCCESS { + used_transfer_src = original_can_capture; + let retry_info = swapchain_retry_create_info(original_info); + result = (device_state.khr_swapchain.fp().create_swapchain_khr)( + device, + &retry_info, + p_allocator, + p_swapchain, + ); + } + if result != vk::Result::SUCCESS { + return result; + } + + if let Some(old_swapchain) = + old_swapchain_to_remove_after_create(result, original_info.old_swapchain) + { + SWAPCHAIN_MAP.remove(&old_swapchain); + } + let swapchain = *p_swapchain; + let mut count = 0u32; + let get_result = (device_state.khr_swapchain.fp().get_swapchain_images_khr)( + device, + swapchain, + &mut count, + null_mut(), + ); + let mut images = Vec::new(); + if get_result == vk::Result::SUCCESS && count > 0 { + images.resize(count as usize, vk::Image::null()); + let image_result = (device_state.khr_swapchain.fp().get_swapchain_images_khr)( + device, + swapchain, + &mut count, + images.as_mut_ptr(), + ); + if image_result == vk::Result::SUCCESS || image_result == vk::Result::INCOMPLETE { + images.truncate(count as usize); + } else { + images.clear(); + } + } + let hwnd = SURFACE_MAP + .get(&original_info.surface) + .map(|surface| surface.hwnd) + .unwrap_or(0); + let present_layout = present_layout_for_mode(original_info.present_mode); + let shared_texture_allowed = + shared_texture_allowed_for_present_mode(original_info.present_mode); + let have_images = !images.is_empty(); + SWAPCHAIN_MAP.insert( + swapchain, + SwapchainState { + device, + hwnd, + extent: original_info.image_extent, + format: original_info.image_format, + images, + can_capture: used_transfer_src && hwnd != 0 && have_images, + present_layout, + shared_texture_allowed, + }, + ); + vk::Result::SUCCESS +} +const _: vk::PFN_vkCreateSwapchainKHR = flux_vkCreateSwapchainKHR; + +fn swapchain_create_info_with_transfer_src( + mut create_info: vk::SwapchainCreateInfoKHR, +) -> vk::SwapchainCreateInfoKHR { + create_info.image_usage |= vk::ImageUsageFlags::TRANSFER_SRC; + create_info +} + +fn swapchain_retry_create_info( + original_info: vk::SwapchainCreateInfoKHR, +) -> vk::SwapchainCreateInfoKHR { + original_info +} + +fn old_swapchain_to_remove_after_create( + result: vk::Result, + old_swapchain: vk::SwapchainKHR, +) -> Option { + (result == vk::Result::SUCCESS && old_swapchain != vk::SwapchainKHR::null()) + .then_some(old_swapchain) +} + +fn present_layout_for_mode(present_mode: vk::PresentModeKHR) -> vk::ImageLayout { + match present_mode { + vk::PresentModeKHR::SHARED_DEMAND_REFRESH + | vk::PresentModeKHR::SHARED_CONTINUOUS_REFRESH => vk::ImageLayout::SHARED_PRESENT_KHR, + _ => vk::ImageLayout::PRESENT_SRC_KHR, + } +} + +fn shared_texture_allowed_for_present_mode(present_mode: vk::PresentModeKHR) -> bool { + present_mode != vk::PresentModeKHR::IMMEDIATE +} + +#[unsafe(no_mangle)] +unsafe extern "system" fn flux_vkDestroySwapchainKHR( + device: vk::Device, + swapchain: vk::SwapchainKHR, + p_allocator: *const vk::AllocationCallbacks, +) { + SWAPCHAIN_MAP.remove(&swapchain); + if let Some(device_state) = DEVICE_MAP.get(&device) { + (device_state.khr_swapchain.fp().destroy_swapchain_khr)(device, swapchain, p_allocator); + } +} +const _: vk::PFN_vkDestroySwapchainKHR = flux_vkDestroySwapchainKHR; + +#[unsafe(no_mangle)] +unsafe extern "system" fn flux_vkGetSwapchainImagesKHR( + device: vk::Device, + swapchain: vk::SwapchainKHR, + p_swapchain_image_count: *mut u32, + p_swapchain_images: *mut vk::Image, +) -> vk::Result { + let Some(device_state) = DEVICE_MAP.get(&device) else { + return vk::Result::ERROR_INITIALIZATION_FAILED; + }; + let result = (device_state.khr_swapchain.fp().get_swapchain_images_khr)( + device, + swapchain, + p_swapchain_image_count, + p_swapchain_images, + ); + if (result == vk::Result::SUCCESS || result == vk::Result::INCOMPLETE) + && !p_swapchain_image_count.is_null() + && !p_swapchain_images.is_null() + { + let count = *p_swapchain_image_count as usize; + if let Some(mut swapchain_state) = SWAPCHAIN_MAP.get_mut(&swapchain) { + swapchain_state.images = std::slice::from_raw_parts(p_swapchain_images, count).to_vec(); + } + } + result +} +const _: vk::PFN_vkGetSwapchainImagesKHR = flux_vkGetSwapchainImagesKHR; + +#[unsafe(no_mangle)] +unsafe extern "system" fn flux_vkQueuePresentKHR( + queue: vk::Queue, + p_present_info: *const vk::PresentInfoKHR, +) -> vk::Result { + if p_present_info.is_null() { + return vk::Result::ERROR_INITIALIZATION_FAILED; + } + let present_info = *p_present_info; + let capture = try_capture_present(queue, &present_info); + let Some(queue_state) = QUEUE_MAP.get(&queue) else { + return vk::Result::ERROR_DEVICE_LOST; + }; + let Some(device_state) = DEVICE_MAP.get(&queue_state.device) else { + return vk::Result::ERROR_DEVICE_LOST; + }; + let chained_slot = capture.chained_wait; + if let Some(chained) = chained_slot { + let modified = present_info_with_chained_wait(present_info, &chained); + return (device_state.khr_swapchain.fp().queue_present_khr)(queue, &modified); + } + (device_state.khr_swapchain.fp().queue_present_khr)(queue, p_present_info) +} +const _: vk::PFN_vkQueuePresentKHR = flux_vkQueuePresentKHR; + +fn present_info_with_chained_wait<'a>( + mut present_info: vk::PresentInfoKHR<'a>, + chained: &'a vk::Semaphore, +) -> vk::PresentInfoKHR<'a> { + present_info.wait_semaphore_count = 1; + present_info.p_wait_semaphores = chained; + present_info +} + +#[unsafe(no_mangle)] +unsafe extern "system" fn flux_vkGetDeviceQueue( + device: vk::Device, + queue_family_index: u32, + queue_index: u32, + p_queue: *mut vk::Queue, +) { + if let Some(device_state) = DEVICE_MAP.get(&device) { + (device_state.ash_device.fp_v1_0().get_device_queue)( + device, + queue_family_index, + queue_index, + p_queue, + ); + if !p_queue.is_null() { + record_queue(device, *p_queue, queue_family_index); + } + } +} +const _: vk::PFN_vkGetDeviceQueue = flux_vkGetDeviceQueue; + +#[unsafe(no_mangle)] +unsafe extern "system" fn flux_vkGetDeviceQueue2( + device: vk::Device, + p_queue_info: *const vk::DeviceQueueInfo2, + p_queue: *mut vk::Queue, +) { + if let Some(device_state) = DEVICE_MAP.get(&device) { + (device_state.ash_device.fp_v1_1().get_device_queue2)(device, p_queue_info, p_queue); + if !p_queue_info.is_null() && !p_queue.is_null() { + record_queue(device, *p_queue, (*p_queue_info).queue_family_index); + } + } +} +const _: vk::PFN_vkGetDeviceQueue2 = flux_vkGetDeviceQueue2; + +#[cfg(test)] +mod tests { + use super::*; + use ash::vk::Handle; + + #[test] + fn format_mode_accepts_obs_capture_8bit_formats() { + assert_eq!( + format_mode(vk::Format::B8G8R8A8_UNORM), + Some(FormatMode::Bgra) + ); + assert_eq!( + format_mode(vk::Format::B8G8R8A8_SRGB), + Some(FormatMode::Bgra) + ); + assert_eq!( + format_mode(vk::Format::R8G8B8A8_UNORM), + Some(FormatMode::Rgba) + ); + assert_eq!( + format_mode(vk::Format::R8G8B8A8_SRGB), + Some(FormatMode::Rgba) + ); + assert_eq!(format_mode(vk::Format::A2B10G10R10_UNORM_PACK32), None); + } + + #[test] + fn hdr_format_flags_mark_ten_bit_and_float_formats() { + assert_eq!( + hdr_format_flags(vk::Format::A2B10G10R10_UNORM_PACK32), + GAME_CAPTURE_FLAG_TEN_BIT | GAME_CAPTURE_FLAG_HDR + ); + assert_eq!( + hdr_format_flags(vk::Format::A2R10G10B10_UNORM_PACK32), + GAME_CAPTURE_FLAG_TEN_BIT | GAME_CAPTURE_FLAG_HDR + ); + assert_eq!( + hdr_format_flags(vk::Format::R16G16B16A16_SFLOAT), + GAME_CAPTURE_FLAG_HDR + ); + assert_eq!(hdr_format_flags(vk::Format::B8G8R8A8_UNORM), 0); + } + + #[test] + fn present_layout_uses_shared_present_only_for_shared_modes() { + assert_eq!( + present_layout_for_mode(vk::PresentModeKHR::SHARED_DEMAND_REFRESH), + vk::ImageLayout::SHARED_PRESENT_KHR + ); + assert_eq!( + present_layout_for_mode(vk::PresentModeKHR::SHARED_CONTINUOUS_REFRESH), + vk::ImageLayout::SHARED_PRESENT_KHR + ); + assert_eq!( + present_layout_for_mode(vk::PresentModeKHR::FIFO), + vk::ImageLayout::PRESENT_SRC_KHR + ); + assert_eq!( + present_layout_for_mode(vk::PresentModeKHR::MAILBOX), + vk::ImageLayout::PRESENT_SRC_KHR + ); + } + + #[test] + fn immediate_present_mode_uses_cpu_fallback_after_real_shared_handle_failure() { + assert!(!shared_texture_allowed_for_present_mode( + vk::PresentModeKHR::IMMEDIATE + )); + assert!(shared_texture_allowed_for_present_mode( + vk::PresentModeKHR::FIFO + )); + assert!(shared_texture_allowed_for_present_mode( + vk::PresentModeKHR::MAILBOX + )); + } + + #[test] + fn shared_texture_reacquires_external_ownership_after_first_frame() { + let queue_family_index = 7; + assert_eq!( + shared_texture_dst_pre_queue_families(false, queue_family_index), + (vk::QUEUE_FAMILY_IGNORED, vk::QUEUE_FAMILY_IGNORED) + ); + assert_eq!( + shared_texture_dst_pre_queue_families(true, queue_family_index), + (vk::QUEUE_FAMILY_EXTERNAL, queue_family_index) + ); + } + + #[test] + fn augmented_swapchain_create_info_preserves_old_swapchain() { + let old = vk::SwapchainKHR::from_raw(0x55); + let original = vk::SwapchainCreateInfoKHR { + image_usage: vk::ImageUsageFlags::COLOR_ATTACHMENT, + old_swapchain: old, + ..Default::default() + }; + + let augmented = swapchain_create_info_with_transfer_src(original); + assert!( + augmented + .image_usage + .contains(vk::ImageUsageFlags::TRANSFER_SRC) + ); + assert!( + augmented + .image_usage + .contains(vk::ImageUsageFlags::COLOR_ATTACHMENT) + ); + assert_eq!(augmented.old_swapchain.as_raw(), old.as_raw()); + } + + #[test] + fn retry_swapchain_create_info_keeps_app_usage_and_old_swapchain() { + let old = vk::SwapchainKHR::from_raw(0x66); + let original = vk::SwapchainCreateInfoKHR { + image_usage: vk::ImageUsageFlags::COLOR_ATTACHMENT, + old_swapchain: old, + ..Default::default() + }; + + let retry = swapchain_retry_create_info(original); + assert_eq!(retry.image_usage, original.image_usage); + assert_eq!(retry.old_swapchain.as_raw(), old.as_raw()); + } + + #[test] + fn old_swapchain_is_removed_only_after_successful_recreate() { + let old = vk::SwapchainKHR::from_raw(0x77); + assert_eq!( + old_swapchain_to_remove_after_create(vk::Result::SUCCESS, old).map(|h| h.as_raw()), + Some(old.as_raw()) + ); + assert_eq!( + old_swapchain_to_remove_after_create(vk::Result::ERROR_INITIALIZATION_FAILED, old) + .map(|h| h.as_raw()), + None + ); + assert_eq!( + old_swapchain_to_remove_after_create(vk::Result::SUCCESS, vk::SwapchainKHR::null()) + .map(|h| h.as_raw()), + None + ); + } + + #[test] + fn present_wait_semaphore_slice_rejects_missing_pointer() { + let waits = [vk::Semaphore::from_raw(0x11), vk::Semaphore::from_raw(0x12)]; + let present_info = vk::PresentInfoKHR { + wait_semaphore_count: waits.len() as u32, + p_wait_semaphores: waits.as_ptr(), + ..Default::default() + }; + let slice = unsafe { present_wait_semaphores(&present_info) }; + assert_eq!( + slice.iter().map(|s| s.as_raw()).collect::>(), + vec![0x11, 0x12] + ); + + let missing_pointer = vk::PresentInfoKHR { + wait_semaphore_count: waits.len() as u32, + p_wait_semaphores: null(), + ..Default::default() + }; + assert!(unsafe { present_wait_semaphores(&missing_pointer) }.is_empty()); + } + + #[test] + fn chained_present_wait_replaces_wait_list_but_preserves_present_payload() { + let original_waits = [vk::Semaphore::from_raw(0x21), vk::Semaphore::from_raw(0x22)]; + let swapchains = [vk::SwapchainKHR::from_raw(0x31)]; + let image_indices = [3_u32]; + let mut results = [vk::Result::SUCCESS]; + let present_info = vk::PresentInfoKHR { + wait_semaphore_count: original_waits.len() as u32, + p_wait_semaphores: original_waits.as_ptr(), + swapchain_count: swapchains.len() as u32, + p_swapchains: swapchains.as_ptr(), + p_image_indices: image_indices.as_ptr(), + p_results: results.as_mut_ptr(), + ..Default::default() + }; + let chained = vk::Semaphore::from_raw(0x44); + + let modified = present_info_with_chained_wait(present_info, &chained); + + assert_eq!(modified.wait_semaphore_count, 1); + assert_eq!( + unsafe { *modified.p_wait_semaphores }.as_raw(), + chained.as_raw() + ); + assert_eq!(modified.swapchain_count, present_info.swapchain_count); + assert_eq!(modified.p_swapchains, present_info.p_swapchains); + assert_eq!(modified.p_image_indices, present_info.p_image_indices); + assert_eq!(modified.p_results, present_info.p_results); + } +} + +#[unsafe(no_mangle)] +unsafe extern "system" fn vkEnumerateInstanceLayerProperties( + p_property_count: *mut u32, + p_properties: *mut vk::LayerProperties, +) -> vk::Result { + if p_property_count.is_null() { + return vk::Result::ERROR_INITIALIZATION_FAILED; + } + if p_properties.is_null() { + *p_property_count = 1; + return vk::Result::SUCCESS; + } + if *p_property_count == 0 { + return vk::Result::INCOMPLETE; + } + *p_property_count = 1; + let mut properties = vk::LayerProperties { + spec_version: vk::API_VERSION_1_0, + implementation_version: 1, + ..Default::default() + }; + let name = LAYER_NAME; + std::ptr::copy_nonoverlapping( + name.as_ptr() as *const c_char, + properties.layer_name.as_mut_ptr(), + name.len().min(properties.layer_name.len()), + ); + *p_properties = properties; + vk::Result::SUCCESS +} diff --git a/fluxer_desktop/native/win-process-loopback/Cargo.lock b/fluxer_desktop/native/win-process-loopback/Cargo.lock new file mode 100644 index 0000000..3401ce1 --- /dev/null +++ b/fluxer_desktop/native/win-process-loopback/Cargo.lock @@ -0,0 +1,545 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer", + "const-oid", + "crypto-common", +] + +[[package]] +name = "fluxer_desktop_native" +version = "0.1.0" +dependencies = [ + "sha2", +] + +[[package]] +name = "fluxer_gpu_rebuild" +version = "0.0.0" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "fluxer_rt_thread" +version = "0.0.0" +dependencies = [ + "libc", + "windows", +] + +[[package]] +name = "fluxer_screen_frame_bus" +version = "0.0.0" +dependencies = [ + "fluxer_gpu_rebuild", + "parking_lot", +] + +[[package]] +name = "fluxer_win_process_loopback" +version = "0.0.0" +dependencies = [ + "fluxer_desktop_native", + "fluxer_rt_thread", + "fluxer_screen_frame_bus", + "napi", + "napi-build", + "napi-derive", + "windows", + "windows-core", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "typenum", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "typenum" +version = "1.20.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] diff --git a/fluxer_desktop/native/win-process-loopback/Cargo.toml b/fluxer_desktop/native/win-process-loopback/Cargo.toml new file mode 100644 index 0000000..60eb339 --- /dev/null +++ b/fluxer_desktop/native/win-process-loopback/Cargo.toml @@ -0,0 +1,40 @@ +[package] +name = "fluxer_win_process_loopback" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +fluxer_desktop_native = {path = "../rust"} +fluxer_rt_thread = {path = "../rt-thread"} +fluxer_screen_frame_bus = {path = "../screen-frame-bus"} +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8"]} +napi-derive = "3.5.6" + +[target.'cfg(target_os = "windows")'.dependencies] +windows = {version = "0.62.2", features = [ + "Win32_Foundation", + "Win32_Media_Audio", + "Win32_Media_KernelStreaming", + "Win32_Media_Multimedia", + "Win32_Security", + "Win32_Storage_FileSystem", + "Win32_System_Com", + "Win32_System_Com_StructuredStorage", + "Win32_System_Diagnostics_ToolHelp", + "Win32_System_Performance", + "Win32_System_Threading", + "Win32_System_Variant", + "Win32_UI_WindowsAndMessaging", +]} +windows-core = "0.62.2" + +[build-dependencies] +napi-build = "2.3.2" diff --git a/fluxer_desktop/native/win-process-loopback/build.rs b/fluxer_desktop/native/win-process-loopback/build.rs new file mode 100644 index 0000000..d45de40 --- /dev/null +++ b/fluxer_desktop/native/win-process-loopback/build.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + napi_build::setup(); +} diff --git a/fluxer_desktop/native/win-process-loopback/index.d.ts b/fluxer_desktop/native/win-process-loopback/index.d.ts new file mode 100644 index 0000000..f960c36 --- /dev/null +++ b/fluxer_desktop/native/win-process-loopback/index.d.ts @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import {EventEmitter} from 'node:events'; + +export interface ProcessLoopbackOptions { + includeProcessTree?: boolean; + captureScope?: 'process' | 'system' | 'session-mixer'; + winCaptureScope?: 'process' | 'system' | 'session-mixer'; + scope?: 'process' | 'system' | 'session-mixer'; + sampleRate?: 48000; + channels?: 2; +} + +export interface AudioFrame { + samples: Float32Array; + sampleRate: number; + channels: number; + timestampUs: bigint; +} + +export declare interface ProcessLoopback { + on(event: 'frame', listener: (frame: AudioFrame) => void): this; + on(event: 'error', listener: (err: Error) => void): this; + on(event: 'closed', listener: () => void): this; + on(event: 'started', listener: () => void): this; + on(event: string | symbol, listener: (...args: Array) => void): this; + off(event: 'frame', listener: (frame: AudioFrame) => void): this; + off(event: 'error', listener: (err: Error) => void): this; + off(event: 'closed', listener: () => void): this; + off(event: 'started', listener: () => void): this; + off(event: string | symbol, listener: (...args: Array) => void): this; + emit(event: 'frame', frame: AudioFrame): boolean; + emit(event: 'error', err: Error): boolean; + emit(event: 'closed'): boolean; + emit(event: 'started'): boolean; +} + +export declare class ProcessLoopback extends EventEmitter { + constructor(pid: number, opts?: ProcessLoopbackOptions); + + start(): Promise; + + stop(): void; +} + +export declare function isSupported(): boolean; + +export interface WinProcessLoopbackBackendInfo { + backend: string; + supported: boolean; + reason: string; + processSupported: boolean; + systemSupported: boolean; + systemExcludesSelf: boolean; + processIncludeSupported: boolean; + processExcludeSupported: boolean; + sessionMixerSupported: boolean; + systemLoopbackMode: 'process-exclude' | 'session-mixer' | 'unavailable'; + minWindowsBuild: number; + minWindowsVersionLabel: string; + detectedWindowsBuild?: number; +} + +export declare function getBackendInfo(): WinProcessLoopbackBackendInfo; + +export declare function pidFromHwnd(hwnd: bigint): number; + +export declare function resolveAudioRootPid(pid: number): number; diff --git a/fluxer_desktop/native/win-process-loopback/index.js b/fluxer_desktop/native/win-process-loopback/index.js new file mode 100644 index 0000000..8276f6e --- /dev/null +++ b/fluxer_desktop/native/win-process-loopback/index.js @@ -0,0 +1,249 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {EventEmitter} = require('node:events'); +const {existsSync} = require('node:fs'); +const {join, sep} = require('node:path'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); +const MODULE_NAME = '@fluxer/win-process-loopback'; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName(arch) { + switch (arch) { + case 'x64': + return 'win-process-loopback.win32-x64-msvc.node'; + case 'arm64': + return 'win-process-loopback.win32-arm64-msvc.node'; + default: + return null; + } +} + +let binding = null; +let loadError = null; + +if (process.platform === 'win32') { + const nativeRoot = resolveNativeRoot(); + const fileName = nativeFileName(process.arch); + if (!fileName) { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot, + packageDir: __dirname, + reason: `unsupported Windows architecture: ${process.arch}`, + }); + throw loadError; + } else { + const nativePath = join(nativeRoot, fileName); + const loaded = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + probe: false, + }); + binding = loaded.binding; + loadError = loaded.loadError; + if (loadError) throw loadError; + } +} else { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: `not supported on platform ${process.platform}`, + }); +} + +function isSupported() { + if (!binding) return false; + try { + return Boolean(binding.isSupported()); + } catch { + return false; + } +} + +function getBackendInfo() { + if (!binding) { + const reason = + process.platform !== 'win32' + ? `@fluxer/win-process-loopback only supports Windows, got ${process.platform}. Microsoft's documented process-loopback minimum is Windows build 20348.` + : `@fluxer/win-process-loopback native binary unavailable: ${loadError?.message ?? 'unknown reason'}`; + return { + backend: 'win-process-loopback', + supported: false, + reason, + processSupported: false, + systemSupported: false, + systemExcludesSelf: false, + processIncludeSupported: false, + processExcludeSupported: false, + sessionMixerSupported: false, + systemLoopbackMode: 'unavailable', + minWindowsBuild: 20348, + minWindowsVersionLabel: 'Windows build 20348 (documented)', + detectedWindowsBuild: undefined, + }; + } + try { + return binding.getBackendInfo(); + } catch (err) { + return { + backend: 'win-process-loopback', + supported: false, + reason: `@fluxer/win-process-loopback getBackendInfo failed: ${err?.message ?? String(err)}`, + processSupported: false, + systemSupported: false, + systemExcludesSelf: false, + processIncludeSupported: false, + processExcludeSupported: false, + sessionMixerSupported: false, + systemLoopbackMode: 'unavailable', + minWindowsBuild: 20348, + minWindowsVersionLabel: 'Windows build 20348 (documented)', + detectedWindowsBuild: undefined, + }; + } +} + +function pidFromHwnd(hwnd) { + if (!binding) return 0; + try { + return binding.pidFromHwnd(hwnd); + } catch { + return 0; + } +} + +function resolveAudioRootPid(pid) { + if (!binding) return pid; + try { + return binding.resolveAudioRootPid(pid); + } catch { + return pid; + } +} + +class ProcessLoopback extends EventEmitter { + constructor(pid, opts = {}) { + super(); + if (!binding) { + throw loadError ?? new Error('@fluxer/win-process-loopback binding unavailable'); + } + const captureScope = opts.captureScope ?? opts.winCaptureScope ?? opts.scope ?? 'process'; + const includeProcessTree = + captureScope === 'system' || captureScope === 'session-mixer' ? false : opts.includeProcessTree !== false; + const sampleRate = opts.sampleRate; + const channels = opts.channels; + this._stopped = false; + this._started = false; + this._startPromise = null; + this._resolveStart = null; + this._rejectStart = null; + this._native = new binding.ProcessLoopback( + pid, + {includeProcessTree, sampleRate, channels, captureScope}, + (frame) => this.emit('frame', frame), + (err) => { + if (!this._started && this._rejectStart) { + this._rejectStart(err); + this._clearStartWaiters(); + } + this.emit('error', err); + }, + () => { + if (this._stopped) return; + this._stopped = true; + if (!this._started && this._rejectStart) { + this._rejectStart(new Error('ProcessLoopback closed before audio capture started')); + this._clearStartWaiters(); + } + queueMicrotask(() => { + try { + this._native.dispose(); + } catch {} + }); + this.emit('closed'); + }, + () => { + this._started = true; + if (this._resolveStart) { + this._resolveStart(); + this._clearStartWaiters(); + } + this.emit('started'); + }, + ); + } + + _clearStartWaiters() { + this._resolveStart = null; + this._rejectStart = null; + } + + setScreenAudioSink(handle) { + if (typeof this._native.setScreenAudioSink !== 'function') return false; + try { + return this._native.setScreenAudioSink(handle) !== false; + } catch { + return false; + } + } + + clearScreenAudioSink() { + if (typeof this._native.clearScreenAudioSink === 'function') { + this._native.clearScreenAudioSink(); + } + } + + start() { + if (this._stopped) return Promise.resolve(); + if (this._started) return Promise.resolve(); + if (this._startPromise) return this._startPromise; + this._startPromise = new Promise((resolve, reject) => { + this._resolveStart = resolve; + this._rejectStart = reject; + try { + this._native.start(); + } catch (error) { + this._clearStartWaiters(); + reject(error); + } + }); + return this._startPromise; + } + + stop() { + if (this._stopped) return; + this._stopped = true; + this.clearScreenAudioSink(); + if (!this._started && this._rejectStart) { + this._rejectStart(new Error('ProcessLoopback stopped before audio capture started')); + this._clearStartWaiters(); + } + this._native.dispose(); + } +} + +function __setBindingForTests(nextBinding, nextLoadError = null) { + binding = nextBinding; + loadError = nextLoadError; +} + +module.exports = { + isSupported, + getBackendInfo, + pidFromHwnd, + resolveAudioRootPid, + ProcessLoopback, + get loadError() { + return loadError; + }, + __setBindingForTests, +}; diff --git a/fluxer_desktop/native/win-process-loopback/index.test.mjs b/fluxer_desktop/native/win-process-loopback/index.test.mjs new file mode 100644 index 0000000..96bd7b4 --- /dev/null +++ b/fluxer_desktop/native/win-process-loopback/index.test.mjs @@ -0,0 +1,272 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +import assert from 'node:assert/strict'; +import {EventEmitter} from 'node:events'; +import {createRequire} from 'node:module'; +import {test} from 'node:test'; + +const requireSrc = createRequire(import.meta.url); +const modulePath = requireSrc.resolve('./index.js'); + +function freshModule() { + delete requireSrc.cache[modulePath]; + return requireSrc('./index.js'); +} + +test('module loads without throwing on non-Windows hosts', () => { + const mod = freshModule(); + assert.equal(typeof mod.isSupported, 'function'); + assert.equal(typeof mod.pidFromHwnd, 'function'); + assert.equal(typeof mod.resolveAudioRootPid, 'function'); + assert.equal(typeof mod.ProcessLoopback, 'function'); +}); + +test('non-Windows hosts surface a load error', {skip: process.platform === 'win32'}, () => { + const mod = freshModule(); + assert.ok(mod.loadError instanceof Error, 'expected loadError to be set on non-Windows'); + assert.match(mod.loadError.message, /not supported on platform/i); +}); + +test('isSupported / pidFromHwnd / resolveAudioRootPid fail soft when binding is absent', () => { + const mod = freshModule(); + mod.__setBindingForTests(null, new Error('test: binary missing')); + assert.equal(mod.isSupported(), false); + assert.equal(mod.pidFromHwnd(123n), 0); + assert.equal(mod.resolveAudioRootPid(1234), 1234); + assert.deepEqual( + { + processSupported: mod.getBackendInfo().processSupported, + systemSupported: mod.getBackendInfo().systemSupported, + sessionMixerSupported: mod.getBackendInfo().sessionMixerSupported, + systemLoopbackMode: mod.getBackendInfo().systemLoopbackMode, + }, + { + processSupported: false, + systemSupported: false, + sessionMixerSupported: false, + systemLoopbackMode: 'unavailable', + }, + ); +}); + +test('constructing ProcessLoopback without a binding throws the recorded loadError', () => { + const mod = freshModule(); + const recorded = new Error('synthetic: binary unavailable'); + mod.__setBindingForTests(null, recorded); + assert.throws( + () => new mod.ProcessLoopback(1234, {}), + (error) => error === recorded, + ); +}); + +test('isSupported swallows binding-side exceptions and returns false', () => { + const mod = freshModule(); + mod.__setBindingForTests({ + isSupported: () => { + throw new Error('binding crashed during isSupported'); + }, + pidFromHwnd: () => 0, + resolveAudioRootPid: (pid) => pid, + ProcessLoopback: function FakeProcessLoopback() {}, + }); + assert.equal(mod.isSupported(), false); +}); + +test('ProcessLoopback wires native frame/error/closed events to the EventEmitter', async () => { + const mod = freshModule(); + let frameCb; + let errorCb; + let closedCb; + let startedCb; + class FakeNative { + constructor(pid, opts, onFrame, onError, onClosed, onStarted) { + this.pid = pid; + this.opts = opts; + frameCb = onFrame; + errorCb = onError; + closedCb = onClosed; + startedCb = onStarted; + } + + start() { + this.started = true; + } + + dispose() { + this.disposed = true; + } + } + mod.__setBindingForTests({ + isSupported: () => true, + pidFromHwnd: () => 0, + resolveAudioRootPid: (pid) => pid, + ProcessLoopback: FakeNative, + }); + const loopback = new mod.ProcessLoopback(4242, {includeProcessTree: false}); + assert.ok(loopback instanceof EventEmitter); + const frames = []; + const errors = []; + let closedCount = 0; + loopback.on('frame', (frame) => frames.push(frame)); + loopback.on('error', (error) => errors.push(error)); + loopback.on('closed', () => closedCount++); + const startPromise = loopback.start(); + startedCb(); + await startPromise; + frameCb({samples: new Float32Array(2), sampleRate: 48000, channels: 2, timestampUs: 0}); + errorCb(new Error('boom')); + closedCb(); + closedCb(); + assert.equal(frames.length, 1); + assert.equal(errors.length, 1); + assert.equal(closedCount, 1); +}); + +test('ProcessLoopback.start waits for the native started callback', async () => { + const mod = freshModule(); + let startedCb; + mod.__setBindingForTests({ + isSupported: () => true, + pidFromHwnd: () => 0, + resolveAudioRootPid: (pid) => pid, + ProcessLoopback: function FakeNative(_pid, _opts, _onFrame, _onError, _onClosed, onStarted) { + startedCb = onStarted; + return {start() {}, dispose() {}}; + }, + }); + const loopback = new mod.ProcessLoopback(4242); + let resolved = false; + const startPromise = loopback.start().then(() => { + resolved = true; + }); + await Promise.resolve(); + assert.equal(resolved, false); + startedCb(); + await startPromise; + assert.equal(resolved, true); +}); + +test('ProcessLoopback.start rejects if native capture errors before start', async () => { + const mod = freshModule(); + let errorCb; + mod.__setBindingForTests({ + isSupported: () => true, + pidFromHwnd: () => 0, + resolveAudioRootPid: (pid) => pid, + ProcessLoopback: function FakeNative(_pid, _opts, _onFrame, onError) { + errorCb = onError; + return {start() {}, dispose() {}}; + }, + }); + const loopback = new mod.ProcessLoopback(4242); + loopback.on('error', () => {}); + const startPromise = loopback.start(); + const error = new Error('activation failed'); + errorCb(error); + await assert.rejects(startPromise, (actual) => actual === error); +}); + +test('opts default includeProcessTree to true and forward sample/channel options', () => { + const mod = freshModule(); + let observedOpts; + mod.__setBindingForTests({ + isSupported: () => true, + pidFromHwnd: () => 0, + resolveAudioRootPid: (pid) => pid, + ProcessLoopback: function FakeNative(_pid, opts) { + observedOpts = opts; + return {start() {}, dispose() {}}; + }, + }); + const ctor = mod.ProcessLoopback; + void new ctor(1, {sampleRate: 44100, channels: 1}); + assert.deepEqual(observedOpts, {includeProcessTree: true, sampleRate: 44100, channels: 1, captureScope: 'process'}); + void new ctor(1, {includeProcessTree: false}); + assert.equal(observedOpts.includeProcessTree, false); +}); + +test('system capture scope forwards EXCLUDE target process tree mode', () => { + const mod = freshModule(); + let observedOpts; + mod.__setBindingForTests({ + isSupported: () => true, + pidFromHwnd: () => 0, + resolveAudioRootPid: (pid) => pid, + ProcessLoopback: function FakeNative(_pid, opts) { + observedOpts = opts; + return {start() {}, dispose() {}}; + }, + }); + void new mod.ProcessLoopback(1, {captureScope: 'system', includeProcessTree: true}); + assert.deepEqual(observedOpts, { + includeProcessTree: false, + sampleRate: undefined, + channels: undefined, + captureScope: 'system', + }); +}); + +test('session mixer capture scope is forwarded and never captures the target process tree directly', () => { + const mod = freshModule(); + let observedOpts; + mod.__setBindingForTests({ + isSupported: () => true, + pidFromHwnd: () => 0, + resolveAudioRootPid: (pid) => pid, + ProcessLoopback: function FakeNative(_pid, opts) { + observedOpts = opts; + return {start() {}, dispose() {}}; + }, + }); + void new mod.ProcessLoopback(1, {captureScope: 'session-mixer', includeProcessTree: true}); + assert.deepEqual(observedOpts, { + includeProcessTree: false, + sampleRate: undefined, + channels: undefined, + captureScope: 'session-mixer', + }); +}); + +function loopbackWithNativeSinkResult(setScreenAudioSink) { + const mod = freshModule(); + mod.__setBindingForTests({ + isSupported: () => true, + pidFromHwnd: () => 0, + resolveAudioRootPid: (pid) => pid, + ProcessLoopback: function FakeNative() { + return {start() {}, dispose() {}, setScreenAudioSink}; + }, + }); + return new mod.ProcessLoopback(4242); +} + +test('setScreenAudioSink reports true when the native attach succeeds', () => { + const loopback = loopbackWithNativeSinkResult(() => undefined); + assert.equal(loopback.setScreenAudioSink({}), true); +}); + +test('setScreenAudioSink reports false when the native attach returns false', () => { + const loopback = loopbackWithNativeSinkResult(() => false); + assert.equal(loopback.setScreenAudioSink({}), false); +}); + +test('setScreenAudioSink reports false when the native attach throws', () => { + const loopback = loopbackWithNativeSinkResult(() => { + throw new Error('attach failed'); + }); + assert.equal(loopback.setScreenAudioSink({}), false); +}); + +test('setScreenAudioSink reports false when the native method is missing', () => { + const mod = freshModule(); + mod.__setBindingForTests({ + isSupported: () => true, + pidFromHwnd: () => 0, + resolveAudioRootPid: (pid) => pid, + ProcessLoopback: function FakeNative() { + return {start() {}, dispose() {}}; + }, + }); + const loopback = new mod.ProcessLoopback(4242); + assert.equal(loopback.setScreenAudioSink({}), false); +}); diff --git a/fluxer_desktop/native/win-process-loopback/loader-diagnostics.cjs b/fluxer_desktop/native/win-process-loopback/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/win-process-loopback/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/win-process-loopback/package.json b/fluxer_desktop/native/win-process-loopback/package.json new file mode 100644 index 0000000..4fb8bf2 --- /dev/null +++ b/fluxer_desktop/native/win-process-loopback/package.json @@ -0,0 +1,28 @@ +{ + "name": "@fluxer/win-process-loopback", + "version": "0.0.0", + "description": "", + "private": true, + "license": "AGPL-3.0-or-later", + "os": [ + "win32" + ], + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "win-process-loopback.win32-x64-msvc.node", + "win-process-loopback.win32-arm64-msvc.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "cargo test --manifest-path Cargo.toml && node --test index.test.mjs", + "test:loader": "node --test index.test.mjs" + } +} diff --git a/fluxer_desktop/native/win-process-loopback/src/lib.rs b/fluxer_desktop/native/win-process-loopback/src/lib.rs new file mode 100644 index 0000000..df3988b --- /dev/null +++ b/fluxer_desktop/native/win-process-loopback/src/lib.rs @@ -0,0 +1,2521 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::ptr; +use std::sync::Arc; + +use fluxer_desktop_native::win_process_loopback::audio_contract; +use fluxer_screen_frame_bus::{NativeScreenFrameSinkHandle, NativeScreenFrameSinkHandleRef}; +use napi::{ + Env, JsValue, + bindgen_prelude::{ + Error, Float32Array, Function, Result, Status, ToNapiValue, Unknown, ValueType, + }, + sys, + threadsafe_function::{ThreadsafeFunction, ThreadsafeFunctionCallMode, UnknownReturnValue}, +}; +use napi_derive::napi; + +pub mod pcm_pool; + +use crate::pcm_pool::{PCM_SLOT_SAMPLES_MAX, PooledPcmFrame}; + +fn retain_screen_audio_sink_handle( + value: Unknown<'_>, +) -> Result> { + if value.get_type()? != ValueType::External { + return Err(Error::new( + Status::InvalidArg, + "ProcessLoopback.setScreenAudioSink expects a native external sink handle", + )); + } + let raw_value = value.value(); + let mut data: *mut std::ffi::c_void = ptr::null_mut(); + let status = unsafe { sys::napi_get_value_external(raw_value.env, raw_value.value, &mut data) }; + if status != sys::Status::napi_ok || data.is_null() { + return Err(Error::new( + Status::InvalidArg, + "ProcessLoopback.setScreenAudioSink received an empty native external sink handle", + )); + } + let handle = unsafe { + NativeScreenFrameSinkHandle::retain_from_raw(data.cast::()) + } + .ok_or_else(|| { + Error::new( + Status::InvalidArg, + "ProcessLoopback.setScreenAudioSink received an invalid native sink handle", + ) + })?; + Ok(Arc::new(handle)) +} + +pub struct ErrorMessage(pub String); + +impl ToNapiValue for ErrorMessage { + unsafe fn to_napi_value(env: sys::napi_env, val: Self) -> Result { + let napi_err = Error::new(Status::GenericFailure, val.0); + let js_err = napi::JsError::from(napi_err); + Ok(unsafe { js_err.into_value(env) }) + } +} + +#[napi(object)] +pub struct LoopbackFrame { + pub samples: napi::bindgen_prelude::Float32Array, + pub sample_rate: u32, + pub channels: u32, + pub timestamp_us: i64, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum CaptureScope { + Process, + System, + SessionMixer, +} + +#[derive(Debug, Clone, Copy)] +#[cfg_attr(not(target_os = "windows"), allow(dead_code))] +struct LoopbackOptions { + include_tree: bool, + capture_scope: CaptureScope, +} + +#[derive(Debug, Clone)] +struct ProcessLoopbackRuntimeProbe { + include_supported: bool, + include_detail: Option, + exclude_supported: bool, + exclude_detail: Option, +} + +impl Default for LoopbackOptions { + fn default() -> Self { + Self { + include_tree: true, + capture_scope: CaptureScope::Process, + } + } +} + +fn validate_options(raw: RawOptions) -> Result { + if let Some(sample_rate) = raw.sample_rate + && !audio_contract::validate_sample_rate(sample_rate) + { + return Err(Error::new( + Status::InvalidArg, + "unsupported sampleRate; expected 48000", + )); + } + if let Some(channels) = raw.channels + && !audio_contract::validate_channels(channels) + { + return Err(Error::new( + Status::InvalidArg, + "unsupported channels; expected 2", + )); + } + let capture_scope = match raw.capture_scope.as_deref() { + Some("session-mixer") => CaptureScope::SessionMixer, + Some("system") => CaptureScope::System, + Some("process") | None => CaptureScope::Process, + Some(other) => { + return Err(Error::new( + Status::InvalidArg, + format!("unknown captureScope: {other}"), + )); + } + }; + let include_tree = match capture_scope { + CaptureScope::System | CaptureScope::SessionMixer => false, + CaptureScope::Process => raw.include_process_tree.unwrap_or(true), + }; + Ok(LoopbackOptions { + include_tree, + capture_scope, + }) +} + +#[derive(Debug, Default)] +struct RawOptions { + include_process_tree: Option, + capture_scope: Option, + sample_rate: Option, + channels: Option, +} + +fn read_raw_options(env: &Env, value: Unknown) -> Result { + use napi::ValueType; + let value_type = value.get_type()?; + if value_type != ValueType::Object { + return Ok(RawOptions::default()); + } + let object: napi::bindgen_prelude::Object = unsafe { value.cast() }?; + Ok(RawOptions { + include_process_tree: object.get("includeProcessTree").ok().flatten(), + capture_scope: object.get("captureScope").ok().flatten(), + sample_rate: object.get("sampleRate").ok().flatten(), + channels: object.get("channels").ok().flatten(), + }) + .map(|mut opts| { + let _ = env; + + opts.capture_scope = opts.capture_scope.map(|s| s.to_ascii_lowercase()); + opts + }) +} + +#[cfg_attr(not(target_os = "windows"), allow(dead_code))] +enum FrameSamples { + Pooled(PooledPcmFrame), + Owned(Vec), +} + +#[cfg_attr(not(target_os = "windows"), allow(dead_code))] +struct FramePayload { + samples: FrameSamples, + timestamp_us: i64, +} + +fn pooled_pcm_into_float32_array(slot: PooledPcmFrame) -> Float32Array { + let (ptr, len, slot) = slot.into_external_parts(); + debug_assert!(!ptr.is_null()); + debug_assert!(len <= PCM_SLOT_SAMPLES_MAX); + if ptr.is_null() || len == 0 || len > PCM_SLOT_SAMPLES_MAX { + drop(slot); + let empty: Vec = Vec::new(); + return Float32Array::new(empty); + } + unsafe { + Float32Array::with_external_data(ptr, len, move |_data, _len| { + let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { + drop(slot); + })); + }) + } +} + +fn loopback_frame_sample_truncation(len: usize) -> usize { + let channels = usize::from(audio_contract::TARGET_CHANNELS); + if channels == 0 { + return 0; + } + len - (len % channels) +} + +fn report_loopback_frame_truncation_once(original_len: usize, kept_len: usize) { + use std::sync::atomic::{AtomicBool, Ordering}; + static REPORTED: AtomicBool = AtomicBool::new(false); + if REPORTED.swap(true, Ordering::Relaxed) { + return; + } + eprintln!( + "win-process-loopback: dropping {} trailing screen-audio sample(s) to keep whole {}-channel frames", + original_len.saturating_sub(kept_len), + audio_contract::TARGET_CHANNELS + ); +} + +fn owned_samples_into_whole_frame_array(mut samples: Vec) -> Float32Array { + let kept = loopback_frame_sample_truncation(samples.len()); + if kept != samples.len() { + report_loopback_frame_truncation_once(samples.len(), kept); + samples.truncate(kept); + } + samples.into() +} + +fn frame_payload_into_loopback_frame(payload: FramePayload) -> LoopbackFrame { + let samples = match payload.samples { + FrameSamples::Pooled(slot) => pooled_pcm_into_float32_array(slot), + FrameSamples::Owned(samples) => owned_samples_into_whole_frame_array(samples), + }; + LoopbackFrame { + samples, + sample_rate: audio_contract::TARGET_SAMPLE_RATE, + channels: u32::from(audio_contract::TARGET_CHANNELS), + timestamp_us: payload.timestamp_us, + } +} + +fn empty_loopback_frame(timestamp_us: i64) -> LoopbackFrame { + LoopbackFrame { + samples: Vec::::new().into(), + sample_rate: audio_contract::TARGET_SAMPLE_RATE, + channels: u32::from(audio_contract::TARGET_CHANNELS), + timestamp_us, + } +} + +fn guard_frame_callback(timestamp_us: i64, build: F) -> LoopbackFrame +where + F: FnOnce() -> LoopbackFrame, +{ + match std::panic::catch_unwind(std::panic::AssertUnwindSafe(build)) { + Ok(frame) => frame, + Err(_) => { + report_loopback_frame_callback_panic_once(); + empty_loopback_frame(timestamp_us) + } + } +} + +fn frame_callback_into_loopback_frame(payload: FramePayload) -> LoopbackFrame { + let timestamp_us = payload.timestamp_us; + guard_frame_callback(timestamp_us, move || { + frame_payload_into_loopback_frame(payload) + }) +} + +fn report_loopback_frame_callback_panic_once() { + use std::sync::atomic::{AtomicBool, Ordering}; + static REPORTED: AtomicBool = AtomicBool::new(false); + if REPORTED.swap(true, Ordering::Relaxed) { + return; + } + eprintln!( + "win-process-loopback: screen-audio frame callback panicked; dropping frame instead of aborting the process" + ); +} + +type FrameTsfn = Arc< + ThreadsafeFunction< + FramePayload, + UnknownReturnValue, + LoopbackFrame, + Status, + false, + true, + FRAME_QUEUE_LIMIT, + >, +>; +type ErrorTsfn = Arc< + ThreadsafeFunction< + ErrorMessage, + UnknownReturnValue, + ErrorMessage, + Status, + false, + true, + ERROR_QUEUE_LIMIT, + >, +>; +type VoidTsfn = + Arc>; +const FRAME_QUEUE_LIMIT: usize = 64; +const ERROR_QUEUE_LIMIT: usize = 8; +const LIFECYCLE_QUEUE_LIMIT: usize = 8; + +#[cfg_attr(not(target_os = "windows"), allow(dead_code))] +struct Callbacks { + on_frame: FrameTsfn, + on_error: ErrorTsfn, + on_closed: VoidTsfn, + on_started: Option, + screen_audio_sink: std::sync::RwLock>>, +} + +#[cfg_attr(not(target_os = "windows"), allow(dead_code))] +impl Callbacks { + fn set_screen_audio_sink(&self, sink: Arc) { + if let Ok(mut guard) = self.screen_audio_sink.write() { + *guard = Some(sink); + } + } + + fn clear_screen_audio_sink(&self) { + if let Ok(mut guard) = self.screen_audio_sink.write() { + *guard = None; + } + } + + fn try_emit_frame_to_sink(&self, frame: &FramePayload) -> bool { + let Ok(guard) = self.screen_audio_sink.read() else { + return false; + }; + let Some(sink) = guard.as_ref() else { + return false; + }; + let samples: &[f32] = match &frame.samples { + FrameSamples::Pooled(slot) => slot.data_slice(), + FrameSamples::Owned(owned) => owned.as_slice(), + }; + let channels = u32::from(audio_contract::TARGET_CHANNELS); + if channels == 0 || samples.is_empty() || samples.len() % (channels as usize) != 0 { + return true; + } + let frames = samples.len() as u32 / channels; + sink.enqueue_screen_audio_f32( + samples, + frames, + channels, + audio_contract::TARGET_SAMPLE_RATE, + frame.timestamp_us, + ); + true + } + + fn emit_frame(&self, frame: FramePayload) { + if self.try_emit_frame_to_sink(&frame) { + return; + } + let _ = self + .on_frame + .call(frame, ThreadsafeFunctionCallMode::NonBlocking); + } + + fn emit_error(&self, message: impl Into) { + let _ = self.on_error.call( + ErrorMessage(message.into()), + ThreadsafeFunctionCallMode::NonBlocking, + ); + } + + fn emit_closed(&self) { + let _ = self + .on_closed + .call((), ThreadsafeFunctionCallMode::NonBlocking); + } + + fn emit_started(&self) { + if let Some(started) = self.on_started.as_ref() { + let _ = started.call((), ThreadsafeFunctionCallMode::NonBlocking); + } + } +} + +fn build_frame_tsfn( + env: &Env, + callback: Function, +) -> Result { + let _ = env; + callback + .build_threadsafe_function::() + .weak::() + .callee_handled::() + .max_queue_size::() + .build_callback(|ctx| Ok(frame_callback_into_loopback_frame(ctx.value))) + .map(Arc::new) + .map_err(clone_napi_err) +} + +fn build_error_tsfn( + env: &Env, + callback: Function, +) -> Result { + let _ = env; + callback + .build_threadsafe_function::() + .weak::() + .callee_handled::() + .max_queue_size::() + .build() + .map(Arc::new) + .map_err(clone_napi_err) +} + +fn clone_napi_err(err: napi::Error) -> Error { + Error::new(Status::GenericFailure, err.to_string()) +} + +fn build_void_tsfn(env: &Env, callback: Function<(), UnknownReturnValue>) -> Result { + let _ = env; + callback + .build_threadsafe_function::<()>() + .weak::() + .callee_handled::() + .max_queue_size::() + .build() + .map(Arc::new) + .map_err(clone_napi_err) +} + +#[napi] +pub struct ProcessLoopback { + inner: platform::Inner, +} + +#[napi] +impl ProcessLoopback { + #[allow(clippy::too_many_arguments)] + #[napi(constructor)] + pub fn new( + env: Env, + pid: u32, + opts: Unknown, + on_frame: Function, + on_error: Function, + on_closed: Function<(), UnknownReturnValue>, + on_started: Option>, + ) -> Result { + let raw = read_raw_options(&env, opts)?; + let options = validate_options(raw)?; + let callbacks = Callbacks { + on_frame: build_frame_tsfn(&env, on_frame)?, + on_error: build_error_tsfn(&env, on_error)?, + on_closed: build_void_tsfn(&env, on_closed)?, + on_started: on_started.map(|cb| build_void_tsfn(&env, cb)).transpose()?, + screen_audio_sink: std::sync::RwLock::new(None), + }; + Ok(Self { + inner: platform::Inner::new(pid, options, callbacks)?, + }) + } + + #[napi(js_name = "setScreenAudioSink")] + pub fn set_screen_audio_sink(&self, sink_handle: Unknown<'_>) -> Result<()> { + let sink = retain_screen_audio_sink_handle(sink_handle)?; + if !sink.supports_screen_audio() { + return Err(Error::new( + Status::InvalidArg, + "ProcessLoopback.setScreenAudioSink handle does not support screen audio", + )); + } + self.inner.set_screen_audio_sink(sink); + Ok(()) + } + + #[napi(js_name = "clearScreenAudioSink")] + pub fn clear_screen_audio_sink(&self) { + self.inner.clear_screen_audio_sink(); + } + + #[napi] + pub fn start(&self) -> Result<()> { + self.inner.start() + } + + #[napi] + pub fn stop(&self) { + self.inner.stop(); + } + + #[napi] + pub fn dispose(&self) { + self.inner.dispose(); + } +} + +impl Drop for ProcessLoopback { + fn drop(&mut self) { + self.inner.dispose(); + } +} + +#[napi(js_name = "isSupported")] +pub fn is_supported() -> bool { + platform::is_supported() +} + +#[napi(object, js_name = "WinProcessLoopbackBackendInfo")] +pub struct WinProcessLoopbackBackendInfo { + pub backend: String, + pub supported: bool, + pub reason: String, + #[napi(js_name = "processSupported")] + pub process_supported: bool, + #[napi(js_name = "systemSupported")] + pub system_supported: bool, + #[napi(js_name = "systemExcludesSelf")] + pub system_excludes_self: bool, + #[napi(js_name = "processIncludeSupported")] + pub process_include_supported: bool, + #[napi(js_name = "processExcludeSupported")] + pub process_exclude_supported: bool, + #[napi(js_name = "sessionMixerSupported")] + pub session_mixer_supported: bool, + #[napi(js_name = "systemLoopbackMode")] + pub system_loopback_mode: String, + #[napi(js_name = "minWindowsBuild")] + pub min_windows_build: u32, + #[napi(js_name = "minWindowsVersionLabel")] + pub min_windows_version_label: String, + #[napi(js_name = "detectedWindowsBuild")] + pub detected_windows_build: Option, +} + +const MIN_WINDOWS_VERSION_LABEL: &str = "Windows build 20348 (Microsoft process-loopback minimum)"; + +fn classify_windows_process_loopback( + detected: Option, + min_build: u32, + runtime_supported: bool, + runtime_detail: Option<&str>, +) -> (bool, String) { + let probe_detail = runtime_detail + .filter(|detail| !detail.is_empty()) + .map(|detail| format!(" Probe detail: {detail}")) + .unwrap_or_default(); + + if runtime_supported { + return ( + true, + match detected { + Some(build) if build < min_build => format!( + "Process loopback activation succeeded on Windows build {build}, \ + below Microsoft's documented {MIN_WINDOWS_VERSION_LABEL}. \ + Fluxer is enabling it because the runtime API probe passed." + ), + Some(build) => format!( + "Process loopback activation succeeded on Windows build {build} \ + (documented minimum: {MIN_WINDOWS_VERSION_LABEL})." + ), + None => format!( + "Process loopback activation succeeded, but the Windows build could not \ + be detected (documented minimum: {MIN_WINDOWS_VERSION_LABEL})." + ), + }, + ); + } + + match detected { + None => ( + false, + format!( + "@fluxer/win-process-loopback only supports Windows. \ + Per-process audio loopback requires {MIN_WINDOWS_VERSION_LABEL}; \ + ActivateAudioInterfaceAsync's per-process virtual device is unavailable on \ + this host.{probe_detail}" + ), + ), + Some(build) if build >= min_build => ( + false, + format!( + "Windows build {build} meets Microsoft's documented \ + {MIN_WINDOWS_VERSION_LABEL}, but Fluxer's runtime process-loopback probe \ + failed. Per-process audio loopback is unavailable.{probe_detail}" + ), + ), + Some(build) => ( + false, + format!( + "Microsoft documents process loopback as requiring \ + {MIN_WINDOWS_VERSION_LABEL}. This host reports Windows build {build}, \ + and Fluxer's runtime process-loopback probe failed. Fluxer must not use \ + system-wide endpoint loopback because it cannot exclude Fluxer's WebRTC \ + playback.{probe_detail}" + ), + ), + } +} + +fn classify_windows_capabilities( + detected: Option, + min_build: u32, + include_supported: bool, + include_detail: Option<&str>, + exclude_supported: bool, + exclude_detail: Option<&str>, +) -> (bool, bool, bool, bool, bool, bool, bool, String, String) { + let (process_include_supported, include_reason) = + classify_windows_process_loopback(detected, min_build, include_supported, include_detail); + let (process_exclude_supported, exclude_reason) = + classify_windows_process_loopback(detected, min_build, exclude_supported, exclude_detail); + let session_mixer_supported = process_include_supported; + let process_supported = process_include_supported; + let system_supported = process_exclude_supported || session_mixer_supported; + let system_excludes_self = system_supported; + let system_loopback_mode = if process_exclude_supported { + "process-exclude".to_owned() + } else if session_mixer_supported { + "session-mixer".to_owned() + } else { + "unavailable".to_owned() + }; + let supported = process_supported || system_supported; + let reason = if supported { + if process_exclude_supported { + "Per-process audio loopback is supported; desktop audio uses process-exclude loopback." + .to_owned() + } else { + "Per-process audio loopback include mode is supported; desktop audio uses Fluxer's session mixer fallback." + .to_owned() + } + } else if include_reason == exclude_reason { + include_reason + } else { + format!("Include probe: {include_reason} Exclude probe: {exclude_reason}") + }; + ( + supported, + process_supported, + system_supported, + system_excludes_self, + process_include_supported, + process_exclude_supported, + session_mixer_supported, + system_loopback_mode, + reason, + ) +} + +#[napi(js_name = "getBackendInfo")] +pub fn get_backend_info() -> WinProcessLoopbackBackendInfo { + let detected = platform::detected_build(); + let runtime_probe = platform::process_loopback_probe(); + let min_build = + fluxer_desktop_native::win_process_loopback::windows_version::PROCESS_LOOPBACK_MIN_BUILD; + let ( + supported, + process_supported, + system_supported, + system_excludes_self, + process_include_supported, + process_exclude_supported, + session_mixer_supported, + system_loopback_mode, + reason, + ) = classify_windows_capabilities( + detected, + min_build, + runtime_probe.include_supported, + runtime_probe.include_detail.as_deref(), + runtime_probe.exclude_supported, + runtime_probe.exclude_detail.as_deref(), + ); + WinProcessLoopbackBackendInfo { + backend: "win-process-loopback".to_owned(), + supported, + reason, + process_supported, + system_supported, + system_excludes_self, + process_include_supported, + process_exclude_supported, + session_mixer_supported, + system_loopback_mode, + min_windows_build: min_build, + min_windows_version_label: MIN_WINDOWS_VERSION_LABEL.to_owned(), + detected_windows_build: detected, + } +} + +#[napi(js_name = "pidFromHwnd")] +pub fn pid_from_hwnd(hwnd: napi::bindgen_prelude::BigInt) -> u32 { + let (_, value, _) = hwnd.get_u64(); + platform::pid_from_hwnd(value) +} + +#[napi(js_name = "resolveAudioRootPid")] +pub fn resolve_audio_root_pid(pid: u32) -> u32 { + platform::resolve_audio_root_pid(pid) +} + +#[cfg(test)] +mod tests { + use super::*; + + fn raw_with_scope(scope: Option<&str>) -> RawOptions { + RawOptions { + include_process_tree: None, + capture_scope: scope.map(|s| s.to_string()), + sample_rate: None, + channels: None, + } + } + + #[test] + fn defaults_include_tree_under_process_scope() { + let opts = validate_options(RawOptions::default()).expect("defaults validate"); + assert!(opts.include_tree); + assert_eq!(opts.capture_scope, CaptureScope::Process); + } + + #[test] + fn system_scope_forces_exclude_target_tree() { + let opts = + validate_options(raw_with_scope(Some("system"))).expect("system scope validates"); + assert!(!opts.include_tree); + assert_eq!(opts.capture_scope, CaptureScope::System); + } + + #[test] + fn session_mixer_scope_ignores_target_tree_flag() { + let mut raw = raw_with_scope(Some("session-mixer")); + raw.include_process_tree = Some(true); + let opts = validate_options(raw).expect("session mixer scope validates"); + assert!(!opts.include_tree); + assert_eq!(opts.capture_scope, CaptureScope::SessionMixer); + } + + #[test] + fn process_scope_respects_include_tree_override() { + let mut raw = raw_with_scope(Some("process")); + raw.include_process_tree = Some(false); + let opts = validate_options(raw).expect("process scope validates"); + assert!(!opts.include_tree); + } + + #[test] + fn unknown_capture_scope_is_rejected() { + let err = validate_options(raw_with_scope(Some("global"))) + .expect_err("unknown scope should fail validation"); + assert_eq!(err.status, Status::InvalidArg); + } + + #[test] + fn unsupported_sample_rate_is_rejected() { + let raw = RawOptions { + sample_rate: Some(44_100), + ..Default::default() + }; + let err = validate_options(raw).expect_err("44.1k should be rejected"); + assert_eq!(err.status, Status::InvalidArg); + assert_eq!(err.reason, "unsupported sampleRate; expected 48000"); + } + + #[test] + fn unsupported_channel_count_is_rejected() { + let raw = RawOptions { + channels: Some(1), + ..Default::default() + }; + let err = validate_options(raw).expect_err("mono should be rejected"); + assert_eq!(err.status, Status::InvalidArg); + assert_eq!(err.reason, "unsupported channels; expected 2"); + } + + #[test] + fn backend_info_classifier_rejects_unknown_host() { + let (supported, reason) = + classify_windows_process_loopback(None, 20_348, false, Some("probe unavailable")); + assert!(!supported); + assert!(reason.contains("only supports Windows")); + assert!(reason.contains("build 20348")); + assert!(reason.contains("probe unavailable")); + } + + #[test] + fn backend_info_classifier_rejects_old_build_when_probe_fails() { + let (supported, reason) = + classify_windows_process_loopback(Some(19_045), 20_348, false, Some("E_NOTIMPL")); + assert!(!supported); + assert!(reason.contains("build 19045")); + assert!(reason.contains("Windows build 20348")); + assert!(reason.contains("E_NOTIMPL")); + } + + #[test] + fn backend_info_classifier_accepts_old_build_when_probe_succeeds() { + let (supported, reason) = + classify_windows_process_loopback(Some(19_045), 20_348, true, None); + assert!(supported); + assert!(reason.contains("build 19045")); + assert!(reason.contains("runtime API probe passed")); + } + + #[test] + fn backend_info_classifier_accepts_exact_floor_when_probe_succeeds() { + let (supported, reason) = + classify_windows_process_loopback(Some(20_348), 20_348, true, None); + assert!(supported); + assert!(reason.contains("build 20348")); + } + + #[test] + fn backend_info_classifier_rejects_newer_build_when_probe_fails() { + let (supported, reason) = + classify_windows_process_loopback(Some(26_100), 20_348, false, Some("timeout")); + assert!(!supported); + assert!(reason.contains("runtime process-loopback probe failed")); + assert!(reason.contains("timeout")); + } + + #[test] + fn backend_info_classifier_accepts_newer_build_when_probe_succeeds() { + let (supported, _) = classify_windows_process_loopback(Some(26_100), 20_348, true, None); + assert!(supported); + } + + #[test] + fn backend_capabilities_reject_windows_10_for_isolated_audio() { + let ( + supported, + process_supported, + system_supported, + system_excludes_self, + process_include_supported, + process_exclude_supported, + session_mixer_supported, + system_loopback_mode, + reason, + ) = classify_windows_capabilities( + Some(19_045), + 20_348, + false, + Some("include E_NOTIMPL"), + false, + Some("exclude E_NOTIMPL"), + ); + assert!(!supported); + assert!(!process_supported); + assert!(!system_supported); + assert!(!system_excludes_self); + assert!(!process_include_supported); + assert!(!process_exclude_supported); + assert!(!session_mixer_supported); + assert_eq!(system_loopback_mode, "unavailable"); + assert!(reason.contains("build 19045")); + assert!(reason.contains("runtime process-loopback probe failed")); + } + + #[test] + fn backend_capabilities_use_session_mixer_when_only_include_mode_works() { + let ( + supported, + process_supported, + system_supported, + system_excludes_self, + process_include_supported, + process_exclude_supported, + session_mixer_supported, + system_loopback_mode, + _, + ) = classify_windows_capabilities( + Some(19_045), + 20_348, + true, + Some("include ok"), + false, + Some("exclude E_NOTIMPL"), + ); + assert!(supported); + assert!(process_supported); + assert!(system_supported); + assert!(system_excludes_self); + assert!(process_include_supported); + assert!(!process_exclude_supported); + assert!(session_mixer_supported); + assert_eq!(system_loopback_mode, "session-mixer"); + } + + #[test] + fn backend_capabilities_keep_runtime_supported_windows_process_exclude_system_mode() { + let ( + supported, + process_supported, + system_supported, + system_excludes_self, + process_include_supported, + process_exclude_supported, + session_mixer_supported, + system_loopback_mode, + _, + ) = classify_windows_capabilities(Some(22_000), 20_348, true, None, true, None); + assert!(supported); + assert!(process_supported); + assert!(system_supported); + assert!(system_excludes_self); + assert!(process_include_supported); + assert!(process_exclude_supported); + assert!(session_mixer_supported); + assert_eq!(system_loopback_mode, "process-exclude"); + } + + #[test] + fn accepts_canonical_48k_stereo() { + let raw = RawOptions { + include_process_tree: Some(true), + capture_scope: Some("process".to_string()), + sample_rate: Some(48_000), + channels: Some(2), + }; + validate_options(raw).expect("canonical 48k stereo should validate"); + } + + #[test] + fn truncation_keeps_only_whole_stereo_frames() { + assert_eq!(loopback_frame_sample_truncation(0), 0); + assert_eq!(loopback_frame_sample_truncation(1), 0); + assert_eq!(loopback_frame_sample_truncation(2), 2); + assert_eq!(loopback_frame_sample_truncation(3), 2); + assert_eq!(loopback_frame_sample_truncation(5), 4); + assert_eq!(loopback_frame_sample_truncation(10), 10); + } + + #[test] + fn owned_odd_length_samples_truncate_without_panic() { + let odd = vec![0.1_f32, 0.2, 0.3]; + let array = owned_samples_into_whole_frame_array(odd); + assert_eq!(array.len(), 2); + } + + #[test] + fn owned_even_length_samples_are_preserved() { + let even = vec![0.1_f32, 0.2, 0.3, 0.4]; + let array = owned_samples_into_whole_frame_array(even); + assert_eq!(array.len(), 4); + } + + #[test] + fn frame_callback_truncates_owned_payload_without_panic() { + let payload = FramePayload { + samples: FrameSamples::Owned(vec![0.5_f32, 0.6, 0.7]), + timestamp_us: 1_234, + }; + let frame = frame_callback_into_loopback_frame(payload); + assert_eq!(frame.samples.len(), 2); + assert_eq!(frame.channels, u32::from(audio_contract::TARGET_CHANNELS)); + assert_eq!(frame.timestamp_us, 1_234); + } + + #[test] + fn frame_callback_swallows_a_panicking_conversion() { + let frame = guard_frame_callback(99, || panic!("forced frame conversion panic")); + assert_eq!(frame.samples.len(), 0); + assert_eq!(frame.timestamp_us, 99); + assert_eq!(frame.sample_rate, audio_contract::TARGET_SAMPLE_RATE); + assert_eq!(frame.channels, u32::from(audio_contract::TARGET_CHANNELS)); + } +} + +#[cfg(target_os = "windows")] +mod platform { + use super::{ + Callbacks, CaptureScope, FRAME_QUEUE_LIMIT, FramePayload, FrameSamples, LoopbackOptions, + ProcessLoopbackRuntimeProbe, + }; + use crate::pcm_pool::{PCM_SLOT_SAMPLES_MAX, PcmFramePool, PcmPoolError, PooledPcmFrame}; + use fluxer_desktop_native::win_process_loopback::{ + audio_contract, process_tree, session_mixer, + }; + use fluxer_rt_thread::{PriorityProfile, RealtimePriorityGuard}; + use napi::bindgen_prelude::{Error, Result, Status}; + use std::{ + collections::BTreeSet, + ffi::c_void, + mem::{ManuallyDrop, size_of, zeroed}, + sync::{ + Arc, Mutex, MutexGuard, OnceLock, + atomic::{AtomicBool, Ordering}, + }, + thread::{self, JoinHandle}, + time::{Duration, Instant}, + }; + use windows::Win32::Foundation::{ + CloseHandle, GetLastError, HANDLE, HWND, INVALID_HANDLE_VALUE, WAIT_TIMEOUT, + }; + use windows::Win32::Media::Audio::{ + AUDCLNT_BUFFERFLAGS_SILENT, AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR, AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, + AUDCLNT_STREAMFLAGS_LOOPBACK, AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY, + AUDIOCLIENT_ACTIVATION_PARAMS, AUDIOCLIENT_ACTIVATION_PARAMS_0, + AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK, AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS, + ActivateAudioInterfaceAsync, AudioSessionStateExpired, DEVICE_STATE_ACTIVE, + IActivateAudioInterfaceAsyncOperation, IActivateAudioInterfaceCompletionHandler, + IActivateAudioInterfaceCompletionHandler_Impl, IAudioCaptureClient, IAudioClient, + IAudioSessionControl, IAudioSessionControl2, IAudioSessionManager2, IMMDeviceEnumerator, + MMDeviceEnumerator, PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE, + PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE, WAVE_FORMAT_PCM, WAVEFORMATEX, + WAVEFORMATEXTENSIBLE, WAVEFORMATEXTENSIBLE_0, eRender, + }; + use windows::Win32::Media::KernelStreaming::WAVE_FORMAT_EXTENSIBLE; + use windows::Win32::Media::Multimedia::KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; + use windows::Win32::Storage::FileSystem::{ + GetFileVersionInfoSizeW, GetFileVersionInfoW, VS_FIXEDFILEINFO, VerQueryValueW, + }; + use windows::Win32::System::Com::StructuredStorage::{ + PROPVARIANT, PROPVARIANT_0, PROPVARIANT_0_0, PROPVARIANT_0_0_0, + }; + use windows::Win32::System::Com::{ + BLOB, CLSCTX_ALL, COINIT_MULTITHREADED, CoCreateInstance, CoInitializeEx, CoUninitialize, + IAgileObject, IAgileObject_Impl, + }; + use windows::Win32::System::Diagnostics::ToolHelp::{ + CreateToolhelp32Snapshot, PROCESSENTRY32W, Process32FirstW, Process32NextW, + TH32CS_SNAPPROCESS, + }; + use windows::Win32::System::Threading::{ + CreateEventW, GetCurrentProcessId, INFINITE, SetEvent, WaitForMultipleObjects, + }; + use windows::Win32::System::Variant::VT_BLOB; + use windows::Win32::UI::WindowsAndMessaging::GetWindowThreadProcessId; + use windows::core::{Interface, PCWSTR, implement}; + + const PROCESS_LOOPBACK_DEVICE: &[u16] = &[ + b'V' as u16, + b'A' as u16, + b'D' as u16, + b'\\' as u16, + b'P' as u16, + b'r' as u16, + b'o' as u16, + b'c' as u16, + b'e' as u16, + b's' as u16, + b's' as u16, + b'_' as u16, + b'L' as u16, + b'o' as u16, + b'o' as u16, + b'p' as u16, + b'b' as u16, + b'a' as u16, + b'c' as u16, + b'k' as u16, + 0, + ]; + + const KERNEL32_PATH_UTF16: &[u16] = { + const BYTES: &[u8] = b"C:\\Windows\\System32\\kernel32.dll\0"; + + macro_rules! literal_utf16 { + ($($c:literal),* $(,)?) => { &[$($c as u16),*] }; + } + let _ = BYTES; + literal_utf16![ + 'C', ':', '\\', 'W', 'i', 'n', 'd', 'o', 'w', 's', '\\', 'S', 'y', 's', 't', 'e', 'm', + '3', '2', '\\', 'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l', '\0' + ] + }; + + const BACKSLASH_UTF16: &[u16] = &[b'\\' as u16, 0]; + const PROCESS_LOOPBACK_PROBE_TIMEOUT_MS: u32 = 1_500; + const SESSION_MIXER_REFRESH_INTERVAL: Duration = Duration::from_millis(1_000); + const SESSION_MIXER_WAIT_TIMEOUT_MS: u32 = 250; + const MAX_SESSION_MIXER_CAPTURES: usize = 48; + const MAX_SESSION_MIXER_EMIT_FRAMES: usize = audio_contract::TARGET_SAMPLE_RATE as usize / 2; + const PROCESS_LOOPBACK_ACTIVATION_ATTEMPTS: usize = 3; + + struct OwnedHandle(HANDLE); + + impl OwnedHandle { + fn new(handle: HANDLE) -> Option { + if handle.is_invalid() { + None + } else { + Some(Self(handle)) + } + } + + fn raw(&self) -> HANDLE { + self.0 + } + } + + impl Drop for OwnedHandle { + fn drop(&mut self) { + if !self.0.is_invalid() && self.0 != INVALID_HANDLE_VALUE { + unsafe { + let _ = CloseHandle(self.0); + } + } + } + } + + unsafe impl Send for OwnedHandle {} + unsafe impl Sync for OwnedHandle {} + + #[derive(Copy, Clone, Debug, Eq, PartialEq)] + enum CaptureSampleFormat { + Float32, + Pcm16, + } + + impl CaptureSampleFormat { + fn label(self) -> &'static str { + match self { + Self::Float32 => "float32", + Self::Pcm16 => "pcm16", + } + } + } + + struct WorkerState { + worker: Option>, + stop_event: Option>, + } + + fn lock_worker(worker: &Mutex) -> MutexGuard<'_, WorkerState> { + worker + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()) + } + + pub(crate) struct Inner { + pid: u32, + options: LoopbackOptions, + callbacks: Arc, + worker: Mutex, + stop_signal: Arc, + disposed: AtomicBool, + } + + impl Inner { + pub(crate) fn new( + pid: u32, + options: LoopbackOptions, + callbacks: Callbacks, + ) -> Result { + let is_excluding_self_system_scope = matches!( + options.capture_scope, + CaptureScope::System | CaptureScope::SessionMixer + ) && !options.include_tree; + if is_self_or_descendant_pid(pid) && !is_excluding_self_system_scope { + return Err(Error::new( + Status::GenericFailure, + "ProcessLoopback refuses to capture Fluxer's own process tree", + )); + } + Ok(Self { + pid, + options, + callbacks: Arc::new(callbacks), + worker: Mutex::new(WorkerState { + worker: None, + stop_event: None, + }), + stop_signal: Arc::new(AtomicBool::new(false)), + disposed: AtomicBool::new(false), + }) + } + + pub(crate) fn set_screen_audio_sink( + &self, + sink: std::sync::Arc, + ) { + self.callbacks.set_screen_audio_sink(sink); + } + + pub(crate) fn clear_screen_audio_sink(&self) { + self.callbacks.clear_screen_audio_sink(); + } + + pub(crate) fn start(&self) -> Result<()> { + if self.disposed.load(Ordering::Acquire) { + return Err(Error::new( + Status::GenericFailure, + "ProcessLoopback is disposed", + )); + } + let mut guard = lock_worker(&self.worker); + if guard.worker.is_some() { + return Ok(()); + } + self.stop_signal.store(false, Ordering::SeqCst); + + let stop_event = unsafe { CreateEventW(None, true, false, PCWSTR::null()) }.map_err( + |err: windows::core::Error| { + hresult_error("CreateEventW for stop signal", err.code().0) + }, + )?; + let stop_event = Arc::new( + OwnedHandle::new(stop_event) + .ok_or_else(|| generic("CreateEventW returned invalid handle"))?, + ); + guard.stop_event = Some(stop_event.clone()); + + let pid = self.pid; + let include_tree = self.options.include_tree; + let capture_scope = self.options.capture_scope; + let stop_signal = self.stop_signal.clone(); + let stop_event_for_thread = stop_event.clone(); + let callbacks = self.callbacks.clone(); + + let join = match thread::Builder::new() + .name("fluxer-win-process-loopback".to_string()) + .spawn(move || { + let result = capture_thread( + pid, + include_tree, + capture_scope, + &callbacks, + stop_event_for_thread.raw(), + stop_signal.as_ref(), + ); + if let Err(message) = result { + callbacks.emit_error(message); + } + callbacks.emit_closed(); + }) { + Ok(join) => join, + Err(err) => { + guard.stop_event = None; + return Err(generic(format!( + "failed to spawn loopback worker thread: {err}" + ))); + } + }; + guard.worker = Some(join); + Ok(()) + } + + pub(crate) fn stop(&self) { + let join = { + let mut guard = lock_worker(&self.worker); + self.stop_signal.store(true, Ordering::SeqCst); + if let Some(event) = guard.stop_event.as_ref() { + unsafe { + let _ = SetEvent(event.raw()); + } + } + guard.worker.take() + }; + if let Some(join) = join { + let _ = join.join(); + } + let mut guard = lock_worker(&self.worker); + guard.stop_event = None; + } + + pub(crate) fn dispose(&self) { + if self.disposed.swap(true, Ordering::AcqRel) { + return; + } + self.stop(); + } + } + + fn generic(message: impl Into) -> Error { + Error::new(Status::GenericFailure, message.into()) + } + + fn hresult_error(stage: &str, hr: i32) -> Error { + generic(format!( + "Windows loopback activation failed at {stage}: hr=0x{:08x}", + hr as u32 + )) + } + + struct CompletionShared { + done_event: Arc, + result: Mutex>>, + } + + unsafe impl Send for CompletionShared {} + unsafe impl Sync for CompletionShared {} + + #[implement(IActivateAudioInterfaceCompletionHandler, IAgileObject)] + struct CompletionHandler { + shared: Arc, + } + + impl IAgileObject_Impl for CompletionHandler_Impl {} + + impl IActivateAudioInterfaceCompletionHandler_Impl for CompletionHandler_Impl { + fn ActivateCompleted( + &self, + operation: windows::core::Ref, + ) -> windows::core::Result<()> { + let outcome = unsafe { + let Some(op) = operation.as_ref() else { + return Ok(()); + }; + let mut activate_hr = windows::core::HRESULT(0); + let mut unknown_raw: Option = None; + let method_hr = op.GetActivateResult(&mut activate_hr, &mut unknown_raw); + if let Err(err) = method_hr { + Err(err.code().0) + } else if activate_hr.0 < 0 { + Err(activate_hr.0) + } else if let Some(unknown) = unknown_raw { + match unknown.cast::() { + Ok(client) => Ok(client), + Err(err) => Err(err.code().0), + } + } else { + Err(0) + } + }; + let mut slot = self + .shared + .result + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()); + *slot = Some(outcome); + drop(slot); + unsafe { + let _ = SetEvent(self.shared.done_event.raw()); + } + Ok(()) + } + } + + fn make_blob_propvariant(data: *mut u8, size: u32) -> PROPVARIANT { + PROPVARIANT { + Anonymous: PROPVARIANT_0 { + Anonymous: ManuallyDrop::new(PROPVARIANT_0_0 { + vt: VT_BLOB, + wReserved1: 0, + wReserved2: 0, + wReserved3: 0, + Anonymous: PROPVARIANT_0_0_0 { + blob: BLOB { + cbSize: size, + pBlobData: data, + }, + }, + }), + }, + } + } + + fn activate_loopback_client( + pid: u32, + include_tree: bool, + capture_scope: CaptureScope, + stop_event: HANDLE, + ) -> std::result::Result { + match capture_scope { + CaptureScope::Process => { + activate_process_loopback_client(pid, include_tree, stop_event) + } + CaptureScope::System => activate_process_loopback_client(pid, false, stop_event), + CaptureScope::SessionMixer => activate_process_loopback_client(pid, true, stop_event), + } + } + + fn activate_process_loopback_client( + pid: u32, + include_tree: bool, + stop_event: HANDLE, + ) -> std::result::Result { + let mut last_error: Option = None; + for attempt in 0..PROCESS_LOOPBACK_ACTIVATION_ATTEMPTS { + match activate_process_loopback_client_with_timeout( + pid, + include_tree, + stop_event, + INFINITE, + ) { + Ok(client) => return Ok(client), + Err(err) => { + if !is_retryable_activation_error(&err) + || attempt + 1 == PROCESS_LOOPBACK_ACTIVATION_ATTEMPTS + { + return Err(err); + } + last_error = Some(err); + let backoff_ms = 50 + (attempt as u32 * 100); + let wait = unsafe { WaitForMultipleObjects(&[stop_event], false, backoff_ms) }; + if wait.0 == 0 { + return Err(generic("ProcessLoopback activation cancelled")); + } + } + } + } + Err(last_error.unwrap_or_else(|| generic("ProcessLoopback activation failed"))) + } + + fn is_retryable_activation_error(err: &Error) -> bool { + let reason = err.reason.to_ascii_lowercase(); + reason.contains("getactivateresult") + && (reason.contains("0x80040155") + || reason.contains("0x80070002") + || reason.contains("0x80070490")) + } + + fn activate_process_loopback_client_with_timeout( + pid: u32, + include_tree: bool, + stop_event: HANDLE, + timeout_ms: u32, + ) -> std::result::Result { + let done = unsafe { CreateEventW(None, true, false, PCWSTR::null()) } + .map_err(|err: windows::core::Error| hresult_error("CreateEventW", err.code().0))?; + let done = Arc::new( + OwnedHandle::new(done) + .ok_or_else(|| generic("CreateEventW returned invalid handle"))?, + ); + + let shared = Arc::new(CompletionShared { + done_event: done.clone(), + result: Mutex::new(None), + }); + let handler: IActivateAudioInterfaceCompletionHandler = CompletionHandler { + shared: shared.clone(), + } + .into(); + + let loopback_mode = if include_tree { + PROCESS_LOOPBACK_MODE_INCLUDE_TARGET_PROCESS_TREE + } else { + PROCESS_LOOPBACK_MODE_EXCLUDE_TARGET_PROCESS_TREE + }; + let mut params = AUDIOCLIENT_ACTIVATION_PARAMS { + ActivationType: AUDIOCLIENT_ACTIVATION_TYPE_PROCESS_LOOPBACK, + Anonymous: AUDIOCLIENT_ACTIVATION_PARAMS_0 { + ProcessLoopbackParams: AUDIOCLIENT_PROCESS_LOOPBACK_PARAMS { + TargetProcessId: pid, + ProcessLoopbackMode: loopback_mode, + }, + }, + }; + + let prop = ManuallyDrop::new(make_blob_propvariant( + &mut params as *mut AUDIOCLIENT_ACTIVATION_PARAMS as *mut u8, + size_of::() as u32, + )); + + let operation = unsafe { + ActivateAudioInterfaceAsync( + PCWSTR(PROCESS_LOOPBACK_DEVICE.as_ptr()), + &IAudioClient::IID, + Some(&*prop), + &handler, + ) + } + .map_err(|err| hresult_error("ActivateAudioInterfaceAsync", err.code().0))?; + let _operation_lifetime = operation; + + let handles = [done.raw(), stop_event]; + let wait = unsafe { WaitForMultipleObjects(&handles, false, timeout_ms) }; + if wait.0 == u32::MAX { + return Err(generic("WaitForMultipleObjects failed during activation")); + } + if wait == WAIT_TIMEOUT { + return Err(generic("ProcessLoopback activation probe timed out")); + } + if wait.0 == 1 { + return Err(generic("ProcessLoopback activation cancelled")); + } + if wait.0 != 0 { + return Err(generic( + "WaitForMultipleObjects returned an unexpected wait result", + )); + } + let outcome = shared + .result + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()) + .take() + .ok_or_else(|| generic("activation completion signalled without a result"))?; + outcome.map_err(|hr| hresult_error("GetActivateResult", hr)) + } + + fn capture_thread( + pid: u32, + include_tree: bool, + capture_scope: CaptureScope, + callbacks: &Callbacks, + stop_event: HANDLE, + stop_signal: &AtomicBool, + ) -> std::result::Result<(), String> { + let _com = ComApartment::initialize().map_err(|err| err.reason.to_string())?; + let _rt_guard = RealtimePriorityGuard::acquire(PriorityProfile::Audio).ok(); + if capture_scope == CaptureScope::SessionMixer { + return session_mixer_capture_thread(callbacks, stop_event, stop_signal); + } + + let client = match activate_loopback_client(pid, include_tree, capture_scope, stop_event) { + Ok(client) => client, + Err(err) if stop_signal.load(Ordering::SeqCst) => return Ok(()), + Err(err) => return Err(err.reason.to_string()), + }; + + let sample_format = initialize_capture_client(&client)?; + + let buffer_event_raw = unsafe { CreateEventW(None, false, false, PCWSTR::null()) } + .map_err(|err: windows::core::Error| { + format!( + "CreateEventW(buffer) failed: hr=0x{:08x}", + err.code().0 as u32 + ) + })?; + let buffer_event = OwnedHandle::new(buffer_event_raw) + .ok_or_else(|| "CreateEventW(buffer) returned invalid handle".to_string())?; + + unsafe { + client.SetEventHandle(buffer_event.raw()).map_err(|err| { + format!( + "AudioClient::SetEventHandle failed: hr=0x{:08x}", + err.code().0 as u32 + ) + })?; + } + + let capture: IAudioCaptureClient = unsafe { + client.GetService::().map_err(|err| { + format!( + "AudioClient::GetService(IAudioCaptureClient) failed: hr=0x{:08x}", + err.code().0 as u32 + ) + })? + }; + + let pcm_pool = PcmFramePool::new(FRAME_QUEUE_LIMIT, PCM_SLOT_SAMPLES_MAX) + .map_err(|err| format!("PcmFramePool::new failed: {err}"))?; + + unsafe { + client.Start().map_err(|err| { + format!( + "AudioClient::Start failed: hr=0x{:08x}", + err.code().0 as u32 + ) + })?; + } + callbacks.emit_started(); + + let pump_result = pump_capture_loop( + &capture, + sample_format, + &pcm_pool, + buffer_event.raw(), + stop_event, + stop_signal, + callbacks, + ); + unsafe { + let _ = client.Stop(); + } + pump_result + } + + type CapturedPacket = session_mixer::MixerPacket; + + struct SessionCapture { + pid: u32, + client: IAudioClient, + capture: IAudioCaptureClient, + buffer_event: OwnedHandle, + sample_format: CaptureSampleFormat, + } + + impl SessionCapture { + fn start(pid: u32, stop_event: HANDLE) -> std::result::Result { + let client = activate_process_loopback_client(pid, true, stop_event) + .map_err(|err| err.reason.to_string())?; + let sample_format = initialize_capture_client(&client)?; + let buffer_event_raw = unsafe { CreateEventW(None, false, false, PCWSTR::null()) } + .map_err(|err: windows::core::Error| { + format!( + "CreateEventW(session buffer) failed: hr=0x{:08x}", + err.code().0 as u32 + ) + })?; + let buffer_event = OwnedHandle::new(buffer_event_raw).ok_or_else(|| { + "CreateEventW(session buffer) returned invalid handle".to_string() + })?; + unsafe { + client.SetEventHandle(buffer_event.raw()).map_err(|err| { + format!( + "AudioClient::SetEventHandle(session) failed: hr=0x{:08x}", + err.code().0 as u32 + ) + })?; + } + let capture: IAudioCaptureClient = unsafe { + client.GetService::().map_err(|err| { + format!( + "AudioClient::GetService(session IAudioCaptureClient) failed: hr=0x{:08x}", + err.code().0 as u32 + ) + })? + }; + unsafe { + client.Start().map_err(|err| { + format!( + "AudioClient::Start(session) failed for pid {pid}: hr=0x{:08x}", + err.code().0 as u32 + ) + })?; + } + Ok(Self { + pid, + client, + capture, + buffer_event, + sample_format, + }) + } + + fn stop(&self) { + unsafe { + let _ = self.client.Stop(); + } + } + } + + fn initialize_capture_client( + client: &IAudioClient, + ) -> std::result::Result { + match initialize_capture_client_with_format(client, CaptureSampleFormat::Float32) { + Ok(()) => Ok(CaptureSampleFormat::Float32), + Err(float_error) => { + match initialize_capture_client_with_format(client, CaptureSampleFormat::Pcm16) { + Ok(()) => Ok(CaptureSampleFormat::Pcm16), + Err(pcm_error) => Err(format!( + "AudioClient::Initialize failed for float32 ({float_error}) and pcm16 ({pcm_error})" + )), + } + } + } + } + + fn initialize_capture_client_with_format( + client: &IAudioClient, + sample_format: CaptureSampleFormat, + ) -> std::result::Result<(), String> { + let target_sample_rate = audio_contract::TARGET_SAMPLE_RATE; + let target_channels = audio_contract::TARGET_CHANNELS; + let stream_flags = AUDCLNT_STREAMFLAGS_LOOPBACK + | AUDCLNT_STREAMFLAGS_EVENTCALLBACK + | AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM + | AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY; + match sample_format { + CaptureSampleFormat::Float32 => { + const CAPTURE_BITS_PER_SAMPLE: u16 = 32; + const CAPTURE_BYTES_PER_SAMPLE: u16 = CAPTURE_BITS_PER_SAMPLE / 8; + let block_align = target_channels * CAPTURE_BYTES_PER_SAMPLE; + let avg_bytes = target_sample_rate * u32::from(block_align); + let format = WAVEFORMATEXTENSIBLE { + Format: WAVEFORMATEX { + wFormatTag: WAVE_FORMAT_EXTENSIBLE as u16, + nChannels: target_channels, + nSamplesPerSec: target_sample_rate, + nAvgBytesPerSec: avg_bytes, + nBlockAlign: block_align, + wBitsPerSample: CAPTURE_BITS_PER_SAMPLE, + cbSize: (size_of::() - size_of::()) + as u16, + }, + Samples: WAVEFORMATEXTENSIBLE_0 { + wValidBitsPerSample: CAPTURE_BITS_PER_SAMPLE, + }, + dwChannelMask: 0x3, + SubFormat: KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, + }; + let format_ptr = (&format as *const WAVEFORMATEXTENSIBLE).cast::(); + unsafe { + client + .Initialize( + AUDCLNT_SHAREMODE_SHARED, + stream_flags, + 0, + 0, + &*format_ptr, + None, + ) + .map_err(|err| { + format!( + "{} init hr=0x{:08x}", + sample_format.label(), + err.code().0 as u32 + ) + }) + } + } + CaptureSampleFormat::Pcm16 => { + const CAPTURE_BITS_PER_SAMPLE: u16 = 16; + const CAPTURE_BYTES_PER_SAMPLE: u16 = CAPTURE_BITS_PER_SAMPLE / 8; + let block_align = target_channels * CAPTURE_BYTES_PER_SAMPLE; + let avg_bytes = target_sample_rate * u32::from(block_align); + let format = WAVEFORMATEX { + wFormatTag: WAVE_FORMAT_PCM as u16, + nChannels: target_channels, + nSamplesPerSec: target_sample_rate, + nAvgBytesPerSec: avg_bytes, + nBlockAlign: block_align, + wBitsPerSample: CAPTURE_BITS_PER_SAMPLE, + cbSize: 0, + }; + unsafe { + client + .Initialize(AUDCLNT_SHAREMODE_SHARED, stream_flags, 0, 0, &format, None) + .map_err(|err| { + format!( + "{} init hr=0x{:08x}", + sample_format.label(), + err.code().0 as u32 + ) + }) + } + } + } + } + + fn session_mixer_capture_thread( + callbacks: &Callbacks, + stop_event: HANDLE, + stop_signal: &AtomicBool, + ) -> std::result::Result<(), String> { + let mut captures: Vec = Vec::new(); + refresh_session_mixer_captures(&mut captures, stop_event)?; + callbacks.emit_started(); + let mut handles: Vec = Vec::with_capacity(MAX_SESSION_MIXER_CAPTURES + 1); + rebuild_session_mixer_handles(&mut handles, &captures, stop_event); + let mut packets: Vec = Vec::new(); + let mut last_refresh = Instant::now(); + while !stop_signal.load(Ordering::SeqCst) { + if last_refresh.elapsed() >= SESSION_MIXER_REFRESH_INTERVAL { + refresh_session_mixer_captures(&mut captures, stop_event)?; + rebuild_session_mixer_handles(&mut handles, &captures, stop_event); + last_refresh = Instant::now(); + } + let wait = + unsafe { WaitForMultipleObjects(&handles, false, SESSION_MIXER_WAIT_TIMEOUT_MS) }; + if wait == WAIT_TIMEOUT { + continue; + } + if wait.0 == u32::MAX { + return Err("WaitForMultipleObjects failed in session mixer".to_string()); + } + if wait.0 == 0 { + break; + } + packets.clear(); + for capture in &captures { + drain_packets_into(&capture.capture, capture.sample_format, &mut packets)?; + } + packets = emit_mixed_packets(std::mem::take(&mut packets), callbacks); + } + for capture in &captures { + capture.stop(); + } + Ok(()) + } + + fn rebuild_session_mixer_handles( + handles: &mut Vec, + captures: &[SessionCapture], + stop_event: HANDLE, + ) { + assert!(captures.len() <= MAX_SESSION_MIXER_CAPTURES); + handles.clear(); + handles.push(stop_event); + for capture in captures { + handles.push(capture.buffer_event.raw()); + } + assert_eq!(handles.len(), captures.len() + 1); + } + + fn refresh_session_mixer_captures( + captures: &mut Vec, + stop_event: HANDLE, + ) -> std::result::Result<(), String> { + let desired = + enumerate_shareable_audio_session_pids().map_err(|err| err.reason.to_string())?; + captures.retain(|capture| { + let keep = desired.contains(&capture.pid); + if !keep { + capture.stop(); + } + keep + }); + for pid in desired { + if captures.len() >= MAX_SESSION_MIXER_CAPTURES { + break; + } + if captures.iter().any(|capture| capture.pid == pid) { + continue; + } + match SessionCapture::start(pid, stop_event) { + Ok(capture) => captures.push(capture), + Err(_) => continue, + } + } + Ok(()) + } + + fn enumerate_shareable_audio_session_pids() -> std::result::Result, Error> { + let enumerator: IMMDeviceEnumerator = unsafe { + CoCreateInstance( + &MMDeviceEnumerator, + None::<&windows::core::IUnknown>, + CLSCTX_ALL, + ) + } + .map_err(|err| hresult_error("CoCreateInstance(MMDeviceEnumerator)", err.code().0))?; + let devices = unsafe { enumerator.EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE) } + .map_err(|err| hresult_error("EnumAudioEndpoints", err.code().0))?; + let device_count = unsafe { devices.GetCount() } + .map_err(|err| hresult_error("IMMDeviceCollection::GetCount", err.code().0))?; + let process_entries = snapshot_process_entries().unwrap_or_default(); + let self_pid = unsafe { GetCurrentProcessId() }; + let mut pids = BTreeSet::new(); + let mut excluded_pids = BTreeSet::new(); + for device_index in 0..device_count { + let Ok(device) = (unsafe { devices.Item(device_index) }) else { + continue; + }; + let Ok(manager) = + (unsafe { device.Activate::(CLSCTX_ALL, None) }) + else { + continue; + }; + let Ok(sessions) = (unsafe { manager.GetSessionEnumerator() }) else { + continue; + }; + let Ok(session_count) = (unsafe { sessions.GetCount() }) else { + continue; + }; + for session_index in 0..session_count { + let Ok(session): std::result::Result = + (unsafe { sessions.GetSession(session_index) }) + else { + continue; + }; + if matches!(unsafe { session.GetState() }, Ok(state) if state == AudioSessionStateExpired) + { + continue; + } + let Ok(session2) = session.cast::() else { + continue; + }; + if unsafe { session2.IsSystemSoundsSession() }.0 <= 0 { + continue; + } + let Ok(pid) = (unsafe { session2.GetProcessId() }) else { + continue; + }; + if pid == 0 { + continue; + } + if process_tree::pid_overlaps_our_process_tree(&process_entries, pid, self_pid) { + excluded_pids.insert(pid); + continue; + } + let root_pid = resolve_audio_root_pid(pid); + if root_pid == 0 { + continue; + } + if process_tree::pid_overlaps_our_process_tree(&process_entries, root_pid, self_pid) + { + excluded_pids.insert(root_pid); + continue; + } + pids.insert(root_pid); + } + } + let capture_pids = pids.into_iter().collect::>(); + let exclude_pids = excluded_pids.into_iter().collect::>(); + Ok( + process_tree::deduplicate_capture_roots(&process_entries, &capture_pids, &exclude_pids) + .into_iter() + .take(MAX_SESSION_MIXER_CAPTURES) + .collect(), + ) + } + + fn drain_packets_into( + capture: &IAudioCaptureClient, + sample_format: CaptureSampleFormat, + packets: &mut Vec, + ) -> std::result::Result<(), String> { + loop { + let packet_size = unsafe { + capture.GetNextPacketSize().map_err(|err| { + format!("GetNextPacketSize failed: hr=0x{:08x}", err.code().0 as u32) + })? + }; + if packet_size == 0 { + return Ok(()); + } + let mut data: *mut u8 = std::ptr::null_mut(); + let mut frames: u32 = 0; + let mut flags: u32 = 0; + let mut device_pos: u64 = 0; + let mut qpc_pos: u64 = 0; + unsafe { + capture + .GetBuffer( + &mut data, + &mut frames, + &mut flags, + Some(&mut device_pos), + Some(&mut qpc_pos), + ) + .map_err(|err| format!("GetBuffer failed: hr=0x{:08x}", err.code().0 as u32))?; + } + let release_result = (|| -> std::result::Result<(), String> { + if frames == 0 { + return Ok(()); + } + let sample_count = audio_contract::sample_count_for_frames(frames) + .ok_or_else(|| format!("frame count overflows sample buffer: {frames}"))?; + let mut samples = Vec::::with_capacity(sample_count); + append_capture_samples(&mut samples, data, sample_count, flags, sample_format); + let timestamp_us = if (flags & AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR.0 as u32) != 0 { + current_performance_timestamp_us() + } else { + audio_contract::qpc_100ns_to_timestamp_us(qpc_pos) + }; + packets.push(CapturedPacket { + timestamp_us, + samples, + }); + Ok(()) + })(); + let release_hr = unsafe { capture.ReleaseBuffer(frames) }; + if let Err(err) = release_hr { + return Err(format!( + "ReleaseBuffer failed: hr=0x{:08x}", + err.code().0 as u32 + )); + } + release_result?; + } + } + + fn emit_mixed_packets( + packets: Vec, + callbacks: &Callbacks, + ) -> Vec { + let mut mixed = session_mixer::mix_packets( + packets, + audio_contract::TARGET_CHANNELS, + audio_contract::TARGET_SAMPLE_RATE, + MAX_SESSION_MIXER_EMIT_FRAMES, + ); + for packet in mixed.drain(..) { + callbacks.emit_frame(FramePayload { + samples: FrameSamples::Owned(packet.samples), + timestamp_us: packet.timestamp_us, + }); + } + mixed + } + + fn pump_capture_loop( + capture: &IAudioCaptureClient, + sample_format: CaptureSampleFormat, + pcm_pool: &PcmFramePool, + buffer_event: HANDLE, + stop_event: HANDLE, + stop_signal: &AtomicBool, + callbacks: &Callbacks, + ) -> std::result::Result<(), String> { + while !stop_signal.load(Ordering::SeqCst) { + let handles = [buffer_event, stop_event]; + let wait = unsafe { WaitForMultipleObjects(&handles, false, INFINITE) }; + if wait.0 == u32::MAX { + return Err("WaitForMultipleObjects failed in capture loop".to_string()); + } + if wait.0 == 1 { + break; + } + if wait.0 != 0 { + continue; + } + drain_packets(capture, sample_format, pcm_pool, callbacks)?; + } + Ok(()) + } + + fn drain_packets( + capture: &IAudioCaptureClient, + sample_format: CaptureSampleFormat, + pcm_pool: &PcmFramePool, + callbacks: &Callbacks, + ) -> std::result::Result<(), String> { + loop { + let packet_size = unsafe { + capture.GetNextPacketSize().map_err(|err| { + format!("GetNextPacketSize failed: hr=0x{:08x}", err.code().0 as u32) + })? + }; + if packet_size == 0 { + return Ok(()); + } + let mut data: *mut u8 = std::ptr::null_mut(); + let mut frames: u32 = 0; + let mut flags: u32 = 0; + let mut device_pos: u64 = 0; + let mut qpc_pos: u64 = 0; + unsafe { + capture + .GetBuffer( + &mut data, + &mut frames, + &mut flags, + Some(&mut device_pos), + Some(&mut qpc_pos), + ) + .map_err(|err| format!("GetBuffer failed: hr=0x{:08x}", err.code().0 as u32))?; + } + let release_result = (|| -> std::result::Result<(), String> { + if frames == 0 { + return Ok(()); + } + let sample_count = audio_contract::sample_count_for_frames(frames) + .ok_or_else(|| format!("frame count overflows sample buffer: {frames}"))?; + let samples = + capture_samples_payload(pcm_pool, data, sample_count, flags, sample_format); + let timestamp_us = if (flags & AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR.0 as u32) != 0 { + current_performance_timestamp_us() + } else { + audio_contract::qpc_100ns_to_timestamp_us(qpc_pos) + }; + callbacks.emit_frame(FramePayload { + samples, + timestamp_us, + }); + Ok(()) + })(); + + let release_hr = unsafe { capture.ReleaseBuffer(frames) }; + if let Err(err) = release_hr { + return Err(format!( + "ReleaseBuffer failed: hr=0x{:08x}", + err.code().0 as u32 + )); + } + release_result?; + } + } + + fn capture_samples_payload( + pcm_pool: &PcmFramePool, + data: *mut u8, + sample_count: usize, + flags: u32, + sample_format: CaptureSampleFormat, + ) -> FrameSamples { + assert!(sample_count > 0); + if sample_count <= pcm_pool.samples_per_slot() as usize + && let Some(mut slot) = pcm_pool.try_acquire() + { + let written = + write_capture_samples_to_slot(&mut slot, data, sample_count, flags, sample_format); + if written.is_ok() { + assert_eq!(slot.filled_len(), sample_count); + return FrameSamples::Pooled(slot); + } + } + let mut samples = Vec::::with_capacity(sample_count); + append_capture_samples(&mut samples, data, sample_count, flags, sample_format); + assert_eq!(samples.len(), sample_count); + FrameSamples::Owned(samples) + } + + fn write_capture_samples_to_slot( + slot: &mut PooledPcmFrame, + data: *mut u8, + sample_count: usize, + flags: u32, + sample_format: CaptureSampleFormat, + ) -> std::result::Result<(), PcmPoolError> { + assert!(sample_count > 0); + assert!(sample_count <= slot.capacity()); + let silent = (flags & AUDCLNT_BUFFERFLAGS_SILENT.0 as u32) != 0 || data.is_null(); + if silent { + return slot.write_with(sample_count, |out| out.fill(0.0)); + } + match sample_format { + CaptureSampleFormat::Float32 => { + let samples = + unsafe { std::slice::from_raw_parts(data as *const f32, sample_count) }; + slot.write(samples) + } + CaptureSampleFormat::Pcm16 => { + let pcm = unsafe { std::slice::from_raw_parts(data as *const i16, sample_count) }; + slot.write_with(sample_count, |out| { + for (out_sample, pcm_sample) in out.iter_mut().zip(pcm.iter().copied()) { + *out_sample = audio_contract::pcm16_to_float32(pcm_sample); + } + }) + } + } + } + + fn append_capture_samples( + out: &mut Vec, + data: *mut u8, + sample_count: usize, + flags: u32, + sample_format: CaptureSampleFormat, + ) { + let silent = (flags & AUDCLNT_BUFFERFLAGS_SILENT.0 as u32) != 0 || data.is_null(); + if silent { + out.resize(sample_count, 0.0); + return; + } + match sample_format { + CaptureSampleFormat::Float32 => { + let samples = + unsafe { std::slice::from_raw_parts(data as *const f32, sample_count) }; + out.extend_from_slice(samples); + } + CaptureSampleFormat::Pcm16 => { + let pcm = unsafe { std::slice::from_raw_parts(data as *const i16, sample_count) }; + out.extend(pcm.iter().copied().map(audio_contract::pcm16_to_float32)); + } + } + } + + fn current_performance_timestamp_us() -> i64 { + use windows::Win32::System::Performance::{ + QueryPerformanceCounter, QueryPerformanceFrequency, + }; + let mut counter: i64 = 0; + let mut frequency: i64 = 0; + unsafe { + if QueryPerformanceCounter(&mut counter).is_err() + || QueryPerformanceFrequency(&mut frequency).is_err() + || frequency <= 0 + { + return 0; + } + } + let micros = (i128::from(counter) * 1_000_000) / i128::from(frequency); + micros.min(i128::from(i64::MAX)) as i64 + } + + struct ComApartment; + + impl ComApartment { + fn initialize() -> Result { + unsafe { + CoInitializeEx(None, COINIT_MULTITHREADED) + .ok() + .map_err(|err| { + Error::new( + Status::GenericFailure, + format!("CoInitializeEx failed: hr=0x{:08x}", err.code().0 as u32), + ) + })?; + } + Ok(Self) + } + } + + impl Drop for ComApartment { + fn drop(&mut self) { + unsafe { + CoUninitialize(); + } + } + } + + pub(crate) fn detected_build() -> Option { + detected_kernel32_build() + } + + pub(crate) fn process_loopback_probe() -> ProcessLoopbackRuntimeProbe { + static PROBE: OnceLock = OnceLock::new(); + PROBE.get_or_init(run_process_loopback_probe).clone() + } + + fn run_process_loopback_probe() -> ProcessLoopbackRuntimeProbe { + let (tx, rx) = std::sync::mpsc::channel(); + let _ = thread::Builder::new() + .name("fluxer-win-process-loopback-probe".to_owned()) + .spawn(move || { + let include_result = probe_process_loopback_activation(true); + let exclude_result = probe_process_loopback_activation(false); + let _ = tx.send((include_result, exclude_result)); + }); + + match rx.recv_timeout(std::time::Duration::from_millis( + u64::from(PROCESS_LOOPBACK_PROBE_TIMEOUT_MS) + 500, + )) { + Ok((include_result, exclude_result)) => ProcessLoopbackRuntimeProbe { + include_supported: include_result.is_ok(), + include_detail: Some(match include_result { + Ok(()) => "include-mode process loopback activation probe succeeded".to_owned(), + Err(err) => err.reason.to_string(), + }), + exclude_supported: exclude_result.is_ok(), + exclude_detail: Some(match exclude_result { + Ok(()) => "exclude-mode process loopback activation probe succeeded".to_owned(), + Err(err) => err.reason.to_string(), + }), + }, + Err(_) => ProcessLoopbackRuntimeProbe { + include_supported: false, + include_detail: Some( + "process loopback activation probe worker timed out".to_owned(), + ), + exclude_supported: false, + exclude_detail: Some( + "process loopback activation probe worker timed out".to_owned(), + ), + }, + } + } + + fn probe_process_loopback_activation(include_tree: bool) -> std::result::Result<(), Error> { + let _com = ComApartment::initialize()?; + let stop_event = unsafe { CreateEventW(None, true, false, PCWSTR::null()) } + .map_err(|err: windows::core::Error| hresult_error("CreateEventW", err.code().0))?; + let stop_event = OwnedHandle::new(stop_event) + .ok_or_else(|| generic("CreateEventW returned invalid probe stop handle"))?; + let current_pid = unsafe { GetCurrentProcessId() }; + + let client = activate_process_loopback_client_with_timeout( + current_pid, + include_tree, + stop_event.raw(), + PROCESS_LOOPBACK_PROBE_TIMEOUT_MS, + )?; + drop(client); + + Ok(()) + } + + fn detected_kernel32_build() -> Option { + let path = PCWSTR(KERNEL32_PATH_UTF16.as_ptr()); + let size = unsafe { GetFileVersionInfoSizeW(path, None) }; + if size == 0 { + return None; + } + let mut buffer = vec![0u8; size as usize]; + let ok = unsafe { + GetFileVersionInfoW(path, Some(0), size, buffer.as_mut_ptr() as *mut c_void).is_ok() + }; + if !ok { + return None; + } + let mut info_ptr: *mut c_void = std::ptr::null_mut(); + let mut info_len: u32 = 0; + let queried = unsafe { + VerQueryValueW( + buffer.as_ptr() as *const c_void, + PCWSTR(BACKSLASH_UTF16.as_ptr()), + &mut info_ptr, + &mut info_len, + ) + .as_bool() + }; + if !queried || info_ptr.is_null() { + return None; + } + let fixed: &VS_FIXEDFILEINFO = unsafe { &*(info_ptr as *const VS_FIXEDFILEINFO) }; + let build = (fixed.dwFileVersionLS >> 16) & 0xffff; + Some(build) + } + + pub(crate) fn is_supported() -> bool { + let probe = process_loopback_probe(); + probe.include_supported || probe.exclude_supported + } + + pub(crate) fn pid_from_hwnd(hwnd: u64) -> u32 { + if hwnd == 0 { + return 0; + } + let mut pid: u32 = 0; + let tid = unsafe { GetWindowThreadProcessId(HWND(hwnd as *mut c_void), Some(&mut pid)) }; + if tid == 0 { 0 } else { pid } + } + + pub(crate) fn resolve_audio_root_pid(pid: u32) -> u32 { + resolve_audio_root_pid_impl(pid).unwrap_or(pid) + } + + struct ResolveEntry { + pid: u32, + parent: u32, + exe: String, + } + + fn resolve_audio_root_pid_impl(start_pid: u32) -> Option { + let entries = snapshot_processes_with_exe()?; + let self_pid = unsafe { GetCurrentProcessId() }; + let mut current = start_pid; + let mut last_browserlike: u32 = if start_pid == self_pid { 0 } else { start_pid }; + for _ in 0..16 { + let Some(entry) = entries.iter().find(|e| e.pid == current) else { + break; + }; + if current != self_pid && is_browser_like(&entry.exe) { + last_browserlike = current; + } + if entry.exe == "explorer.exe" || entry.parent == 0 || entry.parent == current { + break; + } + current = entry.parent; + } + if last_browserlike == 0 || last_browserlike == self_pid { + return Some(start_pid); + } + + let mut self_walk = self_pid; + for _ in 0..32 { + let Some(entry) = entries.iter().find(|e| e.pid == self_walk) else { + break; + }; + if self_walk == last_browserlike { + return Some(start_pid); + } + if entry.parent == 0 || entry.parent == self_walk { + break; + } + self_walk = entry.parent; + } + Some(last_browserlike) + } + + fn is_browser_like(exe: &str) -> bool { + matches!( + exe, + "chrome.exe" | "msedge.exe" | "firefox.exe" | "electron.exe" + ) + } + + fn snapshot_processes_with_exe() -> Option> { + let snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }.ok()?; + let snapshot = OwnedHandle::new(snapshot)?; + let mut pe: PROCESSENTRY32W = unsafe { zeroed() }; + pe.dwSize = size_of::() as u32; + if unsafe { Process32FirstW(snapshot.raw(), &mut pe) }.is_err() { + return None; + } + let mut entries = Vec::new(); + loop { + let exe = wide_until_nul(&pe.szExeFile) + .iter() + .map(|c| char::from_u32(u32::from(*c)).unwrap_or('?')) + .collect::() + .to_ascii_lowercase(); + entries.push(ResolveEntry { + pid: pe.th32ProcessID, + parent: pe.th32ParentProcessID, + exe, + }); + if unsafe { Process32NextW(snapshot.raw(), &mut pe) }.is_err() { + break; + } + } + Some(entries) + } + + fn snapshot_process_entries() -> Option> { + let snapshot = unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) }.ok()?; + let snapshot = OwnedHandle::new(snapshot)?; + let mut pe: PROCESSENTRY32W = unsafe { zeroed() }; + pe.dwSize = size_of::() as u32; + if unsafe { Process32FirstW(snapshot.raw(), &mut pe) }.is_err() { + return None; + } + let mut entries = Vec::new(); + loop { + entries.push(process_tree::ProcessEntry { + pid: pe.th32ProcessID, + parent: pe.th32ParentProcessID, + }); + if unsafe { Process32NextW(snapshot.raw(), &mut pe) }.is_err() { + break; + } + } + Some(entries) + } + + fn wide_until_nul(buf: &[u16]) -> &[u16] { + match buf.iter().position(|&c| c == 0) { + Some(idx) => &buf[..idx], + None => buf, + } + } + + fn is_self_or_descendant_pid(target: u32) -> bool { + let self_pid = unsafe { GetCurrentProcessId() }; + if target == 0 { + return false; + } + if target == self_pid { + return true; + } + snapshot_process_entries() + .map(|entries| process_tree::pid_is_our_descendant(&entries, target, self_pid)) + .unwrap_or(false) + } + + #[allow(dead_code)] + fn _touch_lasterror() -> u32 { + unsafe { GetLastError().0 } + } + + #[cfg(test)] + mod blob_propvariant_tests { + use super::*; + + #[test] + fn blob_propvariant_round_trips_vt_size_and_pointer() { + let mut buffer = [0u8; 32]; + let ptr = buffer.as_mut_ptr(); + let size = buffer.len() as u32; + + let prop = ManuallyDrop::new(make_blob_propvariant(ptr, size)); + unsafe { + let inner = &prop.Anonymous.Anonymous; + assert_eq!(inner.vt, VT_BLOB, "vt tag must be VT_BLOB"); + assert_eq!(inner.wReserved1, 0); + assert_eq!(inner.wReserved2, 0); + assert_eq!(inner.wReserved3, 0); + let blob = inner.Anonymous.blob; + assert_eq!(blob.cbSize, size, "BLOB cbSize must match input"); + assert_eq!(blob.pBlobData, ptr, "BLOB pBlobData must match input"); + } + } + + #[test] + fn propvariant_size_matches_sdk() { + let expected = if cfg!(target_pointer_width = "64") { + 24 + } else { + 16 + }; + assert_eq!(size_of::(), expected); + } + } +} + +#[cfg(not(target_os = "windows"))] +mod platform { + use super::{Callbacks, LoopbackOptions, ProcessLoopbackRuntimeProbe}; + use napi::bindgen_prelude::{Error, Result, Status}; + + pub(crate) struct Inner; + + impl Inner { + pub(crate) fn new( + _pid: u32, + _options: LoopbackOptions, + _callbacks: Callbacks, + ) -> Result { + Err(Error::new( + Status::GenericFailure, + "win-process-loopback only supports Windows", + )) + } + + pub(crate) fn set_screen_audio_sink( + &self, + _sink: std::sync::Arc, + ) { + } + + pub(crate) fn clear_screen_audio_sink(&self) {} + + pub(crate) fn start(&self) -> Result<()> { + Err(Error::new( + Status::GenericFailure, + "win-process-loopback only supports Windows", + )) + } + + pub(crate) fn stop(&self) {} + + pub(crate) fn dispose(&self) {} + } + + pub(crate) fn is_supported() -> bool { + false + } + + pub(crate) fn process_loopback_probe() -> ProcessLoopbackRuntimeProbe { + ProcessLoopbackRuntimeProbe { + include_supported: false, + include_detail: Some( + "process loopback activation probe is only available on Windows".to_owned(), + ), + exclude_supported: false, + exclude_detail: Some( + "process loopback activation probe is only available on Windows".to_owned(), + ), + } + } + + pub(crate) fn detected_build() -> Option { + None + } + + pub(crate) fn pid_from_hwnd(_hwnd: u64) -> u32 { + 0 + } + + pub(crate) fn resolve_audio_root_pid(pid: u32) -> u32 { + pid + } +} diff --git a/fluxer_desktop/native/win-process-loopback/src/pcm_pool.rs b/fluxer_desktop/native/win-process-loopback/src/pcm_pool.rs new file mode 100644 index 0000000..b46b7bc --- /dev/null +++ b/fluxer_desktop/native/win-process-loopback/src/pcm_pool.rs @@ -0,0 +1,511 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::cell::UnsafeCell; +use std::fmt; +use std::sync::atomic::{AtomicU32, AtomicU64, Ordering}; +use std::sync::{Arc, Mutex, MutexGuard}; + +pub const PCM_SLOT_SAMPLES_MAX: usize = 16_384; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PcmPoolError { + ZeroCapacity, + ZeroSamplesPerSlot, + SamplesPerSlotTooLarge(usize), + PayloadTooLarge { offered: usize, capacity: usize }, +} + +impl fmt::Display for PcmPoolError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::ZeroCapacity => write!(f, "PcmFramePool capacity must be > 0"), + Self::ZeroSamplesPerSlot => write!(f, "PcmFramePool samples_per_slot must be > 0"), + Self::SamplesPerSlotTooLarge(n) => write!( + f, + "PcmFramePool samples_per_slot {n} exceeds PCM_SLOT_SAMPLES_MAX={PCM_SLOT_SAMPLES_MAX}" + ), + Self::PayloadTooLarge { offered, capacity } => write!( + f, + "PcmFramePool payload {offered} samples exceeds slot capacity {capacity}" + ), + } + } +} + +impl std::error::Error for PcmPoolError {} + +struct PcmSlotCell { + inner: UnsafeCell>, +} + +unsafe impl Send for PcmSlotCell {} +unsafe impl Sync for PcmSlotCell {} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub struct PcmPoolStats { + pub acquired: u64, + pub released: u64, + pub dropped: u64, + pub in_flight: u32, +} + +pub(crate) struct PcmFramePoolInner { + slots: Vec, + free: Mutex>, + capacity: u32, + samples_per_slot: u32, + acquired_total: AtomicU64, + released_total: AtomicU64, + dropped_total: AtomicU64, + in_flight: AtomicU32, +} + +fn lock_free_list(free: &Mutex>) -> MutexGuard<'_, Vec> { + free.lock().unwrap_or_else(|poisoned| poisoned.into_inner()) +} + +pub struct PcmFramePool { + inner: Arc, +} + +impl fmt::Debug for PcmFramePool { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let stats = self.stats(); + f.debug_struct("PcmFramePool") + .field("capacity", &self.inner.capacity) + .field("samples_per_slot", &self.inner.samples_per_slot) + .field("stats", &stats) + .finish() + } +} + +impl PcmFramePool { + pub fn new(capacity: usize, samples_per_slot: usize) -> Result { + if capacity == 0 { + return Err(PcmPoolError::ZeroCapacity); + } + if samples_per_slot == 0 { + return Err(PcmPoolError::ZeroSamplesPerSlot); + } + if samples_per_slot > PCM_SLOT_SAMPLES_MAX { + return Err(PcmPoolError::SamplesPerSlotTooLarge(samples_per_slot)); + } + assert!(capacity > 0); + assert!(samples_per_slot > 0); + assert!(samples_per_slot <= PCM_SLOT_SAMPLES_MAX); + + let mut slots: Vec = Vec::with_capacity(capacity); + for _ in 0..capacity { + let buf: Box<[f32]> = vec![0.0_f32; samples_per_slot].into_boxed_slice(); + assert_eq!(buf.len(), samples_per_slot); + slots.push(PcmSlotCell { + inner: UnsafeCell::new(buf), + }); + } + assert_eq!(slots.len(), capacity); + + let mut free: Vec = Vec::with_capacity(capacity); + for index in 0..capacity { + free.push(index); + } + assert_eq!(free.len(), capacity); + + let cap_u32 = u32::try_from(capacity).map_err(|_| PcmPoolError::ZeroCapacity)?; + let sps_u32 = + u32::try_from(samples_per_slot).map_err(|_| PcmPoolError::ZeroSamplesPerSlot)?; + let inner = PcmFramePoolInner { + slots, + free: Mutex::new(free), + capacity: cap_u32, + samples_per_slot: sps_u32, + acquired_total: AtomicU64::new(0), + released_total: AtomicU64::new(0), + dropped_total: AtomicU64::new(0), + in_flight: AtomicU32::new(0), + }; + Ok(Self { + inner: Arc::new(inner), + }) + } + + pub fn try_acquire(&self) -> Option { + assert!(self.inner.capacity > 0); + assert!(self.inner.samples_per_slot > 0); + + let mut free = lock_free_list(&self.inner.free); + assert!(free.len() <= self.inner.capacity as usize); + let index = match free.pop() { + Some(idx) => idx, + None => { + drop(free); + self.inner.dropped_total.fetch_add(1, Ordering::Relaxed); + return None; + } + }; + assert!(index < self.inner.capacity as usize); + self.inner.acquired_total.fetch_add(1, Ordering::Relaxed); + let after = self.inner.in_flight.fetch_add(1, Ordering::AcqRel) + 1; + assert!(after <= self.inner.capacity); + drop(free); + + Some(PooledPcmFrame { + slot_index: index, + filled_len: 0, + pool: Arc::clone(&self.inner), + }) + } + + pub fn capacity(&self) -> u32 { + let cap = self.inner.capacity; + assert!(cap > 0); + assert!(cap as usize == self.inner.slots.len()); + cap + } + + pub fn samples_per_slot(&self) -> u32 { + let sps = self.inner.samples_per_slot; + assert!(sps > 0); + assert!(sps as usize <= PCM_SLOT_SAMPLES_MAX); + sps + } + + pub fn stats(&self) -> PcmPoolStats { + assert!(self.inner.capacity > 0); + let in_flight = self.inner.in_flight.load(Ordering::Acquire); + assert!(in_flight <= self.inner.capacity); + let acquired = self.inner.acquired_total.load(Ordering::Relaxed); + let released = self.inner.released_total.load(Ordering::Relaxed); + let dropped = self.inner.dropped_total.load(Ordering::Relaxed); + assert!(released <= acquired); + PcmPoolStats { + acquired, + released, + dropped, + in_flight, + } + } +} + +impl Clone for PcmFramePool { + fn clone(&self) -> Self { + Self { + inner: Arc::clone(&self.inner), + } + } +} + +pub struct PooledPcmFrame { + slot_index: usize, + filled_len: usize, + pool: Arc, +} + +impl PooledPcmFrame { + pub fn write(&mut self, samples: &[f32]) -> Result<(), PcmPoolError> { + debug_assert!(self.slot_index < self.pool.capacity as usize); + let cap = self.pool.samples_per_slot as usize; + if samples.len() > cap { + return Err(PcmPoolError::PayloadTooLarge { + offered: samples.len(), + capacity: cap, + }); + } + debug_assert!(samples.len() <= cap); + + let cell = &self.pool.slots[self.slot_index]; + let buf: &mut [f32] = unsafe { &mut *cell.inner.get() }; + debug_assert_eq!(buf.len(), cap); + if !samples.is_empty() { + buf[..samples.len()].copy_from_slice(samples); + } + self.filled_len = samples.len(); + debug_assert!(self.filled_len <= cap); + Ok(()) + } + + pub fn write_with(&mut self, sample_count: usize, fill: F) -> Result<(), PcmPoolError> + where + F: FnOnce(&mut [f32]), + { + debug_assert!(self.slot_index < self.pool.capacity as usize); + let cap = self.pool.samples_per_slot as usize; + if sample_count > cap { + return Err(PcmPoolError::PayloadTooLarge { + offered: sample_count, + capacity: cap, + }); + } + debug_assert!(sample_count <= cap); + + let cell = &self.pool.slots[self.slot_index]; + let buf: &mut [f32] = unsafe { &mut *cell.inner.get() }; + debug_assert_eq!(buf.len(), cap); + fill(&mut buf[..sample_count]); + self.filled_len = sample_count; + debug_assert!(self.filled_len <= cap); + Ok(()) + } + + pub fn data_slice(&self) -> &[f32] { + debug_assert!(self.slot_index < self.pool.capacity as usize); + debug_assert!(self.filled_len <= self.pool.samples_per_slot as usize); + let cell = &self.pool.slots[self.slot_index]; + let buf: &[f32] = unsafe { &*cell.inner.get() }; + &buf[..self.filled_len] + } + + pub fn filled_len(&self) -> usize { + debug_assert!(self.slot_index < self.pool.capacity as usize); + debug_assert!(self.filled_len <= self.pool.samples_per_slot as usize); + self.filled_len + } + + pub fn capacity(&self) -> usize { + let cap = self.pool.samples_per_slot as usize; + debug_assert!(cap > 0); + debug_assert!(cap <= PCM_SLOT_SAMPLES_MAX); + cap + } + + pub fn as_mut_ptr(&mut self) -> *mut f32 { + debug_assert!(self.slot_index < self.pool.capacity as usize); + debug_assert!(self.filled_len <= self.pool.samples_per_slot as usize); + let cell = &self.pool.slots[self.slot_index]; + let buf: &mut [f32] = unsafe { &mut *cell.inner.get() }; + debug_assert_eq!(buf.len(), self.pool.samples_per_slot as usize); + buf.as_mut_ptr() + } + + pub fn into_external_parts(mut self) -> (*mut f32, usize, Self) { + debug_assert!(self.slot_index < self.pool.capacity as usize); + debug_assert!(self.filled_len <= self.pool.samples_per_slot as usize); + let len = self.filled_len; + let ptr = self.as_mut_ptr(); + debug_assert!(!ptr.is_null()); + (ptr, len, self) + } +} + +impl Drop for PooledPcmFrame { + fn drop(&mut self) { + debug_assert!(self.slot_index < self.pool.capacity as usize); + if self.slot_index >= self.pool.capacity as usize { + return; + } + let mut free = lock_free_list(&self.pool.free); + debug_assert!(free.len() < self.pool.capacity as usize); + free.push(self.slot_index); + let before = self.pool.in_flight.fetch_sub(1, Ordering::AcqRel); + debug_assert!(before >= 1); + self.pool.released_total.fetch_add(1, Ordering::Relaxed); + drop(free); + } +} + +#[cfg(test)] +mod tests { + use super::*; + use std::thread; + + const POOL_CAP: usize = 16; + const SLOT_SAMPLES: usize = 2_048; + + fn default_pool() -> PcmFramePool { + PcmFramePool::new(POOL_CAP, SLOT_SAMPLES).expect("default pool builds") + } + + #[test] + fn new_rejects_zero_capacity() { + let err = PcmFramePool::new(0, SLOT_SAMPLES).unwrap_err(); + assert_eq!(err, PcmPoolError::ZeroCapacity); + } + + #[test] + fn new_rejects_zero_samples_per_slot() { + let err = PcmFramePool::new(4, 0).unwrap_err(); + assert_eq!(err, PcmPoolError::ZeroSamplesPerSlot); + } + + #[test] + fn new_rejects_samples_per_slot_above_max() { + let err = PcmFramePool::new(4, PCM_SLOT_SAMPLES_MAX + 1).unwrap_err(); + assert_eq!( + err, + PcmPoolError::SamplesPerSlotTooLarge(PCM_SLOT_SAMPLES_MAX + 1) + ); + } + + #[test] + fn acquire_release_cycle_increments_counters() { + let pool = PcmFramePool::new(4, SLOT_SAMPLES).expect("pool"); + { + let _slot = pool.try_acquire().expect("slot"); + let stats_held = pool.stats(); + assert_eq!(stats_held.acquired, 1); + assert_eq!(stats_held.in_flight, 1); + } + let stats_after = pool.stats(); + assert_eq!(stats_after.acquired, 1); + assert_eq!(stats_after.released, 1); + assert_eq!(stats_after.in_flight, 0); + } + + #[test] + fn pool_exhausts_at_cap_and_counts_drop() { + let pool = default_pool(); + let mut held = Vec::with_capacity(POOL_CAP); + for _ in 0..POOL_CAP { + held.push(pool.try_acquire().expect("slot in capacity")); + } + assert!(pool.try_acquire().is_none()); + let stats = pool.stats(); + assert_eq!(stats.dropped, 1); + assert_eq!(stats.acquired as usize, POOL_CAP); + assert_eq!(stats.in_flight as usize, POOL_CAP); + } + + #[test] + fn write_then_data_slice_matches_payload() { + let pool = PcmFramePool::new(2, 64).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + let payload = [0.25_f32; 32]; + slot.write(&payload).expect("payload fits"); + assert_eq!(slot.data_slice(), &payload[..]); + assert_eq!(slot.filled_len(), 32); + } + + #[test] + fn write_rejects_payload_larger_than_slot() { + let pool = PcmFramePool::new(2, 64).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + let big = [0.0_f32; 128]; + let err = slot.write(&big).unwrap_err(); + assert!(matches!(err, PcmPoolError::PayloadTooLarge { .. })); + } + + #[test] + fn write_with_fills_exactly_sample_count_samples() { + let pool = PcmFramePool::new(2, 64).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + slot.write_with(8, |out| { + assert_eq!(out.len(), 8); + for (index, sample) in out.iter_mut().enumerate() { + *sample = index as f32; + } + }) + .expect("payload fits"); + assert_eq!(slot.filled_len(), 8); + assert_eq!(slot.data_slice(), &[0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]); + } + + #[test] + fn write_with_rejects_sample_count_larger_than_slot() { + let pool = PcmFramePool::new(2, 64).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + let err = slot.write_with(128, |_| {}).unwrap_err(); + assert!(matches!(err, PcmPoolError::PayloadTooLarge { .. })); + assert_eq!(slot.filled_len(), 0); + } + + #[test] + fn into_external_parts_exposes_filled_pointer_and_length() { + let pool = PcmFramePool::new(2, 64).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + let payload = [1.5_f32; 16]; + slot.write(&payload).expect("write fits"); + let (ptr, len, owned) = slot.into_external_parts(); + assert!(!ptr.is_null()); + assert_eq!(len, 16); + let observed = unsafe { core::slice::from_raw_parts(ptr, len) }; + assert_eq!(observed, &payload[..]); + assert_eq!(pool.stats().in_flight, 1); + drop(owned); + assert_eq!(pool.stats().in_flight, 0); + } + + #[test] + fn into_external_parts_drop_returns_slot_to_pool() { + let pool = PcmFramePool::new(1, 32).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + slot.write(&[0.75_f32; 8]).expect("write"); + let (_ptr, _len, owned) = slot.into_external_parts(); + assert!(pool.try_acquire().is_none()); + drop(owned); + let revived = pool.try_acquire().expect("revived"); + assert_eq!(revived.filled_len(), 0); + drop(revived); + assert_eq!(pool.stats().in_flight, 0); + } + + #[test] + fn pooled_frame_survives_send_across_threads() { + let pool = PcmFramePool::new(2, 64).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + slot.write(&[0.5_f32; 32]).expect("write"); + let handle = thread::spawn(move || { + assert_eq!(slot.filled_len(), 32); + assert_eq!(slot.data_slice()[0], 0.5); + drop(slot); + }); + handle.join().expect("worker"); + assert_eq!(pool.stats().in_flight, 0); + assert_eq!(pool.stats().released, 1); + } + + #[test] + fn capacity_one_pool_round_trips() { + let pool = PcmFramePool::new(1, 32).expect("pool"); + for _ in 0..5 { + let mut slot = pool.try_acquire().expect("slot"); + slot.write(&[1.0, 2.0, 3.0]).expect("write"); + assert_eq!(slot.data_slice(), &[1.0, 2.0, 3.0]); + drop(slot); + } + let stats = pool.stats(); + assert_eq!(stats.acquired, 5); + assert_eq!(stats.released, 5); + assert_eq!(stats.in_flight, 0); + } + + #[test] + fn default_pool_dimensions_match_constants() { + let pool = default_pool(); + assert_eq!(pool.capacity() as usize, POOL_CAP); + assert_eq!(pool.samples_per_slot() as usize, SLOT_SAMPLES); + } + + #[test] + fn full_acquire_drop_cycle_keeps_accounting_consistent() { + let pool = default_pool(); + for _ in 0..4 { + let mut held = Vec::with_capacity(POOL_CAP); + for _ in 0..POOL_CAP { + held.push(pool.try_acquire().expect("slot in capacity")); + } + assert!(pool.try_acquire().is_none()); + drop(held); + let stats = pool.stats(); + assert_eq!(stats.in_flight, 0); + assert_eq!(stats.released, stats.acquired); + } + let stats = pool.stats(); + assert_eq!(stats.acquired as usize, POOL_CAP * 4); + assert_eq!(stats.released as usize, POOL_CAP * 4); + assert_eq!(stats.in_flight, 0); + } + + #[test] + fn external_parts_release_does_not_abort_accounting() { + let pool = PcmFramePool::new(2, 64).expect("pool"); + let mut slot = pool.try_acquire().expect("slot"); + slot.write(&[0.25_f32; 16]).expect("write"); + let (ptr, len, owned) = slot.into_external_parts(); + assert!(!ptr.is_null()); + assert_eq!(len, 16); + drop(owned); + let stats = pool.stats(); + assert_eq!(stats.in_flight, 0); + assert_eq!(stats.released, 1); + assert_eq!(stats.acquired, 1); + } +} diff --git a/fluxer_desktop/native/win-shell/Cargo.lock b/fluxer_desktop/native/win-shell/Cargo.lock new file mode 100644 index 0000000..e2f6584 --- /dev/null +++ b/fluxer_desktop/native/win-shell/Cargo.lock @@ -0,0 +1,382 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "fluxer_win_shell" +version = "0.0.0" +dependencies = [ + "napi", + "napi-build", + "napi-derive", + "windows", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] diff --git a/fluxer_desktop/native/win-shell/Cargo.toml b/fluxer_desktop/native/win-shell/Cargo.toml new file mode 100644 index 0000000..10f45e3 --- /dev/null +++ b/fluxer_desktop/native/win-shell/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "fluxer_win_shell" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8"]} +napi-derive = "3.5.6" + +[target.'cfg(target_os = "windows")'.dependencies] +windows = {version = "0.62.2", features = [ + "Win32_Foundation", + "Win32_Security", + "Win32_System_Com", + "Win32_System_Com_StructuredStorage", + "Win32_System_Registry", + "Win32_System_Variant", + "Win32_UI_Shell", + "Win32_UI_Shell_PropertiesSystem", +]} + +[build-dependencies] +napi-build = "2.3.2" diff --git a/fluxer_desktop/native/win-shell/build.rs b/fluxer_desktop/native/win-shell/build.rs new file mode 100644 index 0000000..d45de40 --- /dev/null +++ b/fluxer_desktop/native/win-shell/build.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + napi_build::setup(); +} diff --git a/fluxer_desktop/native/win-shell/index.d.ts b/fluxer_desktop/native/win-shell/index.d.ts new file mode 100644 index 0000000..53c2682 --- /dev/null +++ b/fluxer_desktop/native/win-shell/index.d.ts @@ -0,0 +1,30 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +export interface CreateShortcutOptions { + lnkPath: string; + target: string; + args?: string; + appUserModelId?: string; + toastActivatorClsid?: string; + iconPath?: string; + iconIndex?: number; + workingDir?: string; + description?: string; +} + +export interface SetRunValueOptions { + name: string; + command: string; +} + +export declare function createShortcut(opts: CreateShortcutOptions): Promise; + +export declare function setCurrentUserRunValue(opts: SetRunValueOptions): Promise; + +export declare function deleteCurrentUserRunValue(name: string): Promise; + +export declare function getCurrentUserRunValue(name: string): Promise; + +export declare function getUserNotificationState(): string; + +export declare const loadError: Error | null; diff --git a/fluxer_desktop/native/win-shell/index.js b/fluxer_desktop/native/win-shell/index.js new file mode 100644 index 0000000..c8b4d87 --- /dev/null +++ b/fluxer_desktop/native/win-shell/index.js @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync} = require('node:fs'); +const {join, sep} = require('node:path'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); +const MODULE_NAME = '@fluxer/win-shell'; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName() { + if (process.platform !== 'win32') { + throw new Error(`@fluxer/win-shell is only supported on Windows, got ${process.platform}`); + } + switch (process.arch) { + case 'x64': + return 'win-shell.win32-x64-msvc.node'; + case 'arm64': + return 'win-shell.win32-arm64-msvc.node'; + default: + throw new Error(`Unsupported Windows architecture: ${process.arch}`); + } +} + +let binding = null; +let loadError = null; + +if (process.platform === 'win32') { + try { + const nativeRoot = resolveNativeRoot(); + const nativePath = join(nativeRoot, nativeFileName()); + const loaded = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + probe: false, + }); + binding = loaded.binding; + loadError = loaded.loadError; + if (loadError) throw loadError; + } catch (error) { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: 'native loader threw before binding load completed', + cause: error, + }); + throw loadError; + } +} + +module.exports = { + createShortcut: binding ? binding.createShortcut : null, + setCurrentUserRunValue: binding ? binding.setCurrentUserRunValue : null, + deleteCurrentUserRunValue: binding ? binding.deleteCurrentUserRunValue : null, + getCurrentUserRunValue: binding ? binding.getCurrentUserRunValue : null, + getUserNotificationState: binding ? binding.getUserNotificationState : null, + loadError, +}; diff --git a/fluxer_desktop/native/win-shell/loader-diagnostics.cjs b/fluxer_desktop/native/win-shell/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/win-shell/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/win-shell/package.json b/fluxer_desktop/native/win-shell/package.json new file mode 100644 index 0000000..e84a13d --- /dev/null +++ b/fluxer_desktop/native/win-shell/package.json @@ -0,0 +1,29 @@ +{ + "name": "@fluxer/win-shell", + "version": "0.0.0", + "description": "", + "private": true, + "license": "AGPL-3.0-or-later", + "os": [ + "win32" + ], + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "win-shell.win32-x64-msvc.node", + "win-shell.win32-arm64-msvc.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "cargo test --manifest-path Cargo.toml", + "check:windows:x64": "cargo check --manifest-path Cargo.toml --target x86_64-pc-windows-msvc --tests", + "check:windows:arm64": "cargo check --manifest-path Cargo.toml --target aarch64-pc-windows-msvc --tests" + } +} diff --git a/fluxer_desktop/native/win-shell/src/lib.rs b/fluxer_desktop/native/win-shell/src/lib.rs new file mode 100644 index 0000000..8d6fe01 --- /dev/null +++ b/fluxer_desktop/native/win-shell/src/lib.rs @@ -0,0 +1,705 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use napi::Task; +use napi::bindgen_prelude::{AsyncTask, Env, Error, Result, Status}; +use napi_derive::napi; + +#[napi(object)] +#[derive(Debug)] +pub struct CreateShortcutOptions { + #[napi(js_name = "lnkPath")] + pub lnk_path: Option, + pub target: Option, + pub args: Option, + #[napi(js_name = "appUserModelId")] + pub app_user_model_id: Option, + #[napi(js_name = "toastActivatorClsid")] + pub toast_activator_clsid: Option, + #[napi(js_name = "iconPath")] + pub icon_path: Option, + #[napi(js_name = "iconIndex")] + pub icon_index: Option, + #[napi(js_name = "workingDir")] + pub working_dir: Option, + pub description: Option, +} + +#[napi(object)] +#[derive(Debug)] +pub struct SetRunValueOptions { + pub name: Option, + pub command: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct ShortcutOptions { + lnk_path: String, + target: String, + args: Option, + app_user_model_id: Option, + toast_activator_clsid: Option, + icon_path: Option, + icon_index: i32, + working_dir: Option, + description: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct RunValueOptions { + name: String, + command: String, +} + +pub struct CreateShortcutTask { + options: ShortcutOptions, +} + +pub struct SetRunValueTask { + options: RunValueOptions, +} + +pub struct DeleteRunValueTask { + name: String, +} + +pub struct GetRunValueTask { + name: String, +} + +#[napi(js_name = "createShortcut")] +pub fn create_shortcut(opts: CreateShortcutOptions) -> Result> { + Ok(AsyncTask::new(CreateShortcutTask { + options: validate_options(opts)?, + })) +} + +#[napi(js_name = "setCurrentUserRunValue")] +pub fn set_current_user_run_value(opts: SetRunValueOptions) -> Result> { + Ok(AsyncTask::new(SetRunValueTask { + options: validate_run_value_options(opts)?, + })) +} + +#[napi(js_name = "deleteCurrentUserRunValue")] +pub fn delete_current_user_run_value(name: String) -> Result> { + Ok(AsyncTask::new(DeleteRunValueTask { + name: require_non_empty(Some(name), "name")?, + })) +} + +#[napi(js_name = "getCurrentUserRunValue")] +pub fn get_current_user_run_value(name: String) -> Result> { + Ok(AsyncTask::new(GetRunValueTask { + name: require_non_empty(Some(name), "name")?, + })) +} + +#[cfg(target_os = "windows")] +#[napi(js_name = "getUserNotificationState")] +pub fn get_user_notification_state() -> Result { + platform::get_user_notification_state() +} + +impl Task for CreateShortcutTask { + type Output = (); + type JsValue = (); + + fn compute(&mut self) -> Result { + platform::create_shortcut(&self.options) + } + + fn resolve(&mut self, _env: Env, _output: Self::Output) -> Result { + Ok(()) + } +} + +impl Task for SetRunValueTask { + type Output = (); + type JsValue = (); + + fn compute(&mut self) -> Result { + platform::set_current_user_run_value(&self.options.name, &self.options.command) + } + + fn resolve(&mut self, _env: Env, _output: Self::Output) -> Result { + Ok(()) + } +} + +impl Task for DeleteRunValueTask { + type Output = (); + type JsValue = (); + + fn compute(&mut self) -> Result { + platform::delete_current_user_run_value(&self.name) + } + + fn resolve(&mut self, _env: Env, _output: Self::Output) -> Result { + Ok(()) + } +} + +impl Task for GetRunValueTask { + type Output = Option; + type JsValue = Option; + + fn compute(&mut self) -> Result { + platform::get_current_user_run_value(&self.name) + } + + fn resolve(&mut self, _env: Env, output: Self::Output) -> Result { + Ok(output) + } +} + +fn validate_options(opts: CreateShortcutOptions) -> Result { + Ok(ShortcutOptions { + lnk_path: require_non_empty(opts.lnk_path, "lnkPath")?, + target: require_non_empty(opts.target, "target")?, + args: opts.args, + app_user_model_id: opts.app_user_model_id, + toast_activator_clsid: opts.toast_activator_clsid, + icon_path: opts.icon_path, + icon_index: opts.icon_index.unwrap_or(0), + working_dir: opts.working_dir, + description: opts.description, + }) +} + +fn validate_run_value_options(opts: SetRunValueOptions) -> Result { + Ok(RunValueOptions { + name: require_non_empty(opts.name, "name")?, + command: require_non_empty(opts.command, "command")?, + }) +} + +fn require_non_empty(value: Option, property: &'static str) -> Result { + match value { + Some(value) if !value.is_empty() => Ok(value), + _ => Err(Error::new( + Status::InvalidArg, + format!("{property} is required and must be non-empty"), + )), + } +} + +#[cfg(target_os = "windows")] +mod platform { + use super::ShortcutOptions; + use napi::bindgen_prelude::{Error, Result, Status}; + use std::ffi::c_void; + use std::mem::ManuallyDrop; + use windows::Win32::Foundation::{ + ERROR_FILE_NOT_FOUND, ERROR_SUCCESS, PROPERTYKEY, WIN32_ERROR, + }; + use windows::Win32::System::Com::StructuredStorage::{ + PROPVARIANT, PROPVARIANT_0, PROPVARIANT_0_0, PROPVARIANT_0_0_0, + }; + use windows::Win32::System::Com::{ + CLSCTX_INPROC_SERVER, CLSIDFromString, COINIT_APARTMENTTHREADED, CoCreateInstance, + CoInitializeEx, CoUninitialize, IPersistFile, + }; + use windows::Win32::System::Registry::{ + HKEY, HKEY_CURRENT_USER, KEY_SET_VALUE, REG_OPTION_NON_VOLATILE, REG_SZ, RRF_RT_REG_SZ, + RegCloseKey, RegCreateKeyExW, RegDeleteValueW, RegGetValueW, RegOpenKeyExW, RegSetValueExW, + }; + use windows::Win32::System::Variant::{VT_CLSID, VT_LPWSTR}; + use windows::Win32::UI::Shell::PropertiesSystem::IPropertyStore; + use windows::Win32::UI::Shell::{ + IShellLinkW, QUNS_ACCEPTS_NOTIFICATIONS, QUNS_APP, QUNS_BUSY, QUNS_NOT_PRESENT, + QUNS_PRESENTATION_MODE, QUNS_QUIET_TIME, QUNS_RUNNING_D3D_FULL_SCREEN, + SHQueryUserNotificationState, ShellLink, + }; + use windows::core::{GUID, Interface, PCWSTR, PWSTR}; + + const CURRENT_USER_RUN_SUBKEY: &str = "Software\\Microsoft\\Windows\\CurrentVersion\\Run"; + const PKEY_APP_USER_MODEL_ID: PROPERTYKEY = PROPERTYKEY { + fmtid: GUID::from_u128(0x9f4c2855_9f79_4b39_a8d0_e1d42de1d5f3), + pid: 5, + }; + const PKEY_APP_USER_MODEL_TOAST_ACTIVATOR_CLSID: PROPERTYKEY = PROPERTYKEY { + fmtid: GUID::from_u128(0x9f4c2855_9f79_4b39_a8d0_e1d42de1d5f3), + pid: 26, + }; + + pub(super) fn get_user_notification_state() -> Result { + let state = unsafe { SHQueryUserNotificationState() } + .map_err(|err| hresult_error("SHQueryUserNotificationState failed", err))?; + let label = match state.0 { + value if value == QUNS_NOT_PRESENT.0 => "not-present", + value if value == QUNS_BUSY.0 => "busy", + value if value == QUNS_RUNNING_D3D_FULL_SCREEN.0 => "running-d3d-full-screen", + value if value == QUNS_PRESENTATION_MODE.0 => "presentation-mode", + value if value == QUNS_ACCEPTS_NOTIFICATIONS.0 => "accepts-notifications", + value if value == QUNS_QUIET_TIME.0 => "quiet-time", + value if value == QUNS_APP.0 => "app", + _ => "unknown", + }; + Ok(label.to_string()) + } + + pub(super) fn create_shortcut(opts: &ShortcutOptions) -> Result<()> { + let _com = ComApartment::initialize()?; + + unsafe { + let link: IShellLinkW = CoCreateInstance(&ShellLink, None, CLSCTX_INPROC_SERVER) + .map_err(|err| hresult_error("CoCreateInstance(CLSID_ShellLink) failed", err))?; + + let target = wide_null(&opts.target); + link.SetPath(PCWSTR(target.as_ptr())) + .map_err(|err| hresult_error("IShellLinkW::SetPath failed", err))?; + + if let Some(args) = opts.args.as_deref() { + let args = wide_null(args); + link.SetArguments(PCWSTR(args.as_ptr())) + .map_err(|err| hresult_error("IShellLinkW::SetArguments failed", err))?; + } + + if let Some(icon_path) = opts.icon_path.as_deref() { + let icon_path = wide_null(icon_path); + link.SetIconLocation(PCWSTR(icon_path.as_ptr()), opts.icon_index) + .map_err(|err| hresult_error("IShellLinkW::SetIconLocation failed", err))?; + } + + if let Some(working_dir) = opts.working_dir.as_deref() { + let working_dir = wide_null(working_dir); + link.SetWorkingDirectory(PCWSTR(working_dir.as_ptr())) + .map_err(|err| hresult_error("IShellLinkW::SetWorkingDirectory failed", err))?; + } + + if let Some(description) = opts.description.as_deref() { + let description = wide_null(description); + link.SetDescription(PCWSTR(description.as_ptr())) + .map_err(|err| hresult_error("IShellLinkW::SetDescription failed", err))?; + } + + if opts.app_user_model_id.is_some() || opts.toast_activator_clsid.is_some() { + let property_store: IPropertyStore = link + .cast() + .map_err(|err| hresult_error("QueryInterface(IPropertyStore) failed", err))?; + if let Some(app_user_model_id) = opts.app_user_model_id.as_deref() { + set_string_property( + &property_store, + &PKEY_APP_USER_MODEL_ID, + app_user_model_id, + "IPropertyStore::SetValue(PKEY_AppUserModel_ID) failed", + )?; + } + if let Some(toast_activator_clsid) = opts.toast_activator_clsid.as_deref() { + set_guid_property( + &property_store, + &PKEY_APP_USER_MODEL_TOAST_ACTIVATOR_CLSID, + toast_activator_clsid, + "IPropertyStore::SetValue(PKEY_AppUserModel_ToastActivatorCLSID) failed", + )?; + } + property_store + .Commit() + .map_err(|err| hresult_error("IPropertyStore::Commit failed", err))?; + } + + let persist: IPersistFile = link + .cast() + .map_err(|err| hresult_error("QueryInterface(IPersistFile) failed", err))?; + let lnk_path = wide_null(&opts.lnk_path); + persist + .Save(PCWSTR(lnk_path.as_ptr()), true) + .map_err(|err| hresult_error("IPersistFile::Save failed", err))?; + } + + Ok(()) + } + + pub(super) fn set_current_user_run_value(name: &str, command: &str) -> Result<()> { + let key = RunKey::create()?; + let name = wide_null(name); + let command = wide_null(command); + let data = + unsafe { std::slice::from_raw_parts(command.as_ptr().cast::(), command.len() * 2) }; + let status = + unsafe { RegSetValueExW(key.raw(), PCWSTR(name.as_ptr()), None, REG_SZ, Some(data)) }; + check_win32(status, "RegSetValueExW(HKCU Run) failed") + } + + pub(super) fn delete_current_user_run_value(name: &str) -> Result<()> { + let Some(key) = RunKey::open_for_set()? else { + return Ok(()); + }; + let name = wide_null(name); + let status = unsafe { RegDeleteValueW(key.raw(), PCWSTR(name.as_ptr())) }; + if status == ERROR_FILE_NOT_FOUND { + return Ok(()); + } + check_win32(status, "RegDeleteValueW(HKCU Run) failed") + } + + pub(super) fn get_current_user_run_value(name: &str) -> Result> { + let subkey = wide_null(CURRENT_USER_RUN_SUBKEY); + let name = wide_null(name); + let mut value_type = REG_SZ; + let mut byte_len = 0u32; + let status = unsafe { + RegGetValueW( + HKEY_CURRENT_USER, + PCWSTR(subkey.as_ptr()), + PCWSTR(name.as_ptr()), + RRF_RT_REG_SZ, + Some(&mut value_type), + None, + Some(&mut byte_len), + ) + }; + if status == ERROR_FILE_NOT_FOUND { + return Ok(None); + } + check_win32(status, "RegGetValueW(HKCU Run size) failed")?; + if byte_len == 0 { + return Ok(Some(String::new())); + } + + let mut buffer = vec![0u16; byte_len.div_ceil(2) as usize]; + let status = unsafe { + RegGetValueW( + HKEY_CURRENT_USER, + PCWSTR(subkey.as_ptr()), + PCWSTR(name.as_ptr()), + RRF_RT_REG_SZ, + Some(&mut value_type), + Some(buffer.as_mut_ptr().cast::()), + Some(&mut byte_len), + ) + }; + check_win32(status, "RegGetValueW(HKCU Run data) failed")?; + let mut code_units = (byte_len / 2) as usize; + while code_units > 0 && buffer[code_units - 1] == 0 { + code_units -= 1; + } + String::from_utf16(&buffer[..code_units]) + .map(Some) + .map_err(|_| Error::new(Status::GenericFailure, "HKCU Run value is not valid UTF-16")) + } + + struct RunKey(HKEY); + + impl RunKey { + fn create() -> Result { + let subkey = wide_null(CURRENT_USER_RUN_SUBKEY); + let mut key = HKEY::default(); + let status = unsafe { + RegCreateKeyExW( + HKEY_CURRENT_USER, + PCWSTR(subkey.as_ptr()), + None, + PCWSTR::null(), + REG_OPTION_NON_VOLATILE, + KEY_SET_VALUE, + None, + &mut key, + None, + ) + }; + check_win32(status, "RegCreateKeyExW(HKCU Run) failed")?; + Ok(Self(key)) + } + + fn open_for_set() -> Result> { + let subkey = wide_null(CURRENT_USER_RUN_SUBKEY); + let mut key = HKEY::default(); + let status = unsafe { + RegOpenKeyExW( + HKEY_CURRENT_USER, + PCWSTR(subkey.as_ptr()), + None, + KEY_SET_VALUE, + &mut key, + ) + }; + if status == ERROR_FILE_NOT_FOUND { + return Ok(None); + } + check_win32(status, "RegOpenKeyExW(HKCU Run) failed")?; + Ok(Some(Self(key))) + } + + fn raw(&self) -> HKEY { + self.0 + } + } + + impl Drop for RunKey { + fn drop(&mut self) { + unsafe { + let _ = RegCloseKey(self.0); + } + } + } + + struct ComApartment; + + impl ComApartment { + fn initialize() -> Result { + unsafe { + CoInitializeEx(None, COINIT_APARTMENTTHREADED) + .ok() + .map_err(|err| hresult_error("CoInitializeEx failed", err))?; + } + Ok(Self) + } + } + + impl Drop for ComApartment { + fn drop(&mut self) { + unsafe { + CoUninitialize(); + } + } + } + + fn wide_null(value: &str) -> Vec { + value.encode_utf16().chain(std::iter::once(0)).collect() + } + + fn propvariant_from_wide_string(wide: &mut [u16]) -> PROPVARIANT { + PROPVARIANT { + Anonymous: PROPVARIANT_0 { + Anonymous: ManuallyDrop::new(PROPVARIANT_0_0 { + vt: VT_LPWSTR, + wReserved1: 0, + wReserved2: 0, + wReserved3: 0, + Anonymous: PROPVARIANT_0_0_0 { + pwszVal: PWSTR(wide.as_mut_ptr()), + }, + }), + }, + } + } + + fn propvariant_from_guid(guid: &mut GUID) -> PROPVARIANT { + PROPVARIANT { + Anonymous: PROPVARIANT_0 { + Anonymous: ManuallyDrop::new(PROPVARIANT_0_0 { + vt: VT_CLSID, + wReserved1: 0, + wReserved2: 0, + wReserved3: 0, + Anonymous: PROPVARIANT_0_0_0 { + puuid: guid as *mut GUID, + }, + }), + }, + } + } + + fn set_string_property( + property_store: &IPropertyStore, + key: &PROPERTYKEY, + value: &str, + label: &'static str, + ) -> Result<()> { + let mut wide = wide_null(value); + unsafe { + let prop = propvariant_from_wide_string(&mut wide); + property_store + .SetValue(key as *const PROPERTYKEY, &prop as *const PROPVARIANT) + .map_err(|err| hresult_error(label, err)) + } + } + + fn set_guid_property( + property_store: &IPropertyStore, + key: &PROPERTYKEY, + value: &str, + label: &'static str, + ) -> Result<()> { + let wide = wide_null(value); + unsafe { + let mut guid = CLSIDFromString(PCWSTR(wide.as_ptr())) + .map_err(|err| hresult_error("CLSIDFromString failed", err))?; + let prop = propvariant_from_guid(&mut guid); + property_store + .SetValue(key as *const PROPERTYKEY, &prop as *const PROPVARIANT) + .map_err(|err| hresult_error(label, err)) + } + } + + fn hresult_error(label: &'static str, err: windows::core::Error) -> Error { + Error::new( + Status::GenericFailure, + format!("{label} (hr 0x{:x})", err.code().0 as u32), + ) + } + + fn check_win32(status: WIN32_ERROR, label: &'static str) -> Result<()> { + if status == ERROR_SUCCESS { + Ok(()) + } else { + Err(win32_error(label, status)) + } + } + + fn win32_error(label: &'static str, status: WIN32_ERROR) -> Error { + Error::new( + Status::GenericFailure, + format!("{label} (win32 {})", status.0), + ) + } +} + +#[cfg(not(target_os = "windows"))] +mod platform { + use super::ShortcutOptions; + use napi::bindgen_prelude::{Error, Result, Status}; + + pub(super) fn create_shortcut(_opts: &ShortcutOptions) -> Result<()> { + Err(Error::new( + Status::GenericFailure, + "win-shell not supported on this platform", + )) + } + + pub(super) fn set_current_user_run_value(_name: &str, _command: &str) -> Result<()> { + Err(Error::new( + Status::GenericFailure, + "win-shell not supported on this platform", + )) + } + + pub(super) fn delete_current_user_run_value(_name: &str) -> Result<()> { + Err(Error::new( + Status::GenericFailure, + "win-shell not supported on this platform", + )) + } + + pub(super) fn get_current_user_run_value(_name: &str) -> Result> { + Err(Error::new( + Status::GenericFailure, + "win-shell not supported on this platform", + )) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn valid_raw_options() -> CreateShortcutOptions { + CreateShortcutOptions { + lnk_path: Some("C:\\Users\\Example\\Desktop\\Fluxer.lnk".to_string()), + target: Some("C:\\Program Files\\Fluxer\\Fluxer.exe".to_string()), + args: None, + app_user_model_id: None, + toast_activator_clsid: None, + icon_path: None, + icon_index: None, + working_dir: None, + description: None, + } + } + + fn valid_run_value_options() -> SetRunValueOptions { + SetRunValueOptions { + name: Some("velopack.fluxer_desktop_canary".to_string()), + command: Some( + "\"C:\\Users\\Example\\AppData\\Local\\Fluxer\\Fluxer.exe\" --autostart" + .to_string(), + ), + } + } + + #[test] + fn validation_rejects_missing_lnk_path() { + let mut opts = valid_raw_options(); + opts.lnk_path = None; + let err = validate_options(opts).expect_err("missing lnkPath should be rejected"); + assert_eq!(err.status, Status::InvalidArg); + assert_eq!(err.reason, "lnkPath is required and must be non-empty"); + } + + #[test] + fn validation_rejects_empty_target() { + let mut opts = valid_raw_options(); + opts.target = Some(String::new()); + let err = validate_options(opts).expect_err("empty target should be rejected"); + assert_eq!(err.status, Status::InvalidArg); + assert_eq!(err.reason, "target is required and must be non-empty"); + } + + #[test] + fn validation_defaults_icon_index_to_zero() { + let opts = validate_options(valid_raw_options()).expect("options should validate"); + assert_eq!(opts.icon_index, 0); + } + + #[test] + fn validation_preserves_optional_fields() { + let mut raw = valid_raw_options(); + raw.args = Some("--startup".to_string()); + raw.icon_path = Some("C:\\Program Files\\Fluxer\\Fluxer.exe".to_string()); + raw.icon_index = Some(2); + raw.working_dir = Some("C:\\Program Files\\Fluxer".to_string()); + raw.description = Some("Fluxer desktop client".to_string()); + raw.app_user_model_id = Some("velopack.fluxer_desktop".to_string()); + raw.toast_activator_clsid = Some("{48EEF21B-F3AE-431E-8CF2-386FFB2143F2}".to_string()); + + let opts = validate_options(raw).expect("options should validate"); + assert_eq!(opts.args.as_deref(), Some("--startup")); + assert_eq!( + opts.app_user_model_id.as_deref(), + Some("velopack.fluxer_desktop") + ); + assert_eq!( + opts.toast_activator_clsid.as_deref(), + Some("{48EEF21B-F3AE-431E-8CF2-386FFB2143F2}") + ); + assert_eq!( + opts.icon_path.as_deref(), + Some("C:\\Program Files\\Fluxer\\Fluxer.exe") + ); + assert_eq!(opts.icon_index, 2); + assert_eq!( + opts.working_dir.as_deref(), + Some("C:\\Program Files\\Fluxer") + ); + assert_eq!(opts.description.as_deref(), Some("Fluxer desktop client")); + } + + #[test] + fn run_value_validation_rejects_missing_name() { + let mut opts = valid_run_value_options(); + opts.name = None; + let err = validate_run_value_options(opts).expect_err("missing name should be rejected"); + assert_eq!(err.status, Status::InvalidArg); + assert_eq!(err.reason, "name is required and must be non-empty"); + } + + #[test] + fn run_value_validation_rejects_empty_command() { + let mut opts = valid_run_value_options(); + opts.command = Some(String::new()); + let err = validate_run_value_options(opts).expect_err("empty command should be rejected"); + assert_eq!(err.status, Status::InvalidArg); + assert_eq!(err.reason, "command is required and must be non-empty"); + } + + #[test] + fn run_value_validation_preserves_fields() { + let opts = + validate_run_value_options(valid_run_value_options()).expect("options should validate"); + assert_eq!(opts.name, "velopack.fluxer_desktop_canary"); + assert_eq!( + opts.command, + "\"C:\\Users\\Example\\AppData\\Local\\Fluxer\\Fluxer.exe\" --autostart" + ); + } + + #[cfg(not(target_os = "windows"))] + #[test] + fn non_windows_worker_preserves_stub_error_contract() { + let opts = validate_options(valid_raw_options()).expect("options should validate"); + let err = platform::create_shortcut(&opts).expect_err("non-Windows should fail"); + assert_eq!(err.status, Status::GenericFailure); + assert_eq!(err.reason, "win-shell not supported on this platform"); + } +} diff --git a/fluxer_desktop/native/win-toast/Cargo.lock b/fluxer_desktop/native/win-toast/Cargo.lock new file mode 100644 index 0000000..7fbf4e9 --- /dev/null +++ b/fluxer_desktop/native/win-toast/Cargo.lock @@ -0,0 +1,440 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "convert_case" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "affbf0190ed2caf063e3def54ff444b449371d55c58e513a95ab98eca50adb49" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "ctor" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01334b89b69ff726750c5ce5073fc8bd860e99aa9a8fc5ca11b04730e3aee97a" + +[[package]] +name = "fluxer_win_toast" +version = "0.0.0" +dependencies = [ + "napi", + "napi-build", + "napi-derive", + "serde", + "windows", +] + +[[package]] +name = "futures" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "libloading" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "754ca22de805bb5744484a5b151a9e1a8e837d5dc232c2d7d8c2e3492edc8b60" +dependencies = [ + "cfg-if", + "windows-link", +] + +[[package]] +name = "memchr" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8" + +[[package]] +name = "napi" +version = "3.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad513ff22558f1830b595ea6eb4091da48145d09a222ce157e781896f78be0b9" +dependencies = [ + "bitflags", + "ctor", + "futures", + "napi-build", + "napi-sys", + "nohash-hasher", + "rustc-hash", + "serde", + "serde_json", +] + +[[package]] +name = "napi-build" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9c366d2c8c60b86fa632df75f745509b52f9128f91a6bad4c796e44abb505e1" + +[[package]] +name = "napi-derive" +version = "3.5.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89b3f766e04667e6da0e181e2da4f85475d5a6513b7cf6a80bea184e224a5b42" +dependencies = [ + "convert_case", + "ctor", + "napi-derive-backend", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "napi-derive-backend" +version = "5.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d5af30503edf933ce7377cf6d4c877a62b0f1107ea05585f1b5e430e88d5baf" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "semver", + "syn", +] + +[[package]] +name = "napi-sys" +version = "3.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f5bcdf71abd3a50d00b49c1c2c75251cb3c913777d6139cd37dabc093a5e400" +dependencies = [ + "libloading", +] + +[[package]] +name = "nohash-hasher" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rustc-hash" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core", + "windows-link", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core", + "windows-link", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/fluxer_desktop/native/win-toast/Cargo.toml b/fluxer_desktop/native/win-toast/Cargo.toml new file mode 100644 index 0000000..0927f59 --- /dev/null +++ b/fluxer_desktop/native/win-toast/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "fluxer_win_toast" +version = "0.0.0" +edition = "2024" +license = "AGPL-3.0-or-later" +publish = false + +[workspace] +resolver = "2" + +[lib] +crate-type = ["cdylib", "rlib"] + +[dependencies] +napi = {version = "3.9.1", default-features = false, features = ["dyn-symbols", "napi8", "serde-json"]} +napi-derive = "3.5.6" +serde = {version = "1.0", features = ["derive"]} + +[target.'cfg(target_os = "windows")'.dependencies] +windows = {version = "0.62.2", features = [ + "Data_Xml_Dom", + "Foundation", + "UI_Notifications", + "Win32_System_WinRT", +]} + +[build-dependencies] +napi-build = "2.3.2" diff --git a/fluxer_desktop/native/win-toast/build.rs b/fluxer_desktop/native/win-toast/build.rs new file mode 100644 index 0000000..d45de40 --- /dev/null +++ b/fluxer_desktop/native/win-toast/build.rs @@ -0,0 +1,5 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +fn main() { + napi_build::setup(); +} diff --git a/fluxer_desktop/native/win-toast/index.d.ts b/fluxer_desktop/native/win-toast/index.d.ts new file mode 100644 index 0000000..bf3adce --- /dev/null +++ b/fluxer_desktop/native/win-toast/index.d.ts @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +export interface ToastAction { + label: string; + args: string; + activationType?: 'foreground' | 'background' | 'protocol'; + imageUri?: string; + hintInputId?: string; +} + +export interface ToastBindingText { + text: string; + hintMaxLines?: number; + hint?: 'attribution'; +} + +export interface ToastInputBox { + id: string; + type: 'text' | 'selection'; + placeholder?: string; + title?: string; + options?: ReadonlyArray<{id: string; content: string}>; +} + +export interface ToastImage { + uri: string; + placement?: 'hero' | 'appLogoOverride'; + hintCrop?: 'circle' | 'none'; + alt?: string; +} + +export interface ToastNotifyOptions { + aumid: string; + tag?: string; + group?: string; + expirationTime?: string; + scenario?: 'default' | 'urgent' | 'reminder' | 'incomingCall' | 'alarm'; + audio?: 'default' | 'silent' | {silent?: boolean; loop?: boolean; src?: string}; + lines: ReadonlyArray; + images?: ReadonlyArray; + inputs?: ReadonlyArray; + actions?: ReadonlyArray; +} + +export interface ToastSupport { + supported: boolean; + reason?: string; +} + +export declare function isSupported(): ToastSupport; + +export declare function notify(opts: ToastNotifyOptions): Promise; + +export declare function dismiss(opts: {aumid: string; tag: string; group?: string}): Promise; + +export declare function clear(opts: {aumid: string}): Promise; + +export declare const loadError: Error | null; diff --git a/fluxer_desktop/native/win-toast/index.js b/fluxer_desktop/native/win-toast/index.js new file mode 100644 index 0000000..e037869 --- /dev/null +++ b/fluxer_desktop/native/win-toast/index.js @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync} = require('node:fs'); +const {join, sep} = require('node:path'); +const {createNativeLoadError, loadNativeBinding} = require('./loader-diagnostics.cjs'); +const MODULE_NAME = '@fluxer/win-toast'; + +function resolveNativeRoot() { + const asarSegment = `${sep}app.asar${sep}`; + if (!__dirname.includes(asarSegment)) return __dirname; + const unpackedDir = __dirname.replace(asarSegment, `${sep}app.asar.unpacked${sep}`); + return existsSync(unpackedDir) ? unpackedDir : __dirname; +} + +function nativeFileName() { + if (process.platform !== 'win32') { + throw new Error(`@fluxer/win-toast is only supported on Windows, got ${process.platform}`); + } + switch (process.arch) { + case 'x64': + return 'win-toast.win32-x64-msvc.node'; + case 'arm64': + return 'win-toast.win32-arm64-msvc.node'; + default: + throw new Error(`Unsupported Windows architecture: ${process.arch}`); + } +} + +let binding = null; +let loadError = null; + +if (process.platform === 'win32') { + try { + const nativeRoot = resolveNativeRoot(); + const nativePath = join(nativeRoot, nativeFileName()); + const loaded = loadNativeBinding({ + moduleName: MODULE_NAME, + nativePath, + nativeRoot, + packageDir: __dirname, + probe: false, + }); + binding = loaded.binding; + loadError = loaded.loadError; + if (loadError) throw loadError; + } catch (error) { + loadError = createNativeLoadError({ + moduleName: MODULE_NAME, + nativeRoot: resolveNativeRoot(), + packageDir: __dirname, + reason: 'native loader threw before binding load completed', + cause: error, + }); + throw loadError; + } +} + +const stubSupport = () => ({supported: false, reason: loadError ? String(loadError.message) : 'addon not loaded'}); +const stubAsync = () => Promise.reject(loadError ?? new Error('@fluxer/win-toast not loaded')); + +module.exports = { + isSupported: binding ? binding.isSupported : stubSupport, + notify: binding ? binding.notify : stubAsync, + dismiss: binding ? binding.dismiss : stubAsync, + clear: binding ? binding.clear : stubAsync, + loadError, +}; diff --git a/fluxer_desktop/native/win-toast/loader-diagnostics.cjs b/fluxer_desktop/native/win-toast/loader-diagnostics.cjs new file mode 100644 index 0000000..14d70b9 --- /dev/null +++ b/fluxer_desktop/native/win-toast/loader-diagnostics.cjs @@ -0,0 +1,524 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +const {existsSync, readdirSync, readFileSync, statSync} = require('node:fs'); +const os = require('node:os'); +const {basename} = require('node:path'); +const {spawnSync} = require('node:child_process'); +const NATIVE_LOAD_ERROR_MARKER = Symbol.for('fluxer.nativeLoadError'); +const MAX_TEXT_LENGTH = 6000; +const MAX_DIRECTORY_ENTRIES = 80; + +function trimText(value, limit = MAX_TEXT_LENGTH) { + const text = Buffer.isBuffer(value) ? value.toString('utf8') : String(value ?? ''); + const trimmed = text.trim(); + if (!trimmed) return null; + return trimmed.length > limit ? `${trimmed.slice(0, limit)}\n...` : trimmed; +} + +function errorDiagnostic(error) { + if (!error) return null; + if (error instanceof Error) { + return { + name: error.name || 'Error', + message: error.message, + code: error.code || null, + stack: trimText(error.stack || error.message), + }; + } + return { + name: typeof error, + message: trimText(String(error)), + code: null, + stack: null, + }; +} + +function formatErrorDiagnostic(diagnostic) { + if (!diagnostic) return null; + const lines = []; + if (diagnostic.code) lines.push(`code=${diagnostic.code}`); + if (diagnostic.stack) lines.push(diagnostic.stack); + else if (diagnostic.message) lines.push(diagnostic.message); + return trimText(lines.join('\n')); +} + +function fileDiagnostic(filePath) { + if (!filePath) return {path: null, exists: false, error: 'not resolved'}; + try { + const stat = statSync(filePath); + return { + path: filePath, + exists: true, + size: stat.size, + mode: `0${(stat.mode & 0o777).toString(8)}`, + mtime: stat.mtime.toISOString(), + isFile: stat.isFile(), + isDirectory: stat.isDirectory(), + }; + } catch (error) { + const reason = error instanceof Error ? error.message : String(error); + return {path: filePath, exists: false, error: reason}; + } +} + +function formatFileDiagnostic(diagnostic) { + if (!diagnostic) return 'not resolved'; + if (!diagnostic.exists) return `exists=false, statError=${diagnostic.error || ''}`; + return [ + `exists=true`, + `size=${diagnostic.size}`, + `mode=${diagnostic.mode}`, + `mtime=${diagnostic.mtime}`, + `isFile=${diagnostic.isFile}`, + ].join(', '); +} + +function directoryDiagnostic(dirPath) { + if (!dirPath) return {path: null, ok: false, error: 'not resolved', entries: [], total: 0, omitted: 0}; + try { + const entries = readdirSync(dirPath, {withFileTypes: true}).map((entry) => ({ + name: entry.name, + type: entry.isDirectory() ? 'directory' : entry.isFile() ? 'file' : 'other', + })); + entries.sort((a, b) => a.name.localeCompare(b.name)); + const visible = entries.slice(0, MAX_DIRECTORY_ENTRIES); + return { + path: dirPath, + ok: true, + entries: visible, + total: entries.length, + omitted: Math.max(0, entries.length - visible.length), + }; + } catch (error) { + return { + path: dirPath, + ok: false, + error: error instanceof Error ? error.message : String(error), + entries: [], + total: 0, + omitted: 0, + }; + } +} + +function formatDirectoryDiagnostic(diagnostic) { + if (!diagnostic) return ''; + if (!diagnostic.ok) return `directory listing failed: ${diagnostic.error || ''}`; + const entries = diagnostic.entries.map((entry) => `${entry.name}${entry.type === 'directory' ? '/' : ''}`); + const suffix = diagnostic.omitted > 0 ? [`...<${diagnostic.omitted} more entries>`] : []; + return [...entries, ...suffix].join('\n') || ''; +} + +function selectedEnvironmentNames(skipNativeProbeEnv) { + const names = [ + 'ELECTRON_RUN_AS_NODE', + 'FLUXER_NATIVE_MODULE_PREFLIGHT_CHILD', + 'LD_LIBRARY_PATH', + 'DYLD_LIBRARY_PATH', + 'DISPLAY', + 'WAYLAND_DISPLAY', + 'XDG_CURRENT_DESKTOP', + 'XDG_SESSION_TYPE', + 'DBUS_SESSION_BUS_ADDRESS', + 'PULSE_SERVER', + 'PIPEWIRE_REMOTE', + 'PATH', + ]; + if (skipNativeProbeEnv) names.push(skipNativeProbeEnv); + return names; +} + +function environmentDiagnostics(skipNativeProbeEnv) { + return Object.fromEntries( + selectedEnvironmentNames(skipNativeProbeEnv).map((name) => [name, process.env[name] ?? null]), + ); +} + +function formatEnvironment(diagnostic) { + return Object.entries(diagnostic) + .map(([name, value]) => `${name}=${value ?? ''}`) + .join('\n'); +} + +function runtimeDiagnostics() { + const versions = process.versions || {}; + let reportHeader = null; + if (process.report && typeof process.report.getReport === 'function') { + try { + reportHeader = process.report.getReport().header || null; + } catch { + reportHeader = null; + } + } + const glibcRuntime = versions.glibcVersionRuntime || reportHeader?.glibcVersionRuntime || ''; + const glibcCompiler = versions.glibcVersionCompiler || reportHeader?.glibcVersionCompiler || ''; + return { + node: versions.node || null, + electron: versions.electron || null, + modules: versions.modules || null, + napi: versions.napi || null, + v8: versions.v8 || null, + uv: versions.uv || null, + openssl: versions.openssl || null, + glibcRuntime, + glibcCompiler, + platform: process.platform, + arch: process.arch, + osType: os.type(), + osRelease: os.release(), + osVersion: typeof os.version === 'function' ? os.version() : null, + execPath: process.execPath, + resourcesPath: process.resourcesPath || null, + cwd: process.cwd(), + }; +} + +function formatRuntimeDiagnostics(diagnostic) { + return [ + `node=${diagnostic.node || ''}`, + `electron=${diagnostic.electron || ''}`, + `modules=${diagnostic.modules || ''}`, + `napi=${diagnostic.napi || ''}`, + `v8=${diagnostic.v8 || ''}`, + `uv=${diagnostic.uv || ''}`, + `openssl=${diagnostic.openssl || ''}`, + `glibcRuntime=${diagnostic.glibcRuntime || ''}`, + `glibcCompiler=${diagnostic.glibcCompiler || ''}`, + `process=${diagnostic.platform}/${diagnostic.arch}`, + `os=${diagnostic.osType} ${diagnostic.osRelease} ${diagnostic.osVersion || ''}`, + `execPath=${diagnostic.execPath}`, + `resourcesPath=${diagnostic.resourcesPath || ''}`, + `cwd=${diagnostic.cwd}`, + ].join('\n'); +} + +const REDISTRIBUTABLE_RUNTIME_PATTERNS = [ + /^vcruntime\d+(?:_\d+)?\.dll$/i, + /^msvcp\d+(?:_\d+)?\.dll$/i, + /^msvcr\d+(?:_\d+)?\.dll$/i, + /^concrt\d+\.dll$/i, + /^vcamp\d+\.dll$/i, + /^vcomp\d+\.dll$/i, +]; + +function readPeImports(filePath) { + let buffer; + try { + buffer = readFileSync(filePath); + } catch { + return null; + } + if (buffer.length < 0x40) return null; + const peOffset = buffer.readUInt32LE(0x3c); + if (peOffset <= 0 || peOffset + 24 >= buffer.length) return null; + if (buffer.readUInt32LE(peOffset) !== 0x4550) return null; + const coffOffset = peOffset + 4; + const numberOfSections = buffer.readUInt16LE(coffOffset + 2); + const sizeOfOptionalHeader = buffer.readUInt16LE(coffOffset + 16); + const optionalHeaderOffset = coffOffset + 20; + if (optionalHeaderOffset + sizeOfOptionalHeader > buffer.length) return null; + const magic = buffer.readUInt16LE(optionalHeaderOffset); + if (magic !== 0x10b && magic !== 0x20b) return null; + const dataDirectoriesOffset = optionalHeaderOffset + (magic === 0x20b ? 112 : 96); + const importEntryOffset = dataDirectoriesOffset + 8; + if (importEntryOffset + 8 > buffer.length) return null; + const importRva = buffer.readUInt32LE(importEntryOffset); + if (importRva === 0) return []; + const sections = []; + const sectionTableOffset = optionalHeaderOffset + sizeOfOptionalHeader; + for (let i = 0; i < numberOfSections; i++) { + const base = sectionTableOffset + i * 40; + if (base + 40 > buffer.length) return null; + sections.push({ + virtualSize: buffer.readUInt32LE(base + 8), + virtualAddress: buffer.readUInt32LE(base + 12), + rawSize: buffer.readUInt32LE(base + 16), + rawPointer: buffer.readUInt32LE(base + 20), + }); + } + const rvaToOffset = (rva) => { + for (const s of sections) { + const span = Math.max(s.virtualSize, s.rawSize); + if (rva >= s.virtualAddress && rva < s.virtualAddress + span) { + return rva - s.virtualAddress + s.rawPointer; + } + } + return -1; + }; + const readCString = (offset) => { + let end = offset; + while (end < buffer.length && buffer[end] !== 0) end++; + return buffer.toString('ascii', offset, end); + }; + const importTableOffset = rvaToOffset(importRva); + if (importTableOffset < 0) return []; + const imports = new Set(); + for (let i = 0; i < 1024; i++) { + const base = importTableOffset + i * 20; + if (base + 20 > buffer.length) break; + const lookupRva = buffer.readUInt32LE(base); + const nameRva = buffer.readUInt32LE(base + 12); + const iatRva = buffer.readUInt32LE(base + 16); + if (lookupRva === 0 && nameRva === 0 && iatRva === 0) break; + const nameOffset = rvaToOffset(nameRva); + if (nameOffset < 0) continue; + const name = readCString(nameOffset); + if (name) imports.add(name); + } + return Array.from(imports); +} + +function windowsImportProbe(nativePath) { + const imports = readPeImports(nativePath); + if (imports === null) return null; + const sortedImports = [...imports].sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase())); + const redistributable = sortedImports.filter((dll) => + REDISTRIBUTABLE_RUNTIME_PATTERNS.some((pattern) => pattern.test(dll)), + ); + return { + command: ['pe-imports', nativePath], + status: 0, + signal: null, + error: null, + stdout: sortedImports.join('\n') || null, + stderr: null, + missing: [], + redistributable, + }; +} + +function dependencyProbe(nativePath) { + if (!nativePath || !existsSync(nativePath)) return null; + if (process.platform === 'win32') return windowsImportProbe(nativePath); + const command = + process.platform === 'linux' + ? ['ldd', nativePath] + : process.platform === 'darwin' + ? ['otool', '-L', nativePath] + : null; + if (!command) return null; + const [bin, ...args] = command; + const result = spawnSync(bin, args, { + encoding: 'utf8', + timeout: 4000, + stdio: ['ignore', 'pipe', 'pipe'], + }); + const stdout = trimText(result.stdout); + const stderr = trimText(result.stderr); + const missing = + process.platform === 'linux' && stdout + ? stdout + .split('\n') + .map((line) => line.trim()) + .filter((line) => line.includes('not found')) + : []; + return { + command, + status: result.status, + signal: result.signal || null, + error: result.error ? result.error.message : null, + stdout, + stderr, + missing, + redistributable: [], + }; +} + +function formatDependencyProbe(diagnostic) { + if (!diagnostic) return null; + const status = diagnostic.error + ? `error=${diagnostic.error}` + : diagnostic.signal + ? `signal=${diagnostic.signal}` + : `status=${diagnostic.status}`; + return [ + `$ ${diagnostic.command.join(' ')}`, + status, + diagnostic.missing?.length ? `missing:\n${diagnostic.missing.join('\n')}` : null, + diagnostic.redistributable?.length + ? `redistributableRuntimeImports (require VC++ redist on host):\n${diagnostic.redistributable.join('\n')}` + : null, + diagnostic.stdout ? `stdout:\n${diagnostic.stdout}` : null, + diagnostic.stderr ? `stderr:\n${diagnostic.stderr}` : null, + ] + .filter(Boolean) + .join('\n'); +} + +function formatExtraDiagnostic(diagnostic) { + if (!diagnostic) return null; + if (typeof diagnostic === 'string') return diagnostic; + if (typeof diagnostic === 'object' && diagnostic.name && diagnostic.text) { + return `${diagnostic.name}:\n${diagnostic.text}`; + } + return `extra:\n${trimText(JSON.stringify(diagnostic, null, 2))}`; +} + +function collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + return { + schemaVersion: 1, + moduleName, + reason, + target: { + platform: process.platform, + arch: process.arch, + }, + packageDir: packageDir || null, + nativeRoot: nativeRoot || null, + nativePath: nativePath || null, + nativeFile: nativePath ? basename(nativePath) : null, + nativeFileStat: fileDiagnostic(nativePath), + runtime: runtimeDiagnostics(), + environment: environmentDiagnostics(skipNativeProbeEnv), + nativeRootEntries: directoryDiagnostic(nativeRoot), + dependencyProbe: dependencyProbe(nativePath), + extraDiagnostics: extraDiagnostics.filter(Boolean), + cause: errorDiagnostic(cause), + }; +} + +function formatNativeDiagnostics(diagnostics) { + const sections = [ + `module=${diagnostics.moduleName}`, + `reason=${diagnostics.reason}`, + `target=${diagnostics.target.platform}/${diagnostics.target.arch}`, + `packageDir=${diagnostics.packageDir || ''}`, + `nativeRoot=${diagnostics.nativeRoot || ''}`, + `nativePath=${diagnostics.nativePath || ''}`, + `nativeFile=${diagnostics.nativeFile || ''}`, + `nativeFileStat=${formatFileDiagnostic(diagnostics.nativeFileStat)}`, + `runtime:\n${formatRuntimeDiagnostics(diagnostics.runtime)}`, + `environment:\n${formatEnvironment(diagnostics.environment)}`, + `nativeRootEntries:\n${formatDirectoryDiagnostic(diagnostics.nativeRootEntries)}`, + ...diagnostics.extraDiagnostics.map(formatExtraDiagnostic).filter(Boolean), + ]; + const dependencyOutput = formatDependencyProbe(diagnostics.dependencyProbe); + if (dependencyOutput) sections.push(`dependencyProbe:\n${dependencyOutput}`); + const causeText = formatErrorDiagnostic(diagnostics.cause); + if (causeText) sections.push(`cause:\n${causeText}`); + return sections.join('\n'); +} + +function isNativeLoadError(error) { + return Boolean(error?.[NATIVE_LOAD_ERROR_MARKER]); +} + +function createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics = [], +}) { + if (isNativeLoadError(cause)) return cause; + const diagnostics = collectNativeDiagnostics({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + cause, + skipNativeProbeEnv, + extraDiagnostics, + }); + const error = new Error(`${moduleName} native module failed to load.\n${formatNativeDiagnostics(diagnostics)}`); + error.name = 'NativeModuleLoadError'; + error[NATIVE_LOAD_ERROR_MARKER] = true; + error.nativeDiagnostics = diagnostics; + error.toJSON = () => ({ + name: error.name, + message: error.message, + nativeDiagnostics: diagnostics, + }); + if (cause) error.cause = cause; + return error; +} + +function probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, timeoutMs = 5000}) { + if (!skipNativeProbeEnv || process.env[skipNativeProbeEnv] === '1') { + return null; + } + const result = spawnSync(process.execPath, ['-e', 'require(process.argv[1])', nativePath], { + env: {...process.env, ELECTRON_RUN_AS_NODE: '1', [skipNativeProbeEnv]: '1'}, + encoding: 'utf8', + stdio: ['ignore', 'pipe', 'pipe'], + timeout: timeoutMs, + }); + if (result.status === 0) return null; + const reason = result.error + ? result.error.message + : result.signal + ? `safety probe terminated by signal ${result.signal}` + : `safety probe exited with code ${result.status}`; + return createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason, + skipNativeProbeEnv, + extraDiagnostics: [ + result.stdout ? {name: 'probeStdout', text: trimText(result.stdout)} : null, + result.stderr ? {name: 'probeStderr', text: trimText(result.stderr)} : null, + ], + }); +} + +function loadNativeBinding({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv, probe = true}) { + if (!existsSync(nativePath)) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'native binary not found', + skipNativeProbeEnv, + }), + }; + } + const nativeProbeError = probe + ? probeNativeBinary({moduleName, nativePath, nativeRoot, packageDir, skipNativeProbeEnv}) + : null; + if (nativeProbeError) { + return {binding: null, loadError: nativeProbeError}; + } + try { + return {binding: require(nativePath), loadError: null}; + } catch (error) { + return { + binding: null, + loadError: createNativeLoadError({ + moduleName, + nativePath, + nativeRoot, + packageDir, + reason: 'require(nativePath) threw', + cause: error, + skipNativeProbeEnv, + }), + }; + } +} + +module.exports = { + collectNativeDiagnostics, + createNativeLoadError, + formatNativeDiagnostics, + isNativeLoadError, + loadNativeBinding, + probeNativeBinary, +}; diff --git a/fluxer_desktop/native/win-toast/package.json b/fluxer_desktop/native/win-toast/package.json new file mode 100644 index 0000000..3a18348 --- /dev/null +++ b/fluxer_desktop/native/win-toast/package.json @@ -0,0 +1,29 @@ +{ + "name": "@fluxer/win-toast", + "version": "0.0.0", + "description": "", + "private": true, + "license": "AGPL-3.0-or-later", + "os": [ + "win32" + ], + "cpu": [ + "x64", + "arm64" + ], + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.js", + "index.d.ts", + "loader-diagnostics.cjs", + "win-toast.win32-x64-msvc.node", + "win-toast.win32-arm64-msvc.node" + ], + "scripts": { + "build": "cargo run --locked --quiet --manifest-path ../../../tools/ci/Cargo.toml -- build-desktop-native-addon", + "test": "cargo test --manifest-path Cargo.toml", + "check:windows:x64": "cargo check --manifest-path Cargo.toml --target x86_64-pc-windows-msvc --tests", + "check:windows:arm64": "cargo check --manifest-path Cargo.toml --target aarch64-pc-windows-msvc --tests" + } +} diff --git a/fluxer_desktop/native/win-toast/src/lib.rs b/fluxer_desktop/native/win-toast/src/lib.rs new file mode 100644 index 0000000..d69c1ed --- /dev/null +++ b/fluxer_desktop/native/win-toast/src/lib.rs @@ -0,0 +1,943 @@ +// SPDX-License-Identifier: AGPL-3.0-or-later + +use napi::Task; +use napi::bindgen_prelude::{AsyncTask, Env, Error, Result, Status, Unknown}; +use napi_derive::napi; +use serde::Deserialize; + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct RawToastNotifyOptions { + aumid: Option, + tag: Option, + group: Option, + expiration_time: Option, + scenario: Option, + audio: Option, + lines: Option>, + images: Option>, + inputs: Option>, + actions: Option>, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct RawToastText { + text: Option, + hint_max_lines: Option, + hint: Option, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct RawToastImage { + uri: Option, + placement: Option, + hint_crop: Option, + alt: Option, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct RawToastInput { + id: Option, + r#type: Option, + placeholder: Option, + title: Option, + options: Option>, +} + +#[derive(Debug, Deserialize)] +struct RawToastInputOption { + id: Option, + content: Option, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "camelCase")] +struct RawToastAction { + label: Option, + args: Option, + activation_type: Option, + image_uri: Option, + hint_input_id: Option, +} + +#[derive(Debug, Deserialize)] +#[serde(untagged)] +enum RawAudio { + Keyword(String), + Object(RawAudioObject), +} + +#[derive(Debug, Deserialize)] +struct RawAudioObject { + silent: Option, + #[serde(rename = "loop")] + loop_audio: Option, + src: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct ToastSpec { + aumid: String, + tag: Option, + group: Option, + expiration_time: Option, + scenario: Option, + audio_src: Option, + audio_silent: bool, + audio_loop: bool, + lines: Vec, + images: Vec, + inputs: Vec, + actions: Vec, +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +struct WindowsDateTimeTicks(i64); + +#[derive(Debug, Clone, PartialEq, Eq)] +struct ToastTextSpec { + text: String, + hint_max_lines: Option, + is_attribution: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct ToastImageSpec { + uri: String, + placement: Option, + hint_crop: Option, + alt: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct ToastInputSpec { + id: String, + r#type: String, + placeholder: Option, + title: Option, + options: Vec, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct ToastInputOption { + id: String, + content: String, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +struct ToastActionSpec { + label: String, + args: String, + activation_type: Option, + image_uri: Option, + hint_input_id: Option, +} + +#[derive(Debug, Deserialize)] +struct RawDismissOptions { + aumid: Option, + tag: Option, + group: Option, +} + +#[derive(Debug, Deserialize)] +struct RawClearOptions { + aumid: Option, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +enum HistoryOp { + Clear, + RemoveTag { tag: String, group: Option }, +} + +#[napi(object)] +pub struct ToastSupport { + pub supported: bool, + pub reason: Option, +} + +pub struct NotifyTask { + spec: ToastSpec, +} + +pub struct HistoryTask { + aumid: String, + op: HistoryOp, +} + +#[napi(js_name = "isSupported")] +pub fn is_supported() -> ToastSupport { + if cfg!(target_os = "windows") { + ToastSupport { + supported: true, + reason: None, + } + } else { + ToastSupport { + supported: false, + reason: Some("win-toast only runs on Windows".to_string()), + } + } +} + +#[napi] +pub fn notify(env: Env, opts: Unknown) -> Result> { + let raw = env + .from_js_value::(opts) + .map_err(|_| invalid_arg("invalid toast spec"))?; + Ok(AsyncTask::new(NotifyTask { + spec: validate_toast_spec(raw)?, + })) +} + +#[napi] +pub fn dismiss(env: Env, opts: Unknown) -> Result> { + let raw = env + .from_js_value::(opts) + .map_err(|_| invalid_arg("history op requires an options object"))?; + let aumid = require_non_empty(raw.aumid, "aumid is required")?; + let tag = require_non_empty(raw.tag, "tag is required for dismiss")?; + Ok(AsyncTask::new(HistoryTask { + aumid, + op: HistoryOp::RemoveTag { + tag, + group: raw.group, + }, + })) +} + +#[napi] +pub fn clear(env: Env, opts: Unknown) -> Result> { + let raw = env + .from_js_value::(opts) + .map_err(|_| invalid_arg("history op requires an options object"))?; + Ok(AsyncTask::new(HistoryTask { + aumid: require_non_empty(raw.aumid, "aumid is required")?, + op: HistoryOp::Clear, + })) +} + +impl Task for NotifyTask { + type Output = (); + type JsValue = (); + + fn compute(&mut self) -> Result { + platform::show_toast(&self.spec) + } + + fn resolve(&mut self, _env: Env, _output: Self::Output) -> Result { + Ok(()) + } +} + +impl Task for HistoryTask { + type Output = (); + type JsValue = (); + + fn compute(&mut self) -> Result { + platform::history_op(&self.aumid, &self.op) + } + + fn resolve(&mut self, _env: Env, _output: Self::Output) -> Result { + Ok(()) + } +} + +fn validate_toast_spec(raw: RawToastNotifyOptions) -> Result { + let (audio_silent, audio_loop, audio_src) = parse_audio(raw.audio); + Ok(ToastSpec { + aumid: require_non_empty(raw.aumid, "aumid is required")?, + tag: raw.tag, + group: raw.group, + expiration_time: raw + .expiration_time + .as_deref() + .map(parse_windows_datetime) + .transpose()?, + scenario: raw.scenario, + audio_src, + audio_silent, + audio_loop, + lines: parse_lines(raw.lines)?, + images: parse_images(raw.images)?, + inputs: parse_inputs(raw.inputs)?, + actions: parse_actions(raw.actions)?, + }) +} + +fn parse_audio(raw: Option) -> (bool, bool, Option) { + match raw { + Some(RawAudio::Keyword(keyword)) if keyword == "silent" => (true, false, None), + Some(RawAudio::Object(raw)) => ( + raw.silent.unwrap_or(false), + raw.loop_audio.unwrap_or(false), + raw.src, + ), + _ => (false, false, None), + } +} + +fn parse_lines(raw: Option>) -> Result> { + let raw = raw.ok_or_else(|| invalid_arg("invalid toast spec"))?; + if raw.is_empty() { + return Err(invalid_arg("invalid toast spec")); + } + raw.into_iter() + .map(|line| { + Ok(ToastTextSpec { + text: require_non_empty(line.text, "invalid toast spec")?, + hint_max_lines: line.hint_max_lines, + is_attribution: line.hint.as_deref() == Some("attribution"), + }) + }) + .collect() +} + +fn parse_images(raw: Option>) -> Result> { + raw.unwrap_or_default() + .into_iter() + .map(|image| { + Ok(ToastImageSpec { + uri: require_non_empty(image.uri, "invalid toast spec")?, + placement: image.placement, + hint_crop: image.hint_crop, + alt: image.alt, + }) + }) + .collect() +} + +fn parse_inputs(raw: Option>) -> Result> { + raw.unwrap_or_default() + .into_iter() + .map(|input| { + Ok(ToastInputSpec { + id: require_non_empty(input.id, "invalid toast spec")?, + r#type: require_non_empty(input.r#type, "invalid toast spec")?, + placeholder: input.placeholder, + title: input.title, + options: input + .options + .unwrap_or_default() + .into_iter() + .map(|option| { + Ok(ToastInputOption { + id: require_non_empty(option.id, "invalid toast spec")?, + content: require_non_empty(option.content, "invalid toast spec")?, + }) + }) + .collect::>>()?, + }) + }) + .collect() +} + +fn parse_actions(raw: Option>) -> Result> { + raw.unwrap_or_default() + .into_iter() + .map(|action| { + Ok(ToastActionSpec { + label: require_non_empty(action.label, "invalid toast spec")?, + args: require_non_empty(action.args, "invalid toast spec")?, + activation_type: action.activation_type, + image_uri: action.image_uri, + hint_input_id: action.hint_input_id, + }) + }) + .collect() +} + +#[cfg(any(target_os = "windows", test))] +fn build_toast_xml(spec: &ToastSpec) -> String { + let mut xml = String::new(); + xml.push_str("'); + xml.push_str(""); + + for image in &spec.images { + xml.push_str("\"");"); + } + + for line in &spec.lines { + xml.push_str("'); + push_escaped(&mut xml, &line.text); + xml.push_str(""); + } + + xml.push_str(""); + + if !spec.inputs.is_empty() || !spec.actions.is_empty() { + xml.push_str(""); + for input in &spec.inputs { + xml.push_str(""); + } else { + xml.push('>'); + for option in &input.options { + xml.push_str(""); + } + xml.push_str(""); + } + } + + for action in &spec.actions { + xml.push_str(""); + } + xml.push_str(""); + } + + if spec.audio_silent { + xml.push_str("